@integrity-labs/agt-cli 0.8.4 → 0.8.7
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/dist/bin/agt.js +2 -2
- package/dist/{chunk-5UGOY3IV.js → chunk-2TSCVXHE.js} +13 -4
- package/dist/chunk-2TSCVXHE.js.map +1 -0
- package/dist/{claude-scheduler-V2QNX3Z4.js → claude-scheduler-VFBZFE6U.js} +2 -2
- package/dist/lib/manager-worker.js +89 -70
- package/dist/lib/manager-worker.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-5UGOY3IV.js.map +0 -1
- /package/dist/{claude-scheduler-V2QNX3Z4.js.map → claude-scheduler-VFBZFE6U.js.map} +0 -0
package/dist/bin/agt.js
CHANGED
|
@@ -3411,7 +3411,7 @@ async function acpxCloseCommand(agent2, _opts, cmd) {
|
|
|
3411
3411
|
import { execSync } from "child_process";
|
|
3412
3412
|
import chalk19 from "chalk";
|
|
3413
3413
|
import ora15 from "ora";
|
|
3414
|
-
var cliVersion = true ? "0.8.
|
|
3414
|
+
var cliVersion = true ? "0.8.7" : "dev";
|
|
3415
3415
|
async function fetchLatestVersion() {
|
|
3416
3416
|
const host2 = AGT_HOST;
|
|
3417
3417
|
if (!host2) return null;
|
|
@@ -3527,7 +3527,7 @@ async function checkForUpdateOnStartup() {
|
|
|
3527
3527
|
}
|
|
3528
3528
|
|
|
3529
3529
|
// src/bin/agt.ts
|
|
3530
|
-
var cliVersion2 = true ? "0.8.
|
|
3530
|
+
var cliVersion2 = true ? "0.8.7" : "dev";
|
|
3531
3531
|
var program = new Command();
|
|
3532
3532
|
program.name("agt").description("Augmented CLI \u2014 agent provisioning and management").version(cliVersion2).option("--json", "Emit machine-readable JSON output (suppress spinners and colors)").option("--skip-update-check", "Skip the automatic update check on startup");
|
|
3533
3533
|
program.hook("preAction", (thisCommand) => {
|
|
@@ -84,6 +84,7 @@ function syncTasksToScheduler(codeName, agentId, tasks) {
|
|
|
84
84
|
existing.deliveryChannel = t.delivery_channel;
|
|
85
85
|
existing.deliveryTo = t.delivery_to;
|
|
86
86
|
existing.enabled = t.enabled;
|
|
87
|
+
if (t.triggered_at) existing.triggeredAt = new Date(t.triggered_at).getTime();
|
|
87
88
|
if (scheduleChanged) {
|
|
88
89
|
existing.nextFireAt = computeNextFire(
|
|
89
90
|
t.schedule_kind,
|
|
@@ -112,6 +113,7 @@ function syncTasksToScheduler(codeName, agentId, tasks) {
|
|
|
112
113
|
deliveryChannel: t.delivery_channel,
|
|
113
114
|
deliveryTo: t.delivery_to,
|
|
114
115
|
enabled: t.enabled,
|
|
116
|
+
triggeredAt: t.triggered_at ? new Date(t.triggered_at).getTime() : null,
|
|
115
117
|
nextFireAt: computeNextFire(
|
|
116
118
|
t.schedule_kind,
|
|
117
119
|
t.schedule_expr,
|
|
@@ -130,9 +132,16 @@ function syncTasksToScheduler(codeName, agentId, tasks) {
|
|
|
130
132
|
}
|
|
131
133
|
function getReadyTasks(state) {
|
|
132
134
|
const now = Date.now();
|
|
133
|
-
const ready = Object.values(state.tasks).filter(
|
|
134
|
-
(t
|
|
135
|
-
|
|
135
|
+
const ready = Object.values(state.tasks).filter((t) => {
|
|
136
|
+
if (!t.enabled) return false;
|
|
137
|
+
if (t.triggeredAt) {
|
|
138
|
+
const triggerReady = !t.lastFireAt || t.triggeredAt > t.lastFireAt;
|
|
139
|
+
if (!triggerReady) {
|
|
140
|
+
}
|
|
141
|
+
if (triggerReady) return true;
|
|
142
|
+
}
|
|
143
|
+
return t.nextFireAt !== null && t.nextFireAt <= now;
|
|
144
|
+
});
|
|
136
145
|
const seen = /* @__PURE__ */ new Set();
|
|
137
146
|
return ready.filter((t) => {
|
|
138
147
|
if (seen.has(t.templateId)) return false;
|
|
@@ -181,4 +190,4 @@ export {
|
|
|
181
190
|
findTaskByTemplate,
|
|
182
191
|
getProjectDir
|
|
183
192
|
};
|
|
184
|
-
//# sourceMappingURL=chunk-
|
|
193
|
+
//# sourceMappingURL=chunk-2TSCVXHE.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/claude-scheduler.ts"],"sourcesContent":["/**\n * In-process scheduler for Claude Code agents.\n *\n * On each manager poll cycle, checks if any tasks are due and fires them\n * via `claude -p`. State persists to disk so nextFireAt/lastFireAt survive\n * manager restarts.\n */\n\nimport { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\nimport { Cron } from 'croner';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface SchedulerTaskState {\n taskId: string;\n templateId: string;\n name: string;\n agentCodeName: string;\n agentId: string;\n scheduleKind: 'cron' | 'every' | 'at';\n scheduleExpr: string | null;\n scheduleEvery: string | null;\n scheduleAt: string | null;\n timezone: string;\n prompt: string;\n sessionTarget: string;\n deliveryMode: string;\n deliveryChannel: string | null;\n deliveryTo: string | null;\n enabled: boolean;\n triggeredAt: number | null; // epoch ms — manual trigger from webapp\n nextFireAt: number | null; // epoch ms, null = completed one-shot\n lastFireAt: number | null;\n lastStatus: 'ok' | 'error' | null;\n firedCount: number;\n}\n\nexport interface SchedulerState {\n version: 1;\n tasks: Record<string, SchedulerTaskState>;\n updatedAt: string;\n}\n\nexport interface SchedulerTaskInput {\n id: string;\n template_id: string;\n name: string;\n schedule_kind: 'cron' | 'every' | 'at';\n schedule_expr: string | null;\n schedule_every: string | null;\n schedule_at: string | null;\n timezone: string;\n prompt: string;\n session_target: string;\n delivery_mode: string;\n delivery_channel: string | null;\n delivery_to: string | null;\n enabled: boolean;\n triggered_at?: string | null;\n}\n\n// ---------------------------------------------------------------------------\n// Interval parsing (reused from Claude Code adapter)\n// ---------------------------------------------------------------------------\n\nfunction parseIntervalMs(scheduleEvery: string | null): number {\n if (!scheduleEvery) return 60 * 60_000; // 1hr default\n const match = scheduleEvery.match(/^(\\d+)\\s*(m|min|h|hr|d)$/i);\n if (!match) return 60 * 60_000;\n const value = parseInt(match[1]!, 10);\n const unit = match[2]!.toLowerCase();\n if (unit === 'h' || unit === 'hr') return value * 60 * 60_000;\n if (unit === 'd') return value * 24 * 60 * 60_000;\n return value * 60_000; // minutes\n}\n\n// ---------------------------------------------------------------------------\n// Next-fire computation\n// ---------------------------------------------------------------------------\n\nexport function computeNextFire(\n kind: 'cron' | 'every' | 'at',\n expr: string | null,\n every: string | null,\n at: string | null,\n timezone: string,\n afterMs?: number,\n): number | null {\n const now = afterMs ?? Date.now();\n\n if (kind === 'cron' && expr) {\n try {\n const cron = new Cron(expr, { timezone: timezone || undefined });\n const next = cron.nextRun(new Date(now));\n return next ? next.getTime() : null;\n } catch {\n return null;\n }\n }\n\n if (kind === 'every') {\n const intervalMs = parseIntervalMs(every);\n return now + intervalMs;\n }\n\n if (kind === 'at' && at) {\n const ts = new Date(at).getTime();\n return isNaN(ts) ? null : ts;\n }\n\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// State persistence\n// ---------------------------------------------------------------------------\n\nfunction getStateDir(codeName: string): string {\n return join(homedir(), '.augmented', codeName, 'claudecode');\n}\n\nfunction getStatePath(codeName: string): string {\n return join(getStateDir(codeName), 'scheduler-state.json');\n}\n\nexport function loadSchedulerState(codeName: string): SchedulerState {\n const path = getStatePath(codeName);\n if (existsSync(path)) {\n try {\n return JSON.parse(readFileSync(path, 'utf-8'));\n } catch { /* corrupted — start fresh */ }\n }\n return { version: 1, tasks: {}, updatedAt: new Date().toISOString() };\n}\n\nexport function saveSchedulerState(codeName: string, state: SchedulerState): void {\n const dir = getStateDir(codeName);\n mkdirSync(dir, { recursive: true });\n state.updatedAt = new Date().toISOString();\n const path = getStatePath(codeName);\n const tmpPath = path + '.tmp';\n writeFileSync(tmpPath, JSON.stringify(state, null, 2));\n renameSync(tmpPath, path);\n}\n\n// ---------------------------------------------------------------------------\n// Sync API tasks → scheduler state\n// ---------------------------------------------------------------------------\n\nexport function syncTasksToScheduler(\n codeName: string,\n agentId: string,\n tasks: SchedulerTaskInput[],\n): SchedulerState {\n const state = loadSchedulerState(codeName);\n const desiredIds = new Set(tasks.map((t) => t.id));\n\n // Remove tasks no longer in API\n for (const id of Object.keys(state.tasks)) {\n if (!desiredIds.has(id)) {\n delete state.tasks[id];\n }\n }\n\n // Add or update tasks\n for (const t of tasks) {\n const existing = state.tasks[t.id];\n if (existing) {\n // Only recompute nextFireAt if the schedule definition actually changed.\n // Without this guard, every sync cycle resets nextFireAt from \"now\",\n // preventing past-due tasks from ever being detected as ready.\n const scheduleChanged =\n existing.scheduleKind !== t.schedule_kind ||\n existing.scheduleExpr !== t.schedule_expr ||\n existing.scheduleEvery !== t.schedule_every ||\n existing.scheduleAt !== t.schedule_at ||\n existing.timezone !== t.timezone;\n\n // Update mutable fields, preserve fire history\n existing.name = t.name;\n existing.templateId = t.template_id;\n existing.scheduleKind = t.schedule_kind;\n existing.scheduleExpr = t.schedule_expr;\n existing.scheduleEvery = t.schedule_every;\n existing.scheduleAt = t.schedule_at;\n existing.timezone = t.timezone;\n existing.prompt = t.prompt;\n existing.sessionTarget = t.session_target;\n existing.deliveryMode = t.delivery_mode;\n existing.deliveryChannel = t.delivery_channel;\n existing.deliveryTo = t.delivery_to;\n existing.enabled = t.enabled;\n if (t.triggered_at) existing.triggeredAt = new Date(t.triggered_at).getTime();\n if (scheduleChanged) {\n existing.nextFireAt = computeNextFire(\n t.schedule_kind, t.schedule_expr, t.schedule_every, t.schedule_at,\n t.timezone, existing.lastFireAt ?? undefined,\n );\n }\n } else {\n // New task\n state.tasks[t.id] = {\n taskId: t.id,\n templateId: t.template_id,\n name: t.name,\n agentCodeName: codeName,\n agentId,\n scheduleKind: t.schedule_kind,\n scheduleExpr: t.schedule_expr,\n scheduleEvery: t.schedule_every,\n scheduleAt: t.schedule_at,\n timezone: t.timezone,\n prompt: t.prompt,\n sessionTarget: t.session_target,\n deliveryMode: t.delivery_mode,\n deliveryChannel: t.delivery_channel,\n deliveryTo: t.delivery_to,\n enabled: t.enabled,\n triggeredAt: t.triggered_at ? new Date(t.triggered_at).getTime() : null,\n nextFireAt: computeNextFire(\n t.schedule_kind, t.schedule_expr, t.schedule_every, t.schedule_at, t.timezone,\n ),\n lastFireAt: null,\n lastStatus: null,\n firedCount: 0,\n };\n }\n }\n\n saveSchedulerState(codeName, state);\n return state;\n}\n\n// ---------------------------------------------------------------------------\n// Ready-task detection\n// ---------------------------------------------------------------------------\n\nexport function getReadyTasks(state: SchedulerState): SchedulerTaskState[] {\n const now = Date.now();\n const ready = Object.values(state.tasks).filter((t) => {\n if (!t.enabled) return false;\n // Manual trigger: triggered_at is set and hasn't been fired yet\n if (t.triggeredAt) {\n const triggerReady = !t.lastFireAt || t.triggeredAt > t.lastFireAt;\n if (!triggerReady) {\n // Debug: trigger was already consumed\n }\n if (triggerReady) return true;\n }\n // Normal schedule\n return t.nextFireAt !== null && t.nextFireAt <= now;\n });\n // Deduplicate by templateId — only fire one task per template per cycle\n const seen = new Set<string>();\n return ready.filter((t) => {\n if (seen.has(t.templateId)) return false;\n seen.add(t.templateId);\n return true;\n });\n}\n\n// ---------------------------------------------------------------------------\n// Post-execution state update\n// ---------------------------------------------------------------------------\n\nexport function markTaskFired(\n codeName: string,\n taskId: string,\n status: 'ok' | 'error',\n): SchedulerState {\n const state = loadSchedulerState(codeName);\n const task = state.tasks[taskId];\n if (!task) return state;\n\n task.lastFireAt = Date.now();\n task.lastStatus = status;\n task.firedCount++;\n\n // Compute next fire\n if (task.scheduleKind === 'at') {\n // One-shot — mark as completed\n task.nextFireAt = null;\n } else {\n task.nextFireAt = computeNextFire(\n task.scheduleKind, task.scheduleExpr, task.scheduleEvery, task.scheduleAt,\n task.timezone, task.lastFireAt,\n );\n }\n\n saveSchedulerState(codeName, state);\n return state;\n}\n\n// ---------------------------------------------------------------------------\n// Find a task by template ID (for work triggers)\n// ---------------------------------------------------------------------------\n\nexport function findTaskByTemplate(state: SchedulerState, templateId: string): SchedulerTaskState | undefined {\n return Object.values(state.tasks).find(\n (t) => t.templateId === templateId && t.enabled,\n );\n}\n\nexport function getProjectDir(codeName: string): string {\n return join(homedir(), '.augmented', codeName, 'project');\n}\n"],"mappings":"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","names":[]}
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
markTaskFired,
|
|
8
8
|
saveSchedulerState,
|
|
9
9
|
syncTasksToScheduler
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-2TSCVXHE.js";
|
|
11
11
|
export {
|
|
12
12
|
computeNextFire,
|
|
13
13
|
findTaskByTemplate,
|
|
@@ -18,4 +18,4 @@ export {
|
|
|
18
18
|
saveSchedulerState,
|
|
19
19
|
syncTasksToScheduler
|
|
20
20
|
};
|
|
21
|
-
//# sourceMappingURL=claude-scheduler-
|
|
21
|
+
//# sourceMappingURL=claude-scheduler-VFBZFE6U.js.map
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
loadSchedulerState,
|
|
18
18
|
markTaskFired,
|
|
19
19
|
syncTasksToScheduler
|
|
20
|
-
} from "../chunk-
|
|
20
|
+
} from "../chunk-2TSCVXHE.js";
|
|
21
21
|
|
|
22
22
|
// src/lib/manager-worker.ts
|
|
23
23
|
import { createHash } from "crypto";
|
|
@@ -351,14 +351,16 @@ var _acpxBin = null;
|
|
|
351
351
|
function getAcpxBin() {
|
|
352
352
|
if (_acpxBin) return _acpxBin;
|
|
353
353
|
const moduleDir = dirname(fileURLToPath(import.meta.url));
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
]) {
|
|
354
|
+
let dir = moduleDir;
|
|
355
|
+
for (let i = 0; i < 6; i++) {
|
|
356
|
+
const candidate = join(dir, "node_modules", ".bin", "acpx");
|
|
358
357
|
if (existsSync(candidate)) {
|
|
359
358
|
_acpxBin = candidate;
|
|
360
359
|
return _acpxBin;
|
|
361
360
|
}
|
|
361
|
+
const parent = dirname(dir);
|
|
362
|
+
if (parent === dir) break;
|
|
363
|
+
dir = parent;
|
|
362
364
|
}
|
|
363
365
|
try {
|
|
364
366
|
execSync("which acpx", { stdio: "ignore" });
|
|
@@ -427,7 +429,7 @@ function spawnSession(config2, session) {
|
|
|
427
429
|
"Skill"
|
|
428
430
|
].join(",");
|
|
429
431
|
args.push("--allowedTools", allowedTools);
|
|
430
|
-
let envPrefix = "
|
|
432
|
+
let envPrefix = "";
|
|
431
433
|
const envIntegrationsPath = join(projectDir, ".env.integrations");
|
|
432
434
|
if (existsSync(envIntegrationsPath)) {
|
|
433
435
|
try {
|
|
@@ -438,7 +440,7 @@ function spawnSession(config2, session) {
|
|
|
438
440
|
const value = line.slice(eqIdx + 1);
|
|
439
441
|
return `${key}=${JSON.stringify(value)}`;
|
|
440
442
|
}).join(" ");
|
|
441
|
-
if (envVars) envPrefix =
|
|
443
|
+
if (envVars) envPrefix = `${envVars} `;
|
|
442
444
|
} catch {
|
|
443
445
|
}
|
|
444
446
|
}
|
|
@@ -539,22 +541,19 @@ async function injectMessage(codeName, type, content, meta, log2) {
|
|
|
539
541
|
mkdirSync(tmpDir, { recursive: true });
|
|
540
542
|
const tmpFile = join(tmpDir, ".agt-inject-prompt.txt");
|
|
541
543
|
writeFileSync2(tmpFile, text);
|
|
542
|
-
_log(`[inject] acpx
|
|
543
|
-
|
|
544
|
-
"claude",
|
|
545
|
-
"prompt",
|
|
546
|
-
"--no-wait",
|
|
547
|
-
"-f",
|
|
548
|
-
tmpFile
|
|
549
|
-
], {
|
|
544
|
+
_log(`[inject] acpx exec (fire-and-forget): cwd=${projectDir}, file=${tmpFile}`);
|
|
545
|
+
const child = spawn(acpx, ["claude", "exec", "-f", tmpFile], {
|
|
550
546
|
cwd: projectDir,
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
547
|
+
stdio: "ignore",
|
|
548
|
+
detached: true
|
|
549
|
+
});
|
|
550
|
+
child.on("error", (err) => {
|
|
551
|
+
_log(`[inject] acpx spawn error for '${codeName}': ${err.message}`);
|
|
554
552
|
});
|
|
553
|
+
child.unref();
|
|
555
554
|
return true;
|
|
556
555
|
} catch (err) {
|
|
557
|
-
_log(`[inject] acpx
|
|
556
|
+
_log(`[inject] acpx exec failed for '${codeName}': ${err.message}`);
|
|
558
557
|
}
|
|
559
558
|
} else {
|
|
560
559
|
_log(`[inject] acpx binary not found \u2014 falling back to tmux send-keys`);
|
|
@@ -641,31 +640,27 @@ function writeAcpxConfig(config2) {
|
|
|
641
640
|
if (existsSync(claudeMdPath)) claudeArgs.push("--system-prompt-file", claudeMdPath);
|
|
642
641
|
claudeArgs.push("--allow-dangerously-skip-permissions");
|
|
643
642
|
claudeArgs.push("--dangerously-skip-permissions");
|
|
643
|
+
claudeArgs.push("--strict-mcp-config");
|
|
644
644
|
const mcpServerNames2 = collectMcpServerNames(mcpConfigPath, channelsConfigPath);
|
|
645
645
|
const mcpPatterns2 = mcpServerNames2.map((name) => `mcp__${name.replace(/-/g, "_")}__*`);
|
|
646
646
|
const allowedTools2 = [...mcpPatterns2, "Bash", "Read", "Write", "Edit", "Grep", "Glob", "Agent", "Skill"].join(",");
|
|
647
647
|
claudeArgs.push("--allowedTools", allowedTools2);
|
|
648
|
-
|
|
648
|
+
const acpCmd = `npx -y @agentclientprotocol/claude-agent-acp ${claudeArgs.map((a) => a.includes(" ") || a.includes("*") ? JSON.stringify(a) : a).join(" ")}`;
|
|
649
649
|
const envIntegrationsPath = join(projectDir, ".env.integrations");
|
|
650
|
+
const wrapperPath = join(projectDir, ".claude", "acpx-agent.sh");
|
|
651
|
+
const wrapperLines = ["#!/usr/bin/env bash"];
|
|
650
652
|
if (existsSync(envIntegrationsPath)) {
|
|
651
|
-
|
|
652
|
-
const envContent = readFileSync2(envIntegrationsPath, "utf-8");
|
|
653
|
-
const envVars = envContent.split("\n").filter((line) => line && !line.startsWith("#") && line.includes("=")).map((line) => {
|
|
654
|
-
const eqIdx = line.indexOf("=");
|
|
655
|
-
const key = line.slice(0, eqIdx);
|
|
656
|
-
const value = line.slice(eqIdx + 1);
|
|
657
|
-
return `${key}=${JSON.stringify(value)}`;
|
|
658
|
-
}).join(" ");
|
|
659
|
-
if (envVars) envPrefix = `CLAUDE_CODE_SIMPLE=1 ${envVars} `;
|
|
660
|
-
} catch {
|
|
661
|
-
}
|
|
653
|
+
wrapperLines.push(`set -a`, `source ${JSON.stringify(envIntegrationsPath)}`, `set +a`);
|
|
662
654
|
}
|
|
655
|
+
wrapperLines.push(`exec ${acpCmd}`);
|
|
656
|
+
mkdirSync(join(projectDir, ".claude"), { recursive: true });
|
|
657
|
+
writeFileSync2(wrapperPath, wrapperLines.join("\n") + "\n", { mode: 493 });
|
|
663
658
|
const acpxConfig = {
|
|
664
659
|
defaultAgent: "claude",
|
|
665
660
|
defaultPermissions: "approve-all",
|
|
666
661
|
agents: {
|
|
667
662
|
claude: {
|
|
668
|
-
command:
|
|
663
|
+
command: wrapperPath
|
|
669
664
|
}
|
|
670
665
|
}
|
|
671
666
|
};
|
|
@@ -2504,7 +2499,8 @@ async function syncAndCheckClaudeScheduler(agent, tasks, boardItems, refreshData
|
|
|
2504
2499
|
delivery_mode: t.delivery_mode ?? "none",
|
|
2505
2500
|
delivery_channel: t.delivery_channel ?? null,
|
|
2506
2501
|
delivery_to: t.delivery_to ?? null,
|
|
2507
|
-
enabled: t.enabled ?? true
|
|
2502
|
+
enabled: t.enabled ?? true,
|
|
2503
|
+
triggered_at: t.triggered_at ?? null
|
|
2508
2504
|
}));
|
|
2509
2505
|
const state3 = syncTasksToScheduler(codeName, agent.agent_id, taskInputs);
|
|
2510
2506
|
claudeSchedulerStates.set(codeName, state3);
|
|
@@ -2578,7 +2574,11 @@ async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
|
|
|
2578
2574
|
}
|
|
2579
2575
|
const output = stdout.trim();
|
|
2580
2576
|
log(`[claude-scheduler] Task '${task.name}' completed for '${codeName}' (${output.length} chars): ${output.slice(0, 300)}`);
|
|
2581
|
-
await processClaudeTaskResult(codeName, agentId, task.templateId, output
|
|
2577
|
+
await processClaudeTaskResult(codeName, agentId, task.templateId, output, {
|
|
2578
|
+
mode: task.deliveryMode,
|
|
2579
|
+
channel: task.deliveryChannel,
|
|
2580
|
+
to: task.deliveryTo
|
|
2581
|
+
});
|
|
2582
2582
|
const updated = markTaskFired(codeName, task.taskId, "ok");
|
|
2583
2583
|
claudeSchedulerStates.set(codeName, updated);
|
|
2584
2584
|
} catch (err) {
|
|
@@ -2588,19 +2588,19 @@ async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
|
|
|
2588
2588
|
claudeSchedulerStates.set(codeName, updated);
|
|
2589
2589
|
}
|
|
2590
2590
|
}
|
|
2591
|
-
async function processClaudeTaskResult(codeName, agentId, templateId, output) {
|
|
2591
|
+
async function processClaudeTaskResult(codeName, agentId, templateId, output, delivery) {
|
|
2592
2592
|
try {
|
|
2593
2593
|
if (STANDUP_TEMPLATES.has(templateId)) {
|
|
2594
2594
|
const standup = parseStandupSummary(output);
|
|
2595
2595
|
await api.post("/host/agent-status", {
|
|
2596
|
-
|
|
2596
|
+
agent_code_name: codeName,
|
|
2597
2597
|
standup,
|
|
2598
2598
|
current_status: "idle"
|
|
2599
2599
|
});
|
|
2600
2600
|
log(`[claude-scheduler] Standup posted for '${codeName}'`);
|
|
2601
2601
|
} else if (TASK_UPDATE_TEMPLATES.has(templateId)) {
|
|
2602
2602
|
await api.post("/host/agent-status", {
|
|
2603
|
-
|
|
2603
|
+
agent_code_name: codeName,
|
|
2604
2604
|
current_tasks: output.slice(0, 2e3)
|
|
2605
2605
|
});
|
|
2606
2606
|
log(`[claude-scheduler] Task update posted for '${codeName}'`);
|
|
@@ -2623,6 +2623,9 @@ async function processClaudeTaskResult(codeName, agentId, templateId, output) {
|
|
|
2623
2623
|
log(`[claude-scheduler] Kanban updates posted for '${codeName}' (${kanbanUpdates.length} updates)`);
|
|
2624
2624
|
}
|
|
2625
2625
|
}
|
|
2626
|
+
if (delivery?.mode === "announce" && delivery.channel && delivery.to && output) {
|
|
2627
|
+
await sendTaskNotification(codeName, delivery.channel, delivery.to, output.slice(0, 4e3));
|
|
2628
|
+
}
|
|
2626
2629
|
} catch (err) {
|
|
2627
2630
|
log(`[claude-scheduler] Failed to post result for '${codeName}': ${err.message}`);
|
|
2628
2631
|
}
|
|
@@ -2720,7 +2723,8 @@ async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
|
|
|
2720
2723
|
delivery_mode: t.delivery_mode ?? "none",
|
|
2721
2724
|
delivery_channel: t.delivery_channel ?? null,
|
|
2722
2725
|
delivery_to: t.delivery_to ?? null,
|
|
2723
|
-
enabled: t.enabled ?? true
|
|
2726
|
+
enabled: t.enabled ?? true,
|
|
2727
|
+
triggered_at: t.triggered_at ?? null
|
|
2724
2728
|
}));
|
|
2725
2729
|
const schedulerState = syncTasksToScheduler(codeName, agent.agent_id, taskInputs);
|
|
2726
2730
|
claudeSchedulerStates.set(codeName, schedulerState);
|
|
@@ -2755,34 +2759,20 @@ async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
|
|
|
2755
2759
|
}
|
|
2756
2760
|
}
|
|
2757
2761
|
}
|
|
2758
|
-
log(`[persistent-session]
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
const markerPath = join2(markerDir, ".agt-pending-task.json");
|
|
2762
|
-
try {
|
|
2763
|
-
mkdirSync2(markerDir, { recursive: true });
|
|
2764
|
-
writeFileSync3(markerPath, JSON.stringify({
|
|
2765
|
-
agent_id: agent.agent_id,
|
|
2766
|
-
task_id: task.taskId,
|
|
2767
|
-
template_id: task.templateId,
|
|
2768
|
-
task_name: task.name,
|
|
2769
|
-
injected_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
2770
|
-
}), "utf-8");
|
|
2771
|
-
} catch (err) {
|
|
2772
|
-
log(`[persistent-session] Failed to write task marker for '${codeName}': ${err.message}`);
|
|
2773
|
-
}
|
|
2774
|
-
const injected = await injectMessage(codeName, "task", prompt, {
|
|
2775
|
-
task_id: task.taskId,
|
|
2776
|
-
template_id: task.templateId,
|
|
2777
|
-
task_name: task.name
|
|
2778
|
-
}, log);
|
|
2779
|
-
if (injected) {
|
|
2780
|
-
const updated = markTaskFired(codeName, task.taskId, "ok");
|
|
2781
|
-
claudeSchedulerStates.set(codeName, updated);
|
|
2782
|
-
log(`[persistent-session] Task '${task.name}' injected for '${codeName}', next fire at ${new Date(updated.tasks[task.taskId]?.nextFireAt ?? 0).toISOString()}`);
|
|
2783
|
-
} else {
|
|
2784
|
-
log(`[persistent-session] Task '${task.name}' injection FAILED for '${codeName}' \u2014 injectMessage returned false`);
|
|
2762
|
+
log(`[persistent-session] Firing task '${task.name}' for '${codeName}' via claude -p`);
|
|
2763
|
+
if (inFlightClaudeTasks.has(task.taskId)) {
|
|
2764
|
+
continue;
|
|
2785
2765
|
}
|
|
2766
|
+
if ((claudeTaskConcurrency.get(codeName) ?? 0) >= MAX_CLAUDE_CONCURRENCY) {
|
|
2767
|
+
break;
|
|
2768
|
+
}
|
|
2769
|
+
inFlightClaudeTasks.add(task.taskId);
|
|
2770
|
+
claudeTaskConcurrency.set(codeName, (claudeTaskConcurrency.get(codeName) ?? 0) + 1);
|
|
2771
|
+
executeAndProcessClaudeTask(codeName, agent.agent_id, task, prompt).catch(() => {
|
|
2772
|
+
}).finally(() => {
|
|
2773
|
+
inFlightClaudeTasks.delete(task.taskId);
|
|
2774
|
+
claudeTaskConcurrency.set(codeName, Math.max(0, (claudeTaskConcurrency.get(codeName) ?? 1) - 1));
|
|
2775
|
+
});
|
|
2786
2776
|
break;
|
|
2787
2777
|
}
|
|
2788
2778
|
}
|
|
@@ -3000,23 +2990,51 @@ async function processDirectChatMessage(agent, msg) {
|
|
|
3000
2990
|
try {
|
|
3001
2991
|
let reply;
|
|
3002
2992
|
if (fw === "claude-code") {
|
|
3003
|
-
const { getProjectDir: ccProjectDir } = await import("../claude-scheduler-
|
|
2993
|
+
const { getProjectDir: ccProjectDir } = await import("../claude-scheduler-VFBZFE6U.js");
|
|
3004
2994
|
const projDir = ccProjectDir(agent.codeName);
|
|
2995
|
+
const mcpConfigPath = join2(projDir, ".mcp.json");
|
|
2996
|
+
const channelsConfigPath = join2(projDir, ".mcp-channels.json");
|
|
2997
|
+
const serverNames = [];
|
|
2998
|
+
for (const p of [mcpConfigPath, channelsConfigPath]) {
|
|
2999
|
+
if (existsSync2(p)) {
|
|
3000
|
+
try {
|
|
3001
|
+
const d = JSON.parse(readFileSync3(p, "utf-8"));
|
|
3002
|
+
if (d.mcpServers) serverNames.push(...Object.keys(d.mcpServers));
|
|
3003
|
+
} catch {
|
|
3004
|
+
}
|
|
3005
|
+
}
|
|
3006
|
+
}
|
|
3007
|
+
const mcpPatterns = serverNames.map((n) => `mcp__${n.replace(/-/g, "_")}__*`);
|
|
3008
|
+
const allowedTools = [...mcpPatterns, "Bash", "Read", "Write", "Edit", "Grep", "Glob"].join(",");
|
|
3005
3009
|
const chatArgs = [
|
|
3006
3010
|
"-p",
|
|
3007
3011
|
msg.content,
|
|
3008
3012
|
"--output-format",
|
|
3009
3013
|
"text",
|
|
3010
3014
|
"--mcp-config",
|
|
3011
|
-
|
|
3015
|
+
mcpConfigPath,
|
|
3016
|
+
"--strict-mcp-config",
|
|
3012
3017
|
"--allowedTools",
|
|
3013
|
-
|
|
3018
|
+
allowedTools
|
|
3014
3019
|
];
|
|
3020
|
+
if (existsSync2(channelsConfigPath)) chatArgs.push("--mcp-config", channelsConfigPath);
|
|
3015
3021
|
const chatClaudeMd = join2(projDir, "CLAUDE.md");
|
|
3016
3022
|
if (existsSync2(chatClaudeMd)) {
|
|
3017
3023
|
chatArgs.push("--system-prompt-file", chatClaudeMd);
|
|
3018
3024
|
}
|
|
3019
|
-
const
|
|
3025
|
+
const envIntPath = join2(projDir, ".env.integrations");
|
|
3026
|
+
const childEnv = { ...process.env };
|
|
3027
|
+
if (existsSync2(envIntPath)) {
|
|
3028
|
+
try {
|
|
3029
|
+
for (const line of readFileSync3(envIntPath, "utf-8").split("\n")) {
|
|
3030
|
+
if (!line || line.startsWith("#") || !line.includes("=")) continue;
|
|
3031
|
+
const eqIdx = line.indexOf("=");
|
|
3032
|
+
childEnv[line.slice(0, eqIdx)] = line.slice(eqIdx + 1);
|
|
3033
|
+
}
|
|
3034
|
+
} catch {
|
|
3035
|
+
}
|
|
3036
|
+
}
|
|
3037
|
+
const { stdout } = await execFilePromiseLong("claude", chatArgs, { cwd: projDir, stdin: "ignore", env: childEnv });
|
|
3020
3038
|
reply = stdout.trim() || "[No response from agent]";
|
|
3021
3039
|
} else {
|
|
3022
3040
|
const { stdout } = await execFilePromiseLong("openclaw", [
|
|
@@ -3419,7 +3437,8 @@ async function execFilePromiseLong(cmd, args, opts) {
|
|
|
3419
3437
|
return new Promise((resolve, reject) => {
|
|
3420
3438
|
const child = sp(cmd, args, {
|
|
3421
3439
|
cwd: opts?.cwd,
|
|
3422
|
-
stdio: [opts?.stdin === "ignore" ? "ignore" : "pipe", "pipe", "pipe"]
|
|
3440
|
+
stdio: [opts?.stdin === "ignore" ? "ignore" : "pipe", "pipe", "pipe"],
|
|
3441
|
+
...opts?.env ? { env: opts.env } : {}
|
|
3423
3442
|
});
|
|
3424
3443
|
let stdout = "";
|
|
3425
3444
|
let stderr = "";
|