@cortexkit/opencode-magic-context 0.38.1 → 0.40.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 (172) hide show
  1. package/README.md +57 -4
  2. package/dist/config/index.d.ts.map +1 -1
  3. package/dist/config/profiles.d.ts +22 -0
  4. package/dist/config/profiles.d.ts.map +1 -0
  5. package/dist/config/project-security.d.ts +14 -6
  6. package/dist/config/project-security.d.ts.map +1 -1
  7. package/dist/config/raw-loader.d.ts +42 -0
  8. package/dist/config/raw-loader.d.ts.map +1 -0
  9. package/dist/config/schema/magic-context.d.ts +966 -531
  10. package/dist/config/schema/magic-context.d.ts.map +1 -1
  11. package/dist/features/magic-context/dreamer/classify.d.ts +3 -2
  12. package/dist/features/magic-context/dreamer/classify.d.ts.map +1 -1
  13. package/dist/features/magic-context/dreamer/evaluate-smart-notes.d.ts +9 -2
  14. package/dist/features/magic-context/dreamer/evaluate-smart-notes.d.ts.map +1 -1
  15. package/dist/features/magic-context/dreamer/map-memories-prompt.d.ts +4 -2
  16. package/dist/features/magic-context/dreamer/map-memories-prompt.d.ts.map +1 -1
  17. package/dist/features/magic-context/dreamer/map-memories.d.ts +21 -5
  18. package/dist/features/magic-context/dreamer/map-memories.d.ts.map +1 -1
  19. package/dist/features/magic-context/dreamer/refresh-primers.d.ts +3 -2
  20. package/dist/features/magic-context/dreamer/refresh-primers.d.ts.map +1 -1
  21. package/dist/features/magic-context/dreamer/task-config.d.ts +2 -1
  22. package/dist/features/magic-context/dreamer/task-config.d.ts.map +1 -1
  23. package/dist/features/magic-context/dreamer/task-executor.d.ts +0 -1
  24. package/dist/features/magic-context/dreamer/task-executor.d.ts.map +1 -1
  25. package/dist/features/magic-context/dreamer/task-scheduler.d.ts +3 -2
  26. package/dist/features/magic-context/dreamer/task-scheduler.d.ts.map +1 -1
  27. package/dist/features/magic-context/dreamer/verify-prompt.d.ts +5 -2
  28. package/dist/features/magic-context/dreamer/verify-prompt.d.ts.map +1 -1
  29. package/dist/features/magic-context/dreamer/verify.d.ts +6 -3
  30. package/dist/features/magic-context/dreamer/verify.d.ts.map +1 -1
  31. package/dist/features/magic-context/fail-closed-block.d.ts +11 -1
  32. package/dist/features/magic-context/fail-closed-block.d.ts.map +1 -1
  33. package/dist/features/magic-context/memory/embedding-failure.d.ts +11 -0
  34. package/dist/features/magic-context/memory/embedding-failure.d.ts.map +1 -0
  35. package/dist/features/magic-context/memory/embedding-openai.d.ts +5 -0
  36. package/dist/features/magic-context/memory/embedding-openai.d.ts.map +1 -1
  37. package/dist/features/magic-context/memory/embedding-provider.d.ts +3 -0
  38. package/dist/features/magic-context/memory/embedding-provider.d.ts.map +1 -1
  39. package/dist/features/magic-context/migrations.d.ts.map +1 -1
  40. package/dist/features/magic-context/mural/compress-cues.d.ts +3 -2
  41. package/dist/features/magic-context/mural/compress-cues.d.ts.map +1 -1
  42. package/dist/features/magic-context/project-embedding-registry.d.ts +2 -0
  43. package/dist/features/magic-context/project-embedding-registry.d.ts.map +1 -1
  44. package/dist/features/magic-context/smart-notes/compiler.d.ts +3 -2
  45. package/dist/features/magic-context/smart-notes/compiler.d.ts.map +1 -1
  46. package/dist/features/magic-context/smart-notes/condition-compiler.d.ts +2 -0
  47. package/dist/features/magic-context/smart-notes/condition-compiler.d.ts.map +1 -1
  48. package/dist/features/magic-context/smart-notes/sandbox-runner.d.ts.map +1 -1
  49. package/dist/features/magic-context/storage-db.d.ts +2 -2
  50. package/dist/features/magic-context/storage-db.d.ts.map +1 -1
  51. package/dist/features/magic-context/storage-meta-persisted.d.ts +7 -2
  52. package/dist/features/magic-context/storage-meta-persisted.d.ts.map +1 -1
  53. package/dist/features/magic-context/storage-meta-shared.d.ts +1 -0
  54. package/dist/features/magic-context/storage-meta-shared.d.ts.map +1 -1
  55. package/dist/features/magic-context/storage-meta.d.ts +1 -1
  56. package/dist/features/magic-context/storage-meta.d.ts.map +1 -1
  57. package/dist/features/magic-context/storage-session-tables.d.ts.map +1 -1
  58. package/dist/features/magic-context/storage-tags.d.ts.map +1 -1
  59. package/dist/features/magic-context/storage.d.ts +1 -1
  60. package/dist/features/magic-context/storage.d.ts.map +1 -1
  61. package/dist/features/magic-context/tool-definition-tokens.d.ts +10 -0
  62. package/dist/features/magic-context/tool-definition-tokens.d.ts.map +1 -1
  63. package/dist/features/magic-context/transform-decision-log.d.ts +11 -0
  64. package/dist/features/magic-context/transform-decision-log.d.ts.map +1 -1
  65. package/dist/features/magic-context/user-memory/review-user-memories.d.ts +3 -2
  66. package/dist/features/magic-context/user-memory/review-user-memories.d.ts.map +1 -1
  67. package/dist/hooks/magic-context/channel2-delivery.d.ts +3 -1
  68. package/dist/hooks/magic-context/channel2-delivery.d.ts.map +1 -1
  69. package/dist/hooks/magic-context/command-handler.d.ts +4 -0
  70. package/dist/hooks/magic-context/command-handler.d.ts.map +1 -1
  71. package/dist/hooks/magic-context/compartment-runner-historian.d.ts +4 -1
  72. package/dist/hooks/magic-context/compartment-runner-historian.d.ts.map +1 -1
  73. package/dist/hooks/magic-context/compartment-runner-partial-recomp.d.ts.map +1 -1
  74. package/dist/hooks/magic-context/compartment-runner-recomp.d.ts.map +1 -1
  75. package/dist/hooks/magic-context/compartment-runner-types.d.ts +4 -1
  76. package/dist/hooks/magic-context/compartment-runner-types.d.ts.map +1 -1
  77. package/dist/hooks/magic-context/ctx-reduce-nudge.d.ts +14 -2
  78. package/dist/hooks/magic-context/ctx-reduce-nudge.d.ts.map +1 -1
  79. package/dist/hooks/magic-context/emergency-fail-closed.d.ts +7 -0
  80. package/dist/hooks/magic-context/emergency-fail-closed.d.ts.map +1 -1
  81. package/dist/hooks/magic-context/format-embed-failure.d.ts +3 -0
  82. package/dist/hooks/magic-context/format-embed-failure.d.ts.map +1 -0
  83. package/dist/hooks/magic-context/hook-handlers.d.ts.map +1 -1
  84. package/dist/hooks/magic-context/hook.d.ts.map +1 -1
  85. package/dist/hooks/magic-context/inject-compartments.d.ts +13 -1
  86. package/dist/hooks/magic-context/inject-compartments.d.ts.map +1 -1
  87. package/dist/hooks/magic-context/lkg-persist.d.ts +14 -0
  88. package/dist/hooks/magic-context/lkg-persist.d.ts.map +1 -0
  89. package/dist/hooks/magic-context/lkg-slot.d.ts +13 -0
  90. package/dist/hooks/magic-context/lkg-slot.d.ts.map +1 -1
  91. package/dist/hooks/magic-context/module-state-sync.d.ts +1 -0
  92. package/dist/hooks/magic-context/module-state-sync.d.ts.map +1 -1
  93. package/dist/hooks/magic-context/module-transport.d.ts +12 -0
  94. package/dist/hooks/magic-context/module-transport.d.ts.map +1 -1
  95. package/dist/hooks/magic-context/raw-fallback-context-limit.d.ts.map +1 -1
  96. package/dist/hooks/magic-context/recomp-orchestrator.d.ts +4 -1
  97. package/dist/hooks/magic-context/recomp-orchestrator.d.ts.map +1 -1
  98. package/dist/hooks/magic-context/rust-mode-transform.d.ts.map +1 -1
  99. package/dist/hooks/magic-context/strip-content.d.ts +6 -0
  100. package/dist/hooks/magic-context/strip-content.d.ts.map +1 -1
  101. package/dist/hooks/magic-context/tail-hygiene-walk.d.ts +12 -0
  102. package/dist/hooks/magic-context/tail-hygiene-walk.d.ts.map +1 -1
  103. package/dist/hooks/magic-context/transform-compartment-phase.d.ts +3 -1
  104. package/dist/hooks/magic-context/transform-compartment-phase.d.ts.map +1 -1
  105. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts +5 -0
  106. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts.map +1 -1
  107. package/dist/hooks/magic-context/transform.d.ts +9 -1
  108. package/dist/hooks/magic-context/transform.d.ts.map +1 -1
  109. package/dist/hooks/magic-context/wrapup-orchestrator.d.ts.map +1 -1
  110. package/dist/{index-2y0ybv5x.js → index-3dkzzh0t.js} +15 -17
  111. package/dist/{index-5znjkjws.js → index-axpym70t.js} +879 -170
  112. package/dist/index-ehpzdy0v.js +126 -0
  113. package/dist/{index-22mw8km4.js → index-rw0qrkvq.js} +4 -2
  114. package/dist/index.d.ts.map +1 -1
  115. package/dist/index.js +18466 -16177
  116. package/dist/model-resolution-r9jbcz86.js +15 -0
  117. package/dist/plugin/dream-timer.d.ts +3 -0
  118. package/dist/plugin/dream-timer.d.ts.map +1 -1
  119. package/dist/plugin/hooks/create-session-hooks.d.ts +2 -0
  120. package/dist/plugin/hooks/create-session-hooks.d.ts.map +1 -1
  121. package/dist/plugin/messages-transform.d.ts +14 -6
  122. package/dist/plugin/messages-transform.d.ts.map +1 -1
  123. package/dist/plugin/rpc-handlers.d.ts.map +1 -1
  124. package/dist/{recomp-orchestrator-p030kxtc.js → recomp-orchestrator-dmyvqekw.js} +2 -3
  125. package/dist/shared/jsonc-edit.d.ts +6 -1
  126. package/dist/shared/jsonc-edit.d.ts.map +1 -1
  127. package/dist/shared/model-resolution.d.ts +53 -0
  128. package/dist/shared/model-resolution.d.ts.map +1 -0
  129. package/dist/shared/model-suggestion-retry.d.ts +4 -1
  130. package/dist/shared/model-suggestion-retry.d.ts.map +1 -1
  131. package/dist/shared/resolve-fallbacks.d.ts +5 -1
  132. package/dist/shared/resolve-fallbacks.d.ts.map +1 -1
  133. package/dist/shared/rpc-types.d.ts +2 -0
  134. package/dist/shared/rpc-types.d.ts.map +1 -1
  135. package/dist/shared/rpc-utils.d.ts +12 -1
  136. package/dist/shared/rpc-utils.d.ts.map +1 -1
  137. package/dist/shared/status-detail-text.d.ts +4 -0
  138. package/dist/shared/status-detail-text.d.ts.map +1 -0
  139. package/dist/shared/subagent-runner.d.ts +2 -1
  140. package/dist/shared/subagent-runner.d.ts.map +1 -1
  141. package/dist/{storage-meta-session-2306t3j7.js → storage-meta-session-yvtbaw7c.js} +1 -1
  142. package/dist/{task-config-at3adc59.js → task-config-a1qmj9bx.js} +2 -2
  143. package/dist/tui/data/context-db.d.ts.map +1 -1
  144. package/dist/tui-compiled/data/context-db.d.ts.map +1 -1
  145. package/package.json +4 -2
  146. package/src/config/index.test.ts +434 -19
  147. package/src/config/index.ts +67 -33
  148. package/src/config/profiles.ts +106 -0
  149. package/src/config/project-security.hostile-matrix.test.ts +480 -0
  150. package/src/config/project-security.test.ts +43 -5
  151. package/src/config/project-security.ts +151 -22
  152. package/src/config/raw-loader.test.ts +424 -0
  153. package/src/config/raw-loader.ts +490 -0
  154. package/src/config/schema/magic-context.test.ts +272 -2
  155. package/src/config/schema/magic-context.ts +342 -49
  156. package/src/shared/jsonc-edit.ts +74 -1
  157. package/src/shared/model-resolution.test.ts +170 -0
  158. package/src/shared/model-resolution.ts +227 -0
  159. package/src/shared/model-suggestion-retry.test.ts +27 -0
  160. package/src/shared/model-suggestion-retry.ts +19 -10
  161. package/src/shared/resolve-fallbacks.test.ts +13 -1
  162. package/src/shared/resolve-fallbacks.ts +19 -4
  163. package/src/shared/rpc-types.ts +2 -0
  164. package/src/shared/rpc-utils.test.ts +32 -0
  165. package/src/shared/rpc-utils.ts +38 -13
  166. package/src/shared/status-detail-text.test.ts +82 -0
  167. package/src/shared/status-detail-text.ts +57 -0
  168. package/src/shared/subagent-runner.ts +3 -1
  169. package/src/tui/data/context-db.ts +1 -0
  170. package/src/tui-compiled/data/context-db.ts +1 -0
  171. package/dist/index-td9depx5.js +0 -46
  172. package/dist/resolve-fallbacks-qf0ekmz9.js +0 -11
@@ -104,6 +104,286 @@ export const PromptSurfaceConfigSchema = z
104
104
  );
105
105
  export type PromptSurfaceConfig = z.infer<typeof PromptSurfaceConfigSchema>;
106
106
 
107
+ /**
108
+ * The flat model-resolution fields are the only fields that move into harness
109
+ * blocks. Keeping this inventory next to the schema gives the loader migration
110
+ * an exhaustive contract instead of a catch-all rule.
111
+ */
112
+ export const PER_HARNESS_MIGRATION_INVENTORY = {
113
+ historian: {
114
+ retained: [
115
+ "temperature",
116
+ "top_p",
117
+ "prompt",
118
+ "tools",
119
+ "disable",
120
+ "description",
121
+ "mode",
122
+ "color",
123
+ "maxSteps",
124
+ "permission",
125
+ "maxTokens",
126
+ "two_pass",
127
+ "disallowed_tools",
128
+ ],
129
+ migrated_execution: ["model", "fallback_models", "variant", "thinking_level"],
130
+ },
131
+ dreamer: {
132
+ retained: [
133
+ "temperature",
134
+ "top_p",
135
+ "prompt",
136
+ "tools",
137
+ "disable",
138
+ "description",
139
+ "mode",
140
+ "color",
141
+ "maxSteps",
142
+ "permission",
143
+ "maxTokens",
144
+ "inject_docs",
145
+ ],
146
+ migrated_execution: ["model", "fallback_models", "variant", "thinking_level"],
147
+ },
148
+ task: {
149
+ retained: ["schedule", "promotion_threshold"],
150
+ migrated_execution: [
151
+ "model",
152
+ "fallback_models",
153
+ "variant",
154
+ "thinking_level",
155
+ "timeout_minutes",
156
+ ],
157
+ },
158
+ } as const;
159
+
160
+ /** OpenCode entry objects permit `variant` and reject Pi-only `thinking_level`. */
161
+ const OcEntryObjectSchema = z
162
+ .object({
163
+ model: z.string().describe("OpenCode model ID (for example, provider/model)."),
164
+ variant: z.string().optional().describe("OpenCode reasoning variant for this entry."),
165
+ })
166
+ .strict();
167
+ export const OcEntrySchema = z.union([z.string(), OcEntryObjectSchema]);
168
+ export type OcEntry = z.infer<typeof OcEntrySchema>;
169
+
170
+ /** Pi entry objects permit `thinking_level` and reject OpenCode-only `variant`. */
171
+ const PiEntryObjectSchema = z
172
+ .object({
173
+ model: z.string().describe("Pi model ID (for example, provider/model)."),
174
+ thinking_level: PiThinkingLevelSchema.describe("Pi thinking level for this entry."),
175
+ })
176
+ .strict();
177
+ export const PiEntrySchema = z.union([z.string(), PiEntryObjectSchema]);
178
+ export type PiEntry = z.infer<typeof PiEntrySchema>;
179
+
180
+ /** Strict model-resolution block used by historian.opencode. */
181
+ export const OpenCodeHarnessBlockSchema = z
182
+ .object({
183
+ model: OcEntrySchema.optional().describe("Primary OpenCode model entry."),
184
+ fallback_models: z
185
+ .array(OcEntrySchema)
186
+ .optional()
187
+ .describe(
188
+ "Ordered fallback OpenCode entries. New-shape configuration requires an array; legacy singleton values migrate to a one-element array.",
189
+ ),
190
+ variant: z
191
+ .string()
192
+ .optional()
193
+ .describe(
194
+ "OpenCode reasoning variant for the primary entry when it declares none. Fallback entries declare variants per-entry.",
195
+ ),
196
+ })
197
+ .strict()
198
+ .describe("Strict OpenCode model-resolution block. It accepts no Pi vocabulary.");
199
+ export type OpenCodeHarnessBlock = z.infer<typeof OpenCodeHarnessBlockSchema>;
200
+
201
+ /** Strict model-resolution block used by historian.pi. */
202
+ export const PiHarnessBlockSchema = z
203
+ .object({
204
+ model: PiEntrySchema.optional().describe("Primary Pi model entry."),
205
+ fallback_models: z
206
+ .array(PiEntrySchema)
207
+ .optional()
208
+ .describe(
209
+ "Ordered fallback Pi entries. New-shape configuration requires an array; legacy singleton values migrate to a one-element array.",
210
+ ),
211
+ thinking_level: PiThinkingLevelSchema.describe(
212
+ "Pi thinking level for the primary entry when it declares none. Fallback entries declare thinking levels per-entry.",
213
+ ),
214
+ })
215
+ .strict()
216
+ .describe("Strict Pi model-resolution block. It accepts no OpenCode vocabulary.");
217
+ export type PiHarnessBlock = z.infer<typeof PiHarnessBlockSchema>;
218
+
219
+ /** Strict OpenCode-only execution override for one dreamer task. */
220
+ export const OpenCodeTaskExecutionSchema = z
221
+ .object({
222
+ model: OcEntrySchema.optional().describe("OpenCode model entry for this task."),
223
+ fallback_models: z
224
+ .array(OcEntrySchema)
225
+ .optional()
226
+ .describe("Ordered OpenCode fallback entries for this task."),
227
+ variant: z
228
+ .string()
229
+ .optional()
230
+ .describe(
231
+ "OpenCode reasoning variant for this task's primary entry when it declares none. Fallback entries declare variants per-entry.",
232
+ ),
233
+ timeout_minutes: z
234
+ .number()
235
+ .min(5)
236
+ .optional()
237
+ .describe("Minutes allowed for this task before it is aborted."),
238
+ })
239
+ .strict();
240
+ export type OpenCodeTaskExecution = z.infer<typeof OpenCodeTaskExecutionSchema>;
241
+
242
+ /** Strict Pi-only execution override for one dreamer task. */
243
+ export const PiTaskExecutionSchema = z
244
+ .object({
245
+ model: PiEntrySchema.optional().describe("Pi model entry for this task."),
246
+ fallback_models: z
247
+ .array(PiEntrySchema)
248
+ .optional()
249
+ .describe("Ordered Pi fallback entries for this task."),
250
+ thinking_level: PiThinkingLevelSchema.describe(
251
+ "Pi thinking level for this task's primary entry when it declares none. Fallback entries declare thinking levels per-entry.",
252
+ ),
253
+ timeout_minutes: z
254
+ .number()
255
+ .min(5)
256
+ .optional()
257
+ .describe("Minutes allowed for this task before it is aborted."),
258
+ })
259
+ .strict();
260
+ export type PiTaskExecution = z.infer<typeof PiTaskExecutionSchema>;
261
+
262
+ /** Strict OpenCode harness block for dreamer execution and task overrides. */
263
+ export const DreamerOpenCodeHarnessBlockSchema = z
264
+ .object({
265
+ model: OcEntrySchema.optional().describe("Primary OpenCode model entry."),
266
+ fallback_models: z
267
+ .array(OcEntrySchema)
268
+ .optional()
269
+ .describe(
270
+ "Ordered fallback OpenCode entries. New-shape configuration requires an array; legacy singleton values migrate to a one-element array.",
271
+ ),
272
+ variant: z
273
+ .string()
274
+ .optional()
275
+ .describe(
276
+ "OpenCode reasoning variant for the primary entry when it declares none. Fallback entries declare variants per-entry.",
277
+ ),
278
+ tasks: z
279
+ .record(z.string(), OpenCodeTaskExecutionSchema)
280
+ .optional()
281
+ .describe(
282
+ "OpenCode task execution overrides. Each named task accepts only model, fallback_models, variant, and timeout_minutes.",
283
+ ),
284
+ })
285
+ .strict()
286
+ .describe("Strict OpenCode dreamer model-resolution block. It accepts no Pi vocabulary.");
287
+ export type DreamerOpenCodeHarnessBlock = z.infer<typeof DreamerOpenCodeHarnessBlockSchema>;
288
+
289
+ /** Strict Pi harness block for dreamer execution and task overrides. */
290
+ export const DreamerPiHarnessBlockSchema = z
291
+ .object({
292
+ model: PiEntrySchema.optional().describe("Primary Pi model entry."),
293
+ fallback_models: z
294
+ .array(PiEntrySchema)
295
+ .optional()
296
+ .describe(
297
+ "Ordered fallback Pi entries. New-shape configuration requires an array; legacy singleton values migrate to a one-element array.",
298
+ ),
299
+ thinking_level: PiThinkingLevelSchema.describe(
300
+ "Pi thinking level for the primary entry when it declares none. Fallback entries declare thinking levels per-entry.",
301
+ ),
302
+ tasks: z
303
+ .record(z.string(), PiTaskExecutionSchema)
304
+ .optional()
305
+ .describe(
306
+ "Pi task execution overrides. Each named task accepts only model, fallback_models, thinking_level, and timeout_minutes.",
307
+ ),
308
+ })
309
+ .strict()
310
+ .describe("Strict Pi dreamer model-resolution block. It accepts no OpenCode vocabulary.");
311
+ export type DreamerPiHarnessBlock = z.infer<typeof DreamerPiHarnessBlockSchema>;
312
+
313
+ /**
314
+ * A profile may select models but must not alter execution policy. Keep this
315
+ * separate from the full harness schemas, whose dreamer blocks also admit task
316
+ * overrides such as timeout_minutes.
317
+ */
318
+ const ProfileOpenCodeModelBlockSchema = z
319
+ .object({
320
+ model: OcEntrySchema.optional().describe("Primary OpenCode model entry."),
321
+ fallback_models: z
322
+ .array(OcEntrySchema)
323
+ .optional()
324
+ .describe("Ordered fallback OpenCode model entries."),
325
+ variant: z
326
+ .string()
327
+ .optional()
328
+ .describe("OpenCode reasoning variant for the primary model entry."),
329
+ })
330
+ .strict()
331
+ .describe("Strict profile-only OpenCode model-selection block.");
332
+ const ProfilePiModelBlockSchema = z
333
+ .object({
334
+ model: PiEntrySchema.optional().describe("Primary Pi model entry."),
335
+ fallback_models: z
336
+ .array(PiEntrySchema)
337
+ .optional()
338
+ .describe("Ordered fallback Pi model entries."),
339
+ thinking_level: PiThinkingLevelSchema.describe(
340
+ "Pi thinking level for the primary model entry.",
341
+ ),
342
+ })
343
+ .strict()
344
+ .describe("Strict profile-only Pi model-selection block.");
345
+ const ProfileHistorianSchema = z
346
+ .object({
347
+ opencode: ProfileOpenCodeModelBlockSchema.optional(),
348
+ pi: ProfilePiModelBlockSchema.optional(),
349
+ })
350
+ .strict();
351
+ const ProfileDreamerSchema = z
352
+ .object({
353
+ opencode: ProfileOpenCodeModelBlockSchema.optional(),
354
+ pi: ProfilePiModelBlockSchema.optional(),
355
+ })
356
+ .strict();
357
+ const ProfileSidekickSchema = AgentOverrideConfigSchema.pick({
358
+ model: true,
359
+ fallback_models: true,
360
+ variant: true,
361
+ })
362
+ .extend({
363
+ thinking_level: PiThinkingLevelSchema.describe(
364
+ "Pi thinking level for the sidekick model selection.",
365
+ ),
366
+ })
367
+ .strict();
368
+
369
+ export const ConfigProfileSchema = z
370
+ .object({
371
+ historian: ProfileHistorianSchema.optional(),
372
+ dreamer: ProfileDreamerSchema.optional(),
373
+ sidekick: ProfileSidekickSchema.optional(),
374
+ })
375
+ .strict()
376
+ .describe(
377
+ "User-owned model-selection overlay. Only historian/dreamer harness model blocks and sidekick model-selection fields are allowed.",
378
+ );
379
+ export type ConfigProfile = z.infer<typeof ConfigProfileSchema>;
380
+
381
+ export const ConfigProfilesSchema = z.record(
382
+ z.string().trim().min(1, "Profile names must not be empty or whitespace-only."),
383
+ ConfigProfileSchema,
384
+ );
385
+ export type ConfigProfiles = z.infer<typeof ConfigProfilesSchema>;
386
+
107
387
  /** A 5-field cron expression, or "" to disable the task. */
108
388
  const CronScheduleSchema = z
109
389
  .string()
@@ -113,23 +393,15 @@ const CronScheduleSchema = z
113
393
  })
114
394
  .describe('5-field cron schedule (e.g. "0 3 * * *"), or "" to disable this task.');
115
395
 
116
- /** Per-task scheduling + model config. Model/fallback/thinking inherit the
117
- * dreamer-level defaults when omitted. Task-specific params (promotion_threshold)
118
- * are optional and only meaningful for their owning task. */
119
- const DreamTaskBaseConfigSchema = z.object({
120
- schedule: CronScheduleSchema.default(""),
121
- model: z.string().optional().describe("Per-task model override (inherits dreamer.model)"),
122
- fallback_models: z
123
- .union([z.string(), z.array(z.string())])
124
- .optional()
125
- .describe("Per-task fallback chain (inherits dreamer.fallback_models)"),
126
- thinking_level: PiThinkingLevelSchema.describe("Pi only: per-task thinking level"),
127
- timeout_minutes: z
128
- .number()
129
- .min(5)
130
- .default(20)
131
- .describe("Minutes allowed for this task before it is aborted"),
132
- });
396
+ /**
397
+ * Harness-independent task metadata. These fields stay at dreamer.tasks.<task>
398
+ * during per-harness migration; model execution fields are deliberately absent.
399
+ */
400
+ const DreamTaskBaseConfigSchema = z
401
+ .object({
402
+ schedule: CronScheduleSchema.default(""),
403
+ })
404
+ .strict();
133
405
 
134
406
  const PromotionThresholdSchema = z
135
407
  .number()
@@ -188,11 +460,8 @@ function defaultTaskConfig(task: DreamTaskName): z.input<typeof DreamTaskConfigS
188
460
  return base;
189
461
  }
190
462
 
191
- // `.default()` in Zod 4 wants the OUTPUT shape; the function form lets us derive
192
- // it by parsing the (partial) input default so timeout_minutes etc. fill in.
193
- /** The `tasks` record: one entry per canonical task, each defaulting to its
194
- * v1-behavior-preserving schedule. Written explicitly (not via fromEntries) so
195
- * the inferred type stays a precise per-key object. */
463
+ /** The harness-independent task metadata record. Schedule remains the only
464
+ * disable control; runtime gates are not configuration fields. */
196
465
  export const DreamTasksSchema = z
197
466
  .object({
198
467
  "map-memories": DreamTaskBaseConfigSchema.default(() =>
@@ -233,50 +502,59 @@ export const DreamTasksSchema = z
233
502
  ),
234
503
  })
235
504
  .describe(
236
- "Per-task scheduling + model config. Each task has its own cron schedule and may override the dreamer-level model.",
505
+ "Harness-independent task metadata. schedule, promotion_threshold, and other task metadata remain here; execution settings live under dreamer.opencode.tasks or dreamer.pi.tasks.",
237
506
  );
238
507
 
239
- /** Combined dreamer agent + per-task scheduling configuration (Dreamer v2). */
240
- export const DreamerConfigSchema = AgentOverrideConfigSchema.merge(
241
- z.object({
242
- tasks: DreamTasksSchema.default(() => DreamTasksSchema.parse({})),
243
- inject_docs: z
244
- .boolean()
245
- .default(true)
246
- .describe(
247
- "Inject ARCHITECTURE.md and STRUCTURE.md into the m[0] `<project-docs>` block (default true)",
248
- ),
249
- thinking_level: PiThinkingLevelSchema.describe(
250
- "Pi only: default thinking level for dreamer subagent invocations. See historian.thinking_level.",
508
+ const AgentMetadataSchema = AgentOverrideConfigSchema.pick({
509
+ temperature: true,
510
+ top_p: true,
511
+ prompt: true,
512
+ tools: true,
513
+ disable: true,
514
+ description: true,
515
+ mode: true,
516
+ color: true,
517
+ maxSteps: true,
518
+ permission: true,
519
+ maxTokens: true,
520
+ });
521
+
522
+ /** Combined dreamer metadata plus two independent strict execution blocks. */
523
+ export const DreamerConfigSchema = AgentMetadataSchema.extend({
524
+ opencode: DreamerOpenCodeHarnessBlockSchema.optional(),
525
+ pi: DreamerPiHarnessBlockSchema.optional(),
526
+ tasks: DreamTasksSchema.default(() => DreamTasksSchema.parse({})),
527
+ inject_docs: z
528
+ .boolean()
529
+ .default(true)
530
+ .describe(
531
+ "Inject ARCHITECTURE.md and STRUCTURE.md into the m[0] `<project-docs>` block (default true)",
251
532
  ),
252
- }),
253
- );
533
+ });
254
534
  export type DreamerConfig = z.infer<typeof DreamerConfigSchema>;
255
535
 
256
536
  export const SidekickConfigSchema = AgentOverrideConfigSchema.extend({
257
537
  timeout_ms: z.number().default(30000).describe("Timeout for sidekick calls in milliseconds"),
258
538
  system_prompt: z.string().optional().describe("Custom system prompt for sidekick"),
259
539
  thinking_level: PiThinkingLevelSchema.describe(
260
- "Pi only: explicit thinking level for sidekick subagent invocations. See historian.thinking_level.",
540
+ "Pi only: explicit thinking level for sidekick subagent invocations. See historian.pi.thinking_level.",
261
541
  ),
262
542
  }).optional();
263
543
  export type SidekickConfig = NonNullable<z.infer<typeof SidekickConfigSchema>>;
264
544
 
265
- /** Historian agent configuration — includes all agent overrides plus two_pass mode.
266
- * Two-pass mode runs a second editor pass after the initial historian pass to clean
267
- * up low-signal U: lines and cross-compartment duplicates. Recommended for models
268
- * without extended thinking; not needed for Claude Sonnet/Opus when reasoning is
269
- * enabled via OpenCode variant config. */
270
- export const HistorianConfigSchema = AgentOverrideConfigSchema.extend({
545
+ /**
546
+ * Historian metadata remains harness-independent. Only model resolution moves to
547
+ * the strict opencode and pi blocks; two_pass and disallowed_tools stay here.
548
+ */
549
+ export const HistorianConfigSchema = AgentMetadataSchema.extend({
550
+ opencode: OpenCodeHarnessBlockSchema.optional(),
551
+ pi: PiHarnessBlockSchema.optional(),
271
552
  two_pass: z
272
553
  .boolean()
273
554
  .default(false)
274
555
  .describe(
275
556
  "Run a second editor pass over historian output to clean low-signal U: lines and cross-compartment duplicates. Adds ~1 extra API call and ~1.3x cost per historian run. Useful for models without extended thinking support. (default: false)",
276
557
  ),
277
- thinking_level: PiThinkingLevelSchema.describe(
278
- "Pi only: explicit thinking level passed as --thinking <level> to Pi historian subagent invocations. Required when using reasoning models (e.g. github-copilot/gpt-5.4) because Pi's default thinking-level resolution can pick a value the provider rejects. OpenCode users set variant instead. Valid: off | minimal | low | medium | high | xhigh | max",
279
- ),
280
558
  disallowed_tools: z
281
559
  .array(z.enum(["*", "read", "aft_outline", "aft_zoom", "aft_search"]))
282
560
  .default([])
@@ -468,6 +746,10 @@ export interface MagicContextConfig {
468
746
  auto_update?: boolean;
469
747
  /** Output language for generated Magic Context prose. USER config only. */
470
748
  language?: string;
749
+ /** Active user-owned model profile after user/project resolution. */
750
+ profile?: string;
751
+ /** Named user-owned model profiles. Declarations are consumed during resolution. */
752
+ profiles?: ConfigProfiles;
471
753
  historian?: HistorianConfig;
472
754
  dreamer?: DreamerConfig;
473
755
  smart_notes: {
@@ -688,11 +970,22 @@ export const MagicContextConfigSchema = z
688
970
  "triggers one cache re-materialization; existing compartments/memories keep their " +
689
971
  "original language until naturally rewritten.",
690
972
  ),
973
+ profile: z
974
+ .string()
975
+ .trim()
976
+ .min(1)
977
+ .optional()
978
+ .describe(
979
+ "Select a named user-owned model profile. A valid project name overrides this user default; an empty string, null, or other non-string project value is ignored with a warning so the user selection still applies. Unknown names warn and use the base configuration.",
980
+ ),
981
+ profiles: ConfigProfilesSchema.optional().describe(
982
+ "User-level named model profiles. A profile may contain only historian/dreamer model, fallback_models, OpenCode variant, and Pi thinking_level fields plus sidekick model-selection fields; task execution policy (including timeout_minutes) is excluded. Project configs may select a name but cannot define profiles.",
983
+ ),
691
984
  historian: HistorianConfigSchema.describe(
692
- "Historian agent configuration (model, fallback_models, variant, temperature, maxTokens, permission, two_pass, etc.)",
985
+ "Historian metadata plus independent strict OpenCode and Pi execution blocks. Retained metadata stays at historian; model, fallback_models, variant, and thinking_level belong only in historian.opencode or historian.pi.",
693
986
  ),
694
987
  dreamer: DreamerConfigSchema.optional().describe(
695
- "Dreamer agent + scheduling configuration (model, fallback_models, disable, schedule, tasks, etc.)",
988
+ "Dreamer metadata and scheduling plus independent strict OpenCode and Pi execution blocks. schedule and promotion_threshold stay at dreamer.tasks; model, fallback_models, variant, thinking_level, and timeout_minutes belong only in the matching harness block.",
696
989
  ),
697
990
  smart_notes: z
698
991
  .object({
@@ -8,7 +8,14 @@ import {
8
8
  type Node,
9
9
  type ParseError,
10
10
  parseTree,
11
- } from "jsonc-parser";
11
+ } from "jsonc-parser/lib/esm/main.js";
12
+
13
+ // ^ Deep ESM import on purpose. jsonc-parser has no "exports" map, and its
14
+ // "main" points at a UMD build whose runtime-relative requires (./impl/format
15
+ // etc.) survive bundling verbatim and then fail to resolve from inside a
16
+ // bundled chunk. When this module became reachable from the plugin entry (the
17
+ // config raw-loader), that broken require made the ENTIRE plugin bundle fail
18
+ // to load at boot. The ESM build bundles statically and safely.
12
19
 
13
20
  interface TokenLocation {
14
21
  offset: number;
@@ -252,6 +259,72 @@ export function setJsoncValue(text: string, path: JSONPath, value: unknown): str
252
259
  return applyEdits(text, modify(text, path, value, {}));
253
260
  }
254
261
 
262
+ function removeObjectProperty(text: string, object: Node, key: string): string {
263
+ const properties = object.children ?? [];
264
+ const index = properties.findIndex((property) => {
265
+ const propertyKey = property.children?.[0];
266
+ return propertyKey !== undefined && getNodeValue(propertyKey) === key;
267
+ });
268
+ if (index === -1) return text;
269
+
270
+ const property = properties[index];
271
+ if (!property) return text;
272
+ const closingBrace = object.offset + object.length - 1;
273
+ const next = properties[index + 1];
274
+ if (next) {
275
+ const followingComma = findComma(text, property.offset + property.length, next.offset);
276
+ if (!followingComma) return text;
277
+ // Leading and trailing comments remain in their original positions. A
278
+ // comment previously adjacent to a migrated field therefore survives as
279
+ // standalone JSONC rather than being discarded with the old key.
280
+ return (
281
+ text.slice(0, property.offset) +
282
+ text.slice(followingComma.offset + followingComma.length)
283
+ );
284
+ }
285
+
286
+ const previous = properties[index - 1];
287
+ const trailingComma = findComma(text, property.offset + property.length, closingBrace);
288
+ if (!previous) {
289
+ const afterProperty = property.offset + property.length;
290
+ if (!trailingComma) return text.slice(0, property.offset) + text.slice(afterProperty);
291
+ return (
292
+ text.slice(0, property.offset) +
293
+ text.slice(afterProperty, trailingComma.offset) +
294
+ text.slice(trailingComma.offset + trailingComma.length)
295
+ );
296
+ }
297
+
298
+ const precedingComma = findComma(text, previous.offset + previous.length, property.offset);
299
+ if (!precedingComma) return text;
300
+ const afterProperty = property.offset + property.length;
301
+ const withoutProperty =
302
+ text.slice(0, precedingComma.offset) +
303
+ text.slice(precedingComma.offset + precedingComma.length, property.offset) +
304
+ text.slice(afterProperty);
305
+ if (!trailingComma) return withoutProperty;
306
+
307
+ // The trailing comma position is measured in the original string. Removing
308
+ // only one character before it (the preceding comma) shifts it left by one.
309
+ const shiftedTrailingComma = trailingComma.offset - 1;
310
+ return (
311
+ withoutProperty.slice(0, shiftedTrailingComma) +
312
+ withoutProperty.slice(shiftedTrailingComma + trailingComma.length)
313
+ );
314
+ }
315
+
316
+ /**
317
+ * Delete an object property without reserializing sibling fields. Comments are
318
+ * retained as JSONC trivia so key migrations do not erase a user's notes.
319
+ */
320
+ export function removeJsoncValue(text: string, path: JSONPath): string {
321
+ const key = path.at(-1);
322
+ if (typeof key !== "string") return text;
323
+ const parent = findNode(text, path.slice(0, -1));
324
+ if (parent?.type !== "object") return text;
325
+ return removeObjectProperty(text, parent, key);
326
+ }
327
+
255
328
  /**
256
329
  * Remove matching array entries while retaining the exact bytes for survivor
257
330
  * comments and surrounding JSONC regions.