@akagilnc/pi-workflow-roles 0.1.3758 → 0.1.3783
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 +3 -2
- package/README.zh-CN.md +3 -2
- package/dist/acp-host/production-host.js +1735 -948
- package/dist/collector-config.js +0 -1
- package/dist/collector-github.js +199 -2
- package/dist/collector-identity.js +128 -41
- package/dist/collector-ledger.js +38 -9
- package/dist/collector-receipt.js +19 -7
- package/dist/collector-role.js +330 -369
- package/dist/collector-target.js +169 -0
- package/dist/collector-tool-schemas.js +51 -14
- package/dist/headless-host/description.js +77 -0
- package/dist/headless-host/mcp-relay.mjs +119 -0
- package/dist/headless-host/production-host.js +26590 -0
- package/dist/host-descriptions.js +44 -3
- package/dist/package-contracts/collector-output.js +32 -0
- package/dist/package-contracts/terminating-infrastructure.js +13 -12
- package/dist/pi/role-turn-host.js +1 -2
- package/dist/public-cli/github-remote.js +45 -0
- package/dist/public-cli/invocation.js +53 -54
- package/dist/public-cli/load-production-external-host.js +19 -0
- package/dist/public-cli/load-production-headless-host.js +36 -0
- package/dist/public-cli/main.js +857 -310
- package/dist/public-cli/option-definitions.js +6 -4
- package/dist/public-cli/run-lifecycle.js +3 -3
- package/dist/public-cli/settlement.js +83 -6
- package/dist/public-role-summons.js +40 -5
- package/dist/role-runtime.js +137 -7
- package/dist/submission-correctable-error.js +24 -0
- package/extensions/role-runtime.ts +0 -1
- package/package.json +1 -1
- package/scripts/build-package.mjs +28 -0
- package/src/acp-host/role-envelope.ts +146 -103
- package/src/acp-host/role-turn-host.ts +12 -0
- package/src/collector-config.ts +0 -1
- package/src/collector-github.ts +236 -2
- package/src/collector-identity.ts +148 -40
- package/src/collector-ledger.ts +48 -10
- package/src/collector-receipt.ts +33 -14
- package/src/collector-role.ts +376 -450
- package/src/collector-target.ts +207 -0
- package/src/collector-tool-schemas.ts +62 -15
- package/src/headless-host/description.ts +123 -0
- package/src/headless-host/production-host.ts +75 -0
- package/src/headless-host/role-turn-host.ts +424 -0
- package/src/host-contracts.ts +2 -1
- package/src/host-descriptions.ts +53 -6
- package/src/package-contracts/collector-output.ts +72 -0
- package/src/package-contracts/terminating-infrastructure.ts +24 -13
- package/src/pi/role-turn-host.ts +1 -2
- package/src/public-cli/cli.ts +12 -3
- package/src/public-cli/collector-run.ts +3 -2
- package/src/public-cli/github-remote.ts +45 -0
- package/src/public-cli/invocation.ts +60 -59
- package/src/public-cli/load-production-external-host.ts +29 -0
- package/src/public-cli/load-production-headless-host.ts +48 -0
- package/src/public-cli/main.ts +5 -0
- package/src/public-cli/option-definitions.ts +6 -4
- package/src/public-cli/run-lifecycle.ts +2 -2
- package/src/public-cli/settlement.ts +82 -6
- package/src/public-role-summons.ts +62 -9
- package/src/role-runtime.ts +166 -13
- package/src/submission-correctable-error.ts +38 -0
|
@@ -45,15 +45,56 @@ export const HOST_DESCRIPTIONS = Object.freeze({
|
|
|
45
45
|
}),
|
|
46
46
|
}),
|
|
47
47
|
});
|
|
48
|
+
/**
|
|
49
|
+
* Headless CLI family (#645). Claude is the first row; codex (#646) adds another.
|
|
50
|
+
* fixedArgs: print mode, isolation without `--bare` (OAuth stays), full permissions.
|
|
51
|
+
* `--setting-sources` empty = load no user/project/local CLAUDE.md/hooks/skills
|
|
52
|
+
* (role envelope is delivered via `--system-prompt` wholesale replace).
|
|
53
|
+
* `--strict-mcp-config` with no `--mcp-config` drops operator MCP + claude.ai connectors.
|
|
54
|
+
*/
|
|
55
|
+
export const HEADLESS_HOST_DESCRIPTIONS = Object.freeze({
|
|
56
|
+
"claude": Object.freeze({
|
|
57
|
+
binaryFromHome: Object.freeze([".local", "bin", "claude"]),
|
|
58
|
+
sessionBindingFile: "claude-headless-session.json",
|
|
59
|
+
fixedArgs: Object.freeze([
|
|
60
|
+
// One result envelope (not stream-json): typed receipt only; no event-stream copy.
|
|
61
|
+
"--output-format", "json",
|
|
62
|
+
"--permission-mode", "bypassPermissions",
|
|
63
|
+
// Empty sources: no user/project/local operator surface (envelope owns materials).
|
|
64
|
+
"--setting-sources", "",
|
|
65
|
+
// With adapter-supplied --mcp-config only (AK relay); drops operator + claude.ai MCP.
|
|
66
|
+
"--strict-mcp-config",
|
|
67
|
+
]),
|
|
68
|
+
promptFlag: "-p",
|
|
69
|
+
modelFlag: "--model",
|
|
70
|
+
effortFlag: "--effort",
|
|
71
|
+
// File path keeps large role envelopes off ARG_MAX.
|
|
72
|
+
systemPromptFlag: "--system-prompt-file",
|
|
73
|
+
jsonSchemaFlag: "--json-schema",
|
|
74
|
+
mcpConfigFlag: "--mcp-config",
|
|
75
|
+
sessionIdFlag: "--session-id",
|
|
76
|
+
resumeFlag: "--resume",
|
|
77
|
+
}),
|
|
78
|
+
});
|
|
48
79
|
export function lookupHostDescription(host) {
|
|
49
80
|
return Object.hasOwn(HOST_DESCRIPTIONS, host) ? HOST_DESCRIPTIONS[host] : undefined;
|
|
50
81
|
}
|
|
82
|
+
export function lookupHeadlessHostDescription(host) {
|
|
83
|
+
return Object.hasOwn(HEADLESS_HOST_DESCRIPTIONS, host) ? HEADLESS_HOST_DESCRIPTIONS[host] : undefined;
|
|
84
|
+
}
|
|
85
|
+
export function lookupHostFamily(host) {
|
|
86
|
+
if (lookupHostDescription(host) !== undefined)
|
|
87
|
+
return "acp";
|
|
88
|
+
if (lookupHeadlessHostDescription(host) !== undefined)
|
|
89
|
+
return "headless";
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
51
92
|
export function packagedExternalHostNames() {
|
|
52
|
-
return Object.keys(HOST_DESCRIPTIONS);
|
|
93
|
+
return [...Object.keys(HOST_DESCRIPTIONS), ...Object.keys(HEADLESS_HOST_DESCRIPTIONS)];
|
|
53
94
|
}
|
|
54
|
-
/** Legal persistent/invocation host: default pi, or a table key. */
|
|
95
|
+
/** Legal persistent/invocation host: default pi, or a table key (any family). */
|
|
55
96
|
export function assertRegisteredHostName(host) {
|
|
56
|
-
if (host === DEFAULT_ROLE_TURN_HOST ||
|
|
97
|
+
if (host === DEFAULT_ROLE_TURN_HOST || lookupHostFamily(host) !== undefined) {
|
|
57
98
|
return host;
|
|
58
99
|
}
|
|
59
100
|
throw new Error(`unregistered host: ${host}`);
|
|
@@ -25,10 +25,16 @@ function validateAcceptedCollectorReceipt(value) {
|
|
|
25
25
|
materials: records(safeGet(group, "materials")),
|
|
26
26
|
findings: records(safeGet(group, "findings"))
|
|
27
27
|
}));
|
|
28
|
+
const unfinishedRaw = safeGet(value, "unfinishedReasons");
|
|
29
|
+
const unfinishedReasons = strings(unfinishedRaw);
|
|
30
|
+
const submissionProjection = projectSubmissionProjection(safeGet(value, "submissionProjection"));
|
|
31
|
+
const prStateRaw = safeGet(value, "prState");
|
|
32
|
+
const prState = typeof prStateRaw === "string" ? prStateRaw : void 0;
|
|
28
33
|
return {
|
|
29
34
|
host: safeGet(value, "host"),
|
|
30
35
|
repository: safeGet(value, "repository"),
|
|
31
36
|
prNumber: safeGet(value, "prNumber"),
|
|
37
|
+
...prState === void 0 ? {} : { prState },
|
|
32
38
|
manifestDigest: safeGet(value, "manifestDigest"),
|
|
33
39
|
activationTime: safeGet(value, "activationTime"),
|
|
34
40
|
deadlineTime: safeGet(value, "deadlineTime"),
|
|
@@ -36,6 +42,8 @@ function validateAcceptedCollectorReceipt(value) {
|
|
|
36
42
|
finalSnapshotId: safeGet(value, "finalSnapshotId"),
|
|
37
43
|
targetHead: safeGet(value, "targetHead"),
|
|
38
44
|
groups,
|
|
45
|
+
...unfinishedReasons.length > 0 ? { unfinishedReasons } : {},
|
|
46
|
+
...submissionProjection === void 0 ? {} : { submissionProjection },
|
|
39
47
|
requestAttempts: records(safeGet(value, "requestAttempts")),
|
|
40
48
|
snapshots: records(safeGet(value, "snapshots")).map((snapshot) => ({
|
|
41
49
|
snapshotId: safeGet(snapshot, "snapshotId"),
|
|
@@ -55,6 +63,30 @@ function validateAcceptedCollectorReceipt(value) {
|
|
|
55
63
|
evidenceRecords: records(safeGet(value, "evidenceRecords")).map((record) => ({ evidenceId: safeGet(record, "evidenceId"), kind: safeGet(record, "kind"), versionId: safeGet(record, "versionId"), contentDigest: safeGet(record, "contentDigest"), firstObservedAt: safeGet(record, "firstObservedAt"), githubId: safeGet(record, "githubId"), authorLogin: safeGet(record, "authorLogin"), htmlUrl: safeGet(record, "htmlUrl"), authoritativeTime: safeGet(record, "authoritativeTime") }))
|
|
56
64
|
};
|
|
57
65
|
}
|
|
66
|
+
function projectSubmissionProjection(raw) {
|
|
67
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return void 0;
|
|
68
|
+
const p = raw;
|
|
69
|
+
const out = {};
|
|
70
|
+
if (p["findingsSource"] === "absent" || p["findingsSource"] === "array" || p["findingsSource"] === "unreadable") {
|
|
71
|
+
out.findingsSource = p["findingsSource"];
|
|
72
|
+
}
|
|
73
|
+
if (typeof p["findingsProjectedCount"] === "number") {
|
|
74
|
+
out.findingsProjectedCount = p["findingsProjectedCount"];
|
|
75
|
+
}
|
|
76
|
+
if (typeof p["findingsUnprojected"] === "boolean") {
|
|
77
|
+
out.findingsUnprojected = p["findingsUnprojected"];
|
|
78
|
+
}
|
|
79
|
+
if (p["unfinishedReasonsSource"] === "absent" || p["unfinishedReasonsSource"] === "array" || p["unfinishedReasonsSource"] === "unreadable") {
|
|
80
|
+
out.unfinishedReasonsSource = p["unfinishedReasonsSource"];
|
|
81
|
+
}
|
|
82
|
+
if (typeof p["unfinishedReasonsProjectedCount"] === "number") {
|
|
83
|
+
out.unfinishedReasonsProjectedCount = p["unfinishedReasonsProjectedCount"];
|
|
84
|
+
}
|
|
85
|
+
if (typeof p["unfinishedReasonsUnprojected"] === "boolean") {
|
|
86
|
+
out.unfinishedReasonsUnprojected = p["unfinishedReasonsUnprojected"];
|
|
87
|
+
}
|
|
88
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
89
|
+
}
|
|
58
90
|
export {
|
|
59
91
|
COLLECTOR_ACCEPTED_TEXT,
|
|
60
92
|
COLLECTOR_HOST,
|
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
import { Type } from "typebox";
|
|
2
2
|
const INFRASTRUCTURE_FAILURE_DECLARATION_KEY = "infrastructureFailure";
|
|
3
3
|
const INFRASTRUCTURE_FAILURE_DIAGNOSTIC_KEY = "diagnostic";
|
|
4
|
+
const infrastructureFailureNested = Type.Object(
|
|
5
|
+
{
|
|
6
|
+
[INFRASTRUCTURE_FAILURE_DIAGNOSTIC_KEY]: Type.Unknown({
|
|
7
|
+
description: "\u975E\u7A7A\u57FA\u7840\u8BBE\u65BD\u5931\u8D25\u8BCA\u65AD\u5B57\u7B26\u4E32\u3002\u65E0\u5931\u8D25\u65F6\u5FC5\u987B\u7701\u7565\u6574\u4E2A infrastructureFailure\u3002\u5F62\u72B6\u6307\u5F15\uFF0C\u975E schema \u95F8\u3002"
|
|
8
|
+
})
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
additionalProperties: true,
|
|
12
|
+
description: "\u57FA\u7840\u8BBE\u65BD\u771F\u5B9E\u5931\u8D25\u58F0\u660E\uFF08\u5982\u9700\uFF09\u3002\u89C4\u8303\u5F62\uFF1A{ diagnostic: \u975E\u7A7A\u8BCA\u65AD\u5B57\u7B26\u4E32 }\uFF1B\u65E0\u5931\u8D25\u65F6\u5FC5\u987B\u7701\u7565\u3002\u5F62\u72B6\u6307\u5F15\uFF0C\u975E schema \u95F8\u3002"
|
|
13
|
+
}
|
|
14
|
+
);
|
|
15
|
+
infrastructureFailureNested.required = [];
|
|
4
16
|
const infrastructureFailureDeclarationSchema = Type.Object(
|
|
5
17
|
{
|
|
6
|
-
[INFRASTRUCTURE_FAILURE_DECLARATION_KEY]:
|
|
7
|
-
{
|
|
8
|
-
[INFRASTRUCTURE_FAILURE_DIAGNOSTIC_KEY]: Type.String({
|
|
9
|
-
minLength: 1,
|
|
10
|
-
description: "\u975E\u7A7A\u57FA\u7840\u8BBE\u65BD\u5931\u8D25\u8BCA\u65AD"
|
|
11
|
-
})
|
|
12
|
-
},
|
|
13
|
-
{
|
|
14
|
-
additionalProperties: true,
|
|
15
|
-
description: "\u57FA\u7840\u8BBE\u65BD\u5931\u8D25\u58F0\u660E"
|
|
16
|
-
}
|
|
17
|
-
)
|
|
18
|
+
[INFRASTRUCTURE_FAILURE_DECLARATION_KEY]: infrastructureFailureNested
|
|
18
19
|
},
|
|
19
20
|
{ additionalProperties: true }
|
|
20
21
|
);
|
|
@@ -91,8 +91,7 @@ function buildActivationFlagArgs(activation) {
|
|
|
91
91
|
"collector",
|
|
92
92
|
"--ak-collector-repo",
|
|
93
93
|
activation.repo,
|
|
94
|
-
"--ak-collector-pr",
|
|
95
|
-
activation.pr,
|
|
94
|
+
...(activation.pr === undefined ? [] : ["--ak-collector-pr", activation.pr]),
|
|
96
95
|
...(activation.requestManifestPath === undefined
|
|
97
96
|
? []
|
|
98
97
|
: ["--ak-collector-request-manifest", activation.requestManifestPath]),
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared github.com remote URL → owner/repo parse (#676 B/D7).
|
|
3
|
+
* One authority for collector admission origin and upstream head owner.
|
|
4
|
+
*/
|
|
5
|
+
/** Parse owner from a github.com remote URL; undefined when not a GitHub owner/repo remote. */
|
|
6
|
+
export function ownerFromGitHubRemoteUrl(remoteUrl) {
|
|
7
|
+
const ownerRepo = ownerRepoFromGitHubRemoteUrl(remoteUrl);
|
|
8
|
+
if (ownerRepo === undefined)
|
|
9
|
+
return undefined;
|
|
10
|
+
return ownerRepo.split("/")[0].toLowerCase();
|
|
11
|
+
}
|
|
12
|
+
/** Parse owner/repo identity from a github.com remote URL; undefined when not exact. */
|
|
13
|
+
export function ownerRepoFromGitHubRemoteUrl(remoteUrl) {
|
|
14
|
+
const trimmed = remoteUrl.trim();
|
|
15
|
+
// git@github.com:owner/repo.git — exact owner/repo identity only.
|
|
16
|
+
const scp = /^git@github\.com:([^/\s]+)\/([^/\s]+?)(?:\.git)?$/i.exec(trimmed);
|
|
17
|
+
if (scp) {
|
|
18
|
+
return `${scp[1]}/${stripGitSuffix(scp[2])}`;
|
|
19
|
+
}
|
|
20
|
+
// ssh://git@github.com/owner/repo(.git) — exact owner/repo identity only.
|
|
21
|
+
const ssh = /^ssh:\/\/git@github\.com\/([^/\s]+)\/([^/\s]+?)(?:\.git)?\/?$/i.exec(trimmed);
|
|
22
|
+
if (ssh) {
|
|
23
|
+
return `${ssh[1]}/${stripGitSuffix(ssh[2])}`;
|
|
24
|
+
}
|
|
25
|
+
// https://github.com/owner/repo(.git) and git://github.com/... — exact two-segment path.
|
|
26
|
+
let parsed;
|
|
27
|
+
try {
|
|
28
|
+
parsed = new URL(trimmed);
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
if (!/^github\.com$/i.test(parsed.hostname))
|
|
34
|
+
return undefined;
|
|
35
|
+
// Non-identity URL material (query/hash/extra path) is not a repository remote.
|
|
36
|
+
if (parsed.search !== "" || parsed.hash !== "")
|
|
37
|
+
return undefined;
|
|
38
|
+
const parts = parsed.pathname.split("/").filter((p) => p.length > 0);
|
|
39
|
+
if (parts.length !== 2)
|
|
40
|
+
return undefined;
|
|
41
|
+
return `${parts[0]}/${stripGitSuffix(parts[1])}`;
|
|
42
|
+
}
|
|
43
|
+
function stripGitSuffix(name) {
|
|
44
|
+
return name.toLowerCase().endsWith(".git") ? name.slice(0, -4) : name;
|
|
45
|
+
}
|
|
@@ -10,7 +10,9 @@ import { activationBookDirectory, ensureRealDirectoryTree, homeFromRunDirectory,
|
|
|
10
10
|
import { resolveBookKeyFromGit } from "../activation-ledger-git.js";
|
|
11
11
|
import { readRunTicketNumber } from "../run-ticket-number.js";
|
|
12
12
|
import { loadDoctorCase, } from "../doctor-evidence.js";
|
|
13
|
-
import {
|
|
13
|
+
import { emptyCollectorManifest, loadCollectorManifest, parseCollectorPrNumber, parseCollectorRepository, } from "../collector-config.js";
|
|
14
|
+
import { resolveCollectorTarget } from "../collector-target.js";
|
|
15
|
+
import { ownerRepoFromGitHubRemoteUrl } from "./github-remote.js";
|
|
14
16
|
import { FixerPacketValidationError, parseFixerPrerequisites, } from "../package-contracts/fixer-packet.js";
|
|
15
17
|
import { createProductionMergerGitState } from "../merger-git-state.js";
|
|
16
18
|
import { validateMergerInput, } from "../merger-contracts.js";
|
|
@@ -1020,10 +1022,11 @@ export function parseCollectorArgv(args) {
|
|
|
1020
1022
|
}
|
|
1021
1023
|
positional.push(token);
|
|
1022
1024
|
}
|
|
1023
|
-
// Unconditional required
|
|
1025
|
+
// Unconditional required from typed table via shared consumer (#342).
|
|
1026
|
+
// #676 D1: --pr is optional; ambiguous targets reject at admission, not by guessing.
|
|
1024
1027
|
options.assertRequired();
|
|
1025
1028
|
return {
|
|
1026
|
-
prNumber: prNumber,
|
|
1029
|
+
...(prNumber === undefined ? {} : { prNumber }),
|
|
1027
1030
|
instruction: positional.join(" "),
|
|
1028
1031
|
attachmentPaths,
|
|
1029
1032
|
...(project === undefined ? {} : { project }),
|
|
@@ -1034,6 +1037,7 @@ export function parseCollectorArgv(args) {
|
|
|
1034
1037
|
/**
|
|
1035
1038
|
* Resolve owner/repo from the project's `origin` remote (github.com only).
|
|
1036
1039
|
* Supports https and SSH GitHub URL shapes; never scrapes instruction prose.
|
|
1040
|
+
* Missing origin / non-github remote → usage. Git execution failure → true cause (exit 1).
|
|
1037
1041
|
*/
|
|
1038
1042
|
export function resolveGitHubRemoteRepository(projectRoot) {
|
|
1039
1043
|
let remoteUrl;
|
|
@@ -1045,7 +1049,13 @@ export function resolveGitHubRemoteRepository(projectRoot) {
|
|
|
1045
1049
|
}).trim();
|
|
1046
1050
|
}
|
|
1047
1051
|
catch (error) {
|
|
1048
|
-
|
|
1052
|
+
// git remote get-url exit 2 = no such remote; other failures keep true cause.
|
|
1053
|
+
if (isGitRemoteMissing(error)) {
|
|
1054
|
+
throw new CliUsageError("collector requires a github.com origin remote or an explicit --repo owner/repo", { cause: error });
|
|
1055
|
+
}
|
|
1056
|
+
throw new Error("collector git failed: cannot read origin remote URL", {
|
|
1057
|
+
cause: error instanceof Error ? error : new Error(String(error)),
|
|
1058
|
+
});
|
|
1049
1059
|
}
|
|
1050
1060
|
if (remoteUrl.length === 0) {
|
|
1051
1061
|
throw new CliUsageError("collector requires a github.com origin remote or an explicit --repo owner/repo");
|
|
@@ -1062,55 +1072,34 @@ export function resolveGitHubRemoteRepository(projectRoot) {
|
|
|
1062
1072
|
throw new CliUsageError(detail, { cause: error });
|
|
1063
1073
|
}
|
|
1064
1074
|
}
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
if (ssh) {
|
|
1075
|
-
return `${ssh[1]}/${stripGitSuffix(ssh[2])}`;
|
|
1076
|
-
}
|
|
1077
|
-
// https://github.com/owner/repo(.git) and git://github.com/... — exact two-segment path.
|
|
1078
|
-
let parsed;
|
|
1079
|
-
try {
|
|
1080
|
-
parsed = new URL(trimmed);
|
|
1081
|
-
}
|
|
1082
|
-
catch {
|
|
1083
|
-
return undefined;
|
|
1084
|
-
}
|
|
1085
|
-
if (!/^github\.com$/i.test(parsed.hostname))
|
|
1086
|
-
return undefined;
|
|
1087
|
-
// Non-identity URL material (query/hash/extra path) is not a repository remote.
|
|
1088
|
-
if (parsed.search !== "" || parsed.hash !== "")
|
|
1089
|
-
return undefined;
|
|
1090
|
-
const parts = parsed.pathname.split("/").filter((p) => p.length > 0);
|
|
1091
|
-
if (parts.length !== 2)
|
|
1092
|
-
return undefined;
|
|
1093
|
-
return `${parts[0]}/${stripGitSuffix(parts[1])}`;
|
|
1094
|
-
}
|
|
1095
|
-
function stripGitSuffix(name) {
|
|
1096
|
-
return name.toLowerCase().endsWith(".git") ? name.slice(0, -4) : name;
|
|
1075
|
+
/**
|
|
1076
|
+
* git remote get-url: exit 2 = no such remote on common git (missing config).
|
|
1077
|
+
* Other statuses keep true cause — do not broaden into usage (#676 B).
|
|
1078
|
+
*/
|
|
1079
|
+
function isGitRemoteMissing(error) {
|
|
1080
|
+
if (typeof error !== "object" || error === null)
|
|
1081
|
+
return false;
|
|
1082
|
+
const status = error.status;
|
|
1083
|
+
return status === 2;
|
|
1097
1084
|
}
|
|
1098
1085
|
/**
|
|
1099
1086
|
* Admit a Collector Role run: assemble the retained leg manifest from typed
|
|
1100
|
-
* declarations, resolve repository
|
|
1101
|
-
*
|
|
1087
|
+
* declarations, resolve repository + PR target (#676 D1), and place the session under #78.
|
|
1088
|
+
* Explicit PR is not preflighted for existence; context resolution uses online association.
|
|
1102
1089
|
*/
|
|
1103
1090
|
export async function admitCollectorInvocation(options) {
|
|
1104
1091
|
if (options.project !== undefined) {
|
|
1105
1092
|
requireOptionPath("--project", options.project);
|
|
1106
1093
|
}
|
|
1107
|
-
let
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1094
|
+
let explicitPrNumber;
|
|
1095
|
+
if (options.prNumber !== undefined) {
|
|
1096
|
+
try {
|
|
1097
|
+
explicitPrNumber = parseCollectorPrNumber(options.prNumber);
|
|
1098
|
+
}
|
|
1099
|
+
catch (error) {
|
|
1100
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
1101
|
+
throw new CliUsageError(detail, { cause: error });
|
|
1102
|
+
}
|
|
1114
1103
|
}
|
|
1115
1104
|
const projectRoot = resolve(options.project ?? options.cwd);
|
|
1116
1105
|
let repository;
|
|
@@ -1149,14 +1138,23 @@ export async function admitCollectorInvocation(options) {
|
|
|
1149
1138
|
const attachmentsDirectory = join(runDirectory, "attachments");
|
|
1150
1139
|
ensureRealDirectoryTree(ledgerHome, sessionDirectory);
|
|
1151
1140
|
ensureRealDirectoryTree(ledgerHome, attachmentsDirectory);
|
|
1141
|
+
// #676 A: freeze task materials BEFORE target resolution so the role receives
|
|
1142
|
+
// real instruction + attachments. Admission binds only explicit --pr or unique
|
|
1143
|
+
// head/commit association — task-text scrape is not a target lock.
|
|
1152
1144
|
const attachments = await freezeAttachments(options.attachmentPaths ?? [], attachmentsDirectory);
|
|
1145
|
+
const instruction = options.instruction ?? "";
|
|
1146
|
+
const instructionEmpty = instruction.trim() === "";
|
|
1147
|
+
const target = await resolveCollectorTarget({
|
|
1148
|
+
projectRoot,
|
|
1149
|
+
repository,
|
|
1150
|
+
...(explicitPrNumber === undefined ? {} : { explicitPrNumber }),
|
|
1151
|
+
});
|
|
1152
|
+
const prNumber = target.kind === "bound" ? target.prNumber : undefined;
|
|
1153
1153
|
let requestManifestPath;
|
|
1154
1154
|
if (manifestCanonicalJson !== undefined) {
|
|
1155
1155
|
requestManifestPath = join(runDirectory, "request-manifest.json");
|
|
1156
1156
|
await writeFile(requestManifestPath, manifestCanonicalJson, "utf8");
|
|
1157
1157
|
}
|
|
1158
|
-
const instruction = options.instruction ?? "";
|
|
1159
|
-
const instructionEmpty = instruction.trim() === "";
|
|
1160
1158
|
const admitted = {
|
|
1161
1159
|
role: "collector",
|
|
1162
1160
|
runId,
|
|
@@ -1166,7 +1164,7 @@ export async function admitCollectorInvocation(options) {
|
|
|
1166
1164
|
principal,
|
|
1167
1165
|
instruction,
|
|
1168
1166
|
instructionEmpty,
|
|
1169
|
-
prNumber,
|
|
1167
|
+
...(prNumber === undefined ? {} : { prNumber }),
|
|
1170
1168
|
repository: repository.canonical,
|
|
1171
1169
|
repositoryDisplay: repository.display,
|
|
1172
1170
|
...(requestManifestPath === undefined ? {} : { requestManifestPath }),
|
|
@@ -1196,19 +1194,20 @@ export async function admitCollectorInvocation(options) {
|
|
|
1196
1194
|
runDirectory,
|
|
1197
1195
|
principal,
|
|
1198
1196
|
admittedRequestPath,
|
|
1199
|
-
prNumber,
|
|
1197
|
+
...(prNumber === undefined ? {} : { prNumber }),
|
|
1200
1198
|
repository,
|
|
1201
1199
|
...(requestManifestPath === undefined ? {} : { requestManifestPath }),
|
|
1202
1200
|
manifestDigest,
|
|
1203
1201
|
};
|
|
1204
1202
|
}
|
|
1205
1203
|
/**
|
|
1206
|
-
* Collector
|
|
1207
|
-
*
|
|
1204
|
+
* #676 A: Collector consumes the real call task + frozen attachments so the role
|
|
1205
|
+
* can identify issue/PR from materials via ak_collector_bind_target. Explicit --pr
|
|
1206
|
+
* still wins at admission; unique head/commit association also binds. No fixed
|
|
1207
|
+
* kickoff rewrite of the caller task; no mechanical task-text target lock.
|
|
1208
1208
|
*/
|
|
1209
|
-
export function buildCollectorTransportPrompt(
|
|
1210
|
-
|
|
1211
|
-
return appendEngineSessionMaterial([COLLECTOR_FIXED_KICKOFF], engineMaterial).join("\n");
|
|
1209
|
+
export function buildCollectorTransportPrompt(admitted, engineMaterial) {
|
|
1210
|
+
return buildInstructionTransportPrompt(admitted, engineMaterial);
|
|
1212
1211
|
}
|
|
1213
1212
|
/** Positive Issue number grammar shared with Doctor case path identity. */
|
|
1214
1213
|
const DOCTOR_ISSUE_NUMBER_PATTERN = /^[1-9]\d*$/;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Family-dispatch loader for packaged external RoleTurnHost factories (#645).
|
|
3
|
+
* ACP rows → production ACP host; headless rows → production headless host.
|
|
4
|
+
* Unregistered names fail closed before any import.
|
|
5
|
+
*/
|
|
6
|
+
import { lookupHostFamily } from "../host-descriptions.js";
|
|
7
|
+
import { loadProductionAcpHostFactory } from "./load-production-acp-host.js";
|
|
8
|
+
import { loadProductionHeadlessHostFactory } from "./load-production-headless-host.js";
|
|
9
|
+
/** Resolve the production host factory for one registered external host key. */
|
|
10
|
+
export async function loadProductionExternalHostFactory(packageRoot, host) {
|
|
11
|
+
const family = lookupHostFamily(host);
|
|
12
|
+
if (family === "acp") {
|
|
13
|
+
return loadProductionAcpHostFactory(packageRoot, host);
|
|
14
|
+
}
|
|
15
|
+
if (family === "headless") {
|
|
16
|
+
return loadProductionHeadlessHostFactory(packageRoot, host);
|
|
17
|
+
}
|
|
18
|
+
throw new Error(`unregistered host: ${host}`);
|
|
19
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
|
|
2
|
+
if (typeof path === "string" && /^\.\.?\//.test(path)) {
|
|
3
|
+
return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
|
|
4
|
+
return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
|
|
5
|
+
});
|
|
6
|
+
}
|
|
7
|
+
return path;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Deferred loader for the packaged generic headless RoleTurnHost factory (#645).
|
|
11
|
+
*
|
|
12
|
+
* Public ak-role bin must not statically value-import the production host (or its
|
|
13
|
+
* role-runtime edges). Specifier is runtime-constructed so esbuild leaves this
|
|
14
|
+
* import external (ADR 0052 discovery stays peer-free).
|
|
15
|
+
*/
|
|
16
|
+
import { existsSync } from "node:fs";
|
|
17
|
+
import { join } from "node:path";
|
|
18
|
+
import { pathToFileURL } from "node:url";
|
|
19
|
+
import { lookupHeadlessHostDescription } from "../host-descriptions.js";
|
|
20
|
+
/**
|
|
21
|
+
* Resolve the production headless host factory for one registered headless host
|
|
22
|
+
* key without a static graph edge into the public CLI bundle.
|
|
23
|
+
*/
|
|
24
|
+
export async function loadProductionHeadlessHostFactory(packageRoot, host) {
|
|
25
|
+
const description = lookupHeadlessHostDescription(host);
|
|
26
|
+
if (description === undefined) {
|
|
27
|
+
throw new Error(`unregistered headless host: ${host}`);
|
|
28
|
+
}
|
|
29
|
+
const built = join(packageRoot, "dist/headless-host/production-host.js");
|
|
30
|
+
const source = join(packageRoot, "src/headless-host/production-host.ts");
|
|
31
|
+
const target = existsSync(built) ? built : source;
|
|
32
|
+
const href = pathToFileURL(target).href;
|
|
33
|
+
const mod = (await import(__rewriteRelativeImportExtension(href)));
|
|
34
|
+
const create = mod.createProductionHeadlessRoleTurnHost;
|
|
35
|
+
return (options) => create({ ...options, description });
|
|
36
|
+
}
|