@byok-sdk/client 0.3.0 → 0.4.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.
Files changed (41) hide show
  1. package/README.md +14 -1
  2. package/dist/adapters/claude/claude-adapter.d.ts +4 -20
  3. package/dist/adapters/claude/events.d.ts +3 -0
  4. package/dist/adapters/claude/process-client.d.ts +15 -1
  5. package/dist/adapters/codex/codex-adapter.d.ts +4 -16
  6. package/dist/adapters/codex/process-runner.d.ts +9 -1
  7. package/dist/adapters/index.d.ts +3 -1
  8. package/dist/adapters/index.js +1057 -261
  9. package/dist/adapters/index.js.map +1 -1
  10. package/dist/adapters/pi/pi-adapter.d.ts +3 -16
  11. package/dist/adapters/pi/resolve-bin.d.ts +1 -1
  12. package/dist/adapters/pi/rpc-client.d.ts +15 -1
  13. package/dist/adapters/process-tree.d.ts +60 -0
  14. package/dist/adapters/taskkill-pid-set.d.ts +34 -0
  15. package/dist/bin/audit-log.d.ts +12 -0
  16. package/dist/bin/byok-agent.js +1452 -507
  17. package/dist/bin/byok-agent.js.map +1 -1
  18. package/dist/bin/byok-approval-mcp.js.map +1 -1
  19. package/dist/bin/commands/workspaces.d.ts +11 -0
  20. package/dist/bin/format.d.ts +13 -0
  21. package/dist/bin/runtime-probe.d.ts +1 -1
  22. package/dist/bin/tasks-view.d.ts +13 -0
  23. package/dist/daemon/approvals.d.ts +2 -2
  24. package/dist/daemon/connection-manager.d.ts +15 -17
  25. package/dist/daemon/control-server.d.ts +18 -1
  26. package/dist/daemon/create-daemon.d.ts +2 -2
  27. package/dist/daemon/daemon-owner.d.ts +4 -2
  28. package/dist/daemon/environment.d.ts +9 -9
  29. package/dist/daemon/git-workspace.d.ts +21 -0
  30. package/dist/daemon/long-poll-transport.d.ts +6 -0
  31. package/dist/daemon/observer.d.ts +13 -0
  32. package/dist/daemon/presence-publisher.d.ts +29 -0
  33. package/dist/daemon/runtime-capabilities.d.ts +1 -1
  34. package/dist/daemon/task-runner.d.ts +34 -40
  35. package/dist/daemon/ws-transport.d.ts +3 -1
  36. package/dist/index.d.ts +4 -2
  37. package/dist/index.js +1413 -455
  38. package/dist/index.js.map +1 -1
  39. package/dist/runtime-failure.d.ts +64 -0
  40. package/dist/types.d.ts +100 -73
  41. package/package.json +14 -14
@@ -2,7 +2,7 @@
2
2
  * M5: per-runtime environment allowlist for spawned agent child processes.
3
3
  *
4
4
  * Before this module existed, `task-runner.ts` built every task's
5
- * `TaskContext.env` as `process.env` verbatim — the daemon's OWN full
5
+ * `RuntimeOperationStartInput.env` as `process.env` verbatim — the daemon's OWN full
6
6
  * environment, unfiltered, handed to whichever runtime CLI (`pi`/`claude`/
7
7
  * `codex`) `pickAdapter` selected. Any credential-shaped variable sitting in
8
8
  * the daemon's own environment for a completely unrelated reason (an
@@ -20,10 +20,9 @@
20
20
  * behave sanely in a non-interactive shell.
21
21
  * 2. Whatever ADDITIONAL names the *specific* runtime adapter about to be
22
22
  * spawned declares it actually needs
23
- * (`RuntimeAdapter.environmentRequirements()` — see `../types.ts`). An
24
- * adapter that declares nothing at all (doesn't implement the optional
25
- * method) gets the platform baseline ONLY fail-closed by construction,
26
- * not by an extra check here.
23
+ * (`RuntimeAdapter.descriptor.environmentRequirements` — see
24
+ * `../types.ts`). A descriptor that declares no names gets the platform
25
+ * baseline only; descriptors are required and frozen before claim.
27
26
  * 3. A per-device, per-runtime operator override (`DaemonConfig
28
27
  * .runtimeEnvironment` — see `create-daemon.ts`) — a local escape hatch
29
28
  * for a product/operator that knows it needs one more variable forwarded
@@ -39,8 +38,8 @@
39
38
  */
40
39
  /**
41
40
  * What one runtime adapter declares it needs beyond the always-included
42
- * platform baseline. Returned from the optional
43
- * `RuntimeAdapter.environmentRequirements()` method (`../types.ts`).
41
+ * platform baseline. Declared in the required frozen
42
+ * `RuntimeAdapter.descriptor.environmentRequirements` (`../types.ts`).
44
43
  */
45
44
  export interface RuntimeEnvironmentRequirements {
46
45
  /**
@@ -69,8 +68,9 @@ export interface BuildRuntimeEnvOptions {
69
68
  ambient: NodeJS.ProcessEnv;
70
69
  /**
71
70
  * The selected runtime adapter's own declared requirements —
72
- * `undefined` (no `environmentRequirements()` implementation on that
73
- * adapter) means "platform baseline only," fail-closed.
71
+ * `undefined` means "platform baseline only" for this helper. The public
72
+ * RuntimeAdapter descriptor always supplies this object before TaskRunner
73
+ * invokes the helper.
74
74
  */
75
75
  requirements?: RuntimeEnvironmentRequirements;
76
76
  /**
@@ -2,6 +2,27 @@ export interface GitWorkspaceConfig {
2
2
  mode: 'local-checkpoints';
3
3
  }
4
4
  export type GitErrorCategory = 'git-unavailable' | 'git-timeout' | 'git-output-limit' | 'git-command-failed' | 'workspace-root-invalid' | 'workspace-root-conflict' | 'workspace-not-owned' | 'repository-root-mismatch' | 'repository-invalid' | 'lease-busy' | 'ledger-invalid';
5
+ /**
6
+ * Runtime projection of {@link GitErrorCategory}: every union member, once,
7
+ * in union order — the single source of truth the CLI's stable-output
8
+ * validators (`bin/format.ts`, `bin/audit-log.ts`, `bin/tasks-view.ts`,
9
+ * `bin/commands/workspaces.ts`) project from when deciding which category
10
+ * strings from ledger records are stable enough to render, so those filters
11
+ * can never drift from the union. The `satisfies` half rejects a string
12
+ * that isn't a union member; the `AssertExhaustive` proof below rejects a
13
+ * union member missing from this list — extending either side alone is a
14
+ * compile error. The runtime half (no duplicates, every consumer projects
15
+ * exactly this list) is `__tests__/git-category-drift.test.ts`.
16
+ */
17
+ export declare const GIT_ERROR_CATEGORIES: readonly ["git-unavailable", "git-timeout", "git-output-limit", "git-command-failed", "workspace-root-invalid", "workspace-root-conflict", "workspace-not-owned", "repository-root-mismatch", "repository-invalid", "lease-busy", "ledger-invalid"];
18
+ /**
19
+ * Runtime projection of `GitWorkspacePhase` (the type itself lives in
20
+ * `git-workspace-store.ts`; the projection lives here beside
21
+ * {@link GIT_ERROR_CATEGORIES} so both category/phase single sources ship
22
+ * from one module) — same exhaustiveness contract, consumed by
23
+ * `bin/tasks-view.ts`'s phase filter.
24
+ */
25
+ export declare const GIT_WORKSPACE_PHASES: readonly ["preparing", "active", "completed", "failed", "cancelled", "interrupted", "salvage"];
5
26
  export declare class GitWorkspaceError extends Error {
6
27
  readonly category: GitErrorCategory;
7
28
  constructor(category: GitErrorCategory, message?: string);
@@ -5,6 +5,12 @@ export interface LongPollClientOptions {
5
5
  auth: AuthManager;
6
6
  getCursor: () => number | undefined;
7
7
  onEnvelope: (envelope: Envelope) => void;
8
+ /**
9
+ * Capabilities advertised by the server that produced the current poll
10
+ * response. Called before any envelopes from that response are delivered.
11
+ * An older responder omitting the additive field is reported as `[]`.
12
+ */
13
+ onServerCapabilities?: (capabilities: string[]) => void;
8
14
  /** Called once the device is found to be revoked (401 surfaced through {@link AuthManager}) — the loop stops itself rather than retrying. */
9
15
  onRevoked?: () => void;
10
16
  /**
@@ -188,6 +188,13 @@ export type DaemonEvent = {
188
188
  conflicted: number;
189
189
  };
190
190
  errorCategory?: string;
191
+ } | {
192
+ kind: 'runtime-disposal-failed';
193
+ ts: string;
194
+ taskId: string;
195
+ runtimeId: string;
196
+ stage: 'signal' | 'quiescence' | 'cleanup';
197
+ reason: string;
191
198
  }
192
199
  /**
193
200
  * Plan `device-assertion-broker`: one `assertion.issue` control call
@@ -350,6 +357,12 @@ export declare class DaemonObserver {
350
357
  };
351
358
  errorCategory?: string;
352
359
  }): void;
360
+ noteRuntimeDisposalFailure(event: {
361
+ taskId: string;
362
+ runtimeId: string;
363
+ stage: 'signal' | 'quiescence' | 'cleanup';
364
+ reason: string;
365
+ }): void;
353
366
  /**
354
367
  * Finding F4: wired from `TaskRunnerDeps.onApprovalDispatched`, called
355
368
  * synchronously by `TaskRunner.dispatchApproval` BEFORE its own
@@ -1,3 +1,30 @@
1
+ /**
2
+ * Presence producer (§12.3): a periodic `{ level: 'online' }` heartbeat to
3
+ * `PUT /byok/presence`, started only when the deployment's capability
4
+ * declaration contains `presence.hints` (ADR-010 — see `capabilities-client.ts`).
5
+ *
6
+ * Three properties this module deliberately holds:
7
+ *
8
+ * - **Only `online`.** The five levels exist in core, but nothing downstream
9
+ * consumes a `thinking`/`working`/`error` mapping yet, so none is invented
10
+ * here. That also keeps the rule below trivially true.
11
+ * - **It never reads or writes task state.** Presence is a lossy, unsigned,
12
+ * TTL-bounded hint that must never become coordination or execution state
13
+ * (`@byok-sdk/core`'s `presence.ts` module doc). This publisher's only input
14
+ * is a clock.
15
+ * - **Stopping IS the offline signal.** No explicit `offline` publish on
16
+ * shutdown: the hosted hint carries a TTL and expiry means *absence*, so a
17
+ * daemon that stops beating disappears on its own. Publishing `offline` too
18
+ * would express the same fact through a second channel that a crashed daemon
19
+ * could never use anyway.
20
+ *
21
+ * Auth reuses the daemon's one device-token lifecycle: {@link authedFetch}
22
+ * attaches the current bearer and, on a 401, renews once through `AuthManager`
23
+ * and retries exactly once. A revoked device (`DeviceRevokedError`) stops this
24
+ * publisher permanently — there is no recourse but a fresh `pair()`, so
25
+ * retrying would be a pure spin.
26
+ */
27
+ import { type ToolsetId } from '@byok-sdk/protocol';
1
28
  import type { AuthManager } from './auth-manager';
2
29
  /**
3
30
  * Client-side defaults, chosen against the hosted defaults
@@ -30,6 +57,8 @@ export declare function assertPresenceHeartbeatCadence(cadence: {
30
57
  export interface PresencePublisherOptions {
31
58
  serverUrl: string;
32
59
  auth: AuthManager;
60
+ /** Sorted logical IDs only. Executable MCP definitions and credentials remain device-local. */
61
+ configuredToolsets?: readonly ToolsetId[];
33
62
  /** Heartbeat cadence. Must sit strictly between {@link PresencePublisherOptions.minimumIntervalMs} and {@link PresencePublisherOptions.ttlMs}. */
34
63
  intervalMs?: number;
35
64
  /** The deployment's presence hint TTL, as this daemon understands it. Only used to validate the cadence. */
@@ -1,7 +1,7 @@
1
1
  import type { RuntimeCapabilities as ProtocolRuntimeCapabilities } from '@byok-sdk/protocol';
2
2
  import type { RuntimeCapabilities } from '../types';
3
3
  /**
4
- * Maps a `RuntimeAdapter`'s own internal `capabilities()` result
4
+ * Maps a frozen `RuntimeAdapterDescriptor`'s internal `capabilities` value
5
5
  * (`../types.ts`'s `RuntimeCapabilities` — `{steer, resume,
6
6
  * approvalInteractive, permissionModes}`, always-required fields) onto the
7
7
  * wire's `RuntimeCapabilities` shape (`@byok-sdk/protocol` — the same field names,
@@ -1,5 +1,6 @@
1
1
  import { type Envelope, type PermissionPolicy, type RuntimeId, type TaskOfferPayload, type TaskOfferWithToolsetsPayload } from '@byok-sdk/protocol';
2
2
  import { type McpToolsetConfig, type RuntimeAdapter } from '../types';
3
+ import { type RuntimeDisposalStage } from '../runtime-failure';
3
4
  import { type ApprovalDecision, type ApprovalOrigin, type ApprovalRegistry } from './approvals';
4
5
  import type { BlobResolver } from './blob-client';
5
6
  import type { TaskQueueWatermark } from './control-protocol';
@@ -205,13 +206,20 @@ export interface TaskRunnerDeps {
205
206
  observation?: GitWorkspaceObservation;
206
207
  errorCategory?: string;
207
208
  }) => void;
209
+ /** Local-only evidence that a semantic terminal outcome could not yet release its runtime ownership. */
210
+ onRuntimeDisposalFailure?: (event: {
211
+ taskId: string;
212
+ runtimeId: string;
213
+ stage: RuntimeDisposalStage;
214
+ reason: string;
215
+ }) => void;
208
216
  /**
209
217
  * M4 Phase 3: this daemon's control-socket identity + the shared registry
210
218
  * backing the control socket's own `approvals.list`/`approvals.resolve`
211
219
  * methods (`create-daemon.ts` constructs ONE `ApprovalRegistry` and passes
212
220
  * the SAME instance here) — see `requestApproval`'s own doc comment for
213
221
  * why `TaskRunner` needs a handle on all three. `storeDir`/`productId` are
214
- * copied verbatim into every task's `TaskContext.approvalChannel`.
222
+ * copied verbatim into every prepared operation's approval channel.
215
223
  */
216
224
  approvalRegistry: ApprovalRegistry;
217
225
  storeDir: string;
@@ -244,7 +252,7 @@ export interface TaskRunnerDeps {
244
252
  * to supply one — mirrors `onStaleApprovalDecision`'s own contract.
245
253
  */
246
254
  onApprovalDispatched?: (taskId: string, approvalId: string) => void;
247
- /** Finding F5(a): overrides {@link DEFAULT_SHUTDOWN_INTERRUPT_TIMEOUT_MS} — see `teardownActiveTask`'s own doc comment. Also the grace window `teardownActiveTask`'s hard-kill escalation (`session.close()`) gets once `session.interrupt()` itself fails to settle in time — see that method's own doc comment for why the same window is reused for both steps. */
255
+ /** Overrides the bounded soft-interrupt window before authoritative `Session.close()` disposal begins. */
248
256
  shutdownInterruptTimeoutMs?: number;
249
257
  /**
250
258
  * M5 batch-3 (workstream 2): overrides {@link DEFAULT_MAX_TASK_OUTPUT_BYTES}
@@ -256,9 +264,10 @@ export interface TaskRunnerDeps {
256
264
  */
257
265
  maxTaskOutputBytes?: number;
258
266
  /**
259
- * M4 (additive-minor, `task.approval_resolved`): the negotiated
260
- * `conn.ack.capabilities` of the CURRENTLY (or most recently) connected
261
- * server — read fresh at call time (mirrors `getCursor`/`getToken`'s own
267
+ * M4 (additive-minor, `task.approval_resolved`): the capabilities advertised
268
+ * by the CURRENT transport's server (`conn.ack` on WS, the latest successful
269
+ * events response on long-poll) — read fresh at call time (mirrors
270
+ * `getCursor`/`getToken`'s own
262
271
  * "read fresh, not captured once" convention elsewhere in this codebase),
263
272
  * since the capability is learned asynchronously, after this `TaskRunner`
264
273
  * is already constructed (`create-daemon.ts`'s `start()` builds `deps`
@@ -319,8 +328,8 @@ export type AdmissionGuardDecision = {
319
328
  };
320
329
  type AcceptedOfferPayload = TaskOfferPayload | TaskOfferWithToolsetsPayload;
321
330
  /**
322
- * Per-connection task orchestration: offer -> (decline | claim -> adapter
323
- * session -> started) -> seq-ordered progress batches -> complete/fail/
331
+ * Per-connection task orchestration: offer -> (decline | prepare -> seal ->
332
+ * claim -> prepared operation -> started) -> seq-ordered progress batches -> complete/fail/
324
333
  * cancelled, plus approve/reject/cancel/steer handling.
325
334
  *
326
335
  * M1 rework (docs/protocol.md §3, §5, §10 — `packages/protocol` is frozen,
@@ -340,13 +349,13 @@ export declare class TaskRunner {
340
349
  * Finding F4 (cancel lost during the offer-processing window): a
341
350
  * `task.cancel` for a taskId that hasn't finished `handleOffer` yet (still
342
351
  * awaiting adapter detection / instruction resolution / workspace setup /
343
- * `adapter.start()`) has no `this.tasks` entry to land on — it used to be
352
+ * prepared operation `start()`) has no `this.tasks` entry to land on — it used to be
344
353
  * silently dropped, and the runtime session `handleOffer` was about to
345
354
  * register would then run an unsupervised ("zombie") turn nobody asked
346
355
  * for anymore. Recording the taskId here lets `handleOffer` consult it at
347
356
  * the two points where it can still safely react (see its body): before
348
357
  * claiming at all (decline instead of ever starting a session), and right
349
- * after `adapter.start()` resolves but before this task is registered as
358
+ * after the prepared operation resolves but before this task is registered as
350
359
  * active (tear the just-started session down immediately, before its
351
360
  * event loop ever pumps a single event). Consumed (deleted) at whichever
352
361
  * checkpoint handles it; a cancel for a taskId that's already active,
@@ -372,12 +381,12 @@ export declare class TaskRunner {
372
381
  * checkpoint-2 cancel-teardown, or successful registration into
373
382
  * `this.tasks`). Bounded eviction on `pendingCancelled` (below) must never
374
383
  * remove an entry for a taskId in this set: doing so is exactly the bug —
375
- * block task A in `adapter.start()`, deliver A's own `task.cancel` (so
384
+ * block task A in prepared-operation `start()`, deliver A's own `task.cancel` (so
376
385
  * `pendingCancelled` gets an entry for A while A is still in-flight),
377
386
  * then deliver `MAX_TRACKED_TASK_IDS` more cancels for unrelated taskIds
378
387
  * nobody ever offered — under naive oldest-wins eviction, A's entry (the
379
388
  * single oldest) gets evicted purely because of unrelated churn, so when
380
- * `adapter.start()` finally resolves, checkpoint 2 finds no cancel marker
389
+ * the prepared operation finally resolves, checkpoint 2 finds no cancel marker
381
390
  * and the already-cancelled task starts a real session. See
382
391
  * `evictPendingCancelled` below for the fix, and
383
392
  * `task-runner-bounded-collections.test.ts` for a test mirroring this
@@ -397,7 +406,7 @@ export declare class TaskRunner {
397
406
  * explicitly relies on redelivered handlers being idempotent for exactly
398
407
  * this reason). `handleOffer` must treat a redelivered offer for a taskId
399
408
  * that's already active (`this.tasks`) or already finished (this set) as
400
- * a no-op — never a second `adapter.start()` call, which would orphan the
409
+ * a no-op — never a second prepared-operation `start()` call, which would orphan the
401
410
  * first session.
402
411
  *
403
412
  * M3-B: unbounded otherwise — a long-lived daemon that's finished many
@@ -455,10 +464,9 @@ export declare class TaskRunner {
455
464
  /** M4 Phase 2: stop claiming any FUTURE `task.offer` — see `stoppingOffers`'s own doc comment. Idempotent. */
456
465
  stopAcceptingOffers(): void;
457
466
  /**
458
- * M4 Phase 2: best-effort shutdown of every currently ACTIVE task, for the
459
- * control socket's `shutdown` RPC. Mirrors `handleCancel`'s best-effort
460
- * `session.interrupt()` style (an interrupt failure is swallowed; the
461
- * terminal message is sent either way) but reports `task.fail` rather than
467
+ * Shutdown of every currently ACTIVE task for the control socket's
468
+ * `shutdown` RPC. Soft interrupt remains bounded, but each task's
469
+ * authoritative close receipt must settle successfully. Reports `task.fail` rather than
462
470
  * `task.cancelled` — these tasks aren't ending because the SERVER
463
471
  * cancelled them, they're ending because this device is shutting down.
464
472
  * `retryable: true` throughout: nothing about the task/policy itself was
@@ -509,28 +517,13 @@ export declare class TaskRunner {
509
517
  * unconditionally, so a hung `interrupt()` (a misbehaving adapter) can
510
518
  * never block `task.fail` from being sent at all.
511
519
  *
512
- * New in this batch hard-kill escalation: when `interrupt()` does NOT
513
- * settle within that same grace window, `session.close()` is tried next
514
- * (ALSO raced against `timeoutMs`, for the identical reason: a hung
515
- * `close()` must not be able to block this forever either — which matters
516
- * far more here than it used to for the pre-existing graceful-shutdown-only
517
- * caller, since THAT path is additionally bounded by an outer deadline
518
- * (`SHUTDOWN_TASK_TEARDOWN_DEADLINE_MS`/`DaemonConfig.shutdownGraceMs`,
519
- * `create-daemon.ts`), while resource-limit enforcement fires during
520
- * ordinary operation with no such outer bound watching it). `close()` is
521
- * every adapter's harder teardown primitive — an actual process-level kill
522
- * (SIGTERM, or `taskkill /F` on Windows — see e.g.
523
- * `ClaudeProcessClient.kill()`/`PiRpcClient.kill()`) as opposed to pi's own
524
- * soft in-band `interrupt()` (an RPC `abort` message that leaves the
525
- * process alive and resumable) — so escalating to it is the closest thing
526
- * to a "hard kill" the `Session` interface exposes. `finish()` below calls
527
- * `session.close()` again regardless (documented idempotent) — this isn't
528
- * a substitute for that, only an earlier, bounded attempt at actually
529
- * stopping a stuck runtime before this method gives up and reports failure
530
- * anyway.
520
+ * After the bounded soft interrupt, `finish()` always awaits the authoritative
521
+ * `Session.close()` receipt. A failed receipt retains active/Git ownership;
522
+ * shutdown surfaces the rejection while resource enforcement leaves local
523
+ * evidence for a later retry.
531
524
  *
532
525
  * Re-checks task identity (`this.tasks.get(...) === active`) immediately
533
- * before sending `task.fail`: the interrupt/hard-kill race above has await
526
+ * before sending `task.fail`: the interrupt race above has await
534
527
  * points during which a DIFFERENT path (a racing `task.cancel`/
535
528
  * `task.reject`, or the session completing normally on its own) may have
536
529
  * already finished this exact task and sent its own terminal message.
@@ -623,7 +616,7 @@ export declare class TaskRunner {
623
616
  *
624
617
  * `inFlightOffers` is naturally tiny (bounded by this device's real
625
618
  * concurrent-offer-processing count — normally single digits, driven by
626
- * how many `task.offer`s are simultaneously mid-`adapter.start()` — nowhere
619
+ * how many `task.offer`s are simultaneously mid-prepared-operation start() — nowhere
627
620
  * near `MAX_TRACKED_TASK_IDS`), so this scan is cheap in practice: it
628
621
  * finds a safe entry at or near the front almost always. The only case
629
622
  * where NO entry is safe to evict is every single tracked cancel
@@ -943,15 +936,16 @@ export declare class TaskRunner {
943
936
  /**
944
937
  * Whether the CURRENTLY connected server advertised `result-document` —
945
938
  * read fresh on every call, never captured, because the answer changes
946
- * across a reconnect (`ConnectionManager.getServerCapabilities` returns
947
- * `[]` from the moment an acked connection closes until a fresh
948
- * `conn.ack` repopulates it). An absent `getServerCapabilities` seam is
939
+ * across a reconnect or transport switch (`ConnectionManager` clears the
940
+ * old advertisement at the boundary, then repopulates it from a fresh WS
941
+ * ack or successful poll response). An absent `getServerCapabilities` seam is
949
942
  * "no capabilities", the fail-closed reading.
950
943
  */
951
944
  private hasResultDocumentCapability;
952
945
  private observeGit;
953
946
  private updateGitPhaseBestEffort;
954
947
  private finish;
948
+ private reserveSemanticTerminal;
955
949
  /** 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). */
956
950
  private addFinishedTaskId;
957
951
  /** `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. */
@@ -1,4 +1,4 @@
1
- import { type CapabilityFlag, type Envelope, type RuntimeInfo } from '@byok-sdk/protocol';
1
+ import { type CapabilityFlag, type Envelope, type RuntimeInfo, type ToolsetId } from '@byok-sdk/protocol';
2
2
  export type ConnectionState = 'connecting' | 'open' | 'closed' | 'degraded' | 'revoked';
3
3
  /** The WS upgrade itself was rejected with a non-101 HTTP status (e.g. 401 for an expired/invalid bearer token). Surfaced via `onConnectOutcome` so `ConnectionManager` can force a reactive token renewal before the next attempt (protocol §6.2, "reactively on 401"). */
4
4
  export declare class WsUnexpectedStatusError extends Error {
@@ -25,6 +25,8 @@ export interface WsTransportOptions {
25
25
  capabilities: CapabilityFlag[];
26
26
  /** Detected runtimes, sent on every `conn.hello` (protocol §10 gap #4/#11). */
27
27
  runtimes?: RuntimeInfo[];
28
+ /** Sorted logical IDs configured locally; no MCP executable definition crosses the wire. */
29
+ configuredToolsets?: readonly ToolsetId[];
28
30
  /** The redelivery cursor to send as `conn.hello.cursor` (protocol §9) — read fresh on every connect so a value learned mid-connection is used on the next reconnect. */
29
31
  getCursor?: () => number | undefined;
30
32
  onEnvelope: (envelope: Envelope) => void;
package/dist/index.d.ts CHANGED
@@ -1,6 +1,8 @@
1
- export type { RuntimeAdapter, RuntimeCapabilities, RuntimeDetectResult, Session, TaskContext, GitWorkspaceConfig, McpStdioServerConfig, McpToolsetConfig, } from './types';
2
- export { PolicyUnsupportedError, SteerUnsupportedError } from './types';
1
+ export type { RuntimeAdapter, RuntimeAdapterDescriptor, RuntimeAdapterPrepareInput, RuntimeAdapterPrepareResult, RuntimeAdapterRejectedOperation, RuntimeAdapterPreparedOperation, PreparedRuntimeOperation, RuntimeOperationManifest, RuntimeOperationStartInput, RuntimeCapabilities, RuntimeDetectResult, Session, GitWorkspaceConfig, McpStdioServerConfig, McpToolsetConfig, } from './types';
2
+ export { PolicyUnsupportedError, SteerUnsupportedError, freezeRuntimeAdapterDescriptor, sealRuntimeOperationManifest } from './types';
3
3
  export type { RuntimeEnvironmentRequirements } from './daemon/environment';
4
+ export { RuntimeExecutionFailure, RuntimeDisposalFailure, RUNTIME_ADAPTER_CONTRACT_VIOLATION_REASON, isRuntimeDisposalFailure, isRuntimeExecutionFailure, projectRuntimeBoundaryFailure, projectRuntimeExecutionFailure, } from './runtime-failure';
5
+ export type { RuntimeExecutionFailureInput, RuntimeDisposalFailureInput, RuntimeDisposalStage, RuntimeFailureCategory, RuntimeFailurePhase, RuntimeFailureProjection, RuntimeRetryDisposition, } from './runtime-failure';
4
6
  export { GitWorkspaceManager, GitWorkspaceError, isGitWorkspaceConfig, prependGitWorkspaceGuidance } from './daemon/git-workspace';
5
7
  export type { GitWorkspaceObservation, GitWorkspaceLease, GitWorkspaceOptions, GitErrorCategory } from './daemon/git-workspace';
6
8
  export { GitWorkspaceStore } from './daemon/git-workspace-store';