@byok-sdk/client 0.11.0 → 0.12.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.
@@ -46,6 +46,8 @@ export declare class AgentEgressController {
46
46
  private readonly options;
47
47
  private readonly latest;
48
48
  private readonly spools;
49
+ private readonly spoolOpens;
50
+ private reliableAppendTail;
49
51
  private readonly latestStatus;
50
52
  private readonly reliableStatus;
51
53
  private readonly drops;
@@ -74,6 +76,8 @@ export declare class AgentEgressController {
74
76
  /** Missing ack capability holds records in their reliable lane; it never makes them lossy. */
75
77
  retryableReliableRecords(serverCapabilities: readonly string[]): readonly AgentReliableEgressRecord[];
76
78
  private spoolFor;
79
+ private bindSpool;
77
80
  private tenantPendingBytes;
81
+ private withAppendTail;
78
82
  private noteDrop;
79
83
  }
@@ -9,6 +9,15 @@ import type { DeviceCredentialStore, InMemoryDeviceCredentialStore } from './dev
9
9
  export declare class DeviceRevokedError extends Error {
10
10
  constructor(message?: string);
11
11
  }
12
+ /**
13
+ * Thrown when the local AuthManager deadline or shutdown cancels its own
14
+ * in-flight request. This is deliberately distinct from `DeviceRevokedError`:
15
+ * only an actual challenge/token HTTP 401 is server authority for revocation.
16
+ */
17
+ export declare class AuthRequestAbortedError extends Error {
18
+ readonly reason: 'deadline' | 'stopped';
19
+ constructor(reason: 'deadline' | 'stopped');
20
+ }
12
21
  export interface AuthManagerOptions {
13
22
  serverUrl: string;
14
23
  store: DeviceStore;
@@ -23,6 +32,8 @@ export interface AuthManagerOptions {
23
32
  * permission to supersede this machine's prior active device rows.
24
33
  */
25
34
  machineId?: () => Promise<string | undefined>;
35
+ /** Upper bound for one pair/challenge/token fetch plus its response-body read. */
36
+ authRequestDeadlineMs?: number;
26
37
  /** Called once revocation is detected, so a caller (ConnectionManager) can stop retrying and surface the state instead of looping. */
27
38
  onRevoked?: () => void;
28
39
  }
@@ -42,7 +53,10 @@ export declare class AuthManager {
42
53
  private stopped;
43
54
  private pairing;
44
55
  private credentialMutationTail;
56
+ /** The sole cancellation authority for the request currently inside the serialized credential mutation. */
57
+ private activeRequest;
45
58
  private readonly credentials;
59
+ private readonly requestDeadlineMs;
46
60
  constructor(opts: AuthManagerOptions);
47
61
  get deviceId(): string | undefined;
48
62
  isRevoked(): boolean;
@@ -62,6 +76,12 @@ export declare class AuthManager {
62
76
  /** Always throws — `never` return type lets call sites use `if (x === 401) this.markRevoked();` without an explicit `return`/`throw` of their own. */
63
77
  private markRevoked;
64
78
  private scheduleProactiveRenewal;
79
+ /**
80
+ * Bounds one complete auth exchange rather than fetch alone. Keeping the
81
+ * controller active through `json()`/`text()` makes a non-cooperative or
82
+ * partial response body cancellable by the same authority that owns fetch.
83
+ */
84
+ private runRequest;
65
85
  private runCredentialMutation;
66
86
  /** Read the current paired authority afresh; metadata without its OS secret is re-pair required. */
67
87
  private loadRecord;
@@ -1,9 +1,25 @@
1
1
  import type { BlobRef } from '@byok-sdk/protocol';
2
2
  import type { AuthManager } from './auth-manager';
3
+ export type BlobRequestAbortReason = 'deadline' | 'cancelled';
4
+ /** A blob request/body read did not complete before its deadline or its owner cancelled it. */
5
+ export declare class BlobRequestAbortedError extends Error {
6
+ readonly reason: BlobRequestAbortReason;
7
+ constructor(reason: BlobRequestAbortReason);
8
+ }
9
+ export interface BlobClientOptions {
10
+ /** Bound for each individual HTTP request and response-body read. Default: 15 seconds. */
11
+ requestDeadlineMs?: number;
12
+ /** Daemon lifecycle authority; aborting it stops all in-flight blob I/O. */
13
+ signal?: AbortSignal;
14
+ }
15
+ export interface BlobRequestOptions {
16
+ /** Task lifecycle authority; aborting it stops this transfer before finalization. */
17
+ signal?: AbortSignal;
18
+ }
3
19
  /** Seam `TaskRunner` depends on, so tests can substitute a fake without spinning up real HTTP endpoints. */
4
20
  export interface BlobResolver {
5
- resolveInstruction(blobRef: BlobRef): Promise<string>;
6
- uploadArtifact(content: string | Uint8Array, contentType: string, options?: {
21
+ resolveInstruction(blobRef: BlobRef, options?: BlobRequestOptions): Promise<string>;
22
+ uploadArtifact(content: string | Uint8Array, contentType: string, options?: BlobRequestOptions & {
7
23
  readonly idempotencyKey?: string;
8
24
  }): Promise<BlobRef>;
9
25
  }
@@ -16,11 +32,13 @@ export declare class BlobClient implements BlobResolver {
16
32
  #private;
17
33
  private readonly serverUrl;
18
34
  private readonly auth;
19
- constructor(serverUrl: string, auth: AuthManager);
35
+ private readonly options;
36
+ private readonly requestDeadlineMs;
37
+ constructor(serverUrl: string, auth: AuthManager, options?: BlobClientOptions);
20
38
  /** `blobRef` -> `GET /byok/blobs/:id/url` -> fetch the presigned download URL -> text content. Always resolves fresh rather than trusting any inlined `BlobRef.url`, per docs/protocol.md §7. */
21
- resolveInstruction(blobRef: BlobRef): Promise<string>;
22
- /** `POST /byok/blobs` (declares size/contentType/contentHash) -> PUT the bytes to the presigned upload URL -> a `BlobRef` for `task.artifact.blobRef`. */
23
- uploadArtifact(content: string | Uint8Array, contentType: string, options?: {
39
+ resolveInstruction(blobRef: BlobRef, options?: BlobRequestOptions): Promise<string>;
40
+ /** `POST /byok/blobs` -> PUT the bytes to the presigned URL -> finalize into a `BlobRef`. */
41
+ uploadArtifact(content: string | Uint8Array, contentType: string, options?: BlobRequestOptions & {
24
42
  readonly idempotencyKey?: string;
25
43
  }): Promise<BlobRef>;
26
44
  }
@@ -2,8 +2,10 @@ import { type CapabilityFlag, type Envelope, type RuntimeInfo, type ToolsetId }
2
2
  import { AuthManager } from './auth-manager';
3
3
  import type { CursorStore } from './cursor-store';
4
4
  import { type FleetJitter } from './deterministic-jitter';
5
+ import { ReplayCursorTooOldError } from './replay-cursor';
5
6
  import { type BackoffOptions, type ConnectionState, type LivenessOptions } from './ws-transport';
6
7
  export type { ConnectionState } from './ws-transport';
8
+ export { ReplayCursorTooOldError } from './replay-cursor';
7
9
  export interface ConnectionManagerOptions {
8
10
  serverUrl: string;
9
11
  deviceId: string;
@@ -35,6 +37,7 @@ export interface ConnectionManagerOptions {
35
37
  longPollIdleDelayMs?: number;
36
38
  fleetJitter?: FleetJitter;
37
39
  onOperationalOutcome?: (outcome: 'success' | 'failure', source: 'reconnect' | 'upload') => void;
40
+ onTerminalError?: (error: ReplayCursorTooOldError) => void;
38
41
  }
39
42
  /**
40
43
  * Owns the daemon's one logical connection to the server, which may be
@@ -119,6 +122,7 @@ export declare class ConnectionManager {
119
122
  private draining;
120
123
  private stopped;
121
124
  private revoked;
125
+ private terminalError;
122
126
  private settledWaiters;
123
127
  private pendingCursorSave;
124
128
  /**
@@ -249,6 +253,8 @@ export declare class ConnectionManager {
249
253
  * advertisement, and cleared across disconnect/switch boundaries.
250
254
  */
251
255
  getServerCapabilities(): readonly string[];
256
+ getTerminalError(): ReplayCursorTooOldError | undefined;
257
+ getMode(): 'ws' | 'long-poll';
252
258
  isConnected(): boolean;
253
259
  isRevoked(): boolean;
254
260
  /**
@@ -496,6 +502,7 @@ export declare class ConnectionManager {
496
502
  private onWsOutcome;
497
503
  private notifySettled;
498
504
  private enterLongPoll;
505
+ private enterReplayCursorTooOld;
499
506
  private exitLongPoll;
500
507
  private scheduleWsProbe;
501
508
  private enterRevoked;
@@ -91,6 +91,8 @@ export interface DaemonConfig {
91
91
  productName: string;
92
92
  productId: string;
93
93
  serverUrl: string;
94
+ /** Bounds one AuthManager pair/challenge/token exchange, including response-body reads. */
95
+ authRequestDeadlineMs?: number;
94
96
  deviceName?: string;
95
97
  /**
96
98
  * Optional override for the client-hashed physical machine identity sent
@@ -16,6 +16,18 @@ export interface DeviceMetadata {
16
16
  * from one pairing response with metadata from another.
17
17
  */
18
18
  export type DeviceRecord = DeviceMetadata & DeviceCredentials;
19
+ /**
20
+ * The one durable authority allowed before a first pairing response is
21
+ * received. It keeps the generated key immutable across a lost response, so
22
+ * an exact server-side retry can prove the same public-key binding.
23
+ */
24
+ export interface FirstPairingAttempt {
25
+ readonly kind: 'first-pairing-attempt-v1';
26
+ readonly deviceName: string;
27
+ readonly devicePublicKey: string;
28
+ readonly devicePrivateKeyPem: string;
29
+ readonly machineId?: string;
30
+ }
19
31
  export interface DeviceCommandResult {
20
32
  readonly exitCode: number;
21
33
  readonly stdout: string;
@@ -44,6 +56,8 @@ export declare class DeviceCredentialStore {
44
56
  #private;
45
57
  constructor(options: DeviceCredentialStoreOptions);
46
58
  read(): Promise<DeviceRecord | undefined>;
59
+ readFirstPairingAttempt(): Promise<FirstPairingAttempt | undefined>;
60
+ saveFirstPairingAttempt(attempt: FirstPairingAttempt): Promise<void>;
47
61
  replace(record: DeviceRecord): Promise<void>;
48
62
  /** Returns true only after the sole secret authority is confirmed absent. */
49
63
  clear(): Promise<boolean>;
@@ -52,6 +66,8 @@ export declare class DeviceCredentialStore {
52
66
  export declare class InMemoryDeviceCredentialStore {
53
67
  #private;
54
68
  read(): Promise<DeviceRecord | undefined>;
69
+ readFirstPairingAttempt(): Promise<FirstPairingAttempt | undefined>;
70
+ saveFirstPairingAttempt(attempt: FirstPairingAttempt): Promise<void>;
55
71
  replace(record: DeviceRecord): Promise<void>;
56
72
  clear(): Promise<boolean>;
57
73
  }
@@ -1,5 +1,6 @@
1
1
  import { type Envelope } from '@byok-sdk/protocol';
2
2
  import { AuthManager } from './auth-manager';
3
+ import { ReplayCursorTooOldError } from './replay-cursor';
3
4
  import { type TransportEndpoint } from './url';
4
5
  /**
5
6
  * A long-poll request failed in a way that today told the caller only
@@ -41,6 +42,8 @@ export interface LongPollClientOptions {
41
42
  onServerCapabilities?: (capabilities: string[]) => void;
42
43
  /** Called once the device is found to be revoked (401 surfaced through {@link AuthManager}) — the loop stops itself rather than retrying. */
43
44
  onRevoked?: () => void;
45
+ /** Called when the server cannot replay the durable cursor supplied to this poll. */
46
+ onReplayCursorTooOld?: (error: ReplayCursorTooOldError) => void;
44
47
  /**
45
48
  * M4 Phase 4 (version-negotiation drill fix), scope narrowed by finding F1:
46
49
  * called ONLY for a batch entry that failed to parse because its `type`
@@ -0,0 +1,9 @@
1
+ /**
2
+ * The server retained no contiguous replay history after the cursor the
3
+ * daemon acknowledged. This is terminal for the current device enrollment:
4
+ * retrying the same cursor can only repeat the loss condition.
5
+ */
6
+ export declare class ReplayCursorTooOldError extends Error {
7
+ readonly recoverableFrom?: number | undefined;
8
+ constructor(recoverableFrom?: number | undefined);
9
+ }
@@ -421,6 +421,7 @@ export declare class TaskRunner {
421
421
  private readonly deps;
422
422
  private readonly tasks;
423
423
  private readonly pendingMessageTasks;
424
+ private readonly messageOutboxesByHome;
424
425
  private readonly messageContextByToken;
425
426
  private readonly messageContextByTask;
426
427
  private readonly memoryContextByToken;
@@ -481,6 +482,8 @@ export declare class TaskRunner {
481
482
  * costs nothing.
482
483
  */
483
484
  private readonly inFlightOffers;
485
+ /** Blob I/O before an offer becomes an active task still belongs to that offer's cancellation authority. */
486
+ private readonly inFlightBlobAborts;
484
487
  /**
485
488
  * Finding P2 (Fix 2c): taskIds that have reached a terminal outcome
486
489
  * (Complete/Failed/Cancelled) this session — populated in `finish()`.
@@ -592,6 +595,7 @@ export declare class TaskRunner {
592
595
  }>;
593
596
  /** Restore activated, unaccepted message drafts before transport admission on daemon restart. */
594
597
  recoverAgentMessageOutboxes(agentsRoot: string): Promise<void>;
598
+ private agentMessageOutbox;
595
599
  /** Retry stable recovered records after a transport handshake/re-handshake. */
596
600
  retryRecoveredAgentMessages(): void;
597
601
  private sendAgentMessageRecord;
@@ -23,6 +23,12 @@ export interface TransportEndpoint {
23
23
  }
24
24
  /** The single construction site for {@link TransportEndpoint} — see that interface's own doc comment for why it is the only one. */
25
25
  export declare function describeEndpoint(transport: TransportEndpoint['transport'], url: string | URL): TransportEndpoint;
26
+ /**
27
+ * The only URL projection used by validation errors. Reading from the parsed
28
+ * structure means userinfo, query, and fragment never enter the diagnostic.
29
+ */
30
+ /** The sole secret-safe projection for a configured server URL diagnostic. */
31
+ export declare function formatServerUrl(value: string | URL): string;
26
32
  /**
27
33
  * M5: thrown by {@link assertServerUrlAllowed} — see that function's own doc
28
34
  * comment for the full allow/deny rule this names. Deliberately ONE error
@@ -68,7 +74,7 @@ export interface AssertServerUrlAllowedOptions {
68
74
  * `localhost` or any `*.localhost` subdomain, an IPv4 literal in
69
75
  * `127.0.0.0/8`, or the IPv6 loopback `::1` — see {@link isLoopbackHostname}.
70
76
  * - `http:`/`ws:` to any other host — refused with a clear, typed
71
- * {@link InsecureServerUrlError} naming the offending URL and the fix
77
+ * {@link InsecureServerUrlError} naming the redacted scheme/host/path and the fix
72
78
  * (use `wss:`/`https:`, or pass `dangerouslyAllowInsecureRemote: true` if
73
79
  * this is a deliberate, understood exception) — UNLESS
74
80
  * `opts.dangerouslyAllowInsecureRemote` is `true`.
package/dist/index.d.ts CHANGED
@@ -3,7 +3,7 @@ export type { AgentRef } from './agent-home';
3
3
  export { AgentHomeError, AgentRefValidationError, AgentHomeResolutionError, AgentHomeCollisionError, AgentHomeBusyError, AgentHomeLeaseCorruptError, AgentHomeLayout, AgentHomeLeaseManager, AgentHomeManager, createAgentHomeProjection, createAgentHomeProjectionConsumer, AGENT_HOME_PROJECTION_STATE_FILE, stableAgentHomeOwnerId, validateAgentRef, } from './agent-home';
4
4
  export { AgentSessionHandoffStore, AgentSessionHandoffStoreError, AgentSessionHandoffCorruptError, AgentSessionHandoffMismatchError, } from './daemon/agent-session-handoff-store';
5
5
  export type { AgentSessionHandoff, AgentSessionHandoffMatch, AgentTaskTerminalEvidence, AgentTaskTerminalMatch, AgentTerminalCause, } from './daemon/agent-session-handoff-store';
6
- export type { AgentHomeResolution, AgentHomeProjection, AgentHomeProjectionInput, AgentHomeProjectionApplyInput, AgentHomeProjectionFunction, AgentHomeProjectionApplyFunction, AgentHomeLease, AgentHomeBinding, } from './agent-home';
6
+ export type { AgentHomeResolution, AgentHomeProjection, AgentHomeProjectionInput, AgentHomeProjectionApplyInput, AgentHomeProjectionFunction, AgentHomeProjectionApplyFunction, AgentHomeLease, AgentHomeBinding, AgentHomeExecutionLease, AgentHomeExecutionBinding, } from './agent-home';
7
7
  export { localStateRelocation, LocalStateRelocationError, LocalStateRelocationBusyError, LocalStateRelocationIntegrityError, } from './local-state-relocation';
8
8
  export type { LocalStateRelocationInput, LocalStateRelocationLease, } from './local-state-relocation';
9
9
  export { PolicyUnsupportedError, SteerUnsupportedError, freezeRuntimeAdapterDescriptor, sealRuntimeOperationManifest } from './types';
@@ -62,8 +62,9 @@ export type { InstallSkillPacksOptions, InstalledSkillPack, ProjectedSkillPack,
62
62
  export { TruthMemoryClient, TruthMemoryClientError } from './daemon/truth-memory-client';
63
63
  export type { LocalMemoryFilter, MemorySelector, TruthManifestQueryInput, TruthManifestRecord, TruthMemoryClientErrorCode, TruthMemoryClientOptions, TruthMemoryMetric, TruthSnapshotCandidateInput, TruthSnapshotWriteInput, TruthTerminalWriteInput, TruthWriteBody, TruthWriteResult, VerifiedTruthRecord, } from './daemon/truth-memory-client';
64
64
  export type { ConnectionState } from './daemon/ws-transport';
65
- export { BlobClient } from './daemon/blob-client';
66
- export type { BlobResolver } from './daemon/blob-client';
65
+ export { ReplayCursorTooOldError } from './daemon/replay-cursor';
66
+ export { BlobClient, BlobRequestAbortedError } from './daemon/blob-client';
67
+ export type { BlobClientOptions, BlobRequestAbortReason, BlobRequestOptions, BlobResolver } from './daemon/blob-client';
67
68
  export { DaemonObserver } from './daemon/observer';
68
69
  export type { DaemonEvent, DaemonEventKind, DaemonEventListener, DaemonTaskInfo, Unsubscribe } from './daemon/observer';
69
70
  export { createServiceLifecycle, UnsupportedServicePlatformError } from './lifecycle/create-service-lifecycle';