@byok-sdk/client 0.10.2 → 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.
Files changed (57) hide show
  1. package/README.md +86 -6
  2. package/dist/adapters/claude/claude-adapter.d.ts +3 -2
  3. package/dist/adapters/claude/permission-mapping.d.ts +48 -1
  4. package/dist/adapters/claude/resolve-approval-mcp-bin.d.ts +3 -2
  5. package/dist/adapters/codex/permission-mapping.d.ts +16 -0
  6. package/dist/adapters/index.js +292 -23
  7. package/dist/adapters/index.js.map +1 -1
  8. package/dist/adapters/mcp-tool-grants.d.ts +36 -0
  9. package/dist/adapters/pi/resolve-extensions.d.ts +4 -1
  10. package/dist/adapters/pi/subagents-policy-config.d.ts +7 -0
  11. package/dist/adapters/pi/subagents-policy-extension.js +137 -0
  12. package/dist/adapters/pi/subagents-policy-extension.js.map +1 -0
  13. package/dist/agent-home.d.ts +33 -0
  14. package/dist/agent-memory/index.d.ts +1 -1
  15. package/dist/agent-memory/index.js +12 -9
  16. package/dist/agent-memory/index.js.map +1 -1
  17. package/dist/bin/agent-memory-mcp-server.d.ts +2 -2
  18. package/dist/bin/agent-message-mcp-server.d.ts +0 -1
  19. package/dist/bin/byok-agent-memory-mcp.js +340 -7
  20. package/dist/bin/byok-agent-memory-mcp.js.map +1 -1
  21. package/dist/bin/byok-agent-message-mcp.js +331 -7
  22. package/dist/bin/byok-agent-message-mcp.js.map +1 -1
  23. package/dist/bin/byok-agent-team-mcp.d.ts +2 -0
  24. package/dist/bin/byok-agent-team-mcp.js +765 -0
  25. package/dist/bin/byok-agent-team-mcp.js.map +1 -0
  26. package/dist/bin/byok-agent.js +16413 -14479
  27. package/dist/bin/byok-agent.js.map +1 -1
  28. package/dist/bin/byok-approval-mcp.js +312 -45
  29. package/dist/bin/byok-approval-mcp.js.map +1 -1
  30. package/dist/bin/commands/team.d.ts +15 -0
  31. package/dist/bin/sdk-reserved-helper-runners.d.ts +2 -0
  32. package/dist/bin/team-mcp-server.d.ts +27 -0
  33. package/dist/bin/team-tmux-view.d.ts +19 -0
  34. package/dist/daemon/agent-egress-controller.d.ts +4 -0
  35. package/dist/daemon/agent-message-mcp-preflight.d.ts +17 -0
  36. package/dist/daemon/auth-manager.d.ts +20 -0
  37. package/dist/daemon/blob-client.d.ts +24 -6
  38. package/dist/daemon/connection-manager.d.ts +7 -0
  39. package/dist/daemon/control-protocol.d.ts +38 -0
  40. package/dist/daemon/create-daemon.d.ts +9 -0
  41. package/dist/daemon/device-credential-store.d.ts +16 -0
  42. package/dist/daemon/long-poll-transport.d.ts +3 -0
  43. package/dist/daemon/mcp-tools-probe.d.ts +105 -0
  44. package/dist/daemon/replay-cursor.d.ts +9 -0
  45. package/dist/daemon/resolve-agent-memory-mcp-bin.d.ts +2 -1
  46. package/dist/daemon/resolve-agent-message-mcp-bin.d.ts +2 -1
  47. package/dist/daemon/task-runner.d.ts +22 -1
  48. package/dist/daemon/team-workspace.d.ts +202 -0
  49. package/dist/daemon/toolset-registry.d.ts +0 -2
  50. package/dist/daemon/url.d.ts +7 -1
  51. package/dist/index.d.ts +8 -3
  52. package/dist/index.js +3611 -1364
  53. package/dist/index.js.map +1 -1
  54. package/dist/sdk-reserved-helper-host.d.ts +25 -0
  55. package/dist/sdk-reserved-mcp.d.ts +23 -0
  56. package/dist/types.d.ts +36 -0
  57. package/package.json +10 -5
@@ -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;
@@ -422,3 +422,41 @@ export interface ShutdownParams {
422
422
  reason?: ShutdownReason;
423
423
  }
424
424
  export declare function parseShutdownParams(value: unknown): ShutdownParams;
425
+ export interface TeamWorkspaceCreateParams {
426
+ workspaceId: string;
427
+ members: string[];
428
+ limits: {
429
+ maxMembers: number;
430
+ maxMessages: number;
431
+ maxBytes: number;
432
+ };
433
+ }
434
+ export interface TeamWorkspaceJoinParams {
435
+ workspaceId: string;
436
+ memberId: string;
437
+ ttlMs?: number;
438
+ }
439
+ export interface TeamContextParams {
440
+ context: string;
441
+ }
442
+ export interface TeamMessagePostParams extends TeamContextParams {
443
+ body: string;
444
+ contentType?: string;
445
+ }
446
+ export interface TeamMessageReadParams extends TeamContextParams {
447
+ afterSeq?: number;
448
+ }
449
+ export interface TeamMessageAckParams extends TeamContextParams {
450
+ throughSeq: number;
451
+ }
452
+ export interface TeamMessageInspectParams {
453
+ workspaceId: string;
454
+ afterSeq?: number;
455
+ }
456
+ export declare function parseTeamWorkspaceCreateParams(value: unknown): TeamWorkspaceCreateParams | undefined;
457
+ export declare function parseTeamWorkspaceJoinParams(value: unknown): TeamWorkspaceJoinParams | undefined;
458
+ export declare function parseTeamContextParams(value: unknown): TeamContextParams | undefined;
459
+ export declare function parseTeamMessagePostParams(value: unknown): TeamMessagePostParams | undefined;
460
+ export declare function parseTeamMessageReadParams(value: unknown): TeamMessageReadParams | undefined;
461
+ export declare function parseTeamMessageAckParams(value: unknown): TeamMessageAckParams | undefined;
462
+ export declare function parseTeamMessageInspectParams(value: unknown): TeamMessageInspectParams | undefined;
@@ -18,6 +18,7 @@ import { type ProgressBatcherOptions } from './progress-batcher';
18
18
  import { type AgentEgressReliableAppendResult } from './agent-egress-controller';
19
19
  import { type AgentEgressStatus } from './agent-egress-policy';
20
20
  import { type AgentEgressSanitizer } from './agent-egress-sanitizer';
21
+ import { type SdkHelperHostConfig } from '../sdk-reserved-helper-host';
21
22
  import { type AgentMemoryHostedProjection } from './agent-memory';
22
23
  import type { AgentMemoryFilesystemHelperConfig } from './agent-memory-filesystem';
23
24
  import { type AgentContentReadRoot } from './agent-content-read';
@@ -90,6 +91,8 @@ export interface DaemonConfig {
90
91
  productName: string;
91
92
  productId: string;
92
93
  serverUrl: string;
94
+ /** Bounds one AuthManager pair/challenge/token exchange, including response-body reads. */
95
+ authRequestDeadlineMs?: number;
93
96
  deviceName?: string;
94
97
  /**
95
98
  * Optional override for the client-hashed physical machine identity sent
@@ -118,6 +121,12 @@ export interface DaemonConfig {
118
121
  * 2 on macOS. Windows remains fail-closed pending its native race proof.
119
122
  */
120
123
  agentMemoryFilesystem?: AgentMemoryFilesystemHelperConfig;
124
+ /**
125
+ * Explicit composition for SDK-reserved MCP helpers when this daemon is
126
+ * embedded in a single-file/SEA product executable. The product entrypoint
127
+ * must also call `runSdkReservedHelperCommand()` before its own CLI parser.
128
+ */
129
+ sdkHelperHost?: SdkHelperHostConfig;
121
130
  /**
122
131
  * Refuse legacy task offers locally. This is an additive capability only
123
132
  * after the SDK-owned Agent home has passed construction-time preflight.
@@ -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,105 @@
1
+ import type { McpStdioServerConfig } from '../types';
2
+ export declare const MCP_TOOLS_PROBE_TIMEOUT_MS = 10000;
3
+ /**
4
+ * Hard cap on the bytes one probed server may write to stdout before its
5
+ * `tools/list` answer is complete. The probe reads a fixed two-message
6
+ * handshake, so a server still streaming past this is either broken or
7
+ * hostile; either way it must not be able to grow the daemon's heap while an
8
+ * offer waits on admission. Exceeding it is a probe failure, never a partial
9
+ * observation.
10
+ */
11
+ export declare const MCP_TOOLS_PROBE_MAX_STDOUT_BYTES = 1048576;
12
+ /**
13
+ * The single admission budget for observing ALL of one task's projected
14
+ * toolset servers, however many there are.
15
+ *
16
+ * `handleOffer` runs inside its connection's FIFO, so anything it awaits also
17
+ * delays the `task.cancel` / `task.approve` / next-offer envelopes queued
18
+ * behind it. Probing a toolset's servers one after another would multiply the
19
+ * per-server timeout by the server count — a device configured to the current
20
+ * ceiling (16 toolsets × 16 servers) could hold the control channel for
21
+ * minutes on a single unresponsive command. The runner therefore starts every
22
+ * probe at once and gives each one this same deadline, so total admission
23
+ * latency is bounded by one timeout regardless of server count, and each probe
24
+ * still kills its own child when the deadline expires.
25
+ */
26
+ export declare const MCP_TOOLSET_PROBE_ADMISSION_TIMEOUT_MS = 10000;
27
+ /**
28
+ * Tool names an adapter is allowed to pre-grant must be OBSERVED, never
29
+ * configured: the daemon's own `mcpToolsets` config carries `command`/`args`
30
+ * only (see `toolset-registry.ts`), so the single authority for "which tools
31
+ * does this server actually expose" is the server's own `tools/list` answer.
32
+ *
33
+ * A name that survives this filter is about to be interpolated into runtime
34
+ * CLI authority — `--allowedTools mcp__<server>__<tool>` for claude, and
35
+ * `mcp_servers.<server>.tools.<tool>.approval_mode` for codex. A comma, a
36
+ * dot, a quote, or whitespace in a tool name would forge additional grants or
37
+ * a different config key out of one legitimate one.
38
+ *
39
+ * A server that reports ANY name outside this shape fails the whole probe —
40
+ * the observation is rejected, and the task is declined permanently rather
41
+ * than partially granted. Granting the well-formed subset and silently
42
+ * dropping the rest would hand the model a toolset it can only half call, and
43
+ * would let one bad name ride along with good ones; only observed, validated
44
+ * names are ever granted, and a list that cannot be validated in full yields
45
+ * no grant at all. The shape is deliberately narrower than MCP's own
46
+ * (unbounded) name rule: the two real servers this SDK ships and every toolset
47
+ * server observed so far satisfy it, and a legitimate server that does not can
48
+ * still be listed and called by a runtime that grants tools itself — it simply
49
+ * cannot be pre-granted here, and this SDK will not admit a task for it.
50
+ */
51
+ export declare const GRANTABLE_TOOL_NAME: RegExp;
52
+ /**
53
+ * The same rule for the SERVER half of the identifier, enforced at grant
54
+ * resolution (`../adapters/mcp-tool-grants.ts`). A projected server name is
55
+ * interpolated into `mcp__<server>__<tool>` for claude and into the flat TOML
56
+ * key `mcp_servers.<server>.tools.<tool>.approval_mode` for codex: a `.` would
57
+ * split that key into a different table, and a quote, comma, or space would
58
+ * forge a second grant out of one. `toolset-registry.ts` already validates
59
+ * configured server names, so this is the second, local gate that keeps the
60
+ * grant surface honest for a server that reached an adapter some other way.
61
+ */
62
+ export declare const GRANTABLE_MCP_SERVER_NAME: RegExp;
63
+ /**
64
+ * A probe failure caused by the server's own ANSWER rather than by its
65
+ * environment — an ungrantable tool name, a malformed tool entry, or an
66
+ * oversized stream. Retrying cannot change it: the same configured command
67
+ * reports the same names next time. Callers use this to decline the task
68
+ * permanently instead of re-offering it forever (see `task-runner.ts`).
69
+ */
70
+ export declare class McpToolsProbeAuthorityError extends Error {
71
+ constructor(message: string);
72
+ }
73
+ export interface McpToolsProbeOptions {
74
+ /** Prefix used in every error message, so a failure names the thing that failed. */
75
+ label?: string;
76
+ timeoutMs?: number;
77
+ /**
78
+ * The exact base environment the RUNTIME child of this task receives
79
+ * (`buildRuntimeEnv`, `./environment.ts`) — never `process.env`. The probe
80
+ * spawns a host-configured command, so it must not become the one place the
81
+ * daemon's own ambient credentials (an `AWS_SECRET_ACCESS_KEY` or
82
+ * `DATABASE_URL` set for the daemon's own deployment, this SDK's own
83
+ * `BYOK_*` control-plane variables) reach a server the real runtime path
84
+ * would have filtered out. Required, deliberately: a caller that forgets it
85
+ * fails to compile rather than silently reinstating the blanket passthrough.
86
+ */
87
+ env: Readonly<Record<string, string>>;
88
+ /**
89
+ * Working directory for the probed child — the same directory the runtime
90
+ * CLI is spawned in, so a server resolving relative paths sees what it will
91
+ * see for real. Omitted only when no such directory is resolved before
92
+ * admission.
93
+ */
94
+ cwd?: string;
95
+ }
96
+ /**
97
+ * Start the exact configured stdio MCP server, complete an
98
+ * `initialize` + `tools/list` handshake, and return the reported tool names.
99
+ * No `tools/call` is ever sent, so an authenticated task binding stays unused
100
+ * until the real runtime invokes it.
101
+ *
102
+ * The child is always killed before this resolves — the probe proves the
103
+ * server can start and enumerate its tools; the runtime spawns its own copy.
104
+ */
105
+ export declare function probeMcpServerTools(server: Readonly<McpStdioServerConfig>, options: McpToolsProbeOptions): Promise<readonly string[]>;
@@ -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
+ }
@@ -1,6 +1,7 @@
1
+ import { type SdkHelperHostConfig } from '../sdk-reserved-helper-host';
1
2
  export interface ResolvedAgentMemoryMcpBin {
2
3
  readonly command: string;
3
4
  readonly args: readonly string[];
4
5
  }
5
6
  /** Resolve the SDK-owned stdio Agent-memory MCP helper shipped beside the client bundle. */
6
- export declare function resolveAgentMemoryMcpBin(externalHelperConfigured?: boolean): ResolvedAgentMemoryMcpBin | undefined;
7
+ export declare function resolveAgentMemoryMcpBin(externalHelperConfigured?: boolean, host?: SdkHelperHostConfig): ResolvedAgentMemoryMcpBin | undefined;
@@ -1,6 +1,7 @@
1
+ import { type SdkHelperHostConfig } from '../sdk-reserved-helper-host';
1
2
  export interface ResolvedAgentMessageMcpBin {
2
3
  readonly command: string;
3
4
  readonly args: readonly string[];
4
5
  }
5
6
  /** Resolve the SDK-owned stdio MCP helper shipped beside the client bundle. */
6
- export declare function resolveAgentMessageMcpBin(): ResolvedAgentMessageMcpBin;
7
+ export declare function resolveAgentMessageMcpBin(host?: SdkHelperHostConfig): ResolvedAgentMessageMcpBin;
@@ -1,5 +1,5 @@
1
1
  import { type AgentMessageContentType, type AgentEgressPolicy, type Envelope, type PermissionPolicy, type RuntimeId, type TerminalProjectionSelection, type TaskOfferPayload, type TaskOfferForAgentPayload, type TaskOfferForAgentWithEgressPayload, type TaskOfferForAgentWithEgressFreshPayload, type TaskOfferWithToolsetsPayload } from '@byok-sdk/protocol';
2
- import { type McpToolsetConfig, type RuntimeAdapter } from '../types';
2
+ import { type McpStdioServerConfig, type McpToolsetConfig, type RuntimeAdapter } from '../types';
3
3
  import { AgentHomeManager, type AgentRef } from '../agent-home';
4
4
  import { AgentSessionHandoffStore, type AgentTerminalCause } from './agent-session-handoff-store';
5
5
  import { type RuntimeDisposalStage } from '../runtime-failure';
@@ -12,6 +12,7 @@ import type { SessionWorkspaceStore } from './session-workspace-store';
12
12
  import type { GitWorkspaceManager, GitWorkspaceObservation } from './git-workspace';
13
13
  import type { GitWorkspaceStore, GitWorkspacePhase } from './git-workspace-store';
14
14
  import type { AgentEgressController } from './agent-egress-controller';
15
+ import { type McpToolsProbeOptions } from './mcp-tools-probe';
15
16
  import type { ResolvedAgentMessageMcpBin } from './resolve-agent-message-mcp-bin';
16
17
  import type { ResolvedAgentMemoryMcpBin } from './resolve-agent-memory-mcp-bin';
17
18
  import { type AgentMemoryAuditWarning, type AgentMemoryHostedProjection } from './agent-memory';
@@ -369,6 +370,22 @@ export interface TaskRunnerDeps {
369
370
  };
370
371
  /** SDK-owned, task-scoped MCP helper. Required only for offers declaring messageEgress. */
371
372
  agentMessageMcpBin?: Readonly<ResolvedAgentMessageMcpBin>;
373
+ /**
374
+ * Production pre-runtime executability/handshake gate for the exact message
375
+ * helper config. `env` is the same allowlisted child environment the runtime
376
+ * gets (`buildRuntimeEnv`), and `cwd` the same working directory, so the
377
+ * helper is proved under the conditions it will actually run in.
378
+ */
379
+ agentMessageMcpPreflight?: (server: Readonly<McpStdioServerConfig>, env: Readonly<Record<string, string>>, cwd?: string) => Promise<void>;
380
+ /**
381
+ * Override the `tools/list` observation of a projected toolset MCP server.
382
+ * Defaults to the real handshake (`mcp-tools-probe.ts`); tests substitute a
383
+ * stub. It is deliberately NOT optional-with-no-default the way
384
+ * `agentMessageMcpPreflight` is: an adapter may only grant tool names that
385
+ * were observed, so a runner with no observation at all would silently
386
+ * project toolsets the model can list and never call.
387
+ */
388
+ mcpToolsetToolsProbe?: (server: Readonly<McpStdioServerConfig>, options: McpToolsProbeOptions) => Promise<readonly string[]>;
372
389
  /** SDK-owned MCP helper injected only into strict Agent tasks. */
373
390
  agentMemoryMcpBin?: Readonly<ResolvedAgentMemoryMcpBin>;
374
391
  /** Explicit external secure-fs helper. No PATH discovery or bundled native addon exists. */
@@ -404,6 +421,7 @@ export declare class TaskRunner {
404
421
  private readonly deps;
405
422
  private readonly tasks;
406
423
  private readonly pendingMessageTasks;
424
+ private readonly messageOutboxesByHome;
407
425
  private readonly messageContextByToken;
408
426
  private readonly messageContextByTask;
409
427
  private readonly memoryContextByToken;
@@ -464,6 +482,8 @@ export declare class TaskRunner {
464
482
  * costs nothing.
465
483
  */
466
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;
467
487
  /**
468
488
  * Finding P2 (Fix 2c): taskIds that have reached a terminal outcome
469
489
  * (Complete/Failed/Cancelled) this session — populated in `finish()`.
@@ -575,6 +595,7 @@ export declare class TaskRunner {
575
595
  }>;
576
596
  /** Restore activated, unaccepted message drafts before transport admission on daemon restart. */
577
597
  recoverAgentMessageOutboxes(agentsRoot: string): Promise<void>;
598
+ private agentMessageOutbox;
578
599
  /** Retry stable recovered records after a transport handshake/re-handshake. */
579
600
  retryRecoveredAgentMessages(): void;
580
601
  private sendAgentMessageRecord;
@@ -0,0 +1,202 @@
1
+ /**
2
+ * The local team channel is deliberately a small, versioned contract. It is
3
+ * not a task outbox and it has no cloud or runtime authority. The state file
4
+ * is an atomic envelope so a post, a receipt, or a membership change is
5
+ * either wholly visible after a restart or not visible at all.
6
+ */
7
+ export declare const TEAM_WORKSPACE_VERSION: 1;
8
+ export declare const TEAM_WORKSPACE_DIRECTORY: string;
9
+ export declare const TEAM_WORKSPACE_STATE_FILENAME = "state.json";
10
+ export declare const TEAM_WORKSPACE_DEFAULT_CONTENT_TYPE = "text/plain";
11
+ /** Bounds are intentionally smaller than the control protocol's 64 KiB line. */
12
+ export declare const TEAM_WORKSPACE_MAX_ID_BYTES = 128;
13
+ export declare const TEAM_WORKSPACE_MAX_BODY_BYTES: number;
14
+ export declare const TEAM_WORKSPACE_MAX_CONTENT_TYPE_BYTES = 128;
15
+ export declare const TEAM_WORKSPACE_MAX_MEMBERS = 256;
16
+ export declare const TEAM_WORKSPACE_MAX_MESSAGES = 100000;
17
+ export declare const TEAM_WORKSPACE_MAX_BYTES: number;
18
+ export declare const TEAM_WORKSPACE_DEFAULT_LEASE_TTL_MS: number;
19
+ export declare const TEAM_WORKSPACE_MIN_LEASE_TTL_MS = 1;
20
+ export declare const TEAM_WORKSPACE_MAX_LEASE_TTL_MS: number;
21
+ /** A stable, content-addressed registry revision. */
22
+ export type TeamWorkspaceRevision = `sha256:${string}`;
23
+ export interface TeamWorkspaceLimits {
24
+ readonly maxMembers: number;
25
+ readonly maxMessages: number;
26
+ readonly maxBytes: number;
27
+ }
28
+ export interface TeamWorkspaceDefinition {
29
+ readonly version: typeof TEAM_WORKSPACE_VERSION;
30
+ readonly workspaceId: string;
31
+ readonly revision: TeamWorkspaceRevision;
32
+ readonly members: readonly string[];
33
+ readonly limits: TeamWorkspaceLimits;
34
+ readonly createdAt: string;
35
+ readonly updatedAt: string;
36
+ }
37
+ export interface TeamWorkspaceMemberReceipt {
38
+ readonly workspaceId: string;
39
+ readonly memberId: string;
40
+ readonly acknowledgedThroughSeq: number;
41
+ readonly deliveredThroughSeq: number;
42
+ readonly registryRevision: TeamWorkspaceRevision;
43
+ readonly updatedAt: string;
44
+ }
45
+ export interface TeamMessage {
46
+ readonly version: typeof TEAM_WORKSPACE_VERSION;
47
+ readonly workspaceId: string;
48
+ readonly seq: number;
49
+ readonly messageId: string;
50
+ readonly senderMemberId: string;
51
+ readonly body: string;
52
+ readonly contentType: string;
53
+ readonly byteCount: number;
54
+ readonly contentHash: TeamWorkspaceRevision;
55
+ readonly createdAt: string;
56
+ }
57
+ /**
58
+ * The token is intentionally opaque to callers. It is returned once from
59
+ * lease issuance and only its digest is persisted; raw bearer material never
60
+ * enters the durable state file.
61
+ */
62
+ export interface TeamMemberLease {
63
+ readonly opaqueToken: string;
64
+ readonly workspaceId: string;
65
+ readonly memberId: string;
66
+ readonly registryRevision: TeamWorkspaceRevision;
67
+ readonly expiresAt: string;
68
+ }
69
+ export interface CreateTeamWorkspaceInput {
70
+ readonly workspaceId: string;
71
+ readonly members: readonly string[];
72
+ readonly limits: TeamWorkspaceLimits;
73
+ }
74
+ export interface UpdateTeamWorkspaceInput {
75
+ readonly workspaceId: string;
76
+ readonly expectedRevision: TeamWorkspaceRevision;
77
+ readonly members: readonly string[];
78
+ readonly limits?: TeamWorkspaceLimits;
79
+ }
80
+ export interface CreateTeamMemberLeaseInput {
81
+ readonly workspaceId: string;
82
+ readonly memberId: string;
83
+ readonly ttlMs?: number;
84
+ }
85
+ export interface TeamPostMessageInput {
86
+ readonly lease: TeamMemberLease;
87
+ readonly body: string;
88
+ readonly contentType?: string;
89
+ }
90
+ export interface TeamReadMessagesInput {
91
+ readonly lease: TeamMemberLease;
92
+ readonly afterSeq?: number;
93
+ }
94
+ export interface TeamAckMessagesInput {
95
+ readonly lease: TeamMemberLease;
96
+ readonly throughSeq: number;
97
+ }
98
+ export interface TeamMessageAcceptedReceipt {
99
+ readonly accepted: true;
100
+ readonly durable: true;
101
+ readonly workspaceId: string;
102
+ readonly memberId: string;
103
+ readonly seq: number;
104
+ readonly messageId: string;
105
+ readonly message: TeamMessage;
106
+ }
107
+ export interface TeamReadMessagesResult {
108
+ readonly workspaceId: string;
109
+ readonly memberId: string;
110
+ readonly messages: readonly TeamMessage[];
111
+ readonly afterSeq: number;
112
+ readonly deliveredThroughSeq: number;
113
+ readonly receipt: TeamWorkspaceMemberReceipt;
114
+ }
115
+ export interface TeamAckReceipt {
116
+ readonly accepted: true;
117
+ readonly durable: true;
118
+ readonly throughSeq: number;
119
+ readonly receipt: TeamWorkspaceMemberReceipt;
120
+ }
121
+ export interface LocalTeamWorkspaceOptions {
122
+ /** Test seam for deterministic lease expiry and timestamps. */
123
+ readonly now?: () => number;
124
+ }
125
+ /** Common typed failure for all rejected local team operations. */
126
+ export declare class TeamWorkspaceError extends Error {
127
+ constructor(message: string);
128
+ }
129
+ export declare class TeamWorkspaceValidationError extends TeamWorkspaceError {
130
+ constructor(message: string);
131
+ }
132
+ export declare class TeamWorkspaceNotFoundError extends TeamWorkspaceError {
133
+ readonly workspaceId: string;
134
+ constructor(workspaceId: string);
135
+ }
136
+ export declare class TeamWorkspaceConflictError extends TeamWorkspaceError {
137
+ constructor(message: string);
138
+ }
139
+ export declare class TeamWorkspaceQuotaError extends TeamWorkspaceError {
140
+ readonly quota: 'members' | 'messages' | 'bytes';
141
+ constructor(quota: 'members' | 'messages' | 'bytes', message: string);
142
+ }
143
+ export declare class TeamWorkspaceLeaseError extends TeamWorkspaceError {
144
+ constructor(message: string);
145
+ }
146
+ export declare class TeamWorkspaceReceiptError extends TeamWorkspaceError {
147
+ constructor(message: string);
148
+ }
149
+ export declare class TeamWorkspaceCorruptError extends TeamWorkspaceError {
150
+ constructor(message: string);
151
+ }
152
+ /** Public fail-closed validators for control/MCP composition sites. */
153
+ export declare function validateTeamWorkspaceId(value: unknown): string;
154
+ export declare function validateTeamMemberId(value: unknown): string;
155
+ export declare function validateTeamMessageBody(value: unknown): string;
156
+ export declare function validateTeamContentType(value: unknown): string;
157
+ /** Encode the full lease as one opaque helper context; it is never model input. */
158
+ export declare function encodeTeamMemberContext(lease: TeamMemberLease): string;
159
+ /** Decode only the exact bounded lease shape emitted by {@link encodeTeamMemberContext}. */
160
+ export declare function decodeTeamMemberContext(value: unknown): TeamMemberLease;
161
+ /**
162
+ * Local-only durable TeamWorkspace authority. The service is intentionally
163
+ * independent from TaskRunner and cloud protocol: a caller must first obtain
164
+ * a member lease, and all message operations derive workspace/member identity
165
+ * from that lease rather than accepting it from model input.
166
+ */
167
+ export declare class LocalTeamWorkspace {
168
+ private readonly rootDir;
169
+ private readonly statePath;
170
+ private readonly now;
171
+ private readonly queueKey;
172
+ constructor(storeDir: string, options?: LocalTeamWorkspaceOptions);
173
+ /** Create the secure state directory; it is safe to call on every start. */
174
+ initialize(): Promise<void>;
175
+ createWorkspace(input: CreateTeamWorkspaceInput): Promise<TeamWorkspaceDefinition>;
176
+ getWorkspace(workspaceId: string): Promise<TeamWorkspaceDefinition | undefined>;
177
+ listWorkspaces(): Promise<readonly TeamWorkspaceDefinition[]>;
178
+ /** Local operator read for the tmux pane; it does not create or advance a member receipt. */
179
+ inspectMessages(workspaceId: string, afterSeq?: number): Promise<readonly TeamMessage[]>;
180
+ /** CAS-guarded membership/limit update. A revision change invalidates all leases. */
181
+ updateWorkspace(input: UpdateTeamWorkspaceInput): Promise<TeamWorkspaceDefinition>;
182
+ createMemberLease(input: CreateTeamMemberLeaseInput): Promise<TeamMemberLease>;
183
+ revokeMemberLease(input: {
184
+ readonly lease: TeamMemberLease;
185
+ }): Promise<void>;
186
+ /** Validate the opaque lease and return its daemon-owned identity. */
187
+ validateMemberLease(lease: TeamMemberLease): Promise<Readonly<{
188
+ workspaceId: string;
189
+ memberId: string;
190
+ registryRevision: TeamWorkspaceRevision;
191
+ expiresAt: string;
192
+ }>>;
193
+ postMessage(input: TeamPostMessageInput): Promise<TeamMessageAcceptedReceipt>;
194
+ readMessages(input: TeamReadMessagesInput): Promise<TeamReadMessagesResult>;
195
+ ackMessages(input: TeamAckMessagesInput): Promise<TeamAckReceipt>;
196
+ private resolveLease;
197
+ private load;
198
+ private save;
199
+ private enqueue;
200
+ }
201
+ /** The longer name is useful at composition sites; both names denote one authority. */
202
+ export { LocalTeamWorkspace as LocalTeamWorkspaceService };