@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.
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { randomUUID, createHash, createPrivateKey, generateKeyPairSync, sign, randomBytes, timingSafeEqual, createHmac } from 'crypto';
2
- import { promises, mkdirSync, linkSync, fstatSync, lstatSync, unlinkSync, constants, existsSync, renameSync, writeFileSync, chmodSync, statSync, readdirSync, readFileSync, realpathSync } from 'fs';
2
+ import { promises, mkdirSync, constants, existsSync, linkSync, fstatSync, lstatSync, unlinkSync, renameSync, writeFileSync, chmodSync, statSync, readdirSync, readFileSync, realpathSync } from 'fs';
3
3
  import * as path3 from 'path';
4
4
  import path3__default, { join, isAbsolute } from 'path';
5
- import { AGENT_CONTENT_ARTIFACT_READ_CAPABILITY, AGENT_CONTENT_TRANSCRIPT_READ_CAPABILITY, AGENT_CONTENT_WORKSPACE_READ_CAPABILITY, AgentRefSchema, AgentHomeProjectionPayloadSchema, byokBlobUrlPath, BYOK_BLOBS_PATH, byokBlobFinalizePath, partitionAgentEvents, BYOK_SKILL_PACKS_PATH, byokSkillPackFilePath, BYOK_RECORDS_PATH, byokRecordPath, TASK_TRANSITIONS, AgentEgressPolicySchema, BYOK_PAIR_PATH, PairResponseSchema, BYOK_CHALLENGE_PATH, BYOK_TOKEN_PATH, CONFIGURED_TOOLSETS_MAX_ITEMS, ToolsetIdSchema, AgentContentReceiptPayloadSchema, encodeEnvelope, PROTOCOL_VERSION, createEnvelope, AGENT_EGRESS_RELIABLE_ACK_CAPABILITY, 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';
5
+ import { AGENT_CONTENT_ARTIFACT_READ_CAPABILITY, AGENT_CONTENT_TRANSCRIPT_READ_CAPABILITY, AGENT_CONTENT_WORKSPACE_READ_CAPABILITY, AgentRefSchema, AgentHomeProjectionPayloadSchema, byokBlobUrlPath, BYOK_BLOBS_PATH, byokBlobFinalizePath, partitionAgentEvents, BYOK_SKILL_PACKS_PATH, byokSkillPackFilePath, BYOK_RECORDS_PATH, byokRecordPath, TASK_TRANSITIONS, AgentEgressPolicySchema, BYOK_PAIR_PATH, PairResponseSchema, BYOK_CHALLENGE_PATH, BYOK_TOKEN_PATH, CONFIGURED_TOOLSETS_MAX_ITEMS, ToolsetIdSchema, AgentContentReceiptPayloadSchema, encodeEnvelope, PROTOCOL_VERSION, createEnvelope, AGENT_EGRESS_RELIABLE_ACK_CAPABILITY, 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';
6
6
  import net, { createServer, createConnection } from 'net';
7
7
  import * as os4 from 'os';
8
8
  import os4__default from 'os';
@@ -627,12 +627,16 @@ var AgentHomeLeaseManager = class _AgentHomeLeaseManager {
627
627
  let handle;
628
628
  let rootGate;
629
629
  let ownsMarker = false;
630
+ let homeIdentity;
630
631
  try {
631
632
  rootGate = await acquirePathMutationGate({
632
633
  scope: "agent-home-root",
633
634
  targetPath: resolution.agentsRoot
634
635
  }, { waitMs: 1e3 });
635
636
  await ensureDirectoryNoSymlink(resolution.agentsRoot, canonicalHome);
637
+ const homeStat = await promises.stat(canonicalHome, { bigint: true });
638
+ if (!homeStat.isDirectory()) throw new AgentHomeResolutionError(`Agent home ${canonicalHome} is not a directory`);
639
+ homeIdentity = Object.freeze({ dev: homeStat.dev, ino: homeStat.ino });
636
640
  const internalDir = await ensureDirectoryNoSymlink(
637
641
  canonicalHome,
638
642
  path3__default.join(canonicalHome, AGENT_HOME_INTERNAL_DIRECTORY)
@@ -671,6 +675,7 @@ var AgentHomeLeaseManager = class _AgentHomeLeaseManager {
671
675
  }
672
676
  const acquiredLockPath = lockPath;
673
677
  if (acquiredLockPath === void 0) throw new AgentHomeError("Agent home lease marker path was not established");
678
+ if (homeIdentity === void 0) throw new AgentHomeError("Agent home lease identity was not established");
674
679
  let released = false;
675
680
  let releaseAttempt;
676
681
  return {
@@ -678,6 +683,7 @@ var AgentHomeLeaseManager = class _AgentHomeLeaseManager {
678
683
  agentRef,
679
684
  canonicalHome,
680
685
  cwd: canonicalHome,
686
+ homeIdentity,
681
687
  release: () => {
682
688
  if (released) return Promise.resolve();
683
689
  if (releaseAttempt !== void 0) return releaseAttempt;
@@ -1032,14 +1038,14 @@ function sameTaskTerminalMatch(entry, expected) {
1032
1038
  return sameRef(entry.agentRef, expected.agentRef) && entry.taskId === expected.taskId && entry.runtimeId === expected.runtimeId && entry.cwd === path3__default.resolve(expected.cwd);
1033
1039
  }
1034
1040
  function sessionFileName(runtimeId, sessionRef) {
1035
- const digest2 = createHash("sha256").update(sessionRef, "utf8").digest("hex");
1041
+ const digest3 = createHash("sha256").update(sessionRef, "utf8").digest("hex");
1036
1042
  const runtime = runtimeId.replace(/[^a-z0-9._-]/giu, "_").slice(0, 64) || "runtime";
1037
- return `${runtime}-${digest2}.jsonl`;
1043
+ return `${runtime}-${digest3}.jsonl`;
1038
1044
  }
1039
1045
  function taskTerminalFileName(runtimeId, taskId) {
1040
- const digest2 = createHash("sha256").update(taskId, "utf8").digest("hex");
1046
+ const digest3 = createHash("sha256").update(taskId, "utf8").digest("hex");
1041
1047
  const runtime = runtimeId.replace(/[^a-z0-9._-]/giu, "_").slice(0, 64) || "runtime";
1042
- return `${runtime}-task-${digest2}.jsonl`;
1048
+ return `${runtime}-task-${digest3}.jsonl`;
1043
1049
  }
1044
1050
  async function evidenceDirectory(cwdInput) {
1045
1051
  if (!path3__default.isAbsolute(cwdInput)) {
@@ -4997,6 +5003,7 @@ var CodexAdapter = class {
4997
5003
  steer: false,
4998
5004
  resume: true,
4999
5005
  approvalInteractive: false,
5006
+ mcpToolsets: true,
5000
5007
  permissionModes: ["auto", "readonly"]
5001
5008
  },
5002
5009
  environmentRequirements: { credentialNames: [] }
@@ -5128,7 +5135,7 @@ ${withStreams.stderr ?? ""}`);
5128
5135
  resumeRef: startInput.manifest.sessionRef,
5129
5136
  instruction: startInput.instruction,
5130
5137
  modelId: manifestModelId,
5131
- policyArgs: [...policyArgs],
5138
+ policyArgs: [...policyArgs, ...codexMcpConfigArgs(startInput.mcpServers)],
5132
5139
  cwd: manifestCwd,
5133
5140
  env: runtimeEnv,
5134
5141
  spawnFn: this.options.spawnFn,
@@ -5158,6 +5165,18 @@ ${withStreams.stderr ?? ""}`);
5158
5165
  return (this.options.resolveBin ?? resolveCodexBin)();
5159
5166
  }
5160
5167
  };
5168
+ function codexMcpConfigArgs(servers) {
5169
+ if (servers === void 0 || Object.keys(servers).length === 0) return [];
5170
+ const args = ["--ignore-user-config"];
5171
+ for (const [name, server] of Object.entries(servers).sort(([left], [right]) => left.localeCompare(right))) {
5172
+ args.push("-c", `mcp_servers.${name}.command=${JSON.stringify(server.command)}`);
5173
+ if (server.args !== void 0) args.push("-c", `mcp_servers.${name}.args=${JSON.stringify([...server.args])}`);
5174
+ for (const [key, value] of Object.entries(server.env ?? {}).sort(([left], [right]) => left.localeCompare(right))) {
5175
+ args.push("-c", `mcp_servers.${name}.env.${key}=${JSON.stringify(value)}`);
5176
+ }
5177
+ }
5178
+ return args;
5179
+ }
5161
5180
  async function resolveRealWorkspaceDir(workspaceDir) {
5162
5181
  return promises.realpath(workspaceDir).catch(() => workspaceDir);
5163
5182
  }
@@ -6884,6 +6903,27 @@ function parseApprovalsRequestParams(value) {
6884
6903
  if (typeof value.summary !== "string") return void 0;
6885
6904
  return { taskId: value.taskId, summary: value.summary };
6886
6905
  }
6906
+ function parseAgentMessagePublishParams(value) {
6907
+ if (!isRecord3(value) || Object.keys(value).some((key) => key !== "contextToken" && key !== "contentType" && key !== "body")) return void 0;
6908
+ if (typeof value.contextToken !== "string" || value.contextToken.length < 32 || value.contextToken.length > 160) return void 0;
6909
+ if (value.contentType !== "text/plain" && value.contentType !== "text/markdown") return void 0;
6910
+ if (typeof value.body !== "string" || value.body.length === 0) return void 0;
6911
+ return { contextToken: value.contextToken, contentType: value.contentType, body: value.body };
6912
+ }
6913
+ function validAgentMemoryContextToken(value) {
6914
+ return typeof value === "string" && value.length >= 32 && value.length <= 160 && !/[\u0000\r\n]/u.test(value);
6915
+ }
6916
+ function parseAgentMemoryRecallParams(value) {
6917
+ if (!isRecord3(value) || Object.keys(value).some((key) => key !== "contextToken" && key !== "path" && key !== "ifRevision")) return void 0;
6918
+ if (!validAgentMemoryContextToken(value.contextToken) || typeof value.path !== "string" || value.ifRevision !== void 0 && typeof value.ifRevision !== "string") return void 0;
6919
+ return { contextToken: value.contextToken, path: value.path, ...value.ifRevision === void 0 ? {} : { ifRevision: value.ifRevision } };
6920
+ }
6921
+ function parseAgentMemorySaveParams(value) {
6922
+ if (!isRecord3(value) || Object.keys(value).some((key) => key !== "contextToken" && key !== "op" && key !== "path" && key !== "expectedRevision" && key !== "content")) return void 0;
6923
+ 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;
6924
+ const content = value.content;
6925
+ return { contextToken: value.contextToken, op: value.op, path: value.path, expectedRevision: value.expectedRevision, ...typeof content === "string" ? { content } : {} };
6926
+ }
6887
6927
  var ASSERTION_AUDIENCE_MAX_BYTES = 256;
6888
6928
  function parseAssertionIssueParams(value) {
6889
6929
  if (!isRecord3(value)) return void 0;
@@ -7141,6 +7181,8 @@ async function startControlServer(opts) {
7141
7181
  }
7142
7182
  return { endpoint, stopServing, close };
7143
7183
  }
7184
+ var AGENT_MESSAGE_MCP_SERVER_NAME = "byokagentmessage";
7185
+ var AGENT_MEMORY_MCP_SERVER_NAME = "byokagentmemory";
7144
7186
  var MAX_LOCAL_MCP_SERVERS_PER_TOOLSET = 16;
7145
7187
  var MAX_LOCAL_MCP_ARGS = 64;
7146
7188
  var MAX_LOCAL_MCP_TOKEN_CHARS = 4096;
@@ -7230,7 +7272,7 @@ function buildState(configured) {
7230
7272
  `DaemonConfig.mcpToolsets.${toolsetId}.mcpServers contains invalid server name ${JSON.stringify(serverName)}`
7231
7273
  );
7232
7274
  }
7233
- if (serverName === APPROVAL_MCP_SERVER_NAME) {
7275
+ if (serverName === APPROVAL_MCP_SERVER_NAME || serverName === AGENT_MESSAGE_MCP_SERVER_NAME || serverName === AGENT_MEMORY_MCP_SERVER_NAME) {
7234
7276
  throw new Error(
7235
7277
  `DaemonConfig.mcpToolsets.${toolsetId}.mcpServers.${serverName} uses a server name reserved by the daemon`
7236
7278
  );
@@ -7393,9 +7435,9 @@ function deterministicJitterMs(input) {
7393
7435
  throw new Error("deterministic jitter ratio must be between 0 and 1");
7394
7436
  }
7395
7437
  if (input.baseMs === 0 || ratio === 0) return Math.round(input.baseMs);
7396
- 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();
7397
- const high = digest2.readUInt32BE(0) & 2097151;
7398
- const low = digest2.readUInt32BE(4);
7438
+ 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();
7439
+ const high = digest3.readUInt32BE(0) & 2097151;
7440
+ const low = digest3.readUInt32BE(4);
7399
7441
  const unit = (high * 4294967296 + low) / 9007199254740992;
7400
7442
  const multiplier = 1 - ratio + unit * ratio * 2;
7401
7443
  return Math.max(0, Math.round(input.baseMs * multiplier));
@@ -9368,9 +9410,9 @@ function isSqliteAvailable() {
9368
9410
  }
9369
9411
  }
9370
9412
  var SECURE_FILE_MODE = 384;
9371
- function openJournalDatabase(path33, busyTimeoutMs, faults) {
9413
+ function openJournalDatabase(path38, busyTimeoutMs, faults) {
9372
9414
  const { DatabaseSync } = loadSqliteModule();
9373
- const db = new DatabaseSync(path33, { timeout: busyTimeoutMs });
9415
+ const db = new DatabaseSync(path38, { timeout: busyTimeoutMs });
9374
9416
  try {
9375
9417
  faults?.onStep?.("after-open");
9376
9418
  db.exec("PRAGMA auto_vacuum = INCREMENTAL;");
@@ -9513,9 +9555,9 @@ var RECEIVED_STATE = "received";
9513
9555
  function byteLength(value) {
9514
9556
  return Buffer.byteLength(value, "utf8");
9515
9557
  }
9516
- function fileBytes(path33) {
9558
+ function fileBytes(path38) {
9517
9559
  try {
9518
- return statSync(path33).size;
9560
+ return statSync(path38).size;
9519
9561
  } catch {
9520
9562
  return 0;
9521
9563
  }
@@ -10745,130 +10787,1262 @@ var ProgressBatcher = class {
10745
10787
  }
10746
10788
  }
10747
10789
  };
10748
-
10749
- // src/daemon/task-runner.ts
10750
- var DEFAULT_APPROVAL_TIMEOUT_MS = 10 * 6e4;
10751
- var DEFAULT_SHUTDOWN_INTERRUPT_TIMEOUT_MS = 5e3;
10752
- var AGENT_TERMINAL_EVIDENCE_MAX_ATTEMPTS = 3;
10753
- var AGENT_TERMINAL_EVIDENCE_RETRY_DELAY_MS = 20;
10754
- var GIT_OBSERVATION_TIMEOUT_MS = 5e3;
10755
- var MAX_PENDING_APPROVALS_PER_TASK = 16;
10756
- var NoPendingApprovalError = class extends Error {
10757
- constructor(taskId) {
10758
- super(`no pending out-of-band approval to resolve for task ${taskId}`);
10759
- this.taskId = taskId;
10760
- this.name = "NoPendingApprovalError";
10790
+ var AGENT_MESSAGE_DIRECTORY = path3__default.join(".byok", "messages");
10791
+ var AGENT_MESSAGE_OUTBOX_FILENAME = "outbox-v1.jsonl";
10792
+ var AgentMessageOutboxError = class extends Error {
10793
+ constructor(message) {
10794
+ super(message);
10795
+ this.name = "AgentMessageOutboxError";
10761
10796
  }
10762
- taskId;
10763
10797
  };
10764
- var MAX_INLINE_ARTIFACT_BYTES = 64 * 1024;
10765
- var MAX_TRACKED_TASK_IDS = 2e3;
10766
- var MAX_DURATION_EXCEEDED_REASON_PREFIX = "resource limit exceeded: maxDurationMs";
10767
- var MAX_OUTPUT_BYTES_EXCEEDED_REASON_PREFIX = "resource limit exceeded: maxTaskOutputBytes";
10768
- var MAX_PROGRESS_BATCH_BYTES_EXCEEDED_REASON_PREFIX = "resource limit exceeded: progressBatch.maxBatchBytes";
10769
- var RESULT_DOCUMENT_UNDELIVERABLE_REASON_PREFIX = "result document undeliverable";
10770
- function resultDocumentRejectionDetail(check) {
10771
- switch (check.reason) {
10772
- case "over-cap":
10773
- 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)`;
10774
- case "not-serializable":
10775
- return "not JSON-serializable (JSON.stringify threw, or produced no output at all)";
10776
- case "not-plain-json":
10777
- 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)";
10778
- default: {
10779
- const exhaustive = check;
10780
- throw new Error(`unhandled result document rejection: ${JSON.stringify(exhaustive)}`);
10798
+ function stableJson(value) {
10799
+ const encoded = JSON.stringify(value);
10800
+ if (encoded === void 0) throw new AgentMessageOutboxError("message outbox value is not serializable");
10801
+ return encoded;
10802
+ }
10803
+ function hashBody(body) {
10804
+ return `sha256:${createHash("sha256").update(body, "utf8").digest("hex")}`;
10805
+ }
10806
+ function sameAgentRef(left, right) {
10807
+ return left.agentId === right.agentId && left.profileRevision === right.profileRevision;
10808
+ }
10809
+ function exactDisposition(record, value) {
10810
+ 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);
10811
+ }
10812
+ var AgentMessageOutbox = class _AgentMessageOutbox {
10813
+ constructor(homeDir, outboxPath) {
10814
+ this.homeDir = homeDir;
10815
+ this.outboxPath = outboxPath;
10816
+ }
10817
+ homeDir;
10818
+ outboxPath;
10819
+ pendingByTask = /* @__PURE__ */ new Map();
10820
+ dispositionByTask = /* @__PURE__ */ new Map();
10821
+ nextCursor = 1;
10822
+ logEntries = 0;
10823
+ writeTail = Promise.resolve();
10824
+ static async open(homeDir) {
10825
+ const directory = path3__default.join(homeDir, AGENT_MESSAGE_DIRECTORY);
10826
+ await ensureSecureDir(directory);
10827
+ const outbox = new _AgentMessageOutbox(homeDir, path3__default.join(directory, AGENT_MESSAGE_OUTBOX_FILENAME));
10828
+ await outbox.load();
10829
+ return outbox;
10830
+ }
10831
+ /** Re-open every existing Agent-local message outbox without following Agent-home symlinks. */
10832
+ static async recover(agentsRoot, tenantId) {
10833
+ if (!path3__default.isAbsolute(agentsRoot)) throw new AgentMessageOutboxError("message outbox recovery root must be absolute");
10834
+ let canonicalRoot;
10835
+ try {
10836
+ canonicalRoot = await promises.realpath(agentsRoot);
10837
+ } catch (error) {
10838
+ if (error.code === "ENOENT") return [];
10839
+ throw error;
10840
+ }
10841
+ const recovered = [];
10842
+ for (const entry of await promises.readdir(canonicalRoot, { withFileTypes: true })) {
10843
+ if (!entry.isDirectory() || entry.isSymbolicLink()) continue;
10844
+ const home = path3__default.join(canonicalRoot, entry.name);
10845
+ const canonicalHome = await promises.realpath(home);
10846
+ if (path3__default.relative(canonicalRoot, canonicalHome) !== entry.name) {
10847
+ throw new AgentMessageOutboxError(`message outbox recovery home escaped the canonical agents root: ${entry.name}`);
10848
+ }
10849
+ try {
10850
+ await promises.lstat(path3__default.join(canonicalHome, AGENT_MESSAGE_DIRECTORY, AGENT_MESSAGE_OUTBOX_FILENAME));
10851
+ } catch (error) {
10852
+ if (error.code === "ENOENT") continue;
10853
+ throw error;
10854
+ }
10855
+ const outbox = await _AgentMessageOutbox.open(canonicalHome);
10856
+ if (outbox.records().some((record) => record.agentRef.agentId !== entry.name)) {
10857
+ throw new AgentMessageOutboxError(`message outbox in ${entry.name} claims a different Agent`);
10858
+ }
10859
+ if (outbox.records().some((record) => record.tenantId !== tenantId)) {
10860
+ throw new AgentMessageOutboxError(`message outbox in ${entry.name} claims a different authenticated tenant`);
10861
+ }
10862
+ recovered.push(outbox);
10781
10863
  }
10864
+ return Object.freeze(recovered);
10782
10865
  }
10866
+ records() {
10867
+ return Object.freeze([...this.pendingByTask.values()].sort((a, b) => a.cursor - b.cursor));
10868
+ }
10869
+ /** Activated records with no exact disposition yet; only these may be transport-replayed. */
10870
+ retryableRecords() {
10871
+ return Object.freeze(this.records().filter((record) => !this.dispositionByTask.has(record.taskId)));
10872
+ }
10873
+ get(taskId) {
10874
+ return this.pendingByTask.get(taskId);
10875
+ }
10876
+ async appendDraft(input) {
10877
+ return this.exclusive(async () => {
10878
+ if (input.contentType !== input.requirement.contentType) throw new AgentMessageOutboxError("message contentType does not match the offer contract");
10879
+ const byteCount = Buffer.byteLength(input.body, "utf8");
10880
+ if (byteCount < 1 || byteCount > input.requirement.maxBytes) throw new AgentMessageOutboxError("message body exceeds the offer byte contract");
10881
+ const existing = this.pendingByTask.get(input.taskId);
10882
+ if (existing !== void 0) {
10883
+ if (existing.tenantId !== input.tenantId || existing.contentType !== input.contentType || existing.body !== input.body || existing.contract !== input.requirement.contract || !sameAgentRef(existing.agentRef, input.agentRef)) {
10884
+ throw new AgentMessageOutboxError("required-message task already has a different immutable draft");
10885
+ }
10886
+ return existing;
10887
+ }
10888
+ if (this.pendingByTask.size >= input.maxPendingEvents) throw new AgentMessageOutboxError("message outbox event quota is exhausted");
10889
+ const pendingBytes = this.records().reduce((sum, record2) => sum + record2.byteCount, 0);
10890
+ if (pendingBytes + byteCount > input.maxPendingBytes) throw new AgentMessageOutboxError("message outbox byte quota is exhausted");
10891
+ const record = Object.freeze({
10892
+ schema: 1,
10893
+ taskId: input.taskId,
10894
+ tenantId: input.tenantId,
10895
+ agentRef: Object.freeze({ ...input.agentRef }),
10896
+ contract: input.requirement.contract,
10897
+ messageId: randomUUID(),
10898
+ cursor: this.nextCursor++,
10899
+ contentType: input.contentType,
10900
+ body: input.body,
10901
+ contentHash: hashBody(input.body),
10902
+ byteCount,
10903
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
10904
+ ...input.sessionRef === void 0 ? {} : { sessionRef: input.sessionRef }
10905
+ });
10906
+ await this.appendEntry({ schema: 1, kind: "append", record });
10907
+ this.pendingByTask.set(record.taskId, record);
10908
+ return record;
10909
+ });
10910
+ }
10911
+ async activate(taskId, sessionRef) {
10912
+ return this.exclusive(async () => {
10913
+ const record = this.pendingByTask.get(taskId);
10914
+ if (record === void 0) return void 0;
10915
+ if (record.sessionRef !== void 0) {
10916
+ if (record.sessionRef !== sessionRef) throw new AgentMessageOutboxError("message draft is already bound to a different session");
10917
+ return record;
10918
+ }
10919
+ await this.appendEntry({ schema: 1, kind: "activate", taskId, messageId: record.messageId, sessionRef });
10920
+ const activated = Object.freeze({ ...record, sessionRef });
10921
+ this.pendingByTask.set(taskId, activated);
10922
+ return activated;
10923
+ });
10924
+ }
10925
+ publishPayload(record) {
10926
+ if (record.sessionRef === void 0) throw new AgentMessageOutboxError("message draft is not bound to a durable session");
10927
+ return AgentMessagePublishPayloadSchema.parse({
10928
+ agentRef: record.agentRef,
10929
+ sessionRef: record.sessionRef,
10930
+ contract: record.contract,
10931
+ messageId: record.messageId,
10932
+ cursor: record.cursor,
10933
+ contentType: record.contentType,
10934
+ body: record.body,
10935
+ contentHash: record.contentHash,
10936
+ byteCount: record.byteCount
10937
+ });
10938
+ }
10939
+ async applyDisposition(taskId, input) {
10940
+ return this.exclusive(async () => {
10941
+ const disposition = AgentMessageDispositionPayloadSchema.parse(input);
10942
+ const record = this.pendingByTask.get(taskId);
10943
+ if (record === void 0) return "unknown";
10944
+ if (!exactDisposition(record, disposition)) return "mismatch";
10945
+ await this.appendEntry({ schema: 1, kind: "disposition", taskId, disposition });
10946
+ if (disposition.outcome === "accepted") {
10947
+ this.pendingByTask.delete(taskId);
10948
+ this.dispositionByTask.delete(taskId);
10949
+ } else {
10950
+ this.dispositionByTask.set(taskId, disposition);
10951
+ }
10952
+ if (this.logEntries >= 512) await this.compact();
10953
+ return disposition.outcome;
10954
+ });
10955
+ }
10956
+ async load() {
10957
+ let raw;
10958
+ try {
10959
+ const stat = await promises.stat(this.outboxPath);
10960
+ if (stat.size > 64 * 1024 * 1024) throw new AgentMessageOutboxError("message outbox exceeds its bounded on-disk size");
10961
+ raw = await promises.readFile(this.outboxPath, "utf8");
10962
+ } catch (error) {
10963
+ if (error.code === "ENOENT") return;
10964
+ throw error;
10965
+ }
10966
+ for (const line of raw.split("\n")) {
10967
+ if (line.length === 0) continue;
10968
+ const entry = JSON.parse(line);
10969
+ this.logEntries += 1;
10970
+ if (entry.schema !== 1) throw new AgentMessageOutboxError("message outbox schema is unsupported");
10971
+ if (entry.kind === "append") {
10972
+ if (this.pendingByTask.has(entry.record.taskId)) throw new AgentMessageOutboxError("message outbox contains duplicate task draft");
10973
+ this.pendingByTask.set(entry.record.taskId, Object.freeze(entry.record));
10974
+ this.nextCursor = Math.max(this.nextCursor, entry.record.cursor + 1);
10975
+ } else if (entry.kind === "activate") {
10976
+ const record = this.pendingByTask.get(entry.taskId);
10977
+ if (record === void 0 || record.messageId !== entry.messageId) throw new AgentMessageOutboxError("message activation has no exact draft");
10978
+ if (record.sessionRef !== void 0 && record.sessionRef !== entry.sessionRef) throw new AgentMessageOutboxError("message activation session conflicts");
10979
+ this.pendingByTask.set(entry.taskId, Object.freeze({ ...record, sessionRef: entry.sessionRef }));
10980
+ } else if (entry.kind === "disposition") {
10981
+ const record = this.pendingByTask.get(entry.taskId);
10982
+ if (record === void 0 || !exactDisposition(record, entry.disposition)) throw new AgentMessageOutboxError("message outbox contains a mismatched disposition");
10983
+ if (entry.disposition.outcome === "accepted") {
10984
+ this.pendingByTask.delete(entry.taskId);
10985
+ this.dispositionByTask.delete(entry.taskId);
10986
+ } else {
10987
+ this.dispositionByTask.set(entry.taskId, entry.disposition);
10988
+ }
10989
+ } else {
10990
+ throw new AgentMessageOutboxError("message outbox entry kind is invalid");
10991
+ }
10992
+ }
10993
+ }
10994
+ async appendEntry(entry) {
10995
+ const handle = await promises.open(this.outboxPath, constants.O_APPEND | constants.O_CREAT | constants.O_WRONLY, 384);
10996
+ try {
10997
+ await handle.write(`${stableJson(entry)}
10998
+ `, void 0, "utf8");
10999
+ await handle.sync();
11000
+ this.logEntries += 1;
11001
+ } finally {
11002
+ await handle.close();
11003
+ }
11004
+ }
11005
+ async compact() {
11006
+ const entries = this.records().flatMap((record) => {
11007
+ const append = { schema: 1, kind: "append", record };
11008
+ const disposition = this.dispositionByTask.get(record.taskId);
11009
+ return disposition === void 0 ? [append] : [append, { schema: 1, kind: "disposition", taskId: record.taskId, disposition }];
11010
+ });
11011
+ await atomicWriteFile(this.outboxPath, entries.map(stableJson).join(entries.length === 0 ? "" : "\n"), { mode: 384 });
11012
+ this.logEntries = entries.length;
11013
+ }
11014
+ async exclusive(fn) {
11015
+ const prior = this.writeTail;
11016
+ let release;
11017
+ this.writeTail = new Promise((resolve) => {
11018
+ release = resolve;
11019
+ });
11020
+ await prior;
11021
+ try {
11022
+ return await fn();
11023
+ } finally {
11024
+ release();
11025
+ }
11026
+ }
11027
+ };
11028
+
11029
+ // src/daemon/memory-guidance.ts
11030
+ var AGENT_MEMORY_GUIDANCE = [
11031
+ "At the start of this Agent task, first read `MEMORY.md` in the provided `cwd`.",
11032
+ "Treat `MEMORY.md` as a concise, self-contained recovery index; if it is empty, initialize a brief index from durable, non-secret task knowledge.",
11033
+ "Read files under `notes/` only as needed, following pointers from the index.",
11034
+ "When task permissions allow and a durable value is learned, update the relevant `notes/` entry and the `MEMORY.md` index.",
11035
+ "Never write credentials, secrets, tokens, API keys, private keys, or other authentication material to `MEMORY.md` or `notes/`."
11036
+ ].join("\n");
11037
+ function prependAgentMemoryGuidance(instruction) {
11038
+ return `${AGENT_MEMORY_GUIDANCE}
11039
+
11040
+ ${instruction}`;
10783
11041
  }
10784
- var DEFAULT_MAX_TASK_OUTPUT_BYTES = 64 * 1024 * 1024;
10785
- function isKnownRuntimeId(id) {
10786
- return RuntimeIdSchema.safeParse(id).success;
10787
- }
10788
- function terminalUsageNumber(value, maximum) {
10789
- return value !== void 0 && Number.isSafeInteger(value) && value >= 0 && value <= maximum ? value : void 0;
11042
+ var AGENT_MEMORY_AUDIT_FILENAME = "agent-memory-audit-v1.jsonl";
11043
+ var AGENT_MEMORY_OUTBOX_FILENAME = "agent-memory-redacted-outbox-v2.json";
11044
+ var AGENT_MEMORY_MAX_FILE_BYTES = 256 * 1024;
11045
+ var AGENT_MEMORY_MAX_SNAPSHOT_BYTES = 1024 * 1024;
11046
+ var AGENT_MEMORY_MAX_SNAPSHOT_FILES = 128;
11047
+ var AGENT_MEMORY_MAX_SNAPSHOT_ENTRIES = 512;
11048
+ var AGENT_MEMORY_MAX_LOCAL_LOG_BYTES = AGENT_MEMORY_MAX_SNAPSHOT_BYTES;
11049
+ var REVISION = /^sha256:[a-f0-9]{64}$/u;
11050
+ var SAFE_SEGMENT = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u;
11051
+ var SECRET_LIKE = /(?:^|[-_.])(secret|token|credential|password|passwd|api[-_]?key|private[-_]?key|cookie)(?:$|[-_.])/iu;
11052
+ var encoder2 = new TextEncoder();
11053
+ var AGENT_MEMORY_PROJECTION_PUBLISH_TIMEOUT_MS = 1e4;
11054
+ var AgentMemoryError = class extends Error {
11055
+ constructor(message) {
11056
+ super(message);
11057
+ this.name = "AgentMemoryError";
11058
+ }
11059
+ };
11060
+ var AgentMemoryRevisionConflictError = class extends AgentMemoryError {
11061
+ constructor(expectedRevision, actualRevision) {
11062
+ super(`Agent memory revision conflict: expected ${expectedRevision}, current ${actualRevision}`);
11063
+ this.expectedRevision = expectedRevision;
11064
+ this.actualRevision = actualRevision;
11065
+ this.name = "AgentMemoryRevisionConflictError";
11066
+ }
11067
+ expectedRevision;
11068
+ actualRevision;
11069
+ };
11070
+ var DRAINED_AGENT_MEMORY_PROJECTION_REPLAY = Object.freeze({ status: "drained" });
11071
+ var AgentMemoryProjectionReplayPendingError = class extends AgentMemoryError {
11072
+ constructor(outcome) {
11073
+ super("Agent memory projection replay remains pending");
11074
+ this.outcome = outcome;
11075
+ this.name = "AgentMemoryProjectionReplayPendingError";
11076
+ }
11077
+ outcome;
11078
+ };
11079
+ function digestBytes(content) {
11080
+ return `sha256:${createHash("sha256").update(content).digest("hex")}`;
10790
11081
  }
10791
- var DEFAULT_RUNTIME_PREFERENCE = ["claude", "codex", "pi"];
10792
- function orderByPreference(candidates, preference) {
10793
- const rank = new Map(preference.map((id, index) => [id, index]));
10794
- return [...candidates].sort((a, b) => (rank.get(a.descriptor.id) ?? preference.length) - (rank.get(b.descriptor.id) ?? preference.length));
11082
+ function digest2(content) {
11083
+ return digestBytes(encoder2.encode(content));
10795
11084
  }
10796
- function adapterSupportsMode(descriptor, mode) {
10797
- return descriptor.capabilities.permissionModes.includes(mode);
11085
+ function nonEmpty(value) {
11086
+ return typeof value === "string" && value.length > 0 && !/[\u0000\r\n]/u.test(value);
10798
11087
  }
10799
- function adapterSupportsMcpToolsets(descriptor) {
10800
- return descriptor.capabilities.mcpToolsets === true;
11088
+ function revision(value) {
11089
+ return typeof value === "string" && REVISION.test(value);
10801
11090
  }
10802
- function withoutRequiredToolsets(payload) {
10803
- const { requiredToolsets, egressPolicy, ...offer } = payload;
10804
- return offer;
11091
+ function taskContext(value) {
11092
+ 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") {
11093
+ throw new AgentMemoryError("Agent memory requires an exact active Agent task context");
11094
+ }
11095
+ return Object.freeze({ ...value, agentRef: Object.freeze({ ...value.agentRef }), homeIdentity: Object.freeze({ ...value.homeIdentity }), canonicalHome: path3__default.resolve(value.canonicalHome) });
10805
11096
  }
10806
- function sameEgressPolicy(left, right) {
10807
- return JSON.stringify(left) === JSON.stringify(right);
11097
+ function validateAgentMemoryPath(value) {
11098
+ if (typeof value !== "string" || value.length === 0 || value.length > 1024 || /[\u0000\\]/u.test(value)) throw new AgentMemoryError("memory path is invalid");
11099
+ if (value === "MEMORY.md") return value;
11100
+ if (path3__default.posix.isAbsolute(value) || /[*?\[{]/u.test(value)) throw new AgentMemoryError("memory path must name exactly one file");
11101
+ const parts = value.split("/");
11102
+ 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))) {
11103
+ throw new AgentMemoryError("memory path must be MEMORY.md or notes/<safe-relative>.md");
11104
+ }
11105
+ return value;
10808
11106
  }
10809
- function offeredAgentRef(payload) {
10810
- if (!Object.prototype.hasOwnProperty.call(payload, "agentRef")) return void 0;
10811
- return validateAgentRef(payload.agentRef);
11107
+ var SECURE_DIRECTORY_DESCRIPTOR_ROOT = "/proc/self/fd";
11108
+ function isAgentMemorySecureFilesystemAvailable(externalHelperConfigured = false) {
11109
+ 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);
11110
+ return nativeLinux || externalHelperConfigured && process.platform === "darwin";
10812
11111
  }
10813
- function offeredSessionRef(payload) {
10814
- if (!Object.prototype.hasOwnProperty.call(payload, "sessionRef")) return void 0;
10815
- const value = payload.sessionRef;
10816
- return typeof value === "string" ? value : void 0;
11112
+ function requireSecureDirectoryDescriptors() {
11113
+ if (!isAgentMemorySecureFilesystemAvailable(false) || process.platform !== "linux") {
11114
+ throw new AgentMemoryError("Agent memory is unavailable because this Node platform lacks safe descriptor-relative filesystem operations");
11115
+ }
11116
+ return SECURE_DIRECTORY_DESCRIPTOR_ROOT;
10817
11117
  }
10818
- function errorMessage4(err) {
10819
- return err instanceof Error ? err.message : String(err);
11118
+ function noFollowFlags(base) {
11119
+ requireSecureDirectoryDescriptors();
11120
+ return base | constants.O_NOFOLLOW;
10820
11121
  }
10821
- function raceSettleFirst(fn, timeoutMs) {
10822
- return new Promise((resolve) => {
10823
- let settled = false;
10824
- const timer = setTimeout(() => {
10825
- if (!settled) {
10826
- settled = true;
10827
- resolve(false);
10828
- }
10829
- }, timeoutMs);
10830
- timer.unref?.();
10831
- void (async () => {
10832
- try {
10833
- await fn();
10834
- } catch {
10835
- }
10836
- if (!settled) {
10837
- settled = true;
10838
- clearTimeout(timer);
10839
- resolve(true);
10840
- }
10841
- })();
10842
- });
11122
+ function descriptorPath(handle) {
11123
+ return `${requireSecureDirectoryDescriptors()}/${handle.fd}`;
10843
11124
  }
10844
- function estimateEventBytes(event) {
11125
+ async function openPinnedDirectory(target, expectedIdentity) {
11126
+ requireSecureDirectoryDescriptors();
11127
+ let handle;
10845
11128
  try {
10846
- return Buffer.byteLength(JSON.stringify(event), "utf8");
10847
- } catch {
10848
- return 0;
11129
+ handle = await promises.open(target, noFollowFlags(constants.O_RDONLY | constants.O_DIRECTORY));
11130
+ const stat = await handle.stat({ bigint: true });
11131
+ 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");
11132
+ return handle;
11133
+ } catch (error) {
11134
+ await handle?.close().catch(() => {
11135
+ });
11136
+ if (error instanceof AgentMemoryError) throw error;
11137
+ throw new AgentMemoryError("memory directory is unavailable or unsafe");
10849
11138
  }
10850
11139
  }
10851
- async function openArtifact(workspaceDir, name) {
10852
- const realWorkspaceDir = await promises.realpath(workspaceDir).catch(() => workspaceDir);
10853
- const candidate = path3__default.resolve(realWorkspaceDir, name);
10854
- const prefix = realWorkspaceDir.endsWith(path3__default.sep) ? realWorkspaceDir : realWorkspaceDir + path3__default.sep;
10855
- if (candidate !== realWorkspaceDir && !candidate.startsWith(prefix)) {
10856
- return { ok: false, reason: `artifact name "${name}" resolves outside the task workspace \u2014 rejected` };
10857
- }
10858
- let realCandidate = candidate;
11140
+ async function withPinnedDirectory(home, parts, operation, expectedHomeIdentity) {
11141
+ const handles = [];
10859
11142
  try {
10860
- realCandidate = await promises.realpath(candidate);
10861
- } catch {
10862
- }
10863
- if (realCandidate !== realWorkspaceDir && !realCandidate.startsWith(prefix)) {
10864
- return { ok: false, reason: `artifact name "${name}" resolves outside the task workspace \u2014 rejected` };
11143
+ let directory = await openPinnedDirectory(home, expectedHomeIdentity);
11144
+ handles.push(directory);
11145
+ for (const part of parts) {
11146
+ directory = await openPinnedDirectory(`${descriptorPath(directory)}/${part}`);
11147
+ handles.push(directory);
11148
+ }
11149
+ return await operation(directory);
11150
+ } finally {
11151
+ await Promise.all(handles.reverse().map((handle) => handle.close().catch(() => {
11152
+ })));
10865
11153
  }
10866
- const O_NOFOLLOW = constants.O_NOFOLLOW ?? 0;
11154
+ }
11155
+ async function withMemoryParent(context, relativePath, operation) {
11156
+ const parts = relativePath.split("/");
11157
+ const fileName = parts.pop();
11158
+ if (fileName === void 0 || parts.some((part) => !SAFE_SEGMENT.test(part) && part !== ".byok")) throw new AgentMemoryError("memory path is invalid");
11159
+ return withPinnedDirectory(context.canonicalHome, parts, (directory) => operation(directory, fileName), context.homeIdentity);
11160
+ }
11161
+ async function readPinnedFile(directory, fileName, maxBytes = AGENT_MEMORY_MAX_FILE_BYTES) {
10867
11162
  let handle;
10868
11163
  try {
10869
- handle = await promises.open(candidate, constants.O_RDONLY | O_NOFOLLOW);
10870
- } catch (err) {
10871
- return { ok: false, reason: `artifact "${name}" could not be opened: ${errorMessage4(err)}` };
11164
+ handle = await promises.open(
11165
+ `${descriptorPath(directory)}/${fileName}`,
11166
+ noFollowFlags(constants.O_RDONLY | constants.O_NONBLOCK)
11167
+ );
11168
+ } catch (error) {
11169
+ if (error.code === "ENOENT") return Object.freeze({ exists: false, content: "", revision: digest2(""), byteCount: 0 });
11170
+ throw new AgentMemoryError("could not open memory file");
11171
+ }
11172
+ try {
11173
+ const before = await handle.stat({ bigint: true });
11174
+ if (!before.isFile() || before.isSymbolicLink() || before.size > BigInt(maxBytes)) throw new AgentMemoryError("memory file is not a bounded regular file");
11175
+ const bytes = Buffer.alloc(Number(before.size));
11176
+ let offset = 0;
11177
+ while (offset < bytes.length) {
11178
+ const read = await handle.read(bytes, offset, bytes.length - offset, offset);
11179
+ if (read.bytesRead === 0) break;
11180
+ offset += read.bytesRead;
11181
+ }
11182
+ const after = await handle.stat({ bigint: true });
11183
+ if (offset !== bytes.length || after.size !== before.size || after.mtimeNs !== before.mtimeNs || after.ino !== before.ino) throw new AgentMemoryError("memory file changed during read");
11184
+ const content = bytes.toString("utf8");
11185
+ if (!Buffer.from(content, "utf8").equals(bytes)) throw new AgentMemoryError("memory file is not valid UTF-8");
11186
+ return Object.freeze({ exists: true, content, revision: digestBytes(bytes), byteCount: bytes.length });
11187
+ } finally {
11188
+ await handle.close().catch(() => {
11189
+ });
11190
+ }
11191
+ }
11192
+ async function readFile(context, relativePath, maxBytes = AGENT_MEMORY_MAX_FILE_BYTES) {
11193
+ if (context.filesystem !== void 0) return context.filesystem.read(relativePath, maxBytes);
11194
+ return withMemoryParent(context, relativePath, (directory, fileName) => readPinnedFile(directory, fileName, maxBytes));
11195
+ }
11196
+ async function syncDirectory(directory) {
11197
+ try {
11198
+ await directory.sync();
11199
+ } catch (error) {
11200
+ if (!["EINVAL", "EPERM"].includes(error.code ?? "")) throw error;
11201
+ }
11202
+ }
11203
+ async function replaceNative(context, relativePath, expected, content) {
11204
+ const byteCount = encoder2.encode(content).byteLength;
11205
+ if (byteCount > AGENT_MEMORY_MAX_FILE_BYTES) throw new AgentMemoryError("memory content exceeds its bounded file size");
11206
+ return withMemoryParent(context, relativePath, async (directory, fileName) => {
11207
+ const before = await readPinnedFile(directory, fileName);
11208
+ if (before.revision !== expected) throw new AgentMemoryRevisionConflictError(expected, before.revision);
11209
+ const parent = descriptorPath(directory);
11210
+ const temporary = `${parent}/.byok-memory-${randomUUID()}.tmp`;
11211
+ let handle;
11212
+ try {
11213
+ const check = await readPinnedFile(directory, fileName);
11214
+ if (check.revision !== expected || check.exists !== before.exists) throw new AgentMemoryRevisionConflictError(expected, check.revision);
11215
+ handle = await promises.open(temporary, noFollowFlags(constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL), 384);
11216
+ await handle.writeFile(content, "utf8");
11217
+ await handle.sync();
11218
+ await handle.close();
11219
+ handle = void 0;
11220
+ const finalCheck = await readPinnedFile(directory, fileName);
11221
+ if (finalCheck.revision !== expected || finalCheck.exists !== before.exists) throw new AgentMemoryRevisionConflictError(expected, finalCheck.revision);
11222
+ await promises.rename(temporary, `${parent}/${fileName}`);
11223
+ await syncDirectory(directory);
11224
+ return Object.freeze({ exists: true, content, revision: digest2(content), byteCount });
11225
+ } catch (error) {
11226
+ await handle?.close().catch(() => {
11227
+ });
11228
+ await promises.rm(temporary, { force: true }).catch(() => {
11229
+ });
11230
+ if (error instanceof AgentMemoryError) throw error;
11231
+ throw new AgentMemoryError("could not atomically replace memory file");
11232
+ }
11233
+ });
11234
+ }
11235
+ async function replace(context, relativePath, expected, content) {
11236
+ if (context.filesystem !== void 0) return context.filesystem.replace(relativePath, expected, content, AGENT_MEMORY_MAX_FILE_BYTES);
11237
+ return replaceNative(context, relativePath, expected, content);
11238
+ }
11239
+ async function removeNative(context, relativePath, expected) {
11240
+ if (relativePath === "MEMORY.md") throw new AgentMemoryError("MEMORY.md may not be deleted");
11241
+ await withMemoryParent(context, relativePath, async (directory, fileName) => {
11242
+ const before = await readPinnedFile(directory, fileName);
11243
+ if (!before.exists || before.revision !== expected) throw new AgentMemoryRevisionConflictError(expected, before.revision);
11244
+ const parent = descriptorPath(directory);
11245
+ const tombstone = `${parent}/.byok-memory-delete-${randomUUID()}.tmp`;
11246
+ try {
11247
+ const check = await readPinnedFile(directory, fileName);
11248
+ if (!check.exists || check.revision !== expected) throw new AgentMemoryRevisionConflictError(expected, check.revision);
11249
+ await promises.rename(`${parent}/${fileName}`, tombstone);
11250
+ await syncDirectory(directory);
11251
+ await promises.rm(tombstone);
11252
+ await syncDirectory(directory);
11253
+ } catch (error) {
11254
+ if (error instanceof AgentMemoryError) throw error;
11255
+ throw new AgentMemoryError("could not atomically delete memory file");
11256
+ }
11257
+ });
11258
+ }
11259
+ async function remove(context, relativePath, expected) {
11260
+ if (relativePath === "MEMORY.md") throw new AgentMemoryError("MEMORY.md may not be deleted");
11261
+ if (context.filesystem !== void 0) return context.filesystem.delete(relativePath, expected);
11262
+ return removeNative(context, relativePath, expected);
11263
+ }
11264
+ async function readInternalFile(context, fileName) {
11265
+ const relativePath = `${AGENT_HOME_INTERNAL_DIRECTORY}/${fileName}`;
11266
+ if (context.filesystem !== void 0) return context.filesystem.read(relativePath, AGENT_MEMORY_MAX_LOCAL_LOG_BYTES);
11267
+ return withPinnedDirectory(
11268
+ context.canonicalHome,
11269
+ [AGENT_HOME_INTERNAL_DIRECTORY],
11270
+ (directory) => readPinnedFile(directory, fileName, AGENT_MEMORY_MAX_LOCAL_LOG_BYTES),
11271
+ context.homeIdentity
11272
+ );
11273
+ }
11274
+ async function replaceInternalFile(context, fileName, expectedRevision, content) {
11275
+ const byteCount = encoder2.encode(content).byteLength;
11276
+ if (byteCount > AGENT_MEMORY_MAX_LOCAL_LOG_BYTES) throw new AgentMemoryError("Agent memory internal state exceeds its bounded size");
11277
+ const relativePath = `${AGENT_HOME_INTERNAL_DIRECTORY}/${fileName}`;
11278
+ if (context.filesystem !== void 0) return context.filesystem.replace(relativePath, expectedRevision, content, AGENT_MEMORY_MAX_LOCAL_LOG_BYTES);
11279
+ return withPinnedDirectory(context.canonicalHome, [AGENT_HOME_INTERNAL_DIRECTORY], async (directory) => {
11280
+ const before = await readPinnedFile(directory, fileName, AGENT_MEMORY_MAX_LOCAL_LOG_BYTES);
11281
+ if (before.revision !== expectedRevision) throw new AgentMemoryRevisionConflictError(expectedRevision, before.revision);
11282
+ const parent = descriptorPath(directory);
11283
+ const temporary = `${parent}/.byok-agent-memory-${randomUUID()}.tmp`;
11284
+ let handle;
11285
+ try {
11286
+ handle = await promises.open(temporary, noFollowFlags(constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL), 384);
11287
+ await handle.writeFile(content, "utf8");
11288
+ await handle.sync();
11289
+ await handle.close();
11290
+ handle = void 0;
11291
+ const check = await readPinnedFile(directory, fileName, AGENT_MEMORY_MAX_LOCAL_LOG_BYTES);
11292
+ if (check.revision !== expectedRevision || check.exists !== before.exists) throw new AgentMemoryRevisionConflictError(expectedRevision, check.revision);
11293
+ await promises.rename(temporary, `${parent}/${fileName}`);
11294
+ await syncDirectory(directory);
11295
+ return Object.freeze({ exists: true, content, revision: digest2(content), byteCount });
11296
+ } catch (error) {
11297
+ await handle?.close().catch(() => {
11298
+ });
11299
+ await promises.rm(temporary, { force: true }).catch(() => {
11300
+ });
11301
+ if (error instanceof AgentMemoryError) throw error;
11302
+ throw new AgentMemoryError("could not atomically replace Agent memory internal state");
11303
+ }
11304
+ }, context.homeIdentity);
11305
+ }
11306
+ function boundedAuditTail(previous, entry) {
11307
+ if (encoder2.encode(entry).byteLength > AGENT_MEMORY_MAX_LOCAL_LOG_BYTES) throw new AgentMemoryError("Agent memory audit entry exceeds its bounded size");
11308
+ const lines = previous.split("\n").filter((line) => line.length > 0);
11309
+ const kept = [entry];
11310
+ let byteCount = encoder2.encode(entry).byteLength;
11311
+ for (let index = lines.length - 1; index >= 0; index -= 1) {
11312
+ const candidate = `${lines[index]}
11313
+ `;
11314
+ const candidateBytes = encoder2.encode(candidate).byteLength;
11315
+ if (byteCount + candidateBytes > AGENT_MEMORY_MAX_LOCAL_LOG_BYTES) break;
11316
+ kept.unshift(candidate);
11317
+ byteCount += candidateBytes;
11318
+ }
11319
+ return kept.join("");
11320
+ }
11321
+ async function audit(context, kind, values) {
11322
+ 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() })}
11323
+ `;
11324
+ const current = await readInternalFile(context, AGENT_MEMORY_AUDIT_FILENAME);
11325
+ await replaceInternalFile(context, AGENT_MEMORY_AUDIT_FILENAME, current.revision, boundedAuditTail(current.content, entry));
11326
+ }
11327
+ async function recordAuditWarning(context, kind, values) {
11328
+ try {
11329
+ await audit(context, kind, values);
11330
+ return void 0;
11331
+ } catch {
11332
+ return Object.freeze({ code: "agent_memory_audit_unavailable" });
11333
+ }
11334
+ }
11335
+ var agentMemoryHomeQueues = /* @__PURE__ */ new Map();
11336
+ async function exclusiveAgentMemoryHome(home, fn) {
11337
+ const previous = agentMemoryHomeQueues.get(home) ?? Promise.resolve();
11338
+ let release;
11339
+ const next = new Promise((resolve) => {
11340
+ release = resolve;
11341
+ });
11342
+ agentMemoryHomeQueues.set(home, next);
11343
+ await previous;
11344
+ try {
11345
+ return await fn();
11346
+ } finally {
11347
+ release();
11348
+ if (agentMemoryHomeQueues.get(home) === next) agentMemoryHomeQueues.delete(home);
11349
+ }
11350
+ }
11351
+ var AgentMemoryService = class {
11352
+ constructor(input) {
11353
+ this.input = input;
11354
+ }
11355
+ input;
11356
+ async recall(input) {
11357
+ const context = taskContext(this.input);
11358
+ const relativePath = validateAgentMemoryPath(input.path);
11359
+ if (input.ifRevision !== void 0 && !revision(input.ifRevision)) throw new AgentMemoryError("ifRevision must be a sha256 content revision");
11360
+ const current = await readFile(context, relativePath);
11361
+ if (!current.exists) throw new AgentMemoryError("memory file does not exist");
11362
+ if (input.ifRevision !== void 0 && current.revision !== input.ifRevision) throw new AgentMemoryRevisionConflictError(input.ifRevision, current.revision);
11363
+ const auditWarning = await exclusiveAgentMemoryHome(context.canonicalHome, () => recordAuditWarning(context, "recall", {
11364
+ path: relativePath,
11365
+ revision: current.revision,
11366
+ byteCount: current.byteCount
11367
+ }));
11368
+ return Object.freeze({
11369
+ path: relativePath,
11370
+ revision: current.revision,
11371
+ content: current.content,
11372
+ ...auditWarning === void 0 ? {} : { auditWarning }
11373
+ });
11374
+ }
11375
+ async save(input) {
11376
+ const context = taskContext(this.input);
11377
+ const relativePath = validateAgentMemoryPath(input.path);
11378
+ if (input.op !== "replace" && input.op !== "delete" || !revision(input.expectedRevision)) throw new AgentMemoryError("memory save requires op and sha256 expectedRevision");
11379
+ if (input.op === "replace" && typeof input.content !== "string") throw new AgentMemoryError("replace requires string content");
11380
+ if (input.op === "delete" && input.content !== void 0) throw new AgentMemoryError("delete does not accept content");
11381
+ const expectedRevision = input.expectedRevision;
11382
+ const content = input.content;
11383
+ return exclusiveAgentMemoryHome(context.canonicalHome, async () => {
11384
+ if (input.op === "delete") {
11385
+ await remove(context, relativePath, expectedRevision);
11386
+ const auditWarning2 = await recordAuditWarning(context, "save", { path: relativePath, operation: "delete" });
11387
+ return Object.freeze({ path: relativePath, deleted: true, ...auditWarning2 === void 0 ? {} : { auditWarning: auditWarning2 } });
11388
+ }
11389
+ if (typeof content !== "string") throw new AgentMemoryError("replace requires string content");
11390
+ const current = await replace(context, relativePath, expectedRevision, content);
11391
+ const auditWarning = await recordAuditWarning(context, "save", { path: relativePath, operation: "replace", revision: current.revision, byteCount: current.byteCount });
11392
+ return Object.freeze({ path: relativePath, revision: current.revision, deleted: false, ...auditWarning === void 0 ? {} : { auditWarning } });
11393
+ });
11394
+ }
11395
+ };
11396
+ async function memoryNotePaths(context) {
11397
+ if (context.filesystem !== void 0) {
11398
+ const paths = await context.filesystem.walk("notes", AGENT_MEMORY_MAX_SNAPSHOT_ENTRIES);
11399
+ const candidates2 = [];
11400
+ for (const candidate of paths) {
11401
+ try {
11402
+ candidates2.push(validateAgentMemoryPath(candidate));
11403
+ } catch {
11404
+ }
11405
+ }
11406
+ return Object.freeze(candidates2);
11407
+ }
11408
+ const candidates = [];
11409
+ let entriesSeen = 0;
11410
+ async function walk(directory, relativeDirectory) {
11411
+ const entries = await promises.readdir(descriptorPath(directory), { withFileTypes: true });
11412
+ for (const entry of entries) {
11413
+ entriesSeen += 1;
11414
+ if (entriesSeen > AGENT_MEMORY_MAX_SNAPSHOT_ENTRIES) throw new AgentMemoryError("memory snapshot exceeds bounded directory entries");
11415
+ const candidate = `${relativeDirectory}/${entry.name}`;
11416
+ if (entry.isSymbolicLink()) throw new AgentMemoryError("memory notes contains a symlink");
11417
+ if (entry.isDirectory()) {
11418
+ if (SAFE_SEGMENT.test(entry.name) && !SECRET_LIKE.test(entry.name) && entry.name !== ".byok") {
11419
+ const nested = await openPinnedDirectory(`${descriptorPath(directory)}/${entry.name}`);
11420
+ try {
11421
+ await walk(nested, candidate);
11422
+ } finally {
11423
+ await nested.close().catch(() => {
11424
+ });
11425
+ }
11426
+ }
11427
+ continue;
11428
+ }
11429
+ if (!entry.isFile()) continue;
11430
+ try {
11431
+ candidates.push(validateAgentMemoryPath(candidate));
11432
+ } catch {
11433
+ }
11434
+ }
11435
+ }
11436
+ await withPinnedDirectory(context.canonicalHome, ["notes"], (directory) => walk(directory, "notes"), context.homeIdentity);
11437
+ return Object.freeze(candidates);
11438
+ }
11439
+ async function captureAgentMemorySnapshot(input) {
11440
+ const context = taskContext(input);
11441
+ const candidates = ["MEMORY.md", ...await memoryNotePaths(context)];
11442
+ const paths = [...new Set(candidates)].sort((a, b) => a.localeCompare(b));
11443
+ if (paths.length > AGENT_MEMORY_MAX_SNAPSHOT_FILES) throw new AgentMemoryError("memory snapshot exceeds bounded file count");
11444
+ const files = [];
11445
+ let totalBytes = 0;
11446
+ for (const relativePath of paths) {
11447
+ const current = await readFile(context, relativePath);
11448
+ if (!current.exists) {
11449
+ if (relativePath === "MEMORY.md") throw new AgentMemoryError("MEMORY.md disappeared before snapshot");
11450
+ continue;
11451
+ }
11452
+ totalBytes += current.byteCount;
11453
+ if (totalBytes > AGENT_MEMORY_MAX_SNAPSHOT_BYTES) throw new AgentMemoryError("memory snapshot exceeds bounded total size");
11454
+ files.push(Object.freeze({ path: relativePath, revision: current.revision, byteCount: current.byteCount, content: current.content }));
11455
+ }
11456
+ const snapshot = Object.freeze({ files: Object.freeze(files), totalBytes });
11457
+ await exclusiveAgentMemoryHome(context.canonicalHome, () => audit(context, "snapshot", {
11458
+ files: files.map((file) => ({ path: file.path, revision: file.revision, byteCount: file.byteCount }))
11459
+ }));
11460
+ return snapshot;
11461
+ }
11462
+ function bytesEqual(left, right) {
11463
+ if (left.byteLength !== right.byteLength) return false;
11464
+ return left.every((value, index) => value === right[index]);
11465
+ }
11466
+ function rawSnapshotBytes(snapshot) {
11467
+ return encoder2.encode(JSON.stringify({
11468
+ files: snapshot.files.map((file) => ({ path: file.path, revision: file.revision, byteCount: file.byteCount, content: file.content }))
11469
+ }));
11470
+ }
11471
+ function redactedBytes(raw, candidate) {
11472
+ if (!(candidate instanceof Uint8Array)) throw new AgentMemoryError("redactor must return Uint8Array bytes");
11473
+ const bytes = candidate.slice();
11474
+ if (bytes.byteLength > AGENT_MEMORY_PROJECTION_MAX_REDACTED_BYTES) throw new AgentMemoryError("redacted snapshot exceeds bounded projection bytes");
11475
+ if (bytesEqual(bytes, rawSnapshotBytes(raw))) throw new AgentMemoryError("identity/pass-through redactor is forbidden");
11476
+ return bytes;
11477
+ }
11478
+ function isPlainRecord(value) {
11479
+ return typeof value === "object" && value !== null && !Array.isArray(value);
11480
+ }
11481
+ function exactKeys(value, keys) {
11482
+ const actual = Object.keys(value).sort();
11483
+ const expected = [...keys].sort();
11484
+ return actual.length === expected.length && actual.every((key, index) => key === expected[index]);
11485
+ }
11486
+ function validOrderingValue(value, allowZero = false) {
11487
+ return typeof value === "number" && Number.isInteger(value) && value >= (allowZero ? 0 : 1) && value <= AGENT_MEMORY_PROJECTION_MAX_ORDERING_VALUE;
11488
+ }
11489
+ function freezeOutboxRecord(value) {
11490
+ 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");
11491
+ const mutation = AgentMemoryProjectionMutationSchema.safeParse(value.mutation);
11492
+ if (!mutation.success) throw new AgentMemoryError("Agent memory outbox state is invalid");
11493
+ return Object.freeze({ version: 2, mutation: Object.freeze(mutation.data), createdAt: value.createdAt });
11494
+ }
11495
+ function freezeOutboxState(value) {
11496
+ 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");
11497
+ if (value.highWater.length > 1 || value.pending.length > 1) throw new AgentMemoryError("Agent memory outbox state is invalid");
11498
+ const highWater = value.highWater.map((entry) => {
11499
+ if (!isPlainRecord(entry) || !exactKeys(entry, ["writerEpoch", "sourceSeq"]) || !validOrderingValue(entry.writerEpoch) || !validOrderingValue(entry.sourceSeq, true)) throw new AgentMemoryError("Agent memory outbox state is invalid");
11500
+ if (entry.writerEpoch !== value.currentWriterEpoch) throw new AgentMemoryError("Agent memory outbox state is invalid");
11501
+ return Object.freeze({ writerEpoch: entry.writerEpoch, sourceSeq: entry.sourceSeq });
11502
+ }).sort((left, right) => left.writerEpoch - right.writerEpoch);
11503
+ const pending = value.pending.map(freezeOutboxRecord).sort((left, right) => left.mutation.sourceSeq - right.mutation.sourceSeq);
11504
+ const seenEpochs = /* @__PURE__ */ new Set();
11505
+ const highWaterByEpoch = /* @__PURE__ */ new Map();
11506
+ for (const entry of highWater) {
11507
+ if (seenEpochs.has(entry.writerEpoch)) throw new AgentMemoryError("Agent memory outbox state is invalid");
11508
+ seenEpochs.add(entry.writerEpoch);
11509
+ highWaterByEpoch.set(entry.writerEpoch, entry.sourceSeq);
11510
+ }
11511
+ const mutationIds = /* @__PURE__ */ new Set();
11512
+ const sourceSeqs = /* @__PURE__ */ new Set();
11513
+ for (const record of pending) {
11514
+ 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");
11515
+ mutationIds.add(record.mutation.mutationId);
11516
+ sourceSeqs.add(record.mutation.sourceSeq);
11517
+ }
11518
+ return Object.freeze({ version: 2, currentWriterEpoch: value.currentWriterEpoch, highWater: Object.freeze(highWater), pending: Object.freeze(pending) });
11519
+ }
11520
+ function outboxStateJson(state) {
11521
+ return JSON.stringify({ version: state.version, currentWriterEpoch: state.currentWriterEpoch, highWater: state.highWater, pending: state.pending });
11522
+ }
11523
+ function initialOutboxState(writerEpoch) {
11524
+ return freezeOutboxState({ version: 2, currentWriterEpoch: writerEpoch, highWater: [], pending: [] });
11525
+ }
11526
+ function stateWithHighWater(state, writerEpoch, sourceSeq, pending) {
11527
+ if (writerEpoch !== state.currentWriterEpoch) throw new AgentMemoryError("Agent memory outbox state is invalid");
11528
+ return freezeOutboxState({ version: 2, currentWriterEpoch: state.currentWriterEpoch, highWater: [Object.freeze({ writerEpoch, sourceSeq })], pending });
11529
+ }
11530
+ async function publishAgentMemoryProjection(port, mutation) {
11531
+ let timer;
11532
+ try {
11533
+ return await new Promise((resolve, reject) => {
11534
+ timer = setTimeout(
11535
+ () => reject(new AgentMemoryError("Agent memory projection publish timed out")),
11536
+ AGENT_MEMORY_PROJECTION_PUBLISH_TIMEOUT_MS
11537
+ );
11538
+ try {
11539
+ Promise.resolve(port.publish(Object.freeze({ mutation }))).then(resolve, reject);
11540
+ } catch (error) {
11541
+ reject(error);
11542
+ }
11543
+ });
11544
+ } finally {
11545
+ if (timer !== void 0) clearTimeout(timer);
11546
+ }
11547
+ }
11548
+ var AgentMemoryRedactedOutbox = class _AgentMemoryRedactedOutbox {
11549
+ constructor(context, grant, filePath) {
11550
+ this.context = context;
11551
+ this.grant = grant;
11552
+ this.filePath = filePath;
11553
+ }
11554
+ context;
11555
+ grant;
11556
+ filePath;
11557
+ state;
11558
+ fileRevision;
11559
+ writeTail = Promise.resolve();
11560
+ static async open(input, grant) {
11561
+ const context = taskContext(input);
11562
+ const outbox = new _AgentMemoryRedactedOutbox(context, grant, path3__default.join(context.canonicalHome, AGENT_HOME_INTERNAL_DIRECTORY, AGENT_MEMORY_OUTBOX_FILENAME));
11563
+ await outbox.load();
11564
+ await outbox.admitGrant();
11565
+ return outbox;
11566
+ }
11567
+ pending() {
11568
+ return this.state.pending;
11569
+ }
11570
+ async append(bytes) {
11571
+ return this.exclusive(async () => {
11572
+ if (this.state.currentWriterEpoch !== this.grant.writerEpoch) throw new AgentMemoryError("Agent memory outbox writer epoch is stale");
11573
+ if (this.state.pending.length > 0) throw new AgentMemoryError("Agent memory outbox has pending projection mutations");
11574
+ const currentHighWater = this.state.highWater[0]?.sourceSeq ?? 0;
11575
+ if (currentHighWater >= AGENT_MEMORY_PROJECTION_MAX_ORDERING_VALUE) throw new AgentMemoryError("Agent memory outbox source sequence is exhausted");
11576
+ const sourceSeq = currentHighWater + 1;
11577
+ const mutation = AgentMemoryProjectionMutationSchema.parse({
11578
+ taskId: this.context.taskId,
11579
+ agentRef: this.context.agentRef,
11580
+ sessionRef: this.context.sessionRef,
11581
+ runtimeId: this.context.runtimeId,
11582
+ grantRef: this.grant.grantRef,
11583
+ writerEpoch: this.grant.writerEpoch,
11584
+ sourceSeq,
11585
+ mutationId: randomUUID(),
11586
+ policyRevision: this.grant.policyRevision,
11587
+ snapshot: { redactedHash: digestBytes(bytes), redactedByteCount: bytes.byteLength, redactedBytes: Buffer.from(bytes).toString("base64url") }
11588
+ });
11589
+ const record = Object.freeze({ version: 2, mutation: Object.freeze(mutation), createdAt: (/* @__PURE__ */ new Date()).toISOString() });
11590
+ await this.persist(stateWithHighWater(this.state, mutation.writerEpoch, mutation.sourceSeq, [record]));
11591
+ return record;
11592
+ });
11593
+ }
11594
+ async replay(port) {
11595
+ return this.exclusive(async () => {
11596
+ for (const record of this.pending()) {
11597
+ if (!(await publishAgentMemoryProjection(port, record.mutation)).accepted) {
11598
+ return Object.freeze({
11599
+ status: "pending",
11600
+ writerEpoch: record.mutation.writerEpoch,
11601
+ sourceSeq: record.mutation.sourceSeq,
11602
+ mutationId: record.mutation.mutationId
11603
+ });
11604
+ }
11605
+ await this.persist(freezeOutboxState({
11606
+ version: 2,
11607
+ currentWriterEpoch: this.state.currentWriterEpoch,
11608
+ highWater: this.state.highWater,
11609
+ pending: this.state.pending.filter((candidate) => candidate.mutation.mutationId !== record.mutation.mutationId)
11610
+ }));
11611
+ }
11612
+ return DRAINED_AGENT_MEMORY_PROJECTION_REPLAY;
11613
+ });
11614
+ }
11615
+ async load() {
11616
+ const current = await readInternalFile(this.context, AGENT_MEMORY_OUTBOX_FILENAME);
11617
+ this.fileRevision = current.revision;
11618
+ this.state = current.exists ? this.loadBody(current.content) : initialOutboxState(this.grant.writerEpoch);
11619
+ }
11620
+ loadBody(body) {
11621
+ try {
11622
+ return freezeOutboxState(JSON.parse(body));
11623
+ } catch {
11624
+ throw new AgentMemoryError("Agent memory outbox state is invalid");
11625
+ }
11626
+ }
11627
+ async admitGrant() {
11628
+ if (this.grant.writerEpoch < this.state.currentWriterEpoch) throw new AgentMemoryError("Agent memory outbox writer epoch is stale");
11629
+ if (this.grant.writerEpoch === this.state.currentWriterEpoch) return;
11630
+ await this.persist(freezeOutboxState({ version: 2, currentWriterEpoch: this.grant.writerEpoch, highWater: [], pending: [] }));
11631
+ }
11632
+ async persist(next) {
11633
+ const replaced = await replaceInternalFile(this.context, AGENT_MEMORY_OUTBOX_FILENAME, this.fileRevision, outboxStateJson(next));
11634
+ this.fileRevision = replaced.revision;
11635
+ this.state = next;
11636
+ }
11637
+ async exclusive(fn) {
11638
+ const previous = this.writeTail;
11639
+ let release;
11640
+ this.writeTail = new Promise((resolve) => {
11641
+ release = resolve;
11642
+ });
11643
+ await previous;
11644
+ try {
11645
+ return await fn();
11646
+ } finally {
11647
+ release();
11648
+ }
11649
+ }
11650
+ };
11651
+ async function snapshotAndProjectAgentMemory(input, projection) {
11652
+ if (projection?.capability !== AGENT_MEMORY_PROJECTION_CAPABILITY || !projection.grant || !projection.redactor || !projection.port) return;
11653
+ const context = taskContext(input);
11654
+ const outbox = await AgentMemoryRedactedOutbox.open(context, projection.grant);
11655
+ const initialReplay = await outbox.replay(projection.port);
11656
+ if (initialReplay.status === "pending") throw new AgentMemoryProjectionReplayPendingError(initialReplay);
11657
+ const snapshot = await captureAgentMemorySnapshot(context);
11658
+ await outbox.append(redactedBytes(snapshot, await projection.redactor.redact(snapshot)));
11659
+ const trailingReplay = await outbox.replay(projection.port);
11660
+ if (trailingReplay.status === "pending") throw new AgentMemoryProjectionReplayPendingError(trailingReplay);
11661
+ }
11662
+ var AGENT_MEMORY_FILESYSTEM_HELPER_PROTOCOL = 2;
11663
+ var AGENT_MEMORY_FILESYSTEM_HELPER_VERSION = "2";
11664
+ var HELPER_REQUEST_TIMEOUT_MS = 1e4;
11665
+ var HELPER_MAX_JSON_LINE_BYTES = 2 * 1024 * 1024;
11666
+ var HELPER_MAX_CONTENT_BYTES = 1 * 1024 * 1024;
11667
+ var HELPER_MAX_STDERR_BYTES = 4 * 1024;
11668
+ var HelperRevisionConflict = class extends Error {
11669
+ constructor(actualRevision) {
11670
+ super("helper revision conflict");
11671
+ this.actualRevision = actualRevision;
11672
+ }
11673
+ actualRevision;
11674
+ };
11675
+ function safeHelperPath(value) {
11676
+ if (typeof value !== "string" || !path3__default.isAbsolute(value) || value.length === 0 || /[\u0000\r\n]/u.test(value)) {
11677
+ throw new AgentMemoryError("Agent memory filesystem helper must be an explicit absolute executable path");
11678
+ }
11679
+ return path3__default.resolve(value);
11680
+ }
11681
+ function helperPlatformSupported() {
11682
+ return process.platform === "darwin";
11683
+ }
11684
+ function isAgentMemoryFilesystemHelperSupported() {
11685
+ return helperPlatformSupported();
11686
+ }
11687
+ function unixIdentity(homeIdentity) {
11688
+ return Object.freeze({ kind: "unix", dev: homeIdentity.dev.toString(10), ino: homeIdentity.ino.toString(10) });
11689
+ }
11690
+ function responseRecord(value) {
11691
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return void 0;
11692
+ const record = value;
11693
+ if (typeof record.id !== "string" || record.protocol !== AGENT_MEMORY_FILESYSTEM_HELPER_PROTOCOL || typeof record.ok !== "boolean") return void 0;
11694
+ if (record.ok) {
11695
+ if (record.result === null || typeof record.result !== "object" || Array.isArray(record.result)) return void 0;
11696
+ return record;
11697
+ }
11698
+ if (record.error === null || typeof record.error !== "object" || Array.isArray(record.error)) return void 0;
11699
+ const error = record.error;
11700
+ if (typeof error.code !== "string" || typeof error.message !== "string" || error.actualRevision !== void 0 && typeof error.actualRevision !== "string") return void 0;
11701
+ return record;
11702
+ }
11703
+ function boundedFileState(result, maxBytes) {
11704
+ let content;
11705
+ try {
11706
+ if (typeof result.contentBase64 !== "string" || !/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}|[A-Za-z0-9+/]{3})?$/u.test(result.contentBase64)) {
11707
+ throw new Error("invalid base64");
11708
+ }
11709
+ content = Buffer.from(result.contentBase64, "base64").toString("utf8");
11710
+ } catch {
11711
+ throw new AgentMemoryError("Agent memory filesystem helper returned an invalid file state");
11712
+ }
11713
+ 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)) {
11714
+ throw new AgentMemoryError("Agent memory filesystem helper returned an invalid file state");
11715
+ }
11716
+ return Object.freeze({ exists: result.exists, content, revision: result.revision, byteCount: result.byteCount });
11717
+ }
11718
+ function encodeReplaceContent(content, maxBytes) {
11719
+ if (!Number.isSafeInteger(maxBytes) || maxBytes < 0 || maxBytes > HELPER_MAX_CONTENT_BYTES) {
11720
+ throw new AgentMemoryError("Agent memory filesystem helper requested an invalid byte limit");
11721
+ }
11722
+ const bytes = Buffer.from(content, "utf8");
11723
+ if (bytes.byteLength > maxBytes) {
11724
+ throw new AgentMemoryError("Agent memory filesystem helper replacement exceeds its requested byte limit");
11725
+ }
11726
+ return bytes.toString("base64").replace(/=+$/u, "");
11727
+ }
11728
+ var AgentMemoryFilesystemHelperClient = class _AgentMemoryFilesystemHelperClient {
11729
+ constructor(child) {
11730
+ this.child = child;
11731
+ child.stdout.on("data", (chunk) => this.handleStdoutChunk(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)));
11732
+ child.stderr.on("data", (chunk) => {
11733
+ this.stderrBytes += Buffer.byteLength(chunk);
11734
+ if (this.stderrBytes > HELPER_MAX_STDERR_BYTES) this.fail(new AgentMemoryError("Agent memory filesystem helper exceeded bounded stderr"));
11735
+ });
11736
+ child.stdin.on("error", () => this.fail(new AgentMemoryError("Agent memory filesystem helper request could not be written")));
11737
+ child.once("error", () => this.fail(new AgentMemoryError("Agent memory filesystem helper could not be started")));
11738
+ child.once("exit", (code, signal) => {
11739
+ if (!this.closed || code !== 0) this.fail(new AgentMemoryError(`Agent memory filesystem helper exited unexpectedly (${code ?? signal ?? "unknown"})`));
11740
+ });
11741
+ }
11742
+ child;
11743
+ pending = /* @__PURE__ */ new Map();
11744
+ stdoutBuffer = Buffer.alloc(0);
11745
+ sequence = 0;
11746
+ stderrBytes = 0;
11747
+ closed = false;
11748
+ fatalError;
11749
+ static async open(input) {
11750
+ if (!helperPlatformSupported()) throw new AgentMemoryError("Agent memory filesystem helper is not admitted on this platform");
11751
+ const helperBin = safeHelperPath(input.helperBin);
11752
+ const child = spawn(helperBin, ["serve"], {
11753
+ stdio: ["pipe", "pipe", "pipe"],
11754
+ shell: false,
11755
+ windowsHide: true,
11756
+ env: Object.freeze({})
11757
+ });
11758
+ const client = new _AgentMemoryFilesystemHelperClient(child);
11759
+ try {
11760
+ const result = await client.request("open", {
11761
+ root: path3__default.resolve(input.canonicalHome),
11762
+ expectedIdentity: unixIdentity(input.homeIdentity)
11763
+ });
11764
+ if (result.helperVersion !== AGENT_MEMORY_FILESYSTEM_HELPER_VERSION) throw new AgentMemoryError("Agent memory filesystem helper version mismatch");
11765
+ const identity = result.identity;
11766
+ if (identity === null || typeof identity !== "object" || Array.isArray(identity)) throw new AgentMemoryError("Agent memory filesystem helper omitted root identity");
11767
+ const actual = identity;
11768
+ const expected = unixIdentity(input.homeIdentity);
11769
+ if (actual.kind !== expected.kind || actual.dev !== expected.dev || actual.ino !== expected.ino) throw new AgentMemoryError("Agent memory filesystem helper root identity mismatch");
11770
+ return client;
11771
+ } catch (error) {
11772
+ await client.close().catch(() => {
11773
+ });
11774
+ throw error;
11775
+ }
11776
+ }
11777
+ async read(relativePath, maxBytes) {
11778
+ return boundedFileState(await this.request("read", { path: relativePath, maxBytes }), maxBytes);
11779
+ }
11780
+ async replace(relativePath, expectedRevision, content, maxBytes) {
11781
+ try {
11782
+ const contentBase64 = encodeReplaceContent(content, maxBytes);
11783
+ return boundedFileState(await this.request("replace", { path: relativePath, expectedRevision, contentBase64, maxBytes }), maxBytes);
11784
+ } catch (error) {
11785
+ if (error instanceof HelperRevisionConflict) throw new AgentMemoryRevisionConflictError(expectedRevision, error.actualRevision);
11786
+ throw error;
11787
+ }
11788
+ }
11789
+ async delete(relativePath, expectedRevision) {
11790
+ try {
11791
+ await this.request("delete", { path: relativePath, expectedRevision });
11792
+ } catch (error) {
11793
+ if (error instanceof HelperRevisionConflict) throw new AgentMemoryRevisionConflictError(expectedRevision, error.actualRevision);
11794
+ throw error;
11795
+ }
11796
+ }
11797
+ async append(relativePath, content, maxBytes) {
11798
+ await this.request("append", { path: relativePath, content, maxBytes });
11799
+ }
11800
+ async walk(relativePath, maxEntries) {
11801
+ const result = await this.request("walk", { path: relativePath, maxEntries });
11802
+ if (!Array.isArray(result.paths) || result.paths.length > maxEntries || result.paths.some((candidate) => typeof candidate !== "string")) {
11803
+ throw new AgentMemoryError("Agent memory filesystem helper returned an invalid walk result");
11804
+ }
11805
+ return Object.freeze([...result.paths]);
11806
+ }
11807
+ async close() {
11808
+ if (this.closed) return;
11809
+ this.closed = true;
11810
+ try {
11811
+ if (this.child.exitCode === null && this.child.signalCode === null && this.fatalError === void 0) await this.requestWhileClosing("close", {});
11812
+ } finally {
11813
+ this.child.stdin.end();
11814
+ if (this.child.exitCode === null && this.child.signalCode === null) this.child.kill();
11815
+ this.rejectPending(new AgentMemoryError("Agent memory filesystem helper is closed"));
11816
+ }
11817
+ }
11818
+ request(op, fields) {
11819
+ if (this.closed) return Promise.reject(new AgentMemoryError("Agent memory filesystem helper is closed"));
11820
+ return this.requestInternal(op, fields);
11821
+ }
11822
+ requestWhileClosing(op, fields) {
11823
+ return this.requestInternal(op, fields);
11824
+ }
11825
+ requestInternal(op, fields) {
11826
+ if (this.fatalError !== void 0) return Promise.reject(this.fatalError);
11827
+ const id = `m${++this.sequence}`;
11828
+ let line;
11829
+ try {
11830
+ line = `${JSON.stringify({ id, protocol: AGENT_MEMORY_FILESYSTEM_HELPER_PROTOCOL, op, ...fields })}
11831
+ `;
11832
+ } catch {
11833
+ return Promise.reject(new AgentMemoryError("Agent memory filesystem helper request could not be encoded"));
11834
+ }
11835
+ if (Buffer.byteLength(line, "utf8") > HELPER_MAX_JSON_LINE_BYTES) {
11836
+ return Promise.reject(new AgentMemoryError("Agent memory filesystem helper request exceeded bounded stdin"));
11837
+ }
11838
+ return new Promise((resolve, reject) => {
11839
+ const timer = setTimeout(() => {
11840
+ this.pending.delete(id);
11841
+ const error = new AgentMemoryError("Agent memory filesystem helper request timed out");
11842
+ reject(error);
11843
+ this.fail(error);
11844
+ }, HELPER_REQUEST_TIMEOUT_MS);
11845
+ timer.unref?.();
11846
+ this.pending.set(id, { resolve: (response) => resolve(response.result), reject, timer });
11847
+ this.child.stdin.write(line, (error) => {
11848
+ if (!error) return;
11849
+ const item = this.pending.get(id);
11850
+ if (item === void 0) return;
11851
+ clearTimeout(item.timer);
11852
+ this.pending.delete(id);
11853
+ item.reject(new AgentMemoryError("Agent memory filesystem helper request could not be written"));
11854
+ });
11855
+ });
11856
+ }
11857
+ handleStdoutChunk(chunk) {
11858
+ let offset = 0;
11859
+ while (offset < chunk.length && this.fatalError === void 0) {
11860
+ const newline = chunk.indexOf(10, offset);
11861
+ const end = newline === -1 ? chunk.length : newline;
11862
+ const segment = chunk.subarray(offset, end);
11863
+ if (this.stdoutBuffer.length + segment.length > HELPER_MAX_JSON_LINE_BYTES) {
11864
+ this.fail(new AgentMemoryError("Agent memory filesystem helper exceeded bounded stdout"));
11865
+ return;
11866
+ }
11867
+ this.stdoutBuffer = this.stdoutBuffer.length === 0 ? Buffer.from(segment) : Buffer.concat([this.stdoutBuffer, segment]);
11868
+ if (newline === -1) return;
11869
+ const line = this.stdoutBuffer;
11870
+ this.stdoutBuffer = Buffer.alloc(0);
11871
+ this.handleLine(line);
11872
+ offset = newline + 1;
11873
+ }
11874
+ }
11875
+ handleLine(line) {
11876
+ let parsed;
11877
+ try {
11878
+ parsed = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(line));
11879
+ } catch {
11880
+ this.fail(new AgentMemoryError("Agent memory filesystem helper returned malformed JSON"));
11881
+ return;
11882
+ }
11883
+ const response = responseRecord(parsed);
11884
+ if (response === void 0) {
11885
+ this.fail(new AgentMemoryError("Agent memory filesystem helper returned an invalid response"));
11886
+ return;
11887
+ }
11888
+ const item = this.pending.get(response.id);
11889
+ if (item === void 0) {
11890
+ this.fail(new AgentMemoryError("Agent memory filesystem helper returned an unsolicited response"));
11891
+ return;
11892
+ }
11893
+ clearTimeout(item.timer);
11894
+ this.pending.delete(response.id);
11895
+ if (response.ok) {
11896
+ item.resolve(response);
11897
+ return;
11898
+ }
11899
+ if (response.error.code === "revision_conflict" && typeof response.error.actualRevision === "string") {
11900
+ item.reject(new HelperRevisionConflict(response.error.actualRevision));
11901
+ return;
11902
+ }
11903
+ item.reject(new AgentMemoryError(`Agent memory filesystem helper rejected the operation: ${response.error.code}`));
11904
+ }
11905
+ fail(error) {
11906
+ if (this.fatalError !== void 0) return;
11907
+ this.fatalError = error;
11908
+ this.rejectPending(error);
11909
+ if (this.child.exitCode === null && this.child.signalCode === null) this.child.kill();
11910
+ }
11911
+ rejectPending(error) {
11912
+ for (const item of this.pending.values()) {
11913
+ clearTimeout(item.timer);
11914
+ item.reject(error);
11915
+ }
11916
+ this.pending.clear();
11917
+ }
11918
+ };
11919
+ async function openAgentMemoryFilesystemHelper(input) {
11920
+ return AgentMemoryFilesystemHelperClient.open(input);
11921
+ }
11922
+
11923
+ // src/daemon/task-runner.ts
11924
+ var DEFAULT_APPROVAL_TIMEOUT_MS = 10 * 6e4;
11925
+ var DEFAULT_SHUTDOWN_INTERRUPT_TIMEOUT_MS = 5e3;
11926
+ var AGENT_TERMINAL_EVIDENCE_MAX_ATTEMPTS = 3;
11927
+ var AGENT_TERMINAL_EVIDENCE_RETRY_DELAY_MS = 20;
11928
+ var GIT_OBSERVATION_TIMEOUT_MS = 5e3;
11929
+ var MAX_PENDING_APPROVALS_PER_TASK = 16;
11930
+ var NoPendingApprovalError = class extends Error {
11931
+ constructor(taskId) {
11932
+ super(`no pending out-of-band approval to resolve for task ${taskId}`);
11933
+ this.taskId = taskId;
11934
+ this.name = "NoPendingApprovalError";
11935
+ }
11936
+ taskId;
11937
+ };
11938
+ var MAX_INLINE_ARTIFACT_BYTES = 64 * 1024;
11939
+ var MAX_TRACKED_TASK_IDS = 2e3;
11940
+ var MAX_DURATION_EXCEEDED_REASON_PREFIX = "resource limit exceeded: maxDurationMs";
11941
+ var MAX_OUTPUT_BYTES_EXCEEDED_REASON_PREFIX = "resource limit exceeded: maxTaskOutputBytes";
11942
+ var MAX_PROGRESS_BATCH_BYTES_EXCEEDED_REASON_PREFIX = "resource limit exceeded: progressBatch.maxBatchBytes";
11943
+ var RESULT_DOCUMENT_UNDELIVERABLE_REASON_PREFIX = "result document undeliverable";
11944
+ function resultDocumentRejectionDetail(check) {
11945
+ switch (check.reason) {
11946
+ case "over-cap":
11947
+ 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)`;
11948
+ case "not-serializable":
11949
+ return "not JSON-serializable (JSON.stringify threw, or produced no output at all)";
11950
+ case "not-plain-json":
11951
+ 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)";
11952
+ default: {
11953
+ const exhaustive = check;
11954
+ throw new Error(`unhandled result document rejection: ${JSON.stringify(exhaustive)}`);
11955
+ }
11956
+ }
11957
+ }
11958
+ var DEFAULT_MAX_TASK_OUTPUT_BYTES = 64 * 1024 * 1024;
11959
+ function isKnownRuntimeId(id) {
11960
+ return RuntimeIdSchema.safeParse(id).success;
11961
+ }
11962
+ function terminalUsageNumber(value, maximum) {
11963
+ return value !== void 0 && Number.isSafeInteger(value) && value >= 0 && value <= maximum ? value : void 0;
11964
+ }
11965
+ var DEFAULT_RUNTIME_PREFERENCE = ["claude", "codex", "pi"];
11966
+ function orderByPreference(candidates, preference) {
11967
+ const rank = new Map(preference.map((id, index) => [id, index]));
11968
+ return [...candidates].sort((a, b) => (rank.get(a.descriptor.id) ?? preference.length) - (rank.get(b.descriptor.id) ?? preference.length));
11969
+ }
11970
+ function adapterSupportsMode(descriptor, mode) {
11971
+ return descriptor.capabilities.permissionModes.includes(mode);
11972
+ }
11973
+ function adapterSupportsMcpToolsets(descriptor) {
11974
+ return descriptor.capabilities.mcpToolsets === true;
11975
+ }
11976
+ function withoutRequiredToolsets(payload) {
11977
+ const { requiredToolsets, egressPolicy, messageEgress, ...offer } = payload;
11978
+ return offer;
11979
+ }
11980
+ function sameEgressPolicy(left, right) {
11981
+ return JSON.stringify(left) === JSON.stringify(right);
11982
+ }
11983
+ function offeredAgentRef(payload) {
11984
+ if (!Object.prototype.hasOwnProperty.call(payload, "agentRef")) return void 0;
11985
+ return validateAgentRef(payload.agentRef);
11986
+ }
11987
+ function offeredSessionRef(payload) {
11988
+ if (!Object.prototype.hasOwnProperty.call(payload, "sessionRef")) return void 0;
11989
+ const value = payload.sessionRef;
11990
+ return typeof value === "string" ? value : void 0;
11991
+ }
11992
+ function errorMessage4(err) {
11993
+ return err instanceof Error ? err.message : String(err);
11994
+ }
11995
+ function raceSettleFirst(fn, timeoutMs) {
11996
+ return new Promise((resolve) => {
11997
+ let settled = false;
11998
+ const timer = setTimeout(() => {
11999
+ if (!settled) {
12000
+ settled = true;
12001
+ resolve(false);
12002
+ }
12003
+ }, timeoutMs);
12004
+ timer.unref?.();
12005
+ void (async () => {
12006
+ try {
12007
+ await fn();
12008
+ } catch {
12009
+ }
12010
+ if (!settled) {
12011
+ settled = true;
12012
+ clearTimeout(timer);
12013
+ resolve(true);
12014
+ }
12015
+ })();
12016
+ });
12017
+ }
12018
+ function estimateEventBytes(event) {
12019
+ try {
12020
+ return Buffer.byteLength(JSON.stringify(event), "utf8");
12021
+ } catch {
12022
+ return 0;
12023
+ }
12024
+ }
12025
+ async function openArtifact(workspaceDir, name) {
12026
+ const realWorkspaceDir = await promises.realpath(workspaceDir).catch(() => workspaceDir);
12027
+ const candidate = path3__default.resolve(realWorkspaceDir, name);
12028
+ const prefix = realWorkspaceDir.endsWith(path3__default.sep) ? realWorkspaceDir : realWorkspaceDir + path3__default.sep;
12029
+ if (candidate !== realWorkspaceDir && !candidate.startsWith(prefix)) {
12030
+ return { ok: false, reason: `artifact name "${name}" resolves outside the task workspace \u2014 rejected` };
12031
+ }
12032
+ let realCandidate = candidate;
12033
+ try {
12034
+ realCandidate = await promises.realpath(candidate);
12035
+ } catch {
12036
+ }
12037
+ if (realCandidate !== realWorkspaceDir && !realCandidate.startsWith(prefix)) {
12038
+ return { ok: false, reason: `artifact name "${name}" resolves outside the task workspace \u2014 rejected` };
12039
+ }
12040
+ const O_NOFOLLOW = constants.O_NOFOLLOW ?? 0;
12041
+ let handle;
12042
+ try {
12043
+ handle = await promises.open(candidate, constants.O_RDONLY | O_NOFOLLOW);
12044
+ } catch (err) {
12045
+ return { ok: false, reason: `artifact "${name}" could not be opened: ${errorMessage4(err)}` };
10872
12046
  }
10873
12047
  try {
10874
12048
  const st = await handle.stat();
@@ -10890,6 +12064,16 @@ var TaskRunner = class {
10890
12064
  }
10891
12065
  deps;
10892
12066
  tasks = /* @__PURE__ */ new Map();
12067
+ pendingMessageTasks = /* @__PURE__ */ new Map();
12068
+ messageContextByToken = /* @__PURE__ */ new Map();
12069
+ messageContextByTask = /* @__PURE__ */ new Map();
12070
+ memoryContextByToken = /* @__PURE__ */ new Map();
12071
+ memoryContextByTask = /* @__PURE__ */ new Map();
12072
+ memoryInFlightByTask = /* @__PURE__ */ new Map();
12073
+ memoryClosingTasks = /* @__PURE__ */ new Set();
12074
+ memoryFilesystemByTask = /* @__PURE__ */ new Map();
12075
+ recoveredMessageOutboxes = /* @__PURE__ */ new Map();
12076
+ recoveredMessageRetryTimers = /* @__PURE__ */ new Map();
10893
12077
  /**
10894
12078
  * Finding F4 (cancel lost during the offer-processing window): a
10895
12079
  * `task.cancel` for a taskId that hasn't finished `handleOffer` yet (still
@@ -11029,6 +12213,128 @@ var TaskRunner = class {
11029
12213
  pendingApprovals: (active.pendingApprovalId !== void 0 ? 1 : 0) + active.approvalQueue.length
11030
12214
  }));
11031
12215
  }
12216
+ /** Authenticated control-socket entry used only by the SDK-owned task MCP helper. */
12217
+ async publishAgentMessage(input) {
12218
+ const taskId = this.messageContextByToken.get(input.contextToken);
12219
+ if (taskId === void 0 || this.messageContextByTask.get(taskId) !== input.contextToken) {
12220
+ throw new Error("invalid or expired Agent message task context");
12221
+ }
12222
+ const pending = this.pendingMessageTasks.get(taskId);
12223
+ const active = this.tasks.get(taskId);
12224
+ const context = pending ?? (active?.messageRequirement && active.agentRef && active.messageOutbox ? { agentRef: active.agentRef, requirement: active.messageRequirement, outbox: active.messageOutbox } : void 0);
12225
+ if (context === void 0) throw new Error("task has no active required Agent message contract");
12226
+ const record = await context.outbox.appendDraft({
12227
+ taskId,
12228
+ tenantId: this.deps.tenantId,
12229
+ agentRef: context.agentRef,
12230
+ requirement: context.requirement,
12231
+ contentType: input.contentType,
12232
+ body: input.body,
12233
+ maxPendingEvents: 64,
12234
+ maxPendingBytes: 4 * 1024 * 1024
12235
+ });
12236
+ if (active === void 0) return { messageId: record.messageId, state: "staged" };
12237
+ const activated = await context.outbox.activate(taskId, active.session.sessionRef);
12238
+ if (activated === void 0) throw new Error("Agent message draft disappeared before activation");
12239
+ this.sendAgentMessageRecord(context.outbox, activated);
12240
+ return { messageId: activated.messageId, state: "pending" };
12241
+ }
12242
+ /** Authenticated control-socket entry used only by the SDK-owned memory MCP helper. */
12243
+ async recallAgentMemory(input) {
12244
+ return this.runMemoryOperation(input.contextToken, (context) => new AgentMemoryService(context).recall(input));
12245
+ }
12246
+ /** Authenticated control-socket entry used only by the SDK-owned memory MCP helper. */
12247
+ async saveAgentMemory(input) {
12248
+ return this.runMemoryOperation(input.contextToken, (context) => new AgentMemoryService(context).save(input));
12249
+ }
12250
+ /** Restore activated, unaccepted message drafts before transport admission on daemon restart. */
12251
+ async recoverAgentMessageOutboxes(agentsRoot) {
12252
+ if (this.deps.tenantId === void 0) throw new Error("Agent message recovery requires authenticated tenant enrollment");
12253
+ for (const outbox of await AgentMessageOutbox.recover(agentsRoot, this.deps.tenantId)) {
12254
+ for (const record of outbox.retryableRecords()) {
12255
+ if (record.sessionRef === void 0) continue;
12256
+ const existing = this.recoveredMessageOutboxes.get(record.taskId);
12257
+ if (existing !== void 0 && existing !== outbox) throw new Error(`multiple Agent message outboxes claim task ${record.taskId}`);
12258
+ this.recoveredMessageOutboxes.set(record.taskId, outbox);
12259
+ }
12260
+ }
12261
+ }
12262
+ /** Retry stable recovered records after a transport handshake/re-handshake. */
12263
+ retryRecoveredAgentMessages() {
12264
+ for (const [taskId, outbox] of this.recoveredMessageOutboxes) {
12265
+ const record = outbox.get(taskId);
12266
+ if (record?.sessionRef !== void 0) this.sendAgentMessageRecord(outbox, record);
12267
+ }
12268
+ }
12269
+ sendAgentMessageRecord(outbox, record) {
12270
+ this.deps.send(createEnvelope("agent.message.publish", outbox.publishPayload(record), {
12271
+ taskId: record.taskId,
12272
+ sessionRef: record.sessionRef
12273
+ }));
12274
+ const active = this.tasks.get(record.taskId);
12275
+ if (active?.messageOutbox === outbox) {
12276
+ if (active.messageRetryTimer !== void 0) clearTimeout(active.messageRetryTimer);
12277
+ active.messageRetryTimer = setTimeout(() => {
12278
+ active.messageRetryTimer = void 0;
12279
+ if (this.tasks.get(record.taskId) !== active) return;
12280
+ const pending = outbox.get(record.taskId);
12281
+ if (pending !== void 0) this.sendAgentMessageRecord(outbox, pending);
12282
+ }, 1e3);
12283
+ active.messageRetryTimer.unref?.();
12284
+ return;
12285
+ }
12286
+ if (this.recoveredMessageOutboxes.get(record.taskId) === outbox) {
12287
+ const prior = this.recoveredMessageRetryTimers.get(record.taskId);
12288
+ if (prior !== void 0) clearTimeout(prior);
12289
+ const timer = setTimeout(() => {
12290
+ this.recoveredMessageRetryTimers.delete(record.taskId);
12291
+ const pending = outbox.get(record.taskId);
12292
+ if (pending !== void 0 && this.recoveredMessageOutboxes.get(record.taskId) === outbox) {
12293
+ this.sendAgentMessageRecord(outbox, pending);
12294
+ }
12295
+ }, 1e3);
12296
+ timer.unref?.();
12297
+ this.recoveredMessageRetryTimers.set(record.taskId, timer);
12298
+ }
12299
+ }
12300
+ async handleAgentMessageDisposition(taskId, disposition) {
12301
+ const active = this.tasks.get(taskId);
12302
+ const outbox = active?.messageOutbox ?? this.recoveredMessageOutboxes.get(taskId);
12303
+ if (outbox === void 0) return;
12304
+ const outcome = await outbox.applyDisposition(taskId, disposition);
12305
+ if (active === void 0) {
12306
+ if (outcome === "accepted" || outcome === "held" || outcome === "refused") {
12307
+ const timer = this.recoveredMessageRetryTimers.get(taskId);
12308
+ if (timer !== void 0) clearTimeout(timer);
12309
+ this.recoveredMessageRetryTimers.delete(taskId);
12310
+ }
12311
+ if (outcome === "accepted") this.recoveredMessageOutboxes.delete(taskId);
12312
+ return;
12313
+ }
12314
+ if (outcome === "held") {
12315
+ if (active.messageRetryTimer !== void 0) clearTimeout(active.messageRetryTimer);
12316
+ active.messageRetryTimer = void 0;
12317
+ return;
12318
+ }
12319
+ if (outcome === "refused") {
12320
+ if (active.messageRetryTimer !== void 0) clearTimeout(active.messageRetryTimer);
12321
+ active.messageRetryTimer = void 0;
12322
+ this.revokeAgentMessageContext(taskId);
12323
+ return;
12324
+ }
12325
+ if (outcome !== "accepted") return;
12326
+ if (active.messageRetryTimer !== void 0) {
12327
+ clearTimeout(active.messageRetryTimer);
12328
+ active.messageRetryTimer = void 0;
12329
+ }
12330
+ active.messageAccepted = true;
12331
+ this.revokeAgentMessageContext(taskId);
12332
+ if (active.pendingMessageCompletion !== void 0) {
12333
+ const completion = active.pendingMessageCompletion;
12334
+ active.pendingMessageCompletion = void 0;
12335
+ await this.publishSuccessfulCompletion(active, completion.finalOutput, completion.document);
12336
+ }
12337
+ }
11032
12338
  /** M4 Phase 2: stop claiming any FUTURE `task.offer` — see `stoppingOffers`'s own doc comment. Idempotent. */
11033
12339
  stopAcceptingOffers() {
11034
12340
  this.stoppingOffers = true;
@@ -11204,6 +12510,9 @@ var TaskRunner = class {
11204
12510
  case "task.reject":
11205
12511
  await this.handleReject(envelope.task_id, envelope.payload.reason, envelope.payload.approvalId);
11206
12512
  return;
12513
+ case "agent.message.disposition":
12514
+ await this.handleAgentMessageDisposition(envelope.task_id, envelope.payload);
12515
+ return;
11207
12516
  default:
11208
12517
  return;
11209
12518
  }
@@ -11234,12 +12543,22 @@ var TaskRunner = class {
11234
12543
  this.decline(taskId, reason, retryable, agentRef);
11235
12544
  };
11236
12545
  const sessionRef = offeredSessionRef(payload);
12546
+ const messageRequirement = "messageEgress" in payload ? payload.messageEgress : void 0;
12547
+ const terminalProjection = "terminalProjection" in payload ? payload.terminalProjection : void 0;
11237
12548
  if ("egressPolicy" in payload) {
11238
12549
  if (this.deps.agentEgressPolicy === void 0 || !sameEgressPolicy(this.deps.agentEgressPolicy, payload.egressPolicy)) {
11239
12550
  decline("Agent egress offer policy is not exactly enabled by this daemon", false);
11240
12551
  return;
11241
12552
  }
11242
12553
  }
12554
+ if (messageRequirement !== void 0 && (agentRef === void 0 || this.deps.agentMessageMcpBin === void 0 || this.deps.tenantId === void 0)) {
12555
+ decline("required Agent message egress is unavailable on this daemon", false);
12556
+ return;
12557
+ }
12558
+ if (terminalProjection?.mode === "result-document" && this.deps.resultDocument === void 0) {
12559
+ decline("offer requires a result document but this daemon has no resultDocument extractor", false);
12560
+ return;
12561
+ }
11243
12562
  this.inFlightOffers.add(taskId);
11244
12563
  let agentBinding;
11245
12564
  let agentLeaseTransferred = false;
@@ -11298,11 +12617,29 @@ var TaskRunner = class {
11298
12617
  }
11299
12618
  const offered = withoutRequiredToolsets(payload);
11300
12619
  const requestedRuntime = payload.dispatchSelection?.runtimeId ?? payload.runtime;
11301
- const pick = await this.pickAdapter(requestedRuntime, payload.policy.mode, requiredToolsets !== void 0);
12620
+ const requiresAgentMemoryMcp = agentRef !== void 0 && this.deps.agentMemoryMcpBin !== void 0 && isAgentMemorySecureFilesystemAvailable(this.deps.agentMemoryFilesystemHelperBin !== void 0);
12621
+ const pick = await this.pickAdapter(
12622
+ requestedRuntime,
12623
+ payload.policy.mode,
12624
+ requiredToolsets !== void 0 || messageRequirement !== void 0 || requiresAgentMemoryMcp
12625
+ );
11302
12626
  if (!pick.ok) {
11303
12627
  decline(pick.reason, pick.retryable);
11304
12628
  return;
11305
12629
  }
12630
+ let taskMcpServers = this.withAgentMessageMcp(
12631
+ resolvedMcp?.ok ? resolvedMcp.servers : void 0,
12632
+ taskId,
12633
+ messageRequirement
12634
+ );
12635
+ if (requiresAgentMemoryMcp && agentRef !== void 0) {
12636
+ try {
12637
+ taskMcpServers = this.withAgentMemoryMcp(taskMcpServers, taskId, agentRef);
12638
+ } catch (error) {
12639
+ decline(`Agent memory MCP configuration failed: ${errorMessage4(error)}`, false);
12640
+ return;
12641
+ }
12642
+ }
11306
12643
  let prepared;
11307
12644
  try {
11308
12645
  prepared = await pick.adapter.prepare({
@@ -11310,7 +12647,7 @@ var TaskRunner = class {
11310
12647
  policy: decision.policy,
11311
12648
  descriptor: pick.descriptor,
11312
12649
  requiredToolsetIds: requiredToolsets ?? [],
11313
- ...resolvedMcp?.ok ? { mcpServers: resolvedMcp.servers } : {}
12650
+ ...taskMcpServers === void 0 ? {} : { mcpServers: taskMcpServers }
11314
12651
  });
11315
12652
  } catch (error) {
11316
12653
  decline(`runtime preparation failed: ${errorMessage4(error)}`, true);
@@ -11365,6 +12702,23 @@ var TaskRunner = class {
11365
12702
  decline(`Agent home initialization failed: ${errorMessage4(error)}`, false);
11366
12703
  return;
11367
12704
  }
12705
+ if (messageRequirement !== void 0) {
12706
+ try {
12707
+ const outbox = await AgentMessageOutbox.open(agentBinding.resolution.canonicalHome);
12708
+ this.pendingMessageTasks.set(taskId, {
12709
+ taskId,
12710
+ agentRef: agentBinding.resolution.agentRef,
12711
+ requirement: messageRequirement,
12712
+ outbox
12713
+ });
12714
+ } catch (error) {
12715
+ await agentBinding.lease.release().catch(() => {
12716
+ });
12717
+ agentBinding = void 0;
12718
+ decline(`Agent message outbox initialization failed: ${errorMessage4(error)}`, true);
12719
+ return;
12720
+ }
12721
+ }
11368
12722
  } else if (this.deps.gitWorkspaceManager && this.deps.gitWorkspaceStore) {
11369
12723
  known = sessionRef ? await this.deps.sessionWorkspaces.get(sessionRef) : void 0;
11370
12724
  const gitManager = this.deps.gitWorkspaceManager;
@@ -11548,9 +12902,9 @@ var TaskRunner = class {
11548
12902
  }
11549
12903
  const startInput = {
11550
12904
  manifest,
11551
- instruction: gitWorkspaceId ? prependGitWorkspaceGuidance(resolvedInstruction) : resolvedInstruction,
12905
+ instruction: agentBinding === void 0 ? gitWorkspaceId ? prependGitWorkspaceGuidance(resolvedInstruction) : resolvedInstruction : prependAgentMemoryGuidance(resolvedInstruction),
11552
12906
  env,
11553
- ...resolvedMcp?.ok ? { mcpServers: resolvedMcp.servers } : {},
12907
+ ...taskMcpServers === void 0 ? {} : { mcpServers: taskMcpServers },
11554
12908
  approvalChannel: {
11555
12909
  taskId,
11556
12910
  storeDir: this.deps.storeDir,
@@ -11620,7 +12974,12 @@ var TaskRunner = class {
11620
12974
  ),
11621
12975
  approvalQueue: [],
11622
12976
  outputBytesSoFar: 0,
11623
- startedAtMs: Date.now()
12977
+ startedAtMs: Date.now(),
12978
+ ...messageRequirement === void 0 ? {} : {
12979
+ messageRequirement,
12980
+ messageOutbox: this.pendingMessageTasks.get(taskId).outbox
12981
+ },
12982
+ ...terminalProjection === void 0 ? {} : { terminalProjection }
11624
12983
  };
11625
12984
  if (agentBinding !== void 0) {
11626
12985
  try {
@@ -11648,6 +13007,7 @@ var TaskRunner = class {
11648
13007
  return;
11649
13008
  }
11650
13009
  }
13010
+ const activatedMessageRecord = active.messageOutbox === void 0 ? void 0 : await active.messageOutbox.activate(taskId, session.sessionRef);
11651
13011
  if (this.pendingCancelled.has(taskId)) {
11652
13012
  const reason = this.pendingCancelled.get(taskId);
11653
13013
  this.pendingCancelled.delete(taskId);
@@ -11673,6 +13033,10 @@ var TaskRunner = class {
11673
13033
  this.deps.send(createEnvelope("task.started", {}, { taskId }));
11674
13034
  agentLeaseTransferred = agentBinding !== void 0;
11675
13035
  this.tasks.set(taskId, active);
13036
+ this.pendingMessageTasks.delete(taskId);
13037
+ if (active.messageOutbox !== void 0 && activatedMessageRecord !== void 0) {
13038
+ this.sendAgentMessageRecord(active.messageOutbox, activatedMessageRecord);
13039
+ }
11676
13040
  if (payload.limits?.maxDurationMs !== void 0) {
11677
13041
  this.armMaxDurationTimer(active, payload.limits.maxDurationMs);
11678
13042
  }
@@ -11690,6 +13054,11 @@ var TaskRunner = class {
11690
13054
  });
11691
13055
  }
11692
13056
  } finally {
13057
+ if (!agentLeaseTransferred) {
13058
+ this.pendingMessageTasks.delete(taskId);
13059
+ this.revokeAgentMessageContext(taskId);
13060
+ this.revokeAgentMemoryContext(taskId);
13061
+ }
11693
13062
  if (agentBinding !== void 0 && !agentLeaseTransferred) {
11694
13063
  await agentBinding.lease.release().catch(() => {
11695
13064
  });
@@ -11697,6 +13066,153 @@ var TaskRunner = class {
11697
13066
  this.inFlightOffers.delete(taskId);
11698
13067
  }
11699
13068
  }
13069
+ withAgentMessageMcp(existing, taskId, requirement) {
13070
+ if (requirement === void 0) return existing;
13071
+ const bin = this.deps.agentMessageMcpBin;
13072
+ const contextToken = `${randomUUID()}.${randomUUID()}`;
13073
+ this.revokeAgentMessageContext(taskId);
13074
+ this.messageContextByToken.set(contextToken, taskId);
13075
+ this.messageContextByTask.set(taskId, contextToken);
13076
+ return Object.freeze({
13077
+ ...existing ?? {},
13078
+ [AGENT_MESSAGE_MCP_SERVER_NAME]: Object.freeze({
13079
+ command: bin.command,
13080
+ args: Object.freeze([...bin.args]),
13081
+ env: Object.freeze({
13082
+ BYOK_STORE_DIR: this.deps.storeDir,
13083
+ BYOK_PRODUCT_ID: this.deps.productId,
13084
+ BYOK_AGENT_MESSAGE_CONTEXT: contextToken
13085
+ })
13086
+ })
13087
+ });
13088
+ }
13089
+ revokeAgentMessageContext(taskId) {
13090
+ const token = this.messageContextByTask.get(taskId);
13091
+ if (token !== void 0) this.messageContextByToken.delete(token);
13092
+ this.messageContextByTask.delete(taskId);
13093
+ }
13094
+ /** Injected only after strict Agent admission; a host registry may never replace this reserved name. */
13095
+ withAgentMemoryMcp(existing, taskId, agentRef) {
13096
+ if (existing !== void 0 && Object.prototype.hasOwnProperty.call(existing, AGENT_MEMORY_MCP_SERVER_NAME)) {
13097
+ throw new Error(`MCP server name "${AGENT_MEMORY_MCP_SERVER_NAME}" is reserved by the daemon`);
13098
+ }
13099
+ const bin = this.deps.agentMemoryMcpBin;
13100
+ const contextToken = `${randomUUID()}.${randomUUID()}`;
13101
+ this.revokeAgentMemoryContext(taskId);
13102
+ this.memoryContextByToken.set(contextToken, Object.freeze({ taskId, agentRef: Object.freeze({ ...agentRef }) }));
13103
+ this.memoryContextByTask.set(taskId, contextToken);
13104
+ return Object.freeze({
13105
+ ...existing ?? {},
13106
+ [AGENT_MEMORY_MCP_SERVER_NAME]: Object.freeze({
13107
+ command: bin.command,
13108
+ args: Object.freeze([...bin.args]),
13109
+ env: Object.freeze({
13110
+ BYOK_STORE_DIR: this.deps.storeDir,
13111
+ BYOK_PRODUCT_ID: this.deps.productId,
13112
+ BYOK_AGENT_MEMORY_CONTEXT: contextToken
13113
+ })
13114
+ })
13115
+ });
13116
+ }
13117
+ /** Reconstruct all sensitive context from the active sealed task, never from MCP/model arguments. */
13118
+ activeMemoryContext(contextToken) {
13119
+ const registered = this.memoryContextByToken.get(contextToken);
13120
+ if (registered === void 0 || this.memoryContextByTask.get(registered.taskId) !== contextToken || this.memoryClosingTasks.has(registered.taskId)) {
13121
+ throw new Error("invalid, expired, or quiescing Agent memory task context");
13122
+ }
13123
+ const active = this.tasks.get(registered.taskId);
13124
+ 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) {
13125
+ throw new Error("Agent memory context no longer matches its exact active task identity");
13126
+ }
13127
+ return Object.freeze({
13128
+ taskId: registered.taskId,
13129
+ tenantId: this.deps.tenantId,
13130
+ deviceId: this.deps.deviceId,
13131
+ agentRef: Object.freeze({ ...active.agentRef }),
13132
+ sessionRef: active.session.sessionRef,
13133
+ runtimeId: active.adapter.descriptor.id,
13134
+ canonicalHome: active.agentBinding.resolution.canonicalHome,
13135
+ leaseId: active.agentBinding.lease.leaseId,
13136
+ homeIdentity: active.agentBinding.lease.homeIdentity
13137
+ });
13138
+ }
13139
+ async runMemoryOperation(contextToken, operation) {
13140
+ let context = this.activeMemoryContext(contextToken);
13141
+ context = await this.bindAgentMemoryFilesystem(context);
13142
+ const current = this.activeMemoryContext(contextToken);
13143
+ if (current.taskId !== context.taskId || current.leaseId !== context.leaseId) throw new Error("Agent memory context changed during secure filesystem handshake");
13144
+ context = Object.freeze({ ...current, ...context.filesystem === void 0 ? {} : { filesystem: context.filesystem } });
13145
+ const pending = this.memoryInFlightByTask.get(context.taskId) ?? /* @__PURE__ */ new Set();
13146
+ this.memoryInFlightByTask.set(context.taskId, pending);
13147
+ const result = operation(context);
13148
+ pending.add(result);
13149
+ try {
13150
+ return await result;
13151
+ } finally {
13152
+ pending.delete(result);
13153
+ if (pending.size === 0) this.memoryInFlightByTask.delete(context.taskId);
13154
+ }
13155
+ }
13156
+ async quiesceAndSnapshotAgentMemory(active) {
13157
+ if (active.agentBinding === void 0 || active.agentRef === void 0 || active.agentHandoff === void 0) return;
13158
+ if (!isAgentMemorySecureFilesystemAvailable(this.deps.agentMemoryFilesystemHelperBin !== void 0)) return;
13159
+ if (this.deps.tenantId === void 0) {
13160
+ console.error(`[byok/client] Agent memory quiescent snapshot skipped for ${active.taskId}: exact tenant context is unavailable`);
13161
+ return;
13162
+ }
13163
+ this.memoryClosingTasks.add(active.taskId);
13164
+ try {
13165
+ await Promise.allSettled([...this.memoryInFlightByTask.get(active.taskId) ?? []]);
13166
+ const context = await this.bindAgentMemoryFilesystem({
13167
+ taskId: active.taskId,
13168
+ tenantId: this.deps.tenantId,
13169
+ deviceId: this.deps.deviceId,
13170
+ agentRef: active.agentRef,
13171
+ sessionRef: active.agentHandoff.sessionRef,
13172
+ runtimeId: active.agentHandoff.runtimeId,
13173
+ canonicalHome: active.agentBinding.resolution.canonicalHome,
13174
+ leaseId: active.agentBinding.lease.leaseId,
13175
+ homeIdentity: active.agentBinding.lease.homeIdentity
13176
+ });
13177
+ await snapshotAndProjectAgentMemory(context, this.deps.agentMemoryHostedProjection);
13178
+ } catch (error) {
13179
+ console.error(`[byok/client] Agent memory quiescent snapshot failed for ${active.taskId}: ${errorMessage4(error)}`);
13180
+ } finally {
13181
+ await this.closeAgentMemoryFilesystem(active.taskId);
13182
+ }
13183
+ }
13184
+ async bindAgentMemoryFilesystem(context) {
13185
+ const helperBin = this.deps.agentMemoryFilesystemHelperBin;
13186
+ if (helperBin === void 0) return context;
13187
+ let filesystem = this.memoryFilesystemByTask.get(context.taskId);
13188
+ if (filesystem === void 0) {
13189
+ filesystem = openAgentMemoryFilesystemHelper({
13190
+ helperBin,
13191
+ canonicalHome: context.canonicalHome,
13192
+ homeIdentity: context.homeIdentity
13193
+ });
13194
+ this.memoryFilesystemByTask.set(context.taskId, filesystem);
13195
+ void filesystem.catch(() => {
13196
+ if (this.memoryFilesystemByTask.get(context.taskId) === filesystem) this.memoryFilesystemByTask.delete(context.taskId);
13197
+ });
13198
+ }
13199
+ return Object.freeze({ ...context, filesystem: await filesystem });
13200
+ }
13201
+ async closeAgentMemoryFilesystem(taskId) {
13202
+ const filesystem = this.memoryFilesystemByTask.get(taskId);
13203
+ this.memoryFilesystemByTask.delete(taskId);
13204
+ if (filesystem === void 0) return;
13205
+ await filesystem.then((authority) => authority.close()).catch(() => {
13206
+ });
13207
+ }
13208
+ revokeAgentMemoryContext(taskId) {
13209
+ const token = this.memoryContextByTask.get(taskId);
13210
+ if (token !== void 0) this.memoryContextByToken.delete(token);
13211
+ this.memoryContextByTask.delete(taskId);
13212
+ this.memoryInFlightByTask.delete(taskId);
13213
+ this.memoryClosingTasks.delete(taskId);
13214
+ void this.closeAgentMemoryFilesystem(taskId);
13215
+ }
11700
13216
  /** Protocol §7: an instruction too large to inline arrives as a `blobRef` — resolve it via the blob client rather than failing closed. */
11701
13217
  async resolveInstruction(instruction) {
11702
13218
  if (typeof instruction === "string") return instruction;
@@ -11774,42 +13290,13 @@ var TaskRunner = class {
11774
13290
  if (!outcome.deliver) return;
11775
13291
  await this.observeGit(active, "completed");
11776
13292
  if (this.tasks.get(active.taskId) !== active || active.beingTornDown) return;
11777
- if (outcome.document !== void 0 && !this.hasResultDocumentCapability()) {
11778
- await this.fail(
11779
- active.taskId,
11780
- `${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`,
11781
- false
11782
- );
13293
+ if (active.messageRequirement !== void 0 && active.messageAccepted !== true) {
13294
+ active.pendingMessageCompletion = { finalOutput, ...outcome.document === void 0 ? {} : { document: outcome.document } };
13295
+ const record = active.messageOutbox?.get(active.taskId);
13296
+ if (record !== void 0) this.sendAgentMessageRecord(active.messageOutbox, record);
11783
13297
  return;
11784
13298
  }
11785
- if (!this.reserveSemanticTerminal(active)) return;
11786
- await this.persistAgentTerminalEvidence(active, "complete");
11787
- this.deps.send(
11788
- createEnvelope(
11789
- "task.complete",
11790
- {
11791
- summary: finalOutput,
11792
- sessionRef: active.session.sessionRef,
11793
- // Spread rather than `document: outcome.document`, so a
11794
- // completion with no document is the exact same payload it
11795
- // was before this field existed — not one carrying an
11796
- // explicit `document: undefined` key.
11797
- //
11798
- // `outcome.document` is the protocol's CANONICAL SNAPSHOT
11799
- // (`checkResultDocument`), never the object the extractor
11800
- // returned: pure data serializes identically at the root
11801
- // (where it was measured) and nested inside this payload
11802
- // (where the codec actually serializes it), so a contextual
11803
- // `toJSON(key)` or an unstable getter cannot make the wire
11804
- // bytes differ from what the cap gate approved.
11805
- ...outcome.document !== void 0 ? { document: outcome.document } : {},
11806
- ...this.terminalInferenceUsagePayload(active),
11807
- ...this.agentTerminalPayload(active)
11808
- },
11809
- { taskId: active.taskId, sessionRef: active.session.sessionRef }
11810
- )
11811
- );
11812
- await this.finish(active.taskId);
13299
+ await this.publishSuccessfulCompletion(active, finalOutput, outcome.document);
11813
13300
  return;
11814
13301
  }
11815
13302
  if (event.type === "progress") {
@@ -11845,6 +13332,26 @@ var TaskRunner = class {
11845
13332
  await this.fail(active.taskId, failure.reason, failure.retryable);
11846
13333
  }
11847
13334
  }
13335
+ async publishSuccessfulCompletion(active, finalOutput, document) {
13336
+ if (document !== void 0 && !this.hasResultDocumentCapability()) {
13337
+ await this.fail(
13338
+ active.taskId,
13339
+ `${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`,
13340
+ false
13341
+ );
13342
+ return;
13343
+ }
13344
+ if (!this.reserveSemanticTerminal(active)) return;
13345
+ await this.persistAgentTerminalEvidence(active, "complete");
13346
+ this.deps.send(createEnvelope("task.complete", {
13347
+ summary: finalOutput,
13348
+ sessionRef: active.session.sessionRef,
13349
+ ...document !== void 0 ? { document } : {},
13350
+ ...this.terminalInferenceUsagePayload(active),
13351
+ ...this.agentTerminalPayload(active)
13352
+ }, { taskId: active.taskId, sessionRef: active.session.sessionRef }));
13353
+ await this.finish(active.taskId);
13354
+ }
11848
13355
  /**
11849
13356
  * Protocol §7: an `artifact` `AgentEvent` only names a file the runtime
11850
13357
  * wrote into the task workspace (`name`/`contentType` — it carries no
@@ -12527,11 +14034,26 @@ var TaskRunner = class {
12527
14034
  * docs/protocol.md §7.2.
12528
14035
  */
12529
14036
  async resolveResultDocument(active, finalOutput) {
14037
+ if (active.terminalProjection?.mode === "none" || active.messageRequirement !== void 0 && active.terminalProjection === void 0) return { deliver: true };
12530
14038
  const extract = this.deps.resultDocument?.extract;
12531
- if (!extract) return { deliver: true };
14039
+ if (!extract) {
14040
+ if (active.terminalProjection?.mode === "result-document") {
14041
+ await this.fail(
14042
+ active.taskId,
14043
+ `${RESULT_DOCUMENT_UNDELIVERABLE_REASON_PREFIX}: the offer requires result contract ${active.terminalProjection.contract} but this daemon has no extractor`,
14044
+ false
14045
+ );
14046
+ return { deliver: false };
14047
+ }
14048
+ return { deliver: true };
14049
+ }
12532
14050
  let document;
12533
14051
  try {
12534
- document = extract(finalOutput, { taskId: active.taskId, sessionRef: active.session.sessionRef });
14052
+ document = extract(finalOutput, {
14053
+ taskId: active.taskId,
14054
+ sessionRef: active.session.sessionRef,
14055
+ ...active.terminalProjection === void 0 ? {} : { terminalProjection: active.terminalProjection }
14056
+ });
12535
14057
  } catch (err) {
12536
14058
  await this.fail(
12537
14059
  active.taskId,
@@ -12548,7 +14070,17 @@ var TaskRunner = class {
12548
14070
  );
12549
14071
  return { deliver: false };
12550
14072
  }
12551
- if (document === void 0) return { deliver: true };
14073
+ if (document === void 0) {
14074
+ if (active.terminalProjection?.mode === "result-document") {
14075
+ await this.fail(
14076
+ active.taskId,
14077
+ `${RESULT_DOCUMENT_UNDELIVERABLE_REASON_PREFIX}: the required result contract ${active.terminalProjection.contract} produced no document`,
14078
+ false
14079
+ );
14080
+ return { deliver: false };
14081
+ }
14082
+ return { deliver: true };
14083
+ }
12552
14084
  const check = checkResultDocument(document);
12553
14085
  if (!check.ok) {
12554
14086
  const detail = resultDocumentRejectionDetail(check);
@@ -12682,6 +14214,10 @@ var TaskRunner = class {
12682
14214
  clearTimeout(active.maxDurationTimer);
12683
14215
  active.maxDurationTimer = void 0;
12684
14216
  }
14217
+ if (active.messageRetryTimer) {
14218
+ clearTimeout(active.messageRetryTimer);
14219
+ active.messageRetryTimer = void 0;
14220
+ }
12685
14221
  active.batcher.stop();
12686
14222
  this.addFinishedTaskId(taskId);
12687
14223
  const queued = active.approvalQueue.splice(0);
@@ -12722,6 +14258,7 @@ var TaskRunner = class {
12722
14258
  if (active.agentBinding !== void 0 && active.agentRef !== void 0 && active.agentHandoff !== void 0) {
12723
14259
  const agentRef = active.agentRef;
12724
14260
  const handoff = active.agentHandoff;
14261
+ await this.quiesceAndSnapshotAgentMemory(active);
12725
14262
  if (!active.agentTerminalPersisted) {
12726
14263
  const cause = active.terminalCause ?? "failed";
12727
14264
  const result = await this.retryAgentTerminalEvidence(
@@ -12760,11 +14297,15 @@ var TaskRunner = class {
12760
14297
  }
12761
14298
  active.gitLease?.release();
12762
14299
  this.tasks.delete(taskId);
14300
+ this.revokeAgentMessageContext(taskId);
14301
+ this.revokeAgentMemoryContext(taskId);
12763
14302
  active.resolveSemanticTerminalSettled?.(leaseReleased);
12764
14303
  return leaseReleased;
12765
14304
  }
12766
14305
  active.gitLease?.release();
12767
14306
  this.tasks.delete(taskId);
14307
+ this.revokeAgentMessageContext(taskId);
14308
+ this.revokeAgentMemoryContext(taskId);
12768
14309
  active.resolveSemanticTerminalSettled?.(true);
12769
14310
  return true;
12770
14311
  }
@@ -12928,9 +14469,9 @@ function metadataStatusEvent(event) {
12928
14469
  return { ...event };
12929
14470
  }
12930
14471
  }
12931
- var encoder2 = new TextEncoder();
14472
+ var encoder3 = new TextEncoder();
12932
14473
  function eventBytes(event) {
12933
- return encoder2.encode(JSON.stringify(event)).length;
14474
+ return encoder3.encode(JSON.stringify(event)).length;
12934
14475
  }
12935
14476
  var AGENT_EGRESS_DIRECTORY = path3__default.join(".byok", "egress");
12936
14477
  var AGENT_RELIABLE_SPOOL_FILENAME = "reliable-v1.jsonl";
@@ -12948,7 +14489,7 @@ var AgentReliableQuotaError = class extends AgentReliableSpoolError {
12948
14489
  }
12949
14490
  reason;
12950
14491
  };
12951
- function sameAgentRef(left, right) {
14492
+ function sameAgentRef2(left, right) {
12952
14493
  return left.agentId === right.agentId && left.profileRevision === right.profileRevision;
12953
14494
  }
12954
14495
  function stableRecordJson(value) {
@@ -12981,7 +14522,7 @@ function assertWireType(value) {
12981
14522
  }
12982
14523
  }
12983
14524
  function sameRecord(record, candidate) {
12984
- 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;
14525
+ 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;
12985
14526
  }
12986
14527
  function parseEntry2(value) {
12987
14528
  if (typeof value !== "object" || value === null || Array.isArray(value)) throw new AgentReliableSpoolError("reliable spool entry is not an object");
@@ -13168,7 +14709,7 @@ var AgentReliableSpool = class _AgentReliableSpool {
13168
14709
  return this.exclusive(async () => {
13169
14710
  const record = this.pending.get(ack.eventId);
13170
14711
  if (!record) return false;
13171
- if (record.cursor !== ack.cursor || record.tenantId !== ack.tenantId || record.sessionRef !== ack.sessionRef || record.policyRevision !== ack.policyRevision || !sameAgentRef(record.agentRef, ack.agentRef)) return false;
14712
+ if (record.cursor !== ack.cursor || record.tenantId !== ack.tenantId || record.sessionRef !== ack.sessionRef || record.policyRevision !== ack.policyRevision || !sameAgentRef2(record.agentRef, ack.agentRef)) return false;
13172
14713
  await this.appendEntry({ schema: 1, kind: "ack", ack: Object.freeze({ ...ack, agentRef: { ...ack.agentRef } }) });
13173
14714
  this.pending.delete(record.eventId);
13174
14715
  if (this.logEntries >= 512) await this.compact();
@@ -13201,7 +14742,7 @@ var AgentReliableSpool = class _AgentReliableSpool {
13201
14742
  } else {
13202
14743
  const record = this.pending.get(entry.ack.eventId);
13203
14744
  if (!record) throw new AgentReliableSpoolError("reliable spool acknowledges an unknown event");
13204
- 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");
14745
+ 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");
13205
14746
  this.pending.delete(record.eventId);
13206
14747
  }
13207
14748
  }
@@ -13863,7 +15404,7 @@ var AgentHomeProjectionCompletionError = class extends Error {
13863
15404
  this.name = "AgentHomeProjectionCompletionError";
13864
15405
  }
13865
15406
  };
13866
- function sameAgentRef2(left, right) {
15407
+ function sameAgentRef3(left, right) {
13867
15408
  return left.agentId === right.agentId && left.profileRevision === right.profileRevision;
13868
15409
  }
13869
15410
  var AgentHomeProjectionCompletionClient = class {
@@ -13906,7 +15447,7 @@ var AgentHomeProjectionCompletionClient = class {
13906
15447
  cause: error
13907
15448
  });
13908
15449
  }
13909
- 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) {
15450
+ 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) {
13910
15451
  throw new AgentHomeProjectionCompletionError(
13911
15452
  "Agent-home projection completion readback does not exactly match the authenticated request"
13912
15453
  );
@@ -13914,6 +15455,15 @@ var AgentHomeProjectionCompletionClient = class {
13914
15455
  return readback;
13915
15456
  }
13916
15457
  };
15458
+ function resolveAgentMessageMcpBin() {
15459
+ const script = path3__default.join(path3__default.dirname(fileURLToPath(import.meta.url)), "bin", "byok-agent-message-mcp.js");
15460
+ return Object.freeze({ command: process.execPath, args: Object.freeze([script]) });
15461
+ }
15462
+ function resolveAgentMemoryMcpBin(externalHelperConfigured = false) {
15463
+ if (!isAgentMemorySecureFilesystemAvailable(externalHelperConfigured)) return void 0;
15464
+ const script = path3__default.join(path3__default.dirname(fileURLToPath(import.meta.url)), "bin", "byok-agent-memory-mcp.js");
15465
+ return Object.freeze({ command: process.execPath, args: Object.freeze([script]) });
15466
+ }
13917
15467
  var AGENT_CONTENT_READ_SURFACES = ["workspace", "transcript", "artifact"];
13918
15468
  var AGENT_CONTENT_READ_CAPABILITIES = Object.freeze({
13919
15469
  workspace: AGENT_CONTENT_WORKSPACE_READ_CAPABILITY,
@@ -14145,11 +15695,11 @@ var RootPolicyError = class extends Error {
14145
15695
  this.reason = reason;
14146
15696
  }
14147
15697
  };
14148
- function sameAgentRef3(left, right) {
15698
+ function sameAgentRef4(left, right) {
14149
15699
  return left.agentId === right.agentId && left.profileRevision === right.profileRevision;
14150
15700
  }
14151
15701
  function sameSessionIdentity(left, right) {
14152
- return sameAgentRef3(left.agentRef, right.agentRef) && left.sessionRef === right.sessionRef && left.runtimeId === right.runtimeId && left.cwd === right.cwd;
15702
+ return sameAgentRef4(left.agentRef, right.agentRef) && left.sessionRef === right.sessionRef && left.runtimeId === right.runtimeId && left.cwd === right.cwd;
14153
15703
  }
14154
15704
  function validateRequest(request) {
14155
15705
  if (!isRecord5(request)) throw new AgentContentReadRequestError("content read request must be an object");
@@ -14421,7 +15971,7 @@ var AgentContentReadPolicyEngine = class {
14421
15971
  }
14422
15972
  async checkSessionIdentity(request, resolver, requiredCwd) {
14423
15973
  const session = request.session;
14424
- if (session === void 0 || !sameAgentRef3(session.agentRef, request.agentRef) || requiredCwd !== void 0 && session.cwd !== requiredCwd) {
15974
+ if (session === void 0 || !sameAgentRef4(session.agentRef, request.agentRef) || requiredCwd !== void 0 && session.cwd !== requiredCwd) {
14425
15975
  return "identity-mismatch";
14426
15976
  }
14427
15977
  let expected;
@@ -14542,6 +16092,7 @@ function computeCapabilities(adapters, agentHomeConfigured = false, strictAgentO
14542
16092
  if (adapters.some((adapter) => adapter.descriptor.capabilities.steer)) flags.push("steer");
14543
16093
  flags.push("blob-upload");
14544
16094
  flags.push("approval-targeting");
16095
+ flags.push(TERMINAL_PROJECTION_SELECTION_CAPABILITY);
14545
16096
  const selectionAdapters = adapters.filter(
14546
16097
  (adapter) => ALL_RUNTIME_IDS.includes(adapter.descriptor.id)
14547
16098
  );
@@ -14560,6 +16111,9 @@ function computeCapabilities(adapters, agentHomeConfigured = false, strictAgentO
14560
16111
  AGENT_EGRESS_RELIABLE_ACK_CAPABILITY,
14561
16112
  AGENT_EGRESS_FRESH_SESSION_CAPABILITY
14562
16113
  );
16114
+ if (agentHomeConfigured && adapters.some((adapter) => adapter.descriptor.capabilities.mcpToolsets === true)) {
16115
+ flags.push(AGENT_MESSAGE_EGRESS_CAPABILITY);
16116
+ }
14563
16117
  }
14564
16118
  if (contentReadPolicies !== void 0) {
14565
16119
  for (const surface of Object.keys(AGENT_CONTENT_READ_CAPABILITIES)) {
@@ -14670,6 +16224,23 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
14670
16224
  "DaemonConfig.agentHome and DaemonConfig.gitWorkspace are mutually exclusive; Agent home is the only workspace authority for Agent offers"
14671
16225
  );
14672
16226
  }
16227
+ if (config.agentMemory !== void 0 && config.agentHome === void 0) {
16228
+ throw new Error("DaemonConfig.agentMemory requires DaemonConfig.agentHome for the exact Agent memory authority");
16229
+ }
16230
+ if (config.agentMemoryFilesystem !== void 0 && config.agentHome === void 0) {
16231
+ throw new Error("DaemonConfig.agentMemoryFilesystem requires DaemonConfig.agentHome for the exact Agent memory authority");
16232
+ }
16233
+ if (config.agentMemoryFilesystem !== void 0 && (!path3__default.isAbsolute(config.agentMemoryFilesystem.helperBin) || /[\u0000\r\n]/u.test(config.agentMemoryFilesystem.helperBin))) {
16234
+ throw new Error("DaemonConfig.agentMemoryFilesystem.helperBin must be an explicit absolute executable path");
16235
+ }
16236
+ const externalAgentMemoryFilesystem = config.agentMemoryFilesystem !== void 0;
16237
+ if (externalAgentMemoryFilesystem && !isAgentMemoryFilesystemHelperSupported()) {
16238
+ throw new Error("DaemonConfig.agentMemoryFilesystem is not admitted on this platform without its native race proof");
16239
+ }
16240
+ if (config.agentMemory !== void 0 && !isAgentMemorySecureFilesystemAvailable(externalAgentMemoryFilesystem)) {
16241
+ throw new Error("DaemonConfig.agentMemory requires a platform with safe descriptor-relative filesystem operations");
16242
+ }
16243
+ const agentMemoryMcpBin = config.agentHome === void 0 ? void 0 : resolveAgentMemoryMcpBin(externalAgentMemoryFilesystem);
14673
16244
  const localAgentRelease = resolveLocalAgentReleaseIdentity(config.localAgentRelease);
14674
16245
  const toolsetRegistry = new McpToolsetRegistry(config.mcpToolsets);
14675
16246
  validatePiByokLauncherConfig(config.piByokLauncher);
@@ -15101,6 +16672,7 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
15101
16672
  approvalRegistry,
15102
16673
  storeDir,
15103
16674
  productId: config.productId,
16675
+ tenantId: record.tenantId,
15104
16676
  // U2 terminal inference usage consumes the one U4a-resolved,
15105
16677
  // process-immutable identity captured above. This is composition-only:
15106
16678
  // TaskRunner receives no config, manifest, or second identity resolver.
@@ -15115,6 +16687,10 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
15115
16687
  // doc comment. Spread rather than assigned so an unconfigured daemon
15116
16688
  // builds the exact `deps` object it did before this seam existed.
15117
16689
  ...config.resultDocument ? { resultDocument: config.resultDocument } : {},
16690
+ ...agentMemoryMcpBin === void 0 ? {} : { agentMemoryMcpBin },
16691
+ ...config.agentMemoryFilesystem === void 0 ? {} : { agentMemoryFilesystemHelperBin: path3__default.resolve(config.agentMemoryFilesystem.helperBin) },
16692
+ ...config.agentHome !== void 0 && config.agentEgress !== void 0 ? { agentMessageMcpBin: resolveAgentMessageMcpBin() } : {},
16693
+ ...config.agentMemory === void 0 ? {} : { agentMemoryHostedProjection: config.agentMemory },
15118
16694
  // M4 Phase 3 hardening: bridges TaskRunner's stale-approval-race
15119
16695
  // finding out to the SAME local observability seam every other
15120
16696
  // daemon-local event already uses (see observer.ts's own module doc
@@ -15145,6 +16721,9 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
15145
16721
  ...activePressureEngine ? { admissionGuard: () => activePressureEngine.admissionGuard() } : {}
15146
16722
  };
15147
16723
  runner = new TaskRunner(deps);
16724
+ if (config.agentHome !== void 0 && config.agentEgress !== void 0) {
16725
+ await runner.recoverAgentMessageOutboxes(path3__default.join(config.agentHome.hostStorageRoot, "agents"));
16726
+ }
15148
16727
  const handleAgentHomeProjectionEnvelope = async (envelope) => {
15149
16728
  if (envelope.type !== "agent.home.projection") return false;
15150
16729
  if (agentHomeManager === void 0 || agentHomeProjectionCompletion === void 0) {
@@ -15327,6 +16906,7 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
15327
16906
  observer.handleInboundEnvelope(envelope);
15328
16907
  if (envelope.type === "agent.home.projection") return handleAgentHomeProjectionEnvelope(envelope).then(() => void 0);
15329
16908
  if (envelope.type === "agent.egress.ack") return handleAgentEgressEnvelope(envelope).then(() => void 0);
16909
+ if (envelope.type === "agent.message.disposition") return runner?.handleEnvelope(envelope) ?? Promise.resolve();
15330
16910
  if (envelope.type === "agent.content.read") return handleAgentContentReadEnvelope(envelope).then(() => void 0);
15331
16911
  return runner?.handleEnvelope(envelope) ?? Promise.resolve();
15332
16912
  },
@@ -15334,6 +16914,7 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
15334
16914
  const wasSettled = connectionState === "open" || connectionState === "degraded";
15335
16915
  connectionState = state;
15336
16916
  observer.noteConnectionState(state);
16917
+ if (!wasSettled && (state === "open" || state === "degraded")) runner?.retryRecoveredAgentMessages();
15337
16918
  if (!wasSettled && (state === "open" || state === "degraded")) runPresenceDiscovery();
15338
16919
  },
15339
16920
  backoff: overrides.backoff,
@@ -15352,6 +16933,7 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
15352
16933
  });
15353
16934
  await connection.start();
15354
16935
  await connection.waitForAck();
16936
+ runner.retryRecoveredAgentMessages();
15355
16937
  for (const record2 of agentEgress.retryableReliableRecords(connection.getServerCapabilities())) {
15356
16938
  dispatchReliableRecord(record2);
15357
16939
  }
@@ -15699,6 +17281,24 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
15699
17281
  if (!runner) throw new ControlError("not_found", "daemon is not started");
15700
17282
  return runner.requestApproval(parsed.taskId, parsed.summary);
15701
17283
  },
17284
+ "agent_messages.publish": (params) => {
17285
+ const parsed = parseAgentMessagePublishParams(params);
17286
+ if (!parsed) throw new ControlError("bad_request", "agent_messages.publish requires exactly {contextToken,contentType,body}");
17287
+ if (!runner) throw new ControlError("not_found", "daemon is not started");
17288
+ return runner.publishAgentMessage(parsed);
17289
+ },
17290
+ "agent_memory.recall": (params) => {
17291
+ const parsed = parseAgentMemoryRecallParams(params);
17292
+ if (!parsed) throw new ControlError("bad_request", "agent_memory.recall requires exactly {contextToken,path,ifRevision?}");
17293
+ if (!runner) throw new ControlError("not_found", "daemon is not started");
17294
+ return runner.recallAgentMemory(parsed);
17295
+ },
17296
+ "agent_memory.save": (params) => {
17297
+ const parsed = parseAgentMemorySaveParams(params);
17298
+ if (!parsed) throw new ControlError("bad_request", "agent_memory.save requires exactly {contextToken,op,path,expectedRevision,content?}");
17299
+ if (!runner) throw new ControlError("not_found", "daemon is not started");
17300
+ return runner.saveAgentMemory(parsed);
17301
+ },
15702
17302
  /**
15703
17303
  * Plan `device-assertion-broker`: mint one short-lived, audience-scoped
15704
17304
  * device assertion for a sibling local process.
@@ -16589,8 +18189,8 @@ var TruthMemoryClient = class {
16589
18189
  #requestId;
16590
18190
  #allowedObjectDownloadOrigins;
16591
18191
  async listManifest(query = {}) {
16592
- const path33 = manifestPath(query);
16593
- const response = await this.#proofFetch(path33, {
18192
+ const path38 = manifestPath(query);
18193
+ const response = await this.#proofFetch(path38, {
16594
18194
  method: "GET",
16595
18195
  operation: "truth.list",
16596
18196
  resource: "records",
@@ -16697,9 +18297,9 @@ var TruthMemoryClient = class {
16697
18297
  }
16698
18298
  async #write(kind, recordKey, requestId, payload, expectedPrimary, expectedSnapshots) {
16699
18299
  assertDistinctExpectedWrites([expectedPrimary, ...expectedSnapshots]);
16700
- const path33 = recordPath(kind, recordKey);
18300
+ const path38 = recordPath(kind, recordKey);
16701
18301
  const body = new TextEncoder().encode(JSON.stringify(payload));
16702
- const response = await this.#proofFetch(path33, {
18302
+ const response = await this.#proofFetch(path38, {
16703
18303
  method: "PUT",
16704
18304
  operation: "truth.write",
16705
18305
  resource: `${kind}/${recordKey}`,
@@ -16725,8 +18325,8 @@ var TruthMemoryClient = class {
16725
18325
  };
16726
18326
  }
16727
18327
  async #readVerified(listed) {
16728
- const path33 = recordPath(listed.kind, listed.recordKey);
16729
- const response = await this.#proofFetch(path33, {
18328
+ const path38 = recordPath(listed.kind, listed.recordKey);
18329
+ const response = await this.#proofFetch(path38, {
16730
18330
  method: "GET",
16731
18331
  operation: "truth.read",
16732
18332
  resource: `${listed.kind}/${listed.recordKey}`,
@@ -16787,16 +18387,16 @@ var TruthMemoryClient = class {
16787
18387
  }
16788
18388
  return { ...listed, bytes };
16789
18389
  }
16790
- async #proofFetch(path33, request) {
18390
+ async #proofFetch(path38, request) {
16791
18391
  const proof = await this.options.signer.sign({
16792
18392
  method: request.method,
16793
- path: path33,
18393
+ path: path38,
16794
18394
  operation: request.operation,
16795
18395
  resource: request.resource,
16796
18396
  requestId: request.requestId,
16797
18397
  body: request.body
16798
18398
  });
16799
- const response = await this.#fetch(new URL(path33, this.#base), {
18399
+ const response = await this.#fetch(new URL(path38, this.#base), {
16800
18400
  method: request.method,
16801
18401
  headers: {
16802
18402
  ...request.headers,
@@ -16807,7 +18407,7 @@ var TruthMemoryClient = class {
16807
18407
  if (!response.ok) {
16808
18408
  throw new TruthMemoryClientError(
16809
18409
  "truth_http_failed",
16810
- `truth request ${request.method} ${path33} failed with HTTP ${response.status}`,
18410
+ `truth request ${request.method} ${path38} failed with HTTP ${response.status}`,
16811
18411
  response.status
16812
18412
  );
16813
18413
  }
@@ -17099,7 +18699,7 @@ ${args.map((a) => ` ${plistString(a)}`).join("\n")}
17099
18699
  }
17100
18700
  function createLaunchdLifecycle(def, deps = {}) {
17101
18701
  const run = deps.run ?? defaultRunner;
17102
- const fs27 = deps.fs ?? promises;
18702
+ const fs29 = deps.fs ?? promises;
17103
18703
  const homedir = deps.homedir ?? (() => os4__default.homedir());
17104
18704
  const getuid = deps.getuid ?? (() => {
17105
18705
  if (typeof process.getuid !== "function") {
@@ -17113,7 +18713,7 @@ function createLaunchdLifecycle(def, deps = {}) {
17113
18713
  const serviceTarget = () => `${domainTarget()}/${label}`;
17114
18714
  async function fileExists(p) {
17115
18715
  try {
17116
- await fs27.stat(p);
18716
+ await fs29.stat(p);
17117
18717
  return true;
17118
18718
  } catch {
17119
18719
  return false;
@@ -17121,9 +18721,9 @@ function createLaunchdLifecycle(def, deps = {}) {
17121
18721
  }
17122
18722
  async function writePlist(program) {
17123
18723
  const xml = generateLaunchdPlist({ label, program, logDir: def.logDir });
17124
- await fs27.mkdir(path3__default.dirname(plistPath()), { recursive: true });
17125
- await fs27.mkdir(def.logDir, { recursive: true });
17126
- await fs27.writeFile(plistPath(), xml, "utf8");
18724
+ await fs29.mkdir(path3__default.dirname(plistPath()), { recursive: true });
18725
+ await fs29.mkdir(def.logDir, { recursive: true });
18726
+ await fs29.writeFile(plistPath(), xml, "utf8");
17127
18727
  }
17128
18728
  async function install(opts = {}) {
17129
18729
  await writePlist(opts.program ?? def.program);
@@ -17134,7 +18734,7 @@ function createLaunchdLifecycle(def, deps = {}) {
17134
18734
  }
17135
18735
  async function uninstall() {
17136
18736
  await runIdempotent(run, "launchctl", ["bootout", serviceTarget()], "launchctl bootout", LAUNCHD_NOT_LOADED);
17137
- await fs27.rm(plistPath(), { force: true });
18737
+ await fs29.rm(plistPath(), { force: true });
17138
18738
  }
17139
18739
  async function start() {
17140
18740
  if (!await fileExists(plistPath())) {
@@ -17218,14 +18818,14 @@ WantedBy=default.target
17218
18818
  }
17219
18819
  function createSystemdLifecycle(def, deps = {}) {
17220
18820
  const run = deps.run ?? defaultRunner;
17221
- const fs27 = deps.fs ?? promises;
18821
+ const fs29 = deps.fs ?? promises;
17222
18822
  const homedir = deps.homedir ?? (() => os4__default.homedir());
17223
18823
  const name = sanitizeServiceName(def.name);
17224
18824
  const unitName = `${name}.service`;
17225
18825
  const unitPath = () => path3__default.join(homedir(), ".config", "systemd", "user", unitName);
17226
18826
  async function fileExists(p) {
17227
18827
  try {
17228
- await fs27.stat(p);
18828
+ await fs29.stat(p);
17229
18829
  return true;
17230
18830
  } catch {
17231
18831
  return false;
@@ -17233,9 +18833,9 @@ function createSystemdLifecycle(def, deps = {}) {
17233
18833
  }
17234
18834
  async function writeUnit(program) {
17235
18835
  const unit = generateSystemdUnit({ name, displayName: def.displayName ?? def.name, program, logDir: def.logDir });
17236
- await fs27.mkdir(path3__default.dirname(unitPath()), { recursive: true });
17237
- await fs27.mkdir(def.logDir, { recursive: true });
17238
- await fs27.writeFile(unitPath(), unit, "utf8");
18836
+ await fs29.mkdir(path3__default.dirname(unitPath()), { recursive: true });
18837
+ await fs29.mkdir(def.logDir, { recursive: true });
18838
+ await fs29.writeFile(unitPath(), unit, "utf8");
17239
18839
  }
17240
18840
  async function install(opts = {}) {
17241
18841
  await writeUnit(opts.program ?? def.program);
@@ -17244,7 +18844,7 @@ function createSystemdLifecycle(def, deps = {}) {
17244
18844
  }
17245
18845
  async function uninstall() {
17246
18846
  await runIdempotent(run, "systemctl", ["--user", "disable", "--now", unitName], "systemctl disable --now", SYSTEMD_NOT_LOADED);
17247
- await fs27.rm(unitPath(), { force: true });
18847
+ await fs29.rm(unitPath(), { force: true });
17248
18848
  await run("systemctl", ["--user", "daemon-reload"]);
17249
18849
  }
17250
18850
  async function start() {
@@ -17307,7 +18907,7 @@ ${argXml}${cwdXml}
17307
18907
  }
17308
18908
  function createWinswLifecycle(def, deps = {}) {
17309
18909
  const run = deps.run ?? defaultRunner;
17310
- const fs27 = deps.fs ?? promises;
18910
+ const fs29 = deps.fs ?? promises;
17311
18911
  const windows = def.windows;
17312
18912
  if (!windows) {
17313
18913
  throw new Error("WinSW service lifecycle requires `ServiceDefinition.windows.winswBin` (the product-bundled WinSW executable path)");
@@ -17319,7 +18919,7 @@ function createWinswLifecycle(def, deps = {}) {
17319
18919
  const xmlPath = path3__default.join(installDir, `${id}.xml`);
17320
18920
  async function fileExists(p) {
17321
18921
  try {
17322
- await fs27.stat(p);
18922
+ await fs29.stat(p);
17323
18923
  return true;
17324
18924
  } catch {
17325
18925
  return false;
@@ -17327,10 +18927,10 @@ function createWinswLifecycle(def, deps = {}) {
17327
18927
  }
17328
18928
  async function writeFiles(program) {
17329
18929
  const xml = generateWinswXml({ id, displayName: def.displayName ?? def.name, program, logDir: def.logDir });
17330
- await fs27.mkdir(installDir, { recursive: true });
17331
- await fs27.mkdir(def.logDir, { recursive: true });
17332
- await fs27.copyFile(winswBin, exePath);
17333
- await fs27.writeFile(xmlPath, xml, "utf8");
18930
+ await fs29.mkdir(installDir, { recursive: true });
18931
+ await fs29.mkdir(def.logDir, { recursive: true });
18932
+ await fs29.copyFile(winswBin, exePath);
18933
+ await fs29.writeFile(xmlPath, xml, "utf8");
17334
18934
  }
17335
18935
  async function install(opts = {}) {
17336
18936
  await writeFiles(opts.program ?? def.program);
@@ -17340,8 +18940,8 @@ function createWinswLifecycle(def, deps = {}) {
17340
18940
  async function uninstall() {
17341
18941
  await runIdempotent(run, exePath, ["stop"], "winsw stop", WINSW_NOT_INSTALLED);
17342
18942
  await runIdempotent(run, exePath, ["uninstall"], "winsw uninstall", WINSW_NOT_INSTALLED);
17343
- await fs27.rm(exePath, { force: true });
17344
- await fs27.rm(xmlPath, { force: true });
18943
+ await fs29.rm(exePath, { force: true });
18944
+ await fs29.rm(xmlPath, { force: true });
17345
18945
  }
17346
18946
  async function start() {
17347
18947
  if (!await fileExists(xmlPath)) {
@@ -17384,6 +18984,6 @@ function createServiceLifecycle(def, opts = {}) {
17384
18984
  }
17385
18985
  }
17386
18986
 
17387
- export { AGENT_CONTENT_READ_CAPABILITIES, AGENT_CONTENT_READ_CAPABILITY_ARTIFACT, AGENT_CONTENT_READ_CAPABILITY_TRANSCRIPT, AGENT_CONTENT_READ_CAPABILITY_WORKSPACE, AGENT_HOME_PROJECTION_STATE_FILE, AgentHomeBusyError, AgentHomeCollisionError, AgentHomeError, AgentHomeLayout, AgentHomeLeaseCorruptError, AgentHomeLeaseManager, AgentHomeManager, AgentHomeResolutionError, AgentRefValidationError, AgentSessionHandoffCorruptError, AgentSessionHandoffMismatchError, AgentSessionHandoffStore, AgentSessionHandoffStoreError, BlobClient, ClaudeAdapter, CodexAdapter, DEFAULT_ACK_CRITICAL_RESERVE_BYTES, DEFAULT_CLEANUP_BATCH_LIMIT, DEFAULT_HARD_BUDGET_RATIO, DEFAULT_INCREMENTAL_VACUUM_PAGES, DEFAULT_LOG_ROTATION, DEFAULT_NORMAL_COMPACTION_INTERVAL_MS, DEFAULT_PRESSURE_COMPACTION_INTERVAL_MS, DEFAULT_RETENTION_MS, DEFAULT_SOFT_BUDGET_RATIO, DaemonObserver, GitWorkspaceError, GitWorkspaceManager, GitWorkspaceStore, JOURNAL_DB_FILENAME, JOURNAL_QUARANTINE_DIRNAME, JOURNAL_TASK_REF_PREFIX, JournalClosedError, JournalCorruptError, JournalRecordTooLargeError, JournalUnavailableError, JournalUnknownTaskError, LocalStateRelocationBusyError, LocalStateRelocationError, LocalStateRelocationIntegrityError, LocalStorageEmergencyError, LocalStoragePolicyError, LocalStoragePressureEngine, McpToolsetDefinitionRevisionConflictError, McpToolsetRevisionConflictError, PI_PACKAGE_NAME, PiAdapter, PolicyUnsupportedError, RUNTIME_ADAPTER_CONTRACT_VIOLATION_REASON, RuntimeDisposalFailure, RuntimeExecutionFailure, SKILL_PACKS_CAPABILITY, SKILL_PACKS_DIRNAME, SKILL_PACK_AUDIT_FILENAME, SKILL_PACK_INSTALL_ERROR_CODES, SKILL_PACK_LOCK_FILENAME, SKILL_PACK_LOCK_SCHEMA, SKILL_PACK_RESPONSE_MAX_BYTES, SecureDirHardeningError, SkillPackInstallError, SqliteLocalTaskJournal, SteerUnsupportedError, TruthMemoryClient, TruthMemoryClientError, UnsupportedServicePlatformError, buildIcaclsArgs, cleanupEligibleAt, cleanupOrderFor, computePressureState, createAgentHomeProjection, createAgentHomeProjectionConsumer, createDaemon, createDaemonWithAdapters, createFilesystemCleanupExecutor, createServiceLifecycle, createStatfsFreeBytesProvider, ensureSecureDir, freezeRuntimeAdapterDescriptor, generateLaunchdPlist, generateSystemdUnit, generateWinswXml, installSkillPacks, isGitWorkspaceConfig, isRuntimeDisposalFailure, isRuntimeExecutionFailure, journalHash, listInstalledSkillPacks, localStateRelocation, nodeAgentProgram, prependGitWorkspaceGuidance, projectRuntimeBoundaryFailure, projectRuntimeExecutionFailure, projectSkillPack, readDeviceEnrollmentStatus, requestDeviceAssertion, resolveLocalAgentReleaseIdentity, resolveLocalStoragePolicy, sanitizeServiceName, sealRuntimeOperationManifest, skillPacksRoot, stableAgentHomeOwnerId, validateAgentRef };
18987
+ export { AGENT_CONTENT_READ_CAPABILITIES, AGENT_CONTENT_READ_CAPABILITY_ARTIFACT, AGENT_CONTENT_READ_CAPABILITY_TRANSCRIPT, AGENT_CONTENT_READ_CAPABILITY_WORKSPACE, AGENT_HOME_PROJECTION_STATE_FILE, AGENT_MEMORY_AUDIT_FILENAME, AGENT_MEMORY_OUTBOX_FILENAME, AgentHomeBusyError, AgentHomeCollisionError, AgentHomeError, AgentHomeLayout, AgentHomeLeaseCorruptError, AgentHomeLeaseManager, AgentHomeManager, AgentHomeResolutionError, AgentMemoryError, AgentMemoryRevisionConflictError, AgentRefValidationError, AgentSessionHandoffCorruptError, AgentSessionHandoffMismatchError, AgentSessionHandoffStore, AgentSessionHandoffStoreError, BlobClient, ClaudeAdapter, CodexAdapter, DEFAULT_ACK_CRITICAL_RESERVE_BYTES, DEFAULT_CLEANUP_BATCH_LIMIT, DEFAULT_HARD_BUDGET_RATIO, DEFAULT_INCREMENTAL_VACUUM_PAGES, DEFAULT_LOG_ROTATION, DEFAULT_NORMAL_COMPACTION_INTERVAL_MS, DEFAULT_PRESSURE_COMPACTION_INTERVAL_MS, DEFAULT_RETENTION_MS, DEFAULT_SOFT_BUDGET_RATIO, DaemonObserver, GitWorkspaceError, GitWorkspaceManager, GitWorkspaceStore, JOURNAL_DB_FILENAME, JOURNAL_QUARANTINE_DIRNAME, JOURNAL_TASK_REF_PREFIX, JournalClosedError, JournalCorruptError, JournalRecordTooLargeError, JournalUnavailableError, JournalUnknownTaskError, LocalStateRelocationBusyError, LocalStateRelocationError, LocalStateRelocationIntegrityError, LocalStorageEmergencyError, LocalStoragePolicyError, LocalStoragePressureEngine, McpToolsetDefinitionRevisionConflictError, McpToolsetRevisionConflictError, PI_PACKAGE_NAME, PiAdapter, PolicyUnsupportedError, RUNTIME_ADAPTER_CONTRACT_VIOLATION_REASON, RuntimeDisposalFailure, RuntimeExecutionFailure, SKILL_PACKS_CAPABILITY, SKILL_PACKS_DIRNAME, SKILL_PACK_AUDIT_FILENAME, SKILL_PACK_INSTALL_ERROR_CODES, SKILL_PACK_LOCK_FILENAME, SKILL_PACK_LOCK_SCHEMA, SKILL_PACK_RESPONSE_MAX_BYTES, SecureDirHardeningError, SkillPackInstallError, SqliteLocalTaskJournal, SteerUnsupportedError, TruthMemoryClient, TruthMemoryClientError, UnsupportedServicePlatformError, buildIcaclsArgs, cleanupEligibleAt, cleanupOrderFor, computePressureState, createAgentHomeProjection, createAgentHomeProjectionConsumer, createDaemon, createDaemonWithAdapters, createFilesystemCleanupExecutor, createServiceLifecycle, createStatfsFreeBytesProvider, ensureSecureDir, freezeRuntimeAdapterDescriptor, generateLaunchdPlist, generateSystemdUnit, generateWinswXml, installSkillPacks, isAgentMemorySecureFilesystemAvailable, isGitWorkspaceConfig, isRuntimeDisposalFailure, isRuntimeExecutionFailure, journalHash, listInstalledSkillPacks, localStateRelocation, nodeAgentProgram, prependGitWorkspaceGuidance, projectRuntimeBoundaryFailure, projectRuntimeExecutionFailure, projectSkillPack, readDeviceEnrollmentStatus, requestDeviceAssertion, resolveLocalAgentReleaseIdentity, resolveLocalStoragePolicy, sanitizeServiceName, sealRuntimeOperationManifest, skillPacksRoot, stableAgentHomeOwnerId, validateAgentRef };
17388
18988
  //# sourceMappingURL=index.js.map
17389
18989
  //# sourceMappingURL=index.js.map