koneck 2.67.0 → 2.69.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cycle.d.ts +107 -0
- package/dist/cycle.d.ts.map +1 -0
- package/dist/cycle.js +136 -0
- package/dist/cycle.js.map +1 -0
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +63 -0
- package/dist/engine.js.map +1 -1
- package/dist/ink-chat.d.ts.map +1 -1
- package/dist/ink-chat.js +98 -9
- package/dist/ink-chat.js.map +1 -1
- package/dist/mention-resolve.d.ts +64 -0
- package/dist/mention-resolve.d.ts.map +1 -0
- package/dist/mention-resolve.js +139 -0
- package/dist/mention-resolve.js.map +1 -0
- package/dist/types.d.ts +11 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/web/api.d.ts.map +1 -1
- package/dist/web/api.js +63 -0
- package/dist/web/api.js.map +1 -1
- package/dist/web/events.d.ts +13 -0
- package/dist/web/events.d.ts.map +1 -1
- package/dist/web/events.js.map +1 -1
- package/dist/web/mention-signals.d.ts +41 -0
- package/dist/web/mention-signals.d.ts.map +1 -0
- package/dist/web/mention-signals.js +117 -0
- package/dist/web/mention-signals.js.map +1 -0
- package/dist/web/mention.d.ts +121 -0
- package/dist/web/mention.d.ts.map +1 -0
- package/dist/web/mention.js +211 -0
- package/dist/web/mention.js.map +1 -0
- package/dist/web/sessions.d.ts.map +1 -1
- package/dist/web/sessions.js +25 -2
- package/dist/web/sessions.js.map +1 -1
- package/dist/web/ui-client.d.ts.map +1 -1
- package/dist/web/ui-client.js +270 -1
- package/dist/web/ui-client.js.map +1 -1
- package/dist/web/ui-css.d.ts.map +1 -1
- package/dist/web/ui-css.js +39 -0
- package/dist/web/ui-css.js.map +1 -1
- package/dist/web/ui.d.ts.map +1 -1
- package/dist/web/ui.js +9 -0
- package/dist/web/ui.js.map +1 -1
- package/package.json +1 -1
package/dist/cycle.d.ts
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Going round in circles, as opposed to repeating one call.
|
|
3
|
+
*
|
|
4
|
+
* The identical-command guard catches "the same thing twelve times". It does not catch the shape
|
|
5
|
+
* that cost 20.9 million tokens: kill the server, start the server, wait and read the log, curl the
|
|
6
|
+
* endpoint, read the log, kill the server — sixty-four times round, every individual call
|
|
7
|
+
* reasonable, none repeated back to back, and `lsof` and a one-off `node -e` probe spliced in at
|
|
8
|
+
* irregular intervals so there is no fixed period to match.
|
|
9
|
+
*
|
|
10
|
+
* ## What the data actually supports
|
|
11
|
+
*
|
|
12
|
+
* This was calibrated against the real sessions on the machine, not chosen. For each, the fewest
|
|
13
|
+
* distinct actions found in any window containing no file writes:
|
|
14
|
+
*
|
|
15
|
+
* session W=12 W=20 W=30
|
|
16
|
+
* finished properly (89 calls) 5 8 11
|
|
17
|
+
* the runaway (290 calls) 1 5 8
|
|
18
|
+
* a second loop (49 calls) 2 2 10
|
|
19
|
+
* a third loop (20 calls) 1 7 –
|
|
20
|
+
*
|
|
21
|
+
* The honest reading is that the separation is thin. A long session that finished properly is
|
|
22
|
+
* genuinely repetitive — its worst twelve-call window held five distinct actions and seven repeats,
|
|
23
|
+
* which is *more* repetitive than the runaway's cycle phase, where six distinct actions accounted
|
|
24
|
+
* for twelve calls. No window size separates them cleanly, and the entire legitimate sample is one
|
|
25
|
+
* session.
|
|
26
|
+
*
|
|
27
|
+
* ## So this warns and never stops
|
|
28
|
+
*
|
|
29
|
+
* A signal this thin can identify something worth mentioning. It cannot justify ending somebody's
|
|
30
|
+
* work: the cost of being wrong is a run killed mid-task, and one legitimate session is not evidence
|
|
31
|
+
* enough to risk that. The identical-command guard stops runs because its evidence is unambiguous —
|
|
32
|
+
* the same command, unchanged, with nothing altered between. This one talks.
|
|
33
|
+
*
|
|
34
|
+
* Two conditions, both required. No file may have been written in the window, because iterating on a
|
|
35
|
+
* file is repetitive by nature and is the most valuable thing an agent does. And the window is
|
|
36
|
+
* twenty calls rather than twelve, where the numbers above leave a real gap: five and two for the
|
|
37
|
+
* loops against eight for the run that finished.
|
|
38
|
+
*/
|
|
39
|
+
/** One tool call, as much of it as this needs. */
|
|
40
|
+
export interface Called {
|
|
41
|
+
name: string;
|
|
42
|
+
/** The one-line detail: a path, a command, a url. */
|
|
43
|
+
detail: string;
|
|
44
|
+
/** Files this call changed, when that is known. Empty or absent means it changed nothing. */
|
|
45
|
+
changed?: readonly string[] | undefined;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* How many recent calls are considered.
|
|
49
|
+
*
|
|
50
|
+
* Twenty, from the table above: at twelve the loops and the legitimate run overlap, and at thirty
|
|
51
|
+
* the legitimate run's own figure falls to eleven while a known loop sits at ten.
|
|
52
|
+
*/
|
|
53
|
+
export declare const WINDOW = 20;
|
|
54
|
+
/**
|
|
55
|
+
* How few distinct actions count as circling.
|
|
56
|
+
*
|
|
57
|
+
* Six. The loops measured five and two; the session that finished measured eight. Six leaves a
|
|
58
|
+
* margin on both sides of a boundary drawn from one legitimate sample, which is the most that sample
|
|
59
|
+
* can honestly carry.
|
|
60
|
+
*/
|
|
61
|
+
export declare const CHURN_DISTINCT = 6;
|
|
62
|
+
/**
|
|
63
|
+
* A signature: the tool and what it was pointed at, tidied and nothing more.
|
|
64
|
+
*
|
|
65
|
+
* The first version stripped digits and long hex runs, reasoning that the same loop reappears with a
|
|
66
|
+
* different port, a fresh pid or a regenerated key. Two things settled that.
|
|
67
|
+
*
|
|
68
|
+
* It caught a false positive immediately: `grep pattern-1` through `grep pattern-8` collapsed into
|
|
69
|
+
* one action, so reading or searching eight numbered things — which is progress — read as a cycle of
|
|
70
|
+
* one. And it bought nothing. Measured across the real sessions, the fewest distinct actions in a
|
|
71
|
+
* no-write window came out identical with the stripping and without it: 8 for the run that finished,
|
|
72
|
+
* 5 and 7 and 2 for the loops. The variation it was written for does not appear in any session on
|
|
73
|
+
* the machine, while the harm it caused appeared in the first test.
|
|
74
|
+
*
|
|
75
|
+
* So it does what the evidence supports and stops. If a loop that varies a token every round turns
|
|
76
|
+
* up later, it can be handled then, with that loop to look at.
|
|
77
|
+
*/
|
|
78
|
+
export declare function signatureOf(call: Called): string;
|
|
79
|
+
export interface Churn {
|
|
80
|
+
/** How many distinct actions accounted for the window. */
|
|
81
|
+
distinct: number;
|
|
82
|
+
/** How many calls were second-or-later occurrences of something already in the window. */
|
|
83
|
+
repeats: number;
|
|
84
|
+
/** The actions themselves, most-repeated first, for saying what is being circled. */
|
|
85
|
+
actions: Array<{
|
|
86
|
+
what: string;
|
|
87
|
+
times: number;
|
|
88
|
+
}>;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Whether the last stretch of calls is circling, or null when it is not.
|
|
92
|
+
*
|
|
93
|
+
* Null while there is not yet a full window: a judgement about whether somebody is going in circles
|
|
94
|
+
* cannot be made from four calls, and guessing early is how a guard becomes something people learn
|
|
95
|
+
* to ignore.
|
|
96
|
+
*/
|
|
97
|
+
export declare function findChurn(history: readonly Called[], window?: number, maxDistinct?: number): Churn | null;
|
|
98
|
+
/**
|
|
99
|
+
* What to tell the model, naming what it is circling.
|
|
100
|
+
*
|
|
101
|
+
* The actions are quoted because a model that has lost its place is usually corrected by seeing the
|
|
102
|
+
* list: "you have been doing these four things" is actionable in a way that "you appear to be
|
|
103
|
+
* looping" is not. And it says what to do instead, because a complaint without an alternative is
|
|
104
|
+
* just a complaint.
|
|
105
|
+
*/
|
|
106
|
+
export declare function describeChurn(churn: Churn, window?: number): string;
|
|
107
|
+
//# sourceMappingURL=cycle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cycle.d.ts","sourceRoot":"","sources":["../src/cycle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAEH,kDAAkD;AAClD,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,qDAAqD;IACrD,MAAM,EAAE,MAAM,CAAC;IACf,6FAA6F;IAC7F,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;CACzC;AAED;;;;;GAKG;AACH,eAAO,MAAM,MAAM,KAAK,CAAC;AAEzB;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,IAAI,CAAC;AAOhC;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAMhD;AAED,MAAM,WAAW,KAAK;IACpB,0DAA0D;IAC1D,QAAQ,EAAE,MAAM,CAAC;IACjB,0FAA0F;IAC1F,OAAO,EAAE,MAAM,CAAC;IAChB,qFAAqF;IACrF,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACjD;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CACvB,OAAO,EAAE,SAAS,MAAM,EAAE,EAC1B,MAAM,SAAS,EACf,WAAW,SAAiB,GAC3B,KAAK,GAAG,IAAI,CA4Bd;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,GAAG,MAAM,CAUnE"}
|
package/dist/cycle.js
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Going round in circles, as opposed to repeating one call.
|
|
3
|
+
*
|
|
4
|
+
* The identical-command guard catches "the same thing twelve times". It does not catch the shape
|
|
5
|
+
* that cost 20.9 million tokens: kill the server, start the server, wait and read the log, curl the
|
|
6
|
+
* endpoint, read the log, kill the server — sixty-four times round, every individual call
|
|
7
|
+
* reasonable, none repeated back to back, and `lsof` and a one-off `node -e` probe spliced in at
|
|
8
|
+
* irregular intervals so there is no fixed period to match.
|
|
9
|
+
*
|
|
10
|
+
* ## What the data actually supports
|
|
11
|
+
*
|
|
12
|
+
* This was calibrated against the real sessions on the machine, not chosen. For each, the fewest
|
|
13
|
+
* distinct actions found in any window containing no file writes:
|
|
14
|
+
*
|
|
15
|
+
* session W=12 W=20 W=30
|
|
16
|
+
* finished properly (89 calls) 5 8 11
|
|
17
|
+
* the runaway (290 calls) 1 5 8
|
|
18
|
+
* a second loop (49 calls) 2 2 10
|
|
19
|
+
* a third loop (20 calls) 1 7 –
|
|
20
|
+
*
|
|
21
|
+
* The honest reading is that the separation is thin. A long session that finished properly is
|
|
22
|
+
* genuinely repetitive — its worst twelve-call window held five distinct actions and seven repeats,
|
|
23
|
+
* which is *more* repetitive than the runaway's cycle phase, where six distinct actions accounted
|
|
24
|
+
* for twelve calls. No window size separates them cleanly, and the entire legitimate sample is one
|
|
25
|
+
* session.
|
|
26
|
+
*
|
|
27
|
+
* ## So this warns and never stops
|
|
28
|
+
*
|
|
29
|
+
* A signal this thin can identify something worth mentioning. It cannot justify ending somebody's
|
|
30
|
+
* work: the cost of being wrong is a run killed mid-task, and one legitimate session is not evidence
|
|
31
|
+
* enough to risk that. The identical-command guard stops runs because its evidence is unambiguous —
|
|
32
|
+
* the same command, unchanged, with nothing altered between. This one talks.
|
|
33
|
+
*
|
|
34
|
+
* Two conditions, both required. No file may have been written in the window, because iterating on a
|
|
35
|
+
* file is repetitive by nature and is the most valuable thing an agent does. And the window is
|
|
36
|
+
* twenty calls rather than twelve, where the numbers above leave a real gap: five and two for the
|
|
37
|
+
* loops against eight for the run that finished.
|
|
38
|
+
*/
|
|
39
|
+
/**
|
|
40
|
+
* How many recent calls are considered.
|
|
41
|
+
*
|
|
42
|
+
* Twenty, from the table above: at twelve the loops and the legitimate run overlap, and at thirty
|
|
43
|
+
* the legitimate run's own figure falls to eleven while a known loop sits at ten.
|
|
44
|
+
*/
|
|
45
|
+
export const WINDOW = 20;
|
|
46
|
+
/**
|
|
47
|
+
* How few distinct actions count as circling.
|
|
48
|
+
*
|
|
49
|
+
* Six. The loops measured five and two; the session that finished measured eight. Six leaves a
|
|
50
|
+
* margin on both sides of a boundary drawn from one legitimate sample, which is the most that sample
|
|
51
|
+
* can honestly carry.
|
|
52
|
+
*/
|
|
53
|
+
export const CHURN_DISTINCT = 6;
|
|
54
|
+
/** Tools that change files. A window containing one of these is progress, whatever its shape. */
|
|
55
|
+
const WRITES = new Set([
|
|
56
|
+
'write_file', 'search_replace', 'apply_diff', 'delete_file', 'move_file',
|
|
57
|
+
]);
|
|
58
|
+
/**
|
|
59
|
+
* A signature: the tool and what it was pointed at, tidied and nothing more.
|
|
60
|
+
*
|
|
61
|
+
* The first version stripped digits and long hex runs, reasoning that the same loop reappears with a
|
|
62
|
+
* different port, a fresh pid or a regenerated key. Two things settled that.
|
|
63
|
+
*
|
|
64
|
+
* It caught a false positive immediately: `grep pattern-1` through `grep pattern-8` collapsed into
|
|
65
|
+
* one action, so reading or searching eight numbered things — which is progress — read as a cycle of
|
|
66
|
+
* one. And it bought nothing. Measured across the real sessions, the fewest distinct actions in a
|
|
67
|
+
* no-write window came out identical with the stripping and without it: 8 for the run that finished,
|
|
68
|
+
* 5 and 7 and 2 for the loops. The variation it was written for does not appear in any session on
|
|
69
|
+
* the machine, while the harm it caused appeared in the first test.
|
|
70
|
+
*
|
|
71
|
+
* So it does what the evidence supports and stops. If a loop that varies a token every round turns
|
|
72
|
+
* up later, it can be handled then, with that loop to look at.
|
|
73
|
+
*/
|
|
74
|
+
export function signatureOf(call) {
|
|
75
|
+
const detail = String(call.detail ?? '')
|
|
76
|
+
.replace(/\s+/g, ' ')
|
|
77
|
+
.trim()
|
|
78
|
+
.slice(0, 80);
|
|
79
|
+
return `${call.name}::${detail}`;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Whether the last stretch of calls is circling, or null when it is not.
|
|
83
|
+
*
|
|
84
|
+
* Null while there is not yet a full window: a judgement about whether somebody is going in circles
|
|
85
|
+
* cannot be made from four calls, and guessing early is how a guard becomes something people learn
|
|
86
|
+
* to ignore.
|
|
87
|
+
*/
|
|
88
|
+
export function findChurn(history, window = WINDOW, maxDistinct = CHURN_DISTINCT) {
|
|
89
|
+
if (history.length < window)
|
|
90
|
+
return null;
|
|
91
|
+
const recent = history.slice(-window);
|
|
92
|
+
/*
|
|
93
|
+
* Anything written is progress, whatever shape the calls make.
|
|
94
|
+
*
|
|
95
|
+
* This is the condition that makes the thin signal safe to act on at all: write, test, write, test
|
|
96
|
+
* is the most repetitive thing an agent does and the most valuable, and it is exempt by
|
|
97
|
+
* construction rather than by a threshold that might or might not hold.
|
|
98
|
+
*/
|
|
99
|
+
if (recent.some(call => WRITES.has(call.name) || (call.changed?.length ?? 0) > 0))
|
|
100
|
+
return null;
|
|
101
|
+
const counts = new Map();
|
|
102
|
+
for (const call of recent) {
|
|
103
|
+
const sig = signatureOf(call);
|
|
104
|
+
counts.set(sig, (counts.get(sig) ?? 0) + 1);
|
|
105
|
+
}
|
|
106
|
+
if (counts.size > maxDistinct)
|
|
107
|
+
return null;
|
|
108
|
+
const actions = [...counts.entries()]
|
|
109
|
+
.map(([what, times]) => ({ what, times }))
|
|
110
|
+
.sort((a, b) => b.times - a.times || a.what.localeCompare(b.what));
|
|
111
|
+
return {
|
|
112
|
+
distinct: counts.size,
|
|
113
|
+
repeats: window - counts.size,
|
|
114
|
+
actions,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* What to tell the model, naming what it is circling.
|
|
119
|
+
*
|
|
120
|
+
* The actions are quoted because a model that has lost its place is usually corrected by seeing the
|
|
121
|
+
* list: "you have been doing these four things" is actionable in a way that "you appear to be
|
|
122
|
+
* looping" is not. And it says what to do instead, because a complaint without an alternative is
|
|
123
|
+
* just a complaint.
|
|
124
|
+
*/
|
|
125
|
+
export function describeChurn(churn, window = WINDOW) {
|
|
126
|
+
// All of them, not the top few: there are at most six by definition, and the one left off the end
|
|
127
|
+
// of a truncated list was the one that made the cycle recognisable.
|
|
128
|
+
const listed = churn.actions
|
|
129
|
+
.map(action => `${action.what.replace('::', ' ')} (${action.times}×)`)
|
|
130
|
+
.join('; ');
|
|
131
|
+
return `Your last ${window} tool calls were ${churn.distinct} distinct actions repeated, and `
|
|
132
|
+
+ `nothing has been written to disk in that time: ${listed}. Restarting something and checking `
|
|
133
|
+
+ `it again is not progress. Use what the last attempt told you, try a different approach, or `
|
|
134
|
+
+ `say plainly what is blocking you — the answer is not going to arrive on the next round.`;
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=cycle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cycle.js","sourceRoot":"","sources":["../src/cycle.ts"],"names":[],"mappings":"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"}
|
package/dist/engine.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAc5B,OAAO,EAA0C,KAAK,IAAI,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"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"}
|
package/dist/engine.js
CHANGED
|
@@ -10,6 +10,7 @@ import { hasWorkspaceOwnedGitRepo, gitScope, createWorktree, mergeWorktree, pres
|
|
|
10
10
|
import { resolveProvider, getApiKey, keyProvenance, servesLocally, servesOwnWeights, isPrivateHost } from './providers.js';
|
|
11
11
|
import { wireEffort, looksLikeEffortUnsupported } from './pace.js';
|
|
12
12
|
import { triggerFor, pickNext, describeSwitch, askSwitch, keyOf as failoverKey } from './failover.js';
|
|
13
|
+
import { findChurn, describeChurn, WINDOW as CHURN_WINDOW } from './cycle.js';
|
|
13
14
|
import { claim, release, describeClaims } from './claims.js';
|
|
14
15
|
import { handoffFrom, describeHandoff, worthSaying } from './handoff.js';
|
|
15
16
|
import { readAll as readLedger, relevant as relevantFindings, withStaleness, describeFindings, deriveFindings, record as recordFindings, changedSince, currentSha } from './ledger.js';
|
|
@@ -1026,6 +1027,16 @@ detectedContext = { current: 0 }) {
|
|
|
1026
1027
|
* no files, which is exactly why running it sixty-four times is a loop.
|
|
1027
1028
|
*/
|
|
1028
1029
|
const commandRuns = new Map();
|
|
1030
|
+
/*
|
|
1031
|
+
* A rolling record of what has been called, for noticing a cycle rather than a repeat.
|
|
1032
|
+
*
|
|
1033
|
+
* The identical-command guard catches "the same thing twelve times". It cannot catch kill, start,
|
|
1034
|
+
* wait, curl, read, kill — sixty-four times round, no call repeated back to back. This holds
|
|
1035
|
+
* enough history to see that shape, and nothing more: it is trimmed to the window it is read over.
|
|
1036
|
+
*/
|
|
1037
|
+
const callHistory = [];
|
|
1038
|
+
/** Whether the model has already been told it is circling, so it is said once and not per turn. */
|
|
1039
|
+
let churnMentioned = false;
|
|
1029
1040
|
/**
|
|
1030
1041
|
* What each call returned, so a repeat can be answered rather than refused.
|
|
1031
1042
|
*
|
|
@@ -1979,6 +1990,34 @@ detectedContext = { current: 0 }) {
|
|
|
1979
1990
|
tui.printWarning(`${config.model} repeated itself ${looping.count} times — cut short.`);
|
|
1980
1991
|
config.onLooping?.(looping.count);
|
|
1981
1992
|
}
|
|
1993
|
+
/*
|
|
1994
|
+
* Circling, as opposed to repeating: said once, and never used to end the run.
|
|
1995
|
+
*
|
|
1996
|
+
* Calibrated against the real sessions on this machine rather than chosen. In any window with no
|
|
1997
|
+
* file writes, the fewest distinct actions were: five for a long session that finished properly,
|
|
1998
|
+
* one and two for two known loops. The separation is thin — the finished session's worst
|
|
1999
|
+
* twelve-call window was *more* repetitive than the runaway's cycle phase — and the entire
|
|
2000
|
+
* legitimate sample is one session.
|
|
2001
|
+
*
|
|
2002
|
+
* A signal that thin can identify something worth mentioning. It cannot justify ending somebody's
|
|
2003
|
+
* work, because the cost of being wrong is a run killed mid-task. So this talks and the
|
|
2004
|
+
* identical-command guard stops, which is the right division: that one's evidence is
|
|
2005
|
+
* unambiguous, and this one's is a judgement call made on four sessions.
|
|
2006
|
+
*/
|
|
2007
|
+
if (!churnMentioned) {
|
|
2008
|
+
const circling = findChurn(callHistory);
|
|
2009
|
+
if (circling) {
|
|
2010
|
+
churnMentioned = true;
|
|
2011
|
+
const said = describeChurn(circling);
|
|
2012
|
+
messages.push({ role: 'system', content: said });
|
|
2013
|
+
trajectory?.add({ t: 'stop', at: Date.now(),
|
|
2014
|
+
reason: `noticed ${circling.distinct} actions repeated over ${CHURN_WINDOW} calls with `
|
|
2015
|
+
+ `nothing written` });
|
|
2016
|
+
config.onCircling?.(said, circling);
|
|
2017
|
+
if (!silent)
|
|
2018
|
+
tui.printWarning(said);
|
|
2019
|
+
}
|
|
2020
|
+
}
|
|
1982
2021
|
if (toolCalls.length === 0) {
|
|
1983
2022
|
// A model may narrate its next step ("Now let me read X…") without attaching the
|
|
1984
2023
|
// tool call, which used to end the run mid-investigation. When the text clearly
|
|
@@ -2145,6 +2184,18 @@ detectedContext = { current: 0 }) {
|
|
|
2145
2184
|
});
|
|
2146
2185
|
for (const tc of toolCalls) {
|
|
2147
2186
|
const sig = tc.name + '::' + tc.arguments;
|
|
2187
|
+
/*
|
|
2188
|
+
* Recorded on attempt, not on execution.
|
|
2189
|
+
*
|
|
2190
|
+
* The first wiring pushed to this record after a call ran, so every call a guard suppressed
|
|
2191
|
+
* was invisible to the cycle check — and the cycle check needs exactly those. Measured: a
|
|
2192
|
+
* six-step cycle had eighteen calls executed out of thirty-seven attempted, so a window of
|
|
2193
|
+
* twenty never filled and the check never ran once. What is being judged is what the model
|
|
2194
|
+
* keeps trying, which is the thing it attempted.
|
|
2195
|
+
*/
|
|
2196
|
+
callHistory.push({ name: tc.name, detail: toolDetailFor(tc.name, tc.arguments) });
|
|
2197
|
+
while (callHistory.length > CHURN_WINDOW * 2)
|
|
2198
|
+
callHistory.shift();
|
|
2148
2199
|
/*
|
|
2149
2200
|
* The same command, again, with nothing changed since.
|
|
2150
2201
|
*
|
|
@@ -2555,6 +2606,18 @@ detectedContext = { current: 0 }) {
|
|
|
2555
2606
|
detail: toolDetailFor(effectiveName, tc.arguments),
|
|
2556
2607
|
resultChars: result.length,
|
|
2557
2608
|
});
|
|
2609
|
+
/*
|
|
2610
|
+
* What this call changed, added to the record made when it was attempted.
|
|
2611
|
+
*
|
|
2612
|
+
* The change list is what makes the cycle check safe to act on: a window containing a write is
|
|
2613
|
+
* progress whatever shape the calls make, so iterating on a file can never be mistaken for
|
|
2614
|
+
* going round in circles.
|
|
2615
|
+
*/
|
|
2616
|
+
if (changedByCommand && changedByCommand.length > 0) {
|
|
2617
|
+
const last = callHistory[callHistory.length - 1];
|
|
2618
|
+
if (last && last.name === effectiveName)
|
|
2619
|
+
last.changed = changedByCommand;
|
|
2620
|
+
}
|
|
2558
2621
|
resultsBySignature.set(sig, spilled.text.slice(0, 20_000));
|
|
2559
2622
|
messages.push({ role: 'tool', tool_call_id: tc.id, content: spilled.text });
|
|
2560
2623
|
}
|