@akagilnc/pi-workflow-roles 0.1.3837 → 0.1.3884
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 +2 -2
- package/README.zh-CN.md +3 -3
- package/dist/acp-host/production-host.js +22736 -22720
- package/dist/engine-detour-tool.js +2 -2
- package/dist/engine-detour.js +23 -7
- package/dist/headless-host/production-host.js +4926 -4926
- package/dist/host-descriptions.js +5 -2
- package/dist/merger-contracts.js +23 -42
- package/dist/merger-git-state.js +69 -42
- package/dist/merger-role.js +30 -41
- package/dist/pi/role-turn-host.js +20 -92
- package/dist/public-cli/invocation.js +12 -39
- package/dist/public-cli/load-production-acp-host.js +1 -1
- package/dist/public-cli/load-production-external-host.js +10 -0
- package/dist/public-cli/load-production-headless-host.js +1 -1
- package/dist/public-cli/main.js +314 -433
- package/dist/public-cli/option-definitions.js +5 -5
- package/dist/public-cli/run-lifecycle.js +0 -2
- package/dist/public-role-summons.js +7 -49
- package/dist/reviewer-agent.js +1 -0
- package/dist/reviewer-child-executor.js +3 -2
- package/dist/role-activation-flags.js +50 -0
- package/dist/role-runtime.js +9 -16
- package/extensions/role-runtime.ts +0 -3
- package/package.json +1 -1
- package/scripts/build-package.mjs +2 -2
- package/souls/merger.md +1 -1
- package/src/acp-host/production-host.ts +31 -94
- package/src/acp-host/role-turn-host.ts +178 -263
- package/src/diagnostic-tail.ts +18 -0
- package/src/engine-detour-tool.ts +5 -4
- package/src/engine-detour.ts +44 -9
- package/src/external-host-turn-loop.ts +172 -0
- package/src/headless-host/production-host.ts +10 -7
- package/src/headless-host/role-turn-host.ts +271 -263
- package/src/host-descriptions.ts +5 -2
- package/src/host-session-record.ts +41 -0
- package/src/merger-contracts.ts +24 -38
- package/src/merger-git-state.ts +67 -37
- package/src/merger-role.ts +52 -48
- package/src/pi/role-turn-host.ts +18 -93
- package/src/prepared-role-turn.ts +68 -0
- package/src/public-cli/cli.ts +11 -20
- package/src/public-cli/invocation.ts +13 -44
- package/src/public-cli/load-production-acp-host.ts +2 -1
- package/src/public-cli/load-production-external-host.ts +17 -0
- package/src/public-cli/load-production-headless-host.ts +2 -1
- package/src/public-cli/merger-run.ts +2 -113
- package/src/public-cli/option-definitions.ts +5 -5
- package/src/public-cli/run-lifecycle.ts +0 -2
- package/src/public-role-summons.ts +12 -69
- package/src/reviewer-agent.ts +12 -1
- package/src/reviewer-child-executor.ts +10 -2
- package/src/role-activation-flags.ts +53 -0
- package/src/{acp-host/role-envelope.ts → role-envelope.ts} +28 -84
- package/src/role-runtime-dependencies.ts +89 -0
- package/src/role-runtime.ts +16 -17
- package/src/{acp-host/session-identity.ts → session-identity.ts} +6 -6
- /package/src/{acp-host/mcp-relay.mjs → mcp-relay.mjs} +0 -0
package/src/public-cli/cli.ts
CHANGED
|
@@ -43,9 +43,9 @@ import { seatModelOnly } from "./registry.ts";
|
|
|
43
43
|
import { CliUsageError } from "./cli-errors.ts";
|
|
44
44
|
import type { CliIo } from "./cli-io.ts";
|
|
45
45
|
import type { PostAdmissionEnv } from "./post-admission.ts";
|
|
46
|
-
import type { RoleTurnHost
|
|
46
|
+
import type { RoleTurnHost } from "../host-contracts.ts";
|
|
47
47
|
import { packagedExternalHostNames } from "../host-descriptions.ts";
|
|
48
|
-
import {
|
|
48
|
+
import { createLazyProductionExternalHost } from "./load-production-external-host.ts";
|
|
49
49
|
import {
|
|
50
50
|
createPiRoleTurnHost,
|
|
51
51
|
appendPiSessionCustomEntry,
|
|
@@ -335,29 +335,20 @@ function resolveRoleTurnHost(
|
|
|
335
335
|
recordLaunchedRolePackageIdentity,
|
|
336
336
|
observeLaunchedRolePackageIdentity,
|
|
337
337
|
});
|
|
338
|
-
// Composition-root adapter table: pi (in-process default) + one
|
|
338
|
+
// Composition-root adapter table: pi (in-process default) + one lazy external host per description-table key (#820).
|
|
339
339
|
const adapters: readonly NamedRoleTurnHostAdapter[] = env.hostAdapters ?? [
|
|
340
340
|
{ name: "pi", create: () => ({ ok: true as const, host: piHost }) },
|
|
341
341
|
...packagedExternalHostNames().map((name) => ({
|
|
342
342
|
name,
|
|
343
|
-
create: () => {
|
|
343
|
+
create: () => ({
|
|
344
|
+
ok: true as const,
|
|
344
345
|
// Factory loads outside the public bin static graph (ADR 0052 peer-free discovery).
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
create({
|
|
352
|
-
packageRoot: env.packageRoot,
|
|
353
|
-
principalAuthority: options.principalAuthority,
|
|
354
|
-
}),
|
|
355
|
-
);
|
|
356
|
-
return (await hostPromise).executeTurn(request);
|
|
357
|
-
},
|
|
358
|
-
},
|
|
359
|
-
};
|
|
360
|
-
},
|
|
346
|
+
host: createLazyProductionExternalHost({
|
|
347
|
+
packageRoot: env.packageRoot,
|
|
348
|
+
hostName: name,
|
|
349
|
+
principalAuthority: options.principalAuthority,
|
|
350
|
+
}),
|
|
351
|
+
}),
|
|
361
352
|
})),
|
|
362
353
|
];
|
|
363
354
|
const hostName = options.seat.host;
|
|
@@ -211,11 +211,10 @@ export type AdmittedReviewerInvocation = AdmittedRoleInvocationBase & {
|
|
|
211
211
|
readonly authorityRefs: readonly string[];
|
|
212
212
|
};
|
|
213
213
|
|
|
214
|
-
/** Mechanical
|
|
214
|
+
/** Mechanical materials read from current Git state (may be empty). */
|
|
215
215
|
export type DerivedMergerEnvelope = {
|
|
216
216
|
readonly targetObjectId: string;
|
|
217
217
|
readonly sourceObjectId: string;
|
|
218
|
-
readonly automaticMergeTreeId: string;
|
|
219
218
|
readonly expectedConflictPaths: readonly string[];
|
|
220
219
|
readonly resolutionScope: readonly string[];
|
|
221
220
|
};
|
|
@@ -810,17 +809,6 @@ export type ParseAnalystArgvResult =
|
|
|
810
809
|
| ParseAnalystSweepArgv
|
|
811
810
|
| ParseAnalystCohortArgv;
|
|
812
811
|
|
|
813
|
-
/** Honest activation-class failure while deriving the active-merge envelope. */
|
|
814
|
-
export class MergerEnvelopeDerivationError extends Error {
|
|
815
|
-
readonly code = "merger-envelope-derivation" as const;
|
|
816
|
-
/** Typed cause for #107 classifyPostAdmissionFailure (isTypedActivationError). */
|
|
817
|
-
readonly knownCause = "activation" as const;
|
|
818
|
-
constructor(message: string, options?: { cause?: unknown }) {
|
|
819
|
-
super(message, options);
|
|
820
|
-
this.name = "MergerEnvelopeDerivationError";
|
|
821
|
-
}
|
|
822
|
-
}
|
|
823
|
-
|
|
824
812
|
/** Reject missing/blank path values so empty overrides cannot silently degrade. */
|
|
825
813
|
function requireOptionPath(
|
|
826
814
|
flag: string,
|
|
@@ -2868,7 +2856,7 @@ export function parseMergerArgv(args: readonly string[]): ParseMergerArgvResult
|
|
|
2868
2856
|
token.startsWith("--resolutionScope=")
|
|
2869
2857
|
) {
|
|
2870
2858
|
throw new CliUsageError(
|
|
2871
|
-
"merger does not accept public packet fields; the adapter
|
|
2859
|
+
"merger does not accept public packet fields; the adapter reads Git merge materials",
|
|
2872
2860
|
);
|
|
2873
2861
|
}
|
|
2874
2862
|
if (token.startsWith("-") && token !== "-") {
|
|
@@ -2894,36 +2882,20 @@ function mergerMaterialFromUtf8(text: string): MergerInput["materials"]["task"]
|
|
|
2894
2882
|
}
|
|
2895
2883
|
|
|
2896
2884
|
/**
|
|
2897
|
-
*
|
|
2898
|
-
*
|
|
2899
|
-
*
|
|
2885
|
+
* Read merger materials from current Git state.
|
|
2886
|
+
* No in-progress merge / empty conflict set still yields materials (possibly empty);
|
|
2887
|
+
* code does not gate attendance on merge state (#827).
|
|
2900
2888
|
*/
|
|
2901
2889
|
export async function deriveMergerEnvelopeFromActiveMerge(
|
|
2902
2890
|
projectRoot: string,
|
|
2903
2891
|
gitState: MergerGitState = createProductionMergerGitState(projectRoot),
|
|
2904
2892
|
): Promise<DerivedMergerEnvelope> {
|
|
2905
|
-
|
|
2906
|
-
try {
|
|
2907
|
-
state = await gitState.activeMerge();
|
|
2908
|
-
} catch (error) {
|
|
2909
|
-
const message =
|
|
2910
|
-
error instanceof Error && error.message.trim() !== ""
|
|
2911
|
-
? error.message
|
|
2912
|
-
: "Assigned repository does not have one ordinary in-progress merge";
|
|
2913
|
-
throw new MergerEnvelopeDerivationError(message, { cause: error });
|
|
2914
|
-
}
|
|
2915
|
-
if (state.unmergedPaths.length === 0) {
|
|
2916
|
-
throw new MergerEnvelopeDerivationError(
|
|
2917
|
-
"Assigned repository does not have one ordinary in-progress merge with a complete conflict set",
|
|
2918
|
-
);
|
|
2919
|
-
}
|
|
2893
|
+
const state = await gitState.activeMerge();
|
|
2920
2894
|
const expectedConflictPaths = Object.freeze([...state.unmergedPaths]);
|
|
2921
|
-
// Scope is derived as the complete conflict set; the role may not broaden it.
|
|
2922
2895
|
const resolutionScope = Object.freeze([...state.unmergedPaths]);
|
|
2923
2896
|
return Object.freeze({
|
|
2924
2897
|
targetObjectId: state.targetObjectId,
|
|
2925
2898
|
sourceObjectId: state.sourceObjectId,
|
|
2926
|
-
automaticMergeTreeId: state.automaticMergeTreeId,
|
|
2927
2899
|
expectedConflictPaths,
|
|
2928
2900
|
resolutionScope,
|
|
2929
2901
|
});
|
|
@@ -2945,8 +2917,9 @@ export type AdmitMergerInvocationOptions = {
|
|
|
2945
2917
|
|
|
2946
2918
|
/**
|
|
2947
2919
|
* Admit a Merger Role run on the common Invocation request.
|
|
2948
|
-
*
|
|
2949
|
-
*
|
|
2920
|
+
* Git materials (parents, conflicts, scope) are read from the worktree and
|
|
2921
|
+
* handed to the role as assignment materials — including when empty (#827).
|
|
2922
|
+
* Callers never supply public packet fields for those facts.
|
|
2950
2923
|
*/
|
|
2951
2924
|
export async function admitMergerInvocation(
|
|
2952
2925
|
options: AdmitMergerInvocationOptions,
|
|
@@ -2960,8 +2933,6 @@ export async function admitMergerInvocation(
|
|
|
2960
2933
|
}
|
|
2961
2934
|
|
|
2962
2935
|
const projectRoot = resolve(options.project ?? options.cwd);
|
|
2963
|
-
// Derive mechanical envelope before placing a run identity so no-merge/drift
|
|
2964
|
-
// fails honestly without orphan ledger rows or guessed packet fields.
|
|
2965
2936
|
const derived = await deriveMergerEnvelopeFromActiveMerge(
|
|
2966
2937
|
projectRoot,
|
|
2967
2938
|
options.gitState ?? createProductionMergerGitState(projectRoot),
|
|
@@ -2988,18 +2959,18 @@ export async function admitMergerInvocation(
|
|
|
2988
2959
|
const attachments = await freezeAttachments(options.attachmentPaths, attachmentsDirectory);
|
|
2989
2960
|
|
|
2990
2961
|
// Intent materials seed primary-source investigation; the method owns the work.
|
|
2962
|
+
const targetLabel = derived.targetObjectId === "" ? "(none observed)" : derived.targetObjectId;
|
|
2963
|
+
const sourceLabel = derived.sourceObjectId === "" ? "(none observed)" : derived.sourceObjectId;
|
|
2991
2964
|
const targetIntent = mergerMaterialFromUtf8(
|
|
2992
|
-
`Investigate primary sources for target parent ${
|
|
2965
|
+
`Investigate primary sources for target parent ${targetLabel}. Do not invent intent.`,
|
|
2993
2966
|
);
|
|
2994
2967
|
const sourceIntent = mergerMaterialFromUtf8(
|
|
2995
|
-
`Investigate primary sources for source parent ${
|
|
2968
|
+
`Investigate primary sources for source parent ${sourceLabel}. Do not invent intent.`,
|
|
2996
2969
|
);
|
|
2997
2970
|
const taskMaterial = mergerMaterialFromUtf8(instruction);
|
|
2998
2971
|
const authorityMaterial = mergerMaterialFromUtf8(instruction);
|
|
2999
2972
|
|
|
3000
|
-
// Validate merger envelope before placing admitted identity.
|
|
3001
2973
|
const mergerInput = validateMergerInput({
|
|
3002
|
-
version: 1,
|
|
3003
2974
|
attemptId: runId,
|
|
3004
2975
|
targetObjectId: derived.targetObjectId,
|
|
3005
2976
|
sourceObjectId: derived.sourceObjectId,
|
|
@@ -3011,7 +2982,6 @@ export async function admitMergerInvocation(
|
|
|
3011
2982
|
},
|
|
3012
2983
|
expectedConflictPaths: [...derived.expectedConflictPaths],
|
|
3013
2984
|
resolutionScope: [...derived.resolutionScope],
|
|
3014
|
-
// Authorized checks remain available on the assignment; default none.
|
|
3015
2985
|
authorizedChecks: [],
|
|
3016
2986
|
});
|
|
3017
2987
|
|
|
@@ -3035,7 +3005,6 @@ export async function admitMergerInvocation(
|
|
|
3035
3005
|
derived: {
|
|
3036
3006
|
targetObjectId: derived.targetObjectId,
|
|
3037
3007
|
sourceObjectId: derived.sourceObjectId,
|
|
3038
|
-
automaticMergeTreeId: derived.automaticMergeTreeId,
|
|
3039
3008
|
expectedConflictPaths: [...derived.expectedConflictPaths],
|
|
3040
3009
|
resolutionScope: [...derived.resolutionScope],
|
|
3041
3010
|
},
|
|
@@ -25,6 +25,7 @@ type GenericAcpHostFactory = (options: {
|
|
|
25
25
|
packageRoot: string;
|
|
26
26
|
principalAuthority: DurablePrincipalAuthority;
|
|
27
27
|
description: AcpHostDescription;
|
|
28
|
+
hostName: string;
|
|
28
29
|
}) => RoleTurnHost;
|
|
29
30
|
|
|
30
31
|
/**
|
|
@@ -48,5 +49,5 @@ export async function loadProductionAcpHostFactory(
|
|
|
48
49
|
createProductionAcpRoleTurnHost: GenericAcpHostFactory;
|
|
49
50
|
};
|
|
50
51
|
const create = mod.createProductionAcpRoleTurnHost;
|
|
51
|
-
return (options) => create({ ...options, description });
|
|
52
|
+
return (options) => create({ ...options, description, hostName: host });
|
|
52
53
|
}
|
|
@@ -27,3 +27,20 @@ export async function loadProductionExternalHostFactory(
|
|
|
27
27
|
}
|
|
28
28
|
throw new Error(`unregistered host: ${host}`);
|
|
29
29
|
}
|
|
30
|
+
|
|
31
|
+
/** Lazy production external host — sole builder for CLI adapter table + public summons (#820). */
|
|
32
|
+
export function createLazyProductionExternalHost(options: {
|
|
33
|
+
readonly packageRoot: string;
|
|
34
|
+
readonly hostName: string;
|
|
35
|
+
readonly principalAuthority: DurablePrincipalAuthority;
|
|
36
|
+
}): RoleTurnHost {
|
|
37
|
+
let hostPromise: Promise<RoleTurnHost> | undefined;
|
|
38
|
+
return {
|
|
39
|
+
executeTurn: async (request) => {
|
|
40
|
+
hostPromise ??= loadProductionExternalHostFactory(options.packageRoot, options.hostName).then((create) =>
|
|
41
|
+
create({ packageRoot: options.packageRoot, principalAuthority: options.principalAuthority }),
|
|
42
|
+
);
|
|
43
|
+
return (await hostPromise).executeTurn(request);
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
}
|
|
@@ -22,6 +22,7 @@ type GenericHeadlessHostFactory = (options: {
|
|
|
22
22
|
packageRoot: string;
|
|
23
23
|
principalAuthority: DurablePrincipalAuthority;
|
|
24
24
|
description: HeadlessHostDescription;
|
|
25
|
+
hostName: string;
|
|
25
26
|
}) => RoleTurnHost;
|
|
26
27
|
|
|
27
28
|
/**
|
|
@@ -44,5 +45,5 @@ export async function loadProductionHeadlessHostFactory(
|
|
|
44
45
|
createProductionHeadlessRoleTurnHost: GenericHeadlessHostFactory;
|
|
45
46
|
};
|
|
46
47
|
const create = mod.createProductionHeadlessRoleTurnHost;
|
|
47
|
-
return (options) => create({ ...options, description });
|
|
48
|
+
return (options) => create({ ...options, description, hostName: host });
|
|
48
49
|
}
|
|
@@ -1,18 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Public Merger Role run:
|
|
3
|
-
*
|
|
2
|
+
* Public Merger Role run: read Git materials → force package merge-only method
|
|
3
|
+
* → post-admission coordinator → settle Terminal result (#114 / #517 / #827).
|
|
4
4
|
* #526: execution via RoleTurnHost; argv is Pi adapter internal.
|
|
5
5
|
*/
|
|
6
6
|
import type {
|
|
7
|
-
DurablePrincipalAuthority,
|
|
8
7
|
MethodBinding,
|
|
9
8
|
RoleTurnKnownFailure,
|
|
10
9
|
RoleTurnRequest,
|
|
11
10
|
} from "../host-contracts.ts";
|
|
12
|
-
import { mkdir, writeFile } from "node:fs/promises";
|
|
13
|
-
import { join, resolve } from "node:path";
|
|
14
|
-
|
|
15
|
-
import { ensureRealDirectoryTree } from "../activation-ledger-topology.ts";
|
|
16
11
|
import { engineSessionMaterialFromOptions } from "../package-resources/engine-material.ts";
|
|
17
12
|
import {
|
|
18
13
|
loadPackagedMethodSkillMaterial,
|
|
@@ -20,13 +15,10 @@ import {
|
|
|
20
15
|
type PackagedMethodSkillMaterial,
|
|
21
16
|
type PackagedMethodSkillProvenance,
|
|
22
17
|
} from "../package-resources/method-skill.ts";
|
|
23
|
-
import { uuidv7 } from "../uuidv7.ts";
|
|
24
18
|
import { CliUsageError } from "./cli-errors.ts";
|
|
25
19
|
import {
|
|
26
20
|
admitMergerInvocation,
|
|
27
21
|
buildMergerTransportPrompt,
|
|
28
|
-
issueAdmissionPlacement,
|
|
29
|
-
MergerEnvelopeDerivationError,
|
|
30
22
|
type AdmittedMergerInvocation,
|
|
31
23
|
} from "./invocation.ts";
|
|
32
24
|
import {
|
|
@@ -116,82 +108,6 @@ async function loadMergerMethodMaterial(
|
|
|
116
108
|
);
|
|
117
109
|
}
|
|
118
110
|
|
|
119
|
-
/**
|
|
120
|
-
* Structural admit shell when envelope derivation fails: keeps role-correct
|
|
121
|
-
* Terminal identity for honest activation-class settlement without inventing
|
|
122
|
-
* parents/conflicts (those stay empty; the gate never receives a guessed packet).
|
|
123
|
-
*/
|
|
124
|
-
async function admitMergerShellForActivationFailure(options: {
|
|
125
|
-
home: string;
|
|
126
|
-
cwd: string;
|
|
127
|
-
instruction: string;
|
|
128
|
-
project?: string;
|
|
129
|
-
createRunId?: () => string;
|
|
130
|
-
principalAuthority: DurablePrincipalAuthority;
|
|
131
|
-
}): Promise<AdmittedMergerInvocation> {
|
|
132
|
-
const projectRoot = resolve(options.project ?? options.cwd);
|
|
133
|
-
const runId = (options.createRunId ?? uuidv7)();
|
|
134
|
-
const {
|
|
135
|
-
principal,
|
|
136
|
-
sessionDirectory,
|
|
137
|
-
sessionFile,
|
|
138
|
-
runDirectory,
|
|
139
|
-
ledgerHome,
|
|
140
|
-
bookKey,
|
|
141
|
-
} = issueAdmissionPlacement(options.principalAuthority, {
|
|
142
|
-
cwd: projectRoot,
|
|
143
|
-
runId,
|
|
144
|
-
role: "merger",
|
|
145
|
-
home: options.home,
|
|
146
|
-
});
|
|
147
|
-
ensureRealDirectoryTree(ledgerHome, sessionDirectory);
|
|
148
|
-
await mkdir(runDirectory, { recursive: true });
|
|
149
|
-
const emptyDerived = {
|
|
150
|
-
targetObjectId: "",
|
|
151
|
-
sourceObjectId: "",
|
|
152
|
-
automaticMergeTreeId: "",
|
|
153
|
-
expectedConflictPaths: [] as string[],
|
|
154
|
-
resolutionScope: [] as string[],
|
|
155
|
-
};
|
|
156
|
-
const admittedRequestPath = join(runDirectory, "admitted-request.json");
|
|
157
|
-
const mergerInputPath = join(runDirectory, "merger-input.json");
|
|
158
|
-
await writeFile(
|
|
159
|
-
admittedRequestPath,
|
|
160
|
-
`${JSON.stringify(
|
|
161
|
-
{
|
|
162
|
-
role: "merger",
|
|
163
|
-
runId,
|
|
164
|
-
bookKey,
|
|
165
|
-
projectRoot,
|
|
166
|
-
instruction: options.instruction,
|
|
167
|
-
instructionEmpty: false,
|
|
168
|
-
mergerInputPath,
|
|
169
|
-
derived: emptyDerived,
|
|
170
|
-
attachments: [],
|
|
171
|
-
sessionDirectory,
|
|
172
|
-
sessionFile,
|
|
173
|
-
},
|
|
174
|
-
null,
|
|
175
|
-
2,
|
|
176
|
-
)}\n`,
|
|
177
|
-
"utf8",
|
|
178
|
-
);
|
|
179
|
-
return {
|
|
180
|
-
role: "merger",
|
|
181
|
-
runId,
|
|
182
|
-
bookKey,
|
|
183
|
-
projectRoot,
|
|
184
|
-
instruction: options.instruction,
|
|
185
|
-
instructionEmpty: false,
|
|
186
|
-
attachments: [],
|
|
187
|
-
runDirectory,
|
|
188
|
-
principal,
|
|
189
|
-
admittedRequestPath,
|
|
190
|
-
mergerInputPath,
|
|
191
|
-
derived: emptyDerived,
|
|
192
|
-
};
|
|
193
|
-
}
|
|
194
|
-
|
|
195
111
|
function mergerTurnOptions(
|
|
196
112
|
admitted: AdmittedMergerInvocation,
|
|
197
113
|
env: MergerRunEnv,
|
|
@@ -263,33 +179,6 @@ export async function runPublicMerger(
|
|
|
263
179
|
presentStructuralRejection(error, io);
|
|
264
180
|
return { exitCode: 2 };
|
|
265
181
|
}
|
|
266
|
-
if (error instanceof MergerEnvelopeDerivationError) {
|
|
267
|
-
const shell = await admitMergerShellForActivationFailure({
|
|
268
|
-
home: env.home,
|
|
269
|
-
principalAuthority: env.principalAuthority,
|
|
270
|
-
cwd: env.cwd,
|
|
271
|
-
instruction: parsed.instruction,
|
|
272
|
-
...(parsed.project === undefined ? {} : { project: parsed.project }),
|
|
273
|
-
...(env.createRunId === undefined
|
|
274
|
-
? {}
|
|
275
|
-
: { createRunId: env.createRunId }),
|
|
276
|
-
});
|
|
277
|
-
await markRunAdmitted(shell, env.principalAuthority);
|
|
278
|
-
return (await presentControlledFailure(
|
|
279
|
-
shell,
|
|
280
|
-
{
|
|
281
|
-
timedOut: false,
|
|
282
|
-
code: null,
|
|
283
|
-
stderr: "",
|
|
284
|
-
thrown: error,
|
|
285
|
-
knownCause: "activation",
|
|
286
|
-
knownDiagnostic: error.message,
|
|
287
|
-
},
|
|
288
|
-
mergerAdapters(env.packageRoot),
|
|
289
|
-
env.principalAuthority,
|
|
290
|
-
io,
|
|
291
|
-
)) as { exitCode: number; admitted: AdmittedMergerInvocation; terminal: TerminalResult };
|
|
292
|
-
}
|
|
293
182
|
throw error;
|
|
294
183
|
}
|
|
295
184
|
|
|
@@ -670,7 +670,7 @@ const NOTARY_OPTIONS = [
|
|
|
670
670
|
] as const satisfies readonly PublicOptionDefinition[];
|
|
671
671
|
|
|
672
672
|
const MERGER_OPTIONS = [
|
|
673
|
-
// Merger project face
|
|
673
|
+
// Merger project face: worktree whose Git materials are handed to the role.
|
|
674
674
|
{
|
|
675
675
|
id: "project",
|
|
676
676
|
owner: "merger",
|
|
@@ -681,8 +681,8 @@ const MERGER_OPTIONS = [
|
|
|
681
681
|
repeatable: false,
|
|
682
682
|
form: "option",
|
|
683
683
|
description: {
|
|
684
|
-
en: "Project root
|
|
685
|
-
zh: "
|
|
684
|
+
en: "Project root whose Git merge materials are read (defaults to cwd).",
|
|
685
|
+
zh: "读取 Git merge 材料的项目根(默认 cwd)。",
|
|
686
686
|
},
|
|
687
687
|
},
|
|
688
688
|
bindOwner("merger", SHARED_ATTACH_SEMANTICS),
|
|
@@ -1231,10 +1231,10 @@ const ROLE_COMMAND_HELP = {
|
|
|
1231
1231
|
},
|
|
1232
1232
|
merger: {
|
|
1233
1233
|
command: "merger",
|
|
1234
|
-
summary: "
|
|
1234
|
+
summary: "Reconcile merge materials in a worktree (escalate when nothing to merge).",
|
|
1235
1235
|
usage: ["ak-role merger [options] <instruction>"],
|
|
1236
1236
|
examples: [
|
|
1237
|
-
'ak-role merger --project /path/to/worktree "Reconcile the
|
|
1237
|
+
'ak-role merger --project /path/to/worktree "Reconcile the merge."',
|
|
1238
1238
|
],
|
|
1239
1239
|
},
|
|
1240
1240
|
inspector: {
|
|
@@ -1386,7 +1386,6 @@ async function loadResumableRunRecord(
|
|
|
1386
1386
|
if (
|
|
1387
1387
|
typeof d.targetObjectId === "string" &&
|
|
1388
1388
|
typeof d.sourceObjectId === "string" &&
|
|
1389
|
-
typeof d.automaticMergeTreeId === "string" &&
|
|
1390
1389
|
Array.isArray(d.expectedConflictPaths) &&
|
|
1391
1390
|
Array.isArray(d.resolutionScope) &&
|
|
1392
1391
|
d.expectedConflictPaths.every((p) => typeof p === "string") &&
|
|
@@ -1395,7 +1394,6 @@ async function loadResumableRunRecord(
|
|
|
1395
1394
|
derived = {
|
|
1396
1395
|
targetObjectId: d.targetObjectId,
|
|
1397
1396
|
sourceObjectId: d.sourceObjectId,
|
|
1398
|
-
automaticMergeTreeId: d.automaticMergeTreeId,
|
|
1399
1397
|
expectedConflictPaths: d.expectedConflictPaths as string[],
|
|
1400
1398
|
resolutionScope: d.resolutionScope as string[],
|
|
1401
1399
|
};
|
|
@@ -65,12 +65,6 @@ export type PublicSummonRequest = {
|
|
|
65
65
|
* Never folded into argv / parent-run lookup keys (ADR 0079 卷宗指针 stays pure).
|
|
66
66
|
*/
|
|
67
67
|
readonly gateReviewInstruction?: string;
|
|
68
|
-
/**
|
|
69
|
-
* Parent-invocation host axis (#617 / #645). Nested institutional legs inherit
|
|
70
|
-
* the live parent `--host` so a single public entry does not require mutating
|
|
71
|
-
* persistent seat host. Seat model/thinking stay on the officer seat table.
|
|
72
|
-
*/
|
|
73
|
-
readonly host?: string;
|
|
74
68
|
/**
|
|
75
69
|
* Offline test inject — same face as public CLI env.roleTurnHost. Production
|
|
76
70
|
* summons leave this unset and use the seat-resolved host.
|
|
@@ -218,7 +212,7 @@ async function createSummonEnv(options: {
|
|
|
218
212
|
let roleTurnHost = piHost;
|
|
219
213
|
if (hostName !== "pi") {
|
|
220
214
|
const { lookupHostFamily } = await import("./host-descriptions.ts");
|
|
221
|
-
const {
|
|
215
|
+
const { createLazyProductionExternalHost } = await import(
|
|
222
216
|
"./public-cli/load-production-external-host.ts"
|
|
223
217
|
);
|
|
224
218
|
// #729 / #645: description tables are the sole host authority — family
|
|
@@ -228,19 +222,12 @@ async function createSummonEnv(options: {
|
|
|
228
222
|
`public role summons host unregistered: host=${hostName} seat=${options.role}`,
|
|
229
223
|
);
|
|
230
224
|
}
|
|
231
|
-
|
|
232
|
-
roleTurnHost = {
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
packageRoot: options.packageRoot,
|
|
238
|
-
principalAuthority,
|
|
239
|
-
}),
|
|
240
|
-
);
|
|
241
|
-
return (await hostPromise).executeTurn(request);
|
|
242
|
-
},
|
|
243
|
-
};
|
|
225
|
+
// Same lazy external host face as public CLI (#820) — one builder, two callers.
|
|
226
|
+
roleTurnHost = createLazyProductionExternalHost({
|
|
227
|
+
packageRoot: options.packageRoot,
|
|
228
|
+
hostName,
|
|
229
|
+
principalAuthority,
|
|
230
|
+
});
|
|
244
231
|
}
|
|
245
232
|
// #788: host is registered above; only then project host-facing provider.
|
|
246
233
|
const { loadHostProvidersTable, projectHostFacingProvider } = await import(
|
|
@@ -291,10 +278,9 @@ export async function summonPublicRole(
|
|
|
291
278
|
const credentials =
|
|
292
279
|
options.credentials ?? (await loadCredentialProviders(agentDir));
|
|
293
280
|
const config = await loadPublicCliConfig(home);
|
|
294
|
-
// Nested
|
|
295
|
-
//
|
|
296
|
-
const
|
|
297
|
-
const seat = resolveEffectiveSeat(config, options.role, credentials, invocation);
|
|
281
|
+
// Nested summons resolve host on the officer seat only (flag>seat>default pi).
|
|
282
|
+
// Parent run host is not an override channel (#821 / ADR 0082 host-flag-two-channels).
|
|
283
|
+
const seat = resolveEffectiveSeat(config, options.role, credentials);
|
|
298
284
|
const env = {
|
|
299
285
|
...(await createSummonEnv({
|
|
300
286
|
role: options.role,
|
|
@@ -420,47 +406,6 @@ export async function summonPublicRole(
|
|
|
420
406
|
}
|
|
421
407
|
|
|
422
408
|
/** Gate officer summons: notary/auditor via --source-run; inspector via pointer instruction. */
|
|
423
|
-
/**
|
|
424
|
-
* Read parent invocation host so nested officers inherit live --host only (#645).
|
|
425
|
-
* Gate source runs necessarily own invocation.json — missing page, bad JSON, or
|
|
426
|
-
* bad shape stay loud (failure-honesty). Absent host field is lawful (seat default).
|
|
427
|
-
*/
|
|
428
|
-
async function parentInvocationHost(sourceRunDirectory: string): Promise<string | undefined> {
|
|
429
|
-
const { readFile } = await import("node:fs/promises");
|
|
430
|
-
const path = join(sourceRunDirectory, "invocation.json");
|
|
431
|
-
let text: string;
|
|
432
|
-
try {
|
|
433
|
-
text = await readFile(path, "utf8");
|
|
434
|
-
} catch (error) {
|
|
435
|
-
const code =
|
|
436
|
-
error instanceof Error && "code" in error
|
|
437
|
-
? (error as NodeJS.ErrnoException).code
|
|
438
|
-
: undefined;
|
|
439
|
-
throw new Error(
|
|
440
|
-
`parent invocation.json required for gate source run at ${path}: ${code ?? (error instanceof Error ? error.message : String(error))}`,
|
|
441
|
-
{ cause: error },
|
|
442
|
-
);
|
|
443
|
-
}
|
|
444
|
-
let raw: unknown;
|
|
445
|
-
try {
|
|
446
|
-
raw = JSON.parse(text) as unknown;
|
|
447
|
-
} catch (error) {
|
|
448
|
-
throw new Error(
|
|
449
|
-
`parent invocation.json unreadable at ${path}: ${error instanceof Error ? error.message : String(error)}`,
|
|
450
|
-
{ cause: error },
|
|
451
|
-
);
|
|
452
|
-
}
|
|
453
|
-
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
454
|
-
throw new Error(`parent invocation.json has non-object shape at ${path}`);
|
|
455
|
-
}
|
|
456
|
-
const host = (raw as Record<string, unknown>).host;
|
|
457
|
-
if (host === undefined) return undefined;
|
|
458
|
-
if (typeof host !== "string" || host.trim() === "") {
|
|
459
|
-
throw new Error(`parent invocation.json host must be a non-empty string at ${path}`);
|
|
460
|
-
}
|
|
461
|
-
return host;
|
|
462
|
-
}
|
|
463
|
-
|
|
464
409
|
export async function summonGateOfficer(options: {
|
|
465
410
|
readonly officer: "inspector" | "notary" | "auditor";
|
|
466
411
|
readonly sourceRunDirectory: string;
|
|
@@ -501,9 +446,8 @@ export async function summonGateOfficer(options: {
|
|
|
501
446
|
submission: options.submission,
|
|
502
447
|
});
|
|
503
448
|
}
|
|
504
|
-
//
|
|
505
|
-
//
|
|
506
|
-
const parentHost = await parentInvocationHost(options.sourceRunDirectory);
|
|
449
|
+
// Officer host is seat-owned only (#821). Parent invocation.json.host stays a
|
|
450
|
+
// hostTransition consumer in post-admission — not an officer override channel.
|
|
507
451
|
const common = {
|
|
508
452
|
cwd: options.cwd,
|
|
509
453
|
...(home === undefined ? {} : { home }),
|
|
@@ -514,7 +458,6 @@ export async function summonGateOfficer(options: {
|
|
|
514
458
|
...(gateReviewInstruction === undefined
|
|
515
459
|
? {}
|
|
516
460
|
: { gateReviewInstruction }),
|
|
517
|
-
...(parentHost === undefined ? {} : { host: parentHost }),
|
|
518
461
|
...(options.roleTurnHost === undefined ? {} : { roleTurnHost: options.roleTurnHost }),
|
|
519
462
|
...(options.createRunId === undefined ? {} : { createRunId: options.createRunId }),
|
|
520
463
|
} as const;
|
package/src/reviewer-agent.ts
CHANGED
|
@@ -29,7 +29,17 @@ export class ReviewerDispatchExecutionError extends Error {
|
|
|
29
29
|
this.name = "ReviewerDispatchExecutionError";
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
|
-
export type
|
|
32
|
+
export type ReviewerDispatchRunOptions = Readonly<{
|
|
33
|
+
context: HostContext;
|
|
34
|
+
signal?: AbortSignal;
|
|
35
|
+
/**
|
|
36
|
+
* Request-scoped RoleHost flag reader (#818).
|
|
37
|
+
* Forwards to axis sub-session so envelope legs arm engine from request.engine,
|
|
38
|
+
* not ambient process.env (same resolveEngineName gate as parent registration).
|
|
39
|
+
*/
|
|
40
|
+
getFlag?: (name: string) => boolean | string | undefined;
|
|
41
|
+
}>;
|
|
42
|
+
export type ReviewerAgentRunner = { run(dispatch: AcceptedReviewerExecution, options: ReviewerDispatchRunOptions): Promise<ReviewerSuccessfulDispatchRunResult>; shutdown(): Promise<void> };
|
|
33
43
|
export type ReviewerAgentFaultPoint = ReviewerWorkspaceFaultPoint;
|
|
34
44
|
type Dependencies = Readonly<{
|
|
35
45
|
fault?(operation: ReviewerAgentFaultPoint): void;
|
|
@@ -81,6 +91,7 @@ export function createReviewerAgentRunner(dependencies: Dependencies = {}): Revi
|
|
|
81
91
|
...(dependencies.packageRoot === undefined
|
|
82
92
|
? {}
|
|
83
93
|
: { packageRoot: dependencies.packageRoot }),
|
|
94
|
+
...(options.getFlag === undefined ? {} : { getFlag: options.getFlag }),
|
|
84
95
|
});
|
|
85
96
|
const disposition = await workspaceOwner.dispose(workspace);
|
|
86
97
|
return [leg.axis, Object.freeze({ status: "successful" as const, report: child.report, usage: child.usage, target: batch.target, prompt: child.prompt, workspaceDisposition: disposition })] as const;
|
|
@@ -9,7 +9,7 @@ import type { Usage } from "@earendil-works/pi-ai";
|
|
|
9
9
|
|
|
10
10
|
import type { HostContext, RoleTurnModelConfig } from "./host-contracts.ts";
|
|
11
11
|
import { createEngineDetourToolDefinition } from "./engine-detour-tool.ts";
|
|
12
|
-
import {
|
|
12
|
+
import { resolveEngineName } from "./engine-detour.ts";
|
|
13
13
|
import {
|
|
14
14
|
appendEngineSessionMaterial,
|
|
15
15
|
engineSessionMaterialFromOptions,
|
|
@@ -101,6 +101,13 @@ export type ReviewerChildExecuteOptions = Readonly<{
|
|
|
101
101
|
credentialScratchParent?: string;
|
|
102
102
|
/** Package root for optional engine method-material on legs (#378). */
|
|
103
103
|
packageRoot?: string;
|
|
104
|
+
/**
|
|
105
|
+
* Request-scoped RoleHost flag reader (#818).
|
|
106
|
+
* Same single gate as registerEngineDetourTool / resolveEngineName:
|
|
107
|
+
* envelope projects request.engine here; absent → pi child-env fallback.
|
|
108
|
+
* Must not read ambient process.env as the primary signal on envelope legs.
|
|
109
|
+
*/
|
|
110
|
+
getFlag?: (name: string) => boolean | string | undefined;
|
|
104
111
|
}>;
|
|
105
112
|
|
|
106
113
|
/**
|
|
@@ -145,7 +152,8 @@ export async function executeReviewerChild(
|
|
|
145
152
|
const selection = await parentSelection(context);
|
|
146
153
|
const { openPiInProcessSession } = await import("./pi/in-process-session.ts");
|
|
147
154
|
const { createRecordSession } = await import("./archivist-record-entry.ts");
|
|
148
|
-
|
|
155
|
+
// One gate with parent registration (#818): request flag, else pi child env.
|
|
156
|
+
const engineName = resolveEngineName(options.getFlag);
|
|
149
157
|
const engineMaterial =
|
|
150
158
|
engineName === undefined
|
|
151
159
|
? undefined
|