mirror of
https://github.com/SikongJueluo/pi-extensions.git
synced 2026-10-05 20:02:55 +08:00
168 lines
5.8 KiB
TypeScript
168 lines
5.8 KiB
TypeScript
/**
|
|
* PIEXTENSIO-21 owner action CLI (offline, package-local).
|
|
*
|
|
* Appends one promotion record to the Judge-owned records file
|
|
* (~/.pi/agent/extensions/pi-permission-ai-judge/promotion-records.jsonl).
|
|
* Never imported by online modules; no npm bin. The three record kinds map
|
|
* one-to-one to the Enforce truth-table promotion gates:
|
|
*
|
|
* cohort_qualified — after a declared replacement cohort meets the frozen
|
|
* floor, citing the cohort id + report reference
|
|
* owner_approval — the owner's explicit approval of that exact candidate
|
|
* activation — the distinct, explicit activation act
|
|
*
|
|
* Fail-closed contract: a record qualifies only its exact candidate
|
|
* identity; the judge re-derives identity from the live runtime, so a
|
|
* mismatched or malformed record is inert and malformed files close all
|
|
* gates. There is no CLI to *remove* authority records by design —
|
|
* rollback to Shadow is the config `mode` switch, and the append-only
|
|
* trail keeps the promotion history auditable.
|
|
*
|
|
* Usage:
|
|
* npx tsx tools/promotion-record.ts --kind cohort_qualified \
|
|
* --provider openai-codex --model gpt-5.6-sol --api openai-codex-responses \
|
|
* [--timeout-cohort 30000] --basis "cohort <id>; report docs/testing/..."
|
|
*
|
|
* judge/permission-system/prompt/tool-schema/review-schema versions are
|
|
* taken from the live package (src/prompt.ts + package constants) so the
|
|
* recorded identity cannot drift from the code that will check it.
|
|
*/
|
|
|
|
import { getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
import {
|
|
appendPromotionRecord,
|
|
promotionRecordsPath,
|
|
type CandidateIdentity,
|
|
type PromotionRecordKind,
|
|
} from "../src/promotion";
|
|
import { PROMPT_VERSION, TOOL_SCHEMA_VERSION } from "../src/prompt";
|
|
|
|
const JUDGE_IDENTITY = "@sikongjueluo/pi-permission-ai-judge@0.0.1";
|
|
const PERMISSION_SYSTEM_VERSION = "25.4.0";
|
|
const REVIEW_SCHEMA_VERSION = "1";
|
|
|
|
interface CliOptions {
|
|
kind: PromotionRecordKind;
|
|
provider: string;
|
|
model: string;
|
|
api: string;
|
|
timeoutCohort: "default" | number;
|
|
basis: string;
|
|
}
|
|
|
|
function usage(): string {
|
|
return [
|
|
"usage: promotion-record --kind <cohort_qualified|owner_approval|activation>",
|
|
" --provider <p> --model <m> --api <api>",
|
|
" [--timeout-cohort default|<ms>] --basis <text>",
|
|
].join("\n");
|
|
}
|
|
|
|
function parseArgs(argv: readonly string[]): CliOptions | { error: string } {
|
|
const args = argv.slice(2);
|
|
const opts: Partial<CliOptions> = {};
|
|
for (let i = 0; i < args.length; i += 1) {
|
|
const arg = args[i] as string;
|
|
const value = args[i + 1];
|
|
if (
|
|
arg === "--kind" ||
|
|
arg === "--provider" ||
|
|
arg === "--model" ||
|
|
arg === "--api" ||
|
|
arg === "--basis"
|
|
) {
|
|
if (value === undefined) return { error: `${arg} requires a value` };
|
|
if (arg === "--kind") {
|
|
if (
|
|
value !== "cohort_qualified" &&
|
|
value !== "owner_approval" &&
|
|
value !== "activation"
|
|
) {
|
|
return { error: `unknown kind: ${value}` };
|
|
}
|
|
opts.kind = value;
|
|
}
|
|
if (arg === "--provider") opts.provider = value;
|
|
if (arg === "--model") opts.model = value;
|
|
if (arg === "--api") opts.api = value;
|
|
if (arg === "--basis") opts.basis = value;
|
|
i += 1;
|
|
continue;
|
|
}
|
|
if (arg === "--timeout-cohort") {
|
|
if (value === undefined) return { error: `${arg} requires a value` };
|
|
if (value === "default") {
|
|
opts.timeoutCohort = "default";
|
|
} else {
|
|
const n = Number(value);
|
|
if (!Number.isInteger(n)) {
|
|
return { error: `--timeout-cohort must be default or an integer` };
|
|
}
|
|
opts.timeoutCohort = n;
|
|
}
|
|
i += 1;
|
|
continue;
|
|
}
|
|
return { error: `unknown option: ${arg}\n${usage()}` };
|
|
}
|
|
if (
|
|
opts.kind === undefined ||
|
|
opts.provider === undefined ||
|
|
opts.model === undefined ||
|
|
opts.api === undefined ||
|
|
opts.basis === undefined ||
|
|
opts.basis.length === 0
|
|
) {
|
|
return { error: usage() };
|
|
}
|
|
return {
|
|
kind: opts.kind,
|
|
provider: opts.provider,
|
|
model: opts.model,
|
|
api: opts.api,
|
|
timeoutCohort: opts.timeoutCohort ?? "default",
|
|
basis: opts.basis,
|
|
};
|
|
}
|
|
|
|
function main(): number {
|
|
const parsed = parseArgs(process.argv);
|
|
if ("error" in parsed) {
|
|
process.stderr.write(`${parsed.error}\n`);
|
|
return 1;
|
|
}
|
|
const identity: CandidateIdentity = {
|
|
judge: JUDGE_IDENTITY,
|
|
permissionSystem: PERMISSION_SYSTEM_VERSION,
|
|
provider: parsed.provider,
|
|
model: parsed.model,
|
|
api: parsed.api,
|
|
promptVersion: PROMPT_VERSION,
|
|
toolSchemaVersion: TOOL_SCHEMA_VERSION,
|
|
reviewSchemaVersion: REVIEW_SCHEMA_VERSION,
|
|
timeoutCohort: parsed.timeoutCohort,
|
|
};
|
|
const agentDir = getAgentDir();
|
|
const error = appendPromotionRecord({
|
|
agentDir,
|
|
record: {
|
|
kind: parsed.kind,
|
|
candidateIdentity: identity,
|
|
recordedAt: new Date().toISOString(),
|
|
basis: parsed.basis,
|
|
},
|
|
});
|
|
if (error !== null) {
|
|
process.stderr.write(`${error}\n`);
|
|
return 1;
|
|
}
|
|
process.stdout.write(
|
|
`appended ${parsed.kind} record to ${promotionRecordsPath(agentDir)}\n` +
|
|
`identity: ${JSON.stringify(identity)}\n` +
|
|
`note: records load at session start; restart sessions to pick this up\n`,
|
|
);
|
|
return 0;
|
|
}
|
|
|
|
process.exit(main());
|