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

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
@@ -91,11 +91,24 @@ export async function classifyPermissionAction(opts) {
91
91
  * the filesystem root, a top-level system directory, home, or a glob under
92
92
  * a shell variable (empty expansion deletes from root). Deterministic,
93
93
  * runs BEFORE the classifier, and NOTHING overrides it — the model must
94
- * re-issue a narrower, named path. $() and backtick substitution ARE
95
- * caught (the separator split lands rm in command position of a segment)
96
- * and a quoted binary ("rm" -rf /) is caught by quote-stripping;
97
- * `command rm`, `\rm`, and variable commands ($CMD) are NOT — the
98
- * classifier prompt's destruction rule is the second layer.
94
+ * re-issue a narrower, named path.
95
+ *
96
+ * Command position is found by walking past forms that shift it, since a
97
+ * wrapper (`bash -c "rm -rf /"`), an exec prefix (`nice rm -rf /`) or an
98
+ * absolute binary (`/bin/rm`) otherwise hides the delete: $()/backtick
99
+ * substitution (the segment split lands rm in command position), the wrappers
100
+ * listed in SHELL_WRAPPERS, their flags and `VAR=value` assignments, plus
101
+ * quoting/escaping on the binary itself ("rm" / \rm / /bin/rm).
102
+ *
103
+ * Ceiling (the classifier prompt's destruction rule is the second layer): an
104
+ * rm behind a variable or a computed command ($CMD, `$(printf rm)`), behind a
105
+ * wrapper not in the set (`chroot / rm -rf /`), and the mirror-image
106
+ * ambiguity — a wrapper OPTION's argument read as command position, so
107
+ * `command -v rm /etc` and `sudo -u root rm -rf /` are judged wrongly (the
108
+ * first is a false positive, the second a miss). A token scan cannot resolve
109
+ * that; a real shell parser is the fix, and the ponytail note below is the
110
+ * standing invitation to write one.
111
+ *
99
112
  * ponytail: token scan, not a shell AST; move to a real parser if models
100
113
  * start hiding critical deletes in the remaining forms. */
101
114
  const TOP_LEVEL_DIRS = "usr|etc|var|bin|sbin|lib|lib64|boot|dev|proc|sys|opt|home|root|tmp|mnt|media|srv|run|data";
@@ -114,17 +127,75 @@ function isCriticalTarget(token) {
114
127
  return true;
115
128
  return false;
116
129
  }
130
+ /** Shell prefixes that put the real command later in the same segment.
131
+ * `bash -c "rm -rf /"` was the whole bypass: the first token was `bash`. */
132
+ const SHELL_WRAPPERS = new Set([
133
+ // Shells: `bash -c "rm -rf /"` was the original bypass.
134
+ "bash",
135
+ "sh",
136
+ "zsh",
137
+ "dash",
138
+ "ksh",
139
+ "ash",
140
+ // Exec prefixes that take no option argument, so the next bare token really
141
+ // is the command: `nice rm -rf /`, `eval 'rm -rf /'`, `busybox rm -rf /`.
142
+ "nice",
143
+ "ionice",
144
+ "setsid",
145
+ "stdbuf",
146
+ "time",
147
+ "eval",
148
+ "exec",
149
+ "busybox",
150
+ "toybox",
151
+ // Privilege/environment prefixes.
152
+ "env",
153
+ "nohup",
154
+ "sudo",
155
+ "doas",
156
+ "su",
157
+ "runuser",
158
+ "command",
159
+ "xargs",
160
+ ]);
161
+ /** The binary a token names once the shell has stripped quoting (`"rm"`),
162
+ * escaping (`\rm`) and any directory prefix (`/bin/rm`, `./rm`). */
163
+ function binaryName(token) {
164
+ const unquoted = token.replace(/^["']+|["']+$/g, "");
165
+ const unescaped = unquoted.startsWith("\\") ? unquoted.slice(1) : unquoted;
166
+ return unescaped.replace(/^.*\//, "");
167
+ }
117
168
  export function isCriticalDeletion(command) {
118
- // rm/rmdir must be in COMMAND POSITION (first token of a segment split
119
- // on shell separators incl. newline and backtick substitution — quotes on
120
- // the binary itself are stripped). "echo about rm /etc" is prose.
169
+ // rm/rmdir must be in COMMAND POSITION: the first token of a segment (split
170
+ // on shell separators incl. newline and backtick substitution) once wrappers,
171
+ // their flags and `VAR=value` assignments are walked past. "echo about rm
172
+ // /etc" is prose — its command position is `echo`, so it is not our business.
121
173
  const segments = command.split(/[;&|()`\n]/);
122
174
  for (const segment of segments) {
123
- const tokens = segment.trim().split(/\s+/);
124
- const first = (tokens[0] ?? "").replace(/^["']+|["']+$/g, "");
125
- if (!/^(rm|rmdir)$/.test(first))
175
+ const tokens = segment
176
+ .trim()
177
+ .split(/\s+/)
178
+ .filter((t) => t.length > 0);
179
+ let i = 0;
180
+ while (i < tokens.length) {
181
+ const token = tokens[i];
182
+ if (/^[A-Za-z_]\w*=/.test(token)) {
183
+ i++; // env assignment FOO=1
184
+ continue;
185
+ }
186
+ if (SHELL_WRAPPERS.has(binaryName(token))) {
187
+ i++; // nested wrappers: bash -c "sh -c 'rm -rf /'"
188
+ continue;
189
+ }
190
+ if (token.startsWith("-")) {
191
+ i++; // wrapper flag (-c, -lc, -0, ...)
192
+ continue;
193
+ }
194
+ break;
195
+ }
196
+ if (!/^(rm|rmdir)$/.test(binaryName(tokens[i] ?? "")))
126
197
  continue;
127
- for (let j = 1; j < tokens.length; j++) {
198
+ for (let j = i + 1; j < tokens.length; j++) {
128
199
  const token = tokens[j];
129
200
  if (token.startsWith("-"))
130
201
  continue; // flags
@@ -80,6 +80,13 @@ export interface Plugin {
80
80
  readonly tools?: readonly PluginTool[];
81
81
  readonly meta?: readonly MetaSectionProvider[];
82
82
  }
83
+ /** Fails closed on every collision class that is unambiguous: duplicate plugin
84
+ * names, duplicate tool names, and duplicate meta-section names. The meta
85
+ * check used to be missing, and renderMeta simply emitted `## <name>` twice —
86
+ * two context sections that look like one, with no error anywhere.
87
+ *
88
+ * Hook "collisions" are not a collision: every plugin's hook runs, in plugin
89
+ * order, by design. */
83
90
  export declare function validatePlugins(plugins: readonly Plugin[]): void;
84
91
  export declare function renderMeta(plugins: readonly Plugin[]): string;
85
92
  export declare function collectTools(plugins: readonly Plugin[]): PluginTool[];
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../src/core/runtime/plugin.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../src/core/runtime/plugin.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,14 @@
1
+ /** Fails closed on every collision class that is unambiguous: duplicate plugin
2
+ * names, duplicate tool names, and duplicate meta-section names. The meta
3
+ * check used to be missing, and renderMeta simply emitted `## <name>` twice —
4
+ * two context sections that look like one, with no error anywhere.
5
+ *
6
+ * Hook "collisions" are not a collision: every plugin's hook runs, in plugin
7
+ * order, by design. */
1
8
  export function validatePlugins(plugins) {
2
9
  const names = new Set();
3
10
  const toolNames = new Set();
11
+ const metaNames = new Set();
4
12
  for (const p of plugins) {
5
13
  if (names.has(p.name))
6
14
  throw new Error(`Duplicate plugin name: ${p.name}`);
@@ -10,6 +18,11 @@ export function validatePlugins(plugins) {
10
18
  throw new Error(`Duplicate tool name: ${t.name} (plugin ${p.name})`);
11
19
  toolNames.add(t.name);
12
20
  }
21
+ for (const m of p.meta ?? []) {
22
+ if (metaNames.has(m.name))
23
+ throw new Error(`Duplicate meta section: ${m.name} (plugin ${p.name})`);
24
+ metaNames.add(m.name);
25
+ }
13
26
  }
14
27
  }
15
28
  export function renderMeta(plugins) {
@@ -1,12 +1,3 @@
1
- /** Process-tree teardown helper shared by MCP stdio clients.
2
-
3
- * The MCP SDK's StdioClientTransport.close() only SIGKILLs its direct
4
- * child. npm/npx-style servers spawn grandchildren (npm -> sh -> node)
5
- * that inherit the stdio pipes; once the direct child dies they are
6
- * reparented to init, so a post-close pgrep -P walk cannot find them.
7
- * Collect the whole descendant tree BEFORE closing, then SIGKILL every
8
- * pid after the SDK close. */
9
- export declare function collectDescendants(pid: number): number[];
10
1
  /** Collect pid + descendants, then SIGKILL them all (best-effort). */
11
2
  export declare function killProcessTree(pid: number): void;
12
3
  //# sourceMappingURL=process-tree.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"process-tree.d.ts","sourceRoot":"","sources":["../../../src/core/runtime/process-tree.ts"],"names":[],"mappings":"AAAA;;;;;;;+BAO+B;AAC/B,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAkBxD;AAED,sEAAsE;AACtE,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CASjD"}
1
+ {"version":3,"file":"process-tree.d.ts","sourceRoot":"","sources":["../../../src/core/runtime/process-tree.ts"],"names":[],"mappings":"AA4BA,sEAAsE;AACtE,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CASjD"}
@@ -6,7 +6,7 @@
6
6
  * reparented to init, so a post-close pgrep -P walk cannot find them.
7
7
  * Collect the whole descendant tree BEFORE closing, then SIGKILL every
8
8
  * pid after the SDK close. */
9
- export function collectDescendants(pid) {
9
+ function collectDescendants(pid) {
10
10
  const descendants = [];
11
11
  const queue = [pid];
12
12
  while (queue.length > 0) {
@@ -1 +1 @@
1
- {"version":3,"file":"run-runtime.d.ts","sourceRoot":"","sources":["../../../src/core/runtime/run-runtime.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"run-runtime.d.ts","sourceRoot":"","sources":["../../../src/core/runtime/run-runtime.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { existsSync, mkdirSync, writeFileSync } from "node:fs";
3
3
  import { join } from "node:path";
4
- import { debugLog, } from "@chengchenccc/agent-contract";
4
+ import { CONSENTED_MCP_TOOLS_ENV, debugLog, decodeEnvList, } from "@chengchenccc/agent-contract";
5
5
  import { resolveModelAlias } from "@chengchenccc/ai";
6
6
  import { createHubTool } from "../coordination/index.js";
7
7
  import { createCoordinationRegistry } from "../coordination/registry.js";
@@ -25,37 +25,16 @@ import { createSkill } from "../tools/skill.js";
25
25
  import { createTodo, createTodoReadTool } from "../tools/todo.js";
26
26
  import { createFileTodoStore } from "../tools/todo-store.js";
27
27
  import { DEFAULT_APPROVAL_TIMEOUT_MS, withApprovalDeadline } from "./approval.js";
28
+ import { estimateMessageTokens } from "./context-estimate.js";
28
29
  import { fakeProvider } from "./fake-provider.js";
29
30
  import { reasoningEffortOptions } from "./model-effort.js";
30
31
  import { classifyPermissionAction, DEFAULT_CLASSIFIER_TIMEOUT_MS, isCriticalDeletion, } from "./permission-classifier.js";
31
32
  import { loadRuntimeCatalog, registerProvidersFromCatalog } from "./runtime-catalog.js";
32
33
  import { loadStreamRules } from "./stream-rules.js";
33
34
  import { toolFilterAllows } from "./tool-filter.js";
34
- /** Token estimation via content char/4 (approx 1 token per 4 chars of
35
- * English/code). More accurate than JSON.stringify char/4 which includes
36
- * ~30% syntax overhead from key names, quotes, braces. Counts actual text +
37
- * block content, adds a fixed overhead per message for role/structure
38
- * framing. Swap for a real tokenizer (tiktoken, provider SDK) by replacing
39
- * this function — the ContextBudget.estimate interface is the extension
40
- * point. */
41
- function estimateMessageTokens(message) {
42
- let chars = message.text?.length ?? 0;
43
- if (message.blocks) {
44
- for (const b of message.blocks) {
45
- if (b.type === "text")
46
- chars += b.text.length;
47
- else if (b.type === "tool_use")
48
- chars += JSON.stringify(b.input).length;
49
- else if (b.type === "tool_result" && typeof b.content === "string")
50
- chars += b.content.length;
51
- else if (b.type === "thinking" && typeof b.text === "string")
52
- chars += b.text.length;
53
- }
54
- }
55
- // ~4 chars/token for content + 4 tokens framing overhead per message
56
- // (role tag, separators — matches Anthropic's documented overhead).
57
- return Math.ceil(chars / 4) + 4;
58
- }
35
+ // The estimator lives in context-estimate.ts: pruning and the compaction
36
+ // budget must measure the same quantity, or pruning's savedTokens are not
37
+ // comparable to the budget they are subtracted from.
59
38
  /** Default native tool timeout (ms) for file/web/bash unless overridden. */
60
39
  const DEFAULT_NATIVE_TOOL_TIMEOUT_MS = 30_000;
61
40
  /** Wall-clock cap on ONE model call: a silent/stuck provider must not leave
@@ -199,8 +178,13 @@ function toPluginTool(t) {
199
178
  * timeouts only on the main path). */
200
179
  async function buildNativeToolStage(deps, ctx) {
201
180
  const { knobs, bashSandbox, scope, registry } = ctx;
181
+ // Write freshness: "require" unless the workspace explicitly downgraded it.
182
+ // Absent knobs.editFreshness therefore means ON, so a workspace that never
183
+ // mentions it still gets the gate. (A backend RPC run never sees a workspace
184
+ // value here at all — see the projectSettings allowlist in assembleRunRuntime.)
185
+ const fileFreshness = knobs.editFreshness ?? "require";
202
186
  const agentTools = [
203
- toPluginTool(createReadTool({ cwd: deps.workspaceRoot })),
187
+ toPluginTool(createReadTool({ cwd: deps.workspaceRoot, freshness: fileFreshness })),
204
188
  toPluginTool(createReadImageTool({ cwd: deps.workspaceRoot })),
205
189
  // ls and tree are the two directory views: ls is flat + mtime sorted
206
190
  // (cheap orientation), tree is recursive (structure). Both are read-side,
@@ -211,8 +195,16 @@ async function buildNativeToolStage(deps, ctx) {
211
195
  toPluginTool(createGrepTool({ workspaceRoot: deps.workspaceRoot })),
212
196
  ];
213
197
  if (deps.workspaceAccess === "read_write") {
214
- agentTools.push(toPluginTool(createWriteTool({ cwd: deps.workspaceRoot })));
215
- agentTools.push(toPluginTool(createEditTool({ cwd: deps.workspaceRoot })));
198
+ agentTools.push(toPluginTool(createWriteTool({
199
+ cwd: deps.workspaceRoot,
200
+ freshness: fileFreshness,
201
+ ...(deps.planMode ? { planMode: deps.planMode } : {}),
202
+ })));
203
+ agentTools.push(toPluginTool(createEditTool({
204
+ cwd: deps.workspaceRoot,
205
+ freshness: fileFreshness,
206
+ ...(deps.planMode ? { planMode: deps.planMode } : {}),
207
+ })));
216
208
  const bashToolOpts = { workspaceRoot: deps.workspaceRoot, scope, timeouts: knobs, registry };
217
209
  if (bashSandbox)
218
210
  bashToolOpts.sandbox = bashSandbox;
@@ -228,12 +220,17 @@ async function buildNativeToolStage(deps, ctx) {
228
220
  // Interactive web (JS/interaction/screenshots): browser is a headless
229
221
  // Chromium subprocess — read_only runs don't get it (it writes
230
222
  // screenshots into the workspace and drives real sessions).
231
- agentTools.push(toPluginTool(createBrowserTool({ workspaceRoot: deps.workspaceRoot })));
223
+ agentTools.push(toPluginTool(createBrowserTool({
224
+ workspaceRoot: deps.workspaceRoot,
225
+ // Opt-in local access (dev-server screenshots). Absent = the same
226
+ // egress rule web_fetch enforces.
227
+ ...(knobs.browserLocalNetwork ? { allowLocalNetwork: true } : {}),
228
+ })));
232
229
  }
233
- // Generic .mcp.json mounting (ADR 0022): user servers + knowledge.
234
- // Skips "product-tools" (the manifest path owns it) and names that
235
- // collide with the native table. The mounted clients join the run's
236
- // teardown set so stdio children never outlive the run.
230
+ // Generic .mcp.json mounting (ADR 0022): user servers + spawner-bridged
231
+ // servers. Skips names that collide with the native table; the mounted
232
+ // clients join the run's teardown set so stdio children never outlive
233
+ // the run.
237
234
  // Standalone gating: an untrusted repo-controlled .mcp.json mounts
238
235
  // nothing (fail-closed) — the mode layer surfaces the warning.
239
236
  let includeWorkspaceMcp = true;
@@ -358,20 +355,23 @@ function createRunPermissionGates(deps, ctx) {
358
355
  eval: true,
359
356
  write: true,
360
357
  edit: true,
361
- create_file: true,
362
358
  // learn/manage_skill write files; the skill branch writes OUTSIDE the
363
359
  // workspace (<agentDir>/managed-skills), so the workspace-sandbox
364
360
  // exemption cannot cover them (omp marks both approval="write").
365
361
  learn: true,
366
362
  manage_skill: true,
367
363
  };
368
- // Product-owned mounts (workspace-bridge: features.ts names them
369
- // "product-tools" / "knowledge") are consented-by-design read/context
370
- // surfaces with their own per-run auth — never gate them, or ask/deny
371
- // modes would demand a human click per history_* call.
372
- const PRODUCT_MOUNTED_PREFIXES = ["mcp__product-tools__", "mcp__knowledge__"];
373
- const isProductMounted = (toolName) => PRODUCT_MOUNTED_PREFIXES.some((p) => toolName.startsWith(p));
374
- const classifierGated = (toolName, input) => !isProductMounted(toolName) &&
364
+ // Consent for mounted MCP tools is INJECTED policy (spawner-declared env
365
+ // var; name + comma codec single-sourced in agent-contract env.ts): the
366
+ // product declares its own read surfaces — reads of the run's
367
+ // conversation / knowledge / artifact storage, the run's todo scratch
368
+ // state, and the interactive ask — so ask/deny modes don't demand a
369
+ // human click per history_* call. oma itself holds NO product tool
370
+ // names; absent = every mcp__ tool is gated (fail-safe: a tool the
371
+ // product adds later stays gated until the spawner lists it).
372
+ const CONSENTED_MCP_TOOLS = new Set(decodeEnvList(process.env[CONSENTED_MCP_TOOLS_ENV]));
373
+ const isConsentedProductTool = (toolName) => CONSENTED_MCP_TOOLS.has(toolName);
374
+ const classifierGated = (toolName, input) => !isConsentedProductTool(toolName) &&
375
375
  (toolName === "bash" ||
376
376
  toolName === "browser" ||
377
377
  toolName === "eval" ||
@@ -460,14 +460,22 @@ function createRunPermissionGates(deps, ctx) {
460
460
  * escalatedActions set per Run) but judge against their own intent —
461
461
  * a subagent's "user request" is the task it was assigned. */
462
462
  const makeSessionPermissionGate = (intentTexts) => async (toolName, input, callId) => {
463
- if (deps.permissionMode === undefined)
463
+ // "yolo" and absent are both ungated BY DESIGN: yolo is the
464
+ // skip-permissions mode. Everything that does not depend on judgment
465
+ // still applies (workspace containment, protected files, secret
466
+ // stripping, egress rules, write freshness) — those are tool-layer
467
+ // boundaries, not this gate.
468
+ const ungated = deps.permissionMode === undefined || deps.permissionMode === "yolo";
469
+ if (ungated)
464
470
  return undefined;
465
471
  if (deps.permissionMode === "auto") {
466
472
  if (!classifierGated(toolName, input))
467
473
  return undefined;
468
- // The auto gate must be fail-CLOSED end to end: the agent loop
469
- // swallows gate exceptions as "no verdict" (= allow), so any
470
- // error in here must convert to a block, never propagate.
474
+ // The auto gate must be fail-CLOSED end to end: any error in here
475
+ // must convert to a block, never propagate. The loop also catches a
476
+ // throwing gate and blocks ("permission gate error — fail closed",
477
+ // pinned by permission-gate-loop.test.ts); this try/catch is
478
+ // defense-in-depth with the block reason naming the real cause.
471
479
  try {
472
480
  return await autoGateDecision(toolName, input, callId, [
473
481
  ...intentTexts,
@@ -481,7 +489,7 @@ function createRunPermissionGates(deps, ctx) {
481
489
  };
482
490
  }
483
491
  }
484
- const isHighRisk = !isProductMounted(toolName) &&
492
+ const isHighRisk = !isConsentedProductTool(toolName) &&
485
493
  (HIGH_RISK_NATIVE_TOOLS[toolName] === true || toolName.startsWith("mcp__"));
486
494
  if (!isHighRisk)
487
495
  return undefined;
@@ -717,12 +725,30 @@ export async function assembleRunRuntime(deps) {
717
725
  // enabled-but-tool-missing — the Run fails loudly rather than silently
718
726
  // running unconstrained.
719
727
  let bashSandbox;
720
- if (projectSettings.bashSandbox) {
728
+ const sandboxConfigured = projectSettings.bashSandbox === true;
729
+ if (sandboxConfigured) {
721
730
  bashSandbox = resolveBashSandbox({
722
731
  workspaceRoot: deps.workspaceRoot,
723
732
  enabled: true,
724
733
  });
725
734
  }
735
+ // Yolo needs a boundary that does not depend on judgment (the 2026
736
+ // consensus after the s1ngularity incident: an installed agent CLI with
737
+ // its permission flag disabled is a standing capability any package can
738
+ // borrow). Force the OS bash sandbox when the platform has one; a missing
739
+ // one degrades with a loud log instead of failing the Run — the remaining
740
+ // static boundaries (workspace containment for file tools, secret
741
+ // stripping, egress rules) still hold.
742
+ const yoloWantsBoundary = deps.permissionMode === "yolo" && bashSandbox === undefined;
743
+ if (yoloWantsBoundary) {
744
+ try {
745
+ bashSandbox = resolveBashSandbox({ workspaceRoot: deps.workspaceRoot, enabled: true });
746
+ debugLog("oma", "yolo: OS bash sandbox force-enabled");
747
+ }
748
+ catch (err) {
749
+ debugLog("oma", `yolo: no OS bash sandbox available (${err instanceof Error ? err.message : String(err)}) — bash runs unsandboxed; workspace/egress boundaries remain`);
750
+ }
751
+ }
726
752
  const scope = deps.coordinationScope ?? deps.runId;
727
753
  const registry = deps.registry ?? createCoordinationRegistry();
728
754
  const toolStage = await buildNativeToolStage(deps, {
@@ -751,6 +777,14 @@ export async function assembleRunRuntime(deps) {
751
777
  const nativeNames = new Set(plugins.flatMap((p) => (p.tools ?? []).map((t) => t.name)));
752
778
  const askGate = deps.permissionMode === "ask";
753
779
  for (const cp of deps.codePlugins) {
780
+ // A shadowed plugin tool is dropped, not merged (spec conflict
781
+ // matrix). Say so: an operator debugging a missing tool otherwise gets
782
+ // no signal at all.
783
+ for (const t of cp.tools ?? []) {
784
+ if (nativeNames.has(t.name)) {
785
+ debugLog("oma", `plugin tool "${t.name}" shadowed by a native tool; dropped`);
786
+ }
787
+ }
754
788
  const tools = (cp.tools ?? [])
755
789
  .filter((t) => !nativeNames.has(t.name))
756
790
  .map((t) => {
@@ -1055,8 +1089,13 @@ export async function assembleRunRuntime(deps) {
1055
1089
  // then dropped entirely (nothing can outlive it); a surface-provided
1056
1090
  // one keeps completed handles for a later Run's resume.
1057
1091
  delegationExecutor.stopLiveSubagents();
1058
- if (!deps.registry)
1092
+ if (!deps.registry) {
1093
+ // Kill what this Run owns BEFORE dropping the table: bg bash/eval
1094
+ // children must not outlive the Run just because the bookkeeping
1095
+ // did (clearAll alone orphans the processes).
1096
+ registry.stopRunningEntries();
1059
1097
  registry.clearAll();
1098
+ }
1060
1099
  // Tear down mounted MCP clients so no child process or connection
1061
1100
  // outlives the Run. Each close is BOUNDED: a stuck transport (e.g. an
1062
1101
  // SSE socket that never answers close) must not wedge the child.
@@ -2,10 +2,6 @@ import { type CatalogSpec, type ModelRuntime } from "@chengchenccc/ai";
2
2
  /** Read and merge the first available runtime models.yml with BUILTIN_CATALOG.
3
3
  * Returns BUILTIN_CATALOG unchanged if no file is found. */
4
4
  export declare function loadRuntimeCatalog(env?: Record<string, string | undefined>): CatalogSpec;
5
- /** Deep-merge two catalogs: override providers/models win; new entries added.
6
- * Provider-level fields (api, baseUrl, apiKeyEnv) are overridden when present.
7
- * Within a provider, models merge by id. */
8
- export declare function mergeCatalogs(base: CatalogSpec, override: CatalogSpec): CatalogSpec;
9
5
  /** Register every provider in the catalog whose apiKey env var is set.
10
6
  * Providers without credentials are silently skipped. */
11
7
  export declare function registerProvidersFromCatalog(runtime: ModelRuntime, catalog: CatalogSpec, env: Record<string, string | undefined>): void;
@@ -1 +1 @@
1
- {"version":3,"file":"runtime-catalog.d.ts","sourceRoot":"","sources":["../../../src/core/runtime/runtime-catalog.ts"],"names":[],"mappings":"AAGA,OAAO,EAGL,KAAK,WAAW,EAEhB,KAAK,YAAY,EAIlB,MAAM,kBAAkB,CAAC;AAsB1B;6DAC6D;AAC7D,wBAAgB,kBAAkB,CAChC,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAe,GACpD,WAAW,CAOb;AAED;;6CAE6C;AAC7C,wBAAgB,aAAa,CAAC,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,WAAW,GAAG,WAAW,CASnF;AASD;0DAC0D;AAC1D,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,YAAY,EACrB,OAAO,EAAE,WAAW,EACpB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GACtC,IAAI,CAgBN"}
1
+ {"version":3,"file":"runtime-catalog.d.ts","sourceRoot":"","sources":["../../../src/core/runtime/runtime-catalog.ts"],"names":[],"mappings":"AAGA,OAAO,EAGL,KAAK,WAAW,EAEhB,KAAK,YAAY,EAIlB,MAAM,kBAAkB,CAAC;AAsB1B;6DAC6D;AAC7D,wBAAgB,kBAAkB,CAChC,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAe,GACpD,WAAW,CAOb;AAuBD;0DAC0D;AAC1D,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,YAAY,EACrB,OAAO,EAAE,WAAW,EACpB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GACtC,IAAI,CAgBN"}
@@ -36,7 +36,7 @@ export function loadRuntimeCatalog(env = process.env) {
36
36
  /** Deep-merge two catalogs: override providers/models win; new entries added.
37
37
  * Provider-level fields (api, baseUrl, apiKeyEnv) are overridden when present.
38
38
  * Within a provider, models merge by id. */
39
- export function mergeCatalogs(base, override) {
39
+ function mergeCatalogs(base, override) {
40
40
  const providers = { ...base.providers };
41
41
  for (const [pid, oSpec] of Object.entries(override.providers)) {
42
42
  const bSpec = providers[pid];
@@ -3,7 +3,14 @@ import type { StreamRule } from "../index.js";
3
3
  * `<root>/.oma/rules/*.md`. Frontmatter `condition: <regex>` is required;
4
4
  * the body below the frontmatter is the reminder text injected when the
5
5
  * rule matches mid-stream. Rule name = filename without `.md`.
6
- * Fail-open: an unreadable file, missing condition, empty body, or invalid
7
- * regex is skipped — rules must never block session startup. */
6
+ *
7
+ * Fail-open: an unreadable file, missing condition, empty body, or invalid
8
+ * regex is skipped — rules must never block session startup.
9
+ *
10
+ * Two silent no-ops were NOT fail-open, they were fail-dead: the value is
11
+ * read from the raw `condition:` line (no YAML parse), so a quoted pattern
12
+ * compiled WITH its quotes and never matched anything, and the frontmatter
13
+ * anchor required bare LF, so a CRLF rule file dropped every rule in it. Both
14
+ * now load as written. */
8
15
  export declare function loadStreamRules(root: string): StreamRule[];
9
16
  //# sourceMappingURL=stream-rules.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"stream-rules.d.ts","sourceRoot":"","sources":["../../../src/core/runtime/stream-rules.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C;;;;;gEAKgE;AAChE,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,EAAE,CA+B1D"}
1
+ {"version":3,"file":"stream-rules.d.ts","sourceRoot":"","sources":["../../../src/core/runtime/stream-rules.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C;;;;;;;;;;;;2BAY2B;AAC3B,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,EAAE,CAuC1D"}
@@ -4,8 +4,15 @@ import { join } from "node:path";
4
4
  * `<root>/.oma/rules/*.md`. Frontmatter `condition: <regex>` is required;
5
5
  * the body below the frontmatter is the reminder text injected when the
6
6
  * rule matches mid-stream. Rule name = filename without `.md`.
7
- * Fail-open: an unreadable file, missing condition, empty body, or invalid
8
- * regex is skipped — rules must never block session startup. */
7
+ *
8
+ * Fail-open: an unreadable file, missing condition, empty body, or invalid
9
+ * regex is skipped — rules must never block session startup.
10
+ *
11
+ * Two silent no-ops were NOT fail-open, they were fail-dead: the value is
12
+ * read from the raw `condition:` line (no YAML parse), so a quoted pattern
13
+ * compiled WITH its quotes and never matched anything, and the frontmatter
14
+ * anchor required bare LF, so a CRLF rule file dropped every rule in it. Both
15
+ * now load as written. */
9
16
  export function loadStreamRules(root) {
10
17
  const dir = join(root, ".oma", "rules");
11
18
  let files;
@@ -21,11 +28,18 @@ export function loadStreamRules(root) {
21
28
  for (const file of files) {
22
29
  try {
23
30
  const content = readFileSync(join(dir, file), "utf8");
24
- const frontmatter = content.match(/^---\n([\s\S]*?)\n---/)?.[1];
25
- const condition = frontmatter?.match(/^condition:\s*(.+)$/m)?.[1]?.trim();
31
+ // \r?\n: a CRLF rule file must load, not silently yield no rules.
32
+ const frontmatter = content.match(/^---\r?\n([\s\S]*?)\r?\n---/)?.[1];
33
+ const raw = frontmatter?.match(/^condition:\s*(.+)$/m)?.[1]?.trim();
34
+ // One matching pair of quotes is YAML syntax, not part of the pattern:
35
+ // `condition: 'foo|bar'` must compile to foo|bar.
36
+ const condition = raw &&
37
+ ((raw.startsWith("'") && raw.endsWith("'")) || (raw.startsWith('"') && raw.endsWith('"')))
38
+ ? raw.slice(1, -1)
39
+ : raw;
26
40
  if (!condition)
27
41
  continue;
28
- const message = content.replace(/^---\n[\s\S]*?\n---\n?/, "").trim();
42
+ const message = content.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/, "").trim();
29
43
  if (!message)
30
44
  continue;
31
45
  rules.push({
@@ -2,6 +2,18 @@ import { type Message } from "@chengchenccc/message";
2
2
  import type { PluginRuntime } from "./plugin-runtime.js";
3
3
  export declare function isLowSignalTitleInput(message: string): boolean;
4
4
  export declare function normalizeGeneratedTitle(raw: string): string | null;
5
+ /** The one-sentence session summary that rides the title call. Null when the
6
+ * model answered with a bare title (older prompts, weak models) — the resume
7
+ * list then falls back to the title/preview it showed before. */
8
+ export declare function normalizeGeneratedSummary(raw: string): string | null;
5
9
  export declare function buildTitleContext(msgs: Message[], maxTurns?: number): string;
6
- export declare function generateTitle(rt: PluginRuntime, context: string): Promise<string | null>;
10
+ /** Title + summary from ONE ephemeral call. Both ride the same 3-7-word
11
+ * prompt because a second call would double the per-session cost for a
12
+ * resume-list nicety; a weak model that answers title-only still yields a
13
+ * usable title and a null summary. */
14
+ export interface GeneratedLabels {
15
+ readonly title: string | null;
16
+ readonly summary: string | null;
17
+ }
18
+ export declare function generateTitleAndSummary(rt: PluginRuntime, context: string): Promise<GeneratedLabels>;
7
19
  //# sourceMappingURL=title.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"title.d.ts","sourceRoot":"","sources":["../../../src/core/runtime/title.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAClE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AA0CzD,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAI9D;AA0BD,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAelE;AAID,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,QAAQ,SAAI,GAAG,MAAM,CAOvE;AAID,wBAAsB,aAAa,CAAC,EAAE,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAmB9F"}
1
+ {"version":3,"file":"title.d.ts","sourceRoot":"","sources":["../../../src/core/runtime/title.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAClE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AA0CzD,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAI9D;AA6CD,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAYlE;AAED;;kEAEkE;AAClE,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAWpE;AAID,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,QAAQ,SAAI,GAAG,MAAM,CAOvE;AAID;;;uCAGuC;AACvC,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAED,wBAAsB,uBAAuB,CAC3C,EAAE,EAAE,aAAa,EACjB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,eAAe,CAAC,CAsB1B"}