@integrity-labs/agt-cli 0.16.6 → 0.16.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 +3 -3
- package/dist/{chunk-PBHUXNV6.js → chunk-IP7B4PSD.js} +1 -1
- package/dist/{chunk-M6FSTVGG.js → chunk-TS7W4YNI.js} +3 -4
- package/dist/{chunk-M6FSTVGG.js.map → chunk-TS7W4YNI.js.map} +1 -1
- package/dist/{claude-scheduler-CLSMSF5W.js → claude-scheduler-XHJS2MZV.js} +2 -2
- package/dist/lib/manager-worker.js +11 -9
- package/dist/lib/manager-worker.js.map +1 -1
- package/package.json +1 -1
- /package/dist/{chunk-PBHUXNV6.js.map → chunk-IP7B4PSD.js.map} +0 -0
- /package/dist/{claude-scheduler-CLSMSF5W.js.map → claude-scheduler-XHJS2MZV.js.map} +0 -0
package/dist/bin/agt.js
CHANGED
|
@@ -48,7 +48,7 @@ import {
|
|
|
48
48
|
success,
|
|
49
49
|
table,
|
|
50
50
|
warn
|
|
51
|
-
} from "../chunk-
|
|
51
|
+
} from "../chunk-IP7B4PSD.js";
|
|
52
52
|
|
|
53
53
|
// src/bin/agt.ts
|
|
54
54
|
import { join as join10 } from "path";
|
|
@@ -3732,7 +3732,7 @@ import { execFileSync, execSync } from "child_process";
|
|
|
3732
3732
|
import { existsSync as existsSync5, realpathSync } from "fs";
|
|
3733
3733
|
import chalk17 from "chalk";
|
|
3734
3734
|
import ora15 from "ora";
|
|
3735
|
-
var cliVersion = true ? "0.16.
|
|
3735
|
+
var cliVersion = true ? "0.16.8" : "dev";
|
|
3736
3736
|
async function fetchLatestVersion() {
|
|
3737
3737
|
const host2 = getHost();
|
|
3738
3738
|
if (!host2) return null;
|
|
@@ -4190,7 +4190,7 @@ function handleError(err) {
|
|
|
4190
4190
|
}
|
|
4191
4191
|
|
|
4192
4192
|
// src/bin/agt.ts
|
|
4193
|
-
var cliVersion2 = true ? "0.16.
|
|
4193
|
+
var cliVersion2 = true ? "0.16.8" : "dev";
|
|
4194
4194
|
var program = new Command();
|
|
4195
4195
|
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");
|
|
4196
4196
|
program.hook("preAction", (thisCommand) => {
|
|
@@ -132,14 +132,13 @@ function syncTasksToScheduler(codeName, agentId, tasks) {
|
|
|
132
132
|
saveSchedulerState(codeName, state);
|
|
133
133
|
return state;
|
|
134
134
|
}
|
|
135
|
-
function getReadyTasks(state) {
|
|
135
|
+
function getReadyTasks(state, inFlightTaskIds) {
|
|
136
136
|
const now = Date.now();
|
|
137
137
|
const ready = Object.values(state.tasks).filter((t) => {
|
|
138
138
|
if (!t.enabled) return false;
|
|
139
|
+
if (inFlightTaskIds?.has(t.taskId)) return false;
|
|
139
140
|
if (t.triggeredAt) {
|
|
140
141
|
const triggerReady = !t.lastFireAt || t.triggeredAt > t.lastFireAt;
|
|
141
|
-
if (!triggerReady) {
|
|
142
|
-
}
|
|
143
142
|
if (triggerReady) return true;
|
|
144
143
|
}
|
|
145
144
|
return t.nextFireAt !== null && t.nextFireAt <= now;
|
|
@@ -192,4 +191,4 @@ export {
|
|
|
192
191
|
findTaskByTemplate,
|
|
193
192
|
getProjectDir
|
|
194
193
|
};
|
|
195
|
-
//# sourceMappingURL=chunk-
|
|
194
|
+
//# sourceMappingURL=chunk-TS7W4YNI.js.map
|
|
@@ -1 +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 /** ENG-4422 §4: JSONB DeliveryTarget (or null). Legacy string form is gone\n * after the migration; in-memory shape is the structured object. */\n deliveryTo: unknown | 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 /** ENG-4422 §4: JSONB DeliveryTarget or null (post-migration). */\n delivery_to: unknown | 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 if (isNaN(ts)) return null;\n // If the 'at' timestamp is in the past and afterMs is set (meaning we already\n // fired), return null to prevent re-firing on state rebuild.\n if (afterMs && ts <= afterMs) return null;\n return ts;\n }\n\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// State persistence\n// ---------------------------------------------------------------------------\n\nfunction getStateDir(codeName: string): string {\n // ENG-4418: unified agent dir — scheduler state lives at the agent root\n // alongside other runtime state files (plugins.json, schedules.json,\n // registration.json). Legacy ~/.augmented/<codeName>/claudecode/scheduler-state.json\n // is migrated up by claudeCodeAdapter.getAgentDir on first poll.\n return join(homedir(), '.augmented', codeName);\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":";AAQA,SAAS,YAAY,WAAW,cAAc,YAAY,qBAAqB;AAC/E,SAAS,YAAY;AACrB,SAAS,eAAe;AACxB,SAAS,YAAY;AA6DrB,SAAS,gBAAgB,eAAsC;AAC7D,MAAI,CAAC,cAAe,QAAO,KAAK;AAChC,QAAM,QAAQ,cAAc,MAAM,2BAA2B;AAC7D,MAAI,CAAC,MAAO,QAAO,KAAK;AACxB,QAAM,QAAQ,SAAS,MAAM,CAAC,GAAI,EAAE;AACpC,QAAM,OAAO,MAAM,CAAC,EAAG,YAAY;AACnC,MAAI,SAAS,OAAO,SAAS,KAAM,QAAO,QAAQ,KAAK;AACvD,MAAI,SAAS,IAAK,QAAO,QAAQ,KAAK,KAAK;AAC3C,SAAO,QAAQ;AACjB;AAMO,SAAS,gBACd,MACA,MACA,OACA,IACA,UACA,SACe;AACf,QAAM,MAAM,WAAW,KAAK,IAAI;AAEhC,MAAI,SAAS,UAAU,MAAM;AAC3B,QAAI;AACF,YAAM,OAAO,IAAI,KAAK,MAAM,EAAE,UAAU,YAAY,OAAU,CAAC;AAC/D,YAAM,OAAO,KAAK,QAAQ,IAAI,KAAK,GAAG,CAAC;AACvC,aAAO,OAAO,KAAK,QAAQ,IAAI;AAAA,IACjC,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAEA,MAAI,SAAS,SAAS;AACpB,UAAM,aAAa,gBAAgB,KAAK;AACxC,WAAO,MAAM;AAAA,EACf;AAEA,MAAI,SAAS,QAAQ,IAAI;AACvB,UAAM,KAAK,IAAI,KAAK,EAAE,EAAE,QAAQ;AAChC,QAAI,MAAM,EAAE,EAAG,QAAO;AAGtB,QAAI,WAAW,MAAM,QAAS,QAAO;AACrC,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAMA,SAAS,YAAY,UAA0B;AAK7C,SAAO,KAAK,QAAQ,GAAG,cAAc,QAAQ;AAC/C;AAEA,SAAS,aAAa,UAA0B;AAC9C,SAAO,KAAK,YAAY,QAAQ,GAAG,sBAAsB;AAC3D;AAEO,SAAS,mBAAmB,UAAkC;AACnE,QAAM,OAAO,aAAa,QAAQ;AAClC,MAAI,WAAW,IAAI,GAAG;AACpB,QAAI;AACF,aAAO,KAAK,MAAM,aAAa,MAAM,OAAO,CAAC;AAAA,IAC/C,QAAQ;AAAA,IAAgC;AAAA,EAC1C;AACA,SAAO,EAAE,SAAS,GAAG,OAAO,CAAC,GAAG,YAAW,oBAAI,KAAK,GAAE,YAAY,EAAE;AACtE;AAEO,SAAS,mBAAmB,UAAkB,OAA6B;AAChF,QAAM,MAAM,YAAY,QAAQ;AAChC,YAAU,KAAK,EAAE,WAAW,KAAK,CAAC;AAClC,QAAM,aAAY,oBAAI,KAAK,GAAE,YAAY;AACzC,QAAM,OAAO,aAAa,QAAQ;AAClC,QAAM,UAAU,OAAO;AACvB,gBAAc,SAAS,KAAK,UAAU,OAAO,MAAM,CAAC,CAAC;AACrD,aAAW,SAAS,IAAI;AAC1B;AAMO,SAAS,qBACd,UACA,SACA,OACgB;AAChB,QAAM,QAAQ,mBAAmB,QAAQ;AACzC,QAAM,aAAa,IAAI,IAAI,MAAM,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;AAGjD,aAAW,MAAM,OAAO,KAAK,MAAM,KAAK,GAAG;AACzC,QAAI,CAAC,WAAW,IAAI,EAAE,GAAG;AACvB,aAAO,MAAM,MAAM,EAAE;AAAA,IACvB;AAAA,EACF;AAGA,aAAW,KAAK,OAAO;AACrB,UAAM,WAAW,MAAM,MAAM,EAAE,EAAE;AACjC,QAAI,UAAU;AAIZ,YAAM,kBACJ,SAAS,iBAAiB,EAAE,iBAC5B,SAAS,iBAAiB,EAAE,iBAC5B,SAAS,kBAAkB,EAAE,kBAC7B,SAAS,eAAe,EAAE,eAC1B,SAAS,aAAa,EAAE;AAG1B,eAAS,OAAO,EAAE;AAClB,eAAS,aAAa,EAAE;AACxB,eAAS,eAAe,EAAE;AAC1B,eAAS,eAAe,EAAE;AAC1B,eAAS,gBAAgB,EAAE;AAC3B,eAAS,aAAa,EAAE;AACxB,eAAS,WAAW,EAAE;AACtB,eAAS,SAAS,EAAE;AACpB,eAAS,gBAAgB,EAAE;AAC3B,eAAS,eAAe,EAAE;AAC1B,eAAS,kBAAkB,EAAE;AAC7B,eAAS,aAAa,EAAE;AACxB,eAAS,UAAU,EAAE;AACrB,UAAI,EAAE,aAAc,UAAS,cAAc,IAAI,KAAK,EAAE,YAAY,EAAE,QAAQ;AAC5E,UAAI,iBAAiB;AACnB,iBAAS,aAAa;AAAA,UACpB,EAAE;AAAA,UAAe,EAAE;AAAA,UAAe,EAAE;AAAA,UAAgB,EAAE;AAAA,UACtD,EAAE;AAAA,UAAU,SAAS,cAAc;AAAA,QACrC;AAAA,MACF;AAAA,IACF,OAAO;AAEL,YAAM,MAAM,EAAE,EAAE,IAAI;AAAA,QAClB,QAAQ,EAAE;AAAA,QACV,YAAY,EAAE;AAAA,QACd,MAAM,EAAE;AAAA,QACR,eAAe;AAAA,QACf;AAAA,QACA,cAAc,EAAE;AAAA,QAChB,cAAc,EAAE;AAAA,QAChB,eAAe,EAAE;AAAA,QACjB,YAAY,EAAE;AAAA,QACd,UAAU,EAAE;AAAA,QACZ,QAAQ,EAAE;AAAA,QACV,eAAe,EAAE;AAAA,QACjB,cAAc,EAAE;AAAA,QAChB,iBAAiB,EAAE;AAAA,QACnB,YAAY,EAAE;AAAA,QACd,SAAS,EAAE;AAAA,QACX,aAAa,EAAE,eAAe,IAAI,KAAK,EAAE,YAAY,EAAE,QAAQ,IAAI;AAAA,QACnE,YAAY;AAAA,UACV,EAAE;AAAA,UAAe,EAAE;AAAA,UAAe,EAAE;AAAA,UAAgB,EAAE;AAAA,UAAa,EAAE;AAAA,QACvE;AAAA,QACA,YAAY;AAAA,QACZ,YAAY;AAAA,QACZ,YAAY;AAAA,MACd;AAAA,IACF;AAAA,EACF;AAEA,qBAAmB,UAAU,KAAK;AAClC,SAAO;AACT;AAMO,SAAS,cAAc,OAA6C;AACzE,QAAM,MAAM,KAAK,IAAI;AACrB,QAAM,QAAQ,OAAO,OAAO,MAAM,KAAK,EAAE,OAAO,CAAC,MAAM;AACrD,QAAI,CAAC,EAAE,QAAS,QAAO;AAEvB,QAAI,EAAE,aAAa;AACjB,YAAM,eAAe,CAAC,EAAE,cAAc,EAAE,cAAc,EAAE;AACxD,UAAI,CAAC,cAAc;AAAA,MAEnB;AACA,UAAI,aAAc,QAAO;AAAA,IAC3B;AAEA,WAAO,EAAE,eAAe,QAAQ,EAAE,cAAc;AAAA,EAClD,CAAC;AAED,QAAM,OAAO,oBAAI,IAAY;AAC7B,SAAO,MAAM,OAAO,CAAC,MAAM;AACzB,QAAI,KAAK,IAAI,EAAE,UAAU,EAAG,QAAO;AACnC,SAAK,IAAI,EAAE,UAAU;AACrB,WAAO;AAAA,EACT,CAAC;AACH;AAMO,SAAS,cACd,UACA,QACA,QACgB;AAChB,QAAM,QAAQ,mBAAmB,QAAQ;AACzC,QAAM,OAAO,MAAM,MAAM,MAAM;AAC/B,MAAI,CAAC,KAAM,QAAO;AAElB,OAAK,aAAa,KAAK,IAAI;AAC3B,OAAK,aAAa;AAClB,OAAK;AAGL,MAAI,KAAK,iBAAiB,MAAM;AAE9B,SAAK,aAAa;AAAA,EACpB,OAAO;AACL,SAAK,aAAa;AAAA,MAChB,KAAK;AAAA,MAAc,KAAK;AAAA,MAAc,KAAK;AAAA,MAAe,KAAK;AAAA,MAC/D,KAAK;AAAA,MAAU,KAAK;AAAA,IACtB;AAAA,EACF;AAEA,qBAAmB,UAAU,KAAK;AAClC,SAAO;AACT;AAMO,SAAS,mBAAmB,OAAuB,YAAoD;AAC5G,SAAO,OAAO,OAAO,MAAM,KAAK,EAAE;AAAA,IAChC,CAAC,MAAM,EAAE,eAAe,cAAc,EAAE;AAAA,EAC1C;AACF;AAEO,SAAS,cAAc,UAA0B;AACtD,SAAO,KAAK,QAAQ,GAAG,cAAc,UAAU,SAAS;AAC1D;","names":[]}
|
|
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 /** ENG-4422 §4: JSONB DeliveryTarget (or null). Legacy string form is gone\n * after the migration; in-memory shape is the structured object. */\n deliveryTo: unknown | 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 /** ENG-4422 §4: JSONB DeliveryTarget or null (post-migration). */\n delivery_to: unknown | 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 if (isNaN(ts)) return null;\n // If the 'at' timestamp is in the past and afterMs is set (meaning we already\n // fired), return null to prevent re-firing on state rebuild.\n if (afterMs && ts <= afterMs) return null;\n return ts;\n }\n\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// State persistence\n// ---------------------------------------------------------------------------\n\nfunction getStateDir(codeName: string): string {\n // ENG-4418: unified agent dir — scheduler state lives at the agent root\n // alongside other runtime state files (plugins.json, schedules.json,\n // registration.json). Legacy ~/.augmented/<codeName>/claudecode/scheduler-state.json\n // is migrated up by claudeCodeAdapter.getAgentDir on first poll.\n return join(homedir(), '.augmented', codeName);\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\n// ENG-4675: pass `inFlightTaskIds` so the manager's poll loop doesn't\n// re-detect tasks that already have a `claude -p` subprocess running.\n// Without this, every supervisor tick (~17s) saw the manual trigger as\n// still ready (because lastFireAt is only set on completion, not on\n// fire start), spamming \"N ready task(s)\" + \"Firing task\" log lines that\n// looked like duplicate fires. The in-flight guard at the spawn site\n// prevented actual duplicate subprocesses, but the log was misleading.\nexport function getReadyTasks(\n state: SchedulerState,\n inFlightTaskIds?: ReadonlySet<string>,\n): SchedulerTaskState[] {\n const now = Date.now();\n const ready = Object.values(state.tasks).filter((t) => {\n if (!t.enabled) return false;\n if (inFlightTaskIds?.has(t.taskId)) 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) 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-TS7W4YNI.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-XHJS2MZV.js.map
|
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
resolveChannels,
|
|
23
23
|
resolveDmTarget,
|
|
24
24
|
wrapScheduledTaskPrompt
|
|
25
|
-
} from "../chunk-
|
|
25
|
+
} from "../chunk-IP7B4PSD.js";
|
|
26
26
|
import {
|
|
27
27
|
findTaskByTemplate,
|
|
28
28
|
getProjectDir,
|
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
loadSchedulerState,
|
|
31
31
|
markTaskFired,
|
|
32
32
|
syncTasksToScheduler
|
|
33
|
-
} from "../chunk-
|
|
33
|
+
} from "../chunk-TS7W4YNI.js";
|
|
34
34
|
import {
|
|
35
35
|
buildAllowedTools,
|
|
36
36
|
getLastFailureContext,
|
|
@@ -1484,7 +1484,7 @@ function clearAgentCaches(agentId, codeName) {
|
|
|
1484
1484
|
var cachedFrameworkVersion = null;
|
|
1485
1485
|
var lastVersionCheckAt = 0;
|
|
1486
1486
|
var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
|
|
1487
|
-
var agtCliVersion = true ? "0.16.
|
|
1487
|
+
var agtCliVersion = true ? "0.16.8" : "dev";
|
|
1488
1488
|
function resolveBrewPath(execFileSync2) {
|
|
1489
1489
|
try {
|
|
1490
1490
|
const out = execFileSync2("which", ["brew"], { timeout: 5e3 }).toString().trim();
|
|
@@ -3782,10 +3782,9 @@ async function syncAndCheckClaudeScheduler(agent, tasks, boardItems, refreshData
|
|
|
3782
3782
|
claudeSchedulerStates.set(codeName, loadSchedulerState(codeName));
|
|
3783
3783
|
}
|
|
3784
3784
|
const state2 = claudeSchedulerStates.get(codeName);
|
|
3785
|
-
const ready = getReadyTasks(state2);
|
|
3785
|
+
const ready = getReadyTasks(state2, inFlightClaudeTasks);
|
|
3786
3786
|
if (ready.length === 0) return;
|
|
3787
3787
|
for (const task of ready) {
|
|
3788
|
-
if (inFlightClaudeTasks.has(task.taskId)) continue;
|
|
3789
3788
|
if ((claudeTaskConcurrency.get(codeName) ?? 0) >= MAX_CLAUDE_CONCURRENCY) break;
|
|
3790
3789
|
if (KANBAN_WORK_TEMPLATES.has(task.templateId) && !hasActionableItems(boardItems)) {
|
|
3791
3790
|
log(`[claude-scheduler] Skipping '${task.name}' for '${codeName}' \u2014 board is empty`);
|
|
@@ -3898,7 +3897,8 @@ async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
|
|
|
3898
3897
|
"--mcp-config",
|
|
3899
3898
|
mcpConfigPath,
|
|
3900
3899
|
"--strict-mcp-config",
|
|
3901
|
-
"--
|
|
3900
|
+
"--permission-mode",
|
|
3901
|
+
"auto",
|
|
3902
3902
|
"--allowedTools",
|
|
3903
3903
|
allowedTools
|
|
3904
3904
|
];
|
|
@@ -4243,7 +4243,7 @@ ${truncateForLog(ctx.tail)}` : `; pane_tail_hash=sha256:${createHash("sha256").u
|
|
|
4243
4243
|
}
|
|
4244
4244
|
const state2 = claudeSchedulerStates.get(codeName);
|
|
4245
4245
|
if (state2) {
|
|
4246
|
-
const ready = getReadyTasks(state2);
|
|
4246
|
+
const ready = getReadyTasks(state2, inFlightClaudeTasks);
|
|
4247
4247
|
if (ready.length > 0) {
|
|
4248
4248
|
log(`[persistent-session] ${ready.length} ready task(s) for '${codeName}': ${ready.map((t) => `${t.name}(next=${t.nextFireAt ? new Date(t.nextFireAt).toISOString() : "null"})`).join(", ")}`);
|
|
4249
4249
|
}
|
|
@@ -4273,7 +4273,6 @@ ${truncateForLog(ctx.tail)}` : `; pane_tail_hash=sha256:${createHash("sha256").u
|
|
|
4273
4273
|
}
|
|
4274
4274
|
}
|
|
4275
4275
|
}
|
|
4276
|
-
log(`[persistent-session] Firing task '${task.name}' for '${codeName}' via claude -p`);
|
|
4277
4276
|
if (inFlightClaudeTasks.has(task.taskId)) {
|
|
4278
4277
|
continue;
|
|
4279
4278
|
}
|
|
@@ -4282,6 +4281,7 @@ ${truncateForLog(ctx.tail)}` : `; pane_tail_hash=sha256:${createHash("sha256").u
|
|
|
4282
4281
|
}
|
|
4283
4282
|
inFlightClaudeTasks.add(task.taskId);
|
|
4284
4283
|
claudeTaskConcurrency.set(codeName, (claudeTaskConcurrency.get(codeName) ?? 0) + 1);
|
|
4284
|
+
log(`[persistent-session] Firing task '${task.name}' for '${codeName}' via claude -p`);
|
|
4285
4285
|
executeAndProcessClaudeTask(codeName, agent.agent_id, task, prompt).catch(() => {
|
|
4286
4286
|
}).finally(() => {
|
|
4287
4287
|
inFlightClaudeTasks.delete(task.taskId);
|
|
@@ -4565,7 +4565,7 @@ async function processDirectChatMessage(agent, msg) {
|
|
|
4565
4565
|
try {
|
|
4566
4566
|
let reply;
|
|
4567
4567
|
if (fw === "claude-code") {
|
|
4568
|
-
const { getProjectDir: ccProjectDir } = await import("../claude-scheduler-
|
|
4568
|
+
const { getProjectDir: ccProjectDir } = await import("../claude-scheduler-XHJS2MZV.js");
|
|
4569
4569
|
const projDir = ccProjectDir(agent.codeName);
|
|
4570
4570
|
const mcpConfigPath = join3(projDir, ".mcp.json");
|
|
4571
4571
|
const serverNames = [];
|
|
@@ -4585,6 +4585,8 @@ async function processDirectChatMessage(agent, msg) {
|
|
|
4585
4585
|
"--mcp-config",
|
|
4586
4586
|
mcpConfigPath,
|
|
4587
4587
|
"--strict-mcp-config",
|
|
4588
|
+
"--permission-mode",
|
|
4589
|
+
"auto",
|
|
4588
4590
|
"--allowedTools",
|
|
4589
4591
|
allowedTools
|
|
4590
4592
|
];
|