intentdna 1.9.0 → 1.9.3

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 (131) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.md +227 -87
  4. package/dist/cli/commands/run-lifecycle.d.ts +61 -11
  5. package/dist/cli/commands/run-lifecycle.js +184 -50
  6. package/dist/cli/commands/run-observer.d.ts +15 -0
  7. package/dist/cli/commands/run-observer.js +210 -0
  8. package/dist/cli/commands/run.d.ts +16 -20
  9. package/dist/cli/commands/run.js +461 -659
  10. package/dist/cli/commands/sync.js +16 -12
  11. package/dist/cli/index.js +14 -5
  12. package/dist/compiler/compile.d.ts +1 -0
  13. package/dist/compiler/compile.js +1 -1
  14. package/dist/compiler/controller.d.ts +16 -0
  15. package/dist/compiler/controller.js +693 -0
  16. package/dist/compiler/input-resolver.d.ts +2 -0
  17. package/dist/compiler/input-resolver.js +22 -6
  18. package/dist/hooks/cli.d.ts +45 -0
  19. package/dist/hooks/cli.js +263 -31
  20. package/dist/hooks/host-event.d.ts +57 -0
  21. package/dist/hooks/host-event.js +187 -0
  22. package/dist/hooks/index.d.ts +1 -0
  23. package/dist/hooks/index.js +1 -0
  24. package/dist/hooks/protocol.d.ts +7 -0
  25. package/dist/hooks/schema.d.ts +1 -0
  26. package/dist/hooks/schema.js +89 -1
  27. package/dist/hooks/state.d.ts +23 -1
  28. package/dist/hooks/state.js +9 -0
  29. package/dist/index.d.ts +1 -0
  30. package/dist/index.js +1 -0
  31. package/dist/mcp/index.js +2 -0
  32. package/dist/mcp/tools-run.d.ts +4 -0
  33. package/dist/mcp/tools-run.js +419 -0
  34. package/dist/mcp/tools-state.d.ts +1 -1
  35. package/dist/mcp/tools-state.js +13 -241
  36. package/dist/runtime/artifact-store.d.ts +146 -0
  37. package/dist/runtime/artifact-store.js +1436 -0
  38. package/dist/runtime/canonical-attempt-outcome.d.ts +12 -0
  39. package/dist/runtime/canonical-attempt-outcome.js +195 -0
  40. package/dist/runtime/canonical-json.d.ts +10 -0
  41. package/dist/runtime/canonical-json.js +105 -0
  42. package/dist/runtime/canonical-run-application.d.ts +93 -0
  43. package/dist/runtime/canonical-run-application.js +229 -0
  44. package/dist/runtime/canonical-run-service.d.ts +454 -0
  45. package/dist/runtime/canonical-run-service.js +4760 -0
  46. package/dist/runtime/canonical-runtime-composition.d.ts +22 -0
  47. package/dist/runtime/canonical-runtime-composition.js +34 -0
  48. package/dist/runtime/canonical-runtime-projection.d.ts +3 -0
  49. package/dist/runtime/canonical-runtime-projection.js +4 -0
  50. package/dist/runtime/canonical-target-compiler.d.ts +46 -0
  51. package/dist/runtime/canonical-target-compiler.js +514 -0
  52. package/dist/runtime/claude-agent-identity.d.ts +2 -0
  53. package/dist/runtime/claude-agent-identity.js +21 -0
  54. package/dist/runtime/executable-run-plan.d.ts +328 -0
  55. package/dist/runtime/executable-run-plan.js +1485 -0
  56. package/dist/runtime/execution-authority.d.ts +83 -0
  57. package/dist/runtime/execution-authority.js +98 -0
  58. package/dist/runtime/handoff-resolver.js +4 -1
  59. package/dist/runtime/harness-pull-adapter.d.ts +146 -0
  60. package/dist/runtime/harness-pull-adapter.js +315 -0
  61. package/dist/runtime/index.d.ts +39 -14
  62. package/dist/runtime/index.js +29 -7
  63. package/dist/runtime/local-execution-authority.d.ts +194 -0
  64. package/dist/runtime/local-execution-authority.js +2897 -0
  65. package/dist/runtime/local-execution-reconciliation.d.ts +20 -0
  66. package/dist/runtime/local-execution-reconciliation.js +107 -0
  67. package/dist/runtime/local-execution-supervisor-script.d.ts +7 -0
  68. package/dist/runtime/local-execution-supervisor-script.js +793 -0
  69. package/dist/runtime/local-provider-sandbox.d.ts +42 -0
  70. package/dist/runtime/local-provider-sandbox.js +154 -0
  71. package/dist/runtime/local-verifier-execution.d.ts +24 -0
  72. package/dist/runtime/local-verifier-execution.js +290 -0
  73. package/dist/runtime/local-windows-execution-supervisor-script.d.ts +6 -0
  74. package/dist/runtime/local-windows-execution-supervisor-script.js +788 -0
  75. package/dist/runtime/plan-store.d.ts +30 -0
  76. package/dist/runtime/plan-store.js +231 -0
  77. package/dist/runtime/process-tree.d.ts +9 -0
  78. package/dist/runtime/process-tree.js +97 -24
  79. package/dist/runtime/providers/codex.js +2 -2
  80. package/dist/runtime/push-driver.d.ts +184 -0
  81. package/dist/runtime/push-driver.js +1002 -0
  82. package/dist/runtime/result-store.d.ts +34 -3
  83. package/dist/runtime/result-store.js +1073 -2
  84. package/dist/runtime/run-binding.d.ts +68 -0
  85. package/dist/runtime/run-binding.js +278 -0
  86. package/dist/runtime/run-contracts.d.ts +575 -0
  87. package/dist/runtime/run-contracts.js +58 -7
  88. package/dist/runtime/run-controller.d.ts +1 -6
  89. package/dist/runtime/run-controller.js +0 -41
  90. package/dist/runtime/run-store.d.ts +85 -7
  91. package/dist/runtime/run-store.js +2528 -186
  92. package/dist/runtime/skill-adapter.d.ts +18 -7
  93. package/dist/runtime/skill-adapter.js +100 -742
  94. package/dist/runtime/structured-output-validator.d.ts +44 -0
  95. package/dist/runtime/structured-output-validator.js +171 -0
  96. package/dist/runtime/verifier-command-binding.d.ts +22 -0
  97. package/dist/runtime/verifier-command-binding.js +162 -0
  98. package/dist/runtime/verifier.d.ts +46 -6
  99. package/dist/runtime/verifier.js +355 -53
  100. package/dist/runtime/windows-job-keeper.d.ts +47 -0
  101. package/dist/runtime/windows-job-keeper.js +231 -0
  102. package/dist/runtime/worker-executor.d.ts +17 -1
  103. package/dist/runtime/worker-executor.js +26 -5
  104. package/dist/runtime/workflow-plan-adapter.d.ts +19 -0
  105. package/dist/runtime/workflow-plan-adapter.js +502 -6
  106. package/dist/runtime/workflow-runtime-manifest.d.ts +1 -0
  107. package/dist/runtime/workflow-runtime-manifest.js +5 -0
  108. package/dist/runtime/workspace-isolation.d.ts +29 -2
  109. package/dist/runtime/workspace-isolation.js +488 -11
  110. package/dist/runtime/workspace-observation.d.ts +44 -0
  111. package/dist/runtime/workspace-observation.js +216 -0
  112. package/dist/schema/controller-registry.d.ts +0 -7
  113. package/dist/schema/controller-registry.js +0 -8
  114. package/dist/schema/types.d.ts +84 -10
  115. package/dist/schema/types.js +2 -0
  116. package/dist/schema/validate.js +151 -1
  117. package/dist/schema/validators/controllers.d.ts +1 -1
  118. package/dist/schema/validators/controllers.js +126 -41
  119. package/dist/schema/workflow-authoring-contract.js +2 -0
  120. package/dist/schema/yaml-parser.js +1 -1
  121. package/dist/templates/flutter-rewrite-new.dna.yaml +1 -0
  122. package/dist/templates/flutter-rewrite.dna.yaml +87 -60
  123. package/dist/templates/safe-refactoring.dna.yaml +1 -0
  124. package/native/windows-job-keeper/README.md +80 -0
  125. package/native/windows-job-keeper/bin/aarch64/intentdna-windows-job-keeper.exe +0 -0
  126. package/native/windows-job-keeper/bin/x86_64/intentdna-windows-job-keeper.exe +0 -0
  127. package/native/windows-job-keeper/keeper.c +979 -0
  128. package/native/windows-job-keeper/manifest.json +33 -0
  129. package/package.json +6 -2
  130. package/scripts/build-windows-job-keeper.mjs +172 -0
  131. package/scripts/verify-windows-job-keeper.mjs +184 -0
@@ -1,4 +1,5 @@
1
- import { spawn } from "node:child_process";
1
+ import { spawn, spawnSync } from "node:child_process";
2
+ import { createHash } from "node:crypto";
2
3
  import { readFile, realpath, stat } from "node:fs/promises";
3
4
  import { dirname, isAbsolute, relative, resolve } from "node:path";
4
5
  import { EvaluatorResultError, parseEvaluatorResult } from "./evaluator.js";
@@ -7,6 +8,191 @@ export const MAX_VERIFIER_EVIDENCE_BYTES = 4096;
7
8
  export const VERIFIER_DIAGNOSTIC_PREFIX = "INTENTDNA_DIAGNOSTIC:";
8
9
  export const MAX_VERIFIER_DIAGNOSTIC_BYTES = 2048;
9
10
  const VERIFIER_DIAGNOSTIC_CODE = /^INTENTDNA_DIAGNOSTIC:[A-Za-z0-9][A-Za-z0-9_.:-]{0,255}$/;
11
+ function assertVerifierRecord(value, label) {
12
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
13
+ throw new TypeError(`${label} must be an object`);
14
+ }
15
+ }
16
+ function assertVerifierExactKeys(value, required, optional = [], label) {
17
+ const allowed = new Set([...required, ...optional]);
18
+ const keys = Object.keys(value);
19
+ if (required.some((key) => !Object.hasOwn(value, key)) || keys.some((key) => !allowed.has(key))) {
20
+ throw new TypeError(`${label} has invalid fields`);
21
+ }
22
+ }
23
+ function assertVerifierTimestamp(value, label) {
24
+ if (typeof value !== "string"
25
+ || !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/u.test(value)) {
26
+ throw new TypeError(`${label} must be a canonical UTC timestamp`);
27
+ }
28
+ const parsed = Date.parse(value);
29
+ if (!Number.isFinite(parsed) || new Date(parsed).toISOString() !== value) {
30
+ throw new TypeError(`${label} must be a valid timestamp`);
31
+ }
32
+ return parsed;
33
+ }
34
+ function assertVerifierDigest(value, label) {
35
+ if (typeof value !== "string" || !/^sha256:[a-f0-9]{64}$/u.test(value)) {
36
+ throw new TypeError(`${label} must be a lowercase sha256 digest`);
37
+ }
38
+ }
39
+ function assertVerifierSignal(value, label) {
40
+ if (typeof value !== "string" || !/^SIG[A-Z0-9]+$/u.test(value)) {
41
+ throw new TypeError(`${label} must be a signal name`);
42
+ }
43
+ }
44
+ function assertVerifierCommandProcessOutcome(value) {
45
+ assertVerifierRecord(value, "verifier execution outcome");
46
+ assertVerifierExactKeys(value, ["kind", "exitCode", "signal", "timedOut", "launchError"], [], "verifier execution outcome");
47
+ if (value.kind === "exited") {
48
+ if (!Number.isSafeInteger(value.exitCode)
49
+ || value.exitCode < 0
50
+ || value.signal !== null
51
+ || value.timedOut !== false
52
+ || value.launchError !== null) {
53
+ throw new TypeError("verifier exited outcome has an invalid tuple");
54
+ }
55
+ return;
56
+ }
57
+ if (value.kind === "signaled") {
58
+ if (value.exitCode !== null || value.timedOut !== false || value.launchError !== null) {
59
+ throw new TypeError("verifier signaled outcome has an invalid tuple");
60
+ }
61
+ assertVerifierSignal(value.signal, "verifier signaled outcome signal");
62
+ return;
63
+ }
64
+ if (value.kind === "timed_out") {
65
+ if ((value.exitCode !== null
66
+ && (!Number.isSafeInteger(value.exitCode) || value.exitCode <= 0))
67
+ || value.timedOut !== true
68
+ || value.launchError !== null) {
69
+ throw new TypeError("verifier timed_out outcome has an invalid tuple");
70
+ }
71
+ if (value.signal !== null)
72
+ assertVerifierSignal(value.signal, "verifier timed_out outcome signal");
73
+ return;
74
+ }
75
+ if (value.kind === "launch_error") {
76
+ if (value.exitCode !== null || value.signal !== null || value.timedOut !== false) {
77
+ throw new TypeError("verifier launch_error outcome has an invalid tuple");
78
+ }
79
+ assertVerifierRecord(value.launchError, "verifier launch error");
80
+ assertVerifierExactKeys(value.launchError, ["code", "messageDigest"], [], "verifier launch error");
81
+ if (typeof value.launchError.code !== "string"
82
+ || !/^[A-Z][A-Z0-9_]*$/u.test(value.launchError.code)) {
83
+ throw new TypeError("verifier launch error code is invalid");
84
+ }
85
+ assertVerifierDigest(value.launchError.messageDigest, "verifier launch error message digest");
86
+ return;
87
+ }
88
+ throw new TypeError("verifier execution outcome kind is invalid");
89
+ }
90
+ export function assertVerifierCommandExecutionResult(value) {
91
+ assertVerifierRecord(value, "verifier command execution result");
92
+ assertVerifierExactKeys(value, [
93
+ "outcome",
94
+ "processTreeDrained",
95
+ "startedAt",
96
+ "completedAt",
97
+ "stdoutDigest",
98
+ "stdoutBytes",
99
+ "stderrDigest",
100
+ "stderrBytes",
101
+ ], ["evidence"], "verifier command execution result");
102
+ assertVerifierCommandProcessOutcome(value.outcome);
103
+ if (typeof value.processTreeDrained !== "boolean") {
104
+ throw new TypeError("verifier command execution processTreeDrained must be a boolean");
105
+ }
106
+ if (value.evidence !== undefined
107
+ && (typeof value.evidence !== "string"
108
+ || value.evidence.length === 0
109
+ || value.evidence.length > MAX_VERIFIER_EVIDENCE_BYTES)) {
110
+ throw new TypeError("verifier command execution evidence is invalid");
111
+ }
112
+ const startedAt = assertVerifierTimestamp(value.startedAt, "verifier command execution startedAt");
113
+ const completedAt = assertVerifierTimestamp(value.completedAt, "verifier command execution completedAt");
114
+ if (completedAt < startedAt) {
115
+ throw new TypeError("verifier command execution completedAt precedes startedAt");
116
+ }
117
+ assertVerifierDigest(value.stdoutDigest, "verifier command execution stdoutDigest");
118
+ assertVerifierDigest(value.stderrDigest, "verifier command execution stderrDigest");
119
+ if (!Number.isSafeInteger(value.stdoutBytes) || value.stdoutBytes < 0
120
+ || !Number.isSafeInteger(value.stderrBytes) || value.stderrBytes < 0) {
121
+ throw new TypeError("verifier command execution byte counts must be non-negative safe integers");
122
+ }
123
+ }
124
+ export function verifierCommandExecutionPassed(execution) {
125
+ return execution.processTreeDrained
126
+ && execution.outcome.kind === "exited"
127
+ && execution.outcome.exitCode === 0;
128
+ }
129
+ export function verifierCommandExecutionTimedOut(execution) {
130
+ return execution.outcome.timedOut;
131
+ }
132
+ export function verifierCommandExecutionExitCode(execution) {
133
+ return execution.outcome.exitCode;
134
+ }
135
+ export function verifierCommandExecutionSignal(execution) {
136
+ return execution.outcome.signal;
137
+ }
138
+ const VERIFIER_PROCESS_DRAIN_GRACE_MS = 1_000;
139
+ function terminateVerifierProcessGroup(child, signal) {
140
+ if (child.pid === undefined)
141
+ return;
142
+ if (process.platform === "win32") {
143
+ const force = signal === "SIGKILL" ? ["/F"] : [];
144
+ const result = spawnSync("taskkill", ["/PID", String(child.pid), "/T", ...force], { windowsHide: true, stdio: "ignore" });
145
+ if (!result.error && result.status === 0)
146
+ return;
147
+ }
148
+ try {
149
+ if (process.platform === "win32")
150
+ child.kill(signal);
151
+ else
152
+ process.kill(-child.pid, signal);
153
+ }
154
+ catch (error) {
155
+ if (error.code !== "ESRCH")
156
+ throw error;
157
+ }
158
+ }
159
+ function verifierProcessGroupExists(child) {
160
+ if (child.pid === undefined)
161
+ return false;
162
+ if (process.platform === "win32")
163
+ return child.exitCode === null && child.signalCode === null;
164
+ try {
165
+ process.kill(-child.pid, 0);
166
+ return true;
167
+ }
168
+ catch (error) {
169
+ const code = error.code;
170
+ if (code === "ESRCH")
171
+ return false;
172
+ if (code === "EPERM")
173
+ return true;
174
+ throw error;
175
+ }
176
+ }
177
+ async function drainVerifierProcessGroup(child) {
178
+ if (!verifierProcessGroupExists(child))
179
+ return;
180
+ terminateVerifierProcessGroup(child, "SIGTERM");
181
+ const deadline = Date.now() + VERIFIER_PROCESS_DRAIN_GRACE_MS;
182
+ while (verifierProcessGroupExists(child) && Date.now() < deadline) {
183
+ await new Promise((resolvePromise) => setTimeout(resolvePromise, 10));
184
+ }
185
+ if (!verifierProcessGroupExists(child))
186
+ return;
187
+ terminateVerifierProcessGroup(child, "SIGKILL");
188
+ const forcedDeadline = Date.now() + VERIFIER_PROCESS_DRAIN_GRACE_MS;
189
+ while (verifierProcessGroupExists(child) && Date.now() < forcedDeadline) {
190
+ await new Promise((resolvePromise) => setTimeout(resolvePromise, 10));
191
+ }
192
+ if (verifierProcessGroupExists(child)) {
193
+ throw new Error("verifier process group did not drain after SIGKILL");
194
+ }
195
+ }
10
196
  const SAFE_COMMAND_VARIABLE_RE = /^[A-Za-z0-9_-]+$/;
11
197
  const SAFE_PATH_VARIABLE_RE = /^[-A-Za-z0-9_./]+$/;
12
198
  function isSafeRelativePathVariable(value) {
@@ -214,60 +400,167 @@ function appendFailureDiagnostic(message, evidence) {
214
400
  const diagnostics = extractVerifierDiagnostics(evidence ?? "").join("\n");
215
401
  return diagnostics ? `${message}\n${diagnostics}` : message;
216
402
  }
217
- export async function execVerifierCommand(projectDir, command, timeoutMs = DEFAULT_VERIFIER_COMMAND_TIMEOUT_MS, env) {
403
+ function verifierLaunchError(error, fallbackCode) {
404
+ const rawCode = error && typeof error === "object" && "code" in error
405
+ ? String(error.code ?? "")
406
+ : "";
407
+ const code = /^[A-Z][A-Z0-9_]*$/u.test(rawCode) ? rawCode : fallbackCode;
408
+ const message = error instanceof Error
409
+ ? `${error.name}:${error.message}`
410
+ : String(error);
411
+ return {
412
+ code,
413
+ messageDigest: `sha256:${createHash("sha256").update(message).digest("hex")}`,
414
+ };
415
+ }
416
+ function verifierProcessOutcome(code, signal, timedOut) {
417
+ if (timedOut) {
418
+ return {
419
+ kind: "timed_out",
420
+ exitCode: code === 0 ? null : code,
421
+ signal,
422
+ timedOut: true,
423
+ launchError: null,
424
+ };
425
+ }
426
+ if (code !== null && signal === null) {
427
+ return {
428
+ kind: "exited",
429
+ exitCode: code,
430
+ signal: null,
431
+ timedOut: false,
432
+ launchError: null,
433
+ };
434
+ }
435
+ if (code === null && signal !== null) {
436
+ return {
437
+ kind: "signaled",
438
+ exitCode: null,
439
+ signal,
440
+ timedOut: false,
441
+ launchError: null,
442
+ };
443
+ }
444
+ return {
445
+ kind: "launch_error",
446
+ exitCode: null,
447
+ signal: null,
448
+ timedOut: false,
449
+ launchError: verifierLaunchError(new Error("process closed without exactly one of exit code or signal"), "INVALID_PROCESS_OUTCOME"),
450
+ };
451
+ }
452
+ export async function execVerifierCommand(projectDir, command, timeoutMs = DEFAULT_VERIFIER_COMMAND_TIMEOUT_MS, env = {}, now = () => new Date()) {
218
453
  return new Promise((resolvePromise) => {
219
- const child = spawn("bash", ["-lc", command], {
220
- cwd: projectDir,
221
- stdio: ["ignore", "pipe", "pipe"],
222
- env: { ...process.env, ...(env ?? {}) },
223
- });
454
+ const startedAt = now().toISOString();
455
+ let child;
456
+ try {
457
+ child = spawn("bash", ["--noprofile", "--norc", "-c", command], {
458
+ cwd: projectDir,
459
+ stdio: ["ignore", "pipe", "pipe"],
460
+ env: { ...env },
461
+ detached: process.platform !== "win32",
462
+ windowsHide: true,
463
+ });
464
+ }
465
+ catch (error) {
466
+ const emptyDigest = `sha256:${createHash("sha256").digest("hex")}`;
467
+ resolvePromise({
468
+ outcome: {
469
+ kind: "launch_error",
470
+ exitCode: null,
471
+ signal: null,
472
+ timedOut: false,
473
+ launchError: verifierLaunchError(error, "SPAWN_ERROR"),
474
+ },
475
+ processTreeDrained: true,
476
+ startedAt,
477
+ completedAt: now().toISOString(),
478
+ stdoutDigest: emptyDigest,
479
+ stdoutBytes: 0,
480
+ stderrDigest: emptyDigest,
481
+ stderrBytes: 0,
482
+ });
483
+ return;
484
+ }
224
485
  let stdoutPendingLine = "";
225
486
  let stderrPendingLine = "";
226
487
  let stdoutBytes = 0;
227
488
  let stderrBytes = 0;
489
+ const stdoutDigest = createHash("sha256");
490
+ const stderrDigest = createHash("sha256");
228
491
  let settled = false;
492
+ let timedOut = false;
493
+ let drainPromise = null;
494
+ let timeoutId = null;
229
495
  const diagnostics = [];
230
- const settle = (result) => {
496
+ const startDrain = () => {
497
+ drainPromise ??= drainVerifierProcessGroup(child).then(() => true, () => false);
498
+ return drainPromise;
499
+ };
500
+ const settle = (outcome, processTreeDrained) => {
231
501
  if (settled)
232
502
  return;
233
503
  settled = true;
234
- clearTimeout(timeoutId);
235
- resolvePromise(result);
504
+ if (timeoutId !== null)
505
+ clearTimeout(timeoutId);
506
+ resolvePromise({
507
+ outcome,
508
+ processTreeDrained,
509
+ evidence: summarizeCommandEvidenceFromStats(stdoutBytes, stderrBytes, [
510
+ ...diagnostics,
511
+ ...extractVerifierDiagnostics(`${stdoutPendingLine}\n${stderrPendingLine}`),
512
+ ]),
513
+ startedAt,
514
+ completedAt: now().toISOString(),
515
+ stdoutDigest: `sha256:${stdoutDigest.digest("hex")}`,
516
+ stdoutBytes,
517
+ stderrDigest: `sha256:${stderrDigest.digest("hex")}`,
518
+ stderrBytes,
519
+ });
236
520
  };
237
- const timeoutId = setTimeout(() => {
238
- child.kill("SIGTERM");
239
- setTimeout(() => child.kill("SIGKILL"), 1000).unref();
240
- diagnostics.push(...extractVerifierDiagnostics(`${stdoutPendingLine}\n${stderrPendingLine}`));
241
- settle({ passed: false, timedOut: true, exitCode: 124, evidence: summarizeCommandEvidenceFromStats(stdoutBytes, stderrBytes, diagnostics) });
521
+ timeoutId = setTimeout(() => {
522
+ timedOut = true;
523
+ void startDrain().then((drained) => {
524
+ if (!drained)
525
+ settle(verifierProcessOutcome(null, null, true), false);
526
+ });
242
527
  }, timeoutMs);
243
- child.stdout.on("data", (chunk) => {
244
- const text = String(chunk);
245
- stdoutBytes += Buffer.byteLength(text, "utf8");
528
+ child.stdout?.on("data", (chunk) => {
529
+ const bytes = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
530
+ const text = bytes.toString("utf8");
531
+ stdoutBytes += bytes.length;
532
+ stdoutDigest.update(bytes);
246
533
  stdoutPendingLine = collectVerifierDiagnostics(stdoutPendingLine, text, diagnostics);
247
534
  });
248
- child.stderr.on("data", (chunk) => {
249
- const text = String(chunk);
250
- stderrBytes += Buffer.byteLength(text, "utf8");
535
+ child.stderr?.on("data", (chunk) => {
536
+ const bytes = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
537
+ const text = bytes.toString("utf8");
538
+ stderrBytes += bytes.length;
539
+ stderrDigest.update(bytes);
251
540
  stderrPendingLine = collectVerifierDiagnostics(stderrPendingLine, text, diagnostics);
252
541
  });
253
- child.on("error", () => settle({
254
- passed: false,
255
- timedOut: false,
256
- exitCode: 1,
257
- evidence: summarizeCommandEvidenceFromStats(stdoutBytes, stderrBytes, [
258
- ...diagnostics,
259
- ...extractVerifierDiagnostics(`${stdoutPendingLine}\n${stderrPendingLine}`),
260
- ]),
261
- }));
262
- child.on("close", (code) => settle({
263
- passed: code === 0,
264
- timedOut: false,
265
- exitCode: code ?? 1,
266
- evidence: summarizeCommandEvidenceFromStats(stdoutBytes, stderrBytes, [
267
- ...diagnostics,
268
- ...extractVerifierDiagnostics(`${stdoutPendingLine}\n${stderrPendingLine}`),
269
- ]),
270
- }));
542
+ child.on("error", (error) => {
543
+ void startDrain().then((drained) => {
544
+ if (timedOut) {
545
+ settle(verifierProcessOutcome(null, null, true), drained);
546
+ return;
547
+ }
548
+ settle({
549
+ kind: "launch_error",
550
+ exitCode: null,
551
+ signal: null,
552
+ timedOut: false,
553
+ launchError: verifierLaunchError(error, "SPAWN_ERROR"),
554
+ }, drained);
555
+ });
556
+ });
557
+ child.on("exit", () => {
558
+ void startDrain();
559
+ });
560
+ child.on("close", async (code, signal) => {
561
+ const drained = await startDrain();
562
+ settle(verifierProcessOutcome(code, signal, timedOut), drained);
563
+ });
271
564
  });
272
565
  }
273
566
  async function execVerifierStdout(projectDir, command, args) {
@@ -522,15 +815,18 @@ export async function runCompletionVerifier(completion, ir, context) {
522
815
  return { passed: false, target: commandResolution.target, evidence: commandResolution.evidence, exit_code: commandResolution.exit_code, message: commandResolution.message };
523
816
  const command = commandResolution.command;
524
817
  const commandResult = await execVerifierCommand(context.projectDir, command, context.commandTimeoutMs, verifierCommandEnv(context.variables));
818
+ const passed = verifierCommandExecutionPassed(commandResult);
819
+ const timedOut = verifierCommandExecutionTimedOut(commandResult);
820
+ const exitCode = verifierCommandExecutionExitCode(commandResult);
525
821
  return {
526
- passed: commandResult.passed,
822
+ passed,
527
823
  target: command,
528
824
  evidence: commandResult.evidence,
529
- exit_code: commandResult.exitCode,
530
- timedOut: commandResult.timedOut,
531
- message: commandResult.passed
825
+ exit_code: exitCode ?? (timedOut ? 124 : 1),
826
+ timedOut,
827
+ message: passed
532
828
  ? undefined
533
- : commandResult.timedOut
829
+ : timedOut
534
830
  ? `Verifier command timed out after ${context.commandTimeoutMs ?? DEFAULT_VERIFIER_COMMAND_TIMEOUT_MS}ms`
535
831
  : appendFailureDiagnostic("Verifier command failed", commandResult.evidence),
536
832
  };
@@ -548,15 +844,18 @@ export async function runCheckpointVerifier(checkpoint, ir, context) {
548
844
  return { passed: false, target: commandResolution.target, evidence: commandResolution.evidence, exit_code: commandResolution.exit_code, message: commandResolution.message };
549
845
  const command = commandResolution.command;
550
846
  const commandResult = await execVerifierCommand(context.projectDir, command, context.commandTimeoutMs, verifierCommandEnv(context.variables));
847
+ const passed = verifierCommandExecutionPassed(commandResult);
848
+ const timedOut = verifierCommandExecutionTimedOut(commandResult);
849
+ const exitCode = verifierCommandExecutionExitCode(commandResult);
551
850
  return {
552
- passed: commandResult.passed,
851
+ passed,
553
852
  target: command,
554
853
  evidence: commandResult.evidence,
555
- exit_code: commandResult.exitCode,
556
- timedOut: commandResult.timedOut,
557
- message: commandResult.passed
854
+ exit_code: exitCode ?? (timedOut ? 124 : 1),
855
+ timedOut,
856
+ message: passed
558
857
  ? checkpoint.message
559
- : commandResult.timedOut
858
+ : timedOut
560
859
  ? `Verifier command timed out after ${context.commandTimeoutMs ?? DEFAULT_VERIFIER_COMMAND_TIMEOUT_MS}ms`
561
860
  : appendFailureDiagnostic("Verifier command failed", commandResult.evidence),
562
861
  };
@@ -598,14 +897,17 @@ export async function runCheckpointVerifier(checkpoint, ir, context) {
598
897
  return { passed: false, target: checkpoint.assert, evidence: "policy_denied", exit_code: 126, message: verifierAssertPolicyMessage(checkpoint.assert) };
599
898
  }
600
899
  const commandResult = await execVerifierCommand(context.projectDir, assertCommands[checkpoint.assert], context.commandTimeoutMs);
900
+ const passed = verifierCommandExecutionPassed(commandResult);
901
+ const timedOut = verifierCommandExecutionTimedOut(commandResult);
902
+ const exitCode = verifierCommandExecutionExitCode(commandResult);
601
903
  return {
602
- passed: commandResult.passed,
904
+ passed,
603
905
  target: checkpoint.assert,
604
906
  evidence: commandResult.evidence,
605
- exit_code: commandResult.exitCode,
606
- message: commandResult.passed
907
+ exit_code: exitCode ?? (timedOut ? 124 : 1),
908
+ message: passed
607
909
  ? checkpoint.message
608
- : commandResult.timedOut
910
+ : timedOut
609
911
  ? `Verifier command timed out after ${context.commandTimeoutMs ?? DEFAULT_VERIFIER_COMMAND_TIMEOUT_MS}ms`
610
912
  : checkpoint.message
611
913
  ? appendFailureDiagnostic(checkpoint.message, commandResult.evidence)
@@ -0,0 +1,47 @@
1
+ import type { CanonicalResultDigest } from "./run-contracts.js";
2
+ export declare const WINDOWS_JOB_KEEPER_PROTOCOL_VERSION: "IDNA_WINDOWS_JOB_KEEPER_V1";
3
+ export declare const WINDOWS_JOB_KEEPER_MANIFEST_SCHEMA: "intentdna.windows_job_keeper_manifest.v1";
4
+ export type WindowsJobKeeperArchitecture = "x86_64" | "aarch64";
5
+ interface WindowsJobKeeperManifestFile {
6
+ readonly path: string;
7
+ readonly sha256: CanonicalResultDigest;
8
+ }
9
+ interface WindowsJobKeeperManifestBinary extends WindowsJobKeeperManifestFile {
10
+ readonly pe_machine: "0x8664" | "0xaa64";
11
+ }
12
+ export interface WindowsJobKeeperManifest {
13
+ readonly schema_version: typeof WINDOWS_JOB_KEEPER_MANIFEST_SCHEMA;
14
+ readonly protocol_version: typeof WINDOWS_JOB_KEEPER_PROTOCOL_VERSION;
15
+ readonly source: WindowsJobKeeperManifestFile;
16
+ readonly toolchain: {
17
+ readonly name: "zig";
18
+ readonly version: "0.16.0";
19
+ readonly build_script: WindowsJobKeeperManifestFile & {
20
+ readonly path: "../../scripts/build-windows-job-keeper.mjs";
21
+ };
22
+ readonly source_date_epoch: 0;
23
+ readonly targets: Readonly<Record<WindowsJobKeeperArchitecture, "x86_64-windows-gnu" | "aarch64-windows-gnu">>;
24
+ };
25
+ readonly binaries: Readonly<Record<WindowsJobKeeperArchitecture, WindowsJobKeeperManifestBinary>>;
26
+ }
27
+ export interface WindowsJobKeeperArtifact {
28
+ readonly architecture: WindowsJobKeeperArchitecture;
29
+ readonly path: string;
30
+ readonly digest: CanonicalResultDigest;
31
+ readonly source_digest: CanonicalResultDigest;
32
+ readonly protocol_version: typeof WINDOWS_JOB_KEEPER_PROTOCOL_VERSION;
33
+ readonly manifest_path: string | null;
34
+ }
35
+ export interface ResolveWindowsJobKeeperOptions {
36
+ readonly platform: NodeJS.Platform;
37
+ readonly architecture?: NodeJS.Architecture;
38
+ readonly helper_path?: string;
39
+ readonly expected_digest?: CanonicalResultDigest;
40
+ }
41
+ export declare class WindowsJobKeeperArtifactError extends Error {
42
+ constructor(message: string, options?: ErrorOptions);
43
+ }
44
+ export declare function parseWindowsJobKeeperManifest(value: unknown): WindowsJobKeeperManifest;
45
+ export declare function readPeMachine(path: string): number;
46
+ export declare function resolveWindowsJobKeeperArtifact(options: ResolveWindowsJobKeeperOptions): WindowsJobKeeperArtifact | null;
47
+ export {};