@integrity-labs/agt-cli 0.28.369 → 0.28.370
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/agt.js +4 -4
- package/dist/{chunk-RIWZIYJ7.js → chunk-NEM6OQLG.js} +1 -1
- package/dist/{chunk-JJX2AH5K.js → chunk-TDZFQHXC.js} +3 -3
- package/dist/{claude-pair-runtime-NNGG572N.js → claude-pair-runtime-N4WSO5ET.js} +2 -2
- package/dist/lib/manager-worker.js +110 -83
- package/dist/lib/manager-worker.js.map +1 -1
- package/dist/{persistent-session-3PAEXZHX.js → persistent-session-PW3D7TJO.js} +2 -2
- package/dist/{responsiveness-probe-UEOKWHTD.js → responsiveness-probe-G7HFTXCV.js} +2 -2
- package/package.json +1 -1
- /package/dist/{chunk-RIWZIYJ7.js.map → chunk-NEM6OQLG.js.map} +0 -0
- /package/dist/{chunk-JJX2AH5K.js.map → chunk-TDZFQHXC.js.map} +0 -0
- /package/dist/{claude-pair-runtime-NNGG572N.js.map → claude-pair-runtime-N4WSO5ET.js.map} +0 -0
- /package/dist/{persistent-session-3PAEXZHX.js.map → persistent-session-PW3D7TJO.js.map} +0 -0
- /package/dist/{responsiveness-probe-UEOKWHTD.js.map → responsiveness-probe-G7HFTXCV.js.map} +0 -0
|
@@ -43,7 +43,7 @@ import {
|
|
|
43
43
|
resolveEffectivePinRaw,
|
|
44
44
|
safeWriteJsonAtomic,
|
|
45
45
|
setConfigHash
|
|
46
|
-
} from "../chunk-
|
|
46
|
+
} from "../chunk-TDZFQHXC.js";
|
|
47
47
|
import {
|
|
48
48
|
getProjectDir as getProjectDir2,
|
|
49
49
|
getReadyTasks,
|
|
@@ -147,16 +147,16 @@ import {
|
|
|
147
147
|
toOpencodeModel,
|
|
148
148
|
transcriptActivityAgeSeconds,
|
|
149
149
|
writeEgressAllowlist
|
|
150
|
-
} from "../chunk-
|
|
150
|
+
} from "../chunk-NEM6OQLG.js";
|
|
151
151
|
import {
|
|
152
152
|
reapOrphanChannelMcps
|
|
153
153
|
} from "../chunk-XWVM4KPK.js";
|
|
154
154
|
|
|
155
155
|
// src/lib/manager-worker.ts
|
|
156
|
-
import { createHash as
|
|
157
|
-
import { readFileSync as readFileSync17, writeFileSync as writeFileSync11, mkdirSync as
|
|
156
|
+
import { createHash as createHash14 } from "crypto";
|
|
157
|
+
import { readFileSync as readFileSync17, writeFileSync as writeFileSync11, mkdirSync as mkdirSync9, existsSync as existsSync11, rmSync as rmSync5, readdirSync as readdirSync5, statSync as statSync4, copyFileSync } from "fs";
|
|
158
158
|
import { execFileSync as syncExecFile } from "child_process";
|
|
159
|
-
import { join as join21, dirname as
|
|
159
|
+
import { join as join21, dirname as dirname6, delimiter as pathDelimiter } from "path";
|
|
160
160
|
import { homedir as homedir10 } from "os";
|
|
161
161
|
import { fileURLToPath } from "url";
|
|
162
162
|
|
|
@@ -4966,10 +4966,35 @@ function closeScheduledRunsForCode(codeName, outcome, reason) {
|
|
|
4966
4966
|
}
|
|
4967
4967
|
|
|
4968
4968
|
// src/lib/manager/scheduler/kanban-route.ts
|
|
4969
|
-
import { createHash as
|
|
4970
|
-
import { writeFileSync as writeFileSync8, renameSync } from "fs";
|
|
4969
|
+
import { createHash as createHash9 } from "crypto";
|
|
4970
|
+
import { writeFileSync as writeFileSync8, renameSync, mkdirSync as mkdirSync6 } from "fs";
|
|
4971
4971
|
import { homedir as homedir7 } from "os";
|
|
4972
|
-
import { join as join17 } from "path";
|
|
4972
|
+
import { join as join17, dirname as dirname5 } from "path";
|
|
4973
|
+
|
|
4974
|
+
// src/lib/manager/scheduler/notify.ts
|
|
4975
|
+
import { createHash as createHash8 } from "crypto";
|
|
4976
|
+
async function enqueueScheduledTaskNotice(opts) {
|
|
4977
|
+
try {
|
|
4978
|
+
const res = await api.post(
|
|
4979
|
+
"/host/scheduled-task/notify",
|
|
4980
|
+
{
|
|
4981
|
+
agent_id: opts.agentId,
|
|
4982
|
+
task_id: opts.taskId,
|
|
4983
|
+
card_id: opts.cardId,
|
|
4984
|
+
content: opts.content,
|
|
4985
|
+
run_id: opts.runId ?? void 0
|
|
4986
|
+
}
|
|
4987
|
+
);
|
|
4988
|
+
return res.ok === true;
|
|
4989
|
+
} catch (err) {
|
|
4990
|
+
const errText = err instanceof Error ? err.message : String(err);
|
|
4991
|
+
const errId = createHash8("sha256").update(errText).digest("hex").slice(0, 12);
|
|
4992
|
+
log(
|
|
4993
|
+
`[scheduled-kanban] notice enqueue failed for agent_id=${opts.agentId} task_id=${opts.taskId} error_id=${errId}`
|
|
4994
|
+
);
|
|
4995
|
+
return false;
|
|
4996
|
+
}
|
|
4997
|
+
}
|
|
4973
4998
|
|
|
4974
4999
|
// src/lib/manager/delivery/scheduled-output.ts
|
|
4975
5000
|
async function deliverScheduledTaskOutput(agentCodeName, agentId, rawTarget, body, taskId) {
|
|
@@ -5315,22 +5340,24 @@ async function routeScheduledTaskViaKanban(codeName, agentId, task, prompt) {
|
|
|
5315
5340
|
` + formatRunMarker(run_id) : `A scheduled task just fired and it is now a card on YOUR board that YOU work. The schedule only controls WHEN this card arrives, not who executes it: nothing else runs it for you, so do the work yourself now. Card: id=${kanban_item_id} title=${JSON.stringify(task.name)}. Call kanban_move("${kanban_item_id}", "in_progress"), do the work described on the card, then write the finished result onto the card and mark it done. Do NOT send, post, or message the result to anyone yourself \u2014 the result you write on the card IS the message that will be delivered to the user; delivery happens automatically from the card.
|
|
5316
5341
|
` + suppressionTeaching + formatRunMarker(run_id);
|
|
5317
5342
|
stampScheduledTurnMarker(codeName, task.taskId, resolveScheduledSlackTarget(task));
|
|
5318
|
-
|
|
5319
|
-
|
|
5320
|
-
|
|
5321
|
-
|
|
5322
|
-
|
|
5323
|
-
|
|
5324
|
-
|
|
5325
|
-
|
|
5326
|
-
|
|
5327
|
-
|
|
5328
|
-
log(`[scheduled-kanban] nudge delivery failed for task '${task.name}' on '${codeName}' \u2014 closing card + falling back to claude -p`);
|
|
5329
|
-
void finishRun(run_id, "failed", { outcomeMessage: "scheduled-task nudge delivery failed", completeKanbanItemId: kanban_item_id });
|
|
5343
|
+
const enqueued = await enqueueScheduledTaskNotice({
|
|
5344
|
+
agentId,
|
|
5345
|
+
taskId: task.taskId,
|
|
5346
|
+
cardId: kanban_item_id,
|
|
5347
|
+
content: nudge,
|
|
5348
|
+
runId: run_id
|
|
5349
|
+
});
|
|
5350
|
+
if (!enqueued) {
|
|
5351
|
+
log(`[scheduled-kanban] durable nudge enqueue failed for task '${task.name}' on '${codeName}' - closing card + run`);
|
|
5352
|
+
void finishRun(run_id, "failed", { outcomeMessage: "scheduled-task durable nudge enqueue failed", completeKanbanItemId: kanban_item_id });
|
|
5330
5353
|
return false;
|
|
5331
5354
|
}
|
|
5332
|
-
|
|
5333
|
-
|
|
5355
|
+
try {
|
|
5356
|
+
const doorbell = directChatDoorbellPath(agentId, homedir7());
|
|
5357
|
+
mkdirSync6(dirname5(doorbell), { recursive: true });
|
|
5358
|
+
writeFileSync8(doorbell, String(Date.now()));
|
|
5359
|
+
} catch (err) {
|
|
5360
|
+
log(`[scheduled-kanban] doorbell ring failed for '${codeName}': ${err.message} (notice still queued)`);
|
|
5334
5361
|
}
|
|
5335
5362
|
trackScheduledRun(codeName, kanban_item_id, run_id);
|
|
5336
5363
|
log(`[scheduled-kanban] Routed task '${task.name}' for '${codeName}' via todo card ${kanban_item_id} run_id=${run_id}`);
|
|
@@ -5375,24 +5402,24 @@ async function processClaudeTaskResult(codeName, agentId, templateId, rawOutput,
|
|
|
5375
5402
|
const assertion = parseDeliverAssertion(rawOutput);
|
|
5376
5403
|
if (!assertion.deliver) {
|
|
5377
5404
|
const trimmed = (rawOutput ?? "").trim();
|
|
5378
|
-
const outputHash = trimmed.length === 0 ? "empty" :
|
|
5405
|
+
const outputHash = trimmed.length === 0 ? "empty" : createHash9("sha256").update(trimmed).digest("hex").slice(0, 12);
|
|
5379
5406
|
log(`[claude-scheduler] Suppressed by delivery_policy=conditional for '${codeName}' (template=${templateId}, task=${delivery?.taskId ?? "n/a"}) \u2014 ${assertion.vacuous ? "deliver marker had a vacuous reason" : "no deliver marker"}; output_len=${trimmed.length} output_hash=${outputHash}`);
|
|
5380
5407
|
if (delivery?.mode === "announce" && delivery.to) {
|
|
5381
5408
|
await reportDeliveryStatus(agentId, delivery.taskId, { status: "skipped", error_code: "SUPPRESSED_BY_POLICY" });
|
|
5382
5409
|
}
|
|
5383
5410
|
return { ok: true };
|
|
5384
5411
|
}
|
|
5385
|
-
const reasonHash =
|
|
5412
|
+
const reasonHash = createHash9("sha256").update(assertion.reason ?? "").digest("hex").slice(0, 12);
|
|
5386
5413
|
log(`[claude-scheduler] Delivering conditional run for '${codeName}' (template=${templateId}, task=${delivery?.taskId ?? "n/a"}) \u2014 agent asserted a concrete trigger (reason_len=${(assertion.reason ?? "").length} reason_hash=${reasonHash})`);
|
|
5387
5414
|
rawOutput = assertion.deliverable;
|
|
5388
5415
|
}
|
|
5389
5416
|
const classification = classifyOutput(rawOutput);
|
|
5390
5417
|
if (classification.action === "suppress") {
|
|
5391
5418
|
const trimmed = (rawOutput ?? "").trim();
|
|
5392
|
-
const outputHash = trimmed.length === 0 ? "empty" :
|
|
5419
|
+
const outputHash = trimmed.length === 0 ? "empty" : createHash9("sha256").update(trimmed).digest("hex").slice(0, 12);
|
|
5393
5420
|
log(`[claude-scheduler] Suppressing delivery for '${codeName}' (template=${templateId}, task=${delivery?.taskId ?? "n/a"}) \u2014 output_len=${trimmed.length} output_hash=${outputHash}`);
|
|
5394
5421
|
if (classification.suppressedNotes) {
|
|
5395
|
-
const notesHash =
|
|
5422
|
+
const notesHash = createHash9("sha256").update(classification.suppressedNotes).digest("hex").slice(0, 12);
|
|
5396
5423
|
log(`[claude-scheduler] Suppressed notes for '${codeName}' (task=${delivery?.taskId ?? "n/a"}) \u2014 notes_len=${classification.suppressedNotes.length} notes_hash=${notesHash}`);
|
|
5397
5424
|
}
|
|
5398
5425
|
if (delivery?.mode === "announce" && delivery.to) {
|
|
@@ -5458,7 +5485,7 @@ async function processClaudeTaskResult(codeName, agentId, templateId, rawOutput,
|
|
|
5458
5485
|
}
|
|
5459
5486
|
|
|
5460
5487
|
// src/lib/manager/scheduler/execution.ts
|
|
5461
|
-
import { createHash as
|
|
5488
|
+
import { createHash as createHash10 } from "crypto";
|
|
5462
5489
|
import { homedir as homedir8 } from "os";
|
|
5463
5490
|
import { join as join18 } from "path";
|
|
5464
5491
|
function claudePidFilePath() {
|
|
@@ -5482,10 +5509,10 @@ function unregisterClaudeSpawn(pid) {
|
|
|
5482
5509
|
}
|
|
5483
5510
|
async function syncAndCheckClaudeScheduler(agent, tasks, boardItems, refreshData) {
|
|
5484
5511
|
const codeName = agent.code_name;
|
|
5485
|
-
const stableTasksHash =
|
|
5486
|
-
const boardHash = boardItems.length > 0 ?
|
|
5512
|
+
const stableTasksHash = createHash10("sha256").update(JSON.stringify(tasks)).digest("hex").slice(0, 16);
|
|
5513
|
+
const boardHash = boardItems.length > 0 ? createHash10("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";
|
|
5487
5514
|
const resolvedModels = resolveModelChain(refreshData);
|
|
5488
|
-
const modelsHash =
|
|
5515
|
+
const modelsHash = createHash10("sha256").update(JSON.stringify(resolvedModels)).digest("hex").slice(0, 16);
|
|
5489
5516
|
const combinedHash = `${stableTasksHash}:${boardHash}:${modelsHash}`;
|
|
5490
5517
|
const prevHash = agentState.knownTasksHashes.get(agent.agent_id);
|
|
5491
5518
|
if (combinedHash !== prevHash) {
|
|
@@ -5523,7 +5550,7 @@ async function syncAndCheckClaudeScheduler(agent, tasks, boardItems, refreshData
|
|
|
5523
5550
|
}
|
|
5524
5551
|
|
|
5525
5552
|
// src/lib/opencode-slack-ingest.ts
|
|
5526
|
-
import { createHash as
|
|
5553
|
+
import { createHash as createHash11 } from "crypto";
|
|
5527
5554
|
|
|
5528
5555
|
// ../../packages/core/dist/channels/slack-rich-text.js
|
|
5529
5556
|
var MAX_DEPTH = 12;
|
|
@@ -6459,7 +6486,7 @@ function startSlackIngest(config2) {
|
|
|
6459
6486
|
// src/lib/opencode-slack-ingest.ts
|
|
6460
6487
|
var ingests = /* @__PURE__ */ new Map();
|
|
6461
6488
|
function fingerprint(params) {
|
|
6462
|
-
return
|
|
6489
|
+
return createHash11("sha256").update(
|
|
6463
6490
|
JSON.stringify({
|
|
6464
6491
|
appToken: params.appToken,
|
|
6465
6492
|
botToken: params.botToken,
|
|
@@ -6540,7 +6567,7 @@ function stopAllOpencodeSlackIngests(log2) {
|
|
|
6540
6567
|
}
|
|
6541
6568
|
|
|
6542
6569
|
// src/lib/manager/opencode-scheduler.ts
|
|
6543
|
-
import { createHash as
|
|
6570
|
+
import { createHash as createHash12 } from "crypto";
|
|
6544
6571
|
var MAX_OPENCODE_SCHED_CONCURRENCY = 2;
|
|
6545
6572
|
var opencodeSchedulerStates = /* @__PURE__ */ new Map();
|
|
6546
6573
|
var inFlightOpencodeTasks = /* @__PURE__ */ new Set();
|
|
@@ -6554,7 +6581,7 @@ function shouldDeliver(task, reply) {
|
|
|
6554
6581
|
}
|
|
6555
6582
|
async function syncAndCheckOpencodeScheduler(agent, tasks, refreshData) {
|
|
6556
6583
|
const codeName = agent.code_name;
|
|
6557
|
-
const tasksHash =
|
|
6584
|
+
const tasksHash = createHash12("sha256").update(JSON.stringify(tasks)).digest("hex").slice(0, 16);
|
|
6558
6585
|
if (knownOpencodeTaskHashes.get(agent.agent_id) !== tasksHash) {
|
|
6559
6586
|
const taskInputs = tasks.map((t) => buildSchedulerTaskInput(t));
|
|
6560
6587
|
const state8 = syncTasksToScheduler(codeName, agent.agent_id, taskInputs);
|
|
@@ -6632,8 +6659,8 @@ async function fireOpencodeScheduledTask(agent, task) {
|
|
|
6632
6659
|
}
|
|
6633
6660
|
|
|
6634
6661
|
// src/lib/opencode-telegram-ingest.ts
|
|
6635
|
-
import { createHash as
|
|
6636
|
-
import { existsSync as existsSync9, mkdirSync as
|
|
6662
|
+
import { createHash as createHash13 } from "crypto";
|
|
6663
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync7, readFileSync as readFileSync15, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync9 } from "fs";
|
|
6637
6664
|
import { randomUUID } from "crypto";
|
|
6638
6665
|
import { join as join19 } from "path";
|
|
6639
6666
|
|
|
@@ -7168,7 +7195,7 @@ function anySignal(signals) {
|
|
|
7168
7195
|
// src/lib/opencode-telegram-ingest.ts
|
|
7169
7196
|
var ingests2 = /* @__PURE__ */ new Map();
|
|
7170
7197
|
function fingerprint2(params) {
|
|
7171
|
-
return
|
|
7198
|
+
return createHash13("sha256").update(
|
|
7172
7199
|
JSON.stringify({
|
|
7173
7200
|
botToken: params.botToken,
|
|
7174
7201
|
allowedChats: [...params.allowedChats].sort(),
|
|
@@ -7210,7 +7237,7 @@ function createFileOffsetStore(codeName, log2, currentBotId) {
|
|
|
7210
7237
|
if (!filePath || !dir) return;
|
|
7211
7238
|
const tmpPath = `${filePath}.${process.pid}.${randomUUID()}.tmp`;
|
|
7212
7239
|
try {
|
|
7213
|
-
|
|
7240
|
+
mkdirSync7(dir, { recursive: true, mode: 448 });
|
|
7214
7241
|
writeFileSync9(
|
|
7215
7242
|
tmpPath,
|
|
7216
7243
|
JSON.stringify({
|
|
@@ -7442,7 +7469,7 @@ function partitionActionableByPoison(actionable, states, config2) {
|
|
|
7442
7469
|
}
|
|
7443
7470
|
|
|
7444
7471
|
// src/lib/restart-flags.ts
|
|
7445
|
-
import { existsSync as existsSync10, mkdirSync as
|
|
7472
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync8, readdirSync as readdirSync4, readFileSync as readFileSync16, renameSync as renameSync3, rmSync as rmSync4, writeFileSync as writeFileSync10 } from "fs";
|
|
7446
7473
|
import { homedir as homedir9 } from "os";
|
|
7447
7474
|
import { join as join20 } from "path";
|
|
7448
7475
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
@@ -8724,7 +8751,7 @@ var sessionLaunchManagedStructure = /* @__PURE__ */ new Map();
|
|
|
8724
8751
|
function projectMcpHash(_codeName, projectDir) {
|
|
8725
8752
|
try {
|
|
8726
8753
|
const raw = readFileSync17(join21(projectDir, ".mcp.json"), "utf-8");
|
|
8727
|
-
return
|
|
8754
|
+
return createHash14("sha256").update(canonicalJson(JSON.parse(raw))).digest("hex");
|
|
8728
8755
|
} catch {
|
|
8729
8756
|
return null;
|
|
8730
8757
|
}
|
|
@@ -9072,7 +9099,7 @@ var agentRestartTimezoneInputs = /* @__PURE__ */ new Map();
|
|
|
9072
9099
|
var lastVersionCheckAt = 0;
|
|
9073
9100
|
var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
|
|
9074
9101
|
var lastResponsivenessProbeAt = 0;
|
|
9075
|
-
var agtCliVersion = true ? "0.28.
|
|
9102
|
+
var agtCliVersion = true ? "0.28.370" : "dev";
|
|
9076
9103
|
function resolveBrewPath(execFileSync2) {
|
|
9077
9104
|
try {
|
|
9078
9105
|
const out = execFileSync2("which", ["brew"], { timeout: 5e3 }).toString().trim();
|
|
@@ -9167,7 +9194,7 @@ async function ensureToolkitCli(toolkitSlug) {
|
|
|
9167
9194
|
toolkitCliEnsured.add(toolkitSlug);
|
|
9168
9195
|
return;
|
|
9169
9196
|
}
|
|
9170
|
-
brewBinDir =
|
|
9197
|
+
brewBinDir = dirname6(brewPath);
|
|
9171
9198
|
const isRoot = typeof process.getuid === "function" && process.getuid() === 0;
|
|
9172
9199
|
log(`[toolkit-install] ${toolkitSlug}: installing via brew (${pkg})\u2026`);
|
|
9173
9200
|
if (isRoot) {
|
|
@@ -9267,7 +9294,7 @@ function ensureClaudeManagedSettings(path = claudeManagedSettingsPath()) {
|
|
|
9267
9294
|
}
|
|
9268
9295
|
if (settings.channelsEnabled === true) return "ok";
|
|
9269
9296
|
settings.channelsEnabled = true;
|
|
9270
|
-
|
|
9297
|
+
mkdirSync9(dirname6(path), { recursive: true });
|
|
9271
9298
|
writeFileSync11(path, `${JSON.stringify(settings, null, 2)}
|
|
9272
9299
|
`);
|
|
9273
9300
|
log(`[managed-settings] set channelsEnabled:true in ${path} (ENG-5786 \u2014 unblocks Claude Code channels)`);
|
|
@@ -9363,7 +9390,7 @@ async function ensureFrameworkBinary(frameworkId) {
|
|
|
9363
9390
|
log(`Claude Code install failed: ${err.message}`);
|
|
9364
9391
|
return;
|
|
9365
9392
|
}
|
|
9366
|
-
const brewBinDir =
|
|
9393
|
+
const brewBinDir = dirname6(brewPath);
|
|
9367
9394
|
if (!process.env.PATH?.split(":").includes(brewBinDir)) {
|
|
9368
9395
|
process.env.PATH = `${brewBinDir}:${process.env.PATH ?? ""}`;
|
|
9369
9396
|
}
|
|
@@ -9797,7 +9824,7 @@ function ensureEvalEmptyMcpConfig() {
|
|
|
9797
9824
|
if (evalEmptyMcpConfigPath && existsSync11(evalEmptyMcpConfigPath)) return evalEmptyMcpConfigPath;
|
|
9798
9825
|
const dir = join21(homedir10(), ".augmented");
|
|
9799
9826
|
try {
|
|
9800
|
-
|
|
9827
|
+
mkdirSync9(dir, { recursive: true });
|
|
9801
9828
|
} catch {
|
|
9802
9829
|
}
|
|
9803
9830
|
const p = join21(dir, ".eval-empty-mcp.json");
|
|
@@ -10091,7 +10118,7 @@ function flushRestartedAgentDiagnostics(hostId, codeNames) {
|
|
|
10091
10118
|
if (codeNames.length === 0) return;
|
|
10092
10119
|
void (async () => {
|
|
10093
10120
|
try {
|
|
10094
|
-
const { collectDiagnostics } = await import("../persistent-session-
|
|
10121
|
+
const { collectDiagnostics } = await import("../persistent-session-PW3D7TJO.js");
|
|
10095
10122
|
await api.post("/host/heartbeat", {
|
|
10096
10123
|
host_id: hostId,
|
|
10097
10124
|
agent_diagnostics: collectDiagnostics(codeNames)
|
|
@@ -10190,7 +10217,7 @@ async function pollCycle() {
|
|
|
10190
10217
|
}
|
|
10191
10218
|
try {
|
|
10192
10219
|
const { detectHostSecurity } = await import("../host-security-6PDFG7F5.js");
|
|
10193
|
-
const { collectDiagnostics } = await import("../persistent-session-
|
|
10220
|
+
const { collectDiagnostics } = await import("../persistent-session-PW3D7TJO.js");
|
|
10194
10221
|
const diagCodeNames = [...agentState.persistentSessionAgents];
|
|
10195
10222
|
const agentDiagnostics = diagCodeNames.length > 0 ? collectDiagnostics(diagCodeNames) : void 0;
|
|
10196
10223
|
let tailscaleHostname;
|
|
@@ -10220,7 +10247,7 @@ async function pollCycle() {
|
|
|
10220
10247
|
claudeAuth = await detectClaudeAuth();
|
|
10221
10248
|
} catch (err) {
|
|
10222
10249
|
const errText = err instanceof Error ? err.message : String(err);
|
|
10223
|
-
const errId =
|
|
10250
|
+
const errId = createHash14("sha256").update(errText).digest("hex").slice(0, 12);
|
|
10224
10251
|
log(`Claude auth detection failed (error_id=${errId})`);
|
|
10225
10252
|
}
|
|
10226
10253
|
const hostHasClaudeCode = state6.agents.some(
|
|
@@ -10345,7 +10372,7 @@ async function pollCycle() {
|
|
|
10345
10372
|
const {
|
|
10346
10373
|
collectResponsivenessProbes,
|
|
10347
10374
|
getResponsivenessIntervalMs
|
|
10348
|
-
} = await import("../responsiveness-probe-
|
|
10375
|
+
} = await import("../responsiveness-probe-G7HFTXCV.js");
|
|
10349
10376
|
const probeIntervalMs = getResponsivenessIntervalMs();
|
|
10350
10377
|
if (now - lastResponsivenessProbeAt > probeIntervalMs) {
|
|
10351
10378
|
const probeCodeNames = [...agentState.persistentSessionAgents];
|
|
@@ -10377,7 +10404,7 @@ async function pollCycle() {
|
|
|
10377
10404
|
collectResponsivenessProbes,
|
|
10378
10405
|
livePendingInboundOldestAgeSeconds,
|
|
10379
10406
|
parkPendingInbound
|
|
10380
|
-
} = await import("../responsiveness-probe-
|
|
10407
|
+
} = await import("../responsiveness-probe-G7HFTXCV.js");
|
|
10381
10408
|
const { getProjectDir: wedgeProjectDir } = await import("../scheduler-engine-HYOOQV4A.js");
|
|
10382
10409
|
const wedgeNow = /* @__PURE__ */ new Date();
|
|
10383
10410
|
const liveAgents = agentState.persistentSessionAgents;
|
|
@@ -11179,7 +11206,7 @@ async function processAgent(agent, agentStates) {
|
|
|
11179
11206
|
try {
|
|
11180
11207
|
const artifacts = generateArtifacts(agent, refreshData, frameworkAdapter);
|
|
11181
11208
|
const changedFiles = [];
|
|
11182
|
-
|
|
11209
|
+
mkdirSync9(agentDir, { recursive: true });
|
|
11183
11210
|
for (const artifact of artifacts) {
|
|
11184
11211
|
const filePath = join21(agentDir, artifact.relativePath);
|
|
11185
11212
|
let existingHash;
|
|
@@ -11257,7 +11284,7 @@ async function processAgent(agent, agentStates) {
|
|
|
11257
11284
|
log(`${verb} '${agent.code_name}': ${fileNames}`);
|
|
11258
11285
|
for (const file of changedFiles) {
|
|
11259
11286
|
const filePath = join21(agentDir, file.relativePath);
|
|
11260
|
-
|
|
11287
|
+
mkdirSync9(dirname6(filePath), { recursive: true });
|
|
11261
11288
|
if (file.relativePath === ".mcp.json") {
|
|
11262
11289
|
safeWriteJsonAtomic(filePath, file.content, { mode: 384 });
|
|
11263
11290
|
} else {
|
|
@@ -11589,7 +11616,7 @@ async function processAgent(agent, agentStates) {
|
|
|
11589
11616
|
const writeDmNoticeMarkers = isChannelAddRestart ? () => {
|
|
11590
11617
|
try {
|
|
11591
11618
|
const agentAugmentedDir = join21(homedir10(), ".augmented", agent.code_name);
|
|
11592
|
-
|
|
11619
|
+
mkdirSync9(agentAugmentedDir, { recursive: true });
|
|
11593
11620
|
const markerJson = JSON.stringify({
|
|
11594
11621
|
version: 1,
|
|
11595
11622
|
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -11672,7 +11699,7 @@ async function processAgent(agent, agentStates) {
|
|
|
11672
11699
|
const behaviourSubset = extractMsTeamsBehaviourSubset(
|
|
11673
11700
|
msteamsEntry?.config
|
|
11674
11701
|
);
|
|
11675
|
-
const behaviourHash =
|
|
11702
|
+
const behaviourHash = createHash14("sha256").update(canonicalJson(behaviourSubset)).digest("hex");
|
|
11676
11703
|
const prevBehaviourHash = agentState.knownMsTeamsBehaviourHashes.get(agent.agent_id);
|
|
11677
11704
|
const msteamsBehaviourRestrictive = isMsTeamsBehaviourRestrictive(behaviourSubset);
|
|
11678
11705
|
const behaviourDecision = decideSenderPolicyRestart({
|
|
@@ -11728,7 +11755,7 @@ async function processAgent(agent, agentStates) {
|
|
|
11728
11755
|
const slackBehaviourSubset = extractSlackBehaviourSubset(
|
|
11729
11756
|
slackEntry?.config
|
|
11730
11757
|
);
|
|
11731
|
-
const slackBehaviourHash =
|
|
11758
|
+
const slackBehaviourHash = createHash14("sha256").update(canonicalJson(slackBehaviourSubset)).digest("hex");
|
|
11732
11759
|
const prevSlackBehaviourHash = agentState.knownSlackBehaviourHashes.get(agent.agent_id);
|
|
11733
11760
|
const slackBehaviourRestrictive = isSlackBehaviourRestrictive(slackBehaviourSubset);
|
|
11734
11761
|
const slackBehaviourDecision = decideSenderPolicyRestart({
|
|
@@ -11786,8 +11813,8 @@ async function processAgent(agent, agentStates) {
|
|
|
11786
11813
|
try {
|
|
11787
11814
|
const agentProvisionDir = agentDir;
|
|
11788
11815
|
const projectDir = join21(homedir10(), ".augmented", agent.code_name, "project");
|
|
11789
|
-
|
|
11790
|
-
|
|
11816
|
+
mkdirSync9(agentProvisionDir, { recursive: true });
|
|
11817
|
+
mkdirSync9(projectDir, { recursive: true });
|
|
11791
11818
|
const provisionMcpPath = join21(agentProvisionDir, ".mcp.json");
|
|
11792
11819
|
const projectMcpPath = join21(projectDir, ".mcp.json");
|
|
11793
11820
|
let mcpConfig = { mcpServers: {} };
|
|
@@ -12097,10 +12124,10 @@ async function processAgent(agent, agentStates) {
|
|
|
12097
12124
|
desiredEntries.push({ serverId, url, headers: mcpHeaders, name: tk.toolkit_name });
|
|
12098
12125
|
}
|
|
12099
12126
|
const hashBasis = desiredEntries.slice().sort((a, b) => a.serverId.localeCompare(b.serverId)).map((e) => {
|
|
12100
|
-
const headersHash =
|
|
12127
|
+
const headersHash = createHash14("sha256").update(canonicalJson(e.headers ?? {})).digest("hex").slice(0, 16);
|
|
12101
12128
|
return `${e.serverId}|${e.url}|${headersHash}`;
|
|
12102
12129
|
}).join("\n");
|
|
12103
|
-
const mcpHash =
|
|
12130
|
+
const mcpHash = createHash14("sha256").update(hashBasis).digest("hex").slice(0, 16);
|
|
12104
12131
|
const prevMcpHash = agentState.knownManagedMcpHashes.get(agent.agent_id);
|
|
12105
12132
|
const structureHash = managedMcpStructureHash(desiredEntries);
|
|
12106
12133
|
const prevStructureHash = agentState.knownManagedMcpStructure.get(agent.agent_id);
|
|
@@ -12115,7 +12142,7 @@ async function processAgent(agent, agentStates) {
|
|
|
12115
12142
|
if (mcpHash !== prevMcpHash) {
|
|
12116
12143
|
for (const e of desiredEntries) {
|
|
12117
12144
|
frameworkAdapter.writeMcpServer(agent.code_name, e.serverId, { url: e.url, headers: e.headers });
|
|
12118
|
-
const urlHash =
|
|
12145
|
+
const urlHash = createHash14("sha256").update(e.url).digest("hex").slice(0, 12);
|
|
12119
12146
|
log(`[managed-toolkit] ${agent.code_name}: wrote '${e.name}' (serverId=${e.serverId}, url_hash=${urlHash})`);
|
|
12120
12147
|
}
|
|
12121
12148
|
if (frameworkAdapter.removeMcpServer && frameworkAdapter.getMcpPath) {
|
|
@@ -12224,7 +12251,7 @@ async function processAgent(agent, agentStates) {
|
|
|
12224
12251
|
if (frameworkAdapter.installSkillFiles) {
|
|
12225
12252
|
const currentIntegrationSkillIds = /* @__PURE__ */ new Set();
|
|
12226
12253
|
const installedIntegrationSkills = [];
|
|
12227
|
-
const { createHash:
|
|
12254
|
+
const { createHash: createHash15 } = await import("crypto");
|
|
12228
12255
|
const refreshAny = refreshData;
|
|
12229
12256
|
const contexts = refreshAny.integration_contexts ?? refreshAny.plugin_contexts ?? [];
|
|
12230
12257
|
const contextBySlug = /* @__PURE__ */ new Map();
|
|
@@ -12253,7 +12280,7 @@ async function processAgent(agent, agentStates) {
|
|
|
12253
12280
|
)
|
|
12254
12281
|
}));
|
|
12255
12282
|
const bundle = buildIntegrationBundle(renderedScopes);
|
|
12256
|
-
const contentHash =
|
|
12283
|
+
const contentHash = createHash15("sha256").update(bundleFingerprint(bundle.files)).digest("hex").slice(0, 12);
|
|
12257
12284
|
const hashKey = `plugin-skill:${agent.agent_id}:${integrationSkillId}`;
|
|
12258
12285
|
if (agentState.knownSkillHashes.get(hashKey) === contentHash) continue;
|
|
12259
12286
|
frameworkAdapter.installSkillFiles(agent.code_name, integrationSkillId, bundle.files);
|
|
@@ -12320,7 +12347,7 @@ async function processAgent(agent, agentStates) {
|
|
|
12320
12347
|
const plan = planGlobalSkillSync(
|
|
12321
12348
|
[...globalSkillsPayload ?? [], ...sharedSkillsPayload ?? []],
|
|
12322
12349
|
prevIds,
|
|
12323
|
-
(content) =>
|
|
12350
|
+
(content) => createHash15("sha256").update(content).digest("hex").slice(0, 12),
|
|
12324
12351
|
(skillId) => agentState.knownSkillHashes.get(`global-skill:${agent.agent_id}:${skillId}`),
|
|
12325
12352
|
{ desiredResolved }
|
|
12326
12353
|
);
|
|
@@ -12371,7 +12398,7 @@ async function processAgent(agent, agentStates) {
|
|
|
12371
12398
|
const slug = hook.integration_slug ?? hook.plugin_slug;
|
|
12372
12399
|
if (!slug) continue;
|
|
12373
12400
|
try {
|
|
12374
|
-
const scriptHash =
|
|
12401
|
+
const scriptHash = createHash15("sha256").update(hook.script).digest("hex").slice(0, 12);
|
|
12375
12402
|
const hookKey = `${agent.agent_id}:${frameworkAdapter.id}:plugin-hook:${slug}:on_install`;
|
|
12376
12403
|
if (agentState.knownSkillHashes.get(hookKey) === scriptHash) continue;
|
|
12377
12404
|
const result = await frameworkAdapter.executePluginHook({
|
|
@@ -12386,9 +12413,9 @@ async function processAgent(agent, agentStates) {
|
|
|
12386
12413
|
} else if (result.timedOut) {
|
|
12387
12414
|
log(`Integration hook on_install '${slug}' TIMED OUT for '${agent.code_name}' after ${result.durationMs}ms`);
|
|
12388
12415
|
} else {
|
|
12389
|
-
const stderrHash =
|
|
12416
|
+
const stderrHash = createHash15("sha256").update(result.stderr).digest("hex").slice(0, 12);
|
|
12390
12417
|
const missingCmd = result.exitCode === 127 ? extractCommandNotFound(result.stderr) : null;
|
|
12391
|
-
const missingCmdHash = missingCmd ?
|
|
12418
|
+
const missingCmdHash = missingCmd ? createHash15("sha256").update(missingCmd).digest("hex").slice(0, 8) : null;
|
|
12392
12419
|
log(
|
|
12393
12420
|
`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}]`
|
|
12394
12421
|
);
|
|
@@ -13162,7 +13189,7 @@ async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
|
|
|
13162
13189
|
const ctx = getLastFailureContext(codeName);
|
|
13163
13190
|
const recovery = prepareForRespawn(codeName);
|
|
13164
13191
|
const tailSummary = !ctx.tail ? "" : KNOWN_SAFE_TAIL_SIGNATURES.has(ctx.signature) ? `; last pane output (${PANE_TAIL_PREVIEW_LINES} of ~20 lines):
|
|
13165
|
-
${truncateForLog(ctx.tail)}` : `; pane_tail_hash=sha256:${
|
|
13192
|
+
${truncateForLog(ctx.tail)}` : `; pane_tail_hash=sha256:${createHash14("sha256").update(ctx.tail).digest("hex").slice(0, 12)} (raw at ~/.augmented/${codeName}/pane.log)`;
|
|
13166
13193
|
const sigSummary = ctx.signature !== "unknown" ? `; signature=${ctx.signature}` : "";
|
|
13167
13194
|
const recoverySummary = recovery ? `; recovery=${recovery}` : "";
|
|
13168
13195
|
log(
|
|
@@ -13176,7 +13203,7 @@ ${truncateForLog(ctx.tail)}` : `; pane_tail_hash=sha256:${createHash13("sha256")
|
|
|
13176
13203
|
);
|
|
13177
13204
|
getHostId().then((hostId) => {
|
|
13178
13205
|
if (!hostId) return;
|
|
13179
|
-
const paneTailHash = zombie.paneTail ? `sha256:${
|
|
13206
|
+
const paneTailHash = zombie.paneTail ? `sha256:${createHash14("sha256").update(zombie.paneTail).digest("hex").slice(0, 12)}` : null;
|
|
13180
13207
|
return api.post("/host/events", {
|
|
13181
13208
|
host_id: hostId,
|
|
13182
13209
|
agent_code_name: codeName,
|
|
@@ -13325,7 +13352,7 @@ ${truncateForLog(ctx.tail)}` : `; pane_tail_hash=sha256:${createHash13("sha256")
|
|
|
13325
13352
|
if (!claudeAuthTupleBySession.has(codeName)) {
|
|
13326
13353
|
claudeAuthTupleBySession.set(codeName, currentAuthTuple);
|
|
13327
13354
|
}
|
|
13328
|
-
const stableTasksHash =
|
|
13355
|
+
const stableTasksHash = createHash14("sha256").update(JSON.stringify(tasks)).digest("hex").slice(0, 16);
|
|
13329
13356
|
const prevHash = agentState.knownTasksHashes.get(agent.agent_id);
|
|
13330
13357
|
if (stableTasksHash !== prevHash) {
|
|
13331
13358
|
const taskInputs = tasks.map((t) => buildSchedulerTaskInput(t));
|
|
@@ -13542,7 +13569,7 @@ async function handleRestartDoorbell(agentId, requestedAt, restartReason) {
|
|
|
13542
13569
|
void api.post("/host/restart-ack", { host_id: hostId, agent_id: agentId, restart_requested_at: requestedAt }).catch((err) => log(`[restart-lane] ack failed for '${codeName}': ${err.message}`));
|
|
13543
13570
|
void (async () => {
|
|
13544
13571
|
try {
|
|
13545
|
-
const { collectDiagnostics } = await import("../persistent-session-
|
|
13572
|
+
const { collectDiagnostics } = await import("../persistent-session-PW3D7TJO.js");
|
|
13546
13573
|
await api.post("/host/heartbeat", {
|
|
13547
13574
|
host_id: hostId,
|
|
13548
13575
|
agent_diagnostics: collectDiagnostics([codeName])
|
|
@@ -13592,7 +13619,7 @@ async function respawnAgentAfterMcpStop(codeName, reason) {
|
|
|
13592
13619
|
}
|
|
13593
13620
|
try {
|
|
13594
13621
|
const hostId = await getHostId();
|
|
13595
|
-
const { collectDiagnostics } = await import("../persistent-session-
|
|
13622
|
+
const { collectDiagnostics } = await import("../persistent-session-PW3D7TJO.js");
|
|
13596
13623
|
await api.post("/host/heartbeat", {
|
|
13597
13624
|
host_id: hostId,
|
|
13598
13625
|
agent_diagnostics: collectDiagnostics([codeName])
|
|
@@ -13878,7 +13905,7 @@ async function processDirectChatMessage(agent, msg) {
|
|
|
13878
13905
|
if (useDoorbell) {
|
|
13879
13906
|
try {
|
|
13880
13907
|
const doorbell = directChatDoorbellPath(agent.agentId, homedir10());
|
|
13881
|
-
|
|
13908
|
+
mkdirSync9(dirname6(doorbell), { recursive: true });
|
|
13882
13909
|
writeFileSync11(doorbell, String(Date.now()));
|
|
13883
13910
|
log(`[direct-chat] Doorbell rung for '${agent.codeName}' (msg=${msg.id}) \u2014 in-session MCP will pull via the cursor`);
|
|
13884
13911
|
return;
|
|
@@ -13988,7 +14015,7 @@ ${formatRunMarker(run_id)}` : KANBAN_CHECK_COMMAND;
|
|
|
13988
14015
|
if (run_id) openInjectedRunByCode.set(codeName, run_id);
|
|
13989
14016
|
try {
|
|
13990
14017
|
const doorbell = directChatDoorbellPath(agentId, homedir10());
|
|
13991
|
-
|
|
14018
|
+
mkdirSync9(dirname6(doorbell), { recursive: true });
|
|
13992
14019
|
writeFileSync11(doorbell, String(Date.now()));
|
|
13993
14020
|
} catch (err) {
|
|
13994
14021
|
log(`[kanban] doorbell ring failed for '${codeName}': ${err.message} (notice still queued)`);
|
|
@@ -14090,7 +14117,7 @@ async function processClaudePairSessions(agents) {
|
|
|
14090
14117
|
killPairSession,
|
|
14091
14118
|
pairTmuxSession,
|
|
14092
14119
|
finalizeClaudePairOnboarding
|
|
14093
|
-
} = await import("../claude-pair-runtime-
|
|
14120
|
+
} = await import("../claude-pair-runtime-N4WSO5ET.js");
|
|
14094
14121
|
for (const pairId of pendingResp.cancelled_pair_ids ?? []) {
|
|
14095
14122
|
log(`[claude-pair] sweeping orphan tmux session for pair ${pairId.slice(0, 8)}`);
|
|
14096
14123
|
const killed = await killPairSession(pairTmuxSession(pairId));
|
|
@@ -14360,7 +14387,7 @@ async function syncMemories(agent, configDir, log2) {
|
|
|
14360
14387
|
if (!file.endsWith(".md")) continue;
|
|
14361
14388
|
try {
|
|
14362
14389
|
const raw = readFileSync17(join21(memoryDir, file), "utf-8");
|
|
14363
|
-
const fileHash =
|
|
14390
|
+
const fileHash = createHash14("sha256").update(raw).digest("hex").slice(0, 16);
|
|
14364
14391
|
currentHashes.set(file, fileHash);
|
|
14365
14392
|
if (prevHashes.get(file) === fileHash) continue;
|
|
14366
14393
|
const parsed = parseMemoryFile(raw, file.replace(/\.md$/, ""));
|
|
@@ -14398,21 +14425,21 @@ async function syncMemories(agent, configDir, log2) {
|
|
|
14398
14425
|
}
|
|
14399
14426
|
async function downloadMemories(agent, memoryDir, log2, { force }) {
|
|
14400
14427
|
const localFiles = existsSync11(memoryDir) ? readdirSync5(memoryDir).filter((f) => f.endsWith(".md")).sort() : [];
|
|
14401
|
-
const localListHash =
|
|
14428
|
+
const localListHash = createHash14("sha256").update(localFiles.join(",")).digest("hex").slice(0, 16);
|
|
14402
14429
|
const prevLocalHash = lastLocalFileHash.get(agent.agent_id);
|
|
14403
14430
|
const prevDownload = lastDownloadHash.get(agent.agent_id);
|
|
14404
14431
|
try {
|
|
14405
14432
|
const dbMemories = await api.post("/host/memories", {
|
|
14406
14433
|
agent_id: agent.agent_id
|
|
14407
14434
|
});
|
|
14408
|
-
const responseHash =
|
|
14435
|
+
const responseHash = createHash14("sha256").update(JSON.stringify(dbMemories.memories ?? [])).digest("hex").slice(0, 16);
|
|
14409
14436
|
if (!force && prevDownload && prevLocalHash === localListHash && lastDownloadHash.get(agent.agent_id) === responseHash) {
|
|
14410
14437
|
return true;
|
|
14411
14438
|
}
|
|
14412
14439
|
lastDownloadHash.set(agent.agent_id, responseHash);
|
|
14413
14440
|
lastLocalFileHash.set(agent.agent_id, localListHash);
|
|
14414
14441
|
if (dbMemories.memories?.length) {
|
|
14415
|
-
|
|
14442
|
+
mkdirSync9(memoryDir, { recursive: true });
|
|
14416
14443
|
let written = 0;
|
|
14417
14444
|
let overwritten = 0;
|
|
14418
14445
|
for (let i = 0; i < dbMemories.memories.length; i++) {
|
|
@@ -14444,7 +14471,7 @@ ${mem.content}
|
|
|
14444
14471
|
}
|
|
14445
14472
|
if (written > 0 || overwritten > 0) {
|
|
14446
14473
|
const updatedFiles = readdirSync5(memoryDir).filter((f) => f.endsWith(".md")).sort();
|
|
14447
|
-
lastLocalFileHash.set(agent.agent_id,
|
|
14474
|
+
lastLocalFileHash.set(agent.agent_id, createHash14("sha256").update(updatedFiles.join(",")).digest("hex").slice(0, 16));
|
|
14448
14475
|
log2(`Memory download for '${agent.code_name}': wrote ${written} new, overwrote ${overwritten} stale`);
|
|
14449
14476
|
}
|
|
14450
14477
|
}
|
|
@@ -14839,8 +14866,8 @@ function restartRunningChannelMcps(basenames) {
|
|
|
14839
14866
|
}
|
|
14840
14867
|
function deployMcpAssets() {
|
|
14841
14868
|
const targetDir = join21(homedir10(), ".augmented", "_mcp");
|
|
14842
|
-
|
|
14843
|
-
const moduleDir =
|
|
14869
|
+
mkdirSync9(targetDir, { recursive: true });
|
|
14870
|
+
const moduleDir = dirname6(fileURLToPath(import.meta.url));
|
|
14844
14871
|
let mcpSourceDir = "";
|
|
14845
14872
|
let dir = moduleDir;
|
|
14846
14873
|
for (let i = 0; i < 6; i++) {
|
|
@@ -14849,7 +14876,7 @@ function deployMcpAssets() {
|
|
|
14849
14876
|
mcpSourceDir = candidate;
|
|
14850
14877
|
break;
|
|
14851
14878
|
}
|
|
14852
|
-
const parent =
|
|
14879
|
+
const parent = dirname6(dir);
|
|
14853
14880
|
if (parent === dir) break;
|
|
14854
14881
|
dir = parent;
|
|
14855
14882
|
}
|
|
@@ -14861,7 +14888,7 @@ function deployMcpAssets() {
|
|
|
14861
14888
|
const fileHash = (p) => {
|
|
14862
14889
|
try {
|
|
14863
14890
|
if (!existsSync11(p)) return null;
|
|
14864
|
-
return
|
|
14891
|
+
return createHash14("sha256").update(readFileSync17(p)).digest("hex");
|
|
14865
14892
|
} catch {
|
|
14866
14893
|
return null;
|
|
14867
14894
|
}
|