@swarmclawai/swarmclaw 1.2.6 → 1.2.9
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 +54 -23
- package/next.config.ts +1 -0
- package/package.json +4 -3
- package/scripts/easy-setup.mjs +1 -1
- package/scripts/postinstall.mjs +1 -1
- package/skills/swarmclaw.md +115 -0
- package/skills/tools/browser.md +131 -0
- package/skills/tools/execute.md +98 -0
- package/skills/tools/files.md +98 -0
- package/skills/tools/memory.md +104 -0
- package/skills/tools/platform.md +144 -0
- package/skills/tools/skills.md +83 -0
- package/src/app/agents/[id]/page.tsx +1 -18
- 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/chats/[id]/messages/route.ts +23 -19
- package/src/app/api/chats/messages-route.test.ts +105 -51
- package/src/app/api/connectors/route.ts +2 -2
- package/src/app/api/mcp-servers/[id]/test/route.ts +3 -2
- package/src/app/api/openclaw/deploy/route.ts +2 -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/settings/route.ts +0 -2
- package/src/app/api/setup/doctor/route.ts +4 -4
- 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/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 +21 -33
- package/src/cli/spec.js +19 -30
- package/src/components/agents/agent-chat-list.tsx +23 -1
- package/src/components/agents/agent-sheet.tsx +2 -40
- package/src/components/agents/inspector-panel.tsx +165 -131
- package/src/components/chat/chat-area.tsx +38 -9
- package/src/components/chat/chat-card.tsx +0 -31
- package/src/components/chat/message-bubble.tsx +1 -108
- package/src/components/chat/message-list.tsx +33 -19
- package/src/components/connectors/connector-sheet.tsx +25 -1
- package/src/components/gateways/gateway-sheet.tsx +5 -2
- 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/agent-execute-defaults.test.ts +24 -0
- package/src/lib/agent-execute-defaults.ts +62 -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/chat/queued-message-queue.test.ts +134 -1
- package/src/lib/chat/queued-message-queue.ts +77 -2
- package/src/lib/server/agents/agent-service.ts +5 -0
- 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 +0 -85
- package/src/lib/server/approvals.test.ts +6 -6
- package/src/lib/server/approvals.ts +0 -6
- package/src/lib/server/autonomy/supervisor-reflection.test.ts +0 -1
- package/src/lib/server/builtin-extensions.ts +1 -2
- package/src/lib/server/capability-router.test.ts +0 -2
- package/src/lib/server/chat-execution/chat-execution-advanced.test.ts +1 -1
- package/src/lib/server/chat-execution/chat-execution-tool-events.test.ts +15 -14
- package/src/lib/server/chat-execution/chat-execution-types.ts +0 -2
- package/src/lib/server/chat-execution/chat-execution-utils.ts +2 -4
- 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 +81 -64
- package/src/lib/server/chat-execution/chat-turn-stream-execution.ts +4 -0
- package/src/lib/server/chat-execution/continuation-evaluator.ts +8 -0
- package/src/lib/server/chat-execution/iteration-event-handler.ts +0 -24
- package/src/lib/server/chat-execution/memory-mutation-tools.ts +1 -1
- package/src/lib/server/chat-execution/message-classifier.test.ts +0 -45
- package/src/lib/server/chat-execution/message-classifier.ts +11 -16
- package/src/lib/server/chat-execution/prompt-builder.test.ts +27 -0
- package/src/lib/server/chat-execution/prompt-builder.ts +14 -31
- package/src/lib/server/chat-execution/prompt-mode.test.ts +24 -0
- package/src/lib/server/chat-execution/prompt-mode.ts +5 -1
- 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 +13 -126
- package/src/lib/server/chat-execution/stream-agent-chat.ts +46 -21
- 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/chatroom-routing.test.ts +4 -0
- 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/discord.ts +2 -2
- package/src/lib/server/connectors/matrix.ts +3 -2
- package/src/lib/server/connectors/signal.ts +5 -4
- package/src/lib/server/connectors/slack.ts +10 -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 +119 -0
- package/src/lib/server/connectors/swarmdock.ts +255 -0
- package/src/lib/server/connectors/teams.ts +3 -2
- package/src/lib/server/connectors/telegram.ts +4 -4
- package/src/lib/server/connectors/whatsapp.ts +2 -2
- package/src/lib/server/daemon/controller.ts +7 -0
- package/src/lib/server/execution-brief.test.ts +2 -25
- package/src/lib/server/execution-brief.ts +12 -35
- package/src/lib/server/execution-engine/task-attempt.ts +0 -1
- package/src/lib/server/gateways/gateway-profile-service.ts +19 -1
- package/src/lib/server/messages/message-repository.test.ts +70 -0
- package/src/lib/server/messages/message-repository.ts +11 -6
- package/src/lib/server/openclaw/deploy.ts +32 -2
- package/src/lib/server/persistence/storage-context.ts +0 -5
- package/src/lib/server/plugins-advanced.test.ts +1 -2
- 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 +1 -10
- 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/process-manager.ts +13 -9
- 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 +15 -1
- package/src/lib/server/runtime/session-run-manager/recovery.ts +0 -1
- package/src/lib/server/runtime/session-run-manager.test.ts +58 -28
- package/src/lib/server/sandbox/session-runtime.test.ts +18 -1
- package/src/lib/server/sandbox/session-runtime.ts +40 -28
- package/src/lib/server/session-tools/autonomy-tools.test.ts +7 -9
- package/src/lib/server/session-tools/context.ts +1 -1
- package/src/lib/server/session-tools/credential-env.ts +109 -0
- package/src/lib/server/session-tools/crud.ts +3 -17
- package/src/lib/server/session-tools/delegate.ts +0 -4
- package/src/lib/server/session-tools/edit_file.ts +3 -2
- package/src/lib/server/session-tools/execute.test.ts +58 -0
- package/src/lib/server/session-tools/execute.ts +334 -0
- package/src/lib/server/session-tools/files-tool.ts +635 -0
- package/src/lib/server/session-tools/index.ts +14 -8
- package/src/lib/server/session-tools/memory-tool.ts +242 -0
- package/src/lib/server/session-tools/memory.ts +1 -1
- package/src/lib/server/session-tools/openclaw-nodes.ts +3 -2
- package/src/lib/server/session-tools/openclaw-workspace.ts +3 -2
- package/src/lib/server/session-tools/platform-tool.ts +617 -0
- package/src/lib/server/session-tools/session-info.ts +3 -2
- package/src/lib/server/session-tools/session-tools-wiring.test.ts +3 -4
- package/src/lib/server/session-tools/shell.ts +7 -122
- package/src/lib/server/session-tools/skills-tool.ts +396 -0
- package/src/lib/server/session-tools/team-context.ts +0 -3
- package/src/lib/server/session-tools/web.ts +2 -2
- package/src/lib/server/storage-normalization.ts +10 -0
- package/src/lib/server/storage.ts +18 -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 +2 -2
- package/src/lib/server/tool-capability-policy-advanced.test.ts +13 -6
- package/src/lib/server/tool-capability-policy.test.ts +2 -1
- package/src/lib/server/tool-capability-policy.ts +60 -35
- package/src/lib/server/tool-planning.ts +11 -12
- package/src/lib/server/universal-tool-access.ts +0 -1
- 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/setup-defaults.ts +5 -0
- 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 +1 -1
- package/src/lib/validation/schemas.test.ts +16 -0
- package/src/lib/validation/schemas.ts +49 -2
- package/src/stores/slices/data-slice.ts +5 -1
- package/src/stores/slices/ui-slice.ts +0 -4
- package/src/stores/use-chat-store.test.ts +231 -0
- package/src/stores/use-chat-store.ts +62 -13
- package/src/types/agent.ts +264 -0
- package/src/types/app-settings.ts +173 -0
- package/src/types/approval.ts +25 -0
- package/src/types/connector.ts +188 -0
- package/src/types/extension.ts +386 -0
- package/src/types/index.ts +16 -3555
- package/src/types/message.ts +56 -0
- package/src/types/misc.ts +737 -0
- package/src/types/protocol.ts +420 -0
- package/src/types/provider.ts +52 -0
- package/src/types/run.ts +180 -0
- package/src/types/schedule.ts +59 -0
- package/src/types/session.ts +215 -0
- package/src/types/skill.ts +157 -0
- package/src/types/swarmdock.ts +29 -0
- package/src/types/task.ts +144 -0
- package/src/types/working-state.ts +204 -0
- package/src/views/settings/section-heartbeat.tsx +2 -2
- 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/sandbox.ts +0 -281
- 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/views/settings/section-wallets.tsx +0 -35
|
@@ -45,9 +45,6 @@ import {
|
|
|
45
45
|
} from '@/lib/capability-selection'
|
|
46
46
|
import { normalizeProviderEndpoint, isLocalOpenClawEndpoint } from '@/lib/openclaw/openclaw-endpoint'
|
|
47
47
|
import { NON_LANGGRAPH_PROVIDER_IDS } from '@/lib/provider-sets'
|
|
48
|
-
import {
|
|
49
|
-
resolveMissionForTurn,
|
|
50
|
-
} from '@/lib/server/missions/mission-service'
|
|
51
48
|
import {
|
|
52
49
|
bridgeHumanReplyFromChat,
|
|
53
50
|
} from '@/lib/server/chatrooms/session-mailbox'
|
|
@@ -76,6 +73,7 @@ import {
|
|
|
76
73
|
import { checkAgentBudgetLimits } from '@/lib/server/cost'
|
|
77
74
|
import {
|
|
78
75
|
classifyMessage,
|
|
76
|
+
type MessageClassification,
|
|
79
77
|
toMessageSemanticsSummary,
|
|
80
78
|
} from '@/lib/server/chat-execution/message-classifier'
|
|
81
79
|
import {
|
|
@@ -87,6 +85,7 @@ import {
|
|
|
87
85
|
} from '@/lib/server/chat-execution/chat-execution-utils'
|
|
88
86
|
import { loadEstopState } from '@/lib/server/runtime/estop'
|
|
89
87
|
import { buildToolSection, joinPromptSegments } from '@/lib/server/chat-execution/prompt-builder'
|
|
88
|
+
import { resolvePromptMode, type PromptMode } from '@/lib/server/chat-execution/prompt-mode'
|
|
90
89
|
import { isDirectConnectorSession } from '@/lib/server/connectors/session-kind'
|
|
91
90
|
import type { ExecuteChatTurnInput } from '@/lib/server/chat-execution/chat-execution'
|
|
92
91
|
|
|
@@ -320,13 +319,17 @@ function buildLightHeartbeatSystemPrompt(session: Session): string | undefined {
|
|
|
320
319
|
return parts.join('\n\n')
|
|
321
320
|
}
|
|
322
321
|
|
|
323
|
-
function buildAgentSystemPrompt(
|
|
322
|
+
function buildAgentSystemPrompt(
|
|
323
|
+
session: Session,
|
|
324
|
+
options?: { lightweightDirectChat?: boolean },
|
|
325
|
+
): string | undefined {
|
|
324
326
|
if (!session.agentId) return undefined
|
|
325
327
|
const agent = getAgent(session.agentId)
|
|
326
328
|
if (!agent) return undefined
|
|
327
329
|
|
|
328
330
|
const settings = loadSettings()
|
|
329
331
|
const allowSilentReplies = isDirectConnectorSession(session)
|
|
332
|
+
const lightweightDirectChat = options?.lightweightDirectChat === true
|
|
330
333
|
const parts: string[] = []
|
|
331
334
|
const enabledExtensions = listUniversalToolAccessExtensionIds(
|
|
332
335
|
getEnabledCapabilityIds(session).length > 0 ? getEnabledCapabilityIds(session) : getEnabledCapabilityIds(agent),
|
|
@@ -341,7 +344,7 @@ function buildAgentSystemPrompt(session: Session): string | undefined {
|
|
|
341
344
|
if (agent.description) identityLines.push(`Description: ${agent.description}`)
|
|
342
345
|
identityLines.push('I should always embody this identity. I am not just an "Assistant" — I am a persistent companion with my own soul and presence.')
|
|
343
346
|
parts.push(identityLines.join('\n'))
|
|
344
|
-
const continuityBlock = buildIdentityContinuityContext(session, agent)
|
|
347
|
+
const continuityBlock = lightweightDirectChat ? null : buildIdentityContinuityContext(session, agent)
|
|
345
348
|
if (continuityBlock) parts.push(continuityBlock)
|
|
346
349
|
|
|
347
350
|
const runtimeLines = [
|
|
@@ -358,50 +361,57 @@ function buildAgentSystemPrompt(session: Session): string | undefined {
|
|
|
358
361
|
if (agent.soul) parts.push(`## Soul\n${agent.soul}`)
|
|
359
362
|
if (agent.systemPrompt) parts.push(`## System Prompt\n${agent.systemPrompt}`)
|
|
360
363
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
364
|
+
if (!lightweightDirectChat) {
|
|
365
|
+
try {
|
|
366
|
+
const runtimeSkills = resolveRuntimeSkills({
|
|
367
|
+
cwd: session.cwd,
|
|
368
|
+
enabledExtensions,
|
|
369
|
+
agentId: agent.id,
|
|
370
|
+
sessionId: session.id,
|
|
371
|
+
userId: session.user,
|
|
372
|
+
agentSkillIds: agent.skillIds || [],
|
|
373
|
+
storedSkills: loadSkills(),
|
|
374
|
+
selectedSkillId: session.skillRuntimeState?.selectedSkillId || null,
|
|
375
|
+
})
|
|
376
|
+
parts.push(...buildRuntimeSkillPromptBlocks(runtimeSkills))
|
|
377
|
+
} catch {
|
|
378
|
+
// Runtime skills are non-critical during prompt assembly.
|
|
379
|
+
}
|
|
376
380
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
381
|
+
try {
|
|
382
|
+
const wsCtx = buildWorkspaceContext({ cwd: session.cwd })
|
|
383
|
+
if (wsCtx.block) parts.push(wsCtx.block)
|
|
384
|
+
} catch {
|
|
385
|
+
// Workspace context is non-critical.
|
|
386
|
+
}
|
|
382
387
|
}
|
|
383
388
|
|
|
384
389
|
const thinkingHint = [
|
|
385
390
|
'## Output Format',
|
|
386
391
|
'If your model supports internal reasoning/thinking, put all internal analysis inside <think>...</think> tags.',
|
|
387
392
|
'Your final response to the user should be clear and concise.',
|
|
393
|
+
...(lightweightDirectChat
|
|
394
|
+
? ['This is a lightweight direct chat turn. Reply naturally in 1-3 short sentences. Do not delegate, plan, or narrate tools unless the user adds a concrete task that needs that escalation.']
|
|
395
|
+
: []),
|
|
388
396
|
allowSilentReplies
|
|
389
397
|
? 'When you truly have nothing to say, respond with ONLY: NO_MESSAGE'
|
|
390
398
|
: 'For direct user chats, always send a visible reply. Never answer with NO_MESSAGE or HEARTBEAT_OK unless this is an explicit heartbeat poll.',
|
|
391
399
|
]
|
|
392
400
|
parts.push(thinkingHint.join('\n'))
|
|
393
401
|
|
|
394
|
-
if (
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
402
|
+
if (!lightweightDirectChat) {
|
|
403
|
+
if (enabledExtensions.length === 0) {
|
|
404
|
+
parts.push(buildNoToolsGuidance().join('\n'))
|
|
405
|
+
} else {
|
|
406
|
+
parts.push(buildEnabledToolsAutonomyGuidance().join('\n'))
|
|
407
|
+
}
|
|
408
|
+
const toolSectionLines = buildToolSection(enabledExtensions)
|
|
409
|
+
if (toolSectionLines.length > 0) parts.push(['## Tool Discipline', ...toolSectionLines].join('\n'))
|
|
410
|
+
const operatingGuidance = collectCapabilityOperatingGuidance(enabledExtensions)
|
|
411
|
+
if (operatingGuidance.length > 0) parts.push(['## Tool Guidance', ...operatingGuidance].join('\n'))
|
|
412
|
+
const capabilityLines = collectCapabilityDescriptions(enabledExtensions)
|
|
413
|
+
if (capabilityLines.length > 0) parts.push(['## Tool Capabilities', ...capabilityLines].join('\n'))
|
|
398
414
|
}
|
|
399
|
-
const toolSectionLines = buildToolSection(enabledExtensions)
|
|
400
|
-
if (toolSectionLines.length > 0) parts.push(['## Tool Discipline', ...toolSectionLines].join('\n'))
|
|
401
|
-
const operatingGuidance = collectCapabilityOperatingGuidance(enabledExtensions)
|
|
402
|
-
if (operatingGuidance.length > 0) parts.push(['## Tool Guidance', ...operatingGuidance].join('\n'))
|
|
403
|
-
const capabilityLines = collectCapabilityDescriptions(enabledExtensions)
|
|
404
|
-
if (capabilityLines.length > 0) parts.push(['## Tool Capabilities', ...capabilityLines].join('\n'))
|
|
405
415
|
|
|
406
416
|
parts.push([
|
|
407
417
|
'## Heartbeats',
|
|
@@ -452,7 +462,7 @@ export interface PreparedExecutableChatTurn {
|
|
|
452
462
|
appSettings: ReturnType<typeof loadSettings>
|
|
453
463
|
lifecycleRunId: string
|
|
454
464
|
agentForSession: ReturnType<typeof getAgent>
|
|
455
|
-
mission:
|
|
465
|
+
mission: null
|
|
456
466
|
executionBrief: ExecutionBrief
|
|
457
467
|
executionBriefContextBlock?: string
|
|
458
468
|
extensionsForRun: string[]
|
|
@@ -470,6 +480,8 @@ export interface PreparedExecutableChatTurn {
|
|
|
470
480
|
runStartedAt: number
|
|
471
481
|
runMessageStartIndex: number
|
|
472
482
|
toolPolicy: ReturnType<typeof resolveSessionToolPolicy>
|
|
483
|
+
classification: MessageClassification | null
|
|
484
|
+
promptMode: PromptMode
|
|
473
485
|
}
|
|
474
486
|
|
|
475
487
|
export type PreparedChatTurn = PreparedBlockedChatTurn | PreparedExecutableChatTurn
|
|
@@ -488,7 +500,6 @@ export async function prepareChatTurn(input: ExecuteChatTurnInput): Promise<Prep
|
|
|
488
500
|
imagePath,
|
|
489
501
|
imageUrl,
|
|
490
502
|
attachedFiles,
|
|
491
|
-
missionId: explicitMissionId,
|
|
492
503
|
internal = false,
|
|
493
504
|
runId,
|
|
494
505
|
source = 'chat',
|
|
@@ -565,17 +576,7 @@ export async function prepareChatTurn(input: ExecuteChatTurnInput): Promise<Prep
|
|
|
565
576
|
try { syncSessionArchiveMemory(session, { agent: agentForSession }) } catch { /* best-effort */ }
|
|
566
577
|
}
|
|
567
578
|
|
|
568
|
-
const mission =
|
|
569
|
-
session,
|
|
570
|
-
message,
|
|
571
|
-
source,
|
|
572
|
-
internal,
|
|
573
|
-
runId: lifecycleRunId,
|
|
574
|
-
explicitMissionId: explicitMissionId || null,
|
|
575
|
-
})
|
|
576
|
-
if (mission?.id) {
|
|
577
|
-
session.missionId = mission.id
|
|
578
|
-
}
|
|
579
|
+
const mission = null
|
|
579
580
|
const extensionsForRun = toolPolicy.enabledExtensions
|
|
580
581
|
if (runMessageStartIndex === 0) {
|
|
581
582
|
await runCapabilityHook(
|
|
@@ -591,12 +592,6 @@ export async function prepareChatTurn(input: ExecuteChatTurnInput): Promise<Prep
|
|
|
591
592
|
let sessionForRun = JSON.stringify(runtimeCapabilityIds) === JSON.stringify(extensionsForRun)
|
|
592
593
|
? session
|
|
593
594
|
: { ...session, tools: sessionForRunSelection.tools, extensions: sessionForRunSelection.extensions }
|
|
594
|
-
if (mission?.id) {
|
|
595
|
-
sessionForRun = {
|
|
596
|
-
...sessionForRun,
|
|
597
|
-
missionId: mission.id,
|
|
598
|
-
}
|
|
599
|
-
}
|
|
600
595
|
if (agentForSession) {
|
|
601
596
|
const preferredRoute = resolvePrimaryAgentRoute(agentForSession, undefined, {
|
|
602
597
|
preferredGatewayTags: session.routePreferredGatewayTags || [],
|
|
@@ -620,6 +615,24 @@ export async function prepareChatTurn(input: ExecuteChatTurnInput): Promise<Prep
|
|
|
620
615
|
}
|
|
621
616
|
}
|
|
622
617
|
|
|
618
|
+
const turnHistory = getMessages(sessionId)
|
|
619
|
+
const classification = !internal
|
|
620
|
+
? await classifyMessage({
|
|
621
|
+
sessionId,
|
|
622
|
+
agentId: session.agentId || null,
|
|
623
|
+
message,
|
|
624
|
+
history: turnHistory,
|
|
625
|
+
}).catch(() => null as MessageClassification | null)
|
|
626
|
+
: null
|
|
627
|
+
const lightweightDirectChat = classification?.isLightweightDirectChat === true
|
|
628
|
+
&& !internal
|
|
629
|
+
&& source === 'chat'
|
|
630
|
+
&& !isDirectConnectorSession(sessionForRun)
|
|
631
|
+
const promptMode = resolvePromptMode(sessionForRun, { preferMinimalPrompt: lightweightDirectChat })
|
|
632
|
+
if (lightweightDirectChat && sessionForRun.thinkingLevel !== 'minimal') {
|
|
633
|
+
sessionForRun = { ...sessionForRun, thinkingLevel: 'minimal' }
|
|
634
|
+
}
|
|
635
|
+
|
|
623
636
|
if (isHeartbeatRun && input.modelOverride) {
|
|
624
637
|
sessionForRun = { ...sessionForRun, model: input.modelOverride }
|
|
625
638
|
}
|
|
@@ -632,7 +645,10 @@ export async function prepareChatTurn(input: ExecuteChatTurnInput): Promise<Prep
|
|
|
632
645
|
if (extensionsForRun.length > 0) {
|
|
633
646
|
const modelResolvePrompt = heartbeatLightContext
|
|
634
647
|
? (joinSystemPromptBlocks(buildLightHeartbeatSystemPrompt(sessionForRun), executionBriefContextBlock) || '')
|
|
635
|
-
: (joinSystemPromptBlocks(
|
|
648
|
+
: (joinSystemPromptBlocks(
|
|
649
|
+
buildAgentSystemPrompt(sessionForRun, { lightweightDirectChat }),
|
|
650
|
+
executionBriefContextBlock,
|
|
651
|
+
) || '')
|
|
636
652
|
const modelResolve = await runCapabilityBeforeModelResolve(
|
|
637
653
|
{
|
|
638
654
|
session: sessionForRun,
|
|
@@ -724,14 +740,7 @@ export async function prepareChatTurn(input: ExecuteChatTurnInput): Promise<Prep
|
|
|
724
740
|
if (shouldPersistUserMessage) {
|
|
725
741
|
const [linkAnalysis, semantics] = await Promise.all([
|
|
726
742
|
!internal ? runLinkUnderstanding(message) : Promise.resolve([]),
|
|
727
|
-
|
|
728
|
-
sessionId,
|
|
729
|
-
agentId: session.agentId || null,
|
|
730
|
-
message,
|
|
731
|
-
history: getMessages(sessionId),
|
|
732
|
-
})
|
|
733
|
-
.then((classification) => toMessageSemanticsSummary(classification))
|
|
734
|
-
.catch(() => undefined),
|
|
743
|
+
Promise.resolve(toMessageSemanticsSummary(classification)),
|
|
735
744
|
])
|
|
736
745
|
const guardedUserText = guardUntrustedText({
|
|
737
746
|
text: message,
|
|
@@ -745,6 +754,7 @@ export async function prepareChatTurn(input: ExecuteChatTurnInput): Promise<Prep
|
|
|
745
754
|
role: 'user',
|
|
746
755
|
text: guardedUserText,
|
|
747
756
|
time: Date.now(),
|
|
757
|
+
runId: lifecycleRunId,
|
|
748
758
|
imagePath: imagePath || undefined,
|
|
749
759
|
imageUrl: imageUrl || undefined,
|
|
750
760
|
attachedFiles: attachedFiles?.length ? attachedFiles : undefined,
|
|
@@ -805,7 +815,12 @@ export async function prepareChatTurn(input: ExecuteChatTurnInput): Promise<Prep
|
|
|
805
815
|
|
|
806
816
|
const systemPrompt = heartbeatLightContext
|
|
807
817
|
? joinSystemPromptBlocks(buildLightHeartbeatSystemPrompt(sessionForRun), executionBriefContextBlock)
|
|
808
|
-
: (hasExtensions
|
|
818
|
+
: (hasExtensions
|
|
819
|
+
? undefined
|
|
820
|
+
: joinSystemPromptBlocks(
|
|
821
|
+
buildAgentSystemPrompt(sessionForRun, { lightweightDirectChat }),
|
|
822
|
+
executionBriefContextBlock,
|
|
823
|
+
))
|
|
809
824
|
|
|
810
825
|
return {
|
|
811
826
|
kind: 'ready',
|
|
@@ -837,5 +852,7 @@ export async function prepareChatTurn(input: ExecuteChatTurnInput): Promise<Prep
|
|
|
837
852
|
runStartedAt,
|
|
838
853
|
runMessageStartIndex,
|
|
839
854
|
toolPolicy,
|
|
855
|
+
classification,
|
|
856
|
+
promptMode,
|
|
840
857
|
}
|
|
841
858
|
}
|
|
@@ -76,6 +76,8 @@ export async function executePreparedChatTurn(params: {
|
|
|
76
76
|
isAutoRunNoHistory,
|
|
77
77
|
executionBrief,
|
|
78
78
|
executionBriefContextBlock,
|
|
79
|
+
classification,
|
|
80
|
+
promptMode,
|
|
79
81
|
} = prepared
|
|
80
82
|
|
|
81
83
|
const emit = partialPersistence.emit
|
|
@@ -151,6 +153,8 @@ export async function executePreparedChatTurn(params: {
|
|
|
151
153
|
history: heartbeatHistory ?? applyContextClearBoundary(getSessionMessages(sessionId)),
|
|
152
154
|
signal: abortController.signal,
|
|
153
155
|
source,
|
|
156
|
+
classification,
|
|
157
|
+
promptMode,
|
|
154
158
|
})
|
|
155
159
|
fullResponse = result.finalResponse || result.fullText
|
|
156
160
|
} else {
|
|
@@ -102,6 +102,13 @@ function checkUnfinishedToolCallsPending(ctx: ContinuationContext): Continuation
|
|
|
102
102
|
return null
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
+
function checkLightweightDirectChat(ctx: ContinuationContext): ContinuationDecision | null {
|
|
106
|
+
if (ctx.classification?.isLightweightDirectChat !== true) return null
|
|
107
|
+
if (!ctx.state.fullText.trim()) return null
|
|
108
|
+
if (ctx.state.hasToolCalls || ctx.state.streamedToolEvents.length > 0) return null
|
|
109
|
+
return { type: false, requiredToolReminderNames: [] }
|
|
110
|
+
}
|
|
111
|
+
|
|
105
112
|
function checkLoopDetection(ctx: ContinuationContext): ContinuationDecision | null {
|
|
106
113
|
const isToolFrequency = (ctx.state.loopDetectionTriggered?.detector === 'tool_frequency') || ctx.state.toolFrequencyBlocked
|
|
107
114
|
if (!ctx.state.loopDetectionTriggered && !isToolFrequency) return null
|
|
@@ -412,6 +419,7 @@ export function evaluateContinuation(ctx: ContinuationContext): ContinuationDeci
|
|
|
412
419
|
const checks = [
|
|
413
420
|
checkUnfinishedToolCallsPending,
|
|
414
421
|
checkLoopDetection,
|
|
422
|
+
checkLightweightDirectChat,
|
|
415
423
|
checkCoordinatorDelegation,
|
|
416
424
|
checkExecutionContinuation,
|
|
417
425
|
checkRequiredTools,
|
|
@@ -12,8 +12,6 @@ import { canonicalizeExtensionId } from '@/lib/server/tool-aliases'
|
|
|
12
12
|
import { logExecution } from '@/lib/server/execution-log'
|
|
13
13
|
import { perf } from '@/lib/server/runtime/perf'
|
|
14
14
|
import {
|
|
15
|
-
getWalletApprovalBoundaryAction,
|
|
16
|
-
isWalletSimulationResult,
|
|
17
15
|
resolveSuccessfulTerminalToolBoundary,
|
|
18
16
|
updateStreamedToolEvents,
|
|
19
17
|
} from '@/lib/server/chat-execution/chat-streaming-utils'
|
|
@@ -21,7 +19,6 @@ import {
|
|
|
21
19
|
resolveToolAction,
|
|
22
20
|
} from '@/lib/server/chat-execution/memory-mutation-tools'
|
|
23
21
|
import {
|
|
24
|
-
hasStateChangingWalletEvidence,
|
|
25
22
|
countExternalExecutionResearchSteps,
|
|
26
23
|
countDistinctExternalResearchHosts,
|
|
27
24
|
} from '@/lib/server/chat-execution/stream-continuation'
|
|
@@ -328,18 +325,9 @@ export async function processIterationEvents(opts: ProcessIterationEventsOpts):
|
|
|
328
325
|
break
|
|
329
326
|
}
|
|
330
327
|
}
|
|
331
|
-
if (boundedExternalExecutionTask && getWalletApprovalBoundaryAction(outputStr || '')) {
|
|
332
|
-
reachedExecutionBoundary = true
|
|
333
|
-
write(`data: ${JSON.stringify({
|
|
334
|
-
t: 'status',
|
|
335
|
-
text: JSON.stringify({ executionBoundary: 'wallet_approval' }),
|
|
336
|
-
})}\n\n`)
|
|
337
|
-
break
|
|
338
|
-
}
|
|
339
328
|
if (
|
|
340
329
|
boundedExternalExecutionTask
|
|
341
330
|
&& ['http_request', 'web', 'web_search', 'web_fetch', 'browser'].includes(toolName)
|
|
342
|
-
&& !hasStateChangingWalletEvidence(state.streamedToolEvents)
|
|
343
331
|
&& countExternalExecutionResearchSteps(state.streamedToolEvents) >= 5
|
|
344
332
|
&& countDistinctExternalResearchHosts(state.streamedToolEvents) >= 3
|
|
345
333
|
) {
|
|
@@ -350,18 +338,6 @@ export async function processIterationEvents(opts: ProcessIterationEventsOpts):
|
|
|
350
338
|
})}\n\n`)
|
|
351
339
|
break
|
|
352
340
|
}
|
|
353
|
-
if (
|
|
354
|
-
boundedExternalExecutionTask
|
|
355
|
-
&& !hasStateChangingWalletEvidence(state.streamedToolEvents)
|
|
356
|
-
&& isWalletSimulationResult(toolName, outputStr || '')
|
|
357
|
-
) {
|
|
358
|
-
executionFollowthroughReason = 'post_simulation'
|
|
359
|
-
write(`data: ${JSON.stringify({
|
|
360
|
-
t: 'status',
|
|
361
|
-
text: JSON.stringify({ executionBoundary: 'post_simulation' }),
|
|
362
|
-
})}\n\n`)
|
|
363
|
-
break
|
|
364
|
-
}
|
|
365
341
|
}
|
|
366
342
|
}
|
|
367
343
|
|
|
@@ -112,7 +112,7 @@ export function shouldTerminateOnSuccessfulMemoryMutation(params: {
|
|
|
112
112
|
: exactToolName === 'memory_update'
|
|
113
113
|
? 'update'
|
|
114
114
|
: resolveToolAction(params.toolInput)
|
|
115
|
-
if (action !== 'store' && action !== 'update') return false
|
|
115
|
+
if (action !== 'store' && action !== 'update' && action !== 'write') return false
|
|
116
116
|
const output = extractSuggestions(params.toolOutput || '').clean.trim()
|
|
117
117
|
if (!output || /^error[:\s]/i.test(output)) return false
|
|
118
118
|
if (!/^(stored|updated) memory\b/i.test(output)) return false
|
|
@@ -26,7 +26,6 @@ describe('parseClassificationResponse', () => {
|
|
|
26
26
|
taskIntent: 'general',
|
|
27
27
|
isDeliverableTask: true,
|
|
28
28
|
isBroadGoal: false,
|
|
29
|
-
walletIntent: 'none',
|
|
30
29
|
hasHumanSignals: false,
|
|
31
30
|
hasSignificantEvent: false,
|
|
32
31
|
isResearchSynthesis: false,
|
|
@@ -40,7 +39,6 @@ describe('parseClassificationResponse', () => {
|
|
|
40
39
|
assert.ok(result)
|
|
41
40
|
assert.equal(result!.isDeliverableTask, true)
|
|
42
41
|
assert.equal(result!.isBroadGoal, false)
|
|
43
|
-
assert.equal(result!.walletIntent, 'none')
|
|
44
42
|
assert.equal(result!.taskIntent, 'general')
|
|
45
43
|
assert.equal(result!.workType, 'general')
|
|
46
44
|
assert.equal(result!.confidence, 0.9)
|
|
@@ -62,7 +60,6 @@ describe('parseClassificationResponse', () => {
|
|
|
62
60
|
taskIntent: 'general',
|
|
63
61
|
isDeliverableTask: true,
|
|
64
62
|
isBroadGoal: false,
|
|
65
|
-
walletIntent: 'none',
|
|
66
63
|
hasHumanSignals: false,
|
|
67
64
|
hasSignificantEvent: false,
|
|
68
65
|
isResearchSynthesis: false,
|
|
@@ -128,44 +125,6 @@ describe('isBroadGoal', () => {
|
|
|
128
125
|
})
|
|
129
126
|
})
|
|
130
127
|
|
|
131
|
-
// ---------------------------------------------------------------------------
|
|
132
|
-
// hasWalletIntent
|
|
133
|
-
// ---------------------------------------------------------------------------
|
|
134
|
-
|
|
135
|
-
describe('hasWalletIntent', () => {
|
|
136
|
-
it('walletIntent none returns false', () => {
|
|
137
|
-
assert.equal(mod.hasWalletIntent(makeClassification({ walletIntent: 'none' }), ''), false)
|
|
138
|
-
})
|
|
139
|
-
|
|
140
|
-
it('walletIntent read_only returns true', () => {
|
|
141
|
-
assert.equal(mod.hasWalletIntent(makeClassification({ walletIntent: 'read_only' }), ''), true)
|
|
142
|
-
})
|
|
143
|
-
|
|
144
|
-
it('walletIntent transactional returns true', () => {
|
|
145
|
-
assert.equal(mod.hasWalletIntent(makeClassification({ walletIntent: 'transactional' }), ''), true)
|
|
146
|
-
})
|
|
147
|
-
|
|
148
|
-
it('falls back to regex when classification is null', () => {
|
|
149
|
-
assert.equal(mod.hasWalletIntent(null, 'check my wallet balance'), false)
|
|
150
|
-
})
|
|
151
|
-
})
|
|
152
|
-
|
|
153
|
-
// ---------------------------------------------------------------------------
|
|
154
|
-
// hasTransactionalWalletIntent
|
|
155
|
-
// ---------------------------------------------------------------------------
|
|
156
|
-
|
|
157
|
-
describe('hasTransactionalWalletIntent', () => {
|
|
158
|
-
it('only transactional returns true', () => {
|
|
159
|
-
assert.equal(mod.hasTransactionalWalletIntent(makeClassification({ walletIntent: 'transactional' }), ''), true)
|
|
160
|
-
assert.equal(mod.hasTransactionalWalletIntent(makeClassification({ walletIntent: 'read_only' }), ''), false)
|
|
161
|
-
assert.equal(mod.hasTransactionalWalletIntent(makeClassification({ walletIntent: 'none' }), ''), false)
|
|
162
|
-
})
|
|
163
|
-
|
|
164
|
-
it('falls back to regex when classification is null', () => {
|
|
165
|
-
assert.equal(mod.hasTransactionalWalletIntent(null, 'swap 1 ETH for USDC'), false)
|
|
166
|
-
})
|
|
167
|
-
})
|
|
168
|
-
|
|
169
128
|
// ---------------------------------------------------------------------------
|
|
170
129
|
// hasHumanSignals
|
|
171
130
|
// ---------------------------------------------------------------------------
|
|
@@ -227,7 +186,6 @@ describe('classifyMessage', () => {
|
|
|
227
186
|
taskIntent: 'coding',
|
|
228
187
|
isDeliverableTask: true,
|
|
229
188
|
isBroadGoal: false,
|
|
230
|
-
walletIntent: 'none',
|
|
231
189
|
hasHumanSignals: false,
|
|
232
190
|
hasSignificantEvent: false,
|
|
233
191
|
isResearchSynthesis: false,
|
|
@@ -244,7 +202,6 @@ describe('classifyMessage', () => {
|
|
|
244
202
|
assert.ok(result)
|
|
245
203
|
assert.equal(result!.isDeliverableTask, true)
|
|
246
204
|
assert.equal(result!.taskIntent, 'coding')
|
|
247
|
-
assert.equal(result!.walletIntent, 'none')
|
|
248
205
|
assert.equal(result!.workType, 'coding')
|
|
249
206
|
assert.deepEqual(result!.explicitToolRequests, ['shell'])
|
|
250
207
|
})
|
|
@@ -284,7 +241,6 @@ describe('classifyMessage', () => {
|
|
|
284
241
|
taskIntent: 'general',
|
|
285
242
|
isDeliverableTask: false,
|
|
286
243
|
isBroadGoal: false,
|
|
287
|
-
walletIntent: 'none',
|
|
288
244
|
hasHumanSignals: false,
|
|
289
245
|
hasSignificantEvent: false,
|
|
290
246
|
isResearchSynthesis: false,
|
|
@@ -326,7 +282,6 @@ function makeClassification(overrides: Partial<import('@/lib/server/chat-executi
|
|
|
326
282
|
taskIntent: 'general',
|
|
327
283
|
isDeliverableTask: false,
|
|
328
284
|
isBroadGoal: false,
|
|
329
|
-
walletIntent: 'none',
|
|
330
285
|
hasHumanSignals: false,
|
|
331
286
|
hasSignificantEvent: false,
|
|
332
287
|
isResearchSynthesis: false,
|
|
@@ -31,7 +31,7 @@ export const MessageClassificationSchema = z.object({
|
|
|
31
31
|
taskIntent: TaskIntentSchema,
|
|
32
32
|
isDeliverableTask: z.boolean(),
|
|
33
33
|
isBroadGoal: z.boolean(),
|
|
34
|
-
|
|
34
|
+
isLightweightDirectChat: z.boolean().optional().default(false),
|
|
35
35
|
hasHumanSignals: z.boolean(),
|
|
36
36
|
hasSignificantEvent: z.boolean(),
|
|
37
37
|
isResearchSynthesis: z.boolean(),
|
|
@@ -47,7 +47,7 @@ export interface MessageClassification {
|
|
|
47
47
|
taskIntent: MessageTaskIntent
|
|
48
48
|
isDeliverableTask: boolean
|
|
49
49
|
isBroadGoal: boolean
|
|
50
|
-
|
|
50
|
+
isLightweightDirectChat?: boolean
|
|
51
51
|
hasHumanSignals: boolean
|
|
52
52
|
hasSignificantEvent: boolean
|
|
53
53
|
isResearchSynthesis: boolean
|
|
@@ -102,7 +102,7 @@ function buildClassificationPrompt(message: string, recentHistory: string): stri
|
|
|
102
102
|
'- taskIntent: The primary execution intent. Use exactly one of: "coding", "research", "browsing", "outreach", "scheduling", or "general". Choose "coding" for repo/code/build/debug/edit tasks. Choose "research" for gathering current info or synthesizing sources. Choose "browsing" for page navigation, rendered-page inspection, form work, or literal browser workflows. Choose "outreach" for sending/sharing/delivering updates to an external channel. Choose "scheduling" for reminders, recurring work, monitoring, or follow-up scheduling. Choose "general" when none of the above clearly fits.',
|
|
103
103
|
'- isDeliverableTask (bool): The user wants a concrete artifact produced — a document, report, plan, proposal, landing page, dashboard, HTML file, markdown file, brief, copy, screenshots, or similar deliverable. NOT simple Q&A, code fixes, or single-command tasks.',
|
|
104
104
|
'- isBroadGoal (bool): The message describes a broad, multi-step goal (50+ chars, no code blocks, no file paths, no numbered lists). Short questions ending with "?" are NOT broad goals.',
|
|
105
|
-
'-
|
|
105
|
+
'- isLightweightDirectChat (bool): This is a low-signal direct chat turn that should get a natural lightweight reply, such as a greeting, acknowledgment, check-in, or simple social/direct question that does NOT require research, file work, planning, delegation, or tool execution.',
|
|
106
106
|
'- hasHumanSignals (bool): The message contains personal signals — preferences ("I prefer", "call me"), relationships ("my wife", "my partner", "my kid"), life events ("birthday", "wedding", "promotion", "moving", "graduation", "hospital"), or personal disclosures.',
|
|
107
107
|
'- hasSignificantEvent (bool): The message mentions a notable life/work event or milestone (birthday, anniversary, wedding, graduation, promotion, new job, relocation, illness, funeral, travel, house, deadline, launch).',
|
|
108
108
|
'- isResearchSynthesis (bool): The task requires gathering information from multiple sources and synthesizing it — research reports, competitive analysis, market overviews, literature reviews, multi-source comparisons. NOT simple factual lookups.',
|
|
@@ -115,13 +115,13 @@ function buildClassificationPrompt(message: string, recentHistory: string): stri
|
|
|
115
115
|
'',
|
|
116
116
|
'Rules:',
|
|
117
117
|
'- Be conservative. When unsure, default to false/none/empty.',
|
|
118
|
+
'- Mark isLightweightDirectChat true only when a short natural reply is enough and escalating into planning, delegation, or tool execution would be unnecessary.',
|
|
118
119
|
'- A message can be both a deliverable task AND a broad goal.',
|
|
119
|
-
'- "walletIntent" should be "transactional" only if the user wants to execute a state-changing action, not just discuss crypto.',
|
|
120
120
|
'- For "explicitToolRequests", only include tools the user explicitly mentions by name or clear synonym. Do not infer tool needs from the task type.',
|
|
121
121
|
'- Prefer the most execution-relevant taskIntent. Example: "research this and send me a voice note" is "research", not "outreach".',
|
|
122
122
|
'',
|
|
123
123
|
'Output shape:',
|
|
124
|
-
'{"taskIntent":"coding|research|browsing|outreach|scheduling|general","isDeliverableTask":bool,"isBroadGoal":bool,"
|
|
124
|
+
'{"taskIntent":"coding|research|browsing|outreach|scheduling|general","isDeliverableTask":bool,"isBroadGoal":bool,"isLightweightDirectChat":bool,"hasHumanSignals":bool,"hasSignificantEvent":bool,"isResearchSynthesis":bool,"workType":"coding|research|writing|review|operations|general","wantsScreenshots":bool,"wantsOutboundDelivery":bool,"wantsVoiceDelivery":bool,"explicitToolRequests":[],"confidence":0.0-1.0}',
|
|
125
125
|
'',
|
|
126
126
|
recentHistory ? `Recent context:\n${recentHistory}\n` : '',
|
|
127
127
|
`User message: ${JSON.stringify(message)}`,
|
|
@@ -272,10 +272,10 @@ export function toMessageSemanticsSummary(classification: MessageClassification
|
|
|
272
272
|
return {
|
|
273
273
|
taskIntent: classification.taskIntent,
|
|
274
274
|
workType: classification.workType || 'general',
|
|
275
|
-
walletIntent: classification.walletIntent,
|
|
276
275
|
isDeliverableTask: classification.isDeliverableTask,
|
|
277
276
|
isBroadGoal: classification.isBroadGoal,
|
|
278
277
|
isResearchSynthesis: classification.isResearchSynthesis,
|
|
278
|
+
isLightweightDirectChat: classification.isLightweightDirectChat === true,
|
|
279
279
|
hasHumanSignals: classification.hasHumanSignals,
|
|
280
280
|
hasSignificantEvent: classification.hasSignificantEvent,
|
|
281
281
|
wantsScreenshots: classification.wantsScreenshots === true,
|
|
@@ -300,16 +300,6 @@ export function isBroadGoal(classification: MessageClassification | null, messag
|
|
|
300
300
|
return classification?.isBroadGoal === true
|
|
301
301
|
}
|
|
302
302
|
|
|
303
|
-
export function hasWalletIntent(classification: MessageClassification | null, message?: string): boolean {
|
|
304
|
-
void message
|
|
305
|
-
return classification?.walletIntent !== undefined && classification.walletIntent !== 'none'
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
export function hasTransactionalWalletIntent(classification: MessageClassification | null, message?: string): boolean {
|
|
309
|
-
void message
|
|
310
|
-
return classification?.walletIntent === 'transactional'
|
|
311
|
-
}
|
|
312
|
-
|
|
313
303
|
export function hasHumanSignals(classification: MessageClassification | null, transcript?: string): boolean {
|
|
314
304
|
void transcript
|
|
315
305
|
return classification?.hasHumanSignals === true
|
|
@@ -324,3 +314,8 @@ export function isResearchSynthesis(classification: MessageClassification | null
|
|
|
324
314
|
void routingIntent
|
|
325
315
|
return classification?.isResearchSynthesis === true
|
|
326
316
|
}
|
|
317
|
+
|
|
318
|
+
export function isLightweightDirectChat(classification: MessageClassification | null, message?: string): boolean {
|
|
319
|
+
void message
|
|
320
|
+
return classification?.isLightweightDirectChat === true
|
|
321
|
+
}
|
|
@@ -26,4 +26,31 @@ describe('buildAgenticExecutionPolicy', () => {
|
|
|
26
26
|
assert.ok(prompt.includes('use the concrete tool now'))
|
|
27
27
|
assert.ok(prompt.includes('prefer the direct `manage_*` tool'))
|
|
28
28
|
})
|
|
29
|
+
|
|
30
|
+
it('adds lightweight direct-chat guidance when classification marks the turn as lightweight', () => {
|
|
31
|
+
const prompt = buildAgenticExecutionPolicy({
|
|
32
|
+
enabledExtensions: ['memory', 'files', 'delegate'],
|
|
33
|
+
loopMode: 'bounded',
|
|
34
|
+
heartbeatPrompt: 'HEARTBEAT',
|
|
35
|
+
heartbeatIntervalSec: 120,
|
|
36
|
+
userMessage: 'Hello',
|
|
37
|
+
history: [],
|
|
38
|
+
classification: {
|
|
39
|
+
taskIntent: 'general',
|
|
40
|
+
isDeliverableTask: false,
|
|
41
|
+
isBroadGoal: false,
|
|
42
|
+
isLightweightDirectChat: true,
|
|
43
|
+
hasHumanSignals: false,
|
|
44
|
+
hasSignificantEvent: false,
|
|
45
|
+
isResearchSynthesis: false,
|
|
46
|
+
workType: 'general',
|
|
47
|
+
explicitToolRequests: [],
|
|
48
|
+
confidence: 0.98,
|
|
49
|
+
},
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
assert.ok(prompt.includes('## Lightweight Chat'))
|
|
53
|
+
assert.ok(prompt.includes('Reply naturally and briefly.'))
|
|
54
|
+
assert.ok(prompt.includes('prefer 1-3 short sentences'))
|
|
55
|
+
})
|
|
29
56
|
})
|