@akagilnc/pi-workflow-roles 0.1.2521 → 0.1.2653-next.71811ca
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 +129 -39
- package/README.zh-CN.md +128 -39
- package/dist/activation-ledger-topology.js +1 -1
- package/dist/archivist-record-entry.js +47 -26
- package/dist/auditor-dossier-tool.js +3 -1
- package/dist/compliance-transport.js +17 -26
- package/dist/doctor-contracts.js +1 -2
- package/dist/dossier-resolution.js +4 -8
- package/dist/engine-detour-tool.js +33 -8
- package/dist/engine-detour.js +1 -0
- package/dist/evidence-child-executor.js +277 -424
- package/dist/host-contracts.js +22 -0
- package/dist/institutional-resolution.js +88 -0
- package/dist/merger-contracts.js +1 -2
- package/dist/navigator-attendance.js +16 -0
- package/dist/notary-contracts.js +2 -3
- package/dist/package-contracts/fixer-output.js +1 -2
- package/dist/packaged-role-registry.js +102 -9
- package/dist/pi/in-process-session.js +671 -0
- package/dist/public-cli/config.js +7 -5
- package/dist/public-cli/main.js +3767 -4113
- package/dist/reviewer-child-executor.js +6 -1
- package/dist/session-opening-materials.js +10 -38
- package/dist/sitian-appender.js +146 -0
- package/dist/sitian-contracts.js +21 -0
- package/dist/sitian-facade.js +15 -0
- package/dist/sitian-reader.js +52 -0
- package/extensions/role-runtime.ts +9 -7
- package/package.json +5 -5
- package/resources/engines/cursor.md +12 -26
- package/resources/engines/hermes.md +2 -23
- package/resources/engines/opus.md +1 -16
- package/resources/navigator-route-playbook.md +2 -2
- package/scripts/build-package.mjs +0 -1
- package/souls/quality-law.md +1 -1
- package/src/activation-ledger-topology.ts +1 -1
- package/src/archivist-record-entry.ts +57 -45
- package/src/auditor-dossier-tool.ts +4 -2
- package/src/canonical-skill-binding.ts +37 -27
- package/src/collector-role.ts +11 -20
- package/src/collector-tool-schemas.ts +1 -4
- package/src/compliance-transport.ts +19 -44
- package/src/doctor-auditor.ts +1 -5
- package/src/doctor-contracts.ts +1 -4
- package/src/doctor-role.ts +31 -8
- package/src/dossier-resolution.ts +8 -8
- package/src/engine-detour-tool.ts +48 -19
- package/src/engine-detour.ts +3 -0
- package/src/evidence-child-executor.ts +277 -485
- package/src/factory-board.ts +3 -3
- package/src/gatekeeper-role.ts +21 -10
- package/src/host-contracts.ts +386 -0
- package/src/in-process-session.ts +1 -115
- package/src/institutional-resolution.ts +135 -0
- package/src/judge-auditor.ts +1 -5
- package/src/judge-role.ts +37 -51
- package/src/merger-contracts.ts +1 -4
- package/src/merger-role.ts +7 -11
- package/src/navigator-attendance.ts +20 -1
- package/src/notary-contracts.ts +8 -11
- package/src/notary-role.ts +7 -23
- package/src/notary-source-run.ts +4 -3
- package/src/package-contracts/fixer-output.ts +1 -4
- package/src/package-resources/method-skill-binding.ts +10 -34
- package/src/packaged-role-registry.ts +122 -12
- package/src/pi/adapter.ts +192 -0
- package/src/pi/durable-principal.ts +98 -0
- package/src/pi/in-process-session.ts +819 -0
- package/src/pi/known-failure.ts +77 -0
- package/src/pi/pi-normalization.ts +284 -0
- package/src/pi/role-turn-host.ts +459 -0
- package/src/public-cli/auto-resume.ts +42 -44
- package/src/public-cli/cli.ts +171 -257
- package/src/public-cli/coder-run.ts +115 -433
- package/src/public-cli/collector-run.ts +62 -314
- package/src/public-cli/config.ts +7 -5
- package/src/public-cli/doctor-run.ts +41 -52
- package/src/public-cli/fixer-run.ts +122 -427
- package/src/public-cli/invocation.ts +287 -96
- package/src/public-cli/judge-run.ts +92 -422
- package/src/public-cli/merger-run.ts +167 -429
- package/src/public-cli/notary-run.ts +41 -47
- package/src/public-cli/option-definitions.ts +136 -3
- package/src/public-cli/post-admission.ts +497 -0
- package/src/public-cli/public-run-credentials.ts +4 -4
- package/src/public-cli/reviewer-dispatch-rejection.ts +106 -0
- package/src/public-cli/reviewer-run.ts +132 -449
- package/src/public-cli/run-lifecycle.ts +339 -327
- package/src/public-cli/settlement.ts +251 -134
- package/src/public-cli/terminal.ts +3 -9
- package/src/public-cli/turn-request.ts +87 -0
- package/src/reviewer-child-executor.ts +9 -1
- package/src/reviewer-role.ts +11 -20
- package/src/role-runtime.ts +129 -78
- package/src/session-opening-materials.ts +25 -39
- package/src/sitian-appender.ts +171 -0
- package/src/sitian-contracts.ts +106 -0
- package/src/sitian-facade.ts +18 -0
- package/src/sitian-reader.ts +62 -0
- package/src/worker-role.ts +42 -40
- package/src/worker-submission-gates.ts +33 -0
- package/dist/archivist-role-run-coordinates.js +0 -28
- package/dist/package-contracts/terminating-infrastructure.js +0 -90
- package/resources/engines/sonnet.md +0 -27
- package/resources/engines/zcode.md +0 -63
- package/src/archivist-role-run-coordinates.ts +0 -45
- package/src/package-contracts/terminating-infrastructure.ts +0 -132
- package/src/public-cli/explicit-internal.ts +0 -427
- package/src/public-cli/one-shot-dispatch.ts +0 -300
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import type { RoleTurnModelConfig } from "./host-contracts.ts";
|
|
4
|
+
import { seatModelOnly, type PublicCliConfig } from "./public-cli/config.ts";
|
|
5
|
+
|
|
6
|
+
export const INSTITUTIONAL_RESOLUTION_FILE = "institutional-resolution.json" as const;
|
|
7
|
+
|
|
8
|
+
/** Non-secret per-seat model selection — alias of the single host-neutral RoleTurnModelConfig. */
|
|
9
|
+
export type InstitutionalSeatSelection = RoleTurnModelConfig;
|
|
10
|
+
|
|
11
|
+
export type InstitutionalResolutionPage = {
|
|
12
|
+
readonly version: 1;
|
|
13
|
+
readonly seats: {
|
|
14
|
+
readonly gatekeeper?: InstitutionalSeatSelection;
|
|
15
|
+
readonly inspector?: InstitutionalSeatSelection;
|
|
16
|
+
readonly notary?: InstitutionalSeatSelection;
|
|
17
|
+
readonly auditor?: InstitutionalSeatSelection;
|
|
18
|
+
readonly evidenceChild?: InstitutionalSeatSelection;
|
|
19
|
+
readonly [seat: string]: InstitutionalSeatSelection | undefined;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export class InstitutionalResolutionError extends Error {
|
|
24
|
+
constructor(message: string, options?: ErrorOptions) {
|
|
25
|
+
super(message, options);
|
|
26
|
+
this.name = "InstitutionalResolutionError";
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function cleanSelection(model?: { provider?: string; model?: string; thinking?: string }): InstitutionalSeatSelection | undefined {
|
|
31
|
+
if (model === undefined) return undefined;
|
|
32
|
+
if (typeof model.provider !== "string" || typeof model.model !== "string") return undefined;
|
|
33
|
+
return {
|
|
34
|
+
provider: model.provider,
|
|
35
|
+
model: model.model,
|
|
36
|
+
...(model.thinking === undefined ? {} : { thinking: model.thinking }),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Resolve effective per-seat institutional selections (#518 §2 Hop 1):
|
|
42
|
+
* - gatekeeper / inspector / notary: seat override > gatekeeper override > parent effective
|
|
43
|
+
* - auditor / evidenceChild: parent effective
|
|
44
|
+
*/
|
|
45
|
+
export function resolveInstitutionalSeatSelections(
|
|
46
|
+
config: PublicCliConfig,
|
|
47
|
+
parentEffectiveModel?: { provider: string; model: string; thinking?: string },
|
|
48
|
+
): InstitutionalResolutionPage {
|
|
49
|
+
const parentSelection = cleanSelection(parentEffectiveModel);
|
|
50
|
+
|
|
51
|
+
const ownInspector = cleanSelection(seatModelOnly(config.seats?.inspector));
|
|
52
|
+
const ownNotary = cleanSelection(seatModelOnly(config.seats?.notary));
|
|
53
|
+
const ownGatekeeper = cleanSelection(seatModelOnly(config.seats?.gatekeeper));
|
|
54
|
+
|
|
55
|
+
const gatekeeper = ownGatekeeper ?? parentSelection;
|
|
56
|
+
const inspector = ownInspector ?? ownGatekeeper ?? parentSelection;
|
|
57
|
+
const notary = ownNotary ?? ownGatekeeper ?? parentSelection;
|
|
58
|
+
const auditor = parentSelection;
|
|
59
|
+
const evidenceChild = parentSelection;
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
version: 1,
|
|
63
|
+
seats: {
|
|
64
|
+
...(gatekeeper === undefined ? {} : { gatekeeper }),
|
|
65
|
+
...(inspector === undefined ? {} : { inspector }),
|
|
66
|
+
...(notary === undefined ? {} : { notary }),
|
|
67
|
+
...(auditor === undefined ? {} : { auditor }),
|
|
68
|
+
...(evidenceChild === undefined ? {} : { evidenceChild }),
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Persist institutional resolution page to run directory (#518 §2 Hop 2).
|
|
75
|
+
* Rewritten on each dispatch and resume. Zero secrets inside page.
|
|
76
|
+
*/
|
|
77
|
+
export async function writeInstitutionalResolutionPage(
|
|
78
|
+
runDirectory: string,
|
|
79
|
+
page: InstitutionalResolutionPage,
|
|
80
|
+
): Promise<void> {
|
|
81
|
+
const filePath = join(runDirectory, INSTITUTIONAL_RESOLUTION_FILE);
|
|
82
|
+
await writeFile(filePath, `${JSON.stringify(page, null, 2)}\n`, "utf8");
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Read institutional seat selection from run directory (#518 §2 Hop 3).
|
|
87
|
+
* Fails loud if page is missing, corrupted, or does not contain a resolution for the seat.
|
|
88
|
+
*/
|
|
89
|
+
export async function readInstitutionalSeatSelection(
|
|
90
|
+
runDirectory: string,
|
|
91
|
+
seat: string,
|
|
92
|
+
): Promise<InstitutionalSeatSelection> {
|
|
93
|
+
const filePath = join(runDirectory, INSTITUTIONAL_RESOLUTION_FILE);
|
|
94
|
+
let raw: string;
|
|
95
|
+
try {
|
|
96
|
+
raw = await readFile(filePath, "utf8");
|
|
97
|
+
} catch (error) {
|
|
98
|
+
throw new InstitutionalResolutionError(
|
|
99
|
+
`institutional resolution page is missing at ${filePath}`,
|
|
100
|
+
{ cause: error },
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
let page: unknown;
|
|
105
|
+
try {
|
|
106
|
+
page = JSON.parse(raw);
|
|
107
|
+
} catch (error) {
|
|
108
|
+
throw new InstitutionalResolutionError(
|
|
109
|
+
`institutional resolution page is corrupted at ${filePath}`,
|
|
110
|
+
{ cause: error },
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (typeof page !== "object" || page === null || (page as { version?: unknown }).version !== 1) {
|
|
115
|
+
throw new InstitutionalResolutionError(
|
|
116
|
+
`institutional resolution page format is invalid at ${filePath}`,
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const seats = (page as { seats?: Record<string, unknown> }).seats;
|
|
121
|
+
if (typeof seats !== "object" || seats === null) {
|
|
122
|
+
throw new InstitutionalResolutionError(
|
|
123
|
+
`institutional resolution page missing seats object at ${filePath}`,
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const selection = cleanSelection(seats[seat] as InstitutionalSeatSelection | undefined);
|
|
128
|
+
if (selection === undefined) {
|
|
129
|
+
throw new InstitutionalResolutionError(
|
|
130
|
+
`institutional resolution page has no resolution for seat "${seat}" at ${filePath}`,
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return selection;
|
|
135
|
+
}
|
package/src/judge-auditor.ts
CHANGED
|
@@ -3,7 +3,6 @@ import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
|
3
3
|
import {
|
|
4
4
|
createComplianceDecisionTool,
|
|
5
5
|
runComplianceAudit,
|
|
6
|
-
type ComplianceCompletion,
|
|
7
6
|
type ComplianceDecision,
|
|
8
7
|
} from "./compliance-transport.ts";
|
|
9
8
|
import { auditorRunDirectory } from "./auditor-dossier-tool.ts";
|
|
@@ -32,9 +31,7 @@ const auditDecisionTool = createComplianceDecisionTool(
|
|
|
32
31
|
* parent-session books. Public CLI injects AK_ROLE_RUN_DIR; bare Pi (ADR 0052)
|
|
33
32
|
* proceeds without that pointer and self-locates per soul.
|
|
34
33
|
*/
|
|
35
|
-
export function createPiJudgeAuditor(
|
|
36
|
-
runCompletion?: ComplianceCompletion,
|
|
37
|
-
): (options: JudgeAuditOptions) => Promise<ComplianceDecision> {
|
|
34
|
+
export function createPiJudgeAuditor(): (options: JudgeAuditOptions) => Promise<ComplianceDecision> {
|
|
38
35
|
return async (options) => {
|
|
39
36
|
const dossier = resolveAuditDossier();
|
|
40
37
|
requireAuditMaterials(dossier);
|
|
@@ -46,7 +43,6 @@ export function createPiJudgeAuditor(
|
|
|
46
43
|
systemPrompt: await loadAuditorSoul("judge"),
|
|
47
44
|
roleLabel: "Judge compliance audit",
|
|
48
45
|
invalidDecisionLabel: "invalid judge audit decision",
|
|
49
|
-
...(runCompletion === undefined ? {} : { runCompletion }),
|
|
50
46
|
context: options.context,
|
|
51
47
|
...(auditorRunDirectory(options.context) === undefined ? {} : { runDirectory: auditorRunDirectory(options.context) }),
|
|
52
48
|
...(options.signal === undefined ? {} : { signal: options.signal }),
|
package/src/judge-role.ts
CHANGED
|
@@ -1,18 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
ExtensionAPI,
|
|
4
|
-
ExtensionContext,
|
|
5
|
-
AgentToolResult,
|
|
6
|
-
} from "@earendil-works/pi-coding-agent";
|
|
1
|
+
import type { RoleHost, HostContext, HostToolResult, HostGatekeeperActions } from "./host-contracts.ts";
|
|
2
|
+
import { stringEnum } from "./host-contracts.ts";
|
|
7
3
|
import { Type, type Static } from "typebox";
|
|
8
4
|
|
|
9
5
|
import { disposeComplianceDecision } from "./audit-escalation.ts";
|
|
10
6
|
import type { ComplianceDecision } from "./compliance-transport.ts";
|
|
11
|
-
import { requireGatekeeperPass, type GatekeeperPassHostActions } from "./gatekeeper-role.ts";
|
|
12
|
-
import {
|
|
13
|
-
failOnInfrastructureFailureDeclaration,
|
|
14
|
-
withInfrastructureFailureDeclaration,
|
|
15
|
-
} from "./package-contracts/terminating-infrastructure.ts";
|
|
16
7
|
|
|
17
8
|
import {
|
|
18
9
|
JUDGE_ACCEPTED_AUDIT_NO_RECEIPT_TEXT,
|
|
@@ -25,36 +16,34 @@ import {
|
|
|
25
16
|
export { JUDGE_OUTPUT_TOOL_NAME };
|
|
26
17
|
export type { JudgeVerdict };
|
|
27
18
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
{
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
Type.
|
|
34
|
-
|
|
35
|
-
{ additionalProperties: false, description: "continue 时的补救说明" },
|
|
36
|
-
),
|
|
19
|
+
const judgeVerdictSchema = Type.Object(
|
|
20
|
+
{
|
|
21
|
+
judgeStatus: stringEnum(["converged", "continue", "escalate"] as const, { description: "converged | continue | escalate — 形状指引,非 schema 闸" }),
|
|
22
|
+
fix: Type.Optional(
|
|
23
|
+
Type.Object(
|
|
24
|
+
{ summary: Type.String({ minLength: 1, description: "continue 时的补救摘要" }) },
|
|
25
|
+
{ additionalProperties: false, description: "continue 时的补救说明" },
|
|
37
26
|
),
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
},
|
|
52
|
-
|
|
53
|
-
|
|
27
|
+
),
|
|
28
|
+
classes: Type.Optional(Type.Array(Type.Object({
|
|
29
|
+
name: Type.String({ minLength: 1 }),
|
|
30
|
+
owner: Type.String({ minLength: 1 }),
|
|
31
|
+
boundary: Type.String({ minLength: 1 }),
|
|
32
|
+
disposition: Type.String({ minLength: 1 }),
|
|
33
|
+
}, { additionalProperties: false }), { minItems: 1, description: "已裁决 finding 类及其 owner 与修理边界" })),
|
|
34
|
+
note: Type.Optional(Type.String({ minLength: 1, description: "可选裁决附注" })),
|
|
35
|
+
evidence: Type.Optional(Type.Unknown({ description: "留存的裁决证据" })),
|
|
36
|
+
decisionGate: Type.Optional(
|
|
37
|
+
Type.Object(
|
|
38
|
+
{
|
|
39
|
+
question: Type.String({ minLength: 1 }),
|
|
40
|
+
options: Type.Array(Type.String({ minLength: 1 }), { minItems: 1 }),
|
|
41
|
+
},
|
|
42
|
+
{ additionalProperties: false, description: "需人权威处置的问题与选项" },
|
|
54
43
|
),
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
44
|
+
),
|
|
45
|
+
},
|
|
46
|
+
{ additionalProperties: true },
|
|
58
47
|
);
|
|
59
48
|
(judgeVerdictSchema as unknown as { required: string[] }).required = [];
|
|
60
49
|
|
|
@@ -65,11 +54,11 @@ export type SoulAuditResult = ComplianceDecision;
|
|
|
65
54
|
export type JudgeRoleDependencies = {
|
|
66
55
|
loadSoul(): Promise<string>;
|
|
67
56
|
auditSoulCompliance(
|
|
68
|
-
options: { context:
|
|
57
|
+
options: { context: HostContext; signal?: AbortSignal },
|
|
69
58
|
): Promise<SoulAuditResult>;
|
|
70
59
|
};
|
|
71
60
|
|
|
72
|
-
export type JudgeRoleHostActions =
|
|
61
|
+
export type JudgeRoleHostActions = HostGatekeeperActions;
|
|
73
62
|
|
|
74
63
|
export function validateVerdict(verdict: JudgeVerdictParameters): JudgeVerdict {
|
|
75
64
|
return validateAcceptedJudgeDetails(verdict);
|
|
@@ -77,13 +66,13 @@ export function validateVerdict(verdict: JudgeVerdictParameters): JudgeVerdict {
|
|
|
77
66
|
|
|
78
67
|
function requireSingletonSubmissionCall(
|
|
79
68
|
toolCallId: string,
|
|
80
|
-
ctx:
|
|
69
|
+
ctx: HostContext,
|
|
81
70
|
): void {
|
|
82
71
|
const leaf = ctx.sessionManager.getLeafEntry();
|
|
83
|
-
if (leaf?.type !== "message" || leaf.message.role !== "assistant") {
|
|
72
|
+
if (leaf?.type !== "message" || leaf.message === undefined || leaf.message.role !== "assistant" || !Array.isArray(leaf.message.content)) {
|
|
84
73
|
throw new Error("大理寺回执非唯一终局工具调用");
|
|
85
74
|
}
|
|
86
|
-
const calls = leaf.message.content.filter((part) => part.type === "toolCall");
|
|
75
|
+
const calls = leaf.message.content.filter((part): part is Extract<typeof part, { type: "toolCall" }> => part.type === "toolCall");
|
|
87
76
|
const call = calls[0];
|
|
88
77
|
if (
|
|
89
78
|
calls.length !== 1 || call === undefined || call.id !== toolCallId ||
|
|
@@ -94,7 +83,7 @@ function requireSingletonSubmissionCall(
|
|
|
94
83
|
}
|
|
95
84
|
|
|
96
85
|
export function createJudgeRoleRuntime(
|
|
97
|
-
pi:
|
|
86
|
+
pi: RoleHost,
|
|
98
87
|
dependencies: JudgeRoleDependencies,
|
|
99
88
|
hostActions: JudgeRoleHostActions,
|
|
100
89
|
): { activate(): Promise<void> } {
|
|
@@ -113,17 +102,14 @@ export function createJudgeRoleRuntime(
|
|
|
113
102
|
description: "提交大理寺终局判词;受理前经审刑院审计。",
|
|
114
103
|
promptSnippet: "提交大理寺终局判词",
|
|
115
104
|
parameters: judgeVerdictSchema,
|
|
116
|
-
async execute(toolCallId, parameters
|
|
105
|
+
async execute(toolCallId: string, parameters: Static<typeof judgeVerdictSchema>, signal: AbortSignal | undefined, _onUpdate: unknown, ctx: HostContext): Promise<HostToolResult<unknown>> {
|
|
117
106
|
if (soul === undefined) throw new Error("大理寺职分未装载");
|
|
118
|
-
// #541: infra declaration fails via the shared host seam before any
|
|
119
|
-
// Gatekeeper + audit courtyard work.
|
|
120
|
-
failOnInfrastructureFailureDeclaration(parameters, hostActions, ctx, toolCallId);
|
|
121
107
|
requireSingletonSubmissionCall(toolCallId, ctx);
|
|
122
108
|
const verdict = validateVerdict(parameters);
|
|
123
109
|
// Candidate verdict is already on the parent session books as this
|
|
124
110
|
// tool-call leaf (first-record-then-audit; run 019fea05 L61/L62).
|
|
125
111
|
// Gatekeeper runs after the draft is booked and before existing auditor.
|
|
126
|
-
await requireGatekeeperPass({
|
|
112
|
+
await pi.requireGatekeeperPass!({
|
|
127
113
|
context: ctx,
|
|
128
114
|
subject: { kind: "judge_draft", material: JSON.stringify(verdict) },
|
|
129
115
|
...(signal === undefined ? {} : { signal }),
|
|
@@ -141,7 +127,7 @@ export function createJudgeRoleRuntime(
|
|
|
141
127
|
hostActions.failInfrastructure(error, ctx, toolCallId);
|
|
142
128
|
}
|
|
143
129
|
const acceptedDetails = verdict;
|
|
144
|
-
return disposeComplianceDecision<
|
|
130
|
+
return disposeComplianceDecision<HostToolResult<unknown>>(
|
|
145
131
|
audit,
|
|
146
132
|
{
|
|
147
133
|
pass: (usage) => ({
|
package/src/merger-contracts.ts
CHANGED
|
@@ -3,7 +3,6 @@ import { isFullGitObjectId } from "./git-object-id.ts";
|
|
|
3
3
|
import { exactUtf8 } from "./exact-utf8.ts";
|
|
4
4
|
import { sha256Hex } from "./sha256.ts";
|
|
5
5
|
import { openToolObjectFromUnion } from "./open-tool-schema.ts";
|
|
6
|
-
import { withInfrastructureFailureDeclaration } from "./package-contracts/terminating-infrastructure.ts";
|
|
7
6
|
|
|
8
7
|
const oidPattern = "^(?:[0-9a-f]{40}|[0-9a-f]{64})$";
|
|
9
8
|
const materialSchema = Type.Object({ bytesBase64: Type.String(), sha256: Type.String() }, { additionalProperties: false });
|
|
@@ -20,9 +19,7 @@ const mergerOutputVariants = Type.Union([
|
|
|
20
19
|
Type.Object({ status: Type.Literal("completed", { description: "completed — 形状指引,非 schema 闸" }), attemptId: Type.String({ minLength: 1, description: "已受理合并 attempt 身份" }), report: Type.String({ minLength: 1, description: "如实结果报告" }), mergeCommitId: Type.String({ pattern: oidPattern, description: "已核验的完成合并 commit object ID" }) }, { additionalProperties: false }),
|
|
21
20
|
Type.Object({ status: Type.Literal("escalate", { description: "escalate — 形状指引,非 schema 闸" }), attemptId: Type.String({ minLength: 1, description: "已受理合并 attempt 身份" }), diagnosis: Type.String({ minLength: 1, description: "合并完成需升级的原因" }), report: Type.String({ minLength: 1, description: "如实结果报告" }) }, { additionalProperties: false }),
|
|
22
21
|
]);
|
|
23
|
-
export const mergerOutputSchema =
|
|
24
|
-
openToolObjectFromUnion(mergerOutputVariants),
|
|
25
|
-
);
|
|
22
|
+
export const mergerOutputSchema = openToolObjectFromUnion(mergerOutputVariants);
|
|
26
23
|
|
|
27
24
|
export type DeepReadonly<T> = T extends (...args: never[]) => unknown ? T : T extends readonly (infer U)[] ? readonly DeepReadonly<U>[] : T extends object ? { readonly [K in keyof T]: DeepReadonly<T[K]> } : T;
|
|
28
25
|
export type MergerMaterial = DeepReadonly<Static<typeof materialSchema>>;
|
package/src/merger-role.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { RoleHost, HostContext, HostToolResult } from "./host-contracts.ts";
|
|
2
2
|
import { MERGER_ACCEPTED_TEXT, MERGER_OUTPUT_TOOL_NAME, mergerOutputSchema, validateMergerInput, validateMergerOutput, type MergerInput } from "./merger-contracts.ts";
|
|
3
3
|
import type { MergerGitState } from "./merger-git-state.ts";
|
|
4
|
-
import { failOnInfrastructureFailureDeclaration } from "./package-contracts/terminating-infrastructure.ts";
|
|
5
4
|
import { exactUtf8 } from "./exact-utf8.ts";
|
|
6
5
|
import { isFullGitObjectId } from "./git-object-id.ts";
|
|
7
6
|
|
|
@@ -9,13 +8,13 @@ export { MERGER_OUTPUT_TOOL_NAME };
|
|
|
9
8
|
export const MERGER_INPUT_FLAG = { name: "ak-merger-input", definition: { description: "Path to an immutable digest-bound Merger v1 input JSON file", type: "string" as const } } as const;
|
|
10
9
|
export const MERGER_ACTIVE_TOOLS = ["read", "grep", "find", "ls", "bash", "write", "edit", MERGER_OUTPUT_TOOL_NAME] as const;
|
|
11
10
|
export type MergerRoleDependencies = { loadSoul(): Promise<string>; loadInput(path: string): Promise<unknown>; gitState: MergerGitState };
|
|
12
|
-
export type MergerRoleHostActions = { failInfrastructure(error: unknown, ctx:
|
|
11
|
+
export type MergerRoleHostActions = { failInfrastructure(error: unknown, ctx: HostContext, toolCallId?: string): never };
|
|
13
12
|
|
|
14
13
|
function same(a: readonly string[], b: readonly string[]): boolean { return a.length === b.length && a.every((value, i) => value === b[i]); }
|
|
15
|
-
function singleton(id: string, ctx:
|
|
14
|
+
function singleton(id: string, ctx: HostContext): void { const leaf = ctx.sessionManager.getLeafEntry(); if (leaf?.type !== "message" || leaf.message === undefined || leaf.message.role !== "assistant" || !Array.isArray(leaf.message.content)) throw new Error("合并回执非唯一终局工具调用"); const calls = leaf.message.content.filter((part): part is Extract<typeof part, { type: "toolCall" }> => part.type === "toolCall"); if (calls.length !== 1 || calls[0]?.id !== id || calls[0]?.name !== MERGER_OUTPUT_TOOL_NAME) throw new Error("合并回执非唯一终局工具调用"); }
|
|
16
15
|
function materialText(input: MergerInput, key: keyof MergerInput["materials"]): string { return exactUtf8(Buffer.from(input.materials[key].bytesBase64, "base64"), `Merger ${key}`); }
|
|
17
16
|
|
|
18
|
-
export function createMergerRoleRuntime(pi:
|
|
17
|
+
export function createMergerRoleRuntime(pi: RoleHost, dependencies: MergerRoleDependencies, host: MergerRoleHostActions) {
|
|
19
18
|
let activation: { soul: string; input: Readonly<MergerInput>; automaticMergeTreeId: string } | undefined;
|
|
20
19
|
let registered = false;
|
|
21
20
|
let accepted = false;
|
|
@@ -29,13 +28,10 @@ export function createMergerRoleRuntime(pi: ExtensionAPI, dependencies: MergerRo
|
|
|
29
28
|
if (state.targetObjectId !== input.targetObjectId || state.sourceObjectId !== input.sourceObjectId || !same(state.unmergedPaths, input.expectedConflictPaths) || state.unmergedPaths.length === 0 || !isFullGitObjectId(state.automaticMergeTreeId) || state.automaticMergeTreeId.length !== input.targetObjectId.length) throw new Error("Merger activation rejected repository parent, merge, automatic-result, or complete conflict-set drift");
|
|
30
29
|
activation = { soul, input, automaticMergeTreeId: state.automaticMergeTreeId }; accepted = false;
|
|
31
30
|
if (!registered) { registered = true;
|
|
32
|
-
pi.registerTool({ name: MERGER_OUTPUT_TOOL_NAME, label: "合并输出", description: "提交合并结果;输出分支为 completed 与 escalate", promptSnippet: "提交合并结果", parameters: mergerOutputSchema,
|
|
33
|
-
async execute(id, params, _signal, _update, ctx) {
|
|
31
|
+
pi.registerTool({ name: MERGER_OUTPUT_TOOL_NAME, label: "合并输出", description: "提交合并结果;输出分支为 completed 与 escalate;基础设施及 Git 失败由 abort 通道承接。", promptSnippet: "提交合并结果", parameters: mergerOutputSchema,
|
|
32
|
+
async execute(id: string, params: unknown, _signal: AbortSignal | undefined, _update: unknown, ctx: HostContext) {
|
|
34
33
|
if (!activation) throw new Error("校书郎未激活");
|
|
35
34
|
if (accepted) throw new Error("合并回执已受理");
|
|
36
|
-
// #541: infra declaration fails via the shared host seam before output
|
|
37
|
-
// validation / Git verification / accepted=true.
|
|
38
|
-
failOnInfrastructureFailureDeclaration(params, host, ctx, id);
|
|
39
35
|
let output;
|
|
40
36
|
try {
|
|
41
37
|
singleton(id, ctx);
|
|
@@ -54,7 +50,7 @@ export function createMergerRoleRuntime(pi: ExtensionAPI, dependencies: MergerRo
|
|
|
54
50
|
const acceptedDetails = output;
|
|
55
51
|
return { content: [{ type: "text" as const, text: MERGER_ACCEPTED_TEXT }], details: acceptedDetails, terminate: true as const };
|
|
56
52
|
} });
|
|
57
|
-
pi.on("before_agent_start", event => { if (!activation) throw new Error("校书郎未激活"); const admitted = { attemptId: activation.input.attemptId, targetObjectId: activation.input.targetObjectId, sourceObjectId: activation.input.sourceObjectId, task: materialText(activation.input, "task"), authority: materialText(activation.input, "authority"), targetIntent: materialText(activation.input, "targetIntent"), sourceIntent: materialText(activation.input, "sourceIntent"), expectedConflictPaths: activation.input.expectedConflictPaths, resolutionScope: activation.input.resolutionScope, authorizedChecks: activation.input.authorizedChecks }; return { systemPrompt: `${event.systemPrompt}\n\n<merger_soul>\n${activation.soul}\n</merger_soul>\n\n<merger_assignment>\n${JSON.stringify(admitted)}\n</merger_assignment>` }; });
|
|
53
|
+
pi.on("before_agent_start", (event) => { if (!activation) throw new Error("校书郎未激活"); const admitted = { attemptId: activation.input.attemptId, targetObjectId: activation.input.targetObjectId, sourceObjectId: activation.input.sourceObjectId, task: materialText(activation.input, "task"), authority: materialText(activation.input, "authority"), targetIntent: materialText(activation.input, "targetIntent"), sourceIntent: materialText(activation.input, "sourceIntent"), expectedConflictPaths: activation.input.expectedConflictPaths, resolutionScope: activation.input.resolutionScope, authorizedChecks: activation.input.authorizedChecks }; return { systemPrompt: `${event.systemPrompt}\n\n<merger_soul>\n${activation.soul}\n</merger_soul>\n\n<merger_assignment>\n${JSON.stringify(admitted)}\n</merger_assignment>` }; });
|
|
58
54
|
}
|
|
59
55
|
const all = pi.getAllTools().map(tool => tool.name);
|
|
60
56
|
for (const name of MERGER_ACTIVE_TOOLS) if (all.filter(item => item === name).length !== 1) throw new Error(`Merger required tool collision or missing: ${name}`);
|
|
@@ -13,6 +13,11 @@ import {
|
|
|
13
13
|
mintNavigatorInvocationId,
|
|
14
14
|
} from "./navigator-invocation-identity.ts";
|
|
15
15
|
import { PACKAGED_ROLE_REGISTRY, type PackagedRole, packagedRoleMetadata } from "./packaged-role-registry.ts";
|
|
16
|
+
import {
|
|
17
|
+
activationBookDirectory,
|
|
18
|
+
resolveActivationLedgerHome,
|
|
19
|
+
} from "./activation-ledger-topology.ts";
|
|
20
|
+
import { sitianReport } from "./sitian-facade.ts";
|
|
16
21
|
import { openInProcessAgentSession } from "./in-process-session.ts";
|
|
17
22
|
import { renderPublicAkRoleCommand } from "./public-command-renderer.ts";
|
|
18
23
|
import { issueRoot, subjectPath } from "./work-subject-identity.ts";
|
|
@@ -1308,7 +1313,21 @@ export function createNativeNavigatorSessionFactory(defaultModelSettingPath = na
|
|
|
1308
1313
|
}
|
|
1309
1314
|
},
|
|
1310
1315
|
providerFailure: () => providerFailure,
|
|
1311
|
-
appendEntry: (customType, data) => {
|
|
1316
|
+
appendEntry: (customType, data) => {
|
|
1317
|
+
opened.session.sessionManager.appendCustomEntry(customType, data);
|
|
1318
|
+
try {
|
|
1319
|
+
sitianReport({
|
|
1320
|
+
level: "event",
|
|
1321
|
+
kind: "attendance",
|
|
1322
|
+
cwd: context.cwd,
|
|
1323
|
+
sessionParent: opened.session.sessionManager.getSessionFile(),
|
|
1324
|
+
payload: { customType, data },
|
|
1325
|
+
source: "navigator-attendance",
|
|
1326
|
+
});
|
|
1327
|
+
} catch {
|
|
1328
|
+
// best-effort persistence in attendance adapter
|
|
1329
|
+
}
|
|
1330
|
+
},
|
|
1312
1331
|
entries: () => opened.session.sessionManager.getEntries(),
|
|
1313
1332
|
setModel: async (next, thinkingLevel) => {
|
|
1314
1333
|
let nextParsed: ReturnType<typeof parseNavigatorModelSetting>;
|
package/src/notary-contracts.ts
CHANGED
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
import { Type } from "typebox";
|
|
7
7
|
|
|
8
8
|
import { openToolObject } from "./open-tool-schema.ts";
|
|
9
|
-
import { withInfrastructureFailureDeclaration } from "./package-contracts/terminating-infrastructure.ts";
|
|
10
9
|
|
|
11
10
|
export const NOTARY_OUTPUT_TOOL_NAME = "ak_notary_output";
|
|
12
11
|
export const NOTARY_ACCEPTED_TEXT = "符宝郎回执已接受";
|
|
@@ -22,17 +21,15 @@ export const NOTARY_SOURCE_RUN_FLAG = {
|
|
|
22
21
|
export const NOTARY_FIXED_KICKOFF =
|
|
23
22
|
"符宝郎案卷已受理;来源 run 定位见会话材料。";
|
|
24
23
|
|
|
25
|
-
export const notaryOutputSchema =
|
|
26
|
-
|
|
27
|
-
Type.
|
|
28
|
-
|
|
29
|
-
description: "pass | bounce — 形状指引,非 schema 闸",
|
|
30
|
-
}),
|
|
31
|
-
findings: Type.Unknown({
|
|
32
|
-
description: "string[] findings,随 pass 或 bounce 留存",
|
|
33
|
-
}),
|
|
24
|
+
export const notaryOutputSchema = openToolObject(
|
|
25
|
+
Type.Object({
|
|
26
|
+
status: Type.Unknown({
|
|
27
|
+
description: "pass | bounce — 形状指引,非 schema 闸",
|
|
34
28
|
}),
|
|
35
|
-
|
|
29
|
+
findings: Type.Unknown({
|
|
30
|
+
description: "string[] findings,随 pass 或 bounce 留存",
|
|
31
|
+
}),
|
|
32
|
+
}),
|
|
36
33
|
);
|
|
37
34
|
|
|
38
35
|
export type NotarySourceRunLocator = {
|
package/src/notary-role.ts
CHANGED
|
@@ -1,12 +1,8 @@
|
|
|
1
|
+
import type { RoleHost, HostContext, HostToolResult } from "./host-contracts.ts";
|
|
1
2
|
/**
|
|
2
3
|
* Public Notary role runtime — direct officer seat (not through Gatekeeper province).
|
|
3
4
|
* Caller supplies only a source-run locator; Notary self-fetches authoritative materials.
|
|
4
5
|
*/
|
|
5
|
-
import type {
|
|
6
|
-
AgentToolResult,
|
|
7
|
-
ExtensionAPI,
|
|
8
|
-
ExtensionContext,
|
|
9
|
-
} from "@earendil-works/pi-coding-agent";
|
|
10
6
|
|
|
11
7
|
import {
|
|
12
8
|
NOTARY_ACCEPTED_TEXT,
|
|
@@ -17,9 +13,6 @@ import {
|
|
|
17
13
|
retainNotarySubmission,
|
|
18
14
|
type NotarySourceRunLocator,
|
|
19
15
|
} from "./notary-contracts.ts";
|
|
20
|
-
import {
|
|
21
|
-
failOnInfrastructureFailureDeclaration,
|
|
22
|
-
} from "./package-contracts/terminating-infrastructure.ts";
|
|
23
16
|
|
|
24
17
|
export {
|
|
25
18
|
NOTARY_ACCEPTED_TEXT,
|
|
@@ -35,20 +28,20 @@ export type NotaryRoleDependencies = {
|
|
|
35
28
|
export type NotaryRoleHostActions = {
|
|
36
29
|
failInfrastructure(
|
|
37
30
|
error: unknown,
|
|
38
|
-
ctx:
|
|
31
|
+
ctx: HostContext,
|
|
39
32
|
toolCallId?: string,
|
|
40
33
|
): never;
|
|
41
34
|
};
|
|
42
35
|
|
|
43
36
|
function requireSingletonSubmissionCall(
|
|
44
37
|
toolCallId: string,
|
|
45
|
-
ctx:
|
|
38
|
+
ctx: HostContext,
|
|
46
39
|
): void {
|
|
47
40
|
const leaf = ctx.sessionManager.getLeafEntry();
|
|
48
|
-
if (leaf?.type !== "message" || leaf.message.role !== "assistant") {
|
|
41
|
+
if (leaf?.type !== "message" || leaf.message === undefined || leaf.message.role !== "assistant" || !Array.isArray(leaf.message.content)) {
|
|
49
42
|
throw new Error("符宝郎回执非唯一终局工具调用");
|
|
50
43
|
}
|
|
51
|
-
const calls = leaf.message.content.filter((part) => part.type === "toolCall");
|
|
44
|
+
const calls = leaf.message.content.filter((part): part is Extract<typeof part, { type: "toolCall" }> => part.type === "toolCall");
|
|
52
45
|
if (
|
|
53
46
|
calls.length !== 1 ||
|
|
54
47
|
calls[0]?.id !== toolCallId ||
|
|
@@ -59,7 +52,7 @@ function requireSingletonSubmissionCall(
|
|
|
59
52
|
}
|
|
60
53
|
|
|
61
54
|
export function createNotaryRoleRuntime(
|
|
62
|
-
pi:
|
|
55
|
+
pi: RoleHost,
|
|
63
56
|
dependencies: NotaryRoleDependencies,
|
|
64
57
|
host: NotaryRoleHostActions,
|
|
65
58
|
) {
|
|
@@ -91,19 +84,10 @@ export function createNotaryRoleRuntime(
|
|
|
91
84
|
description: "提交引文保真与票面对齐的 typed pass/bounce 决议。",
|
|
92
85
|
promptSnippet: "提交符宝郎决议",
|
|
93
86
|
parameters: notaryOutputSchema,
|
|
94
|
-
async execute(
|
|
95
|
-
toolCallId,
|
|
96
|
-
parameters,
|
|
97
|
-
_signal,
|
|
98
|
-
_onUpdate,
|
|
99
|
-
ctx,
|
|
100
|
-
): Promise<AgentToolResult<unknown>> {
|
|
87
|
+
async execute(toolCallId: string, parameters: unknown, _signal: AbortSignal | undefined, _onUpdate: unknown, ctx: HostContext, ): Promise<HostToolResult<unknown>> {
|
|
101
88
|
if (activation === undefined) {
|
|
102
89
|
throw new Error("符宝郎未激活");
|
|
103
90
|
}
|
|
104
|
-
// #541: infra declaration fails via the shared host seam before any
|
|
105
|
-
// pass/bounce projection.
|
|
106
|
-
failOnInfrastructureFailureDeclaration(parameters, host, ctx, toolCallId);
|
|
107
91
|
// Unique submission + terminate only. Shape is not an admission gate
|
|
108
92
|
// (第 0 条 / ADR 0055): lawful pass/bounce projected; else params as-is.
|
|
109
93
|
requireSingletonSubmissionCall(toolCallId, ctx);
|
package/src/notary-source-run.ts
CHANGED
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
resolveActivationLedgerHome,
|
|
14
14
|
} from "./activation-ledger-topology.ts";
|
|
15
15
|
import type { NotarySourceRunLocator } from "./notary-contracts.ts";
|
|
16
|
-
import {
|
|
16
|
+
import { readRoleRunIdentity } from "./public-cli/run-lifecycle.ts";
|
|
17
17
|
|
|
18
18
|
const RUN_DIR_NAME =
|
|
19
19
|
/^([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})@([A-Za-z][A-Za-z0-9_-]*)$/i;
|
|
@@ -109,7 +109,8 @@ export async function resolveNotarySourceRunLocator(options: {
|
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
// Authoritative retained record only — legal role/state via shared reader (DRY #14).
|
|
112
|
-
|
|
112
|
+
// Identity envelope only: principal payload is not interpreted here.
|
|
113
|
+
const runState = await readRoleRunIdentity(real);
|
|
113
114
|
if (runState === undefined) {
|
|
114
115
|
throw new NotarySourceRunError(
|
|
115
116
|
"notary --source-run lacks retained run-state identity",
|
|
@@ -144,7 +145,7 @@ export async function loadNotarySourceRunLocator(
|
|
|
144
145
|
): Promise<NotarySourceRunLocator> {
|
|
145
146
|
const real = await requireRunDirectory(path, path);
|
|
146
147
|
const identity = parseRunDirectoryName(basename(real))!;
|
|
147
|
-
const runState = await
|
|
148
|
+
const runState = await readRoleRunIdentity(real);
|
|
148
149
|
if (runState === undefined) {
|
|
149
150
|
throw new NotarySourceRunError(
|
|
150
151
|
"notary source-run lacks retained run-state identity",
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { Type, type Static } from "typebox";
|
|
2
2
|
import { FIXER_PREREQUISITE_ID_PATTERN, type FixerInvocationInput } from "./fixer-packet.ts";
|
|
3
3
|
import { openToolObjectFromUnion } from "../open-tool-schema.ts";
|
|
4
|
-
import { withInfrastructureFailureDeclaration } from "./terminating-infrastructure.ts";
|
|
5
4
|
|
|
6
5
|
export const FIXER_OUTPUT_TOOL_NAME = "ak_fixer_output";
|
|
7
6
|
export const FIXER_ACCEPTED_TEXT = "修内司回执已接受";
|
|
@@ -41,9 +40,7 @@ const fixerOutputVariants = Type.Union([
|
|
|
41
40
|
Type.Object({ status: Type.Literal("refused", { description: "refused — 形状指引,非 schema 闸" }), report: Type.String({ minLength: 1, description: "如实结果报告" }), classResults: Type.Array(classResultSchema, { minItems: 1, description: "各类拒绝结算" }) }),
|
|
42
41
|
Type.Object({ status: Type.Literal("partially_completed", { description: "partially_completed — 形状指引,非 schema 闸" }), report: Type.String({ minLength: 1, description: "如实结果报告" }), classResults: Type.Array(classResultSchema, { minItems: 1, description: "各类完成或拒绝结算" }), testEvidence: Type.Optional(testEvidenceSchema) }),
|
|
43
42
|
]);
|
|
44
|
-
export const fixerOutputSchema =
|
|
45
|
-
openToolObjectFromUnion(fixerOutputVariants),
|
|
46
|
-
);
|
|
43
|
+
export const fixerOutputSchema = openToolObjectFromUnion(fixerOutputVariants);
|
|
47
44
|
|
|
48
45
|
export type FixerBlocker = Static<typeof blockerSchema>;
|
|
49
46
|
export type FixerClassResult = Static<typeof classResultSchema>;
|
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Canonical Skill binding built from package-owned method material.
|
|
3
|
-
* Lives beside the method seam
|
|
4
|
-
*
|
|
3
|
+
* Lives beside the method seam — used by the Internal role runtime, not the
|
|
4
|
+
* public ak-role bin bundle.
|
|
5
5
|
*/
|
|
6
6
|
import { dirname } from "node:path";
|
|
7
7
|
|
|
8
|
-
import { parseSkillBlock } from "@earendil-works/pi-coding-agent";
|
|
9
|
-
|
|
10
8
|
import {
|
|
9
|
+
captureCanonicalSkillExpansion,
|
|
11
10
|
type CanonicalSkillBinding,
|
|
12
|
-
type CanonicalSkillEvidence,
|
|
13
11
|
type CanonicalSkillName,
|
|
14
12
|
} from "../canonical-skill-binding.ts";
|
|
15
13
|
import {
|
|
@@ -51,36 +49,14 @@ export async function loadPackagedCanonicalSkillBinding<
|
|
|
51
49
|
invocation(originalRequest) {
|
|
52
50
|
return `/skill:${name} ${originalRequest}`;
|
|
53
51
|
},
|
|
54
|
-
captureExpansion(
|
|
55
|
-
|
|
56
|
-
originalRequest,
|
|
57
|
-
): CanonicalSkillEvidence<Name> | undefined {
|
|
58
|
-
const parsed = parseSkillBlock(prompt);
|
|
59
|
-
const matchedPath =
|
|
60
|
-
parsed?.location === configuredPath
|
|
61
|
-
? configuredPath
|
|
62
|
-
: parsed?.location === snapshot.path
|
|
63
|
-
? snapshot.path
|
|
64
|
-
: undefined;
|
|
65
|
-
const expectedContent =
|
|
66
|
-
matchedPath === undefined
|
|
67
|
-
? undefined
|
|
68
|
-
: `References are relative to ${dirname(matchedPath)}.\n\n${snapshot.body}`;
|
|
69
|
-
const userMessage = parsed?.userMessage ?? "";
|
|
70
|
-
if (
|
|
71
|
-
parsed?.name !== name ||
|
|
72
|
-
matchedPath === undefined ||
|
|
73
|
-
parsed.content !== expectedContent ||
|
|
74
|
-
userMessage !== originalRequest
|
|
75
|
-
) {
|
|
76
|
-
return undefined;
|
|
77
|
-
}
|
|
78
|
-
return Object.freeze({
|
|
52
|
+
captureExpansion(evidence, originalRequest) {
|
|
53
|
+
return captureCanonicalSkillExpansion(
|
|
79
54
|
name,
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
55
|
+
snapshot,
|
|
56
|
+
configuredPath,
|
|
57
|
+
evidence,
|
|
58
|
+
originalRequest,
|
|
59
|
+
);
|
|
84
60
|
},
|
|
85
61
|
};
|
|
86
62
|
return Object.freeze(binding);
|