UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

140 lines (139 loc) • 7.5 kB
import { i as executeSqliteQuerySync } from "./kysely-sync-DUWzQvCU.mjs"; import { i as generateSecureToken } from "./secure-random-B2AO8OrW.mjs"; import { h as required, i as isCurrentPlacementTurnClaim } from "./placement-record-BZ5ExJ2S.mjs"; import { S as publishPlacementTurnToolState, a as query, i as getRequired, n as find } from "./placement-row-codec-Cpn-JKmv.mjs"; //#region src/gateway/worker-environments/placement-session-tool-operations.kernel.ts function runningOperations(db, identity) { return query(db).selectFrom("worker_session_tool_operations").select("tool_call_id").where("source_session_id", "=", identity.sessionId).where("source_claim_id", "=", identity.claimId).where("status", "=", "running"); } function assertNoRunningWorkerSessionToolOperations(db, identity) { if (executeSqliteQuerySync(db, runningOperations(db, identity).limit(1)).rows.length) throw new Error(`Session ${identity.sessionId} has a running worker session operation`); } function closeWorkerTurnToolAdmission(db, identity) { executeSqliteQuerySync(db, query(db).deleteFrom("worker_turn_tool_authorities").where("session_id", "=", identity.sessionId).where("claim_id", "=", identity.claimId)); } /** Removes authority and replay data in the same transaction that revokes the turn claim. */ function clearWorkerTurnToolState(db, identity) { closeWorkerTurnToolAdmission(db, identity); publishPlacementTurnToolState(db, identity); executeSqliteQuerySync(db, query(db).deleteFrom("worker_session_tool_operations").where("source_session_id", "=", identity.sessionId).where("source_claim_id", "=", identity.claimId)); } function createPlacementSessionToolOperationKernel(runtime) { const { db, instanceId, now } = runtime; const currentWorkerClaim = (claim) => { const current = find(db, required(claim.sessionId, "session id")); return claim.owner.kind === "worker" && current && isCurrentPlacementTurnClaim(current, claim) ? current : void 0; }; const exactWorkerClaim = (claim) => { if (!currentWorkerClaim(claim)) throw new Error(`Session ${claim.sessionId} worker turn authority changed`); }; const hasToolAuthority = (claim, toolName) => { if (!currentWorkerClaim(claim) || claim.owner.kind !== "worker") return false; const authority = executeSqliteQuerySync(db, query(db).selectFrom("worker_turn_tool_authorities").select("tool_names_json").where("session_id", "=", claim.sessionId).where("environment_id", "=", claim.owner.environmentId).where("owner_epoch", "=", claim.owner.ownerEpoch).where("placement_generation", "=", claim.placementGeneration).where("claim_id", "=", claim.claimId).where("run_id", "=", claim.runId)).rows[0]; if (!authority) return false; try { const names = JSON.parse(authority.tool_names_json); return Array.isArray(names) && names.every((name) => typeof name === "string") && names.includes(toolName); } catch { return false; } }; const runningOperation = (identity) => query(db).updateTable("worker_session_tool_operations").where("source_session_id", "=", identity.sourceSessionId).where("source_claim_id", "=", identity.sourceClaimId).where("tool_call_id", "=", identity.toolCallId).where("request_digest", "=", identity.requestDigest).where("gateway_instance_id", "=", instanceId).where("status", "=", "running"); const settleWorkerSessionToolOperation = (identity, outcome) => { return executeSqliteQuerySync(db, runningOperation(identity).set({ ...outcome, updated_at_ms: now() })).numAffectedRows === 1n; }; return { authorize(claim, toolNames) { const normalized = [...new Set(toolNames.map((name) => required(name, "worker tool name")))].toSorted(); if (claim.owner.kind !== "worker") throw new Error(`Session ${claim.sessionId} turn is not worker-owned`); exactWorkerClaim(claim); const values = { environment_id: claim.owner.environmentId, owner_epoch: claim.owner.ownerEpoch, placement_generation: claim.placementGeneration, claim_id: claim.claimId, run_id: claim.runId, tool_names_json: JSON.stringify(normalized), updated_at_ms: now() }; executeSqliteQuerySync(db, query(db).insertInto("worker_turn_tool_authorities").values({ session_id: claim.sessionId, ...values }).onConflict((conflict) => conflict.column("session_id").doUpdateSet(values))); return normalized; }, seal(claim) { if (claim.owner.kind !== "worker") return; exactWorkerClaim(claim); closeWorkerTurnToolAdmission(db, claim); }, clear(claim) { const current = getRequired(db, required(claim.sessionId, "session id")); if (!isCurrentPlacementTurnClaim(current, claim)) throw new Error(`Session ${claim.sessionId} turn authority changed`); if (executeSqliteQuerySync(db, runningOperations(db, claim).limit(1)).rows.length) return false; clearWorkerTurnToolState(db, claim); return true; }, begin(params) { if (!hasToolAuthority(params.claim, params.toolName)) return { kind: "unauthorized" }; const claimId = params.claim.claimId; const existing = executeSqliteQuerySync(db, query(db).selectFrom("worker_session_tool_operations").selectAll().where("source_session_id", "=", params.claim.sessionId).where("source_claim_id", "=", claimId).where("tool_call_id", "=", params.toolCallId)).rows[0]; if (existing) { if (existing.tool_name !== params.toolName || existing.request_digest !== params.requestDigest) return { kind: "conflict" }; if ((existing.status === "succeeded" || existing.status === "failed") && existing.result_json) return { kind: "completed", resultJson: existing.result_json }; return { kind: existing.status !== "unknown" && existing.gateway_instance_id === instanceId ? "in-progress" : "unknown" }; } if (executeSqliteQuerySync(db, runningOperations(db, params.claim)).rows.length >= 4) return { kind: "capacity" }; const timestamp = now(); const operationSeed = generateSecureToken(32); executeSqliteQuerySync(db, query(db).insertInto("worker_session_tool_operations").values({ source_session_id: params.claim.sessionId, source_claim_id: claimId, tool_call_id: params.toolCallId, tool_name: params.toolName, request_digest: params.requestDigest, operation_seed: operationSeed, status: "running", child_session_key: null, result_json: null, gateway_instance_id: instanceId, created_at_ms: timestamp, updated_at_ms: timestamp })); return { kind: "execute", operationSeed }; }, bindChild(params) { return executeSqliteQuerySync(db, runningOperation(params).set({ child_session_key: params.childSessionKey, updated_at_ms: now() }).where((expression) => expression.or([expression("child_session_key", "is", null), expression("child_session_key", "=", params.childSessionKey)]))).numAffectedRows === 1n; }, complete(params) { return settleWorkerSessionToolOperation(params, { status: params.failed ? "failed" : "succeeded", result_json: params.resultJson }); }, abandon(params) { return settleWorkerSessionToolOperation(params, { status: "unknown" }); }, recover() { const result = executeSqliteQuerySync(db, query(db).updateTable("worker_session_tool_operations").set({ status: "unknown", updated_at_ms: now() }).where("status", "=", "running")); return Number(result.numAffectedRows); } }; } //#endregion export { clearWorkerTurnToolState as n, createPlacementSessionToolOperationKernel as r, assertNoRunningWorkerSessionToolOperations as t };