@paradow/ledge 0.37.0 → 0.38.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 +3740 -1624
- package/dist/cli/reduceWorker.js +1045 -706
- package/package.json +1 -1
package/dist/cli/reduceWorker.js
CHANGED
|
@@ -733,7 +733,7 @@ var BROADCAST_SESSIONS_MAX = 32;
|
|
|
733
733
|
var BROADCAST_LABEL_MAX = 120;
|
|
734
734
|
|
|
735
735
|
// ../protocol/src/messages.ts
|
|
736
|
-
import { z as
|
|
736
|
+
import { z as z24 } from "zod";
|
|
737
737
|
|
|
738
738
|
// ../protocol/src/repos.ts
|
|
739
739
|
import { z as z15 } from "zod";
|
|
@@ -943,264 +943,527 @@ var OpenRigReseatedSchema = z18.object({
|
|
|
943
943
|
links: z18.number().int().optional(),
|
|
944
944
|
linksUnsupported: z18.boolean().optional()
|
|
945
945
|
});
|
|
946
|
+
var HumanQuestionAnswerSchema = z18.discriminatedUnion("kind", [
|
|
947
|
+
z18.object({ kind: z18.literal("option"), id: z18.string() }),
|
|
948
|
+
z18.object({ kind: z18.literal("words"), text: z18.string() })
|
|
949
|
+
]);
|
|
950
|
+
var HumanQuestionSchema = z18.object({
|
|
951
|
+
id: z18.string(),
|
|
952
|
+
question: z18.string(),
|
|
953
|
+
options: z18.array(z18.object({ id: z18.string(), label: z18.string(), recommended: z18.boolean() })),
|
|
954
|
+
answer: HumanQuestionAnswerSchema.nullable()
|
|
955
|
+
});
|
|
956
|
+
var TeamProposalSchema = z18.object({
|
|
957
|
+
/** `existing`: the goal goes to a team that exists; `launch`: a new rig is launched, with what that does in `launch`. */
|
|
958
|
+
kind: z18.enum(["existing", "launch"]),
|
|
959
|
+
rig: z18.string(),
|
|
960
|
+
running: z18.boolean().nullable(),
|
|
961
|
+
folder: z18.string().nullable(),
|
|
962
|
+
seats: z18.array(z18.object({ seat: z18.string(), name: z18.string().nullable(), role: z18.string().nullable(), note: z18.string().nullable() })),
|
|
963
|
+
reason: z18.string().nullable(),
|
|
964
|
+
/** For a launch, before any yes: agents, runtimes, `rig up --plan`'s summary, what it writes in the folder, the seats' permissions. */
|
|
965
|
+
launch: z18.object({ agents: z18.number().int().nullable(), runtimes: z18.array(z18.string()), plan: z18.string().nullable(), writes: z18.array(z18.string()), permissions: z18.string().nullable() }).nullable()
|
|
966
|
+
});
|
|
967
|
+
var HumanAskSchema = z18.object({
|
|
968
|
+
id: z18.string(),
|
|
969
|
+
kind: z18.enum(["decision", "update", "parked"]),
|
|
970
|
+
alert: z18.boolean(),
|
|
971
|
+
priority: z18.enum(["routine", "urgent", "critical"]),
|
|
972
|
+
from: z18.object({ seat: z18.string(), rig: z18.string().nullable(), role: z18.string().nullable() }),
|
|
973
|
+
summary: z18.string(),
|
|
974
|
+
body: z18.string(),
|
|
975
|
+
bodyTruncated: z18.boolean(),
|
|
976
|
+
detail: z18.string().nullable(),
|
|
977
|
+
/** The detail was cut (on the phone, to fit its message): its end, which may decide, is not shown. */
|
|
978
|
+
detailTruncated: z18.literal(true).optional(),
|
|
979
|
+
evidenceRef: z18.string().nullable(),
|
|
980
|
+
questions: z18.array(HumanQuestionSchema),
|
|
981
|
+
createdAt: z18.string(),
|
|
982
|
+
updatedAt: z18.string(),
|
|
983
|
+
/** Max's proposal for this goal (the ask's `ledge-goal:<id>` tag), and the team it names when its block reads. */
|
|
984
|
+
goalId: z18.string().optional(),
|
|
985
|
+
team: TeamProposalSchema.optional()
|
|
986
|
+
});
|
|
987
|
+
var HumanChannelSchema = z18.object({
|
|
988
|
+
/** `no_default`: several people are registered and none is the default, so whose asks they are is unknown (nothing is shown). */
|
|
989
|
+
state: z18.enum(["ready", "no_person", "no_default", "openrig_stopped", "not_installed", "unreachable"]),
|
|
990
|
+
person: z18.object({ address: z18.string(), name: z18.string() }).nullable(),
|
|
991
|
+
/** Slack still posts the person's asks too. */
|
|
992
|
+
slack: z18.boolean(),
|
|
993
|
+
message: z18.string().optional()
|
|
994
|
+
});
|
|
995
|
+
var OpenRigAsksSchema = z18.object({ type: z18.literal("openrig.asks"), requestId: z18.string().min(1).max(100), machineId: z18.string().optional() });
|
|
996
|
+
var OpenRigAsksChangedSchema = z18.object({
|
|
997
|
+
type: z18.literal("openrig.asksChanged"),
|
|
998
|
+
requestId: z18.string().optional(),
|
|
999
|
+
machineId: z18.string().optional(),
|
|
1000
|
+
channel: HumanChannelSchema.optional(),
|
|
1001
|
+
asks: z18.array(HumanAskSchema),
|
|
1002
|
+
truncated: z18.boolean().optional(),
|
|
1003
|
+
/** A read failed (OpenRig unreachable, a row or a source not read): the asks known before are kept; none missing is settled. */
|
|
1004
|
+
partial: z18.boolean().optional(),
|
|
1005
|
+
error: z18.enum(["offline", "outdated"]).optional()
|
|
1006
|
+
});
|
|
1007
|
+
var HumanAnswerSchema = z18.discriminatedUnion("kind", [
|
|
1008
|
+
z18.object({ kind: z18.literal("choice"), questionId: z18.string().min(1).max(200), optionId: z18.string().min(1).max(200) }),
|
|
1009
|
+
z18.object({ kind: z18.literal("words"), text: z18.string().trim().min(1).max(8e3) }),
|
|
1010
|
+
z18.object({ kind: z18.literal("seen") }),
|
|
1011
|
+
z18.object({ kind: z18.literal("resolve"), text: z18.string().trim().min(1).max(8e3) })
|
|
1012
|
+
]);
|
|
1013
|
+
var OpenRigAnswerSchema = z18.object({
|
|
1014
|
+
type: z18.literal("openrig.answer"),
|
|
1015
|
+
requestId: z18.string().min(1).max(100),
|
|
1016
|
+
machineId: z18.string().optional(),
|
|
1017
|
+
qitemId: z18.string().min(1).max(200),
|
|
1018
|
+
answer: HumanAnswerSchema
|
|
1019
|
+
});
|
|
1020
|
+
var HumanAnswerErrorSchema = z18.enum(["not_found", "closed", "refused", "not_closed", "unreachable", "openrig_stopped", "uncertain", "offline", "outdated"]);
|
|
1021
|
+
var OpenRigAnsweredSchema = z18.object({
|
|
1022
|
+
type: z18.literal("openrig.answered"),
|
|
1023
|
+
requestId: z18.string(),
|
|
1024
|
+
machineId: z18.string().optional(),
|
|
1025
|
+
qitemId: z18.string(),
|
|
1026
|
+
ok: z18.boolean(),
|
|
1027
|
+
outcome: z18.enum(["recorded", "answered", "already"]).optional(),
|
|
1028
|
+
remaining: z18.array(z18.string()).optional(),
|
|
1029
|
+
replyId: z18.string().optional(),
|
|
1030
|
+
error: HumanAnswerErrorSchema.optional(),
|
|
1031
|
+
message: z18.string().optional()
|
|
1032
|
+
});
|
|
1033
|
+
var GoalErrorSchema = z18.enum(["no_operator", "openrig_stopped", "unreachable", "uncertain", "refused", "offline", "outdated"]);
|
|
1034
|
+
var GoalIdSchema = z18.string().regex(/^[A-Za-z0-9-]{8,64}$/, "not a goal id");
|
|
1035
|
+
var GoalSchema = z18.object({
|
|
1036
|
+
goalId: z18.string(),
|
|
1037
|
+
qitemId: z18.string(),
|
|
1038
|
+
text: z18.string(),
|
|
1039
|
+
folder: z18.string().nullable(),
|
|
1040
|
+
sentAt: z18.string(),
|
|
1041
|
+
to: z18.string(),
|
|
1042
|
+
state: z18.enum(["sent", "taken", "asking", "proposed", "running", "done", "closed"]),
|
|
1043
|
+
/** The latest proposal: after « Changer d'équipe » Max makes a new one. */
|
|
1044
|
+
proposal: z18.object({
|
|
1045
|
+
askId: z18.string(),
|
|
1046
|
+
team: TeamProposalSchema.nullable(),
|
|
1047
|
+
text: z18.string(),
|
|
1048
|
+
answer: HumanQuestionAnswerSchema.nullable(),
|
|
1049
|
+
answeredAt: z18.string().nullable()
|
|
1050
|
+
}).nullable(),
|
|
1051
|
+
/** Max's question still open (no folder, no fitting team): it waits in « À toi ». */
|
|
1052
|
+
question: z18.object({ askId: z18.string(), text: z18.string() }).nullable(),
|
|
1053
|
+
trail: z18.array(z18.object({
|
|
1054
|
+
at: z18.string(),
|
|
1055
|
+
kind: z18.enum(["sent", "taken", "note", "handed", "closed"]),
|
|
1056
|
+
seat: z18.string(),
|
|
1057
|
+
to: z18.string().optional(),
|
|
1058
|
+
text: z18.string().optional(),
|
|
1059
|
+
qitemId: z18.string()
|
|
1060
|
+
})),
|
|
1061
|
+
current: z18.object({ qitemId: z18.string(), seat: z18.string(), state: z18.string() }).nullable()
|
|
1062
|
+
});
|
|
1063
|
+
var OpenRigGoalSendSchema = z18.object({
|
|
1064
|
+
type: z18.literal("openrig.goal.send"),
|
|
1065
|
+
requestId: z18.string().min(1).max(100),
|
|
1066
|
+
machineId: z18.string().optional(),
|
|
1067
|
+
goalId: GoalIdSchema,
|
|
1068
|
+
text: z18.string().trim().min(1).max(8e3),
|
|
1069
|
+
folder: z18.string().min(1).max(4096).optional()
|
|
1070
|
+
});
|
|
1071
|
+
var OpenRigGoalSentSchema = z18.object({
|
|
1072
|
+
type: z18.literal("openrig.goal.sent"),
|
|
1073
|
+
requestId: z18.string(),
|
|
1074
|
+
machineId: z18.string().optional(),
|
|
1075
|
+
goalId: z18.string(),
|
|
1076
|
+
ok: z18.boolean(),
|
|
1077
|
+
qitemId: z18.string().optional(),
|
|
1078
|
+
error: GoalErrorSchema.optional(),
|
|
1079
|
+
message: z18.string().optional()
|
|
1080
|
+
});
|
|
1081
|
+
var OpenRigGoalsSchema = z18.object({ type: z18.literal("openrig.goals"), requestId: z18.string().min(1).max(100), machineId: z18.string().optional() });
|
|
1082
|
+
var OpenRigGoalsChangedSchema = z18.object({
|
|
1083
|
+
type: z18.literal("openrig.goalsChanged"),
|
|
1084
|
+
requestId: z18.string().optional(),
|
|
1085
|
+
machineId: z18.string().optional(),
|
|
1086
|
+
goals: z18.array(GoalSchema),
|
|
1087
|
+
partial: z18.boolean().optional(),
|
|
1088
|
+
error: z18.enum(["offline", "outdated"]).optional()
|
|
1089
|
+
});
|
|
1090
|
+
var OpenRigGoalReplySchema = z18.object({
|
|
1091
|
+
type: z18.literal("openrig.goal.reply"),
|
|
1092
|
+
requestId: z18.string().min(1).max(100),
|
|
1093
|
+
machineId: z18.string().optional(),
|
|
1094
|
+
goalId: GoalIdSchema,
|
|
1095
|
+
replyId: GoalIdSchema,
|
|
1096
|
+
text: z18.string().trim().min(1).max(8e3)
|
|
1097
|
+
});
|
|
1098
|
+
var OpenRigGoalRepliedSchema = z18.object({
|
|
1099
|
+
type: z18.literal("openrig.goal.replied"),
|
|
1100
|
+
requestId: z18.string(),
|
|
1101
|
+
machineId: z18.string().optional(),
|
|
1102
|
+
goalId: z18.string(),
|
|
1103
|
+
/** The reply's own id, always: sending it again after `uncertain` reuses it, and never wakes Max twice. */
|
|
1104
|
+
replyId: z18.string(),
|
|
1105
|
+
ok: z18.boolean(),
|
|
1106
|
+
qitemId: z18.string().optional(),
|
|
1107
|
+
error: GoalErrorSchema.optional(),
|
|
1108
|
+
message: z18.string().optional()
|
|
1109
|
+
});
|
|
946
1110
|
|
|
947
|
-
// ../protocol/src/
|
|
1111
|
+
// ../protocol/src/rigMessages.ts
|
|
948
1112
|
import { z as z19 } from "zod";
|
|
949
|
-
var
|
|
950
|
-
var
|
|
951
|
-
var
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
1113
|
+
var QitemIdSchema = z19.string().regex(/^qitem-[A-Za-z0-9-]{1,120}$/, "not a qitem");
|
|
1114
|
+
var QITEM = "qitem-[A-Za-z0-9-]+";
|
|
1115
|
+
var RigKindSchema = z19.enum(["message", "handoff", "unblocked", "openrig", "unreadable"]);
|
|
1116
|
+
var RigInfoSchema = z19.object({
|
|
1117
|
+
kind: RigKindSchema,
|
|
1118
|
+
/** The envelope's From, as written (`pilot-lead@ledge-dev-2`, `daemon@kernel`, `ledge@external`, `<unknown sender>`).
|
|
1119
|
+
* On the seat's own `sent`: absent (it is the seat). The item's `peer` is the Ledge session of that seat, when known. */
|
|
1120
|
+
from: z19.string().optional(),
|
|
1121
|
+
/** The envelope's To, as written (a seat, a list `a, b`, or `broadcast to <rig> (n seats)`); on an outgoing item, the
|
|
1122
|
+
* destination the command named. */
|
|
1123
|
+
to: z19.string().optional(),
|
|
1124
|
+
/** The envelope's `Sent:` stamp as an ISO time (OpenRig gives no year: the year of receipt, or the year before when
|
|
1125
|
+
* that would put it in the future). Absent when the envelope has no `Sent:` line. */
|
|
1126
|
+
sentAt: z19.string().datetime().optional(),
|
|
1127
|
+
/** The sender's generation (8 hex), when the stamp carries one. */
|
|
1128
|
+
gen: z19.string().optional(),
|
|
1129
|
+
/** The row the item is about. `handoff`: the row handed (outgoing: the new row, when the command's output names it);
|
|
1130
|
+
* `unblocked`: the parked row now pending; `openrig`: the row it names (a stuck sweep: the stalled row). */
|
|
1131
|
+
qitem: QitemIdSchema.optional(),
|
|
1132
|
+
/** `handoff`: the row's summary as OpenRig carried it (one line, at most 120 characters, `…` when cut). */
|
|
1133
|
+
summary: z19.string().optional(),
|
|
1134
|
+
/** `unblocked`: the row whose closing unblocked it. */
|
|
1135
|
+
blocker: QitemIdSchema.optional(),
|
|
1136
|
+
/** `openrig`: the recovery row OpenRig opened for it (a stuck sweep comes as a handoff of a `qitem-recovery-…` row). */
|
|
1137
|
+
recovery: QitemIdSchema.optional(),
|
|
1138
|
+
/** `openrig`: `stalled` a stuck sweep (a seat that does not move: amber), `custody` a custody check; else absent. */
|
|
1139
|
+
alert: z19.enum(["stalled", "custody"]).optional(),
|
|
1140
|
+
/** `openrig` with an alert: how long, in minutes, as OpenRig said. */
|
|
1141
|
+
minutes: z19.number().int().nonnegative().optional(),
|
|
1142
|
+
/** The seat's own command (an outgoing `sent` or `handoff`): the Bash command, as run. */
|
|
1143
|
+
command: z19.string().optional(),
|
|
1144
|
+
/** OpenRig said the delivery may repeat (`Recovered queue notification`). */
|
|
1145
|
+
recovered: z19.literal(true).optional(),
|
|
1146
|
+
/** Received that many times (2 or more): the same block came again, and this item stands for all of them. */
|
|
1147
|
+
count: z19.number().int().min(2).optional(),
|
|
1148
|
+
/** Blocks pasted together in one prompt: the prompt's id, this block's place and how many there were. The front shows
|
|
1149
|
+
* one raw for the packet, the raws of its items joined. */
|
|
1150
|
+
packet: z19.object({ id: z19.string().min(1), index: z19.number().int().nonnegative(), count: z19.number().int().min(2) }).optional(),
|
|
1151
|
+
/** The block exactly as received (every delivery, separated by a blank line, when `count` is set); outgoing: the command
|
|
1152
|
+
* and what it printed. Always there. */
|
|
1153
|
+
raw: z19.string()
|
|
1154
|
+
});
|
|
1155
|
+
var HANDOFF = new RegExp(`^Queue handoff: (${QITEM}) - check your queue\\.(?: Summary: (.*))?$`);
|
|
1156
|
+
var BLOCKER = new RegExp(`^Blocker (${QITEM}) resolved; parked qitem (${QITEM}) is pending\\.`);
|
|
1157
|
+
var STALLED = new RegExp(`^Stuck sweep: \\S+ on (${QITEM}) \\((\\d+) min\\)`);
|
|
1158
|
+
var CUSTODY = new RegExp(`^Custody not checked\\b.*?(?:for |of )(${QITEM}).*?\\((\\d+) min\\)`);
|
|
1159
|
+
var RigRowSchema = z19.object({
|
|
1160
|
+
id: QitemIdSchema,
|
|
1161
|
+
state: z19.string().min(1),
|
|
1162
|
+
summary: z19.string().optional(),
|
|
1163
|
+
/** Who made it and who holds it (seats). */
|
|
1164
|
+
from: z19.string(),
|
|
1165
|
+
to: z19.string(),
|
|
1166
|
+
/** `handed-off`: the seat it went to. */
|
|
1167
|
+
handedOffTo: z19.string().optional(),
|
|
1168
|
+
/** `blocked`: the row it waits on. */
|
|
1169
|
+
blockedOn: z19.string().optional(),
|
|
1170
|
+
createdAt: z19.string(),
|
|
1171
|
+
claimedAt: z19.string().optional(),
|
|
1172
|
+
updatedAt: z19.string(),
|
|
1173
|
+
/** When Ledge read it. */
|
|
1174
|
+
at: z19.string()
|
|
1175
|
+
});
|
|
1176
|
+
var OpenRigRowSchema = z19.object({ type: z19.literal("openrig.row"), requestId: z19.string().min(1).max(100), qitem: QitemIdSchema, machineId: z19.string().optional() });
|
|
1177
|
+
var OpenRigRowStateSchema = z19.object({
|
|
1178
|
+
type: z19.literal("openrig.rowState"),
|
|
1179
|
+
requestId: z19.string(),
|
|
1180
|
+
machineId: z19.string().optional(),
|
|
1181
|
+
qitem: z19.string(),
|
|
1182
|
+
row: RigRowSchema.optional(),
|
|
1183
|
+
error: z19.string().optional()
|
|
1184
|
+
});
|
|
1185
|
+
|
|
1186
|
+
// ../protocol/src/openrigRoles.ts
|
|
1187
|
+
import { z as z20 } from "zod";
|
|
1188
|
+
var RoleUnknownWhySchema = z20.enum(["resolved_at_launch", "not_exposed", "unreadable", "ambiguous", "not_found", "missing_from_guidance"]);
|
|
1189
|
+
var RoleFromSchema = z20.enum(["import", "defaults", "profile", "rig", "pod", "member", "launch", "openrig", "plugin", "seat"]);
|
|
1190
|
+
var OriginSchema = z20.discriminatedUnion("kind", [
|
|
1191
|
+
z20.object({ kind: z20.literal("here"), key: z20.string().optional() }),
|
|
1192
|
+
z20.object({ kind: z20.literal("inherited"), from: RoleFromSchema, ref: z20.string().optional(), key: z20.string().optional(), path: z20.string().optional() }),
|
|
1193
|
+
z20.object({ kind: z20.literal("absent") }),
|
|
1194
|
+
z20.object({ kind: z20.literal("unknown"), why: RoleUnknownWhySchema, detail: z20.string().optional() })
|
|
956
1195
|
]);
|
|
957
|
-
var SourcedStringSchema =
|
|
958
|
-
var RoleResourceCategorySchema =
|
|
959
|
-
var RoleResourceSchema =
|
|
1196
|
+
var SourcedStringSchema = z20.object({ value: z20.string().nullable(), origin: OriginSchema });
|
|
1197
|
+
var RoleResourceCategorySchema = z20.enum(["skill", "guidance", "plugin", "subagent", "runtime_resource"]);
|
|
1198
|
+
var RoleResourceSchema = z20.object({
|
|
960
1199
|
category: RoleResourceCategorySchema,
|
|
961
|
-
id:
|
|
962
|
-
path:
|
|
963
|
-
absolutePath:
|
|
964
|
-
runtime:
|
|
965
|
-
type:
|
|
966
|
-
target:
|
|
967
|
-
plugin:
|
|
1200
|
+
id: z20.string(),
|
|
1201
|
+
path: z20.string().optional(),
|
|
1202
|
+
absolutePath: z20.string().optional(),
|
|
1203
|
+
runtime: z20.string().optional(),
|
|
1204
|
+
type: z20.string().optional(),
|
|
1205
|
+
target: z20.string().optional(),
|
|
1206
|
+
plugin: z20.object({ version: z20.string().optional(), skills: z20.array(z20.string()), hooks: z20.number().int().min(0) }).optional(),
|
|
968
1207
|
origin: OriginSchema
|
|
969
1208
|
});
|
|
970
|
-
var RoleUsesListSchema =
|
|
971
|
-
var StartupLayerSchema =
|
|
972
|
-
var StartupEntrySchema =
|
|
973
|
-
kind:
|
|
974
|
-
path:
|
|
975
|
-
absolutePath:
|
|
976
|
-
size:
|
|
977
|
-
value:
|
|
978
|
-
builtin:
|
|
1209
|
+
var RoleUsesListSchema = z20.object({ declared: z20.boolean(), key: z20.string(), items: z20.array(RoleResourceSchema) });
|
|
1210
|
+
var StartupLayerSchema = z20.enum(["agent", "profile", "culture", "rig", "pod", "member", "openrig", "seat", "unknown"]);
|
|
1211
|
+
var StartupEntrySchema = z20.object({
|
|
1212
|
+
kind: z20.enum(["file", "action"]),
|
|
1213
|
+
path: z20.string().optional(),
|
|
1214
|
+
absolutePath: z20.string().optional(),
|
|
1215
|
+
size: z20.number().int().min(0).optional(),
|
|
1216
|
+
value: z20.string().optional(),
|
|
1217
|
+
builtin: z20.string().optional(),
|
|
979
1218
|
layer: StartupLayerSchema,
|
|
980
|
-
layerInferred:
|
|
981
|
-
delivery:
|
|
982
|
-
into:
|
|
983
|
-
delivered:
|
|
984
|
-
required:
|
|
985
|
-
orientation:
|
|
986
|
-
appliesOn:
|
|
1219
|
+
layerInferred: z20.boolean(),
|
|
1220
|
+
delivery: z20.string(),
|
|
1221
|
+
into: z20.string().optional(),
|
|
1222
|
+
delivered: z20.boolean().optional(),
|
|
1223
|
+
required: z20.boolean().optional(),
|
|
1224
|
+
orientation: z20.string().optional(),
|
|
1225
|
+
appliesOn: z20.array(z20.string()),
|
|
987
1226
|
origin: OriginSchema
|
|
988
1227
|
});
|
|
989
|
-
var RoleFileErrorSchema =
|
|
990
|
-
var RoleSummarySchema =
|
|
991
|
-
id:
|
|
992
|
-
name:
|
|
993
|
-
version:
|
|
994
|
-
description:
|
|
995
|
-
source:
|
|
996
|
-
path:
|
|
997
|
-
runtime:
|
|
998
|
-
importOnly:
|
|
999
|
-
seats:
|
|
1000
|
-
running:
|
|
1001
|
-
skills:
|
|
1002
|
-
startupFiles:
|
|
1228
|
+
var RoleFileErrorSchema = z20.object({ kind: z20.enum(["missing", "invalid"]), message: z20.string(), line: z20.number().int().optional(), col: z20.number().int().optional() });
|
|
1229
|
+
var RoleSummarySchema = z20.object({
|
|
1230
|
+
id: z20.string(),
|
|
1231
|
+
name: z20.string(),
|
|
1232
|
+
version: z20.string().optional(),
|
|
1233
|
+
description: z20.string().optional(),
|
|
1234
|
+
source: z20.enum(["builtin", "user"]),
|
|
1235
|
+
path: z20.string(),
|
|
1236
|
+
runtime: z20.string().optional(),
|
|
1237
|
+
importOnly: z20.boolean(),
|
|
1238
|
+
seats: z20.number().int().min(0),
|
|
1239
|
+
running: z20.number().int().min(0),
|
|
1240
|
+
skills: z20.array(z20.string()),
|
|
1241
|
+
startupFiles: z20.array(z20.string()),
|
|
1003
1242
|
/** 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:
|
|
1005
|
-
error:
|
|
1006
|
-
});
|
|
1007
|
-
var RoleImportSchema =
|
|
1008
|
-
ref:
|
|
1009
|
-
key:
|
|
1010
|
-
name:
|
|
1011
|
-
path:
|
|
1012
|
-
version:
|
|
1013
|
-
resolved:
|
|
1014
|
-
error:
|
|
1015
|
-
});
|
|
1016
|
-
var RoleProfileSchema =
|
|
1017
|
-
name:
|
|
1018
|
-
description:
|
|
1243
|
+
seatRefs: z20.array(z20.object({ rig: z20.string(), logicalId: z20.string(), session: z20.string().optional() })),
|
|
1244
|
+
error: z20.enum(["missing", "invalid"]).optional()
|
|
1245
|
+
});
|
|
1246
|
+
var RoleImportSchema = z20.object({
|
|
1247
|
+
ref: z20.string(),
|
|
1248
|
+
key: z20.string(),
|
|
1249
|
+
name: z20.string().optional(),
|
|
1250
|
+
path: z20.string().optional(),
|
|
1251
|
+
version: z20.string().optional(),
|
|
1252
|
+
resolved: z20.boolean(),
|
|
1253
|
+
error: z20.string().optional()
|
|
1254
|
+
});
|
|
1255
|
+
var RoleProfileSchema = z20.object({
|
|
1256
|
+
name: z20.string(),
|
|
1257
|
+
description: z20.string().optional(),
|
|
1019
1258
|
runtime: SourcedStringSchema,
|
|
1020
1259
|
model: SourcedStringSchema,
|
|
1021
1260
|
effort: SourcedStringSchema,
|
|
1022
|
-
uses:
|
|
1261
|
+
uses: z20.object({ skills: RoleUsesListSchema, guidance: RoleUsesListSchema, subagents: RoleUsesListSchema, plugins: RoleUsesListSchema, runtimeResources: RoleUsesListSchema }),
|
|
1023
1262
|
/** The role's share of the startup: agent then profile files and actions, in that order. */
|
|
1024
|
-
startup:
|
|
1025
|
-
});
|
|
1026
|
-
var RoleSeatSchema =
|
|
1027
|
-
rig:
|
|
1028
|
-
rigId:
|
|
1029
|
-
rigSpecPath:
|
|
1030
|
-
composed:
|
|
1031
|
-
logicalId:
|
|
1032
|
-
session:
|
|
1033
|
-
label:
|
|
1034
|
-
profile:
|
|
1035
|
-
running:
|
|
1036
|
-
activity:
|
|
1263
|
+
startup: z20.array(StartupEntrySchema)
|
|
1264
|
+
});
|
|
1265
|
+
var RoleSeatSchema = z20.object({
|
|
1266
|
+
rig: z20.string(),
|
|
1267
|
+
rigId: z20.string(),
|
|
1268
|
+
rigSpecPath: z20.string().optional(),
|
|
1269
|
+
composed: z20.boolean(),
|
|
1270
|
+
logicalId: z20.string(),
|
|
1271
|
+
session: z20.string().optional(),
|
|
1272
|
+
label: z20.string().optional(),
|
|
1273
|
+
profile: z20.string(),
|
|
1274
|
+
running: z20.boolean(),
|
|
1275
|
+
activity: z20.string(),
|
|
1037
1276
|
runtime: SourcedStringSchema,
|
|
1038
1277
|
model: SourcedStringSchema,
|
|
1039
1278
|
effort: SourcedStringSchema,
|
|
1040
1279
|
cwd: SourcedStringSchema,
|
|
1041
1280
|
cultureFile: SourcedStringSchema,
|
|
1042
|
-
member:
|
|
1043
|
-
memberStartup:
|
|
1281
|
+
member: z20.string().optional(),
|
|
1282
|
+
memberStartup: z20.array(StartupEntrySchema),
|
|
1044
1283
|
/** The Role tab of this seat: its role guidance (`role.md`), then the startup files of its own (member layer), in delivery
|
|
1045
1284
|
* order — OpenRig's when the seat was launched, else the spec's. */
|
|
1046
|
-
roleDocs:
|
|
1047
|
-
live:
|
|
1048
|
-
resolvedSpec:
|
|
1049
|
-
resources:
|
|
1285
|
+
roleDocs: z20.array(StartupEntrySchema),
|
|
1286
|
+
live: z20.object({
|
|
1287
|
+
resolvedSpec: z20.object({ name: z20.string().optional(), version: z20.string().optional(), hash: z20.string().optional() }),
|
|
1288
|
+
resources: z20.array(RoleResourceSchema),
|
|
1050
1289
|
/** In the order OpenRig returns them. */
|
|
1051
|
-
startup:
|
|
1290
|
+
startup: z20.array(StartupEntrySchema),
|
|
1052
1291
|
/** `rig skill loadout --seat`: the skills projected for this seat; absent when it could not be read. */
|
|
1053
|
-
skills:
|
|
1292
|
+
skills: z20.array(z20.object({ id: z20.string(), trigger: z20.string().optional() })).optional()
|
|
1054
1293
|
}).optional()
|
|
1055
1294
|
});
|
|
1056
|
-
var RoleUnavailableSchema =
|
|
1057
|
-
var RoleDetailSchema =
|
|
1295
|
+
var RoleUnavailableSchema = z20.enum(["harness_prompt", "effective_settings", "delivered_content", "harness_own_files"]);
|
|
1296
|
+
var RoleDetailSchema = z20.object({
|
|
1058
1297
|
summary: RoleSummarySchema,
|
|
1059
|
-
file:
|
|
1060
|
-
path:
|
|
1061
|
-
relativePath:
|
|
1062
|
-
modifiedAt:
|
|
1063
|
-
copies:
|
|
1298
|
+
file: z20.object({
|
|
1299
|
+
path: z20.string(),
|
|
1300
|
+
relativePath: z20.string().optional(),
|
|
1301
|
+
modifiedAt: z20.string().optional(),
|
|
1302
|
+
copies: z20.array(z20.object({ kind: z20.literal("repo"), path: z20.string(), identical: z20.boolean(), modifiedAt: z20.string().optional() }))
|
|
1064
1303
|
}),
|
|
1065
1304
|
error: RoleFileErrorSchema.optional(),
|
|
1066
|
-
role:
|
|
1067
|
-
defaults:
|
|
1068
|
-
imports:
|
|
1069
|
-
importedBy:
|
|
1070
|
-
profiles:
|
|
1305
|
+
role: z20.object({ path: z20.string(), absolutePath: z20.string(), origin: OriginSchema }).nullable(),
|
|
1306
|
+
defaults: z20.object({ runtime: SourcedStringSchema, model: SourcedStringSchema, effort: SourcedStringSchema }),
|
|
1307
|
+
imports: z20.array(RoleImportSchema),
|
|
1308
|
+
importedBy: z20.array(z20.string()),
|
|
1309
|
+
profiles: z20.array(RoleProfileSchema),
|
|
1071
1310
|
/** What the file declares in `resources`. */
|
|
1072
|
-
resources:
|
|
1073
|
-
seats:
|
|
1074
|
-
unavailable:
|
|
1075
|
-
warnings:
|
|
1076
|
-
openrigVersion:
|
|
1077
|
-
});
|
|
1078
|
-
var RoleErrorSchema =
|
|
1079
|
-
var LibraryIdSchema =
|
|
1080
|
-
var OpenRigRolesSchema =
|
|
1081
|
-
var OpenRigRoleListSchema =
|
|
1082
|
-
type:
|
|
1083
|
-
requestId:
|
|
1084
|
-
machineId:
|
|
1085
|
-
roles:
|
|
1086
|
-
openrigVersion:
|
|
1311
|
+
resources: z20.array(RoleResourceSchema),
|
|
1312
|
+
seats: z20.array(RoleSeatSchema),
|
|
1313
|
+
unavailable: z20.array(RoleUnavailableSchema),
|
|
1314
|
+
warnings: z20.array(z20.string()),
|
|
1315
|
+
openrigVersion: z20.string().optional()
|
|
1316
|
+
});
|
|
1317
|
+
var RoleErrorSchema = z20.string();
|
|
1318
|
+
var LibraryIdSchema = z20.string().min(1).max(200).regex(/^[A-Za-z0-9][A-Za-z0-9._:-]*$/, "not a library id");
|
|
1319
|
+
var OpenRigRolesSchema = z20.object({ type: z20.literal("openrig.roles"), requestId: z20.string().min(1).max(100), machineId: z20.string().optional() });
|
|
1320
|
+
var OpenRigRoleListSchema = z20.object({
|
|
1321
|
+
type: z20.literal("openrig.roleList"),
|
|
1322
|
+
requestId: z20.string(),
|
|
1323
|
+
machineId: z20.string().optional(),
|
|
1324
|
+
roles: z20.array(RoleSummarySchema),
|
|
1325
|
+
openrigVersion: z20.string().optional(),
|
|
1087
1326
|
error: RoleErrorSchema.optional()
|
|
1088
1327
|
});
|
|
1089
|
-
var OpenRigRoleSchema =
|
|
1090
|
-
var OpenRigRoleDetailSchema =
|
|
1091
|
-
type:
|
|
1092
|
-
requestId:
|
|
1093
|
-
machineId:
|
|
1094
|
-
roleId:
|
|
1328
|
+
var OpenRigRoleSchema = z20.object({ type: z20.literal("openrig.role"), requestId: z20.string().min(1).max(100), machineId: z20.string().optional(), roleId: LibraryIdSchema });
|
|
1329
|
+
var OpenRigRoleDetailSchema = z20.object({
|
|
1330
|
+
type: z20.literal("openrig.roleDetail"),
|
|
1331
|
+
requestId: z20.string(),
|
|
1332
|
+
machineId: z20.string().optional(),
|
|
1333
|
+
roleId: z20.string(),
|
|
1095
1334
|
role: RoleDetailSchema.optional(),
|
|
1096
1335
|
error: RoleErrorSchema.optional()
|
|
1097
1336
|
});
|
|
1098
|
-
var OpenRigRoleFileSchema =
|
|
1099
|
-
type:
|
|
1100
|
-
requestId:
|
|
1101
|
-
machineId:
|
|
1337
|
+
var OpenRigRoleFileSchema = z20.object({
|
|
1338
|
+
type: z20.literal("openrig.roleFile"),
|
|
1339
|
+
requestId: z20.string().min(1).max(100),
|
|
1340
|
+
machineId: z20.string().optional(),
|
|
1102
1341
|
roleId: LibraryIdSchema,
|
|
1103
|
-
path:
|
|
1104
|
-
});
|
|
1105
|
-
var OpenRigRoleFileContentSchema =
|
|
1106
|
-
type:
|
|
1107
|
-
requestId:
|
|
1108
|
-
machineId:
|
|
1109
|
-
roleId:
|
|
1110
|
-
path:
|
|
1111
|
-
content:
|
|
1112
|
-
size:
|
|
1113
|
-
truncated:
|
|
1342
|
+
path: z20.string().min(1).max(4096)
|
|
1343
|
+
});
|
|
1344
|
+
var OpenRigRoleFileContentSchema = z20.object({
|
|
1345
|
+
type: z20.literal("openrig.roleFileContent"),
|
|
1346
|
+
requestId: z20.string(),
|
|
1347
|
+
machineId: z20.string().optional(),
|
|
1348
|
+
roleId: z20.string(),
|
|
1349
|
+
path: z20.string(),
|
|
1350
|
+
content: z20.string().optional(),
|
|
1351
|
+
size: z20.number().int().min(0).optional(),
|
|
1352
|
+
truncated: z20.boolean().optional(),
|
|
1114
1353
|
error: RoleErrorSchema.optional()
|
|
1115
1354
|
});
|
|
1116
1355
|
var ROLE_FILE_MAX = 512 * 1024;
|
|
1117
1356
|
|
|
1118
1357
|
// ../protocol/src/routing.ts
|
|
1119
|
-
import { z as
|
|
1120
|
-
var MachineInventorySchema =
|
|
1121
|
-
id:
|
|
1122
|
-
label:
|
|
1123
|
-
online:
|
|
1124
|
-
local:
|
|
1125
|
-
sessions:
|
|
1126
|
-
folders:
|
|
1127
|
-
});
|
|
1128
|
-
var RoutingContextSchema =
|
|
1129
|
-
machines:
|
|
1130
|
-
history:
|
|
1131
|
-
});
|
|
1132
|
-
var RouteResultSchema =
|
|
1358
|
+
import { z as z21 } from "zod";
|
|
1359
|
+
var MachineInventorySchema = z21.object({
|
|
1360
|
+
id: z21.string().min(1).max(80),
|
|
1361
|
+
label: z21.string().min(1).max(120),
|
|
1362
|
+
online: z21.boolean(),
|
|
1363
|
+
local: z21.boolean(),
|
|
1364
|
+
sessions: z21.array(z21.object({ id: z21.string(), name: z21.string(), project: z21.string(), cwd: z21.string(), topic: z21.string().optional(), state: z21.string(), readOnly: z21.boolean() })).max(500),
|
|
1365
|
+
folders: z21.array(z21.object({ project: z21.string(), cwd: z21.string() })).max(100)
|
|
1366
|
+
});
|
|
1367
|
+
var RoutingContextSchema = z21.object({
|
|
1368
|
+
machines: z21.array(MachineInventorySchema).min(1).max(20),
|
|
1369
|
+
history: z21.array(z21.object({ question: z21.string().max(2e4), answer: z21.string().max(2e4).optional(), machineId: z21.string().optional() })).max(50)
|
|
1370
|
+
});
|
|
1371
|
+
var RouteResultSchema = z21.object({
|
|
1133
1372
|
/** Null means an answer or a clarification, never an implicit local destination. */
|
|
1134
|
-
machineId:
|
|
1135
|
-
text:
|
|
1136
|
-
choices:
|
|
1373
|
+
machineId: z21.string().nullable(),
|
|
1374
|
+
text: z21.string().min(1).max(2e4),
|
|
1375
|
+
choices: z21.array(z21.string()).max(20).optional()
|
|
1137
1376
|
});
|
|
1138
1377
|
|
|
1139
1378
|
// ../protocol/src/thread.ts
|
|
1140
|
-
import { z as
|
|
1141
|
-
var ToolStatusSchema =
|
|
1142
|
-
var FileChangeSchema =
|
|
1143
|
-
var base = { id:
|
|
1144
|
-
var ThreadItemSchema =
|
|
1145
|
-
|
|
1146
|
-
|
|
1379
|
+
import { z as z22 } from "zod";
|
|
1380
|
+
var ToolStatusSchema = z22.enum(["running", "success", "failed", "unknown"]);
|
|
1381
|
+
var FileChangeSchema = z22.object({ path: z22.string().min(1), from: z22.string().min(1).optional(), change: z22.enum(["add", "edit", "delete", "write"]), diff: z22.string().optional() });
|
|
1382
|
+
var base = { id: z22.string().min(1), at: z22.string().datetime(), turn: z22.number().int().nonnegative(), helperId: z22.string().optional() };
|
|
1383
|
+
var ThreadItemSchema = z22.discriminatedUnion("kind", [
|
|
1384
|
+
z22.object({ kind: z22.literal("prompt"), ...base, text: z22.string(), images: z22.array(AttachmentRefSchema).optional() }),
|
|
1385
|
+
z22.object({ kind: z22.literal("message"), ...base, text: z22.string() }),
|
|
1147
1386
|
/** `id` is the harness's tool_use_id when it gives one, so the result updates the same row; `byClose`: a question
|
|
1148
1387
|
* dismissed by Ledge closing the session. */
|
|
1149
|
-
|
|
1150
|
-
kind:
|
|
1388
|
+
z22.object({
|
|
1389
|
+
kind: z22.literal("tool"),
|
|
1151
1390
|
...base,
|
|
1152
|
-
tool:
|
|
1153
|
-
label:
|
|
1154
|
-
input:
|
|
1155
|
-
output:
|
|
1156
|
-
error:
|
|
1391
|
+
tool: z22.string(),
|
|
1392
|
+
label: z22.string(),
|
|
1393
|
+
input: z22.string().optional(),
|
|
1394
|
+
output: z22.string().optional(),
|
|
1395
|
+
error: z22.string().optional(),
|
|
1157
1396
|
status: ToolStatusSchema,
|
|
1158
|
-
durationMs:
|
|
1159
|
-
files:
|
|
1160
|
-
byClose:
|
|
1397
|
+
durationMs: z22.number().nonnegative().optional(),
|
|
1398
|
+
files: z22.array(FileChangeSchema).default([]),
|
|
1399
|
+
byClose: z22.literal(true).optional(),
|
|
1161
1400
|
/** Images the tool returned (a browser screenshot), in order; `imagesDropped`: some were not kept. */
|
|
1162
|
-
images:
|
|
1163
|
-
imagesDropped:
|
|
1401
|
+
images: z22.array(AttachmentRefSchema).max(ATTACHMENTS_MAX).optional(),
|
|
1402
|
+
imagesDropped: z22.literal(true).optional()
|
|
1164
1403
|
}),
|
|
1165
1404
|
/** `unknown`: answered in the terminal, the daemon only saw the prompt go away; `always`: allowed and not asked again;
|
|
1166
1405
|
* `byClose`: denied by Ledge closing the session. */
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1406
|
+
z22.object({ kind: z22.literal("permission"), ...base, tool: z22.string(), text: z22.string(), decision: z22.enum(["accept", "deny", "always", "unknown"]).optional(), byClose: z22.literal(true).optional() }),
|
|
1407
|
+
z22.object({ kind: z22.literal("reply"), ...base, text: z22.string() }),
|
|
1408
|
+
z22.object({ kind: z22.literal("helper"), ...base, task: z22.string(), agentType: z22.string().optional(), state: z22.enum(["working", "exited"]) }),
|
|
1170
1409
|
/** 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. */
|
|
1171
|
-
|
|
1410
|
+
z22.object({
|
|
1411
|
+
kind: z22.literal("sent"),
|
|
1412
|
+
...base,
|
|
1413
|
+
to: z22.string(),
|
|
1414
|
+
peer: z22.string().optional(),
|
|
1415
|
+
summary: z22.string().optional(),
|
|
1416
|
+
text: z22.string(),
|
|
1417
|
+
status: z22.enum(["sending", "delivered", "read", "failed"]),
|
|
1418
|
+
received: z22.string().optional(),
|
|
1419
|
+
via: z22.literal("ledge").optional(),
|
|
1420
|
+
/** The seat's own `rig send` (its Bash call), docs/mockups/ledge-openrig-messages.html: `rig.command` is what it ran. */
|
|
1421
|
+
rig: RigInfoSchema.optional()
|
|
1422
|
+
}),
|
|
1172
1423
|
/** 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. */
|
|
1173
|
-
|
|
1424
|
+
z22.object({
|
|
1425
|
+
kind: z22.literal("received"),
|
|
1426
|
+
...base,
|
|
1427
|
+
from: z22.string(),
|
|
1428
|
+
peer: z22.string().optional(),
|
|
1429
|
+
text: z22.string(),
|
|
1430
|
+
sent: z22.string().optional(),
|
|
1431
|
+
/** An OpenRig `rig send` typed into this seat (`rig.kind` `message`): `from` is the envelope's sender, `text` its body. */
|
|
1432
|
+
rig: RigInfoSchema.optional()
|
|
1433
|
+
}),
|
|
1174
1434
|
/** An automated notice of the agent's harness about messages between sessions (Claude Code's `[Cross-session idle
|
|
1175
1435
|
* notice]` / `[Cross-session delivery notice]`): a system event, never the user's words. `name` is the session it is
|
|
1176
1436
|
* about as quoted, `peer` its Ledge session id; a delivery notice says the outcome of `count` messages to `recipients`. */
|
|
1177
|
-
|
|
1178
|
-
kind:
|
|
1437
|
+
z22.object({
|
|
1438
|
+
kind: z22.literal("notice"),
|
|
1179
1439
|
...base,
|
|
1180
|
-
notice:
|
|
1440
|
+
notice: z22.enum(["idle", "exited", "unavailable", "expired", "delivery", "other"]),
|
|
1181
1441
|
/** 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. */
|
|
1182
|
-
task:
|
|
1442
|
+
task: z22.string().optional(),
|
|
1183
1443
|
/** A task this session handed over (`outgoing`), and how (issue #92). */
|
|
1184
|
-
taskOp:
|
|
1185
|
-
outgoing:
|
|
1186
|
-
name:
|
|
1187
|
-
peer:
|
|
1188
|
-
time:
|
|
1189
|
-
summary:
|
|
1190
|
-
status:
|
|
1191
|
-
count:
|
|
1192
|
-
recipients:
|
|
1193
|
-
reason:
|
|
1194
|
-
text:
|
|
1444
|
+
taskOp: z22.enum(TRANSMISSION_OPS).optional(),
|
|
1445
|
+
outgoing: z22.literal(true).optional(),
|
|
1446
|
+
name: z22.string().optional(),
|
|
1447
|
+
peer: z22.string().optional(),
|
|
1448
|
+
time: z22.string().optional(),
|
|
1449
|
+
summary: z22.string().optional(),
|
|
1450
|
+
status: z22.enum(["held", "denied", "expired", "delivered", "refused", "dropped", "unknown"]).optional(),
|
|
1451
|
+
count: z22.number().int().positive().optional(),
|
|
1452
|
+
recipients: z22.array(z22.string()).optional(),
|
|
1453
|
+
reason: z22.string().optional(),
|
|
1454
|
+
text: z22.string(),
|
|
1455
|
+
/** An OpenRig block that is not a message: a row handed to this seat or passed by it (`handoff`, `outgoing` for its own),
|
|
1456
|
+
* a row unblocked, OpenRig's own notice, or an unreadable block; `text` is the body (unreadable: the block as is). */
|
|
1457
|
+
rig: RigInfoSchema.optional()
|
|
1195
1458
|
})
|
|
1196
1459
|
]);
|
|
1197
1460
|
|
|
1198
1461
|
// ../protocol/src/mobile.ts
|
|
1199
|
-
import { z as
|
|
1200
|
-
var RelayUrlSchema =
|
|
1462
|
+
import { z as z23 } from "zod";
|
|
1463
|
+
var RelayUrlSchema = z23.string().transform((v, ctx) => {
|
|
1201
1464
|
const bad = () => {
|
|
1202
|
-
ctx.addIssue({ code:
|
|
1203
|
-
return
|
|
1465
|
+
ctx.addIssue({ code: z23.ZodIssueCode.custom, message: "invalid relay URL" });
|
|
1466
|
+
return z23.NEVER;
|
|
1204
1467
|
};
|
|
1205
1468
|
let u;
|
|
1206
1469
|
try {
|
|
@@ -1213,122 +1476,128 @@ var RelayUrlSchema = z22.string().transform((v, ctx) => {
|
|
|
1213
1476
|
if (u.username || u.password || u.pathname !== "/" || u.search || u.hash) return bad();
|
|
1214
1477
|
return u.origin;
|
|
1215
1478
|
});
|
|
1216
|
-
var MobileConfigSchema =
|
|
1217
|
-
var MobileLinkStateSchema =
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1479
|
+
var MobileConfigSchema = z23.object({ enabled: z23.boolean(), relayUrl: RelayUrlSchema, terminalInput: z23.boolean().optional() });
|
|
1480
|
+
var MobileLinkStateSchema = z23.discriminatedUnion("state", [
|
|
1481
|
+
z23.object({ state: z23.literal("off") }),
|
|
1482
|
+
z23.object({ state: z23.literal("connecting") }),
|
|
1483
|
+
z23.object({ state: z23.literal("online") }),
|
|
1484
|
+
z23.object({ state: z23.literal("offline"), retryAt: z23.string().datetime(), error: z23.string() }),
|
|
1485
|
+
z23.object({ state: z23.literal("signed_out"), reason: z23.enum(["other_pc", "session_invalid"]) }),
|
|
1223
1486
|
/** The mobile service did not start (a broken mobile.json, a missing key store): nothing is linked until the daemon restarts. */
|
|
1224
|
-
|
|
1487
|
+
z23.object({ state: z23.literal("unavailable"), error: z23.string().max(200) })
|
|
1225
1488
|
]);
|
|
1226
|
-
var MobilePairingSchema =
|
|
1227
|
-
id:
|
|
1228
|
-
label:
|
|
1229
|
-
code:
|
|
1230
|
-
expiresAt:
|
|
1231
|
-
state:
|
|
1232
|
-
});
|
|
1233
|
-
var MobileStateSchema =
|
|
1234
|
-
enabled:
|
|
1235
|
-
relayUrl:
|
|
1236
|
-
account:
|
|
1237
|
-
signin:
|
|
1489
|
+
var MobilePairingSchema = z23.object({
|
|
1490
|
+
id: z23.string(),
|
|
1491
|
+
label: z23.string(),
|
|
1492
|
+
code: z23.string().nullable(),
|
|
1493
|
+
expiresAt: z23.string().datetime(),
|
|
1494
|
+
state: z23.enum(["waiting", "confirm", "approved", "linked", "expired", "refused"])
|
|
1495
|
+
});
|
|
1496
|
+
var MobileStateSchema = z23.object({
|
|
1497
|
+
enabled: z23.boolean(),
|
|
1498
|
+
relayUrl: z23.string(),
|
|
1499
|
+
account: z23.object({ login: z23.string(), accountId: z23.string() }).nullable(),
|
|
1500
|
+
signin: z23.object({ userCode: z23.string(), verificationUri: z23.string(), expiresAt: z23.string().datetime() }).nullable(),
|
|
1238
1501
|
/** The last sign-in that did not end with a session; cleared by the next one. */
|
|
1239
|
-
signinError:
|
|
1502
|
+
signinError: z23.enum(["expired", "denied", "not_allowed", "failed"]).nullable(),
|
|
1240
1503
|
link: MobileLinkStateSchema,
|
|
1241
|
-
devices:
|
|
1242
|
-
pairings:
|
|
1504
|
+
devices: z23.array(z23.object({ id: z23.string(), label: z23.string(), approvedAt: z23.string(), online: z23.boolean(), lastSeenAt: z23.string().optional() })),
|
|
1505
|
+
pairings: z23.array(MobilePairingSchema),
|
|
1243
1506
|
/** « Taper dans les terminaux depuis le téléphone »: absent from an older daemon, which has no phone terminal. */
|
|
1244
|
-
terminalInput:
|
|
1507
|
+
terminalInput: z23.boolean().optional()
|
|
1245
1508
|
});
|
|
1246
1509
|
|
|
1247
1510
|
// ../protocol/src/messages.ts
|
|
1248
|
-
var RefusalCodeSchema =
|
|
1249
|
-
var SessionEventSchema =
|
|
1250
|
-
name:
|
|
1251
|
-
at:
|
|
1252
|
-
source:
|
|
1253
|
-
tool:
|
|
1254
|
-
text:
|
|
1255
|
-
options:
|
|
1511
|
+
var RefusalCodeSchema = z24.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"]);
|
|
1512
|
+
var SessionEventSchema = z24.object({
|
|
1513
|
+
name: z24.enum(["SessionStart", "UserPromptSubmit", "PreToolUse", "PermissionRequest", "Notification", "Stop", "SessionEnd", "PaneChanged", "PaneGone", "PanePrompt", "PaneIdle", "InjectFailed", "Decided", "PostToolUse", "PostToolUseFailure", "StopFailure", "SubagentStart", "SubagentStop", "PaneTitle", "Renamed", "NativeRequest", "AgentMessage", "QueueUpdate"]),
|
|
1514
|
+
at: z24.string().datetime(),
|
|
1515
|
+
source: z24.enum(["hook", "pane", "daemon"]),
|
|
1516
|
+
tool: z24.string().optional(),
|
|
1517
|
+
text: z24.string().optional(),
|
|
1518
|
+
options: z24.array(z24.string()).optional(),
|
|
1256
1519
|
/** Each option's description, by index ('' when it has none); left out when no option has one. */
|
|
1257
|
-
optionDetails:
|
|
1258
|
-
kind:
|
|
1520
|
+
optionDetails: z24.array(z24.string()).optional(),
|
|
1521
|
+
kind: z24.enum(["question", "permission", "plan", "diff"]).optional(),
|
|
1259
1522
|
/** PanePrompt: what to review in full before deciding (a plan read in the terminal), when `text` is its summary. */
|
|
1260
|
-
body:
|
|
1261
|
-
exitCode:
|
|
1262
|
-
helperId:
|
|
1523
|
+
body: z24.string().optional(),
|
|
1524
|
+
exitCode: z24.number().int().nullable().optional(),
|
|
1525
|
+
helperId: z24.string().optional(),
|
|
1263
1526
|
/** The helper's description, once known (Claude Code: its `.meta.json`, written after SubagentStart). */
|
|
1264
|
-
task:
|
|
1527
|
+
task: z24.string().optional(),
|
|
1265
1528
|
/** PostToolUseFailure: the user interrupted the turn (Esc); Claude then waits for input with no Stop. */
|
|
1266
|
-
interrupt:
|
|
1529
|
+
interrupt: z24.boolean().optional(),
|
|
1267
1530
|
/** UserPromptSubmit, PostToolUse: the images the prompt or the tool result carried, by attachment id. */
|
|
1268
|
-
attachments:
|
|
1531
|
+
attachments: z24.array(AttachmentIdSchema).max(ATTACHMENTS_MAX).optional(),
|
|
1269
1532
|
/** PostToolUse: some of the result's images were not kept. */
|
|
1270
|
-
imagesDropped:
|
|
1533
|
+
imagesDropped: z24.boolean().optional(),
|
|
1271
1534
|
/** QueueUpdate: OMP's queue as it stands. */
|
|
1272
|
-
queue:
|
|
1535
|
+
queue: z24.array(z24.object({ text: z24.string(), mode: z24.enum(["steer", "follow_up"]) })).optional(),
|
|
1273
1536
|
/** AgentMessage: the harness's own message, its text so far (`streaming`) or whole. `messageId` is unique for the session's life. */
|
|
1274
|
-
messageId:
|
|
1275
|
-
streaming:
|
|
1537
|
+
messageId: z24.string().optional(),
|
|
1538
|
+
streaming: z24.boolean().optional(),
|
|
1276
1539
|
/** NativeRequest: the dialog's id, and OMP's `ask` questions when it is one. */
|
|
1277
|
-
requestId:
|
|
1278
|
-
questions:
|
|
1540
|
+
requestId: z24.string().optional(),
|
|
1541
|
+
questions: z24.array(AskQuestionSchema).optional(),
|
|
1279
1542
|
/** Stop: its text is already in the thread as AgentMessage cards, which the reply card must not repeat. */
|
|
1280
|
-
streamed:
|
|
1543
|
+
streamed: z24.boolean().optional(),
|
|
1281
1544
|
/** Native tracking (spec 2026-10-13 §2): the harness's id of the tool call, so its result lands on the same row. */
|
|
1282
|
-
toolUseId:
|
|
1545
|
+
toolUseId: z24.string().optional(),
|
|
1283
1546
|
/** The tool's input as the harness gave it, every string capped by the hook. */
|
|
1284
|
-
toolInput:
|
|
1547
|
+
toolInput: z24.record(z24.string(), z24.unknown()).optional(),
|
|
1285
1548
|
/** PostToolUse: the result as text; SubagentStop: nothing (the report travels in `text`). */
|
|
1286
|
-
output:
|
|
1287
|
-
durationMs:
|
|
1549
|
+
output: z24.string().optional(),
|
|
1550
|
+
durationMs: z24.number().nonnegative().optional(),
|
|
1288
1551
|
/** PostToolUseFailure: the harness's error message. */
|
|
1289
|
-
error:
|
|
1552
|
+
error: z24.string().optional(),
|
|
1290
1553
|
/** The session's transcript, or the helper's on SubagentStop; the daemon reads its tail for the agent's messages. */
|
|
1291
|
-
transcriptPath:
|
|
1554
|
+
transcriptPath: z24.string().optional(),
|
|
1292
1555
|
/** Decided: what was chosen when the daemon knows it. */
|
|
1293
1556
|
decision: DecisionSchema.optional(),
|
|
1294
1557
|
/** Decided: the request was refused by Ledge closing the session (its interrupt keys), not by the user's answer. */
|
|
1295
|
-
byClose:
|
|
1558
|
+
byClose: z24.literal(true).optional(),
|
|
1296
1559
|
/** The agent's own session id when the hook names it, so the daemon can resume that conversation. */
|
|
1297
|
-
agentSessionId:
|
|
1560
|
+
agentSessionId: z24.string().optional()
|
|
1298
1561
|
});
|
|
1299
|
-
var RecentSchema =
|
|
1562
|
+
var RecentSchema = z24.object({ cwd: z24.string().min(1), project: z24.string().min(1), lastAt: z24.string().datetime() });
|
|
1300
1563
|
var SEMVER = /^\d+\.\d+\.\d+$/;
|
|
1301
|
-
var DaemonInfoSchema =
|
|
1302
|
-
version:
|
|
1303
|
-
install:
|
|
1304
|
-
systemd:
|
|
1305
|
-
manual:
|
|
1306
|
-
routing:
|
|
1307
|
-
archive:
|
|
1564
|
+
var DaemonInfoSchema = z24.object({
|
|
1565
|
+
version: z24.string(),
|
|
1566
|
+
install: z24.enum(["npm", "git", "unknown"]),
|
|
1567
|
+
systemd: z24.boolean(),
|
|
1568
|
+
manual: z24.string().optional(),
|
|
1569
|
+
routing: z24.boolean().optional(),
|
|
1570
|
+
archive: z24.boolean().optional(),
|
|
1308
1571
|
/** Takes `model` and `effort` on `session.launch` and answers `models.list` (#58). */
|
|
1309
|
-
models:
|
|
1572
|
+
models: z24.boolean().optional(),
|
|
1310
1573
|
/** Drives OMP over its RPC: launching `omp` sessions is possible when the binary is there (`models.list` says so). */
|
|
1311
|
-
omp:
|
|
1574
|
+
omp: z24.boolean().optional(),
|
|
1312
1575
|
/** Takes `session.model`: a live session's model and effort changed (#58, lot 2). */
|
|
1313
|
-
modelSwitch:
|
|
1576
|
+
modelSwitch: z24.boolean().optional(),
|
|
1314
1577
|
/** Answers `session.commands`: the slash commands a session's agent offers. */
|
|
1315
|
-
commands:
|
|
1578
|
+
commands: z24.boolean().optional(),
|
|
1316
1579
|
/** Takes `approval` on an OMP `session.launch` (auto mode). */
|
|
1317
|
-
ompApproval:
|
|
1580
|
+
ompApproval: z24.boolean().optional(),
|
|
1318
1581
|
/** Its home folder, so a client shows the folders under it as `~` (absent from older daemons). */
|
|
1319
|
-
home:
|
|
1320
|
-
mobile:
|
|
1582
|
+
home: z24.string().optional(),
|
|
1583
|
+
mobile: z24.boolean().optional(),
|
|
1321
1584
|
/** Answers `openrig.status` and `openrig.act`: drives OpenRig on its machine. */
|
|
1322
|
-
openrig:
|
|
1585
|
+
openrig: z24.boolean().optional(),
|
|
1323
1586
|
/** Answers `openrig.roles`, `openrig.role` and `openrig.roleFile`: reads OpenRig's roles (agent specs). */
|
|
1324
|
-
openrigRoles:
|
|
1587
|
+
openrigRoles: z24.boolean().optional(),
|
|
1588
|
+
/** Answers `openrig.row`: a queue row's state, for the OpenRig task cards of a thread. */
|
|
1589
|
+
openrigRows: z24.boolean().optional(),
|
|
1590
|
+
/** Answers `openrig.asks` and `openrig.answer`: what OpenRig asks the person, and their answers (« À toi »). */
|
|
1591
|
+
openrigAsks: z24.boolean().optional(),
|
|
1592
|
+
/** Answers `openrig.goal.send`, `openrig.goals` and `openrig.goal.reply`: goals given to Max (lot 2). */
|
|
1593
|
+
openrigGoals: z24.boolean().optional(),
|
|
1325
1594
|
/** Takes `session.broadcast` (its `ledge bridge` too, on this machine): a client sends it only when this is set. */
|
|
1326
|
-
broadcast:
|
|
1595
|
+
broadcast: z24.boolean().optional()
|
|
1327
1596
|
});
|
|
1328
|
-
var UpdateFailureSchema =
|
|
1329
|
-
var ServerMessageSchema =
|
|
1597
|
+
var UpdateFailureSchema = z24.enum(["git-install", "no-systemd", "sessions-at-risk", "npm-failed", "busy"]);
|
|
1598
|
+
var ServerMessageSchema = z24.discriminatedUnion("type", [
|
|
1330
1599
|
SessionModelAcceptedSchema,
|
|
1331
|
-
|
|
1600
|
+
z24.object({ type: z24.literal("chat.routed"), qid: z24.string(), result: RouteResultSchema }),
|
|
1332
1601
|
DocumentContentSchema,
|
|
1333
1602
|
DocumentErrorSchema,
|
|
1334
1603
|
AttachmentStoredSchema,
|
|
@@ -1339,6 +1608,7 @@ var ServerMessageSchema = z23.discriminatedUnion("type", [
|
|
|
1339
1608
|
ModelsListingSchema,
|
|
1340
1609
|
SessionCommandsListingSchema,
|
|
1341
1610
|
OpenRigStateMessageSchema,
|
|
1611
|
+
OpenRigRowStateSchema,
|
|
1342
1612
|
OpenRigDoneSchema,
|
|
1343
1613
|
OpenRigSpecListSchema,
|
|
1344
1614
|
OpenRigLaunchedSchema,
|
|
@@ -1346,108 +1616,113 @@ var ServerMessageSchema = z23.discriminatedUnion("type", [
|
|
|
1346
1616
|
OpenRigRoleListSchema,
|
|
1347
1617
|
OpenRigRoleDetailSchema,
|
|
1348
1618
|
OpenRigRoleFileContentSchema,
|
|
1619
|
+
OpenRigAsksChangedSchema,
|
|
1620
|
+
OpenRigAnsweredSchema,
|
|
1621
|
+
OpenRigGoalSentSchema,
|
|
1622
|
+
OpenRigGoalsChangedSchema,
|
|
1623
|
+
OpenRigGoalRepliedSchema,
|
|
1349
1624
|
/** `recents`: the folders of past sessions, most recent first. `launcher`: the command that runs `ledge` inside WSL
|
|
1350
1625
|
* (`[node, cli]`), so the overlay can attach a terminal to a session with `<launcher> attach <id>`. `daemon`: absent
|
|
1351
1626
|
* from daemons older than the auto-update. */
|
|
1352
|
-
|
|
1353
|
-
|
|
1627
|
+
z24.object({ type: z24.literal("snapshot"), sessions: z24.array(SessionSchema), recents: z24.array(RecentSchema).optional(), launcher: z24.array(z24.string()).optional(), daemon: DaemonInfoSchema.optional(), sshAlias: SshAliasSchema.optional() }),
|
|
1628
|
+
z24.object({ type: z24.literal("session.updated"), session: SessionSchema }),
|
|
1354
1629
|
/** OMP: the instructions taken back from its queue by an interrupt that restores it; the asker puts them back in its draft. */
|
|
1355
|
-
|
|
1356
|
-
type:
|
|
1357
|
-
id:
|
|
1358
|
-
texts:
|
|
1630
|
+
z24.object({
|
|
1631
|
+
type: z24.literal("session.queue.restored"),
|
|
1632
|
+
id: z24.string(),
|
|
1633
|
+
texts: z24.array(z24.string()),
|
|
1359
1634
|
/** The same instructions with the images they carried; `imagesDropped`: some images could not be put back. */
|
|
1360
|
-
entries:
|
|
1361
|
-
imagesDropped:
|
|
1635
|
+
entries: z24.array(z24.object({ text: z24.string(), attachments: z24.array(AttachmentIdSchema).optional() })).optional(),
|
|
1636
|
+
imagesDropped: z24.boolean().optional()
|
|
1362
1637
|
}),
|
|
1363
|
-
|
|
1364
|
-
|
|
1638
|
+
z24.object({ type: z24.literal("session.removed"), id: z24.string() }),
|
|
1639
|
+
z24.object({ type: z24.literal("session.preview"), id: z24.string(), lines: z24.array(z24.string()) }),
|
|
1365
1640
|
/** A session's thread, on request (spec 2026-10-13 §5); then each created or updated item, broadcast. */
|
|
1366
|
-
|
|
1367
|
-
|
|
1641
|
+
z24.object({ type: z24.literal("session.thread"), id: z24.string(), items: z24.array(ThreadItemSchema) }),
|
|
1642
|
+
z24.object({ type: z24.literal("session.thread.item"), id: z24.string(), item: ThreadItemSchema }),
|
|
1368
1643
|
/** Answer to the requester of a `session.launch`: the session exists (its `session.updated` is broadcast as usual). */
|
|
1369
|
-
|
|
1644
|
+
z24.object({ type: z24.literal("session.launched"), id: z24.string() }),
|
|
1370
1645
|
/** Answer to the requester of a `session.close`, once per row closed: the session `id` and how it ended. */
|
|
1371
|
-
|
|
1372
|
-
|
|
1646
|
+
z24.object({ type: z24.literal("session.closed"), qid: z24.string(), row: z24.number().int().min(0), id: z24.string(), how: z24.enum(["exit", "killed"]) }),
|
|
1647
|
+
z24.object({ type: z24.literal("recents"), recents: z24.array(RecentSchema) }),
|
|
1373
1648
|
/** Answer to the requester of a `folder.list` or `folder.create`: `path` resolved (`~` expanded), `parent` null at the
|
|
1374
1649
|
* root, `entries` its subfolders by name (hidden ones left out). A failure keeps the asked path and says why in `error`,
|
|
1375
1650
|
* never in an `error` message, which the launch form reads as a failed launch. */
|
|
1376
|
-
|
|
1377
|
-
type:
|
|
1378
|
-
path:
|
|
1379
|
-
parent:
|
|
1380
|
-
entries:
|
|
1381
|
-
error:
|
|
1651
|
+
z24.object({
|
|
1652
|
+
type: z24.literal("folder.listing"),
|
|
1653
|
+
path: z24.string(),
|
|
1654
|
+
parent: z24.string().nullable(),
|
|
1655
|
+
entries: z24.array(z24.string()),
|
|
1656
|
+
error: z24.string().optional(),
|
|
1382
1657
|
/** The request this answers, when it carried one. */
|
|
1383
|
-
requestId:
|
|
1658
|
+
requestId: z24.string().optional(),
|
|
1384
1659
|
/** Those `entries` that are Git repositories, with their branch (the short commit when HEAD is detached). */
|
|
1385
|
-
repos:
|
|
1660
|
+
repos: z24.array(z24.object({ name: z24.string(), branch: z24.string() })).optional()
|
|
1386
1661
|
}),
|
|
1387
1662
|
/** 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. */
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1663
|
+
z24.object({ type: z24.literal("chat.step"), qid: z24.string(), step: ChatStepSchema }),
|
|
1664
|
+
z24.object({ type: z24.literal("chat.delta"), qid: z24.string(), text: z24.string() }),
|
|
1665
|
+
z24.object({ type: z24.literal("chat.done"), qid: z24.string(), text: z24.string(), at: z24.string().datetime(), proposal: ChatProposalSchema.optional(), proposals: ChatProposalsSchema.optional(), relay: ChatRelaySchema.optional(), closes: ChatClosesSchema.optional(), projectProposal: ChatProjectProposalSchema.optional(), tasks: ChatTasksSchema.optional(), actions: ChatActionsSchema.optional() }),
|
|
1391
1666
|
/** What a `chat.act` applied: the rows done, and the one that failed with why (the rows after it stay proposed). */
|
|
1392
|
-
|
|
1393
|
-
row:
|
|
1394
|
-
message:
|
|
1667
|
+
z24.object({ type: z24.literal("chat.acted"), rid: z24.string(), qid: z24.string(), done: z24.array(z24.number().int().min(0)), failed: z24.array(z24.object({
|
|
1668
|
+
row: z24.number().int().min(0),
|
|
1669
|
+
message: z24.string(),
|
|
1395
1670
|
/** nothing was sent */
|
|
1396
|
-
refused:
|
|
1671
|
+
refused: z24.literal(true).optional()
|
|
1397
1672
|
})) }),
|
|
1398
1673
|
/** What an `action.run` came to: `row` the action as the daemon resolved it (its summary, its risk); `reason` why it was not
|
|
1399
1674
|
* done, with `code` (`refused`: not possible now; `agent`: a session may not run it; `confirm_required`: destructive, ask
|
|
1400
1675
|
* again; `failed`: the handler turned it down; a free string, as an `error`'s). */
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1676
|
+
z24.object({ type: z24.literal("action.result"), rid: z24.string(), action: z24.string().min(1).max(40), row: ChatActionSchema.optional(), reason: z24.string().optional(), code: z24.string().optional() }),
|
|
1677
|
+
z24.object({ type: z24.literal("chat.error"), qid: z24.string(), message: z24.string() }),
|
|
1678
|
+
z24.object({ type: z24.literal("chat.history"), turns: z24.array(ChatTurnSchema) }),
|
|
1404
1679
|
/** The daemon updating itself: npm is running, then the service restarts (the socket closes right after). */
|
|
1405
|
-
|
|
1680
|
+
z24.object({ type: z24.literal("update.progress"), version: z24.string(), step: z24.enum(["installing", "restarting"]) }),
|
|
1406
1681
|
/** Nothing was changed. `detail`: the useful end of npm's output; `command`: what the person can run instead. */
|
|
1407
|
-
|
|
1682
|
+
z24.object({ type: z24.literal("update.failed"), version: z24.string(), reason: UpdateFailureSchema, detail: z24.string().optional(), command: z24.string().optional() }),
|
|
1408
1683
|
/** The shared machines list (spec 2026-10-14 §5), sent to local clients only. */
|
|
1409
|
-
|
|
1684
|
+
z24.object({ type: z24.literal("machines"), machines: MachinesSchema }),
|
|
1410
1685
|
/** The local daemon's projects and folder rules (spec 2026-10-16 build §5), sent to local clients only. */
|
|
1411
|
-
|
|
1686
|
+
z24.object({ type: z24.literal("projects"), projects: ProjectsSchema }),
|
|
1412
1687
|
/** The island's settings shared by the local fronts; null until a front sets them (the island then sends its own). Local clients only. */
|
|
1413
|
-
|
|
1688
|
+
z24.object({ type: z24.literal("island"), prefs: IslandPrefsSchema.nullable() }),
|
|
1414
1689
|
/** Mobile access (lot 3), after the island in each snapshot and on every change. Local clients only. */
|
|
1415
|
-
|
|
1690
|
+
z24.object({ type: z24.literal("mobile"), state: MobileStateSchema }),
|
|
1416
1691
|
/** The sessions a phone types into (« Téléphone au clavier »), after each snapshot and each change; `device` is its label. */
|
|
1417
|
-
|
|
1692
|
+
z24.object({ type: z24.literal("terminal.holders"), holds: z24.array(z24.object({ id: z24.string(), device: z24.string() })) }),
|
|
1418
1693
|
/** 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`. */
|
|
1419
|
-
|
|
1420
|
-
type:
|
|
1421
|
-
rid:
|
|
1422
|
-
outcome:
|
|
1423
|
-
reason:
|
|
1694
|
+
z24.object({
|
|
1695
|
+
type: z24.literal("message.ack"),
|
|
1696
|
+
rid: z24.string(),
|
|
1697
|
+
outcome: z24.enum(["accepted", "queued", "native", "refused"]),
|
|
1698
|
+
reason: z24.string().optional(),
|
|
1424
1699
|
/** A broadcast's per-recipient outcome, by name. */
|
|
1425
|
-
recipients:
|
|
1700
|
+
recipients: z24.object({ sent: z24.array(z24.string()), refused: z24.array(z24.object({ name: z24.string(), reason: z24.string() })) }).optional()
|
|
1426
1701
|
}),
|
|
1427
1702
|
/** The answer to a `session.broadcast`: who got it (`sent`) and who did not; `reason`: the whole broadcast was refused. */
|
|
1428
|
-
|
|
1703
|
+
z24.object({ type: z24.literal("session.broadcast.result"), rid: z24.string(), sent: z24.array(z24.object({ id: z24.string(), name: z24.string() })), refused: z24.array(z24.object({ name: z24.string(), reason: z24.string() })), reason: z24.string().optional() }),
|
|
1429
1704
|
/** The answer to a `message.read`: the body with its sender, target and time, or the refusal's `reason`. */
|
|
1430
|
-
|
|
1705
|
+
z24.object({ type: z24.literal("message.body"), id: z24.string(), from: z24.string().optional(), to: z24.string().optional(), at: z24.string().optional(), text: z24.string().optional(), reason: z24.string().optional() }),
|
|
1431
1706
|
TaskResultSchema,
|
|
1432
1707
|
SessionHandoverResultSchema,
|
|
1433
1708
|
ContextPrefsMessageSchema,
|
|
1434
1709
|
HandoverResultSchema,
|
|
1435
1710
|
TaskUpdatedSchema,
|
|
1436
|
-
|
|
1437
|
-
type:
|
|
1438
|
-
message:
|
|
1439
|
-
id:
|
|
1711
|
+
z24.object({
|
|
1712
|
+
type: z24.literal("error"),
|
|
1713
|
+
message: z24.string(),
|
|
1714
|
+
id: z24.string().optional(),
|
|
1440
1715
|
/** Le type du message client refusé. */
|
|
1441
|
-
request:
|
|
1716
|
+
request: z24.string().optional(),
|
|
1442
1717
|
/** Ce que le front doit reconnaître au-delà du texte (NOTE_NOT_TYPED) ; une chaîne libre, qu'un front plus ancien ignore. */
|
|
1443
|
-
code:
|
|
1718
|
+
code: z24.string().optional()
|
|
1444
1719
|
})
|
|
1445
1720
|
]);
|
|
1446
1721
|
var MESSAGE_MAX = 2e4;
|
|
1447
1722
|
var SUMMARY_MAX = 500;
|
|
1448
|
-
var launchRows =
|
|
1449
|
-
var ClientMessageSchema =
|
|
1450
|
-
|
|
1723
|
+
var launchRows = z24.array(z24.number().int().min(0).max(MAX_PROPOSALS - 1)).min(1).max(MAX_PROPOSALS).refine((r) => new Set(r).size === r.length, "a row twice");
|
|
1724
|
+
var ClientMessageSchema = z24.discriminatedUnion("type", [
|
|
1725
|
+
z24.object({ type: z24.literal("chat.route"), qid: z24.string().min(1).max(100), text: z24.string().min(1).max(2e4), tool: ToolSchema.optional(), context: RoutingContextSchema }),
|
|
1451
1726
|
DocumentReadSchema,
|
|
1452
1727
|
AttachmentPutSchema,
|
|
1453
1728
|
AttachmentCaptureSchema,
|
|
@@ -1457,6 +1732,7 @@ var ClientMessageSchema = z23.discriminatedUnion("type", [
|
|
|
1457
1732
|
ModelsListSchema,
|
|
1458
1733
|
SessionCommandsSchema,
|
|
1459
1734
|
OpenRigStatusSchema,
|
|
1735
|
+
OpenRigRowSchema,
|
|
1460
1736
|
OpenRigActSchema,
|
|
1461
1737
|
OpenRigSpecsSchema,
|
|
1462
1738
|
OpenRigLaunchSchema,
|
|
@@ -1464,169 +1740,174 @@ var ClientMessageSchema = z23.discriminatedUnion("type", [
|
|
|
1464
1740
|
OpenRigRolesSchema,
|
|
1465
1741
|
OpenRigRoleSchema,
|
|
1466
1742
|
OpenRigRoleFileSchema,
|
|
1743
|
+
OpenRigAsksSchema,
|
|
1744
|
+
OpenRigAnswerSchema,
|
|
1745
|
+
OpenRigGoalSendSchema,
|
|
1746
|
+
OpenRigGoalsSchema,
|
|
1747
|
+
OpenRigGoalReplySchema,
|
|
1467
1748
|
/** The overlay starts a session: `cwd` is a WSL path (`~` allowed), `name` a preferred bot name (the daemon picks a
|
|
1468
1749
|
* free one otherwise), `prompt` the agent's first instruction, `continue` resumes the folder's last conversation.
|
|
1469
1750
|
* With `qid` it is the chat card of that turn: the daemon launches the turn's proposal and ignores the other fields
|
|
1470
1751
|
* but `rows`, `model` and `effort`. */
|
|
1471
|
-
|
|
1472
|
-
type:
|
|
1752
|
+
z24.object({
|
|
1753
|
+
type: z24.literal("session.launch"),
|
|
1473
1754
|
tool: ToolSchema,
|
|
1474
|
-
cwd:
|
|
1475
|
-
name:
|
|
1476
|
-
prompt:
|
|
1477
|
-
continue:
|
|
1478
|
-
qid:
|
|
1755
|
+
cwd: z24.string().min(1),
|
|
1756
|
+
name: z24.string().optional(),
|
|
1757
|
+
prompt: z24.string().optional(),
|
|
1758
|
+
continue: z24.boolean().optional(),
|
|
1759
|
+
qid: z24.string().min(1).optional(),
|
|
1479
1760
|
/** With `qid`: the card's rows to launch (#24), indexes into the turn's `proposals`, each once; the first row without it. */
|
|
1480
1761
|
rows: launchRows.optional(),
|
|
1481
1762
|
/** Folders in addition to `cwd` (Claude: `--add-dir`); resolved like `cwd`. */
|
|
1482
|
-
dirs:
|
|
1763
|
+
dirs: z24.array(z24.string().min(1)).max(20).optional(),
|
|
1483
1764
|
/** The agent's model and effort (#58), the agent's own default when left out; with `qid`, for every row launched. */
|
|
1484
1765
|
model: ModelIdSchema.optional(),
|
|
1485
1766
|
effort: EffortSchema.optional(),
|
|
1486
1767
|
/** OMP only: its approval mode, `always-ask` when left out. */
|
|
1487
1768
|
approval: OmpApprovalSchema.optional(),
|
|
1488
1769
|
/** Routing by the fleet, removed before the message reaches the daemon. */
|
|
1489
|
-
machineId:
|
|
1770
|
+
machineId: z24.string().optional()
|
|
1490
1771
|
}),
|
|
1491
1772
|
/** The launch form's folder browser: list a folder's subfolders (home when no `path`), or make `name` inside `parent`
|
|
1492
1773
|
* and list it. Both are answered with a `folder.listing`. */
|
|
1493
|
-
|
|
1494
|
-
type:
|
|
1495
|
-
path:
|
|
1774
|
+
z24.object({
|
|
1775
|
+
type: z24.literal("folder.list"),
|
|
1776
|
+
path: z24.string().optional(),
|
|
1496
1777
|
/** Routing by the fleet, removed before the message reaches the daemon. */
|
|
1497
|
-
machineId:
|
|
1778
|
+
machineId: z24.string().optional(),
|
|
1498
1779
|
/** Copied into the `folder.listing` that answers; the fleet relays only that listing, from the machine asked. */
|
|
1499
|
-
requestId:
|
|
1780
|
+
requestId: z24.string().min(1).max(100).optional()
|
|
1500
1781
|
}),
|
|
1501
|
-
|
|
1502
|
-
type:
|
|
1503
|
-
parent:
|
|
1504
|
-
name:
|
|
1782
|
+
z24.object({
|
|
1783
|
+
type: z24.literal("folder.create"),
|
|
1784
|
+
parent: z24.string().min(1),
|
|
1785
|
+
name: z24.string().min(1),
|
|
1505
1786
|
/** As for `folder.list`: routing by the fleet, and the request its `folder.listing` answers. */
|
|
1506
|
-
machineId:
|
|
1507
|
-
requestId:
|
|
1787
|
+
machineId: z24.string().optional(),
|
|
1788
|
+
requestId: z24.string().min(1).max(100).optional()
|
|
1508
1789
|
}),
|
|
1509
1790
|
/** `steer`: OMP only, redirect the turn running now instead of queueing the text after it. */
|
|
1510
1791
|
/** OMP's `ask`: the answers to every question of the dialog `requestId`, in order (`selected` labels, and/or free `text`); `cancel` declines it. */
|
|
1511
|
-
|
|
1512
|
-
|
|
1792
|
+
z24.object({ type: z24.literal("session.answer"), id: z24.string(), requestId: z24.string().min(1).max(200), answers: z24.array(z24.object({ id: z24.string(), selected: z24.array(z24.string().max(2e3)).max(100), text: z24.string().max(2e4).optional() })).max(50), cancel: z24.boolean().optional() }),
|
|
1793
|
+
z24.object({ type: z24.literal("session.reply"), id: z24.string(), text: z24.string(), attachments: z24.array(AttachmentIdSchema).max(ATTACHMENTS_MAX).optional(), steer: z24.boolean().optional() }),
|
|
1513
1794
|
/** The chat card's instruction (spec 2026-10-12 relay §4): sent now if the session is free, else queued until it is. */
|
|
1514
|
-
|
|
1795
|
+
z24.object({ type: z24.literal("session.relay"), qid: z24.string().min(1) }),
|
|
1515
1796
|
/** Takes the card's queued instruction back out of the queue. */
|
|
1516
|
-
|
|
1797
|
+
z24.object({ type: z24.literal("session.relay.cancel"), qid: z24.string().min(1) }),
|
|
1517
1798
|
/** The chat's generic card (action registry spec §5.1): applies its rows in order (all of them without `rows`), stopping at
|
|
1518
1799
|
* the first failure. `confirmed` says the front asked again; refused without it when a row is destructive. */
|
|
1519
|
-
|
|
1520
|
-
type:
|
|
1521
|
-
rid:
|
|
1522
|
-
qid:
|
|
1800
|
+
z24.object({
|
|
1801
|
+
type: z24.literal("chat.act"),
|
|
1802
|
+
rid: z24.string().min(1).max(100),
|
|
1803
|
+
qid: z24.string().min(1),
|
|
1523
1804
|
rows: launchRows.optional(),
|
|
1524
|
-
confirmed:
|
|
1805
|
+
confirmed: z24.literal(true).optional(),
|
|
1525
1806
|
/** « Refuser »: each row (session.decide only) answers its session's request with deny, whatever its own decision; never
|
|
1526
1807
|
* confirmed, and allowed on a request shown cut. The rows become `denied`. */
|
|
1527
|
-
deny:
|
|
1808
|
+
deny: z24.literal(true).optional()
|
|
1528
1809
|
}),
|
|
1529
1810
|
/** « Ignorer » the turn's cards, for every device and for Ledge (who is told at the next question); `undo` opens them
|
|
1530
1811
|
* again (« Revoir »). Refused while one of them is under way. Every client gets the turn's `ignoredAt` through chat.history. */
|
|
1531
|
-
|
|
1812
|
+
z24.object({ type: z24.literal("chat.ignore"), qid: z24.string().min(1), undo: z24.literal(true).optional() }),
|
|
1532
1813
|
/** A registry action run outside the chat (#93, the CLI): the daemon resolves `args` as it does a generic card's row, then
|
|
1533
1814
|
* applies it through its own handler. `by` the session that asks, left out for the user; `confirmed` for a destructive one. */
|
|
1534
|
-
|
|
1535
|
-
type:
|
|
1536
|
-
rid:
|
|
1537
|
-
by:
|
|
1815
|
+
z24.object({
|
|
1816
|
+
type: z24.literal("action.run"),
|
|
1817
|
+
rid: z24.string().min(1).max(100),
|
|
1818
|
+
by: z24.string().min(1).optional(),
|
|
1538
1819
|
/** `by`'s key (LEDGE_SESSION_KEY) */
|
|
1539
|
-
key:
|
|
1820
|
+
key: z24.string().max(128).optional(),
|
|
1540
1821
|
action: ActionIdSchema,
|
|
1541
|
-
args:
|
|
1542
|
-
confirmed:
|
|
1822
|
+
args: z24.record(z24.unknown()),
|
|
1823
|
+
confirmed: z24.literal(true).optional()
|
|
1543
1824
|
}),
|
|
1544
1825
|
/** The chat's close card: closes the turn's sessions, `rows` (indexes into its `closes`) or all of them. */
|
|
1545
|
-
|
|
1826
|
+
z24.object({ type: z24.literal("session.close"), qid: z24.string().min(1), rows: launchRows.optional() }),
|
|
1546
1827
|
/** `always` and `note` only where the harness offers them (`offers`); a note goes with `deny`, for the agent to read. */
|
|
1547
|
-
|
|
1548
|
-
type:
|
|
1549
|
-
id:
|
|
1828
|
+
z24.object({
|
|
1829
|
+
type: z24.literal("session.decide"),
|
|
1830
|
+
id: z24.string(),
|
|
1550
1831
|
decision: DecisionSchema,
|
|
1551
|
-
note:
|
|
1832
|
+
note: z24.string().trim().min(1).max(NOTE_MAX).optional(),
|
|
1552
1833
|
/** 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.
|
|
1553
1834
|
* `key` (pendingKey) covers its whole content; older fronts send kind and text only. */
|
|
1554
|
-
expect: PendingSchema.pick({ kind: true, text: true }).extend({ key:
|
|
1835
|
+
expect: PendingSchema.pick({ kind: true, text: true }).extend({ key: z24.string().max(64).optional() }).optional()
|
|
1555
1836
|
}),
|
|
1556
|
-
|
|
1557
|
-
|
|
1837
|
+
z24.object({ type: z24.literal("session.focus"), id: z24.string() }),
|
|
1838
|
+
z24.object({ type: z24.literal("session.kill"), id: z24.string() }),
|
|
1558
1839
|
/** `restoreQueue` (OMP): also take back what waits in its queue, returned as `session.queue.restored` to put in the draft. */
|
|
1559
|
-
|
|
1840
|
+
z24.object({ type: z24.literal("session.interrupt"), id: z24.string(), restoreQueue: z24.boolean().optional() }),
|
|
1560
1841
|
/** Removes a finished (exited) or read-only (hook_only) session from the list; refused for a live launcher session. */
|
|
1561
|
-
|
|
1842
|
+
z24.object({ type: z24.literal("session.dismiss"), id: z24.string() }),
|
|
1562
1843
|
/** Puts a session in the archive: it leaves the live list but is kept. */
|
|
1563
|
-
|
|
1844
|
+
z24.object({ type: z24.literal("session.archive"), id: z24.string() }),
|
|
1564
1845
|
/** Takes an archived session back out of the archive, as it was. */
|
|
1565
|
-
|
|
1846
|
+
z24.object({ type: z24.literal("session.restore"), id: z24.string() }),
|
|
1566
1847
|
/** Starts the agent again on an archived session's conversation. */
|
|
1567
|
-
|
|
1848
|
+
z24.object({ type: z24.literal("session.resume"), id: z24.string() }),
|
|
1568
1849
|
/** Changes a live session's model and effort (#58, lot 2), `null` for the agent's own default: the daemon restarts the
|
|
1569
1850
|
* agent on the same conversation with them, at once when the session is free, else at its next free moment. */
|
|
1570
|
-
|
|
1851
|
+
z24.object({ type: z24.literal("session.model"), id: z24.string(), model: ModelIdSchema.nullable(), effort: EffortSchema.nullable() }),
|
|
1571
1852
|
/** Deletes a session for good, archived or not. */
|
|
1572
|
-
|
|
1853
|
+
z24.object({ type: z24.literal("session.delete"), id: z24.string() }),
|
|
1573
1854
|
/** 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. */
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1855
|
+
z24.object({ type: z24.literal("session.rename"), id: z24.string(), topic: z24.string().max(500) }),
|
|
1856
|
+
z24.object({ type: z24.literal("session.thread"), id: z24.string() }),
|
|
1857
|
+
z24.object({ type: z24.literal("session.register"), session: SessionSchema }),
|
|
1577
1858
|
/** `agent` and `cwd` describe the session when the daemon does not know it yet (a hook from an agent started without ledge). */
|
|
1578
|
-
|
|
1579
|
-
type:
|
|
1580
|
-
id:
|
|
1859
|
+
z24.object({
|
|
1860
|
+
type: z24.literal("session.event"),
|
|
1861
|
+
id: z24.string(),
|
|
1581
1862
|
event: SessionEventSchema,
|
|
1582
1863
|
agent: ToolSchema.optional(),
|
|
1583
|
-
cwd:
|
|
1864
|
+
cwd: z24.string().optional(),
|
|
1584
1865
|
/** The OpenRig seat the agent runs in (`OPENRIG_SESSION_NAME`, `pod-member@rig`), when OpenRig launched it. */
|
|
1585
1866
|
rigSeat: RigSeatNameSchema.optional()
|
|
1586
1867
|
}),
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1868
|
+
z24.object({ type: z24.literal("session.unregister"), id: z24.string(), exitCode: z24.number().int().nullable() }),
|
|
1869
|
+
z24.object({ type: z24.literal("snapshot.request") }),
|
|
1870
|
+
z24.object({ type: z24.literal("chat.ask"), qid: z24.string().min(1), text: z24.string(), images: z24.array(ImageUploadSchema).max(ATTACHMENTS_MAX).optional(), tool: ToolSchema.optional() }),
|
|
1871
|
+
z24.object({ type: z24.literal("chat.cancel"), qid: z24.string().min(1) }),
|
|
1591
1872
|
/** Starts a new conversation with Ledge: the day's turns are cleared and the next question opens a new harness session. */
|
|
1592
|
-
|
|
1873
|
+
z24.object({ type: z24.literal("chat.reset") }),
|
|
1593
1874
|
/** Installs `@paradow/ledge@version` and restarts the service (spec 2026-10-12 auto-update §6). */
|
|
1594
|
-
|
|
1875
|
+
z24.object({ type: z24.literal("update.daemon"), version: z24.string().regex(SEMVER) }),
|
|
1595
1876
|
/** Replaces the daemon's machines list (first connected front wins); refused unless valid. */
|
|
1596
|
-
|
|
1877
|
+
z24.object({ type: z24.literal("machines.set"), machines: MachinesSchema }),
|
|
1597
1878
|
/** Replaces the local daemon's projects and rules as a whole; refused unless valid. */
|
|
1598
|
-
|
|
1879
|
+
z24.object({ type: z24.literal("projects.set"), projects: ProjectsSchema }),
|
|
1599
1880
|
/** Replaces the island's settings as a whole; refused unless valid. */
|
|
1600
|
-
|
|
1881
|
+
z24.object({ type: z24.literal("island.set"), prefs: IslandPrefsSchema }),
|
|
1601
1882
|
/** Mobile access (lot 3), local fronts only. A changed `relayUrl` drops the relay session. */
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1883
|
+
z24.object({ type: z24.literal("mobile.set"), enabled: z24.boolean(), relayUrl: RelayUrlSchema }),
|
|
1884
|
+
z24.object({ type: z24.literal("mobile.signin") }),
|
|
1885
|
+
z24.object({ type: z24.literal("mobile.signin.cancel") }),
|
|
1886
|
+
z24.object({ type: z24.literal("mobile.signout") }),
|
|
1606
1887
|
/** While the link is offline: try again now. */
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1888
|
+
z24.object({ type: z24.literal("mobile.retry") }),
|
|
1889
|
+
z24.object({ type: z24.literal("mobile.pairing.approve"), id: z24.string().min(1).max(100) }),
|
|
1890
|
+
z24.object({ type: z24.literal("mobile.pairing.deny"), id: z24.string().min(1).max(100) }),
|
|
1610
1891
|
/** `id`: the device's fingerprint. */
|
|
1611
|
-
|
|
1892
|
+
z24.object({ type: z24.literal("mobile.device.revoke"), id: z24.string().min(1).max(100) }),
|
|
1612
1893
|
/** « Taper dans les terminaux depuis le téléphone »; turned off, every phone loses the hand. */
|
|
1613
|
-
|
|
1894
|
+
z24.object({ type: z24.literal("mobile.terminal"), input: z24.boolean() }),
|
|
1614
1895
|
/** « Reprendre la main »: the phone typing into the session's terminal goes back to reading it. */
|
|
1615
|
-
|
|
1896
|
+
z24.object({ type: z24.literal("terminal.release"), id: z24.string().min(1) }),
|
|
1616
1897
|
/** 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. */
|
|
1617
|
-
|
|
1898
|
+
z24.object({ type: z24.literal("session.message"), rid: z24.string().min(1), from: z24.string().min(1), to: z24.string().min(1), text: z24.string().min(1).max(MESSAGE_MAX), summary: z24.string().max(SUMMARY_MAX).optional(), intercept: z24.boolean().optional() }),
|
|
1618
1899
|
/** The user writing to every session of a project, or to several sessions (ids or names). */
|
|
1619
|
-
|
|
1620
|
-
type:
|
|
1621
|
-
rid:
|
|
1622
|
-
to:
|
|
1623
|
-
text:
|
|
1624
|
-
summary:
|
|
1900
|
+
z24.object({
|
|
1901
|
+
type: z24.literal("session.broadcast"),
|
|
1902
|
+
rid: z24.string().min(1).max(100),
|
|
1903
|
+
to: z24.union([z24.object({ project: z24.string().min(1).max(80) }), z24.object({ sessions: z24.array(z24.string().min(1)).min(1).max(BROADCAST_SESSIONS_MAX) })]),
|
|
1904
|
+
text: z24.string().trim().min(1).max(MESSAGE_MAX),
|
|
1905
|
+
summary: z24.string().max(SUMMARY_MAX).optional(),
|
|
1625
1906
|
/** How the recipients are told who it went to (« (à <label>) »), instead of the names: a project the fleet turned into sessions. */
|
|
1626
|
-
label:
|
|
1907
|
+
label: z24.string().min(1).max(BROADCAST_LABEL_MAX).optional()
|
|
1627
1908
|
}),
|
|
1628
1909
|
/** `ledge read <id>`: a message's whole body, for its receiver or its sender (`by`, a session id). */
|
|
1629
|
-
|
|
1910
|
+
z24.object({ type: z24.literal("message.read"), id: z24.string().min(1).max(40), by: z24.string().min(1) }),
|
|
1630
1911
|
TaskCmdSchema,
|
|
1631
1912
|
SessionHandoverSchema,
|
|
1632
1913
|
ContextSetSchema,
|
|
@@ -1635,79 +1916,79 @@ var ClientMessageSchema = z23.discriminatedUnion("type", [
|
|
|
1635
1916
|
]);
|
|
1636
1917
|
|
|
1637
1918
|
// ../protocol/src/relay.ts
|
|
1638
|
-
import { z as
|
|
1919
|
+
import { z as z25 } from "zod";
|
|
1639
1920
|
var SEALED_MAX = 1048576;
|
|
1640
|
-
var RelayPeerSchema =
|
|
1641
|
-
var RelayClientMessageSchema =
|
|
1642
|
-
type:
|
|
1921
|
+
var RelayPeerSchema = z25.union([z25.literal("pc"), z25.string().regex(/^d_[A-Za-z0-9_-]{16,40}$/)]);
|
|
1922
|
+
var RelayClientMessageSchema = z25.object({
|
|
1923
|
+
type: z25.literal("send"),
|
|
1643
1924
|
to: RelayPeerSchema,
|
|
1644
|
-
body:
|
|
1925
|
+
body: z25.string().max(SEALED_MAX)
|
|
1645
1926
|
});
|
|
1646
|
-
var RelayServerMessageSchema =
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1927
|
+
var RelayServerMessageSchema = z25.discriminatedUnion("type", [
|
|
1928
|
+
z25.object({ type: z25.literal("welcome"), accountId: z25.string(), self: RelayPeerSchema, online: z25.array(RelayPeerSchema) }),
|
|
1929
|
+
z25.object({ type: z25.literal("deliver"), from: RelayPeerSchema, body: z25.string() }),
|
|
1930
|
+
z25.object({ type: z25.literal("presence"), peer: RelayPeerSchema, online: z25.boolean() }),
|
|
1650
1931
|
/**
|
|
1651
1932
|
* A frame was refused; `to` names the target when it is known.
|
|
1652
1933
|
* Contract: an error concerning a sealed body (`offline`, `too_large`, or a socket closed with 4008
|
|
1653
1934
|
* `rate_limited`) means that body was not delivered, so the sequence on that channel is lost: the
|
|
1654
1935
|
* sender must re-handshake with that peer before sending to it again.
|
|
1655
1936
|
*/
|
|
1656
|
-
|
|
1657
|
-
type:
|
|
1658
|
-
code:
|
|
1937
|
+
z25.object({
|
|
1938
|
+
type: z25.literal("error"),
|
|
1939
|
+
code: z25.enum(["bad_frame", "too_large", "bad_target", "offline", "rate_limited"]),
|
|
1659
1940
|
to: RelayPeerSchema.optional()
|
|
1660
1941
|
})
|
|
1661
1942
|
]);
|
|
1662
1943
|
var PAIR_LABEL_MAX = 40;
|
|
1663
|
-
var KeyB64 =
|
|
1664
|
-
var Nonce =
|
|
1665
|
-
var Fingerprint =
|
|
1666
|
-
var SealedFrameSchema =
|
|
1944
|
+
var KeyB64 = z25.string().regex(/^[A-Za-z0-9_-]{87}$/);
|
|
1945
|
+
var Nonce = z25.string().regex(/^[A-Za-z0-9_-]{43}$/);
|
|
1946
|
+
var Fingerprint = z25.string().regex(/^[A-Za-z0-9_-]{22}$/);
|
|
1947
|
+
var SealedFrameSchema = z25.discriminatedUnion("k", [
|
|
1667
1948
|
/** `e` is the ephemeral key, `f` the fingerprint of the sender's static key. */
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1949
|
+
z25.object({ k: z25.literal("hello"), e: KeyB64, f: Fingerprint }),
|
|
1950
|
+
z25.object({ k: z25.literal("msg"), s: z25.string() }),
|
|
1951
|
+
z25.object({ k: z25.literal("pair_commit"), c: Nonce, label: z25.string().trim().min(1).max(PAIR_LABEL_MAX) }),
|
|
1952
|
+
z25.object({ k: z25.literal("pair_offer"), pc: KeyB64, n: Nonce }),
|
|
1953
|
+
z25.object({ k: z25.literal("pair_reveal"), d: KeyB64, n: Nonce }),
|
|
1954
|
+
z25.object({ k: z25.literal("pair_ok") }),
|
|
1955
|
+
z25.object({ k: z25.literal("pair_no") }),
|
|
1956
|
+
z25.object({ k: z25.literal("denied") }),
|
|
1957
|
+
z25.object({ k: z25.literal("rekey") })
|
|
1677
1958
|
]);
|
|
1678
1959
|
|
|
1679
1960
|
// ../protocol/src/relayAuth.ts
|
|
1680
|
-
import { z as
|
|
1681
|
-
var DeviceStartResponseSchema =
|
|
1682
|
-
deviceCode:
|
|
1683
|
-
userCode:
|
|
1684
|
-
verificationUri:
|
|
1685
|
-
expiresIn:
|
|
1686
|
-
interval:
|
|
1687
|
-
});
|
|
1688
|
-
var DevicePollResponseSchema =
|
|
1689
|
-
|
|
1690
|
-
|
|
1961
|
+
import { z as z26 } from "zod";
|
|
1962
|
+
var DeviceStartResponseSchema = z26.object({
|
|
1963
|
+
deviceCode: z26.string().min(1),
|
|
1964
|
+
userCode: z26.string().min(1),
|
|
1965
|
+
verificationUri: z26.string().min(1),
|
|
1966
|
+
expiresIn: z26.number().int().positive(),
|
|
1967
|
+
interval: z26.number().int().positive()
|
|
1968
|
+
});
|
|
1969
|
+
var DevicePollResponseSchema = z26.discriminatedUnion("status", [
|
|
1970
|
+
z26.object({ status: z26.literal("pending") }),
|
|
1971
|
+
z26.object({ status: z26.literal("slow_down"), interval: z26.number().int().positive() }),
|
|
1691
1972
|
/** `token` is the relay session token, shown this once only. */
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1973
|
+
z26.object({ status: z26.literal("ok"), token: z26.string().min(1), accountId: z26.string().min(1), login: z26.string().min(1) }),
|
|
1974
|
+
z26.object({ status: z26.literal("expired") }),
|
|
1975
|
+
z26.object({ status: z26.literal("denied") }),
|
|
1976
|
+
z26.object({ status: z26.literal("not_allowed") })
|
|
1696
1977
|
]);
|
|
1697
|
-
var DeviceRevokeResponseSchema =
|
|
1698
|
-
var MeResponseSchema =
|
|
1699
|
-
accountId:
|
|
1700
|
-
login:
|
|
1978
|
+
var DeviceRevokeResponseSchema = z26.object({ revoked: z26.number().int().min(0) });
|
|
1979
|
+
var MeResponseSchema = z26.object({
|
|
1980
|
+
accountId: z26.string().min(1),
|
|
1981
|
+
login: z26.string().min(1),
|
|
1701
1982
|
peer: RelayPeerSchema,
|
|
1702
|
-
kind:
|
|
1983
|
+
kind: z26.enum(["pc", "device"])
|
|
1703
1984
|
});
|
|
1704
1985
|
|
|
1705
1986
|
// ../protocol/src/phone.ts
|
|
1706
|
-
import { z as
|
|
1987
|
+
import { z as z28 } from "zod";
|
|
1707
1988
|
|
|
1708
1989
|
// ../protocol/src/push.ts
|
|
1709
|
-
import { z as
|
|
1710
|
-
var B64U =
|
|
1990
|
+
import { z as z27 } from "zod";
|
|
1991
|
+
var B64U = z27.string().regex(/^[A-Za-z0-9_-]*$/);
|
|
1711
1992
|
var PUSH_HOSTS = ["*.push.apple.com", "fcm.googleapis.com", "updates.push.services.mozilla.com", "*.notify.windows.com"];
|
|
1712
1993
|
function pushEndpointAllowed(url) {
|
|
1713
1994
|
let u;
|
|
@@ -1724,67 +2005,70 @@ function pushEndpointAllowed(url) {
|
|
|
1724
2005
|
return host.length > suffix.length && host.endsWith(suffix);
|
|
1725
2006
|
});
|
|
1726
2007
|
}
|
|
1727
|
-
var PushSendSchema =
|
|
1728
|
-
endpoint:
|
|
2008
|
+
var PushSendSchema = z27.object({
|
|
2009
|
+
endpoint: z27.string().url().max(1024),
|
|
1729
2010
|
body: B64U.max(5462),
|
|
1730
|
-
ttl:
|
|
1731
|
-
urgency:
|
|
1732
|
-
topic:
|
|
1733
|
-
});
|
|
1734
|
-
var PushSendResultSchema =
|
|
1735
|
-
result:
|
|
1736
|
-
status:
|
|
1737
|
-
});
|
|
1738
|
-
var PushSubscriptionSchema =
|
|
1739
|
-
endpoint:
|
|
1740
|
-
keys:
|
|
1741
|
-
});
|
|
1742
|
-
var PushPayloadSchema =
|
|
1743
|
-
v:
|
|
1744
|
-
kind:
|
|
1745
|
-
title:
|
|
1746
|
-
body:
|
|
1747
|
-
url:
|
|
1748
|
-
badge:
|
|
1749
|
-
tag:
|
|
2011
|
+
ttl: z27.number().int().min(0).max(86400),
|
|
2012
|
+
urgency: z27.enum(["normal", "high"]),
|
|
2013
|
+
topic: z27.string().regex(/^[A-Za-z0-9_-]{1,32}$/)
|
|
2014
|
+
});
|
|
2015
|
+
var PushSendResultSchema = z27.object({
|
|
2016
|
+
result: z27.enum(["sent", "gone", "failed"]),
|
|
2017
|
+
status: z27.number().int().optional()
|
|
2018
|
+
});
|
|
2019
|
+
var PushSubscriptionSchema = z27.object({
|
|
2020
|
+
endpoint: z27.string().url().max(1024).refine(pushEndpointAllowed),
|
|
2021
|
+
keys: z27.object({ p256dh: B64U.length(87), auth: B64U.length(22) })
|
|
2022
|
+
});
|
|
2023
|
+
var PushPayloadSchema = z27.object({
|
|
2024
|
+
v: z27.literal(1),
|
|
2025
|
+
kind: z27.enum(["request", "question", "finished", "chat", "decision"]),
|
|
2026
|
+
title: z27.string().max(80),
|
|
2027
|
+
body: z27.string().max(120),
|
|
2028
|
+
url: z27.string().regex(/^\/#\/[A-Za-z0-9/%._~-]*$/).max(300),
|
|
2029
|
+
badge: z27.number().int().min(0).max(999),
|
|
2030
|
+
tag: z27.string().regex(/^[A-Za-z0-9_-]{1,32}$/)
|
|
1750
2031
|
});
|
|
1751
2032
|
|
|
1752
2033
|
// ../protocol/src/phone.ts
|
|
2034
|
+
var PHONE_ROW_SUMMARY_MAX = 200;
|
|
2035
|
+
var PhoneRigRowSchema = RigRowSchema.extend({ summary: z28.string().max(PHONE_ROW_SUMMARY_MAX).optional() });
|
|
2036
|
+
var PhoneRowErrorSchema = z28.enum(["not_found", "unavailable"]);
|
|
1753
2037
|
var PHONE_PROTOCOL = 1;
|
|
1754
2038
|
var utf8 = new TextEncoder();
|
|
1755
2039
|
var THREAD_TEXT_MAX = 8192;
|
|
1756
2040
|
var TOOL_TEXT_MAX = 2048;
|
|
1757
2041
|
var CHECKLIST_TASKS_MAX = 50;
|
|
1758
2042
|
var CHECKLIST_TEXT_MAX = 200;
|
|
1759
|
-
var ChecklistStatusSchema =
|
|
1760
|
-
var PhoneChecklistSchema =
|
|
1761
|
-
tasks:
|
|
1762
|
-
done:
|
|
1763
|
-
total:
|
|
1764
|
-
cut:
|
|
1765
|
-
});
|
|
1766
|
-
var PhoneChecklistSummarySchema =
|
|
1767
|
-
done:
|
|
1768
|
-
total:
|
|
1769
|
-
current:
|
|
1770
|
-
cut:
|
|
1771
|
-
});
|
|
1772
|
-
var RiskSchema =
|
|
1773
|
-
var RiskReasonSchema =
|
|
1774
|
-
var OperationIdSchema =
|
|
1775
|
-
var OperationStatusSchema =
|
|
1776
|
-
var OperationReasonSchema =
|
|
1777
|
-
var CapabilitiesSchema =
|
|
2043
|
+
var ChecklistStatusSchema = z28.enum(["pending", "in_progress", "completed"]);
|
|
2044
|
+
var PhoneChecklistSchema = z28.object({
|
|
2045
|
+
tasks: z28.array(z28.object({ text: z28.string().max(CHECKLIST_TEXT_MAX), status: ChecklistStatusSchema })).max(CHECKLIST_TASKS_MAX),
|
|
2046
|
+
done: z28.number().int().nonnegative(),
|
|
2047
|
+
total: z28.number().int().nonnegative(),
|
|
2048
|
+
cut: z28.literal(true).optional()
|
|
2049
|
+
});
|
|
2050
|
+
var PhoneChecklistSummarySchema = z28.object({
|
|
2051
|
+
done: z28.number().int().nonnegative(),
|
|
2052
|
+
total: z28.number().int().nonnegative(),
|
|
2053
|
+
current: z28.string().max(CHECKLIST_TEXT_MAX).optional(),
|
|
2054
|
+
cut: z28.literal(true).optional()
|
|
2055
|
+
});
|
|
2056
|
+
var RiskSchema = z28.enum(["low", "high"]);
|
|
2057
|
+
var RiskReasonSchema = z28.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"]);
|
|
2058
|
+
var OperationIdSchema = z28.string().uuid();
|
|
2059
|
+
var OperationStatusSchema = z28.enum(["received", "applied", "rejected", "failed"]);
|
|
2060
|
+
var OperationReasonSchema = z28.enum([...RefusalCodeSchema.options, "untypable", "confirm_required", "conflict", "assertion_invalid", "assertion_expired", "scheduled", ...ModelRefusalSchema.options]);
|
|
2061
|
+
var CapabilitiesSchema = z28.object({
|
|
1778
2062
|
/** A question can be answered in free text. */
|
|
1779
|
-
freeTextAnswer:
|
|
2063
|
+
freeTextAnswer: z28.boolean(),
|
|
1780
2064
|
/** The session's accept/deny can be typed; absent = true. */
|
|
1781
|
-
decide:
|
|
2065
|
+
decide: z28.boolean().optional(),
|
|
1782
2066
|
/** OMP's dialog questions can be answered from the phone; absent = false. */
|
|
1783
|
-
questions:
|
|
2067
|
+
questions: z28.boolean().optional()
|
|
1784
2068
|
});
|
|
1785
|
-
var CapabilitiesByToolSchema =
|
|
1786
|
-
var PhonePendingSchema = PendingSchema.extend({ key:
|
|
1787
|
-
var SwitchableSessionSchema = SessionSchema.extend({ canSwitch:
|
|
2069
|
+
var CapabilitiesByToolSchema = z28.object({ claude: CapabilitiesSchema, codex: CapabilitiesSchema, cursor: CapabilitiesSchema, omp: CapabilitiesSchema });
|
|
2070
|
+
var PhonePendingSchema = PendingSchema.extend({ key: z28.string().max(64), risk: RiskSchema, riskReason: RiskReasonSchema, cut: z28.literal(true).optional() });
|
|
2071
|
+
var SwitchableSessionSchema = SessionSchema.extend({ canSwitch: z28.literal(true).optional() });
|
|
1788
2072
|
var PhoneSessionSchema = SwitchableSessionSchema.pick({
|
|
1789
2073
|
id: true,
|
|
1790
2074
|
name: true,
|
|
@@ -1812,348 +2096,403 @@ var PhoneSessionSchema = SwitchableSessionSchema.pick({
|
|
|
1812
2096
|
exitedAt: true
|
|
1813
2097
|
}).extend({
|
|
1814
2098
|
pending: PhonePendingSchema.optional(),
|
|
1815
|
-
unread:
|
|
1816
|
-
handover:
|
|
2099
|
+
unread: z28.boolean(),
|
|
2100
|
+
handover: z28.object({ state: HandoverStateSchema, toName: z28.string().max(80).optional() }).strict().optional(),
|
|
1817
2101
|
/** Absent: the agent has no checklist. */
|
|
1818
2102
|
checklist: PhoneChecklistSummarySchema.optional(),
|
|
1819
2103
|
/** Started without Ledge (`hook_only`): the phone can neither close nor resume it. */
|
|
1820
|
-
readOnly:
|
|
2104
|
+
readOnly: z28.literal(true).optional()
|
|
1821
2105
|
});
|
|
1822
|
-
var B64U2 =
|
|
1823
|
-
var CredentialSchema =
|
|
2106
|
+
var B64U2 = z28.string().regex(/^[A-Za-z0-9_-]*$/);
|
|
2107
|
+
var CredentialSchema = z28.object({
|
|
1824
2108
|
id: B64U2.max(1366),
|
|
1825
2109
|
publicKey: B64U2.max(2048),
|
|
1826
|
-
alg:
|
|
2110
|
+
alg: z28.union([z28.literal(-7), z28.literal(-257)]),
|
|
1827
2111
|
clientDataJSON: B64U2.max(4096),
|
|
1828
2112
|
authenticatorData: B64U2.max(4096),
|
|
1829
|
-
transports:
|
|
2113
|
+
transports: z28.array(z28.string().max(20)).max(8).optional()
|
|
1830
2114
|
});
|
|
1831
|
-
var AssertionSchema =
|
|
2115
|
+
var AssertionSchema = z28.object({
|
|
1832
2116
|
id: B64U2.max(1366),
|
|
1833
2117
|
clientDataJSON: B64U2.max(4096),
|
|
1834
2118
|
authenticatorData: B64U2.max(4096),
|
|
1835
2119
|
signature: B64U2.max(1024)
|
|
1836
2120
|
});
|
|
1837
|
-
var ConfirmPurposeSchema =
|
|
2121
|
+
var ConfirmPurposeSchema = z28.enum(["register", "decide", "launch", "close", "act", "pc", "terminal", "rig"]);
|
|
1838
2122
|
var PhoneChatStepSchema = ChatStepSchema.pick({ kind: true, id: true, name: true }).strict();
|
|
1839
|
-
var PhoneChatProposalSchema =
|
|
1840
|
-
project:
|
|
2123
|
+
var PhoneChatProposalSchema = z28.object({
|
|
2124
|
+
project: z28.string().max(80),
|
|
1841
2125
|
tool: ToolSchema,
|
|
1842
|
-
prompt:
|
|
1843
|
-
launchedId:
|
|
1844
|
-
failed:
|
|
2126
|
+
prompt: z28.string().max(TOOL_TEXT_MAX).optional(),
|
|
2127
|
+
launchedId: z28.string().optional(),
|
|
2128
|
+
failed: z28.literal(true).optional()
|
|
1845
2129
|
}).strict();
|
|
1846
|
-
var PhoneChatRelaySchema =
|
|
1847
|
-
id:
|
|
1848
|
-
name:
|
|
2130
|
+
var PhoneChatRelaySchema = z28.object({
|
|
2131
|
+
id: z28.string().min(1),
|
|
2132
|
+
name: z28.string(),
|
|
1849
2133
|
tool: ToolSchema,
|
|
1850
|
-
text:
|
|
1851
|
-
status:
|
|
1852
|
-
sentAt:
|
|
1853
|
-
failed:
|
|
2134
|
+
text: z28.string().max(TOOL_TEXT_MAX),
|
|
2135
|
+
status: z28.enum(["queued", "sent"]).optional(),
|
|
2136
|
+
sentAt: z28.string().datetime().optional(),
|
|
2137
|
+
failed: z28.literal(true).optional(),
|
|
1854
2138
|
/** With `failed`: why, as the PC's code (never its text). */
|
|
1855
|
-
code:
|
|
2139
|
+
code: z28.string().max(40).optional()
|
|
1856
2140
|
}).strict();
|
|
1857
|
-
var CloseHowSchema =
|
|
1858
|
-
var PhoneChatCloseSchema =
|
|
1859
|
-
id:
|
|
1860
|
-
name:
|
|
2141
|
+
var CloseHowSchema = z28.enum(["exit", "killed"]);
|
|
2142
|
+
var PhoneChatCloseSchema = z28.object({
|
|
2143
|
+
id: z28.string().min(1),
|
|
2144
|
+
name: z28.string(),
|
|
1861
2145
|
tool: ToolSchema,
|
|
1862
2146
|
how: CloseHowSchema.optional(),
|
|
1863
|
-
closedAt:
|
|
1864
|
-
failed:
|
|
2147
|
+
closedAt: z28.string().datetime().optional(),
|
|
2148
|
+
failed: z28.literal(true).optional()
|
|
1865
2149
|
}).strict();
|
|
1866
|
-
var PhoneChatTaskSchema =
|
|
1867
|
-
to:
|
|
1868
|
-
toName:
|
|
1869
|
-
title:
|
|
1870
|
-
text:
|
|
1871
|
-
taskId:
|
|
1872
|
-
ownerName:
|
|
1873
|
-
state:
|
|
1874
|
-
failed:
|
|
2150
|
+
var PhoneChatTaskSchema = z28.object({
|
|
2151
|
+
to: z28.string().max(80),
|
|
2152
|
+
toName: z28.string().max(80).optional(),
|
|
2153
|
+
title: z28.string().max(200),
|
|
2154
|
+
text: z28.string().max(TOOL_TEXT_MAX).optional(),
|
|
2155
|
+
taskId: z28.string().max(40).optional(),
|
|
2156
|
+
ownerName: z28.string().max(80).optional(),
|
|
2157
|
+
state: z28.enum(["open", "taken", "done", "refused", "dropped"]).optional(),
|
|
2158
|
+
failed: z28.literal(true).optional()
|
|
1875
2159
|
}).strict();
|
|
1876
|
-
var PhoneChatActionSchema =
|
|
1877
|
-
action:
|
|
1878
|
-
summary:
|
|
2160
|
+
var PhoneChatActionSchema = z28.object({
|
|
2161
|
+
action: z28.string().min(1).max(40),
|
|
2162
|
+
summary: z28.string().max(300),
|
|
1879
2163
|
vars: ActionVarsSchema.optional(),
|
|
1880
|
-
detail:
|
|
1881
|
-
detailCut:
|
|
2164
|
+
detail: z28.string().max(TOOL_TEXT_MAX).optional(),
|
|
2165
|
+
detailCut: z28.literal(true).optional(),
|
|
1882
2166
|
risk: ActionRiskSchema,
|
|
1883
|
-
session:
|
|
2167
|
+
session: z28.string().max(100).optional(),
|
|
1884
2168
|
state: ChatActionStateSchema.optional(),
|
|
1885
|
-
doneAt:
|
|
1886
|
-
code:
|
|
2169
|
+
doneAt: z28.string().datetime().optional(),
|
|
2170
|
+
code: z28.string().max(40).optional()
|
|
1887
2171
|
}).strict();
|
|
1888
|
-
var PhoneTaskSchema =
|
|
1889
|
-
id:
|
|
1890
|
-
title:
|
|
1891
|
-
owner:
|
|
1892
|
-
ownerName:
|
|
2172
|
+
var PhoneTaskSchema = z28.object({
|
|
2173
|
+
id: z28.string().max(40),
|
|
2174
|
+
title: z28.string().max(200),
|
|
2175
|
+
owner: z28.string().max(100),
|
|
2176
|
+
ownerName: z28.string().max(80),
|
|
1893
2177
|
/** 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). */
|
|
1894
|
-
giver:
|
|
1895
|
-
giverName:
|
|
1896
|
-
text:
|
|
1897
|
-
state:
|
|
1898
|
-
result:
|
|
1899
|
-
cut:
|
|
1900
|
-
at:
|
|
1901
|
-
updatedAt:
|
|
1902
|
-
seen:
|
|
2178
|
+
giver: z28.string().max(100).optional(),
|
|
2179
|
+
giverName: z28.string().max(80).optional(),
|
|
2180
|
+
text: z28.string().max(TOOL_TEXT_MAX).optional(),
|
|
2181
|
+
state: z28.enum(["open", "taken", "done", "refused", "dropped"]),
|
|
2182
|
+
result: z28.string().max(TOOL_TEXT_MAX).optional(),
|
|
2183
|
+
cut: z28.literal(true).optional(),
|
|
2184
|
+
at: z28.string(),
|
|
2185
|
+
updatedAt: z28.string(),
|
|
2186
|
+
seen: z28.literal(true).optional()
|
|
1903
2187
|
}).strict();
|
|
1904
|
-
var ChatErrorCodeSchema =
|
|
1905
|
-
var PhoneChatTurnSchema =
|
|
2188
|
+
var ChatErrorCodeSchema = z28.enum(["busy", "timeout", "cancelled", "failed"]);
|
|
2189
|
+
var PhoneChatTurnSchema = z28.object({
|
|
1906
2190
|
qid: OperationIdSchema,
|
|
1907
|
-
question:
|
|
1908
|
-
askedAt:
|
|
1909
|
-
steps:
|
|
1910
|
-
answer:
|
|
1911
|
-
answeredAt:
|
|
2191
|
+
question: z28.string().max(MESSAGE_MAX),
|
|
2192
|
+
askedAt: z28.string().datetime(),
|
|
2193
|
+
steps: z28.array(PhoneChatStepSchema).max(50),
|
|
2194
|
+
answer: z28.string().max(THREAD_TEXT_MAX).optional(),
|
|
2195
|
+
answeredAt: z28.string().datetime().optional(),
|
|
1912
2196
|
error: ChatErrorCodeSchema.optional(),
|
|
1913
|
-
proposals:
|
|
2197
|
+
proposals: z28.array(PhoneChatProposalSchema).max(MAX_PROPOSALS).optional(),
|
|
1914
2198
|
relay: PhoneChatRelaySchema.optional(),
|
|
1915
|
-
closes:
|
|
1916
|
-
cut:
|
|
1917
|
-
tasks:
|
|
1918
|
-
actions:
|
|
1919
|
-
ignoredAt:
|
|
1920
|
-
attachments:
|
|
2199
|
+
closes: z28.array(PhoneChatCloseSchema).min(1).max(MAX_PROPOSALS).optional(),
|
|
2200
|
+
cut: z28.literal(true).optional(),
|
|
2201
|
+
tasks: z28.array(PhoneChatTaskSchema).min(1).max(MAX_PROPOSALS).optional(),
|
|
2202
|
+
actions: z28.array(PhoneChatActionSchema).min(1).max(MAX_PROPOSALS).optional(),
|
|
2203
|
+
ignoredAt: z28.string().datetime().optional(),
|
|
2204
|
+
attachments: z28.array(AttachmentRefSchema).max(ATTACHMENTS_MAX).optional()
|
|
1921
2205
|
}).strict();
|
|
1922
|
-
var sessionId =
|
|
1923
|
-
var PhoneExpectSchema = PendingSchema.pick({ kind: true, text: true }).extend({ key:
|
|
1924
|
-
var CardRowsSchema =
|
|
2206
|
+
var sessionId = z28.string().min(1).max(100);
|
|
2207
|
+
var PhoneExpectSchema = PendingSchema.pick({ kind: true, text: true }).extend({ key: z28.string().min(1).max(64) });
|
|
2208
|
+
var CardRowsSchema = z28.array(z28.number().int().min(0).max(MAX_PROPOSALS - 1)).min(1).max(MAX_PROPOSALS).refine((r) => new Set(r).size === r.length, "a row twice");
|
|
1925
2209
|
var pcOp = { op: OperationIdSchema, assertion: AssertionSchema.optional() };
|
|
1926
|
-
var PcActionSchema =
|
|
1927
|
-
var TerminalIdSchema =
|
|
2210
|
+
var PcActionSchema = z28.enum(["update", "restart"]);
|
|
2211
|
+
var TerminalIdSchema = z28.string().regex(/^t_[A-Za-z0-9_-]{8,40}$/);
|
|
1928
2212
|
var TERM_INPUT_MAX = 4096;
|
|
1929
2213
|
var TERM_SCREEN_MAX = 65536;
|
|
1930
2214
|
var TERM_OUTPUT_MAX = 32768;
|
|
1931
|
-
var TermDimSchema =
|
|
1932
|
-
var TermReleaseWhySchema =
|
|
1933
|
-
var TermExitWhySchema =
|
|
1934
|
-
var PhoneClientMessageSchema =
|
|
1935
|
-
|
|
1936
|
-
|
|
2215
|
+
var TermDimSchema = z28.number().int().min(1).max(1e4);
|
|
2216
|
+
var TermReleaseWhySchema = z28.enum(["asked", "pc", "idle", "expired", "background", "link"]);
|
|
2217
|
+
var TermExitWhySchema = z28.enum(["ended", "gone", "closed", "superseded", "failed"]);
|
|
2218
|
+
var PhoneClientMessageSchema = z28.discriminatedUnion("type", [
|
|
2219
|
+
z28.object({ type: z28.literal("snapshot.request") }),
|
|
2220
|
+
z28.object({ type: z28.literal("session.thread"), id: sessionId }),
|
|
1937
2221
|
/** The session's request with its whole body (up to `PENDING_BODY_MAX`). */
|
|
1938
|
-
|
|
2222
|
+
z28.object({ type: z28.literal("session.pending"), id: sessionId }),
|
|
1939
2223
|
/** The session whose terminal the phone shows; null when it shows none. */
|
|
1940
|
-
|
|
1941
|
-
|
|
2224
|
+
z28.object({ type: z28.literal("session.watch"), id: sessionId.nullable() }),
|
|
2225
|
+
z28.object({ type: z28.literal("session.read"), id: sessionId, at: z28.string().datetime() }),
|
|
1942
2226
|
/**
|
|
1943
2227
|
* Asks for a challenge. One object for every purpose: the gateway refuses a `decide` without `id` and `expect`. A `close`
|
|
1944
2228
|
* or `act` names a turn of the chat (`qid`) or a session (`id`, for `session.close` and `session.resume`).
|
|
1945
2229
|
*/
|
|
1946
|
-
|
|
1947
|
-
type:
|
|
2230
|
+
z28.object({
|
|
2231
|
+
type: z28.literal("confirm.prepare"),
|
|
1948
2232
|
purpose: ConfirmPurposeSchema,
|
|
1949
2233
|
id: sessionId.optional(),
|
|
1950
2234
|
expect: PhoneExpectSchema.optional(),
|
|
1951
2235
|
qid: OperationIdSchema.optional(),
|
|
1952
2236
|
/** A `pc` challenge: what it is for, and for an update the version it covers. */
|
|
1953
2237
|
action: PcActionSchema.optional(),
|
|
1954
|
-
version:
|
|
2238
|
+
version: z28.string().regex(SEMVER).optional(),
|
|
2239
|
+
/** A `rig` challenge: the OpenRig row and the exact answer it covers. */
|
|
2240
|
+
qitem: z28.string().min(1).max(200).optional(),
|
|
2241
|
+
answer: HumanAnswerSchema.optional()
|
|
1955
2242
|
}),
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
type:
|
|
2243
|
+
z28.object({ type: z28.literal("credential.register"), op: OperationIdSchema, credential: CredentialSchema }),
|
|
2244
|
+
z28.object({
|
|
2245
|
+
type: z28.literal("session.decide"),
|
|
1959
2246
|
op: OperationIdSchema,
|
|
1960
2247
|
id: sessionId,
|
|
1961
2248
|
decision: DecisionSchema,
|
|
1962
|
-
note:
|
|
2249
|
+
note: z28.string().trim().min(1).max(NOTE_MAX).optional(),
|
|
1963
2250
|
/** The request the phone showed: refused unless it is still the pending one. */
|
|
1964
2251
|
expect: PhoneExpectSchema,
|
|
1965
2252
|
/** The device check over the challenge of `confirm.prepare`; required by the daemon for a high-risk accept. */
|
|
1966
2253
|
assertion: AssertionSchema.optional()
|
|
1967
2254
|
}),
|
|
1968
|
-
|
|
1969
|
-
type:
|
|
2255
|
+
z28.object({
|
|
2256
|
+
type: z28.literal("session.reply"),
|
|
1970
2257
|
op: OperationIdSchema,
|
|
1971
2258
|
id: sessionId,
|
|
1972
|
-
text:
|
|
1973
|
-
attachments:
|
|
2259
|
+
text: z28.string().max(MESSAGE_MAX),
|
|
2260
|
+
attachments: z28.array(AttachmentIdSchema).max(ATTACHMENTS_MAX).optional(),
|
|
1974
2261
|
/** The request the reply answers, or null when the phone showed none: refused if the session's request is no longer that one. */
|
|
1975
2262
|
expect: PhoneExpectSchema.nullable(),
|
|
1976
2263
|
assertion: AssertionSchema.optional()
|
|
1977
2264
|
}),
|
|
1978
2265
|
/** OMP's `ask`: the answers to every question of the dialog `requestId`, in order; `cancel` declines it. Bounds as the local `session.answer`. */
|
|
1979
|
-
|
|
1980
|
-
type:
|
|
2266
|
+
z28.object({
|
|
2267
|
+
type: z28.literal("session.answer"),
|
|
1981
2268
|
op: OperationIdSchema,
|
|
1982
2269
|
id: sessionId,
|
|
1983
|
-
requestId:
|
|
1984
|
-
answers:
|
|
1985
|
-
cancel:
|
|
2270
|
+
requestId: z28.string().min(1).max(200),
|
|
2271
|
+
answers: z28.array(z28.object({ id: z28.string().max(200), selected: z28.array(z28.string().max(2e3)).max(100), text: z28.string().max(2e4).optional() })).max(50),
|
|
2272
|
+
cancel: z28.literal(true).optional(),
|
|
1986
2273
|
expect: PhoneExpectSchema
|
|
1987
2274
|
}),
|
|
1988
|
-
|
|
2275
|
+
z28.object({ type: z28.literal("session.interrupt"), op: OperationIdSchema, id: sessionId }),
|
|
1989
2276
|
/** Ends a live session as `/exit` would, killed as a last resort; answered with its `how`. Passkey, purpose `close`, over the session. */
|
|
1990
|
-
|
|
2277
|
+
z28.object({ type: z28.literal("session.close"), op: OperationIdSchema, id: sessionId, assertion: AssertionSchema.optional() }),
|
|
1991
2278
|
/** Starts an ended session again on its conversation. Passkey, purpose `act`, over the session. */
|
|
1992
|
-
|
|
2279
|
+
z28.object({ type: z28.literal("session.resume"), op: OperationIdSchema, id: sessionId, assertion: AssertionSchema.optional() }),
|
|
1993
2280
|
/** 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`. */
|
|
1994
|
-
|
|
2281
|
+
z28.object({ type: z28.literal("session.model"), op: OperationIdSchema, id: sessionId, model: ModelIdSchema.nullable(), effort: EffortSchema.nullable() }),
|
|
1995
2282
|
/** One message to several sessions (ids); answered by `operation.result` with `sent` and `refused`. */
|
|
1996
|
-
|
|
2283
|
+
z28.object({ type: z28.literal("session.broadcast"), op: OperationIdSchema, sessions: z28.array(sessionId).min(1).max(BROADCAST_SESSIONS_MAX), text: z28.string().trim().min(1).max(MESSAGE_MAX) }).strict(),
|
|
1997
2284
|
/** The models an agent offers on the PC. */
|
|
1998
|
-
|
|
2285
|
+
z28.object({ type: z28.literal("models.list"), tool: ToolSchema }),
|
|
1999
2286
|
/** The slash commands of a session's agent, for the « / » list of its field. */
|
|
2000
|
-
|
|
2287
|
+
z28.object({ type: z28.literal("session.commands"), id: sessionId }),
|
|
2288
|
+
/** A queue row's state, for an OpenRig task card: asked while the card shows and every `RIG_ROW_REFRESH_MS` (`daemon.rigRows`). */
|
|
2289
|
+
z28.object({ type: z28.literal("openrig.row"), qitem: QitemIdSchema }),
|
|
2001
2290
|
/** The phone drops its passkey from the daemon. */
|
|
2002
|
-
|
|
2291
|
+
z28.object({ type: z28.literal("device.forget"), op: OperationIdSchema }),
|
|
2003
2292
|
/** The phone's push subscription, with the VAPID key it was made with (so a key change is seen). */
|
|
2004
|
-
|
|
2005
|
-
|
|
2293
|
+
z28.object({ type: z28.literal("push.subscribe"), op: OperationIdSchema, subscription: PushSubscriptionSchema, key: B64U2.length(87) }),
|
|
2294
|
+
z28.object({ type: z28.literal("push.unsubscribe"), op: OperationIdSchema }),
|
|
2006
2295
|
/** The phone's own chat with Ledge. */
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2296
|
+
z28.object({ type: z28.literal("chat.ask"), qid: OperationIdSchema, text: z28.string().trim().max(MESSAGE_MAX), attachments: z28.array(AttachmentIdSchema).max(ATTACHMENTS_MAX).optional() }),
|
|
2297
|
+
z28.object({ type: z28.literal("chat.cancel"), qid: OperationIdSchema }),
|
|
2298
|
+
z28.object({ type: z28.literal("chat.reset") }),
|
|
2299
|
+
z28.object({ type: z28.literal("chat.history") }),
|
|
2011
2300
|
/** An image to store, sent alone before the message that names it; no `id`, and at most `ATTACHMENT_MAX` bytes. */
|
|
2012
|
-
AttachmentPutSchema.pick({ type: true, requestId: true }).extend({ image: ImageUploadSchema.extend({ data:
|
|
2301
|
+
AttachmentPutSchema.pick({ type: true, requestId: true }).extend({ image: ImageUploadSchema.extend({ data: z28.string().min(1).max(b64Max(ATTACHMENT_MAX)) }) }),
|
|
2013
2302
|
AttachmentGetSchema.pick({ type: true, requestId: true, attachment: true, preview: true }),
|
|
2014
|
-
|
|
2303
|
+
z28.object({ type: z28.literal("chat.relay"), op: OperationIdSchema, qid: OperationIdSchema }),
|
|
2015
2304
|
/** Takes the card's queued instruction back out of the queue, as the local `session.relay.cancel`. */
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2305
|
+
z28.object({ type: z28.literal("chat.relay.cancel"), op: OperationIdSchema, qid: OperationIdSchema }),
|
|
2306
|
+
z28.object({ type: z28.literal("chat.launch"), op: OperationIdSchema, qid: OperationIdSchema, rows: CardRowsSchema.optional(), assertion: AssertionSchema.optional() }),
|
|
2307
|
+
z28.object({ type: z28.literal("chat.close"), op: OperationIdSchema, qid: OperationIdSchema, rows: CardRowsSchema.optional(), assertion: AssertionSchema.optional() }),
|
|
2019
2308
|
/** A row of the turn's task card: given by Ledge. */
|
|
2020
|
-
|
|
2309
|
+
z28.object({ type: z28.literal("chat.task"), op: OperationIdSchema, qid: OperationIdSchema, row: z28.number().int().min(0).max(MAX_PROPOSALS - 1) }),
|
|
2021
2310
|
/** Rows of the turn's generic card (action registry spec §5): `confirmed` as the local `chat.act`; a row whose action asks
|
|
2022
2311
|
* for a device check (`phone`) needs one assertion, purpose `act`, over the turn. */
|
|
2023
|
-
|
|
2024
|
-
type:
|
|
2312
|
+
z28.object({
|
|
2313
|
+
type: z28.literal("chat.act"),
|
|
2025
2314
|
op: OperationIdSchema,
|
|
2026
2315
|
qid: OperationIdSchema,
|
|
2027
2316
|
rows: CardRowsSchema.optional(),
|
|
2028
|
-
confirmed:
|
|
2317
|
+
confirmed: z28.literal(true).optional(),
|
|
2029
2318
|
assertion: AssertionSchema.optional(),
|
|
2030
2319
|
/** « Refuser », as the local `chat.act`: no passkey, no confirmation. */
|
|
2031
|
-
deny:
|
|
2320
|
+
deny: z28.literal(true).optional()
|
|
2032
2321
|
}),
|
|
2033
2322
|
/** « Ignorer » the turn's cards, or `undo` (« Revoir »), as the local `chat.ignore`. */
|
|
2034
|
-
|
|
2323
|
+
z28.object({ type: z28.literal("chat.ignore"), op: OperationIdSchema, qid: OperationIdSchema, undo: z28.literal(true).optional() }),
|
|
2035
2324
|
/** Puts a closed task Ledge gave away from « À traiter ». */
|
|
2036
|
-
|
|
2325
|
+
z28.object({ type: z28.literal("task.seen"), op: OperationIdSchema, task: z28.string().min(1).max(40) }),
|
|
2037
2326
|
/** Streams a session's pane, read-only; answered by `term.opened` or `operation.result` (rejected). */
|
|
2038
|
-
|
|
2327
|
+
z28.object({ type: z28.literal("term.open"), op: OperationIdSchema, id: sessionId }),
|
|
2039
2328
|
/** « Prendre la main »: passkey, purpose `terminal`, over the session; answered by `term.held` or `operation.result` (rejected). */
|
|
2040
|
-
|
|
2329
|
+
z28.object({ type: z28.literal("term.hold"), op: OperationIdSchema, tid: TerminalIdSchema, assertion: AssertionSchema.optional() }),
|
|
2041
2330
|
/** 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
|
-
|
|
2331
|
+
z28.object({ type: z28.literal("term.input"), tid: TerminalIdSchema, n: z28.number().int().nonnegative(), data: z28.string().min(1).max(TERM_INPUT_MAX) }),
|
|
2043
2332
|
/** « Rendre la main »: back to read-only. */
|
|
2044
|
-
|
|
2333
|
+
z28.object({ type: z28.literal("term.release"), tid: TerminalIdSchema }),
|
|
2045
2334
|
/** The phone saw a gap in `seq`: a fresh `term.opened`, please. */
|
|
2046
|
-
|
|
2047
|
-
|
|
2335
|
+
z28.object({ type: z28.literal("term.resync"), tid: TerminalIdSchema }),
|
|
2336
|
+
z28.object({ type: z28.literal("term.close"), tid: TerminalIdSchema }),
|
|
2048
2337
|
/** Whether the phone's app is in front: a device that is visible gets no push. */
|
|
2049
|
-
|
|
2338
|
+
z28.object({ type: z28.literal("presence"), visible: z28.boolean() }),
|
|
2339
|
+
/** « À toi » (lot 3): what OpenRig on this PC asks the person, then pushed whole when it changes (`openrig.asks`). */
|
|
2340
|
+
z28.object({ type: z28.literal("openrig.asks") }),
|
|
2341
|
+
/** The person's answer to an OpenRig row. A choice, words or a resolve carries an assertion of purpose `rig` bound to that row and that
|
|
2342
|
+
* answer; « Vu » (`seen`) needs none. */
|
|
2343
|
+
z28.object({ type: z28.literal("openrig.answer"), op: OperationIdSchema, qitemId: z28.string().min(1).max(200), answer: HumanAnswerSchema, assertion: AssertionSchema.optional() }),
|
|
2050
2344
|
/** Installs `version` on the PC, then restarts its daemon; the phone asks for its own build's version. Passkey, purpose `pc`. */
|
|
2051
|
-
|
|
2345
|
+
z28.object({ type: z28.literal("daemon.update"), ...pcOp, version: z28.string().regex(SEMVER) }).strict(),
|
|
2052
2346
|
/** Restarts the PC's daemon without installing anything. Passkey, purpose `pc`. */
|
|
2053
|
-
|
|
2347
|
+
z28.object({ type: z28.literal("daemon.restart"), ...pcOp }).strict()
|
|
2054
2348
|
]);
|
|
2055
|
-
var PhoneModelChoiceSchema =
|
|
2349
|
+
var PhoneModelChoiceSchema = z28.object({
|
|
2056
2350
|
id: ModelChoiceSchema.shape.id.max(120),
|
|
2057
|
-
label:
|
|
2058
|
-
description:
|
|
2059
|
-
efforts:
|
|
2351
|
+
label: z28.string().max(80),
|
|
2352
|
+
description: z28.string().max(200).optional(),
|
|
2353
|
+
efforts: z28.array(EffortSchema).max(8).optional(),
|
|
2060
2354
|
defaultEffort: EffortSchema.optional(),
|
|
2061
|
-
isDefault:
|
|
2355
|
+
isDefault: z28.boolean().optional()
|
|
2062
2356
|
}).strict();
|
|
2063
2357
|
var PHONE_COMMANDS_MAX = 300;
|
|
2064
|
-
var PhoneSlashCommandSchema = SlashCommandSchema.extend({ description:
|
|
2065
|
-
var PhoneServerMessageSchema =
|
|
2066
|
-
|
|
2067
|
-
version:
|
|
2068
|
-
name:
|
|
2069
|
-
boot:
|
|
2358
|
+
var PhoneSlashCommandSchema = SlashCommandSchema.extend({ description: z28.string().max(200).optional() }).strict();
|
|
2359
|
+
var PhoneServerMessageSchema = z28.discriminatedUnion("type", [
|
|
2360
|
+
z28.object({ type: z28.literal("snapshot"), protocol: z28.literal(PHONE_PROTOCOL), sessions: z28.array(PhoneSessionSchema), capabilities: CapabilitiesByToolSchema, daemon: z28.object({
|
|
2361
|
+
version: z28.string(),
|
|
2362
|
+
name: z28.string().max(40),
|
|
2363
|
+
boot: z28.string().uuid().optional(),
|
|
2070
2364
|
/** The daemon takes `session.model`. */
|
|
2071
|
-
models:
|
|
2365
|
+
models: z28.literal(true).optional(),
|
|
2072
2366
|
/** The daemon sends `session.checklist` and a session's `checklist` summary. */
|
|
2073
|
-
checklist:
|
|
2367
|
+
checklist: z28.literal(true).optional(),
|
|
2074
2368
|
/** `update`: the daemon can install a version and restart (npm, systemd); `restart`: it can restart (systemd). */
|
|
2075
|
-
update:
|
|
2076
|
-
restart:
|
|
2369
|
+
update: z28.literal(true).optional(),
|
|
2370
|
+
restart: z28.literal(true).optional(),
|
|
2077
2371
|
/** The daemon takes `session.close` and `session.resume`. */
|
|
2078
|
-
sessionClose:
|
|
2372
|
+
sessionClose: z28.literal(true).optional(),
|
|
2079
2373
|
/** The daemon streams a session's terminal (`term.open`). */
|
|
2080
|
-
terminal:
|
|
2374
|
+
terminal: z28.literal(true).optional(),
|
|
2081
2375
|
/** The PC lets a phone type into a terminal (`term.hold`): its setting is on. */
|
|
2082
|
-
terminalInput:
|
|
2376
|
+
terminalInput: z28.literal(true).optional(),
|
|
2083
2377
|
/** The daemon answers `session.commands`. */
|
|
2084
|
-
commands:
|
|
2378
|
+
commands: z28.literal(true).optional(),
|
|
2379
|
+
/** The daemon answers `openrig.row`. */
|
|
2380
|
+
rigRows: z28.literal(true).optional(),
|
|
2381
|
+
/** The daemon answers `openrig.asks` and `openrig.answer` (« À toi »). */
|
|
2382
|
+
openrig: z28.literal(true).optional(),
|
|
2085
2383
|
/** The daemon takes `session.broadcast` (« Écrire au groupe »). */
|
|
2086
|
-
broadcast:
|
|
2087
|
-
}), device:
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2384
|
+
broadcast: z28.literal(true).optional()
|
|
2385
|
+
}), device: z28.object({ confirm: z28.boolean(), push: z28.enum(["on", "off", "stale", "unavailable"]).optional(), chat: z28.literal(true).optional(), contextNear: ContextPrefsSchema.shape.near.optional() }).optional() }),
|
|
2386
|
+
/** « À toi » on this PC: the answer to `openrig.asks`, then each change, whole. */
|
|
2387
|
+
z28.object({ type: z28.literal("openrig.asks"), channel: HumanChannelSchema, asks: z28.array(HumanAskSchema), partial: z28.literal(true).optional(), truncated: z28.literal(true).optional() }),
|
|
2388
|
+
/** An answer's end, as on the desktop (`openrig.answered`), under the phone's `op`. `rejected`: the device check refused it (`reason`). */
|
|
2389
|
+
z28.object({
|
|
2390
|
+
type: z28.literal("openrig.answered"),
|
|
2391
|
+
op: OperationIdSchema,
|
|
2392
|
+
qitemId: z28.string(),
|
|
2393
|
+
ok: z28.boolean(),
|
|
2394
|
+
outcome: z28.enum(["recorded", "answered", "already"]).optional(),
|
|
2395
|
+
remaining: z28.array(z28.string()).optional(),
|
|
2396
|
+
replyId: z28.string().optional(),
|
|
2397
|
+
error: HumanAnswerErrorSchema.optional(),
|
|
2398
|
+
message: z28.string().max(500).optional(),
|
|
2399
|
+
rejected: z28.enum(["confirm_required", "assertion_invalid", "assertion_expired", "unavailable"]).optional()
|
|
2400
|
+
}),
|
|
2401
|
+
z28.object({ type: z28.literal("confirm.challenge"), purpose: ConfirmPurposeSchema, challenge: B64U2.max(64), rpId: z28.string().max(253), credentialId: B64U2.max(1366).optional(), expiresInMs: z28.number().int().positive() }),
|
|
2402
|
+
z28.object({ type: z28.literal("session.updated"), session: PhoneSessionSchema }),
|
|
2403
|
+
z28.object({ type: z28.literal("session.removed"), id: z28.string() }),
|
|
2091
2404
|
/** A session's checklist, sent to the phone that watches it; `null`: it has none (any more). */
|
|
2092
|
-
|
|
2405
|
+
z28.object({ type: z28.literal("session.checklist"), id: z28.string(), checklist: PhoneChecklistSchema.nullable() }),
|
|
2093
2406
|
/** `pending.cut` is set when even this body was shortened. */
|
|
2094
|
-
|
|
2095
|
-
|
|
2407
|
+
z28.object({ type: z28.literal("session.pending"), id: z28.string(), pending: PhonePendingSchema }),
|
|
2408
|
+
z28.object({ type: z28.literal("session.preview"), id: z28.string(), lines: z28.array(z28.string()) }),
|
|
2096
2409
|
/** `cut`: the ids of the items shortened; `more`: older items were left out. */
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
type:
|
|
2410
|
+
z28.object({ type: z28.literal("session.thread"), id: z28.string(), items: z28.array(ThreadItemSchema), cut: z28.array(z28.string()), more: z28.boolean() }),
|
|
2411
|
+
z28.object({ type: z28.literal("session.thread.item"), id: z28.string(), item: ThreadItemSchema, cut: z28.literal(true).optional() }),
|
|
2412
|
+
z28.object({
|
|
2413
|
+
type: z28.literal("operation.result"),
|
|
2101
2414
|
op: OperationIdSchema,
|
|
2102
2415
|
status: OperationStatusSchema,
|
|
2103
2416
|
reason: OperationReasonSchema.optional(),
|
|
2104
2417
|
/** How an applied `session.close` ended the agent: `killed` when it had to be forced. */
|
|
2105
2418
|
how: CloseHowSchema.optional(),
|
|
2106
2419
|
/** An applied `session.broadcast`: the names that got it and those that did not. */
|
|
2107
|
-
sent:
|
|
2108
|
-
refused:
|
|
2420
|
+
sent: z28.array(z28.string().max(100)).max(BROADCAST_SESSIONS_MAX).optional(),
|
|
2421
|
+
refused: z28.array(z28.object({ name: z28.string().max(100), reason: z28.string().max(200) })).max(BROADCAST_SESSIONS_MAX).optional()
|
|
2109
2422
|
}),
|
|
2110
2423
|
/** The models an agent offers; `error`: it could not be asked, `models` is then what Ledge knows without it. */
|
|
2111
|
-
|
|
2424
|
+
z28.object({ type: z28.literal("models.listing"), tool: ToolSchema, models: z28.array(PhoneModelChoiceSchema).max(40), error: z28.literal(true).optional() }),
|
|
2112
2425
|
/** A model change that was accepted and then failed (the agent could not restart). */
|
|
2113
|
-
|
|
2426
|
+
z28.object({ type: z28.literal("session.model.failed"), id: z28.string(), reason: ModelRefusalSchema }),
|
|
2114
2427
|
/** 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
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2428
|
+
z28.object({ type: z28.literal("session.commands.listing"), id: z28.string(), commands: z28.array(PhoneSlashCommandSchema).max(PHONE_COMMANDS_MAX), error: z28.literal(true).optional() }),
|
|
2429
|
+
/** A queue row's state, answering `openrig.row`: `row`, or `error`. */
|
|
2430
|
+
z28.object({ type: z28.literal("openrig.rowState"), qitem: z28.string(), row: PhoneRigRowSchema.optional(), error: PhoneRowErrorSchema.optional() }),
|
|
2431
|
+
z28.object({ type: z28.literal("chat.history"), turns: z28.array(PhoneChatTurnSchema).max(50) }),
|
|
2432
|
+
z28.object({ type: z28.literal("chat.step"), qid: OperationIdSchema, step: PhoneChatStepSchema }),
|
|
2118
2433
|
/** The whole text so far, not an increment. */
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2434
|
+
z28.object({ type: z28.literal("chat.delta"), qid: OperationIdSchema, text: z28.string().max(THREAD_TEXT_MAX) }),
|
|
2435
|
+
z28.object({ type: z28.literal("chat.done"), qid: OperationIdSchema, turn: PhoneChatTurnSchema }),
|
|
2436
|
+
z28.object({ type: z28.literal("chat.error"), qid: OperationIdSchema, code: ChatErrorCodeSchema }),
|
|
2122
2437
|
/** The daemon updating or restarting itself, whoever asked: npm runs, then the service restarts (the link drops right after). */
|
|
2123
|
-
|
|
2438
|
+
z28.object({ type: z28.literal("daemon.progress"), version: z28.string().max(40), step: z28.enum(["installing", "restarting"]) }),
|
|
2124
2439
|
/** Nothing was changed. Never npm's output nor a command: both hold the PC's paths. */
|
|
2125
|
-
|
|
2440
|
+
z28.object({ type: z28.literal("daemon.failed"), version: z28.string().max(40), reason: UpdateFailureSchema }),
|
|
2126
2441
|
/** The tasks Ledge gave, open or closed lately, after each snapshot; then each change. */
|
|
2127
|
-
|
|
2128
|
-
|
|
2442
|
+
z28.object({ type: z28.literal("tasks"), tasks: z28.array(PhoneTaskSchema).max(200) }),
|
|
2443
|
+
z28.object({ type: z28.literal("task.updated"), task: PhoneTaskSchema }),
|
|
2129
2444
|
AttachmentStoredSchema,
|
|
2130
2445
|
AttachmentDataSchema,
|
|
2131
2446
|
AttachmentErrorSchema,
|
|
2132
2447
|
/** 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
|
-
|
|
2134
|
-
type:
|
|
2448
|
+
z28.object({
|
|
2449
|
+
type: z28.literal("term.opened"),
|
|
2135
2450
|
op: OperationIdSchema.optional(),
|
|
2136
2451
|
tid: TerminalIdSchema,
|
|
2137
|
-
id:
|
|
2452
|
+
id: z28.string(),
|
|
2138
2453
|
cols: TermDimSchema,
|
|
2139
2454
|
rows: TermDimSchema,
|
|
2140
|
-
screen:
|
|
2141
|
-
cursor:
|
|
2142
|
-
seq:
|
|
2455
|
+
screen: z28.string().max(TERM_SCREEN_MAX),
|
|
2456
|
+
cursor: z28.object({ x: z28.number().int().nonnegative(), y: z28.number().int().nonnegative() }),
|
|
2457
|
+
seq: z28.number().int().nonnegative(),
|
|
2143
2458
|
/** Read-only for good: a subagent's page shows its parent's pane. */
|
|
2144
|
-
readOnly:
|
|
2459
|
+
readOnly: z28.literal(true).optional()
|
|
2145
2460
|
}),
|
|
2146
2461
|
/** What the pane printed since the last one; `seq` + 1 each time. */
|
|
2147
|
-
|
|
2148
|
-
|
|
2462
|
+
z28.object({ type: z28.literal("term.output"), tid: TerminalIdSchema, seq: z28.number().int().nonnegative(), data: z28.string().min(1).max(TERM_OUTPUT_MAX) }),
|
|
2463
|
+
z28.object({ type: z28.literal("term.size"), tid: TerminalIdSchema, cols: TermDimSchema, rows: TermDimSchema }),
|
|
2149
2464
|
/** The phone holds the hand until `until`, pushed back by each key (5 min), never past 30 min from the hold. */
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2465
|
+
z28.object({ type: z28.literal("term.held"), tid: TerminalIdSchema, until: z28.string().datetime() }),
|
|
2466
|
+
z28.object({ type: z28.literal("term.released"), tid: TerminalIdSchema, why: TermReleaseWhySchema }),
|
|
2467
|
+
z28.object({ type: z28.literal("term.exit"), tid: TerminalIdSchema, why: TermExitWhySchema }),
|
|
2153
2468
|
/** Never carries free text. */
|
|
2154
|
-
|
|
2469
|
+
z28.object({ type: z28.literal("error"), code: z28.enum(["invalid", "unknown_session", "too_large"]), request: z28.string().max(40).optional() })
|
|
2155
2470
|
]);
|
|
2156
2471
|
|
|
2472
|
+
// ../protocol/src/media.ts
|
|
2473
|
+
import { z as z29 } from "zod";
|
|
2474
|
+
var MediaCommandSchema = z29.enum(["previous", "play_pause", "next"]);
|
|
2475
|
+
var ARTWORK_MAX_BYTES = 512 * 1024;
|
|
2476
|
+
var NowPlayingSchema = z29.object({
|
|
2477
|
+
/** The track's title; a session without one is not reported (nothing to show). */
|
|
2478
|
+
title: z29.string().min(1),
|
|
2479
|
+
/** '' when the player gives none (a web video often has no artist). */
|
|
2480
|
+
artist: z29.string(),
|
|
2481
|
+
album: z29.string().optional(),
|
|
2482
|
+
/** The cover as a `data:image/…;base64,` URL; left out when the player has none, or it is over ARTWORK_MAX_BYTES. It can
|
|
2483
|
+
* arrive in a later event than the track itself: players often publish the thumbnail a moment after the title. */
|
|
2484
|
+
artwork: z29.string().startsWith("data:image/").optional(),
|
|
2485
|
+
/** True while it plays; a paused session is still reported (with false), `null` means no media session at all. */
|
|
2486
|
+
playing: z29.boolean(),
|
|
2487
|
+
/** What the player accepts now; a control it refuses is disabled, not hidden, at the front's choice. */
|
|
2488
|
+
can: z29.object({ previous: z29.boolean(), playPause: z29.boolean(), next: z29.boolean() }),
|
|
2489
|
+
/** The player: SMTC's source app id (`Spotify.exe`, `MSEdge`, a packaged app's AUMID), and its display name when
|
|
2490
|
+
* Windows knows one. Several players can hold a session: the one reported is the one playing (Windows' current one
|
|
2491
|
+
* first), else Windows' current one. Commands go to that same player. */
|
|
2492
|
+
player: z29.object({ id: z29.string().min(1), name: z29.string().optional() })
|
|
2493
|
+
});
|
|
2494
|
+
var NowPlayingStateSchema = NowPlayingSchema.nullable();
|
|
2495
|
+
|
|
2157
2496
|
// src/imageInfo.ts
|
|
2158
2497
|
var ok = (mime, width, height) => width > 0 && height > 0 ? { mime, width, height } : null;
|
|
2159
2498
|
function jpeg(b) {
|