@aexol/spectral 0.9.184 → 0.9.186

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 (60) hide show
  1. package/dist/agent/index.d.ts.map +1 -1
  2. package/dist/agent/index.js +8 -2
  3. package/dist/memory/branch.d.ts +13 -0
  4. package/dist/memory/branch.d.ts.map +1 -1
  5. package/dist/memory/branch.js +100 -0
  6. package/dist/memory/hooks/compaction-hook.d.ts.map +1 -1
  7. package/dist/memory/hooks/compaction-hook.js +43 -1
  8. package/dist/memory/hooks/observer-trigger.d.ts.map +1 -1
  9. package/dist/memory/hooks/observer-trigger.js +9 -3
  10. package/dist/memory/index.d.ts +13 -0
  11. package/dist/memory/index.d.ts.map +1 -1
  12. package/dist/memory/index.js +17 -26
  13. package/dist/memory/project-observation-memory.d.ts +16 -0
  14. package/dist/memory/project-observation-memory.d.ts.map +1 -0
  15. package/dist/memory/project-observation-memory.js +27 -0
  16. package/dist/memory/project-observations-store.d.ts +8 -0
  17. package/dist/memory/project-observations-store.d.ts.map +1 -1
  18. package/dist/memory/project-observations-store.js +5 -0
  19. package/dist/memory/prompts.d.ts +2 -2
  20. package/dist/memory/prompts.d.ts.map +1 -1
  21. package/dist/memory/prompts.js +1 -1
  22. package/dist/memory/session-memory.d.ts +21 -0
  23. package/dist/memory/session-memory.d.ts.map +1 -0
  24. package/dist/memory/session-memory.js +33 -0
  25. package/dist/memory/tool-output-compressor.d.ts +0 -1
  26. package/dist/memory/tool-output-compressor.d.ts.map +1 -1
  27. package/dist/memory/tool-output-compressor.js +0 -1
  28. package/dist/memory/tools/compact-context.d.ts +31 -0
  29. package/dist/memory/tools/compact-context.d.ts.map +1 -1
  30. package/dist/memory/tools/compact-context.js +224 -23
  31. package/dist/memory/tools/read-project-observations.d.ts.map +1 -1
  32. package/dist/memory/tools/read-project-observations.js +6 -3
  33. package/dist/memory/tools/recall-observation.d.ts.map +1 -1
  34. package/dist/memory/tools/recall-observation.js +18 -4
  35. package/dist/memory/tools/receive-agent-observations.d.ts.map +1 -1
  36. package/dist/memory/tools/receive-agent-observations.js +4 -2
  37. package/dist/memory/tools/share-project-observation.d.ts.map +1 -1
  38. package/dist/memory/tools/share-project-observation.js +5 -3
  39. package/dist/memory/tools/write-project-observation.d.ts.map +1 -1
  40. package/dist/memory/tools/write-project-observation.js +16 -3
  41. package/dist/sdk/coding-agent/core/agent-session.d.ts.map +1 -1
  42. package/dist/sdk/coding-agent/core/agent-session.js +0 -1
  43. package/dist/sdk/coding-agent/core/compaction/compaction.d.ts +11 -0
  44. package/dist/sdk/coding-agent/core/compaction/compaction.d.ts.map +1 -1
  45. package/dist/sdk/coding-agent/core/compaction/compaction.js +15 -2
  46. package/dist/sdk/coding-agent/core/compaction/policy.d.ts +22 -4
  47. package/dist/sdk/coding-agent/core/compaction/policy.d.ts.map +1 -1
  48. package/dist/sdk/coding-agent/core/compaction/policy.js +39 -6
  49. package/dist/server/inter-agent-broker.d.ts +12 -7
  50. package/dist/server/inter-agent-broker.d.ts.map +1 -1
  51. package/dist/server/inter-agent-broker.js +12 -7
  52. package/dist/server/session-stream.d.ts.map +1 -1
  53. package/dist/server/session-stream.js +6 -4
  54. package/dist/server/sqlite-adapter.d.ts +11 -8
  55. package/dist/server/sqlite-adapter.d.ts.map +1 -1
  56. package/dist/server/sqlite-adapter.js +195 -44
  57. package/dist/server/storage.d.ts +22 -5
  58. package/dist/server/storage.d.ts.map +1 -1
  59. package/dist/server/storage.js +52 -6
  60. package/package.json +1 -1
@@ -0,0 +1,33 @@
1
+ import { registerStatusCommand } from "./commands/status.js";
2
+ import { registerViewCommand } from "./commands/view.js";
3
+ import { registerCompactionHook } from "./hooks/compaction-hook.js";
4
+ import { registerCompactionTrigger } from "./hooks/compaction-trigger.js";
5
+ import { registerObserverTrigger } from "./hooks/observer-trigger.js";
6
+ import { registerToolOutputCompressor } from "./tool-output-compressor.js";
7
+ import { registerCompactContextTool } from "./tools/compact-context.js";
8
+ /**
9
+ * Session/branch memory layer.
10
+ *
11
+ * This layer owns the branch-scoped observational-memory pipeline:
12
+ * - the observer (`observer.ts` via `observer-trigger.ts`) extracts
13
+ * observations from new conversation turns;
14
+ * - the deterministic pruner (`deterministic-pruner.ts` via
15
+ * `compaction-hook.ts`) drops low-value observations at compaction time;
16
+ * - the summary render path (`compaction.ts`) produces the compacted memory
17
+ * that is persisted into `session_memory_snapshots`;
18
+ * - the snapshot persistence hooks (`compaction-hook.ts`,
19
+ * `compaction-trigger.ts`, `observer-trigger.ts`) keep that session memory
20
+ * alive across compactions.
21
+ *
22
+ * It is intentionally separate from `project-observation-memory.ts`, which
23
+ * owns the per-machine + per-cwd, cross-session project observation store.
24
+ */
25
+ export function registerSessionMemory(ext, runtime) {
26
+ registerObserverTrigger(ext, runtime);
27
+ registerCompactContextTool(ext, runtime);
28
+ registerCompactionTrigger(ext, runtime);
29
+ registerCompactionHook(ext, runtime);
30
+ registerToolOutputCompressor(ext, runtime);
31
+ registerStatusCommand(ext, runtime);
32
+ registerViewCommand(ext, runtime);
33
+ }
@@ -5,7 +5,6 @@
5
5
  * This is complementary to the conversation-level compaction pipeline:
6
6
  * - Compressor: compresses raw tool output (pre-context)
7
7
  * - Compaction: summarizes + prunes conversation history (post-context)
8
- * - Unified compaction: narrative summary + observation extraction (at compaction boundary)
9
8
  *
10
9
  * Strategies (mirroring RTK's four-pronged approach):
11
10
  *
@@ -1 +1 @@
1
- {"version":3,"file":"tool-output-compressor.d.ts","sourceRoot":"","sources":["../../src/memory/tool-output-compressor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,KAAK,EAEX,gBAAgB,EAIhB,MAAM,8CAA8C,CAAC;AACtD,OAAO,EAGN,KAAK,0BAA0B,EAC/B,MAAM,+CAA+C,CAAC;AACvD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAO5C,MAAM,MAAM,gBAAgB,GAAG,0BAA0B,CAAC;AAE1D,eAAO,MAAM,yBAAyB,EAAE,gBAEvC,CAAC;AAgsBF,MAAM,WAAW,gBAAgB;IAChC,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,8BAA8B;IAC9C,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC3F;AA8BD,wBAAgB,iCAAiC,CAAC,OAAO,EAAE,OAAO,GAAG,8BAA8B,CAElG;AAED,wBAAgB,mCAAmC,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAE1E;AAkDD;;;;;GAKG;AACH,wBAAgB,0BAA0B,CACzC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,OAAO,EACd,MAAM,EAAE;IAAE,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE,EAC7F,GAAG,CAAC,EAAE,IAAI,CAAC,gBAAgB,EAAE,iBAAiB,CAAC,GAC7C,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,SAAS,CAYvG;AAED,wBAAgB,4BAA4B,CAC3C,GAAG,EAAE,YAAY,EACjB,OAAO,EAAE,OAAO,GACd,IAAI,CAkCN"}
1
+ {"version":3,"file":"tool-output-compressor.d.ts","sourceRoot":"","sources":["../../src/memory/tool-output-compressor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,KAAK,EAEX,gBAAgB,EAIhB,MAAM,8CAA8C,CAAC;AACtD,OAAO,EAGN,KAAK,0BAA0B,EAC/B,MAAM,+CAA+C,CAAC;AACvD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAO5C,MAAM,MAAM,gBAAgB,GAAG,0BAA0B,CAAC;AAE1D,eAAO,MAAM,yBAAyB,EAAE,gBAEvC,CAAC;AAgsBF,MAAM,WAAW,gBAAgB;IAChC,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,8BAA8B;IAC9C,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC3F;AA8BD,wBAAgB,iCAAiC,CAAC,OAAO,EAAE,OAAO,GAAG,8BAA8B,CAElG;AAED,wBAAgB,mCAAmC,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAE1E;AAkDD;;;;;GAKG;AACH,wBAAgB,0BAA0B,CACzC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,OAAO,EACd,MAAM,EAAE;IAAE,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE,EAC7F,GAAG,CAAC,EAAE,IAAI,CAAC,gBAAgB,EAAE,iBAAiB,CAAC,GAC7C,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,SAAS,CAYvG;AAED,wBAAgB,4BAA4B,CAC3C,GAAG,EAAE,YAAY,EACjB,OAAO,EAAE,OAAO,GACd,IAAI,CAkCN"}
@@ -5,7 +5,6 @@
5
5
  * This is complementary to the conversation-level compaction pipeline:
6
6
  * - Compressor: compresses raw tool output (pre-context)
7
7
  * - Compaction: summarizes + prunes conversation history (post-context)
8
- * - Unified compaction: narrative summary + observation extraction (at compaction boundary)
9
8
  *
10
9
  * Strategies (mirroring RTK's four-pronged approach):
11
10
  *
@@ -3,7 +3,38 @@ import { type CompactionPolicy } from "../../sdk/coding-agent/core/compaction/in
3
3
  import { type ExtensionAPI } from "../../sdk/coding-agent/index.js";
4
4
  import type { Runtime } from "../runtime.js";
5
5
  export declare const COMPACT_CONTEXT_TOOL_NAME = "compact_context";
6
+ export interface AutoPruneFrozen {
7
+ /** Session identity used to invalidate the frozen cut after branch/session switches. */
8
+ sessionId: string;
9
+ /** Index into the provider message array for the first kept message. */
10
+ cutIndex: number;
11
+ /** Stable AgentMessage reference at `cutIndex`, used as an O(1) identity anchor. */
12
+ anchor: AgentMessage;
13
+ /** Deterministic DCP-lite summary generated at the original prune. */
14
+ summary: string;
15
+ /** Original tokensBefore for the frozen compaction summary message. */
16
+ tokensBefore: number;
17
+ /** Original compaction timestamp for the frozen compaction summary message. */
18
+ timestamp: string;
19
+ /** Keep-recent token budget that produced this cut (policy-change invalidation). */
20
+ keepRecentTokensUsed: number;
21
+ /** Token estimate of the kept tail at the moment the cut was frozen. */
22
+ tailTokensAtFreeze: number;
23
+ }
6
24
  /** Request-time DCP adapter for isolated loops: it borrows only pure policy helpers. */
7
25
  export declare function buildIsolatedRequestTimePrunedMessages(currentMessages: AgentMessage[], policy?: CompactionPolicy, keepRecentTokens?: number): AgentMessage[];
26
+ export interface IsolatedRequestTimePruneResult {
27
+ messages: AgentMessage[];
28
+ frozen: AutoPruneFrozen | null;
29
+ }
30
+ /**
31
+ * Stateful per-run variant of {@link buildIsolatedRequestTimePrunedMessages}.
32
+ *
33
+ * The subagent loop owns the returned `frozen` value across provider requests.
34
+ * On the first prune we freeze the deterministic summary + cut, then reuse that
35
+ * exact prefix until the kept-tail anchor reference changes or the tail grows
36
+ * past the window-relative hysteresis budget.
37
+ */
38
+ export declare function buildIsolatedRequestTimePrunedMessagesResult(currentMessages: AgentMessage[], policy?: CompactionPolicy, keepRecentTokens?: number, frozen?: AutoPruneFrozen | null, contextWindow?: number | null): IsolatedRequestTimePruneResult;
8
39
  export declare function registerCompactContextTool(ext: ExtensionAPI, runtime: Runtime): void;
9
40
  //# sourceMappingURL=compact-context.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"compact-context.d.ts","sourceRoot":"","sources":["../../../src/memory/tools/compact-context.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,EAUN,KAAK,gBAAgB,EACrB,MAAM,iDAAiD,CAAC;AACzD,OAAO,EAAc,KAAK,YAAY,EAAyB,MAAM,iCAAiC,CAAC;AAKvG,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAE7C,eAAO,MAAM,yBAAyB,oBAAoB,CAAC;AAkX3D,wFAAwF;AACxF,wBAAgB,sCAAsC,CACrD,eAAe,EAAE,YAAY,EAAE,EAC/B,MAAM,GAAE,gBAA4C,EACpD,gBAAgB,CAAC,EAAE,MAAM,GACvB,YAAY,EAAE,CAchB;AAmRD,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,YAAY,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAgPpF"}
1
+ {"version":3,"file":"compact-context.d.ts","sourceRoot":"","sources":["../../../src/memory/tools/compact-context.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,EAaN,KAAK,gBAAgB,EACrB,MAAM,iDAAiD,CAAC;AACzD,OAAO,EAAc,KAAK,YAAY,EAAyB,MAAM,iCAAiC,CAAC;AAMvG,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAE7C,eAAO,MAAM,yBAAyB,oBAAoB,CAAC;AA+F3D,MAAM,WAAW,eAAe;IAC/B,wFAAwF;IACxF,SAAS,EAAE,MAAM,CAAC;IAClB,wEAAwE;IACxE,QAAQ,EAAE,MAAM,CAAC;IACjB,oFAAoF;IACpF,MAAM,EAAE,YAAY,CAAC;IACrB,sEAAsE;IACtE,OAAO,EAAE,MAAM,CAAC;IAChB,uEAAuE;IACvE,YAAY,EAAE,MAAM,CAAC;IACrB,+EAA+E;IAC/E,SAAS,EAAE,MAAM,CAAC;IAClB,oFAAoF;IACpF,oBAAoB,EAAE,MAAM,CAAC;IAC7B,wEAAwE;IACxE,kBAAkB,EAAE,MAAM,CAAC;CAC3B;AAgYD,wFAAwF;AACxF,wBAAgB,sCAAsC,CACrD,eAAe,EAAE,YAAY,EAAE,EAC/B,MAAM,GAAE,gBAA4C,EACpD,gBAAgB,CAAC,EAAE,MAAM,GACvB,YAAY,EAAE,CAMhB;AAED,MAAM,WAAW,8BAA8B;IAC9C,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,MAAM,EAAE,eAAe,GAAG,IAAI,CAAC;CAC/B;AAkBD;;;;;;;GAOG;AACH,wBAAgB,4CAA4C,CAC3D,eAAe,EAAE,YAAY,EAAE,EAC/B,MAAM,GAAE,gBAA4C,EACpD,gBAAgB,CAAC,EAAE,MAAM,EACzB,MAAM,CAAC,EAAE,eAAe,GAAG,IAAI,EAC/B,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,GAC3B,8BAA8B,CA4DhC;AA0SD,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,YAAY,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAgTpF"}
@@ -1,7 +1,8 @@
1
1
  import { Type } from "../../sdk/ai/index.js";
2
- import { compactDcpLite, DEFAULT_COMPACTION_POLICY, estimateTokens, getManualCompactionSettings, normalizeAgentDrivenOlderHistoryKeepRecentTokens, prepareCompaction, decideCompaction, } from "../../sdk/coding-agent/core/compaction/index.js";
2
+ import { compactDcpLite, CONTEXT_LIMIT_RATIO, DEFAULT_COMPACTION_POLICY, estimateTokens, getManualCompactionSettings, normalizeAgentDrivenOlderHistoryKeepRecentTokens, prepareCompaction, decideCompaction, getAutoOlderHistoryHysteresisTokens, getAutoOlderHistoryKeepRecentTokens, } from "../../sdk/coding-agent/core/compaction/index.js";
3
3
  import { defineTool } from "../../sdk/coding-agent/index.js";
4
4
  import { buildSessionContext } from "../../sdk/coding-agent/core/session-manager.js";
5
+ import { createCompactionSummaryMessage } from "../../sdk/coding-agent/core/messages.js";
5
6
  import { rawTokensSinceLastCompaction } from "../branch.js";
6
7
  import { debugLog, withDebugLogContext } from "../debug-log.js";
7
8
  export const COMPACT_CONTEXT_TOOL_NAME = "compact_context";
@@ -61,6 +62,7 @@ function getState(runtime) {
61
62
  request: null,
62
63
  turnCount: 0,
63
64
  lastAgentDrivenRequestTurn: null,
65
+ autoPruneFrozen: null,
64
66
  };
65
67
  stateByRuntime.set(runtime, state);
66
68
  }
@@ -176,12 +178,32 @@ function getAutoOlderHistoryRequestSettings(policy) {
176
178
  pinLatestUserMessage: true,
177
179
  };
178
180
  }
181
+ function getContextWindow(ctx) {
182
+ try {
183
+ const usage = ctx.getContextUsage();
184
+ if (usage && typeof usage.contextWindow === "number" && usage.contextWindow > 0) {
185
+ return usage.contextWindow;
186
+ }
187
+ }
188
+ catch {
189
+ // getContextUsage is optional in some headless harnesses.
190
+ }
191
+ const modelWindow = ctx.model?.contextWindow;
192
+ return typeof modelWindow === "number" && modelWindow > 0 ? modelWindow : undefined;
193
+ }
194
+ function getAutoRequestTimeRequestSettings(policy, contextWindow) {
195
+ const keepRecentTokens = getAutoOlderHistoryKeepRecentTokens(policy, contextWindow) ?? policy.agentDriven.olderHistoryKeepRecentTokens;
196
+ return {
197
+ phaseBoundary: false,
198
+ scope: "older_history",
199
+ keepRecentTokens,
200
+ minRawTokens: keepRecentTokens + policy.agentDriven.olderHistoryMinCompactableTokens,
201
+ pinLatestUserMessage: true,
202
+ };
203
+ }
179
204
  function isContextAtOrAboveRatio(percent, ratio) {
180
205
  return typeof percent === "number" && Number.isFinite(percent) && percent >= ratio * 100;
181
206
  }
182
- function isContextAtOrAboveTokenLimit(contextTokens, tokenLimit) {
183
- return typeof contextTokens === "number" && Number.isFinite(contextTokens) && contextTokens >= tokenLimit;
184
- }
185
207
  function hasPendingModelRequest(ctx) {
186
208
  try {
187
209
  if (!ctx.isIdle())
@@ -196,8 +218,11 @@ function hasPendingModelRequest(ctx) {
196
218
  return false;
197
219
  }
198
220
  }
199
- function decideAutoCompaction(ctx, runtime, state, policy, rawTokens, contextTokens, percent, iterationSignal = false, isMidRun = hasPendingModelRequest(ctx)) {
200
- const requestSettings = getAutoOlderHistoryRequestSettings(policy);
221
+ function decideAutoCompaction(ctx, runtime, state, policy, rawTokens, percent, iterationSignal = false, isMidRun = hasPendingModelRequest(ctx)) {
222
+ const contextWindow = getContextWindow(ctx);
223
+ const requestSettings = isMidRun
224
+ ? getAutoRequestTimeRequestSettings(policy, contextWindow)
225
+ : getAutoOlderHistoryRequestSettings(policy);
201
226
  return decideCompaction({
202
227
  source: "automatic",
203
228
  isMidRun,
@@ -206,17 +231,17 @@ function decideAutoCompaction(ctx, runtime, state, policy, rawTokens, contextTok
206
231
  pending: state.request !== null,
207
232
  cooldownActive: remainingCooldownTurns(state, policy) > 0,
208
233
  hasSufficientMaterial: rawTokens >= requestSettings.minRawTokens,
209
- contextRatioReached: isContextAtOrAboveRatio(percent, policy.autoOlderHistory.contextLimitRatio),
210
- absoluteThresholdReached: isContextAtOrAboveTokenLimit(contextTokens, runtime.config.autoCompactionContextTokens),
211
- rawThresholdReached: rawTokens >= runtime.config.compactionThresholdTokens,
234
+ contextRatioReached: isContextAtOrAboveRatio(percent, CONTEXT_LIMIT_RATIO),
212
235
  iterationReached: iterationSignal,
213
236
  });
214
237
  }
215
- function maybeScheduleAutoOlderHistoryCompaction(ctx, runtime, state, policy, rawTokens, contextTokens, percent, iterationSignal = false) {
216
- const decision = decideAutoCompaction(ctx, runtime, state, policy, rawTokens, contextTokens, percent, iterationSignal);
238
+ function maybeScheduleAutoOlderHistoryCompaction(ctx, runtime, state, policy, rawTokens, percent, iterationSignal = false) {
239
+ const decision = decideAutoCompaction(ctx, runtime, state, policy, rawTokens, percent, iterationSignal);
217
240
  if (decision.mode === "none")
218
241
  return false;
219
- const requestSettings = getAutoOlderHistoryRequestSettings(policy);
242
+ const requestSettings = decision.mode === "request_time"
243
+ ? getAutoRequestTimeRequestSettings(policy, getContextWindow(ctx))
244
+ : getAutoOlderHistoryRequestSettings(policy);
220
245
  state.lastAgentDrivenRequestTurn = state.turnCount;
221
246
  state.request = { source: "auto", mode: decision.mode, ...requestSettings };
222
247
  if (decision.mode === "request_time")
@@ -241,12 +266,27 @@ function getRequestTimeCompactionSettings(request, policy) {
241
266
  function getBranchEntries(ctx) {
242
267
  return ctx.sessionManager.getBranch();
243
268
  }
244
- function createVirtualCompactionEntry(pathEntries, result) {
269
+ function getAutoPruneSessionId(ctx) {
270
+ try {
271
+ return ctx.sessionManager.getSessionId();
272
+ }
273
+ catch {
274
+ return "unknown-session";
275
+ }
276
+ }
277
+ function autoPruneFrozenIdentityMatches(frozen, sessionId, currentMessages) {
278
+ if (frozen.sessionId !== sessionId)
279
+ return false;
280
+ return currentMessages.length > frozen.cutIndex &&
281
+ frozen.cutIndex >= 0 &&
282
+ currentMessages[frozen.cutIndex] === frozen.anchor;
283
+ }
284
+ function createVirtualCompactionEntry(pathEntries, result, timestamp) {
245
285
  return {
246
286
  type: "compaction",
247
287
  id: REQUEST_TIME_VIRTUAL_COMPACTION_ID,
248
288
  parentId: pathEntries[pathEntries.length - 1]?.id ?? null,
249
- timestamp: pathEntries[pathEntries.length - 1]?.timestamp ?? new Date().toISOString(),
289
+ timestamp: timestamp ?? pathEntries[pathEntries.length - 1]?.timestamp ?? new Date().toISOString(),
250
290
  summary: result.summary,
251
291
  firstKeptEntryId: result.firstKeptEntryId,
252
292
  tokensBefore: result.tokensBefore,
@@ -270,12 +310,55 @@ function buildRequestTimePrunedMessages(ctx, request, policy, currentMessages, o
270
310
  message,
271
311
  }));
272
312
  const fromCurrentMessages = buildRequestTimePrunedMessagesFromEntries(currentEntries, request, policy, currentMessages, onFailure);
273
- if (fromCurrentMessages)
313
+ if (fromCurrentMessages) {
314
+ if (typeof fromCurrentMessages.cutIndex === "number") {
315
+ fromCurrentMessages.anchorMessage = currentMessages[fromCurrentMessages.cutIndex];
316
+ }
274
317
  return fromCurrentMessages;
318
+ }
275
319
  return buildRequestTimePrunedMessagesFromEntries(getBranchEntries(ctx), request, policy, currentMessages, onFailure);
276
320
  }
321
+ function buildFrozenRequestTimePrunedMessages(frozen, currentMessages) {
322
+ if (frozen.cutIndex < 0 ||
323
+ currentMessages.length <= frozen.cutIndex ||
324
+ currentMessages[frozen.cutIndex] !== frozen.anchor) {
325
+ return undefined;
326
+ }
327
+ const messages = [
328
+ createCompactionSummaryMessage(frozen.summary, frozen.tokensBefore, frozen.timestamp),
329
+ ...currentMessages.slice(frozen.cutIndex),
330
+ ];
331
+ const originalTokens = rawTokensForCurrentMessages(currentMessages);
332
+ const prunedTokens = rawTokensForCurrentMessages(messages);
333
+ if (prunedTokens >= originalTokens)
334
+ return undefined;
335
+ return {
336
+ messages,
337
+ tokensRemoved: Math.max(0, originalTokens - prunedTokens),
338
+ originalTokens,
339
+ prunedTokens,
340
+ };
341
+ }
277
342
  /** Request-time DCP adapter for isolated loops: it borrows only pure policy helpers. */
278
343
  export function buildIsolatedRequestTimePrunedMessages(currentMessages, policy = DEFAULT_COMPACTION_POLICY, keepRecentTokens) {
344
+ return buildIsolatedRequestTimePrunedMessagesResult(currentMessages, policy, keepRecentTokens).messages;
345
+ }
346
+ function autoPruneHysteresisExceeded(frozen, currentMessages, policy, contextWindow) {
347
+ if (frozen.cutIndex < 0 || frozen.cutIndex >= currentMessages.length)
348
+ return true;
349
+ const tailTokens = rawTokensForCurrentMessages(currentMessages.slice(frozen.cutIndex));
350
+ const hysteresisTokens = getAutoOlderHistoryHysteresisTokens(policy, contextWindow, frozen.keepRecentTokensUsed);
351
+ return tailTokens - frozen.tailTokensAtFreeze > hysteresisTokens;
352
+ }
353
+ /**
354
+ * Stateful per-run variant of {@link buildIsolatedRequestTimePrunedMessages}.
355
+ *
356
+ * The subagent loop owns the returned `frozen` value across provider requests.
357
+ * On the first prune we freeze the deterministic summary + cut, then reuse that
358
+ * exact prefix until the kept-tail anchor reference changes or the tail grows
359
+ * past the window-relative hysteresis budget.
360
+ */
361
+ export function buildIsolatedRequestTimePrunedMessagesResult(currentMessages, policy = DEFAULT_COMPACTION_POLICY, keepRecentTokens, frozen, contextWindow) {
279
362
  const syntheticEntries = currentMessages.map((message, index) => ({
280
363
  type: "message",
281
364
  id: `isolated-${index}`,
@@ -283,18 +366,69 @@ export function buildIsolatedRequestTimePrunedMessages(currentMessages, policy =
283
366
  timestamp: new Date(index).toISOString(),
284
367
  message,
285
368
  }));
286
- return buildRequestTimePrunedMessagesFromEntries(syntheticEntries, { source: "auto", mode: "request_time", scope: "older_history", keepRecentTokens, phaseBoundary: false, minRawTokens: 0, pinLatestUserMessage: true }, policy, currentMessages)?.messages ?? currentMessages;
369
+ const resolvedKeepRecentTokens = getAutoOlderHistoryKeepRecentTokens(policy, contextWindow, keepRecentTokens);
370
+ const request = {
371
+ source: "auto",
372
+ mode: "request_time",
373
+ scope: "older_history",
374
+ keepRecentTokens: resolvedKeepRecentTokens,
375
+ phaseBoundary: false,
376
+ minRawTokens: 0,
377
+ pinLatestUserMessage: true,
378
+ };
379
+ const sessionId = "isolated-subagent-run";
380
+ if (frozen &&
381
+ frozen.keepRecentTokensUsed === resolvedKeepRecentTokens &&
382
+ autoPruneFrozenIdentityMatches(frozen, sessionId, currentMessages) &&
383
+ !autoPruneHysteresisExceeded(frozen, currentMessages, policy, contextWindow)) {
384
+ const reused = buildFrozenRequestTimePrunedMessages(frozen, currentMessages);
385
+ if (reused)
386
+ return { messages: reused.messages, frozen };
387
+ }
388
+ const pruned = buildRequestTimePrunedMessagesFromEntries(syntheticEntries, request, policy, currentMessages);
389
+ const cutIndex = pruned?.cutIndex;
390
+ const anchorMessage = typeof cutIndex === "number" ? currentMessages[cutIndex] : undefined;
391
+ if (!pruned ||
392
+ typeof cutIndex !== "number" ||
393
+ anchorMessage === undefined ||
394
+ typeof pruned.summary !== "string") {
395
+ return { messages: currentMessages, frozen: null };
396
+ }
397
+ const nextFrozen = {
398
+ sessionId,
399
+ cutIndex,
400
+ anchor: anchorMessage,
401
+ summary: pruned.summary,
402
+ tokensBefore: pruned.tokensBefore ?? 0,
403
+ timestamp: pruned.timestamp ?? new Date().toISOString(),
404
+ keepRecentTokensUsed: resolvedKeepRecentTokens ?? 0,
405
+ tailTokensAtFreeze: rawTokensForCurrentMessages(currentMessages.slice(cutIndex)),
406
+ };
407
+ return { messages: pruned.messages, frozen: nextFrozen };
287
408
  }
288
409
  function buildRequestTimePrunedMessagesFromEntries(pathEntries, request, policy, currentMessages, onFailure) {
289
410
  const settings = getRequestTimeCompactionSettings(request, policy);
290
- const preparation = prepareCompaction(pathEntries, settings, { phaseBoundary: request.phaseBoundary, pinLatestUserMessage: request.pinLatestUserMessage });
411
+ const preparation = prepareCompaction(pathEntries, settings, {
412
+ phaseBoundary: request.phaseBoundary,
413
+ pinLatestUserMessage: request.pinLatestUserMessage,
414
+ });
291
415
  if (!preparation) {
292
416
  onFailure?.("prepare_compaction_returned_undefined");
293
417
  return undefined;
294
418
  }
295
419
  const result = compactDcpLite(preparation);
420
+ const summary = result.summary;
421
+ const firstKeptEntryId = result.firstKeptEntryId;
422
+ const tokensBefore = result.tokensBefore;
423
+ const details = result.details;
296
424
  const tokensRemoved = Math.max(0, result.tokensRemoved ?? 0);
297
- const virtualCompaction = createVirtualCompactionEntry(pathEntries, result);
425
+ const virtualTimestamp = pathEntries[pathEntries.length - 1]?.timestamp ?? new Date().toISOString();
426
+ const virtualCompaction = createVirtualCompactionEntry(pathEntries, {
427
+ summary,
428
+ firstKeptEntryId,
429
+ tokensBefore,
430
+ details,
431
+ }, virtualTimestamp);
298
432
  const virtualContext = buildSessionContext([...pathEntries, virtualCompaction]);
299
433
  if (virtualContext.messages.length === 0) {
300
434
  onFailure?.("virtual_context_has_no_messages");
@@ -311,7 +445,17 @@ function buildRequestTimePrunedMessagesFromEntries(pathEntries, request, policy,
311
445
  : `pruned_context_not_smaller:${originalTokens}->${prunedTokens}`);
312
446
  return undefined;
313
447
  }
314
- return { messages: virtualContext.messages, tokensRemoved, originalTokens, prunedTokens };
448
+ const cutIndex = pathEntries.findIndex((entry) => entry.id === firstKeptEntryId);
449
+ return {
450
+ messages: virtualContext.messages,
451
+ tokensRemoved,
452
+ originalTokens,
453
+ prunedTokens,
454
+ cutIndex: cutIndex >= 0 ? cutIndex : undefined,
455
+ summary,
456
+ tokensBefore,
457
+ timestamp: virtualTimestamp,
458
+ };
315
459
  }
316
460
  function notifyDcp(ctx, message, level, telemetry) {
317
461
  const notify = ctx.ui?.notify;
@@ -499,9 +643,8 @@ export function registerCompactContextTool(ext, runtime) {
499
643
  rawTokens = 0;
500
644
  }
501
645
  const usage = ctx.getContextUsage();
502
- const contextTokens = usage?.tokens;
503
646
  const percent = usage?.percent;
504
- maybeScheduleAutoOlderHistoryCompaction(ctx, runtime, state, policy, rawTokens, contextTokens, percent);
647
+ maybeScheduleAutoOlderHistoryCompaction(ctx, runtime, state, policy, rawTokens, percent);
505
648
  }));
506
649
  ext.on("context", withDebug((_event, ctx) => {
507
650
  runtime.ensureConfig(ctx.cwd);
@@ -566,12 +709,53 @@ export function registerCompactContextTool(ext, runtime) {
566
709
  // The context hook is the last zero-lag seam before conversion/provider send.
567
710
  // Treat an explicit provider-preparation event as in-run even if the session
568
711
  // state has not flipped to streaming/pending yet.
569
- const autoSettings = getAutoOlderHistoryRequestSettings(policy);
712
+ const autoContextWindow = getContextWindow(ctx);
713
+ const autoSettings = getAutoRequestTimeRequestSettings(policy, autoContextWindow);
570
714
  const autoRawTokens = Math.max(safeRawTokensForContext(ctx), rawTokensForCurrentMessages(_event.messages));
571
715
  const autoUsage = ctx.getContextUsage();
572
716
  const autoContextTokens = autoUsage?.tokens;
573
717
  const autoPercent = autoUsage?.percent;
574
- const autoDecision = decideAutoCompaction(ctx, runtime, state, policy, autoRawTokens, autoContextTokens, autoPercent, false, isMidRun);
718
+ const autoSessionId = getAutoPruneSessionId(ctx);
719
+ // 2a. Frozen cut reuse. Once the auto path has pruned, keep reusing the
720
+ // exact summary + anchor so the provider prompt prefix stays byte
721
+ // stable across requests. Only invalidate on identity mismatch or
722
+ // when the kept tail grows past the window-relative hysteresis budget.
723
+ let frozen = state.autoPruneFrozen ?? null;
724
+ if (frozen && isMidRun) {
725
+ const identityMatches = autoPruneFrozenIdentityMatches(frozen, autoSessionId, _event.messages);
726
+ const keepRecentMatches = frozen.keepRecentTokensUsed === (autoSettings.keepRecentTokens ?? 0);
727
+ const hysteresisExceeded = autoPruneHysteresisExceeded(frozen, _event.messages, policy, autoContextWindow);
728
+ if (!identityMatches || !keepRecentMatches || hysteresisExceeded) {
729
+ state.autoPruneFrozen = null;
730
+ frozen = null;
731
+ }
732
+ else {
733
+ const frozenPruned = buildFrozenRequestTimePrunedMessages(frozen, _event.messages);
734
+ if (frozenPruned) {
735
+ debugLog("compact_context.request_time.auto_frozen_reuse", {
736
+ rawTokens: autoRawTokens,
737
+ contextTokens: autoContextTokens ?? null,
738
+ percent: autoPercent ?? null,
739
+ originalTokens: frozenPruned.originalTokens,
740
+ prunedTokens: frozenPruned.prunedTokens,
741
+ tokensRemoved: frozenPruned.tokensRemoved,
742
+ cutIndex: frozen.cutIndex,
743
+ });
744
+ notifyRequestTimeApplied(ctx, frozenPruned, { source: "auto", mode: "request_time", ...autoSettings });
745
+ return { messages: frozenPruned.messages };
746
+ }
747
+ debugLog("compact_context.request_time.auto_frozen_invalid", {
748
+ rawTokens: autoRawTokens,
749
+ contextTokens: autoContextTokens ?? null,
750
+ percent: autoPercent ?? null,
751
+ reason: "frozen_identity_or_payload_invalid",
752
+ cutIndex: frozen.cutIndex,
753
+ });
754
+ state.autoPruneFrozen = null;
755
+ frozen = null;
756
+ }
757
+ }
758
+ const autoDecision = decideAutoCompaction(ctx, runtime, state, policy, autoRawTokens, autoPercent, false, isMidRun);
575
759
  if (autoDecision.mode !== "request_time")
576
760
  return;
577
761
  const autoRequest = { source: "auto", mode: autoDecision.mode, ...autoSettings };
@@ -597,7 +781,24 @@ export function registerCompactContextTool(ext, runtime) {
597
781
  originalTokens: autoPruned.originalTokens,
598
782
  prunedTokens: autoPruned.prunedTokens,
599
783
  tokensRemoved: autoPruned.tokensRemoved,
784
+ frozen: false,
600
785
  });
786
+ const autoCutIndex = autoPruned.cutIndex;
787
+ const autoAnchorMessage = autoPruned.anchorMessage;
788
+ if (typeof autoCutIndex === "number" &&
789
+ autoAnchorMessage !== undefined &&
790
+ typeof autoPruned.summary === "string") {
791
+ state.autoPruneFrozen = {
792
+ sessionId: autoSessionId,
793
+ cutIndex: autoCutIndex,
794
+ anchor: autoAnchorMessage,
795
+ summary: autoPruned.summary,
796
+ tokensBefore: autoPruned.tokensBefore ?? 0,
797
+ timestamp: autoPruned.timestamp ?? new Date().toISOString(),
798
+ keepRecentTokensUsed: autoSettings.keepRecentTokens ?? 0,
799
+ tailTokensAtFreeze: rawTokensForCurrentMessages(_event.messages.slice(autoCutIndex)),
800
+ };
801
+ }
601
802
  notifyRequestTimeApplied(ctx, autoPruned, autoRequest);
602
803
  return { messages: autoPruned.messages };
603
804
  }));
@@ -1 +1 @@
1
- {"version":3,"file":"read-project-observations.d.ts","sourceRoot":"","sources":["../../../src/memory/tools/read-project-observations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAC7C,OAAO,EAAc,KAAK,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAGhF,eAAO,MAAM,mCAAmC,8BAA8B,CAAC;AAE/E,eAAO,MAAM,2BAA2B;;iFAqFtC,CAAC;AAEH,wBAAgB,mCAAmC,CAAC,GAAG,EAAE,YAAY,GAAG,IAAI,CAE3E"}
1
+ {"version":3,"file":"read-project-observations.d.ts","sourceRoot":"","sources":["../../../src/memory/tools/read-project-observations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAC7C,OAAO,EAAc,KAAK,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAGhF,eAAO,MAAM,mCAAmC,8BAA8B,CAAC;AAE/E,eAAO,MAAM,2BAA2B;;iFAwFtC,CAAC;AAEH,wBAAgB,mCAAmC,CAAC,GAAG,EAAE,YAAY,GAAG,IAAI,CAE3E"}
@@ -5,13 +5,16 @@ export const READ_PROJECT_OBSERVATIONS_TOOL_NAME = "read_project_observations";
5
5
  export const readProjectObservationsTool = defineTool({
6
6
  name: READ_PROJECT_OBSERVATIONS_TOOL_NAME,
7
7
  label: "Search project observations",
8
- description: "Search all past observations across ALL sessions in this project. " +
8
+ description: "Search past observations across sessions in this project, stored per-machine " +
9
+ "and per-working-directory (cwd). " +
9
10
  "Use this when you need to recall project conventions, past decisions, " +
10
11
  "file locations, or user preferences before editing or creating files. " +
11
12
  "Uses simple substring matching — be specific with your query terms. " +
12
- "Returns up to 20 most recent matching observations with relevance tags.",
13
- promptSnippet: "Use read_project_observations(<query>) to search past observations across ALL sessions in this project.",
13
+ "Returns up to 20 most recent matching observations with relevance tags. " +
14
+ "Results are local to this machine and cwd; they are NOT synced across machines or teams.",
15
+ promptSnippet: "Use read_project_observations(<query>) to search past observations across sessions in this local project working directory.",
14
16
  promptGuidelines: [
17
+ "Results are per-machine + per-cwd and cross-session; they are NOT shared across machines or teams.",
15
18
  "Use read_project_observations before creating new files to find conventions for that file kind.",
16
19
  "Use read_project_observations before editing a file to find past context about that file or its directory.",
17
20
  "Be specific: search for file paths, technology names, or pattern keywords.",
@@ -1 +1 @@
1
- {"version":3,"file":"recall-observation.d.ts","sourceRoot":"","sources":["../../../src/memory/tools/recall-observation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAE7C,OAAO,EAAc,KAAK,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAEhF,OAAO,EAGN,KAAK,uBAAuB,EAE5B,MAAM,cAAc,CAAC;AAGtB,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEvE,eAAO,MAAM,4BAA4B,WAAW,CAAC;AAIrD,KAAK,2BAA2B,GAC7B,IAAI,GACJ,SAAS,GACT,YAAY,GACZ,WAAW,GACX,WAAW,GACX,oBAAoB,GACpB,eAAe,CAAC;AAEnB,KAAK,kBAAkB,GAAG,IAAI,CAAC,iBAAiB,EAAE,IAAI,GAAG,SAAS,GAAG,WAAW,GAAG,WAAW,CAAC,CAAC;AAChG,KAAK,iBAAiB,GAAG,IAAI,CAAC,gBAAgB,EAAE,IAAI,GAAG,SAAS,GAAG,0BAA0B,GAAG,QAAQ,CAAC,GAAG;IAAE,eAAe,EAAE,MAAM,CAAA;CAAE,CAAC;AAExI,MAAM,MAAM,wBAAwB,GAAG;IACtC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,KAAK,6BAA6B,GAAG;IACpC,MAAM,EAAE,uBAAuB,CAAC,QAAQ,CAAC,CAAC;IAC1C,kBAAkB,EAAE,MAAM,CAAC;IAC3B,sBAAsB,EAAE,MAAM,CAAC;IAC/B,WAAW,EAAE,kBAAkB,CAAC;IAChC,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,aAAa,CAAC,EAAE,wBAAwB,EAAE,CAAC;IAC3C,qBAAqB,CAAC,EAAE,MAAM,EAAE,CAAC;IACjC,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC9B,CAAC;AAEF,KAAK,6CAA6C,GAAG;IACpD,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,KAAK,4CAA4C,GAAG;IACnD,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,QAAQ,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG;IAC1C,MAAM,EAAE,2BAA2B,CAAC;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,iBAAiB,EAAE,CAAC;IACjC,wBAAwB,EAAE,6BAA6B,EAAE,CAAC;IAC1D,YAAY,EAAE,6BAA6B,EAAE,CAAC;IAC9C,OAAO,EAAE,6BAA6B,EAAE,CAAC;IACzC,aAAa,EAAE,wBAAwB,EAAE,CAAC;IAC1C,iCAAiC,EAAE,6CAA6C,EAAE,CAAC;IACnF,+BAA+B,EAAE,4CAA4C,EAAE,CAAC;IAChF,qBAAqB,EAAE,MAAM,EAAE,CAAC;IAChC,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAuUF,eAAO,MAAM,qBAAqB;;iFAuChC,CAAC;AAEH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,YAAY,GAAG,IAAI,CAE1D"}
1
+ {"version":3,"file":"recall-observation.d.ts","sourceRoot":"","sources":["../../../src/memory/tools/recall-observation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAE7C,OAAO,EAAc,KAAK,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAEhF,OAAO,EAGN,KAAK,uBAAuB,EAE5B,MAAM,cAAc,CAAC;AAItB,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEvE,eAAO,MAAM,4BAA4B,WAAW,CAAC;AAIrD,KAAK,2BAA2B,GAC7B,IAAI,GACJ,SAAS,GACT,YAAY,GACZ,WAAW,GACX,WAAW,GACX,oBAAoB,GACpB,eAAe,CAAC;AAEnB,KAAK,kBAAkB,GAAG,IAAI,CAAC,iBAAiB,EAAE,IAAI,GAAG,SAAS,GAAG,WAAW,GAAG,WAAW,CAAC,CAAC;AAChG,KAAK,iBAAiB,GAAG,IAAI,CAAC,gBAAgB,EAAE,IAAI,GAAG,SAAS,GAAG,0BAA0B,GAAG,QAAQ,CAAC,GAAG;IAAE,eAAe,EAAE,MAAM,CAAA;CAAE,CAAC;AAExI,MAAM,MAAM,wBAAwB,GAAG;IACtC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,KAAK,6BAA6B,GAAG;IACpC,MAAM,EAAE,uBAAuB,CAAC,QAAQ,CAAC,CAAC;IAC1C,kBAAkB,EAAE,MAAM,CAAC;IAC3B,sBAAsB,EAAE,MAAM,CAAC;IAC/B,WAAW,EAAE,kBAAkB,CAAC;IAChC,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,aAAa,CAAC,EAAE,wBAAwB,EAAE,CAAC;IAC3C,qBAAqB,CAAC,EAAE,MAAM,EAAE,CAAC;IACjC,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC9B,CAAC;AAEF,KAAK,6CAA6C,GAAG;IACpD,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,KAAK,4CAA4C,GAAG;IACnD,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,QAAQ,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG;IAC1C,MAAM,EAAE,2BAA2B,CAAC;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,iBAAiB,EAAE,CAAC;IACjC,wBAAwB,EAAE,6BAA6B,EAAE,CAAC;IAC1D,YAAY,EAAE,6BAA6B,EAAE,CAAC;IAC9C,OAAO,EAAE,6BAA6B,EAAE,CAAC;IACzC,aAAa,EAAE,wBAAwB,EAAE,CAAC;IAC1C,iCAAiC,EAAE,6CAA6C,EAAE,CAAC;IACnF,+BAA+B,EAAE,4CAA4C,EAAE,CAAC;IAChF,qBAAqB,EAAE,MAAM,EAAE,CAAC;IAChC,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAuUF,eAAO,MAAM,qBAAqB;;iFAqDhC,CAAC;AAEH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,YAAY,GAAG,IAAI,CAE1D"}
@@ -1,6 +1,7 @@
1
1
  import { Type } from "../../sdk/ai/index.js";
2
2
  import { defineTool } from "../../sdk/coding-agent/index.js";
3
3
  import { recallMemorySources, } from "../branch.js";
4
+ import { getProjectObsStore } from "../project-observations-store.js";
4
5
  import { renderRecallSourceEntries, renderRecallSourceEntry } from "../serialize.js";
5
6
  import { estimateEntryTokens } from "../tokens.js";
6
7
  export const RECALL_OBSERVATION_TOOL_NAME = "recall";
@@ -315,7 +316,8 @@ function renderFoundResult(result) {
315
316
  export const recallObservationTool = defineTool({
316
317
  name: RECALL_OBSERVATION_TOOL_NAME,
317
318
  label: "Recall memory evidence",
318
- description: "Recover exact evidence and source context behind a compacted observational-memory observation or reflection id on the current branch. " +
319
+ description: "Recover exact evidence and source context behind a compacted observational-memory observation or reflection id " +
320
+ "from the current branch or an earlier session in the same working directory. " +
319
321
  "Use when compressed memory is important and original source context is needed before acting.",
320
322
  promptSnippet: "Use recall(<id>) to recover exact source context behind compacted memory observations/reflections when precision matters.",
321
323
  promptGuidelines: [
@@ -340,10 +342,22 @@ export const recallObservationTool = defineTool({
340
342
  return textResult(message, emptyDetails("invalid_id", memoryId, message));
341
343
  }
342
344
  const branchEntries = ctx.sessionManager.getBranch();
343
- const result = recallMemorySources(branchEntries, memoryId);
345
+ let result = recallMemorySources(branchEntries, memoryId);
344
346
  if (result.status === "not_found") {
345
- const message = `No observation or reflection with id ${memoryId} was found on the current branch.`;
346
- return textResult(message, emptyDetails("not_found", memoryId, message));
347
+ const store = getProjectObsStore();
348
+ const projectId = store?.getProjectByCwd(ctx.cwd);
349
+ const archiveEntries = projectId ? store?.getProjectRecallSource?.(projectId, memoryId) : null;
350
+ if (archiveEntries && archiveEntries.length > 0) {
351
+ const archivedResult = recallMemorySources(archiveEntries, memoryId);
352
+ if (archivedResult.status === "found") {
353
+ result = archivedResult;
354
+ }
355
+ }
356
+ if (result.status === "not_found") {
357
+ const message = `No observation or reflection with id ${memoryId} was found on the current branch ` +
358
+ `or in this project's cross-session memory.`;
359
+ return textResult(message, emptyDetails("not_found", memoryId, message));
360
+ }
347
361
  }
348
362
  return renderFoundResult(result);
349
363
  },
@@ -1 +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"}
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;;;iFAqEvC,CAAC;AAEH,wBAAgB,oCAAoC,CAAC,GAAG,EAAE,YAAY,GAAG,IAAI,CAE5E"}
@@ -6,10 +6,12 @@ export const RECEIVE_AGENT_OBSERVATIONS_TOOL_NAME = "receive_agent_observations"
6
6
  export const receiveAgentObservationsTool = defineTool({
7
7
  name: RECEIVE_AGENT_OBSERVATIONS_TOOL_NAME,
8
8
  label: "Receive observations from other agents",
9
- description: "Poll for observations shared by other active sessions on this project. " +
9
+ description: "Poll for observations shared by other active sessions on this machine working in the " +
10
+ "same directory (same-machine, same-cwd; no cross-machine or team relay). " +
10
11
  "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
+ promptSnippet: "Use receive_agent_observations() to pull shared observations from other active sessions on this machine.",
12
13
  promptGuidelines: [
14
+ "Messages are same-machine + same-cwd only; there is no cross-machine or team fan-out.",
13
15
  "Call at the start of a session or task to pick up context shared by collaborators.",
14
16
  "Deduplicate results by memory_id against observations already in your context.",
15
17
  ],
@@ -1 +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"}
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;;iFAuEtC,CAAC;AAEH,wBAAgB,mCAAmC,CAAC,GAAG,EAAE,YAAY,GAAG,IAAI,CAE3E"}
@@ -40,12 +40,14 @@ function findObservationInStore(store, projectId, memoryId) {
40
40
  export const shareProjectObservationTool = defineTool({
41
41
  name: SHARE_PROJECT_OBSERVATION_TOOL_NAME,
42
42
  label: "Share observation with other agents",
43
- description: "Broadcast a project observation or reflection to other active sessions working on this project. " +
43
+ description: "Share a project observation or reflection with other active sessions on this machine " +
44
+ "working in the same directory (same-machine, same-cwd broadcast — no cross-machine or team relay). " +
44
45
  "Use this when you discover a fact another agent should know immediately, " +
45
46
  "rather than waiting for them to search project memory.",
46
- promptSnippet: "Use share_project_observation(memory_id) to push a known observation to other active sessions.",
47
+ promptSnippet: "Use share_project_observation(memory_id) to push a known observation to other active sessions on this machine.",
47
48
  promptGuidelines: [
48
- "Only share observations that are relevant to collaborators on the same project.",
49
+ "Delivery is same-machine + same-cwd only; there is no cross-machine or team fan-out.",
50
+ "Only share observations that are relevant to collaborators in the same local project directory.",
49
51
  "Prefer sharing high/critical relevance facts — avoid noise.",
50
52
  "The memory_id must be a 12-character lowercase hex id from the current branch or project memory.",
51
53
  ],
@@ -1 +1 @@
1
- {"version":3,"file":"write-project-observation.d.ts","sourceRoot":"","sources":["../../../src/memory/tools/write-project-observation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAC7C,OAAO,EAAc,KAAK,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAIhF,eAAO,MAAM,mCAAmC,8BAA8B,CAAC;AAE/E,eAAO,MAAM,2BAA2B;;;iFA6DtC,CAAC;AAEH,wBAAgB,mCAAmC,CAAC,GAAG,EAAE,YAAY,GAAG,IAAI,CAE3E"}
1
+ {"version":3,"file":"write-project-observation.d.ts","sourceRoot":"","sources":["../../../src/memory/tools/write-project-observation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAC7C,OAAO,EAAc,KAAK,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAIhF,eAAO,MAAM,mCAAmC,8BAA8B,CAAC;AAE/E,eAAO,MAAM,2BAA2B;;;iFA2EtC,CAAC;AAEH,wBAAgB,mCAAmC,CAAC,GAAG,EAAE,YAAY,GAAG,IAAI,CAE3E"}