@cortexkit/opencode-magic-context 0.38.1 → 0.39.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.
- package/README.md +37 -4
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/project-security.d.ts +11 -6
- package/dist/config/project-security.d.ts.map +1 -1
- package/dist/config/raw-loader.d.ts +42 -0
- package/dist/config/raw-loader.d.ts.map +1 -0
- package/dist/config/schema/magic-context.d.ts +585 -550
- package/dist/config/schema/magic-context.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/classify.d.ts +3 -2
- package/dist/features/magic-context/dreamer/classify.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/evaluate-smart-notes.d.ts +9 -2
- package/dist/features/magic-context/dreamer/evaluate-smart-notes.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/map-memories-prompt.d.ts +4 -2
- package/dist/features/magic-context/dreamer/map-memories-prompt.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/map-memories.d.ts +21 -5
- package/dist/features/magic-context/dreamer/map-memories.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/refresh-primers.d.ts +3 -2
- package/dist/features/magic-context/dreamer/refresh-primers.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/task-config.d.ts +2 -1
- package/dist/features/magic-context/dreamer/task-config.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/task-executor.d.ts +0 -1
- package/dist/features/magic-context/dreamer/task-executor.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/task-scheduler.d.ts +3 -2
- package/dist/features/magic-context/dreamer/task-scheduler.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/verify-prompt.d.ts +5 -2
- package/dist/features/magic-context/dreamer/verify-prompt.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/verify.d.ts +6 -3
- package/dist/features/magic-context/dreamer/verify.d.ts.map +1 -1
- package/dist/features/magic-context/migrations.d.ts.map +1 -1
- package/dist/features/magic-context/mural/compress-cues.d.ts +3 -2
- package/dist/features/magic-context/mural/compress-cues.d.ts.map +1 -1
- package/dist/features/magic-context/smart-notes/compiler.d.ts +3 -2
- package/dist/features/magic-context/smart-notes/compiler.d.ts.map +1 -1
- package/dist/features/magic-context/smart-notes/condition-compiler.d.ts +2 -0
- package/dist/features/magic-context/smart-notes/condition-compiler.d.ts.map +1 -1
- package/dist/features/magic-context/smart-notes/sandbox-runner.d.ts.map +1 -1
- package/dist/features/magic-context/storage-db.d.ts +1 -1
- package/dist/features/magic-context/storage-db.d.ts.map +1 -1
- package/dist/features/magic-context/storage-meta-persisted.d.ts +7 -2
- package/dist/features/magic-context/storage-meta-persisted.d.ts.map +1 -1
- package/dist/features/magic-context/storage-meta-shared.d.ts +1 -0
- package/dist/features/magic-context/storage-meta-shared.d.ts.map +1 -1
- package/dist/features/magic-context/storage-meta.d.ts +1 -1
- package/dist/features/magic-context/storage-meta.d.ts.map +1 -1
- package/dist/features/magic-context/storage-session-tables.d.ts.map +1 -1
- package/dist/features/magic-context/storage-tags.d.ts.map +1 -1
- package/dist/features/magic-context/storage.d.ts +1 -1
- package/dist/features/magic-context/storage.d.ts.map +1 -1
- package/dist/features/magic-context/tool-definition-tokens.d.ts +10 -0
- package/dist/features/magic-context/tool-definition-tokens.d.ts.map +1 -1
- package/dist/features/magic-context/transform-decision-log.d.ts +11 -0
- package/dist/features/magic-context/transform-decision-log.d.ts.map +1 -1
- package/dist/features/magic-context/user-memory/review-user-memories.d.ts +3 -2
- package/dist/features/magic-context/user-memory/review-user-memories.d.ts.map +1 -1
- package/dist/hooks/magic-context/channel2-delivery.d.ts +3 -1
- package/dist/hooks/magic-context/channel2-delivery.d.ts.map +1 -1
- package/dist/hooks/magic-context/command-handler.d.ts +4 -0
- package/dist/hooks/magic-context/command-handler.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-historian.d.ts +4 -1
- package/dist/hooks/magic-context/compartment-runner-historian.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-partial-recomp.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-recomp.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-types.d.ts +4 -1
- package/dist/hooks/magic-context/compartment-runner-types.d.ts.map +1 -1
- package/dist/hooks/magic-context/ctx-reduce-nudge.d.ts +14 -2
- package/dist/hooks/magic-context/ctx-reduce-nudge.d.ts.map +1 -1
- package/dist/hooks/magic-context/emergency-fail-closed.d.ts +7 -0
- package/dist/hooks/magic-context/emergency-fail-closed.d.ts.map +1 -1
- package/dist/hooks/magic-context/hook-handlers.d.ts.map +1 -1
- package/dist/hooks/magic-context/hook.d.ts.map +1 -1
- package/dist/hooks/magic-context/inject-compartments.d.ts +12 -1
- package/dist/hooks/magic-context/inject-compartments.d.ts.map +1 -1
- package/dist/hooks/magic-context/lkg-persist.d.ts +14 -0
- package/dist/hooks/magic-context/lkg-persist.d.ts.map +1 -0
- package/dist/hooks/magic-context/lkg-slot.d.ts +13 -0
- package/dist/hooks/magic-context/lkg-slot.d.ts.map +1 -1
- package/dist/hooks/magic-context/module-state-sync.d.ts +1 -0
- package/dist/hooks/magic-context/module-state-sync.d.ts.map +1 -1
- package/dist/hooks/magic-context/module-transport.d.ts +12 -0
- package/dist/hooks/magic-context/module-transport.d.ts.map +1 -1
- package/dist/hooks/magic-context/raw-fallback-context-limit.d.ts.map +1 -1
- package/dist/hooks/magic-context/recomp-orchestrator.d.ts +4 -1
- package/dist/hooks/magic-context/recomp-orchestrator.d.ts.map +1 -1
- package/dist/hooks/magic-context/rust-mode-transform.d.ts.map +1 -1
- package/dist/hooks/magic-context/tail-hygiene-walk.d.ts +12 -0
- package/dist/hooks/magic-context/tail-hygiene-walk.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform-compartment-phase.d.ts +3 -1
- package/dist/hooks/magic-context/transform-compartment-phase.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform-postprocess-phase.d.ts +4 -0
- package/dist/hooks/magic-context/transform-postprocess-phase.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform.d.ts +9 -1
- package/dist/hooks/magic-context/transform.d.ts.map +1 -1
- package/dist/hooks/magic-context/wrapup-orchestrator.d.ts.map +1 -1
- package/dist/{index-2y0ybv5x.js → index-3dkzzh0t.js} +15 -17
- package/dist/index-ehpzdy0v.js +126 -0
- package/dist/{index-5znjkjws.js → index-k2ts0wk1.js} +486 -106
- package/dist/{index-22mw8km4.js → index-rw0qrkvq.js} +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17186 -14853
- package/dist/model-resolution-r9jbcz86.js +15 -0
- package/dist/plugin/dream-timer.d.ts +3 -0
- package/dist/plugin/dream-timer.d.ts.map +1 -1
- package/dist/plugin/hooks/create-session-hooks.d.ts.map +1 -1
- package/dist/plugin/messages-transform.d.ts +14 -6
- package/dist/plugin/messages-transform.d.ts.map +1 -1
- package/dist/plugin/rpc-handlers.d.ts.map +1 -1
- package/dist/{recomp-orchestrator-p030kxtc.js → recomp-orchestrator-2pncxee7.js} +2 -3
- package/dist/shared/jsonc-edit.d.ts +6 -1
- package/dist/shared/jsonc-edit.d.ts.map +1 -1
- package/dist/shared/model-resolution.d.ts +53 -0
- package/dist/shared/model-resolution.d.ts.map +1 -0
- package/dist/shared/model-suggestion-retry.d.ts +4 -1
- package/dist/shared/model-suggestion-retry.d.ts.map +1 -1
- package/dist/shared/resolve-fallbacks.d.ts +5 -1
- package/dist/shared/resolve-fallbacks.d.ts.map +1 -1
- package/dist/shared/status-detail-text.d.ts +4 -0
- package/dist/shared/status-detail-text.d.ts.map +1 -0
- package/dist/shared/subagent-runner.d.ts +2 -1
- package/dist/shared/subagent-runner.d.ts.map +1 -1
- package/dist/{storage-meta-session-2306t3j7.js → storage-meta-session-yvtbaw7c.js} +1 -1
- package/dist/{task-config-at3adc59.js → task-config-a1qmj9bx.js} +2 -2
- package/package.json +4 -2
- package/src/config/index.test.ts +73 -17
- package/src/config/index.ts +38 -23
- package/src/config/project-security.hostile-matrix.test.ts +471 -0
- package/src/config/project-security.test.ts +26 -5
- package/src/config/project-security.ts +141 -22
- package/src/config/raw-loader.test.ts +424 -0
- package/src/config/raw-loader.ts +490 -0
- package/src/config/schema/magic-context.test.ts +144 -2
- package/src/config/schema/magic-context.ts +253 -49
- package/src/shared/jsonc-edit.ts +74 -1
- package/src/shared/model-resolution.test.ts +170 -0
- package/src/shared/model-resolution.ts +227 -0
- package/src/shared/model-suggestion-retry.test.ts +27 -0
- package/src/shared/model-suggestion-retry.ts +19 -10
- package/src/shared/resolve-fallbacks.test.ts +13 -1
- package/src/shared/resolve-fallbacks.ts +19 -4
- package/src/shared/status-detail-text.test.ts +80 -0
- package/src/shared/status-detail-text.ts +56 -0
- package/src/shared/subagent-runner.ts +3 -1
- package/dist/index-td9depx5.js +0 -46
- package/dist/resolve-fallbacks-qf0ekmz9.js +0 -11
|
@@ -104,6 +104,212 @@ 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
|
+
|
|
107
313
|
/** A 5-field cron expression, or "" to disable the task. */
|
|
108
314
|
const CronScheduleSchema = z
|
|
109
315
|
.string()
|
|
@@ -113,23 +319,15 @@ const CronScheduleSchema = z
|
|
|
113
319
|
})
|
|
114
320
|
.describe('5-field cron schedule (e.g. "0 3 * * *"), or "" to disable this task.');
|
|
115
321
|
|
|
116
|
-
/**
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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
|
-
});
|
|
322
|
+
/**
|
|
323
|
+
* Harness-independent task metadata. These fields stay at dreamer.tasks.<task>
|
|
324
|
+
* during per-harness migration; model execution fields are deliberately absent.
|
|
325
|
+
*/
|
|
326
|
+
const DreamTaskBaseConfigSchema = z
|
|
327
|
+
.object({
|
|
328
|
+
schedule: CronScheduleSchema.default(""),
|
|
329
|
+
})
|
|
330
|
+
.strict();
|
|
133
331
|
|
|
134
332
|
const PromotionThresholdSchema = z
|
|
135
333
|
.number()
|
|
@@ -188,11 +386,8 @@ function defaultTaskConfig(task: DreamTaskName): z.input<typeof DreamTaskConfigS
|
|
|
188
386
|
return base;
|
|
189
387
|
}
|
|
190
388
|
|
|
191
|
-
|
|
192
|
-
|
|
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. */
|
|
389
|
+
/** The harness-independent task metadata record. Schedule remains the only
|
|
390
|
+
* disable control; runtime gates are not configuration fields. */
|
|
196
391
|
export const DreamTasksSchema = z
|
|
197
392
|
.object({
|
|
198
393
|
"map-memories": DreamTaskBaseConfigSchema.default(() =>
|
|
@@ -233,50 +428,59 @@ export const DreamTasksSchema = z
|
|
|
233
428
|
),
|
|
234
429
|
})
|
|
235
430
|
.describe(
|
|
236
|
-
"
|
|
431
|
+
"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
432
|
);
|
|
238
433
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
434
|
+
const AgentMetadataSchema = AgentOverrideConfigSchema.pick({
|
|
435
|
+
temperature: true,
|
|
436
|
+
top_p: true,
|
|
437
|
+
prompt: true,
|
|
438
|
+
tools: true,
|
|
439
|
+
disable: true,
|
|
440
|
+
description: true,
|
|
441
|
+
mode: true,
|
|
442
|
+
color: true,
|
|
443
|
+
maxSteps: true,
|
|
444
|
+
permission: true,
|
|
445
|
+
maxTokens: true,
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
/** Combined dreamer metadata plus two independent strict execution blocks. */
|
|
449
|
+
export const DreamerConfigSchema = AgentMetadataSchema.extend({
|
|
450
|
+
opencode: DreamerOpenCodeHarnessBlockSchema.optional(),
|
|
451
|
+
pi: DreamerPiHarnessBlockSchema.optional(),
|
|
452
|
+
tasks: DreamTasksSchema.default(() => DreamTasksSchema.parse({})),
|
|
453
|
+
inject_docs: z
|
|
454
|
+
.boolean()
|
|
455
|
+
.default(true)
|
|
456
|
+
.describe(
|
|
457
|
+
"Inject ARCHITECTURE.md and STRUCTURE.md into the m[0] `<project-docs>` block (default true)",
|
|
251
458
|
),
|
|
252
|
-
|
|
253
|
-
);
|
|
459
|
+
});
|
|
254
460
|
export type DreamerConfig = z.infer<typeof DreamerConfigSchema>;
|
|
255
461
|
|
|
256
462
|
export const SidekickConfigSchema = AgentOverrideConfigSchema.extend({
|
|
257
463
|
timeout_ms: z.number().default(30000).describe("Timeout for sidekick calls in milliseconds"),
|
|
258
464
|
system_prompt: z.string().optional().describe("Custom system prompt for sidekick"),
|
|
259
465
|
thinking_level: PiThinkingLevelSchema.describe(
|
|
260
|
-
"Pi only: explicit thinking level for sidekick subagent invocations. See historian.thinking_level.",
|
|
466
|
+
"Pi only: explicit thinking level for sidekick subagent invocations. See historian.pi.thinking_level.",
|
|
261
467
|
),
|
|
262
468
|
}).optional();
|
|
263
469
|
export type SidekickConfig = NonNullable<z.infer<typeof SidekickConfigSchema>>;
|
|
264
470
|
|
|
265
|
-
/**
|
|
266
|
-
*
|
|
267
|
-
*
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
471
|
+
/**
|
|
472
|
+
* Historian metadata remains harness-independent. Only model resolution moves to
|
|
473
|
+
* the strict opencode and pi blocks; two_pass and disallowed_tools stay here.
|
|
474
|
+
*/
|
|
475
|
+
export const HistorianConfigSchema = AgentMetadataSchema.extend({
|
|
476
|
+
opencode: OpenCodeHarnessBlockSchema.optional(),
|
|
477
|
+
pi: PiHarnessBlockSchema.optional(),
|
|
271
478
|
two_pass: z
|
|
272
479
|
.boolean()
|
|
273
480
|
.default(false)
|
|
274
481
|
.describe(
|
|
275
482
|
"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
483
|
),
|
|
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
484
|
disallowed_tools: z
|
|
281
485
|
.array(z.enum(["*", "read", "aft_outline", "aft_zoom", "aft_search"]))
|
|
282
486
|
.default([])
|
|
@@ -689,10 +893,10 @@ export const MagicContextConfigSchema = z
|
|
|
689
893
|
"original language until naturally rewritten.",
|
|
690
894
|
),
|
|
691
895
|
historian: HistorianConfigSchema.describe(
|
|
692
|
-
"Historian
|
|
896
|
+
"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
897
|
),
|
|
694
898
|
dreamer: DreamerConfigSchema.optional().describe(
|
|
695
|
-
"Dreamer
|
|
899
|
+
"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
900
|
),
|
|
697
901
|
smart_notes: z
|
|
698
902
|
.object({
|
package/src/shared/jsonc-edit.ts
CHANGED
|
@@ -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.
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
normalizeModelEntry,
|
|
5
|
+
resolveDreamerTaskModel,
|
|
6
|
+
resolveFallbackEntries,
|
|
7
|
+
resolveHistorianModel,
|
|
8
|
+
} from "./model-resolution";
|
|
9
|
+
|
|
10
|
+
describe("model-resolution", () => {
|
|
11
|
+
test("normalizes string and object entries to the same model identity", () => {
|
|
12
|
+
expect(normalizeModelEntry("anthropic/sonnet", "opencode")).toEqual({
|
|
13
|
+
model: "anthropic/sonnet",
|
|
14
|
+
});
|
|
15
|
+
expect(
|
|
16
|
+
normalizeModelEntry({ model: "anthropic/sonnet", variant: "high" }, "opencode"),
|
|
17
|
+
).toEqual({ model: "anthropic/sonnet", qualifier: "high" });
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test("keeps ordered qualifier-distinct fallbacks and removes exact duplicates", () => {
|
|
21
|
+
expect(
|
|
22
|
+
resolveFallbackEntries(
|
|
23
|
+
[
|
|
24
|
+
{ model: "anthropic/sonnet", variant: "low" },
|
|
25
|
+
{ model: "anthropic/sonnet", variant: "high" },
|
|
26
|
+
{ model: "anthropic/sonnet", variant: "low" },
|
|
27
|
+
"google/flash",
|
|
28
|
+
"google/flash",
|
|
29
|
+
],
|
|
30
|
+
"opencode",
|
|
31
|
+
),
|
|
32
|
+
).toEqual([
|
|
33
|
+
{ model: "anthropic/sonnet", qualifier: "low" },
|
|
34
|
+
{ model: "anthropic/sonnet", qualifier: "high" },
|
|
35
|
+
{ model: "google/flash" },
|
|
36
|
+
]);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test("does not inherit a block qualifier into unqualified fallbacks", () => {
|
|
40
|
+
const config = {
|
|
41
|
+
historian: {
|
|
42
|
+
opencode: {
|
|
43
|
+
model: "open/primary",
|
|
44
|
+
fallback_models: ["open/fallback"],
|
|
45
|
+
variant: "primary-only",
|
|
46
|
+
},
|
|
47
|
+
pi: {
|
|
48
|
+
model: "pi/primary",
|
|
49
|
+
fallback_models: ["pi/fallback"],
|
|
50
|
+
thinking_level: "high",
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// Inheritance is deliberately absent because a fallback model may not support the
|
|
56
|
+
// qualifier selected for the primary model.
|
|
57
|
+
expect(resolveHistorianModel(config, "opencode")).toEqual({
|
|
58
|
+
primary: { model: "open/primary", qualifier: "primary-only" },
|
|
59
|
+
fallbacks: [{ model: "open/fallback" }],
|
|
60
|
+
});
|
|
61
|
+
expect(resolveHistorianModel(config, "pi")).toEqual({
|
|
62
|
+
primary: { model: "pi/primary", qualifier: "high" },
|
|
63
|
+
fallbacks: [{ model: "pi/fallback" }],
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test("reads historian attempts only from the requested harness", () => {
|
|
68
|
+
const config = {
|
|
69
|
+
historian: {
|
|
70
|
+
model: "flat/ignored",
|
|
71
|
+
opencode: {
|
|
72
|
+
model: { model: "open/code", variant: "oc-primary" },
|
|
73
|
+
fallback_models: [{ model: "open/fallback", variant: "oc-fallback" }],
|
|
74
|
+
},
|
|
75
|
+
pi: {
|
|
76
|
+
model: { model: "pi/model", thinking_level: "high" },
|
|
77
|
+
fallback_models: [{ model: "pi/fallback", thinking_level: "max" }],
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
expect(resolveHistorianModel(config, "opencode")).toEqual({
|
|
83
|
+
primary: { model: "open/code", qualifier: "oc-primary" },
|
|
84
|
+
fallbacks: [{ model: "open/fallback", qualifier: "oc-fallback" }],
|
|
85
|
+
});
|
|
86
|
+
expect(resolveHistorianModel(config, "pi")).toEqual({
|
|
87
|
+
primary: { model: "pi/model", qualifier: "high" },
|
|
88
|
+
fallbacks: [{ model: "pi/fallback", qualifier: "max" }],
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test("resolves ordinary task model and scheduling without crossing harnesses", () => {
|
|
93
|
+
const config = {
|
|
94
|
+
dreamer: {
|
|
95
|
+
tasks: {
|
|
96
|
+
curate: { schedule: "0 4 * * *", promotion_threshold: 3 },
|
|
97
|
+
},
|
|
98
|
+
opencode: {
|
|
99
|
+
model: { model: "open/default", variant: "default-variant" },
|
|
100
|
+
tasks: {
|
|
101
|
+
curate: {
|
|
102
|
+
model: { model: "open/task", variant: "task-variant" },
|
|
103
|
+
fallback_models: [
|
|
104
|
+
"open/bare",
|
|
105
|
+
{ model: "open/qualified", variant: "fb" },
|
|
106
|
+
],
|
|
107
|
+
timeout_minutes: 12,
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
pi: {
|
|
112
|
+
model: { model: "pi/default", thinking_level: "high" },
|
|
113
|
+
tasks: {
|
|
114
|
+
curate: {
|
|
115
|
+
model: { model: "pi/task", thinking_level: "max" },
|
|
116
|
+
fallback_models: [{ model: "pi/fallback", thinking_level: "minimal" }],
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
expect(resolveDreamerTaskModel({ config, harness: "opencode", task: "curate" })).toEqual({
|
|
124
|
+
primary: { model: "open/task", qualifier: "task-variant" },
|
|
125
|
+
fallbacks: [{ model: "open/bare" }, { model: "open/qualified", qualifier: "fb" }],
|
|
126
|
+
schedule: "0 4 * * *",
|
|
127
|
+
timeoutMinutes: 12,
|
|
128
|
+
promotionThreshold: 3,
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
test("uses mural model between compress-cues task and harness default", () => {
|
|
133
|
+
const config = {
|
|
134
|
+
dreamer: {
|
|
135
|
+
tasks: { "compress-cues": { schedule: "0 4 * * *" } },
|
|
136
|
+
opencode: {
|
|
137
|
+
model: { model: "open/default", variant: "default-variant" },
|
|
138
|
+
tasks: { "compress-cues": { variant: "task-local-must-not-leak" } },
|
|
139
|
+
},
|
|
140
|
+
pi: {
|
|
141
|
+
model: { model: "pi/default", thinking_level: "high" },
|
|
142
|
+
tasks: { "compress-cues": {} },
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
expect(
|
|
148
|
+
resolveDreamerTaskModel({
|
|
149
|
+
config,
|
|
150
|
+
harness: "opencode",
|
|
151
|
+
task: "compress-cues",
|
|
152
|
+
muralModel: "mural/model",
|
|
153
|
+
}),
|
|
154
|
+
).toMatchObject({
|
|
155
|
+
primary: { model: "mural/model", qualifier: "default-variant" },
|
|
156
|
+
fallbacks: [],
|
|
157
|
+
});
|
|
158
|
+
expect(
|
|
159
|
+
resolveDreamerTaskModel({
|
|
160
|
+
config,
|
|
161
|
+
harness: "pi",
|
|
162
|
+
task: "compress-cues",
|
|
163
|
+
muralModel: "mural/model",
|
|
164
|
+
}),
|
|
165
|
+
).toMatchObject({
|
|
166
|
+
primary: { model: "mural/model", qualifier: "high" },
|
|
167
|
+
fallbacks: [],
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
});
|