@owlmeans/agent 0.1.18-rc.47 → 0.1.18-rc.49

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.
Files changed (111) hide show
  1. package/README.md +4 -4
  2. package/agent-meta/manifest.json +2 -2
  3. package/agent-meta/skills/agent/SKILL.md +9 -9
  4. package/build/helpers/compaction/types.d.ts +49 -0
  5. package/build/helpers/compaction/types.d.ts.map +1 -0
  6. package/build/helpers/compaction/types.js.map +1 -0
  7. package/build/helpers/compaction.d.ts +6 -20
  8. package/build/helpers/compaction.d.ts.map +1 -1
  9. package/build/helpers/compaction.js +72 -81
  10. package/build/helpers/compaction.js.map +1 -1
  11. package/build/helpers/index.d.ts +3 -1
  12. package/build/helpers/index.d.ts.map +1 -1
  13. package/build/helpers/index.js.map +1 -1
  14. package/build/helpers/rolling/types.d.ts +16 -0
  15. package/build/helpers/rolling/types.d.ts.map +1 -0
  16. package/build/helpers/rolling/types.js +2 -0
  17. package/build/helpers/rolling/types.js.map +1 -0
  18. package/build/helpers/rolling.d.ts +1 -1
  19. package/build/helpers/rolling.d.ts.map +1 -1
  20. package/build/helpers/tools/types.d.ts +38 -0
  21. package/build/helpers/tools/types.d.ts.map +1 -0
  22. package/build/helpers/tools/types.js +2 -0
  23. package/build/helpers/{types.js.map → tools/types.js.map} +1 -1
  24. package/build/helpers/tools.d.ts +6 -26
  25. package/build/helpers/tools.d.ts.map +1 -1
  26. package/build/helpers/tools.js +27 -42
  27. package/build/helpers/tools.js.map +1 -1
  28. package/build/model.d.ts.map +1 -1
  29. package/build/model.js +4 -4
  30. package/build/model.js.map +1 -1
  31. package/build/pipeline/index.d.ts +2 -1
  32. package/build/pipeline/index.d.ts.map +1 -1
  33. package/build/pipeline/results/types.d.ts +139 -0
  34. package/build/pipeline/results/types.d.ts.map +1 -0
  35. package/build/pipeline/results/types.js +2 -0
  36. package/build/pipeline/results/types.js.map +1 -0
  37. package/build/pipeline/{types.local.d.ts → results/types.local.d.ts} +0 -18
  38. package/build/pipeline/results/types.local.d.ts.map +1 -0
  39. package/build/pipeline/results/types.local.js.map +1 -0
  40. package/build/pipeline/results.d.ts +2 -1
  41. package/build/pipeline/results.d.ts.map +1 -1
  42. package/build/pipeline/results.js +22 -22
  43. package/build/pipeline/results.js.map +1 -1
  44. package/build/pipeline/runner/types.d.ts +293 -0
  45. package/build/pipeline/runner/types.d.ts.map +1 -0
  46. package/build/pipeline/runner/types.js +2 -0
  47. package/build/pipeline/runner/types.js.map +1 -0
  48. package/build/pipeline/runner/types.local.d.ts +19 -0
  49. package/build/pipeline/runner/types.local.d.ts.map +1 -0
  50. package/build/pipeline/runner/types.local.js +2 -0
  51. package/build/pipeline/runner/types.local.js.map +1 -0
  52. package/build/pipeline/runner.d.ts +1 -1
  53. package/build/pipeline/runner.d.ts.map +1 -1
  54. package/build/pipeline/runner.js +10 -9
  55. package/build/pipeline/runner.js.map +1 -1
  56. package/build/pipeline/types.d.ts +2 -93
  57. package/build/pipeline/types.d.ts.map +1 -1
  58. package/build/plugins/export.d.ts +2 -2
  59. package/build/plugins/export.d.ts.map +1 -1
  60. package/build/plugins/export.js +2 -2
  61. package/build/plugins/export.js.map +1 -1
  62. package/build/plugins/inquiry.d.ts.map +1 -1
  63. package/build/plugins/inquiry.js +4 -4
  64. package/build/plugins/inquiry.js.map +1 -1
  65. package/build/plugins/memory-events.d.ts +3 -1
  66. package/build/plugins/memory-events.d.ts.map +1 -1
  67. package/build/plugins/memory-events.js +4 -2
  68. package/build/plugins/memory-events.js.map +1 -1
  69. package/build/plugins/memory-graph.d.ts +5 -1
  70. package/build/plugins/memory-graph.d.ts.map +1 -1
  71. package/build/plugins/memory-graph.js +4 -2
  72. package/build/plugins/memory-graph.js.map +1 -1
  73. package/build/plugins/summarize.d.ts.map +1 -1
  74. package/build/plugins/summarize.js +2 -2
  75. package/build/plugins/summarize.js.map +1 -1
  76. package/build/types.d.ts +5 -3
  77. package/build/types.d.ts.map +1 -1
  78. package/package.json +9 -9
  79. package/src/helpers/compaction/types.ts +51 -0
  80. package/src/helpers/compaction.ts +98 -101
  81. package/src/helpers/index.ts +3 -1
  82. package/src/helpers/rolling/types.ts +16 -0
  83. package/src/helpers/rolling.ts +1 -1
  84. package/src/helpers/tools/types.ts +37 -0
  85. package/src/helpers/tools.ts +39 -47
  86. package/src/model.ts +4 -4
  87. package/src/pipeline/index.ts +2 -1
  88. package/src/pipeline/{types.local.ts → results/types.local.ts} +0 -21
  89. package/src/pipeline/results/types.ts +152 -0
  90. package/src/pipeline/results.ts +25 -24
  91. package/src/pipeline/runner/types.local.ts +21 -0
  92. package/src/pipeline/{types.ts → runner/types.ts} +4 -156
  93. package/src/pipeline/runner.ts +14 -12
  94. package/src/plugins/export.ts +2 -2
  95. package/src/plugins/inquiry.ts +4 -4
  96. package/src/plugins/memory-events.ts +8 -2
  97. package/src/plugins/memory-graph.ts +8 -2
  98. package/src/plugins/summarize.ts +2 -2
  99. package/src/types.ts +4 -4
  100. package/tests/inquiry.spec.ts +14 -15
  101. package/tests/pipeline-inquiry.spec.ts +3 -3
  102. package/tests/plugins.spec.ts +10 -10
  103. package/tests/summary.spec.ts +8 -8
  104. package/tests/tools.spec.ts +11 -11
  105. package/build/helpers/types.d.ts +0 -42
  106. package/build/helpers/types.d.ts.map +0 -1
  107. package/build/pipeline/types.local.d.ts.map +0 -1
  108. package/build/pipeline/types.local.js.map +0 -1
  109. package/src/helpers/types.ts +0 -43
  110. /package/build/helpers/{types.js → compaction/types.js} +0 -0
  111. /package/build/pipeline/{types.local.js → results/types.local.js} +0 -0
@@ -19,7 +19,7 @@ import type { MemoryGraphApi, MemoryGraphOptions } from './types.js'
19
19
  * make every write a potential act of forgetting, which is not a decision a single caller has the
20
20
  * standing to take.
21
21
  */
22
- export const memoryGraph = (
22
+ export const makeMemoryGraphApi = (
23
23
  store: MemoryGraphStore,
24
24
  options: Pick<MemoryGraphOptions, 'model' | 'maxNodeChars' | 'action'> & { run?: AgentRun } = {},
25
25
  ): MemoryGraphApi => {
@@ -96,7 +96,7 @@ export const memoryGraphPlugin = (options: MemoryGraphOptions = {}): AgentPlugin
96
96
 
97
97
  const api = (run: AgentRun): MemoryGraphApi | null => store == null
98
98
  ? null
99
- : memoryGraph(store, { ...options, run })
99
+ : makeMemoryGraphApi(store, { ...options, run })
100
100
 
101
101
  return {
102
102
  alias: MEMORY_GRAPH_PLUGIN,
@@ -188,3 +188,9 @@ export const memoryGraphPlugin = (options: MemoryGraphOptions = {}): AgentPlugin
188
188
  },
189
189
  }
190
190
  }
191
+
192
+ /** @deprecated compat:factory-refactor — use `makeMemoryGraphApi(…)` */
193
+ export const memoryGraph = (
194
+ store: MemoryGraphStore,
195
+ options: Pick<MemoryGraphOptions, 'model' | 'maxNodeChars' | 'action'> & { run?: AgentRun } = {},
196
+ ): MemoryGraphApi => makeMemoryGraphApi(store, options)
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  AgentRunStatus, DEFAULT_ADVICE_CHARS, DEFAULT_EVENT_WINDOW, DEFAULT_SUMMARY_CHARS, truncateAt,
3
3
  } from '@owlmeans/agent-common'
4
- import { composeCompaction } from '../helpers/compaction.js'
5
4
  import type { AgentPlugin } from '../types.js'
6
5
  import { ADVICE_HEADING, HISTORY_HEADING } from './consts.local.js'
7
6
  import { SUMMARIZE_PLUGIN } from './consts.js'
8
7
  import type { SummarizeOptions } from './types.js'
8
+ import { compactionHelper } from '../helpers/compaction.js'
9
9
 
10
10
 
11
11
  /**
@@ -73,7 +73,7 @@ export const summarizePlugin = (options: SummarizeOptions = {}): AgentPlugin =>
73
73
  return
74
74
  }
75
75
 
76
- const compaction = await composeCompaction({
76
+ const compaction = await compactionHelper.composeCompaction({
77
77
  model: model?.(run),
78
78
  prompt: run.prompt,
79
79
  messages: result?.messages ?? [],
package/src/types.ts CHANGED
@@ -8,7 +8,7 @@ import type { AgentRunStatus, ConversationEvent, ConversationRef, PipelineSpec,
8
8
  import type { BaseCheckpointSaver } from '@langchain/langgraph'
9
9
  import type { AgentTransport } from './runtime/types.js'
10
10
  import type { ConversationStore, PipelineRunStore } from './stores/types.js'
11
- import type { PipelineModel, PipelineOptions } from './pipeline/types.js'
11
+ import type { PipelineModel, PipelineOptions } from './pipeline/runner/types.js'
12
12
 
13
13
  /** Tools an agent may call, keyed however the caller likes — resolution is by `tool.name`. */
14
14
  export interface AgentToolSet { [key: string]: StructuredToolInterface }
@@ -185,8 +185,8 @@ export interface WithAgentsService {
185
185
  agents: () => AgentService
186
186
  }
187
187
 
188
- export type AgentContext<C extends BasicConfig = BasicConfig> = BasicContext<C> & WithAgentsService
188
+ export interface AgentContext<C extends BasicConfig = BasicConfig> extends BasicContext<C>, WithAgentsService {}
189
189
 
190
- export type AgentServiceApi = Pick<
190
+ export interface AgentServiceApi extends Pick<
191
191
  AgentService, 'agent' | 'use' | 'plugins' | 'flow' | 'transport' | 'conversation' | 'pipeline'
192
- >
192
+ > {}
@@ -1,9 +1,8 @@
1
1
  import { describe, expect, test } from 'bun:test'
2
- import { isFatalError } from '@owlmeans/llm'
3
- import { InquiryDeclined, InquiryUnavailable } from '@owlmeans/llm'
2
+ import { InquiryDeclined, InquiryUnavailable, retryHelper } from '@owlmeans/llm'
4
3
  import { InquiryKind } from '@owlmeans/llm-common'
5
4
  import type { Inquiry, InquiryAnswer } from '@owlmeans/llm-common'
6
- import { ASK_USER_TOOL, inquiryPlugin, isToolError, safeInvokeTool } from '../src/index.js'
5
+ import { ASK_USER_TOOL, inquiryPlugin, toolHelper } from '../src/index.js'
7
6
  import type { AgentRun, AgentToolSet } from '../src/index.js'
8
7
 
9
8
  /**
@@ -43,7 +42,7 @@ describe('agent — the inquiry plugin', () => {
43
42
  test('an answer comes back as JSON', async () => {
44
43
  const tools = toolsOf(async inquiry => ({ inquiryId: inquiry.id, value: 'a' }))
45
44
 
46
- const result = await safeInvokeTool(tools, callOf(asking))
45
+ const result = await toolHelper.safeInvokeTool(tools, callOf(asking))
47
46
 
48
47
  expect(JSON.parse(result as string).value).toBe('a')
49
48
  })
@@ -56,7 +55,7 @@ describe('agent — the inquiry plugin', () => {
56
55
  return { inquiryId: inquiry.id, value: 'x' }
57
56
  })
58
57
 
59
- await safeInvokeTool(tools, callOf({
58
+ await toolHelper.safeInvokeTool(tools, callOf({
60
59
  question: 'Which one?', kind: InquiryKind.Choice, context: 'two candidates',
61
60
  options: [{ value: 'a', label: 'A' }, { value: 'b', label: 'B' }],
62
61
  }))
@@ -75,28 +74,28 @@ describe('agent — the inquiry plugin', () => {
75
74
  return { inquiryId: inquiry.id, value: 'a' }
76
75
  })
77
76
 
78
- const result = await safeInvokeTool(tools, callOf({
77
+ const result = await toolHelper.safeInvokeTool(tools, callOf({
79
78
  question: 'Which one?', kind: InquiryKind.Choice, options: [{ value: 'a', label: 'A' }],
80
79
  }))
81
80
 
82
- expect(isToolError(result)).toBe(true)
81
+ expect(toolHelper.isToolError(result)).toBe(true)
83
82
  expect((result as { error: string }).error).toContain('between 2 and')
84
83
  expect(reached).toBe(false)
85
84
  })
86
85
 
87
86
  test('tells the model to decide for itself when nobody is there', async () => {
88
- const result = await safeInvokeTool(toolsOf(async () => null), callOf(asking))
87
+ const result = await toolHelper.safeInvokeTool(toolsOf(async () => null), callOf(asking))
89
88
 
90
- expect(isToolError(result)).toBe(true)
89
+ expect(toolHelper.isToolError(result)).toBe(true)
91
90
  expect((result as { error: string }).error).toContain('Decide yourself')
92
91
  })
93
92
 
94
93
  test('reads a decline as an answer the model must act on, not as a failure', async () => {
95
94
  const tools = toolsOf(async () => { throw new InquiryDeclined('q1') })
96
95
 
97
- const result = await safeInvokeTool(tools, callOf(asking))
96
+ const result = await toolHelper.safeInvokeTool(tools, callOf(asking))
98
97
 
99
- expect(isToolError(result)).toBe(true)
98
+ expect(toolHelper.isToolError(result)).toBe(true)
100
99
  expect((result as { error: string }).error).toContain('declined')
101
100
  })
102
101
 
@@ -105,17 +104,17 @@ describe('agent — the inquiry plugin', () => {
105
104
  // would cost the agent all 64 turns on a channel that will never answer.
106
105
  const tools = toolsOf(async () => { throw new InquiryUnavailable('connector') })
107
106
 
108
- expect(isFatalError(new InquiryUnavailable('connector'))).not.toBeNull()
109
- await expect(safeInvokeTool(tools, callOf(asking), e => isFatalError(e) != null))
107
+ expect(retryHelper.isFatalError(new InquiryUnavailable('connector'))).not.toBeNull()
108
+ await expect(toolHelper.safeInvokeTool(tools, callOf(asking), e => retryHelper.isFatalError(e) != null))
110
109
  .rejects.toThrow(/unavailable/)
111
110
  })
112
111
 
113
112
  test('contains any other channel failure as a tool error', async () => {
114
113
  const tools = toolsOf(async () => { throw new Error('the socket died') })
115
114
 
116
- const result = await safeInvokeTool(tools, callOf(asking), e => isFatalError(e) != null)
115
+ const result = await toolHelper.safeInvokeTool(tools, callOf(asking), e => retryHelper.isFatalError(e) != null)
117
116
 
118
- expect(isToolError(result)).toBe(true)
117
+ expect(toolHelper.isToolError(result)).toBe(true)
119
118
  expect((result as { error: string }).error).toContain('the socket died')
120
119
  })
121
120
  })
@@ -3,7 +3,7 @@ import {
3
3
  INQUIRY_ANSWERS_KEY, PipelineNotResumableError, PipelineRunStatus,
4
4
  } from '@owlmeans/agent-common'
5
5
  import type { PipelineSpec } from '@owlmeans/agent-common'
6
- import { INQUIRY_STATE_TEXT_CHARS, InquiryKind, answeredWith } from '@owlmeans/llm-common'
6
+ import { INQUIRY_STATE_TEXT_CHARS, InquiryKind, inquiryHelper } from '@owlmeans/llm-common'
7
7
  import type { Inquiry, InquiryAnswer } from '@owlmeans/llm-common'
8
8
  import { createMemoryPipelineRunStore, makePipeline } from '../src/index.js'
9
9
  import type { PipelineModel, PipelineRunStore } from '../src/index.js'
@@ -61,7 +61,7 @@ const build = (
61
61
  for (const asked of questions) {
62
62
  const answer = await ctx.ask(asked)
63
63
  ctx.deps.got.push(answer)
64
- picked.push(answeredWith(answer) ?? '')
64
+ picked.push(inquiryHelper.answeredWith(answer) ?? '')
65
65
  }
66
66
 
67
67
  return { picked: picked.join('+') }
@@ -143,7 +143,7 @@ describe('agent — a pipeline that asks', () => {
143
143
 
144
144
  expect(finished.status).toBe(PipelineRunStatus.Done)
145
145
  // `q1` was answered live before the run parked; a resume carrying only `q2` must not cost it.
146
- expect(answeredWith(ran.got[0])).toBe('one')
146
+ expect(inquiryHelper.answeredWith(ran.got[0])).toBe('one')
147
147
  // What the state holds is what a resumed step reads back — there is no fuller copy anywhere.
148
148
  expect(ran.got[1].text).toHaveLength(INQUIRY_STATE_TEXT_CHARS)
149
149
  expect(answersOf(finished.state).q2.text).toHaveLength(INQUIRY_STATE_TEXT_CHARS)
@@ -5,7 +5,7 @@ import { makePromptService } from '@owlmeans/llm'
5
5
  import { AgentRunStatus } from '@owlmeans/agent-common'
6
6
  import {
7
7
  createMemoryConversationStore, createMemoryEventStore, createMemoryGraphStore, makeAgentModel,
8
- memoryEvents, memoryEventsPlugin, memoryGraph, memoryGraphPlugin, summarizePlugin,
8
+ makeMemoryEventsApi, makeMemoryGraphApi, memoryEventsPlugin, memoryGraphPlugin, summarizePlugin,
9
9
  } from '../src/index.js'
10
10
  import { scriptedModel } from './_tools/model.js'
11
11
 
@@ -125,7 +125,7 @@ describe('agent — the summarize plugin', () => {
125
125
  describe('agent — the subsystem memory graph', () => {
126
126
  test('merges into a node instead of replacing it', async () => {
127
127
  // Replacing would make every write a potential act of forgetting.
128
- const graph = memoryGraph(createMemoryGraphStore())
128
+ const graph = makeMemoryGraphApi(createMemoryGraphStore())
129
129
  await graph.write('p1', 'auth', 'uses OIDC')
130
130
  await graph.write('p1', 'auth', 'tokens live 1h')
131
131
 
@@ -135,7 +135,7 @@ describe('agent — the subsystem memory graph', () => {
135
135
  })
136
136
 
137
137
  test('compacts a node that outgrows its budget', async () => {
138
- const graph = memoryGraph(createMemoryGraphStore(), {
138
+ const graph = makeMemoryGraphApi(createMemoryGraphStore(), {
139
139
  maxNodeChars: 60,
140
140
  model: () => answering('the folded account'),
141
141
  run: {} as never,
@@ -149,7 +149,7 @@ describe('agent — the subsystem memory graph', () => {
149
149
  })
150
150
 
151
151
  test('accumulates links and never links a node to itself', async () => {
152
- const graph = memoryGraph(createMemoryGraphStore())
152
+ const graph = makeMemoryGraphApi(createMemoryGraphStore())
153
153
  await graph.write('p1', 'auth', 'x', ['db'])
154
154
  await graph.write('p1', 'auth', 'y', ['api', 'auth'])
155
155
 
@@ -158,7 +158,7 @@ describe('agent — the subsystem memory graph', () => {
158
158
  })
159
159
 
160
160
  test('follows links to the requested depth and no further', async () => {
161
- const graph = memoryGraph(createMemoryGraphStore())
161
+ const graph = makeMemoryGraphApi(createMemoryGraphStore())
162
162
  await graph.write('p1', 'auth', 'a', ['db'])
163
163
  await graph.write('p1', 'db', 'b', ['storage'])
164
164
  await graph.write('p1', 'storage', 'c')
@@ -170,7 +170,7 @@ describe('agent — the subsystem memory graph', () => {
170
170
  })
171
171
 
172
172
  test('survives a cycle in the graph', async () => {
173
- const graph = memoryGraph(createMemoryGraphStore())
173
+ const graph = makeMemoryGraphApi(createMemoryGraphStore())
174
174
  await graph.write('p1', 'a', 'x', ['b'])
175
175
  await graph.write('p1', 'b', 'y', ['a'])
176
176
 
@@ -181,7 +181,7 @@ describe('agent — the subsystem memory graph', () => {
181
181
  // Bulk-injecting notes spends the context window on knowledge the run cannot tell apart from
182
182
  // what it needs; the agent pulls what it wants by name.
183
183
  const store = createMemoryGraphStore()
184
- await memoryGraph(store).write('p1', 'auth', 'a long private note', ['db'])
184
+ await makeMemoryGraphApi(store).write('p1', 'auth', 'a long private note', ['db'])
185
185
 
186
186
  const scripted = scriptedModel([{ content: 'ok' }])
187
187
  await makeAgentModel({
@@ -212,7 +212,7 @@ describe('agent — the subsystem memory graph', () => {
212
212
 
213
213
  describe('agent — the event sequence memory', () => {
214
214
  test('keeps only the most recent entries', async () => {
215
- const events = memoryEvents(createMemoryEventStore(), { limit: 2 })
215
+ const events = makeMemoryEventsApi(createMemoryEventStore(), { limit: 2 })
216
216
  for (const content of ['a', 'b', 'c']) {
217
217
  await events.append('p1', 'note', content)
218
218
  }
@@ -221,7 +221,7 @@ describe('agent — the event sequence memory', () => {
221
221
  })
222
222
 
223
223
  test('caps a single entry — an event is a line, not a document', async () => {
224
- const events = memoryEvents(createMemoryEventStore(), { maxEventChars: 20 })
224
+ const events = makeMemoryEventsApi(createMemoryEventStore(), { maxEventChars: 20 })
225
225
  await events.append('p1', 'note', 'x'.repeat(500))
226
226
 
227
227
  expect((await events.read('p1', 1))[0].content.length).toBeLessThanOrEqual(20)
@@ -229,7 +229,7 @@ describe('agent — the event sequence memory', () => {
229
229
 
230
230
  test('injects the recent window into a run', async () => {
231
231
  const store = createMemoryEventStore()
232
- await memoryEvents(store).append('p1', 'deploy', 'shipped version 3')
232
+ await makeMemoryEventsApi(store).append('p1', 'deploy', 'shipped version 3')
233
233
 
234
234
  const scripted = scriptedModel([{ content: 'ok' }])
235
235
  await makeAgentModel({
@@ -2,7 +2,7 @@ import { describe, expect, test } from 'bun:test'
2
2
  import { AIMessage, HumanMessage, ToolMessage } from '@langchain/core/messages'
3
3
  import type { LlmModel } from '@owlmeans/llm'
4
4
  import { AgentRunStatus } from '@owlmeans/agent-common'
5
- import { composeCompaction, composeRollingSummary, renderTranscript } from '../src/index.js'
5
+ import { composeRollingSummary, compactionHelper } from '../src/index.js'
6
6
 
7
7
  /** A model double: the helpers only ever call `invoke` (structured) or `ask` (text). */
8
8
  const answering = (answer: unknown): LlmModel => ({
@@ -24,7 +24,7 @@ const conversation = [
24
24
 
25
25
  describe('agent — conversation compaction', () => {
26
26
  test('returns both parts, each inside its own cap', async () => {
27
- const result = await composeCompaction({
27
+ const result = await compactionHelper.composeCompaction({
28
28
  model: answering({ summary: 'S'.repeat(5_000), advice: 'A'.repeat(5_000) }),
29
29
  prompt: 'rename the header',
30
30
  messages: conversation,
@@ -41,7 +41,7 @@ describe('agent — conversation compaction', () => {
41
41
  test('falls back to a usable event when the model fails', async () => {
42
42
  // An exhausted budget is the common case, and asking again would fail the same way — so the
43
43
  // fallback has to carry the facts the caller already holds rather than an apology.
44
- const result = await composeCompaction({
44
+ const result = await compactionHelper.composeCompaction({
45
45
  model: failing(),
46
46
  prompt: 'rename the header',
47
47
  messages: conversation,
@@ -55,7 +55,7 @@ describe('agent — conversation compaction', () => {
55
55
  })
56
56
 
57
57
  test('treats an empty summary as a non-answer, not a short one', async () => {
58
- const result = await composeCompaction({
58
+ const result = await compactionHelper.composeCompaction({
59
59
  model: answering({ summary: ' ', advice: 'do something' }),
60
60
  prompt: 'rename the header',
61
61
  messages: conversation,
@@ -66,7 +66,7 @@ describe('agent — conversation compaction', () => {
66
66
  })
67
67
 
68
68
  test('works with no model at all', async () => {
69
- const result = await composeCompaction({
69
+ const result = await compactionHelper.composeCompaction({
70
70
  prompt: 'rename the header', messages: conversation, status: AgentRunStatus.Ok,
71
71
  })
72
72
 
@@ -74,7 +74,7 @@ describe('agent — conversation compaction', () => {
74
74
  })
75
75
 
76
76
  test('drops an empty advice rather than storing a blank field', async () => {
77
- const result = await composeCompaction({
77
+ const result = await compactionHelper.composeCompaction({
78
78
  model: answering({ summary: 'did the thing', advice: '' }),
79
79
  prompt: 'p', messages: conversation, status: AgentRunStatus.Ok,
80
80
  })
@@ -85,7 +85,7 @@ describe('agent — conversation compaction', () => {
85
85
 
86
86
  describe('agent — transcript rendering', () => {
87
87
  test('names the tools a message called when it carried no text', async () => {
88
- expect(renderTranscript(conversation)).toContain('called write_file')
88
+ expect(compactionHelper.renderTranscript(conversation)).toContain('called write_file')
89
89
  })
90
90
 
91
91
  test('keeps the tail when the budget binds', () => {
@@ -95,7 +95,7 @@ describe('agent — transcript rendering', () => {
95
95
  new AIMessage({ content: 'the final answer' }),
96
96
  ]
97
97
 
98
- const rendered = renderTranscript(long, 100)
98
+ const rendered = compactionHelper.renderTranscript(long, 100)
99
99
 
100
100
  expect(rendered).toContain('the final answer')
101
101
  expect(rendered).not.toContain('AAAA')
@@ -1,8 +1,8 @@
1
1
  import { describe, expect, test } from 'bun:test'
2
2
  import { tool } from '@langchain/core/tools'
3
3
  import * as z from 'zod'
4
- import { isToolError, safeInvokeTool } from '../src/index.js'
5
4
  import type { AgentToolSet } from '../src/index.js'
5
+ import { toolHelper } from '../src/index.js'
6
6
 
7
7
  /**
8
8
  * No model involved: these pin the containment contract the tool loop depends on, which is what
@@ -33,21 +33,21 @@ const callOf = (name: string, args: Record<string, unknown>) =>
33
33
 
34
34
  describe('agent — tool invocation is contained', () => {
35
35
  test('passes a valid call through to the tool', async () => {
36
- expect(await safeInvokeTool(tools, callOf('get_structured_list', { type: 'ui-layout' })))
36
+ expect(await toolHelper.safeInvokeTool(tools, callOf('get_structured_list', { type: 'ui-layout' })))
37
37
  .toBe('listed:ui-layout')
38
38
  })
39
39
 
40
40
  test('an out-of-enum argument resolves to an error naming the valid options', async () => {
41
- const result = await safeInvokeTool(tools, callOf('get_structured_list', { type: 'ui-navigation' }))
41
+ const result = await toolHelper.safeInvokeTool(tools, callOf('get_structured_list', { type: 'ui-navigation' }))
42
42
 
43
- expect(isToolError(result)).toBe(true)
43
+ expect(toolHelper.isToolError(result)).toBe(true)
44
44
  expect((result as { error: string }).error).toContain('ui-screens')
45
45
  })
46
46
 
47
47
  test('a throwing tool resolves to an error instead of rejecting', async () => {
48
- const result = await safeInvokeTool(tools, callOf('explodes', {}))
48
+ const result = await toolHelper.safeInvokeTool(tools, callOf('explodes', {}))
49
49
 
50
- expect(isToolError(result)).toBe(true)
50
+ expect(toolHelper.isToolError(result)).toBe(true)
51
51
  expect((result as { error: string }).error).toContain('the tool itself failed')
52
52
  })
53
53
 
@@ -56,12 +56,12 @@ describe('agent — tool invocation is contained', () => {
56
56
  // variable would otherwise lose the tool permanently.
57
57
  const mismatched = { some_local_name: tools.get_structured_list } as unknown as AgentToolSet
58
58
 
59
- expect(await safeInvokeTool(mismatched, callOf('get_structured_list', { type: 'ui-layout' })))
59
+ expect(await toolHelper.safeInvokeTool(mismatched, callOf('get_structured_list', { type: 'ui-layout' })))
60
60
  .toBe('listed:ui-layout')
61
61
  })
62
62
 
63
63
  test('an unknown tool name resolves to an error instead of throwing', async () => {
64
- const result = await safeInvokeTool(tools, callOf('hallucinated_tool', {}))
64
+ const result = await toolHelper.safeInvokeTool(tools, callOf('hallucinated_tool', {}))
65
65
 
66
66
  expect((result as { error: string }).error).toContain('not found')
67
67
  })
@@ -83,9 +83,9 @@ describe('agent — tool invocation is contained', () => {
83
83
  } as unknown as AgentToolSet
84
84
  const fatal = (e: unknown): boolean => e instanceof OutOfTokens
85
85
 
86
- expect(safeInvokeTool(exploding, callOf('broke', {}), fatal)).rejects.toThrow('no balance')
86
+ expect(toolHelper.safeInvokeTool(exploding, callOf('broke', {}), fatal)).rejects.toThrow('no balance')
87
87
 
88
- const contained = await safeInvokeTool(exploding, callOf('ordinary', {}), fatal)
89
- expect(isToolError(contained)).toBe(true)
88
+ const contained = await toolHelper.safeInvokeTool(exploding, callOf('ordinary', {}), fatal)
89
+ expect(toolHelper.isToolError(contained)).toBe(true)
90
90
  })
91
91
  })
@@ -1,42 +0,0 @@
1
- import type { LlmModel } from '@owlmeans/llm';
2
- import type { BaseMessage } from '@langchain/core/messages';
3
- import { AgentRunStatus } from '@owlmeans/agent-common';
4
- export interface Compaction {
5
- summary: string;
6
- advice?: string;
7
- }
8
- export interface CompactionInput {
9
- /** Omit to skip the model entirely and take the deterministic path. */
10
- model?: LlmModel;
11
- /** The ask that opened the run. */
12
- prompt: string;
13
- messages: readonly BaseMessage[];
14
- status: AgentRunStatus;
15
- /** What happened after the loop — a validation verdict, a build result. */
16
- note?: string;
17
- maxSummaryChars?: number;
18
- maxAdviceChars?: number;
19
- /** LangChain `runName`. Give it a value the application filters, or the summary of a run streams into the user's view of that run. */
20
- action?: string;
21
- /** How much of the transcript to show the model. */
22
- maxTranscriptChars?: number;
23
- }
24
- export interface RollingSummaryInput {
25
- /** Omit to skip the model and take the deterministic path. */
26
- model?: LlmModel;
27
- /** The prose account so far. Empty on the first fold. */
28
- previous: string;
29
- /** What just happened, as one line. */
30
- event: string;
31
- /** Anything the fold may use but that need not survive into the summary. */
32
- details?: string;
33
- /** Hard ceiling on the returned prose, in characters. */
34
- maxChars: number;
35
- /** LangChain `runName`. Give it a value the application filters out of its user-facing stream. */
36
- action?: string;
37
- }
38
- /** The shape a contained tool failure comes back as. Matches what tool bodies return themselves. */
39
- export interface ToolErrorResponse {
40
- error: string;
41
- }
42
- //# sourceMappingURL=types.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/helpers/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAC7C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AAEvD,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,eAAe;IAC9B,uEAAuE;IACvE,KAAK,CAAC,EAAE,QAAQ,CAAA;IAChB,mCAAmC;IACnC,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,SAAS,WAAW,EAAE,CAAA;IAChC,MAAM,EAAE,cAAc,CAAA;IACtB,2EAA2E;IAC3E,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,sIAAsI;IACtI,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,oDAAoD;IACpD,kBAAkB,CAAC,EAAE,MAAM,CAAA;CAC5B;AAED,MAAM,WAAW,mBAAmB;IAClC,8DAA8D;IAC9D,KAAK,CAAC,EAAE,QAAQ,CAAA;IAChB,yDAAyD;IACzD,QAAQ,EAAE,MAAM,CAAA;IAChB,uCAAuC;IACvC,KAAK,EAAE,MAAM,CAAA;IACb,4EAA4E;IAC5E,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,yDAAyD;IACzD,QAAQ,EAAE,MAAM,CAAA;IAChB,kGAAkG;IAClG,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,oGAAoG;AACpG,MAAM,WAAW,iBAAiB;IAAG,KAAK,EAAE,MAAM,CAAA;CAAE"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"types.local.d.ts","sourceRoot":"","sources":["../../src/pipeline/types.local.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,qBAAqB,EAAE,KAAK,oBAAoB,EAAE,KAAK,qBAAqB,EAAE,KAAK,YAAY,EAAE,KAAK,eAAe,EAAE,sBAAsB,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AAC3M,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AACjE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAA;AAE/D,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IAClB,KAAK,EAAE,oBAAoB,EAAE,CAAA;CAC9B;AAED,MAAM,WAAW,IAAI;IACnB,KAAK,EAAE,qBAAqB,CAAA;IAC5B,IAAI,EAAE,cAAc,CAAA;IACpB,kFAAkF;IAClF,QAAQ,EAAE,OAAO,CAAA;IACjB,OAAO,EAAE,OAAO,CAAA;CACjB;AAED,MAAM,WAAW,SAAS,CAAC,CAAC;IAC1B,OAAO,EAAE,qBAAqB,CAAA;IAC9B,IAAI,EAAE,YAAY,CAAA;IAClB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,EAAE,CAAA;IACf,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC1B,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAA;IAC3C,QAAQ,EAAE,SAAS,kBAAkB,EAAE,CAAA;IACvC,KAAK,EAAE,qBAAqB,EAAE,CAAA;IAC9B,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAA;IAC/B,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;IAC/B,IAAI,EAAE,CAAC,CAAA;CACR;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,oBAAoB,EAAE,CAAA;IAC7B,KAAK,EAAE,MAAM,EAAE,CAAA;IACf,MAAM,EAAE,sBAAsB,CAAA;IAC9B,KAAK,EAAE,MAAM,EAAE,CAAA;IACf,IAAI,CAAC,EAAE,eAAe,CAAA;IACtB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,CACP,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACxD,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC9B,UAAU,CAAA;IACf,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,KAAK,UAAU,CAAA;IAC9D,OAAO,EAAE,CAAC,OAAO,CAAC,EAAE;QAAE,YAAY,CAAC,EAAE,mBAAmB,CAAA;KAAE,KAAK;QAC7D,MAAM,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;KAC/E,CAAA;CACF"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"types.local.js","sourceRoot":"","sources":["../../src/pipeline/types.local.ts"],"names":[],"mappings":""}
@@ -1,43 +0,0 @@
1
- import type { LlmModel } from '@owlmeans/llm'
2
- import type { BaseMessage } from '@langchain/core/messages'
3
- import { AgentRunStatus } from '@owlmeans/agent-common'
4
-
5
- export interface Compaction {
6
- summary: string
7
- advice?: string
8
- }
9
-
10
- export interface CompactionInput {
11
- /** Omit to skip the model entirely and take the deterministic path. */
12
- model?: LlmModel
13
- /** The ask that opened the run. */
14
- prompt: string
15
- messages: readonly BaseMessage[]
16
- status: AgentRunStatus
17
- /** What happened after the loop — a validation verdict, a build result. */
18
- note?: string
19
- maxSummaryChars?: number
20
- maxAdviceChars?: number
21
- /** LangChain `runName`. Give it a value the application filters, or the summary of a run streams into the user's view of that run. */
22
- action?: string
23
- /** How much of the transcript to show the model. */
24
- maxTranscriptChars?: number
25
- }
26
-
27
- export interface RollingSummaryInput {
28
- /** Omit to skip the model and take the deterministic path. */
29
- model?: LlmModel
30
- /** The prose account so far. Empty on the first fold. */
31
- previous: string
32
- /** What just happened, as one line. */
33
- event: string
34
- /** Anything the fold may use but that need not survive into the summary. */
35
- details?: string
36
- /** Hard ceiling on the returned prose, in characters. */
37
- maxChars: number
38
- /** LangChain `runName`. Give it a value the application filters out of its user-facing stream. */
39
- action?: string
40
- }
41
-
42
- /** The shape a contained tool failure comes back as. Matches what tool bodies return themselves. */
43
- export interface ToolErrorResponse { error: string }
File without changes