@paradow/ledge 0.35.0 → 0.37.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/cli/index.js +4673 -1524
- package/dist/cli/reduceWorker.js +962 -664
- package/dist/ledge-guide.md +11 -2
- package/package.json +1 -1
package/dist/cli/reduceWorker.js
CHANGED
|
@@ -728,8 +728,12 @@ var ProjectsSchema = z14.object({ projects: z14.array(ProjectSchema).max(100), r
|
|
|
728
728
|
for (const problem of projectsProblems(p)) ctx.addIssue({ code: "custom", message: problem.code, path: problem.path });
|
|
729
729
|
});
|
|
730
730
|
|
|
731
|
+
// ../protocol/src/broadcast.ts
|
|
732
|
+
var BROADCAST_SESSIONS_MAX = 32;
|
|
733
|
+
var BROADCAST_LABEL_MAX = 120;
|
|
734
|
+
|
|
731
735
|
// ../protocol/src/messages.ts
|
|
732
|
-
import { z as
|
|
736
|
+
import { z as z23 } from "zod";
|
|
733
737
|
|
|
734
738
|
// ../protocol/src/repos.ts
|
|
735
739
|
import { z as z15 } from "zod";
|
|
@@ -742,266 +746,461 @@ var ReposInspectSchema = z15.object({
|
|
|
742
746
|
var RepoInfoSchema = z15.object({ path: z15.string(), exists: z15.boolean(), branch: z15.string().nullable() });
|
|
743
747
|
var ReposInfoSchema = z15.object({ type: z15.literal("repos.info"), requestId: z15.string(), repos: z15.array(RepoInfoSchema) });
|
|
744
748
|
|
|
745
|
-
// ../protocol/src/
|
|
749
|
+
// ../protocol/src/commands.ts
|
|
746
750
|
import { z as z16 } from "zod";
|
|
747
|
-
var
|
|
748
|
-
var
|
|
749
|
-
var
|
|
751
|
+
var SlashSourceSchema = z16.enum(["builtin", "project", "user", "plugin", "extension"]);
|
|
752
|
+
var SLASH_COMMANDS_MAX = 400;
|
|
753
|
+
var SLASH_DESCRIPTION_MAX = 300;
|
|
754
|
+
var SlashCommandSchema = z16.object({
|
|
755
|
+
name: z16.string().min(1).max(200),
|
|
756
|
+
description: z16.string().max(SLASH_DESCRIPTION_MAX).optional(),
|
|
757
|
+
argumentHint: z16.string().max(200).optional(),
|
|
758
|
+
aliases: z16.array(z16.string().min(1).max(200)).max(10).optional(),
|
|
759
|
+
source: SlashSourceSchema,
|
|
760
|
+
blocked: z16.enum(["panel", "close"]).optional(),
|
|
761
|
+
bare: z16.literal("panel").optional()
|
|
762
|
+
});
|
|
763
|
+
var SessionCommandsSchema = z16.object({ type: z16.literal("session.commands"), requestId: z16.string().min(1).max(100), id: z16.string() });
|
|
764
|
+
var SessionCommandsListingSchema = z16.object({
|
|
765
|
+
type: z16.literal("session.commands.listing"),
|
|
766
|
+
requestId: z16.string(),
|
|
767
|
+
id: z16.string(),
|
|
768
|
+
tool: ToolSchema.optional(),
|
|
769
|
+
commands: z16.array(SlashCommandSchema).max(SLASH_COMMANDS_MAX),
|
|
770
|
+
error: z16.string().max(200).optional()
|
|
771
|
+
});
|
|
750
772
|
|
|
751
|
-
// ../protocol/src/
|
|
773
|
+
// ../protocol/src/scan.ts
|
|
752
774
|
import { z as z17 } from "zod";
|
|
753
|
-
var
|
|
754
|
-
var
|
|
755
|
-
var
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
775
|
+
var ReposScanSchema = z17.object({ type: z17.literal("repos.scan"), requestId: z17.string().min(1).max(100), machineId: z17.string().optional() });
|
|
776
|
+
var FoundRepoSchema = z17.object({ path: z17.string(), branch: z17.string().nullable(), remote: z17.string().optional(), manifest: z17.string().max(200).optional() });
|
|
777
|
+
var ReposFoundSchema = z17.object({ type: z17.literal("repos.found"), requestId: z17.string(), repos: z17.array(FoundRepoSchema).max(500), truncated: z17.boolean(), error: z17.string().optional() });
|
|
778
|
+
|
|
779
|
+
// ../protocol/src/openrig.ts
|
|
780
|
+
import { z as z18 } from "zod";
|
|
781
|
+
var RigNameSchema = z18.string().min(1).max(100).regex(/^[A-Za-z0-9][A-Za-z0-9._-]*$/, "not a rig name");
|
|
782
|
+
var RigSeatNameSchema = z18.string().min(3).max(200).regex(/^[A-Za-z0-9][A-Za-z0-9._-]*@[A-Za-z0-9][A-Za-z0-9._-]*$/, "not a seat");
|
|
783
|
+
var SeatPermissionsSchema = z18.enum(["inherit", "floor", "auto", "full_bypass"]);
|
|
784
|
+
var RigSeatSchema = z18.object({
|
|
785
|
+
logicalId: z18.string(),
|
|
786
|
+
session: z18.string(),
|
|
787
|
+
runtime: z18.string(),
|
|
788
|
+
model: z18.string().nullable(),
|
|
789
|
+
sessionStatus: z18.string(),
|
|
790
|
+
lifecycleState: z18.string(),
|
|
791
|
+
activity: z18.string(),
|
|
792
|
+
stopped: z18.boolean(),
|
|
793
|
+
work: z18.number().int().min(0)
|
|
794
|
+
});
|
|
795
|
+
var RigSchema = z18.object({
|
|
796
|
+
id: z18.string(),
|
|
797
|
+
name: z18.string(),
|
|
798
|
+
status: z18.string(),
|
|
799
|
+
lifecycleState: z18.string(),
|
|
800
|
+
seats: z18.number().int().min(0),
|
|
801
|
+
running: z18.number().int().min(0),
|
|
802
|
+
attention: z18.number().int().min(0),
|
|
803
|
+
uptime: z18.string().nullable(),
|
|
804
|
+
snapshot: z18.string().nullable(),
|
|
805
|
+
nodes: z18.array(RigSeatSchema)
|
|
806
|
+
});
|
|
807
|
+
var RigDaemonStateSchema = z18.enum(["running", "stopped", "stale", "unknown"]);
|
|
808
|
+
var RigKernelStateSchema = z18.enum(["ready", "starting", "stopped", "skipped", "absent", "unknown"]);
|
|
809
|
+
var OpenRigStateSchema = z18.object({
|
|
810
|
+
installed: z18.boolean(),
|
|
811
|
+
version: z18.string().optional(),
|
|
812
|
+
daemon: z18.object({ state: RigDaemonStateSchema, pid: z18.number().int().optional(), port: z18.number().int().optional(), message: z18.string().optional() }),
|
|
813
|
+
kernel: z18.object({ state: RigKernelStateSchema }),
|
|
814
|
+
rigs: z18.array(RigSchema),
|
|
787
815
|
/** When it was read (ISO). */
|
|
788
|
-
at:
|
|
816
|
+
at: z18.string()
|
|
789
817
|
});
|
|
790
|
-
var OpenRigActionSchema =
|
|
818
|
+
var OpenRigActionSchema = z18.discriminatedUnion("kind", [
|
|
791
819
|
/** `restore` `last`: `rig start --last` (the rigs that were running come back); `none`: `rig daemon start`. */
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
820
|
+
z18.object({ kind: z18.literal("daemon.start"), restore: z18.enum(["last", "none"]).optional(), noKernel: z18.boolean().optional() }),
|
|
821
|
+
z18.object({ kind: z18.literal("daemon.stop") }),
|
|
822
|
+
z18.object({ kind: z18.literal("daemon.restart"), restore: z18.enum(["last", "none"]).optional() }),
|
|
823
|
+
z18.object({ kind: z18.literal("kernel.start") }),
|
|
824
|
+
z18.object({ kind: z18.literal("kernel.stop") }),
|
|
825
|
+
z18.object({ kind: z18.literal("kernel.restart") }),
|
|
826
|
+
z18.object({ kind: z18.literal("rig.start"), rig: RigNameSchema }),
|
|
827
|
+
z18.object({ kind: z18.literal("rig.stop"), rig: RigNameSchema, snapshot: z18.boolean().optional(), force: z18.boolean().optional(), delete: z18.boolean().optional() }),
|
|
828
|
+
z18.object({ kind: z18.literal("rig.restart"), rig: RigNameSchema }),
|
|
801
829
|
/** A seat's settings (docs/mockups/ledge-openrig-groups.html, view 2). `runtime`: moves it now to the other harness (a successor
|
|
802
830
|
* takes its context, `rig seat set-runtime`), `model` and `effort` going with it; without it, `model`, `effort` and `permissions`
|
|
803
831
|
* apply at its next resume. `dryRun`: the move's plan only. */
|
|
804
|
-
|
|
805
|
-
kind:
|
|
832
|
+
z18.object({
|
|
833
|
+
kind: z18.literal("seat.set"),
|
|
806
834
|
seat: RigSeatNameSchema,
|
|
807
|
-
runtime:
|
|
835
|
+
runtime: z18.enum(["claude-code", "codex"]).optional(),
|
|
808
836
|
model: ModelIdSchema.optional(),
|
|
809
837
|
effort: EffortSchema.optional(),
|
|
810
838
|
permissions: SeatPermissionsSchema.optional(),
|
|
811
|
-
dryRun:
|
|
839
|
+
dryRun: z18.boolean().optional()
|
|
812
840
|
})
|
|
813
841
|
]);
|
|
814
|
-
var OpenRigStatusSchema =
|
|
815
|
-
var OpenRigActSchema =
|
|
816
|
-
var OpenRigUnreachableSchema =
|
|
817
|
-
var OpenRigStateMessageSchema =
|
|
818
|
-
type:
|
|
819
|
-
requestId:
|
|
820
|
-
machineId:
|
|
842
|
+
var OpenRigStatusSchema = z18.object({ type: z18.literal("openrig.status"), requestId: z18.string().min(1).max(100), machineId: z18.string().optional() });
|
|
843
|
+
var OpenRigActSchema = z18.object({ type: z18.literal("openrig.act"), requestId: z18.string().min(1).max(100), action: OpenRigActionSchema, machineId: z18.string().optional() });
|
|
844
|
+
var OpenRigUnreachableSchema = z18.enum(["offline", "outdated"]);
|
|
845
|
+
var OpenRigStateMessageSchema = z18.object({
|
|
846
|
+
type: z18.literal("openrig.state"),
|
|
847
|
+
requestId: z18.string().optional(),
|
|
848
|
+
machineId: z18.string().optional(),
|
|
821
849
|
state: OpenRigStateSchema.optional(),
|
|
822
850
|
error: OpenRigUnreachableSchema.optional()
|
|
823
851
|
});
|
|
824
|
-
var OpenRigDoneSchema =
|
|
825
|
-
type:
|
|
826
|
-
requestId:
|
|
827
|
-
machineId:
|
|
828
|
-
ok:
|
|
829
|
-
commands:
|
|
830
|
-
output:
|
|
831
|
-
error:
|
|
832
|
-
});
|
|
833
|
-
var RigSpecMemberSchema =
|
|
834
|
-
id:
|
|
835
|
-
runtime:
|
|
836
|
-
model:
|
|
837
|
-
agent:
|
|
852
|
+
var OpenRigDoneSchema = z18.object({
|
|
853
|
+
type: z18.literal("openrig.done"),
|
|
854
|
+
requestId: z18.string(),
|
|
855
|
+
machineId: z18.string().optional(),
|
|
856
|
+
ok: z18.boolean(),
|
|
857
|
+
commands: z18.array(z18.string()),
|
|
858
|
+
output: z18.string().optional(),
|
|
859
|
+
error: z18.string().optional()
|
|
860
|
+
});
|
|
861
|
+
var RigSpecMemberSchema = z18.object({
|
|
862
|
+
id: z18.string(),
|
|
863
|
+
runtime: z18.string(),
|
|
864
|
+
model: z18.string().optional(),
|
|
865
|
+
agent: z18.string(),
|
|
838
866
|
/** Its `permission_policy`, as written. */
|
|
839
|
-
policy:
|
|
840
|
-
});
|
|
841
|
-
var RigSpecPodSchema =
|
|
842
|
-
var RigSpecSchema =
|
|
843
|
-
name:
|
|
844
|
-
source:
|
|
845
|
-
summary:
|
|
846
|
-
path:
|
|
867
|
+
policy: z18.string().optional()
|
|
868
|
+
});
|
|
869
|
+
var RigSpecPodSchema = z18.object({ id: z18.string(), label: z18.string().optional(), members: z18.array(RigSpecMemberSchema) });
|
|
870
|
+
var RigSpecSchema = z18.object({
|
|
871
|
+
name: z18.string(),
|
|
872
|
+
source: z18.string(),
|
|
873
|
+
summary: z18.string().optional(),
|
|
874
|
+
path: z18.string(),
|
|
847
875
|
/** The rig's `permission_policy`, as written (`builtin:auto`, a file…). */
|
|
848
|
-
policy:
|
|
849
|
-
pods:
|
|
850
|
-
edges:
|
|
851
|
-
});
|
|
852
|
-
var OpenRigSpecsSchema =
|
|
853
|
-
var OpenRigSpecListSchema =
|
|
854
|
-
type:
|
|
855
|
-
requestId:
|
|
856
|
-
machineId:
|
|
857
|
-
specs:
|
|
858
|
-
error:
|
|
859
|
-
});
|
|
860
|
-
var RigRoleSchema =
|
|
861
|
-
var RuntimeSchema =
|
|
862
|
-
var RigPolicySchema =
|
|
876
|
+
policy: z18.string().optional(),
|
|
877
|
+
pods: z18.array(RigSpecPodSchema),
|
|
878
|
+
edges: z18.number().int().min(0)
|
|
879
|
+
});
|
|
880
|
+
var OpenRigSpecsSchema = z18.object({ type: z18.literal("openrig.specs"), requestId: z18.string().min(1).max(100), machineId: z18.string().optional() });
|
|
881
|
+
var OpenRigSpecListSchema = z18.object({
|
|
882
|
+
type: z18.literal("openrig.specList"),
|
|
883
|
+
requestId: z18.string(),
|
|
884
|
+
machineId: z18.string().optional(),
|
|
885
|
+
specs: z18.array(RigSpecSchema),
|
|
886
|
+
error: z18.string().optional()
|
|
887
|
+
});
|
|
888
|
+
var RigRoleSchema = z18.string().min(3).max(201).regex(/^[A-Za-z0-9][A-Za-z0-9_-]*\.[A-Za-z0-9][A-Za-z0-9_-]*$/, "not a role");
|
|
889
|
+
var RuntimeSchema = z18.enum(["claude-code", "codex"]);
|
|
890
|
+
var RigPolicySchema = z18.enum(["auto", "standard", "open", "yolo", "locked"]);
|
|
863
891
|
var RIG_POLICIES = RigPolicySchema.options;
|
|
864
|
-
var RigLaunchSchema =
|
|
892
|
+
var RigLaunchSchema = z18.object({
|
|
865
893
|
spec: RigNameSchema,
|
|
866
|
-
cwd:
|
|
894
|
+
cwd: z18.string().min(1).max(4096),
|
|
867
895
|
name: RigNameSchema,
|
|
868
|
-
seats:
|
|
869
|
-
runtimes:
|
|
870
|
-
models:
|
|
871
|
-
delegation:
|
|
872
|
-
save:
|
|
873
|
-
plan:
|
|
896
|
+
seats: z18.record(RigRoleSchema, z18.number().int().min(0).max(9)).optional(),
|
|
897
|
+
runtimes: z18.record(RigRoleSchema, RuntimeSchema).optional(),
|
|
898
|
+
models: z18.record(RigRoleSchema, z18.string().min(1).max(200).regex(/^[A-Za-z0-9][A-Za-z0-9._:/@[\]=,+-]*$/)).optional(),
|
|
899
|
+
delegation: z18.enum(["pairs", "all"]).optional(),
|
|
900
|
+
save: z18.boolean().optional(),
|
|
901
|
+
plan: z18.boolean().optional(),
|
|
874
902
|
/** The rig's permission policy, in place of the spec's; `policies`: a role's, over the rig's. */
|
|
875
903
|
policy: RigPolicySchema.optional(),
|
|
876
|
-
policies:
|
|
904
|
+
policies: z18.record(RigRoleSchema, RigPolicySchema).optional(),
|
|
877
905
|
/** Accept the harnesses' first-launch warnings (`rig up --non-interruptive`), so a bypassing seat does not stop on Claude's dialog. */
|
|
878
|
-
nonInterruptive:
|
|
879
|
-
});
|
|
880
|
-
var OpenRigLaunchSchema =
|
|
881
|
-
var OpenRigLaunchedSchema =
|
|
882
|
-
type:
|
|
883
|
-
requestId:
|
|
884
|
-
machineId:
|
|
885
|
-
ok:
|
|
886
|
-
plan:
|
|
887
|
-
commands:
|
|
888
|
-
yaml:
|
|
889
|
-
seats:
|
|
890
|
-
output:
|
|
891
|
-
error:
|
|
906
|
+
nonInterruptive: z18.boolean().optional()
|
|
907
|
+
});
|
|
908
|
+
var OpenRigLaunchSchema = z18.object({ type: z18.literal("openrig.launch"), requestId: z18.string().min(1).max(100), launch: RigLaunchSchema, machineId: z18.string().optional() });
|
|
909
|
+
var OpenRigLaunchedSchema = z18.object({
|
|
910
|
+
type: z18.literal("openrig.launched"),
|
|
911
|
+
requestId: z18.string(),
|
|
912
|
+
machineId: z18.string().optional(),
|
|
913
|
+
ok: z18.boolean(),
|
|
914
|
+
plan: z18.boolean(),
|
|
915
|
+
commands: z18.array(z18.string()),
|
|
916
|
+
yaml: z18.string().optional(),
|
|
917
|
+
seats: z18.number().int().optional(),
|
|
918
|
+
output: z18.string().optional(),
|
|
919
|
+
error: z18.string().optional(),
|
|
892
920
|
/** What `rig up --plan` concluded: its status (`planned`…), its errors and its warnings (one per seat without a permission policy…). */
|
|
893
|
-
check:
|
|
921
|
+
check: z18.object({ status: z18.string(), errors: z18.array(z18.string()), warnings: z18.array(z18.string()) }).optional()
|
|
894
922
|
});
|
|
895
|
-
var OpenRigSeatsSchema =
|
|
896
|
-
type:
|
|
897
|
-
requestId:
|
|
898
|
-
machineId:
|
|
923
|
+
var OpenRigSeatsSchema = z18.object({
|
|
924
|
+
type: z18.literal("openrig.seats"),
|
|
925
|
+
requestId: z18.string().min(1).max(100),
|
|
926
|
+
machineId: z18.string().optional(),
|
|
899
927
|
rig: RigNameSchema,
|
|
900
|
-
seats:
|
|
928
|
+
seats: z18.record(RigRoleSchema, z18.number().int().min(0).max(9)),
|
|
901
929
|
/** How the added seats are linked, where OpenRig can add links to a running rig (0.7+): like a launch's. */
|
|
902
|
-
delegation:
|
|
903
|
-
});
|
|
904
|
-
var OpenRigReseatedSchema =
|
|
905
|
-
type:
|
|
906
|
-
requestId:
|
|
907
|
-
machineId:
|
|
908
|
-
ok:
|
|
909
|
-
commands:
|
|
910
|
-
added:
|
|
911
|
-
removed:
|
|
912
|
-
output:
|
|
913
|
-
error:
|
|
930
|
+
delegation: z18.enum(["pairs", "all"]).optional()
|
|
931
|
+
});
|
|
932
|
+
var OpenRigReseatedSchema = z18.object({
|
|
933
|
+
type: z18.literal("openrig.reseated"),
|
|
934
|
+
requestId: z18.string(),
|
|
935
|
+
machineId: z18.string().optional(),
|
|
936
|
+
ok: z18.boolean(),
|
|
937
|
+
commands: z18.array(z18.string()),
|
|
938
|
+
added: z18.array(z18.string()),
|
|
939
|
+
removed: z18.array(z18.string()),
|
|
940
|
+
output: z18.string().optional(),
|
|
941
|
+
error: z18.string().optional(),
|
|
914
942
|
/** The links ensured between the seats; `linksUnsupported`: this OpenRig cannot add links to a running rig (before 0.7). */
|
|
915
|
-
links:
|
|
916
|
-
linksUnsupported:
|
|
943
|
+
links: z18.number().int().optional(),
|
|
944
|
+
linksUnsupported: z18.boolean().optional()
|
|
917
945
|
});
|
|
918
946
|
|
|
947
|
+
// ../protocol/src/openrigRoles.ts
|
|
948
|
+
import { z as z19 } from "zod";
|
|
949
|
+
var RoleUnknownWhySchema = z19.enum(["resolved_at_launch", "not_exposed", "unreadable", "ambiguous", "not_found", "missing_from_guidance"]);
|
|
950
|
+
var RoleFromSchema = z19.enum(["import", "defaults", "profile", "rig", "pod", "member", "launch", "openrig", "plugin", "seat"]);
|
|
951
|
+
var OriginSchema = z19.discriminatedUnion("kind", [
|
|
952
|
+
z19.object({ kind: z19.literal("here"), key: z19.string().optional() }),
|
|
953
|
+
z19.object({ kind: z19.literal("inherited"), from: RoleFromSchema, ref: z19.string().optional(), key: z19.string().optional(), path: z19.string().optional() }),
|
|
954
|
+
z19.object({ kind: z19.literal("absent") }),
|
|
955
|
+
z19.object({ kind: z19.literal("unknown"), why: RoleUnknownWhySchema, detail: z19.string().optional() })
|
|
956
|
+
]);
|
|
957
|
+
var SourcedStringSchema = z19.object({ value: z19.string().nullable(), origin: OriginSchema });
|
|
958
|
+
var RoleResourceCategorySchema = z19.enum(["skill", "guidance", "plugin", "subagent", "runtime_resource"]);
|
|
959
|
+
var RoleResourceSchema = z19.object({
|
|
960
|
+
category: RoleResourceCategorySchema,
|
|
961
|
+
id: z19.string(),
|
|
962
|
+
path: z19.string().optional(),
|
|
963
|
+
absolutePath: z19.string().optional(),
|
|
964
|
+
runtime: z19.string().optional(),
|
|
965
|
+
type: z19.string().optional(),
|
|
966
|
+
target: z19.string().optional(),
|
|
967
|
+
plugin: z19.object({ version: z19.string().optional(), skills: z19.array(z19.string()), hooks: z19.number().int().min(0) }).optional(),
|
|
968
|
+
origin: OriginSchema
|
|
969
|
+
});
|
|
970
|
+
var RoleUsesListSchema = z19.object({ declared: z19.boolean(), key: z19.string(), items: z19.array(RoleResourceSchema) });
|
|
971
|
+
var StartupLayerSchema = z19.enum(["agent", "profile", "culture", "rig", "pod", "member", "openrig", "seat", "unknown"]);
|
|
972
|
+
var StartupEntrySchema = z19.object({
|
|
973
|
+
kind: z19.enum(["file", "action"]),
|
|
974
|
+
path: z19.string().optional(),
|
|
975
|
+
absolutePath: z19.string().optional(),
|
|
976
|
+
size: z19.number().int().min(0).optional(),
|
|
977
|
+
value: z19.string().optional(),
|
|
978
|
+
builtin: z19.string().optional(),
|
|
979
|
+
layer: StartupLayerSchema,
|
|
980
|
+
layerInferred: z19.boolean(),
|
|
981
|
+
delivery: z19.string(),
|
|
982
|
+
into: z19.string().optional(),
|
|
983
|
+
delivered: z19.boolean().optional(),
|
|
984
|
+
required: z19.boolean().optional(),
|
|
985
|
+
orientation: z19.string().optional(),
|
|
986
|
+
appliesOn: z19.array(z19.string()),
|
|
987
|
+
origin: OriginSchema
|
|
988
|
+
});
|
|
989
|
+
var RoleFileErrorSchema = z19.object({ kind: z19.enum(["missing", "invalid"]), message: z19.string(), line: z19.number().int().optional(), col: z19.number().int().optional() });
|
|
990
|
+
var RoleSummarySchema = z19.object({
|
|
991
|
+
id: z19.string(),
|
|
992
|
+
name: z19.string(),
|
|
993
|
+
version: z19.string().optional(),
|
|
994
|
+
description: z19.string().optional(),
|
|
995
|
+
source: z19.enum(["builtin", "user"]),
|
|
996
|
+
path: z19.string(),
|
|
997
|
+
runtime: z19.string().optional(),
|
|
998
|
+
importOnly: z19.boolean(),
|
|
999
|
+
seats: z19.number().int().min(0),
|
|
1000
|
+
running: z19.number().int().min(0),
|
|
1001
|
+
skills: z19.array(z19.string()),
|
|
1002
|
+
startupFiles: z19.array(z19.string()),
|
|
1003
|
+
/** The seats that run it, so a seat of the Rigs page opens its role « seen from » it (`openrig.role` with `id`, then the seat by `rig` + `logicalId`). */
|
|
1004
|
+
seatRefs: z19.array(z19.object({ rig: z19.string(), logicalId: z19.string(), session: z19.string().optional() })),
|
|
1005
|
+
error: z19.enum(["missing", "invalid"]).optional()
|
|
1006
|
+
});
|
|
1007
|
+
var RoleImportSchema = z19.object({
|
|
1008
|
+
ref: z19.string(),
|
|
1009
|
+
key: z19.string(),
|
|
1010
|
+
name: z19.string().optional(),
|
|
1011
|
+
path: z19.string().optional(),
|
|
1012
|
+
version: z19.string().optional(),
|
|
1013
|
+
resolved: z19.boolean(),
|
|
1014
|
+
error: z19.string().optional()
|
|
1015
|
+
});
|
|
1016
|
+
var RoleProfileSchema = z19.object({
|
|
1017
|
+
name: z19.string(),
|
|
1018
|
+
description: z19.string().optional(),
|
|
1019
|
+
runtime: SourcedStringSchema,
|
|
1020
|
+
model: SourcedStringSchema,
|
|
1021
|
+
effort: SourcedStringSchema,
|
|
1022
|
+
uses: z19.object({ skills: RoleUsesListSchema, guidance: RoleUsesListSchema, subagents: RoleUsesListSchema, plugins: RoleUsesListSchema, runtimeResources: RoleUsesListSchema }),
|
|
1023
|
+
/** The role's share of the startup: agent then profile files and actions, in that order. */
|
|
1024
|
+
startup: z19.array(StartupEntrySchema)
|
|
1025
|
+
});
|
|
1026
|
+
var RoleSeatSchema = z19.object({
|
|
1027
|
+
rig: z19.string(),
|
|
1028
|
+
rigId: z19.string(),
|
|
1029
|
+
rigSpecPath: z19.string().optional(),
|
|
1030
|
+
composed: z19.boolean(),
|
|
1031
|
+
logicalId: z19.string(),
|
|
1032
|
+
session: z19.string().optional(),
|
|
1033
|
+
label: z19.string().optional(),
|
|
1034
|
+
profile: z19.string(),
|
|
1035
|
+
running: z19.boolean(),
|
|
1036
|
+
activity: z19.string(),
|
|
1037
|
+
runtime: SourcedStringSchema,
|
|
1038
|
+
model: SourcedStringSchema,
|
|
1039
|
+
effort: SourcedStringSchema,
|
|
1040
|
+
cwd: SourcedStringSchema,
|
|
1041
|
+
cultureFile: SourcedStringSchema,
|
|
1042
|
+
member: z19.string().optional(),
|
|
1043
|
+
memberStartup: z19.array(StartupEntrySchema),
|
|
1044
|
+
/** The Role tab of this seat: its role guidance (`role.md`), then the startup files of its own (member layer), in delivery
|
|
1045
|
+
* order — OpenRig's when the seat was launched, else the spec's. */
|
|
1046
|
+
roleDocs: z19.array(StartupEntrySchema),
|
|
1047
|
+
live: z19.object({
|
|
1048
|
+
resolvedSpec: z19.object({ name: z19.string().optional(), version: z19.string().optional(), hash: z19.string().optional() }),
|
|
1049
|
+
resources: z19.array(RoleResourceSchema),
|
|
1050
|
+
/** In the order OpenRig returns them. */
|
|
1051
|
+
startup: z19.array(StartupEntrySchema),
|
|
1052
|
+
/** `rig skill loadout --seat`: the skills projected for this seat; absent when it could not be read. */
|
|
1053
|
+
skills: z19.array(z19.object({ id: z19.string(), trigger: z19.string().optional() })).optional()
|
|
1054
|
+
}).optional()
|
|
1055
|
+
});
|
|
1056
|
+
var RoleUnavailableSchema = z19.enum(["harness_prompt", "effective_settings", "delivered_content", "harness_own_files"]);
|
|
1057
|
+
var RoleDetailSchema = z19.object({
|
|
1058
|
+
summary: RoleSummarySchema,
|
|
1059
|
+
file: z19.object({
|
|
1060
|
+
path: z19.string(),
|
|
1061
|
+
relativePath: z19.string().optional(),
|
|
1062
|
+
modifiedAt: z19.string().optional(),
|
|
1063
|
+
copies: z19.array(z19.object({ kind: z19.literal("repo"), path: z19.string(), identical: z19.boolean(), modifiedAt: z19.string().optional() }))
|
|
1064
|
+
}),
|
|
1065
|
+
error: RoleFileErrorSchema.optional(),
|
|
1066
|
+
role: z19.object({ path: z19.string(), absolutePath: z19.string(), origin: OriginSchema }).nullable(),
|
|
1067
|
+
defaults: z19.object({ runtime: SourcedStringSchema, model: SourcedStringSchema, effort: SourcedStringSchema }),
|
|
1068
|
+
imports: z19.array(RoleImportSchema),
|
|
1069
|
+
importedBy: z19.array(z19.string()),
|
|
1070
|
+
profiles: z19.array(RoleProfileSchema),
|
|
1071
|
+
/** What the file declares in `resources`. */
|
|
1072
|
+
resources: z19.array(RoleResourceSchema),
|
|
1073
|
+
seats: z19.array(RoleSeatSchema),
|
|
1074
|
+
unavailable: z19.array(RoleUnavailableSchema),
|
|
1075
|
+
warnings: z19.array(z19.string()),
|
|
1076
|
+
openrigVersion: z19.string().optional()
|
|
1077
|
+
});
|
|
1078
|
+
var RoleErrorSchema = z19.string();
|
|
1079
|
+
var LibraryIdSchema = z19.string().min(1).max(200).regex(/^[A-Za-z0-9][A-Za-z0-9._:-]*$/, "not a library id");
|
|
1080
|
+
var OpenRigRolesSchema = z19.object({ type: z19.literal("openrig.roles"), requestId: z19.string().min(1).max(100), machineId: z19.string().optional() });
|
|
1081
|
+
var OpenRigRoleListSchema = z19.object({
|
|
1082
|
+
type: z19.literal("openrig.roleList"),
|
|
1083
|
+
requestId: z19.string(),
|
|
1084
|
+
machineId: z19.string().optional(),
|
|
1085
|
+
roles: z19.array(RoleSummarySchema),
|
|
1086
|
+
openrigVersion: z19.string().optional(),
|
|
1087
|
+
error: RoleErrorSchema.optional()
|
|
1088
|
+
});
|
|
1089
|
+
var OpenRigRoleSchema = z19.object({ type: z19.literal("openrig.role"), requestId: z19.string().min(1).max(100), machineId: z19.string().optional(), roleId: LibraryIdSchema });
|
|
1090
|
+
var OpenRigRoleDetailSchema = z19.object({
|
|
1091
|
+
type: z19.literal("openrig.roleDetail"),
|
|
1092
|
+
requestId: z19.string(),
|
|
1093
|
+
machineId: z19.string().optional(),
|
|
1094
|
+
roleId: z19.string(),
|
|
1095
|
+
role: RoleDetailSchema.optional(),
|
|
1096
|
+
error: RoleErrorSchema.optional()
|
|
1097
|
+
});
|
|
1098
|
+
var OpenRigRoleFileSchema = z19.object({
|
|
1099
|
+
type: z19.literal("openrig.roleFile"),
|
|
1100
|
+
requestId: z19.string().min(1).max(100),
|
|
1101
|
+
machineId: z19.string().optional(),
|
|
1102
|
+
roleId: LibraryIdSchema,
|
|
1103
|
+
path: z19.string().min(1).max(4096)
|
|
1104
|
+
});
|
|
1105
|
+
var OpenRigRoleFileContentSchema = z19.object({
|
|
1106
|
+
type: z19.literal("openrig.roleFileContent"),
|
|
1107
|
+
requestId: z19.string(),
|
|
1108
|
+
machineId: z19.string().optional(),
|
|
1109
|
+
roleId: z19.string(),
|
|
1110
|
+
path: z19.string(),
|
|
1111
|
+
content: z19.string().optional(),
|
|
1112
|
+
size: z19.number().int().min(0).optional(),
|
|
1113
|
+
truncated: z19.boolean().optional(),
|
|
1114
|
+
error: RoleErrorSchema.optional()
|
|
1115
|
+
});
|
|
1116
|
+
var ROLE_FILE_MAX = 512 * 1024;
|
|
1117
|
+
|
|
919
1118
|
// ../protocol/src/routing.ts
|
|
920
|
-
import { z as
|
|
921
|
-
var MachineInventorySchema =
|
|
922
|
-
id:
|
|
923
|
-
label:
|
|
924
|
-
online:
|
|
925
|
-
local:
|
|
926
|
-
sessions:
|
|
927
|
-
folders:
|
|
928
|
-
});
|
|
929
|
-
var RoutingContextSchema =
|
|
930
|
-
machines:
|
|
931
|
-
history:
|
|
932
|
-
});
|
|
933
|
-
var RouteResultSchema =
|
|
1119
|
+
import { z as z20 } from "zod";
|
|
1120
|
+
var MachineInventorySchema = z20.object({
|
|
1121
|
+
id: z20.string().min(1).max(80),
|
|
1122
|
+
label: z20.string().min(1).max(120),
|
|
1123
|
+
online: z20.boolean(),
|
|
1124
|
+
local: z20.boolean(),
|
|
1125
|
+
sessions: z20.array(z20.object({ id: z20.string(), name: z20.string(), project: z20.string(), cwd: z20.string(), topic: z20.string().optional(), state: z20.string(), readOnly: z20.boolean() })).max(500),
|
|
1126
|
+
folders: z20.array(z20.object({ project: z20.string(), cwd: z20.string() })).max(100)
|
|
1127
|
+
});
|
|
1128
|
+
var RoutingContextSchema = z20.object({
|
|
1129
|
+
machines: z20.array(MachineInventorySchema).min(1).max(20),
|
|
1130
|
+
history: z20.array(z20.object({ question: z20.string().max(2e4), answer: z20.string().max(2e4).optional(), machineId: z20.string().optional() })).max(50)
|
|
1131
|
+
});
|
|
1132
|
+
var RouteResultSchema = z20.object({
|
|
934
1133
|
/** Null means an answer or a clarification, never an implicit local destination. */
|
|
935
|
-
machineId:
|
|
936
|
-
text:
|
|
937
|
-
choices:
|
|
1134
|
+
machineId: z20.string().nullable(),
|
|
1135
|
+
text: z20.string().min(1).max(2e4),
|
|
1136
|
+
choices: z20.array(z20.string()).max(20).optional()
|
|
938
1137
|
});
|
|
939
1138
|
|
|
940
1139
|
// ../protocol/src/thread.ts
|
|
941
|
-
import { z as
|
|
942
|
-
var ToolStatusSchema =
|
|
943
|
-
var FileChangeSchema =
|
|
944
|
-
var base = { id:
|
|
945
|
-
var ThreadItemSchema =
|
|
946
|
-
|
|
947
|
-
|
|
1140
|
+
import { z as z21 } from "zod";
|
|
1141
|
+
var ToolStatusSchema = z21.enum(["running", "success", "failed", "unknown"]);
|
|
1142
|
+
var FileChangeSchema = z21.object({ path: z21.string().min(1), from: z21.string().min(1).optional(), change: z21.enum(["add", "edit", "delete", "write"]), diff: z21.string().optional() });
|
|
1143
|
+
var base = { id: z21.string().min(1), at: z21.string().datetime(), turn: z21.number().int().nonnegative(), helperId: z21.string().optional() };
|
|
1144
|
+
var ThreadItemSchema = z21.discriminatedUnion("kind", [
|
|
1145
|
+
z21.object({ kind: z21.literal("prompt"), ...base, text: z21.string(), images: z21.array(AttachmentRefSchema).optional() }),
|
|
1146
|
+
z21.object({ kind: z21.literal("message"), ...base, text: z21.string() }),
|
|
948
1147
|
/** `id` is the harness's tool_use_id when it gives one, so the result updates the same row; `byClose`: a question
|
|
949
1148
|
* dismissed by Ledge closing the session. */
|
|
950
|
-
|
|
951
|
-
kind:
|
|
1149
|
+
z21.object({
|
|
1150
|
+
kind: z21.literal("tool"),
|
|
952
1151
|
...base,
|
|
953
|
-
tool:
|
|
954
|
-
label:
|
|
955
|
-
input:
|
|
956
|
-
output:
|
|
957
|
-
error:
|
|
1152
|
+
tool: z21.string(),
|
|
1153
|
+
label: z21.string(),
|
|
1154
|
+
input: z21.string().optional(),
|
|
1155
|
+
output: z21.string().optional(),
|
|
1156
|
+
error: z21.string().optional(),
|
|
958
1157
|
status: ToolStatusSchema,
|
|
959
|
-
durationMs:
|
|
960
|
-
files:
|
|
961
|
-
byClose:
|
|
1158
|
+
durationMs: z21.number().nonnegative().optional(),
|
|
1159
|
+
files: z21.array(FileChangeSchema).default([]),
|
|
1160
|
+
byClose: z21.literal(true).optional(),
|
|
962
1161
|
/** Images the tool returned (a browser screenshot), in order; `imagesDropped`: some were not kept. */
|
|
963
|
-
images:
|
|
964
|
-
imagesDropped:
|
|
1162
|
+
images: z21.array(AttachmentRefSchema).max(ATTACHMENTS_MAX).optional(),
|
|
1163
|
+
imagesDropped: z21.literal(true).optional()
|
|
965
1164
|
}),
|
|
966
1165
|
/** `unknown`: answered in the terminal, the daemon only saw the prompt go away; `always`: allowed and not asked again;
|
|
967
1166
|
* `byClose`: denied by Ledge closing the session. */
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
1167
|
+
z21.object({ kind: z21.literal("permission"), ...base, tool: z21.string(), text: z21.string(), decision: z21.enum(["accept", "deny", "always", "unknown"]).optional(), byClose: z21.literal(true).optional() }),
|
|
1168
|
+
z21.object({ kind: z21.literal("reply"), ...base, text: z21.string() }),
|
|
1169
|
+
z21.object({ kind: z21.literal("helper"), ...base, task: z21.string(), agentType: z21.string().optional(), state: z21.enum(["working", "exited"]) }),
|
|
971
1170
|
/** A message this session sent to another agent; `id` is the tool_use_id, `to` the address it used, `peer` the Ledge session id, `received` the paired item's id in the peer's thread, `via` set when Ledge's mailbox carries it. */
|
|
972
|
-
|
|
1171
|
+
z21.object({ kind: z21.literal("sent"), ...base, to: z21.string(), peer: z21.string().optional(), summary: z21.string().optional(), text: z21.string(), status: z21.enum(["sending", "delivered", "read", "failed"]), received: z21.string().optional(), via: z21.literal("ledge").optional() }),
|
|
973
1172
|
/** A message another agent sent to this session; `from` is its from-name (else its address), `sent` the paired item's id in the peer's thread. */
|
|
974
|
-
|
|
1173
|
+
z21.object({ kind: z21.literal("received"), ...base, from: z21.string(), peer: z21.string().optional(), text: z21.string(), sent: z21.string().optional() }),
|
|
975
1174
|
/** An automated notice of the agent's harness about messages between sessions (Claude Code's `[Cross-session idle
|
|
976
1175
|
* notice]` / `[Cross-session delivery notice]`): a system event, never the user's words. `name` is the session it is
|
|
977
1176
|
* about as quoted, `peer` its Ledge session id; a delivery notice says the outcome of `count` messages to `recipients`. */
|
|
978
|
-
|
|
979
|
-
kind:
|
|
1177
|
+
z21.object({
|
|
1178
|
+
kind: z21.literal("notice"),
|
|
980
1179
|
...base,
|
|
981
|
-
notice:
|
|
1180
|
+
notice: z21.enum(["idle", "exited", "unavailable", "expired", "delivery", "other"]),
|
|
982
1181
|
/** An `other` notice from Ledge: `task`, the task a `<ledge-task>` is about. An older front, which does not know it, shows it as any other notice. */
|
|
983
|
-
task:
|
|
1182
|
+
task: z21.string().optional(),
|
|
984
1183
|
/** A task this session handed over (`outgoing`), and how (issue #92). */
|
|
985
|
-
taskOp:
|
|
986
|
-
outgoing:
|
|
987
|
-
name:
|
|
988
|
-
peer:
|
|
989
|
-
time:
|
|
990
|
-
summary:
|
|
991
|
-
status:
|
|
992
|
-
count:
|
|
993
|
-
recipients:
|
|
994
|
-
reason:
|
|
995
|
-
text:
|
|
1184
|
+
taskOp: z21.enum(TRANSMISSION_OPS).optional(),
|
|
1185
|
+
outgoing: z21.literal(true).optional(),
|
|
1186
|
+
name: z21.string().optional(),
|
|
1187
|
+
peer: z21.string().optional(),
|
|
1188
|
+
time: z21.string().optional(),
|
|
1189
|
+
summary: z21.string().optional(),
|
|
1190
|
+
status: z21.enum(["held", "denied", "expired", "delivered", "refused", "dropped", "unknown"]).optional(),
|
|
1191
|
+
count: z21.number().int().positive().optional(),
|
|
1192
|
+
recipients: z21.array(z21.string()).optional(),
|
|
1193
|
+
reason: z21.string().optional(),
|
|
1194
|
+
text: z21.string()
|
|
996
1195
|
})
|
|
997
1196
|
]);
|
|
998
1197
|
|
|
999
1198
|
// ../protocol/src/mobile.ts
|
|
1000
|
-
import { z as
|
|
1001
|
-
var RelayUrlSchema =
|
|
1199
|
+
import { z as z22 } from "zod";
|
|
1200
|
+
var RelayUrlSchema = z22.string().transform((v, ctx) => {
|
|
1002
1201
|
const bad = () => {
|
|
1003
|
-
ctx.addIssue({ code:
|
|
1004
|
-
return
|
|
1202
|
+
ctx.addIssue({ code: z22.ZodIssueCode.custom, message: "invalid relay URL" });
|
|
1203
|
+
return z22.NEVER;
|
|
1005
1204
|
};
|
|
1006
1205
|
let u;
|
|
1007
1206
|
try {
|
|
@@ -1014,114 +1213,122 @@ var RelayUrlSchema = z20.string().transform((v, ctx) => {
|
|
|
1014
1213
|
if (u.username || u.password || u.pathname !== "/" || u.search || u.hash) return bad();
|
|
1015
1214
|
return u.origin;
|
|
1016
1215
|
});
|
|
1017
|
-
var MobileConfigSchema =
|
|
1018
|
-
var MobileLinkStateSchema =
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1216
|
+
var MobileConfigSchema = z22.object({ enabled: z22.boolean(), relayUrl: RelayUrlSchema, terminalInput: z22.boolean().optional() });
|
|
1217
|
+
var MobileLinkStateSchema = z22.discriminatedUnion("state", [
|
|
1218
|
+
z22.object({ state: z22.literal("off") }),
|
|
1219
|
+
z22.object({ state: z22.literal("connecting") }),
|
|
1220
|
+
z22.object({ state: z22.literal("online") }),
|
|
1221
|
+
z22.object({ state: z22.literal("offline"), retryAt: z22.string().datetime(), error: z22.string() }),
|
|
1222
|
+
z22.object({ state: z22.literal("signed_out"), reason: z22.enum(["other_pc", "session_invalid"]) }),
|
|
1024
1223
|
/** The mobile service did not start (a broken mobile.json, a missing key store): nothing is linked until the daemon restarts. */
|
|
1025
|
-
|
|
1224
|
+
z22.object({ state: z22.literal("unavailable"), error: z22.string().max(200) })
|
|
1026
1225
|
]);
|
|
1027
|
-
var MobilePairingSchema =
|
|
1028
|
-
id:
|
|
1029
|
-
label:
|
|
1030
|
-
code:
|
|
1031
|
-
expiresAt:
|
|
1032
|
-
state:
|
|
1033
|
-
});
|
|
1034
|
-
var MobileStateSchema =
|
|
1035
|
-
enabled:
|
|
1036
|
-
relayUrl:
|
|
1037
|
-
account:
|
|
1038
|
-
signin:
|
|
1226
|
+
var MobilePairingSchema = z22.object({
|
|
1227
|
+
id: z22.string(),
|
|
1228
|
+
label: z22.string(),
|
|
1229
|
+
code: z22.string().nullable(),
|
|
1230
|
+
expiresAt: z22.string().datetime(),
|
|
1231
|
+
state: z22.enum(["waiting", "confirm", "approved", "linked", "expired", "refused"])
|
|
1232
|
+
});
|
|
1233
|
+
var MobileStateSchema = z22.object({
|
|
1234
|
+
enabled: z22.boolean(),
|
|
1235
|
+
relayUrl: z22.string(),
|
|
1236
|
+
account: z22.object({ login: z22.string(), accountId: z22.string() }).nullable(),
|
|
1237
|
+
signin: z22.object({ userCode: z22.string(), verificationUri: z22.string(), expiresAt: z22.string().datetime() }).nullable(),
|
|
1039
1238
|
/** The last sign-in that did not end with a session; cleared by the next one. */
|
|
1040
|
-
signinError:
|
|
1239
|
+
signinError: z22.enum(["expired", "denied", "not_allowed", "failed"]).nullable(),
|
|
1041
1240
|
link: MobileLinkStateSchema,
|
|
1042
|
-
devices:
|
|
1043
|
-
pairings:
|
|
1241
|
+
devices: z22.array(z22.object({ id: z22.string(), label: z22.string(), approvedAt: z22.string(), online: z22.boolean(), lastSeenAt: z22.string().optional() })),
|
|
1242
|
+
pairings: z22.array(MobilePairingSchema),
|
|
1243
|
+
/** « Taper dans les terminaux depuis le téléphone »: absent from an older daemon, which has no phone terminal. */
|
|
1244
|
+
terminalInput: z22.boolean().optional()
|
|
1044
1245
|
});
|
|
1045
1246
|
|
|
1046
1247
|
// ../protocol/src/messages.ts
|
|
1047
|
-
var RefusalCodeSchema =
|
|
1048
|
-
var SessionEventSchema =
|
|
1049
|
-
name:
|
|
1050
|
-
at:
|
|
1051
|
-
source:
|
|
1052
|
-
tool:
|
|
1053
|
-
text:
|
|
1054
|
-
options:
|
|
1248
|
+
var RefusalCodeSchema = z23.enum(["stale", "read_only", "no_prompt", "not_taken", "busy", "unknown_session", "nothing_to_decide", "not_offered", "free_text", "note_not_typed", "unavailable", "invalid", "attachment_missing", "attachments_not_accepted"]);
|
|
1249
|
+
var SessionEventSchema = z23.object({
|
|
1250
|
+
name: z23.enum(["SessionStart", "UserPromptSubmit", "PreToolUse", "PermissionRequest", "Notification", "Stop", "SessionEnd", "PaneChanged", "PaneGone", "PanePrompt", "PaneIdle", "InjectFailed", "Decided", "PostToolUse", "PostToolUseFailure", "StopFailure", "SubagentStart", "SubagentStop", "PaneTitle", "Renamed", "NativeRequest", "AgentMessage", "QueueUpdate"]),
|
|
1251
|
+
at: z23.string().datetime(),
|
|
1252
|
+
source: z23.enum(["hook", "pane", "daemon"]),
|
|
1253
|
+
tool: z23.string().optional(),
|
|
1254
|
+
text: z23.string().optional(),
|
|
1255
|
+
options: z23.array(z23.string()).optional(),
|
|
1055
1256
|
/** Each option's description, by index ('' when it has none); left out when no option has one. */
|
|
1056
|
-
optionDetails:
|
|
1057
|
-
kind:
|
|
1257
|
+
optionDetails: z23.array(z23.string()).optional(),
|
|
1258
|
+
kind: z23.enum(["question", "permission", "plan", "diff"]).optional(),
|
|
1058
1259
|
/** PanePrompt: what to review in full before deciding (a plan read in the terminal), when `text` is its summary. */
|
|
1059
|
-
body:
|
|
1060
|
-
exitCode:
|
|
1061
|
-
helperId:
|
|
1260
|
+
body: z23.string().optional(),
|
|
1261
|
+
exitCode: z23.number().int().nullable().optional(),
|
|
1262
|
+
helperId: z23.string().optional(),
|
|
1062
1263
|
/** The helper's description, once known (Claude Code: its `.meta.json`, written after SubagentStart). */
|
|
1063
|
-
task:
|
|
1264
|
+
task: z23.string().optional(),
|
|
1064
1265
|
/** PostToolUseFailure: the user interrupted the turn (Esc); Claude then waits for input with no Stop. */
|
|
1065
|
-
interrupt:
|
|
1266
|
+
interrupt: z23.boolean().optional(),
|
|
1066
1267
|
/** UserPromptSubmit, PostToolUse: the images the prompt or the tool result carried, by attachment id. */
|
|
1067
|
-
attachments:
|
|
1268
|
+
attachments: z23.array(AttachmentIdSchema).max(ATTACHMENTS_MAX).optional(),
|
|
1068
1269
|
/** PostToolUse: some of the result's images were not kept. */
|
|
1069
|
-
imagesDropped:
|
|
1270
|
+
imagesDropped: z23.boolean().optional(),
|
|
1070
1271
|
/** QueueUpdate: OMP's queue as it stands. */
|
|
1071
|
-
queue:
|
|
1272
|
+
queue: z23.array(z23.object({ text: z23.string(), mode: z23.enum(["steer", "follow_up"]) })).optional(),
|
|
1072
1273
|
/** AgentMessage: the harness's own message, its text so far (`streaming`) or whole. `messageId` is unique for the session's life. */
|
|
1073
|
-
messageId:
|
|
1074
|
-
streaming:
|
|
1274
|
+
messageId: z23.string().optional(),
|
|
1275
|
+
streaming: z23.boolean().optional(),
|
|
1075
1276
|
/** NativeRequest: the dialog's id, and OMP's `ask` questions when it is one. */
|
|
1076
|
-
requestId:
|
|
1077
|
-
questions:
|
|
1277
|
+
requestId: z23.string().optional(),
|
|
1278
|
+
questions: z23.array(AskQuestionSchema).optional(),
|
|
1078
1279
|
/** Stop: its text is already in the thread as AgentMessage cards, which the reply card must not repeat. */
|
|
1079
|
-
streamed:
|
|
1280
|
+
streamed: z23.boolean().optional(),
|
|
1080
1281
|
/** Native tracking (spec 2026-10-13 §2): the harness's id of the tool call, so its result lands on the same row. */
|
|
1081
|
-
toolUseId:
|
|
1282
|
+
toolUseId: z23.string().optional(),
|
|
1082
1283
|
/** The tool's input as the harness gave it, every string capped by the hook. */
|
|
1083
|
-
toolInput:
|
|
1284
|
+
toolInput: z23.record(z23.string(), z23.unknown()).optional(),
|
|
1084
1285
|
/** PostToolUse: the result as text; SubagentStop: nothing (the report travels in `text`). */
|
|
1085
|
-
output:
|
|
1086
|
-
durationMs:
|
|
1286
|
+
output: z23.string().optional(),
|
|
1287
|
+
durationMs: z23.number().nonnegative().optional(),
|
|
1087
1288
|
/** PostToolUseFailure: the harness's error message. */
|
|
1088
|
-
error:
|
|
1289
|
+
error: z23.string().optional(),
|
|
1089
1290
|
/** The session's transcript, or the helper's on SubagentStop; the daemon reads its tail for the agent's messages. */
|
|
1090
|
-
transcriptPath:
|
|
1291
|
+
transcriptPath: z23.string().optional(),
|
|
1091
1292
|
/** Decided: what was chosen when the daemon knows it. */
|
|
1092
1293
|
decision: DecisionSchema.optional(),
|
|
1093
1294
|
/** Decided: the request was refused by Ledge closing the session (its interrupt keys), not by the user's answer. */
|
|
1094
|
-
byClose:
|
|
1295
|
+
byClose: z23.literal(true).optional(),
|
|
1095
1296
|
/** The agent's own session id when the hook names it, so the daemon can resume that conversation. */
|
|
1096
|
-
agentSessionId:
|
|
1297
|
+
agentSessionId: z23.string().optional()
|
|
1097
1298
|
});
|
|
1098
|
-
var RecentSchema =
|
|
1299
|
+
var RecentSchema = z23.object({ cwd: z23.string().min(1), project: z23.string().min(1), lastAt: z23.string().datetime() });
|
|
1099
1300
|
var SEMVER = /^\d+\.\d+\.\d+$/;
|
|
1100
|
-
var DaemonInfoSchema =
|
|
1101
|
-
version:
|
|
1102
|
-
install:
|
|
1103
|
-
systemd:
|
|
1104
|
-
manual:
|
|
1105
|
-
routing:
|
|
1106
|
-
archive:
|
|
1301
|
+
var DaemonInfoSchema = z23.object({
|
|
1302
|
+
version: z23.string(),
|
|
1303
|
+
install: z23.enum(["npm", "git", "unknown"]),
|
|
1304
|
+
systemd: z23.boolean(),
|
|
1305
|
+
manual: z23.string().optional(),
|
|
1306
|
+
routing: z23.boolean().optional(),
|
|
1307
|
+
archive: z23.boolean().optional(),
|
|
1107
1308
|
/** Takes `model` and `effort` on `session.launch` and answers `models.list` (#58). */
|
|
1108
|
-
models:
|
|
1309
|
+
models: z23.boolean().optional(),
|
|
1109
1310
|
/** Drives OMP over its RPC: launching `omp` sessions is possible when the binary is there (`models.list` says so). */
|
|
1110
|
-
omp:
|
|
1311
|
+
omp: z23.boolean().optional(),
|
|
1111
1312
|
/** Takes `session.model`: a live session's model and effort changed (#58, lot 2). */
|
|
1112
|
-
modelSwitch:
|
|
1313
|
+
modelSwitch: z23.boolean().optional(),
|
|
1314
|
+
/** Answers `session.commands`: the slash commands a session's agent offers. */
|
|
1315
|
+
commands: z23.boolean().optional(),
|
|
1113
1316
|
/** Takes `approval` on an OMP `session.launch` (auto mode). */
|
|
1114
|
-
ompApproval:
|
|
1317
|
+
ompApproval: z23.boolean().optional(),
|
|
1115
1318
|
/** Its home folder, so a client shows the folders under it as `~` (absent from older daemons). */
|
|
1116
|
-
home:
|
|
1117
|
-
mobile:
|
|
1319
|
+
home: z23.string().optional(),
|
|
1320
|
+
mobile: z23.boolean().optional(),
|
|
1118
1321
|
/** Answers `openrig.status` and `openrig.act`: drives OpenRig on its machine. */
|
|
1119
|
-
openrig:
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1322
|
+
openrig: z23.boolean().optional(),
|
|
1323
|
+
/** Answers `openrig.roles`, `openrig.role` and `openrig.roleFile`: reads OpenRig's roles (agent specs). */
|
|
1324
|
+
openrigRoles: z23.boolean().optional(),
|
|
1325
|
+
/** Takes `session.broadcast` (its `ledge bridge` too, on this machine): a client sends it only when this is set. */
|
|
1326
|
+
broadcast: z23.boolean().optional()
|
|
1327
|
+
});
|
|
1328
|
+
var UpdateFailureSchema = z23.enum(["git-install", "no-systemd", "sessions-at-risk", "npm-failed", "busy"]);
|
|
1329
|
+
var ServerMessageSchema = z23.discriminatedUnion("type", [
|
|
1123
1330
|
SessionModelAcceptedSchema,
|
|
1124
|
-
|
|
1331
|
+
z23.object({ type: z23.literal("chat.routed"), qid: z23.string(), result: RouteResultSchema }),
|
|
1125
1332
|
DocumentContentSchema,
|
|
1126
1333
|
DocumentErrorSchema,
|
|
1127
1334
|
AttachmentStoredSchema,
|
|
@@ -1130,102 +1337,117 @@ var ServerMessageSchema = z21.discriminatedUnion("type", [
|
|
|
1130
1337
|
ReposInfoSchema,
|
|
1131
1338
|
ReposFoundSchema,
|
|
1132
1339
|
ModelsListingSchema,
|
|
1340
|
+
SessionCommandsListingSchema,
|
|
1133
1341
|
OpenRigStateMessageSchema,
|
|
1134
1342
|
OpenRigDoneSchema,
|
|
1135
1343
|
OpenRigSpecListSchema,
|
|
1136
1344
|
OpenRigLaunchedSchema,
|
|
1137
1345
|
OpenRigReseatedSchema,
|
|
1346
|
+
OpenRigRoleListSchema,
|
|
1347
|
+
OpenRigRoleDetailSchema,
|
|
1348
|
+
OpenRigRoleFileContentSchema,
|
|
1138
1349
|
/** `recents`: the folders of past sessions, most recent first. `launcher`: the command that runs `ledge` inside WSL
|
|
1139
1350
|
* (`[node, cli]`), so the overlay can attach a terminal to a session with `<launcher> attach <id>`. `daemon`: absent
|
|
1140
1351
|
* from daemons older than the auto-update. */
|
|
1141
|
-
|
|
1142
|
-
|
|
1352
|
+
z23.object({ type: z23.literal("snapshot"), sessions: z23.array(SessionSchema), recents: z23.array(RecentSchema).optional(), launcher: z23.array(z23.string()).optional(), daemon: DaemonInfoSchema.optional(), sshAlias: SshAliasSchema.optional() }),
|
|
1353
|
+
z23.object({ type: z23.literal("session.updated"), session: SessionSchema }),
|
|
1143
1354
|
/** OMP: the instructions taken back from its queue by an interrupt that restores it; the asker puts them back in its draft. */
|
|
1144
|
-
|
|
1145
|
-
type:
|
|
1146
|
-
id:
|
|
1147
|
-
texts:
|
|
1355
|
+
z23.object({
|
|
1356
|
+
type: z23.literal("session.queue.restored"),
|
|
1357
|
+
id: z23.string(),
|
|
1358
|
+
texts: z23.array(z23.string()),
|
|
1148
1359
|
/** The same instructions with the images they carried; `imagesDropped`: some images could not be put back. */
|
|
1149
|
-
entries:
|
|
1150
|
-
imagesDropped:
|
|
1360
|
+
entries: z23.array(z23.object({ text: z23.string(), attachments: z23.array(AttachmentIdSchema).optional() })).optional(),
|
|
1361
|
+
imagesDropped: z23.boolean().optional()
|
|
1151
1362
|
}),
|
|
1152
|
-
|
|
1153
|
-
|
|
1363
|
+
z23.object({ type: z23.literal("session.removed"), id: z23.string() }),
|
|
1364
|
+
z23.object({ type: z23.literal("session.preview"), id: z23.string(), lines: z23.array(z23.string()) }),
|
|
1154
1365
|
/** A session's thread, on request (spec 2026-10-13 §5); then each created or updated item, broadcast. */
|
|
1155
|
-
|
|
1156
|
-
|
|
1366
|
+
z23.object({ type: z23.literal("session.thread"), id: z23.string(), items: z23.array(ThreadItemSchema) }),
|
|
1367
|
+
z23.object({ type: z23.literal("session.thread.item"), id: z23.string(), item: ThreadItemSchema }),
|
|
1157
1368
|
/** Answer to the requester of a `session.launch`: the session exists (its `session.updated` is broadcast as usual). */
|
|
1158
|
-
|
|
1369
|
+
z23.object({ type: z23.literal("session.launched"), id: z23.string() }),
|
|
1159
1370
|
/** Answer to the requester of a `session.close`, once per row closed: the session `id` and how it ended. */
|
|
1160
|
-
|
|
1161
|
-
|
|
1371
|
+
z23.object({ type: z23.literal("session.closed"), qid: z23.string(), row: z23.number().int().min(0), id: z23.string(), how: z23.enum(["exit", "killed"]) }),
|
|
1372
|
+
z23.object({ type: z23.literal("recents"), recents: z23.array(RecentSchema) }),
|
|
1162
1373
|
/** Answer to the requester of a `folder.list` or `folder.create`: `path` resolved (`~` expanded), `parent` null at the
|
|
1163
1374
|
* root, `entries` its subfolders by name (hidden ones left out). A failure keeps the asked path and says why in `error`,
|
|
1164
1375
|
* never in an `error` message, which the launch form reads as a failed launch. */
|
|
1165
|
-
|
|
1166
|
-
type:
|
|
1167
|
-
path:
|
|
1168
|
-
parent:
|
|
1169
|
-
entries:
|
|
1170
|
-
error:
|
|
1376
|
+
z23.object({
|
|
1377
|
+
type: z23.literal("folder.listing"),
|
|
1378
|
+
path: z23.string(),
|
|
1379
|
+
parent: z23.string().nullable(),
|
|
1380
|
+
entries: z23.array(z23.string()),
|
|
1381
|
+
error: z23.string().optional(),
|
|
1171
1382
|
/** The request this answers, when it carried one. */
|
|
1172
|
-
requestId:
|
|
1383
|
+
requestId: z23.string().optional(),
|
|
1173
1384
|
/** Those `entries` that are Git repositories, with their branch (the short commit when HEAD is detached). */
|
|
1174
|
-
repos:
|
|
1385
|
+
repos: z23.array(z23.object({ name: z23.string(), branch: z23.string() })).optional()
|
|
1175
1386
|
}),
|
|
1176
1387
|
/** The chat (spec 2026-10-11 §5): what Ledge reads, its words as they come, the final text, or why it stopped; the day's turns after each snapshot. */
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1388
|
+
z23.object({ type: z23.literal("chat.step"), qid: z23.string(), step: ChatStepSchema }),
|
|
1389
|
+
z23.object({ type: z23.literal("chat.delta"), qid: z23.string(), text: z23.string() }),
|
|
1390
|
+
z23.object({ type: z23.literal("chat.done"), qid: z23.string(), text: z23.string(), at: z23.string().datetime(), proposal: ChatProposalSchema.optional(), proposals: ChatProposalsSchema.optional(), relay: ChatRelaySchema.optional(), closes: ChatClosesSchema.optional(), projectProposal: ChatProjectProposalSchema.optional(), tasks: ChatTasksSchema.optional(), actions: ChatActionsSchema.optional() }),
|
|
1180
1391
|
/** What a `chat.act` applied: the rows done, and the one that failed with why (the rows after it stay proposed). */
|
|
1181
|
-
|
|
1182
|
-
row:
|
|
1183
|
-
message:
|
|
1392
|
+
z23.object({ type: z23.literal("chat.acted"), rid: z23.string(), qid: z23.string(), done: z23.array(z23.number().int().min(0)), failed: z23.array(z23.object({
|
|
1393
|
+
row: z23.number().int().min(0),
|
|
1394
|
+
message: z23.string(),
|
|
1184
1395
|
/** nothing was sent */
|
|
1185
|
-
refused:
|
|
1396
|
+
refused: z23.literal(true).optional()
|
|
1186
1397
|
})) }),
|
|
1187
1398
|
/** What an `action.run` came to: `row` the action as the daemon resolved it (its summary, its risk); `reason` why it was not
|
|
1188
1399
|
* done, with `code` (`refused`: not possible now; `agent`: a session may not run it; `confirm_required`: destructive, ask
|
|
1189
1400
|
* again; `failed`: the handler turned it down; a free string, as an `error`'s). */
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1401
|
+
z23.object({ type: z23.literal("action.result"), rid: z23.string(), action: z23.string().min(1).max(40), row: ChatActionSchema.optional(), reason: z23.string().optional(), code: z23.string().optional() }),
|
|
1402
|
+
z23.object({ type: z23.literal("chat.error"), qid: z23.string(), message: z23.string() }),
|
|
1403
|
+
z23.object({ type: z23.literal("chat.history"), turns: z23.array(ChatTurnSchema) }),
|
|
1193
1404
|
/** The daemon updating itself: npm is running, then the service restarts (the socket closes right after). */
|
|
1194
|
-
|
|
1405
|
+
z23.object({ type: z23.literal("update.progress"), version: z23.string(), step: z23.enum(["installing", "restarting"]) }),
|
|
1195
1406
|
/** Nothing was changed. `detail`: the useful end of npm's output; `command`: what the person can run instead. */
|
|
1196
|
-
|
|
1407
|
+
z23.object({ type: z23.literal("update.failed"), version: z23.string(), reason: UpdateFailureSchema, detail: z23.string().optional(), command: z23.string().optional() }),
|
|
1197
1408
|
/** The shared machines list (spec 2026-10-14 §5), sent to local clients only. */
|
|
1198
|
-
|
|
1409
|
+
z23.object({ type: z23.literal("machines"), machines: MachinesSchema }),
|
|
1199
1410
|
/** The local daemon's projects and folder rules (spec 2026-10-16 build §5), sent to local clients only. */
|
|
1200
|
-
|
|
1411
|
+
z23.object({ type: z23.literal("projects"), projects: ProjectsSchema }),
|
|
1201
1412
|
/** The island's settings shared by the local fronts; null until a front sets them (the island then sends its own). Local clients only. */
|
|
1202
|
-
|
|
1413
|
+
z23.object({ type: z23.literal("island"), prefs: IslandPrefsSchema.nullable() }),
|
|
1203
1414
|
/** Mobile access (lot 3), after the island in each snapshot and on every change. Local clients only. */
|
|
1204
|
-
|
|
1415
|
+
z23.object({ type: z23.literal("mobile"), state: MobileStateSchema }),
|
|
1416
|
+
/** The sessions a phone types into (« Téléphone au clavier »), after each snapshot and each change; `device` is its label. */
|
|
1417
|
+
z23.object({ type: z23.literal("terminal.holders"), holds: z23.array(z23.object({ id: z23.string(), device: z23.string() })) }),
|
|
1205
1418
|
/** The daemon's immediate answer to a `session.message`: delivered (`accepted`), held for later (`queued`), left to Claude's own SendMessage (`native`), or refused with a `reason`. */
|
|
1206
|
-
|
|
1419
|
+
z23.object({
|
|
1420
|
+
type: z23.literal("message.ack"),
|
|
1421
|
+
rid: z23.string(),
|
|
1422
|
+
outcome: z23.enum(["accepted", "queued", "native", "refused"]),
|
|
1423
|
+
reason: z23.string().optional(),
|
|
1424
|
+
/** A broadcast's per-recipient outcome, by name. */
|
|
1425
|
+
recipients: z23.object({ sent: z23.array(z23.string()), refused: z23.array(z23.object({ name: z23.string(), reason: z23.string() })) }).optional()
|
|
1426
|
+
}),
|
|
1427
|
+
/** The answer to a `session.broadcast`: who got it (`sent`) and who did not; `reason`: the whole broadcast was refused. */
|
|
1428
|
+
z23.object({ type: z23.literal("session.broadcast.result"), rid: z23.string(), sent: z23.array(z23.object({ id: z23.string(), name: z23.string() })), refused: z23.array(z23.object({ name: z23.string(), reason: z23.string() })), reason: z23.string().optional() }),
|
|
1207
1429
|
/** The answer to a `message.read`: the body with its sender, target and time, or the refusal's `reason`. */
|
|
1208
|
-
|
|
1430
|
+
z23.object({ type: z23.literal("message.body"), id: z23.string(), from: z23.string().optional(), to: z23.string().optional(), at: z23.string().optional(), text: z23.string().optional(), reason: z23.string().optional() }),
|
|
1209
1431
|
TaskResultSchema,
|
|
1210
1432
|
SessionHandoverResultSchema,
|
|
1211
1433
|
ContextPrefsMessageSchema,
|
|
1212
1434
|
HandoverResultSchema,
|
|
1213
1435
|
TaskUpdatedSchema,
|
|
1214
|
-
|
|
1215
|
-
type:
|
|
1216
|
-
message:
|
|
1217
|
-
id:
|
|
1436
|
+
z23.object({
|
|
1437
|
+
type: z23.literal("error"),
|
|
1438
|
+
message: z23.string(),
|
|
1439
|
+
id: z23.string().optional(),
|
|
1218
1440
|
/** Le type du message client refusé. */
|
|
1219
|
-
request:
|
|
1441
|
+
request: z23.string().optional(),
|
|
1220
1442
|
/** Ce que le front doit reconnaître au-delà du texte (NOTE_NOT_TYPED) ; une chaîne libre, qu'un front plus ancien ignore. */
|
|
1221
|
-
code:
|
|
1443
|
+
code: z23.string().optional()
|
|
1222
1444
|
})
|
|
1223
1445
|
]);
|
|
1224
1446
|
var MESSAGE_MAX = 2e4;
|
|
1225
1447
|
var SUMMARY_MAX = 500;
|
|
1226
|
-
var launchRows =
|
|
1227
|
-
var ClientMessageSchema =
|
|
1228
|
-
|
|
1448
|
+
var launchRows = z23.array(z23.number().int().min(0).max(MAX_PROPOSALS - 1)).min(1).max(MAX_PROPOSALS).refine((r) => new Set(r).size === r.length, "a row twice");
|
|
1449
|
+
var ClientMessageSchema = z23.discriminatedUnion("type", [
|
|
1450
|
+
z23.object({ type: z23.literal("chat.route"), qid: z23.string().min(1).max(100), text: z23.string().min(1).max(2e4), tool: ToolSchema.optional(), context: RoutingContextSchema }),
|
|
1229
1451
|
DocumentReadSchema,
|
|
1230
1452
|
AttachmentPutSchema,
|
|
1231
1453
|
AttachmentCaptureSchema,
|
|
@@ -1233,160 +1455,178 @@ var ClientMessageSchema = z21.discriminatedUnion("type", [
|
|
|
1233
1455
|
ReposInspectSchema,
|
|
1234
1456
|
ReposScanSchema,
|
|
1235
1457
|
ModelsListSchema,
|
|
1458
|
+
SessionCommandsSchema,
|
|
1236
1459
|
OpenRigStatusSchema,
|
|
1237
1460
|
OpenRigActSchema,
|
|
1238
1461
|
OpenRigSpecsSchema,
|
|
1239
1462
|
OpenRigLaunchSchema,
|
|
1240
1463
|
OpenRigSeatsSchema,
|
|
1464
|
+
OpenRigRolesSchema,
|
|
1465
|
+
OpenRigRoleSchema,
|
|
1466
|
+
OpenRigRoleFileSchema,
|
|
1241
1467
|
/** The overlay starts a session: `cwd` is a WSL path (`~` allowed), `name` a preferred bot name (the daemon picks a
|
|
1242
1468
|
* free one otherwise), `prompt` the agent's first instruction, `continue` resumes the folder's last conversation.
|
|
1243
1469
|
* With `qid` it is the chat card of that turn: the daemon launches the turn's proposal and ignores the other fields
|
|
1244
1470
|
* but `rows`, `model` and `effort`. */
|
|
1245
|
-
|
|
1246
|
-
type:
|
|
1471
|
+
z23.object({
|
|
1472
|
+
type: z23.literal("session.launch"),
|
|
1247
1473
|
tool: ToolSchema,
|
|
1248
|
-
cwd:
|
|
1249
|
-
name:
|
|
1250
|
-
prompt:
|
|
1251
|
-
continue:
|
|
1252
|
-
qid:
|
|
1474
|
+
cwd: z23.string().min(1),
|
|
1475
|
+
name: z23.string().optional(),
|
|
1476
|
+
prompt: z23.string().optional(),
|
|
1477
|
+
continue: z23.boolean().optional(),
|
|
1478
|
+
qid: z23.string().min(1).optional(),
|
|
1253
1479
|
/** With `qid`: the card's rows to launch (#24), indexes into the turn's `proposals`, each once; the first row without it. */
|
|
1254
1480
|
rows: launchRows.optional(),
|
|
1255
1481
|
/** Folders in addition to `cwd` (Claude: `--add-dir`); resolved like `cwd`. */
|
|
1256
|
-
dirs:
|
|
1482
|
+
dirs: z23.array(z23.string().min(1)).max(20).optional(),
|
|
1257
1483
|
/** The agent's model and effort (#58), the agent's own default when left out; with `qid`, for every row launched. */
|
|
1258
1484
|
model: ModelIdSchema.optional(),
|
|
1259
1485
|
effort: EffortSchema.optional(),
|
|
1260
1486
|
/** OMP only: its approval mode, `always-ask` when left out. */
|
|
1261
1487
|
approval: OmpApprovalSchema.optional(),
|
|
1262
1488
|
/** Routing by the fleet, removed before the message reaches the daemon. */
|
|
1263
|
-
machineId:
|
|
1489
|
+
machineId: z23.string().optional()
|
|
1264
1490
|
}),
|
|
1265
1491
|
/** The launch form's folder browser: list a folder's subfolders (home when no `path`), or make `name` inside `parent`
|
|
1266
1492
|
* and list it. Both are answered with a `folder.listing`. */
|
|
1267
|
-
|
|
1268
|
-
type:
|
|
1269
|
-
path:
|
|
1493
|
+
z23.object({
|
|
1494
|
+
type: z23.literal("folder.list"),
|
|
1495
|
+
path: z23.string().optional(),
|
|
1270
1496
|
/** Routing by the fleet, removed before the message reaches the daemon. */
|
|
1271
|
-
machineId:
|
|
1497
|
+
machineId: z23.string().optional(),
|
|
1272
1498
|
/** Copied into the `folder.listing` that answers; the fleet relays only that listing, from the machine asked. */
|
|
1273
|
-
requestId:
|
|
1499
|
+
requestId: z23.string().min(1).max(100).optional()
|
|
1274
1500
|
}),
|
|
1275
|
-
|
|
1276
|
-
type:
|
|
1277
|
-
parent:
|
|
1278
|
-
name:
|
|
1501
|
+
z23.object({
|
|
1502
|
+
type: z23.literal("folder.create"),
|
|
1503
|
+
parent: z23.string().min(1),
|
|
1504
|
+
name: z23.string().min(1),
|
|
1279
1505
|
/** As for `folder.list`: routing by the fleet, and the request its `folder.listing` answers. */
|
|
1280
|
-
machineId:
|
|
1281
|
-
requestId:
|
|
1506
|
+
machineId: z23.string().optional(),
|
|
1507
|
+
requestId: z23.string().min(1).max(100).optional()
|
|
1282
1508
|
}),
|
|
1283
1509
|
/** `steer`: OMP only, redirect the turn running now instead of queueing the text after it. */
|
|
1284
1510
|
/** OMP's `ask`: the answers to every question of the dialog `requestId`, in order (`selected` labels, and/or free `text`); `cancel` declines it. */
|
|
1285
|
-
|
|
1286
|
-
|
|
1511
|
+
z23.object({ type: z23.literal("session.answer"), id: z23.string(), requestId: z23.string().min(1).max(200), answers: z23.array(z23.object({ id: z23.string(), selected: z23.array(z23.string().max(2e3)).max(100), text: z23.string().max(2e4).optional() })).max(50), cancel: z23.boolean().optional() }),
|
|
1512
|
+
z23.object({ type: z23.literal("session.reply"), id: z23.string(), text: z23.string(), attachments: z23.array(AttachmentIdSchema).max(ATTACHMENTS_MAX).optional(), steer: z23.boolean().optional() }),
|
|
1287
1513
|
/** The chat card's instruction (spec 2026-10-12 relay §4): sent now if the session is free, else queued until it is. */
|
|
1288
|
-
|
|
1514
|
+
z23.object({ type: z23.literal("session.relay"), qid: z23.string().min(1) }),
|
|
1289
1515
|
/** Takes the card's queued instruction back out of the queue. */
|
|
1290
|
-
|
|
1516
|
+
z23.object({ type: z23.literal("session.relay.cancel"), qid: z23.string().min(1) }),
|
|
1291
1517
|
/** The chat's generic card (action registry spec §5.1): applies its rows in order (all of them without `rows`), stopping at
|
|
1292
1518
|
* the first failure. `confirmed` says the front asked again; refused without it when a row is destructive. */
|
|
1293
|
-
|
|
1294
|
-
type:
|
|
1295
|
-
rid:
|
|
1296
|
-
qid:
|
|
1519
|
+
z23.object({
|
|
1520
|
+
type: z23.literal("chat.act"),
|
|
1521
|
+
rid: z23.string().min(1).max(100),
|
|
1522
|
+
qid: z23.string().min(1),
|
|
1297
1523
|
rows: launchRows.optional(),
|
|
1298
|
-
confirmed:
|
|
1524
|
+
confirmed: z23.literal(true).optional(),
|
|
1299
1525
|
/** « Refuser »: each row (session.decide only) answers its session's request with deny, whatever its own decision; never
|
|
1300
1526
|
* confirmed, and allowed on a request shown cut. The rows become `denied`. */
|
|
1301
|
-
deny:
|
|
1527
|
+
deny: z23.literal(true).optional()
|
|
1302
1528
|
}),
|
|
1303
1529
|
/** « Ignorer » the turn's cards, for every device and for Ledge (who is told at the next question); `undo` opens them
|
|
1304
1530
|
* again (« Revoir »). Refused while one of them is under way. Every client gets the turn's `ignoredAt` through chat.history. */
|
|
1305
|
-
|
|
1531
|
+
z23.object({ type: z23.literal("chat.ignore"), qid: z23.string().min(1), undo: z23.literal(true).optional() }),
|
|
1306
1532
|
/** A registry action run outside the chat (#93, the CLI): the daemon resolves `args` as it does a generic card's row, then
|
|
1307
1533
|
* applies it through its own handler. `by` the session that asks, left out for the user; `confirmed` for a destructive one. */
|
|
1308
|
-
|
|
1309
|
-
type:
|
|
1310
|
-
rid:
|
|
1311
|
-
by:
|
|
1534
|
+
z23.object({
|
|
1535
|
+
type: z23.literal("action.run"),
|
|
1536
|
+
rid: z23.string().min(1).max(100),
|
|
1537
|
+
by: z23.string().min(1).optional(),
|
|
1312
1538
|
/** `by`'s key (LEDGE_SESSION_KEY) */
|
|
1313
|
-
key:
|
|
1539
|
+
key: z23.string().max(128).optional(),
|
|
1314
1540
|
action: ActionIdSchema,
|
|
1315
|
-
args:
|
|
1316
|
-
confirmed:
|
|
1541
|
+
args: z23.record(z23.unknown()),
|
|
1542
|
+
confirmed: z23.literal(true).optional()
|
|
1317
1543
|
}),
|
|
1318
1544
|
/** The chat's close card: closes the turn's sessions, `rows` (indexes into its `closes`) or all of them. */
|
|
1319
|
-
|
|
1545
|
+
z23.object({ type: z23.literal("session.close"), qid: z23.string().min(1), rows: launchRows.optional() }),
|
|
1320
1546
|
/** `always` and `note` only where the harness offers them (`offers`); a note goes with `deny`, for the agent to read. */
|
|
1321
|
-
|
|
1322
|
-
type:
|
|
1323
|
-
id:
|
|
1547
|
+
z23.object({
|
|
1548
|
+
type: z23.literal("session.decide"),
|
|
1549
|
+
id: z23.string(),
|
|
1324
1550
|
decision: DecisionSchema,
|
|
1325
|
-
note:
|
|
1551
|
+
note: z23.string().trim().min(1).max(NOTE_MAX).optional(),
|
|
1326
1552
|
/** The request the user saw when deciding: refused (the request changed) unless it is still the pending one. Left out by older fronts and the island.
|
|
1327
1553
|
* `key` (pendingKey) covers its whole content; older fronts send kind and text only. */
|
|
1328
|
-
expect: PendingSchema.pick({ kind: true, text: true }).extend({ key:
|
|
1554
|
+
expect: PendingSchema.pick({ kind: true, text: true }).extend({ key: z23.string().max(64).optional() }).optional()
|
|
1329
1555
|
}),
|
|
1330
|
-
|
|
1331
|
-
|
|
1556
|
+
z23.object({ type: z23.literal("session.focus"), id: z23.string() }),
|
|
1557
|
+
z23.object({ type: z23.literal("session.kill"), id: z23.string() }),
|
|
1332
1558
|
/** `restoreQueue` (OMP): also take back what waits in its queue, returned as `session.queue.restored` to put in the draft. */
|
|
1333
|
-
|
|
1559
|
+
z23.object({ type: z23.literal("session.interrupt"), id: z23.string(), restoreQueue: z23.boolean().optional() }),
|
|
1334
1560
|
/** Removes a finished (exited) or read-only (hook_only) session from the list; refused for a live launcher session. */
|
|
1335
|
-
|
|
1561
|
+
z23.object({ type: z23.literal("session.dismiss"), id: z23.string() }),
|
|
1336
1562
|
/** Puts a session in the archive: it leaves the live list but is kept. */
|
|
1337
|
-
|
|
1563
|
+
z23.object({ type: z23.literal("session.archive"), id: z23.string() }),
|
|
1338
1564
|
/** Takes an archived session back out of the archive, as it was. */
|
|
1339
|
-
|
|
1565
|
+
z23.object({ type: z23.literal("session.restore"), id: z23.string() }),
|
|
1340
1566
|
/** Starts the agent again on an archived session's conversation. */
|
|
1341
|
-
|
|
1567
|
+
z23.object({ type: z23.literal("session.resume"), id: z23.string() }),
|
|
1342
1568
|
/** Changes a live session's model and effort (#58, lot 2), `null` for the agent's own default: the daemon restarts the
|
|
1343
1569
|
* agent on the same conversation with them, at once when the session is free, else at its next free moment. */
|
|
1344
|
-
|
|
1570
|
+
z23.object({ type: z23.literal("session.model"), id: z23.string(), model: ModelIdSchema.nullable(), effort: EffortSchema.nullable() }),
|
|
1345
1571
|
/** Deletes a session for good, archived or not. */
|
|
1346
|
-
|
|
1572
|
+
z23.object({ type: z23.literal("session.delete"), id: z23.string() }),
|
|
1347
1573
|
/** The user names the session's topic from the overlay (500 characters at most, the hook's prompt cap); an empty topic hands back to the automatic sources. */
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1574
|
+
z23.object({ type: z23.literal("session.rename"), id: z23.string(), topic: z23.string().max(500) }),
|
|
1575
|
+
z23.object({ type: z23.literal("session.thread"), id: z23.string() }),
|
|
1576
|
+
z23.object({ type: z23.literal("session.register"), session: SessionSchema }),
|
|
1351
1577
|
/** `agent` and `cwd` describe the session when the daemon does not know it yet (a hook from an agent started without ledge). */
|
|
1352
|
-
|
|
1353
|
-
type:
|
|
1354
|
-
id:
|
|
1578
|
+
z23.object({
|
|
1579
|
+
type: z23.literal("session.event"),
|
|
1580
|
+
id: z23.string(),
|
|
1355
1581
|
event: SessionEventSchema,
|
|
1356
1582
|
agent: ToolSchema.optional(),
|
|
1357
|
-
cwd:
|
|
1583
|
+
cwd: z23.string().optional(),
|
|
1358
1584
|
/** The OpenRig seat the agent runs in (`OPENRIG_SESSION_NAME`, `pod-member@rig`), when OpenRig launched it. */
|
|
1359
1585
|
rigSeat: RigSeatNameSchema.optional()
|
|
1360
1586
|
}),
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1587
|
+
z23.object({ type: z23.literal("session.unregister"), id: z23.string(), exitCode: z23.number().int().nullable() }),
|
|
1588
|
+
z23.object({ type: z23.literal("snapshot.request") }),
|
|
1589
|
+
z23.object({ type: z23.literal("chat.ask"), qid: z23.string().min(1), text: z23.string(), images: z23.array(ImageUploadSchema).max(ATTACHMENTS_MAX).optional(), tool: ToolSchema.optional() }),
|
|
1590
|
+
z23.object({ type: z23.literal("chat.cancel"), qid: z23.string().min(1) }),
|
|
1365
1591
|
/** Starts a new conversation with Ledge: the day's turns are cleared and the next question opens a new harness session. */
|
|
1366
|
-
|
|
1592
|
+
z23.object({ type: z23.literal("chat.reset") }),
|
|
1367
1593
|
/** Installs `@paradow/ledge@version` and restarts the service (spec 2026-10-12 auto-update §6). */
|
|
1368
|
-
|
|
1594
|
+
z23.object({ type: z23.literal("update.daemon"), version: z23.string().regex(SEMVER) }),
|
|
1369
1595
|
/** Replaces the daemon's machines list (first connected front wins); refused unless valid. */
|
|
1370
|
-
|
|
1596
|
+
z23.object({ type: z23.literal("machines.set"), machines: MachinesSchema }),
|
|
1371
1597
|
/** Replaces the local daemon's projects and rules as a whole; refused unless valid. */
|
|
1372
|
-
|
|
1598
|
+
z23.object({ type: z23.literal("projects.set"), projects: ProjectsSchema }),
|
|
1373
1599
|
/** Replaces the island's settings as a whole; refused unless valid. */
|
|
1374
|
-
|
|
1600
|
+
z23.object({ type: z23.literal("island.set"), prefs: IslandPrefsSchema }),
|
|
1375
1601
|
/** Mobile access (lot 3), local fronts only. A changed `relayUrl` drops the relay session. */
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1602
|
+
z23.object({ type: z23.literal("mobile.set"), enabled: z23.boolean(), relayUrl: RelayUrlSchema }),
|
|
1603
|
+
z23.object({ type: z23.literal("mobile.signin") }),
|
|
1604
|
+
z23.object({ type: z23.literal("mobile.signin.cancel") }),
|
|
1605
|
+
z23.object({ type: z23.literal("mobile.signout") }),
|
|
1380
1606
|
/** While the link is offline: try again now. */
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1607
|
+
z23.object({ type: z23.literal("mobile.retry") }),
|
|
1608
|
+
z23.object({ type: z23.literal("mobile.pairing.approve"), id: z23.string().min(1).max(100) }),
|
|
1609
|
+
z23.object({ type: z23.literal("mobile.pairing.deny"), id: z23.string().min(1).max(100) }),
|
|
1384
1610
|
/** `id`: the device's fingerprint. */
|
|
1385
|
-
|
|
1611
|
+
z23.object({ type: z23.literal("mobile.device.revoke"), id: z23.string().min(1).max(100) }),
|
|
1612
|
+
/** « Taper dans les terminaux depuis le téléphone »; turned off, every phone loses the hand. */
|
|
1613
|
+
z23.object({ type: z23.literal("mobile.terminal"), input: z23.boolean() }),
|
|
1614
|
+
/** « Reprendre la main »: the phone typing into the session's terminal goes back to reading it. */
|
|
1615
|
+
z23.object({ type: z23.literal("terminal.release"), id: z23.string().min(1) }),
|
|
1386
1616
|
/** One session writing to another by Ledge name (`to` may end in ` [ref]`); `intercept` (the Claude hook): answer `native` unless `to` is a live non-Claude session. */
|
|
1387
|
-
|
|
1617
|
+
z23.object({ type: z23.literal("session.message"), rid: z23.string().min(1), from: z23.string().min(1), to: z23.string().min(1), text: z23.string().min(1).max(MESSAGE_MAX), summary: z23.string().max(SUMMARY_MAX).optional(), intercept: z23.boolean().optional() }),
|
|
1618
|
+
/** The user writing to every session of a project, or to several sessions (ids or names). */
|
|
1619
|
+
z23.object({
|
|
1620
|
+
type: z23.literal("session.broadcast"),
|
|
1621
|
+
rid: z23.string().min(1).max(100),
|
|
1622
|
+
to: z23.union([z23.object({ project: z23.string().min(1).max(80) }), z23.object({ sessions: z23.array(z23.string().min(1)).min(1).max(BROADCAST_SESSIONS_MAX) })]),
|
|
1623
|
+
text: z23.string().trim().min(1).max(MESSAGE_MAX),
|
|
1624
|
+
summary: z23.string().max(SUMMARY_MAX).optional(),
|
|
1625
|
+
/** How the recipients are told who it went to (« (à <label>) »), instead of the names: a project the fleet turned into sessions. */
|
|
1626
|
+
label: z23.string().min(1).max(BROADCAST_LABEL_MAX).optional()
|
|
1627
|
+
}),
|
|
1388
1628
|
/** `ledge read <id>`: a message's whole body, for its receiver or its sender (`by`, a session id). */
|
|
1389
|
-
|
|
1629
|
+
z23.object({ type: z23.literal("message.read"), id: z23.string().min(1).max(40), by: z23.string().min(1) }),
|
|
1390
1630
|
TaskCmdSchema,
|
|
1391
1631
|
SessionHandoverSchema,
|
|
1392
1632
|
ContextSetSchema,
|
|
@@ -1395,79 +1635,79 @@ var ClientMessageSchema = z21.discriminatedUnion("type", [
|
|
|
1395
1635
|
]);
|
|
1396
1636
|
|
|
1397
1637
|
// ../protocol/src/relay.ts
|
|
1398
|
-
import { z as
|
|
1638
|
+
import { z as z24 } from "zod";
|
|
1399
1639
|
var SEALED_MAX = 1048576;
|
|
1400
|
-
var RelayPeerSchema =
|
|
1401
|
-
var RelayClientMessageSchema =
|
|
1402
|
-
type:
|
|
1640
|
+
var RelayPeerSchema = z24.union([z24.literal("pc"), z24.string().regex(/^d_[A-Za-z0-9_-]{16,40}$/)]);
|
|
1641
|
+
var RelayClientMessageSchema = z24.object({
|
|
1642
|
+
type: z24.literal("send"),
|
|
1403
1643
|
to: RelayPeerSchema,
|
|
1404
|
-
body:
|
|
1644
|
+
body: z24.string().max(SEALED_MAX)
|
|
1405
1645
|
});
|
|
1406
|
-
var RelayServerMessageSchema =
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1646
|
+
var RelayServerMessageSchema = z24.discriminatedUnion("type", [
|
|
1647
|
+
z24.object({ type: z24.literal("welcome"), accountId: z24.string(), self: RelayPeerSchema, online: z24.array(RelayPeerSchema) }),
|
|
1648
|
+
z24.object({ type: z24.literal("deliver"), from: RelayPeerSchema, body: z24.string() }),
|
|
1649
|
+
z24.object({ type: z24.literal("presence"), peer: RelayPeerSchema, online: z24.boolean() }),
|
|
1410
1650
|
/**
|
|
1411
1651
|
* A frame was refused; `to` names the target when it is known.
|
|
1412
1652
|
* Contract: an error concerning a sealed body (`offline`, `too_large`, or a socket closed with 4008
|
|
1413
1653
|
* `rate_limited`) means that body was not delivered, so the sequence on that channel is lost: the
|
|
1414
1654
|
* sender must re-handshake with that peer before sending to it again.
|
|
1415
1655
|
*/
|
|
1416
|
-
|
|
1417
|
-
type:
|
|
1418
|
-
code:
|
|
1656
|
+
z24.object({
|
|
1657
|
+
type: z24.literal("error"),
|
|
1658
|
+
code: z24.enum(["bad_frame", "too_large", "bad_target", "offline", "rate_limited"]),
|
|
1419
1659
|
to: RelayPeerSchema.optional()
|
|
1420
1660
|
})
|
|
1421
1661
|
]);
|
|
1422
1662
|
var PAIR_LABEL_MAX = 40;
|
|
1423
|
-
var KeyB64 =
|
|
1424
|
-
var Nonce =
|
|
1425
|
-
var Fingerprint =
|
|
1426
|
-
var SealedFrameSchema =
|
|
1663
|
+
var KeyB64 = z24.string().regex(/^[A-Za-z0-9_-]{87}$/);
|
|
1664
|
+
var Nonce = z24.string().regex(/^[A-Za-z0-9_-]{43}$/);
|
|
1665
|
+
var Fingerprint = z24.string().regex(/^[A-Za-z0-9_-]{22}$/);
|
|
1666
|
+
var SealedFrameSchema = z24.discriminatedUnion("k", [
|
|
1427
1667
|
/** `e` is the ephemeral key, `f` the fingerprint of the sender's static key. */
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1668
|
+
z24.object({ k: z24.literal("hello"), e: KeyB64, f: Fingerprint }),
|
|
1669
|
+
z24.object({ k: z24.literal("msg"), s: z24.string() }),
|
|
1670
|
+
z24.object({ k: z24.literal("pair_commit"), c: Nonce, label: z24.string().trim().min(1).max(PAIR_LABEL_MAX) }),
|
|
1671
|
+
z24.object({ k: z24.literal("pair_offer"), pc: KeyB64, n: Nonce }),
|
|
1672
|
+
z24.object({ k: z24.literal("pair_reveal"), d: KeyB64, n: Nonce }),
|
|
1673
|
+
z24.object({ k: z24.literal("pair_ok") }),
|
|
1674
|
+
z24.object({ k: z24.literal("pair_no") }),
|
|
1675
|
+
z24.object({ k: z24.literal("denied") }),
|
|
1676
|
+
z24.object({ k: z24.literal("rekey") })
|
|
1437
1677
|
]);
|
|
1438
1678
|
|
|
1439
1679
|
// ../protocol/src/relayAuth.ts
|
|
1440
|
-
import { z as
|
|
1441
|
-
var DeviceStartResponseSchema =
|
|
1442
|
-
deviceCode:
|
|
1443
|
-
userCode:
|
|
1444
|
-
verificationUri:
|
|
1445
|
-
expiresIn:
|
|
1446
|
-
interval:
|
|
1447
|
-
});
|
|
1448
|
-
var DevicePollResponseSchema =
|
|
1449
|
-
|
|
1450
|
-
|
|
1680
|
+
import { z as z25 } from "zod";
|
|
1681
|
+
var DeviceStartResponseSchema = z25.object({
|
|
1682
|
+
deviceCode: z25.string().min(1),
|
|
1683
|
+
userCode: z25.string().min(1),
|
|
1684
|
+
verificationUri: z25.string().min(1),
|
|
1685
|
+
expiresIn: z25.number().int().positive(),
|
|
1686
|
+
interval: z25.number().int().positive()
|
|
1687
|
+
});
|
|
1688
|
+
var DevicePollResponseSchema = z25.discriminatedUnion("status", [
|
|
1689
|
+
z25.object({ status: z25.literal("pending") }),
|
|
1690
|
+
z25.object({ status: z25.literal("slow_down"), interval: z25.number().int().positive() }),
|
|
1451
1691
|
/** `token` is the relay session token, shown this once only. */
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1692
|
+
z25.object({ status: z25.literal("ok"), token: z25.string().min(1), accountId: z25.string().min(1), login: z25.string().min(1) }),
|
|
1693
|
+
z25.object({ status: z25.literal("expired") }),
|
|
1694
|
+
z25.object({ status: z25.literal("denied") }),
|
|
1695
|
+
z25.object({ status: z25.literal("not_allowed") })
|
|
1456
1696
|
]);
|
|
1457
|
-
var DeviceRevokeResponseSchema =
|
|
1458
|
-
var MeResponseSchema =
|
|
1459
|
-
accountId:
|
|
1460
|
-
login:
|
|
1697
|
+
var DeviceRevokeResponseSchema = z25.object({ revoked: z25.number().int().min(0) });
|
|
1698
|
+
var MeResponseSchema = z25.object({
|
|
1699
|
+
accountId: z25.string().min(1),
|
|
1700
|
+
login: z25.string().min(1),
|
|
1461
1701
|
peer: RelayPeerSchema,
|
|
1462
|
-
kind:
|
|
1702
|
+
kind: z25.enum(["pc", "device"])
|
|
1463
1703
|
});
|
|
1464
1704
|
|
|
1465
1705
|
// ../protocol/src/phone.ts
|
|
1466
|
-
import { z as
|
|
1706
|
+
import { z as z27 } from "zod";
|
|
1467
1707
|
|
|
1468
1708
|
// ../protocol/src/push.ts
|
|
1469
|
-
import { z as
|
|
1470
|
-
var B64U =
|
|
1709
|
+
import { z as z26 } from "zod";
|
|
1710
|
+
var B64U = z26.string().regex(/^[A-Za-z0-9_-]*$/);
|
|
1471
1711
|
var PUSH_HOSTS = ["*.push.apple.com", "fcm.googleapis.com", "updates.push.services.mozilla.com", "*.notify.windows.com"];
|
|
1472
1712
|
function pushEndpointAllowed(url) {
|
|
1473
1713
|
let u;
|
|
@@ -1484,29 +1724,29 @@ function pushEndpointAllowed(url) {
|
|
|
1484
1724
|
return host.length > suffix.length && host.endsWith(suffix);
|
|
1485
1725
|
});
|
|
1486
1726
|
}
|
|
1487
|
-
var PushSendSchema =
|
|
1488
|
-
endpoint:
|
|
1727
|
+
var PushSendSchema = z26.object({
|
|
1728
|
+
endpoint: z26.string().url().max(1024),
|
|
1489
1729
|
body: B64U.max(5462),
|
|
1490
|
-
ttl:
|
|
1491
|
-
urgency:
|
|
1492
|
-
topic:
|
|
1493
|
-
});
|
|
1494
|
-
var PushSendResultSchema =
|
|
1495
|
-
result:
|
|
1496
|
-
status:
|
|
1497
|
-
});
|
|
1498
|
-
var PushSubscriptionSchema =
|
|
1499
|
-
endpoint:
|
|
1500
|
-
keys:
|
|
1501
|
-
});
|
|
1502
|
-
var PushPayloadSchema =
|
|
1503
|
-
v:
|
|
1504
|
-
kind:
|
|
1505
|
-
title:
|
|
1506
|
-
body:
|
|
1507
|
-
url:
|
|
1508
|
-
badge:
|
|
1509
|
-
tag:
|
|
1730
|
+
ttl: z26.number().int().min(0).max(86400),
|
|
1731
|
+
urgency: z26.enum(["normal", "high"]),
|
|
1732
|
+
topic: z26.string().regex(/^[A-Za-z0-9_-]{1,32}$/)
|
|
1733
|
+
});
|
|
1734
|
+
var PushSendResultSchema = z26.object({
|
|
1735
|
+
result: z26.enum(["sent", "gone", "failed"]),
|
|
1736
|
+
status: z26.number().int().optional()
|
|
1737
|
+
});
|
|
1738
|
+
var PushSubscriptionSchema = z26.object({
|
|
1739
|
+
endpoint: z26.string().url().max(1024).refine(pushEndpointAllowed),
|
|
1740
|
+
keys: z26.object({ p256dh: B64U.length(87), auth: B64U.length(22) })
|
|
1741
|
+
});
|
|
1742
|
+
var PushPayloadSchema = z26.object({
|
|
1743
|
+
v: z26.literal(1),
|
|
1744
|
+
kind: z26.enum(["request", "question", "finished", "chat", "decision"]),
|
|
1745
|
+
title: z26.string().max(80),
|
|
1746
|
+
body: z26.string().max(120),
|
|
1747
|
+
url: z26.string().regex(/^\/#\/[A-Za-z0-9/%._~-]*$/).max(300),
|
|
1748
|
+
badge: z26.number().int().min(0).max(999),
|
|
1749
|
+
tag: z26.string().regex(/^[A-Za-z0-9_-]{1,32}$/)
|
|
1510
1750
|
});
|
|
1511
1751
|
|
|
1512
1752
|
// ../protocol/src/phone.ts
|
|
@@ -1516,35 +1756,35 @@ var THREAD_TEXT_MAX = 8192;
|
|
|
1516
1756
|
var TOOL_TEXT_MAX = 2048;
|
|
1517
1757
|
var CHECKLIST_TASKS_MAX = 50;
|
|
1518
1758
|
var CHECKLIST_TEXT_MAX = 200;
|
|
1519
|
-
var ChecklistStatusSchema =
|
|
1520
|
-
var PhoneChecklistSchema =
|
|
1521
|
-
tasks:
|
|
1522
|
-
done:
|
|
1523
|
-
total:
|
|
1524
|
-
cut:
|
|
1525
|
-
});
|
|
1526
|
-
var PhoneChecklistSummarySchema =
|
|
1527
|
-
done:
|
|
1528
|
-
total:
|
|
1529
|
-
current:
|
|
1530
|
-
cut:
|
|
1531
|
-
});
|
|
1532
|
-
var RiskSchema =
|
|
1533
|
-
var RiskReasonSchema =
|
|
1534
|
-
var OperationIdSchema =
|
|
1535
|
-
var OperationStatusSchema =
|
|
1536
|
-
var OperationReasonSchema =
|
|
1537
|
-
var CapabilitiesSchema =
|
|
1759
|
+
var ChecklistStatusSchema = z27.enum(["pending", "in_progress", "completed"]);
|
|
1760
|
+
var PhoneChecklistSchema = z27.object({
|
|
1761
|
+
tasks: z27.array(z27.object({ text: z27.string().max(CHECKLIST_TEXT_MAX), status: ChecklistStatusSchema })).max(CHECKLIST_TASKS_MAX),
|
|
1762
|
+
done: z27.number().int().nonnegative(),
|
|
1763
|
+
total: z27.number().int().nonnegative(),
|
|
1764
|
+
cut: z27.literal(true).optional()
|
|
1765
|
+
});
|
|
1766
|
+
var PhoneChecklistSummarySchema = z27.object({
|
|
1767
|
+
done: z27.number().int().nonnegative(),
|
|
1768
|
+
total: z27.number().int().nonnegative(),
|
|
1769
|
+
current: z27.string().max(CHECKLIST_TEXT_MAX).optional(),
|
|
1770
|
+
cut: z27.literal(true).optional()
|
|
1771
|
+
});
|
|
1772
|
+
var RiskSchema = z27.enum(["low", "high"]);
|
|
1773
|
+
var RiskReasonSchema = z27.enum(["question", "read_only_command", "edit_inside_folder", "pushes", "destroys", "deploys", "publishes", "pipes_to_shell", "network", "writes_outside_folder", "sensitive_path", "unsafe_flag", "shell_syntax", "script", "plan", "unknown"]);
|
|
1774
|
+
var OperationIdSchema = z27.string().uuid();
|
|
1775
|
+
var OperationStatusSchema = z27.enum(["received", "applied", "rejected", "failed"]);
|
|
1776
|
+
var OperationReasonSchema = z27.enum([...RefusalCodeSchema.options, "untypable", "confirm_required", "conflict", "assertion_invalid", "assertion_expired", "scheduled", ...ModelRefusalSchema.options]);
|
|
1777
|
+
var CapabilitiesSchema = z27.object({
|
|
1538
1778
|
/** A question can be answered in free text. */
|
|
1539
|
-
freeTextAnswer:
|
|
1779
|
+
freeTextAnswer: z27.boolean(),
|
|
1540
1780
|
/** The session's accept/deny can be typed; absent = true. */
|
|
1541
|
-
decide:
|
|
1781
|
+
decide: z27.boolean().optional(),
|
|
1542
1782
|
/** OMP's dialog questions can be answered from the phone; absent = false. */
|
|
1543
|
-
questions:
|
|
1783
|
+
questions: z27.boolean().optional()
|
|
1544
1784
|
});
|
|
1545
|
-
var CapabilitiesByToolSchema =
|
|
1546
|
-
var PhonePendingSchema = PendingSchema.extend({ key:
|
|
1547
|
-
var SwitchableSessionSchema = SessionSchema.extend({ canSwitch:
|
|
1785
|
+
var CapabilitiesByToolSchema = z27.object({ claude: CapabilitiesSchema, codex: CapabilitiesSchema, cursor: CapabilitiesSchema, omp: CapabilitiesSchema });
|
|
1786
|
+
var PhonePendingSchema = PendingSchema.extend({ key: z27.string().max(64), risk: RiskSchema, riskReason: RiskReasonSchema, cut: z27.literal(true).optional() });
|
|
1787
|
+
var SwitchableSessionSchema = SessionSchema.extend({ canSwitch: z27.literal(true).optional() });
|
|
1548
1788
|
var PhoneSessionSchema = SwitchableSessionSchema.pick({
|
|
1549
1789
|
id: true,
|
|
1550
1790
|
name: true,
|
|
@@ -1572,288 +1812,346 @@ var PhoneSessionSchema = SwitchableSessionSchema.pick({
|
|
|
1572
1812
|
exitedAt: true
|
|
1573
1813
|
}).extend({
|
|
1574
1814
|
pending: PhonePendingSchema.optional(),
|
|
1575
|
-
unread:
|
|
1576
|
-
handover:
|
|
1815
|
+
unread: z27.boolean(),
|
|
1816
|
+
handover: z27.object({ state: HandoverStateSchema, toName: z27.string().max(80).optional() }).strict().optional(),
|
|
1577
1817
|
/** Absent: the agent has no checklist. */
|
|
1578
1818
|
checklist: PhoneChecklistSummarySchema.optional(),
|
|
1579
1819
|
/** Started without Ledge (`hook_only`): the phone can neither close nor resume it. */
|
|
1580
|
-
readOnly:
|
|
1820
|
+
readOnly: z27.literal(true).optional()
|
|
1581
1821
|
});
|
|
1582
|
-
var B64U2 =
|
|
1583
|
-
var CredentialSchema =
|
|
1822
|
+
var B64U2 = z27.string().regex(/^[A-Za-z0-9_-]*$/);
|
|
1823
|
+
var CredentialSchema = z27.object({
|
|
1584
1824
|
id: B64U2.max(1366),
|
|
1585
1825
|
publicKey: B64U2.max(2048),
|
|
1586
|
-
alg:
|
|
1826
|
+
alg: z27.union([z27.literal(-7), z27.literal(-257)]),
|
|
1587
1827
|
clientDataJSON: B64U2.max(4096),
|
|
1588
1828
|
authenticatorData: B64U2.max(4096),
|
|
1589
|
-
transports:
|
|
1829
|
+
transports: z27.array(z27.string().max(20)).max(8).optional()
|
|
1590
1830
|
});
|
|
1591
|
-
var AssertionSchema =
|
|
1831
|
+
var AssertionSchema = z27.object({
|
|
1592
1832
|
id: B64U2.max(1366),
|
|
1593
1833
|
clientDataJSON: B64U2.max(4096),
|
|
1594
1834
|
authenticatorData: B64U2.max(4096),
|
|
1595
1835
|
signature: B64U2.max(1024)
|
|
1596
1836
|
});
|
|
1597
|
-
var ConfirmPurposeSchema =
|
|
1837
|
+
var ConfirmPurposeSchema = z27.enum(["register", "decide", "launch", "close", "act", "pc", "terminal"]);
|
|
1598
1838
|
var PhoneChatStepSchema = ChatStepSchema.pick({ kind: true, id: true, name: true }).strict();
|
|
1599
|
-
var PhoneChatProposalSchema =
|
|
1600
|
-
project:
|
|
1839
|
+
var PhoneChatProposalSchema = z27.object({
|
|
1840
|
+
project: z27.string().max(80),
|
|
1601
1841
|
tool: ToolSchema,
|
|
1602
|
-
prompt:
|
|
1603
|
-
launchedId:
|
|
1604
|
-
failed:
|
|
1842
|
+
prompt: z27.string().max(TOOL_TEXT_MAX).optional(),
|
|
1843
|
+
launchedId: z27.string().optional(),
|
|
1844
|
+
failed: z27.literal(true).optional()
|
|
1605
1845
|
}).strict();
|
|
1606
|
-
var PhoneChatRelaySchema =
|
|
1607
|
-
id:
|
|
1608
|
-
name:
|
|
1846
|
+
var PhoneChatRelaySchema = z27.object({
|
|
1847
|
+
id: z27.string().min(1),
|
|
1848
|
+
name: z27.string(),
|
|
1609
1849
|
tool: ToolSchema,
|
|
1610
|
-
text:
|
|
1611
|
-
status:
|
|
1612
|
-
sentAt:
|
|
1613
|
-
failed:
|
|
1850
|
+
text: z27.string().max(TOOL_TEXT_MAX),
|
|
1851
|
+
status: z27.enum(["queued", "sent"]).optional(),
|
|
1852
|
+
sentAt: z27.string().datetime().optional(),
|
|
1853
|
+
failed: z27.literal(true).optional(),
|
|
1614
1854
|
/** With `failed`: why, as the PC's code (never its text). */
|
|
1615
|
-
code:
|
|
1855
|
+
code: z27.string().max(40).optional()
|
|
1616
1856
|
}).strict();
|
|
1617
|
-
var CloseHowSchema =
|
|
1618
|
-
var PhoneChatCloseSchema =
|
|
1619
|
-
id:
|
|
1620
|
-
name:
|
|
1857
|
+
var CloseHowSchema = z27.enum(["exit", "killed"]);
|
|
1858
|
+
var PhoneChatCloseSchema = z27.object({
|
|
1859
|
+
id: z27.string().min(1),
|
|
1860
|
+
name: z27.string(),
|
|
1621
1861
|
tool: ToolSchema,
|
|
1622
1862
|
how: CloseHowSchema.optional(),
|
|
1623
|
-
closedAt:
|
|
1624
|
-
failed:
|
|
1863
|
+
closedAt: z27.string().datetime().optional(),
|
|
1864
|
+
failed: z27.literal(true).optional()
|
|
1625
1865
|
}).strict();
|
|
1626
|
-
var PhoneChatTaskSchema =
|
|
1627
|
-
to:
|
|
1628
|
-
toName:
|
|
1629
|
-
title:
|
|
1630
|
-
text:
|
|
1631
|
-
taskId:
|
|
1632
|
-
ownerName:
|
|
1633
|
-
state:
|
|
1634
|
-
failed:
|
|
1866
|
+
var PhoneChatTaskSchema = z27.object({
|
|
1867
|
+
to: z27.string().max(80),
|
|
1868
|
+
toName: z27.string().max(80).optional(),
|
|
1869
|
+
title: z27.string().max(200),
|
|
1870
|
+
text: z27.string().max(TOOL_TEXT_MAX).optional(),
|
|
1871
|
+
taskId: z27.string().max(40).optional(),
|
|
1872
|
+
ownerName: z27.string().max(80).optional(),
|
|
1873
|
+
state: z27.enum(["open", "taken", "done", "refused", "dropped"]).optional(),
|
|
1874
|
+
failed: z27.literal(true).optional()
|
|
1635
1875
|
}).strict();
|
|
1636
|
-
var PhoneChatActionSchema =
|
|
1637
|
-
action:
|
|
1638
|
-
summary:
|
|
1876
|
+
var PhoneChatActionSchema = z27.object({
|
|
1877
|
+
action: z27.string().min(1).max(40),
|
|
1878
|
+
summary: z27.string().max(300),
|
|
1639
1879
|
vars: ActionVarsSchema.optional(),
|
|
1640
|
-
detail:
|
|
1641
|
-
detailCut:
|
|
1880
|
+
detail: z27.string().max(TOOL_TEXT_MAX).optional(),
|
|
1881
|
+
detailCut: z27.literal(true).optional(),
|
|
1642
1882
|
risk: ActionRiskSchema,
|
|
1643
|
-
session:
|
|
1883
|
+
session: z27.string().max(100).optional(),
|
|
1644
1884
|
state: ChatActionStateSchema.optional(),
|
|
1645
|
-
doneAt:
|
|
1646
|
-
code:
|
|
1885
|
+
doneAt: z27.string().datetime().optional(),
|
|
1886
|
+
code: z27.string().max(40).optional()
|
|
1647
1887
|
}).strict();
|
|
1648
|
-
var PhoneTaskSchema =
|
|
1649
|
-
id:
|
|
1650
|
-
title:
|
|
1651
|
-
owner:
|
|
1652
|
-
ownerName:
|
|
1888
|
+
var PhoneTaskSchema = z27.object({
|
|
1889
|
+
id: z27.string().max(40),
|
|
1890
|
+
title: z27.string().max(200),
|
|
1891
|
+
owner: z27.string().max(100),
|
|
1892
|
+
ownerName: z27.string().max(80),
|
|
1653
1893
|
/** Who gave it: `ledge` for Ledge's own; a session for a decision it put to the user (owner `ledge`), with its detail (the options). */
|
|
1654
|
-
giver:
|
|
1655
|
-
giverName:
|
|
1656
|
-
text:
|
|
1657
|
-
state:
|
|
1658
|
-
result:
|
|
1659
|
-
cut:
|
|
1660
|
-
at:
|
|
1661
|
-
updatedAt:
|
|
1662
|
-
seen:
|
|
1894
|
+
giver: z27.string().max(100).optional(),
|
|
1895
|
+
giverName: z27.string().max(80).optional(),
|
|
1896
|
+
text: z27.string().max(TOOL_TEXT_MAX).optional(),
|
|
1897
|
+
state: z27.enum(["open", "taken", "done", "refused", "dropped"]),
|
|
1898
|
+
result: z27.string().max(TOOL_TEXT_MAX).optional(),
|
|
1899
|
+
cut: z27.literal(true).optional(),
|
|
1900
|
+
at: z27.string(),
|
|
1901
|
+
updatedAt: z27.string(),
|
|
1902
|
+
seen: z27.literal(true).optional()
|
|
1663
1903
|
}).strict();
|
|
1664
|
-
var ChatErrorCodeSchema =
|
|
1665
|
-
var PhoneChatTurnSchema =
|
|
1904
|
+
var ChatErrorCodeSchema = z27.enum(["busy", "timeout", "cancelled", "failed"]);
|
|
1905
|
+
var PhoneChatTurnSchema = z27.object({
|
|
1666
1906
|
qid: OperationIdSchema,
|
|
1667
|
-
question:
|
|
1668
|
-
askedAt:
|
|
1669
|
-
steps:
|
|
1670
|
-
answer:
|
|
1671
|
-
answeredAt:
|
|
1907
|
+
question: z27.string().max(MESSAGE_MAX),
|
|
1908
|
+
askedAt: z27.string().datetime(),
|
|
1909
|
+
steps: z27.array(PhoneChatStepSchema).max(50),
|
|
1910
|
+
answer: z27.string().max(THREAD_TEXT_MAX).optional(),
|
|
1911
|
+
answeredAt: z27.string().datetime().optional(),
|
|
1672
1912
|
error: ChatErrorCodeSchema.optional(),
|
|
1673
|
-
proposals:
|
|
1913
|
+
proposals: z27.array(PhoneChatProposalSchema).max(MAX_PROPOSALS).optional(),
|
|
1674
1914
|
relay: PhoneChatRelaySchema.optional(),
|
|
1675
|
-
closes:
|
|
1676
|
-
cut:
|
|
1677
|
-
tasks:
|
|
1678
|
-
actions:
|
|
1679
|
-
ignoredAt:
|
|
1680
|
-
attachments:
|
|
1915
|
+
closes: z27.array(PhoneChatCloseSchema).min(1).max(MAX_PROPOSALS).optional(),
|
|
1916
|
+
cut: z27.literal(true).optional(),
|
|
1917
|
+
tasks: z27.array(PhoneChatTaskSchema).min(1).max(MAX_PROPOSALS).optional(),
|
|
1918
|
+
actions: z27.array(PhoneChatActionSchema).min(1).max(MAX_PROPOSALS).optional(),
|
|
1919
|
+
ignoredAt: z27.string().datetime().optional(),
|
|
1920
|
+
attachments: z27.array(AttachmentRefSchema).max(ATTACHMENTS_MAX).optional()
|
|
1681
1921
|
}).strict();
|
|
1682
|
-
var sessionId =
|
|
1683
|
-
var PhoneExpectSchema = PendingSchema.pick({ kind: true, text: true }).extend({ key:
|
|
1684
|
-
var CardRowsSchema =
|
|
1922
|
+
var sessionId = z27.string().min(1).max(100);
|
|
1923
|
+
var PhoneExpectSchema = PendingSchema.pick({ kind: true, text: true }).extend({ key: z27.string().min(1).max(64) });
|
|
1924
|
+
var CardRowsSchema = z27.array(z27.number().int().min(0).max(MAX_PROPOSALS - 1)).min(1).max(MAX_PROPOSALS).refine((r) => new Set(r).size === r.length, "a row twice");
|
|
1685
1925
|
var pcOp = { op: OperationIdSchema, assertion: AssertionSchema.optional() };
|
|
1686
|
-
var PcActionSchema =
|
|
1687
|
-
var
|
|
1688
|
-
|
|
1689
|
-
|
|
1926
|
+
var PcActionSchema = z27.enum(["update", "restart"]);
|
|
1927
|
+
var TerminalIdSchema = z27.string().regex(/^t_[A-Za-z0-9_-]{8,40}$/);
|
|
1928
|
+
var TERM_INPUT_MAX = 4096;
|
|
1929
|
+
var TERM_SCREEN_MAX = 65536;
|
|
1930
|
+
var TERM_OUTPUT_MAX = 32768;
|
|
1931
|
+
var TermDimSchema = z27.number().int().min(1).max(1e4);
|
|
1932
|
+
var TermReleaseWhySchema = z27.enum(["asked", "pc", "idle", "expired", "background", "link"]);
|
|
1933
|
+
var TermExitWhySchema = z27.enum(["ended", "gone", "closed", "superseded", "failed"]);
|
|
1934
|
+
var PhoneClientMessageSchema = z27.discriminatedUnion("type", [
|
|
1935
|
+
z27.object({ type: z27.literal("snapshot.request") }),
|
|
1936
|
+
z27.object({ type: z27.literal("session.thread"), id: sessionId }),
|
|
1690
1937
|
/** The session's request with its whole body (up to `PENDING_BODY_MAX`). */
|
|
1691
|
-
|
|
1938
|
+
z27.object({ type: z27.literal("session.pending"), id: sessionId }),
|
|
1692
1939
|
/** The session whose terminal the phone shows; null when it shows none. */
|
|
1693
|
-
|
|
1694
|
-
|
|
1940
|
+
z27.object({ type: z27.literal("session.watch"), id: sessionId.nullable() }),
|
|
1941
|
+
z27.object({ type: z27.literal("session.read"), id: sessionId, at: z27.string().datetime() }),
|
|
1695
1942
|
/**
|
|
1696
1943
|
* Asks for a challenge. One object for every purpose: the gateway refuses a `decide` without `id` and `expect`. A `close`
|
|
1697
1944
|
* or `act` names a turn of the chat (`qid`) or a session (`id`, for `session.close` and `session.resume`).
|
|
1698
1945
|
*/
|
|
1699
|
-
|
|
1700
|
-
type:
|
|
1946
|
+
z27.object({
|
|
1947
|
+
type: z27.literal("confirm.prepare"),
|
|
1701
1948
|
purpose: ConfirmPurposeSchema,
|
|
1702
1949
|
id: sessionId.optional(),
|
|
1703
1950
|
expect: PhoneExpectSchema.optional(),
|
|
1704
1951
|
qid: OperationIdSchema.optional(),
|
|
1705
1952
|
/** A `pc` challenge: what it is for, and for an update the version it covers. */
|
|
1706
1953
|
action: PcActionSchema.optional(),
|
|
1707
|
-
version:
|
|
1954
|
+
version: z27.string().regex(SEMVER).optional()
|
|
1708
1955
|
}),
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
type:
|
|
1956
|
+
z27.object({ type: z27.literal("credential.register"), op: OperationIdSchema, credential: CredentialSchema }),
|
|
1957
|
+
z27.object({
|
|
1958
|
+
type: z27.literal("session.decide"),
|
|
1712
1959
|
op: OperationIdSchema,
|
|
1713
1960
|
id: sessionId,
|
|
1714
1961
|
decision: DecisionSchema,
|
|
1715
|
-
note:
|
|
1962
|
+
note: z27.string().trim().min(1).max(NOTE_MAX).optional(),
|
|
1716
1963
|
/** The request the phone showed: refused unless it is still the pending one. */
|
|
1717
1964
|
expect: PhoneExpectSchema,
|
|
1718
1965
|
/** The device check over the challenge of `confirm.prepare`; required by the daemon for a high-risk accept. */
|
|
1719
1966
|
assertion: AssertionSchema.optional()
|
|
1720
1967
|
}),
|
|
1721
|
-
|
|
1722
|
-
type:
|
|
1968
|
+
z27.object({
|
|
1969
|
+
type: z27.literal("session.reply"),
|
|
1723
1970
|
op: OperationIdSchema,
|
|
1724
1971
|
id: sessionId,
|
|
1725
|
-
text:
|
|
1726
|
-
attachments:
|
|
1972
|
+
text: z27.string().max(MESSAGE_MAX),
|
|
1973
|
+
attachments: z27.array(AttachmentIdSchema).max(ATTACHMENTS_MAX).optional(),
|
|
1727
1974
|
/** The request the reply answers, or null when the phone showed none: refused if the session's request is no longer that one. */
|
|
1728
1975
|
expect: PhoneExpectSchema.nullable(),
|
|
1729
1976
|
assertion: AssertionSchema.optional()
|
|
1730
1977
|
}),
|
|
1731
1978
|
/** OMP's `ask`: the answers to every question of the dialog `requestId`, in order; `cancel` declines it. Bounds as the local `session.answer`. */
|
|
1732
|
-
|
|
1733
|
-
type:
|
|
1979
|
+
z27.object({
|
|
1980
|
+
type: z27.literal("session.answer"),
|
|
1734
1981
|
op: OperationIdSchema,
|
|
1735
1982
|
id: sessionId,
|
|
1736
|
-
requestId:
|
|
1737
|
-
answers:
|
|
1738
|
-
cancel:
|
|
1983
|
+
requestId: z27.string().min(1).max(200),
|
|
1984
|
+
answers: z27.array(z27.object({ id: z27.string().max(200), selected: z27.array(z27.string().max(2e3)).max(100), text: z27.string().max(2e4).optional() })).max(50),
|
|
1985
|
+
cancel: z27.literal(true).optional(),
|
|
1739
1986
|
expect: PhoneExpectSchema
|
|
1740
1987
|
}),
|
|
1741
|
-
|
|
1988
|
+
z27.object({ type: z27.literal("session.interrupt"), op: OperationIdSchema, id: sessionId }),
|
|
1742
1989
|
/** Ends a live session as `/exit` would, killed as a last resort; answered with its `how`. Passkey, purpose `close`, over the session. */
|
|
1743
|
-
|
|
1990
|
+
z27.object({ type: z27.literal("session.close"), op: OperationIdSchema, id: sessionId, assertion: AssertionSchema.optional() }),
|
|
1744
1991
|
/** Starts an ended session again on its conversation. Passkey, purpose `act`, over the session. */
|
|
1745
|
-
|
|
1992
|
+
z27.object({ type: z27.literal("session.resume"), op: OperationIdSchema, id: sessionId, assertion: AssertionSchema.optional() }),
|
|
1746
1993
|
/** Changes a live session's model and effort, `null` for the agent's own default; answered by `operation.result` and, when refused, `session.model.failed`. */
|
|
1747
|
-
|
|
1994
|
+
z27.object({ type: z27.literal("session.model"), op: OperationIdSchema, id: sessionId, model: ModelIdSchema.nullable(), effort: EffortSchema.nullable() }),
|
|
1995
|
+
/** One message to several sessions (ids); answered by `operation.result` with `sent` and `refused`. */
|
|
1996
|
+
z27.object({ type: z27.literal("session.broadcast"), op: OperationIdSchema, sessions: z27.array(sessionId).min(1).max(BROADCAST_SESSIONS_MAX), text: z27.string().trim().min(1).max(MESSAGE_MAX) }).strict(),
|
|
1748
1997
|
/** The models an agent offers on the PC. */
|
|
1749
|
-
|
|
1998
|
+
z27.object({ type: z27.literal("models.list"), tool: ToolSchema }),
|
|
1999
|
+
/** The slash commands of a session's agent, for the « / » list of its field. */
|
|
2000
|
+
z27.object({ type: z27.literal("session.commands"), id: sessionId }),
|
|
1750
2001
|
/** The phone drops its passkey from the daemon. */
|
|
1751
|
-
|
|
2002
|
+
z27.object({ type: z27.literal("device.forget"), op: OperationIdSchema }),
|
|
1752
2003
|
/** The phone's push subscription, with the VAPID key it was made with (so a key change is seen). */
|
|
1753
|
-
|
|
1754
|
-
|
|
2004
|
+
z27.object({ type: z27.literal("push.subscribe"), op: OperationIdSchema, subscription: PushSubscriptionSchema, key: B64U2.length(87) }),
|
|
2005
|
+
z27.object({ type: z27.literal("push.unsubscribe"), op: OperationIdSchema }),
|
|
1755
2006
|
/** The phone's own chat with Ledge. */
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
2007
|
+
z27.object({ type: z27.literal("chat.ask"), qid: OperationIdSchema, text: z27.string().trim().max(MESSAGE_MAX), attachments: z27.array(AttachmentIdSchema).max(ATTACHMENTS_MAX).optional() }),
|
|
2008
|
+
z27.object({ type: z27.literal("chat.cancel"), qid: OperationIdSchema }),
|
|
2009
|
+
z27.object({ type: z27.literal("chat.reset") }),
|
|
2010
|
+
z27.object({ type: z27.literal("chat.history") }),
|
|
1760
2011
|
/** An image to store, sent alone before the message that names it; no `id`, and at most `ATTACHMENT_MAX` bytes. */
|
|
1761
|
-
AttachmentPutSchema.pick({ type: true, requestId: true }).extend({ image: ImageUploadSchema.extend({ data:
|
|
2012
|
+
AttachmentPutSchema.pick({ type: true, requestId: true }).extend({ image: ImageUploadSchema.extend({ data: z27.string().min(1).max(b64Max(ATTACHMENT_MAX)) }) }),
|
|
1762
2013
|
AttachmentGetSchema.pick({ type: true, requestId: true, attachment: true, preview: true }),
|
|
1763
|
-
|
|
2014
|
+
z27.object({ type: z27.literal("chat.relay"), op: OperationIdSchema, qid: OperationIdSchema }),
|
|
1764
2015
|
/** Takes the card's queued instruction back out of the queue, as the local `session.relay.cancel`. */
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
2016
|
+
z27.object({ type: z27.literal("chat.relay.cancel"), op: OperationIdSchema, qid: OperationIdSchema }),
|
|
2017
|
+
z27.object({ type: z27.literal("chat.launch"), op: OperationIdSchema, qid: OperationIdSchema, rows: CardRowsSchema.optional(), assertion: AssertionSchema.optional() }),
|
|
2018
|
+
z27.object({ type: z27.literal("chat.close"), op: OperationIdSchema, qid: OperationIdSchema, rows: CardRowsSchema.optional(), assertion: AssertionSchema.optional() }),
|
|
1768
2019
|
/** A row of the turn's task card: given by Ledge. */
|
|
1769
|
-
|
|
2020
|
+
z27.object({ type: z27.literal("chat.task"), op: OperationIdSchema, qid: OperationIdSchema, row: z27.number().int().min(0).max(MAX_PROPOSALS - 1) }),
|
|
1770
2021
|
/** Rows of the turn's generic card (action registry spec §5): `confirmed` as the local `chat.act`; a row whose action asks
|
|
1771
2022
|
* for a device check (`phone`) needs one assertion, purpose `act`, over the turn. */
|
|
1772
|
-
|
|
1773
|
-
type:
|
|
2023
|
+
z27.object({
|
|
2024
|
+
type: z27.literal("chat.act"),
|
|
1774
2025
|
op: OperationIdSchema,
|
|
1775
2026
|
qid: OperationIdSchema,
|
|
1776
2027
|
rows: CardRowsSchema.optional(),
|
|
1777
|
-
confirmed:
|
|
2028
|
+
confirmed: z27.literal(true).optional(),
|
|
1778
2029
|
assertion: AssertionSchema.optional(),
|
|
1779
2030
|
/** « Refuser », as the local `chat.act`: no passkey, no confirmation. */
|
|
1780
|
-
deny:
|
|
2031
|
+
deny: z27.literal(true).optional()
|
|
1781
2032
|
}),
|
|
1782
2033
|
/** « Ignorer » the turn's cards, or `undo` (« Revoir »), as the local `chat.ignore`. */
|
|
1783
|
-
|
|
2034
|
+
z27.object({ type: z27.literal("chat.ignore"), op: OperationIdSchema, qid: OperationIdSchema, undo: z27.literal(true).optional() }),
|
|
1784
2035
|
/** Puts a closed task Ledge gave away from « À traiter ». */
|
|
1785
|
-
|
|
2036
|
+
z27.object({ type: z27.literal("task.seen"), op: OperationIdSchema, task: z27.string().min(1).max(40) }),
|
|
2037
|
+
/** Streams a session's pane, read-only; answered by `term.opened` or `operation.result` (rejected). */
|
|
2038
|
+
z27.object({ type: z27.literal("term.open"), op: OperationIdSchema, id: sessionId }),
|
|
2039
|
+
/** « Prendre la main »: passkey, purpose `terminal`, over the session; answered by `term.held` or `operation.result` (rejected). */
|
|
2040
|
+
z27.object({ type: z27.literal("term.hold"), op: OperationIdSchema, tid: TerminalIdSchema, assertion: AssertionSchema.optional() }),
|
|
2041
|
+
/** Keys to type, as xterm produced them; `n` counts from 0 for the terminal, so a duplicate is dropped and a gap seen. Refused unless held. */
|
|
2042
|
+
z27.object({ type: z27.literal("term.input"), tid: TerminalIdSchema, n: z27.number().int().nonnegative(), data: z27.string().min(1).max(TERM_INPUT_MAX) }),
|
|
2043
|
+
/** « Rendre la main »: back to read-only. */
|
|
2044
|
+
z27.object({ type: z27.literal("term.release"), tid: TerminalIdSchema }),
|
|
2045
|
+
/** The phone saw a gap in `seq`: a fresh `term.opened`, please. */
|
|
2046
|
+
z27.object({ type: z27.literal("term.resync"), tid: TerminalIdSchema }),
|
|
2047
|
+
z27.object({ type: z27.literal("term.close"), tid: TerminalIdSchema }),
|
|
1786
2048
|
/** Whether the phone's app is in front: a device that is visible gets no push. */
|
|
1787
|
-
|
|
2049
|
+
z27.object({ type: z27.literal("presence"), visible: z27.boolean() }),
|
|
1788
2050
|
/** Installs `version` on the PC, then restarts its daemon; the phone asks for its own build's version. Passkey, purpose `pc`. */
|
|
1789
|
-
|
|
2051
|
+
z27.object({ type: z27.literal("daemon.update"), ...pcOp, version: z27.string().regex(SEMVER) }).strict(),
|
|
1790
2052
|
/** Restarts the PC's daemon without installing anything. Passkey, purpose `pc`. */
|
|
1791
|
-
|
|
2053
|
+
z27.object({ type: z27.literal("daemon.restart"), ...pcOp }).strict()
|
|
1792
2054
|
]);
|
|
1793
|
-
var PhoneModelChoiceSchema =
|
|
2055
|
+
var PhoneModelChoiceSchema = z27.object({
|
|
1794
2056
|
id: ModelChoiceSchema.shape.id.max(120),
|
|
1795
|
-
label:
|
|
1796
|
-
description:
|
|
1797
|
-
efforts:
|
|
2057
|
+
label: z27.string().max(80),
|
|
2058
|
+
description: z27.string().max(200).optional(),
|
|
2059
|
+
efforts: z27.array(EffortSchema).max(8).optional(),
|
|
1798
2060
|
defaultEffort: EffortSchema.optional(),
|
|
1799
|
-
isDefault:
|
|
2061
|
+
isDefault: z27.boolean().optional()
|
|
1800
2062
|
}).strict();
|
|
1801
|
-
var
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
2063
|
+
var PHONE_COMMANDS_MAX = 300;
|
|
2064
|
+
var PhoneSlashCommandSchema = SlashCommandSchema.extend({ description: z27.string().max(200).optional() }).strict();
|
|
2065
|
+
var PhoneServerMessageSchema = z27.discriminatedUnion("type", [
|
|
2066
|
+
z27.object({ type: z27.literal("snapshot"), protocol: z27.literal(PHONE_PROTOCOL), sessions: z27.array(PhoneSessionSchema), capabilities: CapabilitiesByToolSchema, daemon: z27.object({
|
|
2067
|
+
version: z27.string(),
|
|
2068
|
+
name: z27.string().max(40),
|
|
2069
|
+
boot: z27.string().uuid().optional(),
|
|
1806
2070
|
/** The daemon takes `session.model`. */
|
|
1807
|
-
models:
|
|
2071
|
+
models: z27.literal(true).optional(),
|
|
1808
2072
|
/** The daemon sends `session.checklist` and a session's `checklist` summary. */
|
|
1809
|
-
checklist:
|
|
2073
|
+
checklist: z27.literal(true).optional(),
|
|
1810
2074
|
/** `update`: the daemon can install a version and restart (npm, systemd); `restart`: it can restart (systemd). */
|
|
1811
|
-
update:
|
|
1812
|
-
restart:
|
|
2075
|
+
update: z27.literal(true).optional(),
|
|
2076
|
+
restart: z27.literal(true).optional(),
|
|
1813
2077
|
/** The daemon takes `session.close` and `session.resume`. */
|
|
1814
|
-
sessionClose:
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
2078
|
+
sessionClose: z27.literal(true).optional(),
|
|
2079
|
+
/** The daemon streams a session's terminal (`term.open`). */
|
|
2080
|
+
terminal: z27.literal(true).optional(),
|
|
2081
|
+
/** The PC lets a phone type into a terminal (`term.hold`): its setting is on. */
|
|
2082
|
+
terminalInput: z27.literal(true).optional(),
|
|
2083
|
+
/** The daemon answers `session.commands`. */
|
|
2084
|
+
commands: z27.literal(true).optional(),
|
|
2085
|
+
/** The daemon takes `session.broadcast` (« Écrire au groupe »). */
|
|
2086
|
+
broadcast: z27.literal(true).optional()
|
|
2087
|
+
}), device: z27.object({ confirm: z27.boolean(), push: z27.enum(["on", "off", "stale", "unavailable"]).optional(), chat: z27.literal(true).optional(), contextNear: ContextPrefsSchema.shape.near.optional() }).optional() }),
|
|
2088
|
+
z27.object({ type: z27.literal("confirm.challenge"), purpose: ConfirmPurposeSchema, challenge: B64U2.max(64), rpId: z27.string().max(253), credentialId: B64U2.max(1366).optional(), expiresInMs: z27.number().int().positive() }),
|
|
2089
|
+
z27.object({ type: z27.literal("session.updated"), session: PhoneSessionSchema }),
|
|
2090
|
+
z27.object({ type: z27.literal("session.removed"), id: z27.string() }),
|
|
1819
2091
|
/** A session's checklist, sent to the phone that watches it; `null`: it has none (any more). */
|
|
1820
|
-
|
|
2092
|
+
z27.object({ type: z27.literal("session.checklist"), id: z27.string(), checklist: PhoneChecklistSchema.nullable() }),
|
|
1821
2093
|
/** `pending.cut` is set when even this body was shortened. */
|
|
1822
|
-
|
|
1823
|
-
|
|
2094
|
+
z27.object({ type: z27.literal("session.pending"), id: z27.string(), pending: PhonePendingSchema }),
|
|
2095
|
+
z27.object({ type: z27.literal("session.preview"), id: z27.string(), lines: z27.array(z27.string()) }),
|
|
1824
2096
|
/** `cut`: the ids of the items shortened; `more`: older items were left out. */
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
type:
|
|
2097
|
+
z27.object({ type: z27.literal("session.thread"), id: z27.string(), items: z27.array(ThreadItemSchema), cut: z27.array(z27.string()), more: z27.boolean() }),
|
|
2098
|
+
z27.object({ type: z27.literal("session.thread.item"), id: z27.string(), item: ThreadItemSchema, cut: z27.literal(true).optional() }),
|
|
2099
|
+
z27.object({
|
|
2100
|
+
type: z27.literal("operation.result"),
|
|
1829
2101
|
op: OperationIdSchema,
|
|
1830
2102
|
status: OperationStatusSchema,
|
|
1831
2103
|
reason: OperationReasonSchema.optional(),
|
|
1832
2104
|
/** How an applied `session.close` ended the agent: `killed` when it had to be forced. */
|
|
1833
|
-
how: CloseHowSchema.optional()
|
|
2105
|
+
how: CloseHowSchema.optional(),
|
|
2106
|
+
/** An applied `session.broadcast`: the names that got it and those that did not. */
|
|
2107
|
+
sent: z27.array(z27.string().max(100)).max(BROADCAST_SESSIONS_MAX).optional(),
|
|
2108
|
+
refused: z27.array(z27.object({ name: z27.string().max(100), reason: z27.string().max(200) })).max(BROADCAST_SESSIONS_MAX).optional()
|
|
1834
2109
|
}),
|
|
1835
2110
|
/** The models an agent offers; `error`: it could not be asked, `models` is then what Ledge knows without it. */
|
|
1836
|
-
|
|
2111
|
+
z27.object({ type: z27.literal("models.listing"), tool: ToolSchema, models: z27.array(PhoneModelChoiceSchema).max(40), error: z27.literal(true).optional() }),
|
|
1837
2112
|
/** A model change that was accepted and then failed (the agent could not restart). */
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
2113
|
+
z27.object({ type: z27.literal("session.model.failed"), id: z27.string(), reason: ModelRefusalSchema }),
|
|
2114
|
+
/** A session's slash commands; `error`: they could not be read (the agent is not listed, or did not answer in time): a command typed by hand still goes. */
|
|
2115
|
+
z27.object({ type: z27.literal("session.commands.listing"), id: z27.string(), commands: z27.array(PhoneSlashCommandSchema).max(PHONE_COMMANDS_MAX), error: z27.literal(true).optional() }),
|
|
2116
|
+
z27.object({ type: z27.literal("chat.history"), turns: z27.array(PhoneChatTurnSchema).max(50) }),
|
|
2117
|
+
z27.object({ type: z27.literal("chat.step"), qid: OperationIdSchema, step: PhoneChatStepSchema }),
|
|
1841
2118
|
/** The whole text so far, not an increment. */
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
2119
|
+
z27.object({ type: z27.literal("chat.delta"), qid: OperationIdSchema, text: z27.string().max(THREAD_TEXT_MAX) }),
|
|
2120
|
+
z27.object({ type: z27.literal("chat.done"), qid: OperationIdSchema, turn: PhoneChatTurnSchema }),
|
|
2121
|
+
z27.object({ type: z27.literal("chat.error"), qid: OperationIdSchema, code: ChatErrorCodeSchema }),
|
|
1845
2122
|
/** The daemon updating or restarting itself, whoever asked: npm runs, then the service restarts (the link drops right after). */
|
|
1846
|
-
|
|
2123
|
+
z27.object({ type: z27.literal("daemon.progress"), version: z27.string().max(40), step: z27.enum(["installing", "restarting"]) }),
|
|
1847
2124
|
/** Nothing was changed. Never npm's output nor a command: both hold the PC's paths. */
|
|
1848
|
-
|
|
2125
|
+
z27.object({ type: z27.literal("daemon.failed"), version: z27.string().max(40), reason: UpdateFailureSchema }),
|
|
1849
2126
|
/** The tasks Ledge gave, open or closed lately, after each snapshot; then each change. */
|
|
1850
|
-
|
|
1851
|
-
|
|
2127
|
+
z27.object({ type: z27.literal("tasks"), tasks: z27.array(PhoneTaskSchema).max(200) }),
|
|
2128
|
+
z27.object({ type: z27.literal("task.updated"), task: PhoneTaskSchema }),
|
|
1852
2129
|
AttachmentStoredSchema,
|
|
1853
2130
|
AttachmentDataSchema,
|
|
1854
2131
|
AttachmentErrorSchema,
|
|
2132
|
+
/** A terminal's first screen (also the answer to `term.resync`, then without `op`): the pane's size, the cursor, and the `seq` the next output takes. */
|
|
2133
|
+
z27.object({
|
|
2134
|
+
type: z27.literal("term.opened"),
|
|
2135
|
+
op: OperationIdSchema.optional(),
|
|
2136
|
+
tid: TerminalIdSchema,
|
|
2137
|
+
id: z27.string(),
|
|
2138
|
+
cols: TermDimSchema,
|
|
2139
|
+
rows: TermDimSchema,
|
|
2140
|
+
screen: z27.string().max(TERM_SCREEN_MAX),
|
|
2141
|
+
cursor: z27.object({ x: z27.number().int().nonnegative(), y: z27.number().int().nonnegative() }),
|
|
2142
|
+
seq: z27.number().int().nonnegative(),
|
|
2143
|
+
/** Read-only for good: a subagent's page shows its parent's pane. */
|
|
2144
|
+
readOnly: z27.literal(true).optional()
|
|
2145
|
+
}),
|
|
2146
|
+
/** What the pane printed since the last one; `seq` + 1 each time. */
|
|
2147
|
+
z27.object({ type: z27.literal("term.output"), tid: TerminalIdSchema, seq: z27.number().int().nonnegative(), data: z27.string().min(1).max(TERM_OUTPUT_MAX) }),
|
|
2148
|
+
z27.object({ type: z27.literal("term.size"), tid: TerminalIdSchema, cols: TermDimSchema, rows: TermDimSchema }),
|
|
2149
|
+
/** The phone holds the hand until `until`, pushed back by each key (5 min), never past 30 min from the hold. */
|
|
2150
|
+
z27.object({ type: z27.literal("term.held"), tid: TerminalIdSchema, until: z27.string().datetime() }),
|
|
2151
|
+
z27.object({ type: z27.literal("term.released"), tid: TerminalIdSchema, why: TermReleaseWhySchema }),
|
|
2152
|
+
z27.object({ type: z27.literal("term.exit"), tid: TerminalIdSchema, why: TermExitWhySchema }),
|
|
1855
2153
|
/** Never carries free text. */
|
|
1856
|
-
|
|
2154
|
+
z27.object({ type: z27.literal("error"), code: z27.enum(["invalid", "unknown_session", "too_large"]), request: z27.string().max(40).optional() })
|
|
1857
2155
|
]);
|
|
1858
2156
|
|
|
1859
2157
|
// src/imageInfo.ts
|