@owlmeans/agent-common 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 (62) hide show
  1. package/README.md +174 -13
  2. package/agent-meta/manifest.json +2 -2
  3. package/agent-meta/skills/agent-common/SKILL.md +1 -1
  4. package/build/consts.d.ts +20 -0
  5. package/build/consts.d.ts.map +1 -1
  6. package/build/consts.js +103 -0
  7. package/build/consts.js.map +1 -1
  8. package/build/errors.d.ts +1 -1
  9. package/build/errors.js +1 -1
  10. package/build/helpers/conversation.d.ts +0 -9
  11. package/build/helpers/conversation.d.ts.map +1 -1
  12. package/build/helpers/conversation.js +0 -28
  13. package/build/helpers/conversation.js.map +1 -1
  14. package/build/helpers/truncate.d.ts +10 -0
  15. package/build/helpers/truncate.d.ts.map +1 -0
  16. package/build/helpers/truncate.js +29 -0
  17. package/build/helpers/truncate.js.map +1 -0
  18. package/build/index.d.ts +4 -1
  19. package/build/index.d.ts.map +1 -1
  20. package/build/index.js +2 -1
  21. package/build/index.js.map +1 -1
  22. package/build/models/pipeline-spec/types.d.ts +57 -0
  23. package/build/models/pipeline-spec/types.d.ts.map +1 -0
  24. package/build/models/pipeline-spec/types.js +2 -0
  25. package/build/models/pipeline-spec/types.js.map +1 -0
  26. package/build/models/pipeline-spec.d.ts +4 -0
  27. package/build/models/pipeline-spec.d.ts.map +1 -0
  28. package/build/models/pipeline-spec.js +171 -0
  29. package/build/models/pipeline-spec.js.map +1 -0
  30. package/build/pipeline.d.ts +4 -43
  31. package/build/pipeline.d.ts.map +1 -1
  32. package/build/pipeline.js +9 -205
  33. package/build/pipeline.js.map +1 -1
  34. package/build/results/types.d.ts +211 -0
  35. package/build/results/types.d.ts.map +1 -0
  36. package/build/results/types.js +2 -0
  37. package/build/results/types.js.map +1 -0
  38. package/build/results.d.ts +3 -55
  39. package/build/results.d.ts.map +1 -1
  40. package/build/results.js +202 -250
  41. package/build/results.js.map +1 -1
  42. package/build/types.d.ts +1 -137
  43. package/build/types.d.ts.map +1 -1
  44. package/package.json +5 -5
  45. package/src/consts.ts +113 -0
  46. package/src/errors.ts +1 -1
  47. package/src/helpers/conversation.ts +0 -32
  48. package/src/helpers/truncate.ts +31 -0
  49. package/src/index.ts +4 -1
  50. package/src/models/pipeline-spec/types.ts +58 -0
  51. package/src/models/pipeline-spec.ts +202 -0
  52. package/src/pipeline.ts +10 -231
  53. package/src/results/types.ts +221 -0
  54. package/src/results.ts +229 -274
  55. package/src/types.ts +2 -147
  56. package/tests/pipeline.spec.ts +26 -29
  57. package/tests/results.spec.ts +24 -26
  58. package/build/flows.d.ts +0 -21
  59. package/build/flows.d.ts.map +0 -1
  60. package/build/flows.js +0 -105
  61. package/build/flows.js.map +0 -1
  62. package/src/flows.ts +0 -113
package/src/types.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { ResourceRecord } from '@owlmeans/resource'
2
- import { type AgentRunStatus, RESULTS_EVERY_STEP, CumulativeResultSource, PipelineRunStatus } from './consts.js'
2
+ import { type AgentRunStatus, PipelineRunStatus } from './consts.js'
3
3
  import type { Inquiry } from '@owlmeans/llm-common'
4
4
 
5
5
  /**
@@ -98,6 +98,7 @@ export interface AgentRunMessage {
98
98
  * the state is serialized into the run row at every step boundary, and a state that carries a
99
99
  * document is a row that carries it too, once per step.
100
100
  */
101
+ // Kept as a type: every state shape must satisfy it without declaring an index signature.
101
102
  export type PipelineState = Record<string, unknown>
102
103
 
103
104
  export interface PipelineStepSpec {
@@ -229,149 +230,3 @@ export interface PipelineProgress {
229
230
  skipped?: boolean
230
231
  note?: string
231
232
  }
232
-
233
- /**
234
- * ONE authoritative fact about what a step produced, small enough to be one line of a prompt.
235
- *
236
- * Every field is optional past `kind` and `name`, because facts differ in what matters about them:
237
- * a type has a shape and an import specifier, an endpoint an alias, a method, a path and a guard, a
238
- * resource a table and an accessor, a file only its path. Put scalars that have no field of their
239
- * own into `attrs`.
240
- */
241
- export interface CumulativeResultFact {
242
- /** What the fact is about — see {@link CumulativeFactKind}. */
243
- kind: string
244
- /** The name to use VERBATIM. */
245
- name: string
246
- /** The file it lives in, relative to the project root. */
247
- path?: string
248
- /** The module specifier it is imported from. */
249
- specifier?: string
250
- /** Its shape or signature, on one line. */
251
- signature?: string
252
- /** Fields, parameters or columns, when there is no signature to say it. */
253
- members?: string[]
254
- /** Another fact's name this one points at — a returned type, a mapped table. */
255
- ref?: string
256
- /** Scalars without a field of their own — `method`, `path`, `guard`, `table`. */
257
- attrs?: Record<string, string>
258
- }
259
-
260
- /**
261
- * One step's contribution to a ledger.
262
- *
263
- * Keyed by `(ledger, runId, step)`. `order` places it in the ledger: the topological index of the
264
- * step in its own pipeline, preceded by the index path of every step it was composed under, so a
265
- * run composed as a step of another sorts inside that step, ahead of the composing step's own
266
- * entry. A single number cannot say that: two pipelines' indexes are not comparable.
267
- *
268
- * `full` and `compact` are rendered once, when the entry is written, and never contain a timestamp,
269
- * a run id or anything else a model would copy without meaning to.
270
- */
271
- export interface CumulativeResultEntry extends ResourceRecord {
272
- /** What entries can see each other — by default the root run of the composition. */
273
- ledger: string
274
- pipeline: string
275
- runId: string
276
- step: string
277
- /** How the entry is headed in a prompt: the step, behind the path of the steps it runs under. */
278
- label: string
279
- order: number[]
280
- /** Bumped every time the entry is re-extracted after a later step rewrote one of its files. */
281
- revision: number
282
- source: CumulativeResultSource
283
- /** An `optional` step that failed: whatever it left behind, and a heading that says so. */
284
- partial?: boolean
285
- facts: CumulativeResultFact[]
286
- /** The files the facts were read from — what a later rewrite is matched against. */
287
- files: string[]
288
- /** A model's summary, rendered and capped. NOT verified, and always labelled so. */
289
- summary?: string
290
- full: string
291
- compact: string
292
- /** Consumers that always get the full entry, by step name, or every later step. */
293
- fullFor?: string[] | typeof RESULTS_EVERY_STEP
294
- /** Consumers that never see the entry in their prompt. */
295
- omitFor?: string[]
296
- /** How many edges back the entry is still rendered in full by default. */
297
- window?: number
298
- updatedAt: string
299
- }
300
-
301
- /**
302
- * The optional model-written summary of a step. OFF unless a step declares one.
303
- *
304
- * Facts are the product; a summary is for what code cannot extract — an intent, a decision taken.
305
- * It is bounded by `maxChars` in code after the model answers, rendered deterministically from the
306
- * schema-shaped answer, and always labelled as unverified wherever it is shown.
307
- */
308
- export interface ResultSummarySpec {
309
- /** Which model — interpreted by the caller's summarize callback (a model role, typically). */
310
- role?: string
311
- instructions: string
312
- /** JSON schema of the answer. Absent means `{ summary: string }`. */
313
- schema?: Record<string, unknown>
314
- /** Defaults to `DEFAULT_RESULT_SUMMARY_CHARS`. */
315
- maxChars?: number
316
- }
317
-
318
- /**
319
- * What one step contributes, and to whom. JSON-safe, so a declaration can live in data.
320
- *
321
- * Extractors are named, never inlined: a declaration names what runs, and the runtime holds the
322
- * code. A consumer not named in `full` or `omit` gets the full entry within `window` edges and the
323
- * names only beyond it.
324
- */
325
- export interface StepResultsSpec {
326
- /** Named deterministic extractors, run in this order. */
327
- extractors?: string[]
328
- /** Later steps that get the FULL entry wherever they sit, or `'*'` for all of them. */
329
- full?: string[] | typeof RESULTS_EVERY_STEP
330
- /** Later steps that are never shown the entry. */
331
- omit?: string[]
332
- /** Overrides {@link CumulativeResultsSpec.window} for this entry. */
333
- window?: number
334
- /** Cap on the full rendering; facts are dropped whole, never cut. */
335
- maxChars?: number
336
- /** Cap on the names-only rendering. */
337
- maxCompactChars?: number
338
- /** Re-extract this entry when a later step rewrites one of its files. Defaults to the spec's. */
339
- refresh?: boolean
340
- summary?: ResultSummarySpec
341
- }
342
-
343
- /** The pipeline-level declaration — merged from a pipeline's own and any overlay with {@link mergeResultsSpecs}. */
344
- export interface CumulativeResultsSpec {
345
- /** Per step, by step name. A step with no entry contributes only what it records itself. */
346
- steps?: Record<string, StepResultsSpec>
347
- /** Default {@link DEFAULT_RESULTS_WINDOW}. */
348
- window?: number
349
- /** Total characters of one view. Default `DEFAULT_RESULTS_MAX_CHARS`. */
350
- maxChars?: number
351
- /** Default cap of one full entry. Default {@link DEFAULT_RESULT_ENTRY_CHARS}. */
352
- maxEntryChars?: number
353
- /** Default cap of one names-only entry. Default {@link DEFAULT_RESULT_COMPACT_CHARS}. */
354
- maxCompactChars?: number
355
- /** Default for {@link StepResultsSpec.refresh}. Defaults to `true`. */
356
- refresh?: boolean
357
- }
358
-
359
- /** What a facts query may narrow by. Every field is an exact match. */
360
- export interface ResultFactQuery {
361
- kind?: string
362
- name?: string
363
- /** A producing step, by its name or by its label. */
364
- step?: string
365
- }
366
-
367
- /** What {@link renderResultEntry} needs of an entry. */
368
- export type RenderableResultEntry = Pick<
369
- CumulativeResultEntry, 'label' | 'source' | 'partial' | 'facts' | 'summary'
370
- >
371
-
372
- export interface ResultRenderCaps {
373
- /** Cap on the full rendering. Default {@link DEFAULT_RESULT_ENTRY_CHARS}. */
374
- maxChars?: number
375
- /** Cap on the names-only rendering. Default {@link DEFAULT_RESULT_COMPACT_CHARS}. */
376
- maxCompactChars?: number
377
- }
@@ -1,9 +1,6 @@
1
1
  import { describe, expect, test } from 'bun:test'
2
2
  import { InquiryKind } from '@owlmeans/llm-common'
3
- import {
4
- INQUIRY_ANSWERS_KEY, PipelineRunStatus,
5
- orderPipelineSteps, pipelineDescendants, pipelineStep, validatePipelineSpec,
6
- } from '../src/index.js'
3
+ import { INQUIRY_ANSWERS_KEY, PipelineRunStatus, makePipelineSpecModel } from '../src/index.js'
7
4
  import type { PipelineRun, PipelineSpec } from '../src/index.js'
8
5
 
9
6
  /**
@@ -18,25 +15,25 @@ const spec = (steps: PipelineSpec['steps'], version = 1): PipelineSpec =>
18
15
 
19
16
  describe('agent-common — pipeline spec validation', () => {
20
17
  test('accepts a linear spec', () => {
21
- expect(() => validatePipelineSpec(spec([
18
+ expect(() => makePipelineSpecModel(spec([
22
19
  { step: 'a' }, { step: 'b', after: ['a'] }, { step: 'c', after: ['b'] },
23
- ]))).not.toThrow()
20
+ ])).validate()).not.toThrow()
24
21
  })
25
22
 
26
23
  test('refuses an empty step list, an empty alias and a bad version at once', () => {
27
- expect(() => validatePipelineSpec({ alias: '', version: 0, steps: [] }))
24
+ expect(() => makePipelineSpecModel({ alias: '', version: 0, steps: [] }).validate())
28
25
  .toThrow(/alias is empty.*version.*no steps/s)
29
26
  })
30
27
 
31
28
  test('names EVERY fault in one message rather than the first', () => {
32
29
  let message = ''
33
30
  try {
34
- validatePipelineSpec(spec([
31
+ makePipelineSpecModel(spec([
35
32
  { step: 'a' },
36
33
  { step: 'a' },
37
34
  { step: 'b', after: ['nope'] },
38
35
  { step: 'c', nonIdempotent: true, attempts: 3 },
39
- ]))
36
+ ])).validate()
40
37
  } catch (e) {
41
38
  message = (e as Error).message
42
39
  }
@@ -46,41 +43,41 @@ describe('agent-common — pipeline spec validation', () => {
46
43
  })
47
44
 
48
45
  test('refuses a step that waits on itself', () => {
49
- expect(() => validatePipelineSpec(spec([{ step: 'a', after: ['a'] }])))
46
+ expect(() => makePipelineSpecModel(spec([{ step: 'a', after: ['a'] }])).validate())
50
47
  .toThrow(/declares itself/)
51
48
  })
52
49
 
53
50
  test('refuses a cycle', () => {
54
- expect(() => validatePipelineSpec(spec([
51
+ expect(() => makePipelineSpecModel(spec([
55
52
  { step: 'a', after: ['c'] }, { step: 'b', after: ['a'] }, { step: 'c', after: ['b'] },
56
- ]))).toThrow(/cycle/)
53
+ ])).validate()).toThrow(/cycle/)
57
54
  })
58
55
 
59
56
  test('refuses attempts below one and a non-positive timeout', () => {
60
- expect(() => validatePipelineSpec(spec([{ step: 'a', attempts: 0 }]))).toThrow(/attempts/)
61
- expect(() => validatePipelineSpec(spec([{ step: 'a', timeout: 0 }]))).toThrow(/timeout/)
57
+ expect(() => makePipelineSpecModel(spec([{ step: 'a', attempts: 0 }])).validate()).toThrow(/attempts/)
58
+ expect(() => makePipelineSpecModel(spec([{ step: 'a', timeout: 0 }])).validate()).toThrow(/timeout/)
62
59
  })
63
60
 
64
61
  test('allows attempts above one on an idempotent step', () => {
65
- expect(() => validatePipelineSpec(spec([{ step: 'a', attempts: 3 }]))).not.toThrow()
62
+ expect(() => makePipelineSpecModel(spec([{ step: 'a', attempts: 3 }])).validate()).not.toThrow()
66
63
  })
67
64
  })
68
65
 
69
66
  describe('agent-common — pipeline ordering', () => {
70
67
  test('respects `after` and keeps declaration order as the tie-break', () => {
71
68
  // `c` and `b` are both ready after `a`; declaration order decides, not a set's iteration.
72
- expect(orderPipelineSteps(spec([
69
+ expect(makePipelineSpecModel(spec([
73
70
  { step: 'a' }, { step: 'c', after: ['a'] }, { step: 'b', after: ['a'] },
74
- ]))).toEqual(['a', 'c', 'b'])
71
+ ])).orderSteps()).toEqual(['a', 'c', 'b'])
75
72
  })
76
73
 
77
74
  test('orders a fan-out/join graph so the join comes last', () => {
78
- const order = orderPipelineSteps(spec([
75
+ const order = makePipelineSpecModel(spec([
79
76
  { step: 'left' },
80
77
  { step: 'right' },
81
78
  { step: 'deeper', after: ['right'] },
82
79
  { step: 'join', after: ['left', 'deeper'] },
83
- ]))
80
+ ])).orderSteps()
84
81
  expect(order.indexOf('join')).toBe(3)
85
82
  expect(order.indexOf('deeper')).toBeGreaterThan(order.indexOf('right'))
86
83
  })
@@ -89,14 +86,14 @@ describe('agent-common — pipeline ordering', () => {
89
86
  const declared = spec([
90
87
  { step: 'a' }, { step: 'b' }, { step: 'c', after: ['a', 'b'] }, { step: 'd', after: ['a'] },
91
88
  ])
92
- expect(orderPipelineSteps(declared)).toEqual(orderPipelineSteps(declared))
89
+ expect(makePipelineSpecModel(declared).orderSteps()).toEqual(makePipelineSpecModel(declared).orderSteps())
93
90
  })
94
91
 
95
92
  test('throws on an unknown `after` and on a cycle', () => {
96
- expect(() => orderPipelineSteps(spec([{ step: 'a', after: ['ghost'] }]))).toThrow(/unknown step/)
97
- expect(() => orderPipelineSteps(spec([
93
+ expect(() => makePipelineSpecModel(spec([{ step: 'a', after: ['ghost'] }])).orderSteps()).toThrow(/unknown step/)
94
+ expect(() => makePipelineSpecModel(spec([
98
95
  { step: 'a', after: ['b'] }, { step: 'b', after: ['a'] },
99
- ]))).toThrow(/cycle/)
96
+ ])).orderSteps()).toThrow(/cycle/)
100
97
  })
101
98
  })
102
99
 
@@ -110,21 +107,21 @@ describe('agent-common — descendants', () => {
110
107
  ])
111
108
 
112
109
  test('a step and everything that transitively waits on it, in order', () => {
113
- expect(pipelineDescendants(graph, 'b')).toEqual(['b', 'd'])
114
- expect(pipelineDescendants(graph, 'a')).toEqual(['a', 'b', 'c', 'd'])
110
+ expect(makePipelineSpecModel(graph).descendants('b')).toEqual(['b', 'd'])
111
+ expect(makePipelineSpecModel(graph).descendants('a')).toEqual(['a', 'b', 'c', 'd'])
115
112
  })
116
113
 
117
114
  test('leaves an unrelated branch alone', () => {
118
- expect(pipelineDescendants(graph, 'b')).not.toContain('aside')
115
+ expect(makePipelineSpecModel(graph).descendants('b')).not.toContain('aside')
119
116
  })
120
117
 
121
118
  test('refuses a step the spec does not declare', () => {
122
- expect(() => pipelineDescendants(graph, 'ghost')).toThrow(/pipeline-step/)
119
+ expect(() => makePipelineSpecModel(graph).descendants('ghost')).toThrow(/pipeline-step/)
123
120
  })
124
121
 
125
122
  test('pipelineStep answers null rather than throwing', () => {
126
- expect(pipelineStep(graph, 'ghost')).toBeNull()
127
- expect(pipelineStep(graph, 'a')?.step).toBe('a')
123
+ expect(makePipelineSpecModel(graph).stepOf('ghost')).toBeNull()
124
+ expect(makePipelineSpecModel(graph).stepOf('a')?.step).toBe('a')
128
125
  })
129
126
  })
130
127
 
@@ -1,8 +1,6 @@
1
1
  import { describe, expect, test } from 'bun:test'
2
2
  import {
3
- CumulativeFactKind, CumulativeResultSource, RESULTS_EVERY_STEP,
4
- compareResultOrder, factKey, mergeResultsSpecs, pipelineAncestors, pipelineAncestry,
5
- pipelineDescendants, renderResultEntry, renderResultSummary, resultLabel,
3
+ CumulativeFactKind, CumulativeResultSource, RESULTS_EVERY_STEP, cumulativeResultsHelper, makePipelineSpecModel,
6
4
  } from '../src/index.js'
7
5
  import type { CumulativeResultFact, PipelineSpec, RenderableResultEntry } from '../src/index.js'
8
6
 
@@ -41,14 +39,14 @@ const entry = (patch: Partial<RenderableResultEntry> = {}): RenderableResultEntr
41
39
 
42
40
  describe('agent-common — pipeline ancestry', () => {
43
41
  test('names every transitive predecessor in topological order, never the step itself', () => {
44
- expect(pipelineAncestors(diamond, 'join')).toEqual(['root', 'left', 'right', 'deeper'])
45
- expect(pipelineAncestors(diamond, 'root')).toEqual([])
42
+ expect(makePipelineSpecModel(diamond).ancestors('join')).toEqual(['root', 'left', 'right', 'deeper'])
43
+ expect(makePipelineSpecModel(diamond).ancestors('root')).toEqual([])
46
44
  // The mirror includes the named step; ancestry deliberately does not.
47
- expect(pipelineDescendants(diamond, 'root')).toContain('root')
45
+ expect(makePipelineSpecModel(diamond).descendants('root')).toContain('root')
48
46
  })
49
47
 
50
48
  test('carries the SHORTEST distance, so a direct predecessor is depth 1', () => {
51
- expect(pipelineAncestry(diamond, 'join')).toEqual([
49
+ expect(makePipelineSpecModel(diamond).ancestry('join')).toEqual([
52
50
  { step: 'root', depth: 2 },
53
51
  { step: 'left', depth: 1 },
54
52
  { step: 'right', depth: 2 },
@@ -57,14 +55,14 @@ describe('agent-common — pipeline ancestry', () => {
57
55
  })
58
56
 
59
57
  test('refuses a step the spec does not declare', () => {
60
- expect(() => pipelineAncestors(diamond, 'ghost')).toThrow(/pipeline-step/)
58
+ expect(() => makePipelineSpecModel(diamond).ancestors('ghost')).toThrow(/pipeline-step/)
61
59
  })
62
60
  })
63
61
 
64
62
  describe('agent-common — rendering an entry', () => {
65
63
  test('the same facts render the same bytes in whatever order they arrive', () => {
66
- const one = renderResultEntry(entry())
67
- const two = renderResultEntry(entry({ facts: [file, create, user] }))
64
+ const one = cumulativeResultsHelper.renderResultEntry(entry())
65
+ const two = cumulativeResultsHelper.renderResultEntry(entry({ facts: [file, create, user] }))
68
66
 
69
67
  expect(two).toEqual(one)
70
68
  expect(one.full).toBe([
@@ -76,7 +74,7 @@ describe('agent-common — rendering an entry', () => {
76
74
  })
77
75
 
78
76
  test('the names-only form lists names by kind and says it is names only', () => {
79
- expect(renderResultEntry(entry()).compact).toBe([
77
+ expect(cumulativeResultsHelper.renderResultEntry(entry()).compact).toBe([
80
78
  '### types (names only)',
81
79
  '- endpoint: `user.create`',
82
80
  '- type: `User`',
@@ -85,7 +83,7 @@ describe('agent-common — rendering an entry', () => {
85
83
  })
86
84
 
87
85
  test('over its cap it drops whole facts, never part of one, and says how many', () => {
88
- const { full } = renderResultEntry(entry(), { maxChars: 200 })
86
+ const { full } = cumulativeResultsHelper.renderResultEntry(entry(), { maxChars: 200 })
89
87
 
90
88
  expect(full.length).toBeLessThanOrEqual(200)
91
89
  expect(full).toContain('- endpoint `user.create`')
@@ -94,8 +92,8 @@ describe('agent-common — rendering an entry', () => {
94
92
  })
95
93
 
96
94
  test('a summary is always labelled unverified, and a summary alone heads the entry so', () => {
97
- const withFacts = renderResultEntry(entry({ summary: 'Chose soft deletes.' })).full
98
- const alone = renderResultEntry(entry({
95
+ const withFacts = cumulativeResultsHelper.renderResultEntry(entry({ summary: 'Chose soft deletes.' })).full
96
+ const alone = cumulativeResultsHelper.renderResultEntry(entry({
99
97
  facts: [], summary: 'Chose soft deletes.', source: CumulativeResultSource.Summarized,
100
98
  })).full
101
99
 
@@ -104,37 +102,37 @@ describe('agent-common — rendering an entry', () => {
104
102
  })
105
103
 
106
104
  test('a partial entry says the step failed, and an empty ordinary entry renders nothing', () => {
107
- expect(renderResultEntry(entry({ partial: true })).full.split('\n')[0])
105
+ expect(cumulativeResultsHelper.renderResultEntry(entry({ partial: true })).full.split('\n')[0])
108
106
  .toBe('### types (partial: the step failed part-way; only what it left in the files is listed)')
109
- expect(renderResultEntry(entry({ facts: [] }))).toEqual({ full: '', compact: '' })
107
+ expect(cumulativeResultsHelper.renderResultEntry(entry({ facts: [] }))).toEqual({ full: '', compact: '' })
110
108
  })
111
109
  })
112
110
 
113
111
  describe('agent-common — ledger helpers', () => {
114
112
  test('a composed run sorts inside its composing step, ahead of that step\'s own entry', () => {
115
113
  const keys = [[3], [1], [3, 2], [3, 1], [0], [4]]
116
- expect([...keys].sort(compareResultOrder)).toEqual([[0], [1], [3, 1], [3, 2], [3], [4]])
114
+ expect([...keys].sort(cumulativeResultsHelper.compareResultOrder)).toEqual([[0], [1], [3, 1], [3, 2], [3], [4]])
117
115
  })
118
116
 
119
117
  test('labels a composed run\'s step by its path under the root run', () => {
120
- expect(resultLabel('r', 'r', 'types')).toBe('types')
121
- expect(resultLabel('r', 'r/design', 'screens')).toBe('design/screens')
122
- expect(resultLabel('r', 'r/design/deep', 'x')).toBe('design/deep/x')
118
+ expect(cumulativeResultsHelper.resultLabel('r', 'r', 'types')).toBe('types')
119
+ expect(cumulativeResultsHelper.resultLabel('r', 'r/design', 'screens')).toBe('design/screens')
120
+ expect(cumulativeResultsHelper.resultLabel('r', 'r/design/deep', 'x')).toBe('design/deep/x')
123
121
  })
124
122
 
125
123
  test('a fact is identified by kind, name and where it comes from', () => {
126
- expect(factKey(user)).toBe(factKey({ ...user, signature: 'other' }))
127
- expect(factKey(user)).not.toBe(factKey({ ...user, specifier: '@app/other' }))
124
+ expect(cumulativeResultsHelper.factKey(user)).toBe(cumulativeResultsHelper.factKey({ ...user, signature: 'other' }))
125
+ expect(cumulativeResultsHelper.factKey(user)).not.toBe(cumulativeResultsHelper.factKey({ ...user, specifier: '@app/other' }))
128
126
  })
129
127
 
130
128
  test('a summary answer renders deterministically and is capped after the fact', () => {
131
- expect(renderResultSummary({ summary: ' two\nlines ' })).toBe('two lines')
132
- expect(renderResultSummary({ b: ['x', 'y'], a: 1 })).toBe('a: 1; b: x, y')
133
- expect(renderResultSummary({ summary: 'word '.repeat(100) }, 40).length).toBeLessThanOrEqual(40)
129
+ expect(cumulativeResultsHelper.renderResultSummary({ summary: ' two\nlines ' })).toBe('two lines')
130
+ expect(cumulativeResultsHelper.renderResultSummary({ b: ['x', 'y'], a: 1 })).toBe('a: 1; b: x, y')
131
+ expect(cumulativeResultsHelper.renderResultSummary({ summary: 'word '.repeat(100) }, 40).length).toBeLessThanOrEqual(40)
134
132
  })
135
133
 
136
134
  test('merging declarations accumulates extractors and consumers, later scalars win', () => {
137
- const merged = mergeResultsSpecs(
135
+ const merged = cumulativeResultsHelper.mergeResultsSpecs(
138
136
  { window: 1, steps: { types: { extractors: ['ts'], full: ['resources'], maxChars: 100 } } },
139
137
  null,
140
138
  { window: 3, steps: { types: { extractors: ['ts', 'schema'], full: ['endpoints'], maxChars: 200 } } },
package/build/flows.d.ts DELETED
@@ -1,21 +0,0 @@
1
- import type { ShallowFlow } from '@owlmeans/flow';
2
- /**
3
- * The lifecycle of one agent run.
4
- *
5
- * Read it as "how far did this run get", not "what did it say". Everything conversational happens
6
- * inside `Working`; the steps exist so a plugin reading `onFinish` can say where a run ended.
7
- * `Fail` is reachable from every working step and is TERMINAL — see the note on that step.
8
- *
9
- * `service` is left as the flow name on every step. A flow step normally binds to a service or an
10
- * entrypoint, but an agent run is driven by whoever holds the model — there is no second party to
11
- * hand control to, and inventing one would put a name in the serialized state that nothing
12
- * resolves.
13
- *
14
- * Each working step keeps exactly ONE non-explicit outgoing transition, so `FlowModel.next()`
15
- * always has an unambiguous answer: that is what lets a driver advance the run without knowing the
16
- * vocabulary. `Fail` is marked explicit precisely so it never becomes that automatic answer.
17
- */
18
- export declare const agentRunFlow: ShallowFlow;
19
- /** Every flow this package declares, for a provider to serve. */
20
- export declare const agentFlows: ShallowFlow[];
21
- //# sourceMappingURL=flows.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"flows.d.ts","sourceRoot":"","sources":["../src/flows.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAGjD;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,YAAY,EAAE,WA0F1B,CAAA;AAED,iEAAiE;AACjE,eAAO,MAAM,UAAU,EAAE,WAAW,EAAmB,CAAA"}
package/build/flows.js DELETED
@@ -1,105 +0,0 @@
1
- import { AGENT_RUN_FLOW, AgentRunStep, AgentRunTransition } from './consts.js';
2
- /**
3
- * The lifecycle of one agent run.
4
- *
5
- * Read it as "how far did this run get", not "what did it say". Everything conversational happens
6
- * inside `Working`; the steps exist so a plugin reading `onFinish` can say where a run ended.
7
- * `Fail` is reachable from every working step and is TERMINAL — see the note on that step.
8
- *
9
- * `service` is left as the flow name on every step. A flow step normally binds to a service or an
10
- * entrypoint, but an agent run is driven by whoever holds the model — there is no second party to
11
- * hand control to, and inventing one would put a name in the serialized state that nothing
12
- * resolves.
13
- *
14
- * Each working step keeps exactly ONE non-explicit outgoing transition, so `FlowModel.next()`
15
- * always has an unambiguous answer: that is what lets a driver advance the run without knowing the
16
- * vocabulary. `Fail` is marked explicit precisely so it never becomes that automatic answer.
17
- */
18
- export const agentRunFlow = {
19
- flow: AGENT_RUN_FLOW,
20
- initialStep: AgentRunStep.Received,
21
- steps: {
22
- [AgentRunStep.Received]: {
23
- index: 0,
24
- step: AgentRunStep.Received,
25
- service: AGENT_RUN_FLOW,
26
- initial: true,
27
- transitions: {
28
- [AgentRunTransition.Prepare]: {
29
- transition: AgentRunTransition.Prepare,
30
- step: AgentRunStep.Prepared,
31
- },
32
- [AgentRunTransition.Fail]: {
33
- transition: AgentRunTransition.Fail,
34
- step: AgentRunStep.Failed,
35
- explicit: true,
36
- },
37
- },
38
- },
39
- [AgentRunStep.Prepared]: {
40
- index: 1,
41
- step: AgentRunStep.Prepared,
42
- service: AGENT_RUN_FLOW,
43
- transitions: {
44
- [AgentRunTransition.Work]: {
45
- transition: AgentRunTransition.Work,
46
- step: AgentRunStep.Working,
47
- },
48
- [AgentRunTransition.Fail]: {
49
- transition: AgentRunTransition.Fail,
50
- step: AgentRunStep.Failed,
51
- explicit: true,
52
- },
53
- },
54
- },
55
- [AgentRunStep.Working]: {
56
- index: 2,
57
- step: AgentRunStep.Working,
58
- service: AGENT_RUN_FLOW,
59
- transitions: {
60
- [AgentRunTransition.Finalize]: {
61
- transition: AgentRunTransition.Finalize,
62
- step: AgentRunStep.Finalizing,
63
- },
64
- [AgentRunTransition.Fail]: {
65
- transition: AgentRunTransition.Fail,
66
- step: AgentRunStep.Failed,
67
- explicit: true,
68
- },
69
- },
70
- },
71
- [AgentRunStep.Finalizing]: {
72
- index: 3,
73
- step: AgentRunStep.Finalizing,
74
- service: AGENT_RUN_FLOW,
75
- transitions: {
76
- [AgentRunTransition.Finish]: {
77
- transition: AgentRunTransition.Finish,
78
- step: AgentRunStep.Finished,
79
- },
80
- [AgentRunTransition.Fail]: {
81
- transition: AgentRunTransition.Fail,
82
- step: AgentRunStep.Failed,
83
- explicit: true,
84
- },
85
- },
86
- },
87
- [AgentRunStep.Finished]: {
88
- index: 4,
89
- step: AgentRunStep.Finished,
90
- service: AGENT_RUN_FLOW,
91
- transitions: {},
92
- },
93
- [AgentRunStep.Failed]: {
94
- index: 5,
95
- step: AgentRunStep.Failed,
96
- service: AGENT_RUN_FLOW,
97
- // Terminal. Nothing resumes a ReAct run — its tool results are side effects already applied
98
- // to the world, so re-entering it re-applies them. Resumable work is a pipeline.
99
- transitions: {},
100
- },
101
- },
102
- };
103
- /** Every flow this package declares, for a provider to serve. */
104
- export const agentFlows = [agentRunFlow];
105
- //# sourceMappingURL=flows.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"flows.js","sourceRoot":"","sources":["../src/flows.ts"],"names":[],"mappings":"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"}