om-memory-system 3.0.0
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/LICENSE +21 -0
- package/README.md +587 -0
- package/dist/adapters/opencode/auto-capture-host.d.ts +3 -0
- package/dist/adapters/opencode/auto-capture-host.js +29 -0
- package/dist/adapters/opencode/auto-capture-summary.d.ts +4 -0
- package/dist/adapters/opencode/auto-capture-summary.js +147 -0
- package/dist/adapters/opencode/conversation.d.ts +2 -0
- package/dist/adapters/opencode/conversation.js +50 -0
- package/dist/adapters/opencode/user-prompt.d.ts +9 -0
- package/dist/adapters/opencode/user-prompt.js +26 -0
- package/dist/adapters/pi/capture.d.ts +41 -0
- package/dist/adapters/pi/capture.js +91 -0
- package/dist/adapters/pi/conversation.d.ts +38 -0
- package/dist/adapters/pi/conversation.js +118 -0
- package/dist/adapters/pi/extension.d.ts +18 -0
- package/dist/adapters/pi/extension.js +234 -0
- package/dist/adapters/pi/import-command.d.ts +29 -0
- package/dist/adapters/pi/import-command.js +232 -0
- package/dist/adapters/pi/profile.d.ts +39 -0
- package/dist/adapters/pi/profile.js +148 -0
- package/dist/adapters/pi/provider.d.ts +50 -0
- package/dist/adapters/pi/provider.js +77 -0
- package/dist/config.d.ts +290 -0
- package/dist/config.js +742 -0
- package/dist/core/capture-context.d.ts +4 -0
- package/dist/core/capture-context.js +132 -0
- package/dist/core/capture.d.ts +15 -0
- package/dist/core/capture.js +68 -0
- package/dist/core/extraction.d.ts +80 -0
- package/dist/core/extraction.js +140 -0
- package/dist/core/host.d.ts +52 -0
- package/dist/core/host.js +1 -0
- package/dist/core/memory-operations.d.ts +27 -0
- package/dist/core/memory-operations.js +247 -0
- package/dist/core/retrieval.d.ts +12 -0
- package/dist/core/retrieval.js +72 -0
- package/dist/importer/discovery.d.ts +31 -0
- package/dist/importer/discovery.js +88 -0
- package/dist/importer/importer.d.ts +87 -0
- package/dist/importer/importer.js +260 -0
- package/dist/importer/ledger.d.ts +44 -0
- package/dist/importer/ledger.js +137 -0
- package/dist/importer/session-loader.d.ts +19 -0
- package/dist/importer/session-loader.js +12 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.js +601 -0
- package/dist/plugin.d.ts +9 -0
- package/dist/plugin.js +11 -0
- package/dist/services/ai/ai-provider-factory.d.ts +7 -0
- package/dist/services/ai/ai-provider-factory.js +41 -0
- package/dist/services/ai/api-key-placeholder.d.ts +1 -0
- package/dist/services/ai/api-key-placeholder.js +14 -0
- package/dist/services/ai/internal-capture-sessions.d.ts +6 -0
- package/dist/services/ai/internal-capture-sessions.js +33 -0
- package/dist/services/ai/opencode-diagnostics.d.ts +7 -0
- package/dist/services/ai/opencode-diagnostics.js +36 -0
- package/dist/services/ai/opencode-host-config.d.ts +10 -0
- package/dist/services/ai/opencode-host-config.js +51 -0
- package/dist/services/ai/opencode-provider-loader.d.ts +3 -0
- package/dist/services/ai/opencode-provider-loader.js +4 -0
- package/dist/services/ai/opencode-provider.d.ts +92 -0
- package/dist/services/ai/opencode-provider.js +419 -0
- package/dist/services/ai/opencode-sdk-client.d.ts +6 -0
- package/dist/services/ai/opencode-sdk-client.js +38 -0
- package/dist/services/ai/profile-llm-client.d.ts +2 -0
- package/dist/services/ai/profile-llm-client.js +27 -0
- package/dist/services/ai/provider-config.d.ts +17 -0
- package/dist/services/ai/provider-config.js +30 -0
- package/dist/services/ai/providers/anthropic-messages.d.ts +34 -0
- package/dist/services/ai/providers/anthropic-messages.js +220 -0
- package/dist/services/ai/providers/base-provider.d.ts +24 -0
- package/dist/services/ai/providers/base-provider.js +24 -0
- package/dist/services/ai/providers/google-gemini.d.ts +15 -0
- package/dist/services/ai/providers/google-gemini.js +228 -0
- package/dist/services/ai/providers/minimax.d.ts +35 -0
- package/dist/services/ai/providers/minimax.js +55 -0
- package/dist/services/ai/providers/openai-chat-completion.d.ts +22 -0
- package/dist/services/ai/providers/openai-chat-completion.js +387 -0
- package/dist/services/ai/providers/openai-responses.d.ts +13 -0
- package/dist/services/ai/providers/openai-responses.js +182 -0
- package/dist/services/ai/providers/orcarouter.d.ts +51 -0
- package/dist/services/ai/providers/orcarouter.js +66 -0
- package/dist/services/ai/session/ai-session-manager.d.ts +24 -0
- package/dist/services/ai/session/ai-session-manager.js +211 -0
- package/dist/services/ai/session/session-types.d.ts +42 -0
- package/dist/services/ai/session/session-types.js +1 -0
- package/dist/services/ai/tools/tool-schema.d.ts +40 -0
- package/dist/services/ai/tools/tool-schema.js +24 -0
- package/dist/services/ai/validators/user-profile-validator.d.ts +12 -0
- package/dist/services/ai/validators/user-profile-validator.js +114 -0
- package/dist/services/api-handlers.d.ts +186 -0
- package/dist/services/api-handlers.js +1284 -0
- package/dist/services/auth-token.d.ts +14 -0
- package/dist/services/auth-token.js +59 -0
- package/dist/services/auto-capture.d.ts +3 -0
- package/dist/services/auto-capture.js +127 -0
- package/dist/services/cleanup-service.d.ts +22 -0
- package/dist/services/cleanup-service.js +109 -0
- package/dist/services/client.d.ts +134 -0
- package/dist/services/client.js +327 -0
- package/dist/services/context.d.ts +10 -0
- package/dist/services/context.js +27 -0
- package/dist/services/cors.d.ts +12 -0
- package/dist/services/cors.js +52 -0
- package/dist/services/deduplication-service.d.ts +28 -0
- package/dist/services/deduplication-service.js +111 -0
- package/dist/services/embedding.d.ts +36 -0
- package/dist/services/embedding.js +200 -0
- package/dist/services/jsonc.d.ts +6 -0
- package/dist/services/jsonc.js +76 -0
- package/dist/services/language-detector.d.ts +2 -0
- package/dist/services/language-detector.js +33 -0
- package/dist/services/legacy-migration.d.ts +77 -0
- package/dist/services/legacy-migration.js +343 -0
- package/dist/services/logger.d.ts +1 -0
- package/dist/services/logger.js +119 -0
- package/dist/services/memory-portability-service.d.ts +37 -0
- package/dist/services/memory-portability-service.js +340 -0
- package/dist/services/memory-scope.d.ts +16 -0
- package/dist/services/memory-scope.js +43 -0
- package/dist/services/migration-service.d.ts +42 -0
- package/dist/services/migration-service.js +307 -0
- package/dist/services/onnxruntime-resolve.d.ts +24 -0
- package/dist/services/onnxruntime-resolve.js +174 -0
- package/dist/services/privacy.d.ts +26 -0
- package/dist/services/privacy.js +69 -0
- package/dist/services/request-timeouts.d.ts +9 -0
- package/dist/services/request-timeouts.js +11 -0
- package/dist/services/runtime-require.d.ts +17 -0
- package/dist/services/runtime-require.js +75 -0
- package/dist/services/secret-resolver.d.ts +1 -0
- package/dist/services/secret-resolver.js +65 -0
- package/dist/services/shard-inventory-service.d.ts +69 -0
- package/dist/services/shard-inventory-service.js +315 -0
- package/dist/services/shard-path-migration-service.d.ts +31 -0
- package/dist/services/shard-path-migration-service.js +367 -0
- package/dist/services/tag-prefix-migration.d.ts +60 -0
- package/dist/services/tag-prefix-migration.js +439 -0
- package/dist/services/tag-prefix-scan.d.ts +26 -0
- package/dist/services/tag-prefix-scan.js +91 -0
- package/dist/services/tags.d.ts +29 -0
- package/dist/services/tags.js +251 -0
- package/dist/services/turso/connection-manager.d.ts +15 -0
- package/dist/services/turso/connection-manager.js +163 -0
- package/dist/services/turso/cross-process-write-lock.d.ts +1 -0
- package/dist/services/turso/cross-process-write-lock.js +89 -0
- package/dist/services/turso/legacy-migrator.d.ts +1 -0
- package/dist/services/turso/legacy-migrator.js +540 -0
- package/dist/services/turso/lifecycle.d.ts +1 -0
- package/dist/services/turso/lifecycle.js +14 -0
- package/dist/services/turso/operation-lock.d.ts +2 -0
- package/dist/services/turso/operation-lock.js +61 -0
- package/dist/services/turso/ready.d.ts +2 -0
- package/dist/services/turso/ready.js +36 -0
- package/dist/services/turso/shard-manager.d.ts +38 -0
- package/dist/services/turso/shard-manager.js +496 -0
- package/dist/services/turso/sqlite-handle-release.d.ts +16 -0
- package/dist/services/turso/sqlite-handle-release.js +56 -0
- package/dist/services/turso/turso-db.d.ts +18 -0
- package/dist/services/turso/turso-db.js +53 -0
- package/dist/services/turso/types.d.ts +42 -0
- package/dist/services/turso/types.js +1 -0
- package/dist/services/turso/vector-search.d.ts +41 -0
- package/dist/services/turso/vector-search.js +384 -0
- package/dist/services/turso/vector-utils.d.ts +7 -0
- package/dist/services/turso/vector-utils.js +67 -0
- package/dist/services/user-memory-learning.d.ts +97 -0
- package/dist/services/user-memory-learning.js +673 -0
- package/dist/services/user-profile/ai-cleanup.d.ts +58 -0
- package/dist/services/user-profile/ai-cleanup.js +486 -0
- package/dist/services/user-profile/profile-context.d.ts +1 -0
- package/dist/services/user-profile/profile-context.js +105 -0
- package/dist/services/user-profile/profile-utils.d.ts +2 -0
- package/dist/services/user-profile/profile-utils.js +45 -0
- package/dist/services/user-profile/types.d.ts +92 -0
- package/dist/services/user-profile/types.js +1 -0
- package/dist/services/user-profile/user-profile-manager.d.ts +65 -0
- package/dist/services/user-profile/user-profile-manager.js +1660 -0
- package/dist/services/user-prompt/user-prompt-manager.d.ts +53 -0
- package/dist/services/user-prompt/user-prompt-manager.js +303 -0
- package/dist/services/web-api-auth.d.ts +3 -0
- package/dist/services/web-api-auth.js +25 -0
- package/dist/services/web-auth.d.ts +46 -0
- package/dist/services/web-auth.js +105 -0
- package/dist/services/web-server.d.ts +57 -0
- package/dist/services/web-server.js +659 -0
- package/dist/shared/api/index.d.ts +4 -0
- package/dist/shared/api/index.js +2 -0
- package/dist/shared/api/portability-schemas.d.ts +56 -0
- package/dist/shared/api/portability-schemas.js +39 -0
- package/dist/shared/api/schemas.d.ts +295 -0
- package/dist/shared/api/schemas.js +134 -0
- package/dist/shared/api/types.d.ts +28 -0
- package/dist/shared/api/types.js +1 -0
- package/dist/types/index.d.ts +27 -0
- package/dist/types/index.js +1 -0
- package/dist/utils/context-limit.d.ts +10 -0
- package/dist/utils/context-limit.js +43 -0
- package/dist/utils/math.d.ts +7 -0
- package/dist/utils/math.js +48 -0
- package/dist/utils/profile.d.ts +2 -0
- package/dist/utils/profile.js +15 -0
- package/dist/v2/adapter.d.ts +5 -0
- package/dist/v2/adapter.js +218 -0
- package/dist/v2/legacy-client.d.ts +15 -0
- package/dist/v2/legacy-client.js +257 -0
- package/dist/v2/memory-bridge.d.ts +18 -0
- package/dist/v2/memory-bridge.js +29 -0
- package/dist/v2/plugin.d.ts +3 -0
- package/dist/v2/plugin.js +24 -0
- package/dist/web/assets/index-DPjEpD1h.css +2 -0
- package/dist/web/assets/index-do26c4JJ.js +122 -0
- package/dist/web/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2 +0 -0
- package/dist/web/favicon.ico +0 -0
- package/dist/web/index.html +14 -0
- package/dist/web/omms-icon.png +0 -0
- package/package.json +138 -0
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
import { existsSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { tursoShardManager } from "./turso/shard-manager.js";
|
|
3
|
+
import { tursoConnectionManager } from "./turso/connection-manager.js";
|
|
4
|
+
import { tursoVectorSearch } from "./turso/vector-search.js";
|
|
5
|
+
import { ensureTursoReady } from "./turso/ready.js";
|
|
6
|
+
import { embeddingService } from "./embedding.js";
|
|
7
|
+
import { CONFIG } from "../config.js";
|
|
8
|
+
import { log } from "./logger.js";
|
|
9
|
+
import { formatTagsForEmbedding } from "./turso/vector-utils.js";
|
|
10
|
+
import { acquireTursoOperationLock } from "./turso/operation-lock.js";
|
|
11
|
+
import { withSqliteFileLockRetry } from "./turso/sqlite-handle-release.js";
|
|
12
|
+
export class MigrationService {
|
|
13
|
+
isRunning = false;
|
|
14
|
+
progressCallback;
|
|
15
|
+
async detectDimensionMismatch() {
|
|
16
|
+
await ensureTursoReady();
|
|
17
|
+
const userShards = await tursoShardManager.getAllShards("user", "");
|
|
18
|
+
const projectShards = await tursoShardManager.getAllShards("project", "");
|
|
19
|
+
const allShards = [...userShards, ...projectShards];
|
|
20
|
+
const mismatches = [];
|
|
21
|
+
for (const shard of allShards) {
|
|
22
|
+
try {
|
|
23
|
+
const db = await tursoConnectionManager.getConnection(shard.dbPath);
|
|
24
|
+
const metadataResult = await db.all(`
|
|
25
|
+
SELECT key, value FROM shard_metadata
|
|
26
|
+
WHERE key IN ('embedding_dimensions', 'embedding_model')
|
|
27
|
+
`);
|
|
28
|
+
const metadata = Object.fromEntries(metadataResult.map((row) => [row.key, row.value]));
|
|
29
|
+
const storedDimensions = parseInt(String(metadata.embedding_dimensions || "0"));
|
|
30
|
+
const storedModel = String(metadata.embedding_model || "unknown");
|
|
31
|
+
if (storedDimensions !== CONFIG.embeddingDimensions) {
|
|
32
|
+
const vectorCount = await tursoVectorSearch.countAllVectors(db);
|
|
33
|
+
mismatches.push({
|
|
34
|
+
shardId: shard.id,
|
|
35
|
+
dbPath: shard.dbPath,
|
|
36
|
+
storedDimensions,
|
|
37
|
+
storedModel,
|
|
38
|
+
vectorCount,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
log("Migration: error checking shard", {
|
|
44
|
+
shardId: shard.id,
|
|
45
|
+
error: String(error),
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
needsMigration: mismatches.length > 0,
|
|
51
|
+
configDimensions: CONFIG.embeddingDimensions,
|
|
52
|
+
configModel: CONFIG.embeddingModel,
|
|
53
|
+
shardMismatches: mismatches,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
async migrateToNewModel(strategy, progressCallback) {
|
|
57
|
+
if (this.isRunning) {
|
|
58
|
+
throw new Error("Migration already running");
|
|
59
|
+
}
|
|
60
|
+
this.isRunning = true;
|
|
61
|
+
let releaseOperationLock;
|
|
62
|
+
this.progressCallback = progressCallback;
|
|
63
|
+
const startTime = Date.now();
|
|
64
|
+
try {
|
|
65
|
+
releaseOperationLock = acquireTursoOperationLock(`dimension-${strategy}`);
|
|
66
|
+
const mismatch = await this.detectDimensionMismatch();
|
|
67
|
+
if (!mismatch.needsMigration) {
|
|
68
|
+
return {
|
|
69
|
+
success: true,
|
|
70
|
+
strategy,
|
|
71
|
+
deletedShards: 0,
|
|
72
|
+
reEmbeddedMemories: 0,
|
|
73
|
+
duration: Date.now() - startTime,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
if (strategy === "fresh-start") {
|
|
77
|
+
return await this.freshStartMigration(mismatch, startTime);
|
|
78
|
+
}
|
|
79
|
+
return await this.reEmbedMigration(mismatch, startTime);
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
log("Migration: failed", { error: String(error) });
|
|
83
|
+
return {
|
|
84
|
+
success: false,
|
|
85
|
+
strategy,
|
|
86
|
+
deletedShards: 0,
|
|
87
|
+
reEmbeddedMemories: 0,
|
|
88
|
+
duration: Date.now() - startTime,
|
|
89
|
+
error: String(error),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
finally {
|
|
93
|
+
releaseOperationLock?.();
|
|
94
|
+
this.isRunning = false;
|
|
95
|
+
this.progressCallback = undefined;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
async freshStartMigration(mismatch, startTime) {
|
|
99
|
+
this.reportProgress({
|
|
100
|
+
phase: "preparing",
|
|
101
|
+
processed: 0,
|
|
102
|
+
total: mismatch.shardMismatches.length,
|
|
103
|
+
});
|
|
104
|
+
let deletedShards = 0;
|
|
105
|
+
for (const [index, shardInfo] of mismatch.shardMismatches.entries()) {
|
|
106
|
+
this.reportProgress({
|
|
107
|
+
phase: "cleanup",
|
|
108
|
+
processed: index,
|
|
109
|
+
total: mismatch.shardMismatches.length,
|
|
110
|
+
currentShard: String(shardInfo.shardId),
|
|
111
|
+
});
|
|
112
|
+
const archivePath = await tursoShardManager.archiveShard(shardInfo.shardId, "fresh-start");
|
|
113
|
+
if (!archivePath) {
|
|
114
|
+
throw new Error(`Migration source shard ${shardInfo.shardId} no longer exists`);
|
|
115
|
+
}
|
|
116
|
+
log("Migration: archived shard for fresh start", {
|
|
117
|
+
shardId: shardInfo.shardId,
|
|
118
|
+
archivePath,
|
|
119
|
+
});
|
|
120
|
+
deletedShards++;
|
|
121
|
+
}
|
|
122
|
+
this.reportProgress({
|
|
123
|
+
phase: "complete",
|
|
124
|
+
processed: mismatch.shardMismatches.length,
|
|
125
|
+
total: mismatch.shardMismatches.length,
|
|
126
|
+
});
|
|
127
|
+
return {
|
|
128
|
+
success: true,
|
|
129
|
+
strategy: "fresh-start",
|
|
130
|
+
deletedShards,
|
|
131
|
+
reEmbeddedMemories: 0,
|
|
132
|
+
duration: Date.now() - startTime,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
async reEmbedMigration(mismatch, startTime) {
|
|
136
|
+
await embeddingService.warmup();
|
|
137
|
+
embeddingService.clearCache();
|
|
138
|
+
const totalMemories = mismatch.shardMismatches.reduce((sum, shard) => sum + shard.vectorCount, 0);
|
|
139
|
+
this.reportProgress({
|
|
140
|
+
phase: "preparing",
|
|
141
|
+
processed: 0,
|
|
142
|
+
total: totalMemories,
|
|
143
|
+
});
|
|
144
|
+
let reEmbeddedCount = 0;
|
|
145
|
+
let processedCount = 0;
|
|
146
|
+
for (const shardInfo of mismatch.shardMismatches) {
|
|
147
|
+
this.reportProgress({
|
|
148
|
+
phase: "re-embedding",
|
|
149
|
+
processed: processedCount,
|
|
150
|
+
total: totalMemories,
|
|
151
|
+
currentShard: String(shardInfo.shardId),
|
|
152
|
+
});
|
|
153
|
+
const shard = await tursoShardManager.getShardById(shardInfo.shardId);
|
|
154
|
+
if (!shard) {
|
|
155
|
+
throw new Error(`Migration source shard ${shardInfo.shardId} no longer exists`);
|
|
156
|
+
}
|
|
157
|
+
const migrated = await tursoShardManager.withScopeWriteLock(shard.scope, shard.scopeHash, () => this.rebuildShardSafely(shard, (processed) => {
|
|
158
|
+
processedCount++;
|
|
159
|
+
this.reportProgress({
|
|
160
|
+
phase: "re-embedding",
|
|
161
|
+
processed: processedCount,
|
|
162
|
+
total: totalMemories,
|
|
163
|
+
currentShard: String(shardInfo.shardId),
|
|
164
|
+
});
|
|
165
|
+
log("Migration: memory staged for re-embed", {
|
|
166
|
+
shardId: shardInfo.shardId,
|
|
167
|
+
memoryId: processed,
|
|
168
|
+
});
|
|
169
|
+
}));
|
|
170
|
+
reEmbeddedCount += migrated;
|
|
171
|
+
}
|
|
172
|
+
this.reportProgress({
|
|
173
|
+
phase: "complete",
|
|
174
|
+
processed: totalMemories,
|
|
175
|
+
total: totalMemories,
|
|
176
|
+
});
|
|
177
|
+
return {
|
|
178
|
+
success: true,
|
|
179
|
+
strategy: "re-embed",
|
|
180
|
+
deletedShards: 0,
|
|
181
|
+
reEmbeddedMemories: reEmbeddedCount,
|
|
182
|
+
duration: Date.now() - startTime,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
async rebuildShardSafely(shard, onProcessed) {
|
|
186
|
+
const sourceDb = await tursoConnectionManager.getConnection(shard.dbPath);
|
|
187
|
+
const memories = await sourceDb.all(`
|
|
188
|
+
SELECT
|
|
189
|
+
id, content, container_tag, tags, type, created_at, updated_at, metadata,
|
|
190
|
+
display_name, user_name, user_email, project_path, project_name, git_repo_url, is_pinned
|
|
191
|
+
FROM memories
|
|
192
|
+
ORDER BY created_at DESC
|
|
193
|
+
`);
|
|
194
|
+
const nonce = `${process.pid}-${Date.now()}`;
|
|
195
|
+
const stagedPath = `${shard.dbPath}.reembed-${nonce}.tmp`;
|
|
196
|
+
const backupPath = `${shard.dbPath}.pre-reembed-${nonce}.bak`;
|
|
197
|
+
const swapStatePath = `${shard.dbPath}.reembed-swap.json`;
|
|
198
|
+
try {
|
|
199
|
+
const stagedDb = await tursoConnectionManager.getConnection(stagedPath);
|
|
200
|
+
await tursoShardManager.initShardDb(stagedDb);
|
|
201
|
+
// Stage bounded batches. Model or database failures only discard this
|
|
202
|
+
// temporary shard; the source is not touched until full verification.
|
|
203
|
+
const batchSize = 50;
|
|
204
|
+
for (let offset = 0; offset < memories.length; offset += batchSize) {
|
|
205
|
+
const stagedBatch = [];
|
|
206
|
+
for (const memory of memories.slice(offset, offset + batchSize)) {
|
|
207
|
+
const content = String(memory.content);
|
|
208
|
+
const tags = memory.tags
|
|
209
|
+
? String(memory.tags)
|
|
210
|
+
.split(",")
|
|
211
|
+
.map((tag) => tag.trim())
|
|
212
|
+
.filter(Boolean)
|
|
213
|
+
: [];
|
|
214
|
+
const embeddingInput = tags.length > 0 ? `${content}\nTags: ${tags.join(", ")}` : content;
|
|
215
|
+
const vector = await embeddingService.embedWithTimeout(embeddingInput, {
|
|
216
|
+
task: "document",
|
|
217
|
+
});
|
|
218
|
+
const tagsVector = tags.length > 0
|
|
219
|
+
? await embeddingService.embedWithTimeout(formatTagsForEmbedding(tags), {
|
|
220
|
+
task: "document",
|
|
221
|
+
})
|
|
222
|
+
: undefined;
|
|
223
|
+
stagedBatch.push({
|
|
224
|
+
record: {
|
|
225
|
+
id: String(memory.id),
|
|
226
|
+
content,
|
|
227
|
+
vector,
|
|
228
|
+
tagsVector,
|
|
229
|
+
containerTag: String(memory.container_tag),
|
|
230
|
+
tags: memory.tags ? String(memory.tags) : undefined,
|
|
231
|
+
type: memory.type ? String(memory.type) : undefined,
|
|
232
|
+
createdAt: Number(memory.created_at),
|
|
233
|
+
updatedAt: Number(memory.updated_at),
|
|
234
|
+
metadata: memory.metadata ? String(memory.metadata) : undefined,
|
|
235
|
+
displayName: memory.display_name ? String(memory.display_name) : undefined,
|
|
236
|
+
userName: memory.user_name ? String(memory.user_name) : undefined,
|
|
237
|
+
userEmail: memory.user_email ? String(memory.user_email) : undefined,
|
|
238
|
+
projectPath: memory.project_path ? String(memory.project_path) : undefined,
|
|
239
|
+
projectName: memory.project_name ? String(memory.project_name) : undefined,
|
|
240
|
+
gitRepoUrl: memory.git_repo_url ? String(memory.git_repo_url) : undefined,
|
|
241
|
+
},
|
|
242
|
+
isPinned: Number(memory.is_pinned ?? 0) === 1,
|
|
243
|
+
});
|
|
244
|
+
onProcessed(String(memory.id));
|
|
245
|
+
}
|
|
246
|
+
await stagedDb.transaction("write", async (tx) => {
|
|
247
|
+
for (const item of stagedBatch) {
|
|
248
|
+
await tursoVectorSearch.insertVectorInTransaction(tx, item.record);
|
|
249
|
+
if (item.isPinned) {
|
|
250
|
+
await tx.execute({
|
|
251
|
+
sql: `UPDATE memories SET is_pinned = 1 WHERE id = ?`,
|
|
252
|
+
args: [item.record.id],
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
const countRow = await stagedDb.get(`SELECT COUNT(*) AS count FROM memories`);
|
|
259
|
+
const stagedCount = Number(countRow?.count ?? 0);
|
|
260
|
+
if (stagedCount !== memories.length) {
|
|
261
|
+
throw new Error(`Migration staged count mismatch for shard ${shard.id}: expected ${memories.length}, got ${stagedCount}`);
|
|
262
|
+
}
|
|
263
|
+
await tursoConnectionManager.closeConnection(stagedPath);
|
|
264
|
+
writeFileSync(swapStatePath, JSON.stringify({ dbPath: shard.dbPath, stagedPath, backupPath }), "utf-8");
|
|
265
|
+
await tursoConnectionManager.closeConnection(shard.dbPath);
|
|
266
|
+
await withSqliteFileLockRetry(() => renameSync(shard.dbPath, backupPath));
|
|
267
|
+
try {
|
|
268
|
+
await withSqliteFileLockRetry(() => renameSync(stagedPath, shard.dbPath));
|
|
269
|
+
}
|
|
270
|
+
catch (error) {
|
|
271
|
+
await withSqliteFileLockRetry(() => renameSync(backupPath, shard.dbPath));
|
|
272
|
+
throw error;
|
|
273
|
+
}
|
|
274
|
+
await tursoShardManager.setVectorCount(shard.id, stagedCount);
|
|
275
|
+
unlinkSync(swapStatePath);
|
|
276
|
+
log("Migration: safely replaced re-embedded shard", {
|
|
277
|
+
shardId: shard.id,
|
|
278
|
+
memories: stagedCount,
|
|
279
|
+
backupPath,
|
|
280
|
+
});
|
|
281
|
+
return stagedCount;
|
|
282
|
+
}
|
|
283
|
+
catch (error) {
|
|
284
|
+
await tursoConnectionManager.closeConnection(stagedPath);
|
|
285
|
+
if (existsSync(stagedPath)) {
|
|
286
|
+
await withSqliteFileLockRetry(() => unlinkSync(stagedPath));
|
|
287
|
+
}
|
|
288
|
+
if (existsSync(swapStatePath) && existsSync(shard.dbPath)) {
|
|
289
|
+
unlinkSync(swapStatePath);
|
|
290
|
+
}
|
|
291
|
+
throw error;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
reportProgress(progress) {
|
|
295
|
+
if (this.progressCallback) {
|
|
296
|
+
this.progressCallback(progress);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
getStatus() {
|
|
300
|
+
return {
|
|
301
|
+
isRunning: this.isRunning,
|
|
302
|
+
configModel: CONFIG.embeddingModel,
|
|
303
|
+
configDimensions: CONFIG.embeddingDimensions,
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
export const migrationService = new MigrationService();
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export declare function getPinnedOnnxruntimePackageRoot(): string;
|
|
2
|
+
/**
|
|
3
|
+
* Resolve the N-API layout directory shipped by the pinned onnxruntime-node
|
|
4
|
+
* package (`napi-v3` for 1.20.x, `napi-v6` for 1.22.x, …).
|
|
5
|
+
*/
|
|
6
|
+
export declare function getOnnxruntimeNapiDirName(platform?: NodeJS.Platform, arch?: string): string;
|
|
7
|
+
export declare function getOnnxruntimeBindingPath(platform?: NodeJS.Platform, arch?: string): string;
|
|
8
|
+
export declare function formatMissingOnnxruntimeBindingError(platform?: NodeJS.Platform, arch?: string): string;
|
|
9
|
+
/**
|
|
10
|
+
* Rewrite onnxruntime-related init failures.
|
|
11
|
+
*
|
|
12
|
+
* When the pinned binding is absent, keep the clear "missing" message.
|
|
13
|
+
* When it is present, preserve the original error so nested-1.24 / dlopen /
|
|
14
|
+
* codesign failures are not misreported as a missing pinned binding (#210).
|
|
15
|
+
*/
|
|
16
|
+
export declare function formatOnnxruntimeInitError(error: unknown, platform?: NodeJS.Platform, arch?: string): Error;
|
|
17
|
+
export declare function assertOnnxruntimeBindingPresent(platform?: NodeJS.Platform, arch?: string): void;
|
|
18
|
+
/**
|
|
19
|
+
* Patch Module._resolveFilename so require() of onnxruntime-node / onnxruntime-common
|
|
20
|
+
* from nested transformers resolves to our direct pinned dependency stack.
|
|
21
|
+
*/
|
|
22
|
+
export declare function installOnnxruntimeResolveShim(): void;
|
|
23
|
+
/** Install resolve shim and fail fast if the platform binding is absent. */
|
|
24
|
+
export declare function prepareOnnxruntimeForTransformers(): void;
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Force resolution of `onnxruntime-node` (and its `onnxruntime-common`) to this
|
|
3
|
+
* package's direct dependency stack.
|
|
4
|
+
*
|
|
5
|
+
* OpenCode installs plugins nested under its own cache. npm/Arborist only honors
|
|
6
|
+
* `overrides` at the install root, so `@huggingface/transformers` would otherwise
|
|
7
|
+
* keep nested `onnxruntime-node@1.24.3` (no darwin/x64 binding). See #184 / #158 / #210.
|
|
8
|
+
*
|
|
9
|
+
* We pin onnxruntime-node@1.20.1:
|
|
10
|
+
* - 1.21.0–1.23.2 crash during macOS Ort::Env process-exit teardown (#225 /
|
|
11
|
+
* microsoft/onnxruntime#24579); the fix shipped in 1.24.1
|
|
12
|
+
* - fixed releases still lack darwin/x64 binaries (microsoft/onnxruntime#27961)
|
|
13
|
+
* - OpenCode's embedded Bun 1.3.14 surfaces the teardown failure as SIGILL
|
|
14
|
+
*
|
|
15
|
+
* Transformers must be loaded via its CJS export so this Module._resolveFilename
|
|
16
|
+
* shim applies; the ESM entry's static `import "onnxruntime-node"` bypasses it.
|
|
17
|
+
*/
|
|
18
|
+
import { createRequire } from "node:module";
|
|
19
|
+
import { existsSync, readdirSync } from "node:fs";
|
|
20
|
+
import { dirname, join } from "node:path";
|
|
21
|
+
import { createRuntimeRequire } from "./runtime-require.js";
|
|
22
|
+
const PACKAGE_NAME = "onnxruntime-node";
|
|
23
|
+
const COMMON_PACKAGE = "onnxruntime-common";
|
|
24
|
+
const PINNED_VERSION_HINT = "1.20.1";
|
|
25
|
+
const requireFromHere = createRuntimeRequire(import.meta);
|
|
26
|
+
let shimInstalled = false;
|
|
27
|
+
let pinnedPackageRoot = null;
|
|
28
|
+
let pinnedNodeEntry = null;
|
|
29
|
+
let pinnedCommonEntry = null;
|
|
30
|
+
function getPinnedOnnxruntimeEntry() {
|
|
31
|
+
if (pinnedNodeEntry)
|
|
32
|
+
return pinnedNodeEntry;
|
|
33
|
+
pinnedNodeEntry = requireFromHere.resolve(PACKAGE_NAME);
|
|
34
|
+
return pinnedNodeEntry;
|
|
35
|
+
}
|
|
36
|
+
function getPinnedOnnxruntimeCommonEntry() {
|
|
37
|
+
if (pinnedCommonEntry)
|
|
38
|
+
return pinnedCommonEntry;
|
|
39
|
+
// Resolve common from the pinned node package so we always get the pinned stack,
|
|
40
|
+
// whether the package manager hoists it or nests it under onnxruntime-node.
|
|
41
|
+
pinnedCommonEntry = createRequire(getPinnedOnnxruntimeEntry()).resolve(COMMON_PACKAGE);
|
|
42
|
+
return pinnedCommonEntry;
|
|
43
|
+
}
|
|
44
|
+
export function getPinnedOnnxruntimePackageRoot() {
|
|
45
|
+
if (pinnedPackageRoot)
|
|
46
|
+
return pinnedPackageRoot;
|
|
47
|
+
const entry = getPinnedOnnxruntimeEntry();
|
|
48
|
+
// package entry is typically …/dist/index.js — walk up to package root
|
|
49
|
+
let dir = dirname(entry);
|
|
50
|
+
for (let i = 0; i < 4; i++) {
|
|
51
|
+
if (existsSync(join(dir, "package.json"))) {
|
|
52
|
+
pinnedPackageRoot = dir;
|
|
53
|
+
return dir;
|
|
54
|
+
}
|
|
55
|
+
dir = dirname(dir);
|
|
56
|
+
}
|
|
57
|
+
pinnedPackageRoot = dirname(entry);
|
|
58
|
+
return pinnedPackageRoot;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Resolve the N-API layout directory shipped by the pinned onnxruntime-node
|
|
62
|
+
* package (`napi-v3` for 1.20.x, `napi-v6` for 1.22.x, …).
|
|
63
|
+
*/
|
|
64
|
+
export function getOnnxruntimeNapiDirName(platform = process.platform, arch = process.arch) {
|
|
65
|
+
const binDir = join(getPinnedOnnxruntimePackageRoot(), "bin");
|
|
66
|
+
if (!existsSync(binDir))
|
|
67
|
+
return "napi-v3";
|
|
68
|
+
let entries;
|
|
69
|
+
try {
|
|
70
|
+
entries = readdirSync(binDir, { withFileTypes: true })
|
|
71
|
+
.filter((entry) => entry.isDirectory() && /^napi-v\d+$/.test(entry.name))
|
|
72
|
+
.map((entry) => entry.name)
|
|
73
|
+
.sort((a, b) => Number(a.slice("napi-v".length)) - Number(b.slice("napi-v".length)));
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return "napi-v3";
|
|
77
|
+
}
|
|
78
|
+
// Prefer a layout that actually contains the platform binding.
|
|
79
|
+
for (let i = entries.length - 1; i >= 0; i--) {
|
|
80
|
+
const name = entries[i];
|
|
81
|
+
if (existsSync(join(binDir, name, platform, arch, "onnxruntime_binding.node"))) {
|
|
82
|
+
return name;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return entries.at(-1) ?? "napi-v3";
|
|
86
|
+
}
|
|
87
|
+
export function getOnnxruntimeBindingPath(platform = process.platform, arch = process.arch) {
|
|
88
|
+
return join(getPinnedOnnxruntimePackageRoot(), "bin", getOnnxruntimeNapiDirName(platform, arch), platform, arch, "onnxruntime_binding.node");
|
|
89
|
+
}
|
|
90
|
+
export function formatMissingOnnxruntimeBindingError(platform = process.platform, arch = process.arch) {
|
|
91
|
+
const bindingPath = getOnnxruntimeBindingPath(platform, arch);
|
|
92
|
+
const intelHint = platform === "darwin" && arch === "x64"
|
|
93
|
+
? ` On Intel Mac (darwin/x64), onnxruntime-node@1.21.0–1.23.2 can crash Bun 1.3.14 on process exit (#225), while fixed releases still lack an x64 binding; omms pins ${PINNED_VERSION_HINT}. If this persists after updating, clear OpenCode's plugin cache (~/.cache/opencode/packages/om-memory-system@*, or opencode-mem@* on pre-migration installs) and reinstall, or configure remote embeddings via embeddingApiUrl + embeddingApiKey.`
|
|
94
|
+
: ` Configure remote embeddings via embeddingApiUrl + embeddingApiKey, or reinstall the plugin so onnxruntime-node@${PINNED_VERSION_HINT} is used.`;
|
|
95
|
+
return `Local embedding native binding missing for ${platform}/${arch} at ${bindingPath}.${intelHint}`;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Rewrite onnxruntime-related init failures.
|
|
99
|
+
*
|
|
100
|
+
* When the pinned binding is absent, keep the clear "missing" message.
|
|
101
|
+
* When it is present, preserve the original error so nested-1.24 / dlopen /
|
|
102
|
+
* codesign failures are not misreported as a missing pinned binding (#210).
|
|
103
|
+
*/
|
|
104
|
+
export function formatOnnxruntimeInitError(error, platform = process.platform, arch = process.arch) {
|
|
105
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
106
|
+
const isOnnxRelated = message.includes("onnxruntime_binding.node") ||
|
|
107
|
+
message.includes("onnxruntime-node") ||
|
|
108
|
+
message.includes("onnxruntime-common") ||
|
|
109
|
+
/napi-v\d+\/[^/]+\/[^/]+/.test(message);
|
|
110
|
+
if (!isOnnxRelated) {
|
|
111
|
+
return error instanceof Error ? error : new Error(message);
|
|
112
|
+
}
|
|
113
|
+
const bindingPath = getOnnxruntimeBindingPath(platform, arch);
|
|
114
|
+
if (!existsSync(bindingPath)) {
|
|
115
|
+
return new Error(formatMissingOnnxruntimeBindingError(platform, arch), { cause: error });
|
|
116
|
+
}
|
|
117
|
+
const intelHint = platform === "darwin" && arch === "x64"
|
|
118
|
+
? ` On Intel Mac nested installs, @huggingface/transformers may resolve onnxruntime-node@1.24+ (no x64 binding); omms pins ${PINNED_VERSION_HINT} via a CJS resolve shim.`
|
|
119
|
+
: "";
|
|
120
|
+
return new Error(`ONNX runtime failed to load despite pinned binding being present at ${bindingPath}. Original error: ${message}.${intelHint} If this persists after updating, clear OpenCode's plugin cache (~/.cache/opencode/packages/om-memory-system@*, or opencode-mem@* on pre-migration installs) and reinstall, or configure remote embeddings via embeddingApiUrl + embeddingApiKey.`, { cause: error });
|
|
121
|
+
}
|
|
122
|
+
export function assertOnnxruntimeBindingPresent(platform = process.platform, arch = process.arch) {
|
|
123
|
+
const bindingPath = getOnnxruntimeBindingPath(platform, arch);
|
|
124
|
+
if (!existsSync(bindingPath)) {
|
|
125
|
+
throw new Error(formatMissingOnnxruntimeBindingError(platform, arch));
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
function resolvePinnedRequest(request, packageName, pinnedEntry) {
|
|
129
|
+
if (request !== packageName && !request.startsWith(`${packageName}/`)) {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
try {
|
|
133
|
+
if (packageName === PACKAGE_NAME) {
|
|
134
|
+
return requireFromHere.resolve(request);
|
|
135
|
+
}
|
|
136
|
+
return createRequire(getPinnedOnnxruntimeEntry()).resolve(request);
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
if (request === packageName)
|
|
140
|
+
return pinnedEntry;
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Patch Module._resolveFilename so require() of onnxruntime-node / onnxruntime-common
|
|
146
|
+
* from nested transformers resolves to our direct pinned dependency stack.
|
|
147
|
+
*/
|
|
148
|
+
export function installOnnxruntimeResolveShim() {
|
|
149
|
+
if (shimInstalled)
|
|
150
|
+
return;
|
|
151
|
+
// Resolve our pin first so a missing direct dep fails before transformers loads.
|
|
152
|
+
const pinnedEntry = getPinnedOnnxruntimeEntry();
|
|
153
|
+
const pinnedCommon = getPinnedOnnxruntimeCommonEntry();
|
|
154
|
+
const Module = requireFromHere("node:module");
|
|
155
|
+
const original = Module._resolveFilename;
|
|
156
|
+
Module._resolveFilename = function (request, parent, isMain, options) {
|
|
157
|
+
// Only intercept the onnx stack. Forwarding every other request through a
|
|
158
|
+
// wrapped _resolveFilename is required, but must not alter Bun --compile
|
|
159
|
+
// parent handling for unrelated packages (#210 follow-up).
|
|
160
|
+
const pinnedNode = resolvePinnedRequest(request, PACKAGE_NAME, pinnedEntry);
|
|
161
|
+
if (pinnedNode)
|
|
162
|
+
return pinnedNode;
|
|
163
|
+
const pinnedCommonResolved = resolvePinnedRequest(request, COMMON_PACKAGE, pinnedCommon);
|
|
164
|
+
if (pinnedCommonResolved)
|
|
165
|
+
return pinnedCommonResolved;
|
|
166
|
+
return original.call(this, request, parent, isMain, options);
|
|
167
|
+
};
|
|
168
|
+
shimInstalled = true;
|
|
169
|
+
}
|
|
170
|
+
/** Install resolve shim and fail fast if the platform binding is absent. */
|
|
171
|
+
export function prepareOnnxruntimeForTransformers() {
|
|
172
|
+
installOnnxruntimeResolveShim();
|
|
173
|
+
assertOnnxruntimeBindingPresent();
|
|
174
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Replace every `<private>…</private>` region with `[REDACTED]`.
|
|
3
|
+
*
|
|
4
|
+
* Scans tags with a depth counter rather than matching pairs with a single
|
|
5
|
+
* regex, so that the two malformed shapes fail *closed*:
|
|
6
|
+
*
|
|
7
|
+
* - An **unclosed** `<private>` redacts to the end of the input. A non-greedy
|
|
8
|
+
* pair match found no closing tag and left the region untouched, so a typo or
|
|
9
|
+
* a truncated message stored the content verbatim.
|
|
10
|
+
* - **Nested** tags close at the outer tag, not the first inner one. Pair
|
|
11
|
+
* matching ended the region at the inner `</private>`, releasing the rest of
|
|
12
|
+
* the outer region and leaving a stray `</private>` in the output.
|
|
13
|
+
*
|
|
14
|
+
* Erring toward redaction is the only safe direction here: the caller persists
|
|
15
|
+
* this string, so a wrong guess in the other direction is an unrecoverable
|
|
16
|
+
* disclosure, while over-redacting merely loses text the user can retype.
|
|
17
|
+
*
|
|
18
|
+
* A `</private>` with no opener is markup rather than content, so it is dropped
|
|
19
|
+
* instead of being surfaced to the user as if it were part of their text.
|
|
20
|
+
*/
|
|
21
|
+
export declare function stripPrivateContent(content: string): string;
|
|
22
|
+
/**
|
|
23
|
+
* True when nothing survives redaction — the caller refuses such a write rather
|
|
24
|
+
* than storing a message that is private in its entirety.
|
|
25
|
+
*/
|
|
26
|
+
export declare function isFullyPrivate(content: string): boolean;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
const REDACTED = "[REDACTED]";
|
|
2
|
+
// `<private>` / `</private>`, case-insensitive, tolerating whitespace inside the
|
|
3
|
+
// tag (`<private >`) the way an XML parser would.
|
|
4
|
+
const PRIVATE_TAG = /<(\/?)private\s*>/gi;
|
|
5
|
+
/**
|
|
6
|
+
* Replace every `<private>…</private>` region with `[REDACTED]`.
|
|
7
|
+
*
|
|
8
|
+
* Scans tags with a depth counter rather than matching pairs with a single
|
|
9
|
+
* regex, so that the two malformed shapes fail *closed*:
|
|
10
|
+
*
|
|
11
|
+
* - An **unclosed** `<private>` redacts to the end of the input. A non-greedy
|
|
12
|
+
* pair match found no closing tag and left the region untouched, so a typo or
|
|
13
|
+
* a truncated message stored the content verbatim.
|
|
14
|
+
* - **Nested** tags close at the outer tag, not the first inner one. Pair
|
|
15
|
+
* matching ended the region at the inner `</private>`, releasing the rest of
|
|
16
|
+
* the outer region and leaving a stray `</private>` in the output.
|
|
17
|
+
*
|
|
18
|
+
* Erring toward redaction is the only safe direction here: the caller persists
|
|
19
|
+
* this string, so a wrong guess in the other direction is an unrecoverable
|
|
20
|
+
* disclosure, while over-redacting merely loses text the user can retype.
|
|
21
|
+
*
|
|
22
|
+
* A `</private>` with no opener is markup rather than content, so it is dropped
|
|
23
|
+
* instead of being surfaced to the user as if it were part of their text.
|
|
24
|
+
*/
|
|
25
|
+
export function stripPrivateContent(content) {
|
|
26
|
+
if (!content.includes("<")) {
|
|
27
|
+
return content;
|
|
28
|
+
}
|
|
29
|
+
let result = "";
|
|
30
|
+
let depth = 0;
|
|
31
|
+
let cursor = 0;
|
|
32
|
+
PRIVATE_TAG.lastIndex = 0;
|
|
33
|
+
let match;
|
|
34
|
+
while ((match = PRIVATE_TAG.exec(content)) !== null) {
|
|
35
|
+
const isClosing = match[1] === "/";
|
|
36
|
+
if (!isClosing) {
|
|
37
|
+
if (depth === 0) {
|
|
38
|
+
result += content.slice(cursor, match.index);
|
|
39
|
+
}
|
|
40
|
+
depth++;
|
|
41
|
+
cursor = PRIVATE_TAG.lastIndex;
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
if (depth === 0) {
|
|
45
|
+
// Stray closing tag: emit the text before it, drop the tag itself.
|
|
46
|
+
result += content.slice(cursor, match.index);
|
|
47
|
+
cursor = PRIVATE_TAG.lastIndex;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
depth--;
|
|
51
|
+
if (depth === 0) {
|
|
52
|
+
result += REDACTED;
|
|
53
|
+
}
|
|
54
|
+
cursor = PRIVATE_TAG.lastIndex;
|
|
55
|
+
}
|
|
56
|
+
if (depth > 0) {
|
|
57
|
+
// Unclosed region: everything from the opening tag onward stays private.
|
|
58
|
+
return result + REDACTED;
|
|
59
|
+
}
|
|
60
|
+
return result + content.slice(cursor);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* True when nothing survives redaction — the caller refuses such a write rather
|
|
64
|
+
* than storing a message that is private in its entirety.
|
|
65
|
+
*/
|
|
66
|
+
export function isFullyPrivate(content) {
|
|
67
|
+
const stripped = stripPrivateContent(content).trim();
|
|
68
|
+
return stripped === REDACTED || stripped === "";
|
|
69
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare const EXTERNAL_PROFILE_CLEANUP_TIMEOUT_MS = 60000;
|
|
2
|
+
export declare const OPENCODE_PROFILE_CLEANUP_TIMEOUT_MS = 300000;
|
|
3
|
+
export declare const NODE_HTTP_IDLE_TIMEOUT_MS: number;
|
|
4
|
+
/**
|
|
5
|
+
* Upper bound an OpenCode v2 model request waits for the per-prompt memory
|
|
6
|
+
* search started in the `prompt` hook. On timeout the request proceeds
|
|
7
|
+
* without memory rather than stalling the agent.
|
|
8
|
+
*/
|
|
9
|
+
export declare const V2_RETRIEVAL_TIMEOUT_MS = 5000;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export const EXTERNAL_PROFILE_CLEANUP_TIMEOUT_MS = 60_000;
|
|
2
|
+
export const OPENCODE_PROFILE_CLEANUP_TIMEOUT_MS = 300_000;
|
|
3
|
+
const HTTP_TIMEOUT_GRACE_MS = 30_000;
|
|
4
|
+
export const NODE_HTTP_IDLE_TIMEOUT_MS = Math.max(EXTERNAL_PROFILE_CLEANUP_TIMEOUT_MS, OPENCODE_PROFILE_CLEANUP_TIMEOUT_MS) +
|
|
5
|
+
HTTP_TIMEOUT_GRACE_MS;
|
|
6
|
+
/**
|
|
7
|
+
* Upper bound an OpenCode v2 model request waits for the per-prompt memory
|
|
8
|
+
* search started in the `prompt` hook. On timeout the request proceeds
|
|
9
|
+
* without memory rather than stalling the agent.
|
|
10
|
+
*/
|
|
11
|
+
export const V2_RETRIEVAL_TIMEOUT_MS = 5_000;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type RuntimeImportMeta = ImportMeta & {
|
|
2
|
+
require?: NodeRequire;
|
|
3
|
+
path?: string;
|
|
4
|
+
dirname?: string;
|
|
5
|
+
dir?: string;
|
|
6
|
+
resolve?: (specifier: string) => string;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Build a require() for resolving/loading packages next to this plugin module.
|
|
10
|
+
* Throws a concrete diagnostic when no usable Bun/Node anchor exists.
|
|
11
|
+
*
|
|
12
|
+
* Prefer a path/url-anchored createRequire over Bun's import.meta.require when a
|
|
13
|
+
* concrete file anchor exists. In OpenCode's Bun --compile host, import.meta.url
|
|
14
|
+
* and import.meta.path are still valid for dynamically imported plugins even when
|
|
15
|
+
* some require referrers later surface as `from ''` (#210).
|
|
16
|
+
*/
|
|
17
|
+
export declare function createRuntimeRequire(meta: RuntimeImportMeta): NodeRequire;
|