@integrity-labs/agt-cli 0.28.37 → 0.28.39
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 +4 -4
- package/dist/{chunk-2V5QUET6.js → chunk-57MHPANB.js} +2 -2
- package/dist/{chunk-T3CD664K.js → chunk-AL7UJNNI.js} +3 -3
- package/dist/{chunk-RYB5QSVS.js → chunk-X5E2Q3W2.js} +18 -1
- package/dist/chunk-X5E2Q3W2.js.map +1 -0
- package/dist/{claude-pair-runtime-64KMVLCZ.js → claude-pair-runtime-LOL5YGXP.js} +2 -2
- package/dist/lib/manager-worker.js +389 -353
- package/dist/lib/manager-worker.js.map +1 -1
- package/dist/{persistent-session-27Q5QJF6.js → persistent-session-CFELQZI7.js} +3 -3
- package/dist/{responsiveness-probe-G6LP7KSH.js → responsiveness-probe-YOT3EKKN.js} +3 -3
- package/package.json +1 -1
- package/dist/chunk-RYB5QSVS.js.map +0 -1
- /package/dist/{chunk-2V5QUET6.js.map → chunk-57MHPANB.js.map} +0 -0
- /package/dist/{chunk-T3CD664K.js.map → chunk-AL7UJNNI.js.map} +0 -0
- /package/dist/{claude-pair-runtime-64KMVLCZ.js.map → claude-pair-runtime-LOL5YGXP.js.map} +0 -0
- /package/dist/{persistent-session-27Q5QJF6.js.map → persistent-session-CFELQZI7.js.map} +0 -0
- /package/dist/{responsiveness-probe-G6LP7KSH.js.map → responsiveness-probe-YOT3EKKN.js.map} +0 -0
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
requireHost,
|
|
24
24
|
safeWriteJsonAtomic,
|
|
25
25
|
setConfigHash
|
|
26
|
-
} from "../chunk-
|
|
26
|
+
} from "../chunk-AL7UJNNI.js";
|
|
27
27
|
import {
|
|
28
28
|
getProjectDir as getProjectDir2,
|
|
29
29
|
getReadyTasks,
|
|
@@ -65,7 +65,7 @@ import {
|
|
|
65
65
|
takeWatchdogGiveUpCount,
|
|
66
66
|
takeZombieDetection,
|
|
67
67
|
transcriptActivityAgeSeconds
|
|
68
|
-
} from "../chunk-
|
|
68
|
+
} from "../chunk-57MHPANB.js";
|
|
69
69
|
import {
|
|
70
70
|
FLAGS_SCHEMA_VERSION,
|
|
71
71
|
FLAG_REGISTRY,
|
|
@@ -99,14 +99,14 @@ import {
|
|
|
99
99
|
sumTranscriptUsageInWindow,
|
|
100
100
|
worseConnectivityOutcome,
|
|
101
101
|
wrapScheduledTaskPrompt
|
|
102
|
-
} from "../chunk-
|
|
102
|
+
} from "../chunk-X5E2Q3W2.js";
|
|
103
103
|
import {
|
|
104
104
|
parsePsRows,
|
|
105
105
|
reapOrphanChannelMcps
|
|
106
106
|
} from "../chunk-XWVM4KPK.js";
|
|
107
107
|
|
|
108
108
|
// src/lib/manager-worker.ts
|
|
109
|
-
import { createHash as
|
|
109
|
+
import { createHash as createHash8 } from "crypto";
|
|
110
110
|
import { readFileSync as readFileSync14, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, existsSync as existsSync9, rmSync as rmSync3, readdirSync as readdirSync5, statSync as statSync4, unlinkSync, copyFileSync } from "fs";
|
|
111
111
|
import { execFileSync as syncExecFile } from "child_process";
|
|
112
112
|
import { join as join16, dirname as dirname4 } from "path";
|
|
@@ -4433,11 +4433,140 @@ async function sendTaskNotification(agentCodeName, channel, to, text) {
|
|
|
4433
4433
|
return { ok: false, error_code: `UNKNOWN_CHANNEL:${channel}` };
|
|
4434
4434
|
}
|
|
4435
4435
|
|
|
4436
|
-
// src/lib/manager/scheduler.ts
|
|
4436
|
+
// src/lib/manager/scheduler/tasks.ts
|
|
4437
|
+
function buildSchedulerTaskInput(t) {
|
|
4438
|
+
return {
|
|
4439
|
+
id: t.id,
|
|
4440
|
+
template_id: t.template_id,
|
|
4441
|
+
name: t.name,
|
|
4442
|
+
schedule_kind: t.schedule_kind,
|
|
4443
|
+
schedule_expr: t.schedule_expr ?? null,
|
|
4444
|
+
schedule_every: t.schedule_every ?? null,
|
|
4445
|
+
schedule_at: t.schedule_at ?? null,
|
|
4446
|
+
timezone: t.timezone ?? "UTC",
|
|
4447
|
+
prompt: t.prompt ?? "",
|
|
4448
|
+
session_target: t.session_target ?? "isolated",
|
|
4449
|
+
delivery_mode: t.delivery_mode ?? "none",
|
|
4450
|
+
delivery_policy: t.delivery_policy ?? "always",
|
|
4451
|
+
delivery_channel: t.delivery_channel ?? null,
|
|
4452
|
+
delivery_to: t.delivery_to ?? null,
|
|
4453
|
+
enabled: t.enabled ?? true,
|
|
4454
|
+
triggered_at: t.triggered_at ?? null
|
|
4455
|
+
};
|
|
4456
|
+
}
|
|
4457
|
+
function deriveScheduledTaskNotify(task) {
|
|
4458
|
+
if (task.deliveryChannel !== "msteams") return {};
|
|
4459
|
+
const to = task.deliveryTo;
|
|
4460
|
+
let conversationId = null;
|
|
4461
|
+
if (typeof to === "string" && to.trim().length > 0) {
|
|
4462
|
+
conversationId = to.trim();
|
|
4463
|
+
} else if (to && typeof to === "object") {
|
|
4464
|
+
const cid = to.conversation_id;
|
|
4465
|
+
if (typeof cid === "string" && cid.trim().length > 0) conversationId = cid.trim();
|
|
4466
|
+
}
|
|
4467
|
+
if (!conversationId) return {};
|
|
4468
|
+
return { notify_channel: "msteams", notify_to: conversationId };
|
|
4469
|
+
}
|
|
4470
|
+
function isScheduledViaKanbanEnabled() {
|
|
4471
|
+
const v = process.env["AGT_SCHEDULED_VIA_KANBAN"];
|
|
4472
|
+
return !(v === "0" || v?.toLowerCase() === "false");
|
|
4473
|
+
}
|
|
4474
|
+
|
|
4475
|
+
// src/lib/manager/scheduler/runs.ts
|
|
4437
4476
|
import { createHash as createHash5 } from "crypto";
|
|
4438
|
-
|
|
4439
|
-
|
|
4440
|
-
|
|
4477
|
+
async function startRun(opts) {
|
|
4478
|
+
try {
|
|
4479
|
+
const res = await api.post(
|
|
4480
|
+
"/host/runs/start",
|
|
4481
|
+
opts
|
|
4482
|
+
);
|
|
4483
|
+
return { run_id: res.run_id ?? null, kanban_item_id: res.kanban_item_id ?? null };
|
|
4484
|
+
} catch (err) {
|
|
4485
|
+
const errText = err instanceof Error ? err.message : String(err);
|
|
4486
|
+
const errId = createHash5("sha256").update(errText).digest("hex").slice(0, 12);
|
|
4487
|
+
log(`[runs] start failed for agent_id=${opts.agent_id} source_type=${opts.source_type} error_id=${errId}`);
|
|
4488
|
+
return { run_id: null, kanban_item_id: null };
|
|
4489
|
+
}
|
|
4490
|
+
}
|
|
4491
|
+
async function finishRun(runId, outcome, options = {}) {
|
|
4492
|
+
try {
|
|
4493
|
+
await api.post("/host/runs/finish", {
|
|
4494
|
+
run_id: runId,
|
|
4495
|
+
outcome,
|
|
4496
|
+
outcome_message: options.outcomeMessage,
|
|
4497
|
+
metadata: options.metadata,
|
|
4498
|
+
complete_kanban_item_id: options.completeKanbanItemId ?? void 0,
|
|
4499
|
+
result: options.result
|
|
4500
|
+
});
|
|
4501
|
+
} catch (err) {
|
|
4502
|
+
const errText = err instanceof Error ? err.message : String(err);
|
|
4503
|
+
const errId = createHash5("sha256").update(errText).digest("hex").slice(0, 12);
|
|
4504
|
+
log(`[runs] finish failed for run_id=${runId} outcome=${outcome} error_id=${errId}`);
|
|
4505
|
+
}
|
|
4506
|
+
}
|
|
4507
|
+
var MAX_PRIOR_RUNS = 5;
|
|
4508
|
+
async function fetchPriorScheduledRuns(agentId, taskId) {
|
|
4509
|
+
try {
|
|
4510
|
+
const data = await api.post("/host/scheduled-tasks/recent-outputs", {
|
|
4511
|
+
agent_id: agentId,
|
|
4512
|
+
task_id: taskId,
|
|
4513
|
+
since_hours: 24,
|
|
4514
|
+
limit: MAX_PRIOR_RUNS
|
|
4515
|
+
});
|
|
4516
|
+
const rows = Array.isArray(data?.runs) ? data.runs.slice(0, MAX_PRIOR_RUNS) : [];
|
|
4517
|
+
return rows.filter((r) => typeof r.output_text === "string" && r.output_text.length > 0).map((r) => ({ startedAt: r.started_at, output: r.output_text }));
|
|
4518
|
+
} catch (err) {
|
|
4519
|
+
const errText = err instanceof Error ? err.message : String(err);
|
|
4520
|
+
const errId = createHash5("sha256").update(errText).digest("hex").slice(0, 12);
|
|
4521
|
+
log(`[runs] prior-runs lookup failed for task_id=${taskId} error_id=${errId}`);
|
|
4522
|
+
return [];
|
|
4523
|
+
}
|
|
4524
|
+
}
|
|
4525
|
+
|
|
4526
|
+
// src/lib/manager/scheduler/state.ts
|
|
4527
|
+
var inFlightClaudeTasks = /* @__PURE__ */ new Set();
|
|
4528
|
+
var claudeTaskConcurrency = /* @__PURE__ */ new Map();
|
|
4529
|
+
var MAX_CLAUDE_CONCURRENCY = 2;
|
|
4530
|
+
var claudeSchedulerStates = /* @__PURE__ */ new Map();
|
|
4531
|
+
var scheduledRunsByCode = /* @__PURE__ */ new Map();
|
|
4532
|
+
var claimedScheduledCards = /* @__PURE__ */ new Set();
|
|
4533
|
+
var completedScheduledCards = /* @__PURE__ */ new Set();
|
|
4534
|
+
function claimScheduledCardDelivery(cardId) {
|
|
4535
|
+
if (claimedScheduledCards.has(cardId)) return false;
|
|
4536
|
+
claimedScheduledCards.add(cardId);
|
|
4537
|
+
return true;
|
|
4538
|
+
}
|
|
4539
|
+
function releaseScheduledCardDelivery(cardId) {
|
|
4540
|
+
claimedScheduledCards.delete(cardId);
|
|
4541
|
+
}
|
|
4542
|
+
function markScheduledCardDeliveryComplete(cardId) {
|
|
4543
|
+
completedScheduledCards.add(cardId);
|
|
4544
|
+
}
|
|
4545
|
+
function trackScheduledRun(codeName, cardId, runId) {
|
|
4546
|
+
let m = scheduledRunsByCode.get(codeName);
|
|
4547
|
+
if (!m) {
|
|
4548
|
+
m = /* @__PURE__ */ new Map();
|
|
4549
|
+
scheduledRunsByCode.set(codeName, m);
|
|
4550
|
+
}
|
|
4551
|
+
m.set(cardId, runId);
|
|
4552
|
+
}
|
|
4553
|
+
function isScheduledCardTracked(cardId) {
|
|
4554
|
+
for (const m of scheduledRunsByCode.values()) {
|
|
4555
|
+
if (m.has(cardId)) return true;
|
|
4556
|
+
}
|
|
4557
|
+
return false;
|
|
4558
|
+
}
|
|
4559
|
+
function closeScheduledRunsForCode(codeName, outcome, reason) {
|
|
4560
|
+
const m = scheduledRunsByCode.get(codeName);
|
|
4561
|
+
if (!m || m.size === 0) return;
|
|
4562
|
+
scheduledRunsByCode.delete(codeName);
|
|
4563
|
+
for (const runId of m.values()) {
|
|
4564
|
+
void finishRun(runId, outcome, { outcomeMessage: reason });
|
|
4565
|
+
}
|
|
4566
|
+
}
|
|
4567
|
+
|
|
4568
|
+
// src/lib/manager/scheduler/kanban-route.ts
|
|
4569
|
+
import { createHash as createHash6 } from "crypto";
|
|
4441
4570
|
|
|
4442
4571
|
// src/lib/delivery-schedule-link.ts
|
|
4443
4572
|
function envSuffixFor2(codeName) {
|
|
@@ -4663,250 +4792,57 @@ async function deliverScheduledTaskOutput(agentCodeName, agentId, rawTarget, bod
|
|
|
4663
4792
|
}
|
|
4664
4793
|
}
|
|
4665
4794
|
|
|
4666
|
-
// src/lib/manager/scheduler.ts
|
|
4667
|
-
function
|
|
4668
|
-
|
|
4669
|
-
|
|
4670
|
-
|
|
4671
|
-
|
|
4672
|
-
schedule_kind: t.schedule_kind,
|
|
4673
|
-
schedule_expr: t.schedule_expr ?? null,
|
|
4674
|
-
schedule_every: t.schedule_every ?? null,
|
|
4675
|
-
schedule_at: t.schedule_at ?? null,
|
|
4676
|
-
timezone: t.timezone ?? "UTC",
|
|
4677
|
-
prompt: t.prompt ?? "",
|
|
4678
|
-
session_target: t.session_target ?? "isolated",
|
|
4679
|
-
delivery_mode: t.delivery_mode ?? "none",
|
|
4680
|
-
delivery_policy: t.delivery_policy ?? "always",
|
|
4681
|
-
delivery_channel: t.delivery_channel ?? null,
|
|
4682
|
-
delivery_to: t.delivery_to ?? null,
|
|
4683
|
-
enabled: t.enabled ?? true,
|
|
4684
|
-
triggered_at: t.triggered_at ?? null
|
|
4685
|
-
};
|
|
4686
|
-
}
|
|
4687
|
-
var inFlightClaudeTasks = /* @__PURE__ */ new Set();
|
|
4688
|
-
var claudeTaskConcurrency = /* @__PURE__ */ new Map();
|
|
4689
|
-
var MAX_CLAUDE_CONCURRENCY = 2;
|
|
4690
|
-
function claudePidFilePath() {
|
|
4691
|
-
return join14(homedir7(), ".augmented", "manager-claude-pids.json");
|
|
4692
|
-
}
|
|
4693
|
-
var inFlightClaudePids = /* @__PURE__ */ new Map();
|
|
4694
|
-
function registerClaudeSpawn(record) {
|
|
4695
|
-
inFlightClaudePids.set(record.pid, record);
|
|
4795
|
+
// src/lib/manager/scheduler/kanban-route.ts
|
|
4796
|
+
async function deliverScheduledCardResult(codeName, agentId, cardId) {
|
|
4797
|
+
if (!claimScheduledCardDelivery(cardId)) {
|
|
4798
|
+
return completedScheduledCards.has(cardId) ? "terminal" : "in_flight";
|
|
4799
|
+
}
|
|
4800
|
+
let card;
|
|
4696
4801
|
try {
|
|
4697
|
-
|
|
4802
|
+
const resp = await api.post("/host/kanban-item", {
|
|
4803
|
+
agent_id: agentId,
|
|
4804
|
+
item_id: cardId
|
|
4805
|
+
});
|
|
4806
|
+
card = resp.item;
|
|
4698
4807
|
} catch (err) {
|
|
4699
|
-
|
|
4808
|
+
if (err instanceof ApiError && err.status >= 400 && err.status < 500) {
|
|
4809
|
+
log(`[scheduled-kanban] delivery: card ${cardId} on '${codeName}' fetch returned ${err.status} (${err.message}) \u2014 treating as terminal, not retrying`);
|
|
4810
|
+
markScheduledCardDeliveryComplete(cardId);
|
|
4811
|
+
return "terminal";
|
|
4812
|
+
}
|
|
4813
|
+
releaseScheduledCardDelivery(cardId);
|
|
4814
|
+
log(`[scheduled-kanban] delivery fetch failed for card ${cardId} on '${codeName}': ${err.message} \u2014 will retry`);
|
|
4815
|
+
return "retry";
|
|
4700
4816
|
}
|
|
4701
|
-
|
|
4702
|
-
|
|
4703
|
-
|
|
4704
|
-
try {
|
|
4705
|
-
removeSpawn(claudePidFilePath(), pid);
|
|
4706
|
-
} catch {
|
|
4817
|
+
if (!card || card.status !== "done" || card.source_type !== "scheduled_task" || !card.source_ref) {
|
|
4818
|
+
markScheduledCardDeliveryComplete(cardId);
|
|
4819
|
+
return "terminal";
|
|
4707
4820
|
}
|
|
4708
|
-
|
|
4709
|
-
|
|
4710
|
-
|
|
4711
|
-
|
|
4712
|
-
|
|
4713
|
-
|
|
4714
|
-
const resolvedModels = resolveModelChain(refreshData);
|
|
4715
|
-
const modelsHash = createHash5("sha256").update(JSON.stringify(resolvedModels)).digest("hex").slice(0, 16);
|
|
4716
|
-
const combinedHash = `${stableTasksHash}:${boardHash}:${modelsHash}`;
|
|
4717
|
-
const prevHash = agentState.knownTasksHashes.get(agent.agent_id);
|
|
4718
|
-
if (combinedHash !== prevHash) {
|
|
4719
|
-
const taskInputs = tasks.map((t) => buildSchedulerTaskInput(t));
|
|
4720
|
-
const state8 = syncTasksToScheduler(codeName, agent.agent_id, taskInputs);
|
|
4721
|
-
claudeSchedulerStates.set(codeName, state8);
|
|
4722
|
-
agentState.knownTasksHashes.set(agent.agent_id, combinedHash);
|
|
4723
|
-
log(`[claude-scheduler] Tasks synced for '${codeName}' (${taskInputs.length} task(s))`);
|
|
4821
|
+
const state7 = claudeSchedulerStates.get(codeName) ?? loadSchedulerState(codeName);
|
|
4822
|
+
const task = state7.tasks[card.source_ref];
|
|
4823
|
+
if (!task) {
|
|
4824
|
+
log(`[scheduled-kanban] delivery: no scheduler task for source_ref=${card.source_ref} on '${codeName}' \u2014 skipping`);
|
|
4825
|
+
markScheduledCardDeliveryComplete(cardId);
|
|
4826
|
+
return "terminal";
|
|
4724
4827
|
}
|
|
4725
|
-
if (!
|
|
4726
|
-
|
|
4828
|
+
if (!isPlainScheduledTemplate(task.templateId)) {
|
|
4829
|
+
log(`[scheduled-kanban] delivery: card ${cardId} template '${task.templateId}' on '${codeName}' is not a plain scheduled template \u2014 skipping manager delivery`);
|
|
4830
|
+
markScheduledCardDeliveryComplete(cardId);
|
|
4831
|
+
return "terminal";
|
|
4727
4832
|
}
|
|
4728
|
-
const
|
|
4729
|
-
|
|
4730
|
-
|
|
4731
|
-
|
|
4732
|
-
|
|
4733
|
-
if (
|
|
4734
|
-
|
|
4735
|
-
|
|
4736
|
-
|
|
4737
|
-
|
|
4738
|
-
let prompt = task.prompt;
|
|
4739
|
-
if (BOARD_INJECT_TEMPLATES.has(task.templateId) && boardItems.length > 0) {
|
|
4740
|
-
const template = PLAN_TEMPLATES.has(task.templateId) ? "morning-plan" : "follow-up";
|
|
4741
|
-
const boardPrefix = formatBoardForPrompt(boardItems, template);
|
|
4742
|
-
prompt = boardPrefix + prompt;
|
|
4743
|
-
}
|
|
4744
|
-
inFlightClaudeTasks.add(task.taskId);
|
|
4745
|
-
claudeTaskConcurrency.set(codeName, (claudeTaskConcurrency.get(codeName) ?? 0) + 1);
|
|
4746
|
-
if (isScheduledViaKanbanEnabled() && isPlainScheduledTemplate(task.templateId)) {
|
|
4747
|
-
await fireScheduledTaskViaKanban(codeName, agent.agent_id, task, prompt);
|
|
4748
|
-
inFlightClaudeTasks.delete(task.taskId);
|
|
4749
|
-
claudeTaskConcurrency.set(codeName, Math.max(0, (claudeTaskConcurrency.get(codeName) ?? 1) - 1));
|
|
4750
|
-
continue;
|
|
4833
|
+
const deliveryPolicy = task.deliveryPolicy ?? "always";
|
|
4834
|
+
if (deliveryPolicy === "never" || deliveryPolicy === "conditional" && card.suppress_delivery !== false) {
|
|
4835
|
+
log(
|
|
4836
|
+
`[scheduled-kanban] Suppressed by delivery_policy=${deliveryPolicy} for card ${cardId} (task '${task.name}') on '${codeName}' \u2014 result recorded on the card only (tombstone)`
|
|
4837
|
+
);
|
|
4838
|
+
if (task.deliveryMode === "announce" && task.deliveryTo) {
|
|
4839
|
+
await reportDeliveryStatus(agentId, task.taskId, {
|
|
4840
|
+
status: "skipped",
|
|
4841
|
+
error_code: "SUPPRESSED_BY_POLICY"
|
|
4842
|
+
});
|
|
4751
4843
|
}
|
|
4752
|
-
|
|
4753
|
-
|
|
4754
|
-
inFlightClaudeTasks.delete(task.taskId);
|
|
4755
|
-
claudeTaskConcurrency.set(codeName, Math.max(0, (claudeTaskConcurrency.get(codeName) ?? 1) - 1));
|
|
4756
|
-
});
|
|
4757
|
-
}
|
|
4758
|
-
}
|
|
4759
|
-
function deriveScheduledTaskNotify(task) {
|
|
4760
|
-
if (task.deliveryChannel !== "msteams") return {};
|
|
4761
|
-
const to = task.deliveryTo;
|
|
4762
|
-
let conversationId = null;
|
|
4763
|
-
if (typeof to === "string" && to.trim().length > 0) {
|
|
4764
|
-
conversationId = to.trim();
|
|
4765
|
-
} else if (to && typeof to === "object") {
|
|
4766
|
-
const cid = to.conversation_id;
|
|
4767
|
-
if (typeof cid === "string" && cid.trim().length > 0) conversationId = cid.trim();
|
|
4768
|
-
}
|
|
4769
|
-
if (!conversationId) return {};
|
|
4770
|
-
return { notify_channel: "msteams", notify_to: conversationId };
|
|
4771
|
-
}
|
|
4772
|
-
async function startRun(opts) {
|
|
4773
|
-
try {
|
|
4774
|
-
const res = await api.post(
|
|
4775
|
-
"/host/runs/start",
|
|
4776
|
-
opts
|
|
4777
|
-
);
|
|
4778
|
-
return { run_id: res.run_id ?? null, kanban_item_id: res.kanban_item_id ?? null };
|
|
4779
|
-
} catch (err) {
|
|
4780
|
-
const errText = err instanceof Error ? err.message : String(err);
|
|
4781
|
-
const errId = createHash5("sha256").update(errText).digest("hex").slice(0, 12);
|
|
4782
|
-
log(`[runs] start failed for agent_id=${opts.agent_id} source_type=${opts.source_type} error_id=${errId}`);
|
|
4783
|
-
return { run_id: null, kanban_item_id: null };
|
|
4784
|
-
}
|
|
4785
|
-
}
|
|
4786
|
-
async function finishRun(runId, outcome, options = {}) {
|
|
4787
|
-
try {
|
|
4788
|
-
await api.post("/host/runs/finish", {
|
|
4789
|
-
run_id: runId,
|
|
4790
|
-
outcome,
|
|
4791
|
-
outcome_message: options.outcomeMessage,
|
|
4792
|
-
metadata: options.metadata,
|
|
4793
|
-
complete_kanban_item_id: options.completeKanbanItemId ?? void 0,
|
|
4794
|
-
result: options.result
|
|
4795
|
-
});
|
|
4796
|
-
} catch (err) {
|
|
4797
|
-
const errText = err instanceof Error ? err.message : String(err);
|
|
4798
|
-
const errId = createHash5("sha256").update(errText).digest("hex").slice(0, 12);
|
|
4799
|
-
log(`[runs] finish failed for run_id=${runId} outcome=${outcome} error_id=${errId}`);
|
|
4800
|
-
}
|
|
4801
|
-
}
|
|
4802
|
-
var MAX_PRIOR_RUNS = 5;
|
|
4803
|
-
async function fetchPriorScheduledRuns(agentId, taskId) {
|
|
4804
|
-
try {
|
|
4805
|
-
const data = await api.post("/host/scheduled-tasks/recent-outputs", {
|
|
4806
|
-
agent_id: agentId,
|
|
4807
|
-
task_id: taskId,
|
|
4808
|
-
since_hours: 24,
|
|
4809
|
-
limit: MAX_PRIOR_RUNS
|
|
4810
|
-
});
|
|
4811
|
-
const rows = Array.isArray(data?.runs) ? data.runs.slice(0, MAX_PRIOR_RUNS) : [];
|
|
4812
|
-
return rows.filter((r) => typeof r.output_text === "string" && r.output_text.length > 0).map((r) => ({ startedAt: r.started_at, output: r.output_text }));
|
|
4813
|
-
} catch (err) {
|
|
4814
|
-
const errText = err instanceof Error ? err.message : String(err);
|
|
4815
|
-
const errId = createHash5("sha256").update(errText).digest("hex").slice(0, 12);
|
|
4816
|
-
log(`[runs] prior-runs lookup failed for task_id=${taskId} error_id=${errId}`);
|
|
4817
|
-
return [];
|
|
4818
|
-
}
|
|
4819
|
-
}
|
|
4820
|
-
function isScheduledViaKanbanEnabled() {
|
|
4821
|
-
const v = process.env["AGT_SCHEDULED_VIA_KANBAN"];
|
|
4822
|
-
return !(v === "0" || v?.toLowerCase() === "false");
|
|
4823
|
-
}
|
|
4824
|
-
var scheduledRunsByCode = /* @__PURE__ */ new Map();
|
|
4825
|
-
var claimedScheduledCards = /* @__PURE__ */ new Set();
|
|
4826
|
-
var completedScheduledCards = /* @__PURE__ */ new Set();
|
|
4827
|
-
function claimScheduledCardDelivery(cardId) {
|
|
4828
|
-
if (claimedScheduledCards.has(cardId)) return false;
|
|
4829
|
-
claimedScheduledCards.add(cardId);
|
|
4830
|
-
return true;
|
|
4831
|
-
}
|
|
4832
|
-
function releaseScheduledCardDelivery(cardId) {
|
|
4833
|
-
claimedScheduledCards.delete(cardId);
|
|
4834
|
-
}
|
|
4835
|
-
function markScheduledCardDeliveryComplete(cardId) {
|
|
4836
|
-
completedScheduledCards.add(cardId);
|
|
4837
|
-
}
|
|
4838
|
-
function trackScheduledRun(codeName, cardId, runId) {
|
|
4839
|
-
let m = scheduledRunsByCode.get(codeName);
|
|
4840
|
-
if (!m) {
|
|
4841
|
-
m = /* @__PURE__ */ new Map();
|
|
4842
|
-
scheduledRunsByCode.set(codeName, m);
|
|
4843
|
-
}
|
|
4844
|
-
m.set(cardId, runId);
|
|
4845
|
-
}
|
|
4846
|
-
function isScheduledCardTracked(cardId) {
|
|
4847
|
-
for (const m of scheduledRunsByCode.values()) {
|
|
4848
|
-
if (m.has(cardId)) return true;
|
|
4849
|
-
}
|
|
4850
|
-
return false;
|
|
4851
|
-
}
|
|
4852
|
-
function closeScheduledRunsForCode(codeName, outcome, reason) {
|
|
4853
|
-
const m = scheduledRunsByCode.get(codeName);
|
|
4854
|
-
if (!m || m.size === 0) return;
|
|
4855
|
-
scheduledRunsByCode.delete(codeName);
|
|
4856
|
-
for (const runId of m.values()) {
|
|
4857
|
-
void finishRun(runId, outcome, { outcomeMessage: reason });
|
|
4858
|
-
}
|
|
4859
|
-
}
|
|
4860
|
-
async function deliverScheduledCardResult(codeName, agentId, cardId) {
|
|
4861
|
-
if (!claimScheduledCardDelivery(cardId)) {
|
|
4862
|
-
return completedScheduledCards.has(cardId) ? "terminal" : "in_flight";
|
|
4863
|
-
}
|
|
4864
|
-
let card;
|
|
4865
|
-
try {
|
|
4866
|
-
const resp = await api.post("/host/kanban-item", {
|
|
4867
|
-
agent_id: agentId,
|
|
4868
|
-
item_id: cardId
|
|
4869
|
-
});
|
|
4870
|
-
card = resp.item;
|
|
4871
|
-
} catch (err) {
|
|
4872
|
-
if (err instanceof ApiError && err.status >= 400 && err.status < 500) {
|
|
4873
|
-
log(`[scheduled-kanban] delivery: card ${cardId} on '${codeName}' fetch returned ${err.status} (${err.message}) \u2014 treating as terminal, not retrying`);
|
|
4874
|
-
markScheduledCardDeliveryComplete(cardId);
|
|
4875
|
-
return "terminal";
|
|
4876
|
-
}
|
|
4877
|
-
releaseScheduledCardDelivery(cardId);
|
|
4878
|
-
log(`[scheduled-kanban] delivery fetch failed for card ${cardId} on '${codeName}': ${err.message} \u2014 will retry`);
|
|
4879
|
-
return "retry";
|
|
4880
|
-
}
|
|
4881
|
-
if (!card || card.status !== "done" || card.source_type !== "scheduled_task" || !card.source_ref) {
|
|
4882
|
-
markScheduledCardDeliveryComplete(cardId);
|
|
4883
|
-
return "terminal";
|
|
4884
|
-
}
|
|
4885
|
-
const state7 = claudeSchedulerStates.get(codeName) ?? loadSchedulerState(codeName);
|
|
4886
|
-
const task = state7.tasks[card.source_ref];
|
|
4887
|
-
if (!task) {
|
|
4888
|
-
log(`[scheduled-kanban] delivery: no scheduler task for source_ref=${card.source_ref} on '${codeName}' \u2014 skipping`);
|
|
4889
|
-
markScheduledCardDeliveryComplete(cardId);
|
|
4890
|
-
return "terminal";
|
|
4891
|
-
}
|
|
4892
|
-
if (!isPlainScheduledTemplate(task.templateId)) {
|
|
4893
|
-
log(`[scheduled-kanban] delivery: card ${cardId} template '${task.templateId}' on '${codeName}' is not a plain scheduled template \u2014 skipping manager delivery`);
|
|
4894
|
-
markScheduledCardDeliveryComplete(cardId);
|
|
4895
|
-
return "terminal";
|
|
4896
|
-
}
|
|
4897
|
-
const deliveryPolicy = task.deliveryPolicy ?? "always";
|
|
4898
|
-
if (deliveryPolicy === "never" || deliveryPolicy === "conditional" && card.suppress_delivery !== false) {
|
|
4899
|
-
log(
|
|
4900
|
-
`[scheduled-kanban] Suppressed by delivery_policy=${deliveryPolicy} for card ${cardId} (task '${task.name}') on '${codeName}' \u2014 result recorded on the card only (tombstone)`
|
|
4901
|
-
);
|
|
4902
|
-
if (task.deliveryMode === "announce" && task.deliveryTo) {
|
|
4903
|
-
await reportDeliveryStatus(agentId, task.taskId, {
|
|
4904
|
-
status: "skipped",
|
|
4905
|
-
error_code: "SUPPRESSED_BY_POLICY"
|
|
4906
|
-
});
|
|
4907
|
-
}
|
|
4908
|
-
markScheduledCardDeliveryComplete(cardId);
|
|
4909
|
-
return "delivered";
|
|
4844
|
+
markScheduledCardDeliveryComplete(cardId);
|
|
4845
|
+
return "delivered";
|
|
4910
4846
|
}
|
|
4911
4847
|
if (card.suppress_delivery === true) {
|
|
4912
4848
|
log(`[scheduled-kanban] Suppressing delivery (structured flag) for card ${cardId} (task '${task.name}') on '${codeName}' \u2014 result recorded on the card only`);
|
|
@@ -4919,12 +4855,17 @@ async function deliverScheduledCardResult(codeName, agentId, cardId) {
|
|
|
4919
4855
|
markScheduledCardDeliveryComplete(cardId);
|
|
4920
4856
|
return "delivered";
|
|
4921
4857
|
}
|
|
4922
|
-
await processClaudeTaskResult(codeName, agentId, task.templateId, card.result ?? "", {
|
|
4858
|
+
const outcome = await processClaudeTaskResult(codeName, agentId, task.templateId, card.result ?? "", {
|
|
4923
4859
|
mode: task.deliveryMode,
|
|
4924
4860
|
channel: task.deliveryChannel,
|
|
4925
4861
|
to: task.deliveryTo,
|
|
4926
4862
|
taskId: task.taskId
|
|
4927
4863
|
});
|
|
4864
|
+
if (!outcome.ok) {
|
|
4865
|
+
releaseScheduledCardDelivery(cardId);
|
|
4866
|
+
log(`[scheduled-kanban] delivery: result post failed for card ${cardId} (task '${task.name}') on '${codeName}': ${outcome.error} \u2014 will retry`);
|
|
4867
|
+
return "retry";
|
|
4868
|
+
}
|
|
4928
4869
|
markScheduledCardDeliveryComplete(cardId);
|
|
4929
4870
|
log(`[scheduled-kanban] Delivered result from card ${cardId} (task '${task.name}') for '${codeName}'`);
|
|
4930
4871
|
return "delivered";
|
|
@@ -5022,6 +4963,152 @@ async function fireScheduledTaskViaKanban(codeName, agentId, task, prompt) {
|
|
|
5022
4963
|
}
|
|
5023
4964
|
return routed;
|
|
5024
4965
|
}
|
|
4966
|
+
async function processClaudeTaskResult(codeName, agentId, templateId, rawOutput, delivery) {
|
|
4967
|
+
try {
|
|
4968
|
+
const classification = classifyOutput(rawOutput);
|
|
4969
|
+
if (classification.action === "suppress") {
|
|
4970
|
+
const trimmed = (rawOutput ?? "").trim();
|
|
4971
|
+
const outputHash = trimmed.length === 0 ? "empty" : createHash6("sha256").update(trimmed).digest("hex").slice(0, 12);
|
|
4972
|
+
log(`[claude-scheduler] Suppressing delivery for '${codeName}' (template=${templateId}, task=${delivery?.taskId ?? "n/a"}) \u2014 output_len=${trimmed.length} output_hash=${outputHash}`);
|
|
4973
|
+
if (classification.suppressedNotes) {
|
|
4974
|
+
const notesHash = createHash6("sha256").update(classification.suppressedNotes).digest("hex").slice(0, 12);
|
|
4975
|
+
log(`[claude-scheduler] Suppressed notes for '${codeName}' (task=${delivery?.taskId ?? "n/a"}) \u2014 notes_len=${classification.suppressedNotes.length} notes_hash=${notesHash}`);
|
|
4976
|
+
}
|
|
4977
|
+
if (delivery?.mode === "announce" && delivery.to) {
|
|
4978
|
+
await reportDeliveryStatus(agentId, delivery.taskId, {
|
|
4979
|
+
status: "skipped",
|
|
4980
|
+
error_code: "NO_CONTENT"
|
|
4981
|
+
});
|
|
4982
|
+
}
|
|
4983
|
+
return { ok: true };
|
|
4984
|
+
}
|
|
4985
|
+
const output = classification.deliverable;
|
|
4986
|
+
if (classification.action === "strip") {
|
|
4987
|
+
log(`[claude-scheduler] Stripped '<no-delivery/>' sentinel from '${codeName}' output (template=${templateId}, task=${delivery?.taskId ?? "n/a"}) \u2014 agent mixed it with real content; delivering the rest.`);
|
|
4988
|
+
}
|
|
4989
|
+
if (STANDUP_TEMPLATES.has(templateId)) {
|
|
4990
|
+
const standup = parseStandupSummary(output);
|
|
4991
|
+
await api.post("/host/agent-status", {
|
|
4992
|
+
agent_code_name: codeName,
|
|
4993
|
+
standup,
|
|
4994
|
+
current_status: "idle"
|
|
4995
|
+
});
|
|
4996
|
+
log(`[claude-scheduler] Standup posted for '${codeName}'`);
|
|
4997
|
+
} else if (TASK_UPDATE_TEMPLATES.has(templateId)) {
|
|
4998
|
+
await api.post("/host/agent-status", {
|
|
4999
|
+
agent_code_name: codeName,
|
|
5000
|
+
current_tasks: output.slice(0, 2e3)
|
|
5001
|
+
});
|
|
5002
|
+
log(`[claude-scheduler] Task update posted for '${codeName}'`);
|
|
5003
|
+
} else if (PLAN_TEMPLATES.has(templateId)) {
|
|
5004
|
+
const planItems = parsePlanItems(output);
|
|
5005
|
+
if (planItems.length > 0) {
|
|
5006
|
+
await api.post("/host/kanban", {
|
|
5007
|
+
agent_id: agentId,
|
|
5008
|
+
add: planItems
|
|
5009
|
+
});
|
|
5010
|
+
log(`[claude-scheduler] Plan items posted for '${codeName}' (${planItems.length} items)`);
|
|
5011
|
+
}
|
|
5012
|
+
} else if (KANBAN_WORK_TEMPLATES.has(templateId)) {
|
|
5013
|
+
const kanbanUpdates = parseKanbanUpdates(output);
|
|
5014
|
+
if (kanbanUpdates.length > 0) {
|
|
5015
|
+
await api.post("/host/kanban", {
|
|
5016
|
+
agent_id: agentId,
|
|
5017
|
+
update: kanbanUpdates
|
|
5018
|
+
});
|
|
5019
|
+
log(`[claude-scheduler] Kanban updates posted for '${codeName}' (${kanbanUpdates.length} updates)`);
|
|
5020
|
+
}
|
|
5021
|
+
}
|
|
5022
|
+
if (delivery?.mode === "announce" && delivery.to) {
|
|
5023
|
+
await deliverScheduledTaskOutput(
|
|
5024
|
+
codeName,
|
|
5025
|
+
agentId,
|
|
5026
|
+
delivery.to,
|
|
5027
|
+
output.slice(0, 4e3),
|
|
5028
|
+
delivery.taskId
|
|
5029
|
+
);
|
|
5030
|
+
}
|
|
5031
|
+
return { ok: true };
|
|
5032
|
+
} catch (err) {
|
|
5033
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
5034
|
+
log(`[claude-scheduler] Failed to post result for '${codeName}': ${errMsg}`);
|
|
5035
|
+
return { ok: false, error: errMsg };
|
|
5036
|
+
}
|
|
5037
|
+
}
|
|
5038
|
+
|
|
5039
|
+
// src/lib/manager/scheduler/execution.ts
|
|
5040
|
+
import { createHash as createHash7 } from "crypto";
|
|
5041
|
+
import { readFileSync as readFileSync12, existsSync as existsSync7 } from "fs";
|
|
5042
|
+
import { homedir as homedir7 } from "os";
|
|
5043
|
+
import { join as join14 } from "path";
|
|
5044
|
+
function claudePidFilePath() {
|
|
5045
|
+
return join14(homedir7(), ".augmented", "manager-claude-pids.json");
|
|
5046
|
+
}
|
|
5047
|
+
var inFlightClaudePids = /* @__PURE__ */ new Map();
|
|
5048
|
+
function registerClaudeSpawn(record) {
|
|
5049
|
+
inFlightClaudePids.set(record.pid, record);
|
|
5050
|
+
try {
|
|
5051
|
+
addSpawn(claudePidFilePath(), record);
|
|
5052
|
+
} catch (err) {
|
|
5053
|
+
log(`[drain] pid-file write failed: ${err.message}`);
|
|
5054
|
+
}
|
|
5055
|
+
}
|
|
5056
|
+
function unregisterClaudeSpawn(pid) {
|
|
5057
|
+
inFlightClaudePids.delete(pid);
|
|
5058
|
+
try {
|
|
5059
|
+
removeSpawn(claudePidFilePath(), pid);
|
|
5060
|
+
} catch {
|
|
5061
|
+
}
|
|
5062
|
+
}
|
|
5063
|
+
async function syncAndCheckClaudeScheduler(agent, tasks, boardItems, refreshData) {
|
|
5064
|
+
const codeName = agent.code_name;
|
|
5065
|
+
const stableTasksHash = createHash7("sha256").update(JSON.stringify(tasks)).digest("hex").slice(0, 16);
|
|
5066
|
+
const boardHash = boardItems.length > 0 ? createHash7("sha256").update(JSON.stringify(boardItems.map((b) => ({ id: b.id, title: b.title, status: b.status, priority: b.priority, deliverable: b.deliverable })))).digest("hex").slice(0, 16) : "empty";
|
|
5067
|
+
const resolvedModels = resolveModelChain(refreshData);
|
|
5068
|
+
const modelsHash = createHash7("sha256").update(JSON.stringify(resolvedModels)).digest("hex").slice(0, 16);
|
|
5069
|
+
const combinedHash = `${stableTasksHash}:${boardHash}:${modelsHash}`;
|
|
5070
|
+
const prevHash = agentState.knownTasksHashes.get(agent.agent_id);
|
|
5071
|
+
if (combinedHash !== prevHash) {
|
|
5072
|
+
const taskInputs = tasks.map((t) => buildSchedulerTaskInput(t));
|
|
5073
|
+
const state8 = syncTasksToScheduler(codeName, agent.agent_id, taskInputs);
|
|
5074
|
+
claudeSchedulerStates.set(codeName, state8);
|
|
5075
|
+
agentState.knownTasksHashes.set(agent.agent_id, combinedHash);
|
|
5076
|
+
log(`[claude-scheduler] Tasks synced for '${codeName}' (${taskInputs.length} task(s))`);
|
|
5077
|
+
}
|
|
5078
|
+
if (!claudeSchedulerStates.has(codeName)) {
|
|
5079
|
+
claudeSchedulerStates.set(codeName, loadSchedulerState(codeName));
|
|
5080
|
+
}
|
|
5081
|
+
const state7 = claudeSchedulerStates.get(codeName);
|
|
5082
|
+
const ready = getReadyTasks(state7, inFlightClaudeTasks);
|
|
5083
|
+
if (ready.length === 0) return;
|
|
5084
|
+
for (const task of ready) {
|
|
5085
|
+
if ((claudeTaskConcurrency.get(codeName) ?? 0) >= MAX_CLAUDE_CONCURRENCY) break;
|
|
5086
|
+
if (KANBAN_WORK_TEMPLATES.has(task.templateId)) {
|
|
5087
|
+
const updated = markTaskFired(codeName, task.taskId, "ok");
|
|
5088
|
+
claudeSchedulerStates.set(codeName, updated);
|
|
5089
|
+
continue;
|
|
5090
|
+
}
|
|
5091
|
+
let prompt = task.prompt;
|
|
5092
|
+
if (BOARD_INJECT_TEMPLATES.has(task.templateId) && boardItems.length > 0) {
|
|
5093
|
+
const template = PLAN_TEMPLATES.has(task.templateId) ? "morning-plan" : "follow-up";
|
|
5094
|
+
const boardPrefix = formatBoardForPrompt(boardItems, template);
|
|
5095
|
+
prompt = boardPrefix + prompt;
|
|
5096
|
+
}
|
|
5097
|
+
inFlightClaudeTasks.add(task.taskId);
|
|
5098
|
+
claudeTaskConcurrency.set(codeName, (claudeTaskConcurrency.get(codeName) ?? 0) + 1);
|
|
5099
|
+
if (isScheduledViaKanbanEnabled() && isPlainScheduledTemplate(task.templateId)) {
|
|
5100
|
+
await fireScheduledTaskViaKanban(codeName, agent.agent_id, task, prompt);
|
|
5101
|
+
inFlightClaudeTasks.delete(task.taskId);
|
|
5102
|
+
claudeTaskConcurrency.set(codeName, Math.max(0, (claudeTaskConcurrency.get(codeName) ?? 1) - 1));
|
|
5103
|
+
continue;
|
|
5104
|
+
}
|
|
5105
|
+
log(`[claude-scheduler] Firing '${task.name}' for '${codeName}'`);
|
|
5106
|
+
executeAndProcessClaudeTask(codeName, agent.agent_id, task, prompt).finally(() => {
|
|
5107
|
+
inFlightClaudeTasks.delete(task.taskId);
|
|
5108
|
+
claudeTaskConcurrency.set(codeName, Math.max(0, (claudeTaskConcurrency.get(codeName) ?? 1) - 1));
|
|
5109
|
+
});
|
|
5110
|
+
}
|
|
5111
|
+
}
|
|
5025
5112
|
async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
|
|
5026
5113
|
const projectDir = getProjectDir2(codeName);
|
|
5027
5114
|
const mcpConfigPath = join14(projectDir, ".mcp.json");
|
|
@@ -5030,7 +5117,7 @@ async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
|
|
|
5030
5117
|
let taskResult;
|
|
5031
5118
|
sanitizeMcpJson(mcpConfigPath, requireHost());
|
|
5032
5119
|
const priorRuns = await fetchPriorScheduledRuns(agentId, task.taskId);
|
|
5033
|
-
prompt = wrapScheduledTaskPrompt(prompt, { priorRuns });
|
|
5120
|
+
prompt = wrapScheduledTaskPrompt(prompt, { priorRuns, timezone: task.timezone });
|
|
5034
5121
|
try {
|
|
5035
5122
|
const claudeMdPath = join14(projectDir, "CLAUDE.md");
|
|
5036
5123
|
const serverNames = [];
|
|
@@ -5111,12 +5198,28 @@ async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
|
|
|
5111
5198
|
const output = stdout.trim();
|
|
5112
5199
|
taskResult = output.slice(0, 4e3) || void 0;
|
|
5113
5200
|
log(`[claude-scheduler] Task '${task.name}' completed for '${codeName}' (${output.length} chars): ${output.slice(0, 300)}`);
|
|
5114
|
-
await processClaudeTaskResult(codeName, agentId, task.templateId, output, {
|
|
5201
|
+
const outcome = await processClaudeTaskResult(codeName, agentId, task.templateId, output, {
|
|
5115
5202
|
mode: task.deliveryMode,
|
|
5116
5203
|
channel: task.deliveryChannel,
|
|
5117
5204
|
to: task.deliveryTo,
|
|
5118
5205
|
taskId: task.taskId
|
|
5119
5206
|
});
|
|
5207
|
+
if (!outcome.ok) {
|
|
5208
|
+
log(`[claude-scheduler] Task '${task.name}' result post failed for '${codeName}': ${outcome.error}`);
|
|
5209
|
+
if (runId) {
|
|
5210
|
+
try {
|
|
5211
|
+
await finishRun(runId, "failed", {
|
|
5212
|
+
outcomeMessage: outcome.error,
|
|
5213
|
+
completeKanbanItemId: kanbanItemId,
|
|
5214
|
+
result: taskResult
|
|
5215
|
+
});
|
|
5216
|
+
} catch {
|
|
5217
|
+
}
|
|
5218
|
+
}
|
|
5219
|
+
const failedUpdate = markTaskFired(codeName, task.taskId, "error");
|
|
5220
|
+
claudeSchedulerStates.set(codeName, failedUpdate);
|
|
5221
|
+
return;
|
|
5222
|
+
}
|
|
5120
5223
|
if (runId) {
|
|
5121
5224
|
await finishRun(runId, "completed", {
|
|
5122
5225
|
metadata: { output_length: output.length },
|
|
@@ -5127,10 +5230,12 @@ async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
|
|
|
5127
5230
|
const updated = markTaskFired(codeName, task.taskId, "ok");
|
|
5128
5231
|
claudeSchedulerStates.set(codeName, updated);
|
|
5129
5232
|
if (task.scheduleKind === "at") {
|
|
5130
|
-
|
|
5131
|
-
|
|
5132
|
-
|
|
5133
|
-
|
|
5233
|
+
try {
|
|
5234
|
+
await api.post("/host/schedules/disable", { agent_id: agentId, task_id: task.taskId });
|
|
5235
|
+
log(`[claude-scheduler] One-off task '${task.name}' fired and disabled for '${codeName}'`);
|
|
5236
|
+
} catch (err) {
|
|
5237
|
+
log(`[claude-scheduler] Failed to disable one-off task '${task.name}': ${err.message}`);
|
|
5238
|
+
}
|
|
5134
5239
|
}
|
|
5135
5240
|
} catch (err) {
|
|
5136
5241
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
@@ -5159,75 +5264,6 @@ async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
|
|
|
5159
5264
|
claudeSchedulerStates.set(codeName, updated);
|
|
5160
5265
|
}
|
|
5161
5266
|
}
|
|
5162
|
-
async function processClaudeTaskResult(codeName, agentId, templateId, rawOutput, delivery) {
|
|
5163
|
-
try {
|
|
5164
|
-
const classification = classifyOutput(rawOutput);
|
|
5165
|
-
if (classification.action === "suppress") {
|
|
5166
|
-
const trimmed = (rawOutput ?? "").trim();
|
|
5167
|
-
const outputHash = trimmed.length === 0 ? "empty" : createHash5("sha256").update(trimmed).digest("hex").slice(0, 12);
|
|
5168
|
-
log(`[claude-scheduler] Suppressing delivery for '${codeName}' (template=${templateId}, task=${delivery?.taskId ?? "n/a"}) \u2014 output_len=${trimmed.length} output_hash=${outputHash}`);
|
|
5169
|
-
if (classification.suppressedNotes) {
|
|
5170
|
-
const notesHash = createHash5("sha256").update(classification.suppressedNotes).digest("hex").slice(0, 12);
|
|
5171
|
-
log(`[claude-scheduler] Suppressed notes for '${codeName}' (task=${delivery?.taskId ?? "n/a"}) \u2014 notes_len=${classification.suppressedNotes.length} notes_hash=${notesHash}`);
|
|
5172
|
-
}
|
|
5173
|
-
if (delivery?.mode === "announce" && delivery.to) {
|
|
5174
|
-
await reportDeliveryStatus(agentId, delivery.taskId, {
|
|
5175
|
-
status: "skipped",
|
|
5176
|
-
error_code: "NO_CONTENT"
|
|
5177
|
-
});
|
|
5178
|
-
}
|
|
5179
|
-
return;
|
|
5180
|
-
}
|
|
5181
|
-
const output = classification.deliverable;
|
|
5182
|
-
if (classification.action === "strip") {
|
|
5183
|
-
log(`[claude-scheduler] Stripped '<no-delivery/>' sentinel from '${codeName}' output (template=${templateId}, task=${delivery?.taskId ?? "n/a"}) \u2014 agent mixed it with real content; delivering the rest.`);
|
|
5184
|
-
}
|
|
5185
|
-
if (STANDUP_TEMPLATES.has(templateId)) {
|
|
5186
|
-
const standup = parseStandupSummary(output);
|
|
5187
|
-
await api.post("/host/agent-status", {
|
|
5188
|
-
agent_code_name: codeName,
|
|
5189
|
-
standup,
|
|
5190
|
-
current_status: "idle"
|
|
5191
|
-
});
|
|
5192
|
-
log(`[claude-scheduler] Standup posted for '${codeName}'`);
|
|
5193
|
-
} else if (TASK_UPDATE_TEMPLATES.has(templateId)) {
|
|
5194
|
-
await api.post("/host/agent-status", {
|
|
5195
|
-
agent_code_name: codeName,
|
|
5196
|
-
current_tasks: output.slice(0, 2e3)
|
|
5197
|
-
});
|
|
5198
|
-
log(`[claude-scheduler] Task update posted for '${codeName}'`);
|
|
5199
|
-
} else if (PLAN_TEMPLATES.has(templateId)) {
|
|
5200
|
-
const planItems = parsePlanItems(output);
|
|
5201
|
-
if (planItems.length > 0) {
|
|
5202
|
-
await api.post("/host/kanban", {
|
|
5203
|
-
agent_id: agentId,
|
|
5204
|
-
add: planItems
|
|
5205
|
-
});
|
|
5206
|
-
log(`[claude-scheduler] Plan items posted for '${codeName}' (${planItems.length} items)`);
|
|
5207
|
-
}
|
|
5208
|
-
} else if (KANBAN_WORK_TEMPLATES.has(templateId)) {
|
|
5209
|
-
const kanbanUpdates = parseKanbanUpdates(output);
|
|
5210
|
-
if (kanbanUpdates.length > 0) {
|
|
5211
|
-
await api.post("/host/kanban", {
|
|
5212
|
-
agent_id: agentId,
|
|
5213
|
-
update: kanbanUpdates
|
|
5214
|
-
});
|
|
5215
|
-
log(`[claude-scheduler] Kanban updates posted for '${codeName}' (${kanbanUpdates.length} updates)`);
|
|
5216
|
-
}
|
|
5217
|
-
}
|
|
5218
|
-
if (delivery?.mode === "announce" && delivery.to) {
|
|
5219
|
-
await deliverScheduledTaskOutput(
|
|
5220
|
-
codeName,
|
|
5221
|
-
agentId,
|
|
5222
|
-
delivery.to,
|
|
5223
|
-
output.slice(0, 4e3),
|
|
5224
|
-
delivery.taskId
|
|
5225
|
-
);
|
|
5226
|
-
}
|
|
5227
|
-
} catch (err) {
|
|
5228
|
-
log(`[claude-scheduler] Failed to post result for '${codeName}': ${err.message}`);
|
|
5229
|
-
}
|
|
5230
|
-
}
|
|
5231
5267
|
|
|
5232
5268
|
// src/lib/wedge-detection.ts
|
|
5233
5269
|
var DEFAULTS = {
|
|
@@ -6438,7 +6474,7 @@ var runningChannelSecretHashes = /* @__PURE__ */ new Map();
|
|
|
6438
6474
|
function projectMcpHash(_codeName, projectDir) {
|
|
6439
6475
|
try {
|
|
6440
6476
|
const raw = readFileSync14(join16(projectDir, ".mcp.json"), "utf-8");
|
|
6441
|
-
return
|
|
6477
|
+
return createHash8("sha256").update(canonicalJson(JSON.parse(raw))).digest("hex");
|
|
6442
6478
|
} catch {
|
|
6443
6479
|
return null;
|
|
6444
6480
|
}
|
|
@@ -6797,7 +6833,7 @@ var cachedMaintenanceWindow = null;
|
|
|
6797
6833
|
var lastVersionCheckAt = 0;
|
|
6798
6834
|
var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
|
|
6799
6835
|
var lastResponsivenessProbeAt = 0;
|
|
6800
|
-
var agtCliVersion = true ? "0.28.
|
|
6836
|
+
var agtCliVersion = true ? "0.28.39" : "dev";
|
|
6801
6837
|
function resolveBrewPath(execFileSync4) {
|
|
6802
6838
|
try {
|
|
6803
6839
|
const out = execFileSync4("which", ["brew"], { timeout: 5e3 }).toString().trim();
|
|
@@ -7906,7 +7942,7 @@ async function pollCycle() {
|
|
|
7906
7942
|
}
|
|
7907
7943
|
try {
|
|
7908
7944
|
const { detectHostSecurity } = await import("../host-security-6PDFG7F5.js");
|
|
7909
|
-
const { collectDiagnostics } = await import("../persistent-session-
|
|
7945
|
+
const { collectDiagnostics } = await import("../persistent-session-CFELQZI7.js");
|
|
7910
7946
|
const diagCodeNames = [...agentState.persistentSessionAgents];
|
|
7911
7947
|
const agentDiagnostics = diagCodeNames.length > 0 ? collectDiagnostics(diagCodeNames) : void 0;
|
|
7912
7948
|
let tailscaleHostname;
|
|
@@ -7936,7 +7972,7 @@ async function pollCycle() {
|
|
|
7936
7972
|
claudeAuth = await detectClaudeAuth();
|
|
7937
7973
|
} catch (err) {
|
|
7938
7974
|
const errText = err instanceof Error ? err.message : String(err);
|
|
7939
|
-
const errId =
|
|
7975
|
+
const errId = createHash8("sha256").update(errText).digest("hex").slice(0, 12);
|
|
7940
7976
|
log(`Claude auth detection failed (error_id=${errId})`);
|
|
7941
7977
|
}
|
|
7942
7978
|
const hostHasClaudeCode = state6.agents.some(
|
|
@@ -8007,7 +8043,7 @@ async function pollCycle() {
|
|
|
8007
8043
|
const {
|
|
8008
8044
|
collectResponsivenessProbes,
|
|
8009
8045
|
getResponsivenessIntervalMs
|
|
8010
|
-
} = await import("../responsiveness-probe-
|
|
8046
|
+
} = await import("../responsiveness-probe-YOT3EKKN.js");
|
|
8011
8047
|
const probeIntervalMs = getResponsivenessIntervalMs();
|
|
8012
8048
|
if (now - lastResponsivenessProbeAt > probeIntervalMs) {
|
|
8013
8049
|
const probeCodeNames = [...agentState.persistentSessionAgents];
|
|
@@ -8039,7 +8075,7 @@ async function pollCycle() {
|
|
|
8039
8075
|
collectResponsivenessProbes,
|
|
8040
8076
|
livePendingInboundOldestAgeSeconds,
|
|
8041
8077
|
parkPendingInbound
|
|
8042
|
-
} = await import("../responsiveness-probe-
|
|
8078
|
+
} = await import("../responsiveness-probe-YOT3EKKN.js");
|
|
8043
8079
|
const { getProjectDir: wedgeProjectDir } = await import("../claude-scheduler-FATCLHDM.js");
|
|
8044
8080
|
const wedgeNow = /* @__PURE__ */ new Date();
|
|
8045
8081
|
const liveAgents = agentState.persistentSessionAgents;
|
|
@@ -9021,7 +9057,7 @@ async function processAgent(agent, agentStates) {
|
|
|
9021
9057
|
const sessionModeForHash = refreshData.agent.session_mode;
|
|
9022
9058
|
const senderPolicyForHash = refreshData.sender_policy ?? null;
|
|
9023
9059
|
const CHANNEL_WRITE_VERSION = 9;
|
|
9024
|
-
const configHash =
|
|
9060
|
+
const configHash = createHash8("sha256").update(
|
|
9025
9061
|
canonicalJson({
|
|
9026
9062
|
writeVersion: CHANNEL_WRITE_VERSION,
|
|
9027
9063
|
config: entry.config,
|
|
@@ -9135,7 +9171,7 @@ async function processAgent(agent, agentStates) {
|
|
|
9135
9171
|
if (channelConfigConverged) {
|
|
9136
9172
|
const hasSenderPolicyChannel = currentChannelIds.has("slack") || currentChannelIds.has("msteams");
|
|
9137
9173
|
const senderPolicyForRestartHash = refreshData.sender_policy ?? null;
|
|
9138
|
-
const senderPolicyHash =
|
|
9174
|
+
const senderPolicyHash = createHash8("sha256").update(canonicalJson({ senderPolicy: senderPolicyForRestartHash })).digest("hex");
|
|
9139
9175
|
const prevSenderPolicyHash = agentState.knownSenderPolicyHashes.get(agent.agent_id);
|
|
9140
9176
|
const senderPolicyDecision = hasSenderPolicyChannel ? decideSenderPolicyRestart({
|
|
9141
9177
|
previousHash: prevSenderPolicyHash,
|
|
@@ -9177,7 +9213,7 @@ async function processAgent(agent, agentStates) {
|
|
|
9177
9213
|
const behaviourSubset = extractMsTeamsBehaviourSubset(
|
|
9178
9214
|
msteamsEntry?.config
|
|
9179
9215
|
);
|
|
9180
|
-
const behaviourHash =
|
|
9216
|
+
const behaviourHash = createHash8("sha256").update(canonicalJson(behaviourSubset)).digest("hex");
|
|
9181
9217
|
const prevBehaviourHash = agentState.knownMsTeamsBehaviourHashes.get(agent.agent_id);
|
|
9182
9218
|
const behaviourDecision = decideSenderPolicyRestart({
|
|
9183
9219
|
previousHash: prevBehaviourHash,
|
|
@@ -9224,7 +9260,7 @@ async function processAgent(agent, agentStates) {
|
|
|
9224
9260
|
const slackBehaviourSubset = extractSlackBehaviourSubset(
|
|
9225
9261
|
slackEntry?.config
|
|
9226
9262
|
);
|
|
9227
|
-
const slackBehaviourHash =
|
|
9263
|
+
const slackBehaviourHash = createHash8("sha256").update(canonicalJson(slackBehaviourSubset)).digest("hex");
|
|
9228
9264
|
const prevSlackBehaviourHash = agentState.knownSlackBehaviourHashes.get(agent.agent_id);
|
|
9229
9265
|
const slackBehaviourDecision = decideSenderPolicyRestart({
|
|
9230
9266
|
previousHash: prevSlackBehaviourHash,
|
|
@@ -9468,10 +9504,10 @@ async function processAgent(agent, agentStates) {
|
|
|
9468
9504
|
desiredEntries.push({ serverId, url, headers: mcpHeaders, name: tk.toolkit_name });
|
|
9469
9505
|
}
|
|
9470
9506
|
const hashBasis = desiredEntries.slice().sort((a, b) => a.serverId.localeCompare(b.serverId)).map((e) => {
|
|
9471
|
-
const headersHash =
|
|
9507
|
+
const headersHash = createHash8("sha256").update(canonicalJson(e.headers ?? {})).digest("hex").slice(0, 16);
|
|
9472
9508
|
return `${e.serverId}|${e.url}|${headersHash}`;
|
|
9473
9509
|
}).join("\n");
|
|
9474
|
-
const mcpHash =
|
|
9510
|
+
const mcpHash = createHash8("sha256").update(hashBasis).digest("hex").slice(0, 16);
|
|
9475
9511
|
const prevMcpHash = agentState.knownManagedMcpHashes.get(agent.agent_id);
|
|
9476
9512
|
const structureHash = managedMcpStructureHash(desiredEntries);
|
|
9477
9513
|
const prevStructureHash = agentState.knownManagedMcpStructure.get(agent.agent_id);
|
|
@@ -9486,7 +9522,7 @@ async function processAgent(agent, agentStates) {
|
|
|
9486
9522
|
if (mcpHash !== prevMcpHash) {
|
|
9487
9523
|
for (const e of desiredEntries) {
|
|
9488
9524
|
frameworkAdapter.writeMcpServer(agent.code_name, e.serverId, { url: e.url, headers: e.headers });
|
|
9489
|
-
const urlHash =
|
|
9525
|
+
const urlHash = createHash8("sha256").update(e.url).digest("hex").slice(0, 12);
|
|
9490
9526
|
log(`[managed-toolkit] ${agent.code_name}: wrote '${e.name}' (serverId=${e.serverId}, url_hash=${urlHash})`);
|
|
9491
9527
|
}
|
|
9492
9528
|
if (frameworkAdapter.removeMcpServer && frameworkAdapter.getMcpPath) {
|
|
@@ -9620,7 +9656,7 @@ async function processAgent(agent, agentStates) {
|
|
|
9620
9656
|
if (frameworkAdapter.installSkillFiles) {
|
|
9621
9657
|
const currentIntegrationSkillIds = /* @__PURE__ */ new Set();
|
|
9622
9658
|
const installedIntegrationSkills = [];
|
|
9623
|
-
const { createHash:
|
|
9659
|
+
const { createHash: createHash9 } = await import("crypto");
|
|
9624
9660
|
const refreshAny = refreshData;
|
|
9625
9661
|
const contexts = refreshAny.integration_contexts ?? refreshAny.plugin_contexts ?? [];
|
|
9626
9662
|
const contextBySlug = /* @__PURE__ */ new Map();
|
|
@@ -9649,7 +9685,7 @@ async function processAgent(agent, agentStates) {
|
|
|
9649
9685
|
)
|
|
9650
9686
|
}));
|
|
9651
9687
|
const bundle = buildIntegrationBundle(renderedScopes);
|
|
9652
|
-
const contentHash =
|
|
9688
|
+
const contentHash = createHash9("sha256").update(bundleFingerprint(bundle.files)).digest("hex").slice(0, 12);
|
|
9653
9689
|
const hashKey = `plugin-skill:${agent.agent_id}:${integrationSkillId}`;
|
|
9654
9690
|
if (agentState.knownSkillHashes.get(hashKey) === contentHash) continue;
|
|
9655
9691
|
frameworkAdapter.installSkillFiles(agent.code_name, integrationSkillId, bundle.files);
|
|
@@ -9708,7 +9744,7 @@ async function processAgent(agent, agentStates) {
|
|
|
9708
9744
|
const plan = planGlobalSkillSync(
|
|
9709
9745
|
refreshAny.global_skills ?? [],
|
|
9710
9746
|
agentState.knownGlobalSkillIds.get(agent.agent_id) ?? /* @__PURE__ */ new Set(),
|
|
9711
|
-
(content) =>
|
|
9747
|
+
(content) => createHash9("sha256").update(content).digest("hex").slice(0, 12),
|
|
9712
9748
|
(skillId) => agentState.knownSkillHashes.get(`global-skill:${agent.agent_id}:${skillId}`)
|
|
9713
9749
|
);
|
|
9714
9750
|
for (const { skillId, content, hash } of plan.installs) {
|
|
@@ -9752,7 +9788,7 @@ async function processAgent(agent, agentStates) {
|
|
|
9752
9788
|
const slug = hook.integration_slug ?? hook.plugin_slug;
|
|
9753
9789
|
if (!slug) continue;
|
|
9754
9790
|
try {
|
|
9755
|
-
const scriptHash =
|
|
9791
|
+
const scriptHash = createHash9("sha256").update(hook.script).digest("hex").slice(0, 12);
|
|
9756
9792
|
const hookKey = `${agent.agent_id}:${frameworkAdapter.id}:plugin-hook:${slug}:on_install`;
|
|
9757
9793
|
if (agentState.knownSkillHashes.get(hookKey) === scriptHash) continue;
|
|
9758
9794
|
const result = await frameworkAdapter.executePluginHook({
|
|
@@ -9767,9 +9803,9 @@ async function processAgent(agent, agentStates) {
|
|
|
9767
9803
|
} else if (result.timedOut) {
|
|
9768
9804
|
log(`Integration hook on_install '${slug}' TIMED OUT for '${agent.code_name}' after ${result.durationMs}ms`);
|
|
9769
9805
|
} else {
|
|
9770
|
-
const stderrHash =
|
|
9806
|
+
const stderrHash = createHash9("sha256").update(result.stderr).digest("hex").slice(0, 12);
|
|
9771
9807
|
const missingCmd = result.exitCode === 127 ? extractCommandNotFound(result.stderr) : null;
|
|
9772
|
-
const missingCmdHash = missingCmd ?
|
|
9808
|
+
const missingCmdHash = missingCmd ? createHash9("sha256").update(missingCmd).digest("hex").slice(0, 8) : null;
|
|
9773
9809
|
log(
|
|
9774
9810
|
`Integration hook on_install '${slug}' exited ${result.exitCode} for '${agent.code_name}' ` + (missingCmdHash ? `[missing_command_hash=${missingCmdHash}] ` : "") + `[stderr_hash=${stderrHash} stderr_len=${result.stderr.length}]`
|
|
9775
9811
|
);
|
|
@@ -10038,10 +10074,10 @@ async function processAgent(agent, agentStates) {
|
|
|
10038
10074
|
} else if (agentFw === "claude-code" && tasks.length > 0) {
|
|
10039
10075
|
await syncAndCheckClaudeScheduler(agent, tasks, boardItems, refreshData);
|
|
10040
10076
|
} else if (frameworkAdapter.syncScheduledTasks && gatewayRunning && gatewayPort) {
|
|
10041
|
-
const stableTasksHash =
|
|
10042
|
-
const boardHash = boardItems.length > 0 ?
|
|
10077
|
+
const stableTasksHash = createHash8("sha256").update(JSON.stringify(tasks)).digest("hex").slice(0, 16);
|
|
10078
|
+
const boardHash = boardItems.length > 0 ? createHash8("sha256").update(JSON.stringify(boardItems.map((b) => ({ id: b.id, title: b.title, status: b.status, priority: b.priority, deliverable: b.deliverable })))).digest("hex").slice(0, 16) : "empty";
|
|
10043
10079
|
const resolvedModels = resolveModelChain(refreshData);
|
|
10044
|
-
const modelsHash =
|
|
10080
|
+
const modelsHash = createHash8("sha256").update(JSON.stringify(resolvedModels)).digest("hex").slice(0, 16);
|
|
10045
10081
|
const combinedHash = `${stableTasksHash}:${boardHash}:${modelsHash}`;
|
|
10046
10082
|
const prevTasksHash = agentState.knownTasksHashes.get(agent.agent_id);
|
|
10047
10083
|
if (combinedHash !== prevTasksHash) {
|
|
@@ -10532,7 +10568,7 @@ async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
|
|
|
10532
10568
|
const ctx = getLastFailureContext(codeName);
|
|
10533
10569
|
const recovery = prepareForRespawn(codeName);
|
|
10534
10570
|
const tailSummary = !ctx.tail ? "" : KNOWN_SAFE_TAIL_SIGNATURES.has(ctx.signature) ? `; last pane output (${PANE_TAIL_PREVIEW_LINES} of ~20 lines):
|
|
10535
|
-
${truncateForLog(ctx.tail)}` : `; pane_tail_hash=sha256:${
|
|
10571
|
+
${truncateForLog(ctx.tail)}` : `; pane_tail_hash=sha256:${createHash8("sha256").update(ctx.tail).digest("hex").slice(0, 12)} (raw at ~/.augmented/${codeName}/pane.log)`;
|
|
10536
10572
|
const sigSummary = ctx.signature !== "unknown" ? `; signature=${ctx.signature}` : "";
|
|
10537
10573
|
const recoverySummary = recovery ? `; recovery=${recovery}` : "";
|
|
10538
10574
|
log(
|
|
@@ -10546,7 +10582,7 @@ ${truncateForLog(ctx.tail)}` : `; pane_tail_hash=sha256:${createHash6("sha256").
|
|
|
10546
10582
|
);
|
|
10547
10583
|
getHostId().then((hostId) => {
|
|
10548
10584
|
if (!hostId) return;
|
|
10549
|
-
const paneTailHash = zombie.paneTail ? `sha256:${
|
|
10585
|
+
const paneTailHash = zombie.paneTail ? `sha256:${createHash8("sha256").update(zombie.paneTail).digest("hex").slice(0, 12)}` : null;
|
|
10550
10586
|
return api.post("/host/events", {
|
|
10551
10587
|
host_id: hostId,
|
|
10552
10588
|
agent_code_name: codeName,
|
|
@@ -10625,7 +10661,7 @@ ${truncateForLog(ctx.tail)}` : `; pane_tail_hash=sha256:${createHash6("sha256").
|
|
|
10625
10661
|
if (!claudeAuthTupleBySession.has(codeName)) {
|
|
10626
10662
|
claudeAuthTupleBySession.set(codeName, currentAuthTuple);
|
|
10627
10663
|
}
|
|
10628
|
-
const stableTasksHash =
|
|
10664
|
+
const stableTasksHash = createHash8("sha256").update(JSON.stringify(tasks)).digest("hex").slice(0, 16);
|
|
10629
10665
|
const prevHash = agentState.knownTasksHashes.get(agent.agent_id);
|
|
10630
10666
|
if (stableTasksHash !== prevHash) {
|
|
10631
10667
|
const taskInputs = tasks.map((t) => buildSchedulerTaskInput(t));
|
|
@@ -11151,7 +11187,7 @@ ${escapeXml(msg.content)}
|
|
|
11151
11187
|
log(`[direct-chat] Reply sent for '${agent.codeName}'`);
|
|
11152
11188
|
} catch (err) {
|
|
11153
11189
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
11154
|
-
const errorId =
|
|
11190
|
+
const errorId = createHash8("sha256").update(errMsg).digest("hex").slice(0, 12);
|
|
11155
11191
|
log(`[direct-chat] Failed to process message for '${agent.codeName}': error_id=${errorId} error=${errMsg.slice(0, 500)}`);
|
|
11156
11192
|
try {
|
|
11157
11193
|
await api.post("/host/direct-chat/reply", {
|
|
@@ -11443,7 +11479,7 @@ async function processClaudePairSessions(agents) {
|
|
|
11443
11479
|
killPairSession,
|
|
11444
11480
|
pairTmuxSession,
|
|
11445
11481
|
finalizeClaudePairOnboarding
|
|
11446
|
-
} = await import("../claude-pair-runtime-
|
|
11482
|
+
} = await import("../claude-pair-runtime-LOL5YGXP.js");
|
|
11447
11483
|
for (const pairId of pendingResp.cancelled_pair_ids ?? []) {
|
|
11448
11484
|
log(`[claude-pair] sweeping orphan tmux session for pair ${pairId.slice(0, 8)}`);
|
|
11449
11485
|
const killed = await killPairSession(pairTmuxSession(pairId));
|
|
@@ -11707,7 +11743,7 @@ async function syncMemories(agent, configDir, log2) {
|
|
|
11707
11743
|
if (!file.endsWith(".md")) continue;
|
|
11708
11744
|
try {
|
|
11709
11745
|
const raw = readFileSync14(join16(memoryDir, file), "utf-8");
|
|
11710
|
-
const fileHash =
|
|
11746
|
+
const fileHash = createHash8("sha256").update(raw).digest("hex").slice(0, 16);
|
|
11711
11747
|
currentHashes.set(file, fileHash);
|
|
11712
11748
|
if (prevHashes.get(file) === fileHash) continue;
|
|
11713
11749
|
const parsed = parseMemoryFile(raw, file.replace(/\.md$/, ""));
|
|
@@ -11745,14 +11781,14 @@ async function syncMemories(agent, configDir, log2) {
|
|
|
11745
11781
|
}
|
|
11746
11782
|
async function downloadMemories(agent, memoryDir, log2, { force }) {
|
|
11747
11783
|
const localFiles = existsSync9(memoryDir) ? readdirSync5(memoryDir).filter((f) => f.endsWith(".md")).sort() : [];
|
|
11748
|
-
const localListHash =
|
|
11784
|
+
const localListHash = createHash8("sha256").update(localFiles.join(",")).digest("hex").slice(0, 16);
|
|
11749
11785
|
const prevLocalHash = lastLocalFileHash.get(agent.agent_id);
|
|
11750
11786
|
const prevDownload = lastDownloadHash.get(agent.agent_id);
|
|
11751
11787
|
try {
|
|
11752
11788
|
const dbMemories = await api.post("/host/memories", {
|
|
11753
11789
|
agent_id: agent.agent_id
|
|
11754
11790
|
});
|
|
11755
|
-
const responseHash =
|
|
11791
|
+
const responseHash = createHash8("sha256").update(JSON.stringify(dbMemories.memories ?? [])).digest("hex").slice(0, 16);
|
|
11756
11792
|
if (!force && prevDownload && prevLocalHash === localListHash && lastDownloadHash.get(agent.agent_id) === responseHash) {
|
|
11757
11793
|
return true;
|
|
11758
11794
|
}
|
|
@@ -11791,7 +11827,7 @@ ${mem.content}
|
|
|
11791
11827
|
}
|
|
11792
11828
|
if (written > 0 || overwritten > 0) {
|
|
11793
11829
|
const updatedFiles = readdirSync5(memoryDir).filter((f) => f.endsWith(".md")).sort();
|
|
11794
|
-
lastLocalFileHash.set(agent.agent_id,
|
|
11830
|
+
lastLocalFileHash.set(agent.agent_id, createHash8("sha256").update(updatedFiles.join(",")).digest("hex").slice(0, 16));
|
|
11795
11831
|
log2(`Memory download for '${agent.code_name}': wrote ${written} new, overwrote ${overwritten} stale`);
|
|
11796
11832
|
}
|
|
11797
11833
|
}
|
|
@@ -12228,7 +12264,7 @@ function deployMcpAssets() {
|
|
|
12228
12264
|
const fileHash = (p) => {
|
|
12229
12265
|
try {
|
|
12230
12266
|
if (!existsSync9(p)) return null;
|
|
12231
|
-
return
|
|
12267
|
+
return createHash8("sha256").update(readFileSync14(p)).digest("hex");
|
|
12232
12268
|
} catch {
|
|
12233
12269
|
return null;
|
|
12234
12270
|
}
|