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