@inflexa-ai/harness 0.14.2 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) 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/app/synthesize-run.d.ts +3 -0
  5. package/dist/app/synthesize-run.js +2 -1
  6. package/dist/billing/noop-usage-recorder.d.ts +7 -0
  7. package/dist/billing/noop-usage-recorder.js +10 -0
  8. package/dist/billing/usage-recorder.d.ts +63 -0
  9. package/dist/billing/usage-recorder.js +15 -0
  10. package/dist/contracts/chat-events.d.ts +44 -3
  11. package/dist/contracts/chat-parts.d.ts +38 -2
  12. package/dist/contracts/chat-parts.js +1 -1
  13. package/dist/contracts/data-profile.d.ts +26 -0
  14. package/dist/contracts/data-profile.js +26 -0
  15. package/dist/contracts/index.d.ts +3 -1
  16. package/dist/contracts/index.js +1 -0
  17. package/dist/contracts/message.d.ts +21 -1
  18. package/dist/contracts/part-registry.js +1 -0
  19. package/dist/contracts/schemas/chat-events.d.ts +48 -2
  20. package/dist/contracts/schemas/chat-events.js +8 -1
  21. package/dist/contracts/schemas/chat-parts.d.ts +46 -3
  22. package/dist/contracts/schemas/chat-parts.js +13 -0
  23. package/dist/contracts/schemas/index.d.ts +2 -1
  24. package/dist/contracts/schemas/index.js +2 -1
  25. package/dist/contracts/schemas/usage.d.ts +11 -0
  26. package/dist/contracts/schemas/usage.js +18 -0
  27. package/dist/contracts/usage.d.ts +51 -0
  28. package/dist/contracts/usage.js +41 -0
  29. package/dist/execution/artifact-metadata.d.ts +3 -0
  30. package/dist/execution/artifact-metadata.js +1 -0
  31. package/dist/execution/dbos-workflow-purger.d.ts +30 -0
  32. package/dist/execution/dbos-workflow-purger.js +193 -0
  33. package/dist/execution/post-step-pipeline.d.ts +3 -0
  34. package/dist/execution/post-step-pipeline.js +2 -0
  35. package/dist/execution/reconcile-manifest.js +28 -15
  36. package/dist/execution/report-runner.d.ts +16 -0
  37. package/dist/execution/report-runner.js +4 -0
  38. package/dist/execution/run-synthesis.d.ts +3 -0
  39. package/dist/execution/run-synthesis.js +2 -0
  40. package/dist/execution/step-summary.d.ts +3 -0
  41. package/dist/execution/step-summary.js +1 -0
  42. package/dist/execution/workflow-purger.d.ts +38 -0
  43. package/dist/execution/workflow-purger.js +24 -0
  44. package/dist/index.d.ts +24 -2
  45. package/dist/index.js +27 -0
  46. package/dist/lib/metrics.js +2 -1
  47. package/dist/loop/metrics.d.ts +18 -0
  48. package/dist/loop/metrics.js +18 -1
  49. package/dist/loop/run-agent.d.ts +35 -0
  50. package/dist/loop/run-agent.js +149 -23
  51. package/dist/loop/run-to-terminal.d.ts +5 -2
  52. package/dist/loop/run-to-terminal.js +36 -2
  53. package/dist/loop/tool-detail.d.ts +54 -0
  54. package/dist/loop/tool-detail.js +105 -0
  55. package/dist/loop/tool-outcome.d.ts +21 -0
  56. package/dist/loop/tool-outcome.js +25 -0
  57. package/dist/loop/types.d.ts +15 -1
  58. package/dist/memory/content-to-cortex.d.ts +28 -4
  59. package/dist/memory/content-to-cortex.js +70 -14
  60. package/dist/memory/thread-history.d.ts +21 -1
  61. package/dist/memory/thread-history.js +29 -7
  62. package/dist/memory/thread-store.d.ts +65 -8
  63. package/dist/memory/thread-store.js +81 -17
  64. package/dist/provenance/collector.d.ts +5 -3
  65. package/dist/provenance/collector.js +5 -3
  66. package/dist/providers/ai-sdk.js +91 -16
  67. package/dist/providers/streaming-chat.js +5 -0
  68. package/dist/providers/types.d.ts +23 -0
  69. package/dist/providers/types.js +1 -0
  70. package/dist/runtime/assemble.d.ts +25 -8
  71. package/dist/runtime/assemble.js +7 -4
  72. package/dist/sandbox/sandbox-step-translate.d.ts +22 -1
  73. package/dist/sandbox/sandbox-step-translate.js +23 -36
  74. package/dist/schemas/run-synthesis.d.ts +2 -2
  75. package/dist/state/index.d.ts +2 -0
  76. package/dist/state/index.js +3 -0
  77. package/dist/state/init.js +28 -0
  78. package/dist/state/purge-analysis.d.ts +74 -0
  79. package/dist/state/purge-analysis.js +205 -0
  80. package/dist/state/vector-store.d.ts +6 -0
  81. package/dist/state/vector-store.js +22 -2
  82. package/dist/tasks/data-profile-activity.d.ts +14 -2
  83. package/dist/tasks/data-profile-activity.js +1 -1
  84. package/dist/tasks/data-profile.d.ts +3 -0
  85. package/dist/tasks/data-profile.js +9 -3
  86. package/dist/tools/bio/pubmed.js +15 -0
  87. package/dist/tools/bio/search-gene.js +12 -0
  88. package/dist/tools/define-tool.d.ts +41 -3
  89. package/dist/tools/define-tool.js +4 -1
  90. package/dist/tools/detail-resolver.d.ts +35 -0
  91. package/dist/tools/detail-resolver.js +39 -0
  92. package/dist/tools/execute-analysis.js +3 -0
  93. package/dist/tools/iterate-report.d.ts +3 -0
  94. package/dist/tools/iterate-report.js +3 -0
  95. package/dist/tools/memory/update-working-memory.js +8 -0
  96. package/dist/tools/research/generate-analogy-report.d.ts +3 -0
  97. package/dist/tools/research/generate-analogy-report.js +6 -0
  98. package/dist/tools/research/generate-plan.d.ts +3 -0
  99. package/dist/tools/research/generate-plan.js +6 -0
  100. package/dist/tools/research/inspect-run.js +4 -0
  101. package/dist/tools/research/literature-reviewer.d.ts +3 -0
  102. package/dist/tools/research/literature-reviewer.js +6 -0
  103. package/dist/tools/workspace/edit-file.js +2 -0
  104. package/dist/tools/workspace/execute-command.js +25 -0
  105. package/dist/tools/workspace/grep.js +3 -0
  106. package/dist/tools/workspace/list-files.js +2 -0
  107. package/dist/tools/workspace/read-file.js +7 -0
  108. package/dist/tools/workspace/workspace-search.js +1 -0
  109. package/dist/tools/workspace/write-file.js +3 -0
  110. package/dist/workflows/execute-analysis.d.ts +7 -0
  111. package/dist/workflows/execute-analysis.js +31 -2
  112. package/dist/workflows/execute-target-assessment.d.ts +3 -0
  113. package/dist/workflows/sandbox-step.d.ts +23 -0
  114. package/dist/workflows/sandbox-step.js +33 -2
  115. package/dist/workflows/target-assessment/lib/clinical-consequence-annotator.d.ts +3 -0
  116. package/dist/workflows/target-assessment/lib/clinical-consequence-annotator.js +1 -0
  117. package/package.json +1 -1
@@ -38,6 +38,8 @@ import type { ChatProvider } from "../providers/types.js";
38
38
  import type { PreviewPublisher } from "../tools/report/preview-publisher.js";
39
39
  import type { ChromeConfig } from "../lib/chrome.js";
40
40
  import type { Logger } from "../lib/logger.js";
41
+ import type { UsageRecorder } from "../billing/usage-recorder.js";
42
+ import type { AgentRunUsage } from "../loop/metrics.js";
41
43
  export interface ReportRunnerDeps {
42
44
  /** Operational logging seam; omitted falls back to no-op. */
43
45
  readonly logger?: Logger;
@@ -50,6 +52,8 @@ export interface ReportRunnerDeps {
50
52
  /** Skills root; the report-builder gets read-only `report-html` skill tools. */
51
53
  readonly skillsDir: string;
52
54
  readonly chrome: ChromeConfig;
55
+ /** LLM usage-accounting seam for the report-builder loop; omitted falls back to the no-op recorder. */
56
+ readonly usageRecorder?: UsageRecorder;
53
57
  }
54
58
  export interface ReportRunnerOptions {
55
59
  readonly resourceId: string;
@@ -75,6 +79,18 @@ export interface ReportRunnerOptions {
75
79
  readonly signal: AbortSignal;
76
80
  /** Event sink — forwarded to the agent loop. */
77
81
  readonly emit: EmitFn;
82
+ /**
83
+ * The calling turn's usage accumulator (`ctx.turnUsage`), so the
84
+ * report-builder loop's tokens land in the turn total its root reports.
85
+ */
86
+ readonly turnUsage?: AgentRunUsage;
87
+ /**
88
+ * The dispatching tool call's id (`ctx.invocationId`), which discriminates
89
+ * the builder loop's usage record keys from those of a sibling iteration
90
+ * dispatched in the same round — same frame, same call path, same
91
+ * loop-local step names.
92
+ */
93
+ readonly invocationId?: string;
78
94
  }
79
95
  export type ReportRunnerResult = {
80
96
  ok: true;
@@ -148,6 +148,10 @@ export async function runReportIteration(deps, opts) {
148
148
  emit: opts.emit,
149
149
  runStep: passthroughStep,
150
150
  resolved: () => outcome !== undefined,
151
+ usageRecorder: deps.usageRecorder,
152
+ // Fold the builder's calls into the turn total the root loop reports.
153
+ turnUsage: opts.turnUsage,
154
+ invocationId: opts.invocationId,
151
155
  }, {
152
156
  tools: [buildReportTool, submitReportTool],
153
157
  nudge: "You stopped without calling submit_report, so this report version " +
@@ -27,6 +27,7 @@ import { type RunSynthesis } from "../schemas/run-synthesis.js";
27
27
  import type { StepSummary } from "../schemas/step-summary.js";
28
28
  import type { ChatDataPart, EmitFn } from "../loop/types.js";
29
29
  import type { ChatProvider } from "../providers/types.js";
30
+ import type { UsageRecorder } from "../billing/usage-recorder.js";
30
31
  import type { RunSession } from "../auth/types.js";
31
32
  import { type Tool } from "../tools/define-tool.js";
32
33
  import type { BioToolKeys } from "../tools/bio/keys.js";
@@ -66,6 +67,8 @@ export interface GenerateRunSynthesisInput {
66
67
  readonly model: string;
67
68
  /** API keys for the bio/chem tools the embedded literature reviewer uses. */
68
69
  readonly bioKeys: BioToolKeys;
70
+ /** LLM usage-accounting seam for the synthesizer + reviewer loops; omitted falls back to the no-op recorder. */
71
+ readonly usageRecorder?: UsageRecorder;
69
72
  /** Step summaries from the completed run. Non-empty. */
70
73
  readonly summaries: readonly StepSummary[];
71
74
  /** Plan analytical narrative for context. */
@@ -349,6 +349,7 @@ export async function generateRunSynthesis(input) {
349
349
  provider: input.provider,
350
350
  model: input.model,
351
351
  bioKeys: input.bioKeys,
352
+ usageRecorder: input.usageRecorder,
352
353
  });
353
354
  const validateTool = buildValidateTool(innerCtx);
354
355
  const submitTool = buildSubmitTool(holder, innerCtx);
@@ -370,6 +371,7 @@ export async function generateRunSynthesis(input) {
370
371
  emit,
371
372
  runStep: passthroughStep,
372
373
  resolved: () => holder.outcome !== null,
374
+ usageRecorder: input.usageRecorder,
373
375
  };
374
376
  await runToTerminal(agent, [{ role: "user", content: prompt }], input.session, loopDeps, {
375
377
  tools: [submitTool, blockerTool],
@@ -30,9 +30,12 @@ import type { AgentChat } from "../providers/types.js";
30
30
  import type { WorkspaceFilesystem } from "../workspace/filesystem.js";
31
31
  import { type StepSummary } from "../schemas/step-summary.js";
32
32
  import type { Logger } from "../lib/logger.js";
33
+ import type { UsageRecorder } from "../billing/usage-recorder.js";
33
34
  export interface GenerateStepSummaryOptions {
34
35
  /** Operational logging seam; omitted falls back to no-op. */
35
36
  readonly logger?: Logger;
37
+ /** LLM usage-accounting seam for the summary loop; omitted falls back to the no-op recorder. */
38
+ readonly usageRecorder?: UsageRecorder;
36
39
  readonly provider: AgentChat;
37
40
  readonly session: AgentSession;
38
41
  readonly modelId: string;
@@ -88,6 +88,7 @@ export async function generateStepSummary(opts) {
88
88
  signal,
89
89
  emit: () => { },
90
90
  runStep: passthroughStep,
91
+ usageRecorder: opts.usageRecorder,
91
92
  });
92
93
  }
93
94
  catch (err) {
@@ -0,0 +1,38 @@
1
+ /**
2
+ * WorkflowPurger — the durability-engine seam for reclaiming workflow rows.
3
+ *
4
+ * Most of what an analysis stores lives in the durability engine's own ledger —
5
+ * sandbox-agent step outputs, the run-event streams, workflow inputs — not in the
6
+ * harness's `cortex_*` tables, so removing an analysis has to reach that ledger.
7
+ * The engine is quarantined out of the state layer, the tools, and the loop
8
+ * (`RunLauncher` holds that line for the launch direction); this seam is the
9
+ * reclaim direction of the same quarantine. It speaks only in workflow ids, so a
10
+ * caller never names an engine type.
11
+ *
12
+ * Cancellation is a separate verb from deletion, and a caller uses it first:
13
+ * deleting the status row of a running workflow does not stop the executor
14
+ * running it, and that executor goes on writing rows behind the delete. A caller
15
+ * that cannot cancel must not proceed to a delete whose completeness it can no
16
+ * longer claim.
17
+ *
18
+ * Absence is a normal outcome throughout. A host may purge from a process that
19
+ * never launched the durable runtime, and the engine creates its schema at first
20
+ * launch — so a missing ledger means "nothing to purge", not a failure. Unknown
21
+ * ids are likewise ignored rather than rejected, which is what makes a re-run of
22
+ * an already-reclaimed purge succeed.
23
+ */
24
+ import type { ResultAsync } from "neverthrow";
25
+ import type { DbError } from "../lib/db-result.js";
26
+ export interface WorkflowPurger {
27
+ /** Workflow ids in a given id namespace (e.g. `dataprofile:{analysisId}:`). Empty when the ledger has no schema yet. */
28
+ findByIdPrefix(prefix: string): ResultAsync<string[], DbError>;
29
+ /** Cancel the given workflows so no executor keeps writing behind a purge. Unknown ids are ignored. */
30
+ cancel(workflowIds: readonly string[]): ResultAsync<void, DbError>;
31
+ /**
32
+ * Delete the given workflows, and their descendants when `includeDescendants`.
33
+ * Resolves to how many workflows the delete targeted, counted immediately before
34
+ * it — a post-fact tally is not available, because the engine owns the delete and
35
+ * reports no count of its own.
36
+ */
37
+ deleteWorkflows(workflowIds: readonly string[], includeDescendants?: boolean): ResultAsync<number, DbError>;
38
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * WorkflowPurger — the durability-engine seam for reclaiming workflow rows.
3
+ *
4
+ * Most of what an analysis stores lives in the durability engine's own ledger —
5
+ * sandbox-agent step outputs, the run-event streams, workflow inputs — not in the
6
+ * harness's `cortex_*` tables, so removing an analysis has to reach that ledger.
7
+ * The engine is quarantined out of the state layer, the tools, and the loop
8
+ * (`RunLauncher` holds that line for the launch direction); this seam is the
9
+ * reclaim direction of the same quarantine. It speaks only in workflow ids, so a
10
+ * caller never names an engine type.
11
+ *
12
+ * Cancellation is a separate verb from deletion, and a caller uses it first:
13
+ * deleting the status row of a running workflow does not stop the executor
14
+ * running it, and that executor goes on writing rows behind the delete. A caller
15
+ * that cannot cancel must not proceed to a delete whose completeness it can no
16
+ * longer claim.
17
+ *
18
+ * Absence is a normal outcome throughout. A host may purge from a process that
19
+ * never launched the durable runtime, and the engine creates its schema at first
20
+ * launch — so a missing ledger means "nothing to purge", not a failure. Unknown
21
+ * ids are likewise ignored rather than rejected, which is what makes a re-run of
22
+ * an already-reclaimed purge succeed.
23
+ */
24
+ export {};
package/dist/index.d.ts CHANGED
@@ -16,6 +16,8 @@ export { createNoopBillingResolver } from "./billing/noop-resolver.js";
16
16
  export type { ResolveBilling, ResolvableSession, BillingMap, BillingHeaders, BillingFetchResult } from "./billing/resolver.js";
17
17
  export { createNoopRunCharge } from "./billing/noop-run-charge.js";
18
18
  export type { RunCharge } from "./billing/run-charge.js";
19
+ export { createNoopUsageRecorder } from "./billing/noop-usage-recorder.js";
20
+ export type { UsageRecorder, LlmUsageRecord } from "./billing/usage-recorder.js";
19
21
  export { createNoopArtifactRegistry } from "./execution/noop-artifact-registry.js";
20
22
  export type { ArtifactRegistry, ArtifactRegistrationInput, ArtifactSyncInput, ExternalRegistrationResult } from "./execution/artifact-registry.js";
21
23
  export type { ArtifactManifestEntry } from "./schemas/artifact-manifest.js";
@@ -24,6 +26,9 @@ export { UnavailablePreviewPublisher } from "./tools/report/preview-publisher.js
24
26
  export type { PreviewPublisher, PreviewMintResult } from "./tools/report/preview-publisher.js";
25
27
  export { createDbosRunLauncher } from "./execution/dbos-run-launcher.js";
26
28
  export type { RunLauncher, LaunchOptions } from "./execution/run-launcher.js";
29
+ export { createDbosWorkflowPurger } from "./execution/dbos-workflow-purger.js";
30
+ export type { DbosWorkflowPurgerDeps } from "./execution/dbos-workflow-purger.js";
31
+ export type { WorkflowPurger } from "./execution/workflow-purger.js";
27
32
  export { defineTool, isToolError } from "./tools/define-tool.js";
28
33
  export type { Tool, ToolDefinition, ToolContext, ToolError } from "./tools/define-tool.js";
29
34
  export { createAskGateway } from "./tools/approval/gateway.js";
@@ -56,10 +61,14 @@ export { createThreadHistory } from "./memory/thread-history.js";
56
61
  export type { ThreadHistory, StoredMessage, MessagePage, RetractOutcome } from "./memory/thread-history.js";
57
62
  export { syntheticUserMessage, syntheticRecordMessage, isSyntheticUserMessage, isSyntheticRecordMessage } from "./memory/ai-sdk-message-storage.js";
58
63
  export { contentToCortexMessages } from "./memory/content-to-cortex.js";
64
+ export type { ContentToCortexOptions } from "./memory/content-to-cortex.js";
59
65
  export { createCardResolver } from "./memory/reconstruct-cards.js";
60
66
  export type { ToolCardResolver, StoredToolCallForCard } from "./memory/reconstruct-cards.js";
61
- export type { CortexChatEvent, TextDeltaEvent, ToolStartedEvent, ToolFinishedEvent, FinishEvent, ChatErrorEvent } from "./contracts/chat-events.js";
62
- export type { CortexChatPart, PresentationPart, PresentationContent, PlanPart, RunCardPart, FileReferencePart, FileReferenceEntry, RunStartedPart, DagStatePart, DagStepState, StepStatus, StepActivityPart, StepPhase, StepFileTreePart, FileTreeEntry, StepOutputPart, StepOutputFile, StepSummaryPart, StepBlockedPart, RunSynthesisPart, SynthesizedFinding, BiologicalTheme, SynthesisProgressPart, SynthesisPhase, RunCompletedPart, RunCompletedFinding, RunFailedPart, PreviewPart, DataPreviewFailedPart, } from "./contracts/chat-parts.js";
67
+ export { createDetailResolver } from "./tools/detail-resolver.js";
68
+ export type { ToolDetailResolver } from "./tools/detail-resolver.js";
69
+ export type { CortexChatEvent, TextDeltaEvent, ToolStartedEvent, ToolFinishedEvent, ToolOutcome, ToolCallDetail, FinishEvent, ChatErrorEvent, } from "./contracts/chat-events.js";
70
+ export type { TokenUsageRollup } from "./contracts/usage.js";
71
+ export type { CortexChatPart, PresentationPart, PresentationContent, PlanPart, RunCardPart, FileReferencePart, FileReferenceEntry, RunStartedPart, DagStatePart, DagStepState, StepStatus, StepActivityPart, StepPhase, StepFileTreePart, FileTreeEntry, StepOutputPart, StepOutputFile, StepSummaryPart, StepUsagePart, StepBlockedPart, RunSynthesisPart, SynthesizedFinding, BiologicalTheme, SynthesisProgressPart, SynthesisPhase, RunCompletedPart, RunCompletedFinding, RunFailedPart, PreviewPart, DataPreviewFailedPart, } from "./contracts/chat-parts.js";
63
72
  export { PART_REGISTRY, isTransient, isReconciling, isSidebarPart } from "./contracts/part-registry.js";
64
73
  export type { CortexChatPartType, PartDescriptor, PartEmitter, PartConsumer } from "./contracts/part-registry.js";
65
74
  export { launchDbos, shutdownDbos, sweepEphemeralWorkflows } from "./runtime/dbos.js";
@@ -71,10 +80,23 @@ export type { PoolConfig } from "./lib/storage.js";
71
80
  export type { Pool } from "pg";
72
81
  export { registerDataProfileWorkflow, triggerDataProfile, runDataProfile } from "./tasks/data-profile.js";
73
82
  export type { DataProfileDeps, DataProfileWorkflowInput, DataProfileTriggerDeps, DataProfileTriggerParams, DataProfileTriggerResult, } from "./tasks/data-profile.js";
83
+ /**
84
+ * The synthetic run id the data-profile workflow stamps onto its own frame — the profile has no run
85
+ * row, so this literal is the only thing that identifies its work as the profile's.
86
+ *
87
+ * An embedder needs it to READ BACK what the harness recorded: usage accounting carries one run-id
88
+ * column, so a host reporting "consumption by run" sees profile calls sitting among real runs and can
89
+ * only separate them by comparing against this value. Exported rather than documented so that
90
+ * comparison is a compile-time coupling — a host holding a copy of the string would keep compiling
91
+ * through a rename here and quietly resume reporting the profile as an unnamed run.
92
+ */
93
+ export { DATA_PROFILE_RUN_LITERAL } from "./contracts/data-profile.js";
74
94
  export { initCortexState } from "./state/init.js";
75
95
  export { upsertAnalysis } from "./state/analyses.js";
76
96
  export { clearDataProfile, loadDataProfileStatus, tryRetryDataProfile, reconcileOrphanedDataProfile } from "./state/data-profile.js";
77
97
  export type { DataProfileInputFile, DataProfileResult, DataProfileStatus } from "./state/data-profile.js";
98
+ export { createAnalysisPurge } from "./state/purge-analysis.js";
99
+ export type { AnalysisPurge, AnalysisPurgeDeps, AnalysisPurgeOutcome } from "./state/purge-analysis.js";
78
100
  export type { StagedInput } from "./execution/staged-input.js";
79
101
  export type { ResolveWorkspaceRoot } from "./workspace/paths.js";
80
102
  export { createSandboxClient } from "./sandbox/create-sandbox.js";
package/dist/index.js CHANGED
@@ -17,12 +17,20 @@ export { createLocalRunAuthorizer } from "./auth/local-run-authorizer.js";
17
17
  export { createNoopBillingResolver } from "./billing/noop-resolver.js";
18
18
  // Seam: run-level billing bracket.
19
19
  export { createNoopRunCharge } from "./billing/noop-run-charge.js";
20
+ // Seam: per-call LLM usage accounting. `record` is fire-and-forget — the loop
21
+ // neither awaits nor guards it, so a realization must not throw and must not
22
+ // block. Wired as the optional `usageRecorder` of `assembleCoreRuntime`;
23
+ // unwired, every call is dropped by `createNoopUsageRecorder`.
24
+ export { createNoopUsageRecorder } from "./billing/noop-usage-recorder.js";
20
25
  // Seam: artifact registration.
21
26
  export { createNoopArtifactRegistry } from "./execution/noop-artifact-registry.js";
22
27
  // Seam: report preview publishing.
23
28
  export { UnavailablePreviewPublisher } from "./tools/report/preview-publisher.js";
24
29
  // Run launching.
25
30
  export { createDbosRunLauncher } from "./execution/dbos-run-launcher.js";
31
+ // Seam: workflow-ledger reclamation — cancels and deletes an analysis's rows in
32
+ // the durability engine's own ledger, so no caller names an engine type.
33
+ export { createDbosWorkflowPurger } from "./execution/dbos-workflow-purger.js";
26
34
  // Tool primitive.
27
35
  export { defineTool, isToolError } from "./tools/define-tool.js";
28
36
  // Seam: tool approval (`ctx.ask`). `createAskGateway` realizes the `Ask` seam
@@ -93,6 +101,11 @@ export { createThreadHistory } from "./memory/thread-history.js";
93
101
  export { syntheticUserMessage, syntheticRecordMessage, isSyntheticUserMessage, isSyntheticRecordMessage } from "./memory/ai-sdk-message-storage.js";
94
102
  export { contentToCortexMessages } from "./memory/content-to-cortex.js";
95
103
  export { createCardResolver } from "./memory/reconstruct-cards.js";
104
+ // The call-detail resolver, the reload counterpart of the `describeCall` hook a
105
+ // tool declares. Built over a tool list the HOST supplies — its own composed
106
+ // roster, `HarnessRuntime.conversationAgent.tools` — because tools contributed
107
+ // through the host-tools seam are invisible to any map the harness holds.
108
+ export { createDetailResolver } from "./tools/detail-resolver.js";
96
109
  export { PART_REGISTRY, isTransient, isReconciling, isSidebarPart } from "./contracts/part-registry.js";
97
110
  // Embedder runtime surface — what a same-process host needs to run durable
98
111
  // workflows itself: the DBOS lifecycle, per-workflow registration + triggers,
@@ -121,6 +134,17 @@ export { createPool } from "./lib/storage.js";
121
134
  // via `tryRetryDataProfile` and then started with `runDataProfile` (the managed retry
122
135
  // route's shape — embedders mirror it).
123
136
  export { registerDataProfileWorkflow, triggerDataProfile, runDataProfile } from "./tasks/data-profile.js";
137
+ /**
138
+ * The synthetic run id the data-profile workflow stamps onto its own frame — the profile has no run
139
+ * row, so this literal is the only thing that identifies its work as the profile's.
140
+ *
141
+ * An embedder needs it to READ BACK what the harness recorded: usage accounting carries one run-id
142
+ * column, so a host reporting "consumption by run" sees profile calls sitting among real runs and can
143
+ * only separate them by comparing against this value. Exported rather than documented so that
144
+ * comparison is a compile-time coupling — a host holding a copy of the string would keep compiling
145
+ * through a rename here and quietly resume reporting the profile as an unnamed run.
146
+ */
147
+ export { DATA_PROFILE_RUN_LITERAL } from "./contracts/data-profile.js";
124
148
  // Ledger surface the embedder needs around a trigger: schema init at boot,
125
149
  // the analysis-state seed (the trigger's CAS transitions the row this creates
126
150
  // — without it every trigger reports "failed"), and run-state observation.
@@ -133,6 +157,9 @@ export { upsertAnalysis } from "./state/analyses.js";
133
157
  // analysis's input set empties (deferring on a running workflow), so the UI
134
158
  // stops advertising a profile that describes files the analysis no longer has.
135
159
  export { clearDataProfile, loadDataProfileStatus, tryRetryDataProfile, reconcileOrphanedDataProfile } from "./state/data-profile.js";
160
+ // Analysis reclamation — `purgeAnalysis` reclaims an analysis's whole Postgres
161
+ // footprint from its id alone; workspace files stay the embedder's to dispose of.
162
+ export { createAnalysisPurge } from "./state/purge-analysis.js";
136
163
  export { createSandboxClient } from "./sandbox/create-sandbox.js";
137
164
  export { MachineBudgetSchema, ResourceLimitsSchema, ResourcePolicySchema, ResourceSpecSchema, parseResourcePolicy } from "./config/resource-limits.js";
138
165
  export { createWorkspaceFilesystem } from "./workspace/filesystem.js";
@@ -14,5 +14,6 @@ export const artifactReconcileDropped = meter.createCounter("cortex.artifact.rec
14
14
  export const lineageInputDropped = meter.createCounter("cortex.artifact.reconcile.input_dropped", {
15
15
  description: "Tracked input reads dropped from lineage at reconcile because they are " +
16
16
  "not content-attestable files of the analysis (directory reads, " +
17
- "out-of-tree resolutions). Tagged by agent_id, step_id, reason.",
17
+ "out-of-tree resolutions, paths absent at reconcile). Tagged by " +
18
+ "agent_id, step_id, reason.",
18
19
  });
@@ -28,20 +28,38 @@
28
28
  * `MeterProvider` is registered globally at runtime (production: the OTLP
29
29
  * exporter from `harness/lib/otel.ts`; tests: an in-memory reader).
30
30
  */
31
+ import { type TokenUsageRollup } from "../contracts/usage.js";
31
32
  import type { ChatUsage } from "../providers/types.js";
32
33
  /**
33
34
  * Token usage summed over every LLM call one `runAgent` made. Each field stays
34
35
  * `undefined` until some call actually reports it, so "the provider told us
35
36
  * nothing" never masquerades as a measured zero.
37
+ *
38
+ * The fold is wider than the instruments: `reasoningTokens` is carried for the
39
+ * usage records and the finish rollups, and deliberately has no OTel counter —
40
+ * whether a provider counts reasoning inside `outputTokens` varies, so a
41
+ * cross-agent counter would sum figures that do not mean the same thing.
36
42
  */
37
43
  export interface AgentRunUsage {
38
44
  inputTokens?: number;
39
45
  outputTokens?: number;
40
46
  cacheCreationInputTokens?: number;
41
47
  cacheReadInputTokens?: number;
48
+ reasoningTokens?: number;
42
49
  }
43
50
  /** Fold one call's reported usage into a run's running total. */
44
51
  export declare function addChatUsage(total: AgentRunUsage, usage: ChatUsage | undefined): void;
52
+ /**
53
+ * Whether anything was ever folded in. An accumulator no call reported into
54
+ * stays empty, and the surfaces that carry a rollup must then carry none at
55
+ * all rather than an all-zero figure.
56
+ *
57
+ * It also accepts the wire-shaped `TokenUsageRollup`, so a surface that RECEIVES
58
+ * a rollup (thread history storing a turn's) decides "reported nothing" with the
59
+ * same predicate the loop used to decide whether to emit one. Restating the rule
60
+ * at either end is how the two would come to disagree.
61
+ */
62
+ export declare function hasReportedUsage(usage: AgentRunUsage | ChatUsage | TokenUsageRollup | undefined): boolean;
45
63
  /** Record one completed `runAgent` invocation. */
46
64
  export declare function recordAgentRun(run: {
47
65
  readonly agentId: string;
@@ -29,6 +29,7 @@
29
29
  * exporter from `harness/lib/otel.ts`; tests: an in-memory reader).
30
30
  */
31
31
  import { metrics } from "@opentelemetry/api";
32
+ import { TOKEN_USAGE_FIELDS } from "../contracts/usage.js";
32
33
  let instruments;
33
34
  function getInstruments() {
34
35
  if (instruments === undefined) {
@@ -61,16 +62,32 @@ function getInstruments() {
61
62
  }
62
63
  return instruments;
63
64
  }
65
+ const _assertRunUsageFields = true;
64
66
  /** Fold one call's reported usage into a run's running total. */
65
67
  export function addChatUsage(total, usage) {
66
68
  if (usage === undefined)
67
69
  return;
68
- for (const key of ["inputTokens", "outputTokens", "cacheCreationInputTokens", "cacheReadInputTokens"]) {
70
+ for (const key of TOKEN_USAGE_FIELDS) {
69
71
  const value = usage[key];
70
72
  if (value !== undefined)
71
73
  total[key] = (total[key] ?? 0) + value;
72
74
  }
73
75
  }
76
+ /**
77
+ * Whether anything was ever folded in. An accumulator no call reported into
78
+ * stays empty, and the surfaces that carry a rollup must then carry none at
79
+ * all rather than an all-zero figure.
80
+ *
81
+ * It also accepts the wire-shaped `TokenUsageRollup`, so a surface that RECEIVES
82
+ * a rollup (thread history storing a turn's) decides "reported nothing" with the
83
+ * same predicate the loop used to decide whether to emit one. Restating the rule
84
+ * at either end is how the two would come to disagree.
85
+ */
86
+ export function hasReportedUsage(usage) {
87
+ if (usage === undefined)
88
+ return false;
89
+ return Object.values(usage).some((value) => value !== undefined);
90
+ }
74
91
  /** Record one completed `runAgent` invocation. */
75
92
  export function recordAgentRun(run) {
76
93
  const { iterations, capHits, inputTokens, outputTokens, cacheReadTokens, cacheWriteTokens } = getInstruments();
@@ -1,13 +1,26 @@
1
1
  import { type FinishReason } from "ai";
2
2
  import type { AgentSession } from "../auth/types.js";
3
+ import type { UsageRecorder } from "../billing/usage-recorder.js";
3
4
  import type { Logger } from "../lib/logger.js";
4
5
  import type { AgentChat, ChatRequest, PromptCachePolicy } from "../providers/types.js";
5
6
  import { type AskApproval, type AskRequest } from "../tools/approval/contract.js";
7
+ import { type AgentRunUsage } from "./metrics.js";
6
8
  import type { AgentDefinition, EmitFn, LoopMessage, RunStep } from "./types.js";
7
9
  export interface AgentFinish {
8
10
  readonly reason: FinishReason | "aborted" | "max_iterations" | "denied";
9
11
  readonly cappedOut: boolean;
10
12
  readonly truncationRecoveries: number;
13
+ /**
14
+ * What this loop's own LLM calls reported, the forced wrap-up included.
15
+ * Absent when no call reported anything — never an all-zero figure.
16
+ */
17
+ readonly usage?: AgentRunUsage;
18
+ /**
19
+ * The whole turn's total, every descendant loop included. Present only on
20
+ * the loop that created the turn accumulator — the run whose options
21
+ * carried none, which is by construction the turn's root.
22
+ */
23
+ readonly turnUsage?: AgentRunUsage;
11
24
  }
12
25
  export interface RunAgentResult {
13
26
  readonly messages: LoopMessage[];
@@ -67,6 +80,28 @@ export interface RunAgentOptions {
67
80
  * progress is emitted and then dropped. This is where it survives.
68
81
  */
69
82
  readonly logger?: Logger;
83
+ /**
84
+ * Per-call LLM usage-accounting seam. Omitted falls back to the no-op
85
+ * recorder. Delivery is fire-and-forget by contract — the loop neither
86
+ * awaits `record` nor guards it, so a realization must not throw or block.
87
+ */
88
+ readonly usageRecorder?: UsageRecorder;
89
+ /**
90
+ * The turn's usage accumulator, supplied by whatever ran this loop as part
91
+ * of a larger turn (a sub-agent-running tool passes `ctx.turnUsage`). A run
92
+ * that receives none creates its own and is therefore the turn's root: it
93
+ * alone reports `turnUsage` on its finish.
94
+ */
95
+ readonly turnUsage?: AgentRunUsage;
96
+ /**
97
+ * The id of the tool call this loop runs inside, supplied by a
98
+ * sub-agent-running tool from its `ctx.invocationId`. It is what separates
99
+ * two dispatches of the *same* sub-agent in one round — they share the run
100
+ * frame, the call path, and every loop-local step name — in the usage
101
+ * record key. Absent for a loop that is not nested inside a tool dispatch
102
+ * (a chat turn's root, a workflow step body, a background task).
103
+ */
104
+ readonly invocationId?: string;
70
105
  }
71
106
  export declare function runAgent(agent: AgentDefinition, initial: readonly LoopMessage[], session: AgentSession, opts: RunAgentOptions): Promise<RunAgentResult>;
72
107
  export declare function finalText(messages: readonly LoopMessage[]): string;