@miraland-labs/conduit-bridge 0.16.162 → 0.16.172
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/dist/driver.js +103 -184
- package/dist/execution.js +545 -454
- package/dist/failure-signal.js +9 -2
- package/dist/investigation.js +57 -8
- package/dist/land-git-reality.js +2 -2
- package/package.json +2 -1
- package/schemas/delivery-report.schema.json +154 -0
- package/schemas/repair-brief.schema.json +52 -0
package/dist/driver.js
CHANGED
|
@@ -291,82 +291,58 @@ export function buildAssignmentPrompt(context) {
|
|
|
291
291
|
"- With it, write unverified_detail: one sentence naming exactly what was missing, in the owner's terms. \"Checking this needs the app deployed and running, and this task cannot deploy\" — not \"environment unavailable\".",
|
|
292
292
|
// The owner reads outcome, limitations and unverified_detail on a phone. Dense prose was the
|
|
293
293
|
// complaint on every delivery this trial; ASD-STE100's rules are the shortest fix.
|
|
294
|
-
"- Write outcome, risks, limitations and every unverified_detail in Simplified Technical English (ASD-STE100): short sentences of at most 20 words, one fact or instruction per sentence, active voice, present tense, and the product's own terms (work package, delivery, gate, Hold) with no synonyms.", "", "When the work is finished, end your reply with exactly one fenced ```json block:", agentReportTemplate);
|
|
294
|
+
"- Write outcome, risks, limitations and every unverified_detail in Simplified Technical English (ASD-STE100): short sentences of at most 20 words, one fact or instruction per sentence, active voice, present tense, and the product's own terms (work package, delivery, gate, Hold) with no synonyms.", "", "- Delivery report limits: each details and acceptance_criteria entry is at most 4000 characters, with at most 100 entries per list; digest, name, and unverified_detail are non-empty and at most 500 characters; uri and pull_request_url are absolute URLs of at most 4000 characters.", "When the work is finished, end your reply with exactly one fenced ```json block:", agentReportTemplate);
|
|
295
295
|
return lines.join("\n");
|
|
296
296
|
}
|
|
297
297
|
/**
|
|
298
|
-
*
|
|
299
|
-
*
|
|
300
|
-
*
|
|
301
|
-
*
|
|
302
|
-
* ``` it meets — including one inside the report's own strings. That is not a rare accident: a
|
|
303
|
-
* report about markdown rendering describes fences, so its summary contains them, and the extractor
|
|
304
|
-
* then preferred a truncated fragment over the perfectly recoverable whole. Every retry wrote the
|
|
305
|
-
* same summary and failed the same way, which is what "malformed report JSON, again" was.
|
|
298
|
+
* The reply's last ```json fenced block, or the whole reply when it has no fence. An opener ends its
|
|
299
|
+
* line (the cursor lane writes a sentence and opens the fence at its end), a closer stands alone,
|
|
300
|
+
* and a JSON string holds no line break, so a fence quoted inside the report's own strings never
|
|
301
|
+
* opens or closes one. Callers read the result with `JSON.parse` and nothing else.
|
|
306
302
|
*/
|
|
307
303
|
export function extractAgentReportJsonText(text) {
|
|
308
|
-
const
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
304
|
+
const lines = text.split(/\r?\n/);
|
|
305
|
+
let opener = -1;
|
|
306
|
+
for (let i = lines.length - 1; i >= 0 && opener < 0; i--)
|
|
307
|
+
if (/```json[ \t]*$/i.test(lines[i]))
|
|
308
|
+
opener = i;
|
|
309
|
+
if (opener < 0)
|
|
310
|
+
return text.trim();
|
|
311
|
+
const closer = lines.findIndex((line, i) => i > opener && /^[ \t]*```[ \t]*$/.test(line));
|
|
312
|
+
return lines.slice(opener + 1, closer < 0 ? undefined : closer).join("\n").trim();
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* A reply whose object does not parse or does not match its strict schema. Each issue is one zod
|
|
316
|
+
* path and message, so the re-ask and the failure detail name the field, not a sentence about it.
|
|
317
|
+
*/
|
|
318
|
+
export class ReplyInvalid extends Error {
|
|
319
|
+
kind;
|
|
320
|
+
issues;
|
|
321
|
+
constructor(kind, issues) {
|
|
322
|
+
super(`${kind}: ${issues.join("; ")}`);
|
|
323
|
+
this.kind = kind;
|
|
324
|
+
this.issues = issues;
|
|
318
325
|
}
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
for (const candidate of candidates) {
|
|
326
|
+
}
|
|
327
|
+
/** Parse a reply's object and validate it strictly; anything else is a ReplyInvalid. */
|
|
328
|
+
export function parseReplyObject(kind, schema, text, structuredOutput) {
|
|
329
|
+
let raw = structuredOutput;
|
|
330
|
+
if (structuredOutput === undefined) {
|
|
325
331
|
try {
|
|
326
|
-
|
|
327
|
-
return candidate;
|
|
332
|
+
raw = JSON.parse(extractAgentReportJsonText(text ?? ""));
|
|
328
333
|
}
|
|
329
|
-
catch {
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
}
|
|
333
|
-
// Nothing parsed: hand back the preferred candidate so the caller reports on the real report.
|
|
334
|
-
return candidates[0];
|
|
335
|
-
}
|
|
336
|
-
/** Parse JSON, or the first balanced `{...}` object when the candidate is truncated prose. */
|
|
337
|
-
export function parseJsonObjectCandidate(candidate) {
|
|
338
|
-
try {
|
|
339
|
-
return JSON.parse(candidate);
|
|
340
|
-
}
|
|
341
|
-
catch {
|
|
342
|
-
const start = candidate.indexOf("{");
|
|
343
|
-
if (start < 0)
|
|
344
|
-
throw new Error("Agent emitted malformed report JSON");
|
|
345
|
-
let depth = 0;
|
|
346
|
-
let inString = false;
|
|
347
|
-
for (let i = start; i < candidate.length; i++) {
|
|
348
|
-
const ch = candidate[i];
|
|
349
|
-
if (inString) {
|
|
350
|
-
if (ch === "\\")
|
|
351
|
-
i++;
|
|
352
|
-
else if (ch === '"')
|
|
353
|
-
inString = false;
|
|
354
|
-
}
|
|
355
|
-
else if (ch === '"')
|
|
356
|
-
inString = true;
|
|
357
|
-
else if (ch === "{")
|
|
358
|
-
depth++;
|
|
359
|
-
else if (ch === "}" && --depth === 0) {
|
|
360
|
-
try {
|
|
361
|
-
return JSON.parse(candidate.slice(start, i + 1));
|
|
362
|
-
}
|
|
363
|
-
catch {
|
|
364
|
-
throw new Error("Agent emitted malformed report JSON");
|
|
365
|
-
}
|
|
366
|
-
}
|
|
334
|
+
catch (error) {
|
|
335
|
+
const reason = error instanceof Error ? error.message : "not JSON";
|
|
336
|
+
throw new ReplyInvalid(kind, [`(root): the last \`\`\`json fenced block, or the whole reply when it has none, is not JSON (${reason})`]);
|
|
367
337
|
}
|
|
368
|
-
throw new Error("Agent emitted malformed report JSON");
|
|
369
338
|
}
|
|
339
|
+
const parsed = schema.safeParse(raw);
|
|
340
|
+
if (parsed.success)
|
|
341
|
+
return parsed.data;
|
|
342
|
+
throw new ReplyInvalid(kind, parsed.error.issues.slice(0, 20).map((issue) => {
|
|
343
|
+
const at = issue.path.reduce((path, key) => typeof key === "number" ? `${path}[${key}]` : path ? `${path}.${key}` : key, "");
|
|
344
|
+
return `${at || "(root)"}: ${issue.message}`;
|
|
345
|
+
}));
|
|
370
346
|
}
|
|
371
347
|
/**
|
|
372
348
|
* Why this run could not decide an acceptance criterion.
|
|
@@ -474,37 +450,41 @@ function quoteCriterion(value) {
|
|
|
474
450
|
const text = value.trim().replace(/\s+/g, " ");
|
|
475
451
|
return `"${text.length > 120 ? `${text.slice(0, 117)}...` : text}"`;
|
|
476
452
|
}
|
|
477
|
-
|
|
478
|
-
export
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
453
|
+
const reportStringList = z.array(z.string().trim().min(1).max(4_000)).max(100);
|
|
454
|
+
export const agentReportSchema = z.object({
|
|
455
|
+
outcome: z.string().trim().min(1).max(20_000),
|
|
456
|
+
changes: reportStringList,
|
|
457
|
+
verification: reportStringList,
|
|
458
|
+
acceptance_results: z.array(z.object({
|
|
459
|
+
criterion: z.string().trim().min(1).max(4_000), status: z.enum(["met", "not_met", "unknown"]),
|
|
460
|
+
unverified_reason: z.enum(UNVERIFIED_REASONS).optional(), unverified_detail: z.string().trim().min(1).max(500).optional(),
|
|
461
|
+
})).max(100),
|
|
462
|
+
evidence: z.array(z.object({
|
|
463
|
+
kind: z.enum(evidenceKinds), name: z.string().trim().min(1).max(500), uri: z.string().url().max(4_000).optional(),
|
|
464
|
+
digest: z.string().trim().min(1).max(500).optional(), details: reportStringList, acceptance_criteria: reportStringList,
|
|
465
|
+
})).max(100),
|
|
466
|
+
assumptions: reportStringList, risks: reportStringList, limitations: reportStringList,
|
|
467
|
+
head_commit: z.string().regex(/^[0-9a-f]{7,64}$/i).optional(),
|
|
468
|
+
pull_request_url: z.string().url().max(4_000).optional(),
|
|
469
|
+
}).strict();
|
|
470
|
+
/** Strict generation requires every key; explicit null carries an optional field's absence. */
|
|
471
|
+
export const agentReportOutputSchema = agentReportSchema.extend({
|
|
472
|
+
acceptance_results: z.array(agentReportSchema.shape.acceptance_results.element.extend({
|
|
473
|
+
unverified_reason: z.enum(UNVERIFIED_REASONS).nullable().transform((value) => value ?? undefined),
|
|
474
|
+
unverified_detail: z.string().trim().min(1).max(500).nullable().transform((value) => value ?? undefined),
|
|
475
|
+
}).strict()).max(100),
|
|
476
|
+
evidence: z.array(agentReportSchema.shape.evidence.element.extend({
|
|
477
|
+
uri: z.string().url().max(4_000).nullable().transform((value) => value ?? undefined),
|
|
478
|
+
digest: z.string().trim().min(1).max(500).nullable().transform((value) => value ?? undefined),
|
|
479
|
+
}).strict()).max(100),
|
|
480
|
+
head_commit: z.string().regex(/^[0-9a-f]{7,64}$/i).nullable().transform((value) => value ?? undefined),
|
|
481
|
+
pull_request_url: z.string().url().max(4_000).nullable().transform((value) => value ?? undefined),
|
|
482
|
+
});
|
|
483
|
+
/** Parse the agent's final fenced JSON block or its native structured output into a bounded report. */
|
|
484
|
+
export function parseAgentReport(text, acceptance, structuredOutput) {
|
|
485
|
+
const data = structuredOutput === undefined
|
|
486
|
+
? parseReplyObject("report_invalid", agentReportSchema, text)
|
|
487
|
+
: parseReplyObject("report_invalid", agentReportOutputSchema, text, structuredOutput);
|
|
508
488
|
// Map every criterion reference — in the results and in the evidence — back to the approved text
|
|
509
489
|
// before anything else reads it. Downstream (the packet, the reviewer, Today) sees text only; the
|
|
510
490
|
// key is transport. An unknown key is refused here, because a key the agent invented cannot be
|
|
@@ -518,8 +498,8 @@ export function parseAgentReport(text, acceptance) {
|
|
|
518
498
|
unknownKeys.push(value.trim());
|
|
519
499
|
return value;
|
|
520
500
|
};
|
|
521
|
-
const acceptanceResults =
|
|
522
|
-
const evidence =
|
|
501
|
+
const acceptanceResults = data.acceptance_results.map((item) => ({ ...item, criterion: toApprovedText(item.criterion) }));
|
|
502
|
+
const evidence = data.evidence.map((item) => ({ ...item, acceptance_criteria: item.acceptance_criteria.map(toApprovedText) }));
|
|
523
503
|
if (unknownKeys.length) {
|
|
524
504
|
const valid = acceptance.length
|
|
525
505
|
? acceptance.map((criterion, index) => `${criterionKeyLabel(index)} = ${quoteCriterion(criterion)}`).join("; ")
|
|
@@ -550,7 +530,7 @@ export function parseAgentReport(text, acceptance) {
|
|
|
550
530
|
// completed work instead of throwing the whole implementation away.
|
|
551
531
|
const mappedCriteria = new Set(evidence.flatMap((item) => item.acceptance_criteria.map(criterionKey)));
|
|
552
532
|
return {
|
|
553
|
-
...
|
|
533
|
+
...data,
|
|
554
534
|
evidence,
|
|
555
535
|
acceptance_results: acceptanceResults.map((item) => {
|
|
556
536
|
const { unverified_reason: reported, unverified_detail: detail, ...rest } = item;
|
|
@@ -588,86 +568,6 @@ export function normalizeEvidenceDigest(digest) {
|
|
|
588
568
|
return `sha256:${trimmed.toLowerCase()}`;
|
|
589
569
|
return trimmed;
|
|
590
570
|
}
|
|
591
|
-
/**
|
|
592
|
-
* Tolerate benign agent-output noise before strict validation: drop empty/whitespace-only entries from
|
|
593
|
-
* string arrays and remove empty optional scalars, so a single stray "" does not fail an otherwise-valid
|
|
594
|
-
* delivery. Required non-empty scalars (outcome, evidence.name, criterion) stay strict — they cannot be
|
|
595
|
-
* fabricated. Mutates `raw` in place.
|
|
596
|
-
*/
|
|
597
|
-
function sanitizeReportShape(raw) {
|
|
598
|
-
if (!raw || typeof raw !== "object")
|
|
599
|
-
return;
|
|
600
|
-
const obj = raw;
|
|
601
|
-
const cleanList = (value) => Array.isArray(value)
|
|
602
|
-
? value.filter((entry) => typeof entry === "string" && entry.trim().length > 0).map((entry) => entry.trim())
|
|
603
|
-
: value;
|
|
604
|
-
for (const key of ["changes", "verification", "assumptions", "risks", "limitations"]) {
|
|
605
|
-
if (key in obj)
|
|
606
|
-
obj[key] = cleanList(obj[key]);
|
|
607
|
-
}
|
|
608
|
-
if (Array.isArray(obj.evidence)) {
|
|
609
|
-
for (const item of obj.evidence) {
|
|
610
|
-
if (!item || typeof item !== "object")
|
|
611
|
-
continue;
|
|
612
|
-
const evidence = item;
|
|
613
|
-
if ("details" in evidence)
|
|
614
|
-
evidence.details = cleanList(evidence.details);
|
|
615
|
-
// Experimental lanes have repeatedly omitted this list on otherwise-useful test evidence.
|
|
616
|
-
// An empty mapping asserts nothing; later logic downgrades any now-unsupported "met" result.
|
|
617
|
-
if (!("acceptance_criteria" in evidence) || evidence.acceptance_criteria == null)
|
|
618
|
-
evidence.acceptance_criteria = [];
|
|
619
|
-
else
|
|
620
|
-
evidence.acceptance_criteria = cleanList(evidence.acceptance_criteria);
|
|
621
|
-
// Agents emit null/"" for optional fields they have no value for — treat as absent.
|
|
622
|
-
if ("digest" in evidence && (typeof evidence.digest !== "string" || evidence.digest.trim() === ""))
|
|
623
|
-
delete evidence.digest;
|
|
624
|
-
else if (typeof evidence.digest === "string")
|
|
625
|
-
evidence.digest = normalizeEvidenceDigest(evidence.digest);
|
|
626
|
-
// Drop non-URL / empty uris so an optional bad uri doesn't fail the whole report.
|
|
627
|
-
if ("uri" in evidence && (typeof evidence.uri !== "string" || !z.string().url().max(4_000).safeParse(evidence.uri).success))
|
|
628
|
-
delete evidence.uri;
|
|
629
|
-
// Witness is Bridge-authored provenance. An agent claiming it must not survive parse.
|
|
630
|
-
if ("witness" in evidence)
|
|
631
|
-
delete evidence.witness;
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
// Same rule for optional top-level scalars: null/"" means absent, not invalid.
|
|
635
|
-
for (const key of ["head_commit", "pull_request_url"]) {
|
|
636
|
-
if (key in obj && (typeof obj[key] !== "string" || obj[key].trim() === ""))
|
|
637
|
-
delete obj[key];
|
|
638
|
-
}
|
|
639
|
-
// And the same rule for keys Conduit does not model at all. An unknown key carrying a real value
|
|
640
|
-
// still fails validation below — it may be the agent asserting something outside the contract, and
|
|
641
|
-
// silently dropping that would hide it. An empty one is only noise, and discarding an otherwise
|
|
642
|
-
// valid delivery over it cost a full staging canary on 2026-07-22 ("assumptions_note": null).
|
|
643
|
-
for (const key of Object.keys(obj)) {
|
|
644
|
-
if (!reportKeys.has(key) && isSemanticallyEmpty(obj[key]))
|
|
645
|
-
delete obj[key];
|
|
646
|
-
}
|
|
647
|
-
}
|
|
648
|
-
/** Top-level keys the delivery contract models. Keep in step with the schema in parseAgentReport. */
|
|
649
|
-
const reportKeys = new Set([
|
|
650
|
-
"outcome", "changes", "verification", "acceptance_results", "evidence",
|
|
651
|
-
"assumptions", "risks", "limitations", "head_commit", "pull_request_url",
|
|
652
|
-
]);
|
|
653
|
-
/**
|
|
654
|
-
* Words agents write when a field has no value. Observed live: after `"assumptions_note": null` was
|
|
655
|
-
* handled, the very next real run emitted `"assumptions_note": "none"` and failed the same way.
|
|
656
|
-
* These carry no claim, so an unknown key holding one is noise rather than an assertion.
|
|
657
|
-
*/
|
|
658
|
-
const noValueWords = new Set(["none", "n/a", "na", "nil", "null", "nothing", "not applicable", "-", "—"]);
|
|
659
|
-
/** Absent-in-practice: null, undefined, blank/no-value string, or an empty array/object. `false` and `0` are values. */
|
|
660
|
-
function isSemanticallyEmpty(value) {
|
|
661
|
-
if (value === null || value === undefined)
|
|
662
|
-
return true;
|
|
663
|
-
if (typeof value === "string")
|
|
664
|
-
return value.trim() === "" || noValueWords.has(value.trim().toLowerCase());
|
|
665
|
-
if (Array.isArray(value))
|
|
666
|
-
return value.length === 0;
|
|
667
|
-
if (typeof value === "object")
|
|
668
|
-
return Object.keys(value).length === 0;
|
|
669
|
-
return false;
|
|
670
|
-
}
|
|
671
571
|
/** A count the vendor printed, or undefined when the envelope holds no usable number. */
|
|
672
572
|
function usageNumber(value) {
|
|
673
573
|
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : undefined;
|
|
@@ -745,6 +645,8 @@ export const claudeCodeDriver = {
|
|
|
745
645
|
// The prompt goes on stdin (`claude -p` reads it there when no prompt argument is given): Linux
|
|
746
646
|
// caps one argument at 128 KiB (MAX_ARG_STRLEN) and an assignment contract exceeds that.
|
|
747
647
|
const args = ["-p", "--output-format", "json", "--max-turns", String(maxTurns)];
|
|
648
|
+
if (input.outputSchemaPath)
|
|
649
|
+
args.push("--json-schema", await readFile(input.outputSchemaPath, "utf8"));
|
|
748
650
|
args.push(...modelArgs(claudeCodeDriver, input.model));
|
|
749
651
|
if (input.resumeSessionId)
|
|
750
652
|
args.push("--resume", input.resumeSessionId);
|
|
@@ -777,7 +679,10 @@ export const claudeCodeDriver = {
|
|
|
777
679
|
if (code !== 0 || !message || message.is_error) {
|
|
778
680
|
return { status: "failed", resultText: message?.result ?? null, sessionId, usage, signal: agentExitSignal({ exit_code: code, stderr, stdout, model: input.model }), error: boundedTail(message?.result || stderr || `agent exited with code ${code}`, 20_000) };
|
|
779
681
|
}
|
|
780
|
-
return {
|
|
682
|
+
return {
|
|
683
|
+
status: "completed", resultText: message.result ?? "", sessionId, usage,
|
|
684
|
+
...(input.outputSchemaPath ? { structuredOutput: message.structured_output ?? null } : {}),
|
|
685
|
+
};
|
|
781
686
|
},
|
|
782
687
|
};
|
|
783
688
|
/**
|
|
@@ -803,6 +708,8 @@ export function codexExecArgs(input, sandbox) {
|
|
|
803
708
|
? ["exec", "resume", "--json", "-c", `sandbox_mode="${sandbox}"`]
|
|
804
709
|
: ["exec", "--json", "--sandbox", sandbox];
|
|
805
710
|
args.push(...modelArgs(codexDriver, input.model));
|
|
711
|
+
if (input.outputSchemaPath)
|
|
712
|
+
args.push("--output-schema", input.outputSchemaPath);
|
|
806
713
|
const networkAccess = input.networkAccess
|
|
807
714
|
?? (sandbox === "workspace-write" && input.capabilities?.includes("external_network"));
|
|
808
715
|
if (sandbox === "workspace-write" && networkAccess) {
|
|
@@ -916,7 +823,17 @@ export const codexDriver = {
|
|
|
916
823
|
if (code !== 0) {
|
|
917
824
|
return { status: "failed", resultText, sessionId: parsed.sessionId, usage: parsed.usage, signal: agentExitSignal({ exit_code: code, stderr, stdout, model: input.model }), error: boundedTail(stderr || stdout || `codex exited with code ${code}`, 20_000) };
|
|
918
825
|
}
|
|
919
|
-
|
|
826
|
+
let structuredOutput = null;
|
|
827
|
+
if (input.outputSchemaPath && parsed.resultText !== null) {
|
|
828
|
+
try {
|
|
829
|
+
structuredOutput = JSON.parse(parsed.resultText);
|
|
830
|
+
}
|
|
831
|
+
catch { /* Validation refuses null. */ }
|
|
832
|
+
}
|
|
833
|
+
return {
|
|
834
|
+
status: "completed", resultText, sessionId: parsed.sessionId, usage: parsed.usage,
|
|
835
|
+
...(input.outputSchemaPath ? { structuredOutput } : {}),
|
|
836
|
+
};
|
|
920
837
|
},
|
|
921
838
|
};
|
|
922
839
|
/**
|
|
@@ -2055,8 +1972,10 @@ export function boundedEnvironment(fuel, fuelSource = "conduit", extraEnv) {
|
|
|
2055
1972
|
"HTTP_PROXY", "HTTPS_PROXY", "NO_PROXY", "ALL_PROXY", "http_proxy", "https_proxy", "no_proxy", "SSL_CERT_FILE", "NODE_EXTRA_CA_CERTS",
|
|
2056
1973
|
// The machine's Rust toolchain and build cache. Without CARGO_TARGET_DIR an agent compiled every
|
|
2057
1974
|
// dependency cold in its worktree, so no `cargo test` it started finished (acme-sim SV1b, 2026-10-10);
|
|
2058
|
-
// CARGO_PROFILE_*
|
|
2059
|
-
|
|
1975
|
+
// CARGO_PROFILE_* and CARGO_INCREMENTAL keep its build identical to the gate's, so the gate reuses
|
|
1976
|
+
// what the agent built; an incremental agent build next to a non-incremental gate build in one
|
|
1977
|
+
// target dir left mixed objects that failed to link (acme-sim, 2026-10-15 and 2026-10-18).
|
|
1978
|
+
"CARGO_HOME", "RUSTUP_HOME", "CARGO_TARGET_DIR", "CARGO_INCREMENTAL",
|
|
2060
1979
|
...Object.keys(process.env).filter((name) => name.startsWith("CARGO_PROFILE_")),
|
|
2061
1980
|
...(fuelSource === "local" ? LOCAL_VENDOR_ENV : [])];
|
|
2062
1981
|
const env = Object.fromEntries(allowed.flatMap((name) => process.env[name] === undefined ? [] : [[name, process.env[name]]]));
|