@sema-agent/core 7.28.1 → 7.30.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 +473 -1
- package/dist/agents/child-model-seat.d.ts +59 -7
- package/dist/agents/child-model-seat.js +33 -7
- package/dist/agents/cross-session-judge.d.ts +27 -5
- package/dist/agents/cross-session-judge.js +23 -12
- package/dist/agents/list-agents-tool.d.ts +31 -6
- package/dist/agents/list-agents-tool.js +66 -14
- package/dist/agents/peer-admission.d.ts +20 -8
- package/dist/agents/peer-admission.js +21 -12
- package/dist/agents/peer-directory.d.ts +29 -0
- package/dist/agents/peer-directory.js +20 -0
- package/dist/agents/peer-held-queue.d.ts +31 -4
- package/dist/agents/peer-held-queue.js +32 -1
- package/dist/agents/peer-session-drain.d.ts +91 -13
- package/dist/agents/peer-session-drain.js +409 -102
- package/dist/agents/read-inbox-tool.d.ts +22 -0
- package/dist/agents/read-inbox-tool.js +39 -0
- package/dist/agents/roster-store.d.ts +2 -4
- package/dist/agents/roster-store.js +1 -1
- package/dist/agents/send-message-tool.d.ts +4 -4
- package/dist/agents/send-message-tool.js +22 -2
- package/dist/agents/subagent.js +62 -44
- package/dist/agents/team-role.d.ts +34 -0
- package/dist/agents/team-role.js +17 -0
- package/dist/agents/verify.js +2 -2
- package/dist/brain/anthropic.js +4 -2
- package/dist/brain/open-responses.js +4 -3
- package/dist/brain/openai.js +2 -1
- package/dist/brain/stream-engine.js +6 -0
- package/dist/brain/stream-shared.d.ts +8 -0
- package/dist/brain/stream-shared.js +8 -0
- package/dist/core/auto-compaction.d.ts +42 -0
- package/dist/core/auto-compaction.js +35 -2
- package/dist/core/child-clamp-seats.d.ts +11 -0
- package/dist/core/child-clamp-seats.js +3 -0
- package/dist/core/context-edit.d.ts +3 -2
- package/dist/core/context-edit.js +6 -5
- package/dist/core/context-guard.d.ts +2 -1
- package/dist/core/context-guard.js +3 -2
- package/dist/core/context-settings.d.ts +103 -0
- package/dist/core/context-settings.js +60 -0
- package/dist/core/engine-notice.d.ts +18 -1
- package/dist/core/frame-settlement.d.ts +97 -0
- package/dist/core/frame-settlement.js +36 -0
- package/dist/core/governance-codes.d.ts +1 -1
- package/dist/core/governance-codes.js +4 -0
- package/dist/core/hooks.d.ts +10 -10
- package/dist/core/mailbox-store.d.ts +41 -7
- package/dist/core/mailbox-store.js +9 -3
- package/dist/core/message-utils.d.ts +13 -0
- package/dist/core/message-utils.js +6 -0
- package/dist/core/parent-spec-seat.d.ts +14 -1
- package/dist/core/parent-spec-seat.js +6 -0
- package/dist/core/permission-rule-model.js +1 -1
- package/dist/core/roles.d.ts +20 -0
- package/dist/core/roles.js +35 -16
- package/dist/core/runner/assemble-result.d.ts +2 -0
- package/dist/core/runner/assemble-result.js +3 -1
- package/dist/core/runner/compaction-call-options.d.ts +26 -2
- package/dist/core/runner/compaction-call-options.js +9 -0
- package/dist/core/runner/compaction-seams.d.ts +4 -3
- package/dist/core/runner/compaction-seams.js +13 -6
- package/dist/core/runner/contracts.d.ts +61 -7
- package/dist/core/runner/decide-continuation.d.ts +2 -1
- package/dist/core/runner/decide-continuation.js +11 -10
- package/dist/core/runner/initial-run-state.js +2 -2
- package/dist/core/runner/prepare-caps-and-workflow.js +9 -1
- package/dist/core/runner/prepare-config-doors.js +4 -1
- package/dist/core/runner/prepare-context-lane.d.ts +9 -2
- package/dist/core/runner/prepare-context-lane.js +15 -10
- package/dist/core/runner/prepare-delegation-surface.d.ts +3 -2
- package/dist/core/runner/prepare-delegation-surface.js +9 -3
- package/dist/core/runner/prepare-file-history.js +10 -8
- package/dist/core/runner/prepare-gate-stations.d.ts +4 -0
- package/dist/core/runner/prepare-gate-stations.js +3 -1
- package/dist/core/runner/prepare-inherited-gate.js +1 -0
- package/dist/core/runner/prepare-prompt-inputs.d.ts +6 -0
- package/dist/core/runner/prepare-prompt-inputs.js +2 -1
- package/dist/core/runner/prepare-task.js +22 -9
- package/dist/core/runner/prepare-wiring-manifest.d.ts +6 -1
- package/dist/core/runner/prepare-wiring-manifest.js +6 -1
- package/dist/core/runner/response-model-seat.d.ts +16 -0
- package/dist/core/runner/response-model-seat.js +27 -0
- package/dist/core/runner/retired-runner-deps.d.ts +3 -1
- package/dist/core/runner/retired-runner-deps.js +1 -0
- package/dist/core/runner/run-compaction-machinery.d.ts +4 -2
- package/dist/core/runner/run-compaction-machinery.js +5 -3
- package/dist/core/runner/run-harness-handlers.js +9 -2
- package/dist/core/runner/run-identity-wiring.js +19 -1
- package/dist/core/runner/run-leg.js +1 -1
- package/dist/core/runner/run-notification-lane.js +8 -2
- package/dist/core/runner/run-recovery-lanes.js +9 -5
- package/dist/core/runner/run-settle-and-teardown.d.ts +4 -22
- package/dist/core/runner/run-settle-and-teardown.js +6 -10
- package/dist/core/runner/run-stop-and-final-verify.js +1 -0
- package/dist/core/runner/run-terminal-adoption.js +5 -5
- package/dist/core/runner/run-turn-boundary.js +32 -5
- package/dist/core/runner/runtask.d.ts +8 -1
- package/dist/core/runner/runtask.js +33 -2
- package/dist/core/runner-deps.d.ts +44 -13
- package/dist/core/side-query.d.ts +6 -5
- package/dist/core/side-query.js +3 -1
- package/dist/core/swappable-deps.d.ts +20 -4
- package/dist/core/swappable-deps.js +6 -1
- package/dist/core/task-event.d.ts +13 -0
- package/dist/core/task-notification.d.ts +144 -2
- package/dist/core/task-notification.js +154 -5
- package/dist/core/task-result.d.ts +27 -3
- package/dist/core/task-spec.d.ts +7 -0
- package/dist/core/tool-catalog-entries.js +2 -1
- package/dist/core/tool-conformance.js +1 -0
- package/dist/core/tool-policy.d.ts +9 -4
- package/dist/core/tool-spec.d.ts +14 -0
- package/dist/core/wiring-manifest.d.ts +12 -0
- package/dist/core/wiring-manifest.js +5 -1
- package/dist/engine/harness/agent-harness.d.ts +14 -1
- package/dist/engine/harness/agent-harness.js +28 -3
- package/dist/engine/harness/types.d.ts +3 -1
- package/dist/engine/llm/types.d.ts +8 -7
- package/dist/engine/llm/types.js +1 -1
- package/dist/engine/session/import-validate.js +4 -0
- package/dist/index.d.ts +10 -7
- package/dist/index.js +7 -5
- package/dist/orchestration/run-workflow-tool.d.ts +4 -4
- package/dist/orchestration/workflow.d.ts +1 -1
- package/dist/orchestration/workflow.js +8 -5
- package/dist/shell/lex.d.ts +30 -51
- package/dist/shell/lex.js +237 -284
- package/dist/stores/cc/mailbox-store.js +3 -2
- package/dist/stores/file/corrupt-read-sink.d.ts +28 -2
- package/dist/stores/file/file-history-store.d.ts +2 -4
- package/dist/stores/file/file-history-store.js +1 -1
- package/dist/stores/file/fs-atomic.d.ts +16 -11
- package/dist/stores/file/fs-atomic.js +17 -6
- package/dist/stores/file/index.d.ts +5 -6
- package/dist/stores/file/index.js +1 -1
- package/dist/stores/file/mailbox-store.d.ts +4 -9
- package/dist/stores/file/mailbox-store.js +29 -30
- package/dist/stores/file/session-policy-store.d.ts +2 -0
- package/dist/stores/file/session-policy-store.js +1 -1
- package/dist/stores/file/session-store.d.ts +11 -7
- package/dist/stores/file/session-store.js +23 -16
- package/dist/stores/file/shared-ledger.js +3 -14
- package/dist/stores/file/tool-result-store.d.ts +2 -0
- package/dist/stores/file/tool-result-store.js +5 -2
- package/dist/stores/file/workflow-journal-store.js +14 -9
- package/dist/tools/fs/bash-program-position.d.ts +15 -24
- package/dist/tools/fs/bash-program-position.js +11 -29
- package/dist/tools/fs/bash-readonly-classifier.d.ts +125 -117
- package/dist/tools/fs/bash-readonly-classifier.js +400 -272
- package/dist/tools/fs/shell-read-check.js +3 -3
- package/dist/tools/fs/shell-read-establish.js +1 -1
- package/dist/tools/fs/shell-read-plan.d.ts +11 -6
- package/dist/tools/fs/shell-read-plan.js +167 -183
- package/dist/tools/fs/shell-read-word.d.ts +50 -0
- package/dist/tools/fs/shell-read-word.js +59 -0
- package/dist/tools/fs/shell-reentry-text.d.ts +10 -0
- package/dist/tools/fs/shell-reentry-text.js +73 -0
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +58 -8
- package/wire-changes/core@7.29.0.json +185 -0
- package/wire-changes/core@7.30.0.json +1821 -0
|
@@ -119,7 +119,7 @@ export type NoticeAudience = "user" | "operator";
|
|
|
119
119
|
* the union's catalog ({@link EngineNotice.code}). The groups below say only which family a row joins,
|
|
120
120
|
* so the catalog is not written a second time here and the two cannot drift.
|
|
121
121
|
*/
|
|
122
|
-
export declare const ENGINE_NOTICE_CODES: readonly ["config.autocompact_window_clamped", "config.env_timeout_discarded", "config.materialize_env_discarded", "config.models_swapped", "config.read_face_swapped", "config.read_face_deployment_clamped", "config.write_face_swapped", "config.write_face_deployment_clamped", "config.tool_model_gate_removed", "config.tool_model_gate_unknown_class", "config.tool_model_gate_env_invalid", "config.artifact_host_invalid", "config.task_root_not_canonical", "config.write_protection_target_view_absent", "config.write_protection_unresolved", "config.execution_env_capability_invalid", "config.tool_card_undeclared", "config.tool_face_undeclared", "config.tool_face_invalid", "config.forward_sink_failed", "config.durable_gate_unavailable", "config.peer_admission_out_of_range", "config.peer_lane_unmounted", "peer.inbound_disposition", "peer.held_settled", "peer.idle_subscription", "classifier.denial_limit", "classifier.fallback", "classifier.role_defaulted", "classifier.contract_broken", "checkpoint.execution_outcome_unrecorded", "delegation.transcript_integrity", "delegation.ask_unresolvable", "mcp.injection_dropped", "mcp.revocation_probe_failed", "mcp.server_redialed", "workflow.governance_key_stripped", "workflow.agent_option_ignored", "memory.session_polluted", "memory.harvest_quarantined", "memory.delegation_static_mark_waived", "memory.content_class_declared", "memory.hold_opened", "memory.hold_released", "memory.hold_disposed", "memory.consolidation_recommended", "memory.consolidation_committed", "memory.consolidation_conflict", "memory.consolidation_incomplete", "memory.consolidation_refused", "memory.consolidation_withheld", "memory.restore_withheld", "memory.project_scope_absent", "route.fallback_to_primary", "route.classifier_unroutable", "route.base_url_changed_key_unchanged", "task.user_steer_undrained", "task.user_followup_undrained", "steering.parked_input_blocked", "task.turn_interrupted", "task.halt_unconsumed", "task.late_approval", "task.interrupt_unconsumed", "memory.capture_opted_out", "memory.capture_optout_unpersisted", "skills.listing_truncated", "agents.listing_truncated", "tool_result.offload_put_failed"];
|
|
122
|
+
export declare const ENGINE_NOTICE_CODES: readonly ["config.autocompact_window_clamped", "config.env_timeout_discarded", "config.materialize_env_discarded", "config.models_swapped", "config.context_settings_swapped", "config.read_face_swapped", "config.read_face_deployment_clamped", "config.write_face_swapped", "config.write_face_deployment_clamped", "config.tool_model_gate_removed", "config.tool_model_gate_unknown_class", "config.tool_model_gate_env_invalid", "config.artifact_host_invalid", "config.task_root_not_canonical", "config.write_protection_target_view_absent", "config.write_protection_unresolved", "config.execution_env_capability_invalid", "config.tool_card_undeclared", "config.tool_face_undeclared", "config.tool_face_invalid", "config.forward_sink_failed", "config.durable_gate_unavailable", "config.peer_admission_out_of_range", "config.peer_lane_unmounted", "peer.inbound_disposition", "peer.held_settled", "peer.idle_subscription", "classifier.denial_limit", "classifier.fallback", "classifier.role_defaulted", "classifier.contract_broken", "checkpoint.execution_outcome_unrecorded", "delegation.transcript_integrity", "delegation.ask_unresolvable", "mcp.injection_dropped", "mcp.revocation_probe_failed", "mcp.server_redialed", "workflow.governance_key_stripped", "workflow.agent_option_ignored", "memory.session_polluted", "memory.harvest_quarantined", "memory.delegation_static_mark_waived", "memory.content_class_declared", "memory.hold_opened", "memory.hold_released", "memory.hold_disposed", "memory.consolidation_recommended", "memory.consolidation_committed", "memory.consolidation_conflict", "memory.consolidation_incomplete", "memory.consolidation_refused", "memory.consolidation_withheld", "memory.restore_withheld", "memory.project_scope_absent", "route.fallback_to_primary", "route.classifier_unroutable", "route.base_url_changed_key_unchanged", "route.response_model_mismatch", "task.user_steer_undrained", "task.user_followup_undrained", "steering.parked_input_blocked", "task.turn_interrupted", "task.halt_unconsumed", "task.late_approval", "task.interrupt_unconsumed", "memory.capture_opted_out", "memory.capture_optout_unpersisted", "skills.listing_truncated", "agents.listing_truncated", "tool_result.offload_put_failed"];
|
|
123
123
|
/** A code this engine mints (see {@link ENGINE_NOTICE_CODES}). NOT the type of
|
|
124
124
|
* `EngineNotice.code`, which stays `string` — a host forwarding its own notices through the same
|
|
125
125
|
* sink is a supported shape, and narrowing that field would break it. */
|
|
@@ -105,6 +105,7 @@ export const ENGINE_NOTICE_CODES = [
|
|
|
105
105
|
"config.env_timeout_discarded",
|
|
106
106
|
"config.materialize_env_discarded",
|
|
107
107
|
"config.models_swapped",
|
|
108
|
+
"config.context_settings_swapped",
|
|
108
109
|
"config.read_face_swapped",
|
|
109
110
|
"config.read_face_deployment_clamped",
|
|
110
111
|
"config.write_face_swapped",
|
|
@@ -157,6 +158,7 @@ export const ENGINE_NOTICE_CODES = [
|
|
|
157
158
|
"route.fallback_to_primary",
|
|
158
159
|
"route.classifier_unroutable",
|
|
159
160
|
"route.base_url_changed_key_unchanged",
|
|
161
|
+
"route.response_model_mismatch",
|
|
160
162
|
"task.user_steer_undrained",
|
|
161
163
|
"task.user_followup_undrained",
|
|
162
164
|
"steering.parked_input_blocked",
|
|
@@ -197,6 +199,7 @@ const NOTICE_AUDIENCE_TABLE = {
|
|
|
197
199
|
"config.env_timeout_discarded": "operator",
|
|
198
200
|
"config.materialize_env_discarded": "operator",
|
|
199
201
|
"config.models_swapped": "operator",
|
|
202
|
+
"config.context_settings_swapped": "operator",
|
|
200
203
|
"config.read_face_swapped": "operator",
|
|
201
204
|
"config.read_face_deployment_clamped": "operator",
|
|
202
205
|
"config.write_face_swapped": "operator",
|
|
@@ -235,6 +238,7 @@ const NOTICE_AUDIENCE_TABLE = {
|
|
|
235
238
|
"route.fallback_to_primary": "operator",
|
|
236
239
|
"route.classifier_unroutable": "operator",
|
|
237
240
|
"route.base_url_changed_key_unchanged": "operator",
|
|
241
|
+
"route.response_model_mismatch": "operator",
|
|
238
242
|
"skills.listing_truncated": "operator",
|
|
239
243
|
"agents.listing_truncated": "operator",
|
|
240
244
|
"tool_result.offload_put_failed": "operator",
|
package/dist/core/hooks.d.ts
CHANGED
|
@@ -540,16 +540,8 @@ export interface StopHookContext {
|
|
|
540
540
|
/** Consecutive blocks so far in this run (resets when a stop() call lets the run proceed). */
|
|
541
541
|
consecutiveBlocks: number;
|
|
542
542
|
/**
|
|
543
|
-
* A view of the conversation this hook is being asked to judge
|
|
544
|
-
*
|
|
545
|
-
* Until now a Stop hook received two counters and nothing else. That is enough for a hook that asks
|
|
546
|
-
* "have I blocked too often?", and structurally insufficient for the entire class of hooks that ask
|
|
547
|
-
* anything ABOUT THE RUN — which is what the prompt-shaped hooks a settings file can declare are for.
|
|
548
|
-
* Measured end to end downstream: the carrier was constructed, invoked, and returned without error, and
|
|
549
|
-
* the model answered "I don't have direct access to your file system…" — because nobody had given the
|
|
550
|
-
* evaluator the session. Every layer reported success and the feature could not work. A hook cannot
|
|
551
|
-
* judge a run it cannot see, so the gap belonged here rather than in any of the layers that were
|
|
552
|
-
* dutifully passing an empty view along.
|
|
543
|
+
* A view of the conversation this hook is being asked to judge — the counters alone cannot serve a
|
|
544
|
+
* hook that asks anything ABOUT THE RUN (the prompt-shaped hooks a settings file declares).
|
|
553
545
|
*
|
|
554
546
|
* LAZY on purpose: a hook that only reads the counters pays nothing, and a hook that needs the
|
|
555
547
|
* conversation gets it in full rather than through a truncation policy the engine would have to invent
|
|
@@ -559,6 +551,14 @@ export interface StopHookContext {
|
|
|
559
551
|
* Absent when the runner cannot supply one (a stop gate wired without a session).
|
|
560
552
|
*/
|
|
561
553
|
getBranch?: () => Promise<readonly SessionTreeEntry[]>;
|
|
554
|
+
/** The run's background tasks still in flight (pending / running / parked under this run's owner triple — the
|
|
555
|
+
* post-compaction reminder's projection; CC `SubagentStop.background_tasks`). LAZY like {@link getBranch};
|
|
556
|
+
* `[]` = none in that projection, ABSENT = no view. A parked child here waits on an approval out of band. */
|
|
557
|
+
backgroundTasks?: () => ReadonlyArray<{
|
|
558
|
+
id: string;
|
|
559
|
+
description?: string;
|
|
560
|
+
status: string;
|
|
561
|
+
}>;
|
|
562
562
|
/** {@link HookSeatSignal} — this invocation's own abort signal. */
|
|
563
563
|
signal?: AbortSignal;
|
|
564
564
|
}
|
|
@@ -44,6 +44,10 @@ export interface MailboxPeerMeta {
|
|
|
44
44
|
* — a delivery receipt's state (`kind: "delivery_notice"`) or an idle notice's kind
|
|
45
45
|
* (`kind: "idle_notice"`). The record's `content` is a human-readable mirror; THIS is the authority. */
|
|
46
46
|
notice?: MailboxPeerNoticeMeta;
|
|
47
|
+
/** `peer_message` only — the SENDER marked it urgent. The recipient's drain presents an urgent record
|
|
48
|
+
* ahead of the non-urgent ones of the same pass and counts it on the inbox notice; it never changes
|
|
49
|
+
* WHEN the recipient reads (the recipient's setting decides that, never the sender). */
|
|
50
|
+
urgent?: boolean;
|
|
47
51
|
}
|
|
48
52
|
/** design/385 — the typed body of a notice-kind record. `state` is drawn from the receipt states on a
|
|
49
53
|
* `delivery_notice` (`held|denied|expired|delivered|refused|dropped`) and from the idle kinds on an
|
|
@@ -67,10 +71,23 @@ export interface MailboxPeerNoticeMeta {
|
|
|
67
71
|
}
|
|
68
72
|
export declare const MAILBOX_PEER_FROM_MODES: readonly ["bypass", "prompting"];
|
|
69
73
|
export type MailboxPeerFromMode = (typeof MAILBOX_PEER_FROM_MODES)[number];
|
|
70
|
-
/**
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
|
|
74
|
+
/**
|
|
75
|
+
* design/385: `peer_message` runs the parity judgment; `idle_subscription` registers the sender as an
|
|
76
|
+
* idle subscriber of the box owner (§5.2, the store form of CC's `notify_when_idle` frame);
|
|
77
|
+
* `idle_notice` / `delivery_notice` land on the notice face (rendered, never enveloped as a peer's words).
|
|
78
|
+
*
|
|
79
|
+
* @contract mailbox.engine_frame_is_not_a_peers_words — an `engine_frame` record is the ENGINE's own
|
|
80
|
+
* task-notification frame parked durably (a child's terminal, a monitor batch, a workflow settle),
|
|
81
|
+
* written by the park seat of the session that owns the box and only over a deployment that vouches
|
|
82
|
+
* every drainer of the box reads the codec (`RunnerDeps.sessionBoxEngineFrames`). Its `content` is the
|
|
83
|
+
* versioned codec text (`encodeEngineFrame`), never a message: the drain decodes it and re-injects the
|
|
84
|
+
* frame through the engine's own notification lane — no parity judgment, no admission gate, no peer
|
|
85
|
+
* envelope — and a codec version this engine does not read STOPS the drain at that record (the box
|
|
86
|
+
* waits for an engine that reads it) rather than rendering engine speech as a peer's words. Only the
|
|
87
|
+
* engine's own lanes (`background_agent` / `monitor` / `workflow`) may be parked this way; an
|
|
88
|
+
* `external` event never is.
|
|
89
|
+
*/
|
|
90
|
+
export declare const MAILBOX_PEER_RECORD_KINDS: readonly ["peer_message", "idle_notice", "delivery_notice", "idle_subscription", "engine_frame"];
|
|
74
91
|
export type MailboxPeerRecordKind = (typeof MAILBOX_PEER_RECORD_KINDS)[number];
|
|
75
92
|
/** The `notice.state` vocabulary the validator admits (the union of both notice families; the drain
|
|
76
93
|
* checks the kind↔state pairing). Spelled here, beside the record kinds, so the store contract is
|
|
@@ -183,7 +200,7 @@ export interface MailboxAppendMessage {
|
|
|
183
200
|
* persists it and hands it back on `claimLease` byte-for-byte (a detached copy), absent stays absent,
|
|
184
201
|
* and a malformed value is REFUSED with `"mailbox.invalid_peer_meta"` before any side effect
|
|
185
202
|
* (`readMailboxPeerMeta` is the one validator; bundled backends call it first). T1 cases in
|
|
186
|
-
* `mailboxStoreContract` cover the
|
|
203
|
+
* `mailboxStoreContract` cover the pass trip, the refusal and the aliasing.
|
|
187
204
|
* - CROSS-PROCESS SAFETY (design/385, optional capability declared on the store): the session-box
|
|
188
205
|
* drain shares ONE box between several OS processes. A backend that is correct under that sharing —
|
|
189
206
|
* seq minted once across processes, lease/ack never crossing, one process's housekeeping never
|
|
@@ -192,6 +209,12 @@ export interface MailboxAppendMessage {
|
|
|
192
209
|
* that does not declare it is refused by the lane at mount time with a named reason
|
|
193
210
|
* ({@link mailboxCrossProcessMountVerdict}) — never silently mounted on luck.
|
|
194
211
|
*/
|
|
212
|
+
/** #1036 — what {@link MailboxStore.ack} answers: did this call actually consume the prefix?
|
|
213
|
+
* `false` = the lease fence refused it (this caller does not hold the box), so nothing was deleted
|
|
214
|
+
* and the records are still parked for whoever does. */
|
|
215
|
+
export interface MailboxAckResult {
|
|
216
|
+
readonly acked: boolean;
|
|
217
|
+
}
|
|
195
218
|
export interface MailboxStore {
|
|
196
219
|
/** design/385 — cross-process safety declaration (see the interface notes). `true` = this backend
|
|
197
220
|
* passed `mailboxCrossProcessContract` over real OS processes; `false` = deliberately process-local
|
|
@@ -202,7 +225,18 @@ export interface MailboxStore {
|
|
|
202
225
|
* that its recipient is in the deployment's pre-delete state (see the interface notes above). */
|
|
203
226
|
append(scope: string, handle: string, msg: MailboxAppendMessage): Promise<number>;
|
|
204
227
|
claimLease(scope: string, handle: string, owner: string, ttlMs: number, now?: number): Promise<MailboxLease | null>;
|
|
205
|
-
|
|
228
|
+
/**
|
|
229
|
+
* Consume the leased prefix up to `upToSeq` (the records are DELETED). Fenced by the current lease:
|
|
230
|
+
* a caller that does not hold it consumes nothing.
|
|
231
|
+
*
|
|
232
|
+
* #1036 — the fence's verdict is REPORTED (`{acked:false}`), not silent. The fence itself is
|
|
233
|
+
* older (MB1): it exists so a stale owner whose lease a live rival already took over cannot delete
|
|
234
|
+
* the rival's messages. What was missing is that a no-op and a success were the same `void` to the
|
|
235
|
+
* caller, so a drain that had lost its lease would clear its own watermark as if the store had
|
|
236
|
+
* obeyed it — the one shape where "the records stay parked" turns into "the records are forgotten".
|
|
237
|
+
* Implementations report `{acked:true}` only when they actually consumed the prefix.
|
|
238
|
+
*/
|
|
239
|
+
ack(scope: string, handle: string, owner: string, upToSeq: number): Promise<MailboxAckResult>;
|
|
206
240
|
releaseLease(scope: string, handle: string, owner: string): Promise<void>;
|
|
207
241
|
/** Observation face (the "N pending" receipt figure) — count of currently-visible messages. */
|
|
208
242
|
peekCount(scope: string, handle: string): Promise<number>;
|
|
@@ -253,7 +287,7 @@ export declare class InMemoryMailboxStore implements MailboxStore {
|
|
|
253
287
|
private box;
|
|
254
288
|
append(scope: string, handle: string, msg: MailboxAppendMessage): Promise<number>;
|
|
255
289
|
claimLease(scope: string, handle: string, owner: string, ttlMs: number, now?: number): Promise<MailboxLease | null>;
|
|
256
|
-
ack(scope: string, handle: string, owner: string, upToSeq: number): Promise<
|
|
290
|
+
ack(scope: string, handle: string, owner: string, upToSeq: number): Promise<MailboxAckResult>;
|
|
257
291
|
releaseLease(scope: string, handle: string, owner: string): Promise<void>;
|
|
258
292
|
peekCount(scope: string, handle: string): Promise<number>;
|
|
259
293
|
drop(scope: string, handle: string): Promise<void>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { assertRetentionPolicy } from "./retention-policy.js";
|
|
2
2
|
export const MAILBOX_PEER_FROM_MODES = ["bypass", "prompting"];
|
|
3
|
-
export const MAILBOX_PEER_RECORD_KINDS = ["peer_message", "idle_notice", "delivery_notice", "idle_subscription"];
|
|
3
|
+
export const MAILBOX_PEER_RECORD_KINDS = ["peer_message", "idle_notice", "delivery_notice", "idle_subscription", "engine_frame"];
|
|
4
4
|
export const MAILBOX_PEER_NOTICE_STATES = ["held", "denied", "expired", "delivered", "refused", "dropped", "idle", "exited", "unavailable"];
|
|
5
5
|
export const MAILBOX_INVALID_PEER_META_CODE = "mailbox.invalid_peer_meta";
|
|
6
6
|
export const MAILBOX_CROSS_PROCESS_UNSAFE_CODE = "mailbox.cross_process_unsafe";
|
|
@@ -42,6 +42,11 @@ export function readMailboxPeerMeta(raw) {
|
|
|
42
42
|
else if (k === "notice") {
|
|
43
43
|
out.notice = readMailboxPeerNoticeMeta(v, refuse);
|
|
44
44
|
}
|
|
45
|
+
else if (k === "urgent") {
|
|
46
|
+
if (typeof v !== "boolean")
|
|
47
|
+
return refuse("urgent must be a boolean");
|
|
48
|
+
out.urgent = v;
|
|
49
|
+
}
|
|
45
50
|
else {
|
|
46
51
|
return refuse(`carries an unknown key ${JSON.stringify(k)}`);
|
|
47
52
|
}
|
|
@@ -171,12 +176,13 @@ export class InMemoryMailboxStore {
|
|
|
171
176
|
async ack(scope, handle, owner, upToSeq) {
|
|
172
177
|
const b = this.boxes.get(this.key(scope, handle));
|
|
173
178
|
if (!b)
|
|
174
|
-
return;
|
|
179
|
+
return { acked: false };
|
|
175
180
|
if (b.lease === undefined || b.lease.owner !== owner)
|
|
176
|
-
return;
|
|
181
|
+
return { acked: false };
|
|
177
182
|
b.messages = b.messages.filter((m) => m.seq > upToSeq);
|
|
178
183
|
if (b.lease.maxSeq <= upToSeq)
|
|
179
184
|
b.lease = undefined;
|
|
185
|
+
return { acked: true };
|
|
180
186
|
}
|
|
181
187
|
async releaseLease(scope, handle, owner) {
|
|
182
188
|
const b = this.boxes.get(this.key(scope, handle));
|
|
@@ -3,6 +3,19 @@ import type { ToolResultMessage } from "../internal/llm.js";
|
|
|
3
3
|
/** Read the (possibly absent) `role` off an AgentMessage defensively — the union doesn't expose it
|
|
4
4
|
* uniformly, so context passes narrow by role with a single cast in one place. */
|
|
5
5
|
export declare function messageRole(m: AgentMessage): string | undefined;
|
|
6
|
+
/**
|
|
7
|
+
* The PROVIDER's own name for the model that answered this assistant message, when it differs from the
|
|
8
|
+
* ENGINE-SELECTED id the message is stamped with — the ONE read of `AssistantMessage.responseModel`, so
|
|
9
|
+
* the per-turn frame, the result face, the side-query projection and the mismatch notice admit it on the
|
|
10
|
+
* same terms rather than on four. The READ twin of `noteResponseModel` (`brain/stream-shared.ts`), which
|
|
11
|
+
* states the same law on the write side; this side re-tests rather than trusting, because a HOST's own
|
|
12
|
+
* Brain mints these messages too. `undefined` ⇒ the provider named the stamped id, named nothing, or
|
|
13
|
+
* named something unreadable — ONE reading, never three.
|
|
14
|
+
*/
|
|
15
|
+
export declare function providerReportedModel(m: {
|
|
16
|
+
readonly model?: unknown;
|
|
17
|
+
readonly responseModel?: unknown;
|
|
18
|
+
}): string | undefined;
|
|
6
19
|
/** True if the message is a tool-result message (shared by the context-edit and context-guard passes).
|
|
7
20
|
* A TYPE PREDICATE, not a bare boolean: `role: "toolResult"` belongs to exactly one member of the
|
|
8
21
|
* union, so callers that check it can read `.content`/`.toolName` and rebuild the message directly
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
export function messageRole(m) {
|
|
2
2
|
return m.role;
|
|
3
3
|
}
|
|
4
|
+
export function providerReportedModel(m) {
|
|
5
|
+
const reported = m.responseModel;
|
|
6
|
+
if (typeof reported !== "string" || reported === "")
|
|
7
|
+
return undefined;
|
|
8
|
+
return reported === m.model ? undefined : reported;
|
|
9
|
+
}
|
|
4
10
|
export function isToolResult(m) {
|
|
5
11
|
return messageRole(m) === "toolResult";
|
|
6
12
|
}
|
|
@@ -13,7 +13,7 @@ import type { Hooks } from "./hooks.js";
|
|
|
13
13
|
import type { ToolExecuteContext } from "./tool-spec.js";
|
|
14
14
|
import type { TaskSpec } from "./types.js";
|
|
15
15
|
/** The fields of a spec the seat carries — one `Pick`, shared by the mint's input and the apply's target. */
|
|
16
|
-
export type ParentSpecSeatFields = Pick<TaskSpec, "principal" | "oneShot" | "clientContext" | "onQuestion" | "hooks" | "additionalDirectories" | "additionalReadDirectories" | "envFacts" | "memoryPersistenceCapable" | "memory">;
|
|
16
|
+
export type ParentSpecSeatFields = Pick<TaskSpec, "principal" | "oneShot" | "clientContext" | "onQuestion" | "hooks" | "additionalDirectories" | "additionalReadDirectories" | "envFacts" | "memoryPersistenceCapable" | "memory" | "compaction" | "skillListingBudgetFraction" | "skillListingMaxDescChars">;
|
|
17
17
|
/**
|
|
18
18
|
* The seat a run hands its children. Every member is the parent's own value, frozen at mint (arrays copied and
|
|
19
19
|
* frozen; the facts object copied). Members present iff the parent carries them — an absent member is "nothing to
|
|
@@ -33,6 +33,16 @@ export type ParentSpecSeatFields = Pick<TaskSpec, "principal" | "oneShot" | "cli
|
|
|
33
33
|
* `true` a default a child's own declaration may narrow.
|
|
34
34
|
* · `memoryOff` — the request-level "memory is off" FACT (`spec.memory.enabled === false`): a REMOVAL
|
|
35
35
|
* (floor) that REWRITES the child's own memory declaration instead of travelling as a value.
|
|
36
|
+
* · `compaction` — the request's CONTEXT-MANAGEMENT settings (enabled / withinTask / reserve / keepRecent /
|
|
37
|
+
* instructions / attachWorkingFiles / staleToolResultOffload / clampTolerance). One set of settings for the
|
|
38
|
+
* whole delegation tree, measured per leg against the leg's OWN window: the numbers are repaired against the
|
|
39
|
+
* child's model by the same sanitizer every leg runs (`sanitizeCompactionSettings`), so a parent's 128k-tuned
|
|
40
|
+
* reserve does not have to be re-tuned to be legal on a 6k child. `enabled: false` is NOT a removal — the
|
|
41
|
+
* reference implementation shares one settings object down the tree rather than flooring children, and a
|
|
42
|
+
* child that authors its own settings wins the whole object (an ORDINARY seat member).
|
|
43
|
+
* · `skillListingBudgetFraction` / `skillListingMaxDescChars` — the listing-budget knobs, the same shape for the
|
|
44
|
+
* same reason: a fraction the parent tuned is a fraction OF EACH LEG'S OWN window, so it travels as a number
|
|
45
|
+
* and every leg multiplies it by its own.
|
|
36
46
|
*/
|
|
37
47
|
export interface ParentSpecSeat {
|
|
38
48
|
readonly principal?: string;
|
|
@@ -45,6 +55,9 @@ export interface ParentSpecSeat {
|
|
|
45
55
|
readonly envFacts?: TaskSpec["envFacts"];
|
|
46
56
|
readonly memoryPersistenceCapable?: boolean;
|
|
47
57
|
readonly memoryOff?: true;
|
|
58
|
+
readonly compaction?: TaskSpec["compaction"];
|
|
59
|
+
readonly skillListingBudgetFraction?: number;
|
|
60
|
+
readonly skillListingMaxDescChars?: number;
|
|
48
61
|
}
|
|
49
62
|
/** Mint the seat off the parent's REBOUND spec and its frozen question face (spec over deps — the caller resolves it once,
|
|
50
63
|
* the same value the parent's own question routing reads). Absent fields stay absent; `oneShot` only when `true`. */
|
|
@@ -9,6 +9,9 @@ export function parentSpecSeatOf(spec, frozenOnQuestion) {
|
|
|
9
9
|
...(spec.additionalReadDirectories !== undefined ? { additionalReadDirectories: Object.freeze([...spec.additionalReadDirectories]) } : {}),
|
|
10
10
|
...(spec.envFacts !== undefined ? { envFacts: Object.freeze({ ...spec.envFacts }) } : {}),
|
|
11
11
|
...(spec.memoryPersistenceCapable !== undefined ? { memoryPersistenceCapable: spec.memoryPersistenceCapable } : {}),
|
|
12
|
+
...(spec.compaction !== undefined ? { compaction: Object.freeze({ ...spec.compaction }) } : {}),
|
|
13
|
+
...(spec.skillListingBudgetFraction !== undefined ? { skillListingBudgetFraction: spec.skillListingBudgetFraction } : {}),
|
|
14
|
+
...(spec.skillListingMaxDescChars !== undefined ? { skillListingMaxDescChars: spec.skillListingMaxDescChars } : {}),
|
|
12
15
|
...(spec.memory?.enabled === false ? { memoryOff: true } : {}),
|
|
13
16
|
});
|
|
14
17
|
}
|
|
@@ -25,6 +28,9 @@ export function applyParentSpecSeat(child, seat) {
|
|
|
25
28
|
...fill("additionalDirectories", (seat.additionalDirectories !== undefined ? [...seat.additionalDirectories] : undefined)),
|
|
26
29
|
...fill("additionalReadDirectories", (seat.additionalReadDirectories !== undefined ? [...seat.additionalReadDirectories] : undefined)),
|
|
27
30
|
...fill("envFacts", (seat.envFacts !== undefined ? { ...seat.envFacts } : undefined)),
|
|
31
|
+
...fill("compaction", (seat.compaction !== undefined ? { ...seat.compaction } : undefined)),
|
|
32
|
+
...fill("skillListingBudgetFraction", seat.skillListingBudgetFraction),
|
|
33
|
+
...fill("skillListingMaxDescChars", seat.skillListingMaxDescChars),
|
|
28
34
|
...(seat.oneShot === true ? { oneShot: true } : {}),
|
|
29
35
|
...(seat.memoryPersistenceCapable === false ? { memoryPersistenceCapable: false } : fill("memoryPersistenceCapable", seat.memoryPersistenceCapable)),
|
|
30
36
|
...(seat.memoryOff === true ? { memory: memoryTurnedOff(child.memory) } : {}),
|
|
@@ -263,7 +263,7 @@ export function parseRuleText(text, behavior) {
|
|
|
263
263
|
}
|
|
264
264
|
const shape = ruleLaneShapeOf(body, MATCH_READING);
|
|
265
265
|
if ("reject" in shape) {
|
|
266
|
-
if (body.trim() === "")
|
|
266
|
+
if (body.trim() === "" || shape.reject === "empty command")
|
|
267
267
|
return reject("invalid.empty_command", `rule "${text}" names no command`);
|
|
268
268
|
if (foldSpacing(body) === undefined)
|
|
269
269
|
return reject("invalid.unbalanced_quotes", `rule "${text}" has unbalanced quoting, so the command it names is not determinable`);
|
package/dist/core/roles.d.ts
CHANGED
|
@@ -37,6 +37,13 @@ export declare function resolveModelDisplayLabel(ref: string): string;
|
|
|
37
37
|
* `resolveModel("best")` refuses like any unknown ref.
|
|
38
38
|
*/
|
|
39
39
|
export declare function expandTiers(models: Record<string, Model> | undefined, tiers: Record<string, ModelRef> | undefined): Record<string, Model> | undefined;
|
|
40
|
+
/**
|
|
41
|
+
* The words a catalog word stands in for under the tier layer, nearest first — what {@link expandTiers}
|
|
42
|
+
* recorded for a key IT wrote: the tier words its resolution passed through and the binding's own catalog
|
|
43
|
+
* word, each naming the model the key resolves to. A deployment's own key (a shadowing one included), and any
|
|
44
|
+
* catalog that was not tier-expanded, stands in for nothing.
|
|
45
|
+
*/
|
|
46
|
+
export declare function tierAliasVia(word: string, models: Record<string, Model> | undefined): readonly string[];
|
|
40
47
|
export interface ModelMention {
|
|
41
48
|
/** The matched model name — always one of `allowedNames` — or undefined if none was found. */
|
|
42
49
|
model?: string;
|
|
@@ -98,6 +105,19 @@ export declare const ROLE_TIER_DEFAULTS: Readonly<Partial<Record<ModelRole, stri
|
|
|
98
105
|
export declare function roleSpecNamesModel(spec: RoleSpec | undefined): boolean;
|
|
99
106
|
export interface ResolvedRole {
|
|
100
107
|
model: Model;
|
|
108
|
+
/**
|
|
109
|
+
* WHICH REF CHOSE {@link model} — the word, when a word chose it (the explicit `spec.model` string, the
|
|
110
|
+
* role's own `model` ref, or the role→tier default's catalog key), else the `Model` object itself (an
|
|
111
|
+
* explicit object, or one a `select` criteria picked: neither has a catalog word).
|
|
112
|
+
*
|
|
113
|
+
* The distinction is load-bearing wherever a LATER tick must ask the catalog the same question again: a
|
|
114
|
+
* word re-resolves against the catalog generation in force at that tick (a hot swap redirects it, a
|
|
115
|
+
* retirement refuses it loudly), while an object is pinned and never re-resolved. The engine-spawned
|
|
116
|
+
* child that inherits its parent's model inherits THIS, not the resolved object, so it lands on the
|
|
117
|
+
* generation its own prepare sees rather than on the one its parent started under; the same word keys
|
|
118
|
+
* the deployment's per-model context-window overlay.
|
|
119
|
+
*/
|
|
120
|
+
modelRef: ModelRef;
|
|
101
121
|
/** The effective thinking level: explicit `spec.thinking` wins, else the role's default. */
|
|
102
122
|
thinking?: ThinkingLevel;
|
|
103
123
|
/** The requested role's preset system prompt, if it set one (independent of model fallback). */
|
package/dist/core/roles.js
CHANGED
|
@@ -22,59 +22,75 @@ export function resolveModelDisplayLabel(ref) {
|
|
|
22
22
|
return Object.hasOwn(CC_MODEL_TIER_ALIASES, ref) ? CC_MODEL_TIER_ALIASES[ref] : ref;
|
|
23
23
|
}
|
|
24
24
|
const TIERS_ACTIVE = Symbol.for("sema.tiersActive");
|
|
25
|
+
const TIER_ALIAS_VIA = Symbol.for("sema.tierAliasVia");
|
|
25
26
|
export function expandTiers(models, tiers) {
|
|
26
27
|
if (!tiers || Object.keys(tiers).length === 0)
|
|
27
28
|
return models;
|
|
28
29
|
const out = { ...models };
|
|
29
30
|
out[TIERS_ACTIVE] = true;
|
|
31
|
+
const aliasVia = new Map();
|
|
32
|
+
out[TIER_ALIAS_VIA] = aliasVia;
|
|
30
33
|
const bound = (name) => {
|
|
31
34
|
const b = Object.hasOwn(tiers, name) ? tiers[name] : undefined;
|
|
32
35
|
return b === undefined ? undefined : resolveModel(b, models);
|
|
33
36
|
};
|
|
34
|
-
const
|
|
35
|
-
|
|
37
|
+
const ownKey = (k) => models !== undefined && Object.hasOwn(models, k) && Boolean(models[k]);
|
|
38
|
+
const landing = (t, self) => {
|
|
39
|
+
const m = bound(t);
|
|
40
|
+
if (!m)
|
|
41
|
+
return undefined;
|
|
42
|
+
const b = Object.hasOwn(tiers, t) ? tiers[t] : undefined;
|
|
43
|
+
return { model: m, via: [...(t !== self && !ownKey(t) ? [t] : []), ...(typeof b === "string" ? [b] : [])] };
|
|
44
|
+
};
|
|
45
|
+
const resolveTier = (name, self = name) => {
|
|
46
|
+
const direct = landing(name, self);
|
|
36
47
|
if (direct)
|
|
37
48
|
return direct;
|
|
38
49
|
const at = DEFAULT_TIER_ORDER.indexOf(name);
|
|
39
50
|
if (at === -1)
|
|
40
51
|
return undefined;
|
|
41
52
|
for (let i = at + 1; i < DEFAULT_TIER_ORDER.length; i++) {
|
|
42
|
-
const next =
|
|
53
|
+
const next = landing(DEFAULT_TIER_ORDER[i], self);
|
|
43
54
|
if (next)
|
|
44
|
-
return next;
|
|
55
|
+
return { model: next.model, via: [...(name !== self && !ownKey(name) ? [name] : []), ...next.via] };
|
|
45
56
|
}
|
|
46
57
|
return undefined;
|
|
47
58
|
};
|
|
48
59
|
const shadowed = (key) => Object.hasOwn(out, key) && Boolean(out[key]);
|
|
49
|
-
const put = (key, m) => {
|
|
60
|
+
const put = (key, m, via) => {
|
|
50
61
|
Object.defineProperty(out, key, { value: m, writable: true, enumerable: true, configurable: true });
|
|
62
|
+
aliasVia.set(key, via);
|
|
51
63
|
};
|
|
52
64
|
const tierNames = new Set([...DEFAULT_TIER_ORDER, ...Object.keys(tiers)]);
|
|
53
65
|
for (const name of tierNames) {
|
|
54
66
|
if (shadowed(name))
|
|
55
67
|
continue;
|
|
56
|
-
const
|
|
57
|
-
if (
|
|
58
|
-
put(name,
|
|
68
|
+
const r = resolveTier(name);
|
|
69
|
+
if (r)
|
|
70
|
+
put(name, r.model, r.via);
|
|
59
71
|
}
|
|
60
72
|
for (const [alias, tier] of Object.entries(CC_MODEL_TIER_ALIASES)) {
|
|
61
73
|
if (shadowed(alias))
|
|
62
74
|
continue;
|
|
63
|
-
const
|
|
64
|
-
if (
|
|
65
|
-
put(alias,
|
|
75
|
+
const r = resolveTier(tier, alias);
|
|
76
|
+
if (r)
|
|
77
|
+
put(alias, r.model, r.via);
|
|
66
78
|
}
|
|
67
79
|
if (!shadowed("best")) {
|
|
68
80
|
for (const name of DEFAULT_TIER_ORDER) {
|
|
69
|
-
const
|
|
70
|
-
if (
|
|
71
|
-
put("best",
|
|
81
|
+
const r = landing(name, "best");
|
|
82
|
+
if (r) {
|
|
83
|
+
put("best", r.model, r.via);
|
|
72
84
|
break;
|
|
73
85
|
}
|
|
74
86
|
}
|
|
75
87
|
}
|
|
76
88
|
return out;
|
|
77
89
|
}
|
|
90
|
+
export function tierAliasVia(word, models) {
|
|
91
|
+
const via = models?.[TIER_ALIAS_VIA];
|
|
92
|
+
return via?.get(word) ?? [];
|
|
93
|
+
}
|
|
78
94
|
export function parseModelMention(text, allowedNames) {
|
|
79
95
|
const allowed = new Map();
|
|
80
96
|
for (const n of allowedNames)
|
|
@@ -181,6 +197,7 @@ export function resolveTaskModel(spec, deps) {
|
|
|
181
197
|
const startParts = specParts(mergeRoles(deps.roles, spec.roles)[start]);
|
|
182
198
|
return {
|
|
183
199
|
model: resolveModel(spec.model, deps.models),
|
|
200
|
+
modelRef: spec.model,
|
|
184
201
|
thinking: spec.thinking ?? startParts.thinking,
|
|
185
202
|
systemPrompt: startParts.systemPrompt,
|
|
186
203
|
};
|
|
@@ -190,9 +207,11 @@ export function resolveTaskModel(spec, deps) {
|
|
|
190
207
|
const startSystemPrompt = specParts(merged[start]).systemPrompt;
|
|
191
208
|
for (const role of [start, ...FALLBACK[start]]) {
|
|
192
209
|
const parts = specParts(merged[role]);
|
|
193
|
-
const
|
|
210
|
+
const fromParts = partsModel(parts, deps.models);
|
|
211
|
+
const model = fromParts ?? roleTierModel(role, deps.models);
|
|
194
212
|
if (model) {
|
|
195
|
-
|
|
213
|
+
const modelRef = fromParts !== undefined ? (parts.model ?? model) : (ROLE_TIER_DEFAULTS[role] ?? model);
|
|
214
|
+
return { model, modelRef, thinking: spec.thinking ?? parts.thinking, systemPrompt: startSystemPrompt };
|
|
196
215
|
}
|
|
197
216
|
}
|
|
198
217
|
throw new Error(`No model for role "${start}": set TaskSpec.model, or configure RunnerDeps.roles / TaskSpec.roles["${start}"] (or a "default" role). A "select" RoleSpec also fails here if no catalog model matches.`);
|
|
@@ -165,6 +165,8 @@ export interface ResultFlags {
|
|
|
165
165
|
* `reasoning.resolved` trace frame carried (runtask resolves once per leg — two faces, one mint);
|
|
166
166
|
* absent when thinking was off/unset for the leg, so the key is omitted. */
|
|
167
167
|
effectiveReasoning?: TaskResult["effectiveReasoning"];
|
|
168
|
+
/** The run's compaction record (see `TaskResult.compaction`); assembly omits the key when absent. */
|
|
169
|
+
compaction?: TaskResult["compaction"];
|
|
168
170
|
/** ruled 2026-08-04 — the usage-governance wait hint carried by the platform terminal the run adopted
|
|
169
171
|
* (`undefined` for every other cause: an expiring environment has no return time to give, and a store
|
|
170
172
|
* failure is not a window). Passed as data rather than read back off `threw` so the seat has a typed
|
|
@@ -2,6 +2,7 @@ import { isDegenerateCutMessage } from "../../brain/terminal-cause.js";
|
|
|
2
2
|
import { extractErrorCode, stripErrorCodePrefix } from "../../brain/errors.js";
|
|
3
3
|
import { terminalProjection } from "./terminal-projection.js";
|
|
4
4
|
import { haltConsumed } from "./halt-attribution.js";
|
|
5
|
+
import { providerReportedModel } from "../message-utils.js";
|
|
5
6
|
import { isErrorLike, readMember } from "../thrown-value.js";
|
|
6
7
|
import { describeRejectedValue } from "../tool-search-setting.js";
|
|
7
8
|
const SALVAGE_ELIGIBLE_TERMINALS = new Set([
|
|
@@ -153,6 +154,7 @@ export function terminalCauseOf(final, stats, flags) {
|
|
|
153
154
|
export function assembleResult(spec, sessionId, final, stats, flags) {
|
|
154
155
|
const taskId = spec.taskId ?? sessionId;
|
|
155
156
|
const text = final ? assistantText(final) : "";
|
|
157
|
+
const responseModel = final === undefined ? undefined : providerReportedModel(final);
|
|
156
158
|
if (!flags.unpricedSpend) {
|
|
157
159
|
const compactionMicroUsd = stats.compactionMicroUsd ?? 0;
|
|
158
160
|
const nestedSubagentMicroUsd = stats.nested?.costMicroUsd ?? 0;
|
|
@@ -176,5 +178,5 @@ export function assembleResult(spec, sessionId, final, stats, flags) {
|
|
|
176
178
|
if (flags.unpricedSpend)
|
|
177
179
|
delete publicStats.costMicroUsd;
|
|
178
180
|
const stampHaltedByUser = haltConsumed(flags.haltConsumption);
|
|
179
|
-
return { taskId, ...(flags.runId !== undefined ? { runId: flags.runId } : {}), sessionId, terminal, ...(flags.model !== undefined ? { model: flags.model } : {}), result: result.trim(), salvagedOutput, ...(apiFailure !== undefined ? { apiFailure } : {}), ...(retryAfterMs !== undefined ? { retryAfterMs } : {}), ...(flags.rewindNotes !== undefined && flags.rewindNotes.length > 0 ? { rewindNotes: flags.rewindNotes } : {}), ...(flags.editedFiles !== undefined && flags.editedFiles.length > 0 ? { editedFiles: flags.editedFiles } : {}), ...(flags.haltedOnUserRejection === true ? { haltedOnUserRejection: true } : {}), ...(stampHaltedByUser ? { haltedByUser: true } : {}), ...(flags.remoteEnvFailures !== undefined && flags.remoteEnvFailures.length > 0 ? { remoteEnvFailures: [...flags.remoteEnvFailures] } : {}), ...(flags.strandedHumanAnswers !== undefined && flags.strandedHumanAnswers.length > 0 ? { strandedHumanAnswers: flags.strandedHumanAnswers } : {}), ...(flags.effectiveReadFace !== undefined ? { effectiveReadFace: flags.effectiveReadFace } : {}), ...(flags.effectiveWriteFace !== undefined ? { effectiveWriteFace: flags.effectiveWriteFace } : {}), ...(flags.effectiveReadDenyPatterns !== undefined && flags.effectiveReadDenyPatterns.length > 0 ? { effectiveReadDenyPatterns: flags.effectiveReadDenyPatterns } : {}), ...(flags.effectiveMemoryScopes !== undefined ? { effectiveMemoryScopes: flags.effectiveMemoryScopes } : {}), ...(flags.effectiveReasoning !== undefined ? { effectiveReasoning: flags.effectiveReasoning } : {}), stats: publicStats };
|
|
181
|
+
return { taskId, ...(flags.runId !== undefined ? { runId: flags.runId } : {}), sessionId, terminal, ...(flags.model !== undefined ? { model: flags.model } : {}), ...(responseModel !== undefined ? { responseModel } : {}), result: result.trim(), salvagedOutput, ...(apiFailure !== undefined ? { apiFailure } : {}), ...(retryAfterMs !== undefined ? { retryAfterMs } : {}), ...(flags.rewindNotes !== undefined && flags.rewindNotes.length > 0 ? { rewindNotes: flags.rewindNotes } : {}), ...(flags.editedFiles !== undefined && flags.editedFiles.length > 0 ? { editedFiles: flags.editedFiles } : {}), ...(flags.haltedOnUserRejection === true ? { haltedOnUserRejection: true } : {}), ...(stampHaltedByUser ? { haltedByUser: true } : {}), ...(flags.remoteEnvFailures !== undefined && flags.remoteEnvFailures.length > 0 ? { remoteEnvFailures: [...flags.remoteEnvFailures] } : {}), ...(flags.strandedHumanAnswers !== undefined && flags.strandedHumanAnswers.length > 0 ? { strandedHumanAnswers: flags.strandedHumanAnswers } : {}), ...(flags.effectiveReadFace !== undefined ? { effectiveReadFace: flags.effectiveReadFace } : {}), ...(flags.effectiveWriteFace !== undefined ? { effectiveWriteFace: flags.effectiveWriteFace } : {}), ...(flags.effectiveReadDenyPatterns !== undefined && flags.effectiveReadDenyPatterns.length > 0 ? { effectiveReadDenyPatterns: flags.effectiveReadDenyPatterns } : {}), ...(flags.effectiveMemoryScopes !== undefined ? { effectiveMemoryScopes: flags.effectiveMemoryScopes } : {}), ...(flags.effectiveReasoning !== undefined ? { effectiveReasoning: flags.effectiveReasoning } : {}), ...(flags.compaction !== undefined ? { compaction: flags.compaction } : {}), stats: publicStats };
|
|
180
182
|
}
|
|
@@ -3,7 +3,9 @@ import type { MaybeCompactOptions } from "../auto-compaction.js";
|
|
|
3
3
|
import type { StaleToolResultOffloadOptions, TaskSpec } from "../types.js";
|
|
4
4
|
import type { Context } from "../../internal/llm.js";
|
|
5
5
|
import { type ToolResultStore } from "../tool-result-store.js";
|
|
6
|
-
import type { Prepared } from "./contracts.js";
|
|
6
|
+
import type { Prepared, RunnerDepsSeat } from "./contracts.js";
|
|
7
|
+
import { type ContextWindowOverlay } from "../context-settings.js";
|
|
8
|
+
import { type CompactionSettings } from "../../internal/harness.js";
|
|
7
9
|
/**
|
|
8
10
|
* The maybeCompact call arguments that MUST be assembled identically on all three
|
|
9
11
|
* of runtask's compaction lanes (turn boundary / prompt-too-long recovery / end-of-task).
|
|
@@ -17,7 +19,8 @@ import type { Prepared } from "./contracts.js";
|
|
|
17
19
|
*
|
|
18
20
|
* Every export is a free function: the turn-boundary lane assembles its call inside `createTurnBoundary`
|
|
19
21
|
* (a module-level function with no `this`), so a method would not be reachable from all three sites.
|
|
20
|
-
* Nothing here
|
|
22
|
+
* Nothing here WRITES runner state; one export ({@link legContextOverlayOption}) READS the live deps seat,
|
|
23
|
+
* because reading it at the moment of the pass — not at prepare — is the whole of what it states.
|
|
21
24
|
*/
|
|
22
25
|
/**
|
|
23
26
|
* Opt-in working-file attachments (LONGRUN-2 / CC 198 post-compact restore parity).
|
|
@@ -142,3 +145,24 @@ writtenRefs: Set<string>): Promise<Context>;
|
|
|
142
145
|
* scope inside `finish()`.
|
|
143
146
|
*/
|
|
144
147
|
export declare function emitInputTruncated(tracer: TracerHook | undefined, taskId: string): NonNullable<MaybeCompactOptions["onInputTruncated"]>;
|
|
148
|
+
/**
|
|
149
|
+
* The deployment's per-model window overlay in force for THIS pass — the one option of the three lanes that
|
|
150
|
+
* is deliberately a LIVE read rather than a prepared value.
|
|
151
|
+
*
|
|
152
|
+
* Two reads, both at the moment of the pass: the leg's own catalog word (the harness's current ref, so a
|
|
153
|
+
* mid-task degrade moves the key along with the model) and the Runner's current deps seat (so a `swapDeps`
|
|
154
|
+
* reaches a leg already running). An object-pinned leg has no word and therefore no overlay — the key is
|
|
155
|
+
* simply absent, which `maybeCompact` reads as "the model card's own declarations", the pre-seat behavior.
|
|
156
|
+
*/
|
|
157
|
+
export declare function legContextOverlayOption(prepared: Prepared, runner: RunnerDepsSeat): Pick<MaybeCompactOptions, "contextOverlay">;
|
|
158
|
+
/**
|
|
159
|
+
* The compaction SETTINGS a pass runs under — the assembly all three lanes share.
|
|
160
|
+
*
|
|
161
|
+
* `{ ...DEFAULT_COMPACTION_SETTINGS, ...spec.compaction }` was hand-copied at each lane, and it is where the
|
|
162
|
+
* deployment's live `enabled` declaration belongs: `maybeCompact` has exactly one enablement gate (it reads
|
|
163
|
+
* `settings.enabled`), so folding the overlay in HERE makes the deployment's switch reach the boundary pass,
|
|
164
|
+
* the recovery pass and the end-of-task summary by one edit instead of three, and keeps "who decides whether
|
|
165
|
+
* compaction runs" a single expression ({@link compactionEnabled}: the request authored it > the deployment
|
|
166
|
+
* declared it > on).
|
|
167
|
+
*/
|
|
168
|
+
export declare function legCompactionSettings(spec: TaskSpec, overlay: ContextWindowOverlay | undefined): CompactionSettings;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { emitTrace } from "../trace.js";
|
|
2
2
|
import { buildToolResultRef, OFFLOAD_TOOL_NAME, PERSISTED_OUTPUT_PREFIX, toolResultContentSegment, toolResultProvenanceOf, } from "../tool-result-store.js";
|
|
3
|
+
import { compactionEnabled, contextOverlayFor } from "../context-settings.js";
|
|
4
|
+
import { DEFAULT_COMPACTION_SETTINGS } from "../../internal/harness.js";
|
|
3
5
|
export function buildWorkingFileAttachments(spec, prepared) {
|
|
4
6
|
if (spec.compaction?.attachWorkingFiles === false || !prepared.readTaskFile)
|
|
5
7
|
return undefined;
|
|
@@ -115,3 +117,10 @@ export function emitInputTruncated(tracer, taskId) {
|
|
|
115
117
|
ts: Date.now(),
|
|
116
118
|
}));
|
|
117
119
|
}
|
|
120
|
+
export function legContextOverlayOption(prepared, runner) {
|
|
121
|
+
const overlay = contextOverlayFor(runner.deps.contextSettings, prepared.harness.getModelRef(), runner.deps);
|
|
122
|
+
return overlay !== undefined ? { contextOverlay: overlay } : {};
|
|
123
|
+
}
|
|
124
|
+
export function legCompactionSettings(spec, overlay) {
|
|
125
|
+
return { ...DEFAULT_COMPACTION_SETTINGS, ...spec.compaction, enabled: compactionEnabled(spec.compaction?.enabled, overlay) };
|
|
126
|
+
}
|
|
@@ -8,10 +8,11 @@ import type { Prepared, RunnerDepsSeat } from "./contracts.js";
|
|
|
8
8
|
* Runner OWNS the consecutive-reuse counter (`prepared.compactionReuseRef`) so the
|
|
9
9
|
* `maxConsecutiveProviderReuse` drift guard spans the whole task across both sites: it FEEDS the current
|
|
10
10
|
* count in as `consecutiveProviderReuse`, and {@link recordCompactionReuse} updates it from the result.
|
|
11
|
-
*
|
|
12
|
-
* the
|
|
11
|
+
* The provider half is absent when no provider is wired (byte-identical to the prior behavior at every
|
|
12
|
+
* call site); the #1036 `onCommitted` effect sink rides unconditionally, which is why the
|
|
13
|
+
* function itself always returns an object.
|
|
13
14
|
*/
|
|
14
|
-
export declare function seamCCompactionOptions(runner: RunnerDepsSeat, prepared: Prepared): Pick<MaybeCompactOptions, "summaryProvider" | "onCompaction" | "maxConsecutiveProviderReuse" | "consecutiveProviderReuse"
|
|
15
|
+
export declare function seamCCompactionOptions(runner: RunnerDepsSeat, prepared: Prepared): Pick<MaybeCompactOptions, "summaryProvider" | "onCompaction" | "maxConsecutiveProviderReuse" | "consecutiveProviderReuse" | "onCommitted">;
|
|
15
16
|
/** Resolve the pre/postCompact lifecycle callbacks (whole-slot `spec.hooks ??
|
|
16
17
|
* deps.hooks`, same resolution as the stop hook) and wrap each in a SWALLOW+TRACE shell before
|
|
17
18
|
* threading them into maybeCompact. The wrapper owns the observability half of the R3 MED contract
|
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
import { hookSeatExpiredError, resolveHookTimeoutMs, runHookSeat } from "../hooks.js";
|
|
2
2
|
import { asThrownError } from "../thrown-value.js";
|
|
3
3
|
export function seamCCompactionOptions(runner, prepared) {
|
|
4
|
-
if (!runner.deps.summaryProvider)
|
|
5
|
-
return undefined;
|
|
6
4
|
return {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
...(runner.deps.summaryProvider
|
|
6
|
+
? {
|
|
7
|
+
summaryProvider: runner.deps.summaryProvider,
|
|
8
|
+
onCompaction: runner.deps.onCompaction,
|
|
9
|
+
maxConsecutiveProviderReuse: runner.deps.maxConsecutiveProviderReuse,
|
|
10
|
+
consecutiveProviderReuse: prepared.compactionReuseRef.consecutive,
|
|
11
|
+
}
|
|
12
|
+
: {}),
|
|
13
|
+
onCommitted: (effect) => {
|
|
14
|
+
if (prepared.frameSettlement.wantsEffects?.() !== true)
|
|
15
|
+
return;
|
|
16
|
+
prepared.frameSettlement.compactions.push(effect);
|
|
17
|
+
},
|
|
11
18
|
};
|
|
12
19
|
}
|
|
13
20
|
export function compactionHookOptions(runner, spec, sessionId, trigger, identity, seatBound) {
|