minimal-agent 0.2.0 → 0.3.0
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 +50 -72
- package/package.json +18 -13
- package/plugins/ralph-wiggum/plugin.js +205 -0
- package/plugins/ralph-wiggum/src/goalState.js +260 -0
- package/plugins/ralph-wiggum/src/{sentinels.ts → sentinels.js} +4 -7
- package/plugins/ralph-wiggum/src/stopHookRunner.js +104 -0
- package/plugins/ralph-wiggum/src/verificationGate.js +202 -0
- package/plugins/workflow-runner/{plugin.ts → plugin.js} +20 -26
- package/plugins/workflow-runner/src/expressions.js +369 -0
- package/plugins/workflow-runner/src/index.js +174 -0
- package/plugins/workflow-runner/src/loader.js +183 -0
- package/plugins/workflow-runner/src/runner.js +290 -0
- package/plugins/workflow-runner/src/stepExecutors/assert.js +28 -0
- package/plugins/workflow-runner/src/stepExecutors/llm.js +44 -0
- package/plugins/workflow-runner/src/stepExecutors/skill.js +103 -0
- package/plugins/workflow-runner/src/stepExecutors/{tool.ts → tool.js} +19 -25
- package/plugins/workflow-runner/src/types.js +59 -0
- package/plugins/workflow-runner/src/{workflowState.ts → workflowState.js} +21 -40
- package/src/bootstrap/cwdArg.js +22 -0
- package/src/bootstrap/workingDir.js +31 -0
- package/src/cli/configWizard.js +272 -0
- package/src/cli/print.js +192 -0
- package/src/config/configFile.js +78 -0
- package/src/config.js +118 -0
- package/src/context/compact.js +357 -0
- package/src/context/microCompactLite.js +151 -0
- package/src/context/persistContext.js +109 -0
- package/src/context/reactiveCompact.js +121 -0
- package/src/context/sessionPath.js +58 -0
- package/src/context/snipCompact.js +112 -0
- package/src/context/tokenCounter.js +66 -0
- package/src/llm/client.js +182 -0
- package/src/loop.js +230 -0
- package/src/main.js +116 -0
- package/src/plugin-sdk.js +24 -0
- package/src/plugins/commandRouter.js +169 -0
- package/src/plugins/hookEngine.js +258 -0
- package/src/plugins/pluginApi.js +23 -0
- package/src/plugins/pluginLoader.js +71 -0
- package/src/plugins/pluginRunner.js +65 -0
- package/src/plugins/transcript.js +171 -0
- package/src/prompts/projectInstructions.js +48 -0
- package/src/prompts/skillList.js +126 -0
- package/src/prompts/system.js +155 -0
- package/src/session/runTurn.js +41 -0
- package/src/session/sessionState.js +19 -0
- package/src/tools/bash/bash.js +352 -0
- package/src/tools/bash/semantics.js +85 -0
- package/src/tools/bash/warnings.js +98 -0
- package/src/tools/edit/edit.js +253 -0
- package/src/tools/edit/multi-edit.js +155 -0
- package/src/tools/glob/glob.js +97 -0
- package/src/tools/grep/grep.js +185 -0
- package/src/tools/grep/rgPath.js +173 -0
- package/src/tools/index.js +94 -0
- package/src/tools/read/read.js +209 -0
- package/src/tools/shared/fileState.js +61 -0
- package/src/tools/shared/fileUtils.js +281 -0
- package/src/tools/shared/schemas.js +16 -0
- package/src/tools/types.js +21 -0
- package/src/tools/webbrowser/browser.js +55 -0
- package/src/tools/webbrowser/webbrowser.js +194 -0
- package/src/tools/webfetch/preapproved.js +267 -0
- package/src/tools/webfetch/webfetch.js +317 -0
- package/src/tools/websearch/websearch.js +161 -0
- package/src/tools/write/write.js +125 -0
- package/src/types/turndown.d.ts +23 -0
- package/src/types.js +16 -0
- package/src/ui/App.js +37 -0
- package/src/ui/InputBox.js +240 -0
- package/src/ui/MessageList.js +28 -0
- package/src/ui/Root.js +70 -0
- package/src/ui/StatusLine.js +41 -0
- package/src/ui/ToolStatus.js +11 -0
- package/src/ui/hooks/useChat.js +234 -0
- package/src/ui/hooks/usePasteHandler.js +137 -0
- package/src/ui/hooks/useTextBuffer.js +55 -0
- package/src/ui/hooks/useTokenUsage.js +30 -0
- package/src/ui/textBuffer.js +217 -0
- package/src/utils/packageRoot.js +37 -0
- package/src/utils/resourcePaths.js +49 -0
- package/src/utils/zodToJson.js +29 -0
- package/dist/main.js +0 -5315
- package/plugins/ralph-wiggum/plugin.ts +0 -275
- package/plugins/ralph-wiggum/scripts/setup-ralph-loop.sh +0 -203
- package/plugins/ralph-wiggum/src/goalState.ts +0 -310
- package/plugins/ralph-wiggum/src/stopHookRunner.ts +0 -136
- package/plugins/ralph-wiggum/src/verificationGate.ts +0 -252
- package/plugins/ralph-wiggum/test/goalState.test.ts +0 -410
- package/plugins/ralph-wiggum/test/verificationGate.test.ts +0 -122
- package/plugins/workflow-runner/src/expressions.ts +0 -371
- package/plugins/workflow-runner/src/index.ts +0 -194
- package/plugins/workflow-runner/src/loader.ts +0 -193
- package/plugins/workflow-runner/src/runner.ts +0 -313
- package/plugins/workflow-runner/src/stepExecutors/assert.ts +0 -30
- package/plugins/workflow-runner/src/stepExecutors/llm.ts +0 -54
- package/plugins/workflow-runner/src/stepExecutors/skill.ts +0 -115
- package/plugins/workflow-runner/src/types.ts +0 -183
- package/plugins/workflow-runner/test/cli.e2e.test.ts +0 -114
- package/plugins/workflow-runner/test/e2e.test.ts +0 -268
- package/plugins/workflow-runner/test/expressions.test.ts +0 -140
- package/plugins/workflow-runner/test/fixtures/cli-e2e.yaml +0 -27
- package/plugins/workflow-runner/test/fixtures/hello-workflow.yaml +0 -49
- package/plugins/workflow-runner/test/graceful.test.ts +0 -139
- package/plugins/workflow-runner/test/loader.test.ts +0 -216
- package/plugins/workflow-runner/test/pluginRunner.isolation.test.ts +0 -230
- package/plugins/workflow-runner/test/runner.test.ts +0 -511
|
@@ -1,230 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ============================================================
|
|
3
|
-
* test/workflows/pluginRunner.isolation.test.ts
|
|
4
|
-
* ------------------------------------------------------------
|
|
5
|
-
* V1 红线:默认 T-A-O-R 不退化。无论 workflows/ 是缺失 / 存在 / 全坏,
|
|
6
|
-
* 非 / 输入 100% 走 runQuery,零 workflow 副作用。
|
|
7
|
-
*
|
|
8
|
-
* 断言重点:
|
|
9
|
-
* - 非 / 输入下 runQuery 被调用一次且参数(input + history)原样透传
|
|
10
|
-
* - 非 / 输入下 pluginCache 保持空(说明 discoverPlugins 没被调)
|
|
11
|
-
* - 没有任何 workflow_* / plugin_* 事件
|
|
12
|
-
* - 即使 workflows/ 满是坏 yaml,也不影响以上结论
|
|
13
|
-
*
|
|
14
|
-
* 这是"在不 / 启动的情况下原始 T-A-O-R 要好使"硬约束的回归门。
|
|
15
|
-
* ============================================================
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
import { afterEach, beforeEach, describe, expect, it, mock } from 'bun:test';
|
|
19
|
-
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
|
|
20
|
-
import { tmpdir } from 'node:os';
|
|
21
|
-
import { join } from 'node:path';
|
|
22
|
-
|
|
23
|
-
import { _resetWorkingDir } from '../../../src/bootstrap/workingDir.ts';
|
|
24
|
-
import {
|
|
25
|
-
_resetPluginCache,
|
|
26
|
-
discoverPlugins,
|
|
27
|
-
} from '../../../src/plugins/commandRouter.ts';
|
|
28
|
-
import type { LoopEvent, Message, Provider } from '../../../src/types.ts';
|
|
29
|
-
import type { PluginEvent } from '../../../src/plugins/pluginApi.ts';
|
|
30
|
-
|
|
31
|
-
type WfOrLoop = LoopEvent | PluginEvent;
|
|
32
|
-
|
|
33
|
-
const fakeProvider: Provider = {
|
|
34
|
-
name: 'test',
|
|
35
|
-
baseURL: '',
|
|
36
|
-
apiKey: '',
|
|
37
|
-
model: 'm',
|
|
38
|
-
contextWindow: 128000,
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
interface RunQueryCall {
|
|
42
|
-
input: string;
|
|
43
|
-
historyLen: number;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
async function collect(
|
|
47
|
-
gen: AsyncGenerator<WfOrLoop, void, void>,
|
|
48
|
-
): Promise<WfOrLoop[]> {
|
|
49
|
-
const out: WfOrLoop[] = [];
|
|
50
|
-
for await (const ev of gen) out.push(ev);
|
|
51
|
-
return out;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
describe('pluginRunner V1 红线:非 / 输入不被 workflow 污染', () => {
|
|
55
|
-
let tmp: string;
|
|
56
|
-
const calls: RunQueryCall[] = [];
|
|
57
|
-
|
|
58
|
-
beforeEach(() => {
|
|
59
|
-
tmp = mkdtempSync(join(tmpdir(), 'wf-iso-'));
|
|
60
|
-
process.env.MINIMAL_AGENT_CWD = tmp;
|
|
61
|
-
_resetWorkingDir();
|
|
62
|
-
_resetPluginCache();
|
|
63
|
-
calls.length = 0;
|
|
64
|
-
|
|
65
|
-
mock.module('../../../src/loop.ts', () => ({
|
|
66
|
-
runQuery: async function* (input: string, opts: { history: Message[] }) {
|
|
67
|
-
calls.push({ input, historyLen: opts.history.length });
|
|
68
|
-
yield { type: 'text', delta: 'fake-reply' };
|
|
69
|
-
yield { type: 'turn_done' };
|
|
70
|
-
},
|
|
71
|
-
}));
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
afterEach(() => {
|
|
75
|
-
delete process.env.MINIMAL_AGENT_CWD;
|
|
76
|
-
_resetWorkingDir();
|
|
77
|
-
_resetPluginCache();
|
|
78
|
-
rmSync(tmp, { recursive: true, force: true });
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
async function run(input: string, history: Message[] = []) {
|
|
82
|
-
const { runWithPlugins } = await import('../../../src/plugins/pluginRunner.ts');
|
|
83
|
-
return collect(
|
|
84
|
-
runWithPlugins(input, {
|
|
85
|
-
provider: fakeProvider,
|
|
86
|
-
history,
|
|
87
|
-
}),
|
|
88
|
-
);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
it('workflows/ 缺失 + 非 / 输入 → 走 runQuery,无 workflow 事件', async () => {
|
|
92
|
-
const events = await run('hello world');
|
|
93
|
-
expect(calls.length).toBe(1);
|
|
94
|
-
expect(calls[0].input).toBe('hello world');
|
|
95
|
-
|
|
96
|
-
const noisyTypes = events
|
|
97
|
-
.map((e) => e.type)
|
|
98
|
-
.filter(
|
|
99
|
-
(t) => t.startsWith('workflow_') || t === 'plugin_progress',
|
|
100
|
-
);
|
|
101
|
-
expect(noisyTypes).toEqual([]);
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
it('workflows/ 存在合法 yaml + 非 / 输入 → 仍只走 runQuery(不发现 workflow,不调 listWorkflows)', async () => {
|
|
105
|
-
mkdirSync(join(tmp, 'workflows'), { recursive: true });
|
|
106
|
-
writeFileSync(
|
|
107
|
-
join(tmp, 'workflows', 'pretty.yaml'),
|
|
108
|
-
`name: pretty
|
|
109
|
-
description: x
|
|
110
|
-
steps:
|
|
111
|
-
- id: a
|
|
112
|
-
type: assert
|
|
113
|
-
condition: 'true'
|
|
114
|
-
`,
|
|
115
|
-
'utf8',
|
|
116
|
-
);
|
|
117
|
-
|
|
118
|
-
const events = await run('just a chat please');
|
|
119
|
-
expect(calls.length).toBe(1);
|
|
120
|
-
expect(events.some((e) => e.type.startsWith('workflow_'))).toBe(false);
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
it('workflows/ 全是坏 yaml + 非 / 输入 → 仍只走 runQuery', async () => {
|
|
124
|
-
mkdirSync(join(tmp, 'workflows'), { recursive: true });
|
|
125
|
-
writeFileSync(join(tmp, 'workflows', 'broken.yaml'), '@@@\nbad:::\n', 'utf8');
|
|
126
|
-
|
|
127
|
-
const events = await run('hi there');
|
|
128
|
-
expect(calls.length).toBe(1);
|
|
129
|
-
expect(calls[0].input).toBe('hi there');
|
|
130
|
-
expect(events.some((e) => e.type.startsWith('workflow_'))).toBe(false);
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
it('非 / 输入 history 长度原样透传给 runQuery', async () => {
|
|
134
|
-
const history: Message[] = [
|
|
135
|
-
{ role: 'system', content: 's' },
|
|
136
|
-
{ role: 'user', content: 'u1' },
|
|
137
|
-
{ role: 'assistant', content: 'a1' },
|
|
138
|
-
];
|
|
139
|
-
await run('next thing', history);
|
|
140
|
-
expect(calls[0].historyLen).toBe(3);
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
it('非 / 输入下 pluginCache 不被填充(discoverPlugins 未被触发)', async () => {
|
|
144
|
-
await run('plain input no slash');
|
|
145
|
-
// 重置过 cache;非 / 输入应该完全跳过 discoverPlugins
|
|
146
|
-
const plugins = Array.from((await stealCache()).values());
|
|
147
|
-
expect(plugins.length).toBe(0);
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
it('/ 输入下 pluginCache 才被填充(对照组)', async () => {
|
|
151
|
-
await run('/nonexistent-cmd');
|
|
152
|
-
const plugins = Array.from((await stealCache()).values());
|
|
153
|
-
// 走过 discoverPlugins,应该至少能发现 ralph-wiggum(workflow 已降为内置能力)
|
|
154
|
-
expect(plugins.length).toBeGreaterThan(0);
|
|
155
|
-
});
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* commandRouter 的 pluginCache 是模块私有变量,没有 getter。
|
|
160
|
-
* 用一个 hack:调用 discoverPlugins(),但只看返回 —— 如果之前没被填,
|
|
161
|
-
* 这一调用结束时会把所有插件加进 cache。所以我们改为读副作用更直接的接口:
|
|
162
|
-
* listAvailableCommands()。如果没 discoverPlugins 过,listAvailableCommands
|
|
163
|
-
* 会返回空数组。
|
|
164
|
-
*/
|
|
165
|
-
async function stealCache(): Promise<Map<string, unknown>> {
|
|
166
|
-
const { listAvailableCommands } = await import('../../../src/plugins/commandRouter.ts');
|
|
167
|
-
const cmds = listAvailableCommands();
|
|
168
|
-
const m = new Map<string, unknown>();
|
|
169
|
-
for (const c of cmds) m.set(c.plugin, c);
|
|
170
|
-
return m;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
describe('pluginRunner V2 红线:/workflow 命令通过 plugin.ts.runCommand 接管', () => {
|
|
174
|
-
let tmp: string;
|
|
175
|
-
const calls: RunQueryCall[] = [];
|
|
176
|
-
|
|
177
|
-
beforeEach(() => {
|
|
178
|
-
tmp = mkdtempSync(join(tmpdir(), 'wf-iso2-'));
|
|
179
|
-
process.env.MINIMAL_AGENT_CWD = tmp;
|
|
180
|
-
_resetWorkingDir();
|
|
181
|
-
_resetPluginCache();
|
|
182
|
-
calls.length = 0;
|
|
183
|
-
|
|
184
|
-
mock.module('../../../src/loop.ts', () => ({
|
|
185
|
-
runQuery: async function* (input: string, opts: { history: Message[] }) {
|
|
186
|
-
calls.push({ input, historyLen: opts.history.length });
|
|
187
|
-
yield { type: 'text', delta: 'should-not-be-here' };
|
|
188
|
-
yield { type: 'turn_done' };
|
|
189
|
-
},
|
|
190
|
-
}));
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
afterEach(() => {
|
|
194
|
-
delete process.env.MINIMAL_AGENT_CWD;
|
|
195
|
-
_resetWorkingDir();
|
|
196
|
-
_resetPluginCache();
|
|
197
|
-
rmSync(tmp, { recursive: true, force: true });
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
it('/workflows 由插件 plugin.ts 接管 → 不走 runQuery,yield 列表 text', async () => {
|
|
201
|
-
const { runWithPlugins } = await import('../../../src/plugins/pluginRunner.ts');
|
|
202
|
-
const events = await collect(
|
|
203
|
-
runWithPlugins('/workflows', { provider: fakeProvider, history: [] }),
|
|
204
|
-
);
|
|
205
|
-
expect(calls.length).toBe(0);
|
|
206
|
-
expect(events.some((e) => e.type === 'text')).toBe(true);
|
|
207
|
-
expect(events.some((e) => e.type === 'turn_done')).toBe(true);
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
it('/workflow book-review-short 缺必填 input → yield error,不走 runQuery', async () => {
|
|
211
|
-
const { runWithPlugins } = await import('../../../src/plugins/pluginRunner.ts');
|
|
212
|
-
const events = await collect(
|
|
213
|
-
runWithPlugins('/workflow book-review-short', {
|
|
214
|
-
provider: fakeProvider,
|
|
215
|
-
history: [],
|
|
216
|
-
}),
|
|
217
|
-
);
|
|
218
|
-
expect(calls.length).toBe(0);
|
|
219
|
-
expect(events.some((e) => e.type === 'error')).toBe(true);
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
it('/workflow 以插件命令形式出现在 listAvailableCommands', async () => {
|
|
223
|
-
await discoverPlugins();
|
|
224
|
-
const { listAvailableCommands } = await import('../../../src/plugins/commandRouter.ts');
|
|
225
|
-
const cmds = listAvailableCommands();
|
|
226
|
-
const wf = cmds.find((c) => c.command === '/workflow');
|
|
227
|
-
expect(wf).toBeDefined();
|
|
228
|
-
expect(wf?.plugin).toBe('workflow-runner');
|
|
229
|
-
});
|
|
230
|
-
});
|