@deepseek-harness-tui/dsh-tui 0.9.3 → 0.10.0-beta.2
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 +24 -6
- package/cordis.patch.yml +117 -39
- package/cordis.yml +13 -10
- package/lib/types/api.d.ts +7 -0
- package/lib/types/api.d.ts.map +1 -0
- package/lib/types/api.js +1 -0
- package/lib/types/cc/hyperlink.d.ts.map +1 -1
- package/lib/types/cc/hyperlink.js +42 -4
- package/lib/types/cc/markdown.d.ts.map +1 -1
- package/lib/types/cc/markdown.js +15 -5
- package/lib/types/commands.d.ts +6 -0
- package/lib/types/commands.d.ts.map +1 -1
- package/lib/types/commands.js +12 -8
- package/lib/types/components/MessageList.d.ts +8 -4
- package/lib/types/components/MessageList.d.ts.map +1 -1
- package/lib/types/components/MessageList.js +94 -32
- package/lib/types/components/PermissionsPicker.d.ts +6 -7
- package/lib/types/components/PermissionsPicker.d.ts.map +1 -1
- package/lib/types/components/PermissionsPicker.js +20 -14
- package/lib/types/components/PromptEditor.d.ts +46 -0
- package/lib/types/components/PromptEditor.d.ts.map +1 -0
- package/lib/types/components/PromptEditor.js +60 -0
- package/lib/types/components/PromptInput.d.ts +12 -0
- package/lib/types/components/PromptInput.d.ts.map +1 -1
- package/lib/types/components/PromptInput.js +1240 -205
- package/lib/types/components/RewindPicker.js +1 -1
- package/lib/types/components/SplitDiffView.d.ts +11 -1
- package/lib/types/components/SplitDiffView.d.ts.map +1 -1
- package/lib/types/components/SplitDiffView.js +9 -2
- package/lib/types/components/StreamingMarkdown.d.ts.map +1 -1
- package/lib/types/components/StreamingMarkdown.js +140 -3
- package/lib/types/components/SubagentDashboard.d.ts.map +1 -1
- package/lib/types/components/SubagentDashboard.js +2 -2
- package/lib/types/components/SubagentDetailScene.js +1 -1
- package/lib/types/components/ThemePicker.d.ts +13 -15
- package/lib/types/components/ThemePicker.d.ts.map +1 -1
- package/lib/types/components/ThemePicker.js +24 -26
- package/lib/types/components/Tooltip.d.ts +67 -0
- package/lib/types/components/Tooltip.d.ts.map +1 -0
- package/lib/types/components/Tooltip.js +220 -0
- package/lib/types/components/approvals/ApprovalPanel.d.ts.map +1 -1
- package/lib/types/components/approvals/ApprovalPanel.js +1 -1
- package/lib/types/components/design-system/Divider.d.ts +14 -1
- package/lib/types/components/design-system/Divider.d.ts.map +1 -1
- package/lib/types/components/design-system/Divider.js +86 -8
- package/lib/types/components/design-system/ThemeProvider.d.ts +5 -2
- package/lib/types/components/design-system/ThemeProvider.d.ts.map +1 -1
- package/lib/types/components/design-system/ThemeProvider.js +52 -16
- package/lib/types/components/design-system/ThemedBox.d.ts +23 -2
- package/lib/types/components/design-system/ThemedBox.d.ts.map +1 -1
- package/lib/types/components/messages/AssistantThinkingMessage.d.ts +11 -6
- package/lib/types/components/messages/AssistantThinkingMessage.d.ts.map +1 -1
- package/lib/types/components/messages/AssistantThinkingMessage.js +10 -6
- package/lib/types/components/messages/AssistantToolUseMessage.d.ts +13 -1
- package/lib/types/components/messages/AssistantToolUseMessage.d.ts.map +1 -1
- package/lib/types/components/messages/AssistantToolUseMessage.js +37 -6
- package/lib/types/components/messages/UserPromptMessage.d.ts.map +1 -1
- package/lib/types/components/messages/UserPromptMessage.js +7 -1
- package/lib/types/components/questions/AskUserQuestionPanel.js +1 -1
- package/lib/types/components/questions/PlanReviewPanel.d.ts.map +1 -1
- package/lib/types/components/questions/PlanReviewPanel.js +29 -3
- package/lib/types/components/sessions/SessionListRow.d.ts +8 -1
- package/lib/types/components/sessions/SessionListRow.d.ts.map +1 -1
- package/lib/types/components/sessions/SessionListRow.js +9 -2
- package/lib/types/components/sessions/SessionPreview.d.ts.map +1 -1
- package/lib/types/components/sessions/SessionPreview.js +5 -3
- package/lib/types/components/sessions/WorkspaceListRow.d.ts +17 -0
- package/lib/types/components/sessions/WorkspaceListRow.d.ts.map +1 -0
- package/lib/types/components/sessions/WorkspaceListRow.js +27 -0
- package/lib/types/components/smoothReveal.d.ts +97 -0
- package/lib/types/components/smoothReveal.d.ts.map +1 -0
- package/lib/types/components/smoothReveal.js +279 -0
- package/lib/types/customTheme.d.ts +4 -0
- package/lib/types/customTheme.d.ts.map +1 -1
- package/lib/types/customTheme.js +9 -5
- package/lib/types/dsh-adapter/approvals.d.ts +114 -2
- package/lib/types/dsh-adapter/approvals.d.ts.map +1 -1
- package/lib/types/dsh-adapter/approvals.js +241 -3
- package/lib/types/dsh-adapter/channel.d.ts +59 -2
- package/lib/types/dsh-adapter/channel.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel.js +499 -80
- package/lib/types/dsh-adapter/childStderr.d.ts.map +1 -1
- package/lib/types/dsh-adapter/childStderr.js +9 -0
- package/lib/types/dsh-adapter/contract.d.ts +17 -13
- package/lib/types/dsh-adapter/contract.d.ts.map +1 -1
- package/lib/types/dsh-adapter/contract.js +36 -27
- package/lib/types/dsh-adapter/credentialRefGuard.d.ts +40 -0
- package/lib/types/dsh-adapter/credentialRefGuard.d.ts.map +1 -0
- package/lib/types/dsh-adapter/credentialRefGuard.js +43 -0
- package/lib/types/dsh-adapter/effect-ledger.d.ts +1 -1
- package/lib/types/dsh-adapter/effect-ledger.d.ts.map +1 -1
- package/lib/types/dsh-adapter/effect-ledger.js +1 -0
- package/lib/types/dsh-adapter/extensions.d.ts +4 -1
- package/lib/types/dsh-adapter/extensions.d.ts.map +1 -1
- package/lib/types/dsh-adapter/extensions.js +8 -1
- package/lib/types/dsh-adapter/index.d.ts +11 -1
- package/lib/types/dsh-adapter/index.d.ts.map +1 -1
- package/lib/types/dsh-adapter/index.js +2 -0
- package/lib/types/dsh-adapter/plugin-test-utils.d.ts +62 -0
- package/lib/types/dsh-adapter/plugin-test-utils.d.ts.map +1 -0
- package/lib/types/dsh-adapter/plugin-test-utils.js +61 -0
- package/lib/types/dsh-adapter/plugin.d.ts +20 -0
- package/lib/types/dsh-adapter/plugin.d.ts.map +1 -1
- package/lib/types/dsh-adapter/plugin.js +174 -39
- package/lib/types/dsh-adapter/plugins-info.d.ts.map +1 -1
- package/lib/types/dsh-adapter/plugins-info.js +12 -0
- package/lib/types/dsh-adapter/preset-resolution.d.ts +49 -0
- package/lib/types/dsh-adapter/preset-resolution.d.ts.map +1 -0
- package/lib/types/dsh-adapter/preset-resolution.js +42 -0
- package/lib/types/dsh-adapter/presets.d.ts +0 -26
- package/lib/types/dsh-adapter/presets.d.ts.map +1 -1
- package/lib/types/dsh-adapter/presets.js +4 -14
- package/lib/types/dsh-adapter/promptDebug.d.ts.map +1 -1
- package/lib/types/dsh-adapter/promptDebug.js +4 -1
- package/lib/types/dsh-adapter/providerGuard.d.ts +75 -0
- package/lib/types/dsh-adapter/providerGuard.d.ts.map +1 -0
- package/lib/types/dsh-adapter/providerGuard.js +104 -0
- package/lib/types/dsh-adapter/questions-answerer.d.ts +49 -0
- package/lib/types/dsh-adapter/questions-answerer.d.ts.map +1 -0
- package/lib/types/dsh-adapter/questions-answerer.js +61 -0
- package/lib/types/dsh-adapter/recap.d.ts.map +1 -1
- package/lib/types/dsh-adapter/recap.js +12 -6
- package/lib/types/dsh-adapter/sanitize.d.ts.map +1 -1
- package/lib/types/dsh-adapter/sanitize.js +6 -1
- package/lib/types/dsh-adapter/sessions/digest.d.ts +21 -1
- package/lib/types/dsh-adapter/sessions/digest.d.ts.map +1 -1
- package/lib/types/dsh-adapter/sessions/digest.js +229 -3
- package/lib/types/dsh-adapter/sessions/frames.d.ts +3 -0
- package/lib/types/dsh-adapter/sessions/frames.d.ts.map +1 -1
- package/lib/types/dsh-adapter/sessions/frames.js +34 -19
- package/lib/types/dsh-adapter/sessions/list.d.ts.map +1 -1
- package/lib/types/dsh-adapter/sessions/list.js +148 -31
- package/lib/types/dsh-adapter/sessions/store.d.ts +8 -0
- package/lib/types/dsh-adapter/sessions/store.d.ts.map +1 -1
- package/lib/types/dsh-adapter/sessions/store.js +24 -4
- package/lib/types/dsh-adapter/sessions/types.d.ts +3 -0
- package/lib/types/dsh-adapter/sessions/types.d.ts.map +1 -1
- package/lib/types/dsh-adapter/settings-sections.d.ts.map +1 -1
- package/lib/types/dsh-adapter/settings-sections.js +13 -1
- package/lib/types/dsh-adapter/settingsEditor.d.ts +6 -0
- package/lib/types/dsh-adapter/settingsEditor.d.ts.map +1 -1
- package/lib/types/dsh-adapter/settingsEditor.js +15 -3
- package/lib/types/dsh-adapter/subagents.d.ts +4 -0
- package/lib/types/dsh-adapter/subagents.d.ts.map +1 -1
- package/lib/types/dsh-adapter/subagents.js +8 -0
- package/lib/types/dsh-adapter/themes.d.ts +51 -0
- package/lib/types/dsh-adapter/themes.d.ts.map +1 -0
- package/lib/types/dsh-adapter/themes.js +272 -0
- package/lib/types/dsh-adapter/toast.d.ts +67 -0
- package/lib/types/dsh-adapter/toast.d.ts.map +1 -0
- package/lib/types/dsh-adapter/toast.js +141 -0
- package/lib/types/dsh-adapter/types.d.ts +1 -1
- package/lib/types/extensions.d.ts +4 -0
- package/lib/types/extensions.d.ts.map +1 -1
- package/lib/types/extensions.js +5 -1
- package/lib/types/history.d.ts.map +1 -1
- package/lib/types/history.js +7 -2
- package/lib/types/hooks/useRuntimeThemeSnapshot.d.ts +10 -0
- package/lib/types/hooks/useRuntimeThemeSnapshot.d.ts.map +1 -0
- package/lib/types/hooks/useRuntimeThemeSnapshot.js +32 -0
- package/lib/types/i18n.d.ts +199 -41
- package/lib/types/i18n.d.ts.map +1 -1
- package/lib/types/i18n.js +82 -20
- package/lib/types/ink/components/App.d.ts +20 -0
- package/lib/types/ink/components/App.d.ts.map +1 -1
- package/lib/types/ink/components/App.js +142 -13
- package/lib/types/ink/components/Box.d.ts +39 -2
- package/lib/types/ink/components/Box.d.ts.map +1 -1
- package/lib/types/ink/components/Box.js +61 -37
- package/lib/types/ink/events/context-menu-event.d.ts +19 -0
- package/lib/types/ink/events/context-menu-event.d.ts.map +1 -0
- package/lib/types/ink/events/context-menu-event.js +20 -0
- package/lib/types/ink/events/drag-event.d.ts +44 -0
- package/lib/types/ink/events/drag-event.d.ts.map +1 -0
- package/lib/types/ink/events/drag-event.js +39 -0
- package/lib/types/ink/events/event-handlers.d.ts +8 -0
- package/lib/types/ink/events/event-handlers.d.ts.map +1 -1
- package/lib/types/ink/events/event-handlers.js +8 -0
- package/lib/types/ink/hit-test.d.ts +69 -0
- package/lib/types/ink/hit-test.d.ts.map +1 -1
- package/lib/types/ink/hit-test.js +260 -6
- package/lib/types/ink/ink.d.ts +25 -0
- package/lib/types/ink/ink.d.ts.map +1 -1
- package/lib/types/ink/ink.js +74 -10
- package/lib/types/ink/parse-keypress.d.ts +20 -0
- package/lib/types/ink/parse-keypress.d.ts.map +1 -1
- package/lib/types/ink/parse-keypress.js +190 -22
- package/lib/types/ink/renderer.d.ts.map +1 -1
- package/lib/types/ink/renderer.js +15 -1
- package/lib/types/ink/screen.d.ts +7 -0
- package/lib/types/ink/screen.d.ts.map +1 -1
- package/lib/types/ink/screen.js +8 -1
- package/lib/types/ink/termio/osc.d.ts +2 -1
- package/lib/types/ink/termio/osc.d.ts.map +1 -1
- package/lib/types/ink/termio/osc.js +26 -5
- package/lib/types/plugin-spec/registry.d.ts +1 -1
- package/lib/types/plugin-spec/registry.js +1 -1
- package/lib/types/presetPrefs.d.ts +6 -5
- package/lib/types/presetPrefs.d.ts.map +1 -1
- package/lib/types/presetPrefs.js +10 -5
- package/lib/types/screens/Chat.d.ts +4 -1
- package/lib/types/screens/Chat.d.ts.map +1 -1
- package/lib/types/screens/Chat.js +169 -68
- package/lib/types/screens/SessionBrowser.d.ts +9 -4
- package/lib/types/screens/SessionBrowser.d.ts.map +1 -1
- package/lib/types/screens/SessionBrowser.js +483 -75
- package/lib/types/screens/SessionTree.d.ts.map +1 -1
- package/lib/types/screens/SessionTree.js +5 -1
- package/lib/types/screens/Settings.d.ts.map +1 -1
- package/lib/types/screens/Settings.js +5 -1
- package/lib/types/screens/StatusLine.d.ts.map +1 -1
- package/lib/types/screens/StatusLine.js +5 -1
- package/lib/types/screens/chatOverlay.d.ts +3 -2
- package/lib/types/screens/chatOverlay.d.ts.map +1 -1
- package/lib/types/screens/chatOverlay.js +2 -0
- package/lib/types/sessionPins.d.ts +33 -0
- package/lib/types/sessionPins.d.ts.map +1 -0
- package/lib/types/sessionPins.js +181 -0
- package/lib/types/sessions/format.d.ts +2 -0
- package/lib/types/sessions/format.d.ts.map +1 -1
- package/lib/types/sessions/format.js +9 -0
- package/lib/types/sessions/view.d.ts +42 -6
- package/lib/types/sessions/view.d.ts.map +1 -1
- package/lib/types/sessions/view.js +133 -35
- package/lib/types/test-utils.d.ts +2 -0
- package/lib/types/test-utils.d.ts.map +1 -0
- package/lib/types/test-utils.js +8 -0
- package/lib/types/theme.d.ts +22 -5
- package/lib/types/theme.d.ts.map +1 -1
- package/lib/types/theme.js +57 -7
- package/lib/types/themeCatalog.d.ts +36 -0
- package/lib/types/themeCatalog.d.ts.map +1 -0
- package/lib/types/themeCatalog.js +157 -0
- package/lib/types/tips.d.ts.map +1 -1
- package/lib/types/tips.js +114 -18
- package/lib/types/update.d.ts +159 -3
- package/lib/types/update.d.ts.map +1 -1
- package/lib/types/update.js +497 -16
- package/lib/types/utils/debug.d.ts.map +1 -1
- package/lib/types/utils/debug.js +2 -1
- package/lib/types/utils/keymap.d.ts +1 -1
- package/lib/types/utils/keymap.d.ts.map +1 -1
- package/lib/types/utils/keymap.js +19 -2
- package/lib/types/utils/urlGuard.d.ts +28 -0
- package/lib/types/utils/urlGuard.d.ts.map +1 -0
- package/lib/types/utils/urlGuard.js +21 -0
- package/node_modules/@deepseek-harness-tui/dsh-auth/README.md +3 -0
- package/node_modules/@deepseek-harness-tui/dsh-auth/lib/credentials.d.ts +7 -11
- package/node_modules/@deepseek-harness-tui/dsh-auth/lib/index.d.ts +16 -1
- package/node_modules/@deepseek-harness-tui/dsh-auth/lib/index.js +23 -2
- package/node_modules/@deepseek-harness-tui/dsh-auth/lib/interaction.d.ts +5 -5
- package/node_modules/@deepseek-harness-tui/dsh-auth/lib/pi-ai.d.ts +23 -0
- package/node_modules/@deepseek-harness-tui/dsh-auth/lib/pi-ai.js +27 -0
- package/node_modules/@deepseek-harness-tui/dsh-auth/lib/profiles.d.ts +17 -3
- package/node_modules/@deepseek-harness-tui/dsh-auth/lib/profiles.js +36 -4
- package/node_modules/@deepseek-harness-tui/dsh-auth/package.json +11 -12
- package/package.json +114 -74
- package/lib/types/dsh-adapter/packaged-skills.d.ts +0 -19
- package/lib/types/dsh-adapter/packaged-skills.d.ts.map +0 -1
- package/lib/types/dsh-adapter/packaged-skills.js +0 -78
- package/skills/audit/SKILL.md +0 -24
- package/skills/bug/SKILL.md +0 -27
- package/skills/pr-comments/SKILL.md +0 -20
- package/skills/practice/SKILL.md +0 -22
- package/skills/release-notes/SKILL.md +0 -25
- package/skills/review/SKILL.md +0 -25
- package/skills/vuln-check/SKILL.md +0 -25
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Smooth streaming reveal — decouples "text the provider has delivered" from
|
|
3
|
+
* "text the screen is showing" (the oh-my-pi `display.smoothStreaming` idea,
|
|
4
|
+
* see their `StreamingRevealController`). A shared ~30fps scheduler advances
|
|
5
|
+
* each reveal cursor by an adaptive catch-up step, so burst-delivered text (a
|
|
6
|
+
* slow provider dumping a large batch, or a non-streaming response arriving
|
|
7
|
+
* whole) paints as a brief even flow instead of one jarring jump, while
|
|
8
|
+
* genuinely streaming text is re-paced to a steady cadence regardless of
|
|
9
|
+
* chunk timing.
|
|
10
|
+
*
|
|
11
|
+
* Step math (per frame): `max(MIN_STEP, ceil(backlog / CATCHUP_FRAMES))` —
|
|
12
|
+
* an EXPONENTIAL decay over the backlog (each frame removes ~1/8 of what is
|
|
13
|
+
* still unrevealed, the oh-my-pi semantics exactly): a large burst lands its
|
|
14
|
+
* first three quarters within a few frames, the remainder tapers, and the
|
|
15
|
+
* MIN_STEP floor turns the tail into a steady linear crawl so the reveal
|
|
16
|
+
* always finishes. A ~2000-character one-shot delivery paints fully in
|
|
17
|
+
* roughly 1.5s; genuinely streaming text (small backlogs) paces at a steady
|
|
18
|
+
* per-frame rate. The reveal never runs ahead of what has arrived.
|
|
19
|
+
*
|
|
20
|
+
* ARCHITECTURE — module-level cursors, not component state. dsh-tui's
|
|
21
|
+
* transcript virtualization feeds on MemoRow prop changes (flattened
|
|
22
|
+
* primitives) and re-measures mounted rows after every MessageList commit;
|
|
23
|
+
* a reveal living in child-component state would change row heights without
|
|
24
|
+
* touching those props, and the memo/signature/height-measure chain would
|
|
25
|
+
* never notice (stale cached heights → blank bands). Instead the cursors
|
|
26
|
+
* live here keyed by row identity, the READS happen during render (they feed
|
|
27
|
+
* the same flattened `text` prop the streaming path already uses), and the
|
|
28
|
+
* WRITES happen on the scheduler tick, which bumps a version that
|
|
29
|
+
* MessageList subscribes to via useSyncExternalStore. A reveal therefore
|
|
30
|
+
* rides the exact same update pipeline as an arriving chunk: prop change →
|
|
31
|
+
* memo miss → re-render → post-commit measurement. Render-phase reads are
|
|
32
|
+
* idempotent and safe to re-run (a discarded concurrent render at worst
|
|
33
|
+
* creates a cursor the tick soon retires).
|
|
34
|
+
*
|
|
35
|
+
* Unit model: reveal positions are UTF-16 code units (Chinese text is one
|
|
36
|
+
* unit per character). Surrogate pairs are never split (see {@link
|
|
37
|
+
* safeSliceEnd}); grapheme clusters longer than a surrogate pair (emoji ZWJ
|
|
38
|
+
* chains) may briefly show a partial cluster mid-reveal — transient frames
|
|
39
|
+
* repaint within 33ms, and settled rendering is always the full text.
|
|
40
|
+
*/
|
|
41
|
+
/** Reveal tick cadence — ~30fps, matching oh-my-pi's controller. */
|
|
42
|
+
export const REVEAL_FRAME_MS = 1000 / 30;
|
|
43
|
+
/** Minimum units (code units or lines) advanced per tick while work remains. */
|
|
44
|
+
export const REVEAL_MIN_STEP = 3;
|
|
45
|
+
/** Catch-up bound: the reveal settles any backlog within this many frames. */
|
|
46
|
+
export const REVEAL_CATCHUP_FRAMES = 8;
|
|
47
|
+
/** Adaptive per-frame step for a backlog (exported for tests). */
|
|
48
|
+
export function revealStep(backlog) {
|
|
49
|
+
return Math.max(REVEAL_MIN_STEP, Math.ceil(Math.max(0, backlog) / REVEAL_CATCHUP_FRAMES));
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Slice boundary guard: never split a surrogate pair. When the boundary lands
|
|
53
|
+
* between a high and a low surrogate, take the pair if the low half has
|
|
54
|
+
* arrived, otherwise step back one unit — showing one fewer character for a
|
|
55
|
+
* frame beats feeding a lone surrogate into markdown rendering.
|
|
56
|
+
*/
|
|
57
|
+
function safeSliceEnd(text, end) {
|
|
58
|
+
if (end <= 0)
|
|
59
|
+
return 0;
|
|
60
|
+
if (end >= text.length)
|
|
61
|
+
return text.length;
|
|
62
|
+
const code = text.charCodeAt(end - 1);
|
|
63
|
+
if (code >= 0xd800 && code <= 0xdbff) {
|
|
64
|
+
return end + 1 < text.length ? end + 1 : end - 1;
|
|
65
|
+
}
|
|
66
|
+
return end;
|
|
67
|
+
}
|
|
68
|
+
const textCursors = new Map();
|
|
69
|
+
const countCursors = new Map();
|
|
70
|
+
/**
|
|
71
|
+
* Keys whose reveal has COMPLETED (caught up, snapped, or replaced). A
|
|
72
|
+
* completed key must not re-create a cursor on a later active read — the
|
|
73
|
+
* content is already fully on screen, and re-revealing would blank it and
|
|
74
|
+
* typewrite again. Bounded FIFO: well past the cap these are all ancient
|
|
75
|
+
* history (the row ids are gone); dropping the oldest half only risks a
|
|
76
|
+
* long-unmounted row re-revealing, which is harmless (it paints full via
|
|
77
|
+
* the active=false path anyway once unmounted... and a remounted streaming
|
|
78
|
+
* row WANTS the reveal).
|
|
79
|
+
*/
|
|
80
|
+
const completedReveals = new Set();
|
|
81
|
+
const COMPLETED_REVEALS_MAX = 2048;
|
|
82
|
+
function markRevealCompleted(key) {
|
|
83
|
+
if (completedReveals.size >= COMPLETED_REVEALS_MAX) {
|
|
84
|
+
let drop = COMPLETED_REVEALS_MAX / 2;
|
|
85
|
+
for (const old of completedReveals) {
|
|
86
|
+
if (drop-- <= 0)
|
|
87
|
+
break;
|
|
88
|
+
completedReveals.delete(old);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
completedReveals.add(key);
|
|
92
|
+
}
|
|
93
|
+
// ---------------------------------------------------------------------------
|
|
94
|
+
// Scheduler + version store (useSyncExternalStore-compatible)
|
|
95
|
+
// ---------------------------------------------------------------------------
|
|
96
|
+
let revealTimer = undefined;
|
|
97
|
+
let revealVersionValue = 0;
|
|
98
|
+
const revealListeners = new Set();
|
|
99
|
+
function ensureRevealTimer() {
|
|
100
|
+
if (revealTimer === undefined) {
|
|
101
|
+
revealTimer = setInterval(revealTick, REVEAL_FRAME_MS);
|
|
102
|
+
revealTimer.unref?.();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
function stopRevealTimerIfIdle() {
|
|
106
|
+
if (textCursors.size === 0 && countCursors.size === 0 && revealTimer !== undefined) {
|
|
107
|
+
clearInterval(revealTimer);
|
|
108
|
+
revealTimer = undefined;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
function revealTick() {
|
|
112
|
+
let advanced = false;
|
|
113
|
+
for (const [key, cursor] of textCursors) {
|
|
114
|
+
const total = cursor.text.length;
|
|
115
|
+
if (cursor.revealed >= total) {
|
|
116
|
+
textCursors.delete(key);
|
|
117
|
+
markRevealCompleted(key);
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
cursor.revealed = Math.min(total, cursor.revealed + revealStep(total - cursor.revealed));
|
|
121
|
+
advanced = true;
|
|
122
|
+
}
|
|
123
|
+
for (const [key, cursor] of countCursors) {
|
|
124
|
+
if (cursor.revealed >= cursor.total) {
|
|
125
|
+
countCursors.delete(key);
|
|
126
|
+
markRevealCompleted(key);
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
cursor.revealed = Math.min(cursor.total, cursor.revealed + revealStep(cursor.total - cursor.revealed));
|
|
130
|
+
advanced = true;
|
|
131
|
+
}
|
|
132
|
+
if (advanced) {
|
|
133
|
+
revealVersionValue += 1;
|
|
134
|
+
for (const listener of [...revealListeners])
|
|
135
|
+
listener();
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
stopRevealTimerIfIdle();
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/** Subscribe to reveal-frame wakeups (MessageList + tool cards). */
|
|
142
|
+
export function subscribeReveal(listener) {
|
|
143
|
+
revealListeners.add(listener);
|
|
144
|
+
return () => {
|
|
145
|
+
revealListeners.delete(listener);
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
/** Snapshot for useSyncExternalStore; bumps once per advancing tick. */
|
|
149
|
+
export function getRevealVersion() {
|
|
150
|
+
return revealVersionValue;
|
|
151
|
+
}
|
|
152
|
+
/** Force-complete (and forget) a cursor: expanded card, replaced view, … */
|
|
153
|
+
export function snapReveal(key) {
|
|
154
|
+
if (textCursors.delete(key) || countCursors.delete(key))
|
|
155
|
+
markRevealCompleted(key);
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Revealed length of `text` for `key` — the cursor logic of
|
|
159
|
+
* {@link revealTextOf} without the slice allocation (layout signatures read
|
|
160
|
+
* this every render for every visible row).
|
|
161
|
+
*/
|
|
162
|
+
export function revealLengthOf(key, text, options) {
|
|
163
|
+
if (!options.enabled) {
|
|
164
|
+
// Mid-stream settings toggle off: retire any cursor, show everything.
|
|
165
|
+
if (textCursors.delete(key)) {
|
|
166
|
+
markRevealCompleted(key);
|
|
167
|
+
stopRevealTimerIfIdle();
|
|
168
|
+
}
|
|
169
|
+
return text.length;
|
|
170
|
+
}
|
|
171
|
+
const existing = textCursors.get(key);
|
|
172
|
+
if (existing === undefined) {
|
|
173
|
+
// Completed earlier (caught up / snapped / replaced): paint full — a
|
|
174
|
+
// re-reveal would blank the already-complete content and typewrite it
|
|
175
|
+
// again.
|
|
176
|
+
if (completedReveals.has(key))
|
|
177
|
+
return text.length;
|
|
178
|
+
if (!options.active)
|
|
179
|
+
return text.length;
|
|
180
|
+
textCursors.set(key, { text, revealed: 0 });
|
|
181
|
+
ensureRevealTimer();
|
|
182
|
+
return 0;
|
|
183
|
+
}
|
|
184
|
+
if (text !== existing.text) {
|
|
185
|
+
if (text.startsWith(existing.text)) {
|
|
186
|
+
existing.text = text;
|
|
187
|
+
ensureRevealTimer();
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
// Replacement: snap to the new text in full.
|
|
191
|
+
textCursors.delete(key);
|
|
192
|
+
markRevealCompleted(key);
|
|
193
|
+
stopRevealTimerIfIdle();
|
|
194
|
+
return text.length;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return Math.min(existing.revealed, text.length);
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Revealed prefix of `text` for `key`. Monotonic appends (streaming deltas)
|
|
201
|
+
* keep the cursor; a non-prefix replacement (rewind, folded preview
|
|
202
|
+
* truncation) snaps to the new text — never animate across a discontinuity
|
|
203
|
+
* the reader did not witness as streaming. Feed the result into the
|
|
204
|
+
* streaming renderer (StreamingMarkdown); the monotonic prefix it receives
|
|
205
|
+
* keeps its stable-prefix memoization fully effective.
|
|
206
|
+
*/
|
|
207
|
+
export function revealTextOf(key, text, options) {
|
|
208
|
+
const len = revealLengthOf(key, text, options);
|
|
209
|
+
if (len >= text.length)
|
|
210
|
+
return text;
|
|
211
|
+
return text.slice(0, safeSliceEnd(text, len));
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Revealed unit count for `key` over `total` units (tool-card body lines,
|
|
215
|
+
* split-diff rows). A SMALLER total always snaps (content that shrank is a
|
|
216
|
+
* replacement, not an append); growth keeps the cursor. Callers own the
|
|
217
|
+
* append-vs-replace decision for growing totals (pass a fresh key or call
|
|
218
|
+
* {@link snapReveal} when the content behind the same key is replaced).
|
|
219
|
+
*/
|
|
220
|
+
export function revealLinesOf(key, total, options) {
|
|
221
|
+
if (total <= REVEAL_MIN_STEP) {
|
|
222
|
+
// Nothing worth animating (a one-line "Running…" card): retire and
|
|
223
|
+
// return full — also covers total === 0.
|
|
224
|
+
if (countCursors.delete(key)) {
|
|
225
|
+
markRevealCompleted(key);
|
|
226
|
+
stopRevealTimerIfIdle();
|
|
227
|
+
}
|
|
228
|
+
return total;
|
|
229
|
+
}
|
|
230
|
+
if (!options.enabled) {
|
|
231
|
+
if (countCursors.delete(key)) {
|
|
232
|
+
markRevealCompleted(key);
|
|
233
|
+
stopRevealTimerIfIdle();
|
|
234
|
+
}
|
|
235
|
+
return total;
|
|
236
|
+
}
|
|
237
|
+
const existing = countCursors.get(key);
|
|
238
|
+
if (existing === undefined) {
|
|
239
|
+
if (completedReveals.has(key))
|
|
240
|
+
return total;
|
|
241
|
+
if (!options.active)
|
|
242
|
+
return total;
|
|
243
|
+
countCursors.set(key, { total, revealed: 0 });
|
|
244
|
+
ensureRevealTimer();
|
|
245
|
+
return 0;
|
|
246
|
+
}
|
|
247
|
+
if (total !== existing.total) {
|
|
248
|
+
if (total > existing.total) {
|
|
249
|
+
existing.total = total;
|
|
250
|
+
ensureRevealTimer();
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
countCursors.delete(key);
|
|
254
|
+
markRevealCompleted(key);
|
|
255
|
+
stopRevealTimerIfIdle();
|
|
256
|
+
return total;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
return Math.min(existing.revealed, total);
|
|
260
|
+
}
|
|
261
|
+
// ---------------------------------------------------------------------------
|
|
262
|
+
// Test seams
|
|
263
|
+
// ---------------------------------------------------------------------------
|
|
264
|
+
/** Clear every cursor/listener/timer (verify scripts between cases). */
|
|
265
|
+
export function resetRevealForTest() {
|
|
266
|
+
textCursors.clear();
|
|
267
|
+
countCursors.clear();
|
|
268
|
+
completedReveals.clear();
|
|
269
|
+
revealListeners.clear();
|
|
270
|
+
if (revealTimer !== undefined) {
|
|
271
|
+
clearInterval(revealTimer);
|
|
272
|
+
revealTimer = undefined;
|
|
273
|
+
}
|
|
274
|
+
revealVersionValue = 0;
|
|
275
|
+
}
|
|
276
|
+
/** Whether the shared scheduler currently runs (refcount assertions). */
|
|
277
|
+
export function isRevealTimerRunning() {
|
|
278
|
+
return revealTimer !== undefined;
|
|
279
|
+
}
|
|
@@ -38,6 +38,10 @@ export type CustomThemeSpec = {
|
|
|
38
38
|
/** The underlying file name without .json — the on-disk load key. */
|
|
39
39
|
file: string;
|
|
40
40
|
};
|
|
41
|
+
/** Whether a value names one of the three built-in overlay palettes. */
|
|
42
|
+
export declare function isThemeBase(value: unknown): value is ThemeBase;
|
|
43
|
+
/** Whether a value is an own key of the complete Theme contract. */
|
|
44
|
+
export declare function isThemeKey(value: unknown): value is keyof Theme;
|
|
41
45
|
/**
|
|
42
46
|
* Whether a name is safe to use as a file name (no path separators or dot
|
|
43
47
|
* traversal). Theme names are user input from DSH_TUI_THEME and /theme, so
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"customTheme.d.ts","sourceRoot":"","sources":["../../src/customTheme.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAIH,OAAO,
|
|
1
|
+
{"version":3,"file":"customTheme.d.ts","sourceRoot":"","sources":["../../src/customTheme.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAIH,OAAO,EAA6B,KAAK,KAAK,EAAE,MAAM,YAAY,CAAA;AAGlE,kDAAkD;AAClD,eAAO,MAAM,gBAAgB,yCAA0C,CAAA;AACvE,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAA;AAEzD,kEAAkE;AAClE,eAAO,MAAM,gBAAgB,QAA2B,CAAA;AAExD,6EAA6E;AAC7E,MAAM,MAAM,eAAe,GAAG;IAC5B,uFAAuF;IACvF,IAAI,EAAE,MAAM,CAAA;IACZ,4DAA4D;IAC5D,WAAW,EAAE,MAAM,CAAA;IACnB,gDAAgD;IAChD,IAAI,EAAE,SAAS,CAAA;IACf,oEAAoE;IACpE,MAAM,EAAE,OAAO,CAAC,KAAK,CAAC,CAAA;IACtB,qEAAqE;IACrE,IAAI,EAAE,MAAM,CAAA;CACb,CAAA;AAuCD,wEAAwE;AACxE,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,SAAS,CAE9D;AAED,oEAAoE;AACpE,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,KAAK,CAE/D;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAQrD;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAUjE;AAMD;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,GACf,eAAe,GAAG,SAAS,CAoE7B;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,EACZ,GAAG,GAAE,MAAyB,GAC7B,eAAe,GAAG,SAAS,CAQ7B;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,GAAE,MAAyB,GAAG,eAAe,EAAE,CAclF;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,eAAe,GAAG,KAAK,CAEvD;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,KAAK,GAAG,SAAS,CAWlE;AAcD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAGtD;AAED,4FAA4F;AAC5F,wBAAgB,qBAAqB,IAAI,IAAI,CAI5C"}
|
package/lib/types/customTheme.js
CHANGED
|
@@ -61,8 +61,13 @@ const cache = new Map();
|
|
|
61
61
|
*/
|
|
62
62
|
const nameIndex = new Map();
|
|
63
63
|
let nameIndexed = false;
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
/** Whether a value names one of the three built-in overlay palettes. */
|
|
65
|
+
export function isThemeBase(value) {
|
|
66
|
+
return typeof value === 'string' && THEME_BASE_NAMES.includes(value);
|
|
67
|
+
}
|
|
68
|
+
/** Whether a value is an own key of the complete Theme contract. */
|
|
69
|
+
export function isThemeKey(value) {
|
|
70
|
+
return typeof value === 'string' && Object.prototype.hasOwnProperty.call(getTheme('dark'), value);
|
|
66
71
|
}
|
|
67
72
|
/**
|
|
68
73
|
* Whether a name is safe to use as a file name (no path separators or dot
|
|
@@ -152,12 +157,11 @@ export function parseCustomTheme(text, fileName) {
|
|
|
152
157
|
warn(`theme "${fileName}": "colors" must be an object; file skipped`);
|
|
153
158
|
return undefined;
|
|
154
159
|
}
|
|
155
|
-
// Key/color validation against the
|
|
160
|
+
// Key/color validation against the Theme contract, so unknown keys and bad
|
|
156
161
|
// values are skipped per key (warned) instead of killing the theme.
|
|
157
|
-
const baseTheme = getTheme(base);
|
|
158
162
|
const colors = {};
|
|
159
163
|
for (const [key, value] of Object.entries(colorsRaw)) {
|
|
160
|
-
if (!(key
|
|
164
|
+
if (!isThemeKey(key)) {
|
|
161
165
|
warn(`theme "${fileName}": unknown color key "${key}" skipped`);
|
|
162
166
|
continue;
|
|
163
167
|
}
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
* no allow-always or feedback channel in the protocol.
|
|
15
15
|
*/
|
|
16
16
|
import type { ApprovalOutcome, ApprovalRequest } from '@deepseek-ai/dsh-user-approval';
|
|
17
|
+
import type { SessionEvent } from '@deepseek-ai/dsh-session';
|
|
17
18
|
/** What the TUI renders while an approval is pending. */
|
|
18
19
|
export interface ApprovalSnapshot {
|
|
19
20
|
/** Stable key so the panel remounts (fresh focus state) per request. */
|
|
@@ -23,6 +24,22 @@ export interface ApprovalSnapshot {
|
|
|
23
24
|
readonly reason?: string;
|
|
24
25
|
/** The gated command, recovered from the paired tool/call event. */
|
|
25
26
|
readonly command?: string;
|
|
27
|
+
/**
|
|
28
|
+
* The request is not anchored to a live tool call of this session (no or
|
|
29
|
+
* unknown callId, the paired call already settled, or the callId is
|
|
30
|
+
* already in flight or consumed by another ask — a genuine approval is
|
|
31
|
+
* asked exactly once per call). A duplicate callId marks EVERY ask on it
|
|
32
|
+
* (the pre-existing one included — attacker-first order) so the verdict
|
|
33
|
+
* can never invert onto the genuine ask). The approval waterfall is an in-process event
|
|
34
|
+
* any plugin can dispatch, so the panel renders a loud warning instead of
|
|
35
|
+
* presenting the text as the agent's own pending command. Verdicts that
|
|
36
|
+
* depend on the session log are re-checked while the ask is queued or on
|
|
37
|
+
* the panel, so they may also appear late — once a same-callId sibling
|
|
38
|
+
* ask is allowed and its tool/result lands (P-4). The in-flight dedup
|
|
39
|
+
* verdict, by contrast, is determined at park() and needs no result to
|
|
40
|
+
* land.
|
|
41
|
+
*/
|
|
42
|
+
readonly external?: true;
|
|
26
43
|
}
|
|
27
44
|
/**
|
|
28
45
|
* Approval store: parks asks from the harness's approval seam, surfaces
|
|
@@ -40,6 +57,30 @@ export declare class ApprovalStore {
|
|
|
40
57
|
* nothing changed (a fresh object per call would loop re-renders).
|
|
41
58
|
*/
|
|
42
59
|
private snapshotCache;
|
|
60
|
+
/**
|
|
61
|
+
* Length of the session log at the last liveness evaluation of the active
|
|
62
|
+
* ask. Events only append, so an unchanged length means no verdict can
|
|
63
|
+
* have flipped — re-checks below this guard are skipped.
|
|
64
|
+
*/
|
|
65
|
+
private externalCheckedAtEvents;
|
|
66
|
+
private notifyQueued;
|
|
67
|
+
/**
|
|
68
|
+
* CallIds whose approval ask already settled (decided or cancelled).
|
|
69
|
+
* Together with the in-flight set this closes the park-after-decide gap:
|
|
70
|
+
* the first ask leaves the queue while its tool is still executing (the
|
|
71
|
+
* callId still reads live), so a twin parked in that window would dodge
|
|
72
|
+
* both the in-flight dedup and the liveness check. A consumed callId
|
|
73
|
+
* never legitimately comes back: the permission layer asks once per call.
|
|
74
|
+
* Fails toward the badge — a hypothetical genuine re-ask after a cancel
|
|
75
|
+
* shows the warning, never the reverse. Evicted when the paired
|
|
76
|
+
* tool/result lands (after that the liveness check marks the twin
|
|
77
|
+
* anyway), keeping the set bounded to in-flight tool executions.
|
|
78
|
+
* Keys are agent-scoped (`agentId::callId`): the store outlives agent
|
|
79
|
+
* switches (/resume, /new) and low-entropy callIds (provider fallback
|
|
80
|
+
* `call-<index>`) repeat across sessions — a bare-callId key would badge
|
|
81
|
+
* the next session's genuine ask with the previous session's residue.
|
|
82
|
+
*/
|
|
83
|
+
private readonly consumedCallIds;
|
|
43
84
|
/**
|
|
44
85
|
* Subscribe to store changes (useSyncExternalStore contract).
|
|
45
86
|
* @param listener - Called after every mutation that changes the snapshot.
|
|
@@ -47,13 +88,63 @@ export declare class ApprovalStore {
|
|
|
47
88
|
*/
|
|
48
89
|
subscribe(listener: () => void): () => void;
|
|
49
90
|
/**
|
|
50
|
-
* The approval the TUI should render now, or null when idle.
|
|
91
|
+
* The approval the TUI should render now, or null when idle. Re-checks the
|
|
92
|
+
* active ask's source badge against the current session log (see
|
|
93
|
+
* {@link refreshActiveExternal}) so a badge that flips while the ask is on
|
|
94
|
+
* the panel surfaces without waiting for another store mutation.
|
|
51
95
|
* @returns The cached snapshot; the reference is stable between mutations.
|
|
52
96
|
*/
|
|
53
97
|
getSnapshot(): ApprovalSnapshot | null;
|
|
54
98
|
private emit;
|
|
99
|
+
/** Notify on a microtask so a re-check triggered inside getSnapshot
|
|
100
|
+
* (during a React render) never fires listeners synchronously mid-render. */
|
|
101
|
+
private scheduleNotify;
|
|
55
102
|
/** Rebuild the cached snapshot after any mutation of active. */
|
|
56
103
|
private rebuildSnapshot;
|
|
104
|
+
/**
|
|
105
|
+
* Re-run the source-badge verdict for the active ask (P-4).
|
|
106
|
+
*
|
|
107
|
+
* The external flag computed at park() is only a point-in-time verdict. A
|
|
108
|
+
* forger can dispatch a second request carrying the SAME callId as a
|
|
109
|
+
* genuinely pending one — both look live at park time; once the first is
|
|
110
|
+
* allowed and its tool/result lands, the twin surfaces with a callId that
|
|
111
|
+
* no longer has an unresolved call and must not keep the unmarked panel
|
|
112
|
+
* data. Re-checked when an ask becomes active (promotion), whenever the
|
|
113
|
+
* snapshot is read (the result usually lands AFTER the twin was promoted),
|
|
114
|
+
* and on every session `tool/result` notification (see
|
|
115
|
+
* {@link noteSessionEvent}). The session log only appends, so the verdict
|
|
116
|
+
* can only flip live→external, never back — the badge may appear late but
|
|
117
|
+
* never falsely.
|
|
118
|
+
*/
|
|
119
|
+
private refreshActiveExternal;
|
|
120
|
+
/**
|
|
121
|
+
* Session-event notification inlet (wired by plugin.ts to the
|
|
122
|
+
* `session/event` firehose). React does not know the session log
|
|
123
|
+
* appended: a badge flip that only happens inside getSnapshot() surfaces
|
|
124
|
+
* solely when something ELSE re-renders the panel (a streaming spinner,
|
|
125
|
+
* say) — a silent render loop leaves the unmarked panel on screen. A
|
|
126
|
+
* landed `tool/result` is the only event type that can flip the active
|
|
127
|
+
* verdict live→external, so everything else is dropped here; the internal
|
|
128
|
+
* length memo then skips the recheck whenever the event belongs to a
|
|
129
|
+
* different session than the active ask's. The notification the SDK
|
|
130
|
+
* fires arrives after the event is already in `session.events`, so the
|
|
131
|
+
* recheck sees the settled result.
|
|
132
|
+
* @param event - The appended session event.
|
|
133
|
+
*/
|
|
134
|
+
noteSessionEvent(event: SessionEvent): void;
|
|
135
|
+
/**
|
|
136
|
+
* Whether another ask with this callId is already parked (active or
|
|
137
|
+
* queued). A genuine approval is raised once per gated tool call — the
|
|
138
|
+
* permission layer asks, waits, and never re-asks for the same callId —
|
|
139
|
+
* so a second ask reusing an in-flight callId is a forged twin dispatched
|
|
140
|
+
* through the in-process waterfall. This is a DETERMINISTIC verdict: it
|
|
141
|
+
* needs no tool/result to land, unlike {@link isLiveToolApproval}, whose
|
|
142
|
+
* twin-reveal window (result lands only after the first ask is allowed
|
|
143
|
+
* and the tool runs) is exactly when promotion-time rechecks still see no
|
|
144
|
+
* result. Derived from the live active+queue set on each park(), so
|
|
145
|
+
* settled asks free their callId automatically.
|
|
146
|
+
*/
|
|
147
|
+
private isCallIdInFlight;
|
|
57
148
|
/**
|
|
58
149
|
* Answerer entry point — called by the `approval/request` waterfall
|
|
59
150
|
* listener when the ask concerns the agent this TUI owns.
|
|
@@ -62,7 +153,10 @@ export declare class ApprovalStore {
|
|
|
62
153
|
* when the ask is withdrawn or the plugin tears down.
|
|
63
154
|
*/
|
|
64
155
|
park(req: ApprovalRequest): Promise<ApprovalOutcome>;
|
|
65
|
-
/** Advance to the next queued ask, if any.
|
|
156
|
+
/** Advance to the next queued ask, if any. The promoted ask's source
|
|
157
|
+
* badge is re-checked against the log that has kept appending since it
|
|
158
|
+
* was parked (P-4: a same-callId twin parked while live may surface long
|
|
159
|
+
* after its call settled). */
|
|
66
160
|
private startNext;
|
|
67
161
|
/**
|
|
68
162
|
* The user decided on the current approval; settles it and drains the
|
|
@@ -76,5 +170,23 @@ export declare class ApprovalStore {
|
|
|
76
170
|
* @param outcome - The outcome every pending ask resolves with.
|
|
77
171
|
*/
|
|
78
172
|
settleAll(outcome: ApprovalOutcome): void;
|
|
173
|
+
/**
|
|
174
|
+
* Record a settling ask's callId as consumed (see {@link consumedCallIds}).
|
|
175
|
+
* @param pending - The ask leaving the active/queue set.
|
|
176
|
+
*/
|
|
177
|
+
private noteConsumed;
|
|
178
|
+
/**
|
|
179
|
+
* A same-callId duplicate just parked: every pending ask on that callId is
|
|
180
|
+
* now source-ambiguous. The forged-first order parks the fake ask clean
|
|
181
|
+
* (isLive true, no duplicate visible yet) and the genuine follow-up would
|
|
182
|
+
* carry the only badge — an inverted verdict. Flip them all, then rebuild
|
|
183
|
+
* + notify so an on-screen panel updates immediately. refreshActiveExternal
|
|
184
|
+
* early-returns on an existing external verdict, so the marking is
|
|
185
|
+
* monotonic and never erased. Deliberately badges instead of cancelling:
|
|
186
|
+
* cancelling both would hand the attacker a force-cancel primitive
|
|
187
|
+
* against genuine approvals.
|
|
188
|
+
* @param callId - The duplicated call id (non-undefined by construction).
|
|
189
|
+
*/
|
|
190
|
+
private markCallIdAmbiguous;
|
|
79
191
|
}
|
|
80
192
|
//# sourceMappingURL=approvals.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"approvals.d.ts","sourceRoot":"","sources":["../../../src/dsh-adapter/approvals.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAA;
|
|
1
|
+
{"version":3,"file":"approvals.d.ts","sourceRoot":"","sources":["../../../src/dsh-adapter/approvals.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAA;AACtF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAE5D,yDAAyD;AACzD,MAAM,WAAW,gBAAgB;IAC/B,wEAAwE;IACxE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,0DAA0D;IAC1D,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,oEAAoE;IACpE,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;IACzB;;;;;;;;;;;;;;OAcG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAA;CACzB;AAgGD;;;;;GAKG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAwB;IAC9C,OAAO,CAAC,MAAM,CAA6B;IAC3C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAwB;IAClD,OAAO,CAAC,GAAG,CAAI;IACf;;;OAGG;IACH,OAAO,CAAC,aAAa,CAAgC;IACrD;;;;OAIG;IACH,OAAO,CAAC,uBAAuB,CAAK;IACpC,OAAO,CAAC,YAAY,CAAQ;IAC5B;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAoB;IAEpD;;;;OAIG;IACH,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI;IAO3C;;;;;;OAMG;IACH,WAAW,IAAI,gBAAgB,GAAG,IAAI;IAKtC,OAAO,CAAC,IAAI;IAIZ;iFAC6E;IAC7E,OAAO,CAAC,cAAc;IAStB,gEAAgE;IAChE,OAAO,CAAC,eAAe;IAOvB;;;;;;;;;;;;;;OAcG;IACH,OAAO,CAAC,qBAAqB;IAY7B;;;;;;;;;;;;;OAaG;IACH,gBAAgB,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI;IAiB3C;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,gBAAgB;IAQxB;;;;;;OAMG;IACH,IAAI,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC;IAyDpD;;;kCAG8B;IAC9B,OAAO,CAAC,SAAS;IAUjB;;;;OAIG;IACH,MAAM,CAAC,OAAO,EAAE,cAAc,GAAG,UAAU,GAAG,IAAI;IAWlD;;;;OAIG;IACH,SAAS,CAAC,OAAO,EAAE,eAAe,GAAG,IAAI;IAazC;;;OAGG;IACH,OAAO,CAAC,YAAY;IAKpB;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,mBAAmB;CAoB5B"}
|