iterate-plugin 3.9.2 → 3.10.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.
@@ -119,8 +119,10 @@ export function registerHistoryTool(ctx) {
119
119
  parameters: {
120
120
  type: {
121
121
  type: 'string',
122
- description: 'Optional entry-type filter: round_start, review_result, atomic_fix, architectural_fix, ' +
123
- 'revert, round_failed, validation, decision, report, resume.',
122
+ description: 'Optional entry-type (decision kind) filter. This plugin emits round_start, review_result, ' +
123
+ 'atomic_fix, architectural_fix, revert, round_failed, validation, decision, report, resume — ' +
124
+ 'a LABEL list, not a closed set: any kind matching the kernel grammar ' +
125
+ '(`^[a-z][a-z0-9_]{0,31}$`) can be recorded and filtered here.',
124
126
  },
125
127
  since: {
126
128
  type: 'string',
@@ -6,7 +6,6 @@
6
6
  * actually delete.
7
7
  *
8
8
  * Artifacts managed:
9
- * - Decision-log entries older than `retainDays` (default 30, via since).
10
9
  * - Stale checkpoint files (checkpoint.json).
11
10
  * - Fix backups left over from old rounds (backups whose fix-id no longer
12
11
  * appears in the registry).
@@ -18,6 +17,14 @@
18
17
  * - Defense events older than `retainDays` (stale iteration streams that
19
18
  * were never cleared).
20
19
  *
20
+ * Artifacts NOT managed (deliberately):
21
+ * - The decision ledger (`.iterate/decision-log.jsonl`). It is a hash CHAIN since the
22
+ * kernel flip: every entry's `prevEntryHash` is the sha256 of the previous line's
23
+ * bytes, so dropping or renumbering one line invalidates every later one. Prune
24
+ * therefore never rewrites it — `pruneDecisionLog` reports what a cutoff WOULD have
25
+ * removed and names the two legal moves instead. The renumbering rewrite that used to
26
+ * live here is deleted, not hidden behind a flag.
27
+ *
21
28
  * Security model:
22
29
  * - Only operates under the resolved project `.iterate/` directory.
23
30
  * - dryRun=true by default — the caller must explicitly opt into deletion.
@@ -28,14 +35,14 @@ import { existsSync, readdirSync, readFileSync, rmSync, unlinkSync, statSync } f
28
35
  import { join } from 'node:path';
29
36
  import { defineTool } from '@deepseek-ai/dsh-tools';
30
37
  import { resolveProjectRootForExec } from "../config-loader.js";
31
- import { writeTextAtomic, writeJsonAtomic } from "../atomic-fs.js";
38
+ import { writeJsonAtomic } from "../atomic-fs.js";
32
39
  import { withProjectLock } from "../file-lock.js";
33
- import { readDecisionEntries, appendDecisionEntry, acquireLogLockChecked, invalidateLogCountCache } from "./decision-log.js";
40
+ import { readDecisionEntries, appendDecisionEntry, decisionLogContestedLine } from "./decision-log.js";
34
41
  import { readRegistry, recomputeRoundCounts } from "./fix.js";
35
42
  import { readExperienceBank, writeExperienceBank } from "./experience-store.js";
36
43
  import { readDefenseEvents, writeDefenseEvents } from "./defense-store.js";
37
44
  import { iterateDir, fixesDir, checkpointPath, fixRegistryPath } from "../paths.js";
38
- /** Default retention for decision-log entries (in days). */
45
+ /** Retention window for the age-based artifacts (checkpoints, defense events). */
39
46
  const DEFAULT_RETAIN_DAYS = 30;
40
47
  const MIN_RETAIN_DAYS = 1;
41
48
  const MAX_RETAIN_DAYS = 365;
@@ -46,8 +53,6 @@ const MAX_RETAIN_DAYS = 365;
46
53
  * (never recent learning).
47
54
  */
48
55
  export const MAX_EXPERIENCE_ENTRIES = 2000;
49
- /** How many compare-and-append retries bound the decision-log rewrite race. */
50
- const MAX_LOG_REWRITE_RETRIES = 3;
51
56
  /** Clamp retainDays to a sane range. */
52
57
  export function clampRetainDays(days) {
53
58
  if (typeof days !== 'number' || !Number.isInteger(days) || days <= 0) {
@@ -150,9 +155,12 @@ function computeDefenseCounts(events) {
150
155
  */
151
156
  export function inspectPrune(projectRoot, retainDays) {
152
157
  const cutoff = cutoffTimestamp(retainDays);
153
- // 1. Decision-log entries older than retainDays.
158
+ // 1. Decision-log entries older than retainDays — REPORTED, never removed: the ledger
159
+ // is a hash chain (see `pruneDecisionLog`). `contestedLogLine` is the one ledger
160
+ // condition a human has to settle, so the dry-run preview surfaces it.
154
161
  const entries = readDecisionEntries(projectRoot);
155
162
  const oldLogEntries = entries.filter((e) => e.timestamp < cutoff).length;
163
+ const contestedLogLine = decisionLogContestedLine(projectRoot);
156
164
  // 2. Checkpoint presence. A checkpoint is only STALE once it is older than
157
165
  // retainDays — a fresh checkpoint means a run was interrupted recently and
158
166
  // may still be resumed, so `hasCheckpoint` is kept as pure existence while
@@ -250,6 +258,7 @@ export function inspectPrune(projectRoot, retainDays) {
250
258
  const sweptDefense = sweepDefenseEvents(defenseStream, cutoff);
251
259
  return {
252
260
  oldLogEntries,
261
+ contestedLogLine,
253
262
  hasCheckpoint,
254
263
  checkpointStale,
255
264
  staleBackups,
@@ -269,89 +278,56 @@ export function inspectPrune(projectRoot, retainDays) {
269
278
  * Returns a detailed report of what was deleted.
270
279
  */
271
280
  /**
272
- * Rewrite the decision log to the entries younger than `cutoff`.
281
+ * What a cutoff WOULD have removed from the decision ledger — and why nothing is removed.
273
282
  *
274
- * The read-before-rewrite is a TOCTOU hot spot: a concurrent worker can append
275
- * a fresh audit line between our read and our atomic rewrite, and an atomic
276
- * rename would silently discard it. So instead of a single read+write we take
277
- * the cross-process log mutex for the whole loop AND keep a bounded
278
- * compare-and-append loop behind it — after each rewrite we re-read the log and
279
- * retry whenever the file grew (a concurrent append slipped in). The loop
280
- * terminates when the file is stable or after `MAX_LOG_REWRITE_RETRIES`, and a
281
- * failure here is surfaced as a structured error instead of truncating the log.
283
+ * THE RENUMBERING REWRITE IS GONE, NOT HIDDEN BEHIND A FLAG. It used to filter the log to
284
+ * the entries younger than `cutoff` and rewrite the whole file (temp + atomic rename, a
285
+ * bounded retry loop, the cross-process lock). Against the kernel's hash chain that is not
286
+ * a destructive-but-honest trim, it is forgery: entry i+1's `prevEntryHash` is the sha256
287
+ * of entry i's BYTES, so dropping line 3 makes every later line's recorded link wrong —
288
+ * and re-emitting the survivors with renumbered `sequence` and recomputed hashes produces
289
+ * a chain that verifies while proving nothing, because it now claims "these decisions were
290
+ * contiguous" about a file somebody deleted the middle of.
282
291
  *
283
- * Lock required: when the mutex cannot be taken (another process is appending
284
- * and does not release within the wait budget) the rewrite is REFUSED with a
285
- * structured error rather than proceeding unlocked — an unlocked rename can
286
- * drop a concurrent appender's audit line (its fd points at the pre-rename
287
- * inode) with no way to detect the loss afterwards.
292
+ * So prune reads, reports, and touches nothing. The two legal moves belong to the ledger
293
+ * itself and both are named in the remedy:
288
294
  *
289
- * `deleted` is the TOTAL across every attempt: attempt 1 may already have
290
- * removed entries before a retry is needed, and reporting only the final
291
- * attempt's slice would under-count the audit entry (the pre-fix code returned
292
- * `deleted: 0` on the exhaustion path even after real deletions).
295
+ * 1. keep appending — an all-legacy log migrates onto the chain on its next write
296
+ * (verified before the swap, the original kept beside it at mode 0600) and a healthy
297
+ * chain simply grows; a line is bounded by PAYLOAD_MAX_CANONICAL_LENGTH, not by age;
298
+ * 2. settle a CONTESTED ledger by CHOOSING archive-and-restart
299
+ * (`iterate_decision_log({operation:"archive"})` / `archiveContestedDecisionLog`),
300
+ * which renames the file to `…contested-<stamp>.jsonl` at mode 0600 and lets the next
301
+ * append start a fresh chain. It refuses a healthy or an all-legacy ledger, so it can
302
+ * never become a way to erase continuity by accident.
293
303
  *
294
- * @param options.afterRewrite test hook run after each successful rewrite —
295
- * simulates a concurrent appender landing lines mid-loop (production passes
296
- * no hook).
297
- * @param options.lock lock wait/stale budgets (tests pass short waits so the
298
- * lock-unavailable refusal is exercised in milliseconds, not 5s).
299
- * @returns the number of entries removed across all attempts, or 0 with an
300
- * `error` when nothing needed removing or the rewrite was refused/failed.
304
+ * `cutoff` stays in the signature because the REPORT needs it: `retained` is exactly the
305
+ * number of lines the old code would have deleted, which is what a human weighs when
306
+ * deciding whether a ledger is worth settling.
301
307
  */
302
- export function rewriteDecisionLogKeepingRecent(projectRoot, cutoff, options = {}) {
303
- const logPath = join(iterateDir(projectRoot), 'decision-log.jsonl');
304
- // Take the cross-process log lock for the ENTIRE loop: with the mutex held,
305
- // no concurrent appender can slip a fresh line into the read-before-rewrite
306
- // window, so the first attempt is nearly always the only one. (The retry
307
- // loop stays as defense-in-depth for a line landing via the afterRewrite
308
- // hook or an already-running appender's buffered write.)
309
- const { acquired, release } = acquireLogLockChecked(projectRoot, options.lock);
310
- if (!acquired) {
311
- return {
312
- deleted: 0,
313
- error: 'failed to acquire the decision-log lock — refusing to rewrite the log unlocked ' +
314
- '(a concurrent appender could lose an audit line). Retry after the other operation finishes.',
315
- };
316
- }
317
- try {
318
- let deletedTotal = 0;
319
- for (let attempt = 0; attempt < MAX_LOG_REWRITE_RETRIES; attempt++) {
320
- try {
321
- const entries = readDecisionEntries(projectRoot);
322
- const kept = entries.filter((e) => e.timestamp >= cutoff);
323
- const deleted = entries.length - kept.length;
324
- if (deleted === 0)
325
- return { deleted: deletedTotal };
326
- writeTextAtomic(logPath, kept.map((e) => JSON.stringify(e)).join('\n') + '\n');
327
- // The append-side entry-count cache is keyed by path and assumes "same
328
- // byte size ⇒ same count"; the rename above replaced the file with a
329
- // different number of entries, so drop the stale cache or the next
330
- // append can report a wrong entryCount when the sizes coincidentally
331
- // match.
332
- invalidateLogCountCache(logPath);
333
- // Accumulate THIS attempt's deletions before the re-read: whatever the
334
- // later attempts (or the exhaustion path) report, these entries are
335
- // already gone from disk and must count toward the audit total.
336
- deletedTotal += deleted;
337
- options.afterRewrite?.();
338
- // A concurrent appender (cross-process, lock-unavailable path) may have
339
- // landed new lines after our read. If the log grew during the write
340
- // window, re-read and prune again instead of accepting a lost audit trail.
341
- const after = readDecisionEntries(projectRoot);
342
- if (after.length <= kept.length)
343
- return { deleted: deletedTotal };
344
- }
345
- catch (err) {
346
- return { deleted: deletedTotal, error: `failed to rewrite decision log: ${String(err)}` };
347
- }
348
- }
349
- return { deleted: deletedTotal, error: `failed to rewrite decision log after ${MAX_LOG_REWRITE_RETRIES} attempts` };
350
- }
351
- finally {
352
- release();
353
- }
308
+ export function pruneDecisionLog(projectRoot, cutoff) {
309
+ const entries = readDecisionEntries(projectRoot);
310
+ const retained = entries.filter((e) => e.timestamp < cutoff).length;
311
+ const contestedLine = decisionLogContestedLine(projectRoot);
312
+ return {
313
+ deleted: 0,
314
+ retained,
315
+ contestedLine,
316
+ code: 'ITERATE_E_DECISION_LOG_IMMUTABLE',
317
+ error: `the decision ledger is a hash chain: ${retained} ` +
318
+ `entr${retained === 1 ? 'y' : 'ies'} older than ${cutoff} are RETAINED, not rewritten`,
319
+ remedy: 'a chain cannot be trimmed in place — every later line links to the bytes of the line before it, ' +
320
+ 'so deleting or renumbering one forges the rest. Two legal moves: (1) keep appending: an all-legacy ' +
321
+ 'ledger migrates onto the chain on its next write and a healthy one just grows; (2) if an append ' +
322
+ 'reports ITERATE_E_DECISION_LOG_INTERLEAVED, a human settles it with archive-and-restart — ' +
323
+ 'iterate_decision_log({operation:"archive"}) moves the contested ledger aside losslessly (mode 0600) ' +
324
+ 'and the next append starts a clean chain. Prune never deletes a ledger line.',
325
+ };
354
326
  }
327
+ /**
328
+ * Actually prune the runtime artifacts (only called when dryRun=false).
329
+ * Returns a detailed report of what was deleted.
330
+ */
355
331
  /**
356
332
  * Execute the prune: apply every cleanable item reported by `inspectPrune`.
357
333
  * Every step is individually hedged so a single disk failure degrades to a
@@ -359,8 +335,13 @@ export function rewriteDecisionLogKeepingRecent(projectRoot, cutoff, options = {
359
335
  */
360
336
  export function executePrune(projectRoot, retainDays, report) {
361
337
  const cutoff = cutoffTimestamp(retainDays);
338
+ const ledger = pruneDecisionLog(projectRoot, cutoff);
362
339
  const result = {
340
+ // Always 0: a ledger line is never deleted by prune. The field stays because the
341
+ // prune summary (and the audit entry it logs) has always carried it.
363
342
  deletedLogEntries: 0,
343
+ retainedLogEntries: ledger.retained,
344
+ decisionLog: { code: ledger.code, error: ledger.error, remedy: ledger.remedy },
364
345
  deletedCheckpoint: false,
365
346
  deletedBackups: [],
366
347
  deletedTemps: [],
@@ -369,16 +350,13 @@ export function executePrune(projectRoot, retainDays, report) {
369
350
  deletedDefenseEvents: 0,
370
351
  errors: [],
371
352
  };
372
- // 1. Rewrite the decision log, keeping only recent entries. Atomic
373
- // (temp + rename) under the cross-process log mutex — REFUSED with a
374
- // structured error when the lock cannot be taken (an unlocked rename could
375
- // drop a concurrent appender's audit line); the bounded compare-and-append
376
- // loop behind it guards the remaining read-before-rewrite window.
377
- {
378
- const { deleted, error } = rewriteDecisionLogKeepingRecent(projectRoot, cutoff);
379
- result.deletedLogEntries = deleted;
380
- if (error)
381
- result.errors.push(error);
353
+ // 1. The decision ledger is READ-ONLY here: nothing is deleted, nothing is rewritten
354
+ // (see `pruneDecisionLog`). The refusal is always reported; a CONTESTED ledger is
355
+ // additionally escalated into `errors`, because that is a broken state only a human can
356
+ // settle — archive-and-restart is offered, never taken.
357
+ if (ledger.contestedLine !== null) {
358
+ result.errors.push(`line ${ledger.contestedLine} of the decision ledger is a legacy record appended after the chain ` +
359
+ `began (ITERATE_E_DECISION_LOG_INTERLEAVED). ${ledger.remedy}`);
382
360
  }
383
361
  // 2. Remove checkpoint — ONLY when it is actually stale (older than
384
362
  // retainDays). A fresh checkpoint is a live resume point for a recently
@@ -499,9 +477,12 @@ export function registerPruneTool(ctx) {
499
477
  },
500
478
  description: 'Inspect or clean up old iterate runtime artifacts (.iterate/). ' +
501
479
  'Defaults to dry-run (report-only, no deletion). Pass `dryRun: false` to actually prune. ' +
502
- 'Manages: old decision-log entries, stale checkpoints, orphaned fix backups, empty fix rounds, ' +
480
+ 'Manages: stale checkpoints, orphaned fix backups, empty fix rounds, ' +
503
481
  'stray temp files left by crashed atomic writes, experience-bank entries beyond the entry cap, ' +
504
482
  'and stale defense events. ' +
483
+ 'The decision ledger is NEVER trimmed: it is a hash chain, so a removed or renumbered line ' +
484
+ 'would invalidate every later one. Prune reports how many lines a cutoff would have removed ' +
485
+ 'and names the two legal moves (keep appending / human-chosen archive-and-restart). ' +
505
486
  'A non-dry-run appends ONE aggregate summary entry (counts per artifact class) to the decision log.',
506
487
  parameters: {
507
488
  dryRun: {
@@ -539,7 +520,10 @@ export function registerPruneTool(ctx) {
539
520
  const registryError = report?.registryError;
540
521
  const lines = [
541
522
  `[dry-run] prune report (retainDays=${value.retainDays}):`,
542
- ` Decision-log entries to remove: ${report?.oldLogEntries ?? '?'} (of ${report?.totalLogEntries ?? '?'})`,
523
+ ` Decision-log entries older than retainDays: ${report?.oldLogEntries ?? '?'} (of ${report?.totalLogEntries ?? '?'}) — RETAINED, a hash chain is never trimmed`,
524
+ report?.contestedLogLine
525
+ ? ` CONTESTED ledger: line ${report.contestedLogLine} is a legacy record after the chain — settle it with iterate_decision_log({operation:"archive"})`
526
+ : ' Ledger continuity: no interleaved legacy records',
543
527
  ` Checkpoint to delete: ${report?.checkpointStale ? 'yes (stale)' : report?.hasCheckpoint ? 'no (fresh — resume point, kept)' : 'none'}`,
544
528
  registryError
545
529
  ? ` Stale backups to delete: none — ${registryError}`
@@ -555,7 +539,7 @@ export function registerPruneTool(ctx) {
555
539
  }
556
540
  const lines = [
557
541
  `Prune complete (retainDays=${value.retainDays}):`,
558
- ` Deleted ${result?.deletedLogEntries ?? 0} old log entries.`,
542
+ ` Decision-log lines deleted: ${result?.deletedLogEntries ?? 0} — ${result?.retainedLogEntries ?? 0} older than the cutoff RETAINED (${result?.decisionLog?.code ?? 'hash chain'}: never renumbered).`,
559
543
  ` Checkpoint deleted: ${result?.deletedCheckpoint ? 'yes' : 'no'}`,
560
544
  ` Deleted ${result?.deletedBackups?.length ?? 0} stale backups.`,
561
545
  ` Deleted ${result?.deletedTemps?.length ?? 0} stray temp files.`,
@@ -600,6 +584,10 @@ export function registerPruneTool(ctx) {
600
584
  action: 'prune',
601
585
  retainDays,
602
586
  deletedLogEntries: result.deletedLogEntries,
587
+ retainedLogEntries: result.retainedLogEntries,
588
+ // The ledger itself was not touched; record WHY, so an audit reader can tell
589
+ // "prune deleted nothing" apart from "prune was refused by an immutable chain".
590
+ decisionLogRefusal: result.decisionLog.code,
603
591
  deletedCheckpoint: result.deletedCheckpoint,
604
592
  deletedBackups: result.deletedBackups.length,
605
593
  trimmedEmptyRounds: result.trimmedEmptyRounds,
package/dist/types.js CHANGED
@@ -1 +1,24 @@
1
- export {};
1
+ /**
2
+ * The decision kinds THIS plugin's own producers write.
3
+ *
4
+ * DISPLAY AND DOCUMENTATION ONLY — never a validity rule. Membership in this list
5
+ * used to be the gate on `iterate_decision_log` appends, and it disagreed with the
6
+ * Python harness's own list (which rejected `round_failed` and `resume` outright):
7
+ * two shells, two closed sets, one shared ledger. The disagreement WAS the bug.
8
+ * Validity is now the kernel's kind GRAMMAR (`DECISION_KIND_PATTERN`,
9
+ * `^[a-z][a-z0-9_]{0,31}$`) — see `src/tools/decision-log.ts` `isDecisionKind` —
10
+ * deliberately open so adding a kind does not need a kernel release, and so a kind
11
+ * produced by either shell is readable by the other.
12
+ */
13
+ export const KNOWN_DECISION_KINDS = [
14
+ 'round_start',
15
+ 'review_result',
16
+ 'atomic_fix',
17
+ 'architectural_fix',
18
+ 'revert',
19
+ 'validation',
20
+ 'decision',
21
+ 'report',
22
+ 'round_failed',
23
+ 'resume',
24
+ ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iterate-plugin",
3
- "version": "3.9.2",
3
+ "version": "3.10.0",
4
4
  "description": "dsh plugin that turns the iterate skill into an autonomous closed-loop harness with quality command center and experience bank (v3.3). Features: plan -> parallel review xN -> atomic fixes -> validate -> loop -> auto-stop, plus dry-run pure-review mode, quality gate compute/persist, writable experience bank, defense events stream (record + bilingual labels), and native command buttons — with a live quality command center (F8/F9/F10 render real session data), §8 assign-fix instruction, and task_mode indicator wired end to end.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -51,7 +51,8 @@
51
51
  "0.2.1-alpha.4": "compatible",
52
52
  "0.2.1-alpha.5": "compatible",
53
53
  "0.2.1-rc.1": "compatible",
54
- "0.2.1-rc.2": "compatible"
54
+ "0.2.1-rc.2": "compatible",
55
+ "0.2.1-rc.3": "compatible"
55
56
  }
56
57
  },
57
58
  "client": {
@@ -88,13 +89,14 @@
88
89
  "test:validate": "tsx --test test/validate.test.ts"
89
90
  },
90
91
  "dependencies": {
92
+ "iterate-kernel": "0.1.9",
91
93
  "js-yaml": "4.3.2"
92
94
  },
93
95
  "peerDependencies": {
94
96
  "@deepseek-ai/cordis": "^4.0.5-alpha.2",
95
- "@deepseek-ai/dsh-session": "0.1.1-rc.1 || 0.1.2-alpha.4 || 0.1.2-alpha.5 || 0.1.2-rc.1 || 0.1.5-alpha.1 || 0.1.5-alpha.2 || 0.1.5-rc.1 || 0.1.5-rc.2 || 0.1.6-alpha.1 || 0.1.6-alpha.2 || 0.1.7-alpha.1 || 0.1.7-alpha.2 || 0.1.7-rc.1 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3 || 0.2.1-alpha.4 || 0.2.1-alpha.5 || 0.2.1-rc.1 || 0.2.1-rc.2",
96
- "@deepseek-ai/dsh-tools": "0.1.1-rc.1 || 0.1.2-alpha.4 || 0.1.2-alpha.5 || 0.1.2-rc.1 || 0.1.5-alpha.1 || 0.1.5-alpha.2 || 0.1.5-rc.1 || 0.1.5-rc.2 || 0.1.6-alpha.1 || 0.1.6-alpha.2 || 0.1.7-alpha.1 || 0.1.7-alpha.2 || 0.1.7-rc.1 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3 || 0.2.1-alpha.4 || 0.2.1-alpha.5 || 0.2.1-rc.1 || 0.2.1-rc.2",
97
- "@deepseek-ai/dsh-util-values": "0.1.2-alpha.5 || 0.1.2-rc.1 || 0.1.5-alpha.1 || 0.1.5-alpha.2 || 0.1.5-rc.1 || 0.1.5-rc.2 || 0.1.6-alpha.1 || 0.1.6-alpha.2 || 0.1.7-alpha.1 || 0.1.7-alpha.2 || 0.1.7-rc.1 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3 || 0.2.1-alpha.4 || 0.2.1-alpha.5 || 0.2.1-rc.1 || 0.2.1-rc.2"
97
+ "@deepseek-ai/dsh-session": "0.1.1-rc.1 || 0.1.2-alpha.4 || 0.1.2-alpha.5 || 0.1.2-rc.1 || 0.1.5-alpha.1 || 0.1.5-alpha.2 || 0.1.5-rc.1 || 0.1.5-rc.2 || 0.1.6-alpha.1 || 0.1.6-alpha.2 || 0.1.7-alpha.1 || 0.1.7-alpha.2 || 0.1.7-rc.1 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3 || 0.2.1-alpha.4 || 0.2.1-alpha.5 || 0.2.1-rc.1 || 0.2.1-rc.2 || 0.2.1-rc.3",
98
+ "@deepseek-ai/dsh-tools": "0.1.1-rc.1 || 0.1.2-alpha.4 || 0.1.2-alpha.5 || 0.1.2-rc.1 || 0.1.5-alpha.1 || 0.1.5-alpha.2 || 0.1.5-rc.1 || 0.1.5-rc.2 || 0.1.6-alpha.1 || 0.1.6-alpha.2 || 0.1.7-alpha.1 || 0.1.7-alpha.2 || 0.1.7-rc.1 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3 || 0.2.1-alpha.4 || 0.2.1-alpha.5 || 0.2.1-rc.1 || 0.2.1-rc.2 || 0.2.1-rc.3",
99
+ "@deepseek-ai/dsh-util-values": "0.1.2-alpha.5 || 0.1.2-rc.1 || 0.1.5-alpha.1 || 0.1.5-alpha.2 || 0.1.5-rc.1 || 0.1.5-rc.2 || 0.1.6-alpha.1 || 0.1.6-alpha.2 || 0.1.7-alpha.1 || 0.1.7-alpha.2 || 0.1.7-rc.1 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3 || 0.2.1-alpha.4 || 0.2.1-alpha.5 || 0.2.1-rc.1 || 0.2.1-rc.2 || 0.2.1-rc.3"
98
100
  },
99
101
  "peerDependenciesMeta": {
100
102
  "@deepseek-ai/cordis": {
@@ -112,11 +114,19 @@
112
114
  },
113
115
  "devDependencies": {
114
116
  "@deepseek-ai/cordis": "4.0.5",
115
- "@deepseek-ai/dsh-agent": "0.2.1-rc.2",
116
- "@deepseek-ai/dsh-jobs": "0.2.1-rc.2",
117
- "@deepseek-ai/dsh-session": "0.2.1-rc.2",
118
- "@deepseek-ai/dsh-tools": "0.2.1-rc.2",
119
- "@deepseek-ai/dsh-util-values": "0.2.1-rc.2",
117
+ "@deepseek-ai/dsh-agent": "0.2.1-rc.3",
118
+ "@deepseek-ai/dsh-jobs": "0.2.1-rc.3",
119
+ "@deepseek-ai/dsh-llm": "0.2.1-rc.3",
120
+ "@deepseek-ai/dsh-ptc-runtime": "0.2.1-rc.3",
121
+ "@deepseek-ai/dsh-sandbox": "0.2.1-rc.3",
122
+ "@deepseek-ai/dsh-sandbox-policy": "0.2.1-rc.3",
123
+ "@deepseek-ai/dsh-scope": "0.2.1-rc.3",
124
+ "@deepseek-ai/dsh-session": "0.2.1-rc.3",
125
+ "@deepseek-ai/dsh-system-prompt": "0.2.1-rc.3",
126
+ "@deepseek-ai/dsh-tools": "0.2.1-rc.3",
127
+ "@deepseek-ai/dsh-user-approval": "0.2.1-rc.3",
128
+ "@deepseek-ai/dsh-util-values": "0.2.1-rc.3",
129
+ "@deepseek-ai/dsh-working-directory": "0.2.1-rc.3",
120
130
  "@types/js-yaml": "4.0.9",
121
131
  "@types/node": "22.15.0",
122
132
  "@types/react": "19.2.2",