@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.
Files changed (128) hide show
  1. package/README.md +2 -1
  2. package/dist/agents/conversation-agent.d.ts +7 -0
  3. package/dist/agents/conversation-agent.js +5 -3
  4. package/dist/agents/sandbox/drug-repurposing-agent.js +5 -1
  5. package/dist/agents/sandbox/immune-profiling-agent.js +4 -1
  6. package/dist/app/synthesize-run.d.ts +3 -0
  7. package/dist/app/synthesize-run.js +2 -1
  8. package/dist/billing/noop-usage-recorder.d.ts +7 -0
  9. package/dist/billing/noop-usage-recorder.js +10 -0
  10. package/dist/billing/usage-recorder.d.ts +63 -0
  11. package/dist/billing/usage-recorder.js +15 -0
  12. package/dist/contracts/chat-events.d.ts +44 -3
  13. package/dist/contracts/chat-parts.d.ts +38 -2
  14. package/dist/contracts/chat-parts.js +1 -1
  15. package/dist/contracts/data-profile.d.ts +26 -0
  16. package/dist/contracts/data-profile.js +26 -0
  17. package/dist/contracts/index.d.ts +3 -1
  18. package/dist/contracts/index.js +1 -0
  19. package/dist/contracts/message.d.ts +21 -1
  20. package/dist/contracts/part-registry.js +1 -0
  21. package/dist/contracts/schemas/chat-events.d.ts +48 -2
  22. package/dist/contracts/schemas/chat-events.js +8 -1
  23. package/dist/contracts/schemas/chat-parts.d.ts +46 -3
  24. package/dist/contracts/schemas/chat-parts.js +13 -0
  25. package/dist/contracts/schemas/index.d.ts +2 -1
  26. package/dist/contracts/schemas/index.js +2 -1
  27. package/dist/contracts/schemas/usage.d.ts +11 -0
  28. package/dist/contracts/schemas/usage.js +18 -0
  29. package/dist/contracts/usage.d.ts +51 -0
  30. package/dist/contracts/usage.js +41 -0
  31. package/dist/execution/artifact-metadata.d.ts +3 -0
  32. package/dist/execution/artifact-metadata.js +1 -0
  33. package/dist/execution/dbos-workflow-purger.d.ts +30 -0
  34. package/dist/execution/dbos-workflow-purger.js +193 -0
  35. package/dist/execution/post-step-pipeline.d.ts +3 -0
  36. package/dist/execution/post-step-pipeline.js +2 -0
  37. package/dist/execution/reconcile-manifest.js +28 -15
  38. package/dist/execution/report-runner.d.ts +16 -0
  39. package/dist/execution/report-runner.js +4 -0
  40. package/dist/execution/run-synthesis.d.ts +3 -0
  41. package/dist/execution/run-synthesis.js +2 -0
  42. package/dist/execution/step-summary.d.ts +3 -0
  43. package/dist/execution/step-summary.js +1 -0
  44. package/dist/execution/workflow-purger.d.ts +38 -0
  45. package/dist/execution/workflow-purger.js +24 -0
  46. package/dist/index.d.ts +24 -2
  47. package/dist/index.js +27 -0
  48. package/dist/lib/metrics.js +2 -1
  49. package/dist/loop/metrics.d.ts +18 -0
  50. package/dist/loop/metrics.js +18 -1
  51. package/dist/loop/run-agent.d.ts +35 -0
  52. package/dist/loop/run-agent.js +149 -23
  53. package/dist/loop/run-to-terminal.d.ts +5 -2
  54. package/dist/loop/run-to-terminal.js +36 -2
  55. package/dist/loop/tool-detail.d.ts +54 -0
  56. package/dist/loop/tool-detail.js +105 -0
  57. package/dist/loop/tool-outcome.d.ts +21 -0
  58. package/dist/loop/tool-outcome.js +25 -0
  59. package/dist/loop/types.d.ts +15 -1
  60. package/dist/memory/content-to-cortex.d.ts +28 -4
  61. package/dist/memory/content-to-cortex.js +70 -14
  62. package/dist/memory/thread-history.d.ts +21 -1
  63. package/dist/memory/thread-history.js +29 -7
  64. package/dist/memory/thread-store.d.ts +65 -8
  65. package/dist/memory/thread-store.js +81 -17
  66. package/dist/prompts/sandbox/bulk-transcriptomics-agent.d.ts +1 -1
  67. package/dist/prompts/sandbox/bulk-transcriptomics-agent.js +17 -13
  68. package/dist/prompts/sandbox/chromatin-agent.d.ts +1 -1
  69. package/dist/prompts/sandbox/chromatin-agent.js +45 -54
  70. package/dist/prompts/sandbox/genomic-variant-agent.d.ts +1 -1
  71. package/dist/prompts/sandbox/genomic-variant-agent.js +39 -37
  72. package/dist/prompts/sandbox-standards.d.ts +1 -1
  73. package/dist/prompts/sandbox-standards.js +21 -2
  74. package/dist/provenance/collector.d.ts +5 -3
  75. package/dist/provenance/collector.js +5 -3
  76. package/dist/providers/ai-sdk.js +91 -16
  77. package/dist/providers/streaming-chat.js +5 -0
  78. package/dist/providers/types.d.ts +23 -0
  79. package/dist/providers/types.js +1 -0
  80. package/dist/reference-data/catalog.js +536 -0
  81. package/dist/runtime/assemble.d.ts +25 -8
  82. package/dist/runtime/assemble.js +7 -4
  83. package/dist/sandbox/sandbox-step-translate.d.ts +22 -1
  84. package/dist/sandbox/sandbox-step-translate.js +23 -36
  85. package/dist/schemas/run-synthesis.d.ts +2 -2
  86. package/dist/state/index.d.ts +2 -0
  87. package/dist/state/index.js +3 -0
  88. package/dist/state/init.js +28 -0
  89. package/dist/state/purge-analysis.d.ts +74 -0
  90. package/dist/state/purge-analysis.js +205 -0
  91. package/dist/state/vector-store.d.ts +6 -0
  92. package/dist/state/vector-store.js +22 -2
  93. package/dist/tasks/data-profile-activity.d.ts +14 -2
  94. package/dist/tasks/data-profile-activity.js +1 -1
  95. package/dist/tasks/data-profile.d.ts +3 -0
  96. package/dist/tasks/data-profile.js +9 -3
  97. package/dist/tools/bio/pubmed.js +15 -0
  98. package/dist/tools/bio/search-gene.js +12 -0
  99. package/dist/tools/define-tool.d.ts +41 -3
  100. package/dist/tools/define-tool.js +4 -1
  101. package/dist/tools/detail-resolver.d.ts +35 -0
  102. package/dist/tools/detail-resolver.js +39 -0
  103. package/dist/tools/execute-analysis.js +3 -0
  104. package/dist/tools/iterate-report.d.ts +3 -0
  105. package/dist/tools/iterate-report.js +3 -0
  106. package/dist/tools/memory/update-working-memory.js +8 -0
  107. package/dist/tools/research/generate-analogy-report.d.ts +3 -0
  108. package/dist/tools/research/generate-analogy-report.js +6 -0
  109. package/dist/tools/research/generate-plan.d.ts +3 -0
  110. package/dist/tools/research/generate-plan.js +6 -0
  111. package/dist/tools/research/inspect-run.js +4 -0
  112. package/dist/tools/research/literature-reviewer.d.ts +3 -0
  113. package/dist/tools/research/literature-reviewer.js +6 -0
  114. package/dist/tools/workspace/edit-file.js +2 -0
  115. package/dist/tools/workspace/execute-command.js +25 -0
  116. package/dist/tools/workspace/grep.js +3 -0
  117. package/dist/tools/workspace/list-files.js +2 -0
  118. package/dist/tools/workspace/read-file.js +7 -0
  119. package/dist/tools/workspace/workspace-search.js +1 -0
  120. package/dist/tools/workspace/write-file.js +3 -0
  121. package/dist/workflows/execute-analysis.d.ts +7 -0
  122. package/dist/workflows/execute-analysis.js +31 -2
  123. package/dist/workflows/execute-target-assessment.d.ts +3 -0
  124. package/dist/workflows/sandbox-step.d.ts +23 -0
  125. package/dist/workflows/sandbox-step.js +33 -2
  126. package/dist/workflows/target-assessment/lib/clinical-consequence-annotator.d.ts +3 -0
  127. package/dist/workflows/target-assessment/lib/clinical-consequence-annotator.js +1 -0
  128. 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
- session,
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 { completed, failed, canceled, budgetExceeded, failureReason, stepStates: stepRuntime };
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inflexa-ai/harness",
3
- "version": "0.14.2",
3
+ "version": "0.15.1",
4
4
  "description": "Host-agnostic bioinformatics agent harness \u2014 hand-rolled agent loop, DBOS-durable workflows, sandboxed compute.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",