@namzu/sdk 62.6.1 → 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 +29 -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/glob-segment.d.ts +8 -1
- package/dist/sandbox/glob-segment.d.ts.map +1 -1
- package/dist/sandbox/glob-segment.js +28 -9
- package/dist/sandbox/glob-segment.js.map +1 -1
- 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 +1 -1
- 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/glob-segment.ts +48 -24
- 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
|
@@ -3,6 +3,7 @@ import { type TurnRecorder, readFoldedHistory } from '../../manager/session/turn
|
|
|
3
3
|
import type { CheckpointScope, SessionCheckpointStore } from '../../store/checkpoint/index.js'
|
|
4
4
|
import type { SessionLog } from '../../store/session-log/index.js'
|
|
5
5
|
import { restoreSpilledRequestInputs } from '../../store/session-log/spilled-input.js'
|
|
6
|
+
import { readOutcomeProgress } from '../../store/session-log/turn-outcome.js'
|
|
6
7
|
import type { SerializedSpanContext } from '../../telemetry/attributes.js'
|
|
7
8
|
import { NamzuError } from '../../types/errors/index.js'
|
|
8
9
|
import type {
|
|
@@ -84,7 +85,14 @@ type CheckpointState = Pick<
|
|
|
84
85
|
| 'review'
|
|
85
86
|
| 'latestUserMessageId'
|
|
86
87
|
| 'workingState'
|
|
87
|
-
>
|
|
88
|
+
> & {
|
|
89
|
+
/**
|
|
90
|
+
* What the turn's outcome record had gathered. Not a field of the document:
|
|
91
|
+
* it is written as its own `outcome_progress_written` record beside the
|
|
92
|
+
* checkpoint, so a release that validates the document strictly reads it.
|
|
93
|
+
*/
|
|
94
|
+
readonly outcome?: Readonly<Record<string, unknown>>
|
|
95
|
+
}
|
|
88
96
|
|
|
89
97
|
/**
|
|
90
98
|
* A checkpoint read back for a resume: the document, the context it
|
|
@@ -276,6 +284,11 @@ export class CheckpointManager {
|
|
|
276
284
|
private restoredNativeAttempts = 0
|
|
277
285
|
private toolStructuredAttemptsSource?: () => number
|
|
278
286
|
private restoredToolAttempts = 0
|
|
287
|
+
private outcomeSource?: () => Readonly<Record<string, unknown>> | undefined
|
|
288
|
+
private restoredOutcomeState: { resumed: boolean; snapshot?: Readonly<Record<string, unknown>> } =
|
|
289
|
+
{
|
|
290
|
+
resumed: false,
|
|
291
|
+
}
|
|
279
292
|
private lastCreatedId?: CheckpointId
|
|
280
293
|
private traceSource?: () => SerializedSpanContext | undefined
|
|
281
294
|
private parkTtlMs?: number
|
|
@@ -329,6 +342,23 @@ export class CheckpointManager {
|
|
|
329
342
|
return this.restoredToolAttempts
|
|
330
343
|
}
|
|
331
344
|
|
|
345
|
+
/** Wire the turn's outcome record in, so every checkpoint carries what it had gathered. */
|
|
346
|
+
setOutcomeSource(source: () => Readonly<Record<string, unknown>> | undefined): void {
|
|
347
|
+
this.outcomeSource = source
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* What a resumed turn's checkpoint kept of the outcome record. `resumed` is
|
|
352
|
+
* false for a turn that began here; true with no `snapshot` for one resumed
|
|
353
|
+
* from a checkpoint that predates the record.
|
|
354
|
+
*/
|
|
355
|
+
get restoredOutcome(): {
|
|
356
|
+
readonly resumed: boolean
|
|
357
|
+
readonly snapshot?: Readonly<Record<string, unknown>>
|
|
358
|
+
} {
|
|
359
|
+
return this.restoredOutcomeState
|
|
360
|
+
}
|
|
361
|
+
|
|
332
362
|
setStructuredReviewAttemptsSource(source: () => number): void {
|
|
333
363
|
this.structuredReviewAttemptsSource = source
|
|
334
364
|
}
|
|
@@ -417,6 +447,7 @@ export class CheckpointManager {
|
|
|
417
447
|
},
|
|
418
448
|
...(latestUserMessageId ? { latestUserMessageId } : {}),
|
|
419
449
|
...withDefined('workingState', this.workingStateSource?.()),
|
|
450
|
+
...withDefined('outcome', this.outcomeSource?.() ?? this.restoredOutcomeState.snapshot),
|
|
420
451
|
}
|
|
421
452
|
}
|
|
422
453
|
|
|
@@ -449,6 +480,19 @@ export class CheckpointManager {
|
|
|
449
480
|
turnCreatedAt: this.turnCreatedAt ?? new Date(recorder.getTurn().startedAt).toISOString(),
|
|
450
481
|
createdAt: new Date().toISOString(),
|
|
451
482
|
}
|
|
483
|
+
// What the outcome record had gathered goes in its own record, ahead of the
|
|
484
|
+
// one that commits the checkpoint, and is read back by the checkpoint's id.
|
|
485
|
+
// Failing to write it is not failing to checkpoint: a resume without it
|
|
486
|
+
// reports no outcome, never a false `unchanged`.
|
|
487
|
+
if (state.outcome !== undefined) {
|
|
488
|
+
await recorder.appendIgnorableRecord({
|
|
489
|
+
type: 'outcome_progress_written',
|
|
490
|
+
turnId: this.scope.turnId,
|
|
491
|
+
ignorable: true,
|
|
492
|
+
checkpointId: document.checkpointId,
|
|
493
|
+
progress: state.outcome,
|
|
494
|
+
})
|
|
495
|
+
}
|
|
452
496
|
const receipt = await this.store.write(this.scope, document)
|
|
453
497
|
await recorder.appendRecord({
|
|
454
498
|
type: 'checkpoint_written',
|
|
@@ -654,6 +698,14 @@ export class CheckpointManager {
|
|
|
654
698
|
this.restoredNativeAttempts = document.review.nativeStructuredAttempts
|
|
655
699
|
this.restoredToolAttempts = document.review.toolStructuredAttempts ?? 0
|
|
656
700
|
this.restoredReviewAttempts = document.review.structuredAttempts
|
|
701
|
+
await this.recorder.flush()
|
|
702
|
+
const progress = await readOutcomeProgress(this.recorder.log, checkpointId).catch(
|
|
703
|
+
() => undefined,
|
|
704
|
+
)
|
|
705
|
+
this.restoredOutcomeState = {
|
|
706
|
+
resumed: true,
|
|
707
|
+
...(progress !== undefined ? { snapshot: progress } : {}),
|
|
708
|
+
}
|
|
657
709
|
this.restoredUserMessage = restored.latestUserMessage
|
|
658
710
|
this.restoredUserMessageId = restored.latestUserMessage
|
|
659
711
|
? document.latestUserMessageId
|
|
@@ -29,7 +29,11 @@ import { resolveRetainedOutput } from '../../tools/retained-output.js'
|
|
|
29
29
|
import type { ToolManager } from '../../toolsets/manager.js'
|
|
30
30
|
import type { ToolSourceRef } from '../../toolsets/types.js'
|
|
31
31
|
import type { ToolResultGuardrailSpec } from '../../types/guardrail/index.js'
|
|
32
|
-
import type {
|
|
32
|
+
import type {
|
|
33
|
+
LaunchedScript,
|
|
34
|
+
ToolCallDeclined,
|
|
35
|
+
ToolCallEscalation,
|
|
36
|
+
} from '../../types/hitl/index.js'
|
|
33
37
|
import type { SessionId, ToolUseId, TurnId } from '../../types/ids/index.js'
|
|
34
38
|
import type { InvocationState } from '../../types/invocation/index.js'
|
|
35
39
|
import {
|
|
@@ -72,6 +76,12 @@ import { compressShellOutput } from '../../utils/shell-compress.js'
|
|
|
72
76
|
import { parseStructuredResultJson } from '../../utils/structured-result-json.js'
|
|
73
77
|
import { ensureScratchpadDirectory } from '../../utils/temp.js'
|
|
74
78
|
import { type BackgroundJobRegistry, type JobProcess, bindOwner } from '../jobs/registry.js'
|
|
79
|
+
import {
|
|
80
|
+
type ScriptEvidence,
|
|
81
|
+
analyzeLaunches,
|
|
82
|
+
changedSinceRead,
|
|
83
|
+
readLaunchedScripts,
|
|
84
|
+
} from './auto-review/script-evidence.js'
|
|
75
85
|
import { type BatchDetach, stillRunningResult } from './detached-calls.js'
|
|
76
86
|
import {
|
|
77
87
|
type ToolAdmissionHost,
|
|
@@ -378,6 +388,8 @@ export interface ToolExecutorConfig {
|
|
|
378
388
|
outsideRootAccess?: 'refuse' | 'review'
|
|
379
389
|
/** See `QueryParams.sandboxEscape`. Default `'refuse'`. */
|
|
380
390
|
sandboxEscape?: 'refuse' | 'review'
|
|
391
|
+
/** See `QueryParams.outsideRootRefusal`. */
|
|
392
|
+
outsideRootRefusal?: string
|
|
381
393
|
/**
|
|
382
394
|
* Read LIVE, not frozen at turn start.
|
|
383
395
|
*
|
|
@@ -577,6 +589,12 @@ export interface ToolCallOutcome {
|
|
|
577
589
|
/** Rich form for the model, when the tool supplied one. */
|
|
578
590
|
content?: ToolResultContent
|
|
579
591
|
isError?: boolean
|
|
592
|
+
/**
|
|
593
|
+
* What the call was about and what came of it, as typed fields; the same value
|
|
594
|
+
* `tool_completed` carries. Absent when a post-tool hook replaced the output,
|
|
595
|
+
* so a reader never takes an exit status from a result the hook rewrote.
|
|
596
|
+
*/
|
|
597
|
+
facts?: ToolFacts
|
|
580
598
|
/** Provider argument admission failure, never inferred from receipt text. */
|
|
581
599
|
inputFailure?: CompletedToolRecord['inputFailure']
|
|
582
600
|
/** A pre-tool hook deliberately skipped execution; its receipt is not an output candidate. */
|
|
@@ -732,6 +750,9 @@ export class ToolExecutor {
|
|
|
732
750
|
private readonly toolCallBudget?: ToolCallBudget
|
|
733
751
|
private readonly preparedBatches = new WeakSet<PreparedToolBatch>()
|
|
734
752
|
private readonly preparedBashDirectories = new WeakMap<PreparedSafetyCall, string>()
|
|
753
|
+
/** Script files a classifier read for a call, by call id, to be checked again when it runs. */
|
|
754
|
+
private readonly reviewedScripts = new Map<string, ScriptEvidence['watch']>()
|
|
755
|
+
private readonly startedAt = Date.now()
|
|
735
756
|
/** Ephemeral proof of actual execution; restored messages do not acquire it. */
|
|
736
757
|
private readonly recordedObservations = new Map<
|
|
737
758
|
string,
|
|
@@ -982,6 +1003,47 @@ export class ToolExecutor {
|
|
|
982
1003
|
)
|
|
983
1004
|
}
|
|
984
1005
|
|
|
1006
|
+
/**
|
|
1007
|
+
* The script files a shell line launches that the agent wrote or that
|
|
1008
|
+
* appeared during the session, read now for the auto-mode classifier.
|
|
1009
|
+
* Remembers what was read for `callId`, so the file can be checked again
|
|
1010
|
+
* just before the call runs.
|
|
1011
|
+
*/
|
|
1012
|
+
async launchedScripts(
|
|
1013
|
+
callId: string,
|
|
1014
|
+
line: string,
|
|
1015
|
+
dialect: ShellDialect,
|
|
1016
|
+
): Promise<readonly LaunchedScript[]> {
|
|
1017
|
+
if (this.sandboxed) {
|
|
1018
|
+
// A sandbox's files are not on this host's disk. Say so rather than
|
|
1019
|
+
// letting a launch inside one look like a launch of nothing.
|
|
1020
|
+
const analysis = analyzeLaunches(line, dialect)
|
|
1021
|
+
const named = analysis.launches.map((l) => l.path)
|
|
1022
|
+
return named.length === 0 && analysis.unresolved.length === 0
|
|
1023
|
+
? []
|
|
1024
|
+
: [
|
|
1025
|
+
{
|
|
1026
|
+
path: named[0] ?? '(launch form not resolved)',
|
|
1027
|
+
origin: 'unknown',
|
|
1028
|
+
state: 'unreadable',
|
|
1029
|
+
note: 'the command runs in a sandbox whose files are not read for this review, so what the script does is not known',
|
|
1030
|
+
},
|
|
1031
|
+
]
|
|
1032
|
+
}
|
|
1033
|
+
const evidence = await readLaunchedScripts(line, dialect, {
|
|
1034
|
+
cwd: effectiveBashWorkingDirectory(this.config.sessionId, this.config.workingDirectory, {
|
|
1035
|
+
sandboxed: false,
|
|
1036
|
+
}),
|
|
1037
|
+
startedAt: this.fileReadTracker.startedAt ?? this.startedAt,
|
|
1038
|
+
...(this.fileReadTracker.authoredFingerprint
|
|
1039
|
+
? { authoredFingerprint: (key: string) => this.fileReadTracker.authoredFingerprint?.(key) }
|
|
1040
|
+
: {}),
|
|
1041
|
+
})
|
|
1042
|
+
if (evidence.watch.length > 0) this.reviewedScripts.set(callId, evidence.watch)
|
|
1043
|
+
else this.reviewedScripts.delete(callId)
|
|
1044
|
+
return evidence.scripts
|
|
1045
|
+
}
|
|
1046
|
+
|
|
985
1047
|
/** The whole of the evidence as one text, from the same ledger. No filesystem I/O. */
|
|
986
1048
|
describeFileEvidence(messages: readonly Message[]): string | undefined {
|
|
987
1049
|
return describeVisibleFileEvidence(
|
|
@@ -1207,11 +1269,12 @@ export class ToolExecutor {
|
|
|
1207
1269
|
toolCall,
|
|
1208
1270
|
!hostModifiedCallIds?.has(toolCall.id),
|
|
1209
1271
|
)
|
|
1210
|
-
const
|
|
1272
|
+
const beforeReview = await this.refusedBeforeReview(prepared, earlierPaths)
|
|
1211
1273
|
const path = inputPathOf(prepared)
|
|
1212
1274
|
if (path !== undefined) earlierPaths.push(path)
|
|
1275
|
+
this.admitScratchpad(beforeReview)
|
|
1276
|
+
const call = await this.refusedOutsideRoot(beforeReview)
|
|
1213
1277
|
calls.set(toolCall.id, call)
|
|
1214
|
-
this.admitScratchpad(call)
|
|
1215
1278
|
const escalation = await this.escalationOrUnfinished(call)
|
|
1216
1279
|
if (escalation) escalations.set(toolCall.id, escalation)
|
|
1217
1280
|
if (await this.writesScratchpad(call, escalation)) scratchpadWrites.add(toolCall.id)
|
|
@@ -1220,6 +1283,49 @@ export class ToolExecutor {
|
|
|
1220
1283
|
return this.publishPreparedBatch(calls, escalations, scratchpadWrites)
|
|
1221
1284
|
}
|
|
1222
1285
|
|
|
1286
|
+
/**
|
|
1287
|
+
* A file tool's path outside the roots, refused with the host's own words
|
|
1288
|
+
* (`QueryParams.outsideRootRefusal`) instead of the tool's generic one.
|
|
1289
|
+
*
|
|
1290
|
+
* It takes the place of the call like any refusal found while preparing it,
|
|
1291
|
+
* so it holds in every permission mode and reaches no reviewer. Only a turn
|
|
1292
|
+
* that refuses (`outsideRootAccess: 'refuse'`) and has no sandbox is looked
|
|
1293
|
+
* at, and only a tool that declares `pathArgument`. The exemptions are the
|
|
1294
|
+
* review's: the scratch folder is a root, and so is this session's retained
|
|
1295
|
+
* output for `read`.
|
|
1296
|
+
*/
|
|
1297
|
+
private async refusedOutsideRoot(call: PreparedDirectCall): Promise<PreparedDirectCall> {
|
|
1298
|
+
const note = this.config.outsideRootRefusal
|
|
1299
|
+
if (call.kind !== 'ready' || note === undefined) return call
|
|
1300
|
+
if (this.config.outsideRootAccess !== 'refuse' || this.sandboxed) return call
|
|
1301
|
+
const tool = this.config.tools.get(call.toolName)
|
|
1302
|
+
if (tool?.pathArgument === undefined) return call
|
|
1303
|
+
if (call.input === null || typeof call.input !== 'object') return call
|
|
1304
|
+
const value = (call.input as Record<string, unknown>)[tool.pathArgument]
|
|
1305
|
+
if (typeof value !== 'string') return call
|
|
1306
|
+
try {
|
|
1307
|
+
const retained =
|
|
1308
|
+
tool.name === 'read' && (await resolveRetainedOutput(this.outputDirectory(), value))
|
|
1309
|
+
if (retained) return call
|
|
1310
|
+
const outside = await pathOutsideRoots(this.rootsNow(), value)
|
|
1311
|
+
if (outside === undefined) return call
|
|
1312
|
+
return {
|
|
1313
|
+
kind: 'synthetic',
|
|
1314
|
+
toolCall: call.toolCall,
|
|
1315
|
+
toolName: call.toolName,
|
|
1316
|
+
input: call.input,
|
|
1317
|
+
message: formatFailedToolOutput(
|
|
1318
|
+
undefined,
|
|
1319
|
+
`${outside} is outside the working directory. ${note}`,
|
|
1320
|
+
),
|
|
1321
|
+
isError: true,
|
|
1322
|
+
}
|
|
1323
|
+
} catch {
|
|
1324
|
+
this.config.abortSignal.throwIfAborted()
|
|
1325
|
+
return call
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1223
1329
|
/**
|
|
1224
1330
|
* A ready call the tool already knows cannot run, answered now instead of
|
|
1225
1331
|
* after a reviewer has been asked about it (`ToolDefinition.refuseBeforeReview`).
|
|
@@ -1461,12 +1567,13 @@ export class ToolExecutor {
|
|
|
1461
1567
|
const earlierPaths: string[] = []
|
|
1462
1568
|
for (const toolCall of response.message.toolCalls ?? []) {
|
|
1463
1569
|
if (changedCallIds.has(toolCall.id)) {
|
|
1464
|
-
const
|
|
1570
|
+
const beforeReview = await this.refusedBeforeReview(
|
|
1465
1571
|
await prepareDirectCall(this.admissionHost(), toolCall, false),
|
|
1466
1572
|
earlierPaths,
|
|
1467
1573
|
)
|
|
1574
|
+
this.admitScratchpad(beforeReview)
|
|
1575
|
+
const call = await this.refusedOutsideRoot(beforeReview)
|
|
1468
1576
|
calls.set(toolCall.id, call)
|
|
1469
|
-
this.admitScratchpad(call)
|
|
1470
1577
|
const escalation = await this.escalationOrUnfinished(call)
|
|
1471
1578
|
if (escalation) escalations.set(toolCall.id, escalation)
|
|
1472
1579
|
else escalations.delete(toolCall.id)
|
|
@@ -1730,13 +1837,25 @@ export class ToolExecutor {
|
|
|
1730
1837
|
let outcome: ToolCallOutcome
|
|
1731
1838
|
started.add(toolCall.id)
|
|
1732
1839
|
try {
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1840
|
+
const reviewedScripts = this.reviewedScripts.get(toolCall.id)
|
|
1841
|
+
this.reviewedScripts.delete(toolCall.id)
|
|
1842
|
+
const moved =
|
|
1843
|
+
reviewedScripts === undefined ? undefined : await changedSinceRead(reviewedScripts)
|
|
1844
|
+
outcome =
|
|
1845
|
+
moved !== undefined
|
|
1846
|
+
? {
|
|
1847
|
+
toolCallId: toolCall.id,
|
|
1848
|
+
toolName: toolCall.function.name,
|
|
1849
|
+
output: `Error: ${moved} changed after the safety check read it, so the command was not run. Run it again and it will be checked against the new text.`,
|
|
1850
|
+
isError: true,
|
|
1851
|
+
}
|
|
1852
|
+
: await this.executeSingle(
|
|
1853
|
+
toolCall,
|
|
1854
|
+
ctx,
|
|
1855
|
+
recordObservation,
|
|
1856
|
+
() => progress.close(),
|
|
1857
|
+
preparedCall,
|
|
1858
|
+
)
|
|
1740
1859
|
} catch (error) {
|
|
1741
1860
|
// A call already answered with the placeholder has no batch
|
|
1742
1861
|
// left to fail; its failure is its outcome.
|
|
@@ -1932,6 +2051,7 @@ export class ToolExecutor {
|
|
|
1932
2051
|
} catch (error) {
|
|
1933
2052
|
failure ??= { error }
|
|
1934
2053
|
} finally {
|
|
2054
|
+
this.reviewedScripts.delete(unit.toolCall.id)
|
|
1935
2055
|
settledUnit()
|
|
1936
2056
|
inFlight.delete(unit)
|
|
1937
2057
|
pump()
|
|
@@ -2269,7 +2389,7 @@ export class ToolExecutor {
|
|
|
2269
2389
|
toolName: name,
|
|
2270
2390
|
toolUseId: nestedId,
|
|
2271
2391
|
output: rawOutput,
|
|
2272
|
-
maxChars: this.config.
|
|
2392
|
+
maxChars: this.toolOutputCap(this.config.tools.get(name), false),
|
|
2273
2393
|
retainedPreviewChars: this.config.retainedToolPreviewChars,
|
|
2274
2394
|
spillDir: this.outputDirectory(),
|
|
2275
2395
|
recoveryRoutes: this.recoveryRoutes(),
|
|
@@ -2734,11 +2854,7 @@ export class ToolExecutor {
|
|
|
2734
2854
|
]
|
|
2735
2855
|
}
|
|
2736
2856
|
}
|
|
2737
|
-
|
|
2738
|
-
// cap only; a host that set the cap explicitly still gets it.
|
|
2739
|
-
const maxToolOutputChars =
|
|
2740
|
-
this.config.maxToolOutputChars ??
|
|
2741
|
-
(executedDefinition?.selfLimitingOutput === true ? 0 : DEFAULT_MAX_TOOL_OUTPUT_CHARS)
|
|
2857
|
+
const maxToolOutputChars = this.toolOutputCap(executedDefinition, true)
|
|
2742
2858
|
const sourceOutput = output
|
|
2743
2859
|
// This owner-bound channel follows the selected receipt after both result
|
|
2744
2860
|
// screening and post-tool replacement. ToolResult.data and raw input can
|
|
@@ -2967,6 +3083,7 @@ export class ToolExecutor {
|
|
|
2967
3083
|
? { handoff: result.handoff }
|
|
2968
3084
|
: {}),
|
|
2969
3085
|
...(revealedTools !== undefined ? { revealedTools } : {}),
|
|
3086
|
+
...(result.facts !== undefined && !postOverride ? { facts: result.facts } : {}),
|
|
2970
3087
|
}
|
|
2971
3088
|
}
|
|
2972
3089
|
|
|
@@ -3465,6 +3582,8 @@ export class ToolExecutor {
|
|
|
3465
3582
|
preparedCall?: PreparedDirectCall,
|
|
3466
3583
|
declined?: ToolCallDeclined,
|
|
3467
3584
|
): Promise<ToolCallOutcome> {
|
|
3585
|
+
// A call that does not run has no use for what a review read of its scripts.
|
|
3586
|
+
this.reviewedScripts.delete(toolCall.id)
|
|
3468
3587
|
const toolName = preparedCall?.toolName ?? toolCall.function.name
|
|
3469
3588
|
let input: unknown = preparedCall?.input ?? {}
|
|
3470
3589
|
if (!preparedCall) {
|
|
@@ -3623,6 +3742,7 @@ export class ToolExecutor {
|
|
|
3623
3742
|
toolName,
|
|
3624
3743
|
output: outcome.output,
|
|
3625
3744
|
isError: outcome.kind === 'error',
|
|
3745
|
+
...(outcome.facts ? { facts: outcome.facts } : {}),
|
|
3626
3746
|
...(outcome.inputFailure ? { inputFailure: outcome.inputFailure } : {}),
|
|
3627
3747
|
...(outcome.skipped ? { skipped: true as const } : {}),
|
|
3628
3748
|
}
|
|
@@ -3638,6 +3758,26 @@ export class ToolExecutor {
|
|
|
3638
3758
|
})
|
|
3639
3759
|
}
|
|
3640
3760
|
|
|
3761
|
+
/**
|
|
3762
|
+
* The per-result character cap for one tool: the turn's cap, tightened by
|
|
3763
|
+
* the tool's own `maxOutputChars`. A tool that bounds its own output is
|
|
3764
|
+
* exempt from the DEFAULT cap only (`exemptSelfLimiting`); a host that set
|
|
3765
|
+
* the cap explicitly still gets it. 0 means no cap.
|
|
3766
|
+
*/
|
|
3767
|
+
private toolOutputCap(
|
|
3768
|
+
definition: ToolDefinition | undefined,
|
|
3769
|
+
exemptSelfLimiting: boolean,
|
|
3770
|
+
): number {
|
|
3771
|
+
const turn =
|
|
3772
|
+
this.config.maxToolOutputChars ??
|
|
3773
|
+
(exemptSelfLimiting && definition?.selfLimitingOutput === true
|
|
3774
|
+
? 0
|
|
3775
|
+
: DEFAULT_MAX_TOOL_OUTPUT_CHARS)
|
|
3776
|
+
const own = definition?.maxOutputChars
|
|
3777
|
+
if (own === undefined || !Number.isFinite(own) || own <= 0) return turn
|
|
3778
|
+
return turn > 0 ? Math.min(turn, Math.floor(own)) : Math.floor(own)
|
|
3779
|
+
}
|
|
3780
|
+
|
|
3641
3781
|
/**
|
|
3642
3782
|
* The host preview and model text may differ; each gets its own text
|
|
3643
3783
|
* budget. Rich bytes are measured separately and withheld whole when
|
|
@@ -3674,7 +3814,7 @@ export class ToolExecutor {
|
|
|
3674
3814
|
toolName,
|
|
3675
3815
|
toolUseId,
|
|
3676
3816
|
output: text,
|
|
3677
|
-
maxChars: this.config.
|
|
3817
|
+
maxChars: this.toolOutputCap(this.config.tools.get(toolName), false),
|
|
3678
3818
|
...(notice ? { notice } : {}),
|
|
3679
3819
|
// A tool may return different host and model text. They must
|
|
3680
3820
|
// never compete for the same exclusive spill filename.
|
|
@@ -2,7 +2,7 @@ import type { Span } from '@opentelemetry/api'
|
|
|
2
2
|
import { consolidationEntry, isConsolidated } from '../../compaction/consolidation.js'
|
|
3
3
|
import type { WorkingStateManager } from '../../compaction/manager.js'
|
|
4
4
|
import { NAMZU } from '../../constants/telemetry/index.js'
|
|
5
|
-
import type { SessionEvent, StepResult } from '../../types/session/index.js'
|
|
5
|
+
import type { SessionEvent, StepResult, TurnOutcome } from '../../types/session/index.js'
|
|
6
6
|
import { toErrorMessage } from '../../utils/error.js'
|
|
7
7
|
import type { TurnContext } from './context.js'
|
|
8
8
|
import type { EventTranslator } from './events.js'
|
|
@@ -53,6 +53,12 @@ export interface TurnFinalization {
|
|
|
53
53
|
* immediately before the record is written.
|
|
54
54
|
*/
|
|
55
55
|
readonly takeSteps: () => readonly StepResult[]
|
|
56
|
+
/**
|
|
57
|
+
* The turn's outcome record, read here for the same reason as the steps: the
|
|
58
|
+
* workspace is compared once, as late as it can be and before the record is
|
|
59
|
+
* written. Never throws; `undefined` means no record was kept.
|
|
60
|
+
*/
|
|
61
|
+
readonly takeOutcome?: () => Promise<TurnOutcome | undefined>
|
|
56
62
|
/**
|
|
57
63
|
* Whether the loop already fired `turn_end` (and `subagent_stop`) when the
|
|
58
64
|
* model finished, so a hook could keep the turn going. Fired there, it is
|
|
@@ -108,6 +114,7 @@ export async function* finalizeTurn(
|
|
|
108
114
|
// Hand the step record to the turn before it settles, so the
|
|
109
115
|
// returned `Turn` carries it.
|
|
110
116
|
ctx.recorder.setSteps(takeSteps())
|
|
117
|
+
if (finalization.takeOutcome) ctx.recorder.setOutcome(await finalization.takeOutcome())
|
|
111
118
|
|
|
112
119
|
// Gates the FINAL result, not the stream — `text_delta` already
|
|
113
120
|
// reached the host as the model produced it. A rewrite is
|
|
@@ -619,6 +619,14 @@ export interface QueryParams {
|
|
|
619
619
|
* is never, because the path is not mounted there to be reached.
|
|
620
620
|
*/
|
|
621
621
|
outsideRootAccess?: 'refuse' | 'review'
|
|
622
|
+
/**
|
|
623
|
+
* The sentence a host wants the model to read when `outsideRootAccess` is
|
|
624
|
+
* `'refuse'` and a file tool names a path outside the roots, for example
|
|
625
|
+
* which setting made the wall and how to move it. Without it the tool's own
|
|
626
|
+
* generic refusal stands. The refusal is made before any review, so no
|
|
627
|
+
* permission mode (bypass included) lifts it.
|
|
628
|
+
*/
|
|
629
|
+
outsideRootRefusal?: string
|
|
622
630
|
/**
|
|
623
631
|
* Whether a call may ask to run outside the turn's sandbox (the shipped
|
|
624
632
|
* `bash` tool's `dangerously_disable_sandbox`).
|
|
@@ -1599,6 +1607,7 @@ export async function* query(params: QueryParams): AsyncGenerator<SessionEvent,
|
|
|
1599
1607
|
: {}),
|
|
1600
1608
|
...(scratchpadDirectory ? { scratchpadDirectory } : {}),
|
|
1601
1609
|
...(params.outsideRootAccess ? { outsideRootAccess: params.outsideRootAccess } : {}),
|
|
1610
|
+
...(params.outsideRootRefusal ? { outsideRootRefusal: params.outsideRootRefusal } : {}),
|
|
1602
1611
|
...(params.sandboxEscape ? { sandboxEscape: params.sandboxEscape } : {}),
|
|
1603
1612
|
permissionMode: () => ctx.permissionMode.current,
|
|
1604
1613
|
env: turnConfig.env ?? {},
|
|
@@ -2823,6 +2832,9 @@ export async function* query(params: QueryParams): AsyncGenerator<SessionEvent,
|
|
|
2823
2832
|
}
|
|
2824
2833
|
}
|
|
2825
2834
|
|
|
2835
|
+
// The parked batch runs here, outside the loop's own steps, so
|
|
2836
|
+
// the outcome record is told about it here.
|
|
2837
|
+
await iterationOrchestrator.outcomeBefore(pendingResume.response)
|
|
2826
2838
|
const resumedBatch = await applyPendingResume(
|
|
2827
2839
|
pendingResume,
|
|
2828
2840
|
ctx.recorder,
|
|
@@ -2831,6 +2843,11 @@ export async function* query(params: QueryParams): AsyncGenerator<SessionEvent,
|
|
|
2831
2843
|
pendingResumeAssistantId,
|
|
2832
2844
|
)
|
|
2833
2845
|
iterationOrchestrator.setResumedToolResults(resumedBatch.results)
|
|
2846
|
+
await iterationOrchestrator.outcomeObserve(
|
|
2847
|
+
ctx.recorder.currentIteration,
|
|
2848
|
+
pendingResume.response,
|
|
2849
|
+
resumedBatch.results,
|
|
2850
|
+
)
|
|
2834
2851
|
yield* eventTranslator.drainPending()
|
|
2835
2852
|
}
|
|
2836
2853
|
|
|
@@ -2911,6 +2928,7 @@ export async function* query(params: QueryParams): AsyncGenerator<SessionEvent,
|
|
|
2911
2928
|
params,
|
|
2912
2929
|
eventTranslator,
|
|
2913
2930
|
takeSteps: () => iterationOrchestrator.getSteps(),
|
|
2931
|
+
takeOutcome: () => iterationOrchestrator.takeOutcome(),
|
|
2914
2932
|
turnEndHooksFired: () => iterationOrchestrator.turnEndHooksFired,
|
|
2915
2933
|
workingStateManager,
|
|
2916
2934
|
resultAssembler,
|
|
@@ -2933,6 +2951,7 @@ export async function* query(params: QueryParams): AsyncGenerator<SessionEvent,
|
|
|
2933
2951
|
if (isTurnInProgressError(err) && !ctx.recorder.isActive) throw err
|
|
2934
2952
|
// A failed turn still spent its steps; report them.
|
|
2935
2953
|
ctx.recorder.setSteps(iterationOrchestrator.getSteps())
|
|
2954
|
+
ctx.recorder.setOutcome(await iterationOrchestrator.takeOutcome())
|
|
2936
2955
|
await executeUserInterruptHooks(err)
|
|
2937
2956
|
yield* eventTranslator.drainPending()
|
|
2938
2957
|
await executeTurnFailureHooks(err)
|