mlx-bun 0.0.8 → 0.0.10
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 +112 -20
- package/docs/design/batching-perf-path.md +232 -0
- package/docs/design/batching-v2-plan.md +515 -0
- package/docs/design/bucketing-stage.md +196 -0
- package/docs/design/compat-cli-surface-design.md +11 -11
- package/docs/design/docs-reorg-plan.md +1 -1
- package/docs/design/dreaming-nightly-pipeline.md +291 -0
- package/docs/design/dspark-speculative-decoding.md +106 -0
- package/docs/design/generic-model-support.md +381 -0
- package/docs/design/memory-inference-path.md +422 -0
- package/docs/design/memory-synthesis.md +287 -0
- package/docs/design/minicpm5-decode-megakernel.md +484 -0
- package/docs/design/mlx-lm-tool-parity-plan.md +535 -0
- package/docs/design/omlx-adoption-map.md +90 -0
- package/docs/design/orpo-dynamic-lambda.md +41 -0
- package/docs/design/orpo-training.md +35 -5
- package/docs/design/parallel-slots.md +12 -1
- package/docs/design/parity-tier-dag.md +13 -5
- package/docs/design/segmented-backward-training.md +15 -0
- package/docs/design/spec-decode-larger-targets.md +229 -0
- package/docs/design/ssd-kv-cold-tier.md +158 -0
- package/docs/design/tauri-desktop-app.md +2 -2
- package/docs/design/the-dreaming-handoff.md +141 -0
- package/docs/design/the-dreaming-master-plan.md +446 -0
- package/docs/design/write-pipeline-entity.md +468 -0
- package/docs/investigations/curve-bisector-routing.md +1 -1
- package/docs/investigations/decode-roofline-lookagain.md +348 -0
- package/docs/investigations/dspark-handoff.md +58 -0
- package/docs/investigations/kernel-perf-review-2026-07.md +171 -0
- package/docs/planning/PRODUCT_ROADMAP.md +2 -2
- package/docs/planning/curve-sampler-research-plan.md +331 -0
- package/docs/planning/memory-docs-and-dag-plan.md +185 -0
- package/docs/planning/pi-integration-review.md +178 -0
- package/docs/planning/release-notes-v0.0.10.md +57 -0
- package/docs/planning/release-notes-v0.0.9.md +122 -0
- package/docs/planning/web-ui-pass-plan.md +213 -0
- package/docs/planning/website-readme-pass-plan.md +224 -0
- package/docs/reference/cli.md +255 -0
- package/docs/reference/embedding.md +7 -2
- package/docs/reference/library-api.md +22 -3
- package/docs/reference/memory.md +5 -5
- package/docs/reference/models.md +137 -0
- package/docs/reference/orpo-quickstart.md +6 -0
- package/docs/reference/server-api.md +187 -9
- package/docs/reference/server-config.md +43 -9
- package/docs/reference/training.md +31 -12
- package/package.json +1 -1
- package/src/anthropic.ts +22 -0
- package/src/assets/curve-designer.html +1 -1
- package/src/chat-template.ts +25 -7
- package/src/cli.ts +1077 -90
- package/src/config.ts +4 -1
- package/src/download.ts +99 -8
- package/src/embed.ts +77 -0
- package/src/eval/perplexity.ts +164 -0
- package/src/eval/runner.ts +15 -6
- package/src/eval/tasks/bfcl.ts +1 -1
- package/src/generate.ts +149 -18
- package/src/harness-pi.ts +43 -17
- package/src/index.ts +2 -0
- package/src/kv-store.ts +240 -98
- package/src/lora.ts +2 -2
- package/src/memory/article.ts +558 -0
- package/src/memory/chunk-validation.ts +150 -0
- package/src/memory/chunk.ts +305 -12
- package/src/memory/cluster.ts +344 -17
- package/src/memory/coverage.ts +134 -0
- package/src/memory/crosslink.ts +0 -0
- package/src/memory/db.ts +184 -45
- package/src/memory/entity.ts +203 -0
- package/src/memory/gate.ts +187 -0
- package/src/memory/history.ts +152 -0
- package/src/memory/ingest.ts +324 -11
- package/src/memory/model.ts +321 -0
- package/src/memory/normalize.ts +671 -0
- package/src/memory/parse.ts +22 -0
- package/src/memory/pipeline.ts +242 -30
- package/src/memory/prompts.ts +42 -0
- package/src/memory/query.ts +334 -0
- package/src/memory/reconcile.ts +734 -0
- package/src/memory/redirect.ts +108 -0
- package/src/memory/reindex.ts +325 -0
- package/src/memory/resolve.ts +307 -0
- package/src/memory/route.ts +272 -0
- package/src/memory/schedule.ts +6 -4
- package/src/memory/stages.ts +774 -0
- package/src/memory/synthesize.ts +1629 -20
- package/src/memory/tools.ts +446 -96
- package/src/memory/vault.ts +462 -35
- package/src/memory/wikify.ts +1057 -16
- package/src/mlx/array.ts +21 -0
- package/src/mlx/ffi.ts +3 -0
- package/src/mlx/ops.ts +72 -0
- package/src/model/compiled-decode.ts +43 -8
- package/src/model/factory.ts +29 -8
- package/src/model/flash-attention.ts +4 -2
- package/src/model/fused-decode-kernel.ts +14 -7
- package/src/model/fused-mlp-kernel.ts +169 -0
- package/src/model/fused-swiglu-kernel.ts +240 -0
- package/src/model/gemma4-base.ts +96 -7
- package/src/model/gemma4.ts +33 -2
- package/src/model/minicpm5.ts +106 -8
- package/src/model/qwen3-delta.ts +48 -1
- package/src/model/qwen3.ts +276 -0
- package/src/model/qwen3_5.ts +11 -2
- package/src/model/steel-linear-kernel.ts +96 -0
- package/src/model/support.ts +38 -8
- package/src/model/universal/archs.ts +332 -0
- package/src/model/universal/dense.ts +576 -0
- package/src/model/universal/modules.ts +163 -0
- package/src/model/universal/rope.ts +366 -0
- package/src/pi-provider.ts +1 -1
- package/src/pi-terminal.ts +29 -8
- package/src/pi-web.ts +19 -2
- package/src/prompt-cache.ts +55 -9
- package/src/quantize/config-writer.ts +1 -1
- package/src/registry.ts +219 -8
- package/src/responses.ts +23 -0
- package/src/sampler.ts +220 -9
- package/src/serve/batch-scheduler.ts +307 -83
- package/src/serve/generation-gateway.ts +156 -29
- package/src/server.ts +746 -73
- package/src/spec/drafter.ts +81 -49
- package/src/spec/dspark/data-dflash.ts +156 -0
- package/src/spec/dspark/data.ts +201 -0
- package/src/spec/dspark/generate-dflash.ts +193 -0
- package/src/spec/dspark/generate.ts +290 -0
- package/src/spec/dspark/loss.ts +213 -0
- package/src/spec/dspark/module-dflash.ts +361 -0
- package/src/spec/dspark/module.ts +540 -0
- package/src/spec/dspark/sample.ts +112 -0
- package/src/spec/generate.ts +40 -19
- package/src/ssd-cache.ts +213 -0
- package/src/status-page.html +1 -1
- package/src/tokenizer.ts +7 -0
- package/src/train/flash-cce.ts +81 -23
- package/src/train/fuse.ts +243 -0
- package/src/train/index.ts +3 -0
- package/src/train/job.ts +8 -0
- package/src/train/loss.ts +341 -40
- package/src/train/prefix-shared.ts +62 -11
- package/src/train/segmented.ts +215 -55
- package/src/train/trainer.ts +112 -38
- package/src/web/skills/memory/SKILL.md +40 -17
package/src/memory/pipeline.ts
CHANGED
|
@@ -1,18 +1,68 @@
|
|
|
1
|
-
// mlx-bun memory — synthesis pipeline orchestrator
|
|
1
|
+
// mlx-bun memory — synthesis pipeline orchestrator (P6-T4).
|
|
2
2
|
//
|
|
3
|
-
// Ties the
|
|
4
|
-
//
|
|
5
|
-
// `mlx-bun memory synthesize` and the nightly launchd job invoke.
|
|
3
|
+
// Ties the stage modules into the cold-start DAG:
|
|
4
|
+
// ingest → SEGMENT → ENTITY-EXTRACT → ROUTE → CREATE → NORMALIZE → commitVault.
|
|
6
5
|
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
6
|
+
// `ingest` + `SEGMENT` already populated the corpus (conversations / messages /
|
|
7
|
+
// chunks live in the MemoryStore), so for an already-chunked conversation
|
|
8
|
+
// SEGMENT is a no-op and the pipeline operates on the existing chunks. The model
|
|
9
|
+
// stages reuse the existing modules verbatim:
|
|
10
|
+
// - ENTITY-EXTRACT — entity.ts `extractEntities` (one chunk per call, persists
|
|
11
|
+
// chunk_entities + a candidate entities/aliases row via the variant resolver).
|
|
12
|
+
// - ROUTE — route.ts `RouteAccumulator` over the persisted chunk_entities
|
|
13
|
+
// (the deterministic canonical join + the B8/P1-T5 CREATE gate); the optional
|
|
14
|
+
// model substantive-engagement gate promotes thin-but-owned candidates.
|
|
15
|
+
// - CREATE/NORMALIZE/commit — synthesize.ts `synthesizeCreate` (sub-cluster →
|
|
16
|
+
// OUTLINE → per-section DRAFT → INFOBOX → assemble → NORMALIZE → gate → write
|
|
17
|
+
// + ledger), with a single `commitVault` at the end of the run.
|
|
12
18
|
//
|
|
13
|
-
//
|
|
19
|
+
// `runSynthesis` keeps the M1 event/summary contract `src/cli.ts` drives; it now
|
|
20
|
+
// runs the real DAG over the default store + vault. The FULL-corpus bootstrap is
|
|
21
|
+
// USER-ACTION (P6-T5) — the agent never starts it; `scripts/experiments`
|
|
22
|
+
// drives a bounded handful of conversations through `runPipeline` directly.
|
|
14
23
|
|
|
15
|
-
|
|
24
|
+
import type { ChunkCall } from "./chunk";
|
|
25
|
+
import type { SectionCall } from "./cluster";
|
|
26
|
+
import { runLinkStage } from "./crosslink";
|
|
27
|
+
import { MemoryStore } from "./db";
|
|
28
|
+
import type { ExtractCall } from "./entity";
|
|
29
|
+
import {
|
|
30
|
+
buildEntityMeta,
|
|
31
|
+
chronoChunkIds,
|
|
32
|
+
runExtractStage,
|
|
33
|
+
runRouteStage,
|
|
34
|
+
runSegmentStage,
|
|
35
|
+
runSynthesizeStage,
|
|
36
|
+
selectCreateTargets,
|
|
37
|
+
type EntityMeta,
|
|
38
|
+
type PipelineCreated,
|
|
39
|
+
type PipelinePatched,
|
|
40
|
+
type RouteDecision,
|
|
41
|
+
} from "./stages";
|
|
42
|
+
import type { SynthesisCall } from "./synthesize";
|
|
43
|
+
import { vaultRoot } from "./vault";
|
|
44
|
+
import { wikifyVault } from "./wikify";
|
|
45
|
+
|
|
46
|
+
// Re-export the factored-out pure helpers + types so existing importers (tests,
|
|
47
|
+
// scripts) keep their `from "./pipeline"` entry points.
|
|
48
|
+
export { buildEntityMeta, selectCreateTargets };
|
|
49
|
+
export type { EntityMeta, PipelineCreated, PipelinePatched, RouteDecision };
|
|
50
|
+
|
|
51
|
+
// ---- stage / event contract (consumed by src/cli.ts) -----------------------
|
|
52
|
+
|
|
53
|
+
export type SynthesisStage =
|
|
54
|
+
| "ingest"
|
|
55
|
+
| "segment"
|
|
56
|
+
| "chunk"
|
|
57
|
+
| "extract"
|
|
58
|
+
| "route"
|
|
59
|
+
| "create"
|
|
60
|
+
| "section-route"
|
|
61
|
+
| "patch"
|
|
62
|
+
| "reconcile"
|
|
63
|
+
| "link"
|
|
64
|
+
| "wikify"
|
|
65
|
+
| "commit";
|
|
16
66
|
|
|
17
67
|
export interface SynthesisEvent {
|
|
18
68
|
type: "stage" | "log" | "done";
|
|
@@ -23,41 +73,203 @@ export interface SynthesisEvent {
|
|
|
23
73
|
export interface SynthesisOptions {
|
|
24
74
|
/** Only synthesize conversations newer than this (ISO date / conv cursor). */
|
|
25
75
|
since?: string;
|
|
26
|
-
/** Override the synthesis model (default e4b).
|
|
76
|
+
/** Override the synthesis model (default e4b). Reserved — the stages resolve
|
|
77
|
+
* their own per-stage adapter via model.ts. */
|
|
27
78
|
model?: string;
|
|
28
|
-
/** Plan only — never write the vault. */
|
|
79
|
+
/** Plan only — never write the vault (deterministic, no model). */
|
|
29
80
|
dryRun?: boolean;
|
|
30
81
|
}
|
|
31
82
|
|
|
32
83
|
export interface SynthesisSummary {
|
|
33
|
-
/** False while stubbed; flip to true when the stages are implemented. */
|
|
34
84
|
implemented: boolean;
|
|
35
85
|
stages: SynthesisStage[];
|
|
36
86
|
note: string;
|
|
37
87
|
}
|
|
38
88
|
|
|
39
|
-
const STAGES: SynthesisStage[] = [
|
|
89
|
+
const STAGES: SynthesisStage[] = [
|
|
90
|
+
"ingest",
|
|
91
|
+
"segment",
|
|
92
|
+
"extract",
|
|
93
|
+
"route",
|
|
94
|
+
"create",
|
|
95
|
+
"section-route",
|
|
96
|
+
"patch",
|
|
97
|
+
"link",
|
|
98
|
+
"wikify",
|
|
99
|
+
"commit",
|
|
100
|
+
];
|
|
101
|
+
|
|
102
|
+
// ---- pipeline run ----------------------------------------------------------
|
|
103
|
+
|
|
104
|
+
export interface PipelineOptions {
|
|
105
|
+
/** Restrict the run to these conversation ids; default every conversation. */
|
|
106
|
+
convIds?: string[];
|
|
107
|
+
/** Vault root (honors MLX_BUN_WIKI); defaults to `vaultRoot()`. */
|
|
108
|
+
root?: string;
|
|
109
|
+
/** Max articles to CREATE this run (bounds a smoke's GPU cost). */
|
|
110
|
+
articleCap?: number;
|
|
111
|
+
/** Entities to force into the CREATE set when they earn a `create`. */
|
|
112
|
+
mustCreate?: string[];
|
|
113
|
+
/** Model-call seam for SEGMENT (the `chunk` stage — our e4b-chunk-300 /
|
|
114
|
+
* `memory-chunk` adapter). Defaults to `callLocal("chunk", …)` inside
|
|
115
|
+
* chunkConversations; tests inject a fake to avoid a real model load. */
|
|
116
|
+
segmentCall?: ChunkCall;
|
|
117
|
+
/** Model-call seam for the CREATE drafting AND the steady-state PATCH (both run
|
|
118
|
+
* the `synthesis` stage). Tests inject a fake. */
|
|
119
|
+
call?: SynthesisCall;
|
|
120
|
+
/** Model-call seam for ENTITY-EXTRACT (the `entity` stage). Tests inject a fake. */
|
|
121
|
+
extractCall?: ExtractCall;
|
|
122
|
+
/** Model-call seam for SECTION-ROUTE (the `section` stage binary/name calls).
|
|
123
|
+
* Tests inject a fake. */
|
|
124
|
+
sectionCall?: SectionCall;
|
|
125
|
+
/** Run the subject-engagement model gate to promote a thin (single-chunk)
|
|
126
|
+
* genuine-subject candidate to a `create` stub. Off by default (a recurring
|
|
127
|
+
* subject already creates). */
|
|
128
|
+
useSubjectGate?: boolean;
|
|
129
|
+
/** Skip the final git commit (tests). */
|
|
130
|
+
commit?: boolean;
|
|
131
|
+
now?: number;
|
|
132
|
+
onEvent?: (e: SynthesisEvent) => void;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export interface PipelineResult {
|
|
136
|
+
convs: number;
|
|
137
|
+
chunks: number;
|
|
138
|
+
/** Distinct resolved entities seen across the routed chunks. */
|
|
139
|
+
entities: number;
|
|
140
|
+
created: PipelineCreated[];
|
|
141
|
+
/** Sections of existing articles patched this run (the self-healing folds). */
|
|
142
|
+
patched: PipelinePatched[];
|
|
143
|
+
skippedByGate: string[];
|
|
144
|
+
/** Subjects captured (not yet articled) — still retrievable by search. */
|
|
145
|
+
captured: string[];
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Run the cold-start synthesis DAG over a bounded set of conversations.
|
|
150
|
+
*
|
|
151
|
+
* This is now a thin ORCHESTRATOR: it drives the four INDEPENDENT, chronological,
|
|
152
|
+
* resumable stage workers (`stages.ts`) in sequence — SEGMENT → ENTITY-EXTRACT →
|
|
153
|
+
* ROUTE → SYNTHESIZE (CREATE + SECTION-ROUTE/PATCH) — so the full DAG behaves as
|
|
154
|
+
* before while each stage is also separately runnable (`mlx-bun memory
|
|
155
|
+
* segment|extract|route|synthesize`). Returns a structured report; SYNTHESIZE
|
|
156
|
+
* commits once at the end via `commitVault`.
|
|
157
|
+
*/
|
|
158
|
+
export async function runPipeline(
|
|
159
|
+
store: MemoryStore,
|
|
160
|
+
opts: PipelineOptions = {},
|
|
161
|
+
): Promise<PipelineResult> {
|
|
162
|
+
const root = opts.root ?? vaultRoot();
|
|
163
|
+
const cap = opts.articleCap ?? 20;
|
|
164
|
+
const emit = opts.onEvent ?? (() => {});
|
|
165
|
+
|
|
166
|
+
emit({ type: "stage", stage: "ingest", message: `ingest: corpus already in the store` });
|
|
167
|
+
|
|
168
|
+
// SEGMENT — (re)chunk every in-scope conversation whose chunked_at is NULL or
|
|
169
|
+
// stale, oldest-first, then proceed on the fresh chunks. A re-run is a no-op
|
|
170
|
+
// (watermark). We do NOT reuse any external chunks.
|
|
171
|
+
await runSegmentStage(store, { convIds: opts.convIds, call: opts.segmentCall, onEvent: opts.onEvent });
|
|
172
|
+
|
|
173
|
+
// Chronological run scope (oldest conversation first) for the result counts.
|
|
174
|
+
const { convs, chunkIds } = chronoChunkIds(store, opts.convIds);
|
|
175
|
+
emit({ type: "log", message: `operating on ${convs} conversation(s), ${chunkIds.length} chunk(s)` });
|
|
176
|
+
|
|
177
|
+
// ENTITY-EXTRACT — every not-yet-extracted chunk, chronological + resumable.
|
|
178
|
+
await runExtractStage(store, { convIds: opts.convIds, call: opts.extractCall, onEvent: opts.onEvent });
|
|
179
|
+
|
|
180
|
+
// ROUTE — deterministic create/capture over chunk_entities + CREATE gate;
|
|
181
|
+
// persists notable + captures thin subjects into _captured (still searchable)
|
|
182
|
+
// so SYNTHESIZE reads the decision back.
|
|
183
|
+
const route = await runRouteStage(store, {
|
|
184
|
+
convIds: opts.convIds,
|
|
185
|
+
useSubjectGate: opts.useSubjectGate,
|
|
186
|
+
onEvent: opts.onEvent,
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
// SYNTHESIZE — chronological CREATE (oldest entity first, oldest chunk first so
|
|
190
|
+
// the latest statement dominates) + SECTION-ROUTE → PATCH self-healing fold.
|
|
191
|
+
const synth = await runSynthesizeStage(store, {
|
|
192
|
+
root,
|
|
193
|
+
limit: cap,
|
|
194
|
+
convIds: opts.convIds,
|
|
195
|
+
mustCreate: opts.mustCreate,
|
|
196
|
+
call: opts.call,
|
|
197
|
+
sectionCall: opts.sectionCall,
|
|
198
|
+
commit: opts.commit,
|
|
199
|
+
now: opts.now,
|
|
200
|
+
onEvent: opts.onEvent,
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
// CROSS-LINK — build the EDGES synthesis (bounded to one article) cannot:
|
|
204
|
+
// inline-link first mentions of other articles + rebuild each ## See also from
|
|
205
|
+
// mentions + co-occurrence. Deterministic (no model) and idempotent.
|
|
206
|
+
await runLinkStage(store, { root, commit: opts.commit, onEvent: (e) => emit({ type: e.type, message: e.message }) });
|
|
207
|
+
|
|
208
|
+
emit({
|
|
209
|
+
type: "done",
|
|
210
|
+
message: `synthesis done — ${synth.created.length} created, ${synth.patched.length} patched, ${synth.skippedByGate.length} gated, ${route.captured.length} captured.`,
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
return {
|
|
214
|
+
convs,
|
|
215
|
+
chunks: chunkIds.length,
|
|
216
|
+
entities: route.decisions.length,
|
|
217
|
+
created: synth.created,
|
|
218
|
+
patched: synth.patched,
|
|
219
|
+
skippedByGate: synth.skippedByGate,
|
|
220
|
+
captured: route.captured,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// ---- cli entry: the M1 contract src/cli.ts drives --------------------------
|
|
40
225
|
|
|
41
226
|
/**
|
|
42
|
-
* Run the synthesis pipeline
|
|
43
|
-
*
|
|
44
|
-
*
|
|
227
|
+
* Run the synthesis pipeline over the default store + vault — the `mlx-bun
|
|
228
|
+
* memory synthesize` path. Keeps the {@link SynthesisSummary} shape the CLI
|
|
229
|
+
* prints. The FULL-corpus run is a long GPU job (USER-ACTION, P6-T5); this entry
|
|
230
|
+
* exists so the wiring is complete — the agent never starts it.
|
|
45
231
|
*/
|
|
46
232
|
export async function runSynthesis(
|
|
47
233
|
opts: SynthesisOptions = {},
|
|
48
234
|
onEvent?: (e: SynthesisEvent) => void,
|
|
49
235
|
): Promise<SynthesisSummary> {
|
|
50
|
-
|
|
51
|
-
type: "log",
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
236
|
+
if (opts.dryRun) {
|
|
237
|
+
onEvent?.({ type: "log", message: "dry-run — no model calls, nothing will be written." });
|
|
238
|
+
for (const stage of STAGES) onEvent?.({ type: "stage", stage, message: `${stage}: planned (dry-run)` });
|
|
239
|
+
onEvent?.({ type: "done", message: "dry-run complete — no articles written." });
|
|
240
|
+
return { implemented: true, stages: STAGES, note: "dry-run: DAG wired; no model calls made." };
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const store = new MemoryStore();
|
|
244
|
+
try {
|
|
245
|
+
// WRITE branch: ingest → segment → extract → route → create → commit.
|
|
246
|
+
const result = await runPipeline(store, { onEvent });
|
|
247
|
+
|
|
248
|
+
// WIKIFY branch: the periodic editorial sweep over EVERY article (P8). Runs
|
|
249
|
+
// after the write branch so freshly-created and patched articles are tightened
|
|
250
|
+
// + infobox-refreshed in the same nightly pass; each article commits its own
|
|
251
|
+
// change (or NO-OPs) behind the same conservative gate. Best-effort: a wikify
|
|
252
|
+
// defect must not lose the create-branch work already committed above.
|
|
253
|
+
onEvent?.({ type: "stage", stage: "wikify", message: "wikify: editorial sweep over every article" });
|
|
254
|
+
let editedCount = 0;
|
|
255
|
+
try {
|
|
256
|
+
const sweep = await wikifyVault({ root: vaultRoot() });
|
|
257
|
+
editedCount = sweep.edited.length;
|
|
258
|
+
onEvent?.({ type: "log", message: ` wikify edited ${editedCount}/${sweep.results.length} article(s)` });
|
|
259
|
+
} catch (err) {
|
|
260
|
+
onEvent?.({ type: "log", message: ` wikify sweep skipped (error: ${String(err)})` });
|
|
261
|
+
}
|
|
262
|
+
onEvent?.({
|
|
263
|
+
type: "done",
|
|
264
|
+
message: `synthesis done — ${result.created.length} created, ${result.patched.length} patched, ${editedCount} wikified.`,
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
return {
|
|
268
|
+
implemented: true,
|
|
269
|
+
stages: STAGES,
|
|
270
|
+
note: `synthesis wired — ${result.created.length} created, ${result.patched.length} patched, ${editedCount} wikified from ${result.convs} conversations.`,
|
|
271
|
+
};
|
|
272
|
+
} finally {
|
|
273
|
+
store.close();
|
|
56
274
|
}
|
|
57
|
-
onEvent?.({ type: "done", message: "no articles were written (synthesis is stubbed)." });
|
|
58
|
-
return {
|
|
59
|
-
implemented: false,
|
|
60
|
-
stages: STAGES,
|
|
61
|
-
note: "M1 stub — pipeline + scheduling wired; stage modules not implemented.",
|
|
62
|
-
};
|
|
63
275
|
}
|
package/src/memory/prompts.ts
CHANGED
|
@@ -9,6 +9,11 @@
|
|
|
9
9
|
//
|
|
10
10
|
// See docs/design/memory-system.md → "Synthesis runs on the local model".
|
|
11
11
|
|
|
12
|
+
import { readFileSync } from "node:fs";
|
|
13
|
+
import { join } from "node:path";
|
|
14
|
+
|
|
15
|
+
import { vaultRoot } from "./vault";
|
|
16
|
+
|
|
12
17
|
export const CHUNK_PROMPT = `TODO(M1): segment a conversation transcript into topic-coherent chunks.`;
|
|
13
18
|
|
|
14
19
|
export const CLUSTER_PROMPT = `TODO(M1): assign a chunk to an existing bucket from Meta/Buckets.md, or propose a new bucket.`;
|
|
@@ -21,3 +26,40 @@ export const STAGE_PROMPTS: Record<string, string> = {
|
|
|
21
26
|
cluster: CLUSTER_PROMPT,
|
|
22
27
|
synthesize: SYNTHESIZE_PROMPT,
|
|
23
28
|
};
|
|
29
|
+
|
|
30
|
+
// ---- meta-policy inlining (ported from lucien scripts/meta-inline.ts) ------
|
|
31
|
+
//
|
|
32
|
+
// Editorial policy lives in the vault's Meta/ pages, not in code. Rather than
|
|
33
|
+
// telling the model to Read those pages at synthesis time (which turns every
|
|
34
|
+
// pipeline call into an agent loop whose context accumulates — a 34k-char
|
|
35
|
+
// conversation was observed ballooning to a 105k-token prefill on the local
|
|
36
|
+
// server), we inline the requested pages directly into the prompt. Calls stay
|
|
37
|
+
// single-turn: one bounded prefill, one bounded generation. The policy is
|
|
38
|
+
// edit-in-the-vault: change a Meta page on disk and the next run reflects it
|
|
39
|
+
// with zero code change.
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Read the named Meta policy pages from the vault's Meta/ dir and concatenate
|
|
43
|
+
* them for inlining into a stage prompt. Each `name` addresses
|
|
44
|
+
* `<vault>/Meta/<name>.md` (a trailing `.md` is accepted and normalized).
|
|
45
|
+
*
|
|
46
|
+
* The vault root honors the MLX_BUN_WIKI override (see {@link vaultRoot}).
|
|
47
|
+
*
|
|
48
|
+
* @throws {Error} If a requested Meta page does not exist on disk, naming it.
|
|
49
|
+
*/
|
|
50
|
+
export function loadMetaPolicy(names: string[]): string {
|
|
51
|
+
const metaDir = join(vaultRoot(), "Meta");
|
|
52
|
+
const sections: string[] = [];
|
|
53
|
+
for (const name of names) {
|
|
54
|
+
const stem = name.trim().endsWith(".md") ? name.trim().slice(0, -3) : name.trim();
|
|
55
|
+
const path = join(metaDir, `${stem}.md`);
|
|
56
|
+
let text: string;
|
|
57
|
+
try {
|
|
58
|
+
text = readFileSync(path, "utf8");
|
|
59
|
+
} catch {
|
|
60
|
+
throw new Error(`Meta policy page not found: ${path}`);
|
|
61
|
+
}
|
|
62
|
+
sections.push(`--- Meta/${stem}.md ---\n\n${text.trim()}`);
|
|
63
|
+
}
|
|
64
|
+
return sections.join("\n\n");
|
|
65
|
+
}
|