@integrity-labs/agt-cli 0.28.37 → 0.28.38

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.
@@ -23,7 +23,7 @@ import {
23
23
  requireHost,
24
24
  safeWriteJsonAtomic,
25
25
  setConfigHash
26
- } from "../chunk-T3CD664K.js";
26
+ } from "../chunk-IJT4B5PH.js";
27
27
  import {
28
28
  getProjectDir as getProjectDir2,
29
29
  getReadyTasks,
@@ -106,7 +106,7 @@ import {
106
106
  } from "../chunk-XWVM4KPK.js";
107
107
 
108
108
  // src/lib/manager-worker.ts
109
- import { createHash as createHash6 } from "crypto";
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
- import { readFileSync as readFileSync12, existsSync as existsSync7 } from "fs";
4439
- import { homedir as homedir7 } from "os";
4440
- import { join as join14 } from "path";
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 buildSchedulerTaskInput(t) {
4668
- return {
4669
- id: t.id,
4670
- template_id: t.template_id,
4671
- name: t.name,
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
- addSpawn(claudePidFilePath(), record);
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
- log(`[drain] pid-file write failed: ${err.message}`);
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
- function unregisterClaudeSpawn(pid) {
4703
- inFlightClaudePids.delete(pid);
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
- var claudeSchedulerStates = /* @__PURE__ */ new Map();
4710
- async function syncAndCheckClaudeScheduler(agent, tasks, boardItems, refreshData) {
4711
- const codeName = agent.code_name;
4712
- const stableTasksHash = createHash5("sha256").update(JSON.stringify(tasks)).digest("hex").slice(0, 16);
4713
- const boardHash = boardItems.length > 0 ? createHash5("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";
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 (!claudeSchedulerStates.has(codeName)) {
4726
- claudeSchedulerStates.set(codeName, loadSchedulerState(codeName));
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 state7 = claudeSchedulerStates.get(codeName);
4729
- const ready = getReadyTasks(state7, inFlightClaudeTasks);
4730
- if (ready.length === 0) return;
4731
- for (const task of ready) {
4732
- if ((claudeTaskConcurrency.get(codeName) ?? 0) >= MAX_CLAUDE_CONCURRENCY) break;
4733
- if (KANBAN_WORK_TEMPLATES.has(task.templateId)) {
4734
- const updated = markTaskFired(codeName, task.taskId, "ok");
4735
- claudeSchedulerStates.set(codeName, updated);
4736
- continue;
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
- log(`[claude-scheduler] Firing '${task.name}' for '${codeName}'`);
4753
- executeAndProcessClaudeTask(codeName, agent.agent_id, task, prompt).finally(() => {
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`);
@@ -5022,6 +4958,149 @@ async function fireScheduledTaskViaKanban(codeName, agentId, task, prompt) {
5022
4958
  }
5023
4959
  return routed;
5024
4960
  }
4961
+ async function processClaudeTaskResult(codeName, agentId, templateId, rawOutput, delivery) {
4962
+ try {
4963
+ const classification = classifyOutput(rawOutput);
4964
+ if (classification.action === "suppress") {
4965
+ const trimmed = (rawOutput ?? "").trim();
4966
+ const outputHash = trimmed.length === 0 ? "empty" : createHash6("sha256").update(trimmed).digest("hex").slice(0, 12);
4967
+ log(`[claude-scheduler] Suppressing delivery for '${codeName}' (template=${templateId}, task=${delivery?.taskId ?? "n/a"}) \u2014 output_len=${trimmed.length} output_hash=${outputHash}`);
4968
+ if (classification.suppressedNotes) {
4969
+ const notesHash = createHash6("sha256").update(classification.suppressedNotes).digest("hex").slice(0, 12);
4970
+ log(`[claude-scheduler] Suppressed notes for '${codeName}' (task=${delivery?.taskId ?? "n/a"}) \u2014 notes_len=${classification.suppressedNotes.length} notes_hash=${notesHash}`);
4971
+ }
4972
+ if (delivery?.mode === "announce" && delivery.to) {
4973
+ await reportDeliveryStatus(agentId, delivery.taskId, {
4974
+ status: "skipped",
4975
+ error_code: "NO_CONTENT"
4976
+ });
4977
+ }
4978
+ return;
4979
+ }
4980
+ const output = classification.deliverable;
4981
+ if (classification.action === "strip") {
4982
+ 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.`);
4983
+ }
4984
+ if (STANDUP_TEMPLATES.has(templateId)) {
4985
+ const standup = parseStandupSummary(output);
4986
+ await api.post("/host/agent-status", {
4987
+ agent_code_name: codeName,
4988
+ standup,
4989
+ current_status: "idle"
4990
+ });
4991
+ log(`[claude-scheduler] Standup posted for '${codeName}'`);
4992
+ } else if (TASK_UPDATE_TEMPLATES.has(templateId)) {
4993
+ await api.post("/host/agent-status", {
4994
+ agent_code_name: codeName,
4995
+ current_tasks: output.slice(0, 2e3)
4996
+ });
4997
+ log(`[claude-scheduler] Task update posted for '${codeName}'`);
4998
+ } else if (PLAN_TEMPLATES.has(templateId)) {
4999
+ const planItems = parsePlanItems(output);
5000
+ if (planItems.length > 0) {
5001
+ await api.post("/host/kanban", {
5002
+ agent_id: agentId,
5003
+ add: planItems
5004
+ });
5005
+ log(`[claude-scheduler] Plan items posted for '${codeName}' (${planItems.length} items)`);
5006
+ }
5007
+ } else if (KANBAN_WORK_TEMPLATES.has(templateId)) {
5008
+ const kanbanUpdates = parseKanbanUpdates(output);
5009
+ if (kanbanUpdates.length > 0) {
5010
+ await api.post("/host/kanban", {
5011
+ agent_id: agentId,
5012
+ update: kanbanUpdates
5013
+ });
5014
+ log(`[claude-scheduler] Kanban updates posted for '${codeName}' (${kanbanUpdates.length} updates)`);
5015
+ }
5016
+ }
5017
+ if (delivery?.mode === "announce" && delivery.to) {
5018
+ await deliverScheduledTaskOutput(
5019
+ codeName,
5020
+ agentId,
5021
+ delivery.to,
5022
+ output.slice(0, 4e3),
5023
+ delivery.taskId
5024
+ );
5025
+ }
5026
+ } catch (err) {
5027
+ log(`[claude-scheduler] Failed to post result for '${codeName}': ${err.message}`);
5028
+ }
5029
+ }
5030
+
5031
+ // src/lib/manager/scheduler/execution.ts
5032
+ import { createHash as createHash7 } from "crypto";
5033
+ import { readFileSync as readFileSync12, existsSync as existsSync7 } from "fs";
5034
+ import { homedir as homedir7 } from "os";
5035
+ import { join as join14 } from "path";
5036
+ function claudePidFilePath() {
5037
+ return join14(homedir7(), ".augmented", "manager-claude-pids.json");
5038
+ }
5039
+ var inFlightClaudePids = /* @__PURE__ */ new Map();
5040
+ function registerClaudeSpawn(record) {
5041
+ inFlightClaudePids.set(record.pid, record);
5042
+ try {
5043
+ addSpawn(claudePidFilePath(), record);
5044
+ } catch (err) {
5045
+ log(`[drain] pid-file write failed: ${err.message}`);
5046
+ }
5047
+ }
5048
+ function unregisterClaudeSpawn(pid) {
5049
+ inFlightClaudePids.delete(pid);
5050
+ try {
5051
+ removeSpawn(claudePidFilePath(), pid);
5052
+ } catch {
5053
+ }
5054
+ }
5055
+ async function syncAndCheckClaudeScheduler(agent, tasks, boardItems, refreshData) {
5056
+ const codeName = agent.code_name;
5057
+ const stableTasksHash = createHash7("sha256").update(JSON.stringify(tasks)).digest("hex").slice(0, 16);
5058
+ 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";
5059
+ const resolvedModels = resolveModelChain(refreshData);
5060
+ const modelsHash = createHash7("sha256").update(JSON.stringify(resolvedModels)).digest("hex").slice(0, 16);
5061
+ const combinedHash = `${stableTasksHash}:${boardHash}:${modelsHash}`;
5062
+ const prevHash = agentState.knownTasksHashes.get(agent.agent_id);
5063
+ if (combinedHash !== prevHash) {
5064
+ const taskInputs = tasks.map((t) => buildSchedulerTaskInput(t));
5065
+ const state8 = syncTasksToScheduler(codeName, agent.agent_id, taskInputs);
5066
+ claudeSchedulerStates.set(codeName, state8);
5067
+ agentState.knownTasksHashes.set(agent.agent_id, combinedHash);
5068
+ log(`[claude-scheduler] Tasks synced for '${codeName}' (${taskInputs.length} task(s))`);
5069
+ }
5070
+ if (!claudeSchedulerStates.has(codeName)) {
5071
+ claudeSchedulerStates.set(codeName, loadSchedulerState(codeName));
5072
+ }
5073
+ const state7 = claudeSchedulerStates.get(codeName);
5074
+ const ready = getReadyTasks(state7, inFlightClaudeTasks);
5075
+ if (ready.length === 0) return;
5076
+ for (const task of ready) {
5077
+ if ((claudeTaskConcurrency.get(codeName) ?? 0) >= MAX_CLAUDE_CONCURRENCY) break;
5078
+ if (KANBAN_WORK_TEMPLATES.has(task.templateId)) {
5079
+ const updated = markTaskFired(codeName, task.taskId, "ok");
5080
+ claudeSchedulerStates.set(codeName, updated);
5081
+ continue;
5082
+ }
5083
+ let prompt = task.prompt;
5084
+ if (BOARD_INJECT_TEMPLATES.has(task.templateId) && boardItems.length > 0) {
5085
+ const template = PLAN_TEMPLATES.has(task.templateId) ? "morning-plan" : "follow-up";
5086
+ const boardPrefix = formatBoardForPrompt(boardItems, template);
5087
+ prompt = boardPrefix + prompt;
5088
+ }
5089
+ inFlightClaudeTasks.add(task.taskId);
5090
+ claudeTaskConcurrency.set(codeName, (claudeTaskConcurrency.get(codeName) ?? 0) + 1);
5091
+ if (isScheduledViaKanbanEnabled() && isPlainScheduledTemplate(task.templateId)) {
5092
+ await fireScheduledTaskViaKanban(codeName, agent.agent_id, task, prompt);
5093
+ inFlightClaudeTasks.delete(task.taskId);
5094
+ claudeTaskConcurrency.set(codeName, Math.max(0, (claudeTaskConcurrency.get(codeName) ?? 1) - 1));
5095
+ continue;
5096
+ }
5097
+ log(`[claude-scheduler] Firing '${task.name}' for '${codeName}'`);
5098
+ executeAndProcessClaudeTask(codeName, agent.agent_id, task, prompt).finally(() => {
5099
+ inFlightClaudeTasks.delete(task.taskId);
5100
+ claudeTaskConcurrency.set(codeName, Math.max(0, (claudeTaskConcurrency.get(codeName) ?? 1) - 1));
5101
+ });
5102
+ }
5103
+ }
5025
5104
  async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
5026
5105
  const projectDir = getProjectDir2(codeName);
5027
5106
  const mcpConfigPath = join14(projectDir, ".mcp.json");
@@ -5159,75 +5238,6 @@ async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
5159
5238
  claudeSchedulerStates.set(codeName, updated);
5160
5239
  }
5161
5240
  }
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
5241
 
5232
5242
  // src/lib/wedge-detection.ts
5233
5243
  var DEFAULTS = {
@@ -6438,7 +6448,7 @@ var runningChannelSecretHashes = /* @__PURE__ */ new Map();
6438
6448
  function projectMcpHash(_codeName, projectDir) {
6439
6449
  try {
6440
6450
  const raw = readFileSync14(join16(projectDir, ".mcp.json"), "utf-8");
6441
- return createHash6("sha256").update(canonicalJson(JSON.parse(raw))).digest("hex");
6451
+ return createHash8("sha256").update(canonicalJson(JSON.parse(raw))).digest("hex");
6442
6452
  } catch {
6443
6453
  return null;
6444
6454
  }
@@ -6797,7 +6807,7 @@ var cachedMaintenanceWindow = null;
6797
6807
  var lastVersionCheckAt = 0;
6798
6808
  var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
6799
6809
  var lastResponsivenessProbeAt = 0;
6800
- var agtCliVersion = true ? "0.28.37" : "dev";
6810
+ var agtCliVersion = true ? "0.28.38" : "dev";
6801
6811
  function resolveBrewPath(execFileSync4) {
6802
6812
  try {
6803
6813
  const out = execFileSync4("which", ["brew"], { timeout: 5e3 }).toString().trim();
@@ -7936,7 +7946,7 @@ async function pollCycle() {
7936
7946
  claudeAuth = await detectClaudeAuth();
7937
7947
  } catch (err) {
7938
7948
  const errText = err instanceof Error ? err.message : String(err);
7939
- const errId = createHash6("sha256").update(errText).digest("hex").slice(0, 12);
7949
+ const errId = createHash8("sha256").update(errText).digest("hex").slice(0, 12);
7940
7950
  log(`Claude auth detection failed (error_id=${errId})`);
7941
7951
  }
7942
7952
  const hostHasClaudeCode = state6.agents.some(
@@ -9021,7 +9031,7 @@ async function processAgent(agent, agentStates) {
9021
9031
  const sessionModeForHash = refreshData.agent.session_mode;
9022
9032
  const senderPolicyForHash = refreshData.sender_policy ?? null;
9023
9033
  const CHANNEL_WRITE_VERSION = 9;
9024
- const configHash = createHash6("sha256").update(
9034
+ const configHash = createHash8("sha256").update(
9025
9035
  canonicalJson({
9026
9036
  writeVersion: CHANNEL_WRITE_VERSION,
9027
9037
  config: entry.config,
@@ -9135,7 +9145,7 @@ async function processAgent(agent, agentStates) {
9135
9145
  if (channelConfigConverged) {
9136
9146
  const hasSenderPolicyChannel = currentChannelIds.has("slack") || currentChannelIds.has("msteams");
9137
9147
  const senderPolicyForRestartHash = refreshData.sender_policy ?? null;
9138
- const senderPolicyHash = createHash6("sha256").update(canonicalJson({ senderPolicy: senderPolicyForRestartHash })).digest("hex");
9148
+ const senderPolicyHash = createHash8("sha256").update(canonicalJson({ senderPolicy: senderPolicyForRestartHash })).digest("hex");
9139
9149
  const prevSenderPolicyHash = agentState.knownSenderPolicyHashes.get(agent.agent_id);
9140
9150
  const senderPolicyDecision = hasSenderPolicyChannel ? decideSenderPolicyRestart({
9141
9151
  previousHash: prevSenderPolicyHash,
@@ -9177,7 +9187,7 @@ async function processAgent(agent, agentStates) {
9177
9187
  const behaviourSubset = extractMsTeamsBehaviourSubset(
9178
9188
  msteamsEntry?.config
9179
9189
  );
9180
- const behaviourHash = createHash6("sha256").update(canonicalJson(behaviourSubset)).digest("hex");
9190
+ const behaviourHash = createHash8("sha256").update(canonicalJson(behaviourSubset)).digest("hex");
9181
9191
  const prevBehaviourHash = agentState.knownMsTeamsBehaviourHashes.get(agent.agent_id);
9182
9192
  const behaviourDecision = decideSenderPolicyRestart({
9183
9193
  previousHash: prevBehaviourHash,
@@ -9224,7 +9234,7 @@ async function processAgent(agent, agentStates) {
9224
9234
  const slackBehaviourSubset = extractSlackBehaviourSubset(
9225
9235
  slackEntry?.config
9226
9236
  );
9227
- const slackBehaviourHash = createHash6("sha256").update(canonicalJson(slackBehaviourSubset)).digest("hex");
9237
+ const slackBehaviourHash = createHash8("sha256").update(canonicalJson(slackBehaviourSubset)).digest("hex");
9228
9238
  const prevSlackBehaviourHash = agentState.knownSlackBehaviourHashes.get(agent.agent_id);
9229
9239
  const slackBehaviourDecision = decideSenderPolicyRestart({
9230
9240
  previousHash: prevSlackBehaviourHash,
@@ -9468,10 +9478,10 @@ async function processAgent(agent, agentStates) {
9468
9478
  desiredEntries.push({ serverId, url, headers: mcpHeaders, name: tk.toolkit_name });
9469
9479
  }
9470
9480
  const hashBasis = desiredEntries.slice().sort((a, b) => a.serverId.localeCompare(b.serverId)).map((e) => {
9471
- const headersHash = createHash6("sha256").update(canonicalJson(e.headers ?? {})).digest("hex").slice(0, 16);
9481
+ const headersHash = createHash8("sha256").update(canonicalJson(e.headers ?? {})).digest("hex").slice(0, 16);
9472
9482
  return `${e.serverId}|${e.url}|${headersHash}`;
9473
9483
  }).join("\n");
9474
- const mcpHash = createHash6("sha256").update(hashBasis).digest("hex").slice(0, 16);
9484
+ const mcpHash = createHash8("sha256").update(hashBasis).digest("hex").slice(0, 16);
9475
9485
  const prevMcpHash = agentState.knownManagedMcpHashes.get(agent.agent_id);
9476
9486
  const structureHash = managedMcpStructureHash(desiredEntries);
9477
9487
  const prevStructureHash = agentState.knownManagedMcpStructure.get(agent.agent_id);
@@ -9486,7 +9496,7 @@ async function processAgent(agent, agentStates) {
9486
9496
  if (mcpHash !== prevMcpHash) {
9487
9497
  for (const e of desiredEntries) {
9488
9498
  frameworkAdapter.writeMcpServer(agent.code_name, e.serverId, { url: e.url, headers: e.headers });
9489
- const urlHash = createHash6("sha256").update(e.url).digest("hex").slice(0, 12);
9499
+ const urlHash = createHash8("sha256").update(e.url).digest("hex").slice(0, 12);
9490
9500
  log(`[managed-toolkit] ${agent.code_name}: wrote '${e.name}' (serverId=${e.serverId}, url_hash=${urlHash})`);
9491
9501
  }
9492
9502
  if (frameworkAdapter.removeMcpServer && frameworkAdapter.getMcpPath) {
@@ -9620,7 +9630,7 @@ async function processAgent(agent, agentStates) {
9620
9630
  if (frameworkAdapter.installSkillFiles) {
9621
9631
  const currentIntegrationSkillIds = /* @__PURE__ */ new Set();
9622
9632
  const installedIntegrationSkills = [];
9623
- const { createHash: createHash7 } = await import("crypto");
9633
+ const { createHash: createHash9 } = await import("crypto");
9624
9634
  const refreshAny = refreshData;
9625
9635
  const contexts = refreshAny.integration_contexts ?? refreshAny.plugin_contexts ?? [];
9626
9636
  const contextBySlug = /* @__PURE__ */ new Map();
@@ -9649,7 +9659,7 @@ async function processAgent(agent, agentStates) {
9649
9659
  )
9650
9660
  }));
9651
9661
  const bundle = buildIntegrationBundle(renderedScopes);
9652
- const contentHash = createHash7("sha256").update(bundleFingerprint(bundle.files)).digest("hex").slice(0, 12);
9662
+ const contentHash = createHash9("sha256").update(bundleFingerprint(bundle.files)).digest("hex").slice(0, 12);
9653
9663
  const hashKey = `plugin-skill:${agent.agent_id}:${integrationSkillId}`;
9654
9664
  if (agentState.knownSkillHashes.get(hashKey) === contentHash) continue;
9655
9665
  frameworkAdapter.installSkillFiles(agent.code_name, integrationSkillId, bundle.files);
@@ -9708,7 +9718,7 @@ async function processAgent(agent, agentStates) {
9708
9718
  const plan = planGlobalSkillSync(
9709
9719
  refreshAny.global_skills ?? [],
9710
9720
  agentState.knownGlobalSkillIds.get(agent.agent_id) ?? /* @__PURE__ */ new Set(),
9711
- (content) => createHash7("sha256").update(content).digest("hex").slice(0, 12),
9721
+ (content) => createHash9("sha256").update(content).digest("hex").slice(0, 12),
9712
9722
  (skillId) => agentState.knownSkillHashes.get(`global-skill:${agent.agent_id}:${skillId}`)
9713
9723
  );
9714
9724
  for (const { skillId, content, hash } of plan.installs) {
@@ -9752,7 +9762,7 @@ async function processAgent(agent, agentStates) {
9752
9762
  const slug = hook.integration_slug ?? hook.plugin_slug;
9753
9763
  if (!slug) continue;
9754
9764
  try {
9755
- const scriptHash = createHash7("sha256").update(hook.script).digest("hex").slice(0, 12);
9765
+ const scriptHash = createHash9("sha256").update(hook.script).digest("hex").slice(0, 12);
9756
9766
  const hookKey = `${agent.agent_id}:${frameworkAdapter.id}:plugin-hook:${slug}:on_install`;
9757
9767
  if (agentState.knownSkillHashes.get(hookKey) === scriptHash) continue;
9758
9768
  const result = await frameworkAdapter.executePluginHook({
@@ -9767,9 +9777,9 @@ async function processAgent(agent, agentStates) {
9767
9777
  } else if (result.timedOut) {
9768
9778
  log(`Integration hook on_install '${slug}' TIMED OUT for '${agent.code_name}' after ${result.durationMs}ms`);
9769
9779
  } else {
9770
- const stderrHash = createHash7("sha256").update(result.stderr).digest("hex").slice(0, 12);
9780
+ const stderrHash = createHash9("sha256").update(result.stderr).digest("hex").slice(0, 12);
9771
9781
  const missingCmd = result.exitCode === 127 ? extractCommandNotFound(result.stderr) : null;
9772
- const missingCmdHash = missingCmd ? createHash7("sha256").update(missingCmd).digest("hex").slice(0, 8) : null;
9782
+ const missingCmdHash = missingCmd ? createHash9("sha256").update(missingCmd).digest("hex").slice(0, 8) : null;
9773
9783
  log(
9774
9784
  `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
9785
  );
@@ -10038,10 +10048,10 @@ async function processAgent(agent, agentStates) {
10038
10048
  } else if (agentFw === "claude-code" && tasks.length > 0) {
10039
10049
  await syncAndCheckClaudeScheduler(agent, tasks, boardItems, refreshData);
10040
10050
  } else if (frameworkAdapter.syncScheduledTasks && gatewayRunning && gatewayPort) {
10041
- const stableTasksHash = createHash6("sha256").update(JSON.stringify(tasks)).digest("hex").slice(0, 16);
10042
- const boardHash = boardItems.length > 0 ? createHash6("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";
10051
+ const stableTasksHash = createHash8("sha256").update(JSON.stringify(tasks)).digest("hex").slice(0, 16);
10052
+ 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
10053
  const resolvedModels = resolveModelChain(refreshData);
10044
- const modelsHash = createHash6("sha256").update(JSON.stringify(resolvedModels)).digest("hex").slice(0, 16);
10054
+ const modelsHash = createHash8("sha256").update(JSON.stringify(resolvedModels)).digest("hex").slice(0, 16);
10045
10055
  const combinedHash = `${stableTasksHash}:${boardHash}:${modelsHash}`;
10046
10056
  const prevTasksHash = agentState.knownTasksHashes.get(agent.agent_id);
10047
10057
  if (combinedHash !== prevTasksHash) {
@@ -10532,7 +10542,7 @@ async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
10532
10542
  const ctx = getLastFailureContext(codeName);
10533
10543
  const recovery = prepareForRespawn(codeName);
10534
10544
  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:${createHash6("sha256").update(ctx.tail).digest("hex").slice(0, 12)} (raw at ~/.augmented/${codeName}/pane.log)`;
10545
+ ${truncateForLog(ctx.tail)}` : `; pane_tail_hash=sha256:${createHash8("sha256").update(ctx.tail).digest("hex").slice(0, 12)} (raw at ~/.augmented/${codeName}/pane.log)`;
10536
10546
  const sigSummary = ctx.signature !== "unknown" ? `; signature=${ctx.signature}` : "";
10537
10547
  const recoverySummary = recovery ? `; recovery=${recovery}` : "";
10538
10548
  log(
@@ -10546,7 +10556,7 @@ ${truncateForLog(ctx.tail)}` : `; pane_tail_hash=sha256:${createHash6("sha256").
10546
10556
  );
10547
10557
  getHostId().then((hostId) => {
10548
10558
  if (!hostId) return;
10549
- const paneTailHash = zombie.paneTail ? `sha256:${createHash6("sha256").update(zombie.paneTail).digest("hex").slice(0, 12)}` : null;
10559
+ const paneTailHash = zombie.paneTail ? `sha256:${createHash8("sha256").update(zombie.paneTail).digest("hex").slice(0, 12)}` : null;
10550
10560
  return api.post("/host/events", {
10551
10561
  host_id: hostId,
10552
10562
  agent_code_name: codeName,
@@ -10625,7 +10635,7 @@ ${truncateForLog(ctx.tail)}` : `; pane_tail_hash=sha256:${createHash6("sha256").
10625
10635
  if (!claudeAuthTupleBySession.has(codeName)) {
10626
10636
  claudeAuthTupleBySession.set(codeName, currentAuthTuple);
10627
10637
  }
10628
- const stableTasksHash = createHash6("sha256").update(JSON.stringify(tasks)).digest("hex").slice(0, 16);
10638
+ const stableTasksHash = createHash8("sha256").update(JSON.stringify(tasks)).digest("hex").slice(0, 16);
10629
10639
  const prevHash = agentState.knownTasksHashes.get(agent.agent_id);
10630
10640
  if (stableTasksHash !== prevHash) {
10631
10641
  const taskInputs = tasks.map((t) => buildSchedulerTaskInput(t));
@@ -11151,7 +11161,7 @@ ${escapeXml(msg.content)}
11151
11161
  log(`[direct-chat] Reply sent for '${agent.codeName}'`);
11152
11162
  } catch (err) {
11153
11163
  const errMsg = err instanceof Error ? err.message : String(err);
11154
- const errorId = createHash6("sha256").update(errMsg).digest("hex").slice(0, 12);
11164
+ const errorId = createHash8("sha256").update(errMsg).digest("hex").slice(0, 12);
11155
11165
  log(`[direct-chat] Failed to process message for '${agent.codeName}': error_id=${errorId} error=${errMsg.slice(0, 500)}`);
11156
11166
  try {
11157
11167
  await api.post("/host/direct-chat/reply", {
@@ -11707,7 +11717,7 @@ async function syncMemories(agent, configDir, log2) {
11707
11717
  if (!file.endsWith(".md")) continue;
11708
11718
  try {
11709
11719
  const raw = readFileSync14(join16(memoryDir, file), "utf-8");
11710
- const fileHash = createHash6("sha256").update(raw).digest("hex").slice(0, 16);
11720
+ const fileHash = createHash8("sha256").update(raw).digest("hex").slice(0, 16);
11711
11721
  currentHashes.set(file, fileHash);
11712
11722
  if (prevHashes.get(file) === fileHash) continue;
11713
11723
  const parsed = parseMemoryFile(raw, file.replace(/\.md$/, ""));
@@ -11745,14 +11755,14 @@ async function syncMemories(agent, configDir, log2) {
11745
11755
  }
11746
11756
  async function downloadMemories(agent, memoryDir, log2, { force }) {
11747
11757
  const localFiles = existsSync9(memoryDir) ? readdirSync5(memoryDir).filter((f) => f.endsWith(".md")).sort() : [];
11748
- const localListHash = createHash6("sha256").update(localFiles.join(",")).digest("hex").slice(0, 16);
11758
+ const localListHash = createHash8("sha256").update(localFiles.join(",")).digest("hex").slice(0, 16);
11749
11759
  const prevLocalHash = lastLocalFileHash.get(agent.agent_id);
11750
11760
  const prevDownload = lastDownloadHash.get(agent.agent_id);
11751
11761
  try {
11752
11762
  const dbMemories = await api.post("/host/memories", {
11753
11763
  agent_id: agent.agent_id
11754
11764
  });
11755
- const responseHash = createHash6("sha256").update(JSON.stringify(dbMemories.memories ?? [])).digest("hex").slice(0, 16);
11765
+ const responseHash = createHash8("sha256").update(JSON.stringify(dbMemories.memories ?? [])).digest("hex").slice(0, 16);
11756
11766
  if (!force && prevDownload && prevLocalHash === localListHash && lastDownloadHash.get(agent.agent_id) === responseHash) {
11757
11767
  return true;
11758
11768
  }
@@ -11791,7 +11801,7 @@ ${mem.content}
11791
11801
  }
11792
11802
  if (written > 0 || overwritten > 0) {
11793
11803
  const updatedFiles = readdirSync5(memoryDir).filter((f) => f.endsWith(".md")).sort();
11794
- lastLocalFileHash.set(agent.agent_id, createHash6("sha256").update(updatedFiles.join(",")).digest("hex").slice(0, 16));
11804
+ lastLocalFileHash.set(agent.agent_id, createHash8("sha256").update(updatedFiles.join(",")).digest("hex").slice(0, 16));
11795
11805
  log2(`Memory download for '${agent.code_name}': wrote ${written} new, overwrote ${overwritten} stale`);
11796
11806
  }
11797
11807
  }
@@ -12228,7 +12238,7 @@ function deployMcpAssets() {
12228
12238
  const fileHash = (p) => {
12229
12239
  try {
12230
12240
  if (!existsSync9(p)) return null;
12231
- return createHash6("sha256").update(readFileSync14(p)).digest("hex");
12241
+ return createHash8("sha256").update(readFileSync14(p)).digest("hex");
12232
12242
  } catch {
12233
12243
  return null;
12234
12244
  }