akm-cli 0.9.0-beta.46 → 0.9.0-beta.48
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/assets/wiki/ingest-workflow-template.md +17 -10
- package/dist/core/config/config-schema.js +55 -69
- package/dist/indexer/search/semantic-status.js +4 -0
- package/dist/llm/embedder.js +15 -0
- package/dist/llm/embedders/deterministic.js +66 -0
- package/dist/scripts/migrate-storage.js +42 -66
- package/dist/sources/wiki-fetchers/youtube.js +62 -4
- package/package.json +1 -1
|
@@ -6,20 +6,27 @@ Schema: {{SCHEMA_PATH}}
|
|
|
6
6
|
Follow these steps. akm commands handle the invariants; use your native
|
|
7
7
|
Read/Write/Edit tools for page edits.
|
|
8
8
|
|
|
9
|
+
This workflow is for ingesting sources that are ALREADY present under
|
|
10
|
+
`{{WIKI_DIR}}/raw/`. Do not ask the user for a source unless the raw queue is
|
|
11
|
+
empty and the caller explicitly asked for interactive ingest.
|
|
12
|
+
|
|
9
13
|
1. **Read the schema.** Open `{{SCHEMA_PATH}}`. It defines the voice, page
|
|
10
14
|
kinds, contradiction policy, and any wiki-specific conventions. Do not
|
|
11
15
|
skip this step even on familiar wikis — the schema may have changed.
|
|
12
16
|
|
|
13
|
-
2. **
|
|
17
|
+
2. **Discover the pending raw queue.**
|
|
14
18
|
```sh
|
|
15
|
-
akm wiki
|
|
16
|
-
# or: cat <source> | akm wiki stash {{WIKI_NAME}} -
|
|
19
|
+
akm wiki lint {{WIKI_NAME}}
|
|
17
20
|
```
|
|
18
|
-
|
|
21
|
+
Focus on `uncited-raw` findings: those raw files exist under `raw/` but are
|
|
22
|
+
not yet cited by any authored page. Treat each `uncited-raw` finding as a
|
|
23
|
+
pending ingest item. If there are no `uncited-raw` findings, exit cleanly
|
|
24
|
+
after a final `akm index` + `akm wiki lint {{WIKI_NAME}}` verification.
|
|
19
25
|
|
|
20
|
-
3. **
|
|
26
|
+
3. **For each pending raw file, read the source and find related pages.**
|
|
27
|
+
Open the raw file directly from `{{WIKI_DIR}}/raw/`, then search:
|
|
21
28
|
```sh
|
|
22
|
-
akm wiki search {{WIKI_NAME}} "<key terms from the source>"
|
|
29
|
+
akm wiki search {{WIKI_NAME}} "<key terms from the raw source>"
|
|
23
30
|
```
|
|
24
31
|
Read the top hits with `akm show wiki:{{WIKI_NAME}}/<page>`. Use
|
|
25
32
|
`akm show wiki:{{WIKI_NAME}}/<page> toc` for large pages.
|
|
@@ -39,8 +46,8 @@ Read/Write/Edit tools for page edits.
|
|
|
39
46
|
6. **Update xrefs both ways.** If page A now xrefs page B, page B must xref
|
|
40
47
|
page A. `akm wiki lint {{WIKI_NAME}}` will flag violations.
|
|
41
48
|
|
|
42
|
-
7. **Append to `log.md`.** One entry per
|
|
43
|
-
summary, refs to created/edited pages. Newest at the top.
|
|
49
|
+
7. **Append to `log.md`.** One entry per ingested raw source: date, raw slug,
|
|
50
|
+
one-line summary, refs to created/edited pages. Newest at the top.
|
|
44
51
|
|
|
45
52
|
8. **Regenerate the index + verify.**
|
|
46
53
|
```sh
|
|
@@ -50,5 +57,5 @@ Read/Write/Edit tools for page edits.
|
|
|
50
57
|
Resolve any lint findings before calling the ingest done.
|
|
51
58
|
|
|
52
59
|
That's it. `akm` never calls an LLM — reasoning is your job; it just owns
|
|
53
|
-
the invariants (raw immutability,
|
|
54
|
-
|
|
60
|
+
the invariants (raw immutability, ref validation, index regeneration,
|
|
61
|
+
structural lint).
|
|
@@ -22,8 +22,14 @@
|
|
|
22
22
|
* - Two exceptions (hard-rejected): openviking source type and legacy
|
|
23
23
|
* `stashes[]` key. Both have explicit migration paths; silently dropping
|
|
24
24
|
* would mask user data loss.
|
|
25
|
-
* -
|
|
26
|
-
*
|
|
25
|
+
* - UNKNOWN-KEY POLICY: object schemas use passthrough (unknown keys are
|
|
26
|
+
* preserved and ignored, NOT rejected). akm runs across multiple installed
|
|
27
|
+
* versions sharing one config.json; a newer version writes keys an older
|
|
28
|
+
* version's schema doesn't know yet, so hard-rejecting unknown keys turned
|
|
29
|
+
* benign version skew into `INVALID_CONFIG_FILE` failures. Known keys are
|
|
30
|
+
* still type-checked; passthrough preserves unknown keys across a
|
|
31
|
+
* load→save round trip so an older reader never strips a newer writer's
|
|
32
|
+
* settings. (Replaced the prior strict-mode object walls.)
|
|
27
33
|
* - `defaultWriteTarget` resolution and similar cross-field invariants are
|
|
28
34
|
* enforced at save time via `superRefine` on the top-level schema.
|
|
29
35
|
*/
|
|
@@ -50,7 +56,7 @@ const LlmCapabilitiesSchema = z
|
|
|
50
56
|
.object({
|
|
51
57
|
structuredOutput: z.boolean().optional(),
|
|
52
58
|
})
|
|
53
|
-
.
|
|
59
|
+
.passthrough();
|
|
54
60
|
/**
|
|
55
61
|
* Connection config used for both top-level `llm` (after migration) and
|
|
56
62
|
* `profiles.llm[*]`. `model` is required at schema level — partial entries
|
|
@@ -75,15 +81,15 @@ export const LlmConnectionConfigSchema = z
|
|
|
75
81
|
judgeModel: z.string().min(1).optional(),
|
|
76
82
|
enableThinking: z.boolean().optional(),
|
|
77
83
|
})
|
|
78
|
-
.
|
|
84
|
+
.passthrough();
|
|
79
85
|
export const LlmProfileConfigSchema = LlmConnectionConfigSchema.extend({
|
|
80
86
|
supportsJsonSchema: z.boolean().optional(),
|
|
81
|
-
}).
|
|
87
|
+
}).passthrough();
|
|
82
88
|
const EmbeddingOllamaOptionsSchema = z
|
|
83
89
|
.object({
|
|
84
90
|
num_ctx: positiveInt.optional(),
|
|
85
91
|
})
|
|
86
|
-
.
|
|
92
|
+
.passthrough();
|
|
87
93
|
/**
|
|
88
94
|
* Embedding connection config. Both `endpoint` and `model` are optional:
|
|
89
95
|
* - Remote: provide `endpoint` (http/https URL) + `model`.
|
|
@@ -107,7 +113,7 @@ export const EmbeddingConnectionConfigSchema = z
|
|
|
107
113
|
contextLength: positiveInt.optional(),
|
|
108
114
|
ollamaOptions: EmbeddingOllamaOptionsSchema.optional(),
|
|
109
115
|
})
|
|
110
|
-
.
|
|
116
|
+
.passthrough();
|
|
111
117
|
// ── Agent profiles ──────────────────────────────────────────────────────────
|
|
112
118
|
// Derives from the canonical VALID_HARNESS_IDS (#565) so the Zod gate cannot
|
|
113
119
|
// drift from the TS union / parse check / setup detection.
|
|
@@ -120,7 +126,7 @@ export const AgentProfileConfigSchema = z
|
|
|
120
126
|
workspace: z.string().min(1).optional(),
|
|
121
127
|
model: z.string().min(1).optional(),
|
|
122
128
|
})
|
|
123
|
-
.
|
|
129
|
+
.passthrough();
|
|
124
130
|
// ── Improve profile / process ──────────────────────────────────────────────
|
|
125
131
|
export const ImproveProcessConfigSchema = z
|
|
126
132
|
.object({
|
|
@@ -151,7 +157,7 @@ export const ImproveProcessConfigSchema = z
|
|
|
151
157
|
// with care — cost is O(n²).
|
|
152
158
|
cosineCandidateLimit: z.number().int().positive().optional(),
|
|
153
159
|
})
|
|
154
|
-
.
|
|
160
|
+
.passthrough()
|
|
155
161
|
.optional(),
|
|
156
162
|
// Consolidate process: judged-state cache (#581). When enabled, a memory
|
|
157
163
|
// whose current content hash equals its cached judged hash is SKIPPED from
|
|
@@ -160,9 +166,9 @@ export const ImproveProcessConfigSchema = z
|
|
|
160
166
|
// time-window slice. Default OFF — when absent the consolidate pass behaves
|
|
161
167
|
// byte-identically to today (the incrementalSince path is unaffected). Only
|
|
162
168
|
// meaningful on the `consolidate` process.
|
|
163
|
-
judgedCache: z.object({ enabled: z.boolean().optional() }).
|
|
164
|
-
qualityGate: z.object({ enabled: z.boolean().optional() }).
|
|
165
|
-
contradictionDetection: z.object({ enabled: z.boolean().optional() }).
|
|
169
|
+
judgedCache: z.object({ enabled: z.boolean().optional() }).passthrough().optional(),
|
|
170
|
+
qualityGate: z.object({ enabled: z.boolean().optional() }).passthrough().optional(),
|
|
171
|
+
contradictionDetection: z.object({ enabled: z.boolean().optional() }).passthrough().optional(),
|
|
166
172
|
// Extract process config (only meaningful for extract process)
|
|
167
173
|
defaultSince: z.string().min(1).optional(),
|
|
168
174
|
maxTotalChars: positiveInt.optional(),
|
|
@@ -236,7 +242,7 @@ export const ImproveProcessConfigSchema = z
|
|
|
236
242
|
// Demotion factor: multiply retrievalSalience by this when stale (default 0.5).
|
|
237
243
|
demotionFactor: z.number().min(0).max(1).optional(),
|
|
238
244
|
})
|
|
239
|
-
.
|
|
245
|
+
.passthrough()
|
|
240
246
|
.optional(),
|
|
241
247
|
// WS-3b: Schema-similarity gate (step 0b). At intake, if a new candidate's
|
|
242
248
|
// body embedding is within epsilon of an existing derived-layer lesson/knowledge
|
|
@@ -253,7 +259,7 @@ export const ImproveProcessConfigSchema = z
|
|
|
253
259
|
// to pass the quality gate and create redundant stash entries.
|
|
254
260
|
confidencePenalty: z.number().min(0).max(1).optional(),
|
|
255
261
|
})
|
|
256
|
-
.
|
|
262
|
+
.passthrough()
|
|
257
263
|
.optional(),
|
|
258
264
|
// WS-3b: Hot-probation intake buffer (step 0c, #604). New system-generated
|
|
259
265
|
// extractions enter captureMode: hot-probation and spend ONE consolidation
|
|
@@ -263,7 +269,7 @@ export const ImproveProcessConfigSchema = z
|
|
|
263
269
|
.object({
|
|
264
270
|
enabled: z.boolean().optional(),
|
|
265
271
|
})
|
|
266
|
-
.
|
|
272
|
+
.passthrough()
|
|
267
273
|
.optional(),
|
|
268
274
|
// WS-3b: Anti-collapse guards (step 8). Prevents the consolidation pipeline
|
|
269
275
|
// from collapsing too aggressively and losing diversity.
|
|
@@ -278,7 +284,7 @@ export const ImproveProcessConfigSchema = z
|
|
|
278
284
|
lexicalDiversityCheck: z.boolean().optional(),
|
|
279
285
|
randomClusterFraction: z.number().min(0).max(1).optional(),
|
|
280
286
|
})
|
|
281
|
-
.
|
|
287
|
+
.passthrough()
|
|
282
288
|
.optional(),
|
|
283
289
|
// WS-3b: CLS (Complementary Learning System) interleaving (step 9).
|
|
284
290
|
// distill/memoryInference prompts include embedding-retrieved existing adjacent
|
|
@@ -290,7 +296,7 @@ export const ImproveProcessConfigSchema = z
|
|
|
290
296
|
// Number of adjacent lessons/knowledge to include in prompts (default 3).
|
|
291
297
|
adjacentCount: z.number().int().min(1).optional(),
|
|
292
298
|
})
|
|
293
|
-
.
|
|
299
|
+
.passthrough()
|
|
294
300
|
.optional(),
|
|
295
301
|
// WS-3b: Distill→source fidelity check (step 10). After a distill proposal,
|
|
296
302
|
// check it against its cited source memories; a contradiction flag forces
|
|
@@ -299,7 +305,7 @@ export const ImproveProcessConfigSchema = z
|
|
|
299
305
|
.object({
|
|
300
306
|
enabled: z.boolean().optional(),
|
|
301
307
|
})
|
|
302
|
-
.
|
|
308
|
+
.passthrough()
|
|
303
309
|
.optional(),
|
|
304
310
|
// #609 — recombine process: minimum related-memory cluster size before an
|
|
305
311
|
// LLM generalization call. Default 3. Only meaningful on `recombine`.
|
|
@@ -342,7 +348,7 @@ export const ImproveProcessConfigSchema = z
|
|
|
342
348
|
// enabled, proposals classified as "low-value" by the deterministic noise
|
|
343
349
|
// gate are deferred. DEFAULT OFF (absent / { enabled: false } = pre-#639
|
|
344
350
|
// byte-identical behaviour). Only meaningful on the `reflect` process.
|
|
345
|
-
lowValueFilter: z.object({ enabled: z.boolean().optional() }).
|
|
351
|
+
lowValueFilter: z.object({ enabled: z.boolean().optional() }).passthrough().optional(),
|
|
346
352
|
// #641 — procedural-aware floor for the `extract` process triage gate.
|
|
347
353
|
// When true, a session must have markers>=1 OR editCommit>=0.5 to pass, even
|
|
348
354
|
// if score>=minScore. DEFAULT OFF (absent/false = pre-#641 byte-identical).
|
|
@@ -360,10 +366,10 @@ export const ImproveProcessConfigSchema = z
|
|
|
360
366
|
profile: z.string().min(1).optional(),
|
|
361
367
|
timeoutMs: z.union([positiveInt, z.null()]).optional(),
|
|
362
368
|
})
|
|
363
|
-
.
|
|
369
|
+
.passthrough()
|
|
364
370
|
.optional(),
|
|
365
371
|
})
|
|
366
|
-
.
|
|
372
|
+
.passthrough();
|
|
367
373
|
const ImproveProfileProcessesSchema = z
|
|
368
374
|
.object({
|
|
369
375
|
reflect: ImproveProcessConfigSchema.optional(),
|
|
@@ -380,37 +386,17 @@ const ImproveProfileProcessesSchema = z
|
|
|
380
386
|
.passthrough()
|
|
381
387
|
.superRefine((val, ctx) => {
|
|
382
388
|
// 0.8.0 removed the duplicated `feedbackDistillation` process key — it was
|
|
383
|
-
// a thin wrapper around `processes.distill.enabled`.
|
|
384
|
-
|
|
385
|
-
|
|
389
|
+
// a thin wrapper around `processes.distill.enabled`. Keep the migration
|
|
390
|
+
// hint so a stale config gets an actionable message rather than silently
|
|
391
|
+
// doing nothing. All OTHER unknown process keys are tolerated (passthrough)
|
|
392
|
+
// — see the unknown-key policy in this file's header. Hard-rejecting them
|
|
393
|
+
// turned benign cross-version skew into INVALID_CONFIG_FILE failures.
|
|
394
|
+
if ("feedbackDistillation" in val) {
|
|
386
395
|
ctx.addIssue({
|
|
387
396
|
code: z.ZodIssueCode.custom,
|
|
388
397
|
message: "feedbackDistillation was removed in 0.8.0 — use processes.distill.enabled instead. " +
|
|
389
398
|
"It now controls both the orchestration gate and the LLM-call gate.",
|
|
390
399
|
});
|
|
391
|
-
return;
|
|
392
|
-
}
|
|
393
|
-
const allowed = new Set([
|
|
394
|
-
"reflect",
|
|
395
|
-
"distill",
|
|
396
|
-
"consolidate",
|
|
397
|
-
"memoryInference",
|
|
398
|
-
"graphExtraction",
|
|
399
|
-
"validation",
|
|
400
|
-
"extract",
|
|
401
|
-
"triage",
|
|
402
|
-
"proactiveMaintenance",
|
|
403
|
-
"recombine",
|
|
404
|
-
"procedural",
|
|
405
|
-
]);
|
|
406
|
-
for (const k of Object.keys(raw)) {
|
|
407
|
-
if (!allowed.has(k)) {
|
|
408
|
-
ctx.addIssue({
|
|
409
|
-
code: z.ZodIssueCode.unrecognized_keys,
|
|
410
|
-
keys: [k],
|
|
411
|
-
message: `Unrecognized improve process key: "${k}".`,
|
|
412
|
-
});
|
|
413
|
-
}
|
|
414
400
|
}
|
|
415
401
|
});
|
|
416
402
|
export const ImproveProfileConfigSchema = z
|
|
@@ -435,10 +421,10 @@ export const ImproveProfileConfigSchema = z
|
|
|
435
421
|
push: z.boolean().optional(),
|
|
436
422
|
message: z.string().min(1).optional(),
|
|
437
423
|
})
|
|
438
|
-
.
|
|
424
|
+
.passthrough()
|
|
439
425
|
.optional(),
|
|
440
426
|
})
|
|
441
|
-
.
|
|
427
|
+
.passthrough();
|
|
442
428
|
// ── Profiles / defaults ────────────────────────────────────────────────────
|
|
443
429
|
export const ProfilesSchema = z
|
|
444
430
|
.object({
|
|
@@ -446,14 +432,14 @@ export const ProfilesSchema = z
|
|
|
446
432
|
agent: z.record(z.string(), AgentProfileConfigSchema).optional(),
|
|
447
433
|
improve: z.record(z.string(), ImproveProfileConfigSchema).optional(),
|
|
448
434
|
})
|
|
449
|
-
.
|
|
435
|
+
.passthrough();
|
|
450
436
|
export const DefaultsSchema = z
|
|
451
437
|
.object({
|
|
452
438
|
llm: z.string().min(1).optional(),
|
|
453
439
|
agent: z.string().min(1).optional(),
|
|
454
440
|
improve: z.string().min(1).optional(),
|
|
455
441
|
})
|
|
456
|
-
.
|
|
442
|
+
.passthrough();
|
|
457
443
|
// ── Sources / registries / installed ────────────────────────────────────────
|
|
458
444
|
const SourceConfigEntryOptionsSchema = z
|
|
459
445
|
.object({
|
|
@@ -477,7 +463,7 @@ export const SourceConfigEntrySchema = z
|
|
|
477
463
|
options: SourceConfigEntryOptionsSchema.optional(),
|
|
478
464
|
wikiName: z.string().min(1).optional(),
|
|
479
465
|
})
|
|
480
|
-
.
|
|
466
|
+
.passthrough()
|
|
481
467
|
.superRefine((entry, ctx) => {
|
|
482
468
|
if (entry.writable === true && (entry.type === "website" || entry.type === "npm")) {
|
|
483
469
|
ctx.addIssue({
|
|
@@ -496,7 +482,7 @@ export const RegistryConfigEntrySchema = z
|
|
|
496
482
|
provider: z.string().min(1).optional(),
|
|
497
483
|
options: z.record(z.unknown()).optional(),
|
|
498
484
|
})
|
|
499
|
-
.
|
|
485
|
+
.passthrough();
|
|
500
486
|
const KitSourceSchema = z.enum(["filesystem", "git", "npm", "github", "website", "local"]);
|
|
501
487
|
export const InstalledStashEntrySchema = z
|
|
502
488
|
.object({
|
|
@@ -512,7 +498,7 @@ export const InstalledStashEntrySchema = z
|
|
|
512
498
|
resolvedRevision: z.string().min(1).optional(),
|
|
513
499
|
wikiName: z.string().min(1).optional(),
|
|
514
500
|
})
|
|
515
|
-
.
|
|
501
|
+
.passthrough()
|
|
516
502
|
.superRefine((entry, ctx) => {
|
|
517
503
|
if (entry.writable === true && entry.source !== "git" && entry.source !== "filesystem") {
|
|
518
504
|
ctx.addIssue({
|
|
@@ -527,7 +513,7 @@ export const OutputConfigSchema = z
|
|
|
527
513
|
format: z.enum(["json", "yaml", "text"]).optional(),
|
|
528
514
|
detail: z.enum(["brief", "normal", "full"]).optional(),
|
|
529
515
|
})
|
|
530
|
-
.
|
|
516
|
+
.passthrough();
|
|
531
517
|
// ── Search ──────────────────────────────────────────────────────────────────
|
|
532
518
|
const SearchGraphBoostSchema = z
|
|
533
519
|
.object({
|
|
@@ -541,29 +527,29 @@ const SearchGraphBoostSchema = z
|
|
|
541
527
|
/** Range [0, 1]; values > 1 hard-error (no silent clamp). */
|
|
542
528
|
confidenceWeight: z.number().finite().min(0).max(1).default(0.2).optional(),
|
|
543
529
|
})
|
|
544
|
-
.
|
|
530
|
+
.passthrough();
|
|
545
531
|
export const SearchConfigSchema = z
|
|
546
532
|
.object({
|
|
547
533
|
minScore: nonNegativeNumber.optional(),
|
|
548
534
|
defaultExcludeTypes: z.array(nonEmptyString).optional(),
|
|
549
|
-
curateRerank: z.object({ enabled: z.boolean().optional() }).
|
|
535
|
+
curateRerank: z.object({ enabled: z.boolean().optional() }).passthrough().optional(),
|
|
550
536
|
graphBoost: SearchGraphBoostSchema.optional(),
|
|
551
537
|
})
|
|
552
|
-
.
|
|
538
|
+
.passthrough();
|
|
553
539
|
// ── Feedback ────────────────────────────────────────────────────────────────
|
|
554
540
|
export const FeedbackConfigSchema = z
|
|
555
541
|
.object({
|
|
556
542
|
requireReason: z.boolean().optional(),
|
|
557
543
|
allowedFailureModes: z.array(nonEmptyString).optional(),
|
|
558
544
|
})
|
|
559
|
-
.
|
|
545
|
+
.passthrough();
|
|
560
546
|
// ── Improve top-level (utility decay, event retention) ─────────────────────
|
|
561
547
|
const ImproveUtilityDecaySchema = z
|
|
562
548
|
.object({
|
|
563
549
|
halfLifeDays: z.number().finite().min(0.1).optional(),
|
|
564
550
|
feedbackStabilityBoost: z.number().finite().min(1).optional(),
|
|
565
551
|
})
|
|
566
|
-
.
|
|
552
|
+
.passthrough();
|
|
567
553
|
// #612 / WS-4 — auto-accept gate calibration + bounded, opt-in per-phase
|
|
568
554
|
// threshold auto-tune. DEFAULT OFF: when absent (or `autoTune: false`) no
|
|
569
555
|
// tuning occurs, so the gate behaves byte-identically to today.
|
|
@@ -586,7 +572,7 @@ const ImproveCalibrationSchema = z
|
|
|
586
572
|
/** Target realized accept rate in [0, 1]. Default 0.9. */
|
|
587
573
|
targetAcceptRate: z.number().finite().min(0).max(1).optional(),
|
|
588
574
|
})
|
|
589
|
-
.
|
|
575
|
+
.passthrough();
|
|
590
576
|
// WS-4 — exploration budget: a fixed fraction of proposals accepted per run
|
|
591
577
|
// regardless of confidence. DEFAULT OFF.
|
|
592
578
|
const ImproveExplorationSchema = z
|
|
@@ -602,7 +588,7 @@ const ImproveExplorationSchema = z
|
|
|
602
588
|
*/
|
|
603
589
|
budgetFraction: z.number().finite().min(0).max(1).optional(),
|
|
604
590
|
})
|
|
605
|
-
.
|
|
591
|
+
.passthrough();
|
|
606
592
|
const ImproveSalienceSchema = z
|
|
607
593
|
.object({
|
|
608
594
|
/**
|
|
@@ -624,7 +610,7 @@ const ImproveSalienceSchema = z
|
|
|
624
610
|
*/
|
|
625
611
|
replayBudget: z.number().int().min(0).optional(),
|
|
626
612
|
})
|
|
627
|
-
.
|
|
613
|
+
.passthrough();
|
|
628
614
|
export const ImproveConfigSchema = z
|
|
629
615
|
.object({
|
|
630
616
|
utilityDecay: ImproveUtilityDecaySchema.optional(),
|
|
@@ -633,7 +619,7 @@ export const ImproveConfigSchema = z
|
|
|
633
619
|
exploration: ImproveExplorationSchema.optional(),
|
|
634
620
|
salience: ImproveSalienceSchema.optional(),
|
|
635
621
|
})
|
|
636
|
-
.
|
|
622
|
+
.passthrough();
|
|
637
623
|
// ── Index / per-pass ────────────────────────────────────────────────────────
|
|
638
624
|
const GRAPH_EXTRACTION_INCLUDE_TYPES_ALLOWED = [
|
|
639
625
|
"memory",
|
|
@@ -712,13 +698,13 @@ export const IndexPassConfigSchema = z.preprocess((raw, ctx) => {
|
|
|
712
698
|
lazyGraphExtraction: z.boolean().optional(),
|
|
713
699
|
})
|
|
714
700
|
.passthrough());
|
|
715
|
-
const MetadataEnhanceSchema = z.object({ enabled: z.boolean().optional() }).
|
|
701
|
+
const MetadataEnhanceSchema = z.object({ enabled: z.boolean().optional() }).passthrough();
|
|
716
702
|
const StalenessDetectionSchema = z
|
|
717
703
|
.object({
|
|
718
704
|
enabled: z.boolean().optional(),
|
|
719
705
|
thresholdDays: positiveInt.optional(),
|
|
720
706
|
})
|
|
721
|
-
.
|
|
707
|
+
.passthrough();
|
|
722
708
|
/**
|
|
723
709
|
* Index config is a union of reserved feature sections and per-pass entries.
|
|
724
710
|
* Passthrough so per-pass entries (keyed by arbitrary pass names like `graph`,
|
|
@@ -791,12 +777,12 @@ export const SetupTaskSchedulesSchema = z
|
|
|
791
777
|
improve: z.string().min(1).optional(),
|
|
792
778
|
index: z.string().min(1).optional(),
|
|
793
779
|
})
|
|
794
|
-
.
|
|
780
|
+
.passthrough();
|
|
795
781
|
export const SetupConfigSchema = z
|
|
796
782
|
.object({
|
|
797
783
|
taskSchedules: SetupTaskSchedulesSchema.optional(),
|
|
798
784
|
})
|
|
799
|
-
.
|
|
785
|
+
.passthrough();
|
|
800
786
|
// ── Top-level AkmConfig ────────────────────────────────────────────────────
|
|
801
787
|
/**
|
|
802
788
|
* Base object schema used both as the top-level shape and as the source of
|
|
@@ -828,7 +814,7 @@ export const AkmConfigShape = {
|
|
|
828
814
|
improve: ImproveConfigSchema.optional(),
|
|
829
815
|
setup: SetupConfigSchema.optional(),
|
|
830
816
|
};
|
|
831
|
-
export const AkmConfigBaseSchema = z.object(AkmConfigShape).
|
|
817
|
+
export const AkmConfigBaseSchema = z.object(AkmConfigShape).passthrough();
|
|
832
818
|
export const AkmConfigSchema = AkmConfigBaseSchema.superRefine((config, ctx) => {
|
|
833
819
|
// #464.a: defaultWriteTarget must name a configured source when sources
|
|
834
820
|
// are present. With no sources configured, error out instead of silently
|
|
@@ -4,8 +4,12 @@
|
|
|
4
4
|
import fs from "node:fs";
|
|
5
5
|
import { writeFileAtomic } from "../../core/common.js";
|
|
6
6
|
import { getCacheDir, getSemanticStatusPath } from "../../core/paths.js";
|
|
7
|
+
import { DETERMINISTIC_EMBED_MODEL_ID, isDeterministicEmbedEnabled } from "../../llm/embedders/deterministic.js";
|
|
7
8
|
import { DEFAULT_LOCAL_MODEL } from "../../llm/embedders/local.js";
|
|
8
9
|
export function deriveSemanticProviderFingerprint(embedding) {
|
|
10
|
+
if (isDeterministicEmbedEnabled()) {
|
|
11
|
+
return `deterministic:${DETERMINISTIC_EMBED_MODEL_ID}`;
|
|
12
|
+
}
|
|
9
13
|
if (embedding?.endpoint) {
|
|
10
14
|
return `remote:${embedding.endpoint}|${embedding.model}|${embedding.dimension ?? "default"}`;
|
|
11
15
|
}
|
package/dist/llm/embedder.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
3
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
4
|
import { embedCacheKey, getCachedEmbedding, setCachedEmbedding } from "./embedders/cache.js";
|
|
5
|
+
import { DETERMINISTIC_EMBED_MODEL_ID, deterministicEmbed, isDeterministicEmbedEnabled, } from "./embedders/deterministic.js";
|
|
5
6
|
import { DEFAULT_LOCAL_MODEL, isTransformersAvailable, LocalEmbedder } from "./embedders/local.js";
|
|
6
7
|
import { hasRemoteEndpoint, RemoteEmbedder } from "./embedders/remote.js";
|
|
7
8
|
// ── Re-exports (public API) ─────────────────────────────────────────────────
|
|
@@ -39,6 +40,10 @@ export function resetLocalEmbedder() {
|
|
|
39
40
|
* and embedding config. Repeated identical queries return the cached vector.
|
|
40
41
|
*/
|
|
41
42
|
export async function embed(text, embeddingConfig, signal) {
|
|
43
|
+
// Deterministic mode (env-gated, test/bench only): model-free, stable.
|
|
44
|
+
if (isDeterministicEmbedEnabled()) {
|
|
45
|
+
return deterministicEmbed(text);
|
|
46
|
+
}
|
|
42
47
|
const key = embedCacheKey(text, embeddingConfig);
|
|
43
48
|
const cached = getCachedEmbedding(key);
|
|
44
49
|
if (cached)
|
|
@@ -58,6 +63,10 @@ export async function embed(text, embeddingConfig, signal) {
|
|
|
58
63
|
export async function embedBatch(texts, embeddingConfig, signal) {
|
|
59
64
|
if (texts.length === 0)
|
|
60
65
|
return [];
|
|
66
|
+
// Deterministic mode (env-gated, test/bench only): model-free, stable.
|
|
67
|
+
if (isDeterministicEmbedEnabled()) {
|
|
68
|
+
return texts.map((t) => deterministicEmbed(t));
|
|
69
|
+
}
|
|
61
70
|
if (embeddingConfig && hasRemoteEndpoint(embeddingConfig)) {
|
|
62
71
|
return new RemoteEmbedder(embeddingConfig).embedBatch(texts, signal);
|
|
63
72
|
}
|
|
@@ -95,6 +104,8 @@ export { cosineSimilarity } from "./embedders/types.js";
|
|
|
95
104
|
* - No config: use `DEFAULT_LOCAL_MODEL` (the shared singleton model).
|
|
96
105
|
*/
|
|
97
106
|
export function resolveEmbeddingModelId(embeddingConfig) {
|
|
107
|
+
if (isDeterministicEmbedEnabled())
|
|
108
|
+
return DETERMINISTIC_EMBED_MODEL_ID;
|
|
98
109
|
if (!embeddingConfig)
|
|
99
110
|
return DEFAULT_LOCAL_MODEL;
|
|
100
111
|
if (hasRemoteEndpoint(embeddingConfig))
|
|
@@ -106,6 +117,10 @@ export function resolveEmbeddingModelId(embeddingConfig) {
|
|
|
106
117
|
* Check whether embedding is available with a detailed reason on failure.
|
|
107
118
|
*/
|
|
108
119
|
export async function checkEmbeddingAvailability(embeddingConfig) {
|
|
120
|
+
// Deterministic mode (env-gated): always available — no model, no network.
|
|
121
|
+
if (isDeterministicEmbedEnabled()) {
|
|
122
|
+
return { available: true };
|
|
123
|
+
}
|
|
109
124
|
if (embeddingConfig && hasRemoteEndpoint(embeddingConfig)) {
|
|
110
125
|
try {
|
|
111
126
|
await new RemoteEmbedder(embeddingConfig).embed("test");
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
+
/** Env var that switches the whole embedding facade into deterministic mode. */
|
|
5
|
+
export const DETERMINISTIC_EMBED_ENV = "AKM_EMBED_DETERMINISTIC";
|
|
6
|
+
/**
|
|
7
|
+
* Vector width. Matches the default local model (`bge-small`, 384 dims) so the
|
|
8
|
+
* index DB's embedding column and sqlite-vec table dimensions line up without
|
|
9
|
+
* any extra config.
|
|
10
|
+
*/
|
|
11
|
+
export const DETERMINISTIC_EMBED_DIM = 384;
|
|
12
|
+
/**
|
|
13
|
+
* Stable model id reported for deterministic mode. Used as the embedding
|
|
14
|
+
* `model_id` and folded into the provider fingerprint so a deterministic index
|
|
15
|
+
* is never confused with a real-model index (and vice versa).
|
|
16
|
+
*/
|
|
17
|
+
export const DETERMINISTIC_EMBED_MODEL_ID = "akm-deterministic-hash-v1";
|
|
18
|
+
/** True when deterministic embedding is enabled via env. */
|
|
19
|
+
export function isDeterministicEmbedEnabled() {
|
|
20
|
+
return process.env[DETERMINISTIC_EMBED_ENV] === "1";
|
|
21
|
+
}
|
|
22
|
+
/** FNV-1a 32-bit hash. Platform- and version-stable. */
|
|
23
|
+
function fnv1a(str) {
|
|
24
|
+
let h = 0x811c9dc5;
|
|
25
|
+
for (let i = 0; i < str.length; i++) {
|
|
26
|
+
h ^= str.charCodeAt(i);
|
|
27
|
+
// 32-bit FNV prime multiply via shifts to stay in uint32.
|
|
28
|
+
h = (h + ((h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24))) >>> 0;
|
|
29
|
+
}
|
|
30
|
+
return h >>> 0;
|
|
31
|
+
}
|
|
32
|
+
/** Lowercase, split on non-alphanumeric, drop empties. */
|
|
33
|
+
function tokenize(text) {
|
|
34
|
+
return text
|
|
35
|
+
.toLowerCase()
|
|
36
|
+
.split(/[^a-z0-9]+/)
|
|
37
|
+
.filter((t) => t.length > 0);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Deterministically embed `text` into a unit-length vector of width `dim`
|
|
41
|
+
* using feature hashing. Empty / token-less input returns a fixed unit
|
|
42
|
+
* vector so cosine similarity never sees a zero vector (NaN guard).
|
|
43
|
+
*/
|
|
44
|
+
export function deterministicEmbed(text, dim = DETERMINISTIC_EMBED_DIM) {
|
|
45
|
+
const vec = new Array(dim).fill(0);
|
|
46
|
+
const tokens = tokenize(text);
|
|
47
|
+
for (const tok of tokens) {
|
|
48
|
+
const h = fnv1a(tok);
|
|
49
|
+
const idx = h % dim;
|
|
50
|
+
// Use a higher bit for the sign so it is independent of the bucket index.
|
|
51
|
+
const sign = (h >>> 16) & 1 ? 1 : -1;
|
|
52
|
+
vec[idx] += sign;
|
|
53
|
+
}
|
|
54
|
+
let norm = 0;
|
|
55
|
+
for (const v of vec)
|
|
56
|
+
norm += v * v;
|
|
57
|
+
norm = Math.sqrt(norm);
|
|
58
|
+
if (norm === 0) {
|
|
59
|
+
// No usable tokens — return a fixed, stable unit vector.
|
|
60
|
+
vec[0] = 1;
|
|
61
|
+
return vec;
|
|
62
|
+
}
|
|
63
|
+
for (let i = 0; i < dim; i++)
|
|
64
|
+
vec[i] /= norm;
|
|
65
|
+
return vec;
|
|
66
|
+
}
|
|
@@ -15785,7 +15785,7 @@ var init_config_schema = __esm(() => {
|
|
|
15785
15785
|
});
|
|
15786
15786
|
LlmCapabilitiesSchema = exports_external.object({
|
|
15787
15787
|
structuredOutput: exports_external.boolean().optional()
|
|
15788
|
-
}).
|
|
15788
|
+
}).passthrough();
|
|
15789
15789
|
LlmConnectionConfigSchema = exports_external.object({
|
|
15790
15790
|
provider: exports_external.string().optional(),
|
|
15791
15791
|
endpoint: exports_external.string(),
|
|
@@ -15800,13 +15800,13 @@ var init_config_schema = __esm(() => {
|
|
|
15800
15800
|
contextLength: positiveInt.optional(),
|
|
15801
15801
|
judgeModel: exports_external.string().min(1).optional(),
|
|
15802
15802
|
enableThinking: exports_external.boolean().optional()
|
|
15803
|
-
}).
|
|
15803
|
+
}).passthrough();
|
|
15804
15804
|
LlmProfileConfigSchema = LlmConnectionConfigSchema.extend({
|
|
15805
15805
|
supportsJsonSchema: exports_external.boolean().optional()
|
|
15806
|
-
}).
|
|
15806
|
+
}).passthrough();
|
|
15807
15807
|
EmbeddingOllamaOptionsSchema = exports_external.object({
|
|
15808
15808
|
num_ctx: positiveInt.optional()
|
|
15809
|
-
}).
|
|
15809
|
+
}).passthrough();
|
|
15810
15810
|
EmbeddingConnectionConfigSchema = exports_external.object({
|
|
15811
15811
|
provider: exports_external.string().optional(),
|
|
15812
15812
|
endpoint: exports_external.string().optional(),
|
|
@@ -15819,7 +15819,7 @@ var init_config_schema = __esm(() => {
|
|
|
15819
15819
|
chunkSize: positiveInt.optional(),
|
|
15820
15820
|
contextLength: positiveInt.optional(),
|
|
15821
15821
|
ollamaOptions: EmbeddingOllamaOptionsSchema.optional()
|
|
15822
|
-
}).
|
|
15822
|
+
}).passthrough();
|
|
15823
15823
|
AgentPlatformSchema = exports_external.enum(VALID_HARNESS_IDS);
|
|
15824
15824
|
AgentProfileConfigSchema = exports_external.object({
|
|
15825
15825
|
platform: AgentPlatformSchema,
|
|
@@ -15827,7 +15827,7 @@ var init_config_schema = __esm(() => {
|
|
|
15827
15827
|
args: exports_external.array(exports_external.string()).optional(),
|
|
15828
15828
|
workspace: exports_external.string().min(1).optional(),
|
|
15829
15829
|
model: exports_external.string().min(1).optional()
|
|
15830
|
-
}).
|
|
15830
|
+
}).passthrough();
|
|
15831
15831
|
ImproveProcessConfigSchema = exports_external.object({
|
|
15832
15832
|
enabled: exports_external.boolean().optional(),
|
|
15833
15833
|
mode: exports_external.enum(["llm", "agent", "sdk"]).optional(),
|
|
@@ -15839,10 +15839,10 @@ var init_config_schema = __esm(() => {
|
|
|
15839
15839
|
enabled: exports_external.boolean().optional(),
|
|
15840
15840
|
cosineThreshold: exports_external.number().min(0).max(1).optional(),
|
|
15841
15841
|
cosineCandidateLimit: exports_external.number().int().positive().optional()
|
|
15842
|
-
}).
|
|
15843
|
-
judgedCache: exports_external.object({ enabled: exports_external.boolean().optional() }).
|
|
15844
|
-
qualityGate: exports_external.object({ enabled: exports_external.boolean().optional() }).
|
|
15845
|
-
contradictionDetection: exports_external.object({ enabled: exports_external.boolean().optional() }).
|
|
15842
|
+
}).passthrough().optional(),
|
|
15843
|
+
judgedCache: exports_external.object({ enabled: exports_external.boolean().optional() }).passthrough().optional(),
|
|
15844
|
+
qualityGate: exports_external.object({ enabled: exports_external.boolean().optional() }).passthrough().optional(),
|
|
15845
|
+
contradictionDetection: exports_external.object({ enabled: exports_external.boolean().optional() }).passthrough().optional(),
|
|
15846
15846
|
defaultSince: exports_external.string().min(1).optional(),
|
|
15847
15847
|
maxTotalChars: positiveInt.optional(),
|
|
15848
15848
|
minContentChars: exports_external.number().int().min(0).optional(),
|
|
@@ -15864,28 +15864,28 @@ var init_config_schema = __esm(() => {
|
|
|
15864
15864
|
enabled: exports_external.boolean().optional(),
|
|
15865
15865
|
staleDays: exports_external.number().int().min(0).optional(),
|
|
15866
15866
|
demotionFactor: exports_external.number().min(0).max(1).optional()
|
|
15867
|
-
}).
|
|
15867
|
+
}).passthrough().optional(),
|
|
15868
15868
|
schemaSimilarity: exports_external.object({
|
|
15869
15869
|
enabled: exports_external.boolean().optional(),
|
|
15870
15870
|
epsilon: exports_external.number().min(0).max(1).optional(),
|
|
15871
15871
|
confidencePenalty: exports_external.number().min(0).max(1).optional()
|
|
15872
|
-
}).
|
|
15872
|
+
}).passthrough().optional(),
|
|
15873
15873
|
hotProbation: exports_external.object({
|
|
15874
15874
|
enabled: exports_external.boolean().optional()
|
|
15875
|
-
}).
|
|
15875
|
+
}).passthrough().optional(),
|
|
15876
15876
|
antiCollapse: exports_external.object({
|
|
15877
15877
|
enabled: exports_external.boolean().optional(),
|
|
15878
15878
|
maxGeneration: exports_external.number().int().min(1).optional(),
|
|
15879
15879
|
lexicalDiversityCheck: exports_external.boolean().optional(),
|
|
15880
15880
|
randomClusterFraction: exports_external.number().min(0).max(1).optional()
|
|
15881
|
-
}).
|
|
15881
|
+
}).passthrough().optional(),
|
|
15882
15882
|
cls: exports_external.object({
|
|
15883
15883
|
enabled: exports_external.boolean().optional(),
|
|
15884
15884
|
adjacentCount: exports_external.number().int().min(1).optional()
|
|
15885
|
-
}).
|
|
15885
|
+
}).passthrough().optional(),
|
|
15886
15886
|
fidelityCheck: exports_external.object({
|
|
15887
15887
|
enabled: exports_external.boolean().optional()
|
|
15888
|
-
}).
|
|
15888
|
+
}).passthrough().optional(),
|
|
15889
15889
|
minClusterSize: exports_external.number().int().min(2).optional(),
|
|
15890
15890
|
maxClustersPerRun: positiveInt.optional(),
|
|
15891
15891
|
maxClusterSize: positiveInt.optional(),
|
|
@@ -15896,7 +15896,7 @@ var init_config_schema = __esm(() => {
|
|
|
15896
15896
|
minRecurrence: exports_external.number().int().min(2).optional(),
|
|
15897
15897
|
maxProposalsPerRun: positiveInt.optional(),
|
|
15898
15898
|
emitAs: exports_external.enum(["workflow", "skill"]).optional(),
|
|
15899
|
-
lowValueFilter: exports_external.object({ enabled: exports_external.boolean().optional() }).
|
|
15899
|
+
lowValueFilter: exports_external.object({ enabled: exports_external.boolean().optional() }).passthrough().optional(),
|
|
15900
15900
|
proceduralAwareFloor: exports_external.boolean().optional(),
|
|
15901
15901
|
applyMode: exports_external.enum(["queue", "promote"]).optional(),
|
|
15902
15902
|
policy: exports_external.string().min(1).optional(),
|
|
@@ -15907,8 +15907,8 @@ var init_config_schema = __esm(() => {
|
|
|
15907
15907
|
mode: exports_external.enum(["llm", "agent", "sdk"]).optional(),
|
|
15908
15908
|
profile: exports_external.string().min(1).optional(),
|
|
15909
15909
|
timeoutMs: exports_external.union([positiveInt, exports_external.null()]).optional()
|
|
15910
|
-
}).
|
|
15911
|
-
}).
|
|
15910
|
+
}).passthrough().optional()
|
|
15911
|
+
}).passthrough();
|
|
15912
15912
|
ImproveProfileProcessesSchema = exports_external.object({
|
|
15913
15913
|
reflect: ImproveProcessConfigSchema.optional(),
|
|
15914
15914
|
distill: ImproveProcessConfigSchema.optional(),
|
|
@@ -15921,35 +15921,11 @@ var init_config_schema = __esm(() => {
|
|
|
15921
15921
|
recombine: ImproveProcessConfigSchema.optional(),
|
|
15922
15922
|
procedural: ImproveProcessConfigSchema.optional()
|
|
15923
15923
|
}).passthrough().superRefine((val, ctx) => {
|
|
15924
|
-
|
|
15925
|
-
if ("feedbackDistillation" in raw) {
|
|
15924
|
+
if ("feedbackDistillation" in val) {
|
|
15926
15925
|
ctx.addIssue({
|
|
15927
15926
|
code: exports_external.ZodIssueCode.custom,
|
|
15928
15927
|
message: "feedbackDistillation was removed in 0.8.0 \u2014 use processes.distill.enabled instead. " + "It now controls both the orchestration gate and the LLM-call gate."
|
|
15929
15928
|
});
|
|
15930
|
-
return;
|
|
15931
|
-
}
|
|
15932
|
-
const allowed = new Set([
|
|
15933
|
-
"reflect",
|
|
15934
|
-
"distill",
|
|
15935
|
-
"consolidate",
|
|
15936
|
-
"memoryInference",
|
|
15937
|
-
"graphExtraction",
|
|
15938
|
-
"validation",
|
|
15939
|
-
"extract",
|
|
15940
|
-
"triage",
|
|
15941
|
-
"proactiveMaintenance",
|
|
15942
|
-
"recombine",
|
|
15943
|
-
"procedural"
|
|
15944
|
-
]);
|
|
15945
|
-
for (const k of Object.keys(raw)) {
|
|
15946
|
-
if (!allowed.has(k)) {
|
|
15947
|
-
ctx.addIssue({
|
|
15948
|
-
code: exports_external.ZodIssueCode.unrecognized_keys,
|
|
15949
|
-
keys: [k],
|
|
15950
|
-
message: `Unrecognized improve process key: "${k}".`
|
|
15951
|
-
});
|
|
15952
|
-
}
|
|
15953
15929
|
}
|
|
15954
15930
|
});
|
|
15955
15931
|
ImproveProfileConfigSchema = exports_external.object({
|
|
@@ -15963,18 +15939,18 @@ var init_config_schema = __esm(() => {
|
|
|
15963
15939
|
enabled: exports_external.boolean().optional(),
|
|
15964
15940
|
push: exports_external.boolean().optional(),
|
|
15965
15941
|
message: exports_external.string().min(1).optional()
|
|
15966
|
-
}).
|
|
15967
|
-
}).
|
|
15942
|
+
}).passthrough().optional()
|
|
15943
|
+
}).passthrough();
|
|
15968
15944
|
ProfilesSchema = exports_external.object({
|
|
15969
15945
|
llm: exports_external.record(exports_external.string(), LlmProfileConfigSchema).optional(),
|
|
15970
15946
|
agent: exports_external.record(exports_external.string(), AgentProfileConfigSchema).optional(),
|
|
15971
15947
|
improve: exports_external.record(exports_external.string(), ImproveProfileConfigSchema).optional()
|
|
15972
|
-
}).
|
|
15948
|
+
}).passthrough();
|
|
15973
15949
|
DefaultsSchema = exports_external.object({
|
|
15974
15950
|
llm: exports_external.string().min(1).optional(),
|
|
15975
15951
|
agent: exports_external.string().min(1).optional(),
|
|
15976
15952
|
improve: exports_external.string().min(1).optional()
|
|
15977
|
-
}).
|
|
15953
|
+
}).passthrough();
|
|
15978
15954
|
SourceConfigEntryOptionsSchema = exports_external.object({
|
|
15979
15955
|
pushOnCommit: exports_external.boolean().optional()
|
|
15980
15956
|
}).passthrough();
|
|
@@ -15988,7 +15964,7 @@ var init_config_schema = __esm(() => {
|
|
|
15988
15964
|
primary: exports_external.boolean().optional(),
|
|
15989
15965
|
options: SourceConfigEntryOptionsSchema.optional(),
|
|
15990
15966
|
wikiName: exports_external.string().min(1).optional()
|
|
15991
|
-
}).
|
|
15967
|
+
}).passthrough().superRefine((entry, ctx) => {
|
|
15992
15968
|
if (entry.writable === true && (entry.type === "website" || entry.type === "npm")) {
|
|
15993
15969
|
ctx.addIssue({
|
|
15994
15970
|
code: exports_external.ZodIssueCode.custom,
|
|
@@ -16002,7 +15978,7 @@ var init_config_schema = __esm(() => {
|
|
|
16002
15978
|
enabled: exports_external.boolean().optional(),
|
|
16003
15979
|
provider: exports_external.string().min(1).optional(),
|
|
16004
15980
|
options: exports_external.record(exports_external.unknown()).optional()
|
|
16005
|
-
}).
|
|
15981
|
+
}).passthrough();
|
|
16006
15982
|
KitSourceSchema = exports_external.enum(["filesystem", "git", "npm", "github", "website", "local"]);
|
|
16007
15983
|
InstalledStashEntrySchema = exports_external.object({
|
|
16008
15984
|
id: nonEmptyString,
|
|
@@ -16016,7 +15992,7 @@ var init_config_schema = __esm(() => {
|
|
|
16016
15992
|
resolvedVersion: exports_external.string().min(1).optional(),
|
|
16017
15993
|
resolvedRevision: exports_external.string().min(1).optional(),
|
|
16018
15994
|
wikiName: exports_external.string().min(1).optional()
|
|
16019
|
-
}).
|
|
15995
|
+
}).passthrough().superRefine((entry, ctx) => {
|
|
16020
15996
|
if (entry.writable === true && entry.source !== "git" && entry.source !== "filesystem") {
|
|
16021
15997
|
ctx.addIssue({
|
|
16022
15998
|
code: exports_external.ZodIssueCode.custom,
|
|
@@ -16027,7 +16003,7 @@ var init_config_schema = __esm(() => {
|
|
|
16027
16003
|
OutputConfigSchema = exports_external.object({
|
|
16028
16004
|
format: exports_external.enum(["json", "yaml", "text"]).optional(),
|
|
16029
16005
|
detail: exports_external.enum(["brief", "normal", "full"]).optional()
|
|
16030
|
-
}).
|
|
16006
|
+
}).passthrough();
|
|
16031
16007
|
SearchGraphBoostSchema = exports_external.object({
|
|
16032
16008
|
directBoostPerEntity: nonNegativeNumber.optional(),
|
|
16033
16009
|
directBoostCap: nonNegativeNumber.optional(),
|
|
@@ -16036,21 +16012,21 @@ var init_config_schema = __esm(() => {
|
|
|
16036
16012
|
maxHops: positiveInt.max(3).optional(),
|
|
16037
16013
|
confidenceMode: exports_external.enum(["off", "blend", "multiply"]).default("blend").optional(),
|
|
16038
16014
|
confidenceWeight: exports_external.number().finite().min(0).max(1).default(0.2).optional()
|
|
16039
|
-
}).
|
|
16015
|
+
}).passthrough();
|
|
16040
16016
|
SearchConfigSchema = exports_external.object({
|
|
16041
16017
|
minScore: nonNegativeNumber.optional(),
|
|
16042
16018
|
defaultExcludeTypes: exports_external.array(nonEmptyString).optional(),
|
|
16043
|
-
curateRerank: exports_external.object({ enabled: exports_external.boolean().optional() }).
|
|
16019
|
+
curateRerank: exports_external.object({ enabled: exports_external.boolean().optional() }).passthrough().optional(),
|
|
16044
16020
|
graphBoost: SearchGraphBoostSchema.optional()
|
|
16045
|
-
}).
|
|
16021
|
+
}).passthrough();
|
|
16046
16022
|
FeedbackConfigSchema = exports_external.object({
|
|
16047
16023
|
requireReason: exports_external.boolean().optional(),
|
|
16048
16024
|
allowedFailureModes: exports_external.array(nonEmptyString).optional()
|
|
16049
|
-
}).
|
|
16025
|
+
}).passthrough();
|
|
16050
16026
|
ImproveUtilityDecaySchema = exports_external.object({
|
|
16051
16027
|
halfLifeDays: exports_external.number().finite().min(0.1).optional(),
|
|
16052
16028
|
feedbackStabilityBoost: exports_external.number().finite().min(1).optional()
|
|
16053
|
-
}).
|
|
16029
|
+
}).passthrough();
|
|
16054
16030
|
ImproveCalibrationSchema = exports_external.object({
|
|
16055
16031
|
autoTune: exports_external.boolean().optional(),
|
|
16056
16032
|
minThreshold: exports_external.number().int().min(0).max(100).optional(),
|
|
@@ -16058,23 +16034,23 @@ var init_config_schema = __esm(() => {
|
|
|
16058
16034
|
maxStep: positiveInt.optional(),
|
|
16059
16035
|
minSamples: nonNegativeNumber.optional(),
|
|
16060
16036
|
targetAcceptRate: exports_external.number().finite().min(0).max(1).optional()
|
|
16061
|
-
}).
|
|
16037
|
+
}).passthrough();
|
|
16062
16038
|
ImproveExplorationSchema = exports_external.object({
|
|
16063
16039
|
enabled: exports_external.boolean().optional(),
|
|
16064
16040
|
budgetFraction: exports_external.number().finite().min(0).max(1).optional()
|
|
16065
|
-
}).
|
|
16041
|
+
}).passthrough();
|
|
16066
16042
|
ImproveSalienceSchema = exports_external.object({
|
|
16067
16043
|
outcomeWeightEnabled: exports_external.boolean().optional(),
|
|
16068
16044
|
salienceThreshold: exports_external.number().min(0).max(1).optional(),
|
|
16069
16045
|
replayBudget: exports_external.number().int().min(0).optional()
|
|
16070
|
-
}).
|
|
16046
|
+
}).passthrough();
|
|
16071
16047
|
ImproveConfigSchema = exports_external.object({
|
|
16072
16048
|
utilityDecay: ImproveUtilityDecaySchema.optional(),
|
|
16073
16049
|
eventRetentionDays: nonNegativeNumber.optional(),
|
|
16074
16050
|
calibration: ImproveCalibrationSchema.optional(),
|
|
16075
16051
|
exploration: ImproveExplorationSchema.optional(),
|
|
16076
16052
|
salience: ImproveSalienceSchema.optional()
|
|
16077
|
-
}).
|
|
16053
|
+
}).passthrough();
|
|
16078
16054
|
GRAPH_EXTRACTION_INCLUDE_TYPES_ALLOWED = [
|
|
16079
16055
|
"memory",
|
|
16080
16056
|
"knowledge",
|
|
@@ -16140,11 +16116,11 @@ var init_config_schema = __esm(() => {
|
|
|
16140
16116
|
memoryInferenceBatchSize: positiveInt.optional(),
|
|
16141
16117
|
lazyGraphExtraction: exports_external.boolean().optional()
|
|
16142
16118
|
}).passthrough());
|
|
16143
|
-
MetadataEnhanceSchema = exports_external.object({ enabled: exports_external.boolean().optional() }).
|
|
16119
|
+
MetadataEnhanceSchema = exports_external.object({ enabled: exports_external.boolean().optional() }).passthrough();
|
|
16144
16120
|
StalenessDetectionSchema = exports_external.object({
|
|
16145
16121
|
enabled: exports_external.boolean().optional(),
|
|
16146
16122
|
thresholdDays: positiveInt.optional()
|
|
16147
|
-
}).
|
|
16123
|
+
}).passthrough();
|
|
16148
16124
|
IndexConfigSchema = exports_external.preprocess((raw, ctx) => {
|
|
16149
16125
|
if (raw === undefined || raw === null)
|
|
16150
16126
|
return raw;
|
|
@@ -16190,10 +16166,10 @@ var init_config_schema = __esm(() => {
|
|
|
16190
16166
|
SetupTaskSchedulesSchema = exports_external.object({
|
|
16191
16167
|
improve: exports_external.string().min(1).optional(),
|
|
16192
16168
|
index: exports_external.string().min(1).optional()
|
|
16193
|
-
}).
|
|
16169
|
+
}).passthrough();
|
|
16194
16170
|
SetupConfigSchema = exports_external.object({
|
|
16195
16171
|
taskSchedules: SetupTaskSchedulesSchema.optional()
|
|
16196
|
-
}).
|
|
16172
|
+
}).passthrough();
|
|
16197
16173
|
AkmConfigShape = {
|
|
16198
16174
|
configVersion: exports_external.union([exports_external.string().min(1), exports_external.number()]).optional(),
|
|
16199
16175
|
profiles: ProfilesSchema.optional(),
|
|
@@ -16214,7 +16190,7 @@ var init_config_schema = __esm(() => {
|
|
|
16214
16190
|
improve: ImproveConfigSchema.optional(),
|
|
16215
16191
|
setup: SetupConfigSchema.optional()
|
|
16216
16192
|
};
|
|
16217
|
-
AkmConfigBaseSchema = exports_external.object(AkmConfigShape).
|
|
16193
|
+
AkmConfigBaseSchema = exports_external.object(AkmConfigShape).passthrough();
|
|
16218
16194
|
AkmConfigSchema = AkmConfigBaseSchema.superRefine((config, ctx) => {
|
|
16219
16195
|
if (config.defaultWriteTarget !== undefined) {
|
|
16220
16196
|
const knownNames = (config.sources ?? []).map((s) => s.name).filter((n) => typeof n === "string" && n.length > 0);
|
|
@@ -4,6 +4,21 @@
|
|
|
4
4
|
import { fetchWithRetry } from "../../core/common.js";
|
|
5
5
|
const YOUTUBE_HOSTS = new Set(["www.youtube.com", "youtube.com", "m.youtube.com", "youtu.be"]);
|
|
6
6
|
const WATCH_HOST = "https://www.youtube.com/watch?v=";
|
|
7
|
+
// Captions: the WEB watch-page caption baseUrls are now Proof-of-Origin-Token
|
|
8
|
+
// gated (they carry `exp=xpe` and return an empty body without a `pot` token).
|
|
9
|
+
// The ANDROID InnerTube `player` client still returns UNGATED caption baseUrls
|
|
10
|
+
// that serve the transcript, so captions are sourced from there. See
|
|
11
|
+
// https://github.com/yt-dlp/yt-dlp/issues/13075.
|
|
12
|
+
const INNERTUBE_PLAYER_URL = "https://www.youtube.com/youtubei/v1/player";
|
|
13
|
+
// Long-stable public InnerTube key; only authorizes the player call (not auth).
|
|
14
|
+
const DEFAULT_INNERTUBE_KEY = "AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8";
|
|
15
|
+
const ANDROID_INNERTUBE_CLIENT = {
|
|
16
|
+
clientName: "ANDROID",
|
|
17
|
+
clientVersion: "20.10.38",
|
|
18
|
+
androidSdkVersion: 34,
|
|
19
|
+
hl: "en",
|
|
20
|
+
gl: "US",
|
|
21
|
+
};
|
|
7
22
|
function extractVideoId(url) {
|
|
8
23
|
if (!YOUTUBE_HOSTS.has(url.hostname))
|
|
9
24
|
return null;
|
|
@@ -19,6 +34,10 @@ function extractVideoId(url) {
|
|
|
19
34
|
const id = url.pathname.slice("/shorts/".length).split("/")[0]?.trim();
|
|
20
35
|
return id || null;
|
|
21
36
|
}
|
|
37
|
+
if (url.pathname.startsWith("/embed/")) {
|
|
38
|
+
const id = url.pathname.slice("/embed/".length).split("/")[0]?.trim();
|
|
39
|
+
return id || null;
|
|
40
|
+
}
|
|
22
41
|
return null;
|
|
23
42
|
}
|
|
24
43
|
function canonicalWatchUrl(videoId) {
|
|
@@ -125,13 +144,45 @@ async function fetchText(url, timeoutMs, signal) {
|
|
|
125
144
|
return response.text();
|
|
126
145
|
}
|
|
127
146
|
function parseTranscript(xml) {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
147
|
+
// Two timedtext shapes: legacy srv1 `<text>` cues, and the format-3
|
|
148
|
+
// `<timedtext format="3">` `<p>` cues that the ANDROID InnerTube caption
|
|
149
|
+
// URLs return. Strip any nested `<s>` word tags via stripTags.
|
|
150
|
+
const matches = [...xml.matchAll(/<text\b[^>]*>([\s\S]*?)<\/text>/g), ...xml.matchAll(/<p\b[^>]*>([\s\S]*?)<\/p>/g)];
|
|
151
|
+
const texts = matches.map((match) => decodeEntities(stripTags(match[1] ?? "")).trim()).filter(Boolean);
|
|
131
152
|
if (texts.length === 0)
|
|
132
153
|
return null;
|
|
133
154
|
return texts.join("\n");
|
|
134
155
|
}
|
|
156
|
+
/**
|
|
157
|
+
* Fetch caption tracks via the ANDROID InnerTube `player` endpoint, whose
|
|
158
|
+
* caption baseUrls are not POT-gated. Returns [] on any failure so the caller
|
|
159
|
+
* can fall back to the (usually empty) watch-page tracks and degrade to a
|
|
160
|
+
* description-only snapshot.
|
|
161
|
+
*/
|
|
162
|
+
async function fetchInnertubeCaptionTracks(videoId, apiKey, timeoutMs, signal) {
|
|
163
|
+
try {
|
|
164
|
+
const response = await fetchWithRetry(`${INNERTUBE_PLAYER_URL}?key=${encodeURIComponent(apiKey)}`, {
|
|
165
|
+
method: "POST",
|
|
166
|
+
headers: {
|
|
167
|
+
"Content-Type": "application/json",
|
|
168
|
+
"User-Agent": "com.google.android.youtube/20.10.38 (Linux; U; Android 14) gzip",
|
|
169
|
+
},
|
|
170
|
+
body: JSON.stringify({ context: { client: ANDROID_INNERTUBE_CLIENT }, videoId }),
|
|
171
|
+
signal,
|
|
172
|
+
}, { timeout: timeoutMs, retries: 1 });
|
|
173
|
+
if (!response.ok)
|
|
174
|
+
return [];
|
|
175
|
+
const json = (await response.json());
|
|
176
|
+
return json.captions?.playerCaptionsTracklistRenderer?.captionTracks ?? [];
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
return [];
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
/** Extract the page's InnerTube API key, falling back to the stable default. */
|
|
183
|
+
function extractInnertubeKey(html) {
|
|
184
|
+
return html.match(/"INNERTUBE_API_KEY":"([^"]+)"/)?.[1] ?? DEFAULT_INNERTUBE_KEY;
|
|
185
|
+
}
|
|
135
186
|
const youtubeFetcher = {
|
|
136
187
|
name: "youtube-transcript",
|
|
137
188
|
matches(url) {
|
|
@@ -157,7 +208,14 @@ const youtubeFetcher = {
|
|
|
157
208
|
if (description) {
|
|
158
209
|
sections.push("## Description", "", description);
|
|
159
210
|
}
|
|
160
|
-
|
|
211
|
+
// Prefer ungated ANDROID InnerTube caption tracks; fall back to the
|
|
212
|
+
// (now usually empty) watch-page tracks so a description-only snapshot
|
|
213
|
+
// still works when InnerTube is unavailable.
|
|
214
|
+
const apiKey = extractInnertubeKey(watchHtml);
|
|
215
|
+
let tracks = await fetchInnertubeCaptionTracks(videoId, apiKey, context.timeoutMs, context.signal);
|
|
216
|
+
if (tracks.length === 0) {
|
|
217
|
+
tracks = playerResponse.captions?.playerCaptionsTracklistRenderer?.captionTracks ?? [];
|
|
218
|
+
}
|
|
161
219
|
const captionUrl = chooseCaptionTrack(tracks);
|
|
162
220
|
if (captionUrl) {
|
|
163
221
|
const transcriptXml = await fetchText(captionUrl, context.timeoutMs, context.signal);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "akm-cli",
|
|
3
|
-
"version": "0.9.0-beta.
|
|
3
|
+
"version": "0.9.0-beta.48",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "akm (Agent Knowledge Management) — A package manager for AI agent skills, commands, tools, and knowledge. Works with Claude Code, OpenCode, Cursor, and any AI coding assistant.",
|
|
6
6
|
"keywords": [
|