letagents 0.12.10 → 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.
Files changed (46) hide show
  1. package/README.md +2 -0
  2. package/dist/api/board-intent-payloads.js +25 -0
  3. package/dist/mcp/codex-session/runtime-bridge.js +42 -25
  4. package/dist/mcp/local-state/agent-sessions.js +15 -0
  5. package/dist/mcp/local-state/local-chat.js +1 -1
  6. package/dist/mcp/rental-tools/context.js +30 -0
  7. package/dist/mcp/server/register-tools.js +23 -12
  8. package/dist/mcp/server/runtime/agent-sessions.js +81 -2
  9. package/dist/mcp/server/runtime/api.js +63 -13
  10. package/dist/mcp/server/runtime/execution-profile.js +19 -0
  11. package/dist/mcp/server/runtime/identity/directory.js +9 -2
  12. package/dist/mcp/server/runtime/identity.js +2 -1
  13. package/dist/mcp/server/runtime/presence.js +4 -2
  14. package/dist/mcp/server/runtime/room-api.js +24 -7
  15. package/dist/mcp/server/runtime/room-state.js +46 -0
  16. package/dist/mcp/server/runtime/rooms.js +70 -0
  17. package/dist/mcp/server/runtime/supervised-room-authority.js +8 -0
  18. package/dist/mcp/server/runtime/supervisor-bridge.js +731 -0
  19. package/dist/mcp/server/runtime/tool-surface-policy.js +26 -0
  20. package/dist/mcp/server/runtime/worker-bearer.js +101 -0
  21. package/dist/mcp/server/runtime-contract.js +27 -0
  22. package/dist/mcp/server/runtime.js +14 -3
  23. package/dist/mcp/server/supervised-tool-facade.js +105 -0
  24. package/dist/mcp/server/tools/agent-sessions.js +113 -5
  25. package/dist/mcp/server/tools/messages/index.js +3 -2
  26. package/dist/mcp/server/tools/messages/message-lookup.js +75 -0
  27. package/dist/mcp/server/tools/messages/read-tool.js +1 -1
  28. package/dist/mcp/server/tools/messages/send-tool.js +5 -45
  29. package/dist/mcp/server/tools/messages/wait-tool.js +212 -91
  30. package/dist/mcp/server/tools/onboarding/device-auth-tools.js +10 -0
  31. package/dist/mcp/server/tools/onboarding/name-tool.js +5 -1
  32. package/dist/mcp/server/tools/onboarding/status-tool.js +18 -0
  33. package/dist/mcp/server/tools/rental/context-tools.js +9 -1
  34. package/dist/mcp/server/tools/rooms/inspection-tools.js +47 -18
  35. package/dist/mcp/server/tools/supervised-room-turn.js +42 -0
  36. package/dist/mcp/server/tools/tasks/board-intent-tools.js +7 -2
  37. package/dist/mcp/server/tools/tasks/index.js +2 -0
  38. package/dist/mcp/server/tools/tasks/verdict-tools.js +51 -0
  39. package/dist/mcp/server.js +16 -7
  40. package/dist/mcp/sse-client.js +3 -3
  41. package/dist/shared/activation-routing.js +57 -4
  42. package/dist/shared/agent-presence.js +1 -0
  43. package/dist/shared/agent-session-bearer.js +28 -0
  44. package/dist/shared/board-manager-failover.js +16 -0
  45. package/dist/shared/room-agent-prompts.js +2 -2
  46. package/package.json +24 -15
@@ -3,6 +3,8 @@ import { getStoredAgentIdentity, saveRoomSession, touchRoomSession, } from "../.
3
3
  import { getCanonicalRoomWebPath, } from "../../room-id.js";
4
4
  import { API_URL, getLetagentsToken } from "./api.js";
5
5
  import { AGENT_INSTANCE_UUID, currentAgentIdentity, currentAgentIdentityKey, } from "./identity.js";
6
+ import { isSupervisedBoundedTurn } from "./worker-bearer.js";
7
+ import { getCurrentSupervisedRoomAuthority, runWithSupervisedRoomAuthority, } from "./supervised-room-authority.js";
6
8
  let mcpServer = null;
7
9
  let sseClient = null;
8
10
  export let currentRoom = null;
@@ -85,6 +87,8 @@ export function toPublicRoomResponse(response, fallbackRoomId) {
85
87
  }
86
88
  export function rememberRoom(state, lastMessageId) {
87
89
  currentRoom = state;
90
+ if (isSupervisedBoundedTurn())
91
+ return state;
88
92
  saveRoomSession({
89
93
  room_id: state.room_id,
90
94
  project_id: state.project_id ?? null,
@@ -109,14 +113,56 @@ export function rememberRoom(state, lastMessageId) {
109
113
  return state;
110
114
  }
111
115
  export function touchCurrentRoom(lastMessageId) {
116
+ if (isSupervisedBoundedTurn())
117
+ return;
112
118
  if (!currentRoom) {
113
119
  return;
114
120
  }
115
121
  touchRoomSession(currentRoom.room_id, lastMessageId);
116
122
  }
117
123
  export function getTargetRoomId(roomId) {
124
+ if (isSupervisedBoundedTurn()) {
125
+ const exactRoomAuthority = getCurrentSupervisedRoomAuthority();
126
+ if (!exactRoomAuthority) {
127
+ throw new Error("The daemon-supervised tool has not received its exact room authority.");
128
+ }
129
+ if (roomId && roomId !== exactRoomAuthority) {
130
+ throw new Error(`The daemon-supervised tool is authorized for ${exactRoomAuthority}, not ${roomId}.`);
131
+ }
132
+ return exactRoomAuthority;
133
+ }
118
134
  return roomId || currentRoom?.room_id || null;
119
135
  }
136
+ /** The last room authority returned by this process's exact daemon effect. */
137
+ export { getCurrentSupervisedRoomAuthority } from "./supervised-room-authority.js";
138
+ /** Public room metadata without inventing join provenance or locality. */
139
+ export function toPublicCurrentRoomState() {
140
+ const exactRoomAuthority = getCurrentSupervisedRoomAuthority();
141
+ if (!exactRoomAuthority)
142
+ return toPublicRoomState(currentRoom);
143
+ return {
144
+ ...toPublicRoomState(toRoomState({ room_id: exactRoomAuthority, joined_via: "join_room" })),
145
+ joined_via: null,
146
+ is_local: null,
147
+ };
148
+ }
149
+ /**
150
+ * Bind only the in-memory default used by one supervised MCP process. The
151
+ * daemon response is the authority; this performs no join, storage, SSE, or
152
+ * repository inspection and can safely rebind after a durable room move.
153
+ */
154
+ export function runWithCurrentSupervisedRoom(roomId, callback) {
155
+ if (!isSupervisedBoundedTurn()) {
156
+ throw new Error("Only a daemon-supervised bounded turn can bind supervisor room authority.");
157
+ }
158
+ const normalized = roomId.trim();
159
+ if (!normalized || normalized.length > 1_024 || /[\u0000-\u001f\u007f]/.test(normalized)) {
160
+ throw new Error("The daemon-supervised room authority is malformed.");
161
+ }
162
+ return runWithSupervisedRoomAuthority(normalized, callback);
163
+ }
120
164
  export function getFallbackProjectId() {
165
+ if (isSupervisedBoundedTurn())
166
+ return null;
121
167
  return currentRoom?.project_id ?? null;
122
168
  }
@@ -9,6 +9,7 @@ import { ensureAgentIdentity, toPublicAgentIdentity, withAgentIdentity, } from "
9
9
  import { withJoinRoomAgentPrompt } from "./messages.js";
10
10
  import { syncRoomPresence } from "./presence.js";
11
11
  import { rememberRoom, toPublicRoomResponse, toRoomState, } from "./room-state.js";
12
+ import { isSupervisedBoundedTurn, requireValidWorkerBearerRuntime } from "./worker-bearer.js";
12
13
  export function normalizeJoinSessionMode(value) {
13
14
  return String(value || "").trim().toLowerCase() === "live" ? "live" : "current";
14
15
  }
@@ -29,6 +30,9 @@ function parseGeneratedGitRefRoomIdentifier(identifier) {
29
30
  };
30
31
  }
31
32
  export async function joinRoomIdentifier(identifier, joinedVia, options = {}) {
33
+ if (isSupervisedBoundedTurn()) {
34
+ throw new Error("Room joins and creation are disabled during a daemon-supervised bounded turn.");
35
+ }
32
36
  const roomId = joinedVia === "join_code" ? normalizeInviteCode(identifier) : identifier.trim();
33
37
  if (joinedVia !== "join_code" && await isLocalRoomStorageEnabled(roomId)) {
34
38
  if (options.allowCreate === false && !getStoredRoomSession(roomId)) {
@@ -186,6 +190,9 @@ export async function joinRoomIdentifierWithoutImplicitGitRefCreate(identifier,
186
190
  }));
187
191
  }
188
192
  export async function createInviteRoom() {
193
+ if (isSupervisedBoundedTurn()) {
194
+ throw new Error("Room joins and creation are disabled during a daemon-supervised bounded turn.");
195
+ }
189
196
  const project = await apiCall("/projects", { method: "POST" });
190
197
  const roomId = typeof project.code === "string"
191
198
  ? project.code
@@ -253,8 +260,71 @@ export async function joinNamedRoom(name, sessionMode) {
253
260
  session_mode: sessionMode,
254
261
  });
255
262
  }
263
+ function bindWorkerRoomFromContext() {
264
+ const configRoom = getRoomFromConfig();
265
+ if (configRoom) {
266
+ const gitContext = buildActiveGitRoomContext({
267
+ repoRoom: configRoom,
268
+ currentBranch: getGitCurrentBranch(),
269
+ defaultBranch: getGitDefaultBranch(),
270
+ });
271
+ const roomId = gitContext.activeRoom ?? configRoom;
272
+ return {
273
+ room: rememberRoom(toRoomState({
274
+ room_id: roomId,
275
+ display_name: roomId,
276
+ joined_via: "config",
277
+ })),
278
+ source: ".letagents.json",
279
+ };
280
+ }
281
+ const gitContext = getGitRoomContext();
282
+ if (gitContext.activeRoom) {
283
+ return {
284
+ room: rememberRoom(toRoomState({
285
+ room_id: gitContext.activeRoom,
286
+ display_name: gitContext.activeRoom,
287
+ joined_via: "git-remote",
288
+ })),
289
+ source: "git remote",
290
+ };
291
+ }
292
+ const savedCurrentRoom = getStoredCurrentRoom();
293
+ if (!savedCurrentRoom) {
294
+ return null;
295
+ }
296
+ return {
297
+ room: rememberRoom(toRoomState({
298
+ room_id: savedCurrentRoom.room_id,
299
+ project_id: savedCurrentRoom.project_id,
300
+ code: savedCurrentRoom.code,
301
+ display_name: savedCurrentRoom.display_name,
302
+ git_room: savedCurrentRoom.git_room,
303
+ joined_via: savedCurrentRoom.joined_via,
304
+ })),
305
+ source: "saved local room state",
306
+ };
307
+ }
256
308
  export async function autoJoinFromContext() {
257
309
  try {
310
+ const workerRuntime = requireValidWorkerBearerRuntime();
311
+ if (workerRuntime.mode === "supervised") {
312
+ // The exact room may change after a durable daemon-owned room move.
313
+ // Bind it per tool effect from the supervisor response, never from
314
+ // ambient repository, persisted state, or launch-time environment.
315
+ console.error("ℹ️ Daemon-supervised bounded turn leaves room selection to its exact supervisor context.");
316
+ return;
317
+ }
318
+ if (workerRuntime.mode === "worker") {
319
+ const bound = bindWorkerRoomFromContext();
320
+ if (bound) {
321
+ console.error(`🏠 Bound worker bearer to room '${bound.room.room_id}' (from ${bound.source}; no join/create request).`);
322
+ }
323
+ else {
324
+ console.error("ℹ️ Worker bearer has no .letagents.json, git remote, or saved room to bind locally.");
325
+ }
326
+ return;
327
+ }
258
328
  const configRoom = getRoomFromConfig();
259
329
  if (configRoom) {
260
330
  const gitContext = buildActiveGitRoomContext({
@@ -0,0 +1,8 @@
1
+ import { AsyncLocalStorage } from "node:async_hooks";
2
+ const exactRoomAuthority = new AsyncLocalStorage();
3
+ export function getCurrentSupervisedRoomAuthority() {
4
+ return exactRoomAuthority.getStore() ?? null;
5
+ }
6
+ export function runWithSupervisedRoomAuthority(roomId, callback) {
7
+ return exactRoomAuthority.run(roomId, callback);
8
+ }