@code-yeongyu/senpi 2026.8.29 → 2026.8.30

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 (200) hide show
  1. package/CHANGELOG.md +66 -0
  2. package/dist/core/agent-session.d.ts +45 -1
  3. package/dist/core/agent-session.d.ts.map +1 -1
  4. package/dist/core/agent-session.js +312 -25
  5. package/dist/core/agent-session.js.map +1 -1
  6. package/dist/core/bash-executor.d.ts.map +1 -1
  7. package/dist/core/bash-executor.js +155 -37
  8. package/dist/core/bash-executor.js.map +1 -1
  9. package/dist/core/credential-accounts.d.ts.map +1 -1
  10. package/dist/core/credential-accounts.js +23 -9
  11. package/dist/core/credential-accounts.js.map +1 -1
  12. package/dist/core/credential-pool/env-slots.d.ts.map +1 -1
  13. package/dist/core/credential-pool/env-slots.js +2 -0
  14. package/dist/core/credential-pool/env-slots.js.map +1 -1
  15. package/dist/core/extensions/builtin/compaction/deterministic-fallback.d.ts +8 -2
  16. package/dist/core/extensions/builtin/compaction/deterministic-fallback.d.ts.map +1 -1
  17. package/dist/core/extensions/builtin/compaction/deterministic-fallback.js +160 -47
  18. package/dist/core/extensions/builtin/compaction/deterministic-fallback.js.map +1 -1
  19. package/dist/core/extensions/builtin/compaction/retained-message-safety.d.ts.map +1 -1
  20. package/dist/core/extensions/builtin/compaction/retained-message-safety.js +31 -8
  21. package/dist/core/extensions/builtin/compaction/retained-message-safety.js.map +1 -1
  22. package/dist/core/extensions/builtin/cursor-cli-oauth/stream.d.ts.map +1 -1
  23. package/dist/core/extensions/builtin/cursor-cli-oauth/stream.js +8 -28
  24. package/dist/core/extensions/builtin/cursor-cli-oauth/stream.js.map +1 -1
  25. package/dist/core/extensions/builtin/permission-system/external-dir.d.ts.map +1 -1
  26. package/dist/core/extensions/builtin/permission-system/external-dir.js +15 -5
  27. package/dist/core/extensions/builtin/permission-system/external-dir.js.map +1 -1
  28. package/dist/core/extensions/builtin/permission-system/parsers.d.ts.map +1 -1
  29. package/dist/core/extensions/builtin/permission-system/parsers.js +16 -1
  30. package/dist/core/extensions/builtin/permission-system/parsers.js.map +1 -1
  31. package/dist/core/extensions/builtin/terminal/manager.d.ts +5 -0
  32. package/dist/core/extensions/builtin/terminal/manager.d.ts.map +1 -1
  33. package/dist/core/extensions/builtin/terminal/manager.js +31 -3
  34. package/dist/core/extensions/builtin/terminal/manager.js.map +1 -1
  35. package/dist/core/extensions/builtin/terminal/monitor-permission.d.ts +8 -0
  36. package/dist/core/extensions/builtin/terminal/monitor-permission.d.ts.map +1 -0
  37. package/dist/core/extensions/builtin/terminal/monitor-permission.js +16 -0
  38. package/dist/core/extensions/builtin/terminal/monitor-permission.js.map +1 -0
  39. package/dist/core/extensions/builtin/terminal/monitor-registry.d.ts +13 -0
  40. package/dist/core/extensions/builtin/terminal/monitor-registry.d.ts.map +1 -1
  41. package/dist/core/extensions/builtin/terminal/monitor-registry.js +403 -3
  42. package/dist/core/extensions/builtin/terminal/monitor-registry.js.map +1 -1
  43. package/dist/core/extensions/builtin/terminal/session-bundle.d.ts.map +1 -1
  44. package/dist/core/extensions/builtin/terminal/session-bundle.js +1 -0
  45. package/dist/core/extensions/builtin/terminal/session-bundle.js.map +1 -1
  46. package/dist/core/extensions/builtin/terminal/tools/kill-bash.d.ts.map +1 -1
  47. package/dist/core/extensions/builtin/terminal/tools/kill-bash.js +5 -2
  48. package/dist/core/extensions/builtin/terminal/tools/kill-bash.js.map +1 -1
  49. package/dist/core/extensions/builtin/terminal/tools/monitor.d.ts +4 -0
  50. package/dist/core/extensions/builtin/terminal/tools/monitor.d.ts.map +1 -1
  51. package/dist/core/extensions/builtin/terminal/tools/monitor.js +40 -2
  52. package/dist/core/extensions/builtin/terminal/tools/monitor.js.map +1 -1
  53. package/dist/core/model-resolver.js +2 -2
  54. package/dist/core/model-resolver.js.map +1 -1
  55. package/dist/core/session-manager.d.ts.map +1 -1
  56. package/dist/core/session-manager.js +10 -1
  57. package/dist/core/session-manager.js.map +1 -1
  58. package/dist/core/settings-manager.d.ts +7 -0
  59. package/dist/core/settings-manager.d.ts.map +1 -1
  60. package/dist/core/settings-manager.js +6 -0
  61. package/dist/core/settings-manager.js.map +1 -1
  62. package/dist/core/tools/bash.d.ts.map +1 -1
  63. package/dist/core/tools/bash.js +97 -15
  64. package/dist/core/tools/bash.js.map +1 -1
  65. package/dist/core/tools/index.d.ts +1 -0
  66. package/dist/core/tools/index.d.ts.map +1 -1
  67. package/dist/core/tools/index.js +8 -0
  68. package/dist/core/tools/index.js.map +1 -1
  69. package/dist/core/tools/output-accumulator.d.ts +1 -1
  70. package/dist/core/tools/output-accumulator.d.ts.map +1 -1
  71. package/dist/core/tools/output-accumulator.js +76 -34
  72. package/dist/core/tools/output-accumulator.js.map +1 -1
  73. package/dist/modes/interactive/components/footer.d.ts +7 -0
  74. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  75. package/dist/modes/interactive/components/footer.js +34 -8
  76. package/dist/modes/interactive/components/footer.js.map +1 -1
  77. package/dist/modes/interactive/grok/chrome.d.ts +2 -0
  78. package/dist/modes/interactive/grok/chrome.d.ts.map +1 -1
  79. package/dist/modes/interactive/grok/chrome.js.map +1 -1
  80. package/dist/modes/interactive/interactive-host-runtime.d.ts +51 -1
  81. package/dist/modes/interactive/interactive-host-runtime.d.ts.map +1 -1
  82. package/dist/modes/interactive/interactive-host-runtime.js +235 -20
  83. package/dist/modes/interactive/interactive-host-runtime.js.map +1 -1
  84. package/dist/modes/interactive/interactive-mode.d.ts +7 -0
  85. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  86. package/dist/modes/interactive/interactive-mode.js +114 -2
  87. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  88. package/dist/modes/rpc/connection-handler.d.ts +8 -1
  89. package/dist/modes/rpc/connection-handler.d.ts.map +1 -1
  90. package/dist/modes/rpc/connection-handler.js +145 -49
  91. package/dist/modes/rpc/connection-handler.js.map +1 -1
  92. package/dist/modes/rpc/host-ensure.d.ts +2 -0
  93. package/dist/modes/rpc/host-ensure.d.ts.map +1 -1
  94. package/dist/modes/rpc/host-ensure.js +15 -2
  95. package/dist/modes/rpc/host-ensure.js.map +1 -1
  96. package/dist/modes/rpc/host-lifecycle.d.ts.map +1 -1
  97. package/dist/modes/rpc/host-lifecycle.js +31 -12
  98. package/dist/modes/rpc/host-lifecycle.js.map +1 -1
  99. package/dist/modes/rpc/host-watchdog.d.ts +2 -0
  100. package/dist/modes/rpc/host-watchdog.d.ts.map +1 -1
  101. package/dist/modes/rpc/host-watchdog.js +23 -12
  102. package/dist/modes/rpc/host-watchdog.js.map +1 -1
  103. package/dist/modes/rpc/multi-session-host.js +6 -4
  104. package/dist/modes/rpc/multi-session-host.js.map +1 -1
  105. package/dist/modes/rpc/rpc-client.d.ts +10 -4
  106. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  107. package/dist/modes/rpc/rpc-client.js +45 -10
  108. package/dist/modes/rpc/rpc-client.js.map +1 -1
  109. package/dist/modes/rpc/rpc-input-validation.d.ts +1 -0
  110. package/dist/modes/rpc/rpc-input-validation.d.ts.map +1 -1
  111. package/dist/modes/rpc/rpc-input-validation.js +66 -0
  112. package/dist/modes/rpc/rpc-input-validation.js.map +1 -1
  113. package/dist/modes/rpc/rpc-types.d.ts +79 -1
  114. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  115. package/dist/modes/rpc/rpc-types.js.map +1 -1
  116. package/dist/modes/rpc/session-binding.d.ts +1 -0
  117. package/dist/modes/rpc/session-binding.d.ts.map +1 -1
  118. package/dist/modes/rpc/session-binding.js +21 -1
  119. package/dist/modes/rpc/session-binding.js.map +1 -1
  120. package/dist/modes/rpc/session-command-router.d.ts.map +1 -1
  121. package/dist/modes/rpc/session-command-router.js +1 -0
  122. package/dist/modes/rpc/session-command-router.js.map +1 -1
  123. package/dist/modes/rpc/session-event-writer.d.ts +2 -0
  124. package/dist/modes/rpc/session-event-writer.d.ts.map +1 -1
  125. package/dist/modes/rpc/session-event-writer.js +54 -7
  126. package/dist/modes/rpc/session-event-writer.js.map +1 -1
  127. package/dist/modes/rpc/session-extension-ui-requests.d.ts +1 -0
  128. package/dist/modes/rpc/session-extension-ui-requests.d.ts.map +1 -1
  129. package/dist/modes/rpc/session-extension-ui-requests.js +5 -0
  130. package/dist/modes/rpc/session-extension-ui-requests.js.map +1 -1
  131. package/dist/modes/rpc/session-registry.d.ts +10 -2
  132. package/dist/modes/rpc/session-registry.d.ts.map +1 -1
  133. package/dist/modes/rpc/session-registry.js +58 -4
  134. package/dist/modes/rpc/session-registry.js.map +1 -1
  135. package/dist/modes/rpc/socket-event-fanout.d.ts +22 -0
  136. package/dist/modes/rpc/socket-event-fanout.d.ts.map +1 -0
  137. package/dist/modes/rpc/socket-event-fanout.js +84 -0
  138. package/dist/modes/rpc/socket-event-fanout.js.map +1 -0
  139. package/docs/rpc.md +3 -1
  140. package/docs/settings.md +34 -0
  141. package/docs/windows.md +2 -0
  142. package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +38 -0
  143. package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -4
  144. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-indirect-eval.js +139 -90
  145. package/node_modules/@code-yeongyu/senpi-codemode/src/prompt/eval-prompt.ts +4 -4
  146. package/node_modules/@earendil-works/pi-agent-core/dist/harness/env/nodejs.d.ts.map +1 -1
  147. package/node_modules/@earendil-works/pi-agent-core/dist/harness/env/nodejs.js +40 -20
  148. package/node_modules/@earendil-works/pi-agent-core/dist/harness/env/nodejs.js.map +1 -1
  149. package/node_modules/@earendil-works/pi-agent-core/dist/harness/types.d.ts +3 -3
  150. package/node_modules/@earendil-works/pi-agent-core/dist/harness/types.d.ts.map +1 -1
  151. package/node_modules/@earendil-works/pi-agent-core/dist/harness/types.js.map +1 -1
  152. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/shell-output.d.ts +1 -1
  153. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/shell-output.d.ts.map +1 -1
  154. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/shell-output.js +28 -9
  155. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/shell-output.js.map +1 -1
  156. package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
  157. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.d.ts.map +1 -1
  158. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.js +58 -31
  159. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.js.map +1 -1
  160. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/measure.d.ts +20 -0
  161. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/measure.d.ts.map +1 -0
  162. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/measure.js +443 -0
  163. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/measure.js.map +1 -0
  164. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.d.ts +2 -0
  165. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.d.ts.map +1 -1
  166. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.js +2 -0
  167. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.js.map +1 -1
  168. package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.js +1 -1
  169. package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.js.map +1 -1
  170. package/node_modules/@earendil-works/pi-ai/dist/index.d.ts +1 -0
  171. package/node_modules/@earendil-works/pi-ai/dist/index.d.ts.map +1 -1
  172. package/node_modules/@earendil-works/pi-ai/dist/index.js +1 -0
  173. package/node_modules/@earendil-works/pi-ai/dist/index.js.map +1 -1
  174. package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
  175. package/node_modules/@earendil-works/pi-ai/dist/providers/data/cloudflare-ai-gateway.json +1 -1
  176. package/node_modules/@earendil-works/pi-ai/dist/providers/data/cloudflare-workers-ai.json +1 -1
  177. package/node_modules/@earendil-works/pi-ai/dist/providers/data/fireworks.json +1 -1
  178. package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode-go.json +1 -1
  179. package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode.json +1 -1
  180. package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
  181. package/node_modules/@earendil-works/pi-ai/dist/providers/data/together.json +1 -1
  182. package/node_modules/@earendil-works/pi-ai/dist/providers/data/zai-coding-cn.json +1 -1
  183. package/node_modules/@earendil-works/pi-ai/dist/providers/data/zai.json +1 -1
  184. package/node_modules/@earendil-works/pi-ai/package.json +2 -2
  185. package/node_modules/@earendil-works/pi-pty/package.json +1 -1
  186. package/node_modules/@earendil-works/pi-telemetry/package.json +1 -1
  187. package/node_modules/@earendil-works/pi-tui/dist/components/editor.d.ts +7 -0
  188. package/node_modules/@earendil-works/pi-tui/dist/components/editor.d.ts.map +1 -1
  189. package/node_modules/@earendil-works/pi-tui/dist/components/editor.js +5 -0
  190. package/node_modules/@earendil-works/pi-tui/dist/components/editor.js.map +1 -1
  191. package/node_modules/@earendil-works/pi-tui/dist/mux.d.ts +1 -1
  192. package/node_modules/@earendil-works/pi-tui/dist/mux.d.ts.map +1 -1
  193. package/node_modules/@earendil-works/pi-tui/dist/mux.js +2 -2
  194. package/node_modules/@earendil-works/pi-tui/dist/mux.js.map +1 -1
  195. package/node_modules/@earendil-works/pi-tui/dist/terminal.d.ts +1 -0
  196. package/node_modules/@earendil-works/pi-tui/dist/terminal.d.ts.map +1 -1
  197. package/node_modules/@earendil-works/pi-tui/dist/terminal.js +19 -0
  198. package/node_modules/@earendil-works/pi-tui/dist/terminal.js.map +1 -1
  199. package/node_modules/@earendil-works/pi-tui/package.json +1 -1
  200. package/package.json +7 -7
@@ -0,0 +1,84 @@
1
+ const DEFAULT_QUEUE_BYTES = 4 * 1024 * 1024;
2
+ /** Independent FIFO actor for one socket. It never shares a drain promise with another sink. */
3
+ export class SocketEventSinkActor {
4
+ constructor(sink, onFailure, maxQueueBytes = DEFAULT_QUEUE_BYTES) {
5
+ this.queue = [];
6
+ this.queuedBytes = 0;
7
+ this.closed = false;
8
+ this.sink = sink;
9
+ this.onFailure = onFailure;
10
+ this.maxQueueBytes = maxQueueBytes;
11
+ }
12
+ enqueue(line, key, onWritten) {
13
+ if (this.closed)
14
+ return;
15
+ const bytes = Buffer.byteLength(line);
16
+ if (key !== undefined) {
17
+ const existing = this.queue.find((entry) => entry.key === key);
18
+ if (existing) {
19
+ this.queuedBytes += bytes - existing.bytes;
20
+ existing.line = line;
21
+ existing.bytes = bytes;
22
+ if (this.queuedBytes <= this.maxQueueBytes)
23
+ return;
24
+ }
25
+ }
26
+ if (this.queuedBytes + bytes > this.maxQueueBytes) {
27
+ this.closed = true;
28
+ this.queue.length = 0;
29
+ this.queuedBytes = 0;
30
+ try {
31
+ this.sink.writeRaw(`${JSON.stringify({ type: "overflow", error: "overflow, resync required" })}\n`);
32
+ }
33
+ catch (cause) {
34
+ this.onFailure(cause);
35
+ }
36
+ this.onFailure(new Error("socket event queue overflow"));
37
+ return;
38
+ }
39
+ this.queue.push({ line, bytes, key, onWritten });
40
+ this.queuedBytes += bytes;
41
+ void this.drain();
42
+ }
43
+ async flush() {
44
+ while (this.draining)
45
+ await this.draining;
46
+ if (this.failure !== undefined)
47
+ throw this.failure;
48
+ }
49
+ close() {
50
+ this.closed = true;
51
+ this.queue.length = 0;
52
+ this.queuedBytes = 0;
53
+ }
54
+ drain() {
55
+ if (this.draining)
56
+ return this.draining;
57
+ this.draining = (async () => {
58
+ try {
59
+ while (!this.closed && this.queue.length > 0) {
60
+ const entry = this.queue.shift();
61
+ this.queuedBytes -= entry.bytes;
62
+ this.sink.writeRaw(entry.line);
63
+ await this.sink.waitForBackpressure();
64
+ entry.onWritten?.();
65
+ }
66
+ }
67
+ catch (cause) {
68
+ this.failure = cause;
69
+ this.closed = true;
70
+ this.queue.length = 0;
71
+ this.queuedBytes = 0;
72
+ this.onFailure(cause);
73
+ }
74
+ })().finally(() => {
75
+ this.draining = undefined;
76
+ // An enqueue that lands between the loop's exit and this reaction sees the
77
+ // stale settled promise and starts nothing; reschedule for it here.
78
+ if (!this.closed && this.queue.length > 0)
79
+ void this.drain();
80
+ });
81
+ return this.draining;
82
+ }
83
+ }
84
+ //# sourceMappingURL=socket-event-fanout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"socket-event-fanout.js","sourceRoot":"","sources":["../../../src/modes/rpc/socket-event-fanout.ts"],"names":[],"mappings":"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","sourcesContent":["type SocketSink = {\n\twriteRaw(chunk: string): void;\n\twaitForBackpressure(): Promise<void>;\n};\n\ntype QueueEntry = { line: string; bytes: number; key?: string; onWritten?: () => void };\n\nconst DEFAULT_QUEUE_BYTES = 4 * 1024 * 1024;\n\n/** Independent FIFO actor for one socket. It never shares a drain promise with another sink. */\nexport class SocketEventSinkActor {\n\tprivate readonly queue: QueueEntry[] = [];\n\tprivate queuedBytes = 0;\n\tprivate draining?: Promise<void>;\n\tprivate closed = false;\n\tprivate failure?: unknown;\n\n\tprivate readonly sink: SocketSink;\n\tprivate readonly onFailure: (cause: unknown) => void;\n\tprivate readonly maxQueueBytes: number;\n\n\tconstructor(sink: SocketSink, onFailure: (cause: unknown) => void, maxQueueBytes = DEFAULT_QUEUE_BYTES) {\n\t\tthis.sink = sink;\n\t\tthis.onFailure = onFailure;\n\t\tthis.maxQueueBytes = maxQueueBytes;\n\t}\n\n\tenqueue(line: string, key?: string, onWritten?: () => void): void {\n\t\tif (this.closed) return;\n\t\tconst bytes = Buffer.byteLength(line);\n\t\tif (key !== undefined) {\n\t\t\tconst existing = this.queue.find((entry) => entry.key === key);\n\t\t\tif (existing) {\n\t\t\t\tthis.queuedBytes += bytes - existing.bytes;\n\t\t\t\texisting.line = line;\n\t\t\t\texisting.bytes = bytes;\n\t\t\t\tif (this.queuedBytes <= this.maxQueueBytes) return;\n\t\t\t}\n\t\t}\n\t\tif (this.queuedBytes + bytes > this.maxQueueBytes) {\n\t\t\tthis.closed = true;\n\t\t\tthis.queue.length = 0;\n\t\t\tthis.queuedBytes = 0;\n\t\t\ttry {\n\t\t\t\tthis.sink.writeRaw(`${JSON.stringify({ type: \"overflow\", error: \"overflow, resync required\" })}\\n`);\n\t\t\t} catch (cause) {\n\t\t\t\tthis.onFailure(cause);\n\t\t\t}\n\t\t\tthis.onFailure(new Error(\"socket event queue overflow\"));\n\t\t\treturn;\n\t\t}\n\t\tthis.queue.push({ line, bytes, key, onWritten });\n\t\tthis.queuedBytes += bytes;\n\t\tvoid this.drain();\n\t}\n\n\tasync flush(): Promise<void> {\n\t\twhile (this.draining) await this.draining;\n\t\tif (this.failure !== undefined) throw this.failure;\n\t}\n\n\tclose(): void {\n\t\tthis.closed = true;\n\t\tthis.queue.length = 0;\n\t\tthis.queuedBytes = 0;\n\t}\n\n\tprivate drain(): Promise<void> {\n\t\tif (this.draining) return this.draining;\n\t\tthis.draining = (async () => {\n\t\t\ttry {\n\t\t\t\twhile (!this.closed && this.queue.length > 0) {\n\t\t\t\t\tconst entry = this.queue.shift()!;\n\t\t\t\t\tthis.queuedBytes -= entry.bytes;\n\t\t\t\t\tthis.sink.writeRaw(entry.line);\n\t\t\t\t\tawait this.sink.waitForBackpressure();\n\t\t\t\t\tentry.onWritten?.();\n\t\t\t\t}\n\t\t\t} catch (cause) {\n\t\t\t\tthis.failure = cause;\n\t\t\t\tthis.closed = true;\n\t\t\t\tthis.queue.length = 0;\n\t\t\t\tthis.queuedBytes = 0;\n\t\t\t\tthis.onFailure(cause);\n\t\t\t}\n\t\t})().finally(() => {\n\t\t\tthis.draining = undefined;\n\t\t\t// An enqueue that lands between the loop's exit and this reaction sees the\n\t\t\t// stale settled promise and starts nothing; reschedule for it here.\n\t\t\tif (!this.closed && this.queue.length > 0) void this.drain();\n\t\t});\n\t\treturn this.draining;\n\t}\n}\n"]}
package/docs/rpc.md CHANGED
@@ -844,9 +844,11 @@ Response:
844
844
  Load a different session file. Can be cancelled by a `session_before_switch` extension event handler.
845
845
 
846
846
  ```json
847
- {"type": "switch_session", "sessionPath": "/path/to/session.jsonl"}
847
+ {"type": "switch_session", "sessionPath": "/path/to/session.jsonl", "cwdOverride": "/path/to/project"}
848
848
  ```
849
849
 
850
+ `cwdOverride` is optional. When supplied, the replacement session and its cwd-bound settings and runtime state are rebuilt for that directory instead of using the session file's stored cwd. The host also resolves `projectTrusted` from its saved project trust store for the replacement cwd; an absent or false decision keeps project-scoped settings and resources disabled.
851
+
850
852
  Response:
851
853
  ```json
852
854
  {"type": "response", "command": "switch_session", "success": true, "data": {"cancelled": false}}
package/docs/settings.md CHANGED
@@ -399,6 +399,8 @@ Windows paths in JSON must use forward slashes or escaped backslashes:
399
399
  | Setting | Type | Default | Description |
400
400
  |---------|------|---------|-------------|
401
401
  | `defaultTools` | string[] | - | Built-in tools enabled initially. When omitted, Pi uses its standard defaults |
402
+ | `experimental.bashEvalOnly` | boolean | `false` | Route `bash` and `powershell` through eval cells only; the tools leave the model's direct tool list |
403
+ | `experimental.workflowEvalOnly` | boolean | `false` | Route the workflow (dag) tool through eval cells only; the tool leaves the model direct tool list |
402
404
 
403
405
  `defaultTools` selects the built-in tools enabled at startup. Extension and SDK custom tools remain enabled. Available built-ins are `read`, `bash`, `powershell`, `edit`, `write`, `grep`, `find`, and `ls`:
404
406
 
@@ -418,6 +420,38 @@ On Windows, select `powershell` instead of `bash`, or include both:
418
420
 
419
421
  An empty array starts with no built-in tools while preserving extension and SDK custom tools. `--tools` replaces this behavior with a strict allowlist for all tools, `--no-tools` disables all tools, and `--no-builtin-tools` disables the built-in defaults. `--exclude-tools` filters the resulting list. A project `defaultTools` array replaces the global array.
420
422
 
423
+ With `experimental.bashEvalOnly` enabled, the `bash` and `powershell` tools disappear from the model's direct tool list and run only inside eval cells:
424
+
425
+ ```json
426
+ {
427
+ "experimental": {
428
+ "bashEvalOnly": true
429
+ }
430
+ }
431
+ ```
432
+
433
+ ```js
434
+ const { output } = await tool.bash({ command: "ls -la" });
435
+ ```
436
+
437
+ Hooks and permission checks apply unchanged to shell commands run this way. If the model hallucinates a direct `bash` or `powershell` call anyway, the call returns a hint redirecting it to an eval cell. When the `eval` tool is unavailable (codemode not loaded), the policy auto-disables and both tools stay on the direct tool list, so shell access is never lost.
438
+
439
+ With `experimental.workflowEvalOnly` enabled, the `workflow` (dag) tool disappears from the model's direct tool list and runs only inside eval cells:
440
+
441
+ ```json
442
+ {
443
+ "experimental": {
444
+ "workflowEvalOnly": true
445
+ }
446
+ }
447
+ ```
448
+
449
+ ```js
450
+ const snapshot = await tool.workflow({ action: "snapshot", run_id });
451
+ ```
452
+
453
+ Hooks and permission checks apply unchanged to workflow operations run this way. When the `eval` tool is unavailable (codemode not loaded), the policy auto-disables and the workflow tool stays on the direct tool list, so workflow access is never lost.
454
+
421
455
  ### Sessions
422
456
 
423
457
  | Setting | Type | Default | Description |
package/docs/windows.md CHANGED
@@ -30,6 +30,8 @@ Or enable both while comparing behavior:
30
30
 
31
31
  The `!` and `!!` editor commands still use Bash.
32
32
 
33
+ With `experimental.bashEvalOnly` enabled, both `bash` and `powershell` run only inside eval cells; see [Settings](settings.md#tools).
34
+
33
35
  ## Custom Bash Path
34
36
 
35
37
  ```json
@@ -12,6 +12,44 @@
12
12
 
13
13
  ### Removed
14
14
 
15
+ ## [2026.8.30] - 2026-08-30
16
+
17
+ ### Breaking Changes
18
+
19
+ ### Added
20
+
21
+ ### Changed
22
+
23
+ - The eval prompt's dependency-graph section is now `<workflow>` and states its contract directly:
24
+ define the workflow spec in code, one node per logically distinct step, rather than hand-authoring
25
+ the graph as a single opaque call.
26
+
27
+ ### Fixed
28
+
29
+ - The JavaScript kernel persistence transform no longer truncates declarations whose multi-line
30
+ initializers contain interior `//` comments (previously emitted unparseable code such as
31
+ `globalThis["jobs"] = {;`, failing cells with `Unexpected token ';'. Expected a property name.`),
32
+ and no longer re-evaluates comment-bearing initializers when persisting bindings — such
33
+ declarations are kept verbatim and their bindings persisted by reference.
34
+ - Last-expression capture no longer inserts `return` before continuation lines (`else`/`catch`/`finally`
35
+ clauses and leading-`.`/operator method-chain lines), and now scans template literals (including
36
+ nested templates in interpolations), regexes, and comments with the same literal-aware scanner as
37
+ the persistence transform — fixing `return else …`, `return .replace(…)`, and mid-argument
38
+ `return )` corruption of valid cells.
39
+ - Last-expression capture now follows real ASI statement semantics: a parenthesized/bracketed/template
40
+ line after a closed block starts a new statement (echo restored), expressions split after a trailing
41
+ operator or `await` stay one statement, tagged templates split across lines invoke the tag, regexes
42
+ directly after a control-structure condition no longer desync the scanner, and labeled final
43
+ statements are left uncaptured instead of emitting invalid `return label: …`.
44
+ - Destructuring patterns carrying interior line comments now persist their bindings, and declarations
45
+ with a dangling trailing comma are left untransformed so the original syntax error surfaces instead
46
+ of being silently "repaired".
47
+ - Rewritten destructuring assignments are emitted with a leading defensive semicolon so they can no
48
+ longer ASI-merge into a preceding unterminated expression statement as a bogus call
49
+ (`foo()\n({…} = …)` previously became `foo()({…} = …)`).
50
+
51
+ ### Removed
52
+
15
53
  ## [2026.8.29] - 2026-08-29
16
54
 
17
55
  ### Breaking Changes
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@code-yeongyu/senpi-codemode",
3
- "version": "2026.8.29",
3
+ "version": "2026.8.30",
4
4
  "private": true,
5
5
  "description": "Source-only senpi extension package for codemode evaluation tools",
6
6
  "type": "module",
@@ -31,14 +31,14 @@
31
31
  },
32
32
  "dependencies": {
33
33
  "@babel/parser": "8.0.4",
34
- "@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.8.29",
34
+ "@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.8.30",
35
35
  "typebox": "1.3.18"
36
36
  },
37
37
  "peerDependencies": {
38
- "@code-yeongyu/senpi": "2026.8.29"
38
+ "@code-yeongyu/senpi": "2026.8.30"
39
39
  },
40
40
  "devDependencies": {
41
- "@code-yeongyu/senpi": "2026.8.29"
41
+ "@code-yeongyu/senpi": "2026.8.30"
42
42
  },
43
43
  "keywords": [
44
44
  "senpi",
@@ -258,20 +258,22 @@ function rewriteDeclaration(code, declarationStart, start, end, keyword) {
258
258
  const preserveDeclaration = source.includes("//") || source.includes("/*");
259
259
  for (const [segmentStart, segmentEnd] of splitDeclarators(code, start, end)) {
260
260
  const segment = code.slice(segmentStart, segmentEnd);
261
- if (!segment.trim()) continue;
261
+ if (!segment.trim()) return undefined;
262
262
  const initializerStart = findTopLevelEquals(segment);
263
263
  if (initializerStart < 0 && keyword === "const") return undefined;
264
264
  const pattern = trimPattern(initializerStart < 0 ? segment : segment.slice(0, initializerStart));
265
265
  const bindings = [];
266
266
  collectPatternNames(pattern, bindings);
267
267
  if (bindings.length === 0) return undefined;
268
+ if (preserveDeclaration) {
269
+ for (const name of bindings) assignments.push(`globalThis[${JSON.stringify(name)}] = ${name};`);
270
+ continue;
271
+ }
268
272
  const target = rewriteBindingPattern(pattern);
269
273
  if (target === undefined) return undefined;
270
274
  const initializer = initializerStart < 0 ? "undefined" : segment.slice(initializerStart + 1).trim();
271
- const [assignmentInitializer, comment] = splitTrailingLineComment(initializer);
272
- const assignmentComment = preserveDeclaration ? "" : comment;
273
275
  assignments.push(
274
- `${target.startsWith("{") || target.startsWith("[") ? `(${target} = ${assignmentInitializer})` : `${target} = ${assignmentInitializer}`};${assignmentComment}`,
276
+ `${target.startsWith("{") || target.startsWith("[") ? `;(${target} = ${initializer})` : `${target} = ${initializer}`};`,
275
277
  );
276
278
  }
277
279
  if (assignments.length === 0) return undefined;
@@ -408,6 +410,7 @@ function splitDeclarators(code, start, end) {
408
410
  if (!/\s/u.test(char)) canStartRegex = isExpressionOperator(char) || char === "(" || char === "[" || char === "{";
409
411
  }
410
412
  if (segmentStart < end && code.slice(segmentStart, end).trim() !== ";") ranges.push([segmentStart, end - (code[end - 1] === ";" ? 1 : 0)]);
413
+ else if (ranges.length > 0 && code.slice(segmentStart, end).trim() !== ";") ranges.push([end, end]);
411
414
  return ranges;
412
415
  }
413
416
 
@@ -501,51 +504,9 @@ function applyTextEdits(code, edits) {
501
504
  return output;
502
505
  }
503
506
 
504
- function splitTrailingLineComment(source) {
505
- let canStartRegex = true;
506
- for (let index = 0; index < source.length; index += 1) {
507
- const char = source[index];
508
- const next = source[index + 1];
509
- if (char === "/" && next === "/") return [source.slice(0, index).trimEnd(), source.slice(index)];
510
- if (char === "/" && next === "*") {
511
- index = skipBlockComment(source, index) - 1;
512
- continue;
513
- }
514
- if (char === "'" || char === '"') {
515
- index = skipQuotedLiteral(source, index) - 1;
516
- canStartRegex = false;
517
- continue;
518
- }
519
- if (char === "`") {
520
- index = skipTemplateLiteral(source, index) - 1;
521
- canStartRegex = false;
522
- continue;
523
- }
524
- if (char === "/" && canStartRegex) {
525
- index = skipRegexLiteral(source, index) - 1;
526
- canStartRegex = false;
527
- continue;
528
- }
529
- if (isIdentifierStart(char)) {
530
- const end = readIdentifier(source, index);
531
- canStartRegex = REGEX_PREFIX_KEYWORDS.has(source.slice(index, end));
532
- index = end - 1;
533
- continue;
534
- }
535
- if (isDecimalDigit(char)) {
536
- index = skipNumberLiteral(source, index) - 1;
537
- canStartRegex = false;
538
- continue;
539
- }
540
- if (!/\s/u.test(char)) canStartRegex = isExpressionOperator(char) || char === "(" || char === "[" || char === "{";
541
- }
542
- return [source, ""];
543
- }
544
-
545
507
  function trimPattern(source) {
546
508
  let start = 0;
547
- let end = source.length;
548
- while (start < end) {
509
+ while (start < source.length) {
549
510
  if (/\s/u.test(source[start])) {
550
511
  start += 1;
551
512
  continue;
@@ -560,12 +521,30 @@ function trimPattern(source) {
560
521
  }
561
522
  break;
562
523
  }
563
- while (true) {
564
- while (end > start && /\s/u.test(source[end - 1])) end -= 1;
565
- if (end < start + 2 || source.slice(end - 2, end) !== "*/") break;
566
- const commentStart = source.lastIndexOf("/*", end - 2);
567
- if (commentStart < start) break;
568
- end = commentStart;
524
+ let end = start;
525
+ for (let index = start; index < source.length; index += 1) {
526
+ const char = source[index];
527
+ const next = source[index + 1];
528
+ if (/\s/u.test(char)) continue;
529
+ if (char === "/" && next === "/") {
530
+ index = skipLineComment(source, index) - 1;
531
+ continue;
532
+ }
533
+ if (char === "/" && next === "*") {
534
+ index = skipBlockComment(source, index) - 1;
535
+ continue;
536
+ }
537
+ if (char === "'" || char === '"') {
538
+ index = skipQuotedLiteral(source, index) - 1;
539
+ end = index + 1;
540
+ continue;
541
+ }
542
+ if (char === "`") {
543
+ index = skipTemplateLiteral(source, index) - 1;
544
+ end = index + 1;
545
+ continue;
546
+ }
547
+ end = index + 1;
569
548
  }
570
549
  return source.slice(start, end);
571
550
  }
@@ -733,66 +712,136 @@ function captureLastExpression(code) {
733
712
  return `${head}return ${tail.replace(/;+$/u, "")};`;
734
713
  }
735
714
 
715
+ const LABEL_PREFIX_RE = /^[\p{ID_Start}$_][\p{ID_Continue}\u200c\u200d$]*\s*:/u;
716
+
736
717
  function isStatementOnly(source) {
737
- return /^(?:const|let|var|if|for|while|switch|try|catch|finally|class|function|import|export|throw|return|do|break|continue|debugger)\b/u.test(
738
- source,
739
- );
718
+ if (
719
+ /^(?:const|let|var|if|for|while|switch|try|catch|finally|class|function|import|export|throw|return|do|break|continue|debugger)\b/u.test(
720
+ source,
721
+ )
722
+ )
723
+ return true;
724
+ return LABEL_PREFIX_RE.test(source);
740
725
  }
741
726
 
727
+ const CONTROL_PAREN_KEYWORDS = new Set(["catch", "for", "if", "switch", "while", "with"]);
728
+
742
729
  function findLastTopLevelStatementStart(code) {
743
730
  let start = 0;
744
731
  let round = 0;
745
732
  let square = 0;
746
733
  let curly = 0;
747
- let quote = "";
748
- let escaped = false;
749
- let lineComment = false;
750
- let blockComment = false;
734
+ let canStartRegex = true;
735
+ let lastSignificant = "";
736
+ let pendingControlParen = false;
737
+ const controlParens = [];
751
738
  for (let index = 0; index < code.length; index += 1) {
752
739
  const char = code[index];
753
740
  const next = code[index + 1];
754
- if (lineComment) {
755
- if (char === "\n") lineComment = false;
741
+ if (char === "/" && next === "/") {
742
+ index = skipLineComment(code, index) - 1;
756
743
  continue;
757
744
  }
758
- if (blockComment) {
759
- if (char === "*" && next === "/") {
760
- blockComment = false;
761
- index += 1;
762
- }
745
+ if (char === "/" && next === "*") {
746
+ index = skipBlockComment(code, index) - 1;
763
747
  continue;
764
748
  }
765
- if (quote) {
766
- if (escaped) {
767
- escaped = false;
768
- } else if (char === "\\") {
769
- escaped = true;
770
- } else if (char === quote) {
771
- quote = "";
772
- }
749
+ if (char === "'" || char === '"' || char === "`") {
750
+ index = (char === "`" ? skipTemplateLiteral(code, index) : skipQuotedLiteral(code, index)) - 1;
751
+ canStartRegex = false;
752
+ lastSignificant = char;
753
+ pendingControlParen = false;
773
754
  continue;
774
755
  }
775
- if (char === "/" && next === "/") {
776
- lineComment = true;
777
- index += 1;
756
+ if (char === "/" && canStartRegex) {
757
+ index = skipRegexLiteral(code, index) - 1;
758
+ canStartRegex = false;
759
+ lastSignificant = char;
760
+ pendingControlParen = false;
778
761
  continue;
779
762
  }
780
- if (char === "/" && next === "*") {
781
- blockComment = true;
782
- index += 1;
763
+ if (isIdentifierStart(char)) {
764
+ const end = readIdentifier(code, index);
765
+ const token = code.slice(index, end);
766
+ canStartRegex = REGEX_PREFIX_KEYWORDS.has(token);
767
+ pendingControlParen = CONTROL_PAREN_KEYWORDS.has(token);
768
+ lastSignificant = code[end - 1];
769
+ index = end - 1;
783
770
  continue;
784
771
  }
785
- if (char === "'" || char === '"' || char === "`") {
786
- quote = char;
772
+ if (isDecimalDigit(char)) {
773
+ index = skipNumberLiteral(code, index) - 1;
774
+ canStartRegex = false;
775
+ lastSignificant = char;
776
+ pendingControlParen = false;
787
777
  continue;
788
778
  }
789
- if (char === "(") round += 1;
790
- else if (char === ")") round -= 1;
791
- else if (char === "[") square += 1;
792
- else if (char === "]") square -= 1;
793
- else if (char === "{") curly += 1;
794
- else if (char === "}") curly -= 1;
795
- else if ((char === ";" || char === "\n") && round === 0 && square === 0 && curly === 0) start = index + 1;
779
+ if (char === "(") {
780
+ round += 1;
781
+ controlParens.push(pendingControlParen);
782
+ pendingControlParen = false;
783
+ canStartRegex = true;
784
+ lastSignificant = char;
785
+ continue;
786
+ }
787
+ if (char === ")") {
788
+ round = Math.max(0, round - 1);
789
+ canStartRegex = controlParens.pop() === true;
790
+ lastSignificant = char;
791
+ continue;
792
+ }
793
+ if (char === "[" || char === "{") {
794
+ if (char === "[") square += 1;
795
+ else curly += 1;
796
+ canStartRegex = true;
797
+ lastSignificant = char;
798
+ pendingControlParen = false;
799
+ continue;
800
+ }
801
+ if (char === "]" || char === "}") {
802
+ if (char === "]") square = Math.max(0, square - 1);
803
+ else curly = Math.max(0, curly - 1);
804
+ canStartRegex = false;
805
+ lastSignificant = char;
806
+ pendingControlParen = false;
807
+ continue;
808
+ }
809
+ if (char === ";" && round === 0 && square === 0 && curly === 0) {
810
+ const nextIndex = nextSignificantIndex(code, index + 1);
811
+ if (nextIndex < code.length && !isContinuationKeyword(code, nextIndex)) start = nextIndex;
812
+ canStartRegex = true;
813
+ lastSignificant = char;
814
+ pendingControlParen = false;
815
+ continue;
816
+ }
817
+ if (isLineTerminator(char) && round === 0 && square === 0 && curly === 0) {
818
+ if (!canStartRegex) {
819
+ const nextIndex = nextSignificantIndex(code, index + 1);
820
+ if (nextIndex < code.length && !isStatementContinuation(code, nextIndex, lastSignificant)) start = nextIndex;
821
+ }
822
+ canStartRegex = true;
823
+ continue;
824
+ }
825
+ if (!/\s/u.test(char)) {
826
+ canStartRegex = isExpressionOperator(char) || char === ",";
827
+ lastSignificant = char;
828
+ pendingControlParen = false;
829
+ }
796
830
  }
797
831
  return start;
798
832
  }
833
+
834
+ const STATEMENT_CONTINUATION_KEYWORDS = new Set(["catch", "else", "finally"]);
835
+
836
+ function isContinuationKeyword(code, index) {
837
+ if (!isIdentifierStart(code[index])) return false;
838
+ return STATEMENT_CONTINUATION_KEYWORDS.has(code.slice(index, readIdentifier(code, index)));
839
+ }
840
+
841
+ function isStatementContinuation(code, index, previousChar) {
842
+ const char = code[index];
843
+ if (char === "(" || char === "[" || char === "`") return previousChar !== "}" && previousChar !== "";
844
+ if (char === "!" || char === "~") return false;
845
+ if (char !== undefined && isDeclarationContinuation(char)) return true;
846
+ return isContinuationKeyword(code, index);
847
+ }
@@ -163,7 +163,7 @@ tool_schema(name?) → dict
163
163
  completion(prompt, model?="default", system?=None, schema?=None) → str | dict
164
164
  Oneshot, stateless (no history/tools). \`model\`: \`"smol"\` fast | \`"default"\` session | \`"slow"\` most capable. \`schema\` (JSON-Schema) → structured output, parsed object.
165
165
  {{#if spawns}}agent(prompt, agent?="{{spawnDefaultAgent}}", model?=None, label?=None, schema?=None, handle?=False) → str | dict
166
- Run a subagent → final output. \`agent\` picks another discovered agent; omit it to use \`{{spawnDefaultAgent}}\`. \`schema\` as in completion(). Background via \`local://\` files named in the prompt. \`handle\` → DAG node dict { text, output, handle: \`agent://<id>\`, id, agent } (parsed under \`data\` when \`schema\` set).
166
+ Run a subagent → final output. \`agent\` picks another discovered agent; omit it to use \`{{spawnDefaultAgent}}\`. \`schema\` as in completion(). Background via \`local://\` files named in the prompt. \`handle\` → workflow node dict { text, output, handle: \`agent://<id>\`, id, agent } (parsed under \`data\` when \`schema\` set).
167
167
  {{#if js}} JS: options are ONE trailing object — agent(prompt, { agent, schema, handle }).
168
168
  {{/if}}{{/if}}parallel(thunks) → list
169
169
  Thunks through a bounded pool (wide as a \`task\` batch — don't pre-shrink), input order kept; returns when all finish, a throwing thunk propagates.
@@ -176,14 +176,14 @@ phase(title) → None
176
176
  \`\`\`
177
177
  </prelude>
178
178
  {{#if spawns}}
179
- <dag>
180
- Pipe handles through stage helpers to build a dependency graph acyclic waves:
179
+ <workflow>
180
+ Define the workflow spec IN CODE: partition the work into logically distinct steps, one node per step, then wire them as acyclic waves — never hand-author the graph as a single opaque call.
181
181
  - **Name nodes.** Capture each \`agent(…, {{#if py}}handle=True{{/if}}{{#if js}}{ handle: true }{{/if}}{{#if jl}}handle=true{{/if}})\` result; carries \`handle\` (\`agent://<id>\`) + \`output\`.
182
182
  - **Wire edges by reference.** Put an upstream node's \`handle\`/\`output\` in the dependent stage's prompt — large transcript never re-inlined. Bulk: \`write("local://<name>.md", …)\`, pass the URI.
183
183
  - **\`pipeline(items, *stages)\` = staged waves**, barrier between stages (every item clears stage N before any enters N+1). **\`parallel(thunks)\` = one wave** of independent nodes.
184
184
  - **Isolate failure.** A raising node re-raises the lowest-index error, aborts its wave; wrap risky nodes in try/except so a failure degrades only its dependent subtree, independent branches finish.
185
185
  - **Acyclic only.** A node never waits on its own descendant.
186
- </dag>
186
+ </workflow>
187
187
  {{/if}}
188
188
 
189
189
  <critical>
@@ -1 +1 @@
1
- {"version":3,"file":"nodejs.d.ts","sourceRoot":"","sources":["../../../src/harness/env/nodejs.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"nodejs.d.ts","sourceRoot":"","sources":["../../../src/harness/env/nodejs.ts"],"names":[],"mappings":"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"}