cueclaw 0.1.2 → 0.1.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 +203 -21
- package/dist/app-6SXWEUZZ.js +3289 -0
- package/dist/{chunk-SEYPA5M2.js → chunk-3HV3MHME.js} +185 -64
- package/dist/chunk-54BGF7G5.js +771 -0
- package/dist/{chunk-RSKXBXSJ.js → chunk-5TV4LNC3.js} +22 -1
- package/dist/{chunk-KRNAXOQ4.js → chunk-FKKDQVRE.js} +2 -1
- package/dist/{chunk-G43R5ASK.js → chunk-HKZ6IN7X.js} +40 -5
- package/dist/chunk-KBLMQZ3P.js +116 -0
- package/dist/{chunk-PZZ6FBGB.js → chunk-MEAAX2SW.js} +4 -3
- package/dist/{chunk-WE5J7GMR.js → chunk-ZOFGQYXX.js} +36 -11
- package/dist/cli.js +123 -89
- package/dist/{config-6NWFKNLW.js → config-FYL6T5JP.js} +4 -1
- package/dist/daemon-Y5HIGH44.js +28 -0
- package/dist/{executor-LS3Y4DO5.js → executor-44MSZ76T.js} +5 -4
- package/dist/logger-HKMIMPCE.js +18 -0
- package/dist/{planner-UU4T5IEN.js → planner-MJ3XBCWH.js} +3 -3
- package/dist/{service-VTUYSAAZ.js → service-Q7USNFFB.js} +4 -3
- package/dist/{setup-NWBKTQCO.js → setup-JK664Y2M.js} +8 -3
- package/dist/{telegram-EFPHL4HC.js → telegram-FH5O4F3K.js} +25 -2
- package/dist/{whatsapp-HFMOFSFI.js → whatsapp-RLNSXSFI.js} +10 -2
- package/package.json +4 -4
- package/dist/app-LWDIWH7K.js +0 -1953
- package/dist/chunk-FAT2VKMJ.js +0 -232
- package/dist/chunk-IB6TU7TP.js +0 -310
- package/dist/chunk-QBOYMF4A.js +0 -42
- package/dist/daemon-WOR4GE5C.js +0 -96
- package/dist/logger-HD23RPWS.js +0 -12
- package/dist/router-ID6RN5AT.js +0 -14
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ConfigError
|
|
3
3
|
} from "./chunk-BVQG3WYO.js";
|
|
4
|
+
import {
|
|
5
|
+
isDev
|
|
6
|
+
} from "./chunk-ZCK3IFLC.js";
|
|
4
7
|
|
|
5
8
|
// src/config.ts
|
|
6
9
|
import { readFileSync, existsSync, mkdirSync, writeFileSync } from "fs";
|
|
7
10
|
import { join } from "path";
|
|
8
11
|
import { homedir } from "os";
|
|
12
|
+
import { createRequire } from "module";
|
|
9
13
|
import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
|
|
10
14
|
import { z } from "zod/v4";
|
|
15
|
+
var require2 = createRequire(import.meta.url);
|
|
16
|
+
var { version: pkgVersion } = require2("../package.json");
|
|
11
17
|
var ConfigSchema = z.object({
|
|
12
18
|
claude: z.object({
|
|
13
19
|
api_key: z.string(),
|
|
@@ -37,13 +43,18 @@ var ConfigSchema = z.object({
|
|
|
37
43
|
}).optional(),
|
|
38
44
|
container: z.object({
|
|
39
45
|
enabled: z.boolean().default(false),
|
|
40
|
-
image: z.string().default("cueclaw-agent:latest"),
|
|
46
|
+
image: z.string().default("ghcr.io/memodb-io/cueclaw-agent:latest"),
|
|
41
47
|
timeout: z.number().default(18e5),
|
|
42
48
|
max_output_size: z.number().default(10485760),
|
|
43
49
|
idle_timeout: z.number().default(18e5),
|
|
44
50
|
network: z.enum(["none", "host", "bridge"]).default("none")
|
|
45
51
|
}).optional()
|
|
46
52
|
});
|
|
53
|
+
var GHCR_IMAGE = "ghcr.io/memodb-io/cueclaw-agent";
|
|
54
|
+
var DEV_IMAGE = "cueclaw-agent:latest";
|
|
55
|
+
function getDefaultImage() {
|
|
56
|
+
return isDev ? DEV_IMAGE : `${GHCR_IMAGE}:${pkgVersion}`;
|
|
57
|
+
}
|
|
47
58
|
function cueclawHome() {
|
|
48
59
|
return join(homedir(), ".cueclaw");
|
|
49
60
|
}
|
|
@@ -104,6 +115,12 @@ function loadConfig() {
|
|
|
104
115
|
merged.claude = merged.claude ?? {};
|
|
105
116
|
merged.claude.base_url = process.env["ANTHROPIC_BASE_URL"];
|
|
106
117
|
}
|
|
118
|
+
if (process.env["CUECLAW_MODEL"]) {
|
|
119
|
+
merged.claude = merged.claude ?? {};
|
|
120
|
+
const model = process.env["CUECLAW_MODEL"];
|
|
121
|
+
merged.claude.planner = { ...merged.claude.planner, model };
|
|
122
|
+
merged.claude.executor = { ...merged.claude.executor, model };
|
|
123
|
+
}
|
|
107
124
|
if (process.env["TELEGRAM_BOT_TOKEN"]) {
|
|
108
125
|
merged.telegram = merged.telegram ?? {};
|
|
109
126
|
merged.telegram.token = process.env["TELEGRAM_BOT_TOKEN"];
|
|
@@ -128,6 +145,9 @@ claude:
|
|
|
128
145
|
executor:
|
|
129
146
|
model: claude-sonnet-4-6
|
|
130
147
|
|
|
148
|
+
container:
|
|
149
|
+
enabled: true
|
|
150
|
+
|
|
131
151
|
logging:
|
|
132
152
|
level: info
|
|
133
153
|
`;
|
|
@@ -236,6 +256,7 @@ function writeConfig(updates) {
|
|
|
236
256
|
}
|
|
237
257
|
|
|
238
258
|
export {
|
|
259
|
+
getDefaultImage,
|
|
239
260
|
cueclawHome,
|
|
240
261
|
ensureCueclawHome,
|
|
241
262
|
loadConfig,
|
|
@@ -29,8 +29,9 @@ function checkEnvironment() {
|
|
|
29
29
|
async function validateAuth(config) {
|
|
30
30
|
try {
|
|
31
31
|
const client = createAnthropicClient(config);
|
|
32
|
+
const model = config.claude.planner.model ?? "claude-haiku-4-5-20251001";
|
|
32
33
|
await client.messages.create({
|
|
33
|
-
model
|
|
34
|
+
model,
|
|
34
35
|
max_tokens: 10,
|
|
35
36
|
messages: [{ role: "user", content: "ping" }]
|
|
36
37
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
cueclawHome
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-5TV4LNC3.js";
|
|
4
4
|
|
|
5
5
|
// src/db.ts
|
|
6
6
|
import Database from "better-sqlite3";
|
|
@@ -113,6 +113,24 @@ function insertWorkflow(db, workflow) {
|
|
|
113
113
|
workflow.updated_at
|
|
114
114
|
);
|
|
115
115
|
}
|
|
116
|
+
function upsertWorkflow(db, workflow) {
|
|
117
|
+
db.prepare(`
|
|
118
|
+
INSERT OR REPLACE INTO workflows (id, name, description, trigger_json, steps_json, failure_policy_json, phase, schema_version, metadata_json, created_at, updated_at)
|
|
119
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
120
|
+
`).run(
|
|
121
|
+
workflow.id,
|
|
122
|
+
workflow.name,
|
|
123
|
+
workflow.description,
|
|
124
|
+
JSON.stringify(workflow.trigger),
|
|
125
|
+
JSON.stringify(workflow.steps),
|
|
126
|
+
JSON.stringify(workflow.failure_policy),
|
|
127
|
+
workflow.phase,
|
|
128
|
+
workflow.schema_version,
|
|
129
|
+
workflow.metadata ? JSON.stringify(workflow.metadata) : null,
|
|
130
|
+
workflow.created_at,
|
|
131
|
+
workflow.updated_at
|
|
132
|
+
);
|
|
133
|
+
}
|
|
116
134
|
function getWorkflow(db, id) {
|
|
117
135
|
const row = db.prepare("SELECT * FROM workflows WHERE id = ?").get(id);
|
|
118
136
|
return row ? rowToWorkflow(row) : void 0;
|
|
@@ -148,8 +166,16 @@ function insertWorkflowRun(db, run) {
|
|
|
148
166
|
`).run(run.id, run.workflow_id, run.trigger_data, run.status, run.started_at, run.completed_at ?? null, run.error ?? null, run.duration_ms ?? null);
|
|
149
167
|
}
|
|
150
168
|
function updateWorkflowRunStatus(db, id, status, error) {
|
|
151
|
-
const
|
|
152
|
-
|
|
169
|
+
const now = /* @__PURE__ */ new Date();
|
|
170
|
+
const completedAt = status !== "running" ? now.toISOString() : null;
|
|
171
|
+
let durationMs = null;
|
|
172
|
+
if (completedAt) {
|
|
173
|
+
const row = db.prepare("SELECT started_at FROM workflow_runs WHERE id = ?").get(id);
|
|
174
|
+
if (row?.started_at) {
|
|
175
|
+
durationMs = now.getTime() - new Date(row.started_at).getTime();
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
db.prepare("UPDATE workflow_runs SET status = ?, completed_at = ?, error = ?, duration_ms = ? WHERE id = ?").run(status, completedAt, error ?? null, durationMs, id);
|
|
153
179
|
}
|
|
154
180
|
function insertStepRun(db, stepRun) {
|
|
155
181
|
db.prepare(`
|
|
@@ -158,8 +184,16 @@ function insertStepRun(db, stepRun) {
|
|
|
158
184
|
`).run(stepRun.id, stepRun.run_id, stepRun.step_id, stepRun.status, stepRun.output_json ?? null, stepRun.error ?? null, stepRun.started_at ?? null, stepRun.completed_at ?? null, stepRun.duration_ms ?? null);
|
|
159
185
|
}
|
|
160
186
|
function updateStepRunStatus(db, id, status, output, error) {
|
|
161
|
-
const
|
|
162
|
-
|
|
187
|
+
const now = /* @__PURE__ */ new Date();
|
|
188
|
+
const completedAt = status === "succeeded" || status === "failed" || status === "skipped" ? now.toISOString() : null;
|
|
189
|
+
let durationMs = null;
|
|
190
|
+
if (completedAt) {
|
|
191
|
+
const row = db.prepare("SELECT started_at FROM step_runs WHERE id = ?").get(id);
|
|
192
|
+
if (row?.started_at) {
|
|
193
|
+
durationMs = now.getTime() - new Date(row.started_at).getTime();
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
db.prepare("UPDATE step_runs SET status = ?, output_json = ?, error = ?, completed_at = ?, duration_ms = ? WHERE id = ?").run(status, output ?? null, error ?? null, completedAt, durationMs, id);
|
|
163
197
|
}
|
|
164
198
|
function getStepRunsByRunId(db, runId) {
|
|
165
199
|
return db.prepare("SELECT * FROM step_runs WHERE run_id = ?").all(runId);
|
|
@@ -171,6 +205,7 @@ function getWorkflowRunsByWorkflowId(db, workflowId) {
|
|
|
171
205
|
export {
|
|
172
206
|
initDb,
|
|
173
207
|
insertWorkflow,
|
|
208
|
+
upsertWorkflow,
|
|
174
209
|
getWorkflow,
|
|
175
210
|
listWorkflows,
|
|
176
211
|
updateWorkflowPhase,
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
// src/logger.ts
|
|
2
|
+
import pino from "pino";
|
|
3
|
+
import { PassThrough } from "stream";
|
|
4
|
+
import { createWriteStream, mkdirSync } from "fs";
|
|
5
|
+
import { join } from "path";
|
|
6
|
+
import { homedir } from "os";
|
|
7
|
+
var listeners = /* @__PURE__ */ new Set();
|
|
8
|
+
var tuiStream = new PassThrough();
|
|
9
|
+
tuiStream.on("data", (chunk) => {
|
|
10
|
+
for (const raw of chunk.toString().split("\n")) {
|
|
11
|
+
if (!raw.trim()) continue;
|
|
12
|
+
try {
|
|
13
|
+
const obj = JSON.parse(raw);
|
|
14
|
+
const lvl = (pino.levels.labels[obj.level] ?? "INFO").toUpperCase();
|
|
15
|
+
const mod = obj.module ? ` [${obj.module}]` : "";
|
|
16
|
+
const msg = obj.msg ?? "";
|
|
17
|
+
for (const fn of listeners) fn(`${lvl}${mod} ${msg}`);
|
|
18
|
+
} catch {
|
|
19
|
+
for (const fn of listeners) fn(raw);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
var fileStream = null;
|
|
24
|
+
var logDir = null;
|
|
25
|
+
var configuredLevel = null;
|
|
26
|
+
function resolveDir(dir) {
|
|
27
|
+
return dir.startsWith("~") ? join(homedir(), dir.slice(1)) : dir;
|
|
28
|
+
}
|
|
29
|
+
function getLevel() {
|
|
30
|
+
return configuredLevel ?? process.env["LOG_LEVEL"] ?? "info";
|
|
31
|
+
}
|
|
32
|
+
var logger = pino({
|
|
33
|
+
level: process.env["LOG_LEVEL"] ?? "info",
|
|
34
|
+
transport: process.env["NODE_ENV"] === "production" ? void 0 : { target: "pino-pretty", options: { colorize: true } }
|
|
35
|
+
});
|
|
36
|
+
function initLogger(opts) {
|
|
37
|
+
const level = opts.level ?? process.env["LOG_LEVEL"] ?? "info";
|
|
38
|
+
configuredLevel = level;
|
|
39
|
+
if (opts.dir) {
|
|
40
|
+
const resolved = resolveDir(opts.dir);
|
|
41
|
+
logDir = resolved;
|
|
42
|
+
mkdirSync(join(resolved, "executions"), { recursive: true });
|
|
43
|
+
fileStream = createWriteStream(join(resolved, "daemon.log"), { flags: "a" });
|
|
44
|
+
logger = pino(
|
|
45
|
+
{ level },
|
|
46
|
+
pino.multistream([
|
|
47
|
+
{ stream: process.stdout },
|
|
48
|
+
{ stream: fileStream }
|
|
49
|
+
])
|
|
50
|
+
);
|
|
51
|
+
} else {
|
|
52
|
+
logger = pino({
|
|
53
|
+
level,
|
|
54
|
+
transport: process.env["NODE_ENV"] === "production" ? void 0 : { target: "pino-pretty", options: { colorize: true } }
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function enableTuiLogging() {
|
|
59
|
+
if (fileStream) {
|
|
60
|
+
logger = pino(
|
|
61
|
+
{ level: getLevel() },
|
|
62
|
+
pino.multistream([
|
|
63
|
+
{ stream: tuiStream },
|
|
64
|
+
{ stream: fileStream }
|
|
65
|
+
])
|
|
66
|
+
);
|
|
67
|
+
} else {
|
|
68
|
+
logger = pino({ level: getLevel() }, tuiStream);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function onLogLine(fn) {
|
|
72
|
+
listeners.add(fn);
|
|
73
|
+
return () => {
|
|
74
|
+
listeners.delete(fn);
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function createChildLogger(bindings) {
|
|
78
|
+
return logger.child(bindings);
|
|
79
|
+
}
|
|
80
|
+
function createExecutionLogger(workflowId, runId) {
|
|
81
|
+
if (!logDir) {
|
|
82
|
+
return logger.child({ workflowId, runId });
|
|
83
|
+
}
|
|
84
|
+
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
85
|
+
const filename = `${workflowId}_${date}.log`;
|
|
86
|
+
const execStream = createWriteStream(join(logDir, "executions", filename), { flags: "a" });
|
|
87
|
+
return pino(
|
|
88
|
+
{ level: getLevel() },
|
|
89
|
+
pino.multistream([
|
|
90
|
+
{ stream: execStream },
|
|
91
|
+
...fileStream ? [{ stream: fileStream }] : []
|
|
92
|
+
])
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
function resetLogger() {
|
|
96
|
+
if (fileStream) {
|
|
97
|
+
fileStream.end();
|
|
98
|
+
fileStream = null;
|
|
99
|
+
}
|
|
100
|
+
logDir = null;
|
|
101
|
+
configuredLevel = null;
|
|
102
|
+
logger = pino({
|
|
103
|
+
level: process.env["LOG_LEVEL"] ?? "info",
|
|
104
|
+
transport: process.env["NODE_ENV"] === "production" ? void 0 : { target: "pino-pretty", options: { colorize: true } }
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export {
|
|
109
|
+
logger,
|
|
110
|
+
initLogger,
|
|
111
|
+
enableTuiLogging,
|
|
112
|
+
onLogLine,
|
|
113
|
+
createChildLogger,
|
|
114
|
+
createExecutionLogger,
|
|
115
|
+
resetLogger
|
|
116
|
+
};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
cueclawHome
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-5TV4LNC3.js";
|
|
4
4
|
import {
|
|
5
5
|
logger
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-KBLMQZ3P.js";
|
|
7
7
|
|
|
8
8
|
// src/service.ts
|
|
9
9
|
import { writeFileSync, existsSync, unlinkSync, mkdirSync } from "fs";
|
|
@@ -91,6 +91,7 @@ function installLaunchd() {
|
|
|
91
91
|
<string>${cliPath}</string>
|
|
92
92
|
<string>daemon</string>
|
|
93
93
|
<string>start</string>
|
|
94
|
+
<string>--foreground</string>
|
|
94
95
|
</array>
|
|
95
96
|
<key>KeepAlive</key>
|
|
96
97
|
<true/>
|
|
@@ -136,7 +137,7 @@ Description=CueClaw Daemon
|
|
|
136
137
|
After=network.target
|
|
137
138
|
|
|
138
139
|
[Service]
|
|
139
|
-
ExecStart=${nodePath} ${cliPath} daemon start
|
|
140
|
+
ExecStart=${nodePath} ${cliPath} daemon start --foreground
|
|
140
141
|
Restart=always
|
|
141
142
|
RestartSec=5
|
|
142
143
|
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createAnthropicClient
|
|
3
3
|
} from "./chunk-DVQFSFIZ.js";
|
|
4
|
-
import {
|
|
5
|
-
getConfiguredSecretKeys
|
|
6
|
-
} from "./chunk-ZCK3IFLC.js";
|
|
7
4
|
import {
|
|
8
5
|
PlannerError
|
|
9
6
|
} from "./chunk-BVQG3WYO.js";
|
|
7
|
+
import {
|
|
8
|
+
getConfiguredSecretKeys
|
|
9
|
+
} from "./chunk-ZCK3IFLC.js";
|
|
10
10
|
import {
|
|
11
11
|
logger
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-KBLMQZ3P.js";
|
|
13
13
|
|
|
14
14
|
// src/planner.ts
|
|
15
15
|
import { z } from "zod/v4";
|
|
@@ -236,9 +236,26 @@ function parsePlannerToolResponse(response) {
|
|
|
236
236
|
}
|
|
237
237
|
return { type: "error", error: "Unexpected planner response format" };
|
|
238
238
|
}
|
|
239
|
-
function buildPlannerSystemPrompt(config) {
|
|
239
|
+
function buildPlannerSystemPrompt(config, channelContext) {
|
|
240
240
|
const identity = config.identity?.name ? `
|
|
241
241
|
User identity: ${config.identity.name}` : "";
|
|
242
|
+
let channelSection;
|
|
243
|
+
if (channelContext && channelContext.channel !== "tui") {
|
|
244
|
+
channelSection = `
|
|
245
|
+
## Channel Context
|
|
246
|
+
|
|
247
|
+
The user is requesting this workflow via **${channelContext.channel}**.
|
|
248
|
+
- Chat ID: ${channelContext.chatJid}
|
|
249
|
+
- Sender: ${channelContext.sender}
|
|
250
|
+
|
|
251
|
+
When the workflow needs to send notifications or messages back to the user, use the chat ID and channel above as the recipient. Do not ask the user for recipient information \u2014 you already have it.`;
|
|
252
|
+
} else {
|
|
253
|
+
channelSection = `
|
|
254
|
+
## Channel Context
|
|
255
|
+
|
|
256
|
+
The user is using the TUI (terminal interface). No chat recipient is available.
|
|
257
|
+
If the workflow needs to send notifications or messages to someone, the step description must require explicit recipient input (e.g., email address, phone number, chat ID) \u2014 do not assume any default recipient.`;
|
|
258
|
+
}
|
|
242
259
|
return `You are CueClaw Planner. Convert user's natural language into a structured Workflow.
|
|
243
260
|
|
|
244
261
|
## Execution Environment
|
|
@@ -276,9 +293,9 @@ You can reference these in workflow steps \u2014 they are available as environme
|
|
|
276
293
|
If a workflow needs credentials not listed above, use the set_secret tool to store them after the user provides the value. Never invent or guess secret values.
|
|
277
294
|
|
|
278
295
|
## User Identity
|
|
279
|
-
${identity}`;
|
|
296
|
+
${identity}${channelSection}`;
|
|
280
297
|
}
|
|
281
|
-
async function generatePlan(userDescription, config) {
|
|
298
|
+
async function generatePlan(userDescription, config, channelContext) {
|
|
282
299
|
const anthropic = createAnthropicClient(config);
|
|
283
300
|
const MAX_RETRIES = 2;
|
|
284
301
|
let retryContext = "";
|
|
@@ -292,13 +309,14 @@ ${retryContext}` : userDescription;
|
|
|
292
309
|
response = await anthropic.messages.create({
|
|
293
310
|
model: config.claude.planner.model,
|
|
294
311
|
max_tokens: 4096,
|
|
295
|
-
system: buildPlannerSystemPrompt(config),
|
|
312
|
+
system: buildPlannerSystemPrompt(config, channelContext),
|
|
296
313
|
messages: [{ role: "user", content: prompt }],
|
|
297
314
|
tools: [plannerTool],
|
|
298
315
|
tool_choice: { type: "tool", name: "create_workflow" }
|
|
299
316
|
});
|
|
300
317
|
} catch (err) {
|
|
301
318
|
const detail = err instanceof Error ? err.message : String(err);
|
|
319
|
+
logger.error({ err, attempt }, "Planner API request failed");
|
|
302
320
|
throw new PlannerError(
|
|
303
321
|
`API request failed: ${detail}. Check your API key and base_url in ~/.cueclaw/config.yaml`
|
|
304
322
|
);
|
|
@@ -322,6 +340,7 @@ ${retryContext}` : userDescription;
|
|
|
322
340
|
if (!parseResult.success) {
|
|
323
341
|
const errMsg = parseResult.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
|
|
324
342
|
if (attempt < MAX_RETRIES) {
|
|
343
|
+
logger.warn({ attempt, errors: errMsg }, "Planner output validation failed, retrying");
|
|
325
344
|
retryContext = `[System] Previous plan had validation issues:
|
|
326
345
|
${errMsg}
|
|
327
346
|
Please fix and try again.`;
|
|
@@ -332,6 +351,7 @@ Please fix and try again.`;
|
|
|
332
351
|
const dagErrors = validateDAG(parseResult.data.steps);
|
|
333
352
|
if (dagErrors.length > 0) {
|
|
334
353
|
if (attempt < MAX_RETRIES) {
|
|
354
|
+
logger.warn({ attempt, dagErrors }, "DAG validation failed, retrying");
|
|
335
355
|
retryContext = `[System] DAG dependency issues:
|
|
336
356
|
${dagErrors.join("\n")}
|
|
337
357
|
Please fix the step dependencies.`;
|
|
@@ -340,7 +360,7 @@ Please fix the step dependencies.`;
|
|
|
340
360
|
throw new PlannerError(`DAG validation failed after ${MAX_RETRIES + 1} attempts: ${dagErrors.join(", ")}`);
|
|
341
361
|
}
|
|
342
362
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
343
|
-
|
|
363
|
+
const workflow = {
|
|
344
364
|
...parseResult.data,
|
|
345
365
|
schema_version: "1.0",
|
|
346
366
|
id: `wf_${nanoid()}`,
|
|
@@ -348,10 +368,12 @@ Please fix the step dependencies.`;
|
|
|
348
368
|
created_at: now,
|
|
349
369
|
updated_at: now
|
|
350
370
|
};
|
|
371
|
+
logger.info({ name: workflow.name, stepCount: workflow.steps.length, workflowId: workflow.id }, "Plan generated successfully");
|
|
372
|
+
return workflow;
|
|
351
373
|
}
|
|
352
374
|
throw new PlannerError("Failed to generate valid plan after retries");
|
|
353
375
|
}
|
|
354
|
-
async function modifyPlan(originalWorkflow, modificationDescription, config) {
|
|
376
|
+
async function modifyPlan(originalWorkflow, modificationDescription, config, channelContext) {
|
|
355
377
|
const plannerOutput = {
|
|
356
378
|
name: originalWorkflow.name,
|
|
357
379
|
description: originalWorkflow.description,
|
|
@@ -369,7 +391,8 @@ ${modificationDescription}
|
|
|
369
391
|
|
|
370
392
|
Preserve unmodified steps' IDs, descriptions, and dependencies \u2014 only change what the user specified.
|
|
371
393
|
Return the complete modified workflow using the create_workflow tool.`;
|
|
372
|
-
const result = await generatePlan(combinedPrompt, config);
|
|
394
|
+
const result = await generatePlan(combinedPrompt, config, channelContext);
|
|
395
|
+
logger.info({ workflowId: originalWorkflow.id }, "Plan modified");
|
|
373
396
|
return {
|
|
374
397
|
...result,
|
|
375
398
|
id: originalWorkflow.id,
|
|
@@ -381,6 +404,7 @@ function confirmPlan(workflow) {
|
|
|
381
404
|
throw new PlannerError(`Cannot confirm workflow in phase "${workflow.phase}"`);
|
|
382
405
|
}
|
|
383
406
|
const nextPhase = workflow.trigger.type === "manual" ? "executing" : "active";
|
|
407
|
+
logger.info({ workflowId: workflow.id, nextPhase }, "Plan confirmed");
|
|
384
408
|
return {
|
|
385
409
|
...workflow,
|
|
386
410
|
phase: nextPhase,
|
|
@@ -388,6 +412,7 @@ function confirmPlan(workflow) {
|
|
|
388
412
|
};
|
|
389
413
|
}
|
|
390
414
|
function rejectPlan(workflow) {
|
|
415
|
+
logger.info({ workflowId: workflow.id }, "Plan rejected");
|
|
391
416
|
return {
|
|
392
417
|
...workflow,
|
|
393
418
|
phase: "planning",
|