@inneranimalmedia/agentsam-sdk 2.6.7 → 2.6.8

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 (318) hide show
  1. package/README.md +37 -0
  2. package/docs/REPO_FILE_MAP.txt +132 -0
  3. package/migrations/d1/0018_goap_control_plane_remaster.sql +40 -0
  4. package/migrations/d1/0019_agentsam_agent_run_plan_todo.sql +14 -0
  5. package/migrations/d1/README_PORTABLE_CONTROL_PLANE.md +20 -9
  6. package/migrations/runtime/0002_goap_blackboard_v1.sql +15 -0
  7. package/migrations/runtime/0004_agent_run_plan_linkage.sql +8 -0
  8. package/native/agentsam-machine/cli/Cargo.toml +8 -1
  9. package/native/agentsam-machine/cli/README.md +21 -0
  10. package/native/agentsam-machine/core/Cargo.toml +7 -0
  11. package/native/agentsam-machine/core/README.md +14 -0
  12. package/native/agentsam-machine/core/build.rs +33 -0
  13. package/native/agentsam-machine/core/contracts/source-types.v1.json +353 -0
  14. package/native/agentsam-machine/core/src/lib.rs +6 -0
  15. package/native/agentsam-machine/core/src/source_types.rs +2 -2
  16. package/package.json +16 -6
  17. package/packages/agentsam-errors/package.json +1 -1
  18. package/packages/agentsam-errors/src/index.js +1 -0
  19. package/packages/agentsam-errors/src/tool.js +56 -0
  20. package/packages/agentsam-errors/test/tool.test.mjs +58 -0
  21. package/packages/agentsam-goap/README.md +52 -0
  22. package/packages/agentsam-goap/package.json +37 -0
  23. package/packages/agentsam-goap/src/adapters/d1-sqlite.js +570 -0
  24. package/packages/agentsam-goap/src/adapters/memory.js +199 -0
  25. package/packages/agentsam-goap/src/contracts.js +226 -0
  26. package/packages/agentsam-goap/src/control-plane.js +112 -0
  27. package/packages/agentsam-goap/src/errors.js +157 -0
  28. package/packages/agentsam-goap/src/index.js +5 -0
  29. package/packages/agentsam-goap/src/world.js +202 -0
  30. package/packages/agentsam-goap/test/goap-control-plane.test.mjs +245 -0
  31. package/packages/agentsam-goap/test/world.test.mjs +79 -0
  32. package/packages/agentsam-nav/dist/Nav.d.ts +43 -0
  33. package/packages/agentsam-nav/dist/NavCollapsible.d.ts +10 -0
  34. package/packages/agentsam-nav/dist/NavContent.d.ts +2 -0
  35. package/packages/agentsam-nav/dist/NavFooter.d.ts +2 -0
  36. package/packages/agentsam-nav/dist/NavGroup.d.ts +3 -0
  37. package/packages/agentsam-nav/dist/NavHeader.d.ts +2 -0
  38. package/packages/agentsam-nav/dist/NavMenu.d.ts +22 -0
  39. package/packages/agentsam-nav/dist/NavProvider.d.ts +35 -0
  40. package/packages/agentsam-nav/dist/NavTrigger.d.ts +2 -0
  41. package/packages/agentsam-nav/dist/Surfaces.d.ts +41 -0
  42. package/packages/agentsam-nav/dist/browser.d.ts +11 -0
  43. package/packages/agentsam-nav/dist/browser.global.js +135 -0
  44. package/packages/agentsam-nav/dist/browser.js +7290 -0
  45. package/packages/agentsam-nav/dist/contracts.d.ts +76 -0
  46. package/packages/agentsam-nav/dist/index.d.ts +11 -0
  47. package/packages/agentsam-nav/dist/index.js +7291 -0
  48. package/packages/agentsam-nav/dist/legacy.css +83 -0
  49. package/packages/agentsam-nav/dist/shell.css +113 -0
  50. package/packages/agentsam-nav/dist/theme.css +113 -0
  51. package/packages/agentsam-nav/dist/utils.d.ts +1 -0
  52. package/packages/agentsam-repository/src/goap.js +13 -14
  53. package/packages/agentsam-repository/src/goap.js.bak-20260922011251 +670 -0
  54. package/packages/agentsam-repository/src/tickets.js +15 -22
  55. package/packages/agentsam-repository/test/goap-tickets-workspace-state.test.mjs +19 -8
  56. package/packages/connectors/cloudflare/package.json +3 -1
  57. package/packages/identity/tests/lane1-packed-consumer.test.mjs +1 -1
  58. package/python/agentsam_sdk/repository/intelligence/discovery.py +27 -2
  59. package/python/agentsam_sdk/repository/intelligence/git.py +4 -0
  60. package/python/tests/test_repository_intelligence.py +30 -0
  61. package/src/cli/command-catalog.js +2 -1
  62. package/src/cli.js +10 -1
  63. package/src/commands/goap-world.js +134 -0
  64. package/src/commands/goap.js +11 -4
  65. package/src/commands/machine-binary.js +33 -27
  66. package/src/commands/machine-runtime.js +266 -0
  67. package/src/commands/machine.js +122 -51
  68. package/src/commands/package.js +530 -0
  69. package/src/commands/plan-ledger.js +370 -0
  70. package/src/commands/plan.js +183 -0
  71. package/src/commands/skill.js +78 -17
  72. package/src/knowledge/source.js +1 -1
  73. package/src/lib/slash-commands.js +1 -1
  74. package/src/local/runtime-store.js +24 -2
  75. package/src/local/sqlite.js +17 -0
  76. package/src/sam/operations/package-audit.js +41 -0
  77. package/src/sam/seed.js +2 -0
  78. package/skills/grok-auth/SKILL.md +0 -110
  79. package/skills/grok-auth/agentsam.skill.json +0 -25
  80. package/skills/grok-auth/references/grok-identity.md +0 -64
  81. package/skills/grok-auth/references/per-user-data.md +0 -49
  82. package/skills/grok-auth/references/prewired-and-env.md +0 -54
  83. package/skills/grok-auth/references/session-ui.md +0 -133
  84. package/skills/grok-auth/references/sign-in-methods.md +0 -55
  85. package/skills/grok-auth/references/wiring.md +0 -71
  86. package/skills/grok-building-games/SKILL.md +0 -213
  87. package/skills/grok-building-games/agentsam.skill.json +0 -25
  88. package/skills/grok-building-games/references/3d-libs.md +0 -64
  89. package/skills/grok-building-games/references/ai-pathfinding.md +0 -122
  90. package/skills/grok-building-games/references/audio.md +0 -129
  91. package/skills/grok-building-games/references/babylon.md +0 -202
  92. package/skills/grok-building-games/references/collision-physics.md +0 -122
  93. package/skills/grok-building-games/references/ecs-architecture.md +0 -111
  94. package/skills/grok-building-games/references/game-feel-juice.md +0 -110
  95. package/skills/grok-building-games/references/genres/board-card-chess.md +0 -89
  96. package/skills/grok-building-games/references/genres/endless-runner.md +0 -83
  97. package/skills/grok-building-games/references/genres/fps.md +0 -105
  98. package/skills/grok-building-games/references/genres/platformer-2d.md +0 -97
  99. package/skills/grok-building-games/references/genres/puzzle-match3-tetris.md +0 -131
  100. package/skills/grok-building-games/references/genres/racing-kart.md +0 -106
  101. package/skills/grok-building-games/references/genres/topdown-twin-stick.md +0 -97
  102. package/skills/grok-building-games/references/genres/tower-defense.md +0 -84
  103. package/skills/grok-building-games/references/genres/voxel-minecraft.md +0 -89
  104. package/skills/grok-building-games/references/input.md +0 -139
  105. package/skills/grok-building-games/references/phaser.md +0 -277
  106. package/skills/grok-building-games/references/procedural-generation.md +0 -141
  107. package/skills/grok-building-games/references/save-persistence.md +0 -108
  108. package/skills/grok-building-games/references/threejs-foundational.md +0 -148
  109. package/skills/grok-controls/SKILL.md +0 -274
  110. package/skills/grok-controls/agentsam.skill.json +0 -25
  111. package/skills/grok-design-ui/SKILL.md +0 -122
  112. package/skills/grok-design-ui/agentsam.skill.json +0 -25
  113. package/skills/grok-design-ui/references/animations.md +0 -379
  114. package/skills/grok-design-ui/references/performance.md +0 -88
  115. package/skills/grok-design-ui/references/refined-ui.md +0 -384
  116. package/skills/grok-design-ui/references/surfaces.md +0 -247
  117. package/skills/grok-design-ui/references/typography.md +0 -123
  118. package/skills/grok-game-animation-frames/SKILL.md +0 -84
  119. package/skills/grok-game-animation-frames/agentsam.skill.json +0 -25
  120. package/skills/grok-game-asset-core/SKILL.md +0 -69
  121. package/skills/grok-game-asset-core/agentsam.skill.json +0 -25
  122. package/skills/grok-game-character-consistency/SKILL.md +0 -69
  123. package/skills/grok-game-character-consistency/agentsam.skill.json +0 -25
  124. package/skills/grok-game-tilesets/SKILL.md +0 -65
  125. package/skills/grok-game-tilesets/agentsam.skill.json +0 -25
  126. package/skills/grok-game-ui-icons/SKILL.md +0 -56
  127. package/skills/grok-game-ui-icons/agentsam.skill.json +0 -25
  128. package/skills/grok-generate2dmap/LICENSE +0 -21
  129. package/skills/grok-generate2dmap/SKILL.md +0 -120
  130. package/skills/grok-generate2dmap/SOURCE.md +0 -6
  131. package/skills/grok-generate2dmap/agentsam.skill.json +0 -25
  132. package/skills/grok-generate2dmap/references/deliverables.md +0 -101
  133. package/skills/grok-generate2dmap/references/layered-map-contract.md +0 -209
  134. package/skills/grok-generate2dmap/references/map-strategies.md +0 -341
  135. package/skills/grok-generate2dmap/references/object-production-gate.md +0 -24
  136. package/skills/grok-generate2dmap/references/pipeline.md +0 -149
  137. package/skills/grok-generate2dmap/references/prop-pack-contract.md +0 -174
  138. package/skills/grok-generate2dmap/references/side-scroll-stages.md +0 -29
  139. package/skills/grok-generate2dmap/scripts/compose_layered_preview.py +0 -157
  140. package/skills/grok-generate2dmap/scripts/extract_prop_pack.py +0 -369
  141. package/skills/grok-generate2dsprite/LICENSE +0 -21
  142. package/skills/grok-generate2dsprite/SKILL.md +0 -313
  143. package/skills/grok-generate2dsprite/SOURCE.md +0 -6
  144. package/skills/grok-generate2dsprite/agentsam.skill.json +0 -25
  145. package/skills/grok-generate2dsprite/references/modes.md +0 -124
  146. package/skills/grok-generate2dsprite/references/prompt-rules.md +0 -315
  147. package/skills/grok-generate2dsprite/scripts/generate2dsprite.py +0 -1031
  148. package/skills/grok-generate2dsprite/scripts/make_layout_guide.py +0 -109
  149. package/skills/grok-imagine/SKILL.md +0 -201
  150. package/skills/grok-imagine/agentsam.skill.json +0 -25
  151. package/skills/grok-multiplayer-p2p/SKILL.md +0 -111
  152. package/skills/grok-multiplayer-p2p/agentsam.skill.json +0 -25
  153. package/skills/grok-multiplayer-p2p/references/react-binding.md +0 -144
  154. package/skills/grok-multiplayer-p2p/references/signaling-relay.md +0 -269
  155. package/skills/grok-neon/SKILL.md +0 -157
  156. package/skills/grok-neon/agentsam.skill.json +0 -25
  157. package/skills/grok-og/SKILL.md +0 -102
  158. package/skills/grok-og/agentsam.skill.json +0 -25
  159. package/skills/grok-og/references/brand-pass.md +0 -53
  160. package/skills/grok-og/references/custom-card.md +0 -107
  161. package/skills/grok-og/references/favicon-and-icons.md +0 -48
  162. package/skills/grok-og/references/og-type-contract.md +0 -18
  163. package/skills/grok-og/references/placeholder-card.md +0 -21
  164. package/skills/grok-og/references/x-banner.md +0 -41
  165. package/skills/grok-threejs/SKILL.md +0 -67
  166. package/skills/grok-threejs/agentsam.skill.json +0 -25
  167. package/skills/grok-threejs/references/llms-full.txt +0 -2780
  168. package/skills/grok-video2dsprite/LICENSE +0 -21
  169. package/skills/grok-video2dsprite/SKILL.md +0 -236
  170. package/skills/grok-video2dsprite/SOURCE.md +0 -6
  171. package/skills/grok-video2dsprite/agentsam.skill.json +0 -25
  172. package/skills/grok-video2dsprite/references/pipeline.md +0 -90
  173. package/skills/grok-video2dsprite/references/prompt-rules.md +0 -63
  174. package/skills/grok-video2dsprite/scripts/video2dsprite.py +0 -490
  175. package/skills/grok-xai-api/SKILL.md +0 -152
  176. package/skills/grok-xai-api/agentsam.skill.json +0 -25
  177. package/test/account-session.test.mjs +0 -101
  178. package/test/agent-capabilities.test.mjs +0 -67
  179. package/test/app-authority-context.test.mjs +0 -32
  180. package/test/app-building-skills.test.mjs +0 -61
  181. package/test/apps-scaffold-contract.test.mjs +0 -115
  182. package/test/blender-cad.test.mjs +0 -146
  183. package/test/cad-docker.test.mjs +0 -96
  184. package/test/capabilities.test.mjs +0 -133
  185. package/test/cli/api-key-env-whoami.test.mjs +0 -129
  186. package/test/cli/catalog-authority.test.mjs +0 -64
  187. package/test/cli/codebaseindex-plan-ux.test.mjs +0 -37
  188. package/test/cli/eval-live.test.mjs +0 -219
  189. package/test/cli/go.test.mjs +0 -128
  190. package/test/cli/local-vault-and-provider-setup.test.mjs +0 -268
  191. package/test/cli/machine-command.test.mjs +0 -34
  192. package/test/cli/mcp-client.test.mjs +0 -256
  193. package/test/cli/preferences-runtime.test.mjs +0 -11
  194. package/test/cli/runtime-persist.test.mjs +0 -56
  195. package/test/cli/runtime-ui.test.mjs +0 -141
  196. package/test/cli/rust-command.test.mjs +0 -21
  197. package/test/cli/skill-npm-and-env.test.mjs +0 -52
  198. package/test/cli/status-actions.test.mjs +0 -88
  199. package/test/cli/wireframes-go-registry.test.mjs +0 -139
  200. package/test/cli-preferences.test.mjs +0 -46
  201. package/test/cloudflare-connector.test.mjs +0 -93
  202. package/test/cloudflare-runtime.test.mjs +0 -82
  203. package/test/context.test.mjs +0 -159
  204. package/test/credentials-audit.test.mjs +0 -113
  205. package/test/deploy-health-scan.test.mjs +0 -95
  206. package/test/deploy-receipt.test.mjs +0 -91
  207. package/test/dockerize.test.mjs +0 -93
  208. package/test/error-diagnostics.test.mjs +0 -124
  209. package/test/eval-context.test.mjs +0 -37
  210. package/test/execution-approvals.test.mjs +0 -27
  211. package/test/fixtures/cad/courtyard-house.json +0 -208
  212. package/test/fixtures/knowledge-rpc-worker.mjs +0 -16
  213. package/test/fixtures/project-authority/customer-app/.agentsam/app.json +0 -5
  214. package/test/fixtures/project-authority/customer-app/apps/my-product/index.ts +0 -1
  215. package/test/fixtures/project-authority/customer-app/wrangler.toml +0 -17
  216. package/test/gcloud-auth.test.mjs +0 -111
  217. package/test/go/build-source-identity.test.mjs +0 -31
  218. package/test/go/cloudflare-probe.test.mjs +0 -317
  219. package/test/go/distribution.test.mjs +0 -28
  220. package/test/google-cloud-connection.test.mjs +0 -83
  221. package/test/google-cloud-inventory.test.mjs +0 -64
  222. package/test/indexing-provider.test.mjs +0 -29
  223. package/test/init-options.test.mjs +0 -35
  224. package/test/integration/ai-access-onboarding.test.mjs +0 -47
  225. package/test/integration/app-command.test.mjs +0 -85
  226. package/test/integration/autorag-project-authority.integration.test.mjs +0 -51
  227. package/test/integration/autorag-runtime-discovery.test.mjs +0 -107
  228. package/test/integration/cad-discovery.test.mjs +0 -158
  229. package/test/integration/cad-docker-executor.test.mjs +0 -249
  230. package/test/integration/cad-execution.test.mjs +0 -184
  231. package/test/integration/cad-project-app.test.mjs +0 -31
  232. package/test/integration/cad-project-d1.test.mjs +0 -25
  233. package/test/integration/cad-project-native.test.mjs +0 -24
  234. package/test/integration/cad-project-workflow.test.mjs +0 -9
  235. package/test/integration/cad-project.test.mjs +0 -85
  236. package/test/integration/cli-help.test.mjs +0 -41
  237. package/test/integration/cms-app-scaffold.test.mjs +0 -77
  238. package/test/integration/cms-site-tenancy-contract.test.mjs +0 -268
  239. package/test/integration/codebaseindex-ingest.test.mjs +0 -255
  240. package/test/integration/core-client.integration.test.mjs +0 -55
  241. package/test/integration/database-hosted-honesty.test.mjs +0 -49
  242. package/test/integration/desktop-keychain-boundary.test.mjs +0 -45
  243. package/test/integration/error-runtime-rpc.test.mjs +0 -71
  244. package/test/integration/features-resolve.test.mjs +0 -147
  245. package/test/integration/hosted-work-host-authority.test.mjs +0 -47
  246. package/test/integration/icon-registry.test.mjs +0 -67
  247. package/test/integration/ingest-discover-models.test.mjs +0 -30
  248. package/test/integration/inspect-path-cli.test.mjs +0 -46
  249. package/test/integration/install-script.test.mjs +0 -110
  250. package/test/integration/inventory-core.test.mjs +0 -53
  251. package/test/integration/inventory-eligibility.test.mjs +0 -59
  252. package/test/integration/inventory-evidence.test.mjs +0 -125
  253. package/test/integration/knowledge-rpc.test.mjs +0 -112
  254. package/test/integration/local-fs.test.mjs +0 -113
  255. package/test/integration/local-provider-bridge.test.mjs +0 -65
  256. package/test/integration/local-studio-settings-production.test.mjs +0 -40
  257. package/test/integration/lockfile-sync.test.mjs +0 -26
  258. package/test/integration/merkle-cli.test.mjs +0 -61
  259. package/test/integration/merkle-persistence-identity.test.mjs +0 -48
  260. package/test/integration/plugins-cloudflare.integration.test.mjs +0 -28
  261. package/test/integration/provider-env-cli.test.mjs +0 -60
  262. package/test/integration/provider-factory.test.mjs +0 -197
  263. package/test/integration/repository-company-graph.test.mjs +0 -90
  264. package/test/integration/runtime-migrations.test.mjs +0 -87
  265. package/test/integration/runtime-status.integration.test.mjs +0 -227
  266. package/test/integration/sam-activity-recovery.test.mjs +0 -147
  267. package/test/integration/sam-decision.test.mjs +0 -584
  268. package/test/integration/sam-kernel.test.mjs +0 -99
  269. package/test/integration/sam-planning-astar.test.mjs +0 -279
  270. package/test/integration/scaffold-cloudflare.test.mjs +0 -46
  271. package/test/integration/skill-runtime.test.mjs +0 -240
  272. package/test/integration/storage-architecture.test.mjs +0 -154
  273. package/test/integration/store-discovery.test.mjs +0 -44
  274. package/test/integration/studio-fs-pty-e2e.test.mjs +0 -294
  275. package/test/integration/terminal-exec.integration.test.mjs +0 -86
  276. package/test/integration/theme-package-registry.test.mjs +0 -31
  277. package/test/integration/tool-runtime.test.mjs +0 -124
  278. package/test/integration/tool-schema.test.mjs +0 -59
  279. package/test/jr-dev-skill.test.mjs +0 -26
  280. package/test/knowledge-context-pack.test.mjs +0 -19
  281. package/test/knowledge-postgres.test.mjs +0 -40
  282. package/test/knowledge-service.test.mjs +0 -110
  283. package/test/knowledge.test.mjs +0 -206
  284. package/test/live/terminal-transport.live.test.mjs +0 -24
  285. package/test/local-sessions.test.mjs +0 -57
  286. package/test/local-studio-deploy.test.mjs +0 -83
  287. package/test/loopback-oauth.test.mjs +0 -53
  288. package/test/merkle-render.test.mjs +0 -31
  289. package/test/mini.test.mjs +0 -140
  290. package/test/model-catalog.test.mjs +0 -70
  291. package/test/models.test.mjs +0 -309
  292. package/test/npm10-lock.test.mjs +0 -29
  293. package/test/ollama.test.mjs +0 -137
  294. package/test/open-url.test.mjs +0 -64
  295. package/test/openai-responses.test.mjs +0 -155
  296. package/test/portable-context.test.mjs +0 -94
  297. package/test/project-config.test.mjs +0 -81
  298. package/test/project-rules.test.mjs +0 -44
  299. package/test/provider-credentials.test.mjs +0 -106
  300. package/test/rehydrate.test.mjs +0 -25
  301. package/test/release-hygiene.test.mjs +0 -42
  302. package/test/repository-snapshot-view.test.mjs +0 -112
  303. package/test/responses-runner.test.mjs +0 -465
  304. package/test/runtime-activity.test.mjs +0 -98
  305. package/test/sdk-worker-contract.test.mjs +0 -72
  306. package/test/security.test.mjs +0 -266
  307. package/test/setup-planner.test.mjs +0 -74
  308. package/test/setup-runtime.test.mjs +0 -51
  309. package/test/shell.test.mjs +0 -281
  310. package/test/skills.test.mjs +0 -22
  311. package/test/smoke.mjs +0 -122
  312. package/test/telemetry.test.mjs +0 -79
  313. package/test/terminal/local-pty.mock.test.mjs +0 -153
  314. package/test/terminal-machine-identity.test.mjs +0 -88
  315. package/test/theme-portability.test.mjs +0 -14
  316. package/test/tools-search.test.mjs +0 -73
  317. package/test/unit/website-assets-bindings.test.mjs +0 -60
  318. package/test/whoami-resume.test.mjs +0 -157
@@ -1,154 +0,0 @@
1
- import assert from 'node:assert/strict';
2
- import fs from 'node:fs';
3
- import os from 'node:os';
4
- import path from 'node:path';
5
- import { spawnSync } from 'node:child_process';
6
- import test from 'node:test';
7
- import { dispatchShellLine } from '../../src/commands/shell.js';
8
- import { createLocalSession, listLocalSessions, loadLocalSession, saveLocalSession } from '../../src/lib/local-sessions.js';
9
- import { createLocalSqliteDatabaseSync } from '../../src/local/sqlite.js';
10
- import { applyRuntimeMigrationsSync } from '../../src/local/migrations.js';
11
- import { finishRuntimeRun, pruneExpiredRuntimeState, runtimeDatabasePath, startRuntimeRun } from '../../src/local/runtime-store.js';
12
-
13
- const repoRoot = path.resolve(new URL('../..', import.meta.url).pathname);
14
-
15
- function fixture(t) {
16
- const base = fs.mkdtempSync(path.join(os.tmpdir(), 'agentsam-storage-'));
17
- t.after(() => fs.rmSync(base, { recursive: true, force: true }));
18
- const home = path.join(base, 'home');
19
- const projectA = path.join(base, 'project-a');
20
- const projectB = path.join(base, 'project-b');
21
- for (const root of [projectA, projectB]) {
22
- fs.mkdirSync(path.join(root, '.agentsam'), { recursive: true });
23
- fs.writeFileSync(path.join(root, '.agentsam', 'config.json'), '{"schema_version":2}\n');
24
- }
25
- fs.mkdirSync(home);
26
- return { base, home, projectA, projectB };
27
- }
28
-
29
- test('S1: a local shell boots and migrates SQLite without cloud configuration', (t) => {
30
- const { home, projectA } = fixture(t);
31
- const env = { ...process.env, HOME: home };
32
- for (const key of ['CLOUDFLARE_API_TOKEN', 'AGENTSAM_API_KEY', 'SUPABASE_URL', 'DATABASE_URL']) delete env[key];
33
- const result = spawnSync(process.execPath, [path.join(repoRoot, 'src/cli.js'), 'shell'], {
34
- cwd: projectA, env, input: '/exit\n', encoding: 'utf8', timeout: 10_000,
35
- });
36
- assert.equal(result.status, 0, result.stderr);
37
- assert.match(result.stdout, /Interactive shell ready/);
38
- assert.equal(fs.existsSync(runtimeDatabasePath(projectA)), true);
39
- assert.equal(fs.existsSync(path.join(home, '.agentsam', 'sessions')), false);
40
- });
41
-
42
- test('S2-S4: cwd and model changes preserve project state; repository switch selects a new authority', async (t) => {
43
- const { home, projectA, projectB } = fixture(t);
44
- const child = path.join(projectA, 'src', 'agent');
45
- fs.mkdirSync(child, { recursive: true });
46
- assert.equal(runtimeDatabasePath(child), runtimeDatabasePath(projectA));
47
-
48
- const session = createLocalSession({ cwd: child, title: 'Project A' }, { home });
49
- const changedModel = saveLocalSession({ ...session, model_key: 'gemini:example' }, { home });
50
- assert.equal(changedModel.id, session.id);
51
- assert.equal(changedModel.project_root, projectA);
52
- assert.equal(loadLocalSession(session.id, { home, cwd: projectA }).model_key, 'gemini:example');
53
- assert.equal(loadLocalSession(session.id, { home, cwd: projectB }), null);
54
-
55
- const runId = await startRuntimeRun({ projectRoot: projectA, model_key: 'test:model' });
56
- let output = '';
57
- const state = { cwd: child, projectRoot: projectA, home, session: changedModel, interactive: false, write(value) { output += value; } };
58
- await dispatchShellLine('/logs', state);
59
- assert.match(output, new RegExp(runId));
60
- await dispatchShellLine(`/cd ${projectB}`, state);
61
- assert.equal(state.cwd, projectB);
62
- assert.equal(state.projectRoot, projectB);
63
- assert.notEqual(state.session.id, session.id);
64
- assert.equal(listLocalSessions({ home, cwd: projectA })[0].id, session.id);
65
- assert.equal(listLocalSessions({ home, cwd: projectB })[0].id, state.session.id);
66
- assert.equal(fs.existsSync(path.join(child, '.agentsam', 'data', 'agentsam.sqlite')), false);
67
- });
68
-
69
- test('S5: expiry cleanup removes transient digests and retains completed run receipts', async (t) => {
70
- const { projectA } = fixture(t);
71
- const runId = await startRuntimeRun({ cwd: projectA, model_key: 'test:model' });
72
- await finishRuntimeRun({ cwd: projectA, id: runId, status: 'completed' });
73
- const db = createLocalSqliteDatabaseSync(runtimeDatabasePath(projectA));
74
- db.prepare(`INSERT INTO agentsam_context_digest
75
- (id, digest_type, agent_run_id, source_hash, digest_hash, digest_text, expires_at_unix)
76
- VALUES (?, 'session', ?, 'source', 'digest', 'expired', 1)`).run('ctx_expired', runId);
77
- db.prepare('INSERT INTO agentsam_compaction_events (id, agent_run_id, expires_at_epoch) VALUES (?, ?, 1)').run('cmp_expired', runId);
78
- db.close();
79
-
80
- const pruned = await pruneExpiredRuntimeState({ cwd: projectA, nowUnix: 2 });
81
- assert.deepEqual(pruned, { digests: 1, compactions: 1, approvals_expired: 0 });
82
- const verified = createLocalSqliteDatabaseSync(runtimeDatabasePath(projectA));
83
- assert.equal(verified.prepare('SELECT status FROM agentsam_agent_run WHERE id = ?').get(runId).status, 'completed');
84
- assert.equal(verified.prepare('SELECT COUNT(*) AS n FROM agentsam_context_digest').get().n, 0);
85
- verified.close();
86
- });
87
-
88
- test('S6-S7: connected Worker bindings do not move sessions or store credential values', (t) => {
89
- const { home, projectA } = fixture(t);
90
- const workerConfig = fs.readFileSync(path.join(repoRoot, 'apps/local-studio/backend/wrangler.jsonc'), 'utf8');
91
- for (const binding of ['"d1_databases"', '"hyperdrive"', '"r2_buckets"', '"services"']) assert.match(workerConfig, new RegExp(binding));
92
- assert.doesNotMatch(workerConfig, /"durable_objects"/);
93
-
94
- const secret = 'storage_secret_do_not_persist_123';
95
- const session = createLocalSession({
96
- cwd: projectA, title: 'Safe project session', last_input: secret,
97
- provider_state: { provider: 'openai', previous_response_id: 'resp_safe', api_key: secret, messages: [{ content: secret }] },
98
- usage_snapshot: { current_context: { input_tokens: 20 }, credential: secret },
99
- last_error: { code: 'failed', message: secret },
100
- }, { home });
101
- assert.equal(session.storage.runtime, 'sqlite');
102
- assert.equal(session.storage.remote, null);
103
- assert.equal(session.storage.capabilities.remote_shared_state, false);
104
- assert.equal(session.storage.capabilities.distributed_actor_semantics, false);
105
- assert.equal(session.last_input, null);
106
- assert.equal(session.provider_state.api_key, undefined);
107
- assert.doesNotMatch(fs.readFileSync(runtimeDatabasePath(projectA), 'utf8'), new RegExp(secret));
108
- assert.equal(fs.statSync(runtimeDatabasePath(projectA)).mode & 0o777, 0o600);
109
- });
110
-
111
- test('S8: canonical migrations upgrade an older runtime database for project sessions', (t) => {
112
- const { base, home, projectA } = fixture(t);
113
- const oldMigrations = path.join(base, 'old-migrations');
114
- fs.mkdirSync(oldMigrations);
115
- fs.copyFileSync(path.join(repoRoot, 'migrations/runtime/0001_cli_runtime.sql'), path.join(oldMigrations, '0001_cli_runtime.sql'));
116
- const db = createLocalSqliteDatabaseSync(runtimeDatabasePath(projectA));
117
- assert.equal(applyRuntimeMigrationsSync(db, { migrationsDir: oldMigrations }).applied, 1);
118
- assert.equal(db.prepare("SELECT name FROM sqlite_master WHERE name = 'agentsam_project_sessions'").get(), undefined);
119
- db.close();
120
-
121
- createLocalSession({ cwd: projectA }, { home });
122
- const upgraded = createLocalSqliteDatabaseSync(runtimeDatabasePath(projectA));
123
- const migrationCount = fs.readdirSync(path.join(repoRoot, 'migrations', 'runtime'))
124
- .filter((name) => /^\d+.*\.sql$/i.test(name)).length;
125
- assert.equal(upgraded.prepare("SELECT COUNT(*) AS n FROM agentsam_schema_migrations").get().n, migrationCount);
126
- assert.ok(upgraded.prepare("SELECT name FROM sqlite_master WHERE name = 'agentsam_project_sessions'").get());
127
- upgraded.close();
128
- });
129
-
130
- test('legacy home-level JSON sessions import only into their matching project SQLite', (t) => {
131
- const { home, projectA, projectB } = fixture(t);
132
- const id = 'asess_00000000-0000-4000-8000-000000000099';
133
- const legacyDir = path.join(home, '.agentsam', 'sessions');
134
- fs.mkdirSync(legacyDir, { recursive: true });
135
- const filename = path.join(legacyDir, `${id}.json`);
136
- fs.writeFileSync(filename, JSON.stringify({
137
- schema_version: 'agentsam-local-session-v1', id, cwd: projectA,
138
- title: 'Prior work', provider_state: { provider: 'openai', api_key: 'legacy_secret' },
139
- }));
140
- assert.equal(loadLocalSession(id, { home, cwd: projectB }), null);
141
- assert.equal(fs.existsSync(runtimeDatabasePath(projectB)), false);
142
- const imported = loadLocalSession(id, { home, cwd: projectA });
143
- assert.equal(imported.project_root, projectA);
144
- assert.equal(imported.provider_state.api_key, undefined);
145
- assert.equal(fs.existsSync(filename), true);
146
- assert.doesNotMatch(fs.readFileSync(runtimeDatabasePath(projectA), 'utf8'), /legacy_secret/);
147
- });
148
-
149
- test('S9: default runtime has no Durable Object binding or dependency', () => {
150
- const pkg = JSON.parse(fs.readFileSync(path.join(repoRoot, 'package.json'), 'utf8'));
151
- assert.equal(Object.keys(pkg.dependencies || {}).some((name) => /durable.?object/i.test(name)), false);
152
- const shell = fs.readFileSync(path.join(repoRoot, 'src/commands/shell.js'), 'utf8');
153
- assert.doesNotMatch(shell, /\bDurableObject\b|\bdurable_objects\b|\bidFromName\s*\(/);
154
- });
@@ -1,44 +0,0 @@
1
- import test from 'node:test';
2
- import assert from 'node:assert/strict';
3
- import fs from 'node:fs';
4
- import os from 'node:os';
5
- import path from 'node:path';
6
- import { discoverProjectStores, selectKnowledgeStore } from '../../src/knowledge/store-discovery.js';
7
- import { initRepository, readConfig } from '../../src/knowledge/config.js';
8
- import { dispatchShellLine } from '../../src/commands/shell.js';
9
-
10
- test('store discovery reads JSONC bindings without leaking credentials or selecting application storage', async () => {
11
- const root = fs.mkdtempSync(path.join(os.tmpdir(), 'agentsam-store-discovery-'));
12
- fs.writeFileSync(path.join(root, 'package.json'), '{"name":"stores"}');
13
- fs.writeFileSync(path.join(root, 'wrangler.jsonc'), `{
14
- // Existing application authority
15
- "vars": {"PASSWORD":"do-not-emit"},
16
- "d1_databases": [{"binding":"DB","database_name":"existing-app"}],
17
- "hyperdrive": [{"binding":"HYPERDRIVE","id":"existing-pool"}],
18
- "env": {"preview":{"d1_databases":[{"binding":"DB","database_name":"preview-app"}]}}
19
- }`);
20
- const result = await discoverProjectStores(root, { DATABASE_URL: 'postgres://secret:password@private/db' });
21
- assert.equal(result.candidates.find(c => c.driver === 'd1').name, 'existing-app');
22
- assert.equal(result.candidates.find(c => c.environment === 'preview').name, 'preview-app');
23
- assert.ok(result.candidates.filter(c => c.role === 'application').every(c => c.selectable === false));
24
- assert.equal(result.candidates.find(c => c.driver === 'postgres').connection_env, 'DATABASE_URL');
25
- assert.doesNotMatch(JSON.stringify(result), /password|secret|do-not-emit/);
26
- assert.equal(fs.existsSync(path.join(root, '.agentsam')), false);
27
- let output = '';
28
- await dispatchShellLine('/db sources', { cwd: root, write: s => { output += s; }, interactive: false });
29
- assert.match(output, /existing-app/);
30
- });
31
-
32
- test('knowledge selection edits existing portable config without overwriting scope or moving runtime data', () => {
33
- const root = fs.mkdtempSync(path.join(os.tmpdir(), 'agentsam-store-selection-'));
34
- fs.writeFileSync(path.join(root, 'package.json'), '{"name":"stores"}');
35
- initRepository(root, { repositoryId: 'repo:test', include: ['src'] });
36
- selectKnowledgeStore(root, 'postgres', 'MY_DATABASE_URL');
37
- assert.deepEqual(readConfig(root).scope.include, ['src']);
38
- assert.equal(readConfig(root).storage.connection_env, 'MY_DATABASE_URL');
39
- assert.equal(fs.existsSync(path.join(root, '.agentsam/data/agentsam.sqlite')), false);
40
- assert.throws(() => selectKnowledgeStore(root, 'postgres', 'postgres://password@host/db'), /connection_env/);
41
- assert.throws(() => selectKnowledgeStore(root, 'd1'), /adapter_unavailable/);
42
- selectKnowledgeStore(root, 'sqlite');
43
- assert.deepEqual(readConfig(root).storage, { driver: 'sqlite' });
44
- });
@@ -1,294 +0,0 @@
1
- /**
2
- * Product-path filesystem ↔ PTY proof.
3
- *
4
- * Exercises the same RuntimeFilesystemAdapter contract Monaco uses, plus a
5
- * real node-pty session on the SAME authorized root, with git diff evidence.
6
- *
7
- * This is not a raw /v1/fs unit test substitute for browser Monaco chrome —
8
- * it is the Monaco adapter + real PTY identity path. Browser UI automation
9
- * is layered on top when Studio is running (see receipt).
10
- */
11
-
12
- import assert from 'node:assert/strict';
13
- import { spawnSync } from 'node:child_process';
14
- import fs from 'node:fs';
15
- import os from 'node:os';
16
- import path from 'node:path';
17
- import { after, before, describe, it } from 'node:test';
18
- import { WebSocket } from 'ws';
19
- import { startLocalPtyServer } from '../../src/local-pty/server.js';
20
- import { getRepositoryFreshness } from '../../src/local-fs/freshness.js';
21
-
22
- /**
23
- * Minimal JS twin of RuntimeFilesystemAdapter (Studio Monaco path).
24
- */
25
- class RuntimeFilesystemAdapter {
26
- constructor(baseUrl, capability) {
27
- this.baseUrl = String(baseUrl).replace(/\/$/, '');
28
- this.capability = capability;
29
- }
30
-
31
- headers(json = false) {
32
- const h = {};
33
- if (json) h['Content-Type'] = 'application/json';
34
- if (this.capability) {
35
- h.Authorization = `Bearer ${this.capability}`;
36
- h['x-agentsam-workspace-capability'] = this.capability;
37
- }
38
- return h;
39
- }
40
-
41
- async read(filePath) {
42
- const u = new URL(`${this.baseUrl}/v1/fs/read`);
43
- u.searchParams.set('path', filePath);
44
- const res = await fetch(u, { headers: this.headers() });
45
- return res.json();
46
- }
47
-
48
- async write(filePath, content, expectedVersion, overwrite = false) {
49
- const res = await fetch(`${this.baseUrl}/v1/fs/write`, {
50
- method: 'POST',
51
- headers: this.headers(true),
52
- body: JSON.stringify({ path: filePath, content, expectedVersion, overwrite }),
53
- });
54
- return res.json();
55
- }
56
- }
57
-
58
- function runGit(cwd, args) {
59
- return spawnSync('git', args, { cwd, encoding: 'utf8' });
60
- }
61
-
62
- function wait(ms) {
63
- return new Promise((r) => setTimeout(r, ms));
64
- }
65
-
66
- /**
67
- * Drive a real PTY over the local WS wire: send a command, collect output.
68
- */
69
- async function ptyExec({ url, capability, cwd, command, timeoutMs = 8000 }) {
70
- const wsUrl = `${url}/?cwd=${encodeURIComponent(cwd)}&cols=100&rows=30&capability=${encodeURIComponent(capability)}`;
71
- const ws = new WebSocket(wsUrl);
72
- let buf = '';
73
- let sessionId = null;
74
- let identity = null;
75
-
76
- ws.on('message', (data) => {
77
- const text = Buffer.isBuffer(data) ? data.toString('utf8') : String(data);
78
- if (text.startsWith('{')) {
79
- try {
80
- const msg = JSON.parse(text);
81
- if (msg.type === 'session_id') sessionId = msg.session_id;
82
- if (msg.type === 'workspace_identity') {
83
- identity = msg;
84
- sessionId = msg.session_id || sessionId;
85
- }
86
- if (msg.type === 'error') buf += `\nPTY_ERROR:${msg.code}:${msg.message}\n`;
87
- return;
88
- } catch {
89
- /* fall through */
90
- }
91
- }
92
- buf += text;
93
- });
94
-
95
- await new Promise((resolve, reject) => {
96
- const t = setTimeout(() => reject(new Error('pty_connect_timeout')), 5000);
97
- ws.on('open', () => {
98
- clearTimeout(t);
99
- resolve();
100
- });
101
- ws.on('error', (err) => {
102
- clearTimeout(t);
103
- reject(err);
104
- });
105
- });
106
-
107
- // Wait for bash prompt
108
- const promptDeadline = Date.now() + 3000;
109
- while (Date.now() < promptDeadline) {
110
- if (/[$#]\s*$/.test(buf.replace(/\x1b\[[0-9;?]*[a-zA-Z]/g, '').trimEnd()) || buf.includes('bash')) break;
111
- await wait(50);
112
- }
113
- await wait(100);
114
- ws.send(`${command}\r`);
115
- const deadline = Date.now() + timeoutMs;
116
- while (Date.now() < deadline) {
117
- await wait(150);
118
- const plain = buf.replace(/\x1b\[[0-9;?]*[a-zA-Z]/g, '');
119
- if (plain.includes('MONACO_MARK_DONE') || plain.includes('diff --git') || plain.includes('+##')) {
120
- break;
121
- }
122
- }
123
- await wait(400);
124
- ws.close();
125
- return { output: buf, sessionId, identity };
126
- }
127
-
128
- describe('Studio filesystem ↔ real PTY product proof', () => {
129
- /** @type {string} */
130
- let root;
131
- /** @type {Awaited<ReturnType<typeof startLocalPtyServer>>} */
132
- let server;
133
- /** @type {RuntimeFilesystemAdapter} */
134
- let adapter;
135
- const monacoMarker = `MONACO_MARK_${Date.now()}`;
136
- const ptyMarker = `PTY_MARK_${Date.now()}`;
137
-
138
- before(async () => {
139
- root = fs.mkdtempSync(path.join(os.tmpdir(), 'agentsam-fs-e2e-'));
140
- runGit(root, ['init']);
141
- runGit(root, ['config', 'user.email', 'agentsam-e2e@local']);
142
- runGit(root, ['config', 'user.name', 'AgentSam E2E']);
143
- fs.writeFileSync(path.join(root, 'README.md'), '# disposable\n');
144
- fs.writeFileSync(path.join(root, 'NOTES.md'), 'stable\n');
145
- runGit(root, ['add', '.']);
146
- runGit(root, ['commit', '-m', 'init']);
147
-
148
- server = await startLocalPtyServer({
149
- cwd: root,
150
- port: 0,
151
- host: '127.0.0.1',
152
- shell: '/bin/bash',
153
- });
154
- assert.ok(server.capability?.capability);
155
- assert.equal(server.cwd, path.resolve(root));
156
-
157
- // Bootstrap as Studio would
158
- const boot = await fetch(server.bootstrapUrl);
159
- const claim = await boot.json();
160
- assert.equal(claim.ok, true);
161
- assert.equal(claim.root, path.resolve(root));
162
- assert.equal(claim.capability, server.capability.capability);
163
-
164
- adapter = new RuntimeFilesystemAdapter(
165
- `http://${server.host}:${server.port}`,
166
- claim.capability,
167
- );
168
- });
169
-
170
- after(async () => {
171
- if (server) await server.close().catch(() => {});
172
- fs.rmSync(root, { recursive: true, force: true });
173
- });
174
-
175
- it('rejects FS without capability', async () => {
176
- const res = await fetch(`http://${server.host}:${server.port}/v1/fs/read?path=README.md`);
177
- const body = await res.json();
178
- assert.equal(body.ok, false);
179
- assert.equal(body.code, 'WORKSPACE_CAPABILITY_REQUIRED');
180
- });
181
-
182
- it('rejects arbitrary root claim', async () => {
183
- const outside = os.tmpdir();
184
- const res = await fetch(
185
- `${server.bootstrapUrl}?root=${encodeURIComponent(outside)}`,
186
- );
187
- const body = await res.json();
188
- assert.equal(body.ok, false);
189
- assert.equal(body.code, 'WORKSPACE_ROOT_MISMATCH');
190
- });
191
-
192
- it('Monaco adapter write → real PTY git diff sees the edit', async () => {
193
- const before = await adapter.read('README.md');
194
- assert.equal(before.ok, true);
195
- const next = `${before.content}\n## ${monacoMarker}\n`;
196
- const wrote = await adapter.write('README.md', next, before.version);
197
- assert.equal(wrote.ok, true, JSON.stringify(wrote));
198
-
199
- // Disk truth
200
- const disk = fs.readFileSync(path.join(root, 'README.md'), 'utf8');
201
- assert.ok(disk.includes(monacoMarker));
202
-
203
- // Same-root PTY git diff
204
- const pty = await ptyExec({
205
- url: server.url,
206
- capability: server.capability.capability,
207
- cwd: root,
208
- command: 'git diff -- README.md; echo MONACO_MARK_DONE',
209
- });
210
- assert.ok(pty.identity, 'workspace_identity required');
211
- assert.equal(pty.identity.root, path.resolve(root));
212
- assert.equal(pty.identity.workspace_id, server.workspace_id);
213
- assert.ok(pty.sessionId || pty.identity.session_id);
214
- assert.ok(
215
- pty.output.includes(monacoMarker) || pty.output.includes('+##'),
216
- `git diff missing Monaco edit.\nOUTPUT:\n${pty.output}`,
217
- );
218
-
219
- // Persist receipt fields on the test for the final report
220
- globalThis.__AGENTSAM_FS_E2E__ = {
221
- root,
222
- workspace_id: server.workspace_id,
223
- runtimeBaseUrl: `http://${server.host}:${server.port}`,
224
- pty_session_id: pty.sessionId || pty.identity.session_id,
225
- monaco_path: 'README.md',
226
- monaco_marker: monacoMarker,
227
- git_diff_snippet: pty.output.slice(0, 1200),
228
- identity: pty.identity,
229
- };
230
- });
231
-
232
- it('PTY disk edit → adapter detects version change → stale Monaco save conflicts', async () => {
233
- const read1 = await adapter.read('NOTES.md');
234
- assert.equal(read1.ok, true);
235
- const staleVersion = read1.version;
236
-
237
- // External change (simulates PTY echo >> NOTES.md)
238
- fs.appendFileSync(path.join(root, 'NOTES.md'), `\n${ptyMarker}\n`);
239
-
240
- const read2 = await adapter.read('NOTES.md');
241
- assert.equal(read2.ok, true);
242
- assert.notEqual(read2.version, staleVersion);
243
- assert.ok(read2.content.includes(ptyMarker));
244
-
245
- // Stale Monaco save must conflict
246
- const conflict = await adapter.write('NOTES.md', 'should not land\n', staleVersion, false);
247
- assert.equal(conflict.ok, false);
248
- assert.equal(conflict.code, 'version_conflict');
249
-
250
- // Intentional overwrite still works
251
- const force = await adapter.write('NOTES.md', `${read2.content}\nforced\n`, null, true);
252
- assert.equal(force.ok, true);
253
-
254
- globalThis.__AGENTSAM_FS_E2E__ = globalThis.__AGENTSAM_FS_E2E__ || {};
255
- globalThis.__AGENTSAM_FS_E2E__.pty_path = 'NOTES.md';
256
- globalThis.__AGENTSAM_FS_E2E__.pty_marker = ptyMarker;
257
- globalThis.__AGENTSAM_FS_E2E__.stale_conflict = conflict;
258
- globalThis.__AGENTSAM_FS_E2E__.external_version_before = staleVersion;
259
- globalThis.__AGENTSAM_FS_E2E__.external_version_after = read2.version;
260
- });
261
-
262
- it('filesystem write marks repository freshness stale (path-scoped)', () => {
263
- const fresh = getRepositoryFreshness(path.resolve(root));
264
- assert.equal(fresh.ok, true);
265
- assert.ok(fresh.generation >= 1);
266
- assert.ok(fresh.stale_paths.includes('README.md') || fresh.stale_paths.includes('NOTES.md'));
267
- });
268
-
269
- it('records final proof receipt JSON', () => {
270
- const receipt = {
271
- schema: 'agentsam.fs-e2e-receipt.v1',
272
- complete: true,
273
- ...globalThis.__AGENTSAM_FS_E2E__,
274
- authorization: {
275
- binding: '127.0.0.1',
276
- capability_required: true,
277
- cors: 'studio-origin-allowlist',
278
- arbitrary_root_claim: 'rejected',
279
- },
280
- scratch_vs_filesystem: {
281
- filesystem: 'real FS + real PTY',
282
- scratch: 'virtual files + virtual shell',
283
- silent_fallback: false,
284
- },
285
- };
286
- const out = path.join(root, '.agentsam-fs-e2e-receipt.json');
287
- // root may be deleted after — write to tmp as well
288
- const durable = path.join(os.tmpdir(), `agentsam-fs-e2e-receipt-${Date.now()}.json`);
289
- fs.writeFileSync(durable, `${JSON.stringify(receipt, null, 2)}\n`);
290
- assert.ok(fs.existsSync(durable));
291
- console.log(`\nFS_E2E_RECEIPT=${durable}\n`);
292
- globalThis.__AGENTSAM_FS_E2E_RECEIPT_PATH__ = durable;
293
- });
294
- });
@@ -1,86 +0,0 @@
1
- import assert from 'node:assert/strict';
2
- import fs from 'node:fs';
3
- import os from 'node:os';
4
- import path from 'node:path';
5
- import test from 'node:test';
6
- import { terminalExec } from '../../src/capabilities/terminal-exec.js';
7
- import { createCapabilityAdapter } from '../../src/agent/capability-adapter.js';
8
- import { toolApprovalKey } from '../../src/lib/execution-approvals.js';
9
-
10
- function root(t) {
11
- const value = fs.mkdtempSync(path.join(os.tmpdir(), 'agentsam-terminal-exec-'));
12
- t.after(() => fs.rmSync(value, { recursive: true, force: true }));
13
- return value;
14
- }
15
-
16
- test('terminal.exec runs a real argv process inside the runtime-owned project', async t => {
17
- const cwd = root(t);
18
- fs.mkdirSync(path.join(cwd, 'src'));
19
- const seen = [];
20
- const result = await terminalExec({
21
- cwd,
22
- relative_cwd: 'src',
23
- command: 'node',
24
- args: ['--version'],
25
- }, {
26
- env: { PATH: process.env.PATH, OPENAI_API_KEY: 'must-not-reach-child' },
27
- run: async (command, args, options) => {
28
- seen.push({ command, args, options });
29
- return { code: 0, signal: null, stdout: 'v24.0.0\n', stderr: '' };
30
- },
31
- });
32
- assert.equal(result.ok, true);
33
- assert.equal(result.cwd, path.join(cwd, 'src'));
34
- assert.equal(seen[0].options.env.OPENAI_API_KEY, undefined);
35
- assert.equal(seen[0].options.env.PATH, process.env.PATH);
36
- });
37
-
38
- test('terminal.exec expands a leading ~ in argv and cwd override (spawn never invokes a shell)', async t => {
39
- const home = os.homedir();
40
- const seen = [];
41
- const result = await terminalExec({
42
- cwd: process.cwd(),
43
- command: 'git',
44
- args: ['-C', '~/agentsam-sdk', 'status'],
45
- }, {
46
- env: { PATH: process.env.PATH },
47
- run: async (command, args, options) => {
48
- seen.push({ command, args, options });
49
- return { code: 0, signal: null, stdout: '', stderr: '' };
50
- },
51
- });
52
- assert.equal(result.ok, true);
53
- assert.equal(seen[0].args[1], path.join(home, 'agentsam-sdk'));
54
- assert.equal(seen[0].args[1].startsWith('~'), false);
55
- });
56
-
57
- test('terminal.exec leaves non-leading tildes and bare relative args untouched', async t => {
58
- const seen = [];
59
- await terminalExec({
60
- cwd: process.cwd(),
61
- command: 'echo',
62
- args: ['user~name', 'plain-arg'],
63
- }, {
64
- env: { PATH: process.env.PATH },
65
- run: async (command, args, options) => {
66
- seen.push({ command, args, options });
67
- return { code: 0, signal: null, stdout: '', stderr: '' };
68
- },
69
- });
70
- assert.deepEqual(seen[0].args, ['user~name', 'plain-arg']);
71
- });
72
-
73
- test('terminal.exec rejects shell strings and working-directory escape', async t => {
74
- const cwd = root(t);
75
- await assert.rejects(terminalExec({ cwd, command: 'git status', args: [] }), /pathless_executable/);
76
- await assert.rejects(terminalExec({ cwd, relative_cwd: '..', command: 'git', args: ['status'] }), /outside_project/);
77
- });
78
-
79
- test('terminal.exec is model-callable and approvals are command-specific', () => {
80
- const adapter = createCapabilityAdapter();
81
- const descriptor = adapter.toolDescriptors().find((row) => row.name === 'terminal.exec');
82
- assert.ok(descriptor);
83
- assert.equal(descriptor.risk, 'write');
84
- assert.equal(toolApprovalKey('terminal.exec', { command: 'git' }), 'terminal.exec:git');
85
- assert.equal(toolApprovalKey('terminal.exec', { command: 'npm' }), 'terminal.exec:npm');
86
- });
@@ -1,31 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import test from "node:test";
3
- import {
4
- listThemePackages,
5
- resolveThemePackage,
6
- } from "../../packages/theme-scenes/src/registry.js";
7
-
8
- test("theme registry resolves donor aliases to neutral canonical ids", () => {
9
- const companions = resolveThemePackage("companions-of-caddo");
10
- assert.equal(companions?.id, "violet");
11
- assert.equal(companions?.packageName, "@inneranimalmedia/theme-violet");
12
- assert.equal(companions?.packageNameLegacy, "@inneranimalmedia/theme-companions-site");
13
-
14
- const fuel = resolveThemePackage("fuelnfreetime");
15
- assert.equal(fuel?.id, "ember");
16
- assert.equal(fuel?.packageName, "@inneranimalmedia/theme-ember");
17
-
18
- const cypress = resolveThemePackage("cypress");
19
- assert.equal(cypress?.path, "packages/theme-church-site");
20
-
21
- const scenes = resolveThemePackage("theme-scenes");
22
- assert.equal(scenes?.kind, "scenes");
23
- });
24
-
25
- test("site themes remain distinct packages (not flattened)", () => {
26
- const sites = listThemePackages("site-theme");
27
- assert.ok(sites.length >= 7);
28
- assert.ok(sites.every((entry) => entry.path.startsWith("packages/theme-")));
29
- assert.ok(sites.every((entry) => entry.packageName.startsWith("@inneranimalmedia/theme-")));
30
- assert.ok(sites.every((entry) => !entry.packageName.includes("-site") || entry.id === "docs"));
31
- });