@tt-a1i/openpi 0.4.0 → 0.6.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/README.md +116 -46
- package/SETUP.md +29 -7
- package/THIRD_PARTY_NOTICES.md +16 -0
- package/assets/openpi-launch-card-v1.webp +0 -0
- package/bin/openpi.js +155 -0
- package/extensions/ai-providers/LICENSE.upstream +23 -0
- package/extensions/ai-providers/README.md +59 -0
- package/extensions/ai-providers/antigravity/credentials.ts +52 -0
- package/extensions/ai-providers/antigravity/discovery.ts +130 -0
- package/extensions/ai-providers/antigravity/google-conversion.ts +455 -0
- package/extensions/ai-providers/antigravity/models.ts +84 -0
- package/extensions/ai-providers/antigravity/oauth.ts +700 -0
- package/extensions/ai-providers/antigravity/provider.ts +1116 -0
- package/extensions/ai-providers/antigravity/routing.ts +340 -0
- package/extensions/ai-providers/antigravity/with-resolvers.d.ts +19 -0
- package/extensions/ai-providers/cursor/constants.ts +5 -0
- package/extensions/ai-providers/cursor/credentials.ts +14 -0
- package/extensions/ai-providers/cursor/discovery.ts +291 -0
- package/extensions/ai-providers/cursor/input-images.ts +106 -0
- package/extensions/ai-providers/cursor/models.ts +45 -0
- package/extensions/ai-providers/cursor/oauth.ts +263 -0
- package/extensions/ai-providers/cursor/proto.ts +1064 -0
- package/extensions/ai-providers/cursor/protobuf.ts +1171 -0
- package/extensions/ai-providers/cursor/provider.ts +1175 -0
- package/extensions/ai-providers/cursor/proxy.ts +213 -0
- package/extensions/ai-providers/cursor/with-resolvers.d.ts +12 -0
- package/extensions/ai-providers/index.ts +86 -0
- package/extensions/ai-providers/oauth-adapter.ts +81 -0
- package/extensions/ai-providers/usage.ts +10 -0
- package/extensions/background-terminals/index.ts +38 -3
- package/extensions/background-terminals/src/domain.ts +2 -0
- package/extensions/background-terminals/src/manager.ts +484 -106
- package/extensions/background-terminals/src/output.ts +33 -0
- package/extensions/background-terminals/src/prompt.ts +13 -5
- package/extensions/background-terminals/src/result-delivery.ts +47 -24
- package/extensions/clear-context/index.ts +83 -0
- package/extensions/context-pivot/index.ts +16 -6
- package/extensions/cron/index.ts +68 -27
- package/extensions/cron/schedule.ts +12 -2
- package/extensions/file-mutation-display/render.ts +17 -257
- package/extensions/file-search/src/binaries.ts +57 -41
- package/extensions/git-read/index.ts +1 -3
- package/extensions/model-info/cache-diagnostics.ts +220 -0
- package/extensions/model-info/index.ts +65 -33
- package/extensions/model-info/session-metrics.ts +96 -0
- package/extensions/plan-mode/bash-policy.ts +54 -9
- package/extensions/plan-mode/index.ts +82 -6
- package/extensions/post-edit/index.ts +16 -6
- package/extensions/sessions/git-stats.ts +258 -72
- package/extensions/sessions/index.ts +153 -86
- package/extensions/sessions/preview-cache.ts +104 -0
- package/extensions/sessions/preview-loader.ts +856 -0
- package/extensions/sessions/sessions.ts +43 -4
- package/extensions/setup/index.ts +138 -130
- package/extensions/shared/activity-status.ts +30 -0
- package/extensions/shared/agent-session-page.ts +319 -0
- package/extensions/shared/agent-tool-renderer.ts +218 -0
- package/extensions/shared/agent-transcript.ts +524 -0
- package/extensions/shared/capability-intent.ts +1 -1
- package/extensions/shared/child-session.ts +457 -21
- package/extensions/shared/completion-inbox.ts +193 -0
- package/extensions/shared/result-delivery.ts +34 -0
- package/extensions/shared/setup-config.ts +83 -34
- package/extensions/shared/setup-episode-state.ts +1 -1
- package/extensions/shared/structured-output.ts +154 -0
- package/extensions/shared/terminal-text.ts +110 -23
- package/extensions/shared/text-projection.ts +72 -15
- package/extensions/shared/tool-activity.ts +382 -0
- package/extensions/shared/tool-surface.ts +29 -2
- package/extensions/shared/transcript-viewport.ts +46 -0
- package/extensions/shared/web-observer-registry.ts +390 -0
- package/extensions/shared/worktree.ts +11 -0
- package/extensions/subagents/index.ts +313 -62
- package/extensions/subagents/navigation.ts +34 -5
- package/extensions/subagents/src/backend.ts +12 -1
- package/extensions/subagents/src/backends/pi.ts +450 -70
- package/extensions/subagents/src/domain.ts +21 -1
- package/extensions/subagents/src/manager.ts +39 -2
- package/extensions/subagents/src/prompt.ts +49 -7
- package/extensions/subagents/src/result-artifact.ts +36 -0
- package/extensions/subagents/src/result-delivery.ts +39 -14
- package/extensions/subagents/src/runtime.ts +15 -1
- package/extensions/subagents/src/ui/takeover.ts +73 -257
- package/extensions/subagents/src/ui/transcript.ts +38 -535
- package/extensions/subagents/src/ui/wait-result.ts +103 -15
- package/extensions/suggestions/src/ui.ts +10 -4
- package/extensions/tasks/index.ts +0 -3
- package/extensions/ui-customization/footer.ts +16 -45
- package/extensions/ui-customization/index.ts +0 -4
- package/extensions/user-input-fold/index.ts +42 -6
- package/extensions/web/index.ts +257 -0
- package/extensions/workflows/acceptance.ts +43 -19
- package/extensions/workflows/artifacts.ts +137 -47
- package/extensions/workflows/completion-projection.ts +459 -0
- package/extensions/workflows/coordinator.ts +8 -10
- package/extensions/workflows/dashboard.ts +175 -228
- package/extensions/workflows/handoff.ts +70 -16
- package/extensions/workflows/index.ts +501 -198
- package/extensions/workflows/journal.ts +148 -13
- package/extensions/workflows/model.ts +79 -5
- package/extensions/workflows/navigation.ts +32 -8
- package/extensions/workflows/progress-projection.ts +306 -0
- package/extensions/workflows/prompt.ts +70 -16
- package/extensions/workflows/replay-safety.ts +42 -21
- package/extensions/workflows/result-delivery.ts +214 -76
- package/extensions/workflows/retention.ts +599 -0
- package/extensions/workflows/runner.ts +389 -345
- package/extensions/workflows/sandbox-child.cjs +25 -3
- package/extensions/workflows/sandbox.ts +62 -8
- package/extensions/workflows/serialization.ts +325 -17
- package/extensions/workflows/tool-renderer.ts +22 -0
- package/extensions/workflows/transcript.ts +149 -0
- package/extensions/workspace-cleanup-guard/index.ts +54 -0
- package/extensions/workspace-cleanup-guard/workspace-provenance.ts +563 -0
- package/package.json +34 -14
- package/skills/subagents/REFERENCE.md +190 -0
- package/skills/subagents/SKILL.md +2 -1
- package/skills/workflows/REFERENCE.md +6 -4
- package/skills/workflows/SKILL.md +1 -1
- package/web/adapter/pi-adapter.ts +664 -0
- package/web/host/browser-launcher.ts +20 -0
- package/web/host/pi-coding-agent-entry.ts +162 -0
- package/web/host/static-assets.ts +4 -0
- package/web/host/terminal-status.ts +38 -0
- package/web/host/web-host.ts +1069 -0
- package/web/http-dispatcher.ts +125 -0
- package/web/protocol/types.ts +467 -0
- package/web/runtime/pi-runtime.ts +1206 -0
- package/web/runtime/types.ts +102 -0
- package/web/runtime/web-host-lease.ts +497 -0
- package/web/trace.ts +18 -0
- package/web/ui/app.js +1700 -0
- package/web/ui/index.html +142 -0
- package/web/ui/styles.css +680 -0
- package/web/vite.config.mjs +34 -0
- package/extensions/execution-convergence/active-evidence.ts +0 -129
- package/extensions/execution-convergence/index.ts +0 -442
- package/extensions/execution-convergence/workspace-provenance.ts +0 -338
- package/extensions/setup/intercom-fs-helper.cjs +0 -130
- package/extensions/setup/intercom.ts +0 -603
- package/extensions/subagents/src/backends/stub.ts +0 -303
|
@@ -0,0 +1,459 @@
|
|
|
1
|
+
import { allocateResultBudgets } from "../shared/result-budget.ts";
|
|
2
|
+
import { sanitizeTerminalText } from "../shared/terminal-text.ts";
|
|
3
|
+
import { projectText } from "../shared/text-projection.ts";
|
|
4
|
+
import {
|
|
5
|
+
countStates,
|
|
6
|
+
formatElapsed,
|
|
7
|
+
resultJson,
|
|
8
|
+
sanitizeWorkflowDisplayLine,
|
|
9
|
+
shortenHome,
|
|
10
|
+
statusWord,
|
|
11
|
+
type WorkflowDetails,
|
|
12
|
+
type WorkflowLogEntry,
|
|
13
|
+
type WorkflowStatus,
|
|
14
|
+
} from "./model.ts";
|
|
15
|
+
import { safeStringify } from "./serialization.ts";
|
|
16
|
+
|
|
17
|
+
const MAX_DISPLAY_ENTRIES = 64;
|
|
18
|
+
const MAX_DISPLAY_BYTES = 64 * 1024;
|
|
19
|
+
const MAX_EXPANDED_ENTRY_BYTES = 48 * 1024;
|
|
20
|
+
const MAX_ALERTS = 16;
|
|
21
|
+
const MAX_FIELD_BYTES = 2 * 1024;
|
|
22
|
+
|
|
23
|
+
export interface WorkflowCompletionSourceEntry {
|
|
24
|
+
deliveryId: string;
|
|
25
|
+
details: WorkflowDetails;
|
|
26
|
+
runDir: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Bounded operator-facing facts. Runtime state remains in artifacts/model context. */
|
|
30
|
+
export interface WorkflowCompletionDisplayEntry {
|
|
31
|
+
deliveryId: string;
|
|
32
|
+
runId: string;
|
|
33
|
+
status: WorkflowStatus;
|
|
34
|
+
summary: string;
|
|
35
|
+
alerts: string[];
|
|
36
|
+
resultPreview?: string;
|
|
37
|
+
expanded: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface WorkflowCompletionDisplay {
|
|
41
|
+
version: 1;
|
|
42
|
+
/** Backward-compatible single-run identity for delivery observers. */
|
|
43
|
+
runId?: string;
|
|
44
|
+
entries: WorkflowCompletionDisplayEntry[];
|
|
45
|
+
omittedEntries?: number;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function boundedLine(value: string, maxBytes = MAX_FIELD_BYTES) {
|
|
49
|
+
return projectText(sanitizeWorkflowDisplayLine(value), {
|
|
50
|
+
maxBytes,
|
|
51
|
+
maxLines: 1,
|
|
52
|
+
recovery: "",
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function labels(details: WorkflowDetails, state: "error" | "uncertain") {
|
|
57
|
+
return details.agents
|
|
58
|
+
.filter((agent) => agent.state === state)
|
|
59
|
+
.map((agent) => sanitizeWorkflowDisplayLine(agent.label));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function completionSummary(details: WorkflowDetails) {
|
|
63
|
+
const { done, failed } = countStates(details);
|
|
64
|
+
const elapsed = formatElapsed(details.startedAt, details.finishedAt);
|
|
65
|
+
return boundedLine(
|
|
66
|
+
`workflow ${details.name ?? details.runId} · ${done + failed}/${details.agents.length} agents · ${elapsed} · ${statusWord(details.status)}`,
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function isDroppedWorkLog(entry: WorkflowLogEntry) {
|
|
71
|
+
if (entry.kind === "pipeline-drop") return true;
|
|
72
|
+
const text = entry.text;
|
|
73
|
+
const negated =
|
|
74
|
+
/\b(?:no|none|nothing|not|zero|0)\b.{0,48}\b(?:dropped|discarded|omitted)\b/iu.test(
|
|
75
|
+
text,
|
|
76
|
+
) ||
|
|
77
|
+
/(?:没有|并未|未曾|未|无|零(?:个|项)?).{0,24}(?:丢弃|丢失|遗漏)/u.test(
|
|
78
|
+
text,
|
|
79
|
+
);
|
|
80
|
+
if (negated) return false;
|
|
81
|
+
return (
|
|
82
|
+
/\b(?:dropped|discarded|omitted)\b/iu.test(text) ||
|
|
83
|
+
/(?:被)?(?:丢弃|丢失|遗漏)/u.test(text)
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** Exceptional evidence that must remain visible while the report is collapsed. */
|
|
88
|
+
function completionAlerts(details: WorkflowDetails) {
|
|
89
|
+
const alerts: string[] = [];
|
|
90
|
+
if (details.error) alerts.push(`Error: ${details.error}`);
|
|
91
|
+
|
|
92
|
+
const failed = labels(details, "error");
|
|
93
|
+
if (failed.length > 0) alerts.push(`Failed agents: ${failed.join(", ")}`);
|
|
94
|
+
const uncertain = labels(details, "uncertain");
|
|
95
|
+
if (uncertain.length > 0) {
|
|
96
|
+
alerts.push(`Uncertain agents: ${uncertain.join(", ")}`);
|
|
97
|
+
}
|
|
98
|
+
if (details.logsDropped) {
|
|
99
|
+
alerts.push(`${details.logsDropped} earlier log line(s) dropped`);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
for (const entry of details.logs ?? []) {
|
|
103
|
+
if (!isDroppedWorkLog(entry)) continue;
|
|
104
|
+
alerts.push(`Dropped work: ${sanitizeWorkflowDisplayLine(entry.text)}`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
for (const agent of details.agents) {
|
|
108
|
+
if (agent.worktreePath) {
|
|
109
|
+
const reason = agent.worktreeCleanup?.reason ?? "cleanup was unsafe";
|
|
110
|
+
alerts.push(
|
|
111
|
+
`Retained worktree [${sanitizeWorkflowDisplayLine(agent.label)}]: ${shortenHome(agent.worktreePath)} (${sanitizeWorkflowDisplayLine(reason)})${
|
|
112
|
+
agent.worktreeHandoffArtifact
|
|
113
|
+
? `; handoff ${sanitizeWorkflowDisplayLine(agent.worktreeHandoffArtifact)}`
|
|
114
|
+
: ""
|
|
115
|
+
}`,
|
|
116
|
+
);
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
if (!agent.worktreeHandoffArtifact) continue;
|
|
120
|
+
const cleanup = agent.worktreeCleanup;
|
|
121
|
+
const work = cleanup?.commits
|
|
122
|
+
? `${cleanup.commits} commit${cleanup.commits === 1 ? "" : "s"} on ${cleanup.branch}`
|
|
123
|
+
: agent.worktreeBranch
|
|
124
|
+
? `branch ${agent.worktreeBranch}`
|
|
125
|
+
: "isolated work";
|
|
126
|
+
alerts.push(
|
|
127
|
+
`Worktree handoff [${sanitizeWorkflowDisplayLine(agent.label)}]: ${sanitizeWorkflowDisplayLine(work)}; ${sanitizeWorkflowDisplayLine(agent.worktreeHandoffArtifact)}`,
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const unique = [...new Set(alerts)].map((alert) =>
|
|
132
|
+
sanitizeTerminalText(boundedLine(alert, 512)),
|
|
133
|
+
);
|
|
134
|
+
if (unique.length <= MAX_ALERTS) return unique;
|
|
135
|
+
return [
|
|
136
|
+
...unique.slice(0, MAX_ALERTS - 1),
|
|
137
|
+
`${unique.length - MAX_ALERTS + 1} more exceptional item(s); expand for evidence`,
|
|
138
|
+
];
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** Short return-value preview for collapsed success cards. */
|
|
142
|
+
function completionResultPreview(details: WorkflowDetails) {
|
|
143
|
+
if (details.result === undefined) return undefined;
|
|
144
|
+
if (typeof details.result === "string") return boundedLine(details.result);
|
|
145
|
+
const serialized = safeStringify(details.result, { maxBytes: 2 * 1024 });
|
|
146
|
+
try {
|
|
147
|
+
return boundedLine(JSON.stringify(JSON.parse(serialized)));
|
|
148
|
+
} catch {
|
|
149
|
+
return boundedLine(resultJson(details.result));
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** Operator evidence, intentionally independent from the model transport report. */
|
|
154
|
+
function buildOperatorReport(
|
|
155
|
+
details: WorkflowDetails,
|
|
156
|
+
runDir: string,
|
|
157
|
+
deliveryId: string,
|
|
158
|
+
) {
|
|
159
|
+
const { done, failed, uncertain } = countStates(details);
|
|
160
|
+
const elapsed = formatElapsed(details.startedAt, details.finishedAt);
|
|
161
|
+
const lines = [
|
|
162
|
+
`Workflow ${details.name ? `"${details.name}"` : details.runId} ${details.status} — ${done}/${details.agents.length} agents ok${failed ? `, ${failed} failed` : ""}${uncertain ? `, ${uncertain} uncertain` : ""} across ${details.phases.length} phase(s) in ${elapsed}.`,
|
|
163
|
+
`Run dir: ${shortenHome(runDir)}`,
|
|
164
|
+
`Delivery id: ${deliveryId}`,
|
|
165
|
+
];
|
|
166
|
+
|
|
167
|
+
const artifacts = [
|
|
168
|
+
details.resultArtifact ? `Result: ${details.resultArtifact}` : undefined,
|
|
169
|
+
details.transcriptArtifact
|
|
170
|
+
? `Transcripts: ${details.transcriptArtifact}`
|
|
171
|
+
: undefined,
|
|
172
|
+
].filter((entry): entry is string => entry !== undefined);
|
|
173
|
+
if (artifacts.length > 0) lines.push("", "Artifacts:", ...artifacts);
|
|
174
|
+
|
|
175
|
+
const replayed = details.agents.filter((agent) => agent.replayed).length;
|
|
176
|
+
if (details.resumedFrom) {
|
|
177
|
+
lines.push(
|
|
178
|
+
`Resumed from ${details.resumedFrom}: replayed ${replayed}/${details.agents.length} agent call(s), ran ${details.agents.length - replayed} for real.`,
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
if (details.resumeNote) lines.push(`Resume: ${details.resumeNote}`);
|
|
182
|
+
if (details.error) lines.push(`Error: ${details.error}`);
|
|
183
|
+
|
|
184
|
+
if (details.logs?.length) {
|
|
185
|
+
lines.push("", "Log:");
|
|
186
|
+
if (details.logsDropped) {
|
|
187
|
+
lines.push(` (${details.logsDropped} earlier line(s) dropped)`);
|
|
188
|
+
}
|
|
189
|
+
for (const entry of details.logs) lines.push(` ${entry.text}`);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const isolated = details.agents.filter(
|
|
193
|
+
(agent) => agent.worktreeBranch || agent.worktreePath,
|
|
194
|
+
);
|
|
195
|
+
if (isolated.length > 0) {
|
|
196
|
+
lines.push("", "Isolated worktrees:");
|
|
197
|
+
for (const agent of isolated) {
|
|
198
|
+
const cleanup = agent.worktreeCleanup;
|
|
199
|
+
const work = cleanup?.commits
|
|
200
|
+
? `${cleanup.commits} commit${cleanup.commits === 1 ? "" : "s"} on ${cleanup.branch}`
|
|
201
|
+
: agent.worktreeBranch
|
|
202
|
+
? `committed to branch ${agent.worktreeBranch}`
|
|
203
|
+
: "no commits";
|
|
204
|
+
lines.push(
|
|
205
|
+
`- [${agent.label}] ${work}${
|
|
206
|
+
agent.worktreePath
|
|
207
|
+
? `; kept at ${shortenHome(agent.worktreePath)} (${cleanup?.reason ?? "uncommitted changes"})`
|
|
208
|
+
: cleanup?.branchDeleted
|
|
209
|
+
? "; empty branch deleted"
|
|
210
|
+
: cleanup?.reason
|
|
211
|
+
? `; cleanup warning: ${cleanup.reason}`
|
|
212
|
+
: ""
|
|
213
|
+
}${agent.worktreeHandoffArtifact ? `; handoff ${agent.worktreeHandoffArtifact}` : ""}`,
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (details.agents.length > 0) {
|
|
219
|
+
lines.push("", "Agents:");
|
|
220
|
+
for (const agent of details.agents) {
|
|
221
|
+
const state =
|
|
222
|
+
agent.state === "done"
|
|
223
|
+
? agent.replayed
|
|
224
|
+
? "ok (replayed)"
|
|
225
|
+
: "ok"
|
|
226
|
+
: agent.state === "error"
|
|
227
|
+
? "FAILED"
|
|
228
|
+
: agent.state === "uncertain"
|
|
229
|
+
? "UNCERTAIN"
|
|
230
|
+
: "running";
|
|
231
|
+
lines.push(
|
|
232
|
+
`- [${agent.label}]${agent.phase ? ` (${agent.phase})` : ""} ${state}` +
|
|
233
|
+
(agent.acceptance
|
|
234
|
+
? ` · deprecated model self-attestation ${agent.acceptance.status}`
|
|
235
|
+
: "") +
|
|
236
|
+
(agent.error ? ` — ${agent.error}` : ""),
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
if (details.result !== undefined) {
|
|
241
|
+
lines.push("", "Result:", resultJson(details.result));
|
|
242
|
+
}
|
|
243
|
+
return sanitizeTerminalText(lines.join("\n"));
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/** Build a byte-bounded display projection without retaining runtime objects. */
|
|
247
|
+
export function buildWorkflowCompletionDisplay(
|
|
248
|
+
sourceEntries: readonly WorkflowCompletionSourceEntry[],
|
|
249
|
+
): WorkflowCompletionDisplay {
|
|
250
|
+
const selected = sourceEntries.slice(0, MAX_DISPLAY_ENTRIES);
|
|
251
|
+
const fixedEntry = ({
|
|
252
|
+
deliveryId,
|
|
253
|
+
details,
|
|
254
|
+
}: WorkflowCompletionSourceEntry) => ({
|
|
255
|
+
deliveryId: boundedLine(deliveryId, 512),
|
|
256
|
+
runId: boundedLine(details.runId, 512),
|
|
257
|
+
status: details.status,
|
|
258
|
+
summary: completionSummary(details),
|
|
259
|
+
alerts: completionAlerts(details),
|
|
260
|
+
resultPreview: completionResultPreview(details),
|
|
261
|
+
expanded: "",
|
|
262
|
+
});
|
|
263
|
+
let fixedEntries = selected.map(fixedEntry);
|
|
264
|
+
const fixedSize = () =>
|
|
265
|
+
Buffer.byteLength(
|
|
266
|
+
JSON.stringify({
|
|
267
|
+
version: 1,
|
|
268
|
+
...(sourceEntries.length === 1
|
|
269
|
+
? { runId: fixedEntries[0]?.runId ?? "" }
|
|
270
|
+
: {}),
|
|
271
|
+
entries: fixedEntries,
|
|
272
|
+
...(sourceEntries.length > selected.length
|
|
273
|
+
? { omittedEntries: sourceEntries.length - selected.length }
|
|
274
|
+
: {}),
|
|
275
|
+
}),
|
|
276
|
+
"utf8",
|
|
277
|
+
);
|
|
278
|
+
while (selected.length > 1 && fixedSize() > MAX_DISPLAY_BYTES / 2) {
|
|
279
|
+
selected.pop();
|
|
280
|
+
fixedEntries.pop();
|
|
281
|
+
}
|
|
282
|
+
const fixedBytes = fixedSize();
|
|
283
|
+
const reports = selected.map(({ deliveryId, details, runDir }) =>
|
|
284
|
+
buildOperatorReport(details, runDir, deliveryId),
|
|
285
|
+
);
|
|
286
|
+
const allocation = allocateResultBudgets(
|
|
287
|
+
reports.map((report) => Buffer.byteLength(report, "utf8")),
|
|
288
|
+
undefined,
|
|
289
|
+
{
|
|
290
|
+
maxBatchBytes: Math.max(0, MAX_DISPLAY_BYTES - fixedBytes),
|
|
291
|
+
maxResultBytes: MAX_EXPANDED_ENTRY_BYTES,
|
|
292
|
+
minResultBytes: 512,
|
|
293
|
+
headroomShare: 0,
|
|
294
|
+
estimatedBytesPerToken: 4,
|
|
295
|
+
},
|
|
296
|
+
);
|
|
297
|
+
let budgets = [...allocation.budgets];
|
|
298
|
+
const projectedEntries = () =>
|
|
299
|
+
fixedEntries.map((entry, index) => ({
|
|
300
|
+
...entry,
|
|
301
|
+
expanded: projectText(reports[index]!, {
|
|
302
|
+
maxBytes: budgets[index] ?? 0,
|
|
303
|
+
maxLines: 600,
|
|
304
|
+
recovery: `Full workflow evidence is available in ${shortenHome(selected[index]!.runDir)}.`,
|
|
305
|
+
}),
|
|
306
|
+
}));
|
|
307
|
+
let entries = projectedEntries();
|
|
308
|
+
const projectedSize = () =>
|
|
309
|
+
Buffer.byteLength(
|
|
310
|
+
JSON.stringify({
|
|
311
|
+
version: 1,
|
|
312
|
+
...(sourceEntries.length === 1
|
|
313
|
+
? { runId: entries[0]?.runId ?? "" }
|
|
314
|
+
: {}),
|
|
315
|
+
entries,
|
|
316
|
+
...(sourceEntries.length > selected.length
|
|
317
|
+
? { omittedEntries: sourceEntries.length - selected.length }
|
|
318
|
+
: {}),
|
|
319
|
+
}),
|
|
320
|
+
"utf8",
|
|
321
|
+
);
|
|
322
|
+
for (let attempt = 0; projectedSize() > MAX_DISPLAY_BYTES; attempt++) {
|
|
323
|
+
if (attempt >= 8) {
|
|
324
|
+
budgets = budgets.map(() => 0);
|
|
325
|
+
} else {
|
|
326
|
+
const expandedBytes = budgets.reduce((sum, budget) => sum + budget, 0);
|
|
327
|
+
const overflow = projectedSize() - MAX_DISPLAY_BYTES;
|
|
328
|
+
const target = Math.max(
|
|
329
|
+
0,
|
|
330
|
+
expandedBytes - overflow - entries.length * 16,
|
|
331
|
+
);
|
|
332
|
+
const scale = expandedBytes > 0 ? target / expandedBytes : 0;
|
|
333
|
+
budgets = budgets.map((budget) => Math.floor(budget * scale));
|
|
334
|
+
}
|
|
335
|
+
entries = projectedEntries();
|
|
336
|
+
}
|
|
337
|
+
const display: WorkflowCompletionDisplay = {
|
|
338
|
+
version: 1,
|
|
339
|
+
...(sourceEntries.length === 1
|
|
340
|
+
? { runId: entries[0]?.runId ?? sourceEntries[0]!.details.runId }
|
|
341
|
+
: {}),
|
|
342
|
+
entries,
|
|
343
|
+
...(sourceEntries.length > selected.length
|
|
344
|
+
? { omittedEntries: sourceEntries.length - selected.length }
|
|
345
|
+
: {}),
|
|
346
|
+
};
|
|
347
|
+
return display;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
function isString(value: unknown, maxBytes = MAX_FIELD_BYTES) {
|
|
351
|
+
return (
|
|
352
|
+
typeof value === "string" && Buffer.byteLength(value, "utf8") <= maxBytes
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function isStatus(value: unknown): value is WorkflowStatus {
|
|
357
|
+
return (
|
|
358
|
+
value === "running" ||
|
|
359
|
+
value === "completed" ||
|
|
360
|
+
value === "failed" ||
|
|
361
|
+
value === "aborted" ||
|
|
362
|
+
value === "uncertain"
|
|
363
|
+
);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
function hasOnlyKeys(value: object, allowed: readonly string[]) {
|
|
367
|
+
const keys = new Set(allowed);
|
|
368
|
+
return Object.keys(value).every((key) => keys.has(key));
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
export function isWorkflowCompletionDisplay(
|
|
372
|
+
value: unknown,
|
|
373
|
+
): value is WorkflowCompletionDisplay {
|
|
374
|
+
if (!value || typeof value !== "object") return false;
|
|
375
|
+
let bytes: number;
|
|
376
|
+
try {
|
|
377
|
+
bytes = Buffer.byteLength(JSON.stringify(value), "utf8");
|
|
378
|
+
} catch {
|
|
379
|
+
return false;
|
|
380
|
+
}
|
|
381
|
+
if (bytes > MAX_DISPLAY_BYTES) return false;
|
|
382
|
+
try {
|
|
383
|
+
const candidate = value as Partial<WorkflowCompletionDisplay>;
|
|
384
|
+
if (
|
|
385
|
+
!hasOnlyKeys(candidate, [
|
|
386
|
+
"version",
|
|
387
|
+
"runId",
|
|
388
|
+
"entries",
|
|
389
|
+
"omittedEntries",
|
|
390
|
+
]) ||
|
|
391
|
+
candidate.version !== 1 ||
|
|
392
|
+
!Array.isArray(candidate.entries) ||
|
|
393
|
+
candidate.entries.length > MAX_DISPLAY_ENTRIES ||
|
|
394
|
+
(candidate.runId !== undefined && !isString(candidate.runId, 512)) ||
|
|
395
|
+
(candidate.omittedEntries !== undefined &&
|
|
396
|
+
(!Number.isSafeInteger(candidate.omittedEntries) ||
|
|
397
|
+
candidate.omittedEntries <= 0))
|
|
398
|
+
) {
|
|
399
|
+
return false;
|
|
400
|
+
}
|
|
401
|
+
return candidate.entries.every((entry) => {
|
|
402
|
+
if (!entry || typeof entry !== "object") return false;
|
|
403
|
+
const record = entry as Partial<WorkflowCompletionDisplayEntry>;
|
|
404
|
+
return (
|
|
405
|
+
hasOnlyKeys(record, [
|
|
406
|
+
"deliveryId",
|
|
407
|
+
"runId",
|
|
408
|
+
"status",
|
|
409
|
+
"summary",
|
|
410
|
+
"alerts",
|
|
411
|
+
"resultPreview",
|
|
412
|
+
"expanded",
|
|
413
|
+
]) &&
|
|
414
|
+
isString(record.deliveryId, 512) &&
|
|
415
|
+
isString(record.runId, 512) &&
|
|
416
|
+
isStatus(record.status) &&
|
|
417
|
+
isString(record.summary) &&
|
|
418
|
+
Array.isArray(record.alerts) &&
|
|
419
|
+
record.alerts.length <= MAX_ALERTS &&
|
|
420
|
+
record.alerts.every((alert) => isString(alert, 512)) &&
|
|
421
|
+
(record.resultPreview === undefined ||
|
|
422
|
+
isString(record.resultPreview)) &&
|
|
423
|
+
isString(record.expanded, MAX_EXPANDED_ENTRY_BYTES)
|
|
424
|
+
);
|
|
425
|
+
});
|
|
426
|
+
} catch {
|
|
427
|
+
return false;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
export function workflowCompletionSummary(
|
|
432
|
+
entry: WorkflowCompletionDisplayEntry,
|
|
433
|
+
) {
|
|
434
|
+
return entry.summary;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
export function workflowCompletionAlerts(
|
|
438
|
+
entry: WorkflowCompletionDisplayEntry,
|
|
439
|
+
) {
|
|
440
|
+
return entry.alerts;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
export function workflowCompletionResultPreview(
|
|
444
|
+
entry: WorkflowCompletionDisplayEntry,
|
|
445
|
+
) {
|
|
446
|
+
return entry.resultPreview;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
export function buildExpandedWorkflowCompletion(
|
|
450
|
+
display: WorkflowCompletionDisplay,
|
|
451
|
+
) {
|
|
452
|
+
const reports = display.entries.map((entry) => entry.expanded);
|
|
453
|
+
if (display.omittedEntries) {
|
|
454
|
+
reports.push(
|
|
455
|
+
`${display.omittedEntries} additional workflow completion(s) omitted from this display projection; their full evidence remains in workflow artifacts and model-visible delivery context.`,
|
|
456
|
+
);
|
|
457
|
+
}
|
|
458
|
+
return reports.join("\n\n");
|
|
459
|
+
}
|
|
@@ -3,23 +3,21 @@ export interface WorkflowLaunchPolicyInput {
|
|
|
3
3
|
background?: boolean;
|
|
4
4
|
}
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
/** Resolve legacy/background and host capability without silently changing semantics. */
|
|
12
|
-
export function resolveWorkflowLaunchPolicy(
|
|
6
|
+
/**
|
|
7
|
+
* Resolve the caller's launch preference to one positive runtime mode.
|
|
8
|
+
* `background` remains only as the published inverse compatibility alias.
|
|
9
|
+
*/
|
|
10
|
+
export function resolveWorkflowLaunchMode(
|
|
13
11
|
input: WorkflowLaunchPolicyInput,
|
|
14
12
|
canDeliverLater: boolean,
|
|
15
|
-
)
|
|
13
|
+
) {
|
|
16
14
|
if (
|
|
17
15
|
input.wait !== undefined &&
|
|
18
16
|
input.background !== undefined &&
|
|
19
17
|
input.wait === input.background
|
|
20
18
|
) {
|
|
21
19
|
throw new Error(
|
|
22
|
-
"wait and background conflict: background is the deprecated inverse of wait",
|
|
20
|
+
"wait and background conflict: background is the deprecated inverse of wait; remove background and provide only wait",
|
|
23
21
|
);
|
|
24
22
|
}
|
|
25
23
|
const wait =
|
|
@@ -30,7 +28,7 @@ export function resolveWorkflowLaunchPolicy(
|
|
|
30
28
|
"This host cannot deliver a workflow result later; use wait: true",
|
|
31
29
|
);
|
|
32
30
|
}
|
|
33
|
-
return
|
|
31
|
+
return wait ? "inline" : "detached";
|
|
34
32
|
}
|
|
35
33
|
|
|
36
34
|
/**
|