pi-session-mcp 0.13.1 → 0.14.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/.pi-session-mcp-cli/config.js +21 -3
- package/.pi-session-mcp-cli/doctor.js +6 -2
- package/.pi-session-mcp-cli/skill-library.js +1 -1
- package/CHANGELOG.md +48 -0
- package/README.md +1 -1
- package/dist/config.d.ts +3 -1
- package/dist/config.js +21 -3
- package/dist/doctor.d.ts +1 -1
- package/dist/doctor.js +6 -2
- package/dist/model-catalog.d.ts +40 -0
- package/dist/model-catalog.js +38 -0
- package/dist/profile-probe.d.ts +28 -0
- package/dist/profile-probe.js +164 -0
- package/dist/setup-cli.d.ts +7 -0
- package/dist/setup-cli.js +36 -0
- package/dist/setup-main.js +10 -3
- package/dist/setup-probe-main.d.ts +14 -0
- package/dist/setup-probe-main.js +76 -0
- package/dist/setup-probe-result.d.ts +39 -0
- package/dist/setup-probe-result.js +62 -0
- package/dist/setup-writers-common.js +1 -1
- package/dist/skill-library.js +1 -1
- package/docs/acceptance.md +124 -1
- package/docs/adr/0014-profile-probe-and-model-catalog.md +89 -0
- package/docs/client-setup.md +18 -12
- package/docs/doctor-design.md +1 -0
- package/docs/installation.md +87 -0
- package/docs/roadmap.md +11 -1
- package/docs/skills.md +42 -25
- package/docs/tool-contracts.md +1 -1
- package/package.json +1 -1
- package/skills/pi-session-profiles/SKILL.md +124 -0
package/dist/setup-main.js
CHANGED
|
@@ -26,9 +26,14 @@ export async function main(argv = process.argv.slice(2), dependencies = {}) {
|
|
|
26
26
|
const failureTargets = request.writer ? [{ client: "writer", scope: request.writer.name.slice(0, request.writer.name.lastIndexOf("-w")), alias: request.writer.name }] : request.isolationControl ? [{ client: "writer", scope: "host", alias: "control" }] : request.repo !== undefined ? [{ client: "modes", scope: "workspace", alias: request.repo.alias }] : request.customizeSkill === undefined ? request.targets : [skillTarget(request.customizeSkill)];
|
|
27
27
|
if (!currentSetupPlatform())
|
|
28
28
|
return write(cliFailure(operation, failureTargets, "platform_unsupported", 1), request.json);
|
|
29
|
+
if (request.listModels)
|
|
30
|
+
return (await import("./setup-probe-main.js")).runListModels(request.json);
|
|
29
31
|
const config = process.env.PI_SESSION_MCP_CONFIG;
|
|
30
|
-
if (!config)
|
|
32
|
+
if (!config) {
|
|
33
|
+
if (request.probe)
|
|
34
|
+
return (await import("./setup-probe-main.js")).runProbe(request.probe, request.json, "", new AbortController().signal);
|
|
31
35
|
return write(cliFailure(operation, failureTargets, "config_required", 1), request.json);
|
|
36
|
+
}
|
|
32
37
|
if (request.customizeSkill !== undefined) {
|
|
33
38
|
const name = request.customizeSkill;
|
|
34
39
|
try {
|
|
@@ -41,12 +46,14 @@ export async function main(argv = process.argv.slice(2), dependencies = {}) {
|
|
|
41
46
|
const root = dependencies.packageRoot ?? resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
42
47
|
const controller = new AbortController();
|
|
43
48
|
let interrupted;
|
|
44
|
-
const onInterrupt = () => { interrupted ??= 130; controller.abort(); };
|
|
45
|
-
const onTerminate = () => { interrupted ??= 143; controller.abort(); };
|
|
49
|
+
const onInterrupt = () => { interrupted ??= 130; controller.abort(interrupted); };
|
|
50
|
+
const onTerminate = () => { interrupted ??= 143; controller.abort(interrupted); };
|
|
46
51
|
process.on("SIGINT", onInterrupt);
|
|
47
52
|
process.on("SIGTERM", onTerminate);
|
|
48
53
|
let setup;
|
|
49
54
|
try {
|
|
55
|
+
if (request.probe)
|
|
56
|
+
return await (await import("./setup-probe-main.js")).runProbe(request.probe, request.json, resolve(config), controller.signal, { packageRoot: root });
|
|
50
57
|
const writerDeps = { configPath: resolve(config), releaseRoot: root, signal: controller.signal };
|
|
51
58
|
if (request.repo !== undefined) {
|
|
52
59
|
try {
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type DoctorResult } from "./doctor.js";
|
|
2
|
+
import { type ModelCatalog } from "./model-catalog.js";
|
|
3
|
+
import type { PiSessionAdapter } from "./pi-adapter.js";
|
|
4
|
+
export interface ProbeMainDependencies {
|
|
5
|
+
readonly catalog?: () => Promise<ModelCatalog>;
|
|
6
|
+
readonly adapter?: () => Promise<PiSessionAdapter>;
|
|
7
|
+
readonly doctor?: () => Promise<DoctorResult>;
|
|
8
|
+
readonly packageRoot?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function runListModels(json: boolean, deps?: ProbeMainDependencies): Promise<number>;
|
|
11
|
+
export declare function runProbe(request: {
|
|
12
|
+
profiles: readonly string[];
|
|
13
|
+
apply: boolean;
|
|
14
|
+
}, json: boolean, configPath: string, signal: AbortSignal, deps?: ProbeMainDependencies): Promise<number>;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { stdoutGuard } from "./stdout-guard-install.js";
|
|
2
|
+
import { chmod, mkdtemp, realpath, rm } from "node:fs/promises";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { dirname, join, resolve } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { loadConfig } from "./config.js";
|
|
7
|
+
import { runDoctor } from "./doctor.js";
|
|
8
|
+
import { createModelCatalog } from "./model-catalog.js";
|
|
9
|
+
import { runProfileProbe } from "./profile-probe.js";
|
|
10
|
+
import { SdkPiSessionAdapter } from "./sdk-pi-adapter.js";
|
|
11
|
+
import { catalogReport, formatCatalogHuman, formatProbeHuman } from "./setup-probe-result.js";
|
|
12
|
+
export async function runListModels(json, deps = {}) {
|
|
13
|
+
let report;
|
|
14
|
+
try {
|
|
15
|
+
report = catalogReport(await (deps.catalog ?? createModelCatalog)());
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
report = catalogReport(undefined);
|
|
19
|
+
}
|
|
20
|
+
stdoutGuard.transportStdout.write(json ? JSON.stringify(report) + "\n" : formatCatalogHuman(report));
|
|
21
|
+
return report.exitCode;
|
|
22
|
+
}
|
|
23
|
+
function refused(apply, refusal) {
|
|
24
|
+
return {
|
|
25
|
+
schemaVersion: 1, operation: apply ? "apply" : "dry-run", status: "failed", refusal,
|
|
26
|
+
doctor: { blocking: [], other: [] }, findings: [], usage: "unknown", exitCode: refusal === "operation_interrupted" ? 130 : 1,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
export async function runProbe(request, json, configPath, signal, deps = {}) {
|
|
30
|
+
let report;
|
|
31
|
+
let catalogFailed = false;
|
|
32
|
+
try {
|
|
33
|
+
if (!configPath)
|
|
34
|
+
report = refused(request.apply, "config_required");
|
|
35
|
+
else {
|
|
36
|
+
const configFile = resolve(configPath);
|
|
37
|
+
const packageRoot = deps.packageRoot ?? resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
38
|
+
report = await runProfileProbe(request, {
|
|
39
|
+
configPath: configFile, loadConfig, signal,
|
|
40
|
+
doctor: deps.doctor ?? (() => runDoctor({ packageRoot, configPath: configFile, loadConfig: () => loadConfig(configFile) })),
|
|
41
|
+
catalog: async () => {
|
|
42
|
+
try {
|
|
43
|
+
return await (deps.catalog ?? createModelCatalog)();
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
catalogFailed = true;
|
|
47
|
+
throw error;
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
adapter: deps.adapter ?? (async () => new SdkPiSessionAdapter({
|
|
51
|
+
diagnosticSink: (diagnostic) => { process.stderr.write(JSON.stringify(diagnostic) + "\n"); },
|
|
52
|
+
protectedReadPaths: [dirname(configFile), dirname(await realpath(configFile))],
|
|
53
|
+
})),
|
|
54
|
+
makeTempDir: async () => {
|
|
55
|
+
const directory = await mkdtemp(join(tmpdir(), "pi-session-mcp-probe-"));
|
|
56
|
+
try {
|
|
57
|
+
await chmod(directory, 0o700);
|
|
58
|
+
return directory;
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
await rm(directory, { recursive: true, force: true });
|
|
62
|
+
throw error;
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
removeDir: (directory) => rm(directory, { recursive: true, force: true }),
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
report = refused(request.apply, signal.aborted ? "operation_interrupted" : catalogFailed ? "catalog_unavailable" : "operation_failed");
|
|
71
|
+
}
|
|
72
|
+
if (signal.aborted)
|
|
73
|
+
report = { ...report, status: "failed", refusal: "operation_interrupted", exitCode: signal.reason === 143 ? 143 : 130 };
|
|
74
|
+
stdoutGuard.transportStdout.write(json ? JSON.stringify(report) + "\n" : formatProbeHuman(report));
|
|
75
|
+
return report.exitCode;
|
|
76
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { type CatalogProvider, type ModelCatalog } from "./model-catalog.js";
|
|
2
|
+
import type { PiSessionCreationErrorCode } from "./pi-adapter.js";
|
|
3
|
+
/** Public, path-free `--list-models` report. */
|
|
4
|
+
export interface ModelCatalogReport {
|
|
5
|
+
readonly schemaVersion: 1;
|
|
6
|
+
readonly status: "ok" | "failed";
|
|
7
|
+
readonly code?: "catalog_unavailable";
|
|
8
|
+
readonly providers: readonly CatalogProvider[];
|
|
9
|
+
readonly omittedIds: number;
|
|
10
|
+
readonly note: "extension_providers_not_listed";
|
|
11
|
+
readonly exitCode: 0 | 1;
|
|
12
|
+
}
|
|
13
|
+
/** Fails closed when no catalog could be created; drops ids that are not plain catalog ids. */
|
|
14
|
+
export declare function catalogReport(catalog: ModelCatalog | undefined): ModelCatalogReport;
|
|
15
|
+
/** One line per provider with its auth presence and model ids, then the fixed extension note. */
|
|
16
|
+
export declare function formatCatalogHuman(report: ModelCatalogReport): string;
|
|
17
|
+
export type ProbeCode = "probe_planned" | "probe_profile_unknown" | "probe_provider_unknown" | "probe_model_unknown" | "probe_auth_missing" | "probe_passed" | "probe_unexpected_reply" | "probe_turn_failed" | "probe_start_failed" | "probe_timeout";
|
|
18
|
+
export interface ProbeFinding {
|
|
19
|
+
readonly profile: string;
|
|
20
|
+
readonly status: "planned" | "ok" | "failed";
|
|
21
|
+
readonly code: ProbeCode;
|
|
22
|
+
readonly startCode?: PiSessionCreationErrorCode;
|
|
23
|
+
readonly providerTurn: boolean;
|
|
24
|
+
readonly durationMs?: number;
|
|
25
|
+
}
|
|
26
|
+
export interface ProbeReport {
|
|
27
|
+
readonly schemaVersion: 1;
|
|
28
|
+
readonly operation: "dry-run" | "apply";
|
|
29
|
+
readonly status: "planned" | "ok" | "failed";
|
|
30
|
+
readonly refusal?: "doctor_failed" | "config_required" | "config_unusable" | "operation_interrupted" | "catalog_unavailable" | "operation_failed";
|
|
31
|
+
readonly doctor: {
|
|
32
|
+
readonly blocking: readonly string[];
|
|
33
|
+
readonly other: readonly string[];
|
|
34
|
+
};
|
|
35
|
+
readonly findings: readonly ProbeFinding[];
|
|
36
|
+
readonly usage: "unknown";
|
|
37
|
+
readonly exitCode: 0 | 1 | 130 | 143;
|
|
38
|
+
}
|
|
39
|
+
export declare function formatProbeHuman(report: ProbeReport): string;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { CATALOG_ID_PATTERN } from "./model-catalog.js";
|
|
2
|
+
const compareIds = (left, right) => (left < right ? -1 : left > right ? 1 : 0);
|
|
3
|
+
const providersOf = (catalog) => {
|
|
4
|
+
let omittedIds = 0;
|
|
5
|
+
const providers = [];
|
|
6
|
+
for (const provider of [...catalog.providers()].sort((left, right) => compareIds(left.id, right.id))) {
|
|
7
|
+
if (!CATALOG_ID_PATTERN.test(provider.id)) {
|
|
8
|
+
omittedIds += 1;
|
|
9
|
+
continue;
|
|
10
|
+
}
|
|
11
|
+
const models = new Set();
|
|
12
|
+
for (const model of provider.models) {
|
|
13
|
+
if (!CATALOG_ID_PATTERN.test(model)) {
|
|
14
|
+
omittedIds += 1;
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
models.add(model);
|
|
18
|
+
}
|
|
19
|
+
providers.push(Object.freeze({
|
|
20
|
+
id: provider.id,
|
|
21
|
+
authConfigured: provider.authConfigured === true,
|
|
22
|
+
models: Object.freeze([...models].sort(compareIds)),
|
|
23
|
+
}));
|
|
24
|
+
}
|
|
25
|
+
return Object.freeze({ providers: Object.freeze(providers), omittedIds });
|
|
26
|
+
};
|
|
27
|
+
/** Fails closed when no catalog could be created; drops ids that are not plain catalog ids. */
|
|
28
|
+
export function catalogReport(catalog) {
|
|
29
|
+
if (catalog === undefined) {
|
|
30
|
+
return Object.freeze({
|
|
31
|
+
schemaVersion: 1,
|
|
32
|
+
status: "failed",
|
|
33
|
+
code: "catalog_unavailable",
|
|
34
|
+
providers: Object.freeze([]),
|
|
35
|
+
omittedIds: 0,
|
|
36
|
+
note: "extension_providers_not_listed",
|
|
37
|
+
exitCode: 1,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
return Object.freeze({
|
|
41
|
+
schemaVersion: 1,
|
|
42
|
+
status: "ok",
|
|
43
|
+
...providersOf(catalog),
|
|
44
|
+
note: "extension_providers_not_listed",
|
|
45
|
+
exitCode: 0,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
/** One line per provider with its auth presence and model ids, then the fixed extension note. */
|
|
49
|
+
export function formatCatalogHuman(report) {
|
|
50
|
+
const lines = report.providers.map((provider) => `${provider.id} (auth: ${provider.authConfigured ? "yes" : "no"}): ${provider.models.join(", ")}`);
|
|
51
|
+
if (report.status === "failed")
|
|
52
|
+
lines.unshift("Model catalog unavailable.");
|
|
53
|
+
lines.push("Providers registered only by Pi extensions are not listed.");
|
|
54
|
+
return `${lines.join("\n")}\n`;
|
|
55
|
+
}
|
|
56
|
+
export function formatProbeHuman(report) {
|
|
57
|
+
const lines = report.findings.map((finding) => `${finding.profile}: ${finding.status} (${finding.code}${finding.startCode === undefined ? "" : ` ${finding.startCode}`})`);
|
|
58
|
+
if (report.refusal !== undefined)
|
|
59
|
+
lines.unshift(`Probe refused: ${report.refusal}`);
|
|
60
|
+
lines.push(`provider turns: ${report.findings.filter((finding) => finding.providerTurn).length}, usage: unknown`);
|
|
61
|
+
return `${lines.join("\n")}\n`;
|
|
62
|
+
}
|
|
@@ -24,7 +24,7 @@ export const FENCE_CODES = ["writer_busy", "writer_fenced", "writer_lease_unavai
|
|
|
24
24
|
* store's own failures count; `ownership_unavailable` and `ownership_diverged` concern one writer's registration.
|
|
25
25
|
*/
|
|
26
26
|
export const INFRASTRUCTURE_CODE = /^(?:ownership_(?!unavailable$|diverged$)[a-z_]+|config_[a-z_]+|operation_aborted|platform_unsupported)$/;
|
|
27
|
-
export const GLOBAL_ERRORS = new Set(["node_version_unsupported", "package_metadata_unreadable", "build_missing", "build_stale", "version_mismatch", "config_missing", "config_unreadable", "config_invalid"]);
|
|
27
|
+
export const GLOBAL_ERRORS = new Set(["node_version_unsupported", "package_metadata_unreadable", "build_missing", "build_stale", "version_mismatch", "config_missing", "config_unreadable", "config_invalid", "skill_controller_selected"]);
|
|
28
28
|
const safeAlias = (alias) => ALIAS_PATTERN.test(alias) ? alias : "invalid";
|
|
29
29
|
export const targetFor = (alias, name) => ({ client: "writer", scope: safeAlias(alias), alias: WRITER_NAME_PATTERN.test(name) || (alias === "host" && name === "control") ? name : "invalid" });
|
|
30
30
|
export const modesTarget = (alias) => ({ client: "modes", scope: "workspace", alias: safeAlias(alias) });
|
package/dist/skill-library.js
CHANGED
|
@@ -13,7 +13,7 @@ export const SKILL_NAME_PATTERN = /^(?=.{1,64}$)[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
|
13
13
|
/** A `skillCatalog` value `builtin:<name>` names a library skill instead of a path. */
|
|
14
14
|
export const BUILTIN_SKILL_PREFIX = "builtin:";
|
|
15
15
|
/** Skills for the controlling agent, which setup installs into Claude Code and Codex. */
|
|
16
|
-
export const CONTROLLER_SKILLS = Object.freeze(["pi-session-orchestration"]);
|
|
16
|
+
export const CONTROLLER_SKILLS = Object.freeze(["pi-session-orchestration", "pi-session-profiles"]);
|
|
17
17
|
export const MAX_SKILL_TREE_FILES = 32;
|
|
18
18
|
export const MAX_SKILL_TREE_BYTES = 1024 * 1024;
|
|
19
19
|
const MAX_SKILL_TREE_DEPTH = 8;
|
package/docs/acceptance.md
CHANGED
|
@@ -112,7 +112,9 @@ env -i HOME="$DISPOSABLE_HOME" PATH="$PATH" PI_SESSION_MCP_CONFIG="$ACCEPTANCE_C
|
|
|
112
112
|
`skill_already_equivalent`, `skill_verified`, `skill_removed`,
|
|
113
113
|
`skill_installed`, `skill_verified`, `skill_removed`); the skill file exists
|
|
114
114
|
after steps 3 and 7 and is gone after steps 6 and 9; a foreign skill placed
|
|
115
|
-
in the same skill directory stays unchanged throughout.
|
|
115
|
+
in the same skill directory stays unchanged throughout. From 0.14.0 on
|
|
116
|
+
(#139), that holds for both controller skills, `pi-session-orchestration`
|
|
117
|
+
and `pi-session-profiles`, each with its own finding in that order.
|
|
116
118
|
- Steps 1 and 2 leave the client state unchanged.
|
|
117
119
|
- After step 9 the `pi-session-mcp` registration is absent, the sentinel is still
|
|
118
120
|
present, and the ownership record set is empty. The ownership directory has
|
|
@@ -2535,6 +2537,127 @@ Not established for `0.13.0`: the sandbox on a host that restricts unprivileged
|
|
|
2535
2537
|
user namespaces; Git inside the sandbox on a linked worktree in live use (the
|
|
2536
2538
|
bubblewrap-gated tests cover it); the macOS upgrade; Level 3.
|
|
2537
2539
|
|
|
2540
|
+
### v0.13.1 post-release evidence
|
|
2541
|
+
|
|
2542
|
+
Recorded on 2026-10-29 for the published release `0.13.1`.
|
|
2543
|
+
|
|
2544
|
+
Tag binding: a fresh clone of the annotated tag `v0.13.1` (tag object
|
|
2545
|
+
`57c0786f1db3f732db797c8204e338ce17cf7fb2`) peels to the merge commit
|
|
2546
|
+
`c25c7687ecb66043a001c689c64e77be3cd25952` of #185, and
|
|
2547
|
+
`git describe --tags --exact-match` reports `v0.13.1`. CI on the merge commit
|
|
2548
|
+
(run 40832546742) and on the tag (run 40833087074) passed on the first attempt.
|
|
2549
|
+
|
|
2550
|
+
- Level 1 on the tag clone after `npm ci`, from 07:32 to 07:34 UTC with
|
|
2551
|
+
Node.js `26.12.1` and npm `12.2.1`: typecheck, `build:cli`, the test run
|
|
2552
|
+
(136 files, 3948 passed, 14 skipped, with
|
|
2553
|
+
`NODE_OPTIONS=--unhandled-rejections=strict`), `build` and
|
|
2554
|
+
`git diff --check` exited with `0`, and the status stayed clean. Doctor
|
|
2555
|
+
reported `packageVersion` `0.13.1` and exited with `2` because no
|
|
2556
|
+
configuration was set (`config_missing`). The manifests were unchanged
|
|
2557
|
+
afterwards.
|
|
2558
|
+
- `pi-session-mcp@0.13.1` on npm: 922 files, shasum
|
|
2559
|
+
`e2e0e2652a2cab6bf891e61bcfa14754258eb59b`, `gitHead` `c25c768` (the merge
|
|
2560
|
+
commit). It was staged and approved by the maintainer; `npm pack` of the tag
|
|
2561
|
+
clone and the dry run of the publishing clone give the same shasum and file
|
|
2562
|
+
count.
|
|
2563
|
+
- Published npm package, Level 2 from a fresh prefix, 07:34 to 07:36 UTC:
|
|
2564
|
+
160 of 160 with delivery off and 228 of 228 with `--events spool`; real
|
|
2565
|
+
registrations and built files unchanged, no process left, leak check passed.
|
|
2566
|
+
Clients: Codex CLI `0.162.1` and Claude Code `2.1.312`. The install smoke on
|
|
2567
|
+
the published package reported `smoke_passed` with version `0.13.1`.
|
|
2568
|
+
- The GitHub release `v0.13.1` was published the same day.
|
|
2569
|
+
|
|
2570
|
+
Operator upgrade of the workstation from `0.13.0` to `0.13.1` on 2026-10-29
|
|
2571
|
+
from 09:35 UTC, following
|
|
2572
|
+
[Installation](installation.md#container-writers): no writer container ran at
|
|
2573
|
+
the start; controller sessions without a coding session stayed open and need a
|
|
2574
|
+
reconnect. Both client targets were removed with `0.13.0`, then
|
|
2575
|
+
`npm install -g pi-session-mcp@0.13.1`. Doctor reported only
|
|
2576
|
+
`writer_not_ready` for the 18 writers. A dry run of each provisioning command
|
|
2577
|
+
with unchanged settings reported `mode_unchanged` and `writer_planned`; with
|
|
2578
|
+
`--apply`, five container repositories with 18 writers (2 with
|
|
2579
|
+
`--tags reachable`, 4, 4 with `--cache go`, 4 with base `dev`, 4 with
|
|
2580
|
+
`--cache uv`, three of them with operator images) reported `writer_accepted`
|
|
2581
|
+
from 09:36 to 09:39 UTC. Client-target `--apply` and `--verify` passed for
|
|
2582
|
+
`claude-code:user` and `codex:user` (`mcp_verified`, `skill_verified`, and
|
|
2583
|
+
`hook_verified` for the wake hook); `--writer <name> --verify` passed for all
|
|
2584
|
+
18 writers. Doctor reported no error, only the known `workspace_gitfile` and
|
|
2585
|
+
`workspace_mode_unset` warnings of the worktree aliases.
|
|
2586
|
+
|
|
2587
|
+
Operator upgrade of the second machine (Linux) from `0.13.0` to `0.13.1` on
|
|
2588
|
+
2026-10-29 from 09:47 UTC, with Node.js `24.22.0` and the same steps: no
|
|
2589
|
+
writer container and no server process of a client session ran at the start.
|
|
2590
|
+
Both client targets were removed with `0.13.0`, then
|
|
2591
|
+
`npm install -g pi-session-mcp@0.13.1`; Doctor reported only
|
|
2592
|
+
`writer_not_ready` for the 14 writers. The dry runs reported `mode_unchanged`
|
|
2593
|
+
and `writer_planned`; with `--apply`, four container repositories with 14
|
|
2594
|
+
writers (4, 2, 4 with an operator image and `--cache go`, 4 with an operator
|
|
2595
|
+
image) reported `writer_accepted` from 09:48 to 09:51 UTC. Client-target
|
|
2596
|
+
`--apply` and `--verify` passed for `claude-code:user` and `codex:user`
|
|
2597
|
+
(`mcp_verified`, `skill_verified`), and `--writer <name> --verify` passed for
|
|
2598
|
+
all 14 writers. Doctor reported no error, only `workspace_mode_unset` for one
|
|
2599
|
+
workspace without a mode.
|
|
2600
|
+
|
|
2601
|
+
### v0.14.0 pre-release acceptance
|
|
2602
|
+
|
|
2603
|
+
`v0.14.0` was accepted on 2026-10-29 from 17:56 to 18:01 UTC on its release
|
|
2604
|
+
candidate `6e18659`, on Linux (`x64`) with Node.js `26.12.1`, npm `12.2.1`,
|
|
2605
|
+
Codex CLI `0.162.1` and Claude Code `2.1.312`. Level 2 passed in the working
|
|
2606
|
+
tree and for the packed tarball, each with delivery off and with delivery on.
|
|
2607
|
+
Level 1 passed except `npm audit`. Later commits on the release pull request
|
|
2608
|
+
change only this record.
|
|
2609
|
+
|
|
2610
|
+
The minor release ships #139 ([ADR 0014](adr/0014-profile-probe-and-model-catalog.md))
|
|
2611
|
+
and #188. The MCP tool contract is unchanged. Setup gains `--list-models` and
|
|
2612
|
+
`--probe-profile`, installs a second controller skill (`pi-session-profiles`)
|
|
2613
|
+
and so reports one more skill finding per client target; Doctor gains the
|
|
2614
|
+
check `skill_controller_selected`.
|
|
2615
|
+
|
|
2616
|
+
The first working-tree run on the earlier candidate `08fc620` failed 36 of 160
|
|
2617
|
+
checks: the Level 2 harness still expected one controller skill finding, so
|
|
2618
|
+
every step saw one finding too many. Setup itself reported the expected
|
|
2619
|
+
findings. The candidate `6e18659` teaches the harness both controller skills
|
|
2620
|
+
with the same number of checks; every result below is from that candidate.
|
|
2621
|
+
|
|
2622
|
+
- Level 1 in a clean worktree of the candidate after `npm ci`: every command
|
|
2623
|
+
exited with `0`, except `npm audit`; `git status --short` printed nothing.
|
|
2624
|
+
- The test run, with `NODE_OPTIONS=--unhandled-rejections=strict`, reported
|
|
2625
|
+
139 files, 4005 passed and 14 skipped, and no unhandled error.
|
|
2626
|
+
- `npm audit` and `npm audit --omit=dev` exited with `1` and name only the
|
|
2627
|
+
known `undici` 8.10.2 finding through `@earendil-works/pi-coding-agent`
|
|
2628
|
+
1.4.0, the latest SDK release, with eight advisories
|
|
2629
|
+
([#120](https://github.com/plagor44-ux/pi-session-mcp/issues/120)).
|
|
2630
|
+
- `npm pack` listed 932 files (tarball shasum
|
|
2631
|
+
`4eb9b77c9484292a8533c6be0fda44ac18656787`, identical in three packs). A
|
|
2632
|
+
scan of the unpacked files for private names, hosts and the maintainer's
|
|
2633
|
+
home path found none.
|
|
2634
|
+
- Doctor reported `packageVersion` `0.14.0`. It exited with `2` because no
|
|
2635
|
+
configuration was set (`config_missing`).
|
|
2636
|
+
- Level 2 in the working tree: 160 of 160 checks passed with delivery off and
|
|
2637
|
+
228 of 228 with `--events spool`.
|
|
2638
|
+
- Packed-tarball Level 2 from a fresh prefix: 160 of 160 with delivery off and
|
|
2639
|
+
228 of 228 with `--events spool`. The install smoke from another fresh prefix
|
|
2640
|
+
reported `smoke_passed` with version `0.14.0`.
|
|
2641
|
+
- In every Level 2 run the real client registrations were unchanged, no process
|
|
2642
|
+
remained, the leak check passed and the disposable directories were removed.
|
|
2643
|
+
- Podman-gated writer tests on the candidate (Podman `6.1.4`): 7 of 7 passed.
|
|
2644
|
+
- Bubblewrap-gated sandbox tests (`test/sandbox-shell.bwrap.test.ts`, bubblewrap
|
|
2645
|
+
`0.13.1`): 12 of 12 passed, none skipped.
|
|
2646
|
+
- Live checks before the candidate, on the merged #139 code: `--list-models`
|
|
2647
|
+
against a real local Pi installation reported authentication for the
|
|
2648
|
+
configured providers, the probe dry run planned two profiles, and one
|
|
2649
|
+
`--probe-profile <alias> --apply` returned `probe_passed` with one provider
|
|
2650
|
+
turn.
|
|
2651
|
+
- Review: #139 had three cross-family reviews (author family OpenAI and
|
|
2652
|
+
Anthropic, reviewer family GLM) and #188 one, each without blocking findings;
|
|
2653
|
+
their findings were fixed or declined with a reason in #187 and #189. The
|
|
2654
|
+
harness fix in this release was checked by its live run above.
|
|
2655
|
+
|
|
2656
|
+
Not established for `0.14.0`: an agent-driven profile change end to end with
|
|
2657
|
+
the `pi-session-profiles` skill, a container writer acceptance of a newly
|
|
2658
|
+
created profile, client-target `--verify` through the installed package, which
|
|
2659
|
+
follows the upgrade; Level 3.
|
|
2660
|
+
|
|
2538
2661
|
### v0.13.1 pre-release acceptance
|
|
2539
2662
|
|
|
2540
2663
|
`v0.13.1` was accepted on 2026-10-29 from 06:40 to 06:48 UTC on its release
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# ADR 0014: Profile probe and offline model catalog
|
|
2
|
+
|
|
3
|
+
**Status:** Accepted (2026-10-29); unreleased. Issue #139.
|
|
4
|
+
|
|
5
|
+
## Context
|
|
6
|
+
|
|
7
|
+
The running MCP server reads its configuration once at startup. A controlling
|
|
8
|
+
agent cannot reconnect its own server to prove a changed execution profile.
|
|
9
|
+
Asking for a manual reconnect prevents autonomous profile setup; starting a
|
|
10
|
+
nested controller would add another agent without testing a different boundary.
|
|
11
|
+
The agent also needs an authoritative offline catalog rather than guessing
|
|
12
|
+
provider and model ids or inspecting credential files.
|
|
13
|
+
|
|
14
|
+
## Decision
|
|
15
|
+
|
|
16
|
+
### 1. Offline model catalog
|
|
17
|
+
|
|
18
|
+
`pi-session-mcp-setup --list-models` reports provider ids, model ids and only
|
|
19
|
+
whether authentication is configured. Package-root `ModelRuntime` access sits
|
|
20
|
+
behind injectable `ModelCatalog`. Creation disables model network access and
|
|
21
|
+
refresh (`allowModelNetwork: false`, `refreshOnCreate: false`); the catalog
|
|
22
|
+
uses only `getProviders`, `getModels` and `checkAuth`. `checkAuth` reads the
|
|
23
|
+
local auth configuration offline and returns only its source and kind; the
|
|
24
|
+
catalog reduces that to a boolean and bounds each lookup. `hasConfiguredAuth`
|
|
25
|
+
is not used because it reads a snapshot that only an online availability
|
|
26
|
+
refresh fills, so offline it always reports no authentication. The catalog
|
|
27
|
+
never calls `getAuth` or `getAvailable`. This is a second narrow exception
|
|
28
|
+
next to the writer wrapper's credential refresh (#142), not general permission
|
|
29
|
+
to bypass `PiSessionAdapter`.
|
|
30
|
+
|
|
31
|
+
Providers registered only by extensions are not listed; every catalog report
|
|
32
|
+
states this limitation. Invalid catalog ids are omitted, never echoed.
|
|
33
|
+
|
|
34
|
+
### 2. Opt-in profile probe
|
|
35
|
+
|
|
36
|
+
`--probe-profile review,code` runs Doctor and offline profile, provider, model
|
|
37
|
+
and authentication-presence checks. Only adding `--apply` authorizes one fixed
|
|
38
|
+
PONG turn per passing profile, sequentially, through `PiSessionAdapter`. The
|
|
39
|
+
production dependency is `SdkPiSessionAdapter`, resolving the profile as the
|
|
40
|
+
server does and protecting both the named and canonical configuration
|
|
41
|
+
directories from read-root expansion through skills.
|
|
42
|
+
|
|
43
|
+
Doctor blocks on global configuration/build/version errors and errors about
|
|
44
|
+
the requested profiles. Other errors and warnings are reported by check id
|
|
45
|
+
without blocking this request. Tests inject a fake catalog, adapter and Doctor;
|
|
46
|
+
no provider credentials are needed.
|
|
47
|
+
|
|
48
|
+
The session uses a private empty temporary directory, mode 0700, created
|
|
49
|
+
internally and removed after bounded session cleanup. This is a narrow
|
|
50
|
+
exception to the configured-workspace rule: the directory is not a workspace
|
|
51
|
+
alias and is never supplied as a tool or CLI argument. It avoids exposing a
|
|
52
|
+
real repository as the probe's working directory; it does not sandbox trusted
|
|
53
|
+
extensions or change coding profiles' existing authority.
|
|
54
|
+
|
|
55
|
+
Start, turn and cleanup deadlines are 60, 120 and 10 seconds respectively.
|
|
56
|
+
Interrupts abort and dispose the current session and skip remaining profiles.
|
|
57
|
+
The fixed prompt is not configurable, the reply text is never reported, usage
|
|
58
|
+
is `unknown`, and each finding records whether a provider turn was accepted.
|
|
59
|
+
No event spool is enabled.
|
|
60
|
+
|
|
61
|
+
### 3. Guarded, lazy CLI entry
|
|
62
|
+
|
|
63
|
+
Ordinary setup requests never import the probe entry or install its stdout
|
|
64
|
+
guard. The catalog and probe load it dynamically after the platform gate;
|
|
65
|
+
the catalog needs no configuration. Its first import installs the same stdout
|
|
66
|
+
guard as the server before SDK evaluation. Only the guard's transport writer
|
|
67
|
+
emits reports; foreign stdout, including extension `console.log`, is dropped.
|
|
68
|
+
Adapter diagnostics are structured JSON on stderr without prompts or secrets.
|
|
69
|
+
Operational exceptions produce fixed refusal codes rather than raw errors.
|
|
70
|
+
|
|
71
|
+
## Contract change and versioning
|
|
72
|
+
|
|
73
|
+
The new setup flags and report schemas are additive public CLI contract
|
|
74
|
+
changes under [ADR 0003](0003-post-mvp-maintenance.md#versioning). This decision
|
|
75
|
+
does not itself bump the version or authorize publication.
|
|
76
|
+
|
|
77
|
+
## Consequences
|
|
78
|
+
|
|
79
|
+
- Doctor and `--verify` remain provider-free. Only
|
|
80
|
+
`--probe-profile … --apply` uses a provider among setup operations.
|
|
81
|
+
- Catalog and probe run on Linux and macOS without acquiring the setup lock
|
|
82
|
+
or changing registrations or configuration.
|
|
83
|
+
- The probe proves the profile on the host using the host Pi agent directory.
|
|
84
|
+
Container acceptance remains Doctor's `container_profile_refused` check and
|
|
85
|
+
writer provisioning, not this probe.
|
|
86
|
+
- A successful probe does not reload any running controller's configuration;
|
|
87
|
+
an MCP server restart or reconnect is still required.
|
|
88
|
+
- Reports expose only sanitized findings, durations and provider-turn presence,
|
|
89
|
+
not paths, credential values, environment values, prompts or reply text.
|
package/docs/client-setup.md
CHANGED
|
@@ -53,6 +53,9 @@ npm run --silent setup -- --apply --target codex:user:pi-session-mcp
|
|
|
53
53
|
npm run --silent setup -- --verify --target codex:user:pi-session-mcp
|
|
54
54
|
npm run --silent setup -- --rollback --target codex:user:pi-session-mcp
|
|
55
55
|
npm run --silent setup -- --remove --target codex:user:pi-session-mcp
|
|
56
|
+
npm run --silent setup -- --list-models --json
|
|
57
|
+
npm run --silent setup -- --probe-profile review,code --json
|
|
58
|
+
npm run --silent setup -- --probe-profile review,code --apply --json
|
|
56
59
|
```
|
|
57
60
|
|
|
58
61
|
Setup runs the previously built `dist/setup-main.js`. The dry-run operation
|
|
@@ -109,27 +112,30 @@ inspected:
|
|
|
109
112
|
None of the failure rows changes a registration or starts the handshake, and
|
|
110
113
|
each exits with `1`.
|
|
111
114
|
|
|
112
|
-
### Controller
|
|
115
|
+
### Controller skills
|
|
113
116
|
|
|
114
|
-
Besides the registration, `--apply` installs the controller
|
|
115
|
-
`pi-session-orchestration`
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
117
|
+
Besides the registration, `--apply` installs the controller skills
|
|
118
|
+
`pi-session-orchestration` (delegating work to Pi workers) and
|
|
119
|
+
`pi-session-profiles` (creating, changing and retiring execution profiles) for
|
|
120
|
+
each target, each as a real copy of the effective skill: your override in the
|
|
121
|
+
skill library if you have one, else the skill shipped in the package
|
|
122
|
+
([Pi skills](skills.md)). Clients discover them without further steps.
|
|
119
123
|
|
|
120
|
-
| Target | Where the
|
|
124
|
+
| Target | Where the skills go |
|
|
121
125
|
| --- | --- |
|
|
122
|
-
| `codex:user:pi-session-mcp` | `~/.agents/skills
|
|
123
|
-
| `claude-code:user:pi-session-mcp`, `claude-code:local:pi-session-mcp` | `~/.claude/skills
|
|
124
|
-
| `claude-code:project:pi-session-mcp` | `<project>/.claude/skills
|
|
126
|
+
| `codex:user:pi-session-mcp` | `~/.agents/skills/<skill>` |
|
|
127
|
+
| `claude-code:user:pi-session-mcp`, `claude-code:local:pi-session-mcp` | `~/.claude/skills/<skill>` (one shared directory per skill) |
|
|
128
|
+
| `claude-code:project:pi-session-mcp` | `<project>/.claude/skills/<skill>`, where `<project>` is the directory in which setup runs |
|
|
125
129
|
|
|
126
|
-
|
|
127
|
-
|
|
130
|
+
`<skill>` is `pi-session-orchestration` or `pi-session-profiles`. Every result
|
|
131
|
+
therefore has one more finding per skill and target, with the same client and
|
|
132
|
+
scope and the skill name as alias:
|
|
128
133
|
|
|
129
134
|
```text
|
|
130
135
|
apply: ok
|
|
131
136
|
claude-code/user/pi-session-mcp: ok (applied)
|
|
132
137
|
claude-code/user/pi-session-orchestration: ok (skill_installed)
|
|
138
|
+
claude-code/user/pi-session-profiles: ok (skill_installed)
|
|
133
139
|
```
|
|
134
140
|
|
|
135
141
|
Skill findings per operation:
|
package/docs/doctor-design.md
CHANGED
|
@@ -28,6 +28,7 @@ and owners are:
|
|
|
28
28
|
| `config_missing` | `PI_SESSION_MCP_CONFIG` is not set, or the file it names is absent | `configuration` |
|
|
29
29
|
| `config_unreadable` | The supported configuration path cannot be read | `configuration` |
|
|
30
30
|
| `config_invalid` | Production `loadConfig` rejects the configuration. One check per schema issue location, at most five, sorted | `configuration:<location>`, a dotted path of schema field names, aliases and array indexes such as `configuration:executionProfiles.review.default`; an unknown key that is not a schema field name, and any other key that is not a valid alias, is shown as `<key>`, because it could be a path or a secret. `configuration` when the error has no location |
|
|
31
|
+
| `skill_controller_selected` | Production `loadConfig` rejects the configuration because a `skillCatalog` value names a controller skill (`builtin:pi-session-orchestration` or `builtin:pi-session-profiles`). Reported instead of `config_invalid` for that location and blocks like it | `configuration:skillCatalog.<alias>` |
|
|
31
32
|
| `workspace_missing` | A resolved configured workspace does not exist | validated workspace alias |
|
|
32
33
|
| `workspace_not_directory` | A resolved workspace is not a directory | validated workspace alias |
|
|
33
34
|
| `workspace_unreadable` | A resolved workspace cannot be read | validated workspace alias |
|
package/docs/installation.md
CHANGED
|
@@ -89,6 +89,89 @@ local user's existing Pi authentication and never prints it. Automated writers
|
|
|
89
89
|
keep selected provider credentials in their private boundary agent copy; see
|
|
90
90
|
[Execution modes](execution-modes.md#writer-storage-launch-and-readiness).
|
|
91
91
|
|
|
92
|
+
## Check profiles: model catalog and probe
|
|
93
|
+
|
|
94
|
+
List the providers and model ids Pi knows locally, with authentication presence
|
|
95
|
+
only (never credential values):
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
pi-session-mcp-setup --list-models
|
|
99
|
+
pi-session-mcp-setup --list-models --json
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
This is offline and read-only: no provider request, credential refresh, model
|
|
103
|
+
network access or setup lock. It requires no `PI_SESSION_MCP_CONFIG`.
|
|
104
|
+
Providers registered only by Pi extensions are not listed; the report includes
|
|
105
|
+
that note. Invalid catalog ids are omitted and counted in `omittedIds`.
|
|
106
|
+
Catalog creation failures report `catalog_unavailable`.
|
|
107
|
+
|
|
108
|
+
With `PI_SESSION_MCP_CONFIG` already set to the configuration to check:
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
pi-session-mcp-setup --probe-profile review,code --json
|
|
112
|
+
pi-session-mcp-setup --probe-profile review,code --apply --json
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
The first command is a dry run: Doctor runs offline, followed by profile,
|
|
116
|
+
provider, model and authentication-presence checks. Only `--apply` sends a
|
|
117
|
+
provider turn: one fixed PONG request per profile that passes the offline
|
|
118
|
+
checks, sequentially. Duplicate aliases are checked once in request order.
|
|
119
|
+
Point the variable at a candidate configuration to check it before going live;
|
|
120
|
+
the commands do not write configuration or registrations. The running MCP
|
|
121
|
+
server still needs a restart or reconnect to read changes.
|
|
122
|
+
|
|
123
|
+
The Doctor gate blocks global configuration/build/version errors and errors
|
|
124
|
+
whose subject is a requested profile. Unrelated errors and all warnings are
|
|
125
|
+
listed by check id in `doctor.other`, without blocking. A blocked request
|
|
126
|
+
reports `doctor_failed`; missing or unusable configuration reports
|
|
127
|
+
`config_required` or `config_unusable`. Unexpected operational failures report
|
|
128
|
+
`operation_failed`, and catalog creation failures report `catalog_unavailable`.
|
|
129
|
+
No raw exception text is printed.
|
|
130
|
+
|
|
131
|
+
Each applied probe starts the normal adapter with the resolved profile in a
|
|
132
|
+
private empty temporary directory (mode 0700), not a real repository. This
|
|
133
|
+
directory is created internally, never supplied as an argument or workspace
|
|
134
|
+
alias, and removed after cleanup. Start and turn deadlines are 60 and 120
|
|
135
|
+
seconds; session cleanup is bounded to 10 seconds. Interrupts abort and dispose
|
|
136
|
+
the current session and skip the remaining profiles. Reports never include
|
|
137
|
+
paths, credential values, environment values, the prompt or reply text.
|
|
138
|
+
Foreign stdout from profiles/extensions is suppressed so JSON remains intact;
|
|
139
|
+
sanitized structured diagnostics go to stderr. No turn event spool is used.
|
|
140
|
+
|
|
141
|
+
| Code | Status | Meaning |
|
|
142
|
+
| --- | --- | --- |
|
|
143
|
+
| `probe_planned` | planned | Dry run: offline checks passed |
|
|
144
|
+
| `probe_profile_unknown` | failed | Alias not in the configuration |
|
|
145
|
+
| `probe_provider_unknown` | failed | Provider not in the catalog (offline) |
|
|
146
|
+
| `probe_model_unknown` | failed | Model not in the provider's catalog (offline) |
|
|
147
|
+
| `probe_auth_missing` | failed | Provider has no configured authentication (offline) |
|
|
148
|
+
| `probe_passed` | ok | Session started, reply was `PONG` |
|
|
149
|
+
| `probe_unexpected_reply` | failed | Turn completed with another reply |
|
|
150
|
+
| `probe_turn_failed` | failed | Turn failed or was aborted |
|
|
151
|
+
| `probe_start_failed` | failed | Session creation refused; carries the existing sanitized `PiSessionCreationErrorCode` |
|
|
152
|
+
| `probe_timeout` | failed | Start or turn deadline passed; the session was aborted and disposed |
|
|
153
|
+
|
|
154
|
+
A profile that fails an offline check is not started; the other profiles are
|
|
155
|
+
still checked. Apply findings include `durationMs`. `providerTurn` records
|
|
156
|
+
whether a provider turn was accepted, including turns that failed or timed out;
|
|
157
|
+
a `false` finding did not spend a provider turn. Provider usage and monetary
|
|
158
|
+
cost remain `unknown`.
|
|
159
|
+
|
|
160
|
+
Exit codes: `0` for a successful catalog, a clean dry run (`planned`) or an
|
|
161
|
+
apply where every profile passed; `1` for a failed check or refusal; `64` for
|
|
162
|
+
invalid usage; `130` for SIGINT and `143` for SIGTERM during a probe (both
|
|
163
|
+
report `operation_interrupted`). Both operations run on Linux and macOS and
|
|
164
|
+
need no setup lock. Probe requests cannot be combined with `--target`,
|
|
165
|
+
`--repo`, `--writer`, `--customize-skill`, `--list-models`, `--verify`,
|
|
166
|
+
`--rollback` or `--remove`. `--list-models` is standalone apart from `--json` and the default `--dry-run`.
|
|
167
|
+
Doctor and `--verify` remain provider-free.
|
|
168
|
+
|
|
169
|
+
**Container repositories:** the probe proves the profile on the host, with
|
|
170
|
+
the host's Pi agent directory. Writer acceptance stays with Doctor's
|
|
171
|
+
`container_profile_refused` check and writer provisioning; a host probe does
|
|
172
|
+
not prove a container writer. See [Execution modes](execution-modes.md) and
|
|
173
|
+
[ADR 0014](adr/0014-profile-probe-and-model-catalog.md).
|
|
174
|
+
|
|
92
175
|
## 1. Install
|
|
93
176
|
|
|
94
177
|
### From npm
|
|
@@ -701,6 +784,10 @@ choose skills.
|
|
|
701
784
|
directory, so concurrent runs cannot lose a recorded base.
|
|
702
785
|
- A `builtin:` name with no override and no shipped skill makes Doctor report
|
|
703
786
|
`skill_missing` and the session start fail with `skill_load_failed`.
|
|
787
|
+
- The controller skills `pi-session-orchestration` and `pi-session-profiles`
|
|
788
|
+
are not worker skills: a `builtin:` catalog entry for either makes the
|
|
789
|
+
configuration invalid, and Doctor reports `skill_controller_selected` for
|
|
790
|
+
`configuration:skillCatalog.<alias>`.
|
|
704
791
|
- An extension allowlist alone makes none of a package's skills available. If a
|
|
705
792
|
profile relied on them, add a catalog entry with the absolute path of the
|
|
706
793
|
package's `SKILL.md` and select it; see the migration section of [Pi
|