@namzu/sdk 62.12.0 → 62.13.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 +23 -0
- package/dist/connector/index.d.ts +1 -1
- package/dist/connector/index.d.ts.map +1 -1
- package/dist/connector/index.js +1 -1
- package/dist/connector/index.js.map +1 -1
- package/dist/connector/mcp/client.d.ts +96 -0
- package/dist/connector/mcp/client.d.ts.map +1 -1
- package/dist/connector/mcp/client.js +488 -13
- package/dist/connector/mcp/client.js.map +1 -1
- package/dist/connector/mcp/index.d.ts +1 -0
- package/dist/connector/mcp/index.d.ts.map +1 -1
- package/dist/connector/mcp/index.js +1 -0
- package/dist/connector/mcp/index.js.map +1 -1
- package/dist/connector/mcp/tasks.d.ts +40 -0
- package/dist/connector/mcp/tasks.d.ts.map +1 -0
- package/dist/connector/mcp/tasks.js +55 -0
- package/dist/connector/mcp/tasks.js.map +1 -0
- package/dist/constants/mcp/index.d.ts +1 -1
- package/dist/constants/mcp/index.d.ts.map +1 -1
- package/dist/constants/mcp/index.js +5 -0
- package/dist/constants/mcp/index.js.map +1 -1
- package/dist/eval/bootstrap.d.ts +122 -0
- package/dist/eval/bootstrap.d.ts.map +1 -0
- package/dist/eval/bootstrap.js +227 -0
- package/dist/eval/bootstrap.js.map +1 -0
- package/dist/eval/compare.d.ts +109 -0
- package/dist/eval/compare.d.ts.map +1 -0
- package/dist/eval/compare.js +324 -0
- package/dist/eval/compare.js.map +1 -0
- package/dist/eval/disclosure.d.ts +70 -0
- package/dist/eval/disclosure.d.ts.map +1 -0
- package/dist/eval/disclosure.js +64 -0
- package/dist/eval/disclosure.js.map +1 -0
- package/dist/eval/experiment.d.ts +23 -0
- package/dist/eval/experiment.d.ts.map +1 -1
- package/dist/eval/experiment.js +87 -15
- package/dist/eval/experiment.js.map +1 -1
- package/dist/eval/from-turn.d.ts.map +1 -1
- package/dist/eval/from-turn.js +5 -0
- package/dist/eval/from-turn.js.map +1 -1
- package/dist/eval/index.d.ts +13 -2
- package/dist/eval/index.d.ts.map +1 -1
- package/dist/eval/index.js +6 -1
- package/dist/eval/index.js.map +1 -1
- package/dist/eval/report-stats.d.ts +77 -0
- package/dist/eval/report-stats.d.ts.map +1 -0
- package/dist/eval/report-stats.js +184 -0
- package/dist/eval/report-stats.js.map +1 -0
- package/dist/eval/run-options.d.ts +30 -0
- package/dist/eval/run-options.d.ts.map +1 -0
- package/dist/eval/run-options.js +20 -0
- package/dist/eval/run-options.js.map +1 -0
- package/dist/eval/run-summary.d.ts +36 -0
- package/dist/eval/run-summary.d.ts.map +1 -0
- package/dist/eval/run-summary.js +57 -0
- package/dist/eval/run-summary.js.map +1 -0
- package/dist/eval/scorers.d.ts +32 -0
- package/dist/eval/scorers.d.ts.map +1 -1
- package/dist/eval/scorers.js +77 -0
- package/dist/eval/scorers.js.map +1 -1
- package/dist/eval/types.d.ts +74 -0
- package/dist/eval/types.d.ts.map +1 -1
- package/dist/manager/session/turn-recorder.d.ts +3 -0
- package/dist/manager/session/turn-recorder.d.ts.map +1 -1
- package/dist/manager/session/turn-recorder.js +18 -2
- package/dist/manager/session/turn-recorder.js.map +1 -1
- package/dist/public-runtime.d.ts +5 -2
- package/dist/public-runtime.d.ts.map +1 -1
- package/dist/public-runtime.js +7 -2
- package/dist/public-runtime.js.map +1 -1
- package/dist/public-types.d.ts +2 -0
- package/dist/public-types.d.ts.map +1 -1
- package/dist/registry/command/kernel-commands.d.ts +3 -0
- package/dist/registry/command/kernel-commands.d.ts.map +1 -1
- package/dist/registry/command/kernel-commands.js +76 -5
- package/dist/registry/command/kernel-commands.js.map +1 -1
- package/dist/runtime/query/index.d.ts.map +1 -1
- package/dist/runtime/query/index.js +15 -0
- package/dist/runtime/query/index.js.map +1 -1
- package/dist/runtime/query/iteration/index.d.ts +18 -1
- package/dist/runtime/query/iteration/index.d.ts.map +1 -1
- package/dist/runtime/query/iteration/index.js +80 -1
- package/dist/runtime/query/iteration/index.js.map +1 -1
- package/dist/runtime/query/iteration/phases/tool-review.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/tool-review.js +18 -1
- package/dist/runtime/query/iteration/phases/tool-review.js.map +1 -1
- package/dist/runtime/query/turn/answer-claims.d.ts +52 -0
- package/dist/runtime/query/turn/answer-claims.d.ts.map +1 -0
- package/dist/runtime/query/turn/answer-claims.js +348 -0
- package/dist/runtime/query/turn/answer-claims.js.map +1 -0
- package/dist/runtime/query/turn/outcome-ledger.d.ts +7 -0
- package/dist/runtime/query/turn/outcome-ledger.d.ts.map +1 -1
- package/dist/runtime/query/turn/outcome-ledger.js +13 -0
- package/dist/runtime/query/turn/outcome-ledger.js.map +1 -1
- package/dist/store/goal/index.d.ts +3 -1
- package/dist/store/goal/index.d.ts.map +1 -1
- package/dist/store/goal/index.js +6 -0
- package/dist/store/goal/index.js.map +1 -1
- package/dist/telemetry/runtime-accessors.d.ts +14 -0
- package/dist/telemetry/runtime-accessors.d.ts.map +1 -1
- package/dist/telemetry/runtime-accessors.js +32 -0
- package/dist/telemetry/runtime-accessors.js.map +1 -1
- package/dist/tools/goal/confirmation.d.ts +154 -0
- package/dist/tools/goal/confirmation.d.ts.map +1 -0
- package/dist/tools/goal/confirmation.js +290 -0
- package/dist/tools/goal/confirmation.js.map +1 -0
- package/dist/tools/goal/index.d.ts +4 -1
- package/dist/tools/goal/index.d.ts.map +1 -1
- package/dist/tools/goal/index.js +27 -2
- package/dist/tools/goal/index.js.map +1 -1
- package/dist/tools/hand-back/index.d.ts +48 -0
- package/dist/tools/hand-back/index.d.ts.map +1 -0
- package/dist/tools/hand-back/index.js +129 -0
- package/dist/tools/hand-back/index.js.map +1 -0
- package/dist/tools/schedules/schedule-tool.d.ts.map +1 -1
- package/dist/tools/schedules/schedule-tool.js +15 -1
- package/dist/tools/schedules/schedule-tool.js.map +1 -1
- package/dist/tools/schedules/types.d.ts +10 -0
- package/dist/tools/schedules/types.d.ts.map +1 -1
- package/dist/types/connector/mcp.d.ts +18 -0
- package/dist/types/connector/mcp.d.ts.map +1 -1
- package/dist/types/goal/index.d.ts +12 -0
- package/dist/types/goal/index.d.ts.map +1 -1
- package/dist/types/session/config.d.ts +8 -0
- package/dist/types/session/config.d.ts.map +1 -1
- package/dist/types/session/hand-back.d.ts +29 -0
- package/dist/types/session/hand-back.d.ts.map +1 -0
- package/dist/types/session/hand-back.js +11 -0
- package/dist/types/session/hand-back.js.map +1 -0
- package/dist/types/session/outcome.d.ts +37 -0
- package/dist/types/session/outcome.d.ts.map +1 -1
- package/dist/types/session/outcome.js.map +1 -1
- package/dist/types/session/turn.d.ts +19 -1
- package/dist/types/session/turn.d.ts.map +1 -1
- package/dist/types/session/turn.js.map +1 -1
- package/package.json +1 -1
- package/src/connector/index.ts +1 -0
- package/src/connector/mcp/client.ts +531 -14
- package/src/connector/mcp/index.ts +1 -0
- package/src/connector/mcp/tasks.ts +81 -0
- package/src/constants/mcp/index.ts +6 -1
- package/src/eval/bootstrap.ts +314 -0
- package/src/eval/compare.ts +473 -0
- package/src/eval/disclosure.ts +136 -0
- package/src/eval/experiment.ts +127 -15
- package/src/eval/from-turn.ts +5 -0
- package/src/eval/index.ts +51 -0
- package/src/eval/report-stats.ts +257 -0
- package/src/eval/run-options.ts +49 -0
- package/src/eval/run-summary.ts +104 -0
- package/src/eval/scorers.ts +83 -0
- package/src/eval/types.ts +76 -0
- package/src/manager/session/turn-recorder.ts +23 -2
- package/src/public-runtime.ts +53 -0
- package/src/public-types.ts +9 -0
- package/src/registry/command/kernel-commands.ts +94 -6
- package/src/runtime/query/index.ts +18 -0
- package/src/runtime/query/iteration/index.ts +85 -1
- package/src/runtime/query/iteration/phases/tool-review.ts +24 -1
- package/src/runtime/query/turn/answer-claims.ts +413 -0
- package/src/runtime/query/turn/outcome-ledger.ts +12 -0
- package/src/store/goal/index.ts +11 -0
- package/src/telemetry/runtime-accessors.ts +29 -0
- package/src/tools/goal/confirmation.ts +375 -0
- package/src/tools/goal/index.ts +37 -1
- package/src/tools/hand-back/index.ts +150 -0
- package/src/tools/schedules/schedule-tool.ts +19 -1
- package/src/tools/schedules/types.ts +10 -0
- package/src/types/connector/mcp.ts +18 -0
- package/src/types/goal/index.ts +13 -0
- package/src/types/session/config.ts +8 -0
- package/src/types/session/hand-back.ts +31 -0
- package/src/types/session/outcome.ts +42 -0
- package/src/types/session/turn.ts +19 -1
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { UNKNOWN } from './disclosure.js'
|
|
2
|
+
import type { RunMode } from './disclosure.js'
|
|
3
|
+
import {
|
|
4
|
+
type RunStats,
|
|
5
|
+
describeIntervals,
|
|
6
|
+
describeSplits,
|
|
7
|
+
groupRuns,
|
|
8
|
+
statsOver,
|
|
9
|
+
} from './report-stats.js'
|
|
10
|
+
import type { ExperimentReport } from './types.js'
|
|
11
|
+
|
|
12
|
+
/** What produced a whole run, across every suite in it. */
|
|
13
|
+
export interface RunDisclosure {
|
|
14
|
+
readonly harnessVersion: string
|
|
15
|
+
readonly sdkVersion: string
|
|
16
|
+
readonly generatedAt: string
|
|
17
|
+
readonly commit: string
|
|
18
|
+
readonly seed: number
|
|
19
|
+
/** Repeats per case; the largest across suites when they differ. */
|
|
20
|
+
readonly repeats: number
|
|
21
|
+
readonly suites: number
|
|
22
|
+
readonly cases: number
|
|
23
|
+
readonly runs: number
|
|
24
|
+
/** `mixed` when suites that used a model differ in kind; a suite with no model at all does not count. */
|
|
25
|
+
readonly mode: RunMode | 'mixed'
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** The numbers for every case of every suite taken together. */
|
|
29
|
+
export interface RunSummary extends Omit<RunStats, 'caseSummaries' | 'reliability'> {
|
|
30
|
+
readonly disclosure: RunDisclosure
|
|
31
|
+
/** Present only when cases were repeated. */
|
|
32
|
+
readonly reliability?: RunStats['reliability']
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function summarizeRun(
|
|
36
|
+
entries: ReadonlyArray<{ suite: string; report: ExperimentReport }>,
|
|
37
|
+
context: {
|
|
38
|
+
seed: number
|
|
39
|
+
harnessVersion: string
|
|
40
|
+
commit: string
|
|
41
|
+
generatedAt: string
|
|
42
|
+
sdkVersion: string
|
|
43
|
+
},
|
|
44
|
+
): RunSummary {
|
|
45
|
+
// A case is named by its suite and its id: two suites may reuse a name.
|
|
46
|
+
const cases = entries.flatMap(({ suite, report }) =>
|
|
47
|
+
groupRuns(report.cases ?? [], (name) => `${suite}\u0000${name}`),
|
|
48
|
+
)
|
|
49
|
+
const { caseSummaries: _perCase, reliability, ...stats } = statsOver(cases, context.seed)
|
|
50
|
+
const repeats = Math.max(1, ...entries.map((e) => e.report.repeats ?? 1))
|
|
51
|
+
// A suite that used no model at all says nothing about whether a model
|
|
52
|
+
// ran, so it does not make a run "mixed" with the suites that did.
|
|
53
|
+
const all = entries.map((e) => e.report.disclosure?.mode ?? UNKNOWN)
|
|
54
|
+
const modes = new Set(all.filter((m) => m !== 'no-model'))
|
|
55
|
+
const mode: RunMode | 'mixed' =
|
|
56
|
+
modes.size === 0
|
|
57
|
+
? all.length === 0
|
|
58
|
+
? UNKNOWN
|
|
59
|
+
: 'no-model'
|
|
60
|
+
: modes.size === 1
|
|
61
|
+
? ([...modes][0] as RunMode)
|
|
62
|
+
: 'mixed'
|
|
63
|
+
return {
|
|
64
|
+
...stats,
|
|
65
|
+
...(repeats > 1 ? { reliability } : {}),
|
|
66
|
+
disclosure: {
|
|
67
|
+
harnessVersion: context.harnessVersion,
|
|
68
|
+
sdkVersion: context.sdkVersion,
|
|
69
|
+
generatedAt: context.generatedAt,
|
|
70
|
+
commit: context.commit,
|
|
71
|
+
seed: context.seed,
|
|
72
|
+
repeats,
|
|
73
|
+
suites: entries.length,
|
|
74
|
+
cases: stats.cases,
|
|
75
|
+
runs: stats.runs,
|
|
76
|
+
mode,
|
|
77
|
+
},
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function formatRunSummary(summary: RunSummary): string {
|
|
82
|
+
const d = summary.disclosure
|
|
83
|
+
const lines = [
|
|
84
|
+
`Whole run: ${summary.passed}/${summary.runs} runs passed across ${d.suites} suite${d.suites === 1 ? '' : 's'}, ${summary.cases} case${summary.cases === 1 ? '' : 's'}${d.repeats > 1 ? ` x ${d.repeats} repeats` : ''}`,
|
|
85
|
+
]
|
|
86
|
+
for (const line of describeIntervals({
|
|
87
|
+
mean: summary.mean,
|
|
88
|
+
passRate: summary.passRate,
|
|
89
|
+
intervals: summary.intervals,
|
|
90
|
+
})) {
|
|
91
|
+
lines.push(` ${line}`)
|
|
92
|
+
}
|
|
93
|
+
if (summary.reliability) {
|
|
94
|
+
const r = summary.reliability
|
|
95
|
+
lines.push(
|
|
96
|
+
` reliability across repeats: ${r.passedEveryRepeat} of ${r.cases} cases passed in every one of ${d.repeats} repeats${r.fraction === null ? '' : ` (${(r.fraction * 100).toFixed(1)}%)`}`,
|
|
97
|
+
)
|
|
98
|
+
}
|
|
99
|
+
for (const line of describeSplits(summary.splits)) lines.push(` ${line}`)
|
|
100
|
+
lines.push(
|
|
101
|
+
` produced by: namzu eval ${d.harnessVersion}, sdk ${d.sdkVersion}, commit ${d.commit}, ${d.generatedAt}, ${d.mode}`,
|
|
102
|
+
)
|
|
103
|
+
return lines.join('\n')
|
|
104
|
+
}
|
package/src/eval/scorers.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { HandBackKind } from '../types/session/hand-back.js'
|
|
2
|
+
import type { TurnOutcomeVerdict } from '../types/session/outcome.js'
|
|
1
3
|
import type { EvalCase, EvalTurn, Score, Scorer } from './types.js'
|
|
2
4
|
|
|
3
5
|
/**
|
|
@@ -152,6 +154,87 @@ export function containsScorer(...required: string[]): Scorer {
|
|
|
152
154
|
}
|
|
153
155
|
}
|
|
154
156
|
|
|
157
|
+
/**
|
|
158
|
+
* The turn's outcome record has one of the given verdicts.
|
|
159
|
+
*
|
|
160
|
+
* Reads the record the kernel settled from what actually ran, not the answer's
|
|
161
|
+
* words. A turn with no record scores unavailable: there is nothing to read,
|
|
162
|
+
* which is not the same as a wrong verdict.
|
|
163
|
+
*/
|
|
164
|
+
export function outcomeScorer(verdicts: readonly TurnOutcomeVerdict[]): Scorer {
|
|
165
|
+
return {
|
|
166
|
+
name: 'outcome',
|
|
167
|
+
// The record either says one of the verdicts or it does not.
|
|
168
|
+
severity: 'gate',
|
|
169
|
+
score(turn: EvalTurn): Score {
|
|
170
|
+
const outcome = turn.outcome
|
|
171
|
+
if (!outcome) {
|
|
172
|
+
return { score: 0, unavailable: true, reason: 'the turn has no outcome record' }
|
|
173
|
+
}
|
|
174
|
+
const ok = verdicts.includes(outcome.verdict)
|
|
175
|
+
return {
|
|
176
|
+
score: ok ? 1 : 0,
|
|
177
|
+
reason: ok
|
|
178
|
+
? `verdict ${outcome.verdict}`
|
|
179
|
+
: `verdict ${outcome.verdict}, expected one of ${verdicts.join(', ')}`,
|
|
180
|
+
details: { verdict: outcome.verdict, expected: verdicts },
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Whether the answer's plain check claims agree with the turn's outcome
|
|
188
|
+
* record: 1 when `compareAnswerToOutcome` found no mismatch, 0 when it found
|
|
189
|
+
* one, with the claim and what the record holds in the detail.
|
|
190
|
+
*
|
|
191
|
+
* Pass `{ expectMismatch: true }` for a case that is built to contradict
|
|
192
|
+
* itself, so the suite proves the comparison fires as well as that it stays
|
|
193
|
+
* silent.
|
|
194
|
+
*/
|
|
195
|
+
export function claimMismatchScorer(opts: { expectMismatch?: boolean } = {}): Scorer {
|
|
196
|
+
const expectMismatch = opts.expectMismatch === true
|
|
197
|
+
return {
|
|
198
|
+
name: 'claim-mismatch',
|
|
199
|
+
severity: 'gate',
|
|
200
|
+
score(turn: EvalTurn): Score {
|
|
201
|
+
const found = turn.claimMismatch
|
|
202
|
+
const ok = expectMismatch ? found !== undefined : found === undefined
|
|
203
|
+
return {
|
|
204
|
+
score: ok ? 1 : 0,
|
|
205
|
+
reason: found
|
|
206
|
+
? `${found.said}, but the record: ${found.recordSays}`
|
|
207
|
+
: 'no claim in the answer contradicts the record',
|
|
208
|
+
details: { claimMismatch: found ?? null, expectMismatch },
|
|
209
|
+
}
|
|
210
|
+
},
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Score whether the turn was handed back to a person.
|
|
216
|
+
*
|
|
217
|
+
* Pass `{ expectKind }` for a case built so the model hands the task back, and
|
|
218
|
+
* the scorer checks it did, and for that kind; with nothing it checks the turn
|
|
219
|
+
* was not handed back. A gate: a scripted suite pins the kernel's side of it.
|
|
220
|
+
*/
|
|
221
|
+
export function handedBackScorer(opts: { expectKind?: HandBackKind } = {}): Scorer {
|
|
222
|
+
return {
|
|
223
|
+
name: 'handed-back',
|
|
224
|
+
severity: 'gate',
|
|
225
|
+
score(turn: EvalTurn): Score {
|
|
226
|
+
const found = turn.handedBack
|
|
227
|
+
const ok =
|
|
228
|
+
opts.expectKind === undefined ? found === undefined : found?.kind === opts.expectKind
|
|
229
|
+
return {
|
|
230
|
+
score: ok ? 1 : 0,
|
|
231
|
+
reason: found ? `handed back (${found.kind}): ${found.blocker}` : 'not handed back',
|
|
232
|
+
details: { handedBack: found ?? null, expectKind: opts.expectKind ?? null },
|
|
233
|
+
}
|
|
234
|
+
},
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
155
238
|
/**
|
|
156
239
|
* Judge the turn with a caller-supplied predicate.
|
|
157
240
|
*
|
package/src/eval/types.ts
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
|
+
import type { AnswerClaimMismatch, TurnOutcome } from '../types/session/outcome.js'
|
|
1
2
|
import type { StepResult } from '../types/session/step.js'
|
|
3
|
+
import type { CaseIntervals } from './bootstrap.js'
|
|
4
|
+
import type { ReportDisclosure } from './disclosure.js'
|
|
5
|
+
import type { RepeatReliability, SplitSummary } from './report-stats.js'
|
|
2
6
|
import type { ScoreUncertainty } from './uncertainty.js'
|
|
3
7
|
|
|
8
|
+
/**
|
|
9
|
+
* Which part of a suite a case belongs to.
|
|
10
|
+
*
|
|
11
|
+
* `development` cases are the ones the harness was shaped against; a number
|
|
12
|
+
* measured on them says how well it fits them. `held-out` cases were not
|
|
13
|
+
* looked at while it was shaped, and only they say how it does on cases it
|
|
14
|
+
* has not seen.
|
|
15
|
+
*/
|
|
16
|
+
export type EvalSplit = 'development' | 'held-out'
|
|
17
|
+
|
|
4
18
|
/**
|
|
5
19
|
* One case in a dataset: an input, and what a good run looks like.
|
|
6
20
|
*
|
|
@@ -19,6 +33,11 @@ export interface EvalCase<TInput = unknown> {
|
|
|
19
33
|
expectedTools?: readonly string[]
|
|
20
34
|
/** Per-case scorer overrides; falls back to the dataset's scorers. */
|
|
21
35
|
scorers?: readonly Scorer[]
|
|
36
|
+
/**
|
|
37
|
+
* `development` or `held-out`. Left out, the case is reported as
|
|
38
|
+
* unassigned. See {@link EvalSplit}.
|
|
39
|
+
*/
|
|
40
|
+
split?: EvalSplit
|
|
22
41
|
}
|
|
23
42
|
|
|
24
43
|
/** What actually happened, handed to every scorer. */
|
|
@@ -36,6 +55,18 @@ export interface EvalTurn {
|
|
|
36
55
|
durationMs: number
|
|
37
56
|
/** Set when the turn threw rather than settling. */
|
|
38
57
|
error?: string
|
|
58
|
+
/**
|
|
59
|
+
* What the turn changed and what checked it, from the kernel's own record of
|
|
60
|
+
* its tool calls. Absent for a turn that never ran the loop.
|
|
61
|
+
*/
|
|
62
|
+
outcome?: TurnOutcome
|
|
63
|
+
/**
|
|
64
|
+
* A plain claim in the answer that the outcome record contradicts, found by
|
|
65
|
+
* `compareAnswerToOutcome`. Absent when there is none.
|
|
66
|
+
*/
|
|
67
|
+
claimMismatch?: AnswerClaimMismatch
|
|
68
|
+
/** The model handed the task back to a person and the turn ended there. Absent otherwise. */
|
|
69
|
+
handedBack?: import('../types/session/hand-back.js').HandBack
|
|
39
70
|
}
|
|
40
71
|
|
|
41
72
|
/**
|
|
@@ -143,6 +174,10 @@ export interface CaseResult {
|
|
|
143
174
|
* mattered. Absent when no gate missed.
|
|
144
175
|
*/
|
|
145
176
|
failedGates?: readonly string[]
|
|
177
|
+
/** Which run of this case this is, from 1. Set on every run of a report that has `repeats`. */
|
|
178
|
+
repeat?: number
|
|
179
|
+
/** The case's declared split, copied here so a report can be cut by it. */
|
|
180
|
+
split?: EvalSplit
|
|
146
181
|
}
|
|
147
182
|
|
|
148
183
|
export interface ExperimentReport {
|
|
@@ -177,4 +212,45 @@ export interface ExperimentReport {
|
|
|
177
212
|
*/
|
|
178
213
|
byScorer: Record<string, number>
|
|
179
214
|
durationMs: number
|
|
215
|
+
/**
|
|
216
|
+
* Times each case ran. When it is above 1, `cases` holds every run of
|
|
217
|
+
* every case, in case order, each marked with its `repeat`, and `passed`,
|
|
218
|
+
* `failed` and `inconclusive` count runs. Absent in a report written before
|
|
219
|
+
* repeats existed, which is read as 1.
|
|
220
|
+
*/
|
|
221
|
+
repeats?: number
|
|
222
|
+
/** Seed of the bootstrap behind {@link intervals}. */
|
|
223
|
+
seed?: number
|
|
224
|
+
/**
|
|
225
|
+
* Passed runs over judged runs. An inconclusive run is in neither number,
|
|
226
|
+
* so it cannot pull the rate either way.
|
|
227
|
+
*/
|
|
228
|
+
passRate?: number
|
|
229
|
+
/**
|
|
230
|
+
* 95% intervals for {@link mean} and {@link passRate}, by a bootstrap over
|
|
231
|
+
* cases. Absent from a report that predates it.
|
|
232
|
+
*/
|
|
233
|
+
intervals?: CaseIntervals
|
|
234
|
+
/** Per case: how many of its repeats passed. Present when `repeats` is above 1. */
|
|
235
|
+
caseSummaries?: readonly CaseRepeatSummary[]
|
|
236
|
+
/** Reliability across repeats. Present when `repeats` is above 1. */
|
|
237
|
+
reliability?: RepeatReliability
|
|
238
|
+
/** Numbers per split. Keys appear only for splits that have cases. */
|
|
239
|
+
splits?: Partial<Record<EvalSplit | 'unassigned', SplitSummary>>
|
|
240
|
+
/** What produced this report. */
|
|
241
|
+
disclosure?: ReportDisclosure
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/** How the repeats of one case went. */
|
|
245
|
+
export interface CaseRepeatSummary {
|
|
246
|
+
case: string
|
|
247
|
+
split?: EvalSplit
|
|
248
|
+
repeats: number
|
|
249
|
+
passed: number
|
|
250
|
+
failed: number
|
|
251
|
+
inconclusive: number
|
|
252
|
+
/** Mean score over the judged repeats; null when none was judged. */
|
|
253
|
+
mean: number | null
|
|
254
|
+
/** Every repeat passed. An inconclusive repeat means this is false. */
|
|
255
|
+
passedEveryRepeat: boolean
|
|
180
256
|
}
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
import { EMPTY_TOKEN_USAGE } from '../../constants/limits.js'
|
|
7
7
|
import { NAMZU } from '../../constants/telemetry/index.js'
|
|
8
8
|
import { resolveModelPricing } from '../../pricing/index.js'
|
|
9
|
+
import { compareAnswerToOutcome } from '../../runtime/query/turn/answer-claims.js'
|
|
9
10
|
import type { SessionTokenBudget } from '../../store/budget/index.js'
|
|
10
11
|
import type { SessionCheckpointStore } from '../../store/checkpoint/index.js'
|
|
11
12
|
import { foldSessionLog } from '../../store/session-log/incremental.js'
|
|
@@ -40,6 +41,7 @@ import {
|
|
|
40
41
|
type SessionEventType,
|
|
41
42
|
isEphemeralEvent,
|
|
42
43
|
} from '../../types/session/events.js'
|
|
44
|
+
import type { HandBack } from '../../types/session/hand-back.js'
|
|
43
45
|
import type { ProjectId, TopicId } from '../../types/session/ids.js'
|
|
44
46
|
import type { TurnOutcome } from '../../types/session/outcome.js'
|
|
45
47
|
import type { SessionRecord } from '../../types/session/records.js'
|
|
@@ -516,6 +518,12 @@ export class TurnRecorder {
|
|
|
516
518
|
else this.#turn.outcome = outcome
|
|
517
519
|
}
|
|
518
520
|
|
|
521
|
+
/** The model handed the task back; set once, as the turn ends. */
|
|
522
|
+
setHandedBack(handBack: HandBack | undefined): void {
|
|
523
|
+
if (handBack === undefined) delete this.#turn.handedBack
|
|
524
|
+
else this.#turn.handedBack = handBack
|
|
525
|
+
}
|
|
526
|
+
|
|
519
527
|
setSteps(steps: readonly StepResult[]): void {
|
|
520
528
|
this.#turn.steps = steps
|
|
521
529
|
}
|
|
@@ -612,6 +620,11 @@ export class TurnRecorder {
|
|
|
612
620
|
settlement(status: TurnSettlement['status']): LiveTurnSettlement {
|
|
613
621
|
const turn = this.getTurn()
|
|
614
622
|
const resultMessageId = this.#lastAssistantEntry()?.id
|
|
623
|
+
// A task handed back says what blocks it in the model's words; those are not
|
|
624
|
+
// an answer to read claims out of.
|
|
625
|
+
const claimMismatch = turn.handedBack
|
|
626
|
+
? undefined
|
|
627
|
+
: compareAnswerToOutcome(turn.result, turn.outcome)
|
|
615
628
|
return {
|
|
616
629
|
status,
|
|
617
630
|
iterations: turn.currentIteration,
|
|
@@ -625,6 +638,8 @@ export class TurnRecorder {
|
|
|
625
638
|
abandonedTaskIds: [...(turn.abandonedTaskIds ?? [])],
|
|
626
639
|
abandonedJobIds: [...(turn.abandonedJobIds ?? [])],
|
|
627
640
|
...(turn.outcome ? { outcome: turn.outcome } : {}),
|
|
641
|
+
...(claimMismatch ? { claimMismatch } : {}),
|
|
642
|
+
...(turn.handedBack ? { handedBack: turn.handedBack } : {}),
|
|
628
643
|
}
|
|
629
644
|
}
|
|
630
645
|
|
|
@@ -1560,9 +1575,15 @@ export function eventDraft(
|
|
|
1560
1575
|
...payload
|
|
1561
1576
|
} = event as SessionEvent & Record<string, unknown>
|
|
1562
1577
|
const draft = JSON.parse(JSON.stringify(payload)) as Record<string, unknown>
|
|
1563
|
-
// The outcome
|
|
1578
|
+
// The outcome, the claim mismatch and the hand-back ride the live settlement
|
|
1579
|
+
// only; the log keeps the outcome in its own record, the other two nowhere.
|
|
1564
1580
|
if (draft.settlement !== undefined && typeof draft.settlement === 'object') {
|
|
1565
|
-
const {
|
|
1581
|
+
const {
|
|
1582
|
+
outcome: _outcome,
|
|
1583
|
+
claimMismatch: _claimMismatch,
|
|
1584
|
+
handedBack: _handedBack,
|
|
1585
|
+
...settlement
|
|
1586
|
+
} = draft.settlement as Record<string, unknown>
|
|
1566
1587
|
draft.settlement = settlement
|
|
1567
1588
|
}
|
|
1568
1589
|
return { ...draft, ...(turnId ? { turnId } : {}) } as Parameters<SessionLog['append']>[1]
|
package/src/public-runtime.ts
CHANGED
|
@@ -283,6 +283,14 @@ export {
|
|
|
283
283
|
SESSION_GOAL_TOOL_NAMES,
|
|
284
284
|
buildSessionGoalTools,
|
|
285
285
|
} from './tools/goal/index.js'
|
|
286
|
+
export {
|
|
287
|
+
DEFAULT_GOAL_REFUSALS,
|
|
288
|
+
GoalConfirmation,
|
|
289
|
+
NO_CHECK_REASON_CODE,
|
|
290
|
+
decideGoalConfirmation,
|
|
291
|
+
describeGoalEnding,
|
|
292
|
+
recordSentence,
|
|
293
|
+
} from './tools/goal/confirmation.js'
|
|
286
294
|
export { SessionGoalActivation } from './manager/goal/activation.js'
|
|
287
295
|
export { buildToolResultHashes, hashToolResult } from './utils/hash.js'
|
|
288
296
|
export {
|
|
@@ -902,6 +910,7 @@ export {
|
|
|
902
910
|
isRecognizedModernError,
|
|
903
911
|
isResourceNotFoundError,
|
|
904
912
|
isUnsupportedProtocolVersionError,
|
|
913
|
+
MCP_TASKS_EXTENSION,
|
|
905
914
|
LocalExecutionContext,
|
|
906
915
|
mcpEraCacheKey,
|
|
907
916
|
MCPHttpStatusError,
|
|
@@ -1399,11 +1408,28 @@ export { anyOf, hasToolCall, stepCountIs } from './types/session/step.js'
|
|
|
1399
1408
|
// ─── evaluation harness ──────────────────────────────────────────────────
|
|
1400
1409
|
|
|
1401
1410
|
export {
|
|
1411
|
+
BOOTSTRAP_RESAMPLES,
|
|
1412
|
+
DEFAULT_EVAL_SEED,
|
|
1413
|
+
EvalUsageError,
|
|
1414
|
+
MIN_CASES_FOR_INTERVAL,
|
|
1415
|
+
bootstrapCases,
|
|
1416
|
+
bootstrapPairedDifference,
|
|
1417
|
+
compareEvalReports,
|
|
1418
|
+
exactSignTestP,
|
|
1419
|
+
formatEvalComparison,
|
|
1420
|
+
formatRunSummary,
|
|
1421
|
+
isEvalUsageError,
|
|
1422
|
+
loadEvalReportFile,
|
|
1423
|
+
summarizeRun,
|
|
1424
|
+
withEvalRunOptions,
|
|
1402
1425
|
compareHarnessTrials,
|
|
1403
1426
|
reviewHarnessCandidate,
|
|
1427
|
+
claimMismatchScorer,
|
|
1428
|
+
handedBackScorer,
|
|
1404
1429
|
completionScorer,
|
|
1405
1430
|
containsScorer,
|
|
1406
1431
|
customScorer,
|
|
1432
|
+
outcomeScorer,
|
|
1407
1433
|
evalTurnFromQuery,
|
|
1408
1434
|
evalTurnFromTurn,
|
|
1409
1435
|
formatReport,
|
|
@@ -1413,6 +1439,33 @@ export {
|
|
|
1413
1439
|
trajectoryScorer,
|
|
1414
1440
|
} from './eval/index.js'
|
|
1415
1441
|
|
|
1442
|
+
// ─── answer against record ───────────────────────────────────────────────
|
|
1443
|
+
|
|
1444
|
+
export {
|
|
1445
|
+
ANSWER_CLAIM_MAX_CHARS,
|
|
1446
|
+
ANSWER_CLAIM_MAX_QUOTE_CHARS,
|
|
1447
|
+
compareAnswerToOutcome,
|
|
1448
|
+
describeAnswerClaimMismatch,
|
|
1449
|
+
} from './runtime/query/turn/answer-claims.js'
|
|
1450
|
+
|
|
1451
|
+
// ─── handing the task back ───────────────────────────────────────────────
|
|
1452
|
+
|
|
1453
|
+
export {
|
|
1454
|
+
HAND_BACK_GUIDANCE,
|
|
1455
|
+
HAND_BACK_GUIDANCE_CONTRIBUTION_ID,
|
|
1456
|
+
HAND_BACK_MAX_CHARS,
|
|
1457
|
+
HAND_BACK_RECEIPT,
|
|
1458
|
+
HAND_BACK_SIBLING_REFUSAL,
|
|
1459
|
+
HAND_BACK_TOOL_NAME,
|
|
1460
|
+
HandBackInputSchema,
|
|
1461
|
+
buildHandBackTool,
|
|
1462
|
+
describeHandBack,
|
|
1463
|
+
handBackKindSentence,
|
|
1464
|
+
handBackModelLines,
|
|
1465
|
+
handBackReason,
|
|
1466
|
+
parseHandBack,
|
|
1467
|
+
} from './tools/hand-back/index.js'
|
|
1468
|
+
|
|
1416
1469
|
// ─── metrics ─────────────────────────────────────────────────────────────
|
|
1417
1470
|
//
|
|
1418
1471
|
// Exported so a host can record its own measurements onto the same series
|
package/src/public-types.ts
CHANGED
|
@@ -406,7 +406,16 @@ export type {
|
|
|
406
406
|
SessionGoalStore,
|
|
407
407
|
} from './store/goal/index.js'
|
|
408
408
|
export type { ResolveGoalRoundAuthority } from './tools/goal/index.js'
|
|
409
|
+
export type {
|
|
410
|
+
GoalConfirmationOptions,
|
|
411
|
+
GoalDecision,
|
|
412
|
+
GoalDecisionInput,
|
|
413
|
+
GoalEnding,
|
|
414
|
+
GoalEndingReport,
|
|
415
|
+
GoalStatement,
|
|
416
|
+
} from './tools/goal/confirmation.js'
|
|
409
417
|
export type { ActiveSessionGoal } from './manager/goal/activation.js'
|
|
418
|
+
export type { HandBack, HandBackKind } from './types/session/hand-back.js'
|
|
410
419
|
|
|
411
420
|
export type {
|
|
412
421
|
AgentHandle,
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
StaleGoalError,
|
|
9
9
|
} from '../../store/goal/index.js'
|
|
10
10
|
import type { HostCommandDescriptor } from '../../types/command/index.js'
|
|
11
|
-
import type { GoalRef, SessionGoal } from '../../types/goal/index.js'
|
|
11
|
+
import type { GoalConfirmedBy, GoalRef, SessionGoal } from '../../types/goal/index.js'
|
|
12
12
|
import type { SessionId, TenantId } from '../../types/ids/index.js'
|
|
13
13
|
import type { Skill } from '../../types/skills/index.js'
|
|
14
14
|
import { isInvocableBy, skillInvocation } from '../../types/skills/index.js'
|
|
@@ -48,9 +48,12 @@ export interface GoalCommandScope {
|
|
|
48
48
|
readonly tenantId: TenantId
|
|
49
49
|
/** Process-local permission for automatic continuation. */
|
|
50
50
|
readonly activation?: SessionGoalActivation
|
|
51
|
+
/** What a goal that does not choose is confirmed by; shown by `/goal status`. Default `model`. */
|
|
52
|
+
readonly defaultConfirmedBy?: GoalConfirmedBy
|
|
51
53
|
}
|
|
52
54
|
|
|
53
|
-
const GOAL_ARGUMENTS =
|
|
55
|
+
const GOAL_ARGUMENTS =
|
|
56
|
+
'[status|set [--confirmed-by record|model] <objective>|<objective>|edit <objective>|pause|resume|clear]'
|
|
54
57
|
const GOAL_USAGE = `Usage: /goal ${GOAL_ARGUMENTS}`
|
|
55
58
|
|
|
56
59
|
type ParsedGoalCommand =
|
|
@@ -78,6 +81,60 @@ function parseGoalCommand(args: readonly string[]): ParsedGoalCommand {
|
|
|
78
81
|
return { kind: 'create', objective: input }
|
|
79
82
|
}
|
|
80
83
|
|
|
84
|
+
type ConfirmedByFlag =
|
|
85
|
+
| {
|
|
86
|
+
readonly kind: 'ok'
|
|
87
|
+
readonly args: readonly string[]
|
|
88
|
+
readonly confirmedBy?: GoalConfirmedBy
|
|
89
|
+
}
|
|
90
|
+
| { readonly kind: 'invalid' }
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Takes `--confirmed-by <model|record>` (or `=`) out of the arguments, but only
|
|
94
|
+
* where a flag stands: at the front, right after `set`, or closing the command. In the middle the words
|
|
95
|
+
* belong to the objective, so a goal about the flag itself keeps its text.
|
|
96
|
+
*/
|
|
97
|
+
function takeConfirmedBy(args: readonly string[]): ConfirmedByFlag {
|
|
98
|
+
const head: string[] = []
|
|
99
|
+
let i = 0
|
|
100
|
+
if ((args[0] ?? '').toLowerCase() === 'set') {
|
|
101
|
+
head.push(args[0] ?? '')
|
|
102
|
+
i = 1
|
|
103
|
+
}
|
|
104
|
+
let found: GoalConfirmedBy | undefined
|
|
105
|
+
for (; i < args.length; i++) {
|
|
106
|
+
const arg = args[i] ?? ''
|
|
107
|
+
let value: string
|
|
108
|
+
if (arg === '--confirmed-by') {
|
|
109
|
+
value = args[i + 1] ?? ''
|
|
110
|
+
i++
|
|
111
|
+
} else if (arg.startsWith('--confirmed-by=')) {
|
|
112
|
+
value = arg.slice('--confirmed-by='.length)
|
|
113
|
+
} else {
|
|
114
|
+
break
|
|
115
|
+
}
|
|
116
|
+
if (value !== 'model' && value !== 'record') return { kind: 'invalid' }
|
|
117
|
+
found = value
|
|
118
|
+
}
|
|
119
|
+
let tail = args.slice(i)
|
|
120
|
+
// A flag may also close the command; in the middle its words are the objective's.
|
|
121
|
+
const last = tail[tail.length - 1] ?? ''
|
|
122
|
+
const beforeLast = tail[tail.length - 2] ?? ''
|
|
123
|
+
let closing: string | undefined
|
|
124
|
+
if (last.startsWith('--confirmed-by=')) {
|
|
125
|
+
closing = last.slice('--confirmed-by='.length)
|
|
126
|
+
tail = tail.slice(0, -1)
|
|
127
|
+
} else if (beforeLast === '--confirmed-by') {
|
|
128
|
+
closing = last
|
|
129
|
+
tail = tail.slice(0, -2)
|
|
130
|
+
}
|
|
131
|
+
if (closing !== undefined) {
|
|
132
|
+
if (closing !== 'model' && closing !== 'record') return { kind: 'invalid' }
|
|
133
|
+
found = closing
|
|
134
|
+
}
|
|
135
|
+
return { kind: 'ok', args: [...head, ...tail], ...(found ? { confirmedBy: found } : {}) }
|
|
136
|
+
}
|
|
137
|
+
|
|
81
138
|
function goalRef(goal: SessionGoal): GoalRef {
|
|
82
139
|
return { id: goal.id, revision: goal.revision }
|
|
83
140
|
}
|
|
@@ -92,10 +149,19 @@ function goalCommands(goal: SessionGoal, armed: boolean): string {
|
|
|
92
149
|
return '/goal set <objective>, /goal clear'
|
|
93
150
|
}
|
|
94
151
|
|
|
95
|
-
function renderGoal(
|
|
152
|
+
function renderGoal(
|
|
153
|
+
title: string,
|
|
154
|
+
goal: SessionGoal,
|
|
155
|
+
armed: boolean,
|
|
156
|
+
defaultConfirmedBy: GoalConfirmedBy = 'model',
|
|
157
|
+
): string {
|
|
96
158
|
return [
|
|
97
159
|
title,
|
|
98
160
|
`Status: ${goal.phase}`,
|
|
161
|
+
...(goal.confirmedBy === 'record' ||
|
|
162
|
+
(goal.confirmedBy === undefined && defaultConfirmedBy === 'record')
|
|
163
|
+
? ['Confirmed by: the record of the work']
|
|
164
|
+
: []),
|
|
99
165
|
`Automatic continuation: ${armed ? 'enabled' : 'paused'}`,
|
|
100
166
|
`Automatic turns: ${goal.roundsAdmitted} / ${goal.maxGoalRounds}`,
|
|
101
167
|
...(goal.blockedReason
|
|
@@ -108,7 +174,12 @@ function renderGoal(title: string, goal: SessionGoal, armed: boolean): string {
|
|
|
108
174
|
}
|
|
109
175
|
|
|
110
176
|
function renderScopedGoal(title: string, goal: SessionGoal, scope: GoalCommandScope): string {
|
|
111
|
-
return renderGoal(
|
|
177
|
+
return renderGoal(
|
|
178
|
+
title,
|
|
179
|
+
goal,
|
|
180
|
+
scope.activation?.isArmed(scope.sessionId, goal) ?? false,
|
|
181
|
+
scope.defaultConfirmedBy,
|
|
182
|
+
)
|
|
112
183
|
}
|
|
113
184
|
|
|
114
185
|
function missingGoal(action: string) {
|
|
@@ -125,7 +196,20 @@ async function runGoalCommand(scope: GoalCommandScope | undefined, args: readonl
|
|
|
125
196
|
reason: 'Goals need a durable session. This host did not supply one for the current command.',
|
|
126
197
|
}
|
|
127
198
|
}
|
|
128
|
-
const
|
|
199
|
+
const flag = takeConfirmedBy(args)
|
|
200
|
+
if (flag.kind === 'invalid') {
|
|
201
|
+
return {
|
|
202
|
+
kind: 'refused' as const,
|
|
203
|
+
reason: `--confirmed-by takes record or model.\n${GOAL_USAGE}`,
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
const command = parseGoalCommand(flag.args)
|
|
207
|
+
if (flag.confirmedBy !== undefined && command.kind !== 'create') {
|
|
208
|
+
return {
|
|
209
|
+
kind: 'refused' as const,
|
|
210
|
+
reason: `--confirmed-by is chosen when a goal is set.\n${GOAL_USAGE}`,
|
|
211
|
+
}
|
|
212
|
+
}
|
|
129
213
|
try {
|
|
130
214
|
const current = await scope.store.getGoal(scope.sessionId, scope.tenantId)
|
|
131
215
|
switch (command.kind) {
|
|
@@ -154,7 +238,11 @@ async function runGoalCommand(scope: GoalCommandScope | undefined, args: readonl
|
|
|
154
238
|
}
|
|
155
239
|
}
|
|
156
240
|
const created = await scope.store.createGoal(
|
|
157
|
-
{
|
|
241
|
+
{
|
|
242
|
+
sessionId: scope.sessionId,
|
|
243
|
+
objective: command.objective,
|
|
244
|
+
...(flag.confirmedBy !== undefined ? { confirmedBy: flag.confirmedBy } : {}),
|
|
245
|
+
},
|
|
158
246
|
scope.tenantId,
|
|
159
247
|
)
|
|
160
248
|
scope.activation?.arm(created)
|
|
@@ -40,6 +40,11 @@ import { getTracer } from '../../telemetry/runtime-accessors.js'
|
|
|
40
40
|
import { buildAdvisoryTools } from '../../tools/advisory/index.js'
|
|
41
41
|
import { SearchToolsTool } from '../../tools/builtins/search-tools.js'
|
|
42
42
|
import { createStructuredOutputTool } from '../../tools/builtins/structuredOutput.js'
|
|
43
|
+
import {
|
|
44
|
+
HAND_BACK_GUIDANCE,
|
|
45
|
+
HAND_BACK_GUIDANCE_CONTRIBUTION_ID,
|
|
46
|
+
buildHandBackTool,
|
|
47
|
+
} from '../../tools/hand-back/index.js'
|
|
43
48
|
import { buildTaskTools } from '../../tools/task/index.js'
|
|
44
49
|
import { ToolManager } from '../../toolsets/manager.js'
|
|
45
50
|
import { toolset } from '../../toolsets/toolset.js'
|
|
@@ -1283,6 +1288,10 @@ export async function* query(params: QueryParams): AsyncGenerator<SessionEvent,
|
|
|
1283
1288
|
for (const tool of taskTools) addRuntimeTool(tool, 'deferred')
|
|
1284
1289
|
}
|
|
1285
1290
|
|
|
1291
|
+
// The way out of a task that cannot be done as specified. Only when the host
|
|
1292
|
+
// turned it on; with it off the tool is absent and the prompt is as before.
|
|
1293
|
+
if (turnConfig.handBack === true) addRuntimeTool(buildHandBackTool(), 'active')
|
|
1294
|
+
|
|
1286
1295
|
// `search_tools` is added only when something could actually be
|
|
1287
1296
|
// deferred — one of the runtime tools above, or a caller toolset that
|
|
1288
1297
|
// declared itself `deferred(...)`. Nothing deferred means nothing for
|
|
@@ -1789,6 +1798,15 @@ export async function* query(params: QueryParams): AsyncGenerator<SessionEvent,
|
|
|
1789
1798
|
// The turn's own registry when the host passed none, so a hook's
|
|
1790
1799
|
// annotation has somewhere to land.
|
|
1791
1800
|
const promptContributions = params.promptContributions ?? new PromptContributionRegistry()
|
|
1801
|
+
if (turnConfig.handBack === true) {
|
|
1802
|
+
promptContributions.replace({
|
|
1803
|
+
id: HAND_BACK_GUIDANCE_CONTRIBUTION_ID,
|
|
1804
|
+
placement: 'static',
|
|
1805
|
+
render: () => HAND_BACK_GUIDANCE,
|
|
1806
|
+
})
|
|
1807
|
+
} else {
|
|
1808
|
+
promptContributions.unregister(HAND_BACK_GUIDANCE_CONTRIBUTION_ID)
|
|
1809
|
+
}
|
|
1792
1810
|
|
|
1793
1811
|
const promptBuilder = new PromptBuilder({
|
|
1794
1812
|
systemPrompt: params.systemPrompt,
|