@akagilnc/pi-workflow-roles 0.1.1751
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/LICENSE +202 -0
- package/README.md +104 -0
- package/README.zh-CN.md +133 -0
- package/THIRD_PARTY_NOTICES.md +60 -0
- package/dist/activation-ledger-git.js +68 -0
- package/dist/activation-ledger-session.js +120 -0
- package/dist/activation-ledger-topology.js +239 -0
- package/dist/activation-reconciliation.js +61 -0
- package/dist/audit-escalation.js +108 -0
- package/dist/auditor-dossier-tool.js +35 -0
- package/dist/canonical-json.js +78 -0
- package/dist/compliance-transport.js +77 -0
- package/dist/doctor-contracts.js +172 -0
- package/dist/dossier-resolution.js +103 -0
- package/dist/evidence-child-executor.js +661 -0
- package/dist/exact-utf8.js +12 -0
- package/dist/git-object-id.js +7 -0
- package/dist/in-process-session.js +50 -0
- package/dist/merger-contracts.js +76 -0
- package/dist/navigator-attendance.js +995 -0
- package/dist/navigator-invocation-identity.js +220 -0
- package/dist/open-tool-schema.js +39 -0
- package/dist/package-contracts/collector-output.js +50 -0
- package/dist/package-contracts/fixer-output.js +72 -0
- package/dist/package-contracts/fixer-packet.js +77 -0
- package/dist/package-contracts/judge-output.js +17 -0
- package/dist/package-contracts/reviewer-output.js +82 -0
- package/dist/package-contracts/terminating-tools.js +173 -0
- package/dist/package-contracts/worker-output.js +13 -0
- package/dist/package-owned-tool-idle.js +104 -0
- package/dist/packaged-role-registry.js +34 -0
- package/dist/public-cli/main.js +23867 -0
- package/dist/public-command-renderer.js +20 -0
- package/dist/reviewer-agent.js +93 -0
- package/dist/reviewer-child-executor.js +23 -0
- package/dist/reviewer-construction.js +95 -0
- package/dist/reviewer-dispatch.js +77 -0
- package/dist/reviewer-execution-ledger.js +160 -0
- package/dist/reviewer-failure-diagnostic.js +17 -0
- package/dist/reviewer-git-snapshot.js +38 -0
- package/dist/reviewer-pinned-git.js +146 -0
- package/dist/reviewer-preflight-error.js +15 -0
- package/dist/reviewer-prompt-identity.js +10 -0
- package/dist/reviewer-scope-prompt.js +21 -0
- package/dist/reviewer-workspace.js +151 -0
- package/dist/sha256.js +5 -0
- package/dist/sitian-record-entry.js +33 -0
- package/dist/stderr-jsonl.js +26 -0
- package/dist/stream-idle-guard.js +75 -0
- package/dist/tool-execution-observation.js +141 -0
- package/dist/uuidv7.js +21 -0
- package/dist/work-subject-identity.js +53 -0
- package/extensions/role-runtime.ts +303 -0
- package/package.json +69 -0
- package/packets/fixer-prerequisites.json +6 -0
- package/packets/fixer-repair.md +5 -0
- package/packets/judge-apply.md +77 -0
- package/packets/judge-authority.md +64 -0
- package/packets/judge-plan.md +55 -0
- package/packets/judge-review.md +49 -0
- package/packets/judge-submission.md +34 -0
- package/resources/methods/code-review/SKILL.md +92 -0
- package/resources/methods/code-review/agents/openai.yaml +3 -0
- package/resources/methods/code-review/provenance.json +26 -0
- package/resources/methods/diagnosing-bugs/SKILL.md +134 -0
- package/resources/methods/diagnosing-bugs/agents/openai.yaml +3 -0
- package/resources/methods/diagnosing-bugs/provenance.json +31 -0
- package/resources/methods/diagnosing-bugs/scripts/hitl-loop.template.sh +41 -0
- package/resources/methods/resolving-merge-conflicts/SKILL.md +14 -0
- package/resources/methods/resolving-merge-conflicts/agents/openai.yaml +3 -0
- package/resources/methods/resolving-merge-conflicts/provenance.json +26 -0
- package/resources/methods/tdd/SKILL.md +38 -0
- package/resources/methods/tdd/agents/openai.yaml +3 -0
- package/resources/methods/tdd/mocking.md +59 -0
- package/resources/methods/tdd/provenance.json +36 -0
- package/resources/methods/tdd/tests.md +77 -0
- package/resources/navigator-route-playbook.md +32 -0
- package/schemas/tool-execution-observation.schema.json +107 -0
- package/scripts/build-package.mjs +65 -0
- package/scripts/generate-tool-execution-observation-schema.ts +7 -0
- package/souls/coder.md +10 -0
- package/souls/collector.md +11 -0
- package/souls/doctor-auditor.md +23 -0
- package/souls/doctor.md +8 -0
- package/souls/fixer-auditor.md +33 -0
- package/souls/fixer.md +13 -0
- package/souls/judge-auditor.md +33 -0
- package/souls/judge.md +74 -0
- package/souls/merger.md +5 -0
- package/souls/navigator.md +5 -0
- package/souls/reviewer-auditor.md +25 -0
- package/souls/reviewer.md +11 -0
- package/src/activation-ledger-git.ts +96 -0
- package/src/activation-ledger-session.ts +188 -0
- package/src/activation-ledger-topology.ts +301 -0
- package/src/activation-ledger.ts +240 -0
- package/src/activation-reconciliation.ts +163 -0
- package/src/activation-trace.ts +38 -0
- package/src/audit-escalation.ts +177 -0
- package/src/auditor-dossier-tool.ts +48 -0
- package/src/auditor-soul.ts +28 -0
- package/src/canonical-json.ts +74 -0
- package/src/canonical-skill-binding.ts +107 -0
- package/src/collector-config.ts +89 -0
- package/src/collector-evidence.ts +461 -0
- package/src/collector-github.ts +656 -0
- package/src/collector-identity.ts +161 -0
- package/src/collector-ledger.ts +827 -0
- package/src/collector-receipt.ts +87 -0
- package/src/collector-role.ts +592 -0
- package/src/collector-tool-schemas.ts +19 -0
- package/src/compliance-transport.ts +130 -0
- package/src/doctor-auditor.ts +53 -0
- package/src/doctor-contracts.ts +166 -0
- package/src/doctor-evidence.ts +47 -0
- package/src/doctor-role.ts +18 -0
- package/src/dossier-resolution.ts +137 -0
- package/src/evidence-child-executor.ts +775 -0
- package/src/exact-utf8.ts +9 -0
- package/src/factory-board.ts +1822 -0
- package/src/git-object-id.ts +11 -0
- package/src/human-format.ts +65 -0
- package/src/in-process-session.ts +78 -0
- package/src/judge-auditor.ts +55 -0
- package/src/judge-recording-anti-forge.ts +53 -0
- package/src/judge-role.ts +160 -0
- package/src/merger-contracts.ts +71 -0
- package/src/merger-git-state.ts +76 -0
- package/src/merger-role.ts +60 -0
- package/src/navigator-attendance.ts +1254 -0
- package/src/navigator-invocation-identity.ts +446 -0
- package/src/open-tool-schema.ts +46 -0
- package/src/package-contracts/collector-output.ts +109 -0
- package/src/package-contracts/fixer-output.ts +81 -0
- package/src/package-contracts/fixer-packet.ts +93 -0
- package/src/package-contracts/judge-output.ts +39 -0
- package/src/package-contracts/reviewer-output.ts +115 -0
- package/src/package-contracts/terminating-tools.ts +259 -0
- package/src/package-contracts/worker-output.ts +36 -0
- package/src/package-owned-tool-idle.ts +134 -0
- package/src/package-resources/method-skill-binding.ts +87 -0
- package/src/package-resources/method-skill.ts +358 -0
- package/src/packaged-role-registry.ts +36 -0
- package/src/public-cli/cli-errors.ts +11 -0
- package/src/public-cli/cli-io.ts +4 -0
- package/src/public-cli/cli.ts +912 -0
- package/src/public-cli/coder-run.ts +575 -0
- package/src/public-cli/collector-run.ts +375 -0
- package/src/public-cli/command-renderer.ts +8 -0
- package/src/public-cli/config.ts +346 -0
- package/src/public-cli/doctor-run.ts +355 -0
- package/src/public-cli/explicit-internal.ts +274 -0
- package/src/public-cli/fixer-run.ts +587 -0
- package/src/public-cli/host-pi-runtime.ts +112 -0
- package/src/public-cli/invocation.ts +1958 -0
- package/src/public-cli/judge-run.ts +507 -0
- package/src/public-cli/main.ts +15 -0
- package/src/public-cli/merger-run.ts +681 -0
- package/src/public-cli/public-run-credentials.ts +71 -0
- package/src/public-cli/registry.ts +153 -0
- package/src/public-cli/reviewer-run.ts +561 -0
- package/src/public-cli/run-lifecycle.ts +884 -0
- package/src/public-cli/settlement.ts +3765 -0
- package/src/public-cli/terminal.ts +325 -0
- package/src/public-command-renderer.ts +43 -0
- package/src/reviewer-agent.ts +94 -0
- package/src/reviewer-auditor.ts +53 -0
- package/src/reviewer-child-executor.ts +31 -0
- package/src/reviewer-construction.ts +137 -0
- package/src/reviewer-dispatch.ts +94 -0
- package/src/reviewer-execution-ledger.ts +206 -0
- package/src/reviewer-failure-diagnostic.ts +18 -0
- package/src/reviewer-git-snapshot.ts +53 -0
- package/src/reviewer-pinned-git.ts +144 -0
- package/src/reviewer-preflight-error.ts +14 -0
- package/src/reviewer-prompt-identity.ts +17 -0
- package/src/reviewer-role.ts +193 -0
- package/src/reviewer-scope-prompt.ts +24 -0
- package/src/reviewer-settlement.ts +63 -0
- package/src/reviewer-workspace.ts +111 -0
- package/src/role-runtime.ts +884 -0
- package/src/sha256.ts +6 -0
- package/src/sitian-record-entry.ts +57 -0
- package/src/stderr-jsonl.ts +28 -0
- package/src/stream-idle-guard.ts +98 -0
- package/src/ticket-snapshot.ts +662 -0
- package/src/ticket-trajectory.ts +1000 -0
- package/src/tool-execution-observation.ts +168 -0
- package/src/uuidv7.ts +1 -0
- package/src/work-subject-identity.ts +94 -0
- package/src/worker-role.ts +434 -0
- package/src/worker-submission-gates.ts +225 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical Skill binding built from package-owned method material.
|
|
3
|
+
* Lives beside the method seam but may import Pi skill parsers — used by the
|
|
4
|
+
* Internal role runtime, not the public ak-role bin bundle.
|
|
5
|
+
*/
|
|
6
|
+
import { dirname } from "node:path";
|
|
7
|
+
|
|
8
|
+
import { parseSkillBlock } from "@earendil-works/pi-coding-agent";
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
type CanonicalSkillBinding,
|
|
12
|
+
type CanonicalSkillEvidence,
|
|
13
|
+
type CanonicalSkillName,
|
|
14
|
+
} from "../canonical-skill-binding.ts";
|
|
15
|
+
import {
|
|
16
|
+
loadPackagedMethodSkillMaterial,
|
|
17
|
+
resolvePackagedMethodSkillPath,
|
|
18
|
+
type PackagedMethodSkillName,
|
|
19
|
+
} from "./method-skill.ts";
|
|
20
|
+
|
|
21
|
+
type PackagedCanonicalSkillName = Extract<
|
|
22
|
+
PackagedMethodSkillName,
|
|
23
|
+
CanonicalSkillName
|
|
24
|
+
>;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Build a CanonicalSkillBinding from package-owned material.
|
|
28
|
+
* captureExpansion accepts the package skill path (configured or realpath).
|
|
29
|
+
*/
|
|
30
|
+
export async function loadPackagedCanonicalSkillBinding<
|
|
31
|
+
Name extends PackagedCanonicalSkillName,
|
|
32
|
+
>(
|
|
33
|
+
packageRoot: string,
|
|
34
|
+
name: Name,
|
|
35
|
+
): Promise<CanonicalSkillBinding<Name>> {
|
|
36
|
+
// Forced expansion bindings are only defined for canonical completion-gated skills.
|
|
37
|
+
// Optional Fixer diagnosing-bugs stays available via --skill without this binding.
|
|
38
|
+
const material = await loadPackagedMethodSkillMaterial(packageRoot, name);
|
|
39
|
+
const configuredPath = resolvePackagedMethodSkillPath(packageRoot, name);
|
|
40
|
+
const snapshot = Object.freeze({
|
|
41
|
+
raw: material.raw,
|
|
42
|
+
path: material.skillPath,
|
|
43
|
+
baseDir: dirname(material.skillPath),
|
|
44
|
+
body: material.body,
|
|
45
|
+
snapshotIdentity: Object.freeze({ text: material.raw }),
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const binding: CanonicalSkillBinding<Name> = {
|
|
49
|
+
name,
|
|
50
|
+
snapshot,
|
|
51
|
+
invocation(originalRequest) {
|
|
52
|
+
return `/skill:${name} ${originalRequest}`;
|
|
53
|
+
},
|
|
54
|
+
captureExpansion(
|
|
55
|
+
prompt,
|
|
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({
|
|
79
|
+
name,
|
|
80
|
+
location: parsed.location,
|
|
81
|
+
content: parsed.content,
|
|
82
|
+
userMessage,
|
|
83
|
+
});
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
return Object.freeze(binding);
|
|
87
|
+
}
|
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Package-owned role method Skill seam (ADR 0052 / #109).
|
|
3
|
+
* Forced methods load from the installed package copy — never ambient home discovery.
|
|
4
|
+
*
|
|
5
|
+
* This module is intentionally free of @earendil-works/pi-coding-agent so the
|
|
6
|
+
* public ak-role bin can load provenance/path without bundling peer runtime.
|
|
7
|
+
*/
|
|
8
|
+
import { createHash } from "node:crypto";
|
|
9
|
+
import { readFile, realpath } from "node:fs/promises";
|
|
10
|
+
import { join } from "node:path";
|
|
11
|
+
|
|
12
|
+
import { sha256Hex } from "../sha256.ts";
|
|
13
|
+
|
|
14
|
+
/** Package-side unavailable method Skill (mirrors CanonicalSkillUnavailableError identity). */
|
|
15
|
+
export class PackagedMethodSkillUnavailableError extends Error {
|
|
16
|
+
readonly code = "canonical-skill-unavailable" as const;
|
|
17
|
+
constructor(
|
|
18
|
+
readonly skillName: PackagedMethodSkillName,
|
|
19
|
+
path: string,
|
|
20
|
+
cause: unknown,
|
|
21
|
+
) {
|
|
22
|
+
super(`Canonical ${skillName} Skill is unavailable at ${path}`, { cause });
|
|
23
|
+
this.name = "CanonicalSkillUnavailableError";
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Method Skill names shipped as package resources under resources/methods/. */
|
|
28
|
+
export type PackagedMethodSkillName =
|
|
29
|
+
| "tdd"
|
|
30
|
+
| "diagnosing-bugs"
|
|
31
|
+
| "code-review"
|
|
32
|
+
| "resolving-merge-conflicts";
|
|
33
|
+
|
|
34
|
+
export type PackagedMethodFileProvenance = Readonly<{
|
|
35
|
+
sha256: string;
|
|
36
|
+
byteLength: number;
|
|
37
|
+
/** Independent upstream git blob OID (sha1) for the exact file bytes. */
|
|
38
|
+
gitBlob: string;
|
|
39
|
+
}>;
|
|
40
|
+
|
|
41
|
+
export type PackagedMethodUpstreamProvenance = Readonly<{
|
|
42
|
+
repository: string;
|
|
43
|
+
path: string;
|
|
44
|
+
/** Immutable upstream commit (full lowercase git object id). */
|
|
45
|
+
commit: string;
|
|
46
|
+
/** Immutable upstream release tag when the snapshot is tag-addressable. */
|
|
47
|
+
tag?: string;
|
|
48
|
+
/** Immutable upstream version label when no single tag is the pin. */
|
|
49
|
+
version?: string;
|
|
50
|
+
license: string;
|
|
51
|
+
copyright: string;
|
|
52
|
+
attribution: string;
|
|
53
|
+
}>;
|
|
54
|
+
|
|
55
|
+
/** Exact upstream provenance + per-file digests recorded beside the skill body. */
|
|
56
|
+
export type PackagedMethodSkillProvenance = Readonly<{
|
|
57
|
+
name: PackagedMethodSkillName;
|
|
58
|
+
kind: "role-method-skill";
|
|
59
|
+
upstream: PackagedMethodUpstreamProvenance;
|
|
60
|
+
packageAdaptation: string;
|
|
61
|
+
files: Readonly<Record<string, PackagedMethodFileProvenance>>;
|
|
62
|
+
}>;
|
|
63
|
+
|
|
64
|
+
export type PackagedMethodSkillMaterial = Readonly<{
|
|
65
|
+
name: PackagedMethodSkillName;
|
|
66
|
+
/** Absolute directory containing SKILL.md and companions. */
|
|
67
|
+
rootDirectory: string;
|
|
68
|
+
/** Absolute SKILL.md path (pass to Pi `--skill`). */
|
|
69
|
+
skillPath: string;
|
|
70
|
+
raw: string;
|
|
71
|
+
body: string;
|
|
72
|
+
provenance: PackagedMethodSkillProvenance;
|
|
73
|
+
/** Companion relative paths present beside SKILL.md (tests/mocking/...). */
|
|
74
|
+
companionRelativePaths: readonly string[];
|
|
75
|
+
}>;
|
|
76
|
+
|
|
77
|
+
const METHOD_SKILL_RELATIVE_ROOT = "resources/methods" as const;
|
|
78
|
+
const GIT_COMMIT_RE = /^[0-9a-f]{40}$/;
|
|
79
|
+
const GIT_BLOB_RE = /^[0-9a-f]{40}$/;
|
|
80
|
+
const SHA256_RE = /^[0-9a-f]{64}$/;
|
|
81
|
+
|
|
82
|
+
const REQUIRED_COMPANIONS: Readonly<
|
|
83
|
+
Record<PackagedMethodSkillName, readonly string[]>
|
|
84
|
+
> = {
|
|
85
|
+
tdd: ["tests.md", "mocking.md", "agents/openai.yaml"],
|
|
86
|
+
"diagnosing-bugs": ["agents/openai.yaml", "scripts/hitl-loop.template.sh"],
|
|
87
|
+
"code-review": ["agents/openai.yaml"],
|
|
88
|
+
"resolving-merge-conflicts": ["agents/openai.yaml"],
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
/** Git blob OID for raw file bytes (sha1 of `blob <size>\\0` + content). */
|
|
92
|
+
export function gitBlobOid(bytes: string | Uint8Array): string {
|
|
93
|
+
const body =
|
|
94
|
+
typeof bytes === "string" ? Buffer.from(bytes, "utf8") : Buffer.from(bytes);
|
|
95
|
+
const header = Buffer.from(`blob ${body.byteLength}\0`, "utf8");
|
|
96
|
+
return createHash("sha1").update(header).update(body).digest("hex");
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** Minimal frontmatter strip — body after closing `---` fence (or full text). */
|
|
100
|
+
export function stripSkillFrontmatter(content: string): string {
|
|
101
|
+
if (!content.startsWith("---")) return content;
|
|
102
|
+
const end = content.indexOf("\n---", 3);
|
|
103
|
+
if (end === -1) return content;
|
|
104
|
+
const after = content.slice(end + "\n---".length);
|
|
105
|
+
return after.replace(/^\r?\n/, "");
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function packagedMethodSkillRelativeDirectory(
|
|
109
|
+
name: PackagedMethodSkillName,
|
|
110
|
+
): string {
|
|
111
|
+
return `${METHOD_SKILL_RELATIVE_ROOT}/${name}`;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function resolvePackagedMethodSkillRoot(
|
|
115
|
+
packageRoot: string,
|
|
116
|
+
name: PackagedMethodSkillName,
|
|
117
|
+
): string {
|
|
118
|
+
return join(packageRoot, packagedMethodSkillRelativeDirectory(name));
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function resolvePackagedMethodSkillPath(
|
|
122
|
+
packageRoot: string,
|
|
123
|
+
name: PackagedMethodSkillName,
|
|
124
|
+
): string {
|
|
125
|
+
return join(resolvePackagedMethodSkillRoot(packageRoot, name), "SKILL.md");
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
129
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function parseProvenance(
|
|
133
|
+
raw: unknown,
|
|
134
|
+
expectedName: PackagedMethodSkillName,
|
|
135
|
+
): PackagedMethodSkillProvenance {
|
|
136
|
+
if (!isRecord(raw)) {
|
|
137
|
+
throw new Error(`Packaged method provenance must be an object for ${expectedName}`);
|
|
138
|
+
}
|
|
139
|
+
if (raw.name !== expectedName) {
|
|
140
|
+
throw new Error(
|
|
141
|
+
`Packaged method provenance name mismatch: expected ${expectedName}, got ${String(raw.name)}`,
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
if (raw.kind !== "role-method-skill") {
|
|
145
|
+
throw new Error(`Packaged method provenance kind must be role-method-skill`);
|
|
146
|
+
}
|
|
147
|
+
if (typeof raw.packageAdaptation !== "string" || raw.packageAdaptation.trim() === "") {
|
|
148
|
+
throw new Error(`Packaged method provenance packageAdaptation must be nonblank`);
|
|
149
|
+
}
|
|
150
|
+
if (!isRecord(raw.upstream)) {
|
|
151
|
+
throw new Error(`Packaged method provenance upstream must be an object`);
|
|
152
|
+
}
|
|
153
|
+
const upstream = raw.upstream;
|
|
154
|
+
for (const key of [
|
|
155
|
+
"repository",
|
|
156
|
+
"path",
|
|
157
|
+
"license",
|
|
158
|
+
"copyright",
|
|
159
|
+
"attribution",
|
|
160
|
+
] as const) {
|
|
161
|
+
if (typeof upstream[key] !== "string" || upstream[key].trim() === "") {
|
|
162
|
+
throw new Error(`Packaged method provenance upstream.${key} must be nonblank`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
if (typeof upstream.commit !== "string" || !GIT_COMMIT_RE.test(upstream.commit)) {
|
|
166
|
+
throw new Error(
|
|
167
|
+
`Packaged method provenance upstream.commit must be a 40-char lowercase git object id`,
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
const tag =
|
|
171
|
+
typeof upstream.tag === "string" && upstream.tag.trim() !== ""
|
|
172
|
+
? upstream.tag.trim()
|
|
173
|
+
: undefined;
|
|
174
|
+
const version =
|
|
175
|
+
typeof upstream.version === "string" && upstream.version.trim() !== ""
|
|
176
|
+
? upstream.version.trim()
|
|
177
|
+
: undefined;
|
|
178
|
+
if (tag === undefined && version === undefined) {
|
|
179
|
+
throw new Error(
|
|
180
|
+
`Packaged method provenance upstream must include nonblank tag or version`,
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
if (!isRecord(raw.files)) {
|
|
184
|
+
throw new Error(`Packaged method provenance files must be an object`);
|
|
185
|
+
}
|
|
186
|
+
const files: Record<string, PackagedMethodFileProvenance> = {};
|
|
187
|
+
for (const [rel, entry] of Object.entries(raw.files)) {
|
|
188
|
+
if (!isRecord(entry)) {
|
|
189
|
+
throw new Error(`Packaged method provenance file entry must be an object: ${rel}`);
|
|
190
|
+
}
|
|
191
|
+
if (typeof entry.sha256 !== "string" || !SHA256_RE.test(entry.sha256)) {
|
|
192
|
+
throw new Error(`Packaged method provenance file sha256 invalid: ${rel}`);
|
|
193
|
+
}
|
|
194
|
+
if (
|
|
195
|
+
typeof entry.byteLength !== "number" ||
|
|
196
|
+
!Number.isInteger(entry.byteLength) ||
|
|
197
|
+
entry.byteLength < 0
|
|
198
|
+
) {
|
|
199
|
+
throw new Error(`Packaged method provenance file byteLength invalid: ${rel}`);
|
|
200
|
+
}
|
|
201
|
+
if (typeof entry.gitBlob !== "string" || !GIT_BLOB_RE.test(entry.gitBlob)) {
|
|
202
|
+
throw new Error(
|
|
203
|
+
`Packaged method provenance file gitBlob must be a 40-char lowercase git object id: ${rel}`,
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
files[rel] = {
|
|
207
|
+
sha256: entry.sha256,
|
|
208
|
+
byteLength: entry.byteLength,
|
|
209
|
+
gitBlob: entry.gitBlob,
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
if (files["SKILL.md"] === undefined) {
|
|
213
|
+
throw new Error(`Packaged method provenance must include SKILL.md`);
|
|
214
|
+
}
|
|
215
|
+
return Object.freeze({
|
|
216
|
+
name: expectedName,
|
|
217
|
+
kind: "role-method-skill",
|
|
218
|
+
packageAdaptation: raw.packageAdaptation,
|
|
219
|
+
upstream: Object.freeze({
|
|
220
|
+
repository: upstream.repository as string,
|
|
221
|
+
path: upstream.path as string,
|
|
222
|
+
commit: upstream.commit,
|
|
223
|
+
...(tag === undefined ? {} : { tag }),
|
|
224
|
+
...(version === undefined ? {} : { version }),
|
|
225
|
+
license: upstream.license as string,
|
|
226
|
+
copyright: upstream.copyright as string,
|
|
227
|
+
attribution: upstream.attribution as string,
|
|
228
|
+
}),
|
|
229
|
+
files: Object.freeze(files),
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Load a package-owned method Skill and verify per-file provenance digests.
|
|
235
|
+
* Does not consult HOME or ambient Skill discovery paths.
|
|
236
|
+
*/
|
|
237
|
+
export async function loadPackagedMethodSkillMaterial(
|
|
238
|
+
packageRoot: string,
|
|
239
|
+
name: PackagedMethodSkillName,
|
|
240
|
+
): Promise<PackagedMethodSkillMaterial> {
|
|
241
|
+
const rootDirectory = resolvePackagedMethodSkillRoot(packageRoot, name);
|
|
242
|
+
const skillPathConfigured = join(rootDirectory, "SKILL.md");
|
|
243
|
+
const provenancePath = join(rootDirectory, "provenance.json");
|
|
244
|
+
|
|
245
|
+
let provenanceRaw: string;
|
|
246
|
+
try {
|
|
247
|
+
provenanceRaw = await readFile(provenancePath, "utf8");
|
|
248
|
+
} catch (error) {
|
|
249
|
+
throw new PackagedMethodSkillUnavailableError(name, provenancePath, error);
|
|
250
|
+
}
|
|
251
|
+
let provenanceJson: unknown;
|
|
252
|
+
try {
|
|
253
|
+
provenanceJson = JSON.parse(provenanceRaw);
|
|
254
|
+
} catch (error) {
|
|
255
|
+
throw new Error(`Packaged method provenance is not valid JSON at ${provenancePath}`, {
|
|
256
|
+
cause: error,
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
const provenance = parseProvenance(provenanceJson, name);
|
|
260
|
+
|
|
261
|
+
// Verify every declared file digest + independent git blob against package bytes (no network).
|
|
262
|
+
for (const [rel, expected] of Object.entries(provenance.files)) {
|
|
263
|
+
const absolute = join(rootDirectory, rel);
|
|
264
|
+
let bytes: Buffer;
|
|
265
|
+
try {
|
|
266
|
+
bytes = await readFile(absolute);
|
|
267
|
+
} catch (error) {
|
|
268
|
+
throw new PackagedMethodSkillUnavailableError(name, absolute, error);
|
|
269
|
+
}
|
|
270
|
+
const actualSha = sha256Hex(bytes);
|
|
271
|
+
const actualBlob = gitBlobOid(bytes);
|
|
272
|
+
if (
|
|
273
|
+
actualSha !== expected.sha256 ||
|
|
274
|
+
bytes.byteLength !== expected.byteLength ||
|
|
275
|
+
actualBlob !== expected.gitBlob
|
|
276
|
+
) {
|
|
277
|
+
throw new Error(
|
|
278
|
+
`Packaged method file digest mismatch for ${name}/${rel}: expected sha256=${expected.sha256} byteLength=${expected.byteLength} gitBlob=${expected.gitBlob}, got sha256=${actualSha} byteLength=${bytes.byteLength} gitBlob=${actualBlob}`,
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
let skillPath: string;
|
|
284
|
+
let raw: string;
|
|
285
|
+
try {
|
|
286
|
+
skillPath = await realpath(skillPathConfigured);
|
|
287
|
+
raw = await readFile(skillPath, "utf8");
|
|
288
|
+
} catch (error) {
|
|
289
|
+
throw new PackagedMethodSkillUnavailableError(name, skillPathConfigured, error);
|
|
290
|
+
}
|
|
291
|
+
const body = stripSkillFrontmatter(raw).trim();
|
|
292
|
+
if (body.length === 0) {
|
|
293
|
+
throw new Error(`Canonical ${name} Skill is empty at ${skillPath}`);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const companionRelativePaths = REQUIRED_COMPANIONS[name].filter(
|
|
297
|
+
(rel) => provenance.files[rel] !== undefined,
|
|
298
|
+
);
|
|
299
|
+
for (const rel of REQUIRED_COMPANIONS[name]) {
|
|
300
|
+
if (provenance.files[rel] === undefined) {
|
|
301
|
+
throw new Error(
|
|
302
|
+
`Packaged method ${name} missing required companion in provenance: ${rel}`,
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return Object.freeze({
|
|
308
|
+
name,
|
|
309
|
+
rootDirectory,
|
|
310
|
+
skillPath,
|
|
311
|
+
raw,
|
|
312
|
+
body,
|
|
313
|
+
provenance,
|
|
314
|
+
companionRelativePaths: Object.freeze([...companionRelativePaths]),
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/** Observed Pi-native method Skill expansion (structured skill block, not free prose). */
|
|
319
|
+
export type ObservedPackagedMethodSkillInvocation = Readonly<{
|
|
320
|
+
name: PackagedMethodSkillName;
|
|
321
|
+
location: string;
|
|
322
|
+
}>;
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Observe one Pi-native packaged method Skill expansion from a user-turn text.
|
|
326
|
+
* Matches the structured `<skill name location>` block format only; rejects ambient
|
|
327
|
+
* home locations that are not in the allowed package path set.
|
|
328
|
+
*/
|
|
329
|
+
export function observePackagedMethodSkillInvocation(
|
|
330
|
+
text: string,
|
|
331
|
+
expected: {
|
|
332
|
+
readonly name: PackagedMethodSkillName;
|
|
333
|
+
readonly allowedLocations: readonly string[];
|
|
334
|
+
},
|
|
335
|
+
): ObservedPackagedMethodSkillInvocation | undefined {
|
|
336
|
+
// Structured skill-block grammar (same shape Pi expands); not free-text classification.
|
|
337
|
+
if (!text.startsWith("<skill name=\"")) return undefined;
|
|
338
|
+
const nameStart = "<skill name=\"".length;
|
|
339
|
+
const nameEnd = text.indexOf("\"", nameStart);
|
|
340
|
+
if (nameEnd <= nameStart) return undefined;
|
|
341
|
+
const name = text.slice(nameStart, nameEnd);
|
|
342
|
+
if (name !== expected.name) return undefined;
|
|
343
|
+
const locationMarker = "\" location=\"";
|
|
344
|
+
if (!text.startsWith(locationMarker, nameEnd)) return undefined;
|
|
345
|
+
const locationStart = nameEnd + locationMarker.length;
|
|
346
|
+
const locationEnd = text.indexOf("\"", locationStart);
|
|
347
|
+
if (locationEnd <= locationStart) return undefined;
|
|
348
|
+
const location = text.slice(locationStart, locationEnd);
|
|
349
|
+
const openTail = ">\n";
|
|
350
|
+
if (!text.startsWith(openTail, locationEnd + 1)) return undefined;
|
|
351
|
+
const closeTag = "\n</skill>";
|
|
352
|
+
const closeAt = text.indexOf(closeTag, locationEnd + 1 + openTail.length);
|
|
353
|
+
if (closeAt === -1) return undefined;
|
|
354
|
+
const afterClose = text.slice(closeAt + closeTag.length);
|
|
355
|
+
if (afterClose.length > 0 && !afterClose.startsWith("\n")) return undefined;
|
|
356
|
+
if (!expected.allowedLocations.includes(location)) return undefined;
|
|
357
|
+
return Object.freeze({ name: expected.name, location });
|
|
358
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/** The single package-owned source for role attendance and settlement metadata. */
|
|
2
|
+
import { COLLECTOR_OUTPUT_TOOL } from "./package-contracts/collector-output.ts";
|
|
3
|
+
import { JUDGE_OUTPUT_TOOL_NAME } from "./package-contracts/judge-output.ts";
|
|
4
|
+
import { REVIEWER_OUTPUT_TOOL_NAME } from "./package-contracts/reviewer-output.ts";
|
|
5
|
+
import { CODER_OUTPUT_TOOL_NAME, FIXER_OUTPUT_TOOL_NAME } from "./package-contracts/worker-output.ts";
|
|
6
|
+
import { DOCTOR_OUTPUT_TOOL_NAME } from "./doctor-contracts.ts";
|
|
7
|
+
import { MERGER_OUTPUT_TOOL_NAME } from "./merger-contracts.ts";
|
|
8
|
+
|
|
9
|
+
export const PACKAGED_ROLE_REGISTRY = [
|
|
10
|
+
{ role: "judge", phases: [null], outputTool: JUDGE_OUTPUT_TOOL_NAME, inputFlag: undefined, phaseFlag: undefined, activationStage: "load-and-install" },
|
|
11
|
+
{ role: "fixer", phases: ["plan", "apply"], outputTool: FIXER_OUTPUT_TOOL_NAME, inputFlag: "ak-fix-packet", phaseFlag: "ak-fixer-phase", activationStage: "load-and-install" },
|
|
12
|
+
{ role: "coder", phases: ["plan", "apply"], outputTool: CODER_OUTPUT_TOOL_NAME, inputFlag: "ak-coder-task", phaseFlag: "ak-coder-phase", activationStage: "load-and-install" },
|
|
13
|
+
{ role: "reviewer", phases: [null], outputTool: REVIEWER_OUTPUT_TOOL_NAME, inputFlag: undefined, phaseFlag: undefined, activationStage: "load-and-install" },
|
|
14
|
+
{ role: "collector", phases: [null], outputTool: COLLECTOR_OUTPUT_TOOL, inputFlag: "ak-collector-repo", phaseFlag: undefined, activationStage: "load-and-install" },
|
|
15
|
+
{ role: "doctor", phases: [null], outputTool: DOCTOR_OUTPUT_TOOL_NAME, inputFlag: "ak-doctor-case", phaseFlag: undefined, activationStage: "load-and-install" },
|
|
16
|
+
{ role: "merger", phases: [null], outputTool: MERGER_OUTPUT_TOOL_NAME, inputFlag: "ak-merger-input", phaseFlag: undefined, activationStage: "prepare-git-and-install" },
|
|
17
|
+
] as const;
|
|
18
|
+
|
|
19
|
+
export type PackagedRole = (typeof PACKAGED_ROLE_REGISTRY)[number]["role"];
|
|
20
|
+
export type PackagedRoleMetadata = (typeof PACKAGED_ROLE_REGISTRY)[number];
|
|
21
|
+
|
|
22
|
+
export function packagedRoleMetadata(role: string): PackagedRoleMetadata | undefined {
|
|
23
|
+
return PACKAGED_ROLE_REGISTRY.find((entry) => entry.role === role);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function packagedRoleInputFlag(role: string): string | undefined {
|
|
27
|
+
return packagedRoleMetadata(role)?.inputFlag;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function packagedRolePhaseFlag(role: string): string | undefined {
|
|
31
|
+
return packagedRoleMetadata(role)?.phaseFlag;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function packagedRoleOutputTool(role: string): string | undefined {
|
|
35
|
+
return packagedRoleMetadata(role)?.outputTool;
|
|
36
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** Shared public CLI usage error (structural reject before admission). */
|
|
2
|
+
export class CliUsageError extends Error {
|
|
3
|
+
readonly code = "AK_ROLE_USAGE";
|
|
4
|
+
constructor(message: string, options?: { cause?: unknown }) {
|
|
5
|
+
super(
|
|
6
|
+
message,
|
|
7
|
+
options?.cause === undefined ? undefined : { cause: options.cause },
|
|
8
|
+
);
|
|
9
|
+
this.name = "CliUsageError";
|
|
10
|
+
}
|
|
11
|
+
}
|