@namzu/sdk 39.0.0 → 41.0.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 +328 -0
- package/dist/bridge/a2a/mapper.d.ts.map +1 -1
- package/dist/bridge/a2a/mapper.js +8 -0
- package/dist/bridge/a2a/mapper.js.map +1 -1
- package/dist/bridge/sse/mapper.d.ts.map +1 -1
- package/dist/bridge/sse/mapper.js +11 -0
- package/dist/bridge/sse/mapper.js.map +1 -1
- package/dist/connector/index.d.ts +2 -2
- package/dist/connector/index.d.ts.map +1 -1
- package/dist/connector/index.js +1 -1
- package/dist/connector/index.js.map +1 -1
- package/dist/connector/mcp/adapter.d.ts.map +1 -1
- package/dist/connector/mcp/adapter.js +112 -10
- package/dist/connector/mcp/adapter.js.map +1 -1
- package/dist/connector/mcp/audio-admission.d.ts +17 -0
- package/dist/connector/mcp/audio-admission.d.ts.map +1 -0
- package/dist/connector/mcp/audio-admission.js +171 -0
- package/dist/connector/mcp/audio-admission.js.map +1 -0
- package/dist/connector/mcp/client.d.ts +252 -1
- package/dist/connector/mcp/client.d.ts.map +1 -1
- package/dist/connector/mcp/client.js +611 -39
- package/dist/connector/mcp/client.js.map +1 -1
- package/dist/connector/mcp/envelope.d.ts +91 -0
- package/dist/connector/mcp/envelope.d.ts.map +1 -0
- package/dist/connector/mcp/envelope.js +173 -0
- package/dist/connector/mcp/envelope.js.map +1 -0
- package/dist/connector/mcp/era.d.ts +130 -0
- package/dist/connector/mcp/era.d.ts.map +1 -0
- package/dist/connector/mcp/era.js +304 -0
- package/dist/connector/mcp/era.js.map +1 -0
- package/dist/connector/mcp/errors.d.ts +106 -0
- package/dist/connector/mcp/errors.d.ts.map +1 -0
- package/dist/connector/mcp/errors.js +154 -0
- package/dist/connector/mcp/errors.js.map +1 -0
- package/dist/connector/mcp/http-sse.d.ts +11 -0
- package/dist/connector/mcp/http-sse.d.ts.map +1 -1
- package/dist/connector/mcp/http-sse.js +21 -6
- package/dist/connector/mcp/http-sse.js.map +1 -1
- package/dist/connector/mcp/index.d.ts +7 -0
- package/dist/connector/mcp/index.d.ts.map +1 -1
- package/dist/connector/mcp/index.js +10 -0
- package/dist/connector/mcp/index.js.map +1 -1
- package/dist/connector/mcp/streamable-http.d.ts +83 -0
- package/dist/connector/mcp/streamable-http.d.ts.map +1 -1
- package/dist/connector/mcp/streamable-http.js +177 -11
- package/dist/connector/mcp/streamable-http.js.map +1 -1
- package/dist/connector/mcp/x-mcp-header.d.ts +56 -0
- package/dist/connector/mcp/x-mcp-header.d.ts.map +1 -0
- package/dist/connector/mcp/x-mcp-header.js +254 -0
- package/dist/connector/mcp/x-mcp-header.js.map +1 -0
- package/dist/constants/mcp/index.d.ts +123 -15
- package/dist/constants/mcp/index.d.ts.map +1 -1
- package/dist/constants/mcp/index.js +135 -16
- package/dist/constants/mcp/index.js.map +1 -1
- package/dist/manager/agent/lifecycle.d.ts.map +1 -1
- package/dist/manager/agent/lifecycle.js +23 -0
- package/dist/manager/agent/lifecycle.js.map +1 -1
- package/dist/manager/run/persistence.d.ts +8 -0
- package/dist/manager/run/persistence.d.ts.map +1 -1
- package/dist/manager/run/persistence.js +12 -0
- package/dist/manager/run/persistence.js.map +1 -1
- package/dist/prompt/coding-agent-doctrine.d.ts +20 -0
- package/dist/prompt/coding-agent-doctrine.d.ts.map +1 -1
- package/dist/prompt/coding-agent-doctrine.js +19 -3
- package/dist/prompt/coding-agent-doctrine.js.map +1 -1
- package/dist/prompt/index.d.ts +1 -1
- package/dist/prompt/index.d.ts.map +1 -1
- package/dist/prompt/index.js +1 -1
- package/dist/prompt/index.js.map +1 -1
- package/dist/public-runtime.d.ts +6 -4
- package/dist/public-runtime.d.ts.map +1 -1
- package/dist/public-runtime.js +8 -4
- package/dist/public-runtime.js.map +1 -1
- package/dist/public-tools.d.ts +11 -0
- package/dist/public-tools.d.ts.map +1 -1
- package/dist/public-tools.js +14 -0
- package/dist/public-tools.js.map +1 -1
- package/dist/registry/tool/execute.d.ts.map +1 -1
- package/dist/registry/tool/execute.js +2 -3
- package/dist/registry/tool/execute.js.map +1 -1
- package/dist/registry/tool/portable.d.ts +65 -0
- package/dist/registry/tool/portable.d.ts.map +1 -0
- package/dist/registry/tool/portable.js +244 -0
- package/dist/registry/tool/portable.js.map +1 -0
- package/dist/registry/tool/schema.d.ts +32 -5
- package/dist/registry/tool/schema.d.ts.map +1 -1
- package/dist/registry/tool/schema.js +35 -9
- package/dist/registry/tool/schema.js.map +1 -1
- package/dist/registry/toolset/catalog.js +8 -8
- package/dist/registry/toolset/catalog.js.map +1 -1
- package/dist/runtime/jobs/awaited-jobs.d.ts +215 -0
- package/dist/runtime/jobs/awaited-jobs.d.ts.map +1 -0
- package/dist/runtime/jobs/awaited-jobs.js +259 -0
- package/dist/runtime/jobs/awaited-jobs.js.map +1 -0
- package/dist/runtime/jobs/registry.d.ts +33 -2
- package/dist/runtime/jobs/registry.d.ts.map +1 -1
- package/dist/runtime/jobs/registry.js +37 -0
- package/dist/runtime/jobs/registry.js.map +1 -1
- package/dist/runtime/query/executor.d.ts +28 -0
- package/dist/runtime/query/executor.d.ts.map +1 -1
- package/dist/runtime/query/executor.js +39 -1
- package/dist/runtime/query/executor.js.map +1 -1
- package/dist/runtime/query/file-evidence-context.d.ts.map +1 -1
- package/dist/runtime/query/file-evidence-context.js +159 -43
- package/dist/runtime/query/file-evidence-context.js.map +1 -1
- package/dist/runtime/query/file-evidence-replay.d.ts +260 -0
- package/dist/runtime/query/file-evidence-replay.d.ts.map +1 -0
- package/dist/runtime/query/file-evidence-replay.js +647 -0
- package/dist/runtime/query/file-evidence-replay.js.map +1 -0
- package/dist/runtime/query/file-evidence-seed.d.ts +50 -0
- package/dist/runtime/query/file-evidence-seed.d.ts.map +1 -0
- package/dist/runtime/query/file-evidence-seed.js +100 -0
- package/dist/runtime/query/file-evidence-seed.js.map +1 -0
- package/dist/runtime/query/index.d.ts.map +1 -1
- package/dist/runtime/query/index.js +94 -2
- package/dist/runtime/query/index.js.map +1 -1
- package/dist/runtime/query/iteration/index.d.ts +87 -9
- package/dist/runtime/query/iteration/index.d.ts.map +1 -1
- package/dist/runtime/query/iteration/index.js +193 -28
- package/dist/runtime/query/iteration/index.js.map +1 -1
- package/dist/runtime/query/iteration/phases/context.d.ts +10 -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/phases/tool-review.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/tool-review.js +5 -1
- package/dist/runtime/query/iteration/phases/tool-review.js.map +1 -1
- package/dist/runtime/query/plugin-hooks.d.ts +14 -0
- package/dist/runtime/query/plugin-hooks.d.ts.map +1 -1
- package/dist/runtime/query/plugin-hooks.js +18 -0
- package/dist/runtime/query/plugin-hooks.js.map +1 -1
- package/dist/runtime/query/repeat-call.d.ts +17 -4
- package/dist/runtime/query/repeat-call.d.ts.map +1 -1
- package/dist/runtime/query/repeat-call.js +26 -19
- package/dist/runtime/query/repeat-call.js.map +1 -1
- package/dist/runtime/query/steering.d.ts +11 -1
- package/dist/runtime/query/steering.d.ts.map +1 -1
- package/dist/runtime/query/steering.js +12 -1
- package/dist/runtime/query/steering.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/sandbox/provider/local.d.ts.map +1 -1
- package/dist/sandbox/provider/local.js +46 -3
- package/dist/sandbox/provider/local.js.map +1 -1
- package/dist/scheduler/completion-inbox.d.ts +48 -2
- package/dist/scheduler/completion-inbox.d.ts.map +1 -1
- package/dist/scheduler/completion-inbox.js +102 -10
- package/dist/scheduler/completion-inbox.js.map +1 -1
- package/dist/scheduler/local.d.ts.map +1 -1
- package/dist/scheduler/local.js +8 -0
- package/dist/scheduler/local.js.map +1 -1
- package/dist/store/run/disk.d.ts +35 -1
- package/dist/store/run/disk.d.ts.map +1 -1
- package/dist/store/run/disk.js +100 -0
- package/dist/store/run/disk.js.map +1 -1
- package/dist/tools/builtins/bash.d.ts.map +1 -1
- package/dist/tools/builtins/bash.js +4 -10
- package/dist/tools/builtins/bash.js.map +1 -1
- package/dist/tools/builtins/edit-apply.d.ts +126 -0
- package/dist/tools/builtins/edit-apply.d.ts.map +1 -0
- package/dist/tools/builtins/edit-apply.js +360 -0
- package/dist/tools/builtins/edit-apply.js.map +1 -0
- package/dist/tools/builtins/edit.d.ts +143 -1
- package/dist/tools/builtins/edit.d.ts.map +1 -1
- package/dist/tools/builtins/edit.js +37 -219
- package/dist/tools/builtins/edit.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 +9 -3
- package/dist/tools/builtins/index.js.map +1 -1
- package/dist/tools/builtins/job.js +1 -1
- package/dist/tools/builtins/job.js.map +1 -1
- package/dist/tools/builtins/read-file.d.ts +2 -2
- package/dist/tools/builtins/read-file.d.ts.map +1 -1
- package/dist/tools/builtins/read-file.js +50 -65
- package/dist/tools/builtins/read-file.js.map +1 -1
- package/dist/tools/builtins/read-render.d.ts +56 -0
- package/dist/tools/builtins/read-render.d.ts.map +1 -0
- package/dist/tools/builtins/read-render.js +73 -0
- package/dist/tools/builtins/read-render.js.map +1 -0
- package/dist/tools/builtins/wait-for-job-bounds.d.ts +67 -0
- package/dist/tools/builtins/wait-for-job-bounds.d.ts.map +1 -0
- package/dist/tools/builtins/wait-for-job-bounds.js +108 -0
- package/dist/tools/builtins/wait-for-job-bounds.js.map +1 -0
- package/dist/tools/builtins/wait-for-job.d.ts +6 -0
- package/dist/tools/builtins/wait-for-job.d.ts.map +1 -0
- package/dist/tools/builtins/wait-for-job.js +162 -0
- package/dist/tools/builtins/wait-for-job.js.map +1 -0
- package/dist/tools/builtins/write-file.js +5 -0
- package/dist/tools/builtins/write-file.js.map +1 -1
- package/dist/tools/coordinator/index.d.ts.map +1 -1
- package/dist/tools/coordinator/index.js +1 -7
- package/dist/tools/coordinator/index.js.map +1 -1
- package/dist/tools/file-read-tracker.d.ts.map +1 -1
- package/dist/tools/file-read-tracker.js +88 -10
- package/dist/tools/file-read-tracker.js.map +1 -1
- package/dist/types/agent/scheduler.d.ts +20 -0
- package/dist/types/agent/scheduler.d.ts.map +1 -1
- package/dist/types/agent/task.d.ts +20 -0
- package/dist/types/agent/task.d.ts.map +1 -1
- package/dist/types/connector/mcp.d.ts +205 -0
- package/dist/types/connector/mcp.d.ts.map +1 -1
- package/dist/types/message/index.d.ts +1 -1
- package/dist/types/message/index.d.ts.map +1 -1
- package/dist/types/message/index.js +2 -0
- package/dist/types/message/index.js.map +1 -1
- package/dist/types/run/entity.d.ts +13 -0
- package/dist/types/run/entity.d.ts.map +1 -1
- package/dist/types/run/events.d.ts +56 -0
- package/dist/types/run/events.d.ts.map +1 -1
- package/dist/types/run/events.js.map +1 -1
- package/dist/types/run/store.d.ts +41 -0
- package/dist/types/run/store.d.ts.map +1 -1
- package/dist/types/sandbox/index.d.ts +83 -15
- package/dist/types/sandbox/index.d.ts.map +1 -1
- package/dist/types/sandbox/index.js.map +1 -1
- package/dist/types/tool/index.d.ts +109 -0
- package/dist/types/tool/index.d.ts.map +1 -1
- package/dist/types/tool/index.js.map +1 -1
- package/dist/utils/env.d.ts +19 -0
- package/dist/utils/env.d.ts.map +1 -0
- package/dist/utils/env.js +25 -0
- package/dist/utils/env.js.map +1 -0
- package/package.json +1 -1
- package/src/bridge/a2a/mapper.ts +8 -0
- package/src/bridge/sse/mapper.ts +11 -0
- package/src/connector/index.ts +27 -0
- package/src/connector/mcp/adapter.ts +123 -10
- package/src/connector/mcp/audio-admission.ts +173 -0
- package/src/connector/mcp/client.ts +694 -45
- package/src/connector/mcp/envelope.ts +235 -0
- package/src/connector/mcp/era.ts +400 -0
- package/src/connector/mcp/errors.ts +171 -0
- package/src/connector/mcp/http-sse.ts +23 -6
- package/src/connector/mcp/index.ts +37 -0
- package/src/connector/mcp/streamable-http.ts +199 -11
- package/src/connector/mcp/x-mcp-header.ts +322 -0
- package/src/constants/mcp/index.ts +145 -16
- package/src/manager/agent/lifecycle.ts +29 -0
- package/src/manager/run/persistence.ts +12 -0
- package/src/prompt/coding-agent-doctrine.ts +31 -4
- package/src/prompt/index.ts +1 -0
- package/src/public-runtime.ts +37 -1
- package/src/public-tools.ts +18 -0
- package/src/registry/tool/execute.ts +2 -4
- package/src/registry/tool/portable.ts +264 -0
- package/src/registry/tool/schema.ts +38 -8
- package/src/registry/toolset/catalog.ts +8 -9
- package/src/runtime/jobs/awaited-jobs.ts +271 -0
- package/src/runtime/jobs/registry.ts +50 -0
- package/src/runtime/query/executor.ts +49 -1
- package/src/runtime/query/file-evidence-context.ts +190 -46
- package/src/runtime/query/file-evidence-replay.ts +776 -0
- package/src/runtime/query/file-evidence-seed.ts +126 -0
- package/src/runtime/query/index.ts +104 -2
- package/src/runtime/query/iteration/index.ts +202 -28
- package/src/runtime/query/iteration/phases/context.ts +10 -0
- package/src/runtime/query/iteration/phases/tool-review.ts +4 -0
- package/src/runtime/query/plugin-hooks.ts +20 -0
- package/src/runtime/query/repeat-call.ts +28 -18
- package/src/runtime/query/steering.ts +11 -0
- package/src/runtime/query/tooling.ts +3 -0
- package/src/sandbox/provider/local.ts +45 -2
- package/src/scheduler/completion-inbox.ts +105 -9
- package/src/scheduler/local.ts +8 -0
- package/src/store/run/disk.ts +108 -0
- package/src/tools/builtins/bash.ts +4 -10
- package/src/tools/builtins/edit-apply.ts +456 -0
- package/src/tools/builtins/edit.ts +39 -270
- package/src/tools/builtins/index.ts +9 -3
- package/src/tools/builtins/job.ts +1 -1
- package/src/tools/builtins/read-file.ts +56 -77
- package/src/tools/builtins/read-render.ts +104 -0
- package/src/tools/builtins/wait-for-job-bounds.ts +179 -0
- package/src/tools/builtins/wait-for-job.ts +184 -0
- package/src/tools/builtins/write-file.ts +5 -0
- package/src/tools/coordinator/index.ts +1 -7
- package/src/tools/file-read-tracker.ts +85 -7
- package/src/types/agent/scheduler.ts +21 -0
- package/src/types/agent/task.ts +21 -0
- package/src/types/connector/mcp.ts +205 -1
- package/src/types/message/index.ts +2 -0
- package/src/types/run/entity.ts +14 -0
- package/src/types/run/events.ts +56 -0
- package/src/types/run/store.ts +42 -0
- package/src/types/sandbox/index.ts +84 -15
- package/src/types/tool/index.ts +104 -0
- package/src/utils/env.ts +23 -0
|
@@ -17,6 +17,7 @@ import { DELEGATION_TIMEOUT_MS } from '../../../tools/coordinator/index.js';
|
|
|
17
17
|
import { NamzuError } from '../../../types/errors/index.js';
|
|
18
18
|
import { createAssistantMessage, createRuntimeContextMessage, createSystemMessage, } from '../../../types/message/index.js';
|
|
19
19
|
import { classifyProviderError } from '../../../types/provider/errors.js';
|
|
20
|
+
import { readPositiveIntEnv } from '../../../utils/env.js';
|
|
20
21
|
import { toErrorMessage } from '../../../utils/error.js';
|
|
21
22
|
import { stableDigest } from '../../../utils/hash.js';
|
|
22
23
|
import { generateMessageId } from '../../../utils/id.js';
|
|
@@ -25,7 +26,7 @@ import { projectObservationContext } from '../observation-context.js';
|
|
|
25
26
|
import { applyLifecycleHookResults } from '../plugin-hooks.js';
|
|
26
27
|
import { diffRequestContext, snapshotRequestContext, } from '../request-context.js';
|
|
27
28
|
import { DEFAULT_MAX_REQUEST_RICH_CONTENT_BYTES, markProviderRejectedImage, projectRequestRichContent, } from '../request-rich-content.js';
|
|
28
|
-
import { formatSteeringNote, isOperatorUserMessage } from '../steering.js';
|
|
29
|
+
import { formatJobNote, formatSteeringNote, isOperatorUserMessage } from '../steering.js';
|
|
29
30
|
import { parseNativeCandidate } from './native-output.js';
|
|
30
31
|
import { runAdvisoryPhase } from './phases/advisory.js';
|
|
31
32
|
import { runIterationCheckpoint } from './phases/checkpoint.js';
|
|
@@ -116,6 +117,42 @@ const SETTLE_GRACE_FRACTION = 0.5;
|
|
|
116
117
|
export function settleGraceMs(remainingBeforeFinalizeMs) {
|
|
117
118
|
return Math.min(Math.floor(remainingBeforeFinalizeMs * SETTLE_GRACE_FRACTION), DELEGATION_TIMEOUT_MS);
|
|
118
119
|
}
|
|
120
|
+
/**
|
|
121
|
+
* The ceiling on the job half of that grace, in milliseconds.
|
|
122
|
+
*
|
|
123
|
+
* `DELEGATION_TIMEOUT_MS` is the wrong ceiling for a shell job, and the gap
|
|
124
|
+
* only opens where it matters most: a run with no `timeoutMs` — the CLI's
|
|
125
|
+
* shipping default, `No run deadline by default` — has infinite time before
|
|
126
|
+
* it must start finishing, so `settleGraceMs` returns the ceiling flat. For a
|
|
127
|
+
* delegated task that is sound, because the hour is the longest the task
|
|
128
|
+
* itself may live: the hold cannot outlast the work. A background job has no
|
|
129
|
+
* such bound. `tail -f`, a watcher and a dev server all outlive any hold, so
|
|
130
|
+
* the same arithmetic parks an interactive session for an hour on a job that
|
|
131
|
+
* was never going to exit.
|
|
132
|
+
*
|
|
133
|
+
* So the job leg gets its own bound, and it is sized to what the wait buys
|
|
134
|
+
* rather than to how long a job may live: a turn in which to use the exit.
|
|
135
|
+
* A model that already waited its `wait_for_job` bound out and saw nothing is
|
|
136
|
+
* not usually two minutes from an exit, and the run ending is not the news
|
|
137
|
+
* being lost — with no run in flight the session announces the exit itself
|
|
138
|
+
* (`docs/cli/background-jobs.md`, *Learning that it ended*), which is the
|
|
139
|
+
* cheaper of the two places to hear it.
|
|
140
|
+
*/
|
|
141
|
+
const DEFAULT_JOB_HOLD_MAX_MS = 2 * 60 * 1000;
|
|
142
|
+
/**
|
|
143
|
+
* The same share of the run, under {@link DEFAULT_JOB_HOLD_MAX_MS}.
|
|
144
|
+
*
|
|
145
|
+
* `NAMZU_JOB_HOLD_MAX_MS` overrides the ceiling for a host that wants a
|
|
146
|
+
* longer or shorter park, the way `NAMZU_JOB_WAIT_TIMEOUT_MS` overrides
|
|
147
|
+
* `wait_for_job`'s own bound — and it is the same parse, so a value that is
|
|
148
|
+
* not a positive whole number of milliseconds leaves the default standing
|
|
149
|
+
* rather than holding a run for `NaN`. Called here rather than at module
|
|
150
|
+
* load, because a host that sets it after import is not ignored.
|
|
151
|
+
*/
|
|
152
|
+
export function awaitedJobGraceMs(remainingBeforeFinalizeMs) {
|
|
153
|
+
const ceiling = readPositiveIntEnv('NAMZU_JOB_HOLD_MAX_MS', DEFAULT_JOB_HOLD_MAX_MS);
|
|
154
|
+
return Math.min(settleGraceMs(remainingBeforeFinalizeMs), ceiling);
|
|
155
|
+
}
|
|
119
156
|
export class IterationOrchestrator {
|
|
120
157
|
ctx;
|
|
121
158
|
advisoryTurn;
|
|
@@ -1301,8 +1338,10 @@ export class IterationOrchestrator {
|
|
|
1301
1338
|
// returned — which is what makes a terminal submit_answer tool
|
|
1302
1339
|
// usable without discarding its output.
|
|
1303
1340
|
if (await this.shouldStop()) {
|
|
1304
|
-
// Outstanding
|
|
1305
|
-
//
|
|
1341
|
+
// Outstanding work outranks the host's stop predicate —
|
|
1342
|
+
// a delegated task the completion inbox is expecting, or
|
|
1343
|
+
// a background job the model told `wait_for_job` it is
|
|
1344
|
+
// waiting on.
|
|
1306
1345
|
//
|
|
1307
1346
|
// This is a precedence rule chosen here, not something
|
|
1308
1347
|
// `stopWhen` implies — a stop predicate is a programmable
|
|
@@ -1311,13 +1350,20 @@ export class IterationOrchestrator {
|
|
|
1311
1350
|
// tool or a captured structured output. Those decide the
|
|
1312
1351
|
// result, so no turn follows and a hold would buy nothing.
|
|
1313
1352
|
// This one only says "stop", and stopping one turn later
|
|
1314
|
-
// with the
|
|
1315
|
-
//
|
|
1353
|
+
// with the result in hand is a better reading of the
|
|
1354
|
+
// host's intent than stopping now and discarding it.
|
|
1316
1355
|
//
|
|
1317
|
-
// Bounded
|
|
1318
|
-
//
|
|
1319
|
-
//
|
|
1320
|
-
//
|
|
1356
|
+
// Bounded by what is left to deliver, not by a count.
|
|
1357
|
+
// Each delivery consumes what it delivered — the inbox is
|
|
1358
|
+
// drained, and a job exit's notice is taken with the
|
|
1359
|
+
// record of the exits it accounts for — so the predicate
|
|
1360
|
+
// is asked again next turn against whatever is still
|
|
1361
|
+
// outstanding. One task deferred it once; two awaited
|
|
1362
|
+
// jobs exiting a minute apart defer it twice, each time
|
|
1363
|
+
// for a turn the model spends on news it has not read.
|
|
1364
|
+
// `maxIterations` and the run's own deadline bound all of
|
|
1365
|
+
// it regardless, and a leg with nothing pending never
|
|
1366
|
+
// opens a hold at all.
|
|
1321
1367
|
if (yield* this.holdForOutstandingWork(iterationNum, true)) {
|
|
1322
1368
|
// Remember WHY the next turn exists, so the turn that
|
|
1323
1369
|
// ends the run can name the host's decision instead of
|
|
@@ -1529,27 +1575,60 @@ export class IterationOrchestrator {
|
|
|
1529
1575
|
}
|
|
1530
1576
|
}
|
|
1531
1577
|
/**
|
|
1532
|
-
* Hold the run open for
|
|
1578
|
+
* Hold the run open for work that has not finished, and deliver it.
|
|
1533
1579
|
*
|
|
1534
|
-
* Returns whether a completion or operator message entered
|
|
1535
|
-
* the caller continues on `true`, so the model gets a turn
|
|
1536
|
-
* That turn is the entire justification for waiting, which
|
|
1580
|
+
* Returns whether a completion, a job exit or an operator message entered
|
|
1581
|
+
* the transcript — the caller continues on `true`, so the model gets a turn
|
|
1582
|
+
* to respond. That turn is the entire justification for waiting, which
|
|
1537
1583
|
* is why only the exits that can still take one call this.
|
|
1538
1584
|
*
|
|
1539
|
-
*
|
|
1540
|
-
*
|
|
1585
|
+
* Two kinds of work qualify and they are raced together, because a run has
|
|
1586
|
+
* one settle point and one grace period to spend at it:
|
|
1587
|
+
*
|
|
1588
|
+
* - a delegated task the `CompletionInbox` is still expecting;
|
|
1589
|
+
* - a background job the model told `wait_for_job` it is waiting on.
|
|
1590
|
+
*
|
|
1591
|
+
* The job half is deliberately narrow. Intent comes from the wait and from
|
|
1592
|
+
* nothing else — a dev server the model started and never waited on is
|
|
1593
|
+
* running because somebody wanted it running, and a hold for it would add
|
|
1594
|
+
* the grace period to the end of every turn for the rest of the session.
|
|
1595
|
+
*
|
|
1596
|
+
* Each leg is opened only when it has something pending: both
|
|
1597
|
+
* `waitForArrival` implementations resolve immediately when their own side
|
|
1598
|
+
* is idle, so racing an idle one would end the hold before it began.
|
|
1599
|
+
*
|
|
1600
|
+
* Bounded by `settleGraceMs` and by `maxIterations`, so work that never
|
|
1601
|
+
* finishes cannot keep the run open. On a run with a deadline the grace is
|
|
1602
|
+
* a share of what is LEFT of it rather than a fresh allowance, so a
|
|
1603
|
+
* `wait_for_job` call that already spent minutes has shortened this hold
|
|
1604
|
+
* by the same minutes. On a run without one — the CLI's default — there is
|
|
1605
|
+
* no remainder to take a share of, and the job leg's own ceiling
|
|
1606
|
+
* (`awaitedJobGraceMs`) is what keeps a timed-out wait from being followed
|
|
1607
|
+
* by an hour of silence.
|
|
1541
1608
|
*/
|
|
1542
1609
|
async *holdForOutstandingWork(iterationNum, hasToolCalls) {
|
|
1543
|
-
|
|
1610
|
+
const inbox = this.ctx.completionInbox?.hasPendingWork ? this.ctx.completionInbox : undefined;
|
|
1611
|
+
const jobs = this.ctx.awaitedJobs?.hasPendingWork ? this.ctx.awaitedJobs : undefined;
|
|
1612
|
+
if (!inbox && !jobs)
|
|
1544
1613
|
return false;
|
|
1545
1614
|
// Read HERE rather than from `forceFinalize`, which was sampled at the
|
|
1546
1615
|
// top of the iteration: one that has since crossed the finalize point
|
|
1547
1616
|
// must not open a wait against a reserve it has already entered.
|
|
1548
|
-
const
|
|
1549
|
-
|
|
1617
|
+
const remainingMs = this.ctx.guard.remainingBeforeFinalizeMs();
|
|
1618
|
+
// One deadline for the race, and it is the LONGEST ceiling any pending
|
|
1619
|
+
// leg justifies. A leg resolving on its own timer ends the whole race,
|
|
1620
|
+
// so handing the job leg its shorter ceiling while a task was also
|
|
1621
|
+
// outstanding would cut the task's hold down to the job's — a run
|
|
1622
|
+
// walking away from a worker it had time for, because a job happened
|
|
1623
|
+
// to be running. A job therefore never shortens a wait, and it never
|
|
1624
|
+
// lengthens one either: where a task is outstanding too, that is how
|
|
1625
|
+
// long this run was waiting anyway.
|
|
1626
|
+
const graceMs = inbox ? settleGraceMs(remainingMs) : awaitedJobGraceMs(remainingMs);
|
|
1627
|
+
this.ctx.log.info('Holding the run open for outstanding work', {
|
|
1550
1628
|
[NAMZU.RUN_ID]: this.ctx.runMgr.id,
|
|
1551
1629
|
[NAMZU.ITERATION]: iterationNum,
|
|
1552
1630
|
'namzu.runtime.grace_ms': graceMs,
|
|
1631
|
+
'namzu.runtime.awaited_jobs': jobs?.outstandingJobIds ?? [],
|
|
1553
1632
|
});
|
|
1554
1633
|
// User input releases this wait without cancelling any child. Both waits
|
|
1555
1634
|
// share a disposable signal so the losing arrival listener cannot leak.
|
|
@@ -1561,7 +1640,8 @@ export class IterationOrchestrator {
|
|
|
1561
1640
|
cancelWait();
|
|
1562
1641
|
try {
|
|
1563
1642
|
await Promise.race([
|
|
1564
|
-
|
|
1643
|
+
...(inbox ? [inbox.waitForArrival(graceMs, waiting.signal)] : []),
|
|
1644
|
+
...(jobs ? [jobs.waitForArrival(graceMs, waiting.signal)] : []),
|
|
1565
1645
|
...(this.ctx.waitForInbound ? [this.ctx.waitForInbound(waiting.signal)] : []),
|
|
1566
1646
|
]);
|
|
1567
1647
|
}
|
|
@@ -1574,12 +1654,13 @@ export class IterationOrchestrator {
|
|
|
1574
1654
|
runSignal.removeEventListener('abort', cancelWait);
|
|
1575
1655
|
}
|
|
1576
1656
|
runSignal.throwIfAborted();
|
|
1577
|
-
const arrived = this.ctx.completionInbox
|
|
1657
|
+
const arrived = this.ctx.completionInbox?.drain() ?? [];
|
|
1578
1658
|
if (arrived.length > 0) {
|
|
1579
1659
|
this.ctx.runMgr.pushMessage(createRuntimeContextMessage(formatCompletionNotification(arrived), 'task-completion'));
|
|
1580
1660
|
}
|
|
1661
|
+
const exited = this.deliverAwaitedJobExits();
|
|
1581
1662
|
const inbound = this.deliverInbound();
|
|
1582
|
-
if (arrived.length === 0 && inbound === 0)
|
|
1663
|
+
if (arrived.length === 0 && !exited && inbound === 0)
|
|
1583
1664
|
return false;
|
|
1584
1665
|
await this.ctx.emitEvent({
|
|
1585
1666
|
type: 'iteration_completed',
|
|
@@ -1591,8 +1672,45 @@ export class IterationOrchestrator {
|
|
|
1591
1672
|
return true;
|
|
1592
1673
|
}
|
|
1593
1674
|
/**
|
|
1594
|
-
*
|
|
1595
|
-
*
|
|
1675
|
+
* Put the job exits this hold was waiting for in front of the model.
|
|
1676
|
+
*
|
|
1677
|
+
* Through `jobNotices`, which is the channel a job exit already travels on
|
|
1678
|
+
* — `attachNotice` rides it out on the next tool result — rather than a
|
|
1679
|
+
* second one built for this path. A turn that called no tools has no such
|
|
1680
|
+
* result, so the queued text becomes a `runtime-context` message instead,
|
|
1681
|
+
* exactly as `deliverInbound` does for steering that found no tool result
|
|
1682
|
+
* to attach to.
|
|
1683
|
+
*
|
|
1684
|
+
* That drain is also what keeps one exit from being delivered twice: the
|
|
1685
|
+
* channel hands its text over once, so an exit already attached to a tool
|
|
1686
|
+
* result earlier in the turn leaves nothing here — and the record of it
|
|
1687
|
+
* went with that delivery, so this returns `false` rather than buying a
|
|
1688
|
+
* turn to re-read what the model has read.
|
|
1689
|
+
*
|
|
1690
|
+
* `takeDelivery` is what pairs the two. Taking the exits first and then
|
|
1691
|
+
* finding no notice would discard them, which is the one way this path
|
|
1692
|
+
* can lose an exit outright; neither is taken unless both are there.
|
|
1693
|
+
*
|
|
1694
|
+
* The channel is not per-job, so the text taken here can include a notice
|
|
1695
|
+
* for a job nobody awaited that ended while the hold was open. Delivering
|
|
1696
|
+
* it is right — it is unread either way, and the alternative is stranding
|
|
1697
|
+
* it — but it is not a reason to WAIT, which is why what opens this hold
|
|
1698
|
+
* is `AwaitedJobs`, and the two are asked separately.
|
|
1699
|
+
*/
|
|
1700
|
+
deliverAwaitedJobExits() {
|
|
1701
|
+
const delivered = this.ctx.awaitedJobs?.takeDelivery(() => this.ctx.jobNotices?.drain());
|
|
1702
|
+
if (!delivered)
|
|
1703
|
+
return false;
|
|
1704
|
+
this.ctx.log.info('Delivering a background job exit the run held open for', {
|
|
1705
|
+
[NAMZU.RUN_ID]: this.ctx.runMgr.id,
|
|
1706
|
+
'namzu.runtime.jobs': delivered.exits.map((job) => job.id),
|
|
1707
|
+
});
|
|
1708
|
+
this.ctx.runMgr.pushMessage(createRuntimeContextMessage(formatJobNote(delivered.text), 'job-exit'));
|
|
1709
|
+
return true;
|
|
1710
|
+
}
|
|
1711
|
+
/**
|
|
1712
|
+
* Account for outstanding work on the way out: deliver what arrived, and
|
|
1713
|
+
* say what did not.
|
|
1596
1714
|
*
|
|
1597
1715
|
* A run that ends with a worker outstanding must not leave the impression
|
|
1598
1716
|
* that the worker's result was delivered. There are exactly two honest
|
|
@@ -1616,18 +1734,31 @@ export class IterationOrchestrator {
|
|
|
1616
1734
|
*/
|
|
1617
1735
|
settleOutstandingWork() {
|
|
1618
1736
|
this.deliverArrivedCompletions();
|
|
1737
|
+
this.deliverArrivedJobExits();
|
|
1619
1738
|
this.recordAbandonedWork();
|
|
1620
1739
|
}
|
|
1621
|
-
/**
|
|
1740
|
+
/** Work this run walked away from. See {@link settleOutstandingWork}. */
|
|
1622
1741
|
recordAbandonedWork() {
|
|
1623
1742
|
const abandoned = this.ctx.completionInbox?.outstandingTaskIds ?? [];
|
|
1624
|
-
if (abandoned.length
|
|
1743
|
+
if (abandoned.length > 0) {
|
|
1744
|
+
this.ctx.log.warn('Run ended with delegated work still running', {
|
|
1745
|
+
[NAMZU.RUN_ID]: this.ctx.runMgr.id,
|
|
1746
|
+
'namzu.runtime.tasks': abandoned,
|
|
1747
|
+
});
|
|
1748
|
+
this.ctx.runMgr.setAbandonedTaskIds(abandoned);
|
|
1749
|
+
}
|
|
1750
|
+
// The same statement for a job the model was waiting on when the grace
|
|
1751
|
+
// ran out. Only awaited ones: a job nobody waited for was never work
|
|
1752
|
+
// this run was holding, so naming it would report an abandonment that
|
|
1753
|
+
// did not happen.
|
|
1754
|
+
const abandonedJobs = this.ctx.awaitedJobs?.outstandingJobIds ?? [];
|
|
1755
|
+
if (abandonedJobs.length === 0)
|
|
1625
1756
|
return;
|
|
1626
|
-
this.ctx.log.warn('Run ended with
|
|
1757
|
+
this.ctx.log.warn('Run ended with an awaited background job still running', {
|
|
1627
1758
|
[NAMZU.RUN_ID]: this.ctx.runMgr.id,
|
|
1628
|
-
'namzu.runtime.
|
|
1759
|
+
'namzu.runtime.jobs': abandonedJobs,
|
|
1629
1760
|
});
|
|
1630
|
-
this.ctx.runMgr.
|
|
1761
|
+
this.ctx.runMgr.setAbandonedJobIds(abandonedJobs);
|
|
1631
1762
|
}
|
|
1632
1763
|
deliverArrivedCompletions() {
|
|
1633
1764
|
const unheard = this.ctx.completionInbox?.drain() ?? [];
|
|
@@ -1658,6 +1789,40 @@ export class IterationOrchestrator {
|
|
|
1658
1789
|
});
|
|
1659
1790
|
this.ctx.runMgr.pushMessage(createRuntimeContextMessage(formatCompletionNotification(unheard), 'task-completion'));
|
|
1660
1791
|
}
|
|
1792
|
+
/**
|
|
1793
|
+
* The job half of {@link deliverArrivedCompletions}: an exit that arrived
|
|
1794
|
+
* too late to earn a turn is still delivered on the way out.
|
|
1795
|
+
*
|
|
1796
|
+
* The window this closes is one tick wide and it is nobody else's. An
|
|
1797
|
+
* awaited job that exits between the hold's grace expiring and the run
|
|
1798
|
+
* settling was never delivered — the hold had already looked — and is no
|
|
1799
|
+
* longer named either, because the exit took it off the outstanding list
|
|
1800
|
+
* on its way past, so `abandonedJobIds` would be lying to claim it. The
|
|
1801
|
+
* host's own listener is no help: the CLI queues an exit for the next
|
|
1802
|
+
* turn only when no run is in flight, and this one is still in flight.
|
|
1803
|
+
* Delivered here it reaches `Run.messages`, so the transcript has it and
|
|
1804
|
+
* a continued thread opens with it.
|
|
1805
|
+
*
|
|
1806
|
+
* Before `recordAbandonedWork`, which then reports only what is still
|
|
1807
|
+
* running, and after `deliverArrivedCompletions`, so the two appended
|
|
1808
|
+
* messages land in the order the work finished in.
|
|
1809
|
+
*/
|
|
1810
|
+
deliverArrivedJobExits() {
|
|
1811
|
+
const delivered = this.ctx.awaitedJobs?.takeDelivery(() => this.ctx.jobNotices?.drain());
|
|
1812
|
+
if (!delivered)
|
|
1813
|
+
return;
|
|
1814
|
+
// Fix the run's answer BEFORE appending anything after it — the same
|
|
1815
|
+
// `resolveResult` tail walk `deliverArrivedCompletions` explains just
|
|
1816
|
+
// above, and the same guard against pinning an empty one.
|
|
1817
|
+
const answer = this.ctx.runMgr.materializeResult();
|
|
1818
|
+
if (answer.length > 0)
|
|
1819
|
+
this.ctx.runMgr.setResult(answer);
|
|
1820
|
+
this.ctx.log.info('Delivering a background job exit the run would have settled over', {
|
|
1821
|
+
[NAMZU.RUN_ID]: this.ctx.runMgr.id,
|
|
1822
|
+
'namzu.runtime.jobs': delivered.exits.map((job) => job.id),
|
|
1823
|
+
});
|
|
1824
|
+
this.ctx.runMgr.pushMessage(createRuntimeContextMessage(formatJobNote(delivered.text), 'job-exit'));
|
|
1825
|
+
}
|
|
1661
1826
|
stepContextMessage(content) {
|
|
1662
1827
|
return createRuntimeContextMessage(`Current step context (runtime-generated; not a new user request):\n${content}`, 'step-context');
|
|
1663
1828
|
}
|