@namzu/sdk 62.6.0 → 62.7.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/CHANGELOG.md +35 -0
- package/dist/agents/QueryAgent.d.ts.map +1 -1
- package/dist/agents/QueryAgent.js +5 -0
- package/dist/agents/QueryAgent.js.map +1 -1
- package/dist/agents/examples/SupervisorAgent.d.ts.map +1 -1
- package/dist/agents/examples/SupervisorAgent.js +1 -0
- package/dist/agents/examples/SupervisorAgent.js.map +1 -1
- package/dist/agents/explore.d.ts.map +1 -1
- package/dist/agents/explore.js +2 -1
- package/dist/agents/explore.js.map +1 -1
- package/dist/compaction/index.d.ts +1 -1
- package/dist/compaction/index.d.ts.map +1 -1
- package/dist/compaction/index.js +1 -1
- package/dist/compaction/index.js.map +1 -1
- package/dist/compaction/model-summary.js +2 -2
- package/dist/compaction/model-summary.js.map +1 -1
- package/dist/compaction/summary.d.ts +16 -1
- package/dist/compaction/summary.d.ts.map +1 -1
- package/dist/compaction/summary.js +25 -2
- package/dist/compaction/summary.js.map +1 -1
- package/dist/config/runtime.d.ts +8 -0
- package/dist/config/runtime.d.ts.map +1 -1
- package/dist/config/runtime.js +15 -0
- package/dist/config/runtime.js.map +1 -1
- package/dist/execution/workflow/runtime.d.ts.map +1 -1
- package/dist/execution/workflow/runtime.js +3 -0
- package/dist/execution/workflow/runtime.js.map +1 -1
- package/dist/execution/workflow/types.d.ts +8 -0
- package/dist/execution/workflow/types.d.ts.map +1 -1
- package/dist/manager/agent/child-session.d.ts +12 -0
- package/dist/manager/agent/child-session.d.ts.map +1 -1
- package/dist/manager/agent/child-session.js +29 -0
- package/dist/manager/agent/child-session.js.map +1 -1
- package/dist/manager/agent/lifecycle.d.ts.map +1 -1
- package/dist/manager/agent/lifecycle.js +8 -0
- package/dist/manager/agent/lifecycle.js.map +1 -1
- package/dist/manager/session/turn-recorder.d.ts +13 -2
- package/dist/manager/session/turn-recorder.d.ts.map +1 -1
- package/dist/manager/session/turn-recorder.js +57 -3
- package/dist/manager/session/turn-recorder.js.map +1 -1
- package/dist/prompt/coding-agent-doctrine.d.ts +3 -3
- package/dist/prompt/coding-agent-doctrine.d.ts.map +1 -1
- package/dist/prompt/coding-agent-doctrine.js +5 -3
- package/dist/prompt/coding-agent-doctrine.js.map +1 -1
- package/dist/provider/fallback.d.ts.map +1 -1
- package/dist/provider/fallback.js +5 -1
- package/dist/provider/fallback.js.map +1 -1
- package/dist/provider/retry.d.ts +25 -0
- package/dist/provider/retry.d.ts.map +1 -1
- package/dist/provider/retry.js +28 -0
- package/dist/provider/retry.js.map +1 -1
- package/dist/public-runtime.d.ts +3 -3
- package/dist/public-runtime.d.ts.map +1 -1
- package/dist/public-runtime.js +3 -3
- package/dist/public-runtime.js.map +1 -1
- package/dist/public-tools.d.ts +4 -2
- package/dist/public-tools.d.ts.map +1 -1
- package/dist/public-tools.js +5 -2
- package/dist/public-tools.js.map +1 -1
- package/dist/runtime/query/auto-review/classifier.d.ts +7 -1
- package/dist/runtime/query/auto-review/classifier.d.ts.map +1 -1
- package/dist/runtime/query/auto-review/classifier.js +8 -4
- package/dist/runtime/query/auto-review/classifier.js.map +1 -1
- package/dist/runtime/query/auto-review/prompt.d.ts.map +1 -1
- package/dist/runtime/query/auto-review/prompt.js +2 -0
- package/dist/runtime/query/auto-review/prompt.js.map +1 -1
- package/dist/runtime/query/auto-review/script-evidence.d.ts +93 -0
- package/dist/runtime/query/auto-review/script-evidence.d.ts.map +1 -0
- package/dist/runtime/query/auto-review/script-evidence.js +566 -0
- package/dist/runtime/query/auto-review/script-evidence.js.map +1 -0
- package/dist/runtime/query/checkpoint.d.ts +13 -0
- package/dist/runtime/query/checkpoint.d.ts.map +1 -1
- package/dist/runtime/query/checkpoint.js +37 -0
- package/dist/runtime/query/checkpoint.js.map +1 -1
- package/dist/runtime/query/executor.d.ts +38 -1
- package/dist/runtime/query/executor.d.ts.map +1 -1
- package/dist/runtime/query/executor.js +130 -11
- package/dist/runtime/query/executor.js.map +1 -1
- package/dist/runtime/query/finalize-turn.d.ts +7 -1
- package/dist/runtime/query/finalize-turn.d.ts.map +1 -1
- package/dist/runtime/query/finalize-turn.js +2 -0
- package/dist/runtime/query/finalize-turn.js.map +1 -1
- package/dist/runtime/query/index.d.ts +8 -0
- package/dist/runtime/query/index.d.ts.map +1 -1
- package/dist/runtime/query/index.js +7 -0
- package/dist/runtime/query/index.js.map +1 -1
- package/dist/runtime/query/iteration/index.d.ts +32 -1
- package/dist/runtime/query/iteration/index.d.ts.map +1 -1
- package/dist/runtime/query/iteration/index.js +158 -10
- package/dist/runtime/query/iteration/index.js.map +1 -1
- package/dist/runtime/query/iteration/phases/compaction.d.ts +12 -0
- package/dist/runtime/query/iteration/phases/compaction.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/compaction.js +48 -1
- package/dist/runtime/query/iteration/phases/compaction.js.map +1 -1
- package/dist/runtime/query/iteration/phases/context.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/context.js +5 -0
- package/dist/runtime/query/iteration/phases/context.js.map +1 -1
- package/dist/runtime/query/iteration/stream-turn.js +3 -3
- package/dist/runtime/query/iteration/stream-turn.js.map +1 -1
- package/dist/runtime/query/prelude-lease.d.ts.map +1 -1
- package/dist/runtime/query/prelude-lease.js +22 -0
- package/dist/runtime/query/prelude-lease.js.map +1 -1
- package/dist/runtime/query/prepare-turn.d.ts.map +1 -1
- package/dist/runtime/query/prepare-turn.js +13 -4
- package/dist/runtime/query/prepare-turn.js.map +1 -1
- package/dist/runtime/query/review-policy.d.ts.map +1 -1
- package/dist/runtime/query/review-policy.js +29 -0
- package/dist/runtime/query/review-policy.js.map +1 -1
- package/dist/runtime/query/tooling.d.ts +2 -0
- package/dist/runtime/query/tooling.d.ts.map +1 -1
- package/dist/runtime/query/tooling.js +1 -0
- package/dist/runtime/query/tooling.js.map +1 -1
- package/dist/runtime/query/turn/completion-audit.d.ts +27 -1
- package/dist/runtime/query/turn/completion-audit.d.ts.map +1 -1
- package/dist/runtime/query/turn/completion-audit.js +71 -6
- package/dist/runtime/query/turn/completion-audit.js.map +1 -1
- package/dist/runtime/query/turn/outcome-ledger.d.ts +113 -0
- package/dist/runtime/query/turn/outcome-ledger.d.ts.map +1 -0
- package/dist/runtime/query/turn/outcome-ledger.js +578 -0
- package/dist/runtime/query/turn/outcome-ledger.js.map +1 -0
- package/dist/sandbox/file-walk.d.ts.map +1 -1
- package/dist/sandbox/file-walk.js +34 -17
- package/dist/sandbox/file-walk.js.map +1 -1
- package/dist/sandbox/glob-segment.d.ts +35 -0
- package/dist/sandbox/glob-segment.d.ts.map +1 -0
- package/dist/sandbox/glob-segment.js +300 -0
- package/dist/sandbox/glob-segment.js.map +1 -0
- package/dist/store/session-log/index.d.ts +1 -0
- package/dist/store/session-log/index.d.ts.map +1 -1
- package/dist/store/session-log/index.js +1 -0
- package/dist/store/session-log/index.js.map +1 -1
- package/dist/store/session-log/turn-outcome.d.ts +37 -0
- package/dist/store/session-log/turn-outcome.d.ts.map +1 -0
- package/dist/store/session-log/turn-outcome.js +77 -0
- package/dist/store/session-log/turn-outcome.js.map +1 -0
- package/dist/tools/builtins/bash.js +1 -1
- package/dist/tools/builtins/bash.js.map +1 -1
- package/dist/tools/builtins/edit-apply.d.ts.map +1 -1
- package/dist/tools/builtins/edit-apply.js +24 -3
- package/dist/tools/builtins/edit-apply.js.map +1 -1
- package/dist/tools/builtins/edit.d.ts +31 -17
- package/dist/tools/builtins/edit.d.ts.map +1 -1
- package/dist/tools/builtins/edit.js +92 -27
- package/dist/tools/builtins/edit.js.map +1 -1
- package/dist/tools/builtins/glob.js +1 -1
- package/dist/tools/builtins/glob.js.map +1 -1
- package/dist/tools/builtins/grep.js +1 -1
- package/dist/tools/builtins/grep.js.map +1 -1
- package/dist/tools/builtins/notebook-guard.d.ts +19 -0
- package/dist/tools/builtins/notebook-guard.d.ts.map +1 -0
- package/dist/tools/builtins/notebook-guard.js +75 -0
- package/dist/tools/builtins/notebook-guard.js.map +1 -0
- package/dist/tools/builtins/read-render.d.ts.map +1 -1
- package/dist/tools/builtins/read-render.js +8 -2
- package/dist/tools/builtins/read-render.js.map +1 -1
- package/dist/tools/builtins/write-file.d.ts.map +1 -1
- package/dist/tools/builtins/write-file.js +14 -1
- package/dist/tools/builtins/write-file.js.map +1 -1
- package/dist/tools/coordinator/agent.d.ts.map +1 -1
- package/dist/tools/coordinator/agent.js +25 -6
- package/dist/tools/coordinator/agent.js.map +1 -1
- package/dist/tools/coordinator/index.d.ts.map +1 -1
- package/dist/tools/coordinator/index.js +6 -1
- package/dist/tools/coordinator/index.js.map +1 -1
- package/dist/tools/coordinator/outcome-record.d.ts +22 -0
- package/dist/tools/coordinator/outcome-record.d.ts.map +1 -0
- package/dist/tools/coordinator/outcome-record.js +91 -0
- package/dist/tools/coordinator/outcome-record.js.map +1 -0
- package/dist/tools/defineTool.d.ts +2 -0
- package/dist/tools/defineTool.d.ts.map +1 -1
- package/dist/tools/defineTool.js +1 -0
- package/dist/tools/defineTool.js.map +1 -1
- package/dist/tools/file-read-tracker.d.ts +23 -2
- package/dist/tools/file-read-tracker.d.ts.map +1 -1
- package/dist/tools/file-read-tracker.js +37 -2
- package/dist/tools/file-read-tracker.js.map +1 -1
- package/dist/tools/untrusted-envelope.d.ts +6 -0
- package/dist/tools/untrusted-envelope.d.ts.map +1 -1
- package/dist/tools/untrusted-envelope.js +22 -1
- package/dist/tools/untrusted-envelope.js.map +1 -1
- package/dist/toolsets/manager.d.ts.map +1 -1
- package/dist/toolsets/manager.js +3 -4
- package/dist/toolsets/manager.js.map +1 -1
- package/dist/toolsets/validation-issues.d.ts +27 -0
- package/dist/toolsets/validation-issues.d.ts.map +1 -0
- package/dist/toolsets/validation-issues.js +83 -0
- package/dist/toolsets/validation-issues.js.map +1 -0
- package/dist/turn/memory-recall.d.ts +13 -0
- package/dist/turn/memory-recall.d.ts.map +1 -1
- package/dist/turn/memory-recall.js +18 -0
- package/dist/turn/memory-recall.js.map +1 -1
- package/dist/types/agent/base.d.ts +19 -0
- package/dist/types/agent/base.d.ts.map +1 -1
- package/dist/types/agent/query.d.ts +2 -0
- package/dist/types/agent/query.d.ts.map +1 -1
- package/dist/types/common/index.d.ts +17 -0
- package/dist/types/common/index.d.ts.map +1 -1
- package/dist/types/common/index.js +8 -1
- package/dist/types/common/index.js.map +1 -1
- package/dist/types/hitl/index.d.ts +22 -0
- package/dist/types/hitl/index.d.ts.map +1 -1
- package/dist/types/hitl/index.js.map +1 -1
- package/dist/types/session/config.d.ts +20 -0
- package/dist/types/session/config.d.ts.map +1 -1
- package/dist/types/session/events.d.ts +3 -3
- package/dist/types/session/events.d.ts.map +1 -1
- package/dist/types/session/index.d.ts +1 -0
- package/dist/types/session/index.d.ts.map +1 -1
- package/dist/types/session/index.js +1 -0
- package/dist/types/session/index.js.map +1 -1
- package/dist/types/session/outcome.d.ts +95 -0
- package/dist/types/session/outcome.d.ts.map +1 -0
- package/dist/types/session/outcome.js +45 -0
- package/dist/types/session/outcome.js.map +1 -0
- package/dist/types/session/prepare-step.d.ts +2 -0
- package/dist/types/session/prepare-step.d.ts.map +1 -1
- package/dist/types/session/records.d.ts +167 -2
- package/dist/types/session/records.d.ts.map +1 -1
- package/dist/types/session/records.js +42 -0
- package/dist/types/session/records.js.map +1 -1
- package/dist/types/session/turn.d.ts +18 -0
- package/dist/types/session/turn.d.ts.map +1 -1
- package/dist/types/session/turn.js.map +1 -1
- package/dist/types/tool/index.d.ts +30 -0
- package/dist/types/tool/index.d.ts.map +1 -1
- package/dist/types/tool/index.js.map +1 -1
- package/package.json +2 -2
- package/src/agents/QueryAgent.ts +5 -0
- package/src/agents/examples/SupervisorAgent.ts +1 -0
- package/src/agents/explore.ts +2 -1
- package/src/compaction/index.ts +1 -0
- package/src/compaction/model-summary.ts +2 -2
- package/src/compaction/summary.ts +30 -2
- package/src/config/runtime.ts +15 -0
- package/src/execution/workflow/runtime.ts +4 -0
- package/src/execution/workflow/types.ts +9 -0
- package/src/manager/agent/child-session.ts +36 -0
- package/src/manager/agent/lifecycle.ts +9 -0
- package/src/manager/session/turn-recorder.ts +62 -3
- package/src/pricing/rates.source.json +1 -1
- package/src/prompt/coding-agent-doctrine.ts +5 -3
- package/src/provider/fallback.ts +6 -1
- package/src/provider/retry.ts +51 -0
- package/src/public-runtime.ts +10 -1
- package/src/public-tools.ts +6 -1
- package/src/runtime/query/auto-review/classifier.ts +21 -5
- package/src/runtime/query/auto-review/prompt.ts +2 -0
- package/src/runtime/query/auto-review/script-evidence.ts +618 -0
- package/src/runtime/query/checkpoint.ts +53 -1
- package/src/runtime/query/executor.ts +159 -19
- package/src/runtime/query/finalize-turn.ts +8 -1
- package/src/runtime/query/index.ts +19 -0
- package/src/runtime/query/iteration/index.ts +167 -14
- package/src/runtime/query/iteration/phases/compaction.ts +53 -3
- package/src/runtime/query/iteration/phases/context.ts +6 -0
- package/src/runtime/query/iteration/stream-turn.ts +3 -3
- package/src/runtime/query/prelude-lease.ts +19 -0
- package/src/runtime/query/prepare-turn.ts +13 -4
- package/src/runtime/query/review-policy.ts +33 -0
- package/src/runtime/query/tooling.ts +3 -0
- package/src/runtime/query/turn/completion-audit.ts +76 -5
- package/src/runtime/query/turn/outcome-ledger.ts +633 -0
- package/src/sandbox/file-walk.ts +38 -17
- package/src/sandbox/glob-segment.ts +309 -0
- package/src/store/session-log/index.ts +7 -0
- package/src/store/session-log/turn-outcome.ts +84 -0
- package/src/tools/builtins/bash.ts +1 -1
- package/src/tools/builtins/edit-apply.ts +26 -4
- package/src/tools/builtins/edit.ts +106 -41
- package/src/tools/builtins/glob.ts +1 -1
- package/src/tools/builtins/grep.ts +1 -1
- package/src/tools/builtins/notebook-guard.ts +76 -0
- package/src/tools/builtins/read-render.ts +12 -2
- package/src/tools/builtins/write-file.ts +12 -1
- package/src/tools/coordinator/agent.ts +31 -7
- package/src/tools/coordinator/index.ts +6 -1
- package/src/tools/coordinator/outcome-record.ts +93 -0
- package/src/tools/defineTool.ts +3 -0
- package/src/tools/file-read-tracker.ts +51 -2
- package/src/tools/untrusted-envelope.ts +23 -1
- package/src/toolsets/manager.ts +6 -10
- package/src/toolsets/validation-issues.ts +97 -0
- package/src/turn/memory-recall.ts +28 -0
- package/src/types/agent/base.ts +20 -0
- package/src/types/agent/query.ts +2 -0
- package/src/types/common/index.ts +25 -1
- package/src/types/hitl/index.ts +27 -0
- package/src/types/session/config.ts +20 -0
- package/src/types/session/events.ts +3 -3
- package/src/types/session/index.ts +1 -0
- package/src/types/session/outcome.ts +139 -0
- package/src/types/session/prepare-step.ts +2 -0
- package/src/types/session/records.ts +67 -1
- package/src/types/session/turn.ts +19 -0
- package/src/types/tool/index.ts +30 -0
|
@@ -4,9 +4,48 @@ import { fingerprintContent } from './builtins/content-fingerprint.js'
|
|
|
4
4
|
/** A body larger than this is not kept; the file's change is still seen by fingerprint. */
|
|
5
5
|
const MAX_BODY_CHARS = 512 * 1024
|
|
6
6
|
const MAX_BODIES = 128
|
|
7
|
+
const MAX_AUTHORED = 1024
|
|
7
8
|
|
|
8
|
-
/**
|
|
9
|
-
|
|
9
|
+
/**
|
|
10
|
+
* What a family of agents (a parent and every descendant) wrote through the
|
|
11
|
+
* write and edit tools: path to the fingerprint of the content last put there,
|
|
12
|
+
* and when the ledger began. Shared by the whole family, because the approval
|
|
13
|
+
* classifier asks "did one of us write this script?". It carries no read
|
|
14
|
+
* state, so sharing it can never stand in for one agent's read of a file.
|
|
15
|
+
*/
|
|
16
|
+
export interface AuthoredLedger {
|
|
17
|
+
readonly startedAt: number
|
|
18
|
+
record(key: string, content: string): void
|
|
19
|
+
fingerprint(key: string): string | undefined
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function createAuthoredLedger(): AuthoredLedger {
|
|
23
|
+
const authored = new Map<string, string>()
|
|
24
|
+
return {
|
|
25
|
+
startedAt: Date.now(),
|
|
26
|
+
record(key, content) {
|
|
27
|
+
authored.delete(key)
|
|
28
|
+
authored.set(key, fingerprintContent(content))
|
|
29
|
+
while (authored.size > MAX_AUTHORED) {
|
|
30
|
+
const oldest = authored.keys().next().value
|
|
31
|
+
if (oldest === undefined) break
|
|
32
|
+
authored.delete(oldest)
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
fingerprint: (key) => authored.get(key),
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Host-owned observation ledger. Share only across turns of the same
|
|
41
|
+
* conversation and filesystem, never between agents: what a ledger holds is
|
|
42
|
+
* what ITS reader has read, and the stale-write refusal compares the disk to
|
|
43
|
+
* that. A child gets `tracker.forChild()`: its own reader record, the family's
|
|
44
|
+
* authored ledger.
|
|
45
|
+
*/
|
|
46
|
+
export function createFileReadTracker(options?: {
|
|
47
|
+
readonly authored?: AuthoredLedger
|
|
48
|
+
}): FileReadTracker {
|
|
10
49
|
const paths = new Set<string>()
|
|
11
50
|
const fingerprints = new Map<string, string>()
|
|
12
51
|
const writes = new Map<string, string>()
|
|
@@ -36,6 +75,11 @@ export function createFileReadTracker(): FileReadTracker {
|
|
|
36
75
|
// than the cap keeps no body (its change is still detectable by
|
|
37
76
|
// fingerprint), and the oldest bodies go first.
|
|
38
77
|
const bodies = new Map<string, string>()
|
|
78
|
+
// What the agent family wrote, by fingerprint. Not a body: a classifier
|
|
79
|
+
// reads the file as it is, and uses this only to say whether it is still
|
|
80
|
+
// what an agent wrote. Shared with children; everything else here is not.
|
|
81
|
+
const authored = options?.authored ?? createAuthoredLedger()
|
|
82
|
+
const keepAuthored = (key: string, content: string): void => authored.record(key, content)
|
|
39
83
|
function keepBody(key: string, content: string | undefined): void {
|
|
40
84
|
bodies.delete(key)
|
|
41
85
|
if (content === undefined || content.length > MAX_BODY_CHARS) return
|
|
@@ -63,6 +107,7 @@ export function createFileReadTracker(): FileReadTracker {
|
|
|
63
107
|
// A write supplies the whole body; any other observation without a
|
|
64
108
|
// witness may be a window of the file, which proves nothing about the rest.
|
|
65
109
|
keepBody(key, fullWriteCallId ? content : undefined)
|
|
110
|
+
if (content !== undefined && fullWriteCallId) keepAuthored(key, content)
|
|
66
111
|
if (next !== undefined && fullWriteCallId) {
|
|
67
112
|
writes.set(key, fullWriteCallId)
|
|
68
113
|
// A full body arrived whole in one call, so the chain is empty
|
|
@@ -76,6 +121,9 @@ export function createFileReadTracker(): FileReadTracker {
|
|
|
76
121
|
const MAX_WINDOWS_PER_FILE = 6
|
|
77
122
|
const MAX_WINDOWED_FILES = 512
|
|
78
123
|
return {
|
|
124
|
+
startedAt: authored.startedAt,
|
|
125
|
+
authoredFingerprint: (key) => authored.fingerprint(key),
|
|
126
|
+
forChild: () => createFileReadTracker({ authored }),
|
|
79
127
|
recordReadWindow(key, window, entry) {
|
|
80
128
|
let forFile = windows.get(key)
|
|
81
129
|
if (forFile) windows.delete(key)
|
|
@@ -127,6 +175,7 @@ export function createFileReadTracker(): FileReadTracker {
|
|
|
127
175
|
reads.delete(key)
|
|
128
176
|
fingerprints.set(key, fingerprintContent(content))
|
|
129
177
|
keepBody(key, content)
|
|
178
|
+
keepAuthored(key, content)
|
|
130
179
|
if (!chained) {
|
|
131
180
|
writes.delete(key)
|
|
132
181
|
chains.delete(key)
|
|
@@ -205,6 +205,28 @@ export function wrapUntrusted(
|
|
|
205
205
|
].join('\n')
|
|
206
206
|
}
|
|
207
207
|
|
|
208
|
+
/**
|
|
209
|
+
* The opening words of the one line the kernel writes beside a delegated
|
|
210
|
+
* agent's answer, outside its block, to say what the run did according to its
|
|
211
|
+
* own record (`describeOutcomeRecord`).
|
|
212
|
+
*/
|
|
213
|
+
export const KERNEL_RECORD_PREFIX = 'Record of this run: '
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* `text` without a kernel record line that follows its last block.
|
|
217
|
+
*
|
|
218
|
+
* A reader of the block must still find it when the kernel has said one more
|
|
219
|
+
* thing after it. The line is the kernel's: everything a child produced is
|
|
220
|
+
* inside the block, where this module has already made its delimiter
|
|
221
|
+
* impossible to forge, so a line out here asks the reader to trust nothing
|
|
222
|
+
* that it did not already trust.
|
|
223
|
+
*/
|
|
224
|
+
function withoutKernelRecord(trimmed: string): string {
|
|
225
|
+
const at = trimmed.lastIndexOf(`\n\n${KERNEL_RECORD_PREFIX}`)
|
|
226
|
+
if (at < 0 || trimmed.indexOf('\n', at + 2) >= 0) return trimmed
|
|
227
|
+
return trimmed.slice(0, at)
|
|
228
|
+
}
|
|
229
|
+
|
|
208
230
|
/**
|
|
209
231
|
* The body of a single wrapped block, when `text` is one.
|
|
210
232
|
*
|
|
@@ -246,7 +268,7 @@ export function wrapUntrusted(
|
|
|
246
268
|
* it, which is the one failure this function must not have.
|
|
247
269
|
*/
|
|
248
270
|
export function untrustedEnvelopeBody(text: string): string | undefined {
|
|
249
|
-
const trimmed = text.trim()
|
|
271
|
+
const trimmed = withoutKernelRecord(text.trim())
|
|
250
272
|
const opening = OPENING_TAG_PATTERN.exec(trimmed)
|
|
251
273
|
if (!opening) return undefined
|
|
252
274
|
const nonce = opening[1]
|
package/src/toolsets/manager.ts
CHANGED
|
@@ -35,6 +35,7 @@ import { type Logger, resolveLogger } from '../utils/logger.js'
|
|
|
35
35
|
import { ToolsetConflictError } from './combine.js'
|
|
36
36
|
import type { ToolSourceRef, Toolset, ToolsetAvailability } from './types.js'
|
|
37
37
|
import { toToolSourceRef, toolsetIsReady } from './types.js'
|
|
38
|
+
import { type ValidationIssueLike, describeValidationIssues } from './validation-issues.js'
|
|
38
39
|
|
|
39
40
|
export type { ToolExecutionResult }
|
|
40
41
|
|
|
@@ -984,16 +985,9 @@ Executable tool names, descriptions, and JSON input schemas are attached through
|
|
|
984
985
|
private validationFailure(
|
|
985
986
|
tool: ToolDefinition,
|
|
986
987
|
rawInput: unknown,
|
|
987
|
-
error: {
|
|
988
|
-
readonly issues: readonly {
|
|
989
|
-
readonly path: readonly PropertyKey[]
|
|
990
|
-
readonly message: string
|
|
991
|
-
}[]
|
|
992
|
-
},
|
|
988
|
+
error: { readonly issues: readonly ValidationIssueLike[] },
|
|
993
989
|
): ToolExecutionResult {
|
|
994
|
-
const errorMessage = error.issues
|
|
995
|
-
.map((issue) => `${issue.path.join('.')}: ${issue.message}`)
|
|
996
|
-
.join('; ')
|
|
990
|
+
const errorMessage = describeValidationIssues(error.issues)
|
|
997
991
|
const isEmptyInput =
|
|
998
992
|
rawInput === null ||
|
|
999
993
|
rawInput === undefined ||
|
|
@@ -1006,7 +1000,9 @@ Executable tool names, descriptions, and JSON input schemas are attached through
|
|
|
1006
1000
|
: ''
|
|
1007
1001
|
const enrichedMessage = isEmptyInput
|
|
1008
1002
|
? `Tool "${tool.name}" was called with no arguments. ${requiredHint}${recoveryHint} Retry the call with the required parameters populated.`
|
|
1009
|
-
: `Validation failed for "${tool.name}": ${errorMessage}
|
|
1003
|
+
: `Validation failed for "${tool.name}": ${errorMessage}${
|
|
1004
|
+
errorMessage.includes('\n') ? '\n' : '. '
|
|
1005
|
+
}${requiredHint}${recoveryHint}`
|
|
1010
1006
|
this.log.error('Tool input validation failed', {
|
|
1011
1007
|
'namzu.tool.name': tool.name,
|
|
1012
1008
|
'namzu.registry.errors': errorMessage,
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool-input validation issues as sentences a model can act on.
|
|
3
|
+
*
|
|
4
|
+
* A zod message is written for a developer reading a stack trace:
|
|
5
|
+
* `edits: Too small: expected array to have >=1 items` names neither what to
|
|
6
|
+
* send nor why. A small model that reads it tends to repeat the same call. Each
|
|
7
|
+
* kind of issue a flat tool schema commonly produces is rewritten here to name
|
|
8
|
+
* the field and the fix; anything not recognised keeps its own message, so
|
|
9
|
+
* nothing is lost by this being a short list.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
export interface ValidationIssueLike {
|
|
13
|
+
readonly path: readonly PropertyKey[]
|
|
14
|
+
readonly message: string
|
|
15
|
+
readonly code?: string
|
|
16
|
+
readonly origin?: string
|
|
17
|
+
readonly minimum?: number | bigint
|
|
18
|
+
readonly maximum?: number | bigint
|
|
19
|
+
readonly expected?: string
|
|
20
|
+
readonly keys?: readonly string[]
|
|
21
|
+
readonly values?: readonly unknown[]
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** `edits.0.old_string` as `edits[0].old_string`, the way a model wrote it. */
|
|
25
|
+
export function renderIssuePath(path: readonly PropertyKey[]): string {
|
|
26
|
+
let out = ''
|
|
27
|
+
for (const part of path) {
|
|
28
|
+
if (typeof part === 'number') out += `[${part}]`
|
|
29
|
+
else out += out === '' ? String(part) : `.${String(part)}`
|
|
30
|
+
}
|
|
31
|
+
return out
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function plural(n: number | bigint, noun: string): string {
|
|
35
|
+
return `${n} ${noun}${n === 1 || n === 1n ? '' : 's'}`
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function received(message: string): string | undefined {
|
|
39
|
+
return /received (\w+)$/.exec(message)?.[1]
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** One issue as one sentence, without a trailing period. */
|
|
43
|
+
export function describeValidationIssue(issue: ValidationIssueLike): string {
|
|
44
|
+
const field = renderIssuePath(issue.path)
|
|
45
|
+
const name = field === '' ? 'The input' : `"${field}"`
|
|
46
|
+
const min = issue.minimum
|
|
47
|
+
switch (issue.code) {
|
|
48
|
+
case 'too_small':
|
|
49
|
+
if (issue.origin === 'string' && min !== undefined && Number(min) <= 1) {
|
|
50
|
+
return `${name} must not be empty; send the text itself`
|
|
51
|
+
}
|
|
52
|
+
if (issue.origin === 'string' && min !== undefined) {
|
|
53
|
+
return `${name} is too short; it needs at least ${plural(min, 'character')}`
|
|
54
|
+
}
|
|
55
|
+
if (issue.origin === 'array' && min !== undefined) {
|
|
56
|
+
return `${name} is a list with too few entries; it needs at least ${plural(min, 'entry').replace('entrys', 'entries')}, or leave the field out`
|
|
57
|
+
}
|
|
58
|
+
if (min !== undefined) return `${name} is too small; the smallest accepted is ${min}`
|
|
59
|
+
break
|
|
60
|
+
case 'too_big':
|
|
61
|
+
if (issue.maximum !== undefined) {
|
|
62
|
+
return `${name} is too large; the largest accepted is ${issue.maximum}`
|
|
63
|
+
}
|
|
64
|
+
break
|
|
65
|
+
case 'invalid_type': {
|
|
66
|
+
const got = received(issue.message)
|
|
67
|
+
if (issue.expected !== undefined && got === 'undefined') {
|
|
68
|
+
return `${name} is missing; send it as ${withArticle(issue.expected)}`
|
|
69
|
+
}
|
|
70
|
+
if (issue.expected !== undefined && got !== undefined) {
|
|
71
|
+
return `${name} must be ${withArticle(issue.expected)}, not ${got === 'null' ? 'null' : `a ${got}`}`
|
|
72
|
+
}
|
|
73
|
+
break
|
|
74
|
+
}
|
|
75
|
+
case 'unrecognized_keys':
|
|
76
|
+
if (issue.keys !== undefined && issue.keys.length > 0) {
|
|
77
|
+
const list = issue.keys.map((key) => `"${key}"`).join(', ')
|
|
78
|
+
return `${field === '' ? '' : `${name} has `}${issue.keys.length === 1 ? 'a field' : 'fields'} this tool does not take: ${list}. Remove ${issue.keys.length === 1 ? 'it' : 'them'}`
|
|
79
|
+
}
|
|
80
|
+
break
|
|
81
|
+
case 'invalid_value':
|
|
82
|
+
if (issue.values !== undefined && issue.values.length > 0) {
|
|
83
|
+
return `${name} must be one of ${issue.values.map((v) => JSON.stringify(v)).join(', ')}`
|
|
84
|
+
}
|
|
85
|
+
break
|
|
86
|
+
}
|
|
87
|
+
const message = issue.message.replace(/\.+$/, '')
|
|
88
|
+
return field === '' ? message : `${field}: ${message}`
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function withArticle(type: string): string {
|
|
92
|
+
return /^[aeiou]/.test(type) ? `an ${type}` : `a ${type}`
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function describeValidationIssues(issues: readonly ValidationIssueLike[]): string {
|
|
96
|
+
return issues.map(describeValidationIssue).join('; ')
|
|
97
|
+
}
|
|
@@ -28,6 +28,19 @@ export interface MemoryRecallOptions {
|
|
|
28
28
|
* aged memory names before relying on it. Default one day.
|
|
29
29
|
*/
|
|
30
30
|
readonly ageNoticeAfterMs?: number
|
|
31
|
+
/**
|
|
32
|
+
* Asked once for each record about to be shown (at most `maxMemories` a
|
|
33
|
+
* request), with its id and metadata, never its body. A returned string is
|
|
34
|
+
* added to the record's line as `caution` and to the event's entry, so the
|
|
35
|
+
* model reads that the record may no longer hold; the record is still
|
|
36
|
+
* shown. A host uses it to check what a record names (a file, say) against
|
|
37
|
+
* the project as it is now. It runs inside the recall deadline, so it must
|
|
38
|
+
* be cheap; a throw, or a deadline passing, means no caution.
|
|
39
|
+
*/
|
|
40
|
+
readonly caution?: (record: {
|
|
41
|
+
readonly id: string
|
|
42
|
+
readonly metadata?: Readonly<Record<string, unknown>>
|
|
43
|
+
}) => string | undefined | Promise<string | undefined>
|
|
31
44
|
/** Clock for ages; defaults to `Date.now`. */
|
|
32
45
|
readonly now?: () => number
|
|
33
46
|
/**
|
|
@@ -593,6 +606,19 @@ export function createMemoryRecallStep(options: MemoryRecallOptions): PrepareSte
|
|
|
593
606
|
)
|
|
594
607
|
)
|
|
595
608
|
continue
|
|
609
|
+
let caution: string | undefined
|
|
610
|
+
if (options.caution) {
|
|
611
|
+
try {
|
|
612
|
+
caution = await options.caution({
|
|
613
|
+
id: entry.id,
|
|
614
|
+
...(full.metadata ? { metadata: full.metadata } : {}),
|
|
615
|
+
})
|
|
616
|
+
} catch {
|
|
617
|
+
// A check that failed says nothing about the record.
|
|
618
|
+
}
|
|
619
|
+
if (expired) break
|
|
620
|
+
if (caution !== undefined) caution = clipped(caution, 200)
|
|
621
|
+
}
|
|
596
622
|
const aged = now() - entry.updatedAt > ageNoticeAfterMs
|
|
597
623
|
// The first aged record pays for the notice it brings with it, so
|
|
598
624
|
// a block of fresh records spends nothing on it.
|
|
@@ -614,6 +640,7 @@ export function createMemoryRecallStep(options: MemoryRecallOptions): PrepareSte
|
|
|
614
640
|
),
|
|
615
641
|
updatedAt: entry.updatedAt,
|
|
616
642
|
...(aged ? { age: describeMemoryAge(entry.updatedAt, now()) } : {}),
|
|
643
|
+
...(caution ? { caution } : {}),
|
|
617
644
|
sourceTurn:
|
|
618
645
|
typeof full.metadata?.turnId === 'string'
|
|
619
646
|
? clipped(full.metadata.turnId, 80)
|
|
@@ -632,6 +659,7 @@ export function createMemoryRecallStep(options: MemoryRecallOptions): PrepareSte
|
|
|
632
659
|
title: entry.title,
|
|
633
660
|
origin: memoryOrigin(full.metadata),
|
|
634
661
|
updatedAt: entry.updatedAt,
|
|
662
|
+
...(caution ? { caution } : {}),
|
|
635
663
|
// Without a selector the words chose it and there is nothing to add;
|
|
636
664
|
// with one, say so when it did not.
|
|
637
665
|
...(reasons.has(entry.id) || options.select
|
package/src/types/agent/base.ts
CHANGED
|
@@ -160,6 +160,20 @@ export interface BaseAgentConfig {
|
|
|
160
160
|
*/
|
|
161
161
|
toolResultGuardrails?: readonly import('../guardrail/index.js').ToolResultGuardrailSpec[]
|
|
162
162
|
|
|
163
|
+
/**
|
|
164
|
+
* This agent's own file observation ledger.
|
|
165
|
+
*
|
|
166
|
+
* A delegated child is a fresh turn with its own executor. It starts with
|
|
167
|
+
* its own read record (`FileReadTracker.forChild`), so a write by its
|
|
168
|
+
* parent or a sibling still makes what it read stale, but it shares the
|
|
169
|
+
* delegating turn's record of what the agents wrote: a script its parent
|
|
170
|
+
* wrote, run by the child, is the session's own, so the auto-mode
|
|
171
|
+
* classifier reads it. Stamped by `AgentManager` from
|
|
172
|
+
* `configOverrides.fileReadTracker`, which the delegation tools fill from
|
|
173
|
+
* the turn's own `ToolContext`.
|
|
174
|
+
*/
|
|
175
|
+
fileReadTracker?: import('../tool/index.js').FileReadTracker
|
|
176
|
+
|
|
163
177
|
/**
|
|
164
178
|
* Tools this turn may NOT use, subtracted from whatever it would
|
|
165
179
|
* otherwise have.
|
|
@@ -232,6 +246,10 @@ export interface BaseAgentConfig {
|
|
|
232
246
|
reasoning?: import('../session/config.js').TurnConfig['reasoning']
|
|
233
247
|
/** Ask once for a check of edits before a turn ends; see {@link import('../session/config.js').TurnConfig.completionAudit}. */
|
|
234
248
|
completionAudit?: boolean
|
|
249
|
+
/** Consecutive end-of-turn hook refusals honoured; see {@link import('../session/config.js').TurnConfig.maxHookRefusals}. */
|
|
250
|
+
maxHookRefusals?: number
|
|
251
|
+
/** The outcome record's hooks; see {@link import('../session/config.js').TurnConfig.outcome}. */
|
|
252
|
+
outcome?: import('../session/config.js').TurnConfig['outcome']
|
|
235
253
|
|
|
236
254
|
/**
|
|
237
255
|
* Deduplicate a retried invocation instead of running it twice.
|
|
@@ -411,6 +429,8 @@ export interface BaseAgentResult {
|
|
|
411
429
|
durationMs: number
|
|
412
430
|
messages: Message[]
|
|
413
431
|
result?: string
|
|
432
|
+
/** What the turn changed and what checked it; see {@link import('../session/turn.js').Turn.outcome}. */
|
|
433
|
+
outcome?: import('../session/outcome.js').TurnOutcome
|
|
414
434
|
/**
|
|
415
435
|
* The schema-validated answer, when the turn was configured to produce one.
|
|
416
436
|
*
|
package/src/types/agent/query.ts
CHANGED
|
@@ -67,6 +67,8 @@ export interface QueryAgentConfig extends BaseAgentConfig {
|
|
|
67
67
|
sandboxTeardownTimeoutMs?: number
|
|
68
68
|
/** See {@link QueryParams.outsideRootAccess}. Default `'refuse'`. */
|
|
69
69
|
outsideRootAccess?: 'refuse' | 'review'
|
|
70
|
+
/** See {@link QueryParams.outsideRootRefusal}. */
|
|
71
|
+
outsideRootRefusal?: string
|
|
70
72
|
/** See {@link QueryParams.sandboxEscape}. Default `'refuse'`. */
|
|
71
73
|
sandboxEscape?: 'refuse' | 'review'
|
|
72
74
|
|
|
@@ -12,6 +12,22 @@ export interface TokenUsage {
|
|
|
12
12
|
cachedTokens: number
|
|
13
13
|
cacheWriteTokens: number
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* The whole prompt this call processed, cache reads and cache writes
|
|
17
|
+
* included: the size of the context the model was given.
|
|
18
|
+
*
|
|
19
|
+
* Drivers disagree on what `promptTokens` counts. Some include cached
|
|
20
|
+
* tokens, others report only the uncached remainder,
|
|
21
|
+
* which on a warm cache is a handful of tokens against a context of tens
|
|
22
|
+
* of thousands. Context size, the compaction trigger and the context meter
|
|
23
|
+
* need the whole prompt, so a driver that excludes cache from
|
|
24
|
+
* `promptTokens` reports it here. Absent means `promptTokens` already is
|
|
25
|
+
* the whole prompt; read it through {@link promptSizeOf}.
|
|
26
|
+
*
|
|
27
|
+
* A per-call fact like {@link TokenUsage.cacheTtlMs}: never summed.
|
|
28
|
+
*/
|
|
29
|
+
fullPromptTokens?: number
|
|
30
|
+
|
|
15
31
|
/**
|
|
16
32
|
* Output tokens the model spent on internal reasoning.
|
|
17
33
|
*
|
|
@@ -88,6 +104,11 @@ export interface CacheMiss {
|
|
|
88
104
|
missedTokens?: number
|
|
89
105
|
}
|
|
90
106
|
|
|
107
|
+
/** The whole prompt one call processed: {@link TokenUsage.fullPromptTokens} when the driver reports it, else `promptTokens`. */
|
|
108
|
+
export function promptSizeOf(usage: TokenUsage): number {
|
|
109
|
+
return usage.fullPromptTokens ?? usage.promptTokens
|
|
110
|
+
}
|
|
111
|
+
|
|
91
112
|
export function accumulateTokenUsage(current: TokenUsage, addition: TokenUsage): TokenUsage {
|
|
92
113
|
return {
|
|
93
114
|
promptTokens: current.promptTokens + addition.promptTokens,
|
|
@@ -105,7 +126,7 @@ export function accumulateTokenUsage(current: TokenUsage, addition: TokenUsage):
|
|
|
105
126
|
...(current.webSearchRequests !== undefined || addition.webSearchRequests !== undefined
|
|
106
127
|
? { webSearchRequests: (current.webSearchRequests ?? 0) + (addition.webSearchRequests ?? 0) }
|
|
107
128
|
: {}),
|
|
108
|
-
// `cacheTtlMs`, `cacheWriteTtlTokens`, `speed`, `serviceTier`,
|
|
129
|
+
// `fullPromptTokens`, `cacheTtlMs`, `cacheWriteTtlTokens`, `speed`, `serviceTier`,
|
|
109
130
|
// `inferenceGeo` and `cacheMiss` are per-call facts and are deliberately
|
|
110
131
|
// not carried into a sum.
|
|
111
132
|
}
|
|
@@ -139,6 +160,9 @@ export function mergeTokenUsage(current: TokenUsage, next: TokenUsage): TokenUsa
|
|
|
139
160
|
totalTokens: Math.max(current.totalTokens, next.totalTokens, promptTokens + completionTokens),
|
|
140
161
|
cachedTokens: Math.max(current.cachedTokens, next.cachedTokens),
|
|
141
162
|
cacheWriteTokens: Math.max(current.cacheWriteTokens, next.cacheWriteTokens),
|
|
163
|
+
...(current.fullPromptTokens !== undefined || next.fullPromptTokens !== undefined
|
|
164
|
+
? { fullPromptTokens: Math.max(current.fullPromptTokens ?? 0, next.fullPromptTokens ?? 0) }
|
|
165
|
+
: {}),
|
|
142
166
|
...(current.reasoningTokens !== undefined || next.reasoningTokens !== undefined
|
|
143
167
|
? { reasoningTokens: Math.max(current.reasoningTokens ?? 0, next.reasoningTokens ?? 0) }
|
|
144
168
|
: {}),
|
package/src/types/hitl/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { CostInfo, TokenUsage } from '../common/index.js'
|
|
2
2
|
import type { CheckpointId, PlanId, SessionId, TurnId } from '../ids/index.js'
|
|
3
3
|
import type { Message } from '../message/index.js'
|
|
4
|
+
import type { ShellDialect } from '../tool/index.js'
|
|
4
5
|
|
|
5
6
|
export type { CheckpointId }
|
|
6
7
|
|
|
@@ -179,6 +180,32 @@ export interface ResumeContext {
|
|
|
179
180
|
readonly transcript: () => readonly Message[]
|
|
180
181
|
/** The turn's cancellation signal, when it has one. */
|
|
181
182
|
readonly signal?: AbortSignal
|
|
183
|
+
/**
|
|
184
|
+
* The script files a shell line launches that the agent wrote, or that came
|
|
185
|
+
* into being during the session, read now. Absent when the host keeps no
|
|
186
|
+
* file ledger. `callId` names the call under review, so a change to a file
|
|
187
|
+
* between this read and the run can be noticed when it runs.
|
|
188
|
+
*/
|
|
189
|
+
readonly launchedScripts?: (
|
|
190
|
+
callId: string,
|
|
191
|
+
line: string,
|
|
192
|
+
dialect: ShellDialect,
|
|
193
|
+
) => Promise<readonly LaunchedScript[]>
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/** A script file a reviewed shell line launches, as read for its reviewer. */
|
|
197
|
+
export interface LaunchedScript {
|
|
198
|
+
readonly path: string
|
|
199
|
+
/** `authored`: written through the write or edit tools. `unknown`: it appeared during the session some other way. */
|
|
200
|
+
readonly origin: 'authored' | 'unknown'
|
|
201
|
+
/** `ok`: shown as read. `changed`: no longer what the agent wrote. `unreadable`: the file could not be read. */
|
|
202
|
+
readonly state: 'ok' | 'changed' | 'unreadable'
|
|
203
|
+
/** One plain sentence saying what the reviewer is looking at. */
|
|
204
|
+
readonly note: string
|
|
205
|
+
/** The text shown: the whole file, or its head and tail with the cut marked. */
|
|
206
|
+
readonly content?: string
|
|
207
|
+
/** Characters of the file left out of `content`. */
|
|
208
|
+
readonly cut?: number
|
|
182
209
|
}
|
|
183
210
|
|
|
184
211
|
/**
|
|
@@ -106,6 +106,26 @@ export interface TurnConfig {
|
|
|
106
106
|
* Off when absent, so an embedding host sees no change.
|
|
107
107
|
*/
|
|
108
108
|
completionAudit?: boolean
|
|
109
|
+
/**
|
|
110
|
+
* How many times in a row `turn_end` / `subagent_stop` hooks may refuse to
|
|
111
|
+
* let the turn end (answer `skip`) before the turn stops honouring them. At
|
|
112
|
+
* the limit the turn ends, one notice naming the hook is recorded, and the
|
|
113
|
+
* remaining refusals are ignored. A tool call between refusals resets the
|
|
114
|
+
* count. Off when absent or 0, so an embedding host sees no change; a
|
|
115
|
+
* positive integer sets the limit.
|
|
116
|
+
*/
|
|
117
|
+
maxHookRefusals?: number
|
|
118
|
+
/**
|
|
119
|
+
* The turn's outcome record: what changed, what was run to check it, and
|
|
120
|
+
* whether the last word of each check was said about the workspace as the
|
|
121
|
+
* turn left it (`Turn.outcome`).
|
|
122
|
+
*
|
|
123
|
+
* The record is kept whether or not this is set, from the kernel's own write
|
|
124
|
+
* tools alone. A host that names `detectChanges` gets the workspace itself
|
|
125
|
+
* compared at the start, after each check and at the end, so a change made
|
|
126
|
+
* by a shell command is seen. It never blocks or prolongs a turn.
|
|
127
|
+
*/
|
|
128
|
+
outcome?: import('./outcome.js').TurnOutcomeConfig
|
|
109
129
|
/** Provider-hosted search for this turn. Explicit opt-in; no local network permission. */
|
|
110
130
|
webSearch?: import('../provider/index.js').ChatCompletionParams['webSearch']
|
|
111
131
|
/** Cumulative parent-and-descendant tokens; 0 is unlimited with usage accounting. */
|
|
@@ -24,7 +24,7 @@ import type { FencingToken } from './durable.js'
|
|
|
24
24
|
import type { Lineage } from './lineage.js'
|
|
25
25
|
import type { MessageStopReason, StopReason } from './stop-reason.js'
|
|
26
26
|
import type { SubSessionKind } from './sub-session.js'
|
|
27
|
-
import type { Origin, TurnBudgetBinding, TurnConfigSnapshot
|
|
27
|
+
import type { LiveTurnSettlement, Origin, TurnBudgetBinding, TurnConfigSnapshot } from './turn.js'
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
30
|
* Live events of a session: the stream a host listens to while a turn runs.
|
|
@@ -809,7 +809,7 @@ type CoreSessionEvent =
|
|
|
809
809
|
* value where an honest gap belongs.
|
|
810
810
|
*/
|
|
811
811
|
cancelCause?: CancelCause
|
|
812
|
-
settlement:
|
|
812
|
+
settlement: LiveTurnSettlement
|
|
813
813
|
}
|
|
814
814
|
/**
|
|
815
815
|
* The turn failed. Terminal, like `turn_completed`.
|
|
@@ -853,7 +853,7 @@ type CoreSessionEvent =
|
|
|
853
853
|
* it sends the reader somewhere specific and wrong.
|
|
854
854
|
*/
|
|
855
855
|
explanation?: { id: string; message: string; hint: string }
|
|
856
|
-
settlement:
|
|
856
|
+
settlement: LiveTurnSettlement
|
|
857
857
|
}
|
|
858
858
|
// Additive 2026-07 (provider capability negotiation): emitted once per
|
|
859
859
|
// turn when the request asks for something the provider DRIVER declared
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* How a turn came out, from the kernel's own record of its tool calls.
|
|
5
|
+
*
|
|
6
|
+
* `checked` says a recognised check passed on the workspace as the turn left
|
|
7
|
+
* it. It does not say the check covers the change.
|
|
8
|
+
*/
|
|
9
|
+
export type TurnOutcomeVerdict =
|
|
10
|
+
/** Nothing in the workspace differs from the start of the turn. */
|
|
11
|
+
| 'unchanged'
|
|
12
|
+
/** Changed; at least one check passed on the final state and none is left failing. */
|
|
13
|
+
| 'checked'
|
|
14
|
+
/** Changed; checks passed, but the workspace changed again after the last pass. */
|
|
15
|
+
| 'stale'
|
|
16
|
+
/** Some check's latest run did not pass. Changed or not. */
|
|
17
|
+
| 'failing'
|
|
18
|
+
/** Changed; checks ran but none established a pass (status hidden, background, timed out, or unknown freshness). */
|
|
19
|
+
| 'inconclusive'
|
|
20
|
+
/** Changed; no check ran. */
|
|
21
|
+
| 'unchecked'
|
|
22
|
+
/**
|
|
23
|
+
* Whether anything changed cannot be told: no write tool ran, but a command
|
|
24
|
+
* that may have changed files did, and no workspace detector answered.
|
|
25
|
+
*/
|
|
26
|
+
| 'unknown'
|
|
27
|
+
|
|
28
|
+
/** What one check established. */
|
|
29
|
+
export type TurnOutcomeCheckResult =
|
|
30
|
+
/** Exit status 0, and the status is the check's own. */
|
|
31
|
+
| 'passed'
|
|
32
|
+
/** Non-zero exit, refused, or errored. */
|
|
33
|
+
| 'failed'
|
|
34
|
+
/** The command line lets something else decide the exit status: a pipe, `;`, `||`, a trailing `&`. */
|
|
35
|
+
| 'masked'
|
|
36
|
+
/** Handed to a background job, continued, timed out, or its status was never seen. */
|
|
37
|
+
| 'unsettled'
|
|
38
|
+
|
|
39
|
+
export interface TurnOutcomeCheck {
|
|
40
|
+
/** As run, clipped. */
|
|
41
|
+
readonly command: string
|
|
42
|
+
/** The loop step the latest run of this command finished in. */
|
|
43
|
+
readonly step: number
|
|
44
|
+
readonly result: TurnOutcomeCheckResult
|
|
45
|
+
readonly exitCode?: number
|
|
46
|
+
/**
|
|
47
|
+
* True when the workspace is the same now as when this check finished. False
|
|
48
|
+
* when it is known to have changed since. Absent when that cannot be told.
|
|
49
|
+
*/
|
|
50
|
+
readonly fresh?: boolean
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface TurnOutcome {
|
|
54
|
+
readonly verdict: TurnOutcomeVerdict
|
|
55
|
+
/**
|
|
56
|
+
* How the workspace was observed. `calls`: only the kernel's own write tools
|
|
57
|
+
* were seen, so a change made by a shell command or another tool is invisible
|
|
58
|
+
* and `unchanged` means only that no write tool ran. `workspace`: the host
|
|
59
|
+
* supplied a change detector and the start, each check and the end were
|
|
60
|
+
* compared with it.
|
|
61
|
+
*/
|
|
62
|
+
readonly basis: 'calls' | 'workspace'
|
|
63
|
+
readonly changes: {
|
|
64
|
+
/** Distinct paths the turn's write tools changed, or the number of write calls when some had no path. */
|
|
65
|
+
readonly total: number
|
|
66
|
+
/** The first of them, bounded. */
|
|
67
|
+
readonly paths: readonly string[]
|
|
68
|
+
/** The workspace moved and no write tool call explains it. */
|
|
69
|
+
readonly outsideKnownTools: boolean
|
|
70
|
+
}
|
|
71
|
+
/** In order of first run; the latest run of each distinct command; bounded. */
|
|
72
|
+
readonly checks: readonly TurnOutcomeCheck[]
|
|
73
|
+
/** How many distinct checks ran, when `checks` was cut. */
|
|
74
|
+
readonly checksTotal?: number
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Host hooks for the turn's outcome record. */
|
|
78
|
+
export interface TurnOutcomeConfig {
|
|
79
|
+
/**
|
|
80
|
+
* A fingerprint of the workspace, or `null` when it cannot be told.
|
|
81
|
+
* `fingerprintWorkspace` is the kernel's own. `null` is never a match.
|
|
82
|
+
* Called before the first call of the turn that could change the workspace,
|
|
83
|
+
* after each check, and once when the turn ends; never in a turn whose calls
|
|
84
|
+
* are all provably read-only.
|
|
85
|
+
*/
|
|
86
|
+
readonly detectChanges?: () => Promise<string | null>
|
|
87
|
+
/** Names a command that checks work, beyond the ones the kernel recognises. */
|
|
88
|
+
readonly isCheck?: (command: string) => boolean
|
|
89
|
+
/** Deadline per detector call. A detector that is late is `null`. Default 3000. */
|
|
90
|
+
readonly detectTimeoutMs?: number
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** Bounds on a recorded outcome. */
|
|
94
|
+
export const OUTCOME_MAX_PATHS = 50
|
|
95
|
+
export const OUTCOME_MAX_CHECKS = 20
|
|
96
|
+
export const OUTCOME_MAX_COMMAND_CHARS = 300
|
|
97
|
+
export const OUTCOME_MAX_PATH_CHARS = 300
|
|
98
|
+
export const OUTCOME_DEFAULT_DETECT_TIMEOUT_MS = 3000
|
|
99
|
+
|
|
100
|
+
const verdict = z.enum([
|
|
101
|
+
'unchanged',
|
|
102
|
+
'checked',
|
|
103
|
+
'stale',
|
|
104
|
+
'failing',
|
|
105
|
+
'inconclusive',
|
|
106
|
+
'unchecked',
|
|
107
|
+
'unknown',
|
|
108
|
+
])
|
|
109
|
+
const count = z.number().int().nonnegative().safe()
|
|
110
|
+
|
|
111
|
+
/** The shape a recorded outcome must have to be written to, or read back from, the session log. */
|
|
112
|
+
export const TurnOutcomeSchema: z.ZodType<TurnOutcome, unknown> = z
|
|
113
|
+
.object({
|
|
114
|
+
verdict,
|
|
115
|
+
basis: z.enum(['calls', 'workspace']),
|
|
116
|
+
changes: z
|
|
117
|
+
.object({
|
|
118
|
+
total: count,
|
|
119
|
+
// One more than the bound: a clipped path ends in an ellipsis.
|
|
120
|
+
paths: z.array(z.string().max(OUTCOME_MAX_PATH_CHARS + 1)).max(OUTCOME_MAX_PATHS),
|
|
121
|
+
outsideKnownTools: z.boolean(),
|
|
122
|
+
})
|
|
123
|
+
.strict(),
|
|
124
|
+
checks: z
|
|
125
|
+
.array(
|
|
126
|
+
z
|
|
127
|
+
.object({
|
|
128
|
+
command: z.string().max(OUTCOME_MAX_COMMAND_CHARS + 1),
|
|
129
|
+
step: count,
|
|
130
|
+
result: z.enum(['passed', 'failed', 'masked', 'unsettled']),
|
|
131
|
+
exitCode: z.number().int().safe().optional(),
|
|
132
|
+
fresh: z.boolean().optional(),
|
|
133
|
+
})
|
|
134
|
+
.strict(),
|
|
135
|
+
)
|
|
136
|
+
.max(OUTCOME_MAX_CHECKS),
|
|
137
|
+
checksTotal: count.optional(),
|
|
138
|
+
})
|
|
139
|
+
.strict()
|
|
@@ -285,6 +285,8 @@ export interface RecalledMemory {
|
|
|
285
285
|
* Absent when no selector ran; the words of the request chose it.
|
|
286
286
|
*/
|
|
287
287
|
readonly reason?: string
|
|
288
|
+
/** Set when the host's check found the record may no longer hold, e.g. `may be out of date: a file it names is gone`. */
|
|
289
|
+
readonly caution?: string
|
|
288
290
|
}
|
|
289
291
|
|
|
290
292
|
/**
|