@agentguard-run/spend 0.4.3 → 0.5.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 (95) hide show
  1. package/CHANGELOG.md +14 -1
  2. package/LICENSE +53 -0
  3. package/README.md +76 -0
  4. package/dist/advisor/conversation.d.ts +5 -0
  5. package/dist/advisor/conversation.d.ts.map +1 -1
  6. package/dist/advisor/conversation.js +34 -2
  7. package/dist/advisor/conversation.js.map +1 -1
  8. package/dist/advisor/output.d.ts.map +1 -1
  9. package/dist/advisor/output.js +9 -2
  10. package/dist/advisor/output.js.map +1 -1
  11. package/dist/advisor/system-prompt.d.ts.map +1 -1
  12. package/dist/advisor/system-prompt.js +6 -3
  13. package/dist/advisor/system-prompt.js.map +1 -1
  14. package/dist/cli/advisor.d.ts.map +1 -1
  15. package/dist/cli/advisor.js +11 -2
  16. package/dist/cli/advisor.js.map +1 -1
  17. package/dist/cli/auth.d.ts.map +1 -1
  18. package/dist/cli/auth.js +20 -1
  19. package/dist/cli/auth.js.map +1 -1
  20. package/dist/cli/config.d.ts +2 -0
  21. package/dist/cli/config.d.ts.map +1 -0
  22. package/dist/cli/config.js +19 -0
  23. package/dist/cli/config.js.map +1 -0
  24. package/dist/cli/license.d.ts +2 -0
  25. package/dist/cli/license.d.ts.map +1 -0
  26. package/dist/cli/license.js +39 -0
  27. package/dist/cli/license.js.map +1 -0
  28. package/dist/cli/main.d.ts.map +1 -1
  29. package/dist/cli/main.js +37 -1
  30. package/dist/cli/main.js.map +1 -1
  31. package/dist/cli/skill.d.ts +2 -0
  32. package/dist/cli/skill.d.ts.map +1 -0
  33. package/dist/cli/skill.js +33 -0
  34. package/dist/cli/skill.js.map +1 -0
  35. package/dist/cli/soft-cap.d.ts +25 -0
  36. package/dist/cli/soft-cap.d.ts.map +1 -0
  37. package/dist/cli/soft-cap.js +204 -0
  38. package/dist/cli/soft-cap.js.map +1 -0
  39. package/dist/cost-table.d.ts.map +1 -1
  40. package/dist/cost-table.js +2 -0
  41. package/dist/cost-table.js.map +1 -1
  42. package/dist/index.d.ts +11 -2
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.js +31 -3
  45. package/dist/index.js.map +1 -1
  46. package/dist/license.d.ts +44 -0
  47. package/dist/license.d.ts.map +1 -0
  48. package/dist/license.js +278 -0
  49. package/dist/license.js.map +1 -0
  50. package/dist/policy.d.ts +15 -1
  51. package/dist/policy.d.ts.map +1 -1
  52. package/dist/policy.js +95 -1
  53. package/dist/policy.js.map +1 -1
  54. package/dist/spend-guard.d.ts +10 -0
  55. package/dist/spend-guard.d.ts.map +1 -1
  56. package/dist/spend-guard.js +14 -1
  57. package/dist/spend-guard.js.map +1 -1
  58. package/dist/types.d.ts +30 -0
  59. package/dist/types.d.ts.map +1 -1
  60. package/dist/workflow/chain-validator.d.ts +4 -0
  61. package/dist/workflow/chain-validator.d.ts.map +1 -0
  62. package/dist/workflow/chain-validator.js +37 -0
  63. package/dist/workflow/chain-validator.js.map +1 -0
  64. package/dist/workflow/context.d.ts +46 -0
  65. package/dist/workflow/context.d.ts.map +1 -0
  66. package/dist/workflow/context.js +360 -0
  67. package/dist/workflow/context.js.map +1 -0
  68. package/dist/workflow/errors.d.ts +43 -0
  69. package/dist/workflow/errors.d.ts.map +1 -0
  70. package/dist/workflow/errors.js +40 -0
  71. package/dist/workflow/errors.js.map +1 -0
  72. package/dist/workflow/index.d.ts +6 -0
  73. package/dist/workflow/index.d.ts.map +1 -0
  74. package/dist/workflow/index.js +20 -0
  75. package/dist/workflow/index.js.map +1 -0
  76. package/dist/workflow/receipt.d.ts +23 -0
  77. package/dist/workflow/receipt.d.ts.map +1 -0
  78. package/dist/workflow/receipt.js +60 -0
  79. package/dist/workflow/receipt.js.map +1 -0
  80. package/dist/workflow/types.d.ts +74 -0
  81. package/dist/workflow/types.d.ts.map +1 -0
  82. package/dist/workflow/types.js +3 -0
  83. package/dist/workflow/types.js.map +1 -0
  84. package/package.json +11 -4
  85. package/src/advisor/conversation.ts +41 -2
  86. package/src/advisor/output.ts +9 -2
  87. package/src/advisor/system-prompt.ts +6 -3
  88. package/src/cli/advisor.ts +9 -2
  89. package/src/cli/auth.ts +19 -1
  90. package/src/workflow/chain-validator.ts +35 -0
  91. package/src/workflow/context.ts +418 -0
  92. package/src/workflow/errors.ts +27 -0
  93. package/src/workflow/index.ts +18 -0
  94. package/src/workflow/receipt.ts +73 -0
  95. package/src/workflow/types.ts +88 -0
@@ -0,0 +1,74 @@
1
+ export type WorkflowState = 'active' | 'paused' | 'cancelled' | 'completed' | 'budget_capped' | 'duration_capped';
2
+ export type ReviewerVerdict = 'approve' | 'block' | 'revise' | null;
3
+ export interface WorkflowConfig {
4
+ name: string;
5
+ budget_cap_usd: number;
6
+ duration_cap_hours: number;
7
+ checkpoint_every_outcomes?: number;
8
+ parent_outcomes?: string[];
9
+ resume_if_exists?: boolean;
10
+ metadata?: Record<string, string | number | boolean>;
11
+ user_id?: string;
12
+ api_base_url?: string;
13
+ license_key?: string;
14
+ post_json?: (url: string, body: unknown, headers: Record<string, string>) => Promise<unknown>;
15
+ get_json?: (url: string, headers: Record<string, string>) => Promise<unknown>;
16
+ }
17
+ export interface ReviewerCascadeEvidence {
18
+ triggered: boolean;
19
+ drafter_model: string | null;
20
+ reviewer_model: string | null;
21
+ drafter_output_hash: string | null;
22
+ reviewer_verdict: ReviewerVerdict;
23
+ reviewer_reasoning_hash: string | null;
24
+ review_started_at: string | null;
25
+ review_completed_at: string | null;
26
+ }
27
+ export interface ReceiptV2 {
28
+ receipt_id: string;
29
+ schema_version: 2;
30
+ outcome_name: string;
31
+ user_id: string;
32
+ cost_usd: number;
33
+ signed_at: string;
34
+ signature: string;
35
+ public_key_fingerprint: string;
36
+ workflow_id: string | null;
37
+ parent_receipt_id: string | null;
38
+ chain_validation_hash: string | null;
39
+ workflow_checkpoint_idx: number | null;
40
+ workflow_total_spend_usd_to_date: number | null;
41
+ is_checkpoint: boolean;
42
+ checkpoint_label: string | null;
43
+ reviewer_cascade: ReviewerCascadeEvidence | null;
44
+ receipt_type?: 'outcome' | 'checkpoint' | 'cancel' | 'cap_hit' | 'completed';
45
+ cancelled_reason?: string | null;
46
+ }
47
+ export interface CheckpointReceipt extends ReceiptV2 {
48
+ is_checkpoint: true;
49
+ }
50
+ export interface WorkflowStatus {
51
+ workflow_id: string;
52
+ state: WorkflowState;
53
+ total_spend_usd: number;
54
+ budget_cap_usd: number;
55
+ budget_remaining_usd: number;
56
+ budget_pct_used: number;
57
+ outcome_count: number;
58
+ last_receipt_id: string | null;
59
+ last_checkpoint_id: string | null;
60
+ last_chain_hash: string | null;
61
+ duration_elapsed_ms: number;
62
+ duration_cap_ms: number;
63
+ duration_remaining_ms: number;
64
+ }
65
+ export interface ValidationResult {
66
+ ok: true;
67
+ }
68
+ export interface ValidationFailure {
69
+ ok: false;
70
+ broken_at: number;
71
+ reason: 'signature_invalid' | 'chain_hash_mismatch' | 'parent_reference_mismatch';
72
+ }
73
+ export type ChainValidationResult = ValidationResult | ValidationFailure;
74
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/workflow/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/workflow/types.ts"],"names":[],"mappings":""}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@agentguard-run/spend",
3
- "version": "0.4.3",
4
- "description": "Local-runtime spend caps and capability-gated model routing for AI agents. Prompts, API keys, and signing keys stay inside the customer runtime. Zero data plane involvement.",
3
+ "version": "0.5.0",
4
+ "description": "All terminology and labels used in AgentGuard materials are descriptive of software functionality only, not legal definitions or guarantees of compliance. Terms like receipt, audit log, evidence, audit trail, and attestation refer solely to cryptographically-signed records produced by the software. Full functional-use disclaimer in README.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "exports": {
@@ -44,6 +44,11 @@
44
44
  "types": "./dist/advisor/conversation.d.ts",
45
45
  "require": "./dist/advisor/conversation.js",
46
46
  "default": "./dist/advisor/conversation.js"
47
+ },
48
+ "./workflow": {
49
+ "types": "./dist/workflow/index.d.ts",
50
+ "require": "./dist/workflow/index.js",
51
+ "default": "./dist/workflow/index.js"
47
52
  }
48
53
  },
49
54
  "bin": {
@@ -65,7 +70,8 @@
65
70
  "src/cli/wizard.ts",
66
71
  "src/bindings",
67
72
  "src/advisor",
68
- "src/cli/advisor.ts"
73
+ "src/cli/advisor.ts",
74
+ "src/workflow"
69
75
  ],
70
76
  "scripts": {
71
77
  "build": "tsc",
@@ -103,7 +109,8 @@
103
109
  "url": "https://agentguard.run/contact"
104
110
  },
105
111
  "dependencies": {
106
- "@noble/ed25519": ">=2.3.0 <4.0.0"
112
+ "@noble/ed25519": "^3.1.0",
113
+ "@noble/hashes": "^2.2.0"
107
114
  },
108
115
  "peerDependencies": {
109
116
  "@anthropic-ai/sdk": ">=0.30.0",
@@ -38,6 +38,9 @@ export interface AdvisorBusinessProfile {
38
38
  requiredCapability: CapabilityTier;
39
39
  primaryModel: string;
40
40
  fallbackModel: string;
41
+ hostedOssModel: string;
42
+ offlineModel: string;
43
+ baaCoveredModel: string;
41
44
  perCallCapCents: number;
42
45
  perDayCapCents: number;
43
46
  perMonthCapCents: number;
@@ -70,8 +73,40 @@ export const ADVISOR_QUESTIONS: AdvisorQuestion[] = [
70
73
  { id: 'confirm', prompt: 'Confirm the setup or list any refinements before I write policy.yaml and quickstart code.' },
71
74
  ];
72
75
 
76
+
77
+
78
+ const CHINESE_ORIGIN_MODEL_PATTERNS = [/kimi/i, /qwen/i, /deepseek/i, /(^|\/)yi([-/]|$)/i, /01-ai/i];
79
+ const HOSTED_OSS_DEFAULT = 'baseten/llama-4';
80
+ const HOSTED_OSS_VELOCITY = 'baseten/llama-4-13b';
81
+ const OFFLINE_DEFAULT = 'offline/llama-4-m4-ultra';
82
+ const BAA_COVERED_DEFAULT = 'anthropic.claude-sonnet-4-v1:0';
83
+
84
+ export function isChineseOriginModel(model: string): boolean {
85
+ return CHINESE_ORIGIN_MODEL_PATTERNS.some((pattern) => pattern.test(model));
86
+ }
87
+
88
+ export function enforceAdvisorModelPolicy(model: string, posture: GovernancePosture, replacement = 'anthropic/claude-haiku-4-5'): string {
89
+ if (posture === 'compliance' && isChineseOriginModel(model)) {
90
+ console.warn(`AgentGuard Advisor blocked ${model} for compliance posture and selected ${replacement}.`);
91
+ return replacement;
92
+ }
93
+ return model;
94
+ }
95
+
96
+ function defaultModelsFor(vertical: string, hint: { primary: string; fallback: string }, posture: GovernancePosture): { primary: string; fallback: string; hostedOss: string; offline: string; baaCovered: string } {
97
+ if (posture === 'velocity') {
98
+ return { primary: HOSTED_OSS_VELOCITY, fallback: 'anthropic/claude-haiku-4-5', hostedOss: HOSTED_OSS_VELOCITY, offline: OFFLINE_DEFAULT, baaCovered: BAA_COVERED_DEFAULT };
99
+ }
100
+ if (posture === 'compliance') {
101
+ const regulatedPrimary = vertical === 'accounting' ? 'openai/gpt-5' : vertical === 'real-estate' ? 'google/gemini-3.1-pro-preview' : 'anthropic/claude-opus-4-7';
102
+ return { primary: regulatedPrimary, fallback: 'anthropic/claude-sonnet-4-6', hostedOss: HOSTED_OSS_DEFAULT, offline: OFFLINE_DEFAULT, baaCovered: BAA_COVERED_DEFAULT };
103
+ }
104
+ return { primary: hint.primary, fallback: HOSTED_OSS_DEFAULT, hostedOss: HOSTED_OSS_DEFAULT, offline: OFFLINE_DEFAULT, baaCovered: BAA_COVERED_DEFAULT };
105
+ }
106
+
73
107
  const VERTICAL_HINTS: Array<{ vertical: string; needles: string[]; scopeLabel: string; capability: CapabilityTier; primary: string; fallback: string; perCall: number }> = [
74
108
  { vertical: 'law-firm', needles: ['law', 'legal', 'attorney', 'paralegal', 'contract', 'matter', 'discovery'], scopeLabel: 'matter', capability: 'data_write', primary: 'anthropic/claude-sonnet-4-6', fallback: 'anthropic/claude-haiku-4-5', perCall: 300 },
109
+ { vertical: 'insurance', needles: ['insurance', 'carrier', 'producer', 'agency', 'policy comparison', 'quote letter', 'certificate of insurance', 'renewal', 'e&o'], scopeLabel: 'policy', capability: 'data_write', primary: 'anthropic/claude-sonnet-4-6', fallback: 'baseten/llama-4', perCall: 150 },
75
110
  { vertical: 'healthcare', needles: ['health', 'medical', 'dental', 'clinic', 'patient', 'chart', 'hipaa', 'encounter'], scopeLabel: 'encounter', capability: 'data_write', primary: 'anthropic.claude-sonnet-4-v1:0', fallback: 'amazon.nova-lite-v1:0', perCall: 300 },
76
111
  { vertical: 'ecommerce', needles: ['shopify', 'commerce', 'store', 'order', 'refund', 'chargeback', 'fraud', 'returns'], scopeLabel: 'order', capability: 'payment_initiate', primary: 'openai/gpt-5-mini', fallback: 'openai/gpt-4o-mini', perCall: 25 },
77
112
  { vertical: 'accounting', needles: ['accounting', 'bookkeeping', 'tax', 'ledger', 'receipt', 'close', 'audit', 'sox'], scopeLabel: 'engagement', capability: 'data_write', primary: 'anthropic/claude-sonnet-4-6', fallback: 'anthropic/claude-haiku-4-5', perCall: 150 },
@@ -157,6 +192,7 @@ export function buildBusinessProfile(answers: AdvisorAnswers, cwd = process.cwd(
157
192
  const tasks = parseTasks(answers.tasks ?? answers.building ?? hint.vertical);
158
193
  const perMonthCapCents = Math.max(1000, budgetCents);
159
194
  const perDayCapCents = Math.max(hint.perCall * 10, Math.ceil(perMonthCapCents / 20));
195
+ const models = defaultModelsFor(hint.vertical, hint, posture);
160
196
  return {
161
197
  vertical: hint.vertical,
162
198
  tenantId: tenantIdFromBusiness(answers.building ?? hint.vertical),
@@ -165,8 +201,11 @@ export function buildBusinessProfile(answers: AdvisorAnswers, cwd = process.cwd(
165
201
  tasks,
166
202
  monthlyBudgetCents: perMonthCapCents,
167
203
  requiredCapability: capabilityFor(joined, hint.capability, posture),
168
- primaryModel: hint.primary,
169
- fallbackModel: hint.fallback,
204
+ primaryModel: enforceAdvisorModelPolicy(models.primary, posture),
205
+ fallbackModel: enforceAdvisorModelPolicy(models.fallback, posture),
206
+ hostedOssModel: enforceAdvisorModelPolicy(models.hostedOss, posture),
207
+ offlineModel: models.offline,
208
+ baaCoveredModel: enforceAdvisorModelPolicy(models.baaCovered, posture),
170
209
  perCallCapCents: hint.perCall,
171
210
  perDayCapCents,
172
211
  perMonthCapCents,
@@ -120,6 +120,10 @@ requiredCapability: ${policy.requiredCapability ?? 'read_only'}
120
120
  scope:
121
121
  tenantId: ${quoteYaml(policy.scope.tenantId)}
122
122
  models:
123
+ cloudFrontierPrimary: ${profile.primaryModel}
124
+ hostedOssFallback: ${profile.hostedOssModel}
125
+ offlineOption: ${profile.offlineModel}
126
+ baaCoveredOption: ${profile.baaCoveredModel}
123
127
  primary: ${profile.primaryModel}
124
128
  fallback: ${profile.fallbackModel}
125
129
  governancePosture:
@@ -127,7 +131,10 @@ governancePosture:
127
131
  auditRetentionDays: ${posture.auditRetentionDays}
128
132
  approvalGates: ${posture.approvalGates}
129
133
  downgradeStyle: ${posture.downgradeStyle}
130
- ${canary}tasks:
134
+ ${canary}reviewerCascade:
135
+ default: ${profile.posture === 'compliance' ? 'always' : 'risk_triggered'}
136
+ triageClassifier: local-3b
137
+ tasks:
131
138
  ${tasks}
132
139
  caps:
133
140
  ${caps}
@@ -180,7 +187,7 @@ function whyForWindow(window: string): string {
180
187
 
181
188
  function systemInstructions(profile: AdvisorBusinessProfile): string {
182
189
  const posture = postureProfile(profile.posture);
183
- return `Run ${profile.vertical} tasks with ${profile.requiredCapability} capability under ${posture.label} governance posture. Prefer ${profile.primaryModel} for high-value work and ${profile.fallbackModel} for routine work. Keep evidence pointers in outputs and escalate anything outside the configured capability tier.`;
190
+ return `Run ${profile.vertical} tasks with ${profile.requiredCapability} capability under ${posture.label} governance posture. Prefer ${profile.primaryModel} for high-value work, ${profile.hostedOssModel} for hosted OSS fallback, ${profile.offlineModel} for offline runs, and ${profile.baaCoveredModel} when a BAA-covered provider is required. Keep evidence pointers in outputs and escalate anything outside the configured capability tier.`;
184
191
  }
185
192
 
186
193
  function quoteYaml(value: string): string {
@@ -70,7 +70,9 @@ Standard: default for most SaaS, e-commerce, real estate, agencies, local servic
70
70
 
71
71
  Compliance: for law, healthcare, dental, accounting, SOX, fintech, and regulated workflows. Default mode is canary, meaning enforce with a 5 percent sticky shadow sample. Capabilities are strict, with payment_execute for financial workflows and data_write minimum for PHI or PII workflows. Downgrade chains are conservative, and regulated capabilities block instead of downgrade. Audit retention is 7 years. Per-action approval gates are on for payment_execute.
72
72
 
73
- Custom: a Solo tier posture for future Outcome Builder workflows. In this release, tell the user Solo tier is required and choose Velocity, Standard, or Compliance instead.`,
73
+ Custom: a Solo tier posture for future Outcome Builder workflows. In this release, tell the user Solo tier is required and choose Velocity, Standard, or Compliance instead.
74
+
75
+ Chinese-origin model block for compliance posture: do not recommend Kimi K2, Qwen, DeepSeek, Yi, 01.AI, or hosted derivatives of those families for regulated or sensitive workflows. The block applies to cloud-frontier routes and hosted-OSS routes. If a user asks for one, state that Advisor cannot select it for compliance posture and offer Claude, GPT, Gemini, Llama on Baseten, Azure OpenAI, AWS Bedrock, Google Vertex, or Amazon Q instead.`,
74
76
  },
75
77
 
76
78
  {
@@ -80,7 +82,7 @@ Custom: a Solo tier posture for future Outcome Builder workflows. In this releas
80
82
 
81
83
  Law firms. Default posture: compliance. Pricing anchor: Solo $49 is about one Clio EasyStart seat. Startup $199 is below one CoCounsel seat. Growth $999 is a fraction of one Harvey seat. Outcomes: contract redlined, cap $2.00, model anthropic/claude-sonnet-4-6, tier data_write. Research memo delivered, cap $3.00, model anthropic/claude-opus-4-7, tier read_only. Matter intake closed, cap $1.50, model anthropic/claude-haiku-4-5, tier data_write. Deposition pack assembled, cap $5.00, model anthropic/claude-opus-4-7, tier read_only. Brief or motion drafted, cap $7.50, model anthropic/claude-opus-4-7, tier data_write. Compliance citations: ABA Model Rule 1.1 competence, ABA Model Rule 1.6 confidentiality, ABA Formal Opinion 512 on generative AI.
82
84
 
83
- Healthcare and dental. Default posture: compliance. Pricing anchor: Solo $49 is below common scribe entry pricing. Startup $199 fits a small multi-provider practice. Growth $999 governs several AI vendors. Outcomes: encounter documented, cap $0.50, model anthropic/claude-haiku-4-5 or BAA-covered Bedrock Anthropic, tier data_write. Claim coded and submitted, cap $0.25, model anthropic/claude-haiku-4-5, tier data_write. Prior auth letter drafted, cap $1.00, model anthropic/claude-sonnet-4-6, tier data_write. Inbox message resolved, cap $0.20, model anthropic/claude-haiku-4-5, tier data_write. Front desk call resolved, cap $0.30, model anthropic/claude-haiku-4-5, tier data_write. Compliance citations: 45 CFR 164.312(b) audit controls, 45 CFR 164.312(c)(1) integrity, 45 CFR 164.314 BAA organizational requirements.
85
+ Insurance agencies. Default posture: compliance. Pricing anchor: Solo $49 is about half a HawkSoft seat at $94 per user. Outcomes: quote letter drafted, cap $0.50, model anthropic/claude-haiku-4-5, tier data_write. Policy comparison generated, cap $1.50, model anthropic/claude-sonnet-4-6, tier data_write. Certificate of insurance issued, cap $0.25, model anthropic/claude-haiku-4-5, tier data_write. Renewal outreach completed, cap $0.15, model anthropic/claude-haiku-4-5, tier data_write. Inbound lead qualified, cap $0.30, model openai/gpt-5-mini, tier data_write. Reviewer Cascade always runs for policy comparisons, quote letters, and claims correspondence. Compliance citations: NAIC Model Bulletin on AI systems, 11 NYCRR section 215.13, Verisk CG 40 47 and CG 40 48 AI exclusions.
84
86
 
85
87
  Accounting and bookkeeping. Default posture: compliance. Pricing anchor: Solo $49 is about one QuickBooks or low-end practice seat. Startup $199 is below four Karbon Team seats. Growth $999 fits a 10 to 15 staff firm. Outcomes: bookkeeping cycle closed, cap $5.00, model anthropic/claude-haiku-4-5, tier data_write. Tax return drafted, cap $7.50, model openai/gpt-5, tier data_write. IRS or state notice response, cap $3.00, model anthropic/claude-sonnet-4-6, tier data_write. Client email resolved, cap $0.50, model anthropic/claude-haiku-4-5, tier data_write. Workpaper completed, cap $4.00, model openai/gpt-5, tier data_write. Compliance citations: PCAOB AS 1215 audit documentation retention, SOX 802 and 18 U.S.C. 1519 record preservation, GLBA Safeguards Rule 16 CFR Part 314.
86
88
 
@@ -96,7 +98,7 @@ Real estate and mortgage. Default posture: standard. Pricing anchor: Solo $49 is
96
98
  Law firm:
97
99
  Tasks: contract redline, novel contract review, legal research, brief drafting, deposition prep, discovery review. Models: anthropic/claude-haiku-4-5 for boilerplate and discovery, anthropic/claude-sonnet-4-6 for research and novel terms, anthropic/claude-opus-4-7 for brief drafting and deposition analysis. Caps: $0.25 discovery tagging, $1 contract redline, $3 novel review, $4 legal research, $8 brief drafting. Scope key: matter or client matter code in agentId, attorney in userId. Compliance: privilege workflows need zero proxy, per-matter accounting, jurisdiction-aware model allowlists, and signed decision receipts for audit packets.
98
100
 
99
- Healthcare:
101
+ Healthcare partner-only:
100
102
  Tasks: patient triage, chart review, clinical documentation drafting, prior authorization narrative, medical literature search, patient education, insurance verification, appointment scheduling. Models: BAA-covered Bedrock Anthropic for PHI, OpenRouter openai/gpt-5-mini or anthropic/claude-haiku-4-5 for non-PHI. Caps: $0.10 education, $0.25 insurance verification, $0.50 triage, $2 chart review, $3 documentation, $5 prior authorization. Scope key: encounter or patient encounter in agentId, clinician in userId. Compliance: PHI workflows require data_write minimum plus provider BAA controls. Non-PHI can use OpenRouter.
101
103
 
102
104
  E-commerce:
@@ -160,6 +162,7 @@ anthropic.claude-opus-4-v1:0: input $15.00, output $75.00.
160
162
  anthropic.claude-sonnet-4-v1:0: input $3.00, output $15.00.
161
163
  amazon.nova-pro-v1:0: input $0.80, output $3.20.
162
164
  amazon.nova-lite-v1:0: input $0.06, output $0.24.
165
+ Baseten hosted OSS default: baseten/llama-4 at $0.108 per H100-hour as a cost-best hosted fallback. Velocity posture may use baseten/llama-4-13b as primary with anthropic/claude-haiku-4-5 as fallback. Compliance posture uses a cloud frontier primary such as anthropic/claude-opus-4-7, openai/gpt-5, or google/gemini-3.1-pro-preview, with Reviewer Cascade always triggered for regulated outcomes. Offline options are Llama-4 on M4 Ultra or dual H100. BAA-covered options are Azure OpenAI, AWS Bedrock, Google Vertex, and Amazon Q.
163
166
  For unknown live OpenRouter models, ask the customer to run agentguard models --sync-pricing before using them in a policy.`,
164
167
  },
165
168
  {
@@ -12,6 +12,7 @@ import { readDecisionSpend, reviewAnomalies } from '../advisor/anomaly';
12
12
  import { ADVISOR_SYSTEM_PROMPT } from '../advisor/system-prompt';
13
13
  import { createAdvisorSessionLogger, writeAdvisorOutputs } from '../advisor/output';
14
14
  import { banner, cyanBold, dim, green, yellow } from './colors';
15
+ import { confirmPostureSoftCap } from './soft-cap';
15
16
 
16
17
  interface AdvisorCliOptions {
17
18
  provider: AdvisorProvider;
@@ -48,8 +49,14 @@ export async function runAdvisor(argv: string[]): Promise<number> {
48
49
 
49
50
  const options = parseOptions(argv);
50
51
  if (options.posture === 'custom') {
51
- console.log('Solo tier required for custom governance posture. Choose velocity, standard, or compliance in this release.');
52
- return 0;
52
+ const ok = await confirmPostureSoftCap('custom', { yes: options.yes || options.defaults });
53
+ if (!ok) return 0;
54
+ options.posture = 'standard';
55
+ }
56
+ if (options.posture && options.posture !== 'standard' && !process.env.AGENTGUARD_LICENSE_KEY) {
57
+ const ok = await confirmPostureSoftCap(options.posture, { yes: options.yes || options.defaults });
58
+ if (!ok) return 0;
59
+ options.posture = 'standard';
53
60
  }
54
61
  const apiKey = resolveAdvisorApiKey(options.provider);
55
62
  if (options.provider !== 'mock' && !apiKey) {
package/src/cli/auth.ts CHANGED
@@ -5,6 +5,7 @@ import * as https from 'https';
5
5
  import * as path from 'path';
6
6
  import * as readline from 'readline';
7
7
  import { AGENTGUARD_SPEND_VERSION } from '../index';
8
+ import { licenseKeyFingerprint, readConfiguredLicenseKey, writeConfiguredLicenseKey } from '../license';
8
9
  import { agentguardHome, banner, dim, green, statusBar, yellow } from './colors';
9
10
 
10
11
  const OPENROUTER_AUTH_URL = 'https://openrouter.ai/api/v1/auth/key';
@@ -50,11 +51,12 @@ export function writeOpenRouterKey(key: string): void {
50
51
 
51
52
  export async function runAuth(argv: string[]): Promise<number> {
52
53
  if (argv.includes('--help') || argv.includes('-h')) {
53
- console.log('agentguard auth openrouter | status | clear openrouter');
54
+ console.log('agentguard auth openrouter | license-key <KEY> | status | clear openrouter');
54
55
  return 0;
55
56
  }
56
57
  const sub = argv[0] ?? 'status';
57
58
  if (sub === 'status') return authStatus();
59
+ if (sub === 'license-key') return configureLicenseKey(argv[1]);
58
60
  if (sub === 'clear' && argv[1] === 'openrouter') return clearOpenRouter();
59
61
  if (sub === 'openrouter') return configureOpenRouter(argv);
60
62
  console.error(`agentguard auth: unknown command '${argv.join(' ')}'`);
@@ -93,12 +95,28 @@ function authStatus(): number {
93
95
  } else {
94
96
  console.log(` ${yellow('openrouter')} not configured`);
95
97
  }
98
+ const licenseKey = readConfiguredLicenseKey();
99
+ if (licenseKey) {
100
+ console.log(` ${green('license')} configured fingerprint ${licenseKeyFingerprint(licenseKey)}`);
101
+ } else {
102
+ console.log(` ${yellow('license')} free tier`);
103
+ }
96
104
  console.log('');
97
105
  console.log(' ' + statusBar());
98
106
  console.log('');
99
107
  return 0;
100
108
  }
101
109
 
110
+ function configureLicenseKey(key?: string): number {
111
+ if (!key?.trim()) {
112
+ console.error('Usage: agentguard auth license-key <KEY>');
113
+ return 2;
114
+ }
115
+ writeConfiguredLicenseKey(key);
116
+ console.log(`License key saved. fingerprint ${licenseKeyFingerprint(key)}`);
117
+ return 0;
118
+ }
119
+
102
120
  function clearOpenRouter(): number {
103
121
  try { fs.unlinkSync(openRouterKeyPath()); } catch { return 0; }
104
122
  console.log('OpenRouter key removed.');
@@ -0,0 +1,35 @@
1
+ import { canonicalJSONStringify, sha256, verifyReceipt } from './receipt';
2
+ import type { ChainValidationResult, ReceiptV2 } from './types';
3
+
4
+ export async function validateReceiptChain(receipts: ReceiptV2[]): Promise<ChainValidationResult> {
5
+ if (receipts.length === 0) return { ok: true };
6
+
7
+ for (let i = 0; i < receipts.length; i++) {
8
+ if (!verifyReceipt(receipts[i]!)) {
9
+ return { ok: false, broken_at: i, reason: 'signature_invalid' };
10
+ }
11
+ if (i === 0) continue;
12
+ const prev = receipts[i - 1]!;
13
+ const curr = receipts[i]!;
14
+ const expectedHash = await computeChainHash(prev);
15
+ if (curr.chain_validation_hash !== expectedHash) {
16
+ return { ok: false, broken_at: i, reason: 'chain_hash_mismatch' };
17
+ }
18
+ if (curr.parent_receipt_id !== prev.receipt_id) {
19
+ return { ok: false, broken_at: i, reason: 'parent_reference_mismatch' };
20
+ }
21
+ }
22
+
23
+ return { ok: true };
24
+ }
25
+
26
+ export async function computeChainHash(receipt: ReceiptV2): Promise<string> {
27
+ const canonical = canonicalJSONStringify({
28
+ receipt_id: receipt.receipt_id,
29
+ workflow_id: receipt.workflow_id,
30
+ cost_usd: receipt.cost_usd,
31
+ signed_at: receipt.signed_at,
32
+ signature: receipt.signature,
33
+ });
34
+ return sha256(canonical);
35
+ }