@dtlabs/ai-toolkit 0.8.0 → 0.9.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.
@@ -13,18 +13,30 @@ export const meta = {
13
13
 
14
14
  async function appendLedgerEntry(featureDir, prefix, entry) {
15
15
  const ledgerPath = `${featureDir}/${prefix}-token-ledger.json`
16
- const entryJson = JSON.stringify(entry)
16
+ // started_at is generated via Bash inside the agent to get a real UTC timestamp with time
17
+ const entryWithoutTs = JSON.stringify({ ...entry, started_at: '__TS__', completed_at: null })
17
18
  await agent(
18
- `Append a JSON object to the ledger array at: ${ledgerPath}\n\n1. Read the file. If it does not exist or cannot be parsed as a JSON array, start with [].\n2. Push this object onto the array: ${entryJson}\n3. Write the full array back (JSON, 2-space indent). Return no output.`,
19
+ `Append a JSON object to the ledger array at: ${ledgerPath}\n\n` +
20
+ `1. Run: date -u +"%Y-%m-%dT%H:%M:%SZ" and capture the output as NOW.\n` +
21
+ `2. Read the file. If it does not exist or cannot be parsed as a JSON array, start with [].\n` +
22
+ `3. Push this object onto the array, replacing "__TS__" in started_at with NOW: ${entryWithoutTs}\n` +
23
+ `4. Write the full array back (JSON, 2-space indent). Return no output.`,
19
24
  { label: 'append-ledger', phase: 'Requirements', model: 'haiku' }
20
25
  )
21
26
  }
22
27
 
23
28
  async function updateLedgerEntry(featureDir, prefix, agentKey, updates) {
24
29
  const ledgerPath = `${featureDir}/${prefix}-token-ledger.json`
25
- const updatesJson = JSON.stringify(updates)
30
+ // completed_at is generated via Bash inside the agent to get a real UTC timestamp with time
31
+ const updatesWithoutTs = JSON.stringify({ ...updates, completed_at: '__TS__' })
26
32
  await agent(
27
- `Update an entry in the ledger array at: ${ledgerPath}\n\n1. Read the file. If it does not exist or cannot be parsed as a JSON array, do nothing.\n2. Search from the end for the last entry where agent === "${agentKey}".\n3. If found, merge these fields into that entry: ${updatesJson}\n4. Write the full array back (JSON, 2-space indent). Return no output.\n5. If not found, do nothing.`,
33
+ `Update an entry in the ledger array at: ${ledgerPath}\n\n` +
34
+ `1. Run: date -u +"%Y-%m-%dT%H:%M:%SZ" and capture the output as NOW.\n` +
35
+ `2. Read the file. If it does not exist or cannot be parsed as a JSON array, do nothing.\n` +
36
+ `3. Search from the end for the last entry where agent === "${agentKey}".\n` +
37
+ `4. If found, merge these fields into that entry, replacing "__TS__" in completed_at with NOW: ${updatesWithoutTs}\n` +
38
+ `5. Write the full array back (JSON, 2-space indent). Return no output.\n` +
39
+ `6. If not found, do nothing.`,
28
40
  { label: 'update-ledger', phase: 'Requirements', model: 'haiku' }
29
41
  )
30
42
  }
@@ -108,14 +120,13 @@ const errors = []
108
120
 
109
121
  if (discoveryResult.needs_requirements) {
110
122
  const reqKey = 'generate-requirements:phase1'
111
- const reqStartedAt = new Date().toISOString()
112
123
  await appendLedgerEntry(feature_dir, prefix, {
113
124
  agent: reqKey,
114
125
  phase: 'phase1',
115
126
  model: 'haiku',
116
127
  status: 'running',
117
128
  phase_delta_tokens: 0,
118
- started_at: reqStartedAt,
129
+ started_at: '__TS__',
119
130
  completed_at: null,
120
131
  })
121
132
  const beforeReq = budget.spent()
@@ -123,7 +134,7 @@ if (discoveryResult.needs_requirements) {
123
134
  const reqTokens = budget.spent() - beforeReq
124
135
  await updateLedgerEntry(feature_dir, prefix, reqKey, {
125
136
  status: 'done',
126
- completed_at: new Date().toISOString(),
137
+ completed_at: '__TS__',
127
138
  phase_delta_tokens: reqTokens,
128
139
  })
129
140
  tokenLedger.push({ agent: 'generate-requirements', model: 'haiku', phase_delta_tokens: reqTokens })
@@ -137,14 +148,13 @@ phase('Tech-Spec')
137
148
 
138
149
  if (discoveryResult.needs_tech_spec) {
139
150
  const specKey = 'generate-tech-spec:phase1'
140
- const specStartedAt = new Date().toISOString()
141
151
  await appendLedgerEntry(feature_dir, prefix, {
142
152
  agent: specKey,
143
153
  phase: 'phase1',
144
154
  model: 'haiku',
145
155
  status: 'running',
146
156
  phase_delta_tokens: 0,
147
- started_at: specStartedAt,
157
+ started_at: '__TS__',
148
158
  completed_at: null,
149
159
  })
150
160
  const beforeSpec = budget.spent()
@@ -152,7 +162,7 @@ if (discoveryResult.needs_tech_spec) {
152
162
  const specTokens = budget.spent() - beforeSpec
153
163
  await updateLedgerEntry(feature_dir, prefix, specKey, {
154
164
  status: 'done',
155
- completed_at: new Date().toISOString(),
165
+ completed_at: '__TS__',
156
166
  phase_delta_tokens: specTokens,
157
167
  })
158
168
  tokenLedger.push({ agent: 'generate-tech-spec', model: 'haiku', phase_delta_tokens: specTokens })
@@ -171,14 +181,13 @@ const MAX_CYCLES = 3
171
181
  for (let cycle = 1; cycle <= MAX_CYCLES; cycle++) {
172
182
  log(`validate-feature-docs cycle ${cycle}`)
173
183
  const valKey = `validate-feature-docs:phase1:cycle${cycle}`
174
- const valStartedAt = new Date().toISOString()
175
184
  await appendLedgerEntry(feature_dir, prefix, {
176
185
  agent: valKey,
177
186
  phase: 'phase1',
178
187
  model: 'haiku',
179
188
  status: 'running',
180
189
  phase_delta_tokens: 0,
181
- started_at: valStartedAt,
190
+ started_at: '__TS__',
182
191
  completed_at: null,
183
192
  })
184
193
  const beforeVal = budget.spent()
@@ -186,7 +195,7 @@ for (let cycle = 1; cycle <= MAX_CYCLES; cycle++) {
186
195
  const valTokens = budget.spent() - beforeVal
187
196
  await updateLedgerEntry(feature_dir, prefix, valKey, {
188
197
  status: 'done',
189
- completed_at: new Date().toISOString(),
198
+ completed_at: '__TS__',
190
199
  phase_delta_tokens: valTokens,
191
200
  })
192
201
  tokenLedger.push({ agent: `validate-feature-docs (cycle ${cycle})`, model: 'haiku', phase_delta_tokens: valTokens })
@@ -13,18 +13,30 @@ export const meta = {
13
13
 
14
14
  async function appendLedgerEntry(featureDir, prefix, entry) {
15
15
  const ledgerPath = `${featureDir}/${prefix}-token-ledger.json`
16
- const entryJson = JSON.stringify(entry)
16
+ // started_at is generated via Bash inside the agent to get a real UTC timestamp with time
17
+ const entryWithoutTs = JSON.stringify({ ...entry, started_at: '__TS__', completed_at: null })
17
18
  await agent(
18
- `Append a JSON object to the ledger array at: ${ledgerPath}\n\n1. Read the file. If it does not exist or cannot be parsed as a JSON array, start with [].\n2. Push this object onto the array: ${entryJson}\n3. Write the full array back (JSON, 2-space indent). Return no output.`,
19
+ `Append a JSON object to the ledger array at: ${ledgerPath}\n\n` +
20
+ `1. Run: date -u +"%Y-%m-%dT%H:%M:%SZ" and capture the output as NOW.\n` +
21
+ `2. Read the file. If it does not exist or cannot be parsed as a JSON array, start with [].\n` +
22
+ `3. Push this object onto the array, replacing "__TS__" in started_at with NOW: ${entryWithoutTs}\n` +
23
+ `4. Write the full array back (JSON, 2-space indent). Return no output.`,
19
24
  { label: 'append-ledger', phase: 'Work Breakdown', model: 'haiku' }
20
25
  )
21
26
  }
22
27
 
23
28
  async function updateLedgerEntry(featureDir, prefix, agentKey, updates) {
24
29
  const ledgerPath = `${featureDir}/${prefix}-token-ledger.json`
25
- const updatesJson = JSON.stringify(updates)
30
+ // completed_at is generated via Bash inside the agent to get a real UTC timestamp with time
31
+ const updatesWithoutTs = JSON.stringify({ ...updates, completed_at: '__TS__' })
26
32
  await agent(
27
- `Update an entry in the ledger array at: ${ledgerPath}\n\n1. Read the file. If it does not exist or cannot be parsed as a JSON array, do nothing.\n2. Search from the end for the last entry where agent === "${agentKey}".\n3. If found, merge these fields into that entry: ${updatesJson}\n4. Write the full array back (JSON, 2-space indent). Return no output.\n5. If not found, do nothing.`,
33
+ `Update an entry in the ledger array at: ${ledgerPath}\n\n` +
34
+ `1. Run: date -u +"%Y-%m-%dT%H:%M:%SZ" and capture the output as NOW.\n` +
35
+ `2. Read the file. If it does not exist or cannot be parsed as a JSON array, do nothing.\n` +
36
+ `3. Search from the end for the last entry where agent === "${agentKey}".\n` +
37
+ `4. If found, merge these fields into that entry, replacing "__TS__" in completed_at with NOW: ${updatesWithoutTs}\n` +
38
+ `5. Write the full array back (JSON, 2-space indent). Return no output.\n` +
39
+ `6. If not found, do nothing.`,
28
40
  { label: 'update-ledger', phase: 'Work Breakdown', model: 'haiku' }
29
41
  )
30
42
  }
@@ -45,14 +57,13 @@ phase('Work Breakdown')
45
57
  const tokenLedger = []
46
58
 
47
59
  log(`Running generate-work-breakdown for ${featurePath}`)
48
- const wbStartedAt = new Date().toISOString()
49
60
  await appendLedgerEntry(featureDir, prefix, {
50
61
  agent: 'generate-work-breakdown:phase2',
51
62
  phase: 'phase2',
52
63
  model: 'haiku',
53
64
  status: 'running',
54
65
  phase_delta_tokens: 0,
55
- started_at: wbStartedAt,
66
+ started_at: '__TS__',
56
67
  completed_at: null,
57
68
  })
58
69
  const beforeWB = budget.spent()
@@ -64,7 +75,7 @@ await agent(featurePath, {
64
75
  const wbTokens = budget.spent() - beforeWB
65
76
  await updateLedgerEntry(featureDir, prefix, 'generate-work-breakdown:phase2', {
66
77
  status: 'done',
67
- completed_at: new Date().toISOString(),
78
+ completed_at: '__TS__',
68
79
  phase_delta_tokens: wbTokens,
69
80
  })
70
81
  tokenLedger.push({ agent: 'generate-work-breakdown', model: 'haiku', phase_delta_tokens: wbTokens })
@@ -11,6 +11,40 @@ export const meta = {
11
11
  ],
12
12
  }
13
13
 
14
+ // ── Ledger helper functions ───────────────────────────────────────────────────
15
+ // Route all ledger I/O through agent() — fs and require() are not available in
16
+ // the Workflow runtime. Mirrors the pattern used by pm-phase2.js.
17
+
18
+ async function appendLedgerEntry(featureDir, prefix, entry) {
19
+ const ledgerPath = `${featureDir}/${prefix}-token-ledger.json`
20
+ const entryWithoutTs = JSON.stringify({ ...entry, started_at: '__TS__', completed_at: null })
21
+ await agent(
22
+ `Append a JSON object to the ledger array at: ${ledgerPath}\n\n` +
23
+ `1. Run: date -u +"%Y-%m-%dT%H:%M:%SZ" and capture the output as NOW.\n` +
24
+ `2. Read the file. If it does not exist or cannot be parsed as a JSON array, start with [].\n` +
25
+ `3. Push this object onto the array, replacing "__TS__" in started_at with NOW: ${entryWithoutTs}\n` +
26
+ `4. Write the full array back (JSON, 2-space indent). Return no output.`,
27
+ { label: 'append-ledger', phase: 'Implementation', model: 'haiku' }
28
+ )
29
+ }
30
+
31
+ async function updateLedgerEntry(featureDir, prefix, agentKey, updates) {
32
+ const ledgerPath = `${featureDir}/${prefix}-token-ledger.json`
33
+ const updatesWithoutTs = JSON.stringify({ ...updates, completed_at: '__TS__' })
34
+ await agent(
35
+ `Update an entry in the ledger array at: ${ledgerPath}\n\n` +
36
+ `1. Run: date -u +"%Y-%m-%dT%H:%M:%SZ" and capture the output as NOW.\n` +
37
+ `2. Read the file. If it does not exist or cannot be parsed as a JSON array, do nothing.\n` +
38
+ `3. Search from the end for the last entry where agent === "${agentKey}".\n` +
39
+ `4. If found, merge these fields into that entry, replacing "__TS__" in completed_at with NOW: ${updatesWithoutTs}\n` +
40
+ `5. Write the full array back (JSON, 2-space indent). Return no output.\n` +
41
+ `6. If not found, do nothing.`,
42
+ { label: 'update-ledger', phase: 'Implementation', model: 'haiku' }
43
+ )
44
+ }
45
+
46
+ // ── Parse args ────────────────────────────────────────────────────────────────
47
+
14
48
  // args: "<path-to-feature.md> --branch feature/FTR-NNN-slug"
15
49
  const argStr = typeof args === 'string' ? args : ''
16
50
  const featurePath = argStr.trim().split(/\s+/)[0]
@@ -31,10 +65,13 @@ const csvPath = `${featureDir}/${prefix}-Work-Breakdown.csv`
31
65
 
32
66
  log(`Reading CSV: ${csvPath}`)
33
67
 
68
+ await appendLedgerEntry(featureDir, prefix, { agent: 'read-wb-csv:phase3', phase: 'phase3', model: 'haiku', status: 'running', phase_delta_tokens: 0, started_at: '__TS__', completed_at: null })
69
+ const beforeCsv = budget.spent()
34
70
  const csvContent = await agent(
35
71
  `Read the file at path: ${csvPath}\nReturn ONLY the raw file contents, nothing else. No explanation, no formatting, no JSON wrapping — just the raw text of the file.`,
36
72
  { label: 'read-wb-csv', phase: 'Parse', model: 'haiku' }
37
73
  )
74
+ await updateLedgerEntry(featureDir, prefix, 'read-wb-csv:phase3', { status: 'done', completed_at: '__TS__', phase_delta_tokens: budget.spent() - beforeCsv })
38
75
 
39
76
  // Parse CSV into structured phases — pure JS, no AI
40
77
  const rows = csvContent
@@ -148,6 +185,8 @@ const executePhase = async (implPhase) => {
148
185
 
149
186
  // Step 1 — impl groups in parallel (INFRA / BE / FE)
150
187
  if (implPhase.impl_groups.length > 0) {
188
+ const implKey = `${implPhase.impl_groups.map(g => g.agent_type).join('+')}:${implPhase.phase_id}${reworkCycle > 0 ? ':rework' + reworkCycle : ''}`
189
+ await appendLedgerEntry(featureDir, prefix, { agent: implKey, phase: 'phase3', model: 'sonnet', status: 'running', phase_delta_tokens: 0, started_at: '__TS__', completed_at: null })
151
190
  const beforeImpl = budget.spent()
152
191
  await parallel(implPhase.impl_groups.map(group => () =>
153
192
  agent(
@@ -160,16 +199,15 @@ const executePhase = async (implPhase) => {
160
199
  )
161
200
  ))
162
201
  const implTokens = budget.spent() - beforeImpl
163
- tokenLedger.push({
164
- agent: `${implPhase.impl_groups.map(g => g.agent_type).join('+')}:${implPhase.phase_id}${reworkCycle > 0 ? ':rework' + reworkCycle : ''}`,
165
- model: 'sonnet',
166
- phase_delta_tokens: implTokens,
167
- })
202
+ await updateLedgerEntry(featureDir, prefix, implKey, { status: 'done', completed_at: '__TS__', phase_delta_tokens: implTokens })
203
+ tokenLedger.push({ agent: implKey, model: 'sonnet', phase_delta_tokens: implTokens })
168
204
  log(`Phase ${implPhase.phase_id} impl groups done — ${implTokens} tokens`)
169
205
  }
170
206
 
171
207
  // Step 2 — test groups in parallel (TEST) — after impl groups
172
208
  if (implPhase.test_groups.length > 0) {
209
+ const testKey = `developer-testing:${implPhase.phase_id}${reworkCycle > 0 ? ':rework' + reworkCycle : ''}`
210
+ await appendLedgerEntry(featureDir, prefix, { agent: testKey, phase: 'phase3', model: 'sonnet', status: 'running', phase_delta_tokens: 0, started_at: '__TS__', completed_at: null })
173
211
  const beforeTest = budget.spent()
174
212
  await parallel(implPhase.test_groups.map(group => () =>
175
213
  agent(
@@ -182,15 +220,14 @@ const executePhase = async (implPhase) => {
182
220
  )
183
221
  ))
184
222
  const testTokens = budget.spent() - beforeTest
185
- tokenLedger.push({
186
- agent: `developer-testing:${implPhase.phase_id}${reworkCycle > 0 ? ':rework' + reworkCycle : ''}`,
187
- model: 'sonnet',
188
- phase_delta_tokens: testTokens,
189
- })
223
+ await updateLedgerEntry(featureDir, prefix, testKey, { status: 'done', completed_at: '__TS__', phase_delta_tokens: testTokens })
224
+ tokenLedger.push({ agent: testKey, model: 'sonnet', phase_delta_tokens: testTokens })
190
225
  log(`Phase ${implPhase.phase_id} test groups done — ${testTokens} tokens`)
191
226
  }
192
227
 
193
228
  // Step 3 — review-solution
229
+ const reviewKey = `review-solution:${implPhase.phase_id}`
230
+ await appendLedgerEntry(featureDir, prefix, { agent: reviewKey, phase: 'phase3', model: 'sonnet', status: 'running', phase_delta_tokens: 0, started_at: '__TS__', completed_at: null })
194
231
  const beforeReview = budget.spent()
195
232
  const review = await agent(
196
233
  `${featurePath} --scope ${implPhase.phase_id}`,
@@ -202,8 +239,9 @@ const executePhase = async (implPhase) => {
202
239
  }
203
240
  )
204
241
  const reviewTokens = budget.spent() - beforeReview
242
+ await updateLedgerEntry(featureDir, prefix, reviewKey, { status: 'done', completed_at: '__TS__', phase_delta_tokens: reviewTokens })
205
243
  tokenLedger.push({
206
- agent: `review-solution:${implPhase.phase_id}`,
244
+ agent: reviewKey,
207
245
  model: 'sonnet',
208
246
  phase_delta_tokens: reviewTokens,
209
247
  })
@@ -238,16 +276,6 @@ const executePhase = async (implPhase) => {
238
276
  }
239
277
 
240
278
  phasesDone++
241
-
242
- // Persist the cumulative token ledger to disk after every completed phase.
243
- // budget.spent() is an in-memory counter — if the workflow is stopped (manual
244
- // stop, crash, timeout) before the Actuals phase, all token data would be lost.
245
- // Writing the full ledger here means the file always reflects real consumption
246
- // up to the last completed phase, recoverable manually or by a resumed run.
247
- await agent(
248
- `Write the following JSON to ${featureDir}/${prefix}-token-ledger.json (overwrite the file if it exists):\n${JSON.stringify(tokenLedger, null, 2)}`,
249
- { label: `persist-ledger:${implPhase.phase_id}`, phase: 'Implementation', model: 'haiku' }
250
- )
251
279
  }
252
280
 
253
281
  // Execute waves: phases in the same wave run in parallel, waves are sequential
@@ -285,6 +313,7 @@ log(`Implementation complete — ${phasesDone} phases, ${usPassed.length} US pas
285
313
  // contention down to a single centralized run with one consolidated failure report.
286
314
  phase('Test')
287
315
 
316
+ await appendLedgerEntry(featureDir, prefix, { agent: 'final-test-run', phase: 'phase3', model: 'haiku', status: 'running', phase_delta_tokens: 0, started_at: '__TS__', completed_at: null })
288
317
  const beforeTest = budget.spent()
289
318
  await agent(
290
319
  `Run the full test suite in the repository root and report the result.
@@ -297,6 +326,7 @@ Do NOT fix failing tests — only report them.`,
297
326
  { label: 'final-test-run', phase: 'Test', model: 'haiku' }
298
327
  )
299
328
  const testTokens = budget.spent() - beforeTest
329
+ await updateLedgerEntry(featureDir, prefix, 'final-test-run', { status: 'done', completed_at: '__TS__', phase_delta_tokens: testTokens })
300
330
  tokenLedger.push({ agent: 'final-test-run', model: 'haiku', phase_delta_tokens: testTokens })
301
331
  log(`Test run complete — ${testTokens} tokens`)
302
332
 
@@ -317,6 +347,7 @@ let issuesDeferred = 0
317
347
 
318
348
  if (issuesOpen > 0 && issuesPath) {
319
349
  log(`Remediation: ${issuesOpen} OPEN issues`)
350
+ await appendLedgerEntry(featureDir, prefix, { agent: 'remediation', phase: 'phase3', model: 'sonnet', status: 'running', phase_delta_tokens: 0, started_at: '__TS__', completed_at: null })
320
351
  const beforeRem = budget.spent()
321
352
 
322
353
  const remResult = await agent(
@@ -339,6 +370,7 @@ Update the Issues Register Status column in place for each resolved/deferred iss
339
370
  )
340
371
 
341
372
  const remTokens = budget.spent() - beforeRem
373
+ await updateLedgerEntry(featureDir, prefix, 'remediation', { status: 'done', completed_at: '__TS__', phase_delta_tokens: remTokens })
342
374
  tokenLedger.push({ agent: 'remediation', model: 'sonnet', phase_delta_tokens: remTokens })
343
375
  issuesFixed = remResult.issues_fixed || 0
344
376
  issuesDeferred = remResult.issues_deferred || 0
@@ -359,6 +391,7 @@ const PR_SCHEMA = {
359
391
  required: ['pr_url'],
360
392
  }
361
393
 
394
+ await appendLedgerEntry(featureDir, prefix, { agent: 'pr-and-registry', phase: 'phase3', model: 'sonnet', status: 'running', phase_delta_tokens: 0, started_at: '__TS__', completed_at: null })
362
395
  const beforePR = budget.spent()
363
396
 
364
397
  const prResult = await agent(
@@ -408,6 +441,7 @@ If the PR creation fails, return { "pr_url": "(PR creation failed)", "registry_u
408
441
  )
409
442
 
410
443
  const prTokens = budget.spent() - beforePR
444
+ await updateLedgerEntry(featureDir, prefix, 'pr-and-registry', { status: 'done', completed_at: '__TS__', phase_delta_tokens: prTokens })
411
445
  tokenLedger.push({ agent: 'pr-and-registry', model: 'sonnet', phase_delta_tokens: prTokens })
412
446
  log(`PR done: ${prResult.pr_url} — ${prTokens} tokens`)
413
447
 
@@ -494,6 +528,7 @@ const ACTUALS_SCHEMA = {
494
528
  required: [],
495
529
  }
496
530
 
531
+ await appendLedgerEntry(featureDir, prefix, { agent: 'write-actuals', phase: 'phase3', model: 'sonnet', status: 'running', phase_delta_tokens: 0, started_at: '__TS__', completed_at: null })
497
532
  const beforeActuals = budget.spent()
498
533
 
499
534
  await agent(
@@ -587,6 +622,7 @@ Return { "token_estimate_path": "<path>", "effort_estimate_path": "<path>" }.`,
587
622
  )
588
623
 
589
624
  const actualsTokens = budget.spent() - beforeActuals
625
+ await updateLedgerEntry(featureDir, prefix, 'write-actuals', { status: 'done', completed_at: '__TS__', phase_delta_tokens: actualsTokens })
590
626
  tokenLedger.push({ agent: 'write-actuals', model: 'sonnet', phase_delta_tokens: actualsTokens })
591
627
  log(`Actuals written — ${actualsTokens} tokens`)
592
628
 
package/CHANGELOG.md CHANGED
@@ -1,4 +1,19 @@
1
1
  ## 0.1.4 (2026-07-21)
2
+ ## [0.9.0](https://github.com/dtlab-tech/ai-toolkit/compare/v0.8.0...v0.9.0) (2026-08-05)
3
+
4
+
5
+ ### Features
6
+
7
+ * **FTR-020:** add initial documentation for Atomic Work Breakdown, Execution Ledger, and Task Checkpoints and Resume ([c5f724a](https://github.com/dtlab-tech/ai-toolkit/commit/c5f724af068e003d36ec196cdec3502fe0c47333))
8
+
9
+
10
+ ### Bug Fixes
11
+
12
+ * **pm-phase3:** replace require/fs/execSync with agent()-based ledger helpers ([1166d6f](https://github.com/dtlab-tech/ai-toolkit/commit/1166d6f17e913405ea737e74236742569740cd0a))
13
+ * **pm-phase3:** replace require/fs/execSync with agent()-based ledger helpers ([bf4077c](https://github.com/dtlab-tech/ai-toolkit/commit/bf4077cca28920876185535aa553ec91c6ffc08b))
14
+ * **pm-phase3:** replace require/fs/execSync with agent()-based ledger helpers ([bb4c88d](https://github.com/dtlab-tech/ai-toolkit/commit/bb4c88df20c62db09de327975ab57e045247dc51))
15
+ * **workflows:** remove new Date() from all workflow call sites ([343b604](https://github.com/dtlab-tech/ai-toolkit/commit/343b604dba2d4d0b12e5590c1b5323b2a9e888f2))
16
+
2
17
  ## [0.8.0](https://github.com/dtlab-tech/ai-toolkit/compare/v0.7.0...v0.8.0) (2026-08-04)
3
18
 
4
19
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dtlabs/ai-toolkit",
3
- "version": "0.8.0",
3
+ "version": "0.9.0",
4
4
  "description": "AI Toolkit — reusable agents and procedures for developers to build AI-powered applications.",
5
5
  "files": [
6
6
  "docs/",