maka-agent 0.2.0-dev.53.20260927 → 0.2.0-dev.62.20260928

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 (55) hide show
  1. package/LICENSE +6 -5
  2. package/dist/workspace-root.js +2 -2
  3. package/native/runtime-host-windows-task-launcher/prebuilds/win32-x64/maka-runtime-host-task-launcher.exe +0 -0
  4. package/node_modules/@maka/acp-executor-plugin/dist/index.js +333 -28
  5. package/node_modules/@maka/acp-executor-plugin/dist/plugin.mjs +283 -27
  6. package/node_modules/@maka/core/dist/executor-catalog.js +10 -1
  7. package/node_modules/@maka/core/dist/message-queue-order.js +47 -0
  8. package/node_modules/@maka/core/dist/model-metadata.js +2 -1
  9. package/node_modules/@maka/core/dist/redaction.js +42 -3
  10. package/node_modules/@maka/core/dist/relative-time-policy.js +71 -0
  11. package/node_modules/@maka/core/dist/relative-time.js +3 -54
  12. package/node_modules/@maka/core/package.json +1 -0
  13. package/node_modules/@maka/eval/dist/egress-audit.js +127 -0
  14. package/node_modules/@maka/eval/dist/harness-executor.js +16 -96
  15. package/node_modules/@maka/eval/harbor/egress-proxy/entrypoint.sh +4 -4
  16. package/node_modules/@maka/eval/harbor/egress_filter.py +153 -104
  17. package/node_modules/@maka/eval/harbor/eval_framework.py +37 -14
  18. package/node_modules/@maka/eval/harbor/relay_agent.py +4 -6
  19. package/node_modules/@maka/eval/harbor/run_trial.py +11 -4
  20. package/node_modules/@maka/runtime/dist/agent-graph-supervisor-wake.js +33 -6
  21. package/node_modules/@maka/runtime/dist/ai-sdk-turn.js +1 -1
  22. package/node_modules/@maka/runtime/dist/plugin-executor-backend.js +26 -5
  23. package/node_modules/@maka/runtime/dist/plugin-executor-service.js +36 -0
  24. package/node_modules/@maka/runtime/dist/provider-error-classification.js +8 -73
  25. package/node_modules/@maka/runtime/dist/provider-error-signals.js +31 -0
  26. package/node_modules/@maka/runtime/dist/provider-retry-policy.js +79 -0
  27. package/node_modules/@maka/runtime/dist/session-manager.js +6 -4
  28. package/node_modules/@maka/runtime/dist/session-projection-helpers.js +2 -0
  29. package/node_modules/@maka/runtime/dist/shell-run-manager.js +8 -8
  30. package/node_modules/@maka/runtime/dist/terminal-run-commit.js +7 -1
  31. package/node_modules/@maka/runtime/dist/tool-runtime.js +5 -4
  32. package/node_modules/@maka/runtime-host/dist/adapter/provider-retry-projector.js +63 -0
  33. package/node_modules/@maka/runtime-host/dist/adapter/session-projector.js +7 -51
  34. package/node_modules/@maka/runtime-host/dist/protocol/index.js +9 -3
  35. package/node_modules/@maka/runtime-host/dist/protocol/message-queue-limits.js +21 -0
  36. package/node_modules/@maka/runtime-host/dist/protocol/message.js +6 -104
  37. package/node_modules/@maka/runtime-host/dist/protocol/queue-mutation.js +136 -0
  38. package/node_modules/@maka/runtime-host/dist/protocol/turn-provider-retry.js +69 -0
  39. package/node_modules/@maka/runtime-host/dist/protocol/turn.js +2 -43
  40. package/node_modules/@maka/runtime-host/dist/server/goal-coordinator.js +42 -5
  41. package/node_modules/@maka/runtime-host/dist/server/message-coordinator.js +105 -233
  42. package/node_modules/@maka/runtime-host/dist/server/message-queue-state.js +69 -0
  43. package/node_modules/@maka/runtime-host/dist/server/provider-retry-projection.js +71 -0
  44. package/node_modules/@maka/runtime-host/dist/server/queued-mutation-executor.js +94 -0
  45. package/node_modules/@maka/runtime-host/dist/server/root-turn-coordinator.js +1 -1
  46. package/node_modules/@maka/runtime-host/dist/server/session-continuity-coordinator.js +9 -58
  47. package/node_modules/@maka/storage/dist/agent-run-store-contract.js +0 -3
  48. package/node_modules/@maka/storage/dist/agent-run-store.js +15 -6
  49. package/node_modules/@maka/storage/dist/execution-persistence-provider.js +1 -0
  50. package/node_modules/@maka/storage/dist/execution-stores.js +1 -0
  51. package/node_modules/@maka/storage/dist/project-catalog.js +41 -42
  52. package/node_modules/@maka/storage/dist/sqlite-runtime-store.js +102 -31
  53. package/node_modules/@maka/storage/dist/sqlite-session-metadata-schema.js +52 -1
  54. package/node_modules/@maka/storage/dist/sqlite-session-metadata-store.js +34 -2
  55. package/package.json +1 -1
@@ -0,0 +1,71 @@
1
+ /*
2
+ * Licensed to the Apache Software Foundation (ASF) under one
3
+ * or more contributor license agreements. See the NOTICE file
4
+ * distributed with this work for additional information
5
+ * regarding copyright ownership. The ASF licenses this file
6
+ * to you under the Apache License, Version 2.0 (the
7
+ * "License"); you may not use this file except in compliance
8
+ * with the License. You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing,
13
+ * software distributed under the License is distributed on an
14
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
+ * KIND, either express or implied. See the License for the
16
+ * specific language governing permissions and limitations
17
+ * under the License.
18
+ */
19
+ export const RELATIVE_HORIZON_MS = 7 * 24 * 60 * 60 * 1000;
20
+ export const JUST_NOW_MS = 60_000;
21
+ export const MINUTE_MS = 60_000;
22
+ export const HOUR_MS = 60 * MINUTE_MS;
23
+ export const DAY_MS = 24 * HOUR_MS;
24
+ export const MONTH_MS = 30 * DAY_MS;
25
+ export const YEAR_MS = 12 * MONTH_MS;
26
+ const MAX_SIDEBAR_REFRESH_MS = 24 * DAY_MS;
27
+ export const JUST_NOW = {
28
+ 'zh-CN': '刚刚',
29
+ 'zh-TW': '剛剛',
30
+ en: 'just now',
31
+ };
32
+ const SIDEBAR_TIME_BUCKETS = [
33
+ { unitMs: MINUTE_MS, maxValue: 60, suffix: 'min' },
34
+ { unitMs: HOUR_MS, maxValue: 24, suffix: 'h' },
35
+ { unitMs: DAY_MS, maxValue: 30, suffix: 'd' },
36
+ { unitMs: MONTH_MS, maxValue: 12, suffix: 'mo' },
37
+ { unitMs: YEAR_MS, maxValue: Number.POSITIVE_INFINITY, suffix: 'y' },
38
+ ];
39
+ export function relativeAgeMs(timestamp, now) {
40
+ return Math.max(0, now - timestamp);
41
+ }
42
+ export function sidebarTimeBucket(ageMs) {
43
+ for (const bucket of SIDEBAR_TIME_BUCKETS) {
44
+ const value = Math.round(ageMs / bucket.unitMs);
45
+ if (value < bucket.maxValue)
46
+ return { value, unitMs: bucket.unitMs, suffix: bucket.suffix };
47
+ }
48
+ throw new Error('Sidebar time buckets must end with an unbounded bucket');
49
+ }
50
+ function nextRoundedBoundaryDelay(ageMs, unitMs, value) {
51
+ return Math.max(1, Math.ceil((value + 0.5) * unitMs - ageMs));
52
+ }
53
+ export function nextRelativeRefreshDelay(timestamp, now) {
54
+ const ageMs = relativeAgeMs(timestamp, now);
55
+ if (ageMs > RELATIVE_HORIZON_MS)
56
+ return null;
57
+ if (ageMs < JUST_NOW_MS)
58
+ return JUST_NOW_MS - ageMs;
59
+ if (ageMs < HOUR_MS)
60
+ return MINUTE_MS;
61
+ return 10 * MINUTE_MS;
62
+ }
63
+ export function nextSidebarRefreshDelay(timestamp, now) {
64
+ const ageMs = relativeAgeMs(timestamp, now);
65
+ if (!Number.isFinite(ageMs))
66
+ return null;
67
+ if (ageMs < JUST_NOW_MS)
68
+ return JUST_NOW_MS - ageMs;
69
+ const bucket = sidebarTimeBucket(ageMs);
70
+ return Math.min(nextRoundedBoundaryDelay(ageMs, bucket.unitMs, bucket.value), MAX_SIDEBAR_REFRESH_MS);
71
+ }
@@ -24,33 +24,7 @@
24
24
  * useful than a relative label like "300 天前".
25
25
  */
26
26
  import { uiLocaleToIntlLocale } from './ui-locale.js';
27
- /** Maximum age (ms) that still gets a relative bucket. Older → absolute. */
28
- const RELATIVE_HORIZON_MS = 7 * 24 * 60 * 60 * 1000;
29
- /** Age below this stays on one just-now label instead of counting seconds. */
30
- const JUST_NOW_MS = 60_000;
31
- const MINUTE_MS = 60_000;
32
- const HOUR_MS = 60 * MINUTE_MS;
33
- const DAY_MS = 24 * HOUR_MS;
34
- const MONTH_MS = 30 * DAY_MS;
35
- const YEAR_MS = 12 * MONTH_MS;
36
- /** Stays below the browser timer ceiling while avoiding needless daily wakes. */
37
- const MAX_SIDEBAR_REFRESH_MS = 24 * DAY_MS;
38
- const SIDEBAR_TIME_BUCKETS = [
39
- { unitMs: MINUTE_MS, maxValue: 60, suffix: 'min' },
40
- { unitMs: HOUR_MS, maxValue: 24, suffix: 'h' },
41
- { unitMs: DAY_MS, maxValue: 30, suffix: 'd' },
42
- { unitMs: MONTH_MS, maxValue: 12, suffix: 'mo' },
43
- { unitMs: YEAR_MS, maxValue: Number.POSITIVE_INFINITY, suffix: 'y' },
44
- ];
45
- const JUST_NOW = {
46
- 'zh-CN': '刚刚',
47
- 'zh-TW': '剛剛',
48
- en: 'just now',
49
- };
50
- /** Future timestamps are treated as age zero and therefore display as just now. */
51
- function relativeAgeMs(ts, now) {
52
- return Math.max(0, now - ts);
53
- }
27
+ import { JUST_NOW, JUST_NOW_MS, RELATIVE_HORIZON_MS, relativeAgeMs, nextRelativeRefreshDelay as nextRelativeRefreshDelayPolicy, nextSidebarRefreshDelay as nextSidebarRefreshDelayPolicy, sidebarTimeBucket, } from './relative-time-policy.js';
54
28
  // One cache per formatter. They used to share `cachedLocale` and clear each
55
29
  // other on a miss, so alternating relative and absolute reads — which is what
56
30
  // the sidebar does, once per row — rebuilt an `Intl` formatter every call.
@@ -175,33 +149,8 @@ export function resetRelativeTimeFormatters() {
175
149
  * past the horizon (never re-render).
176
150
  */
177
151
  export function nextRelativeRefreshDelay(ts, now = Date.now()) {
178
- const ageMs = relativeAgeMs(ts, now);
179
- if (ageMs > RELATIVE_HORIZON_MS)
180
- return null;
181
- if (ageMs < JUST_NOW_MS)
182
- return JUST_NOW_MS - ageMs;
183
- if (ageMs < 60 * 60_000)
184
- return 60_000;
185
- return 10 * 60_000;
186
- }
187
- function nextRoundedBoundaryDelay(ageMs, unitMs, value) {
188
- return Math.max(1, Math.ceil((value + 0.5) * unitMs - ageMs));
189
- }
190
- function sidebarTimeBucket(ageMs) {
191
- for (const bucket of SIDEBAR_TIME_BUCKETS) {
192
- const value = Math.round(ageMs / bucket.unitMs);
193
- if (value < bucket.maxValue)
194
- return { value, unitMs: bucket.unitMs, suffix: bucket.suffix };
195
- }
196
- throw new Error('Sidebar time buckets must end with an unbounded bucket');
152
+ return nextRelativeRefreshDelayPolicy(ts, now);
197
153
  }
198
- /** Refreshes at the next visible sidebar bucket, capped below the timer ceiling. */
199
154
  export function nextSidebarRefreshDelay(ts, now = Date.now()) {
200
- const ageMs = relativeAgeMs(ts, now);
201
- if (!Number.isFinite(ageMs))
202
- return null;
203
- if (ageMs < JUST_NOW_MS)
204
- return JUST_NOW_MS - ageMs;
205
- const bucket = sidebarTimeBucket(ageMs);
206
- return Math.min(nextRoundedBoundaryDelay(ageMs, bucket.unitMs, bucket.value), MAX_SIDEBAR_REFRESH_MS);
155
+ return nextSidebarRefreshDelayPolicy(ts, now);
207
156
  }
@@ -28,6 +28,7 @@
28
28
  "./execution-inspect": "./dist/execution-inspect.js",
29
29
  "./events": "./dist/events.js",
30
30
  "./provider-retry-countdown": "./dist/provider-retry-countdown.js",
31
+ "./message-queue-order": "./dist/message-queue-order.js",
31
32
  "./interaction": "./dist/interaction.js",
32
33
  "./session": "./dist/session.js",
33
34
  "./turn-origin": "./dist/turn-origin.js",
@@ -0,0 +1,127 @@
1
+ /*
2
+ * Licensed to the Apache Software Foundation (ASF) under one
3
+ * or more contributor license agreements. See the NOTICE file
4
+ * distributed with this work for additional information
5
+ * regarding copyright ownership. The ASF licenses this file
6
+ * to you under the Apache License, Version 2.0 (the
7
+ * "License"); you may not use this file except in compliance
8
+ * with the License. You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing,
13
+ * software distributed under the License is distributed on an
14
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
+ * KIND, either express or implied. See the License for the
16
+ * specific language governing permissions and limitations
17
+ * under the License.
18
+ */
19
+ import { createHash } from 'node:crypto';
20
+ import { readFile } from 'node:fs/promises';
21
+ import { join } from 'node:path';
22
+ export const EGRESS_AUDIT_DESTINATION = 'egress-hits.jsonl';
23
+ export const EGRESS_AUDIT_ARTIFACT_PATH = `artifacts/${EGRESS_AUDIT_DESTINATION}`;
24
+ export function egressAuditCollection() {
25
+ return [
26
+ {
27
+ source: '/opt/maka-egress-state/hits.jsonl',
28
+ destination: EGRESS_AUDIT_DESTINATION,
29
+ service: 'maka-eval-mitmproxy',
30
+ },
31
+ ];
32
+ }
33
+ export function describeEgressAudit(bytes, required) {
34
+ if (!required)
35
+ return cleanEvidence();
36
+ if (bytes === undefined)
37
+ return missingEvidence();
38
+ return {
39
+ failureReason: null,
40
+ artifacts: [
41
+ {
42
+ kind: 'egress-audit',
43
+ path: EGRESS_AUDIT_ARTIFACT_PATH,
44
+ bytes: bytes.byteLength,
45
+ sha256: digest(bytes),
46
+ ...summarize(bytes),
47
+ },
48
+ ],
49
+ };
50
+ }
51
+ export async function readEgressAuditEvidence(trialPath, required) {
52
+ if (!required)
53
+ return cleanEvidence();
54
+ const auditPath = join(trialPath, EGRESS_AUDIT_ARTIFACT_PATH);
55
+ try {
56
+ return describeEgressAudit(await readFile(auditPath), true);
57
+ }
58
+ catch (error) {
59
+ const code = errorCode(error);
60
+ if (code === 'ENOENT')
61
+ return missingEvidence();
62
+ return {
63
+ failureReason: `failed to read egress audit log ${auditPath}${code ? ` (${code})` : ''}`,
64
+ artifacts: [{ kind: 'egress-audit-unreadable', path: EGRESS_AUDIT_ARTIFACT_PATH }],
65
+ };
66
+ }
67
+ }
68
+ function summarize(bytes) {
69
+ let truncated = false;
70
+ let policyErrorCount = 0;
71
+ let malformedLineCount = 0;
72
+ for (const line of jsonLines(bytes)) {
73
+ const record = decodeRecord(line);
74
+ if (record === undefined)
75
+ continue;
76
+ if (record === null) {
77
+ malformedLineCount += 1;
78
+ }
79
+ else if (record.ruleId === 'audit_truncated') {
80
+ truncated = true;
81
+ }
82
+ else if (record.ruleId === 'policy_error') {
83
+ policyErrorCount += 1;
84
+ }
85
+ }
86
+ return { truncated, policyErrorCount, malformedLineCount };
87
+ }
88
+ function* jsonLines(bytes) {
89
+ let start = 0;
90
+ while (start < bytes.length) {
91
+ const newline = bytes.indexOf(0x0a, start);
92
+ const end = newline === -1 ? bytes.length : newline;
93
+ yield bytes.subarray(start, end);
94
+ start = newline === -1 ? bytes.length : newline + 1;
95
+ }
96
+ }
97
+ function decodeRecord(line) {
98
+ if (line.length === 0)
99
+ return undefined;
100
+ try {
101
+ const text = new TextDecoder('utf-8', { fatal: true }).decode(line).trim();
102
+ if (!text)
103
+ return undefined;
104
+ const decoded = JSON.parse(text);
105
+ if (!decoded || typeof decoded !== 'object' || Array.isArray(decoded))
106
+ return null;
107
+ return decoded;
108
+ }
109
+ catch {
110
+ return null;
111
+ }
112
+ }
113
+ function cleanEvidence() {
114
+ return { failureReason: null, artifacts: [] };
115
+ }
116
+ function missingEvidence() {
117
+ return {
118
+ failureReason: 'egress audit log missing',
119
+ artifacts: [{ kind: 'egress-audit-missing', path: EGRESS_AUDIT_ARTIFACT_PATH }],
120
+ };
121
+ }
122
+ function digest(bytes) {
123
+ return `sha256:${createHash('sha256').update(bytes).digest('hex')}`;
124
+ }
125
+ function errorCode(error) {
126
+ return error instanceof Error ? error.code : undefined;
127
+ }
@@ -24,6 +24,7 @@ import { chmod, lstat, mkdir, readFile, readdir, unlink, writeFile } from 'node:
24
24
  import { createServer } from 'node:net';
25
25
  import { basename, dirname, join, posix, relative, resolve, sep } from 'node:path';
26
26
  import { createInterface } from 'node:readline';
27
+ import { egressAuditCollection, readEgressAuditEvidence } from './egress-audit.js';
27
28
  import { decodeJsonObject } from './experiment.js';
28
29
  import { BUNDLED_HARNESS_RELAY_ROOT, createHarnessPreparationEnvironment, resolveRealPathWithinRoot, } from './harness-environment.js';
29
30
  import { preflightHarnessInstallation, } from './install-preflight.js';
@@ -344,13 +345,7 @@ async function startTrial(framework, options, specPath, cell, subjectCredentialN
344
345
  environment: environmentConfig,
345
346
  ...(options.egressProxy
346
347
  ? {
347
- artifacts: [
348
- {
349
- source: '/opt/maka-egress-state/hits.jsonl',
350
- destination: EGRESS_AUDIT_DESTINATION,
351
- service: 'maka-eval-mitmproxy',
352
- },
353
- ],
348
+ artifacts: egressAuditCollection(),
354
349
  }
355
350
  : {}),
356
351
  })}\n`, { flag: 'wx', mode: 0o600 });
@@ -555,108 +550,33 @@ async function closeServer(server) {
555
550
  server.close((error) => (error ? rejectClose(error) : resolveClose()));
556
551
  });
557
552
  }
558
- export const EGRESS_AUDIT_DESTINATION = 'egress-hits.jsonl';
559
- export const EGRESS_AUDIT_ARTIFACT_PATH = `artifacts/${EGRESS_AUDIT_DESTINATION}`;
560
- export function collectEgressAuditArtifact(audit, expected) {
561
- if (!expected)
562
- return { missing: false, failureReason: null, artifacts: [] };
563
- if (audit === undefined) {
564
- return {
565
- missing: true,
566
- failureReason: 'egress audit log missing',
567
- artifacts: [{ kind: 'egress-audit-missing', path: EGRESS_AUDIT_ARTIFACT_PATH }],
568
- };
569
- }
570
- const forensics = inspectEgressAudit(audit);
571
- return {
572
- missing: false,
573
- failureReason: null,
574
- artifacts: [
575
- {
576
- kind: 'egress-audit',
577
- path: EGRESS_AUDIT_ARTIFACT_PATH,
578
- bytes: audit.byteLength,
579
- sha256: `sha256:${createHash('sha256').update(audit).digest('hex')}`,
580
- truncated: forensics.truncated,
581
- policyErrorCount: forensics.policyErrorCount,
582
- malformedLineCount: forensics.malformedLineCount,
583
- },
584
- ],
585
- };
586
- }
587
- function inspectEgressAudit(audit) {
588
- let truncated = false;
589
- let policyErrorCount = 0;
590
- let malformedLineCount = 0;
591
- for (const line of audit.toString('utf8').split(/\r?\n/)) {
592
- const trimmed = line.trim();
593
- if (!trimmed)
594
- continue;
595
- let record;
596
- try {
597
- record = JSON.parse(trimmed);
598
- }
599
- catch {
600
- malformedLineCount += 1;
601
- continue;
602
- }
603
- if (!record || typeof record !== 'object' || Array.isArray(record)) {
604
- malformedLineCount += 1;
605
- continue;
606
- }
607
- const ruleId = record.ruleId;
608
- if (ruleId === 'audit_truncated')
609
- truncated = true;
610
- if (ruleId === 'policy_error')
611
- policyErrorCount += 1;
612
- }
613
- return { truncated, policyErrorCount, malformedLineCount };
614
- }
615
- async function readVerification(state, cell, framework, expectEgressAudit) {
553
+ async function readVerification(state, cell, framework, auditRequired) {
616
554
  const result = JSON.parse(await readFile(join(state.trialPath, 'result.json'), 'utf8'));
617
555
  const score = result.verifier_result?.rewards?.[rewardKey(cell)] ?? null;
618
556
  const subjectException = ['AgentTimeoutError', 'NonZeroAgentExitCodeError'].includes(String(result.exception_info?.exception_type));
619
557
  if (result.exception_info && !subjectException) {
620
558
  throw new Error('Trial failed outside subject execution');
621
559
  }
622
- const egressAuditPath = join(state.trialPath, EGRESS_AUDIT_ARTIFACT_PATH);
623
- let egressAudit;
624
- try {
625
- egressAudit = await readFile(egressAuditPath);
626
- }
627
- catch (error) {
628
- const code = error.code;
629
- if (expectEgressAudit && code !== 'ENOENT') {
630
- return {
631
- status: 'infra_failed',
632
- score,
633
- failureReason: `failed to read egress audit log ${egressAuditPath}${code ? ` (${code})` : ''}`,
634
- artifacts: [
635
- { kind: 'trial', framework: cell.executor.kind, trialName: state.trialName },
636
- ...(await collectedArtifactInventory(state.trialPath, framework)),
637
- { kind: 'egress-audit-unreadable', path: EGRESS_AUDIT_ARTIFACT_PATH },
638
- ],
639
- };
640
- }
641
- }
642
- const audit = collectEgressAuditArtifact(egressAudit, expectEgressAudit);
560
+ const audit = await readEgressAuditEvidence(state.trialPath, auditRequired);
561
+ const baseFailure = score === null ? 'verifier produced no reward' : null;
562
+ const status = verificationStatus(audit.failureReason, score, subjectException);
563
+ const collectedArtifacts = await collectedArtifactInventory(state.trialPath, framework);
643
564
  return {
644
- status: audit.failureReason
645
- ? 'infra_failed'
646
- : score === null
647
- ? 'infra_failed'
648
- : subjectException
649
- ? 'subject_failed'
650
- : 'completed',
565
+ status,
651
566
  score,
652
- failureReason: audit.failureReason ?? (score === null ? 'verifier produced no reward' : null),
567
+ failureReason: audit.failureReason ?? baseFailure,
653
568
  artifacts: [
654
569
  { kind: 'trial', framework: cell.executor.kind, trialName: state.trialName },
655
- ...(await collectedArtifactInventory(state.trialPath, framework)),
656
- ...audit.artifacts,
570
+ ...collectedArtifacts,
571
+ ...Array.from(audit.artifacts),
657
572
  ],
658
573
  };
659
574
  }
575
+ function verificationStatus(auditFailure, score, subjectException) {
576
+ if (auditFailure !== null || score === null)
577
+ return 'infra_failed';
578
+ return subjectException ? 'subject_failed' : 'completed';
579
+ }
660
580
  async function collectedArtifactInventory(trialPath, framework) {
661
581
  const root = framework === 'pier'
662
582
  ? join(trialPath, 'artifacts')
@@ -22,10 +22,10 @@ STATE_DIR=/opt/maka-egress-state
22
22
  CERT_DIR=/opt/maka-egress
23
23
 
24
24
  mkdir -p "$STATE_DIR" "$CERT_DIR"
25
- # Create the audit log if this is the first start. Truncating here would erase
26
- # hits already written if the proxy restarts mid-trial, and the trial would
27
- # look like an honest zero-hit run.
28
- touch "$STATE_DIR/hits.jsonl"
25
+ AUDIT_LOG="$STATE_DIR/hits.jsonl"
26
+ # Proxy restarts share one append-only evidence file for the whole trial.
27
+ # Only create it when absent so an earlier process cannot lose recorded hits.
28
+ test -e "$AUDIT_LOG" || touch "$AUDIT_LOG"
29
29
 
30
30
  # confdir holds the CA private key and this cell's audit log, so it stays
31
31
  # container-private and only the certificate reaches the volume the subject