@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
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
import assert from 'node:assert/strict'
|
|
2
|
+
import { test } from 'node:test'
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
ensureRunContext,
|
|
6
|
+
dedup,
|
|
7
|
+
pruneRunContext,
|
|
8
|
+
foldReflectionIntoRunContext,
|
|
9
|
+
buildRunContextSection,
|
|
10
|
+
extractFactsFromMessages,
|
|
11
|
+
} from './run-context'
|
|
12
|
+
import type { RunContext, RunReflection, Message } from '@/types'
|
|
13
|
+
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
// ensureRunContext
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
|
|
18
|
+
test('ensureRunContext returns fresh context for null input', () => {
|
|
19
|
+
const ctx = ensureRunContext(null)
|
|
20
|
+
assert.equal(ctx.version, 0)
|
|
21
|
+
assert.equal(ctx.objective, null)
|
|
22
|
+
assert.deepEqual(ctx.keyFacts, [])
|
|
23
|
+
assert.deepEqual(ctx.discoveries, [])
|
|
24
|
+
assert.deepEqual(ctx.failedApproaches, [])
|
|
25
|
+
assert.deepEqual(ctx.constraints, [])
|
|
26
|
+
assert.deepEqual(ctx.currentPlan, [])
|
|
27
|
+
assert.deepEqual(ctx.completedSteps, [])
|
|
28
|
+
assert.deepEqual(ctx.blockers, [])
|
|
29
|
+
assert.equal(ctx.parentContext, null)
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
test('ensureRunContext returns fresh context for undefined input', () => {
|
|
33
|
+
const ctx = ensureRunContext(undefined)
|
|
34
|
+
assert.equal(ctx.version, 0)
|
|
35
|
+
assert.deepEqual(ctx.keyFacts, [])
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
test('ensureRunContext passes through a valid RunContext', () => {
|
|
39
|
+
const existing: RunContext = {
|
|
40
|
+
objective: 'Ship it',
|
|
41
|
+
constraints: ['No breaking changes'],
|
|
42
|
+
keyFacts: ['Fact A'],
|
|
43
|
+
discoveries: [],
|
|
44
|
+
failedApproaches: [],
|
|
45
|
+
currentPlan: ['Step 1'],
|
|
46
|
+
completedSteps: [],
|
|
47
|
+
blockers: [],
|
|
48
|
+
parentContext: null,
|
|
49
|
+
updatedAt: 1000,
|
|
50
|
+
version: 5,
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const result = ensureRunContext(existing)
|
|
54
|
+
assert.equal(result, existing) // same reference
|
|
55
|
+
assert.equal(result.version, 5)
|
|
56
|
+
assert.deepEqual(result.keyFacts, ['Fact A'])
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
test('ensureRunContext backfills missing arrays on malformed object with version', () => {
|
|
60
|
+
// Simulates persisted data where some array fields were stripped/corrupted
|
|
61
|
+
const malformed = { version: 3, objective: 'Fix it', updatedAt: 1 } as unknown as RunContext
|
|
62
|
+
|
|
63
|
+
const result = ensureRunContext(malformed)
|
|
64
|
+
assert.equal(result.version, 3) // preserves version
|
|
65
|
+
assert.equal(result.objective, 'Fix it')
|
|
66
|
+
assert.deepEqual(result.constraints, [])
|
|
67
|
+
assert.deepEqual(result.keyFacts, [])
|
|
68
|
+
assert.deepEqual(result.discoveries, [])
|
|
69
|
+
assert.deepEqual(result.failedApproaches, [])
|
|
70
|
+
assert.deepEqual(result.currentPlan, [])
|
|
71
|
+
assert.deepEqual(result.completedSteps, [])
|
|
72
|
+
assert.deepEqual(result.blockers, [])
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
test('ensureRunContext returns fresh context for object without version field', () => {
|
|
76
|
+
const noVersion = { objective: 'Something', keyFacts: ['A'] } as unknown as RunContext
|
|
77
|
+
const result = ensureRunContext(noVersion)
|
|
78
|
+
assert.equal(result.version, 0)
|
|
79
|
+
assert.deepEqual(result.keyFacts, [])
|
|
80
|
+
assert.notEqual(result, noVersion) // new object
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
// ---------------------------------------------------------------------------
|
|
84
|
+
// dedup
|
|
85
|
+
// ---------------------------------------------------------------------------
|
|
86
|
+
|
|
87
|
+
test('dedup removes case-insensitive duplicates', () => {
|
|
88
|
+
assert.deepEqual(dedup(['Hello World', 'hello world', 'HELLO WORLD']), ['Hello World'])
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
test('dedup normalizes whitespace', () => {
|
|
92
|
+
assert.deepEqual(dedup(['too many spaces', 'too many spaces']), ['too many spaces'])
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
test('dedup filters out empty and blank strings', () => {
|
|
96
|
+
assert.deepEqual(dedup(['valid', '', ' ', '\t\n', 'also valid']), ['valid', 'also valid'])
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
test('dedup preserves order of first occurrence', () => {
|
|
100
|
+
assert.deepEqual(dedup(['B', 'A', 'b', 'C', 'a']), ['B', 'A', 'C'])
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
// ---------------------------------------------------------------------------
|
|
104
|
+
// pruneRunContext
|
|
105
|
+
// ---------------------------------------------------------------------------
|
|
106
|
+
|
|
107
|
+
test('pruneRunContext enforces array caps keeping most recent entries', () => {
|
|
108
|
+
const ctx = ensureRunContext(null)
|
|
109
|
+
// keyFacts cap is 20 — fill with 25
|
|
110
|
+
ctx.keyFacts = Array.from({ length: 25 }, (_, i) => `fact-${i}`)
|
|
111
|
+
// blockers cap is 8 — fill with 12
|
|
112
|
+
ctx.blockers = Array.from({ length: 12 }, (_, i) => `blocker-${i}`)
|
|
113
|
+
|
|
114
|
+
const pruned = pruneRunContext(ctx)
|
|
115
|
+
assert.equal(pruned.keyFacts.length, 20)
|
|
116
|
+
assert.equal(pruned.keyFacts[0], 'fact-5') // sliced from end
|
|
117
|
+
assert.equal(pruned.keyFacts[19], 'fact-24')
|
|
118
|
+
assert.equal(pruned.blockers.length, 8)
|
|
119
|
+
assert.equal(pruned.blockers[0], 'blocker-4')
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
test('pruneRunContext leaves arrays under cap unchanged', () => {
|
|
123
|
+
const ctx = ensureRunContext(null)
|
|
124
|
+
ctx.keyFacts = ['a', 'b', 'c']
|
|
125
|
+
const pruned = pruneRunContext(ctx)
|
|
126
|
+
assert.deepEqual(pruned.keyFacts, ['a', 'b', 'c'])
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
// ---------------------------------------------------------------------------
|
|
130
|
+
// foldReflectionIntoRunContext
|
|
131
|
+
// ---------------------------------------------------------------------------
|
|
132
|
+
|
|
133
|
+
test('foldReflectionIntoRunContext creates context from null and maps reflection fields', () => {
|
|
134
|
+
const reflection = {
|
|
135
|
+
id: 'r1',
|
|
136
|
+
runId: 'run1',
|
|
137
|
+
sessionId: 's1',
|
|
138
|
+
source: 'test',
|
|
139
|
+
status: 'completed' as const,
|
|
140
|
+
summary: 'Test reflection',
|
|
141
|
+
invariantNotes: ['Invariant A'],
|
|
142
|
+
lessonNotes: ['Lesson B'],
|
|
143
|
+
derivedNotes: ['Derived C'],
|
|
144
|
+
significantEventNotes: ['Event D'],
|
|
145
|
+
failureNotes: ['Failure E'],
|
|
146
|
+
openLoopNotes: ['Open loop F'],
|
|
147
|
+
boundaryNotes: ['Boundary G'],
|
|
148
|
+
createdAt: 1,
|
|
149
|
+
updatedAt: 1,
|
|
150
|
+
} satisfies Partial<RunReflection> as RunReflection
|
|
151
|
+
|
|
152
|
+
const ctx = foldReflectionIntoRunContext(null, reflection)
|
|
153
|
+
assert.equal(ctx.version, 1)
|
|
154
|
+
assert.ok(ctx.keyFacts.includes('Invariant A'))
|
|
155
|
+
assert.ok(ctx.keyFacts.includes('Lesson B'))
|
|
156
|
+
assert.ok(ctx.discoveries.includes('Derived C'))
|
|
157
|
+
assert.ok(ctx.discoveries.includes('Event D'))
|
|
158
|
+
assert.ok(ctx.failedApproaches.includes('Failure E'))
|
|
159
|
+
assert.ok(ctx.blockers.includes('Open loop F'))
|
|
160
|
+
assert.ok(ctx.constraints.includes('Boundary G'))
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
test('foldReflectionIntoRunContext deduplicates when folding', () => {
|
|
164
|
+
const existing: RunContext = {
|
|
165
|
+
objective: null,
|
|
166
|
+
constraints: [],
|
|
167
|
+
keyFacts: ['Already known fact'],
|
|
168
|
+
discoveries: [],
|
|
169
|
+
failedApproaches: [],
|
|
170
|
+
currentPlan: [],
|
|
171
|
+
completedSteps: [],
|
|
172
|
+
blockers: [],
|
|
173
|
+
parentContext: null,
|
|
174
|
+
updatedAt: 1,
|
|
175
|
+
version: 2,
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const reflection = {
|
|
179
|
+
id: 'r2',
|
|
180
|
+
runId: 'run2',
|
|
181
|
+
sessionId: 's2',
|
|
182
|
+
source: 'test',
|
|
183
|
+
status: 'completed' as const,
|
|
184
|
+
summary: 'Dup test',
|
|
185
|
+
invariantNotes: ['already known fact'], // same content, different case
|
|
186
|
+
derivedNotes: [],
|
|
187
|
+
failureNotes: [],
|
|
188
|
+
lessonNotes: [],
|
|
189
|
+
createdAt: 1,
|
|
190
|
+
updatedAt: 1,
|
|
191
|
+
} satisfies Partial<RunReflection> as RunReflection
|
|
192
|
+
|
|
193
|
+
const ctx = foldReflectionIntoRunContext(existing, reflection)
|
|
194
|
+
assert.equal(ctx.keyFacts.length, 1)
|
|
195
|
+
assert.equal(ctx.keyFacts[0], 'Already known fact') // keeps original casing
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
test('foldReflectionIntoRunContext increments version', () => {
|
|
199
|
+
const existing = ensureRunContext(null)
|
|
200
|
+
existing.version = 4
|
|
201
|
+
|
|
202
|
+
const reflection = {
|
|
203
|
+
id: 'r3',
|
|
204
|
+
runId: 'run3',
|
|
205
|
+
sessionId: 's3',
|
|
206
|
+
source: 'test',
|
|
207
|
+
status: 'completed' as const,
|
|
208
|
+
summary: 'Version test',
|
|
209
|
+
invariantNotes: [],
|
|
210
|
+
derivedNotes: [],
|
|
211
|
+
failureNotes: [],
|
|
212
|
+
lessonNotes: [],
|
|
213
|
+
createdAt: 1,
|
|
214
|
+
updatedAt: 1,
|
|
215
|
+
} satisfies Partial<RunReflection> as RunReflection
|
|
216
|
+
|
|
217
|
+
const ctx = foldReflectionIntoRunContext(existing, reflection)
|
|
218
|
+
assert.equal(ctx.version, 5)
|
|
219
|
+
})
|
|
220
|
+
|
|
221
|
+
// ---------------------------------------------------------------------------
|
|
222
|
+
// buildRunContextSection
|
|
223
|
+
// ---------------------------------------------------------------------------
|
|
224
|
+
|
|
225
|
+
test('buildRunContextSection returns null for null context', () => {
|
|
226
|
+
assert.equal(buildRunContextSection(null, false), null)
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
test('buildRunContextSection returns null for minimal prompt', () => {
|
|
230
|
+
const ctx = ensureRunContext(null)
|
|
231
|
+
ctx.keyFacts = ['Something important']
|
|
232
|
+
assert.equal(buildRunContextSection(ctx, true), null)
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
test('buildRunContextSection returns null for empty context', () => {
|
|
236
|
+
const ctx = ensureRunContext(null)
|
|
237
|
+
assert.equal(buildRunContextSection(ctx, false), null)
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
test('buildRunContextSection renders all non-empty fields', () => {
|
|
241
|
+
const ctx: RunContext = {
|
|
242
|
+
objective: 'Fix the pipeline',
|
|
243
|
+
constraints: ['No downtime'],
|
|
244
|
+
keyFacts: ['Build passes locally'],
|
|
245
|
+
discoveries: ['Staging uses different auth'],
|
|
246
|
+
failedApproaches: ['Restart did not help'],
|
|
247
|
+
currentPlan: ['Investigate auth', 'Deploy fix'],
|
|
248
|
+
completedSteps: ['Investigate auth'],
|
|
249
|
+
blockers: ['Waiting on credentials'],
|
|
250
|
+
parentContext: 'Coordinator wants a contained fix',
|
|
251
|
+
updatedAt: 1,
|
|
252
|
+
version: 3,
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const section = buildRunContextSection(ctx, false)
|
|
256
|
+
assert.ok(section)
|
|
257
|
+
assert.match(section, /Working Memory \(RunContext\)/)
|
|
258
|
+
assert.match(section, /Coordinator Context/)
|
|
259
|
+
assert.match(section, /Coordinator wants a contained fix/)
|
|
260
|
+
assert.match(section, /Current Objective/)
|
|
261
|
+
assert.match(section, /Fix the pipeline/)
|
|
262
|
+
assert.match(section, /Constraints/)
|
|
263
|
+
assert.match(section, /No downtime/)
|
|
264
|
+
assert.match(section, /Key Facts/)
|
|
265
|
+
assert.match(section, /Build passes locally/)
|
|
266
|
+
assert.match(section, /Already Tried \(Failed\)/)
|
|
267
|
+
assert.match(section, /Restart did not help/)
|
|
268
|
+
assert.match(section, /Current Plan/)
|
|
269
|
+
assert.match(section, /Blockers/)
|
|
270
|
+
assert.match(section, /Waiting on credentials/)
|
|
271
|
+
assert.match(section, /Discoveries/)
|
|
272
|
+
assert.match(section, /Staging uses different auth/)
|
|
273
|
+
})
|
|
274
|
+
|
|
275
|
+
test('buildRunContextSection renders plan with checkboxes for completed steps', () => {
|
|
276
|
+
const ctx: RunContext = {
|
|
277
|
+
objective: null,
|
|
278
|
+
constraints: [],
|
|
279
|
+
keyFacts: [],
|
|
280
|
+
discoveries: [],
|
|
281
|
+
failedApproaches: [],
|
|
282
|
+
currentPlan: ['Step A', 'Step B', 'Step C'],
|
|
283
|
+
completedSteps: ['step a', 'Step C'], // case-insensitive match
|
|
284
|
+
blockers: [],
|
|
285
|
+
parentContext: null,
|
|
286
|
+
updatedAt: 1,
|
|
287
|
+
version: 1,
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const section = buildRunContextSection(ctx, false)
|
|
291
|
+
assert.ok(section)
|
|
292
|
+
assert.match(section, /\[x\] Step A/)
|
|
293
|
+
assert.match(section, /\[ \] Step B/)
|
|
294
|
+
assert.match(section, /\[x\] Step C/)
|
|
295
|
+
})
|
|
296
|
+
|
|
297
|
+
test('buildRunContextSection respects budget cap', () => {
|
|
298
|
+
const ctx: RunContext = {
|
|
299
|
+
objective: 'A'.repeat(2000),
|
|
300
|
+
constraints: ['B'.repeat(2000)],
|
|
301
|
+
keyFacts: ['C'.repeat(2000)],
|
|
302
|
+
discoveries: [],
|
|
303
|
+
failedApproaches: [],
|
|
304
|
+
currentPlan: [],
|
|
305
|
+
completedSteps: [],
|
|
306
|
+
blockers: [],
|
|
307
|
+
parentContext: null,
|
|
308
|
+
updatedAt: 1,
|
|
309
|
+
version: 1,
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
const section = buildRunContextSection(ctx, false)
|
|
313
|
+
assert.ok(section)
|
|
314
|
+
// The section should exist but be bounded. At 3000 char budget,
|
|
315
|
+
// not all fields can fit with 2000-char values.
|
|
316
|
+
assert.ok(section.length < 4000) // header + budget
|
|
317
|
+
})
|
|
318
|
+
|
|
319
|
+
// ---------------------------------------------------------------------------
|
|
320
|
+
// extractFactsFromMessages
|
|
321
|
+
// ---------------------------------------------------------------------------
|
|
322
|
+
|
|
323
|
+
test('extractFactsFromMessages extracts facts matching keyword patterns', () => {
|
|
324
|
+
const messages = [
|
|
325
|
+
{ text: 'I discovered that the API key must always be rotated monthly for compliance reasons.' },
|
|
326
|
+
{ text: 'Note: the staging environment uses a separate auth service from production.' },
|
|
327
|
+
] as Message[]
|
|
328
|
+
|
|
329
|
+
const result = extractFactsFromMessages(messages)
|
|
330
|
+
assert.ok(result.keyFacts.length > 0)
|
|
331
|
+
})
|
|
332
|
+
|
|
333
|
+
test('extractFactsFromMessages categorizes error patterns as failedApproaches', () => {
|
|
334
|
+
const messages = [
|
|
335
|
+
{ text: "The migration failed: the schema was incompatible with the target version due to column ordering." },
|
|
336
|
+
] as Message[]
|
|
337
|
+
|
|
338
|
+
const result = extractFactsFromMessages(messages)
|
|
339
|
+
assert.ok(result.failedApproaches.length > 0)
|
|
340
|
+
assert.ok(result.failedApproaches.some((f) => /schema/i.test(f)))
|
|
341
|
+
})
|
|
342
|
+
|
|
343
|
+
test('extractFactsFromMessages deduplicates results', () => {
|
|
344
|
+
const messages = [
|
|
345
|
+
{ text: 'Important: always validate the input before processing the request payload.' },
|
|
346
|
+
{ text: 'important: always validate the input before processing the request payload.' },
|
|
347
|
+
] as Message[]
|
|
348
|
+
|
|
349
|
+
const result = extractFactsFromMessages(messages)
|
|
350
|
+
// Same fact stated twice (different case) should be deduped
|
|
351
|
+
const matching = result.keyFacts.filter((f) => /validate the input/i.test(f))
|
|
352
|
+
assert.ok(matching.length <= 1)
|
|
353
|
+
})
|
|
354
|
+
|
|
355
|
+
test('extractFactsFromMessages ignores short messages', () => {
|
|
356
|
+
const messages = [
|
|
357
|
+
{ text: 'OK' },
|
|
358
|
+
{ text: 'Sure, noted.' },
|
|
359
|
+
{ text: '' },
|
|
360
|
+
] as Message[]
|
|
361
|
+
|
|
362
|
+
const result = extractFactsFromMessages(messages)
|
|
363
|
+
assert.equal(result.keyFacts.length, 0)
|
|
364
|
+
assert.equal(result.failedApproaches.length, 0)
|
|
365
|
+
})
|