@mastra/code-sdk 1.8.3-alpha.1 → 1.8.4-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,7 +2,21 @@ export interface ThreadOwnershipClaim {
2
2
  claimed: boolean;
3
3
  unsubscribe(): void;
4
4
  }
5
- export declare function createThreadOwnershipManager(claimThread: (threadId: string) => Promise<ThreadOwnershipClaim>): {
5
+ export interface ThreadClaimContext {
6
+ /**
7
+ * Call from the core `yieldOwnership` callback when this claim is released to
8
+ * another process, so the manager stops tracking it instead of believing it
9
+ * still owns the thread.
10
+ */
11
+ onYield(): void;
12
+ /** Call when lease renewal proves another live owner took the claim, so the manager retries it. */
13
+ onLost(): void;
14
+ }
15
+ /**
16
+ * Keeps one renewable ownership claim per session thread, retries contention,
17
+ * and discards superseded attempts without creating a release-before-acquire gap.
18
+ */
19
+ export declare function createThreadOwnershipManager(claimThread: (threadId: string, context: ThreadClaimContext) => Promise<ThreadOwnershipClaim>): {
6
20
  claim(threadId?: string | null): Promise<boolean>;
7
21
  release(threadId: string): void;
8
22
  close(): void;
@@ -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;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"}
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;AAED,MAAM,WAAW,kBAAkB;IACjC;;;;OAIG;IACH,OAAO,IAAI,IAAI,CAAC;IAChB,mGAAmG;IACnG,MAAM,IAAI,IAAI,CAAC;CAChB;AAmBD;;;GAGG;AACH,wBAAgB,4BAA4B,CAC1C,WAAW,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,kBAAkB,KAAK,OAAO,CAAC,oBAAoB,CAAC,GAC5F;IACD,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,CAgHA"}
@@ -1,6 +1,10 @@
1
1
  //#region src/agent-connections/ownership.ts
2
2
  const OWNERSHIP_RETRY_INITIAL_DELAY_MS = 250;
3
3
  const OWNERSHIP_RETRY_MAX_DELAY_MS = 5e3;
4
+ /**
5
+ * Keeps one renewable ownership claim per session thread, retries contention,
6
+ * and discards superseded attempts without creating a release-before-acquire gap.
7
+ */
4
8
  function createThreadOwnershipManager(claimThread) {
5
9
  const states = /* @__PURE__ */ new Map();
6
10
  let nextGeneration = 0;
@@ -20,9 +24,24 @@ function createThreadOwnershipManager(claimThread) {
20
24
  }, delayMs);
21
25
  state.retryTimer.unref?.();
22
26
  };
27
+ const yieldClaim = (threadId, claimGeneration) => {
28
+ const state = states.get(threadId);
29
+ if (!state || state.generation !== claimGeneration) return;
30
+ clearRetry(state);
31
+ states.delete(threadId);
32
+ };
33
+ const loseClaim = (threadId, claimGeneration) => {
34
+ const state = states.get(threadId);
35
+ if (!state || state.generation !== claimGeneration) return;
36
+ state.claim = void 0;
37
+ scheduleRetry(threadId, claimGeneration);
38
+ };
23
39
  const attemptClaim = async (threadId, claimGeneration, propagateError) => {
24
40
  try {
25
- const nextClaim = await claimThread(threadId);
41
+ const nextClaim = await claimThread(threadId, {
42
+ onYield: () => yieldClaim(threadId, claimGeneration),
43
+ onLost: () => loseClaim(threadId, claimGeneration)
44
+ });
26
45
  const state = states.get(threadId);
27
46
  if (closed || !state || state.generation !== claimGeneration) {
28
47
  nextClaim.unsubscribe();
@@ -32,8 +51,10 @@ function createThreadOwnershipManager(claimThread) {
32
51
  scheduleRetry(threadId, claimGeneration);
33
52
  return false;
34
53
  }
54
+ const previousClaim = state.claim;
35
55
  state.retryDelayMs = OWNERSHIP_RETRY_INITIAL_DELAY_MS;
36
56
  state.claim = nextClaim;
57
+ previousClaim?.unsubscribe();
37
58
  return true;
38
59
  } catch (error) {
39
60
  scheduleRetry(threadId, claimGeneration);
@@ -46,13 +67,11 @@ function createThreadOwnershipManager(claimThread) {
46
67
  if (closed) return false;
47
68
  if (!threadId) return false;
48
69
  const existing = states.get(threadId);
49
- if (existing) {
50
- clearRetry(existing);
51
- existing.claim?.unsubscribe();
52
- }
70
+ if (existing) clearRetry(existing);
53
71
  const state = {
54
72
  generation: ++nextGeneration,
55
- retryDelayMs: OWNERSHIP_RETRY_INITIAL_DELAY_MS
73
+ retryDelayMs: OWNERSHIP_RETRY_INITIAL_DELAY_MS,
74
+ claim: existing?.claim
56
75
  };
57
76
  states.set(threadId, state);
58
77
  return attemptClaim(threadId, state.generation, true);
@@ -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\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
+ {"version":3,"file":"ownership.js","names":[],"sources":["../../src/agent-connections/ownership.ts"],"sourcesContent":["export interface ThreadOwnershipClaim {\n claimed: boolean;\n unsubscribe(): void;\n}\n\nexport interface ThreadClaimContext {\n /**\n * Call from the core `yieldOwnership` callback when this claim is released to\n * another process, so the manager stops tracking it instead of believing it\n * still owns the thread.\n */\n onYield(): void;\n /** Call when lease renewal proves another live owner took the claim, so the manager retries it. */\n onLost(): 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, or until another\n * process asks for the thread and this session yields it.\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\n/**\n * Keeps one renewable ownership claim per session thread, retries contention,\n * and discards superseded attempts without creating a release-before-acquire gap.\n */\nexport function createThreadOwnershipManager(\n claimThread: (threadId: string, context: ThreadClaimContext) => Promise<ThreadOwnershipClaim>,\n): {\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 yieldClaim = (threadId: string, claimGeneration: number) => {\n const state = states.get(threadId);\n if (!state || state.generation !== claimGeneration) return;\n // Core completed the transfer or release before notifying us. Forget the\n // thread entirely: the user is not on it, and switching back re-claims it.\n clearRetry(state);\n states.delete(threadId);\n };\n\n const loseClaim = (threadId: string, claimGeneration: number) => {\n const state = states.get(threadId);\n if (!state || state.generation !== claimGeneration) return;\n // Core already stopped the stale claim. Keep the thread tracked and retry:\n // another process may move away or exit while this session is still alive.\n state.claim = undefined;\n scheduleRetry(threadId, claimGeneration);\n };\n\n const attemptClaim = async (threadId: string, claimGeneration: number, propagateError: boolean): Promise<boolean> => {\n try {\n const nextClaim = await claimThread(threadId, {\n onYield: () => yieldClaim(threadId, claimGeneration),\n onLost: () => loseClaim(threadId, claimGeneration),\n });\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 const previousClaim = state.claim;\n state.retryDelayMs = OWNERSHIP_RETRY_INITIAL_DELAY_MS;\n state.claim = nextClaim;\n previousClaim?.unsubscribe();\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. Keep the live claim until its replacement\n // is ready so lease-backed ownership never has a release-then-acquire gap.\n const existing = states.get(threadId);\n if (existing) clearRetry(existing);\n const state: ThreadClaimState = {\n generation: ++nextGeneration,\n retryDelayMs: OWNERSHIP_RETRY_INITIAL_DELAY_MS,\n claim: existing?.claim,\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":";AAgBA,MAAM,mCAAmC;AACzC,MAAM,+BAA+B;;;;;AAoBrC,SAAgB,6BACd,aAKA;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,cAAc,UAAkB,oBAA4B;EAChE,MAAM,QAAQ,OAAO,IAAI,QAAQ;EACjC,IAAI,CAAC,SAAS,MAAM,eAAe,iBAAiB;EAGpD,WAAW,KAAK;EAChB,OAAO,OAAO,QAAQ;CACxB;CAEA,MAAM,aAAa,UAAkB,oBAA4B;EAC/D,MAAM,QAAQ,OAAO,IAAI,QAAQ;EACjC,IAAI,CAAC,SAAS,MAAM,eAAe,iBAAiB;EAGpD,MAAM,QAAQ,KAAA;EACd,cAAc,UAAU,eAAe;CACzC;CAEA,MAAM,eAAe,OAAO,UAAkB,iBAAyB,mBAA8C;EACnH,IAAI;GACF,MAAM,YAAY,MAAM,YAAY,UAAU;IAC5C,eAAe,WAAW,UAAU,eAAe;IACnD,cAAc,UAAU,UAAU,eAAe;GACnD,CAAC;GACD,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,gBAAgB,MAAM;GAC5B,MAAM,eAAe;GACrB,MAAM,QAAQ;GACd,eAAe,YAAY;GAC3B,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;GAMtB,MAAM,WAAW,OAAO,IAAI,QAAQ;GACpC,IAAI,UAAU,WAAW,QAAQ;GACjC,MAAM,QAA0B;IAC9B,YAAY,EAAE;IACd,cAAc;IACd,OAAO,UAAU;GACnB;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"}
@@ -6,7 +6,14 @@ import type { AgentController, Session } from '@mastra/core/agent-controller';
6
6
  * so every thread the user has loaded has to stay claimed for as long as the
7
7
  * session lives — a thread that is silently released becomes unreachable, and a
8
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.
9
+ * thread and remain live until the thread is deleted, the session is torn down,
10
+ * or ownership is handed to another active session.
11
+ *
12
+ * When another process asks to claim a thread that is *not* this session's
13
+ * current thread, the lease is transferred to that requester atomically. The
14
+ * user is in that other process now, so peers should reach it there. A thread
15
+ * the user is actually looking at is never yielded; the requester keeps
16
+ * retrying until the current session moves away.
10
17
  */
11
18
  export declare function createSessionThreadAdvertisement<TState>(options: {
12
19
  session: Session<TState>;
@@ -1 +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"}
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;;;;;;;;;;;;;;;GAeG;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,CAgF5D"}
@@ -7,12 +7,19 @@ import { createThreadOwnershipManager } from "./ownership.js";
7
7
  * so every thread the user has loaded has to stay claimed for as long as the
8
8
  * session lives — a thread that is silently released becomes unreachable, and a
9
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.
10
+ * thread and remain live until the thread is deleted, the session is torn down,
11
+ * or ownership is handed to another active session.
12
+ *
13
+ * When another process asks to claim a thread that is *not* this session's
14
+ * current thread, the lease is transferred to that requester atomically. The
15
+ * user is in that other process now, so peers should reach it there. A thread
16
+ * the user is actually looking at is never yielded; the requester keeps
17
+ * retrying until the current session moves away.
11
18
  */
12
19
  function createSessionThreadAdvertisement(options) {
13
20
  const { session, controller, projectName } = options;
14
21
  const latestObservedTitles = /* @__PURE__ */ new Map();
15
- const threadOwnership = createThreadOwnershipManager(async (threadId) => {
22
+ const threadOwnership = createThreadOwnershipManager(async (threadId, { onYield, onLost }) => {
16
23
  const revisionAtStart = latestObservedTitles.get(threadId)?.revision ?? 0;
17
24
  const thread = await session.thread.getById({ threadId });
18
25
  const agent = controller.getCurrentAgent(session);
@@ -23,7 +30,10 @@ function createSessionThreadAdvertisement(options) {
23
30
  peer: {
24
31
  label: projectName,
25
32
  ...thread?.title ? { title: thread.title } : {}
26
- }
33
+ },
34
+ yieldOwnership: () => session.thread.getId() !== threadId,
35
+ onOwnershipYielded: onYield,
36
+ onOwnershipLost: onLost
27
37
  });
28
38
  const observedTitle = latestObservedTitles.get(threadId);
29
39
  if (claim.claimed && observedTitle && observedTitle.revision !== revisionAtStart) agent.updateThreadPeerAdvertisement({
@@ -1 +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
+ {"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 remain live until the thread is deleted, the session is torn down,\n * or ownership is handed to another active session.\n *\n * When another process asks to claim a thread that is *not* this session's\n * current thread, the lease is transferred to that requester atomically. The\n * user is in that other process now, so peers should reach it there. A thread\n * the user is actually looking at is never yielded; the requester keeps\n * retrying until the current session moves away.\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, { onYield, onLost }) => {\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 // Read the current thread live rather than tracking events: whichever\n // path moved the session (including silent switches) is reflected here.\n yieldOwnership: () => session.thread.getId() !== threadId,\n onOwnershipYielded: onYield,\n onOwnershipLost: onLost,\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":";;;;;;;;;;;;;;;;;;AAoBA,SAAgB,iCAAyC,SAKK;CAC5D,MAAM,EAAE,SAAS,YAAY,gBAAgB;CAK7C,MAAM,uCAAuB,IAAI,IAA6D;CAE9F,MAAM,kBAAkB,6BAA6B,OAAO,UAAU,EAAE,SAAS,aAAa;EAC5F,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;GAGA,sBAAsB,QAAQ,OAAO,MAAM,MAAM;GACjD,oBAAoB;GACpB,iBAAiB;EACnB,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,12 +1,13 @@
1
1
  import { PubSub, UnixSocketPubSub } from '@mastra/core/events';
2
- import type { PubSubDeliveryMode, Event, EventCallback, SubscribeOptions } from '@mastra/core/events';
2
+ import type { Event, EventCallback, LeaseProvider, PubSubDeliveryMode, SubscribeOptions } from '@mastra/core/events';
3
3
  /**
4
4
  * A PubSub that manages one Unix socket per topic for cross-process signal
5
5
  * coordination within a mastracode resource.
6
6
  *
7
7
  * Socket paths use `/tmp/mc/<resourceId>/<sanitized-topic>.sock` for
8
8
  * inspectability and automatic OS cleanup. Each topic gets its own isolated
9
- * socket so broker election and message routing are per-topic.
9
+ * socket so broker election and message routing are per-topic. A dedicated
10
+ * resource-wide socket exposes filesystem leases shared by every topic.
10
11
  *
11
12
  * Stale sockets from crashed processes are handled by
12
13
  * {@link UnixSocketPubSub}'s built-in election logic: it detects
@@ -18,6 +19,8 @@ declare class SignalsPubSub extends PubSub {
18
19
  #private;
19
20
  constructor(resourceId: string);
20
21
  get supportedModes(): ReadonlyArray<PubSubDeliveryMode>;
22
+ /** Exposes the resource-wide filesystem lease provider shared by every topic socket. */
23
+ getLeaseProvider(): LeaseProvider;
21
24
  publish(topic: string, event: Omit<Event, 'id' | 'createdAt'>, options?: {
22
25
  localOnly?: boolean;
23
26
  }): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"signals-pubsub.d.ts","sourceRoot":"","sources":["../../src/utils/signals-pubsub.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,KAAK,EAAE,kBAAkB,EAAE,KAAK,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAItG;;;;;;;;;;;;;GAaG;AACH,cAAM,aAAc,SAAQ,MAAM;;gBAMpB,UAAU,EAAE,MAAM;IAK9B,IAAa,cAAc,IAAI,aAAa,CAAC,kBAAkB,CAAC,CAE/D;IAEK,OAAO,CACX,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,GAAG,WAAW,CAAC,EACtC,OAAO,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,OAAO,CAAA;KAAE,GAChC,OAAO,CAAC,IAAI,CAAC;IAKV,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,aAAa,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAKtF,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAM5D,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAItB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAM5B,sEAAsE;IACtE,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS;CAmEvD;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM,GAAG,aAAa,CAErE"}
1
+ {"version":3,"file":"signals-pubsub.d.ts","sourceRoot":"","sources":["../../src/utils/signals-pubsub.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,KAAK,EAAE,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAIrH;;;;;;;;;;;;;;GAcG;AACH,cAAM,aAAc,SAAQ,MAAM;;gBAOpB,UAAU,EAAE,MAAM;IAM9B,IAAa,cAAc,IAAI,aAAa,CAAC,kBAAkB,CAAC,CAE/D;IAED,wFAAwF;IACxF,gBAAgB,IAAI,aAAa;IAI3B,OAAO,CACX,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,GAAG,WAAW,CAAC,EACtC,OAAO,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,OAAO,CAAA;KAAE,GAChC,OAAO,CAAC,IAAI,CAAC;IAKV,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,aAAa,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAKtF,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAM5D,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAItB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAM5B,sEAAsE;IACtE,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS;CAmEvD;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM,GAAG,aAAa,CAErE"}
@@ -10,7 +10,8 @@ const THREAD_STREAM_PREFIX = "agent.thread-stream.";
10
10
  *
11
11
  * Socket paths use `/tmp/mc/<resourceId>/<sanitized-topic>.sock` for
12
12
  * inspectability and automatic OS cleanup. Each topic gets its own isolated
13
- * socket so broker election and message routing are per-topic.
13
+ * socket so broker election and message routing are per-topic. A dedicated
14
+ * resource-wide socket exposes filesystem leases shared by every topic.
14
15
  *
15
16
  * Stale sockets from crashed processes are handled by
16
17
  * {@link UnixSocketPubSub}'s built-in election logic: it detects
@@ -20,16 +21,22 @@ const THREAD_STREAM_PREFIX = "agent.thread-stream.";
20
21
  */
21
22
  var SignalsPubSub = class extends PubSub {
22
23
  #resourceId;
24
+ #leaseProvider;
23
25
  #sockets = /* @__PURE__ */ new Map();
24
26
  #pending = /* @__PURE__ */ new Map();
25
27
  #closed = false;
26
28
  constructor(resourceId) {
27
29
  super();
28
30
  this.#resourceId = resourceId;
31
+ this.#leaseProvider = new UnixSocketPubSub(join("/tmp/mc", resourceId, ".leases.sock"));
29
32
  }
30
33
  get supportedModes() {
31
34
  return ["push"];
32
35
  }
36
+ /** Exposes the resource-wide filesystem lease provider shared by every topic socket. */
37
+ getLeaseProvider() {
38
+ return this.#leaseProvider;
39
+ }
33
40
  async publish(topic, event, options) {
34
41
  await (await this.#getOrCreate(topic)).publish(topic, event, options);
35
42
  }
@@ -46,7 +53,7 @@ var SignalsPubSub = class extends PubSub {
46
53
  }
47
54
  async close() {
48
55
  this.#closed = true;
49
- await Promise.allSettled([...this.#sockets.values()].map((s) => s.close()));
56
+ await Promise.allSettled([this.#leaseProvider.close(), ...[...this.#sockets.values()].map((s) => s.close())]);
50
57
  this.#sockets.clear();
51
58
  }
52
59
  /** Get the underlying socket for a topic (for testing/inspection). */
@@ -1 +1 @@
1
- {"version":3,"file":"signals-pubsub.js","names":["#resourceId","#sockets","#pending","#getOrCreate","#topicKey","#closed","#initSocket","#socketPath"],"sources":["../../src/utils/signals-pubsub.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport { mkdir } from 'node:fs/promises';\nimport { join } from 'node:path';\n\nimport { PubSub, UnixSocketPubSub } from '@mastra/core/events';\nimport type { PubSubDeliveryMode, Event, EventCallback, SubscribeOptions } from '@mastra/core/events';\n\nconst THREAD_STREAM_PREFIX = 'agent.thread-stream.';\n\n/**\n * A PubSub that manages one Unix socket per topic for cross-process signal\n * coordination within a mastracode resource.\n *\n * Socket paths use `/tmp/mc/<resourceId>/<sanitized-topic>.sock` for\n * inspectability and automatic OS cleanup. Each topic gets its own isolated\n * socket so broker election and message routing are per-topic.\n *\n * Stale sockets from crashed processes are handled by\n * {@link UnixSocketPubSub}'s built-in election logic: it detects\n * ECONNREFUSED on a dead broker socket, unlinks it, and re-elects.\n * No blanket cleanup is needed here — that would break concurrent\n * mc instances sharing the same resourceId.\n */\nclass SignalsPubSub extends PubSub {\n readonly #resourceId: string;\n readonly #sockets = new Map<string, UnixSocketPubSub>();\n readonly #pending = new Map<string, Promise<UnixSocketPubSub>>();\n #closed = false;\n\n constructor(resourceId: string) {\n super();\n this.#resourceId = resourceId;\n }\n\n override get supportedModes(): ReadonlyArray<PubSubDeliveryMode> {\n return ['push'];\n }\n\n async publish(\n topic: string,\n event: Omit<Event, 'id' | 'createdAt'>,\n options?: { localOnly?: boolean },\n ): Promise<void> {\n const socket = await this.#getOrCreate(topic);\n await socket.publish(topic, event, options);\n }\n\n async subscribe(topic: string, cb: EventCallback, options?: SubscribeOptions): Promise<void> {\n const socket = await this.#getOrCreate(topic);\n await socket.subscribe(topic, cb, options);\n }\n\n async unsubscribe(topic: string, cb: EventCallback): Promise<void> {\n const socket = this.#sockets.get(this.#topicKey(topic));\n if (!socket) return;\n await socket.unsubscribe(topic, cb);\n }\n\n async flush(): Promise<void> {\n await Promise.all([...this.#sockets.values()].map(s => s.flush()));\n }\n\n async close(): Promise<void> {\n this.#closed = true;\n await Promise.allSettled([...this.#sockets.values()].map(s => s.close()));\n this.#sockets.clear();\n }\n\n /** Get the underlying socket for a topic (for testing/inspection). */\n getSocket(topic: string): UnixSocketPubSub | undefined {\n return this.#sockets.get(this.#topicKey(topic));\n }\n\n async #getOrCreate(topic: string): Promise<UnixSocketPubSub> {\n if (this.#closed) throw new Error('SignalsPubSub is closed');\n const key = this.#topicKey(topic);\n const existing = this.#sockets.get(key);\n if (existing) return existing;\n // Deduplicate concurrent callers so only one socket is created per topic.\n let inflight = this.#pending.get(key);\n if (!inflight) {\n inflight = this.#initSocket(topic, key);\n this.#pending.set(key, inflight);\n }\n const socket = await inflight;\n if (this.#closed) throw new Error('SignalsPubSub is closed');\n return socket;\n }\n\n async #initSocket(topic: string, key: string): Promise<UnixSocketPubSub> {\n try {\n const socketPath = await this.#socketPath(topic);\n if (this.#closed) throw new Error('SignalsPubSub is closed');\n const socket = new UnixSocketPubSub(socketPath);\n this.#sockets.set(key, socket);\n return socket;\n } finally {\n this.#pending.delete(key);\n }\n }\n\n async #socketPath(topic: string): Promise<string> {\n let key = this.#topicKey(topic);\n const dir = join('/tmp/mc', this.#resourceId);\n await mkdir(dir, { recursive: true });\n const candidate = join(dir, `${key}.sock`);\n // macOS sun_path limit is 104 bytes; Linux is 108. Use 104 as the\n // conservative bound. When the path is too long, replace the key with\n // a short hash so the socket can still be created.\n if (Buffer.byteLength(candidate) > 104) {\n key = createHash('sha256').update(key).digest('hex').slice(0, 16);\n return join(dir, `${key}.sock`);\n }\n return candidate;\n }\n\n /**\n * Derive a filesystem-safe key for the topic. Thread-stream topics embed\n * a threadId; all other topics use a sanitized version of the topic name.\n */\n #topicKey(topic: string): string {\n if (topic.startsWith(THREAD_STREAM_PREFIX)) {\n const encoded = topic.slice(THREAD_STREAM_PREFIX.length);\n try {\n const decoded = decodeURIComponent(encoded);\n const separatorIdx = decoded.indexOf('\\0');\n if (separatorIdx !== -1) {\n return decoded.slice(separatorIdx + 1);\n }\n } catch {\n // Malformed URI — fall through to sanitized fallback.\n }\n }\n // Fallback: use the topic directly (sanitized for filesystem)\n return topic.replace(/[^a-zA-Z0-9_-]/g, '_');\n }\n}\n\n/**\n * Creates a per-topic PubSub backed by Unix sockets for cross-process signal\n * and workflow event coordination within a mastracode resource.\n *\n * Each topic gets its own Unix socket under `/tmp/mc/<resourceId>/`.\n * Stale sockets from crashed processes are handled by the underlying\n * {@link UnixSocketPubSub}'s broker election logic.\n */\nexport function createSignalsPubSub(resourceId: string): SignalsPubSub {\n return new SignalsPubSub(resourceId);\n}\n"],"mappings":";;;;;AAOA,MAAM,uBAAuB;;;;;;;;;;;;;;;AAgB7B,IAAM,gBAAN,cAA4B,OAAO;CACjC;CACA,2BAAoB,IAAI,IAA8B;CACtD,2BAAoB,IAAI,IAAuC;CAC/D,UAAU;CAEV,YAAY,YAAoB;EAC9B,MAAM;EACN,KAAKA,cAAc;CACrB;CAEA,IAAa,iBAAoD;EAC/D,OAAO,CAAC,MAAM;CAChB;CAEA,MAAM,QACJ,OACA,OACA,SACe;EAEf,OAAM,MADe,KAAKG,aAAa,KAAK,EAAA,CAC/B,QAAQ,OAAO,OAAO,OAAO;CAC5C;CAEA,MAAM,UAAU,OAAe,IAAmB,SAA2C;EAE3F,OAAM,MADe,KAAKA,aAAa,KAAK,EAAA,CAC/B,UAAU,OAAO,IAAI,OAAO;CAC3C;CAEA,MAAM,YAAY,OAAe,IAAkC;EACjE,MAAM,SAAS,KAAKF,SAAS,IAAI,KAAKG,UAAU,KAAK,CAAC;EACtD,IAAI,CAAC,QAAQ;EACb,MAAM,OAAO,YAAY,OAAO,EAAE;CACpC;CAEA,MAAM,QAAuB;EAC3B,MAAM,QAAQ,IAAI,CAAC,GAAG,KAAKH,SAAS,OAAO,CAAC,CAAC,CAAC,KAAI,MAAK,EAAE,MAAM,CAAC,CAAC;CACnE;CAEA,MAAM,QAAuB;EAC3B,KAAKI,UAAU;EACf,MAAM,QAAQ,WAAW,CAAC,GAAG,KAAKJ,SAAS,OAAO,CAAC,CAAC,CAAC,KAAI,MAAK,EAAE,MAAM,CAAC,CAAC;EACxE,KAAKA,SAAS,MAAM;CACtB;;CAGA,UAAU,OAA6C;EACrD,OAAO,KAAKA,SAAS,IAAI,KAAKG,UAAU,KAAK,CAAC;CAChD;CAEA,MAAMD,aAAa,OAA0C;EAC3D,IAAI,KAAKE,SAAS,MAAM,IAAI,MAAM,yBAAyB;EAC3D,MAAM,MAAM,KAAKD,UAAU,KAAK;EAChC,MAAM,WAAW,KAAKH,SAAS,IAAI,GAAG;EACtC,IAAI,UAAU,OAAO;EAErB,IAAI,WAAW,KAAKC,SAAS,IAAI,GAAG;EACpC,IAAI,CAAC,UAAU;GACb,WAAW,KAAKI,YAAY,OAAO,GAAG;GACtC,KAAKJ,SAAS,IAAI,KAAK,QAAQ;EACjC;EACA,MAAM,SAAS,MAAM;EACrB,IAAI,KAAKG,SAAS,MAAM,IAAI,MAAM,yBAAyB;EAC3D,OAAO;CACT;CAEA,MAAMC,YAAY,OAAe,KAAwC;EACvE,IAAI;GACF,MAAM,aAAa,MAAM,KAAKC,YAAY,KAAK;GAC/C,IAAI,KAAKF,SAAS,MAAM,IAAI,MAAM,yBAAyB;GAC3D,MAAM,SAAS,IAAI,iBAAiB,UAAU;GAC9C,KAAKJ,SAAS,IAAI,KAAK,MAAM;GAC7B,OAAO;EACT,UAAU;GACR,KAAKC,SAAS,OAAO,GAAG;EAC1B;CACF;CAEA,MAAMK,YAAY,OAAgC;EAChD,IAAI,MAAM,KAAKH,UAAU,KAAK;EAC9B,MAAM,MAAM,KAAK,WAAW,KAAKJ,WAAW;EAC5C,MAAM,MAAM,KAAK,EAAE,WAAW,KAAK,CAAC;EACpC,MAAM,YAAY,KAAK,KAAK,GAAG,IAAI,MAAM;EAIzC,IAAI,OAAO,WAAW,SAAS,IAAI,KAAK;GACtC,MAAM,WAAW,QAAQ,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,MAAM,GAAG,EAAE;GAChE,OAAO,KAAK,KAAK,GAAG,IAAI,MAAM;EAChC;EACA,OAAO;CACT;;;;;CAMA,UAAU,OAAuB;EAC/B,IAAI,MAAM,WAAW,oBAAoB,GAAG;GAC1C,MAAM,UAAU,MAAM,MAAM,EAA2B;GACvD,IAAI;IACF,MAAM,UAAU,mBAAmB,OAAO;IAC1C,MAAM,eAAe,QAAQ,QAAQ,IAAI;IACzC,IAAI,iBAAiB,IACnB,OAAO,QAAQ,MAAM,eAAe,CAAC;GAEzC,QAAQ,CAER;EACF;EAEA,OAAO,MAAM,QAAQ,mBAAmB,GAAG;CAC7C;AACF;;;;;;;;;AAUA,SAAgB,oBAAoB,YAAmC;CACrE,OAAO,IAAI,cAAc,UAAU;AACrC"}
1
+ {"version":3,"file":"signals-pubsub.js","names":["#resourceId","#leaseProvider","#sockets","#pending","#getOrCreate","#topicKey","#closed","#initSocket","#socketPath"],"sources":["../../src/utils/signals-pubsub.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport { mkdir } from 'node:fs/promises';\nimport { join } from 'node:path';\n\nimport { PubSub, UnixSocketPubSub } from '@mastra/core/events';\nimport type { Event, EventCallback, LeaseProvider, PubSubDeliveryMode, SubscribeOptions } from '@mastra/core/events';\n\nconst THREAD_STREAM_PREFIX = 'agent.thread-stream.';\n\n/**\n * A PubSub that manages one Unix socket per topic for cross-process signal\n * coordination within a mastracode resource.\n *\n * Socket paths use `/tmp/mc/<resourceId>/<sanitized-topic>.sock` for\n * inspectability and automatic OS cleanup. Each topic gets its own isolated\n * socket so broker election and message routing are per-topic. A dedicated\n * resource-wide socket exposes filesystem leases shared by every topic.\n *\n * Stale sockets from crashed processes are handled by\n * {@link UnixSocketPubSub}'s built-in election logic: it detects\n * ECONNREFUSED on a dead broker socket, unlinks it, and re-elects.\n * No blanket cleanup is needed here — that would break concurrent\n * mc instances sharing the same resourceId.\n */\nclass SignalsPubSub extends PubSub {\n readonly #resourceId: string;\n readonly #leaseProvider: UnixSocketPubSub;\n readonly #sockets = new Map<string, UnixSocketPubSub>();\n readonly #pending = new Map<string, Promise<UnixSocketPubSub>>();\n #closed = false;\n\n constructor(resourceId: string) {\n super();\n this.#resourceId = resourceId;\n this.#leaseProvider = new UnixSocketPubSub(join('/tmp/mc', resourceId, '.leases.sock'));\n }\n\n override get supportedModes(): ReadonlyArray<PubSubDeliveryMode> {\n return ['push'];\n }\n\n /** Exposes the resource-wide filesystem lease provider shared by every topic socket. */\n getLeaseProvider(): LeaseProvider {\n return this.#leaseProvider;\n }\n\n async publish(\n topic: string,\n event: Omit<Event, 'id' | 'createdAt'>,\n options?: { localOnly?: boolean },\n ): Promise<void> {\n const socket = await this.#getOrCreate(topic);\n await socket.publish(topic, event, options);\n }\n\n async subscribe(topic: string, cb: EventCallback, options?: SubscribeOptions): Promise<void> {\n const socket = await this.#getOrCreate(topic);\n await socket.subscribe(topic, cb, options);\n }\n\n async unsubscribe(topic: string, cb: EventCallback): Promise<void> {\n const socket = this.#sockets.get(this.#topicKey(topic));\n if (!socket) return;\n await socket.unsubscribe(topic, cb);\n }\n\n async flush(): Promise<void> {\n await Promise.all([...this.#sockets.values()].map(s => s.flush()));\n }\n\n async close(): Promise<void> {\n this.#closed = true;\n await Promise.allSettled([this.#leaseProvider.close(), ...[...this.#sockets.values()].map(s => s.close())]);\n this.#sockets.clear();\n }\n\n /** Get the underlying socket for a topic (for testing/inspection). */\n getSocket(topic: string): UnixSocketPubSub | undefined {\n return this.#sockets.get(this.#topicKey(topic));\n }\n\n async #getOrCreate(topic: string): Promise<UnixSocketPubSub> {\n if (this.#closed) throw new Error('SignalsPubSub is closed');\n const key = this.#topicKey(topic);\n const existing = this.#sockets.get(key);\n if (existing) return existing;\n // Deduplicate concurrent callers so only one socket is created per topic.\n let inflight = this.#pending.get(key);\n if (!inflight) {\n inflight = this.#initSocket(topic, key);\n this.#pending.set(key, inflight);\n }\n const socket = await inflight;\n if (this.#closed) throw new Error('SignalsPubSub is closed');\n return socket;\n }\n\n async #initSocket(topic: string, key: string): Promise<UnixSocketPubSub> {\n try {\n const socketPath = await this.#socketPath(topic);\n if (this.#closed) throw new Error('SignalsPubSub is closed');\n const socket = new UnixSocketPubSub(socketPath);\n this.#sockets.set(key, socket);\n return socket;\n } finally {\n this.#pending.delete(key);\n }\n }\n\n async #socketPath(topic: string): Promise<string> {\n let key = this.#topicKey(topic);\n const dir = join('/tmp/mc', this.#resourceId);\n await mkdir(dir, { recursive: true });\n const candidate = join(dir, `${key}.sock`);\n // macOS sun_path limit is 104 bytes; Linux is 108. Use 104 as the\n // conservative bound. When the path is too long, replace the key with\n // a short hash so the socket can still be created.\n if (Buffer.byteLength(candidate) > 104) {\n key = createHash('sha256').update(key).digest('hex').slice(0, 16);\n return join(dir, `${key}.sock`);\n }\n return candidate;\n }\n\n /**\n * Derive a filesystem-safe key for the topic. Thread-stream topics embed\n * a threadId; all other topics use a sanitized version of the topic name.\n */\n #topicKey(topic: string): string {\n if (topic.startsWith(THREAD_STREAM_PREFIX)) {\n const encoded = topic.slice(THREAD_STREAM_PREFIX.length);\n try {\n const decoded = decodeURIComponent(encoded);\n const separatorIdx = decoded.indexOf('\\0');\n if (separatorIdx !== -1) {\n return decoded.slice(separatorIdx + 1);\n }\n } catch {\n // Malformed URI — fall through to sanitized fallback.\n }\n }\n // Fallback: use the topic directly (sanitized for filesystem)\n return topic.replace(/[^a-zA-Z0-9_-]/g, '_');\n }\n}\n\n/**\n * Creates a per-topic PubSub backed by Unix sockets for cross-process signal\n * and workflow event coordination within a mastracode resource.\n *\n * Each topic gets its own Unix socket under `/tmp/mc/<resourceId>/`.\n * Stale sockets from crashed processes are handled by the underlying\n * {@link UnixSocketPubSub}'s broker election logic.\n */\nexport function createSignalsPubSub(resourceId: string): SignalsPubSub {\n return new SignalsPubSub(resourceId);\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mastra/code-sdk",
3
- "version": "1.8.3-alpha.1",
3
+ "version": "1.8.4-alpha.0",
4
4
  "description": "Mastra Code SDK: the agent core behind Mastra Code (everything except the TUI) — build your own UIs and surfaces on top of it",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -57,18 +57,18 @@
57
57
  "yaml": "^2.7.1",
58
58
  "zod": "^4.6.4",
59
59
  "@mastra/agent-browser": "0.5.3",
60
+ "@mastra/core": "1.72.0-alpha.0",
61
+ "@mastra/duckdb": "1.11.1",
62
+ "@mastra/fastembed": "1.3.2",
60
63
  "@mastra/github-signals": "0.5.0",
61
- "@mastra/core": "1.71.0-alpha.1",
62
- "@mastra/duckdb": "1.11.1-alpha.1",
63
- "@mastra/libsql": "1.23.3-alpha.0",
64
64
  "@mastra/mcp": "2.1.0",
65
- "@mastra/fastembed": "1.3.2-alpha.0",
66
- "@mastra/observability": "1.18.1-alpha.0",
67
- "@mastra/memory": "1.32.1-alpha.0",
65
+ "@mastra/observability": "1.18.1",
68
66
  "@mastra/parallel": "0.1.2",
69
- "@mastra/pg": "1.27.1-alpha.0",
67
+ "@mastra/libsql": "1.23.3",
68
+ "@mastra/pg": "1.27.2-alpha.0",
70
69
  "@mastra/schema-compat": "1.3.11",
71
70
  "@mastra/stagehand": "0.3.5",
71
+ "@mastra/memory": "1.32.2-alpha.0",
72
72
  "@mastra/tavily": "1.1.2"
73
73
  },
74
74
  "devDependencies": {
@@ -80,9 +80,9 @@
80
80
  "typescript": "^6.0.3",
81
81
  "typescript-eslint": "^8.57.0",
82
82
  "vitest": "4.1.11",
83
- "@internal/types-builder": "0.0.111",
84
- "@internal/workspace-test-utils": "0.0.80",
85
- "@internal/lint": "0.0.136"
83
+ "@internal/lint": "0.0.137",
84
+ "@internal/types-builder": "0.0.112",
85
+ "@internal/workspace-test-utils": "0.0.81"
86
86
  },
87
87
  "engines": {
88
88
  "node": ">=22.19.0"