@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,426 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import {
3
+ QueueControl,
4
+ SqliteQueueAdapter,
5
+ buildQueueTopology,
6
+ } from '../../packages/agentsam-queue-control/src/index.js';
7
+
8
+ function clean(value) {
9
+ return value == null ? '' : String(value).trim();
10
+ }
11
+
12
+ export function createLocalQueueControl(database) {
13
+ return new QueueControl({
14
+ adapter: new SqliteQueueAdapter({
15
+ database,
16
+ leaseOwner: 'agentsam-acp-local',
17
+ }),
18
+ topology: buildQueueTopology({
19
+ namespace: 'agentsam',
20
+ environment: 'local',
21
+ mode: 'compact',
22
+ }),
23
+ });
24
+ }
25
+
26
+ async function nextEventSeq(database, runId) {
27
+ const row = await database.prepare(
28
+ 'SELECT COALESCE(MAX(seq), -1) + 1 AS next_seq FROM agentsam_agent_run_event WHERE run_id = ?'
29
+ ).bind(runId).first();
30
+ return Number(row?.next_seq || 0);
31
+ }
32
+
33
+ export async function appendAgentRunEvent(database, {
34
+ runId,
35
+ parentRunId = null,
36
+ eventType,
37
+ phase = null,
38
+ stepId = null,
39
+ label = '',
40
+ detail = null,
41
+ evidence = {},
42
+ dedupeKey = null,
43
+ sourceKind = 'sam',
44
+ sourceName = 'agent-control-plane',
45
+ createdAt = Math.floor(Date.now() / 1000),
46
+ } = {}) {
47
+ const id = clean(runId);
48
+ const type = clean(eventType);
49
+ if (!id) throw new TypeError('runId is required');
50
+ if (!type) throw new TypeError('eventType is required');
51
+
52
+ const eventId = `evt_${randomUUID()}`;
53
+ const seq = await nextEventSeq(database, id);
54
+ await database.prepare(`
55
+ INSERT OR IGNORE INTO agentsam_agent_run_event (
56
+ event_id, run_id, parent_run_id, seq, event_type, phase, step_id,
57
+ label, detail, source_kind, source_name, evidence_json, dedupe_key,
58
+ created_at_unix
59
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
60
+ `).bind(
61
+ eventId,
62
+ id,
63
+ clean(parentRunId) || null,
64
+ seq,
65
+ type,
66
+ clean(phase) || null,
67
+ clean(stepId) || null,
68
+ clean(label),
69
+ detail == null ? null : String(detail),
70
+ clean(sourceKind) || 'sam',
71
+ clean(sourceName) || null,
72
+ JSON.stringify(evidence && typeof evidence === 'object' ? evidence : {}),
73
+ clean(dedupeKey) || null,
74
+ createdAt,
75
+ ).run();
76
+
77
+ return {
78
+ event_id: eventId,
79
+ run_id: id,
80
+ seq,
81
+ event_type: type,
82
+ dedupe_key: clean(dedupeKey) || null,
83
+ };
84
+ }
85
+
86
+ async function pendingChildIds(database, parentRunId) {
87
+ const result = await database.prepare(`
88
+ SELECT child_run_id
89
+ FROM agentsam_agent_run_dependency
90
+ WHERE parent_run_id = ? AND status = 'pending'
91
+ ORDER BY created_at_unix ASC, child_run_id ASC
92
+ `).bind(parentRunId).all();
93
+ return (result.results || []).map((row) => row.child_run_id);
94
+ }
95
+
96
+ export async function spawnChildRun(database, {
97
+ parentRunId,
98
+ childRunId = null,
99
+ accountId = null,
100
+ objective = '',
101
+ role = null,
102
+ workItemId = null,
103
+ stepId = null,
104
+ modelKey = null,
105
+ reasoningEffort = null,
106
+ serviceTier = null,
107
+ runtimeRequirements = {},
108
+ metadata = {},
109
+ priority = 'normal',
110
+ } = {}) {
111
+ const parentId = clean(parentRunId);
112
+ if (!parentId) throw new TypeError('parentRunId is required');
113
+
114
+ const parent = await database.prepare(
115
+ 'SELECT * FROM agentsam_agent_run WHERE id = ? LIMIT 1'
116
+ ).bind(parentId).first();
117
+ if (!parent) {
118
+ const error = new Error(`parent_run_not_found:${parentId}`);
119
+ error.code = 'parent_run_not_found';
120
+ throw error;
121
+ }
122
+ if (['completed', 'failed', 'cancelled'].includes(parent.status)) {
123
+ const error = new Error(`parent_run_terminal:${parent.status}`);
124
+ error.code = 'parent_run_terminal';
125
+ throw error;
126
+ }
127
+
128
+ const currentSuspension = await database.prepare(
129
+ 'SELECT state FROM agentsam_agent_run_suspension WHERE run_id = ? LIMIT 1'
130
+ ).bind(parentId).first();
131
+ if (currentSuspension && currentSuspension.state !== 'waiting_child') {
132
+ const error = new Error(`parent_run_already_suspended:${currentSuspension.state}`);
133
+ error.code = 'parent_run_already_suspended';
134
+ throw error;
135
+ }
136
+
137
+ const childId = clean(childRunId) || `arun_${randomUUID()}`;
138
+ const resolvedAccount = clean(accountId) || clean(parent.account_id) || 'local';
139
+ const now = Math.floor(Date.now() / 1000);
140
+
141
+ await database.prepare(`
142
+ INSERT INTO agentsam_agent_run (
143
+ id, account_id, conversation_id, parent_run_id, source_client, surface,
144
+ mode, model_key, reasoning_effort, requested_service_tier, selected_by,
145
+ status, created_at_unix, updated_at_unix
146
+ ) VALUES (?, ?, ?, ?, ?, ?, 'agent', ?, ?, ?, 'automatic', 'queued', ?, ?)
147
+ `).bind(
148
+ childId,
149
+ resolvedAccount,
150
+ parent.conversation_id || null,
151
+ parentId,
152
+ parent.source_client || 'agentsam-acp',
153
+ parent.surface || 'control-plane',
154
+ clean(modelKey) || parent.model_key || null,
155
+ clean(reasoningEffort) || parent.reasoning_effort || null,
156
+ clean(serviceTier) || parent.requested_service_tier || null,
157
+ now,
158
+ now,
159
+ ).run();
160
+
161
+ await database.prepare(`
162
+ INSERT INTO agentsam_agent_run_dependency (
163
+ parent_run_id, child_run_id, relation, work_item_id, step_id, status,
164
+ metadata_json, created_at_unix, updated_at_unix
165
+ ) VALUES (?, ?, 'blocks', ?, ?, 'pending', ?, ?, ?)
166
+ `).bind(
167
+ parentId,
168
+ childId,
169
+ clean(workItemId) || null,
170
+ clean(stepId) || null,
171
+ JSON.stringify(metadata && typeof metadata === 'object' ? metadata : {}),
172
+ now,
173
+ now,
174
+ ).run();
175
+
176
+ const pending = await pendingChildIds(database, parentId);
177
+ await database.prepare(`
178
+ UPDATE agentsam_agent_run
179
+ SET status = 'paused', updated_at_unix = ?
180
+ WHERE id = ?
181
+ `).bind(now, parentId).run();
182
+
183
+ await database.prepare(`
184
+ INSERT INTO agentsam_agent_run_suspension (
185
+ run_id, state, reason, dependency_run_ids_json, resume_step_id,
186
+ metadata_json, created_at_unix, updated_at_unix
187
+ ) VALUES (?, 'waiting_child', 'child_run', ?, ?, ?, ?, ?)
188
+ ON CONFLICT(run_id) DO UPDATE SET
189
+ state = 'waiting_child',
190
+ reason = 'child_run',
191
+ dependency_run_ids_json = excluded.dependency_run_ids_json,
192
+ resume_step_id = COALESCE(excluded.resume_step_id, agentsam_agent_run_suspension.resume_step_id),
193
+ metadata_json = excluded.metadata_json,
194
+ updated_at_unix = excluded.updated_at_unix
195
+ `).bind(
196
+ parentId,
197
+ JSON.stringify(pending),
198
+ clean(stepId) || null,
199
+ JSON.stringify({
200
+ child_count: pending.length,
201
+ last_child_run_id: childId,
202
+ ...(metadata && typeof metadata === 'object' ? metadata : {}),
203
+ }),
204
+ now,
205
+ now,
206
+ ).run();
207
+
208
+ await appendAgentRunEvent(database, {
209
+ runId: childId,
210
+ parentRunId: parentId,
211
+ eventType: 'run.created',
212
+ phase: 'boot',
213
+ label: 'Child run created',
214
+ evidence: {
215
+ parent_run_id: parentId,
216
+ objective: clean(objective) || null,
217
+ role: clean(role) || null,
218
+ work_item_id: clean(workItemId) || null,
219
+ },
220
+ dedupeKey: `child:${childId}:created`,
221
+ createdAt: now,
222
+ });
223
+ await appendAgentRunEvent(database, {
224
+ runId: childId,
225
+ parentRunId: parentId,
226
+ eventType: 'run.queued',
227
+ phase: 'boot',
228
+ label: 'Child run queued',
229
+ evidence: { parent_run_id: parentId },
230
+ dedupeKey: `child:${childId}:queued`,
231
+ createdAt: now,
232
+ });
233
+ await appendAgentRunEvent(database, {
234
+ runId: parentId,
235
+ parentRunId: parent.parent_run_id || null,
236
+ eventType: 'run.suspended',
237
+ phase: 'waiting',
238
+ stepId: clean(stepId) || null,
239
+ label: 'Waiting for child run',
240
+ evidence: {
241
+ child_run_id: childId,
242
+ dependency_run_ids: pending,
243
+ work_item_id: clean(workItemId) || null,
244
+ },
245
+ dedupeKey: `wait-child:${childId}`,
246
+ createdAt: now,
247
+ });
248
+
249
+ const queueControl = createLocalQueueControl(database);
250
+ const queued = await queueControl.enqueue(
251
+ {
252
+ kind: 'agent.run',
253
+ account_id: resolvedAccount,
254
+ priority,
255
+ payload: {
256
+ run_id: childId,
257
+ parent_run_id: parentId,
258
+ objective: clean(objective) || null,
259
+ role: clean(role) || null,
260
+ work_item_id: clean(workItemId) || null,
261
+ step_id: clean(stepId) || null,
262
+ runtime_requirements: runtimeRequirements && typeof runtimeRequirements === 'object'
263
+ ? runtimeRequirements
264
+ : {},
265
+ },
266
+ },
267
+ {
268
+ id: `job_agent_run_${childId}`,
269
+ account_id: resolvedAccount,
270
+ source_run_id: childId,
271
+ step_id: clean(stepId) || null,
272
+ conversation_id: parent.conversation_id || null,
273
+ idempotency_key: `agent-run:${childId}`,
274
+ priority,
275
+ },
276
+ );
277
+
278
+ return {
279
+ schema: 'agentsam.child-run-spawn.v1',
280
+ parent_run_id: parentId,
281
+ child_run_id: childId,
282
+ dependency_status: 'pending',
283
+ pending_child_run_ids: pending,
284
+ queue: queued.plan?.physical_queue || null,
285
+ queue_job: queued.job,
286
+ published: queued.published,
287
+ };
288
+ }
289
+
290
+ export async function reconcileParentAfterChildTerminal(database, {
291
+ runId,
292
+ status,
293
+ errorCode = null,
294
+ errorMessage = null,
295
+ } = {}) {
296
+ const childId = clean(runId);
297
+ const childStatus = clean(status);
298
+ if (!childId || !childStatus) return { reconciled: false, reason: 'missing_input' };
299
+ if (!['completed', 'failed', 'cancelled', 'partial'].includes(childStatus)) {
300
+ return { reconciled: false, reason: 'non_terminal_child_status' };
301
+ }
302
+
303
+ const child = await database.prepare(
304
+ 'SELECT * FROM agentsam_agent_run WHERE id = ? LIMIT 1'
305
+ ).bind(childId).first();
306
+ if (!child?.parent_run_id) return { reconciled: false, reason: 'no_parent' };
307
+
308
+ const parentId = child.parent_run_id;
309
+ const dependencyStatus = childStatus === 'completed'
310
+ ? 'satisfied'
311
+ : childStatus === 'cancelled'
312
+ ? 'cancelled'
313
+ : 'failed';
314
+ const now = Math.floor(Date.now() / 1000);
315
+
316
+ await database.prepare(`
317
+ UPDATE agentsam_agent_run_dependency
318
+ SET status = ?, completed_at_unix = ?, updated_at_unix = ?
319
+ WHERE child_run_id = ? AND parent_run_id = ? AND status = 'pending'
320
+ `).bind(dependencyStatus, now, now, childId, parentId).run();
321
+
322
+ const remaining = await pendingChildIds(database, parentId);
323
+ await appendAgentRunEvent(database, {
324
+ runId: parentId,
325
+ eventType: 'run.status',
326
+ phase: 'recover',
327
+ label: `Child run ${childStatus}`,
328
+ evidence: {
329
+ child_run_id: childId,
330
+ child_status: childStatus,
331
+ dependency_status: dependencyStatus,
332
+ remaining_child_run_ids: remaining,
333
+ error_code: clean(errorCode) || null,
334
+ error_message: clean(errorMessage) || null,
335
+ },
336
+ dedupeKey: `child-terminal:${childId}:${childStatus}`,
337
+ createdAt: now,
338
+ });
339
+
340
+ if (remaining.length > 0) {
341
+ await database.prepare(`
342
+ UPDATE agentsam_agent_run_suspension
343
+ SET dependency_run_ids_json = ?, updated_at_unix = ?
344
+ WHERE run_id = ? AND state = 'waiting_child'
345
+ `).bind(JSON.stringify(remaining), now, parentId).run();
346
+ return {
347
+ reconciled: true,
348
+ parent_run_id: parentId,
349
+ woke_parent: false,
350
+ dependency_status: dependencyStatus,
351
+ remaining_child_run_ids: remaining,
352
+ };
353
+ }
354
+
355
+ const parent = await database.prepare(
356
+ 'SELECT * FROM agentsam_agent_run WHERE id = ? LIMIT 1'
357
+ ).bind(parentId).first();
358
+ if (!parent || ['completed', 'failed', 'cancelled'].includes(parent.status)) {
359
+ return {
360
+ reconciled: true,
361
+ parent_run_id: parentId,
362
+ woke_parent: false,
363
+ reason: 'parent_terminal',
364
+ dependency_status: dependencyStatus,
365
+ remaining_child_run_ids: [],
366
+ };
367
+ }
368
+
369
+ await database.prepare(
370
+ "DELETE FROM agentsam_agent_run_suspension WHERE run_id = ? AND state = 'waiting_child'"
371
+ ).bind(parentId).run();
372
+ await database.prepare(`
373
+ UPDATE agentsam_agent_run
374
+ SET status = 'queued', updated_at_unix = ?
375
+ WHERE id = ?
376
+ `).bind(now, parentId).run();
377
+
378
+ await appendAgentRunEvent(database, {
379
+ runId: parentId,
380
+ eventType: 'run.woken',
381
+ phase: 'recover',
382
+ label: 'Child dependencies resolved',
383
+ evidence: {
384
+ completed_child_run_id: childId,
385
+ completed_child_status: childStatus,
386
+ resume_reason: 'child_run',
387
+ cancel_requested: Boolean(parent.cancel_requested),
388
+ },
389
+ dedupeKey: `wake-child:${childId}`,
390
+ createdAt: now,
391
+ });
392
+
393
+ const resolvedAccount = clean(parent.account_id) || clean(child.account_id) || 'local';
394
+ const queueControl = createLocalQueueControl(database);
395
+ const queued = await queueControl.enqueue(
396
+ {
397
+ kind: 'agent.resume',
398
+ account_id: resolvedAccount,
399
+ payload: {
400
+ run_id: parentId,
401
+ wake_reason: 'child_run',
402
+ completed_child_run_id: childId,
403
+ completed_child_status: childStatus,
404
+ cancel_requested: Boolean(parent.cancel_requested),
405
+ },
406
+ },
407
+ {
408
+ id: `job_agent_resume_${parentId}_${childId}`,
409
+ account_id: resolvedAccount,
410
+ source_run_id: parentId,
411
+ conversation_id: parent.conversation_id || null,
412
+ idempotency_key: `agent-resume:${parentId}:after:${childId}`,
413
+ priority: parent.cancel_requested ? 'high' : 'normal',
414
+ },
415
+ );
416
+
417
+ return {
418
+ reconciled: true,
419
+ parent_run_id: parentId,
420
+ woke_parent: true,
421
+ dependency_status: dependencyStatus,
422
+ remaining_child_run_ids: [],
423
+ resume_job: queued.job,
424
+ queue: queued.plan?.physical_queue || null,
425
+ };
426
+ }
@@ -0,0 +1,207 @@
1
+ function clean(value) {
2
+ return value == null ? '' : String(value).trim();
3
+ }
4
+
5
+ function json(body, status = 200, headers = {}) {
6
+ return Response.json(body, {
7
+ status,
8
+ headers: {
9
+ 'cache-control': 'no-store',
10
+ ...headers,
11
+ },
12
+ });
13
+ }
14
+
15
+ function errorBody(error, fallback = 'acp_request_failed') {
16
+ return {
17
+ error: {
18
+ code: clean(error?.code) || fallback,
19
+ message: clean(error?.message) || fallback,
20
+ },
21
+ };
22
+ }
23
+
24
+ function statusForError(error) {
25
+ const explicit = Number(error?.status || error?.statusCode);
26
+ if (Number.isInteger(explicit) && explicit >= 400 && explicit <= 599) return explicit;
27
+ const code = clean(error?.code);
28
+ if (code.endsWith('_not_found') || code === 'run_not_found') return 404;
29
+ if (code.includes('required') || code.includes('invalid') || code.startsWith('unknown_')) return 400;
30
+ if (code.includes('terminal') || code.includes('already_')) return 409;
31
+ if (code === 'unauthorized') return 401;
32
+ if (code === 'forbidden') return 403;
33
+ return 500;
34
+ }
35
+
36
+ async function readJson(request) {
37
+ const text = await request.text();
38
+ if (!text) return {};
39
+ try {
40
+ const body = JSON.parse(text);
41
+ if (!body || typeof body !== 'object' || Array.isArray(body)) {
42
+ const error = new TypeError('request_body_must_be_object');
43
+ error.code = 'input_invalid';
44
+ throw error;
45
+ }
46
+ return body;
47
+ } catch (error) {
48
+ if (error?.code === 'input_invalid') throw error;
49
+ const wrapped = new TypeError('invalid_json_body');
50
+ wrapped.code = 'input_invalid';
51
+ throw wrapped;
52
+ }
53
+ }
54
+
55
+ function segments(pathname) {
56
+ return pathname.split('/').filter(Boolean).map((part) => decodeURIComponent(part));
57
+ }
58
+
59
+ function requireMethod(request, allowed) {
60
+ if (allowed.includes(request.method)) return;
61
+ const error = new Error('method_not_allowed');
62
+ error.code = 'method_not_allowed';
63
+ error.status = 405;
64
+ error.allowed = allowed;
65
+ throw error;
66
+ }
67
+
68
+ function ensureControl(control) {
69
+ if (!control || typeof control !== 'object') throw new TypeError('control adapter is required');
70
+ for (const method of ['start', 'getRun', 'events', 'tree', 'spawn', 'cancel', 'receipt']) {
71
+ if (typeof control[method] !== 'function') throw new TypeError(`control.${method}() is required`);
72
+ }
73
+ return control;
74
+ }
75
+
76
+ /**
77
+ * Provider-neutral AgentSam Agent Control Plane HTTP contract.
78
+ *
79
+ * The request router contains no Cloudflare/GCP/Docker/VM assumptions. A host
80
+ * supplies a control adapter backed by its chosen persistence/queue/runtime
81
+ * implementation and may supply authorization separately.
82
+ */
83
+ export function createAgentControlHttpHandler({
84
+ control,
85
+ authorize = null,
86
+ service = 'agentsam-control-plane',
87
+ } = {}) {
88
+ const adapter = ensureControl(control);
89
+
90
+ return async function handleAgentControlRequest(request, context = {}) {
91
+ try {
92
+ if (!(request instanceof Request)) throw new TypeError('Request is required');
93
+
94
+ if (typeof authorize === 'function') {
95
+ const auth = await authorize(request, context);
96
+ if (auth === false || auth?.ok === false) {
97
+ const error = new Error(auth?.message || 'unauthorized');
98
+ error.code = auth?.code || 'unauthorized';
99
+ error.status = auth?.status || 401;
100
+ throw error;
101
+ }
102
+ }
103
+
104
+ const url = new URL(request.url);
105
+ const parts = segments(url.pathname);
106
+
107
+ if (url.pathname === '/health' || url.pathname === '/v1/health') {
108
+ requireMethod(request, ['GET']);
109
+ return json({
110
+ ok: true,
111
+ service,
112
+ protocol: 'agentsam.control.v1',
113
+ });
114
+ }
115
+
116
+ if (parts[0] !== 'v1' || parts[1] !== 'runs') {
117
+ return json(errorBody(Object.assign(new Error('route_not_found'), { code: 'route_not_found' })), 404);
118
+ }
119
+
120
+ // POST /v1/runs
121
+ if (parts.length === 2) {
122
+ requireMethod(request, ['POST']);
123
+ return json(await adapter.start(await readJson(request)), 202);
124
+ }
125
+
126
+ const runId = clean(parts[2]);
127
+ if (!runId) {
128
+ const error = new Error('run_id_required');
129
+ error.code = 'run_id_required';
130
+ throw error;
131
+ }
132
+
133
+ // GET /v1/runs/:id
134
+ if (parts.length === 3) {
135
+ requireMethod(request, ['GET']);
136
+ const run = await adapter.getRun(runId);
137
+ if (!run) {
138
+ const error = new Error(`run_not_found:${runId}`);
139
+ error.code = 'run_not_found';
140
+ throw error;
141
+ }
142
+ return json(run);
143
+ }
144
+
145
+ const action = parts[3];
146
+ if (parts.length !== 4) {
147
+ return json(errorBody(Object.assign(new Error('route_not_found'), { code: 'route_not_found' })), 404);
148
+ }
149
+
150
+ if (action === 'events') {
151
+ requireMethod(request, ['GET']);
152
+ const after = Number(url.searchParams.get('after') ?? -1);
153
+ const limit = Number(url.searchParams.get('limit') ?? 200);
154
+ return json(await adapter.events(runId, {
155
+ after: Number.isFinite(after) ? after : -1,
156
+ limit: Number.isFinite(limit) ? limit : 200,
157
+ }));
158
+ }
159
+
160
+ if (action === 'tree') {
161
+ requireMethod(request, ['GET']);
162
+ const tree = await adapter.tree(runId);
163
+ if (!tree) {
164
+ const error = new Error(`run_not_found:${runId}`);
165
+ error.code = 'run_not_found';
166
+ throw error;
167
+ }
168
+ return json(tree);
169
+ }
170
+
171
+ if (action === 'spawn') {
172
+ requireMethod(request, ['POST']);
173
+ return json(await adapter.spawn(runId, await readJson(request)), 202);
174
+ }
175
+
176
+ if (action === 'cancel') {
177
+ requireMethod(request, ['POST']);
178
+ const run = await adapter.cancel(runId);
179
+ if (!run) {
180
+ const error = new Error(`run_not_found:${runId}`);
181
+ error.code = 'run_not_found';
182
+ throw error;
183
+ }
184
+ return json(run, 202);
185
+ }
186
+
187
+ if (action === 'receipt') {
188
+ requireMethod(request, ['GET']);
189
+ const receipt = await adapter.receipt(runId);
190
+ if (!receipt) {
191
+ const error = new Error(`run_not_found:${runId}`);
192
+ error.code = 'run_not_found';
193
+ throw error;
194
+ }
195
+ return json(receipt);
196
+ }
197
+
198
+ return json(errorBody(Object.assign(new Error('route_not_found'), { code: 'route_not_found' })), 404);
199
+ } catch (error) {
200
+ const status = statusForError(error);
201
+ const headers = status === 405 && Array.isArray(error?.allowed)
202
+ ? { allow: error.allowed.join(', ') }
203
+ : {};
204
+ return json(errorBody(error), status, headers);
205
+ }
206
+ };
207
+ }
@@ -0,0 +1,22 @@
1
+ export {
2
+ DatabaseAgentControlClient,
3
+ LocalAgentControlClient,
4
+ HttpAgentControlClient,
5
+ createAgentControlClient,
6
+ } from './client.js';
7
+ export { createAgentControlHttpHandler } from './http.js';
8
+ export { createRootRun } from './runs.js';
9
+ export {
10
+ appendAgentRunEvent,
11
+ createLocalQueueControl,
12
+ spawnChildRun,
13
+ reconcileParentAfterChildTerminal,
14
+ } from './dependencies.js';
15
+ export { createAgentRunJobHandler } from './agent-executor.js';
16
+ export {
17
+ normalizeRequiredCapabilities,
18
+ normalizeRuntimeRequirements,
19
+ matchRuntimeCandidate,
20
+ selectRuntimeCandidate,
21
+ createRuntimeSelector,
22
+ } from './runtime-selection.js';