@klarkxy/dsh-memory 0.1.3 → 0.1.4

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/client.js CHANGED
@@ -347,8 +347,14 @@ function candidateAvailabilityLabel(count, locale) {
347
347
  return locale === "en" ? "No candidates" : "暂无候选";
348
348
  }
349
349
  function kindLabel(kind, locale) {
350
- if (locale === "en") return kind === "preference" ? "Preference" : kind === "project-fact" ? "Project fact" : kind === "decision" ? "Decision" : "Lesson";
351
- return kind === "preference" ? "偏好" : kind === "project-fact" ? "项目事实" : kind === "decision" ? "决策" : "教训";
350
+ return {
351
+ preference: ["偏好", "Preference"],
352
+ "project-fact": ["项目事实", "Project fact"],
353
+ decision: ["决策", "Decision"],
354
+ vocabulary: ["用语释义", "Vocabulary"],
355
+ activity: ["近期状态", "Recent activity"],
356
+ lesson: ["行动经验", "Method"]
357
+ }[kind][locale === "en" ? 1 : 0];
352
358
  }
353
359
  function statusLabel(status, locale) {
354
360
  return locale === "en" ? {
@@ -568,15 +574,15 @@ function MemorySettingsPanel({ client, sessionId, locale }) {
568
574
  }),
569
575
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("article", {
570
576
  className: "dsh-memory-card",
571
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t(locale, "闲时梦境", "Idle Dream") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
577
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t(locale, "Dream 语境记忆", "Dream context memory") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
572
578
  className: "dsh-memory-meta",
573
- children: t(locale, "闲时自动整理记忆(每天至多一次,自动生效)。", "Auto-organizes memory while idle (at most once a day, applies automatically).")
579
+ children: t(locale, "从原始用户消息记录用语与近期状态,闲时整理已有语境。行动方法由自我改进负责。", "Records vocabulary and recent activity from original user messages, then consolidates context while idle. Self Improve owns methods.")
574
580
  })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
575
581
  type: "button",
576
582
  role: "switch",
577
583
  className: `dsh-memory-switch${draft.dreamIdleEnabled ? " is-on" : ""}`,
578
584
  "aria-checked": draft.dreamIdleEnabled,
579
- "aria-label": draft.dreamIdleEnabled ? t(locale, "关闭闲时整理", "Disable idle Dream") : t(locale, "启用闲时整理", "Enable idle Dream"),
585
+ "aria-label": draft.dreamIdleEnabled ? t(locale, "关闭 Dream 观察与整理", "Disable Dream observation and consolidation") : t(locale, "启用 Dream 观察与整理", "Enable Dream observation and consolidation"),
580
586
  disabled: busy,
581
587
  onClick: () => void action(async () => {
582
588
  await rpc("settings.update", {
@@ -858,6 +864,14 @@ function MemoryChatPanel({ client, sessionId, locale }) {
858
864
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
859
865
  value: "decision",
860
866
  children: kindLabel("decision", locale)
867
+ }),
868
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
869
+ value: "vocabulary",
870
+ children: kindLabel("vocabulary", locale)
871
+ }),
872
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
873
+ value: "activity",
874
+ children: kindLabel("activity", locale)
861
875
  })
862
876
  ]
863
877
  })] }),
@@ -956,6 +970,26 @@ function MemoryRow(props) {
956
970
  scope
957
971
  ]
958
972
  }),
973
+ record.context && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
974
+ className: "dsh-memory-meta",
975
+ children: [
976
+ record.context.subject,
977
+ " · ",
978
+ record.context.domain,
979
+ " · ",
980
+ record.context.key,
981
+ " · ",
982
+ t(locale, "记录于", "Observed"),
983
+ " ",
984
+ new Date(record.context.observedAt).toLocaleString(locale === "en" ? "en-US" : "zh-CN"),
985
+ record.context.activityStatus && ` · ${record.context.activityStatus}`,
986
+ record.context.eventTime && ` · ${record.context.eventTime}`
987
+ ]
988
+ }),
989
+ record.kind === "activity" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
990
+ className: "dsh-memory-meta",
991
+ children: t(locale, "仅表示上次报告的状态;到期不代表任务已完成。", "Last reported state only; expiry never means completion.")
992
+ }),
959
993
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: record.content }),
960
994
  record.evidence.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
961
995
  className: "dsh-memory-meta",
@@ -1046,7 +1080,7 @@ function DreamPanel(props) {
1046
1080
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", { children: t(locale, "梦境整理", "Dream") }),
1047
1081
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1048
1082
  className: "dsh-memory-meta",
1049
- children: t(locale, "闲置且新材料足够时每天至多自动整理一次,结果自动生效。", "Runs at most once a day when idle with enough new material; results apply automatically.")
1083
+ children: t(locale, "每天至多闲时整理一次;不把候选升级为事实,不延长近期状态的有效期。", "Consolidates at most once a day while idle, without confirming candidates or extending activity freshness.")
1050
1084
  }),
1051
1085
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1052
1086
  className: "dsh-memory-row-actions",
@@ -36,6 +36,8 @@ interface DreamSnapshotEntry {
36
36
  status: MemoryRecord['status'];
37
37
  scope: KnowledgeScope;
38
38
  expiresAt?: number;
39
+ kind?: KnowledgeKind;
40
+ context?: MemoryRecord['context'];
39
41
  }
40
42
  interface DreamProposal {
41
43
  title: string;
@@ -46,6 +48,7 @@ interface DreamProposal {
46
48
  evidence: EvidenceRef[];
47
49
  sourceIds: string[];
48
50
  scope: KnowledgeScope;
51
+ context?: MemoryRecord['context'];
49
52
  }
50
53
  interface DreamPlan {
51
54
  id: string;
@@ -72,6 +75,12 @@ interface MemoryPersistedState {
72
75
  tombstones: MemoryTombstone[];
73
76
  dreams: DreamPlan[];
74
77
  lastAttemptAt?: number;
78
+ /** Bounded per-session source cursors; retained when a derived record is deleted. */
79
+ observations?: Array<{
80
+ sessionId: string;
81
+ seq: number;
82
+ updatedAt: number;
83
+ }>;
75
84
  }
76
85
  interface MemoryStatus {
77
86
  runningDreams?: string[];
package/lib/contracts.js CHANGED
@@ -24,7 +24,9 @@ const MAX_PROJECT_ID_CHARS = 32768;
24
24
  const INJECT_KINDS = [
25
25
  "preference",
26
26
  "project-fact",
27
- "decision"
27
+ "decision",
28
+ "vocabulary",
29
+ "activity"
28
30
  ];
29
31
  const RECALL_EXCLUDED_STATUSES = [
30
32
  "candidate",
@@ -1 +1 @@
1
- {"version":3,"file":"contracts.js","names":["FROZEN_CHAT_EVENTS_SLOT","frozenProjectIdFromCwd"],"sources":["../src/contracts.ts"],"sourcesContent":["/** Browser-safe contracts. MemoryRecord / MemoryService match frozen @klarkxy/dsh-ai-services. */\nimport {\n CHAT_EVENTS_SLOT as FROZEN_CHAT_EVENTS_SLOT,\n projectIdFromCwd as frozenProjectIdFromCwd,\n} from '@klarkxy/dsh-ai-services/contracts'\nimport type {\n AiFeatureScope,\n AiServices,\n AuxiliaryRequest,\n AuxiliaryResult,\n EvidenceRef,\n KnowledgeKind,\n KnowledgeScope,\n MemoryQuery,\n MemoryRecord,\n MemoryService,\n NewMemoryRecord,\n PurposeSpec,\n ProducerMessageSource,\n RpcResult,\n UsageReceipt,\n} from '@klarkxy/dsh-ai-services/contracts'\n\nexport type {\n AiFeatureScope,\n AiServices,\n AuxiliaryRequest,\n AuxiliaryResult,\n EvidenceRef,\n KnowledgeKind,\n KnowledgeScope,\n MemoryQuery,\n MemoryRecord,\n MemoryService,\n NewMemoryRecord,\n PurposeSpec,\n ProducerMessageSource,\n RpcResult,\n UsageReceipt,\n}\n\n/** Frozen shared chat-events seat. Must stay equal to @klarkxy/dsh-ai-services CHAT_EVENTS_SLOT. */\nexport const CHAT_EVENTS_SLOT = FROZEN_CHAT_EVENTS_SLOT\n/** Shared untruncated cwd identity. Drive roots (`/` / `C:/`) stay intact. */\nexport const projectIdFromCwd = frozenProjectIdFromCwd\nexport const MEMORY_PLUGIN = '@klarkxy/dsh-memory'\nexport const MEMORY_SOURCE_KIND = 'plugin:@klarkxy/dsh-memory' as const\nexport const MEMORY_RPC_CHANNEL = '/dsh-memory'\nexport const MEMORY_DREAM_PURPOSE = 'memory.dream'\nexport const MEMORY_INJECTION_SECTION = 'dsh-memory:recall'\n/** Frozen pluginName now accepts scoped ids. */\nexport const MEMORY_ACTIVATE_ID = MEMORY_PLUGIN\n\nexport const DEFAULT_IDLE_MS = 15 * 60 * 1000\nexport const MIN_IDLE_MS = 60_000\nexport const MAX_IDLE_MS = 180 * 60_000\nexport const DREAM_MIN_INTERVAL_MS = 24 * 60 * 60 * 1000\nexport const DREAM_MIN_MATERIAL = 3\nexport const MIN_RECALL_RECORDS = 1\nexport const MAX_RECALL_RECORDS = 5\nexport const MAX_RECALL_TOKENS = 800\nexport const MAX_PROJECT_ID_CHARS = 32_768\nexport const INJECT_KINDS: readonly KnowledgeKind[] = ['preference', 'project-fact', 'decision']\nexport const RECALL_EXCLUDED_STATUSES: readonly MemoryRecord['status'][] = [\n 'candidate', 'rejected', 'revoked', 'deleted', 'superseded',\n]\n\nexport interface MemorySettings {\n revision: number\n injectEnabled: boolean\n dreamIdleEnabled: boolean\n idleMs: number\n}\n\nexport const defaultSettings = (): MemorySettings => ({\n revision: 0,\n injectEnabled: true,\n dreamIdleEnabled: true,\n idleMs: DEFAULT_IDLE_MS,\n})\n\nexport type DreamPlanStatus = 'preview' | 'applied' | 'cancelled' | 'stale' | 'failed' | 'noop'\n\nexport interface DreamSnapshotEntry {\n id: string\n revision: number\n status: MemoryRecord['status']\n scope: KnowledgeScope\n expiresAt?: number\n}\n\nexport interface DreamProposal {\n title: string\n content: string\n kind: Exclude<KnowledgeKind, 'lesson'>\n tags: string[]\n exceptions: string[]\n evidence: EvidenceRef[]\n sourceIds: string[]\n scope: KnowledgeScope\n}\n\nexport interface DreamPlan {\n id: string\n revision: number\n sessionId: string\n projectId?: string\n status: DreamPlanStatus\n sourceVersion: string\n snapshot: DreamSnapshotEntry[]\n proposals: DreamProposal[]\n generation: number\n createdAt: number\n updatedAt: number\n error?: string\n}\n\nexport interface MemoryTombstone {\n id: string\n deletedAt: number\n lastRevision: number\n}\n\nexport interface MemoryPersistedState {\n settings: MemorySettings\n records: MemoryRecord[]\n tombstones: MemoryTombstone[]\n dreams: DreamPlan[]\n lastAttemptAt?: number\n}\n\nexport interface MemoryStatus {\n runningDreams?: string[]\n settings: MemorySettings\n records: MemoryRecord[]\n dreams: DreamPlan[]\n projectId?: string\n storageFailed: boolean\n aiAvailable: boolean\n}\n\n/** Matches frozen MemoryService optional mutation identity/lifetime guards. */\nexport interface MemoryMutationOptions { signal?: AbortSignal; isCurrent?: () => boolean }\n\nexport interface PreStepEnter {\n kind: 'enter'\n messages: unknown[]\n startsRequestSeries?: true\n}\nexport type PreStepDecision = { kind: 'reject' } | PreStepEnter\n\nexport interface InjectedMemoryMessage {\n content: Array<{ type: 'text'; text: string }>\n source: ProducerMessageSource & {\n kind: typeof MEMORY_SOURCE_KIND\n plugin: typeof MEMORY_PLUGIN\n form: 'snapshot'\n sections: Array<{ name: string; text: string }>\n }\n}\n\nexport function fail<T = never>(code: string, message: string): RpcResult<T> {\n return { ok: false, error: { code, message } }\n}\n\nexport function ok<T>(value: T): RpcResult<T> {\n return { ok: true, value }\n}\n\nexport function parseSessionId(payload: unknown): string | undefined {\n if (!payload || typeof payload !== 'object' || Array.isArray(payload)) return undefined\n const sessionId = (payload as { sessionId?: unknown }).sessionId\n return typeof sessionId === 'string' && sessionId.length > 0 && sessionId.length <= 200 ? sessionId : undefined\n}\n\nexport function sessionCwd(session: unknown): string | undefined {\n if (!session || typeof session !== 'object') return undefined\n const row = session as { meta?: { cwd?: unknown }; header?: { cwd?: unknown } }\n const cwd = row.meta?.cwd ?? row.header?.cwd\n return typeof cwd === 'string' ? cwd : undefined\n}\n\nexport function scopesEqual(left: KnowledgeScope, right: KnowledgeScope): boolean {\n if (left.kind === 'global') return right.kind === 'global'\n return right.kind === 'project' && left.projectId === right.projectId\n}\n\nexport function scopeKey(scope: KnowledgeScope): string {\n return scope.kind === 'global' ? 'global' : `project:${scope.projectId}`\n}\n\nexport function cloneRecord(record: MemoryRecord): MemoryRecord {\n return structuredClone(record)\n}\n"],"mappings":";;;;AA0CA,MAAa,mBAAmBA;;AAEhC,MAAa,mBAAmBC;AAChC,MAAa,gBAAgB;AAC7B,MAAa,qBAAqB;AAClC,MAAa,qBAAqB;AAClC,MAAa,uBAAuB;AACpC,MAAa,2BAA2B;;AAExC,MAAa,qBAAqB;AAElC,MAAa,kBAAkB;AAC/B,MAAa,cAAc;AAC3B,MAAa,cAAc;AAC3B,MAAa,wBAAwB;AACrC,MAAa,qBAAqB;AAClC,MAAa,qBAAqB;AAClC,MAAa,qBAAqB;AAClC,MAAa,oBAAoB;AACjC,MAAa,uBAAuB;AACpC,MAAa,eAAyC;CAAC;CAAc;CAAgB;AAAU;AAC/F,MAAa,2BAA8D;CACzE;CAAa;CAAY;CAAW;CAAW;AACjD;AASA,MAAa,yBAAyC;CACpD,UAAU;CACV,eAAe;CACf,kBAAkB;CAClB,QAAQ;AACV;AAkFA,SAAgB,KAAgB,MAAc,SAA+B;CAC3E,OAAO;EAAE,IAAI;EAAO,OAAO;GAAE;GAAM;EAAQ;CAAE;AAC/C;AAEA,SAAgB,GAAM,OAAwB;CAC5C,OAAO;EAAE,IAAI;EAAM;CAAM;AAC3B;AAEA,SAAgB,eAAe,SAAsC;CACnE,IAAI,CAAC,WAAW,OAAO,YAAY,YAAY,MAAM,QAAQ,OAAO,GAAG,OAAO,KAAA;CAC9E,MAAM,YAAa,QAAoC;CACvD,OAAO,OAAO,cAAc,YAAY,UAAU,SAAS,KAAK,UAAU,UAAU,MAAM,YAAY,KAAA;AACxG;AAEA,SAAgB,WAAW,SAAsC;CAC/D,IAAI,CAAC,WAAW,OAAO,YAAY,UAAU,OAAO,KAAA;CACpD,MAAM,MAAM;CACZ,MAAM,MAAM,IAAI,MAAM,OAAO,IAAI,QAAQ;CACzC,OAAO,OAAO,QAAQ,WAAW,MAAM,KAAA;AACzC;AAEA,SAAgB,YAAY,MAAsB,OAAgC;CAChF,IAAI,KAAK,SAAS,UAAU,OAAO,MAAM,SAAS;CAClD,OAAO,MAAM,SAAS,aAAa,KAAK,cAAc,MAAM;AAC9D;AAEA,SAAgB,SAAS,OAA+B;CACtD,OAAO,MAAM,SAAS,WAAW,WAAW,WAAW,MAAM;AAC/D;AAEA,SAAgB,YAAY,QAAoC;CAC9D,OAAO,gBAAgB,MAAM;AAC/B"}
1
+ {"version":3,"file":"contracts.js","names":["FROZEN_CHAT_EVENTS_SLOT","frozenProjectIdFromCwd"],"sources":["../src/contracts.ts"],"sourcesContent":["/** Browser-safe contracts. MemoryRecord / MemoryService match frozen @klarkxy/dsh-ai-services. */\nimport {\n CHAT_EVENTS_SLOT as FROZEN_CHAT_EVENTS_SLOT,\n projectIdFromCwd as frozenProjectIdFromCwd,\n} from '@klarkxy/dsh-ai-services/contracts'\nimport type {\n AiFeatureScope,\n AiServices,\n AuxiliaryRequest,\n AuxiliaryResult,\n EvidenceRef,\n KnowledgeKind,\n KnowledgeScope,\n MemoryQuery,\n MemoryRecord,\n MemoryService,\n NewMemoryRecord,\n PurposeSpec,\n ProducerMessageSource,\n RpcResult,\n UsageReceipt,\n} from '@klarkxy/dsh-ai-services/contracts'\n\nexport type {\n AiFeatureScope,\n AiServices,\n AuxiliaryRequest,\n AuxiliaryResult,\n EvidenceRef,\n KnowledgeKind,\n KnowledgeScope,\n MemoryQuery,\n MemoryRecord,\n MemoryService,\n NewMemoryRecord,\n PurposeSpec,\n ProducerMessageSource,\n RpcResult,\n UsageReceipt,\n}\n\n/** Frozen shared chat-events seat. Must stay equal to @klarkxy/dsh-ai-services CHAT_EVENTS_SLOT. */\nexport const CHAT_EVENTS_SLOT = FROZEN_CHAT_EVENTS_SLOT\n/** Shared untruncated cwd identity. Drive roots (`/` / `C:/`) stay intact. */\nexport const projectIdFromCwd = frozenProjectIdFromCwd\nexport const MEMORY_PLUGIN = '@klarkxy/dsh-memory'\nexport const MEMORY_SOURCE_KIND = 'plugin:@klarkxy/dsh-memory' as const\nexport const MEMORY_RPC_CHANNEL = '/dsh-memory'\nexport const MEMORY_DREAM_PURPOSE = 'memory.dream'\nexport const MEMORY_INJECTION_SECTION = 'dsh-memory:recall'\n/** Frozen pluginName now accepts scoped ids. */\nexport const MEMORY_ACTIVATE_ID = MEMORY_PLUGIN\n\nexport const DEFAULT_IDLE_MS = 15 * 60 * 1000\nexport const MIN_IDLE_MS = 60_000\nexport const MAX_IDLE_MS = 180 * 60_000\nexport const DREAM_MIN_INTERVAL_MS = 24 * 60 * 60 * 1000\nexport const DREAM_MIN_MATERIAL = 3\nexport const MIN_RECALL_RECORDS = 1\nexport const MAX_RECALL_RECORDS = 5\nexport const MAX_RECALL_TOKENS = 800\nexport const MAX_PROJECT_ID_CHARS = 32_768\nexport const INJECT_KINDS: readonly KnowledgeKind[] = ['preference', 'project-fact', 'decision', 'vocabulary', 'activity']\nexport const RECALL_EXCLUDED_STATUSES: readonly MemoryRecord['status'][] = [\n 'candidate', 'rejected', 'revoked', 'deleted', 'superseded',\n]\n\nexport interface MemorySettings {\n revision: number\n injectEnabled: boolean\n dreamIdleEnabled: boolean\n idleMs: number\n}\n\nexport const defaultSettings = (): MemorySettings => ({\n revision: 0,\n injectEnabled: true,\n dreamIdleEnabled: true,\n idleMs: DEFAULT_IDLE_MS,\n})\n\nexport type DreamPlanStatus = 'preview' | 'applied' | 'cancelled' | 'stale' | 'failed' | 'noop'\n\nexport interface DreamSnapshotEntry {\n id: string\n revision: number\n status: MemoryRecord['status']\n scope: KnowledgeScope\n expiresAt?: number\n kind?: KnowledgeKind\n context?: MemoryRecord['context']\n}\n\nexport interface DreamProposal {\n title: string\n content: string\n kind: Exclude<KnowledgeKind, 'lesson'>\n tags: string[]\n exceptions: string[]\n evidence: EvidenceRef[]\n sourceIds: string[]\n scope: KnowledgeScope\n context?: MemoryRecord['context']\n}\n\nexport interface DreamPlan {\n id: string\n revision: number\n sessionId: string\n projectId?: string\n status: DreamPlanStatus\n sourceVersion: string\n snapshot: DreamSnapshotEntry[]\n proposals: DreamProposal[]\n generation: number\n createdAt: number\n updatedAt: number\n error?: string\n}\n\nexport interface MemoryTombstone {\n id: string\n deletedAt: number\n lastRevision: number\n}\n\nexport interface MemoryPersistedState {\n settings: MemorySettings\n records: MemoryRecord[]\n tombstones: MemoryTombstone[]\n dreams: DreamPlan[]\n lastAttemptAt?: number\n /** Bounded per-session source cursors; retained when a derived record is deleted. */\n observations?: Array<{ sessionId: string; seq: number; updatedAt: number }>\n}\n\nexport interface MemoryStatus {\n runningDreams?: string[]\n settings: MemorySettings\n records: MemoryRecord[]\n dreams: DreamPlan[]\n projectId?: string\n storageFailed: boolean\n aiAvailable: boolean\n}\n\n/** Matches frozen MemoryService optional mutation identity/lifetime guards. */\nexport interface MemoryMutationOptions { signal?: AbortSignal; isCurrent?: () => boolean }\n\nexport interface PreStepEnter {\n kind: 'enter'\n messages: unknown[]\n startsRequestSeries?: true\n}\nexport type PreStepDecision = { kind: 'reject' } | PreStepEnter\n\nexport interface InjectedMemoryMessage {\n content: Array<{ type: 'text'; text: string }>\n source: ProducerMessageSource & {\n kind: typeof MEMORY_SOURCE_KIND\n plugin: typeof MEMORY_PLUGIN\n form: 'snapshot'\n sections: Array<{ name: string; text: string }>\n }\n}\n\nexport function fail<T = never>(code: string, message: string): RpcResult<T> {\n return { ok: false, error: { code, message } }\n}\n\nexport function ok<T>(value: T): RpcResult<T> {\n return { ok: true, value }\n}\n\nexport function parseSessionId(payload: unknown): string | undefined {\n if (!payload || typeof payload !== 'object' || Array.isArray(payload)) return undefined\n const sessionId = (payload as { sessionId?: unknown }).sessionId\n return typeof sessionId === 'string' && sessionId.length > 0 && sessionId.length <= 200 ? sessionId : undefined\n}\n\nexport function sessionCwd(session: unknown): string | undefined {\n if (!session || typeof session !== 'object') return undefined\n const row = session as { meta?: { cwd?: unknown }; header?: { cwd?: unknown } }\n const cwd = row.meta?.cwd ?? row.header?.cwd\n return typeof cwd === 'string' ? cwd : undefined\n}\n\nexport function scopesEqual(left: KnowledgeScope, right: KnowledgeScope): boolean {\n if (left.kind === 'global') return right.kind === 'global'\n return right.kind === 'project' && left.projectId === right.projectId\n}\n\nexport function scopeKey(scope: KnowledgeScope): string {\n return scope.kind === 'global' ? 'global' : `project:${scope.projectId}`\n}\n\nexport function cloneRecord(record: MemoryRecord): MemoryRecord {\n return structuredClone(record)\n}\n"],"mappings":";;;;AA0CA,MAAa,mBAAmBA;;AAEhC,MAAa,mBAAmBC;AAChC,MAAa,gBAAgB;AAC7B,MAAa,qBAAqB;AAClC,MAAa,qBAAqB;AAClC,MAAa,uBAAuB;AACpC,MAAa,2BAA2B;;AAExC,MAAa,qBAAqB;AAElC,MAAa,kBAAkB;AAC/B,MAAa,cAAc;AAC3B,MAAa,cAAc;AAC3B,MAAa,wBAAwB;AACrC,MAAa,qBAAqB;AAClC,MAAa,qBAAqB;AAClC,MAAa,qBAAqB;AAClC,MAAa,oBAAoB;AACjC,MAAa,uBAAuB;AACpC,MAAa,eAAyC;CAAC;CAAc;CAAgB;CAAY;CAAc;AAAU;AACzH,MAAa,2BAA8D;CACzE;CAAa;CAAY;CAAW;CAAW;AACjD;AASA,MAAa,yBAAyC;CACpD,UAAU;CACV,eAAe;CACf,kBAAkB;CAClB,QAAQ;AACV;AAuFA,SAAgB,KAAgB,MAAc,SAA+B;CAC3E,OAAO;EAAE,IAAI;EAAO,OAAO;GAAE;GAAM;EAAQ;CAAE;AAC/C;AAEA,SAAgB,GAAM,OAAwB;CAC5C,OAAO;EAAE,IAAI;EAAM;CAAM;AAC3B;AAEA,SAAgB,eAAe,SAAsC;CACnE,IAAI,CAAC,WAAW,OAAO,YAAY,YAAY,MAAM,QAAQ,OAAO,GAAG,OAAO,KAAA;CAC9E,MAAM,YAAa,QAAoC;CACvD,OAAO,OAAO,cAAc,YAAY,UAAU,SAAS,KAAK,UAAU,UAAU,MAAM,YAAY,KAAA;AACxG;AAEA,SAAgB,WAAW,SAAsC;CAC/D,IAAI,CAAC,WAAW,OAAO,YAAY,UAAU,OAAO,KAAA;CACpD,MAAM,MAAM;CACZ,MAAM,MAAM,IAAI,MAAM,OAAO,IAAI,QAAQ;CACzC,OAAO,OAAO,QAAQ,WAAW,MAAM,KAAA;AACzC;AAEA,SAAgB,YAAY,MAAsB,OAAgC;CAChF,IAAI,KAAK,SAAS,UAAU,OAAO,MAAM,SAAS;CAClD,OAAO,MAAM,SAAS,aAAa,KAAK,cAAc,MAAM;AAC9D;AAEA,SAAgB,SAAS,OAA+B;CACtD,OAAO,MAAM,SAAS,WAAW,WAAW,WAAW,MAAM;AAC/D;AAEA,SAAgB,YAAY,QAAoC;CAC9D,OAAO,gBAAgB,MAAM;AAC/B"}
package/lib/index.d.ts CHANGED
@@ -23,6 +23,8 @@ declare class MemoryRuntime implements MemoryService {
23
23
  private pending;
24
24
  private ai?;
25
25
  private unregisterPurpose?;
26
+ private unregisterObserverPurpose?;
27
+ private readonly observationJobs;
26
28
  private readonly jobs;
27
29
  private readonly now;
28
30
  private readonly customId?;
@@ -48,6 +50,9 @@ declare class MemoryRuntime implements MemoryService {
48
50
  signal: AbortSignal;
49
51
  next: () => Promise<PreStepDecision>;
50
52
  }): Promise<PreStepDecision>;
53
+ /** Bounded human-only observation, independent of editor services and idle consolidation. */
54
+ observeSession(sessionId: string, session: unknown, signal: AbortSignal): Promise<void>;
55
+ private collectContext;
51
56
  previewDream(sessionId: string, projectId: string | undefined, trigger?: 'manual' | 'idle'): Promise<DreamPlan>;
52
57
  runIdleDream(sessionId: string, projectId: string | undefined, trigger?: 'manual' | 'idle'): Promise<DreamPlan>;
53
58
  get dreamLastAttemptAt(): number | undefined;