@adecore/agents-react 0.23.1 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -11
- package/dist/agents/AccountDot.js +3 -1
- package/dist/agents/accounts.d.ts +3 -2
- package/dist/agents/accounts.js +5 -6
- package/dist/chat/actions.d.ts +2 -2
- package/dist/chat/actions.js +8 -5
- package/dist/chat/bookmarks.d.ts +5 -3
- package/dist/chat/bookmarks.js +13 -14
- package/dist/chat/chat-client.d.ts +1 -0
- package/dist/chat/chat-client.js +7 -0
- package/dist/chat/composer-send.d.ts +3 -0
- package/dist/chat/composer-send.js +22 -0
- package/dist/chat/forks.js +22 -5
- package/dist/chat/index.d.ts +31 -0
- package/dist/chat/index.js +28 -0
- package/dist/chat/logic/embedded-code.d.ts +8 -0
- package/dist/chat/logic/embedded-code.js +371 -0
- package/dist/chat/logic/fork.js +1 -0
- package/dist/chat/logic/goal.d.ts +6 -0
- package/dist/chat/logic/goal.js +26 -0
- package/dist/chat/logic/scrubber.d.ts +1 -1
- package/dist/chat/logic/scrubber.js +6 -5
- package/dist/chat/logic/thought.d.ts +3 -0
- package/dist/chat/logic/thought.js +35 -0
- package/dist/chat/logic/thread-cards.js +2 -0
- package/dist/chat/logic/timeline.d.ts +5 -0
- package/dist/chat/logic/timeline.js +22 -1
- package/dist/chat/logic/tool-catalog.d.ts +1 -0
- package/dist/chat/logic/tool-catalog.js +3 -1
- package/dist/chat/logic/tools.d.ts +14 -0
- package/dist/chat/logic/tools.js +57 -0
- package/dist/chat/preferences.d.ts +6 -0
- package/dist/chat/preferences.js +19 -3
- package/dist/chat/reading-width.d.ts +9 -0
- package/dist/chat/reading-width.js +27 -0
- package/dist/chat/send-key.d.ts +10 -0
- package/dist/chat/send-key.js +24 -0
- package/dist/chat/ui/BookmarkMarker.js +4 -2
- package/dist/chat/ui/BookmarkSubmenu.js +4 -3
- package/dist/chat/ui/ChatActivity.js +14 -10
- package/dist/chat/ui/ChatReferenceChip.js +3 -4
- package/dist/chat/ui/CodeBlock.d.ts +2 -1
- package/dist/chat/ui/CodeBlock.js +14 -96
- package/dist/chat/ui/Composer.d.ts +1 -1
- package/dist/chat/ui/Composer.js +117 -906
- package/dist/chat/ui/ComposerInput.js +5 -4
- package/dist/chat/ui/EditDiff.js +3 -2
- package/dist/chat/ui/GoalDock.d.ts +9 -0
- package/dist/chat/ui/GoalDock.js +63 -0
- package/dist/chat/ui/LimitState.js +5 -3
- package/dist/chat/ui/Markdown.d.ts +2 -1
- package/dist/chat/ui/Markdown.js +70 -31
- package/dist/chat/ui/MermaidBlock.d.ts +3 -0
- package/dist/chat/ui/MermaidBlock.js +73 -0
- package/dist/chat/ui/MessageActions.js +12 -7
- package/dist/chat/ui/Pickers.js +6 -5
- package/dist/chat/ui/PromptComposer.js +4 -3
- package/dist/chat/ui/RowTime.d.ts +3 -0
- package/dist/chat/ui/RowTime.js +12 -0
- package/dist/chat/ui/RunSettings.js +5 -4
- package/dist/chat/ui/ShellCommand.d.ts +3 -0
- package/dist/chat/ui/ShellCommand.js +110 -0
- package/dist/chat/ui/SubagentInfo.js +4 -3
- package/dist/chat/ui/SubagentStopButton.js +4 -3
- package/dist/chat/ui/SubagentTimeline.js +4 -1
- package/dist/chat/ui/Timeline.js +70 -40
- package/dist/chat/ui/TimelineMenu.d.ts +2 -1
- package/dist/chat/ui/TimelineMenu.js +18 -8
- package/dist/chat/ui/UnifiedDiff.js +3 -2
- package/dist/chat/ui/VisualFrame.js +6 -7
- package/dist/chat/ui/code-theme.d.ts +2 -1
- package/dist/chat/ui/code-theme.js +4 -4
- package/dist/chat/ui/composer/ComposerControls.d.ts +27 -0
- package/dist/chat/ui/composer/ComposerControls.js +34 -0
- package/dist/chat/ui/composer/ComposerMenu.d.ts +4 -0
- package/dist/chat/ui/composer/ComposerMenu.js +46 -0
- package/dist/chat/ui/composer/DraftParts.d.ts +18 -0
- package/dist/chat/ui/composer/DraftParts.js +25 -0
- package/dist/chat/ui/composer/QueuedMessages.d.ts +10 -0
- package/dist/chat/ui/composer/QueuedMessages.js +20 -0
- package/dist/chat/ui/composer/key-handler.d.ts +20 -0
- package/dist/chat/ui/composer/key-handler.js +150 -0
- package/dist/chat/ui/composer/keys.d.ts +7 -2
- package/dist/chat/ui/composer/keys.js +19 -9
- package/dist/chat/ui/composer/placeholder.d.ts +9 -0
- package/dist/chat/ui/composer/placeholder.js +38 -0
- package/dist/chat/ui/composer/use-chat-controls.d.ts +28 -0
- package/dist/chat/ui/composer/use-chat-controls.js +112 -0
- package/dist/chat/ui/composer/use-compaction-offer.d.ts +2 -0
- package/dist/chat/ui/composer/use-compaction-offer.js +23 -0
- package/dist/chat/ui/composer/use-composer-draft.d.ts +14 -0
- package/dist/chat/ui/composer/use-composer-draft.js +39 -0
- package/dist/chat/ui/composer/use-composer-menus.d.ts +52 -0
- package/dist/chat/ui/composer/use-composer-menus.js +232 -0
- package/dist/chat/ui/composer/use-composer-submit.d.ts +32 -0
- package/dist/chat/ui/composer/use-composer-submit.js +113 -0
- package/dist/chat/ui/composer/use-draft-files.d.ts +27 -0
- package/dist/chat/ui/composer/use-draft-files.js +111 -0
- package/dist/chat/ui/composer/use-notice.d.ts +6 -0
- package/dist/chat/ui/composer/use-notice.js +18 -0
- package/dist/chat/ui/composer/use-pending-requests.d.ts +2 -0
- package/dist/chat/ui/composer/use-pending-requests.js +22 -0
- package/dist/chat/ui/composer/use-prompt-history.d.ts +8 -0
- package/dist/chat/ui/composer/use-prompt-history.js +57 -0
- package/dist/chat/ui/composer/use-prompt-hold.d.ts +6 -0
- package/dist/chat/ui/composer/use-prompt-hold.js +48 -0
- package/dist/chat/ui/composer/use-take-back.d.ts +13 -0
- package/dist/chat/ui/composer/use-take-back.js +82 -0
- package/dist/chat/ui/diff/EditorEditDiff.js +4 -3
- package/dist/chat/ui/diff/LegacyDiffPool.js +3 -2
- package/dist/chat/ui/diff/LegacyEditDiff.js +3 -2
- package/dist/chat/ui/diff/LegacyUnifiedDiff.js +3 -2
- package/dist/chat/ui/diff/syntax-runtime.d.ts +2 -1
- package/dist/chat/ui/diff/syntax-runtime.js +9 -5
- package/dist/chat/ui/diff-syntax.d.ts +2 -2
- package/dist/chat/ui/diff-syntax.js +1 -2
- package/dist/chat/ui/diff-theme.js +10 -9
- package/dist/chat/ui/file-links.d.ts +1 -1
- package/dist/chat/ui/file-links.js +7 -5
- package/dist/chat/ui/highlighter.d.ts +27 -0
- package/dist/chat/ui/highlighter.js +151 -0
- package/dist/chat/ui/index.d.ts +67 -0
- package/dist/chat/ui/index.js +64 -0
- package/dist/chat/ui/intelligent-ui/UiReply.js +3 -2
- package/dist/chat/ui/intelligent-ui/renderers/content.js +3 -2
- package/dist/chat/ui/intelligent-ui/use-ui-links.js +2 -2
- package/dist/chat/ui/intelligent-ui/use-ui-queries.js +4 -3
- package/dist/chat/ui/markdown-blocks.d.ts +8 -0
- package/dist/chat/ui/markdown-blocks.js +260 -12
- package/dist/chat/ui/mermaid.d.ts +23 -0
- package/dist/chat/ui/mermaid.js +93 -0
- package/dist/chat/ui/rows/ForksRow.js +5 -3
- package/dist/chat/ui/rows/GeneratedImageRow.js +6 -4
- package/dist/chat/ui/rows/MessageRows.d.ts +4 -0
- package/dist/chat/ui/rows/MessageRows.js +36 -13
- package/dist/chat/ui/rows/Rows.js +3 -1
- package/dist/chat/ui/rows/SubagentRow.js +3 -2
- package/dist/chat/ui/rows/VisualRow.js +3 -2
- package/dist/chat/ui/rows/WorkRows.d.ts +6 -3
- package/dist/chat/ui/rows/WorkRows.js +57 -20
- package/dist/chat/ui/rows/WorkflowRow.js +3 -2
- package/dist/chat/ui/timestamp-format.d.ts +1 -0
- package/dist/chat/ui/timestamp-format.js +2 -0
- package/dist/chat/ui/use-diff-source-tokens.js +6 -3
- package/dist/chat/ui/visual-theme.js +3 -2
- package/dist/host.d.ts +9 -6
- package/dist/host.js +19 -8
- package/dist/index.d.ts +8 -0
- package/dist/index.js +9 -0
- package/dist/locales/en/agent-chat.d.ts +658 -0
- package/dist/locales/en/agent-chat.js +657 -0
- package/dist/locales/en/agent-prompts.d.ts +44 -0
- package/dist/locales/en/agent-prompts.js +43 -0
- package/dist/locales/en/agent-providers.d.ts +132 -0
- package/dist/locales/en/agent-providers.js +131 -0
- package/dist/locales/en/agent-usage.d.ts +132 -0
- package/dist/locales/en/agent-usage.js +131 -0
- package/dist/locales/nl/agent-chat.d.ts +658 -0
- package/dist/locales/nl/agent-chat.js +657 -0
- package/dist/locales/nl/agent-prompts.d.ts +44 -0
- package/dist/locales/nl/agent-prompts.js +43 -0
- package/dist/locales/nl/agent-providers.d.ts +132 -0
- package/dist/locales/nl/agent-providers.js +131 -0
- package/dist/locales/nl/agent-usage.d.ts +132 -0
- package/dist/locales/nl/agent-usage.js +131 -0
- package/dist/locales.js +9 -8
- package/dist/prompts/index.d.ts +11 -0
- package/dist/prompts/index.js +12 -0
- package/dist/prompts/logic/subjects.d.ts +2 -1
- package/dist/prompts/logic/subjects.js +6 -4
- package/dist/prompts/ui/ApprovalBody.js +6 -4
- package/dist/prompts/ui/PromptCard.js +5 -4
- package/dist/prompts/ui/PromptView.js +3 -2
- package/dist/prompts/ui/QuestionBody.js +3 -2
- package/dist/providers/AccountColors.js +2 -2
- package/dist/providers/AccountDetail.js +7 -6
- package/dist/providers/AccountVariables.js +3 -1
- package/dist/providers/AddAccountForm.js +3 -2
- package/dist/providers/ProvidersPane.js +4 -3
- package/dist/providers/account-actions.d.ts +4 -3
- package/dist/providers/account-actions.js +6 -7
- package/dist/providers/index.d.ts +16 -0
- package/dist/providers/index.js +17 -0
- package/dist/providers/provider-abilities.d.ts +2 -1
- package/dist/providers/provider-abilities.js +4 -0
- package/dist/state/chats.js +4 -2
- package/dist/state/index.d.ts +4 -0
- package/dist/state/index.js +5 -0
- package/dist/testing/index.d.ts +1 -0
- package/dist/testing/index.js +2 -0
- package/dist/{prompts/logic/prompts.fixtures.d.ts → testing/prompts.d.ts} +1 -1
- package/dist/theme.css +13 -3
- package/dist/usage/UsageBreakdown.js +3 -2
- package/dist/usage/UsageLimits.js +4 -3
- package/dist/usage/UsagePage.js +3 -2
- package/dist/usage/index.d.ts +15 -0
- package/dist/usage/index.js +15 -0
- package/package.json +53 -1031
- package/src/agents/AccountDot.tsx +3 -1
- package/src/agents/accounts.ts +6 -6
- package/src/chat/actions.ts +8 -5
- package/src/chat/bookmarks.ts +23 -14
- package/src/chat/chat-client.ts +8 -0
- package/src/chat/composer-send.ts +27 -0
- package/src/chat/forks.ts +27 -6
- package/src/chat/index.ts +98 -0
- package/src/chat/logic/embedded-code.ts +409 -0
- package/src/chat/logic/fork.ts +1 -0
- package/src/chat/logic/goal.ts +33 -0
- package/src/chat/logic/scrubber.ts +6 -5
- package/src/chat/logic/thought.ts +40 -0
- package/src/chat/logic/thread-cards.ts +2 -0
- package/src/chat/logic/timeline.ts +25 -1
- package/src/chat/logic/tool-catalog.ts +5 -1
- package/src/chat/logic/tools.ts +76 -0
- package/src/chat/preferences.ts +26 -3
- package/src/chat/reading-width.ts +39 -0
- package/src/chat/send-key.ts +38 -0
- package/src/chat/ui/BookmarkMarker.tsx +4 -2
- package/src/chat/ui/BookmarkSubmenu.tsx +4 -3
- package/src/chat/ui/ChatActivity.tsx +14 -10
- package/src/chat/ui/ChatReferenceChip.tsx +3 -4
- package/src/chat/ui/CodeBlock.tsx +33 -124
- package/src/chat/ui/Composer.tsx +162 -1324
- package/src/chat/ui/ComposerInput.tsx +5 -4
- package/src/chat/ui/EditDiff.tsx +3 -2
- package/src/chat/ui/GoalDock.tsx +104 -0
- package/src/chat/ui/LimitState.tsx +5 -3
- package/src/chat/ui/Markdown.tsx +113 -39
- package/src/chat/ui/MermaidBlock.tsx +119 -0
- package/src/chat/ui/MessageActions.tsx +13 -8
- package/src/chat/ui/Pickers.tsx +8 -7
- package/src/chat/ui/PromptComposer.tsx +4 -3
- package/src/chat/ui/RowTime.tsx +21 -0
- package/src/chat/ui/RunSettings.tsx +9 -4
- package/src/chat/ui/ShellCommand.tsx +134 -0
- package/src/chat/ui/SubagentInfo.tsx +4 -3
- package/src/chat/ui/SubagentStopButton.tsx +4 -3
- package/src/chat/ui/SubagentTimeline.tsx +5 -1
- package/src/chat/ui/Timeline.tsx +167 -105
- package/src/chat/ui/TimelineMenu.tsx +42 -7
- package/src/chat/ui/UnifiedDiff.tsx +3 -2
- package/src/chat/ui/VisualFrame.tsx +6 -7
- package/src/chat/ui/code-theme.ts +4 -4
- package/src/chat/ui/composer/ComposerControls.tsx +147 -0
- package/src/chat/ui/composer/ComposerMenu.tsx +138 -0
- package/src/chat/ui/composer/DraftParts.tsx +86 -0
- package/src/chat/ui/composer/QueuedMessages.tsx +64 -0
- package/src/chat/ui/composer/key-handler.ts +174 -0
- package/src/chat/ui/composer/keys.ts +32 -10
- package/src/chat/ui/composer/placeholder.ts +48 -0
- package/src/chat/ui/composer/use-chat-controls.ts +134 -0
- package/src/chat/ui/composer/use-compaction-offer.ts +25 -0
- package/src/chat/ui/composer/use-composer-draft.ts +71 -0
- package/src/chat/ui/composer/use-composer-menus.ts +289 -0
- package/src/chat/ui/composer/use-composer-submit.ts +163 -0
- package/src/chat/ui/composer/use-draft-files.ts +135 -0
- package/src/chat/ui/composer/use-notice.ts +28 -0
- package/src/chat/ui/composer/use-pending-requests.ts +23 -0
- package/src/chat/ui/composer/use-prompt-history.ts +76 -0
- package/src/chat/ui/composer/use-prompt-hold.ts +58 -0
- package/src/chat/ui/composer/use-take-back.ts +91 -0
- package/src/chat/ui/diff/DiffFallbackBoundary.tsx +2 -2
- package/src/chat/ui/diff/EditorEditDiff.tsx +4 -3
- package/src/chat/ui/diff/LegacyDiffPool.tsx +3 -2
- package/src/chat/ui/diff/LegacyEditDiff.tsx +3 -2
- package/src/chat/ui/diff/LegacyUnifiedDiff.tsx +3 -2
- package/src/chat/ui/diff/syntax-runtime.ts +10 -5
- package/src/chat/ui/diff-syntax.ts +2 -2
- package/src/chat/ui/diff-theme.ts +10 -9
- package/src/chat/ui/file-links.ts +7 -5
- package/src/chat/ui/highlighter.ts +185 -0
- package/src/chat/ui/index.ts +109 -0
- package/src/chat/ui/intelligent-ui/UiReply.tsx +5 -4
- package/src/chat/ui/intelligent-ui/renderers/content.tsx +3 -2
- package/src/chat/ui/intelligent-ui/use-ui-links.ts +2 -2
- package/src/chat/ui/intelligent-ui/use-ui-queries.ts +4 -3
- package/src/chat/ui/markdown-blocks.ts +300 -12
- package/src/chat/ui/mermaid.ts +118 -0
- package/src/chat/ui/rows/ForksRow.tsx +5 -3
- package/src/chat/ui/rows/GeneratedImageRow.tsx +6 -4
- package/src/chat/ui/rows/MessageRows.tsx +73 -18
- package/src/chat/ui/rows/Rows.tsx +14 -1
- package/src/chat/ui/rows/SubagentRow.tsx +3 -2
- package/src/chat/ui/rows/VisualRow.tsx +3 -2
- package/src/chat/ui/rows/WorkRows.tsx +148 -47
- package/src/chat/ui/rows/WorkflowRow.tsx +3 -2
- package/src/chat/ui/timestamp-format.ts +2 -0
- package/src/chat/ui/use-diff-source-tokens.ts +10 -4
- package/src/chat/ui/visual-theme.ts +3 -2
- package/src/host.ts +31 -15
- package/src/index.ts +49 -0
- package/src/locales/en/agent-chat.ts +657 -0
- package/src/locales/en/agent-prompts.ts +43 -0
- package/src/locales/en/agent-providers.ts +131 -0
- package/src/locales/en/agent-usage.ts +131 -0
- package/src/locales/nl/agent-chat.ts +657 -0
- package/src/locales/nl/agent-prompts.ts +43 -0
- package/src/locales/nl/agent-providers.ts +131 -0
- package/src/locales/nl/agent-usage.ts +132 -0
- package/src/locales.ts +9 -8
- package/src/prompts/index.ts +35 -0
- package/src/prompts/logic/subjects.ts +12 -4
- package/src/prompts/ui/ApprovalBody.tsx +6 -4
- package/src/prompts/ui/PromptCard.tsx +11 -4
- package/src/prompts/ui/PromptView.tsx +3 -2
- package/src/prompts/ui/QuestionBody.tsx +3 -2
- package/src/providers/AccountColors.tsx +2 -2
- package/src/providers/AccountDetail.tsx +13 -7
- package/src/providers/AccountVariables.tsx +3 -1
- package/src/providers/AddAccountForm.tsx +3 -2
- package/src/providers/ProvidersPane.tsx +4 -3
- package/src/providers/account-actions.ts +7 -7
- package/src/providers/index.ts +17 -0
- package/src/providers/provider-abilities.ts +6 -1
- package/src/state/chats.ts +4 -2
- package/src/state/index.ts +39 -0
- package/src/testing/index.ts +2 -0
- package/src/{prompts/logic/prompts.fixtures.ts → testing/prompts.ts} +1 -1
- package/src/theme.css +13 -3
- package/src/usage/UsageBreakdown.tsx +3 -2
- package/src/usage/UsageLimits.tsx +4 -3
- package/src/usage/UsagePage.tsx +3 -2
- package/src/usage/index.ts +16 -0
- package/dist/locales/en/agent-chat.json +0 -597
- package/dist/locales/en/agent-prompts.json +0 -43
- package/dist/locales/en/agent-providers.json +0 -131
- package/dist/locales/en/agent-usage.json +0 -131
- package/dist/locales/nl/agent-chat.json +0 -597
- package/dist/locales/nl/agent-prompts.json +0 -43
- package/dist/locales/nl/agent-providers.json +0 -131
- package/dist/locales/nl/agent-usage.json +0 -131
- package/src/locales/en/agent-chat.json +0 -597
- package/src/locales/en/agent-prompts.json +0 -43
- package/src/locales/en/agent-providers.json +0 -131
- package/src/locales/en/agent-usage.json +0 -131
- package/src/locales/nl/agent-chat.json +0 -597
- package/src/locales/nl/agent-prompts.json +0 -43
- package/src/locales/nl/agent-providers.json +0 -131
- package/src/locales/nl/agent-usage.json +0 -131
- /package/dist/{prompts/logic/prompts.fixtures.js → testing/prompts.js} +0 -0
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
export const SHELL_LANGUAGE = 'shellscript';
|
|
2
|
+
// Past this a command is drawn without looking for code inside it; the scan is linear, the highlighting is not.
|
|
3
|
+
export const EMBEDDED_CODE_LIMIT = 20_000;
|
|
4
|
+
const MAX_DEPTH = 3;
|
|
5
|
+
const SHELL = {
|
|
6
|
+
language: SHELL_LANGUAGE,
|
|
7
|
+
script: (flag) => /^-[a-zA-Z]*c[a-zA-Z]*$/.test(flag),
|
|
8
|
+
takesValue: new Set(['-o', '-O', '--rcfile', '--init-file'])
|
|
9
|
+
};
|
|
10
|
+
const PYTHON = { language: 'python', script: (flag) => flag === '-c', takesValue: new Set(['-W', '-X']) };
|
|
11
|
+
const NODE = {
|
|
12
|
+
language: 'javascript',
|
|
13
|
+
script: (flag) => ['-e', '--eval', '-p', '--print'].includes(flag),
|
|
14
|
+
takesValue: new Set(['-r', '--require', '--import', '--input-type', '--conditions', '-C'])
|
|
15
|
+
};
|
|
16
|
+
const BUN = { language: 'typescript', script: (flag) => ['-e', '--eval', '-p', '--print'].includes(flag), takesValue: NODE.takesValue };
|
|
17
|
+
const MYSQL = {
|
|
18
|
+
language: 'sql',
|
|
19
|
+
client: true,
|
|
20
|
+
script: (flag) => flag === '-e' || flag === '--execute',
|
|
21
|
+
takesValue: new Set(['-u', '-h', '-P', '-D', '-S', '--user', '--host', '--port', '--database'])
|
|
22
|
+
};
|
|
23
|
+
const INTERPRETERS = {
|
|
24
|
+
bash: SHELL,
|
|
25
|
+
sh: SHELL,
|
|
26
|
+
zsh: SHELL,
|
|
27
|
+
dash: SHELL,
|
|
28
|
+
ksh: SHELL,
|
|
29
|
+
python: PYTHON,
|
|
30
|
+
node: NODE,
|
|
31
|
+
bun: BUN,
|
|
32
|
+
psql: {
|
|
33
|
+
language: 'sql',
|
|
34
|
+
client: true,
|
|
35
|
+
script: (flag) => flag === '-c' || flag === '--command',
|
|
36
|
+
takesValue: new Set(['-d', '-h', '-p', '-U', '-f', '-v', '-o', '--dbname', '--host', '--port', '--username', '--file', '--set'])
|
|
37
|
+
},
|
|
38
|
+
mysql: MYSQL,
|
|
39
|
+
mariadb: MYSQL,
|
|
40
|
+
sqlite3: { language: 'sql', client: true, script: () => false, takesValue: new Set(['-cmd', '-separator', '-newline', '-nullvalue']), positional: 1 }
|
|
41
|
+
};
|
|
42
|
+
// Programs that run the next word as the command; their own options are skipped along with them.
|
|
43
|
+
const WRAPPERS = new Set(['sudo', 'env', 'exec', 'nohup', 'time', 'command', 'nice']);
|
|
44
|
+
// Tools that run a program of a project's environment as `<tool> run <program>`.
|
|
45
|
+
const RUNNERS = new Set(['uv', 'poetry', 'pipenv']);
|
|
46
|
+
// Where a heredoc written to a file goes, by the file's extension.
|
|
47
|
+
const EXTENSIONS = {
|
|
48
|
+
py: 'python',
|
|
49
|
+
js: 'javascript',
|
|
50
|
+
mjs: 'javascript',
|
|
51
|
+
cjs: 'javascript',
|
|
52
|
+
jsx: 'jsx',
|
|
53
|
+
ts: 'typescript',
|
|
54
|
+
mts: 'typescript',
|
|
55
|
+
tsx: 'tsx',
|
|
56
|
+
json: 'json',
|
|
57
|
+
sql: 'sql',
|
|
58
|
+
sh: SHELL_LANGUAGE,
|
|
59
|
+
bash: SHELL_LANGUAGE,
|
|
60
|
+
zsh: SHELL_LANGUAGE,
|
|
61
|
+
yml: 'yaml',
|
|
62
|
+
yaml: 'yaml',
|
|
63
|
+
md: 'markdown',
|
|
64
|
+
css: 'css',
|
|
65
|
+
html: 'html',
|
|
66
|
+
php: 'php',
|
|
67
|
+
xml: 'xml'
|
|
68
|
+
};
|
|
69
|
+
const SEPARATORS = ';&|()';
|
|
70
|
+
const BLANKS = ' \t\r';
|
|
71
|
+
/* The program a command name runs, as the table above knows it: `/usr/bin/python3.12` is `python`. */
|
|
72
|
+
function programOf(word) {
|
|
73
|
+
const name = word.slice(word.lastIndexOf('/') + 1);
|
|
74
|
+
return /^python\d*(\.\d+)?$/.test(name) ? 'python' : name;
|
|
75
|
+
}
|
|
76
|
+
/* Splits `source[from, to)` into simple commands the way a shell would, enough to find the code in them. */
|
|
77
|
+
function parseCommands(source, from, to) {
|
|
78
|
+
const commands = [];
|
|
79
|
+
let current = { words: [], redirects: [], heredocs: [] };
|
|
80
|
+
const pending = [];
|
|
81
|
+
const finish = () => {
|
|
82
|
+
if (current.words.length > 0 || current.redirects.length > 0) {
|
|
83
|
+
commands.push(current);
|
|
84
|
+
}
|
|
85
|
+
current = { words: [], redirects: [], heredocs: [] };
|
|
86
|
+
};
|
|
87
|
+
const skipBlanks = (index) => {
|
|
88
|
+
while (index < to && BLANKS.includes(source[index])) {
|
|
89
|
+
index++;
|
|
90
|
+
}
|
|
91
|
+
return index;
|
|
92
|
+
};
|
|
93
|
+
const readWord = (start) => {
|
|
94
|
+
let index = start;
|
|
95
|
+
let text = '';
|
|
96
|
+
let quoted = null;
|
|
97
|
+
let parts = 0;
|
|
98
|
+
let bare = true;
|
|
99
|
+
while (index < to) {
|
|
100
|
+
const char = source[index];
|
|
101
|
+
if (BLANKS.includes(char) || char === '\n' || SEPARATORS.includes(char) || char === '<' || char === '>') {
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
parts++;
|
|
105
|
+
if (char === "'") {
|
|
106
|
+
const close = source.indexOf("'", index + 1);
|
|
107
|
+
const end = close === -1 || close >= to ? to : close;
|
|
108
|
+
text += source.slice(index + 1, end);
|
|
109
|
+
quoted = { start: index + 1, end };
|
|
110
|
+
bare = false;
|
|
111
|
+
index = Math.min(end + 1, to);
|
|
112
|
+
}
|
|
113
|
+
else if (char === '"') {
|
|
114
|
+
let end = index + 1;
|
|
115
|
+
while (end < to && source[end] !== '"') {
|
|
116
|
+
if (source[end] === '\\' && end + 1 < to && '$`"\\\n'.includes(source[end + 1])) {
|
|
117
|
+
text += source[end + 1];
|
|
118
|
+
end += 2;
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
text += source[end];
|
|
122
|
+
end++;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
quoted = { start: index + 1, end };
|
|
126
|
+
bare = false;
|
|
127
|
+
index = Math.min(end + 1, to);
|
|
128
|
+
}
|
|
129
|
+
else if (char === '\\') {
|
|
130
|
+
text += source[index + 1] ?? '';
|
|
131
|
+
bare = false;
|
|
132
|
+
index += 2;
|
|
133
|
+
}
|
|
134
|
+
else if (char === '$' && source[index + 1] === '(') {
|
|
135
|
+
const end = closingParen(source, index + 1, to);
|
|
136
|
+
text += source.slice(index, end);
|
|
137
|
+
index = end;
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
text += char;
|
|
141
|
+
index++;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
const end = Math.min(index, to);
|
|
145
|
+
const body = bare ? { start, end } : parts === 1 && quoted !== null ? quoted : null;
|
|
146
|
+
return { start, end, text, body };
|
|
147
|
+
};
|
|
148
|
+
const readHeredoc = (start, delimiter, strip) => {
|
|
149
|
+
let index = start;
|
|
150
|
+
while (index < to) {
|
|
151
|
+
const newline = source.indexOf('\n', index);
|
|
152
|
+
const lineEnd = newline === -1 || newline >= to ? to : newline;
|
|
153
|
+
let line = source.slice(index, lineEnd).replace(/\r$/, '');
|
|
154
|
+
if (strip) {
|
|
155
|
+
line = line.replace(/^\t+/, '');
|
|
156
|
+
}
|
|
157
|
+
if (line === delimiter) {
|
|
158
|
+
return { body: { start, end: index }, next: Math.min(lineEnd + 1, to) };
|
|
159
|
+
}
|
|
160
|
+
index = lineEnd + 1;
|
|
161
|
+
}
|
|
162
|
+
return { body: { start, end: to }, next: to };
|
|
163
|
+
};
|
|
164
|
+
let index = from;
|
|
165
|
+
while (index < to) {
|
|
166
|
+
const char = source[index];
|
|
167
|
+
if (char === '\n') {
|
|
168
|
+
index++;
|
|
169
|
+
for (const heredoc of pending) {
|
|
170
|
+
const { body, next } = readHeredoc(index, heredoc.delimiter, heredoc.strip);
|
|
171
|
+
heredoc.command.heredocs.push(body);
|
|
172
|
+
index = next;
|
|
173
|
+
}
|
|
174
|
+
pending.length = 0;
|
|
175
|
+
finish();
|
|
176
|
+
}
|
|
177
|
+
else if (BLANKS.includes(char)) {
|
|
178
|
+
index++;
|
|
179
|
+
}
|
|
180
|
+
else if (char === '\\' && source[index + 1] === '\n') {
|
|
181
|
+
index += 2;
|
|
182
|
+
}
|
|
183
|
+
else if (char === '#') {
|
|
184
|
+
const newline = source.indexOf('\n', index);
|
|
185
|
+
index = newline === -1 || newline >= to ? to : newline;
|
|
186
|
+
}
|
|
187
|
+
else if (source.startsWith('<<<', index)) {
|
|
188
|
+
index = readWord(skipBlanks(index + 3)).end;
|
|
189
|
+
}
|
|
190
|
+
else if (source.startsWith('<<', index)) {
|
|
191
|
+
index += 2;
|
|
192
|
+
const strip = source[index] === '-';
|
|
193
|
+
const word = readWord(skipBlanks(strip ? index + 1 : index));
|
|
194
|
+
if (word.text !== '') {
|
|
195
|
+
pending.push({ command: current, delimiter: word.text, strip });
|
|
196
|
+
}
|
|
197
|
+
index = word.end;
|
|
198
|
+
}
|
|
199
|
+
else if (char === '<' || char === '>' || (char === '&' && source[index + 1] === '>')) {
|
|
200
|
+
let end = index + 1;
|
|
201
|
+
while (end < to && '<>&|'.includes(source[end])) {
|
|
202
|
+
end++;
|
|
203
|
+
}
|
|
204
|
+
const op = source.slice(index, end);
|
|
205
|
+
const target = readWord(skipBlanks(end));
|
|
206
|
+
current.redirects.push({ op, target });
|
|
207
|
+
index = Math.max(target.end, end);
|
|
208
|
+
}
|
|
209
|
+
else if (SEPARATORS.includes(char)) {
|
|
210
|
+
index++;
|
|
211
|
+
finish();
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
const word = readWord(index);
|
|
215
|
+
// The `2` of `2>&1` is a file descriptor, not an argument.
|
|
216
|
+
const descriptor = /^\d+$/.test(word.text) && (source[word.end] === '>' || source[word.end] === '<');
|
|
217
|
+
if (!descriptor) {
|
|
218
|
+
current.words.push(word);
|
|
219
|
+
}
|
|
220
|
+
index = Math.max(word.end, index + 1);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
finish();
|
|
224
|
+
return commands;
|
|
225
|
+
}
|
|
226
|
+
/* Where the `)` that closes the `(` at `open` is, past it; quotes inside do not count. */
|
|
227
|
+
function closingParen(source, open, to) {
|
|
228
|
+
let depth = 0;
|
|
229
|
+
let quote = null;
|
|
230
|
+
for (let index = open; index < to; index++) {
|
|
231
|
+
const char = source[index];
|
|
232
|
+
if (quote !== null) {
|
|
233
|
+
if (char === quote) {
|
|
234
|
+
quote = null;
|
|
235
|
+
}
|
|
236
|
+
else if (char === '\\' && quote === '"') {
|
|
237
|
+
index++;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
else if (char === "'" || char === '"') {
|
|
241
|
+
quote = char;
|
|
242
|
+
}
|
|
243
|
+
else if (char === '(') {
|
|
244
|
+
depth++;
|
|
245
|
+
}
|
|
246
|
+
else if (char === ')') {
|
|
247
|
+
depth--;
|
|
248
|
+
if (depth === 0) {
|
|
249
|
+
return index + 1;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
return to;
|
|
254
|
+
}
|
|
255
|
+
/* The index of the word a command runs, past variable assignments, wrappers and runners; -1 for none. */
|
|
256
|
+
function programIndex(words) {
|
|
257
|
+
let index = 0;
|
|
258
|
+
while (index < words.length) {
|
|
259
|
+
const text = words[index].text;
|
|
260
|
+
if (/^[A-Za-z_][A-Za-z0-9_]*=/.test(text)) {
|
|
261
|
+
index++;
|
|
262
|
+
}
|
|
263
|
+
else if (WRAPPERS.has(programOf(text))) {
|
|
264
|
+
index++;
|
|
265
|
+
while (index < words.length && words[index].text.startsWith('-')) {
|
|
266
|
+
index++;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
else if (RUNNERS.has(programOf(text)) && words[index + 1]?.text === 'run') {
|
|
270
|
+
index += 2;
|
|
271
|
+
}
|
|
272
|
+
else {
|
|
273
|
+
return index;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
return -1;
|
|
277
|
+
}
|
|
278
|
+
/* The code of one simple command: an inline script, or a heredoc fed to an interpreter or written to a file. */
|
|
279
|
+
function codeOf(command) {
|
|
280
|
+
const found = [];
|
|
281
|
+
const at = programIndex(command.words);
|
|
282
|
+
const program = at === -1 ? '' : programOf(command.words[at].text);
|
|
283
|
+
const interpreter = INTERPRETERS[program];
|
|
284
|
+
let readsFile = false;
|
|
285
|
+
if (interpreter !== undefined) {
|
|
286
|
+
let positional = 0;
|
|
287
|
+
for (let index = at + 1; index < command.words.length; index++) {
|
|
288
|
+
const word = command.words[index];
|
|
289
|
+
if (word.text.startsWith('-') && word.text !== '-') {
|
|
290
|
+
if (interpreter.script(word.text)) {
|
|
291
|
+
const script = command.words[index + 1];
|
|
292
|
+
if (script?.body) {
|
|
293
|
+
found.push({ ...script.body, language: interpreter.language });
|
|
294
|
+
}
|
|
295
|
+
readsFile = true;
|
|
296
|
+
index++;
|
|
297
|
+
}
|
|
298
|
+
else if (interpreter.takesValue?.has(word.text)) {
|
|
299
|
+
index++;
|
|
300
|
+
}
|
|
301
|
+
else if (interpreter === PYTHON && word.text === '-m') {
|
|
302
|
+
readsFile = true;
|
|
303
|
+
break;
|
|
304
|
+
}
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
if (interpreter.client !== true) {
|
|
308
|
+
// A script file; whatever follows is its own arguments.
|
|
309
|
+
readsFile = word.text !== '-';
|
|
310
|
+
break;
|
|
311
|
+
}
|
|
312
|
+
if (positional === interpreter.positional && word.body !== null) {
|
|
313
|
+
found.push({ ...word.body, language: interpreter.language });
|
|
314
|
+
}
|
|
315
|
+
positional++;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
const heredocLanguage = interpreter !== undefined && (!readsFile || interpreter.client === true) ? interpreter.language : writtenLanguage(command, program);
|
|
319
|
+
if (heredocLanguage !== null) {
|
|
320
|
+
for (const body of command.heredocs) {
|
|
321
|
+
found.push({ ...body, language: heredocLanguage });
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
return found;
|
|
325
|
+
}
|
|
326
|
+
/* The language of the file a `cat > file` or `tee file` writes, by its extension; null when nothing says. */
|
|
327
|
+
function writtenLanguage(command, program) {
|
|
328
|
+
const targets = command.redirects.filter(({ op }) => op === '>' || op === '>>' || op === '>|').map(({ target }) => target.text);
|
|
329
|
+
if (program === 'tee') {
|
|
330
|
+
targets.push(...command.words
|
|
331
|
+
.slice(programIndex(command.words) + 1)
|
|
332
|
+
.map(({ text }) => text)
|
|
333
|
+
.filter((text) => !text.startsWith('-')));
|
|
334
|
+
}
|
|
335
|
+
for (const target of targets) {
|
|
336
|
+
const dot = target.lastIndexOf('.');
|
|
337
|
+
const language = dot === -1 ? undefined : EXTENSIONS[target.slice(dot + 1).toLowerCase()];
|
|
338
|
+
if (language !== undefined) {
|
|
339
|
+
return language;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
return null;
|
|
343
|
+
}
|
|
344
|
+
function collect(source, from, to, depth, into) {
|
|
345
|
+
for (const command of parseCommands(source, from, to)) {
|
|
346
|
+
for (const code of codeOf(command)) {
|
|
347
|
+
if (code.end <= code.start) {
|
|
348
|
+
continue;
|
|
349
|
+
}
|
|
350
|
+
into.push(code);
|
|
351
|
+
if (code.language === SHELL_LANGUAGE && depth < MAX_DEPTH) {
|
|
352
|
+
collect(source, code.start, code.end, depth + 1, into);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
/*
|
|
358
|
+
* The code inside a shell command that is in another language, or a shell script of its own: the
|
|
359
|
+
* script of `bash -lc '…'`, `python3 -c '…'`, `node -e '…'`, `psql -c '…'`, and a heredoc fed to an
|
|
360
|
+
* interpreter or written to a file of a known kind. Sorted by start, a stretch before the ones inside
|
|
361
|
+
* it, so drawing them in order leaves the innermost language on top. A heuristic over a small shell
|
|
362
|
+
* grammar, not a parser: what it does not recognize stays plain shell.
|
|
363
|
+
*/
|
|
364
|
+
export function embeddedCode(command) {
|
|
365
|
+
if (command.length > EMBEDDED_CODE_LIMIT) {
|
|
366
|
+
return [];
|
|
367
|
+
}
|
|
368
|
+
const found = [];
|
|
369
|
+
collect(command, 0, command.length, 0, found);
|
|
370
|
+
return found.sort((left, right) => left.start - right.start || right.end - left.end);
|
|
371
|
+
}
|
package/dist/chat/logic/fork.js
CHANGED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ChatGoal, ChatGoalAction, ChatInfo, ProviderCapabilities } from '@adecore/agent-contracts';
|
|
2
|
+
export interface GoalView {
|
|
3
|
+
goal: ChatGoal;
|
|
4
|
+
actions: ChatGoalAction[];
|
|
5
|
+
}
|
|
6
|
+
export declare function goalView(info: Pick<ChatInfo, 'goal' | 'activeTurnId'>, goals: ProviderCapabilities['goals'], canAct: boolean): GoalView | null;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* The row above the composer while the chat has a goal. A CLI with calls for goals pauses, resumes and
|
|
3
|
+
* clears it at any time; one that only reads `/goal` clears it with a turn of its own, so only between
|
|
4
|
+
* turns, and a goal it already met clears itself.
|
|
5
|
+
*/
|
|
6
|
+
export function goalView(info, goals, canAct) {
|
|
7
|
+
const goal = info.goal;
|
|
8
|
+
if (goal === undefined) {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
if (!canAct || goals === undefined) {
|
|
12
|
+
return { goal, actions: [] };
|
|
13
|
+
}
|
|
14
|
+
if (goals === 'command') {
|
|
15
|
+
return { goal, actions: goal.status === 'complete' || info.activeTurnId !== null ? [] : ['clear'] };
|
|
16
|
+
}
|
|
17
|
+
const actions = [];
|
|
18
|
+
if (goal.status === 'active') {
|
|
19
|
+
actions.push('pause');
|
|
20
|
+
}
|
|
21
|
+
else if (goal.status !== 'complete') {
|
|
22
|
+
actions.push('resume');
|
|
23
|
+
}
|
|
24
|
+
actions.push('clear');
|
|
25
|
+
return { goal, actions };
|
|
26
|
+
}
|
|
@@ -48,4 +48,4 @@ export interface MessageRange {
|
|
|
48
48
|
export declare function messagesInView({ starts, scrollTop, visibleHeight }: ReadingPosition): MessageRange | null;
|
|
49
49
|
export declare function slotInView(slot: TickSlot, range: MessageRange | null): boolean;
|
|
50
50
|
export declare function stepMessage(starts: readonly number[], eligible: (index: number) => boolean, scrollTop: number, direction: -1 | 1): number | null;
|
|
51
|
-
export declare function threadPaddingLeft(width: number, strip: boolean): number;
|
|
51
|
+
export declare function threadPaddingLeft(width: number, strip: boolean, measure?: number): number;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
import { DEFAULT_READING_WIDTH, readingMeasure } from '../reading-width.js';
|
|
1
2
|
/* With fewer messages than this the thread is short enough to scroll through, and the strip is noise. */
|
|
2
3
|
export const SCRUBBER_MIN_TICKS = 3;
|
|
3
4
|
export const TICK_HEIGHT_PX = 2;
|
|
4
5
|
const PITCH_PX = 8;
|
|
5
6
|
const MIN_PITCH_PX = 4;
|
|
6
|
-
/* The
|
|
7
|
-
const CONTENT_MAX_WIDTH_PX = 768;
|
|
7
|
+
/* The scroller's padding around the thread's content column (`px-4`). */
|
|
8
8
|
const THREAD_PADDING_PX = 16;
|
|
9
9
|
export const STRIP_WIDTH_PX = 24;
|
|
10
10
|
/* The strip's distance from the view's left edge, and the air it keeps between itself and the thread's text. */
|
|
@@ -199,13 +199,14 @@ export function stepMessage(starts, eligible, scrollTop, direction) {
|
|
|
199
199
|
/*
|
|
200
200
|
* The left padding of the thread in a view that draws the strip at its left edge. A wide view keeps
|
|
201
201
|
* the usual padding, since its centered column already clears the strip; a narrower one pads until
|
|
202
|
-
* the column's text starts clear of it, so nothing is ever drawn under the strip.
|
|
202
|
+
* the column's text starts clear of it, so nothing is ever drawn under the strip. `measure` is the
|
|
203
|
+
* widest the content column runs (`readingMeasure`).
|
|
203
204
|
*/
|
|
204
|
-
export function threadPaddingLeft(width, strip) {
|
|
205
|
+
export function threadPaddingLeft(width, strip, measure = readingMeasure(DEFAULT_READING_WIDTH)) {
|
|
205
206
|
if (!strip) {
|
|
206
207
|
return THREAD_PADDING_PX;
|
|
207
208
|
}
|
|
208
209
|
// The column's margin is half of what the padding leaves, so the padding counts the clearance twice.
|
|
209
|
-
const padding = 2 * STRIP_CLEARANCE_PX +
|
|
210
|
+
const padding = 2 * STRIP_CLEARANCE_PX + measure + THREAD_PADDING_PX - Math.floor(width);
|
|
210
211
|
return Math.min(STRIP_CLEARANCE_PX, Math.max(THREAD_PADDING_PX, padding));
|
|
211
212
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { stripMarkdown } from './timeline-copy.js';
|
|
2
|
+
// A line that is nothing but bold, the way a model titles a stretch of its reasoning.
|
|
3
|
+
const HEADING_LINE = /^[ \t]*\*\*([^*\n]+?)\*\*[ \t]*$/;
|
|
4
|
+
const LIST_MARKER = /^[ \t]*(?:[-*+]|\d+[.)])[ \t]+/gm;
|
|
5
|
+
// A sentence ends on a stop with white space after it, so `a.ts` and `1.5` stay whole.
|
|
6
|
+
const SENTENCE_END = /[.!?]["'”’)\]]*(?=\s)/;
|
|
7
|
+
/* The thought as one line of plain text: the marks gone and every run of white space one space. */
|
|
8
|
+
export function flattenThought(text) {
|
|
9
|
+
return stripMarkdown(text.replace(LIST_MARKER, '')).replace(/\s+/g, ' ').trim();
|
|
10
|
+
}
|
|
11
|
+
function headingOf(line) {
|
|
12
|
+
return HEADING_LINE.exec(line)?.[1] ?? null;
|
|
13
|
+
}
|
|
14
|
+
/* What a folded thought shows on its one line: the heading it opens with, or the whole thought flattened for the row to cut off. */
|
|
15
|
+
export function thoughtPreview(text) {
|
|
16
|
+
const heading = headingOf(text.trimStart().split('\n', 1)[0] ?? '');
|
|
17
|
+
return flattenThought(heading ?? text);
|
|
18
|
+
}
|
|
19
|
+
/*
|
|
20
|
+
* What the model is thinking about now, in a line: the heading of the last stretch it titled, which
|
|
21
|
+
* is the one it is on, or the first sentence of a thought that titles nothing.
|
|
22
|
+
*/
|
|
23
|
+
export function thoughtLine(text) {
|
|
24
|
+
const headings = text
|
|
25
|
+
.split('\n')
|
|
26
|
+
.map(headingOf)
|
|
27
|
+
.filter((heading) => heading !== null);
|
|
28
|
+
const last = headings.at(-1);
|
|
29
|
+
if (last !== undefined) {
|
|
30
|
+
return flattenThought(last);
|
|
31
|
+
}
|
|
32
|
+
const flat = flattenThought(text);
|
|
33
|
+
const end = SENTENCE_END.exec(flat);
|
|
34
|
+
return end === null ? flat : flat.slice(0, end.index + end[0].length);
|
|
35
|
+
}
|
|
@@ -276,6 +276,26 @@ function lastAssistantRow(rows) {
|
|
|
276
276
|
}
|
|
277
277
|
return null;
|
|
278
278
|
}
|
|
279
|
+
/*
|
|
280
|
+
* The rows of a running turn with its latest thought over the call it led to, so what the model
|
|
281
|
+
* found does not scroll away behind the next tool line. Nothing when that call follows the thought
|
|
282
|
+
* directly, where the thought's own row already shows it, or when a newer thought came after the call.
|
|
283
|
+
*/
|
|
284
|
+
function withLiveThought(rows, items) {
|
|
285
|
+
const thoughtAt = items.findLastIndex((item) => item.kind === 'thinking' && parentOf(item) === null);
|
|
286
|
+
const thought = items[thoughtAt];
|
|
287
|
+
if (thought?.kind !== 'thinking' || thought.text === '') {
|
|
288
|
+
return rows;
|
|
289
|
+
}
|
|
290
|
+
const after = new Set(items.slice(thoughtAt + 1).map((item) => item.id));
|
|
291
|
+
const index = rows.findIndex((row) => row.kind === 'work-live' && after.has(row.tool.id));
|
|
292
|
+
const call = rows[index];
|
|
293
|
+
const previous = rows[index - 1];
|
|
294
|
+
if (call?.kind !== 'work-live' || (previous?.kind === 'thinking' && previous.id === thought.id)) {
|
|
295
|
+
return rows;
|
|
296
|
+
}
|
|
297
|
+
return [...rows.slice(0, index), { kind: 'live-thought', id: `live-thought-${thought.id}`, item: thought, at: call.tool.createdAt }, ...rows.slice(index)];
|
|
298
|
+
}
|
|
279
299
|
/* Items grouped by turn, in order of first appearance; runs of turnless items keep their place. */
|
|
280
300
|
function chunksByTurn(items) {
|
|
281
301
|
const chunks = [];
|
|
@@ -319,7 +339,8 @@ export function deriveTimelineRows(items, options) {
|
|
|
319
339
|
}
|
|
320
340
|
const active = turnId === options.activeTurnId || turn?.state === 'running';
|
|
321
341
|
if (!turn || active) {
|
|
322
|
-
|
|
342
|
+
const live = rowsForItems(conversation, options, children);
|
|
343
|
+
rows.push(...(active ? withLiveThought(live, conversation) : live));
|
|
323
344
|
if (active) {
|
|
324
345
|
rows.push({ kind: 'working', id: `working-${turnId}`, startedAt: turn?.createdAt ?? Date.now() });
|
|
325
346
|
}
|
|
@@ -6,6 +6,7 @@ function entry(icon, summary, rest = {}) {
|
|
|
6
6
|
grouped: true,
|
|
7
7
|
changesFiles: false,
|
|
8
8
|
readsImage: false,
|
|
9
|
+
shell: false,
|
|
9
10
|
...rest
|
|
10
11
|
};
|
|
11
12
|
}
|
|
@@ -25,7 +26,8 @@ export const TOOL_CATALOG = {
|
|
|
25
26
|
NotebookEdit: entry(SquarePen, ['file_path', 'notebook_path']),
|
|
26
27
|
// Codex reports a patch as the paths it touches; its own input carries no path of its own.
|
|
27
28
|
ApplyPatch: entry(SquarePen, ['summary'], { grouped: false, changesFiles: true }),
|
|
28
|
-
|
|
29
|
+
// Codex's command executions arrive under this name too.
|
|
30
|
+
Bash: entry(Terminal, ['description', 'command'], { shell: true }),
|
|
29
31
|
Grep: entry(Search, ['pattern']),
|
|
30
32
|
Glob: entry(FolderSearch, ['pattern']),
|
|
31
33
|
WebFetch: entry(Globe, ['url', 'query']),
|
|
@@ -5,6 +5,20 @@ export interface FileChange {
|
|
|
5
5
|
after: string;
|
|
6
6
|
}
|
|
7
7
|
export declare function toolSummary(name: string, input: unknown): string;
|
|
8
|
+
export declare function toolSummaryIsPath(name: string, input: unknown): boolean;
|
|
9
|
+
export declare function shellCommand(name: string, input: unknown): string | null;
|
|
10
|
+
export interface ToolCallLines {
|
|
11
|
+
readonly command: string | null;
|
|
12
|
+
readonly args: ReadonlyArray<readonly [key: string, value: string]>;
|
|
13
|
+
readonly text: string | null;
|
|
14
|
+
}
|
|
15
|
+
export declare function toolCallLines(name: string, input: unknown): ToolCallLines;
|
|
16
|
+
export declare function exitCodeOf(output: string): {
|
|
17
|
+
code: number | null;
|
|
18
|
+
rest: string;
|
|
19
|
+
};
|
|
20
|
+
export declare function hasOutput(output: string | null): output is string;
|
|
21
|
+
export declare function hasToolBody(tool: ChatToolItem): boolean;
|
|
8
22
|
export declare function readImagePath(name: string, input: unknown): string | null;
|
|
9
23
|
export declare function fileChanges(name: string, input: unknown): FileChange[];
|
|
10
24
|
export declare function isFileChange(name: string): boolean;
|
package/dist/chat/logic/tools.js
CHANGED
|
@@ -22,6 +22,63 @@ export function toolSummary(name, input) {
|
|
|
22
22
|
}
|
|
23
23
|
return '';
|
|
24
24
|
}
|
|
25
|
+
// The input keys whose value is a file a call is about.
|
|
26
|
+
const PATH_KEYS = new Set(['file_path', 'notebook_path']);
|
|
27
|
+
/* Whether the one line of a call is the path of a file, which a row cuts in the middle so the name stays. */
|
|
28
|
+
export function toolSummaryIsPath(name, input) {
|
|
29
|
+
const known = toolEntry(name);
|
|
30
|
+
if (known === undefined || !isRecord(input)) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
const key = known.summary.find((candidate) => str(input[candidate]) !== null);
|
|
34
|
+
return key !== undefined && PATH_KEYS.has(key);
|
|
35
|
+
}
|
|
36
|
+
/* The command of a shell call, as it was run; null for any other call or one without a command. */
|
|
37
|
+
export function shellCommand(name, input) {
|
|
38
|
+
if (toolEntry(name)?.shell !== true || !isRecord(input)) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
const command = str(input.command);
|
|
42
|
+
return command === null || command.trim() === '' ? null : command;
|
|
43
|
+
}
|
|
44
|
+
// A shell call's description is already the detail of its row.
|
|
45
|
+
const SHELL_SHOWN = new Set(['command', 'description']);
|
|
46
|
+
function valueText(value) {
|
|
47
|
+
// An empty string can be the point of a call (clearing a field), so it shows as one.
|
|
48
|
+
return typeof value === 'string' && value !== '' ? value : (JSON.stringify(value) ?? String(value));
|
|
49
|
+
}
|
|
50
|
+
/* The input of a call as lines: a shell command as itself, every other key as `key value`, null included. */
|
|
51
|
+
export function toolCallLines(name, input) {
|
|
52
|
+
if (!isRecord(input)) {
|
|
53
|
+
const text = input === null || input === undefined || input === '' ? null : valueText(input);
|
|
54
|
+
return { command: null, args: [], text };
|
|
55
|
+
}
|
|
56
|
+
const command = shellCommand(name, input);
|
|
57
|
+
const args = Object.entries(input)
|
|
58
|
+
.filter(([key, value]) => value !== undefined && !(command !== null && SHELL_SHOWN.has(key)))
|
|
59
|
+
.map(([key, value]) => [key, valueText(value)]);
|
|
60
|
+
return { command, args, text: null };
|
|
61
|
+
}
|
|
62
|
+
/* A failed call's output with the exit code its tool put on the first line ("Exit code 1"), as Claude Code's shell does. */
|
|
63
|
+
export function exitCodeOf(output) {
|
|
64
|
+
const match = /^Exit code (-?\d+)[ \t]*(?:\r?\n|$)/.exec(output);
|
|
65
|
+
if (match === null || Number(match[1]) === 0) {
|
|
66
|
+
return { code: null, rest: output };
|
|
67
|
+
}
|
|
68
|
+
return { code: Number(match[1]), rest: output.slice(match[0].length) };
|
|
69
|
+
}
|
|
70
|
+
/* Whether an output says anything; whitespace alone does not. */
|
|
71
|
+
export function hasOutput(output) {
|
|
72
|
+
return output !== null && output.trim() !== '';
|
|
73
|
+
}
|
|
74
|
+
/* Whether opening a call shows anything: a diff, the input it was given or what it answered. */
|
|
75
|
+
export function hasToolBody(tool) {
|
|
76
|
+
if (hasFileChanges(tool) || hasOutput(tool.output)) {
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
const lines = toolCallLines(tool.name, tool.input);
|
|
80
|
+
return lines.command !== null || lines.args.length > 0 || lines.text !== null;
|
|
81
|
+
}
|
|
25
82
|
// What `GET /fs/file` will serve; a path with another suffix is not worth asking the host about.
|
|
26
83
|
const IMAGE_SUFFIXES = ['.png', '.jpg', '.jpeg', '.gif', '.webp', '.svg'];
|
|
27
84
|
/* The image file a tool call looked at, so the row can draw it; null for every other call. */
|