@byok-sdk/client 0.8.0 → 0.8.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.
@@ -1,17 +1,32 @@
1
1
  import { type EnsureSecureDirOptions } from '../util/secure-dir';
2
- export interface DeviceRecord {
3
- deviceId: string;
4
- /** Opaque tenant binding returned by the authenticated pairing response. */
5
- tenantId: string;
6
- /** Current access token (JWT), renewed via challenge/token without re-pairing (protocol §6.2). */
7
- accessToken: string;
8
- /** ISO-8601 expiry for `accessToken` (our best knowledge of it — see auth-manager.ts for how this is derived after `/byok/pair`, which reports no explicit expiry itself). */
9
- expiresAt: string;
10
- /** Ed25519 private key, PKCS8 PEM. Never leaves this device. Stored as a 0600 file; OS keychain integration is deferred (tracked as a future roadmap item, not promised for any specific milestone). */
11
- devicePrivateKeyPem: string;
12
- /** Ed25519 public key, base64url — re-sent verbatim on a post-revocation re-pair (protocol §6.3). */
13
- devicePublicKey: string;
2
+ import { DeviceCredentialStore, InMemoryDeviceCredentialStore, type DeviceMetadata } from './device-credential-store';
3
+ export type { DeviceMetadata, DeviceRecord } from './device-credential-store';
4
+ /**
5
+ * Non-secret projection of an authenticated device enrollment. This is the
6
+ * complete permitted `device.json` shape. The complete record, including
7
+ * these authenticated metadata fields and secret bytes, lives atomically in
8
+ * DeviceCredentialStore; this file is only its deterministic projection.
9
+ *
10
+ * Internal only: the package root exposes DeviceEnrollment/status, never this
11
+ * storage record.
12
+ */
13
+ /** Public credential-blind result of explicit pairing. */
14
+ export interface DeviceEnrollment {
15
+ readonly deviceId: string;
16
+ }
17
+ export interface DeviceEnrollmentStatusOptions {
18
+ productId: string;
19
+ storeDir?: string;
14
20
  }
21
+ /** Credential-blind cold read model for host setup and diagnostics. */
22
+ export type DeviceEnrollmentStatus = {
23
+ state: 'unpaired';
24
+ } | {
25
+ state: 'paired';
26
+ deviceId: string;
27
+ } | {
28
+ state: 're_pair_required';
29
+ };
15
30
  /**
16
31
  * A durable enrollment record cannot be used by any steady-state path. Only
17
32
  * the explicit pair operation may replace it with a fresh authenticated row.
@@ -20,17 +35,17 @@ export declare class DeviceRecordRePairRequiredError extends Error {
20
35
  constructor();
21
36
  }
22
37
  /**
23
- * Persists the device identity issued by `pair()` deviceId, current
24
- * access token + its expiry, and the device's own Ed25519 keypair. This is
25
- * the ONLY credential material the daemon itself ever holds — never a
26
- * runtime's own credentials (see the credential-isolation rule on
27
- * `RuntimeAdapter`). Stored 0600 under `storeDir` (default
28
- * `~/.byok/<productId>/`); OS keychain storage is deferred (tracked as a
29
- * future roadmap item, not promised for any specific milestone).
38
+ * Persists only bounded non-secret enrollment projection. The paired bearer
39
+ * token and private key are never accepted here and are owned by the internal
40
+ * OS DeviceCredentialStore.
30
41
  */
31
42
  export declare class DeviceStore {
32
43
  private readonly secureDirOptions?;
44
+ /** Process-local keyed doubles preserve restart semantics in isolated tests. */
45
+ private static readonly testCredentials;
33
46
  private readonly filePath;
47
+ /** Internal test seam. Product construction always supplies productId and gets an OS store. */
48
+ readonly credentials: DeviceCredentialStore | InMemoryDeviceCredentialStore;
34
49
  /**
35
50
  * `secureDirOptions` is a test-only DI seam (mirrors `EnsureSecureDirOptions`'s
36
51
  * own `run`/`platform` overrides) — every real caller omits it, getting
@@ -41,7 +56,7 @@ export declare class DeviceStore {
41
56
  * ACL-unprotected credential") is verifiable from a real `darwin`/`linux`
42
57
  * CI/dev machine, not just asserted.
43
58
  */
44
- constructor(storeDir: string, secureDirOptions?: EnsureSecureDirOptions | undefined);
59
+ constructor(storeDir: string, secureDirOptions?: EnsureSecureDirOptions | undefined, productId?: string);
45
60
  static defaultDir(productId: string): string;
46
61
  /**
47
62
  * Resolve the one store pathname every daemon/CLI component must share.
@@ -50,14 +65,20 @@ export declare class DeviceStore {
50
65
  * cwd to pin a quarantine directory inode.
51
66
  */
52
67
  static resolveDir(productId: string, configured?: string): string;
53
- load(): Promise<DeviceRecord | undefined>;
68
+ load(): Promise<DeviceMetadata | undefined>;
54
69
  /**
55
70
  * Read and remove the exact bounded, no-follow device record under the
56
71
  * caller's mutation lease. The hard-link guard keeps the inspected inode
57
72
  * identifiable until the synchronous pathname check and unlink complete.
58
73
  */
59
- remove(): Promise<DeviceRecord | undefined>;
60
- save(record: DeviceRecord): Promise<void>;
61
- clear(): Promise<void>;
74
+ remove(): Promise<DeviceMetadata | undefined>;
75
+ save(record: DeviceMetadata): Promise<void>;
62
76
  private openBounded;
63
77
  }
78
+ /**
79
+ * Read the canonical device store without projecting credential or tenant
80
+ * material. A legacy/tampered record remains distinct from an absent record so
81
+ * hosts can require explicit re-pair instead of silently changing semantics.
82
+ * Filesystem and pathname-safety failures intentionally remain errors.
83
+ */
84
+ export declare function readDeviceEnrollmentStatus(options: DeviceEnrollmentStatusOptions): Promise<DeviceEnrollmentStatus>;
@@ -195,6 +195,8 @@ export interface TaskRunnerDeps {
195
195
  workspaceRoot: string;
196
196
  /** Strict Agent offer authority. Absent means legacy offers never resolve an Agent home. */
197
197
  agentHome?: AgentHomeManager;
198
+ /** Local authority: legacy offers are declined after journal/dedup/cancel precedence. */
199
+ strictAgentOnly?: boolean;
198
200
  /** Exact host-selected policy accepted by `task.offer_for_agent_with_egress`. */
199
201
  agentEgressPolicy?: Readonly<AgentEgressPolicy>;
200
202
  /** Always-present projection/sanitizer consumer; it defaults to metadata-only. */
@@ -479,6 +481,12 @@ export declare class TaskRunner {
479
481
  * cancellation intent.
480
482
  */
481
483
  private readonly finishedTaskIds;
484
+ /**
485
+ * Bounded local receive dedup for strict legacy declines. A decline is not a
486
+ * task terminal receipt, so it must never enter `finishedTaskIds`; retaining
487
+ * it separately keeps replay idempotent without claiming or finishing work.
488
+ */
489
+ private readonly strictDeclinedTaskIds;
482
490
  /**
483
491
  * M4 Phase 2 (daemon control socket `shutdown` RPC): set once by
484
492
  * {@link stopAcceptingOffers}, checked at the very top of `handleOffer` —
@@ -1019,6 +1027,7 @@ export declare class TaskRunner {
1019
1027
  private reserveSemanticTerminal;
1020
1028
  /** M3-B: bounded insert for `finishedTaskIds` — see its class-level doc comment and `MAX_TRACKED_TASK_IDS`. Evicts the oldest (first-inserted) entry once over cap, same idiom as `ConnectionHub.checkAndRecordDuplicate` (packages/server/src/hub.ts). */
1021
1029
  private addFinishedTaskId;
1030
+ private addStrictDeclinedTaskId;
1022
1031
  /** `reuseDir`, when set (a known sessionRef's recorded workspace), is used verbatim instead of a fresh `workspaceRoot/<taskId>` directory — `mkdir recursive` is idempotent either way, so ensuring-exists is safe to do unconditionally. */
1023
1032
  private resolveWorkspaceDir;
1024
1033
  /**
package/dist/index.d.ts CHANGED
@@ -4,6 +4,8 @@ export { AgentHomeError, AgentRefValidationError, AgentHomeResolutionError, Agen
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
6
  export type { AgentHomeResolution, AgentHomeProjection, AgentHomeProjectionInput, AgentHomeProjectionApplyInput, AgentHomeProjectionFunction, AgentHomeProjectionApplyFunction, AgentHomeLease, AgentHomeBinding, } from './agent-home';
7
+ export { localStateRelocation, LocalStateRelocationError, LocalStateRelocationBusyError, LocalStateRelocationIntegrityError, } from './local-state-relocation';
8
+ export type { LocalStateRelocationInput, LocalStateRelocationLease, } from './local-state-relocation';
7
9
  export { PolicyUnsupportedError, SteerUnsupportedError, freezeRuntimeAdapterDescriptor, sealRuntimeOperationManifest } from './types';
8
10
  export type { RuntimeEnvironmentRequirements } from './daemon/environment';
9
11
  export { resolveLocalAgentReleaseIdentity } from './release-identity';
@@ -39,10 +41,8 @@ export { SqliteLocalTaskJournal, JOURNAL_DB_FILENAME, JOURNAL_QUARANTINE_DIRNAME
39
41
  export type { SqliteLocalTaskJournalOptions } from './daemon/journal/sqlite-journal';
40
42
  export { LocalStoragePressureEngine, LocalStoragePolicyError, LocalStorageEmergencyError, resolveLocalStoragePolicy, computePressureState, cleanupOrderFor, cleanupEligibleAt, createFilesystemCleanupExecutor, createStatfsFreeBytesProvider, JOURNAL_TASK_REF_PREFIX, DEFAULT_SOFT_BUDGET_RATIO, DEFAULT_HARD_BUDGET_RATIO, DEFAULT_ACK_CRITICAL_RESERVE_BYTES, DEFAULT_CLEANUP_BATCH_LIMIT, DEFAULT_INCREMENTAL_VACUUM_PAGES, DEFAULT_NORMAL_COMPACTION_INTERVAL_MS, DEFAULT_PRESSURE_COMPACTION_INTERVAL_MS, DEFAULT_RETENTION_MS, DEFAULT_LOG_ROTATION, } from './daemon/journal/storage-policy';
41
43
  export type { LocalStoragePolicy, LocalStoragePolicyInput, LocalStoragePressureEngineOptions, LogRotationPolicy, CompactionPolicy, StoragePressureState, StoragePressureEvent, StorageMeasurement, StorageStatusSnapshot, StorageTickResult, CleanupExecutor, CleanupExecution, TimerLike, } from './daemon/journal/storage-policy';
42
- export type { DeviceRecord } from './daemon/store';
43
- export { AuthManager, DeviceRevokedError } from './daemon/auth-manager';
44
- export { StoredDeviceProofSigner } from './daemon/device-proof-signer';
45
- export type { DeviceProofRequest, DeviceProofSigner, StoredDeviceProofSignerOptions, } from './daemon/device-proof-signer';
44
+ export { readDeviceEnrollmentStatus } from './daemon/store';
45
+ export type { DeviceEnrollment, DeviceEnrollmentStatus, DeviceEnrollmentStatusOptions, } from './daemon/store';
46
46
  /**
47
47
  * Plan `skill-pack-delivery-channel`: the device half of the `skills.pack`
48
48
  * channel. The install pipeline and the two read APIs are public because the