@adecore/agents-react 0.39.0 → 0.40.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/dist/agents/AgentModelLabel.js +2 -1
- package/dist/agents/model-name.js +2 -1
- package/dist/agents/provider-paths.d.ts +2 -1
- package/dist/agents/provider-paths.js +7 -3
- package/dist/chat/account-choice.js +57 -28
- package/dist/chat/bookmarks.js +2 -1
- package/dist/chat/drafts.d.ts +0 -1
- package/dist/chat/drafts.js +9 -28
- package/dist/chat/logic/picker-entries.js +2 -8
- package/dist/chat/logic/resume-compaction.js +2 -5
- package/dist/chat/logic/timeline-copy.js +2 -8
- package/dist/chat/logic/timeline.js +9 -10
- package/dist/chat/logic/workflow.js +2 -4
- package/dist/chat/subagent-conversation.js +2 -1
- package/dist/chat/ui/GoalDock.js +2 -1
- package/dist/chat/ui/LimitState.js +4 -6
- package/dist/chat/ui/composer/use-continuable-turn.js +0 -1
- package/dist/chat/ui/composer/use-notice.js +2 -1
- package/dist/chat/ui/composer/use-prompt-history.js +1 -1
- package/dist/chat/ui/composer/use-prompt-menus.js +216 -253
- package/dist/chat/ui/composer/use-take-back.js +2 -2
- package/dist/chat/ui/diff/ApprovalReview.js +1 -1
- package/dist/chat/ui/highlighter.d.ts +0 -6
- package/dist/chat/ui/highlighter.js +0 -12
- package/dist/chat/ui/icons.d.ts +0 -1
- package/dist/chat/ui/icons.js +0 -3
- package/dist/chat/ui/rows/MessageRows.js +2 -5
- package/dist/chat/ui/rows/row-rhythm.js +2 -5
- package/dist/chat/ui/theme-watch.js +3 -7
- package/dist/prompts/logic/usePromptSession.js +2 -1
- package/dist/providers/AddAccountForm.js +2 -1
- package/dist/providers/CliDetail.js +104 -130
- package/dist/providers/account-actions.js +6 -9
- package/dist/state/chats.d.ts +1 -0
- package/dist/state/chats.js +6 -11
- package/dist/state/provider-accounts.d.ts +1 -1
- package/dist/state/provider-accounts.js +2 -8
- package/dist/transport.d.ts +2 -0
- package/dist/transport.js +14 -0
- package/dist/usage/ChartLogo.d.ts +5 -0
- package/dist/usage/ChartLogo.js +48 -0
- package/dist/usage/LimitsList.js +6 -16
- package/dist/usage/UsageBreakdown.js +17 -40
- package/dist/usage/UsageLimits.js +183 -211
- package/dist/usage/UsagePage.js +3 -17
- package/dist/usage/UsageSummary.js +4 -9
- package/dist/usage/limits.js +16 -31
- package/dist/usage/summary.d.ts +1 -1
- package/dist/usage/summary.js +3 -5
- package/package.json +26 -26
- package/src/agents/AccountDot.tsx +1 -0
- package/src/agents/AgentIcon.tsx +2 -0
- package/src/agents/AgentModelLabel.tsx +4 -6
- package/src/agents/ModelOptionControl.tsx +8 -0
- package/src/agents/account-limits.ts +3 -0
- package/src/agents/accounts.ts +12 -0
- package/src/agents/model-name.ts +13 -1
- package/src/agents/model-options.ts +2 -0
- package/src/agents/model-requirement.ts +4 -0
- package/src/agents/option-words.ts +2 -0
- package/src/agents/provider-paths.ts +8 -3
- package/src/agents/status-look.ts +1 -0
- package/src/chat/account-choice.ts +25 -19
- package/src/chat/actions.ts +5 -0
- package/src/chat/attachments.ts +8 -0
- package/src/chat/bookmarks.ts +13 -1
- package/src/chat/chat-client.ts +63 -0
- package/src/chat/chat-references.ts +3 -0
- package/src/chat/composer-effort.ts +5 -0
- package/src/chat/composer-send.ts +4 -0
- package/src/chat/drafts.ts +21 -17
- package/src/chat/forks.ts +4 -0
- package/src/chat/guards.ts +2 -0
- package/src/chat/index.ts +7 -0
- package/src/chat/logic/ansi.ts +47 -0
- package/src/chat/logic/bookmarks.ts +7 -0
- package/src/chat/logic/command-looks.ts +29 -0
- package/src/chat/logic/context-usage.ts +3 -0
- package/src/chat/logic/continue-after-stop.ts +5 -0
- package/src/chat/logic/effort.ts +4 -0
- package/src/chat/logic/embedded-code.ts +63 -0
- package/src/chat/logic/fork.ts +22 -0
- package/src/chat/logic/generated-image.ts +7 -0
- package/src/chat/logic/goal.ts +7 -0
- package/src/chat/logic/handback.ts +2 -0
- package/src/chat/logic/limit.ts +8 -0
- package/src/chat/logic/picker-entries.ts +16 -4
- package/src/chat/logic/resume-compaction.ts +22 -5
- package/src/chat/logic/scrubber.ts +38 -5
- package/src/chat/logic/thought.ts +5 -0
- package/src/chat/logic/thread-cards.ts +6 -0
- package/src/chat/logic/timeline-copy.ts +4 -9
- package/src/chat/logic/timeline-target.ts +15 -0
- package/src/chat/logic/timeline.ts +113 -10
- package/src/chat/logic/tools.ts +30 -0
- package/src/chat/logic/visual-height.ts +5 -0
- package/src/chat/logic/visual-viewport.ts +1 -0
- package/src/chat/logic/welcome.ts +3 -0
- package/src/chat/logic/workflow.ts +26 -7
- package/src/chat/mentions.ts +56 -0
- package/src/chat/pending-prompts.ts +16 -0
- package/src/chat/persisted-json.ts +15 -0
- package/src/chat/preferences.ts +16 -0
- package/src/chat/prompt-hold.ts +2 -0
- package/src/chat/quote.ts +7 -0
- package/src/chat/reading-width.ts +1 -0
- package/src/chat/recent-messages.ts +5 -0
- package/src/chat/resume-compaction-dismissals.ts +1 -0
- package/src/chat/stash.ts +12 -0
- package/src/chat/subagent-conversation.ts +24 -4
- package/src/chat/subagent-list.ts +14 -0
- package/src/chat/subagent-support.ts +1 -0
- package/src/chat/subagent-view.ts +9 -0
- package/src/chat/thumbnails.ts +14 -0
- package/src/chat/timeline-flash.ts +2 -0
- package/src/chat/timeline-scroll.ts +23 -0
- package/src/chat/ui/AccountPill.tsx +4 -0
- package/src/chat/ui/AnsiOutput.tsx +1 -0
- package/src/chat/ui/BookmarkMarker.tsx +5 -0
- package/src/chat/ui/BookmarkSubmenu.tsx +1 -0
- package/src/chat/ui/ChatActivity.tsx +13 -0
- package/src/chat/ui/CodeBlock.tsx +8 -0
- package/src/chat/ui/Composer.tsx +15 -0
- package/src/chat/ui/ComposerInput.tsx +22 -0
- package/src/chat/ui/DiffPool.tsx +3 -0
- package/src/chat/ui/EditDiff.tsx +5 -0
- package/src/chat/ui/GoalDock.tsx +13 -1
- package/src/chat/ui/ImageView.tsx +11 -0
- package/src/chat/ui/LimitState.tsx +23 -6
- package/src/chat/ui/Markdown.tsx +41 -0
- package/src/chat/ui/MermaidBlock.tsx +16 -0
- package/src/chat/ui/MessageActions.tsx +8 -0
- package/src/chat/ui/ModelMarks.tsx +1 -0
- package/src/chat/ui/Pickers.tsx +26 -0
- package/src/chat/ui/PromptComposer.tsx +2 -0
- package/src/chat/ui/PromptEditor.tsx +6 -0
- package/src/chat/ui/QuoteButton.tsx +12 -0
- package/src/chat/ui/ReferenceChip.tsx +3 -0
- package/src/chat/ui/ResumeCompactionDock.tsx +1 -0
- package/src/chat/ui/RowTime.tsx +1 -0
- package/src/chat/ui/RunSettings.tsx +38 -0
- package/src/chat/ui/Scrubber.tsx +14 -0
- package/src/chat/ui/SessionLine.tsx +1 -0
- package/src/chat/ui/ShellCommand.tsx +32 -0
- package/src/chat/ui/SubagentControls.tsx +7 -0
- package/src/chat/ui/SubagentInfo.tsx +1 -0
- package/src/chat/ui/SubagentStopButton.tsx +6 -0
- package/src/chat/ui/SubagentTimeline.tsx +10 -0
- package/src/chat/ui/Timeline.tsx +119 -0
- package/src/chat/ui/TimelineMenu.tsx +5 -0
- package/src/chat/ui/UnifiedDiff.tsx +6 -0
- package/src/chat/ui/UploadThumb.tsx +2 -0
- package/src/chat/ui/VisualDialogs.tsx +3 -0
- package/src/chat/ui/VisualFrame.tsx +15 -0
- package/src/chat/ui/Welcome.tsx +1 -0
- package/src/chat/ui/chip-clipboard.ts +20 -0
- package/src/chat/ui/code-lines.ts +14 -0
- package/src/chat/ui/code-theme.ts +1 -0
- package/src/chat/ui/composer/ComposerControls.tsx +5 -0
- package/src/chat/ui/composer/ComposerMenu.tsx +12 -0
- package/src/chat/ui/composer/DraftParts.tsx +2 -0
- package/src/chat/ui/composer/QueuedMessages.tsx +4 -0
- package/src/chat/ui/composer/chip-paste.ts +16 -0
- package/src/chat/ui/composer/chips.ts +6 -0
- package/src/chat/ui/composer/editor.ts +9 -0
- package/src/chat/ui/composer/insert.ts +2 -0
- package/src/chat/ui/composer/key-handler.ts +39 -0
- package/src/chat/ui/composer/keys.ts +23 -0
- package/src/chat/ui/composer/placeholder.ts +6 -0
- package/src/chat/ui/composer/prompt-picks.ts +1 -0
- package/src/chat/ui/composer/surface.ts +5 -0
- package/src/chat/ui/composer/use-chat-controls.ts +13 -0
- package/src/chat/ui/composer/use-compaction-offer.ts +3 -0
- package/src/chat/ui/composer/use-composer-menus.ts +2 -0
- package/src/chat/ui/composer/use-composer-submit.ts +27 -0
- package/src/chat/ui/composer/use-continuable-turn.ts +3 -5
- package/src/chat/ui/composer/use-draft-files.ts +24 -0
- package/src/chat/ui/composer/use-effort-stepper.ts +8 -0
- package/src/chat/ui/composer/use-notice.ts +4 -1
- package/src/chat/ui/composer/use-pending-requests.ts +3 -0
- package/src/chat/ui/composer/use-prompt-history.ts +20 -11
- package/src/chat/ui/composer/use-prompt-hold.ts +3 -0
- package/src/chat/ui/composer/use-prompt-menus.ts +34 -14
- package/src/chat/ui/composer/use-take-back.ts +20 -2
- package/src/chat/ui/diff/ApprovalReview.tsx +18 -15
- package/src/chat/ui/diff/DiffFallbackBoundary.tsx +1 -0
- package/src/chat/ui/diff/DiffOmitted.tsx +1 -0
- package/src/chat/ui/diff/DiffStack.tsx +2 -0
- package/src/chat/ui/diff/DiffUnavailable.tsx +1 -0
- package/src/chat/ui/diff/EditorEditDiff.tsx +13 -0
- package/src/chat/ui/diff/EditorPatchDiff.tsx +4 -0
- package/src/chat/ui/diff/LegacyDiffPool.tsx +2 -0
- package/src/chat/ui/diff/LegacyEditDiff.tsx +2 -0
- package/src/chat/ui/diff/LegacyUnifiedDiff.tsx +8 -0
- package/src/chat/ui/diff/RawDiff.tsx +1 -0
- package/src/chat/ui/diff/ReviewDiff.tsx +1 -0
- package/src/chat/ui/diff/diff-source-budget.ts +8 -0
- package/src/chat/ui/diff/editor-edit-model.ts +4 -0
- package/src/chat/ui/diff/review-plan.ts +9 -0
- package/src/chat/ui/diff/review-settle.ts +1 -0
- package/src/chat/ui/diff/syntax-runtime.ts +52 -0
- package/src/chat/ui/diff/use-review-stack.ts +9 -0
- package/src/chat/ui/diff-syntax.ts +12 -0
- package/src/chat/ui/diff-theme.ts +2 -0
- package/src/chat/ui/file-links.ts +2 -0
- package/src/chat/ui/find-reveal.ts +1 -0
- package/src/chat/ui/full-diff.ts +1 -0
- package/src/chat/ui/highlighter.ts +29 -15
- package/src/chat/ui/icons.tsx +2 -4
- package/src/chat/ui/index.ts +4 -0
- package/src/chat/ui/intelligent-ui/UiBlockFrame.tsx +25 -0
- package/src/chat/ui/intelligent-ui/UiFallbackPart.tsx +1 -0
- package/src/chat/ui/intelligent-ui/UiReply.tsx +63 -0
- package/src/chat/ui/intelligent-ui/block-local.ts +5 -0
- package/src/chat/ui/intelligent-ui/chart-data.ts +15 -0
- package/src/chat/ui/intelligent-ui/node-text.ts +17 -0
- package/src/chat/ui/intelligent-ui/renderers/binding.ts +1 -0
- package/src/chat/ui/intelligent-ui/renderers/chart.tsx +31 -0
- package/src/chat/ui/intelligent-ui/renderers/choices.tsx +4 -0
- package/src/chat/ui/intelligent-ui/renderers/content.tsx +18 -0
- package/src/chat/ui/intelligent-ui/renderers/data.tsx +11 -0
- package/src/chat/ui/intelligent-ui/renderers/inputs.tsx +13 -0
- package/src/chat/ui/intelligent-ui/renderers/links.tsx +9 -0
- package/src/chat/ui/intelligent-ui/renderers/map-slots.ts +15 -0
- package/src/chat/ui/intelligent-ui/renderers/map-style.ts +15 -0
- package/src/chat/ui/intelligent-ui/renderers/map-view.tsx +48 -0
- package/src/chat/ui/intelligent-ui/renderers/map.tsx +20 -0
- package/src/chat/ui/intelligent-ui/renderers/place-point.ts +3 -0
- package/src/chat/ui/intelligent-ui/renderers/structure.tsx +3 -0
- package/src/chat/ui/intelligent-ui/renderers/text.tsx +8 -0
- package/src/chat/ui/intelligent-ui/reply-parts.ts +5 -0
- package/src/chat/ui/intelligent-ui/table-data.ts +17 -0
- package/src/chat/ui/intelligent-ui/use-in-view.ts +4 -0
- package/src/chat/ui/intelligent-ui/use-later.ts +4 -0
- package/src/chat/ui/intelligent-ui/use-truncated.ts +5 -0
- package/src/chat/ui/intelligent-ui/use-ui-links.ts +29 -0
- package/src/chat/ui/intelligent-ui/use-ui-queries.ts +60 -0
- package/src/chat/ui/markdown-blocks.ts +64 -0
- package/src/chat/ui/mermaid.ts +4 -0
- package/src/chat/ui/quote-selection.ts +10 -0
- package/src/chat/ui/rehype-chips.ts +5 -0
- package/src/chat/ui/rehype-fade.ts +2 -0
- package/src/chat/ui/remark-html-as-text.ts +5 -0
- package/src/chat/ui/reveal.ts +18 -0
- package/src/chat/ui/rows/ForksRow.tsx +5 -0
- package/src/chat/ui/rows/GeneratedImageRow.tsx +10 -0
- package/src/chat/ui/rows/MessageRows.tsx +29 -5
- package/src/chat/ui/rows/RowContainer.tsx +1 -0
- package/src/chat/ui/rows/Rows.tsx +2 -0
- package/src/chat/ui/rows/SubagentRow.tsx +13 -0
- package/src/chat/ui/rows/TaskRow.tsx +7 -0
- package/src/chat/ui/rows/VisualRow.tsx +4 -0
- package/src/chat/ui/rows/WorkRows.tsx +46 -0
- package/src/chat/ui/rows/WorkflowRow.tsx +10 -0
- package/src/chat/ui/rows/row-rhythm.ts +7 -5
- package/src/chat/ui/theme-watch.ts +8 -7
- package/src/chat/ui/thread-references.ts +7 -0
- package/src/chat/ui/use-diff-source-tokens.ts +8 -0
- package/src/chat/ui/useToggleSet.ts +3 -0
- package/src/chat/ui/visual-theme.ts +30 -0
- package/src/chat/visual-bridge.ts +23 -0
- package/src/chat/visual-viewport.ts +12 -0
- package/src/host-hooks.ts +6 -4
- package/src/host.ts +2 -0
- package/src/index.ts +3 -0
- package/src/lazy.tsx +9 -0
- package/src/mounted-registry.ts +4 -0
- package/src/port-transport.ts +32 -0
- package/src/prompts/index.ts +2 -0
- package/src/prompts/logic/focus.ts +5 -0
- package/src/prompts/logic/keys.ts +12 -0
- package/src/prompts/logic/prompts.ts +8 -0
- package/src/prompts/logic/subjects.ts +8 -0
- package/src/prompts/logic/useFocusAfterAnswer.ts +2 -0
- package/src/prompts/logic/usePromptSession.ts +13 -1
- package/src/prompts/ui/ApprovalBody.tsx +2 -0
- package/src/prompts/ui/PromptActions.tsx +2 -0
- package/src/prompts/ui/PromptCard.tsx +6 -0
- package/src/prompts/ui/PromptView.tsx +6 -0
- package/src/prompts/ui/QuestionBody.tsx +11 -0
- package/src/providers/AccountColors.tsx +1 -0
- package/src/providers/AccountDetail.tsx +7 -0
- package/src/providers/AccountVariables.tsx +2 -0
- package/src/providers/AddAccountForm.tsx +5 -1
- package/src/providers/CliDetail.tsx +6 -5
- package/src/providers/InstallPage.tsx +11 -0
- package/src/providers/ProviderList.tsx +3 -0
- package/src/providers/ProvidersPane.tsx +18 -0
- package/src/providers/account-actions.ts +10 -8
- package/src/providers/install-steps.ts +3 -0
- package/src/providers/pane-target.ts +5 -0
- package/src/providers/provider-abilities.ts +1 -0
- package/src/providers/provider-groups.ts +6 -0
- package/src/providers/variables.ts +12 -0
- package/src/scope.ts +2 -0
- package/src/state/chats.ts +61 -14
- package/src/state/index.ts +2 -0
- package/src/state/provider-accounts.ts +7 -9
- package/src/state/providers.ts +1 -0
- package/src/state/usage.ts +6 -0
- package/src/storage.ts +18 -0
- package/src/testing/prompts.ts +5 -0
- package/src/transport.ts +18 -0
- package/src/usage/ChartLogo.tsx +12 -0
- package/src/usage/LimitsList.tsx +7 -8
- package/src/usage/UsageBreakdown.tsx +11 -4
- package/src/usage/UsageChart.tsx +5 -0
- package/src/usage/UsageLimits.tsx +10 -7
- package/src/usage/UsageLimitsCard.tsx +2 -0
- package/src/usage/UsagePage.tsx +16 -19
- package/src/usage/UsageSummary.tsx +4 -5
- package/src/usage/UsageTiles.tsx +2 -0
- package/src/usage/format.ts +7 -0
- package/src/usage/limit-groups.ts +14 -0
- package/src/usage/limits.ts +11 -13
- package/src/usage/money.ts +1 -0
- package/src/usage/summary.ts +15 -7
|
@@ -21,9 +21,11 @@ export function workflowAgentState(agent: ChatWorkflowAgent, workflowRunning: bo
|
|
|
21
21
|
if (agent.status !== 'running') {
|
|
22
22
|
return agent.status;
|
|
23
23
|
}
|
|
24
|
+
|
|
24
25
|
if (!workflowRunning) {
|
|
25
26
|
return 'stopped';
|
|
26
27
|
}
|
|
28
|
+
|
|
27
29
|
return agent.startedAt === null ? 'waiting' : 'running';
|
|
28
30
|
}
|
|
29
31
|
|
|
@@ -35,15 +37,21 @@ export function workflowPhases(workflow: ChatWorkflow, workflowRunning: boolean)
|
|
|
35
37
|
const known = new Map(workflow.phases.map((phase) => [phase.index, phase]));
|
|
36
38
|
const loose: WorkflowAgentView[] = [];
|
|
37
39
|
const byPhase = new Map<number, WorkflowAgentView[]>();
|
|
40
|
+
|
|
38
41
|
for (const agent of workflow.agents) {
|
|
39
42
|
const view = { agent, state: workflowAgentState(agent, workflowRunning) };
|
|
43
|
+
|
|
40
44
|
if (agent.phaseIndex === null || !known.has(agent.phaseIndex)) {
|
|
41
45
|
loose.push(view);
|
|
46
|
+
|
|
42
47
|
continue;
|
|
43
48
|
}
|
|
49
|
+
|
|
44
50
|
byPhase.set(agent.phaseIndex, [...(byPhase.get(agent.phaseIndex) ?? []), view]);
|
|
45
51
|
}
|
|
52
|
+
|
|
46
53
|
const phases = workflow.phases.map((phase) => ({ key: `phase-${phase.index}`, title: phase.title, agents: byPhase.get(phase.index) ?? [] }));
|
|
54
|
+
|
|
47
55
|
return loose.length > 0 ? [{ key: 'loose', title: null, agents: loose }, ...phases] : phases;
|
|
48
56
|
}
|
|
49
57
|
|
|
@@ -70,16 +78,18 @@ export interface WorkflowAgentPreview {
|
|
|
70
78
|
|
|
71
79
|
/* The CLI's previews start with a markdown heading as often as not, which says nothing on one line. */
|
|
72
80
|
function firstLine(text: string | undefined): string | null {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
81
|
+
return (
|
|
82
|
+
text
|
|
83
|
+
?.split('\n')
|
|
84
|
+
.map((part) => part.replace(/^#+\s*/, '').trim())
|
|
85
|
+
.find((part) => part !== '') ?? null
|
|
86
|
+
);
|
|
78
87
|
}
|
|
79
88
|
|
|
80
89
|
/* The second line of an agent: what it does while at work, what it answered once it stopped, what it was asked before. */
|
|
81
90
|
export function workflowAgentPreview(view: WorkflowAgentView): WorkflowAgentPreview {
|
|
82
91
|
const { agent } = view;
|
|
92
|
+
|
|
83
93
|
switch (view.state) {
|
|
84
94
|
case 'running':
|
|
85
95
|
return agent.lastTool === null
|
|
@@ -99,10 +109,12 @@ export type WorkflowCounts = Record<WorkflowAgentState, number> & { total: numbe
|
|
|
99
109
|
|
|
100
110
|
export function workflowCounts(phases: WorkflowPhaseView[]): WorkflowCounts {
|
|
101
111
|
const counts: WorkflowCounts = { total: 0, running: 0, waiting: 0, stopped: 0, done: 0, failed: 0 };
|
|
112
|
+
|
|
102
113
|
for (const view of phases.flatMap((phase) => phase.agents)) {
|
|
103
114
|
counts.total += 1;
|
|
104
115
|
counts[view.state] += 1;
|
|
105
116
|
}
|
|
117
|
+
|
|
106
118
|
return counts;
|
|
107
119
|
}
|
|
108
120
|
|
|
@@ -112,22 +124,28 @@ const DESCRIPTION = /\bdescription\s*:\s*(['"`])((?:\\.|(?!\1)[^\\])*)\1/;
|
|
|
112
124
|
/* The literal from its opening brace to the one that closes it, skipping braces inside strings. */
|
|
113
125
|
function metaLiteral(script: string): string | null {
|
|
114
126
|
const start = META_START.exec(script);
|
|
127
|
+
|
|
115
128
|
if (start === null) {
|
|
116
129
|
return null;
|
|
117
130
|
}
|
|
131
|
+
|
|
118
132
|
const from = start.index + start[0].length - 1;
|
|
119
133
|
let depth = 0;
|
|
120
134
|
let quote: string | null = null;
|
|
135
|
+
|
|
121
136
|
for (let i = from; i < script.length; i++) {
|
|
122
137
|
const char = script[i]!;
|
|
138
|
+
|
|
123
139
|
if (quote !== null) {
|
|
124
140
|
if (char === '\\') {
|
|
125
141
|
i++;
|
|
126
142
|
} else if (char === quote) {
|
|
127
143
|
quote = null;
|
|
128
144
|
}
|
|
145
|
+
|
|
129
146
|
continue;
|
|
130
147
|
}
|
|
148
|
+
|
|
131
149
|
if (char === "'" || char === '"' || char === '`') {
|
|
132
150
|
quote = char;
|
|
133
151
|
} else if (char === '{') {
|
|
@@ -136,6 +154,7 @@ function metaLiteral(script: string): string | null {
|
|
|
136
154
|
return script.slice(from, i + 1);
|
|
137
155
|
}
|
|
138
156
|
}
|
|
157
|
+
|
|
139
158
|
return null;
|
|
140
159
|
}
|
|
141
160
|
|
|
@@ -143,6 +162,6 @@ function metaLiteral(script: string): string | null {
|
|
|
143
162
|
export function workflowDescription(input: unknown): string | null {
|
|
144
163
|
const script = typeof input === 'object' && input !== null && 'script' in input ? input.script : undefined;
|
|
145
164
|
const meta = typeof script === 'string' ? metaLiteral(script) : null;
|
|
146
|
-
|
|
147
|
-
return
|
|
165
|
+
|
|
166
|
+
return (meta === null ? undefined : DESCRIPTION.exec(meta)?.[2]?.replace(/\\(.)/g, '$1').trim()) || null;
|
|
148
167
|
}
|
package/src/chat/mentions.ts
CHANGED
|
@@ -12,12 +12,14 @@ export function carriesMentions(types: readonly string[]): boolean {
|
|
|
12
12
|
|
|
13
13
|
export function droppedMentions(data: Pick<DataTransfer, 'getData'>): string[] {
|
|
14
14
|
const text = data.getData(MENTION_DRAG_TYPE) || data.getData(LEGACY_MENTION_DRAG_TYPE);
|
|
15
|
+
|
|
15
16
|
return text.trim() === '' ? [] : text.trim().split(/\s+/);
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
export function writeMentionDrag(data: Pick<DataTransfer, 'setData'>, paths: readonly string[], legacy = true): void {
|
|
19
20
|
const text = paths.join(' ');
|
|
20
21
|
data.setData(MENTION_DRAG_TYPE, text);
|
|
22
|
+
|
|
21
23
|
if (legacy) {
|
|
22
24
|
data.setData(LEGACY_MENTION_DRAG_TYPE, text);
|
|
23
25
|
}
|
|
@@ -36,6 +38,7 @@ export interface ClipboardChips {
|
|
|
36
38
|
/* The chips among `segments`, without doubles, for the copy of `text`. */
|
|
37
39
|
export function clipboardChipsOf(text: string, segments: readonly ChipSegment[]): ClipboardChips {
|
|
38
40
|
const chips: ClipboardChips = { text, mentions: [], skills: [], references: [] };
|
|
41
|
+
|
|
39
42
|
for (const segment of segments) {
|
|
40
43
|
if (segment.kind === 'mention' && !chips.mentions.includes(segment.path)) {
|
|
41
44
|
chips.mentions.push(segment.path);
|
|
@@ -45,6 +48,7 @@ export function clipboardChipsOf(text: string, segments: readonly ChipSegment[])
|
|
|
45
48
|
chips.references.push({ id: segment.id, name: segment.name });
|
|
46
49
|
}
|
|
47
50
|
}
|
|
51
|
+
|
|
48
52
|
return chips;
|
|
49
53
|
}
|
|
50
54
|
|
|
@@ -64,28 +68,36 @@ const isStrings = (value: unknown): value is string[] => Array.isArray(value) &&
|
|
|
64
68
|
/* The chips of a paste, or null when it has none or its plain text is no longer the text they were copied with. */
|
|
65
69
|
export function readClipboardChips(data: Pick<DataTransfer, 'getData'>): ClipboardChips | null {
|
|
66
70
|
const raw = data.getData(CHIPS_CLIPBOARD_TYPE);
|
|
71
|
+
|
|
67
72
|
if (raw === '') {
|
|
68
73
|
return null;
|
|
69
74
|
}
|
|
75
|
+
|
|
70
76
|
let parsed: unknown;
|
|
77
|
+
|
|
71
78
|
try {
|
|
72
79
|
parsed = JSON.parse(raw);
|
|
73
80
|
} catch {
|
|
74
81
|
return null;
|
|
75
82
|
}
|
|
83
|
+
|
|
76
84
|
if (typeof parsed !== 'object' || parsed === null) {
|
|
77
85
|
return null;
|
|
78
86
|
}
|
|
87
|
+
|
|
79
88
|
const { text, mentions, skills, references } = parsed as Record<string, unknown>;
|
|
89
|
+
|
|
80
90
|
const tokens = Array.isArray(references)
|
|
81
91
|
? references.filter(
|
|
82
92
|
(token): token is ReferenceToken =>
|
|
83
93
|
typeof token === 'object' && token !== null && typeof token.id === 'string' && typeof token.name === 'string' && token.name !== ''
|
|
84
94
|
)
|
|
85
95
|
: [];
|
|
96
|
+
|
|
86
97
|
if (typeof text !== 'string' || lines(text) !== lines(data.getData('text/plain')) || !isStrings(mentions) || !isStrings(skills)) {
|
|
87
98
|
return null;
|
|
88
99
|
}
|
|
100
|
+
|
|
89
101
|
return { text: lines(text), mentions, skills, references: tokens.map(({ id, name }) => ({ id, name })) };
|
|
90
102
|
}
|
|
91
103
|
|
|
@@ -129,16 +141,21 @@ const PATH_GOES_ON = /[.:]$/;
|
|
|
129
141
|
function findQuery(text: string, caret: number, sigil: string): MentionQuery | null {
|
|
130
142
|
const before = text.slice(0, caret);
|
|
131
143
|
const at = before.lastIndexOf(sigil);
|
|
144
|
+
|
|
132
145
|
if (at < 0 || !isBoundary(before[at - 1])) {
|
|
133
146
|
return null;
|
|
134
147
|
}
|
|
148
|
+
|
|
135
149
|
const query = before.slice(at + 1);
|
|
150
|
+
|
|
136
151
|
if (/\s/.test(query) || !isBoundary(text[caret])) {
|
|
137
152
|
return null;
|
|
138
153
|
}
|
|
154
|
+
|
|
139
155
|
if (query !== '' && isTokenEnd(query.at(-1)) && !(sigil === '@' && PATH_GOES_ON.test(query))) {
|
|
140
156
|
return null;
|
|
141
157
|
}
|
|
158
|
+
|
|
142
159
|
return { start: at, query };
|
|
143
160
|
}
|
|
144
161
|
|
|
@@ -154,6 +171,7 @@ export function findMentionQuery(text: string, caret: number): MentionQuery | nu
|
|
|
154
171
|
/* The `$word` the caret sits in. The name has to start with a letter, so `$20` and `$1e6` stay text. */
|
|
155
172
|
export function findSkillQuery(text: string, caret: number): MentionQuery | null {
|
|
156
173
|
const found = findQuery(text, caret, '$');
|
|
174
|
+
|
|
157
175
|
return found === null || (found.query !== '' && !/^[A-Za-z]/.test(found.query)) ? null : found;
|
|
158
176
|
}
|
|
159
177
|
|
|
@@ -163,6 +181,7 @@ export function findSkillQuery(text: string, caret: number): MentionQuery | null
|
|
|
163
181
|
*/
|
|
164
182
|
export function findReferenceQuery(text: string, caret: number): MentionQuery | null {
|
|
165
183
|
const found = findQuery(text, caret, '#');
|
|
184
|
+
|
|
166
185
|
return found === null || !/^\p{L}/u.test(found.query) ? null : found;
|
|
167
186
|
}
|
|
168
187
|
|
|
@@ -170,6 +189,7 @@ export function findReferenceQuery(text: string, caret: number): MentionQuery |
|
|
|
170
189
|
export function insertToken(text: string, query: MentionQuery, sigil: string, value: string): { text: string; caret: number } {
|
|
171
190
|
const end = query.start + 1 + query.query.length;
|
|
172
191
|
const token = `${sigil}${value} `;
|
|
192
|
+
|
|
173
193
|
return { text: `${text.slice(0, query.start)}${token}${text.slice(end)}`, caret: query.start + token.length };
|
|
174
194
|
}
|
|
175
195
|
|
|
@@ -198,29 +218,36 @@ export function mentionPick(index: number, search: MentionSearch, query: string)
|
|
|
198
218
|
if (search.query !== query) {
|
|
199
219
|
return null;
|
|
200
220
|
}
|
|
221
|
+
|
|
201
222
|
return search.files[index] ?? search.files[0] ?? null;
|
|
202
223
|
}
|
|
203
224
|
|
|
204
225
|
function findToken(text: string, sigil: string, value: string, from: number): number {
|
|
205
226
|
const token = `${sigil}${value}`;
|
|
206
227
|
let index = text.indexOf(token, from);
|
|
228
|
+
|
|
207
229
|
while (index >= 0) {
|
|
208
230
|
if (isBoundary(text[index - 1]) && isTokenEnd(text[index + token.length])) {
|
|
209
231
|
return index;
|
|
210
232
|
}
|
|
233
|
+
|
|
211
234
|
index = text.indexOf(token, index + 1);
|
|
212
235
|
}
|
|
236
|
+
|
|
213
237
|
return -1;
|
|
214
238
|
}
|
|
215
239
|
|
|
216
240
|
function present(text: string, sigil: string, chosen: string[]): string[] {
|
|
217
241
|
const found: Array<{ index: number; value: string }> = [];
|
|
242
|
+
|
|
218
243
|
for (const value of new Set(chosen)) {
|
|
219
244
|
const index = findToken(text, sigil, value, 0);
|
|
245
|
+
|
|
220
246
|
if (index >= 0) {
|
|
221
247
|
found.push({ index, value });
|
|
222
248
|
}
|
|
223
249
|
}
|
|
250
|
+
|
|
224
251
|
return found.sort((a, b) => a.index - b.index).map((entry) => entry.value);
|
|
225
252
|
}
|
|
226
253
|
|
|
@@ -237,6 +264,7 @@ export function presentSkills(text: string, chosen: string[]): string[] {
|
|
|
237
264
|
/* The ids among `ids` whose `#name` still sits in the text as a whole token, in text order and without doubles. */
|
|
238
265
|
export function presentReferences(text: string, ids: readonly string[], tokens: readonly ReferenceToken[]): string[] {
|
|
239
266
|
const wanted = new Set(ids);
|
|
267
|
+
|
|
240
268
|
return chipRanges(text, [], [], [], tokens)
|
|
241
269
|
.map((range) => range.value)
|
|
242
270
|
.filter((id, index, found) => wanted.has(id) && found.indexOf(id) === index);
|
|
@@ -247,12 +275,15 @@ export function chipText(segment: ChipSegment): string {
|
|
|
247
275
|
if (segment.kind === 'mention') {
|
|
248
276
|
return `@${segment.path}`;
|
|
249
277
|
}
|
|
278
|
+
|
|
250
279
|
if (segment.kind === 'skill') {
|
|
251
280
|
return `$${segment.name}`;
|
|
252
281
|
}
|
|
282
|
+
|
|
253
283
|
if (segment.kind === 'reference') {
|
|
254
284
|
return `#${segment.name}`;
|
|
255
285
|
}
|
|
286
|
+
|
|
256
287
|
return segment.text;
|
|
257
288
|
}
|
|
258
289
|
|
|
@@ -262,9 +293,11 @@ export function chipText(segment: ChipSegment): string {
|
|
|
262
293
|
*/
|
|
263
294
|
export function pastedMentions(text: string): string[] {
|
|
264
295
|
const tokens = text.trim().split(/\s+/);
|
|
296
|
+
|
|
265
297
|
if (tokens.some((token) => !/^@[^\s@]+$/.test(token))) {
|
|
266
298
|
return [];
|
|
267
299
|
}
|
|
300
|
+
|
|
268
301
|
return [...new Set(tokens.map((token) => token.slice(1)))];
|
|
269
302
|
}
|
|
270
303
|
|
|
@@ -295,27 +328,36 @@ export function chipRanges(
|
|
|
295
328
|
...[...new Set(skills)].map((value) => ({ sigil: '$', kind: 'skill' as const, spelled: value, value })),
|
|
296
329
|
...references.filter((token) => token.name !== '').map((token) => ({ sigil: '#', kind: 'reference' as const, spelled: token.name, value: token.id }))
|
|
297
330
|
].sort((a, b) => b.spelled.length - a.spelled.length);
|
|
331
|
+
|
|
298
332
|
const allowed = (from: number, to: number): boolean => !excluded.some((range) => from < range.to && to > range.from);
|
|
333
|
+
|
|
299
334
|
const ranges: ChipRange[] = [];
|
|
300
335
|
let cursor = 0;
|
|
336
|
+
|
|
301
337
|
while (cursor < text.length) {
|
|
302
338
|
let best: ChipRange | null = null;
|
|
339
|
+
|
|
303
340
|
for (const token of tokens) {
|
|
304
341
|
const length = token.spelled.length + 1;
|
|
305
342
|
let index = findToken(text, token.sigil, token.spelled, cursor);
|
|
343
|
+
|
|
306
344
|
while (index >= 0 && !allowed(index, index + length)) {
|
|
307
345
|
index = findToken(text, token.sigil, token.spelled, index + 1);
|
|
308
346
|
}
|
|
347
|
+
|
|
309
348
|
if (index >= 0 && (best === null || index < best.from)) {
|
|
310
349
|
best = { from: index, to: index + length, kind: token.kind, value: token.value };
|
|
311
350
|
}
|
|
312
351
|
}
|
|
352
|
+
|
|
313
353
|
if (best === null) {
|
|
314
354
|
break;
|
|
315
355
|
}
|
|
356
|
+
|
|
316
357
|
ranges.push(best);
|
|
317
358
|
cursor = best.to;
|
|
318
359
|
}
|
|
360
|
+
|
|
319
361
|
return ranges;
|
|
320
362
|
}
|
|
321
363
|
|
|
@@ -323,9 +365,11 @@ function segmentOf(text: string, range: ChipRange): ChipSegment {
|
|
|
323
365
|
if (range.kind === 'mention') {
|
|
324
366
|
return { kind: 'mention', path: range.value };
|
|
325
367
|
}
|
|
368
|
+
|
|
326
369
|
if (range.kind === 'skill') {
|
|
327
370
|
return { kind: 'skill', name: range.value };
|
|
328
371
|
}
|
|
372
|
+
|
|
329
373
|
return { kind: 'reference', id: range.value, name: text.slice(range.from + 1, range.to) };
|
|
330
374
|
}
|
|
331
375
|
|
|
@@ -349,29 +393,37 @@ export function namedReferenceRanges(
|
|
|
349
393
|
excluded: readonly TextRange[] = []
|
|
350
394
|
): ChipRange[] {
|
|
351
395
|
const byName = new Map<string, string[]>();
|
|
396
|
+
|
|
352
397
|
for (const item of items) {
|
|
353
398
|
if (item.name !== '') {
|
|
354
399
|
byName.set(item.name, [...(byName.get(item.name) ?? []), item.id]);
|
|
355
400
|
}
|
|
356
401
|
}
|
|
402
|
+
|
|
357
403
|
const names = [...byName.keys()].sort((a, b) => b.length - a.length);
|
|
358
404
|
const preferred = new Set(referenced);
|
|
359
405
|
const ranges: ChipRange[] = [];
|
|
360
406
|
let index = text.indexOf('#');
|
|
407
|
+
|
|
361
408
|
while (index >= 0) {
|
|
362
409
|
const from = index;
|
|
410
|
+
|
|
363
411
|
const name =
|
|
364
412
|
from > 0 && !REFERENCE_OPENER.test(text[from - 1]!)
|
|
365
413
|
? undefined
|
|
366
414
|
: names.find((candidate) => text.startsWith(candidate, from + 1) && isTokenEnd(text[from + 1 + candidate.length]));
|
|
415
|
+
|
|
367
416
|
const to = name === undefined ? from + 1 : from + 1 + name.length;
|
|
368
417
|
const ids = name === undefined ? [] : byName.get(name)!;
|
|
369
418
|
const id = ids.find((candidate) => preferred.has(candidate)) ?? (ids.length === 1 ? ids[0] : undefined);
|
|
419
|
+
|
|
370
420
|
if (id !== undefined && !excluded.some((range) => from < range.to && to > range.from)) {
|
|
371
421
|
ranges.push({ from, to, kind: 'reference', value: id });
|
|
372
422
|
}
|
|
423
|
+
|
|
373
424
|
index = text.indexOf('#', to);
|
|
374
425
|
}
|
|
426
|
+
|
|
375
427
|
return ranges;
|
|
376
428
|
}
|
|
377
429
|
|
|
@@ -383,15 +435,19 @@ export function tokenizeNamedReferences(text: string, items: readonly ReferenceT
|
|
|
383
435
|
function segmentsOf(text: string, ranges: readonly ChipRange[]): ChipSegment[] {
|
|
384
436
|
const segments: ChipSegment[] = [];
|
|
385
437
|
let cursor = 0;
|
|
438
|
+
|
|
386
439
|
for (const range of ranges) {
|
|
387
440
|
if (range.from > cursor) {
|
|
388
441
|
segments.push({ kind: 'text', text: text.slice(cursor, range.from) });
|
|
389
442
|
}
|
|
443
|
+
|
|
390
444
|
segments.push(segmentOf(text, range));
|
|
391
445
|
cursor = range.to;
|
|
392
446
|
}
|
|
447
|
+
|
|
393
448
|
if (cursor < text.length) {
|
|
394
449
|
segments.push({ kind: 'text', text: text.slice(cursor) });
|
|
395
450
|
}
|
|
451
|
+
|
|
396
452
|
return segments;
|
|
397
453
|
}
|
|
@@ -40,9 +40,11 @@ export class ChatPromptFeed {
|
|
|
40
40
|
dispose(): void {
|
|
41
41
|
this.disposed = true;
|
|
42
42
|
this.unsubscribe?.();
|
|
43
|
+
|
|
43
44
|
for (const release of this.children.values()) {
|
|
44
45
|
release();
|
|
45
46
|
}
|
|
47
|
+
|
|
46
48
|
this.children.clear();
|
|
47
49
|
}
|
|
48
50
|
|
|
@@ -50,49 +52,63 @@ export class ChatPromptFeed {
|
|
|
50
52
|
if (this.disposed) {
|
|
51
53
|
return;
|
|
52
54
|
}
|
|
55
|
+
|
|
53
56
|
const store = useChats.getState();
|
|
54
57
|
const subjects = new Map<string, ChatPromptSubject>();
|
|
55
58
|
const seen = new Set<string>();
|
|
59
|
+
|
|
56
60
|
const visit = (chatId: string, source?: ChatPromptSubject['source']): void => {
|
|
57
61
|
if (seen.has(chatId)) {
|
|
58
62
|
return;
|
|
59
63
|
}
|
|
64
|
+
|
|
60
65
|
seen.add(chatId);
|
|
61
66
|
const key = this.scope.keyOf(chatId);
|
|
62
67
|
const row = store.byKey[key];
|
|
63
68
|
const info = store.statusByKey[key]?.info ?? row?.info;
|
|
69
|
+
|
|
64
70
|
for (const item of waitingRequestsOf(row)) {
|
|
65
71
|
if (info !== undefined && (chatId === this.chatId || stillWaiting(item, info))) {
|
|
66
72
|
subjects.set(`${chatId}:${item.requestId}`, { kind: 'chat', nodeId: chatId, item, ...(source === undefined ? {} : { source }) });
|
|
67
73
|
}
|
|
68
74
|
}
|
|
75
|
+
|
|
69
76
|
for (const item of Object.values(row?.structure ?? {})) {
|
|
70
77
|
if (item.kind !== 'subagent' || item.childId === undefined || seen.has(item.childId)) {
|
|
71
78
|
continue;
|
|
72
79
|
}
|
|
80
|
+
|
|
73
81
|
const childInfo = store.statusByKey[this.scope.keyOf(item.childId)]?.info;
|
|
82
|
+
|
|
74
83
|
if (item.status !== 'running' && childInfo?.status !== 'needs-you') {
|
|
75
84
|
continue;
|
|
76
85
|
}
|
|
86
|
+
|
|
77
87
|
if (!this.children.has(item.childId)) {
|
|
78
88
|
this.children.set(item.childId, this.scope.chats.observe(item.childId));
|
|
79
89
|
}
|
|
90
|
+
|
|
80
91
|
visit(item.childId, { label: subagentTitle(item), provider: childInfo?.provider });
|
|
81
92
|
}
|
|
82
93
|
};
|
|
94
|
+
|
|
83
95
|
visit(this.chatId);
|
|
96
|
+
|
|
84
97
|
for (const [chatId, release] of this.children) {
|
|
85
98
|
if (!seen.has(chatId)) {
|
|
86
99
|
this.children.delete(chatId);
|
|
87
100
|
release();
|
|
88
101
|
}
|
|
89
102
|
}
|
|
103
|
+
|
|
90
104
|
const next = [...subjects.values()];
|
|
105
|
+
|
|
91
106
|
if (
|
|
92
107
|
this.current === null ||
|
|
93
108
|
next.length !== this.current.length ||
|
|
94
109
|
next.some((subject, index) => {
|
|
95
110
|
const previous = this.current?.[index];
|
|
111
|
+
|
|
96
112
|
return (
|
|
97
113
|
subject.nodeId !== previous?.nodeId ||
|
|
98
114
|
subject.item !== previous.item ||
|
|
@@ -14,22 +14,30 @@ export function persistedJson<T>(
|
|
|
14
14
|
): PersistedJson<T> {
|
|
15
15
|
let memory = fallback;
|
|
16
16
|
let unwritten = false;
|
|
17
|
+
|
|
17
18
|
const currentKey = (): string => (typeof key === 'string' ? key : key());
|
|
19
|
+
|
|
18
20
|
return {
|
|
19
21
|
read: () => {
|
|
20
22
|
hydrateChatStorage();
|
|
23
|
+
|
|
21
24
|
if (unwritten) {
|
|
22
25
|
return memory;
|
|
23
26
|
}
|
|
27
|
+
|
|
24
28
|
try {
|
|
25
29
|
const storage = chatStorageAdapter();
|
|
30
|
+
|
|
26
31
|
if (!storage) {
|
|
27
32
|
return memory;
|
|
28
33
|
}
|
|
34
|
+
|
|
29
35
|
let raw = storage.getItem(currentKey());
|
|
36
|
+
|
|
30
37
|
if (raw === null) {
|
|
31
38
|
for (const legacy of legacyKeys()) {
|
|
32
39
|
raw = storage.getItem(legacy);
|
|
40
|
+
|
|
33
41
|
if (raw !== null) {
|
|
34
42
|
// Keep the old key for clients still using it, and migrate without changing the record.
|
|
35
43
|
try {
|
|
@@ -37,11 +45,14 @@ export function persistedJson<T>(
|
|
|
37
45
|
} catch {
|
|
38
46
|
// A full storage still lets the existing record be read.
|
|
39
47
|
}
|
|
48
|
+
|
|
40
49
|
break;
|
|
41
50
|
}
|
|
42
51
|
}
|
|
43
52
|
}
|
|
53
|
+
|
|
44
54
|
memory = parse(raw);
|
|
55
|
+
|
|
45
56
|
return memory;
|
|
46
57
|
} catch {
|
|
47
58
|
return memory;
|
|
@@ -51,13 +62,17 @@ export function persistedJson<T>(
|
|
|
51
62
|
hydrateChatStorage();
|
|
52
63
|
memory = value;
|
|
53
64
|
unwritten = true;
|
|
65
|
+
|
|
54
66
|
try {
|
|
55
67
|
const storage = chatStorageAdapter();
|
|
68
|
+
|
|
56
69
|
if (!storage) {
|
|
57
70
|
return false;
|
|
58
71
|
}
|
|
72
|
+
|
|
59
73
|
storage.setItem(currentKey(), JSON.stringify(value));
|
|
60
74
|
unwritten = false;
|
|
75
|
+
|
|
61
76
|
return true;
|
|
62
77
|
} catch {
|
|
63
78
|
return false;
|
package/src/chat/preferences.ts
CHANGED
|
@@ -43,9 +43,11 @@ function parseFavoriteModels(stored: unknown): Partial<Record<AgentKind, string[
|
|
|
43
43
|
if (typeof stored !== 'object' || stored === null || Array.isArray(stored)) {
|
|
44
44
|
return {};
|
|
45
45
|
}
|
|
46
|
+
|
|
46
47
|
return Object.fromEntries(
|
|
47
48
|
Object.entries(stored).flatMap(([provider, slugs]) => {
|
|
48
49
|
const kept = Array.isArray(slugs) ? slugs.filter((slug): slug is string => typeof slug === 'string' && slug !== '') : [];
|
|
50
|
+
|
|
49
51
|
return kept.length === 0 ? [] : [[provider, kept]];
|
|
50
52
|
})
|
|
51
53
|
);
|
|
@@ -60,8 +62,10 @@ export function parseChatPreferences(raw: string | null): ChatPreferences {
|
|
|
60
62
|
if (raw === null) {
|
|
61
63
|
return DEFAULT_CHAT_PREFERENCES;
|
|
62
64
|
}
|
|
65
|
+
|
|
63
66
|
try {
|
|
64
67
|
const stored = JSON.parse(raw) as Partial<ChatPreferences>;
|
|
68
|
+
|
|
65
69
|
return {
|
|
66
70
|
selectionByProvider: { ...stored.selectionByProvider },
|
|
67
71
|
lastProvider: stored.lastProvider ?? null,
|
|
@@ -86,9 +90,11 @@ export function defaultProvider(preferences: ChatPreferences): AgentKind | null
|
|
|
86
90
|
/* The model a chat of this provider starts with; null lets the host fall back to the CLI's own default. */
|
|
87
91
|
export function selectionFor(preferences: ChatPreferences, provider: AgentKind | null | undefined): ModelSelection | null {
|
|
88
92
|
const owner = provider ?? preferences.lastProvider;
|
|
93
|
+
|
|
89
94
|
if (owner === null) {
|
|
90
95
|
return null;
|
|
91
96
|
}
|
|
97
|
+
|
|
92
98
|
return preferences.selectionByProvider[owner] ?? null;
|
|
93
99
|
}
|
|
94
100
|
|
|
@@ -112,10 +118,13 @@ export function withSelection(preferences: ChatPreferences, provider: AgentKind,
|
|
|
112
118
|
*/
|
|
113
119
|
export function accountFor(preferences: ChatPreferences, scopeId: string, provider: AgentKind, accounts?: ProviderAccounts | null): string | null {
|
|
114
120
|
const picked = preferences.accountByMachine[scopeId]?.[provider] ?? null;
|
|
121
|
+
|
|
115
122
|
if (picked === null || accounts === undefined) {
|
|
116
123
|
return picked;
|
|
117
124
|
}
|
|
125
|
+
|
|
118
126
|
const account = accounts?.accounts[picked];
|
|
127
|
+
|
|
119
128
|
return account?.kind === provider && account.enabled !== false ? picked : null;
|
|
120
129
|
}
|
|
121
130
|
|
|
@@ -124,6 +133,7 @@ export function withAccount(preferences: ChatPreferences, scopeId: string, provi
|
|
|
124
133
|
const { [provider]: _previous, ...others } = preferences.accountByMachine[scopeId] ?? {};
|
|
125
134
|
const picks = account === null || account === provider ? others : { ...others, [provider]: account };
|
|
126
135
|
const { [scopeId]: _machine, ...machines } = preferences.accountByMachine;
|
|
136
|
+
|
|
127
137
|
return { ...preferences, accountByMachine: Object.keys(picks).length === 0 ? machines : { ...machines, [scopeId]: picks } };
|
|
128
138
|
}
|
|
129
139
|
|
|
@@ -131,8 +141,10 @@ export function withAccount(preferences: ChatPreferences, scopeId: string, provi
|
|
|
131
141
|
export function chatPreferencesPayload(preferences: ChatPreferences, scopeId: string, accounts?: ProviderAccounts | null): ChatPreferencesPayload {
|
|
132
142
|
const picks = Object.keys(preferences.accountByMachine[scopeId] ?? {}).flatMap((provider) => {
|
|
133
143
|
const account = accountFor(preferences, scopeId, provider as AgentKind, accounts);
|
|
144
|
+
|
|
134
145
|
return account === null ? [] : [[provider, account] as const];
|
|
135
146
|
});
|
|
147
|
+
|
|
136
148
|
return {
|
|
137
149
|
runtimeMode: preferences.runtimeMode,
|
|
138
150
|
terminalRuntimeMode: preferences.terminalRuntimeMode,
|
|
@@ -156,6 +168,7 @@ export function readChatPreferences(): ChatPreferences {
|
|
|
156
168
|
|
|
157
169
|
/* Where the remembered default is kept, which an app with more than one window of the same origin listens for. */
|
|
158
170
|
export let CHAT_PREFERENCES_KEY = storageKey();
|
|
171
|
+
|
|
159
172
|
onChatStorageConfiguration(() => {
|
|
160
173
|
CHAT_PREFERENCES_KEY = storageKey();
|
|
161
174
|
});
|
|
@@ -192,6 +205,7 @@ export function rememberSendKey(sendKey: ChatSendKey): void {
|
|
|
192
205
|
export function rememberChatSelection(provider: AgentKind, selection: ModelSelection): void {
|
|
193
206
|
const current = useChatPreferences.getState();
|
|
194
207
|
const previous = current.selectionByProvider[provider];
|
|
208
|
+
|
|
195
209
|
if (
|
|
196
210
|
current.lastProvider === provider &&
|
|
197
211
|
previous?.model === selection.model &&
|
|
@@ -200,6 +214,7 @@ export function rememberChatSelection(provider: AgentKind, selection: ModelSelec
|
|
|
200
214
|
) {
|
|
201
215
|
return;
|
|
202
216
|
}
|
|
217
|
+
|
|
203
218
|
write(withSelection(current, provider, selection));
|
|
204
219
|
}
|
|
205
220
|
|
|
@@ -217,6 +232,7 @@ export function withFavoriteToggled(preferences: ChatPreferences, provider: Agen
|
|
|
217
232
|
const current = preferences.favoriteModels[provider] ?? [];
|
|
218
233
|
const next = current.includes(slug) ? current.filter((each) => each !== slug) : [...current, slug];
|
|
219
234
|
const { [provider]: _previous, ...others } = preferences.favoriteModels;
|
|
235
|
+
|
|
220
236
|
return { ...preferences, favoriteModels: next.length === 0 ? others : { ...others, [provider]: next } };
|
|
221
237
|
}
|
|
222
238
|
|
package/src/chat/prompt-hold.ts
CHANGED
|
@@ -15,6 +15,7 @@ export function holdsPrompt(input: HoldInput, now: number): boolean {
|
|
|
15
15
|
if (input.composing) {
|
|
16
16
|
return true;
|
|
17
17
|
}
|
|
18
|
+
|
|
18
19
|
return input.typedAt !== null && now - input.typedAt < PROMPT_HOLD_PAUSE_MS;
|
|
19
20
|
}
|
|
20
21
|
|
|
@@ -23,5 +24,6 @@ export function holdLiftsIn(input: HoldInput, now: number): number | null {
|
|
|
23
24
|
if (input.composing || input.typedAt === null) {
|
|
24
25
|
return null;
|
|
25
26
|
}
|
|
27
|
+
|
|
26
28
|
return Math.max(0, input.typedAt + PROMPT_HOLD_PAUSE_MS - now);
|
|
27
29
|
}
|