@zelari/core 2.10.0 → 2.11.1

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 (121) hide show
  1. package/dist/context/ContextPolicy.d.ts +48 -0
  2. package/dist/context/ContextPolicy.d.ts.map +1 -0
  3. package/dist/context/ContextPolicy.js +71 -0
  4. package/dist/context/ContextPolicy.js.map +1 -0
  5. package/dist/context/ContextProjector.d.ts +44 -0
  6. package/dist/context/ContextProjector.d.ts.map +1 -0
  7. package/dist/context/ContextProjector.js +156 -0
  8. package/dist/context/ContextProjector.js.map +1 -0
  9. package/dist/context/index.d.ts +11 -0
  10. package/dist/context/index.d.ts.map +1 -0
  11. package/dist/context/index.js +11 -0
  12. package/dist/context/index.js.map +1 -0
  13. package/dist/context/parentContext.d.ts +39 -0
  14. package/dist/context/parentContext.d.ts.map +1 -0
  15. package/dist/context/parentContext.js +67 -0
  16. package/dist/context/parentContext.js.map +1 -0
  17. package/dist/core/AgentHarness.d.ts +38 -0
  18. package/dist/core/AgentHarness.d.ts.map +1 -1
  19. package/dist/core/AgentHarness.js +142 -0
  20. package/dist/core/AgentHarness.js.map +1 -1
  21. package/dist/index.d.ts +1 -0
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +1 -0
  24. package/dist/index.js.map +1 -1
  25. package/dist/runtime/controls/RuntimeControlQueue.d.ts +35 -0
  26. package/dist/runtime/controls/RuntimeControlQueue.d.ts.map +1 -0
  27. package/dist/runtime/controls/RuntimeControlQueue.js +54 -0
  28. package/dist/runtime/controls/RuntimeControlQueue.js.map +1 -0
  29. package/dist/runtime/controls/SteeringObserver.d.ts +24 -0
  30. package/dist/runtime/controls/SteeringObserver.d.ts.map +1 -0
  31. package/dist/runtime/controls/SteeringObserver.js +46 -0
  32. package/dist/runtime/controls/SteeringObserver.js.map +1 -0
  33. package/dist/runtime/controls/index.d.ts +4 -0
  34. package/dist/runtime/controls/index.d.ts.map +1 -0
  35. package/dist/runtime/controls/index.js +4 -0
  36. package/dist/runtime/controls/index.js.map +1 -0
  37. package/dist/runtime/controls/types.d.ts +43 -0
  38. package/dist/runtime/controls/types.d.ts.map +1 -0
  39. package/dist/runtime/controls/types.js +18 -0
  40. package/dist/runtime/controls/types.js.map +1 -0
  41. package/dist/runtime/guards/DuplicateSearchGuard.d.ts +41 -0
  42. package/dist/runtime/guards/DuplicateSearchGuard.d.ts.map +1 -0
  43. package/dist/runtime/guards/DuplicateSearchGuard.js +150 -0
  44. package/dist/runtime/guards/DuplicateSearchGuard.js.map +1 -0
  45. package/dist/runtime/guards/FailureSignatureGuard.d.ts +34 -0
  46. package/dist/runtime/guards/FailureSignatureGuard.d.ts.map +1 -0
  47. package/dist/runtime/guards/FailureSignatureGuard.js +143 -0
  48. package/dist/runtime/guards/FailureSignatureGuard.js.map +1 -0
  49. package/dist/runtime/guards/NoProgressGuard.d.ts +23 -0
  50. package/dist/runtime/guards/NoProgressGuard.d.ts.map +1 -0
  51. package/dist/runtime/guards/NoProgressGuard.js +84 -0
  52. package/dist/runtime/guards/NoProgressGuard.js.map +1 -0
  53. package/dist/runtime/guards/ReasoningWatchdog.d.ts +48 -0
  54. package/dist/runtime/guards/ReasoningWatchdog.d.ts.map +1 -0
  55. package/dist/runtime/guards/ReasoningWatchdog.js +135 -0
  56. package/dist/runtime/guards/ReasoningWatchdog.js.map +1 -0
  57. package/dist/runtime/guards/RepetitionGuard.d.ts +22 -0
  58. package/dist/runtime/guards/RepetitionGuard.d.ts.map +1 -0
  59. package/dist/runtime/guards/RepetitionGuard.js +56 -0
  60. package/dist/runtime/guards/RepetitionGuard.js.map +1 -0
  61. package/dist/runtime/guards/index.d.ts +6 -0
  62. package/dist/runtime/guards/index.d.ts.map +1 -0
  63. package/dist/runtime/guards/index.js +6 -0
  64. package/dist/runtime/guards/index.js.map +1 -0
  65. package/dist/runtime/index.d.ts +4 -0
  66. package/dist/runtime/index.d.ts.map +1 -1
  67. package/dist/runtime/index.js +4 -0
  68. package/dist/runtime/index.js.map +1 -1
  69. package/dist/runtime/observers/MetricsObserver.d.ts +42 -0
  70. package/dist/runtime/observers/MetricsObserver.d.ts.map +1 -0
  71. package/dist/runtime/observers/MetricsObserver.js +110 -0
  72. package/dist/runtime/observers/MetricsObserver.js.map +1 -0
  73. package/dist/runtime/observers/ObserverBus.d.ts +49 -0
  74. package/dist/runtime/observers/ObserverBus.d.ts.map +1 -0
  75. package/dist/runtime/observers/ObserverBus.js +160 -0
  76. package/dist/runtime/observers/ObserverBus.js.map +1 -0
  77. package/dist/runtime/observers/TraceObserver.d.ts +36 -0
  78. package/dist/runtime/observers/TraceObserver.d.ts.map +1 -0
  79. package/dist/runtime/observers/TraceObserver.js +78 -0
  80. package/dist/runtime/observers/TraceObserver.js.map +1 -0
  81. package/dist/runtime/observers/composeObservers.d.ts +10 -0
  82. package/dist/runtime/observers/composeObservers.d.ts.map +1 -0
  83. package/dist/runtime/observers/composeObservers.js +30 -0
  84. package/dist/runtime/observers/composeObservers.js.map +1 -0
  85. package/dist/runtime/observers/index.d.ts +7 -0
  86. package/dist/runtime/observers/index.d.ts.map +1 -0
  87. package/dist/runtime/observers/index.js +7 -0
  88. package/dist/runtime/observers/index.js.map +1 -0
  89. package/dist/runtime/observers/resolve.d.ts +3 -0
  90. package/dist/runtime/observers/resolve.d.ts.map +1 -0
  91. package/dist/runtime/observers/resolve.js +30 -0
  92. package/dist/runtime/observers/resolve.js.map +1 -0
  93. package/dist/runtime/observers/types.d.ts +113 -0
  94. package/dist/runtime/observers/types.d.ts.map +1 -0
  95. package/dist/runtime/observers/types.js +13 -0
  96. package/dist/runtime/observers/types.js.map +1 -0
  97. package/dist/runtime/recorder/Redactor.d.ts +19 -0
  98. package/dist/runtime/recorder/Redactor.d.ts.map +1 -0
  99. package/dist/runtime/recorder/Redactor.js +78 -0
  100. package/dist/runtime/recorder/Redactor.js.map +1 -0
  101. package/dist/runtime/recorder/RunRecorder.d.ts +73 -0
  102. package/dist/runtime/recorder/RunRecorder.d.ts.map +1 -0
  103. package/dist/runtime/recorder/RunRecorder.js +137 -0
  104. package/dist/runtime/recorder/RunRecorder.js.map +1 -0
  105. package/dist/runtime/recorder/index.d.ts +8 -0
  106. package/dist/runtime/recorder/index.d.ts.map +1 -0
  107. package/dist/runtime/recorder/index.js +8 -0
  108. package/dist/runtime/recorder/index.js.map +1 -0
  109. package/dist/runtime/recorder/recorderObserver.d.ts +4 -0
  110. package/dist/runtime/recorder/recorderObserver.d.ts.map +1 -0
  111. package/dist/runtime/recorder/recorderObserver.js +63 -0
  112. package/dist/runtime/recorder/recorderObserver.js.map +1 -0
  113. package/dist/runtime/recorder/retention.d.ts +15 -0
  114. package/dist/runtime/recorder/retention.d.ts.map +1 -0
  115. package/dist/runtime/recorder/retention.js +105 -0
  116. package/dist/runtime/recorder/retention.js.map +1 -0
  117. package/dist/session/taskContract.d.ts +2 -2
  118. package/dist/shared/events.d.ts +40 -2
  119. package/dist/shared/events.d.ts.map +1 -1
  120. package/dist/shared/events.js.map +1 -1
  121. package/package.json +5 -1
@@ -0,0 +1,46 @@
1
+ /**
2
+ * SteeringObserver — drains pending steers at each safe turn boundary and
3
+ * injects them as one user message (Frontier PHASE 2, §26–27).
4
+ *
5
+ * Boundary semantics (§21): this hook runs after tool results are flushed and
6
+ * the assistant turn is sealed, before the next provider call. The in-flight
7
+ * provider request is never mutated. Follow-ups are NOT drained here: they
8
+ * become the next user task after the current run ends (§23.2), which is the
9
+ * caller's decision, not the observer's.
10
+ */
11
+ import { CONTINUE } from '../observers/types.js';
12
+ /**
13
+ * Render steers verbatim and numbered — never compacted automatically, later
14
+ * instructions may supersede earlier ones (§27).
15
+ */
16
+ export function renderSteers(steers) {
17
+ const lines = ['Runtime user steering received during execution:', ''];
18
+ steers.forEach((steer, index) => {
19
+ lines.push(`[${index + 1}]`);
20
+ lines.push(steer.text);
21
+ lines.push('');
22
+ });
23
+ lines.push('Later instructions may supersede earlier ones.');
24
+ return lines.join('\n');
25
+ }
26
+ export class SteeringObserver {
27
+ queue;
28
+ constructor(queue) {
29
+ this.queue = queue;
30
+ }
31
+ async onTurnEnd(_event) {
32
+ const steers = this.queue.drainSteers();
33
+ if (steers.length === 0) {
34
+ return CONTINUE;
35
+ }
36
+ return {
37
+ action: 'inject',
38
+ message: {
39
+ role: 'user',
40
+ kind: 'runtime-steer',
41
+ content: renderSteers(steers),
42
+ },
43
+ };
44
+ }
45
+ }
46
+ //# sourceMappingURL=SteeringObserver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SteeringObserver.js","sourceRoot":"","sources":["../../../src/runtime/controls/SteeringObserver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,QAAQ,EAA8D,MAAM,uBAAuB,CAAC;AAI7G;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,MAA2B;IACtD,MAAM,KAAK,GAAG,CAAC,kDAAkD,EAAE,EAAE,CAAC,CAAC;IACvE,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;QAC9B,KAAK,CAAC,IAAI,CAAC,IAAI,KAAK,GAAG,CAAC,GAAG,CAAC,CAAC;QAC7B,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACvB,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjB,CAAC,CAAC,CAAC;IACH,KAAK,CAAC,IAAI,CAAC,gDAAgD,CAAC,CAAC;IAC7D,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,MAAM,OAAO,gBAAgB;IACE;IAA7B,YAA6B,KAA0B;QAA1B,UAAK,GAAL,KAAK,CAAqB;IAAG,CAAC;IAE3D,KAAK,CAAC,SAAS,CAAC,MAAoB;QAClC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;QACxC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACxB,OAAO,QAAQ,CAAC;QAClB,CAAC;QACD,OAAO;YACL,MAAM,EAAE,QAAQ;YAChB,OAAO,EAAE;gBACP,IAAI,EAAE,MAAM;gBACZ,IAAI,EAAE,eAAe;gBACrB,OAAO,EAAE,YAAY,CAAC,MAAM,CAAC;aAC9B;SACF,CAAC;IACJ,CAAC;CACF"}
@@ -0,0 +1,4 @@
1
+ export * from './types.js';
2
+ export * from './RuntimeControlQueue.js';
3
+ export * from './SteeringObserver.js';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/runtime/controls/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,0BAA0B,CAAC;AACzC,cAAc,uBAAuB,CAAC"}
@@ -0,0 +1,4 @@
1
+ export * from './types.js';
2
+ export * from './RuntimeControlQueue.js';
3
+ export * from './SteeringObserver.js';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/runtime/controls/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,0BAA0B,CAAC;AACzC,cAAc,uBAAuB,CAAC"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Runtime control plane — ControlEvent contracts (Frontier upgrade, PHASE 2).
3
+ *
4
+ * Typed events an external controller (headless stdin bridge, Desktop, tests)
5
+ * can enqueue while a run is in flight. Steers/follow-ups are consumed by the
6
+ * SteeringObserver at safe turn boundaries; cancel/pause/resume are carried
7
+ * as data so higher layers can map them onto cooperative cancellation.
8
+ */
9
+ export interface SteerControlEvent {
10
+ type: 'steer';
11
+ id: string;
12
+ text: string;
13
+ ts: number;
14
+ /** Reserved for future per-agent targeting; v1 always lands on the lead. */
15
+ target?: 'lead';
16
+ }
17
+ export interface FollowUpControlEvent {
18
+ type: 'follow_up';
19
+ id: string;
20
+ text: string;
21
+ ts: number;
22
+ }
23
+ export interface CancelControlEvent {
24
+ type: 'cancel';
25
+ id: string;
26
+ reason?: string;
27
+ ts: number;
28
+ }
29
+ export interface PauseControlEvent {
30
+ type: 'pause';
31
+ id: string;
32
+ ts: number;
33
+ }
34
+ export interface ResumeControlEvent {
35
+ type: 'resume';
36
+ id: string;
37
+ ts: number;
38
+ }
39
+ export type ControlEvent = SteerControlEvent | FollowUpControlEvent | CancelControlEvent | PauseControlEvent | ResumeControlEvent;
40
+ export declare function isSteerControlEvent(event: ControlEvent): event is SteerControlEvent;
41
+ export declare function isFollowUpControlEvent(event: ControlEvent): event is FollowUpControlEvent;
42
+ export declare function isCancelControlEvent(event: ControlEvent): event is CancelControlEvent;
43
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/runtime/controls/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,OAAO,CAAC;IACd,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,4EAA4E;IAC5E,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,WAAW,CAAC;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,QAAQ,CAAC;IACf,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,OAAO,CAAC;IACd,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,QAAQ,CAAC;IACf,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,MAAM,YAAY,GACpB,iBAAiB,GACjB,oBAAoB,GACpB,kBAAkB,GAClB,iBAAiB,GACjB,kBAAkB,CAAC;AAEvB,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,YAAY,GAClB,KAAK,IAAI,iBAAiB,CAE5B;AAED,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,YAAY,GAClB,KAAK,IAAI,oBAAoB,CAE/B;AAED,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,YAAY,GAClB,KAAK,IAAI,kBAAkB,CAE7B"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Runtime control plane — ControlEvent contracts (Frontier upgrade, PHASE 2).
3
+ *
4
+ * Typed events an external controller (headless stdin bridge, Desktop, tests)
5
+ * can enqueue while a run is in flight. Steers/follow-ups are consumed by the
6
+ * SteeringObserver at safe turn boundaries; cancel/pause/resume are carried
7
+ * as data so higher layers can map them onto cooperative cancellation.
8
+ */
9
+ export function isSteerControlEvent(event) {
10
+ return event.type === 'steer';
11
+ }
12
+ export function isFollowUpControlEvent(event) {
13
+ return event.type === 'follow_up';
14
+ }
15
+ export function isCancelControlEvent(event) {
16
+ return event.type === 'cancel';
17
+ }
18
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/runtime/controls/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AA4CH,MAAM,UAAU,mBAAmB,CACjC,KAAmB;IAEnB,OAAO,KAAK,CAAC,IAAI,KAAK,OAAO,CAAC;AAChC,CAAC;AAED,MAAM,UAAU,sBAAsB,CACpC,KAAmB;IAEnB,OAAO,KAAK,CAAC,IAAI,KAAK,WAAW,CAAC;AACpC,CAAC;AAED,MAAM,UAAU,oBAAoB,CAClC,KAAmB;IAEnB,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC;AACjC,CAAC"}
@@ -0,0 +1,41 @@
1
+ import type { AgentObserver, ObserverResult, ToolCallEvent } from '../observers/types.js';
2
+ /** Tools whose primary argument is a search query / target path. */
3
+ export declare const SEARCH_TOOLS: ReadonlySet<string>;
4
+ export declare function isSearchTool(tool: string): boolean;
5
+ /** Lowercase, strip punctuation noise, collapse whitespace, cap length. */
6
+ export declare function normalizeQuery(text: string): string;
7
+ /** Jaccard similarity of the token sets of two normalized queries. */
8
+ export declare function queryJaccard(a: string, b: string): number;
9
+ export interface SearchQueryRef {
10
+ /** Same-scope bucket: duplicates are only counted within a scope. */
11
+ scope: string;
12
+ /** Normalized query text compared for near-duplicates. */
13
+ query: string;
14
+ }
15
+ /** Extract the comparable (scope, query) pair for a search-family call. */
16
+ export declare function extractSearchQuery(toolName: string, args: unknown): SearchQueryRef | undefined;
17
+ export interface DuplicateSearchGuardConfig {
18
+ /** Inject a refine message at this many near-duplicate calls (default 2). */
19
+ warnAfter?: number;
20
+ /** Stop the run at this many near-duplicate calls (default 5). */
21
+ stopAfter?: number;
22
+ /** Token-set similarity above which two queries are duplicates (0–1, default 0.8). */
23
+ similarityThreshold?: number;
24
+ /** Recent queries kept per scope bucket for comparison (default 8). */
25
+ window?: number;
26
+ /** Maximum scope buckets retained (FIFO eviction, default 256). */
27
+ maxBuckets?: number;
28
+ }
29
+ export declare class DuplicateSearchGuard implements AgentObserver {
30
+ private readonly buckets;
31
+ private readonly warnAfter;
32
+ private readonly stopAfter;
33
+ private readonly similarityThreshold;
34
+ private readonly window;
35
+ private readonly maxBuckets;
36
+ constructor(config?: DuplicateSearchGuardConfig);
37
+ onToolCall(event: ToolCallEvent): Promise<ObserverResult>;
38
+ reset(): void;
39
+ private evictIfNeeded;
40
+ }
41
+ //# sourceMappingURL=DuplicateSearchGuard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DuplicateSearchGuard.d.ts","sourceRoot":"","sources":["../../../src/runtime/guards/DuplicateSearchGuard.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EACV,aAAa,EACb,cAAc,EACd,aAAa,EACd,MAAM,uBAAuB,CAAC;AAE/B,oEAAoE;AACpE,eAAO,MAAM,YAAY,EAAE,WAAW,CAAC,MAAM,CAK3C,CAAC;AAEH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAElD;AAED,2EAA2E;AAC3E,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOnD;AAED,sEAAsE;AACtE,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CASzD;AAED,MAAM,WAAW,cAAc;IAC7B,qEAAqE;IACrE,KAAK,EAAE,MAAM,CAAC;IACd,0DAA0D;IAC1D,KAAK,EAAE,MAAM,CAAC;CACf;AAED,2EAA2E;AAC3E,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,OAAO,GACZ,cAAc,GAAG,SAAS,CAgC5B;AAED,MAAM,WAAW,0BAA0B;IACzC,6EAA6E;IAC7E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kEAAkE;IAClE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sFAAsF;IACtF,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,uEAAuE;IACvE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,mEAAmE;IACnE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAYD,qBAAa,oBAAqB,YAAW,aAAa;IACxD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAkC;IAC1D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAS;IAC7C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;gBAExB,MAAM,GAAE,0BAA+B;IAQ7C,UAAU,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,cAAc,CAAC;IAwC/D,KAAK,IAAI,IAAI;IAIb,OAAO,CAAC,aAAa;CAOtB"}
@@ -0,0 +1,150 @@
1
+ /**
2
+ * DuplicateSearchGuard — detect repeated or near-identical search queries
3
+ * (Frontier upgrade, PHASE 1B §16).
4
+ *
5
+ * RepetitionGuard catches *identical* calls of any tool; this guard adds
6
+ * query-level awareness for the search family: the same query — or a
7
+ * cosmetically different one — in the same scope counts as a duplicate even
8
+ * when irrelevant args (limits, depth, key order) differ.
9
+ */
10
+ import { CONTINUE } from '../observers/types.js';
11
+ /** Tools whose primary argument is a search query / target path. */
12
+ export const SEARCH_TOOLS = new Set([
13
+ 'grep_content',
14
+ 'semantic_search',
15
+ 'web_search',
16
+ 'list_files',
17
+ ]);
18
+ export function isSearchTool(tool) {
19
+ return SEARCH_TOOLS.has(tool);
20
+ }
21
+ /** Lowercase, strip punctuation noise, collapse whitespace, cap length. */
22
+ export function normalizeQuery(text) {
23
+ return text
24
+ .toLowerCase()
25
+ .replace(/[.,;:!?()"'`—–…]+/g, ' ')
26
+ .replace(/\s+/g, ' ')
27
+ .trim()
28
+ .slice(0, 512);
29
+ }
30
+ /** Jaccard similarity of the token sets of two normalized queries. */
31
+ export function queryJaccard(a, b) {
32
+ const ta = new Set(a.split(' ').filter(Boolean));
33
+ const tb = new Set(b.split(' ').filter(Boolean));
34
+ if (ta.size === 0 && tb.size === 0)
35
+ return 1;
36
+ if (ta.size === 0 || tb.size === 0)
37
+ return 0;
38
+ let intersection = 0;
39
+ for (const token of ta)
40
+ if (tb.has(token))
41
+ intersection++;
42
+ const union = ta.size + tb.size - intersection;
43
+ return union === 0 ? 0 : intersection / union;
44
+ }
45
+ /** Extract the comparable (scope, query) pair for a search-family call. */
46
+ export function extractSearchQuery(toolName, args) {
47
+ if (!isSearchTool(toolName))
48
+ return undefined;
49
+ const a = (args ?? {});
50
+ switch (toolName) {
51
+ case 'grep_content': {
52
+ if (typeof a.pattern !== 'string' || a.pattern.trim() === '')
53
+ return undefined;
54
+ const include = Array.isArray(a.include)
55
+ ? a.include.join(',')
56
+ : typeof a.include === 'string'
57
+ ? a.include
58
+ : '';
59
+ return {
60
+ scope: [
61
+ typeof a.path === 'string' ? a.path : '',
62
+ include,
63
+ ].join('\u0000'),
64
+ query: normalizeQuery(a.pattern),
65
+ };
66
+ }
67
+ case 'semantic_search':
68
+ case 'web_search': {
69
+ if (typeof a.query !== 'string' || a.query.trim() === '')
70
+ return undefined;
71
+ return { scope: '', query: normalizeQuery(a.query) };
72
+ }
73
+ case 'list_files': {
74
+ if (typeof a.path !== 'string' || a.path.trim() === '')
75
+ return undefined;
76
+ // Depth changes the returned tree, so it belongs to the scope.
77
+ return { scope: String(a.maxDepth ?? ''), query: normalizeQuery(a.path) };
78
+ }
79
+ default:
80
+ return undefined;
81
+ }
82
+ }
83
+ const REFINE_MESSAGE = [
84
+ 'Search queries are being repeated with only cosmetic differences in the same scope.',
85
+ 'Reuse the results already in context, or change the query/scope meaningfully.',
86
+ ].join('\n');
87
+ export class DuplicateSearchGuard {
88
+ buckets = new Map();
89
+ warnAfter;
90
+ stopAfter;
91
+ similarityThreshold;
92
+ window;
93
+ maxBuckets;
94
+ constructor(config = {}) {
95
+ this.warnAfter = config.warnAfter ?? 2;
96
+ this.stopAfter = config.stopAfter ?? 5;
97
+ this.similarityThreshold = config.similarityThreshold ?? 0.8;
98
+ this.window = config.window ?? 8;
99
+ this.maxBuckets = config.maxBuckets ?? 256;
100
+ }
101
+ async onToolCall(event) {
102
+ const ref = extractSearchQuery(event.toolName, event.args);
103
+ if (!ref)
104
+ return CONTINUE;
105
+ const key = `${event.toolName}\u0000${ref.scope}`;
106
+ let bucket = this.buckets.get(key);
107
+ if (!bucket) {
108
+ bucket = { recent: [], duplicateCount: 0 };
109
+ this.buckets.set(key, bucket);
110
+ this.evictIfNeeded();
111
+ }
112
+ let best = 0;
113
+ for (const previous of bucket.recent) {
114
+ best = Math.max(best, queryJaccard(previous, ref.query));
115
+ }
116
+ bucket.duplicateCount = best >= this.similarityThreshold
117
+ ? bucket.duplicateCount + 1
118
+ : 1;
119
+ bucket.recent.push(ref.query);
120
+ if (bucket.recent.length > this.window)
121
+ bucket.recent.shift();
122
+ if (bucket.duplicateCount >= this.stopAfter) {
123
+ return {
124
+ action: 'stop',
125
+ reason: `near-duplicate search queries for "${event.toolName}" ` +
126
+ `${bucket.duplicateCount} times in the same scope`,
127
+ code: 'duplicate_search',
128
+ };
129
+ }
130
+ if (bucket.duplicateCount >= this.warnAfter) {
131
+ return {
132
+ action: 'inject',
133
+ message: { role: 'user', kind: 'runtime-warning', content: REFINE_MESSAGE },
134
+ };
135
+ }
136
+ return CONTINUE;
137
+ }
138
+ reset() {
139
+ this.buckets.clear();
140
+ }
141
+ evictIfNeeded() {
142
+ while (this.buckets.size > this.maxBuckets) {
143
+ const oldest = this.buckets.keys().next().value;
144
+ if (oldest === undefined)
145
+ break;
146
+ this.buckets.delete(oldest);
147
+ }
148
+ }
149
+ }
150
+ //# sourceMappingURL=DuplicateSearchGuard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DuplicateSearchGuard.js","sourceRoot":"","sources":["../../../src/runtime/guards/DuplicateSearchGuard.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAOjD,oEAAoE;AACpE,MAAM,CAAC,MAAM,YAAY,GAAwB,IAAI,GAAG,CAAC;IACvD,cAAc;IACd,iBAAiB;IACjB,YAAY;IACZ,YAAY;CACb,CAAC,CAAC;AAEH,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,OAAO,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AAChC,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,OAAO,IAAI;SACR,WAAW,EAAE;SACb,OAAO,CAAC,oBAAoB,EAAE,GAAG,CAAC;SAClC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;SACpB,IAAI,EAAE;SACN,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AACnB,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,YAAY,CAAC,CAAS,EAAE,CAAS;IAC/C,MAAM,EAAE,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;IACjD,MAAM,EAAE,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;IACjD,IAAI,EAAE,CAAC,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,IAAI,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IAC7C,IAAI,EAAE,CAAC,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,IAAI,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IAC7C,IAAI,YAAY,GAAG,CAAC,CAAC;IACrB,KAAK,MAAM,KAAK,IAAI,EAAE;QAAE,IAAI,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC;YAAE,YAAY,EAAE,CAAC;IAC1D,MAAM,KAAK,GAAG,EAAE,CAAC,IAAI,GAAG,EAAE,CAAC,IAAI,GAAG,YAAY,CAAC;IAC/C,OAAO,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,GAAG,KAAK,CAAC;AAChD,CAAC;AASD,2EAA2E;AAC3E,MAAM,UAAU,kBAAkB,CAChC,QAAgB,EAChB,IAAa;IAEb,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC;QAAE,OAAO,SAAS,CAAC;IAC9C,MAAM,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAA4B,CAAC;IAClD,QAAQ,QAAQ,EAAE,CAAC;QACjB,KAAK,cAAc,CAAC,CAAC,CAAC;YACpB,IAAI,OAAO,CAAC,CAAC,OAAO,KAAK,QAAQ,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE;gBAAE,OAAO,SAAS,CAAC;YAC/E,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;gBACtC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC;gBACrB,CAAC,CAAC,OAAO,CAAC,CAAC,OAAO,KAAK,QAAQ;oBAC7B,CAAC,CAAC,CAAC,CAAC,OAAO;oBACX,CAAC,CAAC,EAAE,CAAC;YACT,OAAO;gBACL,KAAK,EAAE;oBACL,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE;oBACxC,OAAO;iBACR,CAAC,IAAI,CAAC,QAAQ,CAAC;gBAChB,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC,OAAO,CAAC;aACjC,CAAC;QACJ,CAAC;QACD,KAAK,iBAAiB,CAAC;QACvB,KAAK,YAAY,CAAC,CAAC,CAAC;YAClB,IAAI,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ,IAAI,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE;gBAAE,OAAO,SAAS,CAAC;YAC3E,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC;QACvD,CAAC;QACD,KAAK,YAAY,CAAC,CAAC,CAAC;YAClB,IAAI,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE;gBAAE,OAAO,SAAS,CAAC;YACzE,+DAA+D;YAC/D,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,QAAQ,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;QAC5E,CAAC;QACD;YACE,OAAO,SAAS,CAAC;IACrB,CAAC;AACH,CAAC;AAeD,MAAM,cAAc,GAAG;IACrB,qFAAqF;IACrF,+EAA+E;CAChF,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAOb,MAAM,OAAO,oBAAoB;IACd,OAAO,GAAG,IAAI,GAAG,EAAuB,CAAC;IACzC,SAAS,CAAS;IAClB,SAAS,CAAS;IAClB,mBAAmB,CAAS;IAC5B,MAAM,CAAS;IACf,UAAU,CAAS;IAEpC,YAAY,SAAqC,EAAE;QACjD,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC,SAAS,IAAI,CAAC,CAAC;QACvC,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC,SAAS,IAAI,CAAC,CAAC;QACvC,IAAI,CAAC,mBAAmB,GAAG,MAAM,CAAC,mBAAmB,IAAI,GAAG,CAAC;QAC7D,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,CAAC,CAAC;QACjC,IAAI,CAAC,UAAU,GAAG,MAAM,CAAC,UAAU,IAAI,GAAG,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,KAAoB;QACnC,MAAM,GAAG,GAAG,kBAAkB,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QAC3D,IAAI,CAAC,GAAG;YAAE,OAAO,QAAQ,CAAC;QAE1B,MAAM,GAAG,GAAG,GAAG,KAAK,CAAC,QAAQ,SAAS,GAAG,CAAC,KAAK,EAAE,CAAC;QAClD,IAAI,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACnC,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,cAAc,EAAE,CAAC,EAAE,CAAC;YAC3C,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;YAC9B,IAAI,CAAC,aAAa,EAAE,CAAC;QACvB,CAAC;QAED,IAAI,IAAI,GAAG,CAAC,CAAC;QACb,KAAK,MAAM,QAAQ,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;YACrC,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,YAAY,CAAC,QAAQ,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;QAC3D,CAAC;QACD,MAAM,CAAC,cAAc,GAAG,IAAI,IAAI,IAAI,CAAC,mBAAmB;YACtD,CAAC,CAAC,MAAM,CAAC,cAAc,GAAG,CAAC;YAC3B,CAAC,CAAC,CAAC,CAAC;QAEN,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAC9B,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM;YAAE,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QAE9D,IAAI,MAAM,CAAC,cAAc,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YAC5C,OAAO;gBACL,MAAM,EAAE,MAAM;gBACd,MAAM,EAAE,sCAAsC,KAAK,CAAC,QAAQ,IAAI;oBAC9D,GAAG,MAAM,CAAC,cAAc,0BAA0B;gBACpD,IAAI,EAAE,kBAAkB;aACzB,CAAC;QACJ,CAAC;QACD,IAAI,MAAM,CAAC,cAAc,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YAC5C,OAAO;gBACL,MAAM,EAAE,QAAQ;gBAChB,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,EAAE,OAAO,EAAE,cAAc,EAAE;aAC5E,CAAC;QACJ,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,KAAK;QACH,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC;IAEO,aAAa;QACnB,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;YAC3C,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC;YAChD,IAAI,MAAM,KAAK,SAAS;gBAAE,MAAM;YAChC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,34 @@
1
+ import type { AgentObserver, ObserverResult, ToolCallEvent, ToolResultEvent } from '../observers/types.js';
2
+ export interface FailureSignature {
3
+ commandHash: string;
4
+ exitCode?: number;
5
+ normalizedTailHash: string;
6
+ }
7
+ export interface FailureSignatureGuardConfig {
8
+ /** Inject a reassessment message at this many identical failures (default 2). */
9
+ warnAfter?: number;
10
+ /** Stop the run at this many identical failures (default 5). */
11
+ stopAfter?: number;
12
+ /** How many chars of normalized output tail feed the hash (default 2000). */
13
+ tailChars?: number;
14
+ }
15
+ export declare function stripAnsi(text: string): string;
16
+ /**
17
+ * Normalize a failure-output tail: replace volatile tokens with stable
18
+ * placeholders and drop pure progress lines, so identical root failures map
19
+ * to identical text.
20
+ */
21
+ export declare function normalizeFailureTail(text: string, tailChars?: number): string;
22
+ export declare class FailureSignatureGuard implements AgentObserver {
23
+ private readonly counts;
24
+ private readonly pendingArgs;
25
+ private readonly warnAfter;
26
+ private readonly stopAfter;
27
+ private readonly tailChars;
28
+ constructor(config?: FailureSignatureGuardConfig);
29
+ /** Track per-call command hashes so onToolResult can bind args → result. */
30
+ onToolCall(event: ToolCallEvent): Promise<ObserverResult>;
31
+ onToolResult(event: ToolResultEvent): Promise<ObserverResult>;
32
+ reset(): void;
33
+ }
34
+ //# sourceMappingURL=FailureSignatureGuard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FailureSignatureGuard.d.ts","sourceRoot":"","sources":["../../../src/runtime/guards/FailureSignatureGuard.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EACV,aAAa,EACb,cAAc,EACd,aAAa,EACb,eAAe,EAChB,MAAM,uBAAuB,CAAC;AAG/B,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,2BAA2B;IAC1C,iFAAiF;IACjF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gEAAgE;IAChE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6EAA6E;IAC7E,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAYD,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE9C;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,SAAO,GAAG,MAAM,CA4B3E;AA0BD,qBAAa,qBAAsB,YAAW,aAAa;IACzD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA6B;IACpD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA6B;IACzD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;gBAEvB,MAAM,GAAE,2BAAgC;IAMpD,4EAA4E;IACtE,UAAU,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,cAAc,CAAC;IAUzD,YAAY,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC;IAuCnE,KAAK,IAAI,IAAI;CAId"}
@@ -0,0 +1,143 @@
1
+ /**
2
+ * FailureSignatureGuard — detect the same test/build failure recurring after
3
+ * multiple attempts (Frontier upgrade, PHASE 1B).
4
+ *
5
+ * The agent may edit different files yet `npm test` keeps failing with the
6
+ * same error. Fingerprinting the *command* alone is not enough; this guard
7
+ * hashes the command together with a normalized tail of the failure output so
8
+ * that volatile noise (timestamps, durations, PIDs, UUIDs, temp paths,
9
+ * progress lines) does not mask an identical failure.
10
+ *
11
+ * Reaction: inject a root-cause reassessment message at `warnAfter` identical
12
+ * failure signatures, stop the run at `stopAfter`. A later *successful* run of
13
+ * the same command clears its counters.
14
+ */
15
+ import { createHash } from 'node:crypto';
16
+ import { stableStringify } from '../../core/requestSnapshot.js';
17
+ import { CONTINUE } from '../observers/types.js';
18
+ import { toolCallFingerprint } from './RepetitionGuard.js';
19
+ export function stripAnsi(text) {
20
+ return text.replace(/\u001b\[[0-9;]*[A-Za-z]/g, '');
21
+ }
22
+ /**
23
+ * Normalize a failure-output tail: replace volatile tokens with stable
24
+ * placeholders and drop pure progress lines, so identical root failures map
25
+ * to identical text.
26
+ */
27
+ export function normalizeFailureTail(text, tailChars = 2000) {
28
+ const tail = stripAnsi(text).slice(-tailChars);
29
+ const lines = tail.split(/\r?\n/).map((line) => line
30
+ // ISO timestamps and wall-clock times
31
+ .replace(/\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:?\d{2})?/g, '<ts>')
32
+ .replace(/\b\d{1,2}:\d{2}:\d{2}(\.\d+)?\b/g, '<time>')
33
+ // durations
34
+ .replace(/\b\d+(\.\d+)?\s*(ms|s|sec|secs|seconds|min|mins|minutes)\b/gi, '<dur>')
35
+ // UUIDs
36
+ .replace(/\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/gi, '<uuid>')
37
+ // PIDs
38
+ .replace(/\b(?:pid|process)\s*[:=]?\s*\d+\b/gi, '<pid>')
39
+ // temp paths (posix + windows)
40
+ .replace(/(?:\/tmp\/|\/var\/tmp\/|\b[A-Za-z]:\\Users\\[^\\]*\\AppData\\Local\\Temp\\)[^\s"']*/gi, '<tmp>'));
41
+ const meaningful = lines.filter((line) => {
42
+ const trimmed = line.trim();
43
+ if (!trimmed)
44
+ return false;
45
+ // success/progress glyphs (✓ ✔ ● ○ ◐ ◑ ◌) mark progress lines — drop;
46
+ // failure glyphs (✗ ✘ ×) are kept: they carry failure information.
47
+ if (/^\s*[✓✔●○◐◑◌]/.test(line))
48
+ return false;
49
+ // pure spinner / counter lines
50
+ if (/^\s*[|/\\-–—]\s*$/.test(line))
51
+ return false;
52
+ if (/^\s*\d+\s*\/\s*\d+\s*$/.test(line))
53
+ return false;
54
+ return true;
55
+ });
56
+ return meaningful.join('\n');
57
+ }
58
+ /** Extract { exitCode, text } from an unknown tool result, if shape known. */
59
+ function extractFailure(result) {
60
+ if (typeof result === 'string')
61
+ return { text: result };
62
+ if (result && typeof result === 'object') {
63
+ const r = result;
64
+ const parts = [r.stdout, r.stderr, r.output, r.content, r.message]
65
+ .filter((p) => typeof p === 'string');
66
+ if (typeof r.exitCode === 'number' || parts.length > 0) {
67
+ return { exitCode: r.exitCode, text: parts.join('\n') };
68
+ }
69
+ return { text: stableStringify(result).slice(0, 4000) };
70
+ }
71
+ return undefined;
72
+ }
73
+ const REASSESS_MESSAGE = [
74
+ 'The same failure signature has persisted across multiple attempts.',
75
+ 'Do not repeat the previous edit strategy. Re-evaluate the root cause,',
76
+ 'inspect upstream state, or delegate a fresh verification/exploration task.',
77
+ ].join('\n');
78
+ /** Cap on remembered pending tool-call args (FIFO beyond this). */
79
+ const MAX_PENDING = 256;
80
+ export class FailureSignatureGuard {
81
+ counts = new Map();
82
+ pendingArgs = new Map();
83
+ warnAfter;
84
+ stopAfter;
85
+ tailChars;
86
+ constructor(config = {}) {
87
+ this.warnAfter = config.warnAfter ?? 2;
88
+ this.stopAfter = config.stopAfter ?? 5;
89
+ this.tailChars = config.tailChars ?? 2000;
90
+ }
91
+ /** Track per-call command hashes so onToolResult can bind args → result. */
92
+ async onToolCall(event) {
93
+ const { argsHash } = toolCallFingerprint(event.toolName, event.args);
94
+ if (this.pendingArgs.size >= MAX_PENDING) {
95
+ const oldest = this.pendingArgs.keys().next().value;
96
+ if (oldest !== undefined)
97
+ this.pendingArgs.delete(oldest);
98
+ }
99
+ this.pendingArgs.set(event.toolCallId, `${event.toolName}\u0000${argsHash}`);
100
+ return CONTINUE;
101
+ }
102
+ async onToolResult(event) {
103
+ const commandHash = this.pendingArgs.get(event.toolCallId) ?? event.toolName;
104
+ this.pendingArgs.delete(event.toolCallId);
105
+ const failure = extractFailure(event.result);
106
+ if (!failure || !failure.text.trim())
107
+ return CONTINUE;
108
+ const failed = failure.exitCode !== undefined ? failure.exitCode !== 0 : event.ok === false;
109
+ if (!failed) {
110
+ // The command succeeded — clear any accumulated failure counts for it.
111
+ for (const key of this.counts.keys()) {
112
+ if (key.startsWith(`${commandHash}\u0000`))
113
+ this.counts.delete(key);
114
+ }
115
+ return CONTINUE;
116
+ }
117
+ const tailHash = createHash('sha256')
118
+ .update(normalizeFailureTail(failure.text, this.tailChars))
119
+ .digest('hex');
120
+ const key = `${commandHash}\u0000${failure.exitCode ?? 'x'}\u0000${tailHash}`;
121
+ const count = (this.counts.get(key) ?? 0) + 1;
122
+ this.counts.set(key, count);
123
+ if (count >= this.stopAfter) {
124
+ return {
125
+ action: 'stop',
126
+ reason: `same failure signature (exit ${failure.exitCode ?? 'n/a'}) recurred ${count} times after edits`,
127
+ code: 'repeated_failure',
128
+ };
129
+ }
130
+ if (count >= this.warnAfter) {
131
+ return {
132
+ action: 'inject',
133
+ message: { role: 'user', kind: 'runtime-warning', content: REASSESS_MESSAGE },
134
+ };
135
+ }
136
+ return CONTINUE;
137
+ }
138
+ reset() {
139
+ this.counts.clear();
140
+ this.pendingArgs.clear();
141
+ }
142
+ }
143
+ //# sourceMappingURL=FailureSignatureGuard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FailureSignatureGuard.js","sourceRoot":"","sources":["../../../src/runtime/guards/FailureSignatureGuard.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import type { AgentObserver, ObserverResult, ToolCallEvent, TurnEndEvent } from '../observers/types.js';
2
+ /** Tools whose execution changes repository state. */
3
+ export declare const FILE_WRITING_TOOLS: ReadonlySet<string>;
4
+ export interface NoProgressGuardConfig {
5
+ /** Consecutive unproductive turns before the inject warning (default 2). */
6
+ softStallTurns?: number;
7
+ /** Consecutive unproductive turns before the stop (default 5). */
8
+ hardStallTurns?: number;
9
+ }
10
+ export declare class NoProgressGuard implements AgentObserver {
11
+ private readonly seenFingerprints;
12
+ private readonly soft;
13
+ private readonly hard;
14
+ private stallTurns;
15
+ private turnToolCalls;
16
+ private turnWrites;
17
+ private turnNewFingerprints;
18
+ constructor(config?: NoProgressGuardConfig);
19
+ onToolCall(event: ToolCallEvent): Promise<ObserverResult>;
20
+ onTurnEnd(_event: TurnEndEvent): Promise<ObserverResult>;
21
+ reset(): void;
22
+ }
23
+ //# sourceMappingURL=NoProgressGuard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NoProgressGuard.d.ts","sourceRoot":"","sources":["../../../src/runtime/guards/NoProgressGuard.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EACV,aAAa,EACb,cAAc,EACd,aAAa,EACb,YAAY,EACb,MAAM,uBAAuB,CAAC;AAG/B,sDAAsD;AACtD,eAAO,MAAM,kBAAkB,EAAE,WAAW,CAAC,MAAM,CAIjD,CAAC;AAEH,MAAM,WAAW,qBAAqB;IACpC,4EAA4E;IAC5E,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,kEAAkE;IAClE,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAOD,qBAAa,eAAgB,YAAW,aAAa;IACnD,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAqB;IACtD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,mBAAmB,CAAK;gBAEpB,MAAM,GAAE,qBAA0B;IAKxC,UAAU,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,cAAc,CAAC;IAazD,SAAS,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,cAAc,CAAC;IA2B9D,KAAK,IAAI,IAAI;CAOd"}