@namzu/sdk 57.0.0 → 58.0.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 (278) hide show
  1. package/CHANGELOG.md +62 -0
  2. package/dist/agents/QueryAgent.d.ts.map +1 -1
  3. package/dist/agents/QueryAgent.js +3 -0
  4. package/dist/agents/QueryAgent.js.map +1 -1
  5. package/dist/agents/file-definitions.d.ts +35 -4
  6. package/dist/agents/file-definitions.d.ts.map +1 -1
  7. package/dist/agents/file-definitions.js +98 -8
  8. package/dist/agents/file-definitions.js.map +1 -1
  9. package/dist/authorization/protected-paths.d.ts +9 -0
  10. package/dist/authorization/protected-paths.d.ts.map +1 -1
  11. package/dist/authorization/protected-paths.js +29 -3
  12. package/dist/authorization/protected-paths.js.map +1 -1
  13. package/dist/compaction/index.d.ts +1 -0
  14. package/dist/compaction/index.d.ts.map +1 -1
  15. package/dist/compaction/index.js.map +1 -1
  16. package/dist/compaction/manual.d.ts +7 -0
  17. package/dist/compaction/manual.d.ts.map +1 -1
  18. package/dist/compaction/manual.js +31 -12
  19. package/dist/compaction/manual.js.map +1 -1
  20. package/dist/compaction/model-summary.d.ts +101 -0
  21. package/dist/compaction/model-summary.d.ts.map +1 -0
  22. package/dist/compaction/model-summary.js +333 -0
  23. package/dist/compaction/model-summary.js.map +1 -0
  24. package/dist/compaction/verifier.d.ts +6 -1
  25. package/dist/compaction/verifier.d.ts.map +1 -1
  26. package/dist/compaction/verifier.js +3 -3
  27. package/dist/compaction/verifier.js.map +1 -1
  28. package/dist/config/runtime.d.ts +62 -0
  29. package/dist/config/runtime.d.ts.map +1 -1
  30. package/dist/config/runtime.js +16 -0
  31. package/dist/config/runtime.js.map +1 -1
  32. package/dist/connector/mcp/client.d.ts +8 -0
  33. package/dist/connector/mcp/client.d.ts.map +1 -1
  34. package/dist/connector/mcp/client.js +75 -2
  35. package/dist/connector/mcp/client.js.map +1 -1
  36. package/dist/constants/mcp/index.d.ts +2 -0
  37. package/dist/constants/mcp/index.d.ts.map +1 -1
  38. package/dist/constants/mcp/index.js +2 -0
  39. package/dist/constants/mcp/index.js.map +1 -1
  40. package/dist/constants/plugin/index.d.ts +2 -0
  41. package/dist/constants/plugin/index.d.ts.map +1 -1
  42. package/dist/constants/plugin/index.js +2 -0
  43. package/dist/constants/plugin/index.js.map +1 -1
  44. package/dist/manager/agent/lifecycle.d.ts +1 -1
  45. package/dist/manager/agent/lifecycle.d.ts.map +1 -1
  46. package/dist/manager/agent/lifecycle.js +25 -4
  47. package/dist/manager/agent/lifecycle.js.map +1 -1
  48. package/dist/manager/session/turn-recorder.d.ts.map +1 -1
  49. package/dist/manager/session/turn-recorder.js +2 -1
  50. package/dist/manager/session/turn-recorder.js.map +1 -1
  51. package/dist/persona/assembler.d.ts.map +1 -1
  52. package/dist/persona/assembler.js +2 -2
  53. package/dist/persona/assembler.js.map +1 -1
  54. package/dist/plugin/index.d.ts +1 -1
  55. package/dist/plugin/index.d.ts.map +1 -1
  56. package/dist/plugin/lifecycle.d.ts +8 -1
  57. package/dist/plugin/lifecycle.d.ts.map +1 -1
  58. package/dist/plugin/lifecycle.js +9 -2
  59. package/dist/plugin/lifecycle.js.map +1 -1
  60. package/dist/plugin/shell-hook-http.d.ts +39 -0
  61. package/dist/plugin/shell-hook-http.d.ts.map +1 -0
  62. package/dist/plugin/shell-hook-http.js +163 -0
  63. package/dist/plugin/shell-hook-http.js.map +1 -0
  64. package/dist/plugin/shell-hook.d.ts +93 -11
  65. package/dist/plugin/shell-hook.d.ts.map +1 -1
  66. package/dist/plugin/shell-hook.js +223 -39
  67. package/dist/plugin/shell-hook.js.map +1 -1
  68. package/dist/prompt/coding-agent-doctrine.d.ts +1 -1
  69. package/dist/prompt/coding-agent-doctrine.d.ts.map +1 -1
  70. package/dist/prompt/coding-agent-doctrine.js +2 -1
  71. package/dist/prompt/coding-agent-doctrine.js.map +1 -1
  72. package/dist/public-runtime.d.ts +3 -2
  73. package/dist/public-runtime.d.ts.map +1 -1
  74. package/dist/public-runtime.js +2 -2
  75. package/dist/public-runtime.js.map +1 -1
  76. package/dist/public-tools.d.ts +1 -0
  77. package/dist/public-tools.d.ts.map +1 -1
  78. package/dist/public-tools.js +2 -0
  79. package/dist/public-tools.js.map +1 -1
  80. package/dist/public-types.d.ts +1 -1
  81. package/dist/public-types.d.ts.map +1 -1
  82. package/dist/runtime/query/executor.d.ts +4 -0
  83. package/dist/runtime/query/executor.d.ts.map +1 -1
  84. package/dist/runtime/query/executor.js +47 -9
  85. package/dist/runtime/query/executor.js.map +1 -1
  86. package/dist/runtime/query/index.d.ts +8 -0
  87. package/dist/runtime/query/index.d.ts.map +1 -1
  88. package/dist/runtime/query/index.js +29 -1
  89. package/dist/runtime/query/index.js.map +1 -1
  90. package/dist/runtime/query/iteration/index.d.ts.map +1 -1
  91. package/dist/runtime/query/iteration/index.js +6 -2
  92. package/dist/runtime/query/iteration/index.js.map +1 -1
  93. package/dist/runtime/query/iteration/phases/compaction-breaker.d.ts +18 -8
  94. package/dist/runtime/query/iteration/phases/compaction-breaker.d.ts.map +1 -1
  95. package/dist/runtime/query/iteration/phases/compaction-breaker.js +33 -9
  96. package/dist/runtime/query/iteration/phases/compaction-breaker.js.map +1 -1
  97. package/dist/runtime/query/iteration/phases/compaction.d.ts.map +1 -1
  98. package/dist/runtime/query/iteration/phases/compaction.js +52 -5
  99. package/dist/runtime/query/iteration/phases/compaction.js.map +1 -1
  100. package/dist/runtime/query/iteration/phases/context.d.ts +5 -0
  101. package/dist/runtime/query/iteration/phases/context.d.ts.map +1 -1
  102. package/dist/runtime/query/iteration/phases/context.js.map +1 -1
  103. package/dist/runtime/query/iteration/phases/tool-review.d.ts.map +1 -1
  104. package/dist/runtime/query/iteration/phases/tool-review.js +70 -3
  105. package/dist/runtime/query/iteration/phases/tool-review.js.map +1 -1
  106. package/dist/runtime/query/path-escalation.d.ts +2 -0
  107. package/dist/runtime/query/path-escalation.d.ts.map +1 -1
  108. package/dist/runtime/query/path-escalation.js +7 -4
  109. package/dist/runtime/query/path-escalation.js.map +1 -1
  110. package/dist/runtime/query/plugin-hooks.js +2 -2
  111. package/dist/runtime/query/plugin-hooks.js.map +1 -1
  112. package/dist/runtime/query/prepare-turn.d.ts +2 -0
  113. package/dist/runtime/query/prepare-turn.d.ts.map +1 -1
  114. package/dist/runtime/query/prepare-turn.js +25 -0
  115. package/dist/runtime/query/prepare-turn.js.map +1 -1
  116. package/dist/runtime/query/review-policy.d.ts +3 -2
  117. package/dist/runtime/query/review-policy.d.ts.map +1 -1
  118. package/dist/runtime/query/review-policy.js +32 -13
  119. package/dist/runtime/query/review-policy.js.map +1 -1
  120. package/dist/scheduler/local.js +1 -1
  121. package/dist/scheduler/local.js.map +1 -1
  122. package/dist/session/retention/archive.js +1 -1
  123. package/dist/session/retention/archive.js.map +1 -1
  124. package/dist/session/workspace/driver.d.ts +6 -0
  125. package/dist/session/workspace/driver.d.ts.map +1 -1
  126. package/dist/session/workspace/git-worktree.d.ts.map +1 -1
  127. package/dist/session/workspace/git-worktree.js +28 -0
  128. package/dist/session/workspace/git-worktree.js.map +1 -1
  129. package/dist/skills/index.d.ts +1 -1
  130. package/dist/skills/index.d.ts.map +1 -1
  131. package/dist/skills/index.js +1 -1
  132. package/dist/skills/index.js.map +1 -1
  133. package/dist/skills/loader.d.ts +7 -0
  134. package/dist/skills/loader.d.ts.map +1 -1
  135. package/dist/skills/loader.js +53 -0
  136. package/dist/skills/loader.js.map +1 -1
  137. package/dist/skills/registry.d.ts +1 -1
  138. package/dist/skills/registry.d.ts.map +1 -1
  139. package/dist/skills/registry.js +2 -1
  140. package/dist/skills/registry.js.map +1 -1
  141. package/dist/tools/builtins/bash-cwd.d.ts +52 -0
  142. package/dist/tools/builtins/bash-cwd.d.ts.map +1 -0
  143. package/dist/tools/builtins/bash-cwd.js +127 -0
  144. package/dist/tools/builtins/bash-cwd.js.map +1 -0
  145. package/dist/tools/builtins/bash.d.ts.map +1 -1
  146. package/dist/tools/builtins/bash.js +41 -6
  147. package/dist/tools/builtins/bash.js.map +1 -1
  148. package/dist/tools/builtins/bounded-file-read.d.ts +10 -0
  149. package/dist/tools/builtins/bounded-file-read.d.ts.map +1 -0
  150. package/dist/tools/builtins/bounded-file-read.js +67 -0
  151. package/dist/tools/builtins/bounded-file-read.js.map +1 -0
  152. package/dist/tools/builtins/grep.d.ts +6 -1
  153. package/dist/tools/builtins/grep.d.ts.map +1 -1
  154. package/dist/tools/builtins/grep.js +187 -24
  155. package/dist/tools/builtins/grep.js.map +1 -1
  156. package/dist/tools/builtins/read-file.d.ts +1 -0
  157. package/dist/tools/builtins/read-file.d.ts.map +1 -1
  158. package/dist/tools/builtins/read-file.js +113 -28
  159. package/dist/tools/builtins/read-file.js.map +1 -1
  160. package/dist/tools/builtins/read-media.d.ts +68 -0
  161. package/dist/tools/builtins/read-media.d.ts.map +1 -0
  162. package/dist/tools/builtins/read-media.js +397 -0
  163. package/dist/tools/builtins/read-media.js.map +1 -0
  164. package/dist/tools/builtins/search-tools.js +1 -1
  165. package/dist/tools/builtins/search-tools.js.map +1 -1
  166. package/dist/tools/builtins/skill.d.ts +4 -2
  167. package/dist/tools/builtins/skill.d.ts.map +1 -1
  168. package/dist/tools/builtins/skill.js +61 -2
  169. package/dist/tools/builtins/skill.js.map +1 -1
  170. package/dist/tools/builtins/sleep-guard.d.ts +15 -0
  171. package/dist/tools/builtins/sleep-guard.d.ts.map +1 -0
  172. package/dist/tools/builtins/sleep-guard.js +33 -0
  173. package/dist/tools/builtins/sleep-guard.js.map +1 -0
  174. package/dist/tools/builtins/view-image.d.ts +23 -1
  175. package/dist/tools/builtins/view-image.d.ts.map +1 -1
  176. package/dist/tools/builtins/view-image.js +66 -58
  177. package/dist/tools/builtins/view-image.js.map +1 -1
  178. package/dist/tools/file-read-tracker.d.ts.map +1 -1
  179. package/dist/tools/file-read-tracker.js +31 -0
  180. package/dist/tools/file-read-tracker.js.map +1 -1
  181. package/dist/toolsets/manager.js +3 -3
  182. package/dist/toolsets/manager.js.map +1 -1
  183. package/dist/types/agent/base.d.ts +8 -0
  184. package/dist/types/agent/base.d.ts.map +1 -1
  185. package/dist/types/agent/query.d.ts +2 -0
  186. package/dist/types/agent/query.d.ts.map +1 -1
  187. package/dist/types/agent/scheduler.d.ts +8 -0
  188. package/dist/types/agent/scheduler.d.ts.map +1 -1
  189. package/dist/types/agent/task.d.ts +6 -2
  190. package/dist/types/agent/task.d.ts.map +1 -1
  191. package/dist/types/agent/task.js.map +1 -1
  192. package/dist/types/connector/mcp.d.ts +46 -0
  193. package/dist/types/connector/mcp.d.ts.map +1 -1
  194. package/dist/types/hitl/index.d.ts +1 -1
  195. package/dist/types/plugin/index.d.ts +74 -7
  196. package/dist/types/plugin/index.d.ts.map +1 -1
  197. package/dist/types/plugin/index.js +12 -1
  198. package/dist/types/plugin/index.js.map +1 -1
  199. package/dist/types/session/sub-session.d.ts +6 -2
  200. package/dist/types/session/sub-session.d.ts.map +1 -1
  201. package/dist/types/skills/index.d.ts +36 -0
  202. package/dist/types/skills/index.d.ts.map +1 -1
  203. package/dist/types/skills/index.js +9 -0
  204. package/dist/types/skills/index.js.map +1 -1
  205. package/dist/types/tool/index.d.ts +37 -0
  206. package/dist/types/tool/index.d.ts.map +1 -1
  207. package/dist/types/tool/index.js.map +1 -1
  208. package/dist/types/workspace/ref.d.ts +2 -0
  209. package/dist/types/workspace/ref.d.ts.map +1 -1
  210. package/dist/utils/frontmatter.d.ts +12 -0
  211. package/dist/utils/frontmatter.d.ts.map +1 -1
  212. package/dist/utils/frontmatter.js +6 -0
  213. package/dist/utils/frontmatter.js.map +1 -1
  214. package/package.json +1 -1
  215. package/src/agents/QueryAgent.ts +3 -0
  216. package/src/agents/file-definitions.ts +126 -6
  217. package/src/authorization/protected-paths.ts +30 -2
  218. package/src/compaction/index.ts +1 -0
  219. package/src/compaction/manual.ts +68 -30
  220. package/src/compaction/model-summary.ts +412 -0
  221. package/src/compaction/verifier.ts +3 -3
  222. package/src/config/runtime.ts +16 -0
  223. package/src/connector/mcp/client.ts +87 -1
  224. package/src/constants/mcp/index.ts +3 -0
  225. package/src/constants/plugin/index.ts +2 -0
  226. package/src/manager/agent/lifecycle.ts +24 -5
  227. package/src/manager/session/turn-recorder.ts +2 -1
  228. package/src/persona/assembler.ts +2 -2
  229. package/src/plugin/index.ts +5 -0
  230. package/src/plugin/lifecycle.ts +16 -2
  231. package/src/plugin/shell-hook-http.ts +181 -0
  232. package/src/plugin/shell-hook.ts +386 -89
  233. package/src/prompt/coding-agent-doctrine.ts +2 -1
  234. package/src/public-runtime.ts +6 -0
  235. package/src/public-tools.ts +2 -0
  236. package/src/public-types.ts +5 -0
  237. package/src/runtime/query/executor.ts +54 -10
  238. package/src/runtime/query/index.ts +44 -0
  239. package/src/runtime/query/iteration/index.ts +11 -3
  240. package/src/runtime/query/iteration/phases/compaction-breaker.ts +38 -9
  241. package/src/runtime/query/iteration/phases/compaction.ts +67 -5
  242. package/src/runtime/query/iteration/phases/context.ts +6 -0
  243. package/src/runtime/query/iteration/phases/tool-review.ts +74 -3
  244. package/src/runtime/query/path-escalation.ts +11 -5
  245. package/src/runtime/query/plugin-hooks.ts +2 -2
  246. package/src/runtime/query/prepare-turn.ts +33 -0
  247. package/src/runtime/query/review-policy.ts +45 -16
  248. package/src/scheduler/local.ts +1 -1
  249. package/src/session/retention/archive.ts +1 -1
  250. package/src/session/workspace/driver.ts +6 -0
  251. package/src/session/workspace/git-worktree.ts +31 -0
  252. package/src/skills/index.ts +6 -1
  253. package/src/skills/loader.ts +54 -0
  254. package/src/skills/registry.ts +7 -6
  255. package/src/tools/builtins/bash-cwd.ts +156 -0
  256. package/src/tools/builtins/bash.ts +79 -34
  257. package/src/tools/builtins/bounded-file-read.ts +80 -0
  258. package/src/tools/builtins/grep.ts +198 -28
  259. package/src/tools/builtins/read-file.ts +151 -30
  260. package/src/tools/builtins/read-media.ts +483 -0
  261. package/src/tools/builtins/search-tools.ts +1 -1
  262. package/src/tools/builtins/skill.ts +91 -2
  263. package/src/tools/builtins/sleep-guard.ts +31 -0
  264. package/src/tools/builtins/view-image.ts +74 -62
  265. package/src/tools/file-read-tracker.ts +30 -0
  266. package/src/toolsets/manager.ts +3 -3
  267. package/src/types/agent/base.ts +9 -0
  268. package/src/types/agent/query.ts +2 -0
  269. package/src/types/agent/scheduler.ts +9 -0
  270. package/src/types/agent/task.ts +6 -2
  271. package/src/types/connector/mcp.ts +48 -0
  272. package/src/types/hitl/index.ts +1 -1
  273. package/src/types/plugin/index.ts +102 -16
  274. package/src/types/session/sub-session.ts +6 -2
  275. package/src/types/skills/index.ts +48 -0
  276. package/src/types/tool/index.ts +38 -0
  277. package/src/types/workspace/ref.ts +2 -0
  278. package/src/utils/frontmatter.ts +21 -0
@@ -14,13 +14,20 @@
14
14
  * name: reviewer
15
15
  * description: Reviews a diff for correctness and convention drift.
16
16
  * tools: read, grep, glob # optional allowlist, intersected with the parent's set
17
- * model: claude-sonnet-4-5 # optional override
17
+ * disallowedTools: bash # optional; removed from whatever the rest allows
18
+ * model: claude-sonnet-4-5 # optional override; `inherit` keeps the session's
19
+ * effort: high # optional reasoning effort
20
+ * maxTurns: 20 # optional cap on the agent's own turns
21
+ * background: true # optional; runs in the background unless the call says otherwise
22
+ * isolation: worktree # optional; its own Git checkout
23
+ * skills: release-notes # optional; skills loaded into its prompt
18
24
  * readOnly: true # optional; keeps only read-only tools
25
+ * permissionMode: plan # optional; a mode name the host lists
19
26
  * ---
20
27
  * You review changes for this repository. Cite file:line for every finding …
21
28
  * ```
22
29
  *
23
- * `tools` is one comma-separated line: this kernel's frontmatter reader
30
+ * `tools`, `disallowedTools` and `skills` are each one comma-separated line: this kernel's frontmatter reader
24
31
  * refuses block lists and flow sequences by design, so there is no other
25
32
  * form to accept.
26
33
  *
@@ -36,6 +43,7 @@
36
43
  import { readFile, readdir, stat } from 'node:fs/promises'
37
44
  import { join } from 'node:path'
38
45
 
46
+ import type { ReasoningEffort } from '../types/provider/chat.js'
39
47
  import { parseFrontmatter } from '../utils/frontmatter.js'
40
48
  import { SCOPE_ATTRIBUTE } from '../utils/log/types.js'
41
49
  import { type Logger, resolveLogger } from '../utils/logger.js'
@@ -52,6 +60,17 @@ export const DEFAULT_RESERVED_AGENT_NAMES: ReadonlySet<string> = new Set([
52
60
  'explore',
53
61
  ])
54
62
 
63
+ const EFFORT_LEVELS: ReadonlySet<string> = new Set<ReasoningEffort>([
64
+ 'none',
65
+ 'minimal',
66
+ 'low',
67
+ 'medium',
68
+ 'high',
69
+ 'xhigh',
70
+ 'max',
71
+ 'ultra',
72
+ ])
73
+
55
74
  const NAME_PATTERN = /^[a-z][a-z0-9-]{0,63}$/
56
75
 
57
76
  export interface AgentFileDefinition {
@@ -61,7 +80,25 @@ export interface AgentFileDefinition {
61
80
  readonly prompt: string
62
81
  /** Tool names the agent may use; absent means the parent's working set. */
63
82
  readonly tools?: readonly string[]
83
+ /** Tool names removed from the roster whatever else allows them. */
84
+ readonly disallowedTools?: readonly string[]
85
+ /** Absent means the session's model; `model: inherit` in a file is the same thing. */
64
86
  readonly model?: string
87
+ /** Reasoning effort the agent runs with, when its model supports it. */
88
+ readonly effort?: ReasoningEffort
89
+ /** The most turns the agent takes before it stops; a positive whole number. */
90
+ readonly maxTurns?: number
91
+ /** The agent runs in the background when the call does not say. */
92
+ readonly background?: boolean
93
+ /** `worktree`: the agent works in its own Git checkout. */
94
+ readonly isolation?: 'worktree'
95
+ /** Skills whose instructions are loaded into the agent's prompt. */
96
+ readonly skills?: readonly string[]
97
+ /**
98
+ * The permission mode the file asks for, as the word the host accepts. The
99
+ * host decides what it may do with it; the kernel only keeps it.
100
+ */
101
+ readonly permissionMode?: string
65
102
  readonly readOnly: boolean
66
103
  readonly path: string
67
104
  /** The host's label for the root this file came from (`user`, `project`, …). */
@@ -87,6 +124,11 @@ export interface DiscoveredAgentDefinitions {
87
124
  export interface DiscoverAgentDefinitionsOptions {
88
125
  /** Names refused in a file. Defaults to `DEFAULT_RESERVED_AGENT_NAMES`. */
89
126
  readonly reserved?: ReadonlySet<string>
127
+ /**
128
+ * The words a `permissionMode:` line may use. A file naming any other word
129
+ * is reported and skipped. Absent: any non-empty word is kept.
130
+ */
131
+ readonly permissionModes?: ReadonlySet<string>
90
132
  readonly log?: Logger
91
133
  }
92
134
 
@@ -114,7 +156,12 @@ export async function discoverAgentDefinitions(
114
156
  continue
115
157
  }
116
158
  for (const file of files) {
117
- const found = await parseAgentFile(join(root.dir, file), root.source, options.reserved)
159
+ const found = await parseAgentFile(
160
+ join(root.dir, file),
161
+ root.source,
162
+ options.reserved,
163
+ options.permissionModes,
164
+ )
118
165
  if ('reason' in found) skipped.push(found)
119
166
  else byName.set(found.name, found)
120
167
  }
@@ -126,6 +173,7 @@ export async function parseAgentFile(
126
173
  path: string,
127
174
  source: string,
128
175
  reserved: ReadonlySet<string> = DEFAULT_RESERVED_AGENT_NAMES,
176
+ permissionModes?: ReadonlySet<string>,
129
177
  ): Promise<AgentFileDefinition | SkippedAgentFile> {
130
178
  let raw: string
131
179
  try {
@@ -139,7 +187,7 @@ export async function parseAgentFile(
139
187
  reason: `could not be read: ${error instanceof Error ? error.message : String(error)}`,
140
188
  }
141
189
  }
142
- return parseAgentMarkdown(raw, path, source, reserved)
190
+ return parseAgentMarkdown(raw, path, source, reserved, permissionModes)
143
191
  }
144
192
 
145
193
  /** Pure: the file's text to a definition, or the reason it is refused. */
@@ -148,6 +196,7 @@ export function parseAgentMarkdown(
148
196
  path: string,
149
197
  source: string,
150
198
  reserved: ReadonlySet<string> = DEFAULT_RESERVED_AGENT_NAMES,
199
+ permissionModes?: ReadonlySet<string>,
151
200
  ): AgentFileDefinition | SkippedAgentFile {
152
201
  if (!raw.trimStart().startsWith('---')) {
153
202
  return { path, reason: 'has no frontmatter; an agent file starts with `---` and a `name:`' }
@@ -184,10 +233,74 @@ export function parseAgentMarkdown(
184
233
  }
185
234
  tools = list
186
235
  }
187
- const model = scalar(values.model)
188
- if (values.model !== undefined && (model === undefined || model.trim().length === 0)) {
236
+ const listed = (key: 'disallowedTools' | 'skills') => {
237
+ const value = values[key]
238
+ if (value === undefined) return { ok: true as const, list: undefined }
239
+ const list = stringList(value)
240
+ if (list === undefined || list.length === 0) {
241
+ return { ok: false as const, reason: `\`${key}:\` must be a non-empty, comma-separated list` }
242
+ }
243
+ return { ok: true as const, list }
244
+ }
245
+ const denied = listed('disallowedTools')
246
+ if (!denied.ok) return { path, reason: denied.reason }
247
+ const preloaded = listed('skills')
248
+ if (!preloaded.ok) return { path, reason: preloaded.reason }
249
+
250
+ const modelText = scalar(values.model)
251
+ if (values.model !== undefined && (modelText === undefined || modelText.trim().length === 0)) {
189
252
  return { path, reason: '`model:` must be a model name' }
190
253
  }
254
+ // `inherit` is the same as no line: the session's own model.
255
+ const model = modelText?.trim().toLowerCase() === 'inherit' ? undefined : modelText
256
+ let effort: ReasoningEffort | undefined
257
+ if (values.effort !== undefined) {
258
+ const level = scalar(values.effort)?.trim().toLowerCase()
259
+ if (level === undefined || !EFFORT_LEVELS.has(level)) {
260
+ return {
261
+ path,
262
+ reason: `\`effort:\` must be one of ${[...EFFORT_LEVELS].join(', ')}`,
263
+ }
264
+ }
265
+ effort = level as ReasoningEffort
266
+ }
267
+ let maxTurns: number | undefined
268
+ if (values.maxTurns !== undefined) {
269
+ const text = scalar(values.maxTurns)?.trim()
270
+ if (text === undefined || !/^[1-9][0-9]{0,8}$/.test(text)) {
271
+ return { path, reason: '`maxTurns:` must be a positive whole number' }
272
+ }
273
+ maxTurns = Number(text)
274
+ }
275
+ let background: boolean | undefined
276
+ if (values.background !== undefined) {
277
+ const flag = scalar(values.background)
278
+ if (flag !== 'true' && flag !== 'false') {
279
+ return { path, reason: '`background:` must be true or false' }
280
+ }
281
+ background = flag === 'true'
282
+ }
283
+ let isolation: 'worktree' | undefined
284
+ if (values.isolation !== undefined) {
285
+ if (scalar(values.isolation)?.trim() !== 'worktree') {
286
+ return { path, reason: '`isolation:` must be worktree' }
287
+ }
288
+ isolation = 'worktree'
289
+ }
290
+ let permissionMode: string | undefined
291
+ if (values.permissionMode !== undefined) {
292
+ const word = scalar(values.permissionMode)?.trim()
293
+ if (word === undefined || word.length === 0) {
294
+ return { path, reason: '`permissionMode:` must be a mode name' }
295
+ }
296
+ if (permissionModes && !permissionModes.has(word)) {
297
+ return {
298
+ path,
299
+ reason: `\`permissionMode: ${word}\` is not one of ${[...permissionModes].join(', ')}`,
300
+ }
301
+ }
302
+ permissionMode = word
303
+ }
191
304
  let readOnly = false
192
305
  if (values.readOnly !== undefined) {
193
306
  const flag = scalar(values.readOnly)
@@ -211,7 +324,14 @@ export function parseAgentMarkdown(
211
324
  description: description.trim(),
212
325
  prompt,
213
326
  ...(tools ? { tools } : {}),
327
+ ...(denied.list ? { disallowedTools: denied.list } : {}),
214
328
  ...(model ? { model: model.trim() } : {}),
329
+ ...(effort ? { effort } : {}),
330
+ ...(maxTurns !== undefined ? { maxTurns } : {}),
331
+ ...(background !== undefined ? { background } : {}),
332
+ ...(isolation ? { isolation } : {}),
333
+ ...(preloaded.list ? { skills: preloaded.list } : {}),
334
+ ...(permissionMode ? { permissionMode } : {}),
215
335
  readOnly,
216
336
  path,
217
337
  source,
@@ -79,11 +79,29 @@ export const PROTECTED_FILE_NAMES: readonly string[] = [
79
79
  'pyrightconfig.json',
80
80
  ]
81
81
 
82
+ /**
83
+ * Folders under the project directory that hold what a person or the model
84
+ * authors on purpose (skills, agent definitions, commands) and the checkouts
85
+ * of delegated work. Writing there is the point of the folder, so the project
86
+ * directory is not protected on their account. Only the first project
87
+ * directory in a path can be excused: a second one deeper down, or any other
88
+ * protected name below, still protects the path.
89
+ */
90
+ export const PROJECT_DIRECTORY_AUTHORED_FOLDERS: readonly string[] = [
91
+ 'skills',
92
+ 'agents',
93
+ 'commands',
94
+ 'worktrees',
95
+ ]
96
+
97
+ const PROJECT_DIRECTORY = '.namzu'
98
+
82
99
  /** Lower case, with the two letters that fold into ASCII on some file systems. */
83
100
  function fold(name: string): string {
84
101
  return name.toLowerCase().replace(/ı/g, 'i').replace(/ſ/g, 's')
85
102
  }
86
103
 
104
+ const AUTHORED = new Set(PROJECT_DIRECTORY_AUTHORED_FOLDERS.map(fold))
87
105
  const DIRECTORIES = new Set(PROTECTED_DIRECTORIES.map(fold))
88
106
  const FILE_NAMES = new Set(PROTECTED_FILE_NAMES.map(fold))
89
107
  const NESTED = PROTECTED_NESTED_DIRECTORIES.map((sequence) => sequence.map(fold))
@@ -95,8 +113,18 @@ const NESTED = PROTECTED_NESTED_DIRECTORIES.map((sequence) => sequence.map(fold)
95
113
  */
96
114
  export function isProtectedPath(path: string): boolean {
97
115
  const segments = path.split(/[\\/]+/).map(fold)
98
- for (const segment of segments) {
99
- if (DIRECTORIES.has(segment)) return true
116
+ let excused = false
117
+ for (let index = 0; index < segments.length; index += 1) {
118
+ const segment = segments[index] as string
119
+ if (!DIRECTORIES.has(segment)) continue
120
+ if (segment === PROJECT_DIRECTORY && !excused) {
121
+ const next = segments[index + 1]
122
+ if (next !== undefined && AUTHORED.has(next)) {
123
+ excused = true
124
+ continue
125
+ }
126
+ }
127
+ return true
100
128
  }
101
129
  for (const sequence of NESTED) {
102
130
  for (let start = 0; start + sequence.length <= segments.length; start += 1) {
@@ -41,6 +41,7 @@ export {
41
41
  } from './extractor.js'
42
42
 
43
43
  export { buildVerifiedSummary } from './verifier.js'
44
+ export type { CompactionSummaryRoute, CompactionSummaryRouter } from './model-summary.js'
44
45
  export type { CompactionVerificationOptions } from './verifier.js'
45
46
 
46
47
  export {
@@ -16,6 +16,11 @@ import {
16
16
  extractFromUserMessage,
17
17
  } from './extractor.js'
18
18
  import { WorkingStateManager } from './manager.js'
19
+ import {
20
+ type CompactionSummaryRouter,
21
+ replaceableSummaries,
22
+ summarizeWithModel,
23
+ } from './model-summary.js'
19
24
  import { planCompaction } from './plan.js'
20
25
  import { recordRestorable, renderRestoredContext } from './restore.js'
21
26
  import { findRetainedIndices } from './retention.js'
@@ -72,6 +77,33 @@ export interface CompactNowInput extends CompactionVerificationOptions {
72
77
  readonly recoveryNote?: string
73
78
  /** Text the host wants carried across, such as where its plan file is; added to the restored context. */
74
79
  readonly restoreNote?: string
80
+ /**
81
+ * Names the model that writes the summary. Its tokens come back in
82
+ * {@link CompactionResult.usage}. Absent, or when its call fails, the
83
+ * summary is built from the extracted state as before.
84
+ */
85
+ readonly summaryRoute?: CompactionSummaryRouter
86
+ }
87
+
88
+ /** The model-written summary of `span` for a host-triggered pass, if one can be had. */
89
+ async function modelWrittenBody(
90
+ input: CompactNowInput,
91
+ manager: WorkingStateManager,
92
+ span: readonly Message[],
93
+ earlier: readonly Message[],
94
+ onUsage: (usage: TokenUsage) => void,
95
+ ): Promise<string | undefined> {
96
+ return summarizeWithModel({
97
+ manager,
98
+ olderMessages: span,
99
+ previousSummaries: replaceableSummaries(earlier),
100
+ config: input.config,
101
+ resolveRoute: input.summaryRoute,
102
+ onUsage,
103
+ signal: input.signal,
104
+ streamIdleTimeoutMs: input.streamIdleTimeoutMs,
105
+ instructions: input.instructions,
106
+ })
75
107
  }
76
108
 
77
109
  async function publishManualCompaction(
@@ -230,21 +262,24 @@ export async function compactNow(input: CompactNowInput): Promise<CompactionResu
230
262
  const manager = new WorkingStateManager(input.config)
231
263
  populateWorkingState(manager, plan.olderMessages, input.config)
232
264
  let usage: TokenUsage = { ...EMPTY_TOKEN_USAGE }
233
- const body = await buildVerifiedSummary(
234
- manager,
235
- [...plan.olderMessages],
236
- input.provider,
237
- input.config,
238
- (reported) => {
239
- usage = accumulateTokenUsage(usage, reported)
240
- },
241
- input.model ?? '',
242
- {
243
- signal: input.signal,
244
- streamIdleTimeoutMs: input.streamIdleTimeoutMs,
245
- instructions: input.instructions,
246
- },
247
- )
265
+ const onUsage = (reported: TokenUsage) => {
266
+ usage = accumulateTokenUsage(usage, reported)
267
+ }
268
+ const body =
269
+ (await modelWrittenBody(input, manager, plan.olderMessages, plan.systemMessages, onUsage)) ??
270
+ (await buildVerifiedSummary(
271
+ manager,
272
+ [...plan.olderMessages],
273
+ input.provider,
274
+ input.config,
275
+ onUsage,
276
+ input.model ?? '',
277
+ {
278
+ signal: input.signal,
279
+ streamIdleTimeoutMs: input.streamIdleTimeoutMs,
280
+ instructions: input.instructions,
281
+ },
282
+ ))
248
283
 
249
284
  const { messages, summary } = splice(
250
285
  preservedSystem,
@@ -322,21 +357,24 @@ export async function compactRegion(input: CompactRegionInput): Promise<Compacti
322
357
  const manager = new WorkingStateManager(input.config)
323
358
  populateWorkingState(manager, messages.slice(start, end), input.config)
324
359
  let usage: TokenUsage = { ...EMPTY_TOKEN_USAGE }
325
- const body = await buildVerifiedSummary(
326
- manager,
327
- messages.slice(start, end),
328
- input.provider,
329
- input.config,
330
- (reported) => {
331
- usage = accumulateTokenUsage(usage, reported)
332
- },
333
- input.model ?? '',
334
- {
335
- signal: input.signal,
336
- streamIdleTimeoutMs: input.streamIdleTimeoutMs,
337
- instructions: input.instructions,
338
- },
339
- )
360
+ const onUsage = (reported: TokenUsage) => {
361
+ usage = accumulateTokenUsage(usage, reported)
362
+ }
363
+ const body =
364
+ (await modelWrittenBody(input, manager, messages.slice(start, end), [], onUsage)) ??
365
+ (await buildVerifiedSummary(
366
+ manager,
367
+ messages.slice(start, end),
368
+ input.provider,
369
+ input.config,
370
+ onUsage,
371
+ input.model ?? '',
372
+ {
373
+ signal: input.signal,
374
+ streamIdleTimeoutMs: input.streamIdleTimeoutMs,
375
+ instructions: input.instructions,
376
+ },
377
+ ))
340
378
 
341
379
  // Same cross-run ownership as compactNow: a selected region has been
342
380
  // replaced outside a turn, so its only surviving account is pinned.