@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,167 @@
|
|
|
1
|
+
import assert from 'node:assert/strict'
|
|
2
|
+
import { test } from 'node:test'
|
|
3
|
+
|
|
4
|
+
import { buildExecutionBrief, buildExecutionBriefContextBlock, serializeExecutionBriefForDelegation } from './execution-brief'
|
|
5
|
+
import type { Mission, Session, SessionWorkingState } from '@/types'
|
|
6
|
+
|
|
7
|
+
test('buildExecutionBrief prefers working state and folds in mission and run-context fallback data', () => {
|
|
8
|
+
const session = {
|
|
9
|
+
id: 's1',
|
|
10
|
+
name: 'Main Session',
|
|
11
|
+
cwd: '/tmp/project',
|
|
12
|
+
user: 'tester',
|
|
13
|
+
provider: 'openai',
|
|
14
|
+
model: 'gpt-test',
|
|
15
|
+
claudeSessionId: null,
|
|
16
|
+
messages: [],
|
|
17
|
+
createdAt: 1,
|
|
18
|
+
lastActiveAt: 1,
|
|
19
|
+
runContext: {
|
|
20
|
+
objective: 'Fallback objective',
|
|
21
|
+
constraints: ['Do not change the API'],
|
|
22
|
+
keyFacts: ['The build already passes locally.'],
|
|
23
|
+
discoveries: ['The failing path is only used in staging.'],
|
|
24
|
+
failedApproaches: ['Restarting the worker did not help.'],
|
|
25
|
+
currentPlan: ['Fallback step'],
|
|
26
|
+
completedSteps: [],
|
|
27
|
+
blockers: ['Waiting on staging credentials.'],
|
|
28
|
+
parentContext: 'Parent asked for a contained fix.',
|
|
29
|
+
updatedAt: 1,
|
|
30
|
+
version: 1,
|
|
31
|
+
},
|
|
32
|
+
} satisfies Partial<Session> as Session
|
|
33
|
+
|
|
34
|
+
const mission = {
|
|
35
|
+
id: 'm1',
|
|
36
|
+
source: 'chat',
|
|
37
|
+
objective: 'Ship the release fix',
|
|
38
|
+
status: 'active',
|
|
39
|
+
phase: 'executing',
|
|
40
|
+
currentStep: 'Verify staging auth',
|
|
41
|
+
successCriteria: ['Restore staging deploys'],
|
|
42
|
+
waitState: {
|
|
43
|
+
kind: 'approval',
|
|
44
|
+
reason: 'Waiting for deploy approval.',
|
|
45
|
+
},
|
|
46
|
+
createdAt: 1,
|
|
47
|
+
updatedAt: 1,
|
|
48
|
+
} satisfies Partial<Mission> as Mission
|
|
49
|
+
|
|
50
|
+
const workingState = {
|
|
51
|
+
sessionId: 's1',
|
|
52
|
+
missionId: 'm1',
|
|
53
|
+
objective: 'Ship the release fix safely',
|
|
54
|
+
summary: 'Auth mismatch isolated to staging.',
|
|
55
|
+
constraints: ['Do not change the API'],
|
|
56
|
+
successCriteria: ['Restore staging deploys'],
|
|
57
|
+
status: 'progress',
|
|
58
|
+
nextAction: 'Request deploy approval',
|
|
59
|
+
planSteps: [
|
|
60
|
+
{ id: 'p1', text: 'Request deploy approval', status: 'active', createdAt: 1, updatedAt: 1 },
|
|
61
|
+
{ id: 'p2', text: 'Roll staging credentials', status: 'resolved', createdAt: 1, updatedAt: 1 },
|
|
62
|
+
],
|
|
63
|
+
confirmedFacts: [
|
|
64
|
+
{ id: 'f1', statement: 'Auth mismatch isolated to staging.', source: 'tool', status: 'active', createdAt: 1, updatedAt: 1 },
|
|
65
|
+
],
|
|
66
|
+
artifacts: [
|
|
67
|
+
{ id: 'a1', label: 'deploy.log', kind: 'file', path: '/tmp/project/deploy.log', status: 'active', createdAt: 1, updatedAt: 1 },
|
|
68
|
+
],
|
|
69
|
+
decisions: [],
|
|
70
|
+
blockers: [
|
|
71
|
+
{ id: 'b1', summary: 'Deploy approval is pending.', kind: 'approval', nextAction: 'Request deploy approval', status: 'active', createdAt: 1, updatedAt: 1 },
|
|
72
|
+
],
|
|
73
|
+
openQuestions: [],
|
|
74
|
+
hypotheses: [],
|
|
75
|
+
evidenceRefs: [
|
|
76
|
+
{ id: 'e1', type: 'tool', summary: 'Checked deploy logs', value: '403 from staging auth', sessionId: 's1', createdAt: 1 },
|
|
77
|
+
],
|
|
78
|
+
createdAt: 1,
|
|
79
|
+
updatedAt: 1,
|
|
80
|
+
} satisfies SessionWorkingState
|
|
81
|
+
|
|
82
|
+
const brief = buildExecutionBrief({ session, mission, workingState })
|
|
83
|
+
|
|
84
|
+
assert.equal(brief.objective, 'Ship the release fix safely')
|
|
85
|
+
assert.equal(brief.summary, 'Auth mismatch isolated to staging.')
|
|
86
|
+
assert.equal(brief.status, 'progress')
|
|
87
|
+
assert.equal(brief.nextAction, 'Request deploy approval')
|
|
88
|
+
assert.equal(brief.plan[0]?.text, 'Request deploy approval')
|
|
89
|
+
assert.equal(brief.blockers[0], 'Deploy approval is pending. | next: Request deploy approval')
|
|
90
|
+
assert.ok(brief.blockers.some((entry) => /waiting for deploy approval/i.test(entry)))
|
|
91
|
+
assert.ok(brief.facts.some((entry) => /auth mismatch isolated to staging/i.test(entry)))
|
|
92
|
+
assert.ok(brief.artifacts.some((entry) => /deploy\.log/i.test(entry)))
|
|
93
|
+
assert.equal(brief.parentContext, 'Parent asked for a contained fix.')
|
|
94
|
+
assert.equal(brief.missionPhase, 'executing')
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
test('buildExecutionBriefContextBlock renders a single canonical state block', () => {
|
|
98
|
+
const brief = buildExecutionBrief({
|
|
99
|
+
session: {
|
|
100
|
+
id: 's2',
|
|
101
|
+
name: 'Session',
|
|
102
|
+
cwd: '/tmp',
|
|
103
|
+
user: 'tester',
|
|
104
|
+
provider: 'openai',
|
|
105
|
+
model: 'gpt-test',
|
|
106
|
+
claudeSessionId: null,
|
|
107
|
+
messages: [],
|
|
108
|
+
createdAt: 1,
|
|
109
|
+
lastActiveAt: 1,
|
|
110
|
+
} satisfies Partial<Session> as Session,
|
|
111
|
+
workingState: {
|
|
112
|
+
sessionId: 's2',
|
|
113
|
+
objective: 'Finish the rollout',
|
|
114
|
+
summary: 'Everything is ready except final verification.',
|
|
115
|
+
constraints: ['No schema changes'],
|
|
116
|
+
successCriteria: ['Verify production traffic'],
|
|
117
|
+
status: 'progress',
|
|
118
|
+
nextAction: 'Run the final smoke test',
|
|
119
|
+
planSteps: [{ id: 'p1', text: 'Run the final smoke test', status: 'active', createdAt: 1, updatedAt: 1 }],
|
|
120
|
+
confirmedFacts: [{ id: 'f1', statement: 'Staging already passed.', source: 'tool', status: 'active', createdAt: 1, updatedAt: 1 }],
|
|
121
|
+
artifacts: [],
|
|
122
|
+
decisions: [],
|
|
123
|
+
blockers: [],
|
|
124
|
+
openQuestions: [],
|
|
125
|
+
hypotheses: [],
|
|
126
|
+
evidenceRefs: [],
|
|
127
|
+
createdAt: 1,
|
|
128
|
+
updatedAt: 1,
|
|
129
|
+
},
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
const block = buildExecutionBriefContextBlock(brief)
|
|
133
|
+
assert.match(block, /## Execution Brief/)
|
|
134
|
+
assert.match(block, /Objective: Finish the rollout/)
|
|
135
|
+
assert.match(block, /Next action: Run the final smoke test/)
|
|
136
|
+
assert.match(block, /Staging already passed\./)
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
test('serializeExecutionBriefForDelegation creates a bounded handoff summary', () => {
|
|
140
|
+
const text = serializeExecutionBriefForDelegation({
|
|
141
|
+
sessionId: 's3',
|
|
142
|
+
missionId: 'm3',
|
|
143
|
+
objective: 'Repair the deployment pipeline',
|
|
144
|
+
summary: 'The regression is isolated to the release job.',
|
|
145
|
+
status: 'blocked',
|
|
146
|
+
nextAction: 'Fix the release job',
|
|
147
|
+
plan: [
|
|
148
|
+
{ text: 'Fix the release job', status: 'active' },
|
|
149
|
+
{ text: 'Verify deploy output', status: 'resolved' },
|
|
150
|
+
],
|
|
151
|
+
blockers: ['Approval required before production deploy.'],
|
|
152
|
+
facts: ['The build already passes locally.'],
|
|
153
|
+
artifacts: ['/tmp/project/release.log'],
|
|
154
|
+
constraints: ['Keep the current release shape.'],
|
|
155
|
+
successCriteria: ['Production deploy completes'],
|
|
156
|
+
missionStatus: 'active',
|
|
157
|
+
missionPhase: 'executing',
|
|
158
|
+
waitState: null,
|
|
159
|
+
evidenceRefs: [],
|
|
160
|
+
parentContext: 'Parent is waiting on a concise status update.',
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
assert.ok(text)
|
|
164
|
+
assert.match(String(text), /Objective: Repair the deployment pipeline/)
|
|
165
|
+
assert.match(String(text), /Blockers: Approval required before production deploy\./)
|
|
166
|
+
assert.match(String(text), /Parent context: Parent is waiting on a concise status update\./)
|
|
167
|
+
})
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
EvidenceRef,
|
|
3
|
+
ExecutionBrief,
|
|
4
|
+
ExecutionBriefPlanStep,
|
|
5
|
+
Mission,
|
|
6
|
+
Session,
|
|
7
|
+
SessionWorkingState,
|
|
8
|
+
WorkingPlanStep,
|
|
9
|
+
WorkingStateItemStatus,
|
|
10
|
+
WorkingStateStatus,
|
|
11
|
+
} from '@/types'
|
|
12
|
+
import { getSession } from '@/lib/server/sessions/session-repository'
|
|
13
|
+
import { loadSessionWorkingState } from '@/lib/server/working-state/service'
|
|
14
|
+
import { ensureRunContext } from '@/lib/server/run-context'
|
|
15
|
+
import { cleanText, cleanMultiline } from '@/lib/server/text-normalization'
|
|
16
|
+
|
|
17
|
+
const MAX_PLAN_ITEMS = 8
|
|
18
|
+
const MAX_FACTS = 8
|
|
19
|
+
const MAX_BLOCKERS = 6
|
|
20
|
+
const MAX_ARTIFACTS = 6
|
|
21
|
+
const MAX_EVIDENCE = 6
|
|
22
|
+
const MAX_DELEGATION_PLAN_ITEMS = 4
|
|
23
|
+
const MAX_DELEGATION_FACTS = 4
|
|
24
|
+
const MAX_DELEGATION_BLOCKERS = 4
|
|
25
|
+
const MAX_DELEGATION_ARTIFACTS = 4
|
|
26
|
+
const DELEGATION_BUDGET = 1_200
|
|
27
|
+
|
|
28
|
+
function uniqueStrings(values: Array<unknown>, maxItems: number, maxChars = 240): string[] {
|
|
29
|
+
const out: string[] = []
|
|
30
|
+
const seen = new Set<string>()
|
|
31
|
+
for (const value of values) {
|
|
32
|
+
const normalized = cleanText(value, maxChars)
|
|
33
|
+
if (!normalized) continue
|
|
34
|
+
const key = normalized.toLowerCase()
|
|
35
|
+
if (seen.has(key)) continue
|
|
36
|
+
seen.add(key)
|
|
37
|
+
out.push(normalized)
|
|
38
|
+
if (out.length >= maxItems) break
|
|
39
|
+
}
|
|
40
|
+
return out
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function summarizeArtifact(value: unknown): string {
|
|
44
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return ''
|
|
45
|
+
const artifact = value as Record<string, unknown>
|
|
46
|
+
return cleanText(artifact.path || artifact.url || artifact.label, 220)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function summarizeEvidenceRef(ref: EvidenceRef): string {
|
|
50
|
+
const summary = cleanText(ref.summary, 180)
|
|
51
|
+
if (!summary) return ''
|
|
52
|
+
const value = cleanText(ref.value, 140)
|
|
53
|
+
return value ? `[${ref.type}] ${summary}: ${value}` : `[${ref.type}] ${summary}`
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function planStatus(step: WorkingPlanStep): WorkingStateItemStatus {
|
|
57
|
+
return step.status === 'resolved' || step.status === 'superseded' ? step.status : 'active'
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function dedupePlan(steps: ExecutionBriefPlanStep[]): ExecutionBriefPlanStep[] {
|
|
61
|
+
const out: ExecutionBriefPlanStep[] = []
|
|
62
|
+
const seen = new Set<string>()
|
|
63
|
+
for (const step of steps) {
|
|
64
|
+
const text = cleanText(step.text, 240)
|
|
65
|
+
if (!text) continue
|
|
66
|
+
const key = text.toLowerCase()
|
|
67
|
+
if (seen.has(key)) continue
|
|
68
|
+
seen.add(key)
|
|
69
|
+
out.push({
|
|
70
|
+
text,
|
|
71
|
+
status: step.status === 'resolved' || step.status === 'superseded' ? step.status : 'active',
|
|
72
|
+
})
|
|
73
|
+
if (out.length >= MAX_PLAN_ITEMS) break
|
|
74
|
+
}
|
|
75
|
+
return out
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function inferStatus(mission: Mission | null | undefined, workingState: SessionWorkingState | null): WorkingStateStatus {
|
|
79
|
+
if (workingState?.status) return workingState.status
|
|
80
|
+
if (!mission) return 'idle'
|
|
81
|
+
if (mission.status === 'completed') return 'completed'
|
|
82
|
+
if (mission.status === 'waiting') return 'waiting'
|
|
83
|
+
if (mission.status === 'failed' || mission.phase === 'failed') return 'blocked'
|
|
84
|
+
if (mission.waitState) return 'waiting'
|
|
85
|
+
if (mission.phase === 'executing' || mission.phase === 'dispatching' || mission.phase === 'verifying') return 'progress'
|
|
86
|
+
return 'idle'
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function buildPlan(workingState: SessionWorkingState | null, session: Session | null): ExecutionBriefPlanStep[] {
|
|
90
|
+
if (workingState && Array.isArray(workingState.planSteps) && workingState.planSteps.length > 0) {
|
|
91
|
+
return dedupePlan(
|
|
92
|
+
workingState.planSteps
|
|
93
|
+
.filter((step) => step.status !== 'superseded')
|
|
94
|
+
.map((step) => ({
|
|
95
|
+
text: cleanText(step.text, 240),
|
|
96
|
+
status: planStatus(step),
|
|
97
|
+
})),
|
|
98
|
+
)
|
|
99
|
+
}
|
|
100
|
+
const runContext = session?.runContext ? ensureRunContext(session.runContext) : null
|
|
101
|
+
if (!runContext || !Array.isArray(runContext.currentPlan) || runContext.currentPlan.length === 0) return []
|
|
102
|
+
const completed = new Set((runContext.completedSteps || []).map((value) => cleanText(value, 240).toLowerCase()).filter(Boolean))
|
|
103
|
+
return dedupePlan(
|
|
104
|
+
runContext.currentPlan.map((step) => {
|
|
105
|
+
const text = cleanText(step, 240)
|
|
106
|
+
return {
|
|
107
|
+
text,
|
|
108
|
+
status: completed.has(text.toLowerCase()) ? 'resolved' : 'active',
|
|
109
|
+
}
|
|
110
|
+
}),
|
|
111
|
+
)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function buildFacts(workingState: SessionWorkingState | null, session: Session | null): string[] {
|
|
115
|
+
const activeFacts = workingState
|
|
116
|
+
? workingState.confirmedFacts
|
|
117
|
+
.filter((fact) => fact.status === 'active')
|
|
118
|
+
.map((fact) => fact.statement)
|
|
119
|
+
: []
|
|
120
|
+
const runContextFacts = session?.runContext ? ensureRunContext(session.runContext).keyFacts : []
|
|
121
|
+
return uniqueStrings([...activeFacts, ...runContextFacts], MAX_FACTS, 240)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function buildBlockers(workingState: SessionWorkingState | null, mission: Mission | null | undefined, session: Session | null): string[] {
|
|
125
|
+
const activeBlockers = workingState
|
|
126
|
+
? workingState.blockers
|
|
127
|
+
.filter((blocker) => blocker.status === 'active')
|
|
128
|
+
.map((blocker) => blocker.nextAction ? `${blocker.summary} | next: ${blocker.nextAction}` : blocker.summary)
|
|
129
|
+
: []
|
|
130
|
+
const missionBlockers = mission?.waitState?.reason ? [cleanText(mission.waitState.reason, 280)] : []
|
|
131
|
+
const runContextBlockers = session?.runContext ? ensureRunContext(session.runContext).blockers : []
|
|
132
|
+
return uniqueStrings([...activeBlockers, ...missionBlockers, ...runContextBlockers], MAX_BLOCKERS, 280)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function buildArtifacts(workingState: SessionWorkingState | null): string[] {
|
|
136
|
+
const artifacts = workingState
|
|
137
|
+
? workingState.artifacts
|
|
138
|
+
.filter((artifact) => artifact.status === 'active')
|
|
139
|
+
.map((artifact) => summarizeArtifact(artifact))
|
|
140
|
+
: []
|
|
141
|
+
return uniqueStrings(artifacts, MAX_ARTIFACTS, 220)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function buildConstraints(workingState: SessionWorkingState | null, session: Session | null): string[] {
|
|
145
|
+
const workingConstraints = workingState?.constraints || []
|
|
146
|
+
const runContext = session?.runContext ? ensureRunContext(session.runContext) : null
|
|
147
|
+
return uniqueStrings([...(workingConstraints || []), ...(runContext?.constraints || [])], 10, 220)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function buildSuccessCriteria(workingState: SessionWorkingState | null, mission: Mission | null | undefined): string[] {
|
|
151
|
+
return uniqueStrings([...(workingState?.successCriteria || []), ...(mission?.successCriteria || [])], 10, 220)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function buildEvidenceRefs(workingState: SessionWorkingState | null): EvidenceRef[] {
|
|
155
|
+
if (!workingState || !Array.isArray(workingState.evidenceRefs) || workingState.evidenceRefs.length === 0) return []
|
|
156
|
+
return [...workingState.evidenceRefs]
|
|
157
|
+
.filter((ref) => Boolean(cleanText(ref.summary, 180)))
|
|
158
|
+
.slice(-MAX_EVIDENCE)
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export function buildExecutionBrief(params: {
|
|
162
|
+
sessionId?: string | null
|
|
163
|
+
session?: Session | null
|
|
164
|
+
mission?: Mission | null
|
|
165
|
+
workingState?: SessionWorkingState | null
|
|
166
|
+
}): ExecutionBrief {
|
|
167
|
+
const session = params.session
|
|
168
|
+
|| (params.sessionId ? getSession(params.sessionId) || null : null)
|
|
169
|
+
const mission = params.mission || null
|
|
170
|
+
const workingState = params.workingState
|
|
171
|
+
|| (session?.id ? loadSessionWorkingState(session.id, { mission }) : null)
|
|
172
|
+
const runContext = session?.runContext ? ensureRunContext(session.runContext) : null
|
|
173
|
+
const plan = buildPlan(workingState, session)
|
|
174
|
+
const nextAction = cleanText(
|
|
175
|
+
workingState?.nextAction
|
|
176
|
+
|| mission?.currentStep
|
|
177
|
+
|| plan.find((step) => step.status === 'active')?.text,
|
|
178
|
+
240,
|
|
179
|
+
) || null
|
|
180
|
+
|
|
181
|
+
return {
|
|
182
|
+
sessionId: session?.id || params.sessionId || null,
|
|
183
|
+
missionId: mission?.id || workingState?.missionId || null,
|
|
184
|
+
objective: cleanMultiline(
|
|
185
|
+
workingState?.objective
|
|
186
|
+
|| mission?.objective
|
|
187
|
+
|| runContext?.objective,
|
|
188
|
+
900,
|
|
189
|
+
) || null,
|
|
190
|
+
summary: cleanMultiline(
|
|
191
|
+
workingState?.summary
|
|
192
|
+
|| mission?.verifierSummary
|
|
193
|
+
|| mission?.plannerSummary,
|
|
194
|
+
700,
|
|
195
|
+
) || null,
|
|
196
|
+
status: inferStatus(mission, workingState),
|
|
197
|
+
nextAction,
|
|
198
|
+
plan,
|
|
199
|
+
blockers: buildBlockers(workingState, mission, session),
|
|
200
|
+
facts: buildFacts(workingState, session),
|
|
201
|
+
artifacts: buildArtifacts(workingState),
|
|
202
|
+
constraints: buildConstraints(workingState, session),
|
|
203
|
+
successCriteria: buildSuccessCriteria(workingState, mission),
|
|
204
|
+
missionStatus: mission?.status || null,
|
|
205
|
+
missionPhase: mission?.phase || null,
|
|
206
|
+
waitState: mission?.waitState || null,
|
|
207
|
+
evidenceRefs: buildEvidenceRefs(workingState),
|
|
208
|
+
parentContext: cleanMultiline(runContext?.parentContext, 900) || null,
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function buildListSection(title: string, values: string[]): string | null {
|
|
213
|
+
if (!values.length) return null
|
|
214
|
+
return [title, ...values.map((value) => `- ${value}`)].join('\n')
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function formatPlan(plan: ExecutionBriefPlanStep[]): string | null {
|
|
218
|
+
if (!plan.length) return null
|
|
219
|
+
return [
|
|
220
|
+
'Plan',
|
|
221
|
+
...plan.map((step) => `- [${step.status === 'resolved' ? 'x' : ' '}] ${step.text}`),
|
|
222
|
+
].join('\n')
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export function buildExecutionBriefContextBlock(
|
|
226
|
+
brief: ExecutionBrief | null | undefined,
|
|
227
|
+
options?: { title?: string },
|
|
228
|
+
): string {
|
|
229
|
+
if (!brief) return ''
|
|
230
|
+
const hasContent = Boolean(
|
|
231
|
+
brief.parentContext
|
|
232
|
+
|| brief.objective
|
|
233
|
+
|| brief.summary
|
|
234
|
+
|| brief.nextAction
|
|
235
|
+
|| brief.plan.length > 0
|
|
236
|
+
|| brief.blockers.length > 0
|
|
237
|
+
|| brief.facts.length > 0
|
|
238
|
+
|| brief.artifacts.length > 0
|
|
239
|
+
|| brief.constraints.length > 0
|
|
240
|
+
|| brief.successCriteria.length > 0
|
|
241
|
+
|| brief.evidenceRefs.length > 0
|
|
242
|
+
|| brief.missionStatus
|
|
243
|
+
|| brief.missionPhase
|
|
244
|
+
|| brief.waitState?.reason,
|
|
245
|
+
)
|
|
246
|
+
if (!hasContent && brief.status === 'idle') return ''
|
|
247
|
+
const sections = [
|
|
248
|
+
options?.title || '## Execution Brief',
|
|
249
|
+
brief.parentContext ? `Parent context:\n${brief.parentContext}` : '',
|
|
250
|
+
brief.objective ? `Objective: ${brief.objective}` : '',
|
|
251
|
+
brief.summary ? `Summary: ${brief.summary}` : '',
|
|
252
|
+
`Status: ${brief.status}`,
|
|
253
|
+
brief.missionStatus ? `Mission status: ${brief.missionStatus}` : '',
|
|
254
|
+
brief.missionPhase ? `Mission phase: ${brief.missionPhase}` : '',
|
|
255
|
+
brief.waitState?.reason ? `Waiting reason: ${cleanText(brief.waitState.reason, 280)}` : '',
|
|
256
|
+
brief.nextAction ? `Next action: ${brief.nextAction}` : '',
|
|
257
|
+
brief.successCriteria.length > 0 ? `Success criteria: ${brief.successCriteria.join(' | ')}` : '',
|
|
258
|
+
brief.constraints.length > 0 ? `Constraints: ${brief.constraints.join(' | ')}` : '',
|
|
259
|
+
formatPlan(brief.plan),
|
|
260
|
+
buildListSection('Blockers', brief.blockers),
|
|
261
|
+
buildListSection('Facts', brief.facts),
|
|
262
|
+
buildListSection('Artifacts', brief.artifacts),
|
|
263
|
+
buildListSection('Evidence', brief.evidenceRefs.map((ref) => summarizeEvidenceRef(ref)).filter(Boolean)),
|
|
264
|
+
'Trust this execution brief before reconstructing state from the raw transcript or older assistant text.',
|
|
265
|
+
].filter(Boolean)
|
|
266
|
+
return sections.join('\n')
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export function serializeExecutionBriefForDelegation(
|
|
270
|
+
brief: ExecutionBrief | null | undefined,
|
|
271
|
+
): string | null {
|
|
272
|
+
if (!brief) return null
|
|
273
|
+
const parts: string[] = []
|
|
274
|
+
let budget = DELEGATION_BUDGET
|
|
275
|
+
|
|
276
|
+
const append = (line: string): void => {
|
|
277
|
+
if (!line) return
|
|
278
|
+
if (budget - line.length - 1 < 0) return
|
|
279
|
+
parts.push(line)
|
|
280
|
+
budget -= line.length + 1
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
append(brief.objective ? `Objective: ${brief.objective}` : '')
|
|
284
|
+
append(brief.summary ? `Summary: ${cleanText(brief.summary, 280)}` : '')
|
|
285
|
+
append(`Status: ${brief.status}`)
|
|
286
|
+
append(brief.nextAction ? `Next action: ${brief.nextAction}` : '')
|
|
287
|
+
append(brief.successCriteria.length > 0 ? `Success criteria: ${brief.successCriteria.slice(0, 4).join('; ')}` : '')
|
|
288
|
+
append(brief.constraints.length > 0 ? `Constraints: ${brief.constraints.slice(0, 4).join('; ')}` : '')
|
|
289
|
+
append(brief.plan.length > 0 ? `Plan: ${brief.plan.slice(0, MAX_DELEGATION_PLAN_ITEMS).map((step) => step.text).join('; ')}` : '')
|
|
290
|
+
append(brief.blockers.length > 0 ? `Blockers: ${brief.blockers.slice(0, MAX_DELEGATION_BLOCKERS).join('; ')}` : '')
|
|
291
|
+
append(brief.facts.length > 0 ? `Facts: ${brief.facts.slice(0, MAX_DELEGATION_FACTS).join('; ')}` : '')
|
|
292
|
+
append(brief.artifacts.length > 0 ? `Artifacts: ${brief.artifacts.slice(0, MAX_DELEGATION_ARTIFACTS).join('; ')}` : '')
|
|
293
|
+
append(brief.parentContext ? `Parent context: ${cleanText(brief.parentContext, 280)}` : '')
|
|
294
|
+
return parts.length > 0 ? parts.join('\n') : null
|
|
295
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { executeSessionChatTurn } from '@/lib/server/chat-execution/chat-execution'
|
|
2
|
+
import type {
|
|
3
|
+
ExecuteChatTurnInput,
|
|
4
|
+
ExecuteChatTurnResult,
|
|
5
|
+
} from '@/lib/server/chat-execution/chat-execution-types'
|
|
6
|
+
|
|
7
|
+
export function executeExecutionChatTurn(input: ExecuteChatTurnInput): Promise<ExecuteChatTurnResult> {
|
|
8
|
+
return executeSessionChatTurn(input)
|
|
9
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import assert from 'node:assert/strict'
|
|
2
|
+
import fs from 'node:fs'
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
import { describe, it } from 'node:test'
|
|
5
|
+
|
|
6
|
+
const repoRoot = path.resolve(path.dirname(new URL(import.meta.url).pathname), '../../../..')
|
|
7
|
+
const srcRoot = path.join(repoRoot, 'src')
|
|
8
|
+
|
|
9
|
+
const ALLOWED_IMPORTERS = new Set([
|
|
10
|
+
path.join(srcRoot, 'lib/server/chat-execution/chat-execution.ts'),
|
|
11
|
+
path.join(srcRoot, 'lib/server/execution-engine/chat-turn.ts'),
|
|
12
|
+
])
|
|
13
|
+
|
|
14
|
+
function walk(dir: string): string[] {
|
|
15
|
+
const results: string[] = []
|
|
16
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
17
|
+
const fullPath = path.join(dir, entry.name)
|
|
18
|
+
if (entry.isDirectory()) {
|
|
19
|
+
results.push(...walk(fullPath))
|
|
20
|
+
continue
|
|
21
|
+
}
|
|
22
|
+
if (!/\.(?:ts|tsx|js|mjs)$/.test(entry.name)) continue
|
|
23
|
+
if (entry.name.includes('.test.')) continue
|
|
24
|
+
results.push(fullPath)
|
|
25
|
+
}
|
|
26
|
+
return results
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
describe('executeSessionChatTurn import boundary', () => {
|
|
30
|
+
it('is only imported through the execution-engine chat-turn wrapper', () => {
|
|
31
|
+
const offenders: string[] = []
|
|
32
|
+
const importPattern = /import\s*\{[^}]*\bexecuteSessionChatTurn\b[^}]*\}\s*from\s*['"][^'"]+['"]/m
|
|
33
|
+
|
|
34
|
+
for (const filePath of walk(srcRoot)) {
|
|
35
|
+
if (ALLOWED_IMPORTERS.has(filePath)) continue
|
|
36
|
+
const contents = fs.readFileSync(filePath, 'utf8')
|
|
37
|
+
if (importPattern.test(contents)) {
|
|
38
|
+
offenders.push(path.relative(repoRoot, filePath))
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
assert.deepEqual(offenders, [])
|
|
43
|
+
})
|
|
44
|
+
})
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { enqueueSessionRun } from '@/lib/server/runtime/session-run-manager'
|
|
2
|
+
import type { ExecuteChatTurnResult } from '@/lib/server/chat-execution/chat-execution-types'
|
|
3
|
+
import type {
|
|
4
|
+
EnqueueExecutionRequest,
|
|
5
|
+
ExecutionHandle,
|
|
6
|
+
} from '@/lib/server/execution-engine/types'
|
|
7
|
+
import { enqueueTaskAttemptExecution } from '@/lib/server/execution-engine/task-attempt'
|
|
8
|
+
|
|
9
|
+
export function enqueueExecution(
|
|
10
|
+
input: EnqueueExecutionRequest,
|
|
11
|
+
): ExecutionHandle<ExecuteChatTurnResult> {
|
|
12
|
+
if (input.kind === 'session_turn') {
|
|
13
|
+
const result = enqueueSessionRun(input.input)
|
|
14
|
+
return {
|
|
15
|
+
executionId: result.runId,
|
|
16
|
+
promise: result.promise,
|
|
17
|
+
abort: result.abort,
|
|
18
|
+
position: result.position,
|
|
19
|
+
deduped: result.deduped,
|
|
20
|
+
coalesced: result.coalesced,
|
|
21
|
+
unsubscribe: result.unsubscribe,
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return enqueueTaskAttemptExecution(input)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export { executeExecutionChatTurn } from '@/lib/server/execution-engine/chat-turn'
|
|
29
|
+
export type {
|
|
30
|
+
EnqueueExecutionRequest,
|
|
31
|
+
EnqueueTaskAttemptExecutionRequest,
|
|
32
|
+
EnqueueSessionTurnExecutionRequest,
|
|
33
|
+
ExecutionHandle,
|
|
34
|
+
ExecutionResult,
|
|
35
|
+
} from '@/lib/server/execution-engine/types'
|