@sema-agent/core 5.50.0 → 5.52.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 +115 -0
- package/dist/agents/send-message-tool.d.ts +13 -2
- package/dist/agents/send-message-tool.js +13 -7
- package/dist/agents/subagent.js +16 -4
- package/dist/brain/anthropic.js +6 -2
- package/dist/brain/reasoning.d.ts +10 -2
- package/dist/brain/request-params.d.ts +20 -4
- package/dist/brain/status-sink.d.ts +56 -0
- package/dist/brain/status-sink.js +16 -0
- package/dist/core/auto-mode-prompt.js +9 -1
- package/dist/core/hooks.d.ts +24 -1
- package/dist/core/hooks.js +26 -4
- package/dist/core/mcp.js +37 -12
- package/dist/core/memory-engine/delegation-settlement.d.ts +15 -5
- package/dist/core/memory-engine/delegation-settlement.js +3 -3
- package/dist/core/memory-engine/engine.js +10 -2
- package/dist/core/reminder-disclosure.d.ts +41 -0
- package/dist/core/reminder-disclosure.js +11 -1
- package/dist/core/runner/assemble-result.d.ts +6 -0
- package/dist/core/runner/assemble-result.js +1 -1
- package/dist/core/runner/prepare-task.d.ts +15 -0
- package/dist/core/runner/prepare-task.js +89 -43
- package/dist/core/runner/runtask.d.ts +5 -1
- package/dist/core/runner/runtask.js +57 -26
- package/dist/core/task-registry-agent.js +3 -3
- package/dist/core/task-registry-shared.d.ts +6 -0
- package/dist/core/task-registry.js +4 -2
- package/dist/core/tool-policy.d.ts +54 -0
- package/dist/core/tool-policy.js +72 -12
- package/dist/core/tools.js +7 -0
- package/dist/core/trace.d.ts +13 -1
- package/dist/core/types.d.ts +51 -6
- package/dist/engine/harness/agent-harness.d.ts +30 -0
- package/dist/engine/harness/agent-harness.js +41 -7
- package/dist/engine/loop/agent-loop.js +95 -30
- package/dist/engine/loop/types.d.ts +32 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/tools/web.d.ts +10 -1
- package/dist/tools/web.js +5 -4
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +4 -1
package/dist/core/trace.d.ts
CHANGED
|
@@ -142,7 +142,7 @@ export type TraceEvent = {
|
|
|
142
142
|
} | {
|
|
143
143
|
/**
|
|
144
144
|
* How a task's requested reasoning intensity RESOLVED against the model's real capability (design/96 S6).
|
|
145
|
-
* Emitted
|
|
145
|
+
* Emitted at task start when thinking is on, so a deployment can SEE — not silently swallow (§E
|
|
146
146
|
* honesty red-line) — that a binary provider ignored the tier (`graded:false`), that an effort endpoint
|
|
147
147
|
* clamped it down (`clamped:true`), or that a NON-reasoning model dropped the request entirely
|
|
148
148
|
* (`dropped:true` — the frame fires for that model too; it used to be the one arm with no report).
|
|
@@ -150,6 +150,18 @@ export type TraceEvent = {
|
|
|
150
150
|
* task's PRIMARY serving model at leg entry (same law as `TaskResult.model`): a mid-run
|
|
151
151
|
* degradation does not re-emit this frame — the switch is observed on its own seats
|
|
152
152
|
* (`TaskResult.degraded`).
|
|
153
|
+
*
|
|
154
|
+
* A SECOND frame follows, for the same taskId, in exactly one case: the leg's FIRST
|
|
155
|
+
* committed request reported wire facts that CHANGE the resolution the task-start frame claimed
|
|
156
|
+
* (today: the anthropic budget path's cap-wins skip, where a hard per-request output cap too small
|
|
157
|
+
* to host a legal thinking budget deletes the thinking block — so the honest report is
|
|
158
|
+
* `dropped:true`, not the gradient the entry frame guessed). The correction is deduped (a first
|
|
159
|
+
* request that changes nothing emits nothing) and capped at one (only the FIRST committed request
|
|
160
|
+
* is consumed, so the leg's nested internal calls — compaction summary, side query — cannot
|
|
161
|
+
* rewrite the leg's posture). A consumer that keeps only the LATEST frame per taskId is therefore
|
|
162
|
+
* always reading the truth, and one that kept only the first now under-reports a drop it could not
|
|
163
|
+
* have seen before. The result-face twin `TaskResult.effectiveReasoning` moves with it — the two
|
|
164
|
+
* faces are one mint at every instant.
|
|
153
165
|
*/
|
|
154
166
|
kind: "reasoning.resolved";
|
|
155
167
|
version: 1;
|
package/dist/core/types.d.ts
CHANGED
|
@@ -270,7 +270,8 @@ export interface ToolSpec<TParams extends TSchema = TSchema> {
|
|
|
270
270
|
* design/77 §4: optional deployment-injected probe for an `irreversibility:"maybe"` tool. Given the call's
|
|
271
271
|
* (post-hook) args, it reports whether THIS specific call is reversible. The gate calls it ONLY when the
|
|
272
272
|
* surviving decision is `allow` and `irreversibility` resolves to `"maybe"`; it is time-bounded (the
|
|
273
|
-
* approval timeout
|
|
273
|
+
* approval timeout when configured, a 30s default when absent — the probe wait is always finite) and
|
|
274
|
+
* **fail-closed**: a non-`reversible` verdict, a timeout, or a throw all tighten to
|
|
274
275
|
* `ask`. Declaring this probe defaults `irreversibility` to `"maybe"`. It is read from the spec at
|
|
275
276
|
* prepare-time and captured in a closure — NOT a tool argument — so the model cannot monkey-patch it.
|
|
276
277
|
*
|
|
@@ -2898,15 +2899,25 @@ export interface TaskResult {
|
|
|
2898
2899
|
* (`requested`/`effective`/`graded`/`clamped`/`format`/`endpoint`, plus `dropped:true` when a
|
|
2899
2900
|
* non-reasoning model dropped the request entirely — field semantics on
|
|
2900
2901
|
* {@link import("../brain/reasoning.js").ResolvedReasoning}). It is the SAME resolver output the
|
|
2901
|
-
* `reasoning.resolved` trace frame carries
|
|
2902
|
-
*
|
|
2903
|
-
*
|
|
2902
|
+
* `reasoning.resolved` trace frame carries — the two faces cannot tell different stories; this seat
|
|
2903
|
+
* serves consumers without a tracer (the trace frame is the deployment-observability face, this is the
|
|
2904
|
+
* caller face).
|
|
2904
2905
|
*
|
|
2905
2906
|
* **In-presence condition** — mirrors the trace frame exactly: present on every terminal of a leg that ran
|
|
2906
2907
|
* with a REQUESTED thinking tier other than off/unset (the `spec > role > model.defaultThinking` chain);
|
|
2907
2908
|
* absent when thinking was off/unset for the leg, and on prepare failures (the resolution is minted after
|
|
2908
2909
|
* prepare). On a resumed task each leg re-resolves against the leg's own serving model.
|
|
2909
2910
|
*
|
|
2911
|
+
* **Per-request correction** — the leg-entry mint runs before any request exists, so it can only
|
|
2912
|
+
* describe the model's CAPABILITY. When the leg's FIRST committed request reports wire facts that change
|
|
2913
|
+
* that answer, the seat is re-resolved against them by the same resolver and the trace twin is re-emitted
|
|
2914
|
+
* with it. Today that is one arm: the anthropic budget path's cap-wins skip — a HARD per-request output
|
|
2915
|
+
* cap too small to host a legal thinking budget deletes the thinking block, so a leg capped that way now
|
|
2916
|
+
* reports `effective:"off"`/`dropped:true` instead of claiming the gradient the wire never carried. Only
|
|
2917
|
+
* the FIRST committed request is consumed: the seat stays a leg-ENTRY snapshot (see the degradation law
|
|
2918
|
+
* below), and the leg's nested internal brain calls (compaction summary, tool-invoked side query) carry
|
|
2919
|
+
* their own caps and must never restate the leg's posture.
|
|
2920
|
+
*
|
|
2910
2921
|
* **Degradation law** — same as {@link model}, whose resolution this is: the seat describes the leg's
|
|
2911
2922
|
* PRIMARY serving model at leg entry. A mid-run degradation (reactive fallback / near-budget switch)
|
|
2912
2923
|
* changes the serving model WITHOUT re-minting this seat or its trace twin — read {@link degraded} to see
|
|
@@ -3107,6 +3118,19 @@ export interface TaskResult {
|
|
|
3107
3118
|
/** Human-readable statement of what did NOT happen and why — safe to show a user verbatim. */
|
|
3108
3119
|
message: string;
|
|
3109
3120
|
}>;
|
|
3121
|
+
/**
|
|
3122
|
+
* Present (`true`) exactly when the run's FINAL turn was halted by a person's BARE rejection of a
|
|
3123
|
+
* tool call — the parent-thread control-flow boundary: the rejected call's same-message siblings
|
|
3124
|
+
* that had not started were settled un-executed (each an error result coded `gate.batch_halted`),
|
|
3125
|
+
* and the engine deliberately did NOT re-invoke the model, so the run ends awaiting the user's
|
|
3126
|
+
* direction. On this path `status` is still `"completed"` (nothing failed, nothing is suspended,
|
|
3127
|
+
* the session is continuable as ever) — this field is what tells such an end apart from a natural
|
|
3128
|
+
* finish: `result` text is whatever the model had produced BEFORE the rejection (often empty), and
|
|
3129
|
+
* a consumer surface should read the state as "stopped by the user, awaiting their direction",
|
|
3130
|
+
* never as "the task finished its work". Absent everywhere else — including when queued user input
|
|
3131
|
+
* (a steer/follow-up) continued the run past the rejection and it later ended naturally.
|
|
3132
|
+
*/
|
|
3133
|
+
haltedOnUserRejection?: true;
|
|
3110
3134
|
/**
|
|
3111
3135
|
* The deliveries of AskUserQuestion calls a person ANSWERED but whose call never executed to collect
|
|
3112
3136
|
* the answer (the leg ended first — abort, batch teardown, or a loop failure). Rides EVERY terminal
|
|
@@ -3665,6 +3689,21 @@ export type TaskEvent = ({
|
|
|
3665
3689
|
* must not treat "absent" as "a human decided".
|
|
3666
3690
|
*/
|
|
3667
3691
|
settledBy?: import("./tool-policy.js").ApprovalSettledBy;
|
|
3692
|
+
/**
|
|
3693
|
+
* HOW the ask resolution refused, when this frame closes a call an in-process ask resolution
|
|
3694
|
+
* DENIED — the closed vocabulary of {@link import("./tool-policy.js").AskDenyResolution}
|
|
3695
|
+
* (`"human_refused"`, `"window_expired"`, `"no_approver"`, `"blanket_allow_refused"`,
|
|
3696
|
+
* `"approver_unavailable"`, `"task_aborted"`, `"presentation_failed"`, `"approver_error"`,
|
|
3697
|
+
* `"approver_contract"`). Finer-grained than {@link settledBy} (which only says what KIND of
|
|
3698
|
+
* end a settled wait had): this classifies the REFUSAL ARM itself, minted by the resolver at
|
|
3699
|
+
* the arm that composed the deny and carried verbatim on the engine-owned settlement sideband
|
|
3700
|
+
* — never derived from the result text or writable by a tool/policy (an out-of-vocabulary or
|
|
3701
|
+
* self-declared word is dropped at the screens, with a defect reported on the deployment's
|
|
3702
|
+
* error face). ABSENT on every frame that was not such a deny — every executed call, every
|
|
3703
|
+
* policy/hook direct deny, and the durable/decide lane's settlements (which carry their own
|
|
3704
|
+
* `settledBy`/reason instead). A consumer must not read a semantic out of the absence.
|
|
3705
|
+
*/
|
|
3706
|
+
resolution?: import("./tool-policy.js").AskDenyResolution;
|
|
3668
3707
|
/**
|
|
3669
3708
|
* design/252 G-7 — WHOSE settlement that was: the identifier the approval channel reported for
|
|
3670
3709
|
* the party that ended this wait, beside the {@link settledBy} word that says what KIND of end
|
|
@@ -4732,7 +4771,9 @@ export interface EngineNotice {
|
|
|
4732
4771
|
* child build, not de-duplicated across builds: each spec is a distinct fact.
|
|
4733
4772
|
* - `"mcp.revocation_probe_failed"` (design/338) — the deployment's `mcpRevocations.isRevoked`
|
|
4734
4773
|
* probe threw; MCP dispatch FAILS OPEN (revocation is a tightening face) and this announces
|
|
4735
|
-
* once per
|
|
4774
|
+
* once per materialization (a resume re-materializes and may announce again). `detail: { message }`
|
|
4775
|
+
* — no sessionId (a deployment wiring fact, not session-attributed), `"operator"` audience by
|
|
4776
|
+
* the {@link NOTICE_AUDIENCE} default. The refusal itself (`mcp.server_revoked`) is a tool
|
|
4736
4777
|
* RESULT code, not a notice.
|
|
4737
4778
|
* - `"config.models_swapped"` — `Runner.swapModels` replaced the model catalog generation
|
|
4738
4779
|
* (zero-restart model switching). `detail: { models, tiers }` — key COUNTS only, never the
|
|
@@ -4910,7 +4951,11 @@ export interface RunnerDeps {
|
|
|
4910
4951
|
* `mcp.server_revoked` with known-not-executed wording; in-flight calls a revocation raced are
|
|
4911
4952
|
* deliberately not chased (the threat shape is "new calls after removal"). Absent seat = the
|
|
4912
4953
|
* pre-338 semantics. A THROWING probe fails open (revocation is a tightening face — a broken
|
|
4913
|
-
* probe must not brick every MCP call) with a once-per-
|
|
4954
|
+
* probe must not brick every MCP call) with a once-per-MATERIALIZATION `mcp.revocation_probe_failed`
|
|
4955
|
+
* notice (a resume re-materializes and may announce again — the standing condition is re-news at
|
|
4956
|
+
* each fresh mount, never per-call). The notice is a deployment wiring fact: `detail: { message }`
|
|
4957
|
+
* only, no session attribution, `"operator"` audience by the {@link NOTICE_AUDIENCE} default — a
|
|
4958
|
+
* wire projector forwards it operator-tier and needs no per-session de-duplication of its own.
|
|
4914
4959
|
*/
|
|
4915
4960
|
mcpRevocations?: {
|
|
4916
4961
|
isRevoked(serverName: string): boolean;
|
|
@@ -20,6 +20,13 @@ export interface UserMessageProvenance {
|
|
|
20
20
|
/** [c209-C] R3: engine-injected note whose content must stay VISIBLE in derived views (diagnostics,
|
|
21
21
|
* task notifications, steering, recall) but never under user authority — see UserMessage.provenance. */
|
|
22
22
|
provenance?: "engine-note";
|
|
23
|
+
/** The frame's CONTENT is caller/supervisor speech relayed by the engine (a TaskStream steer), not
|
|
24
|
+
* engine-authored guidance. Rides only WITH `provenance:"engine-note"` (the projection semantics are
|
|
25
|
+
* identical — visible, never `[user]` authority); what it changes is the run-control judgment: the
|
|
26
|
+
* human-halt hold below treats a caller-authored frame as USER-provenance input (always passes),
|
|
27
|
+
* while an engine-authored one is held. Minter-stated at the single relay seam that mints such
|
|
28
|
+
* frames — never inferred from text. Metadata only; not persisted on the message. */
|
|
29
|
+
callerAuthored?: true;
|
|
23
30
|
/** RB-30 terminal fix — an OPAQUE payload the engine rides on this queued message (the runner's
|
|
24
31
|
* task-notification frame). Never serialized, never interpreted by the harness: if the message is
|
|
25
32
|
* still undrained at agent_end, the payload is handed back through `onUndrainedEngineNotes` so
|
|
@@ -98,6 +105,22 @@ export declare class AgentHarness<TSkill extends Skill = Skill, TPromptTemplate
|
|
|
98
105
|
* Advisory (swallow-guarded at the call sites via the runner's own closure); never re-entered
|
|
99
106
|
* for the same payload — consumption deletes the sidecar entry in the same step. */
|
|
100
107
|
onEngineNoteConsumed?: (payload: unknown) => void;
|
|
108
|
+
/** Runner-set HOLD — the single gate for every engine-authored injection against the run's live
|
|
109
|
+
* human-halt fact. When it reports true (a person's bare rejection halted the run at this
|
|
110
|
+
* boundary, and only that person's own input may continue it):
|
|
111
|
+
* · a NEW engine-authored steer/followUp is refused at entry (the caller's existing rejection
|
|
112
|
+
* path parks the payload for the session's next run — same lane an idle-race refusal takes);
|
|
113
|
+
* · engine-authored frames ALREADY queued are not drained — they stay queued, delayed past the
|
|
114
|
+
* halted boundary: delivered at the next un-halted boundary if the user's own queued input
|
|
115
|
+
* legitimately continues the run, otherwise handed to the terminal sweep (payload frames park
|
|
116
|
+
* losslessly; payload-less boundary advisories end with the run, the same best-effort fate
|
|
117
|
+
* their mint sites already accept for a final turn).
|
|
118
|
+
* Caller/user-provenance input always passes — it is the awaited direction. One predicate here
|
|
119
|
+
* instead of a per-lane check at every injection site: a future engine lane that never heard of
|
|
120
|
+
* the halt is held by construction rather than by remembering. `nextTurn` is deliberately NOT
|
|
121
|
+
* gated: its splice opens a NEW run (idle-park redelivery), and a halt belongs to the run that
|
|
122
|
+
* minted it. */
|
|
123
|
+
engineInjectionsHeld?: () => boolean;
|
|
101
124
|
/** RB-30 codex F1/F2: shared recovery sweep — collects engine-note payloads from the given queues
|
|
102
125
|
* in DELIVERY order (steer before followUp, each queue forward — the live loop serves steering
|
|
103
126
|
* first, so the recovered redelivery must not present "later" frames ahead of "now/next"),
|
|
@@ -203,6 +226,13 @@ export declare class AgentHarness<TSkill extends Skill = Skill, TPromptTemplate
|
|
|
203
226
|
* The idle check is deliberately NOT here: its message differs per lane, and nextTurn() legitimately has
|
|
204
227
|
* none (queueing the NEXT prompt while idle is exactly what it is for). */
|
|
205
228
|
private enqueueInjection;
|
|
229
|
+
/** The human-halt hold's ENTRY half (see {@link engineInjectionsHeld}): a new engine-authored
|
|
230
|
+
* injection during a halt is refused before it can queue — the refusal is the caller's park
|
|
231
|
+
* signal (the same posture as the backlog-cap refusal above: pend per session, redeliver on the
|
|
232
|
+
* session's next run; nothing is lost). Belt-and-braces beside the drain-side hold: today's
|
|
233
|
+
* engine lanes each check the halt fact before injecting, so this arm exists for the lane that
|
|
234
|
+
* does not — including the one not written yet. */
|
|
235
|
+
private refuseHeldEngineInjection;
|
|
206
236
|
steer(text: string, options?: {
|
|
207
237
|
images?: ImageContent[];
|
|
208
238
|
} & UserMessageProvenance): Promise<void>;
|
|
@@ -34,6 +34,14 @@ function createUserMessage(text, images, provenance) {
|
|
|
34
34
|
};
|
|
35
35
|
}
|
|
36
36
|
const engineNotePayloads = new WeakMap();
|
|
37
|
+
const engineAuthoredInjections = new WeakSet();
|
|
38
|
+
function isEngineAuthoredInjection(options) {
|
|
39
|
+
if (options === undefined)
|
|
40
|
+
return false;
|
|
41
|
+
if (options.engineMinted === true || options.enginePayload !== undefined)
|
|
42
|
+
return true;
|
|
43
|
+
return options.provenance === "engine-note" && options.callerAuthored !== true;
|
|
44
|
+
}
|
|
37
45
|
const ENGINE_NOTE_STEER_BACKLOG_CAP = 50;
|
|
38
46
|
function createFailureMessage(model, error, aborted) {
|
|
39
47
|
return {
|
|
@@ -160,6 +168,7 @@ export class AgentHarness {
|
|
|
160
168
|
onUndrainedEngineNotes;
|
|
161
169
|
onUndrainedUserInputs;
|
|
162
170
|
onEngineNoteConsumed;
|
|
171
|
+
engineInjectionsHeld;
|
|
163
172
|
recoverUndrainedEngineNotes() {
|
|
164
173
|
this.sweepUndrainedEngineNotes([this.nextTurnQueue, this.steerQueue, this.followUpQueue]);
|
|
165
174
|
}
|
|
@@ -184,7 +193,10 @@ export class AgentHarness {
|
|
|
184
193
|
}
|
|
185
194
|
}
|
|
186
195
|
announceUndrainedUserInputs() {
|
|
187
|
-
const counts = {
|
|
196
|
+
const counts = {
|
|
197
|
+
steer: this.steerQueue.filter((m) => !engineAuthoredInjections.has(m)).length,
|
|
198
|
+
followUp: this.followUpQueue.filter((m) => !engineAuthoredInjections.has(m)).length,
|
|
199
|
+
};
|
|
188
200
|
if ((counts.steer > 0 || counts.followUp > 0) && this.onUndrainedUserInputs) {
|
|
189
201
|
try {
|
|
190
202
|
this.onUndrainedUserInputs(counts);
|
|
@@ -439,13 +451,26 @@ export class AgentHarness {
|
|
|
439
451
|
};
|
|
440
452
|
}
|
|
441
453
|
async drainQueuedMessages(queue, mode) {
|
|
442
|
-
let
|
|
443
|
-
if (
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
454
|
+
let messages;
|
|
455
|
+
if (this.engineInjectionsHeld?.() === true) {
|
|
456
|
+
messages = [];
|
|
457
|
+
const limit = mode === "all" ? Number.POSITIVE_INFINITY : 1;
|
|
458
|
+
for (let i = 0; i < queue.length && messages.length < limit; i++) {
|
|
459
|
+
if (engineAuthoredInjections.has(queue[i]))
|
|
460
|
+
continue;
|
|
461
|
+
messages.push(...queue.splice(i, 1));
|
|
462
|
+
i--;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
else {
|
|
466
|
+
let count = mode === "all" ? queue.length : 1;
|
|
467
|
+
if (mode !== "all" && queue.length > 1 && engineNotePayloads.has(queue[0])) {
|
|
468
|
+
count = 1;
|
|
469
|
+
while (count < queue.length && engineNotePayloads.has(queue[count]))
|
|
470
|
+
count++;
|
|
471
|
+
}
|
|
472
|
+
messages = queue.splice(0, count);
|
|
447
473
|
}
|
|
448
|
-
const messages = queue.splice(0, count);
|
|
449
474
|
if (messages.length === 0) {
|
|
450
475
|
return messages;
|
|
451
476
|
}
|
|
@@ -759,19 +784,28 @@ export class AgentHarness {
|
|
|
759
784
|
const m = createUserMessage(text, options?.images, options);
|
|
760
785
|
if (options?.enginePayload !== undefined)
|
|
761
786
|
engineNotePayloads.set(m, options.enginePayload);
|
|
787
|
+
if (isEngineAuthoredInjection(options))
|
|
788
|
+
engineAuthoredInjections.add(m);
|
|
762
789
|
queue.push(m);
|
|
763
790
|
await this.emitQueueUpdate();
|
|
764
791
|
}
|
|
792
|
+
refuseHeldEngineInjection(options) {
|
|
793
|
+
if (isEngineAuthoredInjection(options) && this.engineInjectionsHeld?.() === true) {
|
|
794
|
+
throw new AgentHarnessError("invalid_state", "engine-authored injection refused: the run is halted awaiting the user's own direction — park the payload for the session's next run");
|
|
795
|
+
}
|
|
796
|
+
}
|
|
765
797
|
async steer(text, options) {
|
|
766
798
|
if (this.phase === "idle") {
|
|
767
799
|
throw new AgentHarnessError("invalid_state", "Cannot steer while idle");
|
|
768
800
|
}
|
|
801
|
+
this.refuseHeldEngineInjection(options);
|
|
769
802
|
await this.enqueueInjection(this.steerQueue, text, options);
|
|
770
803
|
}
|
|
771
804
|
async followUp(text, options) {
|
|
772
805
|
if (this.phase === "idle") {
|
|
773
806
|
throw new AgentHarnessError("invalid_state", "Cannot follow up while idle");
|
|
774
807
|
}
|
|
808
|
+
this.refuseHeldEngineInjection(options);
|
|
775
809
|
await this.enqueueInjection(this.followUpQueue, text, options);
|
|
776
810
|
}
|
|
777
811
|
async nextTurn(text, options) {
|
|
@@ -490,12 +490,14 @@ async function executeToolCallsSequential(currentContext, assistantMessage, tool
|
|
|
490
490
|
});
|
|
491
491
|
const preparation = await prepareToolCall(currentContext, assistantMessage, toolCall, config, signal);
|
|
492
492
|
let finalized;
|
|
493
|
+
let halt;
|
|
493
494
|
if (preparation.kind === "immediate") {
|
|
494
495
|
finalized = {
|
|
495
496
|
toolCall,
|
|
496
497
|
result: preparation.result,
|
|
497
498
|
isError: preparation.isError,
|
|
498
499
|
};
|
|
500
|
+
halt = preparation.halt;
|
|
499
501
|
}
|
|
500
502
|
else {
|
|
501
503
|
finalized = await executePreparedWithDisclosure(currentContext, assistantMessage, preparation, config, signal, emit);
|
|
@@ -505,6 +507,16 @@ async function executeToolCallsSequential(currentContext, assistantMessage, tool
|
|
|
505
507
|
await emitToolResultMessage(toolResultMessage, emit);
|
|
506
508
|
finalizedCalls.push(finalized);
|
|
507
509
|
messages.push(toolResultMessage);
|
|
510
|
+
if (halt !== undefined) {
|
|
511
|
+
for (const remaining of toolCalls.slice(i + 1)) {
|
|
512
|
+
if (signal?.aborted)
|
|
513
|
+
break;
|
|
514
|
+
const settled = await settleHaltedToolCall(remaining, halt, emit);
|
|
515
|
+
finalizedCalls.push(settled.finalized);
|
|
516
|
+
messages.push(settled.message);
|
|
517
|
+
}
|
|
518
|
+
return { messages, terminate: true };
|
|
519
|
+
}
|
|
508
520
|
if (signal?.aborted) {
|
|
509
521
|
break;
|
|
510
522
|
}
|
|
@@ -514,6 +526,33 @@ async function executeToolCallsSequential(currentContext, assistantMessage, tool
|
|
|
514
526
|
terminate: shouldTerminateToolBatch(finalizedCalls),
|
|
515
527
|
};
|
|
516
528
|
}
|
|
529
|
+
async function settleHaltedToolCall(toolCall, halt, emit) {
|
|
530
|
+
await emit({
|
|
531
|
+
type: "tool_execution_start",
|
|
532
|
+
toolCallId: toolCall.id,
|
|
533
|
+
toolName: toolCall.name,
|
|
534
|
+
args: toolCall.arguments,
|
|
535
|
+
});
|
|
536
|
+
const finalized = {
|
|
537
|
+
toolCall,
|
|
538
|
+
result: {
|
|
539
|
+
content: [{ type: "text", text: truncateError(halt.reason) }],
|
|
540
|
+
details: halt.details !== undefined ? { ...halt.details } : {},
|
|
541
|
+
},
|
|
542
|
+
isError: true,
|
|
543
|
+
};
|
|
544
|
+
await emit({
|
|
545
|
+
type: "tool_execution_end",
|
|
546
|
+
toolCallId: finalized.toolCall.id,
|
|
547
|
+
toolName: finalized.toolCall.name,
|
|
548
|
+
result: finalized.result,
|
|
549
|
+
isError: true,
|
|
550
|
+
notExecuted: true,
|
|
551
|
+
});
|
|
552
|
+
const message = createToolResultMessage(finalized);
|
|
553
|
+
await emitToolResultMessage(message, emit);
|
|
554
|
+
return { finalized, message };
|
|
555
|
+
}
|
|
517
556
|
function isCallConcurrencySafe(tool, args) {
|
|
518
557
|
if (!tool)
|
|
519
558
|
return false;
|
|
@@ -587,6 +626,7 @@ async function executeToolCallsPartitioned(currentContext, assistantMessage, too
|
|
|
587
626
|
const allFinalized = [];
|
|
588
627
|
const messages = [];
|
|
589
628
|
const remainingCalls = [];
|
|
629
|
+
let halt;
|
|
590
630
|
for (const toolCall of toolCalls) {
|
|
591
631
|
const held = executor?.take(toolCall.id);
|
|
592
632
|
if (!held) {
|
|
@@ -599,6 +639,8 @@ async function executeToolCallsPartitioned(currentContext, assistantMessage, too
|
|
|
599
639
|
await emitToolResultMessage(toolResultMessage, emit);
|
|
600
640
|
allFinalized.push(finalized);
|
|
601
641
|
messages.push(toolResultMessage);
|
|
642
|
+
if (held.immediate?.halt !== undefined)
|
|
643
|
+
halt ??= held.immediate.halt;
|
|
602
644
|
}
|
|
603
645
|
if (executor) {
|
|
604
646
|
for (const orphan of executor.remaining()) {
|
|
@@ -606,33 +648,54 @@ async function executeToolCallsPartitioned(currentContext, assistantMessage, too
|
|
|
606
648
|
await closeOrphanedStreamEntry(orphan, emit);
|
|
607
649
|
}
|
|
608
650
|
}
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
651
|
+
if (halt === undefined) {
|
|
652
|
+
const batches = partitionToolCalls(remainingCalls, currentContext.tools);
|
|
653
|
+
outer: for (const batch of batches) {
|
|
654
|
+
if (signal?.aborted)
|
|
655
|
+
break;
|
|
656
|
+
const entries = [];
|
|
657
|
+
for (const toolCall of batch.calls) {
|
|
658
|
+
await emit({
|
|
659
|
+
type: "tool_execution_start",
|
|
660
|
+
toolCallId: toolCall.id,
|
|
661
|
+
toolName: toolCall.name,
|
|
662
|
+
args: toolCall.arguments,
|
|
663
|
+
});
|
|
664
|
+
const preparation = await prepareToolCall(currentContext, assistantMessage, toolCall, config, signal);
|
|
665
|
+
if (preparation.kind === "immediate") {
|
|
666
|
+
const finalized = { toolCall, result: preparation.result, isError: preparation.isError };
|
|
667
|
+
await emitToolExecutionEnd(finalized, emit);
|
|
668
|
+
entries.push({ finalized, toolCall });
|
|
669
|
+
if (preparation.halt !== undefined) {
|
|
670
|
+
halt = preparation.halt;
|
|
671
|
+
break;
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
else {
|
|
675
|
+
entries.push({ prepared: preparation, toolCall });
|
|
676
|
+
}
|
|
677
|
+
if (signal?.aborted) {
|
|
678
|
+
await settleBatch(entries, true);
|
|
679
|
+
break outer;
|
|
680
|
+
}
|
|
633
681
|
}
|
|
682
|
+
await settleBatch(entries, batch.safe);
|
|
683
|
+
if (halt !== undefined)
|
|
684
|
+
break;
|
|
634
685
|
}
|
|
635
|
-
|
|
686
|
+
}
|
|
687
|
+
if (halt !== undefined && !signal?.aborted) {
|
|
688
|
+
const settledIds = new Set(messages.map((m) => m.toolCallId));
|
|
689
|
+
for (const toolCall of toolCalls) {
|
|
690
|
+
if (settledIds.has(toolCall.id))
|
|
691
|
+
continue;
|
|
692
|
+
if (signal?.aborted)
|
|
693
|
+
break;
|
|
694
|
+
const settled = await settleHaltedToolCall(toolCall, halt, emit);
|
|
695
|
+
allFinalized.push(settled.finalized);
|
|
696
|
+
messages.push(settled.message);
|
|
697
|
+
}
|
|
698
|
+
return { messages, terminate: true };
|
|
636
699
|
}
|
|
637
700
|
return { messages, terminate: shouldTerminateToolBatch(allFinalized) };
|
|
638
701
|
async function settleBatch(entries, concurrent) {
|
|
@@ -720,6 +783,8 @@ class StreamToolExecutor {
|
|
|
720
783
|
const preparation = await prepareToolCall(this.context, partialAssistant, entry.toolCall, this.config, this.signal);
|
|
721
784
|
if (preparation.kind === "immediate") {
|
|
722
785
|
entry.immediate = preparation;
|
|
786
|
+
if (preparation.halt !== undefined)
|
|
787
|
+
this.barrier = true;
|
|
723
788
|
return;
|
|
724
789
|
}
|
|
725
790
|
entry.prepared = preparation;
|
|
@@ -873,11 +938,11 @@ async function prepareToolCall(currentContext, assistantMessage, toolCall, confi
|
|
|
873
938
|
};
|
|
874
939
|
}
|
|
875
940
|
if (beforeResult?.block) {
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
result:
|
|
879
|
-
|
|
880
|
-
};
|
|
941
|
+
const blocked = createErrorToolResult(beforeResult.reason || "Tool execution was blocked");
|
|
942
|
+
if (beforeResult.haltRemaining !== undefined) {
|
|
943
|
+
return { kind: "immediate", result: blocked, isError: true, halt: beforeResult.haltRemaining };
|
|
944
|
+
}
|
|
945
|
+
return { kind: "immediate", result: blocked, isError: true };
|
|
881
946
|
}
|
|
882
947
|
if (beforeResult?.updatedInput !== undefined) {
|
|
883
948
|
finalArgs = validateToolArguments(tool, {
|
|
@@ -48,11 +48,34 @@ export type AgentToolCall = Extract<AssistantMessage["content"][number], {
|
|
|
48
48
|
*
|
|
49
49
|
* Keep in sync with the harness hook result `ToolCallResult` (harness/types.ts), which the harness's
|
|
50
50
|
* `beforeToolCall` callback returns verbatim.
|
|
51
|
+
*
|
|
52
|
+
* `haltRemaining` (additive) makes the block a CONTROL-FLOW BOUNDARY for the whole assistant batch:
|
|
53
|
+
* honored only beside `block: true`, it tells the loop that every tool call of the same assistant
|
|
54
|
+
* message whose preparation had not yet begun must NOT begin — each settles as an error tool result
|
|
55
|
+
* built from the directive (so the transcript still answers every tool call), and the batch votes
|
|
56
|
+
* terminate (the loop starts no further assistant turn; queued follow-up/steering messages still
|
|
57
|
+
* run — they are the input the boundary is waiting for). Calls already executing when the block
|
|
58
|
+
* lands run to completion and settle with their real outcomes; preparation/adjudication is strictly
|
|
59
|
+
* source-ordered in every lane, so "not yet begun" is exactly "after the blocked call". The loop
|
|
60
|
+
* stays policy-agnostic: WHY a block halts the batch (e.g. a person's bare rejection) is entirely
|
|
61
|
+
* the gate owner's judgment, made once, at the site that returns this.
|
|
51
62
|
*/
|
|
52
63
|
export interface BeforeToolCallResult {
|
|
53
64
|
block?: boolean;
|
|
54
65
|
reason?: string;
|
|
55
66
|
updatedInput?: unknown;
|
|
67
|
+
haltRemaining?: HaltRemainingDirective;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* The settlement {@link BeforeToolCallResult.haltRemaining} instructs the loop to apply to each
|
|
71
|
+
* same-batch tool call that never started: `reason` is the model-facing error text (bounded by the
|
|
72
|
+
* loop's standard error truncator), `details` is attached to each settled result verbatim (shallow-
|
|
73
|
+
* copied per call) so a machine-readable mark (e.g. `{ code }`) reaches every not-executed sibling's
|
|
74
|
+
* end frame and transcript entry.
|
|
75
|
+
*/
|
|
76
|
+
export interface HaltRemainingDirective {
|
|
77
|
+
reason: string;
|
|
78
|
+
details?: Record<string, unknown>;
|
|
56
79
|
}
|
|
57
80
|
/**
|
|
58
81
|
* Partial override returned from `afterToolCall`.
|
|
@@ -629,4 +652,13 @@ export type AgentEvent = {
|
|
|
629
652
|
toolName: string;
|
|
630
653
|
result: unknown;
|
|
631
654
|
isError: boolean;
|
|
655
|
+
/**
|
|
656
|
+
* Present (`true`) exactly when this end frame closes a call the loop settled WITHOUT
|
|
657
|
+
* executing it — a never-started sibling under a batch-halt directive (see
|
|
658
|
+
* {@link BeforeToolCallResult.haltRemaining}). LOOP-AUTHORED: it rides the event, never the
|
|
659
|
+
* tool result, so a tool cannot claim it about itself; a consumer that scopes
|
|
660
|
+
* post-execution machinery to executed calls (batch observers, execution audits) keys on
|
|
661
|
+
* this the way it keys on its own blocked-call records. Absent on every executed call.
|
|
662
|
+
*/
|
|
663
|
+
notExecuted?: true;
|
|
632
664
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -136,7 +136,7 @@ export type { SchedulerCapability, SchedulerErrorCode, ScheduledIntent, Schedule
|
|
|
136
136
|
export { createSchedulerTools, type SchedulerToolContext, SCHEDULE_WAKEUP_TOOL_NAME, AUTONOMOUS_LOOP_SENTINEL, AUTONOMOUS_LOOP_DYNAMIC_SENTINEL, } from "./tools/scheduler-tools.js";
|
|
137
137
|
export { resolveAutonomousLoopPrompt, AUTONOMOUS_LOOP_PREAMBLE, AUTONOMOUS_LOOP_PREAMBLE_PERSISTENT, type AutonomousLoopPromptOptions, } from "./tools/loop-tick.js";
|
|
138
138
|
export { tightenTaskSpec, TaskSpecTightenError } from "./core/tighten-task-spec.js";
|
|
139
|
-
export { createAllowDenyPolicy, createApprovalPolicy, COARSE_SHELL_TOOLS, createCoarseCommandNamePolicy, createTranscriptIntegrityPolicy, createUnverifiableDeletePolicy, findUnverifiableRecursiveDelete, combinePolicies, decisionText, resolveAsk, toolPolicyNameSets, type ToolPolicyNameSets, type NamedToolPolicy, type ToolPolicyProjection, type ToolPolicyProjectionComponent, type ConstraintChainEntry, checkToolPolicyProjection, constraintChainEntryOf, constraintChainDigest, type ToolPolicy, type ToolCallRequest, type PermissionResult, type DecisionReason, type ApprovalSettledBy, APPROVAL_SETTLED_BY_VALUES, isApprovalSettledBy, screenApproverAttribution, APPROVER_ATTRIBUTION_MAX_CHARS, type OnAsk, type AskOutcome, type ResolvedAsk, type AskRequest, type AskDelegationProvenance, type AskRuleEvidence, type AskEvidenceAbsence, ASK_EVIDENCE_ABSENCE_VALUES, } from "./core/tool-policy.js";
|
|
139
|
+
export { createAllowDenyPolicy, createApprovalPolicy, COARSE_SHELL_TOOLS, createCoarseCommandNamePolicy, createTranscriptIntegrityPolicy, createUnverifiableDeletePolicy, findUnverifiableRecursiveDelete, combinePolicies, decisionText, resolveAsk, toolPolicyNameSets, type ToolPolicyNameSets, type NamedToolPolicy, type ToolPolicyProjection, type ToolPolicyProjectionComponent, type ConstraintChainEntry, checkToolPolicyProjection, constraintChainEntryOf, constraintChainDigest, type ToolPolicy, type ToolCallRequest, type PermissionResult, type DecisionReason, type ApprovalSettledBy, APPROVAL_SETTLED_BY_VALUES, isApprovalSettledBy, type AskDenyResolution, ASK_DENY_RESOLUTION_VALUES, isAskDenyResolution, screenApproverAttribution, APPROVER_ATTRIBUTION_MAX_CHARS, type OnAsk, type AskOutcome, type ResolvedAsk, type AskRequest, type AskDelegationProvenance, type AskRuleEvidence, type AskEvidenceAbsence, ASK_EVIDENCE_ABSENCE_VALUES, } from "./core/tool-policy.js";
|
|
140
140
|
export { parseAutoModeResponse, createAutoModeDecider, type AutoModeVerdict, type AutoModeDecider, type AutoModeDeciderOptions, type AutoModeClassifyFn, type AutoModeClassifyInput, } from "./core/auto-mode.js";
|
|
141
141
|
export { buildAutoModePrompt, renderAutoModeWindow, renderAutoModeAction, AUTO_MODE_DEFAULTS_SENTINEL, type AutoModeRules, type BuildAutoModePromptOptions, type AutoModeWindowOptions, } from "./core/auto-mode-prompt.js";
|
|
142
142
|
export { AUTO_MODE_BASE_PROMPT, AUTO_MODE_PERMISSIONS_EXTERNAL } from "./core/auto-mode-prompt-assets.js";
|
package/dist/index.js
CHANGED
|
@@ -113,7 +113,7 @@ export { hasScheduler, isValidCronExpr, SchedulerError } from "./core/scheduler.
|
|
|
113
113
|
export { createSchedulerTools, SCHEDULE_WAKEUP_TOOL_NAME, AUTONOMOUS_LOOP_SENTINEL, AUTONOMOUS_LOOP_DYNAMIC_SENTINEL, } from "./tools/scheduler-tools.js";
|
|
114
114
|
export { resolveAutonomousLoopPrompt, AUTONOMOUS_LOOP_PREAMBLE, AUTONOMOUS_LOOP_PREAMBLE_PERSISTENT, } from "./tools/loop-tick.js";
|
|
115
115
|
export { tightenTaskSpec, TaskSpecTightenError } from "./core/tighten-task-spec.js";
|
|
116
|
-
export { createAllowDenyPolicy, createApprovalPolicy, COARSE_SHELL_TOOLS, createCoarseCommandNamePolicy, createTranscriptIntegrityPolicy, createUnverifiableDeletePolicy, findUnverifiableRecursiveDelete, combinePolicies, decisionText, resolveAsk, toolPolicyNameSets, checkToolPolicyProjection, constraintChainEntryOf, constraintChainDigest, APPROVAL_SETTLED_BY_VALUES, isApprovalSettledBy, screenApproverAttribution, APPROVER_ATTRIBUTION_MAX_CHARS, ASK_EVIDENCE_ABSENCE_VALUES, } from "./core/tool-policy.js";
|
|
116
|
+
export { createAllowDenyPolicy, createApprovalPolicy, COARSE_SHELL_TOOLS, createCoarseCommandNamePolicy, createTranscriptIntegrityPolicy, createUnverifiableDeletePolicy, findUnverifiableRecursiveDelete, combinePolicies, decisionText, resolveAsk, toolPolicyNameSets, checkToolPolicyProjection, constraintChainEntryOf, constraintChainDigest, APPROVAL_SETTLED_BY_VALUES, isApprovalSettledBy, ASK_DENY_RESOLUTION_VALUES, isAskDenyResolution, screenApproverAttribution, APPROVER_ATTRIBUTION_MAX_CHARS, ASK_EVIDENCE_ABSENCE_VALUES, } from "./core/tool-policy.js";
|
|
117
117
|
export { parseAutoModeResponse, createAutoModeDecider, } from "./core/auto-mode.js";
|
|
118
118
|
export { buildAutoModePrompt, renderAutoModeWindow, renderAutoModeAction, AUTO_MODE_DEFAULTS_SENTINEL, } from "./core/auto-mode-prompt.js";
|
|
119
119
|
export { AUTO_MODE_BASE_PROMPT, AUTO_MODE_PERMISSIONS_EXTERNAL } from "./core/auto-mode-prompt-assets.js";
|
package/dist/tools/web.d.ts
CHANGED
|
@@ -78,7 +78,16 @@ export interface WebFetchGrounding {
|
|
|
78
78
|
export declare function htmlToText(html: string): string;
|
|
79
79
|
export declare function webFetchToolSpec(config?: WebFetchConfig): ToolSpec;
|
|
80
80
|
/** Defined (harness) form of web_fetch — back-compat for direct execution / tests. For `spec.tools`, use the raw
|
|
81
|
-
* {@link webFetchToolSpec} (prepare-task defineTool-wraps spec.tools entries).
|
|
81
|
+
* {@link webFetchToolSpec} (prepare-task defineTool-wraps spec.tools entries).
|
|
82
|
+
*
|
|
83
|
+
* ⚠️ CTX-DEPENDENT BEHAVIOR: this factory takes no `enrichCtx`, so the product it returns is sealed
|
|
84
|
+
* around the pre-RB-409 minimal `{toolCallId, signal}` ctx. Fed into `TaskSpec.tools`, a finished
|
|
85
|
+
* product is mounted AS-IS (prepare-task's brand branch — there is no ctx seat on
|
|
86
|
+
* `AgentTool.execute` to inject through), so every `ctx.*` read inside {@link webFetchToolSpec}
|
|
87
|
+
* reads absent — including `reminderMark`/`reminderDisclosureCounts`, i.e. the design/319 exact-mark
|
|
88
|
+
* DEFUSE and its observation counters silently do not run for that mount. Two ways to keep them:
|
|
89
|
+
* hand `spec.tools` the raw {@link webFetchToolSpec} (the Runner then wraps it with its own trusted
|
|
90
|
+
* ctx builder), or call `defineTool(webFetchToolSpec(config), { enrichCtx })` yourself. */
|
|
82
91
|
export declare function createWebFetchTool(config?: WebFetchConfig): AgentTool;
|
|
83
92
|
/** {@link createWebFetchSummarizer}'s truncation bound — CC's `MAX_MARKDOWN_LENGTH`
|
|
84
93
|
* (WebFetchTool/utils.ts:128). Exported so a deployment wiring the reference summarizer doesn't need
|
package/dist/tools/web.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Type } from "typebox";
|
|
2
2
|
import { defineTool, errorResult } from "../core/tools.js";
|
|
3
3
|
import { delimitUntrusted, inlineUntrusted } from "../core/untrusted-text.js";
|
|
4
|
-
import { discloseReminderShaped } from "../core/reminder-disclosure.js";
|
|
4
|
+
import { discloseReminderShaped, observeReminderMarkEcho } from "../core/reminder-disclosure.js";
|
|
5
5
|
import { redactSecrets } from "../core/untrusted-egress.js";
|
|
6
6
|
import { isPrivateHost } from "../core/runner/image.js";
|
|
7
7
|
import { binaryMagicFormat } from "./fs/safety.js";
|
|
@@ -493,6 +493,7 @@ export function webFetchToolSpec(config = {}) {
|
|
|
493
493
|
const content = (excerpt
|
|
494
494
|
? `${headline}\nThe error response body${trimmed.length > excerpt.length ? ` (first ${ERROR_BODY_EXCERPT_CHARS} chars)` : ""} follows:\n\n${delimitUntrusted(`WebFetch ${parsed.hostname}`, excerpt)}`
|
|
495
495
|
: headline) + bodyStateNote + promptNote;
|
|
496
|
+
observeReminderMarkEcho({ text: content, mark: ctx.reminderMark, outlet: "webFetch", counts: ctx.reminderDisclosureCounts });
|
|
496
497
|
return {
|
|
497
498
|
content,
|
|
498
499
|
details: {
|
|
@@ -908,9 +909,9 @@ export function createWebSearchTool(config) {
|
|
|
908
909
|
const msg = redactSecrets(e instanceof Error ? e.message : String(e)).trim();
|
|
909
910
|
const headline = "Error (WebSearch): the search backend failed.";
|
|
910
911
|
const verdict = classifySearchFailure(msg);
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
912
|
+
const composed = (msg ? `${headline} The backend's error text follows:\n\n${delimitUntrusted("WebSearch backend error", msg, SEARCH_ERROR_EXCERPT_CHARS)}` : headline) + `\n\n${verdict.hint}`;
|
|
913
|
+
observeReminderMarkEcho({ text: composed, mark: ctx.reminderMark, outlet: "webSearch", counts: ctx.reminderDisclosureCounts });
|
|
914
|
+
return errorResult(composed, failCard({ retryable: verdict.retryable }));
|
|
914
915
|
}
|
|
915
916
|
finally {
|
|
916
917
|
clearTimeout(timer);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"_comment": "design/87 L3 — frozen public export surface of src/index.ts (name -> kind). DO NOT edit by hand to silence a red test. A removed/changed entry = a SemVer-BREAKING change; bump MAJOR and update this fixture in the SAME commit (design/87 §4.2 / §5.2). Regenerate via REGEN in test/export-surface.test.ts.",
|
|
3
|
-
"count":
|
|
3
|
+
"count": 1649,
|
|
4
4
|
"exports": {
|
|
5
5
|
"A2ATaskState": "type",
|
|
6
6
|
"A2ATaskStateReversal": "type",
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"APPROVAL_SETTLED_BY_VALUES": "variable",
|
|
22
22
|
"APPROVER_ATTRIBUTION_MAX_CHARS": "variable",
|
|
23
23
|
"ARTIFACT_LIMITS": "variable",
|
|
24
|
+
"ASK_DENY_RESOLUTION_VALUES": "variable",
|
|
24
25
|
"ASK_EVIDENCE_ABSENCE_VALUES": "variable",
|
|
25
26
|
"AUTONOMOUS_LOOP_DYNAMIC_SENTINEL": "variable",
|
|
26
27
|
"AUTONOMOUS_LOOP_PREAMBLE": "variable",
|
|
@@ -59,6 +60,7 @@
|
|
|
59
60
|
"ArtifactVerifyResult": "type",
|
|
60
61
|
"AskAnswerContinuationSource": "type",
|
|
61
62
|
"AskDelegationProvenance": "interface",
|
|
63
|
+
"AskDenyResolution": "type",
|
|
62
64
|
"AskEffective": "type",
|
|
63
65
|
"AskEvidenceAbsence": "type",
|
|
64
66
|
"AskOutcome": "type",
|
|
@@ -1371,6 +1373,7 @@
|
|
|
1371
1373
|
"inlineUntrusted": "function",
|
|
1372
1374
|
"inspectDegenerate": "function",
|
|
1373
1375
|
"isApprovalSettledBy": "function",
|
|
1376
|
+
"isAskDenyResolution": "function",
|
|
1374
1377
|
"isDelegatedAgentTerminal": "function",
|
|
1375
1378
|
"isInstructionEntry": "function",
|
|
1376
1379
|
"isIsolated": "function",
|