@cortexkit/opencode-magic-context 0.33.0 → 0.34.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/dist/config/agent-disable.d.ts +22 -0
- package/dist/config/agent-disable.d.ts.map +1 -1
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/project-security.d.ts +7 -0
- package/dist/config/project-security.d.ts.map +1 -1
- package/dist/config/schema/magic-context.d.ts +144 -24
- package/dist/config/schema/magic-context.d.ts.map +1 -1
- package/dist/config/transform-mode.d.ts +2 -0
- package/dist/config/transform-mode.d.ts.map +1 -1
- package/dist/{conflict-warning-hook-bwa99a04.js → conflict-warning-hook-fvpfvt0w.js} +1 -1
- package/dist/{derive-budgets-e9j09sjr.js → derive-budgets-9crp0cvd.js} +1 -1
- package/dist/features/builtin-commands/commands.d.ts +1 -1
- package/dist/features/builtin-commands/commands.d.ts.map +1 -1
- package/dist/features/magic-context/compaction-marker.d.ts +106 -0
- package/dist/features/magic-context/compaction-marker.d.ts.map +1 -1
- package/dist/features/magic-context/compartment-chunk-embedding.d.ts +0 -2
- package/dist/features/magic-context/compartment-chunk-embedding.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/classify.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/evaluate-smart-notes.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/map-memories.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/refresh-primers.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/task-executor.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/task-registry.d.ts +0 -1
- package/dist/features/magic-context/dreamer/task-registry.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/verify.d.ts.map +1 -1
- package/dist/features/magic-context/fail-closed-block.d.ts +10 -0
- package/dist/features/magic-context/fail-closed-block.d.ts.map +1 -1
- package/dist/features/magic-context/git-commits/index.d.ts +2 -2
- package/dist/features/magic-context/git-commits/index.d.ts.map +1 -1
- package/dist/features/magic-context/git-commits/storage-git-commit-embeddings.d.ts +0 -2
- package/dist/features/magic-context/git-commits/storage-git-commit-embeddings.d.ts.map +1 -1
- package/dist/features/magic-context/git-commits/storage-git-commits.d.ts +0 -13
- package/dist/features/magic-context/git-commits/storage-git-commits.d.ts.map +1 -1
- package/dist/features/magic-context/memory/embedding-identity.d.ts.map +1 -1
- package/dist/features/magic-context/memory/embedding-local.d.ts +7 -1
- package/dist/features/magic-context/memory/embedding-local.d.ts.map +1 -1
- package/dist/features/magic-context/memory/embedding.d.ts +1 -2
- package/dist/features/magic-context/memory/embedding.d.ts.map +1 -1
- package/dist/features/magic-context/memory/memory-migration.d.ts +0 -6
- package/dist/features/magic-context/memory/memory-migration.d.ts.map +1 -1
- package/dist/features/magic-context/memory/project-identity.d.ts +2 -6
- package/dist/features/magic-context/memory/project-identity.d.ts.map +1 -1
- package/dist/features/magic-context/memory/storage-memory-embeddings.d.ts +0 -1
- package/dist/features/magic-context/memory/storage-memory-embeddings.d.ts.map +1 -1
- package/dist/features/magic-context/memory/storage-memory.d.ts +1 -6
- package/dist/features/magic-context/memory/storage-memory.d.ts.map +1 -1
- package/dist/features/magic-context/memory/verification-paths.d.ts +0 -1
- package/dist/features/magic-context/memory/verification-paths.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.map +1 -1
- package/dist/features/magic-context/mural/render-mural.d.ts.map +1 -1
- package/dist/features/magic-context/mural/storage-mural.d.ts +0 -1
- package/dist/features/magic-context/mural/storage-mural.d.ts.map +1 -1
- package/dist/features/magic-context/overflow-detection.d.ts +8 -1
- package/dist/features/magic-context/overflow-detection.d.ts.map +1 -1
- package/dist/features/magic-context/project-embedding-registry.d.ts +16 -2
- package/dist/features/magic-context/project-embedding-registry.d.ts.map +1 -1
- package/dist/features/magic-context/scheduler.d.ts.map +1 -1
- package/dist/features/magic-context/schema-fence-probe.d.ts +28 -0
- package/dist/features/magic-context/schema-fence-probe.d.ts.map +1 -0
- package/dist/features/magic-context/sidekick/agent.d.ts.map +1 -1
- package/dist/features/magic-context/smart-notes/capabilities.d.ts +0 -2
- package/dist/features/magic-context/smart-notes/capabilities.d.ts.map +1 -1
- package/dist/features/magic-context/smart-notes/compiler.d.ts +0 -1
- package/dist/features/magic-context/smart-notes/compiler.d.ts.map +1 -1
- package/dist/features/magic-context/smart-notes/types.d.ts +0 -16
- package/dist/features/magic-context/smart-notes/types.d.ts.map +1 -1
- package/dist/features/magic-context/storage-db.d.ts +35 -1
- package/dist/features/magic-context/storage-db.d.ts.map +1 -1
- package/dist/features/magic-context/storage-meta-persisted.d.ts +38 -5
- package/dist/features/magic-context/storage-meta-persisted.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.d.ts +1 -1
- package/dist/features/magic-context/storage.d.ts.map +1 -1
- package/dist/features/magic-context/user-memory/review-user-memories.d.ts.map +1 -1
- package/dist/features/magic-context/user-memory/storage-user-memory.d.ts +0 -2
- package/dist/features/magic-context/user-memory/storage-user-memory.d.ts.map +1 -1
- package/dist/hooks/auto-update-checker/types.d.ts +0 -3
- package/dist/hooks/auto-update-checker/types.d.ts.map +1 -1
- package/dist/hooks/magic-context/boundary-execution.d.ts +1 -1
- package/dist/hooks/magic-context/boundary-execution.d.ts.map +1 -1
- package/dist/hooks/magic-context/cache-busting-signals.d.ts +1 -1
- package/dist/hooks/magic-context/cache-busting-signals.d.ts.map +1 -1
- package/dist/hooks/magic-context/channel2-delivery.d.ts.map +1 -1
- package/dist/hooks/magic-context/child-session-spawn.d.ts +26 -0
- package/dist/hooks/magic-context/child-session-spawn.d.ts.map +1 -0
- package/dist/hooks/magic-context/command-handler.d.ts +2 -0
- package/dist/hooks/magic-context/command-handler.d.ts.map +1 -1
- package/dist/hooks/magic-context/compaction-marker-manager.d.ts +49 -0
- package/dist/hooks/magic-context/compaction-marker-manager.d.ts.map +1 -1
- package/dist/hooks/magic-context/compaction-off-transition.d.ts +96 -0
- package/dist/hooks/magic-context/compaction-off-transition.d.ts.map +1 -0
- package/dist/hooks/magic-context/compartment-parser.d.ts +12 -0
- package/dist/hooks/magic-context/compartment-parser.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-historian.d.ts +2 -0
- package/dist/hooks/magic-context/compartment-runner-historian.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-incremental.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-mapping.d.ts +0 -12
- package/dist/hooks/magic-context/compartment-runner-mapping.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-trigger.d.ts +12 -5
- package/dist/hooks/magic-context/compartment-trigger.d.ts.map +1 -1
- package/dist/hooks/magic-context/ctx-reduce-availability.d.ts +47 -1
- package/dist/hooks/magic-context/ctx-reduce-availability.d.ts.map +1 -1
- package/dist/hooks/magic-context/emergency-drop.d.ts +2 -2
- package/dist/hooks/magic-context/event-handler.d.ts +7 -0
- package/dist/hooks/magic-context/event-handler.d.ts.map +1 -1
- package/dist/hooks/magic-context/event-resolvers.d.ts +13 -21
- package/dist/hooks/magic-context/event-resolvers.d.ts.map +1 -1
- package/dist/hooks/magic-context/final-wire-token-estimate.d.ts +2 -0
- package/dist/hooks/magic-context/final-wire-token-estimate.d.ts.map +1 -1
- package/dist/hooks/magic-context/heuristic-cleanup.d.ts +1 -1
- package/dist/hooks/magic-context/historian-prompt.generated.d.ts +1 -1
- package/dist/hooks/magic-context/historian-prompt.generated.d.ts.map +1 -1
- package/dist/hooks/magic-context/hook-handlers.d.ts +1 -0
- package/dist/hooks/magic-context/hook-handlers.d.ts.map +1 -1
- package/dist/hooks/magic-context/hook.d.ts +11 -1
- package/dist/hooks/magic-context/hook.d.ts.map +1 -1
- package/dist/hooks/magic-context/inject-compartments.d.ts +18 -0
- package/dist/hooks/magic-context/inject-compartments.d.ts.map +1 -1
- package/dist/hooks/magic-context/protected-tail-boundary.d.ts.map +1 -1
- package/dist/hooks/magic-context/read-session-db.d.ts.map +1 -1
- package/dist/hooks/magic-context/read-session-true-raw-tokens.d.ts.map +1 -1
- package/dist/hooks/magic-context/rust-mode-transform.d.ts +3 -0
- package/dist/hooks/magic-context/rust-mode-transform.d.ts.map +1 -1
- package/dist/hooks/magic-context/send-session-notification.d.ts +20 -1
- package/dist/hooks/magic-context/send-session-notification.d.ts.map +1 -1
- package/dist/hooks/magic-context/sentinel.d.ts +39 -0
- package/dist/hooks/magic-context/sentinel.d.ts.map +1 -1
- package/dist/hooks/magic-context/tag-messages.d.ts.map +1 -1
- package/dist/hooks/magic-context/tool-drop-target.d.ts +19 -0
- package/dist/hooks/magic-context/tool-drop-target.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform-compartment-phase.d.ts +3 -0
- package/dist/hooks/magic-context/transform-compartment-phase.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform-postprocess-phase.d.ts +30 -0
- package/dist/hooks/magic-context/transform-postprocess-phase.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform.d.ts +27 -0
- package/dist/hooks/magic-context/transform.d.ts.map +1 -1
- package/dist/{index-kxz4tn6m.js → index-56yjaeb2.js} +56 -4
- package/dist/{index-bmkea2dq.js → index-b563sga5.js} +40 -287
- package/dist/{index-y1c9tcm3.js → index-cn960ffd.js} +101 -20
- package/dist/{index-k5y60ymm.js → index-dtas91s5.js} +27 -6
- package/dist/index-fcyvznch.js +279 -0
- package/dist/{index-63dq2g4d.js → index-ry8rh8cv.js} +138 -24
- package/dist/{index-h86twgsp.js → index-s0mdn690.js} +2113 -1067
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1356 -627
- package/dist/plugin/embedding-bootstrap.d.ts.map +1 -1
- package/dist/plugin/hooks/create-session-hooks.d.ts +19 -8
- package/dist/plugin/hooks/create-session-hooks.d.ts.map +1 -1
- package/dist/plugin/messages-transform.d.ts +10 -0
- package/dist/plugin/messages-transform.d.ts.map +1 -1
- package/dist/plugin/rpc-handlers.d.ts +3 -7
- package/dist/plugin/rpc-handlers.d.ts.map +1 -1
- package/dist/plugin/tool-registry.d.ts +8 -0
- package/dist/plugin/tool-registry.d.ts.map +1 -1
- package/dist/{recomp-orchestrator-yh7pepvx.js → recomp-orchestrator-8e82vmvb.js} +6 -5
- package/dist/send-session-notification-6njqewzk.js +20 -0
- package/dist/shared/conflict-detector.d.ts +38 -1
- package/dist/shared/conflict-detector.d.ts.map +1 -1
- package/dist/shared/conflict-fixer.d.ts +18 -1
- package/dist/shared/conflict-fixer.d.ts.map +1 -1
- package/dist/shared/context-limit-provenance.d.ts +3 -0
- package/dist/shared/context-limit-provenance.d.ts.map +1 -0
- package/dist/shared/escalation-bands.d.ts +9 -0
- package/dist/shared/escalation-bands.d.ts.map +1 -0
- package/dist/shared/format-threshold.d.ts +1 -1
- package/dist/shared/models-dev-cache.d.ts +29 -3
- package/dist/shared/models-dev-cache.d.ts.map +1 -1
- package/dist/shared/rpc-server.d.ts.map +1 -1
- package/dist/shared/rpc-types.d.ts +46 -1
- package/dist/shared/rpc-types.d.ts.map +1 -1
- package/dist/shared/sqlite.d.ts +9 -4
- package/dist/shared/sqlite.d.ts.map +1 -1
- package/dist/shared/storage-permissions.d.ts +5 -0
- package/dist/shared/storage-permissions.d.ts.map +1 -0
- package/dist/{storage-meta-session-1phy0b1z.js → storage-meta-session-xt41zbw4.js} +2 -1
- package/dist/{task-config-dn9k9szr.js → task-config-5nx3qfz8.js} +1 -2
- package/dist/tui/compaction-off.d.ts +13 -0
- package/dist/tui/compaction-off.d.ts.map +1 -0
- package/dist/tui/data/context-db.d.ts.map +1 -1
- package/dist/tui-compiled/compaction-off.d.ts +13 -0
- package/dist/tui-compiled/compaction-off.d.ts.map +1 -0
- package/dist/tui-compiled/data/context-db.d.ts.map +1 -1
- package/package.json +8 -3
- package/src/agents/dreamer.ts +68 -0
- package/src/agents/hidden-agent-registrations.ts +331 -0
- package/src/agents/historian.ts +3 -0
- package/src/agents/language-directive.test.ts +138 -0
- package/src/agents/language-directive.ts +121 -0
- package/src/agents/magic-context-prompt.language.test.ts +35 -0
- package/src/agents/magic-context-prompt.test.ts +292 -0
- package/src/agents/magic-context-prompt.ts +182 -0
- package/src/agents/permissions.test.ts +252 -0
- package/src/agents/permissions.ts +186 -0
- package/src/agents/sidekick.ts +1 -0
- package/src/agents/smart-note-compiler.ts +1 -0
- package/src/config/agent-disable.ts +106 -0
- package/src/config/compaction-accessor-guard.test.ts +82 -0
- package/src/config/compaction-backcompat.test.ts +52 -0
- package/src/config/index.test.ts +904 -0
- package/src/config/index.ts +601 -0
- package/src/config/migrate-config-location.test.ts +332 -0
- package/src/config/migrate-config-location.ts +635 -0
- package/src/config/migrate-dreamer-v2.test.ts +287 -0
- package/src/config/migrate-dreamer-v2.ts +336 -0
- package/src/config/migrate-experimental.ts +159 -0
- package/src/config/project-security.test.ts +366 -0
- package/src/config/project-security.ts +588 -0
- package/src/config/prune-config-leaf.test.ts +78 -0
- package/src/config/prune-config-leaf.ts +68 -0
- package/src/config/removed-config-keys.test.ts +116 -0
- package/src/config/schema/agent-overrides.ts +45 -0
- package/src/config/schema/magic-context.test.ts +358 -0
- package/src/config/schema/magic-context.ts +1002 -0
- package/src/config/transform-mode.test.ts +62 -0
- package/src/config/transform-mode.ts +34 -0
- package/src/config/variable.test.ts +323 -0
- package/src/config/variable.ts +192 -0
- package/src/features/magic-context/defaults.ts +1 -0
- package/src/features/magic-context/dreamer/cron.ts +243 -0
- package/src/features/magic-context/dreamer/task-registry.ts +104 -0
- package/src/shared/conflict-detector.test.ts +251 -1
- package/src/shared/conflict-detector.ts +84 -5
- package/src/shared/conflict-fixer.test.ts +309 -0
- package/src/shared/conflict-fixer.ts +82 -5
- package/src/shared/context-limit-provenance.ts +6 -0
- package/src/shared/escalation-bands.test.ts +23 -0
- package/src/shared/escalation-bands.ts +18 -0
- package/src/shared/format-threshold.ts +1 -1
- package/src/shared/models-dev-cache.test.ts +171 -0
- package/src/shared/models-dev-cache.ts +195 -38
- package/src/shared/rpc-server.ts +27 -18
- package/src/shared/rpc-types.ts +46 -1
- package/src/shared/sqlite.ts +13 -33
- package/src/shared/storage-permissions.ts +20 -0
- package/src/tui/compaction-off.test.ts +56 -0
- package/src/tui/compaction-off.ts +29 -0
- package/src/tui/data/context-db.ts +6 -0
- package/src/tui/index.tsx +113 -65
- package/src/tui/slots/sidebar-content.tsx +139 -83
- package/src/tui-compiled/compaction-off.ts +29 -0
- package/src/tui-compiled/data/context-db.ts +6 -0
- package/src/tui-compiled/index.tsx +558 -455
- package/src/tui-compiled/slots/sidebar-content.tsx +237 -161
- package/dist/config/schema.d.ts +0 -3
- package/dist/config/schema.d.ts.map +0 -1
- package/dist/features/magic-context/plugin-messages.d.ts +0 -75
- package/dist/features/magic-context/plugin-messages.d.ts.map +0 -1
- package/dist/hooks/magic-context/compartment-runner-state-xml.d.ts +0 -21
- package/dist/hooks/magic-context/compartment-runner-state-xml.d.ts.map +0 -1
- package/dist/index-anhrfygk.js +0 -57
- package/dist/send-session-notification-tghq9gnh.js +0 -11
- package/dist/shared/opencode-compaction-detector.d.ts +0 -2
- package/dist/shared/opencode-compaction-detector.d.ts.map +0 -1
- package/dist/shared/transcript-opencode.d.ts +0 -71
- package/dist/shared/transcript-opencode.d.ts.map +0 -1
- package/dist/tools/index.d.ts +0 -6
- package/dist/tools/index.d.ts.map +0 -1
- package/src/shared/opencode-compaction-detector.test.ts +0 -239
- package/src/shared/opencode-compaction-detector.ts +0 -81
- package/src/shared/transcript-opencode.ts +0 -302
- /package/dist/{index-v7fmc1s2.js → index-ygcng9t8.js} +0 -0
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
/// <reference types="bun-types" />
|
|
2
|
+
|
|
3
|
+
import { describe, expect, it } from "bun:test";
|
|
4
|
+
import { migrateDreamerV2 } from "./migrate-dreamer-v2";
|
|
5
|
+
|
|
6
|
+
function migrate(raw: Record<string, unknown>): {
|
|
7
|
+
out: Record<string, unknown>;
|
|
8
|
+
warnings: string[];
|
|
9
|
+
} {
|
|
10
|
+
const warnings: string[] = [];
|
|
11
|
+
const out = migrateDreamerV2(raw, warnings);
|
|
12
|
+
return { out, warnings };
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function tasks(
|
|
16
|
+
out: Record<string, unknown>,
|
|
17
|
+
): Record<string, { schedule?: string } & Record<string, unknown>> {
|
|
18
|
+
return (out.dreamer as Record<string, unknown>).tasks as Record<
|
|
19
|
+
string,
|
|
20
|
+
{ schedule?: string } & Record<string, unknown>
|
|
21
|
+
>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
describe("migrateDreamerV2", () => {
|
|
25
|
+
it("is a no-op when there is no dreamer block", () => {
|
|
26
|
+
const { out } = migrate({ enabled: true });
|
|
27
|
+
expect(out).toEqual({ enabled: true });
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("a v2 record MISSING verify-broad gets it backfilled (coupled to verify)", () => {
|
|
31
|
+
// A v2 tasks-object predating the verify-broad split must gain the task
|
|
32
|
+
// rather than silently inheriting Zod's default-on — otherwise a user who
|
|
33
|
+
// disabled verify would get unintended weekly broad verification.
|
|
34
|
+
const v2 = { dreamer: { tasks: { verify: { schedule: "0 3 * * *" } } } };
|
|
35
|
+
const { out, warnings } = migrate(structuredClone(v2));
|
|
36
|
+
expect(tasks(out)["verify-broad"].schedule).toBe("0 4 * * 0");
|
|
37
|
+
expect(warnings).toHaveLength(0);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it("is a no-op when dreamer has only non-legacy keys (e.g. model)", () => {
|
|
41
|
+
const { out, warnings } = migrate({ dreamer: { model: "x/y" } });
|
|
42
|
+
expect(out).toEqual({ dreamer: { model: "x/y" } });
|
|
43
|
+
expect(warnings).toHaveLength(0);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("derives the base cron from the window START", () => {
|
|
47
|
+
const { out } = migrate({ dreamer: { schedule: "02:00-06:00", tasks: ["consolidate"] } });
|
|
48
|
+
expect(tasks(out).curate.schedule).toBe("0 2 * * *");
|
|
49
|
+
expect(tasks(out).verify.schedule).toBe("");
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("listed tasks get the base cron; OMITTED canonical tasks are DISABLED", () => {
|
|
53
|
+
// Deliberate selection: only consolidate + verify. Legacy canonical tasks are disabled;
|
|
54
|
+
// the new classify task defaults on daily because it did not exist in v1.
|
|
55
|
+
const { out } = migrate({
|
|
56
|
+
dreamer: { schedule: "03:30-06:00", tasks: ["consolidate", "verify"] },
|
|
57
|
+
});
|
|
58
|
+
const t = tasks(out);
|
|
59
|
+
expect(t.verify.schedule).toBe("30 3 * * *");
|
|
60
|
+
expect(t.curate.schedule).toBe("30 3 * * *");
|
|
61
|
+
expect(t["classify-memories"].schedule).toBe("0 6 * * *");
|
|
62
|
+
expect(t["maintain-docs"].schedule).toBe(""); // omitted → disabled
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("when tasks array is ABSENT, preserves the v1 default suite (maintain-docs off)", () => {
|
|
66
|
+
const { out } = migrate({ dreamer: { schedule: "02:00-06:00" } });
|
|
67
|
+
const t = tasks(out);
|
|
68
|
+
expect(t.verify.schedule).toBe("0 2 * * *");
|
|
69
|
+
// verify enabled → verify-broad defaults ON weekly; broad_interval_days gone.
|
|
70
|
+
expect(t["verify-broad"].schedule).toBe("0 4 * * 0");
|
|
71
|
+
expect(t.verify.broad_interval_days).toBeUndefined();
|
|
72
|
+
expect(t.curate.schedule).toBe("0 2 * * *");
|
|
73
|
+
expect(t["classify-memories"].schedule).toBe("0 6 * * *");
|
|
74
|
+
expect(t["maintain-docs"].schedule).toBe(""); // not in v1 default list
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("user_memories.enabled false → review-user-memories disabled (opt-out preserved)", () => {
|
|
78
|
+
const { out } = migrate({
|
|
79
|
+
dreamer: { schedule: "02:00-06:00", user_memories: { enabled: false } },
|
|
80
|
+
});
|
|
81
|
+
expect(tasks(out)["review-user-memories"].schedule).toBe("");
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it("user_memories default (no block) → review-user-memories enabled", () => {
|
|
85
|
+
const { out } = migrate({ dreamer: { schedule: "02:00-06:00" } });
|
|
86
|
+
const rum = tasks(out)["review-user-memories"];
|
|
87
|
+
expect(rum.schedule).toBe("0 2 * * *");
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it("carries user_memories.promotion_threshold", () => {
|
|
91
|
+
const { out } = migrate({
|
|
92
|
+
dreamer: {
|
|
93
|
+
schedule: "02:00-06:00",
|
|
94
|
+
user_memories: { enabled: true, promotion_threshold: 7 },
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
expect(tasks(out)["review-user-memories"].promotion_threshold).toBe(7);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it("legacy pin_key_files block is dropped — no key-files task is emitted", () => {
|
|
101
|
+
const { out } = migrate({
|
|
102
|
+
dreamer: {
|
|
103
|
+
schedule: "02:00-06:00",
|
|
104
|
+
pin_key_files: { enabled: true, token_budget: 8000, min_reads: 6 },
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
// key-files was removed (feature moved to AFT's dreamer).
|
|
108
|
+
expect("key-files" in tasks(out)).toBe(false);
|
|
109
|
+
expect("pin_key_files" in (out.dreamer as Record<string, unknown>)).toBe(false);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it("evaluate-smart-notes always gets the base cron", () => {
|
|
113
|
+
const { out } = migrate({ dreamer: { schedule: "02:00-06:00", tasks: [] } });
|
|
114
|
+
expect(tasks(out)["evaluate-smart-notes"].schedule).toBe("0 2 * * *");
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it("new classify-memories and retrospective tasks default on daily unless dreamer is disabled", () => {
|
|
118
|
+
const { out } = migrate({ dreamer: { schedule: "02:00-06:00", tasks: [] } });
|
|
119
|
+
expect(tasks(out)["classify-memories"].schedule).toBe("0 6 * * *");
|
|
120
|
+
expect(tasks(out).retrospective.schedule).toBe("0 5 * * *");
|
|
121
|
+
|
|
122
|
+
const disabled = migrate({ dreamer: { disable: true, schedule: "02:00-06:00" } });
|
|
123
|
+
expect(tasks(disabled.out)["classify-memories"].schedule).toBe("");
|
|
124
|
+
expect(tasks(disabled.out).retrospective.schedule).toBe("");
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it("task_timeout_minutes becomes each task's timeout_minutes", () => {
|
|
128
|
+
const { out } = migrate({
|
|
129
|
+
dreamer: { schedule: "02:00-06:00", task_timeout_minutes: 15 },
|
|
130
|
+
});
|
|
131
|
+
const t = tasks(out);
|
|
132
|
+
expect(t.verify.timeout_minutes).toBe(15);
|
|
133
|
+
expect(t.curate.timeout_minutes).toBe(15);
|
|
134
|
+
expect(t["review-user-memories"].timeout_minutes).toBe(15);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it("drops retired keys and preserves agent-config keys (model, disable)", () => {
|
|
138
|
+
const { out } = migrate({
|
|
139
|
+
dreamer: {
|
|
140
|
+
schedule: "02:00-06:00",
|
|
141
|
+
max_runtime_minutes: 120,
|
|
142
|
+
model: "anthropic/x",
|
|
143
|
+
disable: false,
|
|
144
|
+
},
|
|
145
|
+
});
|
|
146
|
+
const d = out.dreamer as Record<string, unknown>;
|
|
147
|
+
expect(d.model).toBe("anthropic/x");
|
|
148
|
+
expect(d.disable).toBe(false);
|
|
149
|
+
expect("schedule" in d).toBe(false);
|
|
150
|
+
expect("max_runtime_minutes" in d).toBe(false);
|
|
151
|
+
expect("user_memories" in d).toBe(false);
|
|
152
|
+
expect("pin_key_files" in d).toBe(false);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it("falls back to the default base cron on an unparseable window", () => {
|
|
156
|
+
const { out } = migrate({ dreamer: { schedule: "garbage", tasks: ["consolidate"] } });
|
|
157
|
+
expect(tasks(out).curate.schedule).toBe("0 2 * * *");
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it("emits a migration warning", () => {
|
|
161
|
+
const { warnings } = migrate({ dreamer: { schedule: "02:00-06:00" } });
|
|
162
|
+
expect(warnings.join("\n")).toContain("dreamer.tasks");
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it("all canonical tasks are present after migration (no key-files)", () => {
|
|
166
|
+
const { out } = migrate({ dreamer: { schedule: "02:00-06:00" } });
|
|
167
|
+
expect(Object.keys(tasks(out)).sort()).toEqual(
|
|
168
|
+
[
|
|
169
|
+
"map-memories",
|
|
170
|
+
"classify-memories",
|
|
171
|
+
"curate",
|
|
172
|
+
"evaluate-smart-notes",
|
|
173
|
+
"maintain-docs",
|
|
174
|
+
"retrospective",
|
|
175
|
+
"review-user-memories",
|
|
176
|
+
"verify",
|
|
177
|
+
"verify-broad",
|
|
178
|
+
].sort(),
|
|
179
|
+
);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it("strips a stale key-files task from an existing v2 tasks-object", () => {
|
|
183
|
+
const { out } = migrate({
|
|
184
|
+
dreamer: {
|
|
185
|
+
tasks: {
|
|
186
|
+
verify: { schedule: "0 3 * * *" },
|
|
187
|
+
"verify-broad": { schedule: "0 4 * * 0" },
|
|
188
|
+
"key-files": { schedule: "0 2 * * *" },
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
});
|
|
192
|
+
expect("key-files" in tasks(out)).toBe(false);
|
|
193
|
+
expect(tasks(out).verify.schedule).toBe("0 3 * * *");
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it("folds object-shaped retired memory tasks into verify + curate", () => {
|
|
197
|
+
const { out } = migrate({
|
|
198
|
+
dreamer: {
|
|
199
|
+
tasks: {
|
|
200
|
+
verify: { schedule: "0 3 * * *" },
|
|
201
|
+
improve: { schedule: "0 * * * *" },
|
|
202
|
+
"maintain-docs": { schedule: "0 4 * * *" },
|
|
203
|
+
},
|
|
204
|
+
},
|
|
205
|
+
});
|
|
206
|
+
const t = tasks(out);
|
|
207
|
+
expect(t.verify.schedule).toBe("0 3 * * *");
|
|
208
|
+
// verify present+enabled → verify-broad defaults ON weekly.
|
|
209
|
+
expect(t["verify-broad"].schedule).toBe("0 4 * * 0");
|
|
210
|
+
expect(t.verify.broad_interval_days).toBeUndefined();
|
|
211
|
+
expect(t.curate.schedule).toBe("0 * * * *");
|
|
212
|
+
expect(t["classify-memories"].schedule).toBe("0 6 * * *");
|
|
213
|
+
expect(t.retrospective.schedule).toBe("0 5 * * *");
|
|
214
|
+
expect(t["maintain-docs"].schedule).toBe("0 4 * * *");
|
|
215
|
+
expect(t.improve).toBeUndefined();
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
it("v2-object with verify DISABLED backfills verify-broad OFF (not Zod's default-on)", () => {
|
|
219
|
+
const { out } = migrate({
|
|
220
|
+
dreamer: {
|
|
221
|
+
tasks: {
|
|
222
|
+
verify: { schedule: "" },
|
|
223
|
+
curate: { schedule: "0 4 * * 0" },
|
|
224
|
+
},
|
|
225
|
+
},
|
|
226
|
+
});
|
|
227
|
+
const t = tasks(out);
|
|
228
|
+
expect(t.verify.schedule).toBe("");
|
|
229
|
+
// verify off → verify-broad must be off, NOT the schema default 0 4 * * 0.
|
|
230
|
+
expect(t["verify-broad"].schedule).toBe("");
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
it("v2-object with verify ENABLED backfills verify-broad ON weekly", () => {
|
|
234
|
+
const { out } = migrate({
|
|
235
|
+
dreamer: { tasks: { verify: { schedule: "0 3 * * *" } } },
|
|
236
|
+
});
|
|
237
|
+
expect(tasks(out)["verify-broad"].schedule).toBe("0 4 * * 0");
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
it("v2-object strips a stale broad_interval_days from verify", () => {
|
|
241
|
+
const { out } = migrate({
|
|
242
|
+
dreamer: {
|
|
243
|
+
tasks: { verify: { schedule: "0 3 * * *", broad_interval_days: 9 } },
|
|
244
|
+
},
|
|
245
|
+
});
|
|
246
|
+
const t = tasks(out);
|
|
247
|
+
expect(t.verify.broad_interval_days).toBeUndefined();
|
|
248
|
+
expect(t["verify-broad"].schedule).toBe("0 4 * * 0");
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
it("v2-object already reconciled is a stable no-op (idempotent)", () => {
|
|
252
|
+
const input = {
|
|
253
|
+
dreamer: {
|
|
254
|
+
tasks: {
|
|
255
|
+
verify: { schedule: "0 3 * * *" },
|
|
256
|
+
"verify-broad": { schedule: "0 4 * * 0" },
|
|
257
|
+
},
|
|
258
|
+
},
|
|
259
|
+
};
|
|
260
|
+
const { out } = migrate(input);
|
|
261
|
+
// Unchanged → returns the SAME object reference (no churn on every load).
|
|
262
|
+
expect(out).toBe(input);
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
it("maps object-shaped maintain-memory to verify + curate, broad_interval_days dropped", () => {
|
|
266
|
+
const { out } = migrate({
|
|
267
|
+
dreamer: {
|
|
268
|
+
tasks: {
|
|
269
|
+
"maintain-memory": {
|
|
270
|
+
schedule: "0 5 * * *",
|
|
271
|
+
model: "x/y",
|
|
272
|
+
broad_interval_days: 9,
|
|
273
|
+
},
|
|
274
|
+
},
|
|
275
|
+
},
|
|
276
|
+
});
|
|
277
|
+
const t = tasks(out);
|
|
278
|
+
expect(t.verify.schedule).toBe("0 5 * * *");
|
|
279
|
+
expect(t.verify.model).toBe("x/y");
|
|
280
|
+
// The legacy broad_interval_days knob is dropped; broad is its own task.
|
|
281
|
+
expect(t.verify.broad_interval_days).toBeUndefined();
|
|
282
|
+
expect(t["verify-broad"].schedule).toBe("0 4 * * 0");
|
|
283
|
+
expect(t.curate.schedule).toBe("0 5 * * *");
|
|
284
|
+
expect(t.curate.model).toBe("x/y");
|
|
285
|
+
expect(t["maintain-memory"]).toBeUndefined();
|
|
286
|
+
});
|
|
287
|
+
});
|
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory migration of the legacy v1 dreamer config shape to the Dreamer v2
|
|
3
|
+
* per-task shape (shared OpenCode + Pi; runs on every config load, like
|
|
4
|
+
* migrate-experimental). Doctor performs the on-disk equivalent.
|
|
5
|
+
*
|
|
6
|
+
* v1 shape (any subset):
|
|
7
|
+
* dreamer: {
|
|
8
|
+
* schedule: "02:00-06:00", // a TIME WINDOW
|
|
9
|
+
* tasks: ["consolidate","verify"], // an ARRAY of agentic task names
|
|
10
|
+
* task_timeout_minutes: 20,
|
|
11
|
+
* max_runtime_minutes: 120,
|
|
12
|
+
* user_memories: { enabled, promotion_threshold },
|
|
13
|
+
* pin_key_files: { enabled, token_budget, min_reads },
|
|
14
|
+
* }
|
|
15
|
+
*
|
|
16
|
+
* v2 shape:
|
|
17
|
+
* dreamer: {
|
|
18
|
+
* tasks: { <task>: { schedule: "<cron>"|"", model?, timeout_minutes?, ... } }
|
|
19
|
+
* }
|
|
20
|
+
*
|
|
21
|
+
* Rules (see dreamer-v2-AB-spec.md):
|
|
22
|
+
* - Base cron derived from the WINDOW START: "02:00-06:00" → "0 2 * * *".
|
|
23
|
+
* - Legacy `tasks` array PRESENT → it is the user's deliberate selection: each
|
|
24
|
+
* LISTED agentic task gets the base cron; each OMITTED canonical task gets ""
|
|
25
|
+
* (disabled). Built-in defaults are used ONLY when `tasks` is absent.
|
|
26
|
+
* - user_memories.enabled false → review-user-memories "" ; true → base cron
|
|
27
|
+
* (promotion_threshold carried). A legacy pin_key_files block is dropped
|
|
28
|
+
* (key-files pinning moved out of Magic Context).
|
|
29
|
+
* - classify-memories is NEW in v2 and defaults ON daily at 06:00, unless the
|
|
30
|
+
* whole dreamer block is disabled.
|
|
31
|
+
* - evaluate-smart-notes → base cron (it always ran on pending notes).
|
|
32
|
+
* - task_timeout_minutes → each task's timeout_minutes default; max_runtime_minutes dropped.
|
|
33
|
+
* - Object-shaped A+B configs carrying retired memory task keys are folded into
|
|
34
|
+
* verify + curate before schema parsing strips unknown keys.
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
const OLD_VERIFY_TASK = "verify";
|
|
38
|
+
const OLD_CURATE_TASKS = ["consolidate", "archive-stale", "improve"] as const;
|
|
39
|
+
const RETIRED_OBJECT_MEMORY_TASKS = ["maintain-memory", ...OLD_CURATE_TASKS] as const;
|
|
40
|
+
const CANONICAL = [
|
|
41
|
+
"map-memories",
|
|
42
|
+
"verify",
|
|
43
|
+
"verify-broad",
|
|
44
|
+
"curate",
|
|
45
|
+
"classify-memories",
|
|
46
|
+
"retrospective",
|
|
47
|
+
"maintain-docs",
|
|
48
|
+
"evaluate-smart-notes",
|
|
49
|
+
"review-user-memories",
|
|
50
|
+
"promote-primers",
|
|
51
|
+
"refresh-primers",
|
|
52
|
+
] as const;
|
|
53
|
+
const DEFAULT_BASE_CRON = "0 2 * * *"; // matches the historical "02:00-06:00" default window start
|
|
54
|
+
const DEFAULT_CLASSIFY_CRON = "0 6 * * *";
|
|
55
|
+
const DEFAULT_RETROSPECTIVE_CRON = "0 5 * * *";
|
|
56
|
+
const DEFAULT_VERIFY_BROAD_CRON = "0 4 * * 0"; // weekly — replaces the old broad_interval_days cadence
|
|
57
|
+
|
|
58
|
+
/** "02:00-06:00" → "0 2 * * *". Falls back to the default base cron on any
|
|
59
|
+
* unparseable window (never throws — config migration is fail-open). */
|
|
60
|
+
function windowToCron(schedule: unknown): string {
|
|
61
|
+
if (typeof schedule !== "string") return DEFAULT_BASE_CRON;
|
|
62
|
+
const m = /^(\d{1,2}):(\d{2})\s*-/.exec(schedule.trim());
|
|
63
|
+
if (!m) return DEFAULT_BASE_CRON;
|
|
64
|
+
const hour = Number(m[1]);
|
|
65
|
+
const minute = Number(m[2]);
|
|
66
|
+
if (hour >= 24 || minute >= 60) return DEFAULT_BASE_CRON;
|
|
67
|
+
return `${minute} ${hour} * * *`;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function asObject(value: unknown): Record<string, unknown> | undefined {
|
|
71
|
+
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
72
|
+
? (value as Record<string, unknown>)
|
|
73
|
+
: undefined;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function cronIntervalScore(schedule: string): number {
|
|
77
|
+
const parts = schedule.trim().split(/\s+/);
|
|
78
|
+
if (parts.length !== 5) return Number.POSITIVE_INFINITY;
|
|
79
|
+
const [minute, hour, dayOfMonth, month, dayOfWeek] = parts;
|
|
80
|
+
if (month !== "*") return 366 * 24 * 60;
|
|
81
|
+
if (dayOfMonth !== "*") return 31 * 24 * 60;
|
|
82
|
+
if (dayOfWeek !== "*") return 7 * 24 * 60;
|
|
83
|
+
const everyHour = /^\*\/(\d+)$/.exec(hour ?? "");
|
|
84
|
+
if (everyHour) return Math.max(1, Number(everyHour[1])) * 60;
|
|
85
|
+
if (hour === "*") {
|
|
86
|
+
const everyMinute = /^\*\/(\d+)$/.exec(minute ?? "");
|
|
87
|
+
return everyMinute ? Math.max(1, Number(everyMinute[1])) : 60;
|
|
88
|
+
}
|
|
89
|
+
return 24 * 60;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function mostFrequentSchedule(schedules: string[]): string {
|
|
93
|
+
const enabled = schedules.map((s) => s.trim()).filter(Boolean);
|
|
94
|
+
if (enabled.length === 0) return "";
|
|
95
|
+
return enabled.sort((a, b) => cronIntervalScore(a) - cronIntervalScore(b))[0] ?? "";
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function withoutBroadInterval(entry: Record<string, unknown>): Record<string, unknown> {
|
|
99
|
+
const { broad_interval_days: _broad, ...rest } = entry;
|
|
100
|
+
return rest;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** Surgical reconcile for an already-v2 tasks-object config (no legacy keys):
|
|
104
|
+
* backfill `verify-broad` (coupled to verify's enabled state) and strip the
|
|
105
|
+
* dead `broad_interval_days` from every task. Returns rawConfig UNCHANGED when
|
|
106
|
+
* nothing needs touching (so an already-reconciled config is a stable no-op /
|
|
107
|
+
* idempotent). */
|
|
108
|
+
function reconcileV2TasksObject(
|
|
109
|
+
rawConfig: Record<string, unknown>,
|
|
110
|
+
dreamer: Record<string, unknown>,
|
|
111
|
+
tasksObject: Record<string, unknown>,
|
|
112
|
+
): Record<string, unknown> {
|
|
113
|
+
const hasVerifyBroad = "verify-broad" in tasksObject;
|
|
114
|
+
const hasBroadIntervalAnywhere = Object.values(tasksObject).some(
|
|
115
|
+
(v) => asObject(v) && "broad_interval_days" in (v as Record<string, unknown>),
|
|
116
|
+
);
|
|
117
|
+
// key-files was removed (feature moved to AFT); strip any stale task entry.
|
|
118
|
+
const hasStaleKeyFiles = "key-files" in tasksObject;
|
|
119
|
+
if (hasVerifyBroad && !hasBroadIntervalAnywhere && !hasStaleKeyFiles) return rawConfig;
|
|
120
|
+
|
|
121
|
+
const nextTasks: Record<string, unknown> = {};
|
|
122
|
+
for (const [key, value] of Object.entries(tasksObject)) {
|
|
123
|
+
if (key === "key-files") continue;
|
|
124
|
+
const obj = asObject(value);
|
|
125
|
+
nextTasks[key] = obj ? withoutBroadInterval(obj) : value;
|
|
126
|
+
}
|
|
127
|
+
if (!hasVerifyBroad) {
|
|
128
|
+
const verify = asObject(tasksObject.verify);
|
|
129
|
+
const verifyEnabled = typeof verify?.schedule === "string" && verify.schedule.trim() !== "";
|
|
130
|
+
nextTasks["verify-broad"] = {
|
|
131
|
+
schedule: verifyEnabled ? DEFAULT_VERIFY_BROAD_CRON : "",
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
return { ...rawConfig, dreamer: { ...dreamer, tasks: nextTasks } };
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export function migrateDreamerV2(
|
|
138
|
+
rawConfig: Record<string, unknown>,
|
|
139
|
+
warnings: string[],
|
|
140
|
+
): Record<string, unknown> {
|
|
141
|
+
const dreamer = asObject(rawConfig.dreamer);
|
|
142
|
+
if (!dreamer) return rawConfig;
|
|
143
|
+
|
|
144
|
+
const tasksObject = asObject(dreamer.tasks);
|
|
145
|
+
const hasRetiredObjectTasks = tasksObject
|
|
146
|
+
? RETIRED_OBJECT_MEMORY_TASKS.some((task) => task in tasksObject)
|
|
147
|
+
: false;
|
|
148
|
+
|
|
149
|
+
if (tasksObject && !hasRetiredObjectTasks) {
|
|
150
|
+
const hasLegacyOutsideTasks =
|
|
151
|
+
"schedule" in dreamer ||
|
|
152
|
+
"user_memories" in dreamer ||
|
|
153
|
+
"pin_key_files" in dreamer ||
|
|
154
|
+
"task_timeout_minutes" in dreamer ||
|
|
155
|
+
"max_runtime_minutes" in dreamer;
|
|
156
|
+
if (!hasLegacyOutsideTasks) {
|
|
157
|
+
// Already a v2 tasks-object, no legacy keys → only a SURGICAL touch-up
|
|
158
|
+
// is needed (don't reshape an otherwise-valid config): add a
|
|
159
|
+
// `verify-broad` task coupled to verify's enabled state, and strip the
|
|
160
|
+
// dead `broad_interval_days` knob. Without this, a user who DISABLED
|
|
161
|
+
// verify but never wrote verify-broad gets Zod's default `0 4 * * 0`
|
|
162
|
+
// and unintended weekly full-pool LLM verification.
|
|
163
|
+
return reconcileV2TasksObject(rawConfig, dreamer, tasksObject);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Nothing legacy to migrate (no window/array/blocks) → leave as-is; the
|
|
168
|
+
// schema default fills `tasks`.
|
|
169
|
+
const hasLegacy =
|
|
170
|
+
"schedule" in dreamer ||
|
|
171
|
+
Array.isArray(dreamer.tasks) ||
|
|
172
|
+
hasRetiredObjectTasks ||
|
|
173
|
+
"user_memories" in dreamer ||
|
|
174
|
+
"pin_key_files" in dreamer ||
|
|
175
|
+
"task_timeout_minutes" in dreamer ||
|
|
176
|
+
"max_runtime_minutes" in dreamer;
|
|
177
|
+
if (!hasLegacy) return rawConfig;
|
|
178
|
+
|
|
179
|
+
const baseCron = windowToCron(dreamer.schedule);
|
|
180
|
+
const timeout =
|
|
181
|
+
typeof dreamer.task_timeout_minutes === "number" ? dreamer.task_timeout_minutes : undefined;
|
|
182
|
+
const withTimeout = <T extends Record<string, unknown>>(entry: T): T =>
|
|
183
|
+
timeout !== undefined ? { ...entry, timeout_minutes: timeout } : entry;
|
|
184
|
+
const classifySchedule = dreamer.disable === true ? "" : DEFAULT_CLASSIFY_CRON;
|
|
185
|
+
const retrospectiveSchedule = dreamer.disable === true ? "" : DEFAULT_RETROSPECTIVE_CRON;
|
|
186
|
+
|
|
187
|
+
const tasks: Record<string, Record<string, unknown>> = {};
|
|
188
|
+
|
|
189
|
+
if (tasksObject) {
|
|
190
|
+
for (const [key, value] of Object.entries(tasksObject)) {
|
|
191
|
+
if ((RETIRED_OBJECT_MEMORY_TASKS as readonly string[]).includes(key)) continue;
|
|
192
|
+
if (asObject(value)) tasks[key] = { ...(value as Record<string, unknown>) };
|
|
193
|
+
}
|
|
194
|
+
const maintainMemoryEntry = asObject(tasksObject["maintain-memory"]);
|
|
195
|
+
if (maintainMemoryEntry) {
|
|
196
|
+
const schedule =
|
|
197
|
+
typeof maintainMemoryEntry.schedule === "string"
|
|
198
|
+
? maintainMemoryEntry.schedule
|
|
199
|
+
: baseCron;
|
|
200
|
+
tasks.verify = withTimeout({
|
|
201
|
+
...withoutBroadInterval(maintainMemoryEntry),
|
|
202
|
+
...(tasks.verify ?? {}),
|
|
203
|
+
schedule: tasks.verify?.schedule ?? schedule,
|
|
204
|
+
});
|
|
205
|
+
tasks.curate = withTimeout({
|
|
206
|
+
...withoutBroadInterval(maintainMemoryEntry),
|
|
207
|
+
...(tasks.curate ?? {}),
|
|
208
|
+
schedule: tasks.curate?.schedule ?? schedule,
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const oldVerifyEntry = asObject(tasksObject[OLD_VERIFY_TASK]);
|
|
213
|
+
if (oldVerifyEntry) {
|
|
214
|
+
tasks.verify = withTimeout({
|
|
215
|
+
...withoutBroadInterval(oldVerifyEntry),
|
|
216
|
+
...(tasks.verify ?? {}),
|
|
217
|
+
schedule:
|
|
218
|
+
tasks.verify?.schedule ??
|
|
219
|
+
(typeof oldVerifyEntry.schedule === "string"
|
|
220
|
+
? oldVerifyEntry.schedule
|
|
221
|
+
: baseCron),
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// The old internal broad cadence becomes its own task. If verify is
|
|
226
|
+
// enabled (broad used to run inside it), default verify-broad ON weekly;
|
|
227
|
+
// if verify is disabled, leave verify-broad disabled.
|
|
228
|
+
if (!tasks["verify-broad"]) {
|
|
229
|
+
const verifyEnabled =
|
|
230
|
+
typeof tasks.verify?.schedule === "string" && tasks.verify.schedule.trim() !== "";
|
|
231
|
+
tasks["verify-broad"] = withTimeout({
|
|
232
|
+
schedule: verifyEnabled ? DEFAULT_VERIFY_BROAD_CRON : "",
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const oldCurateEntries = OLD_CURATE_TASKS.map((task) => asObject(tasksObject[task])).filter(
|
|
237
|
+
(entry): entry is Record<string, unknown> => Boolean(entry),
|
|
238
|
+
);
|
|
239
|
+
if (oldCurateEntries.length > 0) {
|
|
240
|
+
const oldSchedules = oldCurateEntries.map((entry) =>
|
|
241
|
+
typeof entry.schedule === "string" ? entry.schedule : baseCron,
|
|
242
|
+
);
|
|
243
|
+
tasks.curate = withTimeout({
|
|
244
|
+
...(tasks.curate ?? {}),
|
|
245
|
+
schedule: mostFrequentSchedule(oldSchedules),
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
for (const task of CANONICAL) {
|
|
250
|
+
if (!tasks[task]) {
|
|
251
|
+
const schedule =
|
|
252
|
+
task === "verify" || task === "curate" || task === "verify-broad"
|
|
253
|
+
? ""
|
|
254
|
+
: task === "classify-memories"
|
|
255
|
+
? classifySchedule
|
|
256
|
+
: task === "retrospective"
|
|
257
|
+
? retrospectiveSchedule
|
|
258
|
+
: task === "maintain-docs"
|
|
259
|
+
? ""
|
|
260
|
+
: baseCron;
|
|
261
|
+
tasks[task] = withTimeout({ schedule });
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
} else {
|
|
265
|
+
// Agentic memory maintenance: array present → old verify enables verify,
|
|
266
|
+
// old consolidate/improve/archive-stale enable curate; array absent →
|
|
267
|
+
// historical default suite on.
|
|
268
|
+
const legacyArray = Array.isArray(dreamer.tasks)
|
|
269
|
+
? (dreamer.tasks as unknown[]).filter((t): t is string => typeof t === "string")
|
|
270
|
+
: null;
|
|
271
|
+
const verifySelected = legacyArray ? legacyArray.includes(OLD_VERIFY_TASK) : true;
|
|
272
|
+
const curateSelected = legacyArray
|
|
273
|
+
? legacyArray.some((task) => (OLD_CURATE_TASKS as readonly string[]).includes(task))
|
|
274
|
+
: true;
|
|
275
|
+
tasks.verify = withTimeout({
|
|
276
|
+
schedule: verifySelected ? baseCron : "",
|
|
277
|
+
});
|
|
278
|
+
tasks["verify-broad"] = withTimeout({
|
|
279
|
+
schedule: verifySelected ? DEFAULT_VERIFY_BROAD_CRON : "",
|
|
280
|
+
});
|
|
281
|
+
tasks.curate = withTimeout({
|
|
282
|
+
schedule: curateSelected ? baseCron : "",
|
|
283
|
+
});
|
|
284
|
+
tasks["classify-memories"] = withTimeout({
|
|
285
|
+
schedule: classifySchedule,
|
|
286
|
+
});
|
|
287
|
+
tasks.retrospective = withTimeout({
|
|
288
|
+
schedule: retrospectiveSchedule,
|
|
289
|
+
});
|
|
290
|
+
tasks["maintain-docs"] = withTimeout({
|
|
291
|
+
schedule: legacyArray?.includes("maintain-docs") ? baseCron : "",
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// map-memories (one-time backfill) defaults on so it prepares verify; gated
|
|
296
|
+
// by "unmapped memories exist", so it drains then no-ops.
|
|
297
|
+
tasks["map-memories"] ??= withTimeout({ schedule: baseCron });
|
|
298
|
+
|
|
299
|
+
// evaluate-smart-notes always ran post-suite on pending notes.
|
|
300
|
+
tasks["evaluate-smart-notes"] ??= withTimeout({ schedule: baseCron });
|
|
301
|
+
|
|
302
|
+
// review-user-memories ← user_memories block (default enabled in v1).
|
|
303
|
+
const um = asObject(dreamer.user_memories);
|
|
304
|
+
const umEnabled = um ? um.enabled !== false : true;
|
|
305
|
+
if (um || !tasks["review-user-memories"]) {
|
|
306
|
+
tasks["review-user-memories"] = withTimeout({
|
|
307
|
+
...(tasks["review-user-memories"] ?? {}),
|
|
308
|
+
schedule: umEnabled ? baseCron : "",
|
|
309
|
+
...(um && typeof um.promotion_threshold === "number"
|
|
310
|
+
? { promotion_threshold: um.promotion_threshold }
|
|
311
|
+
: {}),
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// key-files was removed (the feature moved to AFT's dreamer): any legacy
|
|
316
|
+
// pin_key_files block is simply dropped below with the other retired keys —
|
|
317
|
+
// no key-files task is emitted.
|
|
318
|
+
|
|
319
|
+
// Build the new dreamer block: keep agent-config keys (model, disable, etc.),
|
|
320
|
+
// drop the retired scheduling keys, add the tasks record.
|
|
321
|
+
const {
|
|
322
|
+
schedule: _schedule,
|
|
323
|
+
tasks: _tasks,
|
|
324
|
+
task_timeout_minutes: _tto,
|
|
325
|
+
max_runtime_minutes: _max,
|
|
326
|
+
user_memories: _um,
|
|
327
|
+
pin_key_files: _pkf,
|
|
328
|
+
...rest
|
|
329
|
+
} = dreamer;
|
|
330
|
+
|
|
331
|
+
warnings.push(
|
|
332
|
+
'Migrated legacy dreamer scheduling (schedule window / tasks array / user_memories / pin_key_files) → per-task "dreamer.tasks" in-memory (run `doctor` to persist).',
|
|
333
|
+
);
|
|
334
|
+
|
|
335
|
+
return { ...rawConfig, dreamer: { ...rest, tasks } };
|
|
336
|
+
}
|