letagents 0.12.12 → 0.12.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/dist/mcp/git-remote.js +7 -7
  2. package/dist/mcp/local-state/agent-sessions.js +63 -5
  3. package/dist/mcp/local-state/local-chat.js +189 -48
  4. package/dist/mcp/local-state/storage.js +16 -9
  5. package/dist/mcp/server/daemon-tool-executor.js +92 -0
  6. package/dist/mcp/server/register-tools.js +4 -2
  7. package/dist/mcp/server/runtime/agent-sessions.js +16 -7
  8. package/dist/mcp/server/runtime/api.js +11 -3
  9. package/dist/mcp/server/runtime/daemon-tool-context.js +11 -0
  10. package/dist/mcp/server/runtime/execution-profile.js +1 -0
  11. package/dist/mcp/server/runtime/messages.js +55 -0
  12. package/dist/mcp/server/runtime/presence.js +3 -3
  13. package/dist/mcp/server/runtime/room-api.js +2 -2
  14. package/dist/mcp/server/runtime/room-state.js +19 -9
  15. package/dist/mcp/server/runtime/rooms.js +54 -33
  16. package/dist/mcp/server/runtime/supervisor-bridge.js +129 -11
  17. package/dist/mcp/server/runtime/tool-surface-policy.js +7 -0
  18. package/dist/mcp/server/runtime/worker-bearer.js +17 -2
  19. package/dist/mcp/server/runtime-contract.js +1 -0
  20. package/dist/mcp/server/runtime.js +6 -6
  21. package/dist/mcp/server/supervised-tool-facade.js +107 -6
  22. package/dist/mcp/server/tools/messages/read-tool.js +54 -97
  23. package/dist/mcp/server/tools/messages/reasoning-tool.js +2 -0
  24. package/dist/mcp/server/tools/messages/send-tool.js +2 -0
  25. package/dist/mcp/server/tools/messages/status-tool.js +2 -0
  26. package/dist/mcp/server/tools/messages/wait-tool.js +303 -71
  27. package/dist/mcp/server/tools/onboarding/status-tool.js +4 -4
  28. package/dist/mcp/server/tools/rooms/inspection-tools.js +15 -10
  29. package/dist/mcp/server/tools/rooms/repo-initialization-tool.js +2 -1
  30. package/dist/mcp/server/tools/tasks/board-tools.js +34 -2
  31. package/dist/mcp/sse-client.js +163 -20
  32. package/dist/shared/activation-routing.js +163 -23
  33. package/dist/shared/agent-presence.js +6 -0
  34. package/dist/shared/desktop-release-manifest.js +63 -0
  35. package/dist/shared/desktop-release.js +60 -0
  36. package/dist/shared/scoped-ids.js +6 -0
  37. package/package.json +7 -3
  38. package/shared/execution-approval-projection.d.mts +32 -0
  39. package/shared/execution-approval-projection.mjs +107 -0
  40. package/shared/execution-approval-publication-item.d.mts +20 -0
  41. package/shared/execution-approval-publication-item.mjs +61 -0
  42. package/shared/execution-approval-publication.d.mts +53 -0
  43. package/shared/execution-approval-publication.mjs +136 -0
  44. package/shared/execution-delegation-decision.d.mts +37 -0
  45. package/shared/execution-delegation-decision.mjs +73 -0
  46. package/shared/message-contracts.d.mts +32 -0
  47. package/shared/message-contracts.mjs +109 -0
  48. package/shared/room-agent-work.d.mts +31 -0
  49. package/shared/room-agent-work.mjs +44 -0
  50. package/shared/room-resource-invalidation.d.mts +38 -0
  51. package/shared/room-resource-invalidation.mjs +50 -0
  52. package/shared/routing-aliases.d.mts +18 -0
  53. package/shared/routing-aliases.mjs +66 -0
  54. package/shared/sqlite-thread-routing.d.mts +72 -0
  55. package/shared/sqlite-thread-routing.mjs +1038 -0
@@ -3,14 +3,89 @@ import { lstat, readFile, realpath } from "node:fs/promises";
3
3
  import { createConnection } from "node:net";
4
4
  import { homedir } from "node:os";
5
5
  import { join } from "node:path";
6
+ import { parsePositivePgIntegerScopedId } from "../../../../shared/message-contracts.mjs";
6
7
  import { getCurrentSupervisedRoomAuthority } from "./supervised-room-authority.js";
7
8
  const NEGOTIATION_PROTOCOL_VERSION = 1;
8
- const SUPPORTED_SUPERVISOR_PROTOCOL_VERSIONS = new Set([1, 2]);
9
+ const SUPPORTED_SUPERVISOR_PROTOCOL_VERSIONS = new Set([1, 2, 3]);
9
10
  const DEFAULT_REQUEST_TIMEOUT_MS = 5_000;
10
11
  const CONFIRMED_BINDING_VERIFY_TIMEOUT_MS = 250;
11
12
  const SUPERVISOR_CONTEXT_FILE = ".letagents-supervisor-context.json";
12
13
  const WORK_ATTEMPT_MARKER_FILE = ".letagents-work-attempt.json";
13
14
  const MAX_SUPERVISOR_CONTEXT_BYTES = 4 * 1024;
15
+ // An SDK request id is unique only within this MCP process lifetime.
16
+ const CUSTODIAL_POLLING_PROCESS_INCARNATION_ID = randomUUID();
17
+ /** Release reuses BEFORE's exact generation; it never authorizes against a successor. */
18
+ export async function authorizeCustodialPolling(toolName, prior, env = process.env, options = {}, waitRequest) {
19
+ const wait = waitRequest ? { ...waitRequest } : undefined;
20
+ if (toolName === "wait_for_messages") {
21
+ if (!wait || !(typeof wait.mcpRequestId === "string" || Number.isSafeInteger(wait.mcpRequestId))) {
22
+ throw new Error("Custodial wait is missing its exact MCP request id.");
23
+ }
24
+ if (!(wait.roomCursor === null || (typeof wait.roomCursor === "string" && parseRoomMessageNumber(wait.roomCursor) !== null))
25
+ || (prior && (!prior.wait || prior.wait.processIncarnationId !== CUSTODIAL_POLLING_PROCESS_INCARNATION_ID
26
+ || prior.wait.mcpRequestId !== wait.mcpRequestId || typeof wait.offeredFrontier !== "string"
27
+ || parseRoomMessageNumber(wait.offeredFrontier) === null || prior.roomCursor === null
28
+ || parseRoomMessageNumber(wait.offeredFrontier) < parseRoomMessageNumber(prior.roomCursor)))) {
29
+ throw new Error("Custodial wait receipt does not match its original invocation and cursor.");
30
+ }
31
+ }
32
+ else if (wait || prior?.wait)
33
+ throw new Error("Only custodial wait may acknowledge or offer a cursor.");
34
+ if (env.LETAGENTS_EXECUTION_PROFILE?.trim() !== "supervised_mcp_polling")
35
+ throw new Error("Custodial polling profile required.");
36
+ if (env.LETAGENTS_SUPERVISED_BOUNDED_TURNS?.trim() === "1"
37
+ || env.LETAGENTS_TOKEN?.trim() || env.LETAGENTS_AGENT_SESSION_BEARER?.trim()) {
38
+ throw new Error("Custodial polling refuses bounded flags and environment credentials.");
39
+ }
40
+ const coordinates = prior?.coordinates ?? await resolveSupervisorCoordinates(supervisedContextSession(env), env, options);
41
+ if (!coordinates?.roomId || !coordinates.agentSessionId)
42
+ throw new Error("Custodial polling lacks exact worker coordinates.");
43
+ if ((wait?.requestedRoomId && wait.requestedRoomId !== coordinates.roomId)
44
+ || (wait?.requestedAgentSessionId && wait.requestedAgentSessionId !== coordinates.agentSessionId)) {
45
+ throw new Error("Custodial wait room or worker identity does not match its exact authority.");
46
+ }
47
+ const timeoutMs = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
48
+ const negotiated = prior?.negotiated ?? await negotiateSupervisor(coordinates.socketPath, timeoutMs);
49
+ if (!negotiated.custodialPollingV1 || negotiated.generation === null)
50
+ throw new Error("Daemon does not support custodial_polling_v1.");
51
+ if (wait && !negotiated.custodialPollingOffersV1)
52
+ throw new Error("Daemon does not support custodialPollingOffersV1; refusing an unjournaled wait.");
53
+ const apiUrl = prior?.apiUrl ?? env.LETAGENTS_API_URL?.trim();
54
+ if (!apiUrl)
55
+ throw new Error("Custodial polling requires an explicit API URL.");
56
+ const response = await supervisorRequest(coordinates.socketPath, {
57
+ version: negotiated.protocolVersion, id: randomUUID(), method: "supervisor.authorize_custodial_polling",
58
+ params: {
59
+ entry_id: coordinates.entryId, room_id: coordinates.roomId, work_attempt_id: coordinates.workAttemptId,
60
+ execution_generation_id: coordinates.executionGenerationId, agent_session_id: coordinates.agentSessionId,
61
+ daemon_generation: negotiated.generation, api_url: apiUrl, contract: "custodial_polling_v1",
62
+ phase: prior ? "release" : "before", tool_name: toolName,
63
+ ...(prior ? { expected_configuration_revision: prior.configurationRevision } : {}),
64
+ ...(wait ? {
65
+ process_incarnation_id: CUSTODIAL_POLLING_PROCESS_INCARNATION_ID, mcp_request_id: wait.mcpRequestId,
66
+ ...(prior ? { expected_activation_id: prior.wait.activationId, expected_binding_epoch: prior.wait.bindingEpoch,
67
+ input_cursor: prior.roomCursor, offered_frontier: wait.offeredFrontier } : { room_cursor: wait.roomCursor }),
68
+ } : {}),
69
+ },
70
+ }, timeoutMs);
71
+ const result = response.result;
72
+ if (!response.ok || response.version !== negotiated.protocolVersion || !result || result.status !== "authorized" || result.contract !== "custodial_polling_v1"
73
+ || result.room_id !== coordinates.roomId || result.agent_session_id !== coordinates.agentSessionId
74
+ || !Number.isSafeInteger(result.configuration_revision) || Number(result.configuration_revision) < 1
75
+ || (prior && result.configuration_revision !== prior.configurationRevision)
76
+ || (wait && (typeof result.activation_id !== "string" || !result.activation_id.trim()
77
+ || !Number.isSafeInteger(result.binding_epoch) || Number(result.binding_epoch) < 1
78
+ || typeof result.room_cursor !== "string"
79
+ || (prior && (result.activation_id !== prior.wait.activationId || result.binding_epoch !== prior.wait.bindingEpoch
80
+ || result.room_cursor !== prior.roomCursor))))
81
+ || !(result.room_cursor === null || (typeof result.room_cursor === "string" && parseRoomMessageNumber(result.room_cursor) !== null))) {
82
+ throw new Error("Custodial polling authority was rejected or became stale.");
83
+ }
84
+ return { coordinates, negotiated, apiUrl, roomId: coordinates.roomId, agentSessionId: coordinates.agentSessionId,
85
+ roomCursor: result.room_cursor, configurationRevision: Number(result.configuration_revision),
86
+ ...(wait ? { wait: { processIncarnationId: CUSTODIAL_POLLING_PROCESS_INCARNATION_ID, mcpRequestId: wait.mcpRequestId,
87
+ activationId: String(result.activation_id), bindingEpoch: Number(result.binding_epoch) } } : {}) };
88
+ }
14
89
  const confirmedBindingsBySession = new Map();
15
90
  const confirmedRequestsBySession = new Map();
16
91
  const confirmedProtocolsBySession = new Map();
@@ -18,6 +93,42 @@ const pendingCursorCheckpoints = new Map();
18
93
  const activeCursorCheckpointDrains = new Set();
19
94
  const cursorCheckpointRetryTimers = new Map();
20
95
  const CURSOR_CHECKPOINT_RETRY_DELAYS_MS = [250, 1_000, 3_000];
96
+ export async function executeCurrentSupervisedTool(input, env = process.env, options = {}) {
97
+ const coordinates = await requireCurrentSupervisedCoordinates(env, options);
98
+ const timeoutMs = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
99
+ const negotiated = await negotiateSupervisor(coordinates.socketPath, timeoutMs);
100
+ if (negotiated.generation === null)
101
+ throw new Error("The supervised daemon generation is unavailable.");
102
+ const response = await supervisorRequest(coordinates.socketPath, {
103
+ version: negotiated.protocolVersion,
104
+ id: randomUUID(),
105
+ method: "supervisor.execute_bounded_tool",
106
+ params: {
107
+ entry_id: coordinates.entryId,
108
+ work_attempt_id: coordinates.workAttemptId,
109
+ execution_generation_id: coordinates.executionGenerationId,
110
+ ...(coordinates.providerTurnId ? { provider_turn_id: coordinates.providerTurnId } : {}),
111
+ daemon_generation: negotiated.generation,
112
+ mcp_request_id: input.mcpRequestId,
113
+ tool_name: input.toolName,
114
+ input: input.input,
115
+ },
116
+ }, null);
117
+ if (!response.ok) {
118
+ if (/Unsupported daemon method:\s*supervisor\.execute_bounded_tool/i.test(response.error ?? "")) {
119
+ return { state: "unsupported" };
120
+ }
121
+ throw new Error(response.error || "The daemon-owned supervised tool was rejected.");
122
+ }
123
+ const result = response.result && typeof response.result === "object"
124
+ ? response.result
125
+ : {};
126
+ const roomId = typeof result.room_id === "string" ? result.room_id.trim() : "";
127
+ if (!roomId || roomId.length > 1_024 || /[\u0000-\u001f\u007f]/.test(roomId)) {
128
+ throw new Error("The supervised daemon did not return valid exact room authority.");
129
+ }
130
+ return { state: "completed", roomId, result: result.result };
131
+ }
21
132
  export async function prepareCurrentSupervisedEffect(input, env = process.env, options = {}) {
22
133
  const coordinates = await requireCurrentSupervisedCoordinates(env, options);
23
134
  const timeoutMs = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
@@ -147,7 +258,8 @@ async function requireCurrentSupervisedCoordinates(env, options) {
147
258
  * authority for the exact worker session identity.
148
259
  */
149
260
  export async function borrowCurrentSupervisedWorkerCredential(env = process.env, options = {}) {
150
- if (env.LETAGENTS_SUPERVISED_BOUNDED_TURNS?.trim() !== "1") {
261
+ if (env.LETAGENTS_SUPERVISED_BOUNDED_TURNS?.trim() !== "1"
262
+ && env.LETAGENTS_EXECUTION_PROFILE?.trim() !== "supervised_mcp_polling") {
151
263
  return { state: "not_supervised" };
152
264
  }
153
265
  const seed = supervisedContextSession(env);
@@ -412,6 +524,8 @@ export function scheduleSupervisedWorkerCursorCheckpoint(session, roomCursor, en
412
524
  });
413
525
  }
414
526
  async function enqueueSupervisedWorkerCursorCheckpoint(session, roomCursor, env, options) {
527
+ if (parseRoomMessageNumber(roomCursor) === null)
528
+ return;
415
529
  const coordinates = await resolveSupervisorCoordinates(session, env, options);
416
530
  if (!coordinates)
417
531
  return;
@@ -509,13 +623,14 @@ function isNewerRoomCursor(candidate, current) {
509
623
  return false;
510
624
  const candidateNumber = parseRoomMessageNumber(candidate);
511
625
  const currentNumber = parseRoomMessageNumber(current);
512
- if (candidateNumber !== null && currentNumber !== null)
513
- return candidateNumber > currentNumber;
514
- return true;
626
+ if (candidateNumber === null)
627
+ return false;
628
+ if (currentNumber === null)
629
+ return true;
630
+ return candidateNumber > currentNumber;
515
631
  }
516
632
  function parseRoomMessageNumber(cursor) {
517
- const match = /^msg_(\d+)$/.exec(cursor.trim());
518
- return match ? BigInt(match[1]) : null;
633
+ return parsePositivePgIntegerScopedId(cursor, "msg");
519
634
  }
520
635
  /** Transport failures are retryable bookkeeping failures, not worker failures. */
521
636
  export function isRetryableSupervisorBridgeError(error) {
@@ -688,23 +803,26 @@ async function negotiateSupervisor(socketPath, timeoutMs) {
688
803
  const daemonIdentity = hasCompleteIdentity
689
804
  ? [result.generation, result.pid, result.started_at].join(":")
690
805
  : null;
691
- return { protocolVersion, daemonIdentity, generation: hasCompleteIdentity ? Number(result.generation) : null };
806
+ return { protocolVersion, daemonIdentity, generation: hasCompleteIdentity ? Number(result.generation) : null,
807
+ custodialPollingV1: result.capabilities?.custodialPollingV1 === true,
808
+ custodialPollingOffersV1: result.capabilities?.custodialPollingOffersV1 === true };
692
809
  }
693
810
  function supervisorRequest(socketPath, request, timeoutMs) {
694
811
  return new Promise((resolve, reject) => {
695
812
  const socket = createConnection(socketPath);
696
813
  let buffer = "";
697
814
  let finished = false;
698
- const timer = setTimeout(() => {
815
+ const timer = timeoutMs === null ? null : setTimeout(() => {
699
816
  socket.destroy();
700
817
  finish(() => reject(new Error("Timed out communicating with the supervisor daemon.")));
701
818
  }, timeoutMs);
702
- timer.unref();
819
+ timer?.unref();
703
820
  const finish = (operation) => {
704
821
  if (finished)
705
822
  return;
706
823
  finished = true;
707
- clearTimeout(timer);
824
+ if (timer)
825
+ clearTimeout(timer);
708
826
  operation();
709
827
  };
710
828
  socket.setEncoding("utf8");
@@ -1,4 +1,11 @@
1
1
  const TOOL_SURFACE_BY_PROFILE = {
2
+ supervised_mcp_polling: {
3
+ agentSessionLifecycle: false,
4
+ deliveryLoop: true,
5
+ onboarding: false,
6
+ rental: false,
7
+ roomResume: false,
8
+ },
2
9
  supervised_room_turn: {
3
10
  agentSessionLifecycle: false,
4
11
  deliveryLoop: false,
@@ -1,3 +1,4 @@
1
+ import { getDaemonToolExecutionContext } from "./daemon-tool-context.js";
1
2
  export const LETAGENTS_AGENT_SESSION_BEARER_ENV = "LETAGENTS_AGENT_SESSION_BEARER";
2
3
  export const LETAGENTS_SUPERVISED_BOUNDED_TURNS_ENV = "LETAGENTS_SUPERVISED_BOUNDED_TURNS";
3
4
  export class WorkerBearerRuntimeConfigurationError extends Error {
@@ -7,16 +8,22 @@ export class WorkerBearerRuntimeConfigurationError extends Error {
7
8
  }
8
9
  }
9
10
  export function getWorkerBearerRuntime() {
11
+ if (getDaemonToolExecutionContext())
12
+ return { mode: "supervised" };
10
13
  const bearer = process.env.LETAGENTS_AGENT_SESSION_BEARER?.trim();
11
14
  const supervised = process.env.LETAGENTS_SUPERVISED_BOUNDED_TURNS?.trim() === "1";
12
15
  const profile = process.env.LETAGENTS_EXECUTION_PROFILE?.trim();
16
+ const polling = profile === "supervised_mcp_polling";
13
17
  if (supervised !== (profile === "supervised_room_turn")) {
14
18
  return {
15
19
  mode: "invalid",
16
20
  error: "LETAGENTS_EXECUTION_PROFILE=supervised_room_turn and LETAGENTS_SUPERVISED_BOUNDED_TURNS=1 must be configured together.",
17
21
  };
18
22
  }
19
- if (!bearer && !supervised)
23
+ if (polling && (bearer || process.env.LETAGENTS_TOKEN?.trim())) {
24
+ return { mode: "invalid", error: "Custodial polling refuses environment credentials; borrow exact daemon worker authority." };
25
+ }
26
+ if (!bearer && !supervised && !polling)
20
27
  return { mode: "owner" };
21
28
  if (bearer && supervised) {
22
29
  return {
@@ -66,6 +73,13 @@ export function requireValidWorkerBearerRuntime() {
66
73
  return runtime;
67
74
  }
68
75
  export function isSupervisedBoundedTurn() {
76
+ return hasSupervisedWorkerAuthority() && !isCustodialPolling();
77
+ }
78
+ export function isCustodialPolling() {
79
+ return process.env.LETAGENTS_EXECUTION_PROFILE?.trim() === "supervised_mcp_polling";
80
+ }
81
+ /** Credential custody is independent of who owns room delivery. */
82
+ export function hasSupervisedWorkerAuthority() {
69
83
  return requireValidWorkerBearerRuntime().mode === "supervised";
70
84
  }
71
85
  export function workerModeDisabledToolResult(toolDescription = "This owner-auth onboarding tool") {
@@ -90,7 +104,8 @@ export function workerModeDisabledToolResult(toolDescription = "This owner-auth
90
104
  * asks it to do so.
91
105
  */
92
106
  export function supervisedBoundedDeliveryDisabledToolResult(toolName = "wait_for_messages") {
93
- if (process.env.LETAGENTS_SUPERVISED_BOUNDED_TURNS?.trim() !== "1") {
107
+ if (!getDaemonToolExecutionContext()
108
+ && process.env.LETAGENTS_SUPERVISED_BOUNDED_TURNS?.trim() !== "1") {
94
109
  return null;
95
110
  }
96
111
  return {
@@ -19,6 +19,7 @@ export function letAgentsRuntimeContract() {
19
19
  return {
20
20
  format: 1,
21
21
  profiles: {
22
+ supervised_mcp_polling: { contract: "custodial_polling_v1", tools: registeredToolNames("supervised_mcp_polling", "codex") },
22
23
  cursor_supervised_room_turn: {
23
24
  tools: registeredToolNames("supervised_room_turn", "cursor"),
24
25
  },
@@ -2,13 +2,13 @@
2
2
  // in src/mcp/server/runtime/* so tool modules can import focused responsibilities
3
3
  // without turning this file back into the runtime god module.
4
4
  import { isLocalRoomStorageEnabled as isStoredLocalRoomStorageEnabled, touchRoomSession as touchStoredRoomSession, } from "../local-state.js";
5
- import { isSupervisedBoundedTurn } from "./runtime/worker-bearer.js";
5
+ import { hasSupervisedWorkerAuthority } from "./runtime/worker-bearer.js";
6
6
  /** A daemon-supervised turn must always use its exact cloud worker route. */
7
7
  export async function isLocalRoomStorageEnabled(roomId) {
8
- return !isSupervisedBoundedTurn() && isStoredLocalRoomStorageEnabled(roomId);
8
+ return !hasSupervisedWorkerAuthority() && isStoredLocalRoomStorageEnabled(roomId);
9
9
  }
10
10
  export function touchRoomSession(roomId, lastMessageId) {
11
- if (!isSupervisedBoundedTurn())
11
+ if (!hasSupervisedWorkerAuthority())
12
12
  touchStoredRoomSession(roomId, lastMessageId);
13
13
  }
14
14
  export { API_URL, ApiError, apiCall, getAuthorizationHeader, getLetagentsToken, isMissingRouteError, parseApiErrorPayload, resolveApiPath, } from "./runtime/api.js";
@@ -16,10 +16,10 @@ export { clearAuthenticatedAccountCache, getAuthenticatedAccountCache, setAuthen
16
16
  export { RepoRoomAuthRequiredError, maybeHandleRepoRoomAuthRequired, startPendingDeviceAuth, toRepoRoomAuthRequiredResult, } from "./runtime/device-auth.js";
17
17
  export { AGENT_INSTANCE_UUID, currentAgentIdentity, currentAgentIdentityKey, detectAgentIdeLabel, detectAgentRuntimeLabel, ensureAgentIdentity, getConversationIdentity, getSessionLivenessRegistration, resolveOwnerContext, setConversationIdentity, storeCurrentAgentIdentity, toPublicAgentIdentity, withAgentIdentity, } from "./runtime/identity.js";
18
18
  export { agentSessionCredentials, buildAgentDeliveryHeaders, ensureLocalWorkerAgentSession, getAgentSessionRepoBranch, identityFromAgentSession, requireWorkerAgentSession, resolveAgentSession, resolveClientRequestedBase, resolveWorkerToolIdentity, toPublicAgentSession, WORKER_BEARER_AGENT_SESSION_ID, } from "./runtime/agent-sessions.js";
19
- export { appendIncludePromptOnly, getLastMessageId, normalizeOptionalToolString, toAgentReadableMessages, withJoinRoomAgentPrompt, } from "./runtime/messages.js";
20
- export { currentRoom, attachMcpServer, getCurrentSupervisedRoomAuthority, getFallbackProjectId, getTargetRoomId, rememberRoom, runWithCurrentSupervisedRoom, shutdownRuntime, toPublicRoomResponse, toPublicCurrentRoomState, toPublicRoomState, toPublicStoredRoomSession, toRoomState, touchCurrentRoom, withCanonicalRoomLink, } from "./runtime/room-state.js";
19
+ export { appendIncludePromptOnly, AGENT_MESSAGE_BODY_MAX_BYTES, AGENT_MESSAGE_OUTPUT_MAX_BYTES, boundAgentMessageOutput, getLastMessageId, normalizeOptionalToolString, toAgentReadableMessages, withJoinRoomAgentPrompt, } from "./runtime/messages.js";
20
+ export { currentRoom, currentRoomMatchesLocator, attachMcpServer, getCurrentSupervisedRoomAuthority, getFallbackProjectId, getTargetRoomId, rememberRoom, runWithCurrentSupervisedRoom, shutdownRuntime, toPublicRoomResponse, toPublicCurrentRoomState, toPublicRoomState, toPublicStoredRoomSession, toRoomState, touchCurrentRoom, withCanonicalRoomLink, } from "./runtime/room-state.js";
21
21
  export { getRememberedRoomPresence, heartbeatRoomPresence, syncRoomPresence, } from "./runtime/presence.js";
22
22
  export { roomScopedApiCall } from "./runtime/room-api.js";
23
23
  export { borrowSupervisedWorkerCredential, borrowCurrentSupervisedWorkerCredential, bindSupervisedWorkerSession, checkpointSupervisedWorkerCursor, isRetryableSupervisorBridgeError, scheduleSupervisedWorkerCursorCheckpoint, resolveCurrentSupervisedWorkerSession, } from "./runtime/supervisor-bridge.js";
24
24
  export { autoJoinFromContext, buildJoinResponse, createInviteRoom, getCurrentLiveSessionPayload, joinInviteCode, joinNamedRoom, joinRoomIdentifier, joinRoomIdentifierWithoutImplicitGitRefCreate, normalizeJoinSessionMode, } from "./runtime/rooms.js";
25
- export { clearPendingDeviceAuth, clearStoredAuth, clearStoredAuth as clearStoredAuthorization, endStoredAgentSession, getCurrentAgentSession, getLocalStatePath, getPendingDeviceAuth, getStoredAgentIdentity, getStoredAgentSession, getStoredAgentSessionsForRoomIdentity, getStoredAuth, getStoredCurrentRoom, getStoredRoomSession, listStoredCodexLiveSessions, saveAgentSession, setPendingDeviceAuth, setStoredAuth, setStoredAgentIdentity, addLocalChatMessage, addLocalTask, claimLocalTaskReviewLease, getLatestLocalChatMessages, getLocalChatMessages, getLocalTask, listLocalTasks, isLocalChatStorageEnabled, resolveLocalRoomStorageIdentifiers, releaseLocalTaskReviewLease, updateLocalTask, waitForLocalChatMessages, } from "../local-state.js";
25
+ export { clearPendingDeviceAuth, clearStoredAuth, clearStoredAuth as clearStoredAuthorization, endStoredAgentSession, getCurrentAgentSession, getLocalStatePath, getPendingDeviceAuth, getStoredAgentIdentity, getStoredAgentSession, getCurrentAgentSessionSnapshot, getStoredAgentSessionsForRoomIdentity, getStoredActiveAgentSessionsForRoom, getStoredAgentRoutingStateSnapshot, getStoredAuth, getStoredCurrentRoom, getStoredRoomSession, listStoredCodexLiveSessions, saveAgentSession, setPendingDeviceAuth, setStoredAuth, setStoredAgentIdentity, addLocalChatMessage, addLocalTask, claimLocalTaskReviewLease, getLatestLocalChatMessages, getLocalImportedRoutingAuthority, getLocalChatMessages, getLocalChatThreadRoutingMembership, getLocalTask, listLocalActiveTaskOwnerLeases, listLocalTasks, isLocalChatStorageEnabled, resolveLocalRoomStorageIdentifiers, releaseLocalTaskReviewLease, updateLocalTask, waitForLocalChatMessages, } from "../local-state.js";
@@ -1,5 +1,6 @@
1
+ import { parsePositivePgIntegerScopedId } from "../../../shared/message-contracts.mjs";
1
2
  import { runWithCurrentSupervisedRoom } from "./runtime/room-state.js";
2
- import { completeCurrentSupervisedEffect, prepareCurrentSupervisedEffect, } from "./runtime/supervisor-bridge.js";
3
+ import { completeCurrentSupervisedEffect, authorizeCustodialPolling, executeCurrentSupervisedTool, prepareCurrentSupervisedEffect, } from "./runtime/supervisor-bridge.js";
3
4
  const READ_TOOLS = new Set([
4
5
  "get_current_room",
5
6
  "check_repo",
@@ -15,6 +16,47 @@ const READ_TOOLS = new Set([
15
16
  "status_local_codex_session",
16
17
  "rental_list_requests",
17
18
  ]);
19
+ export function supervisedToolIsMutation(toolName) {
20
+ return !READ_TOOLS.has(toolName);
21
+ }
22
+ // The desktop daemon's local control protocol intentionally uses small bounded
23
+ // frames. A read result can be returned live to the provider without copying
24
+ // the entire payload into the durable effect journal.
25
+ const MAX_DURABLE_READ_RESULT_BYTES = 16 * 1024;
26
+ /** Receipt only the bounded page actually returned by wait, never an API tail
27
+ * beyond that page or a cursor inferred from its last visible message. */
28
+ function custodialWaitFrontier(result, inputCursor, roomId) {
29
+ const content = result.content;
30
+ if (result.isError || content.length !== 1 || content[0]?.type !== "text")
31
+ throw new Error("Custodial wait returned no valid bounded page.");
32
+ let output;
33
+ try {
34
+ output = JSON.parse(content[0].text);
35
+ }
36
+ catch {
37
+ throw new Error("Custodial wait returned no valid bounded page.");
38
+ }
39
+ if (!output || Array.isArray(output) || !Array.isArray(output.messages)
40
+ || (output.room_id !== undefined && output.room_id !== roomId))
41
+ throw new Error("Custodial wait returned no valid bounded page.");
42
+ const noProgress = output.messages.length === 0 && (output.truncated === undefined || output.truncated === false)
43
+ && (output.omitted_message_count === undefined || output.omitted_message_count === 0)
44
+ && (output.skipped_message_count === undefined || output.skipped_message_count === 0)
45
+ && (output.skipped_message_ids === undefined || (Array.isArray(output.skipped_message_ids) && output.skipped_message_ids.length === 0));
46
+ const frontier = output.last_observed_message_id;
47
+ if (frontier === undefined || frontier === null) {
48
+ if (!noProgress) {
49
+ throw new Error("Custodial wait is missing its bounded observed frontier.");
50
+ }
51
+ return inputCursor;
52
+ }
53
+ const number = parsePositivePgIntegerScopedId(frontier, "msg");
54
+ const inputNumber = parsePositivePgIntegerScopedId(inputCursor, "msg");
55
+ if (number === null || inputNumber === null || number < inputNumber || (number === inputNumber && !noProgress)) {
56
+ throw new Error("Custodial wait returned an invalid observed frontier.");
57
+ }
58
+ return String(frontier);
59
+ }
18
60
  function instruction(text, data = {}) {
19
61
  const payload = { ...data, instruction: text };
20
62
  return {
@@ -22,13 +64,25 @@ function instruction(text, data = {}) {
22
64
  structuredContent: payload,
23
65
  };
24
66
  }
67
+ export function durableCompletionResult(result, mutation) {
68
+ if (mutation)
69
+ return result;
70
+ const serializedBytes = Buffer.byteLength(JSON.stringify(result), "utf8");
71
+ if (serializedBytes <= MAX_DURABLE_READ_RESULT_BYTES)
72
+ return result;
73
+ return instruction("The read completed, but its large result was returned live instead of being copied into the durable journal. Issue a fresh read request if this exact request is replayed after a restart.", {
74
+ code: "SUPERVISED_READ_RESULT_NOT_RETAINED",
75
+ serialized_bytes: serializedBytes,
76
+ });
77
+ }
25
78
  const productionDependencies = {
79
+ executeTool: executeCurrentSupervisedTool,
26
80
  prepareEffect: prepareCurrentSupervisedEffect,
27
81
  completeEffect: completeCurrentSupervisedEffect,
28
82
  withRoom: runWithCurrentSupervisedRoom,
29
83
  };
30
84
  export function profileAwareToolServer(server, profile, dependencies = productionDependencies, supervisedProvider = process.env.LETAGENTS_SUPERVISOR_PROVIDER?.trim() || null) {
31
- if (profile !== "supervised_room_turn")
85
+ if (profile !== "supervised_room_turn" && profile !== "supervised_mcp_polling")
32
86
  return server;
33
87
  return new Proxy(server, {
34
88
  get(target, property, receiver) {
@@ -37,21 +91,65 @@ export function profileAwareToolServer(server, profile, dependencies = productio
37
91
  return typeof value === "function" ? value.bind(target) : value;
38
92
  }
39
93
  return (name, ...registration) => {
94
+ const mutation = supervisedToolIsMutation(name);
40
95
  const callback = registration.at(-1);
41
96
  if (typeof callback !== "function")
42
97
  throw new Error(`Tool ${name} has no callback.`);
43
98
  const wrapped = async (...call) => {
99
+ if (profile === "supervised_mcp_polling") {
100
+ const authorize = dependencies.authorizePolling ?? ((toolName, prior, wait) => authorizeCustodialPolling(toolName, prior, process.env, {}, wait));
101
+ const input = call[0];
102
+ const extra = (call.length > 1 ? call.at(-1) : undefined);
103
+ let wait;
104
+ if (name === "wait_for_messages") {
105
+ const requestId = extra?.requestId;
106
+ if (!(typeof requestId === "string" || (typeof requestId === "number" && Number.isSafeInteger(requestId)))) {
107
+ throw new Error("Custodial wait is missing its exact MCP request id.");
108
+ }
109
+ if (input?.after_message_id != null && typeof input.after_message_id !== "string")
110
+ throw new Error("Custodial wait requires a valid requested cursor.");
111
+ if ((input?.room_id != null && typeof input.room_id !== "string")
112
+ || (input?.agent_session_id != null && typeof input.agent_session_id !== "string"))
113
+ throw new Error("Custodial wait requires valid requested identity.");
114
+ wait = { mcpRequestId: requestId, roomCursor: input?.after_message_id ?? null,
115
+ ...(typeof input?.room_id === "string" ? { requestedRoomId: input.room_id } : {}),
116
+ ...(typeof input?.agent_session_id === "string" ? { requestedAgentSessionId: input.agent_session_id } : {}) };
117
+ }
118
+ const authority = await authorize(name, undefined, wait);
119
+ return dependencies.withRoom(authority.roomId, async () => {
120
+ if (input?.room_id && input.room_id !== authority.roomId)
121
+ throw new Error("Custodial tool room does not match its exact authority.");
122
+ if (name === "wait_for_messages") {
123
+ if (!authority.roomCursor)
124
+ throw new Error("Custodial polling has no durable cursor; refusing a tail fallback.");
125
+ call[0] = { ...input, after_message_id: authority.roomCursor };
126
+ }
127
+ const result = await callback(...call);
128
+ if (wait)
129
+ await authorize(name, authority, { ...wait,
130
+ offeredFrontier: custodialWaitFrontier(result, authority.roomCursor, authority.roomId) });
131
+ else if (name === "read_messages")
132
+ await authorize(name, authority);
133
+ return result;
134
+ });
135
+ }
44
136
  const extra = (call.at(-1) ?? {});
45
137
  const input = call.length > 1 ? call[0] : {};
46
138
  if (extra.requestId === undefined || extra.requestId === null || String(extra.requestId).trim() === "") {
47
139
  throw new Error(`Supervised tool ${name} is missing its MCP request id; refusing an effect that cannot be deduplicated safely.`);
48
140
  }
49
- const prepared = await dependencies.prepareEffect({
141
+ const executionRequest = {
50
142
  toolName: name,
51
143
  input,
52
144
  mcpRequestId: String(extra.requestId),
53
- mutation: !READ_TOOLS.has(name),
54
- });
145
+ };
146
+ if (dependencies.executeTool) {
147
+ const executed = await dependencies.executeTool(executionRequest);
148
+ if (executed.state === "completed") {
149
+ return dependencies.withRoom(executed.roomId, () => executed.result);
150
+ }
151
+ }
152
+ const prepared = await dependencies.prepareEffect({ ...executionRequest, mutation });
55
153
  return dependencies.withRoom(prepared.roomId, async () => {
56
154
  if (prepared.state === "completed")
57
155
  return prepared.result;
@@ -94,7 +192,10 @@ export function profileAwareToolServer(server, profile, dependencies = productio
94
192
  }
95
193
  // Completion transport is deliberately outside the callback catch.
96
194
  // A reporting failure must never relabel a successful action failed.
97
- await dependencies.completeEffect({ effectId: prepared.effectId, result });
195
+ await dependencies.completeEffect({
196
+ effectId: prepared.effectId,
197
+ result: durableCompletionResult(result, mutation),
198
+ });
98
199
  return result;
99
200
  });
100
201
  };