@byok-sdk/client 0.4.2 → 0.5.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.
@@ -11,7 +11,7 @@ import { type LocalTaskJournal } from './journal/journal';
11
11
  import { type JournalOpenFaultSeam } from './journal/sqlite-support';
12
12
  import { LocalStoragePressureEngine, type LocalStoragePolicyInput } from './journal/storage-policy';
13
13
  import { type ResultDocumentExtractor } from './task-runner';
14
- import type { ProgressBatcherOptions } from './progress-batcher';
14
+ import { type ProgressBatcherOptions } from './progress-batcher';
15
15
  /**
16
16
  * Optional white-label product display info — purely opaque passthrough
17
17
  * (never interpreted, validated, or rendered by the daemon itself). Carried
@@ -223,6 +223,14 @@ export interface DaemonConfig {
223
223
  * explicitly instead to opt out of enforcement altogether.
224
224
  */
225
225
  maxTaskOutputBytes?: number;
226
+ /**
227
+ * Host-owned batching policy for normalized `task.progress` events.
228
+ * `maxBatchBytes`, when set, measures exactly the UTF-8 bytes of
229
+ * `JSON.stringify(events)` and must match the deployment's activity-ingress
230
+ * budget. It is deliberately unset by default because that ingress ceiling
231
+ * is deployment policy, not a frozen protocol constant.
232
+ */
233
+ progressBatch?: ProgressBatcherOptions;
226
234
  /**
227
235
  * additive-minor (`task.complete.document`): the seam through which this
228
236
  * product turns a finished task's final output text into the STRUCTURED
@@ -415,7 +423,6 @@ export interface Daemon {
415
423
  /** Internal seam so tests can substitute stub adapters / faster backoff+batch+liveness+long-poll timing. `createDaemonWithAdapters` (which takes this) is also the real entry point for products supplying a hand-built adapter set `createDaemon` can't construct on its own — e.g. custom adapter options, or an adapter that REPLACES a bundled runtime's implementation under the same id. Honest limit: an adapter id outside `pi`/`claude`/`codex` cannot pass wire validation today — `RuntimeIdSchema` (`@byok-sdk/protocol`) is a closed `z.enum(['pi', 'claude', 'codex'])`, and `isRuntimeId` filtering below (see `detectRuntimes`) drops any detected adapter outside that set before it ever reaches a wire-visible field. A genuinely fourth/namespaced runtime id is a future protocol change, not something this seam enables today. */
416
424
  export interface DaemonOverrides {
417
425
  backoff?: BackoffOptions;
418
- batch?: ProgressBatcherOptions;
419
426
  liveness?: LivenessOptions;
420
427
  /** M4 Phase 3: overrides `TaskRunner`'s default out-of-band approval wait (`DEFAULT_APPROVAL_TIMEOUT_MS`, 10 minutes) before an unanswered `requestApproval` force-resolves as a fail-closed rejection. */
421
428
  approvalTimeoutMs?: number;
@@ -5,7 +5,19 @@ export interface ProgressBatcherOptions {
5
5
  maxBatchSize?: number;
6
6
  /** Otherwise flush at most this often (ms) while events are pending. Default 250 (~4/sec). */
7
7
  flushIntervalMs?: number;
8
+ /**
9
+ * Optional deployment-owned ceiling for the UTF-8 bytes in the serialized
10
+ * `events[]` array. Unset means no byte ceiling; hosts should inject the
11
+ * same value their activity ingress enforces.
12
+ */
13
+ maxBatchBytes?: number;
8
14
  }
15
+ export declare class ProgressEventTooLargeError extends Error {
16
+ readonly actualBytes: number;
17
+ readonly maxBatchBytes: number;
18
+ constructor(actualBytes: number, maxBatchBytes: number);
19
+ }
20
+ export declare function validateProgressBatcherOptions(options?: ProgressBatcherOptions): void;
9
21
  /**
10
22
  * Coalesces a task's `AgentEvent`s into seq-ordered `task.progress` batches:
11
23
  * flush immediately at `maxBatchSize` events, otherwise at most every
@@ -19,6 +31,7 @@ export declare class ProgressBatcher {
19
31
  private timer;
20
32
  private readonly maxBatchSize;
21
33
  private readonly flushIntervalMs;
34
+ private readonly maxBatchBytes;
22
35
  constructor(emit: ProgressEmitter, options?: ProgressBatcherOptions);
23
36
  push(event: AgentEvent): void;
24
37
  /** M4 Phase 4 (part B.3, observability): events buffered right now, not yet flushed as a `task.progress` batch — a cheap per-task queue-depth watermark for the daemon's control-socket `status` result (see `task-runner.ts`'s `getQueueWatermarks`). */
@@ -102,6 +102,8 @@ export declare const MAX_TRACKED_TASK_IDS = 2000;
102
102
  export declare const MAX_DURATION_EXCEEDED_REASON_PREFIX = "resource limit exceeded: maxDurationMs";
103
103
  /** M5 batch-3 (workstream 2): same contract as {@link MAX_DURATION_EXCEEDED_REASON_PREFIX}, for `DaemonConfig.maxTaskOutputBytes` — see `TaskRunner.pump`'s own per-event byte counting. */
104
104
  export declare const MAX_OUTPUT_BYTES_EXCEEDED_REASON_PREFIX = "resource limit exceeded: maxTaskOutputBytes";
105
+ /** Stable fail-closed reason for one normalized event that cannot fit the configured activity batch budget. */
106
+ export declare const MAX_PROGRESS_BATCH_BYTES_EXCEEDED_REASON_PREFIX = "resource limit exceeded: progressBatch.maxBatchBytes";
105
107
  /**
106
108
  * additive-minor (`task.complete.document`): same stable-PREFIX contract as
107
109
  * {@link MAX_DURATION_EXCEEDED_REASON_PREFIX} above, carried by every
package/dist/index.d.ts CHANGED
@@ -9,6 +9,7 @@ export { GitWorkspaceStore } from './daemon/git-workspace-store';
9
9
  export type { GitWorkspaceLedger, GitWorkspaceLedgerRecord, GitWorkspacePhase } from './daemon/git-workspace-store';
10
10
  export { createDaemon, createDaemonWithAdapters } from './daemon/create-daemon';
11
11
  export type { Daemon, DaemonConfig, DaemonStatus, DaemonOverrides, DaemonBranding, HostedJournalConfig, DeviceAssertionConfig } from './daemon/create-daemon';
12
+ export type { ProgressBatcherOptions } from './daemon/progress-batcher';
12
13
  /**
13
14
  * Plan `device-assertion-broker`: the ONLY control-socket capability this
14
15
  * package exposes publicly. `connectControlClient`/`ControlClient` are
package/dist/index.js CHANGED
@@ -8188,6 +8188,11 @@ function createStatfsFreeBytesProvider(dir) {
8188
8188
  var BASE_PLATFORM_ALLOWLIST = [
8189
8189
  "PATH",
8190
8190
  "HOME",
8191
+ // macOS credential-store discovery used by subscription-authenticated
8192
+ // agent CLIs depends on the login account name as well as HOME. Omitting
8193
+ // USER makes `claude auth status` report logged out under the filtered
8194
+ // child environment even when the host CLI is logged in.
8195
+ "USER",
8191
8196
  "USERPROFILE",
8192
8197
  "TMPDIR",
8193
8198
  "TEMP",
@@ -8295,11 +8300,37 @@ function computeEffectivePolicy(offered, ceiling) {
8295
8300
  }
8296
8301
 
8297
8302
  // src/daemon/progress-batcher.ts
8303
+ var ProgressEventTooLargeError = class extends Error {
8304
+ constructor(actualBytes, maxBatchBytes) {
8305
+ super(`Progress event requires ${actualBytes} UTF-8 bytes, exceeding maxBatchBytes ${maxBatchBytes}.`);
8306
+ this.actualBytes = actualBytes;
8307
+ this.maxBatchBytes = maxBatchBytes;
8308
+ this.name = "ProgressEventTooLargeError";
8309
+ }
8310
+ actualBytes;
8311
+ maxBatchBytes;
8312
+ };
8313
+ var encoder = new TextEncoder();
8314
+ function assertPositiveSafeInteger(value, name) {
8315
+ if (value !== void 0 && (!Number.isSafeInteger(value) || value <= 0)) {
8316
+ throw new TypeError(`${name} must be a positive safe integer when configured.`);
8317
+ }
8318
+ }
8319
+ function validateProgressBatcherOptions(options = {}) {
8320
+ assertPositiveSafeInteger(options.maxBatchSize, "maxBatchSize");
8321
+ assertPositiveSafeInteger(options.flushIntervalMs, "flushIntervalMs");
8322
+ assertPositiveSafeInteger(options.maxBatchBytes, "maxBatchBytes");
8323
+ }
8324
+ function encodedEventsBytes(events) {
8325
+ return encoder.encode(JSON.stringify(events)).length;
8326
+ }
8298
8327
  var ProgressBatcher = class {
8299
8328
  constructor(emit, options = {}) {
8300
8329
  this.emit = emit;
8330
+ validateProgressBatcherOptions(options);
8301
8331
  this.maxBatchSize = options.maxBatchSize ?? 10;
8302
8332
  this.flushIntervalMs = options.flushIntervalMs ?? 250;
8333
+ this.maxBatchBytes = options.maxBatchBytes;
8303
8334
  }
8304
8335
  emit;
8305
8336
  buffer = [];
@@ -8307,7 +8338,17 @@ var ProgressBatcher = class {
8307
8338
  timer;
8308
8339
  maxBatchSize;
8309
8340
  flushIntervalMs;
8341
+ maxBatchBytes;
8310
8342
  push(event) {
8343
+ if (this.maxBatchBytes !== void 0) {
8344
+ const eventBytes = encodedEventsBytes([event]);
8345
+ if (eventBytes > this.maxBatchBytes) {
8346
+ throw new ProgressEventTooLargeError(eventBytes, this.maxBatchBytes);
8347
+ }
8348
+ if (this.buffer.length > 0 && encodedEventsBytes([...this.buffer, event]) > this.maxBatchBytes) {
8349
+ this.flush();
8350
+ }
8351
+ }
8311
8352
  this.buffer.push(event);
8312
8353
  if (this.buffer.length >= this.maxBatchSize) {
8313
8354
  this.flush();
@@ -8360,6 +8401,7 @@ var MAX_INLINE_ARTIFACT_BYTES = 64 * 1024;
8360
8401
  var MAX_TRACKED_TASK_IDS = 2e3;
8361
8402
  var MAX_DURATION_EXCEEDED_REASON_PREFIX = "resource limit exceeded: maxDurationMs";
8362
8403
  var MAX_OUTPUT_BYTES_EXCEEDED_REASON_PREFIX = "resource limit exceeded: maxTaskOutputBytes";
8404
+ var MAX_PROGRESS_BATCH_BYTES_EXCEEDED_REASON_PREFIX = "resource limit exceeded: progressBatch.maxBatchBytes";
8363
8405
  var RESULT_DOCUMENT_UNDELIVERABLE_REASON_PREFIX = "result document undeliverable";
8364
8406
  function resultDocumentRejectionDetail(check) {
8365
8407
  switch (check.reason) {
@@ -9216,6 +9258,13 @@ var TaskRunner = class {
9216
9258
  } catch (err) {
9217
9259
  if (this.tasks.get(active.taskId) !== active || active.beingTornDown) return;
9218
9260
  active.batcher.flush();
9261
+ if (err instanceof ProgressEventTooLargeError) {
9262
+ await this.failActiveTaskForResourceLimit(
9263
+ active.taskId,
9264
+ `${MAX_PROGRESS_BATCH_BYTES_EXCEEDED_REASON_PREFIX}: event requires ${err.actualBytes} UTF-8 bytes, exceeding the configured limit of ${err.maxBatchBytes} bytes`
9265
+ );
9266
+ return;
9267
+ }
9219
9268
  const failure = projectRuntimeBoundaryFailure(err, "run");
9220
9269
  if (failure.contractViolation) {
9221
9270
  console.error("[byok/client] runtime adapter events iterable returned an untyped failure", err);
@@ -10302,6 +10351,7 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
10302
10351
  `DaemonConfig.maxTaskOutputBytes must be a positive number (or omitted to use the ${DEFAULT_MAX_TASK_OUTPUT_BYTES}-byte default) \u2014 got ${config.maxTaskOutputBytes}. Pass Number.POSITIVE_INFINITY to explicitly disable the cap; 0 or a negative number is rejected rather than silently treated as "disabled".`
10303
10352
  );
10304
10353
  }
10354
+ validateProgressBatcherOptions(config.progressBatch);
10305
10355
  const presenceCadence = {
10306
10356
  intervalMs: config.presence?.intervalMs ?? DEFAULT_PRESENCE_HEARTBEAT_INTERVAL_MS,
10307
10357
  ttlMs: config.presence?.ttlMs ?? DEFAULT_PRESENCE_TTL_MS,
@@ -10587,7 +10637,7 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
10587
10637
  // untouched. See `observer.ts`'s module doc comment.
10588
10638
  send: sendEnvelope,
10589
10639
  blobClient,
10590
- batcherOptions: overrides.batch,
10640
+ batcherOptions: config.progressBatch,
10591
10641
  sessionWorkspaces,
10592
10642
  gitWorkspaceManager,
10593
10643
  gitWorkspaceStore,