@sema-agent/core 7.17.7 → 7.18.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 +140 -13
- package/dist/agents/send-message-tool.js +1 -0
- package/dist/agents/subagent.d.ts +1 -0
- package/dist/agents/subagent.js +5 -0
- package/dist/agents/teacher.js +1 -0
- package/dist/agents/verify.js +3 -0
- package/dist/brain/route-adjudicator.d.ts +18 -0
- package/dist/brain/route-adjudicator.js +16 -0
- package/dist/core/agent-definition.d.ts +10 -0
- package/dist/core/artifact-host.d.ts +41 -4
- package/dist/core/artifact-host.js +18 -2
- package/dist/core/auto-mode.d.ts +141 -45
- package/dist/core/auto-mode.js +109 -3
- package/dist/core/checkpoint-store.d.ts +16 -2
- package/dist/core/engine-notice.d.ts +47 -1
- package/dist/core/engine-notice.js +7 -0
- package/dist/core/gate-lanes.js +2 -1
- package/dist/core/gate-outcome.d.ts +17 -2
- package/dist/core/gate-outcome.js +16 -0
- package/dist/core/governance-codes.d.ts +1 -1
- package/dist/core/governance-codes.js +8 -0
- package/dist/core/hook-seats.d.ts +66 -0
- package/dist/core/hook-seats.js +37 -0
- package/dist/core/runner/advertised-writable-dirs.d.ts +8 -3
- package/dist/core/runner/assemble-result.d.ts +2 -1
- package/dist/core/runner/assemble-result.js +1 -1
- package/dist/core/runner/classifier-ladder.d.ts +89 -0
- package/dist/core/runner/classifier-ladder.js +65 -0
- package/dist/core/runner/contracts.d.ts +69 -13
- package/dist/core/runner/decide-continuation.d.ts +1 -3
- package/dist/core/runner/decide-continuation.js +1 -7
- package/dist/core/runner/deferral-census.d.ts +52 -0
- package/dist/core/runner/deferral-census.js +44 -0
- package/dist/core/runner/derived-route-fallback.d.ts +8 -4
- package/dist/core/runner/derived-route-fallback.js +8 -3
- package/dist/core/runner/gate-exit.d.ts +12 -0
- package/dist/core/runner/gate-exit.js +6 -3
- package/dist/core/runner/prepare-artifact.d.ts +10 -2
- package/dist/core/runner/prepare-artifact.js +5 -4
- package/dist/core/runner/prepare-caps-and-workflow.d.ts +4 -2
- package/dist/core/runner/prepare-caps-and-workflow.js +30 -31
- package/dist/core/runner/prepare-config-doors.d.ts +1 -1
- package/dist/core/runner/prepare-config-doors.js +5 -3
- package/dist/core/runner/prepare-defer-classify.d.ts +13 -19
- package/dist/core/runner/prepare-defer-classify.js +8 -3
- package/dist/core/runner/prepare-gate-stations.d.ts +26 -0
- package/dist/core/runner/prepare-gate-stations.js +1 -1
- package/dist/core/runner/prepare-hands-readface.d.ts +24 -7
- package/dist/core/runner/prepare-hands-readface.js +60 -24
- package/dist/core/runner/prepare-inherited-gate.d.ts +17 -4
- package/dist/core/runner/prepare-inherited-gate.js +10 -2
- package/dist/core/runner/prepare-lsp.d.ts +2 -2
- package/dist/core/runner/prepare-lsp.js +1 -1
- package/dist/core/runner/prepare-policy-chain.js +2 -0
- package/dist/core/runner/prepare-project-context.js +4 -2
- package/dist/core/runner/prepare-prompt-inputs.d.ts +4 -2
- package/dist/core/runner/prepare-prompt-inputs.js +4 -1
- package/dist/core/runner/prepare-run-refs.d.ts +9 -1
- package/dist/core/runner/prepare-run-refs.js +3 -2
- package/dist/core/runner/prepare-suspend-saga.d.ts +5 -0
- package/dist/core/runner/prepare-suspend-saga.js +5 -2
- package/dist/core/runner/prepare-task.js +18 -13
- package/dist/core/runner/prepare-tool-disclosure-mount.d.ts +5 -0
- package/dist/core/runner/prepare-tool-disclosure-mount.js +10 -2
- package/dist/core/runner/prepare-turn-wiring.d.ts +8 -4
- package/dist/core/runner/prepare-turn-wiring.js +3 -2
- package/dist/core/runner/prepare-wiring-manifest.d.ts +5 -5
- package/dist/core/runner/prepare-wiring-manifest.js +5 -2
- package/dist/core/runner/resume-checkpoint-screen.js +7 -0
- package/dist/core/runner/run-harness-handlers.js +4 -1
- package/dist/core/runner/run-identity-wiring.js +6 -2
- package/dist/core/runner/run-leg.d.ts +2 -0
- package/dist/core/runner/run-leg.js +35 -41
- package/dist/core/runner/run-notification-lane.js +55 -27
- package/dist/core/runner/run-settle-and-teardown.js +1 -0
- package/dist/core/runner/run-terminal-adoption.js +16 -13
- package/dist/core/runner/run-turn-boundary.js +3 -1
- package/dist/core/runner/runtask.d.ts +6 -5
- package/dist/core/runner/runtask.js +26 -14
- package/dist/core/runner/skill-base-dir.d.ts +54 -0
- package/dist/core/runner/skill-base-dir.js +42 -0
- package/dist/core/runner/synthetic-tools.d.ts +16 -19
- package/dist/core/runner/synthetic-tools.js +18 -10
- package/dist/core/runner/tool-defer-gate.d.ts +22 -9
- package/dist/core/runner/tool-defer-gate.js +15 -5
- package/dist/core/runner/tool-disclosure.d.ts +5 -0
- package/dist/core/runner/tool-disclosure.js +2 -0
- package/dist/core/runner/tool-progress-lane.d.ts +42 -0
- package/dist/core/runner/tool-progress-lane.js +61 -0
- package/dist/core/runner-deps.d.ts +48 -7
- package/dist/core/swappable-deps.d.ts +9 -7
- package/dist/core/swappable-deps.js +7 -3
- package/dist/core/task-event.d.ts +96 -29
- package/dist/core/task-notification.d.ts +142 -16
- package/dist/core/task-notification.js +106 -0
- package/dist/core/task-result.d.ts +9 -1
- package/dist/core/task-spec.d.ts +20 -3
- package/dist/core/tighten-task-spec.js +7 -5
- package/dist/core/tool-model-gate.d.ts +1 -1
- package/dist/core/tool-search-setting.d.ts +78 -0
- package/dist/core/tool-search-setting.js +90 -0
- package/dist/core/tool-spec.d.ts +8 -0
- package/dist/core/types.d.ts +1 -1
- package/dist/core/types.js +1 -1
- package/dist/core/wiring-manifest.d.ts +46 -0
- package/dist/core/wiring-manifest.js +9 -1
- package/dist/engine/execution-env/node-execution-env.js +10 -4
- package/dist/engine/harness/agent-harness.d.ts +42 -0
- package/dist/engine/harness/agent-harness.js +61 -29
- package/dist/engine/loop/agent-loop.d.ts +68 -3
- package/dist/engine/loop/agent-loop.js +109 -16
- package/dist/index.d.ts +10 -5
- package/dist/index.js +9 -5
- package/dist/internal/harness.d.ts +1 -1
- package/dist/orchestration/run-workflow-tool.d.ts +3 -0
- package/dist/orchestration/run-workflow-tool.js +6 -1
- package/dist/orchestration/workflow-governance.d.ts +7 -1
- package/dist/orchestration/workflow-governance.js +9 -5
- package/dist/orchestration/workflow-primitives.d.ts +3 -0
- package/dist/orchestration/workflow-primitives.js +6 -1
- package/dist/orchestration/workflow-types.d.ts +48 -13
- package/dist/orchestration/workflow-types.js +1 -1
- package/dist/orchestration/workflow.js +45 -17
- package/dist/prompt-assembly/epoch.js +2 -0
- package/dist/prompt-assembly/packs/sema-default.js +2 -2
- package/dist/prompt-assembly/section-usage.d.ts +26 -0
- package/dist/prompt-assembly/section-usage.js +10 -0
- package/dist/prompt-assembly/types.d.ts +8 -6
- package/dist/prompts/default.d.ts +13 -0
- package/dist/prompts/default.js +16 -8
- package/dist/tools/artifact/artifact-text.d.ts +10 -1
- package/dist/tools/artifact/artifact-text.js +6 -3
- package/dist/tools/artifact/artifact-tool.d.ts +12 -2
- package/dist/tools/artifact/artifact-tool.js +203 -70
- package/dist/tools/artifact/conformance.d.ts +56 -0
- package/dist/tools/artifact/conformance.js +349 -0
- package/dist/tools/artifact/local-stub.js +1 -1
- package/dist/tools/artifact/reject-breaker.d.ts +28 -0
- package/dist/tools/artifact/reject-breaker.js +42 -0
- package/dist/tools/fs/bash-lexer.d.ts +132 -0
- package/dist/tools/fs/bash-lexer.js +563 -0
- package/dist/tools/fs/bash-program-position.d.ts +142 -0
- package/dist/tools/fs/bash-program-position.js +125 -0
- package/dist/tools/fs/bash-readonly-classifier.d.ts +54 -16
- package/dist/tools/fs/bash-readonly-classifier.js +121 -552
- package/dist/tools/fs/containment-face.d.ts +89 -0
- package/dist/tools/fs/containment-face.js +81 -0
- package/dist/tools/fs/fs-bash.d.ts +46 -1
- package/dist/tools/fs/fs-bash.js +158 -22
- package/dist/tools/fs/fs-read.d.ts +1 -1
- package/dist/tools/fs/fs-search-tools.d.ts +3 -3
- package/dist/tools/fs/fs-write.d.ts +4 -3
- package/dist/tools/fs/fs-write.js +9 -9
- package/dist/tools/fs/index.d.ts +24 -5
- package/dist/tools/fs/index.js +18 -6
- package/dist/tools/fs/repo-map.d.ts +2 -2
- package/dist/tools/fs/safety.d.ts +3 -3
- package/dist/tools/fs/safety.js +4 -4
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +85 -9
- package/wire-changes/core@7.17.7.json +49 -0
- package/dist/tools/fs/read-face.d.ts +0 -68
- package/dist/tools/fs/read-face.js +0 -47
|
@@ -20,7 +20,7 @@ import type { ActiveWorktreeSession, AgentHarness, AgentMessage, AgentTool, Anno
|
|
|
20
20
|
import type { Model } from "../../internal/llm.js";
|
|
21
21
|
import type { WorkflowSizeGuideline } from "../../orchestration/workflow-size-guideline.js";
|
|
22
22
|
import type { ToolManifestRow } from "../../prompt-assembly/tool-catalog.js";
|
|
23
|
-
import type { CwdRef,
|
|
23
|
+
import type { CwdRef, ContainmentFace } from "../../tools/fs/index.js";
|
|
24
24
|
import type { MaterializedA2a } from "../a2a.js";
|
|
25
25
|
import type { CompactionForkContext, CompactionPhaseDurations, MaybeCompactOptions, RapidRefillState } from "../auto-compaction.js";
|
|
26
26
|
import type { AutoModeArmingRecipe } from "../auto-mode-arming.js";
|
|
@@ -43,7 +43,7 @@ import type { SystemInjectionPriority, TaskNotificationPayload } from "../task-n
|
|
|
43
43
|
import type { OnAsk, PermissionResult, ResolvedAsk, ToolCallRequest, ToolPolicy } from "../tool-policy.js";
|
|
44
44
|
import type { RemoteExecutionEnv, SnapshotId, WorkspaceHandle } from "../remote-env.js";
|
|
45
45
|
import type { ToolDisclosureManifest } from "../trace.js";
|
|
46
|
-
import type { AgentDefinition, Brain, CompactOutcome, ModelRoles, NestedUsageAccum, RunnerDeps, ShellGateDoctrine, TaskEvent, TaskResult, TaskSpec, TaskStream, ToolActivity, ToolEffect } from "../types.js";
|
|
46
|
+
import type { AgentDefinition, Brain, CompactOutcome, ModelRoles, NestedUsageAccum, RunnerDeps, ShellGateDoctrine, TaskEvent, TaskEventIdentity, TaskResult, TaskSpec, TaskStream, ToolActivity, ToolEffect } from "../types.js";
|
|
47
47
|
import type { WiringManifest } from "../wiring-manifest.js";
|
|
48
48
|
import type { ActiveSkillFrame } from "./active-skill-scope.js";
|
|
49
49
|
import type { BudgetAxis } from "./assemble-result.js";
|
|
@@ -157,6 +157,16 @@ export interface FileHistoryBoundarySeat {
|
|
|
157
157
|
* may no longer match), and the id of the tool call the park is holding (only a `tool_approval` pending
|
|
158
158
|
* action parks ON a call; absent, never guessed, for the kinds that hold none).
|
|
159
159
|
*/
|
|
160
|
+
/** See {@link Prepared.undeliveredContinuationRef} — `parkedSteers` is in ISSUE order, and the wake message rides it with no `seq` (no store minted one). */
|
|
161
|
+
export interface UndeliveredContinuation {
|
|
162
|
+
parkedSteers: Array<Omit<PendingSteerEntry, "seq"> & {
|
|
163
|
+
seq?: number;
|
|
164
|
+
}>;
|
|
165
|
+
orphanTasks: Array<{
|
|
166
|
+
id: string;
|
|
167
|
+
description?: string;
|
|
168
|
+
}>;
|
|
169
|
+
}
|
|
160
170
|
export interface CommittedPause {
|
|
161
171
|
cause: import("../types.js").PausedCause;
|
|
162
172
|
scope: string;
|
|
@@ -278,7 +288,9 @@ export interface Prepared {
|
|
|
278
288
|
* block's single resolution, or the hands-less legs' resolver run (live spec-time facts, with the
|
|
279
289
|
* checkpoint seed folded stricter-wins where one exists). Every leg that completes prepare has a
|
|
280
290
|
* read posture now — this is what its delegation subtree is clamped by even where no faces mount. */
|
|
281
|
-
effectiveReadFace?:
|
|
291
|
+
effectiveReadFace?: ContainmentFace;
|
|
292
|
+
/** owned — the same observation on the WRITE seat (`TaskResult.effectiveWriteFace`). */
|
|
293
|
+
effectiveWriteFace?: ContainmentFace;
|
|
282
294
|
/** The normalized deny ADDITIONS in force (deployment ∪ task ∪ checkpoint seed; built-ins
|
|
283
295
|
* excluded), echoed on `TaskResult.effectiveReadDenyPatterns`. Present iff non-empty; a defensive
|
|
284
296
|
* copy (the wide-scope working array stays the engine's own). */
|
|
@@ -291,6 +303,13 @@ export interface Prepared {
|
|
|
291
303
|
/** The per-task logical cwd ref when a real shell is mounted (else undefined). The Runner
|
|
292
304
|
* reads `cwdRef.current` after each tool to detect a `cd` move and emit `workspace_changed`. */
|
|
293
305
|
cwdRef?: CwdRef;
|
|
306
|
+
/** The run's TOOL-PROGRESS lane seat, late-bound for the same reason `harnessRef` is (the tool band is
|
|
307
|
+
* built during prepare; the lane needs `pushContent` and `ident`, which exist only after it returns).
|
|
308
|
+
* The driver binds `current` right after `pushContent` exists. Always present on a completed prepare —
|
|
309
|
+
* the CELL, not the lane: unbound means "no run body has claimed this band", and emissions no-op. */
|
|
310
|
+
toolProgressRef: {
|
|
311
|
+
current?: ToolProgressLane;
|
|
312
|
+
};
|
|
294
313
|
/** The run's advertised-writable-directory seat (present with hands): `roots` is what the prompt advertised as writable and the fence admitted. Read live wherever a policy is adjudicated off a resumed leg, so the write gate's exemption domain is that same list. */
|
|
295
314
|
advertisedWritableDirs?: Pick<import("./advertised-writable-dirs.js").AdvertisedWritableDirs, "roots">;
|
|
296
315
|
/** The ACTIVE EnterWorktree session ref (mounted with the worktree tools; undefined without
|
|
@@ -462,6 +481,7 @@ export interface Prepared {
|
|
|
462
481
|
* "undiscovered" census the tool_search_usage_reminder lane enumerates. Frozen at prepare time: a
|
|
463
482
|
* name never leaves the deferred set, it only moves into `activeTools`. */
|
|
464
483
|
deferredToolNames?: ReadonlySet<string>;
|
|
484
|
+
/** The deferral census seat, present iff {@link deferredToolNames} is. */ toolDisclosure?: import("./deferral-census.js").ToolDisclosureSeat;
|
|
465
485
|
/** The RESOLVED materialization strategy this run runs under (`true` = "static":
|
|
466
486
|
* activation never swaps the placeholder; the real schema's only in-context carrier is the
|
|
467
487
|
* ToolSearch result text). Consumed by the tools_delta boundary renderer, whose "full schemas are
|
|
@@ -587,6 +607,13 @@ export interface Prepared {
|
|
|
587
607
|
pausedRef: {
|
|
588
608
|
current?: CommittedPause;
|
|
589
609
|
};
|
|
610
|
+
/** A resumed leg's continuation content that has NOT reached the transcript yet: EVERY input frame the
|
|
611
|
+
* prompt carries (parked steers, the caller's wake message) and the orphaned background tasks whose obituary
|
|
612
|
+
* rides it. Set before the prompt, emptied at delivery; a re-park carries it forward (the saga folds the
|
|
613
|
+
* orphans in, the adoption re-parks the frames), and what no re-park carries the undrained account announces. */
|
|
614
|
+
undeliveredContinuationRef: {
|
|
615
|
+
current: UndeliveredContinuation;
|
|
616
|
+
};
|
|
590
617
|
/** Ruled 2026-08-05 (matrix ruling arm A): set true by the resume engine when this leg EXECUTES the
|
|
591
618
|
* approved pending call — the restart-loop cap then counts from a fresh base (consecutive
|
|
592
619
|
* no-progress suspends only). See `suspendChainBase`. */
|
|
@@ -1345,6 +1372,7 @@ export interface PromptFeatureFlags {
|
|
|
1345
1372
|
isolationEnabled: boolean;
|
|
1346
1373
|
reminderMark: string;
|
|
1347
1374
|
readFaceOpen: boolean;
|
|
1375
|
+
writeFaceOpen: boolean;
|
|
1348
1376
|
orchestrationEnabled: boolean;
|
|
1349
1377
|
orchestrationDeferred: boolean;
|
|
1350
1378
|
promptProfile: "simple" | "classic";
|
|
@@ -1782,10 +1810,6 @@ export interface RunInternals {
|
|
|
1782
1810
|
onStatusEvent?: (event: Extract<TaskEvent, {
|
|
1783
1811
|
type: "status";
|
|
1784
1812
|
}>) => void;
|
|
1785
|
-
/**
|
|
1786
|
-
* Trusted run-local system-injection sink. `Runner.runLocked` wires this to the
|
|
1787
|
-
* live TaskStream queue plus the current harness follow-up lane; it is not a public TaskSpec field.
|
|
1788
|
-
*/
|
|
1789
1813
|
/** The run-local per-tool-call detach hub. runtask creates it and exposes
|
|
1790
1814
|
* `TaskStream.detach(toolCallId)`; the hands Bash tool threads `signalFor(toolCallId)` into env.exec. */
|
|
1791
1815
|
detachHub?: import("../tool-detach.js").ToolDetachHub;
|
|
@@ -1797,6 +1821,11 @@ export interface RunInternals {
|
|
|
1797
1821
|
* channel, same posture as every other field here.
|
|
1798
1822
|
*/
|
|
1799
1823
|
sessionReadStates?: SessionReadFileStates;
|
|
1824
|
+
/** Trusted system-injection sink (`Runner.runLocked` wires it to the live TaskStream queue plus the
|
|
1825
|
+
* current harness lanes; never a public TaskSpec field), ADDRESSED TO THE SESSION rather than to this
|
|
1826
|
+
* run: a producer wired here (a background shell, a watch, a delegation settle) keeps the closure long
|
|
1827
|
+
* after the run that mounted it ended, and its frame is then routed to whatever run the SESSION has
|
|
1828
|
+
* live — at that run's next turn boundary, the seat a steer lands on; only a session with NO live run parks it. */
|
|
1800
1829
|
onTaskNotification?: (notification: TaskNotificationPayload,
|
|
1801
1830
|
/** Injection tier (the ladder is LIVE): "next" = the running turn's next boundary
|
|
1802
1831
|
* (arrival order, consecutive frames batch); "later" = the run's would-otherwise-stop seat
|
|
@@ -1812,9 +1841,9 @@ export interface RunInternals {
|
|
|
1812
1841
|
* `injectTaskNotification`), threaded into a CHILD's internals at spawn time so the child's
|
|
1813
1842
|
* SendMessage("main") uplink lands in the parent's queue at a turn boundary (the CC
|
|
1814
1843
|
* "delivered automatically" parent half). DELIBERATELY a separate field from
|
|
1815
|
-
* {@link RunInternals.onTaskNotification}: that one is "inject into
|
|
1816
|
-
* as upstream-observer +
|
|
1817
|
-
* notification (grandchild completions, monitor events) into the parent — double delivery.
|
|
1844
|
+
* {@link RunInternals.onTaskNotification}: that one is "inject into the run's own SESSION" (runtask
|
|
1845
|
+
* wraps it as upstream-observer + session route), and reusing it for the child would tee every
|
|
1846
|
+
* internal child notification (grandchild completions, monitor events) into the parent — double delivery.
|
|
1818
1847
|
*/
|
|
1819
1848
|
parentNotify?: (notification: TaskNotificationPayload, opts?: {
|
|
1820
1849
|
priority?: import("../task-notification.js").SystemInjectionPriority;
|
|
@@ -1831,9 +1860,9 @@ export interface RunInternals {
|
|
|
1831
1860
|
* live (runtask calls it with its wrapped `injectTaskNotification`). The spawner stores it on the
|
|
1832
1861
|
* child's registry handle so a parent/sibling SendMessage can deliver TO the RUNNING child at its
|
|
1833
1862
|
* next turn boundary (CC's in-memory `pendingMessages` pedestal, anchors/2.1.212
|
|
1834
|
-
* messaging-runtime.md §2.5). The injector
|
|
1835
|
-
*
|
|
1836
|
-
* CC's durable-mailbox posture for an idle teammate (§2.4)
|
|
1863
|
+
* messaging-runtime.md §2.5). The injector outlives its run safely: once this lane is over the frame
|
|
1864
|
+
* is routed to whatever run the SESSION has live, and only a session with no live run parks it
|
|
1865
|
+
* (PendingSessionNotifications) — exactly CC's durable-mailbox posture for an idle teammate (§2.4).
|
|
1837
1866
|
* TRUSTED chain (core spawner only); never reachable from TaskSpec.
|
|
1838
1867
|
*/
|
|
1839
1868
|
onNotifyInjectorReady?: (inject: (notification: TaskNotificationPayload, opts?: {
|
|
@@ -2360,6 +2389,30 @@ export interface TurnBoundaryDeps {
|
|
|
2360
2389
|
/** R5(harness 事件处理器族)的依赖包。全部为 runLocked 内声明顺序早于
|
|
2361
2390
|
* 工厂调用点的 const 稳定引用 + 三个 runLocked 形参;this 面(deps.onError)经 runnerHooks 打包
|
|
2362
2391
|
* (B13 #coreView / R9 TurnBoundaryDeps 同款)。*/
|
|
2392
|
+
/** What a PRODUCER of a `tool_progress` frame knows: the FACTS it observed. The frame minus everything only
|
|
2393
|
+
* the run supplies — its discriminator, its {@link TaskEventIdentity} stamp, and the elapsed time, which the
|
|
2394
|
+
* lane computes from its own per-call origin. Derived from the union member, so seat and frame cannot drift. */
|
|
2395
|
+
export type ToolProgressBody = Omit<Extract<TaskEvent, {
|
|
2396
|
+
type: "tool_progress";
|
|
2397
|
+
}>, "type" | "elapsedTimeSeconds" | keyof TaskEventIdentity>;
|
|
2398
|
+
/** The run's TOOL-PROGRESS lane: the ONE mint of `tool_progress` plus the heartbeat clock over the calls
|
|
2399
|
+
* in flight. Declared on the FLOOR because both registry-side readers sit above its implementation (the
|
|
2400
|
+
* driver builds it, the harness-handler lane drives it) and the Prepared seat that carries it to the tool
|
|
2401
|
+
* band is here too. `createToolProgressLane` (tool-progress-lane.ts, layer 1) is its one implementation. */
|
|
2402
|
+
export interface ToolProgressLane {
|
|
2403
|
+
/** Offer ONE frame — from a tool that observes its own progress (the shell's per-second tick) or from
|
|
2404
|
+
* the lane's own heartbeat. The lane stamps the elapsed time, DROPS the offer when it would land inside
|
|
2405
|
+
* the cadence floor, and resets that call's beat clock when it does not. NEVER THROWS: a tool calls this
|
|
2406
|
+
* from inside an environment output callback, where a throw kills the command it is describing. */
|
|
2407
|
+
emit(body: ToolProgressBody): void;
|
|
2408
|
+
/** A tool call entered execution: it joins the heartbeat's in-flight set and its clock starts, stamped
|
|
2409
|
+
* by the LANE (one clock owns one number — see its module header on why that clock is monotonic). */
|
|
2410
|
+
noteStart(toolCallId: string, toolName: string): void;
|
|
2411
|
+
/** A tool call settled: it leaves the in-flight set. */
|
|
2412
|
+
noteEnd(toolCallId: string): void;
|
|
2413
|
+
/** Drop every in-flight call and disarm the timer (turn end / teardown): no beating into the next turn. */
|
|
2414
|
+
clear(): void;
|
|
2415
|
+
}
|
|
2363
2416
|
export interface HarnessHandlersDeps {
|
|
2364
2417
|
spec: TaskSpec;
|
|
2365
2418
|
queue: PushQueue<TaskEvent>;
|
|
@@ -2375,6 +2428,9 @@ export interface HarnessHandlersDeps {
|
|
|
2375
2428
|
emitCommitted: (entryId: string, role: "user" | "assistant" | "toolResult", toolCallId?: string) => void;
|
|
2376
2429
|
startedToolCallIds: Set<string>;
|
|
2377
2430
|
toolStartAt: Map<string, number>;
|
|
2431
|
+
/** The run's tool-progress lane: the handlers tell it when a call enters and leaves execution, and
|
|
2432
|
+
* clear it with the rest of the per-call bookkeeping at turn end. */
|
|
2433
|
+
toolProgress: ToolProgressLane;
|
|
2378
2434
|
writeFamilyOf: (name: string) => WriteFamily | undefined;
|
|
2379
2435
|
toolLabels: Map<string, string>;
|
|
2380
2436
|
postToolBatchHook: ((batch: PostToolBatchCall[], meta?: {
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* · which call a CONTENT-ASK checkpoint parks on, and the one-shot answering face a decide's answer is
|
|
8
8
|
* bound to for the redeemed call;
|
|
9
9
|
* · the capture-time domain checks on a decide's TEXT payloads, and the label a rejected value gets;
|
|
10
|
-
* · the
|
|
10
|
+
* · the continuation prompt itself.
|
|
11
11
|
*
|
|
12
12
|
* Pure over its arguments (the answering face is the one closure, and it closes over its own arguments
|
|
13
13
|
* only). Run-loop machinery (layer 1), named without the `resume-` family prefix: that prefix marks a
|
|
@@ -25,8 +25,6 @@ import type { ResumeRun } from "./contracts.js";
|
|
|
25
25
|
* reported the same decidePath locked no matter how many times the human decided).
|
|
26
26
|
*/
|
|
27
27
|
export declare function resumeDecisionWasNegative(resume: ResumeRun): boolean;
|
|
28
|
-
/** Model-facing result for a batch sibling of a suspended call that v1 does not blind-run (§4.ter). */
|
|
29
|
-
export declare const DEFERRED_REISSUE: string;
|
|
30
28
|
/**
|
|
31
29
|
* Structural deep equality of two JSON-serializable values (order-independent over object keys). Used ONLY
|
|
32
30
|
* by the Runner's `resumeStream` reopen-by-reason guard to confirm an `env_failed` re-resume
|
|
@@ -7,8 +7,6 @@ export function resumeDecisionWasNegative(resume) {
|
|
|
7
7
|
const o = resume.outcome;
|
|
8
8
|
return o.decision === "reject" || o.decision === "deny";
|
|
9
9
|
}
|
|
10
|
-
export const DEFERRED_REISSUE = "[DEFERRED] This tool call shared a batch with a call that suspended for durable approval, so it was " +
|
|
11
|
-
"NOT executed on resume. If you still need it, issue it again now.";
|
|
12
10
|
export function deepJsonEqual(a, b) {
|
|
13
11
|
if (a === b)
|
|
14
12
|
return true;
|
|
@@ -122,11 +120,7 @@ export function resumeContinuation(resume, mark) {
|
|
|
122
120
|
const decided = resume.outcome.decision === "allow"
|
|
123
121
|
? "was APPROVED and has now been executed — its result is in the tool results above"
|
|
124
122
|
: `was DENIED${resume.outcome.reason ? `: ${delimitUntrusted("reviewer note", resume.outcome.reason, REVIEWER_NOTE_MAX_BODY)}` : ""}`;
|
|
125
|
-
|
|
126
|
-
const batchNote = remaining > 0
|
|
127
|
-
? ` ${remaining} other tool call(s) from that batch were returned as deferred and not run — re-issue any you still need.`
|
|
128
|
-
: "";
|
|
129
|
-
return formatHookFeedback(`The pending tool call "${pendingAction.toolName}" ${decided}.${batchNote} ` +
|
|
123
|
+
return formatHookFeedback(`The pending tool call "${pendingAction.toolName}" ${decided}. ` +
|
|
130
124
|
`This is a RESUMED task — every step before this point is already done and its results are in the ` +
|
|
131
125
|
`conversation above. Do NOT restart the task or re-run any tool you already ran; continue from this ` +
|
|
132
126
|
`exact point, building on the existing results, and finish the remaining work.`, mark);
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The deferral CENSUS: what a leg discloses about which of its tools ship as name-only placeholders, and the
|
|
3
|
+
* seat that carries it to the stream. Split out of the disclosure mount because it is a different concern —
|
|
4
|
+
* the mount decides the request FACE, this decides what is SAID about it.
|
|
5
|
+
*/
|
|
6
|
+
import type { ToolSearchPolicy, ResolvedToolSearch } from "../tool-search-setting.js";
|
|
7
|
+
/**
|
|
8
|
+
* The HOST-facing census of this leg's deferral: which tool names ship as name-only placeholders, which the model has activated, and the policy that decided it.
|
|
9
|
+
* @contract tool_disclosure.snapshot — the `tool_disclosure` frame is minted ONLY here ({@link ToolDisclosureSeat.publish}), exists ONLY on a leg that defers
|
|
10
|
+
* at least one tool, and carries the WHOLE census each time (a consumer replaces its copy; the "what just happened" delta is this snapshot's `activated` minus
|
|
11
|
+
* the previous one's). `deferred` is frozen at prepare (a name never leaves it, it only appears in `activated`); both lists are sorted, so two snapshots
|
|
12
|
+
* compare by value. A leg that defers nothing emits no frame at all — absence is "nothing was deferred", never "the disclosure is off".
|
|
13
|
+
*/
|
|
14
|
+
export interface ToolDisclosureSnapshot {
|
|
15
|
+
/** Which arm of `RunnerDeps.toolSearch` decided rule ② on this leg. */
|
|
16
|
+
policy: ToolSearchPolicy;
|
|
17
|
+
/** Present iff `policy === "auto"`: the percent of the window the candidate set had to reach. */
|
|
18
|
+
thresholdPercent?: number;
|
|
19
|
+
/** Every deferred name on this leg, sorted. Non-empty (the frame does not exist otherwise). */
|
|
20
|
+
deferred: readonly string[];
|
|
21
|
+
/** The deferred names ACTIVE on this leg right now, sorted — the engine's state, not a record of commits. Empty on the leg-start census only when nothing
|
|
22
|
+
* seeded it; a RESUMED leg carries whatever the engine reseeded (transcript replay, compaction snapshot, checkpoint) before anything executed. */
|
|
23
|
+
activated: readonly string[];
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* The run-time seat the frame rides: prepare publishes the leg-start census, every activation that changes it publishes the next, and the run's event wiring
|
|
27
|
+
* subscribes once. Snapshots published before the sink is installed are buffered (bounded) and flushed in order, so the leg-start census can be minted at
|
|
28
|
+
* prepare and still arrive after the manifest frame it belongs beside. Deliberately the same shape as `ToolRosterDeltaSeat` — the two are one problem.
|
|
29
|
+
*/
|
|
30
|
+
export declare class ToolDisclosureSeat {
|
|
31
|
+
#private;
|
|
32
|
+
/** Publish a census. A snapshot equal to the last published one is DROPPED: `rematerialize` also runs on an MCP refresh, and a frame that says nothing
|
|
33
|
+
* changed reads as an activation that did not happen. */
|
|
34
|
+
publish(snapshot: ToolDisclosureSnapshot): void;
|
|
35
|
+
/** Install the stream sink; anything published before is flushed in order. */
|
|
36
|
+
subscribe(sink: (snapshot: ToolDisclosureSnapshot) => void): void;
|
|
37
|
+
/** The leg ended: drop the sink so a late publish never reaches a closed stream (it buffers, unread). */
|
|
38
|
+
unsubscribe(): void;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Mint a leg's census producer: the frozen deferred list, the policy it was classified under, and a seat.
|
|
42
|
+
* One place holds both halves, so a census can never name a policy the classification did not run under.
|
|
43
|
+
*
|
|
44
|
+
* `deferred` is frozen because every census shares the one array — a host that mutated the list it was handed
|
|
45
|
+
* would be editing every later frame of the leg. `activated` is filtered to the DEFERRED names: the live
|
|
46
|
+
* activation set is seeded from the transcript and a resume checkpoint, both of which can name an undeferred
|
|
47
|
+
* tool.
|
|
48
|
+
*/
|
|
49
|
+
export declare function createDeferralCensus(deferred: ReadonlySet<string>, policy: ResolvedToolSearch): {
|
|
50
|
+
seat: ToolDisclosureSeat;
|
|
51
|
+
publish: (active: ReadonlySet<string>) => void;
|
|
52
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export class ToolDisclosureSeat {
|
|
2
|
+
#pending = [];
|
|
3
|
+
#sink;
|
|
4
|
+
#last;
|
|
5
|
+
publish(snapshot) {
|
|
6
|
+
if (this.#last !== undefined && sameDisclosure(this.#last, snapshot))
|
|
7
|
+
return;
|
|
8
|
+
this.#last = snapshot;
|
|
9
|
+
if (this.#sink !== undefined)
|
|
10
|
+
this.#sink(snapshot);
|
|
11
|
+
else {
|
|
12
|
+
this.#pending.push(snapshot);
|
|
13
|
+
if (this.#pending.length > 64)
|
|
14
|
+
this.#pending.shift();
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
subscribe(sink) {
|
|
18
|
+
this.#sink = sink;
|
|
19
|
+
const pending = this.#pending;
|
|
20
|
+
this.#pending = [];
|
|
21
|
+
for (const s of pending)
|
|
22
|
+
sink(s);
|
|
23
|
+
}
|
|
24
|
+
unsubscribe() {
|
|
25
|
+
this.#sink = undefined;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function sameDisclosure(a, b) {
|
|
29
|
+
const sameList = (x, y) => x.length === y.length && x.every((n, i) => n === y[i]);
|
|
30
|
+
return a.policy === b.policy && a.thresholdPercent === b.thresholdPercent && sameList(a.deferred, b.deferred) && sameList(a.activated, b.activated);
|
|
31
|
+
}
|
|
32
|
+
export function createDeferralCensus(deferred, policy) {
|
|
33
|
+
const seat = new ToolDisclosureSeat();
|
|
34
|
+
const names = Object.freeze([...deferred].sort());
|
|
35
|
+
return {
|
|
36
|
+
seat,
|
|
37
|
+
publish: (active) => seat.publish({
|
|
38
|
+
policy: policy.policy,
|
|
39
|
+
...(policy.thresholdPercent !== undefined ? { thresholdPercent: policy.thresholdPercent } : {}),
|
|
40
|
+
deferred: names,
|
|
41
|
+
activated: [...active].filter((n) => deferred.has(n)).sort(),
|
|
42
|
+
}),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
@@ -2,15 +2,15 @@
|
|
|
2
2
|
* The derived-leg route pre-flight shared by prepareTask's two derived seats: the compaction-summary model (the
|
|
3
3
|
* driver's own seat, resolved before the offload prelude) and the auto-mode classifier model (the caps-and-workflow
|
|
4
4
|
* phase's arming). Moved whole out of the orchestrator so the phase reaches DOWN for it instead of the two callers
|
|
5
|
-
* spelling it twice. The judgment law
|
|
6
|
-
* around
|
|
5
|
+
* spelling it twice. The judgment law and both announcements live in the brain's route adjudicator; this is the one
|
|
6
|
+
* guarded seat around them.
|
|
7
7
|
*/
|
|
8
8
|
import type { Model } from "../../internal/llm.js";
|
|
9
9
|
import type { RunnerDeps, TaskSpec } from "../types.js";
|
|
10
10
|
/**
|
|
11
11
|
* The derived-leg pairing pre-flight as ONE guarded seat: judge the derived model where its route
|
|
12
|
-
* differs from the primary's, announce
|
|
13
|
-
* derived model) on ANY throw. The pre-flight is advisory — the brain's request gate re-runs the
|
|
12
|
+
* differs from the primary's, announce what a broken pairing means for THIS seat (`onBrokenPairing` —
|
|
13
|
+
* one judgment, and the POSTURE is the deployment's), and ABSTAIN (keep the derived model) on ANY throw. The pre-flight is advisory — the brain's request gate re-runs the
|
|
14
14
|
* same law — so neither a throwing judge face nor a garbage catalog value (the route-identity
|
|
15
15
|
* normalization throws a TypeError on a non-string baseUrl) may widen into "task preparation
|
|
16
16
|
* failed": at the compaction seat the session is already acquired and the throw-cleanup contract
|
|
@@ -31,4 +31,8 @@ export declare function derivedRouteFallsBack(args: {
|
|
|
31
31
|
/** The INVOCATION this seat resolution belongs to. The session cannot stand in for it: two
|
|
32
32
|
* runs of one session that both fall back on the same seat mint otherwise byte-identical lines. */
|
|
33
33
|
runId?: string;
|
|
34
|
+
/** What this seat's DEPLOYMENT asked for on a broken pairing. Absent / `"fall_back"`: swap and announce `route.fallback_to_primary`
|
|
35
|
+
* (the only posture a seat with no knob of its own has). `"refuse"`: keep the seat and announce `route.classifier_unroutable` — never
|
|
36
|
+
* quietly granting a fallback the deployment declined (`autoMode.classifierFallback: "none"`); announcing is why no swap is not no word. */
|
|
37
|
+
onBrokenPairing?: "fall_back" | "refuse";
|
|
34
38
|
}): Promise<boolean>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { adjudicateDerivedRoute, fallbackToPrimaryNotice, sameRouteIdentity } from "../../brain/route-adjudicator.js";
|
|
1
|
+
import { adjudicateDerivedRoute, fallbackToPrimaryNotice, sameRouteIdentity, unroutableSeatNotice } from "../../brain/route-adjudicator.js";
|
|
2
2
|
import { deliverEngineNotice } from "../types.js";
|
|
3
3
|
export async function derivedRouteFallsBack(args) {
|
|
4
4
|
try {
|
|
@@ -7,8 +7,13 @@ export async function derivedRouteFallsBack(args) {
|
|
|
7
7
|
const verdict = await adjudicateDerivedRoute({ brain: args.brain, model: args.derived, getApiKeyAndHeaders: args.getApiKeyAndHeaders });
|
|
8
8
|
if (verdict === undefined || verdict.ok)
|
|
9
9
|
return false;
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
const shared = { seat: args.seat, from: args.derived.id, verdict, ...(args.sessionId !== undefined ? { sessionId: args.sessionId } : {}), ...(args.runId !== undefined ? { runId: args.runId } : {}) };
|
|
11
|
+
const refuse = args.onBrokenPairing === "refuse";
|
|
12
|
+
if (refuse)
|
|
13
|
+
deliverEngineNotice(args.onNotice, unroutableSeatNotice({ ...shared, primary: args.primary.id }));
|
|
14
|
+
else
|
|
15
|
+
deliverEngineNotice(args.onNotice, fallbackToPrimaryNotice({ ...shared, to: args.primary.id }));
|
|
16
|
+
return !refuse;
|
|
12
17
|
}
|
|
13
18
|
catch {
|
|
14
19
|
return false;
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
* contract (`ToolGateInput`, `ToolGateResult`, the observer payload), which stays declared there.
|
|
23
23
|
*/
|
|
24
24
|
import { type ClassifierDenyCause, type DeniedBy, type GateOutcome, type Settlement } from "../gate-outcome.js";
|
|
25
|
+
import type { AutoModeClassifierRound } from "../auto-mode.js";
|
|
25
26
|
import type { AskOrigin, AskOriginFacts } from "../ask-origin.js";
|
|
26
27
|
import { type PermissionResult, type ResolvedAsk, type ToolCallRequest } from "../tool-policy.js";
|
|
27
28
|
import type { SafeNotifier } from "../safe-notify.js";
|
|
@@ -40,6 +41,10 @@ export interface GateExitFacts {
|
|
|
40
41
|
deniedBy?: DeniedBy;
|
|
41
42
|
cause?: ClassifierDenyCause;
|
|
42
43
|
settled?: SettledAsk;
|
|
44
|
+
/** The classifier CLASSIFICATION this pass ran, when it ran one on a decider that can name its model.
|
|
45
|
+
* Orthogonal to `deniedBy`: one that ALLOWED is attributed too (a fallback model's allow is exactly the case
|
|
46
|
+
* an operator needs to see), which is why it rides the disposition's shared member, not the deny arm. */
|
|
47
|
+
classifier?: AutoModeClassifierRound;
|
|
43
48
|
}
|
|
44
49
|
/**
|
|
45
50
|
* Mint the outcome. A defect here is an ENGINE defect (a refusing site that did not say who refused, a
|
|
@@ -220,6 +225,13 @@ export interface GatePass {
|
|
|
220
225
|
* re-check when an inherited station's attested deny arrives through the chain; undefined for a ruling
|
|
221
226
|
* block and every non-classifier deny. The exit mints `GateDisposition.cause` from it. */
|
|
222
227
|
denyCause: ClassifierDenyCause | undefined;
|
|
228
|
+
/** borrowed-mutable — the classifier CLASSIFICATION this pass ran ({@link AutoModeClassifierRound}).
|
|
229
|
+
* Written ONCE by the gate's classifier step from the verdict the decider handed back, on EVERY arm (allow
|
|
230
|
+
* included — a fallback model's allow is the fact the record exists for); absent when no classifier was
|
|
231
|
+
* consulted or the decider could name no model. Both exits mint `GateDisposition.classifier` from it.
|
|
232
|
+
* OPTIONAL rather than `| undefined` (the `probeUnanswered` form): the dispatch that builds the pass is
|
|
233
|
+
* not a station of this lane and has nothing to declare here. */
|
|
234
|
+
classifierRound?: AutoModeClassifierRound;
|
|
223
235
|
/** borrowed-mutable — the standing verdict. The no-policy default until the adjudication station writes the
|
|
224
236
|
* policy's answer; then rewritten by every station that changes the verdict (fold, tightens, org, rule
|
|
225
237
|
* lane, stamp, classifier, sandbox, content lane, resolution, edit chain). */
|
|
@@ -4,8 +4,11 @@ import { inlineUntrusted } from "../untrusted-text.js";
|
|
|
4
4
|
import { formatHookFeedback } from "../reminder-mint.js";
|
|
5
5
|
export function mintGateOutcome(facts) {
|
|
6
6
|
const settlement = facts.settled !== undefined ? Object.freeze({ ...facts.settled.settlement, who: Object.freeze({ ...facts.settled.settlement.who }) }) : undefined;
|
|
7
|
+
const attribution = facts.classifier !== undefined ? Object.freeze({ ...facts.classifier, ...(facts.classifier.fallback !== undefined ? { fallback: Object.freeze({ ...facts.classifier.fallback }) } : {}) }) : undefined;
|
|
7
8
|
const outcome = Object.freeze({
|
|
8
|
-
disposition: Object.freeze(facts.deniedBy !== undefined
|
|
9
|
+
disposition: Object.freeze(facts.deniedBy !== undefined
|
|
10
|
+
? { kind: "denied", deniedBy: facts.deniedBy, ...(facts.cause !== undefined ? { cause: facts.cause } : {}), ...(attribution !== undefined ? { classifier: attribution } : {}) }
|
|
11
|
+
: { kind: "allowed", ...(attribution !== undefined ? { classifier: attribution } : {}) }),
|
|
9
12
|
...(facts.settled !== undefined ? { settlement: settlement, origin: facts.settled.origin } : {}),
|
|
10
13
|
});
|
|
11
14
|
const defects = screenGateOutcome(outcome);
|
|
@@ -114,7 +117,7 @@ export async function exitGate(pass) {
|
|
|
114
117
|
}
|
|
115
118
|
if (pass.deniedBy === undefined)
|
|
116
119
|
throw new Error(`the tool gate refused "${toolName}" without a refusing layer — every deny site attributes itself`);
|
|
117
|
-
const gate = mintGateOutcome({ deniedBy: pass.deniedBy, ...(pass.denyCause !== undefined ? { cause: pass.denyCause } : {}), ...(ledger.settled !== undefined ? { settled: ledger.settled } : {}) });
|
|
120
|
+
const gate = mintGateOutcome({ deniedBy: pass.deniedBy, ...(pass.denyCause !== undefined ? { cause: pass.denyCause } : {}), ...(ledger.settled !== undefined ? { settled: ledger.settled } : {}), ...(pass.classifierRound !== undefined ? { classifier: pass.classifierRound } : {}) });
|
|
118
121
|
const unresolvable = askUnresolvableCauseOf(pass.decision, { toolCallId, toolName }) ?? unresolvableCauseOfSettlement(gate);
|
|
119
122
|
if (unresolvable !== undefined) {
|
|
120
123
|
const cause = unresolvable;
|
|
@@ -142,7 +145,7 @@ export async function exitGate(pass) {
|
|
|
142
145
|
}
|
|
143
146
|
return {
|
|
144
147
|
updatedInput: pass.currentInput === event.input ? undefined : pass.currentInput,
|
|
145
|
-
gate: mintGateOutcome(ledger.settled !== undefined ? { settled: ledger.settled } : {}),
|
|
148
|
+
gate: mintGateOutcome({ ...(ledger.settled !== undefined ? { settled: ledger.settled } : {}), ...(pass.classifierRound !== undefined ? { classifier: pass.classifierRound } : {}) }),
|
|
146
149
|
preToolContext: pass.preToolContext,
|
|
147
150
|
};
|
|
148
151
|
}
|
|
@@ -7,13 +7,18 @@
|
|
|
7
7
|
* face) exactly as `Read` does, then reads the bytes once from the execution env. A local stub's store
|
|
8
8
|
* (`capabilities.storageDir`) must lie OUTSIDE every root the run may write or read — a ledger the model can
|
|
9
9
|
* edit through the fs tools is not a trust boundary — and a store inside one refuses the mount, naming both.
|
|
10
|
+
* The store is a path on THIS process's filesystem; a root is the execution env's canonical key. The two are
|
|
11
|
+
* compared as identities under one spelling family only when both spell the same family (`within` reads both
|
|
12
|
+
* under the root's family); a root of the other family names a filesystem the store cannot be on, and no
|
|
13
|
+
* containment claim is made against it.
|
|
10
14
|
* Synchronous: the stretch has no await (the file read is a closure the tool calls later).
|
|
11
15
|
*/
|
|
12
16
|
import type { ExecutionEnv } from "../../internal/harness.js";
|
|
17
|
+
import type { OnAsk } from "../tool-policy.js";
|
|
13
18
|
import { type ArtifactPlane } from "../artifact-host.js";
|
|
14
19
|
import { RosterBuilder } from "../tool-roster.js";
|
|
15
20
|
import type { AgentTool, RunnerDeps } from "../types.js";
|
|
16
|
-
import type { ReadDenyMatcher,
|
|
21
|
+
import type { ReadDenyMatcher, ContainmentFace } from "../../tools/fs/index.js";
|
|
17
22
|
import type { Prepared } from "./contracts.js";
|
|
18
23
|
export interface PrepareArtifactInput {
|
|
19
24
|
/** borrowed-readonly — the deployment's hosting seat and the notice sink the refusal arm announces on. */
|
|
@@ -26,6 +31,9 @@ export interface PrepareArtifactInput {
|
|
|
26
31
|
sessionId: string;
|
|
27
32
|
/** borrowed-readonly — the spawned name this run carries, when any (call-context attribution only). */
|
|
28
33
|
agentName: string | undefined;
|
|
34
|
+
/** borrowed-readonly — the run's effective approver (`spec.onAsk ?? deps.onAsk`, the seat the tool gate resolves at):
|
|
35
|
+
* the tool's shared-read consent gate rides it as a mount dep (a core mount's execute ctx is minimal). */
|
|
36
|
+
onAsk: OnAsk | undefined;
|
|
29
37
|
/** borrowed-readonly — the run's resolved env: the byte source of a published local file. */
|
|
30
38
|
executionEnv: ExecutionEnv;
|
|
31
39
|
/** borrowed-readonly — the canonical task root the fs fence contains reads to; undefined ⇒ no local-file publish. */
|
|
@@ -35,7 +43,7 @@ export interface PrepareArtifactInput {
|
|
|
35
43
|
/** borrowed-readonly — the read-deny matcher in force for the run, when any. */
|
|
36
44
|
readDenyMatcher: ReadDenyMatcher | undefined;
|
|
37
45
|
/** borrowed-readonly — the resolved read face (`"open"` lifts root containment for reads). */
|
|
38
|
-
resolvedReadFace:
|
|
46
|
+
resolvedReadFace: ContainmentFace | undefined;
|
|
39
47
|
/** borrowed-readonly — the tracked-cwd cell; `current` is read per call as the relative-path base. */
|
|
40
48
|
handsCwdRef: Prepared["cwdRef"];
|
|
41
49
|
/** borrowed-mutable — the run's roster builder; writer here: ONE mount when the seat is valid. */
|
|
@@ -5,10 +5,10 @@ import { RosterBuilder } from "../tool-roster.js";
|
|
|
5
5
|
import { defineTool } from "../tools.js";
|
|
6
6
|
import { inlineUntrusted } from "../untrusted-text.js";
|
|
7
7
|
import { physicalPathOfExisting } from "../physical-path.js";
|
|
8
|
-
import { resolveKey, violationDetails, violationText, withinAnyRoot } from "../../tools/fs/safety.js";
|
|
8
|
+
import { pathFamilyOf, resolveKey, violationDetails, violationText, withinAnyRoot } from "../../tools/fs/safety.js";
|
|
9
9
|
import { createArtifactTool } from "../../tools/artifact/artifact-tool.js";
|
|
10
10
|
export function prepareArtifact(input) {
|
|
11
|
-
const { deps, runId, taskId, sessionId, agentName, executionEnv, rootCanonical, additionalRootsCanonical, readDenyMatcher, resolvedReadFace, handsCwdRef, roster, firstPartyOffload, envHandToolNames } = input;
|
|
11
|
+
const { deps, runId, taskId, sessionId, agentName, onAsk, executionEnv, rootCanonical, additionalRootsCanonical, readDenyMatcher, resolvedReadFace, handsCwdRef, roster, firstPartyOffload, envHandToolNames } = input;
|
|
12
12
|
const host = deps.artifactHost;
|
|
13
13
|
if (host === undefined)
|
|
14
14
|
return { artifactPlane: undefined };
|
|
@@ -26,7 +26,8 @@ export function prepareArtifact(input) {
|
|
|
26
26
|
const storageDir = verdict.capabilities.storageDir;
|
|
27
27
|
if (storageDir !== undefined && rootCanonical !== undefined) {
|
|
28
28
|
const real = physicalPathOfExisting(storageDir);
|
|
29
|
-
const
|
|
29
|
+
const storeFamily = pathFamilyOf({ root: real });
|
|
30
|
+
const inside = [rootCanonical, ...additionalRootsCanonical].find((root) => pathFamilyOf({ root }) === storeFamily && withinAnyRoot([root], real));
|
|
30
31
|
if (inside !== undefined)
|
|
31
32
|
return refuse(`the local artifact store ${storageDir} lies inside a root this run may reach through its file tools (${inside}); put the store outside every task root and additional directory`);
|
|
32
33
|
}
|
|
@@ -56,7 +57,7 @@ export function prepareArtifact(input) {
|
|
|
56
57
|
return { ok: false, reason: "too_large", size: r.value.byteLength, limit: maxBytes, judgedOn: "bytes" };
|
|
57
58
|
return { ok: true, key: rk.key, bytes: r.value };
|
|
58
59
|
};
|
|
59
|
-
const artifactSpec = createArtifactTool({ host, capabilities: verdict.capabilities, runId, taskId, sessionId, ...(agentName !== undefined ? { agentName } : {}), readLocalFile });
|
|
60
|
+
const artifactSpec = createArtifactTool({ host, capabilities: verdict.capabilities, runId, taskId, sessionId, ...(agentName !== undefined ? { agentName } : {}), readLocalFile, ...(onAsk !== undefined ? { onAsk } : {}) });
|
|
60
61
|
roster.mount(firstPartyOffload(defineTool(artifactSpec)), { source: "builtin", mountedBy: "optional" });
|
|
61
62
|
envHandToolNames.add(ARTIFACT_TOOL_NAME);
|
|
62
63
|
return { artifactPlane: verdict.capabilities.plane };
|
|
@@ -33,7 +33,7 @@ import type { OnAsk } from "../tool-policy.js";
|
|
|
33
33
|
import type { RunnerDeps, RuntimeCaps, TaskEvent, TaskSpec, ToolExecuteContext } from "../types.js";
|
|
34
34
|
import type { AutoModeArmFact } from "../wiring-manifest.js";
|
|
35
35
|
import type { AssertAllKeysHandled } from "../ask-origin.js";
|
|
36
|
-
import type {
|
|
36
|
+
import type { ContainmentFace } from "../../tools/fs/index.js";
|
|
37
37
|
import type { InheritedGate, Prepared, RunInternals, RunnerSelfSeat, ToolFaceSnapshot } from "./contracts.js";
|
|
38
38
|
import { type BlockedRef } from "./synthetic-tools.js";
|
|
39
39
|
import { RosterBuilder } from "../tool-roster.js";
|
|
@@ -91,7 +91,9 @@ export interface PrepareCapsAndWorkflowInput {
|
|
|
91
91
|
frozenOnAsk: OnAsk | undefined;
|
|
92
92
|
/** borrowed-readonly — the parent SPEC seat the children inherit (minted once by the inherited-gate phase; the Workflow mount's dep, the same value the delegation ctx carries). */ parentSpecSeat: ParentSpecSeat;
|
|
93
93
|
/** borrowed-readonly — the ONE read-face carrier expression (the Workflow mount's parentReadFace getter calls it). */
|
|
94
|
-
carrierReadFace: () =>
|
|
94
|
+
carrierReadFace: () => ContainmentFace | undefined;
|
|
95
|
+
/** borrowed-readonly — the same carrier expression on the WRITE seat. */
|
|
96
|
+
carrierWriteFace: () => ContainmentFace | undefined;
|
|
95
97
|
/** borrowed-readonly — the filtered display-forwarding wrapper (the Workflow mount's forwardEvent; absent ⇒ no key). */
|
|
96
98
|
forwardEvent: ((e: TaskEvent) => void) | undefined;
|
|
97
99
|
/** borrowed-readonly — the child-chain assembly (the Workflow mount's inheritedGateForChildren). */
|