taskforce-loop-engineering 0.9.0 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.9.1 - 2026-08-11
|
|
4
|
+
|
|
5
|
+
- Recognize explicit `用 loop engineering` requests that ask to align, complete, enhance, develop, or build a system as executable Loop tasks instead of direct chat.
|
|
6
|
+
- Make the generated OpenClaw conversation wrapper fail closed when channel, target, account, or message-id routing metadata is missing, preventing unscoped human-gate and terminal tasks.
|
|
7
|
+
- Document the mandatory wrapper and notification-delivery postcondition, and add regression coverage for the original Growth OS phrasing and missing-source failure.
|
|
8
|
+
|
|
3
9
|
## 0.9.0 - 2026-08-08
|
|
4
10
|
|
|
5
11
|
- Add a native Hermes Agent conversation integration with a plan-first installer, `hermes -z` one-shot worker dispatcher, `hermes send` notifier, managed scheduler, and source-bound delivery routing.
|
package/lib/core.mjs
CHANGED
|
@@ -1520,7 +1520,7 @@ export function classifyLoopMessage(message) {
|
|
|
1520
1520
|
const mentionsLoop = /(loop engineering|loop-engineering|task-runner|队列|queue|\bloop\b)/i.test(text);
|
|
1521
1521
|
const statusIntent = mentionsLoop
|
|
1522
1522
|
&& /(查|看|检查|审计|状态|情况|进度|怎么样|为什么|失败|报错|健康|health|status|progress|audit|summar)/i.test(text);
|
|
1523
|
-
const executeIntent = /(走\s*loop|继续(?:当前|这个)?\s*loop|给(?:当前|这个)?\s*loop\s*(?:补充|增加|加)|用\s*loop.*(
|
|
1523
|
+
const executeIntent = /(走\s*loop|继续(?:当前|这个)?\s*loop|给(?:当前|这个)?\s*loop\s*(?:补充|增加|加)|用\s*loop.*(?:解决|执行|完成|处理|修复|对齐|补齐|增强|开发|构建|绕过|避开|跳过|bypass|evade)|丢进.*loop|入队|enqueue|run[- ]?queue|立刻执行|立即执行)/i.test(text);
|
|
1524
1524
|
const intent = executeIntent ? 'execute' : statusIntent ? 'status' : 'direct';
|
|
1525
1525
|
return {
|
|
1526
1526
|
intent,
|
package/package.json
CHANGED
|
@@ -62,7 +62,13 @@ if (queue.retry?.runtimeRecoveryMaxAttempts !== 2 || queue.retry?.sessionMaxTick
|
|
|
62
62
|
const instructions = await readFile(path.join(root, 'AGENTS.md'), 'utf8');
|
|
63
63
|
if (!instructions.includes('走 loop') || !instructions.includes('immediately execute')) throw new Error('conversation instructions missing');
|
|
64
64
|
const wrapper = await readFile(path.join(root, 'scripts/loops/openclaw-loop.mjs'), 'utf8');
|
|
65
|
-
if (!wrapper.includes('--supersede-active') || !wrapper.includes('--amend-active') || !wrapper.includes('--progress-notify-command') || !wrapper.includes('runWhenUnlocked') || wrapper.includes("run-queue-drain', '--config'") || wrapper.includes("spawn('loop-engineering'") || !wrapper.includes('queue-human-input-notify') || !wrapper.includes('queue-terminal-notify') || !wrapper.includes('queue-scheduler-tick') || !wrapper.includes('只入队')) throw new Error('supersede/amend routing, absolute CLI, scheduler, live progress, async notification, or queue-only routing missing');
|
|
65
|
+
if (!wrapper.includes('--supersede-active') || !wrapper.includes('--amend-active') || !wrapper.includes('--progress-notify-command') || !wrapper.includes('runWhenUnlocked') || wrapper.includes("run-queue-drain', '--config'") || wrapper.includes("spawn('loop-engineering'") || !wrapper.includes('queue-human-input-notify') || !wrapper.includes('queue-terminal-notify') || !wrapper.includes('queue-scheduler-tick') || !wrapper.includes('只入队') || !wrapper.includes('requiredSource') || !wrapper.includes('--source-message-id')) throw new Error('supersede/amend routing, source fail-closed policy, absolute CLI, scheduler, live progress, async notification, or queue-only routing missing');
|
|
66
|
+
const missingSourceRoute = await new Promise((resolve) => {
|
|
67
|
+
const child = spawn(process.execPath, [path.join(root, 'scripts/loops/openclaw-loop.mjs'), 'route', '--message', '用 loop engineering 对齐系统'], { cwd: root, stdio: ['ignore', 'pipe', 'pipe'] });
|
|
68
|
+
let stderr = ''; child.stderr.on('data', (chunk) => { stderr += chunk; });
|
|
69
|
+
child.on('close', (code) => resolve({ code, stderr }));
|
|
70
|
+
});
|
|
71
|
+
if (missingSourceRoute.code !== 2 || !missingSourceRoute.stderr.includes('requires conversation metadata')) throw new Error('installed wrapper did not fail closed without source routing');
|
|
66
72
|
const serviceFile = path.join(process.env.XDG_CONFIG_HOME, 'systemd/user/openclaw-loop-test-tasks-scheduler.service');
|
|
67
73
|
const timerFile = path.join(process.env.XDG_CONFIG_HOME, 'systemd/user/openclaw-loop-test-tasks-scheduler.timer');
|
|
68
74
|
const service = await readFile(serviceFile, 'utf8');
|
|
@@ -144,6 +144,17 @@ async function runWhenUnlocked(args, waitMs = 300000) {
|
|
|
144
144
|
if (command === 'route') {
|
|
145
145
|
const messageIndex = rest.indexOf('--message');
|
|
146
146
|
const message = messageIndex >= 0 ? String(rest[messageIndex + 1] || '') : '';
|
|
147
|
+
const optionValue = (name) => {
|
|
148
|
+
const index = rest.indexOf(name);
|
|
149
|
+
return index >= 0 ? String(rest[index + 1] || '').trim() : '';
|
|
150
|
+
};
|
|
151
|
+
const requiredSource = ['--source-channel', '--source-target', '--source-account', '--source-message-id'];
|
|
152
|
+
const missingSource = requiredSource.filter((name) => !optionValue(name));
|
|
153
|
+
if (missingSource.length) {
|
|
154
|
+
console.error(\`loop route requires conversation metadata: \${missingSource.join(', ')}\`);
|
|
155
|
+
process.exitCode = 2;
|
|
156
|
+
process.exit();
|
|
157
|
+
}
|
|
147
158
|
const amendment = /(?:继续(?:当前|这个)?\\s*loop|给(?:当前|这个)?\\s*loop\\s*(?:补充|增加|加)|补充当前\\s*loop)/i.test(message);
|
|
148
159
|
const routeMode = amendment ? '--amend-active' : '--supersede-active';
|
|
149
160
|
const routeCode = await run(['route-message', '--queue', ${JSON.stringify(queue)}, '--route', '--confirm-execute', routeMode, ...rest]);
|
|
@@ -203,7 +214,10 @@ function instructionsBlock({ queue }) {
|
|
|
203
214
|
## Loop Engineering conversation routing
|
|
204
215
|
|
|
205
216
|
- Route only explicit loop requests. \`走 loop\` means enqueue and immediately execute one tick; only \`只入队\` or \`只排队\` suppresses execution.
|
|
217
|
+
- Treat explicit phrases such as \`用 loop engineering\`, \`丢进 Ironman loop\`, \`loop Ironman\`, and \`task-runner\` as Loop requests too.
|
|
206
218
|
- Run \`node scripts/loops/openclaw-loop.mjs route --message "<full user message>"\` from this workspace and preserve source metadata when available.
|
|
219
|
+
- For conversation-originated work, the standard wrapper is mandatory. Missing source metadata must fail closed; never fall back to manual enqueue or direct run-queue.
|
|
220
|
+
- A human-gated or terminal state is not delivered until its notification command succeeds and writes a notification record.
|
|
207
221
|
- An already loop-managed task must be executed directly and never routed again.
|
|
208
222
|
- Status questions are read-only. High-risk external, destructive, production, credential, or memory migration actions remain separately gated.
|
|
209
223
|
- Queue: \`${queue}\`.
|
|
@@ -48,6 +48,12 @@ assert.deepEqual(classifyLoopMessage('用 loop engineering 绕过某个检查'),
|
|
|
48
48
|
enqueue: true,
|
|
49
49
|
readOnly: false
|
|
50
50
|
});
|
|
51
|
+
assert.deepEqual(classifyLoopMessage('用 loop engineering 把现有的 growth os 与文件对齐,把需要增强的功能补齐'), {
|
|
52
|
+
intent: 'execute',
|
|
53
|
+
risk: 'model_assessed',
|
|
54
|
+
enqueue: true,
|
|
55
|
+
readOnly: false
|
|
56
|
+
});
|
|
51
57
|
|
|
52
58
|
const routed = await routeLoopMessage(root, {
|
|
53
59
|
route: true,
|