@gajae-code/coding-agent 0.12.5 → 0.12.6
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 +32 -0
- package/README.md +4 -1
- package/dist/types/eval/backend.d.ts +2 -2
- package/dist/types/exec/bash-executor.d.ts +10 -8
- package/dist/types/extensibility/extensions/runner.d.ts +9 -7
- package/dist/types/gjc-runtime/launch-tmux.d.ts +22 -3
- package/dist/types/gjc-runtime/psmux-detect.d.ts +3 -0
- package/dist/types/gjc-runtime/team-launch.d.ts +3 -1
- package/dist/types/gjc-runtime/team-runtime.d.ts +14 -2
- package/dist/types/gjc-runtime/team-store.d.ts +0 -4
- package/dist/types/gjc-runtime/team-workers.d.ts +5 -0
- package/dist/types/gjc-runtime/tmux-common.d.ts +1 -0
- package/dist/types/gjc-runtime/tmux-provider-context.d.ts +57 -0
- package/dist/types/gjc-runtime/tmux-sessions.d.ts +8 -1
- package/dist/types/gjc-runtime/ultragoal-change-set.d.ts +3 -0
- package/dist/types/gjc-runtime/ultragoal-review-source.d.ts +83 -0
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +57 -4
- package/dist/types/internal-urls/registry-helpers.d.ts +18 -0
- package/dist/types/modes/components/gjc-bundle-settings.d.ts +1 -0
- package/dist/types/modes/components/plugin-settings.d.ts +2 -0
- package/dist/types/modes/components/settings-selector.d.ts +2 -0
- package/dist/types/sdk/broker/broker.d.ts +18 -0
- package/dist/types/sdk/broker/lifecycle-ledger.d.ts +13 -0
- package/dist/types/sdk/broker/lifecycle.d.ts +1 -0
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +4 -3
- package/dist/types/sdk/bus/index.d.ts +4 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +4 -2
- package/dist/types/sdk/bus/telegram-daemon.d.ts +25 -3
- package/dist/types/sdk/bus/topic-registry.d.ts +63 -3
- package/dist/types/sdk/host/host.d.ts +23 -2
- package/dist/types/sdk/host/reverse-leases.d.ts +7 -4
- package/dist/types/session/agent-session.d.ts +5 -4
- package/dist/types/session/artifacts.d.ts +8 -5
- package/dist/types/session/attempt-record-store.d.ts +19 -0
- package/dist/types/session/client-bridge.d.ts +14 -0
- package/dist/types/session/internal/managed-session-storage.d.ts +5 -2
- package/dist/types/session/messages.d.ts +4 -1
- package/dist/types/session/session-manager.d.ts +6 -4
- package/dist/types/session/session-storage.d.ts +8 -0
- package/dist/types/session/streaming-output.d.ts +32 -0
- package/dist/types/task/receipt.d.ts +2 -0
- package/dist/types/task/types.d.ts +178 -0
- package/dist/types/tools/bash.d.ts +1 -1
- package/dist/types/tools/fetch.d.ts +3 -0
- package/dist/types/tools/output-meta.d.ts +37 -5
- package/dist/types/tools/tool-result.d.ts +1 -0
- package/dist/types/utils/artifact-id.d.ts +2 -0
- package/package.json +8 -8
- package/scripts/capture-sticky-viewport-showcase.ts +131 -5
- package/scripts/verify-gjc-sdk-canonicalization.ts +128 -183
- package/scripts/verify-sticky-viewport-showcase.ts +620 -93
- package/src/config/model-profiles.ts +8 -0
- package/src/config/settings-schema.ts +17 -3
- package/src/cursor.ts +20 -13
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +5 -2
- package/src/eval/backend.ts +2 -2
- package/src/exec/bash-executor.ts +425 -151
- package/src/extensibility/extensions/runner.ts +80 -6
- package/src/extensibility/extensions/wrapper.ts +22 -15
- package/src/extensibility/runtime-skill-discovery.ts +4 -6
- package/src/gjc-runtime/launch-tmux.ts +309 -293
- package/src/gjc-runtime/managed-owner-supervisor.ts +6 -2
- package/src/gjc-runtime/psmux-detect.ts +52 -6
- package/src/gjc-runtime/team-launch.ts +50 -2
- package/src/gjc-runtime/team-runtime.ts +716 -251
- package/src/gjc-runtime/team-store.ts +25 -7
- package/src/gjc-runtime/team-workers.ts +161 -1
- package/src/gjc-runtime/tmux-common.ts +16 -4
- package/src/gjc-runtime/tmux-gc.ts +37 -5
- package/src/gjc-runtime/tmux-owner-isolation.ts +2 -0
- package/src/gjc-runtime/tmux-provider-context.ts +451 -0
- package/src/gjc-runtime/tmux-sessions.ts +409 -156
- package/src/gjc-runtime/ultragoal-change-set.ts +90 -44
- package/src/gjc-runtime/ultragoal-receipt-freshness.ts +7 -0
- package/src/gjc-runtime/ultragoal-review-source.ts +378 -0
- package/src/gjc-runtime/ultragoal-runtime.ts +640 -159
- package/src/gjc-runtime/workflow-manifest.generated.json +40 -1
- package/src/gjc-runtime/workflow-manifest.ts +12 -0
- package/src/internal-urls/agent-protocol.ts +98 -38
- package/src/internal-urls/artifact-protocol.ts +28 -17
- package/src/internal-urls/docs-index.generated.ts +5 -5
- package/src/internal-urls/local-protocol.ts +39 -18
- package/src/internal-urls/registry-helpers.ts +62 -0
- package/src/modes/acp/acp-agent.ts +51 -13
- package/src/modes/acp/acp-client-bridge.ts +154 -11
- package/src/modes/components/gjc-bundle-settings.ts +2 -0
- package/src/modes/components/plugin-settings.ts +5 -0
- package/src/modes/components/settings-selector.ts +4 -0
- package/src/modes/controllers/selector-controller.ts +1 -0
- package/src/prompts/tools/bash.md +2 -2
- package/src/prompts/tools/monitor.md +4 -4
- package/src/sdk/acp/adapter.ts +200 -9
- package/src/sdk/broker/broker.ts +93 -10
- package/src/sdk/broker/lifecycle-ledger.ts +227 -11
- package/src/sdk/broker/lifecycle.ts +691 -92
- package/src/sdk/bus/chat-daemon-control.ts +32 -6
- package/src/sdk/bus/index.ts +347 -14
- package/src/sdk/bus/telegram-daemon-contract.ts +4 -2
- package/src/sdk/bus/telegram-daemon.ts +283 -44
- package/src/sdk/bus/topic-registry.ts +133 -10
- package/src/sdk/host/host.ts +187 -9
- package/src/sdk/host/reverse-leases.ts +249 -42
- package/src/sdk/session.ts +7 -6
- package/src/session/agent-session.ts +930 -354
- package/src/session/artifacts.ts +211 -74
- package/src/session/attempt-record-store.ts +106 -0
- package/src/session/client-bridge.ts +15 -0
- package/src/session/internal/managed-session-scope.ts +252 -67
- package/src/session/internal/managed-session-storage.ts +105 -5
- package/src/session/messages.ts +32 -7
- package/src/session/session-manager.ts +19 -21
- package/src/session/session-storage.ts +146 -24
- package/src/session/streaming-output.ts +198 -40
- package/src/task/index.ts +45 -0
- package/src/task/provider-retry-status.ts +4 -1
- package/src/task/receipt.ts +4 -0
- package/src/task/render.ts +30 -0
- package/src/task/types.ts +34 -0
- package/src/tools/bash.ts +818 -239
- package/src/tools/eval.ts +99 -4
- package/src/tools/fetch.ts +77 -15
- package/src/tools/find.ts +7 -3
- package/src/tools/monitor.ts +7 -1
- package/src/tools/output-meta.ts +706 -116
- package/src/tools/search.ts +9 -4
- package/src/tools/tool-result.ts +1 -0
- package/src/utils/artifact-id.ts +6 -0
|
@@ -2,6 +2,7 @@ import type { AgentToolUpdateCallback } from "@gajae-code/agent-core";
|
|
|
2
2
|
|
|
3
3
|
import { sanitizeText } from "@gajae-code/utils";
|
|
4
4
|
import { formatBytes } from "../tools/render-utils";
|
|
5
|
+
import { isValidArtifactId } from "../utils/artifact-id";
|
|
5
6
|
import { sanitizeWithOptionalSixelPassthrough } from "../utils/sixel";
|
|
6
7
|
|
|
7
8
|
function sanitizeOutputChunk(rawChunk: string): string {
|
|
@@ -49,6 +50,47 @@ export type TerminalArtifactPublisher = (
|
|
|
49
50
|
info: { totalBytes: number; omittedBytes: number },
|
|
50
51
|
) => Promise<TerminalArtifactPublishResult>;
|
|
51
52
|
|
|
53
|
+
const TERMINAL_ARTIFACT_PUBLISH_WAIT_MS = 500;
|
|
54
|
+
const TERMINAL_ARTIFACT_PENDING_LIMIT = 64;
|
|
55
|
+
const pendingTerminalArtifactPublishesByOwner = new WeakMap<object, Set<Promise<TerminalArtifactPublishResult>>>();
|
|
56
|
+
|
|
57
|
+
function pendingTerminalArtifactPublishesForOwner(owner: object): Set<Promise<TerminalArtifactPublishResult>> {
|
|
58
|
+
let pending = pendingTerminalArtifactPublishesByOwner.get(owner);
|
|
59
|
+
if (!pending) {
|
|
60
|
+
pending = new Set();
|
|
61
|
+
pendingTerminalArtifactPublishesByOwner.set(owner, pending);
|
|
62
|
+
}
|
|
63
|
+
return pending;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function terminalPublisherOwner(publisher: TerminalArtifactPublisher): object {
|
|
67
|
+
return (publisher as TerminalArtifactPublisher & { owner?: object }).owner ?? publisher;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async function publishTerminalArtifactBounded(
|
|
71
|
+
publisher: TerminalArtifactPublisher,
|
|
72
|
+
content: string,
|
|
73
|
+
info: { totalBytes: number; omittedBytes: number },
|
|
74
|
+
): Promise<TerminalArtifactPublishResult> {
|
|
75
|
+
const pendingTerminalArtifactPublishes = pendingTerminalArtifactPublishesForOwner(terminalPublisherOwner(publisher));
|
|
76
|
+
if (pendingTerminalArtifactPublishes.size >= TERMINAL_ARTIFACT_PENDING_LIMIT) {
|
|
77
|
+
return { status: "failed", diagnostic: "pending publisher limit reached for this session" };
|
|
78
|
+
}
|
|
79
|
+
const promise = Promise.resolve().then(() => publisher(content, info));
|
|
80
|
+
pendingTerminalArtifactPublishes.add(promise);
|
|
81
|
+
void promise.then(
|
|
82
|
+
() => pendingTerminalArtifactPublishes.delete(promise),
|
|
83
|
+
() => pendingTerminalArtifactPublishes.delete(promise),
|
|
84
|
+
);
|
|
85
|
+
return Promise.race([
|
|
86
|
+
promise,
|
|
87
|
+
Bun.sleep(TERMINAL_ARTIFACT_PUBLISH_WAIT_MS).then(() => ({
|
|
88
|
+
status: "failed" as const,
|
|
89
|
+
diagnostic: `did not settle within ${TERMINAL_ARTIFACT_PUBLISH_WAIT_MS}ms`,
|
|
90
|
+
})),
|
|
91
|
+
]);
|
|
92
|
+
}
|
|
93
|
+
|
|
52
94
|
// =============================================================================
|
|
53
95
|
// Interfaces
|
|
54
96
|
// =============================================================================
|
|
@@ -64,18 +106,60 @@ export interface OutputSummary {
|
|
|
64
106
|
elidedBytes?: number;
|
|
65
107
|
/** Lines elided from the middle when head-retain mode is active. */
|
|
66
108
|
elidedLines?: number;
|
|
109
|
+
/** Exact source ranges retained by head/tail middle-elision windows. */
|
|
110
|
+
headRange?: { start: number; end: number };
|
|
111
|
+
tailRange?: { start: number; end: number };
|
|
112
|
+
/** The retained tail ends with only a byte fragment of its source line. */
|
|
113
|
+
lastLinePartial?: boolean;
|
|
114
|
+
/** The retained head ends with only a byte fragment of its source line. */
|
|
115
|
+
firstLinePartial?: boolean;
|
|
67
116
|
/** Bytes dropped by the per-line column cap (sum across all lines). */
|
|
68
117
|
columnDroppedBytes?: number;
|
|
69
118
|
/** Number of distinct lines that hit the per-line column cap. */
|
|
70
119
|
columnTruncatedLines?: number;
|
|
120
|
+
/** Configured per-line byte/column cap that caused visible omission. */
|
|
121
|
+
columnMax?: number;
|
|
71
122
|
/** Artifact ID for internal URL access (artifact://<id>) when output was persisted. */
|
|
72
123
|
artifactId?: string;
|
|
124
|
+
/** Artifact existence was proven by successful publication or file finalization. */
|
|
125
|
+
artifactVerified?: boolean;
|
|
73
126
|
/** Bytes omitted from artifact storage after the artifact hard cap was reached. */
|
|
74
127
|
artifactTruncatedBytes?: number;
|
|
128
|
+
/** Bytes dropped before the Bash executor received the native output stream. */
|
|
129
|
+
sourceTruncatedBytes?: number;
|
|
130
|
+
/** Exact source capture completeness could not be proven. */
|
|
131
|
+
sourceCaptureIncomplete?: boolean;
|
|
75
132
|
/** Bounded diagnostic when artifact writer or terminal publisher creation, write, finalization, or publication failed. */
|
|
76
133
|
artifactFailureDiagnostic?: string;
|
|
77
134
|
}
|
|
78
135
|
|
|
136
|
+
const kCurrentExecutionArtifactProof = Symbol("OutputSink.CurrentExecutionArtifactProof");
|
|
137
|
+
interface CurrentExecutionArtifactProof {
|
|
138
|
+
artifactId: string;
|
|
139
|
+
payload: string;
|
|
140
|
+
consumed: boolean;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** Verify that this exact OutputSink summary came from a successful current-execution publication. */
|
|
144
|
+
export function hasCurrentExecutionArtifactProof(
|
|
145
|
+
summary: OutputSummary,
|
|
146
|
+
artifactId: string,
|
|
147
|
+
payload?: string,
|
|
148
|
+
): boolean {
|
|
149
|
+
const proof = (summary as OutputSummary & { [kCurrentExecutionArtifactProof]?: CurrentExecutionArtifactProof })[
|
|
150
|
+
kCurrentExecutionArtifactProof
|
|
151
|
+
];
|
|
152
|
+
if (
|
|
153
|
+
!proof ||
|
|
154
|
+
proof.artifactId !== artifactId ||
|
|
155
|
+
proof.consumed ||
|
|
156
|
+
(payload !== undefined && proof.payload !== payload)
|
|
157
|
+
)
|
|
158
|
+
return false;
|
|
159
|
+
proof.consumed = true;
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
|
|
79
163
|
export interface OutputSinkOptions {
|
|
80
164
|
/**
|
|
81
165
|
* Deprecated managed artifact pathname. Bare paths are deliberately ignored:
|
|
@@ -141,7 +225,10 @@ export interface TruncationResult {
|
|
|
141
225
|
elidedBytes?: number;
|
|
142
226
|
/** Lines elided from the middle (truncateMiddle only). */
|
|
143
227
|
elidedLines?: number;
|
|
228
|
+
headRange?: { start: number; end: number };
|
|
229
|
+
tailRange?: { start: number; end: number };
|
|
144
230
|
lastLinePartial?: boolean;
|
|
231
|
+
firstLinePartial?: boolean;
|
|
145
232
|
firstLineExceedsLimit?: boolean;
|
|
146
233
|
lastLineExceedsLimit?: boolean;
|
|
147
234
|
}
|
|
@@ -793,7 +880,7 @@ export function truncateMiddle(content: string, options: TruncationOptions = {})
|
|
|
793
880
|
const head = windows.head;
|
|
794
881
|
const tail = windows.tail;
|
|
795
882
|
if (!head || !tail) return noTruncResult(content, windows.totalLines, windows.totalBytes);
|
|
796
|
-
if (head.lines + tail.lines >= windows.totalLines)
|
|
883
|
+
if (head.lines + tail.lines >= windows.totalLines && tail.kind !== "partial-line")
|
|
797
884
|
return noTruncResult(content, windows.totalLines, windows.totalBytes);
|
|
798
885
|
const legacyElidedLines = Math.max(0, windows.totalLines - head.lines - tail.lines);
|
|
799
886
|
const legacyElidedBytes =
|
|
@@ -802,7 +889,7 @@ export function truncateMiddle(content: string, options: TruncationOptions = {})
|
|
|
802
889
|
: windows.elidedBytes;
|
|
803
890
|
const marker = formatMiddleElisionMarker(legacyElidedLines, legacyElidedBytes);
|
|
804
891
|
const markerBytes = Buffer.byteLength(marker, "utf-8");
|
|
805
|
-
|
|
892
|
+
const result: TruncationResult = {
|
|
806
893
|
content: `${head.content}\n${marker}\n${tail.content}`,
|
|
807
894
|
truncated: true,
|
|
808
895
|
truncatedBy: "middle",
|
|
@@ -816,6 +903,11 @@ export function truncateMiddle(content: string, options: TruncationOptions = {})
|
|
|
816
903
|
firstLineExceedsLimit: false,
|
|
817
904
|
...(tail.lastLinePartial ? { lastLineExceedsLimit: true } : {}),
|
|
818
905
|
};
|
|
906
|
+
Object.defineProperties(result, {
|
|
907
|
+
headRange: { value: { start: head.origin.startLine, end: head.origin.endLine } },
|
|
908
|
+
tailRange: { value: { start: tail.origin.startLine, end: tail.origin.endLine } },
|
|
909
|
+
});
|
|
910
|
+
return result;
|
|
819
911
|
}
|
|
820
912
|
}
|
|
821
913
|
}
|
|
@@ -932,6 +1024,8 @@ export class OutputSink {
|
|
|
932
1024
|
#head = "";
|
|
933
1025
|
#headBytes = 0;
|
|
934
1026
|
#headRetentionDisabled = false;
|
|
1027
|
+
#headEndsMidLine = false;
|
|
1028
|
+
#tailStartsMidLine = false;
|
|
935
1029
|
#totalLines = 0; // newline count
|
|
936
1030
|
#totalBytes = 0;
|
|
937
1031
|
#processedBytes = 0;
|
|
@@ -967,6 +1061,8 @@ export class OutputSink {
|
|
|
967
1061
|
#pendingArtifactContent?: string[];
|
|
968
1062
|
#artifactPublishAttempted = false;
|
|
969
1063
|
#finalized = false;
|
|
1064
|
+
#finalizing = false;
|
|
1065
|
+
#artifactFinalization?: Promise<string | undefined>;
|
|
970
1066
|
|
|
971
1067
|
#fileReady = false;
|
|
972
1068
|
|
|
@@ -1020,9 +1116,10 @@ export class OutputSink {
|
|
|
1020
1116
|
return this.#buffer;
|
|
1021
1117
|
}
|
|
1022
1118
|
|
|
1023
|
-
#setTail(text: string, bytes = Buffer.byteLength(text, "utf-8")): void {
|
|
1119
|
+
#setTail(text: string, bytes = Buffer.byteLength(text, "utf-8"), startsMidLine = false): void {
|
|
1024
1120
|
this.#buffer = text;
|
|
1025
1121
|
this.#bufferBytes = bytes;
|
|
1122
|
+
this.#tailStartsMidLine = startsMidLine;
|
|
1026
1123
|
}
|
|
1027
1124
|
|
|
1028
1125
|
#appendTail(text: string, bytes: number): void {
|
|
@@ -1037,9 +1134,12 @@ export class OutputSink {
|
|
|
1037
1134
|
|
|
1038
1135
|
#trimTailTo(maxBytes: number): void {
|
|
1039
1136
|
if (this.#bufferBytes <= maxBytes) return;
|
|
1040
|
-
const
|
|
1137
|
+
const previous = this.#buffer;
|
|
1138
|
+
const { text, bytes } = truncateTailBytes(previous, maxBytes);
|
|
1139
|
+
const removed = previous.slice(0, previous.length - text.length);
|
|
1041
1140
|
this.#buffer = text;
|
|
1042
1141
|
this.#bufferBytes = bytes;
|
|
1142
|
+
this.#tailStartsMidLine = removed.length > 0 && !removed.endsWith("\n");
|
|
1043
1143
|
}
|
|
1044
1144
|
|
|
1045
1145
|
/**
|
|
@@ -1050,6 +1150,7 @@ export class OutputSink {
|
|
|
1050
1150
|
// F21: with coalescing enabled, accumulate raw chunks and process them in batches; the default
|
|
1051
1151
|
// (disabled) path calls #ingest directly and is byte-identical to the historical per-chunk path.
|
|
1052
1152
|
push(chunk: string): void {
|
|
1153
|
+
if (this.#finalized || this.#finalizing) return;
|
|
1053
1154
|
if (!this.#coalesceSanitize) {
|
|
1054
1155
|
this.#ingest(chunk);
|
|
1055
1156
|
return;
|
|
@@ -1132,6 +1233,10 @@ export class OutputSink {
|
|
|
1132
1233
|
}
|
|
1133
1234
|
}
|
|
1134
1235
|
|
|
1236
|
+
if (this.#headBytes > 0 && this.#bufferBytes === 0 && tailBytes > 0) {
|
|
1237
|
+
this.#headEndsMidLine = !this.#head.endsWith("\n") && !tailChunk.startsWith("\n");
|
|
1238
|
+
this.#tailStartsMidLine = this.#headEndsMidLine;
|
|
1239
|
+
}
|
|
1135
1240
|
this.#pushTail(tailChunk, tailBytes);
|
|
1136
1241
|
}
|
|
1137
1242
|
|
|
@@ -1216,7 +1321,8 @@ export class OutputSink {
|
|
|
1216
1321
|
// Avoid creating a giant intermediate string when chunk alone dominates.
|
|
1217
1322
|
if (dataBytes >= threshold) {
|
|
1218
1323
|
const { text, bytes } = truncateTailBytes(chunk, threshold);
|
|
1219
|
-
|
|
1324
|
+
const removed = chunk.slice(0, chunk.length - text.length);
|
|
1325
|
+
this.#setTail(text, bytes, removed.length > 0 && !removed.endsWith("\n"));
|
|
1220
1326
|
} else {
|
|
1221
1327
|
// Intermediate size is bounded (<= threshold + dataBytes), safe to concat.
|
|
1222
1328
|
this.#appendTail(chunk, dataBytes);
|
|
@@ -1390,13 +1496,19 @@ export class OutputSink {
|
|
|
1390
1496
|
return this.#publishedArtifactId;
|
|
1391
1497
|
}
|
|
1392
1498
|
this.#artifactPublishAttempted = true;
|
|
1499
|
+
this.#finalizing = true;
|
|
1393
1500
|
const content = this.#pendingArtifactContent?.join("") ?? "";
|
|
1394
1501
|
const omittedBytes = this.#artifactTruncatedBytes;
|
|
1395
1502
|
try {
|
|
1396
|
-
const published = await this.#artifactPublisher
|
|
1503
|
+
const published = await publishTerminalArtifactBounded(this.#artifactPublisher!, content, {
|
|
1397
1504
|
totalBytes: this.#totalBytes,
|
|
1398
1505
|
omittedBytes,
|
|
1399
1506
|
});
|
|
1507
|
+
if (typeof published !== "object" || published === null || !("status" in published)) {
|
|
1508
|
+
this.#recordTerminalArtifactDiagnostic("failed", "publisher returned an invalid result");
|
|
1509
|
+
this.#artifactFinalizationFailed = true;
|
|
1510
|
+
return undefined;
|
|
1511
|
+
}
|
|
1400
1512
|
if (published.status === "unavailable") {
|
|
1401
1513
|
this.#recordTerminalArtifactDiagnostic("unavailable");
|
|
1402
1514
|
this.#artifactFinalizationFailed = true;
|
|
@@ -1407,8 +1519,13 @@ export class OutputSink {
|
|
|
1407
1519
|
this.#artifactFinalizationFailed = true;
|
|
1408
1520
|
return undefined;
|
|
1409
1521
|
}
|
|
1410
|
-
if (published.
|
|
1411
|
-
this.#recordTerminalArtifactDiagnostic("failed", "
|
|
1522
|
+
if (published.status !== "published") {
|
|
1523
|
+
this.#recordTerminalArtifactDiagnostic("failed", "publisher returned an invalid result");
|
|
1524
|
+
this.#artifactFinalizationFailed = true;
|
|
1525
|
+
return undefined;
|
|
1526
|
+
}
|
|
1527
|
+
if (!isValidArtifactId(published.artifactId)) {
|
|
1528
|
+
this.#recordTerminalArtifactDiagnostic("failed", "storage returned an invalid artifact id");
|
|
1412
1529
|
this.#artifactFinalizationFailed = true;
|
|
1413
1530
|
return undefined;
|
|
1414
1531
|
}
|
|
@@ -1432,6 +1549,48 @@ export class OutputSink {
|
|
|
1432
1549
|
}
|
|
1433
1550
|
}
|
|
1434
1551
|
|
|
1552
|
+
#shouldFinalizeArtifact(): boolean {
|
|
1553
|
+
return (
|
|
1554
|
+
this.#finalized ||
|
|
1555
|
+
this.#artifactFinalizationFailed ||
|
|
1556
|
+
this.#shouldPublishTerminalArtifact() ||
|
|
1557
|
+
this.#file !== undefined ||
|
|
1558
|
+
(this.#artifactPath !== undefined && this.#artifactTruncatedBytes > 0)
|
|
1559
|
+
);
|
|
1560
|
+
}
|
|
1561
|
+
|
|
1562
|
+
async #finalizeArtifact(): Promise<string | undefined> {
|
|
1563
|
+
let artifactId: string | undefined;
|
|
1564
|
+
if (this.#finalized) artifactId = this.#publishedArtifactId ?? this.#artifactId;
|
|
1565
|
+
else if (!this.#artifactFinalizationFailed) {
|
|
1566
|
+
if (this.#artifactPublisher && !this.#artifactPath) artifactId = await this.#publishTerminalArtifact();
|
|
1567
|
+
if (!this.#artifactFinalizationFailed && this.#artifactPath) {
|
|
1568
|
+
if (this.#artifactTruncatedBytes > 0) this.#createFileSink();
|
|
1569
|
+
if (this.#file) {
|
|
1570
|
+
this.#finalizing = true;
|
|
1571
|
+
await this.#writeArtifactTruncationNotice();
|
|
1572
|
+
try {
|
|
1573
|
+
await this.#file.sink.end();
|
|
1574
|
+
artifactId = this.#file.artifactId;
|
|
1575
|
+
this.#finalized = true;
|
|
1576
|
+
} catch (error) {
|
|
1577
|
+
this.#recordArtifactFailure("end", error);
|
|
1578
|
+
this.#artifactFinalizationFailed = true;
|
|
1579
|
+
this.#file = undefined;
|
|
1580
|
+
this.#fileReady = false;
|
|
1581
|
+
}
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
if (this.#finalized) {
|
|
1586
|
+
this.#pendingFileWrites = undefined;
|
|
1587
|
+
this.#pendingFileWriteBytes = 0;
|
|
1588
|
+
this.#fileReady = false;
|
|
1589
|
+
this.#pendingArtifactContent = undefined;
|
|
1590
|
+
}
|
|
1591
|
+
return artifactId;
|
|
1592
|
+
}
|
|
1593
|
+
|
|
1435
1594
|
createInput(): WritableStream<Uint8Array | string> {
|
|
1436
1595
|
const dec = new TextDecoder("utf-8", { ignoreBOM: true });
|
|
1437
1596
|
const finalize = () => {
|
|
@@ -1457,17 +1616,20 @@ export class OutputSink {
|
|
|
1457
1616
|
* append directly to the tail without reordering the replacement.
|
|
1458
1617
|
*/
|
|
1459
1618
|
replace(text: string): void {
|
|
1619
|
+
if (this.#finalized || this.#finalizing) return;
|
|
1460
1620
|
this.#coalesceBuf = "";
|
|
1461
1621
|
const replacementBytes = Buffer.byteLength(text, "utf-8");
|
|
1462
1622
|
this.#head = "";
|
|
1463
1623
|
this.#headBytes = 0;
|
|
1464
1624
|
this.#setTail("");
|
|
1625
|
+
this.#headEndsMidLine = false;
|
|
1465
1626
|
|
|
1466
1627
|
if (this.#headLimit > 0) {
|
|
1467
1628
|
const head = truncateHeadBytes(text, this.#headLimit);
|
|
1468
1629
|
this.#appendHead(head.text, head.bytes);
|
|
1469
1630
|
const tailText = text.substring(head.text.length);
|
|
1470
|
-
this.#
|
|
1631
|
+
this.#headEndsMidLine = tailText.length > 0 && !head.text.endsWith("\n") && !tailText.startsWith("\n");
|
|
1632
|
+
this.#setTail(tailText, replacementBytes - head.bytes, this.#headEndsMidLine);
|
|
1471
1633
|
} else {
|
|
1472
1634
|
this.#setTail(text, replacementBytes);
|
|
1473
1635
|
}
|
|
@@ -1491,35 +1653,9 @@ export class OutputSink {
|
|
|
1491
1653
|
const totalLines = this.#sawData ? this.#totalLines + 1 : 0;
|
|
1492
1654
|
|
|
1493
1655
|
let artifactId: string | undefined;
|
|
1494
|
-
if (this.#
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
if (this.#artifactPublisher && !this.#artifactPath) {
|
|
1498
|
-
artifactId = await this.#publishTerminalArtifact();
|
|
1499
|
-
}
|
|
1500
|
-
if (!this.#artifactFinalizationFailed && this.#artifactPath) {
|
|
1501
|
-
if (this.#artifactTruncatedBytes > 0) this.#createFileSink();
|
|
1502
|
-
await this.#writeArtifactTruncationNotice();
|
|
1503
|
-
if (this.#file) {
|
|
1504
|
-
try {
|
|
1505
|
-
await this.#file.sink.end();
|
|
1506
|
-
artifactId = this.#file.artifactId;
|
|
1507
|
-
this.#finalized = true;
|
|
1508
|
-
} catch (error) {
|
|
1509
|
-
this.#recordArtifactFailure("end", error);
|
|
1510
|
-
this.#artifactFinalizationFailed = true;
|
|
1511
|
-
this.#file = undefined;
|
|
1512
|
-
this.#fileReady = false;
|
|
1513
|
-
}
|
|
1514
|
-
}
|
|
1515
|
-
}
|
|
1516
|
-
}
|
|
1517
|
-
if (this.#finalized) {
|
|
1518
|
-
// Terminal: the artifact is closed; replay state is no longer needed.
|
|
1519
|
-
this.#pendingFileWrites = undefined;
|
|
1520
|
-
this.#pendingFileWriteBytes = 0;
|
|
1521
|
-
this.#fileReady = false;
|
|
1522
|
-
this.#pendingArtifactContent = undefined;
|
|
1656
|
+
if (this.#shouldFinalizeArtifact()) {
|
|
1657
|
+
this.#artifactFinalization ??= this.#finalizeArtifact();
|
|
1658
|
+
artifactId = await this.#artifactFinalization;
|
|
1523
1659
|
}
|
|
1524
1660
|
// Non-finalized dumps (no artifact sink ever opened) keep the raw replay
|
|
1525
1661
|
// queue so a later post-dump push that spills produces a CUMULATIVE
|
|
@@ -1543,6 +1679,8 @@ export class OutputSink {
|
|
|
1543
1679
|
let outputLines: number;
|
|
1544
1680
|
let elidedBytes: number | undefined;
|
|
1545
1681
|
let elidedLines: number | undefined;
|
|
1682
|
+
let headRange: { start: number; end: number } | undefined;
|
|
1683
|
+
let tailRange: { start: number; end: number } | undefined;
|
|
1546
1684
|
|
|
1547
1685
|
if (headBytes > 0 && effectiveTotalBytes > headBytes + tailBytes) {
|
|
1548
1686
|
// Middle was elided. Emit both explicit windows, including when they are
|
|
@@ -1555,7 +1693,12 @@ export class OutputSink {
|
|
|
1555
1693
|
const tailSep = tailBuf.startsWith("\n") ? "" : "\n";
|
|
1556
1694
|
body = `${headText}${headSep}${marker}${tailSep}${tailBuf}`;
|
|
1557
1695
|
outputBytes = headBytes + markerBytes + tailBytes + headSep.length + tailSep.length;
|
|
1558
|
-
outputLines =
|
|
1696
|
+
outputLines = body.length > 0 ? countNewlines(body) + 1 : 0;
|
|
1697
|
+
headRange = headLines > 0 && !this.#headEndsMidLine ? { start: 1, end: headLines } : undefined;
|
|
1698
|
+
tailRange =
|
|
1699
|
+
tailLines > 0 && !this.#tailStartsMidLine
|
|
1700
|
+
? { start: processedTotalLines - tailLines + 1, end: processedTotalLines }
|
|
1701
|
+
: undefined;
|
|
1559
1702
|
this.#truncated = true;
|
|
1560
1703
|
} else if (headBytes > 0) {
|
|
1561
1704
|
// Head + tail combine into the full buffered output (no overlap or elision).
|
|
@@ -1569,7 +1712,7 @@ export class OutputSink {
|
|
|
1569
1712
|
}
|
|
1570
1713
|
|
|
1571
1714
|
if (this.#columnDroppedBytes > 0) this.#truncated = true;
|
|
1572
|
-
|
|
1715
|
+
const summary: OutputSummary = {
|
|
1573
1716
|
output: `${noticeLine}${body}`,
|
|
1574
1717
|
truncated: this.#truncated,
|
|
1575
1718
|
totalLines,
|
|
@@ -1578,12 +1721,27 @@ export class OutputSink {
|
|
|
1578
1721
|
outputBytes,
|
|
1579
1722
|
elidedBytes,
|
|
1580
1723
|
elidedLines,
|
|
1724
|
+
headRange,
|
|
1725
|
+
tailRange,
|
|
1726
|
+
firstLinePartial: this.#headEndsMidLine || undefined,
|
|
1727
|
+
lastLinePartial: this.#tailStartsMidLine || undefined,
|
|
1581
1728
|
columnDroppedBytes: this.#columnDroppedBytes > 0 ? this.#columnDroppedBytes : undefined,
|
|
1582
1729
|
columnTruncatedLines: this.#columnTruncatedLines > 0 ? this.#columnTruncatedLines : undefined,
|
|
1730
|
+
columnMax: this.#columnDroppedBytes > 0 ? this.#maxColumns : undefined,
|
|
1583
1731
|
artifactTruncatedBytes: this.#artifactTruncatedBytes > 0 ? this.#artifactTruncatedBytes : undefined,
|
|
1584
1732
|
artifactFailureDiagnostic: this.#artifactFailureDiagnostic,
|
|
1585
1733
|
artifactId,
|
|
1734
|
+
artifactVerified: artifactId !== undefined && this.#publishedArtifactId === artifactId,
|
|
1586
1735
|
};
|
|
1736
|
+
if (summary.artifactVerified === true && artifactId !== undefined) {
|
|
1737
|
+
Object.defineProperty(summary, kCurrentExecutionArtifactProof, {
|
|
1738
|
+
value: { artifactId, payload: summary.output, consumed: false } satisfies CurrentExecutionArtifactProof,
|
|
1739
|
+
enumerable: true,
|
|
1740
|
+
configurable: false,
|
|
1741
|
+
writable: false,
|
|
1742
|
+
});
|
|
1743
|
+
}
|
|
1744
|
+
return summary;
|
|
1587
1745
|
}
|
|
1588
1746
|
}
|
|
1589
1747
|
|
package/src/task/index.ts
CHANGED
|
@@ -53,6 +53,9 @@ import {
|
|
|
53
53
|
RepositoryBindingError,
|
|
54
54
|
resolveTaskRepositoryBinding,
|
|
55
55
|
} from "../gjc-runtime/repository-binding";
|
|
56
|
+
import { reconcileReviewSourceDelivery } from "../gjc-runtime/ultragoal-review-source";
|
|
57
|
+
import { classifyUltragoalReviewDelivery, validateUltragoalReviewDispatch } from "../gjc-runtime/ultragoal-runtime";
|
|
58
|
+
|
|
56
59
|
import { initializeLocalRoot, type LocalProtocolOptions, resolveLocalUrlToPath } from "../internal-urls";
|
|
57
60
|
import { ArtifactManager } from "../session/artifacts";
|
|
58
61
|
import { generateCommitMessage } from "../utils/commit-message-generator";
|
|
@@ -1702,6 +1705,27 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
1702
1705
|
if (taskRepositoryBinding.relativeSubdir) {
|
|
1703
1706
|
assertPathUnderRepositoryBinding(taskRepositoryBinding, ".");
|
|
1704
1707
|
}
|
|
1708
|
+
if (task.reviewSource) {
|
|
1709
|
+
await validateUltragoalReviewDispatch({
|
|
1710
|
+
cwd: this.session.cwd,
|
|
1711
|
+
dispatchId: task.reviewSource.dispatchId,
|
|
1712
|
+
cohortId: task.reviewSource.cohortId,
|
|
1713
|
+
taskId: task.id,
|
|
1714
|
+
lane: task.reviewSource.lane,
|
|
1715
|
+
snapshotId: task.reviewSource.snapshotId,
|
|
1716
|
+
generation: task.reviewSource.generation,
|
|
1717
|
+
repositoryBindingDigest: task.reviewSource.repositoryBindingDigest,
|
|
1718
|
+
stateRevision: task.reviewSource.stateRevision,
|
|
1719
|
+
rerunCommand: task.reviewSource.rerunCommand,
|
|
1720
|
+
taskSourceTaskId: task.reviewSource.taskId,
|
|
1721
|
+
createdAt: task.reviewSource.createdAt,
|
|
1722
|
+
});
|
|
1723
|
+
}
|
|
1724
|
+
if (isIsolated && task.reviewSource) {
|
|
1725
|
+
throw new Error(
|
|
1726
|
+
"source-aware review tasks cannot use generic task isolation; dispatch them against the coordinator-owned source snapshot",
|
|
1727
|
+
);
|
|
1728
|
+
}
|
|
1705
1729
|
if (!isIsolated) {
|
|
1706
1730
|
await assertExecutionRootMatchesRepositoryBinding(this.session.cwd, taskRepositoryBinding);
|
|
1707
1731
|
const result = await runSubprocess({
|
|
@@ -1756,11 +1780,32 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
1756
1780
|
parentTelemetry: this.session.getTelemetry?.(),
|
|
1757
1781
|
forkContextSeed,
|
|
1758
1782
|
});
|
|
1783
|
+
const reviewSourceDisposition = task.reviewSource
|
|
1784
|
+
? await reconcileReviewSourceDelivery({
|
|
1785
|
+
cwd: this.session.cwd,
|
|
1786
|
+
repositoryBinding: taskRepositoryBinding,
|
|
1787
|
+
reviewSource: task.reviewSource,
|
|
1788
|
+
})
|
|
1789
|
+
: undefined;
|
|
1790
|
+
const reviewSource =
|
|
1791
|
+
task.reviewSource && reviewSourceDisposition
|
|
1792
|
+
? {
|
|
1793
|
+
...task.reviewSource,
|
|
1794
|
+
...reviewSourceDisposition,
|
|
1795
|
+
...(await classifyUltragoalReviewDelivery({
|
|
1796
|
+
cwd: this.session.cwd,
|
|
1797
|
+
cohortId: task.reviewSource.cohortId,
|
|
1798
|
+
dispatchId: task.reviewSource.dispatchId,
|
|
1799
|
+
observedDisposition: reviewSourceDisposition.disposition,
|
|
1800
|
+
})),
|
|
1801
|
+
}
|
|
1802
|
+
: undefined;
|
|
1759
1803
|
return {
|
|
1760
1804
|
...result,
|
|
1761
1805
|
...(forkContext ? { forkContext } : {}),
|
|
1762
1806
|
forkContextAdvisory,
|
|
1763
1807
|
repositoryBinding: publicRepositoryBinding(taskRepositoryBinding),
|
|
1808
|
+
...(reviewSource ? { reviewSource } : {}),
|
|
1764
1809
|
};
|
|
1765
1810
|
}
|
|
1766
1811
|
|
|
@@ -3,6 +3,7 @@ import type { AgentProgress, TaskToolDetails } from "./types";
|
|
|
3
3
|
export type ProviderRetryKind = NonNullable<AgentProgress["retryState"]>["kind"];
|
|
4
4
|
|
|
5
5
|
const FIRST_EVENT_TIMEOUT_PATTERN = /stream timed out while waiting for the first event/i;
|
|
6
|
+
const FIRST_EVENT_TIMEOUT_WITHOUT_ARTICLE_MESSAGE = "Provider stream timed out while waiting for first event";
|
|
6
7
|
const IDLE_STREAM_STALL_PATTERN = /stream stalled while waiting for the next event/i;
|
|
7
8
|
const STREAM_FIRST_EVENT_TIMEOUT_PROVIDER_CODE = "stream_first_event_timeout";
|
|
8
9
|
|
|
@@ -22,7 +23,9 @@ export function classifyProviderRetryFromTransport(facts: {
|
|
|
22
23
|
return "first_event_timeout";
|
|
23
24
|
}
|
|
24
25
|
const errorMessage = facts.errorMessage ?? "";
|
|
25
|
-
if (FIRST_EVENT_TIMEOUT_PATTERN.test(errorMessage))
|
|
26
|
+
if (FIRST_EVENT_TIMEOUT_PATTERN.test(errorMessage) || errorMessage === FIRST_EVENT_TIMEOUT_WITHOUT_ARTICLE_MESSAGE) {
|
|
27
|
+
return "first_event_timeout";
|
|
28
|
+
}
|
|
26
29
|
if (IDLE_STREAM_STALL_PATTERN.test(errorMessage)) return "idle_stream_stall";
|
|
27
30
|
return "provider_error";
|
|
28
31
|
}
|
package/src/task/receipt.ts
CHANGED
|
@@ -67,6 +67,9 @@ export interface TaskResultReceipt {
|
|
|
67
67
|
forkContextAdvisory?: SingleResult["forkContextAdvisory"];
|
|
68
68
|
/** Resolved repository identity for this delegated lane (#2901). */
|
|
69
69
|
repositoryBinding?: SingleResult["repositoryBinding"];
|
|
70
|
+
/** Runtime-derived review source disposition and rerun guidance (#3469). */
|
|
71
|
+
reviewSource?: SingleResult["reviewSource"];
|
|
72
|
+
|
|
70
73
|
roi?: TaskRoi;
|
|
71
74
|
}
|
|
72
75
|
|
|
@@ -311,6 +314,7 @@ export function buildTaskReceipt(raw: SingleResult): TaskResultReceipt {
|
|
|
311
314
|
forkContext: raw.forkContext,
|
|
312
315
|
forkContextAdvisory: raw.forkContextAdvisory,
|
|
313
316
|
repositoryBinding: raw.repositoryBinding,
|
|
317
|
+
reviewSource: raw.reviewSource,
|
|
314
318
|
roi: buildTaskRoi(raw),
|
|
315
319
|
};
|
|
316
320
|
}
|
package/src/task/render.ts
CHANGED
|
@@ -916,6 +916,36 @@ function renderAgentResult(result: TaskResultReceipt, isLast: boolean, expanded:
|
|
|
916
916
|
if (result.roi?.lowRoi) {
|
|
917
917
|
lines.push(`${continuePrefix}${theme.fg("warning", "low ROI: produced no material contribution")}`);
|
|
918
918
|
}
|
|
919
|
+
if (result.reviewSource?.disposition === "stale_review_delivery") {
|
|
920
|
+
lines.push(
|
|
921
|
+
`${continuePrefix}${theme.fg(
|
|
922
|
+
"warning",
|
|
923
|
+
truncateToWidth(
|
|
924
|
+
replaceTabs(
|
|
925
|
+
sanitizeText(
|
|
926
|
+
`STALE REVIEW: source advanced after dispatch; excluded from review gates. Rerun: ${result.reviewSource.rerunCommand}`,
|
|
927
|
+
),
|
|
928
|
+
),
|
|
929
|
+
|
|
930
|
+
100,
|
|
931
|
+
),
|
|
932
|
+
)}`,
|
|
933
|
+
);
|
|
934
|
+
} else if (result.reviewSource?.disposition === "invalid_provenance") {
|
|
935
|
+
lines.push(
|
|
936
|
+
`${continuePrefix}${theme.fg(
|
|
937
|
+
"error",
|
|
938
|
+
truncateToWidth(
|
|
939
|
+
replaceTabs(
|
|
940
|
+
sanitizeText(
|
|
941
|
+
`INVALID REVIEW PROVENANCE: receipt cannot satisfy review gates. Rerun: ${result.reviewSource.rerunCommand}`,
|
|
942
|
+
),
|
|
943
|
+
),
|
|
944
|
+
100,
|
|
945
|
+
),
|
|
946
|
+
)}`,
|
|
947
|
+
);
|
|
948
|
+
}
|
|
919
949
|
|
|
920
950
|
if (result.persistence?.recoveryRef) {
|
|
921
951
|
const label = result.persistence.ownerWorktreeApplied ? "Recovery patch" : "Unapplied recovery patch";
|
package/src/task/types.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { ThinkingLevel } from "@gajae-code/agent-core";
|
|
|
2
2
|
import type { Usage } from "@gajae-code/ai";
|
|
3
3
|
import { $env } from "@gajae-code/utils";
|
|
4
4
|
import * as z from "zod/v4";
|
|
5
|
+
import type { ReviewDeliveryDisposition, ReviewSourceLane } from "../gjc-runtime/ultragoal-review-source";
|
|
5
6
|
import { isValidTaskId, TASK_ID_DESCRIPTION } from "./id";
|
|
6
7
|
import type { TaskResultReceipt } from "./receipt";
|
|
7
8
|
import type { SpawnRoiReconciliation } from "./roi-reconciliation";
|
|
@@ -114,8 +115,26 @@ const createTaskItemSchema = (_contextEnabled: boolean) =>
|
|
|
114
115
|
.describe(
|
|
115
116
|
"authoritative repository identity; omitted items are stamped from session cwd before discovery/spawn and still fail closed on sibling drift",
|
|
116
117
|
),
|
|
118
|
+
reviewSource: reviewSourceSchema.optional(),
|
|
117
119
|
});
|
|
118
120
|
|
|
121
|
+
const reviewSourceSchema = z
|
|
122
|
+
.object({
|
|
123
|
+
schema: z.literal("gjc.review_source_dispatch.v1"),
|
|
124
|
+
cohortId: z.string().min(1),
|
|
125
|
+
generation: z.number().int().min(1),
|
|
126
|
+
lane: z.enum(["cleaner", "architect", "qa", "critic"]),
|
|
127
|
+
snapshotId: z.string().startsWith("sha256:"),
|
|
128
|
+
repositoryBindingDigest: z.string().startsWith("sha256:"),
|
|
129
|
+
stateRevision: z.number().int().min(0),
|
|
130
|
+
dispatchId: z.string().min(1),
|
|
131
|
+
rerunCommand: z.string().min(1),
|
|
132
|
+
taskId: z.string().min(1),
|
|
133
|
+
createdAt: z.string().min(1),
|
|
134
|
+
})
|
|
135
|
+
.strict()
|
|
136
|
+
.describe("leader-issued immutable review source identity; review lanes only");
|
|
137
|
+
|
|
119
138
|
/** Single task item for parallel execution (default shape with context enabled). */
|
|
120
139
|
export const taskItemSchema = createTaskItemSchema(true);
|
|
121
140
|
export type TaskItem = z.infer<typeof taskItemSchema>;
|
|
@@ -524,6 +543,21 @@ export interface SingleResult {
|
|
|
524
543
|
head?: string;
|
|
525
544
|
branch?: string;
|
|
526
545
|
};
|
|
546
|
+
/** Leader-issued immutable source identity for source-aware review lanes (#3469). */
|
|
547
|
+
reviewSource?: {
|
|
548
|
+
schema: "gjc.review_source_dispatch.v1";
|
|
549
|
+
cohortId: string;
|
|
550
|
+
generation: number;
|
|
551
|
+
lane: ReviewSourceLane;
|
|
552
|
+
snapshotId: string;
|
|
553
|
+
repositoryBindingDigest: string;
|
|
554
|
+
stateRevision: number;
|
|
555
|
+
dispatchId: string;
|
|
556
|
+
rerunCommand: string;
|
|
557
|
+
disposition: ReviewDeliveryDisposition;
|
|
558
|
+
currentSnapshotId?: string;
|
|
559
|
+
deliveryId?: string;
|
|
560
|
+
};
|
|
527
561
|
}
|
|
528
562
|
|
|
529
563
|
/** True only for complete, factual five-bucket cost accounting. */
|