@inneranimalmedia/agentsam-sdk 2.6.1 → 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 (507) 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/AUTH_IDENTITY_CONTRACT.md +25 -27
  17. package/docs/AUTORAG.md +58 -0
  18. package/docs/BRAND_INTELLIGENCE.md +30 -0
  19. package/docs/CLI_SHELL.md +4 -2
  20. package/docs/CMS_CONTEXTUAL_COMPOSER.md +38 -0
  21. package/docs/MERKLE.md +4 -0
  22. package/docs/PROJECT_CONFIG.md +3 -0
  23. package/docs/REPOSITORY_KNOWLEDGE.md +2 -0
  24. package/docs/SDK_WORKER.md +4 -3
  25. package/docs/THEME_REFINERY.md +120 -0
  26. package/docs/architecture/PROVIDER-TOOLS-AND-COMPACTION.md +60 -0
  27. package/docs/architecture/STORAGE.md +87 -0
  28. package/docs/cad-project-tools.md +69 -0
  29. package/docs/cms-scaffolding-pattern-reference.md +73 -0
  30. package/docs/plans/AGENTSAM-INTERACTIVE-UI-REQUIREMENTS-codex-tui-20260919.md +113 -0
  31. package/docs/plans/AGENTSAM-INTERACTIVE-UI-REQUIREMENTS.md +85 -0
  32. package/docs/plans/AGENTSAM-MCP-CLIENT-2026-09-19.md +181 -0
  33. package/docs/plans/ASBD-PORTABLE-WORKGRAPH-UI-DEFERRED.md +34 -0
  34. package/docs/plans/ASTRA-BRIEF-2026-09-18.md +258 -0
  35. package/docs/plans/MULTI-AGENT-SPRINTS-2026-09-19.md +373 -0
  36. package/docs/plans/NEXT-AGENT-HANDOFF.md +167 -0
  37. package/docs/plans/RECONCILIATION-2026-09-19.md +178 -0
  38. package/docs/plugins.md +43 -0
  39. package/docs/tools/README.md +57 -0
  40. package/docs/tools/agent-and-orchestration.md +49 -0
  41. package/docs/tools/browser-and-research.md +46 -0
  42. package/docs/tools/cloudflare-platform.md +32 -0
  43. package/docs/tools/cms.md +25 -0
  44. package/docs/tools/communications.md +41 -0
  45. package/docs/tools/database-and-storage.md +62 -0
  46. package/docs/tools/design-and-media.md +64 -0
  47. package/docs/tools/filesystem-and-code.md +55 -0
  48. package/docs/tools/github.md +56 -0
  49. package/docs/tools/integrations-and-misc.md +16 -0
  50. package/docs/tools/memory.md +21 -0
  51. package/docs/tools/terminal-and-execution.md +29 -0
  52. package/docs/tools/tickets-and-platform.md +30 -0
  53. package/migrations/d1/0001_agentsam_plugin_runtime.sql +177 -0
  54. package/migrations/d1/0002_agentsam_ticket_ownership.sql +8 -0
  55. package/migrations/d1/0002_dynamic_model_policies.sql +23 -0
  56. package/migrations/d1/0003_expand_tool_handler_families.sql +18 -0
  57. package/migrations/d1/0004_project_aware_retrieval_lanes.sql +24 -0
  58. package/migrations/d1/0005_portable_knowledge_identity.sql +104 -0
  59. package/migrations/d1/0006_repair_ops_trail_account_scope.sql +14 -0
  60. package/migrations/d1/0007_repair_ops_trail_agent_run_account.sql +13 -0
  61. package/migrations/d1/0008_backfill_knowledge_run_legacy_tenant.sql +14 -0
  62. package/migrations/d1/0009_normalize_cloudflare_plugin_installations.sql +137 -0
  63. package/migrations/runtime/0001_cli_runtime.sql +67 -0
  64. package/migrations/runtime/0002_project_sessions.sql +15 -0
  65. package/migrations/runtime/0003_provider_continuations.sql +8 -0
  66. package/package.json +92 -12
  67. package/packages/agentsam-brand/README.md +21 -0
  68. package/packages/agentsam-brand/package.json +22 -0
  69. package/packages/agentsam-brand/src/contract.js +59 -0
  70. package/packages/agentsam-brand/src/extract.js +126 -0
  71. package/packages/agentsam-brand/src/goap-actions.js +73 -0
  72. package/packages/agentsam-brand/src/index.js +48 -0
  73. package/packages/agentsam-brand/src/plan.js +115 -0
  74. package/packages/agentsam-brand/src/resolve.js +139 -0
  75. package/packages/agentsam-brand/src/scan.js +250 -0
  76. package/packages/agentsam-brand/test/brand-scan.test.mjs +106 -0
  77. package/packages/agentsam-contracts/README.md +23 -0
  78. package/packages/agentsam-contracts/package.json +32 -0
  79. package/packages/agentsam-contracts/src/adapter.ts +25 -0
  80. package/packages/agentsam-contracts/src/agent.ts +35 -0
  81. package/packages/agentsam-contracts/src/artifacts.ts +33 -0
  82. package/packages/agentsam-contracts/src/authority.ts +29 -0
  83. package/packages/agentsam-contracts/src/context.ts +16 -0
  84. package/packages/agentsam-contracts/src/errors.ts +46 -0
  85. package/packages/agentsam-contracts/src/events.ts +47 -0
  86. package/packages/agentsam-contracts/src/execution.ts +50 -0
  87. package/packages/agentsam-contracts/src/hooks.ts +40 -0
  88. package/packages/agentsam-contracts/src/identity.ts +16 -0
  89. package/packages/agentsam-contracts/src/index.ts +14 -0
  90. package/packages/agentsam-contracts/src/models.ts +9 -0
  91. package/packages/agentsam-contracts/src/providers.ts +33 -0
  92. package/packages/agentsam-contracts/src/repository.ts +64 -0
  93. package/packages/agentsam-contracts/src/tools.ts +98 -0
  94. package/packages/agentsam-contracts/test/contracts.test.mjs +16 -0
  95. package/packages/agentsam-contracts/test/errors-catalog.test.mjs +25 -0
  96. package/packages/agentsam-contracts/test/repository.test.mjs +18 -0
  97. package/packages/agentsam-contracts/tsconfig.json +12 -0
  98. package/packages/agentsam-errors/package.json +17 -0
  99. package/packages/agentsam-errors/src/adapters/anthropic.js +15 -0
  100. package/packages/agentsam-errors/src/adapters/cad.js +109 -0
  101. package/packages/agentsam-errors/src/adapters/cloudflare.js +21 -0
  102. package/packages/agentsam-errors/src/adapters/cursor.js +4 -0
  103. package/packages/agentsam-errors/src/adapters/device.js +38 -0
  104. package/packages/agentsam-errors/src/adapters/docker.js +28 -0
  105. package/packages/agentsam-errors/src/adapters/gcp.js +14 -0
  106. package/packages/agentsam-errors/src/adapters/github.js +22 -0
  107. package/packages/agentsam-errors/src/adapters/google.js +17 -0
  108. package/packages/agentsam-errors/src/adapters/index.js +55 -0
  109. package/packages/agentsam-errors/src/adapters/internal.js +40 -0
  110. package/packages/agentsam-errors/src/adapters/oauth.js +24 -0
  111. package/packages/agentsam-errors/src/adapters/openai.js +17 -0
  112. package/packages/agentsam-errors/src/adapters/process.js +35 -0
  113. package/packages/agentsam-errors/src/adapters/provider.js +114 -0
  114. package/packages/agentsam-errors/src/envelope.js +186 -0
  115. package/packages/agentsam-errors/src/error.js +24 -0
  116. package/packages/agentsam-errors/src/fingerprint.js +35 -0
  117. package/packages/agentsam-errors/src/index.js +10 -0
  118. package/packages/agentsam-errors/src/normalize.js +30 -0
  119. package/packages/agentsam-errors/src/redaction.js +52 -0
  120. package/packages/agentsam-errors/src/render.js +16 -0
  121. package/packages/agentsam-errors/src/retry.js +10 -0
  122. package/packages/agentsam-errors/src/transport.js +52 -0
  123. package/packages/agentsam-errors/src/vocabulary.js +320 -0
  124. package/packages/agentsam-errors/test/envelope.test.mjs +70 -0
  125. package/packages/agentsam-errors/test/providers.test.mjs +99 -0
  126. package/packages/agentsam-errors/test/redaction.test.mjs +21 -0
  127. package/packages/agentsam-errors/test/runtime-cad-device.test.mjs +76 -0
  128. package/packages/agentsam-errors/test/transport.test.mjs +46 -0
  129. package/packages/agentsam-knowledge/package.json +15 -0
  130. package/packages/agentsam-knowledge/src/autorag/index.js +64 -0
  131. package/packages/agentsam-knowledge/src/backends/index.js +26 -0
  132. package/packages/agentsam-knowledge/src/index.js +3 -0
  133. package/packages/agentsam-knowledge/src/providers/index.js +95 -0
  134. package/packages/agentsam-knowledge/test/autorag.test.mjs +42 -0
  135. package/packages/agentsam-nav/README.md +40 -0
  136. package/packages/agentsam-nav/dist/Nav.d.ts +43 -0
  137. package/packages/agentsam-nav/dist/NavCollapsible.d.ts +10 -0
  138. package/packages/agentsam-nav/dist/NavContent.d.ts +2 -0
  139. package/packages/agentsam-nav/dist/NavFooter.d.ts +2 -0
  140. package/packages/agentsam-nav/dist/NavGroup.d.ts +3 -0
  141. package/packages/agentsam-nav/dist/NavHeader.d.ts +2 -0
  142. package/packages/agentsam-nav/dist/NavMenu.d.ts +22 -0
  143. package/packages/agentsam-nav/dist/NavProvider.d.ts +35 -0
  144. package/packages/agentsam-nav/dist/NavTrigger.d.ts +2 -0
  145. package/packages/agentsam-nav/dist/Surfaces.d.ts +41 -0
  146. package/packages/agentsam-nav/dist/browser.d.ts +11 -0
  147. package/packages/agentsam-nav/dist/browser.global.js +132 -0
  148. package/packages/agentsam-nav/dist/browser.js +613 -0
  149. package/packages/agentsam-nav/dist/contracts.d.ts +76 -0
  150. package/packages/agentsam-nav/dist/index.d.ts +11 -0
  151. package/packages/agentsam-nav/dist/index.js +614 -0
  152. package/packages/agentsam-nav/dist/legacy.css +83 -0
  153. package/packages/agentsam-nav/dist/shell.css +113 -0
  154. package/packages/agentsam-nav/dist/theme.css +113 -0
  155. package/packages/agentsam-nav/dist/utils.d.ts +1 -0
  156. package/packages/agentsam-nav/package.json +43 -0
  157. package/packages/agentsam-queue-control/README.md +107 -0
  158. package/packages/agentsam-queue-control/package.json +31 -0
  159. package/packages/agentsam-queue-control/schemas/job-envelope.schema.json +56 -0
  160. package/packages/agentsam-queue-control/src/adapters/cloudflare.js +152 -0
  161. package/packages/agentsam-queue-control/src/adapters/memory.js +38 -0
  162. package/packages/agentsam-queue-control/src/contracts.js +130 -0
  163. package/packages/agentsam-queue-control/src/control-plane.js +141 -0
  164. package/packages/agentsam-queue-control/src/dispatch.js +43 -0
  165. package/packages/agentsam-queue-control/src/index.js +7 -0
  166. package/packages/agentsam-queue-control/src/policy.js +88 -0
  167. package/packages/agentsam-queue-control/src/topology.js +66 -0
  168. package/packages/agentsam-queue-control/test/queue-control.test.mjs +100 -0
  169. package/packages/agentsam-repository/src/goap.js +718 -0
  170. package/packages/agentsam-repository/src/goap.js.bak-20260922011251 +670 -0
  171. package/packages/agentsam-repository/src/index.js +5 -0
  172. package/packages/agentsam-repository/src/merkle/cloudflare-persistence.js +2 -2
  173. package/packages/agentsam-repository/src/tickets.js +184 -0
  174. package/packages/agentsam-repository/src/work-tracking.js +199 -0
  175. package/packages/agentsam-repository/src/workspace-state.js +176 -0
  176. package/packages/agentsam-repository/test/goap-tickets-workspace-state.test.mjs +152 -0
  177. package/packages/connectors/cloudflare/src/index.js +297 -15
  178. package/packages/connectors/cloudflare/src/owner.js +43 -40
  179. package/packages/connectors/cloudflare/src/routes.js +167 -26
  180. package/packages/connectors/cloudflare/tests/connector.test.mjs +112 -2
  181. package/packages/heuristic-theme/README.md +11 -0
  182. package/packages/heuristic-theme/package.json +25 -0
  183. package/packages/heuristic-theme/src/index.js +2 -0
  184. package/packages/heuristic-theme/src/registry.js +36 -0
  185. package/packages/heuristic-theme/src/theme.js +41 -0
  186. package/packages/heuristic-theme/test/theme.test.mjs +26 -0
  187. package/packages/identity/.agentsam/features/oauth-login-portal/agentsam.feature.json +64 -0
  188. package/packages/identity/.agentsam/features/oauth-login-portal/providers.json +96 -0
  189. package/packages/identity/.agentsam/features/oauth-login-portal/resources.json +47 -0
  190. package/packages/identity/.agentsam/features/oauth-login-portal/routes.json +74 -0
  191. package/packages/identity/.agentsam/features/oauth-login-portal/ui.json +35 -0
  192. package/packages/identity/package.json +4 -3
  193. package/packages/identity/src/adapters/cloudflare-d1/ids.js +4 -0
  194. package/packages/identity/src/adapters/cloudflare-d1/index.js +39 -1
  195. package/packages/identity/src/contracts/auth-config.js +23 -40
  196. package/packages/identity/src/contracts/provider.js +3 -2
  197. package/packages/identity/src/frontend/auth-portal/pages/login.html +7 -0
  198. package/packages/identity/src/index.js +2 -0
  199. package/packages/identity/src/oauth/credentials.js +13 -0
  200. package/packages/identity/src/oauth/iam-platform.js +5 -5
  201. package/packages/identity/src/providers/cloudflare/index.js +19 -0
  202. package/packages/identity/src/providers/cloudflare/mapper.js +21 -0
  203. package/packages/identity/src/providers/cloudflare/oauth.js +48 -0
  204. package/packages/identity/src/providers/cloudflare/profile.js +28 -0
  205. package/packages/identity/src/providers/index.js +18 -2
  206. package/packages/identity/src/server/identity-service.js +13 -3
  207. package/packages/identity/src/server/worker-router.js +63 -8
  208. package/packages/identity/tests/auth-config.test.mjs +14 -23
  209. package/packages/identity/tests/identity-service.test.mjs +15 -0
  210. package/packages/providers/completeful/README.md +38 -0
  211. package/packages/providers/completeful/package.json +29 -0
  212. package/packages/providers/completeful/src/client.js +162 -0
  213. package/packages/providers/completeful/src/events.js +49 -0
  214. package/packages/providers/completeful/src/index.js +22 -0
  215. package/packages/providers/completeful/src/tools.js +579 -0
  216. package/packages/providers/completeful/src/webhooks.js +55 -0
  217. package/packages/providers/completeful/test/provider.test.mjs +196 -0
  218. package/packages/theme-church-site/README.md +10 -0
  219. package/packages/theme-church-site/package.json +30 -0
  220. package/packages/theme-church-site/src/harvest.json +14 -0
  221. package/packages/theme-church-site/src/index.js +84 -0
  222. package/packages/theme-church-site/test/theme.test.mjs +19 -0
  223. package/packages/theme-companions-site/README.md +10 -0
  224. package/packages/theme-companions-site/package.json +30 -0
  225. package/packages/theme-companions-site/src/harvest.json +12 -0
  226. package/packages/theme-companions-site/src/index.js +82 -0
  227. package/packages/theme-companions-site/test/theme.test.mjs +19 -0
  228. package/packages/theme-floors-site/README.md +10 -0
  229. package/packages/theme-floors-site/package.json +30 -0
  230. package/packages/theme-floors-site/src/harvest.json +14 -0
  231. package/packages/theme-floors-site/src/index.js +82 -0
  232. package/packages/theme-floors-site/test/theme.test.mjs +19 -0
  233. package/packages/theme-fuelnfree-site/README.md +10 -0
  234. package/packages/theme-fuelnfree-site/package.json +30 -0
  235. package/packages/theme-fuelnfree-site/src/harvest.json +13 -0
  236. package/packages/theme-fuelnfree-site/src/index.js +81 -0
  237. package/packages/theme-fuelnfree-site/test/theme.test.mjs +19 -0
  238. package/packages/theme-handyman-site/README.md +10 -0
  239. package/packages/theme-handyman-site/package.json +30 -0
  240. package/packages/theme-handyman-site/src/harvest.json +10 -0
  241. package/packages/theme-handyman-site/src/index.js +83 -0
  242. package/packages/theme-handyman-site/test/theme.test.mjs +19 -0
  243. package/packages/theme-insurance-site/README.md +10 -0
  244. package/packages/theme-insurance-site/package.json +30 -0
  245. package/packages/theme-insurance-site/src/harvest.json +10 -0
  246. package/packages/theme-insurance-site/src/index.js +83 -0
  247. package/packages/theme-insurance-site/test/theme.test.mjs +19 -0
  248. package/packages/theme-shinshu-site/README.md +10 -0
  249. package/packages/theme-shinshu-site/package.json +30 -0
  250. package/packages/theme-shinshu-site/src/harvest.json +12 -0
  251. package/packages/theme-shinshu-site/src/index.js +83 -0
  252. package/packages/theme-shinshu-site/test/theme.test.mjs +19 -0
  253. package/packages/work-graph/README.md +49 -0
  254. package/packages/work-graph/package.json +19 -0
  255. package/packages/work-graph/prototype/README.md +32 -0
  256. package/packages/work-graph/prototype/data/demo-workgraph.js +47 -0
  257. package/packages/work-graph/prototype/renderers/timeline-model.js +21 -0
  258. package/packages/work-graph/prototype/renderers/workgraph-runtime.js +38 -0
  259. package/packages/work-graph/prototype-gnantt.html +655 -0
  260. package/packages/work-graph/src/adapters/business/projects.js +23 -0
  261. package/packages/work-graph/src/adapters/engineering/git.js +22 -0
  262. package/packages/work-graph/src/adapters/engineering/mcp.js +22 -0
  263. package/packages/work-graph/src/index.js +76 -0
  264. package/packages/work-graph/src/renderers/gantt-model.js +11 -0
  265. package/packages/work-graph/src/renderers/timeline.js +16 -0
  266. package/packages/work-graph/test/work-graph.test.mjs +37 -0
  267. package/protocol/README.md +12 -0
  268. package/protocol/capabilities/cloudflare-cpu-audit-input.schema.json +30 -6
  269. package/protocol/capabilities/cloudflare-cpu-profile-input.schema.json +18 -4
  270. package/protocol/capabilities/cloudflare-wrangler-native-input.schema.json +59 -10
  271. package/protocol/capabilities/knowledge-search-input.schema.json +35 -0
  272. package/protocol/capabilities/manifest.json +163 -1
  273. package/protocol/capabilities/repository-audit-input.schema.json +39 -6
  274. package/protocol/capabilities/repository-snapshot-input.schema.json +15 -3
  275. package/protocol/capabilities/terminal-exec-input.schema.json +55 -0
  276. package/protocol/commerce/catalog.v1.json +50 -0
  277. package/protocol/errors/error-catalog.json +164 -0
  278. package/protocol/errors/error-envelope.schema.json +544 -0
  279. package/protocol/features/README.md +57 -0
  280. package/protocol/features/agentsam.feature.v1.schema.json +290 -0
  281. package/protocol/features/catalog.json +65 -0
  282. package/protocol/features/examples/provider-resend.feature.json +17 -0
  283. package/protocol/go/agentsam-go-runtime.v1.json +17 -0
  284. package/protocol/knowledge/LIFECYCLE.md +140 -0
  285. package/protocol/knowledge/README.md +4 -0
  286. package/protocol/presets/catalog.json +128 -19
  287. package/protocol/rpc/v1/errors.proto +24 -0
  288. package/protocol/theme-refinery/d1-reconciliation.md +48 -0
  289. package/protocol/theme-refinery/product-metadata.schema.json +27 -0
  290. package/protocol/theme-refinery/receipts/products-registry-batch-20260924.json +318 -0
  291. package/protocol/theme-refinery/receipts/theme-harvest-20260924.json +472 -0
  292. package/protocol/theme-refinery/reconciliation-receipt.schema.json +83 -0
  293. package/services/cad/server.py +249 -2
  294. package/src/agent/capability-adapter.js +15 -5
  295. package/src/agent/index.js +2 -1
  296. package/src/agent/model-turn.js +51 -0
  297. package/src/agent/responses-runner.js +304 -38
  298. package/src/capabilities/index.js +1 -0
  299. package/src/capabilities/terminal-exec.js +78 -0
  300. package/src/cli.js +129 -31
  301. package/src/cloudflare/index.js +6 -0
  302. package/src/cloudflare/runtime-status.js +393 -0
  303. package/src/cloudflare/wrangler.js +17 -1
  304. package/src/commands/account-auth.js +1 -1
  305. package/src/commands/app.js +106 -0
  306. package/src/commands/autorag.js +77 -0
  307. package/src/commands/brand.js +293 -0
  308. package/src/commands/cad.js +1 -0
  309. package/src/commands/connections.js +126 -29
  310. package/src/commands/context-economics.js +8 -0
  311. package/src/commands/db.js +7 -0
  312. package/src/commands/deploy.js +2 -2
  313. package/src/commands/env.js +5 -4
  314. package/src/commands/eval.js +209 -14
  315. package/src/commands/go.js +365 -0
  316. package/src/commands/goap.js +176 -0
  317. package/src/commands/identity-init.js +27 -3
  318. package/src/commands/identity-protocol.js +195 -0
  319. package/src/commands/knowledge.js +64 -5
  320. package/src/commands/mcp.js +341 -0
  321. package/src/commands/merkle-persist.js +3 -1
  322. package/src/commands/merkle.js +18 -3
  323. package/src/commands/models.js +109 -23
  324. package/src/commands/plugins.js +175 -0
  325. package/src/commands/preferences.js +28 -8
  326. package/src/commands/product.js +53 -10
  327. package/src/commands/providers.js +389 -0
  328. package/src/commands/resume.js +9 -5
  329. package/src/commands/runtime-status.js +154 -0
  330. package/src/commands/shell.js +469 -54
  331. package/src/commands/status.js +82 -7
  332. package/src/commands/tunnel.js +228 -195
  333. package/src/commands/whoami.js +115 -22
  334. package/src/context/budget.js +6 -0
  335. package/src/context/index.js +2 -1
  336. package/src/context/project-card.js +75 -0
  337. package/src/errors/contract.js +34 -211
  338. package/src/errors/diagnostic.js +95 -50
  339. package/src/errors/index.js +6 -13
  340. package/src/eval/index.js +6 -0
  341. package/src/eval/live.js +439 -0
  342. package/src/features/resolve.js +242 -0
  343. package/src/go/build.js +108 -0
  344. package/src/go/cloudflare.js +207 -0
  345. package/src/go/contract.js +139 -0
  346. package/src/go/discover.js +96 -0
  347. package/src/go/index.js +13 -0
  348. package/src/go/receipts.js +77 -0
  349. package/src/go/registry.js +223 -0
  350. package/src/go/verify.js +31 -0
  351. package/src/index.js +100 -1
  352. package/src/knowledge/config.js +13 -5
  353. package/src/knowledge/engine.js +23 -8
  354. package/src/knowledge/index.js +1 -0
  355. package/src/knowledge/lane-registry.js +46 -0
  356. package/src/knowledge/providers/gemini.js +48 -8
  357. package/src/knowledge/service/client.js +13 -1
  358. package/src/knowledge/service/grpc-codec.js +156 -53
  359. package/src/knowledge/service/job-engine.js +133 -11
  360. package/src/knowledge/service/job-worker.js +34 -5
  361. package/src/knowledge/service/server.js +10 -1
  362. package/src/knowledge/store-discovery.js +77 -0
  363. package/src/lib/account-session.js +100 -25
  364. package/src/lib/agent-instructions.js +3 -0
  365. package/src/lib/auth.js +318 -51
  366. package/src/lib/cad/blender.js +42 -3
  367. package/src/lib/cad/d1-project-store.js +43 -0
  368. package/src/lib/cad/discovery.js +616 -0
  369. package/src/lib/cad/docker-executor.js +352 -0
  370. package/src/lib/cad/freecad.js +253 -0
  371. package/src/lib/cad/index.js +40 -0
  372. package/src/lib/cad/openscad.js +198 -0
  373. package/src/lib/cad/project-cli.d.ts +2 -0
  374. package/src/lib/cad/project-cli.js +21 -0
  375. package/src/lib/cad/project-contract.d.ts +3 -0
  376. package/src/lib/cad/project-contract.js +1 -0
  377. package/src/lib/cad/project-recipe.js +30 -0
  378. package/src/lib/cad/project-runtime.d.ts +2 -0
  379. package/src/lib/cad/project-runtime.js +53 -0
  380. package/src/lib/cad/project-store.js +25 -0
  381. package/src/lib/cad/project-workflows.js +31 -0
  382. package/src/lib/cad-docker.js +21 -2
  383. package/src/lib/cli-preferences.js +17 -2
  384. package/src/lib/core-client.js +97 -33
  385. package/src/lib/deploy/local-studio.js +6 -5
  386. package/src/lib/detect-context.js +14 -16
  387. package/src/lib/dockerize.js +3 -0
  388. package/src/lib/execution-approvals.js +1 -0
  389. package/src/lib/identity-scaffold.js +4 -1
  390. package/src/lib/knowledge-docker.js +9 -1
  391. package/src/lib/local-sessions.js +124 -34
  392. package/src/lib/local-status.js +35 -14
  393. package/src/lib/project-config.js +9 -0
  394. package/src/lib/provider-credentials.js +233 -55
  395. package/src/lib/scaffold/resolve-cloudflare-account.js +37 -0
  396. package/src/lib/scaffold/wizards/cms.js +2 -8
  397. package/src/lib/scaffold/wizards/worker-api.js +2 -8
  398. package/src/lib/slash-commands.js +10 -2
  399. package/src/local/migrations.js +109 -1
  400. package/src/local/runtime-store.js +31 -5
  401. package/src/local/sqlite.js +36 -1
  402. package/src/mcp/authority.js +348 -0
  403. package/src/mcp/client-adapters.js +431 -0
  404. package/src/mcp/client.js +172 -0
  405. package/src/mcp/index.js +47 -0
  406. package/src/mcp/telemetry.js +103 -0
  407. package/src/models/catalog.js +329 -51
  408. package/src/models/discovery.js +59 -4
  409. package/src/models/index.js +2 -0
  410. package/src/models/inventory-core.js +182 -0
  411. package/src/models/policy.js +20 -0
  412. package/src/plugins/cloudflare.js +103 -0
  413. package/src/plugins/completeful.js +69 -0
  414. package/src/plugins/contracts.js +86 -0
  415. package/src/plugins/index.js +11 -0
  416. package/src/plugins/registry.js +207 -0
  417. package/src/plugins/resource-scope.js +45 -0
  418. package/src/plugins/runtime.js +118 -0
  419. package/src/plugins/vectorize.js +31 -0
  420. package/src/progression/engine.js +171 -0
  421. package/src/progression/index.js +1 -0
  422. package/src/providers/cloudflare-chat.js +1 -1
  423. package/src/providers/gemini-generate-content.js +2 -1
  424. package/src/providers/openai-responses.js +45 -16
  425. package/src/providers/tool-schema.js +85 -0
  426. package/src/repository/index.js +29 -0
  427. package/src/rpc/generated/errors_pb.js +961 -1
  428. package/src/security/local-vault.js +415 -0
  429. package/src/security/process.js +20 -4
  430. package/src/status/actions.js +192 -0
  431. package/src/tools/hydrate.js +5 -1
  432. package/src/tools/index.js +7 -0
  433. package/src/tools/redact.js +64 -0
  434. package/src/tools/registry.js +83 -0
  435. package/src/tools/runtime.js +257 -0
  436. package/src/tools/search.js +21 -8
  437. package/src/ui/ansi.js +117 -3
  438. package/src/ui/cli/activity.js +76 -7
  439. package/src/ui/cli/footer.js +110 -9
  440. package/src/ui/cli/help.js +14 -5
  441. package/src/ui/wireframes.js +166 -0
  442. package/test/account-session.test.mjs +76 -11
  443. package/test/apps-scaffold-contract.test.mjs +58 -0
  444. package/test/capabilities.test.mjs +1 -1
  445. package/test/cli/eval-live.test.mjs +219 -0
  446. package/test/cli/go.test.mjs +71 -0
  447. package/test/cli/local-vault-and-provider-setup.test.mjs +268 -0
  448. package/test/cli/mcp-client.test.mjs +245 -0
  449. package/test/cli/runtime-ui.test.mjs +69 -2
  450. package/test/cli/status-actions.test.mjs +88 -0
  451. package/test/cli/wireframes-go-registry.test.mjs +54 -0
  452. package/test/cloudflare-connector.test.mjs +3 -6
  453. package/test/cloudflare-runtime.test.mjs +8 -1
  454. package/test/context.test.mjs +15 -0
  455. package/test/error-diagnostics.test.mjs +9 -0
  456. package/test/fixtures/cad/courtyard-house.json +208 -0
  457. package/test/go/cloudflare-probe.test.mjs +53 -0
  458. package/test/integration/app-command.test.mjs +55 -0
  459. package/test/integration/cad-discovery.test.mjs +158 -0
  460. package/test/integration/cad-docker-executor.test.mjs +249 -0
  461. package/test/integration/cad-execution.test.mjs +184 -0
  462. package/test/integration/cad-project-app.test.mjs +31 -0
  463. package/test/integration/cad-project-d1.test.mjs +25 -0
  464. package/test/integration/cad-project-native.test.mjs +24 -0
  465. package/test/integration/cad-project-workflow.test.mjs +9 -0
  466. package/test/integration/cad-project.test.mjs +82 -0
  467. package/test/integration/cli-help.test.mjs +4 -0
  468. package/test/integration/cms-site-tenancy-contract.test.mjs +256 -0
  469. package/test/integration/core-client.integration.test.mjs +55 -0
  470. package/test/integration/error-runtime-rpc.test.mjs +71 -0
  471. package/test/integration/features-resolve.test.mjs +128 -0
  472. package/test/integration/install-script.test.mjs +107 -0
  473. package/test/integration/inventory-core.test.mjs +53 -0
  474. package/test/integration/plugins-cloudflare.integration.test.mjs +28 -0
  475. package/test/integration/provider-env-cli.test.mjs +1 -1
  476. package/test/integration/runtime-migrations.test.mjs +6 -1
  477. package/test/integration/runtime-status.integration.test.mjs +211 -0
  478. package/test/integration/scaffold-cloudflare.test.mjs +46 -0
  479. package/test/integration/storage-architecture.test.mjs +154 -0
  480. package/test/integration/store-discovery.test.mjs +44 -0
  481. package/test/integration/terminal-exec.integration.test.mjs +86 -0
  482. package/test/integration/tool-runtime.test.mjs +124 -0
  483. package/test/integration/tool-schema.test.mjs +59 -0
  484. package/test/knowledge-service.test.mjs +4 -0
  485. package/test/local-sessions.test.mjs +11 -2
  486. package/test/local-studio-deploy.test.mjs +1 -1
  487. package/test/model-catalog.test.mjs +28 -1
  488. package/test/models.test.mjs +159 -5
  489. package/test/openai-responses.test.mjs +60 -0
  490. package/test/provider-credentials.test.mjs +15 -5
  491. package/test/responses-runner.test.mjs +321 -6
  492. package/test/sdk-worker-contract.test.mjs +5 -1
  493. package/test/shell.test.mjs +149 -4
  494. package/test/smoke.mjs +4 -3
  495. package/test/tools-search.test.mjs +33 -0
  496. package/test/unit/website-assets-bindings.test.mjs +60 -0
  497. package/test/whoami-resume.test.mjs +87 -7
  498. package/examples/gorilla-shell/README.md +0 -15
  499. package/src/lib/gorilla-template.js +0 -54
  500. package/src/lib/prompt-byok.js +0 -57
  501. package/src/lib/save-sdk-token.js +0 -19
  502. package/templates/gorilla-shell/App.tsx +0 -639
  503. package/templates/gorilla-shell/README.md +0 -34
  504. package/templates/gorilla-shell/index.html +0 -15
  505. package/templates/gorilla-shell/main.jsx +0 -9
  506. package/templates/gorilla-shell/package.json +0 -19
  507. package/templates/gorilla-shell/vite.config.js +0 -21
@@ -10,27 +10,36 @@ import { runDb } from './db.js';
10
10
  import { runDeploy } from './deploy.js';
11
11
  import { runStatus } from './status.js';
12
12
  import { runModels } from './models.js';
13
+ import { runProviders } from './providers.js';
13
14
  import { configureCliPreferences } from './preferences.js';
14
15
  import { runCloudflare } from './cloudflare.js';
16
+ import { runConnections } from './connections.js';
17
+ import { runTunnel } from './tunnel.js';
15
18
  import { probeOllamaModel, resolveOllamaConfig } from './ollama.js';
16
19
  import { createInlineActivity } from '../ui/cli/activity.js';
17
20
  import { createCliRuntimePresenter } from '../ui/cli/runtime-events.js';
18
- import { renderCliFooter } from '../ui/cli/footer.js';
21
+ import { renderCliFooter, renderDiffPreview, renderUsagePanel } from '../ui/cli/footer.js';
19
22
  import { diagnosticFromError, renderDiagnosticError } from '../errors/index.js';
20
- import { getModelRecord } from '../models/index.js';
23
+ import { getModelRecord, mergeModelReference } from '../models/index.js';
21
24
  import { discoverProviderModels } from '../models/discovery.js';
22
- import { readCliPreferences, updateCliPreferences } from '../lib/cli-preferences.js';
25
+ import { detectCliProject, findCliProjectRoot, readCliPreferences, updateCliPreferences } from '../lib/cli-preferences.js';
23
26
  import { buildContextEconomicsReport, renderContextEconomics } from './context-economics.js';
24
27
  import { createProviderAdapter } from '../providers/index.js';
25
28
  import { createCapabilityAdapter, runResponsesAgent } from '../agent/index.js';
26
29
  import { resolveProviderCredential } from '../lib/provider-credentials.js';
27
- 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';
28
32
  import { grantExecutionApproval, isExecutionApproved, toolApprovalKey } from '../lib/execution-approvals.js';
29
33
  import { runWhoami } from './whoami.js';
30
34
  import { runLogin, runLogout } from './account-auth.js';
31
35
  import { runHelp } from '../ui/cli/help.js';
36
+ import { hydrateSecureCredentials } from '../security/local-vault.js';
32
37
  import { readAccountSession } from '../lib/account-session.js';
33
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';
34
43
 
35
44
  function writeLine(write, value = '') { write(`${value}\n`); }
36
45
 
@@ -53,6 +62,134 @@ export function renderShellPrompt(cwd, env = process.env) {
53
62
  return `${shellUsername(env)} ${compactCwd(cwd, env.HOME || env.USERPROFILE || '')} > `;
54
63
  }
55
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
+
56
193
  export function tokenizeShellLine(input = '') {
57
194
  const source = String(input);
58
195
  const tokens = [];
@@ -120,9 +257,12 @@ function spawnGit(cwd, args) {
120
257
  function selectedModel(cwd) {
121
258
  const preferences = readCliPreferences(cwd) || {};
122
259
  const snapshot = preferences.modelSnapshot?.model_key === preferences.modelPreference
123
- ? preferences.modelSnapshot
260
+ ? mergeModelReference(preferences.modelSnapshot)
124
261
  : null;
125
- 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
+ }
126
266
  if (!model) throw new Error('Select an exact provider-verified model with /model first so Agent Sam can verify supported runtime controls.');
127
267
  return { preferences, model };
128
268
  }
@@ -168,24 +308,32 @@ async function resolveModelForTurn(cwd, state) {
168
308
 
169
309
  async function chooseReasoning(cwd, args, state) {
170
310
  const { preferences, model } = selectedModel(cwd);
311
+ const supported = ['auto', ...model.reasoning_efforts.filter((value) => value !== 'auto')];
171
312
  let effort = String(args[0] || '').trim().toLowerCase();
172
313
  if (!effort) {
173
314
  if (!state.interactive) {
174
- writeLine(state.write, ` ${model.provider_model_id} reasoning: ${model.reasoning_efforts.join(' | ')}`);
175
- 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'));
176
318
  return;
177
319
  }
178
320
  const choice = await select({
179
- message: `Reasoning level · ${model.provider_model_id}`,
180
- initialValue: model.reasoning_efforts.includes(preferences.reasoningEffort) ? preferences.reasoningEffort : model.reasoning_efforts[0],
181
- 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
+ })),
182
330
  });
183
331
  if (isCancel(choice)) return;
184
332
  effort = choice;
185
333
  }
186
- 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);
187
335
  updateCliPreferences(cwd, { reasoningEffort: effort });
188
- writeLine(state.write, ` reasoning → ${effort}`);
336
+ writeLine(state.write, ' reasoning → ' + effort);
189
337
  }
190
338
 
191
339
  function setServiceTier(cwd, tier, write) {
@@ -240,15 +388,15 @@ export async function runLocalAgent(goal, write, options = {}) {
240
388
  }
241
389
 
242
390
  async function showLocalLogs(cwd, write) {
243
- const dbPath = path.join(cwd, '.agentsam', 'data', 'agentsam.sqlite');
244
- 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; }
245
393
  const { createLocalSqliteDatabase } = await import('../local/sqlite.js');
246
394
  const db = await createLocalSqliteDatabase(dbPath);
247
395
  try {
248
- 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();
249
- if (!calls.results.length) { writeLine(write, ' No local Agent Sam tool-call events yet.'); return; }
250
- writeLine(write, '\n Recent Agent Sam tool calls');
251
- 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}`);
252
400
  writeLine(write, '');
253
401
  } finally { db.close(); }
254
402
  }
@@ -299,6 +447,7 @@ export function renderSessionReceipt(session) {
299
447
  if (componentTotal > 0) {
300
448
  lines.push(`Cost breakdown: input ${formatUsd(breakdown.input)} · cached ${formatUsd(breakdown.cached_input)} · cache write ${formatUsd(breakdown.cache_write)} · output ${formatUsd(breakdown.output)}`);
301
449
  }
450
+ if (session.latest_compaction) lines.push(`Latest compaction: ${session.latest_compaction.compaction_id || 'completed'} · ${formatUsd(session.latest_compaction.cost_usd)}`);
302
451
  if (active) lines.push(`Active context: ${formatCount(active)} tokens`);
303
452
  lines.push('', 'To continue this session, run:', ` agentsam resume ${session.id}`, '', 'Or run:', ' agentsam resume', '', 'and select:', ` ${session.title || 'this session'}`, '');
304
453
  return lines.join('\n');
@@ -306,7 +455,7 @@ export function renderSessionReceipt(session) {
306
455
 
307
456
  function persistSession(state, patch = {}) {
308
457
  if (!state.session) return null;
309
- state.session = saveLocalSession({ ...state.session, ...patch }, { home: state.home });
458
+ state.session = saveLocalSession({ ...state.session, ...patch }, { home: state.home, projectRoot: state.projectRoot });
310
459
  return state.session;
311
460
  }
312
461
 
@@ -317,8 +466,6 @@ function recordSessionInput(state, input) {
317
466
  if (housekeeping.has(value.toLowerCase())) return;
318
467
  persistSession(state, {
319
468
  status: 'active',
320
- last_input: value,
321
- title: sessionTitleFromInput(value),
322
469
  });
323
470
  }
324
471
 
@@ -391,6 +538,55 @@ async function approveToolExecution(request, state) {
391
538
  return true;
392
539
  }
393
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
+
394
590
  async function runInteractiveModelTurn(prompt, state) {
395
591
  const resolved = await resolveModelForTurn(state.cwd, state);
396
592
  const { preferences, model, credential } = resolved;
@@ -402,17 +598,15 @@ async function runInteractiveModelTurn(prompt, state) {
402
598
  });
403
599
  const capabilityAdapter = createCapabilityAdapter();
404
600
 
405
- const samePolicy = state.session
406
- && state.session.model_key === model.model_key
407
- && state.session.reasoning_effort === preferences.reasoningEffort
408
- && state.session.requested_service_tier === preferences.serviceTier;
409
- 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;
410
604
  const previousUsageSnapshot = samePolicy ? state.session?.usage_snapshot : null;
411
605
  const accountId = readAccountSession({ home: state.home })?.account_id || null;
412
606
  let runtimeRunId = null;
413
607
  try {
414
608
  runtimeRunId = await startRuntimeRun({
415
- cwd: state.cwd,
609
+ projectRoot: state.projectRoot,
416
610
  account_id: accountId,
417
611
  mode: 'agent',
418
612
  model_key: model.model_key,
@@ -430,6 +624,8 @@ async function runInteractiveModelTurn(prompt, state) {
430
624
  });
431
625
  state.activity = activity;
432
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 };
433
629
  const startedAt = Date.now();
434
630
  activity.start(`Thinking · ${model.provider_model_id}`);
435
631
 
@@ -444,9 +640,17 @@ async function runInteractiveModelTurn(prompt, state) {
444
640
  modelRecord: model,
445
641
  reasoningEffort: preferences.reasoningEffort,
446
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',
447
649
  previousProviderState,
448
650
  previousUsageSnapshot,
449
651
  cumulativeUsage: state.session?.cumulative_usage || null,
652
+ autoCompact: state.session?.auto_compact !== false,
653
+ onCompaction: result => { if (state.session) checkpointCompaction(state, model, result); },
450
654
  promptCacheKey: state.session?.id || undefined,
451
655
  runId: runtimeRunId || state.session?.id || undefined,
452
656
  beforeRequest: (preflight) => approveModelRequest(preflight, state),
@@ -462,7 +666,7 @@ async function runInteractiveModelTurn(prompt, state) {
462
666
  if (runtimeRunId) {
463
667
  try {
464
668
  await finishRuntimeRun({
465
- cwd: state.cwd,
669
+ projectRoot: state.projectRoot,
466
670
  id: runtimeRunId,
467
671
  status: 'failed',
468
672
  error_code: error?.code || 'interactive_error',
@@ -487,10 +691,12 @@ async function runInteractiveModelTurn(prompt, state) {
487
691
  }
488
692
 
489
693
  state.usageSnapshot = result.usage_snapshot;
694
+ state.providerState = result.provider_state || null;
695
+ state.lastTurnElapsedMs = Date.now() - startedAt;
490
696
  if (runtimeRunId) {
491
697
  try {
492
698
  await finishRuntimeRun({
493
- cwd: state.cwd,
699
+ projectRoot: state.projectRoot,
494
700
  id: runtimeRunId,
495
701
  status: 'completed',
496
702
  actual_service_tier: result.actual_service_tier,
@@ -500,7 +706,7 @@ async function runInteractiveModelTurn(prompt, state) {
500
706
  });
501
707
  if (presenter.state.lastCompaction) {
502
708
  await recordRuntimeCompaction({
503
- cwd: state.cwd,
709
+ projectRoot: state.projectRoot,
504
710
  account_id: accountId,
505
711
  agent_run_id: runtimeRunId,
506
712
  session_id: state.session?.id,
@@ -526,30 +732,66 @@ async function runInteractiveModelTurn(prompt, state) {
526
732
  provider_state: { provider: model.provider, ...(result.provider_state || {}) },
527
733
  usage_snapshot: result.usage_snapshot,
528
734
  cumulative_usage: result.cumulative_usage,
529
- 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),
530
737
  cost_breakdown_usd: Object.fromEntries(['input', 'cached_input', 'cache_write', 'output'].map((key) => [
531
- 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),
532
739
  ])),
533
740
  last_error: null,
534
741
  });
535
742
  }
536
743
 
744
+ const identity = resolveShellIdentity(state);
537
745
  writeLine(state.write, renderCliFooter({
746
+ projectName: identity.project,
747
+ branch: identity.branch,
748
+ cwd: compactCwd(state.cwd),
538
749
  model: result.model || model.provider_model_id,
750
+ provider: model.provider,
751
+ effort: result.reasoning_effort,
539
752
  usageSnapshot: result.usage_snapshot,
540
753
  tier: result.actual_service_tier,
541
- elapsedMs: Date.now() - startedAt,
754
+ elapsedMs: state.lastTurnElapsedMs,
542
755
  }));
543
756
  writeLine(state.write, '');
757
+ state.wroteTurnFooter = true;
544
758
  return result;
545
759
  }
546
760
 
547
761
  export async function dispatchShellLine(line, state = {}) {
548
- const tokens = tokenizeShellLine(line);
762
+ let tokens = tokenizeShellLine(line);
549
763
  const write = state.write || ((text) => process.stdout.write(text));
550
764
  state.write = write;
551
765
  state.cwd = path.resolve(state.cwd || process.cwd());
766
+ state.projectRoot ||= findCliProjectRoot(state.cwd);
552
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
+
553
795
  const [command, ...args] = tokens;
554
796
  try {
555
797
  switch (command.toLowerCase()) {
@@ -564,8 +806,15 @@ export async function dispatchShellLine(line, state = {}) {
564
806
  case '/quit':
565
807
  return { handled: true, exit: true, cwd: state.cwd };
566
808
  case '/model': {
567
- const configured = await configureCliPreferences({ cwd: state.cwd, firstRun: false, section: 'model', home: state.home });
568
- 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
+ }
569
818
  break;
570
819
  }
571
820
  case '/reasoning':
@@ -580,18 +829,37 @@ export async function dispatchShellLine(line, state = {}) {
580
829
  case '/standard':
581
830
  setServiceTier(state.cwd, 'default', write);
582
831
  break;
832
+ case '/compact':
833
+ await runInteractiveCompaction(args, state);
834
+ break;
583
835
  case '/context':
584
836
  if (args[0] === 'repo' || args[0] === 'git') await runContext(['--cwd', state.cwd, ...args.slice(1)]);
585
837
  else write(renderContextEconomics(buildContextEconomicsReport(state.cwd, {
586
838
  activeInputTokens: state.usageSnapshot?.current_context?.input_tokens,
587
839
  estimateKind: state.usageSnapshot?.estimate_kind,
840
+ autoCompact: state.session?.auto_compact,
588
841
  })));
589
842
  break;
590
843
  case '/status':
591
844
  await runStatus(args, { cwd: state.cwd });
592
845
  break;
593
846
  case '/models':
594
- 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
+ }
854
+ break;
855
+ case '/providers':
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
+ }
595
863
  break;
596
864
  case '/login':
597
865
  await runLogin(args, { write, home: state.home });
@@ -604,17 +872,43 @@ export async function dispatchShellLine(line, state = {}) {
604
872
  await runWhoami(args, { write, home: state.home });
605
873
  break;
606
874
  case '/session':
607
- case '/usage':
608
875
  if (state.session) write(renderSessionReceipt(state.session));
609
876
  else writeLine(write, ' No persistent session is active in this shell invocation.');
610
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;
611
891
  case '/cf':
612
892
  case '/cloudflare':
613
893
  await runCloudflare(args.length ? args : ['commands'], { cwd: state.cwd, write });
614
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;
615
902
  case '/settings': {
616
- const configured = await configureCliPreferences({ cwd: state.cwd, firstRun: false, home: state.home });
617
- 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
+ }
618
912
  break;
619
913
  }
620
914
  case '/pwd':
@@ -624,22 +918,100 @@ export async function dispatchShellLine(line, state = {}) {
624
918
  const destination = args.length ? args.join(' ') : process.env.HOME || process.env.USERPROFILE || state.cwd;
625
919
  const next = path.resolve(state.cwd, destination);
626
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
+ }
627
931
  state.cwd = next;
932
+ state.identity = null;
933
+ state.identityCwd = null;
628
934
  writeLine(write, state.cwd);
629
935
  break;
630
936
  }
631
937
  case '/git':
632
938
  spawnGit(state.cwd, args.length ? args : ['status', '--short', '--branch']);
633
939
  break;
634
- case '/diff':
635
- 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 || ''));
636
947
  break;
948
+ }
637
949
  case '/db':
638
- await runDb(args.length ? args : ['status'], { cwd: state.cwd });
950
+ await runDb(args.length ? args : ['status'], { cwd: state.projectRoot || state.cwd, write });
639
951
  break;
640
952
  case '/agent':
641
953
  await runLocalAgent(args.join(' '), write, { interactive: state.interactive });
642
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
+ }
643
1015
  case '/logs':
644
1016
  await showLocalLogs(state.cwd, write);
645
1017
  break;
@@ -663,9 +1035,15 @@ export async function dispatchShellLine(line, state = {}) {
663
1035
  persistSession(state, { last_error: diagnosticFromError(error, { source: 'shell', kind: 'interactive_error' }) });
664
1036
  }
665
1037
  if (!error?.reported) {
666
- for (const line of renderDiagnosticError(error).split('\n')) writeLine(write, ` ${line}`);
1038
+ for (const errorLine of renderDiagnosticError(error).split('\n')) writeLine(write, ` ${errorLine}`);
667
1039
  }
668
1040
  }
1041
+ const skipFooter = state.wroteTurnFooter;
1042
+ state.wroteTurnFooter = false;
1043
+ if (state.persistFooter && !skipFooter) {
1044
+ writeLine(write, renderShellSessionFooter(state));
1045
+ writeLine(write, '');
1046
+ }
669
1047
  return { handled: true, exit: false, cwd: state.cwd };
670
1048
  }
671
1049
 
@@ -673,14 +1051,18 @@ export async function runShell(argv = [], options = {}) {
673
1051
  const write = options.write || ((text) => process.stdout.write(text));
674
1052
  const state = {
675
1053
  cwd: path.resolve(options.cwd || process.cwd()),
1054
+ projectRoot: findCliProjectRoot(options.session?.project_root || options.cwd || process.cwd()),
676
1055
  write,
677
1056
  interactive: options.interactive ?? Boolean(process.stdin.isTTY && process.stdout.isTTY),
678
1057
  usageSnapshot: options.usageSnapshot || options.session?.usage_snapshot || null,
1058
+ providerState: null,
679
1059
  session: options.session || null,
680
1060
  home: options.home,
681
1061
  providerFetchImpl: options.providerFetchImpl,
1062
+ persistFooter: options.persistFooter === true,
682
1063
  };
683
1064
  const sub = argv[0] || '';
1065
+ hydrateSecureCredentials(process.env, { home: state.home });
684
1066
  if (sub === 'list' || sub === 'status') { write(renderShellCatalog()); return; }
685
1067
  if (sub === '--command' || sub === '--once') {
686
1068
  const line = argv.slice(1).join(' ');
@@ -697,40 +1079,73 @@ export async function runShell(argv = [], options = {}) {
697
1079
  status: 'active',
698
1080
  cwd: state.cwd,
699
1081
  active_started_at: new Date().toISOString(),
700
- }, { home: state.home });
1082
+ }, { home: state.home, projectRoot: state.projectRoot });
701
1083
  state.usageSnapshot = state.session.usage_snapshot || state.usageSnapshot;
702
1084
  } else {
703
1085
  const preferences = readCliPreferences(state.cwd) || {};
704
1086
  state.session = createLocalSession({
705
1087
  cwd: state.cwd,
1088
+ project_root: state.projectRoot,
706
1089
  status: 'active',
707
1090
  model_key: preferences.modelPreference !== 'auto' ? preferences.modelPreference : null,
708
1091
  reasoning_effort: preferences.reasoningEffort !== 'auto' ? preferences.reasoningEffort : null,
709
1092
  requested_service_tier: preferences.serviceTier || 'default',
710
- }, { home: state.home });
1093
+ }, { home: state.home, projectRoot: state.projectRoot });
711
1094
  }
712
1095
 
713
1096
  if (options.intro !== 'quiet') {
714
1097
  write(renderShellCatalog());
715
1098
  writeLine(write, ' Interactive shell ready. Type / for the command picker; /exit to return to your host shell.\n');
716
1099
  }
1100
+ state.persistFooter = options.persistFooter !== false;
1101
+ writeLine(write, renderShellSessionFooter(state));
1102
+ writeLine(write, '');
717
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 });
718
1107
  let interrupted = false;
1108
+ let lastSigintAt = 0;
1109
+ const SIGINT_EXIT_WINDOW_MS = 2000;
719
1110
  rl.on('SIGINT', () => {
720
- interrupted = true;
721
- 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(); }
722
1128
  });
723
1129
  const promptText = () => {
724
1130
  if (typeof options.prompt === 'function') return options.prompt(state);
725
1131
  if (typeof options.prompt === 'string' && options.prompt) return options.prompt;
726
1132
  return renderShellPrompt(state.cwd);
727
1133
  };
728
- if (rl.terminal) { rl.setPrompt(promptText()); rl.prompt(); }
729
- for await (const line of rl) {
730
- recordSessionInput(state, line);
731
- const result = await dispatchShellLine(line, state);
732
- if (result.exit) { rl.close(); break; }
1134
+ promptTextRef = promptText;
1135
+ try {
733
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;
734
1149
  }
735
1150
  if (state.session) {
736
1151
  const activeElapsedMs = localSessionElapsedMs(state.session);