@united-workforce/cli 0.7.0 → 0.8.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.
Files changed (111) hide show
  1. package/README.md +32 -5
  2. package/dist/.build-fingerprint +1 -0
  3. package/dist/__tests__/broker-step-active-turns.test.d.ts +20 -0
  4. package/dist/__tests__/broker-step-active-turns.test.d.ts.map +1 -0
  5. package/dist/__tests__/broker-step-active-turns.test.js +428 -0
  6. package/dist/__tests__/broker-step-active-turns.test.js.map +1 -0
  7. package/dist/__tests__/broker-step-turn-chain-phase2.test.d.ts +13 -0
  8. package/dist/__tests__/broker-step-turn-chain-phase2.test.d.ts.map +1 -0
  9. package/dist/__tests__/broker-step-turn-chain-phase2.test.js +429 -0
  10. package/dist/__tests__/broker-step-turn-chain-phase2.test.js.map +1 -0
  11. package/dist/__tests__/e2e-broker-step-suspend.test.d.ts +18 -0
  12. package/dist/__tests__/e2e-broker-step-suspend.test.d.ts.map +1 -0
  13. package/dist/__tests__/e2e-broker-step-suspend.test.js +313 -0
  14. package/dist/__tests__/e2e-broker-step-suspend.test.js.map +1 -0
  15. package/dist/__tests__/e2e-thread-resume-timeout-suspend.test.d.ts +28 -0
  16. package/dist/__tests__/e2e-thread-resume-timeout-suspend.test.d.ts.map +1 -0
  17. package/dist/__tests__/e2e-thread-resume-timeout-suspend.test.js +322 -0
  18. package/dist/__tests__/e2e-thread-resume-timeout-suspend.test.js.map +1 -0
  19. package/dist/__tests__/log-tag-validity.test.d.ts +2 -0
  20. package/dist/__tests__/log-tag-validity.test.d.ts.map +1 -0
  21. package/dist/__tests__/log-tag-validity.test.js +110 -0
  22. package/dist/__tests__/log-tag-validity.test.js.map +1 -0
  23. package/dist/__tests__/setup-agent-discovery.test.js +23 -23
  24. package/dist/__tests__/setup-agent-discovery.test.js.map +1 -1
  25. package/dist/__tests__/step-show-json.test.js +5 -5
  26. package/dist/__tests__/step-show-json.test.js.map +1 -1
  27. package/dist/__tests__/step-show-text.test.d.ts +2 -0
  28. package/dist/__tests__/step-show-text.test.d.ts.map +1 -0
  29. package/dist/__tests__/step-show-text.test.js +192 -0
  30. package/dist/__tests__/step-show-text.test.js.map +1 -0
  31. package/dist/__tests__/step-turns-cli-subprocess.test.d.ts +21 -0
  32. package/dist/__tests__/step-turns-cli-subprocess.test.d.ts.map +1 -0
  33. package/dist/__tests__/step-turns-cli-subprocess.test.js +356 -0
  34. package/dist/__tests__/step-turns-cli-subprocess.test.js.map +1 -0
  35. package/dist/__tests__/step-turns-panorama-phase3.test.d.ts +21 -0
  36. package/dist/__tests__/step-turns-panorama-phase3.test.d.ts.map +1 -0
  37. package/dist/__tests__/step-turns-panorama-phase3.test.js +476 -0
  38. package/dist/__tests__/step-turns-panorama-phase3.test.js.map +1 -0
  39. package/dist/__tests__/step-turns.test.d.ts +24 -0
  40. package/dist/__tests__/step-turns.test.d.ts.map +1 -0
  41. package/dist/__tests__/step-turns.test.js +646 -0
  42. package/dist/__tests__/step-turns.test.js.map +1 -0
  43. package/dist/__tests__/store-turn-chain.test.d.ts +2 -0
  44. package/dist/__tests__/store-turn-chain.test.d.ts.map +1 -0
  45. package/dist/__tests__/store-turn-chain.test.js +341 -0
  46. package/dist/__tests__/store-turn-chain.test.js.map +1 -0
  47. package/dist/__tests__/thread-list-limit-offset.test.d.ts +24 -0
  48. package/dist/__tests__/thread-list-limit-offset.test.d.ts.map +1 -0
  49. package/dist/__tests__/thread-list-limit-offset.test.js +254 -0
  50. package/dist/__tests__/thread-list-limit-offset.test.js.map +1 -0
  51. package/dist/__tests__/thread-list-template-ms-date.test.js +7 -2
  52. package/dist/__tests__/thread-list-template-ms-date.test.js.map +1 -1
  53. package/dist/__tests__/thread.test.js +28 -14
  54. package/dist/__tests__/thread.test.js.map +1 -1
  55. package/dist/cli.js +910 -344
  56. package/dist/cli.js.map +1 -1
  57. package/dist/commands/broker-step.d.ts +10 -3
  58. package/dist/commands/broker-step.d.ts.map +1 -1
  59. package/dist/commands/broker-step.js +231 -27
  60. package/dist/commands/broker-step.js.map +1 -1
  61. package/dist/commands/prompt.d.ts.map +1 -1
  62. package/dist/commands/prompt.js +42 -50
  63. package/dist/commands/prompt.js.map +1 -1
  64. package/dist/commands/setup.d.ts +6 -4
  65. package/dist/commands/setup.d.ts.map +1 -1
  66. package/dist/commands/setup.js +16 -26
  67. package/dist/commands/setup.js.map +1 -1
  68. package/dist/commands/step.d.ts +48 -1
  69. package/dist/commands/step.d.ts.map +1 -1
  70. package/dist/commands/step.js +496 -3
  71. package/dist/commands/step.js.map +1 -1
  72. package/dist/output-mappers.d.ts +8 -0
  73. package/dist/output-mappers.d.ts.map +1 -1
  74. package/dist/output-mappers.js +72 -18
  75. package/dist/output-mappers.js.map +1 -1
  76. package/dist/schemas.d.ts +3 -0
  77. package/dist/schemas.d.ts.map +1 -1
  78. package/dist/schemas.js +17 -3
  79. package/dist/schemas.js.map +1 -1
  80. package/dist/store.d.ts +147 -1
  81. package/dist/store.d.ts.map +1 -1
  82. package/dist/store.js +254 -1
  83. package/dist/store.js.map +1 -1
  84. package/dist/text-renderers.d.ts.map +1 -1
  85. package/dist/text-renderers.js +27 -2
  86. package/dist/text-renderers.js.map +1 -1
  87. package/package.json +7 -6
  88. package/src/__tests__/broker-step-active-turns.test.ts +509 -0
  89. package/src/__tests__/broker-step-turn-chain-phase2.test.ts +525 -0
  90. package/src/__tests__/e2e-broker-step-suspend.test.ts +351 -0
  91. package/src/__tests__/e2e-thread-resume-timeout-suspend.test.ts +360 -0
  92. package/src/__tests__/log-tag-validity.test.ts +124 -0
  93. package/src/__tests__/setup-agent-discovery.test.ts +23 -23
  94. package/src/__tests__/step-show-json.test.ts +5 -5
  95. package/src/__tests__/step-show-text.test.ts +236 -0
  96. package/src/__tests__/step-turns-cli-subprocess.test.ts +411 -0
  97. package/src/__tests__/step-turns-panorama-phase3.test.ts +579 -0
  98. package/src/__tests__/step-turns.test.ts +734 -0
  99. package/src/__tests__/store-turn-chain.test.ts +386 -0
  100. package/src/__tests__/thread-list-limit-offset.test.ts +305 -0
  101. package/src/__tests__/thread-list-template-ms-date.test.ts +7 -2
  102. package/src/__tests__/thread.test.ts +29 -15
  103. package/src/cli.ts +1056 -483
  104. package/src/commands/broker-step.ts +315 -38
  105. package/src/commands/prompt.ts +42 -50
  106. package/src/commands/setup.ts +16 -28
  107. package/src/commands/step.ts +655 -3
  108. package/src/output-mappers.ts +99 -21
  109. package/src/schemas.ts +32 -2
  110. package/src/store.ts +297 -2
  111. package/src/text-renderers.ts +35 -2
package/README.md CHANGED
@@ -16,11 +16,11 @@ workflow → thread → step → turn
16
16
  - **Workflow** (layer 1): YAML template with roles and routing graph
17
17
  - **Thread** (layer 2): Single workflow execution instance
18
18
  - **Step** (layer 3): One moderator→agent→extract cycle
19
- - **Turn** (layer 4): Agent-internal interactions (use `step show` or CAS to inspect)
19
+ - **Turn** (layer 4): Agent-internal interactions (use `step turns` to see the whole-thread turn panorama — every step's turns, with the in-flight step marked 进行中 — or `step show` / CAS to inspect)
20
20
 
21
21
  This package has no library `src/index.ts` — it is consumed as a CLI binary only.
22
22
 
23
- **Dependencies:** `@ocas/core`, `@ocas/fs`, `@united-workforce/util-agent`, `@united-workforce/protocol`, `@united-workforce/util`, `commander`, `dotenv`, `mustache`, `yaml`
23
+ **Dependencies:** `@ocas/core`, `@ocas/cli-kit`, `@ocas/fs`, `@united-workforce/util-agent`, `@united-workforce/protocol`, `@united-workforce/util`, `dotenv`, `mustache`, `yaml`
24
24
 
25
25
  ## Installation
26
26
 
@@ -63,7 +63,7 @@ The `json` and `yaml` envelopes carry the schema hash on the `type` field so con
63
63
  | `uwf thread start <workflow> -p <prompt>` | Create a thread without executing |
64
64
  | `uwf thread exec <thread-id> [--agent <cmd>] [-c <count>] [--background]` | Execute one or more moderator→agent→extract cycles |
65
65
  | `uwf thread show <thread-id>` | Show thread head pointer |
66
- | `uwf thread list [--status <status>] [--all] [--after <date>] [--before <date>] [--skip <n>] [--take <n>]` | List threads (defaults to active: idle + running). Use `--all` to include end/cancelled/suspended, or `--status` to filter explicitly (idle, running, suspended, end, cancelled, active, or comma-separated). Supports time range and pagination. |
66
+ | `uwf thread list [--status <status>] [--all] [--after <date>] [--before <date>] [--limit <n>] [--offset <m>]` | List threads (defaults to active: idle + running). Use `--all` to include end/cancelled/suspended, or `--status` to filter explicitly (idle, running, suspended, end, cancelled, active, or comma-separated). Supports time range and pagination (`--limit`/`--offset`; `--take`/`--skip` are accepted as aliases). |
67
67
  | `uwf thread read <thread-id> [--quota N] [--before <hash>] [--start]` | Render thread as readable markdown |
68
68
 
69
69
  `thread read`, `step list`, and `step show` work on both active and ended threads.
@@ -74,7 +74,12 @@ The `json` and `yaml` envelopes carry the schema hash on the `type` field so con
74
74
 
75
75
  ### Suspend / Resume
76
76
 
77
- When an agent emits `$status: "$SUSPEND"` in its frontmatter, the thread enters `suspended` status. A suspended thread **cannot be advanced with `exec`** — `exec` will detect the suspended head and return immediately without running any agent.
77
+ A thread enters `suspended` status from **two sources**, both landing at the same `$status: "$SUSPEND"` exit:
78
+
79
+ 1. **Voluntary** — an agent emits `$status: "$SUSPEND"` in its frontmatter (it hits a token budget, needs human input, etc.).
80
+ 2. **Timeout checkpoint** — the agent's `send` exceeds the adapter timeout. Instead of killing the step as an error, the broker reports a `kind: "suspended"` result and the step is written as a `$SUSPEND` node carrying the timeout `reason` (and the `nativeId` needed to resume). A timeout is a **checkpoint, not a death** — work already produced is preserved and the thread can be continued.
81
+
82
+ A suspended thread **cannot be advanced with `exec`** — `exec` detects the suspended head and returns immediately without running any agent.
78
83
 
79
84
  To continue a suspended thread, use `resume`:
80
85
 
@@ -83,6 +88,8 @@ uwf thread resume <thread-id> # resume with workflow's defaul
83
88
  uwf thread resume <thread-id> -p "version 1.2.0" # resume with supplementary context
84
89
  ```
85
90
 
91
+ `resume` issues a **fresh `send`** on the same cached `(threadId, role)` session; the underlying agent adapter resumes by its native session id (`--resume <nativeId>`), so the agent continues from its own history rather than starting over. For a timeout-suspended thread, `-p` supplies a short continuation prompt (e.g. `"continue"`) — the original prompt is not re-sent.
92
+
86
93
  > ⚠️ `exec` does not advance suspended threads — you **must** use `resume` to provide context and continue.
87
94
 
88
95
  Examples:
@@ -97,7 +104,8 @@ uwf thread list --all
97
104
  uwf thread list --status running
98
105
  uwf thread list --status active
99
106
  uwf thread list --status idle,end
100
- uwf thread list --after 7d --take 10
107
+ uwf thread list --after 7d --limit 10
108
+ uwf thread list --limit 5 --offset 10
101
109
  uwf thread read 01ARZ3NDEKTSV4RRFFQ69G5FAV --quota 8000
102
110
  uwf thread stop 01ARZ3NDEKTSV4RRFFQ69G5FAV
103
111
  ```
@@ -109,6 +117,7 @@ uwf thread stop 01ARZ3NDEKTSV4RRFFQ69G5FAV
109
117
  | `uwf step list <thread-id>` | List all steps in a thread chronologically |
110
118
  | `uwf step show <step-hash>` | Show step metadata and frontmatter |
111
119
  | `uwf step read <step-hash> [--quota <chars>]` | Read a step's turns as human-readable markdown |
120
+ | `uwf step turns <thread-id> [--role <r>] [--live] [--limit <n>] [--offset <m>]` | Show **all** turns across a thread's steps (whole-chain panorama): each completed step from its `detail.turns` (`✓`), the in-flight step from its active var (`🔄 进行中`) |
112
121
  | `uwf step fork <step-hash>` | Fork a thread from a specific step |
113
122
  | `uwf step ask <step-hash> -p <prompt> [--agent <cmd>] [--no-fork]` | Ask a follow-up question to a historical step's agent (read-only; no thread mutation) |
114
123
 
@@ -118,11 +127,29 @@ Examples:
118
127
  uwf step list 01ARZ3NDEKTSV4RRFFQ69G5FAV
119
128
  uwf step show 32GCDE899RRQ3
120
129
  uwf step read 32GCDE899RRQ3 --quota 2000
130
+ uwf step turns 01ARZ3NDEKTSV4RRFFQ69G5FAV # whole-thread panorama
131
+ uwf step turns 01ARZ3NDEKTSV4RRFFQ69G5FAV --role coder # filter to one role's steps
132
+ uwf step turns 01ARZ3NDEKTSV4RRFFQ69G5FAV --limit 20 --offset 40 # paginate the flat sequence
133
+ uwf step turns 01ARZ3NDEKTSV4RRFFQ69G5FAV --role coder --live # follow the in-flight step
121
134
  uwf step fork 32GCDE899RRQ3
122
135
  uwf step ask 32GCDE899RRQ3 -p "Why did you choose this approach?"
123
136
  uwf step ask 32GCDE899RRQ3 -p "Summarise the key findings" --no-fork
124
137
  ```
125
138
 
139
+ `step turns` is the turn-layer (layer 4) query keyed by `<thread-id>`. Unlike
140
+ `step read` — which renders a *single* completed step's `detail.turns` by step
141
+ hash, quota-bounded — `step turns` renders the **whole-thread turn panorama**: it
142
+ walks the entire thread chain and shows **every** step's turns in chronological
143
+ order, each turn attributed to its owning role/step. Completed steps are read from
144
+ their immutable `detail.turns` and marked `✓`; the in-flight step is read live from
145
+ its `@uwf/active-turns/<thread-id>/<role>` var and marked `🔄 进行中`. **All turns
146
+ show by default** (no truncation); `--limit`/`--offset` paginate the flattened
147
+ cross-step turn sequence. `--role <r>` filters the panorama to one role's steps
148
+ across the whole chain (e.g. on a multi-step thread whose head is a different role,
149
+ `--role developer` still returns the developer step's turns). With `--live` it polls
150
+ the SQLite-backed active var (not SSE) and prints each new turn as it arrives,
151
+ exiting when the in-flight step completes.
152
+
126
153
  ### Workflow (Layer 1: Templates)
127
154
 
128
155
  | Command | Description |
@@ -0,0 +1 @@
1
+ {"hash":"5705b01e563b540d0df2cfb3d50a0ddab22afcd05b62b89bded9a14c0b14e27a","outputs":["__tests__/agent-resolution-llm-free.test.d.ts","__tests__/agent-resolution-llm-free.test.d.ts.map","__tests__/agent-resolution-llm-free.test.js","__tests__/agent-resolution-llm-free.test.js.map","__tests__/broker-prompt.test.d.ts","__tests__/broker-prompt.test.d.ts.map","__tests__/broker-prompt.test.js","__tests__/broker-prompt.test.js.map","__tests__/broker-step-active-turns.test.d.ts","__tests__/broker-step-active-turns.test.d.ts.map","__tests__/broker-step-active-turns.test.js","__tests__/broker-step-active-turns.test.js.map","__tests__/broker-step-turn-chain-phase2.test.d.ts","__tests__/broker-step-turn-chain-phase2.test.d.ts.map","__tests__/broker-step-turn-chain-phase2.test.js","__tests__/broker-step-turn-chain-phase2.test.js.map","__tests__/build-step-entry.test.d.ts","__tests__/build-step-entry.test.d.ts.map","__tests__/build-step-entry.test.js","__tests__/build-step-entry.test.js.map","__tests__/clear-thread-failed-attempts.test.d.ts","__tests__/clear-thread-failed-attempts.test.d.ts.map","__tests__/clear-thread-failed-attempts.test.js","__tests__/clear-thread-failed-attempts.test.js.map","__tests__/concurrency.test.d.ts","__tests__/concurrency.test.d.ts.map","__tests__/concurrency.test.js","__tests__/concurrency.test.js.map","__tests__/config-text-renderer.test.d.ts","__tests__/config-text-renderer.test.d.ts.map","__tests__/config-text-renderer.test.js","__tests__/config-text-renderer.test.js.map","__tests__/config.test.d.ts","__tests__/config.test.d.ts.map","__tests__/config.test.js","__tests__/config.test.js.map","__tests__/current-role.test.d.ts","__tests__/current-role.test.d.ts.map","__tests__/current-role.test.js","__tests__/current-role.test.js.map","__tests__/e2e-broker-step-suspend.test.d.ts","__tests__/e2e-broker-step-suspend.test.d.ts.map","__tests__/e2e-broker-step-suspend.test.js","__tests__/e2e-broker-step-suspend.test.js.map","__tests__/e2e-broker-step.test.d.ts","__tests__/e2e-broker-step.test.d.ts.map","__tests__/e2e-broker-step.test.js","__tests__/e2e-broker-step.test.js.map","__tests__/e2e-mock-agent.test.d.ts","__tests__/e2e-mock-agent.test.d.ts.map","__tests__/e2e-mock-agent.test.js","__tests__/e2e-mock-agent.test.js.map","__tests__/e2e-thread-resume-timeout-suspend.test.d.ts","__tests__/e2e-thread-resume-timeout-suspend.test.d.ts.map","__tests__/e2e-thread-resume-timeout-suspend.test.js","__tests__/e2e-thread-resume-timeout-suspend.test.js.map","__tests__/format-text-default.test.d.ts","__tests__/format-text-default.test.d.ts.map","__tests__/format-text-default.test.js","__tests__/format-text-default.test.js.map","__tests__/format-text-registry.test.d.ts","__tests__/format-text-registry.test.d.ts.map","__tests__/format-text-registry.test.js","__tests__/format-text-registry.test.js.map","__tests__/include-tag.test.d.ts","__tests__/include-tag.test.d.ts.map","__tests__/include-tag.test.js","__tests__/include-tag.test.js.map","__tests__/issue-180-workflow-ref-removed.test.d.ts","__tests__/issue-180-workflow-ref-removed.test.d.ts.map","__tests__/issue-180-workflow-ref-removed.test.js","__tests__/issue-180-workflow-ref-removed.test.js.map","__tests__/log-tag-validity.test.d.ts","__tests__/log-tag-validity.test.d.ts.map","__tests__/log-tag-validity.test.js","__tests__/log-tag-validity.test.js.map","__tests__/log-text-renderer.test.d.ts","__tests__/log-text-renderer.test.d.ts.map","__tests__/log-text-renderer.test.js","__tests__/log-text-renderer.test.js.map","__tests__/log.test.d.ts","__tests__/log.test.d.ts.map","__tests__/log.test.js","__tests__/log.test.js.map","__tests__/moderator-evaluate.test.d.ts","__tests__/moderator-evaluate.test.d.ts.map","__tests__/moderator-evaluate.test.js","__tests__/moderator-evaluate.test.js.map","__tests__/output-mapper-thread-list-startedat.test.d.ts","__tests__/output-mapper-thread-list-startedat.test.d.ts.map","__tests__/output-mapper-thread-list-startedat.test.js","__tests__/output-mapper-thread-list-startedat.test.js.map","__tests__/output-mapper-workflow-add.test.d.ts","__tests__/output-mapper-workflow-add.test.d.ts.map","__tests__/output-mapper-workflow-add.test.js","__tests__/output-mapper-workflow-add.test.js.map","__tests__/pid-recycling.test.d.ts","__tests__/pid-recycling.test.d.ts.map","__tests__/pid-recycling.test.js","__tests__/pid-recycling.test.js.map","__tests__/preload.d.ts","__tests__/preload.d.ts.map","__tests__/preload.js","__tests__/preload.js.map","__tests__/prompt.test.d.ts","__tests__/prompt.test.d.ts.map","__tests__/prompt.test.js","__tests__/prompt.test.js.map","__tests__/resolve-head-hash.test.d.ts","__tests__/resolve-head-hash.test.d.ts.map","__tests__/resolve-head-hash.test.js","__tests__/resolve-head-hash.test.js.map","__tests__/setup-agent-discovery.test.d.ts","__tests__/setup-agent-discovery.test.d.ts.map","__tests__/setup-agent-discovery.test.js","__tests__/setup-agent-discovery.test.js.map","__tests__/setup-complexity.test.d.ts","__tests__/setup-complexity.test.d.ts.map","__tests__/setup-complexity.test.js","__tests__/setup-complexity.test.js.map","__tests__/setup-no-llm.test.d.ts","__tests__/setup-no-llm.test.d.ts.map","__tests__/setup-no-llm.test.js","__tests__/setup-no-llm.test.js.map","__tests__/solve-issue-tea-worktree.test.d.ts","__tests__/solve-issue-tea-worktree.test.d.ts.map","__tests__/solve-issue-tea-worktree.test.js","__tests__/solve-issue-tea-worktree.test.js.map","__tests__/step-ask.test.d.ts","__tests__/step-ask.test.d.ts.map","__tests__/step-ask.test.js","__tests__/step-ask.test.js.map","__tests__/step-read.test.d.ts","__tests__/step-read.test.d.ts.map","__tests__/step-read.test.js","__tests__/step-read.test.js.map","__tests__/step-show-json.test.d.ts","__tests__/step-show-json.test.d.ts.map","__tests__/step-show-json.test.js","__tests__/step-show-json.test.js.map","__tests__/step-show-text.test.d.ts","__tests__/step-show-text.test.d.ts.map","__tests__/step-show-text.test.js","__tests__/step-show-text.test.js.map","__tests__/step-timing.test.d.ts","__tests__/step-timing.test.d.ts.map","__tests__/step-timing.test.js","__tests__/step-timing.test.js.map","__tests__/step-turns-cli-subprocess.test.d.ts","__tests__/step-turns-cli-subprocess.test.d.ts.map","__tests__/step-turns-cli-subprocess.test.js","__tests__/step-turns-cli-subprocess.test.js.map","__tests__/step-turns-panorama-phase3.test.d.ts","__tests__/step-turns-panorama-phase3.test.d.ts.map","__tests__/step-turns-panorama-phase3.test.js","__tests__/step-turns-panorama-phase3.test.js.map","__tests__/step-turns.test.d.ts","__tests__/step-turns.test.d.ts.map","__tests__/step-turns.test.js","__tests__/step-turns.test.js.map","__tests__/store-global-cas.test.d.ts","__tests__/store-global-cas.test.d.ts.map","__tests__/store-global-cas.test.js","__tests__/store-global-cas.test.js.map","__tests__/store-storage-root.test.d.ts","__tests__/store-storage-root.test.d.ts.map","__tests__/store-storage-root.test.js","__tests__/store-storage-root.test.js.map","__tests__/store-turn-chain.test.d.ts","__tests__/store-turn-chain.test.d.ts.map","__tests__/store-turn-chain.test.js","__tests__/store-turn-chain.test.js.map","__tests__/store-unified-threads.test.d.ts","__tests__/store-unified-threads.test.d.ts.map","__tests__/store-unified-threads.test.js","__tests__/store-unified-threads.test.js.map","__tests__/thread-agent-failure-suspended.test.d.ts","__tests__/thread-agent-failure-suspended.test.d.ts.map","__tests__/thread-agent-failure-suspended.test.js","__tests__/thread-agent-failure-suspended.test.js.map","__tests__/thread-cancel-status.test.d.ts","__tests__/thread-cancel-status.test.d.ts.map","__tests__/thread-cancel-status.test.js","__tests__/thread-cancel-status.test.js.map","__tests__/thread-cancel-text-renderer.test.d.ts","__tests__/thread-cancel-text-renderer.test.d.ts.map","__tests__/thread-cancel-text-renderer.test.js","__tests__/thread-cancel-text-renderer.test.js.map","__tests__/thread-join.test.d.ts","__tests__/thread-join.test.d.ts.map","__tests__/thread-join.test.js","__tests__/thread-join.test.js.map","__tests__/thread-list-filters.test.d.ts","__tests__/thread-list-filters.test.d.ts.map","__tests__/thread-list-filters.test.js","__tests__/thread-list-filters.test.js.map","__tests__/thread-list-limit-offset.test.d.ts","__tests__/thread-list-limit-offset.test.d.ts.map","__tests__/thread-list-limit-offset.test.js","__tests__/thread-list-limit-offset.test.js.map","__tests__/thread-list-template-ms-date.test.d.ts","__tests__/thread-list-template-ms-date.test.d.ts.map","__tests__/thread-list-template-ms-date.test.js","__tests__/thread-list-template-ms-date.test.js.map","__tests__/thread-list-workflow-corrupt.test.d.ts","__tests__/thread-list-workflow-corrupt.test.d.ts.map","__tests__/thread-list-workflow-corrupt.test.js","__tests__/thread-list-workflow-corrupt.test.js.map","__tests__/thread-location.test.d.ts","__tests__/thread-location.test.d.ts.map","__tests__/thread-location.test.js","__tests__/thread-location.test.js.map","__tests__/thread-poke.test.d.ts","__tests__/thread-poke.test.d.ts.map","__tests__/thread-poke.test.js","__tests__/thread-poke.test.js.map","__tests__/thread-read-quota.test.d.ts","__tests__/thread-read-quota.test.d.ts.map","__tests__/thread-read-quota.test.js","__tests__/thread-read-quota.test.js.map","__tests__/thread-read-xml-tags.test.d.ts","__tests__/thread-read-xml-tags.test.d.ts.map","__tests__/thread-read-xml-tags.test.js","__tests__/thread-read-xml-tags.test.js.map","__tests__/thread-resume.test.d.ts","__tests__/thread-resume.test.d.ts.map","__tests__/thread-resume.test.js","__tests__/thread-resume.test.js.map","__tests__/thread-show-status.test.d.ts","__tests__/thread-show-status.test.d.ts.map","__tests__/thread-show-status.test.js","__tests__/thread-show-status.test.js.map","__tests__/thread-start-cwd-cli.test.d.ts","__tests__/thread-start-cwd-cli.test.d.ts.map","__tests__/thread-start-cwd-cli.test.js","__tests__/thread-start-cwd-cli.test.js.map","__tests__/thread-step-count.test.d.ts","__tests__/thread-step-count.test.d.ts.map","__tests__/thread-step-count.test.js","__tests__/thread-step-count.test.js.map","__tests__/thread-stop-text-renderer.test.d.ts","__tests__/thread-stop-text-renderer.test.d.ts.map","__tests__/thread-stop-text-renderer.test.js","__tests__/thread-stop-text-renderer.test.js.map","__tests__/thread-suspend-step.test.d.ts","__tests__/thread-suspend-step.test.d.ts.map","__tests__/thread-suspend-step.test.js","__tests__/thread-suspend-step.test.js.map","__tests__/thread-suspended-display.test.d.ts","__tests__/thread-suspended-display.test.d.ts.map","__tests__/thread-suspended-display.test.js","__tests__/thread-suspended-display.test.js.map","__tests__/thread-test-helpers.d.ts","__tests__/thread-test-helpers.d.ts.map","__tests__/thread-test-helpers.js","__tests__/thread-test-helpers.js.map","__tests__/thread.test.d.ts","__tests__/thread.test.d.ts.map","__tests__/thread.test.js","__tests__/thread.test.js.map","__tests__/validate-semantic.test.d.ts","__tests__/validate-semantic.test.d.ts.map","__tests__/validate-semantic.test.js","__tests__/validate-semantic.test.js.map","__tests__/workflow-list-recursive.test.d.ts","__tests__/workflow-list-recursive.test.d.ts.map","__tests__/workflow-list-recursive.test.js","__tests__/workflow-list-recursive.test.js.map","__tests__/workflow-paths.test.d.ts","__tests__/workflow-paths.test.d.ts.map","__tests__/workflow-paths.test.js","__tests__/workflow-paths.test.js.map","__tests__/workflow-resolution.test.d.ts","__tests__/workflow-resolution.test.d.ts.map","__tests__/workflow-resolution.test.js","__tests__/workflow-resolution.test.js.map","__tests__/workflow-show-resolution.test.d.ts","__tests__/workflow-show-resolution.test.d.ts.map","__tests__/workflow-show-resolution.test.js","__tests__/workflow-show-resolution.test.js.map","__tests__/workflow-validate.test.d.ts","__tests__/workflow-validate.test.d.ts.map","__tests__/workflow-validate.test.js","__tests__/workflow-validate.test.js.map","__tests__/write-envelope.test.d.ts","__tests__/write-envelope.test.d.ts.map","__tests__/write-envelope.test.js","__tests__/write-envelope.test.js.map","background/background.d.ts","background/background.d.ts.map","background/background.js","background/background.js.map","background/index.d.ts","background/index.d.ts.map","background/index.js","background/index.js.map","background/types.d.ts","background/types.d.ts.map","background/types.js","background/types.js.map","cli.d.ts","cli.d.ts.map","cli.js","cli.js.map","commands/broker-step.d.ts","commands/broker-step.d.ts.map","commands/broker-step.js","commands/broker-step.js.map","commands/config.d.ts","commands/config.d.ts.map","commands/config.js","commands/config.js.map","commands/log.d.ts","commands/log.d.ts.map","commands/log.js","commands/log.js.map","commands/prompt.d.ts","commands/prompt.d.ts.map","commands/prompt.js","commands/prompt.js.map","commands/setup.d.ts","commands/setup.d.ts.map","commands/setup.js","commands/setup.js.map","commands/shared.d.ts","commands/shared.d.ts.map","commands/shared.js","commands/shared.js.map","commands/step.d.ts","commands/step.d.ts.map","commands/step.js","commands/step.js.map","commands/thread-time-parser.d.ts","commands/thread-time-parser.d.ts.map","commands/thread-time-parser.js","commands/thread-time-parser.js.map","commands/thread.d.ts","commands/thread.d.ts.map","commands/thread.js","commands/thread.js.map","commands/workflow.d.ts","commands/workflow.d.ts.map","commands/workflow.js","commands/workflow.js.map","concurrency/concurrency.d.ts","concurrency/concurrency.d.ts.map","concurrency/concurrency.js","concurrency/concurrency.js.map","concurrency/index.d.ts","concurrency/index.d.ts.map","concurrency/index.js","concurrency/index.js.map","concurrency/types.d.ts","concurrency/types.d.ts.map","concurrency/types.js","concurrency/types.js.map","format.d.ts","format.d.ts.map","format.js","format.js.map","include.d.ts","include.d.ts.map","include.js","include.js.map","moderator/__tests__/evaluate.test.d.ts","moderator/__tests__/evaluate.test.d.ts.map","moderator/__tests__/evaluate.test.js","moderator/__tests__/evaluate.test.js.map","moderator/evaluate.d.ts","moderator/evaluate.d.ts.map","moderator/evaluate.js","moderator/evaluate.js.map","moderator/index.d.ts","moderator/index.d.ts.map","moderator/index.js","moderator/index.js.map","moderator/types.d.ts","moderator/types.d.ts.map","moderator/types.js","moderator/types.js.map","output-mappers.d.ts","output-mappers.d.ts.map","output-mappers.js","output-mappers.js.map","schemas.d.ts","schemas.d.ts.map","schemas.js","schemas.js.map","store.d.ts","store.d.ts.map","store.js","store.js.map","text-renderers.d.ts","text-renderers.d.ts.map","text-renderers.js","text-renderers.js.map","validate-semantic.d.ts","validate-semantic.d.ts.map","validate-semantic.js","validate-semantic.js.map","validate.d.ts","validate.d.ts.map","validate.js","validate.js.map"]}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Phase 2 (#398 + #419) — realtime turn persistence in `executeBrokerStep`.
3
+ *
4
+ * Updated for Phase 2 (#419):
5
+ * - Turns are now written with `prev`+`owner` chain instead of accumulating in
6
+ * `@uwf/active-turns/<tid>/<role>` array var
7
+ * - Detail node no longer contains `turns` array — use `turnsOfStep()` to retrieve
8
+ * - Thread-keyed active vars (`@uwf/active-step/<tid>`, `@uwf/active-turn-head/<tid>`)
9
+ *
10
+ * Covers the acceptance steps:
11
+ * Step 1 — broker-step's `onTurn` writes each turn with `prev`+`owner` chain;
12
+ * the turn chain grows 1→2→3.
13
+ * Step 2 — on completion, detail has `turnCount===3` (no `turns` array);
14
+ * turns are accessible via `turnsOfStep()`.
15
+ * Step 3 — a crash-rerun is a fresh attempt: new step-start isolates old turns
16
+ * via different `owner` reference.
17
+ * Step 4 — cross-process visibility: thread-keyed turn head var is observable.
18
+ */
19
+ export {};
20
+ //# sourceMappingURL=broker-step-active-turns.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"broker-step-active-turns.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/broker-step-active-turns.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG"}
@@ -0,0 +1,428 @@
1
+ /**
2
+ * Phase 2 (#398 + #419) — realtime turn persistence in `executeBrokerStep`.
3
+ *
4
+ * Updated for Phase 2 (#419):
5
+ * - Turns are now written with `prev`+`owner` chain instead of accumulating in
6
+ * `@uwf/active-turns/<tid>/<role>` array var
7
+ * - Detail node no longer contains `turns` array — use `turnsOfStep()` to retrieve
8
+ * - Thread-keyed active vars (`@uwf/active-step/<tid>`, `@uwf/active-turn-head/<tid>`)
9
+ *
10
+ * Covers the acceptance steps:
11
+ * Step 1 — broker-step's `onTurn` writes each turn with `prev`+`owner` chain;
12
+ * the turn chain grows 1→2→3.
13
+ * Step 2 — on completion, detail has `turnCount===3` (no `turns` array);
14
+ * turns are accessible via `turnsOfStep()`.
15
+ * Step 3 — a crash-rerun is a fresh attempt: new step-start isolates old turns
16
+ * via different `owner` reference.
17
+ * Step 4 — cross-process visibility: thread-keyed turn head var is observable.
18
+ */
19
+ import { mkdtemp, rm } from "node:fs/promises";
20
+ import { tmpdir } from "node:os";
21
+ import { join } from "node:path";
22
+ import { putSchema } from "@ocas/core";
23
+ import { createFsStore, createSqliteVarStore } from "@ocas/fs";
24
+ import { createProcessLogger } from "@united-workforce/util";
25
+ import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
26
+ import { executeBrokerStep } from "../commands/broker-step.js";
27
+ import { ACTIVE_TURNS_VAR_PREFIX, activeTurnsVarName, appendActiveTurn, createUwfStore, getActiveTurnHead, readActiveTurns, turnsOfStep, walkTurnChain, } from "../store.js";
28
+ // ── SSE plumbing ─────────────────────────────────────────────────────────────
29
+ function sseFrame(id, event, data) {
30
+ return `id: ${id}\nevent: ${event}\ndata: ${JSON.stringify(data)}\n\n`;
31
+ }
32
+ function turnFrame(id, index, content) {
33
+ return sseFrame(id, "turn", {
34
+ type: "@sumeru/turn",
35
+ value: { index, role: "assistant", content, timestamp: "", toolCalls: null },
36
+ });
37
+ }
38
+ function doneFrame(id, turnCount) {
39
+ return sseFrame(id, "done", {
40
+ type: "@sumeru/summary",
41
+ value: { turnCount, tokens: { in: 9, out: 4 }, durationMs: 42 },
42
+ });
43
+ }
44
+ function delay(ms) {
45
+ return new Promise((resolve) => setTimeout(resolve, ms));
46
+ }
47
+ /** How long each assistant turn is held back, so readers can sample between them. */
48
+ const PER_TURN_MS = 40;
49
+ /**
50
+ * Build a paced SSE Response: each frame is enqueued `PER_TURN_MS` after the
51
+ * previous, so the broker's reader loop fires `onTurn` one turn at a time and a
52
+ * concurrent reader can observe the active var growing. Robust to consumer
53
+ * cancellation (the broker cancels its reader in a `finally`), so a late
54
+ * `enqueue`/`close` after cancel is swallowed rather than surfacing as an
55
+ * unhandled rejection.
56
+ */
57
+ function buildPacedSseResponse(frames) {
58
+ const encoder = new TextEncoder();
59
+ let cancelled = false;
60
+ const stream = new ReadableStream({
61
+ start(controller) {
62
+ void (async () => {
63
+ try {
64
+ for (const frame of frames) {
65
+ if (cancelled)
66
+ return;
67
+ controller.enqueue(encoder.encode(frame));
68
+ await delay(PER_TURN_MS);
69
+ }
70
+ if (!cancelled)
71
+ controller.close();
72
+ }
73
+ catch {
74
+ // Consumer closed/cancelled the stream first — nothing to do.
75
+ }
76
+ })();
77
+ },
78
+ cancel() {
79
+ cancelled = true;
80
+ },
81
+ });
82
+ return new Response(stream, {
83
+ status: 200,
84
+ headers: { "Content-Type": "text/event-stream; charset=utf-8" },
85
+ });
86
+ }
87
+ function buildJsonResponse(status, body) {
88
+ return new Response(JSON.stringify(body), {
89
+ status,
90
+ headers: { "Content-Type": "application/json" },
91
+ });
92
+ }
93
+ // ── Fixture: role schema + workflow ──────────────────────────────────────────
94
+ const ROLE_OUTPUT_SCHEMA = {
95
+ title: "coder-output",
96
+ type: "object",
97
+ required: ["$status"],
98
+ properties: {
99
+ $status: { type: "string", enum: ["done", "failed"] },
100
+ summary: { type: "string" },
101
+ },
102
+ additionalProperties: false,
103
+ };
104
+ /**
105
+ * The final assistant turn carries valid frontmatter so extraction succeeds on
106
+ * the primary send (no retries → exactly one onTurn per emitted turn). Its
107
+ * stored content is this whole block (byte-for-byte), which is what the
108
+ * solidified `detail.turns[last]` holds.
109
+ */
110
+ const FINAL_TURN = `---
111
+ $status: done
112
+ summary: shipped
113
+ ---
114
+ the final answer`;
115
+ const HOST = "http://127.0.0.1:7900";
116
+ const GATEWAY = "coder-gw";
117
+ const ALIAS = "coder-agent";
118
+ const SESSION_ID = "ses_active_turns";
119
+ const THREAD_ID = "06FCACTIVETURNSPHASE2A001";
120
+ const ROLE = "coder";
121
+ function buildConfig() {
122
+ return {
123
+ agents: { [ALIAS]: { host: HOST, gateway: GATEWAY } },
124
+ defaultAgent: ALIAS,
125
+ agentOverrides: null,
126
+ };
127
+ }
128
+ async function buildWorkflow(uwf) {
129
+ const frontmatterHash = (await putSchema(uwf.store, ROLE_OUTPUT_SCHEMA));
130
+ const workflow = {
131
+ version: 1,
132
+ name: "active-turns-wf",
133
+ description: "phase2 realtime turns",
134
+ roles: {
135
+ [ROLE]: {
136
+ description: "writes code",
137
+ goal: "produce a change",
138
+ capabilities: [],
139
+ procedure: "do the work",
140
+ output: "frontmatter+body",
141
+ frontmatter: frontmatterHash,
142
+ },
143
+ },
144
+ graph: {
145
+ [ROLE]: {
146
+ done: { role: "$END", prompt: "", location: null },
147
+ },
148
+ },
149
+ };
150
+ const startHash = (await uwf.store.cas.put(uwf.schemas.startNode, {
151
+ workflow: await uwf.store.cas.put(uwf.schemas.workflow, workflow),
152
+ prompt: "task",
153
+ cwd: "/tmp/work",
154
+ }));
155
+ return { workflow, startHash };
156
+ }
157
+ function resolveFetchUrl(input) {
158
+ if (typeof input === "string")
159
+ return input;
160
+ if (input instanceof URL)
161
+ return input.href;
162
+ return input.url;
163
+ }
164
+ /** Drive one broker step end-to-end with the configured fixture. */
165
+ function runStep(uwf, workflow, startHash, tmpDir) {
166
+ return executeBrokerStep({
167
+ storageRoot: tmpDir,
168
+ uwf,
169
+ config: buildConfig(),
170
+ workflow,
171
+ threadId: THREAD_ID,
172
+ role: ROLE,
173
+ edgePrompt: "go",
174
+ effectiveCwd: "/tmp/work",
175
+ startHash,
176
+ prevHash: null,
177
+ agentOverride: null,
178
+ previousAttempts: null,
179
+ plog: createProcessLogger({
180
+ storageRoot: tmpDir,
181
+ context: { thread: THREAD_ID, workflow: "active-turns-wf" },
182
+ }),
183
+ });
184
+ }
185
+ /** Resolve the `content` of a turn node hash, or `null`. */
186
+ function turnContent(store, hash) {
187
+ const node = store.cas.get(hash);
188
+ if (node === null)
189
+ return null;
190
+ const payload = node.payload;
191
+ return typeof payload.content === "string" ? payload.content : null;
192
+ }
193
+ /**
194
+ * An *independent* reader of the shared on-disk store — a fresh CAS (its own
195
+ * hash-set scan) and a fresh SQLite connection — simulating "process B" reading
196
+ * the WAL-committed active var while "process A" runs the step.
197
+ */
198
+ function openReader(casDir) {
199
+ const cas = createFsStore(casDir);
200
+ const { var: varStore, tag, close } = createSqliteVarStore(join(casDir, "vars"), cas);
201
+ return { store: { cas, var: varStore, tag }, close };
202
+ }
203
+ // ── Tests ────────────────────────────────────────────────────────────────────
204
+ describe("active-turns realtime persistence (#398)", () => {
205
+ let tmpDir;
206
+ let casDir;
207
+ let savedOcasHome;
208
+ let messageBodies;
209
+ beforeEach(async () => {
210
+ savedOcasHome = process.env.OCAS_HOME;
211
+ tmpDir = await mkdtemp(join(tmpdir(), "active-turns-"));
212
+ casDir = join(tmpDir, "cas");
213
+ process.env.OCAS_HOME = casDir;
214
+ messageBodies = [];
215
+ vi.stubGlobal("fetch", async (input, init) => {
216
+ const url = resolveFetchUrl(input);
217
+ if (url.endsWith(`/gateways/${GATEWAY}/sessions`)) {
218
+ return buildJsonResponse(201, {
219
+ type: "@sumeru/session",
220
+ value: { id: SESSION_ID, gateway: GATEWAY },
221
+ });
222
+ }
223
+ if (url.endsWith(`/sessions/${SESSION_ID}/messages`)) {
224
+ messageBodies.push(typeof init?.body === "string" ? init.body : "");
225
+ return buildPacedSseResponse([
226
+ turnFrame(1, 0, "t1"),
227
+ turnFrame(2, 1, "t2"),
228
+ turnFrame(3, 2, FINAL_TURN),
229
+ doneFrame(4, 3),
230
+ ]);
231
+ }
232
+ return buildJsonResponse(500, { error: "unexpected url", url });
233
+ });
234
+ });
235
+ afterEach(async () => {
236
+ vi.unstubAllGlobals();
237
+ if (savedOcasHome === undefined)
238
+ delete process.env.OCAS_HOME;
239
+ else
240
+ process.env.OCAS_HOME = savedOcasHome;
241
+ await rm(tmpDir, { recursive: true, force: true });
242
+ });
243
+ // Step 1 — the turn chain grows 1 → 2 → 3 as onTurn fires (Phase 2: via turn chain, not role-keyed var).
244
+ test("turn chain grows 1 -> 2 -> 3 via prev+owner chain as turns stream", async () => {
245
+ const uwf = await createUwfStore(tmpDir);
246
+ const { workflow, startHash } = await buildWorkflow(uwf);
247
+ const p = runStep(uwf, workflow, startHash, tmpDir);
248
+ // Sample the turn chain head while the step is in flight.
249
+ const chainLengths = [];
250
+ let finished = false;
251
+ void p.finally(() => {
252
+ finished = true;
253
+ });
254
+ while (!finished) {
255
+ const head = getActiveTurnHead(uwf.store, THREAD_ID);
256
+ const len = head !== null ? walkTurnChain(uwf, head).length : 0;
257
+ chainLengths.push(len);
258
+ await delay(PER_TURN_MS / 4);
259
+ }
260
+ const result = await p;
261
+ // Monotonic growth: first occurrences of 1, 2, 3 appear in order.
262
+ const firstIndexOf = (n) => chainLengths.indexOf(n);
263
+ expect(firstIndexOf(1)).toBeGreaterThanOrEqual(0);
264
+ expect(firstIndexOf(2)).toBeGreaterThan(firstIndexOf(1));
265
+ expect(firstIndexOf(3)).toBeGreaterThan(firstIndexOf(2));
266
+ // The chain never shrinks while accumulating.
267
+ const beforeFinal = chainLengths.slice(0, chainLengths.indexOf(3) + 1);
268
+ for (let i = 1; i < beforeFinal.length; i++) {
269
+ expect(beforeFinal[i]).toBeGreaterThanOrEqual(beforeFinal[i - 1]);
270
+ }
271
+ // Verify turns via turn chain (Phase 2: no detail.turns array)
272
+ expect(result.isError).toBe(false);
273
+ const turnHead = getActiveTurnHead(uwf.store, THREAD_ID);
274
+ expect(turnHead).not.toBeNull();
275
+ const chain = walkTurnChain(uwf, turnHead);
276
+ expect(chain).toHaveLength(3);
277
+ const contents = chain.map((h) => turnContent(uwf.store, h));
278
+ expect(contents).toEqual(["t1", "t2", FINAL_TURN]);
279
+ for (const h of chain) {
280
+ const node = uwf.store.cas.get(h);
281
+ expect((node?.payload).role).toBe("assistant");
282
+ }
283
+ });
284
+ // Step 2 — completion: detail has turnCount===3 (no turns array); turns via turnsOfStep.
285
+ test("detail has turnCount===3 (no turns array); turns accessible via turnsOfStep", async () => {
286
+ const uwf = await createUwfStore(tmpDir);
287
+ const { workflow, startHash } = await buildWorkflow(uwf);
288
+ const result = await runStep(uwf, workflow, startHash, tmpDir);
289
+ expect(result.isError).toBe(false);
290
+ const detail = uwf.store.cas.get(result.detailHash)?.payload;
291
+ expect(detail.turnCount).toBe(3);
292
+ expect(detail.turns).toBeUndefined(); // Phase 2: no turns array
293
+ expect(detail.sessionId).toBe(SESSION_ID);
294
+ expect(detail.duration).toBeGreaterThanOrEqual(0);
295
+ // Get turns via turn chain
296
+ const turnHead = getActiveTurnHead(uwf.store, THREAD_ID);
297
+ expect(turnHead).not.toBeNull();
298
+ // Get the step-start owner from first turn
299
+ const firstTurn = uwf.store.cas.get(walkTurnChain(uwf, turnHead)[0])
300
+ ?.payload;
301
+ const stepStartHash = firstTurn.owner;
302
+ const stepTurns = turnsOfStep(uwf, turnHead, stepStartHash);
303
+ expect(stepTurns).toHaveLength(3);
304
+ expect(stepTurns.map((h) => turnContent(uwf.store, h))).toEqual(["t1", "t2", FINAL_TURN]);
305
+ // Role-keyed var is cleared (backward compat)
306
+ const vars = uwf.varStore.list({ exactName: activeTurnsVarName(THREAD_ID, ROLE) });
307
+ expect(vars).toEqual([]);
308
+ // Backward-compat: frontmatter extraction still works
309
+ expect(result.frontmatter).toEqual({ $status: "done", summary: "shipped" });
310
+ });
311
+ // Step 3 — crash-rerun: new step-start isolates old turns via different owner.
312
+ test("crash-rerun: new step-start isolates old turns via different owner", async () => {
313
+ const uwf = await createUwfStore(tmpDir);
314
+ const { workflow, startHash } = await buildWorkflow(uwf);
315
+ // Seed a residual active var (two stale turns) from a "crashed" prior attempt.
316
+ // Phase 2: these will be ignored because the new step gets a new step-start
317
+ // with different owner hash.
318
+ const turnSchemaHash = putSchema(uwf.store, {
319
+ title: "broker-turn",
320
+ type: "object",
321
+ required: ["role", "content"],
322
+ properties: {
323
+ role: { type: "string", enum: ["assistant", "tool"] },
324
+ content: { type: "string" },
325
+ },
326
+ additionalProperties: false,
327
+ });
328
+ for (const stale of ["old1", "old2"]) {
329
+ const h = uwf.store.cas.put(turnSchemaHash, { role: "assistant", content: stale });
330
+ appendActiveTurn(uwf.store, THREAD_ID, ROLE, h);
331
+ }
332
+ expect(readActiveTurns(uwf.store, THREAD_ID, ROLE)).toHaveLength(2);
333
+ const result = await runStep(uwf, workflow, startHash, tmpDir);
334
+ expect(result.isError).toBe(false);
335
+ const detail = uwf.store.cas.get(result.detailHash)?.payload;
336
+ // Only the new attempt's 3 turns counted
337
+ expect(detail.turnCount).toBe(3);
338
+ // Get the new step's turns via turnsOfStep
339
+ const turnHead = getActiveTurnHead(uwf.store, THREAD_ID);
340
+ expect(turnHead).not.toBeNull();
341
+ // Get the step-start owner from the first new turn (will be the new step-start)
342
+ const chain = walkTurnChain(uwf, turnHead);
343
+ // The chain includes ALL turns (old + new), but filtered by owner gives only new ones
344
+ expect(chain.length).toBeGreaterThanOrEqual(3);
345
+ // Find the new step-start (the one that owns the turns with content "t1", "t2", etc.)
346
+ const newTurns = chain.filter((h) => {
347
+ const c = turnContent(uwf.store, h);
348
+ return c === "t1" || c === "t2" || c === FINAL_TURN;
349
+ });
350
+ expect(newTurns).toHaveLength(3);
351
+ const newStepOwner = (uwf.store.cas.get(newTurns[0])?.payload).owner;
352
+ const filteredTurns = turnsOfStep(uwf, turnHead, newStepOwner);
353
+ expect(filteredTurns).toHaveLength(3);
354
+ const contents = filteredTurns.map((h) => turnContent(uwf.store, h));
355
+ expect(contents).toEqual(["t1", "t2", FINAL_TURN]);
356
+ expect(contents).not.toContain("old1");
357
+ expect(contents).not.toContain("old2");
358
+ // Role-keyed active var deleted after step.
359
+ expect(uwf.varStore.list({ exactName: activeTurnsVarName(THREAD_ID, ROLE) })).toEqual([]);
360
+ });
361
+ // Step 4 — cross-process visibility via the thread-keyed turn head var.
362
+ test("an independent reader sees the growing turn chain mid-flight", async () => {
363
+ const uwf = await createUwfStore(tmpDir);
364
+ const { workflow, startHash } = await buildWorkflow(uwf);
365
+ const p = runStep(uwf, workflow, startHash, tmpDir);
366
+ // Sample turn chain from independent reader while step runs
367
+ const { counts, contents } = await sampleTurnChainWhileRunning(casDir, uwf, tmpDir, p);
368
+ // Progress was visible before completion: a non-empty, growing chain.
369
+ const maxObserved = Math.max(...counts);
370
+ expect(maxObserved).toBeGreaterThanOrEqual(2);
371
+ expect(counts.some((n) => n > 0 && n < 3)).toBe(true);
372
+ expect(contents.has("t1")).toBe(true);
373
+ // After completion the role-keyed var is gone
374
+ const after = openReader(casDir);
375
+ try {
376
+ expect(after.store.var.list({ namePrefix: `${ACTIVE_TURNS_VAR_PREFIX}${THREAD_ID}/` })).toEqual([]);
377
+ }
378
+ finally {
379
+ after.close();
380
+ }
381
+ // Thread-keyed turn head var still points to the chain
382
+ const finalHead = getActiveTurnHead(uwf.store, THREAD_ID);
383
+ expect(finalHead).not.toBeNull();
384
+ expect(walkTurnChain(uwf, finalHead)).toHaveLength(3);
385
+ });
386
+ });
387
+ /**
388
+ * Sample the turn chain from an independent reader while a step runs.
389
+ * Returns observed chain lengths and contents.
390
+ */
391
+ async function sampleTurnChainWhileRunning(casDir, uwf, tmpDir, stepPromise) {
392
+ const counts = [];
393
+ const contents = new Set();
394
+ let finished = false;
395
+ void stepPromise.finally(() => {
396
+ finished = true;
397
+ });
398
+ while (!finished) {
399
+ const reader = openReader(casDir);
400
+ try {
401
+ const head = getActiveTurnHead(reader.store, THREAD_ID);
402
+ if (head !== null) {
403
+ const chain = walkTurnChain({
404
+ store: reader.store,
405
+ schemas: uwf.schemas,
406
+ storageRoot: tmpDir,
407
+ varStore: uwf.varStore,
408
+ }, head);
409
+ counts.push(chain.length);
410
+ for (const h of chain) {
411
+ const c = turnContent(reader.store, h);
412
+ if (c !== null)
413
+ contents.add(c);
414
+ }
415
+ }
416
+ else {
417
+ counts.push(0);
418
+ }
419
+ }
420
+ finally {
421
+ reader.close();
422
+ }
423
+ await delay(PER_TURN_MS / 3);
424
+ }
425
+ await stepPromise;
426
+ return { counts, contents, finished };
427
+ }
428
+ //# sourceMappingURL=broker-step-active-turns.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"broker-step-active-turns.test.js","sourceRoot":"","sources":["../../src/__tests__/broker-step-active-turns.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Phase 2 (#419) — Turn chain with prev+owner fields and thread-keyed active vars.
3
+ *
4
+ * Covers the spec acceptance scenarios:
5
+ * 1. onTurn writes each turn with prev pointer and owner reference
6
+ * 2. Step-start/step-complete dual node lifecycle
7
+ * 3. Same role multi-round ownership (#412 regression test)
8
+ * 4. Thread-keyed active vars (not role-keyed)
9
+ * 5. Crash recovery isolation (new attempt gets new step-start)
10
+ * 6. Detail node has no turns array (turns self-contained via chain)
11
+ */
12
+ export {};
13
+ //# sourceMappingURL=broker-step-turn-chain-phase2.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"broker-step-turn-chain-phase2.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/broker-step-turn-chain-phase2.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG"}