@codemem/server 0.44.2 → 0.45.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +526 -2
- package/dist/index.js.map +1 -1
- package/dist/routes/memory-tools.d.ts +15 -0
- package/dist/routes/memory-tools.d.ts.map +1 -0
- package/dist/routes/raw-events.d.ts +1 -1
- package/dist/routes/raw-events.d.ts.map +1 -1
- package/package.json +3 -3
- package/static/app.js +2 -2
- package/static/app.js.br +0 -0
- package/static/app.js.gz +0 -0
- package/static/index.html +1 -1
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EACX,iCAAiC,EACjC,sBAAsB,EACtB,cAAc,EACd,YAAY,EACZ,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,WAAW,EAAE,KAAK,eAAe,EAAiB,OAAO,EAAE,MAAM,eAAe,CAAC;AAE1F,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,OAAO,EAEN,KAAK,0BAA0B,EAC/B,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EACX,iCAAiC,EACjC,sBAAsB,EACtB,cAAc,EACd,YAAY,EACZ,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,WAAW,EAAE,KAAK,eAAe,EAAiB,OAAO,EAAE,MAAM,eAAe,CAAC;AAE1F,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,OAAO,EAEN,KAAK,0BAA0B,EAC/B,MAAM,yBAAyB,CAAC;AAUjC,OAAO,EAAE,KAAK,iBAAiB,EAAkC,MAAM,kBAAkB,CAAC;AAC1F,OAAO,EACN,KAAK,gCAAgC,EACrC,KAAK,uCAAuC,EAG5C,MAAM,wBAAwB,CAAC;AAGhC,YAAY,EACX,iCAAiC,EACjC,kCAAkC,EAClC,sCAAsC,EACtC,sCAAsC,EACtC,8CAA8C,EAC9C,sCAAsC,GACtC,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACN,2BAA2B,EAC3B,4BAA4B,EAC5B,sCAAsC,EACtC,uCAAuC,EACvC,mCAAmC,EACnC,wCAAwC,EACxC,gCAAgC,GAChC,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EACX,oCAAoC,EACpC,2BAA2B,EAC3B,iCAAiC,EACjC,+BAA+B,EAC/B,uBAAuB,EACvB,8BAA8B,EAC9B,+BAA+B,EAC/B,+BAA+B,EAC/B,iCAAiC,EACjC,wCAAwC,EACxC,wBAAwB,EACxB,gCAAgC,EAChC,kCAAkC,EAClC,qBAAqB,GACrB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAAE,OAAO,EAAE,CAAC;AAKnB,uFAAuF;AACvF,wBAAgB,QAAQ,IAAI,WAAW,CAKtC;AAED,mDAAmD;AACnD,wBAAgB,UAAU,IAAI,IAAI,CAGjC;AAED;;;GAGG;AACH,MAAM,WAAW,UAAU;IAC1B,YAAY,CAAC,EAAE,MAAM,WAAW,CAAC;IACjC,OAAO,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;IACjC,QAAQ,CAAC,EAAE,cAAc,GAAG,IAAI,CAAC;IACjC,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,sBAAsB,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;IAC7E,qCAAqC,CAAC,EAAE,MAAM,OAAO,CAAC,iCAAiC,CAAC,CAAC;IACzF,sCAAsC,CAAC,EAAE,uCAAuC,CAAC;IACjF,+BAA+B,CAAC,EAAE,gCAAgC,GAAG,IAAI,CAAC;IAC1E,qBAAqB,CAAC,EAAE,iBAAiB,CAAC,uBAAuB,CAAC,CAAC;IACnE,sBAAsB,CAAC,EAAE,iBAAiB,CAAC,wBAAwB,CAAC,CAAC;IACrE,oBAAoB,CAAC,EAAE;QACtB,OAAO,CAAC,EAAE,0BAA0B,CAAC;QACrC,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,wBAAwB,CAAC,EAAE,MAAM,CAAC;QAClC,4BAA4B,CAAC,EAAE,MAAM,CAAC;KACtC,CAAC;IACF,oBAAoB,CAAC,EAAE,MAAM;QAC5B,KAAK,EACF,UAAU,GACV,SAAS,GACT,UAAU,GACV,OAAO,GACP,UAAU,GACV,iBAAiB,GACjB,iBAAiB,GACjB,IAAI,CAAC;QACR,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KACvB,GAAG,IAAI,CAAC;CACT;AAmCD,wBAAgB,SAAS,CAAC,IAAI,CAAC,EAAE,UAAU,8EAuD1C;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,IAAI,CAAC,EAAE,UAAU,8EA8B9C"}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import { dirname, isAbsolute, join, posix, resolve, win32 } from "node:path";
|
|
3
|
-
import { ACCESS_CLEANUP_OP_TYPE, CODEMEM_CONFIG_ENV_OVERRIDES, CoordinatorReciprocalApprovalRequestChangedError, DEFAULT_TIME_WINDOW_S, LEGACY_SHARED_REVIEW_SCOPE_ID, LOCAL_DEFAULT_SCOPE_ID, LOCAL_SYNC_CAPABILITY, LOCAL_SYNC_FEATURES, LegacyTeamSetupAdditiveConvergenceError, LegacyTeamSetupRosterCapacityError, MemoryStore, PROMPT_TRANSPORT_PROTOCOL_RANGE, RawEventIngestValidationError, RecipientPolicyEdgeRequestError, RecipientPolicyTeamRenameError, RemoteCoordinatorRequestError, SCOPE_MEMBERSHIP_REVOCATION_LIMITATION, SYNC_AUTHORIZATION_REFRESH_HEADER, SYNC_CAPABILITY_HEADER, SYNC_FEATURES_HEADER, SYNC_SCOPE_QUERY_PARAM, VERSION, VERSION as VERSION$1, VIEWER_IDENTITY_TARGET_KEYS, VIEWER_SERVICE_DISCRIMINATOR, addSyncScopeToBoundary, analyzeProjectScopeMappingChangeGuardrails, applyLegacyTeamSetupCompletionManifest, applyLegacyTeamSetupCompletionManifestAndReturnActivation, applyReplicationOps, areLegacyTeamSetupCompletionPolicyFactsAdditivelyCompatible, automaticRecallHealth, buildAuthHeaders, buildBaseUrl, buildDirectPeerAuthHeaders, buildFilterClausesWithContext, buildRawEventEnvelopeFromCodexHook, buildRawEventEnvelopeFromHook, buildViewerIdentityTarget, canonicalCoordinatorLegacyTeamCompletionManifestJson, canonicalMemoryKind, canonicalWorkspaceIdentity, claimRecipientPolicyActorMutations, claimRecipientPolicyPublicationMutation, cleanupNonces, clearLegacyTeamSetupDeviceDecision, clonePromptPackAttempt, coerceObserverCommand, commitDeviceIdentityBindings, commitRecipientPolicyEdges, containLegacyTeamSetupCompletionConflict, coordinatorArchiveGroupAction, coordinatorCreateAddDeviceInviteAction, coordinatorCreateGroupAction, coordinatorCreateInviteAction, coordinatorCreateLegacyTeamCompletionAction, coordinatorCreateScopeAction, coordinatorDisableDeviceAction, coordinatorEnableDeviceAction, coordinatorGetLegacyTeamCompletionAction, coordinatorGrantScopeMembershipAction, coordinatorImportInviteAction, coordinatorListBootstrapGrantsAction, coordinatorListConsumedTeamInvitesAction, coordinatorListDevicesAction, coordinatorListGroupsAction, coordinatorListJoinRequestsAction, coordinatorListLegacyTeamCompletionsAction, coordinatorListReviewedRecipientInviteEvidenceAction, coordinatorListScopeMembershipsAction, coordinatorListScopesAction, coordinatorRemoveDeviceAction, coordinatorRenameDeviceAction, coordinatorRenameGroupAction, coordinatorReviewJoinRequestAction, coordinatorRevokeBootstrapGrantAction, coordinatorRevokeScopeMembershipAction, coordinatorStatusSnapshot, coordinatorUnarchiveGroupAction, coordinatorUpdateScopeAction, countShareableProjectMemories, createCoordinatorReciprocalApproval, decodeInvitePayload, defaultSpaceScopeIdForGroup, deleteProjectScopeSettingsMapping, deriveLegacyTeamSetupCompletionManifest, detectInstallKind, deterministicPolicyTeamId, diagnoseStalePeerReceivedRows, discoverLegacyTeamCandidates, ensureDeviceIdentity, executeShareProvisioning, extractInvitePayload, extractReplicationOps, filterReplicationOpsForSyncWithStatus, fingerprintPublicKey, formatHostPort, friendlyDeviceName, fromJson, getCodememConfigPath, getCodememEnvOverrides, getCoordinatorEnrollmentReconciliationIssueSummary, getCoordinatorGroupPreference, getLegacyTeamSetupDraft, getRecipientPolicyAuthorityState, getSemanticIndexDiagnostics, getSyncResetState, getUpdateStatus, ingestRawEvents, inspectFreshLegacyTeamSetupActivation, inspectLegacyTeamSetupActivation, inviteTokenDigest, isAutomaticRecallMeasurement, isLegacyTeamCandidateSelectable, isLegacyTeamSetupProjectMappingIdentity, isPeerTrustBindingCompatible, isProjectSyncEnablementError, isScopedSyncCapability, legacyTeamCandidateId, legacyTeamCandidateProjectInventory, legacyTeamCanonicalProjectRef, legacyTeamDeviceRef, legacyTeamResolvedProjectRef, legacyTeamSetupApiErrorCode, listAuthorizedScopesForPeer, listCoordinatorJoinRequests, listDeviceIdentityInventory, listInboundScopeRejections, listLegacyRecipientPolicyProjections, listMaintenanceJobs, listObserverProviderOptions, listPerPeerScopeSyncState, listProjectScopeCandidates, listProjectScopeInventory, listProjectScopeSettingsMappings, listRecipientPolicyIntent, listRecipientPolicyReview, listSharingDomainSettingsScopes, loadMemorySnapshotPageForPeer, loadReplicationOpsForPeer, lookupCoordinatorPeers, mergeAddresses, migrateRecipientPolicyIntent, negotiateSyncCapability, normalizeAddress, normalizeHumanPresentationName, normalizeSyncCapability, normalizeSyncFeatures, normalizeTeammateName, parseAcceptedProjectIntent, parsePositiveMemoryId, parseReassignScopePayload, parseRecipientPolicyEdgeCommitRequest, parseStrictInteger, parseSyncScopeRequest, persistShareOperation, personalScopeGrantStatusForPeer, planShareOperation, previewDeviceIdentityBindings, previewLegacyTeamSetupActivation, previewRecipientPolicyEdges, previewRecipientPolicyOnboarding, previewRecipientPolicyOnboardingFromReviewedIntent, probeAvailableCredentials, projectShareLifecycle, promptPackArtifactFingerprint, readCodememConfigFile, readCoordinatorSyncConfig, reassignProjectScopeInventoryProject, recipientInviteAuthoritativeIdentityId, recipientPolicyDigest, recipientReviewedIntentDigest, reconcileCoordinatorEnrollmentSnapshot, reconcileRecipientPolicyProject, reconcileShareOperationAcceptance, reconstructLegacyTeamSetupCompletionManifest, recordAutomaticRecall, recordHighestObservedDirectSignatureVersion, recordNonce, recordPromptPackArtifacts, recordPromptPackTerminal, refreshAuthorizedCoordinatorPeerTrust, refreshConfiguredScopeMembershipCache, refreshLegacyTeamCandidate, rejectInboundScopeFailures, renameRecipientPolicyTeam, replayLegacyTeamSetupActivation, requestJson, requireLegacyTeamSetupAccessDeltaWithinLimit, resolveDbPath, resolveProject, resolveRecipientPolicyReview, resolveRecipientPolicyReviewBulk, runSyncPass, schema, serializeRecipientPolicyCoordinatorGroupMutation, serializeRecipientPolicyPublicationMutation, serializeRecipientPolicyTeamMutation, setLegacyTeamSetupDeviceAssignment, setLegacyTeamSetupDeviceDecision, setLegacyTeamSetupProjectMapping, summarizeInboundScopeRejections, summaryLikeSqlPredicate, supportsSyncFeature, syncScopeResetRequiredPayload, tryUpdateRetrievalDelivery, updatePeerAddresses, upsertCoordinatorGroupPreference, upsertProjectScopeSettingsMapping, validateLegacyTeamSetupCompletionManifest, validateLegacyTeamSetupCompletionManifestBinding, verifyDirectPeerSignature, verifyRecipientReviewedIntent, verifySignature, writeCodememConfigFile } from "@codemem/core";
|
|
3
|
+
import { ACCESS_CLEANUP_OP_TYPE, CODEMEM_CONFIG_ENV_OVERRIDES, CoordinatorReciprocalApprovalRequestChangedError, DEFAULT_TIME_WINDOW_S, LEGACY_SHARED_REVIEW_SCOPE_ID, LOCAL_DEFAULT_SCOPE_ID, LOCAL_SYNC_CAPABILITY, LOCAL_SYNC_FEATURES, LegacyTeamSetupAdditiveConvergenceError, LegacyTeamSetupRosterCapacityError, MEMORY_FILTER_FIELD_TYPES, MEMORY_FILTER_NAMES, MEMORY_KIND_DESCRIPTIONS, MemoryStore, ObserverClient, PROMPT_TRANSPORT_PROTOCOL_RANGE, REMEMBER_MEMORY_KINDS, RawEventIngestValidationError, RecipientPolicyEdgeRequestError, RecipientPolicyTeamRenameError, RemoteCoordinatorRequestError, SCOPE_MEMBERSHIP_REVOCATION_LIMITATION, SYNC_AUTHORIZATION_REFRESH_HEADER, SYNC_CAPABILITY_HEADER, SYNC_FEATURES_HEADER, SYNC_SCOPE_QUERY_PARAM, VERSION, VERSION as VERSION$1, VIEWER_IDENTITY_TARGET_KEYS, VIEWER_SERVICE_DISCRIMINATOR, addSyncScopeToBoundary, analyzeProjectScopeMappingChangeGuardrails, applyLegacyTeamSetupCompletionManifest, applyLegacyTeamSetupCompletionManifestAndReturnActivation, applyReplicationOps, areLegacyTeamSetupCompletionPolicyFactsAdditivelyCompatible, automaticRecallHealth, buildAuthHeaders, buildBaseUrl, buildDirectPeerAuthHeaders, buildDistillReport, buildFilterClausesWithContext, buildRawEventEnvelopeFromCodexHook, buildRawEventEnvelopeFromHook, buildRawEventEnvelopeFromPiEvent, buildViewerIdentityTarget, canonicalCoordinatorLegacyTeamCompletionManifestJson, canonicalMemoryKind, canonicalWorkspaceIdentity, claimRecipientPolicyActorMutations, claimRecipientPolicyPublicationMutation, cleanupNonces, clearLegacyTeamSetupDeviceDecision, clonePromptPackAttempt, coerceObserverCommand, commitDeviceIdentityBindings, commitRecipientPolicyEdges, containLegacyTeamSetupCompletionConflict, coordinatorArchiveGroupAction, coordinatorCreateAddDeviceInviteAction, coordinatorCreateGroupAction, coordinatorCreateInviteAction, coordinatorCreateLegacyTeamCompletionAction, coordinatorCreateScopeAction, coordinatorDisableDeviceAction, coordinatorEnableDeviceAction, coordinatorGetLegacyTeamCompletionAction, coordinatorGrantScopeMembershipAction, coordinatorImportInviteAction, coordinatorListBootstrapGrantsAction, coordinatorListConsumedTeamInvitesAction, coordinatorListDevicesAction, coordinatorListGroupsAction, coordinatorListJoinRequestsAction, coordinatorListLegacyTeamCompletionsAction, coordinatorListReviewedRecipientInviteEvidenceAction, coordinatorListScopeMembershipsAction, coordinatorListScopesAction, coordinatorRemoveDeviceAction, coordinatorRenameDeviceAction, coordinatorRenameGroupAction, coordinatorReviewJoinRequestAction, coordinatorRevokeBootstrapGrantAction, coordinatorRevokeScopeMembershipAction, coordinatorStatusSnapshot, coordinatorUnarchiveGroupAction, coordinatorUpdateScopeAction, countShareableProjectMemories, createCoordinatorReciprocalApproval, decodeInvitePayload, dedupeOrderedIds, defaultSpaceScopeIdForGroup, deleteProjectScopeSettingsMapping, deriveLegacyTeamSetupCompletionManifest, detectInstallKind, deterministicPolicyTeamId, diagnoseStalePeerReceivedRows, discoverLegacyTeamCandidates, ensureDeviceIdentity, executeShareProvisioning, extractInvitePayload, extractReplicationOps, filterReplicationOpsForSyncWithStatus, fingerprintPublicKey, formatHostPort, friendlyDeviceName, fromJson, getCodememConfigPath, getCodememEnvOverrides, getCoordinatorEnrollmentReconciliationIssueSummary, getCoordinatorGroupPreference, getLegacyTeamSetupDraft, getRecipientPolicyAuthorityState, getSemanticIndexDiagnostics, getSyncResetState, getUpdateStatus, ingestRawEvents, inspectFreshLegacyTeamSetupActivation, inspectLegacyTeamSetupActivation, inviteTokenDigest, isAutomaticRecallMeasurement, isLegacyTeamCandidateSelectable, isLegacyTeamSetupProjectMappingIdentity, isPeerTrustBindingCompatible, isProjectSyncEnablementError, isScopedSyncCapability, judgeDistillReport, legacyTeamCandidateId, legacyTeamCandidateProjectInventory, legacyTeamCanonicalProjectRef, legacyTeamDeviceRef, legacyTeamResolvedProjectRef, legacyTeamSetupApiErrorCode, listAuthorizedScopesForPeer, listCoordinatorJoinRequests, listDeviceIdentityInventory, listInboundScopeRejections, listLegacyRecipientPolicyProjections, listMaintenanceJobs, listObserverProviderOptions, listPerPeerScopeSyncState, listProjectScopeCandidates, listProjectScopeInventory, listProjectScopeSettingsMappings, listRecipientPolicyIntent, listRecipientPolicyReview, listSharingDomainSettingsScopes, loadMemorySnapshotPageForPeer, loadReplicationOpsForPeer, lookupCoordinatorPeers, memoryFilterValueMatchesType, mergeAddresses, migrateRecipientPolicyIntent, negotiateSyncCapability, normalizeAddress, normalizeHumanPresentationName, normalizeSyncCapability, normalizeSyncFeatures, normalizeTeammateName, parseAcceptedProjectIntent, parsePositiveMemoryId, parseReassignScopePayload, parseRecipientPolicyEdgeCommitRequest, parseStrictInteger, parseSyncScopeRequest, persistShareOperation, personalScopeGrantStatusForPeer, planShareOperation, previewDeviceIdentityBindings, previewLegacyTeamSetupActivation, previewRecipientPolicyEdges, previewRecipientPolicyOnboarding, previewRecipientPolicyOnboardingFromReviewedIntent, probeAvailableCredentials, projectMatchesFilter, projectShareLifecycle, promptPackArtifactFingerprint, readCodememConfigFile, readCoordinatorSyncConfig, reassignProjectScopeInventoryProject, recipientInviteAuthoritativeIdentityId, recipientPolicyDigest, recipientReviewedIntentDigest, reconcileCoordinatorEnrollmentSnapshot, reconcileRecipientPolicyProject, reconcileShareOperationAcceptance, reconstructLegacyTeamSetupCompletionManifest, recordAutomaticRecall, recordHighestObservedDirectSignatureVersion, recordNonce, recordPromptPackArtifacts, recordPromptPackTerminal, refreshAuthorizedCoordinatorPeerTrust, refreshConfiguredScopeMembershipCache, refreshLegacyTeamCandidate, rejectInboundScopeFailures, renameRecipientPolicyTeam, replayLegacyTeamSetupActivation, requestJson, requireLegacyTeamSetupAccessDeltaWithinLimit, resolveDbPath, resolveProject, resolveProjectRoot, resolveRecipientPolicyReview, resolveRecipientPolicyReviewBulk, runSyncPass, schema, serializeRecipientPolicyCoordinatorGroupMutation, serializeRecipientPolicyPublicationMutation, serializeRecipientPolicyTeamMutation, setLegacyTeamSetupDeviceAssignment, setLegacyTeamSetupDeviceDecision, setLegacyTeamSetupProjectMapping, storeVectors, summarizeInboundScopeRejections, summaryLikeSqlPredicate, supportsSyncFeature, syncScopeResetRequiredPayload, toJson, tryUpdateRetrievalDelivery, updatePeerAddresses, upsertCoordinatorGroupPreference, upsertProjectScopeSettingsMapping, validateLegacyTeamSetupCompletionManifest, validateLegacyTeamSetupCompletionManifestBinding, verifyDirectPeerSignature, verifyRecipientReviewedIntent, verifySignature, writeCodememConfigFile } from "@codemem/core";
|
|
4
4
|
import { serveStatic } from "@hono/node-server/serve-static";
|
|
5
5
|
import { Hono } from "hono";
|
|
6
6
|
import { createMiddleware } from "hono/factory";
|
|
@@ -1548,6 +1548,504 @@ function memoryRoutes(getStore) {
|
|
|
1548
1548
|
return app;
|
|
1549
1549
|
}
|
|
1550
1550
|
//#endregion
|
|
1551
|
+
//#region src/routes/memory-tools.ts
|
|
1552
|
+
/**
|
|
1553
|
+
* Memory tool-support routes — HTTP twins of MCP tools that the viewer
|
|
1554
|
+
* previously lacked (remember, timeline, expand, schema, search_index,
|
|
1555
|
+
* explain, distill_candidates). Used by thin clients (pi extension, CLI)
|
|
1556
|
+
* that prefer HTTP over opening the store in-process.
|
|
1557
|
+
*
|
|
1558
|
+
* Behavioral contracts mirror packages/mcp-server/src/tools/* against the
|
|
1559
|
+
* same @codemem/core MemoryStore APIs. No dependency on @codemem/mcp.
|
|
1560
|
+
*/
|
|
1561
|
+
var ALLOWED_REMEMBER_KINDS = new Set(REMEMBER_MEMORY_KINDS);
|
|
1562
|
+
var MEMORY_TOOLS_MAX_BODY_BYTES = 1048576;
|
|
1563
|
+
var SCHEMA_FIELDS = {
|
|
1564
|
+
title: "short text",
|
|
1565
|
+
body: "long text",
|
|
1566
|
+
subtitle: "short text",
|
|
1567
|
+
facts: "list<string>",
|
|
1568
|
+
narrative: "long text",
|
|
1569
|
+
concepts: "list<string>",
|
|
1570
|
+
files_read: "list<string>",
|
|
1571
|
+
files_modified: "list<string>",
|
|
1572
|
+
prompt_number: "int"
|
|
1573
|
+
};
|
|
1574
|
+
function cleanProject(value) {
|
|
1575
|
+
const trimmed = value?.trim();
|
|
1576
|
+
return trimmed ? trimmed : null;
|
|
1577
|
+
}
|
|
1578
|
+
function resolveWriteProject(input) {
|
|
1579
|
+
return cleanProject(input.project) ?? cleanProject(input.envProject) ?? null;
|
|
1580
|
+
}
|
|
1581
|
+
function buildFilters(raw, defaultProject = null) {
|
|
1582
|
+
const filters = {};
|
|
1583
|
+
let hasAny = false;
|
|
1584
|
+
if (raw.project != null && typeof raw.project !== "string") return {
|
|
1585
|
+
ok: false,
|
|
1586
|
+
error: "project must be a string"
|
|
1587
|
+
};
|
|
1588
|
+
const explicitProject = typeof raw.project === "string" ? cleanProject(raw.project) : void 0;
|
|
1589
|
+
const resolvedProject = explicitProject !== void 0 ? explicitProject || void 0 : cleanProject(defaultProject) || void 0;
|
|
1590
|
+
if (resolvedProject) {
|
|
1591
|
+
filters.project = resolvedProject;
|
|
1592
|
+
hasAny = true;
|
|
1593
|
+
}
|
|
1594
|
+
for (const [key, fieldType] of Object.entries(MEMORY_FILTER_FIELD_TYPES)) {
|
|
1595
|
+
if (key === "project") continue;
|
|
1596
|
+
const val = raw[key];
|
|
1597
|
+
if (val === void 0 || val === null) continue;
|
|
1598
|
+
if (key === "kind" && typeof val !== "string") return {
|
|
1599
|
+
ok: false,
|
|
1600
|
+
error: "kind must be a string"
|
|
1601
|
+
};
|
|
1602
|
+
if (!memoryFilterValueMatchesType(val, fieldType)) return {
|
|
1603
|
+
ok: false,
|
|
1604
|
+
error: `${key} has an invalid type`
|
|
1605
|
+
};
|
|
1606
|
+
filters[key] = val;
|
|
1607
|
+
hasAny = true;
|
|
1608
|
+
}
|
|
1609
|
+
return {
|
|
1610
|
+
ok: true,
|
|
1611
|
+
filters: hasAny ? filters : void 0
|
|
1612
|
+
};
|
|
1613
|
+
}
|
|
1614
|
+
/**
|
|
1615
|
+
* Parse MemoryFilters from a GET query string.
|
|
1616
|
+
*
|
|
1617
|
+
* Full filter surface (arrays / booleans / numbers matching MCP filterSchema)
|
|
1618
|
+
* is accepted via a single JSON-encoded `filters` query param so GET stays
|
|
1619
|
+
* ergonomic without multi-value keys. Top-level `project` and `kind` remain
|
|
1620
|
+
* as convenience aliases for existing callers and override the same keys in
|
|
1621
|
+
* the JSON object when both are present.
|
|
1622
|
+
*
|
|
1623
|
+
* Example:
|
|
1624
|
+
* /api/memories/search_index?query=foo&filters={"include_visibility":["private"]}
|
|
1625
|
+
*/
|
|
1626
|
+
function parseGetFilters(queryGetter) {
|
|
1627
|
+
const filterRaw = {};
|
|
1628
|
+
const filtersParam = queryGetter("filters");
|
|
1629
|
+
if (filtersParam != null && filtersParam.trim() !== "") {
|
|
1630
|
+
let parsed;
|
|
1631
|
+
try {
|
|
1632
|
+
parsed = JSON.parse(filtersParam);
|
|
1633
|
+
} catch {
|
|
1634
|
+
return {
|
|
1635
|
+
ok: false,
|
|
1636
|
+
error: "filters must be valid JSON"
|
|
1637
|
+
};
|
|
1638
|
+
}
|
|
1639
|
+
if (parsed == null || typeof parsed !== "object" || Array.isArray(parsed)) return {
|
|
1640
|
+
ok: false,
|
|
1641
|
+
error: "filters must be a JSON object"
|
|
1642
|
+
};
|
|
1643
|
+
Object.assign(filterRaw, parsed);
|
|
1644
|
+
}
|
|
1645
|
+
const project = queryGetter("project");
|
|
1646
|
+
const kind = queryGetter("kind");
|
|
1647
|
+
if (project != null) filterRaw.project = project;
|
|
1648
|
+
if (kind != null) filterRaw.kind = kind;
|
|
1649
|
+
return buildFilters(filterRaw);
|
|
1650
|
+
}
|
|
1651
|
+
function getMemoryForAccess(store, memoryId, filters) {
|
|
1652
|
+
return store.timeline(null, memoryId, 0, 0, filters ?? null).find((row) => row.id === memoryId) ?? null;
|
|
1653
|
+
}
|
|
1654
|
+
function getManyForAccess(store, ids, filters) {
|
|
1655
|
+
if (ids.length === 0) return [];
|
|
1656
|
+
const results = [];
|
|
1657
|
+
for (const id of ids) {
|
|
1658
|
+
const item = getMemoryForAccess(store, id, filters);
|
|
1659
|
+
if (item) results.push(item);
|
|
1660
|
+
}
|
|
1661
|
+
return results;
|
|
1662
|
+
}
|
|
1663
|
+
function clampInt(value, min, max) {
|
|
1664
|
+
return Math.min(max, Math.max(min, value));
|
|
1665
|
+
}
|
|
1666
|
+
function parseOptionalInt(value) {
|
|
1667
|
+
if (value == null) return null;
|
|
1668
|
+
if (typeof value === "number" && Number.isInteger(value)) return value;
|
|
1669
|
+
if (typeof value === "string") return parseStrictInteger(value);
|
|
1670
|
+
return null;
|
|
1671
|
+
}
|
|
1672
|
+
function parseOptionalBoolean(value) {
|
|
1673
|
+
if (value == null) return false;
|
|
1674
|
+
if (typeof value === "boolean") return value;
|
|
1675
|
+
if (typeof value === "string") {
|
|
1676
|
+
const normalized = value.trim().toLowerCase();
|
|
1677
|
+
if (normalized === "true" || normalized === "1" || normalized === "yes") return true;
|
|
1678
|
+
if (normalized === "false" || normalized === "0" || normalized === "no") return false;
|
|
1679
|
+
}
|
|
1680
|
+
return "invalid";
|
|
1681
|
+
}
|
|
1682
|
+
function parseJsonBody(body) {
|
|
1683
|
+
if (body == null || typeof body !== "object" || Array.isArray(body)) return {
|
|
1684
|
+
ok: false,
|
|
1685
|
+
error: "payload must be an object"
|
|
1686
|
+
};
|
|
1687
|
+
return {
|
|
1688
|
+
ok: true,
|
|
1689
|
+
value: body
|
|
1690
|
+
};
|
|
1691
|
+
}
|
|
1692
|
+
function rememberMemory(store, input) {
|
|
1693
|
+
return store.db.transaction(() => {
|
|
1694
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1695
|
+
const user = process.env.USER ?? "unknown";
|
|
1696
|
+
const cwd = process.cwd();
|
|
1697
|
+
const project = resolveWriteProject({
|
|
1698
|
+
project: input.project,
|
|
1699
|
+
envProject: process.env.CODEMEM_PROJECT
|
|
1700
|
+
});
|
|
1701
|
+
const sessionInfo = store.db.prepare(`INSERT INTO sessions(started_at, ended_at, cwd, project, user, tool_version, metadata_json)
|
|
1702
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`).run(now, now, cwd, project, user, "viewer-api", toJson({ viewer: true }));
|
|
1703
|
+
const sessionId = Number(sessionInfo.lastInsertRowid);
|
|
1704
|
+
const memId = store.remember(sessionId, input.kind, input.title, input.body, input.confidence);
|
|
1705
|
+
if (!getMemoryForAccess(store, memId)) throw new Error("unauthorized_scope");
|
|
1706
|
+
store.db.prepare("UPDATE sessions SET ended_at = ?, metadata_json = ? WHERE id = ?").run((/* @__PURE__ */ new Date()).toISOString(), toJson({ viewer: true }), sessionId);
|
|
1707
|
+
return {
|
|
1708
|
+
memId,
|
|
1709
|
+
title: input.title,
|
|
1710
|
+
body: input.body
|
|
1711
|
+
};
|
|
1712
|
+
})();
|
|
1713
|
+
}
|
|
1714
|
+
function readContextFile(path, displayPath, scope) {
|
|
1715
|
+
if (!existsSync(path)) return null;
|
|
1716
|
+
const text = readFileSync(path, "utf8");
|
|
1717
|
+
return text.trim() ? {
|
|
1718
|
+
path: displayPath,
|
|
1719
|
+
text,
|
|
1720
|
+
scope
|
|
1721
|
+
} : null;
|
|
1722
|
+
}
|
|
1723
|
+
function loadDefaultContextDocuments(includeProjectContext, cwd = process.cwd()) {
|
|
1724
|
+
const projectRoot = resolveProjectRoot(cwd) ?? cwd;
|
|
1725
|
+
return [includeProjectContext ? readContextFile(join(projectRoot, "AGENTS.md"), "AGENTS.md", "project") : null, readContextFile(join(homedir(), ".config", "opencode", "AGENTS.md"), "~/.config/opencode/AGENTS.md", "user")].filter((document) => document != null);
|
|
1726
|
+
}
|
|
1727
|
+
function shouldIncludeProjectContext(args, defaultProject) {
|
|
1728
|
+
if (args.all_projects) return false;
|
|
1729
|
+
const currentProject = resolveProject(process.cwd());
|
|
1730
|
+
if (!currentProject) return false;
|
|
1731
|
+
const explicitProject = typeof args.project === "string" ? args.project.trim() : "";
|
|
1732
|
+
const targetProject = explicitProject ? resolveProject(process.cwd(), explicitProject) : defaultProject;
|
|
1733
|
+
if (!targetProject) return false;
|
|
1734
|
+
return projectMatchesFilter(targetProject, currentProject);
|
|
1735
|
+
}
|
|
1736
|
+
function buildDistillFilters(args, defaultProject) {
|
|
1737
|
+
if (args.all_projects && typeof args.project === "string" && args.project.trim()) return {
|
|
1738
|
+
ok: false,
|
|
1739
|
+
error: "project cannot be combined with all_projects"
|
|
1740
|
+
};
|
|
1741
|
+
return buildFilters(args, args.all_projects ? null : defaultProject);
|
|
1742
|
+
}
|
|
1743
|
+
function mapSearchIndexItem(m) {
|
|
1744
|
+
return {
|
|
1745
|
+
id: m.id,
|
|
1746
|
+
kind: m.kind,
|
|
1747
|
+
title: m.title,
|
|
1748
|
+
score: m.score,
|
|
1749
|
+
created_at: m.created_at,
|
|
1750
|
+
session_id: m.session_id,
|
|
1751
|
+
metadata: m.metadata
|
|
1752
|
+
};
|
|
1753
|
+
}
|
|
1754
|
+
function expandMemories(store, args) {
|
|
1755
|
+
const resolvedProject = args.filters?.project ?? null;
|
|
1756
|
+
const { ordered: orderedIds, invalid: invalidIds } = dedupeOrderedIds(args.ids);
|
|
1757
|
+
const errors = [];
|
|
1758
|
+
if (invalidIds.length > 0) errors.push({
|
|
1759
|
+
code: "INVALID_ARGUMENT",
|
|
1760
|
+
field: "ids",
|
|
1761
|
+
message: "some ids are not valid integers",
|
|
1762
|
+
ids: invalidIds
|
|
1763
|
+
});
|
|
1764
|
+
const missingNotFound = [];
|
|
1765
|
+
const missingProjectMismatch = [];
|
|
1766
|
+
const missingFilterMismatch = [];
|
|
1767
|
+
const anchors = [];
|
|
1768
|
+
const timelineItems = [];
|
|
1769
|
+
const timelineSeen = /* @__PURE__ */ new Set();
|
|
1770
|
+
const sessionProjects = /* @__PURE__ */ new Map();
|
|
1771
|
+
for (const memoryId of orderedIds) {
|
|
1772
|
+
const item = store.get(memoryId);
|
|
1773
|
+
if (!item?.active) {
|
|
1774
|
+
missingNotFound.push(memoryId);
|
|
1775
|
+
continue;
|
|
1776
|
+
}
|
|
1777
|
+
const sessionId = item.session_id;
|
|
1778
|
+
if (resolvedProject && sessionId > 0) {
|
|
1779
|
+
if (!sessionProjects.has(sessionId)) {
|
|
1780
|
+
const row = store.db.prepare("SELECT project FROM sessions WHERE id = ? LIMIT 1").get(sessionId);
|
|
1781
|
+
sessionProjects.set(sessionId, typeof row?.project === "string" ? row.project : null);
|
|
1782
|
+
}
|
|
1783
|
+
if (!projectMatchesFilter(resolvedProject, sessionProjects.get(sessionId) ?? null)) {
|
|
1784
|
+
missingProjectMismatch.push(memoryId);
|
|
1785
|
+
continue;
|
|
1786
|
+
}
|
|
1787
|
+
} else if (resolvedProject && sessionId <= 0) {
|
|
1788
|
+
missingProjectMismatch.push(memoryId);
|
|
1789
|
+
continue;
|
|
1790
|
+
}
|
|
1791
|
+
const expanded = store.timeline(null, memoryId, args.depth_before, args.depth_after, args.filters);
|
|
1792
|
+
const anchor = expanded.find((expandedItem) => expandedItem.id === memoryId);
|
|
1793
|
+
if (!anchor) {
|
|
1794
|
+
missingFilterMismatch.push(memoryId);
|
|
1795
|
+
continue;
|
|
1796
|
+
}
|
|
1797
|
+
anchors.push(anchor);
|
|
1798
|
+
for (const expandedItem of expanded) {
|
|
1799
|
+
const expandedId = expandedItem.id;
|
|
1800
|
+
if (expandedId <= 0 || timelineSeen.has(expandedId)) continue;
|
|
1801
|
+
timelineSeen.add(expandedId);
|
|
1802
|
+
timelineItems.push(expandedItem);
|
|
1803
|
+
}
|
|
1804
|
+
}
|
|
1805
|
+
if (missingNotFound.length > 0) errors.push({
|
|
1806
|
+
code: "NOT_FOUND",
|
|
1807
|
+
field: "ids",
|
|
1808
|
+
message: "some requested ids were not found",
|
|
1809
|
+
ids: missingNotFound
|
|
1810
|
+
});
|
|
1811
|
+
if (missingProjectMismatch.length > 0) errors.push({
|
|
1812
|
+
code: "PROJECT_MISMATCH",
|
|
1813
|
+
field: "project",
|
|
1814
|
+
message: "some requested ids are outside the requested project scope",
|
|
1815
|
+
ids: missingProjectMismatch
|
|
1816
|
+
});
|
|
1817
|
+
if (missingFilterMismatch.length > 0) errors.push({
|
|
1818
|
+
code: "FILTER_MISMATCH",
|
|
1819
|
+
field: "filters",
|
|
1820
|
+
message: "some requested ids are outside the requested filters",
|
|
1821
|
+
ids: missingFilterMismatch
|
|
1822
|
+
});
|
|
1823
|
+
let observations = [];
|
|
1824
|
+
if (args.include_observations) {
|
|
1825
|
+
const observationSeen = /* @__PURE__ */ new Set();
|
|
1826
|
+
const observationIds = [];
|
|
1827
|
+
for (const item of [...anchors, ...timelineItems]) if (item.id > 0 && !observationSeen.has(item.id)) {
|
|
1828
|
+
observationSeen.add(item.id);
|
|
1829
|
+
observationIds.push(item.id);
|
|
1830
|
+
}
|
|
1831
|
+
observations = getManyForAccess(store, observationIds, args.filters);
|
|
1832
|
+
}
|
|
1833
|
+
return {
|
|
1834
|
+
anchors,
|
|
1835
|
+
timeline: timelineItems,
|
|
1836
|
+
observations,
|
|
1837
|
+
missing_ids: orderedIds.filter((memoryId) => missingNotFound.includes(memoryId) || missingProjectMismatch.includes(memoryId) || missingFilterMismatch.includes(memoryId)),
|
|
1838
|
+
errors,
|
|
1839
|
+
metadata: {
|
|
1840
|
+
project: resolvedProject,
|
|
1841
|
+
requested_ids_count: orderedIds.length,
|
|
1842
|
+
returned_anchor_count: anchors.length,
|
|
1843
|
+
timeline_count: timelineItems.length,
|
|
1844
|
+
include_observations: args.include_observations
|
|
1845
|
+
}
|
|
1846
|
+
};
|
|
1847
|
+
}
|
|
1848
|
+
function memoryToolRoutes(getStore) {
|
|
1849
|
+
const app = new Hono();
|
|
1850
|
+
app.post("/api/memories/remember", async (c) => {
|
|
1851
|
+
const store = getStore();
|
|
1852
|
+
const body = await parseJsonObjectBody(c, MEMORY_TOOLS_MAX_BODY_BYTES);
|
|
1853
|
+
if (body instanceof Response) return body;
|
|
1854
|
+
const parsed = parseJsonBody(body);
|
|
1855
|
+
if (!parsed.ok) return c.json({ error: parsed.error }, 400);
|
|
1856
|
+
const args = parsed.value;
|
|
1857
|
+
const kind = typeof args.kind === "string" ? args.kind.trim().toLowerCase() : "";
|
|
1858
|
+
if (!kind || !ALLOWED_REMEMBER_KINDS.has(kind)) return c.json({ error: `kind must be one of: ${[...ALLOWED_REMEMBER_KINDS].join(", ")}` }, 400);
|
|
1859
|
+
const title = typeof args.title === "string" ? args.title : "";
|
|
1860
|
+
const bodyText = typeof args.body === "string" ? args.body : "";
|
|
1861
|
+
if (!title.trim()) return c.json({ error: "title is required" }, 400);
|
|
1862
|
+
if (!bodyText.trim()) return c.json({ error: "body is required" }, 400);
|
|
1863
|
+
let confidence = .5;
|
|
1864
|
+
if (args.confidence != null) {
|
|
1865
|
+
if (typeof args.confidence !== "number" || Number.isNaN(args.confidence)) return c.json({ error: "confidence must be a number" }, 400);
|
|
1866
|
+
confidence = Math.min(1, Math.max(0, args.confidence));
|
|
1867
|
+
}
|
|
1868
|
+
const project = typeof args.project === "string" ? args.project : void 0;
|
|
1869
|
+
try {
|
|
1870
|
+
const result = rememberMemory(store, {
|
|
1871
|
+
kind,
|
|
1872
|
+
title,
|
|
1873
|
+
body: bodyText,
|
|
1874
|
+
confidence,
|
|
1875
|
+
project
|
|
1876
|
+
});
|
|
1877
|
+
try {
|
|
1878
|
+
await storeVectors(store.db, result.memId, result.title, result.body);
|
|
1879
|
+
} catch {}
|
|
1880
|
+
return c.json({ id: result.memId });
|
|
1881
|
+
} catch (err) {
|
|
1882
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1883
|
+
if (msg.includes("Invalid memory kind")) return c.json({ error: msg }, 400);
|
|
1884
|
+
if (msg === "unauthorized_scope") return c.json({ error: msg }, 403);
|
|
1885
|
+
return c.json({ error: msg }, 400);
|
|
1886
|
+
}
|
|
1887
|
+
});
|
|
1888
|
+
app.get("/api/memories/timeline", (c) => {
|
|
1889
|
+
const store = getStore();
|
|
1890
|
+
const query = c.req.query("query") || void 0;
|
|
1891
|
+
const memoryIdRaw = c.req.query("memory_id");
|
|
1892
|
+
const memoryId = memoryIdRaw != null && memoryIdRaw !== "" ? parseStrictInteger(memoryIdRaw) : null;
|
|
1893
|
+
if (memoryIdRaw != null && memoryIdRaw !== "" && memoryId == null) return c.json({ error: "memory_id must be int" }, 400);
|
|
1894
|
+
const depthBefore = clampInt(queryInt(c.req.query("depth_before"), 3), 0, 100);
|
|
1895
|
+
const depthAfter = clampInt(queryInt(c.req.query("depth_after"), 3), 0, 100);
|
|
1896
|
+
const parsedFilters = parseGetFilters((name) => c.req.query(name));
|
|
1897
|
+
if (!parsedFilters.ok) return c.json({ error: parsedFilters.error }, 400);
|
|
1898
|
+
const items = store.timeline(query ?? null, memoryId, depthBefore, depthAfter, parsedFilters.filters);
|
|
1899
|
+
return c.json({ items });
|
|
1900
|
+
});
|
|
1901
|
+
app.post("/api/memories/expand", async (c) => {
|
|
1902
|
+
const store = getStore();
|
|
1903
|
+
const body = await parseJsonObjectBody(c, MEMORY_TOOLS_MAX_BODY_BYTES);
|
|
1904
|
+
if (body instanceof Response) return body;
|
|
1905
|
+
const parsed = parseJsonBody(body);
|
|
1906
|
+
if (!parsed.ok) return c.json({ error: parsed.error }, 400);
|
|
1907
|
+
const args = parsed.value;
|
|
1908
|
+
if (!Array.isArray(args.ids)) return c.json({ error: "ids must be an array" }, 400);
|
|
1909
|
+
if (args.ids.length > 200) return c.json({ error: "ids must contain at most 200 entries" }, 400);
|
|
1910
|
+
const depthBeforeRaw = parseOptionalInt(args.depth_before);
|
|
1911
|
+
const depthAfterRaw = parseOptionalInt(args.depth_after);
|
|
1912
|
+
const depthBefore = clampInt(depthBeforeRaw ?? 3, 0, 100);
|
|
1913
|
+
const depthAfter = clampInt(depthAfterRaw ?? 3, 0, 100);
|
|
1914
|
+
const includeObservations = parseOptionalBoolean(args.include_observations);
|
|
1915
|
+
if (includeObservations === "invalid") return c.json({ error: "include_observations must be a boolean" }, 400);
|
|
1916
|
+
const parsedFilters = buildFilters(args, null);
|
|
1917
|
+
if (!parsedFilters.ok) return c.json({ error: parsedFilters.error }, 400);
|
|
1918
|
+
const value = expandMemories(store, {
|
|
1919
|
+
ids: args.ids,
|
|
1920
|
+
depth_before: depthBefore,
|
|
1921
|
+
depth_after: depthAfter,
|
|
1922
|
+
include_observations: includeObservations === true,
|
|
1923
|
+
filters: parsedFilters.filters
|
|
1924
|
+
});
|
|
1925
|
+
return c.json(value);
|
|
1926
|
+
});
|
|
1927
|
+
app.get("/api/memories/schema", (c) => {
|
|
1928
|
+
return c.json({
|
|
1929
|
+
kinds: Object.keys(MEMORY_KIND_DESCRIPTIONS),
|
|
1930
|
+
kind_descriptions: MEMORY_KIND_DESCRIPTIONS,
|
|
1931
|
+
fields: SCHEMA_FIELDS,
|
|
1932
|
+
filters: MEMORY_FILTER_NAMES
|
|
1933
|
+
});
|
|
1934
|
+
});
|
|
1935
|
+
app.get("/api/memories/search_index", (c) => {
|
|
1936
|
+
const store = getStore();
|
|
1937
|
+
const query = c.req.query("query") ?? "";
|
|
1938
|
+
if (!query.trim()) return c.json({ error: "query required" }, 400);
|
|
1939
|
+
const limit = clampInt(queryInt(c.req.query("limit"), 8), 1, 50);
|
|
1940
|
+
const parsedFilters = parseGetFilters((name) => c.req.query(name));
|
|
1941
|
+
if (!parsedFilters.ok) return c.json({ error: parsedFilters.error }, 400);
|
|
1942
|
+
const items = store.search(query, limit, parsedFilters.filters).map(mapSearchIndexItem);
|
|
1943
|
+
return c.json({ items });
|
|
1944
|
+
});
|
|
1945
|
+
app.post("/api/memories/explain", async (c) => {
|
|
1946
|
+
const store = getStore();
|
|
1947
|
+
const body = await parseJsonObjectBody(c, MEMORY_TOOLS_MAX_BODY_BYTES);
|
|
1948
|
+
if (body instanceof Response) return body;
|
|
1949
|
+
const parsed = parseJsonBody(body);
|
|
1950
|
+
if (!parsed.ok) return c.json({ error: parsed.error }, 400);
|
|
1951
|
+
const args = parsed.value;
|
|
1952
|
+
const query = typeof args.query === "string" ? args.query : null;
|
|
1953
|
+
let ids = null;
|
|
1954
|
+
if (args.ids != null) {
|
|
1955
|
+
if (!Array.isArray(args.ids)) return c.json({ error: "ids must be an array" }, 400);
|
|
1956
|
+
if (args.ids.length > 200) return c.json({ error: "ids must contain at most 200 entries" }, 400);
|
|
1957
|
+
const { ordered, invalid } = dedupeOrderedIds(args.ids);
|
|
1958
|
+
if (invalid.length > 0) return c.json({
|
|
1959
|
+
error: "some ids are not valid integers",
|
|
1960
|
+
ids: invalid
|
|
1961
|
+
}, 400);
|
|
1962
|
+
ids = ordered;
|
|
1963
|
+
}
|
|
1964
|
+
const limit = clampInt(parseOptionalInt(args.limit) ?? 10, 1, 50);
|
|
1965
|
+
const includePackContext = parseOptionalBoolean(args.include_pack_context);
|
|
1966
|
+
if (includePackContext === "invalid") return c.json({ error: "include_pack_context must be a boolean" }, 400);
|
|
1967
|
+
const parsedFilters = buildFilters(args);
|
|
1968
|
+
if (!parsedFilters.ok) return c.json({ error: parsedFilters.error }, 400);
|
|
1969
|
+
const result = store.explain(query, ids, limit, parsedFilters.filters, { includePackContext: includePackContext === true });
|
|
1970
|
+
return c.json(result);
|
|
1971
|
+
});
|
|
1972
|
+
app.post("/api/memories/distill_candidates", async (c) => {
|
|
1973
|
+
const store = getStore();
|
|
1974
|
+
const body = await parseJsonObjectBody(c, MEMORY_TOOLS_MAX_BODY_BYTES);
|
|
1975
|
+
if (body instanceof Response) return body;
|
|
1976
|
+
const parsed = parseJsonBody(body);
|
|
1977
|
+
if (!parsed.ok) return c.json({ error: parsed.error }, 400);
|
|
1978
|
+
const args = parsed.value;
|
|
1979
|
+
const limit = clampInt(parseOptionalInt(args.limit) ?? 10, 1, 50);
|
|
1980
|
+
const minRecurrence = clampInt(parseOptionalInt(args.min_recurrence) ?? 2, 1, 50);
|
|
1981
|
+
const allProjects = parseOptionalBoolean(args.all_projects);
|
|
1982
|
+
if (allProjects === "invalid") return c.json({ error: "all_projects must be a boolean" }, 400);
|
|
1983
|
+
const includeDocumented = parseOptionalBoolean(args.include_documented);
|
|
1984
|
+
if (includeDocumented === "invalid") return c.json({ error: "include_documented must be a boolean" }, 400);
|
|
1985
|
+
const maxEvidenceItems = clampInt(parseOptionalInt(args.max_evidence_items) ?? 5, 1, 20);
|
|
1986
|
+
const judgeParsed = args.judge === void 0 ? true : parseOptionalBoolean(args.judge);
|
|
1987
|
+
if (judgeParsed === "invalid") return c.json({ error: "judge must be a boolean" }, 400);
|
|
1988
|
+
const judge = judgeParsed === true;
|
|
1989
|
+
try {
|
|
1990
|
+
const resolvedDefaultProject = cleanProject(typeof args.project === "string" ? args.project : null) ?? cleanProject(process.env.CODEMEM_PROJECT);
|
|
1991
|
+
const parsedFilters = buildDistillFilters({
|
|
1992
|
+
...args,
|
|
1993
|
+
all_projects: allProjects
|
|
1994
|
+
}, resolvedDefaultProject);
|
|
1995
|
+
if (!parsedFilters.ok) return c.json({ error: parsedFilters.error }, 400);
|
|
1996
|
+
const kinds = typeof args.kind === "string" && args.kind.trim() ? [args.kind] : void 0;
|
|
1997
|
+
const fetchLimit = judge ? Math.min(limit * 3, limit + 20) : limit;
|
|
1998
|
+
let result = await buildDistillReport(store, {
|
|
1999
|
+
candidate: {
|
|
2000
|
+
includeDocumented,
|
|
2001
|
+
maxEvidenceItems
|
|
2002
|
+
},
|
|
2003
|
+
contextDocuments: loadDefaultContextDocuments(shouldIncludeProjectContext({
|
|
2004
|
+
all_projects: allProjects,
|
|
2005
|
+
project: args.project
|
|
2006
|
+
}, resolvedDefaultProject)),
|
|
2007
|
+
corpus: {
|
|
2008
|
+
filters: parsedFilters.filters ?? null,
|
|
2009
|
+
kinds
|
|
2010
|
+
},
|
|
2011
|
+
limit: fetchLimit,
|
|
2012
|
+
minRecurrence
|
|
2013
|
+
});
|
|
2014
|
+
if (judge) {
|
|
2015
|
+
try {
|
|
2016
|
+
const client = new ObserverClient();
|
|
2017
|
+
result = await judgeDistillReport(result, async (system, user) => {
|
|
2018
|
+
return (await client.observe(system, user)).raw;
|
|
2019
|
+
});
|
|
2020
|
+
} catch (err) {
|
|
2021
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2022
|
+
result = {
|
|
2023
|
+
...result,
|
|
2024
|
+
metadata: {
|
|
2025
|
+
...result.metadata,
|
|
2026
|
+
judged: false,
|
|
2027
|
+
judge_error: message
|
|
2028
|
+
}
|
|
2029
|
+
};
|
|
2030
|
+
}
|
|
2031
|
+
if (result.candidates.length > limit) result = {
|
|
2032
|
+
...result,
|
|
2033
|
+
candidates: result.candidates.slice(0, limit),
|
|
2034
|
+
metadata: {
|
|
2035
|
+
...result.metadata,
|
|
2036
|
+
candidate_count: limit
|
|
2037
|
+
}
|
|
2038
|
+
};
|
|
2039
|
+
}
|
|
2040
|
+
return c.json(result);
|
|
2041
|
+
} catch (err) {
|
|
2042
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2043
|
+
return c.json({ error: msg }, 400);
|
|
2044
|
+
}
|
|
2045
|
+
});
|
|
2046
|
+
return app;
|
|
2047
|
+
}
|
|
2048
|
+
//#endregion
|
|
1551
2049
|
//#region src/routes/observer-status.ts
|
|
1552
2050
|
function normalizeActiveObserver(active) {
|
|
1553
2051
|
if (!active) return null;
|
|
@@ -2028,7 +2526,7 @@ function packTransportRoutes(getStore) {
|
|
|
2028
2526
|
//#region src/routes/raw-events.ts
|
|
2029
2527
|
/**
|
|
2030
2528
|
* Raw events routes — GET & POST /api/raw-events, GET /api/raw-events/status,
|
|
2031
|
-
* POST /api/claude-hooks, POST /api/codex-hooks.
|
|
2529
|
+
* POST /api/claude-hooks, POST /api/codex-hooks, POST /api/pi-hooks.
|
|
2032
2530
|
*/
|
|
2033
2531
|
var DEFAULT_MAX_RAW_EVENTS_BODY_BYTES = 1048576;
|
|
2034
2532
|
function configuredMaxRawEventsBodyBytes() {
|
|
@@ -2271,6 +2769,31 @@ function rawEventsRoutes(getStore, sweeper) {
|
|
|
2271
2769
|
return boundedIngestErrorResponse(c, err);
|
|
2272
2770
|
}
|
|
2273
2771
|
});
|
|
2772
|
+
app.post("/api/pi-hooks", async (c) => {
|
|
2773
|
+
const result = await parseJsonObjectBody(c, MAX_RAW_EVENTS_BODY_BYTES);
|
|
2774
|
+
if (result instanceof Response) return result;
|
|
2775
|
+
const payload = result;
|
|
2776
|
+
try {
|
|
2777
|
+
const store = getStore();
|
|
2778
|
+
const target = validateViewerTarget(store, payload, { requirePairedTargets: true });
|
|
2779
|
+
if (!target.ok) return c.json(target.body, target.status);
|
|
2780
|
+
const envelope = buildRawEventEnvelopeFromPiEvent(untargetedPayload(payload));
|
|
2781
|
+
if (envelope === null) return c.json({
|
|
2782
|
+
inserted: 0,
|
|
2783
|
+
skipped: 1
|
|
2784
|
+
});
|
|
2785
|
+
const ingestResult = await ingestNormalizedEnvelope(store, sweeper, {
|
|
2786
|
+
...envelope,
|
|
2787
|
+
source: "pi"
|
|
2788
|
+
});
|
|
2789
|
+
return c.json({
|
|
2790
|
+
inserted: ingestResult.inserted,
|
|
2791
|
+
skipped: ingestResult.skipped
|
|
2792
|
+
});
|
|
2793
|
+
} catch (err) {
|
|
2794
|
+
return boundedIngestErrorResponse(c, err);
|
|
2795
|
+
}
|
|
2796
|
+
});
|
|
2274
2797
|
return app;
|
|
2275
2798
|
}
|
|
2276
2799
|
//#endregion
|
|
@@ -10649,6 +11172,7 @@ function createApp(opts) {
|
|
|
10649
11172
|
app.route("/", diagnosticsRoutes(storeFactory));
|
|
10650
11173
|
app.route("/", statsRoutes(storeFactory));
|
|
10651
11174
|
app.route("/", memoryRoutes(storeFactory));
|
|
11175
|
+
app.route("/", memoryToolRoutes(storeFactory));
|
|
10652
11176
|
app.route("/", packTransportRoutes(storeFactory));
|
|
10653
11177
|
app.route("/", observerStatusRoutes({
|
|
10654
11178
|
getStore: storeFactory,
|