@inflexa-ai/harness 0.14.2 → 0.15.1
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/README.md +2 -1
- package/dist/agents/conversation-agent.d.ts +7 -0
- package/dist/agents/conversation-agent.js +5 -3
- package/dist/agents/sandbox/drug-repurposing-agent.js +5 -1
- package/dist/agents/sandbox/immune-profiling-agent.js +4 -1
- package/dist/app/synthesize-run.d.ts +3 -0
- package/dist/app/synthesize-run.js +2 -1
- package/dist/billing/noop-usage-recorder.d.ts +7 -0
- package/dist/billing/noop-usage-recorder.js +10 -0
- package/dist/billing/usage-recorder.d.ts +63 -0
- package/dist/billing/usage-recorder.js +15 -0
- package/dist/contracts/chat-events.d.ts +44 -3
- package/dist/contracts/chat-parts.d.ts +38 -2
- package/dist/contracts/chat-parts.js +1 -1
- package/dist/contracts/data-profile.d.ts +26 -0
- package/dist/contracts/data-profile.js +26 -0
- package/dist/contracts/index.d.ts +3 -1
- package/dist/contracts/index.js +1 -0
- package/dist/contracts/message.d.ts +21 -1
- package/dist/contracts/part-registry.js +1 -0
- package/dist/contracts/schemas/chat-events.d.ts +48 -2
- package/dist/contracts/schemas/chat-events.js +8 -1
- package/dist/contracts/schemas/chat-parts.d.ts +46 -3
- package/dist/contracts/schemas/chat-parts.js +13 -0
- package/dist/contracts/schemas/index.d.ts +2 -1
- package/dist/contracts/schemas/index.js +2 -1
- package/dist/contracts/schemas/usage.d.ts +11 -0
- package/dist/contracts/schemas/usage.js +18 -0
- package/dist/contracts/usage.d.ts +51 -0
- package/dist/contracts/usage.js +41 -0
- package/dist/execution/artifact-metadata.d.ts +3 -0
- package/dist/execution/artifact-metadata.js +1 -0
- package/dist/execution/dbos-workflow-purger.d.ts +30 -0
- package/dist/execution/dbos-workflow-purger.js +193 -0
- package/dist/execution/post-step-pipeline.d.ts +3 -0
- package/dist/execution/post-step-pipeline.js +2 -0
- package/dist/execution/reconcile-manifest.js +28 -15
- package/dist/execution/report-runner.d.ts +16 -0
- package/dist/execution/report-runner.js +4 -0
- package/dist/execution/run-synthesis.d.ts +3 -0
- package/dist/execution/run-synthesis.js +2 -0
- package/dist/execution/step-summary.d.ts +3 -0
- package/dist/execution/step-summary.js +1 -0
- package/dist/execution/workflow-purger.d.ts +38 -0
- package/dist/execution/workflow-purger.js +24 -0
- package/dist/index.d.ts +24 -2
- package/dist/index.js +27 -0
- package/dist/lib/metrics.js +2 -1
- package/dist/loop/metrics.d.ts +18 -0
- package/dist/loop/metrics.js +18 -1
- package/dist/loop/run-agent.d.ts +35 -0
- package/dist/loop/run-agent.js +149 -23
- package/dist/loop/run-to-terminal.d.ts +5 -2
- package/dist/loop/run-to-terminal.js +36 -2
- package/dist/loop/tool-detail.d.ts +54 -0
- package/dist/loop/tool-detail.js +105 -0
- package/dist/loop/tool-outcome.d.ts +21 -0
- package/dist/loop/tool-outcome.js +25 -0
- package/dist/loop/types.d.ts +15 -1
- package/dist/memory/content-to-cortex.d.ts +28 -4
- package/dist/memory/content-to-cortex.js +70 -14
- package/dist/memory/thread-history.d.ts +21 -1
- package/dist/memory/thread-history.js +29 -7
- package/dist/memory/thread-store.d.ts +65 -8
- package/dist/memory/thread-store.js +81 -17
- package/dist/prompts/sandbox/bulk-transcriptomics-agent.d.ts +1 -1
- package/dist/prompts/sandbox/bulk-transcriptomics-agent.js +17 -13
- package/dist/prompts/sandbox/chromatin-agent.d.ts +1 -1
- package/dist/prompts/sandbox/chromatin-agent.js +45 -54
- package/dist/prompts/sandbox/genomic-variant-agent.d.ts +1 -1
- package/dist/prompts/sandbox/genomic-variant-agent.js +39 -37
- package/dist/prompts/sandbox-standards.d.ts +1 -1
- package/dist/prompts/sandbox-standards.js +21 -2
- package/dist/provenance/collector.d.ts +5 -3
- package/dist/provenance/collector.js +5 -3
- package/dist/providers/ai-sdk.js +91 -16
- package/dist/providers/streaming-chat.js +5 -0
- package/dist/providers/types.d.ts +23 -0
- package/dist/providers/types.js +1 -0
- package/dist/reference-data/catalog.js +536 -0
- package/dist/runtime/assemble.d.ts +25 -8
- package/dist/runtime/assemble.js +7 -4
- package/dist/sandbox/sandbox-step-translate.d.ts +22 -1
- package/dist/sandbox/sandbox-step-translate.js +23 -36
- package/dist/schemas/run-synthesis.d.ts +2 -2
- package/dist/state/index.d.ts +2 -0
- package/dist/state/index.js +3 -0
- package/dist/state/init.js +28 -0
- package/dist/state/purge-analysis.d.ts +74 -0
- package/dist/state/purge-analysis.js +205 -0
- package/dist/state/vector-store.d.ts +6 -0
- package/dist/state/vector-store.js +22 -2
- package/dist/tasks/data-profile-activity.d.ts +14 -2
- package/dist/tasks/data-profile-activity.js +1 -1
- package/dist/tasks/data-profile.d.ts +3 -0
- package/dist/tasks/data-profile.js +9 -3
- package/dist/tools/bio/pubmed.js +15 -0
- package/dist/tools/bio/search-gene.js +12 -0
- package/dist/tools/define-tool.d.ts +41 -3
- package/dist/tools/define-tool.js +4 -1
- package/dist/tools/detail-resolver.d.ts +35 -0
- package/dist/tools/detail-resolver.js +39 -0
- package/dist/tools/execute-analysis.js +3 -0
- package/dist/tools/iterate-report.d.ts +3 -0
- package/dist/tools/iterate-report.js +3 -0
- package/dist/tools/memory/update-working-memory.js +8 -0
- package/dist/tools/research/generate-analogy-report.d.ts +3 -0
- package/dist/tools/research/generate-analogy-report.js +6 -0
- package/dist/tools/research/generate-plan.d.ts +3 -0
- package/dist/tools/research/generate-plan.js +6 -0
- package/dist/tools/research/inspect-run.js +4 -0
- package/dist/tools/research/literature-reviewer.d.ts +3 -0
- package/dist/tools/research/literature-reviewer.js +6 -0
- package/dist/tools/workspace/edit-file.js +2 -0
- package/dist/tools/workspace/execute-command.js +25 -0
- package/dist/tools/workspace/grep.js +3 -0
- package/dist/tools/workspace/list-files.js +2 -0
- package/dist/tools/workspace/read-file.js +7 -0
- package/dist/tools/workspace/workspace-search.js +1 -0
- package/dist/tools/workspace/write-file.js +3 -0
- package/dist/workflows/execute-analysis.d.ts +7 -0
- package/dist/workflows/execute-analysis.js +31 -2
- package/dist/workflows/execute-target-assessment.d.ts +3 -0
- package/dist/workflows/sandbox-step.d.ts +23 -0
- package/dist/workflows/sandbox-step.js +33 -2
- package/dist/workflows/target-assessment/lib/clinical-consequence-annotator.d.ts +3 -0
- package/dist/workflows/target-assessment/lib/clinical-consequence-annotator.js +1 -0
- package/package.json +1 -1
|
@@ -66,6 +66,7 @@ import { createNoopLogger } from "../lib/console-logger.js";
|
|
|
66
66
|
import { unwrapOrThrow } from "../lib/result.js";
|
|
67
67
|
import { RunDedupCollisionError, countArtifactsForRun, insertStepExecution, loadDataProfileStatus, queryActiveRun, queryStepArtifactPaths, seedStepExecutions, setRunSynthesisOutcome, suspendAnalysis as suspendAnalysisQuery, sweepPendingStepExecutions, updateRunStatus, updateStepExecution, } from "../state/index.js";
|
|
68
68
|
import { isBudgetExceeded } from "../loop/budget-exceeded.js";
|
|
69
|
+
import { addChatUsage, hasReportedUsage } from "../loop/metrics.js";
|
|
69
70
|
import { SYNTHESIS_AGENT_ID, loadStepSummariesFromDisk } from "../execution/run-synthesis.js";
|
|
70
71
|
import { MAX_UPSTREAM_ARTIFACTS, composeStepBriefing } from "../prompts/briefing.js";
|
|
71
72
|
import { SYNTHESIS_STEP_ID, runDir, runStepDir, stepSubdir, stepWritePrefix, toSandboxPath } from "../workspace/paths.js";
|
|
@@ -319,11 +320,22 @@ export function synthesizeFindings(args) {
|
|
|
319
320
|
resolveWorkspaceRoot: deps.resolveWorkspaceRoot,
|
|
320
321
|
synthesisModel: deps.synthesisModel,
|
|
321
322
|
bioKeys: deps.bioKeys,
|
|
323
|
+
usageRecorder: deps.usageRecorder,
|
|
322
324
|
}, {
|
|
323
325
|
analysisId,
|
|
324
326
|
runId,
|
|
325
327
|
completedSteps,
|
|
326
|
-
|
|
328
|
+
// Stepped, exactly as a sandbox step's child input is. Synthesis owns a real row in the
|
|
329
|
+
// step-execution ledger (seeded and updated under `SYNTHESIS_STEP_ID` in the body below),
|
|
330
|
+
// so it lists as a step everywhere a run's steps are shown. Handing it the bare run session
|
|
331
|
+
// left every call it makes — the synthesizer loop and each sub-agent it dispatches —
|
|
332
|
+
// recorded against the run carrying no `stepId`, which made the one step in the list unable
|
|
333
|
+
// to report a figure the one that had just spent the most.
|
|
334
|
+
//
|
|
335
|
+
// It also puts the step id into the replay-stable `recordKey`, which is what it is for:
|
|
336
|
+
// synthesis and a plan step of the same run can no longer mint the same key from the same
|
|
337
|
+
// call path.
|
|
338
|
+
session: forStep(session, SYNTHESIS_STEP_ID),
|
|
327
339
|
emit,
|
|
328
340
|
onProgress: (phase, activity, extra = {}) => DBOS.writeStream("events", {
|
|
329
341
|
type: "data-synthesis-progress",
|
|
@@ -530,6 +542,7 @@ export async function runExecuteAnalysisBody(input, deps) {
|
|
|
530
542
|
failed: final.failed,
|
|
531
543
|
canceled: final.canceled,
|
|
532
544
|
budgetExceeded: final.budgetExceeded,
|
|
545
|
+
...(final.usage ? { usage: final.usage } : {}),
|
|
533
546
|
failureReason: synthesisError
|
|
534
547
|
? synthesisError instanceof Error
|
|
535
548
|
? `synthesis-failed: ${synthesisError.message}`
|
|
@@ -601,6 +614,12 @@ async function runSchedulerLoop(args) {
|
|
|
601
614
|
const canceled = new Set();
|
|
602
615
|
const inFlight = new Map();
|
|
603
616
|
const budgetExceededChildIds = new Set();
|
|
617
|
+
// Run-level usage aggregate, folded from the children's own durable results.
|
|
618
|
+
// `getResult` IS the replay-safe source: a recovered parent reads back the
|
|
619
|
+
// identical value from each child's workflow record, so the sum reproduces
|
|
620
|
+
// exactly without a second channel. A child that settled by throwing carries
|
|
621
|
+
// no result and therefore contributes nothing — same rule as its duration.
|
|
622
|
+
const runUsage = {};
|
|
604
623
|
// Child workflow ids the parent itself cancelled (budget / neverFits halt cascade). Their `getResult`
|
|
605
624
|
// rejects with a DBOS cancellation error and lands in the settlement error branch — but the step was
|
|
606
625
|
// CANCELED by us, not failed on its own merits. Keyed here (deterministic: `cancelInFlight` runs the
|
|
@@ -798,6 +817,7 @@ async function runSchedulerLoop(args) {
|
|
|
798
817
|
let stepDurationMs;
|
|
799
818
|
if (settled.kind === "result") {
|
|
800
819
|
const r = settled.result;
|
|
820
|
+
addChatUsage(runUsage, r.usage);
|
|
801
821
|
if (r.status === "complete") {
|
|
802
822
|
completed.add(settled.stepId);
|
|
803
823
|
stepRuntime.set(settled.stepId, {
|
|
@@ -912,7 +932,15 @@ async function runSchedulerLoop(args) {
|
|
|
912
932
|
});
|
|
913
933
|
}
|
|
914
934
|
await drainBudgetExceededNotifications(budgetExceededChildIds);
|
|
915
|
-
return {
|
|
935
|
+
return {
|
|
936
|
+
completed,
|
|
937
|
+
failed,
|
|
938
|
+
canceled,
|
|
939
|
+
budgetExceeded,
|
|
940
|
+
failureReason,
|
|
941
|
+
stepStates: stepRuntime,
|
|
942
|
+
...(hasReportedUsage(runUsage) ? { usage: { ...runUsage } } : {}),
|
|
943
|
+
};
|
|
916
944
|
}
|
|
917
945
|
async function drainBudgetExceededNotifications(childIds) {
|
|
918
946
|
while (true) {
|
|
@@ -1068,6 +1096,7 @@ async function collectAndComplete(args) {
|
|
|
1068
1096
|
note: `${completed.size} of ${input.steps.length} steps completed; results are partial.`,
|
|
1069
1097
|
}
|
|
1070
1098
|
: {}),
|
|
1099
|
+
...(args.usage ? { usage: args.usage } : {}),
|
|
1071
1100
|
});
|
|
1072
1101
|
}
|
|
1073
1102
|
else {
|
|
@@ -35,6 +35,7 @@ import { z } from "zod";
|
|
|
35
35
|
import type { RunSession } from "../auth/types.js";
|
|
36
36
|
import type { RunAuthorizer } from "../execution/run-authorizer.js";
|
|
37
37
|
import type { Logger } from "../lib/logger.js";
|
|
38
|
+
import type { UsageRecorder } from "../billing/usage-recorder.js";
|
|
38
39
|
import type { AgentChat } from "../providers/types.js";
|
|
39
40
|
import { TargetAssessmentInputSchema } from "./target-assessment/schemas.js";
|
|
40
41
|
import { type ProgressPhase } from "./target-assessment/progress.js";
|
|
@@ -88,6 +89,8 @@ export interface ExecuteTargetAssessmentDeps {
|
|
|
88
89
|
readonly decisionModel: string;
|
|
89
90
|
/** Billing-gateway model for synthesis agents — typically `env.TARGET_ASSESSMENT_SYNTHESIS_MODEL`. */
|
|
90
91
|
readonly synthesisModel: string;
|
|
92
|
+
/** LLM usage-accounting seam for the assessment's agent loops; omitted falls back to the no-op recorder. */
|
|
93
|
+
readonly usageRecorder?: UsageRecorder;
|
|
91
94
|
}
|
|
92
95
|
/**
|
|
93
96
|
* Register the executeTargetAssessment workflow with DBOS. Caller is
|
|
@@ -26,8 +26,10 @@
|
|
|
26
26
|
* parent (`executeAnalysis`). This child only owns its own teardown and
|
|
27
27
|
* its own self-cancel on 402.
|
|
28
28
|
*/
|
|
29
|
+
import type { TokenUsageRollup } from "@inflexa-ai/harness/contracts/usage.js";
|
|
29
30
|
import type { Pool } from "pg";
|
|
30
31
|
import type { Logger } from "../lib/logger.js";
|
|
32
|
+
import type { UsageRecorder } from "../billing/usage-recorder.js";
|
|
31
33
|
import type { AgentDefinition, LoopMessage } from "../loop/types.js";
|
|
32
34
|
import type { AgentChat, EmbeddingProvider } from "../providers/types.js";
|
|
33
35
|
import { type RunSession } from "../auth/types.js";
|
|
@@ -125,6 +127,17 @@ export interface SandboxStepResult {
|
|
|
125
127
|
readonly durationMs: number;
|
|
126
128
|
readonly finishReason: string | null;
|
|
127
129
|
readonly error: string | null;
|
|
130
|
+
/**
|
|
131
|
+
* What this step's agent loop reported spending. Absent when it reported
|
|
132
|
+
* nothing — never an all-zero figure.
|
|
133
|
+
*
|
|
134
|
+
* Durable by virtue of being the child's workflow result: the parent folds
|
|
135
|
+
* the run's aggregate from these, and a recovered parent reads back the same
|
|
136
|
+
* values, so the aggregate needs no side channel to survive replay. Optional
|
|
137
|
+
* because this is a durable shape — a child that settled under an older
|
|
138
|
+
* result shape recovers without it.
|
|
139
|
+
*/
|
|
140
|
+
readonly usage?: TokenUsageRollup;
|
|
128
141
|
}
|
|
129
142
|
/** One file in the step's terminal file tree / output listing. */
|
|
130
143
|
export interface StepFileEntry {
|
|
@@ -204,6 +217,11 @@ export interface SandboxStepDeps {
|
|
|
204
217
|
* account of why a step died.
|
|
205
218
|
*/
|
|
206
219
|
readonly logger?: Logger;
|
|
220
|
+
/**
|
|
221
|
+
* LLM usage-accounting seam. Covers this step's agent loop and the
|
|
222
|
+
* post-step sub-agent loops alike; omitted falls back to the no-op recorder.
|
|
223
|
+
*/
|
|
224
|
+
readonly usageRecorder?: UsageRecorder;
|
|
207
225
|
/** Non-streaming chat — drives the agent loop + the post-step sub-agents. */
|
|
208
226
|
readonly provider: AgentChat;
|
|
209
227
|
/** Write-side embedder for the post-step vector index. */
|
|
@@ -275,3 +293,8 @@ export declare function createLineageCollector(input: Pick<SandboxStepInput, "st
|
|
|
275
293
|
* `dbos.application_versions` without per-workflow config.
|
|
276
294
|
*/
|
|
277
295
|
export declare function registerSandboxStep(deps: SandboxStepDeps): (input: SandboxStepInput) => Promise<SandboxStepResult>;
|
|
296
|
+
/**
|
|
297
|
+
* Body extracted so tests can drive it without registering a workflow
|
|
298
|
+
* (the DBOS calls inside still rely on a workflow context being present).
|
|
299
|
+
*/
|
|
300
|
+
export declare function runSandboxStepBody(input: SandboxStepInput, deps: SandboxStepDeps): Promise<SandboxStepResult>;
|
|
@@ -34,6 +34,7 @@ import { isBudgetExceeded } from "../loop/budget-exceeded.js";
|
|
|
34
34
|
import { runAgent } from "../loop/run-agent.js";
|
|
35
35
|
import { durableStep } from "../loop/run-step.js";
|
|
36
36
|
import { activityForTool, applyTreeDelta, isChatDataPart, sandboxTreeDelta, stepPartId } from "../sandbox/sandbox-step-translate.js";
|
|
37
|
+
import { createDetailResolver } from "../tools/detail-resolver.js";
|
|
37
38
|
import { forSubAgent } from "../auth/types.js";
|
|
38
39
|
import { walkStepArtifacts } from "../execution/post-step.js";
|
|
39
40
|
import { collectStepOutputs, generateStepFileMetadata, generateStepSummaryAndWrite, reconcileAndRegisterStepArtifacts, vectorIndexStepOutputs, } from "../execution/post-step-pipeline.js";
|
|
@@ -110,7 +111,7 @@ export function registerSandboxStep(deps) {
|
|
|
110
111
|
* Body extracted so tests can drive it without registering a workflow
|
|
111
112
|
* (the DBOS calls inside still rely on a workflow context being present).
|
|
112
113
|
*/
|
|
113
|
-
async function runSandboxStepBody(input, deps) {
|
|
114
|
+
export async function runSandboxStepBody(input, deps) {
|
|
114
115
|
// Step coordinates bind once — every record below carries them without each
|
|
115
116
|
// call site restating them. REPLAY: a recovered workflow re-executes this body
|
|
116
117
|
// and re-emits its records, so a reader dedups by (runId, stepId), not by count.
|
|
@@ -193,6 +194,9 @@ async function runSandboxStepBody(input, deps) {
|
|
|
193
194
|
lineageCollector,
|
|
194
195
|
blockerHolder,
|
|
195
196
|
});
|
|
197
|
+
// Built over THIS agent's tools, not a shared table: a sandbox agent's roster
|
|
198
|
+
// differs per agent type, and each tool describes its own calls.
|
|
199
|
+
const resolveDetail = createDetailResolver(agent.tools, logger);
|
|
196
200
|
// Forward every loop event onto the child's own DBOS `"events"` stream
|
|
197
201
|
// (the route fans-in parent + every active child stream — Pattern A).
|
|
198
202
|
// Pre-shaped chat data parts (e.g. `data-sandbox-event` from `run-exec.ts`)
|
|
@@ -301,7 +305,7 @@ async function runSandboxStepBody(input, deps) {
|
|
|
301
305
|
// envelope, and surface each tool call as live step activity.
|
|
302
306
|
await safeEmit({ type: "data-loop-event", data: { stepId: input.stepId, event } });
|
|
303
307
|
if (event.type === "tool-started") {
|
|
304
|
-
await emitActivity("executing", activityForTool(event.name, event.input));
|
|
308
|
+
await emitActivity("executing", activityForTool(event.name, event.input, resolveDetail));
|
|
305
309
|
}
|
|
306
310
|
};
|
|
307
311
|
const initial = [{ role: "user", content: input.prompt }];
|
|
@@ -312,12 +316,14 @@ async function runSandboxStepBody(input, deps) {
|
|
|
312
316
|
let transcript;
|
|
313
317
|
let finishReason;
|
|
314
318
|
let hitMaxSteps;
|
|
319
|
+
let stepUsage;
|
|
315
320
|
try {
|
|
316
321
|
const agentResult = await runAgent(agent, initial, session, {
|
|
317
322
|
provider: deps.provider,
|
|
318
323
|
signal: neverAbort,
|
|
319
324
|
emit,
|
|
320
325
|
runStep: durableStep,
|
|
326
|
+
usageRecorder: deps.usageRecorder,
|
|
321
327
|
formatStepName: {
|
|
322
328
|
llm: (iteration) => `llm:${iteration}`,
|
|
323
329
|
tool: (toolName, toolUseId) => `tool:${toolName}:${toolUseId}`,
|
|
@@ -327,6 +333,7 @@ async function runSandboxStepBody(input, deps) {
|
|
|
327
333
|
transcript = agentResult.messages;
|
|
328
334
|
finishReason = agentResult.finish.reason;
|
|
329
335
|
hitMaxSteps = agentResult.finish.cappedOut;
|
|
336
|
+
stepUsage = agentResult.finish.usage;
|
|
330
337
|
}
|
|
331
338
|
catch (err) {
|
|
332
339
|
if (isBudgetExceeded(err)) {
|
|
@@ -380,6 +387,28 @@ async function runSandboxStepBody(input, deps) {
|
|
|
380
387
|
// `failStep` always throws; the outer `throw` is for control-flow analysis.
|
|
381
388
|
throw await failStep("agent_loop", err);
|
|
382
389
|
}
|
|
390
|
+
// What the step's loop spent, emitted the moment the loop is done and before
|
|
391
|
+
// any branch that can end the step — so a step that later blocks or fails its
|
|
392
|
+
// post-step pipeline has still reported what it cost.
|
|
393
|
+
//
|
|
394
|
+
// No rollup means NO PART: the part's contract is to carry a step's rollup,
|
|
395
|
+
// and a loop that reported nothing has none to carry. A part naming the step
|
|
396
|
+
// with an absent usage field would be a claim about a figure nobody made.
|
|
397
|
+
//
|
|
398
|
+
// Stable id per (runId, stepId), like every other step part here, so a
|
|
399
|
+
// DBOS-replayed body's re-emission folds latest-wins onto the one part
|
|
400
|
+
// instead of appearing twice on the run's stream.
|
|
401
|
+
if (stepUsage) {
|
|
402
|
+
await safeEmit({
|
|
403
|
+
type: "data-step-usage",
|
|
404
|
+
id: stepPartId("step-usage", input.runId, input.stepId),
|
|
405
|
+
runId: input.runId,
|
|
406
|
+
stepId: input.stepId,
|
|
407
|
+
agentId: input.agentId,
|
|
408
|
+
modelId: agent.model,
|
|
409
|
+
usage: stepUsage,
|
|
410
|
+
});
|
|
411
|
+
}
|
|
383
412
|
// Blocker (see the harness-sandbox-agents spec): the agent declared it cannot fulfil the step. A
|
|
384
413
|
// blocked step has no deliverables — skip the artifact post-step pipeline,
|
|
385
414
|
// mark `blocked` (carrying the reason), emit the blocked part, and return a
|
|
@@ -414,6 +443,7 @@ async function runSandboxStepBody(input, deps) {
|
|
|
414
443
|
durationMs,
|
|
415
444
|
finishReason,
|
|
416
445
|
error: blockerOutcome.reason,
|
|
446
|
+
...(stepUsage ? { usage: stepUsage } : {}),
|
|
417
447
|
};
|
|
418
448
|
}
|
|
419
449
|
// (4-8) post-step — the body is the explicit composition: walk the artifact
|
|
@@ -547,6 +577,7 @@ async function runSandboxStepBody(input, deps) {
|
|
|
547
577
|
durationMs,
|
|
548
578
|
finishReason,
|
|
549
579
|
error: null,
|
|
580
|
+
...(stepUsage ? { usage: stepUsage } : {}),
|
|
550
581
|
};
|
|
551
582
|
}
|
|
552
583
|
/**
|
|
@@ -19,6 +19,7 @@ import { z } from "zod";
|
|
|
19
19
|
import type { Pool } from "pg";
|
|
20
20
|
import type { AgentSession } from "../../../auth/types.js";
|
|
21
21
|
import type { ChatProvider } from "../../../providers/types.js";
|
|
22
|
+
import type { UsageRecorder } from "../../../billing/usage-recorder.js";
|
|
22
23
|
export declare const OffTargetAnnotationSchema: z.ZodObject<{
|
|
23
24
|
clinical_consequence: z.ZodString;
|
|
24
25
|
provenance: z.ZodString;
|
|
@@ -28,6 +29,8 @@ export interface ClinicalConsequenceAnnotatorDeps {
|
|
|
28
29
|
readonly provider: ChatProvider;
|
|
29
30
|
readonly session: AgentSession;
|
|
30
31
|
readonly model: string;
|
|
32
|
+
/** LLM usage-accounting seam for the annotator loop; omitted falls back to the no-op recorder. */
|
|
33
|
+
readonly usageRecorder?: UsageRecorder;
|
|
31
34
|
}
|
|
32
35
|
export interface AnnotationInput {
|
|
33
36
|
primaryTargetGene: string;
|
|
@@ -126,6 +126,7 @@ export async function annotateClinicalConsequence(pool, input, deps) {
|
|
|
126
126
|
signal: controller.signal,
|
|
127
127
|
emit: () => { },
|
|
128
128
|
runStep: passthroughStep,
|
|
129
|
+
usageRecorder: deps.usageRecorder,
|
|
129
130
|
});
|
|
130
131
|
const raw = extractJsonObject(finalText(messages));
|
|
131
132
|
if (raw === null)
|
package/package.json
CHANGED