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
@@ -603,6 +603,63 @@ function referencesRuntimeSecretStorage(value) {
603
603
  return false;
604
604
  }
605
605
 
606
+ // ../../packages/core/src/agents/explicit-agent-switch.ts
607
+ function escapeRegExp(value) {
608
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
609
+ }
610
+ function agentAliases(agent) {
611
+ switch (agent) {
612
+ case "codex":
613
+ return [
614
+ "codex",
615
+ "\uCF54\uB371\uC2A4",
616
+ "gpt-6-luna",
617
+ "gpt-6-sol",
618
+ "gpt-5.6-luna",
619
+ "gpt-5.6-terra",
620
+ "gpt-5.6-sol",
621
+ "gpt-6-astra"
622
+ ];
623
+ case "claude":
624
+ return ["claude", "\uD074\uB85C\uB4DC", "sonnet", "opus", "fable"];
625
+ case "maestro":
626
+ return [
627
+ "maestro",
628
+ "\uB9C8\uC5D0\uC2A4\uD2B8\uB85C",
629
+ "\uBA54\uC2A4\uD2B8\uB85C",
630
+ "deepseek",
631
+ "deepseek-pro",
632
+ "kimi",
633
+ "kimi-pro",
634
+ "kimi-k3",
635
+ "kimi-code",
636
+ "kimi-k2.7-code",
637
+ "\uB525\uC2DC\uD06C",
638
+ "\uD0A4\uBBF8"
639
+ ];
640
+ }
641
+ }
642
+ function hasExplicitAgentSwitchRequest(prompt, agent) {
643
+ if (!prompt?.trim())
644
+ return false;
645
+ const text = prompt.toLowerCase().replace(/\s+/g, " ").trim();
646
+ const target = `(?:${agentAliases(agent).map(escapeRegExp).join("|")})`;
647
+ 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)";
648
+ const switchSubject = "(?:agent|runtime|model|\uC5D0\uC774\uC804\uD2B8|\uB7F0\uD0C0\uC784|\uBAA8\uB378)";
649
+ return [
650
+ new RegExp(`^/(?:agent|runtime)\\s+${target}(?:\\s|$)`, "iu"),
651
+ new RegExp(`${target}\\s*(?:\uB85C|\uC73C\uB85C)\\s*.{0,16}${switchVerb}`, "iu"),
652
+ new RegExp(`${switchSubject}.{0,24}${target}.{0,24}${switchVerb}`, "iu"),
653
+ new RegExp(`${switchVerb}.{0,24}${switchSubject}.{0,24}${target}`, "iu"),
654
+ new RegExp(`(?:switch|change|set|use).{0,24}${target}`, "iu")
655
+ ].some((pattern) => pattern.test(text));
656
+ }
657
+ function explicitAgentSwitchTargets(prompt) {
658
+ if (!prompt?.trim())
659
+ return [];
660
+ return SUPPORTED_AGENTS.filter((agent) => hasExplicitAgentSwitchRequest(prompt, agent));
661
+ }
662
+
606
663
  // ../../packages/core/src/mcp/canonical-bridge-config.ts
607
664
  var registrations = [];
608
665
  var turnLeases = new Map;
@@ -629,10 +686,189 @@ function canonicalMcpBridgeEnv(scope) {
629
686
 
630
687
  // ../../packages/core/src/mcp/runtime-spec.ts
631
688
  import { createHash, createHmac, timingSafeEqual } from "crypto";
689
+
690
+ // ../../packages/core/src/runtime/actor-topic-scope.ts
691
+ var ACTOR_TOPIC_SCOPE_LIMITS = Object.freeze({
692
+ maxIdsPerList: 200,
693
+ maxIdLength: 128,
694
+ maxSerializedBytes: 8 * 1024
695
+ });
696
+ var SCOPE_KEYS = ["visibleNodeTopicIds", "ownedNodeTopicIds"];
697
+ function idList(value, name) {
698
+ if (!Array.isArray(value))
699
+ return `${name} must be an array of topic ids`;
700
+ if (value.length > ACTOR_TOPIC_SCOPE_LIMITS.maxIdsPerList) {
701
+ return `${name} has ${value.length} ids; at most ${ACTOR_TOPIC_SCOPE_LIMITS.maxIdsPerList} are allowed`;
702
+ }
703
+ const ids = new Set;
704
+ for (const item of value) {
705
+ if (typeof item !== "string")
706
+ return `${name} must contain only strings`;
707
+ const id = item.trim();
708
+ if (!id)
709
+ return `${name} must not contain empty ids`;
710
+ if (id.length > ACTOR_TOPIC_SCOPE_LIMITS.maxIdLength) {
711
+ return `${name} contains an id longer than ${ACTOR_TOPIC_SCOPE_LIMITS.maxIdLength} characters`;
712
+ }
713
+ ids.add(id);
714
+ }
715
+ return [...ids];
716
+ }
717
+ function validateActorTopicScope(value) {
718
+ if (value === undefined)
719
+ return { ok: true, scope: undefined };
720
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
721
+ return {
722
+ ok: false,
723
+ error: "actorTopicScope must be { visibleNodeTopicIds: string[], ownedNodeTopicIds: string[] }"
724
+ };
725
+ }
726
+ const proto = Object.getPrototypeOf(value);
727
+ if (proto !== Object.prototype && proto !== null) {
728
+ return { ok: false, error: "actorTopicScope must be a plain object" };
729
+ }
730
+ const keys = Object.keys(value);
731
+ const unknown = keys.filter((key) => !SCOPE_KEYS.includes(key));
732
+ if (unknown.length || keys.length !== SCOPE_KEYS.length) {
733
+ return {
734
+ ok: false,
735
+ error: unknown.length ? `actorTopicScope has unexpected key(s): ${unknown.join(", ")}` : "actorTopicScope must have both visibleNodeTopicIds and ownedNodeTopicIds"
736
+ };
737
+ }
738
+ const record = value;
739
+ const visible = idList(record.visibleNodeTopicIds, "actorTopicScope.visibleNodeTopicIds");
740
+ if (typeof visible === "string")
741
+ return { ok: false, error: visible };
742
+ const owned = idList(record.ownedNodeTopicIds, "actorTopicScope.ownedNodeTopicIds");
743
+ if (typeof owned === "string")
744
+ return { ok: false, error: owned };
745
+ const scope = { visibleNodeTopicIds: visible, ownedNodeTopicIds: owned };
746
+ const bytes = Buffer.byteLength(JSON.stringify(scope), "utf-8");
747
+ if (bytes > ACTOR_TOPIC_SCOPE_LIMITS.maxSerializedBytes) {
748
+ return {
749
+ ok: false,
750
+ error: `actorTopicScope is ${bytes} bytes serialized; at most ${ACTOR_TOPIC_SCOPE_LIMITS.maxSerializedBytes} are allowed`
751
+ };
752
+ }
753
+ return { ok: true, scope };
754
+ }
755
+ function encodeActorTopicScopeArg(scope) {
756
+ const validated = validateActorTopicScope(scope);
757
+ if (!validated.ok)
758
+ throw new Error(validated.error);
759
+ if (!validated.scope)
760
+ throw new Error("actorTopicScope is required");
761
+ return Buffer.from(JSON.stringify(validated.scope), "utf-8").toString("base64url");
762
+ }
763
+
764
+ // ../../packages/core/src/runtime/remote-session-grant.ts
765
+ var REMOTE_SESSION_GRANT_LIMITS = Object.freeze({
766
+ maxHubUrlLength: 512,
767
+ maxCapabilityLength: 2 * 1024,
768
+ capabilityPrefix: "rsc1."
769
+ });
770
+ var GRANT_KEYS = ["hubUrl", "capability"];
771
+ var LOOPBACK_HOSTS = new Set(["127.0.0.1", "::1", "[::1]", "localhost"]);
772
+ function normalizeHubUrl(raw, name = "remoteSession.hubUrl") {
773
+ if (raw.length > REMOTE_SESSION_GRANT_LIMITS.maxHubUrlLength) {
774
+ return {
775
+ error: `${name} is longer than ${REMOTE_SESSION_GRANT_LIMITS.maxHubUrlLength} characters`
776
+ };
777
+ }
778
+ let url;
779
+ try {
780
+ url = new URL(raw);
781
+ } catch {
782
+ return { error: `${name} must be an absolute URL` };
783
+ }
784
+ if (url.username || url.password) {
785
+ return { error: `${name} must not carry credentials` };
786
+ }
787
+ if (url.search || url.hash || raw.includes("?") || raw.includes("#")) {
788
+ return { error: `${name} must not carry a query or fragment` };
789
+ }
790
+ if (url.protocol === "http:") {
791
+ if (!LOOPBACK_HOSTS.has(url.hostname)) {
792
+ return { error: `${name} must be https:// or a loopback http:// origin` };
793
+ }
794
+ } else if (url.protocol !== "https:") {
795
+ return { error: `${name} must be https:// or a loopback http:// origin` };
796
+ }
797
+ return { url: url.toString().replace(/\/+$/, "") };
798
+ }
799
+ var MAX_REMOTE_SESSION_REPLY_TOKEN_LENGTH = REMOTE_SESSION_GRANT_LIMITS.maxCapabilityLength;
800
+ function validateRemoteSessionGrant(value) {
801
+ if (value === undefined)
802
+ return { ok: true, grant: undefined };
803
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
804
+ return { ok: false, error: "remoteSession must be { hubUrl: string, capability: string }" };
805
+ }
806
+ const proto = Object.getPrototypeOf(value);
807
+ if (proto !== Object.prototype && proto !== null) {
808
+ return { ok: false, error: "remoteSession must be a plain object" };
809
+ }
810
+ const keys = Object.keys(value);
811
+ const unknown = keys.filter((key) => !GRANT_KEYS.includes(key));
812
+ if (unknown.length || keys.length !== GRANT_KEYS.length) {
813
+ return {
814
+ ok: false,
815
+ error: unknown.length ? `remoteSession has unexpected key(s): ${unknown.join(", ")}` : "remoteSession must have both hubUrl and capability"
816
+ };
817
+ }
818
+ const record = value;
819
+ if (typeof record.hubUrl !== "string" || !record.hubUrl.trim()) {
820
+ return { ok: false, error: "remoteSession.hubUrl must be a non-empty string" };
821
+ }
822
+ const hubUrl = normalizeHubUrl(record.hubUrl.trim());
823
+ if ("error" in hubUrl)
824
+ return { ok: false, error: hubUrl.error };
825
+ if (typeof record.capability !== "string" || !record.capability.trim()) {
826
+ return { ok: false, error: "remoteSession.capability must be a non-empty string" };
827
+ }
828
+ const capability = record.capability.trim();
829
+ if (capability.length > REMOTE_SESSION_GRANT_LIMITS.maxCapabilityLength) {
830
+ return {
831
+ ok: false,
832
+ error: `remoteSession.capability is ${capability.length} characters; at most ${REMOTE_SESSION_GRANT_LIMITS.maxCapabilityLength} are allowed`
833
+ };
834
+ }
835
+ if (!capability.startsWith(REMOTE_SESSION_GRANT_LIMITS.capabilityPrefix)) {
836
+ return {
837
+ ok: false,
838
+ error: `remoteSession.capability must start with "${REMOTE_SESSION_GRANT_LIMITS.capabilityPrefix}"`
839
+ };
840
+ }
841
+ if (!/^rsc1\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/.test(capability)) {
842
+ return { ok: false, error: "remoteSession.capability is malformed" };
843
+ }
844
+ return { ok: true, grant: { hubUrl: hubUrl.url, capability } };
845
+ }
846
+ function encodeRemoteSessionGrantArg(grant) {
847
+ const validated = validateRemoteSessionGrant(grant);
848
+ if (!validated.ok)
849
+ throw new Error(validated.error);
850
+ if (!validated.grant)
851
+ throw new Error("remoteSession is required");
852
+ return Buffer.from(JSON.stringify(validated.grant), "utf-8").toString("base64url");
853
+ }
854
+
855
+ // ../../packages/core/src/mcp/runtime-spec.ts
632
856
  var RUNTIME_MCP_KEY = "runtime";
633
857
  var RUNTIME_MCP_BASE_PATH = "/mcp/runtime";
634
858
  var TOKEN_TTL_MS = 4 * 60 * 60 * 1000;
635
859
  var CLAUDE_MCP_TOOL_TIMEOUT_MS = 600000;
860
+ var MCP_URL_BUDGET_CHARS = 14 * 1024;
861
+
862
+ class McpUrlBudgetExceededError extends Error {
863
+ surface;
864
+ urlLength;
865
+ constructor(surface, urlLength) {
866
+ super(`built-in MCP URL for "${surface}" is ${urlLength} characters; the transport allows at most ${MCP_URL_BUDGET_CHARS}`);
867
+ this.surface = surface;
868
+ this.urlLength = urlLength;
869
+ this.name = "McpUrlBudgetExceededError";
870
+ }
871
+ }
636
872
  var runtimePort = NEGOTIUM_PORT;
637
873
  function encodeTokenPart(value) {
638
874
  return Buffer.from(JSON.stringify(value), "utf-8").toString("base64url");
@@ -658,18 +894,26 @@ function issueHostedMcpToken(surface, ctx) {
658
894
  return `${payloadPart}.${signTokenPayload(payloadPart)}`;
659
895
  }
660
896
  function buildRuntimeMcpSpec(agent, ctx) {
661
- const token = issueRuntimeMcpToken(ctx);
662
897
  const base = `http://127.0.0.1:${runtimePort}${RUNTIME_MCP_BASE_PATH}`;
663
- const query = `token=${encodeURIComponent(token)}`;
898
+ const query = `token=${encodeURIComponent(issueRuntimeMcpToken(ctx))}`;
899
+ const url = agent === "codex" ? `${base}/mcp?${query}` : `${base}/sse?${query}`;
900
+ assertMcpUrlWithinBudget(RUNTIME_MCP_KEY, url, ctx.topicId);
664
901
  if (agent === "codex")
665
- return { url: `${base}/mcp?${query}` };
902
+ return { url };
666
903
  return {
667
904
  type: "sse",
668
- url: `${base}/sse?${query}`,
905
+ url,
669
906
  timeout: CLAUDE_MCP_TOOL_TIMEOUT_MS,
670
907
  ...agent === "maestro" ? { lifecycle: "turn" } : {}
671
908
  };
672
909
  }
910
+ function assertMcpUrlWithinBudget(surface, url, topicId) {
911
+ if (url.length <= MCP_URL_BUDGET_CHARS)
912
+ return;
913
+ const error = new McpUrlBudgetExceededError(surface, url.length);
914
+ 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");
915
+ throw error;
916
+ }
673
917
  function hostedMcpCacheIdentity(surface, ctx) {
674
918
  let semanticContext;
675
919
  switch (surface) {
@@ -700,6 +944,9 @@ function hostedMcpCacheIdentity(surface, ctx) {
700
944
  case "session-comm":
701
945
  semanticContext = {
702
946
  userId: ctx.userId,
947
+ actorUserId: ctx.actorUserId ?? null,
948
+ actorTopicScope: ctx.actorTopicScope ?? null,
949
+ remoteSession: ctx.remoteSession ?? null,
703
950
  topicTitle: ctx.topicTitle,
704
951
  topicId: ctx.topicId ?? null,
705
952
  subagentParentTopicId: ctx.subagentParentTopicId ?? null,
@@ -718,13 +965,15 @@ function buildHostedMcpSpec(agent, surface, ctx) {
718
965
  const token = issueHostedMcpToken(surface, ctx);
719
966
  const base = `http://127.0.0.1:${runtimePort}${RUNTIME_MCP_BASE_PATH}/${surface}`;
720
967
  const query = `token=${encodeURIComponent(token)}`;
968
+ const url = agent === "codex" ? `${base}/mcp?${query}` : `${base}/sse?${query}`;
969
+ assertMcpUrlWithinBudget(surface, url, ctx.topicId);
721
970
  if (agent === "codex")
722
- return { url: `${base}/mcp?${query}` };
723
- const queryBound = surface === "session-comm" && (ctx.silent === true || ctx.peerBridge !== undefined);
971
+ return { url };
972
+ const queryBound = surface === "session-comm" && (ctx.silent === true || ctx.peerBridge !== undefined || ctx.remoteSession !== undefined);
724
973
  const lifecycle = queryBound ? "turn" : surface === "session-comm" ? "session" : "process";
725
974
  return {
726
975
  type: "sse",
727
- url: `${base}/sse?${query}`,
976
+ url,
728
977
  timeout: CLAUDE_MCP_TOOL_TIMEOUT_MS,
729
978
  ...agent === "maestro" ? {
730
979
  lifecycle,
@@ -1493,6 +1742,9 @@ function initializeApiTopicsSchema() {
1493
1742
  if (!tableColumns("api_topics").has("subagent_report_mode")) {
1494
1743
  db.exec("ALTER TABLE api_topics ADD COLUMN subagent_report_mode TEXT NOT NULL DEFAULT 'auto'");
1495
1744
  }
1745
+ if (!tableColumns("api_topics").has("derived_by_user_id")) {
1746
+ db.exec("ALTER TABLE api_topics ADD COLUMN derived_by_user_id TEXT");
1747
+ }
1496
1748
  if (!tableColumns("api_topics").has("memory_topic_id")) {
1497
1749
  db.exec("ALTER TABLE api_topics ADD COLUMN memory_topic_id TEXT");
1498
1750
  }
@@ -1584,6 +1836,7 @@ function rowToDto(r, participants = getTopicParticipants(r.id), tellTargets) {
1584
1836
  memoryTopicId: r.memory_topic_id ?? undefined,
1585
1837
  memoryKey: r.memory_key ?? undefined,
1586
1838
  isFork: r.is_fork !== 0,
1839
+ ...r.derived_by_user_id ? { derivedByUserId: r.derived_by_user_id } : {},
1587
1840
  ...r.is_subagent !== 0 ? { isSubagent: true } : {},
1588
1841
  ...r.is_subagent !== 0 ? {
1589
1842
  subagentTellTargetIds: tellTargets ? tellTargets.get(r.id) ?? [] : listSubagentTellTargetIds(r.id)
@@ -1667,6 +1920,9 @@ function buildBuiltinMcpServer(surface, ctx, stdio) {
1667
1920
  const agent = ctx.agent ?? FALLBACK_AGENT;
1668
1921
  return buildHostedMcpSpec(agent, surface, {
1669
1922
  userId: ctx.userId,
1923
+ ...ctx.actorUserId ? { actorUserId: ctx.actorUserId } : {},
1924
+ ...ctx.actorTopicScope ? { actorTopicScope: ctx.actorTopicScope } : {},
1925
+ ...surface === "session-comm" && ctx.remoteSession ? { remoteSession: ctx.remoteSession } : {},
1670
1926
  topicTitle: ctx.session,
1671
1927
  ...ctx.topicId ? { topicId: ctx.topicId } : {},
1672
1928
  ...ctx.queryId ? { queryId: ctx.queryId } : {},
@@ -1762,6 +2018,7 @@ var MCP_CATALOG = {
1762
2018
  build({
1763
2019
  userId,
1764
2020
  actorUserId,
2021
+ actorTopicScope,
1765
2022
  session,
1766
2023
  topicId,
1767
2024
  queryId,
@@ -1777,16 +2034,18 @@ var MCP_CATALOG = {
1777
2034
  }) {
1778
2035
  if (!topicId || !agent)
1779
2036
  return null;
2037
+ const switchTargets = explicitAgentSwitchTargets(currentUserPrompt);
1780
2038
  return buildRuntimeMcpSpec(agent, {
1781
2039
  userId,
1782
2040
  ...actorUserId ? { actorUserId } : {},
2041
+ ...actorTopicScope ? { actorTopicScope } : {},
1783
2042
  topicId,
1784
2043
  topicTitle: session,
1785
2044
  queryId,
1786
2045
  cwd: cwd ?? resolveTopicWorkspaceDir(topicId),
1787
2046
  agent,
1788
2047
  model,
1789
- currentUserPrompt,
2048
+ ...switchTargets.length ? { explicitAgentSwitchTargets: switchTargets } : {},
1790
2049
  autoContinue,
1791
2050
  visualTools,
1792
2051
  fileDeliveryTools,
@@ -1856,6 +2115,9 @@ var MCP_CATALOG = {
1856
2115
  build(ctx) {
1857
2116
  const {
1858
2117
  userId,
2118
+ actorUserId,
2119
+ actorTopicScope,
2120
+ remoteSession,
1859
2121
  session,
1860
2122
  topicId,
1861
2123
  subagentParentTopicId,
@@ -1869,6 +2131,9 @@ var MCP_CATALOG = {
1869
2131
  const cronSessionId = topicId ? getRegisteredCronSession(topicId, effectiveAgent)?.sessionId : undefined;
1870
2132
  const args = [
1871
2133
  `--user-id=${userId}`,
2134
+ ...actorUserId ? [`--actor-user-id=${actorUserId}`] : [],
2135
+ ...actorTopicScope ? [`--actor-topic-scope=${encodeActorTopicScopeArg(actorTopicScope)}`] : [],
2136
+ ...remoteSession ? [`--remote-session-grant=${encodeRemoteSessionGrantArg(remoteSession)}`] : [],
1872
2137
  `--topic=${session}`,
1873
2138
  ...topicId ? [`--topic-id=${topicId}`] : [],
1874
2139
  ...subagentParentTopicId ? [`--subagent-parent-topic-id=${subagentParentTopicId}`] : [],
@@ -2046,6 +2311,9 @@ function getManagerMcpServers(opts) {
2046
2311
  const topicId = opts.topicId;
2047
2312
  return buildScope("manager", {
2048
2313
  userId: opts.userId,
2314
+ actorUserId: opts.actorUserId,
2315
+ actorTopicScope: opts.actorTopicScope,
2316
+ remoteSession: opts.remoteSession,
2049
2317
  session: opts.session ?? "General",
2050
2318
  topicId,
2051
2319
  queryId: opts.queryId,
@@ -2065,6 +2333,8 @@ function getForumMcpServers(opts) {
2065
2333
  const {
2066
2334
  userId,
2067
2335
  actorUserId,
2336
+ actorTopicScope,
2337
+ remoteSession,
2068
2338
  vaultUserId,
2069
2339
  session,
2070
2340
  topicId,
@@ -2098,6 +2368,8 @@ function getForumMcpServers(opts) {
2098
2368
  const base = buildScope("forum", {
2099
2369
  userId,
2100
2370
  actorUserId,
2371
+ actorTopicScope,
2372
+ remoteSession,
2101
2373
  vaultUserId,
2102
2374
  session,
2103
2375
  topicId,
@@ -2178,6 +2450,9 @@ function getMcpServersForQuery(opts) {
2178
2450
  if (opts.sessionType === "manager") {
2179
2451
  return hideProviderIntrospectionForOtium(mergeHostMcpServers(getManagerMcpServers({
2180
2452
  userId: opts.userId || "local",
2453
+ actorUserId: opts.actorUserId,
2454
+ actorTopicScope: opts.actorTopicScope,
2455
+ remoteSession: opts.remoteSession,
2181
2456
  session: opts.session,
2182
2457
  topicId: opts.topicId,
2183
2458
  queryId: opts.queryId,
@@ -2196,6 +2471,8 @@ function getMcpServersForQuery(opts) {
2196
2471
  return hideProviderIntrospectionForOtium(getForumMcpServers({
2197
2472
  userId: opts.userId || "local",
2198
2473
  actorUserId: opts.actorUserId,
2474
+ actorTopicScope: opts.actorTopicScope,
2475
+ remoteSession: opts.remoteSession,
2199
2476
  vaultUserId: opts.vaultUserId,
2200
2477
  session: opts.session || "default",
2201
2478
  topicId: opts.topicId,
@@ -3024,4 +3301,4 @@ function formatDateBucket(d) {
3024
3301
 
3025
3302
  export { __require, CLAUDE_EFFORT_VALUES, CODEX_EFFORT_VALUES, MAESTRO_EFFORT_VALUES, logger, MODEL_SONNET, MODEL_OPUS, MODEL_FABLE, configureRolloutHost, FIXTURES_DIR, clone, assertUuidLike, ensureCwdExists, extractChatPairs, parseJsonlText, writeJsonlFile, hostedCodexHomePath, extractLatestCodexPatchPreview, extractCodexPatchCallIds, readCodexPatchCallIds, readLatestCodexPatchPreview, extractLatestCodexContextUsage, readLatestCodexContextUsage, migrateCodexRolloutNativeMultiAgentMetadata, latestCodexRolloutPath, writeCodexRollout, decodeUuidV7Timestamp };
3026
3303
 
3027
- //# debugId=29C09EB6A3EDEE6764756E2164756E21
3304
+ //# debugId=402C1CFFD9AFF9A864756E2164756E21