@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.
- package/README.md +11 -6
- package/dist/cli/args.d.ts +1 -1
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +9 -2
- package/dist/cli/gateway-commands.d.ts.map +1 -1
- package/dist/cli/gateway-commands.js +35 -5
- package/dist/cli/initial-input.d.ts +3 -2
- package/dist/cli/initial-input.d.ts.map +1 -1
- package/dist/core/coordination/registry.d.ts +11 -0
- package/dist/core/coordination/registry.d.ts.map +1 -1
- package/dist/core/coordination/registry.js +23 -0
- package/dist/core/gateway/artifact.d.ts +0 -1
- package/dist/core/gateway/artifact.d.ts.map +1 -1
- package/dist/core/gateway/artifact.js +1 -1
- package/dist/core/goals/index.d.ts +8 -0
- package/dist/core/goals/index.d.ts.map +1 -0
- package/dist/core/goals/index.js +7 -0
- package/dist/core/goals/runtime.d.ts +112 -0
- package/dist/core/goals/runtime.d.ts.map +1 -0
- package/dist/core/goals/runtime.js +241 -0
- package/dist/core/goals/state.d.ts +82 -0
- package/dist/core/goals/state.d.ts.map +1 -0
- package/dist/core/goals/state.js +225 -0
- package/dist/core/goals/tool.d.ts +10 -0
- package/dist/core/goals/tool.d.ts.map +1 -0
- package/dist/core/goals/tool.js +87 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +4 -3
- package/dist/core/loops/condition.d.ts +57 -0
- package/dist/core/loops/condition.d.ts.map +1 -0
- package/dist/core/loops/condition.js +153 -0
- package/dist/core/loops/index.d.ts +9 -0
- package/dist/core/loops/index.d.ts.map +1 -0
- package/dist/core/loops/index.js +8 -0
- package/dist/core/loops/limits.d.ts +43 -0
- package/dist/core/loops/limits.d.ts.map +1 -0
- package/dist/core/loops/limits.js +219 -0
- package/dist/core/loops/ralph.d.ts +37 -0
- package/dist/core/loops/ralph.d.ts.map +1 -0
- package/dist/core/loops/ralph.js +72 -0
- package/dist/core/loops/runtime.d.ts +95 -0
- package/dist/core/loops/runtime.d.ts.map +1 -0
- package/dist/core/loops/runtime.js +222 -0
- package/dist/core/memory/managed-skills.d.ts +0 -11
- package/dist/core/memory/managed-skills.d.ts.map +1 -1
- package/dist/core/memory/managed-skills.js +2 -2
- package/dist/core/memory/vector-store.d.ts +0 -1
- package/dist/core/memory/vector-store.d.ts.map +1 -1
- package/dist/core/memory/vector-store.js +0 -3
- package/dist/core/plans/index.d.ts +7 -0
- package/dist/core/plans/index.d.ts.map +1 -0
- package/dist/core/plans/index.js +6 -0
- package/dist/core/plans/prompts.d.ts +20 -0
- package/dist/core/plans/prompts.d.ts.map +1 -0
- package/dist/core/plans/prompts.js +72 -0
- package/dist/core/plans/runtime.d.ts +89 -0
- package/dist/core/plans/runtime.d.ts.map +1 -0
- package/dist/core/plans/runtime.js +170 -0
- package/dist/core/plans/state.d.ts +55 -0
- package/dist/core/plans/state.d.ts.map +1 -0
- package/dist/core/plans/state.js +88 -0
- package/dist/core/runtime/agent-loop-messages.d.ts +6 -0
- package/dist/core/runtime/agent-loop-messages.d.ts.map +1 -1
- package/dist/core/runtime/agent-loop-messages.js +40 -15
- package/dist/core/runtime/agent-loop-run.d.ts.map +1 -1
- package/dist/core/runtime/agent-loop-run.js +18 -27
- package/dist/core/runtime/agent-loop-runner.d.ts +1 -23
- package/dist/core/runtime/agent-loop-runner.d.ts.map +1 -1
- package/dist/core/runtime/agent-loop-runner.js +84 -30
- package/dist/core/runtime/agent-loop-types.d.ts +4 -0
- package/dist/core/runtime/agent-loop-types.d.ts.map +1 -1
- package/dist/core/runtime/compaction.d.ts +9 -0
- package/dist/core/runtime/compaction.d.ts.map +1 -1
- package/dist/core/runtime/compaction.js +28 -0
- package/dist/core/runtime/context-estimate.d.ts +37 -4
- package/dist/core/runtime/context-estimate.d.ts.map +1 -1
- package/dist/core/runtime/context-estimate.js +70 -0
- package/dist/core/runtime/create-runtime.d.ts +27 -5
- package/dist/core/runtime/create-runtime.d.ts.map +1 -1
- package/dist/core/runtime/create-runtime.js +22 -2
- package/dist/core/runtime/permission-classifier.d.ts.map +1 -1
- package/dist/core/runtime/permission-classifier.js +83 -12
- package/dist/core/runtime/plugin.d.ts +7 -0
- package/dist/core/runtime/plugin.d.ts.map +1 -1
- package/dist/core/runtime/plugin.js +13 -0
- package/dist/core/runtime/process-tree.d.ts +0 -9
- package/dist/core/runtime/process-tree.d.ts.map +1 -1
- package/dist/core/runtime/process-tree.js +1 -1
- package/dist/core/runtime/run-runtime.d.ts.map +1 -1
- package/dist/core/runtime/run-runtime.js +88 -49
- package/dist/core/runtime/runtime-catalog.d.ts +0 -4
- package/dist/core/runtime/runtime-catalog.d.ts.map +1 -1
- package/dist/core/runtime/runtime-catalog.js +1 -1
- package/dist/core/runtime/stream-rules.d.ts +9 -2
- package/dist/core/runtime/stream-rules.d.ts.map +1 -1
- package/dist/core/runtime/stream-rules.js +19 -5
- package/dist/core/runtime/title.d.ts +13 -1
- package/dist/core/runtime/title.d.ts.map +1 -1
- package/dist/core/runtime/title.js +51 -17
- package/dist/core/runtime/tool-pruning.d.ts.map +1 -1
- package/dist/core/runtime/tool-pruning.js +8 -16
- package/dist/core/session/session-file.d.ts +91 -5
- package/dist/core/session/session-file.d.ts.map +1 -1
- package/dist/core/session/session-file.js +206 -24
- package/dist/core/session/session-loop.d.ts +3 -1
- package/dist/core/session/session-loop.d.ts.map +1 -1
- package/dist/core/session/session-loop.js +8 -4
- package/dist/core/settings/project-settings.d.ts +63 -9
- package/dist/core/settings/project-settings.d.ts.map +1 -1
- package/dist/core/settings/project-settings.js +43 -2
- package/dist/core/settings/workspace-context.js +4 -4
- package/dist/core/store/in-memory-session-store.d.ts.map +1 -1
- package/dist/core/store/in-memory-session-store.js +3 -2
- package/dist/core/store/session-store.contract.test-helper.d.ts +7 -5
- package/dist/core/store/session-store.contract.test-helper.d.ts.map +1 -1
- package/dist/core/store/session-store.contract.test-helper.js +7 -5
- package/dist/core/store/session-store.d.ts +4 -2
- package/dist/core/store/session-store.d.ts.map +1 -1
- package/dist/core/store/session-store.js +4 -2
- package/dist/core/tools/ask-question.d.ts.map +1 -1
- package/dist/core/tools/ask-question.js +51 -5
- package/dist/core/tools/bash-pty.d.ts +13 -4
- package/dist/core/tools/bash-pty.d.ts.map +1 -1
- package/dist/core/tools/bash-pty.js +15 -8
- package/dist/core/tools/bash-sandbox.d.ts +1 -1
- package/dist/core/tools/bash.d.ts +1 -1
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +18 -8
- package/dist/core/tools/browser.d.ts +54 -0
- package/dist/core/tools/browser.d.ts.map +1 -1
- package/dist/core/tools/browser.js +138 -1
- package/dist/core/tools/file-fingerprint.d.ts +24 -0
- package/dist/core/tools/file-fingerprint.d.ts.map +1 -0
- package/dist/core/tools/file-fingerprint.js +61 -0
- package/dist/core/tools/file-tools.d.ts +26 -0
- package/dist/core/tools/file-tools.d.ts.map +1 -1
- package/dist/core/tools/file-tools.js +113 -17
- package/dist/core/tools/index.d.ts +2 -0
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +1 -0
- package/dist/core/tools/mcp-mount.d.ts +1 -1
- package/dist/core/tools/mcp-mount.d.ts.map +1 -1
- package/dist/core/tools/mcp-mount.js +72 -26
- package/dist/core/tools/url-guard.d.ts +15 -0
- package/dist/core/tools/url-guard.d.ts.map +1 -1
- package/dist/core/tools/url-guard.js +54 -14
- package/dist/core/tools/web-ports-std.d.ts.map +1 -1
- package/dist/core/tools/web-ports-std.js +5 -2
- package/dist/modes/print-mode.d.ts +3 -2
- package/dist/modes/print-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +2 -2
- package/dist/modes/shared.d.ts +12 -0
- package/dist/modes/shared.d.ts.map +1 -1
- package/dist/modes/shared.js +23 -6
- package/dist/modes/tui/agent-status.d.ts +20 -0
- package/dist/modes/tui/agent-status.d.ts.map +1 -0
- package/dist/modes/tui/agent-status.js +35 -0
- package/dist/modes/tui/ask-panel.d.ts +166 -0
- package/dist/modes/tui/ask-panel.d.ts.map +1 -0
- package/dist/modes/tui/ask-panel.js +888 -0
- package/dist/modes/tui/pty-console.d.ts +3 -0
- package/dist/modes/tui/pty-console.d.ts.map +1 -1
- package/dist/modes/tui/pty-console.js +19 -0
- package/dist/modes/tui/settings-overlay.d.ts +19 -0
- package/dist/modes/tui/settings-overlay.d.ts.map +1 -1
- package/dist/modes/tui/settings-overlay.js +46 -10
- package/dist/modes/tui/tui-commands-session.d.ts.map +1 -1
- package/dist/modes/tui/tui-commands-session.js +20 -6
- package/dist/modes/tui/tui-commands.d.ts +44 -3
- package/dist/modes/tui/tui-commands.d.ts.map +1 -1
- package/dist/modes/tui/tui-commands.js +380 -6
- package/dist/modes/tui/tui-components.d.ts +10 -0
- package/dist/modes/tui/tui-components.d.ts.map +1 -1
- package/dist/modes/tui/tui-components.js +26 -0
- package/dist/modes/tui/tui-format.d.ts +53 -4
- package/dist/modes/tui/tui-format.d.ts.map +1 -1
- package/dist/modes/tui/tui-format.js +137 -42
- package/dist/modes/tui/tui-frame-provider.d.ts +11 -3
- package/dist/modes/tui/tui-frame-provider.d.ts.map +1 -1
- package/dist/modes/tui/tui-frame-provider.js +14 -5
- package/dist/modes/tui/tui-interactive.d.ts.map +1 -1
- package/dist/modes/tui/tui-interactive.js +14 -0
- package/dist/modes/tui/tui-io.d.ts.map +1 -1
- package/dist/modes/tui/tui-io.js +150 -55
- package/dist/modes/tui/tui-mode.d.ts.map +1 -1
- package/dist/modes/tui/tui-mode.js +428 -23
- package/dist/modes/tui/tui-overlays.d.ts +6 -10
- package/dist/modes/tui/tui-overlays.d.ts.map +1 -1
- package/dist/modes/tui/tui-overlays.js +0 -43
- package/dist/modes/tui/tui-render.d.ts +10 -0
- package/dist/modes/tui/tui-render.d.ts.map +1 -1
- package/dist/modes/tui/tui-render.js +73 -38
- package/dist/modes/tui/tui-seam.d.ts +28 -6
- package/dist/modes/tui/tui-seam.d.ts.map +1 -1
- package/dist/modes/tui/tui-slash.d.ts.map +1 -1
- package/dist/modes/tui/tui-slash.js +3 -1
- package/dist/modes/tui/tui-tool-render.d.ts +14 -1
- package/dist/modes/tui/tui-tool-render.d.ts.map +1 -1
- package/dist/modes/tui/tui-tool-render.js +158 -50
- package/dist/modes/tui/tui-transcript-reconciler.d.ts +4 -0
- package/dist/modes/tui/tui-transcript-reconciler.d.ts.map +1 -1
- package/dist/modes/tui/tui-transcript-reconciler.js +12 -2
- package/dist/modes/tui/view-state.d.ts.map +1 -1
- package/dist/modes/tui/view-state.js +4 -1
- package/dist/protocol/transport.d.ts +10 -0
- package/dist/protocol/transport.d.ts.map +1 -1
- package/dist/protocol/transport.js +1 -0
- 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.
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
* `
|
|
98
|
-
*
|
|
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
|
|
119
|
-
// on shell separators incl. newline and backtick substitution
|
|
120
|
-
//
|
|
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
|
|
124
|
-
|
|
125
|
-
|
|
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":"
|
|
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
|
-
|
|
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":"AAGA,OAAO,EACL,KAAK,gBAAgB,
|
|
1
|
+
{"version":3,"file":"run-runtime.d.ts","sourceRoot":"","sources":["../../../src/core/runtime/run-runtime.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,KAAK,gBAAgB,EAIrB,KAAK,oBAAoB,EAC1B,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,iBAAiB,EAAqB,MAAM,kBAAkB,CAAC;AAMhG,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,iBAAiB,EAItB,KAAK,UAAU,EACf,KAAK,MAAM,EAEX,KAAK,UAAU,EACf,KAAK,YAAY,EAClB,MAAM,aAAa,CAAC;AAQrB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAEpE,OAAO,EAIL,KAAK,YAAY,EAElB,MAAM,iCAAiC,CAAC;AAGzC,OAAO,EAkBL,KAAK,YAAY,EACjB,KAAK,aAAa,EACnB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,KAAK,cAAc,EAGpB,MAAM,uBAAuB,CAAC;AAM/B,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AAuEnE;;;;8DAI8D;AAC9D,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAI1D;AA6BD;;;gFAGgF;AAChF,wBAAsB,iBAAiB,CACrC,YAAY,EAAE,YAAY,EAC1B,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,iBAAiB,CAAC,CAM5B;AAED;;;;;;;;gBAQgB;AAChB,MAAM,WAAW,cACf,SAAQ,IAAI,CAAC,uBAAuB,EAAE,SAAS,GAAG,mBAAmB,GAAG,kBAAkB,CAAC;IAC3F,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB;iDAC6C;IAC7C,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC,2EAA2E;IAC3E,gBAAgB,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;CAC/C;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAC7B;wDACoD;IACpD,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC;IAC7B,+EAA+E;IAC/E,QAAQ,CAAC,eAAe,EAAE,SAAS,cAAc,EAAE,CAAC;IACpD,QAAQ,CAAC,SAAS,EAAE,iBAAiB,CAAC;IACtC,QAAQ,CAAC,aAAa,EAAE,aAAa,GAAG,SAAS,CAAC;IAClD,oEAAoE;IACpE,YAAY,CAAC,GAAG,EAAE,gBAAgB,CAAC,KAAK,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC;IACxD;;sEAEkE;IAClE,eAAe,CAAC,KAAK,EAAE;QACrB,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,CAAC,EAAE,OAAO,CAAC;KAChB,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAClE;;sDAEkD;IAClD,eAAe,IAAI;QACjB,WAAW,EAAE,MAAM,CAAC;QACpB,YAAY,EAAE,MAAM,CAAC;QACrB,eAAe,EAAE,MAAM,CAAC;QACxB,gBAAgB,EAAE,MAAM,CAAC;KAC1B,CAAC;IACF,yDAAyD;IACzD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED;;qDAEqD;AACrD,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,YAAY,EACrB,GAAG,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAe,GAC9D,IAAI,CAON;AAuuBD;;+DAE+D;AAC/D,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,UAAU,CAAC,CA+alF;AAED,YAAY,EAAE,gBAAgB,EAAE,UAAU,EAAE,oBAAoB,EAAE,CAAC"}
|
|
@@ -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
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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({
|
|
215
|
-
|
|
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({
|
|
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 +
|
|
234
|
-
// Skips
|
|
235
|
-
//
|
|
236
|
-
//
|
|
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
|
-
//
|
|
369
|
-
//
|
|
370
|
-
//
|
|
371
|
-
//
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
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
|
-
|
|
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:
|
|
469
|
-
//
|
|
470
|
-
//
|
|
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 = !
|
|
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
|
-
|
|
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;
|
|
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
|
-
|
|
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
|
-
*
|
|
7
|
-
*
|
|
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
|
|
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
|
-
*
|
|
8
|
-
*
|
|
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
|
-
|
|
25
|
-
const
|
|
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
|
-
|
|
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;
|
|
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"}
|