@pellux/goodvibes-tui 1.1.0 → 1.9.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/CHANGELOG.md +85 -21
- package/README.md +28 -18
- package/docs/foundation-artifacts/operator-contract.json +4045 -1763
- package/package.json +2 -2
- package/src/audio/spoken-turn-controller.ts +12 -2
- package/src/audio/spoken-turn-wiring.ts +2 -1
- package/src/cli/help.ts +8 -1
- package/src/cli/service-posture.ts +2 -1
- package/src/cli/status.ts +2 -1
- package/src/cli/surface-command.ts +2 -2
- package/src/core/alert-gating.ts +67 -0
- package/src/core/approval-alert.ts +72 -0
- package/src/core/budget-breach-notifier.ts +106 -0
- package/src/core/composer-state.ts +11 -3
- package/src/core/conversation-line-cache.ts +27 -3
- package/src/core/conversation-rendering.ts +90 -14
- package/src/core/conversation.ts +18 -0
- package/src/core/focus-tracker.ts +41 -0
- package/src/core/long-task-notifier.ts +33 -6
- package/src/core/stream-event-wiring.ts +20 -1
- package/src/core/system-message-noise.ts +87 -0
- package/src/core/system-message-router.ts +104 -51
- package/src/core/turn-cancellation.ts +25 -0
- package/src/core/turn-event-wiring.ts +72 -3
- package/src/daemon/cli.ts +29 -2
- package/src/daemon/handlers/register.ts +8 -1
- package/src/daemon/service-commands.ts +329 -0
- package/src/export/cost-utils.ts +36 -0
- package/src/input/autocomplete.ts +27 -1
- package/src/input/command-registry.ts +84 -5
- package/src/input/commands/checkpoint-runtime.ts +280 -0
- package/src/input/commands/codebase-runtime.ts +232 -0
- package/src/input/commands/config.ts +43 -3
- package/src/input/commands/eval.ts +1 -1
- package/src/input/commands/health-runtime.ts +10 -2
- package/src/input/commands/image-runtime.ts +112 -0
- package/src/input/commands/intelligence-runtime.ts +11 -1
- package/src/input/commands/local-auth-runtime.ts +4 -1
- package/src/input/commands/local-runtime.ts +9 -3
- package/src/input/commands/marketplace-runtime.ts +2 -2
- package/src/input/commands/memory.ts +73 -35
- package/src/input/commands/operator-panel-runtime.ts +71 -31
- package/src/input/commands/planning-runtime.ts +116 -4
- package/src/input/commands/platform-sandbox-runtime.ts +1 -6
- package/src/input/commands/plugin-runtime.ts +2 -2
- package/src/input/commands/policy-dispatch.ts +21 -0
- package/src/input/commands/provider-accounts-runtime.ts +1 -1
- package/src/input/commands/qrcode-runtime.ts +26 -6
- package/src/input/commands/recall-review.ts +35 -0
- package/src/input/commands/remote-runtime-setup.ts +5 -3
- package/src/input/commands/remote-runtime.ts +3 -3
- package/src/input/commands/runtime-services.ts +54 -13
- package/src/input/commands/services-runtime.ts +1 -1
- package/src/input/commands/session-content.ts +20 -9
- package/src/input/commands/session-workflow.ts +16 -1
- package/src/input/commands/settings-sync-runtime.ts +16 -4
- package/src/input/commands/shell-core.ts +25 -8
- package/src/input/commands/skills-runtime.ts +2 -8
- package/src/input/commands/subscription-runtime.ts +2 -2
- package/src/input/commands/test-runtime.ts +277 -0
- package/src/input/commands/websearch-runtime.ts +101 -0
- package/src/input/commands/work-plan-runtime.ts +36 -10
- package/src/input/commands/workstream-runtime.ts +488 -0
- package/src/input/commands.ts +12 -0
- package/src/input/config-modal-types.ts +175 -0
- package/src/input/config-modal.ts +592 -0
- package/src/input/feed-context-factory.ts +10 -8
- package/src/input/handler-command-route.ts +37 -20
- package/src/input/handler-content-actions.ts +17 -2
- package/src/input/handler-feed-routes.ts +74 -114
- package/src/input/handler-feed.ts +79 -27
- package/src/input/handler-interactions.ts +1 -3
- package/src/input/handler-modal-routes.ts +136 -6
- package/src/input/handler-modal-stack.ts +14 -7
- package/src/input/handler-modal-token-routes.ts +16 -49
- package/src/input/handler-onboarding-daemon-adopt.ts +149 -0
- package/src/input/handler-onboarding.ts +71 -59
- package/src/input/handler-picker-routes.ts +26 -102
- package/src/input/handler-shortcuts.ts +74 -14
- package/src/input/handler-types.ts +2 -6
- package/src/input/handler-ui-state.ts +10 -22
- package/src/input/handler.ts +12 -29
- package/src/input/keybindings.ts +23 -8
- package/src/input/model-picker-types.ts +24 -6
- package/src/input/model-picker.ts +77 -2
- package/src/input/onboarding/onboarding-runtime-status.ts +10 -1
- package/src/input/onboarding/onboarding-wizard-apply.ts +8 -1
- package/src/input/onboarding/onboarding-wizard-constants.ts +4 -1
- package/src/input/onboarding/onboarding-wizard-network-adopt.ts +136 -0
- package/src/input/onboarding/onboarding-wizard-steps.ts +9 -6
- package/src/input/onboarding/onboarding-wizard-types.ts +3 -1
- package/src/input/panel-integration-actions.ts +9 -170
- package/src/input/panel-mouse-geometry.ts +97 -0
- package/src/input/panel-paste-flood-guard.ts +86 -0
- package/src/input/selection-modal.ts +11 -0
- package/src/input/session-picker-modal.ts +44 -4
- package/src/input/settings-modal-data.ts +146 -2
- package/src/input/settings-modal-mutations.ts +6 -4
- package/src/input/settings-modal-types.ts +4 -2
- package/src/main.ts +49 -51
- package/src/panels/agent-inspector-shared.ts +2 -1
- package/src/panels/base-panel.ts +22 -1
- package/src/panels/builtin/agent.ts +21 -107
- package/src/panels/builtin/development.ts +15 -61
- package/src/panels/builtin/knowledge.ts +8 -32
- package/src/panels/builtin/operations.ts +84 -428
- package/src/panels/builtin/session.ts +21 -112
- package/src/panels/builtin/shared.ts +18 -46
- package/src/panels/builtin-modals.ts +218 -0
- package/src/panels/builtin-panels.ts +5 -0
- package/src/panels/cost-tracker-panel.ts +36 -10
- package/src/panels/diff-panel.ts +12 -43
- package/src/panels/eval-registry.ts +60 -0
- package/src/panels/fleet-deep-link.ts +31 -0
- package/src/panels/fleet-panel-format.ts +62 -0
- package/src/panels/fleet-panel-worktree-detail.ts +48 -0
- package/src/panels/fleet-panel.ts +758 -0
- package/src/panels/fleet-read-model.ts +520 -0
- package/src/panels/fleet-steer.ts +125 -0
- package/src/panels/fleet-stop.ts +153 -0
- package/src/panels/fleet-tabs.ts +230 -0
- package/src/panels/fleet-transcript.ts +356 -0
- package/src/panels/index.ts +7 -31
- package/src/panels/modals/hooks-modal.ts +187 -0
- package/src/panels/modals/keybindings-modal.ts +166 -0
- package/src/panels/modals/knowledge-modal.ts +158 -0
- package/src/panels/modals/local-auth-modal.ts +132 -0
- package/src/panels/modals/marketplace-modal.ts +218 -0
- package/src/panels/modals/memory-modal.ts +180 -0
- package/src/panels/modals/modal-surface-helpers.ts +54 -0
- package/src/panels/modals/modal-theme.ts +42 -0
- package/src/panels/modals/pairing-modal.ts +163 -0
- package/src/panels/modals/planning-modal.ts +304 -0
- package/src/panels/modals/plugins-modal.ts +174 -0
- package/src/panels/modals/policy-modal.ts +256 -0
- package/src/panels/modals/provider-health-modal.ts +136 -0
- package/src/panels/modals/remote-modal.ts +115 -0
- package/src/panels/modals/sandbox-modal.ts +150 -0
- package/src/panels/modals/security-modal.ts +217 -0
- package/src/panels/modals/services-modal.ts +179 -0
- package/src/panels/modals/settings-sync-modal.ts +142 -0
- package/src/panels/modals/skills-modal.ts +189 -0
- package/src/panels/modals/subscription-modal.ts +172 -0
- package/src/panels/modals/work-plan-modal.ts +177 -0
- package/src/panels/panel-confirm-overlay.ts +72 -0
- package/src/panels/panel-manager.ts +123 -9
- package/src/panels/polish-core.ts +38 -25
- package/src/panels/project-planning-answer-actions.ts +8 -11
- package/src/panels/skills-panel.ts +7 -51
- package/src/panels/types.ts +37 -0
- package/src/permissions/hunk-selection.ts +179 -0
- package/src/permissions/prompt.ts +216 -13
- package/src/renderer/autocomplete-overlay.ts +28 -2
- package/src/renderer/compaction-history-modal.ts +19 -3
- package/src/renderer/compaction-preview.ts +13 -2
- package/src/renderer/compaction-quality.ts +100 -0
- package/src/renderer/compositor.ts +2 -3
- package/src/renderer/config-modal.ts +95 -0
- package/src/renderer/conversation-overlays.ts +10 -17
- package/src/renderer/fleet-tab-strip.ts +56 -0
- package/src/renderer/footer-tips.ts +15 -3
- package/src/renderer/fullscreen-primitives.ts +32 -22
- package/src/renderer/git-status.ts +43 -1
- package/src/renderer/help-overlay.ts +2 -2
- package/src/renderer/layout.ts +0 -4
- package/src/renderer/markdown.ts +7 -3
- package/src/renderer/modal-factory.ts +25 -20
- package/src/renderer/model-workspace.ts +62 -4
- package/src/renderer/overlay-box.ts +21 -17
- package/src/renderer/panel-workspace-bar.ts +8 -2
- package/src/renderer/process-indicator.ts +14 -3
- package/src/renderer/selection-modal-overlay.ts +6 -1
- package/src/renderer/session-picker-modal.ts +196 -3
- package/src/renderer/settings-modal-helpers.ts +2 -0
- package/src/renderer/settings-modal.ts +7 -0
- package/src/renderer/shell-surface.ts +8 -1
- package/src/renderer/status-glyphs.ts +14 -15
- package/src/renderer/system-message.ts +15 -3
- package/src/renderer/terminal-bg-probe.ts +339 -0
- package/src/renderer/terminal-escapes.ts +20 -0
- package/src/renderer/theme-mode-config.ts +67 -0
- package/src/renderer/theme.ts +91 -1
- package/src/renderer/thinking.ts +11 -3
- package/src/renderer/tool-call.ts +15 -9
- package/src/renderer/tool-result-summary.ts +148 -0
- package/src/renderer/turn-injection.ts +133 -0
- package/src/renderer/ui-factory.ts +154 -85
- package/src/renderer/ui-primitives.ts +30 -129
- package/src/runtime/bootstrap-command-context.ts +27 -1
- package/src/runtime/bootstrap-command-parts.ts +41 -10
- package/src/runtime/bootstrap-core.ts +47 -15
- package/src/runtime/bootstrap-hook-bridge.ts +7 -0
- package/src/runtime/bootstrap-shell.ts +48 -17
- package/src/runtime/bootstrap.ts +129 -22
- package/src/runtime/code-index-services.ts +135 -0
- package/src/runtime/legacy-daemon-migration.ts +516 -0
- package/src/runtime/memory-fold.ts +26 -0
- package/src/runtime/onboarding/derivation.ts +7 -2
- package/src/runtime/onboarding/snapshot.ts +27 -1
- package/src/runtime/onboarding/types.ts +31 -1
- package/src/runtime/operator-token-cleanup.ts +82 -1
- package/src/runtime/orchestrator-core-services.ts +59 -0
- package/src/runtime/process-lifecycle.ts +3 -1
- package/src/runtime/resume-notice.ts +209 -0
- package/src/runtime/services.ts +98 -46
- package/src/runtime/session-inbound-inputs.ts +252 -0
- package/src/runtime/session-spine-transport.ts +64 -0
- package/src/runtime/terminal-output-guard.ts +15 -8
- package/src/runtime/ui-services.ts +27 -3
- package/src/runtime/workstream-services.ts +327 -0
- package/src/runtime/wrfc-persistence.ts +124 -17
- package/src/shell/blocking-input.ts +68 -4
- package/src/shell/recovery-input-helpers.ts +170 -1
- package/src/shell/ui-openers.ts +171 -26
- package/src/utils/format-duration.ts +8 -18
- package/src/utils/splash-lines.ts +1 -1
- package/src/utils/terminal-width.ts +52 -0
- package/src/version.ts +1 -1
- package/src/panels/agent-inspector-panel.ts +0 -786
- package/src/panels/approval-panel.ts +0 -252
- package/src/panels/automation-control-panel.ts +0 -479
- package/src/panels/cockpit-panel.ts +0 -481
- package/src/panels/cockpit-read-model.ts +0 -238
- package/src/panels/communication-panel.ts +0 -256
- package/src/panels/control-plane-panel.ts +0 -470
- package/src/panels/debug-panel.ts +0 -615
- package/src/panels/docs-panel.ts +0 -384
- package/src/panels/eval-panel.ts +0 -627
- package/src/panels/file-explorer-panel.ts +0 -673
- package/src/panels/file-preview-panel.ts +0 -517
- package/src/panels/hooks-panel.ts +0 -401
- package/src/panels/incident-review-panel.ts +0 -406
- package/src/panels/intelligence-panel.ts +0 -383
- package/src/panels/knowledge-graph-panel.ts +0 -515
- package/src/panels/marketplace-panel.ts +0 -399
- package/src/panels/memory-panel.ts +0 -558
- package/src/panels/ops-control-panel.ts +0 -329
- package/src/panels/ops-strategy-panel.ts +0 -321
- package/src/panels/orchestration-panel.ts +0 -465
- package/src/panels/panel-list-panel.ts +0 -566
- package/src/panels/plan-dashboard-panel.ts +0 -707
- package/src/panels/policy-panel.ts +0 -517
- package/src/panels/project-planning-panel.ts +0 -731
- package/src/panels/provider-health-panel.ts +0 -678
- package/src/panels/provider-health-tracker.ts +0 -310
- package/src/panels/provider-health-views.ts +0 -567
- package/src/panels/qr-panel.ts +0 -280
- package/src/panels/remote-panel.ts +0 -534
- package/src/panels/routes-panel.ts +0 -241
- package/src/panels/sandbox-panel.ts +0 -456
- package/src/panels/security-panel.ts +0 -447
- package/src/panels/services-panel.ts +0 -329
- package/src/panels/session-browser-panel.ts +0 -496
- package/src/panels/settings-sync-panel.ts +0 -398
- package/src/panels/subscription-panel.ts +0 -342
- package/src/panels/symbol-outline-panel.ts +0 -619
- package/src/panels/system-messages-panel.ts +0 -364
- package/src/panels/tasks-panel.ts +0 -608
- package/src/panels/thinking-panel.ts +0 -333
- package/src/panels/tool-inspector-panel.ts +0 -537
- package/src/panels/work-plan-panel.ts +0 -540
- package/src/panels/worktree-panel.ts +0 -360
- package/src/panels/wrfc-panel.ts +0 -790
- package/src/renderer/agent-detail-modal.ts +0 -466
- package/src/renderer/live-tail-modal.ts +0 -156
- package/src/renderer/process-modal.ts +0 -671
- package/src/renderer/qr-renderer.ts +0 -120
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* summarizeToolResult — one-line human summaries for tool RESULTS, so the
|
|
3
|
+
* transcript (and the fleet attach view, which shares the same rendering seam
|
|
4
|
+
* in conversation-rendering.ts) shows "wrote haiku.txt (532 B)" instead of a
|
|
5
|
+
* raw `{"files_written":1,"bytes_written":532,...}` JSON blob. The full payload
|
|
6
|
+
* stays reachable behind the existing collapse/expand toggle — this only
|
|
7
|
+
* changes the collapsed preview line, never destroys data. (UX-B item 3.)
|
|
8
|
+
*
|
|
9
|
+
* Returning `null` means "no honest summary for this shape" — the caller then
|
|
10
|
+
* falls back to the previous raw-first-line preview, so an unrecognised tool or
|
|
11
|
+
* an unexpected payload is never mis-summarised.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/** Compact byte count: "87 B", "1.2 KB", "3.4 MB". */
|
|
15
|
+
function formatBytes(n: number): string {
|
|
16
|
+
if (!Number.isFinite(n) || n < 0) return '? B';
|
|
17
|
+
if (n < 1024) return `${n} B`;
|
|
18
|
+
if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`;
|
|
19
|
+
return `${(n / (1024 * 1024)).toFixed(1)} MB`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Compact duration: "820ms", "1.2s", "1m3s". */
|
|
23
|
+
function formatDuration(ms: number): string {
|
|
24
|
+
if (!Number.isFinite(ms) || ms < 0) return '?';
|
|
25
|
+
if (ms < 1000) return `${Math.round(ms)}ms`;
|
|
26
|
+
if (ms < 60_000) return `${(ms / 1000).toFixed(1)}s`;
|
|
27
|
+
const s = Math.round(ms / 1000);
|
|
28
|
+
return `${Math.floor(s / 60)}m${s % 60}s`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Trailing filename for compact display (paths can be long/absolute). */
|
|
32
|
+
function baseName(p: string): string {
|
|
33
|
+
const cleaned = p.replace(/[/\\]+$/, '');
|
|
34
|
+
const idx = Math.max(cleaned.lastIndexOf('/'), cleaned.lastIndexOf('\\'));
|
|
35
|
+
return idx >= 0 ? cleaned.slice(idx + 1) : cleaned;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function asNumber(v: unknown): number | undefined {
|
|
39
|
+
return typeof v === 'number' && Number.isFinite(v) ? v : undefined;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function firstPath(files: unknown): string | undefined {
|
|
43
|
+
if (!Array.isArray(files) || files.length === 0) return undefined;
|
|
44
|
+
const first = files[0];
|
|
45
|
+
if (typeof first === 'string') return first;
|
|
46
|
+
if (first && typeof first === 'object') {
|
|
47
|
+
const rec = first as Record<string, unknown>;
|
|
48
|
+
if (typeof rec.path === 'string') return rec.path;
|
|
49
|
+
if (typeof rec.resolved_path === 'string') return rec.resolved_path;
|
|
50
|
+
}
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** write: { files_written, bytes_written, files?: [{path, bytes_written}], dry_run? } */
|
|
55
|
+
function summarizeWrite(obj: Record<string, unknown>): string | null {
|
|
56
|
+
const count = asNumber(obj.files_written);
|
|
57
|
+
const bytes = asNumber(obj.bytes_written);
|
|
58
|
+
if (count === undefined) return null;
|
|
59
|
+
const verb = obj.dry_run === true ? 'would write' : 'wrote';
|
|
60
|
+
const size = bytes !== undefined ? ` (${formatBytes(bytes)})` : '';
|
|
61
|
+
if (count === 1) {
|
|
62
|
+
const path = firstPath(obj.files);
|
|
63
|
+
return path ? `${verb} ${baseName(path)}${size}` : `${verb} 1 file${size}`;
|
|
64
|
+
}
|
|
65
|
+
return `${verb} ${count} files${size}`;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** read: { summary: { files_read, total_lines }, files?: [{path, lineCount}] } */
|
|
69
|
+
function summarizeRead(obj: Record<string, unknown>): string | null {
|
|
70
|
+
const summary = (obj.summary && typeof obj.summary === 'object')
|
|
71
|
+
? obj.summary as Record<string, unknown>
|
|
72
|
+
: obj;
|
|
73
|
+
const count = asNumber(summary.files_read);
|
|
74
|
+
const lines = asNumber(summary.total_lines);
|
|
75
|
+
if (count === undefined && lines === undefined) return null;
|
|
76
|
+
const lineText = lines !== undefined ? ` (${lines} line${lines === 1 ? '' : 's'})` : '';
|
|
77
|
+
if (count === 1) {
|
|
78
|
+
const path = firstPath(obj.files);
|
|
79
|
+
return path ? `read ${baseName(path)}${lineText}` : `read 1 file${lineText}`;
|
|
80
|
+
}
|
|
81
|
+
return `read ${count ?? '?'} files${lineText}`;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** edit: { applied, failed, dry_run } or richer with a path. */
|
|
85
|
+
function summarizeEdit(obj: Record<string, unknown>): string | null {
|
|
86
|
+
const applied = asNumber(obj.applied);
|
|
87
|
+
if (applied === undefined) return null;
|
|
88
|
+
const failed = asNumber(obj.failed) ?? 0;
|
|
89
|
+
const verb = obj.dry_run === true ? 'would apply' : 'applied';
|
|
90
|
+
const path = typeof obj.path === 'string' ? obj.path : firstPath(obj.files);
|
|
91
|
+
const target = path ? ` to ${baseName(path)}` : '';
|
|
92
|
+
const failText = failed > 0 ? `, ${failed} failed` : '';
|
|
93
|
+
return `${verb} ${applied} edit${applied === 1 ? '' : 's'}${target}${failText}`;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** A single exec command result. */
|
|
97
|
+
function summarizeExecOne(obj: Record<string, unknown>): string | null {
|
|
98
|
+
const exit = obj.exit_code;
|
|
99
|
+
const dur = asNumber(obj.duration_ms);
|
|
100
|
+
const durText = dur !== undefined ? ` · ${formatDuration(dur)}` : '';
|
|
101
|
+
if (obj.timed_out === true) return `timed out${durText}`;
|
|
102
|
+
if (obj.cancelled === true) return `cancelled${durText}`;
|
|
103
|
+
const stdout = typeof obj.stdout === 'string' ? obj.stdout : '';
|
|
104
|
+
const lineCount = stdout.length === 0
|
|
105
|
+
? 0
|
|
106
|
+
: stdout.replace(/\n$/, '').split('\n').length;
|
|
107
|
+
const lineText = lineCount > 0 ? ` · ${lineCount} line${lineCount === 1 ? '' : 's'}` : '';
|
|
108
|
+
const code = typeof exit === 'number' ? String(exit) : '?';
|
|
109
|
+
return `exit ${code}${durText}${lineText}`;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** exec: single { cmd, exit_code, ... } or multi { commands: [...], total }. */
|
|
113
|
+
function summarizeExec(obj: Record<string, unknown>): string | null {
|
|
114
|
+
if (Array.isArray(obj.commands)) {
|
|
115
|
+
const total = asNumber(obj.total) ?? obj.commands.length;
|
|
116
|
+
const failed = obj.commands.filter(
|
|
117
|
+
(c) => c && typeof c === 'object' && (c as Record<string, unknown>).success === false,
|
|
118
|
+
).length;
|
|
119
|
+
return failed > 0
|
|
120
|
+
? `${total} commands · ${failed} failed`
|
|
121
|
+
: `${total} commands · all ok`;
|
|
122
|
+
}
|
|
123
|
+
if ('exit_code' in obj || 'timed_out' in obj || 'cancelled' in obj) {
|
|
124
|
+
return summarizeExecOne(obj);
|
|
125
|
+
}
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function summarizeToolResult(toolName: string | undefined, content: string): string | null {
|
|
130
|
+
if (!toolName) return null;
|
|
131
|
+
const family = toolName.includes('__') ? toolName.split('__').pop()! : toolName;
|
|
132
|
+
let data: unknown;
|
|
133
|
+
try {
|
|
134
|
+
data = JSON.parse(content);
|
|
135
|
+
} catch {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
if (data === null || typeof data !== 'object' || Array.isArray(data)) return null;
|
|
139
|
+
const obj = data as Record<string, unknown>;
|
|
140
|
+
|
|
141
|
+
switch (family) {
|
|
142
|
+
case 'write': return summarizeWrite(obj);
|
|
143
|
+
case 'read': return summarizeRead(obj);
|
|
144
|
+
case 'edit': return summarizeEdit(obj);
|
|
145
|
+
case 'exec': return summarizeExec(obj);
|
|
146
|
+
default: return null;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-turn knowledge injection record rendering (Wave-5 W5.2, wo803).
|
|
3
|
+
*
|
|
4
|
+
* The SDK's passive per-turn retrieval engine (wo801, W5.1 —
|
|
5
|
+
* packages/sdk/src/platform/agents/turn-knowledge-injection.ts) stores one
|
|
6
|
+
* `TurnInjectionRecord` per turn on `AgentRecord.turnInjections` (a bounded
|
|
7
|
+
* ring, default 20 entries) and appends the same record to the agent's
|
|
8
|
+
* session transcript as `{type:'knowledge_injection', turn, ...record}`.
|
|
9
|
+
*
|
|
10
|
+
* `TurnInjectionRecord` itself is not re-exported from the SDK's
|
|
11
|
+
* `platform/agents` barrel (only `AgentRecord.turnInjections` carries its
|
|
12
|
+
* structural shape through the already-public `platform/tools` barrel), so
|
|
13
|
+
* the entry type here is DERIVED from `AgentRecord` rather than imported by
|
|
14
|
+
* name — this needs no SDK export change.
|
|
15
|
+
*
|
|
16
|
+
* Reality check (updated wo805): as of Wave-5 wo805 the TUI's main interactive
|
|
17
|
+
* session DOES route through this engine. The SDK `Orchestrator` runs per-turn
|
|
18
|
+
* passive injection on the evolving primary conversation and records each turn
|
|
19
|
+
* on its own bounded ring, exposed via `Orchestrator.getTurnInjections()` — the
|
|
20
|
+
* main-session counterpart to `AgentRecord.turnInjections` (there is no
|
|
21
|
+
* AgentRecord for the primary conversation). `buildMainSessionTurnInjectionsText`
|
|
22
|
+
* renders that ring for `/recall injections` with no agent id. Spawned agents
|
|
23
|
+
* (Task-tool runs, automation-triggered runs, session continuations) still
|
|
24
|
+
* record onto their own `AgentRecord.turnInjections`, rendered per-agent by
|
|
25
|
+
* `buildTurnInjectionsText` when an explicit agent id is given.
|
|
26
|
+
*/
|
|
27
|
+
import type { AgentRecord } from '@pellux/goodvibes-sdk/platform/tools';
|
|
28
|
+
|
|
29
|
+
/** One TurnInjectionRecord entry, derived from AgentRecord (see module doc). */
|
|
30
|
+
export type TurnInjectionEntry = NonNullable<AgentRecord['turnInjections']>[number];
|
|
31
|
+
|
|
32
|
+
function fmtN(n: number): string {
|
|
33
|
+
return n.toLocaleString();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Annotate each injected id with its source (Stage B). Code-index hits get a
|
|
38
|
+
* ` [code]` tag; memory records are left bare so a memory-only line renders
|
|
39
|
+
* byte-identically to the pre-Stage-B output. `injectedSources` is parallel to
|
|
40
|
+
* `injectedIds`; a missing/short entry defaults to memory (no tag).
|
|
41
|
+
*/
|
|
42
|
+
function labelInjectedIds(entry: TurnInjectionEntry): string {
|
|
43
|
+
const sources = entry.injectedSources ?? [];
|
|
44
|
+
return entry.injectedIds
|
|
45
|
+
.map((id, i) => (sources[i] === 'code-index' ? `${id} [code]` : id))
|
|
46
|
+
.join(', ');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Honest one-clause note when a wired code index was queried but injected nothing this turn. */
|
|
50
|
+
function codeSkipNote(entry: TurnInjectionEntry): string {
|
|
51
|
+
return entry.codeInjectionSkipped ? `, code skipped: ${entry.codeInjectionSkipped}` : '';
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Render a single TurnInjectionRecord as one readable line. */
|
|
55
|
+
export function formatTurnInjectionEntry(entry: TurnInjectionEntry): string {
|
|
56
|
+
const backendTag = entry.embeddingBackend === 'fallback-lexical' ? ' [lexical fallback]' : '';
|
|
57
|
+
if (entry.injectedIds.length === 0) {
|
|
58
|
+
// Honest empty state: the engine ran this turn but injected nothing —
|
|
59
|
+
// distinct from "the engine never ran" (see buildTurnInjectionsText).
|
|
60
|
+
const reasonText = entry.reason === 'no records cleared relevance floor'
|
|
61
|
+
? 'nothing injected this turn — nothing cleared the relevance floor'
|
|
62
|
+
: `nothing injected this turn — ${entry.reason ?? 'unknown reason'}`;
|
|
63
|
+
const codeConsidered = entry.codeCandidatesConsidered ? `, code considered ${entry.codeCandidatesConsidered}` : '';
|
|
64
|
+
return ` turn ${entry.turn}: ${reasonText}${backendTag} (considered ${entry.candidatesConsidered}${codeConsidered}, floor ${entry.relevanceFloor})${codeSkipNote(entry)}`;
|
|
65
|
+
}
|
|
66
|
+
const droppedStr = entry.droppedForBudget.length > 0
|
|
67
|
+
? `, dropped for budget: ${entry.droppedForBudget.join(', ')}`
|
|
68
|
+
: '';
|
|
69
|
+
// The retrieval query is part of the record's honesty contract — without it
|
|
70
|
+
// an injected line can't be traced back to WHY those ids were retrieved
|
|
71
|
+
// (Wave-5 replay flagged the omission). Truncated to keep the line scannable.
|
|
72
|
+
const queryStr = entry.query ? ` for ${JSON.stringify(truncateQuery(entry.query))}` : '';
|
|
73
|
+
return (
|
|
74
|
+
` turn ${entry.turn}: injected ${labelInjectedIds(entry)}${queryStr} ` +
|
|
75
|
+
`(~${fmtN(entry.tokenCost)}/${fmtN(entry.budgetTokens)} tok, floor ${entry.relevanceFloor})${droppedStr}${backendTag}${codeSkipNote(entry)}`
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function truncateQuery(query: string): string {
|
|
80
|
+
const flat = query.replace(/\s+/g, ' ').trim();
|
|
81
|
+
return flat.length > 48 ? `${flat.slice(0, 47)}…` : flat;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Render the full per-turn injection history for one agent, most-recent-turn
|
|
86
|
+
* first, with an honest empty state when nothing has been recorded yet.
|
|
87
|
+
*
|
|
88
|
+
* An empty `entries` array is deliberately ambiguous about WHY it's empty
|
|
89
|
+
* (flag disabled, no turn with new input has run yet, or every turn's token
|
|
90
|
+
* budget had no headroom) — the SDK does not distinguish these cases in the
|
|
91
|
+
* ring itself, so this renders all three possibilities rather than guessing.
|
|
92
|
+
*/
|
|
93
|
+
export function buildTurnInjectionsText(agentId: string, entries: readonly TurnInjectionEntry[]): string {
|
|
94
|
+
if (entries.length === 0) {
|
|
95
|
+
return (
|
|
96
|
+
`[recall] No per-turn injection records for agent ${agentId} yet. This means one of: ` +
|
|
97
|
+
'passive knowledge injection is disabled, no turn with new input has run yet, or the ' +
|
|
98
|
+
'token budget had no headroom on every turn so far — there is no record either way.'
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
const lines = [`[recall] Per-turn knowledge injections for agent ${agentId} (${entries.length}, most recent first):`];
|
|
102
|
+
for (const entry of [...entries].reverse()) {
|
|
103
|
+
lines.push(formatTurnInjectionEntry(entry));
|
|
104
|
+
}
|
|
105
|
+
return lines.join('\n');
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Render the MAIN interactive session's per-turn injection ring
|
|
110
|
+
* (`Orchestrator.getTurnInjections()`, wo805), most-recent-turn first, with the
|
|
111
|
+
* same honest empty state as the per-agent path. The main-session counterpart
|
|
112
|
+
* to {@link buildTurnInjectionsText}: no agent id, main-session-appropriate
|
|
113
|
+
* wording, but the identical per-entry rendering (`formatTurnInjectionEntry`).
|
|
114
|
+
*
|
|
115
|
+
* As with the agent path, an empty `entries` array is deliberately ambiguous
|
|
116
|
+
* about WHY it's empty (flag disabled, no turn with new input has run yet, or
|
|
117
|
+
* every turn's token budget had no headroom) — the ring does not distinguish
|
|
118
|
+
* these, so this renders all three possibilities rather than guessing.
|
|
119
|
+
*/
|
|
120
|
+
export function buildMainSessionTurnInjectionsText(entries: readonly TurnInjectionEntry[]): string {
|
|
121
|
+
if (entries.length === 0) {
|
|
122
|
+
return (
|
|
123
|
+
'[recall] No per-turn injection records for the main session yet. This means one of: ' +
|
|
124
|
+
'passive knowledge injection is disabled, no turn with new input has run yet, or the ' +
|
|
125
|
+
'token budget had no headroom on every turn so far — there is no record either way.'
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
const lines = [`[recall] Per-turn knowledge injections for the main session (${entries.length}, most recent first):`];
|
|
129
|
+
for (const entry of [...entries].reverse()) {
|
|
130
|
+
lines.push(formatTurnInjectionEntry(entry));
|
|
131
|
+
}
|
|
132
|
+
return lines.join('\n');
|
|
133
|
+
}
|