chainlesschain 0.47.0 → 0.47.2
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 +6 -5
- package/package.json +1 -1
- package/src/lib/session-hooks.js +71 -9
- package/src/repl/agent-repl.js +29 -6
package/README.md
CHANGED
|
@@ -198,7 +198,7 @@ chainlesschain a --model llama3 # Short alias
|
|
|
198
198
|
chainlesschain agent --provider openai --api-key sk-...
|
|
199
199
|
```
|
|
200
200
|
|
|
201
|
-
Built-in tools (
|
|
201
|
+
Built-in tools (16): `read_file`, `write_file`, `edit_file`, `edit_file_hashed`, `run_shell`, `git`, `search_files`, `search_sessions`, `list_dir`, `run_skill`, `list_skills`, `run_code`, `spawn_sub_agent`, `web_fetch`, `todo_write`, `ask_user_question`
|
|
202
202
|
|
|
203
203
|
Agent slash commands: `/plan` (plan mode), `/plan interactive <request>` (LLM-driven planning with skill recommendations), `/model`, `/provider`, `/clear`, `/compact`, `/task`, `/session`, `/stats`, `/auto` (autonomous agent), `/cowork` (multi-agent collaboration), `/sub-agents` (show active/completed sub-agents)
|
|
204
204
|
|
|
@@ -623,11 +623,11 @@ chainlesschain cowork analyze <path> # Code analysis (style/kn
|
|
|
623
623
|
chainlesschain cowork status # Show cowork status
|
|
624
624
|
```
|
|
625
625
|
|
|
626
|
-
### Web Cowork: Daily Task Collaboration (v0.
|
|
626
|
+
### Web Cowork: Daily Task Collaboration (v0.46.0)
|
|
627
627
|
|
|
628
|
-
Web-based daily task collaboration via the `/#/cowork` page. Powered by SubAgentContext + agentLoop, with
|
|
628
|
+
Web-based daily task collaboration via the `/#/cowork` page. Powered by SubAgentContext + agentLoop, with 11 task templates covering common daily tasks. Prioritizes open-source CLI tools (ffmpeg, pandoc, ImageMagick, Tesseract, etc.) via cli-anything bridge, with automatic tool installation (winget > choco > pip > npm).
|
|
629
629
|
|
|
630
|
-
**
|
|
630
|
+
**11 Task Templates**:
|
|
631
631
|
|
|
632
632
|
| Template ID | Name | Category | Accepts Files |
|
|
633
633
|
| ----------------- | ------------ | --------- | ------------- |
|
|
@@ -637,6 +637,7 @@ Web-based daily task collaboration via the `/#/cowork` page. Powered by SubAgent
|
|
|
637
637
|
| `web-research` | 网络调研 | research | ❌ |
|
|
638
638
|
| `image-process` | 图片处理 | image | ✅ |
|
|
639
639
|
| `code-helper` | 代码辅助 | code | ✅ |
|
|
640
|
+
| `code_review` | 代码评审 | code | ✅ |
|
|
640
641
|
| `system-admin` | 系统管理 | system | ❌ |
|
|
641
642
|
| `file-organize` | 文件整理 | file | ✅ |
|
|
642
643
|
| `network-tools` | 网络工具 | network | ❌ |
|
|
@@ -652,7 +653,7 @@ Web-based daily task collaboration via the `/#/cowork` page. Powered by SubAgent
|
|
|
652
653
|
← { type: "cowork:done", taskId, status, templateName, summary, artifacts, toolsUsed, iterationCount }
|
|
653
654
|
```
|
|
654
655
|
|
|
655
|
-
**Key files**: `src/lib/cowork-task-templates.js` (
|
|
656
|
+
**Key files**: `src/lib/cowork-task-templates.js` (11 templates), `src/lib/cowork-task-runner.js` (pipeline), `src/gateways/ws/action-protocol.js` (WS handler). **Tests**: 79+ (57 unit + 11 integration + 11 E2E).
|
|
656
657
|
|
|
657
658
|
### Cowork Workflow Editor (v0.47.0, N1)
|
|
658
659
|
|
package/package.json
CHANGED
package/src/lib/session-hooks.js
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Session-level hook firing helpers — the "三件套" that complements
|
|
2
|
+
* Session-level hook firing helpers — the "三件套 +1" that complements
|
|
3
3
|
* the tool-level PreToolUse/PostToolUse hooks already wired in
|
|
4
4
|
* `runtime/agent-core.js`.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
* actually fired anywhere in the CLI. This module is the canonical
|
|
8
|
-
* fire site, consumed by `repl/agent-repl.js`.
|
|
6
|
+
* Four session-level events are fired from `repl/agent-repl.js`:
|
|
9
7
|
*
|
|
10
|
-
* - SessionStart
|
|
11
|
-
* - UserPromptSubmit
|
|
12
|
-
* -
|
|
8
|
+
* - SessionStart — once, after sessionId is established
|
|
9
|
+
* - UserPromptSubmit — per user line, before agentLoop()
|
|
10
|
+
* - AssistantResponse — per agent reply, after agentLoop() returns
|
|
11
|
+
* - SessionEnd — once, on rl.close() before shutdown
|
|
13
12
|
*
|
|
14
13
|
* Semantics (matches existing PreToolUse convention):
|
|
15
|
-
* - Fire-and-forget: hook failures NEVER break the host flow
|
|
16
|
-
* -
|
|
14
|
+
* - Fire-and-forget by default: hook failures NEVER break the host flow
|
|
15
|
+
* - `fireSessionHookWithRewrite` opt-in: lets a UserPromptSubmit hook
|
|
16
|
+
* return `{ rewrittenPrompt }` or `{ abort: true }` via stdout JSON
|
|
17
17
|
* - No-op when hookDb is null (REPL without DB)
|
|
18
18
|
*/
|
|
19
19
|
|
|
@@ -26,6 +26,7 @@ import { executeHooks, HookEvents } from "./hook-manager.js";
|
|
|
26
26
|
export const SESSION_HOOK_EVENTS = Object.freeze([
|
|
27
27
|
HookEvents.SessionStart,
|
|
28
28
|
HookEvents.UserPromptSubmit,
|
|
29
|
+
HookEvents.AssistantResponse,
|
|
29
30
|
HookEvents.SessionEnd,
|
|
30
31
|
]);
|
|
31
32
|
|
|
@@ -59,3 +60,64 @@ export async function fireSessionHook(hookDb, eventName, context = {}) {
|
|
|
59
60
|
return [];
|
|
60
61
|
}
|
|
61
62
|
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Fire UserPromptSubmit with rewrite/abort support.
|
|
66
|
+
*
|
|
67
|
+
* A hook may control the prompt by emitting a single JSON line to stdout:
|
|
68
|
+
* {"rewrittenPrompt": "..."} — replace the user prompt
|
|
69
|
+
* {"abort": true, "reason": "..."} — skip agentLoop entirely
|
|
70
|
+
*
|
|
71
|
+
* First matching directive wins (priority order from executeHooks).
|
|
72
|
+
* Malformed JSON / no directive → no change (pure observation).
|
|
73
|
+
*
|
|
74
|
+
* @returns {Promise<{prompt: string, abort: boolean, reason?: string, results: Array}>}
|
|
75
|
+
*/
|
|
76
|
+
export async function fireUserPromptSubmit(
|
|
77
|
+
hookDb,
|
|
78
|
+
originalPrompt,
|
|
79
|
+
context = {},
|
|
80
|
+
) {
|
|
81
|
+
const results = await fireSessionHook(hookDb, HookEvents.UserPromptSubmit, {
|
|
82
|
+
...context,
|
|
83
|
+
prompt: originalPrompt,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
let prompt = originalPrompt;
|
|
87
|
+
let abort = false;
|
|
88
|
+
let reason;
|
|
89
|
+
|
|
90
|
+
for (const r of results) {
|
|
91
|
+
if (!r || !r.success) continue;
|
|
92
|
+
const directive = extractDirective(r);
|
|
93
|
+
if (!directive) continue;
|
|
94
|
+
if (directive.abort) {
|
|
95
|
+
abort = true;
|
|
96
|
+
reason = directive.reason;
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
if (
|
|
100
|
+
typeof directive.rewrittenPrompt === "string" &&
|
|
101
|
+
directive.rewrittenPrompt.trim()
|
|
102
|
+
) {
|
|
103
|
+
prompt = directive.rewrittenPrompt;
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return { prompt, abort, reason, results };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function extractDirective(result) {
|
|
112
|
+
const raw = result.stdout ?? result.output ?? result.result;
|
|
113
|
+
if (raw == null) return null;
|
|
114
|
+
if (typeof raw === "object") return raw;
|
|
115
|
+
if (typeof raw !== "string") return null;
|
|
116
|
+
const trimmed = raw.trim();
|
|
117
|
+
if (!trimmed.startsWith("{")) return null;
|
|
118
|
+
try {
|
|
119
|
+
return JSON.parse(trimmed);
|
|
120
|
+
} catch {
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
}
|
package/src/repl/agent-repl.js
CHANGED
|
@@ -50,7 +50,7 @@ import { CLIAutonomousAgent, GoalStatus } from "../lib/autonomous-agent.js";
|
|
|
50
50
|
import { PromptCompressor } from "../harness/prompt-compressor.js";
|
|
51
51
|
import { feature } from "../lib/feature-flags.js";
|
|
52
52
|
import { recordCompressionMetric } from "../lib/compression-telemetry.js";
|
|
53
|
-
import { fireSessionHook } from "../lib/session-hooks.js";
|
|
53
|
+
import { fireSessionHook, fireUserPromptSubmit } from "../lib/session-hooks.js";
|
|
54
54
|
import { HookEvents } from "../lib/hook-manager.js";
|
|
55
55
|
import { IterationBudget } from "../lib/iteration-budget.js";
|
|
56
56
|
import {
|
|
@@ -1161,15 +1161,29 @@ export async function startAgentRepl(options = {}) {
|
|
|
1161
1161
|
return;
|
|
1162
1162
|
}
|
|
1163
1163
|
|
|
1164
|
-
// Fire UserPromptSubmit hook
|
|
1165
|
-
|
|
1164
|
+
// Fire UserPromptSubmit hook with rewrite/abort support.
|
|
1165
|
+
// Hooks may emit {"rewrittenPrompt": "..."} or {"abort": true, "reason": "..."}
|
|
1166
|
+
// via stdout JSON. Failures fall through to the original prompt.
|
|
1167
|
+
const promptDirective = await fireUserPromptSubmit(_hookDb, trimmed, {
|
|
1166
1168
|
sessionId,
|
|
1167
|
-
prompt: trimmed,
|
|
1168
1169
|
messageCount: messages.length,
|
|
1169
1170
|
});
|
|
1171
|
+
if (promptDirective.abort) {
|
|
1172
|
+
logger.info(
|
|
1173
|
+
chalk.yellow(
|
|
1174
|
+
`[hook] prompt aborted${promptDirective.reason ? `: ${promptDirective.reason}` : ""}`,
|
|
1175
|
+
),
|
|
1176
|
+
);
|
|
1177
|
+
prompt();
|
|
1178
|
+
return;
|
|
1179
|
+
}
|
|
1180
|
+
const effectivePrompt = promptDirective.prompt;
|
|
1181
|
+
if (effectivePrompt !== trimmed) {
|
|
1182
|
+
logger.verbose(`[hook] prompt rewritten by UserPromptSubmit hook`);
|
|
1183
|
+
}
|
|
1170
1184
|
|
|
1171
1185
|
// Add user message
|
|
1172
|
-
messages.push({ role: "user", content:
|
|
1186
|
+
messages.push({ role: "user", content: effectivePrompt });
|
|
1173
1187
|
|
|
1174
1188
|
// Slot-filling: detect intent and fill missing parameters interactively
|
|
1175
1189
|
try {
|
|
@@ -1236,12 +1250,21 @@ export async function startAgentRepl(options = {}) {
|
|
|
1236
1250
|
process.stdout.write("\n");
|
|
1237
1251
|
}
|
|
1238
1252
|
|
|
1253
|
+
// Fire AssistantResponse hook (fire-and-forget; observational only)
|
|
1254
|
+
await fireSessionHook(_hookDb, HookEvents.AssistantResponse, {
|
|
1255
|
+
sessionId,
|
|
1256
|
+
response: response || "",
|
|
1257
|
+
messageCount: messages.length,
|
|
1258
|
+
provider,
|
|
1259
|
+
model: activeModel,
|
|
1260
|
+
});
|
|
1261
|
+
|
|
1239
1262
|
// Auto-save session
|
|
1240
1263
|
if (sessionId) {
|
|
1241
1264
|
try {
|
|
1242
1265
|
if (useJsonl) {
|
|
1243
1266
|
// Append incremental events (user + assistant)
|
|
1244
|
-
appendUserMessage(sessionId,
|
|
1267
|
+
appendUserMessage(sessionId, effectivePrompt);
|
|
1245
1268
|
if (response) {
|
|
1246
1269
|
appendAssistantMessage(sessionId, response);
|
|
1247
1270
|
}
|