@swarmclawai/swarmclaw 1.2.3 → 1.2.5
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 +20 -0
- package/bin/daemon-cmd.js +169 -0
- package/bin/server-cmd.js +3 -0
- package/bin/swarmclaw.js +11 -0
- package/package.json +17 -16
- package/src/app/api/agents/[id]/clone/route.ts +3 -32
- package/src/app/api/agents/[id]/route.ts +6 -158
- package/src/app/api/agents/[id]/status/route.ts +2 -3
- package/src/app/api/agents/[id]/thread/route.ts +4 -17
- package/src/app/api/agents/bulk/route.ts +5 -47
- package/src/app/api/agents/route.ts +5 -119
- package/src/app/api/agents/trash/route.ts +13 -24
- package/src/app/api/auth/route.ts +3 -9
- package/src/app/api/autonomy/estop/route.ts +5 -5
- package/src/app/api/chatrooms/[id]/chat/route.ts +11 -5
- package/src/app/api/chatrooms/[id]/route.ts +23 -2
- package/src/app/api/chatrooms/route.ts +13 -2
- package/src/app/api/chats/[id]/clear/route.ts +2 -13
- package/src/app/api/chats/[id]/deploy/route.ts +2 -3
- package/src/app/api/chats/[id]/edit-resend/route.ts +7 -13
- package/src/app/api/chats/[id]/mailbox/route.ts +6 -8
- package/src/app/api/chats/[id]/queue/route.ts +17 -64
- package/src/app/api/chats/[id]/retry/route.ts +4 -22
- package/src/app/api/chats/[id]/route.ts +10 -138
- package/src/app/api/chats/heartbeat/route.ts +2 -1
- package/src/app/api/chats/migrate-messages/route.ts +7 -0
- package/src/app/api/chats/route.ts +13 -134
- package/src/app/api/connectors/[id]/access/route.ts +12 -229
- package/src/app/api/connectors/[id]/doctor/route.ts +1 -1
- package/src/app/api/connectors/[id]/health/route.ts +12 -39
- package/src/app/api/connectors/[id]/route.ts +14 -122
- package/src/app/api/connectors/[id]/webhook/route.ts +1 -1
- package/src/app/api/connectors/doctor/route.ts +1 -1
- package/src/app/api/connectors/route.ts +12 -70
- package/src/app/api/credentials/[id]/route.ts +2 -4
- package/src/app/api/credentials/route.ts +10 -19
- package/src/app/api/daemon/health-check/route.ts +3 -4
- package/src/app/api/daemon/route.ts +10 -8
- package/src/app/api/documents/route.ts +11 -10
- package/src/app/api/external-agents/route.ts +3 -3
- package/src/app/api/gateways/[id]/health/route.ts +2 -3
- package/src/app/api/gateways/[id]/route.ts +7 -122
- package/src/app/api/gateways/route.ts +3 -103
- package/src/app/api/mcp-servers/[id]/tools/route.ts +5 -5
- package/src/app/api/openclaw/dashboard-url/route.ts +8 -16
- package/src/app/api/openclaw/directory/route.ts +2 -2
- package/src/app/api/openclaw/history/route.ts +3 -5
- package/src/app/api/providers/[id]/models/route.test.ts +60 -0
- package/src/app/api/providers/[id]/models/route.ts +33 -1
- package/src/app/api/providers/[id]/route.test.ts +49 -0
- package/src/app/api/providers/[id]/route.ts +30 -1
- package/src/app/api/providers/ollama/route.ts +6 -5
- package/src/app/api/schedules/[id]/route.ts +14 -108
- package/src/app/api/schedules/[id]/run/route.ts +6 -67
- package/src/app/api/schedules/route.ts +9 -51
- package/src/app/api/settings/route.ts +4 -3
- package/src/app/api/setup/check-provider/route.ts +15 -1
- package/src/app/api/setup/openclaw-device/route.ts +2 -2
- package/src/app/api/system/status/route.ts +2 -2
- package/src/app/api/tasks/[id]/route.ts +16 -202
- package/src/app/api/tasks/bulk/route.ts +5 -86
- package/src/app/api/tasks/metrics/route.ts +2 -1
- package/src/app/api/tasks/route.ts +11 -171
- package/src/app/api/upload/route.ts +1 -1
- package/src/app/api/uploads/[filename]/route.ts +1 -1
- package/src/app/api/uploads/route.ts +1 -1
- package/src/app/api/webhooks/[id]/history/route.ts +2 -2
- package/src/app/layout.tsx +9 -6
- package/src/app/protocols/page.tsx +71 -89
- package/src/app/tasks/page.tsx +32 -32
- package/src/cli/index.js +1 -0
- package/src/cli/spec.js +1 -0
- package/src/components/agents/agent-sheet.tsx +51 -25
- package/src/components/agents/inspector-panel.tsx +15 -4
- package/src/components/auth/setup-wizard/index.tsx +27 -18
- package/src/components/auth/setup-wizard/shared.tsx +2 -2
- package/src/components/auth/setup-wizard/step-agents.tsx +51 -38
- package/src/components/auth/setup-wizard/step-connect.tsx +48 -17
- package/src/components/auth/setup-wizard/types.ts +6 -4
- package/src/components/auth/setup-wizard/utils.test.ts +38 -8
- package/src/components/auth/setup-wizard/utils.ts +14 -8
- package/src/components/chatrooms/chatroom-sheet.tsx +16 -276
- package/src/components/connectors/connector-list.tsx +26 -40
- package/src/components/connectors/connector-sheet.tsx +95 -149
- package/src/components/gateways/gateway-sheet.tsx +61 -110
- package/src/components/layout/live-query-sync.tsx +121 -0
- package/src/components/protocols/structured-session-launcher.tsx +24 -45
- package/src/components/providers/app-query-provider.tsx +17 -0
- package/src/components/providers/provider-list.tsx +150 -77
- package/src/components/providers/provider-sheet.tsx +102 -77
- package/src/components/shared/model-combobox.tsx +5 -4
- package/src/components/skills/skill-list.tsx +5 -18
- package/src/components/skills/skill-sheet.tsx +21 -20
- package/src/components/skills/skills-workspace.tsx +48 -87
- package/src/components/tasks/task-card.tsx +20 -13
- package/src/components/tasks/task-column.tsx +22 -7
- package/src/components/tasks/task-list.tsx +8 -11
- package/src/components/tasks/task-sheet.tsx +111 -103
- package/src/features/agents/queries.ts +20 -0
- package/src/features/chatrooms/queries.ts +20 -0
- package/src/features/chats/queries.ts +27 -0
- package/src/features/connectors/queries.ts +145 -0
- package/src/features/credentials/queries.ts +37 -0
- package/src/features/extensions/queries.ts +26 -0
- package/src/features/external-agents/queries.ts +36 -0
- package/src/features/gateways/queries.ts +274 -0
- package/src/features/missions/queries.ts +23 -0
- package/src/features/projects/queries.ts +20 -0
- package/src/features/protocols/queries.ts +149 -0
- package/src/features/providers/queries.ts +142 -0
- package/src/features/settings/queries.ts +20 -0
- package/src/features/skills/queries.ts +182 -0
- package/src/features/tasks/queries.ts +189 -0
- package/src/hooks/use-ws.ts +3 -2
- package/src/lib/agent-provider-options.test.ts +152 -0
- package/src/lib/agent-provider-options.ts +84 -0
- package/src/lib/app/api-client.ts +2 -2
- package/src/lib/providers/index.test.ts +78 -0
- package/src/lib/providers/index.ts +13 -10
- package/src/lib/query/client.ts +17 -0
- package/src/lib/server/agents/agent-runtime-config.ts +6 -6
- package/src/lib/server/agents/agent-service.ts +429 -0
- package/src/lib/server/agents/agent-thread-session.ts +6 -5
- package/src/lib/server/agents/autonomy-contract.ts +1 -4
- package/src/lib/server/agents/delegation-advisory.test.ts +206 -0
- package/src/lib/server/agents/delegation-advisory.ts +251 -0
- package/src/lib/server/agents/main-agent-loop.ts +98 -40
- package/src/lib/server/agents/subagent-runtime.ts +12 -0
- package/src/lib/server/autonomy/supervisor-reflection.test.ts +20 -1
- package/src/lib/server/autonomy/supervisor-reflection.ts +39 -19
- package/src/lib/server/build-llm.ts +7 -15
- package/src/lib/server/capability-router.test.ts +70 -1
- package/src/lib/server/capability-router.ts +24 -99
- package/src/lib/server/chat-execution/chat-execution-utils.ts +0 -15
- package/src/lib/server/chat-execution/chat-streaming-utils.ts +2 -4
- package/src/lib/server/chat-execution/chat-turn-finalization.ts +77 -12
- package/src/lib/server/chat-execution/chat-turn-partial-persistence.ts +4 -4
- package/src/lib/server/chat-execution/chat-turn-preflight.ts +2 -2
- package/src/lib/server/chat-execution/chat-turn-preparation.ts +41 -17
- package/src/lib/server/chat-execution/chat-turn-stream-execution.ts +4 -2
- package/src/lib/server/chat-execution/chat-turn-tool-routing.test.ts +45 -0
- package/src/lib/server/chat-execution/chat-turn-tool-routing.ts +48 -17
- package/src/lib/server/chat-execution/continuation-evaluator.ts +4 -1
- package/src/lib/server/chat-execution/direct-memory-intent.test.ts +9 -0
- package/src/lib/server/chat-execution/direct-memory-intent.ts +12 -2
- package/src/lib/server/chat-execution/message-classifier.test.ts +35 -23
- package/src/lib/server/chat-execution/message-classifier.ts +74 -32
- package/src/lib/server/chat-execution/prompt-builder.test.ts +29 -0
- package/src/lib/server/chat-execution/prompt-builder.ts +37 -2
- package/src/lib/server/chat-execution/prompt-sections.test.ts +56 -0
- package/src/lib/server/chat-execution/prompt-sections.ts +193 -0
- package/src/lib/server/chat-execution/stream-agent-chat.ts +63 -7
- package/src/lib/server/chat-execution/stream-continuation.test.ts +36 -0
- package/src/lib/server/chat-execution/stream-continuation.ts +28 -13
- package/src/lib/server/chatrooms/chatroom-agent-signals.ts +26 -18
- package/src/lib/server/chatrooms/chatroom-helpers.ts +19 -18
- package/src/lib/server/chatrooms/chatroom-repository.ts +16 -0
- package/src/lib/server/chatrooms/chatroom-routing.test.ts +96 -0
- package/src/lib/server/chatrooms/chatroom-routing.ts +207 -53
- package/src/lib/server/chatrooms/mailbox-utils.ts +4 -2
- package/src/lib/server/chatrooms/session-mailbox.ts +50 -40
- package/src/lib/server/chats/chat-session-service.ts +410 -0
- package/src/lib/server/connectors/access.ts +1 -1
- package/src/lib/server/connectors/commands.ts +7 -6
- package/src/lib/server/connectors/connector-inbound.ts +14 -7
- package/src/lib/server/connectors/connector-outbound.ts +16 -11
- package/src/lib/server/connectors/connector-service.ts +453 -0
- package/src/lib/server/connectors/delivery.ts +17 -12
- package/src/lib/server/connectors/inbound-audio-transcription.ts +5 -14
- package/src/lib/server/connectors/media.ts +1 -1
- package/src/lib/server/connectors/response-media.ts +1 -1
- package/src/lib/server/connectors/session-consolidation.ts +11 -7
- package/src/lib/server/connectors/session.ts +9 -7
- package/src/lib/server/connectors/voice-note.ts +2 -1
- package/src/lib/server/context-manager.ts +20 -1
- package/src/lib/server/cost.ts +2 -3
- package/src/lib/server/credentials/credential-repository.ts +43 -4
- package/src/lib/server/credentials/credential-service.ts +112 -0
- package/src/lib/server/daemon/admin-metadata.ts +64 -0
- package/src/lib/server/daemon/controller.ts +577 -0
- package/src/lib/server/daemon/daemon-runtime.ts +352 -0
- package/src/lib/server/daemon/daemon-status-repository.ts +63 -0
- package/src/lib/server/daemon/types.ts +101 -0
- package/src/lib/server/embeddings.ts +3 -9
- package/src/lib/server/eval/agent-regression.ts +3 -2
- package/src/lib/server/eval/runner.ts +2 -2
- package/src/lib/server/execution-brief.test.ts +167 -0
- package/src/lib/server/execution-brief.ts +295 -0
- package/src/lib/server/execution-engine/chat-turn.ts +9 -0
- package/src/lib/server/execution-engine/import-boundary.test.ts +44 -0
- package/src/lib/server/execution-engine/index.ts +35 -0
- package/src/lib/server/execution-engine/task-attempt.ts +303 -0
- package/src/lib/server/execution-engine/types.ts +33 -0
- package/src/lib/server/gateways/gateway-profile-repository.ts +47 -3
- package/src/lib/server/gateways/gateway-profile-service.ts +200 -0
- package/src/lib/server/memory/session-archive-memory.ts +12 -10
- package/src/lib/server/messages/message-repository.ts +330 -0
- package/src/lib/server/missions/mission-service/core.ts +8 -6
- package/src/lib/server/openclaw/agent-resolver.ts +2 -3
- package/src/lib/server/openclaw/doctor.ts +1 -1
- package/src/lib/server/openclaw/gateway.test.ts +10 -1
- package/src/lib/server/openclaw/gateway.ts +5 -14
- package/src/lib/server/openclaw/health.ts +3 -11
- package/src/lib/server/openclaw/sync.ts +8 -6
- package/src/lib/server/persistence/storage-context.ts +3 -0
- package/src/lib/server/protocols/protocol-agent-turn.ts +25 -17
- package/src/lib/server/protocols/protocol-normalization.ts +1 -1
- package/src/lib/server/protocols/protocol-queries.ts +13 -7
- package/src/lib/server/protocols/protocol-run-lifecycle.ts +16 -20
- package/src/lib/server/protocols/protocol-run-repository.ts +81 -0
- package/src/lib/server/protocols/protocol-step-processors.ts +23 -31
- package/src/lib/server/protocols/protocol-swarm.ts +8 -8
- package/src/lib/server/protocols/protocol-template-repository.ts +42 -0
- package/src/lib/server/protocols/protocol-templates.ts +4 -2
- package/src/lib/server/protocols/protocol-types.ts +10 -7
- package/src/lib/server/provider-endpoint.ts +7 -12
- package/src/lib/server/provider-model-discovery.ts +2 -11
- package/src/lib/server/query-expansion.ts +5 -6
- package/src/lib/server/run-context.test.ts +365 -0
- package/src/lib/server/run-context.ts +367 -0
- package/src/lib/server/runtime/heartbeat-service.ts +7 -5
- package/src/lib/server/runtime/queue/core.ts +61 -190
- package/src/lib/server/runtime/run-ledger.ts +8 -0
- package/src/lib/server/runtime/session-run-manager/drain.ts +2 -2
- package/src/lib/server/runtime/session-run-manager/enqueue.ts +6 -0
- package/src/lib/server/runtime/session-run-manager/state.ts +4 -0
- package/src/lib/server/schedules/schedule-route-service.ts +230 -0
- package/src/lib/server/service-result.ts +16 -0
- package/src/lib/server/session-note.ts +2 -3
- package/src/lib/server/session-reset-policy.ts +4 -3
- package/src/lib/server/session-tools/connector.ts +9 -6
- package/src/lib/server/session-tools/context-mgmt.ts +58 -9
- package/src/lib/server/session-tools/crud.ts +162 -10
- package/src/lib/server/session-tools/delegate.ts +1 -1
- package/src/lib/server/session-tools/manage-tasks.test.ts +152 -0
- package/src/lib/server/session-tools/memory.ts +6 -4
- package/src/lib/server/session-tools/session-info.test.ts +56 -0
- package/src/lib/server/session-tools/session-info.ts +119 -12
- package/src/lib/server/session-tools/skill-runtime.ts +3 -1
- package/src/lib/server/session-tools/skills.ts +15 -15
- package/src/lib/server/session-tools/subagent.test.ts +115 -1
- package/src/lib/server/session-tools/subagent.ts +125 -7
- package/src/lib/server/session-tools/team-context.ts +4 -3
- package/src/lib/server/session-tools/wallet.ts +0 -58
- package/src/lib/server/sessions/session-lineage.ts +55 -0
- package/src/lib/server/sessions/session-repository.ts +2 -2
- package/src/lib/server/skills/learned-skills.ts +24 -23
- package/src/lib/server/skills/runtime-skill-resolver.ts +2 -1
- package/src/lib/server/skills/skill-repository.ts +136 -13
- package/src/lib/server/skills/skill-suggestions.ts +25 -28
- package/src/lib/server/storage-normalization.test.ts +42 -215
- package/src/lib/server/storage-normalization.ts +98 -0
- package/src/lib/server/storage.ts +19 -0
- package/src/lib/server/structured-extract.ts +3 -14
- package/src/lib/server/tasks/task-followups.ts +16 -11
- package/src/lib/server/tasks/task-result.test.ts +25 -29
- package/src/lib/server/tasks/task-result.ts +5 -9
- package/src/lib/server/tasks/task-route-service.ts +449 -0
- package/src/lib/server/text-normalization.ts +41 -0
- package/src/lib/server/tool-planning.ts +6 -42
- package/src/lib/server/upload-path.ts +5 -0
- package/src/lib/server/working-state/extraction.ts +614 -0
- package/src/lib/server/working-state/normalization.ts +866 -0
- package/src/lib/server/working-state/prompt.ts +60 -0
- package/src/lib/server/working-state/repository.ts +38 -0
- package/src/lib/server/working-state/service.test.ts +253 -0
- package/src/lib/server/working-state/service.ts +293 -0
- package/src/lib/validation/schemas.ts +1 -0
- package/src/lib/ws-client.ts +3 -3
- package/src/stores/slices/task-slice.ts +1 -4
- package/src/stores/use-chatroom-store.ts +2 -2
- package/src/types/index.ts +288 -22
- package/src/views/settings/section-providers.tsx +2 -2
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import assert from 'node:assert/strict'
|
|
2
|
+
import { describe, it } from 'node:test'
|
|
3
|
+
|
|
4
|
+
import { buildAgenticExecutionPolicy } from '@/lib/server/chat-execution/prompt-builder'
|
|
5
|
+
|
|
6
|
+
describe('buildAgenticExecutionPolicy', () => {
|
|
7
|
+
it('adds a routing matrix that teaches session introspection, durable tracking, and direct routing', () => {
|
|
8
|
+
const prompt = buildAgenticExecutionPolicy({
|
|
9
|
+
enabledExtensions: ['memory', 'manage_sessions', 'manage_tasks', 'manage_skills', 'spawn_subagent'],
|
|
10
|
+
loopMode: 'bounded',
|
|
11
|
+
heartbeatPrompt: 'HEARTBEAT',
|
|
12
|
+
heartbeatIntervalSec: 120,
|
|
13
|
+
userMessage: 'Figure out what tools you have, then continue the task.',
|
|
14
|
+
history: [],
|
|
15
|
+
mode: 'minimal',
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
assert.ok(prompt.includes('## Routing Matrix'))
|
|
19
|
+
assert.ok(prompt.includes('Current-thread facts already visible in this chat'))
|
|
20
|
+
assert.ok(prompt.includes('`memory_search`'))
|
|
21
|
+
assert.ok(prompt.includes('`sessions_tool` action `identity`'))
|
|
22
|
+
assert.ok(prompt.includes('`sessions_tool` action `history`'))
|
|
23
|
+
assert.ok(prompt.includes('`manage_tasks`'))
|
|
24
|
+
assert.ok(prompt.includes('`manage_skills`'))
|
|
25
|
+
assert.ok(prompt.includes('delegate or spawn a subagent'))
|
|
26
|
+
assert.ok(prompt.includes('use the concrete tool now'))
|
|
27
|
+
assert.ok(prompt.includes('prefer the direct `manage_*` tool'))
|
|
28
|
+
})
|
|
29
|
+
})
|
|
@@ -202,10 +202,11 @@ export function shouldForceAttachmentFollowthrough(params: {
|
|
|
202
202
|
enabledExtensions: string[]
|
|
203
203
|
hasToolCalls: boolean
|
|
204
204
|
hasAttachmentContext: boolean
|
|
205
|
+
classification?: MessageClassification | null
|
|
205
206
|
}): boolean {
|
|
206
207
|
if (!params.hasAttachmentContext) return false
|
|
207
208
|
if (params.hasToolCalls) return false
|
|
208
|
-
const decision = routeTaskIntent(params.userMessage, params.enabledExtensions, null)
|
|
209
|
+
const decision = routeTaskIntent(params.userMessage, params.enabledExtensions, null, params.classification ?? null)
|
|
209
210
|
if (decision.intent !== 'research' && decision.intent !== 'browsing') return false
|
|
210
211
|
return decision.preferredTools.some((toolName) => extensionIdMatches(params.enabledExtensions, toolName))
|
|
211
212
|
}
|
|
@@ -326,6 +327,13 @@ export function buildAgenticExecutionPolicy(opts: {
|
|
|
326
327
|
const extensionLines = isMinimal ? [] : buildExtensionCapabilityLines(opts.enabledExtensions, { delegationEnabled: opts.delegationEnabled, agentId: opts.agentId })
|
|
327
328
|
const toolDisciplineLines = buildToolSection(opts.enabledExtensions)
|
|
328
329
|
const hasMemoryTools = opts.enabledExtensions.some((toolId) => (canonicalizeExtensionId(toolId) || toolId) === 'memory')
|
|
330
|
+
const hasManageSessions = opts.enabledExtensions.some((toolId) => (canonicalizeExtensionId(toolId) || toolId) === 'manage_sessions')
|
|
331
|
+
const hasManageTasks = opts.enabledExtensions.some((toolId) => (canonicalizeExtensionId(toolId) || toolId) === 'manage_tasks')
|
|
332
|
+
const hasManageSkills = opts.enabledExtensions.some((toolId) => (canonicalizeExtensionId(toolId) || toolId) === 'manage_skills')
|
|
333
|
+
const hasDelegationTools = opts.enabledExtensions.some((toolId) => {
|
|
334
|
+
const canonical = canonicalizeExtensionId(toolId) || toolId
|
|
335
|
+
return canonical === 'delegate' || canonical === 'spawn_subagent'
|
|
336
|
+
})
|
|
329
337
|
|
|
330
338
|
const parts: string[] = []
|
|
331
339
|
|
|
@@ -351,6 +359,33 @@ export function buildAgenticExecutionPolicy(opts: {
|
|
|
351
359
|
: 'Loop: BOUNDED — execute multiple steps but finish within recursion budget.',
|
|
352
360
|
)
|
|
353
361
|
|
|
362
|
+
if (hasTooling) {
|
|
363
|
+
parts.push(
|
|
364
|
+
'## Routing Matrix',
|
|
365
|
+
'Current-thread facts already visible in this chat: answer directly from the thread before using tools.',
|
|
366
|
+
hasMemoryTools
|
|
367
|
+
? 'Facts from previous conversations: start with `memory_search`, then `memory_get` only for a targeted follow-up read.'
|
|
368
|
+
: 'Facts from previous conversations: rely on the visible thread only and state when memory tools are unavailable.',
|
|
369
|
+
hasManageSessions
|
|
370
|
+
? 'Harness/session context, lineage, project attachment, or enabled-tool questions: use `sessions_tool` action `identity`.'
|
|
371
|
+
: 'Harness/session introspection is limited here; rely on the runtime orientation block and visible context.',
|
|
372
|
+
hasManageSessions
|
|
373
|
+
? 'Earlier messages from this same session that are not already visible in the thread: use `sessions_tool` action `history`.'
|
|
374
|
+
: 'Do not claim hidden session history is checked when `sessions_tool` is unavailable.',
|
|
375
|
+
hasManageTasks
|
|
376
|
+
? 'Durable backlog or resumable progress tracking: use `manage_tasks` for multi-turn work, delegation, or explicit task-board requests.'
|
|
377
|
+
: 'Do not create pseudo-task workflows in prose when task tooling is unavailable.',
|
|
378
|
+
hasManageSkills
|
|
379
|
+
? 'Missing capability, workflow, or environment setup blocker: use `manage_skills` before repeating generic exploration.'
|
|
380
|
+
: 'If a capability is genuinely missing, say so plainly instead of pretending a skill install happened.',
|
|
381
|
+
hasDelegationTools
|
|
382
|
+
? 'Multi-step specialist work: delegate or spawn a subagent instead of doing the whole chain yourself.'
|
|
383
|
+
: 'If delegation tools are unavailable, execute directly with the tools you do have.',
|
|
384
|
+
'For direct reversible execution, use the concrete tool now instead of creating a task or stopping at advice.',
|
|
385
|
+
'When both `manage_platform` and a direct `manage_*` tool are available, prefer the direct `manage_*` tool.',
|
|
386
|
+
)
|
|
387
|
+
}
|
|
388
|
+
|
|
354
389
|
// Sections skipped in minimal mode
|
|
355
390
|
if (!isMinimal) {
|
|
356
391
|
if (hasMemoryTools) {
|
|
@@ -374,7 +409,7 @@ export function buildAgenticExecutionPolicy(opts: {
|
|
|
374
409
|
'Prefer `use_skill` action `run` for executable skills and `use_skill` action `load` only when the skill is guidance-only.',
|
|
375
410
|
)
|
|
376
411
|
}
|
|
377
|
-
if (
|
|
412
|
+
if (hasManageSkills) {
|
|
378
413
|
parts.push(
|
|
379
414
|
'## Skill Resolution',
|
|
380
415
|
'When you are blocked on a missing capability, binary, or environment setup, call `manage_skills` before repeating generic exploration.',
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import assert from 'node:assert/strict'
|
|
2
2
|
import { after, before, describe, it } from 'node:test'
|
|
3
|
+
import fs from 'node:fs'
|
|
4
|
+
import os from 'node:os'
|
|
5
|
+
import path from 'node:path'
|
|
3
6
|
|
|
4
7
|
let mod: typeof import('@/lib/server/chat-execution/prompt-sections')
|
|
5
8
|
|
|
@@ -86,6 +89,59 @@ describe('prompt-sections', () => {
|
|
|
86
89
|
})
|
|
87
90
|
})
|
|
88
91
|
|
|
92
|
+
describe('buildRuntimeOrientationSection', () => {
|
|
93
|
+
it('includes delegated lineage, workspace markers, project context, and routing guidance', () => {
|
|
94
|
+
const cwd = fs.mkdtempSync(path.join(os.tmpdir(), 'swarmclaw-runtime-orientation-'))
|
|
95
|
+
try {
|
|
96
|
+
fs.writeFileSync(path.join(cwd, 'AGENTS.md'), '# Agent notes')
|
|
97
|
+
const result = mod.buildRuntimeOrientationSection({
|
|
98
|
+
session: {
|
|
99
|
+
id: 'child-session',
|
|
100
|
+
cwd,
|
|
101
|
+
provider: 'openai',
|
|
102
|
+
model: 'gpt-5',
|
|
103
|
+
parentSessionId: 'parent-session',
|
|
104
|
+
agentId: 'agent-1',
|
|
105
|
+
} as never,
|
|
106
|
+
promptMode: 'minimal',
|
|
107
|
+
sessionExtensions: ['files', 'manage_sessions', 'codex_cli'],
|
|
108
|
+
toolPolicy: {
|
|
109
|
+
mode: 'balanced',
|
|
110
|
+
requestedExtensions: ['files', 'manage_sessions', 'codex_cli', 'manage_secrets'],
|
|
111
|
+
enabledExtensions: ['files', 'manage_sessions', 'codex_cli'],
|
|
112
|
+
blockedExtensions: [{ tool: 'manage_secrets', reason: 'blocked by policy', source: 'policy' }],
|
|
113
|
+
},
|
|
114
|
+
agent: {
|
|
115
|
+
id: 'agent-1',
|
|
116
|
+
name: 'Builder',
|
|
117
|
+
delegationTargetMode: 'selected',
|
|
118
|
+
delegationTargetAgentIds: ['qa-1', 'ops-1'],
|
|
119
|
+
} as never,
|
|
120
|
+
activeProjectContext: {
|
|
121
|
+
projectId: 'project-1',
|
|
122
|
+
project: { name: 'Northstar' },
|
|
123
|
+
projectRoot: '/workspace/projects/project-1',
|
|
124
|
+
} as never,
|
|
125
|
+
rootSessionId: 'root-session',
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
assert.ok(result.includes('## Runtime Orientation'))
|
|
129
|
+
assert.ok(result.includes('delegated_child'))
|
|
130
|
+
assert.ok(result.includes('prompt=minimal'))
|
|
131
|
+
assert.ok(result.includes('root=root-session'))
|
|
132
|
+
assert.ok(result.includes('Workspace markers: AGENTS.md'))
|
|
133
|
+
assert.ok(result.includes('Active project: Northstar'))
|
|
134
|
+
assert.ok(result.includes('`manage_sessions`'))
|
|
135
|
+
assert.ok(result.includes('`codex_cli`'))
|
|
136
|
+
assert.ok(result.includes('Policy blocked:'))
|
|
137
|
+
assert.ok(result.includes('sessions_tool'))
|
|
138
|
+
assert.ok(result.includes('use `manage_platform` only as fallback'))
|
|
139
|
+
} finally {
|
|
140
|
+
fs.rmSync(cwd, { recursive: true, force: true })
|
|
141
|
+
}
|
|
142
|
+
})
|
|
143
|
+
})
|
|
144
|
+
|
|
89
145
|
// ---- buildProjectSection ----
|
|
90
146
|
describe('buildProjectSection', () => {
|
|
91
147
|
it('returns null for minimal mode', () => {
|
|
@@ -6,13 +6,24 @@
|
|
|
6
6
|
* The main prompt assembly in stream-agent-chat.ts composes these declaratively.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
import fs from 'node:fs'
|
|
10
|
+
import path from 'node:path'
|
|
9
11
|
import type { Session, Agent } from '@/types'
|
|
12
|
+
import type { PromptMode } from '@/lib/server/chat-execution/prompt-mode'
|
|
13
|
+
import type { MessageClassification } from '@/lib/server/chat-execution/message-classifier'
|
|
10
14
|
import type { ActiveProjectContext } from '@/lib/server/project-context'
|
|
15
|
+
import type { SessionToolPolicyDecision } from '@/lib/server/tool-capability-policy'
|
|
11
16
|
import { buildIdentityContinuityContext } from '@/lib/server/identity-continuity'
|
|
17
|
+
import {
|
|
18
|
+
buildDelegationTaskProfile,
|
|
19
|
+
formatDelegationRationale,
|
|
20
|
+
resolveDelegationAdvisory,
|
|
21
|
+
} from '@/lib/server/agents/delegation-advisory'
|
|
12
22
|
import { getAgent, listAgents } from '@/lib/server/agents/agent-repository'
|
|
13
23
|
import { loadSkills } from '@/lib/server/skills/skill-repository'
|
|
14
24
|
import { buildRuntimeSkillPromptBlocks, resolveRuntimeSkills } from '@/lib/server/skills/runtime-skill-resolver'
|
|
15
25
|
import { resolveTeam } from '@/lib/server/agents/team-resolution'
|
|
26
|
+
import { canonicalizeExtensionId } from '@/lib/server/tool-aliases'
|
|
16
27
|
|
|
17
28
|
// ---------------------------------------------------------------------------
|
|
18
29
|
// Identity: agent name, description, continuity, soul, systemPrompt, skills
|
|
@@ -89,6 +100,148 @@ export function buildThinkingSection(
|
|
|
89
100
|
return text ? `## Reasoning Depth\n${text}` : null
|
|
90
101
|
}
|
|
91
102
|
|
|
103
|
+
// ---------------------------------------------------------------------------
|
|
104
|
+
// Runtime Orientation
|
|
105
|
+
// ---------------------------------------------------------------------------
|
|
106
|
+
|
|
107
|
+
const WORKSPACE_MARKER_FILES = [
|
|
108
|
+
'AGENTS.md',
|
|
109
|
+
'BOOTSTRAP.md',
|
|
110
|
+
'HEARTBEAT.md',
|
|
111
|
+
'IDENTITY.md',
|
|
112
|
+
'TOOLS.md',
|
|
113
|
+
'USER.md',
|
|
114
|
+
]
|
|
115
|
+
|
|
116
|
+
function normalizeRuntimeExtensionId(extensionId: string): string {
|
|
117
|
+
const normalized = extensionId.trim().toLowerCase()
|
|
118
|
+
if (!normalized) return ''
|
|
119
|
+
if (normalized === 'delegate_to_claude_code' || normalized === 'claude_code') return 'claude_code'
|
|
120
|
+
if (normalized === 'delegate_to_codex_cli' || normalized === 'codex_cli') return 'codex_cli'
|
|
121
|
+
if (normalized === 'delegate_to_opencode_cli' || normalized === 'opencode_cli') return 'opencode_cli'
|
|
122
|
+
if (normalized === 'delegate_to_gemini_cli' || normalized === 'gemini_cli') return 'gemini_cli'
|
|
123
|
+
if (['session_info', 'sessions_tool', 'whoami_tool', 'search_history_tool'].includes(normalized)) return 'manage_sessions'
|
|
124
|
+
return canonicalizeExtensionId(normalized)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function canonicalizeEnabledExtensions(enabledExtensions: string[]): string[] {
|
|
128
|
+
const seen = new Set<string>()
|
|
129
|
+
const values: string[] = []
|
|
130
|
+
for (const extensionId of enabledExtensions) {
|
|
131
|
+
const normalized = normalizeRuntimeExtensionId(extensionId)
|
|
132
|
+
if (!normalized || seen.has(normalized)) continue
|
|
133
|
+
seen.add(normalized)
|
|
134
|
+
values.push(normalized)
|
|
135
|
+
}
|
|
136
|
+
return values
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function formatInlineCodeList(values: string[], maxItems = 8): string {
|
|
140
|
+
if (values.length === 0) return '(none)'
|
|
141
|
+
const head = values.slice(0, maxItems).map((value) => `\`${value}\``)
|
|
142
|
+
if (values.length > maxItems) head.push(`... +${values.length - maxItems} more`)
|
|
143
|
+
return head.join(', ')
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function formatPolicyBlocks(blocked: SessionToolPolicyDecision['blockedExtensions'], maxItems = 3): string {
|
|
147
|
+
const lines = blocked
|
|
148
|
+
.slice(0, maxItems)
|
|
149
|
+
.map((entry) => `\`${canonicalizeExtensionId(entry.tool)}\` (${entry.reason})`)
|
|
150
|
+
if (blocked.length > maxItems) lines.push(`... +${blocked.length - maxItems} more`)
|
|
151
|
+
return lines.join(', ')
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function collectWorkspaceMarkers(cwd: string | null | undefined): string[] {
|
|
155
|
+
if (typeof cwd !== 'string' || !cwd.trim()) return []
|
|
156
|
+
return WORKSPACE_MARKER_FILES.filter((filename) => {
|
|
157
|
+
try {
|
|
158
|
+
return fs.existsSync(path.join(cwd, filename))
|
|
159
|
+
} catch {
|
|
160
|
+
return false
|
|
161
|
+
}
|
|
162
|
+
})
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function resolveRuntimeSessionKind(params: {
|
|
166
|
+
session: Session
|
|
167
|
+
isHeartbeat?: boolean
|
|
168
|
+
isConnectorSession?: boolean
|
|
169
|
+
}): string {
|
|
170
|
+
if (params.isHeartbeat) return 'heartbeat'
|
|
171
|
+
if (params.isConnectorSession) return 'connector'
|
|
172
|
+
if (params.session.parentSessionId) return 'delegated_child'
|
|
173
|
+
return 'root_chat'
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export function buildRuntimeOrientationSection(params: {
|
|
177
|
+
session: Session
|
|
178
|
+
promptMode: PromptMode
|
|
179
|
+
sessionExtensions: string[]
|
|
180
|
+
toolPolicy?: SessionToolPolicyDecision | null
|
|
181
|
+
agent?: Agent | null
|
|
182
|
+
activeProjectContext?: ActiveProjectContext | null
|
|
183
|
+
rootSessionId?: string | null
|
|
184
|
+
isHeartbeat?: boolean
|
|
185
|
+
isConnectorSession?: boolean
|
|
186
|
+
}): string {
|
|
187
|
+
const { session, agent } = params
|
|
188
|
+
const activeProjectContext = params.activeProjectContext || null
|
|
189
|
+
const enabledExtensions = canonicalizeEnabledExtensions(params.sessionExtensions)
|
|
190
|
+
const workspaceMarkers = collectWorkspaceMarkers(session.cwd)
|
|
191
|
+
const delegationEnabled = enabledExtensions.some((extensionId) =>
|
|
192
|
+
['delegate', 'spawn_subagent', 'claude_code', 'codex_cli', 'opencode_cli', 'gemini_cli'].includes(extensionId),
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
const lines = [
|
|
196
|
+
'## Runtime Orientation',
|
|
197
|
+
`Session: ${resolveRuntimeSessionKind({ session, isHeartbeat: params.isHeartbeat, isConnectorSession: params.isConnectorSession })} | prompt=${params.promptMode} | id=${session.id}`,
|
|
198
|
+
session.parentSessionId || params.rootSessionId
|
|
199
|
+
? `Lineage: parent=${session.parentSessionId || '(none)'} | root=${params.rootSessionId || session.id}`
|
|
200
|
+
: `Lineage: root=${session.id}`,
|
|
201
|
+
`Agent: ${agent?.name || session.agentId || 'Unassigned'}${session.agentId ? ` [id: ${session.agentId}]` : ''}`,
|
|
202
|
+
`Provider/model: ${session.provider} / ${session.model || '(default)'}`,
|
|
203
|
+
`CWD: ${session.cwd || '(none)'}`,
|
|
204
|
+
]
|
|
205
|
+
|
|
206
|
+
if (workspaceMarkers.length > 0) {
|
|
207
|
+
lines.push(`Workspace markers: ${workspaceMarkers.join(', ')}`)
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (activeProjectContext?.projectId) {
|
|
211
|
+
const projectLabel = activeProjectContext.project?.name || activeProjectContext.projectId
|
|
212
|
+
const projectRoot = activeProjectContext.projectRoot ? ` | root=${activeProjectContext.projectRoot}` : ''
|
|
213
|
+
lines.push(`Active project: ${projectLabel}${projectRoot}`)
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
lines.push(`Enabled capabilities now: ${formatInlineCodeList(enabledExtensions)}`)
|
|
217
|
+
|
|
218
|
+
if (params.toolPolicy?.blockedExtensions?.length) {
|
|
219
|
+
lines.push(`Policy blocked: ${formatPolicyBlocks(params.toolPolicy.blockedExtensions)}`)
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (delegationEnabled) {
|
|
223
|
+
const delegateMode = agent?.delegationTargetMode === 'selected' ? 'selected' : 'all'
|
|
224
|
+
const targetIds = Array.isArray(agent?.delegationTargetAgentIds)
|
|
225
|
+
? agent!.delegationTargetAgentIds.filter((value) => typeof value === 'string' && value.trim())
|
|
226
|
+
: []
|
|
227
|
+
lines.push(
|
|
228
|
+
delegateMode === 'selected'
|
|
229
|
+
? `Delegation: enabled (${delegateMode}) | targets=${targetIds.length ? targetIds.join(', ') : '(none configured)'}`
|
|
230
|
+
: 'Delegation: enabled (all allowed targets)',
|
|
231
|
+
)
|
|
232
|
+
} else {
|
|
233
|
+
lines.push('Delegation: disabled in this runtime')
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (enabledExtensions.includes('manage_sessions')) {
|
|
237
|
+
lines.push('Harness inspection: use `sessions_tool` action `identity` for live session/platform context; use action `history` only when you need earlier messages from this same session.')
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
lines.push('Platform routing: prefer direct `manage_*` tools when enabled; use `manage_platform` only as fallback.')
|
|
241
|
+
|
|
242
|
+
return lines.join('\n')
|
|
243
|
+
}
|
|
244
|
+
|
|
92
245
|
// ---------------------------------------------------------------------------
|
|
93
246
|
// Workspace Context (async — dynamic import)
|
|
94
247
|
// ---------------------------------------------------------------------------
|
|
@@ -414,6 +567,43 @@ export function buildCoordinatorSection(
|
|
|
414
567
|
return lines.join('\n')
|
|
415
568
|
}
|
|
416
569
|
|
|
570
|
+
export function buildDelegationRecommendationSection(params: {
|
|
571
|
+
agent: Agent | null | undefined
|
|
572
|
+
classification?: MessageClassification | null
|
|
573
|
+
}): string | null {
|
|
574
|
+
const agent = params.agent
|
|
575
|
+
if (!agent || agent.delegationEnabled !== true) return null
|
|
576
|
+
const profile = buildDelegationTaskProfile({
|
|
577
|
+
classification: params.classification,
|
|
578
|
+
})
|
|
579
|
+
if (!profile.substantial) return null
|
|
580
|
+
|
|
581
|
+
const advisory = resolveDelegationAdvisory({
|
|
582
|
+
currentAgent: agent,
|
|
583
|
+
agents: listAgents(),
|
|
584
|
+
profile,
|
|
585
|
+
delegationTargetMode: agent.delegationTargetMode === 'selected' ? 'selected' : 'all',
|
|
586
|
+
delegationTargetAgentIds: agent.delegationTargetAgentIds || [],
|
|
587
|
+
})
|
|
588
|
+
if (!advisory.shouldDelegate || !advisory.recommended) return null
|
|
589
|
+
|
|
590
|
+
const recommendation = advisory.recommended
|
|
591
|
+
const workLabel = profile.workType === 'general' ? 'substantial work' : `${profile.workType} work`
|
|
592
|
+
const recommendationLabel = advisory.style === 'managerial'
|
|
593
|
+
? 'Managerial Delegation Recommendation'
|
|
594
|
+
: 'Delegation Recommendation'
|
|
595
|
+
|
|
596
|
+
return [
|
|
597
|
+
`## ${recommendationLabel}`,
|
|
598
|
+
`This request looks like ${workLabel}.`,
|
|
599
|
+
`Best-fit teammate: **${recommendation.agentName}** [id: ${recommendation.agentId}]`,
|
|
600
|
+
`Why: ${formatDelegationRationale(recommendation)}.`,
|
|
601
|
+
advisory.style === 'managerial'
|
|
602
|
+
? 'Prefer delegating the execution and keep your direct work to reconnaissance, validation, and synthesis.'
|
|
603
|
+
: 'Prefer delegating the execution and reserve direct tool use for quick lookups or validation.',
|
|
604
|
+
].join('\n')
|
|
605
|
+
}
|
|
606
|
+
|
|
417
607
|
// ---------------------------------------------------------------------------
|
|
418
608
|
// Credential Awareness
|
|
419
609
|
// ---------------------------------------------------------------------------
|
|
@@ -527,3 +717,6 @@ export function buildCliDelegationContext(opts: {
|
|
|
527
717
|
|
|
528
718
|
return parts.join('\n')
|
|
529
719
|
}
|
|
720
|
+
|
|
721
|
+
// Re-export RunContext prompt builder from its canonical home
|
|
722
|
+
export { buildRunContextSection } from '@/lib/server/run-context'
|
|
@@ -3,7 +3,7 @@ import { HumanMessage, AIMessage } from '@langchain/core/messages'
|
|
|
3
3
|
import { createReactAgent } from '@langchain/langgraph/prebuilt'
|
|
4
4
|
import { MemorySaver } from '@langchain/langgraph'
|
|
5
5
|
import { DEFAULT_HEARTBEAT_INTERVAL_SEC } from '@/lib/runtime/heartbeat-defaults'
|
|
6
|
-
import { getAgent } from '@/lib/server/agents/agent-repository'
|
|
6
|
+
import { getAgent, listAgents } from '@/lib/server/agents/agent-repository'
|
|
7
7
|
import { buildSessionTools } from '@/lib/server/session-tools'
|
|
8
8
|
import { buildChatModel } from '@/lib/server/build-llm'
|
|
9
9
|
import { loadSettings } from '@/lib/server/settings/settings-repository'
|
|
@@ -18,6 +18,7 @@ import { truncateToolResultText } from '@/lib/server/chat-execution/tool-result-
|
|
|
18
18
|
import {
|
|
19
19
|
buildIdentitySection,
|
|
20
20
|
buildThinkingSection,
|
|
21
|
+
buildRuntimeOrientationSection,
|
|
21
22
|
buildWorkspaceSection,
|
|
22
23
|
buildAgentAwarenessSection,
|
|
23
24
|
buildSituationalSection,
|
|
@@ -27,13 +28,15 @@ import {
|
|
|
27
28
|
buildProactiveMemorySection,
|
|
28
29
|
buildCoordinatorSection,
|
|
29
30
|
buildCredentialAwarenessSection,
|
|
31
|
+
buildDelegationRecommendationSection,
|
|
32
|
+
buildRunContextSection,
|
|
30
33
|
} from '@/lib/server/chat-execution/prompt-sections'
|
|
31
34
|
|
|
32
35
|
import { log } from '@/lib/server/logger'
|
|
33
36
|
import { logExecution } from '@/lib/server/execution-log'
|
|
34
37
|
import { buildCurrentDateTimePromptContext } from '@/lib/server/prompt-runtime-context'
|
|
35
38
|
import { expandExtensionIds } from '@/lib/server/tool-aliases'
|
|
36
|
-
import type { Session, Message } from '@/types'
|
|
39
|
+
import type { ExecutionBrief, Session, Message } from '@/types'
|
|
37
40
|
import { getEnabledCapabilityIds } from '@/lib/capability-selection'
|
|
38
41
|
import { enqueueSystemEvent } from '@/lib/server/runtime/system-events'
|
|
39
42
|
import { resolveActiveProjectContext } from '@/lib/server/project-context'
|
|
@@ -89,6 +92,7 @@ import {
|
|
|
89
92
|
DEFAULT_PROMPT_BUDGET,
|
|
90
93
|
MINIMAL_PROMPT_BUDGET,
|
|
91
94
|
} from '@/lib/server/chat-execution/prompt-budget'
|
|
95
|
+
import { resolveSessionLineageIds } from '@/lib/server/sessions/session-lineage'
|
|
92
96
|
import { IterationTimers } from '@/lib/server/chat-execution/iteration-timers'
|
|
93
97
|
import { processIterationEvents } from '@/lib/server/chat-execution/iteration-event-handler'
|
|
94
98
|
import { evaluateContinuation } from '@/lib/server/chat-execution/continuation-evaluator'
|
|
@@ -101,6 +105,11 @@ import {
|
|
|
101
105
|
isResearchSynthesis as classifiedIsResearchSynthesis,
|
|
102
106
|
type MessageClassification,
|
|
103
107
|
} from '@/lib/server/chat-execution/message-classifier'
|
|
108
|
+
import {
|
|
109
|
+
buildDelegationTaskProfile,
|
|
110
|
+
formatDelegationRationale,
|
|
111
|
+
resolveDelegationAdvisory,
|
|
112
|
+
} from '@/lib/server/agents/delegation-advisory'
|
|
104
113
|
|
|
105
114
|
const TAG = 'stream-agent-chat'
|
|
106
115
|
|
|
@@ -174,6 +183,7 @@ interface StreamAgentChatOpts {
|
|
|
174
183
|
attachedFiles?: string[]
|
|
175
184
|
apiKey: string | null
|
|
176
185
|
systemPrompt?: string
|
|
186
|
+
executionBrief?: ExecutionBrief | null
|
|
177
187
|
extraSystemContext?: string[]
|
|
178
188
|
write: (data: string) => void
|
|
179
189
|
history: Message[]
|
|
@@ -213,7 +223,7 @@ export async function streamAgentChat(opts: StreamAgentChatOpts): Promise<Stream
|
|
|
213
223
|
|
|
214
224
|
async function streamAgentChatCore(opts: StreamAgentChatOpts): Promise<StreamAgentChatResult> {
|
|
215
225
|
const startTs = Date.now()
|
|
216
|
-
const { session, message, imagePath, imageUrl, attachedFiles, apiKey, systemPrompt, extraSystemContext, write, history, fallbackCredentialIds, signal } = opts
|
|
226
|
+
const { session, message, imagePath, imageUrl, attachedFiles, apiKey, systemPrompt, executionBrief, extraSystemContext, write, history, fallbackCredentialIds, signal } = opts
|
|
217
227
|
const isHeartbeat = isHeartbeatSource(opts.source)
|
|
218
228
|
const promptMode: PromptMode = opts.promptMode ?? resolvePromptMode(session)
|
|
219
229
|
const isMinimalPrompt = promptMode === 'minimal'
|
|
@@ -221,7 +231,7 @@ async function streamAgentChatCore(opts: StreamAgentChatOpts): Promise<StreamAge
|
|
|
221
231
|
const rawExtensions = getEnabledCapabilityIds(session)
|
|
222
232
|
const hasShellCapability = rawExtensions.some((toolId) => ['shell', 'execute_command'].includes(String(toolId)))
|
|
223
233
|
const extensionManager = getExtensionManager()
|
|
224
|
-
const
|
|
234
|
+
const requestedExtensions = expandExtensionIds([
|
|
225
235
|
...rawExtensions,
|
|
226
236
|
...(hasShellCapability ? ['process'] : []),
|
|
227
237
|
]).filter((id) => !extensionManager.isExplicitlyDisabled(id))
|
|
@@ -249,10 +259,15 @@ async function streamAgentChatCore(opts: StreamAgentChatOpts): Promise<StreamAge
|
|
|
249
259
|
|
|
250
260
|
// Build agent prompt
|
|
251
261
|
const settings = loadSettings()
|
|
262
|
+
const toolPolicy = resolveSessionToolPolicy(requestedExtensions, settings)
|
|
263
|
+
const blockedExtensionIds = new Set(expandExtensionIds(toolPolicy.blockedExtensions.map((entry) => entry.tool)))
|
|
264
|
+
const sessionExtensions = expandExtensionIds(toolPolicy.enabledExtensions)
|
|
265
|
+
.filter((id) => !blockedExtensionIds.has(id))
|
|
266
|
+
.filter((id) => !extensionManager.isExplicitlyDisabled(id))
|
|
252
267
|
const requestedToolPreflightResponse = resolveRequestedToolPreflightResponse({
|
|
253
268
|
message,
|
|
254
269
|
enabledExtensions: sessionExtensions,
|
|
255
|
-
toolPolicy
|
|
270
|
+
toolPolicy,
|
|
256
271
|
appSettings: settings,
|
|
257
272
|
internal: false,
|
|
258
273
|
source: 'chat',
|
|
@@ -296,6 +311,7 @@ async function streamAgentChatCore(opts: StreamAgentChatOpts): Promise<StreamAge
|
|
|
296
311
|
// -------------------------------------------------------------------------
|
|
297
312
|
const promptParts: string[] = []
|
|
298
313
|
const hasProvidedSystemPrompt = typeof systemPrompt === 'string' && systemPrompt.trim().length > 0
|
|
314
|
+
const hasCanonicalExecutionBrief = Boolean(executionBrief)
|
|
299
315
|
const currentThreadRecallRequest = isCurrentThreadRecallRequest(message)
|
|
300
316
|
const hasAttachmentContext = Boolean(
|
|
301
317
|
imagePath
|
|
@@ -347,18 +363,37 @@ async function streamAgentChatCore(opts: StreamAgentChatOpts): Promise<StreamAge
|
|
|
347
363
|
// Composable prompt sections — each builder returns string | null (or string[])
|
|
348
364
|
const thinkingBlock = buildThinkingSection(agentThinkingLevel, isMinimalPrompt)
|
|
349
365
|
if (thinkingBlock) promptParts.push(thinkingBlock)
|
|
366
|
+
const { rootSessionId } = resolveSessionLineageIds(session)
|
|
350
367
|
|
|
351
368
|
// Async sections — run concurrently where possible
|
|
352
|
-
const [workspaceBlock, awarenessBlock, situationalBlock, extensionAuditBlock] = await Promise.all([
|
|
369
|
+
const [runtimeOrientationBlock, workspaceBlock, awarenessBlock, situationalBlock, extensionAuditBlock] = await Promise.all([
|
|
370
|
+
buildRuntimeOrientationSection({
|
|
371
|
+
session,
|
|
372
|
+
promptMode,
|
|
373
|
+
sessionExtensions,
|
|
374
|
+
toolPolicy,
|
|
375
|
+
agent: sessionAgent,
|
|
376
|
+
activeProjectContext,
|
|
377
|
+
rootSessionId,
|
|
378
|
+
isHeartbeat,
|
|
379
|
+
isConnectorSession,
|
|
380
|
+
}),
|
|
353
381
|
!hasProvidedSystemPrompt ? buildWorkspaceSection(session, isMinimalPrompt, agentHeartbeatEnabled) : null,
|
|
354
382
|
buildAgentAwarenessSection(session, sessionExtensions, isMinimalPrompt),
|
|
355
383
|
buildSituationalSection(session, isMinimalPrompt),
|
|
356
384
|
buildExtensionAccessAuditSection(sessionExtensions, agentMcpDisabledTools, isMinimalPrompt),
|
|
357
385
|
])
|
|
386
|
+
if (runtimeOrientationBlock) promptParts.push(runtimeOrientationBlock)
|
|
358
387
|
if (workspaceBlock) promptParts.push(workspaceBlock)
|
|
359
388
|
if (awarenessBlock) promptParts.push(awarenessBlock)
|
|
360
389
|
if (situationalBlock) promptParts.push(situationalBlock)
|
|
361
390
|
|
|
391
|
+
// RunContext — structured working memory that survives compaction
|
|
392
|
+
if (!hasProvidedSystemPrompt && !hasCanonicalExecutionBrief) {
|
|
393
|
+
const runContextBlock = buildRunContextSection(session.runContext, isMinimalPrompt)
|
|
394
|
+
if (runContextBlock) promptParts.push(runContextBlock)
|
|
395
|
+
}
|
|
396
|
+
|
|
362
397
|
// Extra system context — always included (caller-provided context is always relevant)
|
|
363
398
|
if (Array.isArray(extraSystemContext)) {
|
|
364
399
|
for (const block of extraSystemContext) {
|
|
@@ -393,6 +428,22 @@ async function streamAgentChatCore(opts: StreamAgentChatOpts): Promise<StreamAge
|
|
|
393
428
|
|
|
394
429
|
// Await classification before building the agentic execution policy
|
|
395
430
|
const classification = await classificationPromise
|
|
431
|
+
const delegationAdvisory = sessionAgent && agentDelegationEnabled
|
|
432
|
+
? resolveDelegationAdvisory({
|
|
433
|
+
currentAgent: sessionAgent,
|
|
434
|
+
agents: listAgents(),
|
|
435
|
+
profile: buildDelegationTaskProfile({ classification }),
|
|
436
|
+
delegationTargetMode: agentDelegationTargetMode,
|
|
437
|
+
delegationTargetAgentIds: agentDelegationTargetAgentIds,
|
|
438
|
+
})
|
|
439
|
+
: null
|
|
440
|
+
const delegationRecommendationBlock = !isMinimalPrompt
|
|
441
|
+
? buildDelegationRecommendationSection({
|
|
442
|
+
agent: sessionAgent,
|
|
443
|
+
classification,
|
|
444
|
+
})
|
|
445
|
+
: null
|
|
446
|
+
if (delegationRecommendationBlock) promptParts.push(delegationRecommendationBlock)
|
|
396
447
|
|
|
397
448
|
promptParts.push(
|
|
398
449
|
buildAgenticExecutionPolicy({
|
|
@@ -813,7 +864,7 @@ async function streamAgentChatCore(opts: StreamAgentChatOpts): Promise<StreamAge
|
|
|
813
864
|
// -------------------------------------------------------------------------
|
|
814
865
|
const state = new ChatTurnState()
|
|
815
866
|
const limits = new ContinuationLimits(isConnectorSession, isHeartbeat)
|
|
816
|
-
const routingDecision = routeTaskIntent(message, sessionExtensions, null)
|
|
867
|
+
const routingDecision = routeTaskIntent(message, sessionExtensions, null, classification)
|
|
817
868
|
const explicitRequiredToolNames = getExplicitRequiredToolNames(message, sessionExtensions)
|
|
818
869
|
|
|
819
870
|
const boundedExternalExecutionTask = classifiedHasTransactionalWalletIntent(classification, message)
|
|
@@ -1071,6 +1122,8 @@ async function streamAgentChatCore(opts: StreamAgentChatOpts): Promise<StreamAge
|
|
|
1071
1122
|
likelyResearchSynthesisTask,
|
|
1072
1123
|
abortControllerAborted: abortController.signal.aborted,
|
|
1073
1124
|
classification,
|
|
1125
|
+
delegationEnabled: agentDelegationEnabled,
|
|
1126
|
+
delegationPreferenceActive: delegationAdvisory?.shouldDelegate === true,
|
|
1074
1127
|
})
|
|
1075
1128
|
shouldContinue = decision.type
|
|
1076
1129
|
if (decision.requiredToolReminderNames.length > 0) {
|
|
@@ -1123,6 +1176,9 @@ async function streamAgentChatCore(opts: StreamAgentChatOpts): Promise<StreamAge
|
|
|
1123
1176
|
cwd: session.cwd,
|
|
1124
1177
|
frequencyLimitedToolName,
|
|
1125
1178
|
sessionExtensions,
|
|
1179
|
+
isCoordinatorAgent,
|
|
1180
|
+
recommendedDelegateName: delegationAdvisory?.recommended?.agentName || null,
|
|
1181
|
+
delegationRationale: formatDelegationRationale(delegationAdvisory?.recommended),
|
|
1126
1182
|
})
|
|
1127
1183
|
|
|
1128
1184
|
if (continuationPrompt) {
|
|
@@ -290,5 +290,41 @@ describe('stream-continuation', () => {
|
|
|
290
290
|
assert.equal(typeof prompt, 'string')
|
|
291
291
|
assert.ok(prompt!.includes('memory write'))
|
|
292
292
|
})
|
|
293
|
+
|
|
294
|
+
it('renders a coordinator delegation nudge with the recommended delegate', () => {
|
|
295
|
+
const prompt = mod.buildContinuationPrompt({
|
|
296
|
+
type: 'coordinator_delegation_nudge',
|
|
297
|
+
message: 'test',
|
|
298
|
+
fullText: '',
|
|
299
|
+
toolEvents: [],
|
|
300
|
+
requiredToolReminderNames: [],
|
|
301
|
+
isCoordinatorAgent: true,
|
|
302
|
+
recommendedDelegateName: 'Builder',
|
|
303
|
+
delegationRationale: 'capability match: coding; worker role fits execution-heavy work',
|
|
304
|
+
})
|
|
305
|
+
|
|
306
|
+
assert.ok(prompt)
|
|
307
|
+
assert.ok(prompt!.includes('Builder'))
|
|
308
|
+
assert.ok(prompt!.includes('orchestrate'))
|
|
309
|
+
assert.ok(prompt!.includes('Reason: capability match: coding; worker role fits execution-heavy work.'))
|
|
310
|
+
})
|
|
311
|
+
|
|
312
|
+
it('renders an advisory delegation nudge for non-coordinator agents', () => {
|
|
313
|
+
const prompt = mod.buildContinuationPrompt({
|
|
314
|
+
type: 'coordinator_delegation_nudge',
|
|
315
|
+
message: 'test',
|
|
316
|
+
fullText: '',
|
|
317
|
+
toolEvents: [],
|
|
318
|
+
requiredToolReminderNames: [],
|
|
319
|
+
isCoordinatorAgent: false,
|
|
320
|
+
recommendedDelegateName: 'Reviewer',
|
|
321
|
+
delegationRationale: 'capability match: review; currently idle',
|
|
322
|
+
})
|
|
323
|
+
|
|
324
|
+
assert.ok(prompt)
|
|
325
|
+
assert.ok(prompt!.includes('materially better fit'))
|
|
326
|
+
assert.ok(prompt!.includes('Reviewer'))
|
|
327
|
+
assert.ok(prompt!.includes('reconnaissance, validation, or synthesis'))
|
|
328
|
+
})
|
|
293
329
|
})
|
|
294
330
|
})
|