@hobin/developer 0.1.8 → 0.1.9
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/README.md +215 -372
- package/REFERENCE_ROUTING.md +243 -0
- package/extensions/developer.ts +533 -18
- package/extensions/machine.ts +45 -0
- package/extensions/references/behavior-preserving-structural-change.md +87 -8
- package/extensions/skills.ts +330 -52
- package/extensions/state.ts +227 -2
- package/package.json +9 -3
- package/skills/abstraction-review/SKILL.md +37 -23
- package/skills/abstraction-review/reference-policy.json +66 -0
- package/skills/abstraction-review/references/field-card.md +113 -168
- package/skills/abstraction-review/references/repair-table.md +66 -80
- package/skills/abstraction-review/references/worked-examples.md +79 -256
- package/skills/model/SKILL.md +31 -13
- package/skills/model/reference-policy.json +96 -0
- package/skills/model/references/contract-and-replacement-models.md +112 -0
- package/skills/model/references/logic-query-semantics.md +89 -0
- package/skills/model/references/planning-models.md +75 -0
- package/skills/model/references/problem-modeling.md +143 -276
- package/skills/model/references/proof-obligations.md +71 -0
- package/skills/model/references/relational-constraint-models.md +110 -0
- package/skills/model/references/solver-result-boundaries.md +83 -0
- package/skills/model/references/temporal-behavior-models.md +106 -0
- package/skills/naming-judgment/SKILL.md +17 -4
- package/skills/naming-judgment/reference-policy.json +29 -0
- package/skills/naming-judgment/references/domain-naming.md +29 -10
- package/skills/schedule/SKILL.md +16 -6
- package/skills/schedule/reference-policy.json +29 -0
- package/skills/schedule/references/structural-change-timing.md +81 -18
- package/skills/signal/SKILL.md +17 -6
- package/skills/signal/reference-policy.json +29 -0
- package/skills/signal/references/structural-movement.md +87 -14
- package/skills/sketch/SKILL.md +38 -42
- package/skills/sketch/reference-policy.json +212 -0
- package/skills/sketch/references/accumulator-invariants.md +125 -0
- package/skills/sketch/references/closure-and-conventional-interfaces.md +87 -0
- package/skills/sketch/references/composition-by-wishes.md +69 -0
- package/skills/sketch/references/data-driven-design.md +99 -22
- package/skills/sketch/references/data-shape-template-catalog.md +58 -19
- package/skills/sketch/references/design-levels-and-boundaries.md +133 -0
- package/skills/sketch/references/earned-abstraction.md +75 -0
- package/skills/sketch/references/generative-recursion.md +122 -0
- package/skills/sketch/references/generic-dispatch-systems.md +75 -0
- package/skills/sketch/references/language-semantics.md +82 -0
- package/skills/sketch/references/meaning-preserving-conversions.md +69 -0
- package/skills/sketch/references/process-shape-and-resources.md +157 -0
- package/skills/sketch/references/representation-barriers.md +73 -0
- package/skills/sketch/references/responsibility-and-collaboration.md +108 -0
- package/skills/sketch/references/runtime-and-compilation.md +84 -0
- package/skills/sketch/references/selection-and-creation.md +76 -0
- package/skills/sketch/references/state-history-and-order.md +126 -0
- package/skills/sketch/references/type-transitions.md +54 -0
- package/skills/sketch/references/variation-roles.md +59 -0
- package/skills/verify/SKILL.md +17 -6
- package/skills/verify/reference-policy.json +30 -0
- package/skills/verify/references/verifier-selection-and-pass-but-wrong.md +152 -192
- package/SOURCES.md +0 -104
- package/skills/abstraction-review/references/recipe-cards.md +0 -322
- package/skills/model/references/worked-models-and-specialized-techniques.md +0 -218
- package/skills/sketch/references/abstraction-barriers-and-closure.md +0 -160
- package/skills/sketch/references/abstraction-composition-and-state.md +0 -184
- package/skills/sketch/references/composition-generative-recursion-and-accumulators.md +0 -196
- package/skills/sketch/references/generic-operations-and-languages.md +0 -134
- package/skills/sketch/references/processes-state-and-time.md +0 -171
- package/skills/sketch/references/responsibility-and-variation.md +0 -245
package/extensions/developer.ts
CHANGED
|
@@ -18,7 +18,14 @@ import {
|
|
|
18
18
|
import { Container, Markdown, Text } from "@earendil-works/pi-tui";
|
|
19
19
|
import { Type, type Static } from "typebox";
|
|
20
20
|
|
|
21
|
-
import {
|
|
21
|
+
import {
|
|
22
|
+
availablePackageSkills,
|
|
23
|
+
isWithinRoot,
|
|
24
|
+
loadSkillReference,
|
|
25
|
+
loadSkillReferencePolicy,
|
|
26
|
+
renderSkillMethod,
|
|
27
|
+
skillReferencePaths,
|
|
28
|
+
} from "./skills.ts";
|
|
22
29
|
import {
|
|
23
30
|
ACTIVATION_ENTRY,
|
|
24
31
|
FOCUS_ENTRY,
|
|
@@ -28,6 +35,7 @@ import {
|
|
|
28
35
|
MAX_RESPONSE_OPTIONS,
|
|
29
36
|
MAX_RESPONSE_TEXT_CHARS,
|
|
30
37
|
PROTOCOL,
|
|
38
|
+
REFERENCE_TOOL,
|
|
31
39
|
ROUTE_TOOL,
|
|
32
40
|
applyDeveloperEvent,
|
|
33
41
|
canApplyDeveloperEvent,
|
|
@@ -49,6 +57,11 @@ import {
|
|
|
49
57
|
type QuestionResolutionOwner,
|
|
50
58
|
type QuestionUpdate,
|
|
51
59
|
type QuestionUpdateStatus,
|
|
60
|
+
type ReferenceBasis,
|
|
61
|
+
type ReferenceExemption,
|
|
62
|
+
type ReferenceLoadEvent,
|
|
63
|
+
type ReferencePolicyExemption,
|
|
64
|
+
type ReferencePolicyRoute,
|
|
52
65
|
type RouteAlternative,
|
|
53
66
|
type RouteEvent,
|
|
54
67
|
} from "./state.ts";
|
|
@@ -76,7 +89,7 @@ import {
|
|
|
76
89
|
type ImmediateQuestionDisposition,
|
|
77
90
|
} from "./tui.ts";
|
|
78
91
|
|
|
79
|
-
const PROTOCOL_TOOLS = [ROUTE_TOOL, JUDGMENT_TOOL] as const;
|
|
92
|
+
const PROTOCOL_TOOLS = [ROUTE_TOOL, REFERENCE_TOOL, JUDGMENT_TOOL] as const;
|
|
80
93
|
const extensionRoot = dirname(fileURLToPath(import.meta.url));
|
|
81
94
|
const skillsRoot = resolve(extensionRoot, "..", "skills");
|
|
82
95
|
const structuralChangeMethodPath = resolve(
|
|
@@ -142,6 +155,27 @@ function compactLine(value: string, maxChars = 160): string {
|
|
|
142
155
|
return line.length <= maxChars ? line : `${line.slice(0, maxChars - 1)}…`;
|
|
143
156
|
}
|
|
144
157
|
|
|
158
|
+
function referenceRouteSummary(route: ReferencePolicyRoute): string {
|
|
159
|
+
return `${route.id}: question=${route.question} | trigger=${route.trigger} | refines=${route.methodStep} | references=${route.references.join(" + ")} (read order: ${route.readOrder}) | produces=${route.artifacts.join("; ")} | stop=${route.stop} | separate when=${route.separateWhen}`;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function referencePolicyLines(
|
|
163
|
+
routes: ReferencePolicyRoute[],
|
|
164
|
+
exemption: ReferencePolicyExemption | undefined,
|
|
165
|
+
): string[] {
|
|
166
|
+
if (routes.length === 0) return [];
|
|
167
|
+
return [
|
|
168
|
+
"Reference routes (each route extends one named judgment step and answers one narrower question; its references are one co-required set, while read order is declared separately):",
|
|
169
|
+
...routes.map((route) => `- ${referenceRouteSummary(route)}`),
|
|
170
|
+
...(exemption
|
|
171
|
+
? [
|
|
172
|
+
`Reference exemption applies only when: ${exemption.when}`,
|
|
173
|
+
`Exemption evidence: ${exemption.evidence.join(" | ")}`,
|
|
174
|
+
]
|
|
175
|
+
: []),
|
|
176
|
+
];
|
|
177
|
+
}
|
|
178
|
+
|
|
145
179
|
function normalizedQuestion(value: string): string {
|
|
146
180
|
return value
|
|
147
181
|
.toLocaleLowerCase()
|
|
@@ -413,11 +447,13 @@ function protocolPrompt(
|
|
|
413
447
|
"Developer protocol is active.",
|
|
414
448
|
"- Use the default topology as a conditional backbone, not a rigid lifecycle: clarify meaning when needed -> model consequential cases -> sketch the first implementation surface for new behavior or signal the smallest structural movement in existing code -> execute one implementation step -> verify current claims.",
|
|
415
449
|
"- Adapt away from that topology when evidence makes a stage not applicable, but never jump directly from a resolved model to mutation: use sketch for feature implementation or signal for existing-code structural movement first.",
|
|
450
|
+
"- Route by the requested work product, not keywords: use sketch to create an original caller-facing interface, representation boundary, ownership map, or collaboration; use abstraction-review only when a concrete candidate surface already exists to keep/revise/split/reject/defer; use model first only when unresolved cases, rules, or policy can materially change that surface.",
|
|
416
451
|
`- Call ${ROUTE_TOOL} for exactly one concrete judgment or one green-to-green implementation movement.`,
|
|
417
452
|
"- Use target=implementation only when the next local movement, stable landing, and narrow verification are already justified; otherwise choose the focused skill whose scope fits the current question.",
|
|
418
453
|
"- An implementation step has one observable difference and one structural or behavioral purpose. Stop when its failure is locally explainable and the repository is green, pausable, and reviewable.",
|
|
419
454
|
"- For implementation structural work intended to preserve behavior, set execution_profile=behavior-preserving-structure; omit the field for other implementation actions and every skill route.",
|
|
420
455
|
`- Follow the routed method, then close the route with ${JUDGMENT_TOOL}. After every implementation stable landing, route again from the new evidence before selecting another movement.`,
|
|
456
|
+
`- During a reference-bearing skill route, select every policy route whose observable trigger addresses a distinct unresolved question. Each selected route must refine its declared skill-method step, produce its declared artifact, satisfy its stop condition, and hand off instead of absorbing work covered by separate_when. Load every reference in each route's co-required set with ${REFERENCE_TOOL} in its explicit order. Do not select a broad fallback route when a narrower selected route already includes its work. A resolved judgment must apply every loaded reference in reference_basis, or use reference_exemption only when no route trigger applies.`,
|
|
421
457
|
"- Consecutive implementation routes are valid when the new evidence still justifies implementation action. On a reroute after implementation, explicitly reconsider the most plausible skill routes and record why they are not needed; do not choose implementation by momentum.",
|
|
422
458
|
"- Do not carry a predetermined implementation queue through multiple implementation steps. Re-observe after each stable landing and reroute to a skill whenever meaning, cases, design, structural direction, timing, naming, or evidence becomes uncertain.",
|
|
423
459
|
"- Protocol state is routing bookkeeping. Idle never proves product completion, user acceptance, or current verification.",
|
|
@@ -465,6 +501,9 @@ function protocolPrompt(
|
|
|
465
501
|
if (state.activeRoute.methodLocation) {
|
|
466
502
|
lines.push(
|
|
467
503
|
`Active skill location: ${state.activeRoute.methodLocation}. Read it again if compaction or a later turn no longer contains the full instructions.`,
|
|
504
|
+
`Available routed references: ${state.activeRoute.availableReferences.join(", ") || "none"}.`,
|
|
505
|
+
`Reference routes: ${state.activeRoute.referenceRoutes.map((route) => `${route.id}[${route.references.join("+")}; order=${route.readOrder}]`).join(" | ") || "none"}. Re-read the skill's reference-policy.json if trigger or artifact details were compacted.`,
|
|
506
|
+
`Loaded routed references: ${state.activeRoute.loadedReferences.map((reference) => `${reference.path}@${reference.contentSha256.slice(0, 12)}[${reference.referenceRouteIds.join(",") || "legacy"}]`).join(", ") || "none"}.`,
|
|
468
507
|
);
|
|
469
508
|
}
|
|
470
509
|
}
|
|
@@ -534,6 +573,26 @@ function expandedJudgment(
|
|
|
534
573
|
|
|
535
574
|
const evidence = [
|
|
536
575
|
...event.basis.map((basis) => detailLine(theme, "basis", basis)),
|
|
576
|
+
...(event.referenceBasis ?? []).flatMap((reference) => [
|
|
577
|
+
detailLine(
|
|
578
|
+
theme,
|
|
579
|
+
"reference",
|
|
580
|
+
`${reference.path}@${reference.contentSha256.slice(0, 12)} [${reference.referenceRouteIds.join(", ") || "legacy"}] — trigger: ${reference.trigger}`,
|
|
581
|
+
"accent",
|
|
582
|
+
),
|
|
583
|
+
detailLine(theme, "applied rule", reference.appliedRule, "text"),
|
|
584
|
+
detailLine(theme, "resulting artifact", reference.artifact, "text"),
|
|
585
|
+
]),
|
|
586
|
+
...(event.referenceExemption
|
|
587
|
+
? [
|
|
588
|
+
detailLine(
|
|
589
|
+
theme,
|
|
590
|
+
"reference exemption",
|
|
591
|
+
`${event.referenceExemption.reason} — ${event.referenceExemption.evidence.join(" | ")}`,
|
|
592
|
+
"warning",
|
|
593
|
+
),
|
|
594
|
+
]
|
|
595
|
+
: []),
|
|
537
596
|
...event.artifacts.map((artifact) =>
|
|
538
597
|
detailLine(theme, "artifact", artifact),
|
|
539
598
|
),
|
|
@@ -806,11 +865,11 @@ export default async function developer(pi: ExtensionAPI) {
|
|
|
806
865
|
name: ROUTE_TOOL,
|
|
807
866
|
label: "Developer Route Question",
|
|
808
867
|
description:
|
|
809
|
-
"Route one concrete judgment or one green-to-green implementation movement.
|
|
868
|
+
"Route one concrete judgment or one green-to-green implementation movement. Route by work product: sketch creates an original design surface, abstraction-review judges an already-shaped candidate, and model settles unresolved conditions before design. Then use implementation stable landings and verify before completion.",
|
|
810
869
|
promptSnippet: "Choose how to handle one development question",
|
|
811
870
|
promptGuidelines: [
|
|
812
871
|
`Call ${ROUTE_TOOL} only when there is no active Developer route.`,
|
|
813
|
-
`Use ${ROUTE_TOOL} with the most focused skill supported by current evidence; target=implementation requires one movement, one stable landing, and one narrow verification.`,
|
|
872
|
+
`Use ${ROUTE_TOOL} with the most focused skill supported by current evidence; choose sketch for an original interface or boundary, abstraction-review only for an already-shaped candidate, and model only for unresolved condition space. target=implementation requires one movement, one stable landing, and one narrow verification.`,
|
|
814
873
|
`When ${ROUTE_TOOL} follows an implementation judgment with another implementation route, cite the previous landing in known_evidence and record plausible skill routes in alternatives_considered instead of selecting implementation by momentum.`,
|
|
815
874
|
`After a resolved model, use sketch for first feature implementation framing or signal for existing-code structural movement before implementation mutation.`,
|
|
816
875
|
],
|
|
@@ -934,20 +993,29 @@ export default async function developer(pi: ExtensionAPI) {
|
|
|
934
993
|
? (requestedExecutionProfile ?? "ordinary")
|
|
935
994
|
: undefined;
|
|
936
995
|
|
|
937
|
-
const
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
996
|
+
const availableReferences = skill
|
|
997
|
+
? await skillReferencePaths(skill)
|
|
998
|
+
: [];
|
|
999
|
+
const referencePolicy = skill
|
|
1000
|
+
? await loadSkillReferencePolicy(skill, availableReferences)
|
|
1001
|
+
: { routes: [] };
|
|
1002
|
+
let method: string;
|
|
1003
|
+
if (skill) {
|
|
1004
|
+
method = await renderSkillMethod(skill);
|
|
1005
|
+
} else if (executionProfile === "behavior-preserving-structure") {
|
|
1006
|
+
const profile = await readFile(structuralChangeMethodPath, "utf8");
|
|
1007
|
+
method = [
|
|
1008
|
+
'<developer-implementation-profile name="behavior-preserving-structure">',
|
|
1009
|
+
profile.trim(),
|
|
1010
|
+
"</developer-implementation-profile>",
|
|
1011
|
+
].join("\n");
|
|
1012
|
+
} else {
|
|
1013
|
+
method = [
|
|
1014
|
+
"# Implementation action",
|
|
1015
|
+
"",
|
|
1016
|
+
"The next local action is already justified. Keep this route open while using Pi implementation tools and collecting evidence.",
|
|
1017
|
+
].join("\n");
|
|
1018
|
+
}
|
|
951
1019
|
|
|
952
1020
|
const implementationStep =
|
|
953
1021
|
target === "implementation"
|
|
@@ -976,6 +1044,11 @@ export default async function developer(pi: ExtensionAPI) {
|
|
|
976
1044
|
reason,
|
|
977
1045
|
knownEvidence,
|
|
978
1046
|
consideredAlternatives,
|
|
1047
|
+
availableReferences,
|
|
1048
|
+
referenceRoutes: referencePolicy.routes,
|
|
1049
|
+
referenceExemptionCriteria: referencePolicy.exemption,
|
|
1050
|
+
referencePolicySha256: referencePolicy.contentSha256,
|
|
1051
|
+
loadedReferences: [],
|
|
979
1052
|
targetQuestionId,
|
|
980
1053
|
methodLocation: skill?.filePath,
|
|
981
1054
|
executionProfile,
|
|
@@ -1000,6 +1073,17 @@ export default async function developer(pi: ExtensionAPI) {
|
|
|
1000
1073
|
]
|
|
1001
1074
|
: []),
|
|
1002
1075
|
`Reason: ${event.reason}`,
|
|
1076
|
+
`Available references: ${event.availableReferences.join(" | ") || "none"}`,
|
|
1077
|
+
...referencePolicyLines(
|
|
1078
|
+
event.referenceRoutes,
|
|
1079
|
+
event.referenceExemptionCriteria,
|
|
1080
|
+
),
|
|
1081
|
+
`Reference policy SHA-256: ${event.referencePolicySha256 ?? "none"}`,
|
|
1082
|
+
...(skill && event.availableReferences.length > 0
|
|
1083
|
+
? [
|
|
1084
|
+
`Reference contract: choose policy routes by observable trigger, use ${REFERENCE_TOOL} for every reference in each selected route's co-required set, and provide reference_basis for every loaded path; use reference_exemption only when no policy route applies.`,
|
|
1085
|
+
]
|
|
1086
|
+
: []),
|
|
1003
1087
|
`Known evidence: ${event.knownEvidence.length > 0 ? event.knownEvidence.join(" | ") : "none"}`,
|
|
1004
1088
|
`Alternatives reconsidered: ${
|
|
1005
1089
|
event.consideredAlternatives.length > 0
|
|
@@ -1080,6 +1164,15 @@ export default async function developer(pi: ExtensionAPI) {
|
|
|
1080
1164
|
}
|
|
1081
1165
|
text += `\n${detailLine(theme, "revisits", event.targetQuestionId ?? "none")}`;
|
|
1082
1166
|
text += `\n${detailLine(theme, "skill", event.methodLocation ?? "implementation action")}`;
|
|
1167
|
+
if ((event.availableReferences ?? []).length > 0) {
|
|
1168
|
+
text += `\n${detailLine(theme, "references", event.availableReferences.join(", "))}`;
|
|
1169
|
+
}
|
|
1170
|
+
for (const route of event.referenceRoutes ?? []) {
|
|
1171
|
+
text += `\n${detailLine(theme, `reference route ${route.id}`, `${route.question} · ${route.trigger} → ${route.references.join(" + ")} · step=${route.methodStep} · stop=${route.stop} · separate=${route.separateWhen}`)}`;
|
|
1172
|
+
}
|
|
1173
|
+
if (event.referencePolicySha256) {
|
|
1174
|
+
text += `\n${detailLine(theme, "reference policy", event.referencePolicySha256)}`;
|
|
1175
|
+
}
|
|
1083
1176
|
if (event.executionProfile) {
|
|
1084
1177
|
text += `\n${detailLine(theme, "profile", event.executionProfile)}`;
|
|
1085
1178
|
}
|
|
@@ -1095,6 +1188,192 @@ export default async function developer(pi: ExtensionAPI) {
|
|
|
1095
1188
|
},
|
|
1096
1189
|
});
|
|
1097
1190
|
|
|
1191
|
+
const ReferenceParams = Type.Object(
|
|
1192
|
+
{
|
|
1193
|
+
reference_route: Type.String({
|
|
1194
|
+
minLength: 1,
|
|
1195
|
+
maxLength: 128,
|
|
1196
|
+
description:
|
|
1197
|
+
"Exact reference route ID listed by the active Developer skill policy",
|
|
1198
|
+
}),
|
|
1199
|
+
path: Type.String({
|
|
1200
|
+
minLength: 1,
|
|
1201
|
+
maxLength: MAX_ARTIFACT_CHARS,
|
|
1202
|
+
description:
|
|
1203
|
+
"Exact direct skill-relative reference path listed by the active route, such as references/data-driven-design.md",
|
|
1204
|
+
}),
|
|
1205
|
+
reason: Type.String({
|
|
1206
|
+
minLength: 1,
|
|
1207
|
+
maxLength: MAX_EVIDENCE_CHARS,
|
|
1208
|
+
description:
|
|
1209
|
+
"Observable task evidence that makes this reference relevant now",
|
|
1210
|
+
}),
|
|
1211
|
+
},
|
|
1212
|
+
{ additionalProperties: false },
|
|
1213
|
+
);
|
|
1214
|
+
|
|
1215
|
+
pi.registerTool({
|
|
1216
|
+
name: REFERENCE_TOOL,
|
|
1217
|
+
label: "Developer Load Reference",
|
|
1218
|
+
description:
|
|
1219
|
+
"Select one active policy route while loading one reference from its co-required set, recording route ID, exact path, content SHA-256, Source Trace, and trigger evidence.",
|
|
1220
|
+
promptSnippet: "Load an active Developer skill reference with provenance",
|
|
1221
|
+
promptGuidelines: [
|
|
1222
|
+
`Use ${REFERENCE_TOOL}, not an ordinary read, for any reference that supports an active Developer skill judgment.`,
|
|
1223
|
+
`Call ${REFERENCE_TOOL} with a policy reference_route and one not-yet-loaded path from that route's co-required set; state the observable trigger and apply it only at the declared method step. One successful load satisfies an overlapping path for every selected route, so never reload a shared path and provide only one reference_basis entry per distinct path.`,
|
|
1224
|
+
],
|
|
1225
|
+
parameters: ReferenceParams,
|
|
1226
|
+
executionMode: "sequential",
|
|
1227
|
+
renderShell: "self",
|
|
1228
|
+
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
1229
|
+
if (!state.enabled) fail("Developer protocol is off.");
|
|
1230
|
+
const activeRoute = state.activeRoute;
|
|
1231
|
+
if (!activeRoute) fail("There is no active Developer skill route.");
|
|
1232
|
+
if (activeRoute.target === "implementation") {
|
|
1233
|
+
fail("Implementation routes do not load skill references.");
|
|
1234
|
+
}
|
|
1235
|
+
const referenceRouteId = params.reference_route.trim();
|
|
1236
|
+
const path = params.path.trim().replaceAll("\\", "/");
|
|
1237
|
+
const reason = params.reason.trim();
|
|
1238
|
+
if (!referenceRouteId || !path || !reason) {
|
|
1239
|
+
fail(
|
|
1240
|
+
"Reference route, path, and reason must contain non-whitespace text.",
|
|
1241
|
+
);
|
|
1242
|
+
}
|
|
1243
|
+
const skill = availableSkills.get(activeRoute.target);
|
|
1244
|
+
if (!skill || skill.filePath !== activeRoute.methodLocation) {
|
|
1245
|
+
fail(
|
|
1246
|
+
"The active skill source is no longer available at the route's canonical location. Route again against the current Pi resources.",
|
|
1247
|
+
);
|
|
1248
|
+
}
|
|
1249
|
+
const availableReferences =
|
|
1250
|
+
activeRoute.availableReferences.length > 0
|
|
1251
|
+
? activeRoute.availableReferences
|
|
1252
|
+
: await skillReferencePaths(skill);
|
|
1253
|
+
let referenceRoutes = activeRoute.referenceRoutes;
|
|
1254
|
+
if (referenceRoutes.length === 0) {
|
|
1255
|
+
const policy = await loadSkillReferencePolicy(
|
|
1256
|
+
skill,
|
|
1257
|
+
availableReferences,
|
|
1258
|
+
);
|
|
1259
|
+
referenceRoutes = policy.routes;
|
|
1260
|
+
}
|
|
1261
|
+
const policyRoute = referenceRoutes.find(
|
|
1262
|
+
(candidate) => candidate.id === referenceRouteId,
|
|
1263
|
+
);
|
|
1264
|
+
if (!policyRoute) {
|
|
1265
|
+
fail(
|
|
1266
|
+
`Unknown reference route ${referenceRouteId}. Available routes: ${referenceRoutes.map((route) => route.id).join(", ") || "none"}.`,
|
|
1267
|
+
);
|
|
1268
|
+
}
|
|
1269
|
+
if (!policyRoute.references.includes(path)) {
|
|
1270
|
+
fail(
|
|
1271
|
+
`Reference ${path} is not part of route ${policyRoute.id}. Co-required route references: ${policyRoute.references.join(", ")}.`,
|
|
1272
|
+
);
|
|
1273
|
+
}
|
|
1274
|
+
const loadedPaths = new Set(
|
|
1275
|
+
activeRoute.loadedReferences.map((reference) => reference.path),
|
|
1276
|
+
);
|
|
1277
|
+
if (loadedPaths.has(path)) {
|
|
1278
|
+
fail(
|
|
1279
|
+
`Reference ${path} is already loaded. One successful load satisfies overlapping route membership; select ${policyRoute.id} by loading one of its not-yet-loaded paths and keep one reference_basis entry per distinct path.`,
|
|
1280
|
+
);
|
|
1281
|
+
}
|
|
1282
|
+
if (policyRoute.readOrder === "listed") {
|
|
1283
|
+
const pathIndex = policyRoute.references.indexOf(path);
|
|
1284
|
+
const missingEarlierReferences = policyRoute.references
|
|
1285
|
+
.slice(0, pathIndex)
|
|
1286
|
+
.filter((reference) => !loadedPaths.has(reference));
|
|
1287
|
+
if (missingEarlierReferences.length > 0) {
|
|
1288
|
+
fail(
|
|
1289
|
+
`Reference route ${policyRoute.id} declares listed read order. Load these earlier route references first: ${missingEarlierReferences.join(", ")}.`,
|
|
1290
|
+
);
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
const reference = await loadSkillReference(skill, path);
|
|
1294
|
+
const event: ReferenceLoadEvent = {
|
|
1295
|
+
protocol: PROTOCOL,
|
|
1296
|
+
kind: "reference-load",
|
|
1297
|
+
routeId: activeRoute.routeId,
|
|
1298
|
+
target: activeRoute.target,
|
|
1299
|
+
path: reference.path,
|
|
1300
|
+
reason,
|
|
1301
|
+
contentSha256: reference.contentSha256,
|
|
1302
|
+
sourceTrace: reference.sourceTrace,
|
|
1303
|
+
referenceRouteIds: [policyRoute.id],
|
|
1304
|
+
};
|
|
1305
|
+
if (!canApplyDeveloperEvent(state, event)) {
|
|
1306
|
+
fail(
|
|
1307
|
+
"Developer machine guard rejected the reference load for the current active route.",
|
|
1308
|
+
);
|
|
1309
|
+
}
|
|
1310
|
+
const response = [
|
|
1311
|
+
`Route ID: ${event.routeId}`,
|
|
1312
|
+
`Target: ${event.target}`,
|
|
1313
|
+
`Reference policy route: ${policyRoute.id}`,
|
|
1314
|
+
`Route question: ${policyRoute.question}`,
|
|
1315
|
+
`Route trigger: ${policyRoute.trigger}`,
|
|
1316
|
+
`Skill method step refined: ${policyRoute.methodStep}`,
|
|
1317
|
+
`Co-required references: ${policyRoute.references.join(" + ")}`,
|
|
1318
|
+
`Read order: ${policyRoute.readOrder}`,
|
|
1319
|
+
`Expected application artifacts: ${policyRoute.artifacts.join(" | ")}`,
|
|
1320
|
+
`Route stop: ${policyRoute.stop}`,
|
|
1321
|
+
`Separate or hand off when: ${policyRoute.separateWhen}`,
|
|
1322
|
+
`Reference: ${event.path}`,
|
|
1323
|
+
`Reference SHA-256: ${event.contentSha256}`,
|
|
1324
|
+
`Source Trace: ${event.sourceTrace ? "present in loaded content and recorded in tool details" : "not declared by this reference"}`,
|
|
1325
|
+
`Trigger evidence: ${event.reason}`,
|
|
1326
|
+
"",
|
|
1327
|
+
"<developer-reference>",
|
|
1328
|
+
reference.content,
|
|
1329
|
+
"</developer-reference>",
|
|
1330
|
+
].join("\n");
|
|
1331
|
+
ensureSafeToolText(response, "Developer reference result");
|
|
1332
|
+
state = applyDeveloperEvent(state, event);
|
|
1333
|
+
syncProtocolTools();
|
|
1334
|
+
refreshUI(ctx);
|
|
1335
|
+
return textResult(response, event);
|
|
1336
|
+
},
|
|
1337
|
+
renderCall(args, theme, context) {
|
|
1338
|
+
const path =
|
|
1339
|
+
typeof args.path === "string" && args.path.length > 0 ? args.path : "…";
|
|
1340
|
+
return reusableText(
|
|
1341
|
+
`${theme.fg("toolTitle", theme.bold(REFERENCE_TOOL))} ${theme.fg("accent", path)}`,
|
|
1342
|
+
context.lastComponent,
|
|
1343
|
+
);
|
|
1344
|
+
},
|
|
1345
|
+
renderResult(result, { expanded, isPartial }, theme, context) {
|
|
1346
|
+
if (isPartial) {
|
|
1347
|
+
return reusableText(
|
|
1348
|
+
theme.fg("warning", "loading Developer reference…"),
|
|
1349
|
+
context.lastComponent,
|
|
1350
|
+
);
|
|
1351
|
+
}
|
|
1352
|
+
if (context.isError) {
|
|
1353
|
+
return reusableText(
|
|
1354
|
+
theme.fg("error", resultText(result) || "Developer reference failed"),
|
|
1355
|
+
context.lastComponent,
|
|
1356
|
+
);
|
|
1357
|
+
}
|
|
1358
|
+
const event = result.details as ReferenceLoadEvent | undefined;
|
|
1359
|
+
let text = theme.fg(
|
|
1360
|
+
"success",
|
|
1361
|
+
`loaded ${event?.path ?? "Developer reference"}`,
|
|
1362
|
+
);
|
|
1363
|
+
if (expanded && event) {
|
|
1364
|
+
text += `\n${detailLine(theme, "route", event.routeId, "text")}`;
|
|
1365
|
+
text += `\n${detailLine(theme, "target", event.target, "accent")}`;
|
|
1366
|
+
text += `\n${detailLine(theme, "reference route", event.referenceRouteIds.join(", ") || "legacy", "accent")}`;
|
|
1367
|
+
text += `\n${detailLine(theme, "sha256", event.contentSha256)}`;
|
|
1368
|
+
text += `\n${detailLine(theme, "trigger", event.reason, "text")}`;
|
|
1369
|
+
text += `\n${detailLine(theme, "source trace", event.sourceTrace || "not declared", event.sourceTrace ? "muted" : "warning")}`;
|
|
1370
|
+
}
|
|
1371
|
+
if (!expanded && event)
|
|
1372
|
+
text += ` · ${keyHint("app.tools.expand", "provenance")}`;
|
|
1373
|
+
return reusableText(text, context.lastComponent);
|
|
1374
|
+
},
|
|
1375
|
+
});
|
|
1376
|
+
|
|
1098
1377
|
const ChoiceResponseOptionParam = Type.Object(
|
|
1099
1378
|
{
|
|
1100
1379
|
value: Type.String({
|
|
@@ -1204,6 +1483,54 @@ export default async function developer(pi: ExtensionAPI) {
|
|
|
1204
1483
|
},
|
|
1205
1484
|
{ additionalProperties: false },
|
|
1206
1485
|
);
|
|
1486
|
+
const ReferenceBasisParam = Type.Object(
|
|
1487
|
+
{
|
|
1488
|
+
path: Type.String({
|
|
1489
|
+
minLength: 1,
|
|
1490
|
+
maxLength: MAX_ARTIFACT_CHARS,
|
|
1491
|
+
description: `A reference path successfully loaded through ${REFERENCE_TOOL} during this route`,
|
|
1492
|
+
}),
|
|
1493
|
+
trigger: Type.String({
|
|
1494
|
+
minLength: 1,
|
|
1495
|
+
maxLength: MAX_EVIDENCE_CHARS,
|
|
1496
|
+
description:
|
|
1497
|
+
"Observable input, code, state, or requirement condition that made the reference applicable",
|
|
1498
|
+
}),
|
|
1499
|
+
applied_rule: Type.String({
|
|
1500
|
+
minLength: 1,
|
|
1501
|
+
maxLength: MAX_EVIDENCE_CHARS,
|
|
1502
|
+
description:
|
|
1503
|
+
"The concrete derivation, diagnostic, or decision rule taken from the loaded reference",
|
|
1504
|
+
}),
|
|
1505
|
+
artifact: Type.String({
|
|
1506
|
+
minLength: 1,
|
|
1507
|
+
maxLength: MAX_ARTIFACT_CHARS,
|
|
1508
|
+
description:
|
|
1509
|
+
"The result table, model, interface, trace, decision, or check produced by applying that rule",
|
|
1510
|
+
}),
|
|
1511
|
+
},
|
|
1512
|
+
{ additionalProperties: false },
|
|
1513
|
+
);
|
|
1514
|
+
const ReferenceExemptionParam = Type.Object(
|
|
1515
|
+
{
|
|
1516
|
+
reason: Type.String({
|
|
1517
|
+
minLength: 1,
|
|
1518
|
+
maxLength: MAX_EVIDENCE_CHARS,
|
|
1519
|
+
description:
|
|
1520
|
+
"Why none of the active skill's available references can add useful judgment for this resolved local question",
|
|
1521
|
+
}),
|
|
1522
|
+
evidence: Type.Array(
|
|
1523
|
+
Type.String({ minLength: 1, maxLength: MAX_EVIDENCE_CHARS }),
|
|
1524
|
+
{
|
|
1525
|
+
minItems: 1,
|
|
1526
|
+
maxItems: 8,
|
|
1527
|
+
description:
|
|
1528
|
+
"Concrete facts supporting the exemption rather than a convenience claim",
|
|
1529
|
+
},
|
|
1530
|
+
),
|
|
1531
|
+
},
|
|
1532
|
+
{ additionalProperties: false },
|
|
1533
|
+
);
|
|
1207
1534
|
const JudgmentParams = Type.Object({
|
|
1208
1535
|
route_id: Type.String({
|
|
1209
1536
|
minLength: 1,
|
|
@@ -1226,6 +1553,14 @@ export default async function developer(pi: ExtensionAPI) {
|
|
|
1226
1553
|
maxItems: 20,
|
|
1227
1554
|
description: "Evidence supporting the judgment or blocker",
|
|
1228
1555
|
}),
|
|
1556
|
+
reference_basis: Type.Optional(
|
|
1557
|
+
Type.Array(ReferenceBasisParam, {
|
|
1558
|
+
maxItems: 12,
|
|
1559
|
+
description:
|
|
1560
|
+
"Loaded references linked to their trigger, applied rule, and resulting judgment artifact",
|
|
1561
|
+
}),
|
|
1562
|
+
),
|
|
1563
|
+
reference_exemption: Type.Optional(ReferenceExemptionParam),
|
|
1229
1564
|
open_questions: Type.Optional(
|
|
1230
1565
|
Type.Array(OpenQuestionParam, {
|
|
1231
1566
|
maxItems: 10,
|
|
@@ -1257,6 +1592,7 @@ export default async function developer(pi: ExtensionAPI) {
|
|
|
1257
1592
|
promptGuidelines: [
|
|
1258
1593
|
`Use ${JUDGMENT_TOOL} with the exact active Developer route ID.`,
|
|
1259
1594
|
`Use ${JUDGMENT_TOOL} result as Markdown and preserve the routed skill's inspectable tables, diagrams, matrices, timelines, and code blocks instead of reducing them to prose.`,
|
|
1595
|
+
`For a resolved skill route with available references, use ${JUDGMENT_TOOL} reference_basis to connect each relied-on ${REFERENCE_TOOL} load to its trigger, applied rule, and resulting artifact, or use reference_exemption when no reference is relevant. Never provide both.`,
|
|
1260
1596
|
`Use ${JUDGMENT_TOOL} open_questions with resolution_owner, gate, and resolution_criteria. User-owned gated questions require explanatory context before controls; for finite required decisions, add a choice-form response_spec with one field per decision. Use question_updates whenever current evidence settles or changes any existing pending question.`,
|
|
1261
1597
|
`Do not use ${JUDGMENT_TOOL} with resolved, not-applicable, or blocked status without at least one concrete basis.`,
|
|
1262
1598
|
],
|
|
@@ -1291,6 +1627,34 @@ export default async function developer(pi: ExtensionAPI) {
|
|
|
1291
1627
|
if (params.route_id !== activeRoute.routeId) {
|
|
1292
1628
|
fail(`Route ID mismatch. Active route is ${activeRoute.routeId}.`);
|
|
1293
1629
|
}
|
|
1630
|
+
const activeSkill =
|
|
1631
|
+
activeRoute.target === "implementation"
|
|
1632
|
+
? undefined
|
|
1633
|
+
: availableSkills.get(activeRoute.target);
|
|
1634
|
+
let availableReferences: string[] = [];
|
|
1635
|
+
if (activeRoute.target !== "implementation") {
|
|
1636
|
+
if (activeRoute.availableReferences.length > 0) {
|
|
1637
|
+
availableReferences = activeRoute.availableReferences;
|
|
1638
|
+
} else if (
|
|
1639
|
+
activeSkill &&
|
|
1640
|
+
activeSkill.filePath === activeRoute.methodLocation
|
|
1641
|
+
) {
|
|
1642
|
+
availableReferences = await skillReferencePaths(activeSkill);
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
let referenceRoutes = activeRoute.referenceRoutes;
|
|
1646
|
+
if (
|
|
1647
|
+
activeRoute.target !== "implementation" &&
|
|
1648
|
+
referenceRoutes.length === 0 &&
|
|
1649
|
+
activeSkill &&
|
|
1650
|
+
activeSkill.filePath === activeRoute.methodLocation
|
|
1651
|
+
) {
|
|
1652
|
+
const policy = await loadSkillReferencePolicy(
|
|
1653
|
+
activeSkill,
|
|
1654
|
+
availableReferences,
|
|
1655
|
+
);
|
|
1656
|
+
referenceRoutes = policy.routes;
|
|
1657
|
+
}
|
|
1294
1658
|
|
|
1295
1659
|
const basis = params.basis.map((item) => item.trim()).filter(Boolean);
|
|
1296
1660
|
const result = params.result.trim();
|
|
@@ -1299,6 +1663,131 @@ export default async function developer(pi: ExtensionAPI) {
|
|
|
1299
1663
|
fail(`${params.status} judgments require at least one concrete basis.`);
|
|
1300
1664
|
}
|
|
1301
1665
|
|
|
1666
|
+
const referenceBasisInput = (params.reference_basis ?? []).map(
|
|
1667
|
+
(item) => ({
|
|
1668
|
+
path: item.path.trim().replaceAll("\\", "/"),
|
|
1669
|
+
trigger: item.trigger.trim(),
|
|
1670
|
+
appliedRule: item.applied_rule.trim(),
|
|
1671
|
+
artifact: item.artifact.trim(),
|
|
1672
|
+
}),
|
|
1673
|
+
);
|
|
1674
|
+
const referenceExemptionInput = params.reference_exemption
|
|
1675
|
+
? {
|
|
1676
|
+
reason: params.reference_exemption.reason.trim(),
|
|
1677
|
+
evidence: params.reference_exemption.evidence
|
|
1678
|
+
.map((item) => item.trim())
|
|
1679
|
+
.filter(Boolean),
|
|
1680
|
+
}
|
|
1681
|
+
: undefined;
|
|
1682
|
+
if (referenceBasisInput.length > 0 && referenceExemptionInput) {
|
|
1683
|
+
fail("Use reference_basis or reference_exemption, never both.");
|
|
1684
|
+
}
|
|
1685
|
+
if (
|
|
1686
|
+
new Set(referenceBasisInput.map((item) => item.path)).size !==
|
|
1687
|
+
referenceBasisInput.length
|
|
1688
|
+
) {
|
|
1689
|
+
fail("Each reference_basis path must be unique within a judgment.");
|
|
1690
|
+
}
|
|
1691
|
+
if (
|
|
1692
|
+
activeRoute.target === "implementation" &&
|
|
1693
|
+
(referenceBasisInput.length > 0 || referenceExemptionInput)
|
|
1694
|
+
) {
|
|
1695
|
+
fail(
|
|
1696
|
+
"Implementation judgments do not accept skill reference evidence.",
|
|
1697
|
+
);
|
|
1698
|
+
}
|
|
1699
|
+
if (referenceExemptionInput && params.status !== "resolved") {
|
|
1700
|
+
fail(
|
|
1701
|
+
"reference_exemption is valid only for a resolved skill judgment.",
|
|
1702
|
+
);
|
|
1703
|
+
}
|
|
1704
|
+
if (referenceExemptionInput && availableReferences.length === 0) {
|
|
1705
|
+
fail(
|
|
1706
|
+
"reference_exemption is unnecessary because the active skill has no available references.",
|
|
1707
|
+
);
|
|
1708
|
+
}
|
|
1709
|
+
if (referenceExemptionInput && activeRoute.loadedReferences.length > 0) {
|
|
1710
|
+
fail(
|
|
1711
|
+
"reference_exemption is valid only when no routed reference was loaded during this route.",
|
|
1712
|
+
);
|
|
1713
|
+
}
|
|
1714
|
+
if (
|
|
1715
|
+
params.status === "resolved" &&
|
|
1716
|
+
availableReferences.length > 0 &&
|
|
1717
|
+
referenceBasisInput.length === 0 &&
|
|
1718
|
+
!referenceExemptionInput
|
|
1719
|
+
) {
|
|
1720
|
+
fail(
|
|
1721
|
+
`A resolved ${activeRoute.target} judgment must provide reference_basis for references loaded through ${REFERENCE_TOOL}, or a concrete reference_exemption.`,
|
|
1722
|
+
);
|
|
1723
|
+
}
|
|
1724
|
+
const referenceBasisPaths = new Set(
|
|
1725
|
+
referenceBasisInput.map((item) => item.path),
|
|
1726
|
+
);
|
|
1727
|
+
const loadedReferences = new Map(
|
|
1728
|
+
activeRoute.loadedReferences.map((reference) => [
|
|
1729
|
+
reference.path,
|
|
1730
|
+
reference,
|
|
1731
|
+
]),
|
|
1732
|
+
);
|
|
1733
|
+
const loadedPaths = new Set(loadedReferences.keys());
|
|
1734
|
+
const basisMissingForLoaded = [...loadedPaths].filter(
|
|
1735
|
+
(path) => !referenceBasisPaths.has(path),
|
|
1736
|
+
);
|
|
1737
|
+
if (basisMissingForLoaded.length > 0) {
|
|
1738
|
+
fail(
|
|
1739
|
+
`Every loaded reference must have a reference_basis entry: ${basisMissingForLoaded.join(", ")}.`,
|
|
1740
|
+
);
|
|
1741
|
+
}
|
|
1742
|
+
const selectedReferenceRouteIds = new Set(
|
|
1743
|
+
activeRoute.loadedReferences.flatMap(
|
|
1744
|
+
(reference) => reference.referenceRouteIds,
|
|
1745
|
+
),
|
|
1746
|
+
);
|
|
1747
|
+
for (const referenceRouteId of selectedReferenceRouteIds) {
|
|
1748
|
+
const route = referenceRoutes.find(
|
|
1749
|
+
(candidate) => candidate.id === referenceRouteId,
|
|
1750
|
+
);
|
|
1751
|
+
if (!route) {
|
|
1752
|
+
fail(
|
|
1753
|
+
`Loaded reference selected unknown policy route ${referenceRouteId}; route again against the current skill policy.`,
|
|
1754
|
+
);
|
|
1755
|
+
}
|
|
1756
|
+
const missingRouteReferences = route.references.filter(
|
|
1757
|
+
(path) => !loadedPaths.has(path),
|
|
1758
|
+
);
|
|
1759
|
+
if (missingRouteReferences.length > 0) {
|
|
1760
|
+
fail(
|
|
1761
|
+
`Reference route ${route.id} is incomplete. Load its co-required references before judgment: ${missingRouteReferences.join(", ")}.`,
|
|
1762
|
+
);
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1765
|
+
for (const loaded of activeRoute.loadedReferences) {
|
|
1766
|
+
for (const referenceRouteId of loaded.referenceRouteIds) {
|
|
1767
|
+
const route = referenceRoutes.find(
|
|
1768
|
+
(candidate) => candidate.id === referenceRouteId,
|
|
1769
|
+
);
|
|
1770
|
+
if (route && !route.references.includes(loaded.path)) {
|
|
1771
|
+
fail(
|
|
1772
|
+
`Reference ${loaded.path} is not part of its recorded policy route ${route.id}.`,
|
|
1773
|
+
);
|
|
1774
|
+
}
|
|
1775
|
+
}
|
|
1776
|
+
}
|
|
1777
|
+
const referenceBasis: ReferenceBasis[] = referenceBasisInput.map(
|
|
1778
|
+
(item) => {
|
|
1779
|
+
const loaded = loadedReferences.get(item.path);
|
|
1780
|
+
if (!loaded) {
|
|
1781
|
+
fail(
|
|
1782
|
+
`reference_basis path ${item.path} was not successfully loaded through ${REFERENCE_TOOL} during this route.`,
|
|
1783
|
+
);
|
|
1784
|
+
}
|
|
1785
|
+
return { ...loaded, ...item };
|
|
1786
|
+
},
|
|
1787
|
+
);
|
|
1788
|
+
const referenceExemption: ReferenceExemption | undefined =
|
|
1789
|
+
referenceExemptionInput;
|
|
1790
|
+
|
|
1302
1791
|
const openedQuestions = buildOpenedQuestions(
|
|
1303
1792
|
params.open_questions ?? [],
|
|
1304
1793
|
state,
|
|
@@ -1399,6 +1888,8 @@ export default async function developer(pi: ExtensionAPI) {
|
|
|
1399
1888
|
status: params.status,
|
|
1400
1889
|
result,
|
|
1401
1890
|
basis,
|
|
1891
|
+
referenceBasis,
|
|
1892
|
+
referenceExemption,
|
|
1402
1893
|
openedQuestions,
|
|
1403
1894
|
questionUpdates,
|
|
1404
1895
|
artifacts: (params.artifacts ?? [])
|
|
@@ -1796,6 +2287,30 @@ export default async function developer(pi: ExtensionAPI) {
|
|
|
1796
2287
|
});
|
|
1797
2288
|
|
|
1798
2289
|
pi.on("tool_call", (event) => {
|
|
2290
|
+
if (
|
|
2291
|
+
state.enabled &&
|
|
2292
|
+
event.toolName === "read" &&
|
|
2293
|
+
state.activeRoute?.target !== "implementation"
|
|
2294
|
+
) {
|
|
2295
|
+
const activeSkill = state.activeRoute
|
|
2296
|
+
? availableSkills.get(state.activeRoute.target)
|
|
2297
|
+
: undefined;
|
|
2298
|
+
const requestedPath = event.input.path;
|
|
2299
|
+
if (
|
|
2300
|
+
activeSkill &&
|
|
2301
|
+
typeof requestedPath === "string" &&
|
|
2302
|
+
isWithinRoot(
|
|
2303
|
+
resolve(activeSkill.baseDir, "references"),
|
|
2304
|
+
resolve(requestedPath),
|
|
2305
|
+
)
|
|
2306
|
+
) {
|
|
2307
|
+
return {
|
|
2308
|
+
block: true,
|
|
2309
|
+
reason: `Developer skill references must be loaded through ${REFERENCE_TOOL} with a policy route so provenance and application remain auditable.`,
|
|
2310
|
+
};
|
|
2311
|
+
}
|
|
2312
|
+
}
|
|
2313
|
+
|
|
1799
2314
|
const capability = builtinControlledToolCapabilities(pi.getAllTools()).get(
|
|
1800
2315
|
event.toolName,
|
|
1801
2316
|
);
|