@namzu/sdk 52.0.0 → 52.2.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/README.md +1 -1
- package/dist/compaction/verifier.d.ts.map +1 -1
- package/dist/compaction/verifier.js +57 -17
- package/dist/compaction/verifier.js.map +1 -1
- package/dist/connector/tools/adapter.d.ts +2 -2
- package/dist/contracts/a2a.d.ts +94 -94
- package/dist/manager/resident/learning.d.ts +6 -6
- package/dist/manager/resident/store.d.ts +4 -4
- package/dist/pals/activity/subscription-schema.d.ts +48 -48
- package/dist/pals/communication/ingress-schema.d.ts +192 -192
- package/dist/pals/communication/schema.d.ts +18 -18
- package/dist/peers/protocol.d.ts +36 -36
- package/dist/provider/capabilities.d.ts +9 -0
- package/dist/provider/capabilities.d.ts.map +1 -1
- package/dist/provider/capabilities.js +15 -0
- package/dist/provider/capabilities.js.map +1 -1
- package/dist/provider/errors.d.ts.map +1 -1
- package/dist/provider/errors.js +13 -0
- package/dist/provider/errors.js.map +1 -1
- package/dist/provider/fallback.d.ts.map +1 -1
- package/dist/provider/fallback.js +10 -0
- package/dist/provider/fallback.js.map +1 -1
- package/dist/provider/idle-timeout.d.ts.map +1 -1
- package/dist/provider/idle-timeout.js +5 -0
- package/dist/provider/idle-timeout.js.map +1 -1
- package/dist/provider/image-input-refusal.d.ts +9 -0
- package/dist/provider/image-input-refusal.d.ts.map +1 -0
- package/dist/provider/image-input-refusal.js +54 -0
- package/dist/provider/image-input-refusal.js.map +1 -0
- package/dist/provider/index.d.ts +2 -1
- package/dist/provider/index.d.ts.map +1 -1
- package/dist/provider/index.js +2 -1
- package/dist/provider/index.js.map +1 -1
- package/dist/provider/retry.d.ts +7 -2
- package/dist/provider/retry.d.ts.map +1 -1
- package/dist/provider/retry.js +38 -4
- package/dist/provider/retry.js.map +1 -1
- package/dist/public-runtime.d.ts +2 -1
- package/dist/public-runtime.d.ts.map +1 -1
- package/dist/public-runtime.js +1 -1
- package/dist/public-runtime.js.map +1 -1
- package/dist/public-tools.d.ts +1 -0
- package/dist/public-tools.d.ts.map +1 -1
- package/dist/public-tools.js +1 -0
- package/dist/public-tools.js.map +1 -1
- package/dist/runtime/query/deferred-sandbox.d.ts +54 -0
- package/dist/runtime/query/deferred-sandbox.d.ts.map +1 -0
- package/dist/runtime/query/deferred-sandbox.js +75 -0
- package/dist/runtime/query/deferred-sandbox.js.map +1 -0
- 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 +25 -3
- package/dist/runtime/query/executor.js.map +1 -1
- package/dist/runtime/query/index.d.ts +2 -0
- package/dist/runtime/query/index.d.ts.map +1 -1
- package/dist/runtime/query/index.js +94 -25
- package/dist/runtime/query/index.js.map +1 -1
- package/dist/runtime/query/iteration/index.d.ts +26 -0
- package/dist/runtime/query/iteration/index.d.ts.map +1 -1
- package/dist/runtime/query/iteration/index.js +164 -21
- package/dist/runtime/query/iteration/index.js.map +1 -1
- package/dist/runtime/query/iteration/phases/compaction-breaker.d.ts +10 -7
- package/dist/runtime/query/iteration/phases/compaction-breaker.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/compaction-breaker.js +10 -7
- package/dist/runtime/query/iteration/phases/compaction-breaker.js.map +1 -1
- package/dist/runtime/query/iteration/phases/compaction.d.ts +42 -1
- package/dist/runtime/query/iteration/phases/compaction.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/compaction.js +119 -21
- package/dist/runtime/query/iteration/phases/compaction.js.map +1 -1
- package/dist/runtime/query/iteration/provider-rejected-image.d.ts +21 -1
- package/dist/runtime/query/iteration/provider-rejected-image.d.ts.map +1 -1
- package/dist/runtime/query/iteration/provider-rejected-image.js +53 -7
- package/dist/runtime/query/iteration/provider-rejected-image.js.map +1 -1
- package/dist/runtime/query/iteration/request-repair.d.ts +108 -0
- package/dist/runtime/query/iteration/request-repair.d.ts.map +1 -0
- package/dist/runtime/query/iteration/request-repair.js +593 -0
- package/dist/runtime/query/iteration/request-repair.js.map +1 -0
- package/dist/runtime/query/iteration/stream-turn.d.ts +6 -0
- package/dist/runtime/query/iteration/stream-turn.d.ts.map +1 -1
- package/dist/runtime/query/iteration/stream-turn.js +2 -2
- package/dist/runtime/query/iteration/stream-turn.js.map +1 -1
- package/dist/runtime/query/request-rich-content.d.ts +5 -1
- package/dist/runtime/query/request-rich-content.d.ts.map +1 -1
- package/dist/runtime/query/request-rich-content.js +67 -25
- package/dist/runtime/query/request-rich-content.js.map +1 -1
- package/dist/runtime/query/tooling.d.ts +1 -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/tools/builtins/browser.d.ts +4 -4
- package/dist/tools/builtins/code-analysis.d.ts +49 -0
- package/dist/tools/builtins/code-analysis.d.ts.map +1 -0
- package/dist/tools/builtins/code-analysis.js +409 -0
- package/dist/tools/builtins/code-analysis.js.map +1 -0
- package/dist/tools/builtins/computer-use.d.ts +25 -25
- package/dist/tools/builtins/computer-use.d.ts.map +1 -1
- package/dist/tools/builtins/computer-use.js +12 -5
- package/dist/tools/builtins/computer-use.js.map +1 -1
- package/dist/tools/builtins/index.d.ts +1 -0
- package/dist/tools/builtins/index.d.ts.map +1 -1
- package/dist/tools/builtins/index.js +1 -0
- package/dist/tools/builtins/index.js.map +1 -1
- package/dist/tools/builtins/lsp.d.ts +1 -1
- package/dist/tools/builtins/skill.d.ts +2 -2
- package/dist/types/code-analysis/index.d.ts +299 -0
- package/dist/types/code-analysis/index.d.ts.map +1 -0
- package/dist/types/code-analysis/index.js +30 -0
- package/dist/types/code-analysis/index.js.map +1 -0
- package/dist/types/plugin/index.d.ts +6 -6
- package/dist/types/provider/errors.d.ts.map +1 -1
- package/dist/types/provider/errors.js +30 -5
- package/dist/types/provider/errors.js.map +1 -1
- package/dist/types/provider/interface.d.ts +12 -0
- package/dist/types/provider/interface.d.ts.map +1 -1
- package/dist/types/provider/stream.d.ts +1 -1
- package/dist/types/session/config.d.ts +14 -0
- package/dist/types/session/config.d.ts.map +1 -1
- package/dist/types/session/events.d.ts +20 -5
- package/dist/types/session/events.d.ts.map +1 -1
- package/dist/types/session/events.js.map +1 -1
- package/dist/types/session/records.d.ts +127 -127
- package/dist/types/tool/index.d.ts +9 -0
- package/dist/types/tool/index.d.ts.map +1 -1
- package/dist/types/tool/index.js.map +1 -1
- package/package.json +2 -2
- package/src/compaction/verifier.ts +57 -20
- package/src/provider/capabilities.ts +17 -0
- package/src/provider/errors.ts +13 -0
- package/src/provider/fallback.ts +10 -0
- package/src/provider/idle-timeout.ts +5 -0
- package/src/provider/image-input-refusal.ts +54 -0
- package/src/provider/index.ts +2 -0
- package/src/provider/retry.ts +42 -4
- package/src/public-runtime.ts +28 -0
- package/src/public-tools.ts +5 -0
- package/src/runtime/query/deferred-sandbox.ts +110 -0
- package/src/runtime/query/executor.ts +27 -3
- package/src/runtime/query/index.ts +127 -49
- package/src/runtime/query/iteration/index.ts +188 -23
- package/src/runtime/query/iteration/phases/compaction-breaker.ts +10 -8
- package/src/runtime/query/iteration/phases/compaction.ts +151 -35
- package/src/runtime/query/iteration/provider-rejected-image.ts +71 -5
- package/src/runtime/query/iteration/request-repair.ts +743 -0
- package/src/runtime/query/iteration/stream-turn.ts +11 -1
- package/src/runtime/query/request-rich-content.ts +77 -27
- package/src/runtime/query/tooling.ts +2 -0
- package/src/tools/builtins/code-analysis.ts +486 -0
- package/src/tools/builtins/computer-use.ts +13 -4
- package/src/tools/builtins/index.ts +5 -0
- package/src/types/code-analysis/index.ts +325 -0
- package/src/types/provider/errors.ts +33 -5
- package/src/types/provider/interface.ts +13 -0
- package/src/types/provider/stream.ts +1 -1
- package/src/types/session/config.ts +14 -0
- package/src/types/session/events.ts +25 -5
- package/src/types/tool/index.ts +9 -0
|
@@ -336,6 +336,7 @@ export const DEFAULT_TOOL_RETRY_BACKOFF: BackoffPolicy = {
|
|
|
336
336
|
|
|
337
337
|
export interface ToolExecutorConfig {
|
|
338
338
|
fileReadTracker?: FileReadTracker
|
|
339
|
+
codeAnalysis?: import('../../types/code-analysis/index.js').CodeAnalysisProvider
|
|
339
340
|
tools: ToolManager
|
|
340
341
|
/** Exact runtime output-tool owner allowed to retain post-screen, post-hook JSON before preview capping. */
|
|
341
342
|
durableStructuredOutputTool?: ToolDefinition
|
|
@@ -653,6 +654,7 @@ export class ToolExecutor {
|
|
|
653
654
|
/** Set per turn by the orchestrator; see {@link setStepAllowedTools}. */
|
|
654
655
|
private stepAllowedTools?: readonly string[]
|
|
655
656
|
private readonly fileReadTracker: FileReadTracker
|
|
657
|
+
private sandboxGate?: () => Promise<void>
|
|
656
658
|
/** A ledger is rebuilt from history at most once; a second pass would re-append its chains. */
|
|
657
659
|
private fileObservationsSeeded = false
|
|
658
660
|
|
|
@@ -686,6 +688,25 @@ export class ToolExecutor {
|
|
|
686
688
|
|
|
687
689
|
setSandbox(sandbox: Sandbox): void {
|
|
688
690
|
this.config = { ...this.config, sandbox }
|
|
691
|
+
this.sandboxGate = undefined
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
/**
|
|
695
|
+
* Hold tool admission until a sandbox that is still being acquired exists.
|
|
696
|
+
* The gate rejects when none will, and the turn settles that rejection. It
|
|
697
|
+
* is cleared by {@link setSandbox}, so a turn pays the check once.
|
|
698
|
+
*/
|
|
699
|
+
setSandboxGate(gate: () => Promise<void>): void {
|
|
700
|
+
this.sandboxGate = gate
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
private async awaitSandbox(): Promise<void> {
|
|
704
|
+
await this.sandboxGate?.()
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
/** True once the turn has a sandbox, or is certain to get one before a tool runs. */
|
|
708
|
+
private get sandboxed(): boolean {
|
|
709
|
+
return this.config.sandbox !== undefined || this.sandboxGate !== undefined
|
|
689
710
|
}
|
|
690
711
|
|
|
691
712
|
/** Classify only the exact successful result executed by this executor. */
|
|
@@ -771,7 +792,7 @@ export class ToolExecutor {
|
|
|
771
792
|
messages,
|
|
772
793
|
this.fileReadTracker,
|
|
773
794
|
this.config.workingDirectory,
|
|
774
|
-
this.
|
|
795
|
+
this.sandboxed,
|
|
775
796
|
)
|
|
776
797
|
}
|
|
777
798
|
|
|
@@ -953,7 +974,7 @@ export class ToolExecutor {
|
|
|
953
974
|
const tool = this.config.tools.get(toolName)
|
|
954
975
|
return (
|
|
955
976
|
tool?.commandDialect?.({
|
|
956
|
-
sandboxed: this.
|
|
977
|
+
sandboxed: this.sandboxed,
|
|
957
978
|
}) ?? 'sh'
|
|
958
979
|
)
|
|
959
980
|
}
|
|
@@ -979,6 +1000,7 @@ export class ToolExecutor {
|
|
|
979
1000
|
hostModifiedCallIds?: ReadonlySet<string>,
|
|
980
1001
|
): Promise<PreparedToolBatch> {
|
|
981
1002
|
assertUniqueToolCallIds(response.message.toolCalls ?? [])
|
|
1003
|
+
if ((response.message.toolCalls ?? []).length > 0) await this.awaitSandbox()
|
|
982
1004
|
const calls = new Map<string, PreparedDirectCall>()
|
|
983
1005
|
const escalations = new Map<string, ToolCallEscalation>()
|
|
984
1006
|
for (const toolCall of response.message.toolCalls ?? []) {
|
|
@@ -1012,7 +1034,7 @@ export class ToolExecutor {
|
|
|
1012
1034
|
const tool = this.config.tools.get(call.toolName)
|
|
1013
1035
|
if (!tool || call.input === null || typeof call.input !== 'object') return undefined
|
|
1014
1036
|
const input = call.input as Record<string, unknown>
|
|
1015
|
-
const sandboxed = this.
|
|
1037
|
+
const sandboxed = this.sandboxed
|
|
1016
1038
|
let outsidePaths: string[] | undefined
|
|
1017
1039
|
if (
|
|
1018
1040
|
this.config.outsideRootAccess === 'review' &&
|
|
@@ -1199,6 +1221,7 @@ export class ToolExecutor {
|
|
|
1199
1221
|
return { messages: [], results: [], observations: [] }
|
|
1200
1222
|
}
|
|
1201
1223
|
assertUniqueToolCallIds(toolCalls)
|
|
1224
|
+
if (toolCalls.length > 0) await this.awaitSandbox()
|
|
1202
1225
|
|
|
1203
1226
|
// Sampled here, once, and held for every call below. See the note on
|
|
1204
1227
|
// `permissionMode` in the config type.
|
|
@@ -1883,6 +1906,7 @@ export class ToolExecutor {
|
|
|
1883
1906
|
this.dispatchNested(name, input, context, recordObservation, undefined, options),
|
|
1884
1907
|
sandbox: this.config.sandbox,
|
|
1885
1908
|
fileReadTracker: this.fileReadTracker,
|
|
1909
|
+
...(this.config.codeAnalysis ? { codeAnalysis: this.config.codeAnalysis } : {}),
|
|
1886
1910
|
// Bound to this turn, once. Binding here rather than passing the
|
|
1887
1911
|
// owner from the tool is what makes the scoping structural: there
|
|
1888
1912
|
// is no argument a tool could pass to reach another turn's jobs.
|
|
@@ -104,6 +104,13 @@ import { AwaitedJobs } from '../jobs/awaited-jobs.js'
|
|
|
104
104
|
import type { BackgroundJobRegistry } from '../jobs/registry.js'
|
|
105
105
|
import { catchUpFromCursor, settlePreStartCancellation } from './cancelled-before-start.js'
|
|
106
106
|
import { CheckpointManager, findPendingCheckpoint } from './checkpoint.js'
|
|
107
|
+
import {
|
|
108
|
+
type DeferredSandbox,
|
|
109
|
+
type SandboxAcquireMode,
|
|
110
|
+
type SandboxAcquisitionStop,
|
|
111
|
+
SandboxAcquisitionStopped,
|
|
112
|
+
createDeferredSandbox,
|
|
113
|
+
} from './deferred-sandbox.js'
|
|
107
114
|
import type { EventTranslator } from './events.js'
|
|
108
115
|
import { finalizeTurn } from './finalize-turn.js'
|
|
109
116
|
import { GuardCoordinator } from './guard.js'
|
|
@@ -146,6 +153,8 @@ import { ToolingBootstrap } from './tooling.js'
|
|
|
146
153
|
export interface QueryParams {
|
|
147
154
|
/** Share observations across turns of one conversation and filesystem; otherwise run-local. */
|
|
148
155
|
fileReadTracker?: import('../../types/tool/index.js').FileReadTracker
|
|
156
|
+
/** The `code_analysis` tool's index; see `ToolContext.codeAnalysis`. Absent: the tool is not offered. */
|
|
157
|
+
codeAnalysis?: import('../../types/code-analysis/index.js').CodeAnalysisProvider
|
|
149
158
|
/** One ledger shared with the task scheduler and descendant child sessions, keyed by (rootSessionId, rootTurnId). */
|
|
150
159
|
budget?: SessionTokenBudget
|
|
151
160
|
/**
|
|
@@ -1421,6 +1430,7 @@ export async function* query(params: QueryParams): AsyncGenerator<SessionEvent,
|
|
|
1421
1430
|
...(params.skillRegistry ? { skills: params.skillRegistry } : {}),
|
|
1422
1431
|
...(params.web ? { web: params.web } : {}),
|
|
1423
1432
|
...(params.fileReadTracker ? { fileReadTracker: params.fileReadTracker } : {}),
|
|
1433
|
+
...(params.codeAnalysis ? { codeAnalysis: params.codeAnalysis } : {}),
|
|
1424
1434
|
...(params.toolTimeoutMs !== undefined ? { toolTimeoutMs: params.toolTimeoutMs } : {}),
|
|
1425
1435
|
...(params.toolRetryBackoff !== undefined
|
|
1426
1436
|
? { toolRetryBackoff: params.toolRetryBackoff }
|
|
@@ -1825,6 +1835,10 @@ export async function* query(params: QueryParams): AsyncGenerator<SessionEvent,
|
|
|
1825
1835
|
})
|
|
1826
1836
|
|
|
1827
1837
|
let sandbox: Sandbox | undefined
|
|
1838
|
+
/** Set only for `sandbox.acquire: 'concurrent' | 'on-first-use'`. */
|
|
1839
|
+
let deferredSandbox: DeferredSandbox | undefined
|
|
1840
|
+
/** How a stopped acquisition settles the turn; set where the provider is known. */
|
|
1841
|
+
let applyAcquisitionStop: (acquisition: SandboxAcquisitionStop) => void = () => {}
|
|
1828
1842
|
// Decided during checkpoint restore, executed after the sandbox
|
|
1829
1843
|
// exists — the approved tools may well need it.
|
|
1830
1844
|
let pendingResume: PendingResumePlan | null = null
|
|
@@ -2300,6 +2314,20 @@ export async function* query(params: QueryParams): AsyncGenerator<SessionEvent,
|
|
|
2300
2314
|
yield* eventTranslator.drainPending()
|
|
2301
2315
|
}
|
|
2302
2316
|
|
|
2317
|
+
// Checked whether or not a provider is present: a host that mistypes the
|
|
2318
|
+
// option should hear about it before it wires a provider in, not after.
|
|
2319
|
+
const acquireMode: SandboxAcquireMode = turnConfig.sandbox?.acquire ?? 'eager'
|
|
2320
|
+
if (
|
|
2321
|
+
acquireMode !== 'eager' &&
|
|
2322
|
+
acquireMode !== 'concurrent' &&
|
|
2323
|
+
acquireMode !== 'on-first-use'
|
|
2324
|
+
) {
|
|
2325
|
+
throw new NamzuError({
|
|
2326
|
+
code: 'invalid_config',
|
|
2327
|
+
message: `sandbox.acquire must be 'eager', 'concurrent' or 'on-first-use'; received ${String(acquireMode)}`,
|
|
2328
|
+
details: { acquire: String(acquireMode) },
|
|
2329
|
+
})
|
|
2330
|
+
}
|
|
2303
2331
|
// --- Sandbox lifecycle: create before iteration loop ---
|
|
2304
2332
|
if (params.sandboxProvider) {
|
|
2305
2333
|
const rootAtCwd = turnConfig.sandbox?.workspace === 'working-directory'
|
|
@@ -2331,40 +2359,60 @@ export async function* query(params: QueryParams): AsyncGenerator<SessionEvent,
|
|
|
2331
2359
|
},
|
|
2332
2360
|
})
|
|
2333
2361
|
}
|
|
2334
|
-
const
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
? {
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2362
|
+
const sandboxProvider = params.sandboxProvider
|
|
2363
|
+
const startAcquisition = (signal: AbortSignal) =>
|
|
2364
|
+
acquireSandbox({
|
|
2365
|
+
provider: sandboxProvider,
|
|
2366
|
+
config: {
|
|
2367
|
+
...(rootAtCwd ? { workingDirectory: ctx.cwd } : {}),
|
|
2368
|
+
...(rootAtCwd && params.additionalDirectories?.length
|
|
2369
|
+
? { additionalDirectories: params.additionalDirectories }
|
|
2370
|
+
: {}),
|
|
2371
|
+
timeoutMs: turnConfig.sandbox?.timeoutMs,
|
|
2372
|
+
memoryLimitMb: turnConfig.sandbox?.memoryLimitMb,
|
|
2373
|
+
maxProcesses: turnConfig.sandbox?.maxProcesses,
|
|
2374
|
+
},
|
|
2375
|
+
signal,
|
|
2376
|
+
timeoutMs: guard.remainingUntilTimeoutMs(),
|
|
2377
|
+
teardownTimeoutMs: sandboxTeardownTimeoutMs,
|
|
2378
|
+
onLateTeardown: (result) => {
|
|
2379
|
+
if (result.kind === 'destroyed') {
|
|
2380
|
+
ctx.log.info('Late sandbox allocation was destroyed after the turn stopped', {
|
|
2381
|
+
[NAMZU.TURN_ID]: ctx.turnId,
|
|
2382
|
+
})
|
|
2383
|
+
return
|
|
2384
|
+
}
|
|
2385
|
+
ctx.log.error('Late sandbox allocation did not stop cleanly', {
|
|
2351
2386
|
[NAMZU.TURN_ID]: ctx.turnId,
|
|
2387
|
+
...errorAttributes(result.error),
|
|
2352
2388
|
})
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2389
|
+
},
|
|
2390
|
+
})
|
|
2391
|
+
const publishSandbox = async (acquired: Sandbox): Promise<void> => {
|
|
2392
|
+
sandbox = acquired
|
|
2393
|
+
toolExecutor.setSandbox(acquired)
|
|
2394
|
+
|
|
2395
|
+
await eventTranslator.emitEvent({
|
|
2396
|
+
type: 'sandbox_created',
|
|
2397
|
+
turnId: ctx.turnId,
|
|
2398
|
+
sandboxId: acquired.id,
|
|
2399
|
+
environment: acquired.environment,
|
|
2400
|
+
})
|
|
2401
|
+
}
|
|
2402
|
+
const logSandboxCreated = (acquired: Sandbox): void => {
|
|
2403
|
+
ctx.log.info('Sandbox created for run', {
|
|
2404
|
+
'namzu.sandbox.id': acquired.id,
|
|
2405
|
+
'namzu.execution.environment': acquired.environment,
|
|
2406
|
+
'namzu.runtime.root_dir': acquired.rootDir,
|
|
2407
|
+
})
|
|
2408
|
+
}
|
|
2409
|
+
applyAcquisitionStop = (acquisition) => {
|
|
2362
2410
|
if (acquisition.createPending) {
|
|
2363
2411
|
ctx.log.warn(
|
|
2364
2412
|
'Sandbox creation was unsettled when the turn stopped; any returned handle will be released, but a remote allocation hidden behind a lost response requires provider-side reconciliation or a fleet reaper',
|
|
2365
2413
|
{
|
|
2366
2414
|
[NAMZU.TURN_ID]: ctx.turnId,
|
|
2367
|
-
'namzu.sandbox.provider_id':
|
|
2415
|
+
'namzu.sandbox.provider_id': sandboxProvider.id,
|
|
2368
2416
|
},
|
|
2369
2417
|
)
|
|
2370
2418
|
}
|
|
@@ -2374,32 +2422,40 @@ export async function* query(params: QueryParams): AsyncGenerator<SessionEvent,
|
|
|
2374
2422
|
ctx.recorder.setStopReason('timeout')
|
|
2375
2423
|
ctx.log.warn('Sandbox creation exhausted the turn timeout', {
|
|
2376
2424
|
[NAMZU.TURN_ID]: ctx.turnId,
|
|
2377
|
-
'namzu.sandbox.provider_id':
|
|
2425
|
+
'namzu.sandbox.provider_id': sandboxProvider.id,
|
|
2378
2426
|
...errorAttributes(acquisition.error),
|
|
2379
2427
|
})
|
|
2380
2428
|
}
|
|
2381
|
-
yield* resultAssembler.completeTurn(rootSpan)
|
|
2382
|
-
// The turn HAS settled, so the outer `finally` must not read
|
|
2383
|
-
// this as an abandonment — it would persist a second time.
|
|
2384
|
-
settled = true
|
|
2385
|
-
return await resultAssembler.finalize()
|
|
2386
2429
|
}
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2430
|
+
if (acquireMode === 'eager') {
|
|
2431
|
+
const acquisition = await startAcquisition(ctx.abortController.signal)
|
|
2432
|
+
if (acquisition.kind !== 'created') {
|
|
2433
|
+
applyAcquisitionStop(acquisition)
|
|
2434
|
+
yield* resultAssembler.completeTurn(rootSpan)
|
|
2435
|
+
// The turn HAS settled, so the outer `finally` must not read
|
|
2436
|
+
// this as an abandonment — it would persist a second time.
|
|
2437
|
+
settled = true
|
|
2438
|
+
return await resultAssembler.finalize()
|
|
2439
|
+
}
|
|
2440
|
+
await publishSandbox(acquisition.sandbox)
|
|
2441
|
+
yield* eventTranslator.drainPending()
|
|
2442
|
+
logSandboxCreated(acquisition.sandbox)
|
|
2443
|
+
} else {
|
|
2444
|
+
// The handle is published, and `sandbox_created` emitted, when
|
|
2445
|
+
// the provider answers. Until then the executor holds tool
|
|
2446
|
+
// admission on the gate, so nothing runs without it.
|
|
2447
|
+
const deferred = createDeferredSandbox({
|
|
2448
|
+
turnSignal: ctx.abortController.signal,
|
|
2449
|
+
acquire: startAcquisition,
|
|
2450
|
+
publish: async (acquired) => {
|
|
2451
|
+
await publishSandbox(acquired)
|
|
2452
|
+
logSandboxCreated(acquired)
|
|
2453
|
+
},
|
|
2454
|
+
})
|
|
2455
|
+
deferredSandbox = deferred
|
|
2456
|
+
toolExecutor.setSandboxGate(() => deferred.ensure())
|
|
2457
|
+
if (acquireMode === 'concurrent') deferred.start()
|
|
2458
|
+
}
|
|
2403
2459
|
}
|
|
2404
2460
|
|
|
2405
2461
|
// Before the first model call: the cheapest place to refuse, since
|
|
@@ -2525,6 +2581,14 @@ export async function* query(params: QueryParams): AsyncGenerator<SessionEvent,
|
|
|
2525
2581
|
|
|
2526
2582
|
yield* iterationOrchestrator.runLoop()
|
|
2527
2583
|
|
|
2584
|
+
// A turn that never needed the sandbox still settles it: a failure or
|
|
2585
|
+
// a timeout ends the turn the way an eager acquisition would have, and
|
|
2586
|
+
// a handle that arrives after that is released by the lifecycle.
|
|
2587
|
+
if (deferredSandbox) {
|
|
2588
|
+
await deferredSandbox.settle()
|
|
2589
|
+
yield* eventTranslator.drainPending()
|
|
2590
|
+
}
|
|
2591
|
+
|
|
2528
2592
|
yield* finalizeTurn({
|
|
2529
2593
|
ctx,
|
|
2530
2594
|
params,
|
|
@@ -2536,6 +2600,16 @@ export async function* query(params: QueryParams): AsyncGenerator<SessionEvent,
|
|
|
2536
2600
|
rootSpan,
|
|
2537
2601
|
})
|
|
2538
2602
|
} catch (err) {
|
|
2603
|
+
// A deferred acquisition stopped (cancelled, or out of time) and the
|
|
2604
|
+
// turn found out at its first tool call or at its end. Settled the
|
|
2605
|
+
// way the eager path settles it, with nothing else to report.
|
|
2606
|
+
if (err instanceof SandboxAcquisitionStopped) {
|
|
2607
|
+
applyAcquisitionStop(err.acquisition)
|
|
2608
|
+
yield* eventTranslator.drainPending()
|
|
2609
|
+
yield* resultAssembler.completeTurn(rootSpan)
|
|
2610
|
+
settled = true
|
|
2611
|
+
return await resultAssembler.finalize()
|
|
2612
|
+
}
|
|
2539
2613
|
// Another turn is active in this session: nothing of this turn was
|
|
2540
2614
|
// written, so there is nothing to settle. The caller gets the
|
|
2541
2615
|
// refusal itself.
|
|
@@ -2546,6 +2620,10 @@ export async function* query(params: QueryParams): AsyncGenerator<SessionEvent,
|
|
|
2546
2620
|
yield* eventTranslator.drainPending()
|
|
2547
2621
|
yield* resultAssembler.handleError(err, rootSpan)
|
|
2548
2622
|
} finally {
|
|
2623
|
+
// A pending acquisition is withdrawn and awaited first, so a handle
|
|
2624
|
+
// that arrives now is either published (and released below) or
|
|
2625
|
+
// handed to the late-teardown path. Nothing is left allocated.
|
|
2626
|
+
await deferredSandbox?.cancel()
|
|
2549
2627
|
yield* releaseTurnResources({
|
|
2550
2628
|
ctx,
|
|
2551
2629
|
eventTranslator,
|