@aiwg/cli 0.0.0-bootstrap.0 → 2026.7.17

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 (466) hide show
  1. package/README.md +21 -1
  2. package/bin/aiwg.mjs +349 -0
  3. package/dist/src/a2a/agent-card.js +145 -0
  4. package/dist/src/a2a/client.js +411 -0
  5. package/dist/src/a2a/hitl-cli.js +171 -0
  6. package/dist/src/a2a/hitl-driver.js +370 -0
  7. package/dist/src/a2a/hitl.js +181 -0
  8. package/dist/src/a2a/http.js +193 -0
  9. package/dist/src/a2a/jcs.js +128 -0
  10. package/dist/src/a2a/jws.js +187 -0
  11. package/dist/src/a2a/types.js +24 -0
  12. package/dist/src/a2a/webhook.js +240 -0
  13. package/dist/src/activity-log/cli.js +348 -0
  14. package/dist/src/activity-log/parser.js +81 -0
  15. package/dist/src/activity-log/types.js +50 -0
  16. package/dist/src/agents/agent-deployer.js +367 -0
  17. package/dist/src/agents/agent-packager.js +271 -0
  18. package/dist/src/agents/agent-validator.js +271 -0
  19. package/dist/src/agents/grounding/knowledge-base.js +161 -0
  20. package/dist/src/agents/grounding/types.js +12 -0
  21. package/dist/src/agents/index.js +10 -0
  22. package/dist/src/agents/packaged-agent-inventory.js +75 -0
  23. package/dist/src/agents/types.js +7 -0
  24. package/dist/src/api/index.d.ts +10 -0
  25. package/dist/src/api/index.js +10 -0
  26. package/dist/src/artifacts/address-parser.js +73 -0
  27. package/dist/src/artifacts/audit/cli.js +183 -0
  28. package/dist/src/artifacts/audit/drift.js +122 -0
  29. package/dist/src/artifacts/audit/types.js +12 -0
  30. package/dist/src/artifacts/backends/graphology-backend.js +160 -0
  31. package/dist/src/artifacts/backends/json-backend.js +129 -0
  32. package/dist/src/artifacts/backends/sqlite-backend.js +199 -0
  33. package/dist/src/artifacts/browser-export.js +622 -0
  34. package/dist/src/artifacts/capability-resolver.js +94 -0
  35. package/dist/src/artifacts/checksum-manifest.js +91 -0
  36. package/dist/src/artifacts/citation-parser.js +162 -0
  37. package/dist/src/artifacts/cli.js +1462 -0
  38. package/dist/src/artifacts/corpus-tools/citation-densify.js +311 -0
  39. package/dist/src/artifacts/corpus-tools/cli.js +393 -0
  40. package/dist/src/artifacts/corpus-tools/corpus-graph.js +225 -0
  41. package/dist/src/artifacts/corpus-tools/curator.js +186 -0
  42. package/dist/src/artifacts/corpus-tools/discovery-log.js +70 -0
  43. package/dist/src/artifacts/corpus-tools/funder-network.js +141 -0
  44. package/dist/src/artifacts/corpus-tools/induction-audit.js +233 -0
  45. package/dist/src/artifacts/corpus-tools/integrity-scan.js +202 -0
  46. package/dist/src/artifacts/corpus-tools/profile-communities.js +166 -0
  47. package/dist/src/artifacts/corpus-tools/profile-edges.js +72 -0
  48. package/dist/src/artifacts/corpus-tools/profile-embed.js +113 -0
  49. package/dist/src/artifacts/corpus-tools/profile-generate-fm.js +167 -0
  50. package/dist/src/artifacts/corpus-tools/profile-generate.js +181 -0
  51. package/dist/src/artifacts/corpus-tools/profile-metrics.js +98 -0
  52. package/dist/src/artifacts/corpus-tools/profile-status.js +86 -0
  53. package/dist/src/artifacts/corpus-tools/profile-temporal.js +139 -0
  54. package/dist/src/artifacts/corpus-tools/radar-init.js +93 -0
  55. package/dist/src/artifacts/corpus-tools/radar-report.js +118 -0
  56. package/dist/src/artifacts/corpus-tools/radar-shared.js +189 -0
  57. package/dist/src/artifacts/corpus-tools/radar-status.js +68 -0
  58. package/dist/src/artifacts/corpus-tools/sidecar-lint.js +233 -0
  59. package/dist/src/artifacts/corpus-tools/sidecar-repair.js +277 -0
  60. package/dist/src/artifacts/corpus-tools/snapshot.js +591 -0
  61. package/dist/src/artifacts/corpus-tools/source-types.js +164 -0
  62. package/dist/src/artifacts/corpus-tools/vision-extract.js +243 -0
  63. package/dist/src/artifacts/corpus-views/build.js +126 -0
  64. package/dist/src/artifacts/corpus-views/corpus-config.js +124 -0
  65. package/dist/src/artifacts/corpus-views/ref-parser.js +437 -0
  66. package/dist/src/artifacts/corpus-views/renderers.js +353 -0
  67. package/dist/src/artifacts/corpus-views/taxonomies.js +131 -0
  68. package/dist/src/artifacts/dep-graph.js +173 -0
  69. package/dist/src/artifacts/discover-facets.js +375 -0
  70. package/dist/src/artifacts/embedding-index.js +354 -0
  71. package/dist/src/artifacts/enrichment/cli.js +240 -0
  72. package/dist/src/artifacts/enrichment/prompt.js +47 -0
  73. package/dist/src/artifacts/enrichment/store.js +90 -0
  74. package/dist/src/artifacts/enrichment/types.js +12 -0
  75. package/dist/src/artifacts/fortemi-core-query-adapter.js +475 -0
  76. package/dist/src/artifacts/fortemi-core-sync.js +251 -0
  77. package/dist/src/artifacts/fortemi-shard-export.js +44 -0
  78. package/dist/src/artifacts/fulltext.js +108 -0
  79. package/dist/src/artifacts/graph-backend.js +51 -0
  80. package/dist/src/artifacts/graph-query.js +247 -0
  81. package/dist/src/artifacts/hybrid-query.js +225 -0
  82. package/dist/src/artifacts/index-builder.js +1089 -0
  83. package/dist/src/artifacts/index-reader.js +112 -0
  84. package/dist/src/artifacts/index-status.js +193 -0
  85. package/dist/src/artifacts/legacy-index-migration.js +169 -0
  86. package/dist/src/artifacts/move.js +154 -0
  87. package/dist/src/artifacts/operational-state.js +239 -0
  88. package/dist/src/artifacts/prebuilt-build-lock.js +49 -0
  89. package/dist/src/artifacts/query-engine.js +1955 -0
  90. package/dist/src/artifacts/source-graph.js +529 -0
  91. package/dist/src/artifacts/stats.js +169 -0
  92. package/dist/src/artifacts/types.js +606 -0
  93. package/dist/src/artifacts/views/cli.js +293 -0
  94. package/dist/src/artifacts/views/definition.js +170 -0
  95. package/dist/src/artifacts/views/store.js +149 -0
  96. package/dist/src/artifacts/views/types.js +8 -0
  97. package/dist/src/artifacts/watcher.js +255 -0
  98. package/dist/src/catalog/builtin-models.json +636 -0
  99. package/dist/src/catalog/cli.js +228 -0
  100. package/dist/src/catalog/cli.mjs +289 -0
  101. package/dist/src/catalog/index.js +15 -0
  102. package/dist/src/catalog/loader.js +217 -0
  103. package/dist/src/catalog/loader.mjs +251 -0
  104. package/dist/src/catalog/sources.json +41 -0
  105. package/dist/src/catalog/types.js +8 -0
  106. package/dist/src/channel/manager.mjs +504 -0
  107. package/dist/src/cli/agent-spawn.js +178 -0
  108. package/dist/src/cli/cli-extension-loader.js +245 -0
  109. package/dist/src/cli/command-log.js +275 -0
  110. package/dist/src/cli/config-loader.js +232 -0
  111. package/dist/src/cli/env.js +82 -0
  112. package/dist/src/cli/errors.js +150 -0
  113. package/dist/src/cli/find-package-root.js +40 -0
  114. package/dist/src/cli/git-hooks.js +250 -0
  115. package/dist/src/cli/handlers/agentcard.js +206 -0
  116. package/dist/src/cli/handlers/artifacts.js +76 -0
  117. package/dist/src/cli/handlers/best-practices-audit.js +280 -0
  118. package/dist/src/cli/handlers/cockpit.js +137 -0
  119. package/dist/src/cli/handlers/command-log.js +37 -0
  120. package/dist/src/cli/handlers/daemon.js +59 -0
  121. package/dist/src/cli/handlers/diagnose.js +300 -0
  122. package/dist/src/cli/handlers/execution-mode.js +105 -0
  123. package/dist/src/cli/handlers/feedback.js +341 -0
  124. package/dist/src/cli/handlers/help.js +147 -0
  125. package/dist/src/cli/handlers/index.js +302 -0
  126. package/dist/src/cli/handlers/init.js +179 -0
  127. package/dist/src/cli/handlers/install.js +129 -0
  128. package/dist/src/cli/handlers/issues.js +46 -0
  129. package/dist/src/cli/handlers/lint.js +42 -0
  130. package/dist/src/cli/handlers/local-executor.js +315 -0
  131. package/dist/src/cli/handlers/marketplace.js +168 -0
  132. package/dist/src/cli/handlers/mc.js +1010 -0
  133. package/dist/src/cli/handlers/models.js +352 -0
  134. package/dist/src/cli/handlers/packages.js +155 -0
  135. package/dist/src/cli/handlers/ralph-launcher.js +717 -0
  136. package/dist/src/cli/handlers/ralph.js +575 -0
  137. package/dist/src/cli/handlers/refresh.js +432 -0
  138. package/dist/src/cli/handlers/regenerate.js +336 -0
  139. package/dist/src/cli/handlers/repo-access.js +109 -0
  140. package/dist/src/cli/handlers/run.js +165 -0
  141. package/dist/src/cli/handlers/runtime-info.js +301 -0
  142. package/dist/src/cli/handlers/sandbox.js +197 -0
  143. package/dist/src/cli/handlers/scaffolding.js +149 -0
  144. package/dist/src/cli/handlers/script-runner.js +121 -0
  145. package/dist/src/cli/handlers/sdlc-accelerate.js +148 -0
  146. package/dist/src/cli/handlers/serve.js +1835 -0
  147. package/dist/src/cli/handlers/session.js +348 -0
  148. package/dist/src/cli/handlers/setup.js +440 -0
  149. package/dist/src/cli/handlers/skill-lint.js +367 -0
  150. package/dist/src/cli/handlers/skill-usage.js +74 -0
  151. package/dist/src/cli/handlers/steward.js +567 -0
  152. package/dist/src/cli/handlers/subcommands.js +1702 -0
  153. package/dist/src/cli/handlers/team.js +337 -0
  154. package/dist/src/cli/handlers/types.js +11 -0
  155. package/dist/src/cli/handlers/use.js +2772 -0
  156. package/dist/src/cli/handlers/utilities.js +703 -0
  157. package/dist/src/cli/handlers/version.js +134 -0
  158. package/dist/src/cli/handlers/workspace-context.js +93 -0
  159. package/dist/src/cli/handlers/workspace.js +126 -0
  160. package/dist/src/cli/help-generator.js +178 -0
  161. package/dist/src/cli/hooks/builtin/activity-log-hook.js +126 -0
  162. package/dist/src/cli/hooks/executor.js +96 -0
  163. package/dist/src/cli/hooks/index.js +15 -0
  164. package/dist/src/cli/hooks/registry.js +143 -0
  165. package/dist/src/cli/hooks/types.js +13 -0
  166. package/dist/src/cli/log.js +492 -0
  167. package/dist/src/cli/project-isolation/detect.js +70 -0
  168. package/dist/src/cli/project-isolation/index.js +7 -0
  169. package/dist/src/cli/project-isolation/signals.js +17 -0
  170. package/dist/src/cli/project-isolation/warning.js +102 -0
  171. package/dist/src/cli/prompt-utils.js +155 -0
  172. package/dist/src/cli/provider-deployment-plan.js +126 -0
  173. package/dist/src/cli/provider-resolution.js +119 -0
  174. package/dist/src/cli/router.js +305 -0
  175. package/dist/src/cli/scope-resolver.js +392 -0
  176. package/dist/src/cli/skill-usage.js +616 -0
  177. package/dist/src/cli/ui.js +224 -0
  178. package/dist/src/cli/watch-service.js +203 -0
  179. package/dist/src/cli/workflow-orchestrator.js +478 -0
  180. package/dist/src/cli/workspace-signals.js +321 -0
  181. package/dist/src/community/footer.js +14 -0
  182. package/dist/src/community/links.js +123 -0
  183. package/dist/src/community/milestones.js +46 -0
  184. package/dist/src/community/nudge-policy.js +93 -0
  185. package/dist/src/config/aiwg-config.js +883 -0
  186. package/dist/src/config/cli.js +703 -0
  187. package/dist/src/config/gitignore.js +156 -0
  188. package/dist/src/config/project-artifacts.js +76 -0
  189. package/dist/src/config/user-config.js +428 -0
  190. package/dist/src/config/user-registry.js +127 -0
  191. package/dist/src/config/workspace.js +276 -0
  192. package/dist/src/contributors/detect.js +39 -0
  193. package/dist/src/contributors/discover.js +172 -0
  194. package/dist/src/contributors/heuristic.js +186 -0
  195. package/dist/src/contributors/index.js +11 -0
  196. package/dist/src/contributors/types.js +13 -0
  197. package/dist/src/contributors/validation.js +116 -0
  198. package/dist/src/data/community.schema.json +43 -0
  199. package/dist/src/data/community.yaml +16 -0
  200. package/dist/src/extensions/capability-index.js +270 -0
  201. package/dist/src/extensions/claude-hooks-installer.js +268 -0
  202. package/dist/src/extensions/commands/definitions.js +3485 -0
  203. package/dist/src/extensions/deployment-registration.js +463 -0
  204. package/dist/src/extensions/index.js +22 -0
  205. package/dist/src/extensions/loader.js +132 -0
  206. package/dist/src/extensions/managed-marker.js +100 -0
  207. package/dist/src/extensions/manifest.js +196 -0
  208. package/dist/src/extensions/project-local-activity.js +114 -0
  209. package/dist/src/extensions/project-local-discovery.js +261 -0
  210. package/dist/src/extensions/project-local-doctor.js +281 -0
  211. package/dist/src/extensions/project-local-gitignore.js +176 -0
  212. package/dist/src/extensions/project-local-paths.js +121 -0
  213. package/dist/src/extensions/project-local-promote.js +218 -0
  214. package/dist/src/extensions/project-local-remove.js +373 -0
  215. package/dist/src/extensions/project-local-scaffold.js +252 -0
  216. package/dist/src/extensions/project-quickref.js +291 -0
  217. package/dist/src/extensions/registry.js +368 -0
  218. package/dist/src/extensions/shadow-resolver.js +271 -0
  219. package/dist/src/extensions/types.js +86 -0
  220. package/dist/src/extensions/upstream-registry.js +195 -0
  221. package/dist/src/extensions/validation.js +631 -0
  222. package/dist/src/features/catalog.js +88 -0
  223. package/dist/src/features/cli.js +197 -0
  224. package/dist/src/features/installer.js +57 -0
  225. package/dist/src/features/paths.js +14 -0
  226. package/dist/src/features/runtime.js +21 -0
  227. package/dist/src/features/status.js +134 -0
  228. package/dist/src/hooks/laziness-detection.js +532 -0
  229. package/dist/src/intake/git-history-analyzer.js +486 -0
  230. package/dist/src/intake/index.js +9 -0
  231. package/dist/src/issues/cli.js +327 -0
  232. package/dist/src/issues/index.js +5 -0
  233. package/dist/src/issues/live.js +154 -0
  234. package/dist/src/issues/local.js +666 -0
  235. package/dist/src/issues/sync.js +143 -0
  236. package/dist/src/issues/types.js +2 -0
  237. package/dist/src/issues/workflows.js +164 -0
  238. package/dist/src/kb/cli.js +173 -0
  239. package/dist/src/lint/cli.js +171 -0
  240. package/dist/src/lint/loader.js +225 -0
  241. package/dist/src/lint/reporters.js +141 -0
  242. package/dist/src/lint/runner.js +364 -0
  243. package/dist/src/lint/types.js +10 -0
  244. package/dist/src/marketplace/cache.js +110 -0
  245. package/dist/src/marketplace/identifier.js +63 -0
  246. package/dist/src/marketplace/registry.js +36 -0
  247. package/dist/src/marketplace/sources/clawhub.js +53 -0
  248. package/dist/src/marketplace/sources/git.js +45 -0
  249. package/dist/src/marketplace/types.js +9 -0
  250. package/dist/src/mcp/adapters/codex-runtime.js +224 -0
  251. package/dist/src/mcp/cli.mjs +1197 -0
  252. package/dist/src/mcp/elicitation.mjs +149 -0
  253. package/dist/src/mcp/helpers.mjs +287 -0
  254. package/dist/src/mcp/index.mjs +11 -0
  255. package/dist/src/mcp/profiles.js +263 -0
  256. package/dist/src/mcp/profiles.mjs +360 -0
  257. package/dist/src/mcp/registry.js +376 -0
  258. package/dist/src/mcp/registry.mjs +387 -0
  259. package/dist/src/mcp/server.mjs +633 -0
  260. package/dist/src/mcp/test-simple.mjs +271 -0
  261. package/dist/src/mcp/tools/command-run.mjs +101 -0
  262. package/dist/src/mcp/tools/discovery.mjs +291 -0
  263. package/dist/src/mcp/tools/interaction.mjs +87 -0
  264. package/dist/src/mcp/tools/orchestration.mjs +320 -0
  265. package/dist/src/mcp/tools/subsystems.mjs +640 -0
  266. package/dist/src/memory/cli.js +166 -0
  267. package/dist/src/memory/project-registry.js +282 -0
  268. package/dist/src/metrics/artifact-metrics.js +184 -0
  269. package/dist/src/metrics/context-budget.js +174 -0
  270. package/dist/src/metrics/token-counter.js +133 -0
  271. package/dist/src/models/config-loader.js +228 -0
  272. package/dist/src/models/index.js +22 -0
  273. package/dist/src/models/model-capabilities.v1.json +120 -0
  274. package/dist/src/models/model-catalog.v1.json +96 -0
  275. package/dist/src/models/model-discovery.js +521 -0
  276. package/dist/src/models/provider-models.js +73 -0
  277. package/dist/src/models/provider-policy.js +273 -0
  278. package/dist/src/models/resolver.js +245 -0
  279. package/dist/src/models/router.js +70 -0
  280. package/dist/src/models/types.js +8 -0
  281. package/dist/src/models/wrapper-deployment.js +178 -0
  282. package/dist/src/models/wrapper-route.js +54 -0
  283. package/dist/src/ops/cli.js +268 -0
  284. package/dist/src/ops/registry.js +623 -0
  285. package/dist/src/packages/adapters/clawhub.js +113 -0
  286. package/dist/src/packages/adapters/git.js +153 -0
  287. package/dist/src/packages/adapters/gitea.js +93 -0
  288. package/dist/src/packages/adapters/github.js +69 -0
  289. package/dist/src/packages/adapters/local-cache.js +89 -0
  290. package/dist/src/packages/package-registry.js +143 -0
  291. package/dist/src/packages/registry.js +238 -0
  292. package/dist/src/packages/types.js +11 -0
  293. package/dist/src/plugin/cross-framework-ops.js +475 -0
  294. package/dist/src/plugin/framework-config-loader.js +235 -0
  295. package/dist/src/plugin/framework-detector.js +186 -0
  296. package/dist/src/plugin/framework-isolator.js +341 -0
  297. package/dist/src/plugin/framework-migration.js +371 -0
  298. package/dist/src/plugin/metadata-validator.js +906 -0
  299. package/dist/src/plugin/plugin-installer.js +436 -0
  300. package/dist/src/plugin/plugin-status.js +369 -0
  301. package/dist/src/plugin/plugin-uninstaller.js +473 -0
  302. package/dist/src/plugin/registry-validator.js +344 -0
  303. package/dist/src/plugin/skill-command-translator.js +415 -0
  304. package/dist/src/plugin/workspace-creator.js +189 -0
  305. package/dist/src/plugin/workspace-migrator.js +821 -0
  306. package/dist/src/policy/authorization.js +390 -0
  307. package/dist/src/policy/repo-access.js +215 -0
  308. package/dist/src/provenance/cli.js +16 -0
  309. package/dist/src/providers/capability-matrix.js +300 -0
  310. package/dist/src/providers/capability-matrix.yaml +514 -0
  311. package/dist/src/providers/provider-definitions.js +977 -0
  312. package/dist/src/providers/provider-definitions.mjs +159 -0
  313. package/dist/src/providers/provider-inventory.js +169 -0
  314. package/dist/src/quality/feedback-ab.js +174 -0
  315. package/dist/src/quality/feedback-collector.js +176 -0
  316. package/dist/src/quality/feedback-tracker.js +123 -0
  317. package/dist/src/quality/patterns/adr.json +24 -0
  318. package/dist/src/quality/patterns/sad.json +24 -0
  319. package/dist/src/quality/patterns/test-plan.json +24 -0
  320. package/dist/src/quality/patterns/use-case.json +24 -0
  321. package/dist/src/quality/scoring.js +157 -0
  322. package/dist/src/reflections/cli.js +15 -0
  323. package/dist/src/release/plan.js +139 -0
  324. package/dist/src/research/cache/manager.js +227 -0
  325. package/dist/src/research/clients/arxiv.js +156 -0
  326. package/dist/src/research/clients/base.js +197 -0
  327. package/dist/src/research/clients/crossref.js +112 -0
  328. package/dist/src/research/clients/semantic-scholar.js +126 -0
  329. package/dist/src/research/clients/unpaywall.js +87 -0
  330. package/dist/src/research/index.js +14 -0
  331. package/dist/src/research/query-cli.js +336 -0
  332. package/dist/src/research/services/acquisition.js +313 -0
  333. package/dist/src/research/services/archival.js +225 -0
  334. package/dist/src/research/services/citation.js +279 -0
  335. package/dist/src/research/services/discovery.js +243 -0
  336. package/dist/src/research/services/documentation.js +269 -0
  337. package/dist/src/research/services/index.js +14 -0
  338. package/dist/src/research/services/provenance.js +279 -0
  339. package/dist/src/research/services/quality.js +370 -0
  340. package/dist/src/research/services/types.js +7 -0
  341. package/dist/src/research/storage-cli.js +20 -0
  342. package/dist/src/research/types.js +47 -0
  343. package/dist/src/resources/index.d.ts +2 -0
  344. package/dist/src/resources/index.js +2 -0
  345. package/dist/src/resources/web-release.d.ts +89 -0
  346. package/dist/src/resources/web-release.js +882 -0
  347. package/dist/src/rlm/cache/cli.js +162 -0
  348. package/dist/src/rlm/cache/hash.js +35 -0
  349. package/dist/src/rlm/cache/store.js +170 -0
  350. package/dist/src/rlm/cache/types.js +8 -0
  351. package/dist/src/rlm/cli.js +650 -0
  352. package/dist/src/serve/a2a-terminal-observer.js +120 -0
  353. package/dist/src/serve/agent-router.js +205 -0
  354. package/dist/src/serve/dispatch-router.js +171 -0
  355. package/dist/src/serve/executor-registry.js +715 -0
  356. package/dist/src/serve/mission-conductor.js +177 -0
  357. package/dist/src/serve/orchestrator-adapter.js +113 -0
  358. package/dist/src/serve/orchestrator-override.js +94 -0
  359. package/dist/src/serve/orchestrator-pty.js +133 -0
  360. package/dist/src/serve/pty-bridge.js +799 -0
  361. package/dist/src/serve/sandbox-registry.js +832 -0
  362. package/dist/src/serve/screen-reader.js +228 -0
  363. package/dist/src/serve/stack-adapters.js +68 -0
  364. package/dist/src/serve/telemetry.js +143 -0
  365. package/dist/src/skills/adapters/clawhub.js +250 -0
  366. package/dist/src/skills/adapters/local.js +335 -0
  367. package/dist/src/skills/adapters/openclaw.js +316 -0
  368. package/dist/src/skills/cli.js +334 -0
  369. package/dist/src/skills/registry.js +117 -0
  370. package/dist/src/skills/run.js +259 -0
  371. package/dist/src/skills/runtime.js +94 -0
  372. package/dist/src/skills/types.js +10 -0
  373. package/dist/src/smiths/agentsmith/demo.js +116 -0
  374. package/dist/src/smiths/agentsmith/examples.js +300 -0
  375. package/dist/src/smiths/agentsmith/generator.js +503 -0
  376. package/dist/src/smiths/agentsmith/index.js +11 -0
  377. package/dist/src/smiths/agentsmith/types.js +8 -0
  378. package/dist/src/smiths/commandsmith/example.js +220 -0
  379. package/dist/src/smiths/commandsmith/generator.js +288 -0
  380. package/dist/src/smiths/commandsmith/index.js +41 -0
  381. package/dist/src/smiths/commandsmith/templates.js +296 -0
  382. package/dist/src/smiths/commandsmith/types.js +7 -0
  383. package/dist/src/smiths/context-pipeline/aiwg-md.js +104 -0
  384. package/dist/src/smiths/context-pipeline/allowlist.js +195 -0
  385. package/dist/src/smiths/context-pipeline/claude-hook.js +127 -0
  386. package/dist/src/smiths/context-pipeline/discovery.js +187 -0
  387. package/dist/src/smiths/context-pipeline/external-links-section.js +49 -0
  388. package/dist/src/smiths/context-pipeline/finalization.js +147 -0
  389. package/dist/src/smiths/context-pipeline/generator.js +477 -0
  390. package/dist/src/smiths/context-pipeline/index.js +30 -0
  391. package/dist/src/smiths/context-pipeline/legacy-inject.js +103 -0
  392. package/dist/src/smiths/context-pipeline/managed-hook.js +95 -0
  393. package/dist/src/smiths/context-pipeline/overflow.js +212 -0
  394. package/dist/src/smiths/context-pipeline/parallelism-section.js +94 -0
  395. package/dist/src/smiths/context-pipeline/provider-policy.js +64 -0
  396. package/dist/src/smiths/context-pipeline/sanitizer.js +93 -0
  397. package/dist/src/smiths/context-pipeline/types.js +23 -0
  398. package/dist/src/smiths/context-pipeline/workspace-context.js +988 -0
  399. package/dist/src/smiths/hook-bridge/codex-translator.js +129 -0
  400. package/dist/src/smiths/hook-bridge/copilot-translator.js +64 -0
  401. package/dist/src/smiths/hook-bridge/factory-translator.js +43 -0
  402. package/dist/src/smiths/hook-bridge/hermes-translator.js +88 -0
  403. package/dist/src/smiths/hook-bridge/index.js +79 -0
  404. package/dist/src/smiths/hook-bridge/loader.js +116 -0
  405. package/dist/src/smiths/hook-bridge/shim.js +89 -0
  406. package/dist/src/smiths/hook-bridge/types.js +31 -0
  407. package/dist/src/smiths/index.js +39 -0
  408. package/dist/src/smiths/mcpsmith/analyzers/cli-analyzer.js +390 -0
  409. package/dist/src/smiths/mcpsmith/example.js +119 -0
  410. package/dist/src/smiths/mcpsmith/generator.js +236 -0
  411. package/dist/src/smiths/mcpsmith/index.js +17 -0
  412. package/dist/src/smiths/mcpsmith/types.js +10 -0
  413. package/dist/src/smiths/platform-paths.js +101 -0
  414. package/dist/src/smiths/skillsmith/codex-sidecar.js +85 -0
  415. package/dist/src/smiths/skillsmith/collision-detector.js +249 -0
  416. package/dist/src/smiths/skillsmith/demo.js +83 -0
  417. package/dist/src/smiths/skillsmith/examples.js +161 -0
  418. package/dist/src/smiths/skillsmith/generator.js +316 -0
  419. package/dist/src/smiths/skillsmith/index.js +12 -0
  420. package/dist/src/smiths/skillsmith/namespace-adapter.js +180 -0
  421. package/dist/src/smiths/skillsmith/platform-resolver.js +157 -0
  422. package/dist/src/smiths/skillsmith/types.js +9 -0
  423. package/dist/src/smiths/toolsmith/index.js +10 -0
  424. package/dist/src/smiths/toolsmith/runtime-discovery.mjs +710 -0
  425. package/dist/src/smiths/toolsmith/types.js +8 -0
  426. package/dist/src/storage/backends/fortemi.js +227 -0
  427. package/dist/src/storage/backends/fs.js +134 -0
  428. package/dist/src/storage/backends/logseq.js +309 -0
  429. package/dist/src/storage/backends/obsidian.js +233 -0
  430. package/dist/src/storage/cli.js +468 -0
  431. package/dist/src/storage/config.js +272 -0
  432. package/dist/src/storage/index.js +108 -0
  433. package/dist/src/storage/subsystem-cli.js +201 -0
  434. package/dist/src/storage/types.js +33 -0
  435. package/dist/src/testing/corpus/corpus-builder.js +383 -0
  436. package/dist/src/testing/corpus/ground-truth-manager.js +460 -0
  437. package/dist/src/testing/corpus/index.js +10 -0
  438. package/dist/src/testing/fixtures/index.js +9 -0
  439. package/dist/src/testing/fixtures/test-data-factory.js +472 -0
  440. package/dist/src/testing/generators/index.js +11 -0
  441. package/dist/src/testing/generators/test-case-generator.js +393 -0
  442. package/dist/src/testing/generators/test-code-generator.js +384 -0
  443. package/dist/src/testing/generators/use-case-parser.js +325 -0
  444. package/dist/src/testing/index.js +11 -0
  445. package/dist/src/tracker/capability-protocol.js +118 -0
  446. package/dist/src/update/checker.mjs +396 -0
  447. package/dist/src/update/notifier.mjs +235 -0
  448. package/dist/src/writing/content-diversifier.js +804 -0
  449. package/dist/src/writing/example-generator.js +959 -0
  450. package/dist/src/writing/example-templates.js +99 -0
  451. package/dist/src/writing/pattern-library.js +486 -0
  452. package/dist/src/writing/patterns/banned-phrases.json +4413 -0
  453. package/dist/src/writing/patterns/formulaic-structures.json +1300 -0
  454. package/dist/src/writing/patterns/generic-adjectives.json +1510 -0
  455. package/dist/src/writing/patterns/hedging-language.json +2096 -0
  456. package/dist/src/writing/patterns/transition-words.json +1285 -0
  457. package/dist/src/writing/patterns/weak-verbs.json +1112 -0
  458. package/dist/src/writing/prompt-optimizer.js +660 -0
  459. package/dist/src/writing/prompt-templates.js +583 -0
  460. package/dist/src/writing/scoring-config-loader.js +168 -0
  461. package/dist/src/writing/validation-engine.js +595 -0
  462. package/dist/src/writing/validation-rules.js +380 -0
  463. package/dist/src/writing/voice-analyzer.js +439 -0
  464. package/dist/src/writing/voice-calibration.js +661 -0
  465. package/dist/src/writing/voice-profiles.json +588 -0
  466. package/package.json +64 -9
@@ -0,0 +1,799 @@
1
+ /**
2
+ * WebSocket PTY Bridge
3
+ *
4
+ * Bridges browser WebSocket connections to PTY sessions. Three modes:
5
+ *
6
+ * Phase 1 (local exec): spawns commands using node-pty.
7
+ * Phase 2a (sandbox WS): bridges to agentic-sandbox management WebSocket.
8
+ * Connects to the sandbox's management WS server, subscribes to an agent,
9
+ * starts an interactive shell, and multicasts PTY output to all browser clients.
10
+ * Leverages the sandbox's tokio broadcast channel for zero-copy multicast.
11
+ * Phase 2b (sandbox REST, deprecated): polls /api/v1/tasks log endpoint.
12
+ *
13
+ * Browser ↔ serve protocol:
14
+ * Client → Server: { type: 'data', payload: string } — stdin to PTY
15
+ * Client → Server: { type: 'resize', cols: number, rows: number }
16
+ * Client → Server: { type: 'close' } — request graceful shutdown
17
+ * Server → Client: { type: 'data', payload: string } — stdout/stderr from PTY
18
+ * Server → Client: { type: 'exit', code: number } — PTY process exited
19
+ * Server → Client: { type: 'error', message: string } — error notification
20
+ *
21
+ * Sandbox management WS protocol (agentic-sandbox):
22
+ * → { type: 'subscribe', agent_id }
23
+ * → { type: 'start_shell', agent_id, cols, rows }
24
+ * → { type: 'list_sessions', agent_id } — resolves session_name (#901)
25
+ * → { type: 'send_input', agent_id, command_id, data }
26
+ * → { type: 'pty_resize', agent_id, command_id, cols, rows }
27
+ * → { type: 'kill_session', agent_id, session_name } — must use session_name, not command_id
28
+ * ← { type: 'shell_started', agent_id, command_id } — idempotent: same cmd_id on reconnect (#903)
29
+ * ← { type: 'session_list', agent_id, sessions[] } — provides session_name for kill (#901)
30
+ * ← { type: 'output', agent_id, command_id, stream, data, ts, seq? }
31
+ * ← { type: 'session_killed' | 'session_detached', agent_id, exit_code? }
32
+ *
33
+ * Replay-on-attach (#1144). After `shell_started`, AIWG sends:
34
+ * → { type: 'join_session', agent_id, command_id, replay_from }
35
+ * and the sandbox emits buffered output frames carrying `seq`. AIWG prepends
36
+ * `\x1bc` (full terminal reset) ahead of the first replay frame so xterm.js
37
+ * agrees with tmux on the starting cursor/screen state, eliminating the
38
+ * first-frame alignment glitch. On unexpected disconnect, the bridge resumes
39
+ * from the last observed seq instead of replaying from zero.
40
+ *
41
+ * Capability negotiation (operator review on #1144, 2026-05-07): the original
42
+ * implementation gated this strictly on the sandbox advertising `join_session`
43
+ * + `replay_buffer` in a server-hello banner (agentic-sandbox#190). That
44
+ * banner doesn't ship today, so the gate kept replay disabled even on
45
+ * sandboxes that fully support it. The bridge now uses an opportunistic
46
+ * probe: if the banner explicitly signals support, replay is enabled; if the
47
+ * banner is absent, the bridge sends `join_session` anyway and treats an
48
+ * `error` response with an "unknown" / "not supported" / "session not found"
49
+ * message as the negative signal. Either path is logged so an operator can
50
+ * tell from `aiwg serve` output which mode the bridge is running in.
51
+ *
52
+ * @issue #712
53
+ * @issue #1144 — replay session history when AIWG attaches to running session
54
+ * @see #657 — agentic-sandbox PTY transport
55
+ * @see #711 — HTTP server scaffold
56
+ */
57
+ import { loadFeaturePackage } from '../features/runtime.js';
58
+ // ============================================================
59
+ // Constants
60
+ // ============================================================
61
+ const OUTPUT_BUFFER_MAX = 64 * 1024; // 64 KB replay buffer per session
62
+ const SESSION_TTL_MS = 30_000; // 30 s before orphaned session is cleaned up
63
+ // ============================================================
64
+ // Session Registry
65
+ // ============================================================
66
+ export class PtySessionRegistry {
67
+ sessions = new Map();
68
+ cleanupTimer = null;
69
+ constructor() {
70
+ // Periodically clean up orphaned sessions
71
+ this.cleanupTimer = setInterval(() => this.evictExpired(), SESSION_TTL_MS);
72
+ if (this.cleanupTimer.unref)
73
+ this.cleanupTimer.unref();
74
+ }
75
+ get(id) {
76
+ return this.sessions.get(id);
77
+ }
78
+ create(id) {
79
+ const session = {
80
+ id,
81
+ clients: new Map(),
82
+ outputBuffer: '',
83
+ pty: null,
84
+ lastDisconnect: 0,
85
+ exited: false,
86
+ };
87
+ this.sessions.set(id, session);
88
+ return session;
89
+ }
90
+ delete(id) {
91
+ const session = this.sessions.get(id);
92
+ if (session?.pty) {
93
+ try {
94
+ session.pty.kill();
95
+ }
96
+ catch { /* ignore */ }
97
+ }
98
+ this.sessions.delete(id);
99
+ }
100
+ addClient(sessionId, clientId, ws) {
101
+ const session = this.sessions.get(sessionId);
102
+ if (session)
103
+ session.clients.set(clientId, ws);
104
+ }
105
+ removeClient(sessionId, clientId) {
106
+ const session = this.sessions.get(sessionId);
107
+ if (!session)
108
+ return;
109
+ session.clients.delete(clientId);
110
+ if (session.clients.size === 0) {
111
+ session.lastDisconnect = Date.now();
112
+ }
113
+ }
114
+ appendOutput(sessionId, data) {
115
+ const session = this.sessions.get(sessionId);
116
+ if (!session)
117
+ return;
118
+ session.outputBuffer += data;
119
+ if (session.outputBuffer.length > OUTPUT_BUFFER_MAX) {
120
+ session.outputBuffer = session.outputBuffer.slice(-OUTPUT_BUFFER_MAX);
121
+ }
122
+ }
123
+ broadcast(sessionId, msg) {
124
+ const session = this.sessions.get(sessionId);
125
+ if (!session)
126
+ return;
127
+ const text = JSON.stringify(msg);
128
+ for (const ws of session.clients.values()) {
129
+ if (ws.readyState === 1 /* OPEN */) {
130
+ try {
131
+ ws.send(text);
132
+ }
133
+ catch { /* ignore closed sockets */ }
134
+ }
135
+ }
136
+ }
137
+ evictExpired() {
138
+ const cutoff = Date.now() - SESSION_TTL_MS;
139
+ for (const [id, session] of this.sessions) {
140
+ const orphaned = session.clients.size === 0 && session.lastDisconnect < cutoff;
141
+ if (orphaned || session.exited) {
142
+ this.delete(id);
143
+ }
144
+ }
145
+ }
146
+ shutdown() {
147
+ if (this.cleanupTimer)
148
+ clearInterval(this.cleanupTimer);
149
+ for (const id of this.sessions.keys()) {
150
+ this.delete(id);
151
+ }
152
+ }
153
+ }
154
+ // Singleton registry shared across WebSocket connections
155
+ export const registry = new PtySessionRegistry();
156
+ // ============================================================
157
+ // PTY Spawn
158
+ // ============================================================
159
+ /**
160
+ * Spawn a PTY process for the given session.
161
+ *
162
+ * Priority order:
163
+ * 1. Explicit wsEndpoint opt → sandbox management WebSocket bridge
164
+ * 2. Auto-detect: first connected sandbox in registry → sandbox WS bridge
165
+ * 3. AIWG_SANDBOX_ENDPOINT env var → legacy REST polling (deprecated)
166
+ * 4. Fallback → local node-pty
167
+ *
168
+ * @issue #657 — agentic-sandbox PTY transport
169
+ */
170
+ export async function spawnPty(session, command, args, opts = {}) {
171
+ // Phase 2a: explicit management WS endpoint
172
+ if (opts.wsEndpoint) {
173
+ const agentId = opts.agentId || process.env.AIWG_SANDBOX_AGENT_ID || 'agent-01';
174
+ // Look up advertised capabilities for this endpoint so spawnSandboxWsPty
175
+ // can capability-gate the replay-on-attach handshake (#1144).
176
+ let capabilities;
177
+ try {
178
+ const { sandboxRegistry } = await import('./sandbox-registry.js');
179
+ const match = sandboxRegistry.list().find((s) => s.wsEndpoint === opts.wsEndpoint);
180
+ capabilities = match?.wsCapabilities;
181
+ }
182
+ catch { /* registry not available — proceed without capability gating */ }
183
+ await spawnSandboxWsPty(session, agentId, opts.wsEndpoint, { ...opts, capabilities });
184
+ return;
185
+ }
186
+ // Phase 2b: auto-detect — use first connected sandbox from registry
187
+ try {
188
+ const { sandboxRegistry } = await import('./sandbox-registry.js');
189
+ const sandboxes = sandboxRegistry.list();
190
+ const connected = sandboxes.find((s) => s.connected && s.wsEndpoint);
191
+ if (connected) {
192
+ // #1146: refuse silent fallback when multiple ready agents exist and the
193
+ // caller has not chosen one. Single-agent case still auto-selects.
194
+ const explicit = opts.agentId || process.env.AIWG_SANDBOX_AGENT_ID;
195
+ const ready = connected.agents.filter((a) => a.status === 'ready');
196
+ if (!explicit && ready.length > 1) {
197
+ const ids = ready.map((a) => a.agentId).join(', ');
198
+ throw new Error(`Sandbox '${connected.name}' has ${ready.length} ready agents (${ids}); ` +
199
+ `pass ?agent=<agentId> on /ws/pty/:sessionId or set AIWG_SANDBOX_AGENT_ID.`);
200
+ }
201
+ const agentId = explicit
202
+ || ready[0]?.agentId
203
+ || connected.agents[0]?.agentId
204
+ || 'agent-01';
205
+ await spawnSandboxWsPty(session, agentId, connected.wsEndpoint, {
206
+ ...opts,
207
+ capabilities: connected.wsCapabilities,
208
+ });
209
+ return;
210
+ }
211
+ }
212
+ catch (err) {
213
+ // #1146: if we threw above to demand explicit selection, propagate it.
214
+ if (err instanceof Error && err.message.startsWith('Sandbox '))
215
+ throw err;
216
+ /* registry not available — fall through to local PTY */
217
+ }
218
+ // Phase 2c: legacy REST-based sandbox (AIWG_SANDBOX_ENDPOINT env var — deprecated)
219
+ const sandboxEndpoint = opts.sandboxEndpoint || process.env.AIWG_SANDBOX_ENDPOINT;
220
+ if (sandboxEndpoint) {
221
+ await spawnSandboxPty(session, command, args, {
222
+ ...opts,
223
+ sandboxEndpoint,
224
+ agentId: opts.agentId || process.env.AIWG_SANDBOX_AGENT_ID || 'agent-01',
225
+ });
226
+ return;
227
+ }
228
+ // Phase 1: local exec via node-pty
229
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
230
+ let ptyMod;
231
+ try {
232
+ const loaded = await loadFeaturePackage('node-pty');
233
+ ptyMod = loaded.default ?? loaded;
234
+ }
235
+ catch {
236
+ throw new Error('node-pty is required for local PTY sessions. Install it with scoped approval: aiwg features install pty');
237
+ }
238
+ const pty = ptyMod.spawn(command, args, {
239
+ name: 'xterm-256color',
240
+ cols: opts.cols ?? 120,
241
+ rows: opts.rows ?? 30,
242
+ cwd: opts.cwd ?? process.cwd(),
243
+ env: process.env,
244
+ });
245
+ session.pty = pty;
246
+ pty.onData((data) => {
247
+ registry.appendOutput(session.id, data);
248
+ registry.broadcast(session.id, { type: 'data', payload: data });
249
+ });
250
+ pty.onExit(({ exitCode }) => {
251
+ session.exited = true;
252
+ registry.broadcast(session.id, { type: 'exit', code: exitCode });
253
+ });
254
+ }
255
+ /**
256
+ * Spawn a PTY session bridged to an agentic-sandbox management WebSocket.
257
+ *
258
+ * Connects to the sandbox's management WS server at wsEndpoint, subscribes to
259
+ * the target agent, starts an interactive shell, then multicasts PTY output to
260
+ * all browser clients via the existing PtySessionRegistry broadcast channel.
261
+ *
262
+ * The sandbox uses an OutputAggregator that broadcasts all output for an agent_id
263
+ * to every subscribed WS connection. Multiple aiwg bridges sharing the same PTY
264
+ * session receive identical output streams — no extra overhead on the sandbox side.
265
+ * (#903: secondary start_shell for an existing session returns the same command_id
266
+ * as the primary — the PTY is ref-counted and survives until the last subscriber
267
+ * disconnects. The output filter `command_id === commandId` remains correct in all
268
+ * cases whether this is the first or a subsequent attach.)
269
+ *
270
+ * After shell_started, list_sessions is issued to resolve the human-readable
271
+ * session_name (e.g. "main") needed for kill_session. (#901)
272
+ *
273
+ * If the WS connection drops after handshake, the bridge reconnects with exponential
274
+ * backoff rather than marking the session exited. The tmux session on the VM is
275
+ * preserved as long as at least one subscriber is attached. (#902)
276
+ *
277
+ * Future: once aiwg-serve needs operator control (pause/inspect/hand-off), use the
278
+ * formal session protocol keyed on session_id from list_sessions:
279
+ * JoinSession { session_id, role: "controller" | "observer" }
280
+ * SessionInput { session_id, data }
281
+ * SessionResize { session_id, cols, rows }
282
+ * RequestControl / ReleaseControl
283
+ * This enables role-gated stdin and replay from a sequence number. (#904)
284
+ *
285
+ * @issue #657
286
+ */
287
+ async function spawnSandboxWsPty(session, agentId, wsEndpoint, opts) {
288
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
289
+ let wsMod;
290
+ try {
291
+ wsMod = await (new Function('m', 'return import(m)'))('ws');
292
+ }
293
+ catch {
294
+ try {
295
+ // @ts-expect-error Optional runtime dependency; this workspace does not ship @types/ws.
296
+ wsMod = await import('ws');
297
+ }
298
+ catch {
299
+ throw new Error('ws package required for sandbox PTY bridge. Run: npm install ws');
300
+ }
301
+ }
302
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
303
+ const WS = wsMod.WebSocket ?? wsMod.default?.WebSocket ?? wsMod.default;
304
+ if (typeof WS !== 'function') {
305
+ throw new Error('Could not resolve WebSocket constructor from ws package');
306
+ }
307
+ const cols = opts.cols ?? 120;
308
+ const rows = opts.rows ?? 30;
309
+ // Replay-on-attach negotiation (#1144). Three states:
310
+ // "advertised" — banner explicitly lists join_session + replay_buffer.
311
+ // "probe" — no banner present; we'll send join_session opportunistically
312
+ // and watch for an "unknown message" / "not supported" error
313
+ // to demote to "unsupported" if the sandbox can't handle it.
314
+ // "unsupported"— banner advertises capabilities but join_session is absent
315
+ // (rare — would mean a partial banner). Skip entirely.
316
+ //
317
+ // Default behavior is opportunistic: if the operator hasn't supplied a
318
+ // capability map, or it lists no client messages, we still try. This keeps
319
+ // the replay path useful on sandboxes that support join_session without
320
+ // shipping the server-hello banner from agentic-sandbox#190.
321
+ let replayMode;
322
+ if (!opts.capabilities) {
323
+ replayMode = 'probe';
324
+ }
325
+ else if (opts.capabilities.supported_client_messages.includes('join_session') &&
326
+ opts.capabilities.features.includes('replay_buffer')) {
327
+ replayMode = 'advertised';
328
+ }
329
+ else if (opts.capabilities.supported_client_messages.length === 0 &&
330
+ opts.capabilities.features.length === 0) {
331
+ // Sandbox supplied an empty banner — treat as no-banner-yet, probe.
332
+ replayMode = 'probe';
333
+ }
334
+ else {
335
+ // Banner present but missing the bits we care about — don't probe; the
336
+ // sandbox has explicitly told us it doesn't support replay.
337
+ replayMode = 'unsupported';
338
+ }
339
+ console.info(`[pty-bridge] sandbox WS ${wsEndpoint} (agent=${agentId}): replay ${replayMode === 'advertised'
340
+ ? 'enabled (banner)'
341
+ : replayMode === 'probe'
342
+ ? 'enabled (opportunistic — no banner advertised)'
343
+ : 'disabled (banner explicitly omits join_session/replay_buffer)'}`);
344
+ return new Promise((resolve, reject) => {
345
+ let commandId = null;
346
+ // session_name resolved via list_sessions after shell_started (#901)
347
+ // The kill_session handler looks up by session_name, not command_id.
348
+ let sessionName = null;
349
+ let onDataCb = null;
350
+ let onExitCb = null;
351
+ let settled = false;
352
+ let reconnectAttempt = 0;
353
+ const MAX_RECONNECT = 8;
354
+ // #1144: persist last seen sequence number so reconnects resume from gap point
355
+ let lastSeq = 0;
356
+ // #1144: prepend a single xterm full-reset before the first replay frame so
357
+ // the renderer's cursor/screen state agrees with tmux on its starting point.
358
+ // Without the reset, mid-conversation bytes overlap whatever was on screen
359
+ // and produce the alignment glitch described in #1144.
360
+ let needsReplayReset = false;
361
+ // sock is reassigned on each reconnect; sendMsg always uses the current one
362
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
363
+ let sock = null;
364
+ const settle = (err) => {
365
+ if (settled)
366
+ return;
367
+ settled = true;
368
+ if (err)
369
+ reject(err);
370
+ else
371
+ resolve();
372
+ };
373
+ const sendMsg = (msg) => {
374
+ if (sock?.readyState === 1 /* OPEN */) {
375
+ sock.send(JSON.stringify(msg));
376
+ }
377
+ };
378
+ function connect() {
379
+ sock = new WS(wsEndpoint);
380
+ sock.on('open', () => {
381
+ // Subscribe then start (or re-attach to) the interactive shell.
382
+ // After sandbox#ce8e600, start_shell for an existing session returns
383
+ // the same command_id (idempotent attach) — no new PTY is spawned. (#903)
384
+ sendMsg({ type: 'subscribe', agent_id: agentId });
385
+ sendMsg({ type: 'start_shell', agent_id: agentId, cols, rows });
386
+ });
387
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
388
+ sock.on('message', (raw) => {
389
+ let msg;
390
+ try {
391
+ msg = JSON.parse(typeof raw === 'string' ? raw : raw.toString());
392
+ }
393
+ catch {
394
+ return;
395
+ }
396
+ // Shell handshake — record command_id for all subsequent I/O routing.
397
+ // On reconnect, the server returns the same command_id (idempotent). (#903)
398
+ if (msg['type'] === 'shell_started' && msg['agent_id'] === agentId) {
399
+ commandId = msg['command_id'];
400
+ // Resolve the outer promise on first shell_started only
401
+ settle();
402
+ // Request session list to resolve the session_name needed for kill (#901)
403
+ sendMsg({ type: 'list_sessions', agent_id: agentId });
404
+ // #1144: ask the sandbox to replay buffered output so a late attach
405
+ // sees existing session history. On the initial attach lastSeq is 0
406
+ // (full replay); on reconnect lastSeq is the last frame we saw, so
407
+ // we resume from the gap rather than re-rendering history.
408
+ if (replayMode !== 'unsupported') {
409
+ needsReplayReset = true;
410
+ sendMsg({
411
+ type: 'join_session',
412
+ agent_id: agentId,
413
+ command_id: commandId,
414
+ replay_from: lastSeq,
415
+ });
416
+ }
417
+ return;
418
+ }
419
+ // Probe-mode error handling (#1144): if the sandbox doesn't recognize
420
+ // the join_session message, demote to 'unsupported' so the renderer
421
+ // doesn't keep waiting for a replay reset that's never going to come,
422
+ // and log so the operator can see why replay didn't take effect.
423
+ if (msg['type'] === 'error') {
424
+ const errMsg = String(msg['message'] ?? '').toLowerCase();
425
+ const looksLikeUnsupported = errMsg.includes('unknown message') ||
426
+ errMsg.includes('unsupported') ||
427
+ errMsg.includes('not supported') ||
428
+ errMsg.includes('session not found') ||
429
+ errMsg.includes('unknown type');
430
+ if (looksLikeUnsupported &&
431
+ (replayMode === 'probe' || replayMode === 'advertised')) {
432
+ console.warn(`[pty-bridge] sandbox declined join_session for agent=${agentId}: "${msg['message']}" — demoting to unsupported, falling back to live-only`);
433
+ replayMode = 'unsupported';
434
+ // Cancel the pending reset — there will be no replay frame to
435
+ // prepend `\x1bc` to.
436
+ needsReplayReset = false;
437
+ }
438
+ // Other error types are forwarded as-is to the existing handler
439
+ // chain (no action here — pre-existing behavior).
440
+ return;
441
+ }
442
+ // session_list response: find our session by command_id, store session_name (#901)
443
+ if (msg['type'] === 'session_list' && msg['agent_id'] === agentId) {
444
+ const sessions = msg['sessions'];
445
+ const match = sessions?.find((s) => s.command_id === commandId);
446
+ if (match)
447
+ sessionName = match.session_name;
448
+ return;
449
+ }
450
+ // PTY output from the sandbox broadcast channel → forward to browser
451
+ if (msg['type'] === 'output' &&
452
+ msg['agent_id'] === agentId &&
453
+ msg['command_id'] === commandId &&
454
+ (msg['stream'] === 'stdout' || msg['stream'] === undefined) &&
455
+ msg['data']) {
456
+ let data = typeof msg['data'] === 'string' ? msg['data'] : String(msg['data']);
457
+ // #1144: track sandbox-supplied seq so reconnects can resume from
458
+ // the gap point rather than replaying from the buffer head.
459
+ const seq = msg['seq'];
460
+ if (typeof seq === 'number' && seq > lastSeq)
461
+ lastSeq = seq;
462
+ // #1144: emit a full xterm reset (`\x1bc`) ahead of the first frame
463
+ // following a replay request so the renderer agrees with tmux on
464
+ // the starting state. Eliminates the alignment glitch where
465
+ // mid-session bytes overlap the prior screen content.
466
+ if (needsReplayReset) {
467
+ data = '\x1bc' + data;
468
+ needsReplayReset = false;
469
+ }
470
+ onDataCb?.(data);
471
+ return;
472
+ }
473
+ // Session ended on sandbox side (explicit kill or process exit)
474
+ if ((msg['type'] === 'session_killed' || msg['type'] === 'session_detached') &&
475
+ msg['agent_id'] === agentId) {
476
+ onExitCb?.({ exitCode: msg['exit_code'] ?? 0 });
477
+ }
478
+ });
479
+ sock.on('error', (_err) => {
480
+ // If not yet settled, the 'close' event will follow and settle with an error.
481
+ // After settlement, let 'close' drive reconnection.
482
+ });
483
+ sock.on('close', () => {
484
+ if (!settled) {
485
+ // Closed before shell_started — fatal for the initial connect
486
+ settle(new Error(`Sandbox WS closed before shell_started (agent: ${agentId}, endpoint: ${wsEndpoint})`));
487
+ return;
488
+ }
489
+ if (session.exited)
490
+ return; // already torn down intentionally
491
+ // Unexpected mid-session disconnect — reconnect with exponential backoff. (#902)
492
+ // The tmux session on the VM survives as long as we reconnect before the last
493
+ // subscriber drops (sandbox ref-counts WS subscribers per PTY).
494
+ if (reconnectAttempt < MAX_RECONNECT) {
495
+ reconnectAttempt++;
496
+ const delay = Math.min(1_000 * Math.pow(2, reconnectAttempt), 30_000);
497
+ setTimeout(connect, delay);
498
+ }
499
+ else {
500
+ // Exhausted retries — treat as session exit
501
+ onExitCb?.({ exitCode: 1 });
502
+ }
503
+ });
504
+ // 15 s timeout for the initial shell handshake only (not applied on reconnects)
505
+ if (!settled) {
506
+ const timeout = setTimeout(() => settle(new Error(`Timed out waiting for shell_started from agent ${agentId} at ${wsEndpoint}`)), 15_000);
507
+ if (timeout.unref) {
508
+ timeout.unref();
509
+ }
510
+ }
511
+ }
512
+ // PtyLike wrapper: routes browser I/O back through the sandbox management WS
513
+ const sandboxPty = {
514
+ write(data) {
515
+ if (!commandId)
516
+ return;
517
+ sendMsg({ type: 'send_input', agent_id: agentId, command_id: commandId, data });
518
+ },
519
+ resize(c, r) {
520
+ if (!commandId)
521
+ return;
522
+ sendMsg({ type: 'pty_resize', agent_id: agentId, command_id: commandId, cols: c, rows: r });
523
+ },
524
+ kill(_signal) {
525
+ // Kill by session_name — the sandbox KillSession handler looks up by name,
526
+ // not by command_id. session_name is resolved from list_sessions. (#901)
527
+ if (sessionName) {
528
+ sendMsg({ type: 'kill_session', agent_id: agentId, session_name: sessionName });
529
+ }
530
+ sock?.close();
531
+ },
532
+ onData(cb) { onDataCb = cb; },
533
+ onExit(cb) { onExitCb = cb; },
534
+ };
535
+ session.pty = sandboxPty;
536
+ // Wire output → broadcast pipeline (mirrors the local PTY pattern)
537
+ sandboxPty.onData((data) => {
538
+ registry.appendOutput(session.id, data);
539
+ registry.broadcast(session.id, { type: 'data', payload: data });
540
+ });
541
+ sandboxPty.onExit(({ exitCode }) => {
542
+ session.exited = true;
543
+ registry.broadcast(session.id, { type: 'exit', code: exitCode });
544
+ });
545
+ connect();
546
+ });
547
+ }
548
+ /**
549
+ * Spawn a PTY session on a remote agentic-sandbox instance.
550
+ * Submits a task and polls for log output via REST.
551
+ *
552
+ * @deprecated Use spawnSandboxWsPty (management WebSocket) instead.
553
+ * The REST polling approach requires a /api/v1/tasks endpoint that the
554
+ * agentic-sandbox management server does not expose. Left as fallback for
555
+ * custom sandbox implementations that implement the task REST API.
556
+ * @issue #657
557
+ */
558
+ async function spawnSandboxPty(session, command, args, opts) {
559
+ const endpoint = opts.sandboxEndpoint.replace(/\/$/, '');
560
+ const cmdLine = [command, ...args].join(' ');
561
+ // Submit task manifest
562
+ const manifest = {
563
+ manifest_yaml: [
564
+ 'version: "1"',
565
+ 'kind: Task',
566
+ 'metadata:',
567
+ ` name: "pty-${session.id}"`,
568
+ ' labels:',
569
+ ' aiwg_transport: pty',
570
+ ` aiwg_session: "${session.id}"`,
571
+ 'claude:',
572
+ ` prompt: "${cmdLine}"`,
573
+ ' headless: true',
574
+ ' skip_permissions: true',
575
+ 'vm:',
576
+ ' profile: agentic-dev',
577
+ ].join('\n'),
578
+ };
579
+ const resp = await fetch(`${endpoint}/api/v1/tasks`, {
580
+ method: 'POST',
581
+ headers: { 'Content-Type': 'application/json' },
582
+ body: JSON.stringify(manifest),
583
+ });
584
+ if (!resp.ok) {
585
+ const body = await resp.text().catch(() => '');
586
+ throw new Error(`Sandbox task submission failed: ${resp.status} ${body}`);
587
+ }
588
+ const result = await resp.json();
589
+ const taskId = result.task_id;
590
+ // Create a PtyLike wrapper that routes I/O through the sandbox REST API
591
+ let logOffset = 0;
592
+ let stopped = false;
593
+ const sandboxPty = {
594
+ write(data) {
595
+ if (stopped)
596
+ return;
597
+ fetch(`${endpoint}/api/v1/tasks/${taskId}`, {
598
+ method: 'PATCH',
599
+ headers: { 'Content-Type': 'application/json' },
600
+ body: JSON.stringify({ stdin: data }),
601
+ }).catch(() => { });
602
+ },
603
+ resize(_cols, _rows) {
604
+ // Resize handled by browser WS direct connection to sandbox :8121
605
+ },
606
+ kill(_signal) {
607
+ if (stopped)
608
+ return;
609
+ stopped = true;
610
+ fetch(`${endpoint}/api/v1/tasks/${taskId}`, { method: 'DELETE' })
611
+ .catch(() => { });
612
+ },
613
+ onData(callback) {
614
+ // Poll for log output
615
+ const timer = setInterval(async () => {
616
+ if (stopped) {
617
+ clearInterval(timer);
618
+ return;
619
+ }
620
+ try {
621
+ const logResp = await fetch(`${endpoint}/api/v1/tasks/${taskId}/logs?offset=${logOffset}`);
622
+ if (!logResp.ok) {
623
+ if (logResp.status === 404) {
624
+ stopped = true;
625
+ clearInterval(timer);
626
+ }
627
+ return;
628
+ }
629
+ const text = await logResp.text();
630
+ if (text.length > 0) {
631
+ logOffset += text.length;
632
+ callback(text);
633
+ }
634
+ }
635
+ catch { /* retry next poll */ }
636
+ }, 500);
637
+ },
638
+ onExit(callback) {
639
+ // Poll for task completion
640
+ const timer = setInterval(async () => {
641
+ if (stopped) {
642
+ clearInterval(timer);
643
+ return;
644
+ }
645
+ try {
646
+ const statusResp = await fetch(`${endpoint}/api/v1/tasks/${taskId}`);
647
+ if (!statusResp.ok)
648
+ return;
649
+ const task = await statusResp.json();
650
+ if (['completed', 'failed', 'cancelled'].includes(task.state)) {
651
+ stopped = true;
652
+ clearInterval(timer);
653
+ callback({ exitCode: task.state === 'completed' ? 0 : 1 });
654
+ }
655
+ }
656
+ catch { /* retry */ }
657
+ }, 2000);
658
+ },
659
+ };
660
+ session.pty = sandboxPty;
661
+ sandboxPty.onData((data) => {
662
+ registry.appendOutput(session.id, data);
663
+ registry.broadcast(session.id, { type: 'data', payload: data });
664
+ });
665
+ sandboxPty.onExit(({ exitCode }) => {
666
+ session.exited = true;
667
+ registry.broadcast(session.id, { type: 'exit', code: exitCode });
668
+ });
669
+ }
670
+ // ============================================================
671
+ // WebSocket Connection Handler
672
+ // ============================================================
673
+ let clientCounter = 0;
674
+ /**
675
+ * Handle a new WebSocket connection for a PTY session.
676
+ *
677
+ * @param sessionId - PTY session ID from the URL path
678
+ * @param ws - WebSocket-like interface
679
+ * @param command - Command to spawn (default: 'aiwg')
680
+ * @param args - Command arguments (default: ['mc', 'watch'])
681
+ * @param cwd - Working directory
682
+ * @param wsEndpoint - Optional: sandbox management WS URL for explicit agent targeting
683
+ * @param agentId - Optional: sandbox agent ID to target (requires wsEndpoint)
684
+ */
685
+ export async function handlePtyConnection(sessionId, ws, command = 'aiwg', cmdArgs = ['mc', 'watch'], cwd, wsEndpoint, agentId) {
686
+ const clientId = `client-${++clientCounter}`;
687
+ let session = registry.get(sessionId);
688
+ if (!session) {
689
+ // New session — create and spawn PTY
690
+ session = registry.create(sessionId);
691
+ registry.addClient(sessionId, clientId, ws);
692
+ try {
693
+ await spawnPty(session, command, cmdArgs, { cwd, wsEndpoint, agentId });
694
+ }
695
+ catch (err) {
696
+ const msg = err instanceof Error ? err.message : String(err);
697
+ ws.send(JSON.stringify({ type: 'error', message: msg }));
698
+ ws.close(1011, 'PTY spawn failed');
699
+ registry.delete(sessionId);
700
+ return;
701
+ }
702
+ }
703
+ else if (!session.exited) {
704
+ // Reconnect to existing session — replay buffer
705
+ registry.addClient(sessionId, clientId, ws);
706
+ if (session.outputBuffer) {
707
+ // Trim replay to start from the last full-screen erase so that tmux's
708
+ // screen-init sequences (cursor moves, status-bar paint) from before the
709
+ // erase don't render as literal garbage in a fresh xterm.js context.
710
+ // Everything before \x1b[2J would be cleared by the erase anyway;
711
+ // everything after is the session content tmux redrew (MOTD, history, etc).
712
+ // If no erase is found, replay the whole buffer unchanged.
713
+ const ERASE = '\x1b[2J';
714
+ const lastErase = session.outputBuffer.lastIndexOf(ERASE);
715
+ const replay = lastErase !== -1 ? session.outputBuffer.slice(lastErase) : session.outputBuffer;
716
+ ws.send(JSON.stringify({ type: 'data', payload: replay }));
717
+ }
718
+ }
719
+ else {
720
+ // Session exited — inform client
721
+ ws.send(JSON.stringify({ type: 'exit', code: 0 }));
722
+ ws.close(1000, 'Session already exited');
723
+ return;
724
+ }
725
+ // Handle incoming messages from browser
726
+ const onMessage = (rawData) => {
727
+ let msg;
728
+ try {
729
+ msg = JSON.parse(rawData);
730
+ }
731
+ catch {
732
+ return; // ignore malformed frames
733
+ }
734
+ const s = registry.get(sessionId);
735
+ if (!s?.pty)
736
+ return;
737
+ if (msg.type === 'data' && msg.payload !== undefined) {
738
+ s.pty.write(msg.payload);
739
+ }
740
+ else if (msg.type === 'resize' && msg.cols && msg.rows) {
741
+ s.pty.resize(msg.cols, msg.rows);
742
+ }
743
+ else if (msg.type === 'close') {
744
+ s.pty.kill();
745
+ }
746
+ };
747
+ const onClose = () => {
748
+ registry.removeClient(sessionId, clientId);
749
+ };
750
+ return Promise.resolve().then(() => {
751
+ // Return handlers for integration with WebSocket framework
752
+ ws._onMessage = onMessage;
753
+ ws._onClose = onClose;
754
+ });
755
+ }
756
+ // ============================================================
757
+ // Hono WebSocket Factory
758
+ // ============================================================
759
+ /**
760
+ * Create a Hono-compatible WebSocket event object for the PTY route.
761
+ *
762
+ * Used with `upgradeWebSocket` from `@hono/node-server/ws`:
763
+ *
764
+ * ```ts
765
+ * app.get('/ws/pty/:sessionId', upgradeWebSocket((c) => createPtyWsHandler(c)));
766
+ * ```
767
+ */
768
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
769
+ export function createPtyWsHandler(c) {
770
+ const sessionId = c.req.param('sessionId') ?? 'default';
771
+ const q = c.req.query();
772
+ const command = q.command ?? 'aiwg';
773
+ const cmdArgs = q.args ? q.args.split(',') : ['mc', 'watch'];
774
+ const cwd = q.cwd;
775
+ // Explicit sandbox targeting (optional — auto-detected from registry when absent)
776
+ const wsEndpoint = q.wsEndpoint;
777
+ const agentId = q.agentId;
778
+ let wsRef = null;
779
+ return {
780
+ onOpen(_evt, ws) {
781
+ wsRef = ws;
782
+ handlePtyConnection(sessionId, ws, command, cmdArgs, cwd, wsEndpoint, agentId).catch((err) => {
783
+ ws.send(JSON.stringify({ type: 'error', message: String(err) }));
784
+ ws.close(1011);
785
+ });
786
+ },
787
+ onMessage(evt) {
788
+ wsRef?._onMessage?.(evt.data);
789
+ },
790
+ onClose() {
791
+ wsRef?._onClose?.();
792
+ wsRef = null;
793
+ },
794
+ onError(err) {
795
+ console.error(`[pty-bridge] WebSocket error on session ${sessionId}:`, err);
796
+ },
797
+ };
798
+ }
799
+ //# sourceMappingURL=pty-bridge.js.map