@inneranimalmedia/agentsam-sdk 2.6.2 → 2.6.3

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 (497) hide show
  1. package/AGENTSAM.md +45 -1
  2. package/README.md +80 -0
  3. package/apps/README.md +34 -0
  4. package/apps/cad-creator/README.md +728 -0
  5. package/apps/cad-creator/shared/cad/package.json +19 -0
  6. package/apps/cad-creator/shared/cad/src/domain/operations.ts +7 -0
  7. package/apps/cad-creator/shared/cad/src/domain/project-contract.d.ts +3 -0
  8. package/apps/cad-creator/shared/cad/src/domain/project-contract.js +75 -0
  9. package/apps/cad-creator/shared/cad/src/domain/project-tools.d.ts +1 -0
  10. package/apps/cad-creator/shared/cad/src/domain/project-tools.js +24 -0
  11. package/apps/cad-creator/shared/cad/src/domain/validation.ts +191 -0
  12. package/apps/cad-creator/shared/cad/src/index.ts +4 -0
  13. package/apps/cad-creator/shared/cad/src/robotics/types.ts +232 -0
  14. package/apps/cad-creator/shared/cad/src/types.ts +507 -0
  15. package/apps/cad-creator/shared/cad/tsconfig.json +14 -0
  16. package/docs/AUTORAG.md +58 -0
  17. package/docs/BRAND_INTELLIGENCE.md +30 -0
  18. package/docs/CLI_SHELL.md +4 -2
  19. package/docs/CMS_CONTEXTUAL_COMPOSER.md +38 -0
  20. package/docs/MERKLE.md +4 -0
  21. package/docs/PROJECT_CONFIG.md +3 -0
  22. package/docs/REPOSITORY_KNOWLEDGE.md +2 -0
  23. package/docs/THEME_REFINERY.md +120 -0
  24. package/docs/architecture/PROVIDER-TOOLS-AND-COMPACTION.md +60 -0
  25. package/docs/architecture/STORAGE.md +87 -0
  26. package/docs/cad-project-tools.md +69 -0
  27. package/docs/cms-scaffolding-pattern-reference.md +73 -0
  28. package/docs/plans/AGENTSAM-INTERACTIVE-UI-REQUIREMENTS-codex-tui-20260919.md +113 -0
  29. package/docs/plans/AGENTSAM-INTERACTIVE-UI-REQUIREMENTS.md +85 -0
  30. package/docs/plans/AGENTSAM-MCP-CLIENT-2026-09-19.md +181 -0
  31. package/docs/plans/ASBD-PORTABLE-WORKGRAPH-UI-DEFERRED.md +34 -0
  32. package/docs/plans/ASTRA-BRIEF-2026-09-18.md +258 -0
  33. package/docs/plans/MULTI-AGENT-SPRINTS-2026-09-19.md +373 -0
  34. package/docs/plans/NEXT-AGENT-HANDOFF.md +167 -0
  35. package/docs/plans/RECONCILIATION-2026-09-19.md +178 -0
  36. package/docs/plugins.md +43 -0
  37. package/docs/tools/README.md +57 -0
  38. package/docs/tools/agent-and-orchestration.md +49 -0
  39. package/docs/tools/browser-and-research.md +46 -0
  40. package/docs/tools/cloudflare-platform.md +32 -0
  41. package/docs/tools/cms.md +25 -0
  42. package/docs/tools/communications.md +41 -0
  43. package/docs/tools/database-and-storage.md +62 -0
  44. package/docs/tools/design-and-media.md +64 -0
  45. package/docs/tools/filesystem-and-code.md +55 -0
  46. package/docs/tools/github.md +56 -0
  47. package/docs/tools/integrations-and-misc.md +16 -0
  48. package/docs/tools/memory.md +21 -0
  49. package/docs/tools/terminal-and-execution.md +29 -0
  50. package/docs/tools/tickets-and-platform.md +30 -0
  51. package/migrations/d1/0001_agentsam_plugin_runtime.sql +177 -0
  52. package/migrations/d1/0002_agentsam_ticket_ownership.sql +8 -0
  53. package/migrations/d1/0002_dynamic_model_policies.sql +23 -0
  54. package/migrations/d1/0003_expand_tool_handler_families.sql +18 -0
  55. package/migrations/d1/0004_project_aware_retrieval_lanes.sql +24 -0
  56. package/migrations/d1/0005_portable_knowledge_identity.sql +104 -0
  57. package/migrations/d1/0006_repair_ops_trail_account_scope.sql +14 -0
  58. package/migrations/d1/0007_repair_ops_trail_agent_run_account.sql +13 -0
  59. package/migrations/d1/0008_backfill_knowledge_run_legacy_tenant.sql +14 -0
  60. package/migrations/d1/0009_normalize_cloudflare_plugin_installations.sql +137 -0
  61. package/migrations/runtime/0001_cli_runtime.sql +67 -0
  62. package/migrations/runtime/0002_project_sessions.sql +15 -0
  63. package/migrations/runtime/0003_provider_continuations.sql +8 -0
  64. package/package.json +92 -12
  65. package/packages/agentsam-brand/README.md +21 -0
  66. package/packages/agentsam-brand/package.json +22 -0
  67. package/packages/agentsam-brand/src/contract.js +59 -0
  68. package/packages/agentsam-brand/src/extract.js +126 -0
  69. package/packages/agentsam-brand/src/goap-actions.js +73 -0
  70. package/packages/agentsam-brand/src/index.js +48 -0
  71. package/packages/agentsam-brand/src/plan.js +115 -0
  72. package/packages/agentsam-brand/src/resolve.js +139 -0
  73. package/packages/agentsam-brand/src/scan.js +250 -0
  74. package/packages/agentsam-brand/test/brand-scan.test.mjs +106 -0
  75. package/packages/agentsam-contracts/README.md +23 -0
  76. package/packages/agentsam-contracts/package.json +32 -0
  77. package/packages/agentsam-contracts/src/adapter.ts +25 -0
  78. package/packages/agentsam-contracts/src/agent.ts +35 -0
  79. package/packages/agentsam-contracts/src/artifacts.ts +33 -0
  80. package/packages/agentsam-contracts/src/authority.ts +29 -0
  81. package/packages/agentsam-contracts/src/context.ts +16 -0
  82. package/packages/agentsam-contracts/src/errors.ts +46 -0
  83. package/packages/agentsam-contracts/src/events.ts +47 -0
  84. package/packages/agentsam-contracts/src/execution.ts +50 -0
  85. package/packages/agentsam-contracts/src/hooks.ts +40 -0
  86. package/packages/agentsam-contracts/src/identity.ts +16 -0
  87. package/packages/agentsam-contracts/src/index.ts +14 -0
  88. package/packages/agentsam-contracts/src/models.ts +9 -0
  89. package/packages/agentsam-contracts/src/providers.ts +33 -0
  90. package/packages/agentsam-contracts/src/repository.ts +64 -0
  91. package/packages/agentsam-contracts/src/tools.ts +98 -0
  92. package/packages/agentsam-contracts/test/contracts.test.mjs +16 -0
  93. package/packages/agentsam-contracts/test/errors-catalog.test.mjs +25 -0
  94. package/packages/agentsam-contracts/test/repository.test.mjs +18 -0
  95. package/packages/agentsam-contracts/tsconfig.json +12 -0
  96. package/packages/agentsam-errors/package.json +17 -0
  97. package/packages/agentsam-errors/src/adapters/anthropic.js +15 -0
  98. package/packages/agentsam-errors/src/adapters/cad.js +109 -0
  99. package/packages/agentsam-errors/src/adapters/cloudflare.js +21 -0
  100. package/packages/agentsam-errors/src/adapters/cursor.js +4 -0
  101. package/packages/agentsam-errors/src/adapters/device.js +38 -0
  102. package/packages/agentsam-errors/src/adapters/docker.js +28 -0
  103. package/packages/agentsam-errors/src/adapters/gcp.js +14 -0
  104. package/packages/agentsam-errors/src/adapters/github.js +22 -0
  105. package/packages/agentsam-errors/src/adapters/google.js +17 -0
  106. package/packages/agentsam-errors/src/adapters/index.js +55 -0
  107. package/packages/agentsam-errors/src/adapters/internal.js +40 -0
  108. package/packages/agentsam-errors/src/adapters/oauth.js +24 -0
  109. package/packages/agentsam-errors/src/adapters/openai.js +17 -0
  110. package/packages/agentsam-errors/src/adapters/process.js +35 -0
  111. package/packages/agentsam-errors/src/adapters/provider.js +114 -0
  112. package/packages/agentsam-errors/src/envelope.js +186 -0
  113. package/packages/agentsam-errors/src/error.js +24 -0
  114. package/packages/agentsam-errors/src/fingerprint.js +35 -0
  115. package/packages/agentsam-errors/src/index.js +10 -0
  116. package/packages/agentsam-errors/src/normalize.js +30 -0
  117. package/packages/agentsam-errors/src/redaction.js +52 -0
  118. package/packages/agentsam-errors/src/render.js +16 -0
  119. package/packages/agentsam-errors/src/retry.js +10 -0
  120. package/packages/agentsam-errors/src/transport.js +52 -0
  121. package/packages/agentsam-errors/src/vocabulary.js +320 -0
  122. package/packages/agentsam-errors/test/envelope.test.mjs +70 -0
  123. package/packages/agentsam-errors/test/providers.test.mjs +99 -0
  124. package/packages/agentsam-errors/test/redaction.test.mjs +21 -0
  125. package/packages/agentsam-errors/test/runtime-cad-device.test.mjs +76 -0
  126. package/packages/agentsam-errors/test/transport.test.mjs +46 -0
  127. package/packages/agentsam-knowledge/package.json +15 -0
  128. package/packages/agentsam-knowledge/src/autorag/index.js +64 -0
  129. package/packages/agentsam-knowledge/src/backends/index.js +26 -0
  130. package/packages/agentsam-knowledge/src/index.js +3 -0
  131. package/packages/agentsam-knowledge/src/providers/index.js +95 -0
  132. package/packages/agentsam-knowledge/test/autorag.test.mjs +42 -0
  133. package/packages/agentsam-nav/README.md +40 -0
  134. package/packages/agentsam-nav/dist/Nav.d.ts +43 -0
  135. package/packages/agentsam-nav/dist/NavCollapsible.d.ts +10 -0
  136. package/packages/agentsam-nav/dist/NavContent.d.ts +2 -0
  137. package/packages/agentsam-nav/dist/NavFooter.d.ts +2 -0
  138. package/packages/agentsam-nav/dist/NavGroup.d.ts +3 -0
  139. package/packages/agentsam-nav/dist/NavHeader.d.ts +2 -0
  140. package/packages/agentsam-nav/dist/NavMenu.d.ts +22 -0
  141. package/packages/agentsam-nav/dist/NavProvider.d.ts +35 -0
  142. package/packages/agentsam-nav/dist/NavTrigger.d.ts +2 -0
  143. package/packages/agentsam-nav/dist/Surfaces.d.ts +41 -0
  144. package/packages/agentsam-nav/dist/browser.d.ts +11 -0
  145. package/packages/agentsam-nav/dist/browser.global.js +132 -0
  146. package/packages/agentsam-nav/dist/browser.js +613 -0
  147. package/packages/agentsam-nav/dist/contracts.d.ts +76 -0
  148. package/packages/agentsam-nav/dist/index.d.ts +11 -0
  149. package/packages/agentsam-nav/dist/index.js +614 -0
  150. package/packages/agentsam-nav/dist/legacy.css +83 -0
  151. package/packages/agentsam-nav/dist/shell.css +113 -0
  152. package/packages/agentsam-nav/dist/theme.css +113 -0
  153. package/packages/agentsam-nav/dist/utils.d.ts +1 -0
  154. package/packages/agentsam-nav/package.json +43 -0
  155. package/packages/agentsam-queue-control/README.md +107 -0
  156. package/packages/agentsam-queue-control/package.json +31 -0
  157. package/packages/agentsam-queue-control/schemas/job-envelope.schema.json +56 -0
  158. package/packages/agentsam-queue-control/src/adapters/cloudflare.js +152 -0
  159. package/packages/agentsam-queue-control/src/adapters/memory.js +38 -0
  160. package/packages/agentsam-queue-control/src/contracts.js +130 -0
  161. package/packages/agentsam-queue-control/src/control-plane.js +141 -0
  162. package/packages/agentsam-queue-control/src/dispatch.js +43 -0
  163. package/packages/agentsam-queue-control/src/index.js +7 -0
  164. package/packages/agentsam-queue-control/src/policy.js +88 -0
  165. package/packages/agentsam-queue-control/src/topology.js +66 -0
  166. package/packages/agentsam-queue-control/test/queue-control.test.mjs +100 -0
  167. package/packages/agentsam-repository/src/goap.js +718 -0
  168. package/packages/agentsam-repository/src/goap.js.bak-20260922011251 +670 -0
  169. package/packages/agentsam-repository/src/index.js +5 -0
  170. package/packages/agentsam-repository/src/merkle/cloudflare-persistence.js +2 -2
  171. package/packages/agentsam-repository/src/tickets.js +184 -0
  172. package/packages/agentsam-repository/src/work-tracking.js +199 -0
  173. package/packages/agentsam-repository/src/workspace-state.js +176 -0
  174. package/packages/agentsam-repository/test/goap-tickets-workspace-state.test.mjs +152 -0
  175. package/packages/connectors/cloudflare/src/index.js +297 -15
  176. package/packages/connectors/cloudflare/src/owner.js +43 -40
  177. package/packages/connectors/cloudflare/src/routes.js +167 -26
  178. package/packages/connectors/cloudflare/tests/connector.test.mjs +112 -2
  179. package/packages/heuristic-theme/README.md +11 -0
  180. package/packages/heuristic-theme/package.json +25 -0
  181. package/packages/heuristic-theme/src/index.js +2 -0
  182. package/packages/heuristic-theme/src/registry.js +36 -0
  183. package/packages/heuristic-theme/src/theme.js +41 -0
  184. package/packages/heuristic-theme/test/theme.test.mjs +26 -0
  185. package/packages/identity/.agentsam/features/oauth-login-portal/agentsam.feature.json +64 -0
  186. package/packages/identity/.agentsam/features/oauth-login-portal/providers.json +96 -0
  187. package/packages/identity/.agentsam/features/oauth-login-portal/resources.json +47 -0
  188. package/packages/identity/.agentsam/features/oauth-login-portal/routes.json +74 -0
  189. package/packages/identity/.agentsam/features/oauth-login-portal/ui.json +35 -0
  190. package/packages/identity/package.json +3 -2
  191. package/packages/identity/src/adapters/cloudflare-d1/ids.js +4 -0
  192. package/packages/identity/src/adapters/cloudflare-d1/index.js +39 -1
  193. package/packages/identity/src/contracts/provider.js +3 -2
  194. package/packages/identity/src/frontend/auth-portal/pages/login.html +7 -0
  195. package/packages/identity/src/index.js +2 -0
  196. package/packages/identity/src/oauth/credentials.js +13 -0
  197. package/packages/identity/src/oauth/iam-platform.js +5 -5
  198. package/packages/identity/src/providers/cloudflare/index.js +19 -0
  199. package/packages/identity/src/providers/cloudflare/mapper.js +21 -0
  200. package/packages/identity/src/providers/cloudflare/oauth.js +48 -0
  201. package/packages/identity/src/providers/cloudflare/profile.js +28 -0
  202. package/packages/identity/src/providers/index.js +18 -2
  203. package/packages/identity/src/server/identity-service.js +13 -3
  204. package/packages/identity/src/server/worker-router.js +63 -8
  205. package/packages/identity/tests/identity-service.test.mjs +15 -0
  206. package/packages/providers/completeful/README.md +38 -0
  207. package/packages/providers/completeful/package.json +29 -0
  208. package/packages/providers/completeful/src/client.js +162 -0
  209. package/packages/providers/completeful/src/events.js +49 -0
  210. package/packages/providers/completeful/src/index.js +22 -0
  211. package/packages/providers/completeful/src/tools.js +579 -0
  212. package/packages/providers/completeful/src/webhooks.js +55 -0
  213. package/packages/providers/completeful/test/provider.test.mjs +196 -0
  214. package/packages/theme-church-site/README.md +10 -0
  215. package/packages/theme-church-site/package.json +30 -0
  216. package/packages/theme-church-site/src/harvest.json +14 -0
  217. package/packages/theme-church-site/src/index.js +84 -0
  218. package/packages/theme-church-site/test/theme.test.mjs +19 -0
  219. package/packages/theme-companions-site/README.md +10 -0
  220. package/packages/theme-companions-site/package.json +30 -0
  221. package/packages/theme-companions-site/src/harvest.json +12 -0
  222. package/packages/theme-companions-site/src/index.js +82 -0
  223. package/packages/theme-companions-site/test/theme.test.mjs +19 -0
  224. package/packages/theme-floors-site/README.md +10 -0
  225. package/packages/theme-floors-site/package.json +30 -0
  226. package/packages/theme-floors-site/src/harvest.json +14 -0
  227. package/packages/theme-floors-site/src/index.js +82 -0
  228. package/packages/theme-floors-site/test/theme.test.mjs +19 -0
  229. package/packages/theme-fuelnfree-site/README.md +10 -0
  230. package/packages/theme-fuelnfree-site/package.json +30 -0
  231. package/packages/theme-fuelnfree-site/src/harvest.json +13 -0
  232. package/packages/theme-fuelnfree-site/src/index.js +81 -0
  233. package/packages/theme-fuelnfree-site/test/theme.test.mjs +19 -0
  234. package/packages/theme-handyman-site/README.md +10 -0
  235. package/packages/theme-handyman-site/package.json +30 -0
  236. package/packages/theme-handyman-site/src/harvest.json +10 -0
  237. package/packages/theme-handyman-site/src/index.js +83 -0
  238. package/packages/theme-handyman-site/test/theme.test.mjs +19 -0
  239. package/packages/theme-insurance-site/README.md +10 -0
  240. package/packages/theme-insurance-site/package.json +30 -0
  241. package/packages/theme-insurance-site/src/harvest.json +10 -0
  242. package/packages/theme-insurance-site/src/index.js +83 -0
  243. package/packages/theme-insurance-site/test/theme.test.mjs +19 -0
  244. package/packages/theme-shinshu-site/README.md +10 -0
  245. package/packages/theme-shinshu-site/package.json +30 -0
  246. package/packages/theme-shinshu-site/src/harvest.json +12 -0
  247. package/packages/theme-shinshu-site/src/index.js +83 -0
  248. package/packages/theme-shinshu-site/test/theme.test.mjs +19 -0
  249. package/packages/work-graph/README.md +49 -0
  250. package/packages/work-graph/package.json +19 -0
  251. package/packages/work-graph/prototype/README.md +32 -0
  252. package/packages/work-graph/prototype/data/demo-workgraph.js +47 -0
  253. package/packages/work-graph/prototype/renderers/timeline-model.js +21 -0
  254. package/packages/work-graph/prototype/renderers/workgraph-runtime.js +38 -0
  255. package/packages/work-graph/prototype-gnantt.html +655 -0
  256. package/packages/work-graph/src/adapters/business/projects.js +23 -0
  257. package/packages/work-graph/src/adapters/engineering/git.js +22 -0
  258. package/packages/work-graph/src/adapters/engineering/mcp.js +22 -0
  259. package/packages/work-graph/src/index.js +76 -0
  260. package/packages/work-graph/src/renderers/gantt-model.js +11 -0
  261. package/packages/work-graph/src/renderers/timeline.js +16 -0
  262. package/packages/work-graph/test/work-graph.test.mjs +37 -0
  263. package/protocol/README.md +12 -0
  264. package/protocol/capabilities/cloudflare-cpu-audit-input.schema.json +30 -6
  265. package/protocol/capabilities/cloudflare-cpu-profile-input.schema.json +18 -4
  266. package/protocol/capabilities/cloudflare-wrangler-native-input.schema.json +59 -10
  267. package/protocol/capabilities/knowledge-search-input.schema.json +35 -0
  268. package/protocol/capabilities/manifest.json +163 -1
  269. package/protocol/capabilities/repository-audit-input.schema.json +39 -6
  270. package/protocol/capabilities/repository-snapshot-input.schema.json +15 -3
  271. package/protocol/capabilities/terminal-exec-input.schema.json +55 -0
  272. package/protocol/commerce/catalog.v1.json +50 -0
  273. package/protocol/errors/error-catalog.json +164 -0
  274. package/protocol/errors/error-envelope.schema.json +544 -0
  275. package/protocol/features/README.md +57 -0
  276. package/protocol/features/agentsam.feature.v1.schema.json +290 -0
  277. package/protocol/features/catalog.json +65 -0
  278. package/protocol/features/examples/provider-resend.feature.json +17 -0
  279. package/protocol/go/agentsam-go-runtime.v1.json +17 -0
  280. package/protocol/knowledge/LIFECYCLE.md +140 -0
  281. package/protocol/knowledge/README.md +4 -0
  282. package/protocol/presets/catalog.json +128 -19
  283. package/protocol/rpc/v1/errors.proto +24 -0
  284. package/protocol/theme-refinery/d1-reconciliation.md +48 -0
  285. package/protocol/theme-refinery/product-metadata.schema.json +27 -0
  286. package/protocol/theme-refinery/receipts/products-registry-batch-20260924.json +318 -0
  287. package/protocol/theme-refinery/receipts/theme-harvest-20260924.json +472 -0
  288. package/protocol/theme-refinery/reconciliation-receipt.schema.json +83 -0
  289. package/services/cad/server.py +249 -2
  290. package/src/agent/capability-adapter.js +15 -5
  291. package/src/agent/index.js +2 -1
  292. package/src/agent/model-turn.js +51 -0
  293. package/src/agent/responses-runner.js +304 -38
  294. package/src/capabilities/index.js +1 -0
  295. package/src/capabilities/terminal-exec.js +78 -0
  296. package/src/cli.js +117 -9
  297. package/src/cloudflare/index.js +6 -0
  298. package/src/cloudflare/runtime-status.js +393 -0
  299. package/src/cloudflare/wrangler.js +17 -1
  300. package/src/commands/app.js +106 -0
  301. package/src/commands/autorag.js +77 -0
  302. package/src/commands/brand.js +293 -0
  303. package/src/commands/cad.js +1 -0
  304. package/src/commands/connections.js +126 -29
  305. package/src/commands/context-economics.js +8 -0
  306. package/src/commands/db.js +7 -0
  307. package/src/commands/env.js +5 -4
  308. package/src/commands/eval.js +209 -14
  309. package/src/commands/go.js +365 -0
  310. package/src/commands/goap.js +176 -0
  311. package/src/commands/identity-init.js +27 -3
  312. package/src/commands/identity-protocol.js +195 -0
  313. package/src/commands/knowledge.js +64 -5
  314. package/src/commands/mcp.js +341 -0
  315. package/src/commands/merkle-persist.js +3 -1
  316. package/src/commands/merkle.js +18 -3
  317. package/src/commands/models.js +109 -23
  318. package/src/commands/plugins.js +175 -0
  319. package/src/commands/preferences.js +28 -8
  320. package/src/commands/product.js +53 -10
  321. package/src/commands/providers.js +118 -37
  322. package/src/commands/resume.js +9 -5
  323. package/src/commands/runtime-status.js +154 -0
  324. package/src/commands/shell.js +466 -55
  325. package/src/commands/status.js +82 -7
  326. package/src/commands/tunnel.js +228 -195
  327. package/src/commands/whoami.js +78 -6
  328. package/src/context/budget.js +6 -0
  329. package/src/context/index.js +2 -1
  330. package/src/context/project-card.js +75 -0
  331. package/src/errors/contract.js +34 -211
  332. package/src/errors/diagnostic.js +95 -51
  333. package/src/errors/index.js +6 -13
  334. package/src/eval/index.js +6 -0
  335. package/src/eval/live.js +439 -0
  336. package/src/features/resolve.js +242 -0
  337. package/src/go/build.js +108 -0
  338. package/src/go/cloudflare.js +207 -0
  339. package/src/go/contract.js +139 -0
  340. package/src/go/discover.js +96 -0
  341. package/src/go/index.js +13 -0
  342. package/src/go/receipts.js +77 -0
  343. package/src/go/registry.js +223 -0
  344. package/src/go/verify.js +31 -0
  345. package/src/index.js +100 -1
  346. package/src/knowledge/config.js +13 -5
  347. package/src/knowledge/engine.js +23 -8
  348. package/src/knowledge/index.js +1 -0
  349. package/src/knowledge/lane-registry.js +46 -0
  350. package/src/knowledge/providers/gemini.js +48 -8
  351. package/src/knowledge/service/client.js +13 -1
  352. package/src/knowledge/service/grpc-codec.js +156 -53
  353. package/src/knowledge/service/job-engine.js +133 -11
  354. package/src/knowledge/service/job-worker.js +34 -5
  355. package/src/knowledge/service/server.js +10 -1
  356. package/src/knowledge/store-discovery.js +77 -0
  357. package/src/lib/account-session.js +8 -2
  358. package/src/lib/agent-instructions.js +3 -0
  359. package/src/lib/auth.js +7 -3
  360. package/src/lib/cad/blender.js +42 -3
  361. package/src/lib/cad/d1-project-store.js +43 -0
  362. package/src/lib/cad/discovery.js +616 -0
  363. package/src/lib/cad/docker-executor.js +352 -0
  364. package/src/lib/cad/freecad.js +253 -0
  365. package/src/lib/cad/index.js +40 -0
  366. package/src/lib/cad/openscad.js +198 -0
  367. package/src/lib/cad/project-cli.d.ts +2 -0
  368. package/src/lib/cad/project-cli.js +21 -0
  369. package/src/lib/cad/project-contract.d.ts +3 -0
  370. package/src/lib/cad/project-contract.js +1 -0
  371. package/src/lib/cad/project-recipe.js +30 -0
  372. package/src/lib/cad/project-runtime.d.ts +2 -0
  373. package/src/lib/cad/project-runtime.js +53 -0
  374. package/src/lib/cad/project-store.js +25 -0
  375. package/src/lib/cad/project-workflows.js +31 -0
  376. package/src/lib/cad-docker.js +21 -2
  377. package/src/lib/cli-preferences.js +17 -2
  378. package/src/lib/core-client.js +37 -18
  379. package/src/lib/deploy/local-studio.js +6 -5
  380. package/src/lib/dockerize.js +3 -0
  381. package/src/lib/execution-approvals.js +1 -0
  382. package/src/lib/identity-scaffold.js +4 -1
  383. package/src/lib/knowledge-docker.js +9 -1
  384. package/src/lib/local-sessions.js +124 -34
  385. package/src/lib/local-status.js +35 -14
  386. package/src/lib/project-config.js +9 -0
  387. package/src/lib/provider-credentials.js +73 -11
  388. package/src/lib/scaffold/resolve-cloudflare-account.js +37 -0
  389. package/src/lib/scaffold/wizards/cms.js +2 -8
  390. package/src/lib/scaffold/wizards/worker-api.js +2 -8
  391. package/src/lib/slash-commands.js +9 -2
  392. package/src/local/migrations.js +109 -1
  393. package/src/local/runtime-store.js +31 -5
  394. package/src/local/sqlite.js +36 -1
  395. package/src/mcp/authority.js +348 -0
  396. package/src/mcp/client-adapters.js +431 -0
  397. package/src/mcp/client.js +172 -0
  398. package/src/mcp/index.js +47 -0
  399. package/src/mcp/telemetry.js +103 -0
  400. package/src/models/catalog.js +329 -51
  401. package/src/models/discovery.js +24 -4
  402. package/src/models/index.js +2 -0
  403. package/src/models/inventory-core.js +182 -0
  404. package/src/models/policy.js +20 -0
  405. package/src/plugins/cloudflare.js +103 -0
  406. package/src/plugins/completeful.js +69 -0
  407. package/src/plugins/contracts.js +86 -0
  408. package/src/plugins/index.js +11 -0
  409. package/src/plugins/registry.js +207 -0
  410. package/src/plugins/resource-scope.js +45 -0
  411. package/src/plugins/runtime.js +118 -0
  412. package/src/plugins/vectorize.js +31 -0
  413. package/src/progression/engine.js +171 -0
  414. package/src/progression/index.js +1 -0
  415. package/src/providers/cloudflare-chat.js +1 -1
  416. package/src/providers/gemini-generate-content.js +2 -1
  417. package/src/providers/openai-responses.js +45 -16
  418. package/src/providers/tool-schema.js +85 -0
  419. package/src/repository/index.js +29 -0
  420. package/src/rpc/generated/errors_pb.js +961 -1
  421. package/src/security/local-vault.js +415 -0
  422. package/src/security/process.js +20 -4
  423. package/src/status/actions.js +192 -0
  424. package/src/tools/hydrate.js +5 -1
  425. package/src/tools/index.js +7 -0
  426. package/src/tools/redact.js +64 -0
  427. package/src/tools/registry.js +83 -0
  428. package/src/tools/runtime.js +257 -0
  429. package/src/tools/search.js +21 -8
  430. package/src/ui/ansi.js +117 -3
  431. package/src/ui/cli/activity.js +76 -7
  432. package/src/ui/cli/footer.js +110 -9
  433. package/src/ui/cli/help.js +11 -4
  434. package/src/ui/wireframes.js +166 -0
  435. package/test/account-session.test.mjs +28 -0
  436. package/test/apps-scaffold-contract.test.mjs +16 -1
  437. package/test/capabilities.test.mjs +1 -1
  438. package/test/cli/eval-live.test.mjs +219 -0
  439. package/test/cli/go.test.mjs +71 -0
  440. package/test/cli/local-vault-and-provider-setup.test.mjs +268 -0
  441. package/test/cli/mcp-client.test.mjs +245 -0
  442. package/test/cli/runtime-ui.test.mjs +69 -2
  443. package/test/cli/status-actions.test.mjs +88 -0
  444. package/test/cli/wireframes-go-registry.test.mjs +54 -0
  445. package/test/cloudflare-connector.test.mjs +3 -6
  446. package/test/cloudflare-runtime.test.mjs +8 -1
  447. package/test/context.test.mjs +15 -0
  448. package/test/error-diagnostics.test.mjs +9 -0
  449. package/test/fixtures/cad/courtyard-house.json +208 -0
  450. package/test/go/cloudflare-probe.test.mjs +53 -0
  451. package/test/integration/app-command.test.mjs +55 -0
  452. package/test/integration/cad-discovery.test.mjs +158 -0
  453. package/test/integration/cad-docker-executor.test.mjs +249 -0
  454. package/test/integration/cad-execution.test.mjs +184 -0
  455. package/test/integration/cad-project-app.test.mjs +31 -0
  456. package/test/integration/cad-project-d1.test.mjs +25 -0
  457. package/test/integration/cad-project-native.test.mjs +24 -0
  458. package/test/integration/cad-project-workflow.test.mjs +9 -0
  459. package/test/integration/cad-project.test.mjs +82 -0
  460. package/test/integration/cli-help.test.mjs +4 -0
  461. package/test/integration/cms-site-tenancy-contract.test.mjs +256 -0
  462. package/test/integration/core-client.integration.test.mjs +55 -0
  463. package/test/integration/error-runtime-rpc.test.mjs +71 -0
  464. package/test/integration/features-resolve.test.mjs +128 -0
  465. package/test/integration/install-script.test.mjs +107 -0
  466. package/test/integration/inventory-core.test.mjs +53 -0
  467. package/test/integration/plugins-cloudflare.integration.test.mjs +28 -0
  468. package/test/integration/provider-env-cli.test.mjs +1 -1
  469. package/test/integration/runtime-migrations.test.mjs +6 -1
  470. package/test/integration/runtime-status.integration.test.mjs +211 -0
  471. package/test/integration/scaffold-cloudflare.test.mjs +46 -0
  472. package/test/integration/storage-architecture.test.mjs +154 -0
  473. package/test/integration/store-discovery.test.mjs +44 -0
  474. package/test/integration/terminal-exec.integration.test.mjs +86 -0
  475. package/test/integration/tool-runtime.test.mjs +124 -0
  476. package/test/integration/tool-schema.test.mjs +59 -0
  477. package/test/knowledge-service.test.mjs +4 -0
  478. package/test/local-sessions.test.mjs +11 -2
  479. package/test/local-studio-deploy.test.mjs +1 -1
  480. package/test/model-catalog.test.mjs +28 -1
  481. package/test/models.test.mjs +159 -5
  482. package/test/openai-responses.test.mjs +60 -0
  483. package/test/provider-credentials.test.mjs +15 -5
  484. package/test/responses-runner.test.mjs +321 -6
  485. package/test/shell.test.mjs +149 -4
  486. package/test/smoke.mjs +2 -2
  487. package/test/tools-search.test.mjs +33 -0
  488. package/test/unit/website-assets-bindings.test.mjs +60 -0
  489. package/test/whoami-resume.test.mjs +82 -3
  490. package/examples/gorilla-shell/README.md +0 -15
  491. package/src/lib/gorilla-template.js +0 -54
  492. package/templates/gorilla-shell/App.tsx +0 -639
  493. package/templates/gorilla-shell/README.md +0 -34
  494. package/templates/gorilla-shell/index.html +0 -15
  495. package/templates/gorilla-shell/main.jsx +0 -9
  496. package/templates/gorilla-shell/package.json +0 -19
  497. package/templates/gorilla-shell/vite.config.js +0 -21
@@ -0,0 +1,219 @@
1
+ import assert from 'node:assert/strict';
2
+ import fs from 'node:fs';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ import test from 'node:test';
6
+
7
+ import {
8
+ finishLiveEvalRun,
9
+ getLiveEvalStatus,
10
+ startLiveEvalRun,
11
+ } from '../../src/eval/index.js';
12
+ import { recordToolReceipt } from '../../src/mcp/index.js';
13
+ import { runEval } from '../../src/commands/eval.js';
14
+
15
+ function tempHome() {
16
+ return fs.mkdtempSync(path.join(os.tmpdir(), 'agentsam-live-eval-test-'));
17
+ }
18
+
19
+ test('live eval lifecycle: start, track receipts, get status, finish', async () => {
20
+ const home = tempHome();
21
+
22
+ // Initially inactive
23
+ const initialStatus = getLiveEvalStatus({ home });
24
+ assert.equal(initialStatus.active, false);
25
+
26
+ // Start live eval run
27
+ const run = startLiveEvalRun(
28
+ {
29
+ suite: 'sdk-real-work-20260919',
30
+ case: 'cad-shell-unification',
31
+ model: 'Muse Spark 1.3',
32
+ provider: 'cursor',
33
+ client: 'cursor',
34
+ reasoning: 'high',
35
+ },
36
+ { home }
37
+ );
38
+
39
+ assert.ok(run.run_id.startsWith('evr_'));
40
+ assert.equal(run.suite_id, 'sdk-real-work-20260919');
41
+ assert.equal(run.case_id, 'cad-shell-unification');
42
+ assert.equal(run.model, 'Muse Spark 1.3');
43
+
44
+ // Verify starting again fails
45
+ assert.throws(() => startLiveEvalRun({}, { home }), /live_eval_run_already_active/);
46
+
47
+ // Record tool calls into the live session
48
+ recordToolReceipt({ tool_key: 'github_read', source_client: 'cursor', duration_ms: 50 }, { home });
49
+ recordToolReceipt({ tool_key: 'mcp:d1_query', source_client: 'cursor_mcp', duration_ms: 12 }, { home });
50
+ recordToolReceipt({ tool_key: 'terminal_exec', source_client: 'agentsam', duration_ms: 300 }, { home });
51
+
52
+ // Status check reflects active state and tool receipts
53
+ const activeStatus = getLiveEvalStatus({ home });
54
+ assert.equal(activeStatus.active, true);
55
+ assert.equal(activeStatus.receipts_summary.tool_call_count, 3);
56
+ assert.equal(activeStatus.receipts_summary.mcp_call_count, 1);
57
+
58
+ // Finish run
59
+ const result = await finishLiveEvalRun({ home, gate: 'PASS' });
60
+ assert.equal(result.evalRunRow.id, run.run_id);
61
+ assert.equal(result.evalRunRow.passed, 1);
62
+ assert.equal(result.evalRunRow.tool_calls_attempted, 3);
63
+ assert.equal(result.evalRunRow.tool_calls_succeeded, 3);
64
+ assert.equal(result.modelObservationRow.id, `obs_${run.run_id}`);
65
+ assert.equal(result.modelObservationRow.passed, 1);
66
+
67
+ // After finish, status is inactive again
68
+ const postStatus = getLiveEvalStatus({ home });
69
+ assert.equal(postStatus.active, false);
70
+ });
71
+
72
+ test('agentsam eval live CLI commands work end-to-end', async () => {
73
+ const home = tempHome();
74
+ const logs = [];
75
+ const write = (str) => logs.push(str);
76
+
77
+ // CLI start
78
+ const startResult = await runEval(
79
+ ['live', 'start', '--case', 'errors-v1-reconciliation', '--model', 'Muse Spark 1.3', '--json'],
80
+ { home, write }
81
+ );
82
+ assert.ok(startResult.run_id.startsWith('evr_'));
83
+
84
+ // Record receipt
85
+ recordToolReceipt({ tool_key: 'mcp:eval_query', source_client: 'cursor_mcp', duration_ms: 40 }, { home });
86
+
87
+ // CLI status
88
+ const statusResult = await runEval(['live', 'status', '--json'], { home, write });
89
+ assert.equal(statusResult.active, true);
90
+ assert.equal(statusResult.receipts_summary.tool_call_count, 1);
91
+
92
+ // CLI finish
93
+ const finishResult = await runEval(['live', 'finish', '--gate', 'PASS', '--json'], { home, write });
94
+ assert.equal(finishResult.evalRunRow.passed, 1);
95
+ assert.equal(finishResult.summary.gate_status, 'PASS');
96
+
97
+ // Verify non-JSON output formats cleanly
98
+ const emptyStatus = await runEval(['live', 'status'], { home, write });
99
+ assert.equal(emptyStatus.active, false);
100
+ });
101
+
102
+ test('finishLiveEvalRun archives run locally, generates SQL for both tables, and respects configurable tenant/database', async () => {
103
+ const home = tempHome();
104
+ const run = startLiveEvalRun(
105
+ {
106
+ case: 'cad-render-eval',
107
+ model: 'Muse Spark 1.3',
108
+ tenant: 'test-tenant',
109
+ workspace: 'cad-workspace',
110
+ user: 'test-user',
111
+ database: 'test-database',
112
+ },
113
+ { home }
114
+ );
115
+
116
+ assert.equal(run.tenant_id, 'test-tenant');
117
+ assert.equal(run.workspace_id, 'cad-workspace');
118
+ assert.equal(run.user_id, 'test-user');
119
+ assert.equal(run.database, 'test-database');
120
+
121
+ const result = await finishLiveEvalRun({ home, gate: 'PASS' });
122
+
123
+ // 1. Dual record construction
124
+ assert.equal(result.evalRunRow.tenant_id, 'test-tenant');
125
+ assert.equal(result.modelObservationRow.tenant_id, 'test-tenant');
126
+ assert.equal(result.modelObservationRow.workspace_id, 'cad-workspace');
127
+ assert.equal(result.modelObservationRow.user_id, 'test-user');
128
+ assert.equal(result.modelObservationRow.status, 'completed');
129
+ assert.equal(result.modelObservationRow.passed, 1);
130
+ assert.equal(result.modelObservationRow.expected_markers_total, 1);
131
+ assert.equal(typeof result.modelObservationRow.output_chars, 'number');
132
+
133
+ // 2. Both tables present in generated D1 SQL
134
+ assert.ok(result.summary.d1_sql.includes('INSERT OR REPLACE INTO agentsam_eval_runs'));
135
+ assert.ok(result.summary.d1_sql.includes('INSERT OR REPLACE INTO agentsam_model_eval_observations'));
136
+
137
+ // 3. Local archive created
138
+ const archivedRunPath = path.join(home, '.agentsam', 'eval', 'runs', `${run.run_id}.json`);
139
+ assert.ok(fs.existsSync(archivedRunPath), 'Archived run file must exist locally');
140
+ const archived = JSON.parse(fs.readFileSync(archivedRunPath, 'utf8'));
141
+ assert.equal(archived.evalRunRow.id, run.run_id);
142
+ assert.equal(archived.modelObservationRow.id, `obs_${run.run_id}`);
143
+ });
144
+
145
+ test('finishLiveEvalRun preserves active run state and receipts if remote persistence fails without --force', async () => {
146
+ const home = tempHome();
147
+ const run = startLiveEvalRun({ case: 'resilience-test' }, { home });
148
+
149
+ recordToolReceipt({ tool_key: 'git_status', duration_ms: 20 }, { home });
150
+
151
+ // Call finish with remote=true pointing to an unreachable endpoint and wrangler fallback disabled
152
+ const result = await finishLiveEvalRun({
153
+ home,
154
+ remote: true,
155
+ evalEndpoint: 'http://127.0.0.1:1/nonexistent',
156
+ wranglerFallback: false,
157
+ timeoutMs: 100,
158
+ });
159
+
160
+ // Verification: remote failure noted, evidence preserved, active run kept intact
161
+ assert.equal(result.summary.d1_executed, false);
162
+ assert.equal(result.summary.state_preserved, true);
163
+ assert.ok(result.summary.d1_error);
164
+
165
+ const activeStatus = getLiveEvalStatus({ home });
166
+ assert.equal(activeStatus.active, true, 'Active run state must be retained when remote persistence fails');
167
+ assert.equal(activeStatus.receipts_summary.tool_call_count, 1, 'Tool receipts must be preserved when remote persistence fails');
168
+
169
+ // Finish with force: true to finalize and clear active run
170
+ const forcedResult = await finishLiveEvalRun({
171
+ home,
172
+ remote: true,
173
+ evalEndpoint: 'http://127.0.0.1:1/nonexistent',
174
+ wranglerFallback: false,
175
+ timeoutMs: 100,
176
+ force: true,
177
+ });
178
+
179
+ assert.equal(forcedResult.summary.d1_executed, false);
180
+ const clearedStatus = getLiveEvalStatus({ home });
181
+ assert.equal(clearedStatus.active, false, 'Forced finish clears active run');
182
+ });
183
+
184
+ test('finishLiveEvalRun reuses the configured inneranimalmedia MCP bearer for remote persistence', { concurrency: false }, async () => {
185
+ const home = tempHome();
186
+ const mcpDir = path.join(home, '.agentsam', 'mcp');
187
+ fs.mkdirSync(mcpDir, { recursive: true });
188
+ fs.writeFileSync(
189
+ path.join(mcpDir, 'inneranimalmedia.json'),
190
+ JSON.stringify({ auth: { type: 'bearer', token: 'fixture-eval-token' } }),
191
+ );
192
+
193
+ startLiveEvalRun({ case: 'authenticated-remote-eval' }, { home });
194
+
195
+ const originalFetch = globalThis.fetch;
196
+ let authorization = null;
197
+ try {
198
+ globalThis.fetch = async (_url, init = {}) => {
199
+ authorization = new Headers(init.headers).get('Authorization');
200
+ return new Response(JSON.stringify({ ok: true }), {
201
+ status: 200,
202
+ headers: { 'Content-Type': 'application/json' },
203
+ });
204
+ };
205
+
206
+ const result = await finishLiveEvalRun({
207
+ home,
208
+ remote: true,
209
+ evalEndpoint: 'https://example.test/api/eval/record',
210
+ wranglerFallback: false,
211
+ });
212
+
213
+ assert.equal(authorization, 'Bearer fixture-eval-token');
214
+ assert.equal(result.summary.d1_executed, true);
215
+ assert.equal(result.summary.persistence_method, 'http_endpoint');
216
+ } finally {
217
+ globalThis.fetch = originalFetch;
218
+ }
219
+ });
@@ -0,0 +1,71 @@
1
+ import assert from 'node:assert/strict';
2
+ import fs from 'node:fs';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ import test from 'node:test';
6
+ import { fileURLToPath } from 'node:url';
7
+ import { discoverGoRuntime, resolveProductRoot, ensureProductContract, buildGoProduct } from '../../src/go/index.js';
8
+ import { runGo } from '../../src/commands/go.js';
9
+
10
+ const SDK_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..');
11
+
12
+ test('discoverGoRuntime finds agentsam-go-worker without inventing a sibling', () => {
13
+ const discovery = discoverGoRuntime(SDK_ROOT);
14
+ assert.equal(discovery.go.ok, true);
15
+ assert.ok(discovery.runtime, 'expected Go runtime');
16
+ assert.match(discovery.runtime.module, /agentsam-go-worker/);
17
+ assert.equal(path.basename(discovery.runtime.productRoot), 'agentsam-go-worker');
18
+ const productRoot = resolveProductRoot(discovery, 'agentsam-go-worker');
19
+ const contract = ensureProductContract(productRoot);
20
+ assert.equal(contract.product, 'agentsam-go-worker');
21
+ assert.equal(contract.existing, true);
22
+ assert.deepEqual(contract.created, []);
23
+ });
24
+
25
+ test('ensureProductContract refuses missing product roots instead of scaffolding siblings', () => {
26
+ const missing = path.join(os.tmpdir(), `agentsam-go-missing-${Date.now()}`);
27
+ assert.throws(() => ensureProductContract(missing), /product_root_missing/);
28
+ });
29
+
30
+ test('buildGoProduct emits build receipt after go test/vet/build', () => {
31
+ const discovery = discoverGoRuntime(SDK_ROOT);
32
+ const productRoot = resolveProductRoot(discovery);
33
+ const result = buildGoProduct({
34
+ productRoot,
35
+ runtimeRoot: discovery.runtime.runtimeRoot,
36
+ repositoryRoot: discovery.repository_root,
37
+ });
38
+ assert.equal(result.receipt.schema, 'agentsam.go-build-receipt.v1');
39
+ assert.equal(result.tests.ok, true);
40
+ assert.equal(result.vet.ok, true);
41
+ assert.ok(result.build.binary);
42
+ assert.ok(fs.existsSync(result.receiptPath));
43
+ });
44
+
45
+ test('agentsam go --cloudflare agentsam-go-worker --skip-deploy is idempotent', async () => {
46
+ const chunks = [];
47
+ const write = (v) => chunks.push(String(v));
48
+ const first = await runGo(['--cloudflare', 'agentsam-go-worker', '--skip-deploy', '--json'], { write });
49
+ assert.equal(first.ok, true);
50
+ assert.equal(first.product, 'agentsam-go-worker');
51
+ assert.equal(first.scaffold_changes_required, false);
52
+
53
+ const chunks2 = [];
54
+ const second = await runGo(['--cloudflare', 'agentsam-go-worker', '--skip-deploy', '--json'], {
55
+ write: (v) => chunks2.push(String(v)),
56
+ });
57
+ assert.equal(second.ok, true);
58
+ assert.equal(second.existing_product, true);
59
+ assert.equal(second.scaffold_changes_required, false);
60
+ assert.ok(second.deploy.receiptPath);
61
+ assert.ok(fs.existsSync(second.deploy.productPath));
62
+ });
63
+
64
+ test('agentsam go status --json reports discovery', async () => {
65
+ const chunks = [];
66
+ const result = await runGo(['status', '--json'], { write: (v) => chunks.push(String(v)) });
67
+ assert.equal(result.ok, true);
68
+ assert.ok(result.discovery.go.ok);
69
+ const parsed = JSON.parse(chunks.join(''));
70
+ assert.equal(parsed.product, 'agentsam-go-worker');
71
+ });
@@ -0,0 +1,268 @@
1
+ import assert from 'node:assert/strict';
2
+ import fs from 'node:fs';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ import test from 'node:test';
6
+
7
+ import {
8
+ getSecureProviderKey,
9
+ setSecureProviderKey,
10
+ deleteSecureProviderKey,
11
+ listSecureConfiguredProviders,
12
+ hydrateSecureCredentials,
13
+ localVaultPath,
14
+ localVaultSaltPath,
15
+ } from '../../src/security/local-vault.js';
16
+ import { resolveExecutableCommand } from '../../src/security/process.js';
17
+ import {
18
+ resolveProviderCredential,
19
+ setProviderCredential,
20
+ removeProviderCredential,
21
+ exportProviderEnvProfile,
22
+ } from '../../src/lib/provider-credentials.js';
23
+ import {
24
+ validateAndSaveProviderCredential,
25
+ providerChoices,
26
+ runProviders,
27
+ } from '../../src/commands/providers.js';
28
+ import { renderModelsStatus } from '../../src/commands/models.js';
29
+
30
+ // picocolors color-detection differs between local runs and CI runners (CI often
31
+ // reports TTY-like support that enables ANSI output) - strip escape codes before
32
+ // asserting on rendered text content so this test checks content, not incidental
33
+ // terminal color state.
34
+ // eslint-disable-next-line no-control-regex
35
+ const ANSI_PATTERN = /\x1B\[[0-9;]*m/g;
36
+ const stripAnsi = (s) => String(s).replace(ANSI_PATTERN, '');
37
+
38
+ function createTempHome(t) {
39
+ const home = fs.mkdtempSync(path.join(os.tmpdir(), 'agentsam-vault-test-'));
40
+ t.after(() => fs.rmSync(home, { recursive: true, force: true }));
41
+ return home;
42
+ }
43
+
44
+ test('resolveExecutableCommand resolves Windows .cmd shims with shell: true on win32', () => {
45
+ const npxWin = resolveExecutableCommand('npx', 'win32');
46
+ assert.equal(npxWin.command, 'npx.cmd');
47
+ assert.equal(npxWin.shell, true);
48
+
49
+ const npmWin = resolveExecutableCommand('npm', 'win32');
50
+ assert.equal(npmWin.command, 'npm.cmd');
51
+ assert.equal(npmWin.shell, true);
52
+
53
+ const wranglerWin = resolveExecutableCommand('wrangler', 'win32');
54
+ assert.equal(wranglerWin.command, 'wrangler.cmd');
55
+ assert.equal(wranglerWin.shell, true);
56
+
57
+ const scriptWin = resolveExecutableCommand('build.bat', 'win32');
58
+ assert.equal(scriptWin.command, 'build.bat');
59
+ assert.equal(scriptWin.shell, true);
60
+
61
+ // Linux and macOS are untouched
62
+ const npxMac = resolveExecutableCommand('npx', 'darwin');
63
+ assert.equal(npxMac.command, 'npx');
64
+ assert.equal(npxMac.shell, false);
65
+
66
+ const npmLinux = resolveExecutableCommand('npm', 'linux');
67
+ assert.equal(npmLinux.command, 'npm');
68
+ assert.equal(npmLinux.shell, false);
69
+ });
70
+
71
+ test('local vault encrypts with AES-256-GCM and never writes plaintext secrets to disk', (t) => {
72
+ const home = createTempHome(t);
73
+ const opts = { home, disableOsStore: true };
74
+
75
+ const secret = 'sk-proj-testsecret1234567890abcdef';
76
+ setSecureProviderKey('openai', secret, opts);
77
+
78
+ const retrieved = getSecureProviderKey('openai', opts);
79
+ assert.equal(retrieved.value, secret);
80
+ assert.equal(retrieved.source, 'local_vault');
81
+
82
+ // Verify disk contents
83
+ const encFile = localVaultPath(opts);
84
+ assert.equal(fs.existsSync(encFile), true);
85
+ const rawText = fs.readFileSync(encFile, 'utf8');
86
+
87
+ // Plaintext MUST NOT appear anywhere in the file
88
+ assert.equal(rawText.includes(secret), false);
89
+
90
+ // Must have JSON with iv, tag, and data
91
+ const parsed = JSON.parse(rawText);
92
+ assert.ok(parsed.secrets.openai.iv);
93
+ assert.ok(parsed.secrets.openai.tag);
94
+ assert.ok(parsed.secrets.openai.data);
95
+
96
+ // Salt file must exist with restrictive permissions
97
+ const saltFile = localVaultSaltPath(opts);
98
+ assert.equal(fs.existsSync(saltFile), true);
99
+ if (process.platform !== 'win32') {
100
+ assert.equal(fs.statSync(saltFile).mode & 0o777, 0o600);
101
+ assert.equal(fs.statSync(encFile).mode & 0o777, 0o600);
102
+ }
103
+ });
104
+
105
+ test('local vault detects tampering of encrypted ciphertext', (t) => {
106
+ const home = createTempHome(t);
107
+ const opts = { home, disableOsStore: true };
108
+
109
+ setSecureProviderKey('anthropic', 'sk-ant-test-key-12345', opts);
110
+
111
+ // Tamper with the ciphertext
112
+ const encFile = localVaultPath(opts);
113
+ const parsed = JSON.parse(fs.readFileSync(encFile, 'utf8'));
114
+ const originalData = parsed.secrets.anthropic.data;
115
+ // Flip the last hex byte
116
+ const tamperedData = originalData.slice(0, -2) + (originalData.slice(-2) === 'aa' ? 'bb' : 'aa');
117
+ parsed.secrets.anthropic.data = tamperedData;
118
+ fs.writeFileSync(encFile, JSON.stringify(parsed));
119
+
120
+ // Decryption must fail and return null safely rather than corrupt data
121
+ const retrieved = getSecureProviderKey('anthropic', opts);
122
+ assert.equal(retrieved, null);
123
+ });
124
+
125
+ test('local vault stores and retrieves Cloudflare accountId alongside token', (t) => {
126
+ const home = createTempHome(t);
127
+ const opts = { home, disableOsStore: true };
128
+
129
+ setSecureProviderKey('cloudflare', { value: 'cf-token-123', accountId: 'abcdef0123456789abcdef0123456789' }, opts);
130
+ const retrieved = getSecureProviderKey('cloudflare', opts);
131
+
132
+ assert.equal(retrieved.value, 'cf-token-123');
133
+ assert.equal(retrieved.accountId, 'abcdef0123456789abcdef0123456789');
134
+ });
135
+
136
+ test('hydrateSecureCredentials populates process.env for unconfigured providers without overwriting', (t) => {
137
+ const home = createTempHome(t);
138
+ const opts = { home, disableOsStore: true };
139
+
140
+ setSecureProviderKey('gemini', 'gemini-key-val-789', opts);
141
+
142
+ const mockEnv = {
143
+ EXISTING_KEY: 'pre-existing',
144
+ };
145
+
146
+ const hydrated = hydrateSecureCredentials(mockEnv, opts);
147
+ assert.ok(hydrated.includes('gemini'));
148
+ assert.equal(mockEnv.GEMINI_API_KEY, 'gemini-key-val-789');
149
+
150
+ // Second run does not overwrite
151
+ mockEnv.GEMINI_API_KEY = 'user-override';
152
+ hydrateSecureCredentials(mockEnv, opts);
153
+ assert.equal(mockEnv.GEMINI_API_KEY, 'user-override');
154
+ });
155
+
156
+ test('resolveProviderCredential resolves keys from local secure vault across any directory', (t) => {
157
+ const home = createTempHome(t);
158
+ const opts = { home, disableOsStore: true };
159
+
160
+ setProviderCredential('cursor', 'cur-key-secret-456', opts);
161
+
162
+ const resolved = resolveProviderCredential('cursor', { env: {}, home, disableOsStore: true });
163
+ assert.equal(resolved.configured, true);
164
+ assert.equal(resolved.value, 'cur-key-secret-456');
165
+
166
+ // Deleting removes it
167
+ removeProviderCredential('cursor', opts);
168
+ const afterRemove = resolveProviderCredential('cursor', { env: {}, home, disableOsStore: true });
169
+ assert.equal(afterRemove.configured, false);
170
+ });
171
+
172
+ test('validateAndSaveProviderCredential rejects invalid key and does NOT persist it', async (t) => {
173
+ const home = createTempHome(t);
174
+ const opts = {
175
+ home,
176
+ disableOsStore: true,
177
+ fetchImpl: async () => ({
178
+ ok: false,
179
+ status: 401,
180
+ json: async () => ({ error: { message: 'Incorrect API key provided' } }),
181
+ }),
182
+ };
183
+
184
+ const result = await validateAndSaveProviderCredential('openai', 'sk-invalid-test-key', opts);
185
+ assert.equal(result.ok, false);
186
+ assert.equal(result.saved, false);
187
+ assert.match(result.error, /Incorrect API key/i);
188
+
189
+ // Verify it was NOT stored in the vault!
190
+ const retrieved = getSecureProviderKey('openai', opts);
191
+ assert.equal(retrieved, null);
192
+ });
193
+
194
+ test('validateAndSaveProviderCredential verifies valid key and saves ONLY to local vault without plaintext file', async (t) => {
195
+ const home = createTempHome(t);
196
+ const opts = {
197
+ home,
198
+ disableOsStore: true,
199
+ fetchImpl: async () => ({
200
+ ok: true,
201
+ status: 200,
202
+ json: async () => ({
203
+ data: [{ id: 'gpt-4o' }, { id: 'gpt-4o-mini' }],
204
+ }),
205
+ }),
206
+ };
207
+
208
+ const result = await validateAndSaveProviderCredential('openai', 'sk-valid-key-xyz', opts);
209
+ assert.equal(result.ok, true);
210
+ assert.equal(result.saved, true);
211
+ assert.equal(result.model_count, 2);
212
+
213
+ // Verify it was saved to local vault
214
+ const retrieved = getSecureProviderKey('openai', opts);
215
+ assert.equal(retrieved.value, 'sk-valid-key-xyz');
216
+
217
+ // CRITICAL: Plaintext .env profile file MUST NOT be created automatically!
218
+ const plaintextFile = path.join(home, '.agentsam', 'env.d', 'openai.env');
219
+ assert.equal(fs.existsSync(plaintextFile), false, 'Plaintext .env file must NOT exist after secure provider setup');
220
+ });
221
+
222
+ test('exportProviderEnvProfile and agentsam providers export create plaintext .env file ONLY on explicit opt-in', async (t) => {
223
+ const home = createTempHome(t);
224
+ const opts = { home, disableOsStore: true };
225
+
226
+ // Set up provider key in secure vault
227
+ setProviderCredential('anthropic', 'sk-ant-test-export-key', opts);
228
+
229
+ // Confirm NO plaintext file exists yet
230
+ const plaintextFile = path.join(home, '.agentsam', 'env.d', 'anthropic.env');
231
+ assert.equal(fs.existsSync(plaintextFile), false);
232
+
233
+ // Explicit opt-in export via CLI command
234
+ let output = '';
235
+ const exported = await runProviders(['export', 'anthropic'], {
236
+ ...opts,
237
+ write: (text) => { output += text; },
238
+ });
239
+
240
+ assert.equal(exported.provider, 'anthropic');
241
+ assert.equal(fs.existsSync(plaintextFile), true);
242
+ const content = fs.readFileSync(plaintextFile, 'utf8');
243
+ assert.match(content, /ANTHROPIC_API_KEY="sk-ant-test-export-key"/);
244
+ assert.match(output, /Exported anthropic profile/);
245
+ });
246
+
247
+ test('renderModelsStatus shows tip: Select your preferred provider when providers are unconfigured', () => {
248
+ const status = {
249
+ providers: [
250
+ { id: 'openai', label: 'OpenAI', configured: false, credential: 'OPENAI_API_KEY' },
251
+ { id: 'anthropic', label: 'Anthropic', configured: false, credential: 'ANTHROPIC_API_KEY' },
252
+ ],
253
+ discovery: {},
254
+ providerModels: {},
255
+ local: { online: false, models: [] },
256
+ };
257
+
258
+ const output = stripAnsi(renderModelsStatus(status));
259
+ assert.match(output, /Tip:\s*Select your preferred provider/i);
260
+ });
261
+
262
+ test('providerChoices returns valid choice list for select picker', () => {
263
+ const choices = providerChoices();
264
+ assert.ok(choices.length >= 6);
265
+ assert.ok(choices.some((c) => c.value === 'openai'));
266
+ assert.ok(choices.some((c) => c.value === 'anthropic'));
267
+ assert.ok(choices.some((c) => c.value === 'gemini'));
268
+ });