@mastra/code-sdk 1.7.3-alpha.0 → 1.8.0-alpha.11

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 (138) hide show
  1. package/dist/agent-connections/ownership.d.ts +1 -0
  2. package/dist/agent-connections/ownership.d.ts.map +1 -1
  3. package/dist/agent-connections/ownership.js +42 -26
  4. package/dist/agent-connections/ownership.js.map +1 -1
  5. package/dist/agent-connections/registry.d.ts.map +1 -1
  6. package/dist/agent-connections/registry.js +5 -2
  7. package/dist/agent-connections/registry.js.map +1 -1
  8. package/dist/agent-connections/session-advertisement.d.ts +20 -0
  9. package/dist/agent-connections/session-advertisement.d.ts.map +1 -0
  10. package/dist/agent-connections/session-advertisement.js +79 -0
  11. package/dist/agent-connections/session-advertisement.js.map +1 -0
  12. package/dist/agent-connections/tools.d.ts.map +1 -1
  13. package/dist/agent-connections/tools.js +26 -8
  14. package/dist/agent-connections/tools.js.map +1 -1
  15. package/dist/agents/background-completion-events.d.ts +16 -0
  16. package/dist/agents/background-completion-events.d.ts.map +1 -0
  17. package/dist/agents/background-completion-events.js +19 -0
  18. package/dist/agents/background-completion-events.js.map +1 -0
  19. package/dist/agents/background-completion.d.ts +9 -0
  20. package/dist/agents/background-completion.d.ts.map +1 -0
  21. package/dist/agents/background-completion.js +59 -0
  22. package/dist/agents/background-completion.js.map +1 -0
  23. package/dist/agents/mastracode-gateway.d.ts +2 -0
  24. package/dist/agents/mastracode-gateway.d.ts.map +1 -1
  25. package/dist/agents/mastracode-gateway.js +29 -10
  26. package/dist/agents/mastracode-gateway.js.map +1 -1
  27. package/dist/agents/memory.d.ts +1 -1
  28. package/dist/agents/memory.d.ts.map +1 -1
  29. package/dist/agents/memory.js +32 -25
  30. package/dist/agents/memory.js.map +1 -1
  31. package/dist/agents/model.d.ts +41 -1
  32. package/dist/agents/model.d.ts.map +1 -1
  33. package/dist/agents/model.js +141 -7
  34. package/dist/agents/model.js.map +1 -1
  35. package/dist/agents/tool-availability.d.ts +2 -0
  36. package/dist/agents/tool-availability.d.ts.map +1 -1
  37. package/dist/agents/tool-availability.js +33 -4
  38. package/dist/agents/tool-availability.js.map +1 -1
  39. package/dist/agents/tools.d.ts +1 -1
  40. package/dist/agents/tools.d.ts.map +1 -1
  41. package/dist/agents/tools.js +30 -2
  42. package/dist/agents/tools.js.map +1 -1
  43. package/dist/agents/workspace.d.ts +2 -1
  44. package/dist/agents/workspace.d.ts.map +1 -1
  45. package/dist/agents/workspace.js +3 -3
  46. package/dist/agents/workspace.js.map +1 -1
  47. package/dist/auth/account-rotation-processor.d.ts +207 -0
  48. package/dist/auth/account-rotation-processor.d.ts.map +1 -0
  49. package/dist/auth/account-rotation-processor.js +669 -0
  50. package/dist/auth/account-rotation-processor.js.map +1 -0
  51. package/dist/auth/account-routing-context.d.ts +13 -0
  52. package/dist/auth/account-routing-context.d.ts.map +1 -0
  53. package/dist/auth/account-routing-context.js +41 -0
  54. package/dist/auth/account-routing-context.js.map +1 -0
  55. package/dist/auth/index.d.ts +1 -0
  56. package/dist/auth/index.d.ts.map +1 -1
  57. package/dist/auth/index.js +2 -1
  58. package/dist/auth/providers/anthropic.d.ts.map +1 -1
  59. package/dist/auth/providers/anthropic.js +2 -8
  60. package/dist/auth/providers/anthropic.js.map +1 -1
  61. package/dist/auth/providers/github-copilot.d.ts.map +1 -1
  62. package/dist/auth/providers/github-copilot.js +34 -12
  63. package/dist/auth/providers/github-copilot.js.map +1 -1
  64. package/dist/auth/providers/kimi-coding.d.ts.map +1 -1
  65. package/dist/auth/providers/kimi-coding.js +2 -6
  66. package/dist/auth/providers/kimi-coding.js.map +1 -1
  67. package/dist/auth/providers/openai-codex.d.ts +3 -1
  68. package/dist/auth/providers/openai-codex.d.ts.map +1 -1
  69. package/dist/auth/providers/openai-codex.js +50 -24
  70. package/dist/auth/providers/openai-codex.js.map +1 -1
  71. package/dist/auth/providers/xai.d.ts +1 -1
  72. package/dist/auth/providers/xai.d.ts.map +1 -1
  73. package/dist/auth/providers/xai.js +36 -16
  74. package/dist/auth/providers/xai.js.map +1 -1
  75. package/dist/auth/storage.d.ts +130 -8
  76. package/dist/auth/storage.d.ts.map +1 -1
  77. package/dist/auth/storage.js +602 -39
  78. package/dist/auth/storage.js.map +1 -1
  79. package/dist/auth/types.d.ts +69 -2
  80. package/dist/auth/types.d.ts.map +1 -1
  81. package/dist/goal-signal.d.ts +1 -1
  82. package/dist/goal-signal.d.ts.map +1 -1
  83. package/dist/goal-signal.js +1 -1
  84. package/dist/goal-signal.js.map +1 -1
  85. package/dist/index.d.ts +4 -0
  86. package/dist/index.d.ts.map +1 -1
  87. package/dist/index.js +127 -54
  88. package/dist/index.js.map +1 -1
  89. package/dist/mcp/config.d.ts +17 -2
  90. package/dist/mcp/config.d.ts.map +1 -1
  91. package/dist/mcp/config.js +18 -3
  92. package/dist/mcp/config.js.map +1 -1
  93. package/dist/mcp/manager.d.ts +2 -1
  94. package/dist/mcp/manager.d.ts.map +1 -1
  95. package/dist/mcp/manager.js +13 -8
  96. package/dist/mcp/manager.js.map +1 -1
  97. package/dist/mcp/types.d.ts +15 -3
  98. package/dist/mcp/types.d.ts.map +1 -1
  99. package/dist/onboarding/custom-packs.d.ts.map +1 -1
  100. package/dist/onboarding/custom-packs.js +3 -0
  101. package/dist/onboarding/custom-packs.js.map +1 -1
  102. package/dist/onboarding/index.js +1 -1
  103. package/dist/onboarding/packs.d.ts +44 -0
  104. package/dist/onboarding/packs.d.ts.map +1 -1
  105. package/dist/onboarding/packs.js +80 -2
  106. package/dist/onboarding/packs.js.map +1 -1
  107. package/dist/onboarding/settings.d.ts +33 -0
  108. package/dist/onboarding/settings.d.ts.map +1 -1
  109. package/dist/onboarding/settings.js +88 -8
  110. package/dist/onboarding/settings.js.map +1 -1
  111. package/dist/plugin.d.ts +12 -0
  112. package/dist/plugin.d.ts.map +1 -1
  113. package/dist/plugin.js.map +1 -1
  114. package/dist/plugins/loader.d.ts.map +1 -1
  115. package/dist/plugins/loader.js +2 -1
  116. package/dist/plugins/loader.js.map +1 -1
  117. package/dist/providers/claude-max.d.ts.map +1 -1
  118. package/dist/providers/claude-max.js +4 -5
  119. package/dist/providers/claude-max.js.map +1 -1
  120. package/dist/providers/github-copilot.d.ts +1 -1
  121. package/dist/providers/github-copilot.d.ts.map +1 -1
  122. package/dist/providers/github-copilot.js +89 -37
  123. package/dist/providers/github-copilot.js.map +1 -1
  124. package/dist/providers/kimi-coding.d.ts.map +1 -1
  125. package/dist/providers/kimi-coding.js +16 -2
  126. package/dist/providers/kimi-coding.js.map +1 -1
  127. package/dist/providers/openai-codex.d.ts.map +1 -1
  128. package/dist/providers/openai-codex.js +42 -27
  129. package/dist/providers/openai-codex.js.map +1 -1
  130. package/dist/schema.d.ts +28 -1
  131. package/dist/schema.d.ts.map +1 -1
  132. package/dist/schema.js +9 -0
  133. package/dist/schema.js.map +1 -1
  134. package/dist/tools/workflows/save-workflow.d.ts +1 -1
  135. package/dist/tools/workflows/save-workflow.d.ts.map +1 -1
  136. package/dist/workflows/register-primitives.js +2 -2
  137. package/dist/workflows/register-primitives.js.map +1 -1
  138. package/package.json +16 -16
@@ -4,6 +4,7 @@ export interface ThreadOwnershipClaim {
4
4
  }
5
5
  export declare function createThreadOwnershipManager(claimThread: (threadId: string) => Promise<ThreadOwnershipClaim>): {
6
6
  claim(threadId?: string | null): Promise<boolean>;
7
+ release(threadId: string): void;
7
8
  close(): void;
8
9
  };
9
10
  //# sourceMappingURL=ownership.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ownership.d.ts","sourceRoot":"","sources":["../../src/agent-connections/ownership.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,IAAI,IAAI,CAAC;CACrB;AAKD,wBAAgB,4BAA4B,CAAC,WAAW,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,oBAAoB,CAAC,GAAG;IAC9G,KAAK,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAClD,KAAK,IAAI,IAAI,CAAC;CACf,CA4DA"}
1
+ {"version":3,"file":"ownership.d.ts","sourceRoot":"","sources":["../../src/agent-connections/ownership.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,IAAI,IAAI,CAAC;CACrB;AAkBD,wBAAgB,4BAA4B,CAAC,WAAW,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,oBAAoB,CAAC,GAAG;IAC9G,KAAK,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAClD,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,KAAK,IAAI,IAAI,CAAC;CACf,CA0FA"}
@@ -2,28 +2,29 @@
2
2
  const OWNERSHIP_RETRY_INITIAL_DELAY_MS = 250;
3
3
  const OWNERSHIP_RETRY_MAX_DELAY_MS = 5e3;
4
4
  function createThreadOwnershipManager(claimThread) {
5
- let generation = 0;
6
- let currentClaim;
7
- let retryTimer;
8
- let retryDelayMs = OWNERSHIP_RETRY_INITIAL_DELAY_MS;
9
- const clearRetry = () => {
10
- if (retryTimer) clearTimeout(retryTimer);
11
- retryTimer = void 0;
5
+ const states = /* @__PURE__ */ new Map();
6
+ let nextGeneration = 0;
7
+ let closed = false;
8
+ const clearRetry = (state) => {
9
+ if (state.retryTimer) clearTimeout(state.retryTimer);
10
+ state.retryTimer = void 0;
12
11
  };
13
12
  const scheduleRetry = (threadId, claimGeneration) => {
14
- if (claimGeneration !== generation || retryTimer) return;
15
- const delayMs = retryDelayMs;
16
- retryDelayMs = Math.min(retryDelayMs * 2, OWNERSHIP_RETRY_MAX_DELAY_MS);
17
- retryTimer = setTimeout(() => {
18
- retryTimer = void 0;
13
+ const state = states.get(threadId);
14
+ if (closed || !state || state.generation !== claimGeneration || state.retryTimer) return;
15
+ const delayMs = state.retryDelayMs;
16
+ state.retryDelayMs = Math.min(state.retryDelayMs * 2, OWNERSHIP_RETRY_MAX_DELAY_MS);
17
+ state.retryTimer = setTimeout(() => {
18
+ state.retryTimer = void 0;
19
19
  attemptClaim(threadId, claimGeneration, false);
20
20
  }, delayMs);
21
- retryTimer.unref?.();
21
+ state.retryTimer.unref?.();
22
22
  };
23
23
  const attemptClaim = async (threadId, claimGeneration, propagateError) => {
24
24
  try {
25
25
  const nextClaim = await claimThread(threadId);
26
- if (claimGeneration !== generation) {
26
+ const state = states.get(threadId);
27
+ if (closed || !state || state.generation !== claimGeneration) {
27
28
  nextClaim.unsubscribe();
28
29
  return false;
29
30
  }
@@ -31,8 +32,8 @@ function createThreadOwnershipManager(claimThread) {
31
32
  scheduleRetry(threadId, claimGeneration);
32
33
  return false;
33
34
  }
34
- retryDelayMs = OWNERSHIP_RETRY_INITIAL_DELAY_MS;
35
- currentClaim = nextClaim;
35
+ state.retryDelayMs = OWNERSHIP_RETRY_INITIAL_DELAY_MS;
36
+ state.claim = nextClaim;
36
37
  return true;
37
38
  } catch (error) {
38
39
  scheduleRetry(threadId, claimGeneration);
@@ -42,19 +43,34 @@ function createThreadOwnershipManager(claimThread) {
42
43
  };
43
44
  return {
44
45
  async claim(threadId) {
45
- const claimGeneration = ++generation;
46
- clearRetry();
47
- retryDelayMs = OWNERSHIP_RETRY_INITIAL_DELAY_MS;
48
- currentClaim?.unsubscribe();
49
- currentClaim = void 0;
46
+ if (closed) return false;
50
47
  if (!threadId) return false;
51
- return attemptClaim(threadId, claimGeneration, true);
48
+ const existing = states.get(threadId);
49
+ if (existing) {
50
+ clearRetry(existing);
51
+ existing.claim?.unsubscribe();
52
+ }
53
+ const state = {
54
+ generation: ++nextGeneration,
55
+ retryDelayMs: OWNERSHIP_RETRY_INITIAL_DELAY_MS
56
+ };
57
+ states.set(threadId, state);
58
+ return attemptClaim(threadId, state.generation, true);
59
+ },
60
+ release(threadId) {
61
+ const state = states.get(threadId);
62
+ if (!state) return;
63
+ clearRetry(state);
64
+ state.claim?.unsubscribe();
65
+ states.delete(threadId);
52
66
  },
53
67
  close() {
54
- generation++;
55
- clearRetry();
56
- currentClaim?.unsubscribe();
57
- currentClaim = void 0;
68
+ closed = true;
69
+ for (const state of states.values()) {
70
+ clearRetry(state);
71
+ state.claim?.unsubscribe();
72
+ }
73
+ states.clear();
58
74
  }
59
75
  };
60
76
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ownership.js","names":[],"sources":["../../src/agent-connections/ownership.ts"],"sourcesContent":["export interface ThreadOwnershipClaim {\n claimed: boolean;\n unsubscribe(): void;\n}\n\nconst OWNERSHIP_RETRY_INITIAL_DELAY_MS = 250;\nconst OWNERSHIP_RETRY_MAX_DELAY_MS = 5_000;\n\nexport function createThreadOwnershipManager(claimThread: (threadId: string) => Promise<ThreadOwnershipClaim>): {\n claim(threadId?: string | null): Promise<boolean>;\n close(): void;\n} {\n let generation = 0;\n let currentClaim: ThreadOwnershipClaim | undefined;\n let retryTimer: ReturnType<typeof setTimeout> | undefined;\n let retryDelayMs = OWNERSHIP_RETRY_INITIAL_DELAY_MS;\n\n const clearRetry = () => {\n if (retryTimer) clearTimeout(retryTimer);\n retryTimer = undefined;\n };\n\n const scheduleRetry = (threadId: string, claimGeneration: number) => {\n if (claimGeneration !== generation || retryTimer) return;\n const delayMs = retryDelayMs;\n retryDelayMs = Math.min(retryDelayMs * 2, OWNERSHIP_RETRY_MAX_DELAY_MS);\n retryTimer = setTimeout(() => {\n retryTimer = undefined;\n void attemptClaim(threadId, claimGeneration, false);\n }, delayMs);\n retryTimer.unref?.();\n };\n\n const attemptClaim = async (threadId: string, claimGeneration: number, propagateError: boolean): Promise<boolean> => {\n try {\n const nextClaim = await claimThread(threadId);\n if (claimGeneration !== generation) {\n nextClaim.unsubscribe();\n return false;\n }\n if (!nextClaim.claimed) {\n scheduleRetry(threadId, claimGeneration);\n return false;\n }\n retryDelayMs = OWNERSHIP_RETRY_INITIAL_DELAY_MS;\n currentClaim = nextClaim;\n return true;\n } catch (error) {\n scheduleRetry(threadId, claimGeneration);\n if (propagateError) throw error;\n return false;\n }\n };\n\n return {\n async claim(threadId) {\n const claimGeneration = ++generation;\n clearRetry();\n retryDelayMs = OWNERSHIP_RETRY_INITIAL_DELAY_MS;\n currentClaim?.unsubscribe();\n currentClaim = undefined;\n if (!threadId) return false;\n return attemptClaim(threadId, claimGeneration, true);\n },\n close() {\n generation++;\n clearRetry();\n currentClaim?.unsubscribe();\n currentClaim = undefined;\n },\n };\n}\n"],"mappings":";AAKA,MAAM,mCAAmC;AACzC,MAAM,+BAA+B;AAErC,SAAgB,6BAA6B,aAG3C;CACA,IAAI,aAAa;CACjB,IAAI;CACJ,IAAI;CACJ,IAAI,eAAe;CAEnB,MAAM,mBAAmB;EACvB,IAAI,YAAY,aAAa,UAAU;EACvC,aAAa,KAAA;CACf;CAEA,MAAM,iBAAiB,UAAkB,oBAA4B;EACnE,IAAI,oBAAoB,cAAc,YAAY;EAClD,MAAM,UAAU;EAChB,eAAe,KAAK,IAAI,eAAe,GAAG,4BAA4B;EACtE,aAAa,iBAAiB;GAC5B,aAAa,KAAA;GACb,aAAkB,UAAU,iBAAiB,KAAK;EACpD,GAAG,OAAO;EACV,WAAW,QAAQ;CACrB;CAEA,MAAM,eAAe,OAAO,UAAkB,iBAAyB,mBAA8C;EACnH,IAAI;GACF,MAAM,YAAY,MAAM,YAAY,QAAQ;GAC5C,IAAI,oBAAoB,YAAY;IAClC,UAAU,YAAY;IACtB,OAAO;GACT;GACA,IAAI,CAAC,UAAU,SAAS;IACtB,cAAc,UAAU,eAAe;IACvC,OAAO;GACT;GACA,eAAe;GACf,eAAe;GACf,OAAO;EACT,SAAS,OAAO;GACd,cAAc,UAAU,eAAe;GACvC,IAAI,gBAAgB,MAAM;GAC1B,OAAO;EACT;CACF;CAEA,OAAO;EACL,MAAM,MAAM,UAAU;GACpB,MAAM,kBAAkB,EAAE;GAC1B,WAAW;GACX,eAAe;GACf,cAAc,YAAY;GAC1B,eAAe,KAAA;GACf,IAAI,CAAC,UAAU,OAAO;GACtB,OAAO,aAAa,UAAU,iBAAiB,IAAI;EACrD;EACA,QAAQ;GACN;GACA,WAAW;GACX,cAAc,YAAY;GAC1B,eAAe,KAAA;EACjB;CACF;AACF"}
1
+ {"version":3,"file":"ownership.js","names":[],"sources":["../../src/agent-connections/ownership.ts"],"sourcesContent":["export interface ThreadOwnershipClaim {\n claimed: boolean;\n unsubscribe(): void;\n}\n\nconst OWNERSHIP_RETRY_INITIAL_DELAY_MS = 250;\nconst OWNERSHIP_RETRY_MAX_DELAY_MS = 5_000;\n\n/**\n * Per-thread claim bookkeeping. One entry per thread this session has bound, so\n * a thread the user has navigated away from stays claimed — and therefore stays\n * addressable by peers — until the session itself is torn down.\n */\ntype ThreadClaimState = {\n /** Bumped on every new attempt for this thread; late resolutions are dropped. */\n generation: number;\n claim?: ThreadOwnershipClaim;\n retryTimer?: ReturnType<typeof setTimeout>;\n retryDelayMs: number;\n};\n\nexport function createThreadOwnershipManager(claimThread: (threadId: string) => Promise<ThreadOwnershipClaim>): {\n claim(threadId?: string | null): Promise<boolean>;\n release(threadId: string): void;\n close(): void;\n} {\n const states = new Map<string, ThreadClaimState>();\n // Manager-scoped so the counter keeps climbing across `release`/re-claim of the\n // same thread id: a per-thread counter restarts at 1 after a release, letting a\n // pre-release attempt match the new state and be retained.\n let nextGeneration = 0;\n let closed = false;\n\n const clearRetry = (state: ThreadClaimState) => {\n if (state.retryTimer) clearTimeout(state.retryTimer);\n state.retryTimer = undefined;\n };\n\n const scheduleRetry = (threadId: string, claimGeneration: number) => {\n const state = states.get(threadId);\n if (closed || !state || state.generation !== claimGeneration || state.retryTimer) return;\n const delayMs = state.retryDelayMs;\n state.retryDelayMs = Math.min(state.retryDelayMs * 2, OWNERSHIP_RETRY_MAX_DELAY_MS);\n state.retryTimer = setTimeout(() => {\n state.retryTimer = undefined;\n void attemptClaim(threadId, claimGeneration, false);\n }, delayMs);\n state.retryTimer.unref?.();\n };\n\n const attemptClaim = async (threadId: string, claimGeneration: number, propagateError: boolean): Promise<boolean> => {\n try {\n const nextClaim = await claimThread(threadId);\n const state = states.get(threadId);\n // The session closed, or a newer attempt superseded this one while the\n // ownership request was in flight — the late claim must not be retained.\n if (closed || !state || state.generation !== claimGeneration) {\n nextClaim.unsubscribe();\n return false;\n }\n if (!nextClaim.claimed) {\n scheduleRetry(threadId, claimGeneration);\n return false;\n }\n state.retryDelayMs = OWNERSHIP_RETRY_INITIAL_DELAY_MS;\n state.claim = nextClaim;\n return true;\n } catch (error) {\n scheduleRetry(threadId, claimGeneration);\n if (propagateError) throw error;\n return false;\n }\n };\n\n return {\n async claim(threadId) {\n if (closed) return false;\n // A thread-less transition has no claim to make. Threads already claimed\n // stay claimed: being handed no thread is not a reason to drop them.\n if (!threadId) return false;\n\n // Re-claiming the thread supersedes only that thread's previous attempt —\n // the core claim is re-published so title/metadata changes made while the\n // session was away are picked up. Other threads' claims are untouched.\n const existing = states.get(threadId);\n if (existing) {\n clearRetry(existing);\n existing.claim?.unsubscribe();\n }\n const state: ThreadClaimState = {\n generation: ++nextGeneration,\n retryDelayMs: OWNERSHIP_RETRY_INITIAL_DELAY_MS,\n };\n states.set(threadId, state);\n return attemptClaim(threadId, state.generation, true);\n },\n release(threadId) {\n // The thread no longer exists, so its claim has nothing left to answer for.\n // Dropping the state also makes any in-flight attempt for it unsubscribe\n // itself instead of retaining a claim for a deleted thread.\n const state = states.get(threadId);\n if (!state) return;\n clearRetry(state);\n state.claim?.unsubscribe();\n states.delete(threadId);\n },\n close() {\n closed = true;\n for (const state of states.values()) {\n clearRetry(state);\n state.claim?.unsubscribe();\n }\n states.clear();\n },\n };\n}\n"],"mappings":";AAKA,MAAM,mCAAmC;AACzC,MAAM,+BAA+B;AAerC,SAAgB,6BAA6B,aAI3C;CACA,MAAM,yBAAS,IAAI,IAA8B;CAIjD,IAAI,iBAAiB;CACrB,IAAI,SAAS;CAEb,MAAM,cAAc,UAA4B;EAC9C,IAAI,MAAM,YAAY,aAAa,MAAM,UAAU;EACnD,MAAM,aAAa,KAAA;CACrB;CAEA,MAAM,iBAAiB,UAAkB,oBAA4B;EACnE,MAAM,QAAQ,OAAO,IAAI,QAAQ;EACjC,IAAI,UAAU,CAAC,SAAS,MAAM,eAAe,mBAAmB,MAAM,YAAY;EAClF,MAAM,UAAU,MAAM;EACtB,MAAM,eAAe,KAAK,IAAI,MAAM,eAAe,GAAG,4BAA4B;EAClF,MAAM,aAAa,iBAAiB;GAClC,MAAM,aAAa,KAAA;GACnB,aAAkB,UAAU,iBAAiB,KAAK;EACpD,GAAG,OAAO;EACV,MAAM,WAAW,QAAQ;CAC3B;CAEA,MAAM,eAAe,OAAO,UAAkB,iBAAyB,mBAA8C;EACnH,IAAI;GACF,MAAM,YAAY,MAAM,YAAY,QAAQ;GAC5C,MAAM,QAAQ,OAAO,IAAI,QAAQ;GAGjC,IAAI,UAAU,CAAC,SAAS,MAAM,eAAe,iBAAiB;IAC5D,UAAU,YAAY;IACtB,OAAO;GACT;GACA,IAAI,CAAC,UAAU,SAAS;IACtB,cAAc,UAAU,eAAe;IACvC,OAAO;GACT;GACA,MAAM,eAAe;GACrB,MAAM,QAAQ;GACd,OAAO;EACT,SAAS,OAAO;GACd,cAAc,UAAU,eAAe;GACvC,IAAI,gBAAgB,MAAM;GAC1B,OAAO;EACT;CACF;CAEA,OAAO;EACL,MAAM,MAAM,UAAU;GACpB,IAAI,QAAQ,OAAO;GAGnB,IAAI,CAAC,UAAU,OAAO;GAKtB,MAAM,WAAW,OAAO,IAAI,QAAQ;GACpC,IAAI,UAAU;IACZ,WAAW,QAAQ;IACnB,SAAS,OAAO,YAAY;GAC9B;GACA,MAAM,QAA0B;IAC9B,YAAY,EAAE;IACd,cAAc;GAChB;GACA,OAAO,IAAI,UAAU,KAAK;GAC1B,OAAO,aAAa,UAAU,MAAM,YAAY,IAAI;EACtD;EACA,QAAQ,UAAU;GAIhB,MAAM,QAAQ,OAAO,IAAI,QAAQ;GACjC,IAAI,CAAC,OAAO;GACZ,WAAW,KAAK;GAChB,MAAM,OAAO,YAAY;GACzB,OAAO,OAAO,QAAQ;EACxB;EACA,QAAQ;GACN,SAAS;GACT,KAAK,MAAM,SAAS,OAAO,OAAO,GAAG;IACnC,WAAW,KAAK;IAChB,MAAM,OAAO,YAAY;GAC3B;GACA,OAAO,MAAM;EACf;CACF;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/agent-connections/registry.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAEhE,OAAO,KAAK,EAAE,iBAAiB,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAIvF,eAAO,MAAM,uCAAuC,oCAAoC,CAAC;AAEzF,KAAK,mBAAmB,GAAG,CAAC,OAAO,EAAE,sBAAsB,KAAK,OAAO,CAAC,iBAAiB,EAAE,CAAC,GAAG,iBAAiB,EAAE,CAAC;AAEnH,MAAM,WAAW,8BAA8B;IAC7C,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,mBAAmB,CAAC;CACjC;AAED,qBAAa,uBAAuB;;gBAItB,OAAO,GAAE,8BAAmC;IAKlD,aAAa,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;IAQxE,SAAS,CAAC,OAAO,EAAE,sBAAsB,EAAE,UAAU,CAAC,EAAE,kBAAkB,EAAE,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;IAwBvG,cAAc,CAAC,OAAO,EAAE,sBAAsB,EAAE,UAAU,CAAC,EAAE,kBAAkB,EAAE,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;CA8BnH;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,IAAI,CAAC,iBAAiB,EAAE,IAAI,CAAC,GAAG;IAAE,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAE1F"}
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/agent-connections/registry.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAEhE,OAAO,KAAK,EAAE,iBAAiB,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AASvF,eAAO,MAAM,uCAAuC,oCAAoC,CAAC;AAEzF,KAAK,mBAAmB,GAAG,CAAC,OAAO,EAAE,sBAAsB,KAAK,OAAO,CAAC,iBAAiB,EAAE,CAAC,GAAG,iBAAiB,EAAE,CAAC;AAEnH,MAAM,WAAW,8BAA8B;IAC7C,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,mBAAmB,CAAC;CACjC;AAED,qBAAa,uBAAuB;;gBAItB,OAAO,GAAE,8BAAmC;IAKlD,aAAa,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;IAQxE,SAAS,CAAC,OAAO,EAAE,sBAAsB,EAAE,UAAU,CAAC,EAAE,kBAAkB,EAAE,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;IAwBvG,cAAc,CAAC,OAAO,EAAE,sBAAsB,EAAE,UAAU,CAAC,EAAE,kBAAkB,EAAE,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;CA8BnH;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,IAAI,CAAC,iBAAiB,EAAE,IAAI,CAAC,GAAG;IAAE,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAE1F"}
@@ -85,12 +85,14 @@ function normalizePeerIdentity(peer, now) {
85
85
  title: candidate.title,
86
86
  mode: candidate.mode,
87
87
  pid: candidate.pid,
88
- lastSeenAt: typeof candidate.lastSeenAt === "number" ? candidate.lastSeenAt : now
88
+ lastSeenAt: typeof candidate.lastSeenAt === "number" ? candidate.lastSeenAt : now,
89
+ ...peer.selfAdvertised === true ? { selfAdvertised: true } : {}
89
90
  };
90
91
  }
91
92
  function discoveredPeerToView(peer) {
93
+ const { selfAdvertised: _selfAdvertised, ...identity } = peer;
92
94
  return {
93
- ...peer,
95
+ ...identity,
94
96
  relationship: "none",
95
97
  presence: "advertised",
96
98
  displayStatus: "discovered",
@@ -156,6 +158,7 @@ function isPeerLike(value) {
156
158
  return !!value && typeof value === "object" && typeof value.resourceId === "string" && typeof value.threadId === "string";
157
159
  }
158
160
  function isSelf(peer, context) {
161
+ if (peer.selfAdvertised) return true;
159
162
  return (peer.agentId ?? "code-agent") === (context.agent?.agentId ?? "code-agent") && peer.resourceId === context.agent?.resourceId && peer.threadId === context.agent?.threadId;
160
163
  }
161
164
  function comparePeers(a, b) {
@@ -1 +1 @@
1
- {"version":3,"file":"registry.js","names":["#now","#listPeers","#discover","#discoverFromInjectedSource"],"sources":["../../src/agent-connections/registry.ts"],"sourcesContent":["import type { RequestContext } from '@mastra/core/request-context';\nimport type { AgentConnectionContext } from './thread-state.js';\nimport { readAgentConnections } from './thread-state.js';\nimport type { AgentPeerIdentity, AgentPeerView, ConnectedAgentPeer } from './types.js';\n\ntype NormalizedAgentPeerIdentity = Omit<AgentPeerIdentity, 'id' | 'agentId'> & { id: string; agentId: string };\n\nexport const AGENT_CONNECTIONS_DISCOVERY_CONTEXT_KEY = 'mastracode.agentConnectionPeers';\n\ntype PeerDiscoverySource = (context: AgentConnectionContext) => Promise<AgentPeerIdentity[]> | AgentPeerIdentity[];\n\nexport interface AgentConnectionRegistryOptions {\n now?: () => number;\n listPeers?: PeerDiscoverySource;\n}\n\nexport class AgentConnectionRegistry {\n readonly #now: () => number;\n readonly #listPeers?: PeerDiscoverySource;\n\n constructor(options: AgentConnectionRegistryOptions = {}) {\n this.#now = options.now ?? Date.now;\n this.#listPeers = options.listPeers;\n }\n\n async discoverPeers(context: AgentConnectionContext): Promise<AgentPeerView[]> {\n const discovered = await this.#discover(context);\n return discovered\n .filter(peer => !isSelf(peer, context))\n .map(peer => discoveredPeerToView(peer))\n .sort(comparePeers);\n }\n\n async listPeers(context: AgentConnectionContext, savedPeers?: ConnectedAgentPeer[]): Promise<AgentPeerView[]> {\n const saved = savedPeers ?? (await readAgentConnections(context));\n const discovered = await this.discoverPeers(context);\n const byId = new Map<string, AgentPeerView>(discovered.map(peer => [peer.id, peer]));\n\n for (const peer of saved) {\n const canonicalId = stablePeerId(peer);\n const discoveredPeer = peer.id === canonicalId ? byId.get(peer.id) : undefined;\n if (discoveredPeer && sameEndpoint(peer, discoveredPeer)) {\n byId.set(peer.id, {\n ...discoveredPeer,\n relationship: 'saved',\n displayStatus: 'connected',\n canAttemptSend: true,\n connectedAt: peer.connectedAt,\n });\n } else {\n byId.set(peer.id, savedPeerToAbsentView(peer));\n }\n }\n\n return [...byId.values()].sort(comparePeers);\n }\n\n async connectedPeers(context: AgentConnectionContext, savedPeers?: ConnectedAgentPeer[]): Promise<AgentPeerView[]> {\n return (await this.listPeers(context, savedPeers)).filter(peer => peer.relationship === 'saved');\n }\n\n async #discover(context: AgentConnectionContext): Promise<NormalizedAgentPeerIdentity[]> {\n const sources = await Promise.allSettled([\n Promise.resolve().then(() => this.#discoverFromInjectedSource(context)),\n Promise.resolve().then(() => discoverFromCoreAgent(context)),\n Promise.resolve().then(() => discoverFromRequestContext(context.requestContext)),\n Promise.resolve().then(() => discoverFromEnv()),\n ]);\n const byId = new Map<string, NormalizedAgentPeerIdentity>();\n for (const source of sources) {\n if (source.status === 'rejected') continue;\n for (const peer of source.value) {\n const normalized = normalizePeerIdentity(peer, this.#now());\n if (normalized) byId.set(normalized.id, normalized);\n }\n }\n if (byId.size === 0) {\n const rejected = sources.find(source => source.status === 'rejected');\n if (rejected) throw rejected.reason;\n }\n return [...byId.values()].sort(comparePeers);\n }\n\n async #discoverFromInjectedSource(context: AgentConnectionContext): Promise<AgentPeerIdentity[]> {\n if (!this.#listPeers) return [];\n return this.#listPeers(context);\n }\n}\n\nexport function stablePeerId(peer: Omit<AgentPeerIdentity, 'id'> & { id?: string }): string {\n return [peer.agentId ?? 'code-agent', peer.resourceId, peer.threadId].map(part => encodeURIComponent(part)).join(':');\n}\n\nfunction normalizePeerIdentity(peer: unknown, now: number): NormalizedAgentPeerIdentity | undefined {\n if (!peer || typeof peer !== 'object') return;\n const candidate = peer as Partial<AgentPeerIdentity>;\n if (!candidate.resourceId || !candidate.threadId) return;\n const agentId = candidate.agentId ?? 'code-agent';\n const id = stablePeerId({ agentId, resourceId: candidate.resourceId, threadId: candidate.threadId });\n if (candidate.id && candidate.id !== id) return;\n return {\n id,\n agentId,\n resourceId: candidate.resourceId,\n threadId: candidate.threadId,\n label: candidate.label,\n title: candidate.title,\n mode: candidate.mode,\n pid: candidate.pid,\n lastSeenAt: typeof candidate.lastSeenAt === 'number' ? candidate.lastSeenAt : now,\n };\n}\n\nfunction discoveredPeerToView(peer: NormalizedAgentPeerIdentity): AgentPeerView {\n return {\n ...peer,\n relationship: 'none',\n presence: 'advertised',\n displayStatus: 'discovered',\n canAttemptSend: false,\n };\n}\n\nfunction savedPeerToAbsentView(peer: ConnectedAgentPeer): AgentPeerView {\n return {\n id: peer.id,\n agentId: peer.agentId ?? 'code-agent',\n resourceId: peer.resourceId,\n threadId: peer.threadId,\n label: peer.label,\n title: peer.title,\n mode: peer.mode,\n relationship: 'saved',\n presence: 'absent',\n displayStatus: 'saved',\n canAttemptSend: false,\n pid: peer.pid,\n connectedAt: peer.connectedAt,\n lastSeenAt: peer.lastSeenAt,\n };\n}\n\nfunction sameEndpoint(a: AgentPeerIdentity, b: AgentPeerIdentity): boolean {\n return (\n (a.agentId ?? 'code-agent') === (b.agentId ?? 'code-agent') &&\n a.resourceId === b.resourceId &&\n a.threadId === b.threadId\n );\n}\n\nasync function discoverFromCoreAgent(context: AgentConnectionContext): Promise<AgentPeerIdentity[]> {\n if (!context.runtimeAgent?.discoverThreadPeers) return [];\n const peers = await context.runtimeAgent.discoverThreadPeers({ timeoutMs: 100 });\n if (!Array.isArray(peers)) return [];\n return peers.filter(isPeerLike).map(peer => {\n const candidate = peer as AgentPeerIdentity & { metadata?: { mode?: unknown } };\n const metadata = (peer as { metadata?: unknown }).metadata;\n const mode =\n metadata && typeof metadata === 'object' && typeof (metadata as { mode?: unknown }).mode === 'string'\n ? (metadata as { mode: string }).mode\n : candidate.mode;\n return { ...candidate, mode };\n });\n}\n\nfunction discoverFromRequestContext(requestContext: RequestContext | undefined): AgentPeerIdentity[] {\n const value = requestContext?.get(AGENT_CONNECTIONS_DISCOVERY_CONTEXT_KEY);\n if (Array.isArray(value)) return value.filter(isPeerLike) as AgentPeerIdentity[];\n if (value && typeof value === 'object') {\n const peers = (value as { peers?: unknown }).peers;\n if (Array.isArray(peers)) return peers.filter(isPeerLike) as AgentPeerIdentity[];\n }\n return [];\n}\n\nfunction discoverFromEnv(): AgentPeerIdentity[] {\n const raw = process.env.MASTRACODE_AGENT_CONNECTION_PEERS;\n if (!raw) return [];\n try {\n const parsed = JSON.parse(raw) as unknown;\n const peers = Array.isArray(parsed) ? parsed : (parsed as { peers?: unknown })?.peers;\n return Array.isArray(peers) ? (peers.filter(isPeerLike) as AgentPeerIdentity[]) : [];\n } catch {\n return [];\n }\n}\n\nfunction isPeerLike(value: unknown): boolean {\n return (\n !!value &&\n typeof value === 'object' &&\n typeof (value as AgentPeerIdentity).resourceId === 'string' &&\n typeof (value as AgentPeerIdentity).threadId === 'string'\n );\n}\n\nfunction isSelf(peer: AgentPeerIdentity, context: AgentConnectionContext): boolean {\n return (\n (peer.agentId ?? 'code-agent') === (context.agent?.agentId ?? 'code-agent') &&\n peer.resourceId === context.agent?.resourceId &&\n peer.threadId === context.agent?.threadId\n );\n}\n\nfunction comparePeers(a: { id: string }, b: { id: string }): number {\n return a.id.localeCompare(b.id);\n}\n"],"mappings":";;AAOA,MAAa,0CAA0C;AASvD,IAAa,0BAAb,MAAqC;CACnC;CACA;CAEA,YAAY,UAA0C,CAAC,GAAG;EACxD,KAAKA,OAAO,QAAQ,OAAO,KAAK;EAChC,KAAKC,aAAa,QAAQ;CAC5B;CAEA,MAAM,cAAc,SAA2D;EAE7E,QAAO,MADkB,KAAKC,UAAU,OAAO,EAAA,CAE5C,QAAO,SAAQ,CAAC,OAAO,MAAM,OAAO,CAAC,CAAC,CACtC,KAAI,SAAQ,qBAAqB,IAAI,CAAC,CAAC,CACvC,KAAK,YAAY;CACtB;CAEA,MAAM,UAAU,SAAiC,YAA6D;EAC5G,MAAM,QAAQ,cAAe,MAAM,qBAAqB,OAAO;EAC/D,MAAM,aAAa,MAAM,KAAK,cAAc,OAAO;EACnD,MAAM,OAAO,IAAI,IAA2B,WAAW,KAAI,SAAQ,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC;EAEnF,KAAK,MAAM,QAAQ,OAAO;GACxB,MAAM,cAAc,aAAa,IAAI;GACrC,MAAM,iBAAiB,KAAK,OAAO,cAAc,KAAK,IAAI,KAAK,EAAE,IAAI,KAAA;GACrE,IAAI,kBAAkB,aAAa,MAAM,cAAc,GACrD,KAAK,IAAI,KAAK,IAAI;IAChB,GAAG;IACH,cAAc;IACd,eAAe;IACf,gBAAgB;IAChB,aAAa,KAAK;GACpB,CAAC;QAED,KAAK,IAAI,KAAK,IAAI,sBAAsB,IAAI,CAAC;EAEjD;EAEA,OAAO,CAAC,GAAG,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,YAAY;CAC7C;CAEA,MAAM,eAAe,SAAiC,YAA6D;EACjH,QAAQ,MAAM,KAAK,UAAU,SAAS,UAAU,EAAA,CAAG,QAAO,SAAQ,KAAK,iBAAiB,OAAO;CACjG;CAEA,MAAMA,UAAU,SAAyE;EACvF,MAAM,UAAU,MAAM,QAAQ,WAAW;GACvC,QAAQ,QAAQ,CAAC,CAAC,WAAW,KAAKC,4BAA4B,OAAO,CAAC;GACtE,QAAQ,QAAQ,CAAC,CAAC,WAAW,sBAAsB,OAAO,CAAC;GAC3D,QAAQ,QAAQ,CAAC,CAAC,WAAW,2BAA2B,QAAQ,cAAc,CAAC;GAC/E,QAAQ,QAAQ,CAAC,CAAC,WAAW,gBAAgB,CAAC;EAChD,CAAC;EACD,MAAM,uBAAO,IAAI,IAAyC;EAC1D,KAAK,MAAM,UAAU,SAAS;GAC5B,IAAI,OAAO,WAAW,YAAY;GAClC,KAAK,MAAM,QAAQ,OAAO,OAAO;IAC/B,MAAM,aAAa,sBAAsB,MAAM,KAAKH,KAAK,CAAC;IAC1D,IAAI,YAAY,KAAK,IAAI,WAAW,IAAI,UAAU;GACpD;EACF;EACA,IAAI,KAAK,SAAS,GAAG;GACnB,MAAM,WAAW,QAAQ,MAAK,WAAU,OAAO,WAAW,UAAU;GACpE,IAAI,UAAU,MAAM,SAAS;EAC/B;EACA,OAAO,CAAC,GAAG,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,YAAY;CAC7C;CAEA,MAAMG,4BAA4B,SAA+D;EAC/F,IAAI,CAAC,KAAKF,YAAY,OAAO,CAAC;EAC9B,OAAO,KAAKA,WAAW,OAAO;CAChC;AACF;AAEA,SAAgB,aAAa,MAA+D;CAC1F,OAAO;EAAC,KAAK,WAAW;EAAc,KAAK;EAAY,KAAK;CAAQ,CAAC,CAAC,KAAI,SAAQ,mBAAmB,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG;AACtH;AAEA,SAAS,sBAAsB,MAAe,KAAsD;CAClG,IAAI,CAAC,QAAQ,OAAO,SAAS,UAAU;CACvC,MAAM,YAAY;CAClB,IAAI,CAAC,UAAU,cAAc,CAAC,UAAU,UAAU;CAClD,MAAM,UAAU,UAAU,WAAW;CACrC,MAAM,KAAK,aAAa;EAAE;EAAS,YAAY,UAAU;EAAY,UAAU,UAAU;CAAS,CAAC;CACnG,IAAI,UAAU,MAAM,UAAU,OAAO,IAAI;CACzC,OAAO;EACL;EACA;EACA,YAAY,UAAU;EACtB,UAAU,UAAU;EACpB,OAAO,UAAU;EACjB,OAAO,UAAU;EACjB,MAAM,UAAU;EAChB,KAAK,UAAU;EACf,YAAY,OAAO,UAAU,eAAe,WAAW,UAAU,aAAa;CAChF;AACF;AAEA,SAAS,qBAAqB,MAAkD;CAC9E,OAAO;EACL,GAAG;EACH,cAAc;EACd,UAAU;EACV,eAAe;EACf,gBAAgB;CAClB;AACF;AAEA,SAAS,sBAAsB,MAAyC;CACtE,OAAO;EACL,IAAI,KAAK;EACT,SAAS,KAAK,WAAW;EACzB,YAAY,KAAK;EACjB,UAAU,KAAK;EACf,OAAO,KAAK;EACZ,OAAO,KAAK;EACZ,MAAM,KAAK;EACX,cAAc;EACd,UAAU;EACV,eAAe;EACf,gBAAgB;EAChB,KAAK,KAAK;EACV,aAAa,KAAK;EAClB,YAAY,KAAK;CACnB;AACF;AAEA,SAAS,aAAa,GAAsB,GAA+B;CACzE,QACG,EAAE,WAAW,mBAAmB,EAAE,WAAW,iBAC9C,EAAE,eAAe,EAAE,cACnB,EAAE,aAAa,EAAE;AAErB;AAEA,eAAe,sBAAsB,SAA+D;CAClG,IAAI,CAAC,QAAQ,cAAc,qBAAqB,OAAO,CAAC;CACxD,MAAM,QAAQ,MAAM,QAAQ,aAAa,oBAAoB,EAAE,WAAW,IAAI,CAAC;CAC/E,IAAI,CAAC,MAAM,QAAQ,KAAK,GAAG,OAAO,CAAC;CACnC,OAAO,MAAM,OAAO,UAAU,CAAC,CAAC,KAAI,SAAQ;EAC1C,MAAM,YAAY;EAClB,MAAM,WAAY,KAAgC;EAClD,MAAM,OACJ,YAAY,OAAO,aAAa,YAAY,OAAQ,SAAgC,SAAS,WACxF,SAA8B,OAC/B,UAAU;EAChB,OAAO;GAAE,GAAG;GAAW;EAAK;CAC9B,CAAC;AACH;AAEA,SAAS,2BAA2B,gBAAiE;CACnG,MAAM,QAAQ,gBAAgB,IAAI,uCAAuC;CACzE,IAAI,MAAM,QAAQ,KAAK,GAAG,OAAO,MAAM,OAAO,UAAU;CACxD,IAAI,SAAS,OAAO,UAAU,UAAU;EACtC,MAAM,QAAS,MAA8B;EAC7C,IAAI,MAAM,QAAQ,KAAK,GAAG,OAAO,MAAM,OAAO,UAAU;CAC1D;CACA,OAAO,CAAC;AACV;AAEA,SAAS,kBAAuC;CAC9C,MAAM,MAAM,QAAQ,IAAI;CACxB,IAAI,CAAC,KAAK,OAAO,CAAC;CAClB,IAAI;EACF,MAAM,SAAS,KAAK,MAAM,GAAG;EAC7B,MAAM,QAAQ,MAAM,QAAQ,MAAM,IAAI,SAAU,QAAgC;EAChF,OAAO,MAAM,QAAQ,KAAK,IAAK,MAAM,OAAO,UAAU,IAA4B,CAAC;CACrF,QAAQ;EACN,OAAO,CAAC;CACV;AACF;AAEA,SAAS,WAAW,OAAyB;CAC3C,OACE,CAAC,CAAC,SACF,OAAO,UAAU,YACjB,OAAQ,MAA4B,eAAe,YACnD,OAAQ,MAA4B,aAAa;AAErD;AAEA,SAAS,OAAO,MAAyB,SAA0C;CACjF,QACG,KAAK,WAAW,mBAAmB,QAAQ,OAAO,WAAW,iBAC9D,KAAK,eAAe,QAAQ,OAAO,cACnC,KAAK,aAAa,QAAQ,OAAO;AAErC;AAEA,SAAS,aAAa,GAAmB,GAA2B;CAClE,OAAO,EAAE,GAAG,cAAc,EAAE,EAAE;AAChC"}
1
+ {"version":3,"file":"registry.js","names":["#now","#listPeers","#discover","#discoverFromInjectedSource"],"sources":["../../src/agent-connections/registry.ts"],"sourcesContent":["import type { RequestContext } from '@mastra/core/request-context';\nimport type { AgentConnectionContext } from './thread-state.js';\nimport { readAgentConnections } from './thread-state.js';\nimport type { AgentPeerIdentity, AgentPeerView, ConnectedAgentPeer } from './types.js';\n\ntype NormalizedAgentPeerIdentity = Omit<AgentPeerIdentity, 'id' | 'agentId'> & {\n id: string;\n agentId: string;\n /** Carried through normalization so {@link isSelf} can drop our own advertisements. */\n selfAdvertised?: boolean;\n};\n\nexport const AGENT_CONNECTIONS_DISCOVERY_CONTEXT_KEY = 'mastracode.agentConnectionPeers';\n\ntype PeerDiscoverySource = (context: AgentConnectionContext) => Promise<AgentPeerIdentity[]> | AgentPeerIdentity[];\n\nexport interface AgentConnectionRegistryOptions {\n now?: () => number;\n listPeers?: PeerDiscoverySource;\n}\n\nexport class AgentConnectionRegistry {\n readonly #now: () => number;\n readonly #listPeers?: PeerDiscoverySource;\n\n constructor(options: AgentConnectionRegistryOptions = {}) {\n this.#now = options.now ?? Date.now;\n this.#listPeers = options.listPeers;\n }\n\n async discoverPeers(context: AgentConnectionContext): Promise<AgentPeerView[]> {\n const discovered = await this.#discover(context);\n return discovered\n .filter(peer => !isSelf(peer, context))\n .map(peer => discoveredPeerToView(peer))\n .sort(comparePeers);\n }\n\n async listPeers(context: AgentConnectionContext, savedPeers?: ConnectedAgentPeer[]): Promise<AgentPeerView[]> {\n const saved = savedPeers ?? (await readAgentConnections(context));\n const discovered = await this.discoverPeers(context);\n const byId = new Map<string, AgentPeerView>(discovered.map(peer => [peer.id, peer]));\n\n for (const peer of saved) {\n const canonicalId = stablePeerId(peer);\n const discoveredPeer = peer.id === canonicalId ? byId.get(peer.id) : undefined;\n if (discoveredPeer && sameEndpoint(peer, discoveredPeer)) {\n byId.set(peer.id, {\n ...discoveredPeer,\n relationship: 'saved',\n displayStatus: 'connected',\n canAttemptSend: true,\n connectedAt: peer.connectedAt,\n });\n } else {\n byId.set(peer.id, savedPeerToAbsentView(peer));\n }\n }\n\n return [...byId.values()].sort(comparePeers);\n }\n\n async connectedPeers(context: AgentConnectionContext, savedPeers?: ConnectedAgentPeer[]): Promise<AgentPeerView[]> {\n return (await this.listPeers(context, savedPeers)).filter(peer => peer.relationship === 'saved');\n }\n\n async #discover(context: AgentConnectionContext): Promise<NormalizedAgentPeerIdentity[]> {\n const sources = await Promise.allSettled([\n Promise.resolve().then(() => this.#discoverFromInjectedSource(context)),\n Promise.resolve().then(() => discoverFromCoreAgent(context)),\n Promise.resolve().then(() => discoverFromRequestContext(context.requestContext)),\n Promise.resolve().then(() => discoverFromEnv()),\n ]);\n const byId = new Map<string, NormalizedAgentPeerIdentity>();\n for (const source of sources) {\n if (source.status === 'rejected') continue;\n for (const peer of source.value) {\n const normalized = normalizePeerIdentity(peer, this.#now());\n if (normalized) byId.set(normalized.id, normalized);\n }\n }\n if (byId.size === 0) {\n const rejected = sources.find(source => source.status === 'rejected');\n if (rejected) throw rejected.reason;\n }\n return [...byId.values()].sort(comparePeers);\n }\n\n async #discoverFromInjectedSource(context: AgentConnectionContext): Promise<AgentPeerIdentity[]> {\n if (!this.#listPeers) return [];\n return this.#listPeers(context);\n }\n}\n\nexport function stablePeerId(peer: Omit<AgentPeerIdentity, 'id'> & { id?: string }): string {\n return [peer.agentId ?? 'code-agent', peer.resourceId, peer.threadId].map(part => encodeURIComponent(part)).join(':');\n}\n\nfunction normalizePeerIdentity(peer: unknown, now: number): NormalizedAgentPeerIdentity | undefined {\n if (!peer || typeof peer !== 'object') return;\n const candidate = peer as Partial<AgentPeerIdentity>;\n if (!candidate.resourceId || !candidate.threadId) return;\n const agentId = candidate.agentId ?? 'code-agent';\n const id = stablePeerId({ agentId, resourceId: candidate.resourceId, threadId: candidate.threadId });\n if (candidate.id && candidate.id !== id) return;\n return {\n id,\n agentId,\n resourceId: candidate.resourceId,\n threadId: candidate.threadId,\n label: candidate.label,\n title: candidate.title,\n mode: candidate.mode,\n pid: candidate.pid,\n lastSeenAt: typeof candidate.lastSeenAt === 'number' ? candidate.lastSeenAt : now,\n ...((peer as { selfAdvertised?: unknown }).selfAdvertised === true ? { selfAdvertised: true } : {}),\n };\n}\n\nfunction discoveredPeerToView(peer: NormalizedAgentPeerIdentity): AgentPeerView {\n const { selfAdvertised: _selfAdvertised, ...identity } = peer;\n return {\n ...identity,\n relationship: 'none',\n presence: 'advertised',\n displayStatus: 'discovered',\n canAttemptSend: false,\n };\n}\n\nfunction savedPeerToAbsentView(peer: ConnectedAgentPeer): AgentPeerView {\n return {\n id: peer.id,\n agentId: peer.agentId ?? 'code-agent',\n resourceId: peer.resourceId,\n threadId: peer.threadId,\n label: peer.label,\n title: peer.title,\n mode: peer.mode,\n relationship: 'saved',\n presence: 'absent',\n displayStatus: 'saved',\n canAttemptSend: false,\n pid: peer.pid,\n connectedAt: peer.connectedAt,\n lastSeenAt: peer.lastSeenAt,\n };\n}\n\nfunction sameEndpoint(a: AgentPeerIdentity, b: AgentPeerIdentity): boolean {\n return (\n (a.agentId ?? 'code-agent') === (b.agentId ?? 'code-agent') &&\n a.resourceId === b.resourceId &&\n a.threadId === b.threadId\n );\n}\n\nasync function discoverFromCoreAgent(context: AgentConnectionContext): Promise<AgentPeerIdentity[]> {\n if (!context.runtimeAgent?.discoverThreadPeers) return [];\n const peers = await context.runtimeAgent.discoverThreadPeers({ timeoutMs: 100 });\n if (!Array.isArray(peers)) return [];\n return peers.filter(isPeerLike).map(peer => {\n const candidate = peer as AgentPeerIdentity & { metadata?: { mode?: unknown } };\n const metadata = (peer as { metadata?: unknown }).metadata;\n const mode =\n metadata && typeof metadata === 'object' && typeof (metadata as { mode?: unknown }).mode === 'string'\n ? (metadata as { mode: string }).mode\n : candidate.mode;\n return { ...candidate, mode };\n });\n}\n\nfunction discoverFromRequestContext(requestContext: RequestContext | undefined): AgentPeerIdentity[] {\n const value = requestContext?.get(AGENT_CONNECTIONS_DISCOVERY_CONTEXT_KEY);\n if (Array.isArray(value)) return value.filter(isPeerLike) as AgentPeerIdentity[];\n if (value && typeof value === 'object') {\n const peers = (value as { peers?: unknown }).peers;\n if (Array.isArray(peers)) return peers.filter(isPeerLike) as AgentPeerIdentity[];\n }\n return [];\n}\n\nfunction discoverFromEnv(): AgentPeerIdentity[] {\n const raw = process.env.MASTRACODE_AGENT_CONNECTION_PEERS;\n if (!raw) return [];\n try {\n const parsed = JSON.parse(raw) as unknown;\n const peers = Array.isArray(parsed) ? parsed : (parsed as { peers?: unknown })?.peers;\n return Array.isArray(peers) ? (peers.filter(isPeerLike) as AgentPeerIdentity[]) : [];\n } catch {\n return [];\n }\n}\n\nfunction isPeerLike(value: unknown): boolean {\n return (\n !!value &&\n typeof value === 'object' &&\n typeof (value as AgentPeerIdentity).resourceId === 'string' &&\n typeof (value as AgentPeerIdentity).threadId === 'string'\n );\n}\n\nfunction isSelf(peer: NormalizedAgentPeerIdentity, context: AgentConnectionContext): boolean {\n // A session keeps every thread it has loaded advertised, so a thread of ours\n // that is no longer current still comes back from discovery — tagged as our own\n // by the agent that published it. Comparing only the current thread would\n // surface the others as peers a user could \"connect\" to.\n if (peer.selfAdvertised) return true;\n return (\n (peer.agentId ?? 'code-agent') === (context.agent?.agentId ?? 'code-agent') &&\n peer.resourceId === context.agent?.resourceId &&\n peer.threadId === context.agent?.threadId\n );\n}\n\nfunction comparePeers(a: { id: string }, b: { id: string }): number {\n return a.id.localeCompare(b.id);\n}\n"],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import type { AgentController, Session } from '@mastra/core/agent-controller';
2
+ /**
3
+ * Advertise a session's threads to peer agents and keep them advertised.
4
+ *
5
+ * Cross-agent communication addresses peers by `(agentId, resourceId, threadId)`,
6
+ * so every thread the user has loaded has to stay claimed for as long as the
7
+ * session lives — a thread that is silently released becomes unreachable, and a
8
+ * peer that saved it can no longer send to it. Claims are therefore keyed per
9
+ * thread and only released when this session is torn down.
10
+ */
11
+ export declare function createSessionThreadAdvertisement<TState>(options: {
12
+ session: Session<TState>;
13
+ controller: AgentController<TState>;
14
+ /** Human-readable peer label for every thread this session advertises. */
15
+ projectName: string;
16
+ }): {
17
+ claim(threadId: string): Promise<void>;
18
+ close(): void;
19
+ };
20
+ //# sourceMappingURL=session-advertisement.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-advertisement.d.ts","sourceRoot":"","sources":["../../src/agent-connections/session-advertisement.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AAI9E;;;;;;;;GAQG;AACH,wBAAgB,gCAAgC,CAAC,MAAM,EAAE,OAAO,EAAE;IAChE,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IACzB,UAAU,EAAE,eAAe,CAAC,MAAM,CAAC,CAAC;IACpC,0EAA0E;IAC1E,WAAW,EAAE,MAAM,CAAC;CACrB,GAAG;IAAE,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAAC,KAAK,IAAI,IAAI,CAAA;CAAE,CA2E5D"}
@@ -0,0 +1,79 @@
1
+ import { createThreadOwnershipManager } from "./ownership.js";
2
+ //#region src/agent-connections/session-advertisement.ts
3
+ /**
4
+ * Advertise a session's threads to peer agents and keep them advertised.
5
+ *
6
+ * Cross-agent communication addresses peers by `(agentId, resourceId, threadId)`,
7
+ * so every thread the user has loaded has to stay claimed for as long as the
8
+ * session lives — a thread that is silently released becomes unreachable, and a
9
+ * peer that saved it can no longer send to it. Claims are therefore keyed per
10
+ * thread and only released when this session is torn down.
11
+ */
12
+ function createSessionThreadAdvertisement(options) {
13
+ const { session, controller, projectName } = options;
14
+ const latestObservedTitles = /* @__PURE__ */ new Map();
15
+ const threadOwnership = createThreadOwnershipManager(async (threadId) => {
16
+ const revisionAtStart = latestObservedTitles.get(threadId)?.revision ?? 0;
17
+ const thread = await session.thread.getById({ threadId });
18
+ const agent = controller.getCurrentAgent(session);
19
+ const claim = await agent.claimThreadOwnership({
20
+ threadId,
21
+ resourceId: session.identity.getResourceId(),
22
+ streamOptions: () => session.machinery.buildStreamOptions({ threadId }),
23
+ peer: {
24
+ label: projectName,
25
+ ...thread?.title ? { title: thread.title } : {}
26
+ }
27
+ });
28
+ const observedTitle = latestObservedTitles.get(threadId);
29
+ if (claim.claimed && observedTitle && observedTitle.revision !== revisionAtStart) agent.updateThreadPeerAdvertisement({
30
+ resourceId: session.identity.getResourceId(),
31
+ threadId,
32
+ peer: { title: observedTitle.title }
33
+ });
34
+ return claim;
35
+ });
36
+ const claimThreadOwnership = async (threadId) => {
37
+ try {
38
+ await threadOwnership.claim(threadId);
39
+ } catch (error) {
40
+ console.error(`Failed to claim cross-agent thread ownership for ${threadId}`, error);
41
+ }
42
+ };
43
+ const unsubscribeSession = session.subscribe((event) => {
44
+ if (event.type === "thread_changed") claimThreadOwnership(event.threadId);
45
+ else if (event.type === "thread_created") claimThreadOwnership(event.thread.id);
46
+ else if (event.type === "thread_deleted") {
47
+ latestObservedTitles.delete(event.threadId);
48
+ threadOwnership.release(event.threadId);
49
+ } else if (event.type === "thread_title_updated" || event.type === "om_thread_title_updated") {
50
+ const title = event.type === "thread_title_updated" ? event.title : event.newTitle;
51
+ const revision = (latestObservedTitles.get(event.threadId)?.revision ?? 0) + 1;
52
+ latestObservedTitles.set(event.threadId, {
53
+ revision,
54
+ title
55
+ });
56
+ controller.getCurrentAgent(session).updateThreadPeerAdvertisement({
57
+ resourceId: session.identity.getResourceId(),
58
+ threadId: event.threadId,
59
+ peer: { title }
60
+ });
61
+ }
62
+ });
63
+ return {
64
+ /**
65
+ * Claim (and start advertising) a thread. Errors are logged, not thrown:
66
+ * the caller often cannot await the result, and a failed claim is retried
67
+ * by the ownership manager.
68
+ */
69
+ claim: claimThreadOwnership,
70
+ close() {
71
+ unsubscribeSession();
72
+ threadOwnership.close();
73
+ }
74
+ };
75
+ }
76
+ //#endregion
77
+ export { createSessionThreadAdvertisement };
78
+
79
+ //# sourceMappingURL=session-advertisement.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-advertisement.js","names":[],"sources":["../../src/agent-connections/session-advertisement.ts"],"sourcesContent":["import type { AgentController, Session } from '@mastra/core/agent-controller';\n\nimport { createThreadOwnershipManager } from './ownership.js';\n\n/**\n * Advertise a session's threads to peer agents and keep them advertised.\n *\n * Cross-agent communication addresses peers by `(agentId, resourceId, threadId)`,\n * so every thread the user has loaded has to stay claimed for as long as the\n * session lives — a thread that is silently released becomes unreachable, and a\n * peer that saved it can no longer send to it. Claims are therefore keyed per\n * thread and only released when this session is torn down.\n */\nexport function createSessionThreadAdvertisement<TState>(options: {\n session: Session<TState>;\n controller: AgentController<TState>;\n /** Human-readable peer label for every thread this session advertises. */\n projectName: string;\n}): { claim(threadId: string): Promise<void>; close(): void } {\n const { session, controller, projectName } = options;\n // Title updates can land while a thread's ownership request is still in\n // flight, when there is no advertisement to update yet. Remembering the\n // latest title (with a revision) lets the claim apply it afterwards instead\n // of losing the rename.\n const latestObservedTitles = new Map<string, { revision: number; title: string | undefined }>();\n\n const threadOwnership = createThreadOwnershipManager(async threadId => {\n const revisionAtStart = latestObservedTitles.get(threadId)?.revision ?? 0;\n const thread = await session.thread.getById({ threadId });\n const agent = controller.getCurrentAgent(session);\n const claim = await agent.claimThreadOwnership({\n threadId,\n resourceId: session.identity.getResourceId(),\n // The claim answers for `threadId`, which is not necessarily the session's\n // current thread once the user has moved on — so the woken run must bind\n // its memory and request context to the claimed thread, not the current one.\n streamOptions: () => session.machinery.buildStreamOptions({ threadId }),\n peer: {\n label: projectName,\n ...(thread?.title ? { title: thread.title } : {}),\n },\n });\n const observedTitle = latestObservedTitles.get(threadId);\n if (claim.claimed && observedTitle && observedTitle.revision !== revisionAtStart) {\n agent.updateThreadPeerAdvertisement({\n resourceId: session.identity.getResourceId(),\n threadId,\n peer: { title: observedTitle.title },\n });\n }\n return claim;\n });\n\n const claimThreadOwnership = async (threadId: string) => {\n try {\n await threadOwnership.claim(threadId);\n } catch (error) {\n console.error(`Failed to claim cross-agent thread ownership for ${threadId}`, error);\n }\n };\n\n const unsubscribeSession = session.subscribe(event => {\n if (event.type === 'thread_changed') void claimThreadOwnership(event.threadId);\n else if (event.type === 'thread_created') void claimThreadOwnership(event.thread.id);\n else if (event.type === 'thread_deleted') {\n // Claims outlive the session's current thread, so a deleted thread has to be\n // released explicitly or it stays advertised and a peer keeps sending to it.\n latestObservedTitles.delete(event.threadId);\n threadOwnership.release(event.threadId);\n } else if (event.type === 'thread_title_updated' || event.type === 'om_thread_title_updated') {\n const title = event.type === 'thread_title_updated' ? event.title : event.newTitle;\n const revision = (latestObservedTitles.get(event.threadId)?.revision ?? 0) + 1;\n latestObservedTitles.set(event.threadId, { revision, title });\n controller.getCurrentAgent(session).updateThreadPeerAdvertisement({\n resourceId: session.identity.getResourceId(),\n threadId: event.threadId,\n peer: { title },\n });\n }\n });\n\n return {\n /**\n * Claim (and start advertising) a thread. Errors are logged, not thrown:\n * the caller often cannot await the result, and a failed claim is retried\n * by the ownership manager.\n */\n claim: claimThreadOwnership,\n close() {\n unsubscribeSession();\n threadOwnership.close();\n },\n };\n}\n"],"mappings":";;;;;;;;;;;AAaA,SAAgB,iCAAyC,SAKK;CAC5D,MAAM,EAAE,SAAS,YAAY,gBAAgB;CAK7C,MAAM,uCAAuB,IAAI,IAA6D;CAE9F,MAAM,kBAAkB,6BAA6B,OAAM,aAAY;EACrE,MAAM,kBAAkB,qBAAqB,IAAI,QAAQ,CAAC,EAAE,YAAY;EACxE,MAAM,SAAS,MAAM,QAAQ,OAAO,QAAQ,EAAE,SAAS,CAAC;EACxD,MAAM,QAAQ,WAAW,gBAAgB,OAAO;EAChD,MAAM,QAAQ,MAAM,MAAM,qBAAqB;GAC7C;GACA,YAAY,QAAQ,SAAS,cAAc;GAI3C,qBAAqB,QAAQ,UAAU,mBAAmB,EAAE,SAAS,CAAC;GACtE,MAAM;IACJ,OAAO;IACP,GAAI,QAAQ,QAAQ,EAAE,OAAO,OAAO,MAAM,IAAI,CAAC;GACjD;EACF,CAAC;EACD,MAAM,gBAAgB,qBAAqB,IAAI,QAAQ;EACvD,IAAI,MAAM,WAAW,iBAAiB,cAAc,aAAa,iBAC/D,MAAM,8BAA8B;GAClC,YAAY,QAAQ,SAAS,cAAc;GAC3C;GACA,MAAM,EAAE,OAAO,cAAc,MAAM;EACrC,CAAC;EAEH,OAAO;CACT,CAAC;CAED,MAAM,uBAAuB,OAAO,aAAqB;EACvD,IAAI;GACF,MAAM,gBAAgB,MAAM,QAAQ;EACtC,SAAS,OAAO;GACd,QAAQ,MAAM,oDAAoD,YAAY,KAAK;EACrF;CACF;CAEA,MAAM,qBAAqB,QAAQ,WAAU,UAAS;EACpD,IAAI,MAAM,SAAS,kBAAkB,qBAA0B,MAAM,QAAQ;OACxE,IAAI,MAAM,SAAS,kBAAkB,qBAA0B,MAAM,OAAO,EAAE;OAC9E,IAAI,MAAM,SAAS,kBAAkB;GAGxC,qBAAqB,OAAO,MAAM,QAAQ;GAC1C,gBAAgB,QAAQ,MAAM,QAAQ;EACxC,OAAO,IAAI,MAAM,SAAS,0BAA0B,MAAM,SAAS,2BAA2B;GAC5F,MAAM,QAAQ,MAAM,SAAS,yBAAyB,MAAM,QAAQ,MAAM;GAC1E,MAAM,YAAY,qBAAqB,IAAI,MAAM,QAAQ,CAAC,EAAE,YAAY,KAAK;GAC7E,qBAAqB,IAAI,MAAM,UAAU;IAAE;IAAU;GAAM,CAAC;GAC5D,WAAW,gBAAgB,OAAO,CAAC,CAAC,8BAA8B;IAChE,YAAY,QAAQ,SAAS,cAAc;IAC3C,UAAU,MAAM;IAChB,MAAM,EAAE,MAAM;GAChB,CAAC;EACH;CACF,CAAC;CAED,OAAO;;;;;;EAML,OAAO;EACP,QAAQ;GACN,mBAAmB;GACnB,gBAAgB,MAAM;EACxB;CACF;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../src/agent-connections/tools.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,uBAAuB,EAAgB,MAAM,eAAe,CAAC;AA8GtE,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,EAAE,uBAAuB,CAAC;IACnC,QAAQ,CAAC,EAAE,MACP;QACE,sBAAsB,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;QAC9D,mBAAmB,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;KAC5D,GACD,SAAS,CAAC;CACf;AAED,wBAAgB,0BAA0B,CAAC,OAAO,GAAE,2BAAgC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAyXnF"}
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../src/agent-connections/tools.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,uBAAuB,EAAgB,MAAM,eAAe,CAAC;AA8GtE,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,EAAE,uBAAuB,CAAC;IACnC,QAAQ,CAAC,EAAE,MACP;QACE,sBAAsB,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;QAC9D,mBAAmB,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;KAC5D,GACD,SAAS,CAAC;CACf;AAED,wBAAgB,0BAA0B,CAAC,OAAO,GAAE,2BAAgC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAwZnF"}
@@ -252,12 +252,12 @@ The peer does not need to be currently advertised. Disconnecting is idempotent a
252
252
  id: "agent_signal_send",
253
253
  description: `Send a prioritized notification signal to a connected peer agent.
254
254
 
255
- The target must already be saved and freshly advertise the same exact thread endpoint at send time. Use expectsReply to declare whether the peer should send one signal back to this thread. Reuse messageId when retrying the same logical send, and set replyTo to the request messageId when replying. Use priority to indicate urgency: low, medium, high, or urgent.`,
255
+ The target must already be saved and freshly advertise the same exact thread endpoint at send time. Use expectsReply to declare whether the peer owes one signal back to this thread; false removes that obligation but does not prevent or forbid a reply. Signals routed to a notification summary cannot establish a reply obligation until the recipient opens the full notification, so use a priority that routes directly when a reply is required. Reuse messageId when retrying the same logical send, and set replyTo to the request messageId when replying. Use priority to indicate urgency: low, medium, high, or urgent.`,
256
256
  inputSchema: z.object({
257
257
  targetId: z.string().min(1).describe("Connected peer id."),
258
258
  summary: z.string().min(1).describe("Short summary to deliver to the peer."),
259
259
  priority: prioritySchema.default("medium"),
260
- expectsReply: z.boolean().describe("Whether the peer is expected to send one signal back to this thread."),
260
+ expectsReply: z.boolean().describe("Whether the peer owes one signal back. False means no obligation, not no permission to reply."),
261
261
  messageId: z.string().min(1).optional().describe("Stable logical message id. Reuse the same id for a sequential retry; receiver-side notification coalescing also uses it."),
262
262
  replyTo: z.string().min(1).optional().describe("Message id of the peer request this signal replies to."),
263
263
  payload: z.unknown().optional().describe("Optional structured payload for the peer.")
@@ -339,7 +339,7 @@ The target must already be saved and freshly advertise the same exact thread end
339
339
  expectsReply,
340
340
  messageId,
341
341
  ...replyTo ? { replyTo } : {},
342
- returnPeerId,
342
+ ...expectsReply ? { returnPeerId } : {},
343
343
  from: {
344
344
  resourceId: currentAgent.resourceId,
345
345
  threadId: currentAgent.threadId
@@ -356,6 +356,7 @@ The target must already be saved and freshly advertise the same exact thread end
356
356
  attributes: {
357
357
  expectsReply,
358
358
  messageId,
359
+ sourcePeerId: returnPeerId,
359
360
  ...replyTo ? { replyTo } : {},
360
361
  ...expectsReply ? { returnPeerId } : {}
361
362
  },
@@ -372,8 +373,25 @@ The target must already be saved and freshly advertise the same exact thread end
372
373
  requireClaimedOwner: true
373
374
  }
374
375
  });
375
- const accepted = notification.accepted ? await notification.accepted : void 0;
376
- if (!accepted) return {
376
+ let accepted = notification.accepted ? await notification.accepted : void 0;
377
+ if (!accepted) if (notification.decision.action === "persist" || notification.decision.action === "defer" || notification.decision.action === "summarize") {
378
+ if (expectsReply) {
379
+ await notification.persisted;
380
+ return {
381
+ content: `Failed to establish a reply obligation: the signal was queued for a notification summary instead of being delivered directly to ${untrustedPeerLabel(target)}. Send a new signal at a priority that routes directly when a reply is required.`,
382
+ target,
383
+ priority,
384
+ expectsReply,
385
+ messageId,
386
+ replyTo,
387
+ returnPeerId,
388
+ routingAction: "persist",
389
+ isError: true
390
+ };
391
+ }
392
+ accepted = { action: "persist" };
393
+ } else if (notification.decision.action === "discard") accepted = { action: "discard" };
394
+ else return {
377
395
  content: `Failed to send agent signal: ${notification.record.lastDeliveryError ?? "delivery was not acknowledged by the target thread owner"}`,
378
396
  target,
379
397
  priority,
@@ -383,15 +401,15 @@ The target must already be saved and freshly advertise the same exact thread end
383
401
  returnPeerId,
384
402
  isError: true
385
403
  };
386
- if (accepted.action === "blocked") return {
387
- content: `Failed to send agent signal: target thread ${untrustedThreadId(target)} is suspended and did not accept the signal. Retry with the same messageId once it resumes.`,
404
+ if (accepted.action === "blocked" || accepted.action === "discard") return {
405
+ content: accepted.action === "blocked" ? `Failed to send agent signal: target thread ${untrustedThreadId(target)} is suspended and did not accept the signal. Retry with the same messageId once it resumes.` : `Failed to send agent signal: delivery policy discarded the signal before it reached ${untrustedPeerLabel(target)}. Retry with the same messageId if the delivery policy changes.`,
388
406
  target,
389
407
  priority,
390
408
  expectsReply,
391
409
  messageId,
392
410
  replyTo,
393
411
  returnPeerId,
394
- routingAction: "blocked",
412
+ routingAction: accepted.action,
395
413
  isError: true
396
414
  };
397
415
  if (accepted.action === "persist") await notification.persisted;