@tyvm/knowhow 0.0.108 → 0.0.109-dev.38b1faa

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 (236) hide show
  1. package/README.md +45 -0
  2. package/package.json +9 -4
  3. package/scripts/build-for-node.sh +10 -24
  4. package/scripts/publish.sh +86 -0
  5. package/src/agents/base/base.ts +10 -0
  6. package/src/agents/tools/execCommand.ts +49 -6
  7. package/src/agents/tools/index.ts +0 -1
  8. package/src/agents/tools/list.ts +2 -4
  9. package/src/chat/CliChatService.ts +11 -2
  10. package/src/chat/modules/AgentModule.ts +61 -31
  11. package/src/chat/modules/SessionsModule.ts +47 -3
  12. package/src/chat/modules/SystemModule.ts +2 -2
  13. package/src/chat/renderer/CompactRenderer.ts +20 -0
  14. package/src/chat/renderer/ConsoleRenderer.ts +19 -0
  15. package/src/chat/renderer/FancyRenderer.ts +19 -0
  16. package/src/chat/renderer/types.ts +11 -0
  17. package/src/cli.ts +91 -659
  18. package/src/clients/anthropic.ts +18 -17
  19. package/src/clients/index.ts +31 -11
  20. package/src/clients/openai.ts +8 -5
  21. package/src/clients/types.ts +48 -10
  22. package/src/clients/withRetry.ts +89 -0
  23. package/src/cloudWorker.ts +175 -113
  24. package/src/commands/agent.ts +246 -0
  25. package/src/commands/misc.ts +174 -0
  26. package/src/commands/modules.ts +552 -0
  27. package/src/commands/services.ts +77 -0
  28. package/src/commands/workers.ts +168 -0
  29. package/src/config.ts +38 -1
  30. package/src/fileSync.ts +70 -29
  31. package/src/hashes.ts +35 -13
  32. package/src/index.ts +18 -0
  33. package/src/logger.ts +197 -0
  34. package/src/plugins/embedding.ts +11 -6
  35. package/src/plugins/plugins.ts +0 -21
  36. package/src/plugins/vim.ts +5 -16
  37. package/src/processors/JsonCompressor.ts +6 -6
  38. package/src/services/EventService.ts +61 -1
  39. package/src/services/KnowhowClient.ts +34 -4
  40. package/src/services/MediaProcessorService.ts +4 -2
  41. package/src/services/modules/index.ts +102 -53
  42. package/src/services/modules/types.ts +6 -0
  43. package/src/tunnel.ts +216 -0
  44. package/src/types.ts +0 -1
  45. package/src/worker.ts +105 -312
  46. package/src/workers/auth/WsMiddleware.ts +99 -0
  47. package/src/workers/auth/authMiddleware.ts +104 -0
  48. package/src/workers/auth/types.ts +14 -2
  49. package/src/workers/tools/index.ts +2 -0
  50. package/src/workers/tools/reloadConfig.ts +84 -0
  51. package/tests/services/WorkerReloadConfig.test.ts +141 -0
  52. package/tests/unit/clients/AIClient.test.ts +446 -0
  53. package/tests/unit/clients/withRetry.test.ts +319 -0
  54. package/tests/unit/commands/github-credentials.test.ts +210 -0
  55. package/tests/unit/modules/moduleLoading.test.ts +39 -37
  56. package/tests/unit/plugins/pluginLoading.test.ts +0 -85
  57. package/ts_build/package.json +9 -4
  58. package/ts_build/src/agents/base/base.js +11 -0
  59. package/ts_build/src/agents/base/base.js.map +1 -1
  60. package/ts_build/src/agents/tools/execCommand.d.ts +1 -1
  61. package/ts_build/src/agents/tools/execCommand.js +39 -5
  62. package/ts_build/src/agents/tools/execCommand.js.map +1 -1
  63. package/ts_build/src/agents/tools/index.d.ts +0 -1
  64. package/ts_build/src/agents/tools/index.js +0 -1
  65. package/ts_build/src/agents/tools/index.js.map +1 -1
  66. package/ts_build/src/agents/tools/list.js +2 -4
  67. package/ts_build/src/agents/tools/list.js.map +1 -1
  68. package/ts_build/src/chat/CliChatService.js +14 -2
  69. package/ts_build/src/chat/CliChatService.js.map +1 -1
  70. package/ts_build/src/chat/modules/AgentModule.d.ts +1 -1
  71. package/ts_build/src/chat/modules/AgentModule.js +43 -20
  72. package/ts_build/src/chat/modules/AgentModule.js.map +1 -1
  73. package/ts_build/src/chat/modules/SessionsModule.js +37 -3
  74. package/ts_build/src/chat/modules/SessionsModule.js.map +1 -1
  75. package/ts_build/src/chat/modules/SystemModule.js +2 -2
  76. package/ts_build/src/chat/modules/SystemModule.js.map +1 -1
  77. package/ts_build/src/chat/renderer/CompactRenderer.d.ts +4 -0
  78. package/ts_build/src/chat/renderer/CompactRenderer.js +16 -0
  79. package/ts_build/src/chat/renderer/CompactRenderer.js.map +1 -1
  80. package/ts_build/src/chat/renderer/ConsoleRenderer.d.ts +4 -0
  81. package/ts_build/src/chat/renderer/ConsoleRenderer.js +16 -0
  82. package/ts_build/src/chat/renderer/ConsoleRenderer.js.map +1 -1
  83. package/ts_build/src/chat/renderer/FancyRenderer.d.ts +4 -0
  84. package/ts_build/src/chat/renderer/FancyRenderer.js +16 -0
  85. package/ts_build/src/chat/renderer/FancyRenderer.js.map +1 -1
  86. package/ts_build/src/chat/renderer/types.d.ts +2 -0
  87. package/ts_build/src/cli.js +47 -519
  88. package/ts_build/src/cli.js.map +1 -1
  89. package/ts_build/src/clients/anthropic.d.ts +5 -5
  90. package/ts_build/src/clients/anthropic.js +18 -17
  91. package/ts_build/src/clients/anthropic.js.map +1 -1
  92. package/ts_build/src/clients/index.js +9 -10
  93. package/ts_build/src/clients/index.js.map +1 -1
  94. package/ts_build/src/clients/openai.js +4 -4
  95. package/ts_build/src/clients/openai.js.map +1 -1
  96. package/ts_build/src/clients/types.d.ts +15 -8
  97. package/ts_build/src/clients/withRetry.d.ts +2 -0
  98. package/ts_build/src/clients/withRetry.js +60 -0
  99. package/ts_build/src/clients/withRetry.js.map +1 -0
  100. package/ts_build/src/cloudWorker.d.ts +14 -0
  101. package/ts_build/src/cloudWorker.js +105 -66
  102. package/ts_build/src/cloudWorker.js.map +1 -1
  103. package/ts_build/src/commands/agent.d.ts +6 -0
  104. package/ts_build/src/commands/agent.js +229 -0
  105. package/ts_build/src/commands/agent.js.map +1 -0
  106. package/ts_build/src/commands/misc.d.ts +10 -0
  107. package/ts_build/src/commands/misc.js +197 -0
  108. package/ts_build/src/commands/misc.js.map +1 -0
  109. package/ts_build/src/commands/modules.d.ts +3 -0
  110. package/ts_build/src/commands/modules.js +487 -0
  111. package/ts_build/src/commands/modules.js.map +1 -0
  112. package/ts_build/src/commands/services.d.ts +5 -0
  113. package/ts_build/src/commands/services.js +87 -0
  114. package/ts_build/src/commands/services.js.map +1 -0
  115. package/ts_build/src/commands/workers.d.ts +6 -0
  116. package/ts_build/src/commands/workers.js +168 -0
  117. package/ts_build/src/commands/workers.js.map +1 -0
  118. package/ts_build/src/config.d.ts +1 -0
  119. package/ts_build/src/config.js +33 -1
  120. package/ts_build/src/config.js.map +1 -1
  121. package/ts_build/src/fileSync.d.ts +6 -0
  122. package/ts_build/src/fileSync.js +50 -23
  123. package/ts_build/src/fileSync.js.map +1 -1
  124. package/ts_build/src/hashes.d.ts +2 -2
  125. package/ts_build/src/hashes.js +35 -9
  126. package/ts_build/src/hashes.js.map +1 -1
  127. package/ts_build/src/index.d.ts +1 -0
  128. package/ts_build/src/index.js +17 -1
  129. package/ts_build/src/index.js.map +1 -1
  130. package/ts_build/src/logger.d.ts +21 -0
  131. package/ts_build/src/logger.js +106 -0
  132. package/ts_build/src/logger.js.map +1 -0
  133. package/ts_build/src/plugins/embedding.js +4 -3
  134. package/ts_build/src/plugins/embedding.js.map +1 -1
  135. package/ts_build/src/plugins/plugins.d.ts +0 -2
  136. package/ts_build/src/plugins/plugins.js +0 -11
  137. package/ts_build/src/plugins/plugins.js.map +1 -1
  138. package/ts_build/src/plugins/vim.js +3 -9
  139. package/ts_build/src/plugins/vim.js.map +1 -1
  140. package/ts_build/src/processors/JsonCompressor.js +4 -4
  141. package/ts_build/src/processors/JsonCompressor.js.map +1 -1
  142. package/ts_build/src/services/EventService.d.ts +6 -1
  143. package/ts_build/src/services/EventService.js +29 -0
  144. package/ts_build/src/services/EventService.js.map +1 -1
  145. package/ts_build/src/services/KnowhowClient.d.ts +13 -1
  146. package/ts_build/src/services/KnowhowClient.js +19 -2
  147. package/ts_build/src/services/KnowhowClient.js.map +1 -1
  148. package/ts_build/src/services/MediaProcessorService.d.ts +2 -1
  149. package/ts_build/src/services/MediaProcessorService.js +3 -1
  150. package/ts_build/src/services/MediaProcessorService.js.map +1 -1
  151. package/ts_build/src/services/modules/index.d.ts +33 -0
  152. package/ts_build/src/services/modules/index.js +73 -49
  153. package/ts_build/src/services/modules/index.js.map +1 -1
  154. package/ts_build/src/services/modules/types.d.ts +6 -0
  155. package/ts_build/src/tunnel.d.ts +27 -0
  156. package/ts_build/src/tunnel.js +112 -0
  157. package/ts_build/src/tunnel.js.map +1 -0
  158. package/ts_build/src/types.d.ts +0 -1
  159. package/ts_build/src/types.js.map +1 -1
  160. package/ts_build/src/worker.d.ts +1 -4
  161. package/ts_build/src/worker.js +59 -227
  162. package/ts_build/src/worker.js.map +1 -1
  163. package/ts_build/src/workers/auth/WsMiddleware.d.ts +8 -0
  164. package/ts_build/src/workers/auth/WsMiddleware.js +65 -0
  165. package/ts_build/src/workers/auth/WsMiddleware.js.map +1 -0
  166. package/ts_build/src/workers/auth/authMiddleware.d.ts +3 -0
  167. package/ts_build/src/workers/auth/authMiddleware.js +60 -0
  168. package/ts_build/src/workers/auth/authMiddleware.js.map +1 -0
  169. package/ts_build/src/workers/auth/types.d.ts +8 -1
  170. package/ts_build/src/workers/tools/index.d.ts +2 -0
  171. package/ts_build/src/workers/tools/index.js +4 -1
  172. package/ts_build/src/workers/tools/index.js.map +1 -1
  173. package/ts_build/src/workers/tools/reloadConfig.d.ts +14 -0
  174. package/ts_build/src/workers/tools/reloadConfig.js +48 -0
  175. package/ts_build/src/workers/tools/reloadConfig.js.map +1 -0
  176. package/ts_build/tests/services/WorkerReloadConfig.test.d.ts +1 -0
  177. package/ts_build/tests/services/WorkerReloadConfig.test.js +86 -0
  178. package/ts_build/tests/services/WorkerReloadConfig.test.js.map +1 -0
  179. package/ts_build/tests/unit/clients/AIClient.test.d.ts +1 -0
  180. package/ts_build/tests/unit/clients/AIClient.test.js +339 -0
  181. package/ts_build/tests/unit/clients/AIClient.test.js.map +1 -0
  182. package/ts_build/tests/unit/clients/withRetry.test.d.ts +1 -0
  183. package/ts_build/tests/unit/clients/withRetry.test.js +225 -0
  184. package/ts_build/tests/unit/clients/withRetry.test.js.map +1 -0
  185. package/ts_build/tests/unit/commands/github-credentials.test.d.ts +1 -0
  186. package/ts_build/tests/unit/commands/github-credentials.test.js +145 -0
  187. package/ts_build/tests/unit/commands/github-credentials.test.js.map +1 -0
  188. package/ts_build/tests/unit/modules/moduleLoading.test.js +20 -26
  189. package/ts_build/tests/unit/modules/moduleLoading.test.js.map +1 -1
  190. package/ts_build/tests/unit/plugins/pluginLoading.test.js +0 -65
  191. package/ts_build/tests/unit/plugins/pluginLoading.test.js.map +1 -1
  192. package/src/agents/tools/executeScript/README.md +0 -94
  193. package/src/agents/tools/executeScript/definition.ts +0 -79
  194. package/src/agents/tools/executeScript/examples/dependency-injection-validation.ts +0 -272
  195. package/src/agents/tools/executeScript/examples/quick-test.ts +0 -74
  196. package/src/agents/tools/executeScript/examples/serialization-test.ts +0 -321
  197. package/src/agents/tools/executeScript/examples/test-runner.ts +0 -197
  198. package/src/agents/tools/executeScript/index.ts +0 -98
  199. package/src/services/script-execution/SandboxContext.ts +0 -282
  200. package/src/services/script-execution/ScriptExecutor.ts +0 -441
  201. package/src/services/script-execution/ScriptPolicy.ts +0 -194
  202. package/src/services/script-execution/ScriptTracer.ts +0 -249
  203. package/src/services/script-execution/types.ts +0 -134
  204. package/ts_build/src/agents/tools/executeScript/definition.d.ts +0 -2
  205. package/ts_build/src/agents/tools/executeScript/definition.js +0 -76
  206. package/ts_build/src/agents/tools/executeScript/definition.js.map +0 -1
  207. package/ts_build/src/agents/tools/executeScript/examples/dependency-injection-validation.d.ts +0 -18
  208. package/ts_build/src/agents/tools/executeScript/examples/dependency-injection-validation.js +0 -192
  209. package/ts_build/src/agents/tools/executeScript/examples/dependency-injection-validation.js.map +0 -1
  210. package/ts_build/src/agents/tools/executeScript/examples/quick-test.d.ts +0 -3
  211. package/ts_build/src/agents/tools/executeScript/examples/quick-test.js +0 -64
  212. package/ts_build/src/agents/tools/executeScript/examples/quick-test.js.map +0 -1
  213. package/ts_build/src/agents/tools/executeScript/examples/serialization-test.d.ts +0 -15
  214. package/ts_build/src/agents/tools/executeScript/examples/serialization-test.js +0 -266
  215. package/ts_build/src/agents/tools/executeScript/examples/serialization-test.js.map +0 -1
  216. package/ts_build/src/agents/tools/executeScript/examples/test-runner.d.ts +0 -4
  217. package/ts_build/src/agents/tools/executeScript/examples/test-runner.js +0 -208
  218. package/ts_build/src/agents/tools/executeScript/examples/test-runner.js.map +0 -1
  219. package/ts_build/src/agents/tools/executeScript/index.d.ts +0 -28
  220. package/ts_build/src/agents/tools/executeScript/index.js +0 -72
  221. package/ts_build/src/agents/tools/executeScript/index.js.map +0 -1
  222. package/ts_build/src/services/script-execution/SandboxContext.d.ts +0 -34
  223. package/ts_build/src/services/script-execution/SandboxContext.js +0 -189
  224. package/ts_build/src/services/script-execution/SandboxContext.js.map +0 -1
  225. package/ts_build/src/services/script-execution/ScriptExecutor.d.ts +0 -19
  226. package/ts_build/src/services/script-execution/ScriptExecutor.js +0 -269
  227. package/ts_build/src/services/script-execution/ScriptExecutor.js.map +0 -1
  228. package/ts_build/src/services/script-execution/ScriptPolicy.d.ts +0 -28
  229. package/ts_build/src/services/script-execution/ScriptPolicy.js +0 -115
  230. package/ts_build/src/services/script-execution/ScriptPolicy.js.map +0 -1
  231. package/ts_build/src/services/script-execution/ScriptTracer.d.ts +0 -19
  232. package/ts_build/src/services/script-execution/ScriptTracer.js +0 -186
  233. package/ts_build/src/services/script-execution/ScriptTracer.js.map +0 -1
  234. package/ts_build/src/services/script-execution/types.d.ts +0 -108
  235. package/ts_build/src/services/script-execution/types.js +0 -3
  236. package/ts_build/src/services/script-execution/types.js.map +0 -1
package/README.md CHANGED
@@ -362,3 +362,48 @@ For command usage and behavior details, see:
362
362
  - Website: https://knowhow.tyvm.ai
363
363
  - Twitter/X: https://x.com/micahriggan
364
364
  - npm: https://www.npmjs.com/package/@tyvm/knowhow
365
+
366
+ ---
367
+
368
+ ## 🚧 Dev & Nightly Releases
369
+
370
+ Knowhow publishes unstable/preview builds under separate npm dist-tags so that regular users on `latest` are never affected.
371
+
372
+ ### Installing a pre-release build
373
+
374
+ ```bash
375
+ # Latest nightly (date-stamped)
376
+ npm install -g @tyvm/knowhow@nightly
377
+
378
+ # Latest dev snapshot (git-hash-stamped)
379
+ npm install -g @tyvm/knowhow@dev
380
+
381
+ # Pin a specific pre-release version
382
+ npm install -g @tyvm/knowhow@0.0.108-nightly.20250428
383
+ ```
384
+
385
+ ### How dist-tags work
386
+
387
+ | Tag | Installed by default? | Audience |
388
+ |-----|-----------------------|---------|
389
+ | `latest` | āœ… Yes (`npm install @tyvm/knowhow`) | All stable users |
390
+ | `nightly` | āŒ No (must opt-in) | Testers / early adopters |
391
+ | `dev` | āŒ No (must opt-in) | Developers / contributors |
392
+
393
+ Publishing a `nightly` or `dev` release **never moves the `latest` tag**, so existing users won't receive unstable code through normal updates.
394
+
395
+ ### Publishing scripts (for maintainers)
396
+
397
+ ```bash
398
+ # Publish a date-stamped nightly (e.g. 0.0.108-nightly.20250428)
399
+ npm run publish:nightly
400
+
401
+ # Publish a git-hash-stamped dev snapshot (e.g. 0.0.108-dev.abc1234)
402
+ npm run publish:dev
403
+
404
+ # Publish a stable release to `latest`
405
+ npm version patch # or minor / major
406
+ npm run publish:stable
407
+ ```
408
+
409
+ > **Note:** `publish:nightly` and `publish:dev` automatically version-bump the `package.json` with a pre-release suffix before publishing and do **not** create a git tag, keeping your git history clean.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tyvm/knowhow",
3
- "version": "0.0.108",
3
+ "version": "0.0.109-dev.38b1faa",
4
4
  "description": "ai cli with plugins and agents",
5
5
  "main": "ts_build/src/index.js",
6
6
  "bin": {
@@ -21,7 +21,13 @@
21
21
  "lint": "tslint ./src/**/*.ts",
22
22
  "lint:deps": "depcheck --config=.depcheckrc",
23
23
  "lint:all": "npm run lint && npm run lint:deps",
24
- "check:model-pricing": "ts-node scripts/check-model-pricing.ts"
24
+ "check:model-pricing": "ts-node scripts/check-model-pricing.ts",
25
+ "version:nightly": "bash scripts/publish.sh version:nightly",
26
+ "version:dev": "bash scripts/publish.sh version:dev",
27
+ "publish:nightly": "bash scripts/publish.sh nightly",
28
+ "publish:dev": "bash scripts/publish.sh dev",
29
+ "publish:latest": "bash scripts/publish.sh stable",
30
+ "publish:stable": "bash scripts/publish.sh stable"
25
31
  },
26
32
  "keywords": [],
27
33
  "author": "Micah Riggan",
@@ -49,13 +55,12 @@
49
55
  "@inquirer/editor": "^4.2.18",
50
56
  "@modelcontextprotocol/sdk": "^1.13.3",
51
57
  "@simplewebauthn/server": "^13.3.0",
52
- "@tyvm/knowhow-tunnel": "0.0.4",
58
+ "@tyvm/knowhow-tunnel": "0.0.5",
53
59
  "commander": "^14.0.0",
54
60
  "diff": "^5.2.0",
55
61
  "express": "^4.19.2",
56
62
  "gitignore-to-glob": "^0.3.0",
57
63
  "glob": "11.1.0",
58
- "isolated-vm": "^5.0.4",
59
64
  "jiti": "^2.6.1",
60
65
  "marked": "^10.0.0",
61
66
  "marked-terminal": "^6.2.0",
@@ -7,11 +7,10 @@
7
7
  # This script:
8
8
  # 1. Compiles TypeScript with Node 20 (required for workspace deps)
9
9
  # 2. Creates /tmp/knowhow-node-<major> with the compiled output
10
- # 3. Installs the correct isolated-vm version for the target node in that dir
11
- # 4. Symlinks the package globally for ALL installed nvm versions matching the target
10
+ # 3. Symlinks the package globally for ALL installed nvm versions matching the target
12
11
  #
13
- # This approach avoids polluting the workspace node_modules with a different
14
- # isolated-vm ABI, so Node 20 and Node 24 builds can coexist.
12
+ # Note: isolated-vm is now in @tyvm/knowhow-module-script — install that separately
13
+ # for the correct node version if you need script execution support.
15
14
 
16
15
  set -e
17
16
 
@@ -81,23 +80,11 @@ fi
81
80
 
82
81
  # Use the last (latest patch) for building
83
82
  TARGET_NODE_BIN="${TARGET_NODE_BINS[${#TARGET_NODE_BINS[@]}-1]}"
84
- TARGET_NODE_NPM="$(dirname "$TARGET_NODE_BIN")/npm"
85
- TARGET_NODE_DIR="$(dirname "$TARGET_NODE_BIN")"
86
83
  TARGET_NODE_ACTUAL_VERSION="$("$TARGET_NODE_BIN" --version)"
87
84
 
88
85
  echo "šŸŽÆ Found Node $TARGET_VERSION installs: ${TARGET_NODE_BINS[*]}"
89
86
  echo "šŸ”Ø Building with: $TARGET_NODE_BIN ($TARGET_NODE_ACTUAL_VERSION)"
90
87
 
91
- # --- Pick the right isolated-vm version for the target node ---
92
- # isolated-vm@5.x supports Node <22, isolated-vm@6.x requires Node >=22
93
- if [ "$TARGET_MAJOR" -ge 22 ]; then
94
- IVM_VERSION="^6.0.0"
95
- echo "šŸ“Œ Using isolated-vm@6.x (Node >= 22)"
96
- else
97
- IVM_VERSION="^5.0.4"
98
- echo "šŸ“Œ Using isolated-vm@5.x (Node < 22)"
99
- fi
100
-
101
88
  # --- Create staging directory ---
102
89
  STAGING_DIR="/tmp/knowhow-node-${TARGET_MAJOR}"
103
90
  rm -rf "$STAGING_DIR"
@@ -114,13 +101,11 @@ for item in README.md LICENSE .npmignore; do
114
101
  [ -e "$PACKAGE_DIR/$item" ] && cp "$PACKAGE_DIR/$item" "$STAGING_DIR/" || true
115
102
  done
116
103
 
117
- # --- Patch package.json for target isolated-vm version ---
118
- echo "šŸ“ Patching package.json for isolated-vm $IVM_VERSION..."
104
+ # --- Patch package.json to remove workspace protocol deps ---
105
+ echo "šŸ“ Patching package.json..."
119
106
  "$NODE20_BIN" -e "
120
107
  const fs = require('fs');
121
108
  const pkg = JSON.parse(fs.readFileSync('$STAGING_DIR/package.json', 'utf8'));
122
- pkg.dependencies['isolated-vm'] = '$IVM_VERSION';
123
- // Remove workspace protocol deps that won't resolve outside the monorepo
124
109
  if (pkg.dependencies) {
125
110
  for (const [k, v] of Object.entries(pkg.dependencies)) {
126
111
  if (String(v).startsWith('workspace:')) delete pkg.dependencies[k];
@@ -130,13 +115,14 @@ echo "šŸ“ Patching package.json for isolated-vm $IVM_VERSION..."
130
115
  console.log('āœ… package.json patched');
131
116
  "
132
117
 
133
- # --- Install deps in staging dir using target node ---
118
+ # --- Install dependencies in staging dir with target Node ---
119
+ TARGET_NODE_NPM="$(dirname "$TARGET_NODE_BIN")/npm"
134
120
  echo ""
135
121
  echo "šŸ“¦ Installing dependencies in staging dir with Node $TARGET_MAJOR..."
136
122
  cd "$STAGING_DIR"
137
- # Prepend target node bin to PATH so npm/node-gyp uses the correct node version
138
- PATH="$TARGET_NODE_DIR:$PATH" "$TARGET_NODE_NPM" install --no-save 2>&1
139
- echo "āœ… Dependencies installed (isolated-vm compiled for Node $TARGET_MAJOR)"
123
+ "$TARGET_NODE_NPM" install --omit=dev
124
+ echo "āœ… Dependencies installed"
125
+ cd "$PACKAGE_DIR"
140
126
 
141
127
  # --- Symlink globally for ALL matching Node version installs ---
142
128
  PKG_NAME="$("$NODE20_BIN" -e "console.log(require('$STAGING_DIR/package.json').name)")"
@@ -0,0 +1,86 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ COMMAND="${1:-}"
5
+
6
+ get_current_version() {
7
+ node -e "process.stdout.write(require('./package.json').version)"
8
+ }
9
+
10
+ version_nightly() {
11
+ local version
12
+ version=$(get_current_version)
13
+ # Strip any existing pre-release suffix (e.g. -dev.xxx or -nightly.xxx)
14
+ version=$(echo "$version" | sed 's/-.*$//')
15
+ local stamp
16
+ stamp=$(date -u +%Y%m%d)
17
+ local pre="${version}-nightly.${stamp}"
18
+ local current
19
+ current=$(get_current_version)
20
+ if [ "$current" = "$pre" ]; then
21
+ echo "Version already set to: $pre (no change needed)"
22
+ else
23
+ echo "Bumping version to: $pre"
24
+ npm version "$pre" --no-git-tag-version
25
+ fi
26
+ }
27
+
28
+ version_dev() {
29
+ local version
30
+ version=$(get_current_version)
31
+ # Strip any existing pre-release suffix (e.g. -dev.xxx or -nightly.xxx)
32
+ version=$(echo "$version" | sed 's/-.*$//')
33
+ local hash
34
+ hash=$(git rev-parse --short HEAD)
35
+ local pre="${version}-dev.${hash}"
36
+ local current
37
+ current=$(get_current_version)
38
+ if [ "$current" = "$pre" ]; then
39
+ echo "Version already set to: $pre (no change needed)"
40
+ else
41
+ echo "Bumping version to: $pre"
42
+ npm version "$pre" --no-git-tag-version
43
+ fi
44
+ }
45
+
46
+ case "$COMMAND" in
47
+ nightly)
48
+ echo "šŸŒ™ Publishing nightly release..."
49
+ npm run compile
50
+ bash scripts/check-isolated-deps.sh
51
+ version_nightly
52
+ npm publish --tag nightly
53
+ echo "āœ… Nightly published!"
54
+ ;;
55
+ dev)
56
+ echo "šŸ”§ Publishing dev release..."
57
+ npm run compile
58
+ bash scripts/check-isolated-deps.sh
59
+ version_dev
60
+ npm publish --tag dev
61
+ echo "āœ… Dev release published!"
62
+ ;;
63
+ stable|latest)
64
+ echo "šŸš€ Publishing stable release..."
65
+ npm run compile
66
+ bash scripts/check-isolated-deps.sh
67
+ npm publish --tag latest
68
+ echo "āœ… Stable release published!"
69
+ ;;
70
+ version:nightly)
71
+ version_nightly
72
+ ;;
73
+ version:dev)
74
+ version_dev
75
+ ;;
76
+ *)
77
+ echo "Usage: $0 <nightly|dev|stable|version:nightly|version:dev>"
78
+ echo ""
79
+ echo " nightly - Compile, check deps, bump version with nightly stamp, publish to 'nightly' tag"
80
+ echo " dev - Compile, check deps, bump version with git hash, publish to 'dev' tag"
81
+ echo " stable - Compile, check deps, publish to 'latest' tag"
82
+ echo " version:nightly - Only bump version with nightly stamp (no publish)"
83
+ echo " version:dev - Only bump version with git hash (no publish)"
84
+ exit 1
85
+ ;;
86
+ esac
@@ -317,6 +317,15 @@ export abstract class BaseAgent implements IAgent {
317
317
  if (trimmed.startsWith("āœ…") || /^[\d\.\-\*]/.test(trimmed)) return true;
318
318
  }
319
319
 
320
+ // Detect JSON-wrapped finalAnswer output, e.g. {"answer":"..."} or {"finalAnswer":"..."}
321
+ try {
322
+ const parsed = JSON.parse(trimmed);
323
+ if (parsed && typeof parsed === "object") {
324
+ if (typeof parsed.answer === "string") return true;
325
+ if (typeof parsed.finalAnswer === "string") return true;
326
+ }
327
+ } catch (_) {}
328
+
320
329
  return false;
321
330
  }
322
331
 
@@ -713,6 +722,7 @@ export abstract class BaseAgent implements IAgent {
713
722
  messages,
714
723
  tools: this.getEnabledTools(),
715
724
  tool_choice: "auto",
725
+ long_ttl_cache: this.runTime() > 300_000,
716
726
  });
717
727
 
718
728
  // If the agent was paused while the completion was in-flight, wait here
@@ -3,6 +3,7 @@ import { promisify } from "util";
3
3
  import * as fs from "fs";
4
4
  import * as path from "path";
5
5
  import * as os from "os";
6
+ import { services, ToolsService } from "../../services";
6
7
 
7
8
  export const execAsync = promisify(exec);
8
9
 
@@ -256,16 +257,32 @@ const execWithTimeout = async (
256
257
  };
257
258
 
258
259
  // Public tool
259
- export const execCommand = async (
260
+ export async function execCommand(
260
261
  command: string,
261
262
  timeout?: number,
262
263
  continueInBackground?: boolean,
263
264
  logFileName?: string
264
- ): Promise<string> => {
265
- if(!command || typeof command !== "string") {
265
+ ): Promise<string> {
266
+ if (!command || typeof command !== "string") {
266
267
  throw new Error("Invalid command. We received a non-string value. Please ensure you are sending strings of 4k tokens or less.");
267
268
  }
268
269
 
270
+ // Get context from bound ToolsService (same pattern as writeFile)
271
+ const toolService = (
272
+ this instanceof ToolsService ? this : services().Tools
273
+ ) as ToolsService;
274
+ const context = toolService.getContext();
275
+
276
+ // Emit pre-run blocking event — handlers can throw to block the command
277
+ if (context.Events) {
278
+ await context.Events.emitBlocking("exec:pre-run", {
279
+ command,
280
+ timeout,
281
+ continueInBackground,
282
+ logFileName,
283
+ });
284
+ }
285
+
269
286
  const { stdout, stderr, timedOut, killed, pid, logPath } =
270
287
  await execWithTimeout(command, {
271
288
  timeout,
@@ -298,6 +315,32 @@ export const execCommand = async (
298
315
  * : "";
299
316
  */
300
317
 
301
- // return `$ ${command}${statusMsg}\n${trimmed}${trimmedMsg}`;
302
- return `$ ${command}${statusMsg}\n${output}`;
303
- };
318
+ const result = `$ ${command}${statusMsg}\n${output}`;
319
+
320
+ // Emit post-run blocking event — handlers can append extra context
321
+ let eventResults: any[] = [];
322
+ if (context.Events) {
323
+ eventResults = await context.Events.emitBlocking("exec:post-run", {
324
+ command,
325
+ timeout,
326
+ continueInBackground,
327
+ logFileName,
328
+ stdout,
329
+ stderr,
330
+ timedOut,
331
+ killed,
332
+ pid,
333
+ logPath,
334
+ output,
335
+ });
336
+ }
337
+
338
+ // Append any additional context returned by post-run handlers
339
+ let eventResultsText = "";
340
+ if (eventResults && eventResults.length > 0) {
341
+ eventResultsText =
342
+ "\n\nAdditional Information:\n" + JSON.stringify(eventResults, null, 2);
343
+ }
344
+
345
+ return result + eventResultsText;
346
+ };
@@ -20,7 +20,6 @@ export * from "./askHuman";
20
20
  export * from "./aiClient";
21
21
  export * from "./googleSearch";
22
22
  export * from "./stringReplace";
23
- export * from "./executeScript";
24
23
  export * from "./startAgentTask";
25
24
  export * from "./ycmd";
26
25
  export * from "./mcp";
@@ -6,7 +6,6 @@ import * as ycmd from "./ycmd/definitions";
6
6
  import * as language from "./language/definitions";
7
7
  import * as mcp from "./mcp/definitions";
8
8
  import { googleSearchDefinition } from "./googleSearch";
9
- import { executeScriptDefinition } from "./executeScript/definition";
10
9
  import { startAgentTaskDefinition } from "./startAgentTask";
11
10
 
12
11
  function getPluginNames(): string {
@@ -157,8 +156,8 @@ export const includedTools = [
157
156
  },
158
157
  model: {
159
158
  type: "string",
160
- description: "The model to use (default: 'gpt-4o')",
161
- default: "gpt-4o",
159
+ description: "The model to use (default: 'gpt-5.4-nano')",
160
+ default: "gpt-5.4-nano",
162
161
  },
163
162
  },
164
163
  required: ["imageUrl", "question"],
@@ -666,7 +665,6 @@ export const includedTools = [
666
665
  },
667
666
  },
668
667
  },
669
- executeScriptDefinition,
670
668
  googleSearchDefinition,
671
669
  startAgentTaskDefinition,
672
670
  ...ycmd.definitions,
@@ -19,6 +19,7 @@ import editor from "@inquirer/editor";
19
19
  import fs from "fs";
20
20
  import path from "path";
21
21
  import { services } from "../services";
22
+ import { logger } from "../logger";
22
23
 
23
24
  export class CliChatService implements ChatService {
24
25
  private context: ChatContext;
@@ -37,7 +38,7 @@ export class CliChatService implements ChatService {
37
38
  searchMode: false,
38
39
  voiceMode: false,
39
40
  multilineMode: false,
40
- currentModel: "gpt-4o",
41
+ currentModel: "gpt-5.4-nano",
41
42
  currentProvider: "openai",
42
43
  chatHistory: this.chatHistory,
43
44
  plugins,
@@ -265,7 +266,15 @@ export class CliChatService implements ChatService {
265
266
  if (this.context.voiceMode) {
266
267
  value = await voiceToText();
267
268
  } else if (this.context.multilineMode) {
268
- value = await editor({ message: prompt });
269
+ const renderer = this.context.renderer;
270
+ if (renderer) renderer.pause();
271
+ logger.silence();
272
+ try {
273
+ value = await editor({ message: prompt });
274
+ } finally {
275
+ if (renderer) renderer.resume();
276
+ logger.unsilence();
277
+ }
269
278
  this.context.multilineMode = false; // Disable after use like original
270
279
  } else {
271
280
  // Use saved input history for scrollback (InputQueueManager handles reverse access)
@@ -30,11 +30,7 @@ import {
30
30
  Base64ImageProcessor,
31
31
  } from "../../processors/index";
32
32
  import { TaskInfo } from "../types";
33
- import {
34
- createAgent,
35
- agentConstructors,
36
- AgentName,
37
- } from "../../agents";
33
+ import { createAgent, agentConstructors, AgentName } from "../../agents";
38
34
  import { ToolCallEvent } from "../../agents/base/base";
39
35
  import { KnowhowSimpleClient } from "../../services/KnowhowClient";
40
36
 
@@ -80,7 +76,6 @@ export class AgentModule extends BaseChatModule {
80
76
  this.remoteSyncModule = module;
81
77
  }
82
78
 
83
-
84
79
  getCommands(): ChatCommand[] {
85
80
  return [
86
81
  {
@@ -260,7 +255,10 @@ export class AgentModule extends BaseChatModule {
260
255
  if (context) {
261
256
  // Create a temporary agent instance to read its default model/provider
262
257
  const agentContext = services().Agents.getAgentContext();
263
- const tempAgent = createAgent(agentName as AgentName, agentContext) as BaseAgent;
258
+ const tempAgent = createAgent(
259
+ agentName as AgentName,
260
+ agentContext
261
+ ) as BaseAgent;
264
262
  context.selectedAgent = tempAgent;
265
263
  context.agentMode = true;
266
264
  context.currentAgent = agentName;
@@ -455,7 +453,6 @@ export class AgentModule extends BaseChatModule {
455
453
  const agentNames = Object.keys(agentConstructors);
456
454
 
457
455
  if (agentNames.length > 0) {
458
-
459
456
  console.log("\nAvailable agents:");
460
457
  agentNames.forEach((name) => {
461
458
  console.log(` - ${name}`);
@@ -503,39 +500,57 @@ export class AgentModule extends BaseChatModule {
503
500
  console.error(`Session ${sessionId} not found.`);
504
501
  return;
505
502
  }
506
- const lastThread = session.threads[session.threads.length - 1];
507
503
  console.log(`\nšŸ”„ Resuming session: ${sessionId}`);
508
504
  console.log(`Agent: ${session.agentName}`);
509
505
  console.log(`Original task: ${session.initialInput}`);
510
506
  console.log(`Status: ${session.status}`);
511
507
 
512
- const reason = resumeReason
513
- ? `Reason for resuming: ${resumeReason}`
514
- : "";
515
-
516
- // Create resume prompt
517
- const resumePrompt = `You are resuming a previously started task. Here's the context:
518
- ORIGINAL REQUEST:
519
- ${session.initialInput}
520
-
521
- LAST Progress State:
522
- ${JSON.stringify(lastThread)}
523
-
524
- Please continue from where you left off and complete the original request.
525
- ${reason}
526
-
527
- `;
508
+ // Build resume prompt (same pattern as resumeFromMessages)
509
+ const resumePrompt = [
510
+ "You are resuming a previously started task.",
511
+ session.initialInput ? `ORIGINAL REQUEST: ${session.initialInput}` : "",
512
+ "Please continue from where you left off and complete the original request.",
513
+ resumeReason ? `Reason for resuming: ${resumeReason}` : "",
514
+ ]
515
+ .filter(Boolean)
516
+ .join("\n");
517
+
518
+ // Restore the full message history from the last thread
519
+ const threads = session.threads || [];
520
+ // Guard against sessions saved with a flat Message[] instead of Message[][]
521
+ // (a bug where threadUpdate emitted a single thread instead of all threads)
522
+ const normalizedThreads: Message[][] = threads.length > 0 && !Array.isArray(threads[0])
523
+ ? [threads as unknown as Message[]]
524
+ : threads as Message[][];
525
+ const lastThread = normalizedThreads.length > 0 ? normalizedThreads[normalizedThreads.length - 1] : [];
526
+ const resumeMessages = [...lastThread];
527
+
528
+ // Append the resume prompt to the last user message (or add a new one)
529
+ const reversedIndex = [...lastThread]
530
+ .reverse()
531
+ .findIndex((e) => e.role === "user" && typeof e.content === "string");
532
+
533
+ if (reversedIndex === -1) {
534
+ resumeMessages.push({ role: "user", content: resumePrompt });
535
+ } else {
536
+ const actualIndex = lastThread.length - 1 - reversedIndex;
537
+ resumeMessages[actualIndex] = {
538
+ ...resumeMessages[actualIndex],
539
+ content: resumeMessages[actualIndex].content + `\n\n<Workflow>[RESUME CONTEXT]: ${resumePrompt}</Workflow>`,
540
+ };
541
+ }
528
542
 
529
543
  console.log("šŸš€ Session resuming...");
530
544
  const context = this.chatService?.getContext();
531
545
  const agentName = session.agentName || context.currentAgent;
546
+ const previousAgentMode = context?.agentMode;
532
547
 
533
548
  if (!agentName || !agentConstructors[agentName as AgentName]) {
534
549
  console.error(`Agent ${agentName} not found.`);
535
550
  return;
536
551
  }
537
552
 
538
- // Start agent with Knowhow task context if available
553
+ // Start agent with Knowhow task context and restored message history
539
554
  const { agent, taskId } = await this.setupAgent({
540
555
  agentName,
541
556
  input: resumePrompt,
@@ -544,7 +559,20 @@ Please continue from where you left off and complete the original request.
544
559
  chatHistory: [],
545
560
  run: false, // Don't run yet, we need to set up event listeners first
546
561
  });
547
- await this.attachedAgentChatLoop(taskId, agent, resumePrompt);
562
+
563
+ // After resume finishes, revert to normal chat (non-agent mode) so the
564
+ // user can start a fresh conversation instead of staying locked in agent mode.
565
+ agent.agentEvents.once(agent.eventTypes.done, () => {
566
+ const ctx = this.chatService?.getContext();
567
+ if (ctx && !previousAgentMode) {
568
+ ctx.agentMode = false;
569
+ ctx.selectedAgent = undefined;
570
+ ctx.currentAgent = undefined;
571
+ this.chatService?.disableMode("agent");
572
+ }
573
+ });
574
+
575
+ await this.attachedAgentChatLoop(taskId, agent, resumePrompt, resumeMessages);
548
576
  } catch (error) {
549
577
  console.error(
550
578
  `Failed to resume session ${sessionId}:`,
@@ -678,7 +706,7 @@ Please continue from where you left off and complete the original request.
678
706
 
679
707
  // Set up session update listener
680
708
  const threadUpdateHandler = async (threadState: any) => {
681
- this.updateSession(taskId, threadState);
709
+ this.updateSession(taskId, agent.getThreads());
682
710
  taskInfo.totalCost = agent.getTotalCostUsd();
683
711
  };
684
712
  agent.agentEvents.on(agent.eventTypes.threadUpdate, threadUpdateHandler);
@@ -1033,7 +1061,8 @@ Please continue from where you left off and complete the original request.
1033
1061
  async attachedAgentChatLoop(
1034
1062
  taskId: string,
1035
1063
  agent: AttachableAgent,
1036
- initialInput?: string
1064
+ initialInput?: string,
1065
+ initialMessages?: any[]
1037
1066
  ): Promise<void> {
1038
1067
  try {
1039
1068
  let agentFinalOutput: string | undefined;
@@ -1075,7 +1104,7 @@ Please continue from where you left off and complete the original request.
1075
1104
 
1076
1105
  if (context.chatHistory) {
1077
1106
  const found = context.chatHistory.find((h) => h.taskId === taskId);
1078
- found.output = agentFinalOutput;
1107
+ if (found) found.output = agentFinalOutput;
1079
1108
  }
1080
1109
 
1081
1110
  resolve("done");
@@ -1088,7 +1117,8 @@ Please continue from where you left off and complete the original request.
1088
1117
  if (initialInput) {
1089
1118
  const taskInfo = this.taskRegistry.get(taskId);
1090
1119
  (agent as BaseAgent).call(
1091
- taskInfo?.formattedPrompt || taskInfo?.initialInput || initialInput
1120
+ taskInfo?.formattedPrompt || taskInfo?.initialInput || initialInput,
1121
+ initialMessages
1092
1122
  );
1093
1123
  }
1094
1124
  } catch (error) {
@@ -304,15 +304,20 @@ export class SessionsModule extends BaseChatModule {
304
304
  async handleResumeCommand(args: string[]): Promise<void> {
305
305
  const sessionManager = this.agentModule.getSessionManager();
306
306
 
307
- if (args.length === 0) {
307
+ if (args.length === 0 || args[0] === "--all") {
308
308
  // Interactive: show saved sessions for selection
309
- const savedSessions = sessionManager.listAvailableSessions();
309
+ const showAll = args[0] === "--all";
310
+ const allSessions = sessionManager.listAvailableSessions();
311
+ const savedSessions = showAll ? allSessions : allSessions.slice(0, 10);
310
312
  if (savedSessions.length === 0) {
311
313
  console.log("No saved sessions found to resume.");
312
314
  return;
313
315
  }
314
316
 
315
317
  this.printSavedSessionsTable(savedSessions);
318
+ if (!showAll && allSessions.length > 10) {
319
+ console.log(` ... and ${allSessions.length - 10} more. Use /resume --all to see all sessions.`);
320
+ }
316
321
 
317
322
  const allIds = savedSessions.map((s) => s.sessionId);
318
323
  const selectedId = await this.selectByNumber(
@@ -357,8 +362,47 @@ export class SessionsModule extends BaseChatModule {
357
362
  // Check filesystem agent (may have metadata with threads)
358
363
  const fsAgentPath = path.join(".knowhow", "processes", "agents", id);
359
364
  if (fs.existsSync(fsAgentPath)) {
365
+ // Try to load threads from metadata.json and resume
366
+ const metadataPath = path.join(fsAgentPath, "metadata.json");
367
+ if (fs.existsSync(metadataPath)) {
368
+ try {
369
+ const raw = fs.readFileSync(metadataPath, "utf-8");
370
+ const metadata = JSON.parse(raw);
371
+ const threads: any[] = metadata.threads || [];
372
+ const agentName = metadata.agentName || "Developer";
373
+
374
+ // Try to get initialInput from the saved session file (more complete)
375
+ // since metadata.json doesn't always store it
376
+ const savedSession = sessionManager.loadSession(id);
377
+ const initialInput = savedSession?.initialInput || metadata.initialInput || metadata.prompt || "";
378
+
379
+ console.log(`\nšŸ“‹ Found task in filesystem: ${id}`);
380
+ console.log(` Agent : ${agentName}`);
381
+ console.log(` Task : ${initialInput}`);
382
+ console.log(` Status : ${metadata.status || "unknown"}`);
383
+
384
+ const additionalContext = await this.chatService?.getInput(
385
+ "Add any additional context for resuming this session (or press Enter to skip): "
386
+ );
387
+
388
+ // Normalize threads: if flat Message[] (old buggy format), wrap in array
389
+ const normalizedThreads = threads.length > 0 && !Array.isArray(threads[0])
390
+ ? [threads]
391
+ : threads;
392
+
393
+ await this.agentModule.resumeFromMessages({
394
+ agentName,
395
+ taskId: id,
396
+ threads: normalizedThreads,
397
+ input: additionalContext?.trim() || initialInput || "",
398
+ });
399
+ return;
400
+ } catch (e: any) {
401
+ console.error(`āš ļø Failed to load metadata for task ${id}: ${e.message}`);
402
+ }
403
+ }
360
404
  console.log(
361
- `āš ļø Task ${id} exists in the filesystem but has no saved session.\n` +
405
+ `āš ļø Task ${id} exists in the filesystem but has no saved session or metadata.\n` +
362
406
  ` Use /attach ${id} if it is still running.`
363
407
  );
364
408
  return;