@integrity-labs/agt-cli 0.8.5 → 0.8.8
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 +114 -79
- 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.8" : "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.8" : "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";
|
|
@@ -429,7 +429,7 @@ function spawnSession(config2, session) {
|
|
|
429
429
|
"Skill"
|
|
430
430
|
].join(",");
|
|
431
431
|
args.push("--allowedTools", allowedTools);
|
|
432
|
-
let envPrefix = "
|
|
432
|
+
let envPrefix = "";
|
|
433
433
|
const envIntegrationsPath = join(projectDir, ".env.integrations");
|
|
434
434
|
if (existsSync(envIntegrationsPath)) {
|
|
435
435
|
try {
|
|
@@ -440,7 +440,7 @@ function spawnSession(config2, session) {
|
|
|
440
440
|
const value = line.slice(eqIdx + 1);
|
|
441
441
|
return `${key}=${JSON.stringify(value)}`;
|
|
442
442
|
}).join(" ");
|
|
443
|
-
if (envVars) envPrefix =
|
|
443
|
+
if (envVars) envPrefix = `${envVars} `;
|
|
444
444
|
} catch {
|
|
445
445
|
}
|
|
446
446
|
}
|
|
@@ -541,30 +541,17 @@ async function injectMessage(codeName, type, content, meta, log2) {
|
|
|
541
541
|
mkdirSync(tmpDir, { recursive: true });
|
|
542
542
|
const tmpFile = join(tmpDir, ".agt-inject-prompt.txt");
|
|
543
543
|
writeFileSync2(tmpFile, text);
|
|
544
|
-
_log(`[inject] acpx exec: cwd=${projectDir}, file=${tmpFile}
|
|
545
|
-
const
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
});
|
|
550
|
-
let stderr = "";
|
|
551
|
-
child.stderr?.on("data", (d) => {
|
|
552
|
-
stderr += d.toString();
|
|
553
|
-
});
|
|
554
|
-
child.on("error", (err) => {
|
|
555
|
-
_log(`[inject] acpx spawn error for '${codeName}': ${err.message}`);
|
|
556
|
-
resolve(false);
|
|
557
|
-
});
|
|
558
|
-
child.on("close", (code) => {
|
|
559
|
-
if (code === 0) {
|
|
560
|
-
resolve(true);
|
|
561
|
-
} else {
|
|
562
|
-
_log(`[inject] acpx exec exited ${code} for '${codeName}': ${stderr.trim()}`);
|
|
563
|
-
resolve(false);
|
|
564
|
-
}
|
|
565
|
-
});
|
|
544
|
+
_log(`[inject] acpx exec (fire-and-forget): cwd=${projectDir}, file=${tmpFile}`);
|
|
545
|
+
const child = spawn(acpx, ["claude", "exec", "-f", tmpFile], {
|
|
546
|
+
cwd: projectDir,
|
|
547
|
+
stdio: "ignore",
|
|
548
|
+
detached: true
|
|
566
549
|
});
|
|
567
|
-
|
|
550
|
+
child.on("error", (err) => {
|
|
551
|
+
_log(`[inject] acpx spawn error for '${codeName}': ${err.message}`);
|
|
552
|
+
});
|
|
553
|
+
child.unref();
|
|
554
|
+
return true;
|
|
568
555
|
} catch (err) {
|
|
569
556
|
_log(`[inject] acpx exec failed for '${codeName}': ${err.message}`);
|
|
570
557
|
}
|
|
@@ -653,31 +640,27 @@ function writeAcpxConfig(config2) {
|
|
|
653
640
|
if (existsSync(claudeMdPath)) claudeArgs.push("--system-prompt-file", claudeMdPath);
|
|
654
641
|
claudeArgs.push("--allow-dangerously-skip-permissions");
|
|
655
642
|
claudeArgs.push("--dangerously-skip-permissions");
|
|
643
|
+
claudeArgs.push("--strict-mcp-config");
|
|
656
644
|
const mcpServerNames2 = collectMcpServerNames(mcpConfigPath, channelsConfigPath);
|
|
657
645
|
const mcpPatterns2 = mcpServerNames2.map((name) => `mcp__${name.replace(/-/g, "_")}__*`);
|
|
658
646
|
const allowedTools2 = [...mcpPatterns2, "Bash", "Read", "Write", "Edit", "Grep", "Glob", "Agent", "Skill"].join(",");
|
|
659
647
|
claudeArgs.push("--allowedTools", allowedTools2);
|
|
660
|
-
|
|
648
|
+
const acpCmd = `npx -y @agentclientprotocol/claude-agent-acp ${claudeArgs.map((a) => a.includes(" ") || a.includes("*") ? JSON.stringify(a) : a).join(" ")}`;
|
|
661
649
|
const envIntegrationsPath = join(projectDir, ".env.integrations");
|
|
650
|
+
const wrapperPath = join(projectDir, ".claude", "acpx-agent.sh");
|
|
651
|
+
const wrapperLines = ["#!/usr/bin/env bash"];
|
|
662
652
|
if (existsSync(envIntegrationsPath)) {
|
|
663
|
-
|
|
664
|
-
const envContent = readFileSync2(envIntegrationsPath, "utf-8");
|
|
665
|
-
const envVars = envContent.split("\n").filter((line) => line && !line.startsWith("#") && line.includes("=")).map((line) => {
|
|
666
|
-
const eqIdx = line.indexOf("=");
|
|
667
|
-
const key = line.slice(0, eqIdx);
|
|
668
|
-
const value = line.slice(eqIdx + 1);
|
|
669
|
-
return `${key}=${JSON.stringify(value)}`;
|
|
670
|
-
}).join(" ");
|
|
671
|
-
if (envVars) envPrefix = `CLAUDE_CODE_SIMPLE=1 ${envVars} `;
|
|
672
|
-
} catch {
|
|
673
|
-
}
|
|
653
|
+
wrapperLines.push(`set -a`, `source ${JSON.stringify(envIntegrationsPath)}`, `set +a`);
|
|
674
654
|
}
|
|
655
|
+
wrapperLines.push(`exec ${acpCmd}`);
|
|
656
|
+
mkdirSync(join(projectDir, ".claude"), { recursive: true });
|
|
657
|
+
writeFileSync2(wrapperPath, wrapperLines.join("\n") + "\n", { mode: 493 });
|
|
675
658
|
const acpxConfig = {
|
|
676
659
|
defaultAgent: "claude",
|
|
677
660
|
defaultPermissions: "approve-all",
|
|
678
661
|
agents: {
|
|
679
662
|
claude: {
|
|
680
|
-
command:
|
|
663
|
+
command: wrapperPath
|
|
681
664
|
}
|
|
682
665
|
}
|
|
683
666
|
};
|
|
@@ -2516,7 +2499,8 @@ async function syncAndCheckClaudeScheduler(agent, tasks, boardItems, refreshData
|
|
|
2516
2499
|
delivery_mode: t.delivery_mode ?? "none",
|
|
2517
2500
|
delivery_channel: t.delivery_channel ?? null,
|
|
2518
2501
|
delivery_to: t.delivery_to ?? null,
|
|
2519
|
-
enabled: t.enabled ?? true
|
|
2502
|
+
enabled: t.enabled ?? true,
|
|
2503
|
+
triggered_at: t.triggered_at ?? null
|
|
2520
2504
|
}));
|
|
2521
2505
|
const state3 = syncTasksToScheduler(codeName, agent.agent_id, taskInputs);
|
|
2522
2506
|
claudeSchedulerStates.set(codeName, state3);
|
|
@@ -2567,6 +2551,19 @@ async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
|
|
|
2567
2551
|
sanitizeMcpJson(mcpConfigPath, requireHost());
|
|
2568
2552
|
try {
|
|
2569
2553
|
const claudeMdPath = join2(projectDir, "CLAUDE.md");
|
|
2554
|
+
const serverNames = [];
|
|
2555
|
+
const channelsConfigPath = join2(projectDir, ".mcp-channels.json");
|
|
2556
|
+
for (const p of [mcpConfigPath, channelsConfigPath]) {
|
|
2557
|
+
if (existsSync2(p)) {
|
|
2558
|
+
try {
|
|
2559
|
+
const d = JSON.parse(readFileSync3(p, "utf-8"));
|
|
2560
|
+
if (d.mcpServers) serverNames.push(...Object.keys(d.mcpServers));
|
|
2561
|
+
} catch {
|
|
2562
|
+
}
|
|
2563
|
+
}
|
|
2564
|
+
}
|
|
2565
|
+
const mcpPatterns = serverNames.map((n) => `mcp__${n.replace(/-/g, "_")}__*`);
|
|
2566
|
+
const allowedTools = [...mcpPatterns, "Bash", "Read", "Write", "Edit", "Grep", "Glob"].join(",");
|
|
2570
2567
|
const claudeArgs = [
|
|
2571
2568
|
"-p",
|
|
2572
2569
|
prompt,
|
|
@@ -2574,23 +2571,42 @@ async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
|
|
|
2574
2571
|
"text",
|
|
2575
2572
|
"--mcp-config",
|
|
2576
2573
|
mcpConfigPath,
|
|
2574
|
+
"--strict-mcp-config",
|
|
2577
2575
|
"--allowedTools",
|
|
2578
|
-
|
|
2576
|
+
allowedTools
|
|
2579
2577
|
];
|
|
2578
|
+
if (existsSync2(channelsConfigPath)) claudeArgs.push("--mcp-config", channelsConfigPath);
|
|
2580
2579
|
if (existsSync2(claudeMdPath)) {
|
|
2581
2580
|
claudeArgs.push("--system-prompt-file", claudeMdPath);
|
|
2582
2581
|
}
|
|
2582
|
+
const childEnv = { ...process.env };
|
|
2583
|
+
const envIntPath = join2(projectDir, ".env.integrations");
|
|
2584
|
+
if (existsSync2(envIntPath)) {
|
|
2585
|
+
try {
|
|
2586
|
+
for (const line of readFileSync3(envIntPath, "utf-8").split("\n")) {
|
|
2587
|
+
if (!line || line.startsWith("#") || !line.includes("=")) continue;
|
|
2588
|
+
const eqIdx = line.indexOf("=");
|
|
2589
|
+
childEnv[line.slice(0, eqIdx)] = line.slice(eqIdx + 1);
|
|
2590
|
+
}
|
|
2591
|
+
} catch {
|
|
2592
|
+
}
|
|
2593
|
+
}
|
|
2583
2594
|
const { stdout, stderr } = await execFilePromiseLong("claude", claudeArgs, {
|
|
2584
2595
|
cwd: projectDir,
|
|
2585
2596
|
timeout: 3e5,
|
|
2586
|
-
stdin: "ignore"
|
|
2597
|
+
stdin: "ignore",
|
|
2598
|
+
env: childEnv
|
|
2587
2599
|
});
|
|
2588
2600
|
if (stderr) {
|
|
2589
2601
|
log(`[claude-scheduler] Task '${task.name}' stderr for '${codeName}': ${stderr.slice(0, 500)}`);
|
|
2590
2602
|
}
|
|
2591
2603
|
const output = stdout.trim();
|
|
2592
2604
|
log(`[claude-scheduler] Task '${task.name}' completed for '${codeName}' (${output.length} chars): ${output.slice(0, 300)}`);
|
|
2593
|
-
await processClaudeTaskResult(codeName, agentId, task.templateId, output
|
|
2605
|
+
await processClaudeTaskResult(codeName, agentId, task.templateId, output, {
|
|
2606
|
+
mode: task.deliveryMode,
|
|
2607
|
+
channel: task.deliveryChannel,
|
|
2608
|
+
to: task.deliveryTo
|
|
2609
|
+
});
|
|
2594
2610
|
const updated = markTaskFired(codeName, task.taskId, "ok");
|
|
2595
2611
|
claudeSchedulerStates.set(codeName, updated);
|
|
2596
2612
|
} catch (err) {
|
|
@@ -2600,19 +2616,19 @@ async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
|
|
|
2600
2616
|
claudeSchedulerStates.set(codeName, updated);
|
|
2601
2617
|
}
|
|
2602
2618
|
}
|
|
2603
|
-
async function processClaudeTaskResult(codeName, agentId, templateId, output) {
|
|
2619
|
+
async function processClaudeTaskResult(codeName, agentId, templateId, output, delivery) {
|
|
2604
2620
|
try {
|
|
2605
2621
|
if (STANDUP_TEMPLATES.has(templateId)) {
|
|
2606
2622
|
const standup = parseStandupSummary(output);
|
|
2607
2623
|
await api.post("/host/agent-status", {
|
|
2608
|
-
|
|
2624
|
+
agent_code_name: codeName,
|
|
2609
2625
|
standup,
|
|
2610
2626
|
current_status: "idle"
|
|
2611
2627
|
});
|
|
2612
2628
|
log(`[claude-scheduler] Standup posted for '${codeName}'`);
|
|
2613
2629
|
} else if (TASK_UPDATE_TEMPLATES.has(templateId)) {
|
|
2614
2630
|
await api.post("/host/agent-status", {
|
|
2615
|
-
|
|
2631
|
+
agent_code_name: codeName,
|
|
2616
2632
|
current_tasks: output.slice(0, 2e3)
|
|
2617
2633
|
});
|
|
2618
2634
|
log(`[claude-scheduler] Task update posted for '${codeName}'`);
|
|
@@ -2635,6 +2651,9 @@ async function processClaudeTaskResult(codeName, agentId, templateId, output) {
|
|
|
2635
2651
|
log(`[claude-scheduler] Kanban updates posted for '${codeName}' (${kanbanUpdates.length} updates)`);
|
|
2636
2652
|
}
|
|
2637
2653
|
}
|
|
2654
|
+
if (delivery?.mode === "announce" && delivery.channel && delivery.to && output) {
|
|
2655
|
+
await sendTaskNotification(codeName, delivery.channel, delivery.to, output.slice(0, 4e3));
|
|
2656
|
+
}
|
|
2638
2657
|
} catch (err) {
|
|
2639
2658
|
log(`[claude-scheduler] Failed to post result for '${codeName}': ${err.message}`);
|
|
2640
2659
|
}
|
|
@@ -2732,7 +2751,8 @@ async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
|
|
|
2732
2751
|
delivery_mode: t.delivery_mode ?? "none",
|
|
2733
2752
|
delivery_channel: t.delivery_channel ?? null,
|
|
2734
2753
|
delivery_to: t.delivery_to ?? null,
|
|
2735
|
-
enabled: t.enabled ?? true
|
|
2754
|
+
enabled: t.enabled ?? true,
|
|
2755
|
+
triggered_at: t.triggered_at ?? null
|
|
2736
2756
|
}));
|
|
2737
2757
|
const schedulerState = syncTasksToScheduler(codeName, agent.agent_id, taskInputs);
|
|
2738
2758
|
claudeSchedulerStates.set(codeName, schedulerState);
|
|
@@ -2767,34 +2787,20 @@ async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
|
|
|
2767
2787
|
}
|
|
2768
2788
|
}
|
|
2769
2789
|
}
|
|
2770
|
-
log(`[persistent-session]
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
const markerPath = join2(markerDir, ".agt-pending-task.json");
|
|
2774
|
-
try {
|
|
2775
|
-
mkdirSync2(markerDir, { recursive: true });
|
|
2776
|
-
writeFileSync3(markerPath, JSON.stringify({
|
|
2777
|
-
agent_id: agent.agent_id,
|
|
2778
|
-
task_id: task.taskId,
|
|
2779
|
-
template_id: task.templateId,
|
|
2780
|
-
task_name: task.name,
|
|
2781
|
-
injected_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
2782
|
-
}), "utf-8");
|
|
2783
|
-
} catch (err) {
|
|
2784
|
-
log(`[persistent-session] Failed to write task marker for '${codeName}': ${err.message}`);
|
|
2785
|
-
}
|
|
2786
|
-
const injected = await injectMessage(codeName, "task", prompt, {
|
|
2787
|
-
task_id: task.taskId,
|
|
2788
|
-
template_id: task.templateId,
|
|
2789
|
-
task_name: task.name
|
|
2790
|
-
}, log);
|
|
2791
|
-
if (injected) {
|
|
2792
|
-
const updated = markTaskFired(codeName, task.taskId, "ok");
|
|
2793
|
-
claudeSchedulerStates.set(codeName, updated);
|
|
2794
|
-
log(`[persistent-session] Task '${task.name}' injected for '${codeName}', next fire at ${new Date(updated.tasks[task.taskId]?.nextFireAt ?? 0).toISOString()}`);
|
|
2795
|
-
} else {
|
|
2796
|
-
log(`[persistent-session] Task '${task.name}' injection FAILED for '${codeName}' \u2014 injectMessage returned false`);
|
|
2790
|
+
log(`[persistent-session] Firing task '${task.name}' for '${codeName}' via claude -p`);
|
|
2791
|
+
if (inFlightClaudeTasks.has(task.taskId)) {
|
|
2792
|
+
continue;
|
|
2797
2793
|
}
|
|
2794
|
+
if ((claudeTaskConcurrency.get(codeName) ?? 0) >= MAX_CLAUDE_CONCURRENCY) {
|
|
2795
|
+
break;
|
|
2796
|
+
}
|
|
2797
|
+
inFlightClaudeTasks.add(task.taskId);
|
|
2798
|
+
claudeTaskConcurrency.set(codeName, (claudeTaskConcurrency.get(codeName) ?? 0) + 1);
|
|
2799
|
+
executeAndProcessClaudeTask(codeName, agent.agent_id, task, prompt).catch(() => {
|
|
2800
|
+
}).finally(() => {
|
|
2801
|
+
inFlightClaudeTasks.delete(task.taskId);
|
|
2802
|
+
claudeTaskConcurrency.set(codeName, Math.max(0, (claudeTaskConcurrency.get(codeName) ?? 1) - 1));
|
|
2803
|
+
});
|
|
2798
2804
|
break;
|
|
2799
2805
|
}
|
|
2800
2806
|
}
|
|
@@ -3012,23 +3018,51 @@ async function processDirectChatMessage(agent, msg) {
|
|
|
3012
3018
|
try {
|
|
3013
3019
|
let reply;
|
|
3014
3020
|
if (fw === "claude-code") {
|
|
3015
|
-
const { getProjectDir: ccProjectDir } = await import("../claude-scheduler-
|
|
3021
|
+
const { getProjectDir: ccProjectDir } = await import("../claude-scheduler-VFBZFE6U.js");
|
|
3016
3022
|
const projDir = ccProjectDir(agent.codeName);
|
|
3023
|
+
const mcpConfigPath = join2(projDir, ".mcp.json");
|
|
3024
|
+
const channelsConfigPath = join2(projDir, ".mcp-channels.json");
|
|
3025
|
+
const serverNames = [];
|
|
3026
|
+
for (const p of [mcpConfigPath, channelsConfigPath]) {
|
|
3027
|
+
if (existsSync2(p)) {
|
|
3028
|
+
try {
|
|
3029
|
+
const d = JSON.parse(readFileSync3(p, "utf-8"));
|
|
3030
|
+
if (d.mcpServers) serverNames.push(...Object.keys(d.mcpServers));
|
|
3031
|
+
} catch {
|
|
3032
|
+
}
|
|
3033
|
+
}
|
|
3034
|
+
}
|
|
3035
|
+
const mcpPatterns = serverNames.map((n) => `mcp__${n.replace(/-/g, "_")}__*`);
|
|
3036
|
+
const allowedTools = [...mcpPatterns, "Bash", "Read", "Write", "Edit", "Grep", "Glob"].join(",");
|
|
3017
3037
|
const chatArgs = [
|
|
3018
3038
|
"-p",
|
|
3019
3039
|
msg.content,
|
|
3020
3040
|
"--output-format",
|
|
3021
3041
|
"text",
|
|
3022
3042
|
"--mcp-config",
|
|
3023
|
-
|
|
3043
|
+
mcpConfigPath,
|
|
3044
|
+
"--strict-mcp-config",
|
|
3024
3045
|
"--allowedTools",
|
|
3025
|
-
|
|
3046
|
+
allowedTools
|
|
3026
3047
|
];
|
|
3048
|
+
if (existsSync2(channelsConfigPath)) chatArgs.push("--mcp-config", channelsConfigPath);
|
|
3027
3049
|
const chatClaudeMd = join2(projDir, "CLAUDE.md");
|
|
3028
3050
|
if (existsSync2(chatClaudeMd)) {
|
|
3029
3051
|
chatArgs.push("--system-prompt-file", chatClaudeMd);
|
|
3030
3052
|
}
|
|
3031
|
-
const
|
|
3053
|
+
const envIntPath = join2(projDir, ".env.integrations");
|
|
3054
|
+
const childEnv = { ...process.env };
|
|
3055
|
+
if (existsSync2(envIntPath)) {
|
|
3056
|
+
try {
|
|
3057
|
+
for (const line of readFileSync3(envIntPath, "utf-8").split("\n")) {
|
|
3058
|
+
if (!line || line.startsWith("#") || !line.includes("=")) continue;
|
|
3059
|
+
const eqIdx = line.indexOf("=");
|
|
3060
|
+
childEnv[line.slice(0, eqIdx)] = line.slice(eqIdx + 1);
|
|
3061
|
+
}
|
|
3062
|
+
} catch {
|
|
3063
|
+
}
|
|
3064
|
+
}
|
|
3065
|
+
const { stdout } = await execFilePromiseLong("claude", chatArgs, { cwd: projDir, stdin: "ignore", env: childEnv });
|
|
3032
3066
|
reply = stdout.trim() || "[No response from agent]";
|
|
3033
3067
|
} else {
|
|
3034
3068
|
const { stdout } = await execFilePromiseLong("openclaw", [
|
|
@@ -3431,7 +3465,8 @@ async function execFilePromiseLong(cmd, args, opts) {
|
|
|
3431
3465
|
return new Promise((resolve, reject) => {
|
|
3432
3466
|
const child = sp(cmd, args, {
|
|
3433
3467
|
cwd: opts?.cwd,
|
|
3434
|
-
stdio: [opts?.stdin === "ignore" ? "ignore" : "pipe", "pipe", "pipe"]
|
|
3468
|
+
stdio: [opts?.stdin === "ignore" ? "ignore" : "pipe", "pipe", "pipe"],
|
|
3469
|
+
...opts?.env ? { env: opts.env } : {}
|
|
3435
3470
|
});
|
|
3436
3471
|
let stdout = "";
|
|
3437
3472
|
let stderr = "";
|