@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.
@@ -23,7 +23,7 @@ import {
23
23
  requireHost,
24
24
  safeWriteJsonAtomic,
25
25
  setConfigHash
26
- } from "../chunk-T3CD664K.js";
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-2V5QUET6.js";
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-RYB5QSVS.js";
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 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`);
@@ -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
- api.post("/host/schedules/disable", { agent_id: agentId, task_id: task.taskId }).catch(
5131
- (err) => log(`[claude-scheduler] Failed to disable one-off task '${task.name}': ${err.message}`)
5132
- );
5133
- log(`[claude-scheduler] One-off task '${task.name}' fired and disabled for '${codeName}'`);
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 createHash6("sha256").update(canonicalJson(JSON.parse(raw))).digest("hex");
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.37" : "dev";
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-27Q5QJF6.js");
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 = createHash6("sha256").update(errText).digest("hex").slice(0, 12);
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-G6LP7KSH.js");
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-G6LP7KSH.js");
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 = createHash6("sha256").update(
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 = createHash6("sha256").update(canonicalJson({ senderPolicy: senderPolicyForRestartHash })).digest("hex");
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 = createHash6("sha256").update(canonicalJson(behaviourSubset)).digest("hex");
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 = createHash6("sha256").update(canonicalJson(slackBehaviourSubset)).digest("hex");
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 = createHash6("sha256").update(canonicalJson(e.headers ?? {})).digest("hex").slice(0, 16);
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 = createHash6("sha256").update(hashBasis).digest("hex").slice(0, 16);
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 = createHash6("sha256").update(e.url).digest("hex").slice(0, 12);
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: createHash7 } = await import("crypto");
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 = createHash7("sha256").update(bundleFingerprint(bundle.files)).digest("hex").slice(0, 12);
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) => createHash7("sha256").update(content).digest("hex").slice(0, 12),
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 = createHash7("sha256").update(hook.script).digest("hex").slice(0, 12);
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 = createHash7("sha256").update(result.stderr).digest("hex").slice(0, 12);
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 ? createHash7("sha256").update(missingCmd).digest("hex").slice(0, 8) : null;
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 = 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";
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 = createHash6("sha256").update(JSON.stringify(resolvedModels)).digest("hex").slice(0, 16);
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:${createHash6("sha256").update(ctx.tail).digest("hex").slice(0, 12)} (raw at ~/.augmented/${codeName}/pane.log)`;
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:${createHash6("sha256").update(zombie.paneTail).digest("hex").slice(0, 12)}` : null;
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 = createHash6("sha256").update(JSON.stringify(tasks)).digest("hex").slice(0, 16);
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 = createHash6("sha256").update(errMsg).digest("hex").slice(0, 12);
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-64KMVLCZ.js");
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 = createHash6("sha256").update(raw).digest("hex").slice(0, 16);
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 = createHash6("sha256").update(localFiles.join(",")).digest("hex").slice(0, 16);
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 = createHash6("sha256").update(JSON.stringify(dbMemories.memories ?? [])).digest("hex").slice(0, 16);
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, createHash6("sha256").update(updatedFiles.join(",")).digest("hex").slice(0, 16));
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 createHash6("sha256").update(readFileSync14(p)).digest("hex");
12267
+ return createHash8("sha256").update(readFileSync14(p)).digest("hex");
12232
12268
  } catch {
12233
12269
  return null;
12234
12270
  }