@swarmclawai/swarmclaw 1.2.8 → 1.3.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 +39 -6
- package/package.json +2 -2
- package/src/app/agents/[id]/page.tsx +1 -18
- package/src/app/api/activity/route.ts +9 -23
- package/src/app/api/agents/route.ts +17 -1
- package/src/app/api/agents/thread-route.test.ts +0 -1
- package/src/app/api/approvals/route.test.ts +6 -22
- package/src/app/api/approvals/route.ts +13 -5
- package/src/app/api/connectors/route.ts +2 -2
- package/src/app/api/credentials/[id]/route.ts +2 -0
- package/src/app/api/credentials/route.ts +4 -1
- package/src/app/api/goals/[id]/route.ts +28 -0
- package/src/app/api/goals/route.ts +33 -0
- package/src/app/api/portability/export/route.ts +8 -0
- package/src/app/api/portability/import/route.test.ts +80 -0
- package/src/app/api/portability/import/route.ts +28 -0
- package/src/app/api/protocols/templates/[id]/route.ts +2 -1
- package/src/app/api/protocols/templates/route.ts +2 -1
- package/src/app/api/settings/route.ts +13 -2
- package/src/app/api/wallets/[id]/route.ts +15 -157
- package/src/app/api/wallets/generate/route.ts +22 -0
- package/src/app/api/wallets/route.test.ts +147 -0
- package/src/app/api/wallets/route.ts +13 -95
- package/src/app/autonomy/page.tsx +2 -57
- package/src/app/home/page.tsx +3 -0
- package/src/app/protocols/page.tsx +2 -21
- package/src/app/settings/page.tsx +0 -9
- package/src/app/wallets/page.tsx +105 -5
- package/src/cli/index.js +32 -33
- package/src/cli/spec.js +26 -27
- package/src/components/agents/agent-sheet.tsx +2 -40
- package/src/components/agents/inspector-panel.tsx +0 -83
- package/src/components/chat/chat-card.tsx +0 -31
- package/src/components/chat/message-bubble.tsx +1 -108
- package/src/components/connectors/connector-sheet.tsx +25 -1
- package/src/components/layout/sidebar-rail.tsx +6 -10
- package/src/components/projects/project-detail.tsx +3 -35
- package/src/components/projects/tabs/overview-tab.tsx +3 -59
- package/src/components/projects/tabs/work-tab.tsx +7 -77
- package/src/components/protocols/structured-session-launcher.tsx +1 -22
- package/src/components/shared/connector-platform-icon.tsx +1 -0
- package/src/components/tasks/task-card.tsx +4 -34
- package/src/components/tasks/task-sheet.tsx +6 -36
- package/src/components/wallets/wallet-list.tsx +150 -0
- package/src/lib/app/navigation.test.ts +0 -13
- package/src/lib/app/navigation.ts +2 -7
- package/src/lib/app/view-constants.ts +14 -19
- package/src/lib/server/activity/activity-log.ts +16 -1
- package/src/lib/server/agents/agent-service.ts +24 -11
- package/src/lib/server/agents/agent-thread-session.ts +0 -1
- package/src/lib/server/agents/delegation-advisory.test.ts +0 -1
- package/src/lib/server/agents/delegation-jobs.test.ts +0 -69
- package/src/lib/server/agents/delegation-jobs.ts +0 -25
- package/src/lib/server/agents/main-agent-loop.ts +1 -49
- package/src/lib/server/agents/subagent-runtime.ts +0 -1
- package/src/lib/server/approval-match.ts +14 -85
- package/src/lib/server/approvals/approval-hooks.ts +81 -0
- package/src/lib/server/approvals.test.ts +6 -6
- package/src/lib/server/approvals.ts +11 -6
- package/src/lib/server/autonomy/supervisor-reflection.test.ts +0 -1
- package/src/lib/server/builtin-extensions.ts +0 -2
- package/src/lib/server/capability-router.test.ts +0 -2
- package/src/lib/server/chat-execution/chat-execution-tool-events.test.ts +14 -14
- package/src/lib/server/chat-execution/chat-execution-types.ts +0 -2
- package/src/lib/server/chat-execution/chat-execution-utils.ts +0 -2
- package/src/lib/server/chat-execution/chat-streaming-utils.ts +2 -30
- package/src/lib/server/chat-execution/chat-turn-finalization.ts +1 -36
- package/src/lib/server/chat-execution/chat-turn-preparation.ts +2 -22
- package/src/lib/server/chat-execution/iteration-event-handler.ts +0 -24
- package/src/lib/server/chat-execution/message-classifier.test.ts +0 -45
- package/src/lib/server/chat-execution/message-classifier.ts +1 -16
- package/src/lib/server/chat-execution/prompt-builder.test.ts +0 -1
- package/src/lib/server/chat-execution/prompt-builder.ts +0 -30
- package/src/lib/server/chat-execution/prompt-sections.ts +0 -1
- package/src/lib/server/chat-execution/situational-awareness.test.ts +2 -73
- package/src/lib/server/chat-execution/situational-awareness.ts +4 -38
- package/src/lib/server/chat-execution/stream-agent-chat.test.ts +8 -123
- package/src/lib/server/chat-execution/stream-agent-chat.ts +1 -5
- package/src/lib/server/chat-execution/stream-continuation.test.ts +4 -52
- package/src/lib/server/chat-execution/stream-continuation.ts +6 -48
- package/src/lib/server/chatrooms/session-mailbox.ts +0 -10
- package/src/lib/server/chats/chat-session-service.ts +3 -5
- package/src/lib/server/connectors/connector-inbound.ts +0 -1
- package/src/lib/server/connectors/connector-lifecycle.ts +19 -3
- package/src/lib/server/connectors/connector-service.ts +39 -9
- package/src/lib/server/connectors/swarmdock-bidding.ts +74 -0
- package/src/lib/server/connectors/swarmdock-payloads.test.ts +85 -0
- package/src/lib/server/connectors/swarmdock-secret.test.ts +128 -0
- package/src/lib/server/connectors/swarmdock-secret.ts +152 -0
- package/src/lib/server/connectors/swarmdock-tasks.ts +127 -0
- package/src/lib/server/connectors/swarmdock.ts +285 -0
- package/src/lib/server/execution-brief.test.ts +2 -25
- package/src/lib/server/execution-brief.ts +30 -35
- package/src/lib/server/execution-engine/task-attempt.ts +0 -1
- package/src/lib/server/goals/goal-repository.ts +19 -0
- package/src/lib/server/goals/goal-service.ts +143 -0
- package/src/lib/server/persistence/storage-context.ts +0 -5
- package/src/lib/server/portability/export.ts +109 -0
- package/src/lib/server/portability/import.ts +159 -0
- package/src/lib/server/protocols/protocol-normalization.ts +0 -4
- package/src/lib/server/protocols/protocol-queries.ts +0 -6
- package/src/lib/server/protocols/protocol-run-lifecycle.ts +4 -32
- package/src/lib/server/protocols/protocol-service.ts +0 -1
- package/src/lib/server/protocols/protocol-step-helpers.ts +0 -4
- package/src/lib/server/protocols/protocol-step-processors.ts +0 -6
- package/src/lib/server/protocols/protocol-swarm.ts +0 -2
- package/src/lib/server/protocols/protocol-types.ts +0 -2
- package/src/lib/server/provider-health.ts +0 -9
- package/src/lib/server/runtime/daemon-state/core.ts +0 -9
- package/src/lib/server/runtime/daemon-state.test.ts +0 -35
- package/src/lib/server/runtime/heartbeat-service.ts +3 -23
- package/src/lib/server/runtime/queue/core.ts +11 -33
- package/src/lib/server/runtime/runtime-storage-write-paths.test.ts +6 -6
- package/src/lib/server/runtime/scheduler.ts +0 -13
- package/src/lib/server/runtime/session-run-manager/drain.ts +0 -24
- package/src/lib/server/runtime/session-run-manager/enqueue.ts +0 -1
- package/src/lib/server/runtime/session-run-manager/queries.ts +0 -1
- package/src/lib/server/runtime/session-run-manager/recovery.ts +0 -1
- package/src/lib/server/runtime/session-run-manager.test.ts +0 -28
- package/src/lib/server/session-tools/crud.ts +0 -14
- package/src/lib/server/session-tools/delegate.ts +0 -4
- package/src/lib/server/session-tools/index.ts +0 -4
- package/src/lib/server/session-tools/team-context.ts +0 -3
- package/src/lib/server/storage-normalization.ts +13 -0
- package/src/lib/server/storage.ts +75 -45
- package/src/lib/server/tasks/task-checkout.ts +59 -0
- package/src/lib/server/tasks/task-lifecycle.ts +2 -0
- package/src/lib/server/tasks/task-route-service.ts +4 -26
- package/src/lib/server/tasks/task-service.ts +0 -7
- package/src/lib/server/tool-aliases.ts +0 -1
- package/src/lib/server/tool-capability-policy-advanced.test.ts +4 -4
- package/src/lib/server/tool-capability-policy.ts +0 -2
- package/src/lib/server/tool-planning.ts +0 -12
- package/src/lib/server/universal-tool-access.ts +0 -1
- package/src/lib/server/usage/cost-rollup.ts +124 -0
- package/src/lib/server/usage/usage-repository.ts +6 -0
- package/src/lib/server/wallets/wallet-crypto.ts +33 -0
- package/src/lib/server/wallets/wallet-repository.ts +24 -0
- package/src/lib/server/wallets/wallet-service.ts +119 -0
- package/src/lib/server/working-state/extraction.ts +8 -42
- package/src/lib/server/working-state/normalization.ts +10 -103
- package/src/lib/server/working-state/service.ts +12 -21
- package/src/lib/strip-internal-metadata.test.ts +1 -1
- package/src/lib/strip-internal-metadata.ts +1 -1
- package/src/lib/tool-definitions.ts +0 -1
- package/src/lib/validation/schemas.ts +36 -32
- package/src/lib/validation/server-schemas.ts +35 -0
- package/src/stores/slices/data-slice.ts +5 -1
- package/src/stores/slices/ui-slice.ts +0 -4
- package/src/types/agent.ts +10 -84
- package/src/types/app-settings.ts +6 -2
- package/src/types/approval.ts +3 -2
- package/src/types/connector.ts +1 -0
- package/src/types/goal.ts +30 -0
- package/src/types/index.ts +2 -1
- package/src/types/message.ts +0 -1
- package/src/types/misc.ts +2 -4
- package/src/types/protocol.ts +0 -2
- package/src/types/run.ts +0 -3
- package/src/types/session.ts +1 -51
- package/src/types/swarmdock.ts +29 -0
- package/src/types/task.ts +9 -3
- package/src/types/working-state.ts +2 -9
- package/src/views/settings/section-runtime-loop.tsx +0 -14
- package/src/app/api/canvas/[sessionId]/route.ts +0 -35
- package/src/app/api/missions/[id]/actions/route.ts +0 -31
- package/src/app/api/missions/[id]/events/route.ts +0 -14
- package/src/app/api/missions/[id]/route.ts +0 -10
- package/src/app/api/missions/route.test.ts +0 -244
- package/src/app/api/missions/route.ts +0 -57
- package/src/app/api/wallets/[id]/approve/route.ts +0 -79
- package/src/app/api/wallets/[id]/balance-history/route.ts +0 -18
- package/src/app/api/wallets/[id]/send/route.ts +0 -113
- package/src/app/api/wallets/[id]/transactions/route.ts +0 -18
- package/src/app/missions/[id]/page.tsx +0 -3
- package/src/app/missions/page.tsx +0 -685
- package/src/components/canvas/canvas-panel.tsx +0 -267
- package/src/components/wallets/wallet-approval-dialog.tsx +0 -107
- package/src/components/wallets/wallet-panel.tsx +0 -1010
- package/src/components/wallets/wallet-section.tsx +0 -260
- package/src/features/missions/queries.ts +0 -23
- package/src/lib/canvas-content.test.ts +0 -360
- package/src/lib/canvas-content.ts +0 -198
- package/src/lib/server/canvas-content.test.ts +0 -32
- package/src/lib/server/canvas-content.ts +0 -6
- package/src/lib/server/ethereum.ts +0 -591
- package/src/lib/server/evm-swap.ts +0 -476
- package/src/lib/server/missions/mission-intent.test.ts +0 -63
- package/src/lib/server/missions/mission-intent.ts +0 -569
- package/src/lib/server/missions/mission-repository.ts +0 -74
- package/src/lib/server/missions/mission-service/actions.ts +0 -6
- package/src/lib/server/missions/mission-service/bindings.ts +0 -9
- package/src/lib/server/missions/mission-service/context.ts +0 -4
- package/src/lib/server/missions/mission-service/core.ts +0 -2271
- package/src/lib/server/missions/mission-service/queries.ts +0 -12
- package/src/lib/server/missions/mission-service/recovery.ts +0 -5
- package/src/lib/server/missions/mission-service/ticks.ts +0 -9
- package/src/lib/server/missions/mission-service.test.ts +0 -888
- package/src/lib/server/missions/mission-service.ts +0 -6
- package/src/lib/server/session-tools/canvas.ts +0 -105
- package/src/lib/server/session-tools/wallet-tool.test.ts +0 -150
- package/src/lib/server/session-tools/wallet.ts +0 -1287
- package/src/lib/server/solana.ts +0 -327
- package/src/lib/server/wallet/wallet-execution.test.ts +0 -198
- package/src/lib/server/wallet/wallet-portfolio.test.ts +0 -98
- package/src/lib/server/wallet/wallet-portfolio.ts +0 -772
- package/src/lib/server/wallet/wallet-service.test.ts +0 -81
- package/src/lib/server/wallet/wallet-service.ts +0 -225
- package/src/lib/wallet/wallet-transactions.test.ts +0 -75
- package/src/lib/wallet/wallet-transactions.ts +0 -43
- package/src/lib/wallet/wallet.test.ts +0 -333
- package/src/lib/wallet/wallet.ts +0 -183
- package/src/types/mission.ts +0 -185
- package/src/views/settings/section-wallets.tsx +0 -35
|
@@ -2,17 +2,15 @@ import { listAgentIncidents } from '@/lib/server/autonomy/supervisor-incident-re
|
|
|
2
2
|
import { listAgents } from '@/lib/server/agents/agent-repository'
|
|
3
3
|
import { loadChatrooms } from '@/lib/server/chatrooms/chatroom-repository'
|
|
4
4
|
import { loadConnectors } from '@/lib/server/connectors/connector-repository'
|
|
5
|
-
import { loadMission } from '@/lib/server/missions/mission-repository'
|
|
6
5
|
import { loadSchedules } from '@/lib/server/schedules/schedule-repository'
|
|
7
6
|
import { loadTasks } from '@/lib/server/tasks/task-repository'
|
|
8
7
|
import { loadUsage } from '@/lib/server/usage/usage-repository'
|
|
9
8
|
import { listPersistedRuns } from '@/lib/server/runtime/run-ledger'
|
|
10
|
-
import type { BoardTask,
|
|
9
|
+
import type { BoardTask, Schedule, SupervisorIncident, SessionRunRecord } from '@/types'
|
|
11
10
|
|
|
12
11
|
export interface SituationalAwarenessInput {
|
|
13
12
|
agentId: string
|
|
14
13
|
sessionId: string
|
|
15
|
-
missionId?: string | null
|
|
16
14
|
}
|
|
17
15
|
|
|
18
16
|
/** Pre-loaded data passed to the pure formatter. Exported for testing. */
|
|
@@ -21,7 +19,6 @@ export interface SituationalAwarenessData {
|
|
|
21
19
|
schedules: Schedule[]
|
|
22
20
|
failedRuns: SessionRunRecord[]
|
|
23
21
|
incidents: SupervisorIncident[]
|
|
24
|
-
mission: Mission | null
|
|
25
22
|
now: number
|
|
26
23
|
}
|
|
27
24
|
|
|
@@ -170,33 +167,11 @@ function buildFailuresSection(failures: FailureEntry[], now: number): string | n
|
|
|
170
167
|
return lines.join('\n')
|
|
171
168
|
}
|
|
172
169
|
|
|
173
|
-
export function buildGoalAncestrySection(missionId: string | null | undefined): string | null {
|
|
174
|
-
if (!missionId) return null
|
|
175
|
-
const chain: string[] = []
|
|
176
|
-
let currentId: string | null = missionId
|
|
177
|
-
const visited = new Set<string>()
|
|
178
|
-
while (currentId && chain.length < 10) {
|
|
179
|
-
if (visited.has(currentId)) break
|
|
180
|
-
visited.add(currentId)
|
|
181
|
-
const mission = loadMission(currentId)
|
|
182
|
-
if (!mission) break
|
|
183
|
-
chain.unshift(mission.objective.slice(0, 80))
|
|
184
|
-
currentId = mission.parentMissionId || null
|
|
185
|
-
}
|
|
186
|
-
if (chain.length <= 1) return null
|
|
187
|
-
return `### Goal Ancestry\n${chain.map((obj, i) => `${' '.repeat(i)}${i === chain.length - 1 ? '→' : '↓'} ${obj}`).join('\n')}`
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
function buildMissionSection(mission: Mission | null): string | null {
|
|
191
|
-
if (!mission) return null
|
|
192
|
-
if (mission.status === 'completed' || mission.status === 'failed' || mission.status === 'cancelled') return null
|
|
193
|
-
return `### Current Mission\nObjective: ${mission.objective.slice(0, 100)} | Status: ${mission.status} | Phase: ${mission.phase}`
|
|
194
|
-
}
|
|
195
170
|
|
|
196
171
|
// --- pure formatter (testable) ---
|
|
197
172
|
|
|
198
173
|
export function formatSituationalAwareness(data: SituationalAwarenessData): string {
|
|
199
|
-
const { tasks, schedules, failedRuns, incidents,
|
|
174
|
+
const { tasks, schedules, failedRuns, incidents, now } = data
|
|
200
175
|
|
|
201
176
|
const filteredTasks = tasks
|
|
202
177
|
.filter((t) => ACTIVE_TASK_STATUSES.has(t.status))
|
|
@@ -245,13 +220,6 @@ export function formatSituationalAwareness(data: SituationalAwarenessData): stri
|
|
|
245
220
|
charCount += schedulesSection.length
|
|
246
221
|
}
|
|
247
222
|
|
|
248
|
-
// Priority 4: Mission
|
|
249
|
-
const missionSection = buildMissionSection(mission)
|
|
250
|
-
if (missionSection && charCount + missionSection.length + header.length < MAX_CHARS) {
|
|
251
|
-
sections.push(missionSection)
|
|
252
|
-
charCount += missionSection.length
|
|
253
|
-
}
|
|
254
|
-
|
|
255
223
|
if (sections.length === 0) return ''
|
|
256
224
|
|
|
257
225
|
return [header, ...sections].join('\n\n')
|
|
@@ -343,7 +311,7 @@ function computeTodaySpend(sinceTs: number): number {
|
|
|
343
311
|
// --- main builder (loads data, calls formatter) ---
|
|
344
312
|
|
|
345
313
|
export function buildSituationalAwarenessBlock(input: SituationalAwarenessInput): string {
|
|
346
|
-
const { agentId, sessionId
|
|
314
|
+
const { agentId, sessionId } = input
|
|
347
315
|
const now = Date.now()
|
|
348
316
|
|
|
349
317
|
const allTasks = loadTasks() as Record<string, BoardTask>
|
|
@@ -356,7 +324,5 @@ export function buildSituationalAwarenessBlock(input: SituationalAwarenessInput)
|
|
|
356
324
|
|
|
357
325
|
const incidents = listAgentIncidents(agentId)
|
|
358
326
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
return formatSituationalAwareness({ tasks, schedules, failedRuns, incidents, mission, now })
|
|
327
|
+
return formatSituationalAwareness({ tasks, schedules, failedRuns, incidents, now })
|
|
362
328
|
}
|
|
@@ -6,10 +6,8 @@ import type { MessageToolEvent } from '@/types'
|
|
|
6
6
|
import { buildSuccessfulMemoryMutationResponse } from '@/lib/server/chat-execution/memory-mutation-tools'
|
|
7
7
|
import {
|
|
8
8
|
buildToolAvailabilityLines,
|
|
9
|
-
buildExternalWalletExecutionBlock,
|
|
10
9
|
buildToolDisciplineLines,
|
|
11
10
|
getExplicitRequiredToolNames,
|
|
12
|
-
isWalletSimulationResult,
|
|
13
11
|
looksLikeOpenEndedDeliverableTask,
|
|
14
12
|
pruneIncompleteToolEvents,
|
|
15
13
|
resolveExclusiveMemoryWriteTerminalAllowance,
|
|
@@ -35,8 +33,6 @@ import {
|
|
|
35
33
|
parseClassificationResponse,
|
|
36
34
|
isDeliverableTask,
|
|
37
35
|
isBroadGoal,
|
|
38
|
-
hasWalletIntent,
|
|
39
|
-
hasTransactionalWalletIntent,
|
|
40
36
|
hasHumanSignals,
|
|
41
37
|
hasSignificantEvent,
|
|
42
38
|
isResearchSynthesis,
|
|
@@ -122,14 +118,6 @@ describe('buildToolDisciplineLines', () => {
|
|
|
122
118
|
assert.ok(lines.some((line) => line.includes('Store secrets (passwords, API keys, tokens) with `manage_secrets`')))
|
|
123
119
|
})
|
|
124
120
|
|
|
125
|
-
it('adds bounded execution guidance for wallet-connected external-service tasks', () => {
|
|
126
|
-
const lines = buildToolDisciplineLines(['wallet', 'browser', 'http_request', 'manage_capabilities'])
|
|
127
|
-
|
|
128
|
-
assert.ok(lines.some((line) => line.includes('inspect the wallet first with `wallet_tool`')))
|
|
129
|
-
assert.ok(lines.some((line) => line.includes('Use a bounded loop: verify, attempt one reversible step, then execute or state the blocker.')))
|
|
130
|
-
assert.ok(lines.some((line) => line.includes('stop venue-shopping') && line.includes('call_contract')))
|
|
131
|
-
})
|
|
132
|
-
|
|
133
121
|
it('includes concrete local coding tool guidance when coding tools are already available', () => {
|
|
134
122
|
const lines = buildToolDisciplineLines(['files', 'shell', 'delegate'])
|
|
135
123
|
|
|
@@ -173,14 +161,6 @@ describe('buildToolDisciplineLines', () => {
|
|
|
173
161
|
assert.deepEqual(required, [])
|
|
174
162
|
})
|
|
175
163
|
|
|
176
|
-
it('does not force wallet tools based on keyword matching', () => {
|
|
177
|
-
const required = getExplicitRequiredToolNames(
|
|
178
|
-
'Use the available wallets and figure out how to trade on Hyperliquid.',
|
|
179
|
-
['wallet', 'browser', 'http_request'],
|
|
180
|
-
)
|
|
181
|
-
assert.deepEqual(required, [])
|
|
182
|
-
})
|
|
183
|
-
|
|
184
164
|
it('treats explicit curl or terminal execution requests as shell requirements when shell is enabled', () => {
|
|
185
165
|
const required = getExplicitRequiredToolNames(
|
|
186
166
|
'Yeah, do the curl. Curl request.',
|
|
@@ -291,41 +271,6 @@ describe('buildToolDisciplineLines', () => {
|
|
|
291
271
|
})
|
|
292
272
|
})
|
|
293
273
|
|
|
294
|
-
describe('buildExternalWalletExecutionBlock', () => {
|
|
295
|
-
it('omits extension-specific tool names when wallet/network capabilities are unavailable', () => {
|
|
296
|
-
const block = buildExternalWalletExecutionBlock(['files'])
|
|
297
|
-
|
|
298
|
-
assert.equal(block, '')
|
|
299
|
-
})
|
|
300
|
-
|
|
301
|
-
it('uses only enabled wallet-related tools in the external execution block', () => {
|
|
302
|
-
const block = buildExternalWalletExecutionBlock(['wallet', 'http_request', 'manage_capabilities'])
|
|
303
|
-
|
|
304
|
-
assert.ok(block.includes('## External Service Execution'))
|
|
305
|
-
assert.ok(!block.includes('`browser`'))
|
|
306
|
-
assert.ok(!block.includes('`wallet_tool`'))
|
|
307
|
-
assert.ok(!block.includes('`manage_capabilities`'))
|
|
308
|
-
assert.ok(block.includes('Define a stop condition before exploring'))
|
|
309
|
-
})
|
|
310
|
-
})
|
|
311
|
-
|
|
312
|
-
describe('isWalletSimulationResult', () => {
|
|
313
|
-
it('detects simulated wallet transaction outputs and ignores other tool outputs', () => {
|
|
314
|
-
assert.equal(
|
|
315
|
-
isWalletSimulationResult('wallet_tool', '{"status":"simulated","action":"simulate_transaction"}'),
|
|
316
|
-
true,
|
|
317
|
-
)
|
|
318
|
-
assert.equal(
|
|
319
|
-
isWalletSimulationResult('wallet_tool', '{"status":"broadcast","action":"send_transaction"}'),
|
|
320
|
-
false,
|
|
321
|
-
)
|
|
322
|
-
assert.equal(
|
|
323
|
-
isWalletSimulationResult('http_request', '{"status":"simulated"}'),
|
|
324
|
-
false,
|
|
325
|
-
)
|
|
326
|
-
})
|
|
327
|
-
})
|
|
328
|
-
|
|
329
274
|
describe('shouldSkipToolSummaryForShortResponse', () => {
|
|
330
275
|
it('skips forced tool-summary continuation for short responses after pure use_skill calls', () => {
|
|
331
276
|
assert.equal(
|
|
@@ -765,7 +710,7 @@ describe('shouldForceExternalServiceSummary', () => {
|
|
|
765
710
|
it('forces a summary when an external-service run ends with an unfinished exploration sentence', () => {
|
|
766
711
|
assert.equal(
|
|
767
712
|
shouldForceExternalServiceSummary({
|
|
768
|
-
userMessage: 'Try to
|
|
713
|
+
userMessage: 'Try to interact with the Hyperliquid API and stop at the blocker.',
|
|
769
714
|
finalResponse: 'This is promising - Hyperliquid runs on Arbitrum! Let me verify this and check if I can access their interface:',
|
|
770
715
|
hasToolCalls: true,
|
|
771
716
|
toolEventCount: 6,
|
|
@@ -777,8 +722,8 @@ describe('shouldForceExternalServiceSummary', () => {
|
|
|
777
722
|
it('does not force a summary when the final response already states the blocker', () => {
|
|
778
723
|
assert.equal(
|
|
779
724
|
shouldForceExternalServiceSummary({
|
|
780
|
-
userMessage: 'Try to
|
|
781
|
-
finalResponse: 'Last reversible step: I verified the funded Arbitrum
|
|
725
|
+
userMessage: 'Try to interact with the Hyperliquid API and stop at the blocker.',
|
|
726
|
+
finalResponse: 'Last reversible step: I verified the funded Arbitrum account and opened the site. Exact blocker: this runtime cannot complete a signature prompt.',
|
|
782
727
|
hasToolCalls: true,
|
|
783
728
|
toolEventCount: 6,
|
|
784
729
|
}),
|
|
@@ -789,7 +734,6 @@ describe('shouldForceExternalServiceSummary', () => {
|
|
|
789
734
|
|
|
790
735
|
describe('shouldForceExternalExecutionFollowthrough', () => {
|
|
791
736
|
const researchToolEvents = [
|
|
792
|
-
{ name: 'wallet_tool', input: '{"action":"balance","chain":"ethereum"}', output: '{"status":"ok"}' },
|
|
793
737
|
{ name: 'http_request', input: '{"method":"GET","url":"https://example.com/quote"}', output: '{"status":200}' },
|
|
794
738
|
{ name: 'web', input: '{"action":"open","url":"https://example.com/swap"}', output: '{"status":"ok"}' },
|
|
795
739
|
{ name: 'browser', input: '{"action":"read_page"}', output: '{"title":"Swap"}' },
|
|
@@ -826,7 +770,6 @@ describe('shouldForceExternalExecutionFollowthrough', () => {
|
|
|
826
770
|
finalResponse: 'Let me try another aggregator before proceeding.',
|
|
827
771
|
hasToolCalls: true,
|
|
828
772
|
toolEvents: [
|
|
829
|
-
{ name: 'wallet_tool', input: '{"action":"balance","chain":"ethereum"}', output: '{"status":"ok"}' },
|
|
830
773
|
{ name: 'http_request', input: '{"method":"GET","url":"https://api.0x.org/swap/v1/quote"}', output: '{"status":404}' },
|
|
831
774
|
{ name: 'http_request', input: '{"method":"GET","url":"https://apiv5.paraswap.io/prices"}', output: '{"status":400}' },
|
|
832
775
|
{ name: 'http_request', input: '{"method":"POST","url":"https://api.odos.xyz/sor/quote/v2"}', output: '{"status":200}' },
|
|
@@ -836,31 +779,13 @@ describe('shouldForceExternalExecutionFollowthrough', () => {
|
|
|
836
779
|
)
|
|
837
780
|
})
|
|
838
781
|
|
|
839
|
-
it('does not force a followthrough after a wallet approval boundary is reached', () => {
|
|
840
|
-
assert.equal(
|
|
841
|
-
shouldForceExternalExecutionFollowthrough({
|
|
842
|
-
userMessage: 'Do one tiny live swap on Arbitrum and stop at the first approval boundary.',
|
|
843
|
-
finalResponse: 'Current status: approval required for the exact-input token approval.',
|
|
844
|
-
hasToolCalls: true,
|
|
845
|
-
toolEvents: [
|
|
846
|
-
...researchToolEvents,
|
|
847
|
-
{
|
|
848
|
-
name: 'wallet_tool',
|
|
849
|
-
input: '{"action":"send_transaction","chain":"ethereum"}',
|
|
850
|
-
output: '{"type":"extension_wallet_action_request","status":"pending"}',
|
|
851
|
-
},
|
|
852
|
-
],
|
|
853
|
-
}),
|
|
854
|
-
false,
|
|
855
|
-
)
|
|
856
|
-
})
|
|
857
782
|
})
|
|
858
783
|
|
|
859
784
|
describe('shouldForceExternalExecutionKickoffFollowthrough', () => {
|
|
860
785
|
it('forces a bounded continuation when an execution task stops at an intent-only kickoff', () => {
|
|
861
786
|
assert.equal(
|
|
862
787
|
shouldForceExternalExecutionKickoffFollowthrough({
|
|
863
|
-
userMessage: 'Try
|
|
788
|
+
userMessage: 'Try to interact with the NFT marketplace API and show me what happened.',
|
|
864
789
|
finalResponse: 'Let me try to interact directly with the NFT contract and see if I can mint one:',
|
|
865
790
|
hasToolCalls: false,
|
|
866
791
|
toolEvents: [],
|
|
@@ -872,8 +797,8 @@ describe('shouldForceExternalExecutionKickoffFollowthrough', () => {
|
|
|
872
797
|
it('does not force kickoff when the model already surfaced a real blocker or asked a blocking question', () => {
|
|
873
798
|
assert.equal(
|
|
874
799
|
shouldForceExternalExecutionKickoffFollowthrough({
|
|
875
|
-
userMessage: 'Try
|
|
876
|
-
finalResponse: 'Exact blocker: this
|
|
800
|
+
userMessage: 'Try to interact with the NFT marketplace API and show me what happened.',
|
|
801
|
+
finalResponse: 'Exact blocker: this runtime cannot complete the required signature step.',
|
|
877
802
|
hasToolCalls: false,
|
|
878
803
|
toolEvents: [],
|
|
879
804
|
}),
|
|
@@ -881,7 +806,7 @@ describe('shouldForceExternalExecutionKickoffFollowthrough', () => {
|
|
|
881
806
|
)
|
|
882
807
|
assert.equal(
|
|
883
808
|
shouldForceExternalExecutionKickoffFollowthrough({
|
|
884
|
-
userMessage: 'Try
|
|
809
|
+
userMessage: 'Try to interact with the NFT marketplace API and show me what happened.',
|
|
885
810
|
finalResponse: 'Which collection do you want me to target?',
|
|
886
811
|
hasToolCalls: false,
|
|
887
812
|
toolEvents: [],
|
|
@@ -1223,7 +1148,6 @@ describe('parseClassificationResponse', () => {
|
|
|
1223
1148
|
const result = parseClassificationResponse(JSON.stringify({
|
|
1224
1149
|
isDeliverableTask: true,
|
|
1225
1150
|
isBroadGoal: false,
|
|
1226
|
-
walletIntent: 'none',
|
|
1227
1151
|
hasHumanSignals: false,
|
|
1228
1152
|
hasSignificantEvent: false,
|
|
1229
1153
|
isResearchSynthesis: true,
|
|
@@ -1233,14 +1157,13 @@ describe('parseClassificationResponse', () => {
|
|
|
1233
1157
|
assert.ok(result)
|
|
1234
1158
|
assert.equal(result.isDeliverableTask, true)
|
|
1235
1159
|
assert.equal(result.isBroadGoal, false)
|
|
1236
|
-
assert.equal(result.walletIntent, 'none')
|
|
1237
1160
|
assert.equal(result.isResearchSynthesis, true)
|
|
1238
1161
|
assert.deepEqual(result.explicitToolRequests, ['web'])
|
|
1239
1162
|
assert.equal(result.confidence, 0.9)
|
|
1240
1163
|
})
|
|
1241
1164
|
|
|
1242
1165
|
it('extracts JSON from markdown code block', () => {
|
|
1243
|
-
const text = '```json\n{"isDeliverableTask":false,"isBroadGoal":false,"
|
|
1166
|
+
const text = '```json\n{"isDeliverableTask":false,"isBroadGoal":false,"hasHumanSignals":false,"hasSignificantEvent":false,"isResearchSynthesis":false,"explicitToolRequests":[],"confidence":0.8}\n```'
|
|
1244
1167
|
const result = parseClassificationResponse(text)
|
|
1245
1168
|
assert.ok(result)
|
|
1246
1169
|
assert.equal(result.isDeliverableTask, false)
|
|
@@ -1257,19 +1180,6 @@ describe('parseClassificationResponse', () => {
|
|
|
1257
1180
|
assert.equal(result, null)
|
|
1258
1181
|
})
|
|
1259
1182
|
|
|
1260
|
-
it('rejects invalid walletIntent values', () => {
|
|
1261
|
-
const result = parseClassificationResponse(JSON.stringify({
|
|
1262
|
-
isDeliverableTask: false,
|
|
1263
|
-
isBroadGoal: false,
|
|
1264
|
-
walletIntent: 'invalid',
|
|
1265
|
-
hasHumanSignals: false,
|
|
1266
|
-
hasSignificantEvent: false,
|
|
1267
|
-
isResearchSynthesis: false,
|
|
1268
|
-
explicitToolRequests: [],
|
|
1269
|
-
confidence: 0.5,
|
|
1270
|
-
}))
|
|
1271
|
-
assert.equal(result, null)
|
|
1272
|
-
})
|
|
1273
1183
|
})
|
|
1274
1184
|
|
|
1275
1185
|
describe('message classifier adapter functions', () => {
|
|
@@ -1277,7 +1187,6 @@ describe('message classifier adapter functions', () => {
|
|
|
1277
1187
|
taskIntent: 'general',
|
|
1278
1188
|
isDeliverableTask: true,
|
|
1279
1189
|
isBroadGoal: true,
|
|
1280
|
-
walletIntent: 'none',
|
|
1281
1190
|
hasHumanSignals: false,
|
|
1282
1191
|
hasSignificantEvent: false,
|
|
1283
1192
|
isResearchSynthesis: false,
|
|
@@ -1285,23 +1194,10 @@ describe('message classifier adapter functions', () => {
|
|
|
1285
1194
|
confidence: 0.95,
|
|
1286
1195
|
}
|
|
1287
1196
|
|
|
1288
|
-
const walletClassification: MessageClassification = {
|
|
1289
|
-
taskIntent: 'general',
|
|
1290
|
-
isDeliverableTask: false,
|
|
1291
|
-
isBroadGoal: false,
|
|
1292
|
-
walletIntent: 'transactional',
|
|
1293
|
-
hasHumanSignals: false,
|
|
1294
|
-
hasSignificantEvent: false,
|
|
1295
|
-
isResearchSynthesis: false,
|
|
1296
|
-
explicitToolRequests: [],
|
|
1297
|
-
confidence: 0.9,
|
|
1298
|
-
}
|
|
1299
|
-
|
|
1300
1197
|
const humanSignalClassification: MessageClassification = {
|
|
1301
1198
|
taskIntent: 'general',
|
|
1302
1199
|
isDeliverableTask: false,
|
|
1303
1200
|
isBroadGoal: false,
|
|
1304
|
-
walletIntent: 'none',
|
|
1305
1201
|
hasHumanSignals: true,
|
|
1306
1202
|
hasSignificantEvent: true,
|
|
1307
1203
|
isResearchSynthesis: false,
|
|
@@ -1326,17 +1222,6 @@ describe('message classifier adapter functions', () => {
|
|
|
1326
1222
|
assert.equal(isBroadGoal({ ...deliverableClassification, isBroadGoal: false }, 'short'), false)
|
|
1327
1223
|
})
|
|
1328
1224
|
|
|
1329
|
-
it('hasWalletIntent uses classification', () => {
|
|
1330
|
-
assert.equal(hasWalletIntent(walletClassification, 'swap ETH for USDC'), true)
|
|
1331
|
-
assert.equal(hasWalletIntent({ ...walletClassification, walletIntent: 'none' }, 'swap ETH for USDC'), false)
|
|
1332
|
-
})
|
|
1333
|
-
|
|
1334
|
-
it('hasTransactionalWalletIntent distinguishes read_only from transactional', () => {
|
|
1335
|
-
assert.equal(hasTransactionalWalletIntent(walletClassification, 'anything'), true)
|
|
1336
|
-
assert.equal(hasTransactionalWalletIntent({ ...walletClassification, walletIntent: 'read_only' }, 'anything'), false)
|
|
1337
|
-
assert.equal(hasTransactionalWalletIntent({ ...walletClassification, walletIntent: 'none' }, 'anything'), false)
|
|
1338
|
-
})
|
|
1339
|
-
|
|
1340
1225
|
it('hasHumanSignals uses classification', () => {
|
|
1341
1226
|
assert.equal(hasHumanSignals(humanSignalClassification, 'anything'), true)
|
|
1342
1227
|
assert.equal(hasHumanSignals({ ...humanSignalClassification, hasHumanSignals: false }, 'anything'), false)
|
|
@@ -78,7 +78,6 @@ import {
|
|
|
78
78
|
buildToolAvailabilityLines,
|
|
79
79
|
buildToolDisciplineLines,
|
|
80
80
|
buildToolSection,
|
|
81
|
-
buildExternalWalletExecutionBlock,
|
|
82
81
|
buildForcedExternalServiceSummary,
|
|
83
82
|
shouldForceAttachmentFollowthrough,
|
|
84
83
|
joinPromptSegments,
|
|
@@ -101,7 +100,6 @@ import { finalizeStreamResult } from '@/lib/server/chat-execution/post-stream-fi
|
|
|
101
100
|
import {
|
|
102
101
|
classifyMessage,
|
|
103
102
|
isDeliverableTask as classifiedIsDeliverableTask,
|
|
104
|
-
hasTransactionalWalletIntent as classifiedHasTransactionalWalletIntent,
|
|
105
103
|
isResearchSynthesis as classifiedIsResearchSynthesis,
|
|
106
104
|
type MessageClassification,
|
|
107
105
|
} from '@/lib/server/chat-execution/message-classifier'
|
|
@@ -125,7 +123,6 @@ export {
|
|
|
125
123
|
buildToolAvailabilityLines,
|
|
126
124
|
buildToolDisciplineLines,
|
|
127
125
|
buildToolSection,
|
|
128
|
-
buildExternalWalletExecutionBlock,
|
|
129
126
|
shouldForceAttachmentFollowthrough,
|
|
130
127
|
buildForcedExternalServiceSummary,
|
|
131
128
|
}
|
|
@@ -142,7 +139,6 @@ export {
|
|
|
142
139
|
} from '@/lib/server/chat-execution/stream-continuation'
|
|
143
140
|
|
|
144
141
|
export {
|
|
145
|
-
isWalletSimulationResult,
|
|
146
142
|
resolveSuccessfulTerminalToolBoundary,
|
|
147
143
|
shouldForceExternalServiceSummary,
|
|
148
144
|
} from '@/lib/server/chat-execution/chat-streaming-utils'
|
|
@@ -889,7 +885,7 @@ async function streamAgentChatCore(opts: StreamAgentChatOpts): Promise<StreamAge
|
|
|
889
885
|
const routingDecision = routeTaskIntent(message, sessionExtensions, null, classification)
|
|
890
886
|
const explicitRequiredToolNames = getExplicitRequiredToolNames(message, sessionExtensions)
|
|
891
887
|
|
|
892
|
-
const boundedExternalExecutionTask =
|
|
888
|
+
const boundedExternalExecutionTask = false
|
|
893
889
|
const likelyResearchSynthesisTask = classifiedIsResearchSynthesis(classification, routingDecision.intent)
|
|
894
890
|
const shouldEnforceEarlyRequiredToolKickoff = explicitRequiredToolNames.length > 0
|
|
895
891
|
&& classifiedIsDeliverableTask(classification, message)
|
|
@@ -46,36 +46,6 @@ describe('stream-continuation', () => {
|
|
|
46
46
|
})
|
|
47
47
|
})
|
|
48
48
|
|
|
49
|
-
// ---- looksLikeExternalWalletTask ----
|
|
50
|
-
describe('looksLikeExternalWalletTask', () => {
|
|
51
|
-
it('matches wallet keywords', () => {
|
|
52
|
-
assert.equal(mod.looksLikeExternalWalletTask('swap 100 USDC on Arbitrum'), true)
|
|
53
|
-
assert.equal(mod.looksLikeExternalWalletTask('check my wallet balance'), true)
|
|
54
|
-
assert.equal(mod.looksLikeExternalWalletTask('trade ETH for SOL'), true)
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
it('returns false for empty/unrelated text', () => {
|
|
58
|
-
assert.equal(mod.looksLikeExternalWalletTask(''), false)
|
|
59
|
-
assert.equal(mod.looksLikeExternalWalletTask('write me a poem about cats'), false)
|
|
60
|
-
})
|
|
61
|
-
})
|
|
62
|
-
|
|
63
|
-
// ---- looksLikeBoundedExternalExecutionTask ----
|
|
64
|
-
describe('looksLikeBoundedExternalExecutionTask', () => {
|
|
65
|
-
it('requires wallet keywords + action verbs', () => {
|
|
66
|
-
assert.equal(mod.looksLikeBoundedExternalExecutionTask('swap 100 USDC on Arbitrum'), true)
|
|
67
|
-
assert.equal(mod.looksLikeBoundedExternalExecutionTask('buy ETH on the exchange'), true)
|
|
68
|
-
})
|
|
69
|
-
|
|
70
|
-
it('returns false without action verbs', () => {
|
|
71
|
-
assert.equal(mod.looksLikeBoundedExternalExecutionTask('check my wallet balance'), false)
|
|
72
|
-
})
|
|
73
|
-
|
|
74
|
-
it('returns false for non-wallet text', () => {
|
|
75
|
-
assert.equal(mod.looksLikeBoundedExternalExecutionTask('execute the test suite'), false)
|
|
76
|
-
})
|
|
77
|
-
})
|
|
78
|
-
|
|
79
49
|
// ---- looksLikeOpenEndedDeliverableTask ----
|
|
80
50
|
describe('looksLikeOpenEndedDeliverableTask', () => {
|
|
81
51
|
it('matches deliverable patterns', () => {
|
|
@@ -112,24 +82,6 @@ describe('stream-continuation', () => {
|
|
|
112
82
|
})
|
|
113
83
|
})
|
|
114
84
|
|
|
115
|
-
// ---- hasStateChangingWalletEvidence ----
|
|
116
|
-
describe('hasStateChangingWalletEvidence', () => {
|
|
117
|
-
it('detects send_transaction action in wallet_tool', () => {
|
|
118
|
-
const events = [{ name: 'wallet_tool', input: '{"action":"send_transaction"}', output: '{}' }]
|
|
119
|
-
assert.equal(mod.hasStateChangingWalletEvidence(events), true)
|
|
120
|
-
})
|
|
121
|
-
|
|
122
|
-
it('returns false for non-wallet tools', () => {
|
|
123
|
-
const events = [{ name: 'web', input: 'search', output: 'results' }]
|
|
124
|
-
assert.equal(mod.hasStateChangingWalletEvidence(events), false)
|
|
125
|
-
})
|
|
126
|
-
|
|
127
|
-
it('returns false for read-only wallet actions', () => {
|
|
128
|
-
const events = [{ name: 'wallet_tool', input: '{"action":"balance"}', output: '{}' }]
|
|
129
|
-
assert.equal(mod.hasStateChangingWalletEvidence(events), false)
|
|
130
|
-
})
|
|
131
|
-
})
|
|
132
|
-
|
|
133
85
|
// ---- countExternalExecutionResearchSteps ----
|
|
134
86
|
describe('countExternalExecutionResearchSteps', () => {
|
|
135
87
|
it('counts http/web/browser tools', () => {
|
|
@@ -141,12 +93,12 @@ describe('stream-continuation', () => {
|
|
|
141
93
|
assert.equal(mod.countExternalExecutionResearchSteps(events), 2)
|
|
142
94
|
})
|
|
143
95
|
|
|
144
|
-
it('
|
|
96
|
+
it('does not count non-research tools', () => {
|
|
145
97
|
const events = [
|
|
146
|
-
{ name: '
|
|
147
|
-
{ name: '
|
|
98
|
+
{ name: 'shell', input: 'ls', output: 'files' },
|
|
99
|
+
{ name: 'files', input: 'read', output: 'content' },
|
|
148
100
|
]
|
|
149
|
-
assert.equal(mod.countExternalExecutionResearchSteps(events),
|
|
101
|
+
assert.equal(mod.countExternalExecutionResearchSteps(events), 0)
|
|
150
102
|
})
|
|
151
103
|
})
|
|
152
104
|
|
|
@@ -194,25 +194,9 @@ function getRequestedArtifactStatus(params: {
|
|
|
194
194
|
// Tool evidence analysis
|
|
195
195
|
// ---------------------------------------------------------------------------
|
|
196
196
|
|
|
197
|
-
export function hasStateChangingWalletEvidence(toolEvents: MessageToolEvent[]): boolean {
|
|
198
|
-
return toolEvents.some((event) => {
|
|
199
|
-
const input = `${event.input || ''}\n${event.output || ''}`
|
|
200
|
-
return event.name === 'wallet_tool' && (
|
|
201
|
-
/"action":"send_transaction"/.test(input)
|
|
202
|
-
|| /"action":"send"/.test(input)
|
|
203
|
-
|| /"action":"sign_transaction"/.test(input)
|
|
204
|
-
|| /"type":"extension_wallet_action_request"/.test(input)
|
|
205
|
-
|| /"type":"extension_wallet_transfer_request"/.test(input)
|
|
206
|
-
|| /"status":"broadcast"/.test(input)
|
|
207
|
-
)
|
|
208
|
-
})
|
|
209
|
-
}
|
|
210
|
-
|
|
211
197
|
export function countExternalExecutionResearchSteps(toolEvents: MessageToolEvent[]): number {
|
|
212
198
|
return toolEvents.filter((event) => {
|
|
213
|
-
|
|
214
|
-
if (event.name !== 'wallet_tool') return false
|
|
215
|
-
return /"action":"(balance|address|transactions|call_contract|encode_contract_call)"/.test(event.input || '')
|
|
199
|
+
return ['http_request', 'web', 'web_search', 'web_fetch', 'browser'].includes(event.name)
|
|
216
200
|
}).length
|
|
217
201
|
}
|
|
218
202
|
|
|
@@ -238,49 +222,24 @@ export function countDistinctExternalResearchHosts(toolEvents: MessageToolEvent[
|
|
|
238
222
|
// Continuation decision helpers
|
|
239
223
|
// ---------------------------------------------------------------------------
|
|
240
224
|
|
|
241
|
-
export function shouldForceExternalExecutionFollowthrough(
|
|
225
|
+
export function shouldForceExternalExecutionFollowthrough(_params: {
|
|
242
226
|
userMessage: string
|
|
243
227
|
finalResponse: string
|
|
244
228
|
hasToolCalls: boolean
|
|
245
229
|
toolEvents: MessageToolEvent[]
|
|
246
230
|
classification?: MessageClassification | null
|
|
247
231
|
}): boolean {
|
|
248
|
-
|
|
249
|
-
if (!isTransactional) return false
|
|
250
|
-
if (!params.hasToolCalls || params.toolEvents.length < 4) return false
|
|
251
|
-
if (hasStateChangingWalletEvidence(params.toolEvents)) return false
|
|
252
|
-
const distinctHosts = countDistinctExternalResearchHosts(params.toolEvents)
|
|
253
|
-
const trimmed = params.finalResponse.trim()
|
|
254
|
-
if (!trimmed) return countExternalExecutionResearchSteps(params.toolEvents) >= 4 || distinctHosts >= 3
|
|
255
|
-
if (/\b(last reversible step|exact blocker|safest next action|blocked|cannot|can't|missing capability|no-key route unavailable)\b/i.test(trimmed)) {
|
|
256
|
-
return false
|
|
257
|
-
}
|
|
258
|
-
if (countExternalExecutionResearchSteps(params.toolEvents) < 4 && distinctHosts < 3) return false
|
|
259
|
-
return /(let me|i'll|i will|trying|research|query|check|look|promising|now let me|good -|good,)/i.test(trimmed) || trimmed.length < 500
|
|
232
|
+
return false
|
|
260
233
|
}
|
|
261
234
|
|
|
262
|
-
export function shouldForceExternalExecutionKickoffFollowthrough(
|
|
235
|
+
export function shouldForceExternalExecutionKickoffFollowthrough(_params: {
|
|
263
236
|
userMessage: string
|
|
264
237
|
finalResponse: string
|
|
265
238
|
hasToolCalls: boolean
|
|
266
239
|
toolEvents: MessageToolEvent[]
|
|
267
240
|
classification?: MessageClassification | null
|
|
268
241
|
}): boolean {
|
|
269
|
-
|
|
270
|
-
if (!isTransactional) return false
|
|
271
|
-
if (params.hasToolCalls || params.toolEvents.length > 0) return false
|
|
272
|
-
|
|
273
|
-
const trimmed = params.finalResponse.trim()
|
|
274
|
-
if (!trimmed) return true
|
|
275
|
-
if (/^(?:HEARTBEAT_OK|NO_MESSAGE)\b/i.test(trimmed)) return false
|
|
276
|
-
if (/\?\s*$/.test(trimmed)) return false
|
|
277
|
-
if (/\b(last reversible step|exact blocker|blocked|cannot|can't|missing capability|need approval|requires approval|approval boundary|requires human|ask_human|credential|authentication|login|2fa|mfa|captcha)\b/i.test(trimmed)) {
|
|
278
|
-
return false
|
|
279
|
-
}
|
|
280
|
-
if (/\b(done|completed|finished|sent|broadcast|minted|purchased|bought|swapped|claimed)\b/i.test(trimmed)) {
|
|
281
|
-
return false
|
|
282
|
-
}
|
|
283
|
-
return looksLikeIncompleteDeliverableResponse(trimmed) || trimmed.length < 220
|
|
242
|
+
return false
|
|
284
243
|
}
|
|
285
244
|
|
|
286
245
|
export function shouldForceDeliverableFollowthrough(params: {
|
|
@@ -454,8 +413,7 @@ function buildExternalExecutionFollowthroughPrompt(params: {
|
|
|
454
413
|
'You are in a bounded external execution task and have already done enough research.',
|
|
455
414
|
'Do not restart broad discovery. Do not ask the user for another prompt.',
|
|
456
415
|
'Do not spend this continuation on more venue shopping. Use the already confirmed route unless one last fetch is strictly required to prepare execution.',
|
|
457
|
-
'If several venue or aggregator APIs already failed, stop searching for more venues
|
|
458
|
-
'A prose approval request does not count as completion. If the next step is a sign/send/approve action, call the real wallet tool action so the runtime can create the approval request.',
|
|
416
|
+
'If several venue or aggregator APIs already failed, stop searching for more venues and state the blocker.',
|
|
459
417
|
'Do not mutate already confirmed token addresses, router addresses, spender addresses, or network identifiers unless newer tool evidence proves the earlier value was wrong.',
|
|
460
418
|
'Within this continuation, do exactly one of the following:',
|
|
461
419
|
'1. Take the next concrete execution step now using the existing tools and stop at the first approval boundary for a state-changing action.',
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { genId } from '@/lib/id'
|
|
2
2
|
import type { MailboxEnvelope } from '@/types'
|
|
3
3
|
import { loadSession, patchSession } from '@/lib/server/sessions/session-repository'
|
|
4
|
-
import { requestMissionTicksForHumanReply } from '@/lib/server/missions/mission-service'
|
|
5
4
|
|
|
6
5
|
interface MailboxOptions {
|
|
7
6
|
limit?: number
|
|
@@ -167,15 +166,6 @@ export function sendMailboxEnvelope(input: {
|
|
|
167
166
|
lastActiveAt: now,
|
|
168
167
|
}
|
|
169
168
|
})
|
|
170
|
-
if (envelope.type === 'human_reply') {
|
|
171
|
-
requestMissionTicksForHumanReply({
|
|
172
|
-
sessionId: input.toSessionId,
|
|
173
|
-
correlationId: envelope.correlationId || null,
|
|
174
|
-
envelopeId: envelope.id,
|
|
175
|
-
payload: envelope.payload,
|
|
176
|
-
fromSessionId: envelope.fromSessionId || null,
|
|
177
|
-
})
|
|
178
|
-
}
|
|
179
169
|
import('@/lib/server/runtime/watch-jobs')
|
|
180
170
|
.then(({ triggerMailboxWatchJobs }) => {
|
|
181
171
|
triggerMailboxWatchJobs({ sessionId: input.toSessionId, envelope })
|
|
@@ -7,7 +7,6 @@ import { buildAgentDisabledMessage, isAgentDisabled } from '@/lib/server/agents/
|
|
|
7
7
|
import { loadAgent } from '@/lib/server/agents/agent-repository'
|
|
8
8
|
import { clearMainLoopStateForSession } from '@/lib/server/agents/main-agent-loop'
|
|
9
9
|
import { applyResolvedRoute, resolvePrimaryAgentRoute } from '@/lib/server/agents/agent-runtime-config'
|
|
10
|
-
import { enrichSessionWithMissionSummary } from '@/lib/server/missions/mission-service'
|
|
11
10
|
import { cleanupSessionProcesses } from '@/lib/server/runtime/process-manager'
|
|
12
11
|
import { stopActiveSessionProcess } from '@/lib/server/runtime/runtime-state'
|
|
13
12
|
import {
|
|
@@ -60,7 +59,7 @@ export function listChatsForApi(): Record<string, ReturnType<typeof buildSession
|
|
|
60
59
|
sessions[id].currentRunId = run.runningRunId || null
|
|
61
60
|
}
|
|
62
61
|
return Object.fromEntries(
|
|
63
|
-
Object.entries(sessions).map(([id, session]) => [id, buildSessionListSummary(
|
|
62
|
+
Object.entries(sessions).map(([id, session]) => [id, buildSessionListSummary(session)]),
|
|
64
63
|
)
|
|
65
64
|
}
|
|
66
65
|
|
|
@@ -72,7 +71,7 @@ export function getChatSessionForApi(sessionId: string): Session | null {
|
|
|
72
71
|
session.active = !!run.runningRunId
|
|
73
72
|
session.queuedCount = queue.queueLength
|
|
74
73
|
session.currentRunId = run.runningRunId || null
|
|
75
|
-
return
|
|
74
|
+
return session
|
|
76
75
|
}
|
|
77
76
|
|
|
78
77
|
export function createChatSession(input: Record<string, unknown>): ServiceResult<Session> {
|
|
@@ -285,7 +284,7 @@ export function updateChatSession(sessionId: string, updates: Record<string, unk
|
|
|
285
284
|
|
|
286
285
|
saveSession(sessionId, original)
|
|
287
286
|
notify('sessions')
|
|
288
|
-
return
|
|
287
|
+
return original
|
|
289
288
|
}
|
|
290
289
|
|
|
291
290
|
export function deleteChatSession(sessionId: string): boolean {
|
|
@@ -320,7 +319,6 @@ export function queueChatMessage(sessionId: string, body: Record<string, unknown
|
|
|
320
319
|
}
|
|
321
320
|
const queued = enqueueSessionRun({
|
|
322
321
|
sessionId,
|
|
323
|
-
missionId: session.missionId || null,
|
|
324
322
|
message,
|
|
325
323
|
imagePath,
|
|
326
324
|
imageUrl,
|
|
@@ -1095,7 +1095,6 @@ If media sending fails, report the exact error and retry with a corrected path/t
|
|
|
1095
1095
|
|
|
1096
1096
|
const queued = enqueueSessionRun({
|
|
1097
1097
|
sessionId: session.id,
|
|
1098
|
-
missionId: session.missionId || null,
|
|
1099
1098
|
message: modelInputText,
|
|
1100
1099
|
imagePath: firstImagePath,
|
|
1101
1100
|
imageUrl: firstImageUrl,
|