codemem 0.40.0-alpha.1 → 0.40.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +422 -52
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { DEDUP_KEY_BACKFILL_JOB, DEFAULT_COORDINATOR_DB_PATH, DedupKeyBackfillRunner, MUTATING_TOOL_NAMES, MemoryStore, ObserverClient, REF_BACKFILL_JOB, RawEventSweeper, RefBackfillRunner, SCOPE_BACKFILL_JOB, SESSION_CONTEXT_BACKFILL_JOB, SUMMARY_DEDUP_BACKFILL_JOB, ScopeBackfillRunner, SessionContextBackfillRunner, SummaryDedupBackfillRunner, SyncRetentionRunner, VERSION, VectorModelMigrationRunner, aiBackfillStructuredContent, applyBootstrapSnapshot, applyDistillRule, backfillMemoryDedupKeys, backfillNarrativeFromBody, backfillTagsText, backfillVectors, buildAuthHeaders, buildBaseUrl, buildDistillReport, buildRawEventEnvelopeFromCodexHook, buildRawEventEnvelopeFromHook, compareMemoryRoleReports, connect, coordinatorCreateGroupAction, coordinatorCreateInviteAction, coordinatorCreateScopeAction, coordinatorDisableDeviceAction, coordinatorEnrollDeviceAction, coordinatorGrantScopeMembershipAction, coordinatorImportInviteAction, coordinatorListBootstrapGrantsAction, coordinatorListDevicesAction, coordinatorListGroupsAction, coordinatorListJoinRequestsAction, coordinatorListScopeMembershipsAction, coordinatorListScopesAction, coordinatorRemoveDeviceAction, coordinatorRenameDeviceAction, coordinatorReviewJoinRequestAction, coordinatorRevokeBootstrapGrantAction, coordinatorRevokeScopeMembershipAction, coordinatorUpdateScopeAction, createBetterSqliteCoordinatorApp, deactivateLowSignalMemories, deactivateLowSignalObservations, dedupNearDuplicateMemories, draftDistillRule, ensureDeviceIdentity, ensureSchemaBootstrapped, estimateExtractionModelCost, exportMemories, extractApplyPatchPaths, fetchAllSnapshotPages, fingerprintPublicKey, flushRawEvents, formatHostPort, getExtractionBenchmarkProfile, getExtractionModelPricing, getInjectionEvalScenarioPack, getInjectionEvalScenarioPrompts, getMaintenanceJob, getMemoryArtifactReport, getMemoryRoleReport, getRawEventRelinkPlan, getRawEventRelinkReport, getRawEventStatus, getSemanticIndexDiagnostics, getSessionExtractionEval, getSessionExtractionEvalScenario, getWorkspaceCodememConfigPath, hasPendingDedupKeyBackfill, hasPendingRefBackfill, hasPendingScopeBackfill, hasPendingSessionContextBackfill, hasPendingSummaryDedupBackfill, hasUnsyncedSharedMemoryChanges, importMemories, initDatabase, isEmbeddingDisabled, judgeDistillReport, listMaintenanceJobs, listPerPeerScopeSyncState, listRetentionScopeIds, loadObserverConfig, loadPublicKey, loadSqliteVec, mdnsEnabled, planReplicationOpsAgePrune, projectMatchesFilter, pruneReplicationOpsUntilCaughtUp, rawEventsGate, readCodememConfigFile, readCodememConfigFileAtPath, readCoordinatorSyncConfig, readImportPayload, renderUnifiedDiff, replayBatchExtraction, replayBatchExtractionWithTierRouting, requestJson, resolveCodememConfigPath, resolveDbPath, resolveHookProject, resolveProject, resolveProjectRoot, retryRawEventFailures, runSyncDaemon, runSyncPass, scanSecretsRetroactive, schema, scoreExtractionBenchmarkOutput, setPeerProjectFilter, stripJsonComments, stripPrivateObj, stripTrailingCommas, syncPassPreflight, updatePeerAddresses, vacuumDatabase, writeCodememConfigFile } from "@codemem/core";
|
|
2
|
+
import { DEDUP_KEY_BACKFILL_JOB, DEFAULT_COORDINATOR_DB_PATH, DedupKeyBackfillRunner, MUTATING_TOOL_NAMES, MemoryStore, ObserverClient, REF_BACKFILL_JOB, RawEventSweeper, RefBackfillRunner, SCOPE_BACKFILL_JOB, SESSION_CONTEXT_BACKFILL_JOB, SUMMARY_DEDUP_BACKFILL_JOB, ScopeBackfillRunner, SessionContextBackfillRunner, SummaryDedupBackfillRunner, SyncRetentionRunner, VERSION, VectorModelMigrationRunner, aiBackfillStructuredContent, applyBootstrapSnapshot, applyDistillRule, backfillMemoryDedupKeys, backfillNarrativeFromBody, backfillTagsText, backfillVectors, buildAuthHeaders, buildBaseUrl, buildDistillReport, buildRawEventEnvelopeFromCodexHook, buildRawEventEnvelopeFromHook, clonePromptPackAttempt, compareMemoryRoleReports, connect, coordinatorCreateGroupAction, coordinatorCreateInviteAction, coordinatorCreateScopeAction, coordinatorDisableDeviceAction, coordinatorEnrollDeviceAction, coordinatorGrantScopeMembershipAction, coordinatorImportInviteAction, coordinatorListBootstrapGrantsAction, coordinatorListDevicesAction, coordinatorListGroupsAction, coordinatorListJoinRequestsAction, coordinatorListScopeMembershipsAction, coordinatorListScopesAction, coordinatorRemoveDeviceAction, coordinatorRenameDeviceAction, coordinatorReviewJoinRequestAction, coordinatorRevokeBootstrapGrantAction, coordinatorRevokeScopeMembershipAction, coordinatorUpdateScopeAction, createBetterSqliteCoordinatorApp, deactivateLowSignalMemories, deactivateLowSignalObservations, dedupNearDuplicateMemories, draftDistillRule, ensureDeviceIdentity, ensureSchemaBootstrapped, estimateExtractionModelCost, exportMemories, extractApplyPatchPaths, fetchAllSnapshotPages, fingerprintPublicKey, flushRawEvents, formatHostPort, getAttributionDiagnostics, getExtractionBenchmarkProfile, getExtractionModelPricing, getInjectionEvalScenarioPack, getInjectionEvalScenarioPrompts, getMaintenanceJob, getMemoryArtifactReport, getMemoryRoleReport, getRawEventRelinkPlan, getRawEventRelinkReport, getRawEventStatus, getSemanticIndexDiagnostics, getSessionExtractionEval, getSessionExtractionEvalScenario, getWorkspaceCodememConfigPath, hasPendingDedupKeyBackfill, hasPendingRefBackfill, hasPendingScopeBackfill, hasPendingSessionContextBackfill, hasPendingSummaryDedupBackfill, hasUnsyncedSharedMemoryChanges, importMemories, initDatabase, isEmbeddingDisabled, judgeDistillReport, listMaintenanceJobs, listPerPeerScopeSyncState, listRetentionScopeIds, loadObserverConfig, loadPublicKey, loadSqliteVec, mdnsEnabled, planReplicationOpsAgePrune, projectMatchesFilter, promptPackArtifactFingerprint, pruneReplicationOpsUntilCaughtUp, rawEventsGate, readCodememConfigFile, readCodememConfigFileAtPath, readCoordinatorSyncConfig, readImportPayload, recordPromptPackArtifacts, recordPromptPackTerminal, recordRetrievalSurface, renderUnifiedDiff, replayBatchExtraction, replayBatchExtractionWithTierRouting, requestJson, resolveCodememConfigPath, resolveDbPath, resolveHookProject, resolveProject, resolveProjectRoot, resolveRetrievalSession, retryRawEventFailures, runSyncDaemon, runSyncPass, scanSecretsRetroactive, schema, scoreExtractionBenchmarkOutput, setPeerProjectFilter, stripJsonComments, stripPrivateObj, stripTrailingCommas, syncPassPreflight, tryUpdateRetrievalDelivery, updatePeerAddresses, vacuumDatabase, writeCodememConfigFile } from "@codemem/core";
|
|
3
3
|
import { Command, Option } from "commander";
|
|
4
4
|
import omelette from "omelette";
|
|
5
|
+
import { randomInt, randomUUID } from "node:crypto";
|
|
5
6
|
import { appendFileSync, copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, rmdirSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
6
7
|
import { homedir, networkInterfaces } from "node:os";
|
|
7
|
-
import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
8
|
+
import { dirname, isAbsolute, join, posix, relative, resolve, sep, win32 } from "node:path";
|
|
8
9
|
import { styleText } from "node:util";
|
|
9
|
-
import { randomInt, randomUUID } from "node:crypto";
|
|
10
10
|
import * as p from "@clack/prompts";
|
|
11
11
|
import { serve } from "@hono/node-server";
|
|
12
12
|
import { execFileSync, spawn, spawnSync } from "node:child_process";
|
|
@@ -287,11 +287,77 @@ function queryByFile(dbPath, relativePath, project, limit) {
|
|
|
287
287
|
function resolveProject$1(payload) {
|
|
288
288
|
return resolveHookProject(typeof payload.cwd === "string" ? payload.cwd : null, payload.project);
|
|
289
289
|
}
|
|
290
|
+
function sourceSessionId(payload) {
|
|
291
|
+
const value = payload.session_id;
|
|
292
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
293
|
+
}
|
|
290
294
|
async function buildClaudeFileContext(payload, opts, deps = {}) {
|
|
291
295
|
if (envTruthy$3(process.env.CODEMEM_PLUGIN_IGNORE)) return continueResult$2();
|
|
292
|
-
if (!envNotDisabled$2(process.env.CODEMEM_FILE_CONTEXT || "1")) return continueResult$2();
|
|
293
296
|
const filePath = extractFilePath(payload);
|
|
294
297
|
if (!filePath) return continueResult$2();
|
|
298
|
+
const startedAt = (deps.now ?? (() => /* @__PURE__ */ new Date()))();
|
|
299
|
+
const attemptId = (deps.createAttemptId ?? randomUUID)();
|
|
300
|
+
const resolveDb = deps.resolveDb ?? resolveDbPath;
|
|
301
|
+
let resolvedDbPath = null;
|
|
302
|
+
let activeStore = null;
|
|
303
|
+
const getStore = () => {
|
|
304
|
+
resolvedDbPath ??= resolveDb(resolveDbOpt(opts));
|
|
305
|
+
activeStore ??= (deps.createStore ?? ((dbPath) => new MemoryStore(dbPath)))(resolvedDbPath);
|
|
306
|
+
return activeStore;
|
|
307
|
+
};
|
|
308
|
+
const finish = (result) => {
|
|
309
|
+
try {
|
|
310
|
+
activeStore?.close();
|
|
311
|
+
} catch {}
|
|
312
|
+
activeStore = null;
|
|
313
|
+
return result;
|
|
314
|
+
};
|
|
315
|
+
const record = (attemptInput) => {
|
|
316
|
+
if (!envNotDisabled$2(process.env.CODEMEM_RETRIEVAL_LEDGER || "1")) return;
|
|
317
|
+
try {
|
|
318
|
+
resolvedDbPath ??= resolveDb(resolveDbOpt(opts));
|
|
319
|
+
const completedAt = (deps.now ?? (() => /* @__PURE__ */ new Date()))();
|
|
320
|
+
const ledgerInput = {
|
|
321
|
+
...attemptInput,
|
|
322
|
+
attemptId,
|
|
323
|
+
surface: "file_context",
|
|
324
|
+
trigger: "automatic",
|
|
325
|
+
startedAt: startedAt.toISOString(),
|
|
326
|
+
completedAt: completedAt.toISOString(),
|
|
327
|
+
latencyMs: Math.max(0, completedAt.getTime() - startedAt.getTime()),
|
|
328
|
+
recorderVersion: "claude-file-context-v1",
|
|
329
|
+
source: "claude",
|
|
330
|
+
streamId: sourceSessionId(payload),
|
|
331
|
+
sourceSessionId: sourceSessionId(payload),
|
|
332
|
+
mode: "claude_pre_tool_use_read"
|
|
333
|
+
};
|
|
334
|
+
if (deps.recordAttempt) deps.recordAttempt(resolvedDbPath, ledgerInput);
|
|
335
|
+
else {
|
|
336
|
+
const store = getStore();
|
|
337
|
+
recordRetrievalSurface(store.db, {
|
|
338
|
+
...ledgerInput,
|
|
339
|
+
sessionId: resolveRetrievalSession(store.db, "claude", ledgerInput.sourceSessionId)
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
} catch {}
|
|
343
|
+
};
|
|
344
|
+
const updateDelivery = (status) => {
|
|
345
|
+
if (!envNotDisabled$2(process.env.CODEMEM_RETRIEVAL_LEDGER || "1")) return;
|
|
346
|
+
try {
|
|
347
|
+
resolvedDbPath ??= resolveDb(resolveDbOpt(opts));
|
|
348
|
+
if (deps.updateDelivery) deps.updateDelivery(resolvedDbPath, attemptId, status);
|
|
349
|
+
else tryUpdateRetrievalDelivery(getStore().db, attemptId, status);
|
|
350
|
+
} catch {}
|
|
351
|
+
};
|
|
352
|
+
if (!envNotDisabled$2(process.env.CODEMEM_FILE_CONTEXT || "1")) {
|
|
353
|
+
record({
|
|
354
|
+
retrievalStatus: "skipped",
|
|
355
|
+
deliveryStatus: "not_attempted",
|
|
356
|
+
failureCode: "file_context_disabled",
|
|
357
|
+
failureStage: "configuration"
|
|
358
|
+
});
|
|
359
|
+
return finish(continueResult$2());
|
|
360
|
+
}
|
|
295
361
|
const cwd = typeof payload.cwd === "string" && payload.cwd.trim() ? payload.cwd : process.cwd();
|
|
296
362
|
const expandedPath = expandHome$3(filePath);
|
|
297
363
|
const absolutePath = isAbsolute(expandedPath) ? expandedPath : resolve(cwd, expandedPath);
|
|
@@ -299,38 +365,89 @@ async function buildClaudeFileContext(payload, opts, deps = {}) {
|
|
|
299
365
|
const escapesCwd = relativePath === ".." || relativePath.startsWith("../") || isAbsolute(relativePath);
|
|
300
366
|
if (!relativePath || escapesCwd) {
|
|
301
367
|
logHookEvent(`file_context.skip reason=outside_cwd path=${JSON.stringify(filePath)} cwd=${JSON.stringify(cwd)}`);
|
|
302
|
-
|
|
368
|
+
record({
|
|
369
|
+
retrievalStatus: "skipped",
|
|
370
|
+
deliveryStatus: "not_attempted",
|
|
371
|
+
failureCode: "outside_cwd",
|
|
372
|
+
failureStage: "path_validation"
|
|
373
|
+
});
|
|
374
|
+
return finish(continueResult$2());
|
|
303
375
|
}
|
|
304
376
|
const minBytes = Number.parseInt(process.env.CODEMEM_FILE_CONTEXT_MIN_BYTES ?? `${FILE_GATE_MIN_BYTES}`, 10);
|
|
305
377
|
const minBytesEffective = Number.isFinite(minBytes) && minBytes >= 0 ? minBytes : FILE_GATE_MIN_BYTES;
|
|
306
378
|
const stat = (deps.statFile ?? statFile)(absolutePath);
|
|
307
379
|
if (!stat) {
|
|
308
380
|
logHookEvent(`file_context.skip reason=stat_failed path=${JSON.stringify(relativePath)}`);
|
|
309
|
-
|
|
381
|
+
record({
|
|
382
|
+
retrievalStatus: "skipped",
|
|
383
|
+
deliveryStatus: "not_attempted",
|
|
384
|
+
failureCode: "stat_failed",
|
|
385
|
+
failureStage: "file_access",
|
|
386
|
+
repositoryPaths: [relativePath]
|
|
387
|
+
});
|
|
388
|
+
return finish(continueResult$2());
|
|
310
389
|
}
|
|
311
390
|
const bypassSizeGate = SMALL_FILE_BYPASS_PATTERNS.some((p) => p.test(relativePath));
|
|
312
391
|
if (stat.sizeBytes < minBytesEffective && !bypassSizeGate) {
|
|
313
392
|
logHookEvent(`file_context.skip reason=below_size_gate path=${JSON.stringify(relativePath)} size=${stat.sizeBytes} gate=${minBytesEffective}`);
|
|
314
|
-
|
|
393
|
+
record({
|
|
394
|
+
retrievalStatus: "skipped",
|
|
395
|
+
deliveryStatus: "not_attempted",
|
|
396
|
+
failureCode: "below_size_gate",
|
|
397
|
+
failureStage: "size_gate",
|
|
398
|
+
repositoryPaths: [relativePath]
|
|
399
|
+
});
|
|
400
|
+
return finish(continueResult$2());
|
|
315
401
|
}
|
|
316
402
|
const project = resolveProject$1(payload);
|
|
317
|
-
const resolveDb = deps.resolveDb ?? resolveDbPath;
|
|
318
403
|
const queryFn = deps.queryByFile ?? queryByFile;
|
|
319
404
|
let rows = [];
|
|
320
405
|
try {
|
|
321
|
-
|
|
406
|
+
resolvedDbPath ??= resolveDb(resolveDbOpt(opts));
|
|
407
|
+
rows = deps.queryByFile ? queryFn(resolvedDbPath, relativePath, project, FETCH_LIMIT) : getStore().findByFile(relativePath, {
|
|
408
|
+
limit: FETCH_LIMIT,
|
|
409
|
+
...project ? { project } : {}
|
|
410
|
+
});
|
|
322
411
|
} catch (err) {
|
|
323
412
|
logHookEvent(`codemem claude-hook-file-context query failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
324
|
-
|
|
413
|
+
record({
|
|
414
|
+
retrievalStatus: "failed",
|
|
415
|
+
deliveryStatus: "not_attempted",
|
|
416
|
+
failureCode: "query_failed",
|
|
417
|
+
failureStage: "retrieval",
|
|
418
|
+
project,
|
|
419
|
+
filters: project ? { project } : void 0,
|
|
420
|
+
repositoryPaths: [relativePath]
|
|
421
|
+
});
|
|
422
|
+
return finish(continueResult$2());
|
|
325
423
|
}
|
|
326
424
|
if (rows.length === 0) {
|
|
327
425
|
logHookEvent(`file_context.skip reason=no_observations path=${JSON.stringify(relativePath)} project=${JSON.stringify(project ?? "")}`);
|
|
328
|
-
|
|
426
|
+
record({
|
|
427
|
+
retrievalStatus: "no_results",
|
|
428
|
+
deliveryStatus: "not_attempted",
|
|
429
|
+
project,
|
|
430
|
+
filters: project ? { project } : void 0,
|
|
431
|
+
repositoryPaths: [relativePath]
|
|
432
|
+
});
|
|
433
|
+
return finish(continueResult$2());
|
|
329
434
|
}
|
|
330
435
|
const top = scoreAndDedupe(rows, relativePath, DISPLAY_LIMIT);
|
|
331
436
|
if (top.length === 0) {
|
|
332
437
|
logHookEvent(`file_context.skip reason=no_top_after_dedupe path=${JSON.stringify(relativePath)} candidates=${rows.length}`);
|
|
333
|
-
|
|
438
|
+
record({
|
|
439
|
+
retrievalStatus: "succeeded",
|
|
440
|
+
deliveryStatus: "not_attempted",
|
|
441
|
+
candidateIds: rows.map((row) => row.id),
|
|
442
|
+
candidateCount: rows.length,
|
|
443
|
+
selectedIds: [],
|
|
444
|
+
failureCode: "no_top_after_dedupe",
|
|
445
|
+
failureStage: "selection",
|
|
446
|
+
project,
|
|
447
|
+
filters: project ? { project } : void 0,
|
|
448
|
+
repositoryPaths: [relativePath]
|
|
449
|
+
});
|
|
450
|
+
return finish(continueResult$2());
|
|
334
451
|
}
|
|
335
452
|
let staleness = null;
|
|
336
453
|
if (stat.mtimeMs > 0) {
|
|
@@ -343,13 +460,30 @@ async function buildClaudeFileContext(payload, opts, deps = {}) {
|
|
|
343
460
|
newestObservationMs
|
|
344
461
|
};
|
|
345
462
|
}
|
|
346
|
-
|
|
463
|
+
record({
|
|
464
|
+
retrievalStatus: "succeeded",
|
|
465
|
+
deliveryStatus: "not_attempted",
|
|
466
|
+
candidateIds: rows.map((row) => row.id),
|
|
467
|
+
candidateCount: rows.length,
|
|
468
|
+
selectedIds: top.map((row) => row.id),
|
|
469
|
+
project,
|
|
470
|
+
filters: project ? { project } : void 0,
|
|
471
|
+
repositoryPaths: [relativePath]
|
|
472
|
+
});
|
|
473
|
+
let timeline;
|
|
474
|
+
try {
|
|
475
|
+
timeline = formatTimeline(top, relativePath, staleness);
|
|
476
|
+
} catch {
|
|
477
|
+
updateDelivery("failed");
|
|
478
|
+
return finish(continueResult$2());
|
|
479
|
+
}
|
|
347
480
|
logHookEvent(`file_context.ok path=${JSON.stringify(relativePath)} candidates=${rows.length} surfaced=${top.length} project=${JSON.stringify(project ?? "")} stale=${staleness ? "true" : "false"}`);
|
|
348
|
-
|
|
481
|
+
updateDelivery("handed_off");
|
|
482
|
+
return finish({ hookSpecificOutput: {
|
|
349
483
|
hookEventName: "PreToolUse",
|
|
350
484
|
permissionDecision: "allow",
|
|
351
485
|
additionalContext: timeline
|
|
352
|
-
} };
|
|
486
|
+
} });
|
|
353
487
|
}
|
|
354
488
|
var claudeHookFileContextCmd = new Command("claude-hook-file-context").configureHelp(helpStyle).description("Return Claude PreToolUse:Read additionalContext from per-file observation timeline");
|
|
355
489
|
addDbOption(claudeHookFileContextCmd);
|
|
@@ -4332,6 +4466,7 @@ function startMaintenanceWorkerRuntime(options = {}) {
|
|
|
4332
4466
|
if (!isEmbeddingDisabled()) runners.push(new VectorModelMigrationRunner({
|
|
4333
4467
|
dbPath,
|
|
4334
4468
|
signal: options.signal,
|
|
4469
|
+
batchSize: 10,
|
|
4335
4470
|
idleIntervalMs: 5e3
|
|
4336
4471
|
}));
|
|
4337
4472
|
runners.push(createSequentialBackfillCoordinator(store, [
|
|
@@ -4636,6 +4771,9 @@ function parseStrictPositiveId(value) {
|
|
|
4636
4771
|
const n = Number(value.trim());
|
|
4637
4772
|
return Number.isFinite(n) && n >= 1 && Number.isInteger(n) ? n : null;
|
|
4638
4773
|
}
|
|
4774
|
+
function resolveOpenAIResponsesOverride(cliEnabled, configured) {
|
|
4775
|
+
return cliEnabled === true ? true : configured;
|
|
4776
|
+
}
|
|
4639
4777
|
function showMemoryAction(idStr, opts) {
|
|
4640
4778
|
const memoryId = parseStrictPositiveId(idStr);
|
|
4641
4779
|
if (memoryId === null) {
|
|
@@ -5063,10 +5201,11 @@ function createMemoryExtractionReplayCommand() {
|
|
|
5063
5201
|
const observerConfigWithOverrides = {
|
|
5064
5202
|
...observerConfig,
|
|
5065
5203
|
observerTemperature: observerTemperature ?? observerConfig.observerTemperature,
|
|
5066
|
-
observerOpenAIUseResponses: opts.openaiResponses
|
|
5067
|
-
observerReasoningEffort: opts.reasoningEffort
|
|
5068
|
-
observerReasoningSummary: opts.reasoningSummary
|
|
5069
|
-
observerMaxOutputTokens: maxOutputTokens ?? observerConfig.observerMaxTokens
|
|
5204
|
+
observerOpenAIUseResponses: resolveOpenAIResponsesOverride(opts.openaiResponses, observerConfig.observerOpenAIUseResponses),
|
|
5205
|
+
observerReasoningEffort: opts.reasoningEffort === void 0 ? observerConfig.observerReasoningEffort : opts.reasoningEffort.trim() || null,
|
|
5206
|
+
observerReasoningSummary: opts.reasoningSummary === void 0 ? observerConfig.observerReasoningSummary : opts.reasoningSummary.trim() || null,
|
|
5207
|
+
observerMaxOutputTokens: maxOutputTokens ?? observerConfig.observerMaxOutputTokens ?? observerConfig.observerMaxTokens,
|
|
5208
|
+
observerExplicitConfigKeys: maxOutputTokens === null ? observerConfig.observerExplicitConfigKeys : [...new Set([...observerConfig.observerExplicitConfigKeys ?? [], "observerMaxOutputTokens"])]
|
|
5070
5209
|
};
|
|
5071
5210
|
const observer = new ObserverClient(observerConfigWithOverrides);
|
|
5072
5211
|
const result = opts.observerTierRouting === true ? await replayBatchExtractionWithTierRouting(resolveDbOpt(opts), observerConfigWithOverrides, {
|
|
@@ -5139,6 +5278,15 @@ function reconcileExtractionBenchmarkStatus(input) {
|
|
|
5139
5278
|
initialQuality: input.initialQuality
|
|
5140
5279
|
};
|
|
5141
5280
|
}
|
|
5281
|
+
function summarizeBenchmarkReasoning(runs, fallback) {
|
|
5282
|
+
const source = runs[0] ?? fallback;
|
|
5283
|
+
const reasoningEfforts = new Set(runs.map((run) => run.reasoningEffort));
|
|
5284
|
+
const reasoningSummaries = new Set(runs.map((run) => run.reasoningSummary));
|
|
5285
|
+
return {
|
|
5286
|
+
reasoningEffort: reasoningEfforts.size > 1 ? "mixed" : source.reasoningEffort,
|
|
5287
|
+
reasoningSummary: reasoningSummaries.size > 1 ? "mixed" : source.reasoningSummary
|
|
5288
|
+
};
|
|
5289
|
+
}
|
|
5142
5290
|
function createMemoryExtractionBenchmarkCommand() {
|
|
5143
5291
|
const cmd = new Command("extraction-benchmark").configureHelp(helpStyle).description("Run the formal extraction replay benchmark set and print a cost/quality scoreboard").requiredOption("--benchmark <id>", "benchmark profile id").option("--observer-provider <provider>", "override observer provider for this benchmark run").option("--observer-model <model>", "override observer model for this benchmark run").option("--observer-tier-routing", "use replay-only benchmark-backed observer tier routing").option("--openai-responses", "use OpenAI Responses API for this benchmark run").option("--reasoning-effort <level>", "set OpenAI reasoning.effort for this benchmark run (responses path)").option("--reasoning-summary <mode>", "set OpenAI reasoning.summary for this benchmark run (responses path)").option("--max-output-tokens <n>", "override OpenAI max_output_tokens for this benchmark run (responses path)").option("--observer-temperature <value>", "override observer temperature for this benchmark run").option("--transcript-budget <chars>", "override replay transcript budget in characters for this benchmark run").option("--repetitions <n>", "run every benchmark batch 1-10 times to measure model stability", "1");
|
|
5144
5292
|
addDbOption(cmd);
|
|
@@ -5170,10 +5318,11 @@ function createMemoryExtractionBenchmarkCommand() {
|
|
|
5170
5318
|
observerProvider: opts.observerProvider?.trim() || observerConfig.observerProvider,
|
|
5171
5319
|
observerModel: opts.observerModel?.trim() || observerConfig.observerModel,
|
|
5172
5320
|
observerTemperature: observerTemperature ?? observerConfig.observerTemperature,
|
|
5173
|
-
observerOpenAIUseResponses: opts.openaiResponses
|
|
5174
|
-
observerReasoningEffort: opts.reasoningEffort
|
|
5175
|
-
observerReasoningSummary: opts.reasoningSummary
|
|
5176
|
-
observerMaxOutputTokens: maxOutputTokens ?? observerConfig.observerMaxTokens
|
|
5321
|
+
observerOpenAIUseResponses: resolveOpenAIResponsesOverride(opts.openaiResponses, observerConfig.observerOpenAIUseResponses),
|
|
5322
|
+
observerReasoningEffort: opts.reasoningEffort === void 0 ? observerConfig.observerReasoningEffort : opts.reasoningEffort.trim() || null,
|
|
5323
|
+
observerReasoningSummary: opts.reasoningSummary === void 0 ? observerConfig.observerReasoningSummary : opts.reasoningSummary.trim() || null,
|
|
5324
|
+
observerMaxOutputTokens: maxOutputTokens ?? observerConfig.observerMaxOutputTokens ?? observerConfig.observerMaxTokens,
|
|
5325
|
+
observerExplicitConfigKeys: maxOutputTokens === null ? observerConfig.observerExplicitConfigKeys : [...new Set([...observerConfig.observerExplicitConfigKeys ?? [], "observerMaxOutputTokens"])]
|
|
5177
5326
|
};
|
|
5178
5327
|
const observer = new ObserverClient(observerConfigWithOverrides);
|
|
5179
5328
|
const runs = [];
|
|
@@ -5343,16 +5492,20 @@ function createMemoryExtractionBenchmarkCommand() {
|
|
|
5343
5492
|
})
|
|
5344
5493
|
};
|
|
5345
5494
|
const uniqueObserverKeys = Array.from(new Set(runs.map((run) => `${run.provider}::${run.model}::${run.transport}`)));
|
|
5495
|
+
const benchmarkReasoning = summarizeBenchmarkReasoning(runs, {
|
|
5496
|
+
reasoningEffort: observer.reasoningEffort,
|
|
5497
|
+
reasoningSummary: observer.reasoningSummary
|
|
5498
|
+
});
|
|
5346
5499
|
const observerSummary = opts.observerTierRouting === true ? {
|
|
5347
5500
|
provider: uniqueObserverKeys.length === 1 ? runs[0]?.provider ?? observer.provider : "mixed",
|
|
5348
5501
|
model: uniqueObserverKeys.length === 1 ? runs[0]?.model ?? observer.model : "mixed",
|
|
5349
5502
|
transport: uniqueObserverKeys.length === 1 ? runs[0]?.transport ?? "unknown" : "mixed",
|
|
5350
5503
|
tierRouting: true,
|
|
5351
5504
|
openaiUseResponses: uniqueObserverKeys.length === 1 ? runs[0]?.openaiUseResponses ?? observer.openaiUseResponses : null,
|
|
5352
|
-
reasoningEffort:
|
|
5353
|
-
reasoningSummary:
|
|
5354
|
-
maxOutputTokens: uniqueObserverKeys.length === 1 ? runs[0]?.maxOutputTokens ??
|
|
5355
|
-
temperature: uniqueObserverKeys.length === 1 ? runs[0]?.temperature ??
|
|
5505
|
+
reasoningEffort: benchmarkReasoning.reasoningEffort,
|
|
5506
|
+
reasoningSummary: benchmarkReasoning.reasoningSummary,
|
|
5507
|
+
maxOutputTokens: uniqueObserverKeys.length === 1 ? runs[0]?.maxOutputTokens ?? null : null,
|
|
5508
|
+
temperature: uniqueObserverKeys.length === 1 ? runs[0]?.temperature ?? null : null,
|
|
5356
5509
|
transcriptBudget: transcriptBudget ?? null,
|
|
5357
5510
|
selectedObservers: uniqueObserverKeys
|
|
5358
5511
|
} : {
|
|
@@ -5361,10 +5514,10 @@ function createMemoryExtractionBenchmarkCommand() {
|
|
|
5361
5514
|
transport: runs[0]?.transport ?? observer.getStatus().runtime,
|
|
5362
5515
|
tierRouting: false,
|
|
5363
5516
|
openaiUseResponses: observer.openaiUseResponses,
|
|
5364
|
-
reasoningEffort:
|
|
5365
|
-
reasoningSummary:
|
|
5366
|
-
maxOutputTokens:
|
|
5367
|
-
temperature:
|
|
5517
|
+
reasoningEffort: benchmarkReasoning.reasoningEffort,
|
|
5518
|
+
reasoningSummary: benchmarkReasoning.reasoningSummary,
|
|
5519
|
+
maxOutputTokens: runs[0]?.maxOutputTokens ?? null,
|
|
5520
|
+
temperature: runs[0]?.temperature ?? null,
|
|
5368
5521
|
transcriptBudget: transcriptBudget ?? null,
|
|
5369
5522
|
selectedObservers: uniqueObserverKeys
|
|
5370
5523
|
};
|
|
@@ -5390,10 +5543,10 @@ function createMemoryExtractionBenchmarkCommand() {
|
|
|
5390
5543
|
`Transport: ${observerSummary.transport}`,
|
|
5391
5544
|
`Tier routing: ${opts.observerTierRouting === true ? "yes" : "no"}`,
|
|
5392
5545
|
`OpenAI Responses: ${observerSummary.openaiUseResponses === null ? "mixed" : observerSummary.openaiUseResponses ? "yes" : "no"}`,
|
|
5393
|
-
`Reasoning effort: ${observerSummary.reasoningEffort ?? "
|
|
5394
|
-
`Reasoning summary: ${observerSummary.reasoningSummary ?? "
|
|
5395
|
-
`Max output tokens: ${observerSummary.maxOutputTokens ?? "mixed"}`,
|
|
5396
|
-
`Temperature: ${observerSummary.temperature ?? "
|
|
5546
|
+
`Reasoning effort: ${observerSummary.reasoningEffort ?? "not transmitted"}`,
|
|
5547
|
+
`Reasoning summary: ${observerSummary.reasoningSummary ?? "not transmitted"}`,
|
|
5548
|
+
`Max output tokens: ${observerSummary.transport === "codex_consumer" ? "not transmitted" : observerSummary.maxOutputTokens ?? "mixed"}`,
|
|
5549
|
+
`Temperature: ${observerSummary.transport === "mixed" ? "mixed" : observerSummary.temperature ?? "not transmitted"}`,
|
|
5397
5550
|
`Transcript budget override: ${transcriptBudget ?? "default"}`,
|
|
5398
5551
|
`Repetitions: ${summary.repetitions}`,
|
|
5399
5552
|
`Shape-quality passes: ${summary.shapeQualityPasses}/${summary.shapeQualityTotal}`,
|
|
@@ -5511,6 +5664,127 @@ memoryCommand.addCommand(createMemoryRelinkReportCommand());
|
|
|
5511
5664
|
memoryCommand.addCommand(createMemoryRelinkPlanCommand());
|
|
5512
5665
|
//#endregion
|
|
5513
5666
|
//#region src/commands/pack.ts
|
|
5667
|
+
var MAX_INTERNAL_LEDGER_INPUT_BYTES = 16 * 1024;
|
|
5668
|
+
var INTERNAL_LEDGER_INPUT_TIMEOUT_MS = 1e3;
|
|
5669
|
+
var FORBIDDEN_INTERNAL_KEYS = new Set([
|
|
5670
|
+
"body",
|
|
5671
|
+
"context",
|
|
5672
|
+
"pack",
|
|
5673
|
+
"pack_text",
|
|
5674
|
+
"path",
|
|
5675
|
+
"preview",
|
|
5676
|
+
"prompt",
|
|
5677
|
+
"query",
|
|
5678
|
+
"raw_prompt",
|
|
5679
|
+
"title"
|
|
5680
|
+
]);
|
|
5681
|
+
var INTERNAL_LEDGER_KEYS = new Set([
|
|
5682
|
+
"action",
|
|
5683
|
+
"attempt_id",
|
|
5684
|
+
"started_at",
|
|
5685
|
+
"source",
|
|
5686
|
+
"stream_id",
|
|
5687
|
+
"source_session_id",
|
|
5688
|
+
"prompt_number",
|
|
5689
|
+
"request_id",
|
|
5690
|
+
"retrieval_status",
|
|
5691
|
+
"delivery_status",
|
|
5692
|
+
"failure_code",
|
|
5693
|
+
"failure_stage",
|
|
5694
|
+
"original_attempt_id"
|
|
5695
|
+
]);
|
|
5696
|
+
var INTERNAL_LEDGER_ACTIONS = new Set([
|
|
5697
|
+
"record",
|
|
5698
|
+
"delivery",
|
|
5699
|
+
"cache_reuse"
|
|
5700
|
+
]);
|
|
5701
|
+
var INTERNAL_RETRIEVAL_STATUSES = new Set(["skipped", "failed"]);
|
|
5702
|
+
var INTERNAL_DELIVERY_STATUSES = new Set([
|
|
5703
|
+
"handed_off",
|
|
5704
|
+
"failed",
|
|
5705
|
+
"unknown"
|
|
5706
|
+
]);
|
|
5707
|
+
async function readInternalLedgerPayload() {
|
|
5708
|
+
const stdin = process.stdin;
|
|
5709
|
+
if (stdin.isTTY) throw new PackUsageError("internal ledger metadata requires piped stdin JSON");
|
|
5710
|
+
return parseInternalLedgerPayload(await new Promise((resolve, reject) => {
|
|
5711
|
+
let value = "";
|
|
5712
|
+
const cleanup = () => {
|
|
5713
|
+
clearTimeout(timer);
|
|
5714
|
+
stdin.off("data", onData);
|
|
5715
|
+
stdin.off("end", onEnd);
|
|
5716
|
+
stdin.off("error", onError);
|
|
5717
|
+
};
|
|
5718
|
+
const fail = (error) => {
|
|
5719
|
+
cleanup();
|
|
5720
|
+
stdin.pause();
|
|
5721
|
+
reject(error);
|
|
5722
|
+
};
|
|
5723
|
+
const onData = (chunk) => {
|
|
5724
|
+
value += String(chunk);
|
|
5725
|
+
if (Buffer.byteLength(value, "utf8") > MAX_INTERNAL_LEDGER_INPUT_BYTES) fail(new PackUsageError("internal ledger metadata exceeds 16384 bytes"));
|
|
5726
|
+
};
|
|
5727
|
+
const onEnd = () => {
|
|
5728
|
+
cleanup();
|
|
5729
|
+
resolve(value);
|
|
5730
|
+
};
|
|
5731
|
+
const onError = (error) => fail(error);
|
|
5732
|
+
const timer = setTimeout(() => fail(new PackUsageError("internal ledger metadata read timed out")), INTERNAL_LEDGER_INPUT_TIMEOUT_MS);
|
|
5733
|
+
stdin.on("data", onData);
|
|
5734
|
+
stdin.once("end", onEnd);
|
|
5735
|
+
stdin.once("error", onError);
|
|
5736
|
+
}));
|
|
5737
|
+
}
|
|
5738
|
+
function parseInternalLedgerPayload(raw) {
|
|
5739
|
+
if (Buffer.byteLength(raw, "utf8") > MAX_INTERNAL_LEDGER_INPUT_BYTES) throw new PackUsageError("internal ledger metadata exceeds 16384 bytes");
|
|
5740
|
+
let value;
|
|
5741
|
+
try {
|
|
5742
|
+
value = JSON.parse(raw);
|
|
5743
|
+
} catch {
|
|
5744
|
+
throw new PackUsageError("internal ledger metadata must be valid JSON");
|
|
5745
|
+
}
|
|
5746
|
+
if (value == null || typeof value !== "object" || Array.isArray(value)) throw new PackUsageError("internal ledger metadata must be an object");
|
|
5747
|
+
for (const key of Object.keys(value)) {
|
|
5748
|
+
if (FORBIDDEN_INTERNAL_KEYS.has(key)) throw new PackUsageError(`internal ledger metadata rejects sensitive field: ${key}`);
|
|
5749
|
+
if (!INTERNAL_LEDGER_KEYS.has(key)) throw new PackUsageError(`internal ledger metadata contains unsupported field: ${key}`);
|
|
5750
|
+
}
|
|
5751
|
+
const payload = value;
|
|
5752
|
+
if (typeof payload.attempt_id !== "string") throw new PackUsageError("internal ledger metadata requires attempt_id");
|
|
5753
|
+
if (payload.action != null && !INTERNAL_LEDGER_ACTIONS.has(payload.action)) throw new PackUsageError("internal ledger action is invalid");
|
|
5754
|
+
if (payload.retrieval_status != null && !INTERNAL_RETRIEVAL_STATUSES.has(payload.retrieval_status)) throw new PackUsageError("internal ledger retrieval_status is invalid");
|
|
5755
|
+
if (payload.delivery_status != null && !INTERNAL_DELIVERY_STATUSES.has(payload.delivery_status)) throw new PackUsageError("internal ledger delivery_status is invalid");
|
|
5756
|
+
if (payload.prompt_number != null && (!Number.isInteger(payload.prompt_number) || payload.prompt_number < 0)) throw new PackUsageError("internal ledger prompt_number must be a non-negative integer");
|
|
5757
|
+
for (const key of [
|
|
5758
|
+
"started_at",
|
|
5759
|
+
"source",
|
|
5760
|
+
"stream_id",
|
|
5761
|
+
"source_session_id",
|
|
5762
|
+
"request_id",
|
|
5763
|
+
"failure_code",
|
|
5764
|
+
"failure_stage",
|
|
5765
|
+
"original_attempt_id"
|
|
5766
|
+
]) {
|
|
5767
|
+
const field = payload[key];
|
|
5768
|
+
if (field != null && (typeof field !== "string" || field.length > 512)) throw new PackUsageError(`internal ledger metadata field ${key} is invalid`);
|
|
5769
|
+
if (typeof field === "string" && (posix.isAbsolute(field) || win32.isAbsolute(field))) throw new PackUsageError(`internal ledger metadata rejects absolute paths in field: ${key}`);
|
|
5770
|
+
}
|
|
5771
|
+
return payload;
|
|
5772
|
+
}
|
|
5773
|
+
function attemptMetadata(payload) {
|
|
5774
|
+
return {
|
|
5775
|
+
attemptId: payload.attempt_id,
|
|
5776
|
+
startedAt: payload.started_at ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
5777
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5778
|
+
source: payload.source ?? "opencode",
|
|
5779
|
+
streamId: payload.stream_id ?? null,
|
|
5780
|
+
sourceSessionId: payload.source_session_id ?? null,
|
|
5781
|
+
promptNumber: payload.prompt_number ?? null,
|
|
5782
|
+
requestId: payload.request_id ?? null
|
|
5783
|
+
};
|
|
5784
|
+
}
|
|
5785
|
+
function handleInstrumentedPackLedger(db, payload, context, filters, artifacts) {
|
|
5786
|
+
return recordPromptPackArtifacts(db, attemptMetadata(payload), context, filters, artifacts);
|
|
5787
|
+
}
|
|
5514
5788
|
function describeCandidate(candidate) {
|
|
5515
5789
|
const scoreParts = [
|
|
5516
5790
|
candidate.scores.combined_score != null ? `combined=${candidate.scores.combined_score.toFixed(2)}` : null,
|
|
@@ -5584,24 +5858,46 @@ async function withStore(opts, errorCode, run) {
|
|
|
5584
5858
|
async function packAction(context, opts) {
|
|
5585
5859
|
await withStore(opts, "pack_failed", async (store) => {
|
|
5586
5860
|
const { limit, budget, filters, renderOptions } = buildPackRequestOptions(opts, { envProject: process.env.CODEMEM_PROJECT });
|
|
5587
|
-
|
|
5588
|
-
if (opts.
|
|
5589
|
-
|
|
5590
|
-
|
|
5591
|
-
|
|
5592
|
-
|
|
5593
|
-
|
|
5594
|
-
|
|
5595
|
-
|
|
5861
|
+
let result;
|
|
5862
|
+
if (opts.internalLedger) {
|
|
5863
|
+
const artifacts = await store.buildMemoryPackWithTraceAsync(context, limit, budget, filters, renderOptions);
|
|
5864
|
+
result = artifacts.response;
|
|
5865
|
+
let artifactFingerprint;
|
|
5866
|
+
try {
|
|
5867
|
+
artifactFingerprint = promptPackArtifactFingerprint(store.db, context, filters, artifacts);
|
|
5868
|
+
} catch {}
|
|
5869
|
+
let ledgerOutcome;
|
|
5870
|
+
try {
|
|
5871
|
+
const ledgerPayload = await readInternalLedgerPayload();
|
|
5872
|
+
ledgerOutcome = handleInstrumentedPackLedger(store.db, ledgerPayload, context, filters, artifacts);
|
|
5873
|
+
} catch {}
|
|
5874
|
+
emitPackResult(context, opts, result, artifactFingerprint, ledgerOutcome?.ok === false && ledgerOutcome.reason === "idempotency_conflict" ? ledgerOutcome : void 0);
|
|
5596
5875
|
return;
|
|
5597
|
-
}
|
|
5598
|
-
|
|
5599
|
-
p.log.info(`${metrics.total_items} items, ~${metrics.pack_tokens} tokens` + (metrics.fallback_used ? " (fallback)" : "") + ` [fts:${metrics.sources.fts} sem:${metrics.sources.semantic} fuzzy:${metrics.sources.fuzzy}]`);
|
|
5600
|
-
for (const item of result.items) p.log.step(`#${item.id} ${item.kind} ${item.title}`);
|
|
5601
|
-
p.note(result.pack_text, "pack_text");
|
|
5602
|
-
p.outro("done");
|
|
5876
|
+
} else result = await store.buildMemoryPackAsync(context, limit, budget, filters, renderOptions);
|
|
5877
|
+
emitPackResult(context, opts, result);
|
|
5603
5878
|
});
|
|
5604
5879
|
}
|
|
5880
|
+
function emitPackResult(context, opts, result, ledgerArtifactFingerprint, ledgerOutcome) {
|
|
5881
|
+
if (opts.json) {
|
|
5882
|
+
console.log(JSON.stringify({
|
|
5883
|
+
...result,
|
|
5884
|
+
...ledgerArtifactFingerprint ? { ledger_artifact_fingerprint: ledgerArtifactFingerprint } : {},
|
|
5885
|
+
...ledgerOutcome ? { ledger_outcome: ledgerOutcome } : {}
|
|
5886
|
+
}, null, 2));
|
|
5887
|
+
return;
|
|
5888
|
+
}
|
|
5889
|
+
p.intro(`Memory pack for "${context}"`);
|
|
5890
|
+
if (result.items.length === 0) {
|
|
5891
|
+
p.log.warn("No relevant memories found.");
|
|
5892
|
+
p.outro("done");
|
|
5893
|
+
return;
|
|
5894
|
+
}
|
|
5895
|
+
const metrics = result.metrics;
|
|
5896
|
+
p.log.info(`${metrics.total_items} items, ~${metrics.pack_tokens} tokens` + (metrics.fallback_used ? " (fallback)" : "") + ` [fts:${metrics.sources.fts} sem:${metrics.sources.semantic} fuzzy:${metrics.sources.fuzzy}]`);
|
|
5897
|
+
for (const item of result.items) p.log.step(`#${item.id} ${item.kind} ${item.title}`);
|
|
5898
|
+
p.note(result.pack_text, "pack_text");
|
|
5899
|
+
p.outro("done");
|
|
5900
|
+
}
|
|
5605
5901
|
async function traceAction(context, opts) {
|
|
5606
5902
|
await withStore(opts, "pack_trace_failed", async (store) => {
|
|
5607
5903
|
const { limit, budget, filters, renderOptions } = buildPackRequestOptions(opts, { envProject: process.env.CODEMEM_PROJECT });
|
|
@@ -5614,6 +5910,7 @@ async function traceAction(context, opts) {
|
|
|
5614
5910
|
});
|
|
5615
5911
|
}
|
|
5616
5912
|
var packCmd = addPackRequestOptions(new Command("pack").enablePositionalOptions().configureHelp(helpStyle).description("Build a context-aware memory pack").argument("<context>", "context string to search for"));
|
|
5913
|
+
packCmd.addOption(new Option("--internal-ledger").hideHelp());
|
|
5617
5914
|
addDbOption(packCmd);
|
|
5618
5915
|
addJsonOption(packCmd);
|
|
5619
5916
|
packCmd.action(packAction);
|
|
@@ -5623,6 +5920,38 @@ addJsonOption(traceCmd);
|
|
|
5623
5920
|
traceCmd.action(traceAction);
|
|
5624
5921
|
packCmd.addCommand(traceCmd);
|
|
5625
5922
|
var packCommand = packCmd;
|
|
5923
|
+
function handlePromptPackLedger(db, payload) {
|
|
5924
|
+
const metadata = attemptMetadata(payload);
|
|
5925
|
+
if (payload.action === "delivery") {
|
|
5926
|
+
const status = payload.delivery_status;
|
|
5927
|
+
if (!status) throw new PackUsageError("delivery action requires delivery_status");
|
|
5928
|
+
const outcome = tryUpdateRetrievalDelivery(db, payload.attempt_id, status);
|
|
5929
|
+
if (!outcome.ok) throw new Error(outcome.reason);
|
|
5930
|
+
return outcome.value;
|
|
5931
|
+
}
|
|
5932
|
+
if (payload.action === "cache_reuse") {
|
|
5933
|
+
if (!payload.original_attempt_id) throw new PackUsageError("cache_reuse action requires original_attempt_id");
|
|
5934
|
+
const outcome = clonePromptPackAttempt(db, payload.original_attempt_id, metadata);
|
|
5935
|
+
if (!outcome.ok) throw new Error(outcome.reason);
|
|
5936
|
+
return outcome.value;
|
|
5937
|
+
}
|
|
5938
|
+
if (payload.action === "record") {
|
|
5939
|
+
if (!payload.retrieval_status || !payload.failure_code || !payload.failure_stage) throw new PackUsageError("record action requires retrieval_status, failure_code, and failure_stage");
|
|
5940
|
+
const outcome = recordPromptPackTerminal(db, metadata, payload.retrieval_status, payload.failure_code, payload.failure_stage);
|
|
5941
|
+
if (!outcome.ok) throw new Error(outcome.reason);
|
|
5942
|
+
return outcome.value;
|
|
5943
|
+
}
|
|
5944
|
+
throw new PackUsageError("internal ledger action is invalid");
|
|
5945
|
+
}
|
|
5946
|
+
async function promptPackLedgerAction(opts) {
|
|
5947
|
+
await withStore(opts, "prompt_pack_ledger_failed", async (store) => {
|
|
5948
|
+
const payload = await readInternalLedgerPayload();
|
|
5949
|
+
handlePromptPackLedger(store.db, payload);
|
|
5950
|
+
});
|
|
5951
|
+
}
|
|
5952
|
+
var ledgerCmd = new Command("prompt-pack-ledger").description("Record internal prompt-pack lifecycle metadata").action(promptPackLedgerAction);
|
|
5953
|
+
addDbOption(ledgerCmd);
|
|
5954
|
+
var promptPackLedgerCommand = ledgerCmd;
|
|
5626
5955
|
//#endregion
|
|
5627
5956
|
//#region src/commands/recent.ts
|
|
5628
5957
|
var cmd = new Command("recent").configureHelp(helpStyle).description("Show recent memories").option("--limit <n>", "max results", "5").option("--project <project>", "project identifier (defaults to git repo root)").option("--all-projects", "search across all projects").option("--kind <kind>", "filter by memory kind");
|
|
@@ -6134,10 +6463,20 @@ function sqliteVecFailureDiagnostics(error, dbPath) {
|
|
|
6134
6463
|
}
|
|
6135
6464
|
async function runServeCoordinatorMaintenance(store, dependencies) {
|
|
6136
6465
|
const projectShares = await dependencies.advancePendingProjectShares(store, { limit: 3 });
|
|
6466
|
+
const coordinatorEnrollment = await dependencies.reconcileConfiguredCoordinatorEnrollment(store);
|
|
6467
|
+
const enrollmentIssues = coordinatorEnrollment.issues ?? 0;
|
|
6468
|
+
const recipientPolicies = await dependencies.reconcileRecipientPolicyProjects(store, { limit: 3 });
|
|
6137
6469
|
if (projectShares.failed > 0) throw new Error(`share operation maintenance failed for ${projectShares.failed} of ${projectShares.processed} operations`);
|
|
6470
|
+
if (coordinatorEnrollment.failedGroups > 0 || enrollmentIssues > 0) {
|
|
6471
|
+
const groupLabel = coordinatorEnrollment.failedGroups === 1 ? "group" : "groups";
|
|
6472
|
+
const issueLabel = enrollmentIssues === 1 ? "issue" : "issues";
|
|
6473
|
+
throw new Error(`coordinator enrollment maintenance failed for ${coordinatorEnrollment.failedGroups} ${groupLabel} with ${enrollmentIssues} reconciliation ${issueLabel}`);
|
|
6474
|
+
}
|
|
6475
|
+
if (recipientPolicies.failed > 0) throw new Error(`recipient policy maintenance failed for ${recipientPolicies.failed} of ${recipientPolicies.processed} projects`);
|
|
6138
6476
|
return {
|
|
6139
6477
|
projectShares,
|
|
6140
|
-
|
|
6478
|
+
coordinatorEnrollment,
|
|
6479
|
+
recipientPolicies
|
|
6141
6480
|
};
|
|
6142
6481
|
}
|
|
6143
6482
|
async function startBackgroundViewer(invocation) {
|
|
@@ -6172,7 +6511,7 @@ async function startBackgroundViewer(invocation) {
|
|
|
6172
6511
|
p.outro(`Viewer started in background (pid ${child.pid}) at http://${invocation.host}:${invocation.port}`);
|
|
6173
6512
|
}
|
|
6174
6513
|
async function startForegroundViewer(invocation) {
|
|
6175
|
-
const { advancePendingProjectShares, createApp, createSyncApp, closeStore, getStore, reconcileRecipientPolicyProjects } = await import("@codemem/server");
|
|
6514
|
+
const { advancePendingProjectShares, createApp, createSyncApp, closeStore, getStore, reconcileConfiguredCoordinatorEnrollment, reconcileRecipientPolicyProjects } = await import("@codemem/server");
|
|
6176
6515
|
const { serve } = await import("@hono/node-server");
|
|
6177
6516
|
if (invocation.dbPath) process.env.CODEMEM_DB = invocation.dbPath;
|
|
6178
6517
|
if (invocation.configPath) process.env.CODEMEM_CONFIG = invocation.configPath;
|
|
@@ -6266,6 +6605,7 @@ async function startForegroundViewer(invocation) {
|
|
|
6266
6605
|
onAfterCoordinatorRefresh: async () => {
|
|
6267
6606
|
await runServeCoordinatorMaintenance(store, {
|
|
6268
6607
|
advancePendingProjectShares,
|
|
6608
|
+
reconcileConfiguredCoordinatorEnrollment,
|
|
6269
6609
|
reconcileRecipientPolicyProjects
|
|
6270
6610
|
});
|
|
6271
6611
|
},
|
|
@@ -6875,9 +7215,38 @@ function fmtTokens(n) {
|
|
|
6875
7215
|
var statsCmd = new Command("stats").configureHelp(helpStyle).description("Show database statistics");
|
|
6876
7216
|
addDbOption(statsCmd);
|
|
6877
7217
|
addJsonOption(statsCmd);
|
|
7218
|
+
statsCmd.addOption(new Option("-a, --attribution", "show local retrieval attribution diagnostics"));
|
|
7219
|
+
function renderAttributionDiagnostics(report) {
|
|
7220
|
+
const evidence = report.evidenceCompleteness;
|
|
7221
|
+
return [
|
|
7222
|
+
"Retrieval attribution diagnostics (local, bounded)",
|
|
7223
|
+
`Lifecycle: ${report.lifecycle.requestedAttempts} requested, ${report.lifecycle.selectedAttempts} selected, ${report.lifecycle.handedOffAttempts} handed off`,
|
|
7224
|
+
`Exposures: ${report.lifecycle.selectedExposures} selected, ${report.lifecycle.handedOffExposures} handed off`,
|
|
7225
|
+
`Evidence: ${[
|
|
7226
|
+
`${evidence.assessedAttempts} assessed`,
|
|
7227
|
+
`${evidence.unassessedAttempts} unassessed`,
|
|
7228
|
+
`${evidence.assessedKnownAttempts} known`,
|
|
7229
|
+
`${evidence.assessedUnknownAttempts} unknown`,
|
|
7230
|
+
`${evidence.assessmentStatusIndeterminateAttempts} status indeterminate`,
|
|
7231
|
+
`${evidence.assessmentDetailsIncompleteAttempts} details incomplete`,
|
|
7232
|
+
`${evidence.assessmentRowsInvalid} invalid rows`,
|
|
7233
|
+
`${evidence.assessmentRowsOmittedByLimit} omitted by limit`
|
|
7234
|
+
].join(", ")}`,
|
|
7235
|
+
`Latency: ${report.overhead.latencySampleCount} samples, ${report.overhead.averageLatencyMs ?? "unknown"} ms average`,
|
|
7236
|
+
`Steering: ${report.sourceLocationSteering.assessmentCount} source-location assessments, ${report.sourceLocationSteering.matchedPathCount} matched paths`,
|
|
7237
|
+
`Findings: ${report.findings.stale} stale, ${report.findings.harmful} harmful`,
|
|
7238
|
+
"Limitations:",
|
|
7239
|
+
...report.limitations.map((limitation) => `- ${limitation}`)
|
|
7240
|
+
].join("\n");
|
|
7241
|
+
}
|
|
6878
7242
|
var statsCommand = statsCmd.action((opts) => {
|
|
6879
7243
|
const store = new MemoryStore(resolveDbPath(resolveDbOpt(opts)));
|
|
6880
7244
|
try {
|
|
7245
|
+
if (opts.attribution) {
|
|
7246
|
+
const report = getAttributionDiagnostics(store.db);
|
|
7247
|
+
console.log(opts.json ? JSON.stringify(report, null, 2) : renderAttributionDiagnostics(report));
|
|
7248
|
+
return;
|
|
7249
|
+
}
|
|
6881
7250
|
const result = store.stats();
|
|
6882
7251
|
if (opts.json) {
|
|
6883
7252
|
console.log(JSON.stringify(result, null, 2));
|
|
@@ -7981,6 +8350,7 @@ program.addCommand(embedCommand);
|
|
|
7981
8350
|
program.addCommand(recentCommand);
|
|
7982
8351
|
program.addCommand(searchCommand);
|
|
7983
8352
|
program.addCommand(packCommand);
|
|
8353
|
+
program.addCommand(promptPackLedgerCommand, { hidden: true });
|
|
7984
8354
|
program.addCommand(showMemoryCommand, { hidden: true });
|
|
7985
8355
|
program.addCommand(forgetMemoryCommand, { hidden: true });
|
|
7986
8356
|
program.addCommand(rememberMemoryCommand, { hidden: true });
|