@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,23 @@
1
+ /** HITL approval (spec: 2026-08-26-plugin-trust-model-design.md): one handler
2
+ * type, three resolution pipelines (tui overlay / print+json fail-closed /
3
+ * rpc wire with deadline). */
4
+ export interface ApprovalRequest {
5
+ readonly callId: string;
6
+ readonly toolName: string;
7
+ readonly input: unknown;
8
+ /** Who asked: "permission" (ask-mode gate) or "tool" (options.request). */
9
+ readonly source: "permission" | "tool";
10
+ readonly reason?: string;
11
+ }
12
+ export interface ApprovalDecision {
13
+ readonly decision: "allow" | "deny";
14
+ readonly reason?: string;
15
+ }
16
+ export type ApprovalHandler = (req: ApprovalRequest) => Promise<ApprovalDecision>;
17
+ export declare const DEFAULT_APPROVAL_TIMEOUT_MS = 120000;
18
+ export declare function approvalTimeoutMs(): number;
19
+ /** Resolve with a deadline; a silent human fails closed (deny). 0 = wait. */
20
+ export declare function withApprovalDeadline(p: Promise<ApprovalDecision>, timeoutMs: number): Promise<ApprovalDecision>;
21
+ /** The fail-closed handler for headless one-shot modes (print/json). */
22
+ export declare function denyAllApprovals(req: ApprovalRequest): Promise<ApprovalDecision>;
23
+ //# sourceMappingURL=approval.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"approval.d.ts","sourceRoot":"","sources":["../../../src/core/runtime/approval.ts"],"names":[],"mappings":"AAAA;;+BAE+B;AAE/B,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,2EAA2E;IAC3E,QAAQ,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAAC;IACvC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,QAAQ,EAAE,OAAO,GAAG,MAAM,CAAC;IACpC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,MAAM,eAAe,GAAG,CAAC,GAAG,EAAE,eAAe,KAAK,OAAO,CAAC,gBAAgB,CAAC,CAAC;AAElF,eAAO,MAAM,2BAA2B,SAAU,CAAC;AAEnD,wBAAgB,iBAAiB,IAAI,MAAM,CAI1C;AAED,6EAA6E;AAC7E,wBAAgB,oBAAoB,CAClC,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,EAC5B,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,gBAAgB,CAAC,CAW3B;AAED,wEAAwE;AACxE,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAKhF"}
@@ -0,0 +1,25 @@
1
+ /** HITL approval (spec: 2026-08-26-plugin-trust-model-design.md): one handler
2
+ * type, three resolution pipelines (tui overlay / print+json fail-closed /
3
+ * rpc wire with deadline). */
4
+ export const DEFAULT_APPROVAL_TIMEOUT_MS = 120_000;
5
+ export function approvalTimeoutMs() {
6
+ const raw = process.env.OMA_APPROVAL_TIMEOUT_MS;
7
+ const n = raw === undefined ? DEFAULT_APPROVAL_TIMEOUT_MS : Number(raw);
8
+ return Number.isFinite(n) && n >= 0 ? n : DEFAULT_APPROVAL_TIMEOUT_MS;
9
+ }
10
+ /** Resolve with a deadline; a silent human fails closed (deny). 0 = wait. */
11
+ export function withApprovalDeadline(p, timeoutMs) {
12
+ if (timeoutMs === 0)
13
+ return p;
14
+ return Promise.race([
15
+ p,
16
+ new Promise((resolve) => setTimeout(() => resolve({ decision: "deny", reason: "approval deadline exceeded" }), timeoutMs)),
17
+ ]);
18
+ }
19
+ /** The fail-closed handler for headless one-shot modes (print/json). */
20
+ export function denyAllApprovals(req) {
21
+ return Promise.resolve({
22
+ decision: "deny",
23
+ reason: `${req.toolName}: approval requested in non-interactive mode (fail-closed)`,
24
+ });
25
+ }
@@ -0,0 +1,22 @@
1
+ import type { Message } from "@chengchenccc/message";
2
+ import type { SessionStore } from "../persistence/session-store.js";
3
+ export interface CompactionResult {
4
+ readonly entryId: string;
5
+ readonly coveredIds: readonly string[];
6
+ }
7
+ /** Token budget used for compaction cut points. `estimate` is a per-message
8
+ * token cost; `limit` is the target ceiling for the retained context. */
9
+ export interface CompactionBudget {
10
+ estimate(message: Message): number;
11
+ limit: number;
12
+ }
13
+ /** Compact a session branch: summarize oldest entries, append CompactionEntry,
14
+ * leave tail entries intact. Shared by threshold/manual/overflow triggers.
15
+ *
16
+ * The summarizer receives full Message objects (including tool_use/tool_result
17
+ * blocks) so tool semantics survive compaction. The AbortSignal lets stop()
18
+ * cancel an in-flight summarizer; an aborted compaction writes nothing.
19
+ * No-op when the context is already under budget (no summarizer call, no
20
+ * CompactionEntry), and never writes an entry with empty coversEntryIds. */
21
+ export declare function compactSession(store: SessionStore, sessionId: string, summarizer: (messages: readonly Message[], signal?: AbortSignal) => Promise<string>, signal?: AbortSignal, budget?: CompactionBudget): Promise<CompactionResult>;
22
+ //# sourceMappingURL=compaction.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compaction.d.ts","sourceRoot":"","sources":["../../../src/core/runtime/compaction.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAGpE,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;CACxC;AAED;0EAC0E;AAC1E,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAAC;IACnC,KAAK,EAAE,MAAM,CAAC;CACf;AA6DD;;;;;;;6EAO6E;AAC7E,wBAAsB,cAAc,CAClC,KAAK,EAAE,YAAY,EACnB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,CAAC,QAAQ,EAAE,SAAS,OAAO,EAAE,EAAE,MAAM,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,MAAM,CAAC,EACnF,MAAM,CAAC,EAAE,WAAW,EACpB,MAAM,CAAC,EAAE,gBAAgB,GACxB,OAAO,CAAC,gBAAgB,CAAC,CAsC3B"}
@@ -0,0 +1,90 @@
1
+ /** If the cut lands between an assistant tool_use and its tool_result,
2
+ * move the cut before the assistant so the pair stays intact. */
3
+ function adjustCutForToolPairs(messages, cutIdx) {
4
+ let cut = cutIdx;
5
+ while (cut < messages.length && messages[cut]?.type === "message") {
6
+ const first = messages[cut];
7
+ if (first.role !== "tool")
8
+ break;
9
+ const toolUseId = first.message?.blocks?.find((b) => b.type === "tool_result")?.tool_use_id;
10
+ if (!toolUseId)
11
+ break;
12
+ let assocIdx = -1;
13
+ for (let i = 0; i < cut; i++) {
14
+ const m = messages[i];
15
+ if (m.role === "assistant" &&
16
+ m.message?.blocks?.some((b) => b.type === "tool_use" && b.id === toolUseId)) {
17
+ assocIdx = i;
18
+ }
19
+ }
20
+ if (assocIdx === -1)
21
+ break;
22
+ cut = assocIdx;
23
+ break;
24
+ }
25
+ return cut;
26
+ }
27
+ /** Compute the compaction cut point. Always returns a value: without a budget
28
+ * it uses a message-count heuristic; with a budget it accumulates tokens from
29
+ * the oldest messages until the retained tail fits under the limit. When the
30
+ * context is already under the limit, cutIdx is 0 (no compaction needed). */
31
+ function findCut(messages, budget) {
32
+ if (!budget) {
33
+ return { cutIdx: Math.floor(messages.length * 0.6) };
34
+ }
35
+ const tokens = messages.map((m) => budget.estimate(m.message));
36
+ const tokensBefore = tokens.reduce((sum, t) => sum + t, 0);
37
+ if (tokensBefore <= budget.limit) {
38
+ return { cutIdx: 0, tokensBefore };
39
+ }
40
+ let remaining = tokensBefore;
41
+ let cutIdx = 0;
42
+ while (cutIdx < messages.length && remaining > budget.limit) {
43
+ remaining -= tokens[cutIdx];
44
+ cutIdx++;
45
+ }
46
+ return { cutIdx, tokensBefore };
47
+ }
48
+ /** Compact a session branch: summarize oldest entries, append CompactionEntry,
49
+ * leave tail entries intact. Shared by threshold/manual/overflow triggers.
50
+ *
51
+ * The summarizer receives full Message objects (including tool_use/tool_result
52
+ * blocks) so tool semantics survive compaction. The AbortSignal lets stop()
53
+ * cancel an in-flight summarizer; an aborted compaction writes nothing.
54
+ * No-op when the context is already under budget (no summarizer call, no
55
+ * CompactionEntry), and never writes an entry with empty coversEntryIds. */
56
+ export async function compactSession(store, sessionId, summarizer, signal, budget) {
57
+ const branch = await store.readBranch(sessionId);
58
+ const messages = branch.filter((e) => e.type === "message");
59
+ if (messages.length < 4)
60
+ return { entryId: "", coveredIds: [] };
61
+ const { cutIdx: rawCutIdx, tokensBefore } = findCut(messages, budget);
62
+ let cutIdx = rawCutIdx;
63
+ cutIdx = adjustCutForToolPairs(messages, cutIdx);
64
+ // No compaction needed: nothing to cover (under budget or cut collapsed).
65
+ if (cutIdx <= 0)
66
+ return { entryId: "", coveredIds: [] };
67
+ const coveredEntries = messages.slice(0, cutIdx);
68
+ const coveredIds = coveredEntries.map((m) => m.entryId);
69
+ const retainedIds = messages.slice(cutIdx).map((m) => m.entryId);
70
+ const coveredMessages = coveredEntries.map((m) => m.message);
71
+ const summary = await summarizer(coveredMessages, signal);
72
+ // If aborted during summarization, do not write a CompactionEntry.
73
+ if (signal?.aborted)
74
+ return { entryId: "", coveredIds: [] };
75
+ const result = await store.appendBatch(sessionId, {
76
+ entries: [
77
+ {
78
+ type: "compaction",
79
+ summary,
80
+ coversEntryIds: coveredIds,
81
+ ...(budget ? { tokensBefore, retainedEntryIds: retainedIds } : {}),
82
+ createdAt: Date.now(),
83
+ },
84
+ ],
85
+ });
86
+ return {
87
+ entryId: result.appendedIds[0] ?? "",
88
+ coveredIds: coveredIds,
89
+ };
90
+ }
@@ -0,0 +1,42 @@
1
+ /** Context-token estimation helpers absorbed from oh-my-pi
2
+ * (packages/agent/src/harness/compaction/compaction.ts):
3
+ * usage-anchored estimation + silent-overflow detection. */
4
+ /** Per-turn usage quartet (concrete, unlike the all-optional run Usage). */
5
+ export interface TurnUsage {
6
+ readonly inputTokens: number;
7
+ readonly outputTokens: number;
8
+ readonly cacheReadTokens: number;
9
+ readonly cacheWriteTokens: number;
10
+ }
11
+ /** A real-usage anchor: `tokens` is a completed model call's own total
12
+ * (input + output + cache legs) and is authoritative for every entry
13
+ * persisted before `afterEntryId`; only entries AFTER it fall back to
14
+ * per-message estimation (chars/4). Falls back wholesale when the anchor
15
+ * entry no longer exists (post-compaction branch, resumed runs).
16
+ *
17
+ * ponytail: the anchor is in-memory per Run — pi persists usage on
18
+ * assistant messages and re-anchors after resume. Persist it onto the
19
+ * session file if follow-up Runs ever drift badly. */
20
+ export interface UsageAnchor {
21
+ readonly afterEntryId: string | null;
22
+ readonly tokens: number;
23
+ }
24
+ export declare function usageTotalTokens(usage: TurnUsage): number;
25
+ /** Context size estimate anchored on the last real usage: anchor tokens +
26
+ * per-entry estimates for entries persisted after the anchor boundary.
27
+ * Without an anchor (or when the boundary entry is gone) every entry is
28
+ * estimated — the pre-anchor behavior. */
29
+ export declare function estimateContextTokens<T extends {
30
+ entryId: string;
31
+ }>(entries: readonly T[], anchor: UsageAnchor | null, estimateEntry: (entry: T) => number): number;
32
+ /** Silent context overflow (oh-my-pi isContextOverflow): some providers
33
+ * accept an oversized request instead of erroring.
34
+ * - zai-style: the input side exceeds the model window — never legitimate.
35
+ * - Xiaomi-style: input truncated to exactly fill the window, leaving zero
36
+ * room to generate (length-stop with zero output). */
37
+ export declare function isSilentContextOverflow(usage: {
38
+ inputTokens?: number;
39
+ outputTokens?: number;
40
+ cacheReadTokens?: number;
41
+ } | undefined, stopReason: string | undefined, contextLimit: number): boolean;
42
+ //# sourceMappingURL=context-estimate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-estimate.d.ts","sourceRoot":"","sources":["../../../src/core/runtime/context-estimate.ts"],"names":[],"mappings":"AAAA;;4DAE4D;AAE5D,4EAA4E;AAC5E,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;CACnC;AAED;;;;;;;;sDAQsD;AACtD,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,SAAS,GAAG,MAAM,CAEzD;AAED;;;0CAG0C;AAC1C,wBAAgB,qBAAqB,CAAC,CAAC,SAAS;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,EACjE,OAAO,EAAE,SAAS,CAAC,EAAE,EACrB,MAAM,EAAE,WAAW,GAAG,IAAI,EAC1B,aAAa,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,MAAM,GAClC,MAAM,CAOR;AAED;;;;wDAIwD;AACxD,wBAAgB,uBAAuB,CACrC,KAAK,EAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,EAC5F,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,YAAY,EAAE,MAAM,GACnB,OAAO,CAQT"}
@@ -0,0 +1,34 @@
1
+ /** Context-token estimation helpers absorbed from oh-my-pi
2
+ * (packages/agent/src/harness/compaction/compaction.ts):
3
+ * usage-anchored estimation + silent-overflow detection. */
4
+ export function usageTotalTokens(usage) {
5
+ return usage.inputTokens + usage.outputTokens + usage.cacheReadTokens + usage.cacheWriteTokens;
6
+ }
7
+ /** Context size estimate anchored on the last real usage: anchor tokens +
8
+ * per-entry estimates for entries persisted after the anchor boundary.
9
+ * Without an anchor (or when the boundary entry is gone) every entry is
10
+ * estimated — the pre-anchor behavior. */
11
+ export function estimateContextTokens(entries, anchor, estimateEntry) {
12
+ const fallback = () => entries.reduce((sum, e) => sum + estimateEntry(e), 0);
13
+ if (!anchor)
14
+ return fallback();
15
+ const idx = anchor.afterEntryId === null ? -1 : entries.findIndex((e) => e.entryId === anchor.afterEntryId);
16
+ if (anchor.afterEntryId !== null && idx === -1)
17
+ return fallback();
18
+ return anchor.tokens + entries.slice(idx + 1).reduce((sum, e) => sum + estimateEntry(e), 0);
19
+ }
20
+ /** Silent context overflow (oh-my-pi isContextOverflow): some providers
21
+ * accept an oversized request instead of erroring.
22
+ * - zai-style: the input side exceeds the model window — never legitimate.
23
+ * - Xiaomi-style: input truncated to exactly fill the window, leaving zero
24
+ * room to generate (length-stop with zero output). */
25
+ export function isSilentContextOverflow(usage, stopReason, contextLimit) {
26
+ if (!usage || contextLimit <= 0)
27
+ return false;
28
+ const input = (usage.inputTokens ?? 0) + (usage.cacheReadTokens ?? 0);
29
+ if (input <= 0)
30
+ return false;
31
+ if (input > contextLimit)
32
+ return true;
33
+ return (stopReason === "max_tokens" && (usage.outputTokens ?? 0) === 0 && input >= contextLimit * 0.99);
34
+ }
@@ -0,0 +1,81 @@
1
+ import type { BackendInputMessage, BackendRunInput, BackendRunSegment, ProjectedHistoryItem } from "@chengchenccc/agent-contract";
2
+ import type { ModelRuntime } from "@chengchenccc/ai";
3
+ import type { Message } from "@chengchenccc/message";
4
+ import type { RunEventEnvelope } from "../../protocol/index.js";
5
+ import { type MemoryLearnResult } from "../memory/autonomous-memory.js";
6
+ import type { PluginMcpConfig } from "../plugins/plugin-resolve.js";
7
+ import type { ApprovalHandler } from "./approval.js";
8
+ import type { Plugin } from "./plugin.js";
9
+ import type { ToolFilter } from "./tool-filter.js";
10
+ /** The single Runtime assembly entry point for the Oma product.
11
+ * Every mode (print / json / rpc / future TUI) builds the SAME runtime from
12
+ * these options: fresh in-memory SessionStore per Runtime, no state shared
13
+ * across Runtimes except the process-level Provider/ModelRuntime. */
14
+ export interface CreateOmaRuntimeOptions {
15
+ runId: string;
16
+ /** Canonical `<provider>/<model>` id of the Run's model: the context
17
+ * budget and summarizer bind to it. */
18
+ modelId: string;
19
+ workspaceRoot: string;
20
+ workspaceAccess: "read_only" | "read_write";
21
+ modelRuntime: ModelRuntime;
22
+ skillRoots: readonly string[];
23
+ /** The resumed CLI session transcript (ADR 0003 decision 6), validated
24
+ * messages from the session file. Seeds the loop's store in place of the
25
+ * retired wire `history` projection. Absent = fresh session. */
26
+ sessionTranscript?: readonly ProjectedHistoryItem[];
27
+ /** Called for every runtime event as a wire envelope (id/type/data), after
28
+ * the in-process segment stream. Used by RPC mode to forward events to
29
+ * stdout; print/json modes leave it unset. */
30
+ onEvent?: (envelope: RunEventEnvelope) => void;
31
+ /** Real-time session-file persistence: fired after every conversational
32
+ * persist (pi appendMessage). When set, the caller owns writing these
33
+ * messages to its session file as they happen. */
34
+ onPersistMessages?: (messages: readonly Message[]) => void;
35
+ /** --tools filter (CLI): applied to the final tool table. */
36
+ toolFilter?: ToolFilter;
37
+ /** Assembled plugin code components (from assemblePluginRuntime, mode
38
+ * layer). The runtime mounts them; it never reads the registry. */
39
+ pluginComponents?: {
40
+ plugins: readonly Plugin[];
41
+ mcpServers?: readonly PluginMcpConfig[];
42
+ };
43
+ /** Frozen Run permissionMode; "deny" drops plugin code components. */
44
+ permissionMode?: "ask" | "auto" | "deny";
45
+ /** Standalone modes: gate the repo-controlled workspace .mcp.json behind
46
+ * the trust record (spec follow-up #3). */
47
+ gateWorkspaceMcp?: boolean;
48
+ /** HITL approval pipeline; absent + ask = fail-closed. */
49
+ approvalHandler?: ApprovalHandler;
50
+ }
51
+ /** One Runtime = one Run. The loop runs directly in-process; steer injects
52
+ * into the live loop; stop aborts it; close tears down MCP clients and the
53
+ * SessionStore. */
54
+ export interface OmaRuntime {
55
+ /** Start the Run's loop. Returns the segment whose outcome is the Run's
56
+ * ONLY terminal result. A Runtime accepts exactly one run(). */
57
+ run(input: BackendRunInput<"oma">): Promise<BackendRunSegment<"oma">>;
58
+ /** Inject a steer input into the live loop. Throws when no loop is live. */
59
+ steer(input: BackendInputMessage): Promise<void>;
60
+ /** Request cancellation of the live loop. The segment's outcome still
61
+ * resolves (aborted). Safe to call before run() starts the loop. */
62
+ stop(): Promise<void>;
63
+ /** Close MCP clients and the in-memory Store. Call after the run settles. */
64
+ close(): Promise<void>;
65
+ /** Compaction summaries from this run's loop; read before close(). */
66
+ compactions(): Promise<string[]>;
67
+ /** Estimated context footprint of the run's branch under the run model's
68
+ * window — the same estimate/limit pair the compactor decides on. Read
69
+ * before close(). */
70
+ contextUsage(): Promise<{
71
+ estimatedTokens: number;
72
+ limit: number;
73
+ } | undefined>;
74
+ /** The run's background memory-learn pass (omp AutoLearn analog).
75
+ * Present once the run completed; never rejects; resolves after close()
76
+ * too (inputs are captured up front). Surfaces use it for the "learn"
77
+ * indicator. */
78
+ memoryLearning(): Promise<MemoryLearnResult> | undefined;
79
+ }
80
+ export declare function createOmaRuntime(options: CreateOmaRuntimeOptions): Promise<OmaRuntime>;
81
+ //# sourceMappingURL=create-runtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-runtime.d.ts","sourceRoot":"","sources":["../../../src/core/runtime/create-runtime.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,mBAAmB,EACnB,eAAe,EAEf,iBAAiB,EACjB,oBAAoB,EACrB,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAErD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAGhE,OAAO,EAA2B,KAAK,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AACjG,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AACpE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD;;;sEAGsE;AACtE,MAAM,WAAW,uBAAuB;IACtC,KAAK,EAAE,MAAM,CAAC;IACd;4CACwC;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,WAAW,GAAG,YAAY,CAAC;IAC5C,YAAY,EAAE,YAAY,CAAC;IAC3B,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9B;;qEAEiE;IACjE,iBAAiB,CAAC,EAAE,SAAS,oBAAoB,EAAE,CAAC;IACpD;;mDAE+C;IAC/C,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAC/C;;uDAEmD;IACnD,iBAAiB,CAAC,EAAE,CAAC,QAAQ,EAAE,SAAS,OAAO,EAAE,KAAK,IAAI,CAAC;IAC3D,6DAA6D;IAC7D,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB;wEACoE;IACpE,gBAAgB,CAAC,EAAE;QACjB,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;QAC3B,UAAU,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;KACzC,CAAC;IACF,sEAAsE;IACtE,cAAc,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;IACzC;gDAC4C;IAC5C,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,0DAA0D;IAC1D,eAAe,CAAC,EAAE,eAAe,CAAC;CACnC;AAED;;oBAEoB;AACpB,MAAM,WAAW,UAAU;IACzB;qEACiE;IACjE,GAAG,CAAC,KAAK,EAAE,eAAe,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC,CAAC;IACtE,4EAA4E;IAC5E,KAAK,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjD;yEACqE;IACrE,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,6EAA6E;IAC7E,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,sEAAsE;IACtE,WAAW,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACjC;;0BAEsB;IACtB,YAAY,IAAI,OAAO,CAAC;QAAE,eAAe,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC,CAAC;IAChF;;;oBAGgB;IAChB,cAAc,IAAI,OAAO,CAAC,iBAAiB,CAAC,GAAG,SAAS,CAAC;CAC1D;AAuDD,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,GAAG,OAAO,CAAC,UAAU,CAAC,CAiN5F"}
@@ -0,0 +1,250 @@
1
+ import { mapRunEvent } from "../../protocol/index.js";
2
+ import { extractAutonomousMemory } from "../memory/autonomous-memory.js";
3
+ import { assembleRunRuntime } from "./run-runtime.js";
4
+ /** Add workflow (subagent) usage into a run's terminal usage. Missing
5
+ * fields default to 0; an all-zero workflow side leaves the base alone. */
6
+ function mergeWorkflowUsage(base, wf) {
7
+ const zero = (v) => v ?? 0;
8
+ const total = zero(wf?.inputTokens) +
9
+ zero(wf?.outputTokens) +
10
+ zero(wf?.cacheReadTokens) +
11
+ zero(wf?.cacheWriteTokens);
12
+ if (total === 0)
13
+ return base;
14
+ return {
15
+ inputTokens: zero(base?.inputTokens) + zero(wf?.inputTokens),
16
+ outputTokens: zero(base?.outputTokens) + zero(wf?.outputTokens),
17
+ cacheReadTokens: zero(base?.cacheReadTokens) + zero(wf?.cacheReadTokens),
18
+ cacheWriteTokens: zero(base?.cacheWriteTokens) + zero(wf?.cacheWriteTokens),
19
+ };
20
+ }
21
+ /** Attach the persisted message trail when present. A failed/aborted run
22
+ * still surfaces what it did so the caller can persist it for the next
23
+ * turn ("continue" resumes the context). */
24
+ function withMessages(outcome, messages) {
25
+ return messages && messages.length > 0 ? { ...outcome, messages } : outcome;
26
+ }
27
+ function mapLoopResult(result) {
28
+ if (result.status === "completed") {
29
+ // No fabricated output: when the loop persisted no canonical messages,
30
+ // omit them rather than inventing an empty sequence.
31
+ return withMessages({
32
+ status: "completed",
33
+ ...(result.usage ? { usage: result.usage } : {}),
34
+ ...(result.title ? { title: result.title } : {}),
35
+ }, result.messages);
36
+ }
37
+ if (result.status === "stopped") {
38
+ return withMessages({ status: "aborted", error: result.error ?? "stopped by user" }, result.messages);
39
+ }
40
+ return withMessages({ status: "failed", error: result.error ?? "loop failed" }, result.messages);
41
+ }
42
+ export async function createOmaRuntime(options) {
43
+ const rt = await assembleRunRuntime({
44
+ workspaceRoot: options.workspaceRoot,
45
+ workspaceAccess: options.workspaceAccess,
46
+ runId: options.runId,
47
+ modelRuntime: options.modelRuntime,
48
+ modelId: options.modelId,
49
+ skillRoots: options.skillRoots,
50
+ ...(options.onPersistMessages ? { onPersistMessages: options.onPersistMessages } : {}),
51
+ ...(options.pluginComponents?.plugins.length
52
+ ? { codePlugins: options.pluginComponents.plugins }
53
+ : {}),
54
+ ...(options.gateWorkspaceMcp ? { gateWorkspaceMcp: true } : {}),
55
+ ...(options.approvalHandler ? { approvalHandler: options.approvalHandler } : {}),
56
+ ...(options.pluginComponents?.mcpServers?.length
57
+ ? { pluginMcpServers: options.pluginComponents.mcpServers }
58
+ : {}),
59
+ ...(options.permissionMode ? { permissionMode: options.permissionMode } : {}),
60
+ ...(options.toolFilter ? { toolFilter: options.toolFilter } : {}),
61
+ });
62
+ let stopRequested = false;
63
+ let started = false;
64
+ let memoryLearning;
65
+ return {
66
+ async run(input) {
67
+ if (started) {
68
+ throw new Error(`runtime ${options.runId} already ran once (one Runtime = one Run)`);
69
+ }
70
+ started = true;
71
+ // run() resolves only when the loop is at a SAFE steer boundary
72
+ // (message_start fires after acceptingSteer=true) or settled without
73
+ // starting - acceptance therefore implies steer/abort are routable,
74
+ // with no timing window.
75
+ let liveResolve = null;
76
+ const live = new Promise((resolve) => {
77
+ liveResolve = resolve;
78
+ });
79
+ let liveSettled = false;
80
+ const settleLive = () => {
81
+ if (liveSettled)
82
+ return;
83
+ liveSettled = true;
84
+ liveResolve?.();
85
+ };
86
+ // Stop landing before the loop starts settles aborted without running.
87
+ if (stopRequested) {
88
+ settleLive();
89
+ const outcome = { status: "aborted", error: "stopped before start" };
90
+ return {
91
+ events: (async function* () { })(),
92
+ outcome: Promise.resolve(outcome),
93
+ stop: async () => { },
94
+ };
95
+ }
96
+ // Push-based event fan-out: onEvent envelopes feed both the RPC stdout
97
+ // writer and the in-process segment stream.
98
+ const queue = [];
99
+ const waiters = [];
100
+ let closed = false;
101
+ let seq = 0;
102
+ const unsubscribe = rt.session.onEvent((event) => {
103
+ if (event.type === "message_start" || event.type === "agent_end")
104
+ settleLive();
105
+ const envelope = { id: seq++, type: event.type, data: event };
106
+ queue.push(envelope);
107
+ options.onEvent?.(envelope);
108
+ for (const w of waiters.splice(0))
109
+ w();
110
+ });
111
+ // The Run's store is seeded with the full Product history + the current
112
+ // input by the loop itself (buildLoopInput appends history + meta +
113
+ // input atomically). Create the session root first.
114
+ await rt.store.create({
115
+ sessionId: options.runId,
116
+ backendKind: "oma",
117
+ workspaceRoot: options.workspaceRoot,
118
+ leafEntryId: null,
119
+ createdAt: Date.now(),
120
+ updatedAt: Date.now(),
121
+ });
122
+ rt.setActiveRun(input.run);
123
+ const outcomePromise = (async () => {
124
+ try {
125
+ // Workflow mode: the input IS a script to execute (Loop items).
126
+ // No interactive loop, no messages — the script's return value is
127
+ // the run's product (outcome.workflow.value).
128
+ if (input.workflow) {
129
+ const result = await rt.executeWorkflow(input.workflow);
130
+ // Top-level usage so product accounting (usage totals, Loop
131
+ // budget) sees the subagent spend; workflow.usage mirrors it.
132
+ const usage = { inputTokens: 0, outputTokens: result.totalTokens };
133
+ return {
134
+ status: "completed",
135
+ usage,
136
+ workflow: { ok: result.ok, value: result.value, usage },
137
+ };
138
+ }
139
+ const result = await rt.session.startLoop({
140
+ history: options.sessionTranscript ?? [],
141
+ input: input.input,
142
+ run: input.run,
143
+ workspace: input.workspace,
144
+ metadata: input.metadata,
145
+ });
146
+ if (result.status === "completed") {
147
+ // Autonomous memory: extract durable facts from this Run's
148
+ // transcript + compaction summaries, persist to the workspace.
149
+ // Best-effort — never affects the outcome.
150
+ const branch = await rt.store.readBranch(options.runId);
151
+ const compactions = [];
152
+ for (const entry of branch) {
153
+ if (entry.type === "compaction" && entry.summary)
154
+ compactions.push(entry.summary);
155
+ }
156
+ // FIRE-AND-FORGET: awaiting here blocks the outcome (and the
157
+ // TUI busy state) on a second model call the user cannot abort.
158
+ memoryLearning = extractAutonomousMemory({
159
+ modelRuntime: options.modelRuntime,
160
+ modelId: options.modelId,
161
+ workspaceRoot: options.workspaceRoot,
162
+ runId: options.runId,
163
+ messages: result.messages ?? [],
164
+ compactions,
165
+ });
166
+ }
167
+ const outcome = mapLoopResult(result);
168
+ if (outcome.status === "completed") {
169
+ // Fan-out spend (run_workflow / workflow_run subagents) merges
170
+ // into the run's terminal usage (B6).
171
+ return { ...outcome, usage: mergeWorkflowUsage(outcome.usage, rt.workflowUsage()) };
172
+ }
173
+ return outcome;
174
+ }
175
+ catch (caught) {
176
+ const errObj = caught instanceof Error ? caught : new Error(String(caught));
177
+ return { status: "failed", error: errObj.message };
178
+ }
179
+ finally {
180
+ settleLive(); // loop settled without starting: live is still resolved
181
+ unsubscribe();
182
+ closed = true;
183
+ for (const w of waiters.splice(0))
184
+ w();
185
+ }
186
+ })();
187
+ const segment = {
188
+ events: (async function* () {
189
+ while (!closed || queue.length > 0) {
190
+ if (queue.length > 0) {
191
+ yield mapRunEvent(queue.shift());
192
+ continue;
193
+ }
194
+ if (closed)
195
+ return;
196
+ await new Promise((resolve) => waiters.push(resolve));
197
+ }
198
+ })(),
199
+ outcome: outcomePromise,
200
+ stop: async () => {
201
+ rt.session.stop();
202
+ },
203
+ };
204
+ // Acceptance = live: run() resolves once steer/abort are routable.
205
+ await live;
206
+ return segment;
207
+ },
208
+ async steer(input) {
209
+ rt.session.steer(input);
210
+ },
211
+ async stop() {
212
+ stopRequested = true;
213
+ rt.session.stop();
214
+ },
215
+ async close() {
216
+ await rt.close();
217
+ },
218
+ /** Compaction summaries produced by this run's loop (empty when the
219
+ * run never compacted). Read the store BEFORE close(). */
220
+ async compactions() {
221
+ const branch = await rt.store.readBranch(options.runId);
222
+ const summaries = [];
223
+ for (const entry of branch) {
224
+ if (entry.type === "compaction" && entry.summary)
225
+ summaries.push(entry.summary);
226
+ }
227
+ return summaries;
228
+ },
229
+ /** Estimated context footprint under the run model's window (the same
230
+ * estimate/limit the compactor uses). Read before close(). */
231
+ async contextUsage() {
232
+ const budget = rt.contextBudget;
233
+ if (!budget)
234
+ return undefined;
235
+ const branch = await rt.store.readBranch(options.runId);
236
+ let estimatedTokens = 0;
237
+ for (const entry of branch) {
238
+ if (entry.type !== "message")
239
+ continue;
240
+ estimatedTokens += budget.estimate(entry.message);
241
+ }
242
+ return { estimatedTokens, limit: budget.limit };
243
+ },
244
+ /** Background memory-learn pass for the completed run (undefined while
245
+ * the run has not completed). */
246
+ memoryLearning() {
247
+ return memoryLearning;
248
+ },
249
+ };
250
+ }
@@ -0,0 +1,3 @@
1
+ import type { Provider } from "@chengchenccc/ai";
2
+ export declare function fakeProvider(env?: Readonly<Record<string, string | undefined>>): Provider;
3
+ //# sourceMappingURL=fake-provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fake-provider.d.ts","sourceRoot":"","sources":["../../../src/core/runtime/fake-provider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAS,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAmCxD,wBAAgB,YAAY,CAC1B,GAAG,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAe,GAC9D,QAAQ,CA4DV"}