@inneranimalmedia/agentsam-sdk 2.6.10 → 2.6.11

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 (261) hide show
  1. package/README.md +3 -0
  2. package/TRADEMARKS.md +22 -0
  3. package/apps/README.md +3 -0
  4. package/apps/cad-creator/LICENSE +21 -0
  5. package/apps/cad-creator/README.md +4 -1
  6. package/apps/cad-creator/shared/cad/package.json +1 -1
  7. package/apps/client-cms-editor/LICENSE +21 -0
  8. package/apps/client-cms-editor/README.md +3 -0
  9. package/apps/client-cms-editor/backend/package.json +2 -2
  10. package/apps/client-cms-editor/frontend/package.json +3 -3
  11. package/apps/client-cms-editor/package-lock.json +43 -43
  12. package/apps/client-cms-editor/package.json +3 -3
  13. package/apps/client-cms-editor/reference/harvest/HARVEST_RECEIPT.json +1 -1
  14. package/apps/client-cms-editor/reference/harvest/README.md +1 -1
  15. package/apps/client-cms-editor/reference/harvest/plans/ARCHIVE-INTAKE.md +1 -1
  16. package/apps/client-cms-editor/reference/harvest/plans/HARVEST-INDEX.md +1 -1
  17. package/apps/client-cms-editor/reference/harvest/plans/TOOLING-LAB.md +1 -1
  18. package/apps/client-cms-editor/shared/cms/package.json +1 -1
  19. package/docs/MINI.md +4 -2
  20. package/docs/REPO_FILE_MAP.txt +0 -1
  21. package/docs/SOURCE_ARCHITECTURE.md +1 -1
  22. package/docs/architecture/DUAL_ANALYTICS_PRODUCT.md +140 -0
  23. package/docs/content-studio/REVISION_GATE_2026-09-27.md +1 -1
  24. package/docs/engineering/go-portability/01-product-project-deployment-authority.md +69 -0
  25. package/docs/engineering/provider-internal-renames/01-lane-initialization.md +20 -0
  26. package/docs/engineering/provider-internal-renames/01a-runtime-environment-contract.md +115 -0
  27. package/docs/engineering/provider-internal-renames/02-cfoa.md +57 -0
  28. package/docs/engineering/provider-internal-renames/03-gclioa.md +32 -0
  29. package/docs/engineering/provider-internal-renames/04-goaude.md +50 -0
  30. package/docs/engineering/provider-internal-renames/05-gdrv.md +33 -0
  31. package/docs/engineering/provider-internal-renames/06-verification.md +103 -0
  32. package/docs/engineering/provider-internal-renames/README.md +77 -0
  33. package/docs/engineering/provider-internal-renames/runtime-environment-contract.json +220 -0
  34. package/docs/engineering/test-diagnostics/go-cli-test-hang-20261003.md +49 -0
  35. package/docs/engineering/test-diagnostics/go-skip-deploy-docker-fix.md +90 -0
  36. package/docs/knowledge-service-verification.md +3 -3
  37. package/docs/plans/AGENTSAM-SUSPEND-QUEUE-KNOWLEDGE-RDP-2026-10-03.md +870 -0
  38. package/docs/plans/LOCAL-STUDIO-GRADUATION-2026-09-26.md +9 -9
  39. package/docs/plans/MACHINE-FIRST-APPS-TUI-2026-09-24.md +11 -11
  40. package/docs/plans/RECONCILIATION-2026-09-19.md +6 -6
  41. package/docs/plans/SAM_WORK_PROTOCOL.md +667 -0
  42. package/docs/plans/TOMORROW-EXTENSIVE-TODO-2026-09-26.md +2 -2
  43. package/docs/plans/UNIVERSAL-INGEST-MACHINE-FIRST-2026-09-24.md +3 -3
  44. package/examples/hyperspace-demo/src/main.tsx +227 -110
  45. package/migrations/d1/0020_agentsam_receipt_catalog.sql +35 -0
  46. package/migrations/runtime/0006_agent_run_suspend_activity.sql +53 -0
  47. package/migrations/runtime/0007_queue_control.sql +40 -0
  48. package/migrations/runtime/0008_agent_run_dependencies.sql +24 -0
  49. package/migrations/runtime/0008_analytics_legacy_bootstrap.sql +43 -0
  50. package/migrations/runtime/0009_analytics_authority.sql +108 -0
  51. package/package.json +26 -11
  52. package/packages/agentsam-brand/README.md +3 -0
  53. package/packages/agentsam-brand/package.json +2 -2
  54. package/packages/agentsam-contracts/dist/adapter.d.ts +22 -0
  55. package/packages/agentsam-contracts/dist/adapter.js +0 -0
  56. package/packages/agentsam-contracts/dist/agent.d.ts +66 -0
  57. package/packages/agentsam-contracts/dist/agent.js +15 -0
  58. package/packages/agentsam-contracts/dist/artifacts.d.ts +70 -0
  59. package/packages/agentsam-contracts/dist/artifacts.js +0 -0
  60. package/packages/agentsam-contracts/dist/authority.d.ts +21 -0
  61. package/packages/agentsam-contracts/dist/authority.js +0 -0
  62. package/packages/agentsam-contracts/dist/context.d.ts +14 -0
  63. package/packages/agentsam-contracts/dist/context.js +0 -0
  64. package/packages/agentsam-contracts/dist/errors.d.ts +121 -0
  65. package/packages/agentsam-contracts/dist/errors.js +5 -0
  66. package/packages/agentsam-contracts/dist/events.d.ts +53 -0
  67. package/packages/agentsam-contracts/dist/events.js +0 -0
  68. package/packages/agentsam-contracts/dist/execution.d.ts +38 -0
  69. package/packages/agentsam-contracts/dist/execution.js +0 -0
  70. package/packages/agentsam-contracts/dist/hooks.d.ts +31 -0
  71. package/packages/agentsam-contracts/dist/hooks.js +0 -0
  72. package/packages/agentsam-contracts/dist/identity.d.ts +15 -0
  73. package/packages/agentsam-contracts/dist/identity.js +0 -0
  74. package/packages/agentsam-contracts/dist/index.d.ts +16 -0
  75. package/packages/agentsam-contracts/dist/index.js +164 -0
  76. package/packages/agentsam-contracts/dist/models.d.ts +9 -0
  77. package/packages/agentsam-contracts/dist/models.js +0 -0
  78. package/packages/agentsam-contracts/dist/providers.d.ts +27 -0
  79. package/packages/agentsam-contracts/dist/providers.js +0 -0
  80. package/packages/agentsam-contracts/dist/repository.d.ts +56 -0
  81. package/packages/agentsam-contracts/dist/repository.js +15 -0
  82. package/packages/agentsam-contracts/dist/run.d.ts +113 -0
  83. package/packages/agentsam-contracts/dist/run.js +132 -0
  84. package/packages/agentsam-contracts/dist/tools.d.ts +82 -0
  85. package/packages/agentsam-contracts/dist/tools.js +0 -0
  86. package/packages/agentsam-contracts/dist/widgets.d.ts +38 -0
  87. package/packages/agentsam-contracts/dist/widgets.js +0 -0
  88. package/packages/agentsam-contracts/package.json +9 -1
  89. package/packages/agentsam-contracts/scripts/build.mjs +1 -1
  90. package/packages/agentsam-contracts/src/agent.ts +78 -3
  91. package/packages/agentsam-contracts/src/index.ts +2 -0
  92. package/packages/agentsam-contracts/src/run.ts +320 -0
  93. package/packages/agentsam-contracts/src/widgets.ts +58 -0
  94. package/packages/agentsam-contracts/test/contracts.test.mjs +1 -1
  95. package/packages/agentsam-contracts/test/run-contract.test.mjs +94 -0
  96. package/packages/agentsam-errors/package.json +1 -1
  97. package/packages/agentsam-goap/README.md +3 -0
  98. package/packages/agentsam-goap/package.json +2 -2
  99. package/packages/agentsam-hooks/package.json +2 -2
  100. package/packages/agentsam-hooks/src/adapters/agents.js +48 -22
  101. package/packages/agentsam-hooks/src/index.d.ts +16 -0
  102. package/packages/agentsam-knowledge/package.json +1 -1
  103. package/packages/agentsam-knowledge/src/backends/index.js +1 -1
  104. package/packages/agentsam-merch/README.md +62 -0
  105. package/packages/agentsam-merch/bin/agentsam-merch.mjs +16 -0
  106. package/packages/agentsam-merch/package.json +65 -0
  107. package/packages/agentsam-merch/profiles/completeful/dtg-back-large.json +42 -0
  108. package/packages/agentsam-merch/profiles/completeful/laser-engraving.json +39 -0
  109. package/packages/agentsam-merch/profiles/generic/embroidery-digitization.json +22 -0
  110. package/packages/agentsam-merch/schemas/collection-candidate.schema.json +66 -0
  111. package/packages/agentsam-merch/schemas/manufacturing-profile.schema.json +221 -0
  112. package/packages/agentsam-merch/src/adapters-completeful.js +50 -0
  113. package/packages/agentsam-merch/src/cli.js +93 -0
  114. package/packages/agentsam-merch/src/collection-lab.js +60 -0
  115. package/packages/agentsam-merch/src/compatibility.js +501 -0
  116. package/packages/agentsam-merch/src/derivatives.js +112 -0
  117. package/packages/agentsam-merch/src/index.d.ts +316 -0
  118. package/packages/agentsam-merch/src/index.js +46 -0
  119. package/packages/agentsam-merch/src/node/index.d.ts +58 -0
  120. package/packages/agentsam-merch/src/node/index.js +6 -0
  121. package/packages/agentsam-merch/src/node/raster.js +142 -0
  122. package/packages/agentsam-merch/src/node/vector.js +239 -0
  123. package/packages/agentsam-merch/src/product-concepts.js +118 -0
  124. package/packages/agentsam-merch/src/profile-registry.js +104 -0
  125. package/packages/agentsam-merch/src/profile.js +158 -0
  126. package/packages/agentsam-merch/src/transforms.js +215 -0
  127. package/packages/agentsam-merch/test/merch.test.mjs +332 -0
  128. package/packages/agentsam-merch/test/node-transforms.test.mjs +149 -0
  129. package/packages/agentsam-nav/dist/browser.global.js +71 -34
  130. package/packages/agentsam-nav/dist/browser.js +3179 -781
  131. package/packages/agentsam-nav/dist/index.js +3171 -773
  132. package/packages/agentsam-nav/package.json +1 -1
  133. package/packages/agentsam-queue-control/README.md +4 -1
  134. package/packages/agentsam-queue-control/package.json +4 -3
  135. package/packages/agentsam-queue-control/schemas/job-envelope.schema.json +111 -11
  136. package/packages/agentsam-queue-control/src/adapters/cloudflare.js +34 -6
  137. package/packages/agentsam-queue-control/src/adapters/memory.js +11 -2
  138. package/packages/agentsam-queue-control/src/adapters/sqlite.js +259 -0
  139. package/packages/agentsam-queue-control/src/contracts.js +207 -3
  140. package/packages/agentsam-queue-control/src/control-plane.js +62 -2
  141. package/packages/agentsam-queue-control/src/index.js +1 -0
  142. package/packages/agentsam-queue-control/test/queue-control.test.mjs +310 -0
  143. package/packages/agentsam-queue-control/test/sqlite-adapter.test.mjs +91 -0
  144. package/packages/agentsam-rapid-rust/README.md +3 -0
  145. package/packages/agentsam-repository/README.md +3 -0
  146. package/packages/agentsam-repository/package.json +1 -1
  147. package/packages/agentsam-vault/package.json +1 -1
  148. package/packages/agentsam-workbench/README.md +43 -0
  149. package/packages/agentsam-workbench/package.json +13 -3
  150. package/packages/agentsam-workbench/src/agent/AgentConversationSurface.tsx +108 -0
  151. package/packages/agentsam-workbench/src/agent/AgentPanel.tsx +25 -8
  152. package/packages/agentsam-workbench/src/agent/AgentRuntimeField.tsx +152 -0
  153. package/packages/agentsam-workbench/src/agent/Composer.tsx +10 -1
  154. package/packages/agentsam-workbench/src/agent/RunMode.ts +26 -0
  155. package/packages/agentsam-workbench/src/agent/Thread.tsx +18 -3
  156. package/packages/agentsam-workbench/src/agent/index.ts +3 -0
  157. package/packages/agentsam-workbench/src/index.ts +4 -0
  158. package/packages/agentsam-workbench/src/manufacturing/PreflightIndicator.tsx +203 -0
  159. package/packages/agentsam-workbench/src/manufacturing/index.ts +2 -0
  160. package/packages/agentsam-workbench/src/manufacturing/loading.ts +147 -0
  161. package/packages/agentsam-workbench/src/widgets/BuiltinWidget.tsx +22 -0
  162. package/packages/agentsam-workbench/src/widgets/CountdownWidget.tsx +68 -0
  163. package/packages/agentsam-workbench/src/widgets/WidgetFrame.tsx +56 -0
  164. package/packages/agentsam-workbench/src/widgets/countdown.ts +25 -0
  165. package/packages/agentsam-workbench/src/widgets/index.ts +6 -0
  166. package/packages/agentsam-workbench/src/widgets/registry.ts +13 -0
  167. package/packages/agentsam-workbench/src/widgets/useCountdown.ts +128 -0
  168. package/packages/agentsam-workbench/src/widgets/widgets.css +102 -0
  169. package/packages/connectors/{cloudflare → cfoa}/package.json +3 -3
  170. package/packages/heuristic-theme/package.json +1 -1
  171. package/packages/identity/README.md +3 -0
  172. package/packages/identity/backend/README.md +3 -0
  173. package/packages/identity/package.json +1 -1
  174. package/packages/identity/src/frontend/auth-portal/README.md +3 -0
  175. package/packages/identity/src/index.js +1 -1
  176. package/packages/identity/src/oauth/README.md +4 -1
  177. package/packages/identity/src/oauth/credentials.js +1 -1
  178. package/packages/identity/src/server/worker-router.js +1 -1
  179. package/packages/identity/tests/google-desktop-identity-login.test.mjs +1 -1
  180. package/packages/identity/tests/lane1-packed-consumer.test.mjs +1 -1
  181. package/packages/identity/tests/oauth-presets-accounts-ssot.test.mjs +1 -1
  182. package/packages/providers/completeful/package.json +1 -1
  183. package/packages/theme-church-site/package.json +1 -1
  184. package/packages/theme-companions-site/package.json +1 -1
  185. package/packages/theme-floors-site/package.json +1 -1
  186. package/packages/theme-fuelnfree-site/package.json +1 -1
  187. package/packages/theme-handyman-site/package.json +1 -1
  188. package/packages/theme-insurance-site/package.json +1 -1
  189. package/packages/theme-scenes/package.json +1 -1
  190. package/packages/theme-shinshu-site/package.json +1 -1
  191. package/packages/work-graph/package.json +1 -1
  192. package/protocol/models/README.md +3 -0
  193. package/protocol/receipts/agentsam.receipt.v1.json +95 -0
  194. package/protocol/theme-refinery/receipts/products-registry-batch-20260924.json +6 -6
  195. package/protocol/theme-refinery/receipts/theme-harvest-20260924.json +6 -6
  196. package/skills/README.md +3 -0
  197. package/src/acp/agent-executor.js +106 -0
  198. package/src/acp/client.js +316 -0
  199. package/src/acp/dependencies.js +426 -0
  200. package/src/acp/http.js +207 -0
  201. package/src/acp/index.js +22 -0
  202. package/src/acp/runs.js +125 -0
  203. package/src/acp/runtime-selection.js +180 -0
  204. package/src/cli/command-catalog.js +3 -0
  205. package/src/cli.js +24 -0
  206. package/src/commands/cloudflare-login.js +1 -1
  207. package/src/commands/cloudflare.js +2 -2
  208. package/src/commands/connections.js +1 -1
  209. package/src/commands/go.js +76 -6
  210. package/src/commands/merch.js +5 -0
  211. package/src/commands/mini.js +1 -1
  212. package/src/commands/receipts.js +140 -0
  213. package/src/commands/run.js +253 -0
  214. package/src/go/container.js +1 -1
  215. package/src/go/contract.js +61 -18
  216. package/src/go/discover.js +0 -4
  217. package/src/go/index.js +13 -1
  218. package/src/go/probe.js +109 -0
  219. package/src/go/{cloudflare.js → providers/cloudflare.js} +36 -136
  220. package/src/go/verify.js +1 -1
  221. package/src/indexing/ingest/inventory.js +1 -1
  222. package/src/lib/deploy/local-studio.js +2 -2
  223. package/src/lib/mini/scaffold.js +1 -1
  224. package/src/lib/whoami-capabilities.js +1 -1
  225. package/src/local/runtime-store.js +8 -0
  226. package/src/mcp/cloudflare-bundles.js +1 -1
  227. package/src/plugins/index.js +1 -0
  228. package/src/plugins/registry.js +59 -6
  229. package/src/receipts/contract.js +68 -0
  230. package/src/receipts/index.js +20 -0
  231. package/src/receipts/prune.js +64 -0
  232. package/src/receipts/sinks.js +285 -0
  233. package/templates/mini/gadget/app.js +15 -40
  234. package/templates/mini/gadget/index.html +7 -7
  235. package/templates/mini/shared/style.css +1 -1
  236. package/packages/agentsam-repository/src/goap.js.bak-20260922011251 +0 -670
  237. /package/packages/connectors/{cloudflare → cfoa}/OAUTH_STORAGE_AUDIT.md +0 -0
  238. /package/packages/connectors/{cloudflare → cfoa}/src/api-client.js +0 -0
  239. /package/packages/connectors/{cloudflare → cfoa}/src/capabilities.js +0 -0
  240. /package/packages/connectors/{cloudflare → cfoa}/src/credential.js +0 -0
  241. /package/packages/connectors/{cloudflare → cfoa}/src/families/brand-protection.js +0 -0
  242. /package/packages/connectors/{cloudflare → cfoa}/src/families/keyless.js +0 -0
  243. /package/packages/connectors/{cloudflare → cfoa}/src/families/pages.js +0 -0
  244. /package/packages/connectors/{cloudflare → cfoa}/src/families/scanner.js +0 -0
  245. /package/packages/connectors/{cloudflare → cfoa}/src/families/snippets.js +0 -0
  246. /package/packages/connectors/{cloudflare → cfoa}/src/families/tag-gateway.js +0 -0
  247. /package/packages/connectors/{cloudflare → cfoa}/src/families/tags.js +0 -0
  248. /package/packages/connectors/{cloudflare → cfoa}/src/families/token-validation.js +0 -0
  249. /package/packages/connectors/{cloudflare → cfoa}/src/families/vectorize.js +0 -0
  250. /package/packages/connectors/{cloudflare → cfoa}/src/families/workflows.js +0 -0
  251. /package/packages/connectors/{cloudflare → cfoa}/src/index.js +0 -0
  252. /package/packages/connectors/{cloudflare → cfoa}/src/oauth-persist.js +0 -0
  253. /package/packages/connectors/{cloudflare → cfoa}/src/owner.js +0 -0
  254. /package/packages/connectors/{cloudflare → cfoa}/src/permission-options.js +0 -0
  255. /package/packages/connectors/{cloudflare → cfoa}/src/routes.js +0 -0
  256. /package/packages/connectors/{cloudflare → cfoa}/src/scope-catalog.js +0 -0
  257. /package/packages/connectors/{cloudflare → cfoa}/src/vault.js +0 -0
  258. /package/packages/connectors/{cloudflare → cfoa}/tests/capabilities.test.mjs +0 -0
  259. /package/packages/connectors/{cloudflare → cfoa}/tests/connector.test.mjs +0 -0
  260. /package/packages/connectors/{cloudflare → cfoa}/tests/oauth-persist.test.mjs +0 -0
  261. /package/packages/identity/src/oauth/{google-desktop-exchange.js → goaude.js} +0 -0
@@ -0,0 +1,140 @@
1
+ import { readdir, readFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import {
4
+ pruneLocalWorkspaces,
5
+ validateReceiptEnvelope,
6
+ } from "../receipts/index.js";
7
+
8
+ function intFlag(args, flag, fallback) {
9
+ const index = args.indexOf(flag);
10
+ const value = index >= 0 ? Number.parseInt(args[index + 1], 10) : NaN;
11
+ return Number.isFinite(value) && value > 0 ? value : fallback;
12
+ }
13
+
14
+ function numberFlag(args, flag, fallback) {
15
+ const index = args.indexOf(flag);
16
+ const value = index >= 0 ? Number(args[index + 1]) : NaN;
17
+ return Number.isFinite(value) && value >= 0 ? value : fallback;
18
+ }
19
+
20
+ function stringFlag(args, flag) {
21
+ const index = args.indexOf(flag);
22
+ return index >= 0 && args[index + 1] ? args[index + 1] : null;
23
+ }
24
+
25
+ async function readLocalReceipts(directory) {
26
+ const entries = await readdir(directory, { withFileTypes: true }).catch(() => []);
27
+ const receipts = [];
28
+ for (const entry of entries) {
29
+ if (!entry.isFile() || !entry.name.endsWith(".json")) continue;
30
+ try {
31
+ const value = JSON.parse(
32
+ await readFile(path.join(directory, entry.name), "utf8"),
33
+ );
34
+ if (validateReceiptEnvelope(value).ok) receipts.push(value);
35
+ } catch {}
36
+ }
37
+ receipts.sort(
38
+ (a, b) =>
39
+ Number(b.metrics?.created_at_unix || 0) -
40
+ Number(a.metrics?.created_at_unix || 0),
41
+ );
42
+ return receipts;
43
+ }
44
+
45
+ function writeSummary(receipt) {
46
+ const date = new Date(
47
+ Number(receipt.metrics?.created_at_unix || 0) * 1000,
48
+ ).toISOString();
49
+ process.stdout.write(
50
+ [
51
+ receipt.receipt_id,
52
+ receipt.status,
53
+ receipt.kind,
54
+ date,
55
+ receipt.identity?.repository_id || "-",
56
+ ].join("\t") + "\n",
57
+ );
58
+ }
59
+
60
+ export async function runReceipts(args = [], options = {}) {
61
+ const [command = "list", ...rest] = args;
62
+ const localDir =
63
+ options.localDir ||
64
+ stringFlag(rest, "--dir") ||
65
+ path.join(process.cwd(), ".agentsam", "receipts");
66
+
67
+ if (command === "prune") {
68
+ const result = await pruneLocalWorkspaces({
69
+ maxTmpGb: numberFlag(rest, "--max-tmp-gb", 1),
70
+ dryRun: rest.includes("--dry-run"),
71
+ });
72
+ if (rest.includes("--json")) {
73
+ process.stdout.write(JSON.stringify(result, null, 2) + "\n");
74
+ } else {
75
+ process.stdout.write(
76
+ "Scanned " +
77
+ result.scannedCount +
78
+ " AgentSam workdirs; purged " +
79
+ result.purgedCount +
80
+ "; " +
81
+ result.remainingBytes +
82
+ " bytes remain.\n",
83
+ );
84
+ }
85
+ return result;
86
+ }
87
+
88
+ const receipts = options.catalog
89
+ ? await options.catalog.list({
90
+ limit: intFlag(rest, "--limit", 20),
91
+ repositoryId: stringFlag(rest, "--repo"),
92
+ status: command === "failures" ? "failed" : undefined,
93
+ })
94
+ : await readLocalReceipts(localDir);
95
+
96
+ if (command === "show") {
97
+ const id = rest.find((arg) => !arg.startsWith("--"));
98
+ if (!id) throw new Error("agentsam receipts show requires a receipt id");
99
+ const receipt = options.catalog
100
+ ? await options.catalog.get(id)
101
+ : receipts.find((item) => item.receipt_id === id);
102
+ if (!receipt) {
103
+ const error = new Error("Receipt not found: " + id);
104
+ error.code = "AGENTSAM_RECEIPT_NOT_FOUND";
105
+ throw error;
106
+ }
107
+ process.stdout.write(JSON.stringify(receipt, null, 2) + "\n");
108
+ return receipt;
109
+ }
110
+
111
+ if (!["list", "failures"].includes(command)) {
112
+ throw new Error(
113
+ "Usage: agentsam receipts list [--limit 20] [--repo <id>]\n" +
114
+ " agentsam receipts show <receipt-id>\n" +
115
+ " agentsam receipts failures [--repo <id>]\n" +
116
+ " agentsam receipts prune [--max-tmp-gb 1.0] [--dry-run]",
117
+ );
118
+ }
119
+
120
+ const repo = stringFlag(rest, "--repo");
121
+ const limit = intFlag(rest, "--limit", 20);
122
+ const filtered = receipts
123
+ .filter((receipt) => !repo || receipt.identity?.repository_id === repo)
124
+ .filter((receipt) => command !== "failures" || receipt.status === "failed")
125
+ .slice(0, limit);
126
+
127
+ if (rest.includes("--json")) {
128
+ process.stdout.write(JSON.stringify(filtered, null, 2) + "\n");
129
+ } else {
130
+ if (!options.catalog) {
131
+ process.stderr.write(
132
+ " source: local receipt cache (" +
133
+ localDir +
134
+ "); durable D1 catalog requires a host catalog adapter\n",
135
+ );
136
+ }
137
+ for (const receipt of filtered) writeSummary(receipt);
138
+ }
139
+ return filtered;
140
+ }
@@ -0,0 +1,253 @@
1
+ import { createAgentControlClient } from '../acp/client.js';
2
+
3
+ function clean(value) { return value == null ? '' : String(value).trim(); }
4
+ function number(value, fallback) {
5
+ const n = Number(value);
6
+ return Number.isFinite(n) ? n : fallback;
7
+ }
8
+ function sleep(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); }
9
+
10
+ function parse(argv = []) {
11
+ const out = {
12
+ command: clean(argv[0]) || 'help',
13
+ id: null,
14
+ json: false,
15
+ local: false,
16
+ url: '',
17
+ once: false,
18
+ after: -1,
19
+ limit: 200,
20
+ intervalMs: 1000,
21
+ objective: '',
22
+ role: '',
23
+ workItemId: '',
24
+ stepId: '',
25
+ modelKey: '',
26
+ runtimeRequirements: {},
27
+ };
28
+ let i = 1;
29
+ if (!['help', 'start'].includes(out.command) && argv[i] && !String(argv[i]).startsWith('-')) out.id = String(argv[i++]);
30
+ for (; i < argv.length; i += 1) {
31
+ const arg = String(argv[i]);
32
+ if (arg === '--json') out.json = true;
33
+ else if (arg === '--local') out.local = true;
34
+ else if (arg === '--once') out.once = true;
35
+ else if (arg === '--url') out.url = String(argv[++i] || '');
36
+ else if (arg.startsWith('--url=')) out.url = arg.slice(6);
37
+ else if (arg === '--after') out.after = number(argv[++i], -1);
38
+ else if (arg.startsWith('--after=')) out.after = number(arg.slice(8), -1);
39
+ else if (arg === '--limit') out.limit = number(argv[++i], 200);
40
+ else if (arg.startsWith('--limit=')) out.limit = number(arg.slice(8), 200);
41
+ else if (arg === '--interval-ms') out.intervalMs = number(argv[++i], 1000);
42
+ else if (arg.startsWith('--interval-ms=')) out.intervalMs = number(arg.slice(14), 1000);
43
+ else if (arg === '--objective') out.objective = String(argv[++i] || '');
44
+ else if (arg.startsWith('--objective=')) out.objective = arg.slice(12);
45
+ else if (arg === '--role') out.role = String(argv[++i] || '');
46
+ else if (arg.startsWith('--role=')) out.role = arg.slice(7);
47
+ else if (arg === '--work-item') out.workItemId = String(argv[++i] || '');
48
+ else if (arg.startsWith('--work-item=')) out.workItemId = arg.slice(12);
49
+ else if (arg === '--step') out.stepId = String(argv[++i] || '');
50
+ else if (arg.startsWith('--step=')) out.stepId = arg.slice(7);
51
+ else if (arg === '--model') out.modelKey = String(argv[++i] || '');
52
+ else if (arg.startsWith('--model=')) out.modelKey = arg.slice(8);
53
+ else if (arg === '--runtime-json') out.runtimeRequirements = JSON.parse(String(argv[++i] || '{}'));
54
+ else if (arg.startsWith('--runtime-json=')) out.runtimeRequirements = JSON.parse(arg.slice(15) || '{}');
55
+ else throw new Error(`unknown_run_option:${arg}`);
56
+ }
57
+ return out;
58
+ }
59
+
60
+ function help() {
61
+ return [
62
+ 'Agent Sam · run',
63
+ 'Provider-neutral Agent Control Plane inspection and control.',
64
+ '',
65
+ 'Usage:',
66
+ ' agentsam run start --objective <text> [--role <slug>] [--model <key>] [--runtime-json <json>] [--json]',
67
+ ' agentsam run get <run_id> [--json] [--local | --url <base-url>]',
68
+ ' agentsam run tree <run_id> [--json]',
69
+ ' agentsam run events <run_id> [--after <seq>] [--limit <n>] [--json]',
70
+ ' agentsam run watch <run_id> [--after <seq>] [--interval-ms <ms>] [--once] [--json]',
71
+ ' agentsam run spawn <parent_run_id> --objective <text> [--role <slug>] [--runtime-json <json>] [--json]',
72
+ ' agentsam run cancel <run_id> [--json]',
73
+ ' agentsam run receipt <run_id> [--json]',
74
+ '',
75
+ 'Host selection:',
76
+ ' local SQLite is the default.',
77
+ ' AGENTSAM_CONTROL_PLANE_URL or --url selects any HTTP ACP host.',
78
+ ' AGENTSAM_CONTROL_PLANE_TOKEN supplies hosted bearer auth without putting secrets in shell history.',
79
+ ' --local forces the project-local SQLite control plane.',
80
+ ].join('\n');
81
+ }
82
+
83
+ function formatRun(run, clientKind) {
84
+ if (!run) return 'run not found';
85
+ return [
86
+ 'Agent Sam · run',
87
+ `source ${clientKind}`,
88
+ `id ${run.id}`,
89
+ `status ${run.status}${run.cancelRequested ? ' · cancel requested' : ''}`,
90
+ `mode ${run.mode || 'unknown'}`,
91
+ `parent ${run.parentRunId || '—'}`,
92
+ `model ${run.modelKey || '—'}`,
93
+ `calls model ${run.modelCallCount || 0} · tool ${run.toolCallCount || 0}`,
94
+ `cost $${Number(run.costUsd || 0).toFixed(6)}`,
95
+ run.suspension ? `wait ${run.suspension.state} · ${run.suspension.reason}` : null,
96
+ ].filter(Boolean).join('\n');
97
+ }
98
+
99
+ function treeLines(node, depth = 0, rows = []) {
100
+ if (!node) return rows;
101
+ const indent = ' '.repeat(depth);
102
+ rows.push(`${indent}${depth ? '└─ ' : ''}${node.id} · ${node.status} · ${node.mode || 'agent'}`);
103
+ for (const child of node.children || []) treeLines(child, depth + 1, rows);
104
+ return rows;
105
+ }
106
+
107
+ function eventLine(event) {
108
+ const at = event.createdAt ? new Date(event.createdAt).toISOString() : '';
109
+ return `#${event.seq} ${event.eventType}${event.phase ? ` · ${event.phase}` : ''}${event.label ? ` · ${event.label}` : ''}${at ? ` · ${at}` : ''}`;
110
+ }
111
+
112
+ function requireId(args) {
113
+ if (!clean(args.id)) {
114
+ const error = new Error(`run_id_required:${args.command}`);
115
+ error.hint = `Usage: agentsam run ${args.command} <run_id>`;
116
+ throw error;
117
+ }
118
+ }
119
+
120
+ export async function runRun(argv = [], options = {}) {
121
+ const args = parse(argv);
122
+ const write = options.write || ((value) => process.stdout.write(String(value)));
123
+ if (args.command === 'help' || args.command === '--help' || args.command === '-h') {
124
+ write(help() + '\n');
125
+ return { ok: true, command: 'help' };
126
+ }
127
+ if (!['start', 'get', 'tree', 'events', 'watch', 'spawn', 'cancel', 'receipt'].includes(args.command)) {
128
+ throw new Error(`unknown_run_command:${args.command}`);
129
+ }
130
+ if (args.command !== 'start') requireId(args);
131
+
132
+ const client = options.client || createAgentControlClient({
133
+ cwd: options.cwd || process.cwd(),
134
+ url: args.url || options.url,
135
+ token: options.token,
136
+ local: args.local,
137
+ fetchImpl: options.fetchImpl,
138
+ });
139
+
140
+ if (args.command === 'start') {
141
+ if (!clean(args.objective)) {
142
+ const error = new Error('run_objective_required');
143
+ error.hint = 'Usage: agentsam run start --objective <text>';
144
+ throw error;
145
+ }
146
+ const started = await client.start({
147
+ objective: args.objective,
148
+ role: clean(args.role) || 'lead',
149
+ model_key: clean(args.modelKey) || null,
150
+ runtime_requirements: args.runtimeRequirements,
151
+ });
152
+ if (args.json) write(JSON.stringify(started, null, 2) + '\n');
153
+ else {
154
+ write([
155
+ 'Agent Sam · run started',
156
+ `run ${started.run_id}`,
157
+ `queue ${started.queue || 'host-managed'}`,
158
+ `status ${started.status || 'queued'}`,
159
+ ].join('\n') + '\n');
160
+ }
161
+ return started;
162
+ }
163
+
164
+ if (args.command === 'get') {
165
+ const run = await client.getRun(args.id);
166
+ if (!run) throw new Error(`run_not_found:${args.id}`);
167
+ write(args.json ? JSON.stringify(run, null, 2) + '\n' : formatRun(run, client.kind) + '\n');
168
+ return run;
169
+ }
170
+
171
+ if (args.command === 'tree') {
172
+ const tree = await client.tree(args.id);
173
+ if (!tree) throw new Error(`run_not_found:${args.id}`);
174
+ write(args.json ? JSON.stringify(tree, null, 2) + '\n' : treeLines(tree).join('\n') + '\n');
175
+ return tree;
176
+ }
177
+
178
+ if (args.command === 'events') {
179
+ const events = await client.events(args.id, { after: args.after, limit: args.limit });
180
+ write(args.json ? JSON.stringify(events, null, 2) + '\n' : (events.map(eventLine).join('\n') || 'no events') + '\n');
181
+ return events;
182
+ }
183
+
184
+ if (args.command === 'spawn') {
185
+ if (!clean(args.objective)) {
186
+ const error = new Error('child_run_objective_required');
187
+ error.hint = 'Usage: agentsam run spawn <parent_run_id> --objective <text>';
188
+ throw error;
189
+ }
190
+ const spawned = await client.spawn(args.id, {
191
+ objective: args.objective,
192
+ role: clean(args.role) || null,
193
+ work_item_id: clean(args.workItemId) || null,
194
+ step_id: clean(args.stepId) || null,
195
+ model_key: clean(args.modelKey) || null,
196
+ runtime_requirements: args.runtimeRequirements,
197
+ });
198
+ if (args.json) write(JSON.stringify(spawned, null, 2) + '\n');
199
+ else {
200
+ write([
201
+ 'Agent Sam · child run',
202
+ `parent ${spawned.parent_run_id || args.id}`,
203
+ `child ${spawned.child_run_id || '—'}`,
204
+ `queue ${spawned.queue || 'host-managed'}`,
205
+ `status ${spawned.dependency_status || 'pending'}`,
206
+ ].join('\n') + '\n');
207
+ }
208
+ return spawned;
209
+ }
210
+
211
+ if (args.command === 'cancel') {
212
+ const run = await client.cancel(args.id);
213
+ if (!run) throw new Error(`run_not_found:${args.id}`);
214
+ write(args.json ? JSON.stringify(run, null, 2) + '\n' : formatRun(run, client.kind) + '\n');
215
+ return run;
216
+ }
217
+
218
+ if (args.command === 'receipt') {
219
+ const receipt = await client.receipt(args.id);
220
+ if (!receipt) throw new Error(`run_not_found:${args.id}`);
221
+ if (args.json) write(JSON.stringify(receipt, null, 2) + '\n');
222
+ else {
223
+ write([
224
+ 'Agent Sam · run receipt',
225
+ `id ${receipt.runId}`,
226
+ `status ${receipt.status}`,
227
+ `terminal ${receipt.terminal ? 'yes' : 'no'}`,
228
+ `events ${receipt.eventCount}`,
229
+ `children ${receipt.childRunCount}`,
230
+ `cost $${Number(receipt.usage?.costUsd || 0).toFixed(6)}`,
231
+ ].join('\n') + '\n');
232
+ }
233
+ return receipt;
234
+ }
235
+
236
+ let after = args.after;
237
+ const seen = [];
238
+ const wait = options.sleep || sleep;
239
+ do {
240
+ const events = await client.events(args.id, { after, limit: args.limit });
241
+ for (const event of events) {
242
+ seen.push(event);
243
+ after = Math.max(after, Number(event.seq));
244
+ write(args.json ? JSON.stringify(event) + '\n' : eventLine(event) + '\n');
245
+ }
246
+ const run = await client.getRun(args.id);
247
+ if (!run) throw new Error(`run_not_found:${args.id}`);
248
+ if (args.once || ['completed', 'failed', 'cancelled', 'timed_out'].includes(run.status)) {
249
+ return { run, events: seen, after };
250
+ }
251
+ await wait(Math.max(100, args.intervalMs));
252
+ } while (true);
253
+ }
@@ -1,5 +1,5 @@
1
1
  import { spawnSync } from 'node:child_process';
2
- import { probeGoDeploymentWithRetry } from './cloudflare.js';
2
+ import { probeGoDeploymentWithRetry } from './probe.js';
3
3
 
4
4
  function runDocker(args, { productRoot, spawn = spawnSync } = {}) {
5
5
  const res = spawn('docker', args, {
@@ -1,6 +1,7 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { spawnSync } from 'node:child_process';
4
+ import { createRequire } from 'node:module';
4
5
  import { DEFAULT_PRODUCT } from './discover.js';
5
6
 
6
7
  const PACKAGE_TEMPLATE = {
@@ -90,12 +91,16 @@ function rel(root, file) {
90
91
  return path.relative(root, file).split(path.sep).join('/');
91
92
  }
92
93
 
93
- export function preflightToolchain({ requireDocker = false, productRoot = null } = {}) {
94
+ export function preflightToolchain({
95
+ requireDocker = false,
96
+ productRoot = null,
97
+ spawn = spawnSync,
98
+ } = {}) {
94
99
  const checks = [];
95
100
  const push = (id, ok, detail = '') => checks.push({ id, ok, detail });
96
101
 
97
102
  try {
98
- const v = spawnSync('go', ['version'], { encoding: 'utf8' });
103
+ const v = spawn('go', ['version'], { encoding: 'utf8' });
99
104
  push('go', v.status === 0, (v.stdout || v.stderr || '').trim());
100
105
  } catch (e) {
101
106
  push('go', false, e.message);
@@ -103,7 +108,7 @@ export function preflightToolchain({ requireDocker = false, productRoot = null }
103
108
 
104
109
  const wranglerBin = resolveWranglerBin(productRoot);
105
110
  try {
106
- const v = spawnSync(wranglerBin.command, wranglerBin.args.concat(['--version']), {
111
+ const v = spawn(wranglerBin.command, wranglerBin.args.concat(['--version']), {
107
112
  encoding: 'utf8',
108
113
  cwd: productRoot || process.cwd(),
109
114
  });
@@ -112,33 +117,71 @@ export function preflightToolchain({ requireDocker = false, productRoot = null }
112
117
  push('wrangler', false, e.message);
113
118
  }
114
119
 
115
- try {
116
- const v = spawnSync('docker', ['info'], { encoding: 'utf8' });
117
- push('docker', v.status === 0, v.status === 0 ? 'available' : (v.stderr || 'unavailable').trim().slice(0, 200));
118
- } catch (e) {
119
- push('docker', false, e.message);
120
+ // Docker is capability-gated.
121
+ //
122
+ // A caller that does not require a container must not execute
123
+ // `docker info` merely to discover that Docker is optional.
124
+ //
125
+ // Besides wasting work, Docker Desktop may be installed while its
126
+ // daemon is stopped/unhealthy, and `docker info` can block for a
127
+ // significant period.
128
+ if (requireDocker) {
129
+ try {
130
+ const v = spawn('docker', ['info'], {
131
+ encoding: 'utf8',
132
+ timeout: 8000,
133
+ });
134
+ push(
135
+ 'docker',
136
+ v.status === 0,
137
+ v.status === 0
138
+ ? 'available'
139
+ : (v.stderr || 'unavailable').trim().slice(0, 200),
140
+ );
141
+ } catch (e) {
142
+ push('docker', false, e.message);
143
+ }
144
+ } else {
145
+ push('docker', true, 'skipped:not-required');
120
146
  }
121
147
 
122
- const failed = checks.filter((c) => {
123
- if (c.ok) return false;
124
- if (c.id === 'docker' && !requireDocker) return false;
125
- return true;
126
- });
148
+ const failed = checks.filter((c) => !c.ok);
127
149
  return { ok: failed.length === 0, checks, failed, wrangler: wranglerBin };
128
150
  }
129
151
 
130
- function resolveWranglerBin(productRoot) {
152
+ export function resolveWranglerBin(productRoot) {
153
+ if (productRoot) {
154
+ try {
155
+ const req = createRequire(path.join(productRoot, 'package.json'));
156
+ const resolved = req.resolve('wrangler');
157
+ if (resolved) {
158
+ return { command: process.execPath, args: [resolved], source: 'package_dependency' };
159
+ }
160
+ } catch {
161
+ // Fall through to explicit paths / pinned npx fallback.
162
+ }
163
+ }
164
+
131
165
  const candidates = [];
132
166
  if (productRoot) {
133
- candidates.push(path.join(productRoot, 'node_modules', 'wrangler', 'bin', 'wrangler.js'));
134
167
  candidates.push(path.join(productRoot, 'node_modules', '.bin', 'wrangler'));
135
168
  }
136
169
  candidates.push(path.join(process.cwd(), 'node_modules', '.bin', 'wrangler'));
137
170
  for (const candidate of candidates) {
138
171
  if (fs.existsSync(candidate)) {
139
- if (candidate.endsWith('.js')) return { command: process.execPath, args: [candidate] };
140
- return { command: candidate, args: [] };
172
+ return { command: candidate, args: [], source: 'local_bin' };
173
+ }
174
+ }
175
+
176
+ let requested = 'wrangler';
177
+ if (productRoot) {
178
+ try {
179
+ const pkg = JSON.parse(fs.readFileSync(path.join(productRoot, 'package.json'), 'utf8'));
180
+ const version = pkg.dependencies?.wrangler || pkg.devDependencies?.wrangler || '';
181
+ if (version) requested = 'wrangler@' + version;
182
+ } catch {
183
+ // Keep generic fallback only when package metadata is unavailable.
141
184
  }
142
185
  }
143
- return { command: 'npx', args: ['--yes', 'wrangler'] };
186
+ return { command: 'npx', args: ['--yes', requested], source: 'npx_fallback' };
144
187
  }
@@ -180,10 +180,6 @@ export function probeGoToolchain() {
180
180
  }
181
181
 
182
182
  export function resolveGoStateRoot(discovery, cwd = process.cwd()) {
183
- const origin = discovery?.runtime?.origin || null;
184
- if (origin === 'repository' || origin === 'sdk_development_tree') {
185
- return discovery.runtime.productRoot;
186
- }
187
183
  return repositoryRoot(cwd);
188
184
  }
189
185
 
package/src/go/index.js CHANGED
@@ -1,7 +1,19 @@
1
1
  export { discoverGoRuntime, resolveProductRoot, resolveGoStateRoot, probeGoToolchain, DEFAULT_PRODUCT, SDK_ROOT } from './discover.js';
2
2
  export { ensureProductContract, readProductManifest, preflightToolchain } from './contract.js';
3
3
  export { buildGoProduct, runGoBuild, runGoTests, runGoVet } from './build.js';
4
- export { deployGoCloudflare, resolveWranglerIdentity, probeGoDeployment, probeGoDeploymentWithRetry, readLatestWranglerDeployment } from './cloudflare.js';
4
+ export {
5
+ deployGoCloudflare,
6
+ resolveConfiguredDeploymentUrl,
7
+ resolveWranglerIdentity,
8
+ readLatestWranglerDeployment,
9
+ extractWorkersDevUrl,
10
+ guessWorkersDevUrl,
11
+ } from './providers/cloudflare.js';
12
+
13
+ export {
14
+ probeGoDeployment,
15
+ probeGoDeploymentWithRetry,
16
+ } from './probe.js';
5
17
  export { verifyGoContainer } from './container.js';
6
18
  export { verifyGoProduct } from './verify.js';
7
19
  export {
@@ -0,0 +1,109 @@
1
+ const EXPECTED_HASH = '2e60bba13dc2bc37d75dd2ce5deb25466f19cb2994e20889388948879875eae9';
2
+
3
+ export async function probeGoDeployment(origin, {
4
+ fetchImpl = globalThis.fetch,
5
+ expectedSource = null,
6
+ expectedSourceCommit = null,
7
+ expectedTarget = null,
8
+ edge = true,
9
+ } = {}) {
10
+ const base = String(origin).replace(/\/+$/, '');
11
+ const results = {};
12
+ const checks = {};
13
+ const probedAt = new Date().toISOString();
14
+
15
+ async function request(key, pathname, init = {}) {
16
+ try {
17
+ const res = await fetchImpl(base + pathname, init);
18
+ let body = null;
19
+ try { body = await res.json(); } catch {}
20
+ results[key] = {
21
+ path: pathname,
22
+ status: res.status,
23
+ ok: res.status >= 200 && res.status < 300,
24
+ body,
25
+ };
26
+ return results[key];
27
+ } catch (error) {
28
+ results[key] = { path: pathname, status: 0, ok: false, error: error.message };
29
+ return results[key];
30
+ }
31
+ }
32
+
33
+ if (edge) {
34
+ await request('edge_root', '/', { headers: { Accept: 'application/json' } });
35
+ await request('edge_health', '/edge/health', { headers: { Accept: 'application/json' } });
36
+ }
37
+ await request('health', '/health', { headers: { Accept: 'application/json' } });
38
+ await request('runtime', '/v1/runtime', { headers: { Accept: 'application/json' } });
39
+ await request('capabilities', '/v1/capabilities', { headers: { Accept: 'application/json' } });
40
+ await request('hash', '/v1/hash', {
41
+ method: 'POST',
42
+ headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
43
+ body: JSON.stringify({ input: 'agentsam', algorithm: 'sha256' }),
44
+ });
45
+ await request('inspect', '/v1/inspect', {
46
+ method: 'POST',
47
+ headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
48
+ body: JSON.stringify({ files: [{ path: 'demo.css', content: '.button { color: #2563eb; }' }] }),
49
+ });
50
+ await request('malformed', '/v1/inspect', {
51
+ method: 'POST',
52
+ headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
53
+ body: JSON.stringify({ not_files: true }),
54
+ });
55
+
56
+ const health = results.health?.body;
57
+ const runtime = results.runtime?.body;
58
+ const caps = results.capabilities?.body;
59
+ const malformed = results.malformed?.body;
60
+
61
+ if (edge) {
62
+ checks.edge_root = results.edge_root?.status === 200 && results.edge_root?.body?.edge === 'worker';
63
+ checks.edge_health = results.edge_health?.status === 200 && results.edge_health?.body?.edge === 'worker';
64
+ checks.edge_source = expectedSource
65
+ ? results.edge_root?.body?.source === expectedSource
66
+ : true;
67
+ }
68
+ checks.health = results.health?.status === 200
69
+ && health?.ok === true
70
+ && health?.runtime === 'go'
71
+ && (!expectedTarget || health?.target === expectedTarget);
72
+ checks.source_identity = expectedSource ? health?.build?.source === expectedSource : true;
73
+ checks.source_commit = expectedSourceCommit ? health?.build?.commit === expectedSourceCommit : true;
74
+ checks.runtime = results.runtime?.status === 200
75
+ && runtime?.schema === 'agentsam.go-runtime.v1'
76
+ && runtime?.os === 'linux';
77
+ checks.capabilities = results.capabilities?.status === 200
78
+ && caps?.schema === 'agentsam.go-capabilities.v1'
79
+ && ['hash', 'inspect', 'runtime', 'capabilities'].every((name) => caps?.capabilities?.includes(name));
80
+ checks.hash = results.hash?.status === 200 && results.hash?.body?.hash === EXPECTED_HASH;
81
+ checks.inspect = results.inspect?.status === 200
82
+ && results.inspect?.body?.findings?.some((finding) => finding.kind === 'hardcoded_color' && finding.value === '#2563eb');
83
+ checks.error_envelope = results.malformed?.status === 400
84
+ && malformed?.ok === false
85
+ && malformed?.schema_version === 1
86
+ && malformed?.reason === 'input_invalid'
87
+ && malformed?.code === 'INVALID_ARGUMENT';
88
+
89
+ results.deterministic_hash = { ok: checks.hash };
90
+ results.deterministic_inspect = { ok: checks.inspect };
91
+ results.malformed_rejected = { ok: checks.error_envelope };
92
+ const ok = Object.values(checks).every(Boolean);
93
+ return { origin: base, ok, checks, results, probed_at: probedAt };
94
+ }
95
+
96
+ export async function probeGoDeploymentWithRetry(origin, {
97
+ attempts = 12,
98
+ delayMs = 2500,
99
+ ...options
100
+ } = {}) {
101
+ let latest = null;
102
+ for (let attempt = 1; attempt <= attempts; attempt += 1) {
103
+ latest = await probeGoDeployment(origin, options);
104
+ latest.attempt = attempt;
105
+ if (latest.ok) return latest;
106
+ if (attempt < attempts) await new Promise((resolve) => setTimeout(resolve, delayMs));
107
+ }
108
+ return latest || { origin, ok: false, checks: {}, results: {}, probed_at: new Date().toISOString() };
109
+ }