@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
|
@@ -18,15 +18,10 @@ import {
|
|
|
18
18
|
getAgentDir,
|
|
19
19
|
type KeybindingsManager,
|
|
20
20
|
} from "@earendil-works/pi-coding-agent";
|
|
21
|
-
import {
|
|
22
|
-
|
|
23
|
-
matchesKey,
|
|
24
|
-
type TUI,
|
|
25
|
-
truncateToWidth,
|
|
26
|
-
wrapTextWithAnsi,
|
|
27
|
-
} from "@earendil-works/pi-tui";
|
|
28
|
-
import { contextPercent } from "../shared/context-utilization.ts";
|
|
21
|
+
import { type TUI, truncateToWidth } from "@earendil-works/pi-tui";
|
|
22
|
+
import { AgentSessionPage } from "../shared/agent-session-page.ts";
|
|
29
23
|
import { fitNavigationSides } from "../shared/below-editor-navigation.ts";
|
|
24
|
+
import { contextPercent } from "../shared/context-utilization.ts";
|
|
30
25
|
import {
|
|
31
26
|
panelFrame,
|
|
32
27
|
type ScreenHint,
|
|
@@ -68,10 +63,10 @@ import {
|
|
|
68
63
|
workflowGraphRecords,
|
|
69
64
|
} from "./model.ts";
|
|
70
65
|
import { writeFileAtomic } from "./serialization.ts";
|
|
66
|
+
import { WorkflowTranscriptAdapter } from "./transcript.ts";
|
|
71
67
|
|
|
72
68
|
const NOTICE_TTL_MS = 4000;
|
|
73
69
|
const MIN_HEIGHT = 10;
|
|
74
|
-
const TRANSCRIPT_SCROLL_STEP = 20;
|
|
75
70
|
|
|
76
71
|
function wrapSelection(index: number, delta: number, length: number): number {
|
|
77
72
|
if (length === 0) return 0;
|
|
@@ -225,6 +220,14 @@ function normalizeDelivery(value: unknown): WorkflowDetails["delivery"] {
|
|
|
225
220
|
: 0;
|
|
226
221
|
return {
|
|
227
222
|
id: sanitizeLine(record.id, 256),
|
|
223
|
+
...(typeof record.ownerSessionId === "string" && record.ownerSessionId
|
|
224
|
+
? { ownerSessionId: sanitizeLine(record.ownerSessionId, 256) }
|
|
225
|
+
: {}),
|
|
226
|
+
...(typeof record.ownerEpoch === "number" &&
|
|
227
|
+
Number.isSafeInteger(record.ownerEpoch) &&
|
|
228
|
+
record.ownerEpoch >= 0
|
|
229
|
+
? { ownerEpoch: record.ownerEpoch }
|
|
230
|
+
: {}),
|
|
228
231
|
state,
|
|
229
232
|
attempts,
|
|
230
233
|
updatedAt,
|
|
@@ -261,6 +264,10 @@ function normalizeTranscript(value: unknown): TranscriptEntry[] {
|
|
|
261
264
|
typeof entry.name === "string"
|
|
262
265
|
? sanitizeLine(entry.name, 160) || undefined
|
|
263
266
|
: undefined,
|
|
267
|
+
toolCallId:
|
|
268
|
+
typeof entry.toolCallId === "string"
|
|
269
|
+
? sanitizeLine(entry.toolCallId, 1_024) || undefined
|
|
270
|
+
: undefined,
|
|
264
271
|
isError: entry.isError === true,
|
|
265
272
|
timestamp:
|
|
266
273
|
typeof entry.timestamp === "number" ? entry.timestamp : undefined,
|
|
@@ -269,6 +276,35 @@ function normalizeTranscript(value: unknown): TranscriptEntry[] {
|
|
|
269
276
|
return transcript;
|
|
270
277
|
}
|
|
271
278
|
|
|
279
|
+
function normalizeAgentState(value: unknown): AgentRecord["state"] {
|
|
280
|
+
switch (value) {
|
|
281
|
+
case "done":
|
|
282
|
+
case "completed":
|
|
283
|
+
return "done";
|
|
284
|
+
case "error":
|
|
285
|
+
case "failed":
|
|
286
|
+
return "error";
|
|
287
|
+
case "running":
|
|
288
|
+
case "uncertain":
|
|
289
|
+
return value;
|
|
290
|
+
default:
|
|
291
|
+
return "uncertain";
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function normalizeWorkflowStatus(value: unknown): WorkflowDetails["status"] {
|
|
296
|
+
switch (value) {
|
|
297
|
+
case "running":
|
|
298
|
+
case "completed":
|
|
299
|
+
case "failed":
|
|
300
|
+
case "aborted":
|
|
301
|
+
case "uncertain":
|
|
302
|
+
return value;
|
|
303
|
+
default:
|
|
304
|
+
return "uncertain";
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
272
308
|
/** Leniently normalize a workflow.json (including runs from older tooling). */
|
|
273
309
|
export function normalizePersistedWorkflowDetails(
|
|
274
310
|
runId: string,
|
|
@@ -284,14 +320,7 @@ export function normalizePersistedWorkflowDetails(
|
|
|
284
320
|
for (const item of rawAgents) {
|
|
285
321
|
if (!item || typeof item !== "object") continue;
|
|
286
322
|
const a = item as Record<string, unknown>;
|
|
287
|
-
const state =
|
|
288
|
-
a.state === "error" || a.state === "failed"
|
|
289
|
-
? "error"
|
|
290
|
-
: a.state === "uncertain"
|
|
291
|
-
? "uncertain"
|
|
292
|
-
: a.state === "running"
|
|
293
|
-
? "running"
|
|
294
|
-
: "done";
|
|
323
|
+
const state = normalizeAgentState(a.state);
|
|
295
324
|
const index = typeof a.index === "number" ? a.index : agents.length + 1;
|
|
296
325
|
const decodedInvocation = decodeInvocationRecord(a.invocation);
|
|
297
326
|
const invocation =
|
|
@@ -407,16 +436,23 @@ export function normalizePersistedWorkflowDetails(
|
|
|
407
436
|
logs.push({
|
|
408
437
|
at: typeof entry.at === "number" ? entry.at : startedAt,
|
|
409
438
|
text: sanitizeLine(entry.text, MAX_LOG_TEXT),
|
|
439
|
+
...(entry.kind === "pipeline-drop"
|
|
440
|
+
? { kind: "pipeline-drop" as const }
|
|
441
|
+
: {}),
|
|
410
442
|
});
|
|
411
443
|
}
|
|
412
444
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
445
|
+
let status = normalizeWorkflowStatus(record.status);
|
|
446
|
+
if (status !== "running") {
|
|
447
|
+
for (const agent of agents) {
|
|
448
|
+
if (agent.state !== "running" && agent.state !== "uncertain") continue;
|
|
449
|
+
status = "uncertain";
|
|
450
|
+
agent.state = "uncertain";
|
|
451
|
+
agent.error =
|
|
452
|
+
agent.error ??
|
|
453
|
+
"Persisted terminal workflow contained an agent without terminal evidence";
|
|
454
|
+
}
|
|
455
|
+
}
|
|
420
456
|
|
|
421
457
|
return {
|
|
422
458
|
runId,
|
|
@@ -541,22 +577,31 @@ export function loadRunEntries(
|
|
|
541
577
|
referencedRunIds: ReadonlySet<string>,
|
|
542
578
|
/** Hide runs untouched by the current request; live runs always show. */
|
|
543
579
|
startedSince = 0,
|
|
580
|
+
/** Bounded settled projections used only if canonical disk state is unreadable. */
|
|
581
|
+
retained: ReadonlyMap<string, WorkflowDetails> = new Map(),
|
|
544
582
|
): RunEntry[] {
|
|
545
583
|
const entries: RunEntry[] = [];
|
|
546
|
-
|
|
584
|
+
const runIds = new Set([...listPersistedRunIds(), ...retained.keys()]);
|
|
585
|
+
for (const runId of runIds) {
|
|
547
586
|
const live = active.get(runId);
|
|
548
587
|
if (live) {
|
|
549
588
|
entries.push({ runId, details: live, live: true });
|
|
550
589
|
continue;
|
|
551
590
|
}
|
|
552
|
-
const
|
|
591
|
+
const persisted = readPersistedWorkflowDetails(runId, {
|
|
553
592
|
hydrateArtifacts: true,
|
|
554
593
|
});
|
|
594
|
+
const retainedDetails = retained.get(runId);
|
|
595
|
+
const details = persisted ?? retainedDetails;
|
|
555
596
|
if (!details) continue;
|
|
597
|
+
const fromRetention =
|
|
598
|
+
persisted === undefined && retainedDetails !== undefined;
|
|
556
599
|
const touchedAt = Math.max(details.startedAt, details.finishedAt ?? 0);
|
|
557
600
|
if (
|
|
558
601
|
touchedAt < startedSince ||
|
|
559
|
-
(
|
|
602
|
+
(!fromRetention &&
|
|
603
|
+
details.sessionId !== sessionId &&
|
|
604
|
+
!referencedRunIds.has(runId))
|
|
560
605
|
) {
|
|
561
606
|
continue;
|
|
562
607
|
}
|
|
@@ -677,24 +722,24 @@ export class WorkflowDashboard {
|
|
|
677
722
|
private phaseIndex = 0;
|
|
678
723
|
private agentIndex = 0;
|
|
679
724
|
private detailFocus: DetailFocus = "phases";
|
|
680
|
-
private
|
|
681
|
-
private transcriptRowCount = 0;
|
|
682
|
-
private transcriptViewportSize = 1;
|
|
725
|
+
private transcriptPage?: AgentSessionPage;
|
|
683
726
|
private current?: RunEntry;
|
|
684
727
|
private openedDirectly = false;
|
|
685
728
|
private notice?: string;
|
|
686
729
|
private noticeAt = 0;
|
|
687
730
|
private disposed = false;
|
|
688
|
-
private timer
|
|
731
|
+
private timer?: ReturnType<typeof setInterval>;
|
|
689
732
|
private tui: TUI;
|
|
690
733
|
private theme: Theme;
|
|
691
734
|
private keybindings: KeybindingsManager;
|
|
692
735
|
private getActive: () => Map<string, WorkflowDetails>;
|
|
736
|
+
private getRetained: () => ReadonlyMap<string, WorkflowDetails>;
|
|
693
737
|
private sessionId: string;
|
|
694
738
|
private referencedRunIds: ReadonlySet<string>;
|
|
695
739
|
private startedSince: number;
|
|
696
740
|
private close: () => void;
|
|
697
741
|
private onAbort?: (runId: string) => boolean;
|
|
742
|
+
private initialToolsExpanded: boolean;
|
|
698
743
|
|
|
699
744
|
constructor(
|
|
700
745
|
tui: TUI,
|
|
@@ -707,16 +752,20 @@ export class WorkflowDashboard {
|
|
|
707
752
|
close: () => void,
|
|
708
753
|
initialRunId?: string,
|
|
709
754
|
onAbort?: (runId: string) => boolean,
|
|
755
|
+
getRetained: () => ReadonlyMap<string, WorkflowDetails> = () => new Map(),
|
|
756
|
+
initialToolsExpanded = false,
|
|
710
757
|
) {
|
|
711
758
|
this.tui = tui;
|
|
712
759
|
this.theme = theme;
|
|
713
760
|
this.keybindings = keybindings;
|
|
714
761
|
this.getActive = getActive;
|
|
762
|
+
this.getRetained = getRetained;
|
|
715
763
|
this.sessionId = sessionId;
|
|
716
764
|
this.referencedRunIds = referencedRunIds;
|
|
717
765
|
this.startedSince = startedSince;
|
|
718
766
|
this.close = close;
|
|
719
767
|
this.onAbort = onAbort;
|
|
768
|
+
this.initialToolsExpanded = initialToolsExpanded;
|
|
720
769
|
this.refresh();
|
|
721
770
|
if (initialRunId) {
|
|
722
771
|
const resolution = resolveWorkflowRunTarget(
|
|
@@ -736,25 +785,44 @@ export class WorkflowDashboard {
|
|
|
736
785
|
this.noticeAt = Date.now();
|
|
737
786
|
}
|
|
738
787
|
}
|
|
788
|
+
this.refreshTimer();
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
private refreshTimer() {
|
|
792
|
+
const active = Boolean(
|
|
793
|
+
this.notice ||
|
|
794
|
+
(this.view === "list"
|
|
795
|
+
? this.entries.some(
|
|
796
|
+
(entry) => entry.live && entry.details.status === "running",
|
|
797
|
+
)
|
|
798
|
+
: this.view === "detail"
|
|
799
|
+
? this.current?.live && this.current.details.status === "running"
|
|
800
|
+
: this.current?.live && this.selectedAgent()?.state === "running"),
|
|
801
|
+
);
|
|
802
|
+
if (!active) {
|
|
803
|
+
if (this.timer) clearInterval(this.timer);
|
|
804
|
+
this.timer = undefined;
|
|
805
|
+
return;
|
|
806
|
+
}
|
|
807
|
+
if (this.timer) return;
|
|
739
808
|
this.timer = setInterval(() => {
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
this.notice
|
|
744
|
-
) {
|
|
745
|
-
this.refresh();
|
|
746
|
-
this.tui.requestRender();
|
|
747
|
-
}
|
|
809
|
+
this.refresh();
|
|
810
|
+
this.tui.requestRender();
|
|
811
|
+
this.refreshTimer();
|
|
748
812
|
}, SPINNER_INTERVAL_MS);
|
|
749
813
|
}
|
|
750
814
|
|
|
751
815
|
dispose() {
|
|
752
816
|
if (this.disposed) return;
|
|
753
817
|
this.disposed = true;
|
|
754
|
-
clearInterval(this.timer);
|
|
818
|
+
if (this.timer) clearInterval(this.timer);
|
|
819
|
+
this.timer = undefined;
|
|
820
|
+
this.transcriptPage = undefined;
|
|
755
821
|
}
|
|
756
822
|
|
|
757
|
-
invalidate() {
|
|
823
|
+
invalidate() {
|
|
824
|
+
this.transcriptPage?.invalidate();
|
|
825
|
+
}
|
|
758
826
|
|
|
759
827
|
private refresh() {
|
|
760
828
|
const selected = this.entries[this.listIndex]?.runId;
|
|
@@ -763,6 +831,7 @@ export class WorkflowDashboard {
|
|
|
763
831
|
this.sessionId,
|
|
764
832
|
this.referencedRunIds,
|
|
765
833
|
this.startedSince,
|
|
834
|
+
this.getRetained(),
|
|
766
835
|
);
|
|
767
836
|
if (selected) {
|
|
768
837
|
const index = this.entries.findIndex((e) => e.runId === selected);
|
|
@@ -819,11 +888,12 @@ export class WorkflowDashboard {
|
|
|
819
888
|
const target = path.join(runsDir(), entry.runId, "report.md");
|
|
820
889
|
try {
|
|
821
890
|
writeFileAtomic(target, buildWorkflowReport(entry.details));
|
|
822
|
-
this.
|
|
891
|
+
this.setNotice(`saved ${shortenHome(target)}`);
|
|
823
892
|
} catch (error) {
|
|
824
|
-
this.
|
|
893
|
+
this.setNotice(
|
|
894
|
+
`save failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
895
|
+
);
|
|
825
896
|
}
|
|
826
|
-
this.noticeAt = Date.now();
|
|
827
897
|
}
|
|
828
898
|
|
|
829
899
|
/** Request cancellation of a run by id, surfacing the outcome as a notice. */
|
|
@@ -842,6 +912,7 @@ export class WorkflowDashboard {
|
|
|
842
912
|
private setNotice(text: string) {
|
|
843
913
|
this.notice = text;
|
|
844
914
|
this.noticeAt = Date.now();
|
|
915
|
+
this.refreshTimer();
|
|
845
916
|
}
|
|
846
917
|
|
|
847
918
|
handleInput(data: string) {
|
|
@@ -923,57 +994,27 @@ export class WorkflowDashboard {
|
|
|
923
994
|
} else if (left || cancel) {
|
|
924
995
|
this.detailFocus = "phases";
|
|
925
996
|
} else if ((right || confirm) && this.selectedAgent()) {
|
|
926
|
-
this.
|
|
927
|
-
this.view = "transcript";
|
|
997
|
+
this.openTranscriptPage();
|
|
928
998
|
}
|
|
929
999
|
}
|
|
930
1000
|
if (data === "s") this.saveReport();
|
|
931
1001
|
if (data === "x") this.abortRun(this.current);
|
|
932
1002
|
} else {
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
);
|
|
937
|
-
const scrollStep =
|
|
938
|
-
data === "j" || data === "k" ? TRANSCRIPT_SCROLL_STEP : 1;
|
|
939
|
-
const pageStep = Math.max(1, this.transcriptViewportSize - 2);
|
|
940
|
-
if (up) {
|
|
941
|
-
this.transcriptScroll = Math.max(0, this.transcriptScroll - scrollStep);
|
|
942
|
-
} else if (down) {
|
|
943
|
-
this.transcriptScroll = Math.min(
|
|
944
|
-
maxScroll,
|
|
945
|
-
this.transcriptScroll + scrollStep,
|
|
946
|
-
);
|
|
947
|
-
} else if (matchesKey(data, Key.ctrl("u"))) {
|
|
948
|
-
this.transcriptScroll = Math.max(0, this.transcriptScroll - pageStep);
|
|
949
|
-
} else if (matchesKey(data, Key.ctrl("d"))) {
|
|
950
|
-
this.transcriptScroll = Math.min(
|
|
951
|
-
maxScroll,
|
|
952
|
-
this.transcriptScroll + pageStep,
|
|
953
|
-
);
|
|
954
|
-
} else if (data === "g") {
|
|
955
|
-
this.transcriptScroll = 0;
|
|
956
|
-
} else if (data === "G") {
|
|
957
|
-
this.transcriptScroll = maxScroll;
|
|
958
|
-
} else if (cancel || left) {
|
|
959
|
-
this.view = "detail";
|
|
960
|
-
this.detailFocus = "agents";
|
|
961
|
-
}
|
|
1003
|
+
this.transcriptPage?.handleInput(data);
|
|
1004
|
+
this.refreshTimer();
|
|
1005
|
+
return;
|
|
962
1006
|
}
|
|
1007
|
+
this.refreshTimer();
|
|
963
1008
|
this.tui.requestRender();
|
|
964
1009
|
}
|
|
965
1010
|
|
|
966
1011
|
render(width: number): string[] {
|
|
1012
|
+
if (this.view === "transcript" && this.transcriptPage) {
|
|
1013
|
+
return this.transcriptPage.render(width);
|
|
1014
|
+
}
|
|
967
1015
|
const height = Math.max(MIN_HEIGHT, this.tui.terminal.rows - 1);
|
|
968
1016
|
let lines: string[];
|
|
969
|
-
if (this.view === "
|
|
970
|
-
lines = this.renderTranscript(
|
|
971
|
-
this.current.details,
|
|
972
|
-
this.selectedAgent()!,
|
|
973
|
-
width,
|
|
974
|
-
height,
|
|
975
|
-
);
|
|
976
|
-
} else if (this.view === "detail" && this.current) {
|
|
1017
|
+
if (this.view === "detail" && this.current) {
|
|
977
1018
|
lines = this.renderDetail(this.current.details, width, height);
|
|
978
1019
|
} else {
|
|
979
1020
|
lines = this.renderList(width, height);
|
|
@@ -981,6 +1022,53 @@ export class WorkflowDashboard {
|
|
|
981
1022
|
return lines.map((line) => truncateToWidth(line, width, ""));
|
|
982
1023
|
}
|
|
983
1024
|
|
|
1025
|
+
private openTranscriptPage() {
|
|
1026
|
+
const transcriptAdapter = new WorkflowTranscriptAdapter();
|
|
1027
|
+
this.view = "transcript";
|
|
1028
|
+
this.transcriptPage = new AgentSessionPage(
|
|
1029
|
+
this.tui,
|
|
1030
|
+
this.theme,
|
|
1031
|
+
this.keybindings,
|
|
1032
|
+
{
|
|
1033
|
+
getState: () => {
|
|
1034
|
+
const details = this.current?.details;
|
|
1035
|
+
const agent = this.selectedAgent();
|
|
1036
|
+
if (!details || !agent) return undefined;
|
|
1037
|
+
return {
|
|
1038
|
+
id: agent.callId ?? `agent-${agent.index}`,
|
|
1039
|
+
title: agent.label,
|
|
1040
|
+
status: agent.state,
|
|
1041
|
+
document: transcriptAdapter.document(
|
|
1042
|
+
agent.transcript,
|
|
1043
|
+
agent.worktreePath,
|
|
1044
|
+
),
|
|
1045
|
+
metadata: [
|
|
1046
|
+
`${details.name ?? details.runId} · ${agent.phase ?? "unphased"}`,
|
|
1047
|
+
agent.model,
|
|
1048
|
+
agentContext(agent),
|
|
1049
|
+
agent.acceptance
|
|
1050
|
+
? `acceptance:${agent.acceptance.status}`
|
|
1051
|
+
: undefined,
|
|
1052
|
+
formatElapsed(agent.startedAt, agent.finishedAt),
|
|
1053
|
+
],
|
|
1054
|
+
errorText: agent.error,
|
|
1055
|
+
emptyText:
|
|
1056
|
+
"transcript unavailable (this run predates transcript capture)",
|
|
1057
|
+
};
|
|
1058
|
+
},
|
|
1059
|
+
close: () => {
|
|
1060
|
+
this.transcriptPage = undefined;
|
|
1061
|
+
this.view = "detail";
|
|
1062
|
+
this.detailFocus = "agents";
|
|
1063
|
+
this.refreshTimer();
|
|
1064
|
+
this.tui.requestRender();
|
|
1065
|
+
},
|
|
1066
|
+
},
|
|
1067
|
+
{ toolsExpanded: this.initialToolsExpanded },
|
|
1068
|
+
);
|
|
1069
|
+
this.tui.requestRender();
|
|
1070
|
+
}
|
|
1071
|
+
|
|
984
1072
|
/** Bordered panel with a title in the top border, padded to exact height. */
|
|
985
1073
|
private panel(
|
|
986
1074
|
title: string,
|
|
@@ -1313,117 +1401,6 @@ export class WorkflowDashboard {
|
|
|
1313
1401
|
lines.push(this.hintLine(hints, width));
|
|
1314
1402
|
return lines;
|
|
1315
1403
|
}
|
|
1316
|
-
|
|
1317
|
-
private transcriptRows(agent: AgentRecord, width: number): string[] {
|
|
1318
|
-
const theme = this.theme;
|
|
1319
|
-
const rows: string[] = [];
|
|
1320
|
-
if (agent.transcript.length === 0) {
|
|
1321
|
-
return [
|
|
1322
|
-
theme.fg(
|
|
1323
|
-
"dim",
|
|
1324
|
-
" transcript unavailable (this run predates transcript capture)",
|
|
1325
|
-
),
|
|
1326
|
-
];
|
|
1327
|
-
}
|
|
1328
|
-
|
|
1329
|
-
for (const entry of agent.transcript) {
|
|
1330
|
-
// Tool calls are one-liners: the arrow shows direction, the accent name
|
|
1331
|
-
// says what ran, and the arguments collapse to a single compact line
|
|
1332
|
-
// instead of a vertical JSON block.
|
|
1333
|
-
if (entry.role === "tool") {
|
|
1334
|
-
const name = entry.name ? sanitizeLine(entry.name, 160) : "unknown";
|
|
1335
|
-
const args = compactInlineJson(entry.text);
|
|
1336
|
-
rows.push(
|
|
1337
|
-
` ${theme.fg("muted", "→")} ${theme.fg("accent", name)}${args ? theme.fg("dim", ` ${args}`) : ""}`,
|
|
1338
|
-
);
|
|
1339
|
-
continue;
|
|
1340
|
-
}
|
|
1341
|
-
const label = transcriptLabel(entry);
|
|
1342
|
-
const color = transcriptColor(entry);
|
|
1343
|
-
const marker = entry.role === "toolResult" ? "←" : "●";
|
|
1344
|
-
rows.push(
|
|
1345
|
-
` ${theme.fg(color, marker)} ${theme.bold(theme.fg(color, label))}`,
|
|
1346
|
-
);
|
|
1347
|
-
const contentWidth = Math.max(8, width - 4);
|
|
1348
|
-
const styled = theme.fg(
|
|
1349
|
-
entry.role === "thinking" || entry.role === "toolResult"
|
|
1350
|
-
? "dim"
|
|
1351
|
-
: entry.isError
|
|
1352
|
-
? "error"
|
|
1353
|
-
: "text",
|
|
1354
|
-
sanitizeTerminalText(entry.text),
|
|
1355
|
-
);
|
|
1356
|
-
for (const line of wrapTextWithAnsi(styled, contentWidth)) {
|
|
1357
|
-
rows.push(` ${line}`);
|
|
1358
|
-
}
|
|
1359
|
-
rows.push("");
|
|
1360
|
-
}
|
|
1361
|
-
return rows;
|
|
1362
|
-
}
|
|
1363
|
-
|
|
1364
|
-
private renderTranscript(
|
|
1365
|
-
details: WorkflowDetails,
|
|
1366
|
-
agent: AgentRecord,
|
|
1367
|
-
width: number,
|
|
1368
|
-
height: number,
|
|
1369
|
-
): string[] {
|
|
1370
|
-
const theme = this.theme;
|
|
1371
|
-
const lines: string[] = [];
|
|
1372
|
-
const right = theme.fg(
|
|
1373
|
-
"dim",
|
|
1374
|
-
[
|
|
1375
|
-
agent.model,
|
|
1376
|
-
agentContext(agent),
|
|
1377
|
-
agent.acceptance ? `acceptance:${agent.acceptance.status}` : undefined,
|
|
1378
|
-
formatElapsed(agent.startedAt, agent.finishedAt),
|
|
1379
|
-
]
|
|
1380
|
-
.filter(Boolean)
|
|
1381
|
-
.join(" · ") + " ",
|
|
1382
|
-
);
|
|
1383
|
-
lines.push(
|
|
1384
|
-
fitNavigationSides(
|
|
1385
|
-
` ${stateGlyph(agent.state, theme, Date.now())} ${theme.bold(theme.fg("accent", agent.label))}`,
|
|
1386
|
-
right,
|
|
1387
|
-
width,
|
|
1388
|
-
),
|
|
1389
|
-
);
|
|
1390
|
-
lines.push(
|
|
1391
|
-
fitNavigationSides(
|
|
1392
|
-
` ${theme.fg("muted", `${details.name ?? details.runId} · ${agent.phase ?? "unphased"}`)}`,
|
|
1393
|
-
theme.fg("dim", `${agent.transcript.length} entries `),
|
|
1394
|
-
width,
|
|
1395
|
-
),
|
|
1396
|
-
);
|
|
1397
|
-
|
|
1398
|
-
const panelHeight = height - 3;
|
|
1399
|
-
const bodyHeight = Math.max(1, panelHeight - 2);
|
|
1400
|
-
const rows = this.transcriptRows(agent, width - 2);
|
|
1401
|
-
this.transcriptRowCount = rows.length;
|
|
1402
|
-
this.transcriptViewportSize = bodyHeight;
|
|
1403
|
-
const maxScroll = Math.max(0, rows.length - bodyHeight);
|
|
1404
|
-
this.transcriptScroll = Math.min(this.transcriptScroll, maxScroll);
|
|
1405
|
-
const visible = rows.slice(
|
|
1406
|
-
this.transcriptScroll,
|
|
1407
|
-
this.transcriptScroll + bodyHeight,
|
|
1408
|
-
);
|
|
1409
|
-
const position =
|
|
1410
|
-
rows.length > bodyHeight
|
|
1411
|
-
? `Transcript · ${this.transcriptScroll + 1}-${Math.min(rows.length, this.transcriptScroll + bodyHeight)}/${rows.length}`
|
|
1412
|
-
: "Transcript";
|
|
1413
|
-
lines.push(...this.panel(position, visible, width, panelHeight));
|
|
1414
|
-
lines.push(
|
|
1415
|
-
this.hintLine(
|
|
1416
|
-
[
|
|
1417
|
-
["j/k", "scroll"],
|
|
1418
|
-
["ctrl-u/d", "page"],
|
|
1419
|
-
["g/G", "top/bottom"],
|
|
1420
|
-
["h/left/esc", "back"],
|
|
1421
|
-
],
|
|
1422
|
-
width,
|
|
1423
|
-
),
|
|
1424
|
-
);
|
|
1425
|
-
return lines;
|
|
1426
|
-
}
|
|
1427
1404
|
}
|
|
1428
1405
|
|
|
1429
1406
|
/**
|
|
@@ -1438,39 +1415,6 @@ function displayError(error: string) {
|
|
|
1438
1415
|
return clean;
|
|
1439
1416
|
}
|
|
1440
1417
|
|
|
1441
|
-
function transcriptLabel(entry: TranscriptEntry): string {
|
|
1442
|
-
if (entry.role === "user") return "user";
|
|
1443
|
-
if (entry.role === "assistant") return "assistant";
|
|
1444
|
-
if (entry.role === "thinking") return "thinking";
|
|
1445
|
-
const name = entry.name ? sanitizeLine(entry.name, 160) : "unknown";
|
|
1446
|
-
return name;
|
|
1447
|
-
}
|
|
1448
|
-
|
|
1449
|
-
/**
|
|
1450
|
-
* Tool arguments arrive pretty-printed over many lines; the transcript shows
|
|
1451
|
-
* them inline. Non-JSON text passes through flattened.
|
|
1452
|
-
*/
|
|
1453
|
-
function compactInlineJson(text: string) {
|
|
1454
|
-
const flat = sanitizeTerminalText(text).trim();
|
|
1455
|
-
if (!flat) return "";
|
|
1456
|
-
try {
|
|
1457
|
-
return JSON.stringify(JSON.parse(flat));
|
|
1458
|
-
} catch {
|
|
1459
|
-
return flat.replace(/\s+/g, " ");
|
|
1460
|
-
}
|
|
1461
|
-
}
|
|
1462
|
-
|
|
1463
|
-
function transcriptColor(
|
|
1464
|
-
entry: TranscriptEntry,
|
|
1465
|
-
): "accent" | "success" | "dim" | "warning" | "error" | "muted" {
|
|
1466
|
-
if (entry.isError) return "error";
|
|
1467
|
-
if (entry.role === "user") return "accent";
|
|
1468
|
-
if (entry.role === "assistant") return "success";
|
|
1469
|
-
if (entry.role === "thinking") return "dim";
|
|
1470
|
-
if (entry.role === "tool") return "warning";
|
|
1471
|
-
return "muted";
|
|
1472
|
-
}
|
|
1473
|
-
|
|
1474
1418
|
function groupGlyph(group: PhaseGroup, theme: Theme) {
|
|
1475
1419
|
if (group.agents.length === 0) return theme.fg("dim", "○");
|
|
1476
1420
|
if (group.agents.some((a) => a.state === "running"))
|
|
@@ -1489,6 +1433,7 @@ export async function showWorkflowDashboard(
|
|
|
1489
1433
|
initialRunId?: string,
|
|
1490
1434
|
startedSince = 0,
|
|
1491
1435
|
onAbort?: (runId: string) => boolean,
|
|
1436
|
+
getRetained?: () => ReadonlyMap<string, WorkflowDetails>,
|
|
1492
1437
|
) {
|
|
1493
1438
|
await ctx.ui.custom<void>(
|
|
1494
1439
|
(tui, theme, keybindings, done) => {
|
|
@@ -1506,12 +1451,14 @@ export async function showWorkflowDashboard(
|
|
|
1506
1451
|
},
|
|
1507
1452
|
initialRunId,
|
|
1508
1453
|
onAbort,
|
|
1454
|
+
getRetained,
|
|
1455
|
+
ctx.ui.getToolsExpanded(),
|
|
1509
1456
|
);
|
|
1510
1457
|
return dashboard;
|
|
1511
1458
|
},
|
|
1512
1459
|
{
|
|
1513
1460
|
overlay: true,
|
|
1514
|
-
overlayOptions: { anchor: "
|
|
1461
|
+
overlayOptions: { anchor: "top-left", width: "100%", maxHeight: "100%" },
|
|
1515
1462
|
},
|
|
1516
1463
|
);
|
|
1517
1464
|
}
|