iterate-plugin 3.5.7 → 3.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/README.md +64 -20
  2. package/README.zh-CN.md +53 -17
  3. package/dist/approval-gate.js +143 -23
  4. package/dist/atomic-fs.js +148 -17
  5. package/dist/config-loader.js +509 -34
  6. package/dist/config-write.js +83 -22
  7. package/dist/evidence.js +141 -87
  8. package/dist/file-lock.js +165 -0
  9. package/dist/index.js +54 -28
  10. package/dist/jobs.js +56 -11
  11. package/dist/live.js +52 -13
  12. package/dist/meta-review.js +132 -28
  13. package/dist/method-scope.js +31 -8
  14. package/dist/review-scope.js +32 -9
  15. package/dist/review.js +96 -17
  16. package/dist/session-hooks.js +55 -13
  17. package/dist/skill-prompt.js +396 -66
  18. package/dist/tools/checkpoint.js +85 -19
  19. package/dist/tools/config.js +44 -2
  20. package/dist/tools/context.js +22 -5
  21. package/dist/tools/decision-log.js +111 -27
  22. package/dist/tools/defense-events.js +90 -31
  23. package/dist/tools/defense-store.js +28 -3
  24. package/dist/tools/experience-bank.js +108 -43
  25. package/dist/tools/experience-store.js +54 -4
  26. package/dist/tools/fix.js +347 -119
  27. package/dist/tools/history.js +68 -14
  28. package/dist/tools/present.js +80 -0
  29. package/dist/tools/prune.js +150 -53
  30. package/dist/tools/quality-gate.js +52 -2
  31. package/dist/tools/quality-store.js +17 -1
  32. package/dist/tools/review.js +158 -7
  33. package/dist/tools/transcript.js +173 -36
  34. package/dist/tools/triage.js +67 -12
  35. package/dist/tools/validate.js +115 -24
  36. package/dist/transcript.js +267 -4
  37. package/lib/client.js +945 -181
  38. package/lib/parse.js +760 -57
  39. package/package.json +14 -12
  40. package/src/approval-gate.ts +160 -23
  41. package/src/atomic-fs.ts +152 -17
  42. package/src/client/index.ts +600 -129
  43. package/src/config-loader.ts +542 -35
  44. package/src/config-write.ts +108 -26
  45. package/src/evidence.ts +194 -92
  46. package/src/file-lock.ts +184 -0
  47. package/src/index.ts +53 -29
  48. package/src/jobs.ts +63 -12
  49. package/src/live.ts +48 -11
  50. package/src/meta-review.ts +151 -34
  51. package/src/method-scope.ts +31 -8
  52. package/src/review-scope.ts +33 -10
  53. package/src/review.ts +96 -17
  54. package/src/session-hooks.ts +54 -12
  55. package/src/skill-prompt.ts +396 -66
  56. package/src/tools/checkpoint.ts +81 -19
  57. package/src/tools/config.ts +44 -2
  58. package/src/tools/context.ts +25 -5
  59. package/src/tools/decision-log.ts +115 -22
  60. package/src/tools/defense-events.ts +87 -31
  61. package/src/tools/defense-store.ts +30 -3
  62. package/src/tools/experience-bank.ts +112 -44
  63. package/src/tools/experience-store.ts +54 -5
  64. package/src/tools/fix.ts +258 -20
  65. package/src/tools/history.ts +74 -15
  66. package/src/tools/present.ts +89 -0
  67. package/src/tools/prune.ts +154 -51
  68. package/src/tools/quality-gate.ts +57 -2
  69. package/src/tools/quality-store.ts +18 -1
  70. package/src/tools/review.ts +155 -6
  71. package/src/tools/transcript.ts +178 -36
  72. package/src/tools/triage.ts +70 -13
  73. package/src/tools/validate.ts +138 -32
  74. package/src/transcript.ts +279 -4
  75. package/src/types.ts +29 -1
@@ -3,7 +3,8 @@
3
3
  *
4
4
  * Wires the {@link decideApproval} policy gate to dsh's `tools/pre-execute`
5
5
  * waterfall. This is the AUTHORITATIVE approval seam for destructive iterate
6
- * tools (`iterate_fix` / `iterate_rollback` / `iterate_prune` with dryRun:false):
6
+ * tools (`iterate_fix` / `iterate_rollback` / `iterate_prune` with dryRun:false
7
+ * / `iterate_config` with operation:"write"):
7
8
  *
8
9
  * - `allow` policy → the call runs.
9
10
  * - `deny` policy → the call is refused (fail-closed), surfaced as an
@@ -13,11 +14,19 @@
13
14
  * plus the human-readable reason) so durable
14
15
  * projections can route it distinctly from a normal
15
16
  * tool failure.
16
- * - `ask` policy → return `{ kind: 'ask', reason }`; dsh's own
17
- * scheduler routes it through the `approval` service
18
- * (see `@deepseek-ai/dsh-user-approval`), which
19
- * prompts the human and audits an approve/deny pair
20
- * on the session.
17
+ * - `ask` policy → return `{ kind: 'ask', reason, displayReason }`;
18
+ * dsh's own scheduler routes it through the
19
+ * `approval` service (see
20
+ * `@deepseek-ai/dsh-user-approval`), which prompts
21
+ * the human and audits an approve/deny pair on the
22
+ * session. `reason` is the audited English summary;
23
+ * `displayReason` (dsh 0.2.x) is the localized
24
+ * prompt text. LANGUAGE RULE: human-facing text
25
+ * follows the project `language` (zh|en); every
26
+ * structured/durable field (deny reason,
27
+ * `info.reason`, decision-log entries, console
28
+ * logs) stays English so machine matchers never
29
+ * depend on a locale.
21
30
  * - canceled → if the caller aborted the invocation before
22
31
  * dispatch, return `{ kind: 'cancel' }` (the
23
32
  * canonical 0.1.6-alpha.1 cancellation result) so a
@@ -31,7 +40,15 @@
31
40
  *
32
41
  * Safety properties:
33
42
  * - Read-only tools and non-iterate tools are always allowed (the gate only
34
- * inspects the three destructive iterate toolnames).
43
+ * inspects the four gated iterate toolnames, and of those only prune
44
+ * `dryRun:false` and `iterate_config` `operation:"write"` — a config read
45
+ * or a dry-run prune short-circuits to allow inside the policy gate).
46
+ * - The approval policy is read from the SESSION workspace whenever a
47
+ * session cwd is known — the model-controlled `path` argument can never
48
+ * choose where the human's policy comes from (it is only consulted when
49
+ * there is no session cwd at all). Otherwise `path:` could point at an
50
+ * `approval: allow` directory to self-grant, or dodge a `deny` in the
51
+ * workspace.
35
52
  * - If the project root / observatory config cannot be resolved, the policy
36
53
  * degrades to `ask` (fail-safe: destructive writes always require consent).
37
54
  */
@@ -81,8 +98,14 @@ export function gateDecision(exec: ToolExecution): PreToolDecision {
81
98
  return { kind: 'cancel' }
82
99
  }
83
100
 
84
- // Resolve the project root (use the call's own `path` arg, else the agent's
85
- // session cwd) to read the effective observatory policy.
101
+ // Resolve the project root whose observatory policy governs this call.
102
+ // SECURITY: the policy source must NOT be model-controlled. `path` is a
103
+ // model argument, so reading the policy from the path-resolved root let a
104
+ // call point `path:` at a directory with `approval: allow` (self-grant) or
105
+ // dodge a human's `deny` in the session workspace. When the session cwd is
106
+ // known, the SESSION workspace's config is authoritative and `path` is
107
+ // ignored; the path-resolved root is only the fallback when there is no
108
+ // session cwd at all (headless/tests). The `ask` default stays fail-closed.
86
109
  let argPath: string | undefined
87
110
  let sessionCwd: string | undefined
88
111
  try {
@@ -95,17 +118,25 @@ export function gateDecision(exec: ToolExecution): PreToolDecision {
95
118
  } catch {
96
119
  // hostile/proxied exec — fall through with both undefined (defaults to ask)
97
120
  }
98
- const resolved = resolveProjectRoot(argPath, sessionCwd)
121
+ // Passing sessionCwd as BOTH the input and the anchor makes the root the
122
+ // session cwd itself (no server-cwd fallback can sneak in), anchored there.
123
+ const resolved = sessionCwd
124
+ ? resolveProjectRoot(sessionCwd, sessionCwd)
125
+ : resolveProjectRoot(argPath, sessionCwd)
99
126
  let policy: 'ask' | 'deny' | 'allow' = 'ask'
127
+ // Project language for the HUMAN-FACING ask prompt only (see the header's
128
+ // language rule: structured deny/audit text stays English).
129
+ let language: 'zh' | 'en' = 'en'
100
130
  if (resolved.ok) {
101
131
  const { config } = loadEffectiveConfig(resolved.root)
102
132
  const p = config.observatory?.approval
103
133
  if (p === 'deny') policy = 'deny'
104
134
  else if (p === 'allow') policy = 'allow'
105
135
  // anything else (including a corrupt/missing `ask`) → 'ask'
136
+ if (config.language === 'zh') language = 'zh'
106
137
  }
107
138
 
108
- const decision = decideApproval(exec, policy)
139
+ const decision = decideApproval(exec, policy, language)
109
140
  if (decision.kind === 'deny') {
110
141
  return {
111
142
  kind: 'deny',
@@ -114,11 +145,19 @@ export function gateDecision(exec: ToolExecution): PreToolDecision {
114
145
  ...DENY_INFO,
115
146
  // ToolErrorInfo.reason (0.1.6-alpha.1): raw user-facing detail kept in
116
147
  // durable state; the model-facing text still only sees `reason` above.
148
+ // Deliberately the ENGLISH structured reason, never the localized
149
+ // prompt — durable projections must match on a stable string.
117
150
  reason: decision.reason,
118
151
  },
119
152
  }
120
153
  }
121
- if (decision.kind === 'ask') return { kind: 'ask', reason: decision.reason }
154
+ // dsh 0.2.x: `displayReason` is the localized prompt text (en + the
155
+ // configured locale); `reason` stays the audited English summary. Both are
156
+ // forwarded so the approval UI can show the full localized explanation
157
+ // (including the WARNING line for gate-disabling config writes).
158
+ if (decision.kind === 'ask') {
159
+ return { kind: 'ask', reason: decision.reason, displayReason: decision.displayReason }
160
+ }
122
161
  return { kind: 'allow' }
123
162
  }
124
163
 
@@ -139,6 +178,9 @@ export function registerSessionHooks(ctx: Context): void {
139
178
  return Promise.resolve({
140
179
  kind: 'ask',
141
180
  reason: 'iterate approval gate unavailable — require consent',
181
+ // Fail-safe path: no config was read, so the localized prompt falls
182
+ // back to English (displayReason requires at least an `en` entry).
183
+ displayReason: { en: 'iterate approval gate unavailable — require consent' },
142
184
  })
143
185
  }
144
186
  if (decision.kind === 'ask') {