@namzu/sdk 56.2.0 → 57.1.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 +48 -0
- package/dist/agents/QueryAgent.d.ts.map +1 -1
- package/dist/agents/QueryAgent.js +2 -0
- package/dist/agents/QueryAgent.js.map +1 -1
- package/dist/agents/index.d.ts +1 -0
- package/dist/agents/index.d.ts.map +1 -1
- package/dist/agents/index.js +1 -0
- package/dist/agents/index.js.map +1 -1
- package/dist/agents/plan.d.ts +20 -0
- package/dist/agents/plan.d.ts.map +1 -0
- package/dist/agents/plan.js +27 -0
- package/dist/agents/plan.js.map +1 -0
- package/dist/compaction/dangling.d.ts +12 -1
- package/dist/compaction/dangling.d.ts.map +1 -1
- package/dist/compaction/dangling.js +17 -3
- package/dist/compaction/dangling.js.map +1 -1
- package/dist/compaction/index.d.ts +3 -2
- package/dist/compaction/index.d.ts.map +1 -1
- package/dist/compaction/index.js.map +1 -1
- package/dist/compaction/manager.d.ts +16 -0
- package/dist/compaction/manager.d.ts.map +1 -1
- package/dist/compaction/manager.js +55 -0
- package/dist/compaction/manager.js.map +1 -1
- package/dist/compaction/manual.d.ts +9 -0
- package/dist/compaction/manual.d.ts.map +1 -1
- package/dist/compaction/manual.js +64 -16
- package/dist/compaction/manual.js.map +1 -1
- package/dist/compaction/model-summary.d.ts +101 -0
- package/dist/compaction/model-summary.d.ts.map +1 -0
- package/dist/compaction/model-summary.js +333 -0
- package/dist/compaction/model-summary.js.map +1 -0
- package/dist/compaction/restore.d.ts +51 -0
- package/dist/compaction/restore.d.ts.map +1 -0
- package/dist/compaction/restore.js +123 -0
- package/dist/compaction/restore.js.map +1 -0
- package/dist/compaction/types.d.ts +22 -0
- package/dist/compaction/types.d.ts.map +1 -1
- package/dist/compaction/verifier.d.ts +6 -1
- package/dist/compaction/verifier.d.ts.map +1 -1
- package/dist/compaction/verifier.js +3 -3
- package/dist/compaction/verifier.js.map +1 -1
- package/dist/compaction/wire.d.ts +3 -1
- package/dist/compaction/wire.d.ts.map +1 -1
- package/dist/compaction/wire.js +16 -0
- package/dist/compaction/wire.js.map +1 -1
- package/dist/config/runtime.d.ts +62 -0
- package/dist/config/runtime.d.ts.map +1 -1
- package/dist/config/runtime.js +16 -0
- package/dist/config/runtime.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/plugin/shell-hook.d.ts +19 -1
- package/dist/plugin/shell-hook.d.ts.map +1 -1
- package/dist/plugin/shell-hook.js +21 -5
- package/dist/plugin/shell-hook.js.map +1 -1
- package/dist/prompt/coding-agent-doctrine.d.ts +2 -2
- 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/errors.d.ts +3 -0
- package/dist/provider/errors.d.ts.map +1 -1
- package/dist/provider/errors.js +23 -3
- package/dist/provider/errors.js.map +1 -1
- package/dist/provider/fallback.d.ts.map +1 -1
- package/dist/provider/fallback.js +34 -3
- package/dist/provider/fallback.js.map +1 -1
- package/dist/provider/retry.d.ts +12 -5
- package/dist/provider/retry.d.ts.map +1 -1
- package/dist/provider/retry.js +22 -8
- package/dist/provider/retry.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 +4 -1
- package/dist/public-runtime.js.map +1 -1
- package/dist/public-types.d.ts +1 -1
- package/dist/public-types.d.ts.map +1 -1
- package/dist/runtime/jobs/registry.d.ts +42 -0
- package/dist/runtime/jobs/registry.d.ts.map +1 -1
- package/dist/runtime/jobs/registry.js +77 -0
- package/dist/runtime/jobs/registry.js.map +1 -1
- package/dist/runtime/query/calls-that-never-started.d.ts +12 -0
- package/dist/runtime/query/calls-that-never-started.d.ts.map +1 -0
- package/dist/runtime/query/calls-that-never-started.js +41 -0
- package/dist/runtime/query/calls-that-never-started.js.map +1 -0
- package/dist/runtime/query/executor/tool-call-admission.d.ts +2 -2
- package/dist/runtime/query/executor/tool-call-admission.d.ts.map +1 -1
- package/dist/runtime/query/executor/tool-call-admission.js +13 -9
- package/dist/runtime/query/executor/tool-call-admission.js.map +1 -1
- package/dist/runtime/query/executor.d.ts +11 -0
- package/dist/runtime/query/executor.d.ts.map +1 -1
- package/dist/runtime/query/executor.js +48 -0
- package/dist/runtime/query/executor.js.map +1 -1
- package/dist/runtime/query/file-evidence-context.js +1 -1
- package/dist/runtime/query/file-evidence-context.js.map +1 -1
- package/dist/runtime/query/index.d.ts +12 -0
- package/dist/runtime/query/index.d.ts.map +1 -1
- package/dist/runtime/query/index.js +9 -0
- package/dist/runtime/query/index.js.map +1 -1
- package/dist/runtime/query/iteration/phases/compaction.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/compaction.js +66 -1
- package/dist/runtime/query/iteration/phases/compaction.js.map +1 -1
- package/dist/runtime/query/iteration/phases/context.d.ts +5 -0
- package/dist/runtime/query/iteration/phases/context.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/context.js.map +1 -1
- package/dist/runtime/query/iteration/stream-turn.d.ts.map +1 -1
- package/dist/runtime/query/iteration/stream-turn.js +45 -25
- package/dist/runtime/query/iteration/stream-turn.js.map +1 -1
- package/dist/runtime/query/prepare-turn.d.ts.map +1 -1
- package/dist/runtime/query/prepare-turn.js +10 -1
- package/dist/runtime/query/prepare-turn.js.map +1 -1
- package/dist/scheduler/completion-inbox.js +3 -3
- package/dist/scheduler/completion-inbox.js.map +1 -1
- package/dist/scheduler/local.js +1 -1
- package/dist/scheduler/local.js.map +1 -1
- package/dist/tools/admit-facts.d.ts.map +1 -1
- package/dist/tools/admit-facts.js +1 -0
- package/dist/tools/admit-facts.js.map +1 -1
- package/dist/tools/builtins/bash.d.ts +42 -1
- package/dist/tools/builtins/bash.d.ts.map +1 -1
- package/dist/tools/builtins/bash.js +115 -48
- package/dist/tools/builtins/bash.js.map +1 -1
- package/dist/tools/builtins/read-file.d.ts.map +1 -1
- package/dist/tools/builtins/read-file.js +55 -5
- package/dist/tools/builtins/read-file.js.map +1 -1
- package/dist/tools/file-read-tracker.d.ts.map +1 -1
- package/dist/tools/file-read-tracker.js +28 -0
- package/dist/tools/file-read-tracker.js.map +1 -1
- package/dist/types/agent/base.d.ts +14 -0
- package/dist/types/agent/base.d.ts.map +1 -1
- package/dist/types/agent/scheduler.d.ts +8 -0
- package/dist/types/agent/scheduler.d.ts.map +1 -1
- package/dist/types/errors/catalog.d.ts +3 -0
- package/dist/types/errors/catalog.d.ts.map +1 -1
- package/dist/types/errors/catalog.js +24 -2
- package/dist/types/errors/catalog.js.map +1 -1
- package/dist/types/plugin/index.d.ts +2 -2
- package/dist/types/provider/error.d.ts +7 -0
- package/dist/types/provider/error.d.ts.map +1 -1
- package/dist/types/provider/errors.d.ts +24 -0
- package/dist/types/provider/errors.d.ts.map +1 -1
- package/dist/types/provider/errors.js +101 -1
- package/dist/types/provider/errors.js.map +1 -1
- package/dist/types/provider/stream.d.ts +6 -0
- package/dist/types/provider/stream.d.ts.map +1 -1
- package/dist/types/session/events.d.ts +8 -0
- package/dist/types/session/events.d.ts.map +1 -1
- package/dist/types/session/events.js.map +1 -1
- package/dist/types/tool/index.d.ts +54 -0
- package/dist/types/tool/index.d.ts.map +1 -1
- package/dist/types/tool/index.js.map +1 -1
- package/dist/types/tool/presentation.d.ts +4 -0
- package/dist/types/tool/presentation.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/agents/QueryAgent.ts +2 -0
- package/src/agents/index.ts +1 -0
- package/src/agents/plan.ts +30 -0
- package/src/compaction/dangling.ts +35 -3
- package/src/compaction/index.ts +5 -0
- package/src/compaction/manager.ts +53 -0
- package/src/compaction/manual.ts +110 -40
- package/src/compaction/model-summary.ts +412 -0
- package/src/compaction/restore.ts +159 -0
- package/src/compaction/types.ts +25 -0
- package/src/compaction/verifier.ts +3 -3
- package/src/compaction/wire.ts +26 -1
- package/src/config/runtime.ts +16 -0
- package/src/index.ts +2 -0
- package/src/plugin/shell-hook.ts +33 -5
- package/src/prompt/coding-agent-doctrine.ts +5 -3
- package/src/provider/errors.ts +27 -3
- package/src/provider/fallback.ts +34 -2
- package/src/provider/retry.ts +30 -10
- package/src/public-runtime.ts +12 -0
- package/src/public-types.ts +3 -0
- package/src/runtime/jobs/registry.ts +105 -0
- package/src/runtime/query/calls-that-never-started.ts +37 -0
- package/src/runtime/query/executor/tool-call-admission.ts +12 -9
- package/src/runtime/query/executor.ts +51 -0
- package/src/runtime/query/file-evidence-context.ts +1 -1
- package/src/runtime/query/index.ts +23 -0
- package/src/runtime/query/iteration/phases/compaction.ts +65 -1
- package/src/runtime/query/iteration/phases/context.ts +7 -0
- package/src/runtime/query/iteration/stream-turn.ts +47 -25
- package/src/runtime/query/prepare-turn.ts +10 -1
- package/src/scheduler/completion-inbox.ts +3 -3
- package/src/scheduler/local.ts +1 -1
- package/src/tools/admit-facts.ts +1 -0
- package/src/tools/builtins/bash.ts +149 -54
- package/src/tools/builtins/read-file.ts +66 -4
- package/src/tools/file-read-tracker.ts +25 -1
- package/src/types/agent/base.ts +16 -0
- package/src/types/agent/scheduler.ts +9 -0
- package/src/types/errors/catalog.ts +31 -2
- package/src/types/provider/error.ts +7 -0
- package/src/types/provider/errors.ts +120 -1
- package/src/types/provider/stream.ts +6 -0
- package/src/types/session/events.ts +8 -0
- package/src/types/tool/index.ts +56 -0
- package/src/types/tool/presentation.ts +4 -0
|
@@ -6,6 +6,7 @@ import { unknownProgramInLine } from '../../authorization/program.js'
|
|
|
6
6
|
import { type SkillGrantSet, compileSkillGrant } from '../../authorization/skill-grant.js'
|
|
7
7
|
import { extractFromToolCall, extractFromToolResult } from '../../compaction/extractor.js'
|
|
8
8
|
import type { WorkingStateManager } from '../../compaction/manager.js'
|
|
9
|
+
import { recordRestorable } from '../../compaction/restore.js'
|
|
9
10
|
import { GENAI, NAMZU } from '../../constants/telemetry/index.js'
|
|
10
11
|
import type { PluginLifecycleManager } from '../../plugin/lifecycle.js'
|
|
11
12
|
import { buildProbeContext } from '../../probe/context.js'
|
|
@@ -13,6 +14,7 @@ import { ProbeVetoError } from '../../probe/errors.js'
|
|
|
13
14
|
import { probe as defaultProbeRegistry } from '../../probe/registry.js'
|
|
14
15
|
import type { ProbeEnforcement } from '../../probe/registry.js'
|
|
15
16
|
import type { ActivityStore } from '../../store/activity/memory.js'
|
|
17
|
+
import { fingerprintContent } from '../../tools/builtins/content-fingerprint.js'
|
|
16
18
|
import { sandboxShellSpawn, withoutBashStartup } from '../../tools/command-shell.js'
|
|
17
19
|
import { isAlwaysDestructive } from '../../tools/defineTool.js'
|
|
18
20
|
import { createFileReadTracker } from '../../tools/file-read-tracker.js'
|
|
@@ -700,6 +702,8 @@ export class ToolExecutor {
|
|
|
700
702
|
private emitEvent: EmitEvent
|
|
701
703
|
private log: Logger
|
|
702
704
|
private workingStateManager?: WorkingStateManager
|
|
705
|
+
private historyView?: () => readonly Message[]
|
|
706
|
+
private systemPromptView?: () => string | undefined
|
|
703
707
|
private probes: ProbeEnforcement
|
|
704
708
|
private parentSpan?: Span
|
|
705
709
|
private readonly toolCallBudget?: ToolCallBudget
|
|
@@ -749,6 +753,35 @@ export class ToolExecutor {
|
|
|
749
753
|
this.workingStateManager = manager
|
|
750
754
|
}
|
|
751
755
|
|
|
756
|
+
/**
|
|
757
|
+
* Read the conversation the model is shown, as it is now. Lets a tool ask
|
|
758
|
+
* whether an earlier receipt is still there (see `ToolContext.receiptVisible`)
|
|
759
|
+
* without being handed the history.
|
|
760
|
+
*/
|
|
761
|
+
setHistoryView(view: () => readonly Message[]): void {
|
|
762
|
+
this.historyView = view
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
/** The system prompt assembled for the current iteration, for `ToolContext.conversation`. */
|
|
766
|
+
setSystemPromptView(view: () => string | undefined): void {
|
|
767
|
+
this.systemPromptView = view
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
private receiptVisible = (callId: string, renderedFingerprint: string): boolean => {
|
|
771
|
+
const history = this.historyView?.()
|
|
772
|
+
if (!history) return false
|
|
773
|
+
for (let index = history.length - 1; index >= 0; index -= 1) {
|
|
774
|
+
const message = history[index]
|
|
775
|
+
if (message?.role !== 'tool' || message.toolCallId !== callId) continue
|
|
776
|
+
return (
|
|
777
|
+
typeof message.content === 'string' &&
|
|
778
|
+
message.isError !== true &&
|
|
779
|
+
fingerprintContent(message.content) === renderedFingerprint
|
|
780
|
+
)
|
|
781
|
+
}
|
|
782
|
+
return false
|
|
783
|
+
}
|
|
784
|
+
|
|
752
785
|
setSandbox(sandbox: Sandbox): void {
|
|
753
786
|
this.config = { ...this.config, sandbox }
|
|
754
787
|
this.sandboxGate = undefined
|
|
@@ -2185,6 +2218,15 @@ export class ToolExecutor {
|
|
|
2185
2218
|
this.dispatchNested(name, input, context, recordObservation, undefined, options),
|
|
2186
2219
|
sandbox: this.config.sandbox,
|
|
2187
2220
|
fileReadTracker: this.fileReadTracker,
|
|
2221
|
+
receiptVisible: this.receiptVisible,
|
|
2222
|
+
...(this.historyView
|
|
2223
|
+
? {
|
|
2224
|
+
conversation: {
|
|
2225
|
+
messages: () => this.historyView?.() ?? [],
|
|
2226
|
+
systemPrompt: () => this.systemPromptView?.(),
|
|
2227
|
+
},
|
|
2228
|
+
}
|
|
2229
|
+
: {}),
|
|
2188
2230
|
...(this.config.codeAnalysis ? { codeAnalysis: this.config.codeAnalysis } : {}),
|
|
2189
2231
|
// Bound to this turn, once. Binding here rather than passing the
|
|
2190
2232
|
// owner from the tool is what makes the scoping structural: there
|
|
@@ -2584,6 +2626,15 @@ export class ToolExecutor {
|
|
|
2584
2626
|
|
|
2585
2627
|
if (this.workingStateManager) {
|
|
2586
2628
|
extractFromToolResult(this.workingStateManager, toolName, output, effectiveIsError)
|
|
2629
|
+
// What the model was shown, kept whole so a compaction can put it back.
|
|
2630
|
+
recordRestorable(this.workingStateManager, {
|
|
2631
|
+
toolName,
|
|
2632
|
+
input,
|
|
2633
|
+
output: typeof modelContent === 'string' ? modelContent : output,
|
|
2634
|
+
success: !effectiveIsError,
|
|
2635
|
+
unchanged: result.facts?.kind === 'read' && result.facts.unchanged === true,
|
|
2636
|
+
workingDirectory: this.config.workingDirectory,
|
|
2637
|
+
})
|
|
2587
2638
|
for (const pin of this.config.abortSignal.aborted ? [] : (result.workingState ?? [])) {
|
|
2588
2639
|
this.workingStateManager.pin(pin.key, pin.text, toolName)
|
|
2589
2640
|
}
|
|
@@ -149,7 +149,7 @@ export function recordFileEvidence(
|
|
|
149
149
|
const lead =
|
|
150
150
|
recorded.length === 0
|
|
151
151
|
? `${FILE_EVIDENCE_HEADING}: ${FILE_EVIDENCE_GUIDANCE}`
|
|
152
|
-
: `${FILE_EVIDENCE_HEADING}, changed since the last entry. An entry for a path replaces the earlier entries for that path.`
|
|
152
|
+
: `${FILE_EVIDENCE_HEADING}, changed since the last entry; \`bodyInCall\` and \`observedFingerprint\` read as before. An entry for a path replaces the earlier entries for that path.`
|
|
153
153
|
return [
|
|
154
154
|
lead,
|
|
155
155
|
...(gone.length > 0 ? [`${NO_LONGER_HOLDS}${JSON.stringify(gone)}`] : []),
|
|
@@ -10,6 +10,7 @@ import { SkillGrantSet } from '../../authorization/skill-grant.js'
|
|
|
10
10
|
import { repairToolMessageHistory, toolHistoryRepairChanged } from '../../compaction/dangling.js'
|
|
11
11
|
import { extractFromUserMessage } from '../../compaction/extractor.js'
|
|
12
12
|
import { WorkingStateManager } from '../../compaction/manager.js'
|
|
13
|
+
import type { CompactionSummaryRouter } from '../../compaction/model-summary.js'
|
|
13
14
|
import type { ContextReducer } from '../../compaction/reducer.js'
|
|
14
15
|
import { serializeState as serializeWorkingState } from '../../compaction/serializer.js'
|
|
15
16
|
import { restoreWorkingState, snapshotWorkingState } from '../../compaction/wire.js'
|
|
@@ -814,6 +815,19 @@ export interface QueryParams {
|
|
|
814
815
|
*/
|
|
815
816
|
projectInstructionContext?: ProjectInstructionContext
|
|
816
817
|
|
|
818
|
+
/**
|
|
819
|
+
* Text the host wants carried across a compaction, such as where its plan
|
|
820
|
+
* file is. Asked once per pass, after the summary is written; the answer is
|
|
821
|
+
* appended to the restored context. Returning nothing adds nothing.
|
|
822
|
+
*/
|
|
823
|
+
postCompactionContext?: () => string | undefined | Promise<string | undefined>
|
|
824
|
+
|
|
825
|
+
/**
|
|
826
|
+
* Names the model that writes a compaction summary. Asked once per pass;
|
|
827
|
+
* resolving nothing, or a call that fails, keeps the extracted summary.
|
|
828
|
+
*/
|
|
829
|
+
compactionSummaryRoute?: CompactionSummaryRouter
|
|
830
|
+
|
|
817
831
|
/**
|
|
818
832
|
* Where this conversation's durable state lives.
|
|
819
833
|
*
|
|
@@ -1656,6 +1670,8 @@ export async function* query(params: QueryParams): AsyncGenerator<SessionEvent,
|
|
|
1656
1670
|
const compactionConfig = params.compactionConfig
|
|
1657
1671
|
? CompactionConfigSchema.parse(params.compactionConfig)
|
|
1658
1672
|
: undefined
|
|
1673
|
+
// The live history, so a tool can ask whether an earlier receipt is still in it.
|
|
1674
|
+
toolExecutor.setHistoryView(() => ctx.recorder.messages)
|
|
1659
1675
|
if (compactionConfig && compactionConfig.strategy !== 'disabled') {
|
|
1660
1676
|
workingStateManager = new WorkingStateManager(compactionConfig)
|
|
1661
1677
|
toolExecutor.setWorkingStateManager(workingStateManager)
|
|
@@ -1829,6 +1845,12 @@ export async function* query(params: QueryParams): AsyncGenerator<SessionEvent,
|
|
|
1829
1845
|
...(params.projectInstructionContext
|
|
1830
1846
|
? { projectInstructionContext: params.projectInstructionContext }
|
|
1831
1847
|
: {}),
|
|
1848
|
+
...(params.postCompactionContext
|
|
1849
|
+
? { postCompactionContext: params.postCompactionContext }
|
|
1850
|
+
: {}),
|
|
1851
|
+
...(params.compactionSummaryRoute
|
|
1852
|
+
? { compactionSummaryRoute: params.compactionSummaryRoute }
|
|
1853
|
+
: {}),
|
|
1832
1854
|
...(providerContextWindow !== undefined ? { providerContextWindow } : {}),
|
|
1833
1855
|
resolveModelContextWindow: async (model) => {
|
|
1834
1856
|
if (!modelContextWindows.has(model)) {
|
|
@@ -2339,6 +2361,7 @@ export async function* query(params: QueryParams): AsyncGenerator<SessionEvent,
|
|
|
2339
2361
|
? `${segments.static}\n\n---\n\n${segments.dynamic}`
|
|
2340
2362
|
: segments.static
|
|
2341
2363
|
|
|
2364
|
+
toolExecutor.setSystemPromptView(() => assembledPrompt)
|
|
2342
2365
|
ctx.recorder.markRunning()
|
|
2343
2366
|
// A resumed turn already began; `turn_resuming` above continued it.
|
|
2344
2367
|
if (!params.resumeFromCheckpoint) {
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
resolveTriggerThreshold,
|
|
4
4
|
} from '../../../../compaction/context-window.js'
|
|
5
5
|
import { findDanglingMessages } from '../../../../compaction/dangling.js'
|
|
6
|
+
import { replaceableSummaries, summarizeWithModel } from '../../../../compaction/model-summary.js'
|
|
6
7
|
import {
|
|
7
8
|
type CompactionPlan,
|
|
8
9
|
DEFAULT_SOFT_TARGET,
|
|
@@ -11,6 +12,7 @@ import {
|
|
|
11
12
|
} from '../../../../compaction/plan.js'
|
|
12
13
|
import type { ContextReducer, ContextReduction } from '../../../../compaction/reducer.js'
|
|
13
14
|
import { createSlidingWindowReducer } from '../../../../compaction/reducer.js'
|
|
15
|
+
import { renderRestoredContext } from '../../../../compaction/restore.js'
|
|
14
16
|
import { findRetainedIndices } from '../../../../compaction/retention.js'
|
|
15
17
|
import { serializeState } from '../../../../compaction/serializer.js'
|
|
16
18
|
import {
|
|
@@ -959,7 +961,51 @@ async function runCompactionCheckInner(
|
|
|
959
961
|
|
|
960
962
|
let compactedContent: string
|
|
961
963
|
|
|
962
|
-
|
|
964
|
+
// A model the host set aside writes the summary when it can. Its own cost
|
|
965
|
+
// goes on the turn's ledger at the price of the model that answered. When
|
|
966
|
+
// there is no such model, or its call does not give a summary, the
|
|
967
|
+
// extracted state below is used, with its verifier step where that applies.
|
|
968
|
+
let modelRoute: { providerId: string; model: string } | undefined
|
|
969
|
+
const modelWritten = !ctx.compactionSummaryRoute
|
|
970
|
+
? undefined
|
|
971
|
+
: await summarizeWithModel({
|
|
972
|
+
manager,
|
|
973
|
+
olderMessages,
|
|
974
|
+
previousSummaries: replaceableSummaries(systemMessages),
|
|
975
|
+
config,
|
|
976
|
+
resolveRoute: async (signal) => {
|
|
977
|
+
const route = await ctx.compactionSummaryRoute?.(signal)
|
|
978
|
+
if (route) {
|
|
979
|
+
modelRoute = {
|
|
980
|
+
providerId: route.providerId ?? ctx.recorder.servingProviderId,
|
|
981
|
+
model: route.model,
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
return route
|
|
985
|
+
},
|
|
986
|
+
onUsage: (usage) => {
|
|
987
|
+
if (modelRoute) ctx.recorder.accumulateUsage(usage, modelRoute)
|
|
988
|
+
},
|
|
989
|
+
signal: ctx.abortController.signal,
|
|
990
|
+
onFallback: (reason, error) => {
|
|
991
|
+
if (reason === 'no_route') return
|
|
992
|
+
if (error !== undefined) noteCompactionError(ctx, error)
|
|
993
|
+
ctx.log.warn(
|
|
994
|
+
'The model-written compaction summary was not used — using the extracted state',
|
|
995
|
+
{
|
|
996
|
+
[NAMZU.TURN_ID]: ctx.recorder.turnId,
|
|
997
|
+
'namzu.runtime.reason': reason,
|
|
998
|
+
...(error !== undefined
|
|
999
|
+
? { 'exception.message': error instanceof Error ? error.message : String(error) }
|
|
1000
|
+
: {}),
|
|
1001
|
+
},
|
|
1002
|
+
)
|
|
1003
|
+
},
|
|
1004
|
+
})
|
|
1005
|
+
|
|
1006
|
+
if (modelWritten !== undefined) {
|
|
1007
|
+
compactedContent = modelWritten
|
|
1008
|
+
} else if (config.llmVerification && manager.slotCount() < config.richStateThreshold) {
|
|
963
1009
|
// Named once and used twice on purpose. The model the summariser is
|
|
964
1010
|
// asked for is also the model its tokens are priced at, and computing
|
|
965
1011
|
// the two separately is how they drift — a router that sends compaction
|
|
@@ -1003,6 +1049,24 @@ async function runCompactionCheckInner(
|
|
|
1003
1049
|
compactedContent = serializeState(manager.getState())
|
|
1004
1050
|
}
|
|
1005
1051
|
|
|
1052
|
+
// Skills the model loaded and files it read go back verbatim after the
|
|
1053
|
+
// summary, with whatever the host wants carried across (its plan file).
|
|
1054
|
+
let hostNote: string | undefined
|
|
1055
|
+
try {
|
|
1056
|
+
hostNote = await ctx.postCompactionContext?.()
|
|
1057
|
+
} catch (error) {
|
|
1058
|
+
// A host note is an addition; the pass does not depend on it.
|
|
1059
|
+
ctx.log.warn('The host note for a compaction could not be read — leaving it out', {
|
|
1060
|
+
[NAMZU.TURN_ID]: ctx.recorder.turnId,
|
|
1061
|
+
'exception.message': error instanceof Error ? error.message : String(error),
|
|
1062
|
+
})
|
|
1063
|
+
}
|
|
1064
|
+
const restored = renderRestoredContext(manager.getState(), {
|
|
1065
|
+
contextWindowTokens: budget,
|
|
1066
|
+
...(hostNote ? { hostNote } : {}),
|
|
1067
|
+
})
|
|
1068
|
+
if (restored) compactedContent = `${compactedContent}\n\n${restored}`
|
|
1069
|
+
|
|
1006
1070
|
// The pointer is only worth a line when both tools it names are mounted.
|
|
1007
1071
|
const mounted = new Set(ctx.tools?.listNames?.() ?? [])
|
|
1008
1072
|
const compactionMessage = buildCompactionMessage(
|
|
@@ -2,6 +2,7 @@ import type { AdvisoryContext } from '../../../../advisory/context.js'
|
|
|
2
2
|
import type { SkillGrantSet } from '../../../../authorization/skill-grant.js'
|
|
3
3
|
import type { AgentBus } from '../../../../bus/index.js'
|
|
4
4
|
import type { WorkingStateManager } from '../../../../compaction/manager.js'
|
|
5
|
+
import type { CompactionSummaryRouter } from '../../../../compaction/model-summary.js'
|
|
5
6
|
import type { ContextReducer } from '../../../../compaction/reducer.js'
|
|
6
7
|
import type { CompactionConfig } from '../../../../config/runtime.js'
|
|
7
8
|
import { NAMZU } from '../../../../constants/telemetry/index.js'
|
|
@@ -256,6 +257,12 @@ export interface IterationContext {
|
|
|
256
257
|
/** Live project policy; separate from human inbound continuation. */
|
|
257
258
|
readonly projectInstructionContext?: ProjectInstructionContext
|
|
258
259
|
|
|
260
|
+
/** Host text appended to the restored context after a compaction. */
|
|
261
|
+
readonly postCompactionContext?: () => string | undefined | Promise<string | undefined>
|
|
262
|
+
|
|
263
|
+
/** Where a model-written compaction summary is asked for; see {@link CompactionSummaryRouter}. */
|
|
264
|
+
readonly compactionSummaryRoute?: CompactionSummaryRouter
|
|
265
|
+
|
|
259
266
|
readonly workingStateManager?: WorkingStateManager
|
|
260
267
|
|
|
261
268
|
/**
|
|
@@ -30,7 +30,7 @@ import type {
|
|
|
30
30
|
ReasoningBlock,
|
|
31
31
|
ToolInputError,
|
|
32
32
|
} from '../../../types/message/index.js'
|
|
33
|
-
import { ProviderError } from '../../../types/provider/errors.js'
|
|
33
|
+
import { ProviderError, classifyProviderError } from '../../../types/provider/errors.js'
|
|
34
34
|
import type {
|
|
35
35
|
ChatCompletionResponse,
|
|
36
36
|
LLMProvider,
|
|
@@ -298,7 +298,7 @@ export async function* streamProviderTurn(
|
|
|
298
298
|
let streamedLength = 0
|
|
299
299
|
const toolBuckets = new Map<number, ToolCallBucket>()
|
|
300
300
|
// Places a fragment that came without an index; see `ToolCallIndexer`.
|
|
301
|
-
|
|
301
|
+
let toolIndexer = new ToolCallIndexer()
|
|
302
302
|
// The call the model's latest output went to, or `undefined` once text,
|
|
303
303
|
// reasoning or a hosted tool followed it. An output limit, a content
|
|
304
304
|
// filter or a dropped stream stops the response wherever it is, so this
|
|
@@ -397,6 +397,36 @@ export async function* streamProviderTurn(
|
|
|
397
397
|
})
|
|
398
398
|
: undefined
|
|
399
399
|
|
|
400
|
+
/**
|
|
401
|
+
* Drop everything the failed attempt wrote, so the request starts over under
|
|
402
|
+
* the same message: after a restarting retry, or a swap that says the same.
|
|
403
|
+
* The failed attempt's text, reasoning and tool calls are void. A call is
|
|
404
|
+
* restarted only while still being written and nothing runs before the
|
|
405
|
+
* response settles, so a half-written call never reaches the model.
|
|
406
|
+
*/
|
|
407
|
+
const discardAttempt = () => {
|
|
408
|
+
toolBuckets.clear()
|
|
409
|
+
toolIndexer = new ToolCallIndexer()
|
|
410
|
+
framingViolated = false
|
|
411
|
+
text = new StreamTextAccumulator()
|
|
412
|
+
reasoningBuckets.clear()
|
|
413
|
+
citations.length = 0
|
|
414
|
+
replayState = undefined
|
|
415
|
+
id = ''
|
|
416
|
+
finishReason = 'stop'
|
|
417
|
+
reportedFinishReason = undefined
|
|
418
|
+
finishDetail = undefined
|
|
419
|
+
streamedLength = 0
|
|
420
|
+
lastOutputCall = undefined
|
|
421
|
+
usage = {
|
|
422
|
+
promptTokens: 0,
|
|
423
|
+
completionTokens: 0,
|
|
424
|
+
totalTokens: 0,
|
|
425
|
+
cachedTokens: 0,
|
|
426
|
+
cacheWriteTokens: 0,
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
400
430
|
try {
|
|
401
431
|
for (;;) {
|
|
402
432
|
const next = it.next()
|
|
@@ -426,28 +456,7 @@ export async function* streamProviderTurn(
|
|
|
426
456
|
...(chunk.retry.restart ? { restart: true } : {}),
|
|
427
457
|
})
|
|
428
458
|
yield* drainPending()
|
|
429
|
-
if (chunk.retry.restart)
|
|
430
|
-
// The failed attempt's text and reasoning are void: the request
|
|
431
|
-
// starts over under the same message. The decorator never
|
|
432
|
-
// restarts once a tool call began, so no bucket is left behind.
|
|
433
|
-
text = new StreamTextAccumulator()
|
|
434
|
-
reasoningBuckets.clear()
|
|
435
|
-
citations.length = 0
|
|
436
|
-
replayState = undefined
|
|
437
|
-
id = ''
|
|
438
|
-
finishReason = 'stop'
|
|
439
|
-
reportedFinishReason = undefined
|
|
440
|
-
finishDetail = undefined
|
|
441
|
-
streamedLength = 0
|
|
442
|
-
lastOutputCall = undefined
|
|
443
|
-
usage = {
|
|
444
|
-
promptTokens: 0,
|
|
445
|
-
completionTokens: 0,
|
|
446
|
-
totalTokens: 0,
|
|
447
|
-
cachedTokens: 0,
|
|
448
|
-
cacheWriteTokens: 0,
|
|
449
|
-
}
|
|
450
|
-
}
|
|
459
|
+
if (chunk.retry.restart) discardAttempt()
|
|
451
460
|
continue
|
|
452
461
|
}
|
|
453
462
|
|
|
@@ -472,8 +481,10 @@ export async function* streamProviderTurn(
|
|
|
472
481
|
code: chunk.fallback.code,
|
|
473
482
|
...(chunk.fallback.status !== undefined ? { status: chunk.fallback.status } : {}),
|
|
474
483
|
reason: chunk.fallback.reason,
|
|
484
|
+
...(chunk.fallback.restart ? { restart: true } : {}),
|
|
475
485
|
})
|
|
476
486
|
yield* drainPending()
|
|
487
|
+
if (chunk.fallback.restart) discardAttempt()
|
|
477
488
|
continue
|
|
478
489
|
}
|
|
479
490
|
|
|
@@ -947,10 +958,21 @@ export async function* streamProviderTurn(
|
|
|
947
958
|
|
|
948
959
|
// Not after a framing violation: the calls it would recover are the ones
|
|
949
960
|
// the stream was just refused for garbling.
|
|
961
|
+
//
|
|
962
|
+
// Nor when the stream was cut by a connection that may come back and no
|
|
963
|
+
// call came through whole: the retries are spent, the half-written call is
|
|
964
|
+
// not the model's mistake, and telling the model about it would spend a
|
|
965
|
+
// turn on a fault it cannot fix. The failure goes up and the turn pauses.
|
|
966
|
+
const cutByConnection =
|
|
967
|
+
streamError !== undefined &&
|
|
968
|
+
streamCause !== undefined &&
|
|
969
|
+
classifyProviderError(streamCause).retryable
|
|
950
970
|
const recoveredToolInputFromStreamError =
|
|
951
971
|
streamError !== undefined &&
|
|
952
972
|
!framingViolated &&
|
|
953
|
-
toolCalls.some(
|
|
973
|
+
toolCalls.some(
|
|
974
|
+
(tc) => tc.id && tc.function.name && (!cutByConnection || !tc.metadata?.inputError),
|
|
975
|
+
)
|
|
954
976
|
const effectiveFinishReason: ChatCompletionResponse['finishReason'] =
|
|
955
977
|
recoveredToolInputFromStreamError ? 'tool_calls' : finishReason
|
|
956
978
|
|
|
@@ -43,6 +43,7 @@ import { generateCheckpointId, generateTurnId } from '../../utils/id.js'
|
|
|
43
43
|
import { errorAttributes } from '../../utils/log/exception.js'
|
|
44
44
|
import type { Logger } from '../../utils/logger.js'
|
|
45
45
|
import { AUTO_APPROVE_POLICY_NAME, createSessionApprovalPolicy } from './approval-policy.js'
|
|
46
|
+
import { readCallsThatNeverStarted } from './calls-that-never-started.js'
|
|
46
47
|
import { type TurnContext, TurnContextFactory } from './context.js'
|
|
47
48
|
import { EventTranslator } from './events.js'
|
|
48
49
|
import type { QueryParams } from './index.js'
|
|
@@ -681,9 +682,17 @@ export async function prepareTurn(params: QueryParams): Promise<PreparedTurn> {
|
|
|
681
682
|
pinCompaction: true,
|
|
682
683
|
})),
|
|
683
684
|
])
|
|
684
|
-
|
|
685
|
+
let initialRepair = params.resumeFromCheckpoint
|
|
685
686
|
? { messages: projectedInitialMessages, report: undefined }
|
|
686
687
|
: repairToolMessageHistory(projectedInitialMessages)
|
|
688
|
+
if (initialRepair.report && initialRepair.report.syntheticToolResultsInserted > 0) {
|
|
689
|
+
// A call that was waiting for approval when the session ended never ran;
|
|
690
|
+
// the model is told so rather than that its outcome is unknown.
|
|
691
|
+
const neverRan = await readCallsThatNeverStarted(storage.log)
|
|
692
|
+
if (neverRan.size > 0) {
|
|
693
|
+
initialRepair = repairToolMessageHistory(projectedInitialMessages, { neverRan })
|
|
694
|
+
}
|
|
695
|
+
}
|
|
687
696
|
const initialMessages = initialRepair.messages
|
|
688
697
|
if (initialRepair.report && toolHistoryRepairChanged(initialRepair.report)) {
|
|
689
698
|
pendingHistoryRepairs.push({
|
|
@@ -46,7 +46,7 @@ const OWNED_WORK_DISPLAY_LIMIT = 16
|
|
|
46
46
|
|
|
47
47
|
/** What the model is told about owned work, wherever it is shown. */
|
|
48
48
|
const OWNED_WORK_GUIDANCE =
|
|
49
|
-
'Each task gives its id (taskId), its state, how its turn ended (turnStatus), why it stopped (stopReason), and whether its result has reached this conversation (resultDelivery: delivered-to-history, or not-delivered). Result delivery is not proof that you answered the original request. Answer the latest operator message and complete the requested synthesis of available results unless the operator cancelled or changed that request. Reuse delivered results when sufficient; do not relaunch completed work. If a delivered result is no longer visible, retrieve it by task ID. A terminal state with a non-end_turn stop reason is not successful task completion. Do not repeat a synthesis already given.'
|
|
49
|
+
'Each task gives its id (taskId), its state, how its turn ended (turnStatus), why it stopped (stopReason), and whether its result has reached this conversation (resultDelivery: delivered-to-history, or not-delivered). Result delivery is not proof that you answered the original request. Answer the latest operator message and complete the requested synthesis of available results unless the operator cancelled or changed that request. Reuse delivered results when sufficient; do not relaunch completed work. If a delivered result is no longer visible, retrieve it by task ID. A terminal state with a non-end_turn stop reason is not successful task completion. Results reach you and not the user, so before you end a reply check that the result of every finished task has been reported to the user, once. Do not repeat a synthesis already given.'
|
|
50
50
|
|
|
51
51
|
/**
|
|
52
52
|
* The ids of the tasks that earlier recorded entries name, read from the
|
|
@@ -809,8 +809,8 @@ export function formatCompletionNotification(handles: readonly TaskHandle[]): st
|
|
|
809
809
|
|
|
810
810
|
const preamble =
|
|
811
811
|
handles.length === 1
|
|
812
|
-
? 'A task you launched has finished. This is its result — you were not waiting on it, so it arrives here rather than as a tool result.'
|
|
813
|
-
: `${handles.length} tasks you launched have finished. These are their results — you were not waiting on them, so they arrive here rather than as tool results.`
|
|
812
|
+
? 'A task you launched has finished. This is its result — you were not waiting on it, so it arrives here rather than as a tool result. The user has not seen it; tell them what it found in your next message.'
|
|
813
|
+
: `${handles.length} tasks you launched have finished. These are their results — you were not waiting on them, so they arrive here rather than as tool results. The user has not seen them; tell them what each found in your next message.`
|
|
814
814
|
|
|
815
815
|
return `${preamble}\n\n${blocks.join('\n\n')}`
|
|
816
816
|
}
|
package/src/scheduler/local.ts
CHANGED
|
@@ -133,7 +133,7 @@ export class LocalTaskScheduler implements TaskScheduler {
|
|
|
133
133
|
...(options.phaseDetail ? { phaseDetail: options.phaseDetail } : {}),
|
|
134
134
|
...(options.phaseOrder !== undefined ? { phaseOrder: options.phaseOrder } : {}),
|
|
135
135
|
input: {
|
|
136
|
-
messages: [createUserMessage(options.prompt)],
|
|
136
|
+
messages: [...(options.inheritedMessages ?? []), createUserMessage(options.prompt)],
|
|
137
137
|
workingDirectory: options.workingDirectory,
|
|
138
138
|
taskStore: this.parentInput?.taskStore,
|
|
139
139
|
runtimeToolOverrides: this.parentInput?.runtimeToolOverrides,
|
package/src/tools/admit-facts.ts
CHANGED
|
@@ -118,6 +118,7 @@ export function admitToolFacts(value: unknown): ToolFacts | undefined {
|
|
|
118
118
|
? { bytes: count(value.binary.bytes) }
|
|
119
119
|
: undefined,
|
|
120
120
|
background: flag(value.background),
|
|
121
|
+
continued: flag(value.continued),
|
|
121
122
|
})
|
|
122
123
|
case 'read': {
|
|
123
124
|
const path = text(value.path)
|