@sema-agent/core 1.451.0 → 1.452.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/observer.js +3 -0
- package/dist/agents/subagent.js +51 -12
- package/dist/core/ask-question.js +5 -5
- package/dist/core/background-agent-store.d.ts +2 -0
- package/dist/core/background-agent-store.js +5 -1
- package/dist/core/exec-output-tail.d.ts +18 -1
- package/dist/core/exec-output-tail.js +38 -5
- package/dist/core/lsp-session.d.ts +1 -0
- package/dist/core/lsp-session.js +24 -6
- package/dist/core/lsp.d.ts +10 -0
- package/dist/core/lsp.js +63 -6
- package/dist/core/mailbox-store.d.ts +3 -2
- package/dist/core/mailbox-store.js +19 -4
- package/dist/core/memory.js +1 -1
- package/dist/core/runner/prepare-task.js +10 -1
- package/dist/core/runner/runtask.js +1 -1
- package/dist/core/session-reconcile.js +5 -2
- package/dist/core/task-notification.d.ts +1 -0
- package/dist/core/task-registry.d.ts +15 -9
- package/dist/core/task-registry.js +290 -53
- package/dist/core/tool-result-store.js +2 -2
- package/dist/core/tools.d.ts +5 -0
- package/dist/core/tools.js +3 -0
- package/dist/core/types.d.ts +1 -0
- package/dist/core/workflow-journal-store.d.ts +2 -0
- package/dist/core/workflow-journal-store.js +14 -0
- package/dist/engine/execution-env/node-execution-env.d.ts +1 -0
- package/dist/engine/execution-env/node-execution-env.js +130 -20
- package/dist/engine/lsp/node-lsp-manager.d.ts +3 -1
- package/dist/engine/lsp/node-lsp-manager.js +22 -5
- package/dist/engine/lsp/stdio-lsp-transport.d.ts +1 -1
- package/dist/engine/lsp/stdio-lsp-transport.js +17 -6
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/orchestration/run-workflow-tool.d.ts +1 -0
- package/dist/orchestration/run-workflow-tool.js +29 -6
- package/dist/orchestration/workflow.d.ts +9 -0
- package/dist/orchestration/workflow.js +80 -5
- package/dist/stores/cc/mailbox-store.js +6 -1
- package/dist/stores/file/background-agent-store.js +3 -2
- package/dist/stores/file/mailbox-store.d.ts +1 -1
- package/dist/stores/file/mailbox-store.js +9 -7
- package/dist/tools/fs/encoding.d.ts +5 -0
- package/dist/tools/fs/encoding.js +6 -0
- package/dist/tools/fs/index.js +184 -120
- package/dist/tools/fs/notebook.d.ts +43 -0
- package/dist/tools/fs/notebook.js +141 -0
- package/dist/tools/fs/repo-map.js +2 -2
- package/dist/tools/fs/search.js +141 -12
- package/dist/tools/gitea-issue.js +4 -2
- package/dist/tools/monitor.js +12 -8
- package/dist/tools/scheduler-tools.js +16 -16
- package/dist/tools/task-list.js +34 -12
- package/dist/tools/web.d.ts +2 -0
- package/dist/tools/web.js +105 -19
- package/dist/tools/worktree.js +14 -14
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Type } from "typebox";
|
|
2
|
-
import { defineTool } from "./tools.js";
|
|
2
|
+
import { defineTool, errorResult } from "./tools.js";
|
|
3
3
|
export class InMemoryToolResultStore {
|
|
4
4
|
opts;
|
|
5
5
|
map = new Map();
|
|
@@ -146,7 +146,7 @@ export function makeReadToolResultTool(store) {
|
|
|
146
146
|
const lim = Math.min(READ_MAX_LIMIT, Math.max(1, intOr(limit, READ_DEFAULT_LIMIT)));
|
|
147
147
|
const slice = await store.get(ref, { offset: off, limit: lim });
|
|
148
148
|
if (!slice) {
|
|
149
|
-
return `No persisted output for ref "${ref}" — it may have expired, or this process has no durable tool-result store (the preview above is all that remains)
|
|
149
|
+
return errorResult(`No persisted output for ref "${ref}" — it may have expired, or this process has no durable tool-result store (the preview above is all that remains).`);
|
|
150
150
|
}
|
|
151
151
|
const end = slice.offset + slice.content.length;
|
|
152
152
|
const more = end < slice.totalChars ? `\n…[${slice.totalChars - end} more chars — call again with offset=${end}.]` : "";
|
package/dist/core/tools.d.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import type { TSchema } from "typebox";
|
|
2
2
|
import type { AgentTool, Skill } from "../internal/harness.js";
|
|
3
3
|
import type { SkillSpec, ToolSpec } from "./types.js";
|
|
4
|
+
export declare function errorResult(text: string, details?: unknown): {
|
|
5
|
+
content: string;
|
|
6
|
+
isError: true;
|
|
7
|
+
details?: unknown;
|
|
8
|
+
};
|
|
4
9
|
export declare function defineTool<TParams extends TSchema = TSchema>(spec: ToolSpec<TParams>): AgentTool<TParams>;
|
|
5
10
|
export declare function toSkill(spec: SkillSpec): Skill;
|
package/dist/core/tools.js
CHANGED
|
@@ -22,6 +22,9 @@ function isEmptyToolContent(content) {
|
|
|
22
22
|
return true;
|
|
23
23
|
return content.every((b) => b.type === "text" && (typeof b.text !== "string" || b.text.trim() === ""));
|
|
24
24
|
}
|
|
25
|
+
export function errorResult(text, details) {
|
|
26
|
+
return details === undefined ? { content: text, isError: true } : { content: text, isError: true, details };
|
|
27
|
+
}
|
|
25
28
|
export function defineTool(spec) {
|
|
26
29
|
const executionMode = spec.executionMode ?? (spec.effect === "read" ? "parallel" : "sequential");
|
|
27
30
|
const tool = {
|
package/dist/core/types.d.ts
CHANGED
|
@@ -664,6 +664,7 @@ export interface BackgroundChildEvent {
|
|
|
664
664
|
resumable?: boolean;
|
|
665
665
|
status?: "completed" | "killed" | "failed";
|
|
666
666
|
seq?: number;
|
|
667
|
+
completionId?: string;
|
|
667
668
|
stoppedBy?: "user" | "parent" | "system" | (string & {});
|
|
668
669
|
summary?: string;
|
|
669
670
|
usage?: {
|
|
@@ -11,6 +11,8 @@ export interface WorkflowJournalStore {
|
|
|
11
11
|
export declare const MAX_JOURNAL_RESULT_BYTES: number;
|
|
12
12
|
export declare function oversizeJournalResult(serialized: string): boolean;
|
|
13
13
|
export declare function callKeyOrdinal(callKey: string): number;
|
|
14
|
+
export declare const JOURNAL_OVERSIZE_ERROR_CODE = "workflow.journal_oversize";
|
|
15
|
+
export declare function journalOversizeTombstone(result: TaskResult, bytes: number): TaskResult;
|
|
14
16
|
export declare class InMemoryWorkflowJournalStore implements WorkflowJournalStore {
|
|
15
17
|
private readonly runs;
|
|
16
18
|
load(runId: string, scope: string): Promise<WorkflowJournalEntry[]>;
|
|
@@ -7,6 +7,20 @@ export function callKeyOrdinal(callKey) {
|
|
|
7
7
|
const n = Number.parseInt(i >= 0 ? callKey.slice(0, i) : callKey, 10);
|
|
8
8
|
return Number.isFinite(n) ? n : 0;
|
|
9
9
|
}
|
|
10
|
+
export const JOURNAL_OVERSIZE_ERROR_CODE = "workflow.journal_oversize";
|
|
11
|
+
export function journalOversizeTombstone(result, bytes) {
|
|
12
|
+
const message = `[journal] result omitted: ${bytes} bytes exceeds the ${MAX_JOURNAL_RESULT_BYTES}-byte per-entry journal cap; ` +
|
|
13
|
+
`the agent's real terminal status was "${result.status}". A resume re-runs this agent (and everything after it) live.`;
|
|
14
|
+
return {
|
|
15
|
+
taskId: result.taskId,
|
|
16
|
+
sessionId: result.sessionId,
|
|
17
|
+
status: "failed",
|
|
18
|
+
result: message,
|
|
19
|
+
errorCode: JOURNAL_OVERSIZE_ERROR_CODE,
|
|
20
|
+
errorMessage: message,
|
|
21
|
+
stats: result.stats,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
10
24
|
function snapshot(entry) {
|
|
11
25
|
try {
|
|
12
26
|
return structuredClone(entry);
|
|
@@ -71,6 +71,7 @@ export declare class NodeExecutionEnv implements ExecutionEnv, BackgroundShellCa
|
|
|
71
71
|
}): Promise<Result<string[], FileError>>;
|
|
72
72
|
readBinaryFile(path: string, abortSignal?: AbortSignal): Promise<Result<Uint8Array, FileError>>;
|
|
73
73
|
writeFile(path: string, content: string | Uint8Array, abortSignal?: AbortSignal): Promise<Result<void, FileError>>;
|
|
74
|
+
private writeFileInPlace;
|
|
74
75
|
writeFileExclusive(path: string, content: string | Uint8Array, abortSignal?: AbortSignal): Promise<Result<void, FileError>>;
|
|
75
76
|
appendFile(path: string, content: string | Uint8Array): Promise<Result<void, FileError>>;
|
|
76
77
|
fileInfo(path: string): Promise<Result<FileInfo, FileError>>;
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
import { closeSync, constants, createReadStream, mkdtempSync, openSync, readSync, statSync, truncateSync, unlinkSync } from "node:fs";
|
|
4
|
-
import { access, appendFile, lstat, mkdir, mkdtemp, readdir, readFile, readlink, realpath, rm, writeFile, } from "node:fs/promises";
|
|
4
|
+
import { access, appendFile, lstat, mkdir, mkdtemp, open, readdir, readFile, readlink, realpath, rename, rm, unlink, writeFile, } from "node:fs/promises";
|
|
5
5
|
import { tmpdir } from "node:os";
|
|
6
6
|
import { isAbsolute, join, resolve } from "node:path";
|
|
7
7
|
import { createInterface } from "node:readline";
|
|
8
8
|
import { ExecutionError, err, FileError, ok, toError, } from "../harness/types.js";
|
|
9
9
|
import { killProcessTree, shutdownDebug } from "./kill-tree.js";
|
|
10
10
|
import { scrubSecretEnv } from "../../core/secret-env.js";
|
|
11
|
-
import { RollingTailBuffer, markTruncated } from "../../core/exec-output-tail.js";
|
|
11
|
+
import { RollingTailBuffer, markTruncated, newStreamCursorState, sliceStreamIncrement, } from "../../core/exec-output-tail.js";
|
|
12
12
|
import { BackgroundShellError } from "../../core/background-shell.js";
|
|
13
13
|
import { SchedulerError } from "../../core/scheduler.js";
|
|
14
14
|
const MAX_TIMER_TIMEOUT_MS = 2_147_000_000;
|
|
@@ -88,6 +88,11 @@ function toFileError(error, path) {
|
|
|
88
88
|
function abortResult(signal, path) {
|
|
89
89
|
return signal?.aborted ? err(new FileError("aborted", "aborted", path)) : undefined;
|
|
90
90
|
}
|
|
91
|
+
function isFsSupportError(error) {
|
|
92
|
+
const code = isNodeError(error) ? error.code : undefined;
|
|
93
|
+
return code === "EINVAL" || code === "ENOTSUP" || code === "EPERM" || code === "ENOSYS";
|
|
94
|
+
}
|
|
95
|
+
const RENAME_FALLBACK_CODES = new Set(["EXDEV", "EPERM", "EEXIST", "EBUSY"]);
|
|
91
96
|
async function pathExists(path) {
|
|
92
97
|
try {
|
|
93
98
|
await access(path, constants.F_OK);
|
|
@@ -744,10 +749,116 @@ export class NodeExecutionEnv {
|
|
|
744
749
|
}
|
|
745
750
|
try {
|
|
746
751
|
await mkdir(resolve(resolved, ".."), { recursive: true });
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
752
|
+
}
|
|
753
|
+
catch (error) {
|
|
754
|
+
return err(toFileError(error, resolved));
|
|
755
|
+
}
|
|
756
|
+
const afterMkdirAbort = abortResult(abortSignal, resolved);
|
|
757
|
+
if (afterMkdirAbort) {
|
|
758
|
+
return afterMkdirAbort;
|
|
759
|
+
}
|
|
760
|
+
let target = resolved;
|
|
761
|
+
let existingMode;
|
|
762
|
+
let st;
|
|
763
|
+
try {
|
|
764
|
+
st = await lstat(resolved);
|
|
765
|
+
}
|
|
766
|
+
catch {
|
|
767
|
+
st = undefined;
|
|
768
|
+
}
|
|
769
|
+
if (st !== undefined) {
|
|
770
|
+
if (st.isSymbolicLink()) {
|
|
771
|
+
let hop;
|
|
772
|
+
try {
|
|
773
|
+
const link = await readlink(resolved);
|
|
774
|
+
const parentReal = await realpath(resolve(resolved, "..")).catch(() => resolve(resolved, ".."));
|
|
775
|
+
hop = isAbsolute(link) ? link : resolve(parentReal, link);
|
|
776
|
+
}
|
|
777
|
+
catch {
|
|
778
|
+
hop = undefined;
|
|
779
|
+
}
|
|
780
|
+
if (hop === undefined)
|
|
781
|
+
return await this.writeFileInPlace(resolved, content, abortSignal);
|
|
782
|
+
let hopStat;
|
|
783
|
+
try {
|
|
784
|
+
hopStat = await lstat(hop);
|
|
785
|
+
}
|
|
786
|
+
catch {
|
|
787
|
+
hopStat = undefined;
|
|
788
|
+
}
|
|
789
|
+
if (hopStat !== undefined && !hopStat.isFile()) {
|
|
790
|
+
return await this.writeFileInPlace(resolved, content, abortSignal);
|
|
791
|
+
}
|
|
792
|
+
target = hop;
|
|
793
|
+
existingMode = hopStat !== undefined ? hopStat.mode & 0o7777 : undefined;
|
|
794
|
+
}
|
|
795
|
+
else if (!st.isFile()) {
|
|
796
|
+
return await this.writeFileInPlace(resolved, content, abortSignal);
|
|
750
797
|
}
|
|
798
|
+
else {
|
|
799
|
+
existingMode = st.mode & 0o7777;
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
const tmp = `${target}.tmp.${process.pid}.${randomUUID().replace(/-/g, "").slice(0, 12)}`;
|
|
803
|
+
let tmpDurable = false;
|
|
804
|
+
try {
|
|
805
|
+
const handle = await open(tmp, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | (constants.O_NOFOLLOW ?? 0));
|
|
806
|
+
try {
|
|
807
|
+
await handle.writeFile(content, { signal: abortSignal });
|
|
808
|
+
if (existingMode !== undefined) {
|
|
809
|
+
try {
|
|
810
|
+
await handle.chmod(existingMode);
|
|
811
|
+
}
|
|
812
|
+
catch (error) {
|
|
813
|
+
if (!isFsSupportError(error))
|
|
814
|
+
throw error;
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
try {
|
|
818
|
+
await handle.sync();
|
|
819
|
+
}
|
|
820
|
+
catch (error) {
|
|
821
|
+
if (!isFsSupportError(error))
|
|
822
|
+
throw error;
|
|
823
|
+
}
|
|
824
|
+
tmpDurable = true;
|
|
825
|
+
}
|
|
826
|
+
finally {
|
|
827
|
+
await handle.close().catch(() => { });
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
catch (error) {
|
|
831
|
+
await unlink(tmp).catch(() => { });
|
|
832
|
+
if (isNodeError(error) && error.code === "EACCES" && existingMode !== undefined) {
|
|
833
|
+
return await this.writeFileInPlace(resolved, content, abortSignal);
|
|
834
|
+
}
|
|
835
|
+
return err(toFileError(error, resolved));
|
|
836
|
+
}
|
|
837
|
+
const preRenameAbort = abortResult(abortSignal, resolved);
|
|
838
|
+
if (preRenameAbort) {
|
|
839
|
+
await unlink(tmp).catch(() => { });
|
|
840
|
+
return preRenameAbort;
|
|
841
|
+
}
|
|
842
|
+
try {
|
|
843
|
+
await rename(tmp, target);
|
|
844
|
+
return ok(undefined);
|
|
845
|
+
}
|
|
846
|
+
catch (error) {
|
|
847
|
+
const code = isNodeError(error) ? error.code : undefined;
|
|
848
|
+
if (tmpDurable && code !== undefined && RENAME_FALLBACK_CODES.has(code)) {
|
|
849
|
+
const inPlace = await this.writeFileInPlace(resolved, content, abortSignal);
|
|
850
|
+
if (inPlace.ok) {
|
|
851
|
+
await unlink(tmp).catch(() => { });
|
|
852
|
+
return inPlace;
|
|
853
|
+
}
|
|
854
|
+
return err(new FileError("unknown", `write to ${resolved} failed after the atomic rename was refused (${code}): ${inPlace.error.message}. The new content was preserved at ${tmp}`, resolved, inPlace.error));
|
|
855
|
+
}
|
|
856
|
+
await unlink(tmp).catch(() => { });
|
|
857
|
+
return err(toFileError(error, resolved));
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
async writeFileInPlace(resolved, content, abortSignal) {
|
|
861
|
+
try {
|
|
751
862
|
await writeFile(resolved, content, { signal: abortSignal });
|
|
752
863
|
return ok(undefined);
|
|
753
864
|
}
|
|
@@ -913,8 +1024,8 @@ export class NodeExecutionEnv {
|
|
|
913
1024
|
const shellId = `bg_${++this.bgCounter}_${randomUUID()}`;
|
|
914
1025
|
const entry = {
|
|
915
1026
|
child,
|
|
916
|
-
stdout: {
|
|
917
|
-
stderr: {
|
|
1027
|
+
stdout: { ...newStreamCursorState(), totalBytes: 0 },
|
|
1028
|
+
stderr: { ...newStreamCursorState(), totalBytes: 0 },
|
|
918
1029
|
status: "running",
|
|
919
1030
|
...(handover !== undefined
|
|
920
1031
|
? { spool: { outPath: handover.spool.outPath, errPath: handover.spool.errPath, fileCursor: { ...handover.cursors }, ephemeral: true } }
|
|
@@ -928,15 +1039,19 @@ export class NodeExecutionEnv {
|
|
|
928
1039
|
entry.stderr.tail.push(seedErr);
|
|
929
1040
|
entry.stdout.totalBytes += stdoutSoFar.droppedBytes + seedOut.length;
|
|
930
1041
|
entry.stderr.totalBytes += stderrSoFar.droppedBytes + seedErr.length;
|
|
1042
|
+
entry.stdout.acceptedBytes += seedOut.length;
|
|
1043
|
+
entry.stderr.acceptedBytes += seedErr.length;
|
|
931
1044
|
child.stdout?.on("data", (chunk) => {
|
|
932
1045
|
const buf = typeof chunk === "string" ? Buffer.from(chunk, "utf8") : chunk;
|
|
933
1046
|
entry.stdout.tail.push(buf);
|
|
934
1047
|
entry.stdout.totalBytes += buf.length;
|
|
1048
|
+
entry.stdout.acceptedBytes += buf.length;
|
|
935
1049
|
});
|
|
936
1050
|
child.stderr?.on("data", (chunk) => {
|
|
937
1051
|
const buf = typeof chunk === "string" ? Buffer.from(chunk, "utf8") : chunk;
|
|
938
1052
|
entry.stderr.tail.push(buf);
|
|
939
1053
|
entry.stderr.totalBytes += buf.length;
|
|
1054
|
+
entry.stderr.acceptedBytes += buf.length;
|
|
940
1055
|
});
|
|
941
1056
|
child.on("error", () => {
|
|
942
1057
|
if (entry.timer)
|
|
@@ -1040,8 +1155,8 @@ export class NodeExecutionEnv {
|
|
|
1040
1155
|
const shellId = `bg_${++this.bgCounter}_${randomUUID()}`;
|
|
1041
1156
|
const entry = {
|
|
1042
1157
|
child,
|
|
1043
|
-
stdout: {
|
|
1044
|
-
stderr: {
|
|
1158
|
+
stdout: { ...newStreamCursorState(), totalBytes: 0 },
|
|
1159
|
+
stderr: { ...newStreamCursorState(), totalBytes: 0 },
|
|
1045
1160
|
status: "running",
|
|
1046
1161
|
...(spool ? { spool } : {}),
|
|
1047
1162
|
};
|
|
@@ -1051,11 +1166,13 @@ export class NodeExecutionEnv {
|
|
|
1051
1166
|
const buf = Buffer.from(chunk, "utf8");
|
|
1052
1167
|
entry.stdout.tail.push(buf);
|
|
1053
1168
|
entry.stdout.totalBytes += buf.length;
|
|
1169
|
+
entry.stdout.acceptedBytes += buf.length;
|
|
1054
1170
|
});
|
|
1055
1171
|
child.stderr?.on("data", (chunk) => {
|
|
1056
1172
|
const buf = Buffer.from(chunk, "utf8");
|
|
1057
1173
|
entry.stderr.tail.push(buf);
|
|
1058
1174
|
entry.stderr.totalBytes += buf.length;
|
|
1175
|
+
entry.stderr.acceptedBytes += buf.length;
|
|
1059
1176
|
});
|
|
1060
1177
|
child.stdout?.on("error", () => { });
|
|
1061
1178
|
child.stderr?.on("error", () => { });
|
|
@@ -1098,17 +1215,9 @@ export class NodeExecutionEnv {
|
|
|
1098
1215
|
return err(new BackgroundShellError("not_found", `Unknown background shell: ${shellId}`));
|
|
1099
1216
|
}
|
|
1100
1217
|
this.syncSpool(entry);
|
|
1101
|
-
const
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
const startByte = Math.max(s.cursorBytes, droppedBytes);
|
|
1105
|
-
const startInTail = startByte - droppedBytes;
|
|
1106
|
-
const inc = startInTail <= 0 ? text : Buffer.from(text, "utf8").subarray(startInTail).toString("utf8");
|
|
1107
|
-
s.cursorBytes = s.totalBytes;
|
|
1108
|
-
return { inc, droppedBeforeCursor };
|
|
1109
|
-
};
|
|
1110
|
-
const out = slice(entry.stdout);
|
|
1111
|
-
const er = slice(entry.stderr);
|
|
1218
|
+
const terminal = entry.status !== "running";
|
|
1219
|
+
const out = sliceStreamIncrement(entry.stdout, terminal);
|
|
1220
|
+
const er = sliceStreamIncrement(entry.stderr, terminal);
|
|
1112
1221
|
const droppedBeforeCursor = out.droppedBeforeCursor + er.droppedBeforeCursor;
|
|
1113
1222
|
return ok({
|
|
1114
1223
|
stdout: out.inc,
|
|
@@ -1165,6 +1274,7 @@ export class NodeExecutionEnv {
|
|
|
1165
1274
|
if (got > 0) {
|
|
1166
1275
|
stream.tail.push(buf.subarray(0, got));
|
|
1167
1276
|
stream.totalBytes += got;
|
|
1277
|
+
stream.acceptedBytes += got;
|
|
1168
1278
|
spool.fileCursor[lane] += got;
|
|
1169
1279
|
}
|
|
1170
1280
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type SessionWarmup } from "../../core/lsp-session.js";
|
|
2
2
|
import { type LspChildProcess } from "./stdio-lsp-transport.js";
|
|
3
|
-
import type
|
|
3
|
+
import { type LspServerManager, type LspSession, type LspReadText } from "../../core/lsp.js";
|
|
4
4
|
import type { ExecutionEnv } from "../../internal/harness.js";
|
|
5
5
|
export declare const DEFAULT_LSP_SERVERS: Readonly<Record<string, string>>;
|
|
6
6
|
export type LspSpawn = (command: string, args: string[], cwd: string, signal?: AbortSignal) => Promise<LspChildProcess | undefined>;
|
|
@@ -40,6 +40,8 @@ export declare class NodeLspManager implements LspServerManager {
|
|
|
40
40
|
dispose(): Promise<void>;
|
|
41
41
|
}
|
|
42
42
|
export declare function languageFor(filePath: string): string | undefined;
|
|
43
|
+
export { MAX_LSP_FILE_BYTES } from "../../core/lsp.js";
|
|
44
|
+
export declare const defaultLspReadText: LspReadText;
|
|
43
45
|
export declare function lspSpawnOptions(cwd: string): {
|
|
44
46
|
cwd: string;
|
|
45
47
|
stdio: ["pipe", "pipe", "ignore"];
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
2
|
import { killProcessTree } from "../execution-env/kill-tree.js";
|
|
3
|
-
import { readFile } from "node:fs/promises";
|
|
4
|
-
import { dirname, extname, resolve as resolvePath } from "node:path";
|
|
3
|
+
import { readFile, stat } from "node:fs/promises";
|
|
4
|
+
import { basename, dirname, extname, resolve as resolvePath } from "node:path";
|
|
5
5
|
import { existsSync } from "node:fs";
|
|
6
6
|
import { pathToUri } from "../../core/lsp-protocol.js";
|
|
7
7
|
import { LspDiagnosticsRegistry } from "../../core/lsp-diagnostics.js";
|
|
8
8
|
import { TransportLspSession } from "../../core/lsp-session.js";
|
|
9
9
|
import { StdioLspTransport } from "./stdio-lsp-transport.js";
|
|
10
|
+
import { MAX_LSP_FILE_BYTES } from "../../core/lsp.js";
|
|
10
11
|
export const DEFAULT_LSP_SERVERS = {
|
|
11
12
|
typescript: "typescript-language-server --stdio",
|
|
12
13
|
javascript: "typescript-language-server --stdio",
|
|
@@ -43,7 +44,7 @@ const CLIENT_CAPABILITIES = {
|
|
|
43
44
|
definition: {}, references: {}, hover: {}, documentSymbol: {}, implementation: {}, callHierarchy: {},
|
|
44
45
|
publishDiagnostics: { versionSupport: true },
|
|
45
46
|
},
|
|
46
|
-
workspace: { symbol: {} },
|
|
47
|
+
workspace: { symbol: {}, workspaceFolders: false },
|
|
47
48
|
};
|
|
48
49
|
export class NodeLspManager {
|
|
49
50
|
servers;
|
|
@@ -62,7 +63,7 @@ export class NodeLspManager {
|
|
|
62
63
|
constructor(opts = {}) {
|
|
63
64
|
this.servers = { ...DEFAULT_LSP_SERVERS, ...opts.servers };
|
|
64
65
|
this.spawnFn = opts.spawn ?? defaultLspSpawn;
|
|
65
|
-
this.readTextFn = opts.readText ??
|
|
66
|
+
this.readTextFn = opts.readText ?? defaultLspReadText;
|
|
66
67
|
this.resolveRootFn = opts.resolveRoot ?? defaultResolveRoot;
|
|
67
68
|
this.log = opts.log ?? (() => { });
|
|
68
69
|
this.warmup = opts.warmup;
|
|
@@ -175,7 +176,14 @@ export class NodeLspManager {
|
|
|
175
176
|
}
|
|
176
177
|
const transport = new StdioLspTransport(child);
|
|
177
178
|
try {
|
|
178
|
-
|
|
179
|
+
const rootUri = pathToUri(root);
|
|
180
|
+
await transport.request("initialize", {
|
|
181
|
+
processId: null,
|
|
182
|
+
rootPath: root,
|
|
183
|
+
rootUri,
|
|
184
|
+
workspaceFolders: [{ uri: rootUri, name: basename(root) || root }],
|
|
185
|
+
capabilities: CLIENT_CAPABILITIES,
|
|
186
|
+
}, signal);
|
|
179
187
|
transport.notify("initialized", {});
|
|
180
188
|
}
|
|
181
189
|
catch (e) {
|
|
@@ -218,6 +226,15 @@ function defaultResolveRoot(filePath, env) {
|
|
|
218
226
|
}
|
|
219
227
|
return dirname(resolvePath(filePath));
|
|
220
228
|
}
|
|
229
|
+
export { MAX_LSP_FILE_BYTES } from "../../core/lsp.js";
|
|
230
|
+
export const defaultLspReadText = async (fp, signal) => {
|
|
231
|
+
const st = await stat(fp);
|
|
232
|
+
if (!st.isFile())
|
|
233
|
+
throw new Error(`not a regular file: ${fp}`);
|
|
234
|
+
if (st.size > MAX_LSP_FILE_BYTES)
|
|
235
|
+
throw new Error(`file too large for LSP analysis (${st.size} bytes exceeds ${MAX_LSP_FILE_BYTES})`);
|
|
236
|
+
return readFile(fp, { encoding: "utf8", signal });
|
|
237
|
+
};
|
|
221
238
|
export function lspSpawnOptions(cwd) {
|
|
222
239
|
return process.platform === "win32"
|
|
223
240
|
? { cwd, stdio: ["pipe", "pipe", "ignore"], shell: true }
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { encodeFrame, makeFrameDecoder } from "./frame-decoder.js";
|
|
2
2
|
import { killProcessTree } from "../execution-env/kill-tree.js";
|
|
3
|
+
import { brandLspFailure } from "../../core/lsp.js";
|
|
3
4
|
export class StdioLspTransport {
|
|
4
5
|
child;
|
|
5
6
|
requestTimeoutMs;
|
|
@@ -44,8 +45,10 @@ export class StdioLspTransport {
|
|
|
44
45
|
const p = this.pending.get(msg.id);
|
|
45
46
|
if (p) {
|
|
46
47
|
this.pending.delete(msg.id);
|
|
47
|
-
if (msg.error)
|
|
48
|
-
|
|
48
|
+
if (msg.error) {
|
|
49
|
+
const message = msg.error.message ?? "LSP error";
|
|
50
|
+
p.reject(brandLspFailure(new Error(message), jsonRpcReason(msg.error.code), message));
|
|
51
|
+
}
|
|
49
52
|
else
|
|
50
53
|
p.resolve(msg.result);
|
|
51
54
|
}
|
|
@@ -68,6 +71,7 @@ export class StdioLspTransport {
|
|
|
68
71
|
}
|
|
69
72
|
failAll(err) {
|
|
70
73
|
this.isClosed = true;
|
|
74
|
+
brandLspFailure(err, "server_terminated");
|
|
71
75
|
for (const p of this.pending.values())
|
|
72
76
|
p.reject(err);
|
|
73
77
|
this.pending.clear();
|
|
@@ -77,13 +81,13 @@ export class StdioLspTransport {
|
|
|
77
81
|
}
|
|
78
82
|
request(method, params, signal) {
|
|
79
83
|
if (this.isClosed)
|
|
80
|
-
return Promise.reject(new Error("LSP transport closed"));
|
|
84
|
+
return Promise.reject(brandLspFailure(new Error("LSP transport closed"), "server_terminated"));
|
|
81
85
|
const id = ++this.nextId;
|
|
82
86
|
return new Promise((resolve, reject) => {
|
|
83
87
|
const timer = setTimeout(() => {
|
|
84
88
|
if (this.pending.delete(id)) {
|
|
85
89
|
signal?.removeEventListener("abort", onAbort);
|
|
86
|
-
reject(new Error(`LSP ${method} timed out after ${this.requestTimeoutMs}ms`));
|
|
90
|
+
reject(brandLspFailure(new Error(`LSP ${method} timed out after ${this.requestTimeoutMs}ms`), "timeout"));
|
|
87
91
|
}
|
|
88
92
|
}, this.requestTimeoutMs);
|
|
89
93
|
timer.unref?.();
|
|
@@ -95,7 +99,7 @@ export class StdioLspTransport {
|
|
|
95
99
|
if (this.pending.delete(id)) {
|
|
96
100
|
cleanup();
|
|
97
101
|
this.notify("$/cancelRequest", { id });
|
|
98
|
-
reject(new Error(`LSP ${method} aborted`));
|
|
102
|
+
reject(brandLspFailure(new Error(`LSP ${method} aborted`), "cancelled"));
|
|
99
103
|
}
|
|
100
104
|
};
|
|
101
105
|
this.pending.set(id, {
|
|
@@ -119,7 +123,7 @@ export class StdioLspTransport {
|
|
|
119
123
|
catch (e) {
|
|
120
124
|
if (this.pending.delete(id)) {
|
|
121
125
|
cleanup();
|
|
122
|
-
reject(e instanceof Error ? e : new Error(String(e)));
|
|
126
|
+
reject(brandLspFailure(e instanceof Error ? e : new Error(String(e)), "server_terminated"));
|
|
123
127
|
}
|
|
124
128
|
}
|
|
125
129
|
});
|
|
@@ -145,6 +149,13 @@ export class StdioLspTransport {
|
|
|
145
149
|
}
|
|
146
150
|
}
|
|
147
151
|
}
|
|
152
|
+
function jsonRpcReason(code) {
|
|
153
|
+
if (code === -32601)
|
|
154
|
+
return "unsupported_operation";
|
|
155
|
+
if (code === -32800 || code === -32802)
|
|
156
|
+
return "cancelled";
|
|
157
|
+
return "server_error";
|
|
158
|
+
}
|
|
148
159
|
function toError(e) {
|
|
149
160
|
if (e instanceof Error)
|
|
150
161
|
return e;
|
package/dist/index.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ export { assembleCodeTools, type CodeToolsConfig, CODE_ROLE, assembleFullBodyToo
|
|
|
13
13
|
export { capAggregateToolResults, AGGREGATE_TOOL_RESULT_BUDGET_CHARS, DEFAULT_BUDGET_EXEMPT_TOOLS, type AggregateBudgetOptions, } from "./core/tool-result-budget.js";
|
|
14
14
|
export { capAggregateMediaBytes, AGGREGATE_MEDIA_BUDGET_BYTES, type MediaStripInfo } from "./core/media-byte-cap.js";
|
|
15
15
|
export { createAskUserQuestionTool, createDurableQuestionPolicy, QUESTION_AWAITS_RESUME, type OnQuestion, type AskQuestion, type AskQuestionOption, type AskQuestionRequest, type QuestionAnswer, type QuestionAnswerItem, } from "./core/ask-question.js";
|
|
16
|
-
export { createLspTool, gitCheckIgnoreFilter, LSP_OPERATIONS, type LspOperation, type LspServerManager, type LspSession, type LspResult, type LspLocation, type LspSymbolInfo, type LspRequestParams, type LspToolOptions, type LspTransport, type LspReadText, } from "./core/lsp.js";
|
|
16
|
+
export { createLspTool, gitCheckIgnoreFilter, LSP_OPERATIONS, LSP_FAILURE_BRAND, brandLspFailure, lspFailureOf, type LspNoneReason, type LspOperation, type LspServerManager, type LspSession, type LspResult, type LspLocation, type LspSymbolInfo, type LspRequestParams, type LspToolOptions, type LspTransport, type LspReadText, } from "./core/lsp.js";
|
|
17
17
|
export { buildRequest, parseResult, callHierarchyMethod, pathToUri } from "./core/lsp-protocol.js";
|
|
18
18
|
export { TransportLspSession, type SessionWarmup } from "./core/lsp-session.js";
|
|
19
19
|
export { LspDiagnosticsRegistry, formatDiagnosticsBlock, formatDiagnosticsSummary, type LspDiagnostic, type LspFileDiagnostics, } from "./core/lsp-diagnostics.js";
|
|
@@ -153,7 +153,7 @@ export { FileWorkflowRunStore, type FileWorkflowRunStoreOptions } from "./stores
|
|
|
153
153
|
export { workflowRunStoreContract } from "./core/workflow-run-store-contract.js";
|
|
154
154
|
export { type BackgroundAgentStore, type BackgroundAgentRecord, type BackgroundAgentRowSummary, type BackgroundAgentUsage, canAccessAgentRecord, BackgroundAgentStoreError, InMemoryBackgroundAgentStore, reconcileParkedAgents, } from "./core/background-agent-store.js";
|
|
155
155
|
export { FileBackgroundAgentStore, type FileBackgroundAgentStoreOptions } from "./stores/file/background-agent-store.js";
|
|
156
|
-
export { type WorkflowJournalStore, type WorkflowJournalEntry, InMemoryWorkflowJournalStore, callKeyOrdinal, } from "./core/workflow-journal-store.js";
|
|
156
|
+
export { type WorkflowJournalStore, type WorkflowJournalEntry, InMemoryWorkflowJournalStore, callKeyOrdinal, JOURNAL_OVERSIZE_ERROR_CODE, } from "./core/workflow-journal-store.js";
|
|
157
157
|
export { untrustedEgressForHuman, redactHostLeaks, redactSecrets, boundedRedactedSummary } from "./core/untrusted-egress.js";
|
|
158
158
|
export { MemoryRosterStore, FileRosterStore, type RosterStore, type RosterEntry, type RosterAccess, type RosterGcOptions } from "./agents/roster-store.js";
|
|
159
159
|
export { CONFIG_CATALOG_VERSION, describeConfigCatalog, resolveEffectiveConfig, type ConfigKnob, type ConfigOverrideDeclaration, type ConfigProvenance, type EffectiveConfigField, } from "./config/catalog.js";
|
package/dist/index.js
CHANGED
|
@@ -11,7 +11,7 @@ export { assembleCodeTools, CODE_ROLE, assembleFullBodyTools, FULL_BODY_ROLE } f
|
|
|
11
11
|
export { capAggregateToolResults, AGGREGATE_TOOL_RESULT_BUDGET_CHARS, DEFAULT_BUDGET_EXEMPT_TOOLS, } from "./core/tool-result-budget.js";
|
|
12
12
|
export { capAggregateMediaBytes, AGGREGATE_MEDIA_BUDGET_BYTES } from "./core/media-byte-cap.js";
|
|
13
13
|
export { createAskUserQuestionTool, createDurableQuestionPolicy, QUESTION_AWAITS_RESUME, } from "./core/ask-question.js";
|
|
14
|
-
export { createLspTool, gitCheckIgnoreFilter, LSP_OPERATIONS, } from "./core/lsp.js";
|
|
14
|
+
export { createLspTool, gitCheckIgnoreFilter, LSP_OPERATIONS, LSP_FAILURE_BRAND, brandLspFailure, lspFailureOf, } from "./core/lsp.js";
|
|
15
15
|
export { buildRequest, parseResult, callHierarchyMethod, pathToUri } from "./core/lsp-protocol.js";
|
|
16
16
|
export { TransportLspSession } from "./core/lsp-session.js";
|
|
17
17
|
export { LspDiagnosticsRegistry, formatDiagnosticsBlock, formatDiagnosticsSummary, } from "./core/lsp-diagnostics.js";
|
|
@@ -140,7 +140,7 @@ export { FileWorkflowRunStore } from "./stores/file/workflow-run-store.js";
|
|
|
140
140
|
export { workflowRunStoreContract } from "./core/workflow-run-store-contract.js";
|
|
141
141
|
export { canAccessAgentRecord, BackgroundAgentStoreError, InMemoryBackgroundAgentStore, reconcileParkedAgents, } from "./core/background-agent-store.js";
|
|
142
142
|
export { FileBackgroundAgentStore } from "./stores/file/background-agent-store.js";
|
|
143
|
-
export { InMemoryWorkflowJournalStore, callKeyOrdinal, } from "./core/workflow-journal-store.js";
|
|
143
|
+
export { InMemoryWorkflowJournalStore, callKeyOrdinal, JOURNAL_OVERSIZE_ERROR_CODE, } from "./core/workflow-journal-store.js";
|
|
144
144
|
export { untrustedEgressForHuman, redactHostLeaks, redactSecrets, boundedRedactedSummary } from "./core/untrusted-egress.js";
|
|
145
145
|
export { MemoryRosterStore, FileRosterStore } from "./agents/roster-store.js";
|
|
146
146
|
export { CONFIG_CATALOG_VERSION, describeConfigCatalog, resolveEffectiveConfig, } from "./config/catalog.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Type } from "typebox";
|
|
2
|
-
import { defineTool } from "../core/tools.js";
|
|
2
|
+
import { defineTool, errorResult } from "../core/tools.js";
|
|
3
3
|
import { redactSecrets, redactHostLeaks, boundedRedactedSummary } from "../core/untrusted-egress.js";
|
|
4
4
|
import { startWorkflow } from "./workflow.js";
|
|
5
5
|
import { buildWorkflowPrimitives } from "./workflow-primitives.js";
|
|
@@ -27,8 +27,19 @@ function completionSummary(name, run) {
|
|
|
27
27
|
const failed = run.agents.filter((a) => a.status === "failed").length;
|
|
28
28
|
const secs = run.endedAt !== undefined ? Math.max(0, Math.round((run.endedAt - run.startedAt) / 1000)) : undefined;
|
|
29
29
|
const elapsed = secs === undefined ? "" : secs >= 60 ? ` in ${Math.floor(secs / 60)}m${secs % 60}s` : ` in ${secs}s`;
|
|
30
|
+
const resumeClause = run.resume === undefined
|
|
31
|
+
? ""
|
|
32
|
+
: run.resume.journalEntries === 0
|
|
33
|
+
? "; resume found NO journal entries — nothing replayed, the whole script re-ran live"
|
|
34
|
+
: `; resume: replayed ${run.resume.replayed} of ${run.resume.journalEntries} journaled results`;
|
|
30
35
|
return (`workflow${cleanName ? ` "${cleanName}"` : ""} completed${elapsed} — agents ${done}/${run.agents.length} done` +
|
|
31
|
-
`${failed > 0 ? ` (${failed} failed)` : ""}; full result + per-agent rows via TaskOutput("${run.id}")`);
|
|
36
|
+
`${failed > 0 ? ` (${failed} failed)` : ""}${resumeClause}; full result + per-agent rows via TaskOutput("${run.id}")`);
|
|
37
|
+
}
|
|
38
|
+
function completionIdFromRejection(err) {
|
|
39
|
+
if (err === null || (typeof err !== "object" && typeof err !== "function"))
|
|
40
|
+
return undefined;
|
|
41
|
+
const run = err.workflowRun;
|
|
42
|
+
return run?.completionId;
|
|
32
43
|
}
|
|
33
44
|
function workflowUsageBlock(run) {
|
|
34
45
|
const agents = run.agents;
|
|
@@ -38,19 +49,26 @@ function workflowUsageBlock(run) {
|
|
|
38
49
|
agents_done: agents.filter((a) => a.status === "completed").length,
|
|
39
50
|
agents_error: agents.filter((a) => a.status === "failed").length,
|
|
40
51
|
agents_empty_result: agents.filter((a) => a.status === "completed" && (a.output === undefined || a.output === "")).length,
|
|
52
|
+
agents_replayed: agents.filter((a) => a.replayed === true).length,
|
|
53
|
+
...(run.journalSkips !== undefined && run.journalSkips > 0 ? { journal_skipped: run.journalSkips } : {}),
|
|
54
|
+
...(run.resume !== undefined ? { resume_journal_entries: run.resume.journalEntries } : {}),
|
|
41
55
|
subagent_tokens: run.stats.tokens + run.stats.nested.tokens,
|
|
42
56
|
tool_uses: agents.reduce((n, a) => n + (a.toolCalls ?? 0), 0),
|
|
43
57
|
...(run.endedAt !== undefined ? { duration_ms: run.endedAt - run.startedAt } : {}),
|
|
44
58
|
};
|
|
45
59
|
}
|
|
46
|
-
function workflowDiagnostics(runId, journalRef) {
|
|
60
|
+
function workflowDiagnostics(runId, journalRef, resumeMiss) {
|
|
47
61
|
const journalTeach = journalRef === undefined
|
|
48
62
|
? ""
|
|
49
63
|
: journalRef.includes("://")
|
|
50
64
|
? ` Full journal: ${journalRef}.`
|
|
51
65
|
: ` Full journal: ${journalRef} (a deployment data path — if Read is denied there, use the TaskOutput route above instead).`;
|
|
66
|
+
const resumeMissTeach = resumeMiss
|
|
67
|
+
? ` NOTE: this was a resume, but the prior run's journal was not usable under this scope — verify the runId and that this deployment wires a journalStore; the results above are from a full live re-run.`
|
|
68
|
+
: "";
|
|
52
69
|
return (`Per-agent rows (status / error / elapsed): TaskOutput({ task_id: "${runId}" }) — read them before assuming an empty or unexpected result.` +
|
|
53
70
|
journalTeach +
|
|
71
|
+
resumeMissTeach +
|
|
54
72
|
` To continue after editing the script: re-invoke with resumeFromRunId: "${runId}" (completed agents replay from the journal; only new/changed calls run).`);
|
|
55
73
|
}
|
|
56
74
|
const SCRIPT_ERROR_CODES = new Set([
|
|
@@ -151,7 +169,7 @@ export async function createRunWorkflowTool(d) {
|
|
|
151
169
|
const builtinsEnabled = d.builtinWorkflows !== false;
|
|
152
170
|
const namedWorkflowSection = renderNamedWorkflowListing(await collectNamedWorkflowListings(d.scriptStore, builtinsEnabled));
|
|
153
171
|
const sizeGuidelineSection = workflowSizeGuidelineSection(d.sizeGuideline ?? lim.sizeGuideline);
|
|
154
|
-
const structuredError = (message) => (
|
|
172
|
+
const structuredError = (message) => errorResult(JSON.stringify({ error: message }));
|
|
155
173
|
return defineTool({
|
|
156
174
|
name: RUN_WORKFLOW_TOOL_NAME,
|
|
157
175
|
aliases: ["RunWorkflow", "run_workflow"],
|
|
@@ -460,6 +478,7 @@ export async function createRunWorkflowTool(d) {
|
|
|
460
478
|
...(notification.result !== undefined ? { result: redactHostLeaks(notification.result) } : {}),
|
|
461
479
|
...(notification.usage !== undefined ? { usage: notification.usage } : {}),
|
|
462
480
|
...(notification.diagnostics !== undefined ? { diagnostics: redactHostLeaks(notification.diagnostics) } : {}),
|
|
481
|
+
...(notification.completionId !== undefined ? { completionId: notification.completionId } : {}),
|
|
463
482
|
}))
|
|
464
483
|
.catch(() => { });
|
|
465
484
|
};
|
|
@@ -472,7 +491,8 @@ export async function createRunWorkflowTool(d) {
|
|
|
472
491
|
summary: completionSummary(meta.name, res.run),
|
|
473
492
|
result: boundedRedactedSummary(res.result, 4000),
|
|
474
493
|
usage: workflowUsageBlock(res.run),
|
|
475
|
-
diagnostics: workflowDiagnostics(runId, d.journalStore?.locator?.(runId, res.run.scope)),
|
|
494
|
+
diagnostics: workflowDiagnostics(runId, d.journalStore?.locator?.(runId, res.run.scope), res.run.resume?.journalEntries === 0),
|
|
495
|
+
completionId: res.run.completionId,
|
|
476
496
|
}), (err) => fire({
|
|
477
497
|
task_id: runId,
|
|
478
498
|
task_type: "workflow",
|
|
@@ -480,6 +500,7 @@ export async function createRunWorkflowTool(d) {
|
|
|
480
500
|
status: "failed",
|
|
481
501
|
summary: failureSummary(err, runId),
|
|
482
502
|
diagnostics: workflowDiagnostics(runId, undefined),
|
|
503
|
+
...(completionIdFromRejection(err) !== undefined ? { completionId: completionIdFromRejection(err) } : {}),
|
|
483
504
|
}))
|
|
484
505
|
.catch(() => { });
|
|
485
506
|
}
|
|
@@ -504,7 +525,8 @@ export async function createRunWorkflowTool(d) {
|
|
|
504
525
|
summary: completionSummary(meta.name, res.run),
|
|
505
526
|
result: boundedRedactedSummary(res.result, 4000),
|
|
506
527
|
usage: workflowUsageBlock(res.run),
|
|
507
|
-
diagnostics: workflowDiagnostics(runId, d.journalStore?.locator?.(runId, res.run.scope)),
|
|
528
|
+
diagnostics: workflowDiagnostics(runId, d.journalStore?.locator?.(runId, res.run.scope), res.run.resume?.journalEntries === 0),
|
|
529
|
+
completionId: res.run.completionId,
|
|
508
530
|
}), (err) => fire({
|
|
509
531
|
task_id: runId,
|
|
510
532
|
task_type: "workflow",
|
|
@@ -512,6 +534,7 @@ export async function createRunWorkflowTool(d) {
|
|
|
512
534
|
status: "failed",
|
|
513
535
|
summary: failureSummary(err, runId),
|
|
514
536
|
diagnostics: workflowDiagnostics(runId, undefined),
|
|
537
|
+
...(completionIdFromRejection(err) !== undefined ? { completionId: completionIdFromRejection(err) } : {}),
|
|
515
538
|
}))
|
|
516
539
|
.catch(() => { });
|
|
517
540
|
}
|