@gencode/uni 0.4.0 → 0.4.2
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/CHANGELOG.md +16 -0
- package/README.md +9 -0
- package/dist/shared/src/agent-progress.d.ts +2 -0
- package/dist/shared/src/agent-progress.d.ts.map +1 -1
- package/dist/shared/src/agent-progress.js.map +1 -1
- package/dist/shared/src/artifacts.d.ts +46 -0
- package/dist/shared/src/artifacts.d.ts.map +1 -0
- package/dist/shared/src/artifacts.js +68 -0
- package/dist/shared/src/artifacts.js.map +1 -0
- package/dist/shared/src/index.d.ts +5 -1
- package/dist/shared/src/index.d.ts.map +1 -1
- package/dist/shared/src/index.js +2 -0
- package/dist/shared/src/index.js.map +1 -1
- package/dist/shared/src/json-value.d.ts +11 -0
- package/dist/shared/src/json-value.d.ts.map +1 -0
- package/dist/shared/src/json-value.js +67 -0
- package/dist/shared/src/json-value.js.map +1 -0
- package/dist/shared/src/run-events.d.ts +10 -14
- package/dist/shared/src/run-events.d.ts.map +1 -1
- package/dist/shared/src/session.d.ts +2 -0
- package/dist/shared/src/session.d.ts.map +1 -1
- package/dist/uni/src/mcp/artifact-server.d.ts +10 -0
- package/dist/uni/src/mcp/artifact-server.d.ts.map +1 -0
- package/dist/uni/src/mcp/artifact-server.js +186 -0
- package/dist/uni/src/mcp/artifact-server.js.map +1 -0
- package/dist/uni/src/mcp/pi-artifact-extension.d.ts +4 -0
- package/dist/uni/src/mcp/pi-artifact-extension.d.ts.map +1 -0
- package/dist/uni/src/mcp/pi-artifact-extension.js +52 -0
- package/dist/uni/src/mcp/pi-artifact-extension.js.map +1 -0
- package/dist/uni/src/providers/claude-code.d.ts.map +1 -1
- package/dist/uni/src/providers/claude-code.js +23 -2
- package/dist/uni/src/providers/claude-code.js.map +1 -1
- package/dist/uni/src/providers/codex.d.ts.map +1 -1
- package/dist/uni/src/providers/codex.js +4 -0
- package/dist/uni/src/providers/codex.js.map +1 -1
- package/dist/uni/src/providers/opencode.d.ts.map +1 -1
- package/dist/uni/src/providers/opencode.js +36 -0
- package/dist/uni/src/providers/opencode.js.map +1 -1
- package/dist/uni/src/providers/pi.d.ts.map +1 -1
- package/dist/uni/src/providers/pi.js +7 -0
- package/dist/uni/src/providers/pi.js.map +1 -1
- package/dist/uni/src/run.d.ts.map +1 -1
- package/dist/uni/src/run.js +87 -228
- package/dist/uni/src/run.js.map +1 -1
- package/dist/uni/src/session/artifact-store.d.ts +15 -0
- package/dist/uni/src/session/artifact-store.d.ts.map +1 -0
- package/dist/uni/src/session/artifact-store.js +177 -0
- package/dist/uni/src/session/artifact-store.js.map +1 -0
- package/dist/uni/src/session/change-operation-store.d.ts +66 -0
- package/dist/uni/src/session/change-operation-store.d.ts.map +1 -0
- package/dist/uni/src/session/change-operation-store.js +370 -0
- package/dist/uni/src/session/change-operation-store.js.map +1 -0
- package/dist/uni/src/session/session-store.d.ts +1 -2
- package/dist/uni/src/session/session-store.d.ts.map +1 -1
- package/dist/uni/src/session/session-store.js.map +1 -1
- package/dist/uni/src/transcript/turn-accumulator.d.ts +0 -2
- package/dist/uni/src/transcript/turn-accumulator.d.ts.map +1 -1
- package/dist/uni/src/transcript/turn-accumulator.js +0 -1
- package/dist/uni/src/transcript/turn-accumulator.js.map +1 -1
- package/dist/uni/src/types.d.ts +5 -1
- package/dist/uni/src/types.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/uni/src/run.js
CHANGED
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { isAgentDiagnosticEvent } from "@gencode/shared";
|
|
4
|
-
import { ArtifactAccumulator } from "./artifacts/accumulator.js";
|
|
5
|
-
import { ARTIFACTS_START_TAG, parseAndStripArtifacts, } from "./artifacts/parser.js";
|
|
6
|
-
import { ArtifactStreamFilter } from "./artifacts/stream-filter.js";
|
|
7
|
-
import { inferArtifactsFromToolExecution } from "./artifacts/tool-inference.js";
|
|
8
4
|
import { generateApiKey } from "./auth/api-key.js";
|
|
9
5
|
import { normalizeProviderEvent } from "./events/normalizer.js";
|
|
10
6
|
import { UniHookDispatcher } from "./hooks.js";
|
|
11
7
|
import { providerStateDir, providerStateRoot } from "./providers/provider-state.js";
|
|
12
8
|
import { createBuiltInProviderRegistry } from "./providers/registry.js";
|
|
13
|
-
import {
|
|
9
|
+
import { createChangeOperationRecorder } from "./session/change-operation-store.js";
|
|
10
|
+
import { clearArtifactPersistenceFailure, loadArtifactPersistenceFailure, } from "./mcp/artifact-server.js";
|
|
14
11
|
import { createUniContextStore, } from "./session/context-store.js";
|
|
15
12
|
import { resolveRunInput } from "./session/input.js";
|
|
16
13
|
import { initializeUniLogger, uniLogger } from "./session/logger.js";
|
|
@@ -29,18 +26,39 @@ const ALIGNED_RUNTIME_AGENTS = new Set([
|
|
|
29
26
|
"opencode",
|
|
30
27
|
"pi",
|
|
31
28
|
]);
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
29
|
+
function artifactToolPrompt(runtimeAgent) {
|
|
30
|
+
const toolName = runtimeAgent === "pi"
|
|
31
|
+
? "record_artifacts"
|
|
32
|
+
: runtimeAgent === "opencode"
|
|
33
|
+
? "aimax_record_artifacts"
|
|
34
|
+
: "mcp__aimax__record_artifacts";
|
|
35
|
+
return [
|
|
36
|
+
"## Mandatory Current-Run Finalization Checkpoint",
|
|
37
|
+
`The final-artifact tool exposed by this provider is \`${toolName}\`.`,
|
|
38
|
+
"This instruction applies to the CURRENT user request, including every follow-up, resume, retry, or new turn in an existing Session.",
|
|
39
|
+
"For THIS run, call the final-artifact tool exactly once after all generation, modification, export, publication, download, upload, and verification work is complete.",
|
|
40
|
+
"A final-artifact tool call visible in earlier conversation history is already consumed by that earlier run and never satisfies THIS run.",
|
|
41
|
+
"Before calling it, audit the entire run and submit every final user-deliverable file and URL in one call.",
|
|
42
|
+
"The artifact tool must be the final tool call. After it succeeds, immediately provide the final answer and call no other tool.",
|
|
43
|
+
"If this run produced no final artifacts, still call it with `{ \"artifacts\": [] }`.",
|
|
44
|
+
"Do not provide the final answer until THIS run's fresh final-artifact tool call has completed.",
|
|
45
|
+
"A final artifact must have been created, generated, exported, downloaded, published, or materially modified in this run specifically for the user. It must be part of the requested result, remain valid when the task ends, be something the user will directly open/use, and not be an intermediate output.",
|
|
46
|
+
"Record by default:",
|
|
47
|
+
"- Final PDF, DOCX, XLSX, PPTX, images, audio, video, reports, data exports, archives, installers, and release packages.",
|
|
48
|
+
"- Standalone scripts, examples, or projects explicitly requested as deliverables.",
|
|
49
|
+
"- Files downloaded into the workspace for delivery to the user.",
|
|
50
|
+
"- User-openable online documents, Wiki pages, published pages, share links, and download links.",
|
|
51
|
+
"- Modified source files only when the user explicitly requested those files themselves as deliverables.",
|
|
52
|
+
"- Child-agent deliverables only when the root run adopts them as part of the final result.",
|
|
53
|
+
"Do not record by default:",
|
|
54
|
+
"- Ordinary repository code changes, unit-test changes, configuration files, lockfiles, changesets, or routine engineering-documentation edits.",
|
|
55
|
+
"- Temporary files, caches, logs, test stdout/stderr, build intermediates, node_modules, or dependency caches.",
|
|
56
|
+
"- Files that were only read, or outputs that were deleted, failed, rolled back, replaced, or are no longer valid.",
|
|
57
|
+
"- AIMax internal state, session files, memory files, context snapshots, or tool-result spill files.",
|
|
58
|
+
"Decision test: without looking at the git diff, is this file or URL itself something the user asked to take away, open, or use? If not, do not record it.",
|
|
59
|
+
"Final reminder: an earlier call never counts. THIS run must make its own fresh call, including when resuming the same Session.",
|
|
60
|
+
].join("\n");
|
|
61
|
+
}
|
|
44
62
|
export const UNSUPPORTED_UNI_RUN_OPTION_MESSAGES = {
|
|
45
63
|
agent: "--agent is not supported by @gencode/uni yet",
|
|
46
64
|
skillsLoadPaths: "--skillsLoadPaths is not supported by @gencode/uni yet",
|
|
@@ -274,21 +292,22 @@ function promptText(input) {
|
|
|
274
292
|
return input.message;
|
|
275
293
|
return input.singleUserText ?? JSON.stringify(input.messages);
|
|
276
294
|
}
|
|
277
|
-
function providerInput(input, aligned) {
|
|
295
|
+
function providerInput(input, aligned, runtimeAgent) {
|
|
278
296
|
if (!aligned)
|
|
279
297
|
return input;
|
|
298
|
+
const prompt = artifactToolPrompt(runtimeAgent);
|
|
280
299
|
if (input.kind === "text") {
|
|
281
|
-
return { kind: "text", message: `${input.message}\n\n${
|
|
300
|
+
return { kind: "text", message: `${input.message}\n\n${prompt}` };
|
|
282
301
|
}
|
|
283
302
|
const messages = [
|
|
284
303
|
...input.messages,
|
|
285
|
-
{ role: "user", content:
|
|
304
|
+
{ role: "user", content: prompt },
|
|
286
305
|
];
|
|
287
306
|
return {
|
|
288
307
|
kind: "messages",
|
|
289
308
|
messages,
|
|
290
309
|
...(input.singleUserText
|
|
291
|
-
? { singleUserText: `${input.singleUserText}\n\n${
|
|
310
|
+
? { singleUserText: `${input.singleUserText}\n\n${prompt}` }
|
|
292
311
|
: {}),
|
|
293
312
|
};
|
|
294
313
|
}
|
|
@@ -444,46 +463,6 @@ function firstString(record, keys) {
|
|
|
444
463
|
}
|
|
445
464
|
return undefined;
|
|
446
465
|
}
|
|
447
|
-
function patchSummary(input) {
|
|
448
|
-
const added = [];
|
|
449
|
-
const modified = [];
|
|
450
|
-
const deleted = [];
|
|
451
|
-
const changes = input.changes;
|
|
452
|
-
if (Array.isArray(changes)) {
|
|
453
|
-
for (const raw of changes) {
|
|
454
|
-
if (!raw || typeof raw !== "object")
|
|
455
|
-
continue;
|
|
456
|
-
const change = raw;
|
|
457
|
-
const file = firstString(change, ["path", "file", "file_path"]);
|
|
458
|
-
if (!file)
|
|
459
|
-
continue;
|
|
460
|
-
const kind = firstString(change, ["kind", "type", "operation"])?.toLowerCase();
|
|
461
|
-
if (kind === "add" || kind === "create")
|
|
462
|
-
added.push(file);
|
|
463
|
-
else if (kind === "delete" || kind === "remove")
|
|
464
|
-
deleted.push(file);
|
|
465
|
-
else
|
|
466
|
-
modified.push(file);
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
const patch = firstString(input, ["patch", "input"]);
|
|
470
|
-
if (patch) {
|
|
471
|
-
for (const line of patch.split(/\r?\n/)) {
|
|
472
|
-
const match = line.match(/^\*\*\* (Add|Update|Delete) File:\s*(.+)$/);
|
|
473
|
-
if (!match?.[2])
|
|
474
|
-
continue;
|
|
475
|
-
if (match[1] === "Add")
|
|
476
|
-
added.push(match[2].trim());
|
|
477
|
-
else if (match[1] === "Delete")
|
|
478
|
-
deleted.push(match[2].trim());
|
|
479
|
-
else
|
|
480
|
-
modified.push(match[2].trim());
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
if (added.length + modified.length + deleted.length === 0)
|
|
484
|
-
return undefined;
|
|
485
|
-
return { added, modified, deleted };
|
|
486
|
-
}
|
|
487
466
|
function structuredFileChangesPatch(input) {
|
|
488
467
|
const changes = toRecord(input).changes;
|
|
489
468
|
if (!Array.isArray(changes))
|
|
@@ -512,19 +491,19 @@ function structuredFileChangesPatch(input) {
|
|
|
512
491
|
markers.push("*** End Patch");
|
|
513
492
|
return markers.length > 2 ? markers.join("\n") : undefined;
|
|
514
493
|
}
|
|
515
|
-
function
|
|
494
|
+
function changeAuditStartEvent(event) {
|
|
516
495
|
if (canonicalToolName(event.name) !== "apply_patch")
|
|
517
496
|
return event;
|
|
518
497
|
const patch = structuredFileChangesPatch(event.input);
|
|
519
498
|
return patch ? { ...event, name: "apply_patch", input: { patch } } : event;
|
|
520
499
|
}
|
|
521
|
-
function
|
|
500
|
+
function changeAuditEndEvent(event) {
|
|
522
501
|
if (canonicalToolName(event.name) !== "apply_patch")
|
|
523
502
|
return event;
|
|
524
503
|
const patch = structuredFileChangesPatch(event.input);
|
|
525
504
|
return patch ? { ...event, name: "apply_patch", input: { patch } } : event;
|
|
526
505
|
}
|
|
527
|
-
function
|
|
506
|
+
function changeAuditFileOperation(event) {
|
|
528
507
|
const name = canonicalToolName(event.name);
|
|
529
508
|
if (name !== "apply_patch")
|
|
530
509
|
return { ...event, name };
|
|
@@ -535,94 +514,6 @@ function artifactAuditFileOperation(event) {
|
|
|
535
514
|
...(patch ? { input: { patch } } : {}),
|
|
536
515
|
};
|
|
537
516
|
}
|
|
538
|
-
function inferenceResultForTool(params) {
|
|
539
|
-
const input = toRecord(params.input);
|
|
540
|
-
const name = canonicalToolName(params.name);
|
|
541
|
-
const content = [{ type: "text", text: params.output }];
|
|
542
|
-
if (name === "write_file") {
|
|
543
|
-
return { content, details: { path: firstString(input, ["path", "file_path", "filePath"]) } };
|
|
544
|
-
}
|
|
545
|
-
if (name === "edit_file") {
|
|
546
|
-
return {
|
|
547
|
-
content,
|
|
548
|
-
details: {
|
|
549
|
-
path: firstString(input, ["path", "file_path", "filePath"]),
|
|
550
|
-
occurrences: params.isError ? 0 : 1,
|
|
551
|
-
},
|
|
552
|
-
};
|
|
553
|
-
}
|
|
554
|
-
if (name === "apply_patch")
|
|
555
|
-
return { content, details: { summary: patchSummary(input) } };
|
|
556
|
-
if (name === "exec" || name === "bash" || name === "process") {
|
|
557
|
-
return {
|
|
558
|
-
content,
|
|
559
|
-
details: {
|
|
560
|
-
status: params.isError ? "failed" : "completed",
|
|
561
|
-
cwd: firstString(input, ["cwd"]) ?? undefined,
|
|
562
|
-
},
|
|
563
|
-
};
|
|
564
|
-
}
|
|
565
|
-
return params.output;
|
|
566
|
-
}
|
|
567
|
-
function inferenceResult(event) {
|
|
568
|
-
return inferenceResultForTool(event);
|
|
569
|
-
}
|
|
570
|
-
function isInternalArtifact(artifact, dataDir) {
|
|
571
|
-
if (artifact.kind !== "file")
|
|
572
|
-
return false;
|
|
573
|
-
const internalRoot = path.resolve(dataDir, ".aimax");
|
|
574
|
-
const candidate = path.resolve(artifact.file);
|
|
575
|
-
return candidate === internalRoot || candidate.startsWith(`${internalRoot}${path.sep}`);
|
|
576
|
-
}
|
|
577
|
-
function parseArtifactArray(value, workspaceDir) {
|
|
578
|
-
if (!Array.isArray(value))
|
|
579
|
-
return [];
|
|
580
|
-
const now = new Date().toISOString();
|
|
581
|
-
return value.flatMap((raw) => {
|
|
582
|
-
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
583
|
-
return [];
|
|
584
|
-
const record = raw;
|
|
585
|
-
const label = typeof record.label === "string" && record.label.trim()
|
|
586
|
-
? record.label.trim()
|
|
587
|
-
: undefined;
|
|
588
|
-
if (record.kind === "file" && typeof record.file === "string" && record.file.trim() && label) {
|
|
589
|
-
const file = path.isAbsolute(record.file) ? path.normalize(record.file) : path.resolve(workspaceDir, record.file);
|
|
590
|
-
return [{ kind: "file", file, label, timestamp: now }];
|
|
591
|
-
}
|
|
592
|
-
if (record.kind === "url" && typeof record.url === "string" && record.url.trim() && label) {
|
|
593
|
-
try {
|
|
594
|
-
const url = new URL(record.url).toString();
|
|
595
|
-
return [{ kind: "url", url, label, timestamp: now }];
|
|
596
|
-
}
|
|
597
|
-
catch {
|
|
598
|
-
return [];
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
|
-
return [];
|
|
602
|
-
});
|
|
603
|
-
}
|
|
604
|
-
function extractChildArtifacts(toolName, output, workspaceDir) {
|
|
605
|
-
const normalized = toolName.trim().toLowerCase().replaceAll("-", "_");
|
|
606
|
-
if (!normalized.includes("subagent") && normalized !== "task" && normalized !== "batch")
|
|
607
|
-
return [];
|
|
608
|
-
let parsed;
|
|
609
|
-
try {
|
|
610
|
-
parsed = JSON.parse(output);
|
|
611
|
-
}
|
|
612
|
-
catch {
|
|
613
|
-
return [];
|
|
614
|
-
}
|
|
615
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
616
|
-
return [];
|
|
617
|
-
const record = parsed;
|
|
618
|
-
const direct = parseArtifactArray(record.artifacts, workspaceDir);
|
|
619
|
-
if (direct.length > 0)
|
|
620
|
-
return direct;
|
|
621
|
-
const result = record.result;
|
|
622
|
-
return result && typeof result === "object" && !Array.isArray(result)
|
|
623
|
-
? parseArtifactArray(result.artifacts, workspaceDir)
|
|
624
|
-
: [];
|
|
625
|
-
}
|
|
626
517
|
export async function runUni(options) {
|
|
627
518
|
const config = await resolveRunConfig(options);
|
|
628
519
|
initializeUniLogger(config.dataDir);
|
|
@@ -691,13 +582,28 @@ export async function runUni(options) {
|
|
|
691
582
|
runtimeAgent: config.runtimeAgent,
|
|
692
583
|
messageId: config.messageId,
|
|
693
584
|
});
|
|
694
|
-
const
|
|
695
|
-
const
|
|
696
|
-
?
|
|
585
|
+
const workspaceDir = config.projectDir ?? path.join(config.dataDir, "workspace");
|
|
586
|
+
const artifactStoreOptions = aligned
|
|
587
|
+
? {
|
|
588
|
+
dataDir: config.dataDir,
|
|
589
|
+
logicalSessionId: config.sessionId,
|
|
590
|
+
storeName: config.sessionStore,
|
|
591
|
+
workspaceDir,
|
|
592
|
+
projectDir: config.projectDir,
|
|
593
|
+
urlWhitelist: config.artifactsUrlWhitelist,
|
|
594
|
+
}
|
|
595
|
+
: undefined;
|
|
596
|
+
if (artifactStoreOptions) {
|
|
597
|
+
await clearArtifactPersistenceFailure(artifactStoreOptions);
|
|
598
|
+
}
|
|
599
|
+
const artifactTool = artifactStoreOptions
|
|
600
|
+
? {
|
|
601
|
+
configBase64: Buffer.from(JSON.stringify(artifactStoreOptions), "utf8")
|
|
602
|
+
.toString("base64url"),
|
|
603
|
+
}
|
|
697
604
|
: undefined;
|
|
698
|
-
const workspaceDir = config.projectDir ?? config.dataDir;
|
|
699
605
|
const operationRecorder = aligned && config.channel !== "CRON"
|
|
700
|
-
?
|
|
606
|
+
? createChangeOperationRecorder({
|
|
701
607
|
dataDir: config.dataDir,
|
|
702
608
|
logicalSessionId: config.sessionId,
|
|
703
609
|
storeName: config.sessionStore,
|
|
@@ -713,6 +619,7 @@ export async function runUni(options) {
|
|
|
713
619
|
let observedUsage;
|
|
714
620
|
let observedModel;
|
|
715
621
|
let observedError;
|
|
622
|
+
let artifactPersistenceFailure;
|
|
716
623
|
let observedTitle;
|
|
717
624
|
const emitProgress = async (event) => {
|
|
718
625
|
progressEvents.push(event);
|
|
@@ -741,9 +648,7 @@ export async function runUni(options) {
|
|
|
741
648
|
});
|
|
742
649
|
const emitVisibleText = async (type, text) => {
|
|
743
650
|
rawProviderText += text;
|
|
744
|
-
const visible =
|
|
745
|
-
if (!visible)
|
|
746
|
-
return;
|
|
651
|
+
const visible = text;
|
|
747
652
|
await flushCompletedTools();
|
|
748
653
|
turn.addText(visible);
|
|
749
654
|
const normalized = normalizeProviderEvent(config.sessionId, config.messageId, { type, text: visible });
|
|
@@ -803,8 +708,9 @@ export async function runUni(options) {
|
|
|
803
708
|
flashModel: config.flashModel,
|
|
804
709
|
timeoutMs: config.timeoutMs,
|
|
805
710
|
runtimeConfig: config.runtimeConfig,
|
|
806
|
-
input: providerInput(config.input, aligned),
|
|
711
|
+
input: providerInput(config.input, aligned, config.runtimeAgent),
|
|
807
712
|
signal: abortController.signal,
|
|
713
|
+
artifactTool,
|
|
808
714
|
}, async (providerEvent) => {
|
|
809
715
|
let eventForProgress = providerEvent;
|
|
810
716
|
if (providerEvent.type === "session_started") {
|
|
@@ -838,18 +744,9 @@ export async function runUni(options) {
|
|
|
838
744
|
return;
|
|
839
745
|
}
|
|
840
746
|
if (providerEvent.type === "file_operation") {
|
|
841
|
-
const auditEvent =
|
|
747
|
+
const auditEvent = changeAuditFileOperation(providerEvent);
|
|
842
748
|
await Promise.resolve(operationRecorder?.recordObservedFileOperation(auditEvent))
|
|
843
749
|
.catch(() => undefined);
|
|
844
|
-
if (!providerEvent.isError && artifactAccumulator) {
|
|
845
|
-
artifactAccumulator.addInferred(inferArtifactsFromToolExecution({
|
|
846
|
-
toolName: canonicalToolName(providerEvent.name),
|
|
847
|
-
input: toRecord(providerEvent.input),
|
|
848
|
-
result: inferenceResultForTool(providerEvent),
|
|
849
|
-
isError: false,
|
|
850
|
-
workspaceDir,
|
|
851
|
-
}).filter((artifact) => !isInternalArtifact(artifact, config.dataDir)));
|
|
852
|
-
}
|
|
853
750
|
return;
|
|
854
751
|
}
|
|
855
752
|
if (providerEvent.type === "tool_start") {
|
|
@@ -861,7 +758,7 @@ export async function runUni(options) {
|
|
|
861
758
|
startedAt: Date.now(),
|
|
862
759
|
});
|
|
863
760
|
turn.startTool(providerEvent);
|
|
864
|
-
await Promise.resolve(operationRecorder?.onToolStart(
|
|
761
|
+
await Promise.resolve(operationRecorder?.onToolStart(changeAuditStartEvent(providerEvent)))
|
|
865
762
|
.catch(() => undefined);
|
|
866
763
|
await hookDispatcher.dispatchBeforeToolCall({
|
|
867
764
|
toolCallId: providerEvent.toolCallId,
|
|
@@ -879,9 +776,14 @@ export async function runUni(options) {
|
|
|
879
776
|
input: active.params,
|
|
880
777
|
}
|
|
881
778
|
: providerEvent;
|
|
779
|
+
const normalizedToolName = toolEndEvent.name.toLowerCase();
|
|
780
|
+
if (toolEndEvent.isError
|
|
781
|
+
&& normalizedToolName.includes("record_artifacts")) {
|
|
782
|
+
artifactPersistenceFailure = toolEndEvent.output || "record_artifacts failed";
|
|
783
|
+
}
|
|
882
784
|
eventForProgress = toolEndEvent;
|
|
883
|
-
|
|
884
|
-
await Promise.resolve(operationRecorder?.onToolEnd(
|
|
785
|
+
turn.endTool(toolEndEvent);
|
|
786
|
+
await Promise.resolve(operationRecorder?.onToolEnd(changeAuditEndEvent(toolEndEvent)))
|
|
885
787
|
.catch(() => undefined);
|
|
886
788
|
if (config.runtimeAgent === "opencode"
|
|
887
789
|
&& !observedToolStartIds.has(toolEndEvent.toolCallId)
|
|
@@ -903,17 +805,6 @@ export async function runUni(options) {
|
|
|
903
805
|
}).catch((error) => {
|
|
904
806
|
uniLogger.warn("failed to persist observable tool result", { error: String(error) });
|
|
905
807
|
});
|
|
906
|
-
if (aligned && paired && !providerEvent.isError && artifactAccumulator) {
|
|
907
|
-
artifactAccumulator.addChild(extractChildArtifacts(toolEndEvent.name, toolEndEvent.output, workspaceDir)
|
|
908
|
-
.filter((artifact) => !isInternalArtifact(artifact, config.dataDir)));
|
|
909
|
-
artifactAccumulator.addInferred(inferArtifactsFromToolExecution({
|
|
910
|
-
toolName: canonicalToolName(toolEndEvent.name),
|
|
911
|
-
input: params,
|
|
912
|
-
result: inferenceResult(toolEndEvent),
|
|
913
|
-
isError: toolEndEvent.isError,
|
|
914
|
-
workspaceDir,
|
|
915
|
-
}).filter((artifact) => !isInternalArtifact(artifact, config.dataDir)));
|
|
916
|
-
}
|
|
917
808
|
await hookDispatcher.dispatchAfterToolCall({
|
|
918
809
|
toolCallId: providerEvent.toolCallId,
|
|
919
810
|
toolName: toolEndEvent.name,
|
|
@@ -957,47 +848,12 @@ export async function runUni(options) {
|
|
|
957
848
|
await bindProviderSession(providerResult.sessionId);
|
|
958
849
|
observedProviderSessionIds.add(providerResult.sessionId);
|
|
959
850
|
}
|
|
960
|
-
const filterTail = streamFilter?.finish() ?? "";
|
|
961
|
-
if (filterTail) {
|
|
962
|
-
await flushCompletedTools();
|
|
963
|
-
turn.addText(filterTail);
|
|
964
|
-
for (const event of normalizeProviderEvent(config.sessionId, config.messageId, {
|
|
965
|
-
type: "text_delta",
|
|
966
|
-
text: filterTail,
|
|
967
|
-
}))
|
|
968
|
-
await emitProgress(event);
|
|
969
|
-
}
|
|
970
851
|
const hasExplicitTerminalText = providerResult.text !== undefined || observedDoneText !== undefined;
|
|
971
852
|
const rawProviderFinalText = providerResult.text ?? observedDoneText ?? rawProviderText;
|
|
972
853
|
const providerFinalText = aligned
|
|
973
854
|
? redactKnownProviderIds(rawProviderFinalText, observedProviderSessionIds)
|
|
974
855
|
: rawProviderFinalText;
|
|
975
|
-
|
|
976
|
-
? parseAndStripArtifacts(providerFinalText, { workspaceDir })
|
|
977
|
-
: { cleanedText: providerFinalText, artifacts: [], warnings: [] };
|
|
978
|
-
const streamedArtifactParse = aligned
|
|
979
|
-
&& rawProviderText !== providerFinalText
|
|
980
|
-
&& rawProviderText.includes(ARTIFACTS_START_TAG)
|
|
981
|
-
? parseAndStripArtifacts(rawProviderText, { workspaceDir })
|
|
982
|
-
: undefined;
|
|
983
|
-
artifactAccumulator?.addOwn(parsedArtifacts.artifacts.filter((artifact) => !isInternalArtifact(artifact, config.dataDir)));
|
|
984
|
-
artifactAccumulator?.addOwn((streamedArtifactParse?.artifacts ?? [])
|
|
985
|
-
.filter((artifact) => !isInternalArtifact(artifact, config.dataDir)));
|
|
986
|
-
for (const warning of new Set([
|
|
987
|
-
...parsedArtifacts.warnings,
|
|
988
|
-
...(streamedArtifactParse?.warnings ?? []),
|
|
989
|
-
])) {
|
|
990
|
-
await emitProgress({
|
|
991
|
-
type: "diagnostic",
|
|
992
|
-
sessionId: config.sessionId,
|
|
993
|
-
messageId: config.messageId,
|
|
994
|
-
level: "warn",
|
|
995
|
-
scope: "runner",
|
|
996
|
-
phase: "artifact_declaration",
|
|
997
|
-
message: warning,
|
|
998
|
-
});
|
|
999
|
-
}
|
|
1000
|
-
if (rawProviderText && state.finalText !== parsedArtifacts.cleanedText) {
|
|
856
|
+
if (rawProviderText && state.finalText !== providerFinalText) {
|
|
1001
857
|
await emitProgress({
|
|
1002
858
|
type: "diagnostic",
|
|
1003
859
|
sessionId: config.sessionId,
|
|
@@ -1010,12 +866,17 @@ export async function runUni(options) {
|
|
|
1010
866
|
}
|
|
1011
867
|
const usage = providerResult.usage ?? observedUsage;
|
|
1012
868
|
const rawError = providerResult.error ?? observedError;
|
|
1013
|
-
const
|
|
869
|
+
const providerError = rawError
|
|
1014
870
|
? redactKnownProviderIds(rawError, observedProviderSessionIds)
|
|
1015
871
|
: undefined;
|
|
872
|
+
artifactPersistenceFailure ??= artifactStoreOptions
|
|
873
|
+
? await loadArtifactPersistenceFailure(artifactStoreOptions)
|
|
874
|
+
: undefined;
|
|
875
|
+
const error = artifactPersistenceFailure
|
|
876
|
+
? `Artifact persistence failed: ${artifactPersistenceFailure}`
|
|
877
|
+
: providerError;
|
|
1016
878
|
const durationMs = Date.now() - startedAt;
|
|
1017
|
-
|
|
1018
|
-
state.finalText = parsedArtifacts.cleanedText;
|
|
879
|
+
state.finalText = providerFinalText;
|
|
1019
880
|
if (!usage && aligned) {
|
|
1020
881
|
await emitProgress({
|
|
1021
882
|
type: "diagnostic",
|
|
@@ -1028,12 +889,11 @@ export async function runUni(options) {
|
|
|
1028
889
|
});
|
|
1029
890
|
}
|
|
1030
891
|
const finalEntries = turn.finalize({
|
|
1031
|
-
fallbackContent:
|
|
892
|
+
fallbackContent: providerFinalText,
|
|
1032
893
|
forceFallbackContent: hasExplicitTerminalText,
|
|
1033
894
|
usage,
|
|
1034
895
|
durationMs,
|
|
1035
896
|
error,
|
|
1036
|
-
artifacts: artifacts.length > 0 ? artifacts : undefined,
|
|
1037
897
|
});
|
|
1038
898
|
await appendTurnEntries(finalEntries);
|
|
1039
899
|
if (usage && contextStore) {
|
|
@@ -1067,11 +927,10 @@ export async function runUni(options) {
|
|
|
1067
927
|
});
|
|
1068
928
|
}
|
|
1069
929
|
const resultPayload = {
|
|
1070
|
-
text:
|
|
930
|
+
text: providerFinalText,
|
|
1071
931
|
usage: usage ?? { ...ZERO_USAGE },
|
|
1072
932
|
durationMs,
|
|
1073
933
|
...(error ? { error } : {}),
|
|
1074
|
-
...(aligned && artifacts.length > 0 ? { artifacts } : {}),
|
|
1075
934
|
};
|
|
1076
935
|
if (error) {
|
|
1077
936
|
uniLogger.error("uni run failed", {
|