ethagent 3.0.1 → 3.1.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 +6 -1
- package/package.json +3 -1
- package/src/app/FirstRun.tsx +1 -24
- package/src/app/firstRunConfig.ts +26 -0
- package/src/auth/openaiOAuth/landingPage.ts +2 -11
- package/src/chat/ChatScreen.tsx +32 -117
- package/src/chat/MessageList.tsx +18 -260
- package/src/chat/chatEnvironment.ts +16 -0
- package/src/chat/chatTurnContext.ts +50 -0
- package/src/chat/chatTurnOrchestrator.ts +5 -112
- package/src/chat/chatTurnRows.ts +64 -0
- package/src/chat/commands.ts +3 -178
- package/src/chat/continuityEditReview.ts +42 -0
- package/src/chat/input/ChatInput.tsx +10 -144
- package/src/chat/input/chatInputHelpers.ts +62 -0
- package/src/chat/input/inputRendering.tsx +93 -0
- package/src/chat/messageMarkdown.ts +220 -0
- package/src/chat/messageRows.ts +43 -0
- package/src/chat/planImplementation.ts +62 -0
- package/src/chat/slashCommandHandlers.ts +165 -0
- package/src/chat/slashCommandViews.ts +120 -0
- package/src/cli/main.tsx +7 -0
- package/src/identity/continuity/challenges.ts +123 -0
- package/src/identity/continuity/envelope.ts +49 -1484
- package/src/identity/continuity/envelopeCreate.ts +322 -0
- package/src/identity/continuity/envelopeCrypto.ts +182 -0
- package/src/identity/continuity/envelopeParse.ts +441 -0
- package/src/identity/continuity/envelopeTypes.ts +204 -0
- package/src/identity/continuity/envelopeVersion.ts +1 -0
- package/src/identity/continuity/payloadNormalization.ts +183 -0
- package/src/identity/continuity/publicSkills.ts +5 -5
- package/src/identity/continuity/skills/loadSkills.ts +12 -69
- package/src/identity/continuity/skills/skillPaths.ts +76 -0
- package/src/identity/continuity/skillsNormalization.ts +119 -0
- package/src/identity/continuity/snapshotToken.ts +28 -0
- package/src/identity/hub/continuity/completion.ts +67 -0
- package/src/identity/hub/continuity/effects.ts +5 -62
- package/src/identity/hub/profile/effects.ts +6 -170
- package/src/identity/hub/profile/operatorSave.ts +202 -0
- package/src/identity/registry/erc8004/metadata.ts +31 -23
- package/src/identity/wallet/browserWallet/html.ts +1 -57
- package/src/identity/wallet/browserWallet/walletPageSource.ts +85 -0
- package/src/identity/wallet/page/controller.ts +1 -1
- package/src/identity/wallet/page/errorView.ts +122 -0
- package/src/identity/wallet/page/view.ts +3 -114
- package/src/mcp/manager.ts +8 -66
- package/src/mcp/managerHelpers.ts +70 -0
- package/src/models/ModelPicker.tsx +69 -889
- package/src/models/huggingface.ts +20 -137
- package/src/models/huggingfaceStorage.ts +136 -0
- package/src/models/llamacpp.ts +37 -303
- package/src/models/llamacppCommands.ts +44 -0
- package/src/models/llamacppConfig.ts +34 -0
- package/src/models/llamacppDiscovery.ts +176 -0
- package/src/models/llamacppOutput.ts +65 -0
- package/src/models/modelPickerCatalogFlow.ts +56 -0
- package/src/models/modelPickerCredentials.ts +166 -0
- package/src/models/modelPickerData.ts +41 -0
- package/src/models/modelPickerDisplay.tsx +132 -0
- package/src/models/modelPickerHfFlow.ts +192 -0
- package/src/models/modelPickerLocalRunnerFlow.ts +115 -0
- package/src/models/modelPickerTypes.ts +69 -0
- package/src/models/modelPickerUninstallFlow.ts +48 -0
- package/src/models/modelPickerViewHelpers.ts +174 -0
- package/src/providers/openai-chat.ts +5 -124
- package/src/providers/openaiChatWire.ts +124 -0
- package/src/runtime/providerTurn.ts +38 -0
- package/src/runtime/textToolParser.ts +161 -0
- package/src/runtime/toolIntent.ts +1 -1
- package/src/runtime/turn.ts +43 -499
- package/src/runtime/turnNudges.ts +223 -0
- package/src/runtime/turnTypes.ts +86 -0
- package/src/ui/terminalTitle.ts +30 -0
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import type { Provider } from '../providers/contracts.js'
|
|
2
|
+
import {
|
|
3
|
+
looksLikeToolStateClaim,
|
|
4
|
+
} from './toolClaimGuards.js'
|
|
5
|
+
import type { ContinuationNudgeReason, ExecutedToolUse } from './turnTypes.js'
|
|
6
|
+
|
|
7
|
+
export const MAX_CONTINUATION_NUDGES = 3
|
|
8
|
+
export const MAX_TOOL_USES_PER_TURN = 25
|
|
9
|
+
|
|
10
|
+
const CONTINUATION_NUDGE_TEXT =
|
|
11
|
+
'Continue with the task. Use the appropriate tools to proceed.'
|
|
12
|
+
|
|
13
|
+
const TOOL_CAPABILITY_NUDGE_TEXT =
|
|
14
|
+
'You do have access to the provided tools in this environment. Continue by making the appropriate tool call; do not ask the user to run commands or paste command output.'
|
|
15
|
+
|
|
16
|
+
const TOOL_STATE_CLAIM_NUDGE_TEXT =
|
|
17
|
+
'Do not claim that files, directories, or workspace state changed unless you have executed the appropriate tool. Call the tool now.'
|
|
18
|
+
|
|
19
|
+
export const TOOL_STATE_CLAIM_REPAIR_NUDGE_TEXT =
|
|
20
|
+
'The previous assistant response claimed workspace state without executing a tool. '
|
|
21
|
+
+ 'Treat that claim as unreliable. '
|
|
22
|
+
+ TOOL_STATE_CLAIM_NUDGE_TEXT
|
|
23
|
+
|
|
24
|
+
export const TOOL_PROTOCOL_FAKE_NUDGE_TEXT =
|
|
25
|
+
'The previous response printed tool names or a tool menu instead of calling a tool. Tool names are not text output. Make exactly one native tool call now.'
|
|
26
|
+
|
|
27
|
+
export const TOOL_DELEGATION_NUDGE_TEXT =
|
|
28
|
+
'Do not ask the user to run native tools. You have access to the tools in this environment. Make exactly one native tool call now.'
|
|
29
|
+
|
|
30
|
+
export const TOOL_BUDGET_NUDGE_TEXT =
|
|
31
|
+
'You have reached the tool-call budget for this turn. Do not call any more tools. Produce your final answer now using only what you already know from earlier tool results.'
|
|
32
|
+
|
|
33
|
+
const PRIVATE_CONTINUITY_NUDGE_TEXT =
|
|
34
|
+
'SOUL.md and MEMORY.md are existing private identity-vault scaffold files. Do not search workspace folders, read plans/, create files, or overwrite them. If exact private text is needed for a surgical removal or targeted replacement, call read_private_continuity_file with {"file":"MEMORY.md"} or {"file":"SOUL.md"}. If the user wants private continuity changed, call propose_private_continuity_edit. For memory/preferences use {"file":"MEMORY.md","appendToSection":"Durable User Preferences","appendText":"- User preference or memory note."}. For persona use {"file":"SOUL.md","appendToSection":"Persona","appendText":"- Persona or standing behavior note."}.'
|
|
35
|
+
|
|
36
|
+
const PRIVATE_CONTINUITY_REPAIR_NUDGE_TEXT =
|
|
37
|
+
'The previous propose_private_continuity_edit call had invalid or missing input. Retry the same native tool now with complete arguments. Do not answer in prose and do not search for markdown files. For memory/preferences use {"file":"MEMORY.md","appendToSection":"Durable User Preferences","appendText":"- User preference or memory note."}. For persona use {"file":"SOUL.md","appendToSection":"Persona","appendText":"- Persona or standing behavior note."}.'
|
|
38
|
+
|
|
39
|
+
const WRITE_FILE_REPAIR_NUDGE_TEXT =
|
|
40
|
+
'The previous write_file call was rejected because the arguments were missing or malformed. Retry the same native tool now with a JSON object (not a JSON string) shaped exactly like {"path":"relative/path.ext","content":"...complete file contents..."}. Both fields are required and must be non-empty. Do not answer in prose.'
|
|
41
|
+
|
|
42
|
+
export const REASONING_ONLY_NUDGE_TEXT =
|
|
43
|
+
'You produced private reasoning but no user-visible answer. Answer the user now in visible text. Do not continue only with reasoning.'
|
|
44
|
+
|
|
45
|
+
type RepairNudge = {
|
|
46
|
+
text: string
|
|
47
|
+
reason: ContinuationNudgeReason
|
|
48
|
+
failureMessage: string
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function nextToolResultRepairNudge(
|
|
52
|
+
provider: Pick<Provider, 'id' | 'supportsTools'>,
|
|
53
|
+
completedTools: ExecutedToolUse[],
|
|
54
|
+
): RepairNudge | null {
|
|
55
|
+
if (!provider.supportsTools) return null
|
|
56
|
+
const failedPrivateEdit = completedTools.some(completed =>
|
|
57
|
+
completed.name === 'propose_private_continuity_edit'
|
|
58
|
+
&& !completed.result.ok
|
|
59
|
+
&& completed.result.summary === 'propose_private_continuity_edit rejected input',
|
|
60
|
+
)
|
|
61
|
+
if (failedPrivateEdit) {
|
|
62
|
+
return {
|
|
63
|
+
text: PRIVATE_CONTINUITY_REPAIR_NUDGE_TEXT,
|
|
64
|
+
reason: 'private_continuity_tool_repair',
|
|
65
|
+
failureMessage: 'Model called propose_private_continuity_edit with invalid input after corrective nudges',
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const failedWriteFile = completedTools.some(completed =>
|
|
70
|
+
completed.name === 'write_file'
|
|
71
|
+
&& !completed.result.ok
|
|
72
|
+
&& completed.result.summary === 'write_file rejected input',
|
|
73
|
+
)
|
|
74
|
+
if (failedWriteFile) {
|
|
75
|
+
return {
|
|
76
|
+
text: WRITE_FILE_REPAIR_NUDGE_TEXT,
|
|
77
|
+
reason: 'write_file_repair',
|
|
78
|
+
failureMessage: 'Model called write_file with invalid input after corrective nudges',
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const failedWorkspacePrivateRead = completedTools.some(completed =>
|
|
83
|
+
completed.name === 'read_file'
|
|
84
|
+
&& !completed.result.ok
|
|
85
|
+
&& /read_private_continuity_file/.test(completed.result.content),
|
|
86
|
+
)
|
|
87
|
+
if (failedWorkspacePrivateRead) {
|
|
88
|
+
return {
|
|
89
|
+
text: 'The previous read_file call targeted private identity continuity markdown. Retry now with read_private_continuity_file and complete input such as {"file":"MEMORY.md"} or {"file":"SOUL.md"}. Do not search workspace folders.',
|
|
90
|
+
reason: 'private_continuity_tool_repair',
|
|
91
|
+
failureMessage: 'Model kept reading private continuity files via read_file after corrective nudges',
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return null
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function nextNudge(
|
|
98
|
+
provider: Pick<Provider, 'supportsTools'>,
|
|
99
|
+
assistantText: string,
|
|
100
|
+
): { text: string; reason: ContinuationNudgeReason; keepAssistantContext: boolean } | null {
|
|
101
|
+
if (provider.supportsTools && looksLikePrivateContinuityWorkspaceCreationIntent(assistantText)) {
|
|
102
|
+
return {
|
|
103
|
+
text: PRIVATE_CONTINUITY_NUDGE_TEXT,
|
|
104
|
+
reason: 'private_continuity_tool',
|
|
105
|
+
keepAssistantContext: false,
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (provider.supportsTools && looksLikeToolCapabilityConfusion(assistantText)) {
|
|
109
|
+
return {
|
|
110
|
+
text: TOOL_CAPABILITY_NUDGE_TEXT,
|
|
111
|
+
reason: 'tool_capability',
|
|
112
|
+
keepAssistantContext: false,
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (looksLikeContinuationIntent(assistantText)) {
|
|
116
|
+
return {
|
|
117
|
+
text: CONTINUATION_NUDGE_TEXT,
|
|
118
|
+
reason: 'continuation',
|
|
119
|
+
keepAssistantContext: true,
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return null
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export function looksLikePrivateContinuityWorkspaceCreationIntent(text: string): boolean {
|
|
126
|
+
const lower = text.toLowerCase()
|
|
127
|
+
if (!/\b(soul|memory)\.md\b/.test(lower)) return false
|
|
128
|
+
return [
|
|
129
|
+
/\b(create|write|make|generate|scaffold|overwrite|replace|locate|find|search|read|check|inspect)\b.{0,100}\b(soul|memory)\.md\b/,
|
|
130
|
+
/\b(soul|memory)\.md\b.{0,100}\b(create|write|make|generate|scaffold|overwrite|replace|locate|find|search|read|check|inspect)\b/,
|
|
131
|
+
/\bplans?[\\/][^\s]*\b(soul|memory)\b/,
|
|
132
|
+
].some(pattern => pattern.test(lower))
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export function looksLikeToolCapabilityConfusion(text: string): boolean {
|
|
136
|
+
const lower = text.toLowerCase()
|
|
137
|
+
const limitation =
|
|
138
|
+
/\b(i (do not|don't|cannot|can't) (have|access|run|execute|inspect|read|list|use)|no direct access|unable to|not able to|currently operating under|limitations and restrictions)\b/
|
|
139
|
+
const toolTask =
|
|
140
|
+
/\b(run|execute|shell command|command output|local machine|terminal|files?|directories|workspace|paste|share the contents)\b/
|
|
141
|
+
return limitation.test(lower) && toolTask.test(lower)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function looksLikeToolStateClaimWithoutTool(text: string): boolean {
|
|
145
|
+
return looksLikeToolStateClaim(text)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function looksLikeFakeToolProtocolText(text: string): boolean {
|
|
149
|
+
const lower = text.toLowerCase()
|
|
150
|
+
if (!lower.trim()) return false
|
|
151
|
+
|
|
152
|
+
const toolNames = new Set(
|
|
153
|
+
[...lower.matchAll(/\b(change_directory|edit_file|propose_private_continuity_edit|read_private_continuity_file|list_directory|read_file|run_bash|write_file|delete_file)\b/g)]
|
|
154
|
+
.map(match => match[1]),
|
|
155
|
+
)
|
|
156
|
+
if (toolNames.size < 2) return false
|
|
157
|
+
|
|
158
|
+
const codeBlock = /```|code\s*(?:-|:)?\s*block/.test(lower)
|
|
159
|
+
const toolMenu = /\b(available tools|tool functions|functions are|tools are|native tools)\b/.test(lower)
|
|
160
|
+
const actionIntent = /\b(let'?s|let me|i'?ll|i will|first|next)\b.{0,80}\b(list|read|inspect|execute|run|change|edit|write)\b/.test(lower)
|
|
161
|
+
const commaSeparatedTools = /(?:change_directory|edit_file|propose_private_continuity_edit|read_private_continuity_file|list_directory|read_file|run_bash|write_file|delete_file)(?:\s*,\s*|\s+){1,}/.test(lower)
|
|
162
|
+
|
|
163
|
+
return (codeBlock || toolMenu || actionIntent) && commaSeparatedTools
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export function looksLikeToolDelegationText(text: string): boolean {
|
|
167
|
+
const lower = text.toLowerCase()
|
|
168
|
+
if (!lower.trim()) return false
|
|
169
|
+
|
|
170
|
+
const toolName = '(?:change_directory|edit_file|propose_private_continuity_edit|read_private_continuity_file|list_directory|read_file|run_bash|write_file|delete_file)'
|
|
171
|
+
if (!new RegExp(`\\b${toolName}\\b`).test(lower)) return false
|
|
172
|
+
|
|
173
|
+
const directToolRef = `(?:\`?${toolName}\`?|the\\s+\`?${toolName}\`?\\s+tool)`
|
|
174
|
+
const action = '(?:run|execute|call|use|invoke)'
|
|
175
|
+
const askPrefix = "(?:please|kindly|can you|could you|would you|you can|you should|you need to|you'll need to|try to|go ahead and)"
|
|
176
|
+
const selfPrefix = "(?:i'll|i will|let me|let's|we should|we need to|before proceeding|first|next|now)"
|
|
177
|
+
|
|
178
|
+
const askUser = new RegExp(`\\b${askPrefix}\\b.{0,100}\\b${action}\\b.{0,50}${directToolRef}`).test(lower)
|
|
179
|
+
const selfIntent = new RegExp(`\\b${selfPrefix}\\b.{0,100}\\b${action}\\b.{0,50}${directToolRef}`).test(lower)
|
|
180
|
+
const commandForm = new RegExp(`\\b${action}\\s+${directToolRef}\\b`).test(lower)
|
|
181
|
+
&& /\b(please|before proceeding|first|next|now|to proceed)\b/.test(lower)
|
|
182
|
+
const asksForOutput = new RegExp(`${directToolRef}.{0,120}\\b(output|result|files?|directory structure|working directory)\\b`).test(lower)
|
|
183
|
+
&& /\b(please|you|run|paste|share|provide)\b/.test(lower)
|
|
184
|
+
|
|
185
|
+
return askUser || selfIntent || commandForm || asksForOutput
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export function looksLikeContinuationIntent(text: string): boolean {
|
|
189
|
+
const lower = text.toLowerCase()
|
|
190
|
+
|
|
191
|
+
const completionMarkers =
|
|
192
|
+
/\b(done|finished|completed|complete|summary|that's all|that is all|all set|hope this helps|let me know if)\b/
|
|
193
|
+
if (completionMarkers.test(lower)) return false
|
|
194
|
+
|
|
195
|
+
const actionVerbs =
|
|
196
|
+
'(do|create|write|edit|update|fix|implement|add|run|check|make|build|set up|go|proceed|begin)'
|
|
197
|
+
|
|
198
|
+
const shortMessage = lower.length < 80
|
|
199
|
+
|
|
200
|
+
const patterns: RegExp[] = [
|
|
201
|
+
new RegExp(
|
|
202
|
+
`\\bso now (i|let me|we) (need to|have to|should|must|will) ${actionVerbs}\\b`,
|
|
203
|
+
),
|
|
204
|
+
new RegExp(`\\bnow i('ll| will) ${actionVerbs}\\b`),
|
|
205
|
+
new RegExp(
|
|
206
|
+
`\\blet me (go ahead and |now )?${actionVerbs}\\b`,
|
|
207
|
+
),
|
|
208
|
+
new RegExp(`\\btime to ${actionVerbs}\\b`),
|
|
209
|
+
]
|
|
210
|
+
|
|
211
|
+
if (shortMessage) {
|
|
212
|
+
patterns.push(
|
|
213
|
+
new RegExp(
|
|
214
|
+
`\\bi('ll| will| need to| have to| must) (now )?${actionVerbs}\\b`,
|
|
215
|
+
),
|
|
216
|
+
new RegExp(
|
|
217
|
+
`\\bnext,?\\s+(i('ll| will)|let me|i need to) ${actionVerbs}\\b`,
|
|
218
|
+
),
|
|
219
|
+
)
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return patterns.some(re => re.test(lower))
|
|
223
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type { Message, Provider, ProviderRetryStreamEvent } from '../providers/contracts.js'
|
|
2
|
+
import type { ToolResult } from '../tools/contracts.js'
|
|
3
|
+
|
|
4
|
+
export type ProviderTurnEvent =
|
|
5
|
+
| { type: 'text'; delta: string }
|
|
6
|
+
| { type: 'thinking'; delta: string }
|
|
7
|
+
| { type: 'thinking_end' }
|
|
8
|
+
| ProviderRetryStreamEvent
|
|
9
|
+
| { type: 'tool_use_start'; id: string; name: string }
|
|
10
|
+
| { type: 'tool_use_delta'; id: string; delta: string }
|
|
11
|
+
| { type: 'tool_use_stop'; id: string; name: string; input: Record<string, unknown> }
|
|
12
|
+
| { type: 'done'; stopReason?: TurnStopReason }
|
|
13
|
+
| { type: 'error'; message: string }
|
|
14
|
+
| { type: 'cancelled' }
|
|
15
|
+
|
|
16
|
+
export type TurnStopReason = 'end_turn' | 'tool_use' | 'max_tokens' | 'stop_sequence' | 'unknown'
|
|
17
|
+
|
|
18
|
+
export type ContinuationNudgeReason =
|
|
19
|
+
| 'continuation'
|
|
20
|
+
| 'tool_capability'
|
|
21
|
+
| 'tool_state_claim'
|
|
22
|
+
| 'tool_protocol_fake'
|
|
23
|
+
| 'tool_delegation'
|
|
24
|
+
| 'tool_budget'
|
|
25
|
+
| 'private_continuity_tool'
|
|
26
|
+
| 'private_continuity_tool_repair'
|
|
27
|
+
| 'write_file_repair'
|
|
28
|
+
| 'reasoning_only'
|
|
29
|
+
|
|
30
|
+
export type TurnEvent =
|
|
31
|
+
| { type: 'iteration_start'; index: number }
|
|
32
|
+
| { type: 'text'; delta: string }
|
|
33
|
+
| { type: 'thinking'; delta: string }
|
|
34
|
+
| { type: 'thinking_end' }
|
|
35
|
+
| ProviderRetryStreamEvent
|
|
36
|
+
| { type: 'tool_use_start'; id: string; name: string }
|
|
37
|
+
| { type: 'tool_use_delta'; id: string; delta: string }
|
|
38
|
+
| {
|
|
39
|
+
type: 'tool_use_stop'
|
|
40
|
+
id: string
|
|
41
|
+
name: string
|
|
42
|
+
input: Record<string, unknown>
|
|
43
|
+
}
|
|
44
|
+
| { type: 'assistant_message_committed'; text: string }
|
|
45
|
+
| {
|
|
46
|
+
type: 'tool_executed'
|
|
47
|
+
id: string
|
|
48
|
+
name: string
|
|
49
|
+
input: Record<string, unknown>
|
|
50
|
+
result: ToolResult
|
|
51
|
+
cwd: string
|
|
52
|
+
}
|
|
53
|
+
| { type: 'continuation_nudge'; attempt: number; reason: ContinuationNudgeReason }
|
|
54
|
+
| { type: 'local_tool_recovery' }
|
|
55
|
+
| { type: 'error'; message: string; discardAssistant?: boolean }
|
|
56
|
+
| { type: 'cancelled' }
|
|
57
|
+
| { type: 'done'; finishedNormally: boolean; stopReason?: TurnStopReason }
|
|
58
|
+
|
|
59
|
+
export type PendingToolUse = {
|
|
60
|
+
id: string
|
|
61
|
+
name: string
|
|
62
|
+
input: Record<string, unknown>
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export type ExecutedToolUse = {
|
|
66
|
+
id: string
|
|
67
|
+
name: string
|
|
68
|
+
input: Record<string, unknown>
|
|
69
|
+
result: ToolResult
|
|
70
|
+
cwd: string
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export type ToolBatchRunner = (
|
|
74
|
+
pendingToolUses: PendingToolUse[],
|
|
75
|
+
) => Promise<{ cancelled: boolean; completedTools: ExecutedToolUse[] }>
|
|
76
|
+
|
|
77
|
+
export type RebuildMessages = () => Message[] | Promise<Message[]>
|
|
78
|
+
|
|
79
|
+
export type RuntimeTurnParams = {
|
|
80
|
+
provider: Provider
|
|
81
|
+
signal: AbortSignal
|
|
82
|
+
initialMessages: Message[]
|
|
83
|
+
rebuildMessages: RebuildMessages
|
|
84
|
+
runToolBatch: ToolBatchRunner
|
|
85
|
+
maxContinuationNudges?: number
|
|
86
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export const TITLE_STATIC = 'ethagent'
|
|
2
|
+
export const TITLE_ANIMATION_FRAMES = [
|
|
3
|
+
'ethagent ⠋',
|
|
4
|
+
'ethagent ⠙',
|
|
5
|
+
'ethagent ⠹',
|
|
6
|
+
'ethagent ⠸',
|
|
7
|
+
'ethagent ⠼',
|
|
8
|
+
'ethagent ⠴',
|
|
9
|
+
'ethagent ⠦',
|
|
10
|
+
'ethagent ⠧',
|
|
11
|
+
'ethagent ⠇',
|
|
12
|
+
'ethagent ⠏',
|
|
13
|
+
] as const
|
|
14
|
+
export const TITLE_ANIMATION_INTERVAL_MS = 80
|
|
15
|
+
|
|
16
|
+
export function setTerminalTitle(title: string): void {
|
|
17
|
+
const clean = title.replace(/[\x00-\x1f]/g, '')
|
|
18
|
+
if (process.platform === 'win32') {
|
|
19
|
+
process.title = clean
|
|
20
|
+
}
|
|
21
|
+
if (process.stdout.isTTY) {
|
|
22
|
+
process.stdout.write(`\x1b]0;${clean}\x07`)
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function clearTerminalTitle(): void {
|
|
27
|
+
if (process.stdout.isTTY) {
|
|
28
|
+
process.stdout.write('\x1b]0;\x07')
|
|
29
|
+
}
|
|
30
|
+
}
|