letagents 0.12.11 → 0.12.13

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 (49) hide show
  1. package/dist/mcp/git-remote.js +7 -7
  2. package/dist/mcp/local-state/agent-sessions.js +78 -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 +25 -12
  7. package/dist/mcp/server/runtime/agent-sessions.js +58 -9
  8. package/dist/mcp/server/runtime/api.js +40 -4
  9. package/dist/mcp/server/runtime/daemon-tool-context.js +11 -0
  10. package/dist/mcp/server/runtime/execution-profile.js +19 -0
  11. package/dist/mcp/server/runtime/identity/directory.js +2 -2
  12. package/dist/mcp/server/runtime/messages.js +55 -0
  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 +59 -3
  16. package/dist/mcp/server/runtime/rooms.js +67 -32
  17. package/dist/mcp/server/runtime/supervised-room-authority.js +8 -0
  18. package/dist/mcp/server/runtime/supervisor-bridge.js +702 -24
  19. package/dist/mcp/server/runtime/tool-surface-policy.js +26 -0
  20. package/dist/mcp/server/runtime/worker-bearer.js +44 -6
  21. package/dist/mcp/server/runtime-contract.js +27 -0
  22. package/dist/mcp/server/runtime.js +15 -4
  23. package/dist/mcp/server/supervised-tool-facade.js +134 -0
  24. package/dist/mcp/server/tools/agent-sessions.js +74 -7
  25. package/dist/mcp/server/tools/messages/index.js +3 -2
  26. package/dist/mcp/server/tools/messages/read-tool.js +54 -97
  27. package/dist/mcp/server/tools/messages/reasoning-tool.js +2 -0
  28. package/dist/mcp/server/tools/messages/send-tool.js +5 -0
  29. package/dist/mcp/server/tools/messages/status-tool.js +2 -0
  30. package/dist/mcp/server/tools/messages/wait-tool.js +344 -71
  31. package/dist/mcp/server/tools/onboarding/status-tool.js +9 -8
  32. package/dist/mcp/server/tools/rooms/inspection-tools.js +40 -22
  33. package/dist/mcp/server/tools/rooms/repo-initialization-tool.js +2 -1
  34. package/dist/mcp/server/tools/supervised-room-turn.js +42 -0
  35. package/dist/mcp/server/tools/tasks/board-tools.js +34 -2
  36. package/dist/mcp/server.js +14 -7
  37. package/dist/mcp/sse-client.js +163 -20
  38. package/dist/shared/activation-routing.js +187 -20
  39. package/dist/shared/agent-presence.js +6 -0
  40. package/dist/shared/desktop-release-manifest.js +63 -0
  41. package/dist/shared/desktop-release.js +60 -0
  42. package/dist/shared/scoped-ids.js +6 -0
  43. package/package.json +11 -3
  44. package/shared/message-contracts.d.mts +32 -0
  45. package/shared/message-contracts.mjs +109 -0
  46. package/shared/routing-aliases.d.mts +18 -0
  47. package/shared/routing-aliases.mjs +66 -0
  48. package/shared/sqlite-thread-routing.d.mts +72 -0
  49. package/shared/sqlite-thread-routing.mjs +1038 -0
@@ -9,7 +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 { requireValidWorkerBearerRuntime } from "./worker-bearer.js";
12
+ import { isSupervisedBoundedTurn, requireValidWorkerBearerRuntime } from "./worker-bearer.js";
13
13
  export function normalizeJoinSessionMode(value) {
14
14
  return String(value || "").trim().toLowerCase() === "live" ? "live" : "current";
15
15
  }
@@ -20,7 +20,7 @@ function isNotFoundApiError(error) {
20
20
  return error instanceof ApiError && error.status === 404;
21
21
  }
22
22
  function parseGeneratedGitRefRoomIdentifier(identifier) {
23
- const match = /^git-room:github\.com:([^/:\s]+\/[^/:\s]+):(branch|tag):[A-Za-z0-9_-]+$/.exec(identifier.trim());
23
+ const match = /^github\.com\/([^/\s]+\/[^/\s]+)\/focus\/git:(branch|tag):[A-Za-z0-9_-]+$/.exec(identifier.trim());
24
24
  if (!match) {
25
25
  return null;
26
26
  }
@@ -30,6 +30,9 @@ function parseGeneratedGitRefRoomIdentifier(identifier) {
30
30
  };
31
31
  }
32
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
+ }
33
36
  const roomId = joinedVia === "join_code" ? normalizeInviteCode(identifier) : identifier.trim();
34
37
  if (joinedVia !== "join_code" && await isLocalRoomStorageEnabled(roomId)) {
35
38
  if (options.allowCreate === false && !getStoredRoomSession(roomId)) {
@@ -64,6 +67,7 @@ export async function joinRoomIdentifier(identifier, joinedVia, options = {}) {
64
67
  const agentIdentity = await ensureAgentIdentity();
65
68
  const room = rememberRoom(toRoomState({
66
69
  room_id: joinedRoomId,
70
+ navigation_locator: joinedRoomId === roomId ? null : roomId,
67
71
  project_id: typeof response.project_id === "string" ? response.project_id : null,
68
72
  code: typeof response.code === "string"
69
73
  ? response.code
@@ -187,6 +191,9 @@ export async function joinRoomIdentifierWithoutImplicitGitRefCreate(identifier,
187
191
  }));
188
192
  }
189
193
  export async function createInviteRoom() {
194
+ if (isSupervisedBoundedTurn()) {
195
+ throw new Error("Room joins and creation are disabled during a daemon-supervised bounded turn.");
196
+ }
190
197
  const project = await apiCall("/projects", { method: "POST" });
191
198
  const roomId = typeof project.code === "string"
192
199
  ? project.code
@@ -254,7 +261,27 @@ export async function joinNamedRoom(name, sessionMode) {
254
261
  session_mode: sessionMode,
255
262
  });
256
263
  }
257
- function bindWorkerRoomFromContext() {
264
+ async function bindWorkerRoomLocator(roomLocator, source) {
265
+ const response = await apiCall(`/rooms/resolve/${encodeURIComponent(roomLocator)}`);
266
+ if (response.room_exists !== true) {
267
+ throw new ApiError(404, JSON.stringify({ error: "Room not found", code: "ROOM_NOT_FOUND" }));
268
+ }
269
+ const roomId = typeof response.canonical_room_id === "string"
270
+ ? response.canonical_room_id
271
+ : roomLocator;
272
+ return {
273
+ room: rememberRoom(toRoomState({
274
+ room_id: roomId,
275
+ navigation_locator: roomId === roomLocator ? null : roomLocator,
276
+ project_id: roomId,
277
+ display_name: roomId,
278
+ git_room: response.git_room ?? null,
279
+ joined_via: source === ".letagents.json" ? "config" : "git-remote",
280
+ })),
281
+ source,
282
+ };
283
+ }
284
+ async function bindWorkerRoomFromContext() {
258
285
  const configRoom = getRoomFromConfig();
259
286
  if (configRoom) {
260
287
  const gitContext = buildActiveGitRoomContext({
@@ -262,26 +289,26 @@ function bindWorkerRoomFromContext() {
262
289
  currentBranch: getGitCurrentBranch(),
263
290
  defaultBranch: getGitDefaultBranch(),
264
291
  });
265
- const roomId = gitContext.activeRoom ?? configRoom;
266
- return {
267
- room: rememberRoom(toRoomState({
268
- room_id: roomId,
269
- display_name: roomId,
270
- joined_via: "config",
271
- })),
272
- source: ".letagents.json",
273
- };
292
+ const roomId = gitContext.activeRoomLocator ?? configRoom;
293
+ try {
294
+ return await bindWorkerRoomLocator(roomId, ".letagents.json");
295
+ }
296
+ catch (error) {
297
+ if (roomId === configRoom || !isNotFoundApiError(error))
298
+ throw error;
299
+ return bindWorkerRoomLocator(configRoom, ".letagents.json");
300
+ }
274
301
  }
275
302
  const gitContext = getGitRoomContext();
276
- if (gitContext.activeRoom) {
277
- return {
278
- room: rememberRoom(toRoomState({
279
- room_id: gitContext.activeRoom,
280
- display_name: gitContext.activeRoom,
281
- joined_via: "git-remote",
282
- })),
283
- source: "git remote",
284
- };
303
+ if (gitContext.activeRoomLocator) {
304
+ try {
305
+ return await bindWorkerRoomLocator(gitContext.activeRoomLocator, "git remote");
306
+ }
307
+ catch (error) {
308
+ if (!gitContext.repoRoom || !isNotFoundApiError(error))
309
+ throw error;
310
+ return bindWorkerRoomLocator(gitContext.repoRoom, "git remote");
311
+ }
285
312
  }
286
313
  const savedCurrentRoom = getStoredCurrentRoom();
287
314
  if (!savedCurrentRoom) {
@@ -301,10 +328,18 @@ function bindWorkerRoomFromContext() {
301
328
  }
302
329
  export async function autoJoinFromContext() {
303
330
  try {
304
- if (requireValidWorkerBearerRuntime().mode === "worker") {
305
- const bound = bindWorkerRoomFromContext();
331
+ const workerRuntime = requireValidWorkerBearerRuntime();
332
+ if (workerRuntime.mode === "supervised") {
333
+ // The exact room may change after a durable daemon-owned room move.
334
+ // Bind it per tool effect from the supervisor response, never from
335
+ // ambient repository, persisted state, or launch-time environment.
336
+ console.error("ℹ️ Daemon-supervised bounded turn leaves room selection to its exact supervisor context.");
337
+ return;
338
+ }
339
+ if (workerRuntime.mode === "worker") {
340
+ const bound = await bindWorkerRoomFromContext();
306
341
  if (bound) {
307
- console.error(`🏠 Bound worker bearer to room '${bound.room.room_id}' (from ${bound.source}; no join/create request).`);
342
+ console.error(`🏠 Bound worker bearer to existing room '${bound.room.room_id}' (from ${bound.source}).`);
308
343
  }
309
344
  else {
310
345
  console.error("ℹ️ Worker bearer has no .letagents.json, git remote, or saved room to bind locally.");
@@ -318,17 +353,17 @@ export async function autoJoinFromContext() {
318
353
  currentBranch: getGitCurrentBranch(),
319
354
  defaultBranch: getGitDefaultBranch(),
320
355
  });
321
- if (gitContext.activeRefRoom && gitContext.currentBranch) {
322
- const joinedBranchRoom = await joinExistingRoomIdentifier(gitContext.activeRefRoom, "config");
356
+ if (gitContext.activeRefRoomLocator && gitContext.currentBranch) {
357
+ const joinedBranchRoom = await joinExistingRoomIdentifier(gitContext.activeRefRoomLocator, "config");
323
358
  if (joinedBranchRoom) {
324
359
  await ensureAgentIdentity();
325
- console.error(`🏠 Auto-joined existing branch room '${gitContext.activeRefRoom}' (from .letagents.json + branch '${gitContext.currentBranch}')`);
360
+ console.error(`🏠 Auto-joined existing branch room '${gitContext.activeRefRoomLocator}' (from .letagents.json + branch '${gitContext.currentBranch}')`);
326
361
  return;
327
362
  }
328
363
  }
329
364
  await joinRoomIdentifier(configRoom, "config");
330
365
  await ensureAgentIdentity();
331
- const branchNote = gitContext.activeRefRoom && gitContext.currentBranch
366
+ const branchNote = gitContext.activeRefRoomLocator && gitContext.currentBranch
332
367
  ? `; branch '${gitContext.currentBranch}' has no existing Git Room`
333
368
  : "";
334
369
  console.error(`🏠 Auto-joined room '${configRoom}' (from .letagents.json${branchNote})`);
@@ -336,17 +371,17 @@ export async function autoJoinFromContext() {
336
371
  }
337
372
  const gitContext = getGitRoomContext();
338
373
  if (gitContext.repoRoom) {
339
- if (gitContext.activeRefRoom && gitContext.currentBranch) {
340
- const joinedBranchRoom = await joinExistingRoomIdentifier(gitContext.activeRefRoom, "git-remote");
374
+ if (gitContext.activeRefRoomLocator && gitContext.currentBranch) {
375
+ const joinedBranchRoom = await joinExistingRoomIdentifier(gitContext.activeRefRoomLocator, "git-remote");
341
376
  if (joinedBranchRoom) {
342
377
  await ensureAgentIdentity();
343
- console.error(`🏠 Auto-joined existing branch room '${gitContext.activeRefRoom}' (inferred from git remote and branch '${gitContext.currentBranch}' — consider adding a .letagents.json)`);
378
+ console.error(`🏠 Auto-joined existing branch room '${gitContext.activeRefRoomLocator}' (inferred from git remote and branch '${gitContext.currentBranch}' — consider adding a .letagents.json)`);
344
379
  return;
345
380
  }
346
381
  }
347
382
  await joinRoomIdentifier(gitContext.repoRoom, "git-remote");
348
383
  await ensureAgentIdentity();
349
- const branchNote = gitContext.activeRefRoom && gitContext.currentBranch
384
+ const branchNote = gitContext.activeRefRoomLocator && gitContext.currentBranch
350
385
  ? `; branch '${gitContext.currentBranch}' has no existing Git Room`
351
386
  : "";
352
387
  console.error(`🏠 Auto-joined room '${gitContext.repoRoom}' (inferred from git remote${branchNote} — consider adding a .letagents.json)`);
@@ -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
+ }