Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .engineering
Submodule .engineering updated 96 files
+113 βˆ’0 artifacts/comprehension/delivery-ledger.md
+26 βˆ’0 artifacts/comprehension/portfolio-audience-pipeline-pedagogy.md
+29 βˆ’0 artifacts/comprehension/portfolio-audience-pipeline-rejected-paths.md
+41 βˆ’0 artifacts/comprehension/portfolio-audience-pipeline-synthesis.md
+147 βˆ’0 artifacts/comprehension/technique-output-audience-pipeline.md
+1 βˆ’0 artifacts/planning/2026-07-12-work-package-cluster3-delivery-ledger/.session-token
+27 βˆ’0 artifacts/planning/2026-07-12-work-package-cluster3-delivery-ledger/02-assumptions-log.md
+81 βˆ’0 artifacts/planning/2026-07-12-work-package-cluster3-delivery-ledger/02-design-philosophy.md
+43 βˆ’0 artifacts/planning/2026-07-12-work-package-cluster3-delivery-ledger/06-test-plan.md
+149 βˆ’0 artifacts/planning/2026-07-12-work-package-cluster3-delivery-ledger/06-work-package-plan.md
+18 βˆ’0 artifacts/planning/2026-07-12-work-package-cluster3-delivery-ledger/08-provenance-log.md
+17 βˆ’0 artifacts/planning/2026-07-12-work-package-cluster3-delivery-ledger/09-debt-ledger.md
+38 βˆ’0 artifacts/planning/2026-07-12-work-package-cluster3-delivery-ledger/09-review-findings.md
+22 βˆ’0 artifacts/planning/2026-07-12-work-package-cluster3-delivery-ledger/10-architecture-summary.md
+30 βˆ’0 artifacts/planning/2026-07-12-work-package-cluster3-delivery-ledger/10-change-block-index.md
+40 βˆ’0 artifacts/planning/2026-07-12-work-package-cluster3-delivery-ledger/10-code-review.md
+22 βˆ’0 artifacts/planning/2026-07-12-work-package-cluster3-delivery-ledger/10-manual-diff-review.md
+27 βˆ’0 artifacts/planning/2026-07-12-work-package-cluster3-delivery-ledger/10-structural-analysis.md
+44 βˆ’0 artifacts/planning/2026-07-12-work-package-cluster3-delivery-ledger/10-test-suite-review.md
+71 βˆ’0 artifacts/planning/2026-07-12-work-package-cluster3-delivery-ledger/11-validation.md
+59 βˆ’0 artifacts/planning/2026-07-12-work-package-cluster3-delivery-ledger/12-strategic-review.md
+54 βˆ’0 artifacts/planning/2026-07-12-work-package-cluster3-delivery-ledger/README.md
+603 βˆ’0 artifacts/planning/2026-07-12-work-package-cluster3-delivery-ledger/session.json
+1 βˆ’0 artifacts/planning/2026-07-13-work-package-224-audience-attribute/.session-token
+27 βˆ’0 artifacts/planning/2026-07-13-work-package-224-audience-attribute/01-assumptions-log.md
+71 βˆ’0 artifacts/planning/2026-07-13-work-package-224-audience-attribute/02-design-philosophy.md
+50 βˆ’0 artifacts/planning/2026-07-13-work-package-224-audience-attribute/06-test-plan.md
+95 βˆ’0 artifacts/planning/2026-07-13-work-package-224-audience-attribute/06-work-package-plan.md
+21 βˆ’0 artifacts/planning/2026-07-13-work-package-224-audience-attribute/08-provenance-log.md
+123 βˆ’0 artifacts/planning/2026-07-13-work-package-224-audience-attribute/09-code-review.md
+30 βˆ’0 artifacts/planning/2026-07-13-work-package-224-audience-attribute/09-debt-ledger.md
+44 βˆ’0 artifacts/planning/2026-07-13-work-package-224-audience-attribute/10-architecture-summary.md
+79 βˆ’0 artifacts/planning/2026-07-13-work-package-224-audience-attribute/10-change-block-index.md
+73 βˆ’0 artifacts/planning/2026-07-13-work-package-224-audience-attribute/10-test-suite-review.md
+44 βˆ’0 artifacts/planning/2026-07-13-work-package-224-audience-attribute/12-strategic-review-1.md
+32 βˆ’0 artifacts/planning/2026-07-13-work-package-224-audience-attribute/13-2026-07-14-pr227-review-analysis.md
+42 βˆ’0 artifacts/planning/2026-07-13-work-package-224-audience-attribute/14-COMPLETE.md
+33 βˆ’0 artifacts/planning/2026-07-13-work-package-224-audience-attribute/15-codebase-comprehension.md
+50 βˆ’0 artifacts/planning/2026-07-13-work-package-224-audience-attribute/README.md
+1,884 βˆ’0 artifacts/planning/2026-07-13-work-package-224-audience-attribute/session.json
+1 βˆ’0 artifacts/planning/2026-07-13-workflow-design-224-verbosity-pr2/.session-token
+25 βˆ’0 artifacts/planning/2026-07-13-workflow-design-224-verbosity-pr2/03-assumptions-log.md
+64 βˆ’0 artifacts/planning/2026-07-13-workflow-design-224-verbosity-pr2/05-impact-analysis.md
+29 βˆ’0 artifacts/planning/2026-07-13-workflow-design-224-verbosity-pr2/06-draft-review.md
+11 βˆ’0 artifacts/planning/2026-07-13-workflow-design-224-verbosity-pr2/10-post-update-review.md
+48 βˆ’0 artifacts/planning/2026-07-13-workflow-design-224-verbosity-pr2/11-COMPLETE.md
+69 βˆ’0 artifacts/planning/2026-07-13-workflow-design-224-verbosity-pr2/README.md
+1,113 βˆ’0 artifacts/planning/2026-07-13-workflow-design-224-verbosity-pr2/session.json
+1 βˆ’0 artifacts/planning/2026-07-14-midnight-system-review-workflow/.session-token
+33 βˆ’0 artifacts/planning/2026-07-14-midnight-system-review-workflow/03-assumptions-log.md
+82 βˆ’0 artifacts/planning/2026-07-14-midnight-system-review-workflow/04-pattern-analysis.md
+19 βˆ’0 artifacts/planning/2026-07-14-midnight-system-review-workflow/06-draft-attestation.md
+42 βˆ’0 artifacts/planning/2026-07-14-midnight-system-review-workflow/08-quality-review.md
+55 βˆ’0 artifacts/planning/2026-07-14-midnight-system-review-workflow/11-COMPLETE.md
+117 βˆ’0 artifacts/planning/2026-07-14-midnight-system-review-workflow/README.md
+1,940 βˆ’0 artifacts/planning/2026-07-14-midnight-system-review-workflow/session.json
+1 βˆ’0 artifacts/planning/2026-07-14-msr-blind-run-pr1849/.session-token
+62 βˆ’0 artifacts/planning/2026-07-14-msr-blind-run-pr1849/01-change-surface.md
+350 βˆ’0 artifacts/planning/2026-07-14-msr-blind-run-pr1849/02-investigation-plan.md
+393 βˆ’0 artifacts/planning/2026-07-14-msr-blind-run-pr1849/03-evidence-log.md
+160 βˆ’0 artifacts/planning/2026-07-14-msr-blind-run-pr1849/04-findings-register.md
+118 βˆ’0 artifacts/planning/2026-07-14-msr-blind-run-pr1849/05-review-report.md
+93 βˆ’0 artifacts/planning/2026-07-14-msr-blind-run-pr1849/06-jina-comparison.md
+21 βˆ’0 artifacts/planning/2026-07-14-msr-blind-run-pr1849/README.md
+366 βˆ’0 artifacts/planning/2026-07-14-msr-blind-run-pr1849/session.json
+1 βˆ’0 artifacts/planning/2026-07-14-review-midnight-node-pr-1807/.session-token
+29 βˆ’0 artifacts/planning/2026-07-14-review-midnight-node-pr-1807/01-prior-feedback-triage.md
+70 βˆ’0 artifacts/planning/2026-07-14-review-midnight-node-pr-1807/02-design-philosophy.md
+54 βˆ’0 artifacts/planning/2026-07-14-review-midnight-node-pr-1807/03-assumptions-log.md
+21 βˆ’0 artifacts/planning/2026-07-14-review-midnight-node-pr-1807/06-test-plan.md
+91 βˆ’0 artifacts/planning/2026-07-14-review-midnight-node-pr-1807/06-work-package-plan.md
+165 βˆ’0 artifacts/planning/2026-07-14-review-midnight-node-pr-1807/09-code-review.md
+59 βˆ’0 artifacts/planning/2026-07-14-review-midnight-node-pr-1807/10-architecture-summary.md
+42 βˆ’0 artifacts/planning/2026-07-14-review-midnight-node-pr-1807/10-change-block-index.md
+50 βˆ’0 artifacts/planning/2026-07-14-review-midnight-node-pr-1807/10-test-suite-review.md
+62 βˆ’0 artifacts/planning/2026-07-14-review-midnight-node-pr-1807/12-strategic-review-1.md
+104 βˆ’0 artifacts/planning/2026-07-14-review-midnight-node-pr-1807/13-review-summary.md
+22 βˆ’0 artifacts/planning/2026-07-14-review-midnight-node-pr-1807/14-completion-summary.md
+109 βˆ’0 artifacts/planning/2026-07-14-review-midnight-node-pr-1807/15-local-environment-tooling.md
+46 βˆ’0 artifacts/planning/2026-07-14-review-midnight-node-pr-1807/README.md
+11 βˆ’0 artifacts/planning/2026-07-14-review-midnight-node-pr-1807/debt-ledger.md
+29 βˆ’0 artifacts/planning/2026-07-14-review-midnight-node-pr-1807/portfolio-pedagogy.md
+33 βˆ’0 artifacts/planning/2026-07-14-review-midnight-node-pr-1807/portfolio-rejected-paths.md
+28 βˆ’0 artifacts/planning/2026-07-14-review-midnight-node-pr-1807/portfolio-synthesis.md
+1,785 βˆ’0 artifacts/planning/2026-07-14-review-midnight-node-pr-1807/session.json
+1 βˆ’0 artifacts/planning/2026-07-14-token-use-tracking/.session-token
+51 βˆ’0 artifacts/planning/2026-07-14-token-use-tracking/02-assumptions-log.md
+65 βˆ’0 artifacts/planning/2026-07-14-token-use-tracking/02-design-philosophy.md
+105 βˆ’0 artifacts/planning/2026-07-14-token-use-tracking/03-requirements-elicitation.md
+152 βˆ’0 artifacts/planning/2026-07-14-token-use-tracking/04-kb-research.md
+286 βˆ’0 artifacts/planning/2026-07-14-token-use-tracking/05-implementation-analysis.md
+52 βˆ’0 artifacts/planning/2026-07-14-token-use-tracking/06-test-plan.md
+131 βˆ’0 artifacts/planning/2026-07-14-token-use-tracking/06-work-package-plan.md
+49 βˆ’0 artifacts/planning/2026-07-14-token-use-tracking/README.md
+10 βˆ’0 artifacts/planning/2026-07-14-token-use-tracking/deferred-items.md
+1,862 βˆ’0 artifacts/planning/2026-07-14-token-use-tracking/session.json
104 changes: 103 additions & 1 deletion schemas/session-file.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,107 @@
},
"default": {}
},
"usage": {
"type": "object",
"properties": {
"perActivity": {
"type": "object",
"additionalProperties": {
"type": "object",
"properties": {
"input_tokens": {
"type": "integer",
"minimum": 0
},
"output_tokens": {
"type": "integer",
"minimum": 0
},
"total_tokens": {
"type": "integer",
"minimum": 0
},
"cache_read_tokens": {
"type": "integer",
"minimum": 0
},
"cache_write_5m_tokens": {
"type": "integer",
"minimum": 0
},
"cache_write_1h_tokens": {
"type": "integer",
"minimum": 0
},
"model": {
"type": "string"
},
"cost_usd": {
"anyOf": [
{
"type": "number",
"minimum": 0
},
{
"type": "null"
}
]
},
"priceTableVersion": {
"type": "string"
}
},
"required": [
"input_tokens",
"output_tokens",
"total_tokens",
"cost_usd"
],
"additionalProperties": false
},
"default": {}
},
"workflowTotal": {
"type": "object",
"properties": {
"input_tokens": {
"type": "integer",
"minimum": 0
},
"output_tokens": {
"type": "integer",
"minimum": 0
},
"total_tokens": {
"type": "integer",
"minimum": 0
},
"cost_usd": {
"anyOf": [
{
"type": "number",
"minimum": 0
},
{
"type": "null"
}
]
}
},
"required": [
"input_tokens",
"output_tokens",
"total_tokens",
"cost_usd"
],
"additionalProperties": false
}
},
"required": [
"workflowTotal"
],
"additionalProperties": false
},
"history": {
"type": "array",
"items": {
Expand Down Expand Up @@ -170,7 +271,8 @@
"technique_fetched",
"resource_fetched",
"technique_bundled",
"variables_seeded"
"variables_seeded",
"usage_recorded"
]
},
"activity": {
Expand Down
3 changes: 2 additions & 1 deletion schemas/state.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,8 @@
"technique_fetched",
"resource_fetched",
"technique_bundled",
"variables_seeded"
"variables_seeded",
"usage_recorded"
]
},
"activity": {
Expand Down
25 changes: 25 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,18 @@ export interface ServerConfig {
traceStore?: TraceStore;
/** Minimum seconds between checkpoint issuance and response. Default 3. Set to 0 for testing. */
minCheckpointResponseSeconds?: number;
/**
* Per-model USD-per-MTok rates ({ input, output }) for cost estimation.
* Cache rates are derived from base input via universal multipliers.
*/
priceTable?: PriceTable;
/** Version string stamped on each cost estimate; env-overridable via PRICE_TABLE_VERSION. */
priceTableVersion?: string;
}

/** USD-per-MTok base input/output rates keyed by model id. */
export type PriceTable = Record<string, { input: number; output: number }>;

/** Config shape after startup β€” traceStore is guaranteed present. */
export interface ResolvedServerConfig extends ServerConfig {
traceStore: TraceStore;
Expand All @@ -49,6 +59,19 @@ const PROJECT_ROOT = resolve(import.meta.dirname, '..');
export const DEFAULT_BUNDLE_HEADROOM_FRACTION = 0.8;
export const DEFAULT_BUNDLE_CHARS_PER_TOKEN = 4;

/**
* Build-time price table seeded from Anthropic pricing (2026-07-15).
* Re-confirm figures at build time when pricing changes.
*/
export const DEFAULT_PRICE_TABLE: PriceTable = {
'claude-opus-4-8': { input: 5, output: 25 },
'claude-sonnet-5': { input: 2, output: 10 },
'claude-haiku-4-5': { input: 1, output: 5 },
'claude-fable-5': { input: 10, output: 50 },
};

export const DEFAULT_PRICE_TABLE_VERSION = '2026-07-15';

function envOrDefault(key: string, fallback: string): string {
const value = process.env[key]?.trim();
return value || fallback;
Expand Down Expand Up @@ -131,5 +154,7 @@ export function loadConfig(argv: readonly string[] = process.argv.slice(2)): Ser
serverVersion: envOrDefault('SERVER_VERSION', '2.1.0'),
bundleHeadroomFraction: envNumberOrDefault('BUNDLE_HEADROOM_FRACTION', DEFAULT_BUNDLE_HEADROOM_FRACTION),
bundleCharsPerToken: envNumberOrDefault('BUNDLE_CHARS_PER_TOKEN', DEFAULT_BUNDLE_CHARS_PER_TOKEN),
priceTable: DEFAULT_PRICE_TABLE,
priceTableVersion: envOrDefault('PRICE_TABLE_VERSION', DEFAULT_PRICE_TABLE_VERSION),
};
}
37 changes: 37 additions & 0 deletions src/schema/session.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,36 @@ export const ActiveCheckpointSchema = z.object({
});
export type ActiveCheckpoint = z.infer<typeof ActiveCheckpointSchema>;

/** Per-activity token usage entry with cost frozen at capture time. */
export const ActivityUsageEntrySchema = z.object({
input_tokens: z.number().int().nonnegative(),
output_tokens: z.number().int().nonnegative(),
total_tokens: z.number().int().nonnegative(),
cache_read_tokens: z.number().int().nonnegative().optional(),
cache_write_5m_tokens: z.number().int().nonnegative().optional(),
cache_write_1h_tokens: z.number().int().nonnegative().optional(),
model: z.string().optional(),
cost_usd: z.number().nonnegative().nullable(),
priceTableVersion: z.string().optional(),
});
export type ActivityUsageEntry = z.infer<typeof ActivityUsageEntrySchema>;

/** Rolled-up per-workflow token totals (child-inclusive at completion). */
export const WorkflowUsageTotalSchema = z.object({
input_tokens: z.number().int().nonnegative(),
output_tokens: z.number().int().nonnegative(),
total_tokens: z.number().int().nonnegative(),
cost_usd: z.number().nonnegative().nullable(),
});
export type WorkflowUsageTotal = z.infer<typeof WorkflowUsageTotalSchema>;

/** Durable usage roll-up persisted on SessionFile. */
export const SessionUsageSchema = z.object({
perActivity: z.record(ActivityUsageEntrySchema).default({}),
workflowTotal: WorkflowUsageTotalSchema,
});
export type SessionUsage = z.infer<typeof SessionUsageSchema>;

/**
* Base (non-recursive) shape of `SessionFile`. Used as the building block for
* the recursive `SessionFileSchema` below; the only structural extension is
Expand Down Expand Up @@ -101,6 +131,12 @@ const SessionFileBaseSchema = z.object({
*/
checkpointResponses: z.record(CheckpointResponseSchema).default({}),

/**
* Rolled-up token usage and cost estimate. Absent until the first
* `usage_recorded` event; child-inclusive at workflow completion.
*/
usage: SessionUsageSchema.optional(),

/** Append-only event log for the session. */
history: z.array(HistoryEntrySchema).default([]),

Expand Down Expand Up @@ -177,6 +213,7 @@ export interface SessionFile {
completedActivities: string[];
skippedActivities: string[];
checkpointResponses: Record<string, CheckpointResponse>;
usage?: SessionUsage;
history: HistoryEntry[];
status: 'running' | 'completed' | 'aborted';
triggeredWorkflows: EmbeddedSessionRef[];
Expand Down
4 changes: 4 additions & 0 deletions src/schema/state.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ export const HistoryEventTypeSchema = z.enum([
// session variable bag at session creation. ONE event per session; `data`
// carries { variables: <the seeded map> }.
'variables_seeded',
// Token-use tracking (#232): per-activity native usage relayed by the
// orchestrator at the next_activity transition seam. `data` carries the
// token figures, model, cost_usd (nullable), and priceTableVersion.
'usage_recorded',
]);
export type HistoryEventType = z.infer<typeof HistoryEventTypeSchema>;

Expand Down
45 changes: 41 additions & 4 deletions src/tools/workflow-tools.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { z } from 'zod';
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import type { ServerConfig } from '../config.js';
import { DEFAULT_BUNDLE_HEADROOM_FRACTION, DEFAULT_BUNDLE_CHARS_PER_TOKEN } from '../config.js';
import { DEFAULT_BUNDLE_HEADROOM_FRACTION, DEFAULT_BUNDLE_CHARS_PER_TOKEN, DEFAULT_PRICE_TABLE, DEFAULT_PRICE_TABLE_VERSION } from '../config.js';
import { listWorkflows, listWorkflowsWithDiagnostics, loadWorkflow, loadWorkflowWithDiagnostics, getActivity, getCheckpoint, readActivityRaw, buildFragmentsLookup, TERMINAL_SENTINEL } from '../loaders/workflow-loader.js';
import { injectCheckpointFragmentBodies, resolveCheckpointFragment, scanCheckpointRefLines } from '../loaders/fragment-resolver.js';
import { resolveTechniques, formatTechniqueBundle, composeActivityTechnique, projectTechnique, projectTechniqueToYaml } from '../loaders/technique-loader.js';
Expand All @@ -28,6 +28,7 @@ import {
import type { SessionFile } from '../schema/session.schema.js';
import { buildValidation, validateWorkflowVersion, validateActivityTransition, validateStepManifest, validateTechniqueFetches, validateTransitionCondition, validateActivityManifest } from '../utils/validation.js';
import type { StepManifestEntry, ActivityManifestEntry } from '../utils/validation.js';
import { finalizeUsageTree, recordActivityUsage, type UsageInput } from '../utils/usage.js';
import { createTraceToken, decodeTraceToken } from '../trace.js';
import type { TraceEvent, TraceTokenPayload } from '../trace.js';

Expand All @@ -42,6 +43,16 @@ const activityManifestSchema = z.array(z.object({
transition_condition: z.string().optional(),
})).optional().describe('Activity completion manifest from the orchestrator. Reports the sequence of activities completed so far with outcomes and transition conditions.');

const usageParamSchema = z.object({
input_tokens: z.number().int().nonnegative(),
output_tokens: z.number().int().nonnegative(),
total_tokens: z.number().int().nonnegative().optional(),
cache_read_tokens: z.number().int().nonnegative().optional(),
cache_write_5m_tokens: z.number().int().nonnegative().optional(),
cache_write_1h_tokens: z.number().int().nonnegative().optional(),
model: z.string().min(1).optional(),
}).optional().describe('Native model usage for the activity being EXITED, relayed by the orchestrator/harness (the worker cannot self-measure). Omit entirely when no usage is available β€” the run still completes. Absent sub-figures are omitted, not fabricated.');

/**
* Wrap a tool handler so any thrown `SessionStoreError` is re-thrown with a
* user-facing message. Keeps the per-handler logic terse β€” handlers can
Expand Down Expand Up @@ -123,7 +134,7 @@ export async function composeActivityArtifacts(

/** The views `inspect_session` can project. `summary` is the default composite. */
export const INSPECT_SESSION_VIEWS = [
'summary', 'identity', 'variables', 'checkpoints', 'activities', 'history', 'children',
'summary', 'identity', 'variables', 'checkpoints', 'activities', 'history', 'children', 'usage',
] as const;
export type InspectSessionView = (typeof INSPECT_SESSION_VIEWS)[number];

Expand Down Expand Up @@ -215,16 +226,24 @@ export function projectChildren(s: SessionFile): Array<Record<string, unknown>>
});
}

/** Usage projection: rolled-up per-activity and per-workflow token figures. */
export function projectUsage(s: SessionFile): Record<string, unknown> | undefined {
return s.usage ? { ...s.usage } : undefined;
}

/** Summary (default) view: the composite of all projections for the addressed session. */
export function projectSummary(s: SessionFile): Record<string, unknown> {
return {
const summary: Record<string, unknown> = {
identity: projectIdentity(s),
activities: projectActivities(s),
variables: s.variables ?? {},
checkpoints: projectCheckpoints(s),
history: projectHistory(s),
children: projectChildren(s),
};
const usage = projectUsage(s);
if (usage) summary['usage'] = usage;
return summary;
}

/**
Expand All @@ -247,6 +266,7 @@ export function projectSessionView(
case 'activities': return projectActivities(s);
case 'history': return projectHistory(s);
case 'children': return projectChildren(s);
case 'usage': return projectUsage(s) ?? {};
case 'summary':
default:
return projectSummary(s);
Expand Down Expand Up @@ -377,8 +397,9 @@ export function registerWorkflowTools(server: McpServer, config: ServerConfig):
transition_condition: z.string().optional().describe('The transition condition that led to this activity (from the transitions field of the previous activity). Enables server-side validation of condition-activity consistency.'),
step_manifest: stepManifestSchema,
activity_manifest: activityManifestSchema,
usage: usageParamSchema,
},
withAuditLog('next_activity', withSessionStoreErrors(async ({ session_index, activity_id, transition_condition, step_manifest, activity_manifest }) => {
withAuditLog('next_activity', withSessionStoreErrors(async ({ session_index, activity_id, transition_condition, step_manifest, activity_manifest, usage }) => {
const loaded = await loadSessionForTool(config.workspaceDir, session_index);
const { state } = loaded;

Expand Down Expand Up @@ -431,11 +452,23 @@ export function registerWorkflowTools(server: McpServer, config: ServerConfig):
...activityManifestWarnings,
);

const priceTable = config.priceTable ?? DEFAULT_PRICE_TABLE;
const priceTableVersion = config.priceTableVersion ?? DEFAULT_PRICE_TABLE_VERSION;

const next = advanceSession(state, (draft) => {
const now = new Date().toISOString();
// Exit-prior: any non-empty previous activity is recorded as
// completed once we transition off it.
if (draft.currentActivity) {
if (usage) {
recordActivityUsage(
draft,
draft.currentActivity,
usage as UsageInput,
priceTable,
priceTableVersion,
);
}
draft.history.push({ timestamp: now, type: 'activity_exited', activity: draft.currentActivity });
if (!draft.completedActivities.includes(draft.currentActivity)) {
draft.completedActivities.push(draft.currentActivity);
Expand All @@ -450,6 +483,7 @@ export function registerWorkflowTools(server: McpServer, config: ServerConfig):
// across the work-package, prism, and meta workflows; the TERMINAL_SENTINEL
// is the contentless terminal reached via an explicit terminal transition.
if (activity_id === 'complete' || isTerminal) {
finalizeUsageTree(draft);
draft.history.push({ timestamp: now, type: 'workflow_completed' });
draft.status = 'completed';
}
Expand Down Expand Up @@ -1302,6 +1336,9 @@ export function registerWorkflowTools(server: McpServer, config: ServerConfig):
};
}

const usage = projectUsage(state);
if (usage) response['usage'] = usage;

// get_workflow_status reads but does not advance β€” keep the on-disk state stable.
response['session_index'] = session_index;

Expand Down
1 change: 1 addition & 0 deletions src/utils/session/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ const TOP_LEVEL_KEY_PRIORITY = [
'skippedActivities',
'variables',
'checkpointResponses',
'usage',
'history',
'triggeredWorkflows',
'parentSession',
Expand Down
Loading
Loading