@byok-sdk/client 0.9.1 → 0.10.1

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.
@@ -15,6 +15,14 @@ export interface AuthManagerOptions {
15
15
  /** Internal-only credential custody seam. Product construction uses store.credentials. */
16
16
  credentials?: DeviceCredentialStore | InMemoryDeviceCredentialStore;
17
17
  deviceName?: string;
18
+ /**
19
+ * Optional resolver for the client-hashed physical machine identity sent
20
+ * with `POST /byok/pair` (protocol §6.1). Resolved once per pair attempt; a
21
+ * resolver that yields `undefined` omits the field entirely rather than
22
+ * sending a placeholder, because the server treats its presence as
23
+ * permission to supersede this machine's prior active device rows.
24
+ */
25
+ machineId?: () => Promise<string | undefined>;
18
26
  /** Called once revocation is detected, so a caller (ConnectionManager) can stop retrying and surface the state instead of looping. */
19
27
  onRevoked?: () => void;
20
28
  }
@@ -91,6 +91,14 @@ export interface DaemonConfig {
91
91
  productId: string;
92
92
  serverUrl: string;
93
93
  deviceName?: string;
94
+ /**
95
+ * Optional override for the client-hashed physical machine identity sent
96
+ * with `POST /byok/pair` (protocol §6.1). Defaults to `resolveMachineId`
97
+ * over this product id, which probes one OS identifier and hashes it; a
98
+ * host that has its own machine authority can supply it here, and one that
99
+ * wants no supersession at all supplies `async () => undefined`.
100
+ */
101
+ machineId?: () => Promise<string | undefined>;
94
102
  workspaceRoot: string;
95
103
  /**
96
104
  * Strict Agent execution boundary. The host selects one absolute branded
@@ -0,0 +1,25 @@
1
+ import type { DeviceCommandRunner } from './device-credential-store';
2
+ export interface ResolveMachineIdOptions {
3
+ /** Domain separator for the digest — the same machine yields a different value per product. */
4
+ readonly productId: string;
5
+ readonly platform?: NodeJS.Platform;
6
+ /** Injectable process runner, same shape as the credential store's. Tests substitute a double; production gets `runDeviceCommand`. */
7
+ readonly run?: DeviceCommandRunner;
8
+ /** Injectable file read for the Linux probe, for the same reason `run` is injectable. */
9
+ readonly readFile?: (path: string) => Promise<string>;
10
+ /**
11
+ * Upper bound on the whole probe, in milliseconds (default 2000). A probe
12
+ * that never settles — a wedged `ioreg`, a hung registry read, an NFS-backed
13
+ * `/etc/machine-id` — must not stall pairing, because `AuthManager` awaits
14
+ * this value inline before `POST /byok/pair`. On expiry the resolution is
15
+ * `undefined`, the same as every other failure shape.
16
+ */
17
+ readonly timeoutMs?: number;
18
+ }
19
+ /**
20
+ * The lowercase-hex digest for this machine, or `undefined` when no OS
21
+ * identifier is available. Never throws: every probe failure, every
22
+ * unsupported platform, every empty read, and a probe that exceeds
23
+ * `timeoutMs` all collapse to the same `undefined`.
24
+ */
25
+ export declare function resolveMachineId(options: ResolveMachineIdOptions): Promise<string | undefined>;
package/dist/index.js CHANGED
@@ -6275,13 +6275,15 @@ var AuthManager = class {
6275
6275
  }
6276
6276
  const keyPair = existing ? { privateKey: importPrivateKeyPem(existing.devicePrivateKeyPem), publicKeyBase64Url: existing.devicePublicKey } : generateDeviceKeyPair();
6277
6277
  const url = new URL(BYOK_PAIR_PATH, toHttpBase(this.opts.serverUrl));
6278
+ const machineId = await this.opts.machineId?.();
6278
6279
  const res = await fetch(url, {
6279
6280
  method: "POST",
6280
6281
  headers: { "content-type": "application/json" },
6281
6282
  body: JSON.stringify({
6282
6283
  pairingCode,
6283
6284
  deviceName: this.opts.deviceName ?? os4__default.hostname(),
6284
- devicePublicKey: keyPair.publicKeyBase64Url
6285
+ devicePublicKey: keyPair.publicKeyBase64Url,
6286
+ ...machineId === void 0 ? {} : { machineId }
6285
6287
  })
6286
6288
  });
6287
6289
  if (!res.ok) {
@@ -6590,6 +6592,67 @@ var BlobClient = class {
6590
6592
  throw lastFailure;
6591
6593
  }
6592
6594
  };
6595
+ var DEFAULT_TIMEOUT_MS2 = 2e3;
6596
+ var DARWIN_UUID_RE = /"IOPlatformUUID"\s*=\s*"([^"]+)"/u;
6597
+ var WIN32_GUID_RE = /MachineGuid\s+REG_SZ\s+(\S+)/u;
6598
+ var LINUX_MACHINE_ID_PATHS = ["/etc/machine-id", "/var/lib/dbus/machine-id"];
6599
+ async function probeDarwin(run) {
6600
+ const result = await run("ioreg", ["-rd1", "-c", "IOPlatformExpertDevice"]);
6601
+ if (result.exitCode !== 0) return void 0;
6602
+ return DARWIN_UUID_RE.exec(result.stdout)?.[1];
6603
+ }
6604
+ async function probeLinux(readFile3) {
6605
+ for (const path38 of LINUX_MACHINE_ID_PATHS) {
6606
+ try {
6607
+ const contents = (await readFile3(path38)).trim();
6608
+ if (contents.length > 0) return contents;
6609
+ } catch {
6610
+ }
6611
+ }
6612
+ return void 0;
6613
+ }
6614
+ async function probeWin32(run) {
6615
+ const result = await run("reg", [
6616
+ "query",
6617
+ "HKLM\\SOFTWARE\\Microsoft\\Cryptography",
6618
+ "/v",
6619
+ "MachineGuid"
6620
+ ]);
6621
+ if (result.exitCode !== 0) return void 0;
6622
+ return WIN32_GUID_RE.exec(result.stdout)?.[1];
6623
+ }
6624
+ async function withTimeout(probe, timeoutMs) {
6625
+ let timer;
6626
+ const expiry = new Promise((resolve) => {
6627
+ timer = setTimeout(() => resolve(void 0), timeoutMs);
6628
+ timer.unref?.();
6629
+ });
6630
+ try {
6631
+ return await Promise.race([probe, expiry]);
6632
+ } finally {
6633
+ if (timer !== void 0) clearTimeout(timer);
6634
+ }
6635
+ }
6636
+ async function resolveMachineId(options) {
6637
+ const platform = options.platform ?? process.platform;
6638
+ const run = options.run ?? runDeviceCommand;
6639
+ const readFile3 = options.readFile ?? ((path38) => fs13.readFile(path38, "utf8"));
6640
+ let probe;
6641
+ if (platform === "darwin") probe = probeDarwin(run);
6642
+ else if (platform === "linux") probe = probeLinux(readFile3);
6643
+ else if (platform === "win32") probe = probeWin32(run);
6644
+ if (probe === void 0) return void 0;
6645
+ let raw;
6646
+ try {
6647
+ raw = await withTimeout(probe, options.timeoutMs ?? DEFAULT_TIMEOUT_MS2);
6648
+ } catch {
6649
+ return void 0;
6650
+ }
6651
+ const trimmed = raw?.trim();
6652
+ if (trimmed === void 0 || trimmed.length === 0) return void 0;
6653
+ return createHash("sha256").update(`${options.productId}
6654
+ ${trimmed}`, "utf8").digest("hex");
6655
+ }
6593
6656
  var PRESENCE_HINTS_CAPABILITY = "presence.hints";
6594
6657
  var CapabilityDiscoveryError = class extends Error {
6595
6658
  constructor(message, options) {
@@ -11189,7 +11252,7 @@ async function readPinnedFile(directory, fileName, maxBytes = AGENT_MEMORY_MAX_F
11189
11252
  });
11190
11253
  }
11191
11254
  }
11192
- async function readFile(context, relativePath, maxBytes = AGENT_MEMORY_MAX_FILE_BYTES) {
11255
+ async function readFile2(context, relativePath, maxBytes = AGENT_MEMORY_MAX_FILE_BYTES) {
11193
11256
  if (context.filesystem !== void 0) return context.filesystem.read(relativePath, maxBytes);
11194
11257
  return withMemoryParent(context, relativePath, (directory, fileName) => readPinnedFile(directory, fileName, maxBytes));
11195
11258
  }
@@ -11357,7 +11420,7 @@ var AgentMemoryService = class {
11357
11420
  const context = taskContext(this.input);
11358
11421
  const relativePath = validateAgentMemoryPath(input.path);
11359
11422
  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);
11423
+ const current = await readFile2(context, relativePath);
11361
11424
  if (!current.exists) throw new AgentMemoryError("memory file does not exist");
11362
11425
  if (input.ifRevision !== void 0 && current.revision !== input.ifRevision) throw new AgentMemoryRevisionConflictError(input.ifRevision, current.revision);
11363
11426
  const auditWarning = await exclusiveAgentMemoryHome(context.canonicalHome, () => recordAuditWarning(context, "recall", {
@@ -11444,7 +11507,7 @@ async function captureAgentMemorySnapshot(input) {
11444
11507
  const files = [];
11445
11508
  let totalBytes = 0;
11446
11509
  for (const relativePath of paths) {
11447
- const current = await readFile(context, relativePath);
11510
+ const current = await readFile2(context, relativePath);
11448
11511
  if (!current.exists) {
11449
11512
  if (relativePath === "MEMORY.md") throw new AgentMemoryError("MEMORY.md disappeared before snapshot");
11450
11513
  continue;
@@ -12960,6 +13023,7 @@ var TaskRunner = class {
12960
13023
  gitLease,
12961
13024
  gitBaseline,
12962
13025
  summaryParts: [],
13026
+ finalTextParts: [],
12963
13027
  batcher: new ProgressBatcher(
12964
13028
  (seq, events) => {
12965
13029
  const projected = active.egressEnabled ? this.deps.agentEgress?.projectLatestValue({
@@ -13265,6 +13329,9 @@ var TaskRunner = class {
13265
13329
  if (event.type === "usage") {
13266
13330
  active.lastUsage = event;
13267
13331
  }
13332
+ if (event.type === "tool_use" || event.type === "tool_result" || event.type === "needs_approval") {
13333
+ active.finalTextParts.length = 0;
13334
+ }
13268
13335
  if (event.type === "needs_approval") {
13269
13336
  active.batcher.flush();
13270
13337
  const { taskId } = active;
@@ -13298,7 +13365,8 @@ var TaskRunner = class {
13298
13365
  this.sendAgentMessageRecord(outbox, record);
13299
13366
  return;
13300
13367
  }
13301
- const body = finalOutput.trim();
13368
+ const finalTextRun = active.finalTextParts.join("").trim();
13369
+ const body = finalTextRun !== "" ? finalTextRun : finalOutput.trim();
13302
13370
  if (outbox === void 0 || active.agentRef === void 0) {
13303
13371
  active.pendingMessageCompletion = void 0;
13304
13372
  await this.fail(active.taskId, "required Agent message lane is unavailable for this task", false);
@@ -13342,6 +13410,7 @@ var TaskRunner = class {
13342
13410
  }
13343
13411
  if (event.type === "progress") {
13344
13412
  active.summaryParts.push(event.text);
13413
+ active.finalTextParts.push(event.text);
13345
13414
  }
13346
13415
  if (event.type === "artifact") {
13347
13416
  if (active.agentRef !== void 0 && this.deps.agentEgress !== void 0) {
@@ -16426,6 +16495,7 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
16426
16495
  serverUrl: config.serverUrl,
16427
16496
  store,
16428
16497
  deviceName: config.deviceName,
16498
+ machineId: config.machineId ?? (() => resolveMachineId({ productId: config.productId })),
16429
16499
  onRevoked: () => {
16430
16500
  connectionState = "revoked";
16431
16501
  }
@@ -17724,7 +17794,7 @@ function connectAndHandshake(endpoint, token, opts) {
17724
17794
  });
17725
17795
  });
17726
17796
  }
17727
- function withTimeout(promise, ms, message) {
17797
+ function withTimeout2(promise, ms, message) {
17728
17798
  return new Promise((resolve, reject) => {
17729
17799
  const timer = setTimeout(() => reject(new Error(message)), ms);
17730
17800
  timer.unref?.();
@@ -17803,7 +17873,7 @@ function createControlClient(socket, reader, opts) {
17803
17873
  async request(method, params) {
17804
17874
  if (closed) throw new Error("control connection is closed");
17805
17875
  const { promise } = send(method, params);
17806
- const result = await withTimeout(promise, opts.requestTimeoutMs ?? 1e4, `control request "${method}" timed out`);
17876
+ const result = await withTimeout2(promise, opts.requestTimeoutMs ?? 1e4, `control request "${method}" timed out`);
17807
17877
  return result;
17808
17878
  },
17809
17879
  subscribe(method, params, onEvent) {
@@ -18740,7 +18810,7 @@ ${args.map((a) => ` ${plistString(a)}`).join("\n")}
18740
18810
  }
18741
18811
  function createLaunchdLifecycle(def, deps = {}) {
18742
18812
  const run = deps.run ?? defaultRunner;
18743
- const fs29 = deps.fs ?? promises;
18813
+ const fs30 = deps.fs ?? promises;
18744
18814
  const homedir = deps.homedir ?? (() => os4__default.homedir());
18745
18815
  const getuid = deps.getuid ?? (() => {
18746
18816
  if (typeof process.getuid !== "function") {
@@ -18754,7 +18824,7 @@ function createLaunchdLifecycle(def, deps = {}) {
18754
18824
  const serviceTarget = () => `${domainTarget()}/${label}`;
18755
18825
  async function fileExists(p) {
18756
18826
  try {
18757
- await fs29.stat(p);
18827
+ await fs30.stat(p);
18758
18828
  return true;
18759
18829
  } catch {
18760
18830
  return false;
@@ -18762,9 +18832,9 @@ function createLaunchdLifecycle(def, deps = {}) {
18762
18832
  }
18763
18833
  async function writePlist(program) {
18764
18834
  const xml = generateLaunchdPlist({ label, program, logDir: def.logDir });
18765
- await fs29.mkdir(path3__default.dirname(plistPath()), { recursive: true });
18766
- await fs29.mkdir(def.logDir, { recursive: true });
18767
- await fs29.writeFile(plistPath(), xml, "utf8");
18835
+ await fs30.mkdir(path3__default.dirname(plistPath()), { recursive: true });
18836
+ await fs30.mkdir(def.logDir, { recursive: true });
18837
+ await fs30.writeFile(plistPath(), xml, "utf8");
18768
18838
  }
18769
18839
  async function install(opts = {}) {
18770
18840
  await writePlist(opts.program ?? def.program);
@@ -18775,7 +18845,7 @@ function createLaunchdLifecycle(def, deps = {}) {
18775
18845
  }
18776
18846
  async function uninstall() {
18777
18847
  await runIdempotent(run, "launchctl", ["bootout", serviceTarget()], "launchctl bootout", LAUNCHD_NOT_LOADED);
18778
- await fs29.rm(plistPath(), { force: true });
18848
+ await fs30.rm(plistPath(), { force: true });
18779
18849
  }
18780
18850
  async function start() {
18781
18851
  if (!await fileExists(plistPath())) {
@@ -18859,14 +18929,14 @@ WantedBy=default.target
18859
18929
  }
18860
18930
  function createSystemdLifecycle(def, deps = {}) {
18861
18931
  const run = deps.run ?? defaultRunner;
18862
- const fs29 = deps.fs ?? promises;
18932
+ const fs30 = deps.fs ?? promises;
18863
18933
  const homedir = deps.homedir ?? (() => os4__default.homedir());
18864
18934
  const name = sanitizeServiceName(def.name);
18865
18935
  const unitName = `${name}.service`;
18866
18936
  const unitPath = () => path3__default.join(homedir(), ".config", "systemd", "user", unitName);
18867
18937
  async function fileExists(p) {
18868
18938
  try {
18869
- await fs29.stat(p);
18939
+ await fs30.stat(p);
18870
18940
  return true;
18871
18941
  } catch {
18872
18942
  return false;
@@ -18874,9 +18944,9 @@ function createSystemdLifecycle(def, deps = {}) {
18874
18944
  }
18875
18945
  async function writeUnit(program) {
18876
18946
  const unit = generateSystemdUnit({ name, displayName: def.displayName ?? def.name, program, logDir: def.logDir });
18877
- await fs29.mkdir(path3__default.dirname(unitPath()), { recursive: true });
18878
- await fs29.mkdir(def.logDir, { recursive: true });
18879
- await fs29.writeFile(unitPath(), unit, "utf8");
18947
+ await fs30.mkdir(path3__default.dirname(unitPath()), { recursive: true });
18948
+ await fs30.mkdir(def.logDir, { recursive: true });
18949
+ await fs30.writeFile(unitPath(), unit, "utf8");
18880
18950
  }
18881
18951
  async function install(opts = {}) {
18882
18952
  await writeUnit(opts.program ?? def.program);
@@ -18885,7 +18955,7 @@ function createSystemdLifecycle(def, deps = {}) {
18885
18955
  }
18886
18956
  async function uninstall() {
18887
18957
  await runIdempotent(run, "systemctl", ["--user", "disable", "--now", unitName], "systemctl disable --now", SYSTEMD_NOT_LOADED);
18888
- await fs29.rm(unitPath(), { force: true });
18958
+ await fs30.rm(unitPath(), { force: true });
18889
18959
  await run("systemctl", ["--user", "daemon-reload"]);
18890
18960
  }
18891
18961
  async function start() {
@@ -18948,7 +19018,7 @@ ${argXml}${cwdXml}
18948
19018
  }
18949
19019
  function createWinswLifecycle(def, deps = {}) {
18950
19020
  const run = deps.run ?? defaultRunner;
18951
- const fs29 = deps.fs ?? promises;
19021
+ const fs30 = deps.fs ?? promises;
18952
19022
  const windows = def.windows;
18953
19023
  if (!windows) {
18954
19024
  throw new Error("WinSW service lifecycle requires `ServiceDefinition.windows.winswBin` (the product-bundled WinSW executable path)");
@@ -18960,7 +19030,7 @@ function createWinswLifecycle(def, deps = {}) {
18960
19030
  const xmlPath = path3__default.join(installDir, `${id}.xml`);
18961
19031
  async function fileExists(p) {
18962
19032
  try {
18963
- await fs29.stat(p);
19033
+ await fs30.stat(p);
18964
19034
  return true;
18965
19035
  } catch {
18966
19036
  return false;
@@ -18968,10 +19038,10 @@ function createWinswLifecycle(def, deps = {}) {
18968
19038
  }
18969
19039
  async function writeFiles(program) {
18970
19040
  const xml = generateWinswXml({ id, displayName: def.displayName ?? def.name, program, logDir: def.logDir });
18971
- await fs29.mkdir(installDir, { recursive: true });
18972
- await fs29.mkdir(def.logDir, { recursive: true });
18973
- await fs29.copyFile(winswBin, exePath);
18974
- await fs29.writeFile(xmlPath, xml, "utf8");
19041
+ await fs30.mkdir(installDir, { recursive: true });
19042
+ await fs30.mkdir(def.logDir, { recursive: true });
19043
+ await fs30.copyFile(winswBin, exePath);
19044
+ await fs30.writeFile(xmlPath, xml, "utf8");
18975
19045
  }
18976
19046
  async function install(opts = {}) {
18977
19047
  await writeFiles(opts.program ?? def.program);
@@ -18981,8 +19051,8 @@ function createWinswLifecycle(def, deps = {}) {
18981
19051
  async function uninstall() {
18982
19052
  await runIdempotent(run, exePath, ["stop"], "winsw stop", WINSW_NOT_INSTALLED);
18983
19053
  await runIdempotent(run, exePath, ["uninstall"], "winsw uninstall", WINSW_NOT_INSTALLED);
18984
- await fs29.rm(exePath, { force: true });
18985
- await fs29.rm(xmlPath, { force: true });
19054
+ await fs30.rm(exePath, { force: true });
19055
+ await fs30.rm(xmlPath, { force: true });
18986
19056
  }
18987
19057
  async function start() {
18988
19058
  if (!await fileExists(xmlPath)) {