@chengchenccc/oh-my-agent 0.2.0-rc.2 → 0.2.0-rc.3

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 (209) hide show
  1. package/README.md +11 -6
  2. package/dist/cli/args.d.ts +1 -1
  3. package/dist/cli/args.d.ts.map +1 -1
  4. package/dist/cli/args.js +9 -2
  5. package/dist/cli/gateway-commands.d.ts.map +1 -1
  6. package/dist/cli/gateway-commands.js +35 -5
  7. package/dist/cli/initial-input.d.ts +3 -2
  8. package/dist/cli/initial-input.d.ts.map +1 -1
  9. package/dist/core/coordination/registry.d.ts +11 -0
  10. package/dist/core/coordination/registry.d.ts.map +1 -1
  11. package/dist/core/coordination/registry.js +23 -0
  12. package/dist/core/gateway/artifact.d.ts +0 -1
  13. package/dist/core/gateway/artifact.d.ts.map +1 -1
  14. package/dist/core/gateway/artifact.js +1 -1
  15. package/dist/core/goals/index.d.ts +8 -0
  16. package/dist/core/goals/index.d.ts.map +1 -0
  17. package/dist/core/goals/index.js +7 -0
  18. package/dist/core/goals/runtime.d.ts +112 -0
  19. package/dist/core/goals/runtime.d.ts.map +1 -0
  20. package/dist/core/goals/runtime.js +241 -0
  21. package/dist/core/goals/state.d.ts +82 -0
  22. package/dist/core/goals/state.d.ts.map +1 -0
  23. package/dist/core/goals/state.js +225 -0
  24. package/dist/core/goals/tool.d.ts +10 -0
  25. package/dist/core/goals/tool.d.ts.map +1 -0
  26. package/dist/core/goals/tool.js +87 -0
  27. package/dist/core/index.d.ts.map +1 -1
  28. package/dist/core/index.js +4 -3
  29. package/dist/core/loops/condition.d.ts +57 -0
  30. package/dist/core/loops/condition.d.ts.map +1 -0
  31. package/dist/core/loops/condition.js +153 -0
  32. package/dist/core/loops/index.d.ts +9 -0
  33. package/dist/core/loops/index.d.ts.map +1 -0
  34. package/dist/core/loops/index.js +8 -0
  35. package/dist/core/loops/limits.d.ts +43 -0
  36. package/dist/core/loops/limits.d.ts.map +1 -0
  37. package/dist/core/loops/limits.js +219 -0
  38. package/dist/core/loops/ralph.d.ts +37 -0
  39. package/dist/core/loops/ralph.d.ts.map +1 -0
  40. package/dist/core/loops/ralph.js +72 -0
  41. package/dist/core/loops/runtime.d.ts +95 -0
  42. package/dist/core/loops/runtime.d.ts.map +1 -0
  43. package/dist/core/loops/runtime.js +222 -0
  44. package/dist/core/memory/managed-skills.d.ts +0 -11
  45. package/dist/core/memory/managed-skills.d.ts.map +1 -1
  46. package/dist/core/memory/managed-skills.js +2 -2
  47. package/dist/core/memory/vector-store.d.ts +0 -1
  48. package/dist/core/memory/vector-store.d.ts.map +1 -1
  49. package/dist/core/memory/vector-store.js +0 -3
  50. package/dist/core/plans/index.d.ts +7 -0
  51. package/dist/core/plans/index.d.ts.map +1 -0
  52. package/dist/core/plans/index.js +6 -0
  53. package/dist/core/plans/prompts.d.ts +20 -0
  54. package/dist/core/plans/prompts.d.ts.map +1 -0
  55. package/dist/core/plans/prompts.js +72 -0
  56. package/dist/core/plans/runtime.d.ts +89 -0
  57. package/dist/core/plans/runtime.d.ts.map +1 -0
  58. package/dist/core/plans/runtime.js +170 -0
  59. package/dist/core/plans/state.d.ts +55 -0
  60. package/dist/core/plans/state.d.ts.map +1 -0
  61. package/dist/core/plans/state.js +88 -0
  62. package/dist/core/runtime/agent-loop-messages.d.ts +6 -0
  63. package/dist/core/runtime/agent-loop-messages.d.ts.map +1 -1
  64. package/dist/core/runtime/agent-loop-messages.js +40 -15
  65. package/dist/core/runtime/agent-loop-run.d.ts.map +1 -1
  66. package/dist/core/runtime/agent-loop-run.js +18 -27
  67. package/dist/core/runtime/agent-loop-runner.d.ts +1 -23
  68. package/dist/core/runtime/agent-loop-runner.d.ts.map +1 -1
  69. package/dist/core/runtime/agent-loop-runner.js +84 -30
  70. package/dist/core/runtime/agent-loop-types.d.ts +4 -0
  71. package/dist/core/runtime/agent-loop-types.d.ts.map +1 -1
  72. package/dist/core/runtime/compaction.d.ts +9 -0
  73. package/dist/core/runtime/compaction.d.ts.map +1 -1
  74. package/dist/core/runtime/compaction.js +28 -0
  75. package/dist/core/runtime/context-estimate.d.ts +37 -4
  76. package/dist/core/runtime/context-estimate.d.ts.map +1 -1
  77. package/dist/core/runtime/context-estimate.js +70 -0
  78. package/dist/core/runtime/create-runtime.d.ts +27 -5
  79. package/dist/core/runtime/create-runtime.d.ts.map +1 -1
  80. package/dist/core/runtime/create-runtime.js +22 -2
  81. package/dist/core/runtime/permission-classifier.d.ts.map +1 -1
  82. package/dist/core/runtime/permission-classifier.js +83 -12
  83. package/dist/core/runtime/plugin.d.ts +7 -0
  84. package/dist/core/runtime/plugin.d.ts.map +1 -1
  85. package/dist/core/runtime/plugin.js +13 -0
  86. package/dist/core/runtime/process-tree.d.ts +0 -9
  87. package/dist/core/runtime/process-tree.d.ts.map +1 -1
  88. package/dist/core/runtime/process-tree.js +1 -1
  89. package/dist/core/runtime/run-runtime.d.ts.map +1 -1
  90. package/dist/core/runtime/run-runtime.js +88 -49
  91. package/dist/core/runtime/runtime-catalog.d.ts +0 -4
  92. package/dist/core/runtime/runtime-catalog.d.ts.map +1 -1
  93. package/dist/core/runtime/runtime-catalog.js +1 -1
  94. package/dist/core/runtime/stream-rules.d.ts +9 -2
  95. package/dist/core/runtime/stream-rules.d.ts.map +1 -1
  96. package/dist/core/runtime/stream-rules.js +19 -5
  97. package/dist/core/runtime/title.d.ts +13 -1
  98. package/dist/core/runtime/title.d.ts.map +1 -1
  99. package/dist/core/runtime/title.js +51 -17
  100. package/dist/core/runtime/tool-pruning.d.ts.map +1 -1
  101. package/dist/core/runtime/tool-pruning.js +8 -16
  102. package/dist/core/session/session-file.d.ts +91 -5
  103. package/dist/core/session/session-file.d.ts.map +1 -1
  104. package/dist/core/session/session-file.js +206 -24
  105. package/dist/core/session/session-loop.d.ts +3 -1
  106. package/dist/core/session/session-loop.d.ts.map +1 -1
  107. package/dist/core/session/session-loop.js +8 -4
  108. package/dist/core/settings/project-settings.d.ts +63 -9
  109. package/dist/core/settings/project-settings.d.ts.map +1 -1
  110. package/dist/core/settings/project-settings.js +43 -2
  111. package/dist/core/settings/workspace-context.js +4 -4
  112. package/dist/core/store/in-memory-session-store.d.ts.map +1 -1
  113. package/dist/core/store/in-memory-session-store.js +3 -2
  114. package/dist/core/store/session-store.contract.test-helper.d.ts +7 -5
  115. package/dist/core/store/session-store.contract.test-helper.d.ts.map +1 -1
  116. package/dist/core/store/session-store.contract.test-helper.js +7 -5
  117. package/dist/core/store/session-store.d.ts +4 -2
  118. package/dist/core/store/session-store.d.ts.map +1 -1
  119. package/dist/core/store/session-store.js +4 -2
  120. package/dist/core/tools/ask-question.d.ts.map +1 -1
  121. package/dist/core/tools/ask-question.js +51 -5
  122. package/dist/core/tools/bash-pty.d.ts +13 -4
  123. package/dist/core/tools/bash-pty.d.ts.map +1 -1
  124. package/dist/core/tools/bash-pty.js +15 -8
  125. package/dist/core/tools/bash-sandbox.d.ts +1 -1
  126. package/dist/core/tools/bash.d.ts +1 -1
  127. package/dist/core/tools/bash.d.ts.map +1 -1
  128. package/dist/core/tools/bash.js +18 -8
  129. package/dist/core/tools/browser.d.ts +54 -0
  130. package/dist/core/tools/browser.d.ts.map +1 -1
  131. package/dist/core/tools/browser.js +138 -1
  132. package/dist/core/tools/file-fingerprint.d.ts +24 -0
  133. package/dist/core/tools/file-fingerprint.d.ts.map +1 -0
  134. package/dist/core/tools/file-fingerprint.js +61 -0
  135. package/dist/core/tools/file-tools.d.ts +26 -0
  136. package/dist/core/tools/file-tools.d.ts.map +1 -1
  137. package/dist/core/tools/file-tools.js +113 -17
  138. package/dist/core/tools/index.d.ts +2 -0
  139. package/dist/core/tools/index.d.ts.map +1 -1
  140. package/dist/core/tools/index.js +1 -0
  141. package/dist/core/tools/mcp-mount.d.ts +1 -1
  142. package/dist/core/tools/mcp-mount.d.ts.map +1 -1
  143. package/dist/core/tools/mcp-mount.js +72 -26
  144. package/dist/core/tools/url-guard.d.ts +15 -0
  145. package/dist/core/tools/url-guard.d.ts.map +1 -1
  146. package/dist/core/tools/url-guard.js +54 -14
  147. package/dist/core/tools/web-ports-std.d.ts.map +1 -1
  148. package/dist/core/tools/web-ports-std.js +5 -2
  149. package/dist/modes/print-mode.d.ts +3 -2
  150. package/dist/modes/print-mode.d.ts.map +1 -1
  151. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  152. package/dist/modes/rpc/rpc-mode.js +2 -2
  153. package/dist/modes/shared.d.ts +12 -0
  154. package/dist/modes/shared.d.ts.map +1 -1
  155. package/dist/modes/shared.js +23 -6
  156. package/dist/modes/tui/agent-status.d.ts +20 -0
  157. package/dist/modes/tui/agent-status.d.ts.map +1 -0
  158. package/dist/modes/tui/agent-status.js +35 -0
  159. package/dist/modes/tui/ask-panel.d.ts +166 -0
  160. package/dist/modes/tui/ask-panel.d.ts.map +1 -0
  161. package/dist/modes/tui/ask-panel.js +888 -0
  162. package/dist/modes/tui/pty-console.d.ts +3 -0
  163. package/dist/modes/tui/pty-console.d.ts.map +1 -1
  164. package/dist/modes/tui/pty-console.js +19 -0
  165. package/dist/modes/tui/settings-overlay.d.ts +19 -0
  166. package/dist/modes/tui/settings-overlay.d.ts.map +1 -1
  167. package/dist/modes/tui/settings-overlay.js +46 -10
  168. package/dist/modes/tui/tui-commands-session.d.ts.map +1 -1
  169. package/dist/modes/tui/tui-commands-session.js +20 -6
  170. package/dist/modes/tui/tui-commands.d.ts +44 -3
  171. package/dist/modes/tui/tui-commands.d.ts.map +1 -1
  172. package/dist/modes/tui/tui-commands.js +380 -6
  173. package/dist/modes/tui/tui-components.d.ts +10 -0
  174. package/dist/modes/tui/tui-components.d.ts.map +1 -1
  175. package/dist/modes/tui/tui-components.js +26 -0
  176. package/dist/modes/tui/tui-format.d.ts +53 -4
  177. package/dist/modes/tui/tui-format.d.ts.map +1 -1
  178. package/dist/modes/tui/tui-format.js +137 -42
  179. package/dist/modes/tui/tui-frame-provider.d.ts +11 -3
  180. package/dist/modes/tui/tui-frame-provider.d.ts.map +1 -1
  181. package/dist/modes/tui/tui-frame-provider.js +14 -5
  182. package/dist/modes/tui/tui-interactive.d.ts.map +1 -1
  183. package/dist/modes/tui/tui-interactive.js +14 -0
  184. package/dist/modes/tui/tui-io.d.ts.map +1 -1
  185. package/dist/modes/tui/tui-io.js +150 -55
  186. package/dist/modes/tui/tui-mode.d.ts.map +1 -1
  187. package/dist/modes/tui/tui-mode.js +428 -23
  188. package/dist/modes/tui/tui-overlays.d.ts +6 -10
  189. package/dist/modes/tui/tui-overlays.d.ts.map +1 -1
  190. package/dist/modes/tui/tui-overlays.js +0 -43
  191. package/dist/modes/tui/tui-render.d.ts +10 -0
  192. package/dist/modes/tui/tui-render.d.ts.map +1 -1
  193. package/dist/modes/tui/tui-render.js +73 -38
  194. package/dist/modes/tui/tui-seam.d.ts +28 -6
  195. package/dist/modes/tui/tui-seam.d.ts.map +1 -1
  196. package/dist/modes/tui/tui-slash.d.ts.map +1 -1
  197. package/dist/modes/tui/tui-slash.js +3 -1
  198. package/dist/modes/tui/tui-tool-render.d.ts +14 -1
  199. package/dist/modes/tui/tui-tool-render.d.ts.map +1 -1
  200. package/dist/modes/tui/tui-tool-render.js +158 -50
  201. package/dist/modes/tui/tui-transcript-reconciler.d.ts +4 -0
  202. package/dist/modes/tui/tui-transcript-reconciler.d.ts.map +1 -1
  203. package/dist/modes/tui/tui-transcript-reconciler.js +12 -2
  204. package/dist/modes/tui/view-state.d.ts.map +1 -1
  205. package/dist/modes/tui/view-state.js +4 -1
  206. package/dist/protocol/transport.d.ts +10 -0
  207. package/dist/protocol/transport.d.ts.map +1 -1
  208. package/dist/protocol/transport.js +1 -0
  209. package/package.json +11 -11
@@ -31,22 +31,63 @@ export function sessionDirFor(workspaceRoot) {
31
31
  export function sessionDir() {
32
32
  return process.env.OMA_SESSION_DIR ?? sessionDirFor(process.cwd());
33
33
  }
34
+ /** Session ids reach the FILESYSTEM, and callers supply them (RPC resume,
35
+ * TUI rename/delete): validate at this boundary. A path-shaped id
36
+ * ("../x", "/abs", "a\\b", "..", trailing dots) would otherwise escape the
37
+ * session directory and read/append/rename/delete outside it. Legit ids
38
+ * are newSessionId() UUIDs (hex + dashes). */
39
+ export function assertSafeSessionId(id) {
40
+ if (!/^[A-Za-z0-9_-]+$/.test(id)) {
41
+ throw new Error(`invalid session id: ${JSON.stringify(id.slice(0, 40))}`);
42
+ }
43
+ return id;
44
+ }
45
+ /** The single place a session id becomes a path. */
46
+ function sessionFilePath(id, dir) {
47
+ return join(dir, `${assertSafeSessionId(id)}.jsonl`);
48
+ }
34
49
  export function sessionPath(id) {
35
- return join(sessionDir(), `${id}.jsonl`);
50
+ return sessionFilePath(id, sessionDir());
36
51
  }
37
52
  export function newSessionId() {
38
53
  return crypto.randomUUID();
39
54
  }
40
55
  /** Load the transcript messages of a session file (wire-loose shape).
41
- * Missing/corrupt files yield [] (the caller falls back to a fresh
42
- * session). A compaction event REPLACES everything recorded before it:
43
- * the summary becomes one context message, later turns stay live — so a
44
- * resumed session does not re-inflate the context it already compacted. */
56
+ * Missing/corrupt files yield [] (the caller falls back to a fresh session).
57
+ *
58
+ * A compaction event carries `replacesEarlierMessages`: whether its summary
59
+ * describes everything written before it. Only then is folding lossless —
60
+ * the summary becomes one context message and later turns stay live.
61
+ *
62
+ * Why a flag and not a count: the count would have to be expressed in THIS
63
+ * file's message-event space, but the run that produces the summary counts
64
+ * coverage over its own session branch, and the two spaces do not align —
65
+ * the branch also holds entries that never reach the file (re-seeded
66
+ * `product_history`, the `meta` entry) and folds a prior summary into a
67
+ * single entry. A count in the wrong space lands past the real boundary and
68
+ * DELETES messages the summary never described. The flag cannot be
69
+ * mis-indexed: when the summary does not describe the whole file, the
70
+ * transcript is simply left intact (a resume may re-summarize, never lose).
71
+ *
72
+ * Events without the field (files written before it existed) keep the
73
+ * historical behavior: the summary replaces everything before it.
74
+ *
75
+ * The LAST event that says "replaces" decides the fold, and a later event
76
+ * saying otherwise does NOT cancel it. That distinction is what makes manual
77
+ * /compact stick: /compact folds the transcript to one summary, a later
78
+ * automatic compaction covers only part of what follows, and "latest event
79
+ * wins" would resurrect every message the user deliberately compacted away.
80
+ * Ignoring a partial summary is always safe — its content is either already
81
+ * summarized or still present as messages. */
45
82
  export function loadSessionMessages(id, dir = sessionDir()) {
46
- const path = join(dir, `${id}.jsonl`);
83
+ const path = sessionFilePath(id, dir);
47
84
  if (!existsSync(path))
48
85
  return [];
49
86
  const messages = [];
87
+ /** The latest summary that MAY fold, with the index it folds from. A later
88
+ * non-folding summary must not overwrite these. */
89
+ let foldSummary;
90
+ let foldFrom;
50
91
  for (const line of readFileSync(path, "utf8").split("\n")) {
51
92
  if (!line.trim())
52
93
  continue;
@@ -57,18 +98,28 @@ export function loadSessionMessages(id, dir = sessionDir()) {
57
98
  if (evt.type === "message" && evt.message?.role)
58
99
  messages.push(evt.message);
59
100
  else if (evt.type === "compaction" && typeof evt.summary === "string") {
60
- messages.length = 0;
61
- messages.push({
62
- role: "user",
63
- text: `<previous_session_summary>\n${evt.summary}\n</previous_session_summary>`,
64
- });
101
+ // Evaluated in this file's own index space: everything parsed before
102
+ // it. Only a replacing event re-points the fold; a partial summary
103
+ // leaves an earlier fold in place (see the header note on /compact).
104
+ if (evt.replacesEarlierMessages !== false) {
105
+ foldSummary = evt.summary;
106
+ foldFrom = messages.length;
107
+ }
65
108
  }
66
109
  }
67
110
  catch {
68
111
  /* skip malformed line */
69
112
  }
70
113
  }
71
- return messages;
114
+ if (foldSummary === undefined || foldFrom === undefined)
115
+ return messages;
116
+ return [
117
+ {
118
+ role: "user",
119
+ text: `<previous_session_summary>\n${foldSummary}\n</previous_session_summary>`,
120
+ },
121
+ ...messages.slice(foldFrom),
122
+ ];
72
123
  }
73
124
  /** Scan one session directory (all *.jsonl) into summaries. */
74
125
  function scanSessionDir(dir, workspace) {
@@ -82,6 +133,7 @@ function scanSessionDir(dir, workspace) {
82
133
  const path = join(dir, entry);
83
134
  let preview = "";
84
135
  let title;
136
+ let summaryText;
85
137
  let headerCwd;
86
138
  let forkOf;
87
139
  try {
@@ -98,6 +150,10 @@ function scanSessionDir(dir, workspace) {
98
150
  title = evt.title;
99
151
  continue;
100
152
  }
153
+ if (evt.type === "summary" && typeof evt.summary === "string") {
154
+ summaryText = evt.summary;
155
+ continue;
156
+ }
101
157
  if (evt.type === "fork_of" && typeof evt.parentId === "string") {
102
158
  forkOf = evt.parentId;
103
159
  continue;
@@ -115,6 +171,8 @@ function scanSessionDir(dir, workspace) {
115
171
  const summary = { id, modifiedAt: statSync(path).mtimeMs, preview };
116
172
  if (title !== undefined)
117
173
  summary.title = title;
174
+ if (summaryText !== undefined)
175
+ summary.summary = summaryText;
118
176
  if (workspace !== undefined)
119
177
  summary.workspace = workspace;
120
178
  else if (headerCwd !== undefined)
@@ -155,7 +213,7 @@ export function listAllSessions() {
155
213
  * the TUI to mark a session titled so the loop stops re-generating one on
156
214
  * every completed turn. */
157
215
  export function readSessionTitle(id, dir = sessionDir()) {
158
- const path = join(dir, `${id}.jsonl`);
216
+ const path = sessionFilePath(id, dir);
159
217
  let raw;
160
218
  try {
161
219
  raw = readFileSync(path, "utf-8");
@@ -183,7 +241,7 @@ export function readSessionTitle(id, dir = sessionDir()) {
183
241
  * loop after each completed run. Last one wins in listings. Also the
184
242
  * primitive for /rename: appending a new title overrides the display. */
185
243
  export function appendSessionTitle(id, title, dir = sessionDir()) {
186
- const path = join(dir, `${id}.jsonl`);
244
+ const path = sessionFilePath(id, dir);
187
245
  if (!existsSync(path))
188
246
  return;
189
247
  appendFileSync(path, `${JSON.stringify({
@@ -192,9 +250,22 @@ export function appendSessionTitle(id, title, dir = sessionDir()) {
192
250
  title,
193
251
  })}\n`);
194
252
  }
253
+ /** Append a summary event: a one-sentence description of what the session is
254
+ * about, produced by the same ephemeral call as the title. Last one wins in
255
+ * listings (the resume list shows it next to the title). */
256
+ export function appendSessionSummary(id, summary, dir = sessionDir()) {
257
+ const path = sessionFilePath(id, dir);
258
+ if (!existsSync(path))
259
+ return;
260
+ appendFileSync(path, `${JSON.stringify({
261
+ type: "summary",
262
+ timestamp: new Date().toISOString(),
263
+ summary,
264
+ })}\n`);
265
+ }
195
266
  /** Delete a session file. Returns false when it did not exist. */
196
267
  export function deleteSession(id, dir = sessionDir()) {
197
- const path = join(dir, `${id}.jsonl`);
268
+ const path = sessionFilePath(id, dir);
198
269
  if (!existsSync(path))
199
270
  return false;
200
271
  rmSync(path, { force: true });
@@ -203,30 +274,141 @@ export function deleteSession(id, dir = sessionDir()) {
203
274
  /** Rename a session's display title (last-wins title event). Returns false
204
275
  * when the session file does not exist. */
205
276
  export function renameSession(id, title, dir = sessionDir()) {
206
- const path = join(dir, `${id}.jsonl`);
277
+ const path = sessionFilePath(id, dir);
207
278
  if (!existsSync(path))
208
279
  return false;
209
280
  appendSessionTitle(id, title, dir);
210
281
  return true;
211
282
  }
283
+ /** Append a goal-mode event (omp mode-change analogue): the latest goal
284
+ * state transition for the session. Replay reconstructs the current goal
285
+ * (active/paused/complete/dropped); a goal:null event means dropped. */
286
+ export function appendSessionGoalEvent(id, state, dir = sessionDir()) {
287
+ const path = sessionFilePath(id, dir);
288
+ if (!existsSync(path))
289
+ return;
290
+ appendFileSync(path, `${JSON.stringify({
291
+ type: "goal",
292
+ timestamp: new Date().toISOString(),
293
+ goalMode: state,
294
+ })}\n`);
295
+ }
296
+ /** Append a goal COMPLETION record: the objective and the tracked usage at the
297
+ * moment the model declared the goal done. Separate from the mode-change
298
+ * events so a completed goal reads as a durable achievement rather than a
299
+ * status flip (and so resumed sessions can report what was finished). */
300
+ export function appendSessionGoalCompletion(id, record, dir = sessionDir()) {
301
+ const path = sessionFilePath(id, dir);
302
+ if (!existsSync(path))
303
+ return;
304
+ appendFileSync(path, `${JSON.stringify({
305
+ type: "goal_completed",
306
+ timestamp: new Date().toISOString(),
307
+ ...record,
308
+ })}
309
+ `);
310
+ }
311
+ /** Append a plan-mode event (same mode-change shape as the goal one): the
312
+ * mode plus whether it is paused. The draft itself is a FILE, so the journal
313
+ * only needs the mode; resume restores "planning, paused" and never
314
+ * re-enters a read-only planning turn on its own. */
315
+ export function appendSessionPlanEvent(id, state, paused, dir = sessionDir()) {
316
+ const path = sessionFilePath(id, dir);
317
+ if (!existsSync(path))
318
+ return;
319
+ appendFileSync(path, `${JSON.stringify({
320
+ type: "plan",
321
+ timestamp: new Date().toISOString(),
322
+ planMode: state ? { planPath: state.planPath, paused } : null,
323
+ })}\n`);
324
+ }
325
+ /** Replay plan-mode events: the latest one wins (null = plan mode off). */
326
+ export function loadSessionPlanState(id, dir = sessionDir()) {
327
+ const path = sessionFilePath(id, dir);
328
+ if (!existsSync(path))
329
+ return null;
330
+ let latest = null;
331
+ for (const line of readFileSync(path, "utf8").split("\n")) {
332
+ if (!line.trim() || !line.includes('"plan"'))
333
+ continue;
334
+ try {
335
+ const evt = JSON.parse(line);
336
+ if (evt.type !== "plan")
337
+ continue;
338
+ const mode = evt.planMode;
339
+ if (mode === null || mode === undefined || typeof mode.planPath !== "string") {
340
+ latest = null;
341
+ continue;
342
+ }
343
+ latest = { planPath: mode.planPath, paused: mode.paused === true };
344
+ }
345
+ catch {
346
+ /* skip malformed line */
347
+ }
348
+ }
349
+ return latest;
350
+ }
351
+ /** Reconstruct the current goal-mode state by replaying goal events (last
352
+ * one wins; malformed entries are skipped like every other scan here). */
353
+ export function loadSessionGoalState(id, dir = sessionDir()) {
354
+ const path = sessionFilePath(id, dir);
355
+ if (!existsSync(path))
356
+ return null;
357
+ let latest = null;
358
+ for (const line of readFileSync(path, "utf8").split("\n")) {
359
+ if (!line.trim() || !line.includes('"goal"'))
360
+ continue;
361
+ try {
362
+ const evt = JSON.parse(line);
363
+ if (evt.type !== "goal")
364
+ continue;
365
+ const mode = evt.goalMode;
366
+ if (mode === null || mode === undefined || mode.goal == null) {
367
+ latest = null; // explicit drop/clear wins from here on
368
+ continue;
369
+ }
370
+ const g = mode.goal;
371
+ if (typeof g.objective !== "string" ||
372
+ typeof g.status !== "string" ||
373
+ typeof g.id !== "string") {
374
+ continue;
375
+ }
376
+ latest = {
377
+ enabled: mode.enabled === true,
378
+ mode: mode.mode === "exiting" ? "exiting" : "active",
379
+ ...(mode.reason === "completed" ? { reason: "completed" } : {}),
380
+ goal: g,
381
+ };
382
+ }
383
+ catch {
384
+ /* skip malformed line */
385
+ }
386
+ }
387
+ return latest;
388
+ }
212
389
  /** Append a compaction event: records that the run compacted everything
213
390
  * recorded so far in this file into `summary`. Must be called AFTER the
214
391
  * turn's messages are appended. */
215
- export function appendSessionCompaction(id, summary, dir = sessionDir()) {
216
- const path = join(dir, `${id}.jsonl`);
392
+ export function appendSessionCompaction(id, summary, dir = sessionDir(),
393
+ /** Whether this summary describes EVERYTHING already in the file, which is
394
+ * what makes folding on load lossless. Omit when unknown: the load path
395
+ * then keeps the transcript intact instead of folding it. */
396
+ replacesEarlierMessages) {
397
+ const path = sessionFilePath(id, dir);
217
398
  if (!existsSync(path))
218
399
  return;
219
400
  appendFileSync(path, `${JSON.stringify({
220
401
  type: "compaction",
221
402
  timestamp: new Date().toISOString(),
222
403
  summary,
404
+ ...(replacesEarlierMessages !== undefined ? { replacesEarlierMessages } : {}),
223
405
  })}\n`);
224
406
  }
225
407
  /** Read the durable event tree of one session as parentId-chained message
226
408
  * nodes (used by the TUI branch-tree fork overlay). Only message events
227
409
  * carry an id; corrupt lines are skipped. */
228
410
  export function loadSessionBranchNodes(id, dir = sessionDir()) {
229
- const path = join(dir, `${id}.jsonl`);
411
+ const path = sessionFilePath(id, dir);
230
412
  if (!existsSync(path))
231
413
  return [];
232
414
  const lines = readFileSync(path, "utf8")
@@ -281,7 +463,7 @@ export function loadSessionBranchNodes(id, dir = sessionDir()) {
281
463
  /** Copy a session file through the line at `cutIndex` into a NEW file with a
282
464
  * fork_of marker. Shared by the ordinal and event-id fork paths. */
283
465
  function copySessionThrough(id, cutIndex, dir, markerFields) {
284
- const path = join(dir, `${id}.jsonl`);
466
+ const path = sessionFilePath(id, dir);
285
467
  if (!existsSync(path))
286
468
  return null;
287
469
  const lines = readFileSync(path, "utf8")
@@ -303,13 +485,13 @@ function copySessionThrough(id, cutIndex, dir, markerFields) {
303
485
  /* keep the original header line */
304
486
  }
305
487
  const marker = JSON.stringify({ type: "fork_of", parentId: id, ...markerFields });
306
- writeFileSync(join(dir, `${newId}.jsonl`), `${[...kept, marker].join("\n")}\n`);
488
+ writeFileSync(sessionFilePath(newId, dir), `${[...kept, marker].join("\n")}\n`);
307
489
  return newId;
308
490
  }
309
491
  /** Fork a session at the Nth user message (1-based) — the existing /fork
310
492
  * semantics. */
311
493
  export function forkSession(id, ordinal, dir = sessionDir()) {
312
- const path = join(dir, `${id}.jsonl`);
494
+ const path = sessionFilePath(id, dir);
313
495
  if (!existsSync(path))
314
496
  return null;
315
497
  const lines = readFileSync(path, "utf8")
@@ -340,7 +522,7 @@ export function forkSession(id, ordinal, dir = sessionDir()) {
340
522
  /** Fork a session through a specific branch node (message event id). The new
341
523
  * session contains every event up to and INCLUDING that node. */
342
524
  export function forkSessionAtEvent(id, eventId, dir = sessionDir()) {
343
- const path = join(dir, `${id}.jsonl`);
525
+ const path = sessionFilePath(id, dir);
344
526
  if (!existsSync(path))
345
527
  return null;
346
528
  const lines = readFileSync(path, "utf8")
@@ -389,7 +571,7 @@ function readLastMessageId(path) {
389
571
  }
390
572
  export function appendSessionMessages(id, cwd, messages, dir = sessionDir()) {
391
573
  mkdirSync(dir, { recursive: true });
392
- const path = join(dir, `${id}.jsonl`);
574
+ const path = sessionFilePath(id, dir);
393
575
  const cacheKey = `${dir}:${id}`;
394
576
  if (!existsSync(path)) {
395
577
  writeFileSync(path, `${JSON.stringify({
@@ -5,7 +5,7 @@ import type { OmaRuntime } from "../runtime/create-runtime.js";
5
5
  * stores only the session id. A completed turn (input + assistant/tool
6
6
  * messages + compaction summaries) is appended after the run. */
7
7
  /** Finalize a turn in the session file: writes compaction summaries and the
8
- * auto title. Conversational messages are written in REAL TIME via
8
+ * auto title/summary. Conversational messages are written in REAL TIME via
9
9
  * createOmaRuntime's onPersistMessages (pi appendMessage) when the caller
10
10
  * wires it; `messages` is the fallback batch for one-shot modes (print)
11
11
  * that do not use real-time persistence. */
@@ -17,6 +17,8 @@ export declare function persistSessionTurn(opts: {
17
17
  messages?: readonly unknown[];
18
18
  /** The run's auto-generated title (outcome.title), when present. */
19
19
  title?: string;
20
+ /** The run's one-sentence session summary (outcome.summary), when present. */
21
+ summary?: string;
20
22
  /** Session directory; defaults to the current workspace. Cross-workspace
21
23
  * resumes pass the source workspace's dir so new turns append there. */
22
24
  dir?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"session-loop.d.ts","sourceRoot":"","sources":["../../../src/core/session/session-loop.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAU/D;;;;kEAIkE;AAElE;;;;6CAI6C;AAC7C,wBAAsB,kBAAkB,CAAC,IAAI,EAAE;IAC7C,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,UAAU,CAAC;IACpB,uEAAuE;IACvE,QAAQ,CAAC,EAAE,SAAS,OAAO,EAAE,CAAC;IAC9B,oEAAoE;IACpE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;6EACyE;IACzE,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,GAAG,OAAO,CAAC,IAAI,CAAC,CAQhB;AAED;;;sEAGsE;AACtE,wBAAgB,cAAc,CAC5B,SAAS,CAAC,EAAE,MAAM,EAClB,GAAG,CAAC,EAAE,MAAM,GACX;IACD,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IACpC,GAAG,EAAE,MAAM,CAAC;CACb,CAIA"}
1
+ {"version":3,"file":"session-loop.d.ts","sourceRoot":"","sources":["../../../src/core/session/session-loop.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAW/D;;;;kEAIkE;AAElE;;;;6CAI6C;AAC7C,wBAAsB,kBAAkB,CAAC,IAAI,EAAE;IAC7C,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,UAAU,CAAC;IACpB,uEAAuE;IACvE,QAAQ,CAAC,EAAE,SAAS,OAAO,EAAE,CAAC;IAC9B,oEAAoE;IACpE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8EAA8E;IAC9E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;6EACyE;IACzE,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,GAAG,OAAO,CAAC,IAAI,CAAC,CAgBhB;AAED;;;sEAGsE;AACtE,wBAAgB,cAAc,CAC5B,SAAS,CAAC,EAAE,MAAM,EAClB,GAAG,CAAC,EAAE,MAAM,GACX;IACD,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IACpC,GAAG,EAAE,MAAM,CAAC;CACb,CAIA"}
@@ -1,11 +1,11 @@
1
- import { appendSessionCompaction, appendSessionMessages, appendSessionTitle, loadSessionMessages, newSessionId, sessionDir, } from "./session-file.js";
1
+ import { appendSessionCompaction, appendSessionMessages, appendSessionSummary, appendSessionTitle, loadSessionMessages, newSessionId, sessionDir, } from "./session-file.js";
2
2
  /** Shared session persistence for the interactive CLI modes (TUI) and the
3
3
  * one-shot modes (print). Mirrors the RPC mode's session semantics
4
4
  * (ADR 0003 decision 6): the CLI owns its session file; the product
5
5
  * stores only the session id. A completed turn (input + assistant/tool
6
6
  * messages + compaction summaries) is appended after the run. */
7
7
  /** Finalize a turn in the session file: writes compaction summaries and the
8
- * auto title. Conversational messages are written in REAL TIME via
8
+ * auto title/summary. Conversational messages are written in REAL TIME via
9
9
  * createOmaRuntime's onPersistMessages (pi appendMessage) when the caller
10
10
  * wires it; `messages` is the fallback batch for one-shot modes (print)
11
11
  * that do not use real-time persistence. */
@@ -13,11 +13,15 @@ export async function persistSessionTurn(opts) {
13
13
  if (opts.messages && opts.messages.length > 0) {
14
14
  appendSessionMessages(opts.sessionId, opts.cwd, opts.messages, opts.dir);
15
15
  }
16
- for (const summary of await opts.runtime.compactions()) {
17
- appendSessionCompaction(opts.sessionId, summary, opts.dir);
16
+ for (const compaction of await opts.runtime.compactions()) {
17
+ // The flag rides the event: a summary that does NOT describe the whole
18
+ // file must not fold it, or a resume drops the messages it never saw.
19
+ appendSessionCompaction(opts.sessionId, compaction.summary, opts.dir, compaction.replacesEarlierMessages);
18
20
  }
19
21
  if (opts.title)
20
22
  appendSessionTitle(opts.sessionId, opts.title, opts.dir);
23
+ if (opts.summary)
24
+ appendSessionSummary(opts.sessionId, opts.summary, opts.dir);
21
25
  }
22
26
  /** Resolve the session for a new TUI turn: a --session id resumes that
23
27
  * file (missing/corrupt file degrades to fresh); otherwise a new id.
@@ -1,7 +1,12 @@
1
1
  /** Project-level oma settings (`.oma/settings.json` in the workspace root).
2
- * Standalone TUI-only. The product backend keeps agent.yml as its model
3
- * truth and can be overridden per-run by the run parameter, so this file
4
- * never conflicts with agent.yml in the backend->oma chain. */
2
+ *
3
+ * Every mode reads this file, but what a mode HONORS differs on purpose: a
4
+ * standalone run (TUI / print / json) takes it whole, while a product run
5
+ * (rpc) takes only `bashSandbox` — a workspace file must never steer the
6
+ * product's permission classifier, web access, step budget or timeouts. The
7
+ * product backend keeps agent.yml as its model truth and passes per-run
8
+ * values in the run snapshot, so this file cannot conflict with agent.yml in
9
+ * the backend -> oma chain. */
5
10
  export interface ProjectSettings {
6
11
  /** Canonical `<provider>/<model>` id chosen in the TUI. */
7
12
  model?: string;
@@ -21,6 +26,11 @@ export interface ProjectSettings {
21
26
  modelTimeoutMs?: number;
22
27
  /** MCP call timeout ms (env OMA_MCP_TIMEOUT_MS). */
23
28
  mcpTimeoutMs?: number;
29
+ /** HITL ask inactivity timeout ms (env OMA_ASK_TIMEOUT_MS, omp ask.timeout
30
+ * which is in SECONDS): when a question sits untouched this long, the
31
+ * unanswered ones are auto-answered with their recommended option and the
32
+ * answer is marked timedOut. 0 / absent = disabled (omp's default). */
33
+ askTimeoutMs?: number;
24
34
  /** Disable web tools (env OMA_DISABLE_WEB=1). */
25
35
  disableWeb?: boolean;
26
36
  /** Generate auto titles (env OMA_TITLE_ENABLED=0 disables). */
@@ -52,15 +62,50 @@ export interface ProjectSettings {
52
62
  model?: string;
53
63
  };
54
64
  /** Standalone permission gate (ADR 0020): "ask" cards every high-risk
55
- * tool, "auto" runs the classifier, "deny" blocks them. Absent = ungated
56
- * (legacy default). The product RPC path never reads this — its
57
- * permissionMode arrives frozen in the run snapshot. */
58
- permissionMode?: "ask" | "auto" | "deny";
65
+ * tool, "auto" runs the classifier, "deny" blocks them, "yolo" runs
66
+ * ungated (the industry's skip-permissions mode — judgment gates off,
67
+ * STATIC boundaries stay: workspace containment, protected files,
68
+ * secret stripping, egress rules, write freshness; the OS bash sandbox
69
+ * is force-enabled when the platform has one). Absent = ungated (legacy
70
+ * default). The product RPC path never reads this — its permissionMode
71
+ * arrives frozen in the run snapshot. */
72
+ permissionMode?: "ask" | "auto" | "deny" | "yolo";
73
+ /** Plan mode master switch (default ON). Turn it off in a workspace that
74
+ * should never enter the read-only planning loop. */
75
+ planEnabled?: boolean;
76
+ /** Model used while planning ("provider/model"); absent = the session's
77
+ * model. Planning benefits from a stronger reader than the implementer,
78
+ * and the modes are separate so the two can differ. */
79
+ planModel?: string;
80
+ /** Goal mode master switch (default ON). A workspace that does not want an
81
+ * autonomous multi-turn objective driver can turn it off; /goal and
82
+ * /guided-goal then refuse with a hint. */
83
+ goalEnabled?: boolean;
84
+ /** Loop mode between-iteration action (`/loop`): "prompt" re-submits the
85
+ * prompt, "compact" summarizes the context first, "reset" starts a fresh
86
+ * session first, "ralph" runs the build loop (one work-queue item per fresh
87
+ * session). Absent = "prompt". */
88
+ loopAction?: "prompt" | "compact" | "reset" | "ralph";
59
89
  /** Read-side tool-result pruning: old tool output outside the protect
60
90
  * window is replaced by a short summary before each model call (a lighter
61
91
  * touch than compaction). Absent = pruning OFF — the loop only prunes when
62
92
  * a run was explicitly configured for it. */
63
93
  prune?: PruneKnobs;
94
+ /** Let the browser tool open loopback/RFC1918 targets, so a local dev server
95
+ * can be inspected (env OMA_BROWSER_LOCAL_NETWORK=1). Cloud metadata
96
+ * endpoints stay refused either way. Opt-IN because it loosens a boundary;
97
+ * like permissionMode it is standalone-only — a backend RPC run ignores the
98
+ * workspace file and takes the value from its frozen run snapshot. */
99
+ browserLocalNetwork?: boolean;
100
+ /** Write freshness policy for the file tools (env OMA_EDIT_FRESHNESS).
101
+ * "require" = edit/write must echo the [fingerprint ...] a read reported, so
102
+ * a write can never land on content the caller has not seen; "off" restores
103
+ * the pre-fingerprint behaviour. ABSENT MEANS "require": the gate is on
104
+ * unless a workspace deliberately downgrades it, and a workspace can only do
105
+ * that for itself (`read_only` file tools cannot edit `.oma/settings.json` —
106
+ * it is in PROTECTED_FILES). A backend RPC run ignores the workspace value
107
+ * entirely and keeps the default. */
108
+ editFreshness?: "off" | "require";
64
109
  }
65
110
  /** Tool-output pruning knobs (see tool-pruning.ts for the mechanics). */
66
111
  export interface PruneKnobs {
@@ -86,6 +131,7 @@ export interface RuntimeKnobs {
86
131
  maxSteps?: number;
87
132
  modelTimeoutMs?: number;
88
133
  mcpTimeoutMs?: number;
134
+ askTimeoutMs?: number;
89
135
  maxToolTimeoutMs?: number;
90
136
  bashTimeoutMs?: number;
91
137
  evalTimeoutMs?: number;
@@ -93,6 +139,11 @@ export interface RuntimeKnobs {
93
139
  permissionClassifierModel?: string;
94
140
  permissionClassifierTimeoutMs?: number;
95
141
  disableWeb?: boolean;
142
+ /** Browser tool: allow loopback/RFC1918 targets (metadata stays refused). */
143
+ browserLocalNetwork?: boolean;
144
+ /** File-tool write freshness: "require" (fingerprint echo) or "off".
145
+ * The assembly treats absence as "require". */
146
+ editFreshness?: "off" | "require";
96
147
  titleEnabled?: boolean;
97
148
  conversationTitled?: boolean;
98
149
  memoryExtract?: boolean;
@@ -107,6 +158,9 @@ export interface RuntimeKnobs {
107
158
  export declare function resolveRuntimeKnobs(settings: ProjectSettings | undefined, env?: Readonly<Record<string, string | undefined>>): RuntimeKnobs;
108
159
  /** Effective standalone permission mode: an explicit choice (CLI flag,
109
160
  * /permission override) wins; the workspace setting is the default.
110
- * "off" (a TUI override) and absent both resolve to undefined = ungated. */
111
- export declare function resolvePermissionMode(explicit?: "ask" | "auto" | "deny" | "off", workspaceRoot?: string): "ask" | "auto" | "deny" | undefined;
161
+ * "off" (a TUI override) and absent both resolve to undefined = ungated.
162
+ * "yolo" stays DISTINCT from undefined: both are ungated at the gate, but
163
+ * yolo additionally force-enables the OS bash sandbox — collapsing it into
164
+ * undefined would silently drop that compensating control. */
165
+ export declare function resolvePermissionMode(explicit?: "ask" | "auto" | "deny" | "yolo" | "off", workspaceRoot?: string): "ask" | "auto" | "deny" | "yolo" | undefined;
112
166
  //# sourceMappingURL=project-settings.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"project-settings.d.ts","sourceRoot":"","sources":["../../../src/core/settings/project-settings.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"project-settings.d.ts","sourceRoot":"","sources":["../../../src/core/settings/project-settings.ts"],"names":[],"mappings":"AAGA;;;;;;;;gCAQgC;AAChC,MAAM,WAAW,eAAe;IAC9B,2DAA2D;IAC3D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;+CAE2C;IAC3C,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,2EAA2E;IAC3E,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,uEAAuE;IACvE,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,yEAAyE;IACzE,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,yCAAyC;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,+DAA+D;IAC/D,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,oDAAoD;IACpD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;4EAGwE;IACxE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iDAAiD;IACjD,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,+DAA+D;IAC/D,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,4EAA4E;IAC5E,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,sDAAsD;IACtD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;iCAG6B;IAC7B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B;oCACgC;IAChC,yBAAyB,CAAC,EAAE,MAAM,CAAC;IACnC,8DAA8D;IAC9D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;8DAE0D;IAC1D,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,mFAAmF;IACnF,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;uEACmE;IACnE,YAAY,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACrD;;;;;;;8CAO0C;IAC1C,cAAc,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IAClD;0DACsD;IACtD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;4DAEwD;IACxD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;gDAE4C;IAC5C,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;uCAGmC;IACnC,UAAU,CAAC,EAAE,QAAQ,GAAG,SAAS,GAAG,OAAO,GAAG,OAAO,CAAC;IACtD;;;kDAG8C;IAC9C,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB;;;;2EAIuE;IACvE,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B;;;;;;;0CAOsC;IACtC,aAAa,CAAC,EAAE,KAAK,GAAG,SAAS,CAAC;CACnC;AAED,yEAAyE;AACzE,MAAM,WAAW,UAAU;IACzB,4DAA4D;IAC5D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,oEAAoE;IACpE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,uEAAuE;IACvE,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B;AAMD,oEAAoE;AACpE,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,CA4HjE;AAED;gDACgD;AAChD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,eAAe,GAAG,IAAI,CAWjF;AAED,iFAAiF;AACjF,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAGpE;AAED,4EAA4E;AAC5E,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAExD;AAYD;oEACoE;AACpE,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,yBAAyB,CAAC,EAAE,MAAM,CAAC;IACnC,6BAA6B,CAAC,EAAE,MAAM,CAAC;IACvC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,6EAA6E;IAC7E,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B;oDACgD;IAChD,aAAa,CAAC,EAAE,KAAK,GAAG,SAAS,CAAC;IAClC,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,wEAAwE;IACxE,KAAK,CAAC,EAAE,UAAU,CAAC;CACpB;AAqBD;;0EAE0E;AAC1E,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,eAAe,GAAG,SAAS,EACrC,GAAG,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAe,GAC9D,YAAY,CAkDd;AAED;;;;;+DAK+D;AAC/D,wBAAgB,qBAAqB,CACnC,QAAQ,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,EACnD,aAAa,CAAC,EAAE,MAAM,GACrB,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,CAM9C"}
@@ -34,6 +34,9 @@ export function loadProjectSettings(root) {
34
34
  if ("mcpTimeoutMs" in parsed && typeof parsed.mcpTimeoutMs === "number") {
35
35
  result.mcpTimeoutMs = parsed.mcpTimeoutMs;
36
36
  }
37
+ if ("askTimeoutMs" in parsed && typeof parsed.askTimeoutMs === "number") {
38
+ result.askTimeoutMs = parsed.askTimeoutMs;
39
+ }
37
40
  if ("disableWeb" in parsed && typeof parsed.disableWeb === "boolean") {
38
41
  result.disableWeb = parsed.disableWeb;
39
42
  }
@@ -59,6 +62,29 @@ export function loadProjectSettings(root) {
59
62
  if ("bashSandbox" in parsed && typeof parsed.bashSandbox === "boolean") {
60
63
  result.bashSandbox = parsed.bashSandbox;
61
64
  }
65
+ if ("browserLocalNetwork" in parsed && typeof parsed.browserLocalNetwork === "boolean") {
66
+ result.browserLocalNetwork = parsed.browserLocalNetwork;
67
+ }
68
+ if ("editFreshness" in parsed &&
69
+ (parsed.editFreshness === "off" || parsed.editFreshness === "require")) {
70
+ result.editFreshness = parsed.editFreshness;
71
+ }
72
+ if ("planEnabled" in parsed && typeof parsed.planEnabled === "boolean") {
73
+ result.planEnabled = parsed.planEnabled;
74
+ }
75
+ if ("planModel" in parsed && typeof parsed.planModel === "string" && parsed.planModel.trim()) {
76
+ result.planModel = parsed.planModel.trim();
77
+ }
78
+ if ("goalEnabled" in parsed && typeof parsed.goalEnabled === "boolean") {
79
+ result.goalEnabled = parsed.goalEnabled;
80
+ }
81
+ if ("loopAction" in parsed &&
82
+ (parsed.loopAction === "prompt" ||
83
+ parsed.loopAction === "compact" ||
84
+ parsed.loopAction === "reset" ||
85
+ parsed.loopAction === "ralph")) {
86
+ result.loopAction = parsed.loopAction;
87
+ }
62
88
  if ("memoryVector" in parsed &&
63
89
  typeof parsed.memoryVector === "object" &&
64
90
  parsed.memoryVector !== null) {
@@ -74,7 +100,8 @@ export function loadProjectSettings(root) {
74
100
  if ("permissionMode" in parsed &&
75
101
  (parsed.permissionMode === "ask" ||
76
102
  parsed.permissionMode === "auto" ||
77
- parsed.permissionMode === "deny")) {
103
+ parsed.permissionMode === "deny" ||
104
+ parsed.permissionMode === "yolo")) {
78
105
  result.permissionMode = parsed.permissionMode;
79
106
  }
80
107
  if ("maxToolTimeoutMs" in parsed && typeof parsed.maxToolTimeoutMs === "number") {
@@ -161,6 +188,9 @@ export function resolveRuntimeKnobs(settings, env = process.env) {
161
188
  const bashTimeoutMs = s.bashTimeoutMs ?? envPositive(env, "OMA_BASH_TIMEOUT_MS");
162
189
  if (bashTimeoutMs !== undefined)
163
190
  knobs.bashTimeoutMs = bashTimeoutMs;
191
+ const askTimeoutMs = s.askTimeoutMs ?? envNumber(env, "OMA_ASK_TIMEOUT_MS");
192
+ if (askTimeoutMs !== undefined)
193
+ knobs.askTimeoutMs = askTimeoutMs;
164
194
  const evalTimeoutMs = envNumber(env, "OMA_EVAL_TIMEOUT_MS");
165
195
  if (evalTimeoutMs !== undefined)
166
196
  knobs.evalTimeoutMs = evalTimeoutMs;
@@ -196,11 +226,22 @@ export function resolveRuntimeKnobs(settings, env = process.env) {
196
226
  knobs.memoryModel = s.memoryModel;
197
227
  if (s.prune)
198
228
  knobs.prune = s.prune;
229
+ const browserLocalNetwork = s.browserLocalNetwork ?? (env.OMA_BROWSER_LOCAL_NETWORK === "1" ? true : undefined);
230
+ if (browserLocalNetwork !== undefined)
231
+ knobs.browserLocalNetwork = browserLocalNetwork;
232
+ // Only an explicit "off" resolves here; absence stays undefined so the
233
+ // assembly can apply its "require" default in one visible place.
234
+ const editFreshness = s.editFreshness ?? (env.OMA_EDIT_FRESHNESS === "off" ? "off" : undefined);
235
+ if (editFreshness !== undefined)
236
+ knobs.editFreshness = editFreshness;
199
237
  return knobs;
200
238
  }
201
239
  /** Effective standalone permission mode: an explicit choice (CLI flag,
202
240
  * /permission override) wins; the workspace setting is the default.
203
- * "off" (a TUI override) and absent both resolve to undefined = ungated. */
241
+ * "off" (a TUI override) and absent both resolve to undefined = ungated.
242
+ * "yolo" stays DISTINCT from undefined: both are ungated at the gate, but
243
+ * yolo additionally force-enables the OS bash sandbox — collapsing it into
244
+ * undefined would silently drop that compensating control. */
204
245
  export function resolvePermissionMode(explicit, workspaceRoot) {
205
246
  if (explicit)
206
247
  return explicit === "off" ? undefined : explicit;
@@ -8,10 +8,10 @@ import { agentDir } from "../session/session-file.js";
8
8
  * values (Loop scopes still pass their own LOOP.md config).
9
9
  *
10
10
  * Context-file discovery absorbs oh-my-pi's context-files.md semantics,
11
- * ponytail cut: AGENTS.md only (no multi-provider discovery), no @
12
- * imports (the agent has read tools), and no RULES.md stickiness (the
13
- * prompt lands in the SYSTEM role, which is sticky by construction —
14
- * omp needs re-attachment because it injects into the opening message). */
11
+ * ponytail cut: AGENTS.md only (no multi-provider discovery), no @ imports
12
+ * (the agent has read tools), and no sticky-rule file (the prompt lands in
13
+ * the SYSTEM role, which is sticky by construction — omp needs a separate
14
+ * RULES.md because it injects into the opening message instead). */
15
15
  function readTextOrNull(path) {
16
16
  try {
17
17
  return readFileSync(path, "utf-8");
@@ -1 +1 @@
1
- {"version":3,"file":"in-memory-session-store.d.ts","sourceRoot":"","sources":["../../../src/core/store/in-memory-session-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAuC,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAS5F,wBAAgB,0BAA0B,IAAI,YAAY,CAgIzD"}
1
+ {"version":3,"file":"in-memory-session-store.d.ts","sourceRoot":"","sources":["../../../src/core/store/in-memory-session-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAuC,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAS5F,wBAAgB,0BAA0B,IAAI,YAAY,CAiIzD"}