@aiwayds/dsh-topics-memory 0.23.0 → 0.24.1
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/lib/config.d.ts +18 -1
- package/lib/config.js +13 -0
- package/lib/digest.d.ts +38 -5
- package/lib/digest.js +57 -15
- package/lib/ilog.d.ts +14 -0
- package/lib/index.d.ts +9 -1
- package/lib/index.js +1 -0
- package/lib/okf.d.ts +34 -0
- package/lib/okf.js +41 -0
- package/lib/paths.d.ts +9 -0
- package/lib/paths.js +15 -0
- package/lib/pipeline-events.d.ts +4 -2
- package/lib/pipeline-events.js +2 -0
- package/lib/service.d.ts +88 -3
- package/lib/service.js +435 -19
- package/lib/store.d.ts +30 -3
- package/lib/store.js +28 -1
- package/package.json +2 -2
- package/src/config.ts +15 -0
- package/src/digest.ts +85 -12
- package/src/ilog.ts +9 -0
- package/src/index.ts +9 -0
- package/src/okf.ts +68 -0
- package/src/paths.ts +15 -0
- package/src/pipeline-events.ts +6 -2
- package/src/service.ts +476 -21
- package/src/store.ts +45 -3
package/src/store.ts
CHANGED
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
appendFile,
|
|
19
19
|
access,
|
|
20
20
|
rm,
|
|
21
|
+
copyFile,
|
|
21
22
|
} from 'node:fs/promises'
|
|
22
23
|
import { readFileSync } from 'node:fs'
|
|
23
24
|
import { constants as FsConstants } from 'node:fs'
|
|
@@ -38,19 +39,34 @@ export interface TopicMeta {
|
|
|
38
39
|
generatedAt: string
|
|
39
40
|
}
|
|
40
41
|
|
|
41
|
-
|
|
42
|
+
/** Bundle entry kinds (B2, spec §6.5 落点表: store kind 枚举). Canonical
|
|
43
|
+
* model lives in okf.ts next to TopicStatus; re-exported here as the store
|
|
44
|
+
* surface the spec anchors on. */
|
|
45
|
+
export type TopicKind = okf.TopicKind
|
|
46
|
+
export const topicKindOf = okf.topicKindOf
|
|
47
|
+
|
|
48
|
+
export type ObservationKind = 'decision' | 'finding' | 'constraint' | 'question' | 'turn' | 'signal'
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Where an observation came from. `fed` marks entries that arrived through
|
|
52
|
+
* the feedObservations write seam (B-line dual-write, spec 卷二 §6.2) — the
|
|
53
|
+
* provenance marker the distill lane and audits key on.
|
|
54
|
+
*/
|
|
55
|
+
export type ObservationSource = 'model' | 'auto' | 'fed'
|
|
42
56
|
|
|
43
57
|
export interface Observation {
|
|
44
58
|
id: string
|
|
45
59
|
at: string
|
|
46
60
|
kind: ObservationKind
|
|
47
|
-
source:
|
|
61
|
+
source: ObservationSource
|
|
48
62
|
text: string
|
|
49
63
|
sessionId?: string
|
|
50
64
|
distilled: boolean
|
|
51
65
|
distilledInto?: string[]
|
|
52
66
|
/** Failed distill attempts: fed to the model but consumed by no op (GC counter). */
|
|
53
67
|
attempts?: number
|
|
68
|
+
/** Write-seam audit: the feedObservations caller identity (opts.actor). */
|
|
69
|
+
by?: string
|
|
54
70
|
}
|
|
55
71
|
|
|
56
72
|
export interface SaveResult {
|
|
@@ -418,6 +434,30 @@ export class BundleStore {
|
|
|
418
434
|
})
|
|
419
435
|
}
|
|
420
436
|
|
|
437
|
+
/**
|
|
438
|
+
* Lesson removal seam (B2/卷二 §6.2 MJ-B, spec 红线④): archive BEFORE the
|
|
439
|
+
* roster removal — the file is copied byte-exact to `archive/lessons/`
|
|
440
|
+
* inside the bundle and only then deleted from `topics/`, both landing in
|
|
441
|
+
* ONE git commit so `git revert` restores the lesson exactly (自动写资产
|
|
442
|
+
* 必须可回滚). Returns undefined when the slug doesn't exist (idempotent).
|
|
443
|
+
*/
|
|
444
|
+
async archiveLesson(slug: string, opts: { message: string }): Promise<{ archivedTo: string; committed: boolean } | undefined> {
|
|
445
|
+
return this.enqueue(async () => {
|
|
446
|
+
const clean = okf.slugify(slug)
|
|
447
|
+
const file = this.topicPath(clean)
|
|
448
|
+
if (!(await existsSync(file))) return undefined
|
|
449
|
+
const stamp = new Date().toISOString().replace(/[:.]/g, '-')
|
|
450
|
+
const archivedTo = `archive/lessons/${clean}-${stamp}.md`
|
|
451
|
+
const absTo = join(this.root, archivedTo)
|
|
452
|
+
await mkdir(dirname(absTo), { recursive: true })
|
|
453
|
+
await copyFile(file, absTo)
|
|
454
|
+
await rm(file)
|
|
455
|
+
await this.regenerateIndex()
|
|
456
|
+
const committed = await this.commit([archivedTo, `topics/${clean}.md`, 'index.md'], opts.message)
|
|
457
|
+
return { archivedTo, committed }
|
|
458
|
+
})
|
|
459
|
+
}
|
|
460
|
+
|
|
421
461
|
private async regenerateIndex(): Promise<void> { const metas = await this.listTopics()
|
|
422
462
|
const entries: okf.IndexEntry[] = metas.map((m) => ({
|
|
423
463
|
slug: m.slug,
|
|
@@ -483,9 +523,10 @@ export class BundleStore {
|
|
|
483
523
|
|
|
484
524
|
async appendObservation(input: {
|
|
485
525
|
kind: ObservationKind
|
|
486
|
-
source:
|
|
526
|
+
source: ObservationSource
|
|
487
527
|
text: string
|
|
488
528
|
sessionId?: string
|
|
529
|
+
by?: string
|
|
489
530
|
}): Promise<Observation> {
|
|
490
531
|
const obs: Observation = {
|
|
491
532
|
id: `obs-${Date.now().toString(36)}-${randomUUID().slice(0, 8)}`,
|
|
@@ -496,6 +537,7 @@ export class BundleStore {
|
|
|
496
537
|
distilled: false,
|
|
497
538
|
}
|
|
498
539
|
if (input.sessionId !== undefined) obs.sessionId = input.sessionId
|
|
540
|
+
if (input.by !== undefined) obs.by = input.by
|
|
499
541
|
// Behind the store-wide queue: markDistilled / recordUnconsumed rewrite
|
|
500
542
|
// the whole JSONL file — an unsynchronized append here could interleave
|
|
501
543
|
// with a rewrite and lose either the new observation or the rewrite.
|