@swarmclawai/swarmclaw 1.2.4 → 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 +14 -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]/route.test.ts +49 -0
- 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 +5 -5
- package/src/components/auth/setup-wizard/index.tsx +4 -4
- package/src/components/auth/setup-wizard/step-agents.tsx +1 -1
- package/src/components/auth/setup-wizard/step-connect.tsx +1 -1
- package/src/components/auth/setup-wizard/utils.ts +1 -1
- 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 +60 -61
- package/src/components/providers/provider-sheet.tsx +74 -56
- 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/app/api-client.ts +2 -2
- package/src/lib/query/client.ts +17 -0
- package/src/lib/server/agents/agent-runtime-config.ts +1 -1
- 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 +44 -267
- package/src/lib/server/storage-normalization.ts +75 -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 +277 -12
|
@@ -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
|
})
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
import fs from 'node:fs'
|
|
9
9
|
import os from 'node:os'
|
|
10
10
|
import path from 'node:path'
|
|
11
|
-
import type { MessageToolEvent } from '@/types'
|
|
11
|
+
import type { Message, MessageToolEvent } from '@/types'
|
|
12
12
|
import { extractSuggestions } from '@/lib/server/suggestions'
|
|
13
13
|
import { isSuccessfulMemoryMutationToolEvent } from '@/lib/server/chat-execution/memory-mutation-tools'
|
|
14
14
|
import type { MessageClassification } from '@/lib/server/chat-execution/message-classifier'
|
|
@@ -108,7 +108,7 @@ function looksLikeIncompleteDeliverableResponse(text: string): boolean {
|
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
function hasRecentDeliverableContext(
|
|
111
|
-
history:
|
|
111
|
+
history: Message[] | undefined,
|
|
112
112
|
userMessage: string,
|
|
113
113
|
): boolean {
|
|
114
114
|
if (!Array.isArray(history) || history.length === 0) return false
|
|
@@ -116,7 +116,7 @@ function hasRecentDeliverableContext(
|
|
|
116
116
|
if (!trimmed || trimmed.length > 160) return false
|
|
117
117
|
return history
|
|
118
118
|
.slice(-8)
|
|
119
|
-
.some((entry) => entry.role === 'user' &&
|
|
119
|
+
.some((entry) => entry.role === 'user' && entry.semantics?.isDeliverableTask === true)
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
const ARTIFACT_PATH_EXT_RE = /\.(?:md|txt|html?|json|csv|ya?ml|xml|pdf|png|jpe?g|webp|gif|svg|zip|ts|tsx|js|jsx|mjs|cjs|py|sql|sh)$/i
|
|
@@ -245,7 +245,7 @@ export function shouldForceExternalExecutionFollowthrough(params: {
|
|
|
245
245
|
toolEvents: MessageToolEvent[]
|
|
246
246
|
classification?: MessageClassification | null
|
|
247
247
|
}): boolean {
|
|
248
|
-
const isTransactional = params.classification
|
|
248
|
+
const isTransactional = params.classification?.walletIntent === 'transactional'
|
|
249
249
|
if (!isTransactional) return false
|
|
250
250
|
if (!params.hasToolCalls || params.toolEvents.length < 4) return false
|
|
251
251
|
if (hasStateChangingWalletEvidence(params.toolEvents)) return false
|
|
@@ -266,7 +266,7 @@ export function shouldForceExternalExecutionKickoffFollowthrough(params: {
|
|
|
266
266
|
toolEvents: MessageToolEvent[]
|
|
267
267
|
classification?: MessageClassification | null
|
|
268
268
|
}): boolean {
|
|
269
|
-
const isTransactional = params.classification
|
|
269
|
+
const isTransactional = params.classification?.walletIntent === 'transactional'
|
|
270
270
|
if (!isTransactional) return false
|
|
271
271
|
if (params.hasToolCalls || params.toolEvents.length > 0) return false
|
|
272
272
|
|
|
@@ -289,11 +289,11 @@ export function shouldForceDeliverableFollowthrough(params: {
|
|
|
289
289
|
hasToolCalls: boolean
|
|
290
290
|
toolEvents: MessageToolEvent[]
|
|
291
291
|
cwd?: string
|
|
292
|
-
history?:
|
|
292
|
+
history?: Message[]
|
|
293
293
|
classification?: MessageClassification | null
|
|
294
294
|
}): boolean {
|
|
295
295
|
const recentDeliverableContext = hasRecentDeliverableContext(params.history, params.userMessage)
|
|
296
|
-
const isDeliverable = params.classification
|
|
296
|
+
const isDeliverable = params.classification?.isDeliverableTask === true
|
|
297
297
|
const deliverableIntent = isDeliverable || recentDeliverableContext
|
|
298
298
|
const requestedArtifacts = getRequestedArtifactStatus({
|
|
299
299
|
userMessage: params.userMessage,
|
|
@@ -744,6 +744,9 @@ export function buildContinuationPrompt(params: {
|
|
|
744
744
|
cwd?: string
|
|
745
745
|
frequencyLimitedToolName?: string
|
|
746
746
|
sessionExtensions?: string[]
|
|
747
|
+
isCoordinatorAgent?: boolean
|
|
748
|
+
recommendedDelegateName?: string | null
|
|
749
|
+
delegationRationale?: string | null
|
|
747
750
|
}): string | null {
|
|
748
751
|
switch (params.type) {
|
|
749
752
|
case 'memory_write_followthrough':
|
|
@@ -819,12 +822,24 @@ export function buildContinuationPrompt(params: {
|
|
|
819
822
|
].filter(Boolean).join('\n')
|
|
820
823
|
|
|
821
824
|
case 'coordinator_delegation_nudge':
|
|
822
|
-
return
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
825
|
+
return params.isCoordinatorAgent
|
|
826
|
+
? [
|
|
827
|
+
'IMPORTANT: You have specialist workers available but you have been doing substantial work directly with tools.',
|
|
828
|
+
params.recommendedDelegateName
|
|
829
|
+
? `Delegate the remaining execution via \`spawn_subagent\` to ${params.recommendedDelegateName} now.`
|
|
830
|
+
: 'You MUST delegate the remaining work via `spawn_subagent` to the appropriate specialist worker NOW.',
|
|
831
|
+
'As a coordinator, your job is to orchestrate — not to do the work yourself. Direct tool use is only for quick lookups and validation.',
|
|
832
|
+
params.delegationRationale ? `Reason: ${params.delegationRationale}.` : '',
|
|
833
|
+
'Review the workers listed in your system prompt and delegate immediately.',
|
|
834
|
+
].filter(Boolean).join('\n')
|
|
835
|
+
: [
|
|
836
|
+
'You have delegation available and a teammate is a materially better fit for the remaining work.',
|
|
837
|
+
params.recommendedDelegateName
|
|
838
|
+
? `Use \`spawn_subagent\` to hand the execution to ${params.recommendedDelegateName} now.`
|
|
839
|
+
: 'Use `spawn_subagent` to hand the execution to the best-fit teammate now.',
|
|
840
|
+
params.delegationRationale ? `Reason: ${params.delegationRationale}.` : '',
|
|
841
|
+
'Keep your direct tool use to reconnaissance, validation, or synthesis unless delegation is blocked.',
|
|
842
|
+
].filter(Boolean).join('\n')
|
|
828
843
|
|
|
829
844
|
case 'loop_recovery': {
|
|
830
845
|
const freqTool = params.frequencyLimitedToolName
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Chatroom, Agent } from '@/types'
|
|
2
|
-
import {
|
|
2
|
+
import { patchChatroom } from '@/lib/server/chatrooms/chatroom-repository'
|
|
3
3
|
import { notify } from '@/lib/server/ws-hub'
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -35,25 +35,33 @@ export function isImplicitlyMentioned(text: string, agent: Agent): boolean {
|
|
|
35
35
|
* Useful for acknowledging tasks or agreeing with teammates.
|
|
36
36
|
*/
|
|
37
37
|
export function addAgentReaction(chatroomId: string, messageId: string, agentId: string, emoji: string) {
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
const updated = patchChatroom(chatroomId, (current) => {
|
|
39
|
+
const chatroom = current as Chatroom | null
|
|
40
|
+
if (!chatroom) return null
|
|
41
|
+
const message = chatroom.messages.find(m => m.id === messageId)
|
|
42
|
+
if (!message) return chatroom
|
|
43
|
+
if (message.reactions.some(r => r.reactorId === agentId && r.emoji === emoji)) return chatroom
|
|
41
44
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
45
|
+
return {
|
|
46
|
+
...chatroom,
|
|
47
|
+
messages: chatroom.messages.map((entry) => (
|
|
48
|
+
entry.id !== messageId
|
|
49
|
+
? entry
|
|
50
|
+
: {
|
|
51
|
+
...entry,
|
|
52
|
+
reactions: [
|
|
53
|
+
...entry.reactions,
|
|
54
|
+
{
|
|
55
|
+
emoji,
|
|
56
|
+
reactorId: agentId,
|
|
57
|
+
time: Date.now(),
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
}
|
|
61
|
+
)),
|
|
62
|
+
}
|
|
52
63
|
})
|
|
53
|
-
|
|
54
|
-
chatrooms[chatroomId] = chatroom
|
|
55
|
-
saveChatrooms(chatrooms)
|
|
56
|
-
notify(`chatroom:${chatroomId}`)
|
|
64
|
+
if (updated) notify(`chatroom:${chatroomId}`)
|
|
57
65
|
}
|
|
58
66
|
|
|
59
67
|
/**
|