@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.
- package/dist/agent-connections/ownership.d.ts +1 -0
- package/dist/agent-connections/ownership.d.ts.map +1 -1
- package/dist/agent-connections/ownership.js +42 -26
- package/dist/agent-connections/ownership.js.map +1 -1
- package/dist/agent-connections/registry.d.ts.map +1 -1
- package/dist/agent-connections/registry.js +5 -2
- package/dist/agent-connections/registry.js.map +1 -1
- package/dist/agent-connections/session-advertisement.d.ts +20 -0
- package/dist/agent-connections/session-advertisement.d.ts.map +1 -0
- package/dist/agent-connections/session-advertisement.js +79 -0
- package/dist/agent-connections/session-advertisement.js.map +1 -0
- package/dist/agent-connections/tools.d.ts.map +1 -1
- package/dist/agent-connections/tools.js +26 -8
- package/dist/agent-connections/tools.js.map +1 -1
- package/dist/agents/background-completion-events.d.ts +16 -0
- package/dist/agents/background-completion-events.d.ts.map +1 -0
- package/dist/agents/background-completion-events.js +19 -0
- package/dist/agents/background-completion-events.js.map +1 -0
- package/dist/agents/background-completion.d.ts +9 -0
- package/dist/agents/background-completion.d.ts.map +1 -0
- package/dist/agents/background-completion.js +59 -0
- package/dist/agents/background-completion.js.map +1 -0
- package/dist/agents/mastracode-gateway.d.ts +2 -0
- package/dist/agents/mastracode-gateway.d.ts.map +1 -1
- package/dist/agents/mastracode-gateway.js +29 -10
- package/dist/agents/mastracode-gateway.js.map +1 -1
- package/dist/agents/memory.d.ts +1 -1
- package/dist/agents/memory.d.ts.map +1 -1
- package/dist/agents/memory.js +32 -25
- package/dist/agents/memory.js.map +1 -1
- package/dist/agents/model.d.ts +41 -1
- package/dist/agents/model.d.ts.map +1 -1
- package/dist/agents/model.js +141 -7
- package/dist/agents/model.js.map +1 -1
- package/dist/agents/tool-availability.d.ts +2 -0
- package/dist/agents/tool-availability.d.ts.map +1 -1
- package/dist/agents/tool-availability.js +33 -4
- package/dist/agents/tool-availability.js.map +1 -1
- package/dist/agents/tools.d.ts +1 -1
- package/dist/agents/tools.d.ts.map +1 -1
- package/dist/agents/tools.js +30 -2
- package/dist/agents/tools.js.map +1 -1
- package/dist/agents/workspace.d.ts +2 -1
- package/dist/agents/workspace.d.ts.map +1 -1
- package/dist/agents/workspace.js +3 -3
- package/dist/agents/workspace.js.map +1 -1
- package/dist/auth/account-rotation-processor.d.ts +207 -0
- package/dist/auth/account-rotation-processor.d.ts.map +1 -0
- package/dist/auth/account-rotation-processor.js +669 -0
- package/dist/auth/account-rotation-processor.js.map +1 -0
- package/dist/auth/account-routing-context.d.ts +13 -0
- package/dist/auth/account-routing-context.d.ts.map +1 -0
- package/dist/auth/account-routing-context.js +41 -0
- package/dist/auth/account-routing-context.js.map +1 -0
- package/dist/auth/index.d.ts +1 -0
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +2 -1
- package/dist/auth/providers/anthropic.d.ts.map +1 -1
- package/dist/auth/providers/anthropic.js +2 -8
- package/dist/auth/providers/anthropic.js.map +1 -1
- package/dist/auth/providers/github-copilot.d.ts.map +1 -1
- package/dist/auth/providers/github-copilot.js +34 -12
- package/dist/auth/providers/github-copilot.js.map +1 -1
- package/dist/auth/providers/kimi-coding.d.ts.map +1 -1
- package/dist/auth/providers/kimi-coding.js +2 -6
- package/dist/auth/providers/kimi-coding.js.map +1 -1
- package/dist/auth/providers/openai-codex.d.ts +3 -1
- package/dist/auth/providers/openai-codex.d.ts.map +1 -1
- package/dist/auth/providers/openai-codex.js +50 -24
- package/dist/auth/providers/openai-codex.js.map +1 -1
- package/dist/auth/providers/xai.d.ts +1 -1
- package/dist/auth/providers/xai.d.ts.map +1 -1
- package/dist/auth/providers/xai.js +36 -16
- package/dist/auth/providers/xai.js.map +1 -1
- package/dist/auth/storage.d.ts +130 -8
- package/dist/auth/storage.d.ts.map +1 -1
- package/dist/auth/storage.js +602 -39
- package/dist/auth/storage.js.map +1 -1
- package/dist/auth/types.d.ts +69 -2
- package/dist/auth/types.d.ts.map +1 -1
- package/dist/goal-signal.d.ts +1 -1
- package/dist/goal-signal.d.ts.map +1 -1
- package/dist/goal-signal.js +1 -1
- package/dist/goal-signal.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +127 -54
- package/dist/index.js.map +1 -1
- package/dist/mcp/config.d.ts +17 -2
- package/dist/mcp/config.d.ts.map +1 -1
- package/dist/mcp/config.js +18 -3
- package/dist/mcp/config.js.map +1 -1
- package/dist/mcp/manager.d.ts +2 -1
- package/dist/mcp/manager.d.ts.map +1 -1
- package/dist/mcp/manager.js +13 -8
- package/dist/mcp/manager.js.map +1 -1
- package/dist/mcp/types.d.ts +15 -3
- package/dist/mcp/types.d.ts.map +1 -1
- package/dist/onboarding/custom-packs.d.ts.map +1 -1
- package/dist/onboarding/custom-packs.js +3 -0
- package/dist/onboarding/custom-packs.js.map +1 -1
- package/dist/onboarding/index.js +1 -1
- package/dist/onboarding/packs.d.ts +44 -0
- package/dist/onboarding/packs.d.ts.map +1 -1
- package/dist/onboarding/packs.js +80 -2
- package/dist/onboarding/packs.js.map +1 -1
- package/dist/onboarding/settings.d.ts +33 -0
- package/dist/onboarding/settings.d.ts.map +1 -1
- package/dist/onboarding/settings.js +88 -8
- package/dist/onboarding/settings.js.map +1 -1
- package/dist/plugin.d.ts +12 -0
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js.map +1 -1
- package/dist/plugins/loader.d.ts.map +1 -1
- package/dist/plugins/loader.js +2 -1
- package/dist/plugins/loader.js.map +1 -1
- package/dist/providers/claude-max.d.ts.map +1 -1
- package/dist/providers/claude-max.js +4 -5
- package/dist/providers/claude-max.js.map +1 -1
- package/dist/providers/github-copilot.d.ts +1 -1
- package/dist/providers/github-copilot.d.ts.map +1 -1
- package/dist/providers/github-copilot.js +89 -37
- package/dist/providers/github-copilot.js.map +1 -1
- package/dist/providers/kimi-coding.d.ts.map +1 -1
- package/dist/providers/kimi-coding.js +16 -2
- package/dist/providers/kimi-coding.js.map +1 -1
- package/dist/providers/openai-codex.d.ts.map +1 -1
- package/dist/providers/openai-codex.js +42 -27
- package/dist/providers/openai-codex.js.map +1 -1
- package/dist/schema.d.ts +28 -1
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +9 -0
- package/dist/schema.js.map +1 -1
- package/dist/tools/workflows/save-workflow.d.ts +1 -1
- package/dist/tools/workflows/save-workflow.d.ts.map +1 -1
- package/dist/workflows/register-primitives.js +2 -2
- package/dist/workflows/register-primitives.js.map +1 -1
- 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;
|
|
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
|
-
|
|
6
|
-
let
|
|
7
|
-
let
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
|
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;
|
|
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
|
-
...
|
|
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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
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
|
|
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
|
|
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
|
-
|
|
376
|
-
if (!accepted)
|
|
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:
|
|
412
|
+
routingAction: accepted.action,
|
|
395
413
|
isError: true
|
|
396
414
|
};
|
|
397
415
|
if (accepted.action === "persist") await notification.persisted;
|