@byok-sdk/client 0.8.1 → 0.9.0

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.
@@ -3,7 +3,7 @@ import { randomUUID, createHash, randomBytes, timingSafeEqual, createHmac, creat
3
3
  import { readFileSync, promises, linkSync, fstatSync, lstatSync, unlinkSync, constants, readSync, openSync, writeFileSync, fchmodSync, fsyncSync, closeSync, opendirSync, existsSync, realpathSync, mkdirSync, renameSync, chmodSync, statSync, readdirSync } from 'fs';
4
4
  import * as path3 from 'path';
5
5
  import path3__default, { isAbsolute, join } from 'path';
6
- import { AGENT_CONTENT_ARTIFACT_READ_CAPABILITY, AGENT_CONTENT_TRANSCRIPT_READ_CAPABILITY, AGENT_CONTENT_WORKSPACE_READ_CAPABILITY, TASK_STATES, AgentEgressPolicySchema, AgentHomeProjectionPayloadSchema, CONFIGURED_TOOLSETS_MAX_ITEMS, ToolsetIdSchema, AgentRefSchema, AgentContentReceiptPayloadSchema, BYOK_PAIR_PATH, PairResponseSchema, BYOK_CHALLENGE_PATH, BYOK_TOKEN_PATH, partitionAgentEvents, TASK_TRANSITIONS, encodeEnvelope, PROTOCOL_VERSION, createEnvelope, AGENT_EGRESS_RELIABLE_ACK_CAPABILITY, byokBlobUrlPath, BYOK_BLOBS_PATH, byokBlobFinalizePath, STRICT_AGENT_ONLY_CAPABILITY, AGENT_HOME_PROJECTION_CAPABILITY, AGENT_EGRESS_POLICY_CAPABILITY, AGENT_EGRESS_FRESH_SESSION_CAPABILITY, AgentHomeProjectionCompletionRequestSchema, byokAgentHomeProjectionCompletionPath, AgentHomeProjectionReadbackSchema, parseMessage, checkResultDocument, MAX_MESSAGES_PER_BATCH, BYOK_CAPABILITIES_PATH, BYOK_PRESENCE_PATH, RuntimeIdSchema, TERMINAL_INFERENCE_USAGE_MAX_DURATION_MS, TERMINAL_INFERENCE_USAGE_MAX_TOKENS, RESULT_DOCUMENT_MAX_BYTES, decodeEnvelope, BYOK_EVENTS_PATH, BYOK_MESSAGES_PATH, MessagesSendResponseSchema, UnknownMessageTypeError, BYOK_WS_PATH } from '@byok-sdk/protocol';
6
+ import { AGENT_CONTENT_ARTIFACT_READ_CAPABILITY, AGENT_CONTENT_TRANSCRIPT_READ_CAPABILITY, AGENT_CONTENT_WORKSPACE_READ_CAPABILITY, TASK_STATES, AgentEgressPolicySchema, AgentHomeProjectionPayloadSchema, CONFIGURED_TOOLSETS_MAX_ITEMS, ToolsetIdSchema, AgentRefSchema, AgentContentReceiptPayloadSchema, BYOK_PAIR_PATH, PairResponseSchema, BYOK_CHALLENGE_PATH, BYOK_TOKEN_PATH, partitionAgentEvents, TASK_TRANSITIONS, encodeEnvelope, PROTOCOL_VERSION, createEnvelope, AGENT_EGRESS_RELIABLE_ACK_CAPABILITY, byokBlobUrlPath, BYOK_BLOBS_PATH, byokBlobFinalizePath, TERMINAL_PROJECTION_SELECTION_CAPABILITY, STRICT_AGENT_ONLY_CAPABILITY, AGENT_HOME_PROJECTION_CAPABILITY, AGENT_EGRESS_POLICY_CAPABILITY, AGENT_EGRESS_FRESH_SESSION_CAPABILITY, AGENT_MESSAGE_EGRESS_CAPABILITY, AgentHomeProjectionCompletionRequestSchema, byokAgentHomeProjectionCompletionPath, AgentHomeProjectionReadbackSchema, parseMessage, checkResultDocument, MAX_MESSAGES_PER_BATCH, BYOK_CAPABILITIES_PATH, BYOK_PRESENCE_PATH, AgentMessagePublishPayloadSchema, AgentMessageDispositionPayloadSchema, RuntimeIdSchema, AGENT_MEMORY_PROJECTION_CAPABILITY, TERMINAL_INFERENCE_USAGE_MAX_DURATION_MS, TERMINAL_INFERENCE_USAGE_MAX_TOKENS, RESULT_DOCUMENT_MAX_BYTES, decodeEnvelope, BYOK_EVENTS_PATH, BYOK_MESSAGES_PATH, MessagesSendResponseSchema, UnknownMessageTypeError, AGENT_MEMORY_PROJECTION_MAX_ORDERING_VALUE, AgentMemoryProjectionMutationSchema, AGENT_MEMORY_PROJECTION_MAX_REDACTED_BYTES, BYOK_WS_PATH } from '@byok-sdk/protocol';
7
7
  import net, { createServer, createConnection } from 'net';
8
8
  import * as os from 'os';
9
9
  import os__default from 'os';
@@ -649,12 +649,16 @@ var AgentHomeLeaseManager = class _AgentHomeLeaseManager {
649
649
  let handle;
650
650
  let rootGate;
651
651
  let ownsMarker = false;
652
+ let homeIdentity;
652
653
  try {
653
654
  rootGate = await acquirePathMutationGate({
654
655
  scope: "agent-home-root",
655
656
  targetPath: resolution.agentsRoot
656
657
  }, { waitMs: 1e3 });
657
658
  await ensureDirectoryNoSymlink(resolution.agentsRoot, canonicalHome);
659
+ const homeStat = await promises.stat(canonicalHome, { bigint: true });
660
+ if (!homeStat.isDirectory()) throw new AgentHomeResolutionError(`Agent home ${canonicalHome} is not a directory`);
661
+ homeIdentity = Object.freeze({ dev: homeStat.dev, ino: homeStat.ino });
658
662
  const internalDir = await ensureDirectoryNoSymlink(
659
663
  canonicalHome,
660
664
  path3__default.join(canonicalHome, AGENT_HOME_INTERNAL_DIRECTORY)
@@ -693,6 +697,7 @@ var AgentHomeLeaseManager = class _AgentHomeLeaseManager {
693
697
  }
694
698
  const acquiredLockPath = lockPath;
695
699
  if (acquiredLockPath === void 0) throw new AgentHomeError("Agent home lease marker path was not established");
700
+ if (homeIdentity === void 0) throw new AgentHomeError("Agent home lease identity was not established");
696
701
  let released = false;
697
702
  let releaseAttempt;
698
703
  return {
@@ -700,6 +705,7 @@ var AgentHomeLeaseManager = class _AgentHomeLeaseManager {
700
705
  agentRef,
701
706
  canonicalHome,
702
707
  cwd: canonicalHome,
708
+ homeIdentity,
703
709
  release: () => {
704
710
  if (released) return Promise.resolve();
705
711
  if (releaseAttempt !== void 0) return releaseAttempt;
@@ -1048,14 +1054,14 @@ function sameTaskTerminalMatch(entry, expected) {
1048
1054
  return sameRef(entry.agentRef, expected.agentRef) && entry.taskId === expected.taskId && entry.runtimeId === expected.runtimeId && entry.cwd === path3__default.resolve(expected.cwd);
1049
1055
  }
1050
1056
  function sessionFileName(runtimeId, sessionRef) {
1051
- const digest2 = createHash("sha256").update(sessionRef, "utf8").digest("hex");
1057
+ const digest3 = createHash("sha256").update(sessionRef, "utf8").digest("hex");
1052
1058
  const runtime = runtimeId.replace(/[^a-z0-9._-]/giu, "_").slice(0, 64) || "runtime";
1053
- return `${runtime}-${digest2}.jsonl`;
1059
+ return `${runtime}-${digest3}.jsonl`;
1054
1060
  }
1055
1061
  function taskTerminalFileName(runtimeId, taskId) {
1056
- const digest2 = createHash("sha256").update(taskId, "utf8").digest("hex");
1062
+ const digest3 = createHash("sha256").update(taskId, "utf8").digest("hex");
1057
1063
  const runtime = runtimeId.replace(/[^a-z0-9._-]/giu, "_").slice(0, 64) || "runtime";
1058
- return `${runtime}-task-${digest2}.jsonl`;
1064
+ return `${runtime}-task-${digest3}.jsonl`;
1059
1065
  }
1060
1066
  async function evidenceDirectory(cwdInput) {
1061
1067
  if (!path3__default.isAbsolute(cwdInput)) {
@@ -4794,6 +4800,7 @@ var CodexAdapter = class {
4794
4800
  steer: false,
4795
4801
  resume: true,
4796
4802
  approvalInteractive: false,
4803
+ mcpToolsets: true,
4797
4804
  permissionModes: ["auto", "readonly"]
4798
4805
  },
4799
4806
  environmentRequirements: { credentialNames: [] }
@@ -4925,7 +4932,7 @@ ${withStreams.stderr ?? ""}`);
4925
4932
  resumeRef: startInput.manifest.sessionRef,
4926
4933
  instruction: startInput.instruction,
4927
4934
  modelId: manifestModelId,
4928
- policyArgs: [...policyArgs],
4935
+ policyArgs: [...policyArgs, ...codexMcpConfigArgs(startInput.mcpServers)],
4929
4936
  cwd: manifestCwd,
4930
4937
  env: runtimeEnv,
4931
4938
  spawnFn: this.options.spawnFn,
@@ -4955,6 +4962,18 @@ ${withStreams.stderr ?? ""}`);
4955
4962
  return (this.options.resolveBin ?? resolveCodexBin)();
4956
4963
  }
4957
4964
  };
4965
+ function codexMcpConfigArgs(servers) {
4966
+ if (servers === void 0 || Object.keys(servers).length === 0) return [];
4967
+ const args = ["--ignore-user-config"];
4968
+ for (const [name, server] of Object.entries(servers).sort(([left], [right]) => left.localeCompare(right))) {
4969
+ args.push("-c", `mcp_servers.${name}.command=${JSON.stringify(server.command)}`);
4970
+ if (server.args !== void 0) args.push("-c", `mcp_servers.${name}.args=${JSON.stringify([...server.args])}`);
4971
+ for (const [key, value] of Object.entries(server.env ?? {}).sort(([left], [right]) => left.localeCompare(right))) {
4972
+ args.push("-c", `mcp_servers.${name}.env.${key}=${JSON.stringify(value)}`);
4973
+ }
4974
+ }
4975
+ return args;
4976
+ }
4958
4977
  async function resolveRealWorkspaceDir(workspaceDir) {
4959
4978
  return promises.realpath(workspaceDir).catch(() => workspaceDir);
4960
4979
  }
@@ -6666,6 +6685,27 @@ function parseApprovalsRequestParams(value) {
6666
6685
  if (typeof value.summary !== "string") return void 0;
6667
6686
  return { taskId: value.taskId, summary: value.summary };
6668
6687
  }
6688
+ function parseAgentMessagePublishParams(value) {
6689
+ if (!isRecord3(value) || Object.keys(value).some((key) => key !== "contextToken" && key !== "contentType" && key !== "body")) return void 0;
6690
+ if (typeof value.contextToken !== "string" || value.contextToken.length < 32 || value.contextToken.length > 160) return void 0;
6691
+ if (value.contentType !== "text/plain" && value.contentType !== "text/markdown") return void 0;
6692
+ if (typeof value.body !== "string" || value.body.length === 0) return void 0;
6693
+ return { contextToken: value.contextToken, contentType: value.contentType, body: value.body };
6694
+ }
6695
+ function validAgentMemoryContextToken(value) {
6696
+ return typeof value === "string" && value.length >= 32 && value.length <= 160 && !/[\u0000\r\n]/u.test(value);
6697
+ }
6698
+ function parseAgentMemoryRecallParams(value) {
6699
+ if (!isRecord3(value) || Object.keys(value).some((key) => key !== "contextToken" && key !== "path" && key !== "ifRevision")) return void 0;
6700
+ if (!validAgentMemoryContextToken(value.contextToken) || typeof value.path !== "string" || value.ifRevision !== void 0 && typeof value.ifRevision !== "string") return void 0;
6701
+ return { contextToken: value.contextToken, path: value.path, ...value.ifRevision === void 0 ? {} : { ifRevision: value.ifRevision } };
6702
+ }
6703
+ function parseAgentMemorySaveParams(value) {
6704
+ if (!isRecord3(value) || Object.keys(value).some((key) => key !== "contextToken" && key !== "op" && key !== "path" && key !== "expectedRevision" && key !== "content")) return void 0;
6705
+ if (!validAgentMemoryContextToken(value.contextToken) || value.op !== "replace" && value.op !== "delete" || typeof value.path !== "string" || typeof value.expectedRevision !== "string" || value.op === "replace" && typeof value.content !== "string" || value.op === "delete" && value.content !== void 0) return void 0;
6706
+ const content = value.content;
6707
+ return { contextToken: value.contextToken, op: value.op, path: value.path, expectedRevision: value.expectedRevision, ...typeof content === "string" ? { content } : {} };
6708
+ }
6669
6709
  var ASSERTION_AUDIENCE_MAX_BYTES = 256;
6670
6710
  function parseAssertionIssueParams(value) {
6671
6711
  if (!isRecord3(value)) return void 0;
@@ -6923,6 +6963,8 @@ async function startControlServer(opts) {
6923
6963
  }
6924
6964
  return { endpoint, stopServing, close };
6925
6965
  }
6966
+ var AGENT_MESSAGE_MCP_SERVER_NAME = "byokagentmessage";
6967
+ var AGENT_MEMORY_MCP_SERVER_NAME = "byokagentmemory";
6926
6968
  var MAX_LOCAL_MCP_SERVERS_PER_TOOLSET = 16;
6927
6969
  var MAX_LOCAL_MCP_ARGS = 64;
6928
6970
  var MAX_LOCAL_MCP_TOKEN_CHARS = 4096;
@@ -7012,7 +7054,7 @@ function buildState(configured) {
7012
7054
  `DaemonConfig.mcpToolsets.${toolsetId}.mcpServers contains invalid server name ${JSON.stringify(serverName)}`
7013
7055
  );
7014
7056
  }
7015
- if (serverName === APPROVAL_MCP_SERVER_NAME) {
7057
+ if (serverName === APPROVAL_MCP_SERVER_NAME || serverName === AGENT_MESSAGE_MCP_SERVER_NAME || serverName === AGENT_MEMORY_MCP_SERVER_NAME) {
7016
7058
  throw new Error(
7017
7059
  `DaemonConfig.mcpToolsets.${toolsetId}.mcpServers.${serverName} uses a server name reserved by the daemon`
7018
7060
  );
@@ -7175,9 +7217,9 @@ function deterministicJitterMs(input) {
7175
7217
  throw new Error("deterministic jitter ratio must be between 0 and 1");
7176
7218
  }
7177
7219
  if (input.baseMs === 0 || ratio === 0) return Math.round(input.baseMs);
7178
- const digest2 = createHash("sha256").update("byok-jitter-v1\0", "utf8").update(input.domain, "utf8").update("\0", "utf8").update(input.seed, "utf8").update("\0", "utf8").update(String(input.sequence), "utf8").digest();
7179
- const high = digest2.readUInt32BE(0) & 2097151;
7180
- const low = digest2.readUInt32BE(4);
7220
+ const digest3 = createHash("sha256").update("byok-jitter-v1\0", "utf8").update(input.domain, "utf8").update("\0", "utf8").update(input.seed, "utf8").update("\0", "utf8").update(String(input.sequence), "utf8").digest();
7221
+ const high = digest3.readUInt32BE(0) & 2097151;
7222
+ const low = digest3.readUInt32BE(4);
7181
7223
  const unit = (high * 4294967296 + low) / 9007199254740992;
7182
7224
  const multiplier = 1 - ratio + unit * ratio * 2;
7183
7225
  return Math.max(0, Math.round(input.baseMs * multiplier));
@@ -9187,9 +9229,9 @@ function isSqliteAvailable() {
9187
9229
  }
9188
9230
  }
9189
9231
  var SECURE_FILE_MODE = 384;
9190
- function openJournalDatabase(path36, busyTimeoutMs, faults) {
9232
+ function openJournalDatabase(path41, busyTimeoutMs, faults) {
9191
9233
  const { DatabaseSync } = loadSqliteModule();
9192
- const db = new DatabaseSync(path36, { timeout: busyTimeoutMs });
9234
+ const db = new DatabaseSync(path41, { timeout: busyTimeoutMs });
9193
9235
  try {
9194
9236
  faults?.onStep?.("after-open");
9195
9237
  db.exec("PRAGMA auto_vacuum = INCREMENTAL;");
@@ -9332,9 +9374,9 @@ var RECEIVED_STATE = "received";
9332
9374
  function byteLength(value) {
9333
9375
  return Buffer.byteLength(value, "utf8");
9334
9376
  }
9335
- function fileBytes(path36) {
9377
+ function fileBytes(path41) {
9336
9378
  try {
9337
- return statSync(path36).size;
9379
+ return statSync(path41).size;
9338
9380
  } catch {
9339
9381
  return 0;
9340
9382
  }
@@ -10561,130 +10603,1262 @@ var ProgressBatcher = class {
10561
10603
  }
10562
10604
  }
10563
10605
  };
10564
-
10565
- // src/daemon/task-runner.ts
10566
- var DEFAULT_APPROVAL_TIMEOUT_MS = 10 * 6e4;
10567
- var DEFAULT_SHUTDOWN_INTERRUPT_TIMEOUT_MS = 5e3;
10568
- var AGENT_TERMINAL_EVIDENCE_MAX_ATTEMPTS = 3;
10569
- var AGENT_TERMINAL_EVIDENCE_RETRY_DELAY_MS = 20;
10570
- var GIT_OBSERVATION_TIMEOUT_MS = 5e3;
10571
- var MAX_PENDING_APPROVALS_PER_TASK = 16;
10572
- var NoPendingApprovalError = class extends Error {
10573
- constructor(taskId) {
10574
- super(`no pending out-of-band approval to resolve for task ${taskId}`);
10575
- this.taskId = taskId;
10576
- this.name = "NoPendingApprovalError";
10606
+ var AGENT_MESSAGE_DIRECTORY = path3__default.join(".byok", "messages");
10607
+ var AGENT_MESSAGE_OUTBOX_FILENAME = "outbox-v1.jsonl";
10608
+ var AgentMessageOutboxError = class extends Error {
10609
+ constructor(message) {
10610
+ super(message);
10611
+ this.name = "AgentMessageOutboxError";
10577
10612
  }
10578
- taskId;
10579
10613
  };
10580
- var MAX_INLINE_ARTIFACT_BYTES = 64 * 1024;
10581
- var MAX_TRACKED_TASK_IDS = 2e3;
10582
- var MAX_DURATION_EXCEEDED_REASON_PREFIX = "resource limit exceeded: maxDurationMs";
10583
- var MAX_OUTPUT_BYTES_EXCEEDED_REASON_PREFIX = "resource limit exceeded: maxTaskOutputBytes";
10584
- var MAX_PROGRESS_BATCH_BYTES_EXCEEDED_REASON_PREFIX = "resource limit exceeded: progressBatch.maxBatchBytes";
10585
- var RESULT_DOCUMENT_UNDELIVERABLE_REASON_PREFIX = "result document undeliverable";
10586
- function resultDocumentRejectionDetail(check) {
10587
- switch (check.reason) {
10588
- case "over-cap":
10589
- return `${check.bytes} bytes as canonical JSON, over the ${RESULT_DOCUMENT_MAX_BYTES}-byte limit (it is never truncated \u2014 a truncated JSON document is not valid JSON; use artifactRefs for a result this size)`;
10590
- case "not-serializable":
10591
- return "not JSON-serializable (JSON.stringify threw, or produced no output at all)";
10592
- case "not-plain-json":
10593
- return "not plain JSON data: it does not equal its own JSON round trip, so serializing it would silently change it (an undefined-valued key, NaN, a function or symbol value, a Date, a toJSON that rewrites the value, or a getter that answers differently on a second read)";
10594
- default: {
10595
- const exhaustive = check;
10596
- throw new Error(`unhandled result document rejection: ${JSON.stringify(exhaustive)}`);
10614
+ function stableJson(value) {
10615
+ const encoded = JSON.stringify(value);
10616
+ if (encoded === void 0) throw new AgentMessageOutboxError("message outbox value is not serializable");
10617
+ return encoded;
10618
+ }
10619
+ function hashBody(body) {
10620
+ return `sha256:${createHash("sha256").update(body, "utf8").digest("hex")}`;
10621
+ }
10622
+ function sameAgentRef(left, right) {
10623
+ return left.agentId === right.agentId && left.profileRevision === right.profileRevision;
10624
+ }
10625
+ function exactDisposition(record, value) {
10626
+ return value.messageId === record.messageId && value.cursor === record.cursor && value.contract === record.contract && value.contentHash === record.contentHash && value.sessionRef === record.sessionRef && sameAgentRef(value.agentRef, record.agentRef);
10627
+ }
10628
+ var AgentMessageOutbox = class _AgentMessageOutbox {
10629
+ constructor(homeDir, outboxPath) {
10630
+ this.homeDir = homeDir;
10631
+ this.outboxPath = outboxPath;
10632
+ }
10633
+ homeDir;
10634
+ outboxPath;
10635
+ pendingByTask = /* @__PURE__ */ new Map();
10636
+ dispositionByTask = /* @__PURE__ */ new Map();
10637
+ nextCursor = 1;
10638
+ logEntries = 0;
10639
+ writeTail = Promise.resolve();
10640
+ static async open(homeDir) {
10641
+ const directory = path3__default.join(homeDir, AGENT_MESSAGE_DIRECTORY);
10642
+ await ensureSecureDir(directory);
10643
+ const outbox = new _AgentMessageOutbox(homeDir, path3__default.join(directory, AGENT_MESSAGE_OUTBOX_FILENAME));
10644
+ await outbox.load();
10645
+ return outbox;
10646
+ }
10647
+ /** Re-open every existing Agent-local message outbox without following Agent-home symlinks. */
10648
+ static async recover(agentsRoot, tenantId) {
10649
+ if (!path3__default.isAbsolute(agentsRoot)) throw new AgentMessageOutboxError("message outbox recovery root must be absolute");
10650
+ let canonicalRoot;
10651
+ try {
10652
+ canonicalRoot = await promises.realpath(agentsRoot);
10653
+ } catch (error) {
10654
+ if (error.code === "ENOENT") return [];
10655
+ throw error;
10656
+ }
10657
+ const recovered = [];
10658
+ for (const entry of await promises.readdir(canonicalRoot, { withFileTypes: true })) {
10659
+ if (!entry.isDirectory() || entry.isSymbolicLink()) continue;
10660
+ const home = path3__default.join(canonicalRoot, entry.name);
10661
+ const canonicalHome = await promises.realpath(home);
10662
+ if (path3__default.relative(canonicalRoot, canonicalHome) !== entry.name) {
10663
+ throw new AgentMessageOutboxError(`message outbox recovery home escaped the canonical agents root: ${entry.name}`);
10664
+ }
10665
+ try {
10666
+ await promises.lstat(path3__default.join(canonicalHome, AGENT_MESSAGE_DIRECTORY, AGENT_MESSAGE_OUTBOX_FILENAME));
10667
+ } catch (error) {
10668
+ if (error.code === "ENOENT") continue;
10669
+ throw error;
10670
+ }
10671
+ const outbox = await _AgentMessageOutbox.open(canonicalHome);
10672
+ if (outbox.records().some((record) => record.agentRef.agentId !== entry.name)) {
10673
+ throw new AgentMessageOutboxError(`message outbox in ${entry.name} claims a different Agent`);
10674
+ }
10675
+ if (outbox.records().some((record) => record.tenantId !== tenantId)) {
10676
+ throw new AgentMessageOutboxError(`message outbox in ${entry.name} claims a different authenticated tenant`);
10677
+ }
10678
+ recovered.push(outbox);
10597
10679
  }
10680
+ return Object.freeze(recovered);
10598
10681
  }
10682
+ records() {
10683
+ return Object.freeze([...this.pendingByTask.values()].sort((a, b) => a.cursor - b.cursor));
10684
+ }
10685
+ /** Activated records with no exact disposition yet; only these may be transport-replayed. */
10686
+ retryableRecords() {
10687
+ return Object.freeze(this.records().filter((record) => !this.dispositionByTask.has(record.taskId)));
10688
+ }
10689
+ get(taskId) {
10690
+ return this.pendingByTask.get(taskId);
10691
+ }
10692
+ async appendDraft(input) {
10693
+ return this.exclusive(async () => {
10694
+ if (input.contentType !== input.requirement.contentType) throw new AgentMessageOutboxError("message contentType does not match the offer contract");
10695
+ const byteCount = Buffer.byteLength(input.body, "utf8");
10696
+ if (byteCount < 1 || byteCount > input.requirement.maxBytes) throw new AgentMessageOutboxError("message body exceeds the offer byte contract");
10697
+ const existing = this.pendingByTask.get(input.taskId);
10698
+ if (existing !== void 0) {
10699
+ if (existing.tenantId !== input.tenantId || existing.contentType !== input.contentType || existing.body !== input.body || existing.contract !== input.requirement.contract || !sameAgentRef(existing.agentRef, input.agentRef)) {
10700
+ throw new AgentMessageOutboxError("required-message task already has a different immutable draft");
10701
+ }
10702
+ return existing;
10703
+ }
10704
+ if (this.pendingByTask.size >= input.maxPendingEvents) throw new AgentMessageOutboxError("message outbox event quota is exhausted");
10705
+ const pendingBytes = this.records().reduce((sum, record2) => sum + record2.byteCount, 0);
10706
+ if (pendingBytes + byteCount > input.maxPendingBytes) throw new AgentMessageOutboxError("message outbox byte quota is exhausted");
10707
+ const record = Object.freeze({
10708
+ schema: 1,
10709
+ taskId: input.taskId,
10710
+ tenantId: input.tenantId,
10711
+ agentRef: Object.freeze({ ...input.agentRef }),
10712
+ contract: input.requirement.contract,
10713
+ messageId: randomUUID(),
10714
+ cursor: this.nextCursor++,
10715
+ contentType: input.contentType,
10716
+ body: input.body,
10717
+ contentHash: hashBody(input.body),
10718
+ byteCount,
10719
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
10720
+ ...input.sessionRef === void 0 ? {} : { sessionRef: input.sessionRef }
10721
+ });
10722
+ await this.appendEntry({ schema: 1, kind: "append", record });
10723
+ this.pendingByTask.set(record.taskId, record);
10724
+ return record;
10725
+ });
10726
+ }
10727
+ async activate(taskId, sessionRef) {
10728
+ return this.exclusive(async () => {
10729
+ const record = this.pendingByTask.get(taskId);
10730
+ if (record === void 0) return void 0;
10731
+ if (record.sessionRef !== void 0) {
10732
+ if (record.sessionRef !== sessionRef) throw new AgentMessageOutboxError("message draft is already bound to a different session");
10733
+ return record;
10734
+ }
10735
+ await this.appendEntry({ schema: 1, kind: "activate", taskId, messageId: record.messageId, sessionRef });
10736
+ const activated = Object.freeze({ ...record, sessionRef });
10737
+ this.pendingByTask.set(taskId, activated);
10738
+ return activated;
10739
+ });
10740
+ }
10741
+ publishPayload(record) {
10742
+ if (record.sessionRef === void 0) throw new AgentMessageOutboxError("message draft is not bound to a durable session");
10743
+ return AgentMessagePublishPayloadSchema.parse({
10744
+ agentRef: record.agentRef,
10745
+ sessionRef: record.sessionRef,
10746
+ contract: record.contract,
10747
+ messageId: record.messageId,
10748
+ cursor: record.cursor,
10749
+ contentType: record.contentType,
10750
+ body: record.body,
10751
+ contentHash: record.contentHash,
10752
+ byteCount: record.byteCount
10753
+ });
10754
+ }
10755
+ async applyDisposition(taskId, input) {
10756
+ return this.exclusive(async () => {
10757
+ const disposition = AgentMessageDispositionPayloadSchema.parse(input);
10758
+ const record = this.pendingByTask.get(taskId);
10759
+ if (record === void 0) return "unknown";
10760
+ if (!exactDisposition(record, disposition)) return "mismatch";
10761
+ await this.appendEntry({ schema: 1, kind: "disposition", taskId, disposition });
10762
+ if (disposition.outcome === "accepted") {
10763
+ this.pendingByTask.delete(taskId);
10764
+ this.dispositionByTask.delete(taskId);
10765
+ } else {
10766
+ this.dispositionByTask.set(taskId, disposition);
10767
+ }
10768
+ if (this.logEntries >= 512) await this.compact();
10769
+ return disposition.outcome;
10770
+ });
10771
+ }
10772
+ async load() {
10773
+ let raw;
10774
+ try {
10775
+ const stat = await promises.stat(this.outboxPath);
10776
+ if (stat.size > 64 * 1024 * 1024) throw new AgentMessageOutboxError("message outbox exceeds its bounded on-disk size");
10777
+ raw = await promises.readFile(this.outboxPath, "utf8");
10778
+ } catch (error) {
10779
+ if (error.code === "ENOENT") return;
10780
+ throw error;
10781
+ }
10782
+ for (const line of raw.split("\n")) {
10783
+ if (line.length === 0) continue;
10784
+ const entry = JSON.parse(line);
10785
+ this.logEntries += 1;
10786
+ if (entry.schema !== 1) throw new AgentMessageOutboxError("message outbox schema is unsupported");
10787
+ if (entry.kind === "append") {
10788
+ if (this.pendingByTask.has(entry.record.taskId)) throw new AgentMessageOutboxError("message outbox contains duplicate task draft");
10789
+ this.pendingByTask.set(entry.record.taskId, Object.freeze(entry.record));
10790
+ this.nextCursor = Math.max(this.nextCursor, entry.record.cursor + 1);
10791
+ } else if (entry.kind === "activate") {
10792
+ const record = this.pendingByTask.get(entry.taskId);
10793
+ if (record === void 0 || record.messageId !== entry.messageId) throw new AgentMessageOutboxError("message activation has no exact draft");
10794
+ if (record.sessionRef !== void 0 && record.sessionRef !== entry.sessionRef) throw new AgentMessageOutboxError("message activation session conflicts");
10795
+ this.pendingByTask.set(entry.taskId, Object.freeze({ ...record, sessionRef: entry.sessionRef }));
10796
+ } else if (entry.kind === "disposition") {
10797
+ const record = this.pendingByTask.get(entry.taskId);
10798
+ if (record === void 0 || !exactDisposition(record, entry.disposition)) throw new AgentMessageOutboxError("message outbox contains a mismatched disposition");
10799
+ if (entry.disposition.outcome === "accepted") {
10800
+ this.pendingByTask.delete(entry.taskId);
10801
+ this.dispositionByTask.delete(entry.taskId);
10802
+ } else {
10803
+ this.dispositionByTask.set(entry.taskId, entry.disposition);
10804
+ }
10805
+ } else {
10806
+ throw new AgentMessageOutboxError("message outbox entry kind is invalid");
10807
+ }
10808
+ }
10809
+ }
10810
+ async appendEntry(entry) {
10811
+ const handle = await promises.open(this.outboxPath, constants.O_APPEND | constants.O_CREAT | constants.O_WRONLY, 384);
10812
+ try {
10813
+ await handle.write(`${stableJson(entry)}
10814
+ `, void 0, "utf8");
10815
+ await handle.sync();
10816
+ this.logEntries += 1;
10817
+ } finally {
10818
+ await handle.close();
10819
+ }
10820
+ }
10821
+ async compact() {
10822
+ const entries = this.records().flatMap((record) => {
10823
+ const append = { schema: 1, kind: "append", record };
10824
+ const disposition = this.dispositionByTask.get(record.taskId);
10825
+ return disposition === void 0 ? [append] : [append, { schema: 1, kind: "disposition", taskId: record.taskId, disposition }];
10826
+ });
10827
+ await atomicWriteFile(this.outboxPath, entries.map(stableJson).join(entries.length === 0 ? "" : "\n"), { mode: 384 });
10828
+ this.logEntries = entries.length;
10829
+ }
10830
+ async exclusive(fn) {
10831
+ const prior = this.writeTail;
10832
+ let release;
10833
+ this.writeTail = new Promise((resolve) => {
10834
+ release = resolve;
10835
+ });
10836
+ await prior;
10837
+ try {
10838
+ return await fn();
10839
+ } finally {
10840
+ release();
10841
+ }
10842
+ }
10843
+ };
10844
+
10845
+ // src/daemon/memory-guidance.ts
10846
+ var AGENT_MEMORY_GUIDANCE = [
10847
+ "At the start of this Agent task, first read `MEMORY.md` in the provided `cwd`.",
10848
+ "Treat `MEMORY.md` as a concise, self-contained recovery index; if it is empty, initialize a brief index from durable, non-secret task knowledge.",
10849
+ "Read files under `notes/` only as needed, following pointers from the index.",
10850
+ "When task permissions allow and a durable value is learned, update the relevant `notes/` entry and the `MEMORY.md` index.",
10851
+ "Never write credentials, secrets, tokens, API keys, private keys, or other authentication material to `MEMORY.md` or `notes/`."
10852
+ ].join("\n");
10853
+ function prependAgentMemoryGuidance(instruction) {
10854
+ return `${AGENT_MEMORY_GUIDANCE}
10855
+
10856
+ ${instruction}`;
10599
10857
  }
10600
- var DEFAULT_MAX_TASK_OUTPUT_BYTES = 64 * 1024 * 1024;
10601
- function isKnownRuntimeId(id) {
10602
- return RuntimeIdSchema.safeParse(id).success;
10603
- }
10604
- function terminalUsageNumber(value, maximum) {
10605
- return value !== void 0 && Number.isSafeInteger(value) && value >= 0 && value <= maximum ? value : void 0;
10858
+ var AGENT_MEMORY_AUDIT_FILENAME = "agent-memory-audit-v1.jsonl";
10859
+ var AGENT_MEMORY_OUTBOX_FILENAME = "agent-memory-redacted-outbox-v2.json";
10860
+ var AGENT_MEMORY_MAX_FILE_BYTES = 256 * 1024;
10861
+ var AGENT_MEMORY_MAX_SNAPSHOT_BYTES = 1024 * 1024;
10862
+ var AGENT_MEMORY_MAX_SNAPSHOT_FILES = 128;
10863
+ var AGENT_MEMORY_MAX_SNAPSHOT_ENTRIES = 512;
10864
+ var AGENT_MEMORY_MAX_LOCAL_LOG_BYTES = AGENT_MEMORY_MAX_SNAPSHOT_BYTES;
10865
+ var REVISION = /^sha256:[a-f0-9]{64}$/u;
10866
+ var SAFE_SEGMENT = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u;
10867
+ var SECRET_LIKE = /(?:^|[-_.])(secret|token|credential|password|passwd|api[-_]?key|private[-_]?key|cookie)(?:$|[-_.])/iu;
10868
+ var encoder2 = new TextEncoder();
10869
+ var AGENT_MEMORY_PROJECTION_PUBLISH_TIMEOUT_MS = 1e4;
10870
+ var AgentMemoryError = class extends Error {
10871
+ constructor(message) {
10872
+ super(message);
10873
+ this.name = "AgentMemoryError";
10874
+ }
10875
+ };
10876
+ var AgentMemoryRevisionConflictError = class extends AgentMemoryError {
10877
+ constructor(expectedRevision, actualRevision) {
10878
+ super(`Agent memory revision conflict: expected ${expectedRevision}, current ${actualRevision}`);
10879
+ this.expectedRevision = expectedRevision;
10880
+ this.actualRevision = actualRevision;
10881
+ this.name = "AgentMemoryRevisionConflictError";
10882
+ }
10883
+ expectedRevision;
10884
+ actualRevision;
10885
+ };
10886
+ var DRAINED_AGENT_MEMORY_PROJECTION_REPLAY = Object.freeze({ status: "drained" });
10887
+ var AgentMemoryProjectionReplayPendingError = class extends AgentMemoryError {
10888
+ constructor(outcome) {
10889
+ super("Agent memory projection replay remains pending");
10890
+ this.outcome = outcome;
10891
+ this.name = "AgentMemoryProjectionReplayPendingError";
10892
+ }
10893
+ outcome;
10894
+ };
10895
+ function digestBytes(content) {
10896
+ return `sha256:${createHash("sha256").update(content).digest("hex")}`;
10606
10897
  }
10607
- var DEFAULT_RUNTIME_PREFERENCE = ["claude", "codex", "pi"];
10608
- function orderByPreference(candidates, preference) {
10609
- const rank = new Map(preference.map((id, index) => [id, index]));
10610
- return [...candidates].sort((a, b) => (rank.get(a.descriptor.id) ?? preference.length) - (rank.get(b.descriptor.id) ?? preference.length));
10898
+ function digest2(content) {
10899
+ return digestBytes(encoder2.encode(content));
10611
10900
  }
10612
- function adapterSupportsMode(descriptor, mode) {
10613
- return descriptor.capabilities.permissionModes.includes(mode);
10901
+ function nonEmpty(value) {
10902
+ return typeof value === "string" && value.length > 0 && !/[\u0000\r\n]/u.test(value);
10614
10903
  }
10615
- function adapterSupportsMcpToolsets(descriptor) {
10616
- return descriptor.capabilities.mcpToolsets === true;
10904
+ function revision(value) {
10905
+ return typeof value === "string" && REVISION.test(value);
10617
10906
  }
10618
- function withoutRequiredToolsets(payload) {
10619
- const { requiredToolsets, egressPolicy, ...offer } = payload;
10620
- return offer;
10907
+ function taskContext(value) {
10908
+ if (!value || !nonEmpty(value.taskId) || !nonEmpty(value.tenantId) || !nonEmpty(value.deviceId) || !nonEmpty(value.sessionRef) || !nonEmpty(value.runtimeId) || !nonEmpty(value.leaseId) || !value.agentRef || !nonEmpty(value.agentRef.agentId) || !nonEmpty(value.agentRef.profileRevision) || !path3__default.isAbsolute(value.canonicalHome) || typeof value.homeIdentity?.dev !== "bigint" || typeof value.homeIdentity.ino !== "bigint") {
10909
+ throw new AgentMemoryError("Agent memory requires an exact active Agent task context");
10910
+ }
10911
+ return Object.freeze({ ...value, agentRef: Object.freeze({ ...value.agentRef }), homeIdentity: Object.freeze({ ...value.homeIdentity }), canonicalHome: path3__default.resolve(value.canonicalHome) });
10621
10912
  }
10622
- function sameEgressPolicy(left, right) {
10623
- return JSON.stringify(left) === JSON.stringify(right);
10913
+ function validateAgentMemoryPath(value) {
10914
+ if (typeof value !== "string" || value.length === 0 || value.length > 1024 || /[\u0000\\]/u.test(value)) throw new AgentMemoryError("memory path is invalid");
10915
+ if (value === "MEMORY.md") return value;
10916
+ if (path3__default.posix.isAbsolute(value) || /[*?\[{]/u.test(value)) throw new AgentMemoryError("memory path must name exactly one file");
10917
+ const parts = value.split("/");
10918
+ if (parts.length < 2 || parts[0] !== "notes" || !value.endsWith(".md") || parts.some((part) => part === "" || part === "." || part === ".." || part === ".byok" || !SAFE_SEGMENT.test(part) || SECRET_LIKE.test(part))) {
10919
+ throw new AgentMemoryError("memory path must be MEMORY.md or notes/<safe-relative>.md");
10920
+ }
10921
+ return value;
10624
10922
  }
10625
- function offeredAgentRef(payload) {
10626
- if (!Object.prototype.hasOwnProperty.call(payload, "agentRef")) return void 0;
10627
- return validateAgentRef(payload.agentRef);
10923
+ var SECURE_DIRECTORY_DESCRIPTOR_ROOT = "/proc/self/fd";
10924
+ function isAgentMemorySecureFilesystemAvailable(externalHelperConfigured = false) {
10925
+ const nativeLinux = process.platform === "linux" && typeof constants.O_NOFOLLOW === "number" && typeof constants.O_DIRECTORY === "number" && typeof constants.O_NONBLOCK === "number" && existsSync(SECURE_DIRECTORY_DESCRIPTOR_ROOT);
10926
+ return nativeLinux || externalHelperConfigured && process.platform === "darwin";
10628
10927
  }
10629
- function offeredSessionRef(payload) {
10630
- if (!Object.prototype.hasOwnProperty.call(payload, "sessionRef")) return void 0;
10631
- const value = payload.sessionRef;
10632
- return typeof value === "string" ? value : void 0;
10928
+ function requireSecureDirectoryDescriptors() {
10929
+ if (!isAgentMemorySecureFilesystemAvailable(false) || process.platform !== "linux") {
10930
+ throw new AgentMemoryError("Agent memory is unavailable because this Node platform lacks safe descriptor-relative filesystem operations");
10931
+ }
10932
+ return SECURE_DIRECTORY_DESCRIPTOR_ROOT;
10633
10933
  }
10634
- function errorMessage4(err) {
10635
- return err instanceof Error ? err.message : String(err);
10934
+ function noFollowFlags(base) {
10935
+ requireSecureDirectoryDescriptors();
10936
+ return base | constants.O_NOFOLLOW;
10636
10937
  }
10637
- function raceSettleFirst(fn, timeoutMs) {
10638
- return new Promise((resolve) => {
10639
- let settled = false;
10640
- const timer = setTimeout(() => {
10641
- if (!settled) {
10642
- settled = true;
10643
- resolve(false);
10644
- }
10645
- }, timeoutMs);
10646
- timer.unref?.();
10647
- void (async () => {
10648
- try {
10649
- await fn();
10650
- } catch {
10651
- }
10652
- if (!settled) {
10653
- settled = true;
10654
- clearTimeout(timer);
10655
- resolve(true);
10656
- }
10657
- })();
10658
- });
10938
+ function descriptorPath(handle) {
10939
+ return `${requireSecureDirectoryDescriptors()}/${handle.fd}`;
10659
10940
  }
10660
- function estimateEventBytes(event) {
10941
+ async function openPinnedDirectory(target, expectedIdentity) {
10942
+ requireSecureDirectoryDescriptors();
10943
+ let handle;
10661
10944
  try {
10662
- return Buffer.byteLength(JSON.stringify(event), "utf8");
10663
- } catch {
10664
- return 0;
10945
+ handle = await promises.open(target, noFollowFlags(constants.O_RDONLY | constants.O_DIRECTORY));
10946
+ const stat = await handle.stat({ bigint: true });
10947
+ if (!stat.isDirectory() || stat.isSymbolicLink() || expectedIdentity !== void 0 && (stat.dev !== expectedIdentity.dev || stat.ino !== expectedIdentity.ino)) throw new AgentMemoryError("memory directory is not a real directory");
10948
+ return handle;
10949
+ } catch (error) {
10950
+ await handle?.close().catch(() => {
10951
+ });
10952
+ if (error instanceof AgentMemoryError) throw error;
10953
+ throw new AgentMemoryError("memory directory is unavailable or unsafe");
10665
10954
  }
10666
10955
  }
10667
- async function openArtifact(workspaceDir, name) {
10668
- const realWorkspaceDir = await promises.realpath(workspaceDir).catch(() => workspaceDir);
10669
- const candidate = path3__default.resolve(realWorkspaceDir, name);
10670
- const prefix = realWorkspaceDir.endsWith(path3__default.sep) ? realWorkspaceDir : realWorkspaceDir + path3__default.sep;
10671
- if (candidate !== realWorkspaceDir && !candidate.startsWith(prefix)) {
10672
- return { ok: false, reason: `artifact name "${name}" resolves outside the task workspace \u2014 rejected` };
10673
- }
10674
- let realCandidate = candidate;
10956
+ async function withPinnedDirectory(home, parts, operation, expectedHomeIdentity) {
10957
+ const handles = [];
10675
10958
  try {
10676
- realCandidate = await promises.realpath(candidate);
10677
- } catch {
10678
- }
10679
- if (realCandidate !== realWorkspaceDir && !realCandidate.startsWith(prefix)) {
10680
- return { ok: false, reason: `artifact name "${name}" resolves outside the task workspace \u2014 rejected` };
10959
+ let directory = await openPinnedDirectory(home, expectedHomeIdentity);
10960
+ handles.push(directory);
10961
+ for (const part of parts) {
10962
+ directory = await openPinnedDirectory(`${descriptorPath(directory)}/${part}`);
10963
+ handles.push(directory);
10964
+ }
10965
+ return await operation(directory);
10966
+ } finally {
10967
+ await Promise.all(handles.reverse().map((handle) => handle.close().catch(() => {
10968
+ })));
10681
10969
  }
10682
- const O_NOFOLLOW = constants.O_NOFOLLOW ?? 0;
10970
+ }
10971
+ async function withMemoryParent(context, relativePath, operation) {
10972
+ const parts = relativePath.split("/");
10973
+ const fileName = parts.pop();
10974
+ if (fileName === void 0 || parts.some((part) => !SAFE_SEGMENT.test(part) && part !== ".byok")) throw new AgentMemoryError("memory path is invalid");
10975
+ return withPinnedDirectory(context.canonicalHome, parts, (directory) => operation(directory, fileName), context.homeIdentity);
10976
+ }
10977
+ async function readPinnedFile(directory, fileName, maxBytes = AGENT_MEMORY_MAX_FILE_BYTES) {
10683
10978
  let handle;
10684
10979
  try {
10685
- handle = await promises.open(candidate, constants.O_RDONLY | O_NOFOLLOW);
10686
- } catch (err) {
10687
- return { ok: false, reason: `artifact "${name}" could not be opened: ${errorMessage4(err)}` };
10980
+ handle = await promises.open(
10981
+ `${descriptorPath(directory)}/${fileName}`,
10982
+ noFollowFlags(constants.O_RDONLY | constants.O_NONBLOCK)
10983
+ );
10984
+ } catch (error) {
10985
+ if (error.code === "ENOENT") return Object.freeze({ exists: false, content: "", revision: digest2(""), byteCount: 0 });
10986
+ throw new AgentMemoryError("could not open memory file");
10987
+ }
10988
+ try {
10989
+ const before = await handle.stat({ bigint: true });
10990
+ if (!before.isFile() || before.isSymbolicLink() || before.size > BigInt(maxBytes)) throw new AgentMemoryError("memory file is not a bounded regular file");
10991
+ const bytes = Buffer.alloc(Number(before.size));
10992
+ let offset = 0;
10993
+ while (offset < bytes.length) {
10994
+ const read = await handle.read(bytes, offset, bytes.length - offset, offset);
10995
+ if (read.bytesRead === 0) break;
10996
+ offset += read.bytesRead;
10997
+ }
10998
+ const after = await handle.stat({ bigint: true });
10999
+ if (offset !== bytes.length || after.size !== before.size || after.mtimeNs !== before.mtimeNs || after.ino !== before.ino) throw new AgentMemoryError("memory file changed during read");
11000
+ const content = bytes.toString("utf8");
11001
+ if (!Buffer.from(content, "utf8").equals(bytes)) throw new AgentMemoryError("memory file is not valid UTF-8");
11002
+ return Object.freeze({ exists: true, content, revision: digestBytes(bytes), byteCount: bytes.length });
11003
+ } finally {
11004
+ await handle.close().catch(() => {
11005
+ });
11006
+ }
11007
+ }
11008
+ async function readFile(context, relativePath, maxBytes = AGENT_MEMORY_MAX_FILE_BYTES) {
11009
+ if (context.filesystem !== void 0) return context.filesystem.read(relativePath, maxBytes);
11010
+ return withMemoryParent(context, relativePath, (directory, fileName) => readPinnedFile(directory, fileName, maxBytes));
11011
+ }
11012
+ async function syncDirectory(directory) {
11013
+ try {
11014
+ await directory.sync();
11015
+ } catch (error) {
11016
+ if (!["EINVAL", "EPERM"].includes(error.code ?? "")) throw error;
11017
+ }
11018
+ }
11019
+ async function replaceNative(context, relativePath, expected, content) {
11020
+ const byteCount = encoder2.encode(content).byteLength;
11021
+ if (byteCount > AGENT_MEMORY_MAX_FILE_BYTES) throw new AgentMemoryError("memory content exceeds its bounded file size");
11022
+ return withMemoryParent(context, relativePath, async (directory, fileName) => {
11023
+ const before = await readPinnedFile(directory, fileName);
11024
+ if (before.revision !== expected) throw new AgentMemoryRevisionConflictError(expected, before.revision);
11025
+ const parent = descriptorPath(directory);
11026
+ const temporary = `${parent}/.byok-memory-${randomUUID()}.tmp`;
11027
+ let handle;
11028
+ try {
11029
+ const check = await readPinnedFile(directory, fileName);
11030
+ if (check.revision !== expected || check.exists !== before.exists) throw new AgentMemoryRevisionConflictError(expected, check.revision);
11031
+ handle = await promises.open(temporary, noFollowFlags(constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL), 384);
11032
+ await handle.writeFile(content, "utf8");
11033
+ await handle.sync();
11034
+ await handle.close();
11035
+ handle = void 0;
11036
+ const finalCheck = await readPinnedFile(directory, fileName);
11037
+ if (finalCheck.revision !== expected || finalCheck.exists !== before.exists) throw new AgentMemoryRevisionConflictError(expected, finalCheck.revision);
11038
+ await promises.rename(temporary, `${parent}/${fileName}`);
11039
+ await syncDirectory(directory);
11040
+ return Object.freeze({ exists: true, content, revision: digest2(content), byteCount });
11041
+ } catch (error) {
11042
+ await handle?.close().catch(() => {
11043
+ });
11044
+ await promises.rm(temporary, { force: true }).catch(() => {
11045
+ });
11046
+ if (error instanceof AgentMemoryError) throw error;
11047
+ throw new AgentMemoryError("could not atomically replace memory file");
11048
+ }
11049
+ });
11050
+ }
11051
+ async function replace(context, relativePath, expected, content) {
11052
+ if (context.filesystem !== void 0) return context.filesystem.replace(relativePath, expected, content, AGENT_MEMORY_MAX_FILE_BYTES);
11053
+ return replaceNative(context, relativePath, expected, content);
11054
+ }
11055
+ async function removeNative(context, relativePath, expected) {
11056
+ if (relativePath === "MEMORY.md") throw new AgentMemoryError("MEMORY.md may not be deleted");
11057
+ await withMemoryParent(context, relativePath, async (directory, fileName) => {
11058
+ const before = await readPinnedFile(directory, fileName);
11059
+ if (!before.exists || before.revision !== expected) throw new AgentMemoryRevisionConflictError(expected, before.revision);
11060
+ const parent = descriptorPath(directory);
11061
+ const tombstone = `${parent}/.byok-memory-delete-${randomUUID()}.tmp`;
11062
+ try {
11063
+ const check = await readPinnedFile(directory, fileName);
11064
+ if (!check.exists || check.revision !== expected) throw new AgentMemoryRevisionConflictError(expected, check.revision);
11065
+ await promises.rename(`${parent}/${fileName}`, tombstone);
11066
+ await syncDirectory(directory);
11067
+ await promises.rm(tombstone);
11068
+ await syncDirectory(directory);
11069
+ } catch (error) {
11070
+ if (error instanceof AgentMemoryError) throw error;
11071
+ throw new AgentMemoryError("could not atomically delete memory file");
11072
+ }
11073
+ });
11074
+ }
11075
+ async function remove(context, relativePath, expected) {
11076
+ if (relativePath === "MEMORY.md") throw new AgentMemoryError("MEMORY.md may not be deleted");
11077
+ if (context.filesystem !== void 0) return context.filesystem.delete(relativePath, expected);
11078
+ return removeNative(context, relativePath, expected);
11079
+ }
11080
+ async function readInternalFile(context, fileName) {
11081
+ const relativePath = `${AGENT_HOME_INTERNAL_DIRECTORY}/${fileName}`;
11082
+ if (context.filesystem !== void 0) return context.filesystem.read(relativePath, AGENT_MEMORY_MAX_LOCAL_LOG_BYTES);
11083
+ return withPinnedDirectory(
11084
+ context.canonicalHome,
11085
+ [AGENT_HOME_INTERNAL_DIRECTORY],
11086
+ (directory) => readPinnedFile(directory, fileName, AGENT_MEMORY_MAX_LOCAL_LOG_BYTES),
11087
+ context.homeIdentity
11088
+ );
11089
+ }
11090
+ async function replaceInternalFile(context, fileName, expectedRevision, content) {
11091
+ const byteCount = encoder2.encode(content).byteLength;
11092
+ if (byteCount > AGENT_MEMORY_MAX_LOCAL_LOG_BYTES) throw new AgentMemoryError("Agent memory internal state exceeds its bounded size");
11093
+ const relativePath = `${AGENT_HOME_INTERNAL_DIRECTORY}/${fileName}`;
11094
+ if (context.filesystem !== void 0) return context.filesystem.replace(relativePath, expectedRevision, content, AGENT_MEMORY_MAX_LOCAL_LOG_BYTES);
11095
+ return withPinnedDirectory(context.canonicalHome, [AGENT_HOME_INTERNAL_DIRECTORY], async (directory) => {
11096
+ const before = await readPinnedFile(directory, fileName, AGENT_MEMORY_MAX_LOCAL_LOG_BYTES);
11097
+ if (before.revision !== expectedRevision) throw new AgentMemoryRevisionConflictError(expectedRevision, before.revision);
11098
+ const parent = descriptorPath(directory);
11099
+ const temporary = `${parent}/.byok-agent-memory-${randomUUID()}.tmp`;
11100
+ let handle;
11101
+ try {
11102
+ handle = await promises.open(temporary, noFollowFlags(constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL), 384);
11103
+ await handle.writeFile(content, "utf8");
11104
+ await handle.sync();
11105
+ await handle.close();
11106
+ handle = void 0;
11107
+ const check = await readPinnedFile(directory, fileName, AGENT_MEMORY_MAX_LOCAL_LOG_BYTES);
11108
+ if (check.revision !== expectedRevision || check.exists !== before.exists) throw new AgentMemoryRevisionConflictError(expectedRevision, check.revision);
11109
+ await promises.rename(temporary, `${parent}/${fileName}`);
11110
+ await syncDirectory(directory);
11111
+ return Object.freeze({ exists: true, content, revision: digest2(content), byteCount });
11112
+ } catch (error) {
11113
+ await handle?.close().catch(() => {
11114
+ });
11115
+ await promises.rm(temporary, { force: true }).catch(() => {
11116
+ });
11117
+ if (error instanceof AgentMemoryError) throw error;
11118
+ throw new AgentMemoryError("could not atomically replace Agent memory internal state");
11119
+ }
11120
+ }, context.homeIdentity);
11121
+ }
11122
+ function boundedAuditTail(previous, entry) {
11123
+ if (encoder2.encode(entry).byteLength > AGENT_MEMORY_MAX_LOCAL_LOG_BYTES) throw new AgentMemoryError("Agent memory audit entry exceeds its bounded size");
11124
+ const lines = previous.split("\n").filter((line) => line.length > 0);
11125
+ const kept = [entry];
11126
+ let byteCount = encoder2.encode(entry).byteLength;
11127
+ for (let index = lines.length - 1; index >= 0; index -= 1) {
11128
+ const candidate = `${lines[index]}
11129
+ `;
11130
+ const candidateBytes = encoder2.encode(candidate).byteLength;
11131
+ if (byteCount + candidateBytes > AGENT_MEMORY_MAX_LOCAL_LOG_BYTES) break;
11132
+ kept.unshift(candidate);
11133
+ byteCount += candidateBytes;
11134
+ }
11135
+ return kept.join("");
11136
+ }
11137
+ async function audit(context, kind, values) {
11138
+ const entry = `${JSON.stringify({ version: 1, kind, taskId: context.taskId, tenantId: context.tenantId, deviceId: context.deviceId, agentRef: context.agentRef, sessionRef: context.sessionRef, runtimeId: context.runtimeId, ...values, recordedAt: (/* @__PURE__ */ new Date()).toISOString() })}
11139
+ `;
11140
+ const current = await readInternalFile(context, AGENT_MEMORY_AUDIT_FILENAME);
11141
+ await replaceInternalFile(context, AGENT_MEMORY_AUDIT_FILENAME, current.revision, boundedAuditTail(current.content, entry));
11142
+ }
11143
+ async function recordAuditWarning(context, kind, values) {
11144
+ try {
11145
+ await audit(context, kind, values);
11146
+ return void 0;
11147
+ } catch {
11148
+ return Object.freeze({ code: "agent_memory_audit_unavailable" });
11149
+ }
11150
+ }
11151
+ var agentMemoryHomeQueues = /* @__PURE__ */ new Map();
11152
+ async function exclusiveAgentMemoryHome(home, fn) {
11153
+ const previous = agentMemoryHomeQueues.get(home) ?? Promise.resolve();
11154
+ let release;
11155
+ const next = new Promise((resolve) => {
11156
+ release = resolve;
11157
+ });
11158
+ agentMemoryHomeQueues.set(home, next);
11159
+ await previous;
11160
+ try {
11161
+ return await fn();
11162
+ } finally {
11163
+ release();
11164
+ if (agentMemoryHomeQueues.get(home) === next) agentMemoryHomeQueues.delete(home);
11165
+ }
11166
+ }
11167
+ var AgentMemoryService = class {
11168
+ constructor(input) {
11169
+ this.input = input;
11170
+ }
11171
+ input;
11172
+ async recall(input) {
11173
+ const context = taskContext(this.input);
11174
+ const relativePath = validateAgentMemoryPath(input.path);
11175
+ if (input.ifRevision !== void 0 && !revision(input.ifRevision)) throw new AgentMemoryError("ifRevision must be a sha256 content revision");
11176
+ const current = await readFile(context, relativePath);
11177
+ if (!current.exists) throw new AgentMemoryError("memory file does not exist");
11178
+ if (input.ifRevision !== void 0 && current.revision !== input.ifRevision) throw new AgentMemoryRevisionConflictError(input.ifRevision, current.revision);
11179
+ const auditWarning = await exclusiveAgentMemoryHome(context.canonicalHome, () => recordAuditWarning(context, "recall", {
11180
+ path: relativePath,
11181
+ revision: current.revision,
11182
+ byteCount: current.byteCount
11183
+ }));
11184
+ return Object.freeze({
11185
+ path: relativePath,
11186
+ revision: current.revision,
11187
+ content: current.content,
11188
+ ...auditWarning === void 0 ? {} : { auditWarning }
11189
+ });
11190
+ }
11191
+ async save(input) {
11192
+ const context = taskContext(this.input);
11193
+ const relativePath = validateAgentMemoryPath(input.path);
11194
+ if (input.op !== "replace" && input.op !== "delete" || !revision(input.expectedRevision)) throw new AgentMemoryError("memory save requires op and sha256 expectedRevision");
11195
+ if (input.op === "replace" && typeof input.content !== "string") throw new AgentMemoryError("replace requires string content");
11196
+ if (input.op === "delete" && input.content !== void 0) throw new AgentMemoryError("delete does not accept content");
11197
+ const expectedRevision = input.expectedRevision;
11198
+ const content = input.content;
11199
+ return exclusiveAgentMemoryHome(context.canonicalHome, async () => {
11200
+ if (input.op === "delete") {
11201
+ await remove(context, relativePath, expectedRevision);
11202
+ const auditWarning2 = await recordAuditWarning(context, "save", { path: relativePath, operation: "delete" });
11203
+ return Object.freeze({ path: relativePath, deleted: true, ...auditWarning2 === void 0 ? {} : { auditWarning: auditWarning2 } });
11204
+ }
11205
+ if (typeof content !== "string") throw new AgentMemoryError("replace requires string content");
11206
+ const current = await replace(context, relativePath, expectedRevision, content);
11207
+ const auditWarning = await recordAuditWarning(context, "save", { path: relativePath, operation: "replace", revision: current.revision, byteCount: current.byteCount });
11208
+ return Object.freeze({ path: relativePath, revision: current.revision, deleted: false, ...auditWarning === void 0 ? {} : { auditWarning } });
11209
+ });
11210
+ }
11211
+ };
11212
+ async function memoryNotePaths(context) {
11213
+ if (context.filesystem !== void 0) {
11214
+ const paths = await context.filesystem.walk("notes", AGENT_MEMORY_MAX_SNAPSHOT_ENTRIES);
11215
+ const candidates2 = [];
11216
+ for (const candidate of paths) {
11217
+ try {
11218
+ candidates2.push(validateAgentMemoryPath(candidate));
11219
+ } catch {
11220
+ }
11221
+ }
11222
+ return Object.freeze(candidates2);
11223
+ }
11224
+ const candidates = [];
11225
+ let entriesSeen = 0;
11226
+ async function walk(directory, relativeDirectory) {
11227
+ const entries = await promises.readdir(descriptorPath(directory), { withFileTypes: true });
11228
+ for (const entry of entries) {
11229
+ entriesSeen += 1;
11230
+ if (entriesSeen > AGENT_MEMORY_MAX_SNAPSHOT_ENTRIES) throw new AgentMemoryError("memory snapshot exceeds bounded directory entries");
11231
+ const candidate = `${relativeDirectory}/${entry.name}`;
11232
+ if (entry.isSymbolicLink()) throw new AgentMemoryError("memory notes contains a symlink");
11233
+ if (entry.isDirectory()) {
11234
+ if (SAFE_SEGMENT.test(entry.name) && !SECRET_LIKE.test(entry.name) && entry.name !== ".byok") {
11235
+ const nested = await openPinnedDirectory(`${descriptorPath(directory)}/${entry.name}`);
11236
+ try {
11237
+ await walk(nested, candidate);
11238
+ } finally {
11239
+ await nested.close().catch(() => {
11240
+ });
11241
+ }
11242
+ }
11243
+ continue;
11244
+ }
11245
+ if (!entry.isFile()) continue;
11246
+ try {
11247
+ candidates.push(validateAgentMemoryPath(candidate));
11248
+ } catch {
11249
+ }
11250
+ }
11251
+ }
11252
+ await withPinnedDirectory(context.canonicalHome, ["notes"], (directory) => walk(directory, "notes"), context.homeIdentity);
11253
+ return Object.freeze(candidates);
11254
+ }
11255
+ async function captureAgentMemorySnapshot(input) {
11256
+ const context = taskContext(input);
11257
+ const candidates = ["MEMORY.md", ...await memoryNotePaths(context)];
11258
+ const paths = [...new Set(candidates)].sort((a, b) => a.localeCompare(b));
11259
+ if (paths.length > AGENT_MEMORY_MAX_SNAPSHOT_FILES) throw new AgentMemoryError("memory snapshot exceeds bounded file count");
11260
+ const files = [];
11261
+ let totalBytes = 0;
11262
+ for (const relativePath of paths) {
11263
+ const current = await readFile(context, relativePath);
11264
+ if (!current.exists) {
11265
+ if (relativePath === "MEMORY.md") throw new AgentMemoryError("MEMORY.md disappeared before snapshot");
11266
+ continue;
11267
+ }
11268
+ totalBytes += current.byteCount;
11269
+ if (totalBytes > AGENT_MEMORY_MAX_SNAPSHOT_BYTES) throw new AgentMemoryError("memory snapshot exceeds bounded total size");
11270
+ files.push(Object.freeze({ path: relativePath, revision: current.revision, byteCount: current.byteCount, content: current.content }));
11271
+ }
11272
+ const snapshot = Object.freeze({ files: Object.freeze(files), totalBytes });
11273
+ await exclusiveAgentMemoryHome(context.canonicalHome, () => audit(context, "snapshot", {
11274
+ files: files.map((file) => ({ path: file.path, revision: file.revision, byteCount: file.byteCount }))
11275
+ }));
11276
+ return snapshot;
11277
+ }
11278
+ function bytesEqual(left, right) {
11279
+ if (left.byteLength !== right.byteLength) return false;
11280
+ return left.every((value, index) => value === right[index]);
11281
+ }
11282
+ function rawSnapshotBytes(snapshot) {
11283
+ return encoder2.encode(JSON.stringify({
11284
+ files: snapshot.files.map((file) => ({ path: file.path, revision: file.revision, byteCount: file.byteCount, content: file.content }))
11285
+ }));
11286
+ }
11287
+ function redactedBytes(raw, candidate) {
11288
+ if (!(candidate instanceof Uint8Array)) throw new AgentMemoryError("redactor must return Uint8Array bytes");
11289
+ const bytes = candidate.slice();
11290
+ if (bytes.byteLength > AGENT_MEMORY_PROJECTION_MAX_REDACTED_BYTES) throw new AgentMemoryError("redacted snapshot exceeds bounded projection bytes");
11291
+ if (bytesEqual(bytes, rawSnapshotBytes(raw))) throw new AgentMemoryError("identity/pass-through redactor is forbidden");
11292
+ return bytes;
11293
+ }
11294
+ function isPlainRecord(value) {
11295
+ return typeof value === "object" && value !== null && !Array.isArray(value);
11296
+ }
11297
+ function exactKeys(value, keys) {
11298
+ const actual = Object.keys(value).sort();
11299
+ const expected = [...keys].sort();
11300
+ return actual.length === expected.length && actual.every((key, index) => key === expected[index]);
11301
+ }
11302
+ function validOrderingValue(value, allowZero = false) {
11303
+ return typeof value === "number" && Number.isInteger(value) && value >= (allowZero ? 0 : 1) && value <= AGENT_MEMORY_PROJECTION_MAX_ORDERING_VALUE;
11304
+ }
11305
+ function freezeOutboxRecord(value) {
11306
+ if (!isPlainRecord(value) || !exactKeys(value, ["version", "mutation", "createdAt"]) || value.version !== 2 || typeof value.createdAt !== "string" || value.createdAt.length === 0 || /[\u0000\r\n]/u.test(value.createdAt)) throw new AgentMemoryError("Agent memory outbox state is invalid");
11307
+ const mutation = AgentMemoryProjectionMutationSchema.safeParse(value.mutation);
11308
+ if (!mutation.success) throw new AgentMemoryError("Agent memory outbox state is invalid");
11309
+ return Object.freeze({ version: 2, mutation: Object.freeze(mutation.data), createdAt: value.createdAt });
11310
+ }
11311
+ function freezeOutboxState(value) {
11312
+ if (!isPlainRecord(value) || !exactKeys(value, ["version", "currentWriterEpoch", "highWater", "pending"]) || value.version !== 2 || !validOrderingValue(value.currentWriterEpoch) || !Array.isArray(value.highWater) || !Array.isArray(value.pending)) throw new AgentMemoryError("Agent memory outbox state is invalid");
11313
+ if (value.highWater.length > 1 || value.pending.length > 1) throw new AgentMemoryError("Agent memory outbox state is invalid");
11314
+ const highWater = value.highWater.map((entry) => {
11315
+ if (!isPlainRecord(entry) || !exactKeys(entry, ["writerEpoch", "sourceSeq"]) || !validOrderingValue(entry.writerEpoch) || !validOrderingValue(entry.sourceSeq, true)) throw new AgentMemoryError("Agent memory outbox state is invalid");
11316
+ if (entry.writerEpoch !== value.currentWriterEpoch) throw new AgentMemoryError("Agent memory outbox state is invalid");
11317
+ return Object.freeze({ writerEpoch: entry.writerEpoch, sourceSeq: entry.sourceSeq });
11318
+ }).sort((left, right) => left.writerEpoch - right.writerEpoch);
11319
+ const pending = value.pending.map(freezeOutboxRecord).sort((left, right) => left.mutation.sourceSeq - right.mutation.sourceSeq);
11320
+ const seenEpochs = /* @__PURE__ */ new Set();
11321
+ const highWaterByEpoch = /* @__PURE__ */ new Map();
11322
+ for (const entry of highWater) {
11323
+ if (seenEpochs.has(entry.writerEpoch)) throw new AgentMemoryError("Agent memory outbox state is invalid");
11324
+ seenEpochs.add(entry.writerEpoch);
11325
+ highWaterByEpoch.set(entry.writerEpoch, entry.sourceSeq);
11326
+ }
11327
+ const mutationIds = /* @__PURE__ */ new Set();
11328
+ const sourceSeqs = /* @__PURE__ */ new Set();
11329
+ for (const record of pending) {
11330
+ if (record.mutation.writerEpoch !== value.currentWriterEpoch || mutationIds.has(record.mutation.mutationId) || sourceSeqs.has(record.mutation.sourceSeq) || record.mutation.sourceSeq > (highWaterByEpoch.get(record.mutation.writerEpoch) ?? 0)) throw new AgentMemoryError("Agent memory outbox state is invalid");
11331
+ mutationIds.add(record.mutation.mutationId);
11332
+ sourceSeqs.add(record.mutation.sourceSeq);
11333
+ }
11334
+ return Object.freeze({ version: 2, currentWriterEpoch: value.currentWriterEpoch, highWater: Object.freeze(highWater), pending: Object.freeze(pending) });
11335
+ }
11336
+ function outboxStateJson(state) {
11337
+ return JSON.stringify({ version: state.version, currentWriterEpoch: state.currentWriterEpoch, highWater: state.highWater, pending: state.pending });
11338
+ }
11339
+ function initialOutboxState(writerEpoch) {
11340
+ return freezeOutboxState({ version: 2, currentWriterEpoch: writerEpoch, highWater: [], pending: [] });
11341
+ }
11342
+ function stateWithHighWater(state, writerEpoch, sourceSeq, pending) {
11343
+ if (writerEpoch !== state.currentWriterEpoch) throw new AgentMemoryError("Agent memory outbox state is invalid");
11344
+ return freezeOutboxState({ version: 2, currentWriterEpoch: state.currentWriterEpoch, highWater: [Object.freeze({ writerEpoch, sourceSeq })], pending });
11345
+ }
11346
+ async function publishAgentMemoryProjection(port, mutation) {
11347
+ let timer;
11348
+ try {
11349
+ return await new Promise((resolve, reject) => {
11350
+ timer = setTimeout(
11351
+ () => reject(new AgentMemoryError("Agent memory projection publish timed out")),
11352
+ AGENT_MEMORY_PROJECTION_PUBLISH_TIMEOUT_MS
11353
+ );
11354
+ try {
11355
+ Promise.resolve(port.publish(Object.freeze({ mutation }))).then(resolve, reject);
11356
+ } catch (error) {
11357
+ reject(error);
11358
+ }
11359
+ });
11360
+ } finally {
11361
+ if (timer !== void 0) clearTimeout(timer);
11362
+ }
11363
+ }
11364
+ var AgentMemoryRedactedOutbox = class _AgentMemoryRedactedOutbox {
11365
+ constructor(context, grant, filePath) {
11366
+ this.context = context;
11367
+ this.grant = grant;
11368
+ this.filePath = filePath;
11369
+ }
11370
+ context;
11371
+ grant;
11372
+ filePath;
11373
+ state;
11374
+ fileRevision;
11375
+ writeTail = Promise.resolve();
11376
+ static async open(input, grant) {
11377
+ const context = taskContext(input);
11378
+ const outbox = new _AgentMemoryRedactedOutbox(context, grant, path3__default.join(context.canonicalHome, AGENT_HOME_INTERNAL_DIRECTORY, AGENT_MEMORY_OUTBOX_FILENAME));
11379
+ await outbox.load();
11380
+ await outbox.admitGrant();
11381
+ return outbox;
11382
+ }
11383
+ pending() {
11384
+ return this.state.pending;
11385
+ }
11386
+ async append(bytes) {
11387
+ return this.exclusive(async () => {
11388
+ if (this.state.currentWriterEpoch !== this.grant.writerEpoch) throw new AgentMemoryError("Agent memory outbox writer epoch is stale");
11389
+ if (this.state.pending.length > 0) throw new AgentMemoryError("Agent memory outbox has pending projection mutations");
11390
+ const currentHighWater = this.state.highWater[0]?.sourceSeq ?? 0;
11391
+ if (currentHighWater >= AGENT_MEMORY_PROJECTION_MAX_ORDERING_VALUE) throw new AgentMemoryError("Agent memory outbox source sequence is exhausted");
11392
+ const sourceSeq = currentHighWater + 1;
11393
+ const mutation = AgentMemoryProjectionMutationSchema.parse({
11394
+ taskId: this.context.taskId,
11395
+ agentRef: this.context.agentRef,
11396
+ sessionRef: this.context.sessionRef,
11397
+ runtimeId: this.context.runtimeId,
11398
+ grantRef: this.grant.grantRef,
11399
+ writerEpoch: this.grant.writerEpoch,
11400
+ sourceSeq,
11401
+ mutationId: randomUUID(),
11402
+ policyRevision: this.grant.policyRevision,
11403
+ snapshot: { redactedHash: digestBytes(bytes), redactedByteCount: bytes.byteLength, redactedBytes: Buffer.from(bytes).toString("base64url") }
11404
+ });
11405
+ const record = Object.freeze({ version: 2, mutation: Object.freeze(mutation), createdAt: (/* @__PURE__ */ new Date()).toISOString() });
11406
+ await this.persist(stateWithHighWater(this.state, mutation.writerEpoch, mutation.sourceSeq, [record]));
11407
+ return record;
11408
+ });
11409
+ }
11410
+ async replay(port) {
11411
+ return this.exclusive(async () => {
11412
+ for (const record of this.pending()) {
11413
+ if (!(await publishAgentMemoryProjection(port, record.mutation)).accepted) {
11414
+ return Object.freeze({
11415
+ status: "pending",
11416
+ writerEpoch: record.mutation.writerEpoch,
11417
+ sourceSeq: record.mutation.sourceSeq,
11418
+ mutationId: record.mutation.mutationId
11419
+ });
11420
+ }
11421
+ await this.persist(freezeOutboxState({
11422
+ version: 2,
11423
+ currentWriterEpoch: this.state.currentWriterEpoch,
11424
+ highWater: this.state.highWater,
11425
+ pending: this.state.pending.filter((candidate) => candidate.mutation.mutationId !== record.mutation.mutationId)
11426
+ }));
11427
+ }
11428
+ return DRAINED_AGENT_MEMORY_PROJECTION_REPLAY;
11429
+ });
11430
+ }
11431
+ async load() {
11432
+ const current = await readInternalFile(this.context, AGENT_MEMORY_OUTBOX_FILENAME);
11433
+ this.fileRevision = current.revision;
11434
+ this.state = current.exists ? this.loadBody(current.content) : initialOutboxState(this.grant.writerEpoch);
11435
+ }
11436
+ loadBody(body) {
11437
+ try {
11438
+ return freezeOutboxState(JSON.parse(body));
11439
+ } catch {
11440
+ throw new AgentMemoryError("Agent memory outbox state is invalid");
11441
+ }
11442
+ }
11443
+ async admitGrant() {
11444
+ if (this.grant.writerEpoch < this.state.currentWriterEpoch) throw new AgentMemoryError("Agent memory outbox writer epoch is stale");
11445
+ if (this.grant.writerEpoch === this.state.currentWriterEpoch) return;
11446
+ await this.persist(freezeOutboxState({ version: 2, currentWriterEpoch: this.grant.writerEpoch, highWater: [], pending: [] }));
11447
+ }
11448
+ async persist(next) {
11449
+ const replaced = await replaceInternalFile(this.context, AGENT_MEMORY_OUTBOX_FILENAME, this.fileRevision, outboxStateJson(next));
11450
+ this.fileRevision = replaced.revision;
11451
+ this.state = next;
11452
+ }
11453
+ async exclusive(fn) {
11454
+ const previous = this.writeTail;
11455
+ let release;
11456
+ this.writeTail = new Promise((resolve) => {
11457
+ release = resolve;
11458
+ });
11459
+ await previous;
11460
+ try {
11461
+ return await fn();
11462
+ } finally {
11463
+ release();
11464
+ }
11465
+ }
11466
+ };
11467
+ async function snapshotAndProjectAgentMemory(input, projection) {
11468
+ if (projection?.capability !== AGENT_MEMORY_PROJECTION_CAPABILITY || !projection.grant || !projection.redactor || !projection.port) return;
11469
+ const context = taskContext(input);
11470
+ const outbox = await AgentMemoryRedactedOutbox.open(context, projection.grant);
11471
+ const initialReplay = await outbox.replay(projection.port);
11472
+ if (initialReplay.status === "pending") throw new AgentMemoryProjectionReplayPendingError(initialReplay);
11473
+ const snapshot = await captureAgentMemorySnapshot(context);
11474
+ await outbox.append(redactedBytes(snapshot, await projection.redactor.redact(snapshot)));
11475
+ const trailingReplay = await outbox.replay(projection.port);
11476
+ if (trailingReplay.status === "pending") throw new AgentMemoryProjectionReplayPendingError(trailingReplay);
11477
+ }
11478
+ var AGENT_MEMORY_FILESYSTEM_HELPER_PROTOCOL = 2;
11479
+ var AGENT_MEMORY_FILESYSTEM_HELPER_VERSION = "2";
11480
+ var HELPER_REQUEST_TIMEOUT_MS = 1e4;
11481
+ var HELPER_MAX_JSON_LINE_BYTES = 2 * 1024 * 1024;
11482
+ var HELPER_MAX_CONTENT_BYTES = 1 * 1024 * 1024;
11483
+ var HELPER_MAX_STDERR_BYTES = 4 * 1024;
11484
+ var HelperRevisionConflict = class extends Error {
11485
+ constructor(actualRevision) {
11486
+ super("helper revision conflict");
11487
+ this.actualRevision = actualRevision;
11488
+ }
11489
+ actualRevision;
11490
+ };
11491
+ function safeHelperPath(value) {
11492
+ if (typeof value !== "string" || !path3__default.isAbsolute(value) || value.length === 0 || /[\u0000\r\n]/u.test(value)) {
11493
+ throw new AgentMemoryError("Agent memory filesystem helper must be an explicit absolute executable path");
11494
+ }
11495
+ return path3__default.resolve(value);
11496
+ }
11497
+ function helperPlatformSupported() {
11498
+ return process.platform === "darwin";
11499
+ }
11500
+ function isAgentMemoryFilesystemHelperSupported() {
11501
+ return helperPlatformSupported();
11502
+ }
11503
+ function unixIdentity(homeIdentity) {
11504
+ return Object.freeze({ kind: "unix", dev: homeIdentity.dev.toString(10), ino: homeIdentity.ino.toString(10) });
11505
+ }
11506
+ function responseRecord(value) {
11507
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return void 0;
11508
+ const record = value;
11509
+ if (typeof record.id !== "string" || record.protocol !== AGENT_MEMORY_FILESYSTEM_HELPER_PROTOCOL || typeof record.ok !== "boolean") return void 0;
11510
+ if (record.ok) {
11511
+ if (record.result === null || typeof record.result !== "object" || Array.isArray(record.result)) return void 0;
11512
+ return record;
11513
+ }
11514
+ if (record.error === null || typeof record.error !== "object" || Array.isArray(record.error)) return void 0;
11515
+ const error = record.error;
11516
+ if (typeof error.code !== "string" || typeof error.message !== "string" || error.actualRevision !== void 0 && typeof error.actualRevision !== "string") return void 0;
11517
+ return record;
11518
+ }
11519
+ function boundedFileState(result, maxBytes) {
11520
+ let content;
11521
+ try {
11522
+ if (typeof result.contentBase64 !== "string" || !/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}|[A-Za-z0-9+/]{3})?$/u.test(result.contentBase64)) {
11523
+ throw new Error("invalid base64");
11524
+ }
11525
+ content = Buffer.from(result.contentBase64, "base64").toString("utf8");
11526
+ } catch {
11527
+ throw new AgentMemoryError("Agent memory filesystem helper returned an invalid file state");
11528
+ }
11529
+ if (typeof result.exists !== "boolean" || typeof result.revision !== "string" || typeof result.byteCount !== "number" || !Number.isSafeInteger(result.byteCount) || result.byteCount < 0 || result.byteCount > maxBytes || Buffer.byteLength(content, "utf8") !== result.byteCount || !/^sha256:[a-f0-9]{64}$/u.test(result.revision)) {
11530
+ throw new AgentMemoryError("Agent memory filesystem helper returned an invalid file state");
11531
+ }
11532
+ return Object.freeze({ exists: result.exists, content, revision: result.revision, byteCount: result.byteCount });
11533
+ }
11534
+ function encodeReplaceContent(content, maxBytes) {
11535
+ if (!Number.isSafeInteger(maxBytes) || maxBytes < 0 || maxBytes > HELPER_MAX_CONTENT_BYTES) {
11536
+ throw new AgentMemoryError("Agent memory filesystem helper requested an invalid byte limit");
11537
+ }
11538
+ const bytes = Buffer.from(content, "utf8");
11539
+ if (bytes.byteLength > maxBytes) {
11540
+ throw new AgentMemoryError("Agent memory filesystem helper replacement exceeds its requested byte limit");
11541
+ }
11542
+ return bytes.toString("base64").replace(/=+$/u, "");
11543
+ }
11544
+ var AgentMemoryFilesystemHelperClient = class _AgentMemoryFilesystemHelperClient {
11545
+ constructor(child) {
11546
+ this.child = child;
11547
+ child.stdout.on("data", (chunk) => this.handleStdoutChunk(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)));
11548
+ child.stderr.on("data", (chunk) => {
11549
+ this.stderrBytes += Buffer.byteLength(chunk);
11550
+ if (this.stderrBytes > HELPER_MAX_STDERR_BYTES) this.fail(new AgentMemoryError("Agent memory filesystem helper exceeded bounded stderr"));
11551
+ });
11552
+ child.stdin.on("error", () => this.fail(new AgentMemoryError("Agent memory filesystem helper request could not be written")));
11553
+ child.once("error", () => this.fail(new AgentMemoryError("Agent memory filesystem helper could not be started")));
11554
+ child.once("exit", (code, signal) => {
11555
+ if (!this.closed || code !== 0) this.fail(new AgentMemoryError(`Agent memory filesystem helper exited unexpectedly (${code ?? signal ?? "unknown"})`));
11556
+ });
11557
+ }
11558
+ child;
11559
+ pending = /* @__PURE__ */ new Map();
11560
+ stdoutBuffer = Buffer.alloc(0);
11561
+ sequence = 0;
11562
+ stderrBytes = 0;
11563
+ closed = false;
11564
+ fatalError;
11565
+ static async open(input) {
11566
+ if (!helperPlatformSupported()) throw new AgentMemoryError("Agent memory filesystem helper is not admitted on this platform");
11567
+ const helperBin = safeHelperPath(input.helperBin);
11568
+ const child = spawn(helperBin, ["serve"], {
11569
+ stdio: ["pipe", "pipe", "pipe"],
11570
+ shell: false,
11571
+ windowsHide: true,
11572
+ env: Object.freeze({})
11573
+ });
11574
+ const client = new _AgentMemoryFilesystemHelperClient(child);
11575
+ try {
11576
+ const result = await client.request("open", {
11577
+ root: path3__default.resolve(input.canonicalHome),
11578
+ expectedIdentity: unixIdentity(input.homeIdentity)
11579
+ });
11580
+ if (result.helperVersion !== AGENT_MEMORY_FILESYSTEM_HELPER_VERSION) throw new AgentMemoryError("Agent memory filesystem helper version mismatch");
11581
+ const identity = result.identity;
11582
+ if (identity === null || typeof identity !== "object" || Array.isArray(identity)) throw new AgentMemoryError("Agent memory filesystem helper omitted root identity");
11583
+ const actual = identity;
11584
+ const expected = unixIdentity(input.homeIdentity);
11585
+ if (actual.kind !== expected.kind || actual.dev !== expected.dev || actual.ino !== expected.ino) throw new AgentMemoryError("Agent memory filesystem helper root identity mismatch");
11586
+ return client;
11587
+ } catch (error) {
11588
+ await client.close().catch(() => {
11589
+ });
11590
+ throw error;
11591
+ }
11592
+ }
11593
+ async read(relativePath, maxBytes) {
11594
+ return boundedFileState(await this.request("read", { path: relativePath, maxBytes }), maxBytes);
11595
+ }
11596
+ async replace(relativePath, expectedRevision, content, maxBytes) {
11597
+ try {
11598
+ const contentBase64 = encodeReplaceContent(content, maxBytes);
11599
+ return boundedFileState(await this.request("replace", { path: relativePath, expectedRevision, contentBase64, maxBytes }), maxBytes);
11600
+ } catch (error) {
11601
+ if (error instanceof HelperRevisionConflict) throw new AgentMemoryRevisionConflictError(expectedRevision, error.actualRevision);
11602
+ throw error;
11603
+ }
11604
+ }
11605
+ async delete(relativePath, expectedRevision) {
11606
+ try {
11607
+ await this.request("delete", { path: relativePath, expectedRevision });
11608
+ } catch (error) {
11609
+ if (error instanceof HelperRevisionConflict) throw new AgentMemoryRevisionConflictError(expectedRevision, error.actualRevision);
11610
+ throw error;
11611
+ }
11612
+ }
11613
+ async append(relativePath, content, maxBytes) {
11614
+ await this.request("append", { path: relativePath, content, maxBytes });
11615
+ }
11616
+ async walk(relativePath, maxEntries) {
11617
+ const result = await this.request("walk", { path: relativePath, maxEntries });
11618
+ if (!Array.isArray(result.paths) || result.paths.length > maxEntries || result.paths.some((candidate) => typeof candidate !== "string")) {
11619
+ throw new AgentMemoryError("Agent memory filesystem helper returned an invalid walk result");
11620
+ }
11621
+ return Object.freeze([...result.paths]);
11622
+ }
11623
+ async close() {
11624
+ if (this.closed) return;
11625
+ this.closed = true;
11626
+ try {
11627
+ if (this.child.exitCode === null && this.child.signalCode === null && this.fatalError === void 0) await this.requestWhileClosing("close", {});
11628
+ } finally {
11629
+ this.child.stdin.end();
11630
+ if (this.child.exitCode === null && this.child.signalCode === null) this.child.kill();
11631
+ this.rejectPending(new AgentMemoryError("Agent memory filesystem helper is closed"));
11632
+ }
11633
+ }
11634
+ request(op, fields) {
11635
+ if (this.closed) return Promise.reject(new AgentMemoryError("Agent memory filesystem helper is closed"));
11636
+ return this.requestInternal(op, fields);
11637
+ }
11638
+ requestWhileClosing(op, fields) {
11639
+ return this.requestInternal(op, fields);
11640
+ }
11641
+ requestInternal(op, fields) {
11642
+ if (this.fatalError !== void 0) return Promise.reject(this.fatalError);
11643
+ const id = `m${++this.sequence}`;
11644
+ let line;
11645
+ try {
11646
+ line = `${JSON.stringify({ id, protocol: AGENT_MEMORY_FILESYSTEM_HELPER_PROTOCOL, op, ...fields })}
11647
+ `;
11648
+ } catch {
11649
+ return Promise.reject(new AgentMemoryError("Agent memory filesystem helper request could not be encoded"));
11650
+ }
11651
+ if (Buffer.byteLength(line, "utf8") > HELPER_MAX_JSON_LINE_BYTES) {
11652
+ return Promise.reject(new AgentMemoryError("Agent memory filesystem helper request exceeded bounded stdin"));
11653
+ }
11654
+ return new Promise((resolve, reject) => {
11655
+ const timer = setTimeout(() => {
11656
+ this.pending.delete(id);
11657
+ const error = new AgentMemoryError("Agent memory filesystem helper request timed out");
11658
+ reject(error);
11659
+ this.fail(error);
11660
+ }, HELPER_REQUEST_TIMEOUT_MS);
11661
+ timer.unref?.();
11662
+ this.pending.set(id, { resolve: (response) => resolve(response.result), reject, timer });
11663
+ this.child.stdin.write(line, (error) => {
11664
+ if (!error) return;
11665
+ const item = this.pending.get(id);
11666
+ if (item === void 0) return;
11667
+ clearTimeout(item.timer);
11668
+ this.pending.delete(id);
11669
+ item.reject(new AgentMemoryError("Agent memory filesystem helper request could not be written"));
11670
+ });
11671
+ });
11672
+ }
11673
+ handleStdoutChunk(chunk) {
11674
+ let offset = 0;
11675
+ while (offset < chunk.length && this.fatalError === void 0) {
11676
+ const newline = chunk.indexOf(10, offset);
11677
+ const end = newline === -1 ? chunk.length : newline;
11678
+ const segment = chunk.subarray(offset, end);
11679
+ if (this.stdoutBuffer.length + segment.length > HELPER_MAX_JSON_LINE_BYTES) {
11680
+ this.fail(new AgentMemoryError("Agent memory filesystem helper exceeded bounded stdout"));
11681
+ return;
11682
+ }
11683
+ this.stdoutBuffer = this.stdoutBuffer.length === 0 ? Buffer.from(segment) : Buffer.concat([this.stdoutBuffer, segment]);
11684
+ if (newline === -1) return;
11685
+ const line = this.stdoutBuffer;
11686
+ this.stdoutBuffer = Buffer.alloc(0);
11687
+ this.handleLine(line);
11688
+ offset = newline + 1;
11689
+ }
11690
+ }
11691
+ handleLine(line) {
11692
+ let parsed;
11693
+ try {
11694
+ parsed = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(line));
11695
+ } catch {
11696
+ this.fail(new AgentMemoryError("Agent memory filesystem helper returned malformed JSON"));
11697
+ return;
11698
+ }
11699
+ const response = responseRecord(parsed);
11700
+ if (response === void 0) {
11701
+ this.fail(new AgentMemoryError("Agent memory filesystem helper returned an invalid response"));
11702
+ return;
11703
+ }
11704
+ const item = this.pending.get(response.id);
11705
+ if (item === void 0) {
11706
+ this.fail(new AgentMemoryError("Agent memory filesystem helper returned an unsolicited response"));
11707
+ return;
11708
+ }
11709
+ clearTimeout(item.timer);
11710
+ this.pending.delete(response.id);
11711
+ if (response.ok) {
11712
+ item.resolve(response);
11713
+ return;
11714
+ }
11715
+ if (response.error.code === "revision_conflict" && typeof response.error.actualRevision === "string") {
11716
+ item.reject(new HelperRevisionConflict(response.error.actualRevision));
11717
+ return;
11718
+ }
11719
+ item.reject(new AgentMemoryError(`Agent memory filesystem helper rejected the operation: ${response.error.code}`));
11720
+ }
11721
+ fail(error) {
11722
+ if (this.fatalError !== void 0) return;
11723
+ this.fatalError = error;
11724
+ this.rejectPending(error);
11725
+ if (this.child.exitCode === null && this.child.signalCode === null) this.child.kill();
11726
+ }
11727
+ rejectPending(error) {
11728
+ for (const item of this.pending.values()) {
11729
+ clearTimeout(item.timer);
11730
+ item.reject(error);
11731
+ }
11732
+ this.pending.clear();
11733
+ }
11734
+ };
11735
+ async function openAgentMemoryFilesystemHelper(input) {
11736
+ return AgentMemoryFilesystemHelperClient.open(input);
11737
+ }
11738
+
11739
+ // src/daemon/task-runner.ts
11740
+ var DEFAULT_APPROVAL_TIMEOUT_MS = 10 * 6e4;
11741
+ var DEFAULT_SHUTDOWN_INTERRUPT_TIMEOUT_MS = 5e3;
11742
+ var AGENT_TERMINAL_EVIDENCE_MAX_ATTEMPTS = 3;
11743
+ var AGENT_TERMINAL_EVIDENCE_RETRY_DELAY_MS = 20;
11744
+ var GIT_OBSERVATION_TIMEOUT_MS = 5e3;
11745
+ var MAX_PENDING_APPROVALS_PER_TASK = 16;
11746
+ var NoPendingApprovalError = class extends Error {
11747
+ constructor(taskId) {
11748
+ super(`no pending out-of-band approval to resolve for task ${taskId}`);
11749
+ this.taskId = taskId;
11750
+ this.name = "NoPendingApprovalError";
11751
+ }
11752
+ taskId;
11753
+ };
11754
+ var MAX_INLINE_ARTIFACT_BYTES = 64 * 1024;
11755
+ var MAX_TRACKED_TASK_IDS = 2e3;
11756
+ var MAX_DURATION_EXCEEDED_REASON_PREFIX = "resource limit exceeded: maxDurationMs";
11757
+ var MAX_OUTPUT_BYTES_EXCEEDED_REASON_PREFIX = "resource limit exceeded: maxTaskOutputBytes";
11758
+ var MAX_PROGRESS_BATCH_BYTES_EXCEEDED_REASON_PREFIX = "resource limit exceeded: progressBatch.maxBatchBytes";
11759
+ var RESULT_DOCUMENT_UNDELIVERABLE_REASON_PREFIX = "result document undeliverable";
11760
+ function resultDocumentRejectionDetail(check) {
11761
+ switch (check.reason) {
11762
+ case "over-cap":
11763
+ return `${check.bytes} bytes as canonical JSON, over the ${RESULT_DOCUMENT_MAX_BYTES}-byte limit (it is never truncated \u2014 a truncated JSON document is not valid JSON; use artifactRefs for a result this size)`;
11764
+ case "not-serializable":
11765
+ return "not JSON-serializable (JSON.stringify threw, or produced no output at all)";
11766
+ case "not-plain-json":
11767
+ return "not plain JSON data: it does not equal its own JSON round trip, so serializing it would silently change it (an undefined-valued key, NaN, a function or symbol value, a Date, a toJSON that rewrites the value, or a getter that answers differently on a second read)";
11768
+ default: {
11769
+ const exhaustive = check;
11770
+ throw new Error(`unhandled result document rejection: ${JSON.stringify(exhaustive)}`);
11771
+ }
11772
+ }
11773
+ }
11774
+ var DEFAULT_MAX_TASK_OUTPUT_BYTES = 64 * 1024 * 1024;
11775
+ function isKnownRuntimeId(id) {
11776
+ return RuntimeIdSchema.safeParse(id).success;
11777
+ }
11778
+ function terminalUsageNumber(value, maximum) {
11779
+ return value !== void 0 && Number.isSafeInteger(value) && value >= 0 && value <= maximum ? value : void 0;
11780
+ }
11781
+ var DEFAULT_RUNTIME_PREFERENCE = ["claude", "codex", "pi"];
11782
+ function orderByPreference(candidates, preference) {
11783
+ const rank = new Map(preference.map((id, index) => [id, index]));
11784
+ return [...candidates].sort((a, b) => (rank.get(a.descriptor.id) ?? preference.length) - (rank.get(b.descriptor.id) ?? preference.length));
11785
+ }
11786
+ function adapterSupportsMode(descriptor, mode) {
11787
+ return descriptor.capabilities.permissionModes.includes(mode);
11788
+ }
11789
+ function adapterSupportsMcpToolsets(descriptor) {
11790
+ return descriptor.capabilities.mcpToolsets === true;
11791
+ }
11792
+ function withoutRequiredToolsets(payload) {
11793
+ const { requiredToolsets, egressPolicy, messageEgress, ...offer } = payload;
11794
+ return offer;
11795
+ }
11796
+ function sameEgressPolicy(left, right) {
11797
+ return JSON.stringify(left) === JSON.stringify(right);
11798
+ }
11799
+ function offeredAgentRef(payload) {
11800
+ if (!Object.prototype.hasOwnProperty.call(payload, "agentRef")) return void 0;
11801
+ return validateAgentRef(payload.agentRef);
11802
+ }
11803
+ function offeredSessionRef(payload) {
11804
+ if (!Object.prototype.hasOwnProperty.call(payload, "sessionRef")) return void 0;
11805
+ const value = payload.sessionRef;
11806
+ return typeof value === "string" ? value : void 0;
11807
+ }
11808
+ function errorMessage4(err) {
11809
+ return err instanceof Error ? err.message : String(err);
11810
+ }
11811
+ function raceSettleFirst(fn, timeoutMs) {
11812
+ return new Promise((resolve) => {
11813
+ let settled = false;
11814
+ const timer = setTimeout(() => {
11815
+ if (!settled) {
11816
+ settled = true;
11817
+ resolve(false);
11818
+ }
11819
+ }, timeoutMs);
11820
+ timer.unref?.();
11821
+ void (async () => {
11822
+ try {
11823
+ await fn();
11824
+ } catch {
11825
+ }
11826
+ if (!settled) {
11827
+ settled = true;
11828
+ clearTimeout(timer);
11829
+ resolve(true);
11830
+ }
11831
+ })();
11832
+ });
11833
+ }
11834
+ function estimateEventBytes(event) {
11835
+ try {
11836
+ return Buffer.byteLength(JSON.stringify(event), "utf8");
11837
+ } catch {
11838
+ return 0;
11839
+ }
11840
+ }
11841
+ async function openArtifact(workspaceDir, name) {
11842
+ const realWorkspaceDir = await promises.realpath(workspaceDir).catch(() => workspaceDir);
11843
+ const candidate = path3__default.resolve(realWorkspaceDir, name);
11844
+ const prefix = realWorkspaceDir.endsWith(path3__default.sep) ? realWorkspaceDir : realWorkspaceDir + path3__default.sep;
11845
+ if (candidate !== realWorkspaceDir && !candidate.startsWith(prefix)) {
11846
+ return { ok: false, reason: `artifact name "${name}" resolves outside the task workspace \u2014 rejected` };
11847
+ }
11848
+ let realCandidate = candidate;
11849
+ try {
11850
+ realCandidate = await promises.realpath(candidate);
11851
+ } catch {
11852
+ }
11853
+ if (realCandidate !== realWorkspaceDir && !realCandidate.startsWith(prefix)) {
11854
+ return { ok: false, reason: `artifact name "${name}" resolves outside the task workspace \u2014 rejected` };
11855
+ }
11856
+ const O_NOFOLLOW = constants.O_NOFOLLOW ?? 0;
11857
+ let handle;
11858
+ try {
11859
+ handle = await promises.open(candidate, constants.O_RDONLY | O_NOFOLLOW);
11860
+ } catch (err) {
11861
+ return { ok: false, reason: `artifact "${name}" could not be opened: ${errorMessage4(err)}` };
10688
11862
  }
10689
11863
  try {
10690
11864
  const st = await handle.stat();
@@ -10706,6 +11880,16 @@ var TaskRunner = class {
10706
11880
  }
10707
11881
  deps;
10708
11882
  tasks = /* @__PURE__ */ new Map();
11883
+ pendingMessageTasks = /* @__PURE__ */ new Map();
11884
+ messageContextByToken = /* @__PURE__ */ new Map();
11885
+ messageContextByTask = /* @__PURE__ */ new Map();
11886
+ memoryContextByToken = /* @__PURE__ */ new Map();
11887
+ memoryContextByTask = /* @__PURE__ */ new Map();
11888
+ memoryInFlightByTask = /* @__PURE__ */ new Map();
11889
+ memoryClosingTasks = /* @__PURE__ */ new Set();
11890
+ memoryFilesystemByTask = /* @__PURE__ */ new Map();
11891
+ recoveredMessageOutboxes = /* @__PURE__ */ new Map();
11892
+ recoveredMessageRetryTimers = /* @__PURE__ */ new Map();
10709
11893
  /**
10710
11894
  * Finding F4 (cancel lost during the offer-processing window): a
10711
11895
  * `task.cancel` for a taskId that hasn't finished `handleOffer` yet (still
@@ -10845,6 +12029,128 @@ var TaskRunner = class {
10845
12029
  pendingApprovals: (active.pendingApprovalId !== void 0 ? 1 : 0) + active.approvalQueue.length
10846
12030
  }));
10847
12031
  }
12032
+ /** Authenticated control-socket entry used only by the SDK-owned task MCP helper. */
12033
+ async publishAgentMessage(input) {
12034
+ const taskId = this.messageContextByToken.get(input.contextToken);
12035
+ if (taskId === void 0 || this.messageContextByTask.get(taskId) !== input.contextToken) {
12036
+ throw new Error("invalid or expired Agent message task context");
12037
+ }
12038
+ const pending = this.pendingMessageTasks.get(taskId);
12039
+ const active = this.tasks.get(taskId);
12040
+ const context = pending ?? (active?.messageRequirement && active.agentRef && active.messageOutbox ? { agentRef: active.agentRef, requirement: active.messageRequirement, outbox: active.messageOutbox } : void 0);
12041
+ if (context === void 0) throw new Error("task has no active required Agent message contract");
12042
+ const record = await context.outbox.appendDraft({
12043
+ taskId,
12044
+ tenantId: this.deps.tenantId,
12045
+ agentRef: context.agentRef,
12046
+ requirement: context.requirement,
12047
+ contentType: input.contentType,
12048
+ body: input.body,
12049
+ maxPendingEvents: 64,
12050
+ maxPendingBytes: 4 * 1024 * 1024
12051
+ });
12052
+ if (active === void 0) return { messageId: record.messageId, state: "staged" };
12053
+ const activated = await context.outbox.activate(taskId, active.session.sessionRef);
12054
+ if (activated === void 0) throw new Error("Agent message draft disappeared before activation");
12055
+ this.sendAgentMessageRecord(context.outbox, activated);
12056
+ return { messageId: activated.messageId, state: "pending" };
12057
+ }
12058
+ /** Authenticated control-socket entry used only by the SDK-owned memory MCP helper. */
12059
+ async recallAgentMemory(input) {
12060
+ return this.runMemoryOperation(input.contextToken, (context) => new AgentMemoryService(context).recall(input));
12061
+ }
12062
+ /** Authenticated control-socket entry used only by the SDK-owned memory MCP helper. */
12063
+ async saveAgentMemory(input) {
12064
+ return this.runMemoryOperation(input.contextToken, (context) => new AgentMemoryService(context).save(input));
12065
+ }
12066
+ /** Restore activated, unaccepted message drafts before transport admission on daemon restart. */
12067
+ async recoverAgentMessageOutboxes(agentsRoot) {
12068
+ if (this.deps.tenantId === void 0) throw new Error("Agent message recovery requires authenticated tenant enrollment");
12069
+ for (const outbox of await AgentMessageOutbox.recover(agentsRoot, this.deps.tenantId)) {
12070
+ for (const record of outbox.retryableRecords()) {
12071
+ if (record.sessionRef === void 0) continue;
12072
+ const existing = this.recoveredMessageOutboxes.get(record.taskId);
12073
+ if (existing !== void 0 && existing !== outbox) throw new Error(`multiple Agent message outboxes claim task ${record.taskId}`);
12074
+ this.recoveredMessageOutboxes.set(record.taskId, outbox);
12075
+ }
12076
+ }
12077
+ }
12078
+ /** Retry stable recovered records after a transport handshake/re-handshake. */
12079
+ retryRecoveredAgentMessages() {
12080
+ for (const [taskId, outbox] of this.recoveredMessageOutboxes) {
12081
+ const record = outbox.get(taskId);
12082
+ if (record?.sessionRef !== void 0) this.sendAgentMessageRecord(outbox, record);
12083
+ }
12084
+ }
12085
+ sendAgentMessageRecord(outbox, record) {
12086
+ this.deps.send(createEnvelope("agent.message.publish", outbox.publishPayload(record), {
12087
+ taskId: record.taskId,
12088
+ sessionRef: record.sessionRef
12089
+ }));
12090
+ const active = this.tasks.get(record.taskId);
12091
+ if (active?.messageOutbox === outbox) {
12092
+ if (active.messageRetryTimer !== void 0) clearTimeout(active.messageRetryTimer);
12093
+ active.messageRetryTimer = setTimeout(() => {
12094
+ active.messageRetryTimer = void 0;
12095
+ if (this.tasks.get(record.taskId) !== active) return;
12096
+ const pending = outbox.get(record.taskId);
12097
+ if (pending !== void 0) this.sendAgentMessageRecord(outbox, pending);
12098
+ }, 1e3);
12099
+ active.messageRetryTimer.unref?.();
12100
+ return;
12101
+ }
12102
+ if (this.recoveredMessageOutboxes.get(record.taskId) === outbox) {
12103
+ const prior = this.recoveredMessageRetryTimers.get(record.taskId);
12104
+ if (prior !== void 0) clearTimeout(prior);
12105
+ const timer = setTimeout(() => {
12106
+ this.recoveredMessageRetryTimers.delete(record.taskId);
12107
+ const pending = outbox.get(record.taskId);
12108
+ if (pending !== void 0 && this.recoveredMessageOutboxes.get(record.taskId) === outbox) {
12109
+ this.sendAgentMessageRecord(outbox, pending);
12110
+ }
12111
+ }, 1e3);
12112
+ timer.unref?.();
12113
+ this.recoveredMessageRetryTimers.set(record.taskId, timer);
12114
+ }
12115
+ }
12116
+ async handleAgentMessageDisposition(taskId, disposition) {
12117
+ const active = this.tasks.get(taskId);
12118
+ const outbox = active?.messageOutbox ?? this.recoveredMessageOutboxes.get(taskId);
12119
+ if (outbox === void 0) return;
12120
+ const outcome = await outbox.applyDisposition(taskId, disposition);
12121
+ if (active === void 0) {
12122
+ if (outcome === "accepted" || outcome === "held" || outcome === "refused") {
12123
+ const timer = this.recoveredMessageRetryTimers.get(taskId);
12124
+ if (timer !== void 0) clearTimeout(timer);
12125
+ this.recoveredMessageRetryTimers.delete(taskId);
12126
+ }
12127
+ if (outcome === "accepted") this.recoveredMessageOutboxes.delete(taskId);
12128
+ return;
12129
+ }
12130
+ if (outcome === "held") {
12131
+ if (active.messageRetryTimer !== void 0) clearTimeout(active.messageRetryTimer);
12132
+ active.messageRetryTimer = void 0;
12133
+ return;
12134
+ }
12135
+ if (outcome === "refused") {
12136
+ if (active.messageRetryTimer !== void 0) clearTimeout(active.messageRetryTimer);
12137
+ active.messageRetryTimer = void 0;
12138
+ this.revokeAgentMessageContext(taskId);
12139
+ return;
12140
+ }
12141
+ if (outcome !== "accepted") return;
12142
+ if (active.messageRetryTimer !== void 0) {
12143
+ clearTimeout(active.messageRetryTimer);
12144
+ active.messageRetryTimer = void 0;
12145
+ }
12146
+ active.messageAccepted = true;
12147
+ this.revokeAgentMessageContext(taskId);
12148
+ if (active.pendingMessageCompletion !== void 0) {
12149
+ const completion = active.pendingMessageCompletion;
12150
+ active.pendingMessageCompletion = void 0;
12151
+ await this.publishSuccessfulCompletion(active, completion.finalOutput, completion.document);
12152
+ }
12153
+ }
10848
12154
  /** M4 Phase 2: stop claiming any FUTURE `task.offer` — see `stoppingOffers`'s own doc comment. Idempotent. */
10849
12155
  stopAcceptingOffers() {
10850
12156
  this.stoppingOffers = true;
@@ -11020,6 +12326,9 @@ var TaskRunner = class {
11020
12326
  case "task.reject":
11021
12327
  await this.handleReject(envelope.task_id, envelope.payload.reason, envelope.payload.approvalId);
11022
12328
  return;
12329
+ case "agent.message.disposition":
12330
+ await this.handleAgentMessageDisposition(envelope.task_id, envelope.payload);
12331
+ return;
11023
12332
  default:
11024
12333
  return;
11025
12334
  }
@@ -11050,12 +12359,22 @@ var TaskRunner = class {
11050
12359
  this.decline(taskId, reason, retryable, agentRef);
11051
12360
  };
11052
12361
  const sessionRef = offeredSessionRef(payload);
12362
+ const messageRequirement = "messageEgress" in payload ? payload.messageEgress : void 0;
12363
+ const terminalProjection = "terminalProjection" in payload ? payload.terminalProjection : void 0;
11053
12364
  if ("egressPolicy" in payload) {
11054
12365
  if (this.deps.agentEgressPolicy === void 0 || !sameEgressPolicy(this.deps.agentEgressPolicy, payload.egressPolicy)) {
11055
12366
  decline("Agent egress offer policy is not exactly enabled by this daemon", false);
11056
12367
  return;
11057
12368
  }
11058
12369
  }
12370
+ if (messageRequirement !== void 0 && (agentRef === void 0 || this.deps.agentMessageMcpBin === void 0 || this.deps.tenantId === void 0)) {
12371
+ decline("required Agent message egress is unavailable on this daemon", false);
12372
+ return;
12373
+ }
12374
+ if (terminalProjection?.mode === "result-document" && this.deps.resultDocument === void 0) {
12375
+ decline("offer requires a result document but this daemon has no resultDocument extractor", false);
12376
+ return;
12377
+ }
11059
12378
  this.inFlightOffers.add(taskId);
11060
12379
  let agentBinding;
11061
12380
  let agentLeaseTransferred = false;
@@ -11114,11 +12433,29 @@ var TaskRunner = class {
11114
12433
  }
11115
12434
  const offered = withoutRequiredToolsets(payload);
11116
12435
  const requestedRuntime = payload.dispatchSelection?.runtimeId ?? payload.runtime;
11117
- const pick = await this.pickAdapter(requestedRuntime, payload.policy.mode, requiredToolsets !== void 0);
12436
+ const requiresAgentMemoryMcp = agentRef !== void 0 && this.deps.agentMemoryMcpBin !== void 0 && isAgentMemorySecureFilesystemAvailable(this.deps.agentMemoryFilesystemHelperBin !== void 0);
12437
+ const pick = await this.pickAdapter(
12438
+ requestedRuntime,
12439
+ payload.policy.mode,
12440
+ requiredToolsets !== void 0 || messageRequirement !== void 0 || requiresAgentMemoryMcp
12441
+ );
11118
12442
  if (!pick.ok) {
11119
12443
  decline(pick.reason, pick.retryable);
11120
12444
  return;
11121
12445
  }
12446
+ let taskMcpServers = this.withAgentMessageMcp(
12447
+ resolvedMcp?.ok ? resolvedMcp.servers : void 0,
12448
+ taskId,
12449
+ messageRequirement
12450
+ );
12451
+ if (requiresAgentMemoryMcp && agentRef !== void 0) {
12452
+ try {
12453
+ taskMcpServers = this.withAgentMemoryMcp(taskMcpServers, taskId, agentRef);
12454
+ } catch (error) {
12455
+ decline(`Agent memory MCP configuration failed: ${errorMessage4(error)}`, false);
12456
+ return;
12457
+ }
12458
+ }
11122
12459
  let prepared;
11123
12460
  try {
11124
12461
  prepared = await pick.adapter.prepare({
@@ -11126,7 +12463,7 @@ var TaskRunner = class {
11126
12463
  policy: decision.policy,
11127
12464
  descriptor: pick.descriptor,
11128
12465
  requiredToolsetIds: requiredToolsets ?? [],
11129
- ...resolvedMcp?.ok ? { mcpServers: resolvedMcp.servers } : {}
12466
+ ...taskMcpServers === void 0 ? {} : { mcpServers: taskMcpServers }
11130
12467
  });
11131
12468
  } catch (error) {
11132
12469
  decline(`runtime preparation failed: ${errorMessage4(error)}`, true);
@@ -11181,6 +12518,23 @@ var TaskRunner = class {
11181
12518
  decline(`Agent home initialization failed: ${errorMessage4(error)}`, false);
11182
12519
  return;
11183
12520
  }
12521
+ if (messageRequirement !== void 0) {
12522
+ try {
12523
+ const outbox = await AgentMessageOutbox.open(agentBinding.resolution.canonicalHome);
12524
+ this.pendingMessageTasks.set(taskId, {
12525
+ taskId,
12526
+ agentRef: agentBinding.resolution.agentRef,
12527
+ requirement: messageRequirement,
12528
+ outbox
12529
+ });
12530
+ } catch (error) {
12531
+ await agentBinding.lease.release().catch(() => {
12532
+ });
12533
+ agentBinding = void 0;
12534
+ decline(`Agent message outbox initialization failed: ${errorMessage4(error)}`, true);
12535
+ return;
12536
+ }
12537
+ }
11184
12538
  } else if (this.deps.gitWorkspaceManager && this.deps.gitWorkspaceStore) {
11185
12539
  known = sessionRef ? await this.deps.sessionWorkspaces.get(sessionRef) : void 0;
11186
12540
  const gitManager = this.deps.gitWorkspaceManager;
@@ -11364,9 +12718,9 @@ var TaskRunner = class {
11364
12718
  }
11365
12719
  const startInput = {
11366
12720
  manifest,
11367
- instruction: gitWorkspaceId ? prependGitWorkspaceGuidance(resolvedInstruction) : resolvedInstruction,
12721
+ instruction: agentBinding === void 0 ? gitWorkspaceId ? prependGitWorkspaceGuidance(resolvedInstruction) : resolvedInstruction : prependAgentMemoryGuidance(resolvedInstruction),
11368
12722
  env,
11369
- ...resolvedMcp?.ok ? { mcpServers: resolvedMcp.servers } : {},
12723
+ ...taskMcpServers === void 0 ? {} : { mcpServers: taskMcpServers },
11370
12724
  approvalChannel: {
11371
12725
  taskId,
11372
12726
  storeDir: this.deps.storeDir,
@@ -11436,7 +12790,12 @@ var TaskRunner = class {
11436
12790
  ),
11437
12791
  approvalQueue: [],
11438
12792
  outputBytesSoFar: 0,
11439
- startedAtMs: Date.now()
12793
+ startedAtMs: Date.now(),
12794
+ ...messageRequirement === void 0 ? {} : {
12795
+ messageRequirement,
12796
+ messageOutbox: this.pendingMessageTasks.get(taskId).outbox
12797
+ },
12798
+ ...terminalProjection === void 0 ? {} : { terminalProjection }
11440
12799
  };
11441
12800
  if (agentBinding !== void 0) {
11442
12801
  try {
@@ -11464,6 +12823,7 @@ var TaskRunner = class {
11464
12823
  return;
11465
12824
  }
11466
12825
  }
12826
+ const activatedMessageRecord = active.messageOutbox === void 0 ? void 0 : await active.messageOutbox.activate(taskId, session.sessionRef);
11467
12827
  if (this.pendingCancelled.has(taskId)) {
11468
12828
  const reason = this.pendingCancelled.get(taskId);
11469
12829
  this.pendingCancelled.delete(taskId);
@@ -11489,6 +12849,10 @@ var TaskRunner = class {
11489
12849
  this.deps.send(createEnvelope("task.started", {}, { taskId }));
11490
12850
  agentLeaseTransferred = agentBinding !== void 0;
11491
12851
  this.tasks.set(taskId, active);
12852
+ this.pendingMessageTasks.delete(taskId);
12853
+ if (active.messageOutbox !== void 0 && activatedMessageRecord !== void 0) {
12854
+ this.sendAgentMessageRecord(active.messageOutbox, activatedMessageRecord);
12855
+ }
11492
12856
  if (payload.limits?.maxDurationMs !== void 0) {
11493
12857
  this.armMaxDurationTimer(active, payload.limits.maxDurationMs);
11494
12858
  }
@@ -11506,6 +12870,11 @@ var TaskRunner = class {
11506
12870
  });
11507
12871
  }
11508
12872
  } finally {
12873
+ if (!agentLeaseTransferred) {
12874
+ this.pendingMessageTasks.delete(taskId);
12875
+ this.revokeAgentMessageContext(taskId);
12876
+ this.revokeAgentMemoryContext(taskId);
12877
+ }
11509
12878
  if (agentBinding !== void 0 && !agentLeaseTransferred) {
11510
12879
  await agentBinding.lease.release().catch(() => {
11511
12880
  });
@@ -11513,6 +12882,153 @@ var TaskRunner = class {
11513
12882
  this.inFlightOffers.delete(taskId);
11514
12883
  }
11515
12884
  }
12885
+ withAgentMessageMcp(existing, taskId, requirement) {
12886
+ if (requirement === void 0) return existing;
12887
+ const bin = this.deps.agentMessageMcpBin;
12888
+ const contextToken = `${randomUUID()}.${randomUUID()}`;
12889
+ this.revokeAgentMessageContext(taskId);
12890
+ this.messageContextByToken.set(contextToken, taskId);
12891
+ this.messageContextByTask.set(taskId, contextToken);
12892
+ return Object.freeze({
12893
+ ...existing ?? {},
12894
+ [AGENT_MESSAGE_MCP_SERVER_NAME]: Object.freeze({
12895
+ command: bin.command,
12896
+ args: Object.freeze([...bin.args]),
12897
+ env: Object.freeze({
12898
+ BYOK_STORE_DIR: this.deps.storeDir,
12899
+ BYOK_PRODUCT_ID: this.deps.productId,
12900
+ BYOK_AGENT_MESSAGE_CONTEXT: contextToken
12901
+ })
12902
+ })
12903
+ });
12904
+ }
12905
+ revokeAgentMessageContext(taskId) {
12906
+ const token = this.messageContextByTask.get(taskId);
12907
+ if (token !== void 0) this.messageContextByToken.delete(token);
12908
+ this.messageContextByTask.delete(taskId);
12909
+ }
12910
+ /** Injected only after strict Agent admission; a host registry may never replace this reserved name. */
12911
+ withAgentMemoryMcp(existing, taskId, agentRef) {
12912
+ if (existing !== void 0 && Object.prototype.hasOwnProperty.call(existing, AGENT_MEMORY_MCP_SERVER_NAME)) {
12913
+ throw new Error(`MCP server name "${AGENT_MEMORY_MCP_SERVER_NAME}" is reserved by the daemon`);
12914
+ }
12915
+ const bin = this.deps.agentMemoryMcpBin;
12916
+ const contextToken = `${randomUUID()}.${randomUUID()}`;
12917
+ this.revokeAgentMemoryContext(taskId);
12918
+ this.memoryContextByToken.set(contextToken, Object.freeze({ taskId, agentRef: Object.freeze({ ...agentRef }) }));
12919
+ this.memoryContextByTask.set(taskId, contextToken);
12920
+ return Object.freeze({
12921
+ ...existing ?? {},
12922
+ [AGENT_MEMORY_MCP_SERVER_NAME]: Object.freeze({
12923
+ command: bin.command,
12924
+ args: Object.freeze([...bin.args]),
12925
+ env: Object.freeze({
12926
+ BYOK_STORE_DIR: this.deps.storeDir,
12927
+ BYOK_PRODUCT_ID: this.deps.productId,
12928
+ BYOK_AGENT_MEMORY_CONTEXT: contextToken
12929
+ })
12930
+ })
12931
+ });
12932
+ }
12933
+ /** Reconstruct all sensitive context from the active sealed task, never from MCP/model arguments. */
12934
+ activeMemoryContext(contextToken) {
12935
+ const registered = this.memoryContextByToken.get(contextToken);
12936
+ if (registered === void 0 || this.memoryContextByTask.get(registered.taskId) !== contextToken || this.memoryClosingTasks.has(registered.taskId)) {
12937
+ throw new Error("invalid, expired, or quiescing Agent memory task context");
12938
+ }
12939
+ const active = this.tasks.get(registered.taskId);
12940
+ if (active === void 0 || active.finalizationStarted || active.agentBinding === void 0 || active.agentRef === void 0 || active.agentHandoff === void 0 || this.deps.tenantId === void 0 || active.agentRef.agentId !== registered.agentRef.agentId || active.agentRef.profileRevision !== registered.agentRef.profileRevision || active.agentBinding.resolution.agentRef.agentId !== registered.agentRef.agentId || active.agentBinding.resolution.agentRef.profileRevision !== registered.agentRef.profileRevision || active.agentHandoff.sessionRef !== active.session.sessionRef || active.agentHandoff.runtimeId !== active.adapter.descriptor.id || active.agentHandoff.cwd !== active.agentBinding.lease.cwd || active.agentBinding.lease.canonicalHome !== active.agentBinding.resolution.canonicalHome) {
12941
+ throw new Error("Agent memory context no longer matches its exact active task identity");
12942
+ }
12943
+ return Object.freeze({
12944
+ taskId: registered.taskId,
12945
+ tenantId: this.deps.tenantId,
12946
+ deviceId: this.deps.deviceId,
12947
+ agentRef: Object.freeze({ ...active.agentRef }),
12948
+ sessionRef: active.session.sessionRef,
12949
+ runtimeId: active.adapter.descriptor.id,
12950
+ canonicalHome: active.agentBinding.resolution.canonicalHome,
12951
+ leaseId: active.agentBinding.lease.leaseId,
12952
+ homeIdentity: active.agentBinding.lease.homeIdentity
12953
+ });
12954
+ }
12955
+ async runMemoryOperation(contextToken, operation) {
12956
+ let context = this.activeMemoryContext(contextToken);
12957
+ context = await this.bindAgentMemoryFilesystem(context);
12958
+ const current = this.activeMemoryContext(contextToken);
12959
+ if (current.taskId !== context.taskId || current.leaseId !== context.leaseId) throw new Error("Agent memory context changed during secure filesystem handshake");
12960
+ context = Object.freeze({ ...current, ...context.filesystem === void 0 ? {} : { filesystem: context.filesystem } });
12961
+ const pending = this.memoryInFlightByTask.get(context.taskId) ?? /* @__PURE__ */ new Set();
12962
+ this.memoryInFlightByTask.set(context.taskId, pending);
12963
+ const result = operation(context);
12964
+ pending.add(result);
12965
+ try {
12966
+ return await result;
12967
+ } finally {
12968
+ pending.delete(result);
12969
+ if (pending.size === 0) this.memoryInFlightByTask.delete(context.taskId);
12970
+ }
12971
+ }
12972
+ async quiesceAndSnapshotAgentMemory(active) {
12973
+ if (active.agentBinding === void 0 || active.agentRef === void 0 || active.agentHandoff === void 0) return;
12974
+ if (!isAgentMemorySecureFilesystemAvailable(this.deps.agentMemoryFilesystemHelperBin !== void 0)) return;
12975
+ if (this.deps.tenantId === void 0) {
12976
+ console.error(`[byok/client] Agent memory quiescent snapshot skipped for ${active.taskId}: exact tenant context is unavailable`);
12977
+ return;
12978
+ }
12979
+ this.memoryClosingTasks.add(active.taskId);
12980
+ try {
12981
+ await Promise.allSettled([...this.memoryInFlightByTask.get(active.taskId) ?? []]);
12982
+ const context = await this.bindAgentMemoryFilesystem({
12983
+ taskId: active.taskId,
12984
+ tenantId: this.deps.tenantId,
12985
+ deviceId: this.deps.deviceId,
12986
+ agentRef: active.agentRef,
12987
+ sessionRef: active.agentHandoff.sessionRef,
12988
+ runtimeId: active.agentHandoff.runtimeId,
12989
+ canonicalHome: active.agentBinding.resolution.canonicalHome,
12990
+ leaseId: active.agentBinding.lease.leaseId,
12991
+ homeIdentity: active.agentBinding.lease.homeIdentity
12992
+ });
12993
+ await snapshotAndProjectAgentMemory(context, this.deps.agentMemoryHostedProjection);
12994
+ } catch (error) {
12995
+ console.error(`[byok/client] Agent memory quiescent snapshot failed for ${active.taskId}: ${errorMessage4(error)}`);
12996
+ } finally {
12997
+ await this.closeAgentMemoryFilesystem(active.taskId);
12998
+ }
12999
+ }
13000
+ async bindAgentMemoryFilesystem(context) {
13001
+ const helperBin = this.deps.agentMemoryFilesystemHelperBin;
13002
+ if (helperBin === void 0) return context;
13003
+ let filesystem = this.memoryFilesystemByTask.get(context.taskId);
13004
+ if (filesystem === void 0) {
13005
+ filesystem = openAgentMemoryFilesystemHelper({
13006
+ helperBin,
13007
+ canonicalHome: context.canonicalHome,
13008
+ homeIdentity: context.homeIdentity
13009
+ });
13010
+ this.memoryFilesystemByTask.set(context.taskId, filesystem);
13011
+ void filesystem.catch(() => {
13012
+ if (this.memoryFilesystemByTask.get(context.taskId) === filesystem) this.memoryFilesystemByTask.delete(context.taskId);
13013
+ });
13014
+ }
13015
+ return Object.freeze({ ...context, filesystem: await filesystem });
13016
+ }
13017
+ async closeAgentMemoryFilesystem(taskId) {
13018
+ const filesystem = this.memoryFilesystemByTask.get(taskId);
13019
+ this.memoryFilesystemByTask.delete(taskId);
13020
+ if (filesystem === void 0) return;
13021
+ await filesystem.then((authority) => authority.close()).catch(() => {
13022
+ });
13023
+ }
13024
+ revokeAgentMemoryContext(taskId) {
13025
+ const token = this.memoryContextByTask.get(taskId);
13026
+ if (token !== void 0) this.memoryContextByToken.delete(token);
13027
+ this.memoryContextByTask.delete(taskId);
13028
+ this.memoryInFlightByTask.delete(taskId);
13029
+ this.memoryClosingTasks.delete(taskId);
13030
+ void this.closeAgentMemoryFilesystem(taskId);
13031
+ }
11516
13032
  /** Protocol §7: an instruction too large to inline arrives as a `blobRef` — resolve it via the blob client rather than failing closed. */
11517
13033
  async resolveInstruction(instruction) {
11518
13034
  if (typeof instruction === "string") return instruction;
@@ -11590,42 +13106,13 @@ var TaskRunner = class {
11590
13106
  if (!outcome.deliver) return;
11591
13107
  await this.observeGit(active, "completed");
11592
13108
  if (this.tasks.get(active.taskId) !== active || active.beingTornDown) return;
11593
- if (outcome.document !== void 0 && !this.hasResultDocumentCapability()) {
11594
- await this.fail(
11595
- active.taskId,
11596
- `${RESULT_DOCUMENT_UNDELIVERABLE_REASON_PREFIX}: the connected server stopped advertising the result-document capability before this completion could be sent (a reconnect to an older server), so it would silently discard this document`,
11597
- false
11598
- );
13109
+ if (active.messageRequirement !== void 0 && active.messageAccepted !== true) {
13110
+ active.pendingMessageCompletion = { finalOutput, ...outcome.document === void 0 ? {} : { document: outcome.document } };
13111
+ const record = active.messageOutbox?.get(active.taskId);
13112
+ if (record !== void 0) this.sendAgentMessageRecord(active.messageOutbox, record);
11599
13113
  return;
11600
13114
  }
11601
- if (!this.reserveSemanticTerminal(active)) return;
11602
- await this.persistAgentTerminalEvidence(active, "complete");
11603
- this.deps.send(
11604
- createEnvelope(
11605
- "task.complete",
11606
- {
11607
- summary: finalOutput,
11608
- sessionRef: active.session.sessionRef,
11609
- // Spread rather than `document: outcome.document`, so a
11610
- // completion with no document is the exact same payload it
11611
- // was before this field existed — not one carrying an
11612
- // explicit `document: undefined` key.
11613
- //
11614
- // `outcome.document` is the protocol's CANONICAL SNAPSHOT
11615
- // (`checkResultDocument`), never the object the extractor
11616
- // returned: pure data serializes identically at the root
11617
- // (where it was measured) and nested inside this payload
11618
- // (where the codec actually serializes it), so a contextual
11619
- // `toJSON(key)` or an unstable getter cannot make the wire
11620
- // bytes differ from what the cap gate approved.
11621
- ...outcome.document !== void 0 ? { document: outcome.document } : {},
11622
- ...this.terminalInferenceUsagePayload(active),
11623
- ...this.agentTerminalPayload(active)
11624
- },
11625
- { taskId: active.taskId, sessionRef: active.session.sessionRef }
11626
- )
11627
- );
11628
- await this.finish(active.taskId);
13115
+ await this.publishSuccessfulCompletion(active, finalOutput, outcome.document);
11629
13116
  return;
11630
13117
  }
11631
13118
  if (event.type === "progress") {
@@ -11661,6 +13148,26 @@ var TaskRunner = class {
11661
13148
  await this.fail(active.taskId, failure.reason, failure.retryable);
11662
13149
  }
11663
13150
  }
13151
+ async publishSuccessfulCompletion(active, finalOutput, document) {
13152
+ if (document !== void 0 && !this.hasResultDocumentCapability()) {
13153
+ await this.fail(
13154
+ active.taskId,
13155
+ `${RESULT_DOCUMENT_UNDELIVERABLE_REASON_PREFIX}: the connected server stopped advertising the result-document capability before this completion could be sent (a reconnect to an older server), so it would silently discard this document`,
13156
+ false
13157
+ );
13158
+ return;
13159
+ }
13160
+ if (!this.reserveSemanticTerminal(active)) return;
13161
+ await this.persistAgentTerminalEvidence(active, "complete");
13162
+ this.deps.send(createEnvelope("task.complete", {
13163
+ summary: finalOutput,
13164
+ sessionRef: active.session.sessionRef,
13165
+ ...document !== void 0 ? { document } : {},
13166
+ ...this.terminalInferenceUsagePayload(active),
13167
+ ...this.agentTerminalPayload(active)
13168
+ }, { taskId: active.taskId, sessionRef: active.session.sessionRef }));
13169
+ await this.finish(active.taskId);
13170
+ }
11664
13171
  /**
11665
13172
  * Protocol §7: an `artifact` `AgentEvent` only names a file the runtime
11666
13173
  * wrote into the task workspace (`name`/`contentType` — it carries no
@@ -12343,11 +13850,26 @@ var TaskRunner = class {
12343
13850
  * docs/protocol.md §7.2.
12344
13851
  */
12345
13852
  async resolveResultDocument(active, finalOutput) {
13853
+ if (active.terminalProjection?.mode === "none" || active.messageRequirement !== void 0 && active.terminalProjection === void 0) return { deliver: true };
12346
13854
  const extract = this.deps.resultDocument?.extract;
12347
- if (!extract) return { deliver: true };
13855
+ if (!extract) {
13856
+ if (active.terminalProjection?.mode === "result-document") {
13857
+ await this.fail(
13858
+ active.taskId,
13859
+ `${RESULT_DOCUMENT_UNDELIVERABLE_REASON_PREFIX}: the offer requires result contract ${active.terminalProjection.contract} but this daemon has no extractor`,
13860
+ false
13861
+ );
13862
+ return { deliver: false };
13863
+ }
13864
+ return { deliver: true };
13865
+ }
12348
13866
  let document;
12349
13867
  try {
12350
- document = extract(finalOutput, { taskId: active.taskId, sessionRef: active.session.sessionRef });
13868
+ document = extract(finalOutput, {
13869
+ taskId: active.taskId,
13870
+ sessionRef: active.session.sessionRef,
13871
+ ...active.terminalProjection === void 0 ? {} : { terminalProjection: active.terminalProjection }
13872
+ });
12351
13873
  } catch (err) {
12352
13874
  await this.fail(
12353
13875
  active.taskId,
@@ -12364,7 +13886,17 @@ var TaskRunner = class {
12364
13886
  );
12365
13887
  return { deliver: false };
12366
13888
  }
12367
- if (document === void 0) return { deliver: true };
13889
+ if (document === void 0) {
13890
+ if (active.terminalProjection?.mode === "result-document") {
13891
+ await this.fail(
13892
+ active.taskId,
13893
+ `${RESULT_DOCUMENT_UNDELIVERABLE_REASON_PREFIX}: the required result contract ${active.terminalProjection.contract} produced no document`,
13894
+ false
13895
+ );
13896
+ return { deliver: false };
13897
+ }
13898
+ return { deliver: true };
13899
+ }
12368
13900
  const check = checkResultDocument(document);
12369
13901
  if (!check.ok) {
12370
13902
  const detail = resultDocumentRejectionDetail(check);
@@ -12498,6 +14030,10 @@ var TaskRunner = class {
12498
14030
  clearTimeout(active.maxDurationTimer);
12499
14031
  active.maxDurationTimer = void 0;
12500
14032
  }
14033
+ if (active.messageRetryTimer) {
14034
+ clearTimeout(active.messageRetryTimer);
14035
+ active.messageRetryTimer = void 0;
14036
+ }
12501
14037
  active.batcher.stop();
12502
14038
  this.addFinishedTaskId(taskId);
12503
14039
  const queued = active.approvalQueue.splice(0);
@@ -12538,6 +14074,7 @@ var TaskRunner = class {
12538
14074
  if (active.agentBinding !== void 0 && active.agentRef !== void 0 && active.agentHandoff !== void 0) {
12539
14075
  const agentRef = active.agentRef;
12540
14076
  const handoff = active.agentHandoff;
14077
+ await this.quiesceAndSnapshotAgentMemory(active);
12541
14078
  if (!active.agentTerminalPersisted) {
12542
14079
  const cause = active.terminalCause ?? "failed";
12543
14080
  const result = await this.retryAgentTerminalEvidence(
@@ -12576,11 +14113,15 @@ var TaskRunner = class {
12576
14113
  }
12577
14114
  active.gitLease?.release();
12578
14115
  this.tasks.delete(taskId);
14116
+ this.revokeAgentMessageContext(taskId);
14117
+ this.revokeAgentMemoryContext(taskId);
12579
14118
  active.resolveSemanticTerminalSettled?.(leaseReleased);
12580
14119
  return leaseReleased;
12581
14120
  }
12582
14121
  active.gitLease?.release();
12583
14122
  this.tasks.delete(taskId);
14123
+ this.revokeAgentMessageContext(taskId);
14124
+ this.revokeAgentMemoryContext(taskId);
12584
14125
  active.resolveSemanticTerminalSettled?.(true);
12585
14126
  return true;
12586
14127
  }
@@ -12744,9 +14285,9 @@ function metadataStatusEvent(event) {
12744
14285
  return { ...event };
12745
14286
  }
12746
14287
  }
12747
- var encoder2 = new TextEncoder();
14288
+ var encoder3 = new TextEncoder();
12748
14289
  function eventBytes(event) {
12749
- return encoder2.encode(JSON.stringify(event)).length;
14290
+ return encoder3.encode(JSON.stringify(event)).length;
12750
14291
  }
12751
14292
  var AGENT_EGRESS_DIRECTORY = path3__default.join(".byok", "egress");
12752
14293
  var AGENT_RELIABLE_SPOOL_FILENAME = "reliable-v1.jsonl";
@@ -12764,7 +14305,7 @@ var AgentReliableQuotaError = class extends AgentReliableSpoolError {
12764
14305
  }
12765
14306
  reason;
12766
14307
  };
12767
- function sameAgentRef(left, right) {
14308
+ function sameAgentRef2(left, right) {
12768
14309
  return left.agentId === right.agentId && left.profileRevision === right.profileRevision;
12769
14310
  }
12770
14311
  function stableRecordJson(value) {
@@ -12797,7 +14338,7 @@ function assertWireType(value) {
12797
14338
  }
12798
14339
  }
12799
14340
  function sameRecord(record, candidate) {
12800
- return record.wireType === candidate.wireType && sameAgentRef(record.agentRef, candidate.agentRef) && record.tenantId === candidate.tenantId && record.policyRevision === candidate.policyRevision && record.eventId === candidate.eventId && record.cursor === candidate.cursor && record.payloadHash === candidate.payloadHash && record.byteCount === candidate.byteCount && record.sessionRef === candidate.sessionRef && record.taskId === candidate.taskId;
14341
+ return record.wireType === candidate.wireType && sameAgentRef2(record.agentRef, candidate.agentRef) && record.tenantId === candidate.tenantId && record.policyRevision === candidate.policyRevision && record.eventId === candidate.eventId && record.cursor === candidate.cursor && record.payloadHash === candidate.payloadHash && record.byteCount === candidate.byteCount && record.sessionRef === candidate.sessionRef && record.taskId === candidate.taskId;
12801
14342
  }
12802
14343
  function parseEntry2(value) {
12803
14344
  if (typeof value !== "object" || value === null || Array.isArray(value)) throw new AgentReliableSpoolError("reliable spool entry is not an object");
@@ -12984,7 +14525,7 @@ var AgentReliableSpool = class _AgentReliableSpool {
12984
14525
  return this.exclusive(async () => {
12985
14526
  const record = this.pending.get(ack.eventId);
12986
14527
  if (!record) return false;
12987
- if (record.cursor !== ack.cursor || record.tenantId !== ack.tenantId || record.sessionRef !== ack.sessionRef || record.policyRevision !== ack.policyRevision || !sameAgentRef(record.agentRef, ack.agentRef)) return false;
14528
+ if (record.cursor !== ack.cursor || record.tenantId !== ack.tenantId || record.sessionRef !== ack.sessionRef || record.policyRevision !== ack.policyRevision || !sameAgentRef2(record.agentRef, ack.agentRef)) return false;
12988
14529
  await this.appendEntry({ schema: 1, kind: "ack", ack: Object.freeze({ ...ack, agentRef: { ...ack.agentRef } }) });
12989
14530
  this.pending.delete(record.eventId);
12990
14531
  if (this.logEntries >= 512) await this.compact();
@@ -13017,7 +14558,7 @@ var AgentReliableSpool = class _AgentReliableSpool {
13017
14558
  } else {
13018
14559
  const record = this.pending.get(entry.ack.eventId);
13019
14560
  if (!record) throw new AgentReliableSpoolError("reliable spool acknowledges an unknown event");
13020
- if (record.cursor !== entry.ack.cursor || record.tenantId !== entry.ack.tenantId || record.sessionRef !== entry.ack.sessionRef || record.policyRevision !== entry.ack.policyRevision || !sameAgentRef(record.agentRef, entry.ack.agentRef)) throw new AgentReliableSpoolError("reliable spool contains a mismatched ack");
14561
+ if (record.cursor !== entry.ack.cursor || record.tenantId !== entry.ack.tenantId || record.sessionRef !== entry.ack.sessionRef || record.policyRevision !== entry.ack.policyRevision || !sameAgentRef2(record.agentRef, entry.ack.agentRef)) throw new AgentReliableSpoolError("reliable spool contains a mismatched ack");
13021
14562
  this.pending.delete(record.eventId);
13022
14563
  }
13023
14564
  }
@@ -13679,7 +15220,7 @@ var AgentHomeProjectionCompletionError = class extends Error {
13679
15220
  this.name = "AgentHomeProjectionCompletionError";
13680
15221
  }
13681
15222
  };
13682
- function sameAgentRef2(left, right) {
15223
+ function sameAgentRef3(left, right) {
13683
15224
  return left.agentId === right.agentId && left.profileRevision === right.profileRevision;
13684
15225
  }
13685
15226
  var AgentHomeProjectionCompletionClient = class {
@@ -13722,7 +15263,7 @@ var AgentHomeProjectionCompletionClient = class {
13722
15263
  cause: error
13723
15264
  });
13724
15265
  }
13725
- if (readback.tenantId !== this.options.tenantId || readback.deviceId !== this.options.deviceId || readback.requestId !== completion.requestId || !sameAgentRef2(readback.agentRef, completion.agentRef) || readback.projectionHash !== completion.projectionHash || readback.status !== completion.outcome || readback.completedAt === void 0) {
15266
+ if (readback.tenantId !== this.options.tenantId || readback.deviceId !== this.options.deviceId || readback.requestId !== completion.requestId || !sameAgentRef3(readback.agentRef, completion.agentRef) || readback.projectionHash !== completion.projectionHash || readback.status !== completion.outcome || readback.completedAt === void 0) {
13726
15267
  throw new AgentHomeProjectionCompletionError(
13727
15268
  "Agent-home projection completion readback does not exactly match the authenticated request"
13728
15269
  );
@@ -13730,6 +15271,15 @@ var AgentHomeProjectionCompletionClient = class {
13730
15271
  return readback;
13731
15272
  }
13732
15273
  };
15274
+ function resolveAgentMessageMcpBin() {
15275
+ const script = path3__default.join(path3__default.dirname(fileURLToPath(import.meta.url)), "bin", "byok-agent-message-mcp.js");
15276
+ return Object.freeze({ command: process.execPath, args: Object.freeze([script]) });
15277
+ }
15278
+ function resolveAgentMemoryMcpBin(externalHelperConfigured = false) {
15279
+ if (!isAgentMemorySecureFilesystemAvailable(externalHelperConfigured)) return void 0;
15280
+ const script = path3__default.join(path3__default.dirname(fileURLToPath(import.meta.url)), "bin", "byok-agent-memory-mcp.js");
15281
+ return Object.freeze({ command: process.execPath, args: Object.freeze([script]) });
15282
+ }
13733
15283
  var AGENT_CONTENT_READ_SURFACES = ["workspace", "transcript", "artifact"];
13734
15284
  var AGENT_CONTENT_READ_CAPABILITIES = Object.freeze({
13735
15285
  workspace: AGENT_CONTENT_WORKSPACE_READ_CAPABILITY,
@@ -13961,11 +15511,11 @@ var RootPolicyError = class extends Error {
13961
15511
  this.reason = reason;
13962
15512
  }
13963
15513
  };
13964
- function sameAgentRef3(left, right) {
15514
+ function sameAgentRef4(left, right) {
13965
15515
  return left.agentId === right.agentId && left.profileRevision === right.profileRevision;
13966
15516
  }
13967
15517
  function sameSessionIdentity(left, right) {
13968
- return sameAgentRef3(left.agentRef, right.agentRef) && left.sessionRef === right.sessionRef && left.runtimeId === right.runtimeId && left.cwd === right.cwd;
15518
+ return sameAgentRef4(left.agentRef, right.agentRef) && left.sessionRef === right.sessionRef && left.runtimeId === right.runtimeId && left.cwd === right.cwd;
13969
15519
  }
13970
15520
  function validateRequest(request) {
13971
15521
  if (!isRecord5(request)) throw new AgentContentReadRequestError("content read request must be an object");
@@ -14237,7 +15787,7 @@ var AgentContentReadPolicyEngine = class {
14237
15787
  }
14238
15788
  async checkSessionIdentity(request, resolver, requiredCwd) {
14239
15789
  const session = request.session;
14240
- if (session === void 0 || !sameAgentRef3(session.agentRef, request.agentRef) || requiredCwd !== void 0 && session.cwd !== requiredCwd) {
15790
+ if (session === void 0 || !sameAgentRef4(session.agentRef, request.agentRef) || requiredCwd !== void 0 && session.cwd !== requiredCwd) {
14241
15791
  return "identity-mismatch";
14242
15792
  }
14243
15793
  let expected;
@@ -14358,6 +15908,7 @@ function computeCapabilities(adapters, agentHomeConfigured = false, strictAgentO
14358
15908
  if (adapters.some((adapter) => adapter.descriptor.capabilities.steer)) flags.push("steer");
14359
15909
  flags.push("blob-upload");
14360
15910
  flags.push("approval-targeting");
15911
+ flags.push(TERMINAL_PROJECTION_SELECTION_CAPABILITY);
14361
15912
  const selectionAdapters = adapters.filter(
14362
15913
  (adapter) => ALL_RUNTIME_IDS.includes(adapter.descriptor.id)
14363
15914
  );
@@ -14376,6 +15927,9 @@ function computeCapabilities(adapters, agentHomeConfigured = false, strictAgentO
14376
15927
  AGENT_EGRESS_RELIABLE_ACK_CAPABILITY,
14377
15928
  AGENT_EGRESS_FRESH_SESSION_CAPABILITY
14378
15929
  );
15930
+ if (agentHomeConfigured && adapters.some((adapter) => adapter.descriptor.capabilities.mcpToolsets === true)) {
15931
+ flags.push(AGENT_MESSAGE_EGRESS_CAPABILITY);
15932
+ }
14379
15933
  }
14380
15934
  if (contentReadPolicies !== void 0) {
14381
15935
  for (const surface of Object.keys(AGENT_CONTENT_READ_CAPABILITIES)) {
@@ -14486,6 +16040,23 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
14486
16040
  "DaemonConfig.agentHome and DaemonConfig.gitWorkspace are mutually exclusive; Agent home is the only workspace authority for Agent offers"
14487
16041
  );
14488
16042
  }
16043
+ if (config.agentMemory !== void 0 && config.agentHome === void 0) {
16044
+ throw new Error("DaemonConfig.agentMemory requires DaemonConfig.agentHome for the exact Agent memory authority");
16045
+ }
16046
+ if (config.agentMemoryFilesystem !== void 0 && config.agentHome === void 0) {
16047
+ throw new Error("DaemonConfig.agentMemoryFilesystem requires DaemonConfig.agentHome for the exact Agent memory authority");
16048
+ }
16049
+ if (config.agentMemoryFilesystem !== void 0 && (!path3__default.isAbsolute(config.agentMemoryFilesystem.helperBin) || /[\u0000\r\n]/u.test(config.agentMemoryFilesystem.helperBin))) {
16050
+ throw new Error("DaemonConfig.agentMemoryFilesystem.helperBin must be an explicit absolute executable path");
16051
+ }
16052
+ const externalAgentMemoryFilesystem = config.agentMemoryFilesystem !== void 0;
16053
+ if (externalAgentMemoryFilesystem && !isAgentMemoryFilesystemHelperSupported()) {
16054
+ throw new Error("DaemonConfig.agentMemoryFilesystem is not admitted on this platform without its native race proof");
16055
+ }
16056
+ if (config.agentMemory !== void 0 && !isAgentMemorySecureFilesystemAvailable(externalAgentMemoryFilesystem)) {
16057
+ throw new Error("DaemonConfig.agentMemory requires a platform with safe descriptor-relative filesystem operations");
16058
+ }
16059
+ const agentMemoryMcpBin = config.agentHome === void 0 ? void 0 : resolveAgentMemoryMcpBin(externalAgentMemoryFilesystem);
14489
16060
  const localAgentRelease = resolveLocalAgentReleaseIdentity(config.localAgentRelease);
14490
16061
  const toolsetRegistry = new McpToolsetRegistry(config.mcpToolsets);
14491
16062
  validatePiByokLauncherConfig(config.piByokLauncher);
@@ -14917,6 +16488,7 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
14917
16488
  approvalRegistry,
14918
16489
  storeDir,
14919
16490
  productId: config.productId,
16491
+ tenantId: record.tenantId,
14920
16492
  // U2 terminal inference usage consumes the one U4a-resolved,
14921
16493
  // process-immutable identity captured above. This is composition-only:
14922
16494
  // TaskRunner receives no config, manifest, or second identity resolver.
@@ -14931,6 +16503,10 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
14931
16503
  // doc comment. Spread rather than assigned so an unconfigured daemon
14932
16504
  // builds the exact `deps` object it did before this seam existed.
14933
16505
  ...config.resultDocument ? { resultDocument: config.resultDocument } : {},
16506
+ ...agentMemoryMcpBin === void 0 ? {} : { agentMemoryMcpBin },
16507
+ ...config.agentMemoryFilesystem === void 0 ? {} : { agentMemoryFilesystemHelperBin: path3__default.resolve(config.agentMemoryFilesystem.helperBin) },
16508
+ ...config.agentHome !== void 0 && config.agentEgress !== void 0 ? { agentMessageMcpBin: resolveAgentMessageMcpBin() } : {},
16509
+ ...config.agentMemory === void 0 ? {} : { agentMemoryHostedProjection: config.agentMemory },
14934
16510
  // M4 Phase 3 hardening: bridges TaskRunner's stale-approval-race
14935
16511
  // finding out to the SAME local observability seam every other
14936
16512
  // daemon-local event already uses (see observer.ts's own module doc
@@ -14961,6 +16537,9 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
14961
16537
  ...activePressureEngine ? { admissionGuard: () => activePressureEngine.admissionGuard() } : {}
14962
16538
  };
14963
16539
  runner = new TaskRunner(deps);
16540
+ if (config.agentHome !== void 0 && config.agentEgress !== void 0) {
16541
+ await runner.recoverAgentMessageOutboxes(path3__default.join(config.agentHome.hostStorageRoot, "agents"));
16542
+ }
14964
16543
  const handleAgentHomeProjectionEnvelope = async (envelope) => {
14965
16544
  if (envelope.type !== "agent.home.projection") return false;
14966
16545
  if (agentHomeManager === void 0 || agentHomeProjectionCompletion === void 0) {
@@ -15143,6 +16722,7 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
15143
16722
  observer.handleInboundEnvelope(envelope);
15144
16723
  if (envelope.type === "agent.home.projection") return handleAgentHomeProjectionEnvelope(envelope).then(() => void 0);
15145
16724
  if (envelope.type === "agent.egress.ack") return handleAgentEgressEnvelope(envelope).then(() => void 0);
16725
+ if (envelope.type === "agent.message.disposition") return runner?.handleEnvelope(envelope) ?? Promise.resolve();
15146
16726
  if (envelope.type === "agent.content.read") return handleAgentContentReadEnvelope(envelope).then(() => void 0);
15147
16727
  return runner?.handleEnvelope(envelope) ?? Promise.resolve();
15148
16728
  },
@@ -15150,6 +16730,7 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
15150
16730
  const wasSettled = connectionState === "open" || connectionState === "degraded";
15151
16731
  connectionState = state;
15152
16732
  observer.noteConnectionState(state);
16733
+ if (!wasSettled && (state === "open" || state === "degraded")) runner?.retryRecoveredAgentMessages();
15153
16734
  if (!wasSettled && (state === "open" || state === "degraded")) runPresenceDiscovery();
15154
16735
  },
15155
16736
  backoff: overrides.backoff,
@@ -15168,6 +16749,7 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
15168
16749
  });
15169
16750
  await connection.start();
15170
16751
  await connection.waitForAck();
16752
+ runner.retryRecoveredAgentMessages();
15171
16753
  for (const record2 of agentEgress.retryableReliableRecords(connection.getServerCapabilities())) {
15172
16754
  dispatchReliableRecord(record2);
15173
16755
  }
@@ -15515,6 +17097,24 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
15515
17097
  if (!runner) throw new ControlError("not_found", "daemon is not started");
15516
17098
  return runner.requestApproval(parsed.taskId, parsed.summary);
15517
17099
  },
17100
+ "agent_messages.publish": (params) => {
17101
+ const parsed = parseAgentMessagePublishParams(params);
17102
+ if (!parsed) throw new ControlError("bad_request", "agent_messages.publish requires exactly {contextToken,contentType,body}");
17103
+ if (!runner) throw new ControlError("not_found", "daemon is not started");
17104
+ return runner.publishAgentMessage(parsed);
17105
+ },
17106
+ "agent_memory.recall": (params) => {
17107
+ const parsed = parseAgentMemoryRecallParams(params);
17108
+ if (!parsed) throw new ControlError("bad_request", "agent_memory.recall requires exactly {contextToken,path,ifRevision?}");
17109
+ if (!runner) throw new ControlError("not_found", "daemon is not started");
17110
+ return runner.recallAgentMemory(parsed);
17111
+ },
17112
+ "agent_memory.save": (params) => {
17113
+ const parsed = parseAgentMemorySaveParams(params);
17114
+ if (!parsed) throw new ControlError("bad_request", "agent_memory.save requires exactly {contextToken,op,path,expectedRevision,content?}");
17115
+ if (!runner) throw new ControlError("not_found", "daemon is not started");
17116
+ return runner.saveAgentMemory(parsed);
17117
+ },
15518
17118
  /**
15519
17119
  * Plan `device-assertion-broker`: mint one short-lived, audience-scoped
15520
17120
  * device assertion for a sibling local process.
@@ -16088,7 +17688,7 @@ ${args.map((a) => ` ${plistString(a)}`).join("\n")}
16088
17688
  }
16089
17689
  function createLaunchdLifecycle(def, deps = {}) {
16090
17690
  const run = deps.run ?? defaultRunner;
16091
- const fs28 = deps.fs ?? promises;
17691
+ const fs30 = deps.fs ?? promises;
16092
17692
  const homedir = deps.homedir ?? (() => os__default.homedir());
16093
17693
  const getuid = deps.getuid ?? (() => {
16094
17694
  if (typeof process.getuid !== "function") {
@@ -16102,7 +17702,7 @@ function createLaunchdLifecycle(def, deps = {}) {
16102
17702
  const serviceTarget = () => `${domainTarget()}/${label}`;
16103
17703
  async function fileExists(p) {
16104
17704
  try {
16105
- await fs28.stat(p);
17705
+ await fs30.stat(p);
16106
17706
  return true;
16107
17707
  } catch {
16108
17708
  return false;
@@ -16110,9 +17710,9 @@ function createLaunchdLifecycle(def, deps = {}) {
16110
17710
  }
16111
17711
  async function writePlist(program) {
16112
17712
  const xml = generateLaunchdPlist({ label, program, logDir: def.logDir });
16113
- await fs28.mkdir(path3__default.dirname(plistPath()), { recursive: true });
16114
- await fs28.mkdir(def.logDir, { recursive: true });
16115
- await fs28.writeFile(plistPath(), xml, "utf8");
17713
+ await fs30.mkdir(path3__default.dirname(plistPath()), { recursive: true });
17714
+ await fs30.mkdir(def.logDir, { recursive: true });
17715
+ await fs30.writeFile(plistPath(), xml, "utf8");
16116
17716
  }
16117
17717
  async function install(opts = {}) {
16118
17718
  await writePlist(opts.program ?? def.program);
@@ -16123,7 +17723,7 @@ function createLaunchdLifecycle(def, deps = {}) {
16123
17723
  }
16124
17724
  async function uninstall() {
16125
17725
  await runIdempotent(run, "launchctl", ["bootout", serviceTarget()], "launchctl bootout", LAUNCHD_NOT_LOADED);
16126
- await fs28.rm(plistPath(), { force: true });
17726
+ await fs30.rm(plistPath(), { force: true });
16127
17727
  }
16128
17728
  async function start() {
16129
17729
  if (!await fileExists(plistPath())) {
@@ -16207,14 +17807,14 @@ WantedBy=default.target
16207
17807
  }
16208
17808
  function createSystemdLifecycle(def, deps = {}) {
16209
17809
  const run = deps.run ?? defaultRunner;
16210
- const fs28 = deps.fs ?? promises;
17810
+ const fs30 = deps.fs ?? promises;
16211
17811
  const homedir = deps.homedir ?? (() => os__default.homedir());
16212
17812
  const name = sanitizeServiceName(def.name);
16213
17813
  const unitName = `${name}.service`;
16214
17814
  const unitPath = () => path3__default.join(homedir(), ".config", "systemd", "user", unitName);
16215
17815
  async function fileExists(p) {
16216
17816
  try {
16217
- await fs28.stat(p);
17817
+ await fs30.stat(p);
16218
17818
  return true;
16219
17819
  } catch {
16220
17820
  return false;
@@ -16222,9 +17822,9 @@ function createSystemdLifecycle(def, deps = {}) {
16222
17822
  }
16223
17823
  async function writeUnit(program) {
16224
17824
  const unit = generateSystemdUnit({ name, displayName: def.displayName ?? def.name, program, logDir: def.logDir });
16225
- await fs28.mkdir(path3__default.dirname(unitPath()), { recursive: true });
16226
- await fs28.mkdir(def.logDir, { recursive: true });
16227
- await fs28.writeFile(unitPath(), unit, "utf8");
17825
+ await fs30.mkdir(path3__default.dirname(unitPath()), { recursive: true });
17826
+ await fs30.mkdir(def.logDir, { recursive: true });
17827
+ await fs30.writeFile(unitPath(), unit, "utf8");
16228
17828
  }
16229
17829
  async function install(opts = {}) {
16230
17830
  await writeUnit(opts.program ?? def.program);
@@ -16233,7 +17833,7 @@ function createSystemdLifecycle(def, deps = {}) {
16233
17833
  }
16234
17834
  async function uninstall() {
16235
17835
  await runIdempotent(run, "systemctl", ["--user", "disable", "--now", unitName], "systemctl disable --now", SYSTEMD_NOT_LOADED);
16236
- await fs28.rm(unitPath(), { force: true });
17836
+ await fs30.rm(unitPath(), { force: true });
16237
17837
  await run("systemctl", ["--user", "daemon-reload"]);
16238
17838
  }
16239
17839
  async function start() {
@@ -16296,7 +17896,7 @@ ${argXml}${cwdXml}
16296
17896
  }
16297
17897
  function createWinswLifecycle(def, deps = {}) {
16298
17898
  const run = deps.run ?? defaultRunner;
16299
- const fs28 = deps.fs ?? promises;
17899
+ const fs30 = deps.fs ?? promises;
16300
17900
  const windows = def.windows;
16301
17901
  if (!windows) {
16302
17902
  throw new Error("WinSW service lifecycle requires `ServiceDefinition.windows.winswBin` (the product-bundled WinSW executable path)");
@@ -16308,7 +17908,7 @@ function createWinswLifecycle(def, deps = {}) {
16308
17908
  const xmlPath = path3__default.join(installDir, `${id}.xml`);
16309
17909
  async function fileExists(p) {
16310
17910
  try {
16311
- await fs28.stat(p);
17911
+ await fs30.stat(p);
16312
17912
  return true;
16313
17913
  } catch {
16314
17914
  return false;
@@ -16316,10 +17916,10 @@ function createWinswLifecycle(def, deps = {}) {
16316
17916
  }
16317
17917
  async function writeFiles(program) {
16318
17918
  const xml = generateWinswXml({ id, displayName: def.displayName ?? def.name, program, logDir: def.logDir });
16319
- await fs28.mkdir(installDir, { recursive: true });
16320
- await fs28.mkdir(def.logDir, { recursive: true });
16321
- await fs28.copyFile(winswBin, exePath);
16322
- await fs28.writeFile(xmlPath, xml, "utf8");
17919
+ await fs30.mkdir(installDir, { recursive: true });
17920
+ await fs30.mkdir(def.logDir, { recursive: true });
17921
+ await fs30.copyFile(winswBin, exePath);
17922
+ await fs30.writeFile(xmlPath, xml, "utf8");
16323
17923
  }
16324
17924
  async function install(opts = {}) {
16325
17925
  await writeFiles(opts.program ?? def.program);
@@ -16329,8 +17929,8 @@ function createWinswLifecycle(def, deps = {}) {
16329
17929
  async function uninstall() {
16330
17930
  await runIdempotent(run, exePath, ["stop"], "winsw stop", WINSW_NOT_INSTALLED);
16331
17931
  await runIdempotent(run, exePath, ["uninstall"], "winsw uninstall", WINSW_NOT_INSTALLED);
16332
- await fs28.rm(exePath, { force: true });
16333
- await fs28.rm(xmlPath, { force: true });
17932
+ await fs30.rm(exePath, { force: true });
17933
+ await fs30.rm(xmlPath, { force: true });
16334
17934
  }
16335
17935
  async function start() {
16336
17936
  if (!await fileExists(xmlPath)) {
@@ -16375,7 +17975,7 @@ function createServiceLifecycle(def, opts = {}) {
16375
17975
 
16376
17976
  // src/bin/official-release.ts
16377
17977
  var OFFICIAL_LOCAL_AGENT_RELEASE = resolveLocalAgentReleaseIdentity({
16378
- version: "0.8.1"
17978
+ version: "0.9.0"
16379
17979
  });
16380
17980
 
16381
17981
  // src/bin/config.ts
@@ -18590,8 +20190,8 @@ async function runTasksFollowCommand(config, deps) {
18590
20190
  });
18591
20191
  return;
18592
20192
  }
18593
- const path36 = auditLogPath(storeDir);
18594
- await followAuditLog(path36, (event) => log(formatDaemonEventLine(event)), {
20193
+ const path41 = auditLogPath(storeDir);
20194
+ await followAuditLog(path41, (event) => log(formatDaemonEventLine(event)), {
18595
20195
  signal: deps.signal,
18596
20196
  pollIntervalMs: deps.pollIntervalMs,
18597
20197
  fromEnd: true