@chengchenccc/oh-my-agent 0.1.1-rc.1

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 (276) hide show
  1. package/README.md +79 -0
  2. package/dist/cli/args.d.ts +24 -0
  3. package/dist/cli/args.d.ts.map +1 -0
  4. package/dist/cli/args.js +120 -0
  5. package/dist/cli/initial-input.d.ts +32 -0
  6. package/dist/cli/initial-input.d.ts.map +1 -0
  7. package/dist/cli/initial-input.js +131 -0
  8. package/dist/cli.d.ts +3 -0
  9. package/dist/cli.d.ts.map +1 -0
  10. package/dist/cli.js +3 -0
  11. package/dist/core/__fixtures__/echo-model.d.ts +6 -0
  12. package/dist/core/__fixtures__/echo-model.d.ts.map +1 -0
  13. package/dist/core/__fixtures__/echo-model.js +10 -0
  14. package/dist/core/__fixtures__/mcp-echo-server.d.ts +2 -0
  15. package/dist/core/__fixtures__/mcp-echo-server.d.ts.map +1 -0
  16. package/dist/core/__fixtures__/mcp-echo-server.js +54 -0
  17. package/dist/core/agent-runtime.d.ts +21 -0
  18. package/dist/core/agent-runtime.d.ts.map +1 -0
  19. package/dist/core/agent-runtime.js +14 -0
  20. package/dist/core/memory/autonomous-memory.d.ts +21 -0
  21. package/dist/core/memory/autonomous-memory.d.ts.map +1 -0
  22. package/dist/core/memory/autonomous-memory.js +202 -0
  23. package/dist/core/persistence/in-memory-session-store.d.ts +3 -0
  24. package/dist/core/persistence/in-memory-session-store.d.ts.map +1 -0
  25. package/dist/core/persistence/in-memory-session-store.js +109 -0
  26. package/dist/core/persistence/session-store.contract.d.ts +5 -0
  27. package/dist/core/persistence/session-store.contract.d.ts.map +1 -0
  28. package/dist/core/persistence/session-store.contract.js +183 -0
  29. package/dist/core/persistence/session-store.d.ts +33 -0
  30. package/dist/core/persistence/session-store.d.ts.map +1 -0
  31. package/dist/core/persistence/session-store.js +22 -0
  32. package/dist/core/persistence/session-tree.d.ts +49 -0
  33. package/dist/core/persistence/session-tree.d.ts.map +1 -0
  34. package/dist/core/persistence/session-tree.js +1 -0
  35. package/dist/core/plugins/plugin-code.d.ts +13 -0
  36. package/dist/core/plugins/plugin-code.d.ts.map +1 -0
  37. package/dist/core/plugins/plugin-code.js +62 -0
  38. package/dist/core/plugins/plugin-marketplace.d.ts +72 -0
  39. package/dist/core/plugins/plugin-marketplace.d.ts.map +1 -0
  40. package/dist/core/plugins/plugin-marketplace.js +340 -0
  41. package/dist/core/plugins/plugin-resolve.d.ts +32 -0
  42. package/dist/core/plugins/plugin-resolve.d.ts.map +1 -0
  43. package/dist/core/plugins/plugin-resolve.js +101 -0
  44. package/dist/core/plugins/plugin-trust.d.ts +24 -0
  45. package/dist/core/plugins/plugin-trust.d.ts.map +1 -0
  46. package/dist/core/plugins/plugin-trust.js +89 -0
  47. package/dist/core/runtime/agent-event.d.ts +98 -0
  48. package/dist/core/runtime/agent-event.d.ts.map +1 -0
  49. package/dist/core/runtime/agent-event.js +1 -0
  50. package/dist/core/runtime/agent-loop.d.ts +142 -0
  51. package/dist/core/runtime/agent-loop.d.ts.map +1 -0
  52. package/dist/core/runtime/agent-loop.js +1008 -0
  53. package/dist/core/runtime/approval.d.ts +23 -0
  54. package/dist/core/runtime/approval.d.ts.map +1 -0
  55. package/dist/core/runtime/approval.js +25 -0
  56. package/dist/core/runtime/compaction.d.ts +22 -0
  57. package/dist/core/runtime/compaction.d.ts.map +1 -0
  58. package/dist/core/runtime/compaction.js +90 -0
  59. package/dist/core/runtime/context-estimate.d.ts +42 -0
  60. package/dist/core/runtime/context-estimate.d.ts.map +1 -0
  61. package/dist/core/runtime/context-estimate.js +34 -0
  62. package/dist/core/runtime/create-runtime.d.ts +81 -0
  63. package/dist/core/runtime/create-runtime.d.ts.map +1 -0
  64. package/dist/core/runtime/create-runtime.js +250 -0
  65. package/dist/core/runtime/fake-provider.d.ts +3 -0
  66. package/dist/core/runtime/fake-provider.d.ts.map +1 -0
  67. package/dist/core/runtime/fake-provider.js +87 -0
  68. package/dist/core/runtime/logger.d.ts +13 -0
  69. package/dist/core/runtime/logger.d.ts.map +1 -0
  70. package/dist/core/runtime/logger.js +38 -0
  71. package/dist/core/runtime/loop-input.d.ts +39 -0
  72. package/dist/core/runtime/loop-input.d.ts.map +1 -0
  73. package/dist/core/runtime/loop-input.js +38 -0
  74. package/dist/core/runtime/message-cache.d.ts +16 -0
  75. package/dist/core/runtime/message-cache.d.ts.map +1 -0
  76. package/dist/core/runtime/message-cache.js +23 -0
  77. package/dist/core/runtime/model-catalog.d.ts +10 -0
  78. package/dist/core/runtime/model-catalog.d.ts.map +1 -0
  79. package/dist/core/runtime/model-catalog.js +14 -0
  80. package/dist/core/runtime/plugin-runtime.d.ts +41 -0
  81. package/dist/core/runtime/plugin-runtime.d.ts.map +1 -0
  82. package/dist/core/runtime/plugin-runtime.js +1 -0
  83. package/dist/core/runtime/plugin.d.ts +86 -0
  84. package/dist/core/runtime/plugin.d.ts.map +1 -0
  85. package/dist/core/runtime/plugin.js +23 -0
  86. package/dist/core/runtime/process-tree.d.ts +12 -0
  87. package/dist/core/runtime/process-tree.d.ts.map +1 -0
  88. package/dist/core/runtime/process-tree.js +40 -0
  89. package/dist/core/runtime/prompt.d.ts +23 -0
  90. package/dist/core/runtime/prompt.d.ts.map +1 -0
  91. package/dist/core/runtime/prompt.js +29 -0
  92. package/dist/core/runtime/prompts.d.ts +16 -0
  93. package/dist/core/runtime/prompts.d.ts.map +1 -0
  94. package/dist/core/runtime/prompts.js +36 -0
  95. package/dist/core/runtime/retry.d.ts +20 -0
  96. package/dist/core/runtime/retry.d.ts.map +1 -0
  97. package/dist/core/runtime/retry.js +60 -0
  98. package/dist/core/runtime/run-runtime.d.ts +93 -0
  99. package/dist/core/runtime/run-runtime.d.ts.map +1 -0
  100. package/dist/core/runtime/run-runtime.js +664 -0
  101. package/dist/core/runtime/runtime-catalog.d.ts +12 -0
  102. package/dist/core/runtime/runtime-catalog.d.ts.map +1 -0
  103. package/dist/core/runtime/runtime-catalog.js +89 -0
  104. package/dist/core/runtime/stream-rules.d.ts +9 -0
  105. package/dist/core/runtime/stream-rules.d.ts.map +1 -0
  106. package/dist/core/runtime/stream-rules.js +44 -0
  107. package/dist/core/runtime/title.d.ts +7 -0
  108. package/dist/core/runtime/title.d.ts.map +1 -0
  109. package/dist/core/runtime/title.js +114 -0
  110. package/dist/core/runtime/todo.d.ts +16 -0
  111. package/dist/core/runtime/todo.d.ts.map +1 -0
  112. package/dist/core/runtime/todo.js +40 -0
  113. package/dist/core/runtime/tool-filter.d.ts +12 -0
  114. package/dist/core/runtime/tool-filter.d.ts.map +1 -0
  115. package/dist/core/runtime/tool-filter.js +23 -0
  116. package/dist/core/runtime/tool-pruning.d.ts +29 -0
  117. package/dist/core/runtime/tool-pruning.d.ts.map +1 -0
  118. package/dist/core/runtime/tool-pruning.js +105 -0
  119. package/dist/core/session/input-history.d.ts +8 -0
  120. package/dist/core/session/input-history.d.ts.map +1 -0
  121. package/dist/core/session/input-history.js +41 -0
  122. package/dist/core/session/session-file.d.ts +84 -0
  123. package/dist/core/session/session-file.d.ts.map +1 -0
  124. package/dist/core/session/session-file.js +392 -0
  125. package/dist/core/session/session-loop.d.ts +33 -0
  126. package/dist/core/session/session-loop.d.ts.map +1 -0
  127. package/dist/core/session/session-loop.js +30 -0
  128. package/dist/core/settings/project-settings.d.ts +46 -0
  129. package/dist/core/settings/project-settings.d.ts.map +1 -0
  130. package/dist/core/settings/project-settings.js +85 -0
  131. package/dist/core/settings/workspace-context.d.ts +10 -0
  132. package/dist/core/settings/workspace-context.d.ts.map +1 -0
  133. package/dist/core/settings/workspace-context.js +48 -0
  134. package/dist/core/tools/agent-fs-like.d.ts +15 -0
  135. package/dist/core/tools/agent-fs-like.d.ts.map +1 -0
  136. package/dist/core/tools/agent-fs-like.js +4 -0
  137. package/dist/core/tools/ask-question.d.ts +5 -0
  138. package/dist/core/tools/ask-question.d.ts.map +1 -0
  139. package/dist/core/tools/ask-question.js +51 -0
  140. package/dist/core/tools/bash.d.ts +5 -0
  141. package/dist/core/tools/bash.d.ts.map +1 -0
  142. package/dist/core/tools/bash.js +109 -0
  143. package/dist/core/tools/eval.d.ts +10 -0
  144. package/dist/core/tools/eval.d.ts.map +1 -0
  145. package/dist/core/tools/eval.js +83 -0
  146. package/dist/core/tools/file-tools.d.ts +18 -0
  147. package/dist/core/tools/file-tools.d.ts.map +1 -0
  148. package/dist/core/tools/file-tools.js +249 -0
  149. package/dist/core/tools/glob.d.ts +5 -0
  150. package/dist/core/tools/glob.d.ts.map +1 -0
  151. package/dist/core/tools/glob.js +54 -0
  152. package/dist/core/tools/grep.d.ts +5 -0
  153. package/dist/core/tools/grep.d.ts.map +1 -0
  154. package/dist/core/tools/grep.js +61 -0
  155. package/dist/core/tools/index.d.ts +14 -0
  156. package/dist/core/tools/index.d.ts.map +1 -0
  157. package/dist/core/tools/index.js +14 -0
  158. package/dist/core/tools/ls-tree.d.ts +10 -0
  159. package/dist/core/tools/ls-tree.d.ts.map +1 -0
  160. package/dist/core/tools/ls-tree.js +177 -0
  161. package/dist/core/tools/mcp-mount.d.ts +63 -0
  162. package/dist/core/tools/mcp-mount.d.ts.map +1 -0
  163. package/dist/core/tools/mcp-mount.js +287 -0
  164. package/dist/core/tools/read-image.d.ts +6 -0
  165. package/dist/core/tools/read-image.d.ts.map +1 -0
  166. package/dist/core/tools/read-image.js +98 -0
  167. package/dist/core/tools/skill.d.ts +11 -0
  168. package/dist/core/tools/skill.d.ts.map +1 -0
  169. package/dist/core/tools/skill.js +65 -0
  170. package/dist/core/tools/skills.d.ts +10 -0
  171. package/dist/core/tools/skills.d.ts.map +1 -0
  172. package/dist/core/tools/skills.js +116 -0
  173. package/dist/core/tools/todo-store.d.ts +8 -0
  174. package/dist/core/tools/todo-store.d.ts.map +1 -0
  175. package/dist/core/tools/todo-store.js +100 -0
  176. package/dist/core/tools/todo.d.ts +8 -0
  177. package/dist/core/tools/todo.d.ts.map +1 -0
  178. package/dist/core/tools/todo.js +91 -0
  179. package/dist/core/tools/url-guard.d.ts +12 -0
  180. package/dist/core/tools/url-guard.d.ts.map +1 -0
  181. package/dist/core/tools/url-guard.js +58 -0
  182. package/dist/core/tools/web-ports-std.d.ts +6 -0
  183. package/dist/core/tools/web-ports-std.d.ts.map +1 -0
  184. package/dist/core/tools/web-ports-std.js +145 -0
  185. package/dist/core/tools/web-ports.d.ts +56 -0
  186. package/dist/core/tools/web-ports.d.ts.map +1 -0
  187. package/dist/core/tools/web-ports.js +62 -0
  188. package/dist/core/tools/workspace-sandbox.d.ts +20 -0
  189. package/dist/core/tools/workspace-sandbox.d.ts.map +1 -0
  190. package/dist/core/tools/workspace-sandbox.js +84 -0
  191. package/dist/core/workflow/subagent-registry.d.ts +40 -0
  192. package/dist/core/workflow/subagent-registry.d.ts.map +1 -0
  193. package/dist/core/workflow/subagent-registry.js +118 -0
  194. package/dist/core/workflow/workflow-evaluator.d.ts +23 -0
  195. package/dist/core/workflow/workflow-evaluator.d.ts.map +1 -0
  196. package/dist/core/workflow/workflow-evaluator.js +21 -0
  197. package/dist/core/workflow/workflow-executor.d.ts +108 -0
  198. package/dist/core/workflow/workflow-executor.d.ts.map +1 -0
  199. package/dist/core/workflow/workflow-executor.js +549 -0
  200. package/dist/core/workflow/workflow-tools.d.ts +50 -0
  201. package/dist/core/workflow/workflow-tools.d.ts.map +1 -0
  202. package/dist/core/workflow/workflow-tools.js +217 -0
  203. package/dist/index.d.ts +14 -0
  204. package/dist/index.d.ts.map +1 -0
  205. package/dist/index.js +13 -0
  206. package/dist/main.d.ts +9 -0
  207. package/dist/main.d.ts.map +1 -0
  208. package/dist/main.js +97 -0
  209. package/dist/modes/json-mode.d.ts +5 -0
  210. package/dist/modes/json-mode.d.ts.map +1 -0
  211. package/dist/modes/json-mode.js +46 -0
  212. package/dist/modes/print-mode.d.ts +20 -0
  213. package/dist/modes/print-mode.d.ts.map +1 -0
  214. package/dist/modes/print-mode.js +82 -0
  215. package/dist/modes/rpc/jsonl.d.ts +23 -0
  216. package/dist/modes/rpc/jsonl.d.ts.map +1 -0
  217. package/dist/modes/rpc/jsonl.js +88 -0
  218. package/dist/modes/rpc/rpc-mode.d.ts +18 -0
  219. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -0
  220. package/dist/modes/rpc/rpc-mode.js +326 -0
  221. package/dist/modes/tui/settings-overlay.d.ts +23 -0
  222. package/dist/modes/tui/settings-overlay.d.ts.map +1 -0
  223. package/dist/modes/tui/settings-overlay.js +136 -0
  224. package/dist/modes/tui/tui-commands-session.d.ts +3 -0
  225. package/dist/modes/tui/tui-commands-session.d.ts.map +1 -0
  226. package/dist/modes/tui/tui-commands-session.js +191 -0
  227. package/dist/modes/tui/tui-commands.d.ts +52 -0
  228. package/dist/modes/tui/tui-commands.d.ts.map +1 -0
  229. package/dist/modes/tui/tui-commands.js +373 -0
  230. package/dist/modes/tui/tui-components.d.ts +81 -0
  231. package/dist/modes/tui/tui-components.d.ts.map +1 -0
  232. package/dist/modes/tui/tui-components.js +248 -0
  233. package/dist/modes/tui/tui-format.d.ts +65 -0
  234. package/dist/modes/tui/tui-format.d.ts.map +1 -0
  235. package/dist/modes/tui/tui-format.js +246 -0
  236. package/dist/modes/tui/tui-frame-provider.d.ts +16 -0
  237. package/dist/modes/tui/tui-frame-provider.d.ts.map +1 -0
  238. package/dist/modes/tui/tui-frame-provider.js +31 -0
  239. package/dist/modes/tui/tui-interactive.d.ts +16 -0
  240. package/dist/modes/tui/tui-interactive.d.ts.map +1 -0
  241. package/dist/modes/tui/tui-interactive.js +123 -0
  242. package/dist/modes/tui/tui-io.d.ts +12 -0
  243. package/dist/modes/tui/tui-io.d.ts.map +1 -0
  244. package/dist/modes/tui/tui-io.js +620 -0
  245. package/dist/modes/tui/tui-mode.d.ts +120 -0
  246. package/dist/modes/tui/tui-mode.d.ts.map +1 -0
  247. package/dist/modes/tui/tui-mode.js +391 -0
  248. package/dist/modes/tui/tui-render.d.ts +54 -0
  249. package/dist/modes/tui/tui-render.d.ts.map +1 -0
  250. package/dist/modes/tui/tui-render.js +399 -0
  251. package/dist/modes/tui/tui-slash.d.ts +12 -0
  252. package/dist/modes/tui/tui-slash.d.ts.map +1 -0
  253. package/dist/modes/tui/tui-slash.js +64 -0
  254. package/dist/modes/tui/tui-tool-render.d.ts +6 -0
  255. package/dist/modes/tui/tui-tool-render.d.ts.map +1 -0
  256. package/dist/modes/tui/tui-tool-render.js +79 -0
  257. package/dist/modes/tui/tui-transcript-reconciler.d.ts +25 -0
  258. package/dist/modes/tui/tui-transcript-reconciler.d.ts.map +1 -0
  259. package/dist/modes/tui/tui-transcript-reconciler.js +97 -0
  260. package/dist/modes/tui/view-state.d.ts +67 -0
  261. package/dist/modes/tui/view-state.d.ts.map +1 -0
  262. package/dist/modes/tui/view-state.js +353 -0
  263. package/dist/prompts/agents/subagent.md +26 -0
  264. package/dist/prompts/system/base.md +20 -0
  265. package/dist/prompts/system/memory.md +23 -0
  266. package/dist/prompts/system/safety.md +12 -0
  267. package/dist/protocol/index.d.ts +5 -0
  268. package/dist/protocol/index.d.ts.map +1 -0
  269. package/dist/protocol/index.js +2 -0
  270. package/dist/protocol/mapping.d.ts +25 -0
  271. package/dist/protocol/mapping.d.ts.map +1 -0
  272. package/dist/protocol/mapping.js +115 -0
  273. package/dist/protocol/transport.d.ts +1396 -0
  274. package/dist/protocol/transport.d.ts.map +1 -0
  275. package/dist/protocol/transport.js +170 -0
  276. package/package.json +49 -0
@@ -0,0 +1,88 @@
1
+ /** Strict LF-framed JSONL reader (Pi-style).
2
+ *
3
+ * - Only `\n` (0x0A) splits frames - a generic line reader that also splits
4
+ * on Unicode separators would corrupt payloads.
5
+ * - Byte-buffer based: half packets accumulate until the next LF.
6
+ * - A single frame is bounded (`maxLineBytes`); oversized frames are skipped
7
+ * and reported via `onOversize`. The buffer is trimmed so a hostile peer
8
+ * cannot grow memory without bound - trimmed bytes belong to an oversized
9
+ * frame, which is discarded up to its terminating LF. */
10
+ export function createJsonlReader(stream, opts = {}) {
11
+ const maxLineBytes = opts.maxLineBytes ?? 16 * 1024 * 1024;
12
+ const reader = stream.getReader();
13
+ const lines = (async function* () {
14
+ const decoder = new TextDecoder("utf-8");
15
+ let buffer = new Uint8Array(0);
16
+ // True while the buffered bytes belong to a frame already known to
17
+ // exceed the bound: everything up to the next LF is discarded.
18
+ let inOversize = false;
19
+ try {
20
+ for (;;) {
21
+ const { done, value } = await reader.read();
22
+ if (done)
23
+ break;
24
+ if (value.length === 0)
25
+ continue;
26
+ // Append the chunk.
27
+ const next = new Uint8Array(buffer.length + value.length);
28
+ next.set(buffer, 0);
29
+ next.set(value, buffer.length);
30
+ buffer = next;
31
+ // Split on LF only.
32
+ let start = 0;
33
+ for (let i = 0; i < buffer.length; i++) {
34
+ if (buffer[i] !== 0x0a)
35
+ continue;
36
+ const lineLen = i - start;
37
+ if (inOversize) {
38
+ // The LF terminates the truncated oversized frame: discard it.
39
+ inOversize = false;
40
+ }
41
+ else if (lineLen <= maxLineBytes) {
42
+ yield decoder.decode(buffer.subarray(start, i));
43
+ }
44
+ else {
45
+ // The frame ended at this LF (it is complete but oversized).
46
+ opts.onOversize?.(lineLen);
47
+ }
48
+ start = i + 1;
49
+ }
50
+ buffer = buffer.slice(start);
51
+ // Memory bound: a partial frame longer than the bound can never
52
+ // become a valid line - drop it, discard up to the next LF.
53
+ if (buffer.length > maxLineBytes) {
54
+ inOversize = true;
55
+ buffer = new Uint8Array(0);
56
+ }
57
+ }
58
+ // Trailing frame without a final LF.
59
+ if (buffer.length > 0) {
60
+ if (!inOversize && buffer.length <= maxLineBytes) {
61
+ yield decoder.decode(buffer);
62
+ }
63
+ else if (!inOversize) {
64
+ opts.onOversize?.(buffer.length);
65
+ }
66
+ }
67
+ }
68
+ finally {
69
+ try {
70
+ reader.releaseLock();
71
+ }
72
+ catch {
73
+ /* already released */
74
+ }
75
+ }
76
+ })();
77
+ return {
78
+ lines,
79
+ async cancel() {
80
+ try {
81
+ await reader.cancel();
82
+ }
83
+ catch {
84
+ /* stream already closed */
85
+ }
86
+ },
87
+ };
88
+ }
@@ -0,0 +1,18 @@
1
+ import type { ModelRuntime } from "@chengchenccc/ai";
2
+ /** Minimal RPC mode: stdin JSONL commands, stdout JSONL outputs only, stderr
3
+ * for logs. One process = at most one execute = one Run = one outcome, then
4
+ * the process exits. No Session lifecycle, no HTTP, no registry. */
5
+ export interface RpcModeOptions {
6
+ modelRuntime: ModelRuntime;
7
+ stdin?: ReadableStream<Uint8Array>;
8
+ /** stdout writer; the RPC mode ONLY writes JSONL lines here. */
9
+ writeLine?: (line: string) => void;
10
+ log?: (line: string) => void;
11
+ }
12
+ export interface RpcModeController {
13
+ readonly promise: Promise<number>;
14
+ /** Abort the live Run (SIGINT/SIGTERM path): outcome settles aborted. */
15
+ stop(): void;
16
+ }
17
+ export declare function runRpcMode(opts: RpcModeOptions): RpcModeController;
18
+ //# sourceMappingURL=rpc-mode.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rpc-mode.d.ts","sourceRoot":"","sources":["../../../src/modes/rpc/rpc-mode.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAmCrD;;qEAEqE;AAErE,MAAM,WAAW,cAAc;IAC7B,YAAY,EAAE,YAAY,CAAC;IAC3B,KAAK,CAAC,EAAE,cAAc,CAAC,UAAU,CAAC,CAAC;IACnC,gEAAgE;IAChE,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9B;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IAClC,yEAAyE;IACzE,IAAI,IAAI,IAAI,CAAC;CACd;AAMD,wBAAgB,UAAU,CAAC,IAAI,EAAE,cAAc,GAAG,iBAAiB,CA0VlE"}
@@ -0,0 +1,326 @@
1
+ import { existsSync, statSync } from "node:fs";
2
+ import { debugLog } from "@chengchenccc/agent-contract";
3
+ import { MessageSchema } from "@chengchenccc/message";
4
+ import { assemblePluginRuntime } from "../../core/plugins/plugin-resolve.js";
5
+ import { approvalTimeoutMs, withApprovalDeadline, } from "../../core/runtime/approval.js";
6
+ import { createOmaRuntime } from "../../core/runtime/create-runtime.js";
7
+ import { buildSystemPrompt, readMemorySummary } from "../../core/runtime/prompts.js";
8
+ import { appendSessionCompaction, appendSessionMessages, loadSessionMessages, newSessionId, } from "../../core/session/session-file.js";
9
+ import { readWorkspaceSystemPrompt, scanWorkspaceSkillRoots, } from "../../core/settings/workspace-context.js";
10
+ import { codingAgentCommandSchema, eventOutputSchema, outcomeOutputSchema, responseOutputSchema, } from "../../protocol/index.js";
11
+ import { createJsonlReader } from "./jsonl.js";
12
+ function redactError(err) {
13
+ return err instanceof Error ? err.message : String(err);
14
+ }
15
+ export function runRpcMode(opts) {
16
+ const stdin = opts.stdin ?? Bun.stdin.stream();
17
+ const write = opts.writeLine ?? ((line) => process.stdout.write(line));
18
+ const log = opts.log ?? ((line) => process.stderr.write(`${line}\n`));
19
+ // Serialized output: whole lines, in order, never interleaved.
20
+ let writeChain = Promise.resolve();
21
+ const emit = (output) => {
22
+ const line = JSON.stringify(output);
23
+ writeChain = writeChain.then(() => {
24
+ write(`${line}\n`);
25
+ });
26
+ };
27
+ const emitResponse = (id, command, success, error) => {
28
+ emit(responseOutputSchema.parse({
29
+ id,
30
+ type: "response",
31
+ command,
32
+ success,
33
+ ...(success ? {} : { error: error ?? "command failed" }),
34
+ }));
35
+ };
36
+ let runtime = null;
37
+ let currentRunId = null;
38
+ let executed = false;
39
+ let finished = false;
40
+ const reader = createJsonlReader(stdin);
41
+ /** runId → (callId → resolver). Late/unknown resolutions fail soft. */
42
+ const pendingApprovalsByRun = new Map();
43
+ const promise = (async () => {
44
+ try {
45
+ for await (const line of reader.lines) {
46
+ if (finished) {
47
+ // The Run settled: the process exits. One more line is read so a
48
+ // protocol-violating second execute gets an explicit rejection;
49
+ // anything else (or EOF) just ends the process.
50
+ if (line.trim()) {
51
+ try {
52
+ const command = codingAgentCommandSchema.parse(JSON.parse(line));
53
+ if (command.type === "execute") {
54
+ emitResponse(command.id, "execute", false, "a process accepts at most one execute command");
55
+ }
56
+ }
57
+ catch {
58
+ /* ignored: the process is exiting */
59
+ }
60
+ }
61
+ break;
62
+ }
63
+ if (!line.trim())
64
+ continue;
65
+ let command;
66
+ try {
67
+ command = codingAgentCommandSchema.parse(JSON.parse(line));
68
+ }
69
+ catch {
70
+ // Malformed JSON: a failure response keeps the protocol clean; the
71
+ // peer settles the Run failed on an uncorrelated response.
72
+ log(`malformed command (${line.length} bytes)`);
73
+ emit(responseOutputSchema.parse({
74
+ id: "",
75
+ type: "response",
76
+ command: "execute",
77
+ success: false,
78
+ error: "malformed JSON command",
79
+ }));
80
+ continue;
81
+ }
82
+ switch (command.type) {
83
+ case "execute": {
84
+ if (executed) {
85
+ // Protocol invariant: one process → at most one execute.
86
+ emitResponse(command.id, "execute", false, "a process accepts at most one execute command");
87
+ break;
88
+ }
89
+ executed = true; // consumed synchronously: no double-execute race
90
+ // Acceptance is AWAITED so the success response is always the
91
+ // first output; the loop itself runs concurrently below so
92
+ // steer/abort written after acceptance stay routable.
93
+ await acceptExecute(command);
94
+ break;
95
+ }
96
+ case "steer":
97
+ handleSteer(command);
98
+ break;
99
+ case "abort":
100
+ handleAbort(command);
101
+ break;
102
+ case "resolve_approval": {
103
+ const resolve = pendingApprovalsByRun.get(command.runId)?.get(command.callId);
104
+ if (resolve) {
105
+ resolve({ decision: command.decision });
106
+ emitResponse(command.id, "resolve_approval", true);
107
+ }
108
+ else {
109
+ emitResponse(command.id, "resolve_approval", false, `no pending approval ${command.callId}`);
110
+ }
111
+ break;
112
+ }
113
+ }
114
+ }
115
+ if (!finished) {
116
+ log(`stdin closed before an outcome was produced (executed=${executed})`);
117
+ return 1;
118
+ }
119
+ return await writeChain.then(() => 0);
120
+ }
121
+ catch (err) {
122
+ log(`rpc mode failed: ${redactError(err)}`);
123
+ return 1;
124
+ }
125
+ })();
126
+ /** Validate + assemble the Runtime, START the loop, and emit the execute
127
+ * response ONLY once the loop is live (agent_start). Awaited by the
128
+ * reader so the acceptance response is always the first output AND
129
+ * implies steer/abort are routable; the outcome runs CONCURRENTLY via
130
+ * driveOutcome, keeping steer/abort routable for the whole run. */
131
+ async function acceptExecute(command) {
132
+ const input = command.input;
133
+ debugLog("oma", `execute_received runId=${input.run.runId}`);
134
+ const err = await validateExecute(input, opts.modelRuntime);
135
+ if (err) {
136
+ emitResponse(command.id, "execute", false, err);
137
+ return;
138
+ }
139
+ const runId = input.run.runId;
140
+ // Session (ADR 0003 decision 6): the child owns its session file; the
141
+ // product forwards the branch's opaque reference. Resume loads the
142
+ // transcript as the loop's seed history (validated at the file
143
+ // boundary); a ref whose file is missing/empty degrades to a fresh
144
+ // session (the first-turn bridge already lives in the input message).
145
+ const resumeId = input.run.cliSessionRef;
146
+ const sessionId = resumeId ?? newSessionId();
147
+ const loaded = resumeId ? loadSessionMessages(resumeId) : [];
148
+ // A corrupt line must degrade that entry (log + skip), never brick the
149
+ // whole resume: MessageSchema.parse throws on the first bad shape.
150
+ const parsedTranscript = [];
151
+ for (const [i, message] of loaded.entries()) {
152
+ try {
153
+ parsedTranscript.push({
154
+ productEntryId: `session:${i}`,
155
+ message: MessageSchema.parse(message),
156
+ });
157
+ }
158
+ catch {
159
+ console.warn(`[rpc] skipping malformed session line ${i} for ${resumeId}`);
160
+ }
161
+ }
162
+ const sessionTranscript = parsedTranscript.length > 0 ? parsedTranscript : undefined;
163
+ let effectiveInput;
164
+ let segment;
165
+ try {
166
+ // cwd-based meta (ADR 0003 decision 6): skills and the system prompt
167
+ // live in workspace files (.oma/skills + AGENTS.md/SOUL.md/USER.md).
168
+ // Explicit run-input values (Loop scopes) win over the cwd fallback.
169
+ const cwdSkills = scanWorkspaceSkillRoots(input.workspace.root);
170
+ const cwdPrompt = readWorkspaceSystemPrompt(input.workspace.root);
171
+ effectiveInput = input.run.systemPrompt
172
+ ? input
173
+ : {
174
+ ...input,
175
+ run: {
176
+ ...input.run,
177
+ systemPrompt: buildSystemPrompt({
178
+ workspacePrompt: cwdPrompt,
179
+ memorySummary: readMemorySummary(input.workspace.root),
180
+ cwd: input.workspace.root,
181
+ }),
182
+ },
183
+ };
184
+ // Plugin components (spec): policy resolved in the mode layer — RPC
185
+ // NEVER loads project-scope code; user-scope needs enablement only.
186
+ const pluginRt = await assemblePluginRuntime(input.workspace.root, "rpc");
187
+ for (const w of pluginRt.warnings)
188
+ debugLog("oma", `plugin: ${w}`);
189
+ // HITL approval pipe (spec): emit approval_request on stdout, park the
190
+ // resolver, resolve on the resolve_approval command; deadline = deny.
191
+ const pendingApprovals = new Map();
192
+ pendingApprovalsByRun.set(runId, pendingApprovals);
193
+ let approvalSeq = 10_000;
194
+ const rpcApproval = (req) => new Promise((resolve) => {
195
+ pendingApprovals.set(req.callId, resolve);
196
+ emit(eventOutputSchema.parse({
197
+ type: "event",
198
+ runId,
199
+ // Own id space (10k+): the runtime's envelope seq is internal;
200
+ // consumers key on type + data.callId, not global id order.
201
+ event: {
202
+ id: approvalSeq++,
203
+ type: "approval_request",
204
+ data: {
205
+ callId: req.callId,
206
+ toolName: req.toolName,
207
+ reason: req.reason ?? `${req.toolName} requested approval (${req.source})`,
208
+ input: req.input,
209
+ },
210
+ },
211
+ }));
212
+ });
213
+ runtime = await createOmaRuntime({
214
+ runId,
215
+ modelId: input.run.model.modelId,
216
+ workspaceRoot: input.workspace.root,
217
+ workspaceAccess: input.workspace.access,
218
+ modelRuntime: opts.modelRuntime,
219
+ skillRoots: input.run.skillRoots?.length ? input.run.skillRoots : cwdSkills,
220
+ ...(pluginRt.plugins.length || pluginRt.mcpServers.length
221
+ ? { pluginComponents: { plugins: pluginRt.plugins, mcpServers: pluginRt.mcpServers } }
222
+ : {}),
223
+ ...(input.run.permissionMode ? { permissionMode: input.run.permissionMode } : {}),
224
+ approvalHandler: (req) => withApprovalDeadline(rpcApproval(req), approvalTimeoutMs()),
225
+ sessionTranscript,
226
+ onEvent: (event) => {
227
+ if (!finished)
228
+ emit(eventOutputSchema.parse({ type: "event", runId, event }));
229
+ },
230
+ });
231
+ // run() resolves when the loop is live: acceptance ⟹ routable.
232
+ segment = await runtime.run(effectiveInput);
233
+ debugLog("oma", `runtime_assembled runId=${runId} skills=${(input.run.skillRoots?.length ? input.run.skillRoots : cwdSkills).length} access=${input.workspace.access}`);
234
+ }
235
+ catch (caught) {
236
+ emitResponse(command.id, "execute", false, `runtime assembly failed: ${redactError(caught)}`);
237
+ return;
238
+ }
239
+ // Acceptance: the runtime is assembled, event forwarding is registered,
240
+ // the loop is live, and steer/abort route to it.
241
+ currentRunId = runId;
242
+ debugLog("oma", `loop_live runId=${runId}`);
243
+ emitResponse(command.id, "execute", true, undefined);
244
+ void driveOutcome(runtime, segment, runId, sessionId, effectiveInput.input.message);
245
+ }
246
+ /** Await the outcome, emit the outcome envelope, flush, close the runtime,
247
+ * then END the reader so the process exits on its own (one Run → one
248
+ * outcome → exit) - no dependency on the parent closing stdin. */
249
+ async function driveOutcome(runtime, segment, runId, sessionId, inputMessage) {
250
+ let outcome;
251
+ try {
252
+ outcome = await segment.outcome;
253
+ }
254
+ catch (caught) {
255
+ outcome = { status: "failed", error: redactError(caught) };
256
+ }
257
+ // Persist the turn into the child's session file (user + assistant/tool
258
+ // messages) so the next run resumes the transcript (ADR 0003).
259
+ if (outcome.status === "completed" && outcome.messages?.length) {
260
+ appendSessionMessages(sessionId, process.cwd(), [inputMessage, ...outcome.messages]);
261
+ for (const summary of await runtime.compactions()) {
262
+ appendSessionCompaction(sessionId, summary);
263
+ }
264
+ }
265
+ const outcomeWithRef = {
266
+ ...outcome,
267
+ cliSessionRef: sessionId,
268
+ };
269
+ finished = true;
270
+ await runtime.close().catch(() => { });
271
+ debugLog("oma", `runtime_closed runId=${runId}`);
272
+ emit(outcomeOutputSchema.parse({ type: "outcome", runId, outcome: outcomeWithRef }));
273
+ debugLog("oma", `outcome runId=${runId} status=${outcome.status}`);
274
+ await writeChain;
275
+ // Unblock the reader: the pending stdin read resolves done and the main
276
+ // promise returns; main() exits with the code. Never a hard process.exit
277
+ // before the outcome is written and the runtime closed.
278
+ await reader.cancel();
279
+ debugLog("oma", `rpc_exit runId=${runId}`);
280
+ }
281
+ function handleSteer(command) {
282
+ debugLog("oma", `steer_received runId=${command.runId}`);
283
+ if (!executed || command.runId !== currentRunId || !runtime) {
284
+ emitResponse(command.id, "steer", false, `no live run for runId: ${command.runId} (current: ${currentRunId ?? "none"})`);
285
+ return;
286
+ }
287
+ try {
288
+ void runtime.steer(command.input).then(() => emitResponse(command.id, "steer", true), (err) => emitResponse(command.id, "steer", false, redactError(err)));
289
+ }
290
+ catch (caught) {
291
+ emitResponse(command.id, "steer", false, redactError(caught));
292
+ }
293
+ }
294
+ function handleAbort(command) {
295
+ debugLog("oma", `abort_received runId=${command.runId}`);
296
+ if (!executed || command.runId !== currentRunId || !runtime) {
297
+ emitResponse(command.id, "abort", false, `no live run for runId: ${command.runId} (current: ${currentRunId ?? "none"})`);
298
+ return;
299
+ }
300
+ void runtime.stop().then(() => emitResponse(command.id, "abort", true), (err) => emitResponse(command.id, "abort", false, redactError(err)));
301
+ }
302
+ return {
303
+ promise,
304
+ stop() {
305
+ if (runtime)
306
+ void runtime.stop().catch(() => { });
307
+ },
308
+ };
309
+ }
310
+ /** Execute acceptance preflight: payload schema valid (already parsed),
311
+ * model valid/available in the runtime catalog, workspace valid. */
312
+ async function validateExecute(input, modelRuntime) {
313
+ if (input.run.model.backendKind !== "oma") {
314
+ return `unsupported backend kind: ${input.run.model.backendKind}`;
315
+ }
316
+ if (!existsSync(input.workspace.root) || !statSync(input.workspace.root).isDirectory()) {
317
+ return `workspace root is not a directory: ${input.workspace.root}`;
318
+ }
319
+ const catalog = await modelRuntime.getCatalog();
320
+ const model = catalog.models.find((m) => `${m.providerId}/${m.modelId}` === input.run.model.modelId);
321
+ if (!model)
322
+ return `model not found in catalog: ${input.run.model.modelId}`;
323
+ if (model.available === false)
324
+ return `model unavailable: ${input.run.model.modelId}`;
325
+ return null;
326
+ }
@@ -0,0 +1,23 @@
1
+ import { Container } from "@chengchenccc/tui";
2
+ import type { ProjectSettings } from "../../core/settings/project-settings.js";
3
+ /** Modal settings editor (omp SettingsSelector-container-inspired but lean):
4
+ * a SelectList of rows; Enter toggles booleans or opens a text Input for
5
+ * numeric/string values; Esc closes. Changes are held in a local copy. */
6
+ export declare class SettingsOverlay extends Container {
7
+ private readonly onDone;
8
+ private readonly settings;
9
+ private list;
10
+ private readonly listSlot;
11
+ private editing;
12
+ constructor(settings: ProjectSettings, onDone: () => void);
13
+ getSettings(): ProjectSettings;
14
+ private displayValue;
15
+ private itemFor;
16
+ private rebuild;
17
+ private handleSelect;
18
+ private beginEdit;
19
+ private commitEdit;
20
+ handleInput(data: string): void;
21
+ render(width: number): string[];
22
+ }
23
+ //# sourceMappingURL=settings-overlay.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings-overlay.d.ts","sourceRoot":"","sources":["../../../src/modes/tui/settings-overlay.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,SAAS,EAKV,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yCAAyC,CAAC;AAmD/E;;2EAE2E;AAC3E,qBAAa,eAAgB,SAAQ,SAAS;IAQ1C,OAAO,CAAC,QAAQ,CAAC,MAAM;IAPzB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAkB;IAC3C,OAAO,CAAC,IAAI,CAAc;IAC1B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA8B;IACvD,OAAO,CAAC,OAAO,CAAkD;gBAG/D,QAAQ,EAAE,eAAe,EACR,MAAM,EAAE,MAAM,IAAI;IAOrC,WAAW,IAAI,eAAe;IAI9B,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,OAAO;IAQf,OAAO,CAAC,OAAO;IAiBf,OAAO,CAAC,YAAY;IAWpB,OAAO,CAAC,SAAS;IAcjB,OAAO,CAAC,UAAU;IAgBlB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAQtB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE;CAUzC"}
@@ -0,0 +1,136 @@
1
+ import { applyBackgroundToLine, Container, Input, SelectList, truncateToWidth, } from "@chengchenccc/tui";
2
+ const OVERLAY_BG = (s) => `\u001b[48;5;235m${s}\u001b[0m`;
3
+ function overlayLines(lines, width) {
4
+ const innerWidth = Math.max(1, width - 2);
5
+ return lines.map((line) => {
6
+ const content = truncateToWidth(line, innerWidth, "", true);
7
+ return `\u001b[36m\u2502\u001b[0m${applyBackgroundToLine(content, innerWidth, OVERLAY_BG)}\u001b[36m\u2502\u001b[0m`;
8
+ });
9
+ }
10
+ const THEME = {
11
+ selectedPrefix: (s) => `\u001b[36m${s}\u001b[0m`,
12
+ selectedText: (s) => `\u001b[1m${s}\u001b[0m`,
13
+ description: (s) => `\u001b[2m${s}\u001b[0m`,
14
+ scrollInfo: (s) => `\u001b[2m${s}\u001b[0m`,
15
+ noMatch: (s) => `\u001b[2m${s}\u001b[0m`,
16
+ };
17
+ const ROWS = [
18
+ { key: "maxSteps", label: "maxSteps", kind: "number" },
19
+ { key: "modelTimeoutMs", label: "modelTimeoutMs", kind: "number" },
20
+ { key: "mcpTimeoutMs", label: "mcpTimeoutMs", kind: "number" },
21
+ { key: "bashTimeoutMs", label: "bashTimeoutMs", kind: "number" },
22
+ { key: "maxToolTimeoutMs", label: "maxToolTimeoutMs", kind: "number" },
23
+ { key: "memoryExtract", label: "memoryExtract", kind: "boolean" },
24
+ { key: "memoryModel", label: "memoryModel", kind: "string" },
25
+ { key: "titleEnabled", label: "titleEnabled", kind: "boolean" },
26
+ { key: "disableWeb", label: "disableWeb", kind: "boolean" },
27
+ { key: "enableClaude", label: "enableClaude", kind: "boolean" },
28
+ { key: "enableCodex", label: "enableCodex", kind: "boolean" },
29
+ { key: "enableAgents", label: "enableAgents", kind: "boolean" },
30
+ ];
31
+ function rowValue(settings, key) {
32
+ const v = settings[key];
33
+ if (typeof v === "boolean")
34
+ return v ? "on" : "off";
35
+ if (v === undefined || v === null || v === "")
36
+ return "(unset)";
37
+ if (Array.isArray(v))
38
+ return v.length > 0 ? `${v.length} dirs` : "(none)";
39
+ return String(v);
40
+ }
41
+ /** Modal settings editor (omp SettingsSelector-container-inspired but lean):
42
+ * a SelectList of rows; Enter toggles booleans or opens a text Input for
43
+ * numeric/string values; Esc closes. Changes are held in a local copy. */
44
+ export class SettingsOverlay extends Container {
45
+ onDone;
46
+ settings;
47
+ list;
48
+ listSlot = new Container();
49
+ editing = null;
50
+ constructor(settings, onDone) {
51
+ super();
52
+ this.onDone = onDone;
53
+ this.settings = { ...settings };
54
+ this.rebuild();
55
+ }
56
+ getSettings() {
57
+ return this.settings;
58
+ }
59
+ displayValue(key) {
60
+ return rowValue(this.settings, key);
61
+ }
62
+ itemFor(row) {
63
+ return {
64
+ value: row.key,
65
+ label: row.label,
66
+ description: `${row.kind} · ${this.displayValue(row.key)}`,
67
+ };
68
+ }
69
+ rebuild() {
70
+ this.list = new SelectList(ROWS.map((row) => this.itemFor(row)), 12, THEME, { minPrimaryColumnWidth: 16, maxPrimaryColumnWidth: 24 });
71
+ this.list.onSelect = (item) => {
72
+ const row = ROWS.find((r) => r.key === item.value);
73
+ if (row)
74
+ this.handleSelect(row);
75
+ };
76
+ this.list.onCancel = () => this.onDone();
77
+ this.listSlot.clear();
78
+ this.listSlot.addChild(this.list);
79
+ this.invalidate();
80
+ }
81
+ handleSelect(row) {
82
+ if (row.kind === "boolean") {
83
+ const current = this.settings[row.key];
84
+ const next = typeof current === "boolean" ? !current : true;
85
+ Reflect.set(this.settings, row.key, next);
86
+ this.rebuild();
87
+ return;
88
+ }
89
+ this.beginEdit(row);
90
+ }
91
+ beginEdit(row) {
92
+ const input = new Input();
93
+ input.setValue(this.displayValue(row.key) === "(unset)" ? "" : String(this.settings[row.key]));
94
+ input.onSubmit = () => {
95
+ this.commitEdit(row, input.getValue());
96
+ };
97
+ input.onEscape = () => {
98
+ this.editing = null;
99
+ this.invalidate();
100
+ };
101
+ this.editing = { row, input };
102
+ this.invalidate();
103
+ }
104
+ commitEdit(row, raw) {
105
+ let value = raw.trim();
106
+ if (row.kind === "number") {
107
+ const parsed = Number(raw.trim());
108
+ if (!Number.isFinite(parsed)) {
109
+ this.editing = null;
110
+ this.invalidate();
111
+ return;
112
+ }
113
+ value = parsed;
114
+ }
115
+ Reflect.set(this.settings, row.key, value);
116
+ this.editing = null;
117
+ this.rebuild();
118
+ }
119
+ handleInput(data) {
120
+ if (this.editing) {
121
+ this.editing.input.handleInput(data);
122
+ return;
123
+ }
124
+ this.list.handleInput(data);
125
+ }
126
+ render(width) {
127
+ const lines = [];
128
+ lines.push(" settings — enter to edit, esc to close");
129
+ if (this.editing) {
130
+ lines.push(` edit ${this.editing.row.label} (${this.editing.row.kind}):`);
131
+ lines.push(...this.editing.input.render(Math.max(1, width - 4)));
132
+ }
133
+ lines.push(...this.list.render(Math.max(1, width - 2)));
134
+ return overlayLines(lines, width);
135
+ }
136
+ }
@@ -0,0 +1,3 @@
1
+ import type { CommandDef, TuiSessionContext } from "./tui-commands.js";
2
+ export declare function buildSessionCommands(ctx: TuiSessionContext): CommandDef[];
3
+ //# sourceMappingURL=tui-commands-session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tui-commands-session.d.ts","sourceRoot":"","sources":["../../../src/modes/tui/tui-commands-session.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAGvE,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,iBAAiB,GAAG,UAAU,EAAE,CAiMzE"}