@hongmaple0820/scale-engine 0.40.1 → 0.40.2

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 (35) hide show
  1. package/dist/api/cli.js +267 -7
  2. package/dist/api/cli.js.map +1 -1
  3. package/dist/api/doctor.js +1 -1
  4. package/dist/api/doctor.js.map +1 -1
  5. package/dist/bootstrap/DependencyBootstrap.d.ts +1 -0
  6. package/dist/bootstrap/DependencyBootstrap.js +137 -25
  7. package/dist/bootstrap/DependencyBootstrap.js.map +1 -1
  8. package/dist/capabilities/InstalledSkillsIntegration.js +29 -9
  9. package/dist/capabilities/InstalledSkillsIntegration.js.map +1 -1
  10. package/dist/context/ContextBudget.js +2 -2
  11. package/dist/core/GbrainRuntime.d.ts +25 -0
  12. package/dist/core/GbrainRuntime.js +270 -0
  13. package/dist/core/GbrainRuntime.js.map +1 -0
  14. package/dist/env/EnvironmentDoctor.js +221 -5
  15. package/dist/env/EnvironmentDoctor.js.map +1 -1
  16. package/dist/memory/MemoryProviders.js +38 -91
  17. package/dist/memory/MemoryProviders.js.map +1 -1
  18. package/dist/runtime/ModelUsageLedger.d.ts +53 -2
  19. package/dist/runtime/ModelUsageLedger.js +243 -39
  20. package/dist/runtime/ModelUsageLedger.js.map +1 -1
  21. package/dist/setup/SetupVerification.d.ts +42 -0
  22. package/dist/setup/SetupVerification.js +180 -0
  23. package/dist/setup/SetupVerification.js.map +1 -0
  24. package/dist/tools/ToolCapabilityRegistry.js +10 -0
  25. package/dist/tools/ToolCapabilityRegistry.js.map +1 -1
  26. package/dist/workflow/VerificationProfile.js +1 -1
  27. package/dist/workflow/VerificationProfile.js.map +1 -1
  28. package/docs/CONTEXT_BUDGET.md +12 -2
  29. package/docs/GOVERNANCE_DASHBOARD.md +7 -0
  30. package/docs/start/quickstart.md +1 -0
  31. package/package.json +3 -2
  32. package/scripts/workflow/lib/gbrain-runtime.mjs +185 -0
  33. package/scripts/workflow/lib/report-output.mjs +107 -0
  34. package/scripts/workflow/provider-rehearsal.mjs +129 -48
  35. package/scripts/workflow/setup-smoke.mjs +142 -8
package/dist/api/cli.js CHANGED
@@ -33,6 +33,7 @@ import { quickStart, detectPlatform, governanceNextSteps } from './quickstart.js
33
33
  import { bootstrapDependencies } from '../bootstrap/DependencyBootstrap.js';
34
34
  import { renderDependencyBootstrapReport } from '../bootstrap/DependencyBootstrapRenderer.js';
35
35
  import { runSetupWizard } from '../setup/SetupWizard.js';
36
+ import { verifySetup } from '../setup/SetupVerification.js';
36
37
  import { normalizeLanguage, resolveCliLanguage } from '../i18n/Language.js';
37
38
  import { SkillDiscovery } from '../skills/SkillDiscovery.js';
38
39
  import { inspectRequiredWorkflowSkills, inspectWorkflowSkills } from '../skills/SkillDoctor.js';
@@ -75,7 +76,7 @@ import { doctorHtmlArtifacts, renderHtmlArtifact, resolveHtmlArtifactForOpen, se
75
76
  import { renderGovernanceDashboard } from '../output/GovernanceDashboard.js';
76
77
  import { cleanupWorkspaceLifecycle, inspectWorkspaceLifecycle, } from '../workflow/WorkspaceLifecycle.js';
77
78
  import { inspectWorkspaceSafety } from '../workflow/WorkspaceSafety.js';
78
- import { RuntimeEvidenceLedger, SessionLedger, createAiOsAdoption, createAiOsBenchmark, createAiOsDashboard, createAiOsDoctor, createAiOsMigration, createAiOsPlan, createAiOsRun, createAiOsStatus, doctorRuntimeEvidence, evaluateFinalReportReadiness, } from '../runtime/index.js';
79
+ import { ModelUsageLedger, RuntimeEvidenceLedger, SessionLedger, buildModelUsageInput, createAiOsAdoption, createAiOsBenchmark, createAiOsDashboard, createAiOsDoctor, createAiOsMigration, createAiOsPlan, createAiOsRun, createAiOsStatus, doctorRuntimeEvidence, evaluateFinalReportReadiness, } from '../runtime/index.js';
79
80
  import { MemoryFabric, MemoryBrain, doctorMemoryFabric, renderContextPackMarkdown, renderMemoryLearningCandidateMarkdown, inspectMemoryProviders, recallMemoryProviders, settleMemoryLearning, useMemoryProvider, writeMemoryProvidersConfig, } from '../memory/index.js';
80
81
  import { resolveWorkspaceTopology, workspaceTopologyPath, workspaceTopologyTemplate, } from '../workflow/WorkspaceTopology.js';
81
82
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
@@ -2817,7 +2818,6 @@ const bootstrap = defineCommand({
2817
2818
  meta: { name: 'bootstrap', description: 'Bootstrap third-party workflow dependencies with explicit install intent' },
2818
2819
  subCommands: { deps: bootstrapDepsCommand },
2819
2820
  });
2820
- // ============================================================================
2821
2821
  const setup = defineCommand({
2822
2822
  meta: { name: 'setup', description: 'Interactive SCALE setup for third-party skills, CLIs, memory, and knowledge providers' },
2823
2823
  args: {
@@ -2828,6 +2828,7 @@ const setup = defineCommand({
2828
2828
  include: { type: 'string', description: 'Additional dependency ids to include explicitly' },
2829
2829
  apply: { type: 'boolean', default: false, description: 'Run install commands for ready dependencies' },
2830
2830
  yes: { type: 'boolean', default: false, description: 'Confirm installation without prompting' },
2831
+ verify: { type: 'boolean', default: false, description: 'Verify governed setup and dependency readiness instead of running the setup wizard' },
2831
2832
  interactive: { type: 'boolean', default: true, description: 'Prompt before installation when dependencies are ready' },
2832
2833
  lang: { type: 'string', description: 'Output language zh/en. Defaults to zh, then SCALE_LANG, then .scale/config.yaml locale.' },
2833
2834
  'memory-provider': { type: 'string', description: 'Switch memory provider during setup: gbrain, agentmemory, or scale-local' },
@@ -2840,10 +2841,24 @@ const setup = defineCommand({
2840
2841
  async run({ args }) {
2841
2842
  const projectDir = resolve(String(args.dir ?? PROJECT_DIR));
2842
2843
  const lang = resolveCliLanguage({ lang: args.lang, projectDir, scaleDir: SCALE_DIR });
2843
- const explicitPacks = parseCommaList(args.pack);
2844
- const recommendedPacks = args.profile
2845
- ? getBootstrapPlanForProfile(String(args.profile), args['governance-pack'] ? String(args['governance-pack']) : undefined).packs
2846
- : [];
2844
+ const { explicitPacks, recommendedPacks } = resolveSetupPacks(args);
2845
+ if (isTruthyFlag(args.verify)) {
2846
+ const verification = await verifySetup({
2847
+ projectDir,
2848
+ scaleDir: SCALE_DIR,
2849
+ packIds: explicitPacks.length > 0 ? uniqueStrings([...recommendedPacks, ...explicitPacks]) : recommendedPacks,
2850
+ includeIds: parseCommaList(args.include),
2851
+ });
2852
+ if (args.json) {
2853
+ console.log(JSON.stringify(verification, null, 2));
2854
+ }
2855
+ else {
2856
+ renderSetupVerifyReport(verification, lang);
2857
+ }
2858
+ if (!verification.ok)
2859
+ process.exitCode = 1;
2860
+ return;
2861
+ }
2847
2862
  const report = await runSetupWizard({
2848
2863
  projectDir,
2849
2864
  scaleDir: SCALE_DIR,
@@ -2892,6 +2907,64 @@ const setup = defineCommand({
2892
2907
  });
2893
2908
  // config command — Configuration profile management
2894
2909
  // ============================================================================
2910
+ function resolveSetupPacks(args) {
2911
+ const explicitPacks = parseCommaList(args.pack);
2912
+ const recommendedPacks = args.profile
2913
+ ? getBootstrapPlanForProfile(String(args.profile), args['governance-pack'] ? String(args['governance-pack']) : undefined).packs
2914
+ : [];
2915
+ return { explicitPacks, recommendedPacks };
2916
+ }
2917
+ function renderSetupVerifyReport(report, lang) {
2918
+ if (lang === 'zh') {
2919
+ console.log('\nSCALE 安装验收');
2920
+ console.log(` 项目: ${report.projectDir}`);
2921
+ console.log(` 依赖包: ${report.packIds.join(', ') || 'full'}`);
2922
+ console.log(` 结论: ${report.ok ? '通过' : '未通过'}`);
2923
+ console.log(` 阻塞项: ${report.summary.blockingIssues.length}`);
2924
+ console.log(` 受管能力: ${report.summary.installedTools}/${report.summary.totalTools}`);
2925
+ console.log(` 记忆供应商: ${report.summary.availableMemoryProviders}`);
2926
+ console.log(` 代码图谱供应商: ${report.summary.availableCodeProviders}`);
2927
+ if (report.summary.blockingIssues.length > 0) {
2928
+ console.log(' 阻塞详情:');
2929
+ for (const issue of report.summary.blockingIssues)
2930
+ console.log(` - ${issue}`);
2931
+ }
2932
+ if (report.warnings.length > 0) {
2933
+ console.log(` 警告${report.warnings.length > 12 ? ` (显示前 12 条,共 ${report.warnings.length} 条)` : ''}:`);
2934
+ for (const warning of report.warnings.slice(0, 12))
2935
+ console.log(` - ${warning}`);
2936
+ }
2937
+ if (report.recommendations.length > 0) {
2938
+ console.log(' 下一步:');
2939
+ for (const command of report.recommendations.slice(0, 12))
2940
+ console.log(` ${command}`);
2941
+ }
2942
+ return;
2943
+ }
2944
+ console.log('\nSCALE Setup Verification');
2945
+ console.log(` Project: ${report.projectDir}`);
2946
+ console.log(` Packs: ${report.packIds.join(', ') || 'full'}`);
2947
+ console.log(` Result: ${report.ok ? 'passed' : 'failed'}`);
2948
+ console.log(` Blocking issues: ${report.summary.blockingIssues.length}`);
2949
+ console.log(` Governed capabilities: ${report.summary.installedTools}/${report.summary.totalTools}`);
2950
+ console.log(` Memory providers: ${report.summary.availableMemoryProviders}`);
2951
+ console.log(` Code providers: ${report.summary.availableCodeProviders}`);
2952
+ if (report.summary.blockingIssues.length > 0) {
2953
+ console.log(' Blockers:');
2954
+ for (const issue of report.summary.blockingIssues)
2955
+ console.log(` - ${issue}`);
2956
+ }
2957
+ if (report.warnings.length > 0) {
2958
+ console.log(` Warnings${report.warnings.length > 12 ? ` (showing first 12 of ${report.warnings.length})` : ''}:`);
2959
+ for (const warning of report.warnings.slice(0, 12))
2960
+ console.log(` - ${warning}`);
2961
+ }
2962
+ if (report.recommendations.length > 0) {
2963
+ console.log(' Next:');
2964
+ for (const command of report.recommendations.slice(0, 12))
2965
+ console.log(` ${command}`);
2966
+ }
2967
+ }
2895
2968
  const configProfile = defineCommand({
2896
2969
  meta: { name: 'profile', description: 'View or switch configuration profile' },
2897
2970
  args: {
@@ -4537,6 +4610,169 @@ function normalizeRuntimeSessionStatus(value) {
4537
4610
  return normalized;
4538
4611
  throw new Error(`Invalid runtime session status "${normalized}"; expected active, completed, failed, or abandoned.`);
4539
4612
  }
4613
+ function parseNonNegativeNumberArg(value, name) {
4614
+ if (value === undefined || value === null || value === '')
4615
+ return undefined;
4616
+ const parsed = Number(value);
4617
+ if (!Number.isFinite(parsed) || parsed < 0) {
4618
+ throw new Error(`${name} must be a non-negative number.`);
4619
+ }
4620
+ return parsed;
4621
+ }
4622
+ function parseJsonArg(value, name) {
4623
+ try {
4624
+ return JSON.parse(String(value ?? 'null'));
4625
+ }
4626
+ catch {
4627
+ throw new Error(`${name} must be valid JSON.`);
4628
+ }
4629
+ }
4630
+ function parseMetadataJson(value, name = '--metadata-json') {
4631
+ const parsed = parseJsonArg(value ?? '{}', name);
4632
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
4633
+ throw new Error(`${name} must be a JSON object.`);
4634
+ }
4635
+ return parsed;
4636
+ }
4637
+ function hasModelUsageArgs(args) {
4638
+ return [
4639
+ 'provider',
4640
+ 'model',
4641
+ 'usage-json',
4642
+ 'usage-file',
4643
+ 'input-tokens',
4644
+ 'output-tokens',
4645
+ 'cache-eligible-tokens',
4646
+ 'cache-creation-input-tokens',
4647
+ 'cache-read-input-tokens',
4648
+ 'cached-tokens',
4649
+ 'estimated-cost-usd',
4650
+ ].some(key => args[key] !== undefined && args[key] !== '');
4651
+ }
4652
+ function buildModelUsageRecordInput(args, defaults = {}) {
4653
+ const usagePayload = args['usage-file']
4654
+ ? parseJsonArg(readFileSync(resolve(PROJECT_DIR, String(args['usage-file'])), 'utf-8'), '--usage-file')
4655
+ : args['usage-json']
4656
+ ? parseJsonArg(args['usage-json'], '--usage-json')
4657
+ : undefined;
4658
+ const provider = String(args.provider ?? defaults.provider ?? '').trim();
4659
+ if (!provider)
4660
+ throw new Error('Model usage recording requires --provider.');
4661
+ return buildModelUsageInput({
4662
+ provider,
4663
+ model: args.model ? String(args.model) : undefined,
4664
+ taskId: args['task-id'] ? String(args['task-id']) : defaults.taskId,
4665
+ sessionId: args['session-id'] ? String(args['session-id']) : defaults.sessionId,
4666
+ inputTokens: parseNonNegativeNumberArg(args['input-tokens'], '--input-tokens'),
4667
+ outputTokens: parseNonNegativeNumberArg(args['output-tokens'], '--output-tokens'),
4668
+ cacheEligibleTokens: parseNonNegativeNumberArg(args['cache-eligible-tokens'], '--cache-eligible-tokens'),
4669
+ cacheCreationInputTokens: parseNonNegativeNumberArg(args['cache-creation-input-tokens'], '--cache-creation-input-tokens'),
4670
+ cacheReadInputTokens: parseNonNegativeNumberArg(args['cache-read-input-tokens'], '--cache-read-input-tokens'),
4671
+ cachedTokens: parseNonNegativeNumberArg(args['cached-tokens'], '--cached-tokens'),
4672
+ estimatedCostUsd: parseNonNegativeNumberArg(args['estimated-cost-usd'], '--estimated-cost-usd'),
4673
+ metadata: args['metadata-json'] !== undefined ? parseMetadataJson(args['metadata-json']) : undefined,
4674
+ timestamp: args.timestamp ? String(args.timestamp) : undefined,
4675
+ usagePayload,
4676
+ });
4677
+ }
4678
+ const tokenRecord = defineCommand({
4679
+ meta: { name: 'record', description: 'Record real model usage from provider usage payloads or explicit token counts' },
4680
+ args: {
4681
+ provider: { type: 'string', required: true, description: 'Model provider: anthropic, openai, codex, etc.' },
4682
+ model: { type: 'string', description: 'Optional model id' },
4683
+ 'task-id': { type: 'string', description: 'Task id linked to this model usage' },
4684
+ 'session-id': { type: 'string', description: 'Session id linked to this model usage' },
4685
+ 'usage-json': { type: 'string', description: 'Raw provider response or usage JSON to normalize into the usage ledger' },
4686
+ 'usage-file': { type: 'string', description: 'Path to a JSON file containing a raw provider response or usage payload' },
4687
+ 'input-tokens': { type: 'string', description: 'Explicit input token count; overrides usage JSON when provided' },
4688
+ 'output-tokens': { type: 'string', description: 'Explicit output token count; overrides usage JSON when provided' },
4689
+ 'cache-eligible-tokens': { type: 'string', description: 'Explicit cache-eligible token count' },
4690
+ 'cache-creation-input-tokens': { type: 'string', description: 'Explicit Anthropic cache creation token count' },
4691
+ 'cache-read-input-tokens': { type: 'string', description: 'Explicit Anthropic cache read token count' },
4692
+ 'cached-tokens': { type: 'string', description: 'Explicit OpenAI cached token count' },
4693
+ 'estimated-cost-usd': { type: 'string', description: 'Optional estimated cost in USD' },
4694
+ timestamp: { type: 'string', description: 'Optional ISO timestamp' },
4695
+ 'metadata-json': { type: 'string', default: '{}', description: 'Additional JSON metadata' },
4696
+ json: { type: 'boolean', default: false },
4697
+ },
4698
+ run({ args }) {
4699
+ const record = new ModelUsageLedger(SCALE_DIR).record(buildModelUsageRecordInput(args));
4700
+ if (args.json) {
4701
+ console.log(JSON.stringify(record, null, 2));
4702
+ return;
4703
+ }
4704
+ console.log(`Model usage recorded: ${record.id}`);
4705
+ console.log(` Provider: ${record.provider}`);
4706
+ console.log(` Model: ${record.model ?? 'unknown'}`);
4707
+ console.log(` Tokens: input ${record.inputTokens}, output ${record.outputTokens}, total ${record.totalTokens}`);
4708
+ if (record.cacheSavingsTokens > 0)
4709
+ console.log(` Cache savings: ${record.cacheSavingsTokens} tokens`);
4710
+ },
4711
+ });
4712
+ const tokenReport = defineCommand({
4713
+ meta: { name: 'report', description: 'Summarize recorded model usage by day, provider, model, and task' },
4714
+ args: {
4715
+ day: { type: 'string', description: 'Exact UTC day in YYYY-MM-DD format' },
4716
+ since: { type: 'string', description: 'ISO timestamp lower bound' },
4717
+ until: { type: 'string', description: 'ISO timestamp upper bound' },
4718
+ 'since-days': { type: 'string', default: '7d', description: 'Relative time window when day/since/until are omitted; use all to disable' },
4719
+ provider: { type: 'string', description: 'Filter by provider' },
4720
+ model: { type: 'string', description: 'Filter by model id' },
4721
+ 'task-id': { type: 'string', description: 'Filter by task id' },
4722
+ 'session-id': { type: 'string', description: 'Filter by session id' },
4723
+ limit: { type: 'string', description: 'Maximum recent records to include in the report; defaults to 20' },
4724
+ json: { type: 'boolean', default: false },
4725
+ },
4726
+ run({ args }) {
4727
+ const limit = parsePositiveIntArg(args.limit, '--limit');
4728
+ const sinceDays = args.day || args.since || args.until ? undefined : parseSinceDays(args['since-days']) ?? 7;
4729
+ const since = args.since
4730
+ ? String(args.since)
4731
+ : sinceDays
4732
+ ? new Date(Date.now() - sinceDays * 24 * 60 * 60 * 1000).toISOString()
4733
+ : undefined;
4734
+ const report = new ModelUsageLedger(SCALE_DIR).report({
4735
+ day: args.day ? String(args.day) : undefined,
4736
+ since,
4737
+ until: args.until ? String(args.until) : undefined,
4738
+ provider: args.provider ? String(args.provider) : undefined,
4739
+ model: args.model ? String(args.model) : undefined,
4740
+ taskId: args['task-id'] ? String(args['task-id']) : undefined,
4741
+ sessionId: args['session-id'] ? String(args['session-id']) : undefined,
4742
+ limit,
4743
+ });
4744
+ if (args.json) {
4745
+ console.log(JSON.stringify(report, null, 2));
4746
+ return;
4747
+ }
4748
+ console.log('SCALE Token Report');
4749
+ if (report.filters.day)
4750
+ console.log(` Day: ${report.filters.day}`);
4751
+ else if (report.filters.since || report.filters.until)
4752
+ console.log(` Window: ${report.filters.since ?? '-inf'} -> ${report.filters.until ?? 'now'}`);
4753
+ console.log(` Records: ${report.summary.totalRecords}`);
4754
+ console.log(` Tokens: input ${report.summary.totalInputTokens}, output ${report.summary.totalOutputTokens}, total ${report.summary.totalTokens}`);
4755
+ console.log(` Cache: eligible ${report.summary.cacheEligibleTokens}, create ${report.summary.cacheCreationInputTokens}, read ${report.summary.cacheReadInputTokens}, cached ${report.summary.cachedTokens}, saved ${report.summary.cacheSavingsTokens}`);
4756
+ if (report.summary.estimatedCostUsd !== undefined)
4757
+ console.log(` Estimated cost: $${report.summary.estimatedCostUsd.toFixed(6)}`);
4758
+ for (const row of report.byProvider.slice(0, 5)) {
4759
+ console.log(` Provider ${row.key}: ${row.records} record(s), ${row.totalTokens} total tokens, ${row.cacheSavingsTokens} saved`);
4760
+ }
4761
+ for (const row of report.byModel.slice(0, 5)) {
4762
+ console.log(` Model ${row.key}: ${row.records} record(s), ${row.totalTokens} total tokens`);
4763
+ }
4764
+ for (const row of report.byTask.slice(0, 5)) {
4765
+ console.log(` Task ${row.key}: ${row.records} record(s), ${row.totalTokens} total tokens`);
4766
+ }
4767
+ for (const row of report.records.slice(0, 10)) {
4768
+ console.log(` Recent ${row.timestamp}: ${row.provider}/${row.model ?? 'unknown'} task=${row.taskId ?? '-'} total=${row.totalTokens}`);
4769
+ }
4770
+ },
4771
+ });
4772
+ const token = defineCommand({
4773
+ meta: { name: 'token', description: 'Record and audit real model token usage' },
4774
+ subCommands: { record: tokenRecord, report: tokenReport },
4775
+ });
4540
4776
  const runtimeStart = defineCommand({
4541
4777
  meta: { name: 'start', description: 'Start a runtime session ledger' },
4542
4778
  args: {
@@ -4604,6 +4840,18 @@ const runtimeRecord = defineCommand({
4604
4840
  'exit-code': { type: 'string', description: 'Exit code when applicable' },
4605
4841
  summary: { type: 'string', required: true, description: 'Short output summary' },
4606
4842
  artifacts: { type: 'string', description: 'Comma-separated artifact paths' },
4843
+ provider: { type: 'string', description: 'Optional model provider when attaching model usage: anthropic, openai, codex, etc.' },
4844
+ model: { type: 'string', description: 'Optional model id when attaching model usage' },
4845
+ 'usage-json': { type: 'string', description: 'Raw provider response or usage JSON to normalize into the usage ledger' },
4846
+ 'usage-file': { type: 'string', description: 'Path to a JSON file containing a raw provider response or usage payload' },
4847
+ 'input-tokens': { type: 'string', description: 'Explicit input token count; overrides usage JSON when provided' },
4848
+ 'output-tokens': { type: 'string', description: 'Explicit output token count; overrides usage JSON when provided' },
4849
+ 'cache-eligible-tokens': { type: 'string', description: 'Explicit cache-eligible token count' },
4850
+ 'cache-creation-input-tokens': { type: 'string', description: 'Explicit Anthropic cache creation token count' },
4851
+ 'cache-read-input-tokens': { type: 'string', description: 'Explicit Anthropic cache read token count' },
4852
+ 'cached-tokens': { type: 'string', description: 'Explicit OpenAI cached token count' },
4853
+ 'estimated-cost-usd': { type: 'string', description: 'Optional estimated cost in USD' },
4854
+ timestamp: { type: 'string', description: 'Optional ISO timestamp for the usage record' },
4607
4855
  'metadata-json': { type: 'string', default: '{}', description: 'Additional JSON metadata' },
4608
4856
  json: { type: 'boolean', default: false },
4609
4857
  },
@@ -4648,13 +4896,24 @@ const runtimeRecord = defineCommand({
4648
4896
  },
4649
4897
  });
4650
4898
  }
4899
+ const usageRecord = hasModelUsageArgs(args)
4900
+ ? new ModelUsageLedger(SCALE_DIR).record(buildModelUsageRecordInput(args, {
4901
+ taskId: record.taskId,
4902
+ sessionId: record.sessionId,
4903
+ }))
4904
+ : undefined;
4651
4905
  if (args.json) {
4652
- console.log(JSON.stringify(record, null, 2));
4906
+ console.log(JSON.stringify(usageRecord ? { evidence: record, usage: usageRecord } : record, null, 2));
4653
4907
  return;
4654
4908
  }
4655
4909
  console.log(`Runtime evidence recorded: ${record.id}`);
4656
4910
  console.log(` Status: ${record.status}`);
4657
4911
  console.log(` Kind: ${record.kind}`);
4912
+ if (usageRecord) {
4913
+ console.log(` Model usage: ${usageRecord.provider}/${usageRecord.model ?? 'unknown'} ${usageRecord.totalTokens} total tokens`);
4914
+ if (usageRecord.cacheSavingsTokens > 0)
4915
+ console.log(` Cache savings: ${usageRecord.cacheSavingsTokens} tokens`);
4916
+ }
4658
4917
  if (record.redactionApplied)
4659
4918
  console.log(' Redaction: applied');
4660
4919
  },
@@ -6274,6 +6533,7 @@ const main = defineCommand({
6274
6533
  workflow,
6275
6534
  evidence,
6276
6535
  runtime,
6536
+ token,
6277
6537
  memory,
6278
6538
  diagnose,
6279
6539
  hunt,