total-recall-brain 3.13.0 → 3.13.2

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.
Files changed (152) hide show
  1. package/README.md +91 -108
  2. package/bin/antigravity.mjs +81 -52
  3. package/bin/total-recall.mjs +32 -57
  4. package/extension/background.js +1 -1
  5. package/extension/options/options.js +9 -3
  6. package/extension/sidepanel/sidepanel.js +0 -2
  7. package/frontend/index.html +4 -3
  8. package/frontend/package.json +1 -0
  9. package/frontend/src/App.tsx +109 -152
  10. package/frontend/src/api.ts +605 -23
  11. package/frontend/src/components/BrainSelector.tsx +211 -199
  12. package/frontend/src/components/DocumentEditorModal.tsx +57 -0
  13. package/frontend/src/components/DocumentTable.tsx +47 -0
  14. package/frontend/src/components/Graph3D.tsx +120 -71
  15. package/frontend/src/components/UsageChart.tsx +3 -3
  16. package/frontend/src/components/brand/BrandMark.tsx +95 -0
  17. package/frontend/src/index.css +286 -86
  18. package/frontend/src/pages/ApiKeysPage.tsx +1878 -215
  19. package/frontend/src/pages/ChatPage.tsx +103 -45
  20. package/frontend/src/pages/DesignDocsPage.tsx +5 -5
  21. package/frontend/src/pages/FilesPage.tsx +10 -6
  22. package/frontend/src/pages/GraphPage.tsx +5 -3
  23. package/frontend/src/pages/HelpPage.tsx +2 -2
  24. package/frontend/src/pages/InboxPage.tsx +82 -0
  25. package/frontend/src/pages/InstructionsPage.tsx +1 -1
  26. package/frontend/src/pages/IntegrationsPage.tsx +21 -10
  27. package/frontend/src/pages/LoginPage.tsx +95 -77
  28. package/frontend/src/pages/MemoryPage.tsx +31 -8
  29. package/frontend/src/pages/OnboardingPage.tsx +690 -0
  30. package/frontend/src/pages/OpenWikiPage.tsx +412 -323
  31. package/frontend/src/pages/SettingsPage.tsx +825 -922
  32. package/frontend/src/pages/SkillsPage.tsx +272 -147
  33. package/frontend/src/pages/UsagePage.tsx +23 -31
  34. package/frontend/src/pages/VaultPage.tsx +223 -0
  35. package/frontend/src/types.ts +13 -0
  36. package/package.json +4 -4
  37. package/scaffold/.agent/skills/total-recall/SKILL.md +43 -4
  38. package/scaffold/.agent/skills/total-recall/modules/README.md +10 -0
  39. package/scaffold/.agent/skills/total-recall/modules/agents/README.md +5 -0
  40. package/scaffold/.agent/skills/total-recall/{skills/tr-cli-agents → modules/agents}/agents.yml +8 -0
  41. package/scaffold/.agent/skills/total-recall/modules/skill-deploy/README.md +9 -0
  42. package/scaffold/.agent/skills/total-recall/openwiki/README.md +10 -0
  43. package/scaffold/.agent/skills/total-recall/openwiki/architecture.md +5 -0
  44. package/scaffold/.agent/skills/total-recall/openwiki/ide.md +10 -0
  45. package/scaffold/.agent/skills/total-recall/openwiki/memory.md +11 -0
  46. package/scaffold/.agent/skills/total-recall/openwiki/secrets.md +7 -0
  47. package/scaffold/.agent/skills/total-recall/openwiki/skills.md +7 -0
  48. package/src/cli/brain.mjs +63 -5
  49. package/src/cli/connect.mjs +11 -19
  50. package/src/cli/daemon.mjs +5 -4
  51. package/src/cli/deploy-ui.mjs +8 -3
  52. package/src/cli/deploy.mjs +84 -27
  53. package/src/cli/ingest/index.mjs +1 -1
  54. package/src/cli/init.mjs +63 -34
  55. package/src/cli/remember.mjs +3 -3
  56. package/src/cli/secret.mjs +812 -0
  57. package/src/cli/setup.mjs +34 -7
  58. package/src/cli/share.mjs +2 -2
  59. package/src/cli/skill.mjs +396 -35
  60. package/src/cli/sync.mjs +4 -4
  61. package/src/cli/task.mjs +287 -0
  62. package/src/core/clarity-rewriter.mjs +5 -5
  63. package/src/core/config.mjs +44 -2
  64. package/src/core/conflict-detector.mjs +5 -5
  65. package/src/core/crons.mjs +54 -0
  66. package/src/core/daemon-loop.mjs +87 -235
  67. package/src/core/dream.mjs +170 -10
  68. package/src/core/env-import.mjs +456 -0
  69. package/src/core/fact-seeker.mjs +14 -14
  70. package/src/core/import-rules.mjs +20 -7
  71. package/src/core/inference-engine.mjs +3 -3
  72. package/src/core/migrate.mjs +14 -8
  73. package/src/core/okf-adapter.mjs +2 -2
  74. package/src/core/operation-validator.mjs +39 -331
  75. package/src/core/optimizer.mjs +7 -0
  76. package/src/core/post-mortem.mjs +2 -2
  77. package/src/core/project-brain.mjs +349 -0
  78. package/src/core/provider-catalog.mjs +296 -0
  79. package/src/core/quick-capture.mjs +1 -1
  80. package/src/core/remote-vault-sync.mjs +194 -0
  81. package/src/core/research-queue.mjs +7 -11
  82. package/src/core/runtime.mjs +33 -9
  83. package/src/core/scheduler.mjs +59 -5
  84. package/src/core/schema.mjs +14 -0
  85. package/src/core/search.mjs +1 -1
  86. package/src/core/secrets-env-export.mjs +323 -0
  87. package/src/core/secrets-rotate.mjs +245 -0
  88. package/src/core/secrets-store.mjs +645 -0
  89. package/src/core/semantic-index.mjs +3 -3
  90. package/src/core/skills-registry.mjs +1062 -0
  91. package/src/core/source-adapters.mjs +4 -1
  92. package/src/core/ssss-host-extension.mjs +94 -0
  93. package/src/core/ssss-kernel-bridge.mjs +530 -0
  94. package/src/core/surface.mjs +14 -0
  95. package/src/core/task-envelope.mjs +298 -0
  96. package/src/core/task-executors.mjs +600 -0
  97. package/src/core/validated-write.mjs +124 -56
  98. package/src/core/vault.mjs +21 -34
  99. package/src/core/webauthn-store.mjs +272 -0
  100. package/src/server/api.mjs +26 -16
  101. package/src/server/auth.mjs +51 -0
  102. package/src/server/index.mjs +3 -3
  103. package/src/server/keys.mjs +30 -4
  104. package/src/server/rest.mjs +198 -25
  105. package/src/server/route-manifest.json +156 -0
  106. package/src/server/routes/_shared.mjs +40 -1
  107. package/src/server/routes/docs.mjs +260 -0
  108. package/src/server/routes/memory.mjs +145 -32
  109. package/src/server/routes/research.mjs +30 -0
  110. package/src/server/routes/secrets.mjs +470 -0
  111. package/src/server/routes/share.mjs +2 -2
  112. package/src/server/routes/skills.mjs +452 -54
  113. package/src/server/routes/sync.mjs +48 -0
  114. package/src/server/routes/webauthn.mjs +184 -0
  115. package/src/server/update-route-manifest.mjs +39 -0
  116. package/templates/onboarding-interview.md +32 -22
  117. package/templates/openwiki/README.md +10 -0
  118. package/templates/openwiki/architecture.md +5 -0
  119. package/templates/openwiki/ide.md +10 -0
  120. package/templates/openwiki/memory.md +11 -0
  121. package/templates/openwiki/secrets.md +7 -0
  122. package/templates/openwiki/skills.md +7 -0
  123. package/scaffold/.agent/skills/total-recall/skills/tr-cli-agents/SKILL.md +0 -121
  124. package/scaffold/.agent/skills/total-recall/skills/tr-cli-agents/references/antigravity.txt +0 -20
  125. package/scaffold/.agent/skills/total-recall/skills/tr-cli-agents/references/claude.txt +0 -58
  126. package/scaffold/.agent/skills/total-recall/skills/tr-cli-agents/references/codex.txt +0 -81
  127. package/scaffold/.agent/skills/total-recall/skills/tr-cli-agents/references/gemini.txt +0 -44
  128. package/scaffold/.agent/skills/total-recall/skills/tr-cli-agents/scripts/.gitkeep +0 -0
  129. package/scaffold/.agent/skills/total-recall/skills/tr-cli-agents/subagents/.gitkeep +0 -0
  130. package/scaffold/.agent/skills/total-recall/skills/tr-research/SKILL.md +0 -338
  131. package/scaffold/.agent/skills/total-recall/skills/tr-skill/SKILL.md +0 -282
  132. package/scaffold/.agent/skills/total-recall/skills/tr-skill/assets/skill-template/SKILL.md +0 -20
  133. package/scaffold/.agent/skills/total-recall/skills/tr-skill/evals/evals.json +0 -20
  134. package/scaffold/.agent/skills/total-recall/skills/tr-skill/hooks/on-skill-add.sh +0 -14
  135. package/scaffold/.agent/skills/total-recall/skills/tr-skill/hooks/on-skill-delete.sh +0 -14
  136. package/scaffold/.agent/skills/total-recall/skills/tr-skill/learnings/.gitkeep +0 -0
  137. package/scaffold/.agent/skills/total-recall/skills/tr-skill/references/.gitkeep +0 -0
  138. package/scaffold/.agent/skills/total-recall/skills/tr-skill/scripts/.gitkeep +0 -0
  139. package/scaffold/.agent/skills/total-recall/skills/tr-skill/scripts/create-skill.sh +0 -65
  140. package/scaffold/.agent/skills/total-recall/skills/tr-skill/scripts/enforce-skill-optimization.mjs +0 -135
  141. package/scaffold/.agent/skills/total-recall/skills/tr-skill/scripts/skill-watcher.mjs +0 -64
  142. package/scaffold/.agent/skills/total-recall/skills/tr-skill/subagents/audit-all-skills.md +0 -62
  143. package/scaffold/.agent/skills/total-recall/skills/tr-ssss/SKILL.md +0 -298
  144. package/scaffold/.agent/skills/total-recall/skills/tr-ssss/evals/evals.json +0 -17
  145. package/scaffold/.agent/skills/total-recall/skills/tr-ssss/references/CHANGELOG.md +0 -124
  146. package/scaffold/.agent/skills/total-recall/skills/tr-ssss/references/ssss-spec.md +0 -1325
  147. package/scaffold/.agent/skills/total-recall/skills/tr-ssss/scripts/validate-schema.mjs +0 -232
  148. package/scaffold/.agent/skills/total-recall/skills/tr-ssss/subagents/audit-ssss-node.md +0 -9
  149. /package/scaffold/.agent/skills/total-recall/{skills/tr-cli-agents/evals → modules/skill-deploy/scripts}/.gitkeep +0 -0
  150. /package/scaffold/.agent/skills/total-recall/{skills/tr-skill → modules/skill-deploy}/scripts/find-skills.mjs +0 -0
  151. /package/scaffold/.agent/skills/total-recall/{skills/tr-skill → modules/skill-deploy}/scripts/install-skill.mjs +0 -0
  152. /package/scaffold/.agent/skills/total-recall/{skills/tr-skill → modules/skill-deploy}/scripts/scan-skill.mjs +0 -0
@@ -58,7 +58,10 @@ export function loadResearchConfig(configPath = DEFAULT_CONFIG_PATH) {
58
58
  githubToken: process.env.GITHUB_TOKEN || secrets.github_token || fileConfig.github_token || defaultGithubToken || null,
59
59
  fetchTimeoutMs: fileConfig.fetch_timeout_ms || 10000,
60
60
  maxResultsPerSource: fileConfig.max_results_per_source || 5,
61
- userAgent: fileConfig.user_agent || 'TotalRecall/1.0 (knowledge-acquisition; +https://github.com/gregiteen/total-recall)',
61
+ userAgent:
62
+ fileConfig.user_agent ||
63
+ process.env.TR_USER_AGENT ||
64
+ 'TotalRecall/1.0 (knowledge-acquisition)',
62
65
  // Daily cap: protects free-tier users from unexpected overage charges.
63
66
  // Default 50/day ≈ 1,500/month — within Brave/Tavily/Exa free tiers (~1,000/month each).
64
67
  // Set to 0 to disable (paid plans with high volume).
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Total Recall host extension registry for SSSS 0.9.
3
+ *
4
+ * Core document primitives come from `@ssss/cli/registry/core.json`.
5
+ * Package-shipped SSSS extensions are consumed as-is.
6
+ * Everything else in local Zod schemas becomes the TR host extension.
7
+ */
8
+ import { createRequire } from 'node:module';
9
+ import { loadRegistries } from '@ssss/cli/registry';
10
+ import { SSSS_SCHEMAS } from './schema.mjs';
11
+
12
+ const require = createRequire(import.meta.url);
13
+ const core = require('@ssss/cli/registry/core.json');
14
+
15
+ const CORE_TYPES = new Set(Object.keys(core.document_primitives || {}));
16
+
17
+ function packageKnownTypes() {
18
+ const base = loadRegistries();
19
+ return new Set([...base.types.keys()]);
20
+ }
21
+
22
+ /** Types that exist only in Total Recall (or product overlays carried here). */
23
+ export function listHostOnlyTypes() {
24
+ const known = packageKnownTypes();
25
+ return Object.keys(SSSS_SCHEMAS)
26
+ .filter((type) => !known.has(type))
27
+ .sort();
28
+ }
29
+
30
+ /** Types present in TR Zod schemas that already come from package core/extensions. */
31
+ export function listTypesProvidedByPackage() {
32
+ const known = packageKnownTypes();
33
+ return Object.keys(SSSS_SCHEMAS)
34
+ .filter((type) => known.has(type))
35
+ .sort();
36
+ }
37
+
38
+ /** Types Total Recall must consume from the package core registry. */
39
+ export function listCoreTypes() {
40
+ return [...CORE_TYPES].sort();
41
+ }
42
+
43
+ /** Core types present in package but missing from local Zod registry. */
44
+ export function listMissingCoreSchemas() {
45
+ return listCoreTypes().filter((type) => !(type in SSSS_SCHEMAS));
46
+ }
47
+
48
+ function hostPrimitive(type, options = {}) {
49
+ return {
50
+ family: options.family || 'host',
51
+ append_only: !!options.append_only,
52
+ portability: options.portability || 'tenant_private',
53
+ required_fields: options.required_fields || ['type', 'title', 'description', 'timestamp'],
54
+ optional_fields: options.optional_fields || [],
55
+ notes: options.notes || `Total Recall host extension type '${type}'.`,
56
+ };
57
+ }
58
+
59
+ /**
60
+ * Build a package-compatible extension registry for TR host-only types.
61
+ * Field enforcement remains partially on Zod overlays until full declaration
62
+ * migration; the registry makes composition and kernel routing possible.
63
+ */
64
+ export function buildTotalRecallHostExtension() {
65
+ const document_primitives = {};
66
+ for (const type of listHostOnlyTypes()) {
67
+ const resourceBound = new Set([
68
+ 'email_account', 'deployment', 'workspace', 'workspace_transfer',
69
+ ]);
70
+ const structural = new Set(['language_convention', 'extension']);
71
+ let portability = 'tenant_private';
72
+ if (resourceBound.has(type)) portability = 'resource_bound';
73
+ if (structural.has(type)) portability = 'structural';
74
+ document_primitives[type] = hostPrimitive(type, {
75
+ portability,
76
+ family: resourceBound.has(type) ? 'resource' : 'host',
77
+ });
78
+ }
79
+
80
+ return {
81
+ registry: 'total-recall',
82
+ extends: 'ssss',
83
+ version: '1.0.0',
84
+ description: 'Total Recall host-only document primitives for SSSS 0.9 composition.',
85
+ document_primitives,
86
+ };
87
+ }
88
+
89
+ export function getTotalRecallHostExtension() {
90
+ return buildTotalRecallHostExtension();
91
+ }
92
+
93
+ // Lazy: package types must be loadable before first composition.
94
+ export const TOTAL_RECALL_HOST_EXTENSION = buildTotalRecallHostExtension();
@@ -0,0 +1,530 @@
1
+ /**
2
+ * SSSS 0.9 package kernel bridge for Total Recall.
3
+ *
4
+ * Modes (env TR_SSSS_KERNEL_MODE):
5
+ * kernel-core — **default** — package kernel for core types (incl. memory/workflow)
6
+ * kernel-low-risk — package kernel for structural low-risk core types only
7
+ * kernel — package kernel for all package-known types
8
+ * shadow — legacy commits; package kernel dry-runs for verdict diffs
9
+ * legacy — local processOperation only (fallback / unit tests)
10
+ */
11
+ import fs from 'node:fs';
12
+ import path from 'node:path';
13
+ import crypto from 'node:crypto';
14
+ import matter from 'gray-matter';
15
+ import { createEngine } from '@ssss/cli/engine';
16
+ import { createValidator } from '@ssss/cli/validator';
17
+ import {
18
+ composeRegistryLayers,
19
+ loadRegistries,
20
+ resolvePrimitiveDefinition,
21
+ } from '@ssss/cli/registry';
22
+ import { detectDirectWrites } from '@ssss/cli/guard';
23
+ import { logger } from './logger.mjs';
24
+ import { validateMemoryNode } from './total-recall-memory-validator.mjs';
25
+ import {
26
+ TOTAL_RECALL_HOST_EXTENSION,
27
+ listCoreTypes,
28
+ listHostOnlyTypes,
29
+ listMissingCoreSchemas,
30
+ } from './ssss-host-extension.mjs';
31
+
32
+ const LOW_RISK_TYPES = new Set(['rule', 'page', 'assistant', 'skill', 'model']);
33
+ /** Core package primitives safe to cut over before full host-type migration. */
34
+ const CORE_ROUTE_TYPES = new Set([
35
+ ...LOW_RISK_TYPES,
36
+ 'memory',
37
+ 'workflow',
38
+ 'task',
39
+ 'security_role',
40
+ 'run',
41
+ 'conversation',
42
+ 'conflict',
43
+ 'migration',
44
+ 'release',
45
+ 'primitive',
46
+ ]);
47
+ const PROTOCOL_PATHS = new Set([
48
+ 'references/ssss-spec.md',
49
+ 'references/admin-protocol-evolution-policy.md',
50
+ 'references/user-local-optimizer-boundary-policy.md',
51
+ ]);
52
+
53
+ /** Source modules allowed to touch derived / non-canonical state without kernel. */
54
+ const DIRECT_WRITE_APPROVED = [
55
+ 'src/core/operation-validator.mjs',
56
+ 'src/core/ssss-kernel-bridge.mjs',
57
+ 'src/core/validated-write.mjs',
58
+ 'src/core/vault.mjs',
59
+ 'src/core/surface.mjs',
60
+ 'src/core/blackboard.mjs',
61
+ 'src/core/steering.mjs',
62
+ 'src/core/logger.mjs',
63
+ 'src/core/daemon-control.mjs',
64
+ 'src/core/snapshot.mjs',
65
+ ];
66
+
67
+ let cachedEngine = null;
68
+ let cachedRegistryKey = '';
69
+
70
+ export function getKernelMode() {
71
+ // Default to package-kernel cutover for core types (memory/workflow/low-risk).
72
+ // Set TR_SSSS_KERNEL_MODE=legacy only when deliberately testing the old pipeline.
73
+ const mode = (process.env.TR_SSSS_KERNEL_MODE || 'kernel-core').toLowerCase();
74
+ if (['legacy', 'shadow', 'kernel-low-risk', 'kernel-core', 'kernel'].includes(mode)) return mode;
75
+ return 'kernel-core';
76
+ }
77
+
78
+ export function inventorySummary() {
79
+ return {
80
+ mode: getKernelMode(),
81
+ core_types: listCoreTypes(),
82
+ host_only_types: listHostOnlyTypes(),
83
+ missing_local_core_schemas: listMissingCoreSchemas(),
84
+ low_risk_types: [...LOW_RISK_TYPES],
85
+ core_route_types: [...CORE_ROUTE_TYPES],
86
+ };
87
+ }
88
+
89
+ export function mapTrPrincipal(envelope, callOptions = {}, vaultRoot = '') {
90
+ if (callOptions.principal) return callOptions.principal;
91
+
92
+ const role = callOptions.agentRole || envelope?.actor?.role || null;
93
+ if (!role) return null;
94
+
95
+ let kind = 'agent';
96
+ let capabilities = [];
97
+
98
+ if (role === 'system') {
99
+ kind = 'system';
100
+ capabilities = ['*:*'];
101
+ } else if (role === 'admin') {
102
+ kind = 'human';
103
+ capabilities = ['*:*'];
104
+ } else if (role === 'optimizer') {
105
+ kind = 'agent';
106
+ capabilities = ['memory:create', 'memory:replace', 'memory:patch', 'write:memory', 'memory:*'];
107
+ } else if (vaultRoot && /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(role)) {
108
+ const roleFile = path.join(vaultRoot, 'roles', role, 'ROLE.md');
109
+ if (fs.existsSync(roleFile)) {
110
+ try {
111
+ const { data } = matter(fs.readFileSync(roleFile, 'utf8'));
112
+ capabilities = Array.isArray(data.permissions) ? data.permissions : [];
113
+ } catch {
114
+ capabilities = [];
115
+ }
116
+ }
117
+ }
118
+
119
+ // Map TR write:* style permissions into capability wildcards the package authorizer understands.
120
+ // Package kernel may require either local (`memory:create`) or qualified (`ssss:memory:create`) IDs.
121
+ const expanded = new Set(capabilities);
122
+ const addTypeCaps = (type) => {
123
+ for (const action of ['*', 'create', 'replace', 'patch', 'delete', 'event', 'append']) {
124
+ expanded.add(`${type}:${action}`);
125
+ expanded.add(`ssss:${type}:${action}`);
126
+ }
127
+ };
128
+ for (const perm of capabilities) {
129
+ if (perm === 'write:*' || perm === '*:*') expanded.add('*:*');
130
+ if (perm.startsWith('write:')) addTypeCaps(perm.slice('write:'.length));
131
+ // Also expand bare type wildcards / actions already present.
132
+ const match = /^([a-z][a-z0-9_-]*):([a-z*].*)$/i.exec(perm);
133
+ if (match) {
134
+ expanded.add(perm);
135
+ expanded.add(`ssss:${perm}`);
136
+ if (match[2] === '*') addTypeCaps(match[1]);
137
+ }
138
+ }
139
+ // Optimizer convenience: memory mutations.
140
+ if (role === 'optimizer') addTypeCaps('memory');
141
+
142
+ return {
143
+ id: `tr-role:${role}`,
144
+ kind,
145
+ workspaceIds: [envelope.workspace_id].filter(Boolean),
146
+ capabilities: [...expanded],
147
+ authentication: {
148
+ provider: 'total-recall-role-adapter',
149
+ assurance: role === 'admin' || role === 'system' ? 'elevated' : 'verified',
150
+ },
151
+ role,
152
+ };
153
+ }
154
+
155
+ /**
156
+ * Package loadRegistries() already includes core + any shipped package extensions.
157
+ * Re-compose so the Total Recall host extension attaches without colliding with
158
+ * package-provided types.
159
+ */
160
+ export function createTotalRecallRegistrySet(options = {}) {
161
+ const base = loadRegistries(options.registryDir);
162
+ const host = TOTAL_RECALL_HOST_EXTENSION;
163
+ const packageExtensions = new Map();
164
+
165
+ for (const def of base.types.values()) {
166
+ if (!def.registry || def.registry === 'core') continue;
167
+ if (!packageExtensions.has(def.registry)) {
168
+ packageExtensions.set(def.registry, {
169
+ registry: def.registry,
170
+ extends: 'ssss',
171
+ version: base.extensionVersions?.get(def.registry) || '0.0.0',
172
+ document_primitives: {},
173
+ });
174
+ }
175
+ const { type, qualified_type, registry, ...rest } = def;
176
+ packageExtensions.get(def.registry).document_primitives[def.type] = rest;
177
+ }
178
+
179
+ return composeRegistryLayers({
180
+ core: base.core,
181
+ installed: [...packageExtensions.values()],
182
+ repository: [host],
183
+ });
184
+ }
185
+
186
+ export function getTotalRecallEngine(options = {}) {
187
+ const registrySet = createTotalRecallRegistrySet(options);
188
+ const key = `${[...registrySet.types.keys()].sort().join(',')}|${options.leaseStore || ''}`;
189
+ if (!cachedEngine || cachedRegistryKey !== key || options.forceNew) {
190
+ const validator = createValidator({ registrySet });
191
+ cachedEngine = createEngine({
192
+ registrySet,
193
+ validator,
194
+ leaseStore: options.leaseStore,
195
+ verifyPrincipal: async (envelope, callOptions) =>
196
+ mapTrPrincipal(envelope, callOptions, callOptions.vaultRoot || options.vaultRoot || ''),
197
+ });
198
+ cachedRegistryKey = key;
199
+ }
200
+ return cachedEngine;
201
+ }
202
+
203
+ function declaredTypeFromEnvelope(envelope) {
204
+ if (envelope?.primitive_type) return envelope.primitive_type;
205
+ if (envelope?.type === 'operation' && typeof envelope.content === 'string') {
206
+ try {
207
+ return matter(envelope.content).data?.type || null;
208
+ } catch {
209
+ return null;
210
+ }
211
+ }
212
+ if (envelope?.type === 'patch' || envelope?.type === 'delete') {
213
+ // Caller may pass resolved type; otherwise unknown until kernel reads the file.
214
+ return envelope.primitive_type || null;
215
+ }
216
+ return null;
217
+ }
218
+
219
+ export function isLowRiskEnvelope(envelope) {
220
+ const declared = declaredTypeFromEnvelope(envelope);
221
+ return declared ? LOW_RISK_TYPES.has(declared) : false;
222
+ }
223
+
224
+ export function isCoreRouteEnvelope(envelope) {
225
+ const declared = declaredTypeFromEnvelope(envelope);
226
+ return declared ? CORE_ROUTE_TYPES.has(declared) : false;
227
+ }
228
+
229
+ export function isProtocolPath(vfsPath = '') {
230
+ const cleaned = String(vfsPath).replace(/^\/+/, '');
231
+ if (PROTOCOL_PATHS.has(cleaned)) return true;
232
+ if (cleaned.startsWith('fixtures/')) return true;
233
+ if (cleaned.endsWith('schema.mjs') || cleaned.endsWith('schema.spec.mjs')) return true;
234
+ return false;
235
+ }
236
+
237
+ function stringifyDocument(data, body) {
238
+ // Prefer gray-matter when available; fall back to simple frontmatter dump.
239
+ try {
240
+ return matter.stringify(body || '', data);
241
+ } catch {
242
+ const yaml = Object.entries(data)
243
+ .map(([key, value]) => `${key}: ${JSON.stringify(value)}`)
244
+ .join('\n');
245
+ return `---\n${yaml}\n---\n\n${body || ''}`;
246
+ }
247
+ }
248
+
249
+ /**
250
+ * Host preflight: satisfy package universal frontmatter + TR memory policy.
251
+ * Returns { envelope, warnings, errors }.
252
+ */
253
+ /**
254
+ * Stable timestamp for a given idempotency key so exact retries prepare identical
255
+ * content (package kernel binds idempotency to the request hash of content).
256
+ */
257
+ export function stableTimestampForKey(idempotencyKey, fallback = () => new Date().toISOString()) {
258
+ if (typeof idempotencyKey !== 'string' || !idempotencyKey) return fallback();
259
+ const digest = crypto.createHash('sha256').update(idempotencyKey).digest();
260
+ // Fixed epoch window so values stay valid ISO datetimes without needing wall clock.
261
+ const ms = Date.UTC(2026, 0, 1) + (digest.readUInt32BE(0) % (86400_000 * 365));
262
+ return new Date(ms).toISOString();
263
+ }
264
+
265
+ export function prepareEnvelopeForKernel(envelope, options = {}) {
266
+ const warnings = [];
267
+ const errors = [];
268
+ const role = options.agentRole || envelope.actor?.role || null;
269
+ const now = options.clock?.() || stableTimestampForKey(envelope.idempotency_key);
270
+ let env = { ...envelope };
271
+
272
+ if (env.type === 'operation' && typeof env.content === 'string') {
273
+ let parsed;
274
+ try {
275
+ parsed = matter(env.content);
276
+ } catch (error) {
277
+ return { envelope: env, warnings, errors: [`Content parsing error: ${error.message}`] };
278
+ }
279
+ const data = { ...parsed.data };
280
+
281
+ // Package 0.9 universal frontmatter.
282
+ if (!data.title && data.name) data.title = data.name;
283
+ if (!data.description) {
284
+ data.description = data.title || data.name || data.slug || 'Total Recall document';
285
+ }
286
+ if (!data.timestamp) {
287
+ data.timestamp = data.updated || data.created || now;
288
+ }
289
+
290
+ if (data.type === 'memory') {
291
+ if (!env.dry_run) {
292
+ data.updated = now;
293
+ data.last_accessed = now;
294
+ if (!data.created) data.created = now;
295
+ }
296
+ // TR host overlay (schema v2 + sentiment_target).
297
+ const overlay = validateMemoryNode(data);
298
+ if (!overlay.success) errors.push(...overlay.errors);
299
+
300
+ if (role === 'optimizer') {
301
+ if (data.priority === 'absolute') warnings.push('Optimizer writing Tier 1 (priority: absolute) node.');
302
+ if (data.immutable === true) warnings.push('Optimizer writing immutable node.');
303
+ }
304
+ }
305
+
306
+ if (data.type === 'workflow') {
307
+ if (!data.title && data.name) data.title = data.name;
308
+ if (!data.name && data.title) data.name = data.title;
309
+ }
310
+
311
+ if (data.type === 'schema-proposal' && data.status === 'accepted') {
312
+ if (role !== 'admin' && role !== 'system') {
313
+ errors.push('Only admin may accept schema-proposals.');
314
+ }
315
+ if (!data.reviewed_by) errors.push('Accepted schema-proposals must have reviewed_by.');
316
+ }
317
+
318
+ env = { ...env, content: stringifyDocument(data, parsed.content) };
319
+ }
320
+
321
+ if (env.type === 'patch' && env.patches && typeof env.patches === 'object') {
322
+ const patches = { ...env.patches };
323
+ // Memory touch stamps on patch.
324
+ if (!env.dry_run && (patches.type === 'memory' || options.resolvedType === 'memory' || env.path?.includes('memory-vault/') || /^(invariants|patterns|anti-patterns|preferences|decisions|concepts|facts|lore)\//.test(env.path || ''))) {
325
+ patches.updated = patches.updated || now;
326
+ patches.last_accessed = patches.last_accessed || now;
327
+ }
328
+ env = { ...env, patches };
329
+ }
330
+
331
+ return { envelope: env, warnings, errors };
332
+ }
333
+
334
+ /**
335
+ * Execute through the package kernel (async).
336
+ * Injects actor.role for the engine façade when only agentRole is supplied.
337
+ */
338
+ export async function processViaPackageKernel(envelope, vaultRoot, options = {}) {
339
+ const engine = getTotalRecallEngine({
340
+ leaseStore: options.leaseStore,
341
+ vaultRoot,
342
+ registryDir: options.registryDir,
343
+ forceNew: options.forceNew,
344
+ });
345
+
346
+ const role = options.agentRole || envelope.actor?.role;
347
+ let env = {
348
+ ...envelope,
349
+ actor: envelope.actor || (role ? { role } : undefined),
350
+ };
351
+
352
+ // Host policy: protocol paths require admin/system before kernel sees the write.
353
+ if (isProtocolPath(env.path) && role !== 'admin' && role !== 'system') {
354
+ return {
355
+ success: false,
356
+ type: env.type,
357
+ operation_id: null,
358
+ path: env.path,
359
+ committed_at: null,
360
+ dry_run: !!env.dry_run,
361
+ validation: {
362
+ valid: false,
363
+ type: null,
364
+ errors: [`Protocol path '${env.path}' requires admin role.`],
365
+ warnings: [],
366
+ },
367
+ };
368
+ }
369
+
370
+ const prepared = prepareEnvelopeForKernel(env, { ...options, agentRole: role });
371
+ if (prepared.errors.length) {
372
+ return {
373
+ success: false,
374
+ type: env.type,
375
+ operation_id: null,
376
+ path: env.path,
377
+ committed_at: null,
378
+ dry_run: !!env.dry_run,
379
+ validation: {
380
+ valid: false,
381
+ type: declaredTypeFromEnvelope(env),
382
+ errors: prepared.errors,
383
+ warnings: prepared.warnings,
384
+ },
385
+ repair: {
386
+ field_errors: prepared.errors.map((issue) => ({ field: '(host-policy)', issue })),
387
+ },
388
+ };
389
+ }
390
+ env = prepared.envelope;
391
+
392
+ const eventsDir = path.join(vaultRoot, '.events');
393
+ if (!env.dry_run && !fs.existsSync(eventsDir)) fs.mkdirSync(eventsDir, { recursive: true });
394
+ const response = await engine.processOperation(env, vaultRoot, {
395
+ ...options,
396
+ vaultRoot,
397
+ principal: options.principal || mapTrPrincipal(env, options, vaultRoot),
398
+ });
399
+
400
+ if (prepared.warnings.length && response?.validation) {
401
+ response.validation.warnings = [
402
+ ...(response.validation.warnings || []),
403
+ ...prepared.warnings,
404
+ ];
405
+ }
406
+
407
+ // Handle successful commit TR-specific audit log
408
+ if (response.success && !env.dry_run) {
409
+ if (response.type !== 'event') {
410
+ const eventsDir = path.join(vaultRoot, '.events');
411
+ if (!fs.existsSync(eventsDir)) fs.mkdirSync(eventsDir, { recursive: true });
412
+ const auditLog = {
413
+ id: crypto.randomUUID(),
414
+ event_type: 'audit',
415
+ timestamp: new Date().toISOString(),
416
+ operation_id: response.operation_id,
417
+ path: response.path,
418
+ actor: env.actor?.role || 'system',
419
+ payload: {
420
+ resolved_type: response.validation?.type || 'memory'
421
+ }
422
+ };
423
+ fs.appendFileSync(path.join(eventsDir, 'audit.jsonl'), JSON.stringify(auditLog) + '\n');
424
+ }
425
+ }
426
+
427
+ return response;
428
+ }
429
+
430
+ export function compareVerdicts(localResult, kernelResult) {
431
+ return {
432
+ success_match: !!localResult?.success === !!kernelResult?.success,
433
+ valid_match: !!localResult?.validation?.valid === !!kernelResult?.validation?.valid,
434
+ type_match: (localResult?.validation?.type || null) === (kernelResult?.validation?.type || null),
435
+ local: {
436
+ success: !!localResult?.success,
437
+ valid: !!localResult?.validation?.valid,
438
+ type: localResult?.validation?.type || null,
439
+ errors: localResult?.validation?.errors || [],
440
+ },
441
+ kernel: {
442
+ success: !!kernelResult?.success,
443
+ valid: !!kernelResult?.validation?.valid,
444
+ type: kernelResult?.validation?.type || null,
445
+ errors: kernelResult?.validation?.errors || [],
446
+ },
447
+ };
448
+ }
449
+
450
+ export async function shadowCompare(envelope, vaultRoot, localResult, options = {}) {
451
+ const dryEnvelope = { ...envelope, dry_run: true };
452
+ try {
453
+ const kernelResult = await processViaPackageKernel(dryEnvelope, vaultRoot, options);
454
+ const comparison = compareVerdicts(localResult, kernelResult);
455
+ if (!comparison.success_match || !comparison.valid_match) {
456
+ logger.warn?.(
457
+ 'ssss-shadow',
458
+ `Verdict drift path=${envelope.path} local.success=${comparison.local.success} kernel.success=${comparison.kernel.success} local.valid=${comparison.local.valid} kernel.valid=${comparison.kernel.valid}`,
459
+ );
460
+ }
461
+ return { comparison, kernelResult };
462
+ } catch (error) {
463
+ logger.warn?.('ssss-shadow', `Kernel shadow failed: ${error.message}`);
464
+ return {
465
+ comparison: {
466
+ success_match: false,
467
+ valid_match: false,
468
+ type_match: false,
469
+ error: error.message,
470
+ },
471
+ kernelResult: null,
472
+ };
473
+ }
474
+ }
475
+
476
+ export function shouldRouteToKernel(envelope) {
477
+ const mode = getKernelMode();
478
+ if (mode === 'legacy' || mode === 'shadow') return false;
479
+ if (mode === 'kernel') return true;
480
+ if (mode === 'kernel-core') return isCoreRouteEnvelope(envelope);
481
+ if (mode === 'kernel-low-risk') return isLowRiskEnvelope(envelope);
482
+ return false;
483
+ }
484
+
485
+ /**
486
+ * Scan host source for direct filesystem mutations outside approved adapters.
487
+ * Returns package-guard findings filtered to canonical-risk paths.
488
+ */
489
+ export function scanDirectCanonicalWrites(rootDir = path.resolve('src'), options = {}) {
490
+ const approved = options.approved || DIRECT_WRITE_APPROVED;
491
+ const findings = detectDirectWrites(rootDir, { approved, ...options });
492
+
493
+ // Highlight likely canonical vault mutations (memory nodes, vault docs).
494
+ const CANONICAL_HINT = /memory-vault|writeNode\b|safeStringify\b|processOperation\b|atomicWrite\b/;
495
+ return findings.map((finding) => ({
496
+ ...finding,
497
+ severity: CANONICAL_HINT.test(finding.source) ? 'high' : 'medium',
498
+ }));
499
+ }
500
+
501
+ /**
502
+ * Modules that write memory-like markdown without going through Operation Contract.
503
+ * Heuristic: atomicWrite/writeNode call sites outside the approved contract surface.
504
+ */
505
+ export function listUnapprovedCanonicalWriters(rootDir = path.resolve('src/core')) {
506
+ const approved = new Set(DIRECT_WRITE_APPROVED.map((p) => path.normalize(p)));
507
+ const writers = [];
508
+ const stack = [path.resolve(rootDir)];
509
+ while (stack.length) {
510
+ const dir = stack.pop();
511
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
512
+ if (entry.name === 'node_modules' || entry.name.endsWith('.spec.mjs')) continue;
513
+ const absolute = path.join(dir, entry.name);
514
+ if (entry.isDirectory()) { stack.push(absolute); continue; }
515
+ if (!entry.name.endsWith('.mjs')) continue;
516
+ const relative = path.relative(process.cwd(), absolute).split(path.sep).join('/');
517
+ if (approved.has(relative) || approved.has(path.normalize(relative))) continue;
518
+ const text = fs.readFileSync(absolute, 'utf8');
519
+ if (/\bwriteNode\s*\(/.test(text) || /\batomicWrite\s*\([^)]*\.md/.test(text)) {
520
+ writers.push(relative);
521
+ }
522
+ }
523
+ }
524
+ return writers.sort();
525
+ }
526
+
527
+ export function resolveHostDefinition(type) {
528
+ const registrySet = createTotalRecallRegistrySet();
529
+ return resolvePrimitiveDefinition(registrySet, type);
530
+ }
@@ -4,6 +4,7 @@ import crypto from 'crypto';
4
4
  import { loadSkills, atomicWrite, walkMd } from './vault.mjs';
5
5
  import { getNodes } from './vault-cache.mjs';
6
6
  import matter from 'gray-matter';
7
+ import { logger } from './logger.mjs';
7
8
  import {
8
9
  buildMemoryLayerIndex,
9
10
  inferMemoryLayer,
@@ -428,6 +429,19 @@ export async function buildRulesBlock(skillsDir, nodes = [], { consumer = 'ide',
428
429
  } catch (err) {
429
430
  console.error('Error in skills injection:', err);
430
431
  }
432
+
433
+ // 4. Inject OpenWiki Codebase Summary
434
+ try {
435
+ const baseDir = path.dirname(path.dirname(skillsDir));
436
+ const quickstartPath = path.join(baseDir, 'openwiki', 'quickstart.md');
437
+ if (fs.existsSync(quickstartPath)) {
438
+ const quickstart = fs.readFileSync(quickstartPath, 'utf8');
439
+ const summary = quickstart.length > 2500 ? quickstart.substring(0, 2500) + '\n... (truncated)' : quickstart;
440
+ combined += `\n\n## OpenWiki Codebase Summary\n\nThe following is the generated codebase overview from OpenWiki:\n\n${summary}`;
441
+ }
442
+ } catch (err) {
443
+ console.error('Error injecting OpenWiki:', err);
444
+ }
431
445
  }
432
446
 
433
447
  return combined.trim();