@sema-agent/core 7.11.1 → 7.12.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 +41 -0
- package/dist/core/auto-mode-arming.d.ts +10 -14
- package/dist/core/auto-mode-arming.js +3 -9
- package/dist/core/auto-mode-defaults.d.ts +0 -2
- package/dist/core/auto-mode-defaults.js +0 -1
- package/dist/core/auto-mode-rebuild.d.ts +6 -13
- package/dist/core/auto-mode-rebuild.js +0 -2
- package/dist/core/auto-mode.d.ts +30 -89
- package/dist/core/auto-mode.js +12 -59
- package/dist/core/checkpoint-store.d.ts +1 -3
- package/dist/core/gate-fold.js +1 -9
- package/dist/core/gate-lanes.js +15 -9
- package/dist/core/hooks.d.ts +6 -0
- package/dist/core/runner/compaction-knobs.d.ts +45 -0
- package/dist/core/runner/compaction-knobs.js +3 -0
- package/dist/core/runner/contracts.d.ts +49 -10
- package/dist/core/runner/denial-limit-arms.d.ts +10 -13
- package/dist/core/runner/denial-limit-arms.js +9 -7
- package/dist/core/runner/gate-exit.js +9 -1
- package/dist/core/runner/prepare-caps-and-workflow.d.ts +1 -1
- package/dist/core/runner/prepare-caps-and-workflow.js +0 -5
- package/dist/core/runner/prepare-suspend-saga.d.ts +0 -2
- package/dist/core/runner/prepare-suspend-saga.js +2 -10
- package/dist/core/runner/prepare-task.js +1 -1
- package/dist/core/runner/prepare-wiring-manifest.d.ts +1 -1
- package/dist/core/runner/prepare-wiring-manifest.js +1 -8
- package/dist/core/runner/run-attachment-seats.d.ts +22 -0
- package/dist/core/runner/run-attachment-seats.js +187 -0
- package/dist/core/runner/run-brain-sinks.d.ts +29 -0
- package/dist/core/runner/run-brain-sinks.js +61 -0
- package/dist/core/runner/run-clock-and-content.d.ts +52 -0
- package/dist/core/runner/run-clock-and-content.js +26 -0
- package/dist/core/runner/run-compaction-machinery.d.ts +35 -0
- package/dist/core/runner/run-compaction-machinery.js +98 -0
- package/dist/core/runner/run-git-lane.d.ts +64 -0
- package/dist/core/runner/run-git-lane.js +102 -0
- package/dist/core/runner/run-identity-wiring.d.ts +77 -0
- package/dist/core/runner/run-identity-wiring.js +85 -0
- package/dist/core/runner/run-leg.d.ts +106 -0
- package/dist/core/runner/run-leg.js +462 -0
- package/dist/core/runner/run-notification-lane.d.ts +55 -0
- package/dist/core/runner/run-notification-lane.js +128 -0
- package/dist/core/runner/run-reasoning-seat.d.ts +27 -0
- package/dist/core/runner/run-reasoning-seat.js +48 -0
- package/dist/core/runner/run-recovery-lanes.d.ts +54 -0
- package/dist/core/runner/run-recovery-lanes.js +180 -0
- package/dist/core/runner/run-settle-and-teardown.d.ts +109 -0
- package/dist/core/runner/run-settle-and-teardown.js +324 -0
- package/dist/core/runner/run-stop-and-final-verify.d.ts +32 -0
- package/dist/core/runner/run-stop-and-final-verify.js +159 -0
- package/dist/core/runner/run-telemetry-and-budget-seats.d.ts +38 -0
- package/dist/core/runner/run-telemetry-and-budget-seats.js +159 -0
- package/dist/core/runner/run-terminal-adoption.d.ts +99 -0
- package/dist/core/runner/run-terminal-adoption.js +120 -0
- package/dist/core/runner/run-tool-mount-facts.d.ts +26 -0
- package/dist/core/runner/run-tool-mount-facts.js +58 -0
- package/dist/core/runner/run-turn-boundary.d.ts +0 -35
- package/dist/core/runner/run-turn-boundary.js +1 -3
- package/dist/core/runner/runtask.d.ts +14 -3
- package/dist/core/runner/runtask.js +125 -2102
- package/dist/core/runner-deps.d.ts +4 -14
- package/dist/core/store-contracts/workflow-journal-store-contract.d.ts +7 -0
- package/dist/core/store-contracts/workflow-journal-store-contract.js +85 -0
- package/dist/core/tool-policy.d.ts +37 -93
- package/dist/core/tool-policy.js +1 -11
- package/dist/core/trace.d.ts +6 -7
- package/dist/core/wiring-manifest.d.ts +5 -22
- package/dist/core/wiring-manifest.js +3 -11
- package/dist/core/workflow-journal-store.d.ts +35 -4
- package/dist/core/workflow-journal-store.js +19 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +3 -2
- package/dist/orchestration/workflow.js +2 -0
- package/dist/stores/file/workflow-journal-store.d.ts +7 -10
- package/dist/stores/file/workflow-journal-store.js +2 -4
- package/dist/tools/fs/fs-bash.js +3 -2
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +10 -10
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { eventDefaultOn } from "../../prompt-assembly/event-registry.js";
|
|
2
|
+
import { stripGitStatusUnits } from "./git-status-frame.js";
|
|
3
|
+
import { SKILLS_LISTING_PROBE_HEADER } from "./synthetic-tools.js";
|
|
4
|
+
import { AGENT_LISTING_REMOVED_HEADER, SKILLS_LISTING_DELTA_HEADER, SKILLS_LISTING_REMOVED_HEADER, agentListingDeltaHeader, agentListingInitialHeader, createAttachmentState, replayAnnouncedListing, replayAnnouncedModels } from "./turn-attachments.js";
|
|
5
|
+
export async function runAttachmentSeats(input) {
|
|
6
|
+
const { spec, prepared, resume, rs, runner, next } = input;
|
|
7
|
+
rs.counters.wroteThisRun = false;
|
|
8
|
+
rs.counters.finalVerifyInjections = 0;
|
|
9
|
+
rs.counters.groundingSignalPreR9 = false;
|
|
10
|
+
rs.counters.groundingSignalPostR9 = false;
|
|
11
|
+
rs.attach.attachmentsCfg = spec.attachments;
|
|
12
|
+
rs.attach.agentListingOn = rs.attach.attachmentsCfg?.agentListing !== false && eventDefaultOn("agent_listing");
|
|
13
|
+
rs.attach.skillsListingOn = rs.attach.attachmentsCfg?.skillsListing !== false && eventDefaultOn("skills_listing");
|
|
14
|
+
const listingsLive = (rs.attach.agentListingOn && prepared.agentListing !== undefined) || (rs.attach.skillsListingOn && prepared.skillsListing !== undefined);
|
|
15
|
+
const backgroundTasksLive = (rs.attach.attachmentsCfg?.backgroundTasks ?? eventDefaultOn("background_tasks")) === true;
|
|
16
|
+
rs.attach.attachState = rs.attach.attachmentsCfg !== undefined || listingsLive || backgroundTasksLive ? createAttachmentState() : undefined;
|
|
17
|
+
rs.attach.dateState = prepared.dateChange !== undefined ? { announcedDate: prepared.dateChange.legDate } : undefined;
|
|
18
|
+
rs.attach.instrProbe = runner.deps.probeInstructionSources;
|
|
19
|
+
rs.attach.instrState =
|
|
20
|
+
rs.attach.instrProbe !== undefined && prepared.instructionSources !== undefined && prepared.instructionSources.length > 0
|
|
21
|
+
? { lastAnnouncedHash: new Map(prepared.instructionSources.map((s) => [s.path, s.contentHash])) }
|
|
22
|
+
: undefined;
|
|
23
|
+
rs.attach.sizeGuidelineState =
|
|
24
|
+
prepared.workflowSizeGuideline !== undefined
|
|
25
|
+
? { announcedGuideline: prepared.workflowSizeGuideline.legGuideline, current: prepared.workflowSizeGuideline.current }
|
|
26
|
+
: undefined;
|
|
27
|
+
rs.counters.cadenceTurns = 0;
|
|
28
|
+
rs.turn.lastTurnHadToolCalls = false;
|
|
29
|
+
if (rs.attach.attachState !== undefined && (rs.attach.attachmentsCfg?.backgroundTasks ?? eventDefaultOn("background_tasks")) === true) {
|
|
30
|
+
try {
|
|
31
|
+
const branch = await prepared.session.getBranch();
|
|
32
|
+
for (let i = branch.length - 1; i >= 0; i--) {
|
|
33
|
+
const e = branch[i];
|
|
34
|
+
if (e.type === "compaction") {
|
|
35
|
+
rs.attach.attachState.postCompactPending = true;
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
if (e.type === "message" && e.message.role === "assistant")
|
|
39
|
+
break;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (rs.attach.attachState !== undefined &&
|
|
46
|
+
((rs.attach.agentListingOn && prepared.agentListing?.seedAnnounced === true) ||
|
|
47
|
+
(rs.attach.skillsListingOn && prepared.skillsListing?.seedAnnounced === true))) {
|
|
48
|
+
const cpListings = resume?.cp.state.announcedListings;
|
|
49
|
+
let entryListings;
|
|
50
|
+
try {
|
|
51
|
+
entryListings = await prepared.session.getAnnouncedListing();
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
entryListings = undefined;
|
|
55
|
+
}
|
|
56
|
+
const textOf = (m) => {
|
|
57
|
+
const c = m.content;
|
|
58
|
+
if (typeof c === "string")
|
|
59
|
+
return c;
|
|
60
|
+
if (Array.isArray(c)) {
|
|
61
|
+
return c
|
|
62
|
+
.map((b) => (b !== null && typeof b === "object" && b.type === "text" ? String(b.text ?? "") : ""))
|
|
63
|
+
.join("\n");
|
|
64
|
+
}
|
|
65
|
+
return "";
|
|
66
|
+
};
|
|
67
|
+
let branchTexts = null;
|
|
68
|
+
const branchLoad = async () => {
|
|
69
|
+
if (branchTexts === null) {
|
|
70
|
+
const branch = await prepared.session.getBranch();
|
|
71
|
+
branchTexts = branch.flatMap((e) => {
|
|
72
|
+
if (e.type !== "message")
|
|
73
|
+
return [];
|
|
74
|
+
const m = e.message;
|
|
75
|
+
if (m.role !== "user")
|
|
76
|
+
return [];
|
|
77
|
+
const full = textOf(e.message);
|
|
78
|
+
if (m.engineMinted === true)
|
|
79
|
+
return [stripGitStatusUnits(full, prepared.reminderMark)];
|
|
80
|
+
if (Array.isArray(m.engineSegments) && m.engineSegments.length > 0) {
|
|
81
|
+
return m.engineSegments.map((s) => stripGitStatusUnits(full.slice(Math.max(0, s.start), Math.max(0, s.end)), prepared.reminderMark));
|
|
82
|
+
}
|
|
83
|
+
if (typeof m.enginePrefixChars === "number" && m.enginePrefixChars > 0)
|
|
84
|
+
return [stripGitStatusUnits(full.slice(0, m.enginePrefixChars), prepared.reminderMark)];
|
|
85
|
+
return [];
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
return branchTexts;
|
|
89
|
+
};
|
|
90
|
+
const branchReplay = async (headers) => {
|
|
91
|
+
try {
|
|
92
|
+
return replayAnnouncedListing(await branchLoad(), headers);
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
return undefined;
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
if (rs.attach.agentListingOn && prepared.agentListing?.seedAnnounced === true) {
|
|
99
|
+
const listing = prepared.agentListing;
|
|
100
|
+
if (cpListings?.agents === undefined && entryListings?.agents !== undefined) {
|
|
101
|
+
const descOf = new Map(listing.entries.map((e) => [e.name, e.description]));
|
|
102
|
+
rs.attach.attachState.announcedAgentTypes = new Map(entryListings.agents.map((n) => [n, descOf.get(n) ?? ""]));
|
|
103
|
+
prepared.announcedListingsRef.agents = [...entryListings.agents];
|
|
104
|
+
if (entryListings.models !== undefined) {
|
|
105
|
+
rs.attach.attachState.announcedModels = [...entryListings.models];
|
|
106
|
+
prepared.announcedListingsRef.models = [...entryListings.models];
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
else if (cpListings?.agents !== undefined) {
|
|
110
|
+
const descOf = new Map(listing.entries.map((e) => [e.name, e.description]));
|
|
111
|
+
rs.attach.attachState.announcedAgentTypes = new Map(cpListings.agents.map((n) => [n, descOf.get(n) ?? ""]));
|
|
112
|
+
prepared.announcedListingsRef.agents = [...cpListings.agents];
|
|
113
|
+
if (cpListings.models !== undefined) {
|
|
114
|
+
rs.attach.attachState.announcedModels = [...cpListings.models];
|
|
115
|
+
prepared.announcedListingsRef.models = [...cpListings.models];
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
const replayed = await branchReplay({
|
|
120
|
+
initial: agentListingInitialHeader(listing.toolName),
|
|
121
|
+
delta: agentListingDeltaHeader(listing.toolName),
|
|
122
|
+
removed: AGENT_LISTING_REMOVED_HEADER,
|
|
123
|
+
});
|
|
124
|
+
if (replayed !== undefined) {
|
|
125
|
+
const descOf = new Map(listing.entries.map((e) => [e.name, e.description]));
|
|
126
|
+
rs.attach.attachState.announcedAgentTypes = new Map([...replayed].map((n) => [n, descOf.get(n) ?? ""]));
|
|
127
|
+
prepared.announcedListingsRef.agents = [...replayed];
|
|
128
|
+
const replayedModels = replayAnnouncedModels(await branchLoad());
|
|
129
|
+
if (replayedModels !== undefined) {
|
|
130
|
+
rs.attach.attachState.announcedModels = [...replayedModels];
|
|
131
|
+
prepared.announcedListingsRef.models = [...replayedModels];
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (rs.attach.skillsListingOn && prepared.skillsListing?.seedAnnounced === true) {
|
|
137
|
+
const listing = prepared.skillsListing;
|
|
138
|
+
if (cpListings?.skills === undefined && entryListings?.skills !== undefined) {
|
|
139
|
+
const descOf = new Map(listing.entries.map((e) => [e.name, e.description]));
|
|
140
|
+
rs.attach.attachState.announcedSkills = new Map(entryListings.skills.map((n) => [n, descOf.get(n) ?? ""]));
|
|
141
|
+
prepared.announcedListingsRef.skills = [...entryListings.skills];
|
|
142
|
+
}
|
|
143
|
+
else if (cpListings?.skills !== undefined) {
|
|
144
|
+
const descOf = new Map(listing.entries.map((e) => [e.name, e.description]));
|
|
145
|
+
rs.attach.attachState.announcedSkills = new Map(cpListings.skills.map((n) => [n, descOf.get(n) ?? ""]));
|
|
146
|
+
prepared.announcedListingsRef.skills = [...cpListings.skills];
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
const replayed = await branchReplay({
|
|
150
|
+
initial: SKILLS_LISTING_PROBE_HEADER,
|
|
151
|
+
delta: SKILLS_LISTING_DELTA_HEADER,
|
|
152
|
+
removed: SKILLS_LISTING_REMOVED_HEADER,
|
|
153
|
+
});
|
|
154
|
+
if (replayed !== undefined) {
|
|
155
|
+
const descOf = new Map(listing.entries.map((e) => [e.name, e.description]));
|
|
156
|
+
rs.attach.attachState.announcedSkills = new Map([...replayed].map((n) => [n, descOf.get(n) ?? ""]));
|
|
157
|
+
prepared.announcedListingsRef.skills = [...replayed];
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
if (spec.finalVerification === true && resume !== undefined) {
|
|
163
|
+
try {
|
|
164
|
+
const branch = await prepared.session.getBranch();
|
|
165
|
+
for (const e of branch) {
|
|
166
|
+
if (e.type !== "message")
|
|
167
|
+
continue;
|
|
168
|
+
const m = e.message;
|
|
169
|
+
if (m.role === "user") {
|
|
170
|
+
const text = typeof m.content === "string"
|
|
171
|
+
? m.content
|
|
172
|
+
: m.content.map((b) => (b !== null && typeof b === "object" && b.type === "text" ? String(b.text ?? "") : "")).join("\n");
|
|
173
|
+
const fvMarked = m.engineMinted === true && text.includes(`<system-reminder mark="${prepared.reminderMark}">[final verification]`);
|
|
174
|
+
if ((fvMarked || text.includes("<system-reminder>[final verification]")) && rs.counters.finalVerifyInjections < 2)
|
|
175
|
+
rs.counters.finalVerifyInjections += 1;
|
|
176
|
+
}
|
|
177
|
+
else if (m.role === "toolResult" && !rs.counters.wroteThisRun && (prepared.toolEffects.get(m.toolName) ?? "write") !== "read") {
|
|
178
|
+
rs.counters.wroteThisRun = true;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
catch {
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
rs.attach.attachmentsInjected = 0;
|
|
186
|
+
return await next({});
|
|
187
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { ReasoningWireFacts } from "../../brain/reasoning.js";
|
|
2
|
+
import type { PushQueue } from "../push-queue.js";
|
|
3
|
+
import type { TaskEvent } from "../types.js";
|
|
4
|
+
import type { RunInternals, RunState } from "./contracts.js";
|
|
5
|
+
export interface RunBrainSinksInput {
|
|
6
|
+
/** borrowed-mutable — the run's event queue: the `status` frames. */
|
|
7
|
+
queue: PushQueue<TaskEvent>;
|
|
8
|
+
/** borrowed-readonly — the trusted run-scoped channel: `agentName` (the display name), `onStatusEvent` (the second sink). */
|
|
9
|
+
internals: RunInternals | undefined;
|
|
10
|
+
/** borrowed-readonly — the run's mutable state, read for the telemetry frames' tracer and task id. */
|
|
11
|
+
rs: RunState;
|
|
12
|
+
/** borrowed-readonly — the run's event-identity mint (stamped on every status frame). */
|
|
13
|
+
ident: () => {
|
|
14
|
+
eventId: string;
|
|
15
|
+
parentToolCallId?: string;
|
|
16
|
+
sourceTaskId?: string;
|
|
17
|
+
};
|
|
18
|
+
/** borrowed-readonly — the spawning tool call's id when this task runs AS A SUB-AGENT; absent on a top-level run. */
|
|
19
|
+
parentToolCallId: string | undefined;
|
|
20
|
+
/** borrowed-readonly — the reasoning lane's wire-facts consumer, the third sink the composition installs. */
|
|
21
|
+
observeReasoningWireFacts: (facts: ReasoningWireFacts) => void;
|
|
22
|
+
}
|
|
23
|
+
export interface RunBrainSinksResult {
|
|
24
|
+
/** design/99 BC-2: the subagent's human display name for its `task_progress` ticks, or undefined. */
|
|
25
|
+
subagentName: string | undefined;
|
|
26
|
+
/** Compose the three brain ALS sinks around a brain-driving call. */
|
|
27
|
+
withBrainSinks: <T>(fn: () => Promise<T>) => Promise<T>;
|
|
28
|
+
}
|
|
29
|
+
export declare function runBrainSinks(input: RunBrainSinksInput): RunBrainSinksResult;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { runWithBrainTelemetry, runWithReasoningWireFacts, runWithStatusSink } from "../../brain/status-sink.js";
|
|
2
|
+
import { createSafeNotifier, observeThenableRejection } from "../safe-notify.js";
|
|
3
|
+
import { emitTrace } from "../trace.js";
|
|
4
|
+
import { inlineUntrusted } from "../untrusted-text.js";
|
|
5
|
+
export function runBrainSinks(input) {
|
|
6
|
+
const { queue, internals, rs, ident, parentToolCallId, observeReasoningWireFacts } = input;
|
|
7
|
+
const subagentName = parentToolCallId !== undefined && internals?.agentName !== undefined ? inlineUntrusted(internals.agentName.slice(0, 320), 80) : undefined;
|
|
8
|
+
const statusSinkNotifier = createSafeNotifier({
|
|
9
|
+
onError: (f) => console.warn(`[sema-core] ${f.site}: run-internals status sink threw (contained; further failures counted, not re-disclosed): ${f.error.message}`),
|
|
10
|
+
});
|
|
11
|
+
const statusEmit = (s) => {
|
|
12
|
+
const frame = {
|
|
13
|
+
type: "status",
|
|
14
|
+
phase: s.phase,
|
|
15
|
+
...(s.detail !== undefined ? { detail: s.detail } : {}),
|
|
16
|
+
...(s.retryInSec !== undefined ? { retryInSec: s.retryInSec } : {}),
|
|
17
|
+
...(s.retryInMs !== undefined ? { retryInMs: s.retryInMs } : {}),
|
|
18
|
+
...(s.retryAtMs !== undefined ? { retryAtMs: s.retryAtMs } : {}),
|
|
19
|
+
...(s.elapsedMs !== undefined ? { elapsedMs: s.elapsedMs } : {}),
|
|
20
|
+
...(s.timeoutMs !== undefined ? { timeoutMs: s.timeoutMs } : {}),
|
|
21
|
+
...(s.attempt !== undefined ? { attempt: s.attempt } : {}),
|
|
22
|
+
...(s.maxRetries !== undefined ? { maxRetries: s.maxRetries } : {}),
|
|
23
|
+
...(s.errClass !== undefined ? { errClass: s.errClass } : {}),
|
|
24
|
+
...(s.errorStatus !== undefined ? { errorStatus: s.errorStatus } : {}),
|
|
25
|
+
...ident(),
|
|
26
|
+
};
|
|
27
|
+
Object.freeze(frame);
|
|
28
|
+
const accepted = queue.push(frame);
|
|
29
|
+
if (accepted && internals?.onStatusEvent !== undefined) {
|
|
30
|
+
statusSinkNotifier.notify(() => observeThenableRejection(internals.onStatusEvent?.(frame), statusSinkNotifier, "runtask.onStatusEvent"), "runtask.onStatusEvent");
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
const telemetryEmit = (t) => {
|
|
34
|
+
emitTrace(rs.telemetry.tracer, () => t.kind === "failover"
|
|
35
|
+
? {
|
|
36
|
+
kind: "brain.failover",
|
|
37
|
+
version: 1,
|
|
38
|
+
taskId: rs.telemetry.taskId,
|
|
39
|
+
servedIndex: t.servedIndex,
|
|
40
|
+
total: t.total,
|
|
41
|
+
...(t.errorCode !== undefined ? { errorCode: t.errorCode } : {}),
|
|
42
|
+
ts: Date.now(),
|
|
43
|
+
}
|
|
44
|
+
: t.kind === "breaker"
|
|
45
|
+
? { kind: "breaker.transition", version: 1, taskId: rs.telemetry.taskId, key: t.key, phase: t.phase, failures: t.failures, ts: Date.now() }
|
|
46
|
+
: t.kind === "retry"
|
|
47
|
+
? {
|
|
48
|
+
kind: "brain.retry",
|
|
49
|
+
version: 1,
|
|
50
|
+
taskId: rs.telemetry.taskId,
|
|
51
|
+
attempt: t.attempt,
|
|
52
|
+
phase: t.phase,
|
|
53
|
+
...(t.errClass !== undefined ? { errClass: t.errClass } : {}),
|
|
54
|
+
...(t.nextDelayMs !== undefined ? { nextDelayMs: t.nextDelayMs } : {}),
|
|
55
|
+
ts: Date.now(),
|
|
56
|
+
}
|
|
57
|
+
: { kind: "vision.placeholder", version: 1, taskId: rs.telemetry.taskId, count: t.count, ts: Date.now() });
|
|
58
|
+
};
|
|
59
|
+
const withBrainSinks = (fn) => runWithStatusSink(statusEmit, () => runWithBrainTelemetry(telemetryEmit, () => runWithReasoningWireFacts(observeReasoningWireFacts, fn)));
|
|
60
|
+
return { subagentName, withBrainSinks };
|
|
61
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* design/393 S5 — the run body's CLOCK and CONTENT seats (R6), verbatim from `Runner.runLocked`: the per-slice
|
|
3
|
+
* walltime window and its MONOTONIC deadline anchored at task start, the hard-abort timer armed against that same
|
|
4
|
+
* deadline (a suspendable task gets the backstop grace), the content-event push that also forwards a sub-agent's
|
|
5
|
+
* events to the parent's display sink, the run's own committed-tail ref and the `message_committed` mint that
|
|
6
|
+
* retires a settled tool call from the started set.
|
|
7
|
+
*/
|
|
8
|
+
import type { PushQueue } from "../push-queue.js";
|
|
9
|
+
import type { TaskEvent, TaskSpec } from "../types.js";
|
|
10
|
+
import { startTimeout } from "./clock-and-limits.js";
|
|
11
|
+
import type { Prepared, RunInternals, RunState } from "./contracts.js";
|
|
12
|
+
export interface RunClockAndContentInput {
|
|
13
|
+
/** borrowed-readonly — the task spec: `limits.maxWalltimeMs`. */
|
|
14
|
+
spec: TaskSpec;
|
|
15
|
+
/** borrowed-readonly — the leg's prepared seat: the harness and abort controller the timer is armed against, the
|
|
16
|
+
* resource-suspend eligibility. */
|
|
17
|
+
prepared: Prepared;
|
|
18
|
+
/** borrowed-mutable — the run's event queue: content events and `message_committed` frames. */
|
|
19
|
+
queue: PushQueue<TaskEvent>;
|
|
20
|
+
/** borrowed-readonly — the trusted run-scoped channel: `onForwardEvent` (the parent's display sink). */
|
|
21
|
+
internals: RunInternals | undefined;
|
|
22
|
+
/** borrowed-mutable — the run's mutable state: `counters.walltimeSyncBackstopFired` is zeroed here; the monotonic
|
|
23
|
+
* task-start anchor is read. */
|
|
24
|
+
rs: RunState;
|
|
25
|
+
/** borrowed-readonly — the run's event-identity mint (stamped on every committed frame). */
|
|
26
|
+
ident: () => {
|
|
27
|
+
eventId: string;
|
|
28
|
+
parentToolCallId?: string;
|
|
29
|
+
sourceTaskId?: string;
|
|
30
|
+
};
|
|
31
|
+
/** borrowed-readonly — the spawning tool call's id when this task runs AS A SUB-AGENT; forwarding is gated on it. */
|
|
32
|
+
parentToolCallId: string | undefined;
|
|
33
|
+
/** borrowed-mutable — the tool-mount lane's started-call set: a committed toolResult retires its id here. */
|
|
34
|
+
startedToolCallIds: Set<string>;
|
|
35
|
+
}
|
|
36
|
+
export interface RunClockAndContentResult {
|
|
37
|
+
/** design/164: the PER-SLICE active clock, or undefined (0 is a valid, exhausted window). */
|
|
38
|
+
effectiveTimeoutMs: number | undefined;
|
|
39
|
+
/** RB-20: the monotonic deadline every walltime enforcement decision reads, or undefined. */
|
|
40
|
+
walltimeMonotonicDeadline: number | undefined;
|
|
41
|
+
/** The hard walltime backstop: its fired flag / lateness, and the clear the teardown calls. */
|
|
42
|
+
timeout: ReturnType<typeof startTimeout>;
|
|
43
|
+
/** Push a content event to the run's queue and, for a sub-agent, to the parent's display sink. */
|
|
44
|
+
pushContent: (e: TaskEvent) => void;
|
|
45
|
+
/** #483: the id of the LAST entry this run committed — the settle-time seal's provenance anchor. */
|
|
46
|
+
ownCommittedTailRef: {
|
|
47
|
+
current: string | undefined;
|
|
48
|
+
};
|
|
49
|
+
/** The `message_committed` mint every persisted entry of this run flows through. */
|
|
50
|
+
emitCommitted: (entryId: string, role: "user" | "assistant" | "toolResult", toolCallId?: string) => void;
|
|
51
|
+
}
|
|
52
|
+
export declare function runClockAndContent(input: RunClockAndContentInput): RunClockAndContentResult;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { startTimeout } from "./clock-and-limits.js";
|
|
2
|
+
export function runClockAndContent(input) {
|
|
3
|
+
const { spec, prepared, queue, internals, rs, ident, parentToolCallId, startedToolCallIds } = input;
|
|
4
|
+
const effectiveTimeoutMs = spec.limits?.maxWalltimeMs;
|
|
5
|
+
const walltimeMonotonicDeadline = effectiveTimeoutMs !== undefined ? rs.telemetry.taskStartMonotonic + effectiveTimeoutMs : undefined;
|
|
6
|
+
rs.counters.walltimeSyncBackstopFired = false;
|
|
7
|
+
const timeout = startTimeout(prepared.harness, prepared.abortController, walltimeMonotonicDeadline !== undefined ? walltimeMonotonicDeadline - performance.now() : undefined, prepared.suspendForResource !== undefined);
|
|
8
|
+
const pushContent = (e) => {
|
|
9
|
+
queue.push(e);
|
|
10
|
+
if (parentToolCallId !== undefined && internals?.onForwardEvent) {
|
|
11
|
+
try {
|
|
12
|
+
internals.onForwardEvent(e);
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
const ownCommittedTailRef = { current: undefined };
|
|
19
|
+
const emitCommitted = (entryId, role, toolCallId) => {
|
|
20
|
+
ownCommittedTailRef.current = entryId;
|
|
21
|
+
if (role === "toolResult" && toolCallId !== undefined)
|
|
22
|
+
startedToolCallIds.delete(toolCallId);
|
|
23
|
+
queue.push({ type: "message_committed", entryId, role, ...(toolCallId !== undefined ? { toolCallId } : {}), ...ident() });
|
|
24
|
+
};
|
|
25
|
+
return { effectiveTimeoutMs, walltimeMonotonicDeadline, timeout, pushContent, ownCommittedTailRef, emitCommitted };
|
|
26
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { Model } from "../../internal/llm.js";
|
|
2
|
+
import { type MaybeCompactOptions } from "../auto-compaction.js";
|
|
3
|
+
import { type ModelPricing } from "../pricing.js";
|
|
4
|
+
import type { Brain, TaskSpec } from "../types.js";
|
|
5
|
+
import type { Stats } from "./assemble-result.js";
|
|
6
|
+
import type { Prepared, RunnerDepsSeat, RunState } from "./contracts.js";
|
|
7
|
+
export interface RunCompactionMachineryInput {
|
|
8
|
+
/** borrowed-readonly — the task spec: the compaction settings. */
|
|
9
|
+
spec: TaskSpec;
|
|
10
|
+
/** borrowed-readonly — the leg's prepared seat: the model, the prompt overhead, the usage governance and the session id. */
|
|
11
|
+
prepared: Prepared;
|
|
12
|
+
/** borrowed-mutable — the run's mutable state: `telemetry.unpricedSpend` is set when a summarizer model is unpriced;
|
|
13
|
+
* the budget ceiling and the telemetry are read. */
|
|
14
|
+
rs: RunState;
|
|
15
|
+
/** borrowed-mutable — the run's usage counters: every accounted summary call adds to them (compaction spend
|
|
16
|
+
* tracked separately too). */
|
|
17
|
+
stats: Stats;
|
|
18
|
+
/** borrowed-readonly — the telemetry lane's price-table door (#462), consulted for every summarizer table adopted. */
|
|
19
|
+
noteUnevaluablePriceTable: (p: ModelPricing) => void;
|
|
20
|
+
/** borrowed-readonly — the Runner's deployment deps, read LIVE (`brain`, `pricing`, `onError`). */
|
|
21
|
+
runner: RunnerDepsSeat;
|
|
22
|
+
}
|
|
23
|
+
export interface RunCompactionMachineryResult {
|
|
24
|
+
/** The accounted compaction brain — `complete` always provided, usage recorded inline. */
|
|
25
|
+
compactionBrain: Brain;
|
|
26
|
+
/** design/64 §25 (A): whether ROUTINE turn-boundary compaction runs for this task. */
|
|
27
|
+
withinTaskCompaction: boolean;
|
|
28
|
+
/** §17.4: the consecutive-failure circuit breaker shared by every compaction lane of the run. */
|
|
29
|
+
compactionBreaker: {
|
|
30
|
+
failures: number;
|
|
31
|
+
};
|
|
32
|
+
/** design/145 门A/§3: the window-safety wiring (per call — a mid-task degrade moves the fallback target). */
|
|
33
|
+
windowSafetyOptions: (mainModel: Model) => Pick<MaybeCompactOptions, "fallbackBudget" | "onWindowSafety">;
|
|
34
|
+
}
|
|
35
|
+
export declare function runCompactionMachinery(input: RunCompactionMachineryInput): RunCompactionMachineryResult;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { DEFAULT_COMPACTION_SETTINGS } from "../../internal/harness.js";
|
|
2
|
+
import { resolveTriggerWindow } from "../context-edit.js";
|
|
3
|
+
import { sanitizeCompactionSettings } from "../auto-compaction.js";
|
|
4
|
+
import { isModelPriced, modelCostToPricing } from "../pricing.js";
|
|
5
|
+
import { emitTrace } from "../trace.js";
|
|
6
|
+
import { runWithReasoningWireFacts, runWithStatusSink } from "../../brain/status-sink.js";
|
|
7
|
+
import { cacheFamilyOf, usageCostMicroUsd } from "./usage-accounting.js";
|
|
8
|
+
export function runCompactionMachinery(input) {
|
|
9
|
+
const { spec, prepared, rs, stats, noteUnevaluablePriceTable, runner } = input;
|
|
10
|
+
const recordCompactionUsage = (m, msg) => {
|
|
11
|
+
const u = msg?.usage;
|
|
12
|
+
if (!u)
|
|
13
|
+
return;
|
|
14
|
+
const fam = cacheFamilyOf(m);
|
|
15
|
+
const price = runner.deps.pricing?.[m.id] ?? modelCostToPricing(m.cost);
|
|
16
|
+
const priced = isModelPriced(m, runner.deps.pricing);
|
|
17
|
+
if (!priced)
|
|
18
|
+
rs.telemetry.unpricedSpend = true;
|
|
19
|
+
noteUnevaluablePriceTable(price);
|
|
20
|
+
const { totalInputTokens, uncachedInputTokens, costMicroUsd } = usageCostMicroUsd(fam, u, price);
|
|
21
|
+
stats.tokens += u.totalTokens || 0;
|
|
22
|
+
stats.promptTokens += uncachedInputTokens;
|
|
23
|
+
stats.totalInputTokens += totalInputTokens;
|
|
24
|
+
stats.cachedTokens += u.cacheRead || 0;
|
|
25
|
+
stats.cacheWriteTokens += u.cacheWrite || 0;
|
|
26
|
+
stats.outputTokens += u.output || 0;
|
|
27
|
+
stats.costMicroUsd += costMicroUsd;
|
|
28
|
+
stats.compactionMicroUsd = (stats.compactionMicroUsd ?? 0) + costMicroUsd;
|
|
29
|
+
emitTrace(rs.telemetry.tracer, () => ({
|
|
30
|
+
kind: "brain.call", version: 1, taskId: rs.telemetry.taskId, model: m.id, provider: m.provider,
|
|
31
|
+
promptTokens: uncachedInputTokens, totalInputTokens, completionTokens: u.output || 0,
|
|
32
|
+
cacheRead: u.cacheRead || 0, cacheWrite: u.cacheWrite || 0,
|
|
33
|
+
latencyMs: 0, ...(priced ? { costMicroUsd } : {}), ...(typeof msg?.stopReason === "string" ? { stopReason: msg.stopReason } : {}), ts: Date.now(),
|
|
34
|
+
}));
|
|
35
|
+
};
|
|
36
|
+
const compactionBrain = {
|
|
37
|
+
stream: runner.deps.brain.stream,
|
|
38
|
+
complete: async (m, c, o) => {
|
|
39
|
+
const msg = await runWithStatusSink(() => { }, async () => await runWithReasoningWireFacts(() => { }, async () => runner.deps.brain.complete
|
|
40
|
+
? await runner.deps.brain.complete(m, c, o)
|
|
41
|
+
: await (await Promise.resolve(runner.deps.brain.stream(m, c, o))).result()));
|
|
42
|
+
recordCompactionUsage(m, msg);
|
|
43
|
+
return msg;
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
const withinTaskCompaction = (spec.compaction?.enabled ?? true) && (spec.compaction?.withinTask ?? true);
|
|
47
|
+
const compactionBreaker = { failures: 0 };
|
|
48
|
+
if (spec.compaction?.enabled ?? true) {
|
|
49
|
+
const prefixWindow = resolveTriggerWindow(prepared.model).window;
|
|
50
|
+
if (Number.isFinite(prefixWindow) && prefixWindow > 0) {
|
|
51
|
+
const prefixSettings = sanitizeCompactionSettings({ ...DEFAULT_COMPACTION_SETTINGS, ...spec.compaction }, prefixWindow);
|
|
52
|
+
const prefixCompactAt = prefixWindow - prefixSettings.reserveTokens;
|
|
53
|
+
if (prepared.promptOverheadTokens >= prefixCompactAt) {
|
|
54
|
+
runner.deps.onError?.(new Error(`compaction cannot help: the fixed request prefix (system prompt + tool schemas, ≈${prepared.promptOverheadTokens} tokens) ` +
|
|
55
|
+
`already meets or exceeds the compaction threshold (${prefixCompactAt} of a ${prefixWindow}-token window). ` +
|
|
56
|
+
`Compaction only shrinks conversation history, so this run will re-trigger or overflow regardless — ` +
|
|
57
|
+
`shrink the system prompt/tool surface or use a larger-window model.`), { phase: "config", sessionId: prepared.sessionId });
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const windowSafetyOptions = (mainModel) => ({
|
|
62
|
+
...(rs.budget.maxCostMicroUsd !== undefined
|
|
63
|
+
? {
|
|
64
|
+
fallbackBudget: {
|
|
65
|
+
spentMicroUsd: () => stats.costMicroUsd,
|
|
66
|
+
capMicroUsd: rs.budget.maxCostMicroUsd,
|
|
67
|
+
mainInputPer1M: (runner.deps.pricing?.[mainModel.id] ?? modelCostToPricing(mainModel.cost)).inputPer1M,
|
|
68
|
+
},
|
|
69
|
+
}
|
|
70
|
+
: {}),
|
|
71
|
+
onWindowSafety: (info) => {
|
|
72
|
+
if (info.kind === "fallback") {
|
|
73
|
+
emitTrace(rs.telemetry.tracer, () => ({
|
|
74
|
+
kind: "compaction.model_fallback",
|
|
75
|
+
version: 1,
|
|
76
|
+
taskId: rs.telemetry.taskId,
|
|
77
|
+
contentTokens: info.contentTokens,
|
|
78
|
+
headroomTokens: info.headroomTokens ?? 0,
|
|
79
|
+
truncationRatio: info.truncationRatio,
|
|
80
|
+
...(info.estCostMicroUsd !== undefined ? { estCostMicroUsd: info.estCostMicroUsd } : {}),
|
|
81
|
+
ts: Date.now(),
|
|
82
|
+
}));
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
emitTrace(rs.telemetry.tracer, () => ({
|
|
86
|
+
kind: "compaction.clamp_disclosure",
|
|
87
|
+
version: 1,
|
|
88
|
+
taskId: rs.telemetry.taskId,
|
|
89
|
+
truncationRatio: info.truncationRatio,
|
|
90
|
+
reason: info.reason ?? "tolerance",
|
|
91
|
+
...(info.estCostMicroUsd !== undefined ? { estCostMicroUsd: info.estCostMicroUsd } : {}),
|
|
92
|
+
ts: Date.now(),
|
|
93
|
+
}));
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
return { compactionBrain, withinTaskCompaction, compactionBreaker, windowSafetyOptions };
|
|
98
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* design/393 S5 — the run body's GIT LANE (R12), verbatim from `Runner.runLocked`: the git-status re-assert closure
|
|
3
|
+
* installed on `prepared.gitStatusRef` (compaction landing hook + owed-frame retry — an engine-minted standalone
|
|
4
|
+
* append with the receipt in hand, the announced mirror written only then), the parked mirror flush (F4), the
|
|
5
|
+
* owed-frame retry subscription registered BEFORE the boundary handler, the boundary subscription itself and the
|
|
6
|
+
* harness event subscription that dispatches to the five handler closures. The three subscriptions are taken in the
|
|
7
|
+
* order the driver took them, and the four closures come back for the driver to await and release where it did.
|
|
8
|
+
*/
|
|
9
|
+
import type { AgentHarnessEvent } from "../../internal/harness.js";
|
|
10
|
+
import type { Model } from "../../internal/llm.js";
|
|
11
|
+
import type { PushQueue } from "../push-queue.js";
|
|
12
|
+
import type { TaskEvent } from "../types.js";
|
|
13
|
+
import type { Prepared, RunnerDepsSeat, RunState } from "./contracts.js";
|
|
14
|
+
export interface RunGitLaneInput {
|
|
15
|
+
/** borrowed-mutable — the leg's prepared seat: `gitStatusRef.reassert` is installed here and the ref's announced /
|
|
16
|
+
* mirror / protection slots are written by the closures; the harness is subscribed to, the session appended to. */
|
|
17
|
+
prepared: Prepared;
|
|
18
|
+
/** borrowed-mutable — the run's event queue: the re-assert pushes its `steering_injected` echo frame. */
|
|
19
|
+
queue: PushQueue<TaskEvent>;
|
|
20
|
+
/** borrowed-mutable — the run's mutable state: the re-assert counts itself on `attach.attachmentsInjected`. */
|
|
21
|
+
rs: RunState;
|
|
22
|
+
/** borrowed-readonly — the run's event-identity mint (stamped on the echo frame). */
|
|
23
|
+
ident: () => {
|
|
24
|
+
eventId: string;
|
|
25
|
+
parentToolCallId?: string;
|
|
26
|
+
sourceTaskId?: string;
|
|
27
|
+
};
|
|
28
|
+
/** borrowed-readonly — the harness-handlers lane's message_update closure, dispatched to by identity. */
|
|
29
|
+
onMessageUpdate: (event: Extract<AgentHarnessEvent, {
|
|
30
|
+
type: "message_update";
|
|
31
|
+
}>) => void;
|
|
32
|
+
/** borrowed-readonly — the harness-handlers lane's message_end closure. */
|
|
33
|
+
onMessageEnd: (event: Extract<AgentHarnessEvent, {
|
|
34
|
+
type: "message_end";
|
|
35
|
+
}>) => void;
|
|
36
|
+
/** borrowed-readonly — the harness-handlers lane's tool_execution_start closure. */
|
|
37
|
+
onToolStart: (event: Extract<AgentHarnessEvent, {
|
|
38
|
+
type: "tool_execution_start";
|
|
39
|
+
}>) => void;
|
|
40
|
+
/** borrowed-readonly — the harness-handlers lane's tool_execution_end closure. */
|
|
41
|
+
onToolEnd: (event: Extract<AgentHarnessEvent, {
|
|
42
|
+
type: "tool_execution_end";
|
|
43
|
+
}>) => void;
|
|
44
|
+
/** borrowed-readonly — the harness-handlers lane's turn_end closure. */
|
|
45
|
+
onTurnEnd: () => void;
|
|
46
|
+
/** borrowed-readonly — the turn-boundary lane's handler, registered on `turn_boundary` AFTER the owed-frame retry
|
|
47
|
+
* so a pending announcement is re-asserted into the session ahead of the boundary's own context work. */
|
|
48
|
+
onTurnBoundary: (event: {
|
|
49
|
+
model: Model;
|
|
50
|
+
}) => Promise<undefined>;
|
|
51
|
+
/** borrowed-readonly — the Runner's deployment deps, read LIVE inside the closures (`onError`). */
|
|
52
|
+
runner: RunnerDepsSeat;
|
|
53
|
+
}
|
|
54
|
+
export interface RunGitLaneResult {
|
|
55
|
+
/** The parked-mirror flush — awaited at the serialization points (the owed-frame retry here; prompt settle in the driver). */
|
|
56
|
+
flushGitMirror: () => Promise<void>;
|
|
57
|
+
/** The owed-frame retry's `turn_boundary` subscription; the driver's teardown releases it first. */
|
|
58
|
+
unsubGitRetry: () => void;
|
|
59
|
+
/** The boundary handler's `turn_boundary` subscription; released second. */
|
|
60
|
+
unsubBoundary: () => void;
|
|
61
|
+
/** The harness event subscription; released third. */
|
|
62
|
+
unsub: () => void;
|
|
63
|
+
}
|
|
64
|
+
export declare function runGitLane(input: RunGitLaneInput): RunGitLaneResult;
|