@aexol/spectral 0.9.14 → 0.9.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/dist/memory/config.d.ts +12 -0
  2. package/dist/memory/config.d.ts.map +1 -1
  3. package/dist/memory/config.js +27 -0
  4. package/dist/memory/hooks/compaction-hook.d.ts.map +1 -1
  5. package/dist/memory/hooks/compaction-hook.js +5 -2
  6. package/dist/memory/hooks/compaction-trigger.d.ts.map +1 -1
  7. package/dist/memory/hooks/compaction-trigger.js +3 -2
  8. package/dist/memory/hooks/inter-agent-receiver.d.ts +3 -0
  9. package/dist/memory/hooks/inter-agent-receiver.d.ts.map +1 -0
  10. package/dist/memory/hooks/inter-agent-receiver.js +183 -0
  11. package/dist/memory/hooks/observer-trigger.d.ts.map +1 -1
  12. package/dist/memory/hooks/observer-trigger.js +14 -0
  13. package/dist/memory/index.d.ts.map +1 -1
  14. package/dist/memory/index.js +6 -0
  15. package/dist/memory/inter-agent-broker-singleton.d.ts +5 -0
  16. package/dist/memory/inter-agent-broker-singleton.d.ts.map +1 -0
  17. package/dist/memory/inter-agent-broker-singleton.js +7 -0
  18. package/dist/memory/inter-agent-relay.d.ts +7 -0
  19. package/dist/memory/inter-agent-relay.d.ts.map +1 -0
  20. package/dist/memory/inter-agent-relay.js +41 -0
  21. package/dist/memory/project-observations-store.d.ts +7 -0
  22. package/dist/memory/project-observations-store.d.ts.map +1 -1
  23. package/dist/memory/tools/receive-agent-observations.d.ts +17 -0
  24. package/dist/memory/tools/receive-agent-observations.d.ts.map +1 -0
  25. package/dist/memory/tools/receive-agent-observations.js +68 -0
  26. package/dist/memory/tools/share-project-observation.d.ts +8 -0
  27. package/dist/memory/tools/share-project-observation.d.ts.map +1 -0
  28. package/dist/memory/tools/share-project-observation.js +106 -0
  29. package/dist/relay/dispatcher.d.ts +1 -1
  30. package/dist/relay/dispatcher.d.ts.map +1 -1
  31. package/dist/relay/dispatcher.js +13 -0
  32. package/dist/sdk/coding-agent/core/agent-session.d.ts +10 -0
  33. package/dist/sdk/coding-agent/core/agent-session.d.ts.map +1 -1
  34. package/dist/sdk/coding-agent/core/agent-session.js +37 -1
  35. package/dist/server/handlers/files-search.d.ts +26 -0
  36. package/dist/server/handlers/files-search.d.ts.map +1 -0
  37. package/dist/server/handlers/files-search.js +89 -0
  38. package/dist/server/inter-agent-broker.d.ts +111 -0
  39. package/dist/server/inter-agent-broker.d.ts.map +1 -0
  40. package/dist/server/inter-agent-broker.js +136 -0
  41. package/dist/server/session-stream.d.ts +1 -0
  42. package/dist/server/session-stream.d.ts.map +1 -1
  43. package/dist/server/session-stream.js +55 -6
  44. package/dist/server/storage.d.ts +28 -0
  45. package/dist/server/storage.d.ts.map +1 -1
  46. package/dist/server/storage.js +101 -0
  47. package/dist/server/wire.d.ts +15 -0
  48. package/dist/server/wire.d.ts.map +1 -1
  49. package/package.json +1 -1
@@ -1,3 +1,14 @@
1
+ export type Relevance = "low" | "medium" | "high" | "critical";
2
+ export interface InterAgentConfig {
3
+ /** Auto-broadcast captured observations/reflections to other sessions. Default: true. */
4
+ broadcastOnCompact?: boolean;
5
+ /** Minimum relevance to auto-broadcast. Default: "high". */
6
+ minBroadcastRelevance?: Relevance;
7
+ /** Auto-receive shared observations on session start. Default: true. */
8
+ autoReceive?: boolean;
9
+ /** TTL in seconds for observation messages. Default: 86400. */
10
+ ttlSeconds?: number;
11
+ }
1
12
  export interface Config {
2
13
  observationThresholdTokens: number;
3
14
  compactionThresholdTokens: number;
@@ -14,6 +25,7 @@ export interface Config {
14
25
  prunerMaxTurnsPerPass?: number;
15
26
  /** @deprecated Use reflectorMaxTurnsPerPass and prunerMaxTurnsPerPass. */
16
27
  compactionMaxToolCalls?: number;
28
+ interAgent?: InterAgentConfig;
17
29
  }
18
30
  export interface EffectiveTurnLimits {
19
31
  observerMaxTurnsPerRun: number;
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/memory/config.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,MAAM;IACtB,0BAA0B,EAAE,MAAM,CAAC;IACnC,yBAAyB,EAAE,MAAM,CAAC;IAClC,yBAAyB,EAAE,MAAM,CAAC;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,eAAe,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;IACnD,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,0EAA0E;IAC1E,sBAAsB,CAAC,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,mBAAmB;IACnC,sBAAsB,EAAE,MAAM,CAAC;IAC/B,wBAAwB,EAAE,MAAM,CAAC;IACjC,qBAAqB,EAAE,MAAM,CAAC;CAC9B;AAED,eAAO,MAAM,QAAQ,EAAE,MAOtB,CAAC;AAiCF,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,mBAAmB,CAMrE;AAED,wBAAgB,aAAa,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAAC,MAAM,CAAC,CAOnF;AAaD,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAUpF"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/memory/config.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,UAAU,CAAC;AAE/D,MAAM,WAAW,gBAAgB;IAChC,yFAAyF;IACzF,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,4DAA4D;IAC5D,qBAAqB,CAAC,EAAE,SAAS,CAAC;IAClC,wEAAwE;IACxE,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,+DAA+D;IAC/D,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,MAAM;IACtB,0BAA0B,EAAE,MAAM,CAAC;IACnC,yBAAyB,EAAE,MAAM,CAAC;IAClC,yBAAyB,EAAE,MAAM,CAAC;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,eAAe,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;IACnD,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,0EAA0E;IAC1E,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,UAAU,CAAC,EAAE,gBAAgB,CAAC;CAC9B;AAED,MAAM,WAAW,mBAAmB;IACnC,sBAAsB,EAAE,MAAM,CAAC;IAC/B,wBAAwB,EAAE,MAAM,CAAC;IACjC,qBAAqB,EAAE,MAAM,CAAC;CAC9B;AAED,eAAO,MAAM,QAAQ,EAAE,MAOtB,CAAC;AA0DF,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,mBAAmB,CAMrE;AAED,wBAAgB,aAAa,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAAC,MAAM,CAAC,CAOnF;AAaD,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAUpF"}
@@ -27,6 +27,24 @@ function normalizeTurnLimit(normalized, key) {
27
27
  normalized[key] = value;
28
28
  }
29
29
  }
30
+ const RELEVANCE_VALUES = ["low", "medium", "high", "critical"];
31
+ function normalizeInterAgentConfig(value) {
32
+ if (!value || typeof value !== "object")
33
+ return undefined;
34
+ const raw = value;
35
+ const normalized = {};
36
+ if (typeof raw.broadcastOnCompact === "boolean")
37
+ normalized.broadcastOnCompact = raw.broadcastOnCompact;
38
+ if (typeof raw.autoReceive === "boolean")
39
+ normalized.autoReceive = raw.autoReceive;
40
+ if (typeof raw.minBroadcastRelevance === "string" && RELEVANCE_VALUES.includes(raw.minBroadcastRelevance)) {
41
+ normalized.minBroadcastRelevance = raw.minBroadcastRelevance;
42
+ }
43
+ if (Number.isInteger(raw.ttlSeconds) && typeof raw.ttlSeconds === "number" && raw.ttlSeconds >= 0) {
44
+ normalized.ttlSeconds = raw.ttlSeconds;
45
+ }
46
+ return Object.keys(normalized).length > 0 ? normalized : undefined;
47
+ }
30
48
  function normalizeSettingsConfig(value) {
31
49
  const normalized = { ...value };
32
50
  if ("passive" in normalized && typeof normalized.passive !== "boolean")
@@ -38,6 +56,15 @@ function normalizeSettingsConfig(value) {
38
56
  normalizeTurnLimit(normalized, "reflectorMaxTurnsPerPass");
39
57
  normalizeTurnLimit(normalized, "prunerMaxTurnsPerPass");
40
58
  normalizeTurnLimit(normalized, "compactionMaxToolCalls");
59
+ if ("interAgent" in normalized) {
60
+ const normalizedInterAgent = normalizeInterAgentConfig(normalized.interAgent);
61
+ if (normalizedInterAgent) {
62
+ normalized.interAgent = normalizedInterAgent;
63
+ }
64
+ else {
65
+ delete normalized.interAgent;
66
+ }
67
+ }
41
68
  return normalized;
42
69
  }
43
70
  export function resolveTurnLimits(config) {
@@ -1 +1 @@
1
- {"version":3,"file":"compaction-hook.d.ts","sourceRoot":"","sources":["../../../src/memory/hooks/compaction-hook.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAA+C,MAAM,iCAAiC,CAAC;AAsBjH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAmE7C,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,YAAY,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CA8XhF"}
1
+ {"version":3,"file":"compaction-hook.d.ts","sourceRoot":"","sources":["../../../src/memory/hooks/compaction-hook.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAA+C,MAAM,iCAAiC,CAAC;AAuBjH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAmE7C,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,YAAY,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CA+XhF"}
@@ -1,6 +1,7 @@
1
1
  import { debugLog, withDebugLogContext } from "../debug-log.js";
2
2
  import { resolveTurnLimits } from "../config.js";
3
3
  import { getProjectObsStore } from "../project-observations-store.js";
4
+ import { relayProjectObservations } from "../inter-agent-relay.js";
4
5
  import { collectObservationsByCoverage, findLastCompactionIndex, gapRawEntries, getMemoryState, } from "../branch.js";
5
6
  import { coverageTagCounts, migrateLegacyReflections, observationPoolTokens, renderSummary, runPruner, runReflector, } from "../compaction.js";
6
7
  import { observationsToPromptLines, runObserver } from "../observer.js";
@@ -60,8 +61,9 @@ function emitAgentLoopProgress(emitProgress, phase, event) {
60
61
  export function registerCompactionHook(ext, runtime) {
61
62
  ext.on("session_before_compact", async (event, ctx) => {
62
63
  if (runtime.compactHookInFlight) {
63
- if (ctx.hasUI)
64
- ctx.ui.notify("Observational memory: another compaction is already in progress; cancelling duplicate", "warning");
64
+ // Expected race: another compaction process reached the hook first.
65
+ // Let the in-flight compaction continue; this is not a user-facing error.
66
+ debugLog("compaction.skip_duplicate", { reason: "hook already in flight" });
65
67
  return { cancel: true };
66
68
  }
67
69
  runtime.compactHookInFlight = true;
@@ -382,5 +384,6 @@ export function registerCompactionHook(ext, runtime) {
382
384
  projectId,
383
385
  sessionId,
384
386
  });
387
+ relayProjectObservations(ctx.cwd, projectId, sessionId, projectObs, "reflection");
385
388
  }
386
389
  }
@@ -1 +1 @@
1
- {"version":3,"file":"compaction-trigger.d.ts","sourceRoot":"","sources":["../../../src/memory/hooks/compaction-trigger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAEpE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAU7C,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,YAAY,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAsFnF"}
1
+ {"version":3,"file":"compaction-trigger.d.ts","sourceRoot":"","sources":["../../../src/memory/hooks/compaction-trigger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAEpE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAU7C,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,YAAY,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAyFnF"}
@@ -67,8 +67,9 @@ export function registerCompactionTrigger(ext, runtime) {
67
67
  },
68
68
  onError: (error) => {
69
69
  runtime.compactInFlight = false;
70
- if (error.message === "Compaction cancelled") {
71
- // We already notified the user with the real reason before returning { cancel: true }.
70
+ if (error.message === "Compaction cancelled" ||
71
+ error.message === "Compaction already in progress") {
72
+ // Expected races: another compaction is already running. No need to surface.
72
73
  return;
73
74
  }
74
75
  if (hasUI)
@@ -0,0 +1,3 @@
1
+ import type { ExtensionAPI } from "../../sdk/coding-agent/index.js";
2
+ export declare function registerInterAgentReceiver(ext: ExtensionAPI): void;
3
+ //# sourceMappingURL=inter-agent-receiver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inter-agent-receiver.d.ts","sourceRoot":"","sources":["../../../src/memory/hooks/inter-agent-receiver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAoFpE,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,YAAY,GAAG,IAAI,CAgIlE"}
@@ -0,0 +1,183 @@
1
+ import { getProjectObsStore } from "../project-observations-store.js";
2
+ import { getInterAgentBroker } from "../inter-agent-broker-singleton.js";
3
+ import { loadConfig } from "../config.js";
4
+ import { debugLog } from "../debug-log.js";
5
+ const DELIVERED_OBSERVATIONS_CUSTOM_TYPE = "om.inter-agent.delivered";
6
+ function getSessionId(ctx) {
7
+ return ctx.sessionManager.getSessionId?.();
8
+ }
9
+ function isAlreadyDelivered(branch, memoryId) {
10
+ if (!Array.isArray(branch))
11
+ return false;
12
+ for (const entry of branch) {
13
+ if (typeof entry === "object" &&
14
+ entry !== null &&
15
+ entry.type === "custom_entry" &&
16
+ entry.customType === DELIVERED_OBSERVATIONS_CUSTOM_TYPE) {
17
+ const ids = entry.data?.ids;
18
+ if (Array.isArray(ids) && ids.includes(memoryId))
19
+ return true;
20
+ }
21
+ }
22
+ return false;
23
+ }
24
+ function formatObservationPrompt(observations) {
25
+ const lines = observations.map((o) => `- [${o.relevance}] ${o.content} (shared by ${o.sourceSessionId}, id: \`${o.memoryId}\`)`);
26
+ return [
27
+ "",
28
+ "---",
29
+ "SHARED PROJECT OBSERVATIONS from other active sessions:",
30
+ "The following observations were shared by other agents working on this project. " +
31
+ "Consider them when making decisions, but verify against current code.",
32
+ "",
33
+ ...lines,
34
+ "---",
35
+ ].join("\n");
36
+ }
37
+ function parseObservationPayload(msg) {
38
+ if (msg.kind !== "observation")
39
+ return null;
40
+ try {
41
+ const raw = JSON.parse(msg.payload);
42
+ if (typeof raw.memoryId !== "string" ||
43
+ typeof raw.content !== "string" ||
44
+ typeof raw.sourceSessionId !== "string" ||
45
+ typeof raw.timestamp !== "string") {
46
+ return null;
47
+ }
48
+ const relevance = ["low", "medium", "high", "critical"].includes(String(raw.relevance))
49
+ ? raw.relevance
50
+ : "medium";
51
+ const originType = raw.originType === "reflection" ? "reflection" : "observation";
52
+ return {
53
+ memoryId: raw.memoryId,
54
+ content: raw.content,
55
+ relevance,
56
+ sourceSessionId: raw.sourceSessionId,
57
+ originType,
58
+ timestamp: raw.timestamp,
59
+ };
60
+ }
61
+ catch {
62
+ return null;
63
+ }
64
+ }
65
+ export function registerInterAgentReceiver(ext) {
66
+ const broker = getInterAgentBroker();
67
+ if (!broker) {
68
+ debugLog("inter_agent.receiver.disabled", { reason: "no_broker" });
69
+ return;
70
+ }
71
+ const receivers = new Map();
72
+ ext.on("session_start", (_event, ctx) => {
73
+ const store = getProjectObsStore();
74
+ if (!store)
75
+ return;
76
+ const projectId = store.getProjectByCwd(ctx.cwd);
77
+ const sessionId = getSessionId(ctx);
78
+ if (!projectId || !sessionId)
79
+ return;
80
+ const config = loadConfig(ctx.cwd);
81
+ if (config.interAgent?.autoReceive === false) {
82
+ debugLog("inter_agent.receiver.auto_disabled", { sessionId });
83
+ return;
84
+ }
85
+ // Rebuild delivered set from persisted custom entries so reloads don't
86
+ // re-inject the same observations.
87
+ const deliveredIds = new Set();
88
+ const branch = ctx.sessionManager.getBranch?.();
89
+ if (Array.isArray(branch)) {
90
+ for (const entry of branch) {
91
+ if (typeof entry === "object" &&
92
+ entry !== null &&
93
+ entry.type === "custom_entry" &&
94
+ entry.customType === DELIVERED_OBSERVATIONS_CUSTOM_TYPE) {
95
+ const ids = entry.data?.ids;
96
+ if (Array.isArray(ids)) {
97
+ for (const id of ids)
98
+ deliveredIds.add(id);
99
+ }
100
+ }
101
+ }
102
+ }
103
+ const state = {
104
+ sessionId,
105
+ projectId,
106
+ deliveredIds,
107
+ pending: [],
108
+ };
109
+ receivers.set(sessionId, state);
110
+ // Poll historical/undelivered messages.
111
+ const msgs = broker.poll({
112
+ projectId,
113
+ channel: "memory",
114
+ recipientSessionId: sessionId,
115
+ markDelivered: true,
116
+ limit: 50,
117
+ });
118
+ for (const msg of msgs) {
119
+ if (msg.senderSessionId === sessionId)
120
+ continue;
121
+ const obs = parseObservationPayload(msg);
122
+ if (!obs || deliveredIds.has(obs.memoryId))
123
+ continue;
124
+ state.pending.push(obs);
125
+ deliveredIds.add(obs.memoryId);
126
+ }
127
+ // Subscribe to live messages.
128
+ state.unsubscribe = broker.subscribe({
129
+ sessionId,
130
+ projectId,
131
+ channel: "memory",
132
+ handler: (msg) => {
133
+ if (msg.senderSessionId === sessionId)
134
+ return;
135
+ const obs = parseObservationPayload(msg);
136
+ if (!obs || state.deliveredIds.has(obs.memoryId))
137
+ return;
138
+ state.pending.push(obs);
139
+ state.deliveredIds.add(obs.memoryId);
140
+ if (ctx.hasUI && ctx.ui) {
141
+ ctx.ui.notify(`Shared observation from ${msg.senderName ?? msg.senderSessionId}: ${obs.content.slice(0, 120)}`, "info");
142
+ }
143
+ },
144
+ });
145
+ if (state.pending.length > 0) {
146
+ debugLog("inter_agent.receiver.pending", {
147
+ sessionId,
148
+ count: state.pending.length,
149
+ });
150
+ }
151
+ });
152
+ ext.on("before_agent_start", (event, ctx) => {
153
+ const sessionId = getSessionId(ctx);
154
+ if (!sessionId)
155
+ return undefined;
156
+ const state = receivers.get(sessionId);
157
+ if (!state || state.pending.length === 0)
158
+ return undefined;
159
+ const observations = state.pending.splice(0, state.pending.length);
160
+ if (observations.length > 0) {
161
+ ext.appendEntry(DELIVERED_OBSERVATIONS_CUSTOM_TYPE, { ids: observations.map((o) => o.memoryId) });
162
+ }
163
+ const appendix = formatObservationPrompt(observations);
164
+ debugLog("inter_agent.receiver.inject", {
165
+ sessionId,
166
+ count: observations.length,
167
+ chars: appendix.length,
168
+ });
169
+ return {
170
+ systemPrompt: `${event.systemPrompt}\n${appendix}`,
171
+ };
172
+ });
173
+ ext.on("session_shutdown", (_event, ctx) => {
174
+ const sessionId = getSessionId(ctx);
175
+ if (!sessionId)
176
+ return;
177
+ const state = receivers.get(sessionId);
178
+ if (!state)
179
+ return;
180
+ state.unsubscribe?.();
181
+ receivers.delete(sessionId);
182
+ });
183
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"observer-trigger.d.ts","sourceRoot":"","sources":["../../../src/memory/hooks/observer-trigger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAWpE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAK7C,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,YAAY,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAyKjF"}
1
+ {"version":3,"file":"observer-trigger.d.ts","sourceRoot":"","sources":["../../../src/memory/hooks/observer-trigger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAWpE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAO7C,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,YAAY,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CA0KjF"}
@@ -5,6 +5,8 @@ import { observationsToPromptLines, runObserver } from "../observer.js";
5
5
  import { serializeSourceAddressedBranchEntries } from "../serialize.js";
6
6
  import { estimateEntryTokens, estimateStringTokens } from "../tokens.js";
7
7
  import { OBSERVATION_CUSTOM_TYPE, reflectionToPromptLine } from "../types.js";
8
+ import { getProjectObsStore } from "../project-observations-store.js";
9
+ import { relayProjectObservations } from "../inter-agent-relay.js";
8
10
  export function registerObserverTrigger(ext, runtime) {
9
11
  ext.on("turn_end", (_event, ctx) => {
10
12
  process.stderr.write("[obs-mem] turn_end fired\n");
@@ -146,6 +148,7 @@ export function registerObserverTrigger(ext, runtime) {
146
148
  records,
147
149
  });
148
150
  ext.appendEntry(OBSERVATION_CUSTOM_TYPE, data);
151
+ relayObservationRecords(cwd, ctx.sessionManager.getSessionId?.(), records);
149
152
  debugLog("observer.appended", { count: records.length, tokenCount: observationTokens, coversFromId, coversUpToId: effectiveCoversUpToId });
150
153
  if (hasUI && ui)
151
154
  ui.notify(`Observational memory: ${records.length} observation${records.length === 1 ? "" : "s"} recorded (~${observationTokens.toLocaleString()} tokens)`, "info");
@@ -157,3 +160,14 @@ export function registerObserverTrigger(ext, runtime) {
157
160
  }));
158
161
  });
159
162
  }
163
+ function relayObservationRecords(cwd, sessionId, records) {
164
+ if (!sessionId || records.length === 0)
165
+ return;
166
+ const store = getProjectObsStore();
167
+ if (!store)
168
+ return;
169
+ const projectId = store.getProjectByCwd(cwd);
170
+ if (!projectId)
171
+ return;
172
+ relayProjectObservations(cwd, projectId, sessionId, records, "observation");
173
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/memory/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAWjE,MAAM,CAAC,OAAO,UAAU,mBAAmB,CAAC,GAAG,EAAE,YAAY,QAe5D"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/memory/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAcjE,MAAM,CAAC,OAAO,UAAU,mBAAmB,CAAC,GAAG,EAAE,YAAY,QAmB5D"}
@@ -2,10 +2,13 @@ import { registerStatusCommand } from "./commands/status.js";
2
2
  import { registerViewCommand } from "./commands/view.js";
3
3
  import { registerCompactionHook } from "./hooks/compaction-hook.js";
4
4
  import { registerCompactionTrigger } from "./hooks/compaction-trigger.js";
5
+ import { registerInterAgentReceiver } from "./hooks/inter-agent-receiver.js";
5
6
  import { registerObserverTrigger } from "./hooks/observer-trigger.js";
6
7
  import { Runtime } from "./runtime.js";
7
8
  import { registerRecallTool } from "./tools/recall-observation.js";
8
9
  import { registerReadProjectObservationsTool } from "./tools/read-project-observations.js";
10
+ import { registerReceiveAgentObservationsTool } from "./tools/receive-agent-observations.js";
11
+ import { registerShareProjectObservationTool } from "./tools/share-project-observation.js";
9
12
  import { registerWriteProjectObservationTool } from "./tools/write-project-observation.js";
10
13
  export default function observationalMemory(ext) {
11
14
  const runtime = new Runtime();
@@ -19,4 +22,7 @@ export default function observationalMemory(ext) {
19
22
  registerRecallTool(ext);
20
23
  registerReadProjectObservationsTool(ext);
21
24
  registerWriteProjectObservationTool(ext);
25
+ registerShareProjectObservationTool(ext);
26
+ registerReceiveAgentObservationsTool(ext);
27
+ registerInterAgentReceiver(ext);
22
28
  }
@@ -0,0 +1,5 @@
1
+ import type { InterAgentBroker, InterAgentMessage } from "../server/inter-agent-broker.js";
2
+ export declare function setInterAgentBroker(broker: InterAgentBroker): void;
3
+ export declare function getInterAgentBroker(): InterAgentBroker | null;
4
+ export type { InterAgentBroker, InterAgentMessage };
5
+ //# sourceMappingURL=inter-agent-broker-singleton.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inter-agent-broker-singleton.d.ts","sourceRoot":"","sources":["../../src/memory/inter-agent-broker-singleton.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAI3F,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,gBAAgB,GAAG,IAAI,CAElE;AAED,wBAAgB,mBAAmB,IAAI,gBAAgB,GAAG,IAAI,CAE7D;AAED,YAAY,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,CAAC"}
@@ -0,0 +1,7 @@
1
+ let _broker = null;
2
+ export function setInterAgentBroker(broker) {
3
+ _broker = broker;
4
+ }
5
+ export function getInterAgentBroker() {
6
+ return _broker;
7
+ }
@@ -0,0 +1,7 @@
1
+ export interface RelayObservation {
2
+ id: string;
3
+ content: string;
4
+ relevance: string;
5
+ }
6
+ export declare function relayProjectObservations(cwd: string, projectId: string, sessionId: string, observations: RelayObservation[], originType?: "observation" | "reflection"): void;
7
+ //# sourceMappingURL=inter-agent-relay.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inter-agent-relay.d.ts","sourceRoot":"","sources":["../../src/memory/inter-agent-relay.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,gBAAgB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,wBAAwB,CACvC,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,gBAAgB,EAAE,EAChC,UAAU,GAAE,aAAa,GAAG,YAA2B,GACrD,IAAI,CAiCN"}
@@ -0,0 +1,41 @@
1
+ import { getInterAgentBroker } from "./inter-agent-broker-singleton.js";
2
+ import { loadConfig } from "./config.js";
3
+ import { debugLog } from "./debug-log.js";
4
+ const RELEVANCE_RANK = {
5
+ low: 0,
6
+ medium: 1,
7
+ high: 2,
8
+ critical: 3,
9
+ };
10
+ export function relayProjectObservations(cwd, projectId, sessionId, observations, originType = "reflection") {
11
+ const broker = getInterAgentBroker();
12
+ if (!broker)
13
+ return;
14
+ const config = loadConfig(cwd).interAgent;
15
+ if (config?.broadcastOnCompact === false) {
16
+ debugLog("inter_agent.broadcast.disabled", { sessionId });
17
+ return;
18
+ }
19
+ const minRank = RELEVANCE_RANK[config?.minBroadcastRelevance ?? "high"] ?? 2;
20
+ const ttlSeconds = config?.ttlSeconds;
21
+ for (const obs of observations) {
22
+ if ((RELEVANCE_RANK[obs.relevance] ?? 0) < minRank)
23
+ continue;
24
+ broker.send({
25
+ projectId,
26
+ channel: "memory",
27
+ senderSessionId: sessionId,
28
+ kind: "observation",
29
+ payload: JSON.stringify({
30
+ memoryId: obs.id,
31
+ content: obs.content,
32
+ relevance: obs.relevance,
33
+ sourceSessionId: sessionId,
34
+ originType,
35
+ timestamp: new Date().toISOString(),
36
+ }),
37
+ ttlSeconds,
38
+ });
39
+ debugLog("inter_agent.broadcast.sent", { memoryId: obs.id, sessionId });
40
+ }
41
+ }
@@ -17,6 +17,13 @@ export interface ProjectObservationsStore {
17
17
  createdAt: number;
18
18
  sessionId: string;
19
19
  }>;
20
+ getProjectObservationById(projectId: string, observationId: string): {
21
+ id: string;
22
+ content: string;
23
+ relevance: string;
24
+ createdAt: number;
25
+ sessionId: string;
26
+ } | null;
20
27
  getProjectByCwd(cwd: string): string | null;
21
28
  }
22
29
  export declare function setProjectObsStore(store: ProjectObservationsStore): void;
@@ -1 +1 @@
1
- {"version":3,"file":"project-observations-store.d.ts","sourceRoot":"","sources":["../../src/memory/project-observations-store.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,WAAW,wBAAwB;IACxC,yBAAyB,CACxB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,EACvE,SAAS,EAAE,MAAM,GACf,IAAI,CAAC;IACR,yBAAyB,CACxB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,KAAK,CAAC,EAAE,MAAM,GACZ,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACvF,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;CAC5C;AAID,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,wBAAwB,GAAG,IAAI,CAExE;AAED,wBAAgB,kBAAkB,IAAI,wBAAwB,GAAG,IAAI,CAEpE"}
1
+ {"version":3,"file":"project-observations-store.d.ts","sourceRoot":"","sources":["../../src/memory/project-observations-store.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,WAAW,wBAAwB;IACxC,yBAAyB,CACxB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,EACvE,SAAS,EAAE,MAAM,GACf,IAAI,CAAC;IACR,yBAAyB,CACxB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,KAAK,CAAC,EAAE,MAAM,GACZ,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACvF,yBAAyB,CACxB,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,MAAM,GACnB;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACnG,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;CAC5C;AAID,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,wBAAwB,GAAG,IAAI,CAExE;AAED,wBAAgB,kBAAkB,IAAI,wBAAwB,GAAG,IAAI,CAEpE"}
@@ -0,0 +1,17 @@
1
+ import { Type } from "../../sdk/ai/index.js";
2
+ import { type ExtensionAPI } from "../../sdk/coding-agent/index.js";
3
+ export declare const RECEIVE_AGENT_OBSERVATIONS_TOOL_NAME = "receive_agent_observations";
4
+ export interface AgentObservationPayload {
5
+ memoryId: string;
6
+ content: string;
7
+ relevance: "low" | "medium" | "high" | "critical";
8
+ sourceSessionId: string;
9
+ originType: "observation" | "reflection";
10
+ timestamp: string;
11
+ }
12
+ export declare const receiveAgentObservationsTool: import("../../sdk/coding-agent/index.js").ToolDefinition<Type.TObject<{
13
+ since: Type.TOptional<Type.TNumber>;
14
+ limit: Type.TOptional<Type.TNumber>;
15
+ }>, unknown> & import("../../sdk/coding-agent/index.js").ToolDefinition<any, any>;
16
+ export declare function registerReceiveAgentObservationsTool(ext: ExtensionAPI): void;
17
+ //# sourceMappingURL=receive-agent-observations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"receive-agent-observations.d.ts","sourceRoot":"","sources":["../../../src/memory/tools/receive-agent-observations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAC7C,OAAO,EAAc,KAAK,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAIhF,eAAO,MAAM,oCAAoC,+BAA+B,CAAC;AAEjF,MAAM,WAAW,uBAAuB;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,UAAU,CAAC;IAClD,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,aAAa,GAAG,YAAY,CAAC;IACzC,SAAS,EAAE,MAAM,CAAC;CAClB;AAED,eAAO,MAAM,4BAA4B;;;iFAmEvC,CAAC;AAEH,wBAAgB,oCAAoC,CAAC,GAAG,EAAE,YAAY,GAAG,IAAI,CAE5E"}
@@ -0,0 +1,68 @@
1
+ import { Type } from "../../sdk/ai/index.js";
2
+ import { defineTool } from "../../sdk/coding-agent/index.js";
3
+ import { getProjectObsStore } from "../project-observations-store.js";
4
+ import { getInterAgentBroker } from "../inter-agent-broker-singleton.js";
5
+ export const RECEIVE_AGENT_OBSERVATIONS_TOOL_NAME = "receive_agent_observations";
6
+ export const receiveAgentObservationsTool = defineTool({
7
+ name: RECEIVE_AGENT_OBSERVATIONS_TOOL_NAME,
8
+ label: "Receive observations from other agents",
9
+ description: "Poll for observations shared by other active sessions on this project. " +
10
+ "Call this before starting work or when you suspect another agent has shared context.",
11
+ promptSnippet: "Use receive_agent_observations() to pull shared observations from other active sessions.",
12
+ promptGuidelines: [
13
+ "Call at the start of a session or task to pick up context shared by collaborators.",
14
+ "Deduplicate results by memory_id against observations already in your context.",
15
+ ],
16
+ parameters: Type.Object({
17
+ since: Type.Optional(Type.Number({ description: "Optional timestamp (ms) to only fetch newer messages." })),
18
+ limit: Type.Optional(Type.Number({ default: 20 })),
19
+ }),
20
+ async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
21
+ const store = getProjectObsStore();
22
+ const broker = getInterAgentBroker();
23
+ if (!store || !broker) {
24
+ return {
25
+ content: [{ type: "text", text: "Receiving is unavailable outside spectral serve." }],
26
+ details: { status: "unavailable", observations: [] },
27
+ };
28
+ }
29
+ const projectId = store.getProjectByCwd(ctx.cwd);
30
+ const sessionId = ctx.sessionManager.getSessionId?.();
31
+ if (!projectId || !sessionId) {
32
+ return {
33
+ content: [{ type: "text", text: "No project/session context." }],
34
+ details: { status: "no_context", observations: [] },
35
+ };
36
+ }
37
+ const msgs = broker.poll({
38
+ projectId,
39
+ channel: "memory",
40
+ recipientSessionId: sessionId,
41
+ since: params.since,
42
+ limit: params.limit ?? 20,
43
+ markDelivered: true,
44
+ });
45
+ const observations = msgs
46
+ .filter((m) => m.kind === "observation")
47
+ .map((m) => JSON.parse(m.payload));
48
+ if (observations.length === 0) {
49
+ return {
50
+ content: [{ type: "text", text: "No new observations from other agents." }],
51
+ details: { status: "no_new", observations: [] },
52
+ };
53
+ }
54
+ const lines = observations.map((o) => `[${o.relevance}] ${o.content} (from ${o.sourceSessionId}, id: \`${o.memoryId}\`)`);
55
+ return {
56
+ content: [
57
+ {
58
+ type: "text",
59
+ text: `New shared observations:\n\n${lines.join("\n")}`,
60
+ },
61
+ ],
62
+ details: { status: "ok", observations },
63
+ };
64
+ },
65
+ });
66
+ export function registerReceiveAgentObservationsTool(ext) {
67
+ ext.registerTool(receiveAgentObservationsTool);
68
+ }
@@ -0,0 +1,8 @@
1
+ import { Type } from "../../sdk/ai/index.js";
2
+ import { type ExtensionAPI } from "../../sdk/coding-agent/index.js";
3
+ export declare const SHARE_PROJECT_OBSERVATION_TOOL_NAME = "share_project_observation";
4
+ export declare const shareProjectObservationTool: import("../../sdk/coding-agent/index.js").ToolDefinition<Type.TObject<{
5
+ memory_id: Type.TString;
6
+ }>, unknown> & import("../../sdk/coding-agent/index.js").ToolDefinition<any, any>;
7
+ export declare function registerShareProjectObservationTool(ext: ExtensionAPI): void;
8
+ //# sourceMappingURL=share-project-observation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"share-project-observation.d.ts","sourceRoot":"","sources":["../../../src/memory/tools/share-project-observation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAC7C,OAAO,EAAc,KAAK,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAOhF,eAAO,MAAM,mCAAmC,8BAA8B,CAAC;AA6C/E,eAAO,MAAM,2BAA2B;;iFAqEtC,CAAC;AAEH,wBAAgB,mCAAmC,CAAC,GAAG,EAAE,YAAY,GAAG,IAAI,CAE3E"}