@aiwg/cli 2026.9.20 → 2026.9.21

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 (272) hide show
  1. package/agentic/code/providers/capability-matrix.yaml +58 -6
  2. package/agentic/code/providers/model-capabilities.v1.json +34 -0
  3. package/agentic/code/providers/model-catalog.v1.json +55 -13
  4. package/bin/aiwg.mjs +2 -0
  5. package/dist/src/api/index.d.ts +1 -0
  6. package/dist/src/api/index.js +1 -0
  7. package/dist/src/artifacts/enrichment/cli.js +1 -1
  8. package/dist/src/artifacts/views/cli.js +1 -1
  9. package/dist/src/audit/operator-decision.js +10 -0
  10. package/dist/src/auth/credential-store.js +127 -27
  11. package/dist/src/catalog/builtin-models.json +272 -40
  12. package/dist/src/catalog/cli.js +1 -1
  13. package/dist/src/catalog/cli.mjs +1 -1
  14. package/dist/src/cli/handlers/effect.js +872 -0
  15. package/dist/src/cli/handlers/help.js +2 -0
  16. package/dist/src/cli/handlers/index.js +4 -2
  17. package/dist/src/cli/handlers/models.js +41 -10
  18. package/dist/src/cli/handlers/ralph.js +1 -1
  19. package/dist/src/cli/handlers/sessions.js +113 -37
  20. package/dist/src/cli/handlers/subcommands.js +31 -0
  21. package/dist/src/cli/handlers/use.js +46 -9
  22. package/dist/src/cli/handlers/utilities.js +10 -0
  23. package/dist/src/cli/handlers/workspace.js +3 -1
  24. package/dist/src/cli/scope-resolver.js +112 -9
  25. package/dist/src/cli/services/deployment-verification.js +5 -0
  26. package/dist/src/config/aiwg-config.js +3 -0
  27. package/dist/src/decision/acceptance-rollout.js +93 -0
  28. package/dist/src/decision/acceptance.js +175 -0
  29. package/dist/src/decision/adapters/jev.js +234 -13
  30. package/dist/src/decision/adapters/llm-subagent.js +60 -12
  31. package/dist/src/decision/admission.js +550 -0
  32. package/dist/src/decision/batch-benchmark.js +47 -0
  33. package/dist/src/decision/batch-receipts/accounting.js +75 -0
  34. package/dist/src/decision/batch-receipts/export.js +40 -0
  35. package/dist/src/decision/batch-receipts/index.js +9 -0
  36. package/dist/src/decision/batch-receipts/protection.js +40 -0
  37. package/dist/src/decision/batch-receipts/receipt.js +44 -0
  38. package/dist/src/decision/batch-receipts/result-store.js +341 -0
  39. package/dist/src/decision/batch-receipts/store.js +362 -0
  40. package/dist/src/decision/batch-receipts/types.js +2 -0
  41. package/dist/src/decision/batch-receipts/validate.js +127 -0
  42. package/dist/src/decision/batch.js +68 -0
  43. package/dist/src/decision/calibration/index.js +4 -0
  44. package/dist/src/decision/calibration/receipts.js +130 -0
  45. package/dist/src/decision/calibration/registry.js +258 -0
  46. package/dist/src/decision/calibration/types.js +2 -0
  47. package/dist/src/decision/compile-cache/benchmark.js +62 -0
  48. package/dist/src/decision/compile-cache/compatibility.js +65 -0
  49. package/dist/src/decision/compile-cache/file-store.js +280 -0
  50. package/dist/src/decision/compile-cache/identity.js +26 -0
  51. package/dist/src/decision/compile-cache/index.js +9 -0
  52. package/dist/src/decision/compile-cache/prefix.js +21 -0
  53. package/dist/src/decision/compile-cache/records.js +86 -0
  54. package/dist/src/decision/compile-cache/runtime.js +68 -0
  55. package/dist/src/decision/compile-cache/store.js +108 -0
  56. package/dist/src/decision/compile-cache/types.js +2 -0
  57. package/dist/src/decision/context-plan.js +234 -0
  58. package/dist/src/decision/context-qualification.js +60 -0
  59. package/dist/src/decision/ensemble/aggregate.js +215 -0
  60. package/dist/src/decision/ensemble/contract.js +316 -0
  61. package/dist/src/decision/ensemble/drift.js +51 -0
  62. package/dist/src/decision/ensemble/index.js +6 -0
  63. package/dist/src/decision/ensemble/report.js +71 -0
  64. package/dist/src/decision/ensemble/types.js +2 -0
  65. package/dist/src/decision/evaluate.js +1024 -78
  66. package/dist/src/decision/file-lifecycle-store.js +168 -0
  67. package/dist/src/decision/graph-beam.js +40 -0
  68. package/dist/src/decision/graph-budget.js +110 -0
  69. package/dist/src/decision/graph-decision-bridge.js +93 -0
  70. package/dist/src/decision/graph-evidence.js +123 -0
  71. package/dist/src/decision/graph-flow-adapter.js +56 -0
  72. package/dist/src/decision/graph-flow.js +105 -0
  73. package/dist/src/decision/graph-parallel.js +79 -0
  74. package/dist/src/decision/graph-patterns.js +20 -0
  75. package/dist/src/decision/graph-run-store.js +66 -0
  76. package/dist/src/decision/graph-run.js +166 -0
  77. package/dist/src/decision/graph-skill-bridge.js +91 -0
  78. package/dist/src/decision/graph-templates.js +56 -0
  79. package/dist/src/decision/graph.js +115 -0
  80. package/dist/src/decision/index.js +35 -0
  81. package/dist/src/decision/job-accounting.js +93 -0
  82. package/dist/src/decision/job-contract.js +186 -0
  83. package/dist/src/decision/job-effects.js +95 -0
  84. package/dist/src/decision/job-evaluate.js +80 -0
  85. package/dist/src/decision/job-gateway.js +121 -0
  86. package/dist/src/decision/job-http.js +194 -0
  87. package/dist/src/decision/job-payload-store.js +258 -0
  88. package/dist/src/decision/job-poll-limiter.js +92 -0
  89. package/dist/src/decision/job-quota.js +158 -0
  90. package/dist/src/decision/job-runtime.js +240 -0
  91. package/dist/src/decision/job-scheduler.js +26 -0
  92. package/dist/src/decision/job-service.js +117 -0
  93. package/dist/src/decision/job-store.js +264 -0
  94. package/dist/src/decision/job-worker.js +141 -0
  95. package/dist/src/decision/lifecycle.js +148 -0
  96. package/dist/src/decision/patterns/artifacts.js +173 -0
  97. package/dist/src/decision/patterns/catalog.js +108 -0
  98. package/dist/src/decision/patterns/durable-review.js +237 -0
  99. package/dist/src/decision/patterns/index.js +8 -0
  100. package/dist/src/decision/patterns/playground.js +401 -0
  101. package/dist/src/decision/patterns/recorded.js +43 -0
  102. package/dist/src/decision/patterns/specs.js +174 -0
  103. package/dist/src/decision/patterns/types.js +2 -0
  104. package/dist/src/decision/portable-secrets.js +53 -0
  105. package/dist/src/decision/projection.js +210 -0
  106. package/dist/src/decision/qualification/capture.js +67 -0
  107. package/dist/src/decision/qualification/drift.js +68 -0
  108. package/dist/src/decision/qualification/gate-evidence.js +172 -0
  109. package/dist/src/decision/qualification/gates.js +83 -0
  110. package/dist/src/decision/qualification/index.js +12 -0
  111. package/dist/src/decision/qualification/load.js +275 -0
  112. package/dist/src/decision/qualification/manifest.js +37 -0
  113. package/dist/src/decision/qualification/privacy.js +41 -0
  114. package/dist/src/decision/qualification/quality.js +213 -0
  115. package/dist/src/decision/qualification/release.js +116 -0
  116. package/dist/src/decision/qualification/runner.js +408 -0
  117. package/dist/src/decision/qualification/types.js +2 -0
  118. package/dist/src/decision/receipts.js +30 -7
  119. package/dist/src/decision/result-cache/index.js +6 -0
  120. package/dist/src/decision/result-cache/integrity.js +28 -0
  121. package/dist/src/decision/result-cache/key.js +28 -0
  122. package/dist/src/decision/result-cache/service.js +92 -0
  123. package/dist/src/decision/result-cache/store.js +327 -0
  124. package/dist/src/decision/result-cache/types.js +3 -0
  125. package/dist/src/decision/review/authorization.js +110 -0
  126. package/dist/src/decision/review/effect-ledger-adapter.js +321 -0
  127. package/dist/src/decision/review/index.js +12 -0
  128. package/dist/src/decision/review/migrate.js +34 -0
  129. package/dist/src/decision/review/operator-audit.js +59 -0
  130. package/dist/src/decision/review/recovery-adapters.js +252 -0
  131. package/dist/src/decision/review/recovery.js +33 -0
  132. package/dist/src/decision/review/service.js +498 -0
  133. package/dist/src/decision/review/store.js +219 -0
  134. package/dist/src/decision/review/types.js +2 -0
  135. package/dist/src/decision/review/validate.js +205 -0
  136. package/dist/src/decision/scheduler.js +115 -0
  137. package/dist/src/decision/telemetry/context.js +42 -0
  138. package/dist/src/decision/telemetry/debug-sidecar.js +152 -0
  139. package/dist/src/decision/telemetry/exporter.js +77 -0
  140. package/dist/src/decision/telemetry/file-debug-backend.js +112 -0
  141. package/dist/src/decision/telemetry/index.js +13 -0
  142. package/dist/src/decision/telemetry/mapping.js +146 -0
  143. package/dist/src/decision/telemetry/metrics.js +164 -0
  144. package/dist/src/decision/telemetry/otlp-http.js +183 -0
  145. package/dist/src/decision/telemetry/redaction.js +93 -0
  146. package/dist/src/decision/telemetry/retention.js +109 -0
  147. package/dist/src/decision/telemetry/runtime.js +310 -0
  148. package/dist/src/decision/telemetry/trace.js +158 -0
  149. package/dist/src/decision/telemetry/types.js +3 -0
  150. package/dist/src/decision/validate.js +135 -5
  151. package/dist/src/effects/checkpoint-sinks.js +94 -0
  152. package/dist/src/effects/checkpoint.js +76 -0
  153. package/dist/src/effects/decision-links.js +54 -0
  154. package/dist/src/effects/errors.js +48 -0
  155. package/dist/src/effects/identity.js +178 -0
  156. package/dist/src/effects/index.js +25 -0
  157. package/dist/src/effects/keyring.js +160 -0
  158. package/dist/src/effects/keys.js +130 -0
  159. package/dist/src/effects/ledger.js +435 -0
  160. package/dist/src/effects/lifecycle.js +110 -0
  161. package/dist/src/effects/lock-recovery.js +269 -0
  162. package/dist/src/effects/reader.js +103 -0
  163. package/dist/src/effects/records.js +114 -0
  164. package/dist/src/effects/redaction.js +51 -0
  165. package/dist/src/effects/schema.js +73 -0
  166. package/dist/src/effects/store.js +239 -0
  167. package/dist/src/effects/types.js +49 -0
  168. package/dist/src/effects/verifiers/decision.js +168 -0
  169. package/dist/src/effects/verifiers/file.js +106 -0
  170. package/dist/src/effects/verifiers/git.js +206 -0
  171. package/dist/src/effects/verifiers/index.js +90 -0
  172. package/dist/src/effects/verifiers/registry.js +148 -0
  173. package/dist/src/effects/verifiers/review.js +123 -0
  174. package/dist/src/effects/verifiers/tracker.js +299 -0
  175. package/dist/src/effects/verifiers/types.js +21 -0
  176. package/dist/src/effects/verify.js +295 -0
  177. package/dist/src/evidence/bundle.js +2 -0
  178. package/dist/src/extensions/commands/definitions.js +28 -0
  179. package/dist/src/extensions/manifest.js +1 -0
  180. package/dist/src/marketplace/provenance.js +25 -27
  181. package/dist/src/mcp/cli.mjs +11 -1
  182. package/dist/src/mcp/grok-build-config.mjs +189 -0
  183. package/dist/src/mcp/registry.js +21 -1
  184. package/dist/src/mcp/registry.mjs +20 -1
  185. package/dist/src/models/grok-build-models.js +356 -0
  186. package/dist/src/models/index.js +1 -0
  187. package/dist/src/models/model-capabilities.v1.json +34 -0
  188. package/dist/src/models/model-catalog.v1.json +55 -13
  189. package/dist/src/models/model-discovery.js +41 -5
  190. package/dist/src/models/provider-policy.js +3 -2
  191. package/dist/src/plugin/skill-command-translator.js +1 -0
  192. package/dist/src/providers/capability-matrix.yaml +58 -6
  193. package/dist/src/providers/grok-build-acp.js +192 -0
  194. package/dist/src/providers/grok-build-dispatch.js +251 -0
  195. package/dist/src/providers/grok-build-headless.js +170 -0
  196. package/dist/src/providers/muse-paths.js +105 -0
  197. package/dist/src/providers/provider-definitions.js +93 -7
  198. package/dist/src/providers/provider-definitions.mjs +21 -1
  199. package/dist/src/providers/provider-inventory.js +3 -0
  200. package/dist/src/security/artifact-attestation.js +4 -8
  201. package/dist/src/security/signing.js +119 -0
  202. package/dist/src/sessions/adapters/grok-build.js +187 -0
  203. package/dist/src/sessions/adapters/muse.js +412 -0
  204. package/dist/src/sessions/contracts.js +1 -1
  205. package/dist/src/sessions/grok-build-cli.js +196 -0
  206. package/dist/src/sessions/index.js +3 -0
  207. package/dist/src/sessions/workspace-discovery.js +9 -5
  208. package/dist/src/skills/deployer.js +33 -5
  209. package/dist/src/smiths/context-pipeline/claude-hook.js +2 -0
  210. package/dist/src/smiths/context-pipeline/context-file-safety.js +18 -0
  211. package/dist/src/smiths/context-pipeline/finalization.js +1 -32
  212. package/dist/src/smiths/context-pipeline/generator.js +12 -0
  213. package/dist/src/smiths/context-pipeline/index.js +1 -1
  214. package/dist/src/smiths/context-pipeline/managed-hook.js +2 -0
  215. package/dist/src/smiths/context-pipeline/muse-bridge.js +50 -0
  216. package/dist/src/smiths/context-pipeline/provider-policy.js +5 -0
  217. package/dist/src/smiths/context-pipeline/workspace-context.js +22 -16
  218. package/dist/src/smiths/hook-bridge/grok-build-translator.js +64 -0
  219. package/dist/src/smiths/hook-bridge/index.js +3 -0
  220. package/dist/src/smiths/hook-bridge/shim.js +2 -0
  221. package/dist/src/smiths/hook-bridge/types.js +5 -0
  222. package/dist/src/smiths/skillsmith/collision-detector.js +1 -0
  223. package/dist/src/storage/protected-files.js +102 -0
  224. package/dist/src/tracker/forge-http.js +371 -0
  225. package/dist/src/tracker/project-inputs.js +41 -0
  226. package/package.json +2 -1
  227. package/schemas/decision/BatchReceipt.v1.schema.json +50 -0
  228. package/schemas/decision/CalibrationArtifact.v1.schema.json +76 -0
  229. package/schemas/decision/CalibrationCompatibility.v1.schema.json +18 -0
  230. package/schemas/decision/CalibrationGovernanceReceipt.v1.schema.json +44 -0
  231. package/schemas/decision/DecisionAdmissionEvidence.v1.schema.json +43 -0
  232. package/schemas/decision/DecisionBinding.schema.json +309 -0
  233. package/schemas/decision/DecisionBinding.v1alpha2.schema.json +369 -0
  234. package/schemas/decision/DecisionCacheTelemetry.v1.schema.json +20 -0
  235. package/schemas/decision/DecisionChampionChallenger.v1.schema.json +100 -0
  236. package/schemas/decision/DecisionCompileCacheEntry.v1.schema.json +35 -0
  237. package/schemas/decision/DecisionCompileCacheIdentity.v1.schema.json +26 -0
  238. package/schemas/decision/DecisionCompileCacheTombstone.v1.schema.json +31 -0
  239. package/schemas/decision/DecisionContextEvidence.v1.schema.json +136 -0
  240. package/schemas/decision/DecisionDefinition.schema.json +159 -0
  241. package/schemas/decision/DecisionDefinition.v1alpha2.schema.json +203 -0
  242. package/schemas/decision/DecisionDriftResponse.v1.schema.json +55 -0
  243. package/schemas/decision/DecisionEnsembleAggregate.v1.schema.json +86 -0
  244. package/schemas/decision/DecisionEnsembleIntegrityReport.v1.schema.json +64 -0
  245. package/schemas/decision/DecisionEnsemblePolicy.v1.schema.json +130 -0
  246. package/schemas/decision/DecisionGraph.v1.schema.json +77 -0
  247. package/schemas/decision/DecisionJob.v1.schema.json +71 -0
  248. package/schemas/decision/DecisionLifecyclePolicy.v1.schema.json +52 -0
  249. package/schemas/decision/DecisionProjectionPolicy.v1.schema.json +65 -0
  250. package/schemas/decision/DecisionProviderPrefixCompatibility.v1.schema.json +45 -0
  251. package/schemas/decision/DecisionProviderPrefixEvidence.v1.schema.json +17 -0
  252. package/schemas/decision/DecisionProviderPrefixIdentity.v1.schema.json +36 -0
  253. package/schemas/decision/DecisionResult.schema.json +524 -0
  254. package/schemas/decision/DecisionResult.v1alpha2.schema.json +619 -0
  255. package/schemas/decision/DecisionResultCacheEntry.v1.schema.json +53 -0
  256. package/schemas/decision/DecisionReview.schema.json +29 -0
  257. package/schemas/decision/DecisionRuleset.schema.json +313 -0
  258. package/schemas/decision/DecisionRuleset.v1alpha2.schema.json +313 -0
  259. package/schemas/decision/DecisionTelemetryTrace.v1.schema.json +95 -0
  260. package/schemas/decision/RulesetResult.schema.json +721 -0
  261. package/schemas/decision/RulesetResult.v1alpha2.schema.json +891 -0
  262. package/schemas/security/pr-evidence-receipt.v1.schema.json +77 -0
  263. package/schemas/security/threat-assessment-report.v1.schema.json +71 -1
  264. package/tools/agents/deploy-agents.mjs +21 -6
  265. package/tools/agents/providers/base.mjs +7 -4
  266. package/tools/agents/providers/codex.mjs +1 -1
  267. package/tools/agents/providers/grok-build.d.mts +7 -0
  268. package/tools/agents/providers/grok-build.mjs +249 -9
  269. package/tools/agents/providers/model-role.mjs +6 -3
  270. package/tools/agents/providers/muse-hooks.mjs +785 -0
  271. package/tools/agents/providers/muse-paths.mjs +112 -0
  272. package/tools/agents/providers/muse.mjs +336 -0
@@ -52,6 +52,8 @@ export const EXIT_CODE_MAP = {
52
52
  factory: { allow: 0, block: 1, warn: 0 },
53
53
  hermes: { allow: 0, block: 1, warn: 2 },
54
54
  openclaw: { allow: 0, block: 1, warn: 2 },
55
+ // Grok only honors an explicit PreToolUse deny decision; other failures are fail-open.
56
+ 'grok-build': { allow: 0, block: 2, warn: 0 },
55
57
  };
56
58
  /**
57
59
  * Generate a small bash shim that:
@@ -27,5 +27,10 @@ export const NATIVE_ENV_VAR_MAP = {
27
27
  copilot: {
28
28
  PROJECT_DIR: '$GITHUB_WORKSPACE',
29
29
  },
30
+ 'grok-build': {
31
+ PROJECT_DIR: '$GROK_WORKSPACE_ROOT',
32
+ HOOK_EVENT: '$GROK_HOOK_EVENT',
33
+ SESSION_ID: '$GROK_SESSION_ID',
34
+ },
30
35
  };
31
36
  //# sourceMappingURL=types.js.map
@@ -37,6 +37,7 @@ const PLATFORM_BUILTINS = {
37
37
  'warp': ['help', 'settings'],
38
38
  'hermes': [],
39
39
  'grokbot': [],
40
+ 'muse': [],
40
41
  'openclaw': [],
41
42
  'openhuman': [],
42
43
  'factory': [],
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Protected local file primitives shared by durable stores.
3
+ *
4
+ * Records are stored as exact RFC 8785 canonical bytes, keyed with HMAC-SHA256,
5
+ * kept in owner-only directories, and published with exclusive hard links so a
6
+ * destination is written at most once. Nothing here knows about any caller's
7
+ * lifecycle or error taxonomy: callers supply their own error factories.
8
+ *
9
+ * This module must not import from `src/decision/**`.
10
+ */
11
+ import { createHmac, randomUUID, timingSafeEqual } from 'node:crypto';
12
+ import { link, lstat, mkdir, open, rm } from 'node:fs/promises';
13
+ import { join } from 'node:path';
14
+ import { canonicalJson } from '../security/artifact-trust.js';
15
+ /** Opaque by default: callers learn only that stored bytes failed verification. */
16
+ export class ProtectedFileIntegrityError extends Error {
17
+ constructor() { super('Protected file integrity check failed'); this.name = 'ProtectedFileIntegrityError'; }
18
+ }
19
+ export function requireIntegrityKey(key, label) {
20
+ if (!(key instanceof Uint8Array) || key.length < 32)
21
+ throw new Error(`${label} integrity key must be at least 32 bytes`);
22
+ return Buffer.from(key);
23
+ }
24
+ export function keyedName(key, domain, parts) {
25
+ return createHmac('sha256', key).update(`${domain}\0`).update(parts.join('\0')).digest('hex');
26
+ }
27
+ export function macFor(key, domain, value) {
28
+ return createHmac('sha256', key).update(`${domain}:`).update(canonicalJson(value)).digest('hex');
29
+ }
30
+ export function macMatches(key, domain, value, mac) {
31
+ return typeof mac === 'string' && /^[a-f0-9]{64}$/.test(mac)
32
+ && timingSafeEqual(Buffer.from(mac, 'hex'), Buffer.from(macFor(key, domain, value), 'hex'));
33
+ }
34
+ /** Stored bytes must be exactly the canonical serialization, so no byte can change unnoticed. */
35
+ export function serialized(value) { return `${canonicalJson(value)}\n`; }
36
+ /** Parse stored bytes, refusing anything that is not exactly its canonical serialization. */
37
+ export function parseCanonical(raw, integrityError = () => new ProtectedFileIntegrityError()) {
38
+ const parsed = JSON.parse(raw);
39
+ if (serialized(parsed) !== raw)
40
+ throw integrityError();
41
+ return parsed;
42
+ }
43
+ /** Create (if needed) and require an owner-only directory. Any group or other permission bit is refused. */
44
+ export async function ensurePrivateDirectory(directory, message, error = (text) => new Error(text)) {
45
+ await mkdir(directory, { recursive: true, mode: 0o700 });
46
+ const stat = await lstat(directory);
47
+ if (!stat.isDirectory() || (stat.mode & 0o077) !== 0)
48
+ throw error(message);
49
+ }
50
+ export async function syncDirectory(directory) {
51
+ const handle = await open(directory, 'r');
52
+ try {
53
+ await handle.sync();
54
+ }
55
+ finally {
56
+ await handle.close();
57
+ }
58
+ }
59
+ export async function exists(path) {
60
+ try {
61
+ await lstat(path);
62
+ return true;
63
+ }
64
+ catch (error) {
65
+ if (error.code === 'ENOENT')
66
+ return false;
67
+ throw error;
68
+ }
69
+ }
70
+ /** Exclusive hard-link publication. Returns false when the destination already exists. */
71
+ export async function publishExclusive(directory, destination, contents, temporaryPrefix) {
72
+ const temporary = join(directory, `.${temporaryPrefix}-${randomUUID()}.tmp`);
73
+ const handle = await open(temporary, 'wx', 0o600);
74
+ try {
75
+ await handle.writeFile(contents, 'utf8');
76
+ await handle.sync();
77
+ }
78
+ finally {
79
+ await handle.close();
80
+ }
81
+ try {
82
+ await link(temporary, destination);
83
+ }
84
+ catch (error) {
85
+ if (error.code === 'EEXIST')
86
+ return false;
87
+ throw error;
88
+ }
89
+ finally {
90
+ await rm(temporary, { force: true });
91
+ }
92
+ await syncDirectory(directory);
93
+ return true;
94
+ }
95
+ /**
96
+ * Publish a body-free tombstone record in canonical form. An existing tombstone
97
+ * already has the same effect, so publication is idempotent.
98
+ */
99
+ export async function publishTombstone(directory, path, record, temporaryPrefix) {
100
+ await publishExclusive(directory, path, serialized(record), temporaryPrefix);
101
+ }
102
+ //# sourceMappingURL=protected-files.js.map
@@ -0,0 +1,371 @@
1
+ /**
2
+ * Read-only, bounded, paginated Gitea/GitHub REST reads for tracker effect
3
+ * verifiers (#2719).
4
+ *
5
+ * This is the TypeScript port of the forge layer in
6
+ * `tools/security/pr-evidence-forge.mjs` (bounded bodies, header-driven
7
+ * pagination, provider-specific auth headers), plus the reads the verifiers
8
+ * need: issue state, one page of issue comments, PR merge state and a commit
9
+ * message. Two transports share one parser: HTTP (`fetch`) and the forge CLI
10
+ * (`gh api --include`).
11
+ *
12
+ * Every failure is a `ForgeReadError` with a fixed message and a classified
13
+ * `code`. Access material stays inside the transport: it is never part of an
14
+ * error, a result or a log line.
15
+ */
16
+ import { spawn } from 'node:child_process';
17
+ export class ForgeReadError extends Error {
18
+ code;
19
+ status;
20
+ constructor(code, status) {
21
+ super(`Forge read failed: ${code}${status ? ` (HTTP ${status})` : ''}`);
22
+ this.code = code;
23
+ this.status = status;
24
+ this.name = 'ForgeReadError';
25
+ }
26
+ }
27
+ export const FORGE_MAX_RESPONSE_BYTES = 2_000_000;
28
+ const REPOSITORY = /^[\w.-]+\/[\w.-]+$/;
29
+ const OBJECT_ID = /^(?:[a-f0-9]{40}|[a-f0-9]{64})$/;
30
+ async function boundedBody(response, maxBytes) {
31
+ const declared = Number(response.headers.get('content-length'));
32
+ if (Number.isFinite(declared) && declared > maxBytes) {
33
+ await response.body?.cancel().catch(() => undefined);
34
+ throw new ForgeReadError('oversized');
35
+ }
36
+ const reader = response.body?.getReader();
37
+ if (!reader) {
38
+ const bytes = new Uint8Array(await response.arrayBuffer());
39
+ if (bytes.length > maxBytes)
40
+ throw new ForgeReadError('oversized');
41
+ return bytes;
42
+ }
43
+ const chunks = [];
44
+ let size = 0;
45
+ try {
46
+ while (true) {
47
+ const { value, done } = await reader.read();
48
+ if (done)
49
+ break;
50
+ size += value.length;
51
+ if (size > maxBytes) {
52
+ await reader.cancel().catch(() => undefined);
53
+ throw new ForgeReadError('oversized');
54
+ }
55
+ chunks.push(value);
56
+ }
57
+ }
58
+ finally {
59
+ reader.releaseLock();
60
+ }
61
+ const result = new Uint8Array(size);
62
+ let offset = 0;
63
+ for (const chunk of chunks) {
64
+ result.set(chunk, offset);
65
+ offset += chunk.length;
66
+ }
67
+ return result;
68
+ }
69
+ const aborted = (signal) => signal?.aborted === true;
70
+ /** HTTP transport over `fetch`, with the provider's auth header and a response bound. */
71
+ export function httpForgeTransport(options) {
72
+ if (!/^https?:\/\//.test(options.apiBaseUrl ?? ''))
73
+ throw new TypeError('A trusted API base URL is required');
74
+ const base = options.apiBaseUrl.replace(/\/+$/, '');
75
+ const fetchImpl = options.fetchImpl ?? fetch;
76
+ const maxBytes = options.maxResponseBytes ?? FORGE_MAX_RESPONSE_BYTES;
77
+ const authorization = options.provider === 'github' ? `Bearer ${options.token}` : `token ${options.token}`;
78
+ return async (path, signal) => {
79
+ let response;
80
+ try {
81
+ response = await fetchImpl(`${base}${path}`, {
82
+ method: 'GET',
83
+ headers: { accept: 'application/json', authorization },
84
+ redirect: 'error',
85
+ ...(signal ? { signal } : {}),
86
+ });
87
+ }
88
+ catch {
89
+ throw new ForgeReadError(aborted(signal) ? 'timeout' : 'network-error');
90
+ }
91
+ const headers = {};
92
+ response.headers.forEach((value, name) => { headers[name.toLowerCase()] = value; });
93
+ let body;
94
+ try {
95
+ body = await boundedBody(response, maxBytes);
96
+ }
97
+ catch (error) {
98
+ if (error instanceof ForgeReadError)
99
+ throw error;
100
+ throw new ForgeReadError(aborted(signal) ? 'timeout' : 'network-error');
101
+ }
102
+ return { status: response.status, headers, body };
103
+ };
104
+ }
105
+ function spawnCliRunner(binary, maxBytes) {
106
+ return (args, signal) => new Promise((resolve, reject) => {
107
+ const child = spawn(binary, args, {
108
+ stdio: ['ignore', 'pipe', 'ignore'], windowsHide: true, ...(signal ? { signal } : {}),
109
+ env: { ...process.env, GH_PROMPT_DISABLED: '1', NO_COLOR: '1', GH_NO_UPDATE_NOTIFIER: '1' },
110
+ });
111
+ const chunks = [];
112
+ let size = 0;
113
+ let oversized = false;
114
+ child.stdout.on('data', (chunk) => {
115
+ size += chunk.length;
116
+ if (size > maxBytes + 65_536) {
117
+ oversized = true;
118
+ child.kill();
119
+ }
120
+ else
121
+ chunks.push(chunk);
122
+ });
123
+ child.once('error', () => reject(new ForgeReadError(aborted(signal) ? 'timeout' : 'network-error')));
124
+ child.once('close', code => {
125
+ if (oversized)
126
+ reject(new ForgeReadError('oversized'));
127
+ else
128
+ resolve({ stdout: new Uint8Array(Buffer.concat(chunks)), exitCode: code ?? 1 });
129
+ });
130
+ });
131
+ }
132
+ /** Split `gh api --include` output into status, headers and body. */
133
+ export function parseIncludedResponse(output) {
134
+ const text = Buffer.from(output);
135
+ let split = text.indexOf('\r\n\r\n');
136
+ let gap = 4;
137
+ if (split < 0) {
138
+ split = text.indexOf('\n\n');
139
+ gap = 2;
140
+ }
141
+ if (split < 0)
142
+ throw new ForgeReadError('malformed');
143
+ const head = text.subarray(0, split).toString('latin1').split(/\r?\n/);
144
+ const status = /^HTTP\/[0-9.]+ ([0-9]{3})\b/.exec(head[0] ?? '');
145
+ if (!status)
146
+ throw new ForgeReadError('malformed');
147
+ const headers = {};
148
+ for (const line of head.slice(1)) {
149
+ const colon = line.indexOf(':');
150
+ if (colon > 0)
151
+ headers[line.slice(0, colon).trim().toLowerCase()] = line.slice(colon + 1).trim();
152
+ }
153
+ return { status: Number(status[1]), headers, body: new Uint8Array(text.subarray(split + gap)) };
154
+ }
155
+ /**
156
+ * CLI transport: `gh api --include --hostname <host> <path>`. The CLI holds
157
+ * its own credentials. Gitea has no CLI with a raw read API (`tea` has no
158
+ * `api` command), so only GitHub is supported.
159
+ */
160
+ export function cliForgeTransport(options) {
161
+ if (options.provider !== 'github')
162
+ throw new TypeError('The forge CLI transport supports GitHub only');
163
+ if (!/^[A-Za-z0-9.-]+(?::[0-9]+)?$/.test(options.hostname ?? ''))
164
+ throw new TypeError('A forge hostname is required');
165
+ const maxBytes = options.maxResponseBytes ?? FORGE_MAX_RESPONSE_BYTES;
166
+ const runner = options.runner ?? spawnCliRunner(options.binary ?? 'gh', maxBytes);
167
+ return async (path, signal) => {
168
+ if (!path.startsWith('/'))
169
+ throw new ForgeReadError('malformed');
170
+ const { stdout, exitCode } = await runner(['api', '--include', '--method', 'GET', '--hostname', options.hostname, path.slice(1)], signal)
171
+ .catch((error) => {
172
+ if (error instanceof ForgeReadError)
173
+ throw error;
174
+ throw new ForgeReadError(aborted(signal) ? 'timeout' : 'network-error');
175
+ });
176
+ // `gh api --include` prints the status line even for a non-2xx exit. With
177
+ // nothing on stdout the CLI never reached the API: exit 4 is gh's
178
+ // "authentication required".
179
+ if (stdout.length === 0 && exitCode !== 0)
180
+ throw new ForgeReadError(exitCode === 4 ? 'auth-denied' : 'network-error');
181
+ const parsed = parseIncludedResponse(stdout);
182
+ if (parsed.body.length > maxBytes)
183
+ throw new ForgeReadError('oversized');
184
+ return parsed;
185
+ };
186
+ }
187
+ /** Classify a non-2xx response. A GitHub 403 with an exhausted quota is a rate limit, not an auth failure. */
188
+ export function classifyStatus(response) {
189
+ const { status, headers } = response;
190
+ if (status >= 200 && status < 300)
191
+ return null;
192
+ if (status === 429)
193
+ return 'rate-limited';
194
+ if (status === 403 && (headers['x-ratelimit-remaining'] === '0' || headers['retry-after'] !== undefined))
195
+ return 'rate-limited';
196
+ if (status === 401 || status === 403)
197
+ return 'auth-denied';
198
+ if (status === 404 || status === 410)
199
+ return 'not-found';
200
+ if (status >= 500)
201
+ return 'server-error';
202
+ return 'unexpected-status';
203
+ }
204
+ function parseJson(response) {
205
+ const failure = classifyStatus(response);
206
+ if (failure)
207
+ throw new ForgeReadError(failure, response.status);
208
+ try {
209
+ return JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(response.body));
210
+ }
211
+ catch {
212
+ throw new ForgeReadError('malformed');
213
+ }
214
+ }
215
+ /**
216
+ * Pagination from the provider headers (`x-hasmore`, `link rel="next"`,
217
+ * `x-total-count` / `x-total`). A full page with no pagination signal is
218
+ * ambiguous and never complete.
219
+ */
220
+ export function forgePagination(headers, items, page, pageSize) {
221
+ const totalHeader = headers['x-total-count'] ?? headers['x-total'];
222
+ const total = totalHeader !== undefined && /^[0-9]+$/.test(totalHeader) ? Number(totalHeader) : undefined;
223
+ const hasMore = headers['x-hasmore'];
224
+ if (items.length > pageSize) {
225
+ // The server ignored the page size and returned the whole collection at once.
226
+ const whole = page === 1 && hasMore !== 'true' && (total === undefined || total === items.length);
227
+ return { items, nextPage: null, complete: whole, ...(total !== undefined ? { totalCount: total } : {}) };
228
+ }
229
+ const linkedNext = /<[^>]+[?&]page=(\d+)[^>]*>;\s*rel="next"/.exec(headers.link ?? '');
230
+ const nextPage = hasMore === 'true' ? page + 1
231
+ : hasMore === 'false' ? null
232
+ : linkedNext ? Number(linkedNext[1])
233
+ : total !== undefined && items.length === pageSize && page * pageSize < total ? page + 1 : null;
234
+ const ambiguous = hasMore === undefined && !linkedNext && total === undefined && items.length === pageSize;
235
+ const totalsAgree = total === undefined || total === (page - 1) * pageSize + items.length;
236
+ return { items, nextPage, complete: !ambiguous && nextPage === null && totalsAgree, ...(total !== undefined ? { totalCount: total } : {}) };
237
+ }
238
+ const str = (value) => (typeof value === 'string' ? value : null);
239
+ const login = (user) => {
240
+ if (!user || typeof user !== 'object')
241
+ return null;
242
+ const value = user;
243
+ return str(value.login) ?? str(value.username);
244
+ };
245
+ function repoPath(repository, number) {
246
+ if (!REPOSITORY.test(repository) || repository.split('/').some(part => part === '.' || part === '..'))
247
+ throw new ForgeReadError('malformed');
248
+ if (number !== undefined && (!Number.isSafeInteger(number) || number < 1))
249
+ throw new ForgeReadError('malformed');
250
+ return `/repos/${repository.split('/').map(encodeURIComponent).join('/')}`;
251
+ }
252
+ /** Build the read-only reader over one transport. */
253
+ export function createForgeReader(provider, transport, options = {}) {
254
+ const pageSize = options.pageSize ?? (provider === 'github' ? 100 : 50);
255
+ const get = async (path, signal) => {
256
+ if (aborted(signal))
257
+ throw new ForgeReadError('timeout');
258
+ return transport(path, signal);
259
+ };
260
+ const object = (value) => {
261
+ if (!value || typeof value !== 'object' || Array.isArray(value))
262
+ throw new ForgeReadError('malformed');
263
+ return value;
264
+ };
265
+ return {
266
+ provider,
267
+ pageSize,
268
+ async getIssue(repository, number, signal) {
269
+ const issue = object(parseJson(await get(`${repoPath(repository, number)}/issues/${number}`, signal)));
270
+ const state = issue.state;
271
+ if (state !== 'open' && state !== 'closed')
272
+ throw new ForgeReadError('malformed');
273
+ const found = issue.number ?? issue.index;
274
+ if (found !== undefined && found !== number)
275
+ throw new ForgeReadError('malformed');
276
+ return { number, state, closedAt: str(issue.closed_at), isPullRequest: Boolean(issue.pull_request) };
277
+ },
278
+ async listIssueComments(repository, number, page, signal) {
279
+ if (!Number.isSafeInteger(page) || page < 1)
280
+ throw new ForgeReadError('malformed');
281
+ const sizeParam = provider === 'github' ? 'per_page' : 'limit';
282
+ const response = await get(`${repoPath(repository, number)}/issues/${number}/comments?page=${page}&${sizeParam}=${pageSize}`, signal);
283
+ const raw = parseJson(response);
284
+ if (!Array.isArray(raw))
285
+ throw new ForgeReadError('malformed');
286
+ const items = raw.map((entry) => {
287
+ const item = object(entry);
288
+ if (typeof item.id !== 'number')
289
+ throw new ForgeReadError('malformed');
290
+ return { id: item.id, author: login(item.user) ?? login(item.poster), body: str(item.body) ?? '', createdAt: str(item.created_at) };
291
+ });
292
+ return forgePagination(response.headers, items, page, pageSize);
293
+ },
294
+ async getPullState(repository, number, signal) {
295
+ const pr = object(parseJson(await get(`${repoPath(repository, number)}/pulls/${number}`, signal)));
296
+ const state = pr.state;
297
+ if (state !== 'open' && state !== 'closed')
298
+ throw new ForgeReadError('malformed');
299
+ if (typeof pr.merged !== 'boolean')
300
+ throw new ForgeReadError('malformed');
301
+ const base = pr.base && typeof pr.base === 'object' ? pr.base : {};
302
+ const head = pr.head && typeof pr.head === 'object' ? pr.head : {};
303
+ const mergeSha = str(pr.merge_commit_sha);
304
+ return {
305
+ number,
306
+ state,
307
+ merged: pr.merged,
308
+ mergedAt: str(pr.merged_at),
309
+ mergeCommitSha: pr.merged && mergeSha && OBJECT_ID.test(mergeSha) ? mergeSha : null,
310
+ baseRef: str(base.ref),
311
+ baseSha: str(base.sha),
312
+ headSha: str(head.sha),
313
+ };
314
+ },
315
+ async getCommitMessage(repository, sha, signal) {
316
+ if (!OBJECT_ID.test(sha))
317
+ throw new ForgeReadError('malformed');
318
+ const path = provider === 'github' ? `${repoPath(repository)}/commits/${sha}` : `${repoPath(repository)}/git/commits/${sha}`;
319
+ const commit = object(parseJson(await get(path, signal)));
320
+ const inner = commit.commit && typeof commit.commit === 'object' ? commit.commit : commit;
321
+ return str(inner.message);
322
+ },
323
+ };
324
+ }
325
+ /**
326
+ * Derive the REST API base from a tracker remote URL (`git@host:o/r.git`,
327
+ * `ssh://git@host:2222/o/r.git`, `https://host/prefix/o/r.git`). Returns the
328
+ * host, the `owner/repo` path and the API base, or null when the URL cannot
329
+ * be parsed. No host is assumed: the base always comes from the remote.
330
+ */
331
+ export function forgeEndpointFromRemote(provider, remoteUrl) {
332
+ if (!remoteUrl)
333
+ return null;
334
+ let origin;
335
+ let host;
336
+ let path;
337
+ const scp = /^[\w.-]+@([A-Za-z0-9.-]+):(?!\/)(.+)$/.exec(remoteUrl);
338
+ if (scp) {
339
+ host = scp[1];
340
+ origin = `https://${host}`;
341
+ path = scp[2];
342
+ }
343
+ else {
344
+ let url;
345
+ try {
346
+ url = new URL(remoteUrl);
347
+ }
348
+ catch {
349
+ return null;
350
+ }
351
+ if (!['https:', 'http:', 'ssh:', 'git:'].includes(url.protocol) || !url.hostname)
352
+ return null;
353
+ host = url.hostname;
354
+ const web = url.protocol === 'https:' || url.protocol === 'http:';
355
+ origin = web ? `${url.protocol}//${url.host}` : `https://${host}`;
356
+ path = url.pathname.replace(/^\/+/, '');
357
+ }
358
+ const parts = path.replace(/\.git$/, '').replace(/\/+$/, '').split('/').filter(Boolean);
359
+ if (parts.length < 2)
360
+ return null;
361
+ const repository = parts.slice(-2).join('/');
362
+ if (!REPOSITORY.test(repository))
363
+ return null;
364
+ const prefix = parts.slice(0, -2).join('/');
365
+ const webBase = prefix ? `${origin}/${prefix}` : origin;
366
+ const apiBaseUrl = provider === 'github'
367
+ ? (host.toLowerCase() === 'github.com' ? 'https://api.github.com' : `${webBase}/api/v3`)
368
+ : `${webBase}/api/v1`;
369
+ return { host: host.toLowerCase(), repository, apiBaseUrl };
370
+ }
371
+ //# sourceMappingURL=forge-http.js.map
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Project inputs for tracker authority: the `.aiwg/aiwg.config` and the git
3
+ * remote URLs. Shared by context finalization and the `aiwg effect` tracker
4
+ * verifiers, so both resolve the tracker from the same facts.
5
+ */
6
+ import { execFile } from 'node:child_process';
7
+ import { promisify } from 'node:util';
8
+ import { readAiwgConfig } from '../config/aiwg-config.js';
9
+ const execFileAsync = promisify(execFile);
10
+ /** The project config, or `null` when it is missing or unreadable. */
11
+ export async function readConfig(projectPath) {
12
+ try {
13
+ return await readAiwgConfig(projectPath);
14
+ }
15
+ catch {
16
+ return null;
17
+ }
18
+ }
19
+ /** Git remote name to fetch URL; `{}` outside a repository or when git fails. */
20
+ export async function readGitRemoteUrls(projectPath) {
21
+ try {
22
+ const { stdout } = await execFileAsync('git', ['-C', projectPath, 'remote', '-v'], {
23
+ maxBuffer: 1024 * 1024,
24
+ timeout: 10_000,
25
+ });
26
+ const urls = {};
27
+ for (const line of stdout.split(/\r?\n/)) {
28
+ const match = line.match(/^(\S+)\s+(\S+)\s+\((fetch|push)\)$/);
29
+ if (!match)
30
+ continue;
31
+ const [, name, url, direction] = match;
32
+ if (direction === 'fetch' || !urls[name])
33
+ urls[name] = url;
34
+ }
35
+ return urls;
36
+ }
37
+ catch {
38
+ return {};
39
+ }
40
+ }
41
+ //# sourceMappingURL=project-inputs.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiwg/cli",
3
- "version": "2026.9.20",
3
+ "version": "2026.9.21",
4
4
  "description": "Lightweight AIWG CLI for signed, versioned web-backed resources.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -28,6 +28,7 @@
28
28
  "bin/",
29
29
  "agentic/code/providers/",
30
30
  "schemas/security/",
31
+ "schemas/decision/",
31
32
  "schemas/dataset/",
32
33
  "dist/src/",
33
34
  "!dist/**/*.d.ts",
@@ -0,0 +1,50 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://aiwg.io/schemas/decision/BatchReceipt.v1.schema.json",
4
+ "title": "Decision Batch Receipt v1",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": ["schemaVersion", "revision", "tenantId", "projectId", "batchId", "invocationId", "runId", "plan", "subjectHash", "stateHash", "executionEnvelope", "questionIds", "attempts", "answerReferences", "allocations", "status", "createdAtEpochMs", "updatedAtEpochMs", "terminalAtEpochMs"],
8
+ "properties": {
9
+ "schemaVersion": { "const": "decision-batch-receipt/v1" },
10
+ "revision": { "type": "integer", "minimum": 1 },
11
+ "tenantId": { "$ref": "#/$defs/text" }, "projectId": { "$ref": "#/$defs/text" },
12
+ "batchId": { "$ref": "#/$defs/text" }, "invocationId": { "$ref": "#/$defs/text" }, "runId": { "$ref": "#/$defs/text" },
13
+ "plan": { "type": "object", "additionalProperties": false, "required": ["planDigest", "partitionId", "nativeBatchGroupId"], "properties": {
14
+ "planDigest": { "$ref": "#/$defs/hash" }, "partitionId": { "$ref": "#/$defs/text" },
15
+ "nativeBatchGroupId": { "type": ["string", "null"], "minLength": 1 }
16
+ } },
17
+ "subjectHash": { "$ref": "#/$defs/hash" }, "stateHash": { "$ref": "#/$defs/hash" },
18
+ "executionEnvelope": { "$ref": "#/$defs/text" },
19
+ "questionIds": { "type": "array", "minItems": 1, "uniqueItems": true, "items": { "$ref": "#/$defs/text" } },
20
+ "attempts": { "type": "array", "items": { "$ref": "#/$defs/attempt" } },
21
+ "answerReferences": { "type": "array", "items": { "$ref": "#/$defs/answer" } },
22
+ "allocations": { "type": "array", "items": { "$ref": "#/$defs/allocation" } },
23
+ "status": { "enum": ["acquired", "running", "completed", "failed", "execution-uncertain"] },
24
+ "createdAtEpochMs": { "$ref": "#/$defs/time" }, "updatedAtEpochMs": { "$ref": "#/$defs/time" },
25
+ "terminalAtEpochMs": { "oneOf": [{ "$ref": "#/$defs/time" }, { "type": "null" }] },
26
+ "traceParent": { "type": "string", "pattern": "^00-[0-9a-f]{32}-[0-9a-f]{16}-0[01]$" }
27
+ },
28
+ "$defs": {
29
+ "text": { "type": "string", "minLength": 1 }, "time": { "type": "integer", "minimum": 0 },
30
+ "count": { "type": "integer", "minimum": 0 }, "nullableCount": { "oneOf": [{ "$ref": "#/$defs/count" }, { "type": "null" }] },
31
+ "hash": { "type": "string", "pattern": "^sha256:[0-9a-f]{64}$" },
32
+ "usage": { "type": "object", "additionalProperties": false, "required": ["inputTokens", "outputTokens"], "properties": { "inputTokens": { "$ref": "#/$defs/nullableCount" }, "outputTokens": { "$ref": "#/$defs/nullableCount" } } },
33
+ "cost": { "oneOf": [
34
+ { "type": "object", "additionalProperties": false, "required": ["kind", "currency", "amountMicros"], "properties": { "kind": { "const": "provider-authoritative" }, "currency": { "const": "USD" }, "amountMicros": { "$ref": "#/$defs/count" } } },
35
+ { "type": "object", "additionalProperties": false, "required": ["kind", "currency", "amountMicros", "priceCatalogId", "priceCatalogVersion", "effectiveAt"], "properties": { "kind": { "const": "client-derived" }, "currency": { "const": "USD" }, "amountMicros": { "$ref": "#/$defs/count" }, "priceCatalogId": { "$ref": "#/$defs/text" }, "priceCatalogVersion": { "$ref": "#/$defs/text" }, "effectiveAt": { "$ref": "#/$defs/text" } } },
36
+ { "type": "object", "additionalProperties": false, "required": ["kind", "currency", "upperBoundMicros", "boundPolicyId", "boundPolicyVersion"], "properties": { "kind": { "const": "bounded-unknown" }, "currency": { "const": "USD" }, "upperBoundMicros": { "$ref": "#/$defs/count" }, "boundPolicyId": { "$ref": "#/$defs/text" }, "boundPolicyVersion": { "$ref": "#/$defs/text" } } },
37
+ { "type": "object", "additionalProperties": false, "required": ["kind"], "properties": { "kind": { "const": "unknown" } } }
38
+ ] },
39
+ "attempt": { "type": "object", "additionalProperties": false, "required": ["ordinal", "adapterId", "adapterVersion", "requestedModel", "actualModel", "providerRequestId", "status", "dispatchedAtEpochMs", "completedAtEpochMs", "usage", "cost", "fallbackFromAttemptOrdinal"], "properties": {
40
+ "ordinal": { "type": "integer", "minimum": 1 }, "adapterId": { "$ref": "#/$defs/text" }, "adapterVersion": { "$ref": "#/$defs/text" }, "requestedModel": { "$ref": "#/$defs/text" },
41
+ "actualModel": { "type": ["string", "null"] }, "providerRequestId": { "type": ["string", "null"], "minLength": 1, "maxLength": 256, "pattern": "^[!-~]+$" },
42
+ "status": { "enum": ["not-sent", "failed", "succeeded", "execution-uncertain"] },
43
+ "dispatchedAtEpochMs": { "oneOf": [{ "$ref": "#/$defs/time" }, { "type": "null" }] }, "completedAtEpochMs": { "oneOf": [{ "$ref": "#/$defs/time" }, { "type": "null" }] },
44
+ "usage": { "$ref": "#/$defs/usage" }, "cost": { "$ref": "#/$defs/cost" },
45
+ "fallbackFromAttemptOrdinal": { "type": ["integer", "null"], "minimum": 1 }
46
+ } },
47
+ "answer": { "type": "object", "additionalProperties": false, "required": ["questionId", "answerId", "resultId"], "properties": { "questionId": { "$ref": "#/$defs/text" }, "answerId": { "$ref": "#/$defs/text" }, "resultId": { "$ref": "#/$defs/text" } } },
48
+ "allocation": { "type": "object", "additionalProperties": false, "required": ["questionId", "kind", "algorithm", "algorithmVersion", "inputTokens", "outputTokens"], "properties": { "questionId": { "$ref": "#/$defs/text" }, "kind": { "const": "estimated" }, "algorithm": { "const": "largest-remainder-weighted" }, "algorithmVersion": { "const": "1" }, "inputTokens": { "$ref": "#/$defs/nullableCount" }, "outputTokens": { "$ref": "#/$defs/nullableCount" } } }
49
+ }
50
+ }