@sema-agent/core
Version:
Stateless, task-oriented AI agent core
370 lines (369 loc) • 20.4 kB
JavaScript
import { observeThenableRejection } from "../safe-notify.js";
import { forwardSafely } from "./forward-sink.js";
import { emitTrace } from "../trace.js";
import { primaryActivityArg } from "../arg-summary.js";
import { redactSecrets } from "../untrusted-egress.js";
import { readDegradation } from "../../brain/degrading.js";
import { OUTPUT_TOOL_NAME } from "./synthetic-tools.js";
import { usageCostMicroUsd } from "./usage-accounting.js";
import { advanceCadenceClock, reduceToolEnd } from "./turn-attachments.js";
import { gatedCallIdOf } from "./park-commit.js";
import { hasVerifiableStructureSignal } from "./grounding-signal.js";
import { batchResponseDigest, toolEndBodyFrom } from "./tool-end-body.js";
import { createResponseModelSeat } from "./response-model-seat.js";
import { createToolCallIdFoldSeat } from "./tool-call-id-fold-seat.js";
import { createToolTurnIncompleteSeat } from "./tool-turn-repair-seat.js";
import { wireOf } from "../tool-name-readings.js";
import { rosterToolName } from "../tool-roster.js";
export function createHarnessHandlers(input) {
const { prepared, stats, rs, deps } = input;
const responseModelSeat = createResponseModelSeat(deps.runnerHooks.onNotice, { sessionId: prepared.sessionId, runId: prepared.runId });
const toolCallIdFoldSeat = createToolCallIdFoldSeat(deps.runnerHooks.onNotice, { sessionId: prepared.sessionId, runId: prepared.runId });
const toolTurnIncompleteSeat = createToolTurnIncompleteSeat(deps.runnerHooks.onNotice, { sessionId: prepared.sessionId, runId: prepared.runId });
const { spec, queue, internals, ident, parentToolCallId, subagentName, pushContent, emitCommitted, startedToolCallIds, toolStartAt, toolProgress, writeFamilyOf, toolLabels, postToolBatchHook, batchArgs, internalsNotifier } = deps;
let lastWorkspaceCwd = prepared.cwdRef?.current;
const announceWorkspaceMove = () => {
const cwdNow = prepared.cwdRef?.current;
if (cwdNow !== undefined && cwdNow !== lastWorkspaceCwd) {
lastWorkspaceCwd = cwdNow;
queue.push({ type: "workspace_changed", cwd: cwdNow, ...ident() });
}
};
const onMessageUpdate = (event) => {
const ev = event.assistantMessageEvent;
if (rs.turn.callStartAt === undefined)
rs.turn.callStartAt = Date.now();
if (ev.type === "text_delta" && ev.delta) {
if (rs.turn.firstTokenAt === undefined)
rs.turn.firstTokenAt = Date.now();
pushContent({ type: "text_delta", delta: ev.delta, ...ident() });
}
else if (ev.type === "text_end" && typeof ev.content === "string" && ev.content.length !== 0) {
pushContent({ type: "text_end", content: ev.content, ...ident() });
}
else if (ev.type === "thinking_delta" && ev.delta) {
pushContent({ type: "reasoning_delta", delta: ev.delta, ...ident() });
}
if (rs.budget.streamCancel && !rs.limits.budgetHit && (ev.type === "text_delta" || ev.type === "thinking_delta") && ev.delta) {
rs.budget.callOutputChars += ev.delta.length;
if (rs.budget.callOutputChars - rs.budget.lastStreamBudgetCheck >= 256) {
rs.budget.lastStreamBudgetCheck = rs.budget.callOutputChars;
const projectedAxis = rs.budget.projectedOverBudget();
if (projectedAxis !== undefined) {
rs.limits.budgetHit = "exceeded";
rs.limits.budgetAxis = projectedAxis;
void prepared.harness.abort();
prepared.abortController.abort();
}
}
}
};
const onMessageEnd = (event) => {
const m = event.message;
if (event.entryId !== undefined && (m.role === "user" || m.role === "assistant" || m.role === "toolResult")) {
emitCommitted(event.entryId, m.role, m.role === "toolResult" ? m.toolCallId : undefined);
}
if (event.entryId !== undefined && m.role === "user")
prepared.fileHistoryBoundary?.begin(event.entryId);
if (event.entryId !== undefined && m.role === "user" && prepared.frameReceipts.pending.length > 0) {
const c = m.content;
const text = typeof c === "string"
? c
: Array.isArray(c) && c.length >= 1 && c[0].type === "text"
? c[0].text
: undefined;
if (text !== undefined) {
const settled = prepared.frameReceipts.pending.filter((r) => r.text === text);
if (settled.length > 0) {
prepared.frameReceipts.pending = prepared.frameReceipts.pending.filter((r) => r.text !== text);
for (const r of settled)
r.commit(event.entryId);
}
}
}
if (rs.degrade.degraded === undefined && m.role === "assistant") {
const deg = readDegradation(m);
if (deg)
rs.degrade.recordDegraded({ from: deg.from, to: deg.to, reason: deg.reason, atTurn: stats.turns + 1, ...(deg.chain && { chain: deg.chain }) });
}
if (m.role === "assistant" && "usage" in m && m.usage) {
if (m.usageMissing) {
rs.turn.turnUsageMissing = true;
stats.usageMissing = true;
}
const u = m.usage;
stats.tokens += u.totalTokens || 0;
const { totalInputTokens: turnTotal, uncachedInputTokens: turnUncached, costMicroUsd: turnCostMicroUsd, } = usageCostMicroUsd(rs.telemetry.cacheFamily, u, rs.telemetry.pricing);
if (!rs.telemetry.pricingConfigured)
rs.telemetry.unpricedSpend = true;
stats.promptTokens += turnUncached;
stats.totalInputTokens += turnTotal;
stats.cachedTokens += u.cacheRead || 0;
stats.cacheWriteTokens += u.cacheWrite || 0;
stats.outputTokens += u.output || 0;
stats.costMicroUsd += turnCostMicroUsd;
rs.turn.turnUsage ??= { inputTokens: 0, totalInputTokens: 0, outputTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0, costMicroUsd: 0 };
rs.turn.turnUsage.inputTokens += turnUncached;
rs.turn.turnUsage.totalInputTokens += turnTotal;
rs.turn.turnUsage.outputTokens += u.output || 0;
rs.turn.turnUsage.cacheReadTokens += u.cacheRead || 0;
rs.turn.turnUsage.cacheWriteTokens += u.cacheWrite || 0;
rs.turn.turnUsage.costMicroUsd += turnCostMicroUsd;
const callEndAt = Date.now();
const callIssuedAt = prepared.callIssuedAtRef.current;
prepared.callIssuedAtRef.current = undefined;
emitTrace(rs.telemetry.tracer, () => ({
kind: "brain.call",
version: 1,
taskId: rs.telemetry.taskId,
model: m.model,
provider: m.provider,
turn: stats.turns + 1,
promptTokens: turnUncached,
totalInputTokens: turnTotal,
completionTokens: u.output || 0,
cacheRead: u.cacheRead || 0,
cacheWrite: u.cacheWrite || 0,
latencyMs: rs.turn.callStartAt !== undefined ? callEndAt - rs.turn.callStartAt : 0,
firstTokenMs: rs.turn.callStartAt !== undefined && rs.turn.firstTokenAt !== undefined ? rs.turn.firstTokenAt - rs.turn.callStartAt : undefined,
...(callIssuedAt !== undefined ? { callStartedAt: callIssuedAt } : {}),
...(rs.telemetry.pricingConfigured ? { costMicroUsd: turnCostMicroUsd } : {}),
...(m.role === "assistant" && typeof m.stopReason === "string"
? { stopReason: m.stopReason }
: {}),
ts: callEndAt,
}));
const cacheRowUnknown = m.usageMissing === true || m.stopReason === "aborted";
if (prepared.cacheBreakDetector && prepared.cacheFingerprint && !cacheRowUnknown) {
const finding = prepared.cacheBreakDetector.observe({
turn: stats.turns + 1,
systemPrompt: prepared.cacheFingerprint.systemPrompt,
tools: prepared.cacheFingerprint.tools,
modelKey: `${m.provider}:${m.model}`,
cacheRead: u.cacheRead || 0,
});
if (finding) {
rs.telemetry.cacheBreakReported = true;
deps.runnerHooks.onError?.(new Error(`prompt-cache: break at turn ${finding.turn} — cache_read ${finding.cacheReadBefore}→${finding.cacheReadAfter}; ${finding.detail}. See design/31.`), { phase: "prompt-cache", sessionId: prepared.sessionId, classification: finding.cause });
}
}
}
if (m.role === "assistant" && typeof m.stopReason === "string") {
rs.turn.turnStopReason = m.stopReason;
}
if (m.role === "assistant") {
rs.turn.turnResponseModel = responseModelSeat.observe(m);
toolCallIdFoldSeat.observe(m);
toolTurnIncompleteSeat.observe(m);
}
if (m.role === "assistant") {
const rep = m.repetition;
if (rep !== undefined) {
const observed = [
...(rep.cut !== undefined ? [{ action: "cut", e: rep.cut }] : []),
...(rep.spared ?? []).map((e) => ({ action: "spared", e })),
];
for (const { action, e } of observed) {
if (action === "cut")
rs.counters.repetitionCuts += 1;
else
rs.counters.repetitionSpared += 1;
const segment = redactSecrets(e.segment);
if (rs.counters.repetitionEvents.length < rs.counters.REPETITION_EVENTS_CAP) {
rs.counters.repetitionEvents.push({ turn: stats.turns + 1, action, rule: e.rule, period: e.period, reps: e.reps, segment });
}
emitTrace(rs.telemetry.tracer, () => ({
kind: "repetition.detected",
version: 1,
taskId: rs.telemetry.taskId,
turn: stats.turns + 1,
action,
rule: e.rule,
period: e.period,
reps: e.reps,
segment,
ts: Date.now(),
}));
}
}
}
if (m.role === "assistant") {
const content = m.content;
if (rs.attach.attachState !== undefined)
rs.counters.cadenceTurns = advanceCadenceClock(rs.attach.attachState, rs.counters.cadenceTurns, content, writeFamilyOf);
rs.turn.lastTurnHadToolCalls = Array.isArray(content) && content.some((c) => c?.type === "toolCall");
}
rs.turn.callStartAt = undefined;
rs.turn.firstTokenAt = undefined;
rs.budget.callOutputChars = 0;
rs.budget.lastStreamBudgetCheck = 0;
};
const onToolStart = (event) => {
const startAt = Date.now();
toolStartAt.set(event.toolCallId, startAt);
startedToolCallIds.add(event.toolCallId);
toolProgress.noteStart(event.toolCallId, event.toolName);
batchArgs?.set(event.toolCallId, event.args);
stats.toolCalls += 1;
if (spec.finalVerification === true && !(rs.counters.finalVerifyInjections === 0 ? rs.counters.groundingSignalPreR9 : rs.counters.groundingSignalPostR9) && hasVerifiableStructureSignal(event.args)) {
if (rs.counters.finalVerifyInjections === 0)
rs.counters.groundingSignalPreR9 = true;
else
rs.counters.groundingSignalPostR9 = true;
}
const activityArg = primaryActivityArg(event.args);
internalsNotifier.notify(() => observeThenableRejection(internals?.onActivity?.({ phase: "start", toolCallId: event.toolCallId, toolName: event.toolName, at: startAt, ...(activityArg !== undefined ? { arg: activityArg } : {}) }), internalsNotifier, "runtask.onActivity.start"), "runtask.onActivity.start");
pushContent({
type: "tool_start",
toolCallId: event.toolCallId,
toolName: event.toolName,
...(toolLabels.get(event.toolName) !== undefined ? { label: toolLabels.get(event.toolName) } : {}),
args: event.args,
...ident(),
});
};
const onToolEnd = (event) => {
const toolStarted = toolStartAt.get(event.toolCallId);
toolStartAt.delete(event.toolCallId);
toolProgress.noteEnd(event.toolCallId);
const toolNow = Date.now();
if (!rs.counters.wroteThisRun && (prepared.toolEffects.get(wireOf("verify.wroteThisRun", { toolName: rosterToolName(prepared.toolRosterDeltas.current, event.toolName) })) ?? "write") !== "read") {
rs.counters.wroteThisRun = true;
}
if (rs.attach.attachState !== undefined && !event.isError) {
const det = event.result?.details;
if (det !== undefined)
reduceToolEnd(rs.attach.attachState, det);
}
if (postToolBatchHook !== undefined) {
if (prepared.blockedToolCalls.delete(event.toolCallId) || event.notExecuted === true) {
batchArgs?.delete(event.toolCallId);
}
else {
const digest = batchResponseDigest(event.result);
rs.turn.toolBatch.push({
toolName: event.toolName,
input: batchArgs?.get(event.toolCallId),
toolCallId: event.toolCallId,
...(digest !== undefined ? { response: digest } : {}),
isError: event.isError,
});
batchArgs?.delete(event.toolCallId);
}
}
emitTrace(rs.telemetry.tracer, () => ({
kind: "tool.call",
version: 1,
taskId: rs.telemetry.taskId,
name: event.toolName,
toolCallId: event.toolCallId,
turn: stats.turns + 1,
durationMs: toolStarted !== undefined ? toolNow - toolStarted : 0,
ok: !event.isError,
ts: toolNow,
}));
internalsNotifier.notify(() => observeThenableRejection(internals?.onActivity?.({ phase: "end", toolCallId: event.toolCallId, toolName: event.toolName, isError: event.isError, at: toolNow }), internalsNotifier, "runtask.onActivity.end"), "runtask.onActivity.end");
if (prepared.lspDiagnostics && !event.isError) {
const name = event.toolName;
if (name === "Edit" || name === "Write" || name === "NotebookEdit") {
const d = event.result?.details;
const p = typeof d?.filePath === "string" ? d.filePath : typeof d?.notebookPath === "string" ? d.notebookPath : undefined;
if (p)
prepared.lspDiagnostics.nudge(p);
}
}
const gate = prepared.gateOutcomes.get(event.toolCallId);
if (gate !== undefined)
prepared.gateOutcomes.delete(event.toolCallId);
pushContent({
type: "tool_end",
toolCallId: event.toolCallId,
toolName: event.toolName,
...(toolLabels.get(event.toolName) !== undefined ? { label: toolLabels.get(event.toolName) } : {}),
isError: event.isError,
...toolEndBodyFrom(event.result, event.isError, prepared.structuredProjector, gate, prepared.abortController.signal.aborted ? gatedCallIdOf(prepared) : undefined),
...ident(),
});
announceWorkspaceMove();
if (spec.outputSchema && event.toolName === OUTPUT_TOOL_NAME) {
if (event.isError) {
rs.degrade.outputErrorStreak += 1;
if (rs.degrade.outputErrorStreak > rs.limits.outputRetryCap) {
rs.degrade.outputInvalid = true;
void prepared.harness.abort();
prepared.abortController.abort();
}
}
else {
rs.degrade.outputErrorStreak = 0;
}
}
};
const onTurnEnd = () => {
stats.turns += 1;
if (prepared.abortController.signal.aborted)
prepared.workCutRef.abortedBeforeTurnEnd = true;
const stopExtra = rs.turn.turnStopReason !== undefined ? { stopReason: rs.turn.turnStopReason } : {};
const servedExtra = rs.turn.turnResponseModel !== undefined ? { responseModel: rs.turn.turnResponseModel } : {};
const usage = rs.turn.turnUsage ?? { inputTokens: 0, totalInputTokens: 0, outputTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0, costMicroUsd: 0 };
const turnUsageUnknown = rs.turn.turnUsage === undefined || rs.turn.turnUsageMissing;
if (turnUsageUnknown)
stats.usageMissing = true;
queue.push({ type: "turn_end", usage, ...(turnUsageUnknown ? { usageMissing: true } : {}), ...stopExtra, ...servedExtra, ...ident() });
rs.turn.turnUsageMissing = false;
rs.turn.turnStopReason = undefined;
rs.turn.turnResponseModel = undefined;
if (parentToolCallId !== undefined) {
const progress = {
type: "task_progress",
taskId: rs.telemetry.taskId,
...(internals?.delegationTaskType !== undefined ? { taskType: internals.delegationTaskType } : {}),
...(internals?.cycleSeq !== undefined ? { seq: internals.cycleSeq } : {}),
...(internals?.parentTaskId !== undefined ? { parentTaskId: internals.parentTaskId } : {}),
...(subagentName ? { name: subagentName } : {}),
...(internals?.explicitAgentName !== undefined ? { spawnName: internals.explicitAgentName } : {}),
model: prepared.model.id,
usage: { totalTokens: stats.tokens, toolUses: stats.toolCalls, durationMs: Date.now() - rs.telemetry.taskStart },
status: "running",
...ident(),
};
queue.push(progress);
forwardSafely(internals, progress);
}
emitTrace(rs.telemetry.tracer, () => ({ kind: "turn.end", version: 1, taskId: rs.telemetry.taskId, turn: stats.turns, ts: Date.now() }));
rs.turn.turnUsage = undefined;
rs.turn.callStartAt = undefined;
rs.turn.firstTokenAt = undefined;
rs.budget.callOutputChars = 0;
rs.budget.lastStreamBudgetCheck = 0;
toolStartAt.clear();
toolProgress.clear();
if (rs.limits.effectiveMaxTurns !== undefined && rs.limits.effectiveMaxTurns > 0 && stats.turns >= rs.limits.effectiveMaxTurns) {
rs.limits.turnsExceeded = true;
if (prepared.suspendForResource === undefined) {
void prepared.harness.abort();
prepared.abortController.abort();
}
}
else {
if (rs.degrade.degraded === undefined &&
spec.limits?.degrade &&
rs.degrade.degradeToModel &&
rs.budget.maxCostMicroUsd !== undefined &&
stats.costMicroUsd >= spec.limits.degrade.atCostFraction * rs.budget.maxCostMicroUsd) {
void prepared.harness.setModel(rs.degrade.degradeToModel, spec.limits.degrade.to);
rs.degrade.recordDegraded({ from: prepared.model.id, to: rs.degrade.degradeToModel.id, reason: "budget", atTurn: stats.turns }, rs.degrade.degradeToModel);
}
const overAxis = rs.limits.budgetHit ? undefined : rs.budget.overBudget();
if (overAxis !== undefined) {
rs.limits.budgetHit = "exceeded";
rs.limits.budgetAxis = overAxis;
if (prepared.suspendForResource === undefined) {
void prepared.harness.abort();
prepared.abortController.abort();
}
}
}
};
return { onMessageUpdate, onMessageEnd, onToolStart, onToolEnd, onTurnEnd, announceWorkspaceMove };
}