@akagilnc/pi-workflow-roles 0.1.3545 → 0.1.3552
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 +1 -1
- package/README.zh-CN.md +1 -1
- package/dist/grok/production-host.js +36 -430
- package/dist/public-cli/main.js +60 -46
- package/dist/public-cli/registry.js +2 -1
- package/package.json +1 -1
- package/src/doctor-evidence.ts +1 -8
- package/src/grok/production-host.ts +13 -216
- package/src/grok/role-turn-host.ts +4 -358
- package/src/host-transition-prior-native.ts +23 -32
- package/src/public-cli/cli.ts +33 -4
- package/src/public-cli/countersign-run.ts +1 -0
- package/src/public-cli/inspector-run.ts +1 -0
- package/src/public-cli/notary-run.ts +1 -0
- package/src/public-cli/option-definitions.ts +11 -0
- package/src/public-cli/post-admission.ts +5 -1
- package/src/public-cli/registry.ts +1 -0
- package/src/public-cli/seat-ticket-binding.ts +6 -2
- package/src/grok/bash-seatbelt.ts +0 -69
package/dist/public-cli/main.js
CHANGED
|
@@ -15270,7 +15270,8 @@ var init_registry2 = __esm({
|
|
|
15270
15270
|
"roles",
|
|
15271
15271
|
"config",
|
|
15272
15272
|
"help",
|
|
15273
|
-
"resume"
|
|
15273
|
+
"resume",
|
|
15274
|
+
"new"
|
|
15274
15275
|
];
|
|
15275
15276
|
STARTUP_CANDIDATES = {
|
|
15276
15277
|
judge: [
|
|
@@ -16863,7 +16864,6 @@ async function discoverCaseFiles(root) {
|
|
|
16863
16864
|
for (const item of await readdir(dir, { withFileTypes: true })) {
|
|
16864
16865
|
const path = resolve5(dir, item.name);
|
|
16865
16866
|
if (item.isDirectory()) {
|
|
16866
|
-
if (item.name === GROK_HOME_DIR_NAME) continue;
|
|
16867
16867
|
await walk(path, depth + 1);
|
|
16868
16868
|
} else if (item.isFile() && (item.name.endsWith(".jsonl") || item.name === "stderr.log" && depth === 1)) found.push(path);
|
|
16869
16869
|
}
|
|
@@ -16992,13 +16992,11 @@ async function loadDoctorCase(runsPath) {
|
|
|
16992
16992
|
const cost2 = { invocations: sourceList(runDirs.length, runDirs), legs: sourceList(legs.length, legs), modelApiTurns: sourceList(turns.count, turns.sources), outputTokens: sourceList(tokens.count, tokens.sources), toolCalls: sourceList(calls.count, calls.sources), retries: { ...sourceList(retryDirs.length, retryDirs), evidence: "literal run-dir naming" }, statuses, commits, sessions, outputBytes: { ...sourceList(rawBytes, legs), payload: "raw JSONL bytes", providerWireBytes: "unavailable" } };
|
|
16993
16993
|
return { version: 1, identity: { issueNumber: Number(match[1]), runsPath: await stableRunsIdentity(root) }, evidence, cost: cost2 };
|
|
16994
16994
|
}
|
|
16995
|
-
var GROK_HOME_DIR_NAME;
|
|
16996
16995
|
var init_doctor_evidence = __esm({
|
|
16997
16996
|
"src/doctor-evidence.ts"() {
|
|
16998
16997
|
"use strict";
|
|
16999
16998
|
init_sha256();
|
|
17000
16999
|
init_terminating_tools();
|
|
17001
|
-
GROK_HOME_DIR_NAME = "grok-home";
|
|
17002
17000
|
}
|
|
17003
17001
|
});
|
|
17004
17002
|
|
|
@@ -19359,6 +19357,16 @@ var init_option_definitions = __esm({
|
|
|
19359
19357
|
"ak-role --host grok-build resume 01abc\u2026",
|
|
19360
19358
|
"ak-role --engine agy resume 01abc\u2026"
|
|
19361
19359
|
]
|
|
19360
|
+
},
|
|
19361
|
+
new: {
|
|
19362
|
+
command: "new",
|
|
19363
|
+
summary: "Explicit fresh summons: same role options as a direct call, but always mint a new run (skip same-ticket auto-resume). Caller chooses new vs resume; no overflow judgment.",
|
|
19364
|
+
usage: ["ak-role new <role> [options] \u2026"],
|
|
19365
|
+
examples: [
|
|
19366
|
+
'ak-role new countersign --attach ./ticket.md "\u88C1\uFF1A\u672C\u7968 #708 \u662F\u5426\u8DB3\u4EE5\u5F00\u5DE5\u3002"',
|
|
19367
|
+
"ak-role new notary --source-run 01abc\u2026@countersign",
|
|
19368
|
+
"ak-role --host grok-build new inspector --attach ./change.patch"
|
|
19369
|
+
]
|
|
19362
19370
|
}
|
|
19363
19371
|
};
|
|
19364
19372
|
PUBLIC_COMMAND_HELP = {
|
|
@@ -23708,6 +23716,7 @@ async function resolveSeatTicketBinding(admitted, env = {}) {
|
|
|
23708
23716
|
return resolution;
|
|
23709
23717
|
}
|
|
23710
23718
|
async function tryResumeSameTicketSeatRun(input) {
|
|
23719
|
+
if (input.freshSummons === true) return void 0;
|
|
23711
23720
|
const previousRunId = await findLatestRunIdForSeatTicket({
|
|
23712
23721
|
home: input.home,
|
|
23713
23722
|
bookKey: resolveBookKeyFromGit(input.projectRoot),
|
|
@@ -27905,7 +27914,7 @@ var init_turn_request = __esm({
|
|
|
27905
27914
|
|
|
27906
27915
|
// src/host-transition-prior-native.ts
|
|
27907
27916
|
import { access as access2, readdir as readdir6 } from "node:fs/promises";
|
|
27908
|
-
import { join as join26 } from "node:path";
|
|
27917
|
+
import { dirname as dirname11, join as join26 } from "node:path";
|
|
27909
27918
|
function isKnownRoleTurnHost(value) {
|
|
27910
27919
|
return KNOWN_ROLE_TURN_HOSTS.includes(value);
|
|
27911
27920
|
}
|
|
@@ -27921,41 +27930,28 @@ async function listPiNativeRecordPaths(sessionFile) {
|
|
|
27921
27930
|
throw error;
|
|
27922
27931
|
}
|
|
27923
27932
|
}
|
|
27924
|
-
async function
|
|
27925
|
-
const
|
|
27926
|
-
let
|
|
27933
|
+
async function listSitianRecordPaths(sessionParent) {
|
|
27934
|
+
const sessionRoot = dirname11(sessionParent);
|
|
27935
|
+
let entries;
|
|
27927
27936
|
try {
|
|
27928
|
-
|
|
27937
|
+
entries = await readdir6(sessionRoot, { withFileTypes: true });
|
|
27929
27938
|
} catch (error) {
|
|
27930
27939
|
if (isEnoent2(error)) return [];
|
|
27931
27940
|
throw error;
|
|
27932
27941
|
}
|
|
27933
|
-
const
|
|
27934
|
-
for (const
|
|
27935
|
-
if (!
|
|
27936
|
-
|
|
27937
|
-
try {
|
|
27938
|
-
sessionDirs = await readdir6(join26(grokSessionsDir, cwdEntry.name), { withFileTypes: true });
|
|
27939
|
-
} catch (error) {
|
|
27940
|
-
if (isEnoent2(error)) continue;
|
|
27941
|
-
throw error;
|
|
27942
|
-
}
|
|
27943
|
-
for (const sessEntry of sessionDirs) {
|
|
27944
|
-
if (!sessEntry.isDirectory()) continue;
|
|
27945
|
-
updatesPaths.push(join26(grokSessionsDir, cwdEntry.name, sessEntry.name, "updates.jsonl"));
|
|
27946
|
-
}
|
|
27947
|
-
}
|
|
27948
|
-
updatesPaths.sort();
|
|
27949
|
-
const present = [];
|
|
27950
|
-
for (const updatesFile of updatesPaths) {
|
|
27942
|
+
const recordPaths = [];
|
|
27943
|
+
for (const entry of entries) {
|
|
27944
|
+
if (!entry.isDirectory()) continue;
|
|
27945
|
+
const recordFile = join26(sessionRoot, entry.name, "records.jsonl");
|
|
27951
27946
|
try {
|
|
27952
|
-
await access2(
|
|
27953
|
-
|
|
27947
|
+
await access2(recordFile);
|
|
27948
|
+
recordPaths.push(recordFile);
|
|
27954
27949
|
} catch (error) {
|
|
27955
27950
|
if (!isEnoent2(error)) throw error;
|
|
27956
27951
|
}
|
|
27957
27952
|
}
|
|
27958
|
-
|
|
27953
|
+
recordPaths.sort();
|
|
27954
|
+
return recordPaths;
|
|
27959
27955
|
}
|
|
27960
27956
|
async function projectHostTransitionPriorNative(input) {
|
|
27961
27957
|
if (input.previousHost === input.liveHost) return void 0;
|
|
@@ -27969,7 +27965,7 @@ async function projectHostTransitionPriorNative(input) {
|
|
|
27969
27965
|
priorNativePaths: paths2
|
|
27970
27966
|
};
|
|
27971
27967
|
}
|
|
27972
|
-
const paths = await
|
|
27968
|
+
const paths = await listSitianRecordPaths(input.piSessionFile);
|
|
27973
27969
|
return {
|
|
27974
27970
|
previousHost: "grok-build",
|
|
27975
27971
|
priorNativePaths: paths
|
|
@@ -28458,7 +28454,6 @@ async function dispatchPostAdmissionTurn(input) {
|
|
|
28458
28454
|
const hostTransition = previousHost !== void 0 && liveHost !== void 0 && principalCoordinates !== void 0 ? await projectHostTransitionPriorNative({
|
|
28459
28455
|
previousHost,
|
|
28460
28456
|
liveHost,
|
|
28461
|
-
runDirectory: admitted.runDirectory,
|
|
28462
28457
|
piSessionFile: principalCoordinates.sessionFile
|
|
28463
28458
|
}) : void 0;
|
|
28464
28459
|
turnRequest = request;
|
|
@@ -29370,6 +29365,7 @@ async function runPublicCountersign(argv, env, io, parseCountersignArgv2) {
|
|
|
29370
29365
|
projectRoot,
|
|
29371
29366
|
role: "countersign",
|
|
29372
29367
|
ticketNumber: probedTicketNumber,
|
|
29368
|
+
freshSummons: env.freshSummons,
|
|
29373
29369
|
summons,
|
|
29374
29370
|
resume: (runId, materials) => runPublicCountersignResume(
|
|
29375
29371
|
{ runId, ...materials === void 0 ? {} : { summons: materials } },
|
|
@@ -29995,6 +29991,7 @@ async function runPublicNotary(argv, env, io, parseNotaryArgv2) {
|
|
|
29995
29991
|
projectRoot,
|
|
29996
29992
|
role: "notary",
|
|
29997
29993
|
ticketNumber,
|
|
29994
|
+
freshSummons: env.freshSummons,
|
|
29998
29995
|
summons,
|
|
29999
29996
|
resume: (runId, materials) => runPublicNotaryResume(
|
|
30000
29997
|
{ runId, ...materials === void 0 ? {} : { summons: materials } },
|
|
@@ -30150,6 +30147,7 @@ async function runPublicInspector(argv, env, io, parseInspectorArgv2) {
|
|
|
30150
30147
|
projectRoot,
|
|
30151
30148
|
role: "inspector",
|
|
30152
30149
|
ticketNumber: probedTicketNumber,
|
|
30150
|
+
freshSummons: env.freshSummons,
|
|
30153
30151
|
summons,
|
|
30154
30152
|
resume: (runId, materials) => runPublicInspectorResume(
|
|
30155
30153
|
{ runId, ...materials === void 0 ? {} : { summons: materials } },
|
|
@@ -30884,9 +30882,9 @@ var init_analyst_book_key = __esm({
|
|
|
30884
30882
|
// src/atomic-write.ts
|
|
30885
30883
|
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
30886
30884
|
import { rename, rm as rm2, writeFile as writeFile10 } from "node:fs/promises";
|
|
30887
|
-
import { dirname as
|
|
30885
|
+
import { dirname as dirname12, join as join30 } from "node:path";
|
|
30888
30886
|
async function writeFileAtomically(destination, contents) {
|
|
30889
|
-
const parent =
|
|
30887
|
+
const parent = dirname12(destination);
|
|
30890
30888
|
const temporary = join30(parent, `.atomic-write-${randomUUID6()}.tmp`);
|
|
30891
30889
|
try {
|
|
30892
30890
|
await writeFile10(temporary, contents);
|
|
@@ -30904,7 +30902,7 @@ var init_atomic_write = __esm({
|
|
|
30904
30902
|
|
|
30905
30903
|
// src/analyst-index.ts
|
|
30906
30904
|
import { open as open3, readFile as readFile17, unlink as unlink4 } from "node:fs/promises";
|
|
30907
|
-
import { dirname as
|
|
30905
|
+
import { dirname as dirname13, join as join31 } from "node:path";
|
|
30908
30906
|
function sleep(ms) {
|
|
30909
30907
|
return new Promise((resolve12) => {
|
|
30910
30908
|
setTimeout(resolve12, ms);
|
|
@@ -30912,8 +30910,8 @@ function sleep(ms) {
|
|
|
30912
30910
|
}
|
|
30913
30911
|
async function withAnalystLibraryIndexLock(ledgerHome, fn) {
|
|
30914
30912
|
const indexPath = analystLibraryIndexPath(ledgerHome);
|
|
30915
|
-
ensureRealDirectoryTree(ledgerHome,
|
|
30916
|
-
const lockPath = join31(
|
|
30913
|
+
ensureRealDirectoryTree(ledgerHome, dirname13(indexPath));
|
|
30914
|
+
const lockPath = join31(dirname13(indexPath), LIBRARY_INDEX_LOCK_NAME);
|
|
30917
30915
|
assertLedgerFileInsideHome(lockPath, ledgerHome);
|
|
30918
30916
|
const startedAt = Date.now();
|
|
30919
30917
|
while (true) {
|
|
@@ -31049,7 +31047,7 @@ async function readAnalystLibraryIndexPage(ledgerHome) {
|
|
|
31049
31047
|
}
|
|
31050
31048
|
async function writeAnalystLibraryIndexPage(ledgerHome, page) {
|
|
31051
31049
|
const path = analystLibraryIndexPath(ledgerHome);
|
|
31052
|
-
ensureRealDirectoryTree(ledgerHome,
|
|
31050
|
+
ensureRealDirectoryTree(ledgerHome, dirname13(path));
|
|
31053
31051
|
assertLedgerFileInsideHome(path, ledgerHome);
|
|
31054
31052
|
await writeFileAtomically(path, `${JSON.stringify(page, null, 2)}
|
|
31055
31053
|
`);
|
|
@@ -32426,7 +32424,7 @@ var init_analyst_metric_family = __esm({
|
|
|
32426
32424
|
|
|
32427
32425
|
// src/analyst-page.ts
|
|
32428
32426
|
import { createHash as createHash7 } from "node:crypto";
|
|
32429
|
-
import { dirname as
|
|
32427
|
+
import { dirname as dirname14, join as join33 } from "node:path";
|
|
32430
32428
|
function analystIssuePageKey(address) {
|
|
32431
32429
|
const parts = ["book", address.bookKey];
|
|
32432
32430
|
if (address.issueNumber !== void 0) {
|
|
@@ -32556,7 +32554,7 @@ async function buildAnalystIssueMetricsPage(input) {
|
|
|
32556
32554
|
}
|
|
32557
32555
|
async function writeAnalystIssueMetricsPage(ledgerHome, page) {
|
|
32558
32556
|
const path = analystIssuePagePath(ledgerHome, analystIssuePageAddressFromPage(page));
|
|
32559
|
-
ensureRealDirectoryTree(ledgerHome,
|
|
32557
|
+
ensureRealDirectoryTree(ledgerHome, dirname14(path));
|
|
32560
32558
|
assertLedgerFileInsideHome(path, ledgerHome);
|
|
32561
32559
|
await writeFileAtomically(path, `${JSON.stringify(page, null, 2)}
|
|
32562
32560
|
`);
|
|
@@ -33077,7 +33075,8 @@ function createRoleEnvironment(env, options) {
|
|
|
33077
33075
|
...projectSeatHost(options.seat),
|
|
33078
33076
|
...timeoutMs === void 0 ? {} : { timeoutMs },
|
|
33079
33077
|
...env.createRunId === void 0 ? {} : { createRunId: env.createRunId },
|
|
33080
|
-
...options.config?.autoResumeLimit === void 0 ? {} : { autoResumeLimit: options.config.autoResumeLimit }
|
|
33078
|
+
...options.config?.autoResumeLimit === void 0 ? {} : { autoResumeLimit: options.config.autoResumeLimit },
|
|
33079
|
+
...env.freshSummons === true ? { freshSummons: true } : {}
|
|
33081
33080
|
};
|
|
33082
33081
|
}
|
|
33083
33082
|
function cliResultFromRoleRun(result2) {
|
|
@@ -33564,8 +33563,8 @@ async function runAkRole(argv, env) {
|
|
|
33564
33563
|
packageRoot: await realpath6(env.packageRoot),
|
|
33565
33564
|
principalAuthority: env.principalAuthority ?? piDurablePrincipalAuthority
|
|
33566
33565
|
};
|
|
33567
|
-
|
|
33568
|
-
const acceptsSeatAxes = parsed.command !== void 0 && (isPublicCallableRole(parsed.command) || parsed.command === "resume");
|
|
33566
|
+
let parsed = parseArgv(argv);
|
|
33567
|
+
const acceptsSeatAxes = parsed.command !== void 0 && (isPublicCallableRole(parsed.command) || parsed.command === "resume" || parsed.command === "new");
|
|
33569
33568
|
if (parsed.host !== void 0 && !parsed.help && parsed.command !== void 0 && parsed.command !== "help" && !acceptsSeatAxes) {
|
|
33570
33569
|
throw new CliUsageError(`host axis is role commands only; refused command ${parsed.command}`);
|
|
33571
33570
|
}
|
|
@@ -33610,6 +33609,21 @@ async function runAkRole(argv, env) {
|
|
|
33610
33609
|
exitCode: await runConfigCommand(parsed.args, home, env.packageRoot, io)
|
|
33611
33610
|
};
|
|
33612
33611
|
}
|
|
33612
|
+
if (parsed.command === "new") {
|
|
33613
|
+
const role = parsed.args[0];
|
|
33614
|
+
if (role === void 0) {
|
|
33615
|
+
throw new CliUsageError("usage: ak-role new <role> \u2026");
|
|
33616
|
+
}
|
|
33617
|
+
if (!isPublicCallableRole(role)) {
|
|
33618
|
+
throw new CliUsageError(`usage: ak-role new <role> \u2026; unknown role: ${role}`);
|
|
33619
|
+
}
|
|
33620
|
+
parsed = {
|
|
33621
|
+
...parsed,
|
|
33622
|
+
command: role,
|
|
33623
|
+
args: parsed.args.slice(1)
|
|
33624
|
+
};
|
|
33625
|
+
env = { ...env, freshSummons: true };
|
|
33626
|
+
}
|
|
33613
33627
|
if (parsed.command === "resume") {
|
|
33614
33628
|
const agentDir = resolveAgentDir(env, home);
|
|
33615
33629
|
const cwd = env.cwd ?? process.cwd();
|
|
@@ -33639,7 +33653,7 @@ async function runAkRole(argv, env) {
|
|
|
33639
33653
|
);
|
|
33640
33654
|
return cliResultFromRoleRun(result2);
|
|
33641
33655
|
}
|
|
33642
|
-
if (isPublicCliSupportCommand(parsed.command)) {
|
|
33656
|
+
if (parsed.command !== void 0 && isPublicCliSupportCommand(parsed.command)) {
|
|
33643
33657
|
throw new CliUsageError(`unhandled support command: ${parsed.command}`);
|
|
33644
33658
|
}
|
|
33645
33659
|
if (parsed.command === "judge") {
|
|
@@ -34014,7 +34028,7 @@ var init_cli = __esm({
|
|
|
34014
34028
|
|
|
34015
34029
|
// src/public-cli/main.ts
|
|
34016
34030
|
import { existsSync as existsSync11 } from "node:fs";
|
|
34017
|
-
import { dirname as
|
|
34031
|
+
import { dirname as dirname15, join as join35 } from "node:path";
|
|
34018
34032
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
34019
34033
|
|
|
34020
34034
|
// src/public-cli/host-pi-runtime.ts
|
|
@@ -34101,7 +34115,7 @@ function linkPackage(packageRoot2, name, targetDir) {
|
|
|
34101
34115
|
}
|
|
34102
34116
|
|
|
34103
34117
|
// src/public-cli/main.ts
|
|
34104
|
-
var here =
|
|
34118
|
+
var here = dirname15(fileURLToPath4(import.meta.url));
|
|
34105
34119
|
function resolvePackageRoot(binDir) {
|
|
34106
34120
|
const canonical = join35(binDir, "..", "..");
|
|
34107
34121
|
if (existsSync11(join35(canonical, "package.json"))) {
|
package/package.json
CHANGED
package/src/doctor-evidence.ts
CHANGED
|
@@ -5,14 +5,7 @@ import type { DoctorCase, DoctorCaseCost, DoctorCount, DoctorEvidenceEntry } fro
|
|
|
5
5
|
import { AcceptedDetailsContractError, acceptedFacts, isTerminatingToolName, validateAcceptedDetails } from "./package-contracts/terminating-tools.ts";
|
|
6
6
|
|
|
7
7
|
function record(value: unknown): value is Record<string, unknown> { return typeof value === "object" && value !== null && !Array.isArray(value); }
|
|
8
|
-
|
|
9
|
-
* Grok controlled-home ledger directory under a run (ADR 0077 / #594).
|
|
10
|
-
* Native journals (e.g. updates.jsonl) are retained for dossier parity but are
|
|
11
|
-
* not Pi session records — walking them into deriveSession yields false incomplete
|
|
12
|
-
* legs. Explicit skip is the leave-trace exclusion until a grok parser exists.
|
|
13
|
-
*/
|
|
14
|
-
const GROK_HOME_DIR_NAME = "grok-home";
|
|
15
|
-
async function discoverCaseFiles(root: string): Promise<string[]> { const found: string[] = []; async function walk(dir: string, depth: number) { for (const item of await readdir(dir, { withFileTypes: true })) { const path = resolve(dir, item.name); if (item.isDirectory()) { if (item.name === GROK_HOME_DIR_NAME) continue; await walk(path, depth + 1); } else if (item.isFile() && (item.name.endsWith(".jsonl") || (item.name === "stderr.log" && depth === 1))) found.push(path); } } await walk(root, 0); return found.sort(); }
|
|
8
|
+
async function discoverCaseFiles(root: string): Promise<string[]> { const found: string[] = []; async function walk(dir: string, depth: number) { for (const item of await readdir(dir, { withFileTypes: true })) { const path = resolve(dir, item.name); if (item.isDirectory()) { await walk(path, depth + 1); } else if (item.isFile() && (item.name.endsWith(".jsonl") || (item.name === "stderr.log" && depth === 1))) found.push(path); } } await walk(root, 0); return found.sort(); }
|
|
16
9
|
function sourceList(count: number, sources: string[]) { return { count, sources: [...new Set(sources)].sort() }; }
|
|
17
10
|
function accumulate(metric: DoctorCount, value: number, source: string) { metric.count += value; if (value) metric.sources.push(source); }
|
|
18
11
|
function timestamp(row: Record<string, unknown>) { return typeof row.timestamp === "string" && Number.isFinite(Date.parse(row.timestamp)) ? row.timestamp : undefined; }
|
|
@@ -1,20 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Production composition for the grok-build RoleTurnHost adapter (#580 / #522 / #
|
|
2
|
+
* Production composition for the grok-build RoleTurnHost adapter (#580 / #522 / #717).
|
|
3
3
|
* Owns injectables around the S6 true adapter; does not alter S6 adapter behavior.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* into S6 as request.home (bash-seatbelt.ts: "callers must pass the isolated home").
|
|
9
|
-
* Auth is copied from the operator home into grok-home for the turn and scrubbed
|
|
10
|
-
* on settle together with AK seatbelt hook residue; the session dossier directly
|
|
11
|
-
* written under grok-home survives settlement (ADR 0048 / ADR 0077 / #594).
|
|
12
|
-
*
|
|
13
|
-
* Known residual risk: a hard process crash between auth copy and settle can leave
|
|
14
|
-
* auth.json in the retained run ledger for one crash window. The next open scrubs
|
|
15
|
-
* residual auth before recopying; single-crash-window residue is accepted.
|
|
5
|
+
* Grok CLI uses the operator home (`~/.grok`) and credentials in place. The
|
|
6
|
+
* factory does not create a run-scoped grok home, does not rewrite HOME, and
|
|
7
|
+
* does not copy or scrub auth.json. Sititian records on the run are the dossier.
|
|
16
8
|
*/
|
|
17
|
-
import { readFile
|
|
9
|
+
import { readFile } from "node:fs/promises";
|
|
18
10
|
import { join } from "node:path";
|
|
19
11
|
import { fileURLToPath } from "node:url";
|
|
20
12
|
|
|
@@ -22,7 +14,7 @@ import { loadCanonicalSkillBinding as loadHomeCanonicalSkillBinding } from "../c
|
|
|
22
14
|
import { createGhCollectorGitHubTransport, createGhIssueSoftFetcher } from "../collector-github.ts";
|
|
23
15
|
import { createPiDoctorAuditor } from "../doctor-auditor.ts";
|
|
24
16
|
import { loadDoctorCase } from "../doctor-evidence.ts";
|
|
25
|
-
import type { DurablePrincipalAuthority, RoleTurnHost
|
|
17
|
+
import type { DurablePrincipalAuthority, RoleTurnHost } from "../host-contracts.ts";
|
|
26
18
|
import { createPiJudgeAuditor } from "../judge-auditor.ts";
|
|
27
19
|
import { createProductionMergerGitState } from "../merger-git-state.ts";
|
|
28
20
|
import { createNativeNavigatorSessionFactory, createNavigatorAttendance } from "../navigator-attendance.ts";
|
|
@@ -35,28 +27,11 @@ import { createReviewerPinnedGitReader } from "../reviewer-pinned-git.ts";
|
|
|
35
27
|
import { loadGatekeeperSessionMaterials, loadMainRoleSessionMaterials } from "../session-opening-materials.ts";
|
|
36
28
|
import { createComposedGrokRoleTurnHost } from "./role-envelope.ts";
|
|
37
29
|
import {
|
|
38
|
-
assertControlledGrokAuthIsNotSymlink,
|
|
39
|
-
assertControlledGrokHomeIsRealDirectory,
|
|
40
30
|
connectGrokAcpStdio,
|
|
41
31
|
controlledGrokChildEnv,
|
|
42
|
-
inspectControlledGrok,
|
|
43
|
-
prepareControlledGrokHome,
|
|
44
|
-
scrubAkBashSeatbeltHooks,
|
|
45
|
-
type GrokCapabilityDeclaration,
|
|
46
32
|
} from "./role-turn-host.ts";
|
|
47
33
|
import { createGrokSessionIdentityAuthority } from "./session-identity.ts";
|
|
48
34
|
|
|
49
|
-
/**
|
|
50
|
-
* Authoritative production isolation binding consumed by inspect/connect and by
|
|
51
|
-
* the S6 request.home rewrite (controlledHome).
|
|
52
|
-
*/
|
|
53
|
-
export type ProductionGrokIsolationBinding = Readonly<{
|
|
54
|
-
operatorHome: string;
|
|
55
|
-
controlledHome: string;
|
|
56
|
-
binary: string;
|
|
57
|
-
env: NodeJS.ProcessEnv;
|
|
58
|
-
}>;
|
|
59
|
-
|
|
60
35
|
export type ProductionGrokHostOptions = Readonly<{
|
|
61
36
|
packageRoot: string;
|
|
62
37
|
principalAuthority: DurablePrincipalAuthority;
|
|
@@ -66,137 +41,13 @@ function resolveGrokBinary(operatorHome: string): string {
|
|
|
66
41
|
return join(operatorHome, ".grok", "bin", "grok");
|
|
67
42
|
}
|
|
68
43
|
|
|
69
|
-
|
|
70
|
-
* Primary-failure state for cleanup settlement. `undefined` is a legal primary
|
|
71
|
-
* value (throw/reject undefined), so presence is an explicit discriminant — never
|
|
72
|
-
* inferred from `value !== undefined`.
|
|
73
|
-
*/
|
|
74
|
-
export type ProductionGrokPrimaryFailure =
|
|
75
|
-
| Readonly<{ present: false }>
|
|
76
|
-
| Readonly<{ present: true; value: unknown }>;
|
|
77
|
-
|
|
78
|
-
export const NO_PRODUCTION_GROK_PRIMARY_FAILURE = {
|
|
79
|
-
present: false,
|
|
80
|
-
} as const satisfies ProductionGrokPrimaryFailure;
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Sole cleanup settlement for production controlled homes: scrub auth secrets
|
|
84
|
-
* (auth.json) and AK seatbelt hook residue while preserving the session dossier
|
|
85
|
-
* under the runDirectory ledger. Refuses symlink home/auth so rm never follows a
|
|
86
|
-
* redirect (#594 F1/F4). Cleanup failure is never silenced. When a primary failure
|
|
87
|
-
* is present and cleanup also fails, both surface as AggregateError (including
|
|
88
|
-
* primary value `undefined`); cleanup success rethrows the primary value as-is.
|
|
89
|
-
*/
|
|
90
|
-
export async function settleProductionGrokHomeCleanup(
|
|
91
|
-
controlledHome: string,
|
|
92
|
-
primaryFailure: ProductionGrokPrimaryFailure,
|
|
93
|
-
concurrentMessage: string,
|
|
94
|
-
): Promise<void> {
|
|
95
|
-
try {
|
|
96
|
-
await assertControlledGrokHomeIsRealDirectory(controlledHome);
|
|
97
|
-
const authPath = join(controlledHome, "auth.json");
|
|
98
|
-
await assertControlledGrokAuthIsNotSymlink(authPath);
|
|
99
|
-
await rm(authPath, { force: true });
|
|
100
|
-
await scrubAkBashSeatbeltHooks(controlledHome);
|
|
101
|
-
} catch (cleanupFailure) {
|
|
102
|
-
if (primaryFailure.present) {
|
|
103
|
-
throw new AggregateError([primaryFailure.value, cleanupFailure], concurrentMessage, {
|
|
104
|
-
cause: primaryFailure.value,
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
throw cleanupFailure;
|
|
108
|
-
}
|
|
109
|
-
if (primaryFailure.present) {
|
|
110
|
-
throw primaryFailure.value;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Open the production isolation root under runDirectory: auth copy into grok-home.
|
|
116
|
-
* If auth copy fails after the root is created, auth secrets are scrubbed; open
|
|
117
|
-
* failure and cleanup failure both surface (AggregateError when concurrent).
|
|
118
|
-
*/
|
|
119
|
-
export async function openProductionGrokHome(
|
|
120
|
-
runDirectory: string,
|
|
121
|
-
operatorHome: string,
|
|
122
|
-
): Promise<string> {
|
|
123
|
-
const controlledHome = join(runDirectory, "grok-home");
|
|
124
|
-
try {
|
|
125
|
-
await prepareControlledGrokHome(operatorHome, controlledHome);
|
|
126
|
-
return controlledHome;
|
|
127
|
-
} catch (error) {
|
|
128
|
-
await settleProductionGrokHomeCleanup(
|
|
129
|
-
controlledHome,
|
|
130
|
-
{ present: true, value: error },
|
|
131
|
-
"production grok home open failed and its cleanup also failed",
|
|
132
|
-
);
|
|
133
|
-
// settle always throws when primaryFailure is present.
|
|
134
|
-
throw error;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
function childEnv(controlledHome: string, packageRoot: string): NodeJS.ProcessEnv {
|
|
44
|
+
function childEnv(packageRoot: string): NodeJS.ProcessEnv {
|
|
139
45
|
return {
|
|
140
|
-
...controlledGrokChildEnv(process.env
|
|
46
|
+
...controlledGrokChildEnv(process.env),
|
|
141
47
|
AK_PACKAGE_ROOT: packageRoot,
|
|
142
48
|
};
|
|
143
49
|
}
|
|
144
50
|
|
|
145
|
-
/**
|
|
146
|
-
* Single production isolation binding: auth root, GROK_HOME/HOME, and binary
|
|
147
|
-
* resolution. Production executeTurn passes controlledHome as S6 request.home
|
|
148
|
-
* (seatbelt hang root proven separately by S6 seatbelt tests).
|
|
149
|
-
*/
|
|
150
|
-
export async function bindProductionGrokIsolation(
|
|
151
|
-
runDirectory: string,
|
|
152
|
-
operatorHome: string,
|
|
153
|
-
packageRoot: string,
|
|
154
|
-
): Promise<ProductionGrokIsolationBinding> {
|
|
155
|
-
const controlledHome = await openProductionGrokHome(runDirectory, operatorHome);
|
|
156
|
-
return {
|
|
157
|
-
operatorHome,
|
|
158
|
-
controlledHome,
|
|
159
|
-
binary: resolveGrokBinary(operatorHome),
|
|
160
|
-
env: childEnv(controlledHome, packageRoot),
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Bind isolation under runDirectory, run the turn body, always scrub auth.json
|
|
166
|
-
* and AK seatbelt hooks via settleProductionGrokHomeCleanup (no silent catch)
|
|
167
|
-
* while preserving the session dossier under runDirectory/grok-home. Success,
|
|
168
|
-
* typed-result, and throw paths all clean up; cleanup failure and primary+cleanup
|
|
169
|
-
* both surface. Same-run Grok isolation only — no cross-run home override (#637).
|
|
170
|
-
*/
|
|
171
|
-
export async function withProductionGrokIsolation<T>(
|
|
172
|
-
runDirectory: string,
|
|
173
|
-
operatorHome: string,
|
|
174
|
-
packageRoot: string,
|
|
175
|
-
run: (binding: ProductionGrokIsolationBinding) => Promise<T>,
|
|
176
|
-
): Promise<T> {
|
|
177
|
-
let binding: ProductionGrokIsolationBinding | undefined;
|
|
178
|
-
let primaryFailure: ProductionGrokPrimaryFailure = NO_PRODUCTION_GROK_PRIMARY_FAILURE;
|
|
179
|
-
let value!: T;
|
|
180
|
-
try {
|
|
181
|
-
binding = await bindProductionGrokIsolation(runDirectory, operatorHome, packageRoot);
|
|
182
|
-
value = await run(binding);
|
|
183
|
-
} catch (error) {
|
|
184
|
-
primaryFailure = { present: true, value: error };
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
if (binding !== undefined) {
|
|
188
|
-
// No catch: settle throws cleanup failure or AggregateError with primary.
|
|
189
|
-
await settleProductionGrokHomeCleanup(
|
|
190
|
-
binding.controlledHome,
|
|
191
|
-
primaryFailure,
|
|
192
|
-
"production grok isolation turn and cleanup failed",
|
|
193
|
-
);
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
if (primaryFailure.present) throw primaryFailure.value;
|
|
197
|
-
return value;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
51
|
const navigatorRoutePlaybookPath = fileURLToPath(
|
|
201
52
|
new URL("../../resources/navigator-route-playbook.md", import.meta.url),
|
|
202
53
|
);
|
|
@@ -267,78 +118,24 @@ export function createGrokRoleRuntimeDependencies(packageRoot: string): RoleRunt
|
|
|
267
118
|
};
|
|
268
119
|
}
|
|
269
120
|
|
|
270
|
-
async function recordGrokCapabilities(
|
|
271
|
-
request: RoleTurnRequest,
|
|
272
|
-
declaration: GrokCapabilityDeclaration,
|
|
273
|
-
): Promise<void> {
|
|
274
|
-
await writeFile(
|
|
275
|
-
join(request.runDirectory, "grok-capabilities.json"),
|
|
276
|
-
`${JSON.stringify(declaration)}\n`,
|
|
277
|
-
"utf8",
|
|
278
|
-
);
|
|
279
|
-
}
|
|
280
|
-
|
|
281
121
|
/**
|
|
282
122
|
* Assemble the production grok-build RoleTurnHost from the S6 true adapter.
|
|
283
|
-
*
|
|
123
|
+
* Grok subprocesses inherit the operator home; the run directory is sitian-only.
|
|
284
124
|
*/
|
|
285
125
|
export function createProductionGrokRoleTurnHost(options: ProductionGrokHostOptions): RoleTurnHost {
|
|
286
126
|
const { packageRoot, principalAuthority } = options;
|
|
287
|
-
|
|
288
|
-
let turn: ProductionGrokIsolationBinding | undefined;
|
|
289
|
-
let serial = Promise.resolve();
|
|
127
|
+
const env = childEnv(packageRoot);
|
|
290
128
|
|
|
291
|
-
|
|
129
|
+
return createComposedGrokRoleTurnHost({
|
|
292
130
|
sessionIdentity: createGrokSessionIdentityAuthority(principalAuthority),
|
|
293
131
|
roleRuntimeDependencies: createGrokRoleRuntimeDependencies(packageRoot),
|
|
294
|
-
recordCapabilities: recordGrokCapabilities,
|
|
295
|
-
async inspect(request) {
|
|
296
|
-
if (turn === undefined) {
|
|
297
|
-
throw new Error("production grok inspect requires an active isolated turn");
|
|
298
|
-
}
|
|
299
|
-
return inspectControlledGrok({
|
|
300
|
-
binary: turn.binary,
|
|
301
|
-
cwd: request.cwd,
|
|
302
|
-
env: turn.env,
|
|
303
|
-
packageRoot,
|
|
304
|
-
});
|
|
305
|
-
},
|
|
306
132
|
async connect(request) {
|
|
307
|
-
if (turn === undefined) {
|
|
308
|
-
throw new Error("production grok connect requires an active isolated turn");
|
|
309
|
-
}
|
|
310
133
|
return connectGrokAcpStdio({
|
|
311
|
-
binary:
|
|
134
|
+
binary: resolveGrokBinary(request.home),
|
|
312
135
|
cwd: request.cwd,
|
|
313
|
-
env
|
|
136
|
+
env,
|
|
314
137
|
...(request.model === undefined ? {} : { model: request.model.model }),
|
|
315
138
|
});
|
|
316
139
|
},
|
|
317
140
|
});
|
|
318
|
-
|
|
319
|
-
return {
|
|
320
|
-
executeTurn(request) {
|
|
321
|
-
const execution = serial.then(() =>
|
|
322
|
-
withProductionGrokIsolation(
|
|
323
|
-
request.runDirectory,
|
|
324
|
-
request.home,
|
|
325
|
-
packageRoot,
|
|
326
|
-
async (binding) => {
|
|
327
|
-
turn = binding;
|
|
328
|
-
try {
|
|
329
|
-
// S6 seatbelt hangs on request.home — same isolated root as GROK_HOME.
|
|
330
|
-
return await inner.executeTurn({ ...request, home: binding.controlledHome });
|
|
331
|
-
} finally {
|
|
332
|
-
turn = undefined;
|
|
333
|
-
}
|
|
334
|
-
},
|
|
335
|
-
),
|
|
336
|
-
);
|
|
337
|
-
serial = execution.then(
|
|
338
|
-
() => undefined,
|
|
339
|
-
() => undefined,
|
|
340
|
-
);
|
|
341
|
-
return execution;
|
|
342
|
-
},
|
|
343
|
-
};
|
|
344
141
|
}
|