letagents 0.12.11 → 0.12.12

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.
@@ -3,7 +3,7 @@ import { join } from "path";
3
3
  import { z } from "zod";
4
4
  import { getRoomFromConfig } from "../../../config-reader.js";
5
5
  import { buildActiveGitRoomContext, getGitCurrentBranch, getGitDefaultBranch, getGitRoomContext, } from "../../../git-remote.js";
6
- import { currentAgentIdentity, currentAgentIdentityKey, currentRoom, getConversationIdentity, getCurrentLiveSessionPayload, getStoredAgentIdentity, listStoredCodexLiveSessions, toPublicAgentIdentity, toPublicRoomState, withJoinRoomAgentPrompt, } from "../../runtime.js";
6
+ import { currentAgentIdentity, currentAgentIdentityKey, currentRoom, getConversationIdentity, getCurrentLiveSessionPayload, getStoredAgentIdentity, listStoredCodexLiveSessions, toPublicAgentIdentity, toPublicCurrentRoomState, withJoinRoomAgentPrompt, } from "../../runtime.js";
7
7
  import { requireValidWorkerBearerRuntime } from "../../runtime/worker-bearer.js";
8
8
  import { findExistingConfig, resolveGitRoot, } from "../../repo-context.js";
9
9
  import { jsonToolResponse } from "./response.js";
@@ -29,16 +29,17 @@ export function registerRoomInspectionTools(server) {
29
29
  }
30
30
  export async function getCurrentRoomPayload(conversationId) {
31
31
  const runtime = requireValidWorkerBearerRuntime();
32
- const workerAuth = runtime.mode === "worker"
33
- ? { source: "worker_bearer", expires_at: null, account: null }
32
+ const publicCurrentRoom = toPublicCurrentRoomState();
33
+ const workerAuth = runtime.mode !== "owner"
34
+ ? { source: runtime.mode === "worker" ? "worker_bearer" : "daemon_supervised", expires_at: null, account: null }
34
35
  : null;
35
- const localCodexDetails = runtime.mode === "worker"
36
+ const localCodexDetails = runtime.mode !== "owner"
36
37
  ? { current_local_codex_session: null, local_codex_session_count: 0 }
37
38
  : {
38
39
  current_local_codex_session: getCurrentLiveSessionPayload(currentRoom?.room_id),
39
40
  local_codex_session_count: listStoredCodexLiveSessions().length,
40
41
  };
41
- if (!currentRoom) {
42
+ if (!publicCurrentRoom) {
42
43
  return {
43
44
  connected: false,
44
45
  message: "Not currently in any room",
@@ -46,12 +47,13 @@ export async function getCurrentRoomPayload(conversationId) {
46
47
  ...(workerAuth ? { auth: workerAuth } : {}),
47
48
  };
48
49
  }
49
- const auth = runtime.mode === "worker"
50
+ const auth = runtime.mode !== "owner"
50
51
  ? null
51
52
  : (await ownerAuthStoreLoader()).getStoredAuth();
52
- return withJoinRoomAgentPrompt({
53
+ const payload = {
53
54
  connected: true,
54
- ...toPublicRoomState(currentRoom),
55
+ ...publicCurrentRoom,
56
+ ...(runtime.mode === "supervised" ? { room_binding: "daemon_supervised" } : {}),
55
57
  ...localCodexDetails,
56
58
  agent_identity: toPublicAgentIdentity(getConversationIdentity(conversationId)
57
59
  ?? currentAgentIdentity
@@ -63,9 +65,14 @@ export async function getCurrentRoomPayload(conversationId) {
63
65
  account: auth.account ?? null,
64
66
  }
65
67
  : null),
66
- });
68
+ };
69
+ // A daemon-supervised worker is already bound to its exact authorized room.
70
+ // A normal join prompt is both false and dangerous here because it can lead
71
+ // the provider to request an unrelated room move.
72
+ return runtime.mode === "supervised" ? payload : withJoinRoomAgentPrompt(payload);
67
73
  }
68
74
  function getRepoInspectionPayload(targetDir) {
75
+ const runtime = requireValidWorkerBearerRuntime();
69
76
  const startDir = targetDir || process.cwd();
70
77
  const repoRoot = resolveGitRoot(startDir);
71
78
  const configDir = repoRoot ? findExistingConfig(startDir) : null;
@@ -95,10 +102,16 @@ function getRepoInspectionPayload(targetDir) {
95
102
  git_current_branch: gitContext?.currentBranch ?? configGitContext?.currentBranch ?? null,
96
103
  git_default_branch: gitContext?.defaultBranch ?? configGitContext?.defaultBranch ?? null,
97
104
  detected_room_from_context: detectedRoom ?? null,
98
- current_room: toPublicRoomState(currentRoom),
99
- current_room_scope: currentRoomScope(currentRoomMatchesContext, detectedRoom),
100
- warning: repoWarning({ repoRoot, detectedRoom, currentRoomMatchesContext }),
101
- join_hint: joinHint({ repoRoot, detectedRoom, currentRoomMatchesContext }),
105
+ current_room: toPublicCurrentRoomState(),
106
+ current_room_scope: runtime.mode === "supervised"
107
+ ? "daemon_supervised_exact_room"
108
+ : currentRoomScope(currentRoomMatchesContext, detectedRoom),
109
+ warning: runtime.mode === "supervised"
110
+ ? null
111
+ : repoWarning({ repoRoot, detectedRoom, currentRoomMatchesContext }),
112
+ join_hint: runtime.mode === "supervised"
113
+ ? null
114
+ : joinHint({ repoRoot, detectedRoom, currentRoomMatchesContext }),
102
115
  };
103
116
  }
104
117
  function readConfigContents(configPath) {
@@ -0,0 +1,42 @@
1
+ import { z } from "zod";
2
+ const MAX_ROOM_REPLY_BYTES = 32 * 1024;
3
+ function completionResponse(outcome) {
4
+ const payload = {
5
+ accepted: true,
6
+ outcome,
7
+ instruction: "The daemon recorded this exact turn completion. End the provider turn without sending the activating reply through another tool.",
8
+ };
9
+ return {
10
+ content: [{ type: "text", text: JSON.stringify(payload, null, 2) }],
11
+ structuredContent: payload,
12
+ };
13
+ }
14
+ /**
15
+ * Cursor's documented terminal `result` is the concatenation of every
16
+ * assistant delta, including progress narration. A supervised provider must
17
+ * therefore propose its public reply over a distinct, turn-scoped channel.
18
+ *
19
+ * This tool is registered only for `supervised_room_turn`. The normal
20
+ * supervised tool facade binds it to the exact provider turn and commits its
21
+ * request/result through the durable bounded-effect journal before the daemon
22
+ * will consider it publishable.
23
+ */
24
+ export function registerSupervisedRoomTurnTools(server) {
25
+ server.tool("complete_room_turn", "Record the single public completion for this supervised room turn. Call exactly once after all work is finished. Progress text outside this tool is live activity only and is never published as the room reply.", {
26
+ outcome: z.enum(["reply", "no_reply"]).describe("Use reply to publish one answer, or no_reply when the activating message needs no response."),
27
+ text: z.string().optional().describe("The concise public room reply. Required only when outcome is reply."),
28
+ }, async ({ outcome, text }) => {
29
+ const normalized = text?.trim() ?? "";
30
+ if (outcome === "reply") {
31
+ if (!normalized)
32
+ throw new Error("complete_room_turn requires non-empty text when outcome is reply.");
33
+ if (Buffer.byteLength(normalized, "utf8") > MAX_ROOM_REPLY_BYTES) {
34
+ throw new Error("complete_room_turn reply exceeds the 32 KiB public-message limit.");
35
+ }
36
+ }
37
+ else if (normalized) {
38
+ throw new Error("complete_room_turn must omit text when outcome is no_reply.");
39
+ }
40
+ return completionResponse(outcome);
41
+ });
42
+ }
@@ -5,14 +5,21 @@ import { registerRoomResources } from "./server/resources.js";
5
5
  import { registerTools } from "./server/register-tools.js";
6
6
  import { attachMcpServer, autoJoinFromContext, shutdownRuntime } from "./server/runtime.js";
7
7
  import { requireValidWorkerBearerRuntime } from "./server/runtime/worker-bearer.js";
8
- const server = new McpServer({
9
- name: "letagents",
10
- version: "0.2.0",
11
- });
12
- attachMcpServer(server);
13
- registerRoomResources(server);
14
- registerTools(server);
8
+ import { executionProfile } from "./server/runtime/execution-profile.js";
9
+ import { LETAGENTS_RUNTIME_CONTRACT_ARG, letAgentsRuntimeContract, } from "./server/runtime-contract.js";
15
10
  async function main() {
11
+ if (process.argv.slice(2).includes(LETAGENTS_RUNTIME_CONTRACT_ARG)) {
12
+ process.stdout.write(`${JSON.stringify(letAgentsRuntimeContract())}\n`);
13
+ return;
14
+ }
15
+ const server = new McpServer({
16
+ name: "letagents",
17
+ version: "0.2.0",
18
+ });
19
+ attachMcpServer(server);
20
+ registerRoomResources(server);
21
+ const profile = executionProfile();
22
+ registerTools(server, profile);
16
23
  requireValidWorkerBearerRuntime();
17
24
  const transport = new StdioServerTransport();
18
25
  await server.connect(transport);
@@ -33,6 +33,50 @@ export function attachAgentMessageActivation(message, identity, context = {}) {
33
33
  },
34
34
  };
35
35
  }
36
+ /**
37
+ * Send-time receipts are the activation authority. For a snapshot-bearing
38
+ * message, a receipt activates and the absence of one is the durable
39
+ * send-time "silent" — never re-promoted by re-running the router against
40
+ * later task/thread/session state, which would create a second authority.
41
+ * Only messages that predate routing snapshots keep the lazy per-reader
42
+ * decision, so legacy backlog mentions still activate rotated sessions.
43
+ */
44
+ export function attachAgentMessageActivationsFromReceipts(messages, identity, receiptsMap, snapshotNumbers, context = {}) {
45
+ if (!identity || identity.session_kind !== "worker") {
46
+ return [...messages];
47
+ }
48
+ return messages.map((message) => {
49
+ const msgIdStr = String(message.id ?? "");
50
+ const msgNum = parseInt(msgIdStr.replace(/^msg_/, ""), 10);
51
+ const receipt = !isNaN(msgNum) ? receiptsMap.get(msgNum) || receiptsMap.get(msgIdStr) : null;
52
+ if (receipt) {
53
+ const reason = receipt.activation_reason;
54
+ return {
55
+ ...message,
56
+ activation: {
57
+ for_current_agent: {
58
+ decision: "activate",
59
+ reason: reason || "explicit_mention",
60
+ addressed: true,
61
+ },
62
+ },
63
+ };
64
+ }
65
+ if (!isNaN(msgNum) && snapshotNumbers.has(msgNum)) {
66
+ return {
67
+ ...message,
68
+ activation: {
69
+ for_current_agent: {
70
+ decision: "silent",
71
+ reason: "unaddressed",
72
+ addressed: false,
73
+ },
74
+ },
75
+ };
76
+ }
77
+ return attachAgentMessageActivation(message, identity, context);
78
+ });
79
+ }
36
80
  export function attachAgentMessageActivations(messages, identity, context = {}) {
37
81
  if (!identity || identity.session_kind !== "worker") {
38
82
  return [...messages];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "letagents",
3
- "version": "0.12.11",
3
+ "version": "0.12.12",
4
4
  "description": "Let Agents Chat — MCP server for AI agent communication",
5
5
  "type": "module",
6
6
  "main": "dist/mcp/server.js",
@@ -62,8 +62,12 @@
62
62
  "ws": "8.21.0"
63
63
  },
64
64
  "overrides": {
65
+ "@hono/node-server": "2.0.10",
65
66
  "@esbuild-kit/core-utils": {
66
67
  "esbuild": "^0.25.12"
67
- }
68
+ },
69
+ "fast-uri": "3.1.5",
70
+ "hono": "4.12.34",
71
+ "ip-address": "10.3.1"
68
72
  }
69
73
  }