@indigoai-us/hq-cli 5.303.0 → 5.304.0
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/CHANGELOG.md +3 -0
- package/dist/commands/lanes.d.ts +2 -0
- package/dist/commands/lanes.js +21 -6
- package/dist/lib/doctor/deep/executor.d.ts +4 -10
- package/dist/lib/doctor/deep/executor.js +1 -1
- package/dist/lib/doctor/deep/parity.d.ts +2 -29
- package/dist/lib/doctor/deep/parity.js +7 -7
- package/dist/lib/doctor/deep/sandbox.d.ts +7 -25
- package/dist/lib/doctor/deep/sandbox.js +4 -4
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [5.304.0] — 2026-10-01
|
|
6
|
+
- `hq lanes show` skips owner refresh only when a failed Outpost dispatch explicitly recorded `pickup_persisted: false` and no owner heartbeat; it reports `remote_refresh: "skipped_undispatched"` in that case.
|
|
7
|
+
|
|
5
8
|
## [5.303.0] — 2026-10-01
|
|
6
9
|
- Fixed bundled shell scripts failing on Windows when Git Bash is installed but `bash` is not on `PATH`.
|
|
7
10
|
- The bundled token usage script now reads Claude sessions from `CLAUDE_CONFIG_DIR/projects` and the standard Claude project directories, including nested session folders (subagent transcripts are skipped), when no project directory is given.
|
package/dist/commands/lanes.d.ts
CHANGED
|
@@ -132,6 +132,8 @@ export declare function parseMaxRuntimeSeconds(raw: unknown, workerId: string):
|
|
|
132
132
|
export declare const PICKUP_TERMINAL_REASONS: readonly ["account_not_logged_in", "unknown_account", "account_disabled", "model_unsupported"];
|
|
133
133
|
export declare const DISPATCH_TERMINAL_REASON: "outpost_dispatch_failed";
|
|
134
134
|
export type PickupTerminalReason = (typeof PICKUP_TERMINAL_REASONS)[number];
|
|
135
|
+
/** Only explicit non-persistence evidence can prove a failed dispatch has no owner snapshot. */
|
|
136
|
+
export declare function isUndispatchedOutpostMirror(lane: LaneRecord): boolean;
|
|
135
137
|
/**
|
|
136
138
|
* The CAS updater a pickup failure applies. A lane that is already terminal
|
|
137
139
|
* for any reason (a cancel that landed after the caller's last read, an
|
package/dist/commands/lanes.js
CHANGED
|
@@ -1284,9 +1284,7 @@ async function refreshRemoteLaneMirrors(ctx, hqRoot, records, parentLaneIds, opt
|
|
|
1284
1284
|
((parentLaneIds === undefined || (lane.senior.kind === "lane" &&
|
|
1285
1285
|
lane.senior.id !== undefined &&
|
|
1286
1286
|
parentLaneIds.has(lane.senior.id))) || options.laneIds?.has(lane.lane_id) === true) &&
|
|
1287
|
-
!(
|
|
1288
|
-
lane.terminal_reason === DISPATCH_TERMINAL_REASON &&
|
|
1289
|
-
lane.last_host_report_at === undefined) &&
|
|
1287
|
+
!isUndispatchedOutpostMirror(lane) &&
|
|
1290
1288
|
(options.includeTerminalMirrors === true || !isTerminalLaneState(lane.state)))
|
|
1291
1289
|
.sort((a, b) => {
|
|
1292
1290
|
const terminalOrder = Number(isTerminalLaneState(a.state)) - Number(isTerminalLaneState(b.state));
|
|
@@ -2524,6 +2522,14 @@ export const PICKUP_TERMINAL_REASONS = [
|
|
|
2524
2522
|
"model_unsupported",
|
|
2525
2523
|
];
|
|
2526
2524
|
export const DISPATCH_TERMINAL_REASON = "outpost_dispatch_failed";
|
|
2525
|
+
/** Only explicit non-persistence evidence can prove a failed dispatch has no owner snapshot. */
|
|
2526
|
+
export function isUndispatchedOutpostMirror(lane) {
|
|
2527
|
+
return lane.host.kind === "outpost" &&
|
|
2528
|
+
isTerminalLaneState(lane.state) &&
|
|
2529
|
+
lane.terminal_reason === DISPATCH_TERMINAL_REASON &&
|
|
2530
|
+
lane.terminal_details?.pickup_persisted === false &&
|
|
2531
|
+
lane.last_host_report_at == null;
|
|
2532
|
+
}
|
|
2527
2533
|
function parsePickupTerminalReason(value) {
|
|
2528
2534
|
if (PICKUP_TERMINAL_REASONS.includes(value)) {
|
|
2529
2535
|
return value;
|
|
@@ -3647,6 +3653,9 @@ async function dispatchCreatedOutpostLane(ctx, input) {
|
|
|
3647
3653
|
command_id: dispatched.commandId,
|
|
3648
3654
|
exit_code: dispatched.exitCode,
|
|
3649
3655
|
status: dispatched.status,
|
|
3656
|
+
...(remote?.pickup_persisted === false || ownerError.details.pickup_persisted === false
|
|
3657
|
+
? { pickup_persisted: false }
|
|
3658
|
+
: {}),
|
|
3650
3659
|
owner_error: ownerError,
|
|
3651
3660
|
});
|
|
3652
3661
|
await emitLaneTerminalStateSoft(ctx, failed);
|
|
@@ -5038,13 +5047,17 @@ async function runOutpostIdentityBackfillTarget(ctx, opts) {
|
|
|
5038
5047
|
}
|
|
5039
5048
|
async function refreshOutpostLaneCopiesForShow(ctx, lane, localLanes) {
|
|
5040
5049
|
if (lane.host.kind !== "outpost" || laneRunsHere(lane, reconcileHostFromCtx(ctx))) {
|
|
5041
|
-
return lane;
|
|
5050
|
+
return { lane };
|
|
5051
|
+
}
|
|
5052
|
+
if (isUndispatchedOutpostMirror(lane)) {
|
|
5053
|
+
return { lane, remoteRefresh: "skipped_undispatched" };
|
|
5042
5054
|
}
|
|
5043
5055
|
const outpostId = lane.host.id;
|
|
5044
5056
|
const descendantIds = localLanes
|
|
5045
5057
|
.filter((candidate) => candidate.lane_id !== lane.lane_id &&
|
|
5046
5058
|
candidate.host.kind === "outpost" && candidate.host.id === outpostId &&
|
|
5047
5059
|
walkHitsLane(ctx.hqRoot(), candidate.senior, lane.lane_id, ctx))
|
|
5060
|
+
.filter((candidate) => !isUndispatchedOutpostMirror(candidate))
|
|
5048
5061
|
.map((candidate) => candidate.lane_id);
|
|
5049
5062
|
const refresh = async (laneId) => {
|
|
5050
5063
|
try {
|
|
@@ -5061,13 +5074,14 @@ async function refreshOutpostLaneCopiesForShow(ctx, lane, localLanes) {
|
|
|
5061
5074
|
};
|
|
5062
5075
|
const refreshed = await mapWithConcurrency([lane.lane_id, ...descendantIds], OUTPOST_HIRE_SUMMARY_CONCURRENCY, refresh);
|
|
5063
5076
|
const refreshedLane = refreshed[0] ?? null;
|
|
5064
|
-
return refreshedLane ?? lane;
|
|
5077
|
+
return { lane: refreshedLane ?? lane };
|
|
5065
5078
|
}
|
|
5066
5079
|
async function runShow(ctx, laneId, hasPendingQuestionForLane) {
|
|
5067
5080
|
const hqRoot = ctx.hqRoot();
|
|
5068
5081
|
const initialLane = readLane(hqRoot, laneId);
|
|
5069
5082
|
const initialLanes = loadAllLanes(hqRoot);
|
|
5070
|
-
const
|
|
5083
|
+
const showRefresh = await refreshOutpostLaneCopiesForShow(ctx, initialLane, initialLanes);
|
|
5084
|
+
const storedLane = showRefresh.lane;
|
|
5071
5085
|
const allLanes = loadAllLanes(hqRoot);
|
|
5072
5086
|
const hire = await laneHirePresentation(ctx, storedLane, allLanes);
|
|
5073
5087
|
const remoteQueued = storedLane.state === "queued" &&
|
|
@@ -5152,6 +5166,7 @@ async function runShow(ctx, laneId, hasPendingQuestionForLane) {
|
|
|
5152
5166
|
stalled_reason: lane.stalled_reason ?? null,
|
|
5153
5167
|
ask_delivery_failed: askDeliveryFailed,
|
|
5154
5168
|
delivery,
|
|
5169
|
+
...(showRefresh.remoteRefresh === undefined ? {} : { remote_refresh: showRefresh.remoteRefresh }),
|
|
5155
5170
|
provider_session_id: lane.provider_session_id ?? null,
|
|
5156
5171
|
provider_session_id_absent: lane.provider_session_id_absent ?? null,
|
|
5157
5172
|
envelope_repair_mode: lane.envelope_repair?.mode ?? null,
|
|
@@ -55,7 +55,7 @@ export declare const DEFAULT_PER_CASE_TIMEOUT_MS = 10000;
|
|
|
55
55
|
*/
|
|
56
56
|
export declare const SANDBOX_COPY_RELPATHS: readonly string[];
|
|
57
57
|
/** One gate execution's inputs. */
|
|
58
|
-
|
|
58
|
+
interface GateRunInput {
|
|
59
59
|
/** Absolute path to the sandboxed `hook-gate.sh`. */
|
|
60
60
|
gatePath: string;
|
|
61
61
|
/** Absolute path to the sandboxed hook script the gate delegates to. */
|
|
@@ -72,7 +72,7 @@ export interface GateRunInput {
|
|
|
72
72
|
timeoutMs: number;
|
|
73
73
|
}
|
|
74
74
|
/** One gate execution's observed outcome. */
|
|
75
|
-
|
|
75
|
+
interface GateRunResult {
|
|
76
76
|
/** True when the execution exceeded {@link GateRunInput.timeoutMs}. */
|
|
77
77
|
timedOut: boolean;
|
|
78
78
|
/** Gate exit code, or null when it did not exit normally (timeout/signal). */
|
|
@@ -81,14 +81,7 @@ export interface GateRunResult {
|
|
|
81
81
|
stderr: string;
|
|
82
82
|
}
|
|
83
83
|
/** Executes one gate invocation. Injectable so the aggregation is unit-testable. */
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* The real runner: spawn `bash hook-gate.sh <id> <script>` with the payload on
|
|
87
|
-
* stdin, `HQ_HOOK_PROFILE` set, and a hard timeout. A timeout is detected by the
|
|
88
|
-
* `ETIMEDOUT` error code spawnSync reports; the process is killed with SIGKILL so
|
|
89
|
-
* a hook cannot ignore the bound.
|
|
90
|
-
*/
|
|
91
|
-
export declare const defaultGateRunner: GateRunner;
|
|
84
|
+
type GateRunner = (input: GateRunInput) => GateRunResult;
|
|
92
85
|
/** Options for {@link runDeepGuardTests}. All are injectable for testing. */
|
|
93
86
|
export interface DeepTestOptions {
|
|
94
87
|
/** Per-case time bound. Default: {@link DEFAULT_PER_CASE_TIMEOUT_MS}. */
|
|
@@ -109,4 +102,5 @@ export declare function cleanupAllDeepSandboxes(): void;
|
|
|
109
102
|
* an empty list — creating no sandbox — when there is nothing to run.
|
|
110
103
|
*/
|
|
111
104
|
export declare function runDeepGuardTests(context: CheckContext, options?: DeepTestOptions): Promise<CheckResult[]>;
|
|
105
|
+
export {};
|
|
112
106
|
//# sourceMappingURL=executor.d.ts.map
|
|
@@ -74,7 +74,7 @@ export const SANDBOX_COPY_RELPATHS = [
|
|
|
74
74
|
* `ETIMEDOUT` error code spawnSync reports; the process is killed with SIGKILL so
|
|
75
75
|
* a hook cannot ignore the bound.
|
|
76
76
|
*/
|
|
77
|
-
|
|
77
|
+
const defaultGateRunner = (input) => {
|
|
78
78
|
const result = spawnSync("bash", [input.gatePath, input.hookId, input.hookScriptPath], {
|
|
79
79
|
input: input.payload,
|
|
80
80
|
cwd: input.cwd,
|
|
@@ -46,14 +46,6 @@ import { type PlatformId } from "../payload-shapes.js";
|
|
|
46
46
|
export declare const PARITY_FAMILY_ID = "deep-parity";
|
|
47
47
|
/** Human title of the parity family. */
|
|
48
48
|
export declare const PARITY_FAMILY_TITLE = "Cross-platform parity replay";
|
|
49
|
-
/** Default per-case, per-platform time bound. */
|
|
50
|
-
export declare const DEFAULT_PARITY_TIMEOUT_MS = 10000;
|
|
51
|
-
/** The gate profile the replay runs each hook under. Strict is the broadest
|
|
52
|
-
* allowlist, so profile gating never suppresses the hook whose LOGIC is being
|
|
53
|
-
* compared across platforms — profile-specific behaviour is US-008's concern. */
|
|
54
|
-
export declare const PARITY_GATE_PROFILE = "strict";
|
|
55
|
-
/** The session id stamped onto every replay payload. */
|
|
56
|
-
export declare const PARITY_SESSION_ID = "hq-doctor-parity-replay";
|
|
57
49
|
/** One platform replay's inputs. */
|
|
58
50
|
export interface PlatformRunInput {
|
|
59
51
|
/** Which platform's adapter path to exercise. */
|
|
@@ -85,27 +77,7 @@ export interface PlatformRunOutput {
|
|
|
85
77
|
/** Executes one platform replay. Injectable so the comparison is unit-testable. */
|
|
86
78
|
export type PlatformRunner = (input: PlatformRunInput) => PlatformRunOutput;
|
|
87
79
|
/** The real runner set, keyed by platform. */
|
|
88
|
-
|
|
89
|
-
/** The real Claude runner: the canonical script through the Claude gate. */
|
|
90
|
-
export declare const defaultClaudeRunner: PlatformRunner;
|
|
91
|
-
/** The real Codex runner: the duplicated mirror through the Codex gate. */
|
|
92
|
-
export declare const defaultCodexRunner: PlatformRunner;
|
|
93
|
-
/**
|
|
94
|
-
* The real Grok runner. Grok shares the canonical `.claude/hooks/` scripts with
|
|
95
|
-
* Claude — it has no mirror of its own — and its adapter's whole job is to read
|
|
96
|
-
* the camelCase payload, map the tool alias back to canonical, build the Claude
|
|
97
|
-
* JSON, and pipe it to the same `hook-gate.sh`. We reproduce that routing here
|
|
98
|
-
* (via {@link toGrokPayload} + {@link grokToSnakePayload}) rather than spawn the
|
|
99
|
-
* adapter directly, for two reasons: the live adapter runs a whole tool
|
|
100
|
-
* *sequence* where the replay needs one isolated hook, and its user-global
|
|
101
|
-
* bridge cannot be simulated from a project-local run (US-005 keeps bridge
|
|
102
|
-
* presence a wiring check). The Grok block protocol still applies — the gate's
|
|
103
|
-
* non-zero exit is what the adapter would translate into a stdout `deny`
|
|
104
|
-
* ({@link interpretGrokBlock} in verdictFor recognises both).
|
|
105
|
-
*/
|
|
106
|
-
export declare const defaultGrokRunner: PlatformRunner;
|
|
107
|
-
/** The default real runner set wired to each platform's real adapter path. */
|
|
108
|
-
export declare const defaultPlatformRunners: PlatformRunners;
|
|
80
|
+
type PlatformRunners = Record<PlatformId, PlatformRunner>;
|
|
109
81
|
/** Options for {@link runParityReplay}. All injectable for testing. */
|
|
110
82
|
export interface ParityOptions {
|
|
111
83
|
/** Per-case, per-platform time bound. Default: {@link DEFAULT_PARITY_TIMEOUT_MS}. */
|
|
@@ -126,4 +98,5 @@ export declare function cleanupAllParitySandboxes(): void;
|
|
|
126
98
|
* empty list — creating no sandbox — when there is nothing to replay.
|
|
127
99
|
*/
|
|
128
100
|
export declare function runParityReplay(context: CheckContext, options?: ParityOptions): Promise<CheckResult[]>;
|
|
101
|
+
export {};
|
|
129
102
|
//# sourceMappingURL=parity.d.ts.map
|
|
@@ -52,13 +52,13 @@ export const PARITY_FAMILY_ID = "deep-parity";
|
|
|
52
52
|
/** Human title of the parity family. */
|
|
53
53
|
export const PARITY_FAMILY_TITLE = "Cross-platform parity replay";
|
|
54
54
|
/** Default per-case, per-platform time bound. */
|
|
55
|
-
|
|
55
|
+
const DEFAULT_PARITY_TIMEOUT_MS = 10_000;
|
|
56
56
|
/** The gate profile the replay runs each hook under. Strict is the broadest
|
|
57
57
|
* allowlist, so profile gating never suppresses the hook whose LOGIC is being
|
|
58
58
|
* compared across platforms — profile-specific behaviour is US-008's concern. */
|
|
59
|
-
|
|
59
|
+
const PARITY_GATE_PROFILE = "strict";
|
|
60
60
|
/** The session id stamped onto every replay payload. */
|
|
61
|
-
|
|
61
|
+
const PARITY_SESSION_ID = "hq-doctor-parity-replay";
|
|
62
62
|
function spawnWithStdin(command, args, stdin, input) {
|
|
63
63
|
const result = spawnSync(command, args, {
|
|
64
64
|
input: stdin,
|
|
@@ -96,12 +96,12 @@ function runThroughGate(input, platformDir, payload) {
|
|
|
96
96
|
return { ...spawned };
|
|
97
97
|
}
|
|
98
98
|
/** The real Claude runner: the canonical script through the Claude gate. */
|
|
99
|
-
|
|
99
|
+
const defaultClaudeRunner = (input) => runThroughGate(input, ".claude", JSON.stringify(toClaudePayload(input.testCase, {
|
|
100
100
|
cwd: input.sandbox,
|
|
101
101
|
sessionId: PARITY_SESSION_ID,
|
|
102
102
|
})));
|
|
103
103
|
/** The real Codex runner: the duplicated mirror through the Codex gate. */
|
|
104
|
-
|
|
104
|
+
const defaultCodexRunner = (input) => runThroughGate(input, ".codex", JSON.stringify(toCodexPayload(input.testCase, {
|
|
105
105
|
cwd: input.sandbox,
|
|
106
106
|
sessionId: PARITY_SESSION_ID,
|
|
107
107
|
})));
|
|
@@ -118,7 +118,7 @@ export const defaultCodexRunner = (input) => runThroughGate(input, ".codex", JSO
|
|
|
118
118
|
* non-zero exit is what the adapter would translate into a stdout `deny`
|
|
119
119
|
* ({@link interpretGrokBlock} in verdictFor recognises both).
|
|
120
120
|
*/
|
|
121
|
-
|
|
121
|
+
const defaultGrokRunner = (input) => {
|
|
122
122
|
const grokPayload = toGrokPayload(input.testCase, {
|
|
123
123
|
cwd: input.sandbox,
|
|
124
124
|
sessionId: PARITY_SESSION_ID,
|
|
@@ -127,7 +127,7 @@ export const defaultGrokRunner = (input) => {
|
|
|
127
127
|
return runThroughGate(input, ".claude", JSON.stringify(snake));
|
|
128
128
|
};
|
|
129
129
|
/** The default real runner set wired to each platform's real adapter path. */
|
|
130
|
-
|
|
130
|
+
const defaultPlatformRunners = {
|
|
131
131
|
claude: defaultClaudeRunner,
|
|
132
132
|
codex: defaultCodexRunner,
|
|
133
133
|
grok: defaultGrokRunner,
|
|
@@ -46,21 +46,10 @@ export declare const DEFAULT_PER_CASE_TIMEOUT_MS = 15000;
|
|
|
46
46
|
* come under sandboxed execution.
|
|
47
47
|
*/
|
|
48
48
|
export type Precondition = "git-repo" | "session-metadata" | "company-scaffold";
|
|
49
|
-
/** Every precondition the default seeder knows how to provide. */
|
|
50
|
-
export declare const KNOWN_PRECONDITIONS: readonly Precondition[];
|
|
51
|
-
/**
|
|
52
|
-
* Tree entries copied from the live tree into every sandbox — the same curated
|
|
53
|
-
* set US-008 uses: the hook scripts and gate, the settings a hook may read, the
|
|
54
|
-
* Codex/Grok mirrors, and `core/` (helpers hooks source). Missing entries are
|
|
55
|
-
* skipped so a partial tree still sandboxes cleanly.
|
|
56
|
-
*/
|
|
57
|
-
export declare const SANDBOX_SEED_RELPATHS: readonly string[];
|
|
58
49
|
/** Remove every side-effect sandbox still on disk. Safe to call repeatedly. */
|
|
59
50
|
export declare function cleanupAllSandboxes(): void;
|
|
60
51
|
/** The sandbox roots this process is still tracking (for tests). */
|
|
61
52
|
export declare function trackedSandboxRoots(): string[];
|
|
62
|
-
/** Remove one sandbox and stop tracking it. Idempotent. */
|
|
63
|
-
export declare function destroySandbox(root: string): void;
|
|
64
53
|
/** The outcome of seeding one sandbox. */
|
|
65
54
|
export interface SeedResult {
|
|
66
55
|
/** Absolute, realpath'd sandbox root under the OS temp dir. */
|
|
@@ -91,7 +80,7 @@ export interface SeedOptions {
|
|
|
91
80
|
*/
|
|
92
81
|
export declare function seedSandbox(hqRoot: string, opts?: SeedOptions): SeedResult;
|
|
93
82
|
/** One hook execution's inputs. */
|
|
94
|
-
|
|
83
|
+
interface HookRunInput {
|
|
95
84
|
/** Absolute path to the sandboxed hook script. */
|
|
96
85
|
hookScriptPath: string;
|
|
97
86
|
/** The JSON payload piped to the hook's stdin. */
|
|
@@ -104,7 +93,7 @@ export interface HookRunInput {
|
|
|
104
93
|
timeoutMs: number;
|
|
105
94
|
}
|
|
106
95
|
/** One hook execution's observed result (exit code is diagnostic only). */
|
|
107
|
-
|
|
96
|
+
interface HookRunResult {
|
|
108
97
|
/** True when the execution exceeded {@link HookRunInput.timeoutMs}. */
|
|
109
98
|
timedOut: boolean;
|
|
110
99
|
/** Exit code, or null when it did not exit normally (timeout/signal). */
|
|
@@ -113,17 +102,9 @@ export interface HookRunResult {
|
|
|
113
102
|
stderr: string;
|
|
114
103
|
}
|
|
115
104
|
/** Runs one hook. Injectable so the tier's aggregation is unit-testable. */
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* The real runner: fire the hook script directly with the payload on stdin, its
|
|
119
|
-
* cwd and session env pointed at the sandbox, and a hard timeout. Unlike a pure
|
|
120
|
-
* guard (US-008), a side-effecting hook is run directly rather than through the
|
|
121
|
-
* gate — its exit code is irrelevant here; what matters is the effect it leaves
|
|
122
|
-
* on the sandbox, which effects.ts reads afterwards.
|
|
123
|
-
*/
|
|
124
|
-
export declare const defaultHookRunner: HookRunner;
|
|
105
|
+
type HookRunner = (input: HookRunInput) => HookRunResult;
|
|
125
106
|
/** One parsed side-effecting fixture case (its own shape, not the US-007 schema). */
|
|
126
|
-
|
|
107
|
+
interface SideEffectCase {
|
|
127
108
|
name: string;
|
|
128
109
|
event: string;
|
|
129
110
|
tool: string;
|
|
@@ -136,13 +117,13 @@ export interface SideEffectCase {
|
|
|
136
117
|
expectedFailure?: ExpectedFailure;
|
|
137
118
|
}
|
|
138
119
|
/** A validated side-effecting fixture. */
|
|
139
|
-
|
|
120
|
+
interface SideEffectFixture {
|
|
140
121
|
hookId: string;
|
|
141
122
|
relpath: string;
|
|
142
123
|
cases: SideEffectCase[];
|
|
143
124
|
}
|
|
144
125
|
/** A side-effecting fixture that could not be parsed. */
|
|
145
|
-
|
|
126
|
+
interface SideEffectFixtureError {
|
|
146
127
|
hookId: string | null;
|
|
147
128
|
relpath: string;
|
|
148
129
|
message: string;
|
|
@@ -187,4 +168,5 @@ export interface SideEffectTestOptions {
|
|
|
187
168
|
* Returns an empty list when there is nothing side-effecting to run.
|
|
188
169
|
*/
|
|
189
170
|
export declare function runSideEffectTests(context: CheckContext, options?: SideEffectTestOptions): Promise<CheckResult[]>;
|
|
171
|
+
export {};
|
|
190
172
|
//# sourceMappingURL=sandbox.d.ts.map
|
|
@@ -46,7 +46,7 @@ import { captureEffectBaseline, describeEffect, defaultGitProbe, evaluateEffects
|
|
|
46
46
|
/** Default per-case time bound. No latency target, but no case may hang. */
|
|
47
47
|
export const DEFAULT_PER_CASE_TIMEOUT_MS = 15_000;
|
|
48
48
|
/** Every precondition the default seeder knows how to provide. */
|
|
49
|
-
|
|
49
|
+
const KNOWN_PRECONDITIONS = [
|
|
50
50
|
"git-repo",
|
|
51
51
|
"session-metadata",
|
|
52
52
|
"company-scaffold",
|
|
@@ -57,7 +57,7 @@ export const KNOWN_PRECONDITIONS = [
|
|
|
57
57
|
* Codex/Grok mirrors, and `core/` (helpers hooks source). Missing entries are
|
|
58
58
|
* skipped so a partial tree still sandboxes cleanly.
|
|
59
59
|
*/
|
|
60
|
-
|
|
60
|
+
const SANDBOX_SEED_RELPATHS = [
|
|
61
61
|
".claude/hooks",
|
|
62
62
|
".claude/settings.json",
|
|
63
63
|
".claude/settings.local.json",
|
|
@@ -100,7 +100,7 @@ function removeTree(root) {
|
|
|
100
100
|
}
|
|
101
101
|
}
|
|
102
102
|
/** Remove one sandbox and stop tracking it. Idempotent. */
|
|
103
|
-
|
|
103
|
+
function destroySandbox(root) {
|
|
104
104
|
removeTree(root);
|
|
105
105
|
trackedSandboxes.delete(root);
|
|
106
106
|
}
|
|
@@ -198,7 +198,7 @@ function git(root, args) {
|
|
|
198
198
|
* gate — its exit code is irrelevant here; what matters is the effect it leaves
|
|
199
199
|
* on the sandbox, which effects.ts reads afterwards.
|
|
200
200
|
*/
|
|
201
|
-
|
|
201
|
+
const defaultHookRunner = (input) => {
|
|
202
202
|
const result = spawnSync("bash", [input.hookScriptPath], {
|
|
203
203
|
input: input.payload,
|
|
204
204
|
cwd: input.cwd,
|