vouchington-tooling 0.12.3 → 0.13.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/README.md CHANGED
@@ -71,14 +71,13 @@ vouchington stage-review-payload optional|required <source> <destination>
71
71
  For persistent `pnpm-install`, v5 metadata tracks structural inputs separately from the
72
72
  `--install-scripts` policy. A warm scripts-enabled tree can therefore toggle
73
73
  `true → false → true` without forced reconciliation; a tree first installed with scripts disabled
74
- uses one script-suppressed verification install followed by `pnpm rebuild --pending --recursive`.
75
- When only newly pending dependency package IDs remain, it instead rebuilds those exact IDs without
76
- rerunning first-party workspace hooks.
77
- If that generic rebuild leaves only pnpm's root importer marker, one root-only pending rebuild runs
78
- the root lifecycle hook; every other residual ID remains a hard failure. An isolated native-binary
74
+ uses two script-suppressed verification installs followed by `pnpm rebuild --pending --recursive`.
75
+ Before rebuilding, duplicate pending IDs are collapsed. After rebuilding, IDs proven absent from
76
+ the current workspace and installed virtual-store lockfiles are reported and removed; live
77
+ dependency and workspace-importer IDs are retained. Any residual ID after the generic rebuild remains a reported hard failure. An isolated native-binary
79
78
  mismatch uses one strict forced install only when structural provenance
80
79
  matches, workspace links are valid, and pnpm records empty `ignoredBuilds` and `pendingBuilds` ledgers;
81
- otherwise it retains the script-free then strict reconciliation. Native and workspace-link health
80
+ otherwise it retains the two script-free reconciliation passes. Native and workspace-link health
82
81
  are verified before its metadata stamp is refreshed.
83
82
  The command emits a structured non-secret provenance diagnostic identifying changed structural
84
83
  categories, the last script policy, script capability, and native-binary health.
@@ -153,6 +152,10 @@ import {
153
152
  checkHarnessConfig,
154
153
  dumpHarnessPolicy,
155
154
  } from 'vouchington-tooling/agent-harness-config'
155
+ import {
156
+ inspectHarnessEnvironment,
157
+ selectHarnessSession,
158
+ } from 'vouchington-tooling/agent-harness-identity'
156
159
  import {
157
160
  readVitestReportAttempts,
158
161
  writeVitestBlobManifest,
@@ -247,6 +250,10 @@ classifier stays available in plan mode; its defined sandbox profile still requi
247
250
  `unrestricted` disables the sandbox. Repo checks surface user-level trust or mode prerequisites
248
251
  instead of claiming that repo files alone activate them.
249
252
 
253
+ `agent-harness-identity` inspects the four harness environment signals without choosing a winner.
254
+ Callers supply their own precedence to `selectHarnessSession`, keeping product-specific identity
255
+ policy out of this package.
256
+
250
257
  `checkWorkspaceGatesPolicy` rejects tracked test assertions that hard-code the exact version of a
251
258
  dependency declared by a non-fixture package manifest. Assert dependency membership or placement,
252
259
  or derive a configuration or documentation package spec from that manifest instead.
@@ -1,5 +1,7 @@
1
- export declare const HARNESS_IDS: readonly ['claude', 'codex', 'grok', 'cursor'];
2
- export type HarnessId = (typeof HARNESS_IDS)[number];
1
+ import { HARNESS_IDS } from '../agent-harness-identity/index.mts';
2
+ import type { HarnessId } from '../agent-harness-identity/index.mts';
3
+ export { HARNESS_IDS };
4
+ export type { HarnessId };
3
5
  export type ApplyTarget = {
4
6
  readonly kind: 'global';
5
7
  } | {
@@ -1 +1,2 @@
1
- export const HARNESS_IDS = ['claude', 'codex', 'grok', 'cursor'];
1
+ import { HARNESS_IDS } from '../agent-harness-identity/index.mjs';
2
+ export { HARNESS_IDS };
@@ -0,0 +1,26 @@
1
+ export declare const HARNESS_IDS: readonly ['claude', 'codex', 'grok', 'cursor'];
2
+ export type HarnessId = (typeof HARNESS_IDS)[number];
3
+ export interface HarnessEnvironment {
4
+ readonly claude: {
5
+ readonly compatMode: boolean;
6
+ readonly sessionId?: string;
7
+ };
8
+ readonly codex: {
9
+ readonly sessionId?: string;
10
+ };
11
+ readonly cursor: {
12
+ readonly agentMarker: boolean;
13
+ readonly sessionId?: string;
14
+ };
15
+ readonly grok: {
16
+ readonly agentMarker: boolean;
17
+ readonly hookEvent: boolean;
18
+ readonly sessionId?: string;
19
+ };
20
+ }
21
+ export interface HarnessSessionIdentity {
22
+ readonly harness: HarnessId;
23
+ readonly sessionId: string;
24
+ }
25
+ export declare function inspectHarnessEnvironment(env?: NodeJS.ProcessEnv): HarnessEnvironment;
26
+ export declare function selectHarnessSession(environment: HarnessEnvironment, precedence: readonly HarnessId[]): HarnessSessionIdentity | undefined;
@@ -0,0 +1,30 @@
1
+ export const HARNESS_IDS = ['claude', 'codex', 'grok', 'cursor'];
2
+ function session(value) {
3
+ return value ? { sessionId: value } : {};
4
+ }
5
+ export function inspectHarnessEnvironment(env = process.env) {
6
+ return {
7
+ claude: {
8
+ compatMode: env.CLAUDECODE === '1',
9
+ ...session(env.CLAUDE_CODE_SESSION_ID),
10
+ },
11
+ codex: session(env.CODEX_THREAD_ID),
12
+ cursor: {
13
+ agentMarker: Boolean(env.CURSOR_AGENT),
14
+ ...session(env.CURSOR_SESSION_ID),
15
+ },
16
+ grok: {
17
+ agentMarker: Boolean(env.GROK_AGENT),
18
+ hookEvent: Boolean(env.GROK_HOOK_EVENT),
19
+ ...session(env.GROK_SESSION_ID),
20
+ },
21
+ };
22
+ }
23
+ export function selectHarnessSession(environment, precedence) {
24
+ for (const harness of precedence) {
25
+ const sessionId = environment[harness].sessionId;
26
+ if (sessionId)
27
+ return { harness, sessionId };
28
+ }
29
+ return undefined;
30
+ }
package/dist/index.d.mts CHANGED
@@ -1,5 +1,7 @@
1
1
  export { linkSkill, readSkillManifest } from './skill-discovery/index.mts';
2
2
  export type { LinkSkillOptions, LinkSkillResult, SkillManifest, SkillManifestEntry, } from './skill-discovery/index.mts';
3
+ export { HARNESS_IDS, inspectHarnessEnvironment, selectHarnessSession, } from './agent-harness-identity/index.mts';
4
+ export type { HarnessEnvironment, HarnessSessionIdentity } from './agent-harness-identity/index.mts';
3
5
  export { codexChildren, codexIdentity, computeTranscriptFacts, formatTranscriptFacts, formatUnavailable, resolveTranscriptFile, runRetrospectiveTranscript, } from './retrospective-transcript/index.mts';
4
6
  export type { ResolveOptions, TokenTotals, TranscriptFacts, } from './retrospective-transcript/index.mts';
5
7
  export { runRetrospectiveFacts } from './retrospective-facts/index.mts';
@@ -13,7 +15,7 @@ export type { EphemeralListenerOptions, RunnerPortPolicy } from './runner-port-p
13
15
  export { extractAlterTableAddColumnLocations, extractCreateIndexMetadata, extractCreateTableMetadata, extractDefaultFunction, extractDropIndexMetadata, extractFuncCallArgColumnNames, extractMigrationConstraintMetadata, initSqlAst, lineOfUtf8ByteOffset, MissingSqlAstParserError, parseSql, } from './sql-ast/index.mts';
14
16
  export type { ForeignKey, SqlCreateIndexMetadata, SqlCreateTableColumn, SqlCreateTableMetadata, SqlDropIndexMetadata, SqlIndexParam, SqlMigrationConstraintMetadata, } from './sql-ast/index.mts';
15
17
  export { dollarQuoteEnd, lineOf, maskSqlQuotedText, readDollarQuoteDelimiter, readStringLiteral, splitSqlStatements, sqlFragments, stripSqlComments, } from './sql-scanner/index.mts';
16
- export { applyHarnessConfig, checkHarnessConfig, dumpHarnessPolicy, planHarnessConfig, DEFAULT_EXTRA_WRITABLE_ROOTS, HARNESS_IDS, } from './agent-harness-config/index.mts';
18
+ export { applyHarnessConfig, checkHarnessConfig, dumpHarnessPolicy, planHarnessConfig, DEFAULT_EXTRA_WRITABLE_ROOTS, } from './agent-harness-config/index.mts';
17
19
  export type { ApplyTarget, FilePlan, FileResult, HarnessApplyResult, HarnessCheckResult, HarnessConfigOptions, HarnessId, HarnessPlan, HarnessPolicyDump, JsonPatch, KeyDrift, TomlPatch, TomlValue, } from './agent-harness-config/index.mts';
18
20
  export { auditCiJobRuntime, parseWorkflowNameMatch } from './gha-runtime-audit/index.mts';
19
21
  export type { GhApiExecutor, RuntimeAuditOptions, RuntimeAuditResult, RuntimeAuditWorkflowFilter, RuntimeJobResult, RuntimeSample, } from './gha-runtime-audit/index.mts';
package/dist/index.mjs CHANGED
@@ -1,5 +1,6 @@
1
1
  /* eslint-disable max-lines -- package entry point enumerates the supported public API. */
2
2
  export { linkSkill, readSkillManifest } from './skill-discovery/index.mjs';
3
+ export { HARNESS_IDS, inspectHarnessEnvironment, selectHarnessSession, } from './agent-harness-identity/index.mjs';
3
4
  export { codexChildren, codexIdentity, computeTranscriptFacts, formatTranscriptFacts, formatUnavailable, resolveTranscriptFile, runRetrospectiveTranscript, } from './retrospective-transcript/index.mjs';
4
5
  export { runRetrospectiveFacts } from './retrospective-facts/index.mjs';
5
6
  export { appendJournal, assertSessionId, cleanupSnapshotPartitions, partitionSnapshot, probeBlackboard, readJournal, resolveBlackboardConnection, } from './agent-blackboard/index.mjs';
@@ -7,7 +8,7 @@ export { buildSessionFrictionReport, classifyFrictionObservation, FRICTION_LOG_M
7
8
  export { EphemeralListenerAttemptsExhaustedError, isRunnerReservedPort, listenOnRunnerUnreservedEphemeralPort, loadRunnerPortPolicy, runnerPortPolicy, validateRunnerPortPolicy, } from './runner-port-policy/index.mjs';
8
9
  export { extractAlterTableAddColumnLocations, extractCreateIndexMetadata, extractCreateTableMetadata, extractDefaultFunction, extractDropIndexMetadata, extractFuncCallArgColumnNames, extractMigrationConstraintMetadata, initSqlAst, lineOfUtf8ByteOffset, MissingSqlAstParserError, parseSql, } from './sql-ast/index.mjs';
9
10
  export { dollarQuoteEnd, lineOf, maskSqlQuotedText, readDollarQuoteDelimiter, readStringLiteral, splitSqlStatements, sqlFragments, stripSqlComments, } from './sql-scanner/index.mjs';
10
- export { applyHarnessConfig, checkHarnessConfig, dumpHarnessPolicy, planHarnessConfig, DEFAULT_EXTRA_WRITABLE_ROOTS, HARNESS_IDS, } from './agent-harness-config/index.mjs';
11
+ export { applyHarnessConfig, checkHarnessConfig, dumpHarnessPolicy, planHarnessConfig, DEFAULT_EXTRA_WRITABLE_ROOTS, } from './agent-harness-config/index.mjs';
11
12
  export { auditCiJobRuntime, parseWorkflowNameMatch } from './gha-runtime-audit/index.mjs';
12
13
  export { checkGhaWorkspacePolicy } from './gha-workspace-policy/index.mjs';
13
14
  export { requireUpToDate } from './require-up-to-date/index.mjs';
@@ -1,7 +1,7 @@
1
1
  import { scheduler } from 'node:timers/promises';
2
2
  import { runPnpm } from './exec.mjs';
3
3
  import { nativeBinariesMatchRuntime, repairedNativeBinariesMatchRuntime } from './native-health.mjs';
4
- import { buildLedgersAllowNativeRepair, deduplicatePendingBuilds, pendingBuilds, } from './pending-builds.mjs';
4
+ import { buildLedgersAllowNativeRepair, deduplicatePendingBuilds, pendingBuilds, pruneStalePendingBuilds, } from './pending-builds.mjs';
5
5
  import { INSTALL_TERMINATION_FAILED } from './process.mjs';
6
6
  import { formatReleaseAgeFailure, isReleaseAgeViolation } from './release-age.mjs';
7
7
  import { findWorkspaceLinkMismatches, forcedInstallArgs, logWorkspaceLinkMismatches, } from './support.mjs';
@@ -60,8 +60,9 @@ export async function finalizePendingBuilds(options, runCapture, phase) {
60
60
  failPendingBuildLedger(phase, deduplicated);
61
61
  await install(['rebuild', '--pending', '--recursive'], options, 'pending scripts rebuild');
62
62
  const after = await pendingBuilds();
63
- if (after.kind !== 'clear')
64
- failPendingBuildLedger(phase, after);
63
+ const finalState = after.kind === 'pending' ? await pruneStalePendingBuilds() : after;
64
+ if (finalState.kind !== 'clear')
65
+ failPendingBuildLedger(phase, finalState);
65
66
  }
66
67
  await verifyInstallHealth(runCapture, phase);
67
68
  return options.installScripts ? { kind: 'clear' } : before;
@@ -8,4 +8,5 @@ export type PendingBuildState = {
8
8
  };
9
9
  export declare function pendingBuilds(): Promise<PendingBuildState>;
10
10
  export declare function deduplicatePendingBuilds(): Promise<PendingBuildState>;
11
+ export declare function pruneStalePendingBuilds(): Promise<PendingBuildState>;
11
12
  export declare function buildLedgersAllowNativeRepair(): Promise<boolean>;
@@ -1,11 +1,15 @@
1
- import { readFile, writeFile } from 'node:fs/promises';
1
+ import { randomUUID } from 'node:crypto';
2
+ import { readFile, rename, rm, writeFile } from 'node:fs/promises';
2
3
  import path from 'node:path';
3
4
  import { parse, stringify } from 'yaml';
4
5
  const modulesPath = () => path.join(process.cwd(), 'node_modules', '.modules.yaml');
6
+ function isRecord(value) {
7
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
8
+ }
5
9
  async function buildLedgers() {
6
10
  try {
7
11
  const value = parse(await readFile(modulesPath(), 'utf8'));
8
- if (typeof value !== 'object' || value === null || Array.isArray(value))
12
+ if (!isRecord(value))
9
13
  return undefined;
10
14
  // oxlint-disable-next-line no-mistakes/ts-no-const-aliases -- validate the parsed YAML object before reading its fields
11
15
  const record = value;
@@ -29,21 +33,61 @@ async function buildLedgers() {
29
33
  export async function pendingBuilds() {
30
34
  return (await buildLedgers())?.pendingBuilds ?? { kind: 'unknown' };
31
35
  }
36
+ async function lockfileBuildIds(lockfilePath) {
37
+ try {
38
+ const lockfile = parse(await readFile(lockfilePath, 'utf8'));
39
+ if (!isRecord(lockfile) || !isRecord(lockfile.importers) || !isRecord(lockfile.packages))
40
+ return undefined;
41
+ return new Set([...Object.keys(lockfile.importers), ...Object.keys(lockfile.packages)]);
42
+ }
43
+ catch {
44
+ return undefined;
45
+ }
46
+ }
47
+ async function currentBuildIds(record) {
48
+ if (typeof record.virtualStoreDir !== 'string')
49
+ return undefined;
50
+ const [wanted, installed] = await Promise.all([
51
+ lockfileBuildIds(path.join(process.cwd(), 'pnpm-lock.yaml')),
52
+ lockfileBuildIds(path.resolve(path.dirname(modulesPath()), record.virtualStoreDir, 'lock.yaml')),
53
+ ]);
54
+ if (wanted === undefined || installed === undefined)
55
+ return undefined;
56
+ return new Set([...wanted, ...installed]);
57
+ }
58
+ async function rewritePendingBuilds(record, originalLength, ids) {
59
+ if (ids.length === originalLength)
60
+ return { ids: ids.toSorted(), kind: 'pending' };
61
+ const temporary = `${modulesPath()}.${randomUUID()}.tmp`;
62
+ try {
63
+ await writeFile(temporary, stringify({ ...record, pendingBuilds: ids }), { flag: 'wx' });
64
+ await rename(temporary, modulesPath());
65
+ }
66
+ catch {
67
+ await rm(temporary, { force: true });
68
+ return { kind: 'unknown' };
69
+ }
70
+ return pendingBuilds();
71
+ }
32
72
  export async function deduplicatePendingBuilds() {
33
73
  const ledgers = await buildLedgers();
34
74
  if (ledgers === undefined || ledgers.pendingBuilds.kind !== 'pending')
35
75
  return ledgers?.pendingBuilds ?? { kind: 'unknown' };
36
76
  // `buildLedgers` has already verified every pending entry is a string.
37
77
  const unique = [...new Set(ledgers.record.pendingBuilds)];
38
- if (unique.length === ledgers.pendingBuilds.ids.length)
39
- return ledgers.pendingBuilds;
40
- try {
41
- await writeFile(modulesPath(), stringify({ ...ledgers.record, pendingBuilds: unique }));
42
- }
43
- catch {
78
+ return rewritePendingBuilds(ledgers.record, ledgers.pendingBuilds.ids.length, unique);
79
+ }
80
+ export async function pruneStalePendingBuilds() {
81
+ const ledgers = await buildLedgers();
82
+ if (ledgers === undefined || ledgers.pendingBuilds.kind !== 'pending')
83
+ return ledgers?.pendingBuilds ?? { kind: 'unknown' };
84
+ const current = await currentBuildIds(ledgers.record);
85
+ if (current === undefined)
44
86
  return { kind: 'unknown' };
45
- }
46
- return pendingBuilds();
87
+ const stale = ledgers.pendingBuilds.ids.filter((id) => !current.has(id));
88
+ if (stale.length > 0)
89
+ console.warn(`pending-build-ledger-pruned-stale IDs: ${JSON.stringify(stale)}`);
90
+ return rewritePendingBuilds(ledgers.record, ledgers.pendingBuilds.ids.length, ledgers.pendingBuilds.ids.filter((id) => current.has(id)));
47
91
  }
48
92
  export async function buildLedgersAllowNativeRepair() {
49
93
  const ledgers = await buildLedgers();
@@ -15,6 +15,11 @@ calls=0
15
15
  if [ -f "$PNPM_CALLS" ]; then calls="$(cat "$PNPM_CALLS")"; fi
16
16
  calls=$((calls + 1))
17
17
  printf '%s' "$calls" > "$PNPM_CALLS"
18
+ write_modules() {
19
+ mkdir -p "$PNPM_NODE_MODULES/.pnpm"
20
+ cp pnpm-lock.yaml "$PNPM_NODE_MODULES/.pnpm/lock.yaml"
21
+ printf 'virtualStoreDir: .pnpm\npendingBuilds: [%s]\n' "$1" > "$PNPM_NODE_MODULES/.modules.yaml"
22
+ }
18
23
  print_release_age_violation() {
19
24
  printf '%s\\n' '✗ Lockfile failed supply-chain policy check (1 entries in 0.1s)'
20
25
  printf '%s\\n' '[ERR_PNPM_MINIMUM_RELEASE_AGE_VIOLATION] 1 lockfile entries failed verification:'
@@ -31,36 +36,36 @@ fi
31
36
  if [ -n "\${PNPM_SLEEP_SECONDS:-}" ]; then sleep "$PNPM_SLEEP_SECONDS"; fi
32
37
  if [ "\${1:-}" != rebuild ] && [ "\${PNPM_INVALID_PENDING_BUILDS:-0}" = 1 ]; then
33
38
  mkdir -p "$PNPM_NODE_MODULES"
34
- printf 'pendingBuilds: invalid\n' > "$PNPM_NODE_MODULES/.modules.yaml"
39
+ printf 'virtualStoreDir: .pnpm\npendingBuilds: invalid\n' > "$PNPM_NODE_MODULES/.modules.yaml"
35
40
  elif [ "\${1:-}" != rebuild ] && [ -n "\${PNPM_PENDING_BUILDS:-}" ]; then
36
41
  mkdir -p "$PNPM_NODE_MODULES"
37
- printf 'pendingBuilds: [%s]\\n' "$PNPM_PENDING_BUILDS" > "$PNPM_NODE_MODULES/.modules.yaml"
42
+ write_modules "$PNPM_PENDING_BUILDS"
38
43
  elif [ ! -f "$PNPM_NODE_MODULES/.modules.yaml" ]; then
39
44
  mkdir -p "$PNPM_NODE_MODULES"
40
- printf 'pendingBuilds: []\\n' > "$PNPM_NODE_MODULES/.modules.yaml"
45
+ write_modules ''
41
46
  fi
42
47
  case " $* " in
43
48
  *' rebuild --pending --workspace-root '*)
44
- printf 'pendingBuilds: [%s]\n' "\${PNPM_WORKSPACE_ROOT_REBUILD_PENDING_BUILDS:-}" > "$PNPM_NODE_MODULES/.modules.yaml"
49
+ write_modules "\${PNPM_WORKSPACE_ROOT_REBUILD_PENDING_BUILDS:-}"
45
50
  ;;
46
51
  *' rebuild '*)
47
52
  if [ "\${PNPM_REPAIR_NATIVE_ON_REBUILD:-0}" = 1 ]; then
48
53
  cp "$PNPM_NATIVE_REPLACEMENT" "$PNPM_NATIVE_ADDON"
49
54
  fi
50
55
  if [ "\${PNPM_REBUILD_INVALID_LEDGER:-0}" = 1 ]; then
51
- printf 'pendingBuilds: invalid\n' > "$PNPM_NODE_MODULES/.modules.yaml"
56
+ printf 'virtualStoreDir: .pnpm\npendingBuilds: invalid\n' > "$PNPM_NODE_MODULES/.modules.yaml"
52
57
  elif [ "\${PNPM_REBUILD_REQUIRES_DEDUPED:-0}" = 1 ] && sed -n 's/.*\\[\\(.*\\)\\].*/\\1/p' "$PNPM_NODE_MODULES/.modules.yaml" | tr ',' '\\n' | sed 's/^ *//; s/ *$//' | sort | uniq -d | grep -q .; then
53
58
  cp "$PNPM_NODE_MODULES/.modules.yaml" "$PNPM_NODE_MODULES/.modules.yaml.rebuild-left-pending"
54
59
  elif [ -n "\${PNPM_REBUILD_PENDING_BUILDS:-}" ]; then
55
- printf 'pendingBuilds: [%s]\n' "$PNPM_REBUILD_PENDING_BUILDS" > "$PNPM_NODE_MODULES/.modules.yaml"
60
+ write_modules "$PNPM_REBUILD_PENDING_BUILDS"
56
61
  else
57
- printf 'pendingBuilds: []\n' > "$PNPM_NODE_MODULES/.modules.yaml"
62
+ write_modules ''
58
63
  fi
59
64
  if [ "\${PNPM_REBUILD_BREAK_LINK:-0}" = 1 ]; then rm -f "$PNPM_DEPENDENCY_LINK"; fi
60
65
  ;;
61
66
  *' --force '*)
62
67
  if [ -z "\${PNPM_PENDING_BUILDS:-}" ]; then
63
- printf 'pendingBuilds: []\n' > "$PNPM_NODE_MODULES/.modules.yaml"
68
+ write_modules ''
64
69
  fi
65
70
  if [ "\${PNPM_DELETE_NATIVE:-0}" = 1 ]; then
66
71
  rm -f "$PNPM_NATIVE_ADDON"
@@ -18,10 +18,11 @@ export async function makeFixture() {
18
18
  const pnpmBin = join(root, 'bin');
19
19
  const pnpmLog = join(root, 'pnpm.log');
20
20
  const summary = join(root, 'summary.md');
21
+ const lockfile = 'lockfileVersion: 9\nimporters:\n .: {}\n backend: {}\n packages/consumer: {}\n packages/dependency: {}\npackages:\n dependency: {}\n';
21
22
  await Promise.all([
22
23
  writeJson(join(root, 'package.json'), { name: 'fixture-root', private: true }),
23
24
  writeFile(join(root, 'pnpm-workspace.yaml'), 'packages:\n - packages/*\nminimumReleaseAge: 2880\n'),
24
- writeFile(join(root, 'pnpm-lock.yaml'), 'lockfileVersion: 9\npackages: {}\n'),
25
+ writeFile(join(root, 'pnpm-lock.yaml'), lockfile),
25
26
  writeJson(join(consumer, 'package.json'), {
26
27
  name: '@fixture/consumer',
27
28
  dependencies: { '@fixture/dependency': 'workspace:^' },
@@ -2,6 +2,7 @@ import { existsSync, globSync } from 'node:fs';
2
2
  import { readFile } from 'node:fs/promises';
3
3
  import { homedir } from 'node:os';
4
4
  import { basename, dirname, join } from 'node:path';
5
+ import { inspectHarnessEnvironment, selectHarnessSession, } from '../agent-harness-identity/index.mjs';
5
6
  import { codexChildren, codexIdentity, computeCodex } from './codex.mjs';
6
7
  import { segmentCodex } from './codex-segment.mjs';
7
8
  import { computeClaude } from './claude.mjs';
@@ -28,9 +29,8 @@ export function resolveTranscriptFile(options) {
28
29
  }
29
30
  const env = options.env ?? process.env;
30
31
  const sessionId = options.sessionId ??
31
- ['CODEX_THREAD_ID', 'CLAUDE_CODE_SESSION_ID', 'CURSOR_SESSION_ID', 'GROK_SESSION_ID']
32
- .map((key) => env[key])
33
- .find(Boolean);
32
+ selectHarnessSession(inspectHarnessEnvironment(env), ['codex', 'claude', 'cursor', 'grok'])
33
+ ?.sessionId;
34
34
  if (!sessionId)
35
35
  return {
36
36
  error: 'no session id (pass --session-id or set CODEX_THREAD_ID, CLAUDE_CODE_SESSION_ID, CURSOR_SESSION_ID, or GROK_SESSION_ID)',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vouchington-tooling",
3
- "version": "0.12.3",
3
+ "version": "0.13.0",
4
4
  "description": "Vouchington CLI and extractable tooling libraries.",
5
5
  "homepage": "https://github.com/vouchington/vouchington-tooling/tree/main/packages/vouchington-tooling#readme",
6
6
  "bugs": {
@@ -33,6 +33,11 @@
33
33
  "import": "./dist/index.mjs",
34
34
  "default": "./dist/index.mjs"
35
35
  },
36
+ "./agent-harness-identity": {
37
+ "types": "./dist/agent-harness-identity/index.d.mts",
38
+ "import": "./dist/agent-harness-identity/index.mjs",
39
+ "default": "./dist/agent-harness-identity/index.mjs"
40
+ },
36
41
  "./skill-discovery": {
37
42
  "types": "./dist/skill-discovery/index.d.mts",
38
43
  "import": "./dist/skill-discovery/index.mjs",