@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.
Files changed (269) hide show
  1. package/README.md +54 -23
  2. package/next.config.ts +1 -0
  3. package/package.json +4 -3
  4. package/scripts/easy-setup.mjs +1 -1
  5. package/scripts/postinstall.mjs +1 -1
  6. package/skills/swarmclaw.md +115 -0
  7. package/skills/tools/browser.md +131 -0
  8. package/skills/tools/execute.md +98 -0
  9. package/skills/tools/files.md +98 -0
  10. package/skills/tools/memory.md +104 -0
  11. package/skills/tools/platform.md +144 -0
  12. package/skills/tools/skills.md +83 -0
  13. package/src/app/agents/[id]/page.tsx +1 -18
  14. package/src/app/api/agents/thread-route.test.ts +0 -1
  15. package/src/app/api/approvals/route.test.ts +6 -22
  16. package/src/app/api/chats/[id]/messages/route.ts +23 -19
  17. package/src/app/api/chats/messages-route.test.ts +105 -51
  18. package/src/app/api/connectors/route.ts +2 -2
  19. package/src/app/api/mcp-servers/[id]/test/route.ts +3 -2
  20. package/src/app/api/openclaw/deploy/route.ts +2 -0
  21. package/src/app/api/portability/export/route.ts +8 -0
  22. package/src/app/api/portability/import/route.test.ts +80 -0
  23. package/src/app/api/portability/import/route.ts +28 -0
  24. package/src/app/api/settings/route.ts +0 -2
  25. package/src/app/api/setup/doctor/route.ts +4 -4
  26. package/src/app/api/wallets/[id]/route.ts +15 -157
  27. package/src/app/api/wallets/generate/route.ts +22 -0
  28. package/src/app/api/wallets/route.test.ts +147 -0
  29. package/src/app/api/wallets/route.ts +13 -95
  30. package/src/app/autonomy/page.tsx +2 -57
  31. package/src/app/protocols/page.tsx +2 -21
  32. package/src/app/settings/page.tsx +0 -9
  33. package/src/app/wallets/page.tsx +105 -5
  34. package/src/cli/index.js +21 -33
  35. package/src/cli/spec.js +19 -30
  36. package/src/components/agents/agent-chat-list.tsx +23 -1
  37. package/src/components/agents/agent-sheet.tsx +2 -40
  38. package/src/components/agents/inspector-panel.tsx +165 -131
  39. package/src/components/chat/chat-area.tsx +38 -9
  40. package/src/components/chat/chat-card.tsx +0 -31
  41. package/src/components/chat/message-bubble.tsx +1 -108
  42. package/src/components/chat/message-list.tsx +33 -19
  43. package/src/components/connectors/connector-sheet.tsx +25 -1
  44. package/src/components/gateways/gateway-sheet.tsx +5 -2
  45. package/src/components/layout/sidebar-rail.tsx +6 -10
  46. package/src/components/projects/project-detail.tsx +3 -35
  47. package/src/components/projects/tabs/overview-tab.tsx +3 -59
  48. package/src/components/projects/tabs/work-tab.tsx +7 -77
  49. package/src/components/protocols/structured-session-launcher.tsx +1 -22
  50. package/src/components/shared/connector-platform-icon.tsx +1 -0
  51. package/src/components/tasks/task-card.tsx +4 -34
  52. package/src/components/tasks/task-sheet.tsx +6 -36
  53. package/src/components/wallets/wallet-list.tsx +150 -0
  54. package/src/lib/agent-execute-defaults.test.ts +24 -0
  55. package/src/lib/agent-execute-defaults.ts +62 -0
  56. package/src/lib/app/navigation.test.ts +0 -13
  57. package/src/lib/app/navigation.ts +2 -7
  58. package/src/lib/app/view-constants.ts +14 -19
  59. package/src/lib/chat/queued-message-queue.test.ts +134 -1
  60. package/src/lib/chat/queued-message-queue.ts +77 -2
  61. package/src/lib/server/agents/agent-service.ts +5 -0
  62. package/src/lib/server/agents/agent-thread-session.ts +0 -1
  63. package/src/lib/server/agents/delegation-advisory.test.ts +0 -1
  64. package/src/lib/server/agents/delegation-jobs.test.ts +0 -69
  65. package/src/lib/server/agents/delegation-jobs.ts +0 -25
  66. package/src/lib/server/agents/main-agent-loop.ts +1 -49
  67. package/src/lib/server/agents/subagent-runtime.ts +0 -1
  68. package/src/lib/server/approval-match.ts +0 -85
  69. package/src/lib/server/approvals.test.ts +6 -6
  70. package/src/lib/server/approvals.ts +0 -6
  71. package/src/lib/server/autonomy/supervisor-reflection.test.ts +0 -1
  72. package/src/lib/server/builtin-extensions.ts +1 -2
  73. package/src/lib/server/capability-router.test.ts +0 -2
  74. package/src/lib/server/chat-execution/chat-execution-advanced.test.ts +1 -1
  75. package/src/lib/server/chat-execution/chat-execution-tool-events.test.ts +15 -14
  76. package/src/lib/server/chat-execution/chat-execution-types.ts +0 -2
  77. package/src/lib/server/chat-execution/chat-execution-utils.ts +2 -4
  78. package/src/lib/server/chat-execution/chat-streaming-utils.ts +2 -30
  79. package/src/lib/server/chat-execution/chat-turn-finalization.ts +1 -36
  80. package/src/lib/server/chat-execution/chat-turn-preparation.ts +81 -64
  81. package/src/lib/server/chat-execution/chat-turn-stream-execution.ts +4 -0
  82. package/src/lib/server/chat-execution/continuation-evaluator.ts +8 -0
  83. package/src/lib/server/chat-execution/iteration-event-handler.ts +0 -24
  84. package/src/lib/server/chat-execution/memory-mutation-tools.ts +1 -1
  85. package/src/lib/server/chat-execution/message-classifier.test.ts +0 -45
  86. package/src/lib/server/chat-execution/message-classifier.ts +11 -16
  87. package/src/lib/server/chat-execution/prompt-builder.test.ts +27 -0
  88. package/src/lib/server/chat-execution/prompt-builder.ts +14 -31
  89. package/src/lib/server/chat-execution/prompt-mode.test.ts +24 -0
  90. package/src/lib/server/chat-execution/prompt-mode.ts +5 -1
  91. package/src/lib/server/chat-execution/prompt-sections.ts +0 -1
  92. package/src/lib/server/chat-execution/situational-awareness.test.ts +2 -73
  93. package/src/lib/server/chat-execution/situational-awareness.ts +4 -38
  94. package/src/lib/server/chat-execution/stream-agent-chat.test.ts +13 -126
  95. package/src/lib/server/chat-execution/stream-agent-chat.ts +46 -21
  96. package/src/lib/server/chat-execution/stream-continuation.test.ts +4 -52
  97. package/src/lib/server/chat-execution/stream-continuation.ts +6 -48
  98. package/src/lib/server/chatrooms/chatroom-routing.test.ts +4 -0
  99. package/src/lib/server/chatrooms/session-mailbox.ts +0 -10
  100. package/src/lib/server/chats/chat-session-service.ts +3 -5
  101. package/src/lib/server/connectors/connector-inbound.ts +0 -1
  102. package/src/lib/server/connectors/connector-lifecycle.ts +19 -3
  103. package/src/lib/server/connectors/connector-service.ts +39 -9
  104. package/src/lib/server/connectors/discord.ts +2 -2
  105. package/src/lib/server/connectors/matrix.ts +3 -2
  106. package/src/lib/server/connectors/signal.ts +5 -4
  107. package/src/lib/server/connectors/slack.ts +10 -9
  108. package/src/lib/server/connectors/swarmdock-bidding.ts +74 -0
  109. package/src/lib/server/connectors/swarmdock-payloads.test.ts +85 -0
  110. package/src/lib/server/connectors/swarmdock-secret.test.ts +128 -0
  111. package/src/lib/server/connectors/swarmdock-secret.ts +152 -0
  112. package/src/lib/server/connectors/swarmdock-tasks.ts +119 -0
  113. package/src/lib/server/connectors/swarmdock.ts +255 -0
  114. package/src/lib/server/connectors/teams.ts +3 -2
  115. package/src/lib/server/connectors/telegram.ts +4 -4
  116. package/src/lib/server/connectors/whatsapp.ts +2 -2
  117. package/src/lib/server/daemon/controller.ts +7 -0
  118. package/src/lib/server/execution-brief.test.ts +2 -25
  119. package/src/lib/server/execution-brief.ts +12 -35
  120. package/src/lib/server/execution-engine/task-attempt.ts +0 -1
  121. package/src/lib/server/gateways/gateway-profile-service.ts +19 -1
  122. package/src/lib/server/messages/message-repository.test.ts +70 -0
  123. package/src/lib/server/messages/message-repository.ts +11 -6
  124. package/src/lib/server/openclaw/deploy.ts +32 -2
  125. package/src/lib/server/persistence/storage-context.ts +0 -5
  126. package/src/lib/server/plugins-advanced.test.ts +1 -2
  127. package/src/lib/server/portability/export.ts +109 -0
  128. package/src/lib/server/portability/import.ts +159 -0
  129. package/src/lib/server/protocols/protocol-normalization.ts +0 -4
  130. package/src/lib/server/protocols/protocol-queries.ts +0 -6
  131. package/src/lib/server/protocols/protocol-run-lifecycle.ts +4 -32
  132. package/src/lib/server/protocols/protocol-service.ts +0 -1
  133. package/src/lib/server/protocols/protocol-step-helpers.ts +0 -4
  134. package/src/lib/server/protocols/protocol-step-processors.ts +0 -6
  135. package/src/lib/server/protocols/protocol-swarm.ts +0 -2
  136. package/src/lib/server/protocols/protocol-types.ts +0 -2
  137. package/src/lib/server/provider-health.ts +1 -10
  138. package/src/lib/server/runtime/daemon-state/core.ts +0 -9
  139. package/src/lib/server/runtime/daemon-state.test.ts +0 -35
  140. package/src/lib/server/runtime/heartbeat-service.ts +3 -23
  141. package/src/lib/server/runtime/process-manager.ts +13 -9
  142. package/src/lib/server/runtime/queue/core.ts +11 -33
  143. package/src/lib/server/runtime/runtime-storage-write-paths.test.ts +6 -6
  144. package/src/lib/server/runtime/scheduler.ts +0 -13
  145. package/src/lib/server/runtime/session-run-manager/drain.ts +0 -24
  146. package/src/lib/server/runtime/session-run-manager/enqueue.ts +0 -1
  147. package/src/lib/server/runtime/session-run-manager/queries.ts +15 -1
  148. package/src/lib/server/runtime/session-run-manager/recovery.ts +0 -1
  149. package/src/lib/server/runtime/session-run-manager.test.ts +58 -28
  150. package/src/lib/server/sandbox/session-runtime.test.ts +18 -1
  151. package/src/lib/server/sandbox/session-runtime.ts +40 -28
  152. package/src/lib/server/session-tools/autonomy-tools.test.ts +7 -9
  153. package/src/lib/server/session-tools/context.ts +1 -1
  154. package/src/lib/server/session-tools/credential-env.ts +109 -0
  155. package/src/lib/server/session-tools/crud.ts +3 -17
  156. package/src/lib/server/session-tools/delegate.ts +0 -4
  157. package/src/lib/server/session-tools/edit_file.ts +3 -2
  158. package/src/lib/server/session-tools/execute.test.ts +58 -0
  159. package/src/lib/server/session-tools/execute.ts +334 -0
  160. package/src/lib/server/session-tools/files-tool.ts +635 -0
  161. package/src/lib/server/session-tools/index.ts +14 -8
  162. package/src/lib/server/session-tools/memory-tool.ts +242 -0
  163. package/src/lib/server/session-tools/memory.ts +1 -1
  164. package/src/lib/server/session-tools/openclaw-nodes.ts +3 -2
  165. package/src/lib/server/session-tools/openclaw-workspace.ts +3 -2
  166. package/src/lib/server/session-tools/platform-tool.ts +617 -0
  167. package/src/lib/server/session-tools/session-info.ts +3 -2
  168. package/src/lib/server/session-tools/session-tools-wiring.test.ts +3 -4
  169. package/src/lib/server/session-tools/shell.ts +7 -122
  170. package/src/lib/server/session-tools/skills-tool.ts +396 -0
  171. package/src/lib/server/session-tools/team-context.ts +0 -3
  172. package/src/lib/server/session-tools/web.ts +2 -2
  173. package/src/lib/server/storage-normalization.ts +10 -0
  174. package/src/lib/server/storage.ts +18 -45
  175. package/src/lib/server/tasks/task-checkout.ts +59 -0
  176. package/src/lib/server/tasks/task-lifecycle.ts +2 -0
  177. package/src/lib/server/tasks/task-route-service.ts +4 -26
  178. package/src/lib/server/tasks/task-service.ts +0 -7
  179. package/src/lib/server/tool-aliases.ts +2 -2
  180. package/src/lib/server/tool-capability-policy-advanced.test.ts +13 -6
  181. package/src/lib/server/tool-capability-policy.test.ts +2 -1
  182. package/src/lib/server/tool-capability-policy.ts +60 -35
  183. package/src/lib/server/tool-planning.ts +11 -12
  184. package/src/lib/server/universal-tool-access.ts +0 -1
  185. package/src/lib/server/wallets/wallet-crypto.ts +33 -0
  186. package/src/lib/server/wallets/wallet-repository.ts +24 -0
  187. package/src/lib/server/wallets/wallet-service.ts +119 -0
  188. package/src/lib/server/working-state/extraction.ts +8 -42
  189. package/src/lib/server/working-state/normalization.ts +10 -103
  190. package/src/lib/server/working-state/service.ts +12 -21
  191. package/src/lib/setup-defaults.ts +5 -0
  192. package/src/lib/strip-internal-metadata.test.ts +1 -1
  193. package/src/lib/strip-internal-metadata.ts +1 -1
  194. package/src/lib/tool-definitions.ts +1 -1
  195. package/src/lib/validation/schemas.test.ts +16 -0
  196. package/src/lib/validation/schemas.ts +49 -2
  197. package/src/stores/slices/data-slice.ts +5 -1
  198. package/src/stores/slices/ui-slice.ts +0 -4
  199. package/src/stores/use-chat-store.test.ts +231 -0
  200. package/src/stores/use-chat-store.ts +62 -13
  201. package/src/types/agent.ts +264 -0
  202. package/src/types/app-settings.ts +173 -0
  203. package/src/types/approval.ts +25 -0
  204. package/src/types/connector.ts +188 -0
  205. package/src/types/extension.ts +386 -0
  206. package/src/types/index.ts +16 -3555
  207. package/src/types/message.ts +56 -0
  208. package/src/types/misc.ts +737 -0
  209. package/src/types/protocol.ts +420 -0
  210. package/src/types/provider.ts +52 -0
  211. package/src/types/run.ts +180 -0
  212. package/src/types/schedule.ts +59 -0
  213. package/src/types/session.ts +215 -0
  214. package/src/types/skill.ts +157 -0
  215. package/src/types/swarmdock.ts +29 -0
  216. package/src/types/task.ts +144 -0
  217. package/src/types/working-state.ts +204 -0
  218. package/src/views/settings/section-heartbeat.tsx +2 -2
  219. package/src/views/settings/section-runtime-loop.tsx +0 -14
  220. package/src/app/api/canvas/[sessionId]/route.ts +0 -35
  221. package/src/app/api/missions/[id]/actions/route.ts +0 -31
  222. package/src/app/api/missions/[id]/events/route.ts +0 -14
  223. package/src/app/api/missions/[id]/route.ts +0 -10
  224. package/src/app/api/missions/route.test.ts +0 -244
  225. package/src/app/api/missions/route.ts +0 -57
  226. package/src/app/api/wallets/[id]/approve/route.ts +0 -79
  227. package/src/app/api/wallets/[id]/balance-history/route.ts +0 -18
  228. package/src/app/api/wallets/[id]/send/route.ts +0 -113
  229. package/src/app/api/wallets/[id]/transactions/route.ts +0 -18
  230. package/src/app/missions/[id]/page.tsx +0 -3
  231. package/src/app/missions/page.tsx +0 -685
  232. package/src/components/canvas/canvas-panel.tsx +0 -267
  233. package/src/components/wallets/wallet-approval-dialog.tsx +0 -107
  234. package/src/components/wallets/wallet-panel.tsx +0 -1010
  235. package/src/components/wallets/wallet-section.tsx +0 -260
  236. package/src/features/missions/queries.ts +0 -23
  237. package/src/lib/canvas-content.test.ts +0 -360
  238. package/src/lib/canvas-content.ts +0 -198
  239. package/src/lib/server/canvas-content.test.ts +0 -32
  240. package/src/lib/server/canvas-content.ts +0 -6
  241. package/src/lib/server/ethereum.ts +0 -591
  242. package/src/lib/server/evm-swap.ts +0 -476
  243. package/src/lib/server/missions/mission-intent.test.ts +0 -63
  244. package/src/lib/server/missions/mission-intent.ts +0 -569
  245. package/src/lib/server/missions/mission-repository.ts +0 -74
  246. package/src/lib/server/missions/mission-service/actions.ts +0 -6
  247. package/src/lib/server/missions/mission-service/bindings.ts +0 -9
  248. package/src/lib/server/missions/mission-service/context.ts +0 -4
  249. package/src/lib/server/missions/mission-service/core.ts +0 -2271
  250. package/src/lib/server/missions/mission-service/queries.ts +0 -12
  251. package/src/lib/server/missions/mission-service/recovery.ts +0 -5
  252. package/src/lib/server/missions/mission-service/ticks.ts +0 -9
  253. package/src/lib/server/missions/mission-service.test.ts +0 -888
  254. package/src/lib/server/missions/mission-service.ts +0 -6
  255. package/src/lib/server/session-tools/canvas.ts +0 -105
  256. package/src/lib/server/session-tools/sandbox.ts +0 -281
  257. package/src/lib/server/session-tools/wallet-tool.test.ts +0 -150
  258. package/src/lib/server/session-tools/wallet.ts +0 -1287
  259. package/src/lib/server/solana.ts +0 -327
  260. package/src/lib/server/wallet/wallet-execution.test.ts +0 -198
  261. package/src/lib/server/wallet/wallet-portfolio.test.ts +0 -98
  262. package/src/lib/server/wallet/wallet-portfolio.ts +0 -772
  263. package/src/lib/server/wallet/wallet-service.test.ts +0 -81
  264. package/src/lib/server/wallet/wallet-service.ts +0 -225
  265. package/src/lib/wallet/wallet-transactions.test.ts +0 -75
  266. package/src/lib/wallet/wallet-transactions.ts +0 -43
  267. package/src/lib/wallet/wallet.test.ts +0 -333
  268. package/src/lib/wallet/wallet.ts +0 -183
  269. 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(session: Session): string | undefined {
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
- try {
362
- const runtimeSkills = resolveRuntimeSkills({
363
- cwd: session.cwd,
364
- enabledExtensions,
365
- agentId: agent.id,
366
- sessionId: session.id,
367
- userId: session.user,
368
- agentSkillIds: agent.skillIds || [],
369
- storedSkills: loadSkills(),
370
- selectedSkillId: session.skillRuntimeState?.selectedSkillId || null,
371
- })
372
- parts.push(...buildRuntimeSkillPromptBlocks(runtimeSkills))
373
- } catch {
374
- // Runtime skills are non-critical during prompt assembly.
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
- try {
378
- const wsCtx = buildWorkspaceContext({ cwd: session.cwd })
379
- if (wsCtx.block) parts.push(wsCtx.block)
380
- } catch {
381
- // Workspace context is non-critical.
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 (enabledExtensions.length === 0) {
395
- parts.push(buildNoToolsGuidance().join('\n'))
396
- } else {
397
- parts.push(buildEnabledToolsAutonomyGuidance().join('\n'))
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: Awaited<ReturnType<typeof resolveMissionForTurn>>
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 = await resolveMissionForTurn({
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(buildAgentSystemPrompt(sessionForRun), executionBriefContextBlock) || '')
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
- classifyMessage({
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 ? undefined : joinSystemPromptBlocks(buildAgentSystemPrompt(sessionForRun), executionBriefContextBlock))
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
- walletIntent: z.enum(['none', 'read_only', 'transactional']),
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
- walletIntent: 'none' | 'read_only' | 'transactional'
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
- '- walletIntent: "none" if no crypto/wallet/trading context. "read_only" if mentioning wallet/crypto but only for checking balances, viewing transactions, or research. "transactional" if the user wants to swap, trade, buy, sell, mint, claim, deposit, withdraw, bridge, or execute a transaction.',
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,"walletIntent":"none|read_only|transactional","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}',
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
  })