@lcv-ideas-software/cross-review 4.0.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.
Files changed (122) hide show
  1. package/CHANGELOG.md +2568 -0
  2. package/LICENSE +201 -0
  3. package/NOTICE +26 -0
  4. package/README.md +208 -0
  5. package/SECURITY.md +52 -0
  6. package/dist/scripts/api-streaming-smoke.d.ts +1 -0
  7. package/dist/scripts/api-streaming-smoke.js +78 -0
  8. package/dist/scripts/api-streaming-smoke.js.map +1 -0
  9. package/dist/scripts/runtime-default-smoke.d.ts +1 -0
  10. package/dist/scripts/runtime-default-smoke.js +88 -0
  11. package/dist/scripts/runtime-default-smoke.js.map +1 -0
  12. package/dist/scripts/runtime-smoke.d.ts +1 -0
  13. package/dist/scripts/runtime-smoke.js +148 -0
  14. package/dist/scripts/runtime-smoke.js.map +1 -0
  15. package/dist/scripts/smoke.d.ts +1 -0
  16. package/dist/scripts/smoke.js +6156 -0
  17. package/dist/scripts/smoke.js.map +1 -0
  18. package/dist/src/core/cache-manifest.d.ts +22 -0
  19. package/dist/src/core/cache-manifest.js +133 -0
  20. package/dist/src/core/cache-manifest.js.map +1 -0
  21. package/dist/src/core/caller-tokens.d.ts +32 -0
  22. package/dist/src/core/caller-tokens.js +240 -0
  23. package/dist/src/core/caller-tokens.js.map +1 -0
  24. package/dist/src/core/config.d.ts +9 -0
  25. package/dist/src/core/config.js +643 -0
  26. package/dist/src/core/config.js.map +1 -0
  27. package/dist/src/core/convergence.d.ts +5 -0
  28. package/dist/src/core/convergence.js +186 -0
  29. package/dist/src/core/convergence.js.map +1 -0
  30. package/dist/src/core/cost.d.ts +59 -0
  31. package/dist/src/core/cost.js +359 -0
  32. package/dist/src/core/cost.js.map +1 -0
  33. package/dist/src/core/file-config.d.ts +316 -0
  34. package/dist/src/core/file-config.js +490 -0
  35. package/dist/src/core/file-config.js.map +1 -0
  36. package/dist/src/core/orchestrator.d.ts +199 -0
  37. package/dist/src/core/orchestrator.js +3430 -0
  38. package/dist/src/core/orchestrator.js.map +1 -0
  39. package/dist/src/core/prompt-parts.d.ts +58 -0
  40. package/dist/src/core/prompt-parts.js +122 -0
  41. package/dist/src/core/prompt-parts.js.map +1 -0
  42. package/dist/src/core/relator-lottery.d.ts +23 -0
  43. package/dist/src/core/relator-lottery.js +112 -0
  44. package/dist/src/core/relator-lottery.js.map +1 -0
  45. package/dist/src/core/reports.d.ts +2 -0
  46. package/dist/src/core/reports.js +82 -0
  47. package/dist/src/core/reports.js.map +1 -0
  48. package/dist/src/core/session-store.d.ts +149 -0
  49. package/dist/src/core/session-store.js +1923 -0
  50. package/dist/src/core/session-store.js.map +1 -0
  51. package/dist/src/core/status.d.ts +61 -0
  52. package/dist/src/core/status.js +249 -0
  53. package/dist/src/core/status.js.map +1 -0
  54. package/dist/src/core/timeouts.d.ts +2 -0
  55. package/dist/src/core/timeouts.js +3 -0
  56. package/dist/src/core/timeouts.js.map +1 -0
  57. package/dist/src/core/types.d.ts +604 -0
  58. package/dist/src/core/types.js +36 -0
  59. package/dist/src/core/types.js.map +1 -0
  60. package/dist/src/dashboard/server.d.ts +2 -0
  61. package/dist/src/dashboard/server.js +339 -0
  62. package/dist/src/dashboard/server.js.map +1 -0
  63. package/dist/src/mcp/server.d.ts +54 -0
  64. package/dist/src/mcp/server.js +1584 -0
  65. package/dist/src/mcp/server.js.map +1 -0
  66. package/dist/src/observability/logger.d.ts +9 -0
  67. package/dist/src/observability/logger.js +24 -0
  68. package/dist/src/observability/logger.js.map +1 -0
  69. package/dist/src/peers/anthropic.d.ts +14 -0
  70. package/dist/src/peers/anthropic.js +290 -0
  71. package/dist/src/peers/anthropic.js.map +1 -0
  72. package/dist/src/peers/base.d.ts +72 -0
  73. package/dist/src/peers/base.js +416 -0
  74. package/dist/src/peers/base.js.map +1 -0
  75. package/dist/src/peers/deepseek.d.ts +12 -0
  76. package/dist/src/peers/deepseek.js +246 -0
  77. package/dist/src/peers/deepseek.js.map +1 -0
  78. package/dist/src/peers/errors.d.ts +2 -0
  79. package/dist/src/peers/errors.js +185 -0
  80. package/dist/src/peers/errors.js.map +1 -0
  81. package/dist/src/peers/gemini.d.ts +13 -0
  82. package/dist/src/peers/gemini.js +215 -0
  83. package/dist/src/peers/gemini.js.map +1 -0
  84. package/dist/src/peers/grok.d.ts +17 -0
  85. package/dist/src/peers/grok.js +346 -0
  86. package/dist/src/peers/grok.js.map +1 -0
  87. package/dist/src/peers/model-selection.d.ts +4 -0
  88. package/dist/src/peers/model-selection.js +260 -0
  89. package/dist/src/peers/model-selection.js.map +1 -0
  90. package/dist/src/peers/openai.d.ts +14 -0
  91. package/dist/src/peers/openai.js +299 -0
  92. package/dist/src/peers/openai.js.map +1 -0
  93. package/dist/src/peers/perplexity.d.ts +18 -0
  94. package/dist/src/peers/perplexity.js +375 -0
  95. package/dist/src/peers/perplexity.js.map +1 -0
  96. package/dist/src/peers/registry.d.ts +3 -0
  97. package/dist/src/peers/registry.js +77 -0
  98. package/dist/src/peers/registry.js.map +1 -0
  99. package/dist/src/peers/retry.d.ts +2 -0
  100. package/dist/src/peers/retry.js +36 -0
  101. package/dist/src/peers/retry.js.map +1 -0
  102. package/dist/src/peers/stub.d.ts +13 -0
  103. package/dist/src/peers/stub.js +344 -0
  104. package/dist/src/peers/stub.js.map +1 -0
  105. package/dist/src/peers/text.d.ts +18 -0
  106. package/dist/src/peers/text.js +39 -0
  107. package/dist/src/peers/text.js.map +1 -0
  108. package/dist/src/security/redact.d.ts +2 -0
  109. package/dist/src/security/redact.js +128 -0
  110. package/dist/src/security/redact.js.map +1 -0
  111. package/docs/api-keys.md +34 -0
  112. package/docs/architecture.md +118 -0
  113. package/docs/caching.md +135 -0
  114. package/docs/costs.md +40 -0
  115. package/docs/evidence-preflight.md +88 -0
  116. package/docs/github-security-baseline.md +32 -0
  117. package/docs/model-selection.md +105 -0
  118. package/docs/reports/cross-review-v2-api-capability-smoke-2026-04-30.md +354 -0
  119. package/docs/reports/cross-review-v2-format-recovery-findings-2026-04-28.md +223 -0
  120. package/docs/reports/cross-review-v2-official-provider-docs-refresh-2026-05-05.md +60 -0
  121. package/docs/reports/cross-review-v2-token-streaming-smoke-2026-04-30.md +119 -0
  122. package/package.json +88 -0
@@ -0,0 +1,133 @@
1
+ // v2.21.0 (caching): per-session cache manifest persistence.
2
+ //
3
+ // Path: ${data_dir}/sessions/${session_id}/cache_manifest.json
4
+ //
5
+ // Atomic write pattern mirrors session-store.ts writeJson: tmp file via
6
+ // `flag: "wx"` + crypto-random nonce + retry-on-Windows-EPERM. The
7
+ // manifest is APPEND-ONLY at the entry level — every peer call adds one
8
+ // row. Readers (dashboard, reports, FinOps) snapshot the file; the
9
+ // runtime never deletes rows from it.
10
+ //
11
+ // Concurrency: appends within the same process are serialized via a
12
+ // short re-read + write cycle. Cross-process appends on the same
13
+ // session are NOT supported (same as the rest of session-store.ts —
14
+ // SECURITY.md documents single-process-per-data-dir).
15
+ import crypto from "node:crypto";
16
+ import fs from "node:fs";
17
+ import path from "node:path";
18
+ export const CACHE_SCHEMA_VERSION_DEFAULT = "v1";
19
+ const MANIFEST_FILENAME = "cache_manifest.json";
20
+ const ATOMIC_WRITE_RETRY_CODES = new Set(["EPERM", "EACCES", "EBUSY", "EEXIST"]);
21
+ const ATOMIC_WRITE_MAX_ATTEMPTS = 5;
22
+ const TMP_NONCE_BYTES = 2;
23
+ function manifestPath(dataDir, sessionId) {
24
+ return path.resolve(dataDir, "sessions", sessionId, MANIFEST_FILENAME);
25
+ }
26
+ function writeJsonAtomic(file, data) {
27
+ fs.mkdirSync(path.dirname(file), { recursive: true });
28
+ const nonce = crypto.randomBytes(TMP_NONCE_BYTES).toString("hex");
29
+ const tmp = `${file}.${process.pid}.${Date.now()}.${nonce}.tmp`;
30
+ fs.writeFileSync(tmp, `${JSON.stringify(data, null, 2)}\n`, "utf8");
31
+ let lastErr = null;
32
+ for (let attempt = 0; attempt < ATOMIC_WRITE_MAX_ATTEMPTS; attempt += 1) {
33
+ try {
34
+ fs.renameSync(tmp, file);
35
+ return;
36
+ }
37
+ catch (err) {
38
+ lastErr = err;
39
+ const code = err.code;
40
+ if (!code || !ATOMIC_WRITE_RETRY_CODES.has(code))
41
+ break;
42
+ const wait = 10 * 2 ** attempt;
43
+ const start = Date.now();
44
+ while (Date.now() - start < wait) {
45
+ /* spin */
46
+ }
47
+ }
48
+ }
49
+ try {
50
+ fs.unlinkSync(tmp);
51
+ }
52
+ catch {
53
+ /* ignore */
54
+ }
55
+ throw lastErr;
56
+ }
57
+ /**
58
+ * Read the manifest from disk. Returns null when the file is absent
59
+ * (most sessions never emit cache telemetry, so the manifest is
60
+ * lazily created on first append).
61
+ */
62
+ export function readCacheManifest(dataDir, sessionId) {
63
+ const file = manifestPath(dataDir, sessionId);
64
+ if (!fs.existsSync(file))
65
+ return null;
66
+ try {
67
+ const raw = fs.readFileSync(file, "utf8");
68
+ const parsed = JSON.parse(raw);
69
+ return parsed;
70
+ }
71
+ catch {
72
+ return null;
73
+ }
74
+ }
75
+ /**
76
+ * Write a complete manifest, replacing any existing file. Mostly used
77
+ * by tests; production callers should append entries via
78
+ * appendCacheManifestEntry.
79
+ */
80
+ export function writeCacheManifest(dataDir, sessionId, manifest) {
81
+ writeJsonAtomic(manifestPath(dataDir, sessionId), manifest);
82
+ }
83
+ /**
84
+ * Append a single entry to the session manifest. Lazily creates the
85
+ * manifest if it does not exist. Each call performs (a) read-current,
86
+ * (b) push entry, (c) atomic-write. This is sequential within a
87
+ * process; concurrent calls in the same process must be awaited in
88
+ * order by the caller.
89
+ */
90
+ export function appendCacheManifestEntry(dataDir, sessionId, entry, cacheSchemaVersion = CACHE_SCHEMA_VERSION_DEFAULT) {
91
+ const file = manifestPath(dataDir, sessionId);
92
+ const nowIso = new Date().toISOString();
93
+ let current;
94
+ if (fs.existsSync(file)) {
95
+ try {
96
+ const raw = fs.readFileSync(file, "utf8");
97
+ current = JSON.parse(raw);
98
+ }
99
+ catch {
100
+ // Corrupted manifest: rebuild from scratch with this entry as
101
+ // the sole row. Old contents are best-effort backed up next to
102
+ // the file with a `.corrupt-<ts>` suffix so an operator can
103
+ // forensically inspect.
104
+ const corrupt = `${file}.corrupt-${Date.now()}`;
105
+ try {
106
+ fs.renameSync(file, corrupt);
107
+ }
108
+ catch {
109
+ /* ignore */
110
+ }
111
+ current = {
112
+ session_id: sessionId,
113
+ cache_schema_version: cacheSchemaVersion,
114
+ created_at: nowIso,
115
+ updated_at: nowIso,
116
+ entries: [],
117
+ };
118
+ }
119
+ }
120
+ else {
121
+ current = {
122
+ session_id: sessionId,
123
+ cache_schema_version: cacheSchemaVersion,
124
+ created_at: nowIso,
125
+ updated_at: nowIso,
126
+ entries: [],
127
+ };
128
+ }
129
+ current.entries.push(entry);
130
+ current.updated_at = nowIso;
131
+ writeJsonAtomic(file, current);
132
+ }
133
+ //# sourceMappingURL=cache-manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache-manifest.js","sourceRoot":"","sources":["../../../src/core/cache-manifest.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ import type { PeerId } from "./types.js";
2
+ export declare const TOKEN_BYTES = 32;
3
+ export declare const TOKEN_HEX_LENGTH: number;
4
+ export type HostTokensMap = Record<PeerId, string>;
5
+ export interface HostTokensRecord {
6
+ filePath: string;
7
+ map: HostTokensMap;
8
+ generated_at: string | null;
9
+ }
10
+ export interface ParentProcessSnapshot {
11
+ parent_pid: number | null;
12
+ parent_exe_basename: string | null;
13
+ }
14
+ export type TokenVerification = {
15
+ method: "token";
16
+ verified: true;
17
+ } | {
18
+ method: "absent";
19
+ verified: false;
20
+ };
21
+ export declare function getTokensFilePath(dataDir: string): string;
22
+ export declare function generateHostTokens(dataDir: string, options?: {
23
+ overwrite?: boolean;
24
+ }): HostTokensRecord | null;
25
+ export declare function loadHostTokens(dataDir: string): HostTokensRecord | null;
26
+ export declare function ensureHostTokens(dataDir: string): HostTokensRecord | null;
27
+ export declare function tokensMatch(a: unknown, b: unknown): boolean;
28
+ export declare function resolveAgentForToken(presented: string | null, tokensMap: HostTokensMap | undefined): PeerId | null;
29
+ export declare function getEnvToken(): string | null;
30
+ export declare function isHardEnforceMode(): boolean;
31
+ export declare function verifyTokenForCaller(declaredCaller: PeerId, tokensRecord: HostTokensRecord | null): TokenVerification;
32
+ export declare function getParentProcessSnapshot(): ParentProcessSnapshot;
@@ -0,0 +1,240 @@
1
+ // Module: cross-review/src/core/caller-tokens.ts
2
+ // Description: F1 caller capability tokens (v2.18.0). Generates and validates
3
+ // per-host secret tokens that complement the v2.17.0 clientInfo identity gate.
4
+ //
5
+ // Threat model: pre-v2.18.0 the v2.17.0 cross-check between declared `caller`
6
+ // and `clientInfo.name` only catches *inconsistent* self-reports — both
7
+ // fields are declared by the caller. An attacker that lies consistently in
8
+ // both fields passes the gate. F1 introduces a per-host secret bound to the
9
+ // operator's MCP host config (env var CROSS_REVIEW_CALLER_TOKEN),
10
+ // authoritative on match and rejected on mismatch.
11
+ //
12
+ // Operator decisions 2026-05-05:
13
+ // 1. Option C (Hybrid): token enforcement + best-effort parent-process
14
+ // snapshot as forensics-only metadata.
15
+ // 2. Tokens file path: default `<data_dir>/host-tokens.json` AND
16
+ // overridable via CROSS_REVIEW_TOKENS_FILE env var (note: same env
17
+ // name as v1 for operator simplicity, but the v2 default location is
18
+ // `<data_dir>/host-tokens.json` because v2 has its own data_dir
19
+ // separate from v1's STATE_DIR).
20
+ // 3. regenerate_caller_tokens MCP tool ships in v2.18.0.
21
+ // 4. Ship permissive: CROSS_REVIEW_REQUIRE_TOKEN remains opt-in.
22
+ import { spawnSync } from "node:child_process";
23
+ import crypto from "node:crypto";
24
+ import fs from "node:fs";
25
+ import path from "node:path";
26
+ import { PEERS } from "./types.js";
27
+ export const TOKEN_BYTES = 32;
28
+ export const TOKEN_HEX_LENGTH = TOKEN_BYTES * 2;
29
+ export function getTokensFilePath(dataDir) {
30
+ const override = process.env.CROSS_REVIEW_TOKENS_FILE;
31
+ if (typeof override === "string" && override.trim().length > 0) {
32
+ return path.resolve(override.trim());
33
+ }
34
+ return path.join(dataDir, "host-tokens.json");
35
+ }
36
+ export function generateHostTokens(dataDir, options = {}) {
37
+ const filePath = getTokensFilePath(dataDir);
38
+ const map = {};
39
+ for (const agent of PEERS) {
40
+ map[agent] = crypto.randomBytes(TOKEN_BYTES).toString("hex");
41
+ }
42
+ const seen = new Set();
43
+ for (const tok of Object.values(map)) {
44
+ if (seen.has(tok)) {
45
+ throw new Error("caller-tokens: generated tokens collide; refusing to write file");
46
+ }
47
+ seen.add(tok);
48
+ }
49
+ const payload = {
50
+ version: 1,
51
+ generated_at: new Date().toISOString(),
52
+ tokens: map,
53
+ };
54
+ const dir = path.dirname(filePath);
55
+ try {
56
+ fs.mkdirSync(dir, { recursive: true });
57
+ }
58
+ catch {
59
+ /* best-effort */
60
+ }
61
+ try {
62
+ fs.writeFileSync(filePath, JSON.stringify(payload, null, 2), {
63
+ flag: options.overwrite ? "w" : "wx",
64
+ mode: 0o600,
65
+ });
66
+ }
67
+ catch (err) {
68
+ if (typeof err === "object" &&
69
+ err !== null &&
70
+ "code" in err &&
71
+ err.code === "EEXIST" &&
72
+ !options.overwrite) {
73
+ // Lost race to a concurrent boot; caller falls back to load.
74
+ return null;
75
+ }
76
+ throw err;
77
+ }
78
+ if (process.platform !== "win32") {
79
+ try {
80
+ fs.chmodSync(filePath, 0o600);
81
+ }
82
+ catch {
83
+ /* best-effort POSIX hardening */
84
+ }
85
+ }
86
+ return { filePath, map, generated_at: payload.generated_at };
87
+ }
88
+ export function loadHostTokens(dataDir) {
89
+ const filePath = getTokensFilePath(dataDir);
90
+ let raw;
91
+ try {
92
+ raw = fs.readFileSync(filePath, "utf8");
93
+ }
94
+ catch (err) {
95
+ if (typeof err === "object" &&
96
+ err !== null &&
97
+ "code" in err &&
98
+ err.code === "ENOENT") {
99
+ return null;
100
+ }
101
+ return null;
102
+ }
103
+ let parsed;
104
+ try {
105
+ parsed = JSON.parse(raw);
106
+ }
107
+ catch {
108
+ return null;
109
+ }
110
+ if (typeof parsed !== "object" ||
111
+ parsed === null ||
112
+ parsed.version !== 1 ||
113
+ typeof parsed.tokens !== "object" ||
114
+ parsed.tokens === null) {
115
+ return null;
116
+ }
117
+ const tokensIn = parsed.tokens;
118
+ const map = {};
119
+ const seen = new Set();
120
+ for (const agent of PEERS) {
121
+ const tok = tokensIn[agent];
122
+ if (typeof tok !== "string" || tok.length !== TOKEN_HEX_LENGTH || !/^[0-9a-f]+$/i.test(tok)) {
123
+ return null;
124
+ }
125
+ if (seen.has(tok)) {
126
+ return null;
127
+ }
128
+ seen.add(tok);
129
+ map[agent] = tok.toLowerCase();
130
+ }
131
+ const generated_at = parsed.generated_at;
132
+ return {
133
+ filePath,
134
+ map,
135
+ generated_at: typeof generated_at === "string" ? generated_at : null,
136
+ };
137
+ }
138
+ export function ensureHostTokens(dataDir) {
139
+ const existing = loadHostTokens(dataDir);
140
+ if (existing)
141
+ return existing;
142
+ const generated = generateHostTokens(dataDir);
143
+ if (generated)
144
+ return generated;
145
+ return loadHostTokens(dataDir);
146
+ }
147
+ export function tokensMatch(a, b) {
148
+ if (typeof a !== "string" || typeof b !== "string")
149
+ return false;
150
+ if (a.length !== b.length || a.length === 0)
151
+ return false;
152
+ const ba = Buffer.from(a, "hex");
153
+ const bb = Buffer.from(b, "hex");
154
+ if (ba.length !== bb.length || ba.length === 0)
155
+ return false;
156
+ try {
157
+ return crypto.timingSafeEqual(ba, bb);
158
+ }
159
+ catch {
160
+ return false;
161
+ }
162
+ }
163
+ export function resolveAgentForToken(presented, tokensMap) {
164
+ if (!presented || !tokensMap)
165
+ return null;
166
+ let matched = null;
167
+ for (const agent of PEERS) {
168
+ const stored = tokensMap[agent];
169
+ if (tokensMatch(presented, stored) && matched === null) {
170
+ matched = agent;
171
+ }
172
+ }
173
+ return matched;
174
+ }
175
+ export function getEnvToken() {
176
+ const raw = process.env.CROSS_REVIEW_CALLER_TOKEN;
177
+ if (typeof raw !== "string")
178
+ return null;
179
+ const trimmed = raw.trim();
180
+ return trimmed.length > 0 ? trimmed : null;
181
+ }
182
+ export function isHardEnforceMode() {
183
+ return process.env.CROSS_REVIEW_REQUIRE_TOKEN === "true";
184
+ }
185
+ export function verifyTokenForCaller(declaredCaller, tokensRecord) {
186
+ const presented = getEnvToken();
187
+ if (!presented)
188
+ return { method: "absent", verified: false };
189
+ if (!tokensRecord || !tokensRecord.map) {
190
+ throw new Error("identity_forgery_blocked: CROSS_REVIEW_CALLER_TOKEN is set but the host-tokens.json file could not be loaded; either remove the env var, regenerate the tokens file via the regenerate_caller_tokens tool, or repair the file (default path: <data_dir>/host-tokens.json; override via CROSS_REVIEW_TOKENS_FILE).");
191
+ }
192
+ const agent = resolveAgentForToken(presented, tokensRecord.map);
193
+ if (!agent) {
194
+ throw new Error("identity_forgery_blocked: CROSS_REVIEW_CALLER_TOKEN does not match any known agent's secret in host-tokens.json. Either the token is stale (regenerate via regenerate_caller_tokens) or the host-tokens.json file has been rotated without re-distributing the new value.");
195
+ }
196
+ if (agent !== declaredCaller) {
197
+ throw new Error(`identity_forgery_blocked: CROSS_REVIEW_CALLER_TOKEN resolves to agent='${agent}' but caller declared='${declaredCaller}'. The token is bound to a specific agent's MCP host config; declaring a different caller from a host carrying another agent's token is identity forgery.`);
198
+ }
199
+ return { method: "token", verified: true };
200
+ }
201
+ export function getParentProcessSnapshot() {
202
+ const snapshot = {
203
+ parent_pid: typeof process.ppid === "number" ? process.ppid : null,
204
+ parent_exe_basename: null,
205
+ };
206
+ if (!snapshot.parent_pid)
207
+ return snapshot;
208
+ if (process.platform === "win32") {
209
+ // Windows path (added v2.18.2 / Tier 5): shell out to `tasklist` and
210
+ // parse the leading quoted CSV field. Best-effort, time-bounded 500ms,
211
+ // never throws. "PID not found" output starts with INFO/INFORMAÇÕES
212
+ // (no leading quote) so we use that as a discriminator.
213
+ try {
214
+ const r = spawnSync("tasklist", ["/FI", `PID eq ${snapshot.parent_pid}`, "/FO", "CSV", "/NH"], { encoding: "utf8", timeout: 500, windowsHide: true });
215
+ const stdout = String(r.stdout || "").trim();
216
+ if (stdout.startsWith('"')) {
217
+ const m = stdout.match(/^"([^"]+)"/);
218
+ if (m && m[1].length > 0 && m[1].length < 128) {
219
+ snapshot.parent_exe_basename = m[1];
220
+ }
221
+ }
222
+ }
223
+ catch {
224
+ /* best-effort */
225
+ }
226
+ }
227
+ else {
228
+ try {
229
+ const comm = fs.readFileSync(`/proc/${snapshot.parent_pid}/comm`, "utf8").trim();
230
+ if (comm.length > 0 && comm.length < 128) {
231
+ snapshot.parent_exe_basename = comm;
232
+ }
233
+ }
234
+ catch {
235
+ /* best-effort */
236
+ }
237
+ }
238
+ return snapshot;
239
+ }
240
+ //# sourceMappingURL=caller-tokens.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"caller-tokens.js","sourceRoot":"","sources":["../../../src/core/caller-tokens.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { AppConfig, PeerId } from "./types.js";
2
+ export declare const VERSION = "4.0.0";
3
+ export declare const RELEASE_DATE = "2026-05-15";
4
+ export declare const DEFAULT_MAX_OUTPUT_TOKENS = 20000;
5
+ export declare function getLastFileConfigResult(): import("./file-config.js").ApplyFileConfigResult | undefined;
6
+ export declare function loadConfig(): AppConfig;
7
+ export declare function missingFinancialControlVars(config: AppConfig, peers: PeerId[], options?: {
8
+ untilStopped?: boolean;
9
+ }): string[];