letagents 0.12.25 → 0.12.26

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.
@@ -7,7 +7,7 @@ import { durableCompletionResult, supervisedToolIsMutation } from "./supervised-
7
7
  const handlersByProvider = new Map();
8
8
  const SUPERVISED_PROVIDERS = new Set(["claude-code", "cursor", "codex", "open-model"]);
9
9
  const WORKSPACE_SCOPED_TOOLS = new Set(["check_repo", "check_repo_visibility", "initialize_repo"]);
10
- function handlersForProvider(provider) {
10
+ function handlersForProvider(provider, apiUrl) {
11
11
  const normalizedProvider = provider.trim().toLowerCase();
12
12
  const existing = handlersByProvider.get(normalizedProvider);
13
13
  if (existing)
@@ -26,7 +26,7 @@ function handlersForProvider(provider) {
26
26
  return {};
27
27
  },
28
28
  };
29
- registerTools(recorder, "supervised_room_turn", normalizedProvider || null, { executionOwner: "daemon" });
29
+ registerTools(recorder, "supervised_room_turn", normalizedProvider || null, { executionOwner: "daemon", apiUrl });
30
30
  handlersByProvider.set(normalizedProvider, handlers);
31
31
  return handlers;
32
32
  }
@@ -73,7 +73,7 @@ function validateExecutionContext(input) {
73
73
  */
74
74
  export async function executeDaemonTool(input) {
75
75
  const context = validateExecutionContext(input);
76
- const tool = handlersForProvider(context.provider).get(context.toolName);
76
+ const tool = handlersForProvider(context.provider, context.apiUrl).get(context.toolName);
77
77
  if (!tool)
78
78
  throw new Error(`Unsupported supervised tool: ${context.toolName}`);
79
79
  const parsedInput = tool.inputSchema
@@ -1,3 +1,5 @@
1
+ import { localSupervisedRoomToolAvailable } from "../../../shared/local-supervised-tools.mjs";
2
+ import { isLocalRoomApi } from "../../../shared/room-api-origin.mjs";
1
3
  import { registerRoomKnowledgeTools } from "./tools/knowledge.js";
2
4
  import { registerAgentSessionTools } from "./tools/agent-sessions.js";
3
5
  import { registerMessageTools, registerStatusTools } from "./tools/messages.js";
@@ -11,9 +13,25 @@ import { profileAwareToolServer } from "./supervised-tool-facade.js";
11
13
  import { workerAwareToolServer } from "./worker-tool-facade.js";
12
14
  import { registerWorkspaceTools } from "./tools/workspace.js";
13
15
  export function registerTools(server, profile = "autonomous_mcp_worker", supervisedProvider = process.env.LETAGENTS_SUPERVISOR_PROVIDER?.trim() || null, options = {}) {
16
+ const localRoom = profile === "supervised_room_turn" && isLocalRoomApi(options.apiUrl ?? process.env.LETAGENTS_API_URL);
17
+ const routedServer = localRoom ? new Proxy(server, {
18
+ get(target, property, receiver) {
19
+ const value = Reflect.get(target, property, receiver);
20
+ if (property !== "tool")
21
+ return typeof value === "function" ? value.bind(target) : value;
22
+ return (name, ...registration) => {
23
+ if (!localSupervisedRoomToolAvailable(name))
24
+ return undefined;
25
+ if (name === "update_task" && typeof registration[0] === "string") {
26
+ registration[0] += " In local rooms, close verified merged work with status 'done'; no close intent or replacement work lease is needed.";
27
+ }
28
+ return target.tool.call(target, name, ...registration);
29
+ };
30
+ },
31
+ }) : server;
14
32
  const profileTools = options.executionOwner === "daemon"
15
- ? server
16
- : profileAwareToolServer(server, profile, undefined, supervisedProvider);
33
+ ? routedServer
34
+ : profileAwareToolServer(routedServer, profile, undefined, supervisedProvider);
17
35
  const tools = profile === "autonomous_mcp_worker" || profile === "interactive_desktop"
18
36
  ? workerAwareToolServer(profileTools) : profileTools;
19
37
  const surface = toolSurfaceForExecutionProfile(profile);
@@ -4,7 +4,7 @@ export const LETAGENTS_RUNTIME_CONTRACT_ARG = "--letagents-runtime-contract";
4
4
  * Discover through the production registration path rather than maintaining a
5
5
  * second capability list that can drift from the MCP server.
6
6
  */
7
- export function registeredToolNames(profile, supervisedProvider = null) {
7
+ export function registeredToolNames(profile, supervisedProvider = null, apiUrl = process.env.LETAGENTS_API_URL) {
8
8
  const names = new Set();
9
9
  const recorder = {
10
10
  tool(name) {
@@ -12,16 +12,16 @@ export function registeredToolNames(profile, supervisedProvider = null) {
12
12
  return {};
13
13
  },
14
14
  };
15
- registerTools(recorder, profile, supervisedProvider);
15
+ registerTools(recorder, profile, supervisedProvider, { apiUrl });
16
16
  return [...names].sort();
17
17
  }
18
- export function letAgentsRuntimeContract() {
18
+ export function letAgentsRuntimeContract(apiUrl = process.env.LETAGENTS_API_URL) {
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
+ supervised_mcp_polling: { contract: "custodial_polling_v1", tools: registeredToolNames("supervised_mcp_polling", "codex", apiUrl) },
23
23
  cursor_supervised_room_turn: {
24
- tools: registeredToolNames("supervised_room_turn", "cursor"),
24
+ tools: registeredToolNames("supervised_room_turn", "cursor", apiUrl),
25
25
  },
26
26
  },
27
27
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "letagents",
3
- "version": "0.12.25",
3
+ "version": "0.12.26",
4
4
  "description": "Let Agents Chat \u2014 MCP server for AI agent communication",
5
5
  "type": "module",
6
6
  "main": "dist/mcp/server.js",
@@ -0,0 +1,3 @@
1
+ export function localSupervisedRoomToolOperation(name: string): string | null;
2
+ export function localSupervisedRoomToolAvailable(name: string): boolean;
3
+ export function defineLocalSupervisedToolHandlers<T>(handlers: Record<string, (context: T) => Promise<unknown>>): Readonly<Record<string, (context: T) => Promise<unknown>>>;
@@ -0,0 +1,31 @@
1
+ // One catalog for supervised local discovery and storage dispatch. Aliases share
2
+ // an operation; handler completeness is checked when the local executor loads.
3
+ const operations = Object.freeze({
4
+ get_current_room: "get_current_room", read_messages: "read_messages",
5
+ send_message: "send_message", send_thread_message: "send_message",
6
+ post_status: "send_message", post_reasoning: "send_message",
7
+ get_board: "get_board", create_task: "add_task", add_task: "add_task",
8
+ claim_task: "claim_task", complete_task: "update_task", update_task: "update_task",
9
+ release_task_lease: "change_task_lease", handoff_task_lease: "change_task_lease",
10
+ claim_task_review: "claim_task_review", get_room_artifacts: "get_room_artifacts",
11
+ publish_room_artifact: "publish_room_artifact", get_message_thread: "get_message_thread",
12
+ });
13
+
14
+ export function localSupervisedRoomToolOperation(name) {
15
+ return Object.hasOwn(operations, name) ? operations[name] : null;
16
+ }
17
+
18
+ export function localSupervisedRoomToolAvailable(name) {
19
+ // Cursor completion is handled by the daemon. Local room moves are unsupported.
20
+ return name === "complete_room_turn" || localSupervisedRoomToolOperation(name) !== null;
21
+ }
22
+
23
+ export function defineLocalSupervisedToolHandlers(handlers) {
24
+ const required = new Set(Object.values(operations));
25
+ if (Object.keys(handlers).length !== required.size
26
+ || Object.keys(handlers).some(name => !required.has(name))
27
+ || [...required].some(name => typeof handlers[name] !== "function")) {
28
+ throw new Error("Local room handlers do not match the advertised tool contract.");
29
+ }
30
+ return Object.freeze(handlers);
31
+ }