@bastani/atomic 0.9.13-alpha.2 → 0.9.13-alpha.3
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 +14 -0
- package/README.md +1 -1
- package/dist/builtin/i-have-adhd/package.json +1 -1
- package/dist/builtin/intercom/CHANGELOG.md +14 -0
- package/dist/builtin/intercom/README.md +12 -12
- package/dist/builtin/intercom/incoming-message-delivery.ts +1 -1
- package/dist/builtin/intercom/index.ts +1 -1
- package/dist/builtin/intercom/intercom-tool.ts +1 -1
- package/dist/builtin/intercom/intercom-utils.ts +3 -6
- package/dist/builtin/intercom/package.json +1 -1
- package/dist/builtin/intercom/result-renderers.ts +1 -1
- package/dist/builtin/intercom/session-target.ts +1 -15
- package/dist/builtin/intercom/skills/intercom/SKILL.md +6 -6
- package/dist/builtin/intercom/terminal-ordering-barrier.ts +2 -2
- package/dist/builtin/intercom/ui/inline-message.ts +7 -5
- package/dist/builtin/intercom/ui/session-list.ts +23 -10
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/subagents/CHANGELOG.md +19 -0
- package/dist/builtin/subagents/README.md +29 -73
- package/dist/builtin/subagents/package.json +2 -2
- package/dist/builtin/subagents/prompts/parallel-cleanup.md +1 -1
- package/dist/builtin/subagents/prompts/parallel-review.md +1 -1
- package/dist/builtin/subagents/prompts/review-loop.md +2 -2
- package/dist/builtin/subagents/skills/subagent/SKILL.md +23 -59
- package/dist/builtin/subagents/skills/tmux/SKILL.md +1 -1
- package/dist/builtin/subagents/src/extension/control-notices.ts +2 -3
- package/dist/builtin/subagents/src/extension/doctor.ts +2 -14
- package/dist/builtin/subagents/src/extension/fanout-child.ts +1 -17
- package/dist/builtin/subagents/src/extension/index.ts +9 -70
- package/dist/builtin/subagents/src/extension/notification-content.ts +1 -1
- package/dist/builtin/subagents/src/extension/prompt-guidance.ts +2 -2
- package/dist/builtin/subagents/src/extension/schemas.ts +2 -8
- package/dist/builtin/subagents/src/extension/tool-description.ts +2 -3
- package/dist/builtin/subagents/src/intercom/intercom-bridge.ts +1 -1
- package/dist/builtin/subagents/src/intercom/result-intercom.ts +5 -46
- package/dist/builtin/subagents/src/runs/{background → foreground}/completion-notification.ts +2 -2
- package/dist/builtin/subagents/src/runs/foreground/inprocess-run-sync.ts +25 -88
- package/dist/builtin/subagents/src/runs/{background → foreground}/notify.ts +5 -5
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-context.ts +11 -28
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-parallel-task.ts +3 -0
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-parallel.ts +2 -0
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-runtime.ts +0 -5
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-single.ts +30 -20
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-status.ts +4 -4
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-types.ts +0 -13
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor.ts +104 -48
- package/dist/builtin/subagents/src/runs/inprocess/index.ts +1 -1
- package/dist/builtin/subagents/src/runs/inprocess/runner.ts +5 -5
- package/dist/builtin/subagents/src/runs/inprocess/runtime-support/nested-api.ts +0 -7
- package/dist/builtin/subagents/src/runs/inprocess/runtime-support/nested-core.ts +0 -10
- package/dist/builtin/subagents/src/runs/inprocess/runtime-support/nested-projection.ts +3 -108
- package/dist/builtin/subagents/src/runs/inprocess/runtime-support/nested-sanitize.ts +0 -4
- package/dist/builtin/subagents/src/runs/shared/subagent-control.ts +1 -1
- package/dist/builtin/subagents/src/shared/formatters.ts +3 -5
- package/dist/builtin/subagents/src/shared/model-resolution.ts +78 -0
- package/dist/builtin/subagents/src/shared/status-format.ts +4 -11
- package/dist/builtin/subagents/src/shared/types-config.ts +10 -9
- package/dist/builtin/subagents/src/shared/types-nested.ts +127 -0
- package/dist/builtin/subagents/src/shared/types-results.ts +2 -8
- package/dist/builtin/subagents/src/shared/types-runtime.ts +0 -9
- package/dist/builtin/subagents/src/shared/types.ts +1 -1
- package/dist/builtin/subagents/src/shared/utils.ts +1 -109
- package/dist/builtin/subagents/src/slash/slash-commands.ts +7 -17
- package/dist/builtin/subagents/src/tui/render-event-formatting.ts +2 -252
- package/dist/builtin/subagents/src/tui/render-layout.ts +4 -4
- package/dist/builtin/subagents/src/tui/render-result.ts +0 -9
- package/dist/builtin/subagents/src/tui/render-stable-output.ts +1 -34
- package/dist/builtin/subagents/src/tui/render.ts +0 -2
- package/dist/builtin/web-access/package.json +1 -1
- package/dist/builtin/workflows/CHANGELOG.md +12 -0
- package/dist/builtin/workflows/README.md +3 -3
- package/dist/builtin/workflows/package.json +1 -1
- package/dist/builtin/workflows/src/durable/backend.ts +11 -0
- package/dist/builtin/workflows/src/durable/completed-catalog-stage-groups.ts +63 -25
- package/dist/builtin/workflows/src/durable/completed-catalog.ts +166 -25
- package/dist/builtin/workflows/src/durable/dbos-backend.ts +42 -24
- package/dist/builtin/workflows/src/durable/dbos-envelope.ts +2 -1
- package/dist/builtin/workflows/src/durable/run-timing.ts +28 -7
- package/dist/builtin/workflows/src/durable/scoped-backend.ts +4 -0
- package/dist/builtin/workflows/src/durable/targeted-inspection.ts +110 -0
- package/dist/builtin/workflows/src/engine/run-durable-stage-session.ts +2 -2
- package/dist/builtin/workflows/src/engine/runtime.ts +19 -2
- package/dist/builtin/workflows/src/extension/extension-runtime-state.ts +3 -0
- package/dist/builtin/workflows/src/extension/index.bundle.mjs +680 -152
- package/dist/builtin/workflows/src/extension/runtime-durable-resume.ts +6 -0
- package/dist/builtin/workflows/src/extension/ui-surface.ts +2 -0
- package/dist/builtin/workflows/src/extension/workflow-command-registration.ts +6 -1
- package/dist/builtin/workflows/src/extension/workflow-targets.ts +18 -10
- package/dist/builtin/workflows/src/extension/workflow-tool-inspection.ts +31 -16
- package/dist/builtin/workflows/src/extension/workflow-tool-send.ts +75 -3
- package/dist/builtin/workflows/src/extension/workflow-tool.ts +53 -6
- package/dist/builtin/workflows/src/runs/background/run-inspect.ts +5 -1
- package/dist/builtin/workflows/src/runs/foreground/executor-stage-call.ts +94 -60
- package/dist/builtin/workflows/src/runs/foreground/executor-stage-context.ts +1 -1
- package/dist/builtin/workflows/src/runs/foreground/executor-stage-control.ts +7 -7
- package/dist/builtin/workflows/src/runs/foreground/executor-stage-factory.ts +25 -4
- package/dist/builtin/workflows/src/runs/foreground/executor-stage-types.ts +4 -1
- package/dist/builtin/workflows/src/runs/foreground/executor-types.ts +2 -0
- package/dist/builtin/workflows/src/runs/foreground/stage-runner-controller.ts +142 -4
- package/dist/builtin/workflows/src/runs/foreground/stage-runner-types.ts +2 -0
- package/dist/builtin/workflows/src/runs/foreground/stage-session-heartbeat.ts +88 -0
- package/dist/builtin/workflows/src/tui/run-detail.ts +25 -12
- package/dist/builtin/workflows/src/tui/workflow-resume-selector.ts +1 -0
- package/dist/core/agent-session-events.d.ts.map +1 -1
- package/dist/core/agent-session-events.js +16 -5
- package/dist/core/agent-session-events.js.map +1 -1
- package/dist/core/agent-session-export.d.ts.map +1 -1
- package/dist/core/agent-session-export.js +2 -1
- package/dist/core/agent-session-export.js.map +1 -1
- package/dist/core/agent-session-methods.d.ts +8 -4
- package/dist/core/agent-session-methods.d.ts.map +1 -1
- package/dist/core/agent-session-methods.js.map +1 -1
- package/dist/core/agent-session-prompt.d.ts.map +1 -1
- package/dist/core/agent-session-prompt.js +5 -0
- package/dist/core/agent-session-prompt.js.map +1 -1
- package/dist/core/agent-session-summary.d.ts +21 -0
- package/dist/core/agent-session-summary.d.ts.map +1 -0
- package/dist/core/agent-session-summary.js +156 -0
- package/dist/core/agent-session-summary.js.map +1 -0
- package/dist/core/agent-session-tool-registry.d.ts.map +1 -1
- package/dist/core/agent-session-tool-registry.js +0 -4
- package/dist/core/agent-session-tool-registry.js.map +1 -1
- package/dist/core/agent-session-transfer.d.ts.map +1 -1
- package/dist/core/agent-session-transfer.js +0 -2
- package/dist/core/agent-session-transfer.js.map +1 -1
- package/dist/core/agent-session-tree.d.ts.map +1 -1
- package/dist/core/agent-session-tree.js +6 -0
- package/dist/core/agent-session-tree.js.map +1 -1
- package/dist/core/agent-session.d.ts +7 -3
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +8 -5
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/atomic-guide-command.js +2 -2
- package/dist/core/atomic-guide-command.js.map +1 -1
- package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
- package/dist/core/compaction/branch-summarization.js +1 -0
- package/dist/core/compaction/branch-summarization.js.map +1 -1
- package/dist/core/compaction/index.d.ts +1 -0
- package/dist/core/compaction/index.d.ts.map +1 -1
- package/dist/core/compaction/index.js +1 -0
- package/dist/core/compaction/index.js.map +1 -1
- package/dist/core/compaction/session-summarization.d.ts +41 -0
- package/dist/core/compaction/session-summarization.d.ts.map +1 -0
- package/dist/core/compaction/session-summarization.js +97 -0
- package/dist/core/compaction/session-summarization.js.map +1 -0
- package/dist/core/extensions/ui-types.d.ts +2 -0
- package/dist/core/extensions/ui-types.d.ts.map +1 -1
- package/dist/core/extensions/ui-types.js.map +1 -1
- package/dist/core/session-manager-core.d.ts +3 -1
- package/dist/core/session-manager-core.d.ts.map +1 -1
- package/dist/core/session-manager-core.js +9 -1
- package/dist/core/session-manager-core.js.map +1 -1
- package/dist/core/session-manager-entries.d.ts +24 -1
- package/dist/core/session-manager-entries.d.ts.map +1 -1
- package/dist/core/session-manager-entries.js +48 -0
- package/dist/core/session-manager-entries.js.map +1 -1
- package/dist/core/session-manager-list.d.ts.map +1 -1
- package/dist/core/session-manager-list.js +10 -0
- package/dist/core/session-manager-list.js.map +1 -1
- package/dist/core/session-manager-types.d.ts +10 -1
- package/dist/core/session-manager-types.d.ts.map +1 -1
- package/dist/core/session-manager-types.js.map +1 -1
- package/dist/core/settings-manager-basic-accessors.d.ts +3 -0
- package/dist/core/settings-manager-basic-accessors.d.ts.map +1 -1
- package/dist/core/settings-manager-basic-accessors.js +5 -0
- package/dist/core/settings-manager-basic-accessors.js.map +1 -1
- package/dist/core/settings-manager.d.ts +1 -1
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/settings-types.d.ts +4 -0
- package/dist/core/settings-types.d.ts.map +1 -1
- package/dist/core/settings-types.js.map +1 -1
- package/dist/core/slash-commands.js +2 -2
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/tools/bash.d.ts +0 -15
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +3 -81
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/fetch-url.d.ts +12 -0
- package/dist/core/tools/fetch-url.d.ts.map +1 -1
- package/dist/core/tools/fetch-url.js +76 -10
- package/dist/core/tools/fetch-url.js.map +1 -1
- package/dist/core/tools/index.js +4 -4
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/session-temp-dir.d.ts +2 -2
- package/dist/core/tools/session-temp-dir.d.ts.map +1 -1
- package/dist/core/tools/session-temp-dir.js +6 -7
- package/dist/core/tools/session-temp-dir.js.map +1 -1
- package/dist/core/usage-totals.js +1 -1
- package/dist/core/usage-totals.js.map +1 -1
- package/dist/modes/interactive/components/footer.js +1 -1
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/host-session-picker.d.ts.map +1 -1
- package/dist/modes/interactive/components/host-session-picker.js +1 -0
- package/dist/modes/interactive/components/host-session-picker.js.map +1 -1
- package/dist/modes/interactive/components/session-selector-list.d.ts.map +1 -1
- package/dist/modes/interactive/components/session-selector-list.js +29 -4
- package/dist/modes/interactive/components/session-selector-list.js.map +1 -1
- package/dist/modes/interactive/components/session-selector-search.js +1 -1
- package/dist/modes/interactive/components/session-selector-search.js.map +1 -1
- package/dist/modes/interactive/components/tree-selector-content.d.ts.map +1 -1
- package/dist/modes/interactive/components/tree-selector-content.js +6 -0
- package/dist/modes/interactive/components/tree-selector-content.js.map +1 -1
- package/dist/modes/interactive-engine/protocol.d.ts.map +1 -1
- package/dist/modes/interactive-engine/protocol.js +4 -1
- package/dist/modes/interactive-engine/protocol.js.map +1 -1
- package/docs/environment-variables.md +1 -1
- package/docs/intercom.md +20 -21
- package/docs/quickstart.md +5 -1
- package/docs/sdk.md +1 -1
- package/docs/session-format.md +15 -0
- package/docs/sessions.md +8 -0
- package/docs/settings.md +6 -0
- package/docs/subagents.md +19 -31
- package/docs/tools.md +3 -5
- package/docs/usage.md +1 -1
- package/docs/windows.md +1 -1
- package/docs/workflows.md +60 -32
- package/npm-shrinkwrap.json +29 -29
- package/package.json +2 -2
- package/dist/builtin/subagents/src/runs/background/async-job-tracker.ts +0 -331
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-async.ts +0 -161
- package/dist/builtin/subagents/src/runs/inprocess/background-single.ts +0 -195
- package/dist/builtin/subagents/src/runs/inprocess/background.ts +0 -90
- package/dist/builtin/subagents/src/shared/types-async.ts +0 -247
- package/dist/builtin/subagents/src/tui/render-widget-graph.ts +0 -217
- package/dist/builtin/subagents/src/tui/render-widget.ts +0 -366
- package/dist/core/async/format.d.ts +0 -3
- package/dist/core/async/format.d.ts.map +0 -1
- package/dist/core/async/format.js +0 -52
- package/dist/core/async/format.js.map +0 -1
- package/dist/core/async/job-manager.d.ts +0 -40
- package/dist/core/async/job-manager.d.ts.map +0 -1
- package/dist/core/async/job-manager.js +0 -293
- package/dist/core/async/job-manager.js.map +0 -1
- package/dist/core/async/session-manager.d.ts +0 -16
- package/dist/core/async/session-manager.d.ts.map +0 -1
- package/dist/core/async/session-manager.js +0 -34
- package/dist/core/async/session-manager.js.map +0 -1
- package/dist/core/async/types.d.ts +0 -23
- package/dist/core/async/types.d.ts.map +0 -1
- package/dist/core/async/types.js +0 -2
- package/dist/core/async/types.js.map +0 -1
- package/dist/core/tools/bash-async-execution.d.ts +0 -27
- package/dist/core/tools/bash-async-execution.d.ts.map +0 -1
- package/dist/core/tools/bash-async-execution.js +0 -81
- package/dist/core/tools/bash-async-execution.js.map +0 -1
- package/dist/core/tools/bash-async-jobs.d.ts +0 -30
- package/dist/core/tools/bash-async-jobs.d.ts.map +0 -1
- package/dist/core/tools/bash-async-jobs.js +0 -87
- package/dist/core/tools/bash-async-jobs.js.map +0 -1
- package/dist/core/tools/bash-async-output.d.ts +0 -13
- package/dist/core/tools/bash-async-output.d.ts.map +0 -1
- package/dist/core/tools/bash-async-output.js +0 -122
- package/dist/core/tools/bash-async-output.js.map +0 -1
|
@@ -1,217 +0,0 @@
|
|
|
1
|
-
import { keyHintIfBound } from "@bastani/atomic";
|
|
2
|
-
import type { AsyncJobState } from "../shared/types.ts";
|
|
3
|
-
import {
|
|
4
|
-
formatNestedWidgetLines,
|
|
5
|
-
modelThinkingBadge,
|
|
6
|
-
widgetActivity,
|
|
7
|
-
widgetJobName,
|
|
8
|
-
widgetStats,
|
|
9
|
-
widgetStatusGlyph,
|
|
10
|
-
widgetStepActivity,
|
|
11
|
-
widgetStepActivityLine,
|
|
12
|
-
widgetStepPulseGlyph,
|
|
13
|
-
widgetStepStats,
|
|
14
|
-
widgetStepStatus,
|
|
15
|
-
} from "./render-event-formatting.ts";
|
|
16
|
-
import { getTermWidth, type Theme, truncLine } from "./render-layout.ts";
|
|
17
|
-
import { buildLiveStatusLine, themeBold } from "./render-status-progress.ts";
|
|
18
|
-
|
|
19
|
-
export function widgetParallelAgentDetails(
|
|
20
|
-
job: AsyncJobState,
|
|
21
|
-
theme: Theme,
|
|
22
|
-
expanded = false,
|
|
23
|
-
width = getTermWidth(),
|
|
24
|
-
_now?: number,
|
|
25
|
-
pulseFrame?: number,
|
|
26
|
-
): string[] {
|
|
27
|
-
if (!job.steps?.length) return [];
|
|
28
|
-
if (job.mode !== "parallel") return [];
|
|
29
|
-
const total = job.stepsTotal ?? job.steps.length;
|
|
30
|
-
const lines: string[] = [];
|
|
31
|
-
for (const [index, step] of job.steps.entries()) {
|
|
32
|
-
const marker = index === job.steps.length - 1 ? "└" : "├";
|
|
33
|
-
const activity = widgetStepActivity(step, job.updatedAt);
|
|
34
|
-
const itemTitle = "Agent";
|
|
35
|
-
const modelDisplay = modelThinkingBadge(theme, step.model, step.thinking, step.fastMode);
|
|
36
|
-
lines.push(
|
|
37
|
-
` ${theme.fg("dim", `${marker} ${widgetStepPulseGlyph(step.status, theme, pulseFrame)} ${itemTitle} ${index + 1}/${total}: ${step.agent} · ${widgetStepStatus(step.status, theme)}${modelDisplay}${activity ? ` · ${activity}` : ""}`)}`,
|
|
38
|
-
);
|
|
39
|
-
for (const nestedLine of formatNestedWidgetLines(
|
|
40
|
-
step.children,
|
|
41
|
-
theme,
|
|
42
|
-
width,
|
|
43
|
-
expanded,
|
|
44
|
-
job.updatedAt,
|
|
45
|
-
expanded ? 8 : 1,
|
|
46
|
-
pulseFrame,
|
|
47
|
-
))
|
|
48
|
-
lines.push(` ${nestedLine}`);
|
|
49
|
-
}
|
|
50
|
-
return lines;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export function foregroundStyleWidgetStepLines(
|
|
54
|
-
job: AsyncJobState,
|
|
55
|
-
theme: Theme,
|
|
56
|
-
step: NonNullable<AsyncJobState["steps"]>[number],
|
|
57
|
-
itemTitle: "Agent" | "Step",
|
|
58
|
-
index: number,
|
|
59
|
-
total: number,
|
|
60
|
-
expanded: boolean,
|
|
61
|
-
width: number,
|
|
62
|
-
pulseFrame?: number,
|
|
63
|
-
): string[] {
|
|
64
|
-
const status = widgetStepStatus(step.status, theme);
|
|
65
|
-
const stats = widgetStepStats(theme, step);
|
|
66
|
-
const modelDisplay = modelThinkingBadge(theme, step.model, step.thinking, step.fastMode);
|
|
67
|
-
const lines = [
|
|
68
|
-
` ${widgetStepPulseGlyph(step.status, theme, pulseFrame)} ${itemTitle} ${index}/${total}: ${themeBold(theme, step.agent)} ${theme.fg("dim", "·")} ${status}${modelDisplay}${stats ? ` ${theme.fg("dim", "·")} ${stats}` : ""}`,
|
|
69
|
-
];
|
|
70
|
-
const activity = widgetStepActivityLine(step, width, expanded, job.updatedAt);
|
|
71
|
-
if (activity) lines.push(` ${theme.fg("dim", `⎿ ${activity}`)}`);
|
|
72
|
-
for (const nestedLine of formatNestedWidgetLines(
|
|
73
|
-
step.children,
|
|
74
|
-
theme,
|
|
75
|
-
width,
|
|
76
|
-
expanded,
|
|
77
|
-
job.updatedAt,
|
|
78
|
-
undefined,
|
|
79
|
-
pulseFrame,
|
|
80
|
-
)) {
|
|
81
|
-
lines.push(` ${nestedLine}`);
|
|
82
|
-
}
|
|
83
|
-
if (step.status === "running") {
|
|
84
|
-
const expandHint = keyHintIfBound("app.tools.expand", "for live detail");
|
|
85
|
-
if (!expanded && expandHint) lines.push(` ${theme.fg("accent", `Press ${expandHint}`)}`);
|
|
86
|
-
if (expanded) {
|
|
87
|
-
const liveStatus = buildLiveStatusLine(step, job.updatedAt);
|
|
88
|
-
if (liveStatus && liveStatus !== activity) lines.push(` ${theme.fg("accent", liveStatus)}`);
|
|
89
|
-
for (const tool of step.recentTools?.slice(-3) ?? []) {
|
|
90
|
-
const maxArgsLen = Math.max(40, width - 30);
|
|
91
|
-
const argsPreview = tool.args.length <= maxArgsLen ? tool.args : `${tool.args.slice(0, maxArgsLen)}...`;
|
|
92
|
-
lines.push(` ${theme.fg("dim", `${tool.tool}${argsPreview ? `: ${argsPreview}` : ""}`)}`);
|
|
93
|
-
}
|
|
94
|
-
for (const line of step.recentOutput?.slice(-5) ?? []) {
|
|
95
|
-
lines.push(` ${theme.fg("dim", line)}`);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
return lines;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
export function foregroundStyleWidgetDetails(
|
|
103
|
-
job: AsyncJobState,
|
|
104
|
-
theme: Theme,
|
|
105
|
-
expanded: boolean,
|
|
106
|
-
width: number,
|
|
107
|
-
pulseFrame?: number,
|
|
108
|
-
): string[] {
|
|
109
|
-
if (!job.steps?.length)
|
|
110
|
-
return [
|
|
111
|
-
` ${theme.fg("dim", `⎿ ${widgetActivity(job)}`)}`,
|
|
112
|
-
...formatNestedWidgetLines(
|
|
113
|
-
job.nestedChildren,
|
|
114
|
-
theme,
|
|
115
|
-
width,
|
|
116
|
-
expanded,
|
|
117
|
-
job.updatedAt,
|
|
118
|
-
undefined,
|
|
119
|
-
pulseFrame,
|
|
120
|
-
).map((line) => ` ${line}`),
|
|
121
|
-
];
|
|
122
|
-
const total = job.stepsTotal ?? job.steps.length;
|
|
123
|
-
const itemTitle = job.mode === "parallel" ? "Agent" : "Step";
|
|
124
|
-
const lines: string[] = [];
|
|
125
|
-
for (const [index, step] of job.steps.entries()) {
|
|
126
|
-
lines.push(
|
|
127
|
-
...foregroundStyleWidgetStepLines(job, theme, step, itemTitle, index + 1, total, expanded, width, pulseFrame),
|
|
128
|
-
);
|
|
129
|
-
}
|
|
130
|
-
const attached = new Set(job.steps.flatMap((step) => step.children?.map((child) => child.id) ?? []));
|
|
131
|
-
const unattached = job.nestedChildren?.filter((child) => !attached.has(child.id)) ?? [];
|
|
132
|
-
for (const nestedLine of formatNestedWidgetLines(
|
|
133
|
-
unattached,
|
|
134
|
-
theme,
|
|
135
|
-
width,
|
|
136
|
-
expanded,
|
|
137
|
-
job.updatedAt,
|
|
138
|
-
undefined,
|
|
139
|
-
pulseFrame,
|
|
140
|
-
)) {
|
|
141
|
-
lines.push(` ${nestedLine}`);
|
|
142
|
-
}
|
|
143
|
-
return lines;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
export function buildSingleWidgetLines(
|
|
147
|
-
job: AsyncJobState,
|
|
148
|
-
theme: Theme,
|
|
149
|
-
width: number,
|
|
150
|
-
expanded: boolean,
|
|
151
|
-
_now?: number,
|
|
152
|
-
pulseFrame?: number,
|
|
153
|
-
): string[] {
|
|
154
|
-
const stats = widgetStats(job, theme);
|
|
155
|
-
const count = job.stepsTotal ?? job.agents?.length ?? job.steps?.length;
|
|
156
|
-
const mode = widgetJobName(job);
|
|
157
|
-
const title = `async subagent ${mode}${count && count > 1 ? ` (${count})` : ""}`;
|
|
158
|
-
return [
|
|
159
|
-
`${theme.fg("toolTitle", themeBold(theme, title))} ${theme.fg("dim", "· background")}`,
|
|
160
|
-
`${widgetStatusGlyph(job, theme, pulseFrame)} ${themeBold(theme, mode)}${stats ? ` ${theme.fg("dim", "·")} ${stats}` : ""}`,
|
|
161
|
-
...foregroundStyleWidgetDetails(job, theme, expanded, width, pulseFrame),
|
|
162
|
-
].map((line) => truncLine(line, width));
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
export function compactSingleWidgetLines(
|
|
166
|
-
job: AsyncJobState,
|
|
167
|
-
theme: Theme,
|
|
168
|
-
width: number,
|
|
169
|
-
_now?: number,
|
|
170
|
-
pulseFrame?: number,
|
|
171
|
-
): string[] {
|
|
172
|
-
const fullLines = buildSingleWidgetLines(job, theme, width, false, _now, pulseFrame);
|
|
173
|
-
if (fullLines.length <= 10 || !job.steps?.length || job.mode !== "parallel") return fullLines;
|
|
174
|
-
|
|
175
|
-
const total = job.stepsTotal ?? job.steps.length;
|
|
176
|
-
const itemTitle = "Agent";
|
|
177
|
-
const lines = fullLines.slice(0, 2);
|
|
178
|
-
for (const [index, step] of job.steps.entries()) {
|
|
179
|
-
const status = widgetStepStatus(step.status, theme);
|
|
180
|
-
const activity = widgetStepActivityLine(step, width, false, job.updatedAt);
|
|
181
|
-
const stepStats = widgetStepStats(theme, step);
|
|
182
|
-
const activitySuffix = activity ? ` ${theme.fg("dim", "·")} ${theme.fg("dim", activity)}` : "";
|
|
183
|
-
const modelDisplay = modelThinkingBadge(theme, step.model, step.thinking, step.fastMode);
|
|
184
|
-
lines.push(
|
|
185
|
-
` ${widgetStepPulseGlyph(step.status, theme, pulseFrame)} ${itemTitle} ${index + 1}/${total}: ${themeBold(theme, step.agent)} ${theme.fg("dim", "·")} ${status}${modelDisplay}${activitySuffix}${stepStats ? ` ${theme.fg("dim", "·")} ${stepStats}` : ""}`,
|
|
186
|
-
);
|
|
187
|
-
for (const nestedLine of formatNestedWidgetLines(
|
|
188
|
-
step.children,
|
|
189
|
-
theme,
|
|
190
|
-
width,
|
|
191
|
-
false,
|
|
192
|
-
job.updatedAt,
|
|
193
|
-
undefined,
|
|
194
|
-
pulseFrame,
|
|
195
|
-
))
|
|
196
|
-
lines.push(` ${nestedLine}`);
|
|
197
|
-
}
|
|
198
|
-
const expandHint = keyHintIfBound("app.tools.expand", "for live detail");
|
|
199
|
-
if (expandHint && job.steps.some((step) => step.status === "running"))
|
|
200
|
-
lines.push(theme.fg("accent", ` Press ${expandHint}`));
|
|
201
|
-
return lines.map((line) => truncLine(line, width));
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
export function fitWidgetLineBudget(lines: string[], theme: Theme, width: number, expanded: boolean): string[] {
|
|
205
|
-
const rows = process.stdout.rows || 30;
|
|
206
|
-
const budget = expanded
|
|
207
|
-
? Math.max(12, Math.min(24, Math.floor(rows * 0.55)))
|
|
208
|
-
: Math.max(10, Math.min(14, Math.floor(rows * 0.35)));
|
|
209
|
-
if (lines.length <= budget) return lines;
|
|
210
|
-
const visibleLines = Math.max(1, budget - 1);
|
|
211
|
-
const hiddenCount = lines.length - visibleLines;
|
|
212
|
-
const expandHint = keyHintIfBound("app.tools.expand", "expands");
|
|
213
|
-
const hint = expanded
|
|
214
|
-
? `… ${hiddenCount} live-detail lines hidden`
|
|
215
|
-
: `… ${hiddenCount} lines hidden${expandHint ? ` · ${expandHint}` : ""}`;
|
|
216
|
-
return [...lines.slice(0, visibleLines), truncLine(theme.fg("dim", hint), width)];
|
|
217
|
-
}
|
|
@@ -1,366 +0,0 @@
|
|
|
1
|
-
import * as path from "node:path";
|
|
2
|
-
import type { ExtensionContext } from "@bastani/atomic";
|
|
3
|
-
import { type Component, Container, Text } from "@earendil-works/pi-tui";
|
|
4
|
-
import { type AsyncJobState, MAX_WIDGET_JOBS, WIDGET_KEY } from "../shared/types.ts";
|
|
5
|
-
import { widgetActivity, widgetJobName, widgetStats, widgetStatusGlyph } from "./render-event-formatting.ts";
|
|
6
|
-
import { getTermWidth, runningPulseGlyph, type Theme, truncLine } from "./render-layout.ts";
|
|
7
|
-
import { advanceResultPulseFrame } from "./render-result-animation.ts";
|
|
8
|
-
import { widgetRenderKey } from "./render-stable-output.ts";
|
|
9
|
-
import { themeBold } from "./render-status-progress.ts";
|
|
10
|
-
import {
|
|
11
|
-
buildSingleWidgetLines,
|
|
12
|
-
compactSingleWidgetLines,
|
|
13
|
-
fitWidgetLineBudget,
|
|
14
|
-
widgetParallelAgentDetails,
|
|
15
|
-
} from "./render-widget-graph.ts";
|
|
16
|
-
|
|
17
|
-
class LiveWidgetComponent implements Component {
|
|
18
|
-
private readonly container = new Container();
|
|
19
|
-
|
|
20
|
-
constructor(
|
|
21
|
-
private readonly getJobs: () => AsyncJobState[],
|
|
22
|
-
private readonly theme: Theme,
|
|
23
|
-
private readonly getExpanded: () => boolean,
|
|
24
|
-
private readonly getNow: () => number,
|
|
25
|
-
private readonly getPulseFrame: () => number | undefined,
|
|
26
|
-
) {}
|
|
27
|
-
|
|
28
|
-
render(width: number): string[] {
|
|
29
|
-
const jobs = this.getJobs();
|
|
30
|
-
const expanded = this.getExpanded();
|
|
31
|
-
const now = this.getNow();
|
|
32
|
-
const pulseFrame = this.getPulseFrame();
|
|
33
|
-
const lines = this.buildLines(jobs, width, expanded, now, pulseFrame);
|
|
34
|
-
this.container.clear();
|
|
35
|
-
for (const line of fitWidgetLineBudget(lines, this.theme, width, expanded))
|
|
36
|
-
this.container.addChild(new Text(line, 1, 0));
|
|
37
|
-
return this.container.render(width);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
private buildLines(
|
|
41
|
-
jobs: AsyncJobState[],
|
|
42
|
-
width: number,
|
|
43
|
-
expanded: boolean,
|
|
44
|
-
now: number,
|
|
45
|
-
pulseFrame: number | undefined,
|
|
46
|
-
): string[] {
|
|
47
|
-
if (expanded) return buildWidgetLines(jobs, this.theme, width, true, now, pulseFrame);
|
|
48
|
-
if (jobs.length === 1) return compactSingleWidgetLines(jobs[0]!, this.theme, width, now, pulseFrame);
|
|
49
|
-
return buildWidgetLines(jobs, this.theme, width, false, now, pulseFrame);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
invalidate(): void {
|
|
53
|
-
this.container.invalidate();
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function buildWidgetComponent(
|
|
58
|
-
getJobs: () => AsyncJobState[],
|
|
59
|
-
getExpanded: () => boolean,
|
|
60
|
-
getNow: () => number,
|
|
61
|
-
getPulseFrame: () => number | undefined,
|
|
62
|
-
): (_tui: unknown, theme: Theme) => Component {
|
|
63
|
-
return (_tui, theme) => new LiveWidgetComponent(getJobs, theme, getExpanded, getNow, getPulseFrame);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
interface RenderRequestingContext {
|
|
67
|
-
ui: ExtensionContext["ui"] & { requestRender?: () => void };
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
interface WidgetAnimationState {
|
|
71
|
-
latestCtx?: ExtensionContext;
|
|
72
|
-
latestJobs: AsyncJobState[];
|
|
73
|
-
frameNow: number;
|
|
74
|
-
expanded: boolean;
|
|
75
|
-
snapshotKey?: string;
|
|
76
|
-
pulseFrame?: number;
|
|
77
|
-
mountedCtx?: ExtensionContext;
|
|
78
|
-
mountedOwnerKey?: string;
|
|
79
|
-
surfaceKey?: object;
|
|
80
|
-
mounted: boolean;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
interface WidgetSurfaceState {
|
|
84
|
-
activeOwner?: object;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
interface WidgetRegistry {
|
|
88
|
-
states: WeakMap<object, WidgetAnimationState>;
|
|
89
|
-
surfaces: WeakMap<object, WidgetSurfaceState>;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const defaultWidgetOwner = {};
|
|
93
|
-
|
|
94
|
-
function getWidgetRegistry(): WidgetRegistry {
|
|
95
|
-
const key = "__piSubagentWidgetRegistry";
|
|
96
|
-
const globalStore = globalThis as Record<string, unknown>;
|
|
97
|
-
const existing = globalStore[key] as WidgetRegistry | undefined;
|
|
98
|
-
if (existing?.states instanceof WeakMap && existing.surfaces instanceof WeakMap) return existing;
|
|
99
|
-
const registry: WidgetRegistry = { states: new WeakMap(), surfaces: new WeakMap() };
|
|
100
|
-
globalStore[key] = registry;
|
|
101
|
-
return registry;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
function getWidgetState(owner: object): WidgetAnimationState {
|
|
105
|
-
const registry = getWidgetRegistry();
|
|
106
|
-
const existing = registry.states.get(owner);
|
|
107
|
-
if (existing) return existing;
|
|
108
|
-
const state: WidgetAnimationState = {
|
|
109
|
-
latestJobs: [],
|
|
110
|
-
frameNow: 0,
|
|
111
|
-
expanded: false,
|
|
112
|
-
mounted: false,
|
|
113
|
-
};
|
|
114
|
-
registry.states.set(owner, state);
|
|
115
|
-
return state;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
function getSurfaceState(surfaceKey: object): WidgetSurfaceState {
|
|
119
|
-
const registry = getWidgetRegistry();
|
|
120
|
-
const existing = registry.surfaces.get(surfaceKey);
|
|
121
|
-
if (existing) return existing;
|
|
122
|
-
const surface = {};
|
|
123
|
-
registry.surfaces.set(surfaceKey, surface);
|
|
124
|
-
return surface;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
function getExpanded(state: WidgetAnimationState): boolean {
|
|
128
|
-
if (!state.latestCtx?.hasUI) return state.expanded;
|
|
129
|
-
const expanded = state.latestCtx.ui.getToolsExpanded?.();
|
|
130
|
-
if (typeof expanded === "boolean") state.expanded = expanded;
|
|
131
|
-
return state.expanded;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
function clearWidgetState(state: WidgetAnimationState): void {
|
|
135
|
-
state.latestCtx = undefined;
|
|
136
|
-
state.latestJobs = [];
|
|
137
|
-
state.frameNow = 0;
|
|
138
|
-
state.expanded = false;
|
|
139
|
-
state.snapshotKey = undefined;
|
|
140
|
-
state.pulseFrame = undefined;
|
|
141
|
-
state.mountedCtx = undefined;
|
|
142
|
-
state.mountedOwnerKey = undefined;
|
|
143
|
-
state.surfaceKey = undefined;
|
|
144
|
-
state.mounted = false;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
function getWidgetOwnerKey(ctx: ExtensionContext): string {
|
|
148
|
-
const resolvedCwd = ctx.cwd ? path.resolve(ctx.cwd) : undefined;
|
|
149
|
-
const cwdOwner = resolvedCwd ?? "cwd:unknown";
|
|
150
|
-
let sessionOwner = "session:unknown";
|
|
151
|
-
try {
|
|
152
|
-
const sessionFile = ctx.sessionManager.getSessionFile?.();
|
|
153
|
-
if (sessionFile) {
|
|
154
|
-
const resolvedSessionFile = resolvedCwd ? path.resolve(resolvedCwd, sessionFile) : path.resolve(sessionFile);
|
|
155
|
-
sessionOwner = `sessionFile:${resolvedSessionFile}`;
|
|
156
|
-
}
|
|
157
|
-
} catch {
|
|
158
|
-
// Fall through to the session id fallback below.
|
|
159
|
-
}
|
|
160
|
-
if (sessionOwner === "session:unknown") {
|
|
161
|
-
try {
|
|
162
|
-
const sessionId = ctx.sessionManager.getSessionId?.();
|
|
163
|
-
if (sessionId) sessionOwner = `sessionId:${sessionId}`;
|
|
164
|
-
} catch {
|
|
165
|
-
// Keep the unknown marker; cwd still scopes ownership.
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
// If no session identifier is available, cwd is the best available owner
|
|
169
|
-
// boundary and may intentionally coalesce concurrent sessions in one folder.
|
|
170
|
-
return `${sessionOwner}|cwd:${cwdOwner}`;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
function requestWidgetRender(ctx: ExtensionContext): void {
|
|
174
|
-
(ctx as RenderRequestingContext).ui.requestRender?.();
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
function unmountWidgetBestEffort(ctx: ExtensionContext | undefined): void {
|
|
178
|
-
if (!ctx?.hasUI) return;
|
|
179
|
-
try {
|
|
180
|
-
ctx.ui.setWidget(WIDGET_KEY, undefined);
|
|
181
|
-
} catch {
|
|
182
|
-
// Best-effort teardown only: stale host contexts can reject cleanup during
|
|
183
|
-
// reload/session rebinding, but local state still needs to move on so the
|
|
184
|
-
// next status update can mount cleanly on the active UI context.
|
|
185
|
-
}
|
|
186
|
-
try {
|
|
187
|
-
ctx.ui.setStatus?.(WIDGET_KEY, undefined);
|
|
188
|
-
} catch {
|
|
189
|
-
// Status mirroring cleanup must never block primary widget teardown.
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
function widgetSnapshotKey(jobs: AsyncJobState[]): string {
|
|
194
|
-
return jobs.map(widgetRenderKey).join("\n");
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
function refreshWidgetSnapshot(state: WidgetAnimationState, jobs: AsyncJobState[]): void {
|
|
198
|
-
const snapshotKey = widgetSnapshotKey(jobs);
|
|
199
|
-
if (snapshotKey === state.snapshotKey) return;
|
|
200
|
-
state.snapshotKey = snapshotKey;
|
|
201
|
-
state.frameNow = Date.now();
|
|
202
|
-
state.pulseFrame = advanceResultPulseFrame(state.pulseFrame);
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
function releaseMountedWidget(state: WidgetAnimationState, owner: object): void {
|
|
206
|
-
if (state.mounted) unmountWidgetBestEffort(state.mountedCtx);
|
|
207
|
-
if (state.surfaceKey) {
|
|
208
|
-
const surface = getSurfaceState(state.surfaceKey);
|
|
209
|
-
if (surface.activeOwner === owner) surface.activeOwner = undefined;
|
|
210
|
-
}
|
|
211
|
-
clearWidgetState(state);
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
// Full teardown. Explicit API ownership is authoritative; logical context
|
|
215
|
-
// matching only protects context-driven teardown from stale session wrappers.
|
|
216
|
-
export function stopWidgetAnimation(ownerCtx?: ExtensionContext, owner: object = defaultWidgetOwner): void {
|
|
217
|
-
const state = getWidgetState(owner);
|
|
218
|
-
if (ownerCtx && state.mounted && state.mountedOwnerKey !== getWidgetOwnerKey(ownerCtx)) return;
|
|
219
|
-
if (state.surfaceKey && getSurfaceState(state.surfaceKey).activeOwner !== owner) {
|
|
220
|
-
clearWidgetState(state);
|
|
221
|
-
return;
|
|
222
|
-
}
|
|
223
|
-
releaseMountedWidget(state, owner);
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
export function buildWidgetLines(
|
|
227
|
-
jobs: AsyncJobState[],
|
|
228
|
-
theme: Theme,
|
|
229
|
-
width = getTermWidth(),
|
|
230
|
-
expanded = false,
|
|
231
|
-
now: number = Date.now(),
|
|
232
|
-
pulseFrame?: number,
|
|
233
|
-
): string[] {
|
|
234
|
-
if (jobs.length === 0) return [];
|
|
235
|
-
if (jobs.length === 1) return buildSingleWidgetLines(jobs[0]!, theme, width, expanded, now, pulseFrame);
|
|
236
|
-
const running = jobs.filter((job) => job.status === "running");
|
|
237
|
-
const queued = jobs.filter((job) => job.status === "queued");
|
|
238
|
-
const finished = jobs.filter((job) => job.status !== "running" && job.status !== "queued");
|
|
239
|
-
|
|
240
|
-
const lines: string[] = [];
|
|
241
|
-
const hasActive = running.length > 0 || queued.length > 0;
|
|
242
|
-
const headerGlyph = running.length > 0 ? runningPulseGlyph(pulseFrame) : hasActive ? "●" : "○";
|
|
243
|
-
lines.push(
|
|
244
|
-
truncLine(
|
|
245
|
-
`${theme.fg(hasActive ? "accent" : "dim", headerGlyph)} ${theme.fg(hasActive ? "accent" : "dim", "Async agents")} ${theme.fg("dim", "· background")}`,
|
|
246
|
-
width,
|
|
247
|
-
),
|
|
248
|
-
);
|
|
249
|
-
|
|
250
|
-
const items: string[][] = [];
|
|
251
|
-
let hiddenRunning = 0;
|
|
252
|
-
let hiddenFinished = 0;
|
|
253
|
-
let queuedSummaryShown = false;
|
|
254
|
-
let slots = MAX_WIDGET_JOBS;
|
|
255
|
-
|
|
256
|
-
for (const job of running) {
|
|
257
|
-
if (slots <= 0) {
|
|
258
|
-
hiddenRunning++;
|
|
259
|
-
continue;
|
|
260
|
-
}
|
|
261
|
-
const stats = widgetStats(job, theme);
|
|
262
|
-
items.push([
|
|
263
|
-
`${widgetStatusGlyph(job, theme, pulseFrame)} ${themeBold(theme, widgetJobName(job))}${stats ? ` ${theme.fg("dim", "·")} ${stats}` : ""}`,
|
|
264
|
-
` ${theme.fg("dim", `⎿ ${widgetActivity(job)}`)}`,
|
|
265
|
-
...widgetParallelAgentDetails(job, theme, expanded, width, now, pulseFrame),
|
|
266
|
-
]);
|
|
267
|
-
slots--;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
if (queued.length > 0 && slots > 0) {
|
|
271
|
-
items.push([`${theme.fg("muted", "◦")} ${theme.fg("dim", `${queued.length} queued`)}`]);
|
|
272
|
-
queuedSummaryShown = true;
|
|
273
|
-
slots--;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
for (const job of finished) {
|
|
277
|
-
if (slots <= 0) {
|
|
278
|
-
hiddenFinished++;
|
|
279
|
-
continue;
|
|
280
|
-
}
|
|
281
|
-
const stats = widgetStats(job, theme);
|
|
282
|
-
items.push([
|
|
283
|
-
`${widgetStatusGlyph(job, theme, pulseFrame)} ${themeBold(theme, widgetJobName(job))}${stats ? ` ${theme.fg("dim", "·")} ${stats}` : ""}`,
|
|
284
|
-
` ${theme.fg("dim", `⎿ ${widgetActivity(job)}`)}`,
|
|
285
|
-
...widgetParallelAgentDetails(job, theme, expanded, width, now, pulseFrame),
|
|
286
|
-
]);
|
|
287
|
-
slots--;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
const hiddenQueued = queued.length > 0 && !queuedSummaryShown ? queued.length : 0;
|
|
291
|
-
const hiddenTotal = hiddenRunning + hiddenFinished + hiddenQueued;
|
|
292
|
-
if (hiddenTotal > 0) {
|
|
293
|
-
const parts: string[] = [];
|
|
294
|
-
if (hiddenRunning > 0) parts.push(`${hiddenRunning} running`);
|
|
295
|
-
if (hiddenQueued > 0) parts.push(`${hiddenQueued} queued`);
|
|
296
|
-
if (hiddenFinished > 0) parts.push(`${hiddenFinished} finished`);
|
|
297
|
-
items.push([theme.fg("dim", `+${hiddenTotal} more (${parts.join(", ")})`)]);
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
for (let i = 0; i < items.length; i++) {
|
|
301
|
-
const item = items[i]!;
|
|
302
|
-
const last = i === items.length - 1;
|
|
303
|
-
const branch = last ? "└─" : "├─";
|
|
304
|
-
const continuation = last ? " " : "│ ";
|
|
305
|
-
lines.push(truncLine(`${theme.fg("dim", branch)} ${item[0]}`, width));
|
|
306
|
-
for (const detail of item.slice(1)) {
|
|
307
|
-
lines.push(truncLine(`${theme.fg("dim", continuation)} ${detail}`, width));
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
return lines;
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
/** Render the async jobs widget for one ExtensionAPI owner. */
|
|
315
|
-
export function renderWidget(ctx: ExtensionContext, jobs: AsyncJobState[], owner: object = defaultWidgetOwner): void {
|
|
316
|
-
const state = getWidgetState(owner);
|
|
317
|
-
const ownerKey = getWidgetOwnerKey(ctx);
|
|
318
|
-
const requestedSurfaceKey = ctx.ui.setWidget;
|
|
319
|
-
const requestedSurface = getSurfaceState(requestedSurfaceKey);
|
|
320
|
-
if (jobs.length === 0) {
|
|
321
|
-
if (requestedSurface.activeOwner && requestedSurface.activeOwner !== owner) return;
|
|
322
|
-
if (state.surfaceKey && getSurfaceState(state.surfaceKey).activeOwner !== owner) return;
|
|
323
|
-
if (state.mounted && state.mountedOwnerKey !== ownerKey) return;
|
|
324
|
-
stopWidgetAnimation(ctx, owner);
|
|
325
|
-
return;
|
|
326
|
-
}
|
|
327
|
-
if (!ctx.hasUI) {
|
|
328
|
-
stopWidgetAnimation(ctx, owner);
|
|
329
|
-
return;
|
|
330
|
-
}
|
|
331
|
-
// Workflow stages forward the host's setWidget function, which identifies the
|
|
332
|
-
// shared widget surface. A stage cannot replace a parent already active there.
|
|
333
|
-
if (!state.mounted && requestedSurface.activeOwner && requestedSurface.activeOwner !== owner) return;
|
|
334
|
-
state.latestCtx = ctx;
|
|
335
|
-
state.latestJobs = [...jobs];
|
|
336
|
-
refreshWidgetSnapshot(state, jobs);
|
|
337
|
-
if (state.mounted && state.mountedOwnerKey !== ownerKey) {
|
|
338
|
-
releaseMountedWidget(state, owner);
|
|
339
|
-
state.latestCtx = ctx;
|
|
340
|
-
state.latestJobs = [...jobs];
|
|
341
|
-
refreshWidgetSnapshot(state, jobs);
|
|
342
|
-
}
|
|
343
|
-
if (!state.mounted) {
|
|
344
|
-
const surface = getSurfaceState(requestedSurfaceKey);
|
|
345
|
-
if (surface.activeOwner && surface.activeOwner !== owner) return;
|
|
346
|
-
ctx.ui.setWidget(
|
|
347
|
-
WIDGET_KEY,
|
|
348
|
-
buildWidgetComponent(
|
|
349
|
-
() => state.latestJobs,
|
|
350
|
-
() => getExpanded(state),
|
|
351
|
-
() => state.frameNow,
|
|
352
|
-
() => state.pulseFrame,
|
|
353
|
-
),
|
|
354
|
-
{ placement: "belowEditor" },
|
|
355
|
-
);
|
|
356
|
-
state.mountedCtx = ctx;
|
|
357
|
-
state.mountedOwnerKey = ownerKey;
|
|
358
|
-
state.surfaceKey = requestedSurfaceKey;
|
|
359
|
-
state.mounted = true;
|
|
360
|
-
surface.activeOwner = owner;
|
|
361
|
-
} else {
|
|
362
|
-
state.mountedCtx = ctx;
|
|
363
|
-
state.mountedOwnerKey = ownerKey;
|
|
364
|
-
requestWidgetRender(ctx);
|
|
365
|
-
}
|
|
366
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../../src/core/async/format.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAwB/E,wBAAgB,4BAA4B,CAAC,GAAG,EAAE,mBAAmB,GAAG,uBAAuB,CA6B9F"}
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
const INLINE_OUTPUT_LIMIT = 12_000;
|
|
2
|
-
const LARGE_OUTPUT_PREVIEW_LIMIT = 4_000;
|
|
3
|
-
function truncateText(text, maxChars) {
|
|
4
|
-
if (text.length <= maxChars)
|
|
5
|
-
return text;
|
|
6
|
-
return text.slice(0, maxChars).trimEnd();
|
|
7
|
-
}
|
|
8
|
-
function jobOutput(job) {
|
|
9
|
-
const output = job.output.trimEnd();
|
|
10
|
-
if (output.length > 0)
|
|
11
|
-
return output;
|
|
12
|
-
return job.error ? `(no output)\n\n${job.error}` : "(no output)";
|
|
13
|
-
}
|
|
14
|
-
function statusLine(job) {
|
|
15
|
-
if (job.error)
|
|
16
|
-
return `Error: ${job.error}`;
|
|
17
|
-
if (job.exitCode !== undefined && job.exitCode !== null && job.exitCode !== 0) {
|
|
18
|
-
return `Command exited with code ${job.exitCode}`;
|
|
19
|
-
}
|
|
20
|
-
return undefined;
|
|
21
|
-
}
|
|
22
|
-
export function formatAsyncResultForFollowUp(job) {
|
|
23
|
-
const elapsedMs = (job.endedAt ?? Date.now()) - job.startedAt;
|
|
24
|
-
const header = `Async bash job ${job.jobId} ${job.status}: ${job.command}`;
|
|
25
|
-
const body = jobOutput(job);
|
|
26
|
-
const status = statusLine(job);
|
|
27
|
-
const inline = [header, body, status]
|
|
28
|
-
.filter((part) => part !== undefined && part.length > 0)
|
|
29
|
-
.join("\n\n");
|
|
30
|
-
let content = inline;
|
|
31
|
-
if (inline.length > INLINE_OUTPUT_LIMIT && job.fullOutputPath) {
|
|
32
|
-
const preview = truncateText(body, LARGE_OUTPUT_PREVIEW_LIMIT);
|
|
33
|
-
content = [header, preview, `[Output truncated for async follow-up. Full output: ${job.fullOutputPath}]`, status]
|
|
34
|
-
.filter((part) => part !== undefined && part.length > 0)
|
|
35
|
-
.join("\n\n");
|
|
36
|
-
}
|
|
37
|
-
return {
|
|
38
|
-
customType: "async-job-result",
|
|
39
|
-
content,
|
|
40
|
-
display: true,
|
|
41
|
-
details: {
|
|
42
|
-
jobId: job.jobId,
|
|
43
|
-
type: "bash",
|
|
44
|
-
status: job.status,
|
|
45
|
-
command: job.command,
|
|
46
|
-
exitCode: job.exitCode,
|
|
47
|
-
fullOutputPath: job.fullOutputPath,
|
|
48
|
-
wallTimeMs: elapsedMs,
|
|
49
|
-
},
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
//# sourceMappingURL=format.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"format.js","sourceRoot":"","sources":["../../../src/core/async/format.ts"],"names":[],"mappings":"AAEA,MAAM,mBAAmB,GAAG,MAAM,CAAC;AACnC,MAAM,0BAA0B,GAAG,KAAK,CAAC;AAEzC,SAAS,YAAY,CAAC,IAAY,EAAE,QAAgB;IACnD,IAAI,IAAI,CAAC,MAAM,IAAI,QAAQ;QAAE,OAAO,IAAI,CAAC;IACzC,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,OAAO,EAAE,CAAC;AAC1C,CAAC;AAED,SAAS,SAAS,CAAC,GAAwB;IAC1C,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;IACpC,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,MAAM,CAAC;IACrC,OAAO,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,kBAAkB,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC;AAClE,CAAC;AAED,SAAS,UAAU,CAAC,GAAwB;IAC3C,IAAI,GAAG,CAAC,KAAK;QAAE,OAAO,UAAU,GAAG,CAAC,KAAK,EAAE,CAAC;IAC5C,IAAI,GAAG,CAAC,QAAQ,KAAK,SAAS,IAAI,GAAG,CAAC,QAAQ,KAAK,IAAI,IAAI,GAAG,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;QAC/E,OAAO,4BAA4B,GAAG,CAAC,QAAQ,EAAE,CAAC;IACnD,CAAC;IACD,OAAO,SAAS,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,4BAA4B,CAAC,GAAwB;IACpE,MAAM,SAAS,GAAG,CAAC,GAAG,CAAC,OAAO,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC,GAAG,GAAG,CAAC,SAAS,CAAC;IAC9D,MAAM,MAAM,GAAG,kBAAkB,GAAG,CAAC,KAAK,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,CAAC,OAAO,EAAE,CAAC;IAC3E,MAAM,IAAI,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;IAC5B,MAAM,MAAM,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC;IAC/B,MAAM,MAAM,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC;SACnC,MAAM,CAAC,CAAC,IAAI,EAAkB,EAAE,CAAC,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;SACvE,IAAI,CAAC,MAAM,CAAC,CAAC;IACf,IAAI,OAAO,GAAG,MAAM,CAAC;IACrB,IAAI,MAAM,CAAC,MAAM,GAAG,mBAAmB,IAAI,GAAG,CAAC,cAAc,EAAE,CAAC;QAC/D,MAAM,OAAO,GAAG,YAAY,CAAC,IAAI,EAAE,0BAA0B,CAAC,CAAC;QAC/D,OAAO,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,uDAAuD,GAAG,CAAC,cAAc,GAAG,EAAE,MAAM,CAAC;aAC/G,MAAM,CAAC,CAAC,IAAI,EAAkB,EAAE,CAAC,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;aACvE,IAAI,CAAC,MAAM,CAAC,CAAC;IAChB,CAAC;IACD,OAAO;QACN,UAAU,EAAE,kBAAkB;QAC9B,OAAO;QACP,OAAO,EAAE,IAAI;QACb,OAAO,EAAE;YACR,KAAK,EAAE,GAAG,CAAC,KAAK;YAChB,IAAI,EAAE,MAAM;YACZ,MAAM,EAAE,GAAG,CAAC,MAAM;YAClB,OAAO,EAAE,GAAG,CAAC,OAAO;YACpB,QAAQ,EAAE,GAAG,CAAC,QAAQ;YACtB,cAAc,EAAE,GAAG,CAAC,cAAc;YAClC,UAAU,EAAE,SAAS;SACrB;KACD,CAAC;AACH,CAAC","sourcesContent":["import type { AsyncJobDeliveryMessage, ManagedAsyncBashJob } from \"./types.js\";\n\nconst INLINE_OUTPUT_LIMIT = 12_000;\nconst LARGE_OUTPUT_PREVIEW_LIMIT = 4_000;\n\nfunction truncateText(text: string, maxChars: number): string {\n\tif (text.length <= maxChars) return text;\n\treturn text.slice(0, maxChars).trimEnd();\n}\n\nfunction jobOutput(job: ManagedAsyncBashJob): string {\n\tconst output = job.output.trimEnd();\n\tif (output.length > 0) return output;\n\treturn job.error ? `(no output)\\n\\n${job.error}` : \"(no output)\";\n}\n\nfunction statusLine(job: ManagedAsyncBashJob): string | undefined {\n\tif (job.error) return `Error: ${job.error}`;\n\tif (job.exitCode !== undefined && job.exitCode !== null && job.exitCode !== 0) {\n\t\treturn `Command exited with code ${job.exitCode}`;\n\t}\n\treturn undefined;\n}\n\nexport function formatAsyncResultForFollowUp(job: ManagedAsyncBashJob): AsyncJobDeliveryMessage {\n\tconst elapsedMs = (job.endedAt ?? Date.now()) - job.startedAt;\n\tconst header = `Async bash job ${job.jobId} ${job.status}: ${job.command}`;\n\tconst body = jobOutput(job);\n\tconst status = statusLine(job);\n\tconst inline = [header, body, status]\n\t\t.filter((part): part is string => part !== undefined && part.length > 0)\n\t\t.join(\"\\n\\n\");\n\tlet content = inline;\n\tif (inline.length > INLINE_OUTPUT_LIMIT && job.fullOutputPath) {\n\t\tconst preview = truncateText(body, LARGE_OUTPUT_PREVIEW_LIMIT);\n\t\tcontent = [header, preview, `[Output truncated for async follow-up. Full output: ${job.fullOutputPath}]`, status]\n\t\t\t.filter((part): part is string => part !== undefined && part.length > 0)\n\t\t\t.join(\"\\n\\n\");\n\t}\n\treturn {\n\t\tcustomType: \"async-job-result\",\n\t\tcontent,\n\t\tdisplay: true,\n\t\tdetails: {\n\t\t\tjobId: job.jobId,\n\t\t\ttype: \"bash\",\n\t\t\tstatus: job.status,\n\t\t\tcommand: job.command,\n\t\t\texitCode: job.exitCode,\n\t\t\tfullOutputPath: job.fullOutputPath,\n\t\t\twallTimeMs: elapsedMs,\n\t\t},\n\t};\n}\n"]}
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { type ManagedBashJob } from "../tools/bash-async-jobs.js";
|
|
2
|
-
import type { AsyncJobDeliveryCallback, AsyncJobDeliveryHandler } from "./types.js";
|
|
3
|
-
interface AsyncJobManagerOptions {
|
|
4
|
-
onJobComplete: AsyncJobDeliveryCallback;
|
|
5
|
-
maxRunningJobs?: number;
|
|
6
|
-
maxRetainedJobs?: number;
|
|
7
|
-
completedJobTtlMs?: number;
|
|
8
|
-
}
|
|
9
|
-
export declare class AsyncJobManager {
|
|
10
|
-
#private;
|
|
11
|
-
static instance(): AsyncJobManager | undefined;
|
|
12
|
-
static setInstance(value: AsyncJobManager | undefined): void;
|
|
13
|
-
static resetForTests(): void;
|
|
14
|
-
constructor(options: AsyncJobManagerOptions);
|
|
15
|
-
get disposed(): boolean;
|
|
16
|
-
get atCapacity(): boolean;
|
|
17
|
-
registerSession(): symbol;
|
|
18
|
-
releaseSession(sessionId: symbol): void;
|
|
19
|
-
transferSessionDeliveries(sourceId: symbol, targetId: symbol, handler: AsyncJobDeliveryHandler): void;
|
|
20
|
-
isSessionDisposed(sessionId: symbol): boolean;
|
|
21
|
-
registerBashJob(job: ManagedBashJob, onComplete?: AsyncJobDeliveryHandler, sessionId?: symbol): void;
|
|
22
|
-
completeBashJob(job: ManagedBashJob): void;
|
|
23
|
-
acknowledgeDeliveries(jobIds: readonly string[]): void;
|
|
24
|
-
isDeliverySuppressed(jobId: string): boolean;
|
|
25
|
-
deliveryState(): {
|
|
26
|
-
queued: number;
|
|
27
|
-
delivering: boolean;
|
|
28
|
-
pendingJobIds: string[];
|
|
29
|
-
};
|
|
30
|
-
retentionState(): {
|
|
31
|
-
jobs: number;
|
|
32
|
-
suppressions: number;
|
|
33
|
-
handlers: number;
|
|
34
|
-
queued: number;
|
|
35
|
-
sessions: number;
|
|
36
|
-
};
|
|
37
|
-
dispose(): void;
|
|
38
|
-
}
|
|
39
|
-
export {};
|
|
40
|
-
//# sourceMappingURL=job-manager.d.ts.map
|