@sema-agent/core 7.11.0 → 7.11.2
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 +39 -1
- package/dist/core/auto-mode-defaults.d.ts +13 -9
- package/dist/core/auto-mode-defaults.js +1 -1
- package/dist/core/governance-codes.d.ts +1 -0
- package/dist/core/governance-codes.js +1 -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 +78 -1
- package/dist/core/runner/prepare-caps-and-workflow.js +23 -8
- package/dist/core/runner/run-attachment-seats.d.ts +20 -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 +96 -0
- package/dist/core/runner/run-identity-wiring.js +84 -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-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-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.js +126 -1624
- package/dist/core/runner/stream-halt-verbs.d.ts +38 -0
- package/dist/core/runner/stream-halt-verbs.js +82 -0
- package/dist/core/runner/stream-lifecycle-verbs.d.ts +34 -0
- package/dist/core/runner/stream-lifecycle-verbs.js +126 -0
- package/dist/core/runner/stream-reap.d.ts +30 -0
- package/dist/core/runner/stream-reap.js +40 -0
- package/dist/core/runner/stream-settle-backstop.d.ts +38 -0
- package/dist/core/runner/stream-settle-backstop.js +113 -0
- package/dist/core/runner/stream-steer-verb.d.ts +30 -0
- package/dist/core/runner/stream-steer-verb.js +185 -0
- package/dist/tools/fs/bash-readonly-classifier.d.ts +5 -0
- package/dist/tools/fs/bash-readonly-classifier.js +1 -0
- package/dist/tools/fs/fs-bash.js +6 -4
- package/package.json +1 -1
|
@@ -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;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { GIT_STATUS_ECHO_PREVIEW } from "./git-status-frame.js";
|
|
2
|
+
import { wrapGitFrame } from "./git-leg-delivery.js";
|
|
3
|
+
export function runGitLane(input) {
|
|
4
|
+
const { prepared, queue, rs, ident, onMessageUpdate, onMessageEnd, onToolStart, onToolEnd, onTurnEnd, onTurnBoundary, runner } = input;
|
|
5
|
+
prepared.gitStatusRef.reassert = async () => {
|
|
6
|
+
const ref = prepared.gitStatusRef;
|
|
7
|
+
const frame = ref.frame;
|
|
8
|
+
if (frame === undefined)
|
|
9
|
+
return;
|
|
10
|
+
delete ref.mirrorOwed;
|
|
11
|
+
const use = ref.overBudgetShrunk && frame.shrunk !== undefined
|
|
12
|
+
? { kind: "degraded", body: frame.shrunk.body, hash: frame.shrunk.hash }
|
|
13
|
+
: { kind: frame.kind, body: frame.body, hash: frame.hash };
|
|
14
|
+
const wrapped = wrapGitFrame(use.body, prepared.reminderMark);
|
|
15
|
+
try {
|
|
16
|
+
const entryId = await prepared.session.appendMessage({
|
|
17
|
+
role: "user",
|
|
18
|
+
content: [{ type: "text", text: wrapped }],
|
|
19
|
+
timestamp: Date.now(),
|
|
20
|
+
engineMinted: true,
|
|
21
|
+
});
|
|
22
|
+
ref.announced = { kind: use.kind, hash: use.hash, entryId };
|
|
23
|
+
ref.protectedText = wrapped;
|
|
24
|
+
if (use.kind === "full" && frame.shrunk !== undefined) {
|
|
25
|
+
ref.wrappedShrink = { find: wrapped, replace: wrapGitFrame(frame.shrunk.body, prepared.reminderMark) };
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
delete ref.wrappedShrink;
|
|
29
|
+
}
|
|
30
|
+
queue.push({ type: "steering_injected", source: "git_status", preview: GIT_STATUS_ECHO_PREVIEW[use.kind], ...ident() });
|
|
31
|
+
rs.attach.attachmentsInjected += 1;
|
|
32
|
+
try {
|
|
33
|
+
await prepared.session.appendGitAnnouncement?.({ kind: use.kind, hash: use.hash, entryId });
|
|
34
|
+
}
|
|
35
|
+
catch (mirrorErr) {
|
|
36
|
+
try {
|
|
37
|
+
runner.deps.onError?.(new Error(`git announcement mirror write failed (frame delivered; next leg re-announces): ${mirrorErr instanceof Error ? mirrorErr.message : String(mirrorErr)}`), { phase: "degraded", sessionId: prepared.sessionId });
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
ref.announced = { kind: use.kind, hash: use.hash, pending: true };
|
|
45
|
+
try {
|
|
46
|
+
runner.deps.onError?.(new Error(`git status frame re-assert append failed (pending; retried at the next boundary): ${err instanceof Error ? err.message : String(err)}`), { phase: "degraded", sessionId: prepared.sessionId });
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
const flushGitMirror = async () => {
|
|
53
|
+
const owed = prepared.gitStatusRef.mirrorOwed;
|
|
54
|
+
if (owed === undefined)
|
|
55
|
+
return;
|
|
56
|
+
if (prepared.gitStatusRef.overBudgetShrunk && owed.kind === "full") {
|
|
57
|
+
delete prepared.gitStatusRef.mirrorOwed;
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
delete prepared.gitStatusRef.mirrorOwed;
|
|
61
|
+
try {
|
|
62
|
+
await prepared.session.appendGitAnnouncement?.(owed);
|
|
63
|
+
}
|
|
64
|
+
catch (mirrorErr) {
|
|
65
|
+
prepared.gitStatusRef.mirrorOwed = owed;
|
|
66
|
+
try {
|
|
67
|
+
runner.deps.onError?.(new Error(`git announcement mirror write failed (frame delivered; retried at the next serialization point): ${mirrorErr instanceof Error ? mirrorErr.message : String(mirrorErr)}`), { phase: "degraded", sessionId: prepared.sessionId });
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
const unsubGitRetry = prepared.harness.on("turn_boundary", async () => {
|
|
74
|
+
await flushGitMirror();
|
|
75
|
+
if (prepared.gitStatusRef.announced?.pending === true)
|
|
76
|
+
await prepared.gitStatusRef.reassert?.();
|
|
77
|
+
return undefined;
|
|
78
|
+
});
|
|
79
|
+
const unsubBoundary = prepared.harness.on("turn_boundary", onTurnBoundary);
|
|
80
|
+
const unsub = prepared.harness.subscribe((event) => {
|
|
81
|
+
switch (event.type) {
|
|
82
|
+
case "message_update":
|
|
83
|
+
onMessageUpdate(event);
|
|
84
|
+
break;
|
|
85
|
+
case "message_end":
|
|
86
|
+
onMessageEnd(event);
|
|
87
|
+
break;
|
|
88
|
+
case "tool_execution_start":
|
|
89
|
+
onToolStart(event);
|
|
90
|
+
break;
|
|
91
|
+
case "tool_execution_end":
|
|
92
|
+
onToolEnd(event);
|
|
93
|
+
break;
|
|
94
|
+
case "turn_end":
|
|
95
|
+
onTurnEnd();
|
|
96
|
+
break;
|
|
97
|
+
default:
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
return { flushGitMirror, unsubGitRetry, unsubBoundary, unsub };
|
|
102
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* design/393 S5 — the run body's IDENTITY WIRING (R1), verbatim from `Runner.runLocked`, the first thing after
|
|
3
|
+
* `prepared` exists: the notification lane's bindings (harness, session anchor, identity mint), the backstop
|
|
4
|
+
* carrier's immediate seats, the event-identity mint (`ident`) and the run's canonical task id, the wiring-manifest
|
|
5
|
+
* frame and the roster-delta subscription, the delegation-lifecycle spawn station and its owed-terminal carrier,
|
|
6
|
+
* the manual-compact mooted channel, the harness sinks for undrained engine notes / user inputs / consumed
|
|
7
|
+
* engine notes, the notify / capture-opt-out bridges, the spawner's injector hand-back, the idle-parked
|
|
8
|
+
* notification redelivery at turn open, the loop latch and the live-task handle publication, and the run's
|
|
9
|
+
* usage counters. Every push here happens before the run's first model/tool interaction, as it did.
|
|
10
|
+
*/
|
|
11
|
+
import { type AgentHarness } from "../../internal/harness.js";
|
|
12
|
+
import type { PeerInboundChainRef } from "../../agents/peer-admission.js";
|
|
13
|
+
import { type PendingSessionNotifications, type SystemInjectionPriority, type TaskNotificationPayload } from "../task-notification.js";
|
|
14
|
+
import type { PushQueue } from "../push-queue.js";
|
|
15
|
+
import type { TaskEvent, TaskSpec } from "../types.js";
|
|
16
|
+
import type { Stats } from "./assemble-result.js";
|
|
17
|
+
import type { CaptureOptOutRef, LiveHandle, ManualCompactRef, NotifyRef, Prepared, RunInternals, RunnerDepsSeat, TaskIdRef } from "./contracts.js";
|
|
18
|
+
export interface RunIdentityWiringInput {
|
|
19
|
+
/** borrowed-readonly — the task spec: the task id and the memory / notification seats it names. */
|
|
20
|
+
spec: TaskSpec;
|
|
21
|
+
/** borrowed-mutable — the run's event queue: the manifest, roster-delta, compaction_outcome and redelivered
|
|
22
|
+
* task_notification frames. */
|
|
23
|
+
queue: PushQueue<TaskEvent>;
|
|
24
|
+
/** borrowed-mutable — the leg's prepared seat: the harness sinks (`onUndrainedEngineNotes`, `engineInjectionsHeld`,
|
|
25
|
+
* `onUndrainedUserInputs`, `onEngineNoteConsumed`) are installed, `nextTurn` is appended to; the ids, the identity
|
|
26
|
+
* envelope, the hold refs, the memory session and the listing / roster faces are read. */
|
|
27
|
+
prepared: Prepared;
|
|
28
|
+
/** borrowed-readonly — the trusted run-scoped channel: `parentToolCallId`, `onNotifyInjectorReady`. */
|
|
29
|
+
internals: RunInternals | undefined;
|
|
30
|
+
/** borrowed-mutable — the stream layer's backstop carrier: the memory scopes, the edited-files reader and the
|
|
31
|
+
* owed delegation terminal are published here the moment they exist. */
|
|
32
|
+
taskIdRef: TaskIdRef | undefined;
|
|
33
|
+
/** borrowed-readonly — the stream layer's readiness callback, fired ONCE with the live-task handle. */
|
|
34
|
+
onReady: (handle: LiveHandle) => void;
|
|
35
|
+
/** borrowed-mutable — the manual /compact seat: its mooted-frame channel is installed here. */
|
|
36
|
+
manualCompactRef: ManualCompactRef;
|
|
37
|
+
/** borrowed-mutable — the notify() bridge: `inject` is bound here (absent on the public path). */
|
|
38
|
+
notifyRef: NotifyRef | undefined;
|
|
39
|
+
/** borrowed-mutable — the capture opt-out bridge: `flip` is bound here when a memory session mounted. */
|
|
40
|
+
captureOptOutRef: CaptureOptOutRef | undefined;
|
|
41
|
+
/** borrowed-readonly — the notification lane's ONE injection entry (the driver's, until the lane itself moves). */
|
|
42
|
+
injectTaskNotification: (notification: TaskNotificationPayload, opts?: {
|
|
43
|
+
priority?: SystemInjectionPriority;
|
|
44
|
+
}) => Promise<"queued" | "parked" | "dropped_duplicate">;
|
|
45
|
+
/** borrowed-mutable — the turn-open dedup set (the cross-layer double-park dedup): a redelivered frame's key is registered here. */
|
|
46
|
+
deliveredAtTurnOpen: Set<string>;
|
|
47
|
+
/** borrowed-mutable — design/176: the peer inbound chain, overwritten by each consumed peer-class payload. */
|
|
48
|
+
peerInboundChainRef: PeerInboundChainRef;
|
|
49
|
+
/** borrowed-mutable — the driver's notification-lane bindings, a VIEW over its `let`s (never a copy): this lane binds
|
|
50
|
+
* the harness, the session anchor and the identity mint the lane's closures read, and reads the anchor back in
|
|
51
|
+
* the undrained-notes sink. */
|
|
52
|
+
notificationLane: {
|
|
53
|
+
harness: AgentHarness | undefined;
|
|
54
|
+
sessionId: string | undefined;
|
|
55
|
+
ident: () => {
|
|
56
|
+
eventId?: string;
|
|
57
|
+
parentToolCallId?: string;
|
|
58
|
+
sourceTaskId?: string;
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
/** borrowed-mutable — the driver's held agent_end account (backlog #389), a view over its `let`: the undrained-inputs
|
|
62
|
+
* sink writes it when the run committed a durable park; the driver's tail reads it. */
|
|
63
|
+
undrainedUserAtEnd: {
|
|
64
|
+
current: {
|
|
65
|
+
steer: number;
|
|
66
|
+
followUp: number;
|
|
67
|
+
} | undefined;
|
|
68
|
+
};
|
|
69
|
+
/** borrowed-mutable — the Runner's per-session parked-notification store: drained at turn open, re-pended on failure. */
|
|
70
|
+
pendingSessionNotifications: PendingSessionNotifications;
|
|
71
|
+
/** borrowed-readonly — the Runner's deployment deps, read LIVE (`onDelegationLifecycle`, `onNotice`). */
|
|
72
|
+
runner: RunnerDepsSeat;
|
|
73
|
+
}
|
|
74
|
+
export interface RunIdentityWiringResult {
|
|
75
|
+
/** The run's canonical task id (`spec.taskId ?? sessionId`) — one source, shared with `ident()`. */
|
|
76
|
+
runSourceTaskId: string;
|
|
77
|
+
/** The spawning tool call's id when this task runs AS A SUB-AGENT; undefined on a top-level run. */
|
|
78
|
+
parentToolCallId: string | undefined;
|
|
79
|
+
/** The event-identity mint: a fresh eventId per event, plus the sub-agent attribution when there is one. */
|
|
80
|
+
ident: () => {
|
|
81
|
+
eventId: string;
|
|
82
|
+
parentToolCallId?: string;
|
|
83
|
+
sourceTaskId?: string;
|
|
84
|
+
};
|
|
85
|
+
/** #281 件B: the delegation-lifecycle emission closure both stations share (spawn here, terminal in the driver). */
|
|
86
|
+
emitDelegationLifecycle: (event: import("../types.js").DelegationLifecycleEvent) => void;
|
|
87
|
+
/** The loop-liveness latch and the two attribution seats (ended / userInterrupted / userHalted). */
|
|
88
|
+
loopLatch: {
|
|
89
|
+
ended: boolean;
|
|
90
|
+
userInterrupted: boolean;
|
|
91
|
+
userHalted: boolean;
|
|
92
|
+
};
|
|
93
|
+
/** This leg's usage counters, zeroed here; the harness-handlers lane is their writer from the first turn on. */
|
|
94
|
+
stats: Stats;
|
|
95
|
+
}
|
|
96
|
+
export declare function runIdentityWiring(input: RunIdentityWiringInput): RunIdentityWiringResult;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { uuidv7 } from "../../internal/harness.js";
|
|
2
|
+
import { createSafeNotifier } from "../safe-notify.js";
|
|
3
|
+
import { discloseDroppedPending, renderTaskNotificationXml, taskNotificationDedupKey } from "../task-notification.js";
|
|
4
|
+
import { deliverDelegationLifecycle, deliverEngineNotice, undrainedUserInputNotices } from "../types.js";
|
|
5
|
+
export function runIdentityWiring(input) {
|
|
6
|
+
const { spec, queue, prepared, internals, taskIdRef, onReady, manualCompactRef, notifyRef, captureOptOutRef, injectTaskNotification, deliveredAtTurnOpen, peerInboundChainRef, notificationLane, undrainedUserAtEnd, pendingSessionNotifications, runner } = input;
|
|
7
|
+
notificationLane.harness = prepared.harness;
|
|
8
|
+
notificationLane.sessionId = prepared.sessionId;
|
|
9
|
+
if (taskIdRef)
|
|
10
|
+
taskIdRef.effectiveMemoryScopes = prepared.effectiveMemoryScopes;
|
|
11
|
+
if (taskIdRef)
|
|
12
|
+
taskIdRef.editedFiles = prepared.editedFilesSnapshot;
|
|
13
|
+
const runSourceTaskId = spec.taskId ?? prepared.sessionId;
|
|
14
|
+
const parentToolCallId = internals?.parentToolCallId;
|
|
15
|
+
const ident = () => parentToolCallId !== undefined ? { eventId: uuidv7(), parentToolCallId, sourceTaskId: runSourceTaskId } : { eventId: uuidv7() };
|
|
16
|
+
notificationLane.ident = ident;
|
|
17
|
+
queue.push({ type: "wiring_manifest", manifest: prepared.wiringManifest, ...ident() });
|
|
18
|
+
prepared.toolRosterDeltas.subscribe((delta) => queue.push({ type: "tool_roster_delta", delta, ...ident() }));
|
|
19
|
+
const delegationLifecycleNotifier = createSafeNotifier({
|
|
20
|
+
onError: (f) => console.warn(`[sema-core] ${f.site}: delegation-lifecycle observer threw (contained; further failures counted, not re-disclosed): ${f.error.message}`),
|
|
21
|
+
});
|
|
22
|
+
const emitDelegationLifecycle = (event) => {
|
|
23
|
+
if (!prepared.hookIdentity.isDelegatedChild)
|
|
24
|
+
return;
|
|
25
|
+
if (runner.deps.onDelegationLifecycle === undefined)
|
|
26
|
+
return;
|
|
27
|
+
deliverDelegationLifecycle(runner.deps.onDelegationLifecycle, event, delegationLifecycleNotifier, "runtask.onDelegationLifecycle");
|
|
28
|
+
};
|
|
29
|
+
emitDelegationLifecycle({ phase: "spawn", identity: prepared.hookIdentity });
|
|
30
|
+
if (taskIdRef !== undefined && prepared.hookIdentity.isDelegatedChild && runner.deps.onDelegationLifecycle !== undefined) {
|
|
31
|
+
taskIdRef.delegationTerminalOwed = prepared.hookIdentity;
|
|
32
|
+
}
|
|
33
|
+
manualCompactRef.emitMooted = (reason) => {
|
|
34
|
+
queue.push({ type: "compaction_outcome", outcome: "mooted", trigger: "manual", reason, ...ident() });
|
|
35
|
+
};
|
|
36
|
+
prepared.harness.onUndrainedEngineNotes = (payloads) => {
|
|
37
|
+
if (notificationLane.sessionId === undefined)
|
|
38
|
+
return;
|
|
39
|
+
for (const p of payloads)
|
|
40
|
+
pendingSessionNotifications.pend(notificationLane.sessionId, p);
|
|
41
|
+
};
|
|
42
|
+
prepared.harness.engineInjectionsHeld = () => prepared.batchHaltRef.current !== undefined;
|
|
43
|
+
prepared.harness.onUndrainedUserInputs = (counts) => {
|
|
44
|
+
if (prepared.pausedRef.current !== undefined) {
|
|
45
|
+
undrainedUserAtEnd.current = counts;
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
for (const notice of undrainedUserInputNotices(counts, spec.taskId ?? prepared.sessionId, prepared.sessionId, prepared.runId)) {
|
|
49
|
+
deliverEngineNotice(runner.deps.onNotice, notice);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
prepared.harness.onEngineNoteConsumed = (p) => {
|
|
53
|
+
const peer = p?.peer;
|
|
54
|
+
if (peer !== undefined && Array.isArray(peer.hopChain))
|
|
55
|
+
peerInboundChainRef.current = [...peer.hopChain];
|
|
56
|
+
};
|
|
57
|
+
if (notifyRef)
|
|
58
|
+
notifyRef.inject = injectTaskNotification;
|
|
59
|
+
if (captureOptOutRef && prepared.memoryEngineSession?.captureOptOut !== undefined) {
|
|
60
|
+
captureOptOutRef.flip = prepared.memoryEngineSession.captureOptOut.flip;
|
|
61
|
+
}
|
|
62
|
+
try {
|
|
63
|
+
internals?.onNotifyInjectorReady?.(injectTaskNotification);
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
}
|
|
67
|
+
{
|
|
68
|
+
const pendingIdle = pendingSessionNotifications.drain(prepared.sessionId);
|
|
69
|
+
if (pendingIdle !== undefined) {
|
|
70
|
+
for (const payload of discloseDroppedPending(pendingIdle)) {
|
|
71
|
+
const parkedPriority = pendingIdle.priorities?.get(payload);
|
|
72
|
+
deliveredAtTurnOpen.add(taskNotificationDedupKey(payload));
|
|
73
|
+
queue.push({ type: "task_notification", notification: payload, ...(parkedPriority !== undefined ? { priority: parkedPriority } : {}), ...ident() });
|
|
74
|
+
void prepared.harness.nextTurn(renderTaskNotificationXml(payload), { provenance: "engine-note", enginePayload: payload }).catch(() => {
|
|
75
|
+
pendingSessionNotifications.pend(prepared.sessionId, payload, parkedPriority);
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
const loopLatch = { ended: false, userInterrupted: false, userHalted: false };
|
|
81
|
+
onReady({ harness: prepared.harness, abortController: prepared.abortController, loop: loopLatch, reminderMark: prepared.reminderMark, sessionId: prepared.sessionId, runId: prepared.runId, hookTimeoutMs: prepared.hookTimeoutMs, hookIdentity: prepared.hookIdentity });
|
|
82
|
+
const stats = { turns: 0, tokens: 0, toolCalls: 0, promptTokens: 0, totalInputTokens: 0, cachedTokens: 0, cacheWriteTokens: 0, cacheWriteTokensLong: 0, outputTokens: 0, costMicroUsd: 0 };
|
|
83
|
+
return { runSourceTaskId, parentToolCallId, ident, emitDelegationLifecycle, loopLatch, stats };
|
|
84
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* design/393 S5 — the run body's REASONING seat (R5), verbatim from `Runner.runLocked`: the leg-entry resolution of
|
|
3
|
+
* the requested reasoning intensity against the model's real capability, the ONE publisher that writes both faces
|
|
4
|
+
* (the `reasoning.resolved` trace frame and the `TaskResult.effectiveReasoning` seat on the backstop carrier), and
|
|
5
|
+
* the wire-facts consumer that lets the leg's FIRST committed request correct the cap-blind mint (once; foreign
|
|
6
|
+
* reports are contained at their call sites, not here).
|
|
7
|
+
*/
|
|
8
|
+
import { type ReasoningWireFacts, type ResolvedReasoning } from "../../brain/reasoning.js";
|
|
9
|
+
import type { Prepared, RunState, TaskIdRef } from "./contracts.js";
|
|
10
|
+
export interface RunReasoningSeatInput {
|
|
11
|
+
/** borrowed-readonly — the leg's prepared seat: the requested thinking level and the model. */
|
|
12
|
+
prepared: Prepared;
|
|
13
|
+
/** borrowed-readonly — the run's mutable state, read for the trace frames' tracer and task id. */
|
|
14
|
+
rs: RunState;
|
|
15
|
+
/** borrowed-mutable — the stream layer's backstop carrier: `effectiveReasoning` is published here at every mint. */
|
|
16
|
+
taskIdRef: TaskIdRef | undefined;
|
|
17
|
+
/** borrowed-mutable — the driver's resolution cell (the `let` the result assembly reads): the leg-entry mint and
|
|
18
|
+
* the first-committed-request correction are written here, and the dedupe reads it back. */
|
|
19
|
+
reasoningResolution: {
|
|
20
|
+
current: ResolvedReasoning | undefined;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export interface RunReasoningSeatResult {
|
|
24
|
+
/** The CONSUMPTION half of {@link ReasoningWireFacts} — installed as the third brain ALS sink. */
|
|
25
|
+
observeReasoningWireFacts: (facts: ReasoningWireFacts) => void;
|
|
26
|
+
}
|
|
27
|
+
export declare function runReasoningSeat(input: RunReasoningSeatInput): RunReasoningSeatResult;
|