negotium 0.19.2 → 0.20.0

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 (96) hide show
  1. package/dist/agent-helpers.js +1146 -212
  2. package/dist/agent-helpers.js.map +27 -19
  3. package/dist/background-bash.js.map +1 -1
  4. package/dist/browser-runtime.js.map +1 -1
  5. package/dist/{chunk-kkftbxh2.js → chunk-7he9nzfa.js} +1 -1
  6. package/dist/{chunk-55dd0xw7.js → chunk-ad6xqvqy.js} +2 -2
  7. package/dist/{chunk-55dd0xw7.js.map → chunk-ad6xqvqy.js.map} +2 -2
  8. package/dist/{chunk-exqz8bez.js → chunk-hh3f0yr7.js} +3 -3
  9. package/dist/{chunk-exqz8bez.js.map → chunk-hh3f0yr7.js.map} +2 -2
  10. package/dist/{chunk-aqqmzmft.js → chunk-nm59q3em.js} +1 -1
  11. package/dist/{chunk-cv7vdsrn.js → chunk-w3c6fc0p.js} +286 -9
  12. package/dist/{chunk-cv7vdsrn.js.map → chunk-w3c6fc0p.js.map} +10 -7
  13. package/dist/hosted-agent.js +290 -12
  14. package/dist/hosted-agent.js.map +10 -7
  15. package/dist/main.js +4599 -2724
  16. package/dist/main.js.map +49 -38
  17. package/dist/mcp-factories.js +1166 -162
  18. package/dist/mcp-factories.js.map +27 -19
  19. package/dist/media.js.map +1 -1
  20. package/dist/prompts.js.map +1 -1
  21. package/dist/query-runtime.js.map +3 -3
  22. package/dist/registry.js +3 -3
  23. package/dist/rollout.js +2 -2
  24. package/dist/runtime/src/agents/explicit-agent-switch.ts +101 -0
  25. package/dist/runtime/src/agents/mcp-tools/spawn-subagent.ts +126 -43
  26. package/dist/runtime/src/agents/public-helpers.ts +5 -0
  27. package/dist/runtime/src/agents/self-config-core.ts +28 -58
  28. package/dist/runtime/src/application/submit-runtime-gateway-turn.ts +34 -8
  29. package/dist/runtime/src/index.ts +75 -0
  30. package/dist/runtime/src/mcp/peer-bridge.ts +6 -1
  31. package/dist/runtime/src/mcp/runtime-spec.ts +145 -10
  32. package/dist/runtime/src/mcp/session-comm/actor-policy.ts +87 -0
  33. package/dist/runtime/src/mcp/session-comm/context.ts +42 -1
  34. package/dist/runtime/src/mcp/session-comm/default-host.ts +178 -2
  35. package/dist/runtime/src/mcp/session-comm/hub-remote-session.ts +553 -0
  36. package/dist/runtime/src/mcp/session-comm/limits.ts +8 -0
  37. package/dist/runtime/src/mcp/session-comm/peer-forward.ts +78 -3
  38. package/dist/runtime/src/mcp/session-comm/server.ts +167 -4
  39. package/dist/runtime/src/mcp/session-comm/topic-catalog.ts +7 -0
  40. package/dist/runtime/src/mcp/session-comm/topics.ts +32 -10
  41. package/dist/runtime/src/mcp-runtime-host.ts +2 -1
  42. package/dist/runtime/src/node-host.ts +26 -0
  43. package/dist/runtime/src/platform/mcp-config.ts +56 -2
  44. package/dist/runtime/src/query/active-rooms.ts +6 -0
  45. package/dist/runtime/src/runtime/actor-topic-reach.ts +85 -0
  46. package/dist/runtime/src/runtime/actor-topic-scope.ts +175 -0
  47. package/dist/runtime/src/runtime/ask-callbacks.ts +73 -20
  48. package/dist/runtime/src/runtime/gateway-capabilities.ts +22 -0
  49. package/dist/runtime/src/runtime/remote-session-grant.ts +221 -0
  50. package/dist/runtime/src/runtime/remote-session-inbox.ts +476 -0
  51. package/dist/runtime/src/runtime/remote-session-reply-outbox.ts +156 -0
  52. package/dist/runtime/src/runtime/turn-runner.ts +157 -33
  53. package/dist/runtime/src/runtime-gateway.ts +20 -0
  54. package/dist/runtime/src/storage/api-topics.ts +22 -3
  55. package/dist/runtime/src/storage/remote-session.ts +526 -0
  56. package/dist/runtime/src/storage/runtime-turn-requests.ts +75 -2
  57. package/dist/runtime/src/topics/derive.ts +3 -0
  58. package/dist/runtime/src/types/api.ts +6 -0
  59. package/dist/runtime/src/types.ts +42 -0
  60. package/dist/runtime/src/version.ts +1 -1
  61. package/dist/runtime-gateway.js.map +2 -2
  62. package/dist/runtime-helpers.js.map +1 -1
  63. package/dist/storage.js +16 -4
  64. package/dist/storage.js.map +4 -4
  65. package/dist/types/packages/core/src/agents/explicit-agent-switch.d.ts +26 -0
  66. package/dist/types/packages/core/src/agents/mcp-tools/spawn-subagent.d.ts +12 -2
  67. package/dist/types/packages/core/src/agents/public-helpers.d.ts +1 -0
  68. package/dist/types/packages/core/src/agents/self-config-core.d.ts +15 -1
  69. package/dist/types/packages/core/src/mcp/peer-bridge.d.ts +6 -1
  70. package/dist/types/packages/core/src/mcp/runtime-spec.d.ts +69 -2
  71. package/dist/types/packages/core/src/mcp/session-comm/context.d.ts +17 -1
  72. package/dist/types/packages/core/src/mcp/session-comm/hub-remote-session.d.ts +149 -0
  73. package/dist/types/packages/core/src/mcp/session-comm/limits.d.ts +8 -0
  74. package/dist/types/packages/core/src/mcp/session-comm/peer-forward.d.ts +23 -2
  75. package/dist/types/packages/core/src/mcp/session-comm/topic-catalog.d.ts +6 -0
  76. package/dist/types/packages/core/src/platform/mcp-config.d.ts +10 -1
  77. package/dist/types/packages/core/src/query/active-rooms.d.ts +6 -0
  78. package/dist/types/packages/core/src/runtime/actor-topic-reach.d.ts +49 -0
  79. package/dist/types/packages/core/src/runtime/actor-topic-scope.d.ts +91 -0
  80. package/dist/types/packages/core/src/runtime/ask-callbacks.d.ts +8 -2
  81. package/dist/types/packages/core/src/runtime/remote-session-grant.d.ts +91 -0
  82. package/dist/types/packages/core/src/runtime/remote-session-inbox.d.ts +83 -0
  83. package/dist/types/packages/core/src/runtime/remote-session-reply-outbox.d.ts +37 -0
  84. package/dist/types/packages/core/src/runtime/turn-runner.d.ts +22 -3
  85. package/dist/types/packages/core/src/runtime-gateway.d.ts +20 -0
  86. package/dist/types/packages/core/src/storage/api-topics.d.ts +3 -0
  87. package/dist/types/packages/core/src/storage/remote-session.d.ts +135 -0
  88. package/dist/types/packages/core/src/storage/runtime-turn-requests.d.ts +12 -1
  89. package/dist/types/packages/core/src/topics/derive.d.ts +2 -0
  90. package/dist/types/packages/core/src/types/api.d.ts +6 -0
  91. package/dist/types/packages/core/src/types.d.ts +40 -0
  92. package/dist/types/packages/core/src/version.d.ts +1 -1
  93. package/dist/vault.js.map +1 -1
  94. package/package.json +1 -1
  95. /package/dist/{chunk-kkftbxh2.js.map → chunk-7he9nzfa.js.map} +0 -0
  96. /package/dist/{chunk-aqqmzmft.js.map → chunk-nm59q3em.js.map} +0 -0
@@ -26,6 +26,7 @@ import {
26
26
  RUN_DIR,
27
27
  SESSION_COMM_SERVER,
28
28
  SESSION_WORKSPACE_DIR,
29
+ SUPPORTED_AGENTS,
29
30
  SYSTEM_HEALTH_SERVER,
30
31
  TASK_SERVER,
31
32
  TOKEN_STATS_SERVER,
@@ -41,7 +42,7 @@ import {
41
42
  logger,
42
43
  resolveFallbackAgent,
43
44
  resolveTopicWorkspaceDir
44
- } from "./chunk-55dd0xw7.js";
45
+ } from "./chunk-ad6xqvqy.js";
45
46
  import {
46
47
  __require
47
48
  } from "./chunk-2mx7fq49.js";
@@ -260,12 +261,248 @@ function referencesRuntimeSecretStorage(value) {
260
261
  return false;
261
262
  }
262
263
 
264
+ // ../../packages/core/src/agents/explicit-agent-switch.ts
265
+ function escapeRegExp(value) {
266
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
267
+ }
268
+ function agentAliases(agent) {
269
+ switch (agent) {
270
+ case "codex":
271
+ return [
272
+ "codex",
273
+ "\uCF54\uB371\uC2A4",
274
+ "gpt-6-luna",
275
+ "gpt-6-sol",
276
+ "gpt-5.6-luna",
277
+ "gpt-5.6-terra",
278
+ "gpt-5.6-sol",
279
+ "gpt-6-astra"
280
+ ];
281
+ case "claude":
282
+ return ["claude", "\uD074\uB85C\uB4DC", "sonnet", "opus", "fable"];
283
+ case "maestro":
284
+ return [
285
+ "maestro",
286
+ "\uB9C8\uC5D0\uC2A4\uD2B8\uB85C",
287
+ "\uBA54\uC2A4\uD2B8\uB85C",
288
+ "deepseek",
289
+ "deepseek-pro",
290
+ "kimi",
291
+ "kimi-pro",
292
+ "kimi-k3",
293
+ "kimi-code",
294
+ "kimi-k2.7-code",
295
+ "\uB525\uC2DC\uD06C",
296
+ "\uD0A4\uBBF8"
297
+ ];
298
+ }
299
+ }
300
+ function hasExplicitAgentSwitchRequest(prompt, agent) {
301
+ if (!prompt?.trim())
302
+ return false;
303
+ const text = prompt.toLowerCase().replace(/\s+/g, " ").trim();
304
+ const target = `(?:${agentAliases(agent).map(escapeRegExp).join("|")})`;
305
+ const switchVerb = "(?:\uBC14\uAFD4|\uBC14\uAFD4\uC918|\uBCC0\uACBD|\uBCC0\uACBD\uD574|\uC804\uD658|\uC804\uD658\uD574|\uC124\uC815|\uC124\uC815\uD574|\uC368\uC918|\uC0AC\uC6A9|\uAC00|switch|change|set|use)";
306
+ const switchSubject = "(?:agent|runtime|model|\uC5D0\uC774\uC804\uD2B8|\uB7F0\uD0C0\uC784|\uBAA8\uB378)";
307
+ return [
308
+ new RegExp(`^/(?:agent|runtime)\\s+${target}(?:\\s|$)`, "iu"),
309
+ new RegExp(`${target}\\s*(?:\uB85C|\uC73C\uB85C)\\s*.{0,16}${switchVerb}`, "iu"),
310
+ new RegExp(`${switchSubject}.{0,24}${target}.{0,24}${switchVerb}`, "iu"),
311
+ new RegExp(`${switchVerb}.{0,24}${switchSubject}.{0,24}${target}`, "iu"),
312
+ new RegExp(`(?:switch|change|set|use).{0,24}${target}`, "iu")
313
+ ].some((pattern) => pattern.test(text));
314
+ }
315
+ function explicitAgentSwitchTargets(prompt) {
316
+ if (!prompt?.trim())
317
+ return [];
318
+ return SUPPORTED_AGENTS.filter((agent) => hasExplicitAgentSwitchRequest(prompt, agent));
319
+ }
320
+
263
321
  // ../../packages/core/src/mcp/runtime-spec.ts
264
322
  import { createHash, createHmac, timingSafeEqual } from "crypto";
323
+
324
+ // ../../packages/core/src/runtime/actor-topic-scope.ts
325
+ var ACTOR_TOPIC_SCOPE_LIMITS = Object.freeze({
326
+ maxIdsPerList: 200,
327
+ maxIdLength: 128,
328
+ maxSerializedBytes: 8 * 1024
329
+ });
330
+ var SCOPE_KEYS = ["visibleNodeTopicIds", "ownedNodeTopicIds"];
331
+ function idList(value, name) {
332
+ if (!Array.isArray(value))
333
+ return `${name} must be an array of topic ids`;
334
+ if (value.length > ACTOR_TOPIC_SCOPE_LIMITS.maxIdsPerList) {
335
+ return `${name} has ${value.length} ids; at most ${ACTOR_TOPIC_SCOPE_LIMITS.maxIdsPerList} are allowed`;
336
+ }
337
+ const ids = new Set;
338
+ for (const item of value) {
339
+ if (typeof item !== "string")
340
+ return `${name} must contain only strings`;
341
+ const id = item.trim();
342
+ if (!id)
343
+ return `${name} must not contain empty ids`;
344
+ if (id.length > ACTOR_TOPIC_SCOPE_LIMITS.maxIdLength) {
345
+ return `${name} contains an id longer than ${ACTOR_TOPIC_SCOPE_LIMITS.maxIdLength} characters`;
346
+ }
347
+ ids.add(id);
348
+ }
349
+ return [...ids];
350
+ }
351
+ function validateActorTopicScope(value) {
352
+ if (value === undefined)
353
+ return { ok: true, scope: undefined };
354
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
355
+ return {
356
+ ok: false,
357
+ error: "actorTopicScope must be { visibleNodeTopicIds: string[], ownedNodeTopicIds: string[] }"
358
+ };
359
+ }
360
+ const proto = Object.getPrototypeOf(value);
361
+ if (proto !== Object.prototype && proto !== null) {
362
+ return { ok: false, error: "actorTopicScope must be a plain object" };
363
+ }
364
+ const keys = Object.keys(value);
365
+ const unknown = keys.filter((key) => !SCOPE_KEYS.includes(key));
366
+ if (unknown.length || keys.length !== SCOPE_KEYS.length) {
367
+ return {
368
+ ok: false,
369
+ error: unknown.length ? `actorTopicScope has unexpected key(s): ${unknown.join(", ")}` : "actorTopicScope must have both visibleNodeTopicIds and ownedNodeTopicIds"
370
+ };
371
+ }
372
+ const record = value;
373
+ const visible = idList(record.visibleNodeTopicIds, "actorTopicScope.visibleNodeTopicIds");
374
+ if (typeof visible === "string")
375
+ return { ok: false, error: visible };
376
+ const owned = idList(record.ownedNodeTopicIds, "actorTopicScope.ownedNodeTopicIds");
377
+ if (typeof owned === "string")
378
+ return { ok: false, error: owned };
379
+ const scope = { visibleNodeTopicIds: visible, ownedNodeTopicIds: owned };
380
+ const bytes = Buffer.byteLength(JSON.stringify(scope), "utf-8");
381
+ if (bytes > ACTOR_TOPIC_SCOPE_LIMITS.maxSerializedBytes) {
382
+ return {
383
+ ok: false,
384
+ error: `actorTopicScope is ${bytes} bytes serialized; at most ${ACTOR_TOPIC_SCOPE_LIMITS.maxSerializedBytes} are allowed`
385
+ };
386
+ }
387
+ return { ok: true, scope };
388
+ }
389
+ function encodeActorTopicScopeArg(scope) {
390
+ const validated = validateActorTopicScope(scope);
391
+ if (!validated.ok)
392
+ throw new Error(validated.error);
393
+ if (!validated.scope)
394
+ throw new Error("actorTopicScope is required");
395
+ return Buffer.from(JSON.stringify(validated.scope), "utf-8").toString("base64url");
396
+ }
397
+
398
+ // ../../packages/core/src/runtime/remote-session-grant.ts
399
+ var REMOTE_SESSION_GRANT_LIMITS = Object.freeze({
400
+ maxHubUrlLength: 512,
401
+ maxCapabilityLength: 2 * 1024,
402
+ capabilityPrefix: "rsc1."
403
+ });
404
+ var GRANT_KEYS = ["hubUrl", "capability"];
405
+ var LOOPBACK_HOSTS = new Set(["127.0.0.1", "::1", "[::1]", "localhost"]);
406
+ function normalizeHubUrl(raw, name = "remoteSession.hubUrl") {
407
+ if (raw.length > REMOTE_SESSION_GRANT_LIMITS.maxHubUrlLength) {
408
+ return {
409
+ error: `${name} is longer than ${REMOTE_SESSION_GRANT_LIMITS.maxHubUrlLength} characters`
410
+ };
411
+ }
412
+ let url;
413
+ try {
414
+ url = new URL(raw);
415
+ } catch {
416
+ return { error: `${name} must be an absolute URL` };
417
+ }
418
+ if (url.username || url.password) {
419
+ return { error: `${name} must not carry credentials` };
420
+ }
421
+ if (url.search || url.hash || raw.includes("?") || raw.includes("#")) {
422
+ return { error: `${name} must not carry a query or fragment` };
423
+ }
424
+ if (url.protocol === "http:") {
425
+ if (!LOOPBACK_HOSTS.has(url.hostname)) {
426
+ return { error: `${name} must be https:// or a loopback http:// origin` };
427
+ }
428
+ } else if (url.protocol !== "https:") {
429
+ return { error: `${name} must be https:// or a loopback http:// origin` };
430
+ }
431
+ return { url: url.toString().replace(/\/+$/, "") };
432
+ }
433
+ var MAX_REMOTE_SESSION_REPLY_TOKEN_LENGTH = REMOTE_SESSION_GRANT_LIMITS.maxCapabilityLength;
434
+ function validateRemoteSessionGrant(value) {
435
+ if (value === undefined)
436
+ return { ok: true, grant: undefined };
437
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
438
+ return { ok: false, error: "remoteSession must be { hubUrl: string, capability: string }" };
439
+ }
440
+ const proto = Object.getPrototypeOf(value);
441
+ if (proto !== Object.prototype && proto !== null) {
442
+ return { ok: false, error: "remoteSession must be a plain object" };
443
+ }
444
+ const keys = Object.keys(value);
445
+ const unknown = keys.filter((key) => !GRANT_KEYS.includes(key));
446
+ if (unknown.length || keys.length !== GRANT_KEYS.length) {
447
+ return {
448
+ ok: false,
449
+ error: unknown.length ? `remoteSession has unexpected key(s): ${unknown.join(", ")}` : "remoteSession must have both hubUrl and capability"
450
+ };
451
+ }
452
+ const record = value;
453
+ if (typeof record.hubUrl !== "string" || !record.hubUrl.trim()) {
454
+ return { ok: false, error: "remoteSession.hubUrl must be a non-empty string" };
455
+ }
456
+ const hubUrl = normalizeHubUrl(record.hubUrl.trim());
457
+ if ("error" in hubUrl)
458
+ return { ok: false, error: hubUrl.error };
459
+ if (typeof record.capability !== "string" || !record.capability.trim()) {
460
+ return { ok: false, error: "remoteSession.capability must be a non-empty string" };
461
+ }
462
+ const capability = record.capability.trim();
463
+ if (capability.length > REMOTE_SESSION_GRANT_LIMITS.maxCapabilityLength) {
464
+ return {
465
+ ok: false,
466
+ error: `remoteSession.capability is ${capability.length} characters; at most ${REMOTE_SESSION_GRANT_LIMITS.maxCapabilityLength} are allowed`
467
+ };
468
+ }
469
+ if (!capability.startsWith(REMOTE_SESSION_GRANT_LIMITS.capabilityPrefix)) {
470
+ return {
471
+ ok: false,
472
+ error: `remoteSession.capability must start with "${REMOTE_SESSION_GRANT_LIMITS.capabilityPrefix}"`
473
+ };
474
+ }
475
+ if (!/^rsc1\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/.test(capability)) {
476
+ return { ok: false, error: "remoteSession.capability is malformed" };
477
+ }
478
+ return { ok: true, grant: { hubUrl: hubUrl.url, capability } };
479
+ }
480
+ function encodeRemoteSessionGrantArg(grant) {
481
+ const validated = validateRemoteSessionGrant(grant);
482
+ if (!validated.ok)
483
+ throw new Error(validated.error);
484
+ if (!validated.grant)
485
+ throw new Error("remoteSession is required");
486
+ return Buffer.from(JSON.stringify(validated.grant), "utf-8").toString("base64url");
487
+ }
488
+
489
+ // ../../packages/core/src/mcp/runtime-spec.ts
265
490
  var RUNTIME_MCP_KEY = "runtime";
266
491
  var RUNTIME_MCP_BASE_PATH = "/mcp/runtime";
267
492
  var TOKEN_TTL_MS = 4 * 60 * 60 * 1000;
268
493
  var CLAUDE_MCP_TOOL_TIMEOUT_MS = 600000;
494
+ var MCP_URL_BUDGET_CHARS = 14 * 1024;
495
+
496
+ class McpUrlBudgetExceededError extends Error {
497
+ surface;
498
+ urlLength;
499
+ constructor(surface, urlLength) {
500
+ super(`built-in MCP URL for "${surface}" is ${urlLength} characters; the transport allows at most ${MCP_URL_BUDGET_CHARS}`);
501
+ this.surface = surface;
502
+ this.urlLength = urlLength;
503
+ this.name = "McpUrlBudgetExceededError";
504
+ }
505
+ }
269
506
  var runtimePort = NEGOTIUM_PORT;
270
507
  function encodeTokenPart(value) {
271
508
  return Buffer.from(JSON.stringify(value), "utf-8").toString("base64url");
@@ -291,18 +528,26 @@ function issueHostedMcpToken(surface, ctx) {
291
528
  return `${payloadPart}.${signTokenPayload(payloadPart)}`;
292
529
  }
293
530
  function buildRuntimeMcpSpec(agent, ctx) {
294
- const token = issueRuntimeMcpToken(ctx);
295
531
  const base = `http://127.0.0.1:${runtimePort}${RUNTIME_MCP_BASE_PATH}`;
296
- const query = `token=${encodeURIComponent(token)}`;
532
+ const query = `token=${encodeURIComponent(issueRuntimeMcpToken(ctx))}`;
533
+ const url = agent === "codex" ? `${base}/mcp?${query}` : `${base}/sse?${query}`;
534
+ assertMcpUrlWithinBudget(RUNTIME_MCP_KEY, url, ctx.topicId);
297
535
  if (agent === "codex")
298
- return { url: `${base}/mcp?${query}` };
536
+ return { url };
299
537
  return {
300
538
  type: "sse",
301
- url: `${base}/sse?${query}`,
539
+ url,
302
540
  timeout: CLAUDE_MCP_TOOL_TIMEOUT_MS,
303
541
  ...agent === "maestro" ? { lifecycle: "turn" } : {}
304
542
  };
305
543
  }
544
+ function assertMcpUrlWithinBudget(surface, url, topicId) {
545
+ if (url.length <= MCP_URL_BUDGET_CHARS)
546
+ return;
547
+ const error = new McpUrlBudgetExceededError(surface, url.length);
548
+ logger.error({ topicId, surface, urlLength: url.length, budget: MCP_URL_BUDGET_CHARS }, "built-in MCP URL exceeds the transport budget; refusing to mint the token");
549
+ throw error;
550
+ }
306
551
  function hostedMcpCacheIdentity(surface, ctx) {
307
552
  let semanticContext;
308
553
  switch (surface) {
@@ -333,6 +578,9 @@ function hostedMcpCacheIdentity(surface, ctx) {
333
578
  case "session-comm":
334
579
  semanticContext = {
335
580
  userId: ctx.userId,
581
+ actorUserId: ctx.actorUserId ?? null,
582
+ actorTopicScope: ctx.actorTopicScope ?? null,
583
+ remoteSession: ctx.remoteSession ?? null,
336
584
  topicTitle: ctx.topicTitle,
337
585
  topicId: ctx.topicId ?? null,
338
586
  subagentParentTopicId: ctx.subagentParentTopicId ?? null,
@@ -351,13 +599,15 @@ function buildHostedMcpSpec(agent, surface, ctx) {
351
599
  const token = issueHostedMcpToken(surface, ctx);
352
600
  const base = `http://127.0.0.1:${runtimePort}${RUNTIME_MCP_BASE_PATH}/${surface}`;
353
601
  const query = `token=${encodeURIComponent(token)}`;
602
+ const url = agent === "codex" ? `${base}/mcp?${query}` : `${base}/sse?${query}`;
603
+ assertMcpUrlWithinBudget(surface, url, ctx.topicId);
354
604
  if (agent === "codex")
355
- return { url: `${base}/mcp?${query}` };
356
- const queryBound = surface === "session-comm" && (ctx.silent === true || ctx.peerBridge !== undefined);
605
+ return { url };
606
+ const queryBound = surface === "session-comm" && (ctx.silent === true || ctx.peerBridge !== undefined || ctx.remoteSession !== undefined);
357
607
  const lifecycle = queryBound ? "turn" : surface === "session-comm" ? "session" : "process";
358
608
  return {
359
609
  type: "sse",
360
- url: `${base}/sse?${query}`,
610
+ url,
361
611
  timeout: CLAUDE_MCP_TOOL_TIMEOUT_MS,
362
612
  ...agent === "maestro" ? {
363
613
  lifecycle,
@@ -1126,6 +1376,9 @@ function initializeApiTopicsSchema() {
1126
1376
  if (!tableColumns("api_topics").has("subagent_report_mode")) {
1127
1377
  db.exec("ALTER TABLE api_topics ADD COLUMN subagent_report_mode TEXT NOT NULL DEFAULT 'auto'");
1128
1378
  }
1379
+ if (!tableColumns("api_topics").has("derived_by_user_id")) {
1380
+ db.exec("ALTER TABLE api_topics ADD COLUMN derived_by_user_id TEXT");
1381
+ }
1129
1382
  if (!tableColumns("api_topics").has("memory_topic_id")) {
1130
1383
  db.exec("ALTER TABLE api_topics ADD COLUMN memory_topic_id TEXT");
1131
1384
  }
@@ -1217,6 +1470,7 @@ function rowToDto(r, participants = getTopicParticipants(r.id), tellTargets) {
1217
1470
  memoryTopicId: r.memory_topic_id ?? undefined,
1218
1471
  memoryKey: r.memory_key ?? undefined,
1219
1472
  isFork: r.is_fork !== 0,
1473
+ ...r.derived_by_user_id ? { derivedByUserId: r.derived_by_user_id } : {},
1220
1474
  ...r.is_subagent !== 0 ? { isSubagent: true } : {},
1221
1475
  ...r.is_subagent !== 0 ? {
1222
1476
  subagentTellTargetIds: tellTargets ? tellTargets.get(r.id) ?? [] : listSubagentTellTargetIds(r.id)
@@ -1300,6 +1554,9 @@ function buildBuiltinMcpServer(surface, ctx, stdio) {
1300
1554
  const agent = ctx.agent ?? FALLBACK_AGENT;
1301
1555
  return buildHostedMcpSpec(agent, surface, {
1302
1556
  userId: ctx.userId,
1557
+ ...ctx.actorUserId ? { actorUserId: ctx.actorUserId } : {},
1558
+ ...ctx.actorTopicScope ? { actorTopicScope: ctx.actorTopicScope } : {},
1559
+ ...surface === "session-comm" && ctx.remoteSession ? { remoteSession: ctx.remoteSession } : {},
1303
1560
  topicTitle: ctx.session,
1304
1561
  ...ctx.topicId ? { topicId: ctx.topicId } : {},
1305
1562
  ...ctx.queryId ? { queryId: ctx.queryId } : {},
@@ -1395,6 +1652,7 @@ var MCP_CATALOG = {
1395
1652
  build({
1396
1653
  userId,
1397
1654
  actorUserId,
1655
+ actorTopicScope,
1398
1656
  session,
1399
1657
  topicId,
1400
1658
  queryId,
@@ -1410,16 +1668,18 @@ var MCP_CATALOG = {
1410
1668
  }) {
1411
1669
  if (!topicId || !agent)
1412
1670
  return null;
1671
+ const switchTargets = explicitAgentSwitchTargets(currentUserPrompt);
1413
1672
  return buildRuntimeMcpSpec(agent, {
1414
1673
  userId,
1415
1674
  ...actorUserId ? { actorUserId } : {},
1675
+ ...actorTopicScope ? { actorTopicScope } : {},
1416
1676
  topicId,
1417
1677
  topicTitle: session,
1418
1678
  queryId,
1419
1679
  cwd: cwd ?? resolveTopicWorkspaceDir(topicId),
1420
1680
  agent,
1421
1681
  model,
1422
- currentUserPrompt,
1682
+ ...switchTargets.length ? { explicitAgentSwitchTargets: switchTargets } : {},
1423
1683
  autoContinue,
1424
1684
  visualTools,
1425
1685
  fileDeliveryTools,
@@ -1489,6 +1749,9 @@ var MCP_CATALOG = {
1489
1749
  build(ctx) {
1490
1750
  const {
1491
1751
  userId,
1752
+ actorUserId,
1753
+ actorTopicScope,
1754
+ remoteSession,
1492
1755
  session,
1493
1756
  topicId,
1494
1757
  subagentParentTopicId,
@@ -1502,6 +1765,9 @@ var MCP_CATALOG = {
1502
1765
  const cronSessionId = topicId ? getRegisteredCronSession(topicId, effectiveAgent)?.sessionId : undefined;
1503
1766
  const args = [
1504
1767
  `--user-id=${userId}`,
1768
+ ...actorUserId ? [`--actor-user-id=${actorUserId}`] : [],
1769
+ ...actorTopicScope ? [`--actor-topic-scope=${encodeActorTopicScopeArg(actorTopicScope)}`] : [],
1770
+ ...remoteSession ? [`--remote-session-grant=${encodeRemoteSessionGrantArg(remoteSession)}`] : [],
1505
1771
  `--topic=${session}`,
1506
1772
  ...topicId ? [`--topic-id=${topicId}`] : [],
1507
1773
  ...subagentParentTopicId ? [`--subagent-parent-topic-id=${subagentParentTopicId}`] : [],
@@ -1679,6 +1945,9 @@ function getManagerMcpServers(opts) {
1679
1945
  const topicId = opts.topicId;
1680
1946
  return buildScope("manager", {
1681
1947
  userId: opts.userId,
1948
+ actorUserId: opts.actorUserId,
1949
+ actorTopicScope: opts.actorTopicScope,
1950
+ remoteSession: opts.remoteSession,
1682
1951
  session: opts.session ?? "General",
1683
1952
  topicId,
1684
1953
  queryId: opts.queryId,
@@ -1698,6 +1967,8 @@ function getForumMcpServers(opts) {
1698
1967
  const {
1699
1968
  userId,
1700
1969
  actorUserId,
1970
+ actorTopicScope,
1971
+ remoteSession,
1701
1972
  vaultUserId,
1702
1973
  session,
1703
1974
  topicId,
@@ -1731,6 +2002,8 @@ function getForumMcpServers(opts) {
1731
2002
  const base = buildScope("forum", {
1732
2003
  userId,
1733
2004
  actorUserId,
2005
+ actorTopicScope,
2006
+ remoteSession,
1734
2007
  vaultUserId,
1735
2008
  session,
1736
2009
  topicId,
@@ -1811,6 +2084,9 @@ function getMcpServersForQuery(opts) {
1811
2084
  if (opts.sessionType === "manager") {
1812
2085
  return hideProviderIntrospectionForOtium(mergeHostMcpServers(getManagerMcpServers({
1813
2086
  userId: opts.userId || "local",
2087
+ actorUserId: opts.actorUserId,
2088
+ actorTopicScope: opts.actorTopicScope,
2089
+ remoteSession: opts.remoteSession,
1814
2090
  session: opts.session,
1815
2091
  topicId: opts.topicId,
1816
2092
  queryId: opts.queryId,
@@ -1829,6 +2105,8 @@ function getMcpServersForQuery(opts) {
1829
2105
  return hideProviderIntrospectionForOtium(getForumMcpServers({
1830
2106
  userId: opts.userId || "local",
1831
2107
  actorUserId: opts.actorUserId,
2108
+ actorTopicScope: opts.actorTopicScope,
2109
+ remoteSession: opts.remoteSession,
1832
2110
  vaultUserId: opts.vaultUserId,
1833
2111
  session: opts.session || "default",
1834
2112
  topicId: opts.topicId,
@@ -2726,7 +3004,7 @@ import { tmpdir } from "os";
2726
3004
  import { dirname as dirname5, join as join3 } from "path";
2727
3005
 
2728
3006
  // ../../packages/core/src/version.ts
2729
- var NEGOTIUM_VERSION = "0.19.2";
3007
+ var NEGOTIUM_VERSION = "0.20.0";
2730
3008
 
2731
3009
  // ../../packages/core/src/agents/codex-native-multi-agent.ts
2732
3010
  var moduleRequire = createRequire(import.meta.url);
@@ -3455,7 +3733,7 @@ async function createCodexVaultHookBridge(userId) {
3455
3733
  });
3456
3734
  restrictIpcEndpoint(socketPath);
3457
3735
  if (IS_WINDOWS2) {
3458
- const { BINARIES_DIR } = await import("./chunk-kkftbxh2.js");
3736
+ const { BINARIES_DIR } = await import("./chunk-7he9nzfa.js");
3459
3737
  const cached = await ensureCodexWindowsWrapperExe(join5(BINARIES_DIR, "codex-hook-wrapper"));
3460
3738
  await copyFile(cached, wrapperPath);
3461
3739
  await writeFile2(join5(root, "codex-with-hooks.cfg"), [process.execPath, codexCliScriptPath(), socketPath, token].join(`
@@ -4769,4 +5047,4 @@ export {
4769
5047
  buildClaudeDisallowedTools
4770
5048
  };
4771
5049
 
4772
- //# debugId=AE528B367B6FEEB064756E2164756E21
5050
+ //# debugId=977679FF27CBA61D64756E2164756E21