codemem 0.25.2 → 0.26.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/dist/index.js +37 -35
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { DEFAULT_COORDINATOR_DB_PATH, DedupKeyBackfillRunner, MemoryStore, ObserverClient, RawEventSweeper, SyncRetentionRunner, VERSION, VectorModelMigrationRunner, aiBackfillStructuredContent, applyBootstrapSnapshot, backfillMemoryDedupKeys, backfillNarrativeFromBody, backfillTagsText, backfillVectors, buildAuthHeaders, buildBaseUrl, buildRawEventEnvelopeFromHook, compareMemoryRoleReports, connect, coordinatorCreateGroupAction, coordinatorCreateInviteAction, coordinatorDisableDeviceAction, coordinatorEnrollDeviceAction, coordinatorImportInviteAction, coordinatorListBootstrapGrantsAction, coordinatorListDevicesAction, coordinatorListGroupsAction, coordinatorListJoinRequestsAction, coordinatorRemoveDeviceAction, coordinatorRenameDeviceAction, coordinatorReviewJoinRequestAction, coordinatorRevokeBootstrapGrantAction, createBetterSqliteCoordinatorApp, deactivateLowSignalMemories, deactivateLowSignalObservations, dedupNearDuplicateMemories, ensureDeviceIdentity, exportMemories, fetchAllSnapshotPages, fingerprintPublicKey, flushRawEvents, getExtractionBenchmarkProfile, getInjectionEvalScenarioPack, getInjectionEvalScenarioPrompts, getMemoryRoleReport, getRawEventRelinkPlan, getRawEventRelinkReport, getRawEventStatus, getSessionExtractionEval, getSessionExtractionEvalScenario, getWorkspaceCodememConfigPath, hasPendingDedupKeyBackfill, hasUnsyncedSharedMemoryChanges, importMemories, initDatabase, isEmbeddingDisabled, loadObserverConfig, loadPublicKey, loadSqliteVec, planReplicationOpsAgePrune, pruneReplicationOpsUntilCaughtUp, rawEventsGate, readCodememConfigFile, readCodememConfigFileAtPath, readCoordinatorSyncConfig, readImportPayload, replayBatchExtraction, replayBatchExtractionWithTierRouting, requestJson, resolveCodememConfigPath, resolveDbPath, resolveHookProject, resolveProject, retryRawEventFailures, runSyncDaemon, runSyncPass, schema, setPeerProjectFilter, stripJsonComments, stripPrivateObj, stripTrailingCommas, syncPassPreflight, updatePeerAddresses, vacuumDatabase, writeCodememConfigFile } from "@codemem/core";
|
|
2
|
+
import { DEFAULT_COORDINATOR_DB_PATH, DedupKeyBackfillRunner, MUTATING_TOOL_NAMES, MemoryStore, ObserverClient, RawEventSweeper, RefBackfillRunner, SessionContextBackfillRunner, SyncRetentionRunner, VERSION, VectorModelMigrationRunner, aiBackfillStructuredContent, applyBootstrapSnapshot, backfillMemoryDedupKeys, backfillNarrativeFromBody, backfillTagsText, backfillVectors, buildAuthHeaders, buildBaseUrl, buildRawEventEnvelopeFromHook, compareMemoryRoleReports, connect, coordinatorCreateGroupAction, coordinatorCreateInviteAction, coordinatorDisableDeviceAction, coordinatorEnrollDeviceAction, coordinatorImportInviteAction, coordinatorListBootstrapGrantsAction, coordinatorListDevicesAction, coordinatorListGroupsAction, coordinatorListJoinRequestsAction, coordinatorRemoveDeviceAction, coordinatorRenameDeviceAction, coordinatorReviewJoinRequestAction, coordinatorRevokeBootstrapGrantAction, createBetterSqliteCoordinatorApp, deactivateLowSignalMemories, deactivateLowSignalObservations, dedupNearDuplicateMemories, ensureDeviceIdentity, ensureSchemaBootstrapped, exportMemories, extractApplyPatchPaths, fetchAllSnapshotPages, fingerprintPublicKey, flushRawEvents, getExtractionBenchmarkProfile, getInjectionEvalScenarioPack, getInjectionEvalScenarioPrompts, getMemoryRoleReport, getRawEventRelinkPlan, getRawEventRelinkReport, getRawEventStatus, getSemanticIndexDiagnostics, getSessionExtractionEval, getSessionExtractionEvalScenario, getWorkspaceCodememConfigPath, hasPendingDedupKeyBackfill, hasPendingRefBackfill, hasPendingSessionContextBackfill, hasUnsyncedSharedMemoryChanges, importMemories, initDatabase, isEmbeddingDisabled, loadObserverConfig, loadPublicKey, loadSqliteVec, planReplicationOpsAgePrune, pruneReplicationOpsUntilCaughtUp, rawEventsGate, readCodememConfigFile, readCodememConfigFileAtPath, readCoordinatorSyncConfig, readImportPayload, replayBatchExtraction, replayBatchExtractionWithTierRouting, requestJson, resolveCodememConfigPath, resolveDbPath, resolveHookProject, resolveProject, retryRawEventFailures, runSyncDaemon, runSyncPass, schema, setPeerProjectFilter, stripJsonComments, stripPrivateObj, stripTrailingCommas, syncPassPreflight, updatePeerAddresses, vacuumDatabase, writeCodememConfigFile } from "@codemem/core";
|
|
3
3
|
import { Command, Option } from "commander";
|
|
4
4
|
import omelette from "omelette";
|
|
5
5
|
import { appendFileSync, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, rmdirSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
@@ -536,18 +536,6 @@ var MAX_FILES_MODIFIED = 64;
|
|
|
536
536
|
var MAX_WORKING_SET_PATHS = 8;
|
|
537
537
|
var MAX_QUERY_CHARS = 500;
|
|
538
538
|
var MAX_QUERY_FILE_BASENAMES = 5;
|
|
539
|
-
var MUTATING_TOOL_NAMES = new Set([
|
|
540
|
-
"edit",
|
|
541
|
-
"write",
|
|
542
|
-
"multiedit",
|
|
543
|
-
"notebookedit",
|
|
544
|
-
"apply_patch"
|
|
545
|
-
]);
|
|
546
|
-
var APPLY_PATCH_PATH_PREFIXES = [
|
|
547
|
-
"*** Add File: ",
|
|
548
|
-
"*** Update File: ",
|
|
549
|
-
"*** Delete File: "
|
|
550
|
-
];
|
|
551
539
|
function defaultSessionState() {
|
|
552
540
|
return {
|
|
553
541
|
first_prompt: "",
|
|
@@ -629,14 +617,6 @@ function clearSessionState(sessionId) {
|
|
|
629
617
|
rmSync(path, { force: true });
|
|
630
618
|
} catch {}
|
|
631
619
|
}
|
|
632
|
-
function extractApplyPatchPaths(patchText) {
|
|
633
|
-
const out = [];
|
|
634
|
-
for (const line of patchText.split("\n")) for (const prefix of APPLY_PATCH_PATH_PREFIXES) if (line.startsWith(prefix)) {
|
|
635
|
-
const path = line.slice(prefix.length).trim();
|
|
636
|
-
if (path) out.push(path);
|
|
637
|
-
}
|
|
638
|
-
return out;
|
|
639
|
-
}
|
|
640
620
|
function extractModifiedPathsFromHook(payload) {
|
|
641
621
|
const toolName = String(payload.tool_name ?? "").trim().toLowerCase();
|
|
642
622
|
if (!MUTATING_TOOL_NAMES.has(toolName)) return [];
|
|
@@ -771,11 +751,19 @@ function emitStructuredError$1(errorCode, message) {
|
|
|
771
751
|
}
|
|
772
752
|
/** Try to POST the hook payload to the running viewer server.
|
|
773
753
|
*
|
|
774
|
-
* Returns `ok: true`
|
|
775
|
-
*
|
|
776
|
-
*
|
|
777
|
-
*
|
|
778
|
-
*
|
|
754
|
+
* Returns `ok: true` whenever the viewer accepts the request and
|
|
755
|
+
* returns a well-shaped JSON body with numeric `inserted` / `skipped`
|
|
756
|
+
* fields. That includes the `{inserted: 0, skipped: 1}` response the
|
|
757
|
+
* viewer emits when the payload maps to a null envelope (Stop with no
|
|
758
|
+
* assistant text, UserPromptSubmit with empty prompt, etc.) — that
|
|
759
|
+
* determination is deterministic, so retrying via the direct fallback
|
|
760
|
+
* would produce the exact same null envelope and the same skip. We
|
|
761
|
+
* accept those as benign no-ops instead of triggering the durability
|
|
762
|
+
* dance pointlessly.
|
|
763
|
+
*
|
|
764
|
+
* If a future server change adds a new `skipped` reason that IS
|
|
765
|
+
* transient, we'll need a reason field in the response and updated
|
|
766
|
+
* client handling — not an unconditional fail-over.
|
|
779
767
|
*/
|
|
780
768
|
async function tryHttpIngest(payload, host, port) {
|
|
781
769
|
const url = `http://${host}:${port}/api/claude-hooks`;
|
|
@@ -821,14 +809,6 @@ async function tryHttpIngest(payload, host, port) {
|
|
|
821
809
|
skipped: 0
|
|
822
810
|
};
|
|
823
811
|
}
|
|
824
|
-
if (obj.skipped > 0) {
|
|
825
|
-
logHookFailure("codemem claude-hook-ingest HTTP accepted but skipped payload");
|
|
826
|
-
return {
|
|
827
|
-
ok: false,
|
|
828
|
-
inserted: obj.inserted,
|
|
829
|
-
skipped: obj.skipped
|
|
830
|
-
};
|
|
831
|
-
}
|
|
832
812
|
return {
|
|
833
813
|
ok: true,
|
|
834
814
|
inserted: obj.inserted,
|
|
@@ -856,6 +836,7 @@ function directEnqueue(payload, dbPath) {
|
|
|
856
836
|
try {
|
|
857
837
|
loadSqliteVec(db);
|
|
858
838
|
} catch {}
|
|
839
|
+
ensureSchemaBootstrapped(db);
|
|
859
840
|
const strippedPayload = stripPrivateObj(envelope.payload);
|
|
860
841
|
if (db.prepare("SELECT 1 FROM raw_events WHERE source = ? AND stream_id = ? AND event_id = ? LIMIT 1").get(envelope.source, envelope.session_stream_id, envelope.event_id)) return {
|
|
861
842
|
inserted: 0,
|
|
@@ -3460,7 +3441,7 @@ function resolveLegacyServeInvocation(opts) {
|
|
|
3460
3441
|
configPath: opts.config ?? null,
|
|
3461
3442
|
host: opts.host,
|
|
3462
3443
|
port: parsePort(opts.port),
|
|
3463
|
-
background: opts.restart
|
|
3444
|
+
background: Boolean(opts.restart || opts.background)
|
|
3464
3445
|
};
|
|
3465
3446
|
}
|
|
3466
3447
|
function resolveServeInvocation(action, opts) {
|
|
@@ -3791,6 +3772,8 @@ async function startForegroundViewer(invocation) {
|
|
|
3791
3772
|
});
|
|
3792
3773
|
const vectorMigrationRunner = new VectorModelMigrationRunner({ dbPath: resolveDbPath(invocation.dbPath ?? void 0) });
|
|
3793
3774
|
const dedupKeyBackfillRunner = new DedupKeyBackfillRunner({ dbPath: resolveDbPath(invocation.dbPath ?? void 0) });
|
|
3775
|
+
const sessionContextBackfillRunner = new SessionContextBackfillRunner({ dbPath: resolveDbPath(invocation.dbPath ?? void 0) });
|
|
3776
|
+
const refBackfillRunner = new RefBackfillRunner({ dbPath: resolveDbPath(invocation.dbPath ?? void 0) });
|
|
3794
3777
|
const syncRuntimeStatus = {
|
|
3795
3778
|
phase: syncEnabled ? "starting" : "disabled",
|
|
3796
3779
|
detail: syncEnabled ? "Waiting for viewer startup to finish" : "Sync is disabled"
|
|
@@ -3841,6 +3824,14 @@ async function startForegroundViewer(invocation) {
|
|
|
3841
3824
|
dedupKeyBackfillRunner.start();
|
|
3842
3825
|
p.log.step("Dedup-key maintenance runner started");
|
|
3843
3826
|
}
|
|
3827
|
+
if (hasPendingSessionContextBackfill(store.db)) {
|
|
3828
|
+
sessionContextBackfillRunner.start();
|
|
3829
|
+
p.log.step("Session-context backfill runner started");
|
|
3830
|
+
}
|
|
3831
|
+
if (hasPendingRefBackfill(store.db)) {
|
|
3832
|
+
refBackfillRunner.start();
|
|
3833
|
+
p.log.step("Ref backfill runner started");
|
|
3834
|
+
}
|
|
3844
3835
|
if (syncConfig.syncRetentionEnabled) {
|
|
3845
3836
|
retentionRunner.start();
|
|
3846
3837
|
p.log.step("Retention maintenance runner started");
|
|
@@ -3899,6 +3890,8 @@ async function startForegroundViewer(invocation) {
|
|
|
3899
3890
|
syncAbort.abort();
|
|
3900
3891
|
retentionAbort.abort();
|
|
3901
3892
|
await sweeper.stop();
|
|
3893
|
+
await sessionContextBackfillRunner.stop();
|
|
3894
|
+
await refBackfillRunner.stop();
|
|
3902
3895
|
await dedupKeyBackfillRunner.stop();
|
|
3903
3896
|
await vectorMigrationRunner.stop();
|
|
3904
3897
|
await retentionRunner.stop();
|
|
@@ -4821,12 +4814,14 @@ statusCmd.action((opts) => {
|
|
|
4821
4814
|
last_sync_at: schema.syncPeers.last_sync_at,
|
|
4822
4815
|
last_error: schema.syncPeers.last_error
|
|
4823
4816
|
}).from(schema.syncPeers).all();
|
|
4817
|
+
const semanticIndex = getSemanticIndexDiagnostics(store.db);
|
|
4824
4818
|
if (opts.json) {
|
|
4825
4819
|
console.log(JSON.stringify({
|
|
4826
4820
|
enabled: config.sync_enabled === true,
|
|
4827
4821
|
host: config.sync_host ?? "0.0.0.0",
|
|
4828
4822
|
port: config.sync_port ?? 7337,
|
|
4829
4823
|
interval_s: config.sync_interval_s ?? 120,
|
|
4824
|
+
semantic_index: semanticIndex,
|
|
4830
4825
|
device_id: deviceRow?.device_id ?? null,
|
|
4831
4826
|
fingerprint: deviceRow?.fingerprint ?? null,
|
|
4832
4827
|
coordinator_url: config.sync_coordinator_url ?? null,
|
|
@@ -4854,6 +4849,13 @@ statusCmd.action((opts) => {
|
|
|
4854
4849
|
const label = peer.name || peer.peer_device_id;
|
|
4855
4850
|
p.log.message(` ${label}: last_sync=${peer.last_sync_at ?? "never"}, status=${peer.last_error ?? "ok"}`);
|
|
4856
4851
|
}
|
|
4852
|
+
p.log.info([
|
|
4853
|
+
"Semantic index:",
|
|
4854
|
+
` State: ${semanticIndex.state}`,
|
|
4855
|
+
` Summary: ${semanticIndex.summary}`,
|
|
4856
|
+
` Coverage: ${semanticIndex.indexed_memory_count}/${semanticIndex.embeddable_memory_count}`,
|
|
4857
|
+
` Mode: ${semanticIndex.mode === "semantic" ? `semantic (${semanticIndex.semantic_search_model ?? semanticIndex.current_model})` : "keyword-only"}`
|
|
4858
|
+
].join("\n"));
|
|
4857
4859
|
p.outro(`${peers.length} peer(s)`);
|
|
4858
4860
|
} finally {
|
|
4859
4861
|
store.close();
|