@folsom/fuse 0.37.1 → 0.42.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.
package/dist/index.d.ts CHANGED
@@ -1,51 +1,3 @@
1
- /** A minimal fetch signature, satisfied by the global fetch and most polyfills. */
2
- type FetchLike = (input: string | URL, init?: RequestInit) => Promise<Response>;
3
- /** Per-call options accepted by every service method. */
4
- interface CallOptions {
5
- /** Abort the request (and, for events, the stream). */
6
- signal?: AbortSignal;
7
- }
8
- interface TransportConfig {
9
- baseUrl: string;
10
- token?: string;
11
- fetch?: FetchLike;
12
- userAgent?: string;
13
- requestId?: () => string;
14
- timeoutMs?: number;
15
- headers?: Record<string, string>;
16
- }
17
- type Query = Record<string, string | undefined>;
18
- interface RequestSpec {
19
- query?: Query;
20
- body?: unknown;
21
- signal?: AbortSignal;
22
- }
23
- declare class Transport {
24
- private readonly baseUrl;
25
- private readonly token?;
26
- private readonly fetchImpl;
27
- private readonly userAgent?;
28
- private readonly requestId?;
29
- private readonly timeoutMs?;
30
- private readonly defaultHeaders?;
31
- constructor(cfg: TransportConfig);
32
- /** Perform a request and decode a JSON response body. */
33
- json<T>(method: string, path: string, spec?: RequestSpec): Promise<T>;
34
- /** Perform a request that returns no body (e.g. 204 actions). */
35
- noContent(method: string, path: string, spec?: RequestSpec): Promise<void>;
36
- /**
37
- * Open a long-lived stream (SSE). Applies NO timeout — only the caller's
38
- * signal cancels it — and checks the status eagerly so connect-time errors
39
- * (404/401/...) surface as a FuseApiError before any iteration.
40
- */
41
- stream(path: string, query: Query | undefined, signal?: AbortSignal): Promise<Response>;
42
- private execute;
43
- private buildUrl;
44
- private buildHeaders;
45
- private decodeJson;
46
- private withTimeout;
47
- }
48
-
49
1
  /** Spec is the hardware/runtime spec for a microVM. */
50
2
  interface Spec {
51
3
  cpus?: number;
@@ -261,11 +213,16 @@ interface Health {
261
213
  interface EgressSpec {
262
214
  /** "direct" (the default) or "proxy". */
263
215
  mode?: string;
264
- /** Proxy backend, required when mode is "proxy" (today "mock"; later
265
- * "cloudflare-warp"). */
216
+ /** Proxy backend, required when mode is "proxy" ("cloudflare-warp",
217
+ * "wireguard", or "mock" for local development). */
266
218
  provider?: string;
267
219
  /** "socks5" (the default for proxy) or "http". */
268
220
  protocol?: string;
221
+ /** Name of an entry in the request's secrets holding the provider's
222
+ * credential, for a provider that takes one: "wireguard" reads a base64
223
+ * wg-quick config. The value goes to the host's egress backend only and is
224
+ * left out of the secrets the guest receives. */
225
+ secret?: string;
269
226
  }
270
227
  /**
271
228
  * EgressStatus is how the environment's outbound traffic is actually routed.
@@ -318,6 +275,9 @@ interface CreateRequest {
318
275
  /** Graphical session geometry. Omit it for an environment with no desktop,
319
276
  * in which case a desktop image keeps its baked default geometry. */
320
277
  desktop?: DesktopSpec;
278
+ /** Human-in-the-loop policy. Omit it for an environment that cannot raise
279
+ * interventions. */
280
+ hitl?: HitlSpec;
321
281
  }
322
282
  /** EnvironmentInfo is the server's view of a single microVM. */
323
283
  interface EnvironmentInfo {
@@ -386,6 +346,18 @@ interface ExecRequest {
386
346
  * default; the server clamps anything above its ceiling.
387
347
  */
388
348
  timeout_ms?: number;
349
+ /**
350
+ * Wait for a human when a hitl gate holds the exec (202), or when a
351
+ * human holds the environment (423, a takeover or a pending stall), then
352
+ * run (the default). false throws a PendingInterventionError or a
353
+ * LockedError at once instead. Not sent on the wire.
354
+ */
355
+ wait?: boolean;
356
+ /**
357
+ * Called with the pending intervention when a gate holds the exec, before
358
+ * waiting on it. It overrides the client's onHeld. Not sent on the wire.
359
+ */
360
+ onHeld?: (held: Intervention) => void;
389
361
  }
390
362
  /**
391
363
  * ExecResult is the outcome of a guest command.
@@ -493,7 +465,9 @@ interface Snapshot {
493
465
  * those bytes is intact, and nothing more: it is not a cross-build
494
466
  * identity, because two builds of the same recipe produce different rootfs
495
467
  * bytes (timestamps, inode ordering, package caches). It can never be used
496
- * as a cache key. */
468
+ * as a cache key. The live snapshot a migrate takes in its final pause on
469
+ * an xfs or btrfs host is the exception: its digest is a sha256 of its
470
+ * per-block hash table, not of the rootfs file. */
497
471
  digest?: string;
498
472
  /** Whether this artifact is the rootfs alone (`"disk"`), or the rootfs plus
499
473
  * the guest's memory and vCPU state (`"live"`). Restoring a disk snapshot
@@ -527,6 +501,14 @@ interface HostCapacity {
527
501
  arch?: string;
528
502
  gpus?: number;
529
503
  gpu_kind?: string;
504
+ /** The firecracker release the host agent reports it runs ("v1.17.0").
505
+ * Probed at registration (a declared value is ignored) and only set on
506
+ * capacity. Live migration needs both hosts on the same release. Omitted
507
+ * on qemu hosts and older agents. */
508
+ firecracker?: string;
509
+ /** The firecracker release the host's fc-agent.py pins. Differs from
510
+ * firecracker when the host has not run fc-update since the pin moved. */
511
+ firecracker_pinned?: string;
530
512
  /** Fractional GPU capacity: MIG instance count by profile name (e.g.
531
513
  * {"1g.10gb": 4}). Like gpus, it requires backend "qemu". When the host
532
514
  * reports per-instance MIG inventory (mig_instances), this map is a derived
@@ -608,7 +590,31 @@ interface APIKey {
608
590
  created_at: string;
609
591
  last_used_at?: string;
610
592
  revoked_at?: string;
593
+ /** The hitl scopes the key holds. Absent for a full-access key. */
594
+ scopes?: string[];
595
+ /** The environment a scoped key is limited to (and its forks). */
596
+ env_id?: string;
597
+ /** RFC 3339 timestamp. Scoped keys always expire. */
598
+ expires_at?: string;
599
+ /** The actor the key is pinned to, if any. */
600
+ actor?: ActorLabel;
601
+ created_by?: string;
602
+ }
603
+ /** ActorLabel is an actor as a caller names it: an opaque id (at most 128
604
+ * bytes) and an optional display name. */
605
+ interface ActorLabel {
606
+ id: string;
607
+ name?: string;
611
608
  }
609
+ /** Hitl scopes a key can be minted with. */
610
+ declare const HitlScope: {
611
+ readonly Read: "hitl:read";
612
+ readonly Decide: "hitl:decide";
613
+ readonly Secret: "hitl:secret";
614
+ readonly Takeover: "hitl:takeover";
615
+ readonly ActAs: "hitl:act-as";
616
+ };
617
+ type HitlScope = (typeof HitlScope)[keyof typeof HitlScope];
612
618
  /**
613
619
  * CreatedAPIKey is returned by apiKeys.create. `key` is the raw secret and is
614
620
  * unrecoverable after this response.
@@ -622,6 +628,8 @@ declare const State: {
622
628
  readonly Running: "running";
623
629
  readonly Draining: "draining";
624
630
  readonly Destroying: "destroying";
631
+ /** A destroy that ran out of retries. The environment may still exist on its host; destroy it again to retry. */
632
+ readonly DestroyFailed: "destroy_failed";
625
633
  readonly Destroyed: "destroyed";
626
634
  readonly Failed: "failed";
627
635
  };
@@ -643,6 +651,477 @@ declare const HealthState: {
643
651
  readonly Failing: "failing";
644
652
  };
645
653
  type HealthState = (typeof HealthState)[keyof typeof HealthState];
654
+ /** HitlSpec is the human-in-the-loop policy an environment is created with. */
655
+ interface HitlSpec {
656
+ /** Default deadline for every intervention, 10 to 86400. Omitted means 600. */
657
+ timeout_seconds?: number;
658
+ /** "reject" (the default) or "allow". */
659
+ on_timeout?: string;
660
+ /** The request kinds the agent may raise. */
661
+ requests: HitlRequests;
662
+ /** Refuse decisions made without a pinned or asserted actor. */
663
+ require_actor?: boolean;
664
+ /** Webhooks told about every intervention and audit event. */
665
+ notify?: HitlWebhook[];
666
+ /** Gates hold, record or deny exec, lifecycle and computer actions, and
667
+ * raise stall and expiry signals. */
668
+ gates?: HitlGate[];
669
+ /** Allowlist mode for exec. */
670
+ exec?: HitlExecPolicy;
671
+ }
672
+ /** HitlGate is one enforced gate. */
673
+ interface HitlGate {
674
+ name: string;
675
+ /** HitlKind.Exec, HitlKind.Lifecycle, HitlKind.Computer, HitlKind.Stall
676
+ * or HitlKind.Expiry. */
677
+ on: string;
678
+ match: HitlGateMatch;
679
+ /** One of GateMode; ask is the default. */
680
+ mode?: string;
681
+ /** The default reach of an approval: "once", "env" or "ttl:<duration>". */
682
+ grant?: string;
683
+ timeout_seconds?: number;
684
+ /** Returned on a deny and shown to the human on an ask. */
685
+ reason?: string;
686
+ /** A webhook asked allow, deny or ask before a human sees an ask gate's
687
+ * action. A failure or timeout means ask. */
688
+ decide?: HitlDecider;
689
+ }
690
+ /** HitlDecider is a gate's decider. secret names an entry in the create
691
+ * request's secrets; its value signs each call (Fuse-Signature). */
692
+ interface HitlDecider {
693
+ url: string;
694
+ secret: string;
695
+ }
696
+ /** HitlGateMatch selects the actions a gate applies to. Every field set must
697
+ * match. */
698
+ interface HitlGateMatch {
699
+ /** Matches the basename of argv[0] after env, sudo, nice, timeout, xargs,
700
+ * nohup and stdbuf are stripped. */
701
+ program?: string[];
702
+ /** Matches when these appear in argv in this order. */
703
+ args?: string[];
704
+ /** Matches any opaque shell exec. */
705
+ shell?: boolean;
706
+ /** Matches lifecycle actions (one of LifecycleAction) or, on a computer
707
+ * gate, computer action names ("type", "key", "left_click", ...). */
708
+ action?: string[];
709
+ /** Matches a computer "type" whose text contains any entry, ignoring
710
+ * case. */
711
+ text_contains?: string[];
712
+ /** Raises a stall after this many consecutive non-zero exec exits
713
+ * (on: "stall"). */
714
+ failures?: number;
715
+ /** Raises a stall after this long with no exec or computer call
716
+ * (on: "stall"). */
717
+ quiet_seconds?: number;
718
+ /** Raises an expiry this long before the idle_timeout or max_runtime reap
719
+ * (on: "expiry"). */
720
+ before_seconds?: number;
721
+ }
722
+ /** HitlExecPolicy is allowlist mode for exec: a program not in allow is held
723
+ * with default ("ask" or "deny"), interpreters and shells included. */
724
+ interface HitlExecPolicy {
725
+ default: string;
726
+ allow: string[];
727
+ }
728
+ /** ExecPayload is a held exec. */
729
+ interface ExecPayload {
730
+ argv: string[];
731
+ shell?: string;
732
+ timeout_ms?: number;
733
+ }
734
+ /** ComputerPayload is a held computer action. input is the body as sent. */
735
+ interface ComputerPayload {
736
+ action: string;
737
+ text?: string;
738
+ input: unknown;
739
+ }
740
+ /** DeciderNote is what a gate's decider said when it sent an action on to a
741
+ * human: verdict "ask" with its reason, or the error fuse fell through on. */
742
+ interface DeciderNote {
743
+ /** One of DeciderVerdict. */
744
+ verdict: string;
745
+ reason?: string;
746
+ error?: string;
747
+ }
748
+ /** InterventionReview is a review request. content is the file as fuse read
749
+ * it at request time when path is set. */
750
+ interface InterventionReview {
751
+ path?: string;
752
+ content: string;
753
+ /** One of ReviewFormat. */
754
+ format: string;
755
+ message?: string;
756
+ truncated?: boolean;
757
+ }
758
+ /** LifecyclePayload is a held environment action. */
759
+ interface LifecyclePayload {
760
+ /** One of LifecycleAction. */
761
+ action: string;
762
+ }
763
+ /** InterventionResult is what running an approved gated action produced. */
764
+ interface InterventionResult {
765
+ exec?: ExecResult;
766
+ computer?: ComputerResult;
767
+ env_id?: string;
768
+ snapshot_id?: string;
769
+ error?: string;
770
+ /** RFC 3339 timestamp. */
771
+ finished_at: string;
772
+ }
773
+ /** HitlWebhook is one hitl.notify target. `secret` names an entry of the
774
+ * create request's secrets; its value signs each delivery with HMAC-SHA256
775
+ * in the Fuse-Signature header. */
776
+ interface HitlWebhook {
777
+ url: string;
778
+ secret: string;
779
+ }
780
+ /** HitlRequests declares the allowed request kinds. Present means allowed;
781
+ * set ask (to {}) to allow asks. */
782
+ interface HitlRequests {
783
+ ask?: HitlAskRequests;
784
+ secret?: HitlSecretRequests;
785
+ takeover?: HitlTakeoverRequests;
786
+ review?: HitlReviewRequests;
787
+ }
788
+ /** HitlReviewRequests configures review requests. Present means allowed. */
789
+ type HitlReviewRequests = Record<string, never>;
790
+ /** HitlSecretRequests declares where a secret may land. The agent names one
791
+ * of these targets; it can never choose a path or variable of its own. */
792
+ interface HitlSecretRequests {
793
+ targets: HitlSecretTarget[];
794
+ }
795
+ /** HitlSecretTarget is one declared destination: exactly one of env (a
796
+ * variable later execs see) or file (an absolute guest path, written 0600).
797
+ * label is what the human is shown first. */
798
+ interface HitlSecretTarget {
799
+ env?: string;
800
+ file?: string;
801
+ label: string;
802
+ }
803
+ /** HitlTakeoverRequests declares the ways a human may take over: "terminal"
804
+ * and "desktop" (which needs a desktop block). Empty means terminal. */
805
+ interface HitlTakeoverRequests {
806
+ via?: string[];
807
+ }
808
+ /** HitlAskRequests configures ask requests. Present means allowed. */
809
+ type HitlAskRequests = Record<string, never>;
810
+ /** Actor is who raised or decided an intervention: "master", "key:<id>", or
811
+ * "system" for fuse itself (expiry, cancel on destroy). */
812
+ interface Actor {
813
+ id: string;
814
+ name?: string;
815
+ /** "key", "system" or "decider". */
816
+ source: string;
817
+ key_id?: string;
818
+ }
819
+ /** InterventionPayload is what was asked, attempted or noticed: a question
820
+ * for an ask, the held exec or lifecycle action for a gate, a secret or
821
+ * takeover request, or a signal. */
822
+ interface InterventionPayload {
823
+ question?: string;
824
+ choices?: string[];
825
+ exec?: ExecPayload;
826
+ lifecycle?: LifecyclePayload;
827
+ /** Set for a secret request only. */
828
+ secret?: InterventionSecret;
829
+ /** Set for a takeover only. */
830
+ takeover?: InterventionTakeover;
831
+ /** What fuse saw, for a stall or expiry. */
832
+ signal?: InterventionSignal;
833
+ /** The held computer action. */
834
+ computer?: ComputerPayload;
835
+ /** What is up for review. */
836
+ review?: InterventionReview;
837
+ /** The gate decider's note on an action it escalated. */
838
+ decider?: DeciderNote;
839
+ }
840
+ /** InterventionSecret is a secret request. label comes from the Fusefile and
841
+ * is what a human should read first; message is the agent's own text. The
842
+ * value is never part of any intervention. */
843
+ interface InterventionSecret {
844
+ target: string;
845
+ label: string;
846
+ message?: string;
847
+ }
848
+ /** InterventionTakeover is a takeover. holder and started_at are set while a
849
+ * human holds the environment, which is also when the agent's exec and
850
+ * computer calls get 423. */
851
+ interface InterventionTakeover {
852
+ via: string;
853
+ reason?: string;
854
+ holder?: Actor;
855
+ /** RFC 3339 timestamp. */
856
+ started_at?: string;
857
+ }
858
+ /** InterventionSignal is what fuse noticed on its own. A stall carries the
859
+ * failure run or the quiet time; an expiry carries the reaper due and when,
860
+ * and once approved the time the environment is kept until. */
861
+ interface InterventionSignal {
862
+ failures?: number;
863
+ last_exit_code?: number;
864
+ quiet_seconds?: number;
865
+ /** "idle_timeout" or "max_runtime". */
866
+ reaper?: string;
867
+ /** RFC 3339 timestamp. */
868
+ reap_at?: string;
869
+ /** RFC 3339 timestamp. */
870
+ extended_until?: string;
871
+ }
872
+ /** InterventionDecision is how an intervention resolved. An expired one
873
+ * carries the on_timeout outcome and a cancelled one a reject. */
874
+ interface InterventionDecision {
875
+ /** "approve" or "reject". */
876
+ outcome: string;
877
+ answer?: string;
878
+ reason?: string;
879
+ decided_by: Actor;
880
+ decided_at: string;
881
+ /** The argv the approver replaced a held exec's with. */
882
+ edited_argv?: string[];
883
+ /** The text the approver replaced a held type's with. */
884
+ edited_text?: string;
885
+ /** How far an approval of a gated action reaches. */
886
+ grant?: string;
887
+ }
888
+ /** Intervention is one request for a human. */
889
+ interface Intervention {
890
+ id: string;
891
+ env_id: string;
892
+ kind: string;
893
+ /** Names the gate that raised it; absent for a request. */
894
+ gate?: string;
895
+ /** The gate's mode (one of GateMode). notify and deny records are born
896
+ * terminal. */
897
+ mode?: string;
898
+ /** One of InterventionState. */
899
+ state: string;
900
+ payload: InterventionPayload;
901
+ triggered_by: Actor;
902
+ /** RFC 3339 timestamp. */
903
+ deadline: string;
904
+ on_timeout: string;
905
+ /** Set on every terminal intervention. */
906
+ decision?: InterventionDecision;
907
+ /** Set once an approved gated action has been performed. */
908
+ result?: InterventionResult;
909
+ created_at: string;
910
+ updated_at: string;
911
+ }
912
+ /** AuditEntry is one row of the hash-chained hitl audit log. It holds who
913
+ * did what and when, never intervention content. Hashes are hex. */
914
+ interface AuditEntry {
915
+ seq: number;
916
+ /** One of AuditEvent. */
917
+ event: string;
918
+ intervention_id?: string;
919
+ env_id?: string;
920
+ actor: Actor;
921
+ remote_addr?: string;
922
+ /** Links a takeover_session row to its cast; fetch it with
923
+ * interventions.getRecording. */
924
+ recording_id?: string;
925
+ /** RFC 3339 timestamp. */
926
+ at: string;
927
+ prev_hash: string;
928
+ hash: string;
929
+ }
930
+ /** Audit log events. */
931
+ declare const AuditEvent: {
932
+ readonly Raised: "raised";
933
+ readonly Decided: "decided";
934
+ readonly Expired: "expired";
935
+ readonly Cancelled: "cancelled";
936
+ readonly InviteMinted: "invite_minted";
937
+ readonly InviteUsed: "invite_used";
938
+ readonly GrantCreated: "grant_created";
939
+ readonly GrantUsed: "grant_used";
940
+ readonly SecretAnswered: "secret_answered";
941
+ readonly TakeoverStarted: "takeover_started";
942
+ readonly TakeoverEnded: "takeover_ended";
943
+ readonly TakeoverSession: "takeover_session";
944
+ };
945
+ type AuditEvent = (typeof AuditEvent)[keyof typeof AuditEvent];
946
+ /** Intervention kinds: ask is a request, exec and lifecycle are the gate
947
+ * surfaces. */
948
+ declare const HitlKind: {
949
+ readonly Ask: "ask";
950
+ readonly Exec: "exec";
951
+ readonly Lifecycle: "lifecycle";
952
+ readonly Computer: "computer";
953
+ readonly Review: "review";
954
+ readonly Secret: "secret";
955
+ readonly Takeover: "takeover";
956
+ readonly Stall: "stall";
957
+ readonly Expiry: "expiry";
958
+ };
959
+ type HitlKind = (typeof HitlKind)[keyof typeof HitlKind];
960
+ /** Gate modes. */
961
+ declare const GateMode: {
962
+ readonly Ask: "ask";
963
+ readonly Notify: "notify";
964
+ readonly Deny: "deny";
965
+ };
966
+ type GateMode = (typeof GateMode)[keyof typeof GateMode];
967
+ /** Decider verdicts. */
968
+ declare const DeciderVerdict: {
969
+ readonly Allow: "allow";
970
+ readonly Deny: "deny";
971
+ readonly Ask: "ask";
972
+ };
973
+ type DeciderVerdict = (typeof DeciderVerdict)[keyof typeof DeciderVerdict];
974
+ /** The actor source a decider's decision records. */
975
+ declare const ACTOR_SOURCE_DECIDER = "decider";
976
+ /** Review formats. */
977
+ declare const ReviewFormat: {
978
+ readonly Text: "text";
979
+ readonly Diff: "diff";
980
+ };
981
+ type ReviewFormat = (typeof ReviewFormat)[keyof typeof ReviewFormat];
982
+ /** Takeover modes: terminal gives the human a shell, desktop the live
983
+ * desktop view (it needs a desktop block). */
984
+ declare const TakeoverVia: {
985
+ readonly Terminal: "terminal";
986
+ readonly Desktop: "desktop";
987
+ };
988
+ type TakeoverVia = (typeof TakeoverVia)[keyof typeof TakeoverVia];
989
+ /** Lifecycle actions a gate can hold. */
990
+ declare const LifecycleAction: {
991
+ readonly Destroy: "destroy";
992
+ readonly Fork: "fork";
993
+ readonly Migrate: "migrate";
994
+ readonly Drain: "drain";
995
+ readonly Snapshot: "snapshot";
996
+ readonly RotateToken: "rotate-token";
997
+ };
998
+ type LifecycleAction = (typeof LifecycleAction)[keyof typeof LifecycleAction];
999
+ /** The gate name the exec allowlist reports. */
1000
+ declare const ALLOWLIST_GATE = "exec.allowlist";
1001
+ /** Intervention states. pending is the only non-terminal one. */
1002
+ declare const InterventionState: {
1003
+ readonly Pending: "pending";
1004
+ readonly Approved: "approved";
1005
+ readonly Rejected: "rejected";
1006
+ readonly Expired: "expired";
1007
+ readonly Cancelled: "cancelled";
1008
+ };
1009
+ type InterventionState = (typeof InterventionState)[keyof typeof InterventionState];
1010
+ /** Intervention outcomes. Every terminal intervention carries one. */
1011
+ declare const InterventionOutcome: {
1012
+ readonly Approve: "approve";
1013
+ readonly Reject: "reject";
1014
+ };
1015
+ type InterventionOutcome = (typeof InterventionOutcome)[keyof typeof InterventionOutcome];
1016
+ /** isInterventionTerminal reports whether the intervention is resolved. */
1017
+ declare function isInterventionTerminal(i: Intervention | undefined): boolean;
1018
+ /** isInterventionApproved reports whether the intervention resolved as
1019
+ * approve, which includes an expiry under on_timeout: allow. */
1020
+ declare function isInterventionApproved(i: Intervention | undefined): boolean;
1021
+ /** isInterventionSettled reports whether a caller has its final answer:
1022
+ * resolved, and for an approved gated action that fuse performs, performed
1023
+ * (with the outcome on result). */
1024
+ declare function isInterventionSettled(i: Intervention | undefined): boolean;
1025
+
1026
+ /** A minimal fetch signature, satisfied by the global fetch and most polyfills. */
1027
+ type FetchLike = (input: string | URL, init?: RequestInit) => Promise<Response>;
1028
+ /** Per-call options accepted by every service method. */
1029
+ interface CallOptions {
1030
+ /** Abort the request (and, for events, the stream). */
1031
+ signal?: AbortSignal;
1032
+ }
1033
+ interface TransportConfig {
1034
+ baseUrl: string;
1035
+ token?: string;
1036
+ fetch?: FetchLike;
1037
+ userAgent?: string;
1038
+ requestId?: () => string;
1039
+ timeoutMs?: number;
1040
+ headers?: Record<string, string>;
1041
+ actor?: ActorLabel;
1042
+ hitlNoWait?: boolean;
1043
+ onHeld?: (held: Intervention) => void;
1044
+ }
1045
+ /** Gated is the answer to a call a hitl gate may hold: the action's own
1046
+ * response body, or the held intervention of a 202. */
1047
+ type Gated<T> = {
1048
+ held: Intervention;
1049
+ } | {
1050
+ held?: undefined;
1051
+ value: T;
1052
+ };
1053
+ type Query = Record<string, string | undefined>;
1054
+ interface RequestSpec {
1055
+ query?: Query;
1056
+ body?: unknown;
1057
+ signal?: AbortSignal;
1058
+ }
1059
+ declare class Transport {
1060
+ private readonly baseUrl;
1061
+ private readonly token?;
1062
+ private readonly fetchImpl;
1063
+ private readonly userAgent?;
1064
+ private readonly requestId?;
1065
+ private readonly timeoutMs?;
1066
+ private readonly defaultHeaders?;
1067
+ private readonly actor?;
1068
+ /** The client's hitl wait options. */
1069
+ readonly hitlNoWait: boolean;
1070
+ readonly onHeld?: (held: Intervention) => void;
1071
+ constructor(cfg: TransportConfig);
1072
+ /** Perform a request and decode a JSON response body. */
1073
+ json<T>(method: string, path: string, spec?: RequestSpec): Promise<T>;
1074
+ /**
1075
+ * Perform a call a hitl gate may hold. A 202 answers with the held
1076
+ * intervention; anything else 2xx with the action's own (possibly empty)
1077
+ * body.
1078
+ */
1079
+ gated<T>(method: string, path: string, spec?: RequestSpec): Promise<Gated<T>>;
1080
+ /** Like noContent for a call a hitl gate may hold: it resolves to the held
1081
+ * intervention of a 202, or undefined when the action itself answered. */
1082
+ gatedNoContent(method: string, path: string, spec?: RequestSpec): Promise<Intervention | undefined>;
1083
+ /** Perform a request and return the response body as text. */
1084
+ text(method: string, path: string, spec?: RequestSpec): Promise<string>;
1085
+ private decodeHeld;
1086
+ /** Perform a request that returns no body (e.g. 204 actions). */
1087
+ noContent(method: string, path: string, spec?: RequestSpec): Promise<void>;
1088
+ /**
1089
+ * Open a long-lived stream (SSE). Applies NO timeout — only the caller's
1090
+ * signal cancels it — and checks the status eagerly so connect-time errors
1091
+ * (404/401/...) surface as a FuseApiError before any iteration.
1092
+ */
1093
+ stream(path: string, query: Query | undefined, signal?: AbortSignal): Promise<Response>;
1094
+ private execute;
1095
+ private buildUrl;
1096
+ private buildHeaders;
1097
+ private decodeJson;
1098
+ private withTimeout;
1099
+ }
1100
+
1101
+ /** Per-call options for calls a takeover can lock or a gate can hold
1102
+ * (computer actions). */
1103
+ interface LockedCallOptions extends CallOptions {
1104
+ /** Wait out a takeover or a pending stall, and a gate's hold (the
1105
+ * default). false throws a LockedError or a PendingInterventionError at
1106
+ * once instead. */
1107
+ wait?: boolean;
1108
+ /** Called with the pending intervention when a gate holds the action,
1109
+ * before waiting on it. It overrides the client's onHeld. */
1110
+ onHeld?: (held: Intervention) => void;
1111
+ }
1112
+ /** Body for environments.takeover. */
1113
+ interface TakeoverOptions {
1114
+ /** How to take over: TakeoverVia.Terminal (the default) or
1115
+ * TakeoverVia.Desktop. */
1116
+ via?: string;
1117
+ /** Recorded when no agent request is pending to claim. */
1118
+ reason?: string;
1119
+ }
1120
+ /** Body for environments.release. */
1121
+ interface ReleaseOptions {
1122
+ /** Handed back to the agent as the takeover's answer. */
1123
+ note?: string;
1124
+ }
646
1125
 
647
1126
  /** Filters (and pagination) for environments.list / environments.listPage. */
648
1127
  interface ListEnvironmentsOptions {
@@ -662,6 +1141,57 @@ interface EnvironmentPage {
662
1141
  /** Empty/undefined once there are no more results. */
663
1142
  nextCursor?: string;
664
1143
  }
1144
+ /** What environments.requestHuman sends. */
1145
+ interface HumanRequest {
1146
+ /** The request kind: "ask" (the default), "secret", "takeover" or
1147
+ * "review". */
1148
+ kind?: string;
1149
+ /** What the human is asked (ask). */
1150
+ question?: string;
1151
+ /** Restricts the answer to one of these (ask). */
1152
+ choices?: string[];
1153
+ /** A declared secret target: its env name or file path (secret). */
1154
+ target?: string;
1155
+ /** The agent's text shown under the target's label (secret), or with the
1156
+ * content up for review (review). */
1157
+ message?: string;
1158
+ /** How the human takes over: "terminal" or "desktop" (takeover). */
1159
+ via?: string;
1160
+ /** Why the agent wants a human to take over (takeover). */
1161
+ reason?: string;
1162
+ /** An absolute guest file to review; exclusive with content (review). */
1163
+ path?: string;
1164
+ /** Inline text or a diff to review (review). */
1165
+ content?: string;
1166
+ /** ReviewFormat.Text or ReviewFormat.Diff; unset guesses (review). */
1167
+ format?: string;
1168
+ /** Shortens the policy's deadline. It can never extend it. */
1169
+ timeoutSeconds?: number;
1170
+ /** Wait for a decision (the default). false returns the pending
1171
+ * intervention at once; wait on it later with interventions.wait. */
1172
+ wait?: boolean;
1173
+ }
1174
+ /** What environments.requestReview sends: exactly one of path and content. */
1175
+ interface ReviewRequest {
1176
+ /** An absolute guest file; fuse reads it at request time. */
1177
+ path?: string;
1178
+ /** Inline text or a diff. */
1179
+ content?: string;
1180
+ /** ReviewFormat.Text or ReviewFormat.Diff; unset guesses. */
1181
+ format?: string;
1182
+ message?: string;
1183
+ timeoutSeconds?: number;
1184
+ /** Wait for the verdict (the default). false returns at once with the
1185
+ * pending intervention and approved false. */
1186
+ wait?: boolean;
1187
+ }
1188
+ /** ReviewResult is a finished review. comments are the reviewer's, on
1189
+ * either outcome. */
1190
+ interface ReviewResult {
1191
+ approved: boolean;
1192
+ comments: string;
1193
+ intervention: Intervention;
1194
+ }
665
1195
  /** EnvironmentsService manages microVM lifecycle. */
666
1196
  declare class EnvironmentsService {
667
1197
  private readonly t;
@@ -676,13 +1206,18 @@ declare class EnvironmentsService {
676
1206
  get(vmId: string, opts?: CallOptions): Promise<EnvironmentInfo>;
677
1207
  /** Provision a new environment. Blocks until running or creation fails. */
678
1208
  create(body: CreateRequest, opts?: CallOptions): Promise<EnvironmentInfo>;
679
- /** Fork an environment into a new one; returns the new environment. */
1209
+ /** Fork an environment into a new one; returns the new environment. When a
1210
+ * hitl gate holds the fork, it waits for the decision and returns the
1211
+ * environment the orchestrator then forked. */
680
1212
  fork(vmId: string, body?: ForkOptions, opts?: CallOptions): Promise<EnvironmentInfo>;
681
1213
  /**
682
1214
  * Migrate an environment to another host; returns the new environment. The
683
1215
  * source is drained and destroyed once the new one is running.
684
1216
  */
685
1217
  migrate(vmId: string, body?: MigrateOptions, opts?: CallOptions): Promise<EnvironmentInfo>;
1218
+ /** heldEnvironment waits out a held fork or migrate and returns the
1219
+ * environment it produced. */
1220
+ private heldEnvironment;
686
1221
  /**
687
1222
  * Run a command inside a running environment's guest and return its exit code
688
1223
  * with stdout and stderr kept separate.
@@ -692,6 +1227,16 @@ declare class EnvironmentsService {
692
1227
  *
693
1228
  * Exec requires the master token.
694
1229
  *
1230
+ * When a hitl gate holds the exec, exec waits for the decision (bounded
1231
+ * only by `opts.signal`); the orchestrator runs the approved (possibly
1232
+ * edited) argv and exec returns its result. A rejection throws an
1233
+ * InterventionRejectedError, a deny gate an error isHitlDenied matches.
1234
+ * `wait: false` throws a PendingInterventionError at once instead.
1235
+ *
1236
+ * While a human holds a takeover (or an ask-mode stall is pending) the
1237
+ * server answers 423; exec waits for that intervention to settle and runs
1238
+ * the command then. `wait: false` throws a LockedError at once instead.
1239
+ *
695
1240
  * @example
696
1241
  * const out = await client.environments.exec(id, { cmd: ["ls", "-l"] });
697
1242
  * if (out.exit_code !== 0) console.error(out.stderr);
@@ -703,13 +1248,20 @@ declare class EnvironmentsService {
703
1248
  * booted from a desktop image; on any other image the server answers 503
704
1249
  * with a reason.
705
1250
  *
1251
+ * Like exec, it waits out a takeover or a pending stall (a 423) and then
1252
+ * acts; `opts.wait: false` throws a LockedError at once instead. When a
1253
+ * computer gate holds the action (a 202), it waits for the decision and
1254
+ * returns what the orchestrator got performing it (with the human's edited
1255
+ * text, if any); a rejection throws an InterventionRejectedError, and
1256
+ * `opts.wait: false` throws a PendingInterventionError at once instead.
1257
+ *
706
1258
  * @example
707
1259
  * const res = await client.environments.computer(id, {
708
1260
  * action: "left_click",
709
1261
  * coordinate: [512, 384],
710
1262
  * });
711
1263
  */
712
- computer(vmId: string, action: ComputerAction, opts?: CallOptions): Promise<ComputerResult>;
1264
+ computer(vmId: string, action: ComputerAction, opts?: LockedCallOptions): Promise<ComputerResult>;
713
1265
  /**
714
1266
  * Execute one computer tool_use against the environment and shape the
715
1267
  * answer for the Messages API: the tool_use block's `input` goes in
@@ -719,7 +1271,7 @@ declare class EnvironmentsService {
719
1271
  * An action the environment refuses (a malformed coordinate, a display
720
1272
  * that is not up) resolves to `is_error` content rather than throwing: the
721
1273
  * agent loop should feed it to the model, which is the party that can
722
- * correct it. A throw is reserved for failures the model cannot fix —
1274
+ * correct it. So does a hitl deny gate or a human rejecting a held action. A throw is reserved for failures the model cannot fix —
723
1275
  * transport, auth, an unknown environment.
724
1276
  *
725
1277
  * @example
@@ -730,20 +1282,66 @@ declare class EnvironmentsService {
730
1282
  * }
731
1283
  * }
732
1284
  */
733
- computerToolResult(vmId: string, input: unknown, opts?: CallOptions): Promise<ComputerToolResult>;
1285
+ computerToolResult(vmId: string, input: unknown, opts?: LockedCallOptions): Promise<ComputerToolResult>;
1286
+ private postComputer;
1287
+ /**
1288
+ * Ask a human for something the environment's hitl policy declares. By
1289
+ * default it waits (by long-polling) until the intervention resolves
1290
+ * (answered, rejected, expired or cancelled); read the result off the
1291
+ * returned intervention's decision. Pass `wait: false` to get the pending
1292
+ * handle instead. Pass `opts.signal` to stop waiting early.
1293
+ *
1294
+ * @example
1295
+ * const res = await client.environments.requestHuman(id, {
1296
+ * question: "deploy to prod?",
1297
+ * choices: ["yes", "no"],
1298
+ * });
1299
+ * if (isInterventionApproved(res)) console.log(res.decision?.answer);
1300
+ */
1301
+ requestHuman(vmId: string, req: HumanRequest, opts?: CallOptions): Promise<Intervention>;
1302
+ /**
1303
+ * Submit a guest file or a text/diff blob for a human to approve or reject
1304
+ * with comments, and wait for the verdict. Needs hitl.requests.review.
1305
+ * With `wait: false` it returns at once with the pending intervention and
1306
+ * approved false.
1307
+ *
1308
+ * @example
1309
+ * const r = await client.environments.requestReview(id, {
1310
+ * content: diff,
1311
+ * format: ReviewFormat.Diff,
1312
+ * });
1313
+ * if (!r.approved) console.log(r.comments);
1314
+ */
1315
+ requestReview(vmId: string, req: ReviewRequest, opts?: CallOptions): Promise<ReviewResult>;
1316
+ /**
1317
+ * Take over the environment as a human: claims the agent's pending
1318
+ * takeover request if there is one, else opens a new one. While held, the
1319
+ * agent's exec and computer calls wait. Returns the held intervention.
1320
+ * Needs the hitl:takeover scope; a conflict when someone else holds it.
1321
+ */
1322
+ takeover(vmId: string, body?: TakeoverOptions, opts?: CallOptions): Promise<Intervention>;
1323
+ /**
1324
+ * End a takeover and hand the environment back to the agent. The note is
1325
+ * the takeover's answer. Returns the approved intervention; a conflict when
1326
+ * nothing is held.
1327
+ */
1328
+ release(vmId: string, body?: ReleaseOptions, opts?: CallOptions): Promise<Intervention>;
734
1329
  /** Report whether the environment has a live display and at what geometry.
735
1330
  * up is false with a reason on an image with no desktop. */
736
1331
  computerDisplay(vmId: string, opts?: CallOptions): Promise<ComputerDisplay>;
737
1332
  /**
738
1333
  * Phase-1 only: quiesces the guest workload and moves the environment to
739
1334
  * "draining" so you can inspect its output, but does not destroy it. Call
740
- * destroy() afterward to remove it - a drained environment left
741
- * undestroyed keeps running (and billing) forever.
1335
+ * destroy() afterward to remove it. A drained environment left
1336
+ * undestroyed keeps running (and billing) until the orchestrator's
1337
+ * draining timeout (5m30s by default) passes, and then the orchestrator
1338
+ * destroys it.
742
1339
  */
743
1340
  drain(vmId: string, opts?: CallOptions): Promise<EnvironmentInfo>;
744
1341
  /** Rotate the per-VM credentials and push them to the guest. */
745
1342
  rotateToken(vmId: string, opts?: CallOptions): Promise<void>;
746
- /** Forcefully destroy an environment. Idempotent. */
1343
+ /** Forcefully destroy an environment. Idempotent. When a hitl gate holds
1344
+ * the destroy, it waits for the decision. */
747
1345
  destroy(vmId: string, opts?: CallOptions): Promise<void>;
748
1346
  /**
749
1347
  * Open the SSE event stream for an environment. The returned promise rejects
@@ -801,7 +1399,10 @@ declare class SnapshotsService {
801
1399
  * be `"disk"` even for a `live: true` request that reached a host too old to
802
1400
  * honour it. A caller that depends on the memory being there should check
803
1401
  * it. `live: true` against a backend with no live-snapshot support throws a
804
- * 501 `unimplemented`, not a conflict. */
1402
+ * 501 `unimplemented`, not a conflict.
1403
+ *
1404
+ * When a hitl gate holds the snapshot, create waits for the decision and
1405
+ * returns the snapshot the orchestrator then took. */
805
1406
  create(vmId: string, body?: SnapshotRequest, opts?: CallOptions): Promise<Snapshot>;
806
1407
  /** List snapshots, optionally filtered. Transparently walks every result
807
1408
  * page. For explicit single-page control (e.g. a cursor from a previous
@@ -869,6 +1470,19 @@ declare class HostsService {
869
1470
  private action;
870
1471
  }
871
1472
 
1473
+ /** Options for apiKeys.create. Naming scopes mints a key limited to the hitl
1474
+ * routes; it must expire (ttlSeconds, at most 86400). */
1475
+ interface CreateApiKeyOptions {
1476
+ label?: string;
1477
+ /** One or more of HitlScope. Omit for a full-access key. */
1478
+ scopes?: string[];
1479
+ /** Limit a scoped key to this environment and its forks. */
1480
+ envId?: string;
1481
+ /** Lifetime in seconds. */
1482
+ ttlSeconds?: number;
1483
+ /** Pin the key to this actor. */
1484
+ actor?: ActorLabel;
1485
+ }
872
1486
  /**
873
1487
  * ApiKeysService manages revocable API keys. These endpoints require the
874
1488
  * master token; the server enforces this.
@@ -877,16 +1491,121 @@ declare class ApiKeysService {
877
1491
  private readonly t;
878
1492
  constructor(t: Transport);
879
1493
  /**
880
- * Issue a new API key. The raw secret is returned once in `key` and is
881
- * unrecoverable afterward.
1494
+ * Issue a new API key. Pass a label, or options for a scoped key. The raw
1495
+ * secret is returned once in `key` and is unrecoverable afterward.
882
1496
  */
883
- create(label?: string, opts?: CallOptions): Promise<CreatedAPIKey>;
1497
+ create(labelOrOptions?: string | CreateApiKeyOptions, opts?: CallOptions): Promise<CreatedAPIKey>;
884
1498
  /** List API key metadata. */
885
1499
  list(opts?: CallOptions): Promise<APIKey[]>;
886
1500
  /** Revoke the API key with the given id. */
887
1501
  revoke(id: string, opts?: CallOptions): Promise<void>;
888
1502
  }
889
1503
 
1504
+ /** Filters (and pagination) for interventions.list / interventions.listPage. */
1505
+ interface ListInterventionsOptions {
1506
+ /** Only interventions raised by this environment. */
1507
+ envId?: string;
1508
+ /** One of InterventionState. */
1509
+ state?: string;
1510
+ /** Only interventions raised or decided by this actor id. */
1511
+ actor?: string;
1512
+ /** Page size. Only consulted by listPage; list() always walks every page. */
1513
+ limit?: number;
1514
+ /** Opaque cursor from a previous listPage() call's nextCursor. */
1515
+ cursor?: string;
1516
+ }
1517
+ /** One page of interventions.listPage, plus the cursor to fetch the next one. */
1518
+ interface InterventionPage {
1519
+ interventions: Intervention[];
1520
+ /** Empty/undefined once there are no more results. */
1521
+ nextCursor?: string;
1522
+ }
1523
+ /** Filters (and pagination) for interventions.audit / interventions.auditPage. */
1524
+ interface AuditOptions {
1525
+ /** Only rows for this environment. */
1526
+ envId?: string;
1527
+ /** Only rows for this actor id. */
1528
+ actor?: string;
1529
+ /** An RFC 3339 time, or a duration like "24h" meaning that long ago. */
1530
+ since?: string;
1531
+ /** Page size. Only consulted by auditPage; audit() always walks every page. */
1532
+ limit?: number;
1533
+ /** Opaque cursor from a previous auditPage() call's nextCursor. */
1534
+ cursor?: string;
1535
+ }
1536
+ /** One page of interventions.auditPage, plus the cursor to fetch the next one. */
1537
+ interface AuditPage {
1538
+ entries: AuditEntry[];
1539
+ /** Empty/undefined once there are no more results. */
1540
+ nextCursor?: string;
1541
+ }
1542
+ /** Body for interventions.approve: an answer to an ask, or an approval of a
1543
+ * held exec or lifecycle action. */
1544
+ interface ApproveOptions {
1545
+ /** An ask needs an answer (one of its choices, when it has any). */
1546
+ answer?: string;
1547
+ reason?: string;
1548
+ /** Replaces a held exec's argv. It is re-checked against the deny gates,
1549
+ * and cannot be combined with grant. */
1550
+ editedArgv?: string[];
1551
+ /** Replaces a held computer "type" action's text. It is re-checked against
1552
+ * the deny gates, and cannot be combined with grant. */
1553
+ editedText?: string;
1554
+ /** "once", "env" or "ttl:<duration>"; unset takes the gate's default. */
1555
+ grant?: string;
1556
+ /** How long approving an expiry keeps the environment alive. Zero or unset
1557
+ * is one more full window of the reaper that was due. */
1558
+ extendSeconds?: number;
1559
+ }
1560
+ /** Body for interventions.reject. */
1561
+ interface RejectOptions {
1562
+ /** Fed back to the agent; the server fills one in when empty. */
1563
+ reason?: string;
1564
+ }
1565
+ /** InterventionsService reads and decides hitl interventions. */
1566
+ declare class InterventionsService {
1567
+ private readonly t;
1568
+ constructor(t: Transport);
1569
+ /** List every intervention matching options, oldest first, walking every
1570
+ * page. */
1571
+ list(options?: ListInterventionsOptions, opts?: CallOptions): Promise<Intervention[]>;
1572
+ /** List one page of interventions matching options. */
1573
+ listPage(options?: ListInterventionsOptions, opts?: CallOptions): Promise<InterventionPage>;
1574
+ /** List every hitl audit row matching options, oldest first, walking every
1575
+ * page. */
1576
+ audit(options?: AuditOptions, opts?: CallOptions): Promise<AuditEntry[]>;
1577
+ /** List one page of the hitl audit log matching options. */
1578
+ auditPage(options?: AuditOptions, opts?: CallOptions): Promise<AuditPage>;
1579
+ /** Fetch a takeover session's recording (an AuditEntry's recording_id):
1580
+ * asciinema v2 cast text for a terminal session, json (an input-action log
1581
+ * plus screenshots) for a desktop one. Needs the hitl:read scope. */
1582
+ getRecording(recordingId: string, opts?: CallOptions): Promise<string>;
1583
+ /** Fetch one intervention without waiting. */
1584
+ get(id: string, opts?: CallOptions): Promise<Intervention>;
1585
+ /**
1586
+ * Block until the intervention settles: resolved, and for an approved gated
1587
+ * action, performed, with the outcome on result. Every intervention has a deadline,
1588
+ * so this always returns eventually. A dropped connection or an
1589
+ * orchestrator restart is survived by polling again; the server, not the
1590
+ * connection, holds the state. Pass `opts.signal` to stop early.
1591
+ */
1592
+ wait(id: string, opts?: CallOptions): Promise<Intervention>;
1593
+ /** Resolve a pending intervention as approved. For a held exec or
1594
+ * lifecycle action, the orchestrator then performs it (optionally with
1595
+ * body.editedArgv, or granting repeats with body.grant); wait on the id for
1596
+ * its result. Requires the master token. */
1597
+ approve(id: string, body?: ApproveOptions, opts?: CallOptions): Promise<Intervention>;
1598
+ /** Resolve a pending intervention as rejected. Requires the master token. */
1599
+ reject(id: string, body?: RejectOptions, opts?: CallOptions): Promise<Intervention>;
1600
+ /**
1601
+ * Answer a pending secret request. The value is written into the guest at
1602
+ * the declared target and dropped; fuse never stores, logs or returns it.
1603
+ * Needs the hitl:secret scope.
1604
+ */
1605
+ answerSecret(id: string, value: string, opts?: CallOptions): Promise<Intervention>;
1606
+ private decide;
1607
+ }
1608
+
890
1609
  /** Options for constructing a FuseClient. */
891
1610
  interface FuseClientOptions {
892
1611
  /** Base URL of the Fuse control plane, e.g. "https://fuse.example.com". Required. */
@@ -903,6 +1622,16 @@ interface FuseClientOptions {
903
1622
  timeoutMs?: number;
904
1623
  /** Extra default headers merged into every request. */
905
1624
  headers?: Record<string, string>;
1625
+ /** The human this client acts for, sent as the Fuse-Actor and
1626
+ * Fuse-Actor-Name headers on every request. */
1627
+ actor?: ActorLabel;
1628
+ /** Make every hitl-gated call throw a PendingInterventionError at once
1629
+ * instead of waiting for the human. exec's `wait: false` does the same for
1630
+ * one exec. */
1631
+ hitlNoWait?: boolean;
1632
+ /** Called with the pending intervention whenever a gate holds a call,
1633
+ * before the call waits on it. exec's `onHeld` overrides it. */
1634
+ onHeld?: (held: Intervention) => void;
906
1635
  }
907
1636
  /**
908
1637
  * FuseClient is the entry point for the Fuse API. It groups the resource
@@ -917,10 +1646,17 @@ declare class FuseClient {
917
1646
  readonly snapshots: SnapshotsService;
918
1647
  readonly hosts: HostsService;
919
1648
  readonly apiKeys: ApiKeysService;
1649
+ readonly interventions: InterventionsService;
1650
+ private readonly options;
920
1651
  constructor(options: FuseClientOptions);
1652
+ /**
1653
+ * withActor returns a copy of this client, with the same options otherwise,
1654
+ * that sends `actor` as the Fuse-Actor headers. This client is unchanged.
1655
+ */
1656
+ withActor(actor: ActorLabel): FuseClient;
921
1657
  }
922
1658
 
923
- declare const VERSION = "0.37.1";
1659
+ declare const VERSION = "0.42.0";
924
1660
 
925
1661
  /** Stable, machine-readable error codes returned by the server. */
926
1662
  declare const ErrorCode: {
@@ -930,6 +1666,8 @@ declare const ErrorCode: {
930
1666
  readonly Unavailable: "unavailable";
931
1667
  readonly Internal: "internal";
932
1668
  readonly Unauthorized: "unauthorized";
1669
+ readonly Forbidden: "forbidden";
1670
+ readonly Locked: "locked";
933
1671
  };
934
1672
  type ErrorCode = (typeof ErrorCode)[keyof typeof ErrorCode];
935
1673
  interface FuseApiErrorInit {
@@ -950,12 +1688,74 @@ declare class FuseApiError extends Error {
950
1688
  readonly body?: string;
951
1689
  constructor(init: FuseApiErrorInit);
952
1690
  }
1691
+ /** Reasons a forbidden error carries in details.reason. */
1692
+ declare const ForbiddenReason: {
1693
+ readonly MissingScope: "missing_scope";
1694
+ readonly EnvScope: "env_scope";
1695
+ readonly SelfApproval: "self_approval";
1696
+ readonly ActorRequired: "actor_required";
1697
+ readonly ActorMismatch: "actor_mismatch";
1698
+ readonly ActorNotAllowed: "actor_not_allowed";
1699
+ readonly MintEscalation: "mint_escalation";
1700
+ /** A hitl deny gate (or the exec allowlist in deny mode) refused the
1701
+ * action. The message is the gate's reason; details.gate names the gate. */
1702
+ readonly HitlDenied: "hitl_denied";
1703
+ };
1704
+ type ForbiddenReason = (typeof ForbiddenReason)[keyof typeof ForbiddenReason];
1705
+ /**
1706
+ * ForbiddenError is a 403 forbidden response: the caller is authenticated
1707
+ * but not allowed. `reason` is one of ForbiddenReason (empty when the server
1708
+ * sent none) and `scope` names the missing scope for missing_scope.
1709
+ */
1710
+ declare class ForbiddenError extends FuseApiError {
1711
+ readonly reason: string;
1712
+ readonly scope?: string;
1713
+ constructor(init: FuseApiErrorInit);
1714
+ }
1715
+ /**
1716
+ * LockedError is a 423 locked response: a human holds the environment (a
1717
+ * takeover) or an ask-mode stall is pending, so agent exec and computer calls
1718
+ * are refused. exec and computer wait it out by default; this surfaces only
1719
+ * when waiting is off. Wait on `interventionId` with interventions.wait.
1720
+ */
1721
+ declare class LockedError extends FuseApiError {
1722
+ readonly interventionId: string;
1723
+ /** "takeover" or "stall". */
1724
+ readonly kind: string;
1725
+ constructor(init: FuseApiErrorInit);
1726
+ }
1727
+ /** isLocked reports whether err is a 423 locked api error. */
1728
+ declare function isLocked(err: unknown): err is LockedError;
953
1729
  /** FuseError is a transport, configuration, or decoding failure (not an API error). */
954
1730
  declare class FuseError extends Error {
955
1731
  constructor(message: string, options?: {
956
1732
  cause?: unknown;
957
1733
  });
958
1734
  }
1735
+ /**
1736
+ * PendingInterventionError is thrown instead of waiting when a hitl gate held
1737
+ * the call and waiting is off (exec's `wait: false` or the client's
1738
+ * `hitlNoWait`). Wait on `intervention.id` with interventions.wait; the
1739
+ * outcome is on its result.
1740
+ */
1741
+ declare class PendingInterventionError extends Error {
1742
+ readonly intervention: Intervention;
1743
+ constructor(intervention: Intervention);
1744
+ }
1745
+ /**
1746
+ * InterventionRejectedError is a held call a human rejected, or that expired
1747
+ * or was cancelled without approval. `intervention.decision.reason` is for
1748
+ * the agent to read and replan on.
1749
+ */
1750
+ declare class InterventionRejectedError extends Error {
1751
+ readonly intervention: Intervention;
1752
+ constructor(intervention: Intervention);
1753
+ }
1754
+ /** HeldActionError is an approved held action fuse could not perform. */
1755
+ declare class HeldActionError extends Error {
1756
+ readonly intervention: Intervention;
1757
+ constructor(intervention: Intervention);
1758
+ }
959
1759
  /** isFuseApiError is a type guard for FuseApiError. */
960
1760
  declare function isFuseApiError(err: unknown): err is FuseApiError;
961
1761
  /** isNotFound reports whether err is a not_found api error. */
@@ -964,9 +1764,13 @@ declare function isNotFound(err: unknown): boolean;
964
1764
  declare function isConflict(err: unknown): boolean;
965
1765
  /** isUnauthorized reports whether err is an unauthorized api error. */
966
1766
  declare function isUnauthorized(err: unknown): boolean;
1767
+ /** isForbidden reports whether err is a forbidden api error. */
1768
+ declare function isForbidden(err: unknown): err is ForbiddenError;
1769
+ /** isHitlDenied reports whether err is an action a hitl gate denied. */
1770
+ declare function isHitlDenied(err: unknown): err is ForbiddenError;
967
1771
  /** isInvalidArgument reports whether err is an invalid_argument api error. */
968
1772
  declare function isInvalidArgument(err: unknown): boolean;
969
1773
  /** isUnavailable reports whether err is an unavailable api error. */
970
1774
  declare function isUnavailable(err: unknown): boolean;
971
1775
 
972
- export { type APIKey, ApiKeysService, type CallOptions, type ComputerAction, type ComputerDisplay, type ComputerResult, type ComputerToolResult, type CreateRequest, type CreatedAPIKey, type DesktopSpec, type EgressHealth, type EgressSpec, type EgressStatus, type Endpoint, type EnvironmentInfo, EnvironmentsService, ErrorCode, type Event, type ExecRequest, type ExecResult, type ExposeSpec, type FetchLike, type ForkOptions, FuseApiError, type FuseApiErrorInit, FuseClient, type FuseClientOptions, FuseError, type GPUDevice, type Health, HealthState, type HealthcheckHttp, type HealthcheckSpec, type Host, type HostCapacity, HostsService, type ListEnvironmentsOptions, type ListSnapshotsOptions, type MIGInstance, type MigrateOptions, type RegisterHostRequest, type Snapshot, type SnapshotExport, type SnapshotRequest, SnapshotsService, type Spec, State, type ToolResultBlock, type ToolResultImageSource, VERSION, isConflict, isFuseApiError, isInvalidArgument, isNotFound, isTerminalState, isUnauthorized, isUnavailable };
1776
+ export { ACTOR_SOURCE_DECIDER, ALLOWLIST_GATE, type APIKey, type Actor, type ActorLabel, ApiKeysService, type ApproveOptions, type AuditEntry, AuditEvent, type AuditOptions, type AuditPage, type CallOptions, type ComputerAction, type ComputerDisplay, type ComputerPayload, type ComputerResult, type ComputerToolResult, type CreateApiKeyOptions, type CreateRequest, type CreatedAPIKey, type DeciderNote, DeciderVerdict, type DesktopSpec, type EgressHealth, type EgressSpec, type EgressStatus, type Endpoint, type EnvironmentInfo, EnvironmentsService, ErrorCode, type Event, type ExecPayload, type ExecRequest, type ExecResult, type ExposeSpec, type FetchLike, ForbiddenError, ForbiddenReason, type ForkOptions, FuseApiError, type FuseApiErrorInit, FuseClient, type FuseClientOptions, FuseError, type GPUDevice, GateMode, type Health, HealthState, type HealthcheckHttp, type HealthcheckSpec, HeldActionError, type HitlAskRequests, type HitlDecider, type HitlExecPolicy, type HitlGate, type HitlGateMatch, HitlKind, type HitlRequests, type HitlReviewRequests, HitlScope, type HitlSecretRequests, type HitlSecretTarget, type HitlSpec, type HitlTakeoverRequests, type HitlWebhook, type Host, type HostCapacity, HostsService, type HumanRequest, type Intervention, type InterventionDecision, InterventionOutcome, type InterventionPage, type InterventionPayload, InterventionRejectedError, type InterventionResult, type InterventionReview, type InterventionSecret, type InterventionSignal, InterventionState, type InterventionTakeover, InterventionsService, LifecycleAction, type LifecyclePayload, type ListEnvironmentsOptions, type ListInterventionsOptions, type ListSnapshotsOptions, type LockedCallOptions, LockedError, type MIGInstance, type MigrateOptions, PendingInterventionError, type RegisterHostRequest, type RejectOptions, type ReleaseOptions, ReviewFormat, type ReviewRequest, type ReviewResult, type Snapshot, type SnapshotExport, type SnapshotRequest, SnapshotsService, type Spec, State, type TakeoverOptions, TakeoverVia, type ToolResultBlock, type ToolResultImageSource, VERSION, isConflict, isForbidden, isFuseApiError, isHitlDenied, isInterventionApproved, isInterventionSettled, isInterventionTerminal, isInvalidArgument, isLocked, isNotFound, isTerminalState, isUnauthorized, isUnavailable };