@llm4ts/flow 2.31.1 → 2.32.0

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 (86) hide show
  1. package/dist/Approval.d.ts +1 -1
  2. package/dist/Approval.d.ts.map +1 -1
  3. package/dist/Artifacts.d.ts +2 -2
  4. package/dist/Artifacts.d.ts.map +1 -1
  5. package/dist/BenchReport.d.ts +2 -2
  6. package/dist/BenchReport.d.ts.map +1 -1
  7. package/dist/Chat.d.ts +5 -2
  8. package/dist/Chat.d.ts.map +1 -1
  9. package/dist/Chat.js +7 -4
  10. package/dist/Chat.js.map +1 -1
  11. package/dist/CostLedger.d.ts +2 -2
  12. package/dist/CostLedger.d.ts.map +1 -1
  13. package/dist/Equiv.d.ts +3 -3
  14. package/dist/Equiv.d.ts.map +1 -1
  15. package/dist/Flow.d.ts +16 -1
  16. package/dist/Flow.d.ts.map +1 -1
  17. package/dist/Flow.js +29 -3
  18. package/dist/Flow.js.map +1 -1
  19. package/dist/FlowError.d.ts +15 -2
  20. package/dist/FlowError.d.ts.map +1 -1
  21. package/dist/FlowError.js +15 -0
  22. package/dist/FlowError.js.map +1 -1
  23. package/dist/FlowEvents.d.ts +12 -1
  24. package/dist/FlowEvents.d.ts.map +1 -1
  25. package/dist/FlowEvents.js +11 -0
  26. package/dist/FlowEvents.js.map +1 -1
  27. package/dist/Gates.d.ts +1 -1
  28. package/dist/Gates.d.ts.map +1 -1
  29. package/dist/Judgment.d.ts +2 -2
  30. package/dist/Judgment.d.ts.map +1 -1
  31. package/dist/Landing.d.ts +1 -1
  32. package/dist/Landing.d.ts.map +1 -1
  33. package/dist/NodePreflight.d.ts +1 -1
  34. package/dist/NodePreflight.d.ts.map +1 -1
  35. package/dist/Pack.d.ts +9 -1
  36. package/dist/Pack.d.ts.map +1 -1
  37. package/dist/Pack.js +26 -2
  38. package/dist/Pack.js.map +1 -1
  39. package/dist/Persistence.d.ts +2 -2
  40. package/dist/Persistence.d.ts.map +1 -1
  41. package/dist/PlanExecution.d.ts +1 -1
  42. package/dist/PlanExecution.d.ts.map +1 -1
  43. package/dist/ProgramJudge.d.ts +1 -1
  44. package/dist/ProgramJudge.d.ts.map +1 -1
  45. package/dist/ProgramJudge.js +6 -3
  46. package/dist/ProgramJudge.js.map +1 -1
  47. package/dist/Replay.d.ts +2 -2
  48. package/dist/Replay.d.ts.map +1 -1
  49. package/dist/Retro.d.ts +51 -4
  50. package/dist/Retro.d.ts.map +1 -1
  51. package/dist/Retro.js +196 -11
  52. package/dist/Retro.js.map +1 -1
  53. package/dist/Review.d.ts +38 -6
  54. package/dist/Review.d.ts.map +1 -1
  55. package/dist/Review.js +147 -8
  56. package/dist/Review.js.map +1 -1
  57. package/dist/ReviewCache.d.ts +1 -1
  58. package/dist/ReviewCache.d.ts.map +1 -1
  59. package/dist/Reviewer.d.ts +2 -0
  60. package/dist/Reviewer.d.ts.map +1 -1
  61. package/dist/Reviewer.js +3 -1
  62. package/dist/Reviewer.js.map +1 -1
  63. package/dist/Roster.d.ts +1 -1
  64. package/dist/Roster.d.ts.map +1 -1
  65. package/dist/RosterSeats.d.ts +1 -1
  66. package/dist/RosterSeats.d.ts.map +1 -1
  67. package/dist/Stall.d.ts +29 -0
  68. package/dist/Stall.d.ts.map +1 -0
  69. package/dist/Stall.js +49 -0
  70. package/dist/Stall.js.map +1 -0
  71. package/dist/Stories.d.ts +8 -1
  72. package/dist/Stories.d.ts.map +1 -1
  73. package/dist/Stories.js +7 -1
  74. package/dist/Stories.js.map +1 -1
  75. package/package.json +3 -2
  76. package/src/Chat.ts +15 -7
  77. package/src/Flow.ts +48 -3
  78. package/src/FlowError.ts +16 -0
  79. package/src/FlowEvents.ts +14 -0
  80. package/src/Pack.ts +34 -1
  81. package/src/ProgramJudge.ts +3 -0
  82. package/src/Retro.ts +229 -4
  83. package/src/Review.ts +202 -8
  84. package/src/Reviewer.ts +3 -1
  85. package/src/Stall.ts +85 -0
  86. package/src/Stories.ts +14 -0
package/src/Stall.ts ADDED
@@ -0,0 +1,85 @@
1
+ // Stopping conditions the model does not control (ADR 0027 decision 11).
2
+ // Pure helpers over a chat's chunk stream; `Chat` applies them when asked.
3
+ import * as Duration from "effect/Duration"
4
+ import * as Effect from "effect/Effect"
5
+ import * as Ref from "effect/Ref"
6
+ import * as Stream from "effect/Stream"
7
+ import type { LlmError } from "@llm4ts/core/Errors"
8
+ import type { LlmChunk } from "@llm4ts/core/Models"
9
+ import { Stalled } from "./FlowError.ts"
10
+
11
+ export interface StallOptions {
12
+ /** The same tool call with the same arguments this many times in a row ends the turn. Default 5. */
13
+ readonly repeats?: number
14
+ /** No chunk for this long ends the turn; absent means never (a slow seat looks like silence). */
15
+ readonly silence?: Duration.Duration
16
+ }
17
+
18
+ export const defaultStallRepeats = 5
19
+
20
+ /** The identity of a tool call chunk: name and arguments; `undefined` for anything else. */
21
+ export const toolCallKey = (chunk: LlmChunk): string | undefined =>
22
+ chunk.metadata.event === "tool_use"
23
+ ? `${chunk.metadata.tool_name ?? chunk.metadata.toolName ?? ""}|${chunk.metadata.tool_input ?? chunk.metadata.toolInput ?? ""}`
24
+ : undefined
25
+
26
+ export interface RepeatState {
27
+ readonly key: string | undefined
28
+ readonly count: number
29
+ }
30
+
31
+ export const noRepeats: RepeatState = { key: undefined, count: 0 }
32
+
33
+ /** The repeat state after one chunk: a different call resets, the same one counts. */
34
+ export const nextRepeat = (state: RepeatState, chunk: LlmChunk): RepeatState => {
35
+ const key = toolCallKey(chunk)
36
+ if (key === undefined) {
37
+ return state
38
+ }
39
+ return key === state.key ? { key, count: state.count + 1 } : { key, count: 1 }
40
+ }
41
+
42
+ /**
43
+ * The stream, ended with `Stalled` when one tool call repeats `repeats`
44
+ * times in a row (the state is shared across a chat's turns through `state`)
45
+ * or when no chunk arrives within `silence`.
46
+ */
47
+ export const stallGuard = <R>(
48
+ stream: Stream.Stream<LlmChunk, LlmError, R>,
49
+ options: StallOptions,
50
+ state: Ref.Ref<RepeatState>
51
+ ): Stream.Stream<LlmChunk, LlmError | Stalled, R> => {
52
+ const limit = Math.max(2, options.repeats ?? defaultStallRepeats)
53
+ const counted = Stream.mapEffect(stream, (chunk) =>
54
+ Effect.flatMap(
55
+ Ref.modify(state, (current) => {
56
+ const next = nextRepeat(current, chunk)
57
+ return [next, next] as const
58
+ }),
59
+ (next) =>
60
+ next.count >= limit
61
+ ? Effect.fail(
62
+ Stalled.make({
63
+ signal: "repeated-tool-call",
64
+ detail: `the same tool call ${next.count} times in a row: ${(next.key ?? "").slice(0, 200)}`
65
+ })
66
+ )
67
+ : Effect.succeed(chunk)
68
+ )
69
+ )
70
+ const silence = options.silence
71
+ return silence === undefined
72
+ ? counted
73
+ : counted.pipe(
74
+ Stream.timeoutOrElse({
75
+ duration: silence,
76
+ orElse: () =>
77
+ Stream.fail(
78
+ Stalled.make({
79
+ signal: "silence",
80
+ detail: `no output for ${Duration.format(silence)} (LLM4TS_STALL_MINUTES)`
81
+ })
82
+ )
83
+ })
84
+ )
85
+ }
package/src/Stories.ts CHANGED
@@ -72,6 +72,7 @@ import {
72
72
  ReviewIssue,
73
73
  ReviewResult,
74
74
  applyTriage,
75
+ loadRepoReviewRules,
75
76
  type FixPromptOptions,
76
77
  type GateTriageOptions
77
78
  } from "./Review.ts"
@@ -85,6 +86,7 @@ import { cachedValue, fingerprintOf } from "./ReviewCache.ts"
85
86
  import { withContract, type ContractProfile } from "./AutonomyContract.ts"
86
87
  import { fabricatedStatusIssues, unverifiedClaims } from "./Evidence.ts"
87
88
  import type { Trailer } from "./CarriedNotes.ts"
89
+ import type { StallOptions } from "./Stall.ts"
88
90
  import type { Reviewer } from "./Reviewer.ts"
89
91
  import {
90
92
  dependentsOf,
@@ -659,6 +661,12 @@ export interface StoriesOptions {
659
661
  readonly fix?: FixPromptOptions
660
662
  /** Test-file and marker patterns for the oracle guard; default rules when absent (ADR 0027). */
661
663
  readonly oracleRules?: OracleRules
664
+ /** Independent votes of the adversarial lens per review round (ADR 0027 decision 7). Default 1. */
665
+ readonly votes?: number
666
+ /** Who applies review findings: the implementer's chat (default) or a separate fixer (ADR 0027 decision 8). */
667
+ readonly fixer?: "coder" | "separate"
668
+ /** End a coder turn that repeats one tool call or goes silent (ADR 0027 decision 11). */
669
+ readonly stall?: StallOptions
662
670
  /**
663
671
  * The autonomy contract profile for the coder's system prompt (ADR 0027
664
672
  * decision 5). Default: the roster executor's `contract`, else `full`.
@@ -1447,6 +1455,12 @@ export const implementStoriesFlow = Effect.fn("@llm4ts/flow/Stories.implement")(
1447
1455
  result
1448
1456
  ),
1449
1457
  onTaskReply: (task, _reply, trailer, startedAt) => checkEvidence(task, trailer, startedAt),
1458
+ // The repository's own review rules ride along as one extra lens
1459
+ // (ADR 0027 decision 9), read from the epic checkout.
1460
+ repoRules: loadRepoReviewRules(files, context.workDir),
1461
+ ...(options.votes === undefined ? {} : { votes: options.votes }),
1462
+ ...(options.fixer === undefined ? {} : { fixer: options.fixer }),
1463
+ ...(options.stall === undefined ? {} : { stall: options.stall }),
1450
1464
  ...(options.reviewers === undefined ? {} : { reviewers: options.reviewers }),
1451
1465
  ...(options.maxRounds === undefined ? {} : { maxRounds: options.maxRounds })
1452
1466
  })