@namzu/sdk 0.1.4 → 0.1.5-rc.1
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/dist/advisory/executor.d.ts +2 -2
- package/dist/advisory/executor.d.ts.map +1 -1
- package/dist/advisory/executor.js.map +1 -1
- package/dist/agents/AbstractAgent.d.ts +20 -2
- package/dist/agents/AbstractAgent.d.ts.map +1 -1
- package/dist/agents/AbstractAgent.js +23 -1
- package/dist/agents/AbstractAgent.js.map +1 -1
- package/dist/agents/PipelineAgent.d.ts.map +1 -1
- package/dist/agents/PipelineAgent.js +1 -1
- package/dist/agents/PipelineAgent.js.map +1 -1
- package/dist/agents/ReactiveAgent.d.ts.map +1 -1
- package/dist/agents/ReactiveAgent.js +1 -0
- package/dist/agents/ReactiveAgent.js.map +1 -1
- package/dist/agents/RouterAgent.d.ts.map +1 -1
- package/dist/agents/RouterAgent.js +4 -2
- package/dist/agents/RouterAgent.js.map +1 -1
- package/dist/agents/SupervisorAgent.d.ts.map +1 -1
- package/dist/agents/SupervisorAgent.js +4 -1
- package/dist/agents/SupervisorAgent.js.map +1 -1
- package/dist/agents/__tests__/lock.test.d.ts +2 -0
- package/dist/agents/__tests__/lock.test.d.ts.map +1 -0
- package/dist/agents/__tests__/lock.test.js +131 -0
- package/dist/agents/__tests__/lock.test.js.map +1 -0
- package/dist/agents/index.d.ts +2 -0
- package/dist/agents/index.d.ts.map +1 -1
- package/dist/agents/index.js +1 -0
- package/dist/agents/index.js.map +1 -1
- package/dist/agents/lock.d.ts +42 -0
- package/dist/agents/lock.d.ts.map +1 -0
- package/dist/agents/lock.js +54 -0
- package/dist/agents/lock.js.map +1 -0
- package/dist/bridge/a2a/message.d.ts.map +1 -1
- package/dist/bridge/a2a/message.js.map +1 -1
- package/dist/bridge/tools/connector/router.d.ts +4 -5
- package/dist/bridge/tools/connector/router.d.ts.map +1 -1
- package/dist/bridge/tools/connector/router.js.map +1 -1
- package/dist/compaction/__tests__/SlidingWindowManager.test.d.ts +2 -0
- package/dist/compaction/__tests__/SlidingWindowManager.test.d.ts.map +1 -0
- package/dist/compaction/__tests__/SlidingWindowManager.test.js +113 -0
- package/dist/compaction/__tests__/SlidingWindowManager.test.js.map +1 -0
- package/dist/compaction/__tests__/dangling.test.d.ts +2 -0
- package/dist/compaction/__tests__/dangling.test.d.ts.map +1 -0
- package/dist/compaction/__tests__/dangling.test.js +356 -0
- package/dist/compaction/__tests__/dangling.test.js.map +1 -0
- package/dist/compaction/__tests__/factory.test.d.ts +2 -0
- package/dist/compaction/__tests__/factory.test.d.ts.map +1 -0
- package/dist/compaction/__tests__/factory.test.js +43 -0
- package/dist/compaction/__tests__/factory.test.js.map +1 -0
- package/dist/compaction/dangling.d.ts +96 -0
- package/dist/compaction/dangling.d.ts.map +1 -0
- package/dist/compaction/dangling.js +274 -0
- package/dist/compaction/dangling.js.map +1 -0
- package/dist/compaction/factory.d.ts +20 -0
- package/dist/compaction/factory.d.ts.map +1 -0
- package/dist/compaction/factory.js +35 -0
- package/dist/compaction/factory.js.map +1 -0
- package/dist/compaction/index.d.ts +5 -0
- package/dist/compaction/index.d.ts.map +1 -1
- package/dist/compaction/index.js +3 -0
- package/dist/compaction/index.js.map +1 -1
- package/dist/compaction/interface.d.ts +33 -0
- package/dist/compaction/interface.d.ts.map +1 -0
- package/dist/compaction/interface.js +2 -0
- package/dist/compaction/interface.js.map +1 -0
- package/dist/compaction/managers/index.d.ts +4 -0
- package/dist/compaction/managers/index.d.ts.map +1 -0
- package/dist/compaction/managers/index.js +4 -0
- package/dist/compaction/managers/index.js.map +1 -0
- package/dist/compaction/managers/null.d.ts +12 -0
- package/dist/compaction/managers/null.d.ts.map +1 -0
- package/dist/compaction/managers/null.js +15 -0
- package/dist/compaction/managers/null.js.map +1 -0
- package/dist/compaction/managers/slidingWindow.d.ts +27 -0
- package/dist/compaction/managers/slidingWindow.d.ts.map +1 -0
- package/dist/compaction/managers/slidingWindow.js +41 -0
- package/dist/compaction/managers/slidingWindow.js.map +1 -0
- package/dist/compaction/managers/structured.d.ts +23 -0
- package/dist/compaction/managers/structured.d.ts.map +1 -0
- package/dist/compaction/managers/structured.js +144 -0
- package/dist/compaction/managers/structured.js.map +1 -0
- package/dist/compaction/types.d.ts +1 -1
- package/dist/compaction/types.d.ts.map +1 -1
- package/dist/config/runtime.d.ts +16 -16
- package/dist/config/runtime.js +1 -1
- package/dist/config/runtime.js.map +1 -1
- package/dist/constants/agent/index.d.ts +1 -1
- package/dist/constants/agent/index.d.ts.map +1 -1
- package/dist/gateway/local.d.ts +2 -2
- package/dist/gateway/local.d.ts.map +1 -1
- package/dist/gateway/local.js +10 -1
- package/dist/gateway/local.js.map +1 -1
- package/dist/index.d.ts +18 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -2
- package/dist/index.js.map +1 -1
- package/dist/manager/agent/lifecycle.d.ts.map +1 -1
- package/dist/manager/agent/lifecycle.js +3 -2
- package/dist/manager/agent/lifecycle.js.map +1 -1
- package/dist/manager/run/persistence.d.ts +1 -2
- package/dist/manager/run/persistence.d.ts.map +1 -1
- package/dist/manager/run/persistence.js +2 -1
- package/dist/manager/run/persistence.js.map +1 -1
- package/dist/plugin/__tests__/lifecycle.test.d.ts +2 -0
- package/dist/plugin/__tests__/lifecycle.test.d.ts.map +1 -0
- package/dist/plugin/__tests__/lifecycle.test.js +332 -0
- package/dist/plugin/__tests__/lifecycle.test.js.map +1 -0
- package/dist/plugin/lifecycle.d.ts +2 -2
- package/dist/plugin/lifecycle.d.ts.map +1 -1
- package/dist/plugin/lifecycle.js +28 -2
- package/dist/plugin/lifecycle.js.map +1 -1
- package/dist/plugin/resolver.d.ts +2 -2
- package/dist/plugin/resolver.d.ts.map +1 -1
- package/dist/plugin/resolver.js.map +1 -1
- package/dist/registry/agent/definitions.d.ts +3 -2
- package/dist/registry/agent/definitions.d.ts.map +1 -1
- package/dist/registry/agent/definitions.js.map +1 -1
- package/dist/registry/tool/execute.d.ts +2 -5
- package/dist/registry/tool/execute.d.ts.map +1 -1
- package/dist/registry/tool/execute.js.map +1 -1
- package/dist/runtime/decision/parser.d.ts.map +1 -1
- package/dist/runtime/decision/parser.js +15 -40
- package/dist/runtime/decision/parser.js.map +1 -1
- package/dist/runtime/query/context-cache.d.ts +3 -3
- package/dist/runtime/query/context-cache.d.ts.map +1 -1
- package/dist/runtime/query/context-cache.js.map +1 -1
- package/dist/runtime/query/context.d.ts +1 -1
- package/dist/runtime/query/context.d.ts.map +1 -1
- package/dist/runtime/query/context.js.map +1 -1
- package/dist/runtime/query/events.js +11 -11
- package/dist/runtime/query/events.js.map +1 -1
- package/dist/runtime/query/executor.d.ts +4 -2
- package/dist/runtime/query/executor.d.ts.map +1 -1
- package/dist/runtime/query/executor.js +1 -0
- package/dist/runtime/query/executor.js.map +1 -1
- package/dist/runtime/query/index.d.ts +5 -3
- package/dist/runtime/query/index.d.ts.map +1 -1
- package/dist/runtime/query/index.js +2 -1
- package/dist/runtime/query/index.js.map +1 -1
- package/dist/runtime/query/iteration/index.d.ts +2 -2
- package/dist/runtime/query/iteration/index.d.ts.map +1 -1
- package/dist/runtime/query/iteration/index.js.map +1 -1
- package/dist/runtime/query/iteration/phases/advisory.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/advisory.js.map +1 -1
- package/dist/runtime/query/iteration/phases/checkpoint.d.ts +1 -1
- package/dist/runtime/query/iteration/phases/checkpoint.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/checkpoint.js.map +1 -1
- package/dist/runtime/query/iteration/phases/context.d.ts +2 -2
- package/dist/runtime/query/iteration/phases/context.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/plan.d.ts +1 -1
- package/dist/runtime/query/iteration/phases/plan.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/plan.js.map +1 -1
- package/dist/runtime/query/prompt.d.ts +2 -2
- package/dist/runtime/query/prompt.d.ts.map +1 -1
- package/dist/runtime/query/prompt.js.map +1 -1
- package/dist/runtime/query/result.d.ts +1 -1
- package/dist/runtime/query/result.d.ts.map +1 -1
- package/dist/runtime/query/result.js.map +1 -1
- package/dist/runtime/query/tooling.d.ts +4 -2
- package/dist/runtime/query/tooling.d.ts.map +1 -1
- package/dist/runtime/query/tooling.js +1 -0
- package/dist/runtime/query/tooling.js.map +1 -1
- package/dist/store/conversation/memory.d.ts +1 -1
- package/dist/store/conversation/memory.d.ts.map +1 -1
- package/dist/store/conversation/memory.js +15 -3
- package/dist/store/conversation/memory.js.map +1 -1
- package/dist/store/run/disk.d.ts +1 -2
- package/dist/store/run/disk.d.ts.map +1 -1
- package/dist/store/run/disk.js +21 -13
- package/dist/store/run/disk.js.map +1 -1
- package/dist/tools/builtins/__tests__/structuredOutput.example.d.ts +140 -0
- package/dist/tools/builtins/__tests__/structuredOutput.example.d.ts.map +1 -0
- package/dist/tools/builtins/__tests__/structuredOutput.example.js +183 -0
- package/dist/tools/builtins/__tests__/structuredOutput.example.js.map +1 -0
- package/dist/tools/builtins/__tests__/structuredOutput.test.d.ts +2 -0
- package/dist/tools/builtins/__tests__/structuredOutput.test.d.ts.map +1 -0
- package/dist/tools/builtins/__tests__/structuredOutput.test.js +224 -0
- package/dist/tools/builtins/__tests__/structuredOutput.test.js.map +1 -0
- package/dist/tools/builtins/grep.d.ts.map +1 -1
- package/dist/tools/builtins/grep.js +1 -2
- package/dist/tools/builtins/grep.js.map +1 -1
- package/dist/tools/builtins/index.d.ts +1 -0
- package/dist/tools/builtins/index.d.ts.map +1 -1
- package/dist/tools/builtins/index.js +3 -0
- package/dist/tools/builtins/index.js.map +1 -1
- package/dist/tools/builtins/ls.d.ts +1 -1
- package/dist/tools/builtins/structuredOutput.d.ts +27 -0
- package/dist/tools/builtins/structuredOutput.d.ts.map +1 -0
- package/dist/tools/builtins/structuredOutput.js +46 -0
- package/dist/tools/builtins/structuredOutput.js.map +1 -0
- package/dist/tools/task/list.d.ts +1 -1
- package/dist/tools/task/list.d.ts.map +1 -1
- package/dist/tools/task/list.js.map +1 -1
- package/dist/types/agent/base.d.ts +4 -1
- package/dist/types/agent/base.d.ts.map +1 -1
- package/dist/types/agent/index.d.ts +1 -0
- package/dist/types/agent/index.d.ts.map +1 -1
- package/dist/types/agent/index.js +1 -0
- package/dist/types/agent/index.js.map +1 -1
- package/dist/types/agent/manager.d.ts +27 -0
- package/dist/types/agent/manager.d.ts.map +1 -0
- package/dist/types/agent/manager.js +2 -0
- package/dist/types/agent/manager.js.map +1 -0
- package/dist/types/agent/reactive.d.ts +2 -2
- package/dist/types/agent/reactive.d.ts.map +1 -1
- package/dist/types/agent/supervisor.d.ts +2 -2
- package/dist/types/agent/supervisor.d.ts.map +1 -1
- package/dist/types/agent/task.d.ts +0 -2
- package/dist/types/agent/task.d.ts.map +1 -1
- package/dist/types/agent/task.js +0 -2
- package/dist/types/agent/task.js.map +1 -1
- package/dist/types/common/index.d.ts +0 -1
- package/dist/types/common/index.d.ts.map +1 -1
- package/dist/types/common/index.js +0 -1
- package/dist/types/common/index.js.map +1 -1
- package/dist/types/hitl/index.d.ts +1 -2
- package/dist/types/hitl/index.d.ts.map +1 -1
- package/dist/types/hitl/index.js.map +1 -1
- package/dist/types/invocation/__tests__/state.test.d.ts +2 -0
- package/dist/types/invocation/__tests__/state.test.d.ts.map +1 -0
- package/dist/types/invocation/__tests__/state.test.js +167 -0
- package/dist/types/invocation/__tests__/state.test.js.map +1 -0
- package/dist/types/invocation/index.d.ts +37 -0
- package/dist/types/invocation/index.d.ts.map +1 -0
- package/dist/types/invocation/index.js +23 -0
- package/dist/types/invocation/index.js.map +1 -0
- package/dist/types/plugin/index.d.ts +6 -0
- package/dist/types/plugin/index.d.ts.map +1 -1
- package/dist/types/plugin/index.js +16 -0
- package/dist/types/plugin/index.js.map +1 -1
- package/dist/types/run/events.d.ts +1 -1
- package/dist/types/run/events.d.ts.map +1 -1
- package/dist/types/run/index.d.ts +1 -0
- package/dist/types/run/index.d.ts.map +1 -1
- package/dist/types/run/index.js +1 -0
- package/dist/types/run/index.js.map +1 -1
- package/dist/types/run/metadata.d.ts +1 -1
- package/dist/types/run/metadata.d.ts.map +1 -1
- package/dist/types/run/state.d.ts +1 -1
- package/dist/types/run/state.d.ts.map +1 -1
- package/dist/types/run/stop-reason.d.ts +2 -0
- package/dist/types/run/stop-reason.d.ts.map +1 -0
- package/dist/types/run/stop-reason.js +2 -0
- package/dist/types/run/stop-reason.js.map +1 -0
- package/dist/types/structured-output/index.d.ts +51 -0
- package/dist/types/structured-output/index.d.ts.map +1 -0
- package/dist/types/structured-output/index.js +2 -0
- package/dist/types/structured-output/index.js.map +1 -0
- package/dist/types/tool/index.d.ts +36 -0
- package/dist/types/tool/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/advisory/executor.ts +2 -4
- package/src/agents/AbstractAgent.ts +26 -3
- package/src/agents/PipelineAgent.ts +1 -1
- package/src/agents/ReactiveAgent.ts +1 -0
- package/src/agents/RouterAgent.ts +8 -2
- package/src/agents/SupervisorAgent.ts +5 -1
- package/src/agents/__tests__/lock.test.ts +158 -0
- package/src/agents/index.ts +2 -0
- package/src/agents/lock.ts +66 -0
- package/src/bridge/a2a/message.ts +1 -2
- package/src/bridge/tools/connector/router.ts +4 -5
- package/src/compaction/__tests__/SlidingWindowManager.test.ts +139 -0
- package/src/compaction/__tests__/dangling.test.ts +447 -0
- package/src/compaction/__tests__/factory.test.ts +53 -0
- package/src/compaction/dangling.ts +321 -0
- package/src/compaction/factory.ts +41 -0
- package/src/compaction/index.ts +14 -0
- package/src/compaction/interface.ts +35 -0
- package/src/compaction/managers/index.ts +3 -0
- package/src/compaction/managers/null.ts +19 -0
- package/src/compaction/managers/slidingWindow.ts +57 -0
- package/src/compaction/managers/structured.ts +169 -0
- package/src/compaction/types.ts +1 -1
- package/src/config/runtime.ts +1 -1
- package/src/constants/agent/index.ts +1 -1
- package/src/gateway/local.ts +13 -4
- package/src/index.ts +38 -1
- package/src/manager/agent/lifecycle.ts +3 -2
- package/src/manager/run/persistence.ts +3 -8
- package/src/plugin/__tests__/lifecycle.test.ts +430 -0
- package/src/plugin/lifecycle.ts +32 -6
- package/src/plugin/resolver.ts +3 -3
- package/src/registry/agent/definitions.ts +3 -2
- package/src/registry/tool/execute.ts +2 -5
- package/src/runtime/decision/parser.ts +15 -40
- package/src/runtime/query/context-cache.ts +3 -4
- package/src/runtime/query/context.ts +1 -2
- package/src/runtime/query/events.ts +11 -11
- package/src/runtime/query/executor.ts +5 -3
- package/src/runtime/query/index.ts +11 -4
- package/src/runtime/query/iteration/index.ts +2 -2
- package/src/runtime/query/iteration/phases/advisory.ts +1 -2
- package/src/runtime/query/iteration/phases/checkpoint.ts +1 -2
- package/src/runtime/query/iteration/phases/context.ts +2 -2
- package/src/runtime/query/iteration/phases/plan.ts +1 -2
- package/src/runtime/query/prompt.ts +3 -3
- package/src/runtime/query/result.ts +1 -2
- package/src/runtime/query/tooling.ts +5 -2
- package/src/store/conversation/memory.ts +21 -5
- package/src/store/run/disk.ts +18 -16
- package/src/tools/builtins/__tests__/structuredOutput.example.ts +221 -0
- package/src/tools/builtins/__tests__/structuredOutput.test.ts +275 -0
- package/src/tools/builtins/grep.ts +1 -2
- package/src/tools/builtins/index.ts +3 -0
- package/src/tools/builtins/structuredOutput.ts +55 -0
- package/src/tools/task/list.ts +1 -2
- package/src/types/agent/base.ts +5 -1
- package/src/types/agent/index.ts +1 -0
- package/src/types/agent/manager.ts +36 -0
- package/src/types/agent/reactive.ts +2 -2
- package/src/types/agent/supervisor.ts +2 -2
- package/src/types/agent/task.ts +0 -4
- package/src/types/common/index.ts +0 -2
- package/src/types/hitl/index.ts +1 -2
- package/src/types/invocation/__tests__/state.test.ts +210 -0
- package/src/types/invocation/index.ts +55 -0
- package/src/types/plugin/index.ts +19 -0
- package/src/types/run/events.ts +1 -10
- package/src/types/run/index.ts +1 -0
- package/src/types/run/metadata.ts +1 -1
- package/src/types/run/state.ts +1 -1
- package/src/types/run/stop-reason.ts +10 -0
- package/src/types/structured-output/index.ts +56 -0
- package/src/types/tool/index.ts +45 -0
|
@@ -0,0 +1,447 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import type {
|
|
3
|
+
AssistantMessage,
|
|
4
|
+
Message,
|
|
5
|
+
ToolMessage,
|
|
6
|
+
UserMessage,
|
|
7
|
+
} from '../../types/message/index.js'
|
|
8
|
+
import { findDanglingMessages, findSafeTrimIndex, removeDanglingMessages } from '../dangling.js'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Test helpers to create messages with proper typing.
|
|
12
|
+
*/
|
|
13
|
+
function createUserMessage(content: string): UserMessage {
|
|
14
|
+
return { role: 'user', content, timestamp: Date.now() }
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function createAssistantMessage(content: string | null, toolCallIds?: string[]): AssistantMessage {
|
|
18
|
+
const msg: AssistantMessage = {
|
|
19
|
+
role: 'assistant',
|
|
20
|
+
content,
|
|
21
|
+
timestamp: Date.now(),
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (toolCallIds && toolCallIds.length > 0) {
|
|
25
|
+
msg.toolCalls = toolCallIds.map((id) => ({
|
|
26
|
+
id,
|
|
27
|
+
type: 'function',
|
|
28
|
+
function: { name: 'test_tool', arguments: '{}' },
|
|
29
|
+
}))
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return msg
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function createToolMessage(content: string, toolCallId: string): ToolMessage {
|
|
36
|
+
return { role: 'tool', content, toolCallId, timestamp: Date.now() }
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
describe('findDanglingMessages', () => {
|
|
40
|
+
it('should find no dangling messages in a valid sequence', () => {
|
|
41
|
+
const messages: Message[] = [
|
|
42
|
+
createUserMessage('test'),
|
|
43
|
+
createAssistantMessage('response', ['call-1']),
|
|
44
|
+
createToolMessage('result', 'call-1'),
|
|
45
|
+
createUserMessage('next'),
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
const result = findDanglingMessages(messages)
|
|
49
|
+
|
|
50
|
+
expect(result.isValid).toBe(true)
|
|
51
|
+
expect(result.assistantsWithUnmatchedCalls).toHaveLength(0)
|
|
52
|
+
expect(result.orphanedToolMessages).toHaveLength(0)
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
it('should find assistant messages with unmatched tool calls', () => {
|
|
56
|
+
const messages: Message[] = [
|
|
57
|
+
createUserMessage('test'),
|
|
58
|
+
createAssistantMessage('response', ['call-1']),
|
|
59
|
+
// Missing tool message for call-1
|
|
60
|
+
createUserMessage('next'),
|
|
61
|
+
]
|
|
62
|
+
|
|
63
|
+
const result = findDanglingMessages(messages)
|
|
64
|
+
|
|
65
|
+
expect(result.isValid).toBe(false)
|
|
66
|
+
expect(result.assistantsWithUnmatchedCalls).toContain(1)
|
|
67
|
+
expect(result.orphanedToolMessages).toHaveLength(0)
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
it('should find orphaned tool messages with no matching assistant call', () => {
|
|
71
|
+
const messages: Message[] = [
|
|
72
|
+
createUserMessage('test'),
|
|
73
|
+
createAssistantMessage('response', ['call-1']),
|
|
74
|
+
createToolMessage('result', 'call-1'),
|
|
75
|
+
createToolMessage('orphan', 'call-2'), // No assistant call for call-2
|
|
76
|
+
]
|
|
77
|
+
|
|
78
|
+
const result = findDanglingMessages(messages)
|
|
79
|
+
|
|
80
|
+
expect(result.isValid).toBe(false)
|
|
81
|
+
expect(result.orphanedToolMessages).toContain(3)
|
|
82
|
+
expect(result.assistantsWithUnmatchedCalls).toHaveLength(0)
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
it('should handle multiple tool calls in a single assistant message', () => {
|
|
86
|
+
const messages: Message[] = [
|
|
87
|
+
createUserMessage('test'),
|
|
88
|
+
createAssistantMessage('response', ['call-1', 'call-2']),
|
|
89
|
+
createToolMessage('result-1', 'call-1'),
|
|
90
|
+
createToolMessage('result-2', 'call-2'),
|
|
91
|
+
createUserMessage('next'),
|
|
92
|
+
]
|
|
93
|
+
|
|
94
|
+
const result = findDanglingMessages(messages)
|
|
95
|
+
|
|
96
|
+
expect(result.isValid).toBe(true)
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
it('should find partially satisfied multiple tool calls', () => {
|
|
100
|
+
const messages: Message[] = [
|
|
101
|
+
createUserMessage('test'),
|
|
102
|
+
createAssistantMessage('response', ['call-1', 'call-2']),
|
|
103
|
+
createToolMessage('result-1', 'call-1'),
|
|
104
|
+
// Missing result for call-2
|
|
105
|
+
]
|
|
106
|
+
|
|
107
|
+
const result = findDanglingMessages(messages)
|
|
108
|
+
|
|
109
|
+
expect(result.isValid).toBe(false)
|
|
110
|
+
expect(result.assistantsWithUnmatchedCalls).toContain(1)
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
it('should find no dangling messages in an assistant-only sequence', () => {
|
|
114
|
+
const messages: Message[] = [
|
|
115
|
+
createUserMessage('test'),
|
|
116
|
+
createAssistantMessage('response without tools'),
|
|
117
|
+
createUserMessage('next'),
|
|
118
|
+
]
|
|
119
|
+
|
|
120
|
+
const result = findDanglingMessages(messages)
|
|
121
|
+
|
|
122
|
+
expect(result.isValid).toBe(true)
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
it('should handle empty message array', () => {
|
|
126
|
+
const messages: Message[] = []
|
|
127
|
+
|
|
128
|
+
const result = findDanglingMessages(messages)
|
|
129
|
+
|
|
130
|
+
expect(result.isValid).toBe(true)
|
|
131
|
+
expect(result.assistantsWithUnmatchedCalls).toHaveLength(0)
|
|
132
|
+
expect(result.orphanedToolMessages).toHaveLength(0)
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
it('should find multiple dangling issues in complex sequence', () => {
|
|
136
|
+
const messages: Message[] = [
|
|
137
|
+
createUserMessage('test'),
|
|
138
|
+
createAssistantMessage('response', ['call-1']), // Unmatched
|
|
139
|
+
createToolMessage('orphan-1', 'call-999'), // Orphaned
|
|
140
|
+
createAssistantMessage('response2', ['call-2']),
|
|
141
|
+
createToolMessage('result-2', 'call-2'),
|
|
142
|
+
createToolMessage('orphan-2', 'call-888'), // Orphaned
|
|
143
|
+
]
|
|
144
|
+
|
|
145
|
+
const result = findDanglingMessages(messages)
|
|
146
|
+
|
|
147
|
+
expect(result.isValid).toBe(false)
|
|
148
|
+
expect(result.assistantsWithUnmatchedCalls).toContain(1)
|
|
149
|
+
expect(result.orphanedToolMessages).toContain(2)
|
|
150
|
+
expect(result.orphanedToolMessages).toContain(5)
|
|
151
|
+
})
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
describe('removeDanglingMessages', () => {
|
|
155
|
+
it('should return a copy when messages are already valid', () => {
|
|
156
|
+
const messages: Message[] = [
|
|
157
|
+
createUserMessage('test'),
|
|
158
|
+
createAssistantMessage('response', ['call-1']),
|
|
159
|
+
createToolMessage('result', 'call-1'),
|
|
160
|
+
]
|
|
161
|
+
|
|
162
|
+
const cleaned = removeDanglingMessages(messages)
|
|
163
|
+
|
|
164
|
+
expect(cleaned).toEqual(messages)
|
|
165
|
+
expect(cleaned).not.toBe(messages) // Different object
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
it('should remove assistant messages with unmatched tool calls', () => {
|
|
169
|
+
const messages: Message[] = [
|
|
170
|
+
createUserMessage('test'),
|
|
171
|
+
createAssistantMessage('response', ['call-1']), // Will be removed
|
|
172
|
+
createUserMessage('next'),
|
|
173
|
+
]
|
|
174
|
+
|
|
175
|
+
const cleaned = removeDanglingMessages(messages)
|
|
176
|
+
|
|
177
|
+
expect(cleaned).toHaveLength(2)
|
|
178
|
+
expect(cleaned[0]?.role).toBe('user')
|
|
179
|
+
expect(cleaned[1]?.role).toBe('user')
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
it('should remove orphaned tool messages', () => {
|
|
183
|
+
const messages: Message[] = [
|
|
184
|
+
createUserMessage('test'),
|
|
185
|
+
createAssistantMessage('response', ['call-1']),
|
|
186
|
+
createToolMessage('result', 'call-1'),
|
|
187
|
+
createToolMessage('orphan', 'call-999'), // Will be removed
|
|
188
|
+
]
|
|
189
|
+
|
|
190
|
+
const cleaned = removeDanglingMessages(messages)
|
|
191
|
+
|
|
192
|
+
expect(cleaned).toHaveLength(3)
|
|
193
|
+
expect(cleaned[3]).toBeUndefined()
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
it('should remove assistant and following orphaned tool messages together', () => {
|
|
197
|
+
const messages: Message[] = [
|
|
198
|
+
createUserMessage('test'),
|
|
199
|
+
createAssistantMessage('response', ['call-1', 'call-2']), // call-2 unmatched → assistant flagged
|
|
200
|
+
createToolMessage('attempt', 'call-1'), // Matches assistant's call list → removed with assistant
|
|
201
|
+
createUserMessage('next'),
|
|
202
|
+
]
|
|
203
|
+
|
|
204
|
+
const cleaned = removeDanglingMessages(messages)
|
|
205
|
+
|
|
206
|
+
expect(cleaned).toHaveLength(2)
|
|
207
|
+
expect(cleaned[0]?.role).toBe('user')
|
|
208
|
+
expect(cleaned[0]?.content).toBe('test')
|
|
209
|
+
expect(cleaned[1]?.role).toBe('user')
|
|
210
|
+
expect(cleaned[1]?.content).toBe('next')
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
it('should preserve message order while removing dangling messages', () => {
|
|
214
|
+
const messages: Message[] = [
|
|
215
|
+
createUserMessage('1'),
|
|
216
|
+
createAssistantMessage('response', ['call-1']),
|
|
217
|
+
createToolMessage('result', 'call-1'),
|
|
218
|
+
createUserMessage('2'),
|
|
219
|
+
createAssistantMessage('response2', ['call-2']), // Unmatched
|
|
220
|
+
createUserMessage('3'),
|
|
221
|
+
]
|
|
222
|
+
|
|
223
|
+
const cleaned = removeDanglingMessages(messages)
|
|
224
|
+
|
|
225
|
+
// Only the unmatched assistant is removed; valid pair and users are kept in order
|
|
226
|
+
expect(cleaned).toHaveLength(5)
|
|
227
|
+
expect(cleaned[0]?.content).toBe('1')
|
|
228
|
+
expect(cleaned[1]?.content).toBe('response')
|
|
229
|
+
expect(cleaned[2]?.content).toBe('result')
|
|
230
|
+
expect(cleaned[3]?.content).toBe('2')
|
|
231
|
+
expect(cleaned[4]?.content).toBe('3')
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
it('should handle complex cleanup with multiple dangling issues', () => {
|
|
235
|
+
const messages: Message[] = [
|
|
236
|
+
createUserMessage('start'),
|
|
237
|
+
createAssistantMessage('response1', ['call-1']), // Unmatched
|
|
238
|
+
createToolMessage('orphan-1', 'call-999'), // Orphaned
|
|
239
|
+
createAssistantMessage('response2', ['call-2']),
|
|
240
|
+
createToolMessage('result-2', 'call-2'),
|
|
241
|
+
createToolMessage('orphan-2', 'call-888'), // Orphaned
|
|
242
|
+
createUserMessage('end'),
|
|
243
|
+
]
|
|
244
|
+
|
|
245
|
+
const cleaned = removeDanglingMessages(messages)
|
|
246
|
+
|
|
247
|
+
// Removed: assistant('response1') (unmatched), orphan-1, orphan-2
|
|
248
|
+
// Kept: start, response2, result-2 (valid pair), end
|
|
249
|
+
expect(cleaned).toHaveLength(4)
|
|
250
|
+
expect(cleaned[0]?.content).toBe('start')
|
|
251
|
+
expect(cleaned[1]?.content).toBe('response2')
|
|
252
|
+
expect(cleaned[2]?.content).toBe('result-2')
|
|
253
|
+
expect(cleaned[3]?.content).toBe('end')
|
|
254
|
+
})
|
|
255
|
+
|
|
256
|
+
it('should remove dangling assistant messages when they have follow-up tool attempts', () => {
|
|
257
|
+
const messages: Message[] = [
|
|
258
|
+
createUserMessage('test'),
|
|
259
|
+
createAssistantMessage('response', ['call-1', 'call-2']), // call-2 unmatched → assistant flagged
|
|
260
|
+
createToolMessage('wrong-result', 'call-1'), // Matches assistant's call list → removed with assistant
|
|
261
|
+
createUserMessage('next'),
|
|
262
|
+
]
|
|
263
|
+
|
|
264
|
+
const cleaned = removeDanglingMessages(messages)
|
|
265
|
+
|
|
266
|
+
expect(cleaned).toHaveLength(2)
|
|
267
|
+
expect(cleaned[0]?.content).toBe('test')
|
|
268
|
+
expect(cleaned[1]?.content).toBe('next')
|
|
269
|
+
})
|
|
270
|
+
})
|
|
271
|
+
|
|
272
|
+
describe('findSafeTrimIndex', () => {
|
|
273
|
+
it('should not trim when target is 0', () => {
|
|
274
|
+
const messages: Message[] = [
|
|
275
|
+
createUserMessage('test'),
|
|
276
|
+
createAssistantMessage('response', ['call-1']),
|
|
277
|
+
createToolMessage('result', 'call-1'),
|
|
278
|
+
]
|
|
279
|
+
|
|
280
|
+
const safeIdx = findSafeTrimIndex(messages, 0)
|
|
281
|
+
|
|
282
|
+
expect(safeIdx).toBe(0)
|
|
283
|
+
})
|
|
284
|
+
|
|
285
|
+
it('should not trim when target is at end', () => {
|
|
286
|
+
const messages: Message[] = [
|
|
287
|
+
createUserMessage('test'),
|
|
288
|
+
createAssistantMessage('response', ['call-1']),
|
|
289
|
+
createToolMessage('result', 'call-1'),
|
|
290
|
+
]
|
|
291
|
+
|
|
292
|
+
const safeIdx = findSafeTrimIndex(messages, messages.length)
|
|
293
|
+
|
|
294
|
+
expect(safeIdx).toBe(messages.length)
|
|
295
|
+
})
|
|
296
|
+
|
|
297
|
+
it('should clamp negative target index to 0', () => {
|
|
298
|
+
const messages: Message[] = [createUserMessage('test')]
|
|
299
|
+
|
|
300
|
+
const safeIdx = findSafeTrimIndex(messages, -5)
|
|
301
|
+
|
|
302
|
+
expect(safeIdx).toBeGreaterThanOrEqual(0)
|
|
303
|
+
})
|
|
304
|
+
|
|
305
|
+
it('should clamp target beyond array length', () => {
|
|
306
|
+
const messages: Message[] = [createUserMessage('test')]
|
|
307
|
+
|
|
308
|
+
const safeIdx = findSafeTrimIndex(messages, 1000)
|
|
309
|
+
|
|
310
|
+
expect(safeIdx).toBeLessThanOrEqual(messages.length)
|
|
311
|
+
})
|
|
312
|
+
|
|
313
|
+
it('should skip orphaned tool messages at trim point', () => {
|
|
314
|
+
const messages: Message[] = [
|
|
315
|
+
createUserMessage('test'),
|
|
316
|
+
createAssistantMessage('response', ['call-1']),
|
|
317
|
+
createToolMessage('result', 'call-1'),
|
|
318
|
+
createToolMessage('orphan', 'call-999'), // Orphaned at trim point
|
|
319
|
+
createUserMessage('next'),
|
|
320
|
+
]
|
|
321
|
+
|
|
322
|
+
const safeIdx = findSafeTrimIndex(messages, 3)
|
|
323
|
+
|
|
324
|
+
// Should skip the orphaned tool message at index 3
|
|
325
|
+
expect(safeIdx).toBeGreaterThan(3)
|
|
326
|
+
})
|
|
327
|
+
|
|
328
|
+
it('should preserve complete tool call/result pairs', () => {
|
|
329
|
+
const messages: Message[] = [
|
|
330
|
+
createUserMessage('test'),
|
|
331
|
+
createAssistantMessage('response', ['call-1']),
|
|
332
|
+
createToolMessage('result', 'call-1'),
|
|
333
|
+
createUserMessage('next'),
|
|
334
|
+
]
|
|
335
|
+
|
|
336
|
+
// Try to trim in middle of pair
|
|
337
|
+
const safeIdx = findSafeTrimIndex(messages, 2)
|
|
338
|
+
|
|
339
|
+
// Algorithm advances forward past the pair — safeIdx=3 keeps the pair intact in slice(0, safeIdx)
|
|
340
|
+
expect(safeIdx).toBe(3)
|
|
341
|
+
})
|
|
342
|
+
|
|
343
|
+
it('should not start with a tool message after trim', () => {
|
|
344
|
+
const messages: Message[] = [
|
|
345
|
+
createUserMessage('test'),
|
|
346
|
+
createAssistantMessage('response', ['call-1']),
|
|
347
|
+
createToolMessage('result', 'call-1'),
|
|
348
|
+
createToolMessage('orphan', 'call-999'),
|
|
349
|
+
createUserMessage('next'),
|
|
350
|
+
]
|
|
351
|
+
|
|
352
|
+
const safeIdx = findSafeTrimIndex(messages, 3)
|
|
353
|
+
|
|
354
|
+
if (safeIdx < messages.length) {
|
|
355
|
+
const firstAfterTrim = messages[safeIdx]
|
|
356
|
+
if (firstAfterTrim) {
|
|
357
|
+
expect(firstAfterTrim.role).not.toBe('tool')
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
})
|
|
361
|
+
|
|
362
|
+
it('should handle complex scenario with multiple pairs', () => {
|
|
363
|
+
const messages: Message[] = [
|
|
364
|
+
createUserMessage('1'),
|
|
365
|
+
createAssistantMessage('response1', ['call-1']),
|
|
366
|
+
createToolMessage('result-1', 'call-1'),
|
|
367
|
+
createUserMessage('2'),
|
|
368
|
+
createAssistantMessage('response2', ['call-2']),
|
|
369
|
+
createToolMessage('result-2', 'call-2'),
|
|
370
|
+
createUserMessage('3'),
|
|
371
|
+
]
|
|
372
|
+
|
|
373
|
+
// Try to trim between the two pairs
|
|
374
|
+
const safeIdx = findSafeTrimIndex(messages, 4)
|
|
375
|
+
|
|
376
|
+
// Should either include or exclude the second pair completely
|
|
377
|
+
const kept = messages.slice(0, safeIdx)
|
|
378
|
+
const danglingResult = findDanglingMessages(kept)
|
|
379
|
+
expect(danglingResult.isValid).toBe(true)
|
|
380
|
+
})
|
|
381
|
+
|
|
382
|
+
it('should produce valid message sequence after trim', () => {
|
|
383
|
+
const messages: Message[] = [
|
|
384
|
+
createUserMessage('1'),
|
|
385
|
+
createAssistantMessage('response1', ['call-1']),
|
|
386
|
+
createToolMessage('result-1', 'call-1'),
|
|
387
|
+
createUserMessage('2'),
|
|
388
|
+
createAssistantMessage('response2', ['call-2']),
|
|
389
|
+
createToolMessage('result-2', 'call-2'),
|
|
390
|
+
createUserMessage('3'),
|
|
391
|
+
createAssistantMessage('unmatched', ['call-3']), // Unmatched
|
|
392
|
+
]
|
|
393
|
+
|
|
394
|
+
// Try various trim points — target within bounds (excludes edge case target=messages.length
|
|
395
|
+
// where trailing unmatched assistant cannot be trimmed forward)
|
|
396
|
+
for (let target = 0; target < messages.length; target++) {
|
|
397
|
+
const safeIdx = findSafeTrimIndex(messages, target)
|
|
398
|
+
const keptMessages = messages.slice(0, safeIdx)
|
|
399
|
+
|
|
400
|
+
const result = findDanglingMessages(keptMessages)
|
|
401
|
+
expect(result.isValid).toBe(true)
|
|
402
|
+
}
|
|
403
|
+
})
|
|
404
|
+
|
|
405
|
+
it('should not trim unnecessarily if sequence is already valid', () => {
|
|
406
|
+
const messages: Message[] = [
|
|
407
|
+
createUserMessage('test'),
|
|
408
|
+
createAssistantMessage('response', ['call-1']),
|
|
409
|
+
createToolMessage('result', 'call-1'),
|
|
410
|
+
createUserMessage('next'),
|
|
411
|
+
]
|
|
412
|
+
|
|
413
|
+
const safeIdx = findSafeTrimIndex(messages, messages.length)
|
|
414
|
+
|
|
415
|
+
expect(safeIdx).toBe(messages.length)
|
|
416
|
+
})
|
|
417
|
+
|
|
418
|
+
it('should advance past unsatisfied assistant message and its following tool attempt', () => {
|
|
419
|
+
const messages: Message[] = [
|
|
420
|
+
createUserMessage('test'),
|
|
421
|
+
createAssistantMessage('response', ['call-1']), // Unmatched at index 1
|
|
422
|
+
createToolMessage('attempt', 'call-1'), // Following attempt at index 2
|
|
423
|
+
createUserMessage('next'),
|
|
424
|
+
]
|
|
425
|
+
|
|
426
|
+
const safeIdx = findSafeTrimIndex(messages, 2)
|
|
427
|
+
|
|
428
|
+
// Should skip past the unmatched call and its following tool message
|
|
429
|
+
expect(safeIdx).toBeGreaterThan(2)
|
|
430
|
+
const kept = messages.slice(0, safeIdx)
|
|
431
|
+
expect(findDanglingMessages(kept).isValid).toBe(true)
|
|
432
|
+
})
|
|
433
|
+
|
|
434
|
+
it('should handle all-tool-messages scenario', () => {
|
|
435
|
+
const messages: Message[] = [
|
|
436
|
+
createUserMessage('test'),
|
|
437
|
+
createToolMessage('orphan-1', 'call-999'),
|
|
438
|
+
createToolMessage('orphan-2', 'call-888'),
|
|
439
|
+
]
|
|
440
|
+
|
|
441
|
+
const safeIdx = findSafeTrimIndex(messages, 1)
|
|
442
|
+
|
|
443
|
+
// Algorithm advances past both orphan tools — kept portion (slice from safeIdx) is empty and valid
|
|
444
|
+
const kept = messages.slice(safeIdx)
|
|
445
|
+
expect(findDanglingMessages(kept).isValid).toBe(true)
|
|
446
|
+
})
|
|
447
|
+
})
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { RUNTIME_DEFAULTS } from '../../config/runtime.js'
|
|
3
|
+
import { createConversationManager } from '../factory.js'
|
|
4
|
+
import {
|
|
5
|
+
NullManager,
|
|
6
|
+
SlidingWindowManager,
|
|
7
|
+
StructuredCompactionManager,
|
|
8
|
+
} from '../managers/index.js'
|
|
9
|
+
|
|
10
|
+
describe('createConversationManager', () => {
|
|
11
|
+
const config = RUNTIME_DEFAULTS.compaction
|
|
12
|
+
|
|
13
|
+
it('should create NullManager for disabled strategy', () => {
|
|
14
|
+
const manager = createConversationManager('disabled', config)
|
|
15
|
+
expect(manager).toBeInstanceOf(NullManager)
|
|
16
|
+
expect(manager.name).toBe('null')
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
it('should create SlidingWindowManager for sliding-window strategy', () => {
|
|
20
|
+
const manager = createConversationManager('sliding-window', config)
|
|
21
|
+
expect(manager).toBeInstanceOf(SlidingWindowManager)
|
|
22
|
+
expect(manager.name).toBe('sliding-window')
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
it('should create StructuredCompactionManager for structured strategy', () => {
|
|
26
|
+
const manager = createConversationManager('structured', config)
|
|
27
|
+
expect(manager).toBeInstanceOf(StructuredCompactionManager)
|
|
28
|
+
expect(manager.name).toBe('structured')
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
it('should throw on unknown strategy', () => {
|
|
32
|
+
// @ts-expect-error - Testing invalid input
|
|
33
|
+
expect(() => createConversationManager('unknown', config)).toThrow()
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
it('should pass keepRecentMessages to SlidingWindowManager', () => {
|
|
37
|
+
const customConfig = {
|
|
38
|
+
...config,
|
|
39
|
+
keepRecentMessages: 8,
|
|
40
|
+
}
|
|
41
|
+
const manager = createConversationManager('sliding-window', customConfig)
|
|
42
|
+
expect(manager).toBeInstanceOf(SlidingWindowManager)
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it('should pass config to StructuredCompactionManager', () => {
|
|
46
|
+
const customConfig = {
|
|
47
|
+
...config,
|
|
48
|
+
convoTextBudget: 5000,
|
|
49
|
+
}
|
|
50
|
+
const manager = createConversationManager('structured', customConfig)
|
|
51
|
+
expect(manager).toBeInstanceOf(StructuredCompactionManager)
|
|
52
|
+
})
|
|
53
|
+
})
|