@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
@@ -13,25 +13,33 @@ import { runModels } from './models.js';
13
13
  import { runProviders } from './providers.js';
14
14
  import { configureCliPreferences } from './preferences.js';
15
15
  import { runCloudflare } from './cloudflare.js';
16
+ import { runConnections } from './connections.js';
17
+ import { runTunnel } from './tunnel.js';
16
18
  import { probeOllamaModel, resolveOllamaConfig } from './ollama.js';
17
19
  import { createInlineActivity } from '../ui/cli/activity.js';
18
20
  import { createCliRuntimePresenter } from '../ui/cli/runtime-events.js';
19
- import { renderCliFooter } from '../ui/cli/footer.js';
21
+ import { renderCliFooter, renderDiffPreview, renderUsagePanel } from '../ui/cli/footer.js';
20
22
  import { diagnosticFromError, renderDiagnosticError } from '../errors/index.js';
21
- import { getModelRecord } from '../models/index.js';
23
+ import { getModelRecord, mergeModelReference } from '../models/index.js';
22
24
  import { discoverProviderModels } from '../models/discovery.js';
23
- import { readCliPreferences, updateCliPreferences } from '../lib/cli-preferences.js';
25
+ import { detectCliProject, findCliProjectRoot, readCliPreferences, updateCliPreferences } from '../lib/cli-preferences.js';
24
26
  import { buildContextEconomicsReport, renderContextEconomics } from './context-economics.js';
25
27
  import { createProviderAdapter } from '../providers/index.js';
26
28
  import { createCapabilityAdapter, runResponsesAgent } from '../agent/index.js';
27
29
  import { resolveProviderCredential } from '../lib/provider-credentials.js';
28
- import { createLocalSession, saveLocalSession, sessionTitleFromInput, localSessionElapsedMs } from '../lib/local-sessions.js';
30
+ import { createLocalSession, saveLocalSession, localSessionElapsedMs, loadSessionContinuation } from '../lib/local-sessions.js';
31
+ import { runtimeDatabasePath } from '../local/runtime-store.js';
29
32
  import { grantExecutionApproval, isExecutionApproved, toolApprovalKey } from '../lib/execution-approvals.js';
30
33
  import { runWhoami } from './whoami.js';
31
34
  import { runLogin, runLogout } from './account-auth.js';
32
35
  import { runHelp } from '../ui/cli/help.js';
36
+ import { hydrateSecureCredentials } from '../security/local-vault.js';
33
37
  import { readAccountSession } from '../lib/account-session.js';
34
38
  import { startRuntimeRun, finishRuntimeRun, recordRuntimeCompaction } from '../local/runtime-store.js';
39
+ import { tryResolveGitContext } from '../../packages/agentsam-repository/src/git-context.js';
40
+ import { syncWorkspaceStateToD1, readWorkspaceStateFromD1 } from '../../packages/agentsam-repository/src/workspace-state.js';
41
+ import { syncGitCommitsToD1 } from '../../packages/agentsam-repository/src/work-tracking.js';
42
+ import { readGoapState, listGoapTickets, createGoapGoal, switchGoapGoal, closeGoapGoal, renderGoapStatus, renderGoapList, renderGoapGoal, renderGoapWhy, renderGoapPlan } from '../../packages/agentsam-repository/src/goap.js';
35
43
 
36
44
  function writeLine(write, value = '') { write(`${value}\n`); }
37
45
 
@@ -54,6 +62,134 @@ export function renderShellPrompt(cwd, env = process.env) {
54
62
  return `${shellUsername(env)} ${compactCwd(cwd, env.HOME || env.USERPROFILE || '')} > `;
55
63
  }
56
64
 
65
+ export const PASTE_COLLAPSE_LINE_THRESHOLD = 5;
66
+ export const PASTE_COLLAPSE_CHAR_THRESHOLD = 300;
67
+ const BRACKETED_PASTE_START = '\x1b[200~';
68
+ const BRACKETED_PASTE_END = '\x1b[201~';
69
+ const BRACKETED_PASTE_ENABLE = '\x1b[?2004h';
70
+ const BRACKETED_PASTE_DISABLE = '\x1b[?2004l';
71
+
72
+ export function countPasteLines(text) {
73
+ const source = String(text ?? '').replace(/\r\n/g, '\n').replace(/\r/g, '\n');
74
+ if (!source) return 0;
75
+ const parts = source.split('\n');
76
+ if (parts[parts.length - 1] === '') parts.pop();
77
+ return Math.max(parts.length, source ? 1 : 0);
78
+ }
79
+
80
+ export function shouldCollapsePaste(text) {
81
+ const source = String(text ?? '');
82
+ return countPasteLines(source) > PASTE_COLLAPSE_LINE_THRESHOLD || source.length > PASTE_COLLAPSE_CHAR_THRESHOLD;
83
+ }
84
+
85
+ export function renderCollapsedPaste(text) {
86
+ return `[Pasted ${countPasteLines(text)} lines — Enter to run, Backspace to clear]`;
87
+ }
88
+
89
+ export function installPasteCollapse(rl, options = {}) {
90
+ if (!rl || rl.terminal === false || typeof rl._ttyWrite !== 'function') return () => {};
91
+ const original = rl._ttyWrite.bind(rl);
92
+ let collapsed = null;
93
+ let collecting = '';
94
+ let inPaste = false;
95
+
96
+ function refresh(line) {
97
+ rl.line = line;
98
+ rl.cursor = line.length;
99
+ if (typeof rl._refreshLine === 'function') rl._refreshLine();
100
+ }
101
+
102
+ function showCollapsed(text) {
103
+ collapsed = text;
104
+ refresh(renderCollapsedPaste(text));
105
+ }
106
+
107
+ function consumePaste(body) {
108
+ inPaste = false;
109
+ collecting = '';
110
+ if (shouldCollapsePaste(body)) {
111
+ showCollapsed(body);
112
+ return true;
113
+ }
114
+ original(body);
115
+ return true;
116
+ }
117
+
118
+ rl._ttyWrite = (s, key) => {
119
+ const str = s == null ? '' : String(s);
120
+
121
+ if (str.includes(BRACKETED_PASTE_START) || key?.name === 'paste-start') {
122
+ inPaste = true;
123
+ collecting = str.replace(BRACKETED_PASTE_START, '');
124
+ if (collecting.includes(BRACKETED_PASTE_END) || key?.name === 'paste-end') {
125
+ return consumePaste(collecting.replace(BRACKETED_PASTE_END, ''));
126
+ }
127
+ return undefined;
128
+ }
129
+ if (inPaste) {
130
+ collecting += str;
131
+ if (collecting.includes(BRACKETED_PASTE_END) || key?.name === 'paste-end') {
132
+ return consumePaste(collecting.replace(BRACKETED_PASTE_END, ''));
133
+ }
134
+ return undefined;
135
+ }
136
+
137
+ if (collapsed) {
138
+ if (key?.name === 'backspace' || key?.name === 'delete') {
139
+ collapsed = null;
140
+ refresh('');
141
+ return undefined;
142
+ }
143
+ if (key?.name === 'return' || key?.name === 'enter') {
144
+ const text = collapsed;
145
+ collapsed = null;
146
+ rl.line = text;
147
+ rl.cursor = text.length;
148
+ return original('\n', { name: 'return' });
149
+ }
150
+ }
151
+
152
+ if (str && !key?.name && shouldCollapsePaste(str.replace(/\n$/, ''))) {
153
+ const payload = str.endsWith('\n') ? str.slice(0, -1) : str;
154
+ showCollapsed(payload);
155
+ return undefined;
156
+ }
157
+
158
+ return original(s, key);
159
+ };
160
+
161
+ try { options.output?.write?.(BRACKETED_PASTE_ENABLE); } catch { /* ignore */ }
162
+ return () => {
163
+ rl._ttyWrite = original;
164
+ try { options.output?.write?.(BRACKETED_PASTE_DISABLE); } catch { /* ignore */ }
165
+ };
166
+ }
167
+
168
+ function resolveShellIdentity(state) {
169
+ if (state.identity && state.identityCwd === state.cwd) return state.identity;
170
+ try { state.identity = detectCliProject(state.cwd); }
171
+ catch { state.identity = { project: path.basename(state.cwd || ''), branch: '', root: state.cwd }; }
172
+ state.identityCwd = state.cwd;
173
+ return state.identity;
174
+ }
175
+
176
+ function renderShellSessionFooter(state) {
177
+ const identity = resolveShellIdentity(state);
178
+ const preferences = readCliPreferences(state.cwd) || {};
179
+ return renderCliFooter({
180
+ projectName: identity.project,
181
+ branch: identity.branch,
182
+ cwd: compactCwd(state.cwd),
183
+ model: state.session?.provider_model_id || state.session?.model_key || preferences.modelPreference,
184
+ provider: state.providerState?.provider || state.session?.provider_state?.provider,
185
+ effort: state.session?.reasoning_effort || preferences.reasoningEffort,
186
+ tier: state.session?.actual_service_tier || state.session?.requested_service_tier || preferences.serviceTier,
187
+ usageSnapshot: state.usageSnapshot || state.session?.usage_snapshot,
188
+ elapsedMs: state.lastTurnElapsedMs ?? (state.session ? localSessionElapsedMs(state.session) : undefined),
189
+ action: state.activity?.active ? 'working' : '',
190
+ });
191
+ }
192
+
57
193
  export function tokenizeShellLine(input = '') {
58
194
  const source = String(input);
59
195
  const tokens = [];
@@ -121,9 +257,12 @@ function spawnGit(cwd, args) {
121
257
  function selectedModel(cwd) {
122
258
  const preferences = readCliPreferences(cwd) || {};
123
259
  const snapshot = preferences.modelSnapshot?.model_key === preferences.modelPreference
124
- ? preferences.modelSnapshot
260
+ ? mergeModelReference(preferences.modelSnapshot)
125
261
  : null;
126
- const model = snapshot || getModelRecord(preferences.modelPreference);
262
+ let model = snapshot || getModelRecord(preferences.modelPreference);
263
+ if (!model && (preferences.modelPreference === 'auto' || !preferences.modelPreference)) {
264
+ model = mergeModelReference(preferences.modelSnapshot) || listModelCatalog()[0];
265
+ }
127
266
  if (!model) throw new Error('Select an exact provider-verified model with /model first so Agent Sam can verify supported runtime controls.');
128
267
  return { preferences, model };
129
268
  }
@@ -169,24 +308,32 @@ async function resolveModelForTurn(cwd, state) {
169
308
 
170
309
  async function chooseReasoning(cwd, args, state) {
171
310
  const { preferences, model } = selectedModel(cwd);
311
+ const supported = ['auto', ...model.reasoning_efforts.filter((value) => value !== 'auto')];
172
312
  let effort = String(args[0] || '').trim().toLowerCase();
173
313
  if (!effort) {
174
314
  if (!state.interactive) {
175
- writeLine(state.write, ` ${model.provider_model_id} reasoning: ${model.reasoning_efforts.join(' | ')}`);
176
- writeLine(state.write, ` current: ${preferences.reasoningEffort || 'auto'}`);
315
+ writeLine(state.write, ' ' + model.provider_model_id + ' reasoning: ' + supported.join(' | '));
316
+ writeLine(state.write, ' provider default: ' + (model.default_reasoning_effort || 'unknown'));
317
+ writeLine(state.write, ' current: ' + (preferences.reasoningEffort || 'auto'));
177
318
  return;
178
319
  }
179
320
  const choice = await select({
180
- message: `Reasoning level · ${model.provider_model_id}`,
181
- initialValue: model.reasoning_efforts.includes(preferences.reasoningEffort) ? preferences.reasoningEffort : model.reasoning_efforts[0],
182
- options: model.reasoning_efforts.map((value) => ({ value, label: value === 'xhigh' ? 'Extra high' : value === 'max' ? 'Max' : value[0].toUpperCase() + value.slice(1) })),
321
+ message: 'Reasoning level · ' + model.provider_model_id,
322
+ initialValue: supported.includes(preferences.reasoningEffort) ? preferences.reasoningEffort : 'auto',
323
+ options: supported.map((value) => ({
324
+ value,
325
+ label: value === 'auto' ? 'Automatic' : value === 'xhigh' ? 'Extra high' : value === 'max' ? 'Max' : value[0].toUpperCase() + value.slice(1),
326
+ hint: value === 'auto'
327
+ ? 'omit reasoning.effort · provider default ' + (model.default_reasoning_effort || 'unknown')
328
+ : value === model.default_reasoning_effort ? 'provider default' : undefined,
329
+ })),
183
330
  });
184
331
  if (isCancel(choice)) return;
185
332
  effort = choice;
186
333
  }
187
- if (!model.reasoning_efforts.includes(effort)) throw new Error(`unsupported reasoning level for ${model.provider_model_id}: ${effort}`);
334
+ if (!supported.includes(effort)) throw new Error('unsupported reasoning level for ' + model.provider_model_id + ': ' + effort);
188
335
  updateCliPreferences(cwd, { reasoningEffort: effort });
189
- writeLine(state.write, ` reasoning → ${effort}`);
336
+ writeLine(state.write, ' reasoning → ' + effort);
190
337
  }
191
338
 
192
339
  function setServiceTier(cwd, tier, write) {
@@ -241,15 +388,15 @@ export async function runLocalAgent(goal, write, options = {}) {
241
388
  }
242
389
 
243
390
  async function showLocalLogs(cwd, write) {
244
- const dbPath = path.join(cwd, '.agentsam', 'data', 'agentsam.sqlite');
245
- if (!fs.existsSync(dbPath)) { writeLine(write, ' No local Agent Sam DB found. Run `agentsam init . --yes` first.'); return; }
391
+ const dbPath = runtimeDatabasePath(cwd);
392
+ if (!fs.existsSync(dbPath)) { writeLine(write, ' No local Agent Sam runs yet.'); return; }
246
393
  const { createLocalSqliteDatabase } = await import('../local/sqlite.js');
247
394
  const db = await createLocalSqliteDatabase(dbPath);
248
395
  try {
249
- const calls = await db.prepare('SELECT id, session_id, tool_name, status, created_at, completed_at FROM agent_tool_calls ORDER BY created_at DESC LIMIT 20').all();
250
- if (!calls.results.length) { writeLine(write, ' No local Agent Sam tool-call events yet.'); return; }
251
- writeLine(write, '\n Recent Agent Sam tool calls');
252
- for (const row of calls.results) writeLine(write, ` ${String(row.created_at || '').padEnd(20)} ${String(row.status || '').padEnd(10)} ${row.tool_name}`);
396
+ const runs = await db.prepare('SELECT id, model_key, status, started_at_unix FROM agentsam_agent_run ORDER BY started_at_unix DESC LIMIT 20').all();
397
+ if (!runs.results.length) { writeLine(write, ' No local Agent Sam runs yet.'); return; }
398
+ writeLine(write, '\n Recent Agent Sam runs');
399
+ for (const row of runs.results) writeLine(write, ` ${new Date(Number(row.started_at_unix) * 1000).toISOString()} ${String(row.status || '').padEnd(10)} ${row.model_key || 'model unknown'} · ${row.id}`);
253
400
  writeLine(write, '');
254
401
  } finally { db.close(); }
255
402
  }
@@ -300,6 +447,7 @@ export function renderSessionReceipt(session) {
300
447
  if (componentTotal > 0) {
301
448
  lines.push(`Cost breakdown: input ${formatUsd(breakdown.input)} · cached ${formatUsd(breakdown.cached_input)} · cache write ${formatUsd(breakdown.cache_write)} · output ${formatUsd(breakdown.output)}`);
302
449
  }
450
+ if (session.latest_compaction) lines.push(`Latest compaction: ${session.latest_compaction.compaction_id || 'completed'} · ${formatUsd(session.latest_compaction.cost_usd)}`);
303
451
  if (active) lines.push(`Active context: ${formatCount(active)} tokens`);
304
452
  lines.push('', 'To continue this session, run:', ` agentsam resume ${session.id}`, '', 'Or run:', ' agentsam resume', '', 'and select:', ` ${session.title || 'this session'}`, '');
305
453
  return lines.join('\n');
@@ -307,7 +455,7 @@ export function renderSessionReceipt(session) {
307
455
 
308
456
  function persistSession(state, patch = {}) {
309
457
  if (!state.session) return null;
310
- state.session = saveLocalSession({ ...state.session, ...patch }, { home: state.home });
458
+ state.session = saveLocalSession({ ...state.session, ...patch }, { home: state.home, projectRoot: state.projectRoot });
311
459
  return state.session;
312
460
  }
313
461
 
@@ -318,8 +466,6 @@ function recordSessionInput(state, input) {
318
466
  if (housekeeping.has(value.toLowerCase())) return;
319
467
  persistSession(state, {
320
468
  status: 'active',
321
- last_input: value,
322
- title: sessionTitleFromInput(value),
323
469
  });
324
470
  }
325
471
 
@@ -392,6 +538,55 @@ async function approveToolExecution(request, state) {
392
538
  return true;
393
539
  }
394
540
 
541
+ async function runInteractiveCompaction(args, state) {
542
+ if (args.length > 1 || (args.length && args[0] !== 'status')) throw new Error('Usage: /compact [status]');
543
+ const { model } = selectedModel(state.cwd);
544
+ const supported = ['openai', 'grok'].includes(model.provider) && model.capabilities?.compaction === true;
545
+ const report = buildContextEconomicsReport(state.cwd, { activeInputTokens: state.usageSnapshot?.current_context?.input_tokens ?? state.session?.usage_snapshot?.current_context?.input_tokens });
546
+ if (args[0] === 'status') {
547
+ writeLine(state.write, ` Compaction · ${model.provider}/${model.provider_model_id}`);
548
+ writeLine(state.write, ` support ${supported ? 'native' : 'unavailable'}`);
549
+ writeLine(state.write, ` active ${report.active_input_tokens ?? 'unknown'} tokens`);
550
+ writeLine(state.write, ` threshold ${report.compact_at_tokens ?? 'unknown'} tokens`);
551
+ writeLine(state.write, ` automatic ${supported && state.session?.auto_compact !== false ? 'enabled' : 'disabled'}`);
552
+ const receipt = state.session?.latest_compaction;
553
+ writeLine(state.write, ` latest ${receipt ? `${receipt.compaction_id} · ${receipt.created_at} · ${formatUsd(receipt.cost_usd)}` : 'none'}`);
554
+ return;
555
+ }
556
+ if (!supported) throw new Error(`native_compaction_unavailable:${model.provider}`);
557
+ if (!state.session || state.session.model_key !== model.model_key) throw new Error('compaction_active_model_session_required');
558
+ const previous = state.providerState || loadSessionContinuation(state.session);
559
+ if (!previous?.previous_response_id && !previous?.compacted_input?.length && !previous?.compaction_input?.length) throw new Error('compaction_context_unavailable');
560
+ const credential = resolveProviderCredential(model.provider, { home: state.home });
561
+ const provider = createProviderAdapter({ modelRecord: model, credential, fetchImpl: state.providerFetchImpl });
562
+ if (typeof provider.compact !== 'function') throw new Error(`native_compaction_unavailable:${model.provider}`);
563
+ const result = await provider.compact({
564
+ model: model.provider_model_id, modelRecord: model, providerState: previous,
565
+ promptCacheKey: state.session.id,
566
+ tokensBefore: report.active_input_tokens, cumulativeUsage: state.session.cumulative_usage,
567
+ });
568
+ if (!Array.isArray(result.output) || !result.output.length) throw new Error('provider_compaction_output_missing');
569
+ const receipt = checkpointCompaction(state, model, result);
570
+ writeLine(state.write, ` Compacted · ${receipt.compaction_id} · ${formatUsd(receipt.cost_usd)} · continuation saved`);
571
+ }
572
+
573
+ function checkpointCompaction(state, model, result) {
574
+ const delta = result.usage_delta || result.usage || {};
575
+ const cumulative = Object.fromEntries(['input_tokens', 'output_tokens', 'cached_input_tokens', 'cache_write_tokens', 'reasoning_tokens'].map(key => [key, Number(state.session.cumulative_usage?.[key] || 0) + Number(delta[key] || 0)]));
576
+ const providerState = { provider: model.provider, ...(result.provider_state || {}), previous_response_id: null, compacted_input: result.output };
577
+ const usageSnapshot = { current_context: { input_tokens: Math.ceil(JSON.stringify(result.output).length / 4), window_tokens: model.context_window }, cumulative, estimate_kind: 'local' };
578
+ const receipt = { compaction_id: result.compaction_id, provider: model.provider, model: model.provider_model_id, created_at: new Date().toISOString(), usage: delta, cost_usd: result.cost?.total_usd || 0 };
579
+ persistSession(state, {
580
+ provider_state: providerState, usage_snapshot: usageSnapshot, cumulative_usage: cumulative,
581
+ latest_compaction: receipt,
582
+ total_cost_usd: Number(state.session.total_cost_usd || 0) + Number(result.cost?.total_usd || 0),
583
+ cost_breakdown_usd: Object.fromEntries(['input', 'cached_input', 'cache_write', 'output'].map(key => [key, Number(state.session.cost_breakdown_usd?.[key] || 0) + Number(result.cost?.components_usd?.[key] || 0)])),
584
+ });
585
+ state.providerState = providerState;
586
+ state.usageSnapshot = usageSnapshot;
587
+ return receipt;
588
+ }
589
+
395
590
  async function runInteractiveModelTurn(prompt, state) {
396
591
  const resolved = await resolveModelForTurn(state.cwd, state);
397
592
  const { preferences, model, credential } = resolved;
@@ -403,17 +598,15 @@ async function runInteractiveModelTurn(prompt, state) {
403
598
  });
404
599
  const capabilityAdapter = createCapabilityAdapter();
405
600
 
406
- const samePolicy = state.session
407
- && state.session.model_key === model.model_key
408
- && state.session.reasoning_effort === preferences.reasoningEffort
409
- && state.session.requested_service_tier === preferences.serviceTier;
410
- const previousProviderState = samePolicy ? state.session?.provider_state : null;
601
+ // Reasoning/tier changes do not change the provider conversation's owner.
602
+ const samePolicy = state.session && state.session.model_key === model.model_key;
603
+ const previousProviderState = samePolicy ? (state.providerState || loadSessionContinuation(state.session)) : null;
411
604
  const previousUsageSnapshot = samePolicy ? state.session?.usage_snapshot : null;
412
605
  const accountId = readAccountSession({ home: state.home })?.account_id || null;
413
606
  let runtimeRunId = null;
414
607
  try {
415
608
  runtimeRunId = await startRuntimeRun({
416
- cwd: state.cwd,
609
+ projectRoot: state.projectRoot,
417
610
  account_id: accountId,
418
611
  mode: 'agent',
419
612
  model_key: model.model_key,
@@ -431,6 +624,8 @@ async function runInteractiveModelTurn(prompt, state) {
431
624
  });
432
625
  state.activity = activity;
433
626
  const presenter = createCliRuntimePresenter({ activity, write: state.write, state });
627
+ const startingCostUsd = Number(state.session?.total_cost_usd || 0);
628
+ const startingCostBreakdown = { ...state.session?.cost_breakdown_usd };
434
629
  const startedAt = Date.now();
435
630
  activity.start(`Thinking · ${model.provider_model_id}`);
436
631
 
@@ -445,9 +640,17 @@ async function runInteractiveModelTurn(prompt, state) {
445
640
  modelRecord: model,
446
641
  reasoningEffort: preferences.reasoningEffort,
447
642
  serviceTier: preferences.serviceTier,
643
+ // The interactive CLI turn is the main agentic engineering loop, not a
644
+ // one-shot lookup -- the previous unconfigurable default (quick: 8
645
+ // rounds) killed genuine multi-step work for no reason tied to risk,
646
+ // cost, or progress. `agent` (128 rounds) is the right default here;
647
+ // preferences.runMode leaves room for a future explicit override.
648
+ runMode: preferences.runMode || 'agent',
448
649
  previousProviderState,
449
650
  previousUsageSnapshot,
450
651
  cumulativeUsage: state.session?.cumulative_usage || null,
652
+ autoCompact: state.session?.auto_compact !== false,
653
+ onCompaction: result => { if (state.session) checkpointCompaction(state, model, result); },
451
654
  promptCacheKey: state.session?.id || undefined,
452
655
  runId: runtimeRunId || state.session?.id || undefined,
453
656
  beforeRequest: (preflight) => approveModelRequest(preflight, state),
@@ -463,7 +666,7 @@ async function runInteractiveModelTurn(prompt, state) {
463
666
  if (runtimeRunId) {
464
667
  try {
465
668
  await finishRuntimeRun({
466
- cwd: state.cwd,
669
+ projectRoot: state.projectRoot,
467
670
  id: runtimeRunId,
468
671
  status: 'failed',
469
672
  error_code: error?.code || 'interactive_error',
@@ -488,10 +691,12 @@ async function runInteractiveModelTurn(prompt, state) {
488
691
  }
489
692
 
490
693
  state.usageSnapshot = result.usage_snapshot;
694
+ state.providerState = result.provider_state || null;
695
+ state.lastTurnElapsedMs = Date.now() - startedAt;
491
696
  if (runtimeRunId) {
492
697
  try {
493
698
  await finishRuntimeRun({
494
- cwd: state.cwd,
699
+ projectRoot: state.projectRoot,
495
700
  id: runtimeRunId,
496
701
  status: 'completed',
497
702
  actual_service_tier: result.actual_service_tier,
@@ -501,7 +706,7 @@ async function runInteractiveModelTurn(prompt, state) {
501
706
  });
502
707
  if (presenter.state.lastCompaction) {
503
708
  await recordRuntimeCompaction({
504
- cwd: state.cwd,
709
+ projectRoot: state.projectRoot,
505
710
  account_id: accountId,
506
711
  agent_run_id: runtimeRunId,
507
712
  session_id: state.session?.id,
@@ -527,30 +732,66 @@ async function runInteractiveModelTurn(prompt, state) {
527
732
  provider_state: { provider: model.provider, ...(result.provider_state || {}) },
528
733
  usage_snapshot: result.usage_snapshot,
529
734
  cumulative_usage: result.cumulative_usage,
530
- total_cost_usd: Number(state.session.total_cost_usd || 0) + Number(result.total_cost_usd || 0),
735
+ latest_compaction: result.compaction || state.session.latest_compaction,
736
+ total_cost_usd: startingCostUsd + Number(result.total_cost_usd || 0),
531
737
  cost_breakdown_usd: Object.fromEntries(['input', 'cached_input', 'cache_write', 'output'].map((key) => [
532
- key, Number(state.session.cost_breakdown_usd?.[key] || 0) + Number(result.cost_breakdown_usd?.[key] || 0),
738
+ key, Number(startingCostBreakdown[key] || 0) + Number(result.cost_breakdown_usd?.[key] || 0),
533
739
  ])),
534
740
  last_error: null,
535
741
  });
536
742
  }
537
743
 
744
+ const identity = resolveShellIdentity(state);
538
745
  writeLine(state.write, renderCliFooter({
746
+ projectName: identity.project,
747
+ branch: identity.branch,
748
+ cwd: compactCwd(state.cwd),
539
749
  model: result.model || model.provider_model_id,
750
+ provider: model.provider,
751
+ effort: result.reasoning_effort,
540
752
  usageSnapshot: result.usage_snapshot,
541
753
  tier: result.actual_service_tier,
542
- elapsedMs: Date.now() - startedAt,
754
+ elapsedMs: state.lastTurnElapsedMs,
543
755
  }));
544
756
  writeLine(state.write, '');
757
+ state.wroteTurnFooter = true;
545
758
  return result;
546
759
  }
547
760
 
548
761
  export async function dispatchShellLine(line, state = {}) {
549
- const tokens = tokenizeShellLine(line);
762
+ let tokens = tokenizeShellLine(line);
550
763
  const write = state.write || ((text) => process.stdout.write(text));
551
764
  state.write = write;
552
765
  state.cwd = path.resolve(state.cwd || process.cwd());
766
+ state.projectRoot ||= findCliProjectRoot(state.cwd);
553
767
  if (!tokens.length) return { handled: true, exit: false, cwd: state.cwd };
768
+
769
+ // Strip a leading "agentsam" prefix if present -- it should never change
770
+ // whether the rest of the line resolves, only whether it was typed at all.
771
+ if (tokens[0].toLowerCase() === 'agentsam') {
772
+ tokens = tokens.slice(1);
773
+ if (!tokens.length) tokens = ['help'];
774
+ }
775
+
776
+ // Recognized short verbs (with or without a stripped "agentsam" prefix)
777
+ // become their slash command. Anything unrecognized -- typo, a host-CLI-only
778
+ // verb like "merkle" typed here, whatever -- is left as plain text and
779
+ // falls through to the natural-language model turn below. There is no
780
+ // path left that hard-errors on an unrecognized word; "unknown command"
781
+ // only fires for input that explicitly starts with "/".
782
+ if (!tokens[0].startsWith('/')) {
783
+ const bare = tokens[0].toLowerCase();
784
+ const commonVerbs = [
785
+ 'help', 'exit', 'quit', 'clear', 'status', 'models', 'model',
786
+ 'whoami', 'cf', 'cloudflare', 'db', 'tunnel', 'connections',
787
+ 'connect', 'usage', 'session', 'providers', 'settings', 'logs',
788
+ 'git', 'diff', 'pwd', 'cd', 'fast', 'flex', 'standard', 'reasoning',
789
+ ];
790
+ if (commonVerbs.includes(bare)) {
791
+ tokens[0] = `/${bare}`;
792
+ }
793
+ }
794
+
554
795
  const [command, ...args] = tokens;
555
796
  try {
556
797
  switch (command.toLowerCase()) {
@@ -565,8 +806,15 @@ export async function dispatchShellLine(line, state = {}) {
565
806
  case '/quit':
566
807
  return { handled: true, exit: true, cwd: state.cwd };
567
808
  case '/model': {
568
- const configured = await configureCliPreferences({ cwd: state.cwd, firstRun: false, section: 'model', home: state.home });
569
- state.cwd = configured.identity.root;
809
+ state.rl?.pause?.();
810
+ try {
811
+ const configured = await configureCliPreferences({ cwd: state.cwd, firstRun: false, section: 'model', home: state.home });
812
+ state.cwd = configured.identity.root;
813
+ state.projectRoot = findCliProjectRoot(state.cwd);
814
+ } finally {
815
+ if (process.stdin.isPaused()) process.stdin.resume();
816
+ state.rl?.resume?.();
817
+ }
570
818
  break;
571
819
  }
572
820
  case '/reasoning':
@@ -581,21 +829,37 @@ export async function dispatchShellLine(line, state = {}) {
581
829
  case '/standard':
582
830
  setServiceTier(state.cwd, 'default', write);
583
831
  break;
832
+ case '/compact':
833
+ await runInteractiveCompaction(args, state);
834
+ break;
584
835
  case '/context':
585
836
  if (args[0] === 'repo' || args[0] === 'git') await runContext(['--cwd', state.cwd, ...args.slice(1)]);
586
837
  else write(renderContextEconomics(buildContextEconomicsReport(state.cwd, {
587
838
  activeInputTokens: state.usageSnapshot?.current_context?.input_tokens,
588
839
  estimateKind: state.usageSnapshot?.estimate_kind,
840
+ autoCompact: state.session?.auto_compact,
589
841
  })));
590
842
  break;
591
843
  case '/status':
592
844
  await runStatus(args, { cwd: state.cwd });
593
845
  break;
594
846
  case '/models':
595
- await runModels(args, { cwd: state.cwd, write, home: state.home });
847
+ state.rl?.pause?.();
848
+ try {
849
+ await runModels(args, { cwd: state.cwd, write, home: state.home, interactive: state.interactive });
850
+ } finally {
851
+ if (process.stdin.isPaused()) process.stdin.resume();
852
+ state.rl?.resume?.();
853
+ }
596
854
  break;
597
855
  case '/providers':
598
- await runProviders(args, { write, home: state.home, interactive: state.interactive });
856
+ state.rl?.pause?.();
857
+ try {
858
+ await runProviders(args, { write, home: state.home, interactive: state.interactive });
859
+ } finally {
860
+ if (process.stdin.isPaused()) process.stdin.resume();
861
+ state.rl?.resume?.();
862
+ }
599
863
  break;
600
864
  case '/login':
601
865
  await runLogin(args, { write, home: state.home });
@@ -608,17 +872,43 @@ export async function dispatchShellLine(line, state = {}) {
608
872
  await runWhoami(args, { write, home: state.home });
609
873
  break;
610
874
  case '/session':
611
- case '/usage':
612
875
  if (state.session) write(renderSessionReceipt(state.session));
613
876
  else writeLine(write, ' No persistent session is active in this shell invocation.');
614
877
  break;
878
+ case '/usage':
879
+ write(renderUsagePanel({
880
+ session: state.session,
881
+ usage: state.usageSnapshot || state.session?.usage_snapshot,
882
+ provider: state.providerState?.provider || state.session?.provider,
883
+ model: state.providerState?.model || state.session?.model,
884
+ effort: state.providerState?.reasoning_effort || state.session?.reasoning_effort,
885
+ tier: state.providerState?.service_tier || state.session?.service_tier,
886
+ estimateKind: state.usageSnapshot?.estimate_kind,
887
+ elapsedMs: state.session ? localSessionElapsedMs(state.session) : null,
888
+ cost: state.usageSnapshot?.cumulative?.cost_usd ?? state.session?.cumulative_usage?.cost_usd,
889
+ }));
890
+ break;
615
891
  case '/cf':
616
892
  case '/cloudflare':
617
893
  await runCloudflare(args.length ? args : ['commands'], { cwd: state.cwd, write });
618
894
  break;
895
+ case '/connections':
896
+ case '/connect':
897
+ await runConnections(args, { cwd: state.cwd, write, home: state.home });
898
+ break;
899
+ case '/tunnel':
900
+ await runTunnel(args, { cwd: state.cwd, write, home: state.home, interactive: state.interactive });
901
+ break;
619
902
  case '/settings': {
620
- const configured = await configureCliPreferences({ cwd: state.cwd, firstRun: false, home: state.home });
621
- state.cwd = configured.identity.root;
903
+ state.rl?.pause?.();
904
+ try {
905
+ const configured = await configureCliPreferences({ cwd: state.cwd, firstRun: false, home: state.home });
906
+ state.cwd = configured.identity.root;
907
+ state.projectRoot = findCliProjectRoot(state.cwd);
908
+ } finally {
909
+ if (process.stdin.isPaused()) process.stdin.resume();
910
+ state.rl?.resume?.();
911
+ }
622
912
  break;
623
913
  }
624
914
  case '/pwd':
@@ -628,22 +918,100 @@ export async function dispatchShellLine(line, state = {}) {
628
918
  const destination = args.length ? args.join(' ') : process.env.HOME || process.env.USERPROFILE || state.cwd;
629
919
  const next = path.resolve(state.cwd, destination);
630
920
  if (!fs.existsSync(next) || !fs.statSync(next).isDirectory()) throw new Error(`directory not found: ${next}`);
921
+ const nextRoot = findCliProjectRoot(next);
922
+ if (nextRoot !== (state.projectRoot || findCliProjectRoot(state.cwd))) {
923
+ if (state.session) {
924
+ persistSession(state, { status: 'paused', active_elapsed_ms: localSessionElapsedMs(state.session), active_started_at: null });
925
+ state.session = createLocalSession({ cwd: next, project_root: nextRoot }, { projectRoot: nextRoot, home: state.home });
926
+ }
927
+ state.projectRoot = nextRoot;
928
+ state.usageSnapshot = null;
929
+ state.providerState = null;
930
+ }
631
931
  state.cwd = next;
932
+ state.identity = null;
933
+ state.identityCwd = null;
632
934
  writeLine(write, state.cwd);
633
935
  break;
634
936
  }
635
937
  case '/git':
636
938
  spawnGit(state.cwd, args.length ? args : ['status', '--short', '--branch']);
637
939
  break;
638
- case '/diff':
639
- spawnGit(state.cwd, ['diff', ...args]);
940
+ case '/diff': {
941
+ const result = spawnSync('git', ['diff', '--no-color', ...args], { cwd: state.cwd, encoding: 'utf8', shell: false });
942
+ if (result.error) throw result.error;
943
+ if (result.status !== 0 && !String(result.stdout || '').trim()) {
944
+ throw new Error(String(result.stderr || `git exited ${result.status}`).trim());
945
+ }
946
+ write(renderDiffPreview(result.stdout || ''));
640
947
  break;
948
+ }
641
949
  case '/db':
642
- await runDb(args.length ? args : ['status'], { cwd: state.cwd });
950
+ await runDb(args.length ? args : ['status'], { cwd: state.projectRoot || state.cwd, write });
643
951
  break;
644
952
  case '/agent':
645
953
  await runLocalAgent(args.join(' '), write, { interactive: state.interactive });
646
954
  break;
955
+ case '/goap': {
956
+ const sub = (args[0] || 'status').toLowerCase();
957
+ if (sub === 'list' || sub === 'ls') {
958
+ const listState = await listGoapTickets({ cwd: state.cwd });
959
+ if (!listState.ok) {
960
+ writeLine(write, ` Failed to list GOAP tickets: ${listState.error || 'unknown error'}`);
961
+ } else {
962
+ writeLine(write, renderGoapList(listState));
963
+ }
964
+ break;
965
+ }
966
+ if (sub === 'new' || sub === 'create') {
967
+ const title = args.slice(1).join(' ').trim();
968
+ if (!title) {
969
+ writeLine(write, ' Usage: /goap new <title>');
970
+ break;
971
+ }
972
+ const res = await createGoapGoal({ cwd: state.cwd, title, accountId: readAccountSession({ home: state.home })?.account_id || null });
973
+ if (!res.ok) {
974
+ writeLine(write, ` Failed to create GOAP goal: ${res.error || 'unknown error'}`);
975
+ } else {
976
+ writeLine(write, ` Created and activated goal: ${res.ticketId} ("${res.title}")`);
977
+ }
978
+ break;
979
+ }
980
+ if (sub === 'switch' || sub === 'select') {
981
+ const tid = args[1]?.trim();
982
+ if (!tid) {
983
+ writeLine(write, ' Usage: /goap switch <ticket_id>');
984
+ break;
985
+ }
986
+ const res = await switchGoapGoal({ cwd: state.cwd, ticketId: tid, accountId: readAccountSession({ home: state.home })?.account_id || null });
987
+ if (!res.ok) {
988
+ writeLine(write, ` Failed to switch GOAP goal: ${res.error || 'unknown error'}`);
989
+ } else {
990
+ writeLine(write, ` Switched active GOAP goal to: ${res.ticketId}`);
991
+ }
992
+ break;
993
+ }
994
+ if (sub === 'close') {
995
+ const tid = args[1]?.trim() || null;
996
+ const res = await closeGoapGoal({ cwd: state.cwd, ticketId: tid, accountId: readAccountSession({ home: state.home })?.account_id || null });
997
+ if (!res.ok) {
998
+ writeLine(write, ` Failed to close GOAP goal: ${res.error || 'unknown error'}`);
999
+ } else {
1000
+ writeLine(write, ` Closed GOAP goal: ${res.ticketId} [${res.status}]`);
1001
+ }
1002
+ break;
1003
+ }
1004
+ const goapState = await readGoapState({ cwd: state.cwd, accountId: readAccountSession({ home: state.home })?.account_id || null });
1005
+ if (!goapState.ok) {
1006
+ writeLine(write, ` Failed to read GOAP state: ${goapState.error || 'unknown error'}`);
1007
+ break;
1008
+ }
1009
+ if (sub === 'goal') writeLine(write, renderGoapGoal(goapState));
1010
+ else if (sub === 'why') writeLine(write, renderGoapWhy(goapState));
1011
+ else if (sub === 'plan') writeLine(write, renderGoapPlan(goapState));
1012
+ else writeLine(write, renderGoapStatus(goapState));
1013
+ break;
1014
+ }
647
1015
  case '/logs':
648
1016
  await showLocalLogs(state.cwd, write);
649
1017
  break;
@@ -667,9 +1035,15 @@ export async function dispatchShellLine(line, state = {}) {
667
1035
  persistSession(state, { last_error: diagnosticFromError(error, { source: 'shell', kind: 'interactive_error' }) });
668
1036
  }
669
1037
  if (!error?.reported) {
670
- for (const line of renderDiagnosticError(error).split('\n')) writeLine(write, ` ${line}`);
1038
+ for (const errorLine of renderDiagnosticError(error).split('\n')) writeLine(write, ` ${errorLine}`);
671
1039
  }
672
1040
  }
1041
+ const skipFooter = state.wroteTurnFooter;
1042
+ state.wroteTurnFooter = false;
1043
+ if (state.persistFooter && !skipFooter) {
1044
+ writeLine(write, renderShellSessionFooter(state));
1045
+ writeLine(write, '');
1046
+ }
673
1047
  return { handled: true, exit: false, cwd: state.cwd };
674
1048
  }
675
1049
 
@@ -677,14 +1051,18 @@ export async function runShell(argv = [], options = {}) {
677
1051
  const write = options.write || ((text) => process.stdout.write(text));
678
1052
  const state = {
679
1053
  cwd: path.resolve(options.cwd || process.cwd()),
1054
+ projectRoot: findCliProjectRoot(options.session?.project_root || options.cwd || process.cwd()),
680
1055
  write,
681
1056
  interactive: options.interactive ?? Boolean(process.stdin.isTTY && process.stdout.isTTY),
682
1057
  usageSnapshot: options.usageSnapshot || options.session?.usage_snapshot || null,
1058
+ providerState: null,
683
1059
  session: options.session || null,
684
1060
  home: options.home,
685
1061
  providerFetchImpl: options.providerFetchImpl,
1062
+ persistFooter: options.persistFooter === true,
686
1063
  };
687
1064
  const sub = argv[0] || '';
1065
+ hydrateSecureCredentials(process.env, { home: state.home });
688
1066
  if (sub === 'list' || sub === 'status') { write(renderShellCatalog()); return; }
689
1067
  if (sub === '--command' || sub === '--once') {
690
1068
  const line = argv.slice(1).join(' ');
@@ -701,40 +1079,73 @@ export async function runShell(argv = [], options = {}) {
701
1079
  status: 'active',
702
1080
  cwd: state.cwd,
703
1081
  active_started_at: new Date().toISOString(),
704
- }, { home: state.home });
1082
+ }, { home: state.home, projectRoot: state.projectRoot });
705
1083
  state.usageSnapshot = state.session.usage_snapshot || state.usageSnapshot;
706
1084
  } else {
707
1085
  const preferences = readCliPreferences(state.cwd) || {};
708
1086
  state.session = createLocalSession({
709
1087
  cwd: state.cwd,
1088
+ project_root: state.projectRoot,
710
1089
  status: 'active',
711
1090
  model_key: preferences.modelPreference !== 'auto' ? preferences.modelPreference : null,
712
1091
  reasoning_effort: preferences.reasoningEffort !== 'auto' ? preferences.reasoningEffort : null,
713
1092
  requested_service_tier: preferences.serviceTier || 'default',
714
- }, { home: state.home });
1093
+ }, { home: state.home, projectRoot: state.projectRoot });
715
1094
  }
716
1095
 
717
1096
  if (options.intro !== 'quiet') {
718
1097
  write(renderShellCatalog());
719
1098
  writeLine(write, ' Interactive shell ready. Type / for the command picker; /exit to return to your host shell.\n');
720
1099
  }
1100
+ state.persistFooter = options.persistFooter !== false;
1101
+ writeLine(write, renderShellSessionFooter(state));
1102
+ writeLine(write, '');
721
1103
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout, terminal: Boolean(process.stdin.isTTY && process.stdout.isTTY) });
1104
+ state.rl = rl;
1105
+ let promptTextRef = () => renderShellPrompt(state.cwd);
1106
+ const restorePaste = installPasteCollapse(rl, { output: process.stdout });
722
1107
  let interrupted = false;
1108
+ let lastSigintAt = 0;
1109
+ const SIGINT_EXIT_WINDOW_MS = 2000;
723
1110
  rl.on('SIGINT', () => {
724
- interrupted = true;
725
- rl.close();
1111
+ const now = Date.now();
1112
+ if (now - lastSigintAt < SIGINT_EXIT_WINDOW_MS) {
1113
+ // Second Ctrl+C within the window: actually exit, matching the
1114
+ // near-universal CLI convention (bash, node REPL, Claude Code, Codex).
1115
+ interrupted = true;
1116
+ rl.close();
1117
+ return;
1118
+ }
1119
+ // First Ctrl+C: cancel the current line/prompt only, matching what the
1120
+ // footer already tells the user ("ctrl-c to cancel"). A single press
1121
+ // used to close the whole session outright -- that's the discrepancy
1122
+ // being fixed here, not the intended behavior.
1123
+ lastSigintAt = now;
1124
+ write('\n (Ctrl+C again within 2s to exit)\n');
1125
+ rl.line = '';
1126
+ rl.cursor = 0;
1127
+ if (rl.terminal) { rl.setPrompt(promptTextRef()); rl.prompt(); }
726
1128
  });
727
1129
  const promptText = () => {
728
1130
  if (typeof options.prompt === 'function') return options.prompt(state);
729
1131
  if (typeof options.prompt === 'string' && options.prompt) return options.prompt;
730
1132
  return renderShellPrompt(state.cwd);
731
1133
  };
732
- if (rl.terminal) { rl.setPrompt(promptText()); rl.prompt(); }
733
- for await (const line of rl) {
734
- recordSessionInput(state, line);
735
- const result = await dispatchShellLine(line, state);
736
- if (result.exit) { rl.close(); break; }
1134
+ promptTextRef = promptText;
1135
+ try {
737
1136
  if (rl.terminal) { rl.setPrompt(promptText()); rl.prompt(); }
1137
+ for await (const line of rl) {
1138
+ recordSessionInput(state, line);
1139
+ const result = await dispatchShellLine(line, state);
1140
+ if (result.exit) { rl.close(); break; }
1141
+ if (process.stdin.isPaused()) {
1142
+ process.stdin.resume();
1143
+ }
1144
+ if (rl.terminal) { rl.setPrompt(promptText()); rl.prompt(); }
1145
+ }
1146
+ } finally {
1147
+ restorePaste();
1148
+ state.rl = null;
738
1149
  }
739
1150
  if (state.session) {
740
1151
  const activeElapsedMs = localSessionElapsedMs(state.session);