@av-pi-studio/protocol 0.0.111 → 0.0.112
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/client-capabilities.d.ts +1 -0
- package/dist/client-capabilities.js +3 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/messages.d.ts +20762 -10009
- package/dist/messages.js +259 -0
- package/dist/source-detect.d.ts +18 -0
- package/dist/source-detect.js +61 -0
- package/package.json +1 -1
package/dist/messages.js
CHANGED
|
@@ -1054,6 +1054,252 @@ export const viewerSettingsUpdateSchema = z
|
|
|
1054
1054
|
})
|
|
1055
1055
|
.passthrough();
|
|
1056
1056
|
// ===========================================================================
|
|
1057
|
+
// Skills (swe/architecture/pi-resource-management.md § Wire family `skills_*`) — sprint-075
|
|
1058
|
+
// slice 1: list + toggle only. Preview/install/remove/update_all land in sprints 076-077 and
|
|
1059
|
+
// reuse `skillInfoSchema`/`piPackageInfoSchema` below.
|
|
1060
|
+
// ===========================================================================
|
|
1061
|
+
/**
|
|
1062
|
+
* One resolved Agent Skill as Pi's `DefaultPackageManager.resolve()` reports it, projected to the
|
|
1063
|
+
* wire. `origin` documents the four values Pi emits today (`"agent_dir" | "agents_dir" |
|
|
1064
|
+
* "package" | "settings_path"`) but stays `z.string()` — a future Pi origin must still parse on
|
|
1065
|
+
* an older client/daemon pair. `removable` reflects Pi's own rule (e.g. a package-owned skill
|
|
1066
|
+
* isn't independently removable); the daemon never re-derives it.
|
|
1067
|
+
*/
|
|
1068
|
+
export const skillInfoSchema = z
|
|
1069
|
+
.object({
|
|
1070
|
+
name: z.string(),
|
|
1071
|
+
description: z.string().optional(),
|
|
1072
|
+
path: z.string(),
|
|
1073
|
+
enabled: z.boolean(),
|
|
1074
|
+
/** `"agent_dir" | "agents_dir" | "package" | "settings_path"` today; open string on the wire. */
|
|
1075
|
+
origin: z.string(),
|
|
1076
|
+
originLabel: z.string(),
|
|
1077
|
+
packageSource: z.string().optional(),
|
|
1078
|
+
shadowedBy: z.string().optional(),
|
|
1079
|
+
symlinkTarget: z.string().optional(),
|
|
1080
|
+
provenance: z
|
|
1081
|
+
.object({
|
|
1082
|
+
source: z.string(),
|
|
1083
|
+
sourceUrl: z.string().optional(),
|
|
1084
|
+
installedAt: z.string().optional(),
|
|
1085
|
+
})
|
|
1086
|
+
.passthrough()
|
|
1087
|
+
.optional(),
|
|
1088
|
+
disableModelInvocation: z.boolean().optional(),
|
|
1089
|
+
warnings: z.array(z.string()).optional(),
|
|
1090
|
+
removable: z.boolean(),
|
|
1091
|
+
})
|
|
1092
|
+
.passthrough();
|
|
1093
|
+
/** One `settings.json` `packages[]` entry resolved against disk. */
|
|
1094
|
+
export const piPackageInfoSchema = z
|
|
1095
|
+
.object({
|
|
1096
|
+
source: z.string(),
|
|
1097
|
+
kind: z.enum(["npm", "git", "local"]),
|
|
1098
|
+
identity: z.string(),
|
|
1099
|
+
pinned: z.boolean(),
|
|
1100
|
+
curated: z.boolean(),
|
|
1101
|
+
filtered: z.boolean(),
|
|
1102
|
+
skillCount: z.number(),
|
|
1103
|
+
})
|
|
1104
|
+
.passthrough();
|
|
1105
|
+
export const skillsListRequestSchema = z
|
|
1106
|
+
.object({
|
|
1107
|
+
type: z.literal("skills_list_request"),
|
|
1108
|
+
requestId: z.string(),
|
|
1109
|
+
})
|
|
1110
|
+
.passthrough();
|
|
1111
|
+
/**
|
|
1112
|
+
* `ok`/`error` are domain fields, not `rpc_error` (mirrors `extension_packs_set_response`'s
|
|
1113
|
+
* idiom) — e.g. a corrupt `settings.json` answers `{ok:false, error:"settings_unreadable"}`
|
|
1114
|
+
* rather than failing transport-level. `message` is an optional human-readable elaboration.
|
|
1115
|
+
*/
|
|
1116
|
+
export const skillsListResponseSchema = z
|
|
1117
|
+
.object({
|
|
1118
|
+
type: z.literal("skills_list_response"),
|
|
1119
|
+
requestId: z.string(),
|
|
1120
|
+
ok: z.boolean(),
|
|
1121
|
+
error: z.string().optional(),
|
|
1122
|
+
message: z.string().optional(),
|
|
1123
|
+
skills: z.array(skillInfoSchema),
|
|
1124
|
+
packages: z.array(piPackageInfoSchema),
|
|
1125
|
+
})
|
|
1126
|
+
.passthrough();
|
|
1127
|
+
export const skillsSetEnabledRequestSchema = z
|
|
1128
|
+
.object({
|
|
1129
|
+
type: z.literal("skills_set_enabled_request"),
|
|
1130
|
+
requestId: z.string(),
|
|
1131
|
+
path: z.string(),
|
|
1132
|
+
enabled: z.boolean(),
|
|
1133
|
+
})
|
|
1134
|
+
.passthrough();
|
|
1135
|
+
export const skillsSetEnabledResponseSchema = z
|
|
1136
|
+
.object({
|
|
1137
|
+
type: z.literal("skills_set_enabled_response"),
|
|
1138
|
+
requestId: z.string(),
|
|
1139
|
+
ok: z.boolean(),
|
|
1140
|
+
error: z.string().optional(),
|
|
1141
|
+
message: z.string().optional(),
|
|
1142
|
+
skills: z.array(skillInfoSchema),
|
|
1143
|
+
packages: z.array(piPackageInfoSchema),
|
|
1144
|
+
})
|
|
1145
|
+
.passthrough();
|
|
1146
|
+
/**
|
|
1147
|
+
* Broadcast, no `requestId` — every connected client re-lists. `reason` is a plain string
|
|
1148
|
+
* (`"mutation" | "external"` documented) so a future reason stays append-only, same idiom as
|
|
1149
|
+
* `agent_timeline_reset`'s `reason`.
|
|
1150
|
+
*/
|
|
1151
|
+
export const skillsChangedSchema = z
|
|
1152
|
+
.object({
|
|
1153
|
+
type: z.literal("skills_changed"),
|
|
1154
|
+
reason: z.string(),
|
|
1155
|
+
})
|
|
1156
|
+
.passthrough();
|
|
1157
|
+
/**
|
|
1158
|
+
* Base wire shape — used as the `sessionMessageSchema` union member (a `discriminatedUnion` entry
|
|
1159
|
+
* must stay a `ZodObject`, so the xor-refine below wraps this one instead of replacing it).
|
|
1160
|
+
*/
|
|
1161
|
+
const skillsRemoveRequestShapeSchema = z
|
|
1162
|
+
.object({
|
|
1163
|
+
type: z.literal("skills_remove_request"),
|
|
1164
|
+
requestId: z.string(),
|
|
1165
|
+
path: z.string().optional(),
|
|
1166
|
+
packageSource: z.string().optional(),
|
|
1167
|
+
})
|
|
1168
|
+
.passthrough();
|
|
1169
|
+
/**
|
|
1170
|
+
* Exactly one of `path` (a top-level skill: `settings_path`/symlink/plain directory) or
|
|
1171
|
+
* `packageSource` (a whole `settings.json` `packages[]` entry) must be set — never both, never
|
|
1172
|
+
* neither. A package-owned skill's own `path` is rejected daemon-side as `not_removable`; this
|
|
1173
|
+
* schema only enforces the wire shape/xor, not daemon-side removability.
|
|
1174
|
+
*/
|
|
1175
|
+
export const skillsRemoveRequestSchema = skillsRemoveRequestShapeSchema.refine((v) => (v.path !== undefined) !== (v.packageSource !== undefined), { message: "exactly one of path or packageSource is required" });
|
|
1176
|
+
/**
|
|
1177
|
+
* `stderr` is the spawned CLI's truncated (≤2048 bytes) stderr tail when the underlying `skills
|
|
1178
|
+
* remove`/`pi remove` process failed; absent on success or on a pre-spawn domain error (e.g.
|
|
1179
|
+
* `not_found`/`not_removable`). `ok`/`error` are domain fields, same idiom as `skills_list_response`.
|
|
1180
|
+
*/
|
|
1181
|
+
export const skillsRemoveResponseSchema = z
|
|
1182
|
+
.object({
|
|
1183
|
+
type: z.literal("skills_remove_response"),
|
|
1184
|
+
requestId: z.string(),
|
|
1185
|
+
ok: z.boolean(),
|
|
1186
|
+
error: z.string().optional(),
|
|
1187
|
+
message: z.string().optional(),
|
|
1188
|
+
stderr: z.string().optional(),
|
|
1189
|
+
skills: z.array(skillInfoSchema),
|
|
1190
|
+
packages: z.array(piPackageInfoSchema),
|
|
1191
|
+
})
|
|
1192
|
+
.passthrough();
|
|
1193
|
+
/** One engine's outcome inside an `ItemResult[]` list (used by `skills_update_all_response`). */
|
|
1194
|
+
export const itemResultSchema = z
|
|
1195
|
+
.object({
|
|
1196
|
+
target: z.string(),
|
|
1197
|
+
ok: z.boolean(),
|
|
1198
|
+
error: z.string().optional(),
|
|
1199
|
+
stderr: z.string().optional(),
|
|
1200
|
+
})
|
|
1201
|
+
.passthrough();
|
|
1202
|
+
export const skillsUpdateAllRequestSchema = z
|
|
1203
|
+
.object({
|
|
1204
|
+
type: z.literal("skills_update_all_request"),
|
|
1205
|
+
requestId: z.string(),
|
|
1206
|
+
})
|
|
1207
|
+
.passthrough();
|
|
1208
|
+
/** One `ItemResult` per engine (`agent-skills` via `skills update -g -y`, `pi-packages` via `pi update --extensions`); a mixed ok/failed `results` array is expected, not an overall failure. */
|
|
1209
|
+
export const skillsUpdateAllResponseSchema = z
|
|
1210
|
+
.object({
|
|
1211
|
+
type: z.literal("skills_update_all_response"),
|
|
1212
|
+
requestId: z.string(),
|
|
1213
|
+
ok: z.boolean(),
|
|
1214
|
+
error: z.string().optional(),
|
|
1215
|
+
message: z.string().optional(),
|
|
1216
|
+
results: z.array(itemResultSchema),
|
|
1217
|
+
skills: z.array(skillInfoSchema),
|
|
1218
|
+
packages: z.array(piPackageInfoSchema),
|
|
1219
|
+
})
|
|
1220
|
+
.passthrough();
|
|
1221
|
+
// ---------------------------------------------------------------------------
|
|
1222
|
+
// Skills preview/install (sprint-077/task-002). Client-side source classification lives in
|
|
1223
|
+
// `source-detect.ts`'s `detectSkillSource()` — the daemon always re-runs detection
|
|
1224
|
+
// authoritatively, so neither request below carries a client-sent `kind`. Reuses
|
|
1225
|
+
// `itemResultSchema`/`skillInfoSchema`/`piPackageInfoSchema` above.
|
|
1226
|
+
// ---------------------------------------------------------------------------
|
|
1227
|
+
/**
|
|
1228
|
+
* One installable skill found inside a `skills_repo`/`local_path` preview (never package kinds —
|
|
1229
|
+
* those answer `{ok:true, kind, skills: []}`, nothing to pick). `valid` means frontmatter had a
|
|
1230
|
+
* name + description; `exists` means `~/.agents/skills/<sanitized-name>` is already present
|
|
1231
|
+
* (drives the client's "Already installed — replace" checkbox).
|
|
1232
|
+
*/
|
|
1233
|
+
export const previewSkillSchema = z
|
|
1234
|
+
.object({
|
|
1235
|
+
name: z.string(),
|
|
1236
|
+
description: z.string().optional(),
|
|
1237
|
+
relPath: z.string(),
|
|
1238
|
+
warnings: z.array(z.string()).optional(),
|
|
1239
|
+
valid: z.boolean(),
|
|
1240
|
+
exists: z.boolean(),
|
|
1241
|
+
})
|
|
1242
|
+
.passthrough();
|
|
1243
|
+
export const skillsPreviewRequestSchema = z
|
|
1244
|
+
.object({
|
|
1245
|
+
type: z.literal("skills_preview_request"),
|
|
1246
|
+
requestId: z.string(),
|
|
1247
|
+
source: z.string(),
|
|
1248
|
+
})
|
|
1249
|
+
.passthrough();
|
|
1250
|
+
/**
|
|
1251
|
+
* `ok`/`error` are domain fields, same idiom as every other `skills_*` response. `kind` is the
|
|
1252
|
+
* daemon's own `detect(source)` result (never the client's) and `source` echoes the request's
|
|
1253
|
+
* `source` verbatim — both present even on a domain failure (e.g. `invalid_source`), so a client
|
|
1254
|
+
* can still render what it tried against. `pi_npm`/`pi_git` always answer
|
|
1255
|
+
* `{ok:true, kind, source, skills: []}` — package kinds install as a unit, nothing to pick.
|
|
1256
|
+
*/
|
|
1257
|
+
export const skillsPreviewResponseSchema = z
|
|
1258
|
+
.object({
|
|
1259
|
+
type: z.literal("skills_preview_response"),
|
|
1260
|
+
requestId: z.string(),
|
|
1261
|
+
ok: z.boolean(),
|
|
1262
|
+
error: z.string().optional(),
|
|
1263
|
+
message: z.string().optional(),
|
|
1264
|
+
kind: z.string(),
|
|
1265
|
+
source: z.string(),
|
|
1266
|
+
skills: z.array(previewSkillSchema),
|
|
1267
|
+
})
|
|
1268
|
+
.passthrough();
|
|
1269
|
+
/**
|
|
1270
|
+
* `skills` is the picked skill names from a prior preview — meaningful for `skills_repo` only
|
|
1271
|
+
* (ignored for `pi_npm`/`pi_git`, which install every skill the package ships; irrelevant for
|
|
1272
|
+
* `local_path`, which registers the whole folder). `replace` resends an already-installed picked
|
|
1273
|
+
* name instead of excluding it as `name_exists`.
|
|
1274
|
+
*/
|
|
1275
|
+
export const skillsInstallRequestSchema = z
|
|
1276
|
+
.object({
|
|
1277
|
+
type: z.literal("skills_install_request"),
|
|
1278
|
+
requestId: z.string(),
|
|
1279
|
+
source: z.string(),
|
|
1280
|
+
skills: z.array(z.string()).optional(),
|
|
1281
|
+
replace: z.boolean().optional(),
|
|
1282
|
+
})
|
|
1283
|
+
.passthrough();
|
|
1284
|
+
/**
|
|
1285
|
+
* One `ItemResult` per engine outcome — one entry for `pi_npm`/`pi_git`/`local_path`, one per
|
|
1286
|
+
* picked name for `skills_repo` (e.g. a `name_exists` exclusion still gets its own `ItemResult`).
|
|
1287
|
+
* `skills`/`packages` are the post-mutation list, same idiom as every other mutating `skills_*`
|
|
1288
|
+
* response.
|
|
1289
|
+
*/
|
|
1290
|
+
export const skillsInstallResponseSchema = z
|
|
1291
|
+
.object({
|
|
1292
|
+
type: z.literal("skills_install_response"),
|
|
1293
|
+
requestId: z.string(),
|
|
1294
|
+
ok: z.boolean(),
|
|
1295
|
+
error: z.string().optional(),
|
|
1296
|
+
message: z.string().optional(),
|
|
1297
|
+
results: z.array(itemResultSchema),
|
|
1298
|
+
skills: z.array(skillInfoSchema),
|
|
1299
|
+
packages: z.array(piPackageInfoSchema),
|
|
1300
|
+
})
|
|
1301
|
+
.passthrough();
|
|
1302
|
+
// ===========================================================================
|
|
1057
1303
|
// Extension UI (features/extension-ui-rpc.md) — additive, new in sprint-066
|
|
1058
1304
|
// ===========================================================================
|
|
1059
1305
|
/**
|
|
@@ -1263,6 +1509,19 @@ export const sessionMessageSchema = z.discriminatedUnion("type", [
|
|
|
1263
1509
|
viewerSettingsSetRequestSchema,
|
|
1264
1510
|
viewerSettingsSetResponseSchema,
|
|
1265
1511
|
viewerSettingsUpdateSchema,
|
|
1512
|
+
skillsListRequestSchema,
|
|
1513
|
+
skillsListResponseSchema,
|
|
1514
|
+
skillsSetEnabledRequestSchema,
|
|
1515
|
+
skillsSetEnabledResponseSchema,
|
|
1516
|
+
skillsChangedSchema,
|
|
1517
|
+
skillsRemoveRequestShapeSchema,
|
|
1518
|
+
skillsRemoveResponseSchema,
|
|
1519
|
+
skillsUpdateAllRequestSchema,
|
|
1520
|
+
skillsUpdateAllResponseSchema,
|
|
1521
|
+
skillsPreviewRequestSchema,
|
|
1522
|
+
skillsPreviewResponseSchema,
|
|
1523
|
+
skillsInstallRequestSchema,
|
|
1524
|
+
skillsInstallResponseSchema,
|
|
1266
1525
|
agentUiRequestSchema,
|
|
1267
1526
|
agentUiResolvedSchema,
|
|
1268
1527
|
agentUiRespondRequestSchema,
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** One of the four installable source kinds the daemon recognizes; `null` from
|
|
2
|
+
* `detectSkillSource` means "anything else" (`invalid_source` once the server rejects it). */
|
|
3
|
+
export type SkillSourceKind = "pi_npm" | "pi_git" | "skills_repo" | "local_path";
|
|
4
|
+
export interface DetectedSkillSource {
|
|
5
|
+
kind: SkillSourceKind;
|
|
6
|
+
/** Whitespace-trimmed input, unchanged otherwise. Further parsing (e.g. splitting a
|
|
7
|
+
* `/tree/<ref>/<path>` subpath out of a `skills_repo` URL, or expanding a `~` in a
|
|
8
|
+
* `local_path`) is each consumer's own job — this classifier never reaches into the
|
|
9
|
+
* filesystem or re-derives host state. */
|
|
10
|
+
normalized: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Classify a skill install source string. Returns `null` for anything that isn't one of the four
|
|
14
|
+
* recognized kinds (relative paths, bare words, non-git `http(s)` file URLs, empty input) — the
|
|
15
|
+
* caller renders that as "Unrecognized" / the daemon answers `invalid_source`.
|
|
16
|
+
*/
|
|
17
|
+
export declare function detectSkillSource(input: string): DetectedSkillSource | null;
|
|
18
|
+
//# sourceMappingURL=source-detect.d.ts.map
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// Pure, zero-dependency classifier for a skill install source string
|
|
2
|
+
// (swe/architecture/pi-resource-management.md § Source kinds). Shared between the web client
|
|
3
|
+
// (live label while the user types a source) and the daemon (`source-detect.ts` re-export per
|
|
4
|
+
// the architecture doc's component table) — the **daemon's** classification is always
|
|
5
|
+
// authoritative; a client-sent `kind` on the wire is ignored (`skills_preview_request` /
|
|
6
|
+
// `skills_install_request` carry only `source`, never `kind`).
|
|
7
|
+
//
|
|
8
|
+
// No filesystem access here: `local_path` is detected purely syntactically (absolute or
|
|
9
|
+
// `~`-prefixed); whether the directory actually exists on the daemon host is checked server-side.
|
|
10
|
+
const NPM_SOURCE_RE = /^npm:\S+$/;
|
|
11
|
+
const GIT_PREFIX_SOURCE_RE = /^git:\S+$/;
|
|
12
|
+
const SSH_URL_RE = /^ssh:\/\/\S+$/;
|
|
13
|
+
/** `git@host:owner/repo[.git]` scp-like syntax. */
|
|
14
|
+
const SCP_LIKE_RE = /^git@[^\s:/]+:\S+$/;
|
|
15
|
+
/** `http(s)://[www.]<github|gitlab>.com/<anything>` — covers plain repo URLs and
|
|
16
|
+
* `/tree/<ref>/<path>` subpaths alike; the subpath is opaque here. */
|
|
17
|
+
const GIT_HOST_URL_RE = /^https?:\/\/(?:[^\s/]*\.)?(?:github|gitlab)\.com\/\S+$/i;
|
|
18
|
+
/** Any scheme'd URL ending in `.git` (self-hosted Git forges, bare `git://`, etc). */
|
|
19
|
+
const GIT_SUFFIX_URL_RE = /^[a-zA-Z][a-zA-Z0-9+.-]*:\/\/\S+\.git$/;
|
|
20
|
+
const URL_SCHEME_RE = /^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//;
|
|
21
|
+
/** GitHub/GitLab slug segment: alnum, `.`/`_`/`-` in the middle, never a leading/trailing one. */
|
|
22
|
+
const OWNER_REPO_SEGMENT = "[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?";
|
|
23
|
+
/** `owner/repo` or `owner/repo@skill` — exactly one slash, so a deeper or `./`/`../`-prefixed
|
|
24
|
+
* relative path never matches (those fall through to `null`). */
|
|
25
|
+
const OWNER_REPO_RE = new RegExp(`^${OWNER_REPO_SEGMENT}/${OWNER_REPO_SEGMENT}(?:@${OWNER_REPO_SEGMENT})?$`);
|
|
26
|
+
/**
|
|
27
|
+
* Classify a skill install source string. Returns `null` for anything that isn't one of the four
|
|
28
|
+
* recognized kinds (relative paths, bare words, non-git `http(s)` file URLs, empty input) — the
|
|
29
|
+
* caller renders that as "Unrecognized" / the daemon answers `invalid_source`.
|
|
30
|
+
*/
|
|
31
|
+
export function detectSkillSource(input) {
|
|
32
|
+
const normalized = input.trim();
|
|
33
|
+
if (normalized.length === 0) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
if (NPM_SOURCE_RE.test(normalized)) {
|
|
37
|
+
return { kind: "pi_npm", normalized };
|
|
38
|
+
}
|
|
39
|
+
if (GIT_PREFIX_SOURCE_RE.test(normalized)) {
|
|
40
|
+
return { kind: "pi_git", normalized };
|
|
41
|
+
}
|
|
42
|
+
if (normalized === "~" || normalized.startsWith("~/") || normalized.startsWith("/")) {
|
|
43
|
+
return { kind: "local_path", normalized };
|
|
44
|
+
}
|
|
45
|
+
if (SCP_LIKE_RE.test(normalized) ||
|
|
46
|
+
SSH_URL_RE.test(normalized) ||
|
|
47
|
+
GIT_HOST_URL_RE.test(normalized) ||
|
|
48
|
+
GIT_SUFFIX_URL_RE.test(normalized)) {
|
|
49
|
+
return { kind: "skills_repo", normalized };
|
|
50
|
+
}
|
|
51
|
+
if (URL_SCHEME_RE.test(normalized)) {
|
|
52
|
+
// Has a URL scheme but isn't a recognized git host or `*.git` suffix — e.g. a plain file
|
|
53
|
+
// link. Explicitly not installable, never falls through to the owner/repo check below.
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
if (OWNER_REPO_RE.test(normalized)) {
|
|
57
|
+
return { kind: "skills_repo", normalized };
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=source-detect.js.map
|