@integrity-labs/agt-cli 0.9.10 → 0.10.1
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 +69 -12
- package/dist/bin/agt.js.map +1 -1
- package/dist/{chunk-IT34G2Y2.js → chunk-N7TRKQMT.js} +90 -11
- package/dist/chunk-N7TRKQMT.js.map +1 -0
- package/dist/{chunk-2TSCVXHE.js → chunk-QU7FBXH3.js} +4 -2
- package/dist/chunk-QU7FBXH3.js.map +1 -0
- package/dist/{claude-scheduler-VFBZFE6U.js → claude-scheduler-7PVWQHWU.js} +2 -2
- package/dist/lib/manager-worker.js +75 -5
- package/dist/lib/manager-worker.js.map +1 -1
- package/mcp/index.js +70 -0
- package/package.json +2 -1
- package/dist/chunk-2TSCVXHE.js.map +0 -1
- package/dist/chunk-IT34G2Y2.js.map +0 -1
- /package/dist/{claude-scheduler-VFBZFE6U.js.map → claude-scheduler-7PVWQHWU.js.map} +0 -0
|
@@ -30,7 +30,9 @@ function computeNextFire(kind, expr, every, at, timezone, afterMs) {
|
|
|
30
30
|
}
|
|
31
31
|
if (kind === "at" && at) {
|
|
32
32
|
const ts = new Date(at).getTime();
|
|
33
|
-
|
|
33
|
+
if (isNaN(ts)) return null;
|
|
34
|
+
if (afterMs && ts <= afterMs) return null;
|
|
35
|
+
return ts;
|
|
34
36
|
}
|
|
35
37
|
return null;
|
|
36
38
|
}
|
|
@@ -190,4 +192,4 @@ export {
|
|
|
190
192
|
findTaskByTemplate,
|
|
191
193
|
getProjectDir
|
|
192
194
|
};
|
|
193
|
-
//# sourceMappingURL=chunk-
|
|
195
|
+
//# sourceMappingURL=chunk-QU7FBXH3.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 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 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":";AAQA,SAAS,YAAY,WAAW,cAAc,YAAY,qBAAqB;AAC/E,SAAS,YAAY;AACrB,SAAS,eAAe;AACxB,SAAS,YAAY;AA0DrB,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;AAC7C,SAAO,KAAK,QAAQ,GAAG,cAAc,UAAU,YAAY;AAC7D;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":[]}
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
markTaskFired,
|
|
8
8
|
saveSchedulerState,
|
|
9
9
|
syncTasksToScheduler
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-QU7FBXH3.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-7PVWQHWU.js.map
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
provisionStopHook,
|
|
10
10
|
requireHost,
|
|
11
11
|
resolveChannels
|
|
12
|
-
} from "../chunk-
|
|
12
|
+
} from "../chunk-N7TRKQMT.js";
|
|
13
13
|
import {
|
|
14
14
|
findTaskByTemplate,
|
|
15
15
|
getProjectDir,
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
loadSchedulerState,
|
|
18
18
|
markTaskFired,
|
|
19
19
|
syncTasksToScheduler
|
|
20
|
-
} from "../chunk-
|
|
20
|
+
} from "../chunk-QU7FBXH3.js";
|
|
21
21
|
import {
|
|
22
22
|
getProjectDir as getProjectDir2,
|
|
23
23
|
injectMessage,
|
|
@@ -1591,10 +1591,11 @@ async function processAgent(agent, agentStates) {
|
|
|
1591
1591
|
const fw = agentFrameworkCache.get(agent.code_name) ?? "openclaw";
|
|
1592
1592
|
if (fw === "claude-code" && isSessionHealthy(agent.code_name)) {
|
|
1593
1593
|
const names = integrations.map((i) => i.display_name || i.definition_id).join(", ");
|
|
1594
|
-
injectMessage(agent.code_name, "system", `Your integrations have been updated. You now have access to: ${names}.
|
|
1594
|
+
injectMessage(agent.code_name, "system", `Your integrations have been updated. You now have access to: ${names}. Your .env.integrations and CLAUDE.md have been refreshed \u2014 credentials are available immediately via environment variables.`, {
|
|
1595
1595
|
task_name: "integration-update"
|
|
1596
1596
|
}, log).catch(() => {
|
|
1597
1597
|
});
|
|
1598
|
+
log(`[hot-reload] Notified '${agent.code_name}' about integration update: ${names}`);
|
|
1598
1599
|
}
|
|
1599
1600
|
const managedIntegrations = integrations.filter((i) => i.auth_type === "managed");
|
|
1600
1601
|
if (managedIntegrations.length > 0 && frameworkAdapter.writeMcpServer) {
|
|
@@ -1636,6 +1637,25 @@ async function processAgent(agent, agentStates) {
|
|
|
1636
1637
|
}
|
|
1637
1638
|
}
|
|
1638
1639
|
needsGatewayRestart = true;
|
|
1640
|
+
if (managedIntegrations.length > 0 && fw === "claude-code" && isSessionHealthy(agent.code_name)) {
|
|
1641
|
+
const mcpNames = managedIntegrations.map((i) => i.display_name || i.definition_id).join(", ");
|
|
1642
|
+
log(`[hot-reload] MCP servers changed for '${agent.code_name}': ${mcpNames} \u2014 restarting session`);
|
|
1643
|
+
const delivered = await injectMessage(
|
|
1644
|
+
agent.code_name,
|
|
1645
|
+
"system",
|
|
1646
|
+
`New MCP tool servers have been configured: ${mcpNames}. Note: MCP servers require a session restart to connect. Your manager will restart your session shortly.`,
|
|
1647
|
+
{ task_name: "mcp-update" },
|
|
1648
|
+
log
|
|
1649
|
+
).catch(() => false);
|
|
1650
|
+
const delay = delivered ? 8e3 : 3e3;
|
|
1651
|
+
if (!delivered) {
|
|
1652
|
+
log(`[hot-reload] Inject notification unconfirmed for '${agent.code_name}' \u2014 proceeding with shorter delay`);
|
|
1653
|
+
}
|
|
1654
|
+
setTimeout(() => {
|
|
1655
|
+
stopPersistentSession(agent.code_name, log);
|
|
1656
|
+
log(`[hot-reload] Session stopped for '${agent.code_name}' \u2014 will respawn with new MCP servers`);
|
|
1657
|
+
}, delay);
|
|
1658
|
+
}
|
|
1639
1659
|
}
|
|
1640
1660
|
const resolvedDefIds = new Set(integrations.map((i) => i.definition_id));
|
|
1641
1661
|
for (const capId of resolvedDefIds) {
|
|
@@ -1652,15 +1672,30 @@ async function processAgent(agent, agentStates) {
|
|
|
1652
1672
|
const skillKey = `${agent.agent_id}:${capId}`;
|
|
1653
1673
|
const prevSkillHash = knownSkillHashes.get(skillKey);
|
|
1654
1674
|
if (skillHash !== prevSkillHash) {
|
|
1675
|
+
const installedSkillNames = [];
|
|
1655
1676
|
if (frameworkAdapter.installSkillFiles && capData.skills) {
|
|
1656
1677
|
for (const skill of capData.skills) {
|
|
1657
1678
|
if (skill.files.length > 0) {
|
|
1658
1679
|
frameworkAdapter.installSkillFiles(agent.code_name, skill.id, skill.files);
|
|
1680
|
+
installedSkillNames.push(skill.name || skill.id);
|
|
1659
1681
|
log(`Installed skill '${skill.id}' for '${agent.code_name}' (${skill.files.length} file(s))`);
|
|
1660
1682
|
}
|
|
1661
1683
|
}
|
|
1662
1684
|
}
|
|
1663
1685
|
knownSkillHashes.set(skillKey, skillHash);
|
|
1686
|
+
const agentFw2 = agentFrameworkCache.get(agent.code_name) ?? "openclaw";
|
|
1687
|
+
if (agentFw2 === "claude-code" && installedSkillNames.length > 0 && isSessionHealthy(agent.code_name)) {
|
|
1688
|
+
const names = installedSkillNames.join(", ");
|
|
1689
|
+
injectMessage(
|
|
1690
|
+
agent.code_name,
|
|
1691
|
+
"system",
|
|
1692
|
+
`New skills installed: ${names}. These are available immediately \u2014 Claude Code loads skills on demand from .claude/skills/.`,
|
|
1693
|
+
{ task_name: "skill-update" },
|
|
1694
|
+
log
|
|
1695
|
+
).catch(() => {
|
|
1696
|
+
});
|
|
1697
|
+
log(`[hot-reload] Notified '${agent.code_name}' about new skills: ${names}`);
|
|
1698
|
+
}
|
|
1664
1699
|
}
|
|
1665
1700
|
const ALLOWED_CLI_PACKAGES = /* @__PURE__ */ new Set([
|
|
1666
1701
|
"xero-cli",
|
|
@@ -2274,6 +2309,12 @@ async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
|
|
|
2274
2309
|
});
|
|
2275
2310
|
const updated = markTaskFired(codeName, task.taskId, "ok");
|
|
2276
2311
|
claudeSchedulerStates.set(codeName, updated);
|
|
2312
|
+
if (task.scheduleKind === "at") {
|
|
2313
|
+
api.post("/host/schedules/disable", { agent_id: agentId, task_id: task.taskId }).catch(
|
|
2314
|
+
(err) => log(`[claude-scheduler] Failed to disable one-off task '${task.name}': ${err.message}`)
|
|
2315
|
+
);
|
|
2316
|
+
log(`[claude-scheduler] One-off task '${task.name}' fired and disabled for '${codeName}'`);
|
|
2317
|
+
}
|
|
2277
2318
|
} catch (err) {
|
|
2278
2319
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
2279
2320
|
log(`[claude-scheduler] Task '${task.name}' failed for '${codeName}': ${errMsg}`);
|
|
@@ -2683,7 +2724,7 @@ async function processDirectChatMessage(agent, msg) {
|
|
|
2683
2724
|
try {
|
|
2684
2725
|
let reply;
|
|
2685
2726
|
if (fw === "claude-code") {
|
|
2686
|
-
const { getProjectDir: ccProjectDir } = await import("../claude-scheduler-
|
|
2727
|
+
const { getProjectDir: ccProjectDir } = await import("../claude-scheduler-7PVWQHWU.js");
|
|
2687
2728
|
const projDir = ccProjectDir(agent.codeName);
|
|
2688
2729
|
const mcpConfigPath = join(projDir, ".mcp.json");
|
|
2689
2730
|
const channelsConfigPath = join(projDir, ".mcp-channels.json");
|
|
@@ -3397,6 +3438,32 @@ function generateArtifacts(agent, refreshData, adapter) {
|
|
|
3397
3438
|
}
|
|
3398
3439
|
const resolvedChannels = resolveChannels(agentChannelPolicy, orgChannelPolicy);
|
|
3399
3440
|
const effectiveChannels = agent.status === "paused" ? [] : resolvedChannels;
|
|
3441
|
+
const tasks = refreshData.scheduled_tasks;
|
|
3442
|
+
const taskTimezones = [...new Set(
|
|
3443
|
+
(tasks ?? []).map((t) => typeof t.timezone === "string" ? t.timezone.trim() : "").filter((tz) => tz.length > 0)
|
|
3444
|
+
)];
|
|
3445
|
+
const teamTimezoneRaw = refreshData.team?.timezone;
|
|
3446
|
+
const teamTimezone = typeof teamTimezoneRaw === "string" && teamTimezoneRaw.trim().length > 0 ? teamTimezoneRaw.trim() : void 0;
|
|
3447
|
+
const agentTimezone = (taskTimezones.length === 1 ? taskTimezones[0] : void 0) ?? teamTimezone ?? "UTC";
|
|
3448
|
+
const orgDefaults = refreshData.model_defaults;
|
|
3449
|
+
const personalitySeed = orgDefaults?.org?.settings?.personality_seed;
|
|
3450
|
+
let reportsTo;
|
|
3451
|
+
const agentData = refreshData.agent;
|
|
3452
|
+
const reportsToId = agentData.reports_to;
|
|
3453
|
+
const reportsToType = agentData.reports_to_type ?? "agent";
|
|
3454
|
+
if (reportsToId) {
|
|
3455
|
+
const reportsToName = agentData.reports_to_name;
|
|
3456
|
+
const reportsToTitle = agentData.reports_to_title;
|
|
3457
|
+
const reportsToDesc = agentData.reports_to_description;
|
|
3458
|
+
if (reportsToName) {
|
|
3459
|
+
reportsTo = {
|
|
3460
|
+
name: reportsToName,
|
|
3461
|
+
type: reportsToType,
|
|
3462
|
+
title: reportsToTitle,
|
|
3463
|
+
description: reportsToDesc
|
|
3464
|
+
};
|
|
3465
|
+
}
|
|
3466
|
+
}
|
|
3400
3467
|
const provisionInput = {
|
|
3401
3468
|
agent: refreshData.agent,
|
|
3402
3469
|
charterFrontmatter,
|
|
@@ -3406,7 +3473,10 @@ function generateArtifacts(agent, refreshData, adapter) {
|
|
|
3406
3473
|
resolvedChannels: effectiveChannels,
|
|
3407
3474
|
deploymentTarget: "local_docker",
|
|
3408
3475
|
gatewayPort: 9e3,
|
|
3409
|
-
team: refreshData.team ?? void 0
|
|
3476
|
+
team: refreshData.team ?? void 0,
|
|
3477
|
+
timezone: agentTimezone,
|
|
3478
|
+
reportsTo,
|
|
3479
|
+
personalitySeed
|
|
3410
3480
|
};
|
|
3411
3481
|
const provisionOutput = provision(provisionInput, adapter.id);
|
|
3412
3482
|
return provisionOutput.artifacts;
|