zam-core 0.7.1 → 0.7.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 CHANGED
@@ -562,6 +562,7 @@ interface Prerequisite {
562
562
  /** A prerequisite row joined with the token it points to. */
563
563
  interface PrerequisiteWithToken extends Prerequisite {
564
564
  slug: string;
565
+ title: string;
565
566
  concept: string;
566
567
  domain: string;
567
568
  bloom_level: number;
@@ -603,6 +604,7 @@ declare function getDependents(db: Database, tokenId: string): Promise<Prerequis
603
604
  interface NeighborhoodToken {
604
605
  id: string;
605
606
  slug: string;
607
+ title: string;
606
608
  concept: string;
607
609
  domain: string;
608
610
  bloom_level: number;
@@ -802,6 +804,7 @@ type SymbiosisMode = "shadowing" | "copilot" | "autonomy";
802
804
  interface Token {
803
805
  id: string;
804
806
  slug: string;
807
+ title: string;
805
808
  concept: string;
806
809
  domain: string;
807
810
  bloom_level: BloomLevel$1;
@@ -817,6 +820,7 @@ interface Token {
817
820
  }
818
821
  interface CreateTokenInput {
819
822
  slug: string;
823
+ title?: string;
820
824
  concept: string;
821
825
  domain?: string;
822
826
  bloom_level?: BloomLevel$1;
@@ -828,6 +832,7 @@ interface CreateTokenInput {
828
832
  topic_id?: string | null;
829
833
  }
830
834
  interface UpdateTokenInput {
835
+ title?: string | null;
831
836
  concept?: string;
832
837
  domain?: string;
833
838
  bloom_level?: BloomLevel$1;
@@ -840,6 +845,11 @@ interface UpdateTokenInput {
840
845
  }
841
846
  interface ListTokensOptions {
842
847
  domain?: string;
848
+ /**
849
+ * Filter by domain prefix using `/` as separator (e.g. "docuware-cops").
850
+ * Matches exact or startsWith(prefix + "/").
851
+ */
852
+ domainPrefix?: string;
843
853
  }
844
854
  interface TokenDeleteImpact {
845
855
  cards: number;
@@ -915,6 +925,7 @@ declare function listTokens(db: Database, options?: ListTokensOptions): Promise<
915
925
  interface PersonalCard {
916
926
  tokenId: string;
917
927
  slug: string;
928
+ title: string;
918
929
  concept: string;
919
930
  domain: string;
920
931
  bloomLevel: BloomLevel$1;
@@ -938,6 +949,18 @@ interface PersonalCard {
938
949
  topicId: string | null;
939
950
  }
940
951
  declare function slugify(text: string): string;
952
+ /**
953
+ * Strip domain prefix (using / separator) from slug for display.
954
+ */
955
+ declare function getShortSlug(slug: string, domainPrefix?: string | null): string;
956
+ /**
957
+ * Primary display name for a token: human title if present, else short slug.
958
+ * Never falls back to concept (which is a spoiler).
959
+ */
960
+ declare function getDisplayTitle(t: {
961
+ title?: string | null;
962
+ slug: string;
963
+ }, activeDomainScope?: string | null): string;
941
964
  declare function generateTokenSlug(db: Database, domain: string, concept: string, question?: string | null): Promise<string>;
942
965
  declare function listPersonalCards(db: Database, userId: string, options?: {
943
966
  query?: string;
@@ -946,6 +969,7 @@ declare function listPersonalCards(db: Database, userId: string, options?: {
946
969
  interface CurriculumCardInput {
947
970
  question: string;
948
971
  concept: string;
972
+ title?: string;
949
973
  domain: string;
950
974
  source_link?: string | null;
951
975
  context?: string;
@@ -982,6 +1006,7 @@ interface FoundationProposalInput {
982
1006
  question: string;
983
1007
  concept: string;
984
1008
  domain: string;
1009
+ title?: string;
985
1010
  context?: string;
986
1011
  bloom_level?: number;
987
1012
  symbiosis_mode?: string | null;
@@ -1002,6 +1027,7 @@ interface SourceProposalInput {
1002
1027
  question: string;
1003
1028
  concept: string;
1004
1029
  domain: string;
1030
+ title?: string;
1005
1031
  bloom_level: number;
1006
1032
  symbiosis_mode: string;
1007
1033
  excerpt: string;
@@ -1054,7 +1080,9 @@ interface EmbeddedTokenRow {
1054
1080
  * vector derives from exactly this string — never the slug, which is an
1055
1081
  * identifier, not meaning.
1056
1082
  */
1057
- declare function embeddingContentForToken(t: Pick<Token, "concept" | "question" | "domain">): string;
1083
+ declare function embeddingContentForToken(t: Pick<Token, "concept" | "question" | "domain"> & {
1084
+ title?: string | null;
1085
+ }): string;
1058
1086
  declare function computeContentHash(text: string): string;
1059
1087
  /**
1060
1088
  * Encode a vector as a little-endian float32 BLOB. Builds a fresh buffer (no
@@ -1855,6 +1883,7 @@ interface ReviewQueueItem {
1855
1883
  cardId: string;
1856
1884
  tokenId: string;
1857
1885
  slug: string;
1886
+ title: string;
1858
1887
  concept: string;
1859
1888
  domain: string;
1860
1889
  bloomLevel: number;
@@ -2233,4 +2262,4 @@ declare function resolveAllBeliefPaths(db: Database): Promise<string[]>;
2233
2262
  */
2234
2263
  declare function resolveAllGoalPaths(db: Database): Promise<string[]>;
2235
2264
 
2236
- export { type ADOConfig, type ADOCredentials, type AgentSkill, type AnalysisResult, type ApplySessionSynthesisInput, type ApplySessionSynthesisResult, BUILT_IN_SENSITIVE_MATCHERS, type BloomLevel$1 as BloomLevel, type CaptureDecision, type CaptureDenialReason, type CaptureRequest, type Card, type CardDeletionImpact, type CardState$1 as CardState, type CascadeBlockResult, type CommandRecord, type CommandSequence, type ConfirmFoundationsResult, type ConnectionOptions, type CreateAgentSkillInput, type CreateGoalInput, type CreateReviewInput, type CreateSessionInput, type CreateTokenInput, type Credentials, type CurriculumCardInput, DEFAULT_OBSERVER_POLICY, DEFAULT_REVIEW_CONTEXT_MAX_CHARS, type Database, type DatabaseProvider, type DatabaseTargetInfo, type DatabaseValue, type DeleteCardResult, type DeleteTokenResult, type DiscoveryOptions, type DomainCompetence, type EmbeddedTokenRow, type EmbeddingCoverage, type EmbeddingStaleness, type EvaluateInput, type EvaluateResult, type ExecuteReviewActionInput, type ExecutionContext, type FSRSParameters, type FoundationProposalInput, type FoundationSuggestion, type Goal, type GoalFrontmatter, type GoalStatus, type GoalSummary, HOMEBREW_CASK, type HybridScoredToken, type HybridSearchOptions, type ImportCurriculumResult, type ImportResult, type InstallChannel, type InstallConfig, type InstallMode, type InstallPlan, type InstallResult, type LocalLLMRunner, type LogStepInput, type MachineAiConfig, type MachineProviderRecord, type MachineRoleBinding, type MonitorEvent, type Neighborhood, type NeighborhoodToken, OBSERVER_POLICY_UNSET_HINT, OBSERVER_POLICY_VERSION, type ObservationRating, type ObserverConsent, type ObserverPolicy, type ObserverRetention, type ObserverScope, type ObserverSettingKey, type OllamaDetectionOptions, type PersonalCard, type PrepareSessionSynthesisInput, type Prerequisite, type PrerequisiteWithToken, type PromptInput, REVIEW_CONTEXT_CACHE_TTL_MS, type Rating, type RecallPrompt, type RemoteDatabaseOptions, type RepoPaths, type ResolvedCaptureTarget, type ResolvedReference, type ReviewActionResult, type ReviewActionType, type ReviewContext, type ReviewLog, type ReviewQueue, type ReviewQueueItem, type ReviewQueueOptions, type RunResult, SIDECAR_POLICY_FILE, SNAPSHOT_VERSION, type SchedulingCard, type Session, type SessionStep, type SessionSummary, type SessionSynthesisCandidate, type SessionSynthesisEvidence, type SessionSynthesisPreview, type SessionSynthesisRecord, type SidecarPrivacyPolicy, type SkillProposal, type SkillSource, type SnapshotManifest, type SourceProposalInput, type SplitProposalInput, type Statement, type SuggestFoundationsOptions, type SupportedLocale, type SymbiosisMode, type SynthesisConfidence, type SystemProfile, type Token, type TokenDeleteImpact, type TokenEmbedding, type TokenNeedingEmbedding, type TokenPattern, type TranslationKey, type TursoCredentials, UI_OBSERVATION_PROTOCOL_VERSION, type UiActionType, type UiApplicationContext, type UiCandidateToken, type UiEvidenceRef, type UiEvidenceType, type UiObservationKind, type UiObservationReport, type UiObservedAction, type UnblockResult, type UpdateActionKind, type UpdateCardInput, type UpdateDecision, type UpdateStep, type UpdateStepKind, type UpdateTokenInput, type UserSetting, type UserStats, WINGET_PACKAGE_ID, type WorkItem, type WorkspaceConfig, type WorkspaceKind, type WorkspaceSourceControl, addPrerequisite, analyzeObservation, appendUiObservationReport, applySessionSynthesis, buildAncestorMap, buildReviewQueue, buildUiSynthesisCandidates, cascadeBlock, clearADOCredentials, clearProviderApiKey, clearReviewContextCache, clearTursoCredentials, compareVersions, computeContentHash, confirmCardSplit, confirmFoundations, confirmSourceImport, cosineSimilarity, createAgentSkill, createFSRS, createGoal, createToken, decidePostCapture, decidePreCapture, decideUpdate, decodeEmbedding, deleteCardForUser, deleteSetting, deleteToken, deprecateToken, detectSyncProvider, detectSystemLocale, discoverSkills, distributeGlobalSkills, embeddingContentForToken, encodeEmbedding, endSession, ensureCard, ensureMonitorDir, ensureUiObserverDir, evaluateRating, executeReviewAction, exportSnapshot, extractTasks, extractTokenRefs, fetchActiveWorkItems, findTokens, generateBashHooks, generateBashUnhooks, generateConceptFreeCue, generatePowerShellHooks, generatePowerShellUnhooks, generatePrompt, generateTokenSlug, generateZshHooks, generateZshUnhooks, getADOCredentials, getActiveWorkspace, getActiveWorkspaceId, getAgentSkill, getAllSettings, getAllSettingsDetailed, getBlockedCards, getCard, getCardById, getCardDeletionImpact, getConfiguredWorkspaces, getDatabaseTargetInfo, getDefaultDbPath, getDependents, getDomainCompetence, getDueCards, getEmbeddingCoverage, getGoal, getGoalTree, getInstallChannel, getInstallMode, getMachineAiConfig, getMonitorDir, getMonitorLogStats, getMonitorPath, getPackageSkillPath, getPrerequisites, getProviderApiKey, getRepoPaths, getReviewsForCard, getReviewsForUser, getSessionSummary, getSessionSynthesisRecords, getSetting, getSystemProfile, getTokenById, getTokenBySlug, getTokenDeleteImpact, getTokenEmbedding, getTokenNeighborhood, getTursoCredentials, getUiObservationPath, getUiObserverDir, getUserStats, hasCommand, importCurriculumCards, importSnapshot, injectShellHooks, installFastFlowLM, installOllama, installOpenCode, interleave, isObserverPolicyConfigured, isOllamaInstalled, isUiObservationReport, listAgentSkills, listEmbeddedTokens, listGoals, listPersonalCards, listProviderApiKeyRefs, listTokens, listTokensNeedingEmbedding, loadADOConfig, loadCredentials, loadInstallConfig, logReview, logStep, matchBuiltInSensitive, matchDenylist, matchesFilePath, monitorLogExists, normalizeLocale, normalizePath, openDatabase, openDatabaseWithSync, openRemoteDatabase, pairCommands, parseGoalFile, parseMonitorLog, parseObserverList, parseObserverPolicy, parseSnapshot, parseUiObservationLog, planOpenCodeInstall, planUpdate, prepareLocalModel, prepareSessionSynthesis, readMonitorLog, readUiObservationLog, removeConfiguredWorkspace, resolveAllBeliefPaths, resolveAllGoalPaths, resolveObserverPolicy, resolveOllamaCommand, resolveReference, resolveRepoPath, resolveReviewContext, saveConfiguredWorkspaces, saveCredentials, saveInstallConfig, saveMachineAiConfig, searchTokensHybrid, serializeGoal, setADOCredentials, setActiveWorkspaceId, setInstallChannel, setInstallMode, setProviderApiKey, setSetting, setTursoCredentials, slugify, startSession, suggestFoundations, syncObserverSidecarPolicy, t, toSidecarPrivacyPolicy, uiObservationLogExists, uiObservationTimeSpan, unblockReady, updateCard, updateGoalStatus, updateToken, upsertConfiguredWorkspace, upsertTokenEmbedding, verifySnapshot, wouldCreateCycle, writeMonitorEvent };
2265
+ export { type ADOConfig, type ADOCredentials, type AgentSkill, type AnalysisResult, type ApplySessionSynthesisInput, type ApplySessionSynthesisResult, BUILT_IN_SENSITIVE_MATCHERS, type BloomLevel$1 as BloomLevel, type CaptureDecision, type CaptureDenialReason, type CaptureRequest, type Card, type CardDeletionImpact, type CardState$1 as CardState, type CascadeBlockResult, type CommandRecord, type CommandSequence, type ConfirmFoundationsResult, type ConnectionOptions, type CreateAgentSkillInput, type CreateGoalInput, type CreateReviewInput, type CreateSessionInput, type CreateTokenInput, type Credentials, type CurriculumCardInput, DEFAULT_OBSERVER_POLICY, DEFAULT_REVIEW_CONTEXT_MAX_CHARS, type Database, type DatabaseProvider, type DatabaseTargetInfo, type DatabaseValue, type DeleteCardResult, type DeleteTokenResult, type DiscoveryOptions, type DomainCompetence, type EmbeddedTokenRow, type EmbeddingCoverage, type EmbeddingStaleness, type EvaluateInput, type EvaluateResult, type ExecuteReviewActionInput, type ExecutionContext, type FSRSParameters, type FoundationProposalInput, type FoundationSuggestion, type Goal, type GoalFrontmatter, type GoalStatus, type GoalSummary, HOMEBREW_CASK, type HybridScoredToken, type HybridSearchOptions, type ImportCurriculumResult, type ImportResult, type InstallChannel, type InstallConfig, type InstallMode, type InstallPlan, type InstallResult, type ListTokensOptions, type LocalLLMRunner, type LogStepInput, type MachineAiConfig, type MachineProviderRecord, type MachineRoleBinding, type MonitorEvent, type Neighborhood, type NeighborhoodToken, OBSERVER_POLICY_UNSET_HINT, OBSERVER_POLICY_VERSION, type ObservationRating, type ObserverConsent, type ObserverPolicy, type ObserverRetention, type ObserverScope, type ObserverSettingKey, type OllamaDetectionOptions, type PersonalCard, type PrepareSessionSynthesisInput, type Prerequisite, type PrerequisiteWithToken, type PromptInput, REVIEW_CONTEXT_CACHE_TTL_MS, type Rating, type RecallPrompt, type RemoteDatabaseOptions, type RepoPaths, type ResolvedCaptureTarget, type ResolvedReference, type ReviewActionResult, type ReviewActionType, type ReviewContext, type ReviewLog, type ReviewQueue, type ReviewQueueItem, type ReviewQueueOptions, type RunResult, SIDECAR_POLICY_FILE, SNAPSHOT_VERSION, type SchedulingCard, type Session, type SessionStep, type SessionSummary, type SessionSynthesisCandidate, type SessionSynthesisEvidence, type SessionSynthesisPreview, type SessionSynthesisRecord, type SidecarPrivacyPolicy, type SkillProposal, type SkillSource, type SnapshotManifest, type SourceProposalInput, type SplitProposalInput, type Statement, type SuggestFoundationsOptions, type SupportedLocale, type SymbiosisMode, type SynthesisConfidence, type SystemProfile, type Token, type TokenDeleteImpact, type TokenEmbedding, type TokenNeedingEmbedding, type TokenPattern, type TranslationKey, type TursoCredentials, UI_OBSERVATION_PROTOCOL_VERSION, type UiActionType, type UiApplicationContext, type UiCandidateToken, type UiEvidenceRef, type UiEvidenceType, type UiObservationKind, type UiObservationReport, type UiObservedAction, type UnblockResult, type UpdateActionKind, type UpdateCardInput, type UpdateDecision, type UpdateStep, type UpdateStepKind, type UpdateTokenInput, type UserSetting, type UserStats, WINGET_PACKAGE_ID, type WorkItem, type WorkspaceConfig, type WorkspaceKind, type WorkspaceSourceControl, addPrerequisite, analyzeObservation, appendUiObservationReport, applySessionSynthesis, buildAncestorMap, buildReviewQueue, buildUiSynthesisCandidates, cascadeBlock, clearADOCredentials, clearProviderApiKey, clearReviewContextCache, clearTursoCredentials, compareVersions, computeContentHash, confirmCardSplit, confirmFoundations, confirmSourceImport, cosineSimilarity, createAgentSkill, createFSRS, createGoal, createToken, decidePostCapture, decidePreCapture, decideUpdate, decodeEmbedding, deleteCardForUser, deleteSetting, deleteToken, deprecateToken, detectSyncProvider, detectSystemLocale, discoverSkills, distributeGlobalSkills, embeddingContentForToken, encodeEmbedding, endSession, ensureCard, ensureMonitorDir, ensureUiObserverDir, evaluateRating, executeReviewAction, exportSnapshot, extractTasks, extractTokenRefs, fetchActiveWorkItems, findTokens, generateBashHooks, generateBashUnhooks, generateConceptFreeCue, generatePowerShellHooks, generatePowerShellUnhooks, generatePrompt, generateTokenSlug, generateZshHooks, generateZshUnhooks, getADOCredentials, getActiveWorkspace, getActiveWorkspaceId, getAgentSkill, getAllSettings, getAllSettingsDetailed, getBlockedCards, getCard, getCardById, getCardDeletionImpact, getConfiguredWorkspaces, getDatabaseTargetInfo, getDefaultDbPath, getDependents, getDisplayTitle, getDomainCompetence, getDueCards, getEmbeddingCoverage, getGoal, getGoalTree, getInstallChannel, getInstallMode, getMachineAiConfig, getMonitorDir, getMonitorLogStats, getMonitorPath, getPackageSkillPath, getPrerequisites, getProviderApiKey, getRepoPaths, getReviewsForCard, getReviewsForUser, getSessionSummary, getSessionSynthesisRecords, getSetting, getShortSlug, getSystemProfile, getTokenById, getTokenBySlug, getTokenDeleteImpact, getTokenEmbedding, getTokenNeighborhood, getTursoCredentials, getUiObservationPath, getUiObserverDir, getUserStats, hasCommand, importCurriculumCards, importSnapshot, injectShellHooks, installFastFlowLM, installOllama, installOpenCode, interleave, isObserverPolicyConfigured, isOllamaInstalled, isUiObservationReport, listAgentSkills, listEmbeddedTokens, listGoals, listPersonalCards, listProviderApiKeyRefs, listTokens, listTokensNeedingEmbedding, loadADOConfig, loadCredentials, loadInstallConfig, logReview, logStep, matchBuiltInSensitive, matchDenylist, matchesFilePath, monitorLogExists, normalizeLocale, normalizePath, openDatabase, openDatabaseWithSync, openRemoteDatabase, pairCommands, parseGoalFile, parseMonitorLog, parseObserverList, parseObserverPolicy, parseSnapshot, parseUiObservationLog, planOpenCodeInstall, planUpdate, prepareLocalModel, prepareSessionSynthesis, readMonitorLog, readUiObservationLog, removeConfiguredWorkspace, resolveAllBeliefPaths, resolveAllGoalPaths, resolveObserverPolicy, resolveOllamaCommand, resolveReference, resolveRepoPath, resolveReviewContext, saveConfiguredWorkspaces, saveCredentials, saveInstallConfig, saveMachineAiConfig, searchTokensHybrid, serializeGoal, setADOCredentials, setActiveWorkspaceId, setInstallChannel, setInstallMode, setProviderApiKey, setSetting, setTursoCredentials, slugify, startSession, suggestFoundations, syncObserverSidecarPolicy, t, toSidecarPrivacyPolicy, uiObservationLogExists, uiObservationTimeSpan, unblockReady, updateCard, updateGoalStatus, updateToken, upsertConfiguredWorkspace, upsertTokenEmbedding, verifySnapshot, wouldCreateCycle, writeMonitorEvent };
package/dist/index.js CHANGED
@@ -530,6 +530,7 @@ var SCHEMA = `
530
530
  CREATE TABLE IF NOT EXISTS tokens (
531
531
  id TEXT PRIMARY KEY,
532
532
  slug TEXT UNIQUE NOT NULL,
533
+ title TEXT NOT NULL DEFAULT '',
533
534
  concept TEXT NOT NULL,
534
535
  domain TEXT NOT NULL DEFAULT '',
535
536
  bloom_level INTEGER NOT NULL DEFAULT 1 CHECK (bloom_level BETWEEN 1 AND 5),
@@ -678,6 +679,8 @@ CREATE INDEX IF NOT EXISTS idx_cards_token_user ON cards(token_id, user_id);
678
679
  CREATE INDEX IF NOT EXISTS idx_review_logs_card ON review_logs(card_id);
679
680
  CREATE INDEX IF NOT EXISTS idx_review_logs_user ON review_logs(user_id, reviewed_at);
680
681
  CREATE INDEX IF NOT EXISTS idx_session_steps_session ON session_steps(session_id);
682
+ CREATE INDEX IF NOT EXISTS idx_tokens_title ON tokens(title);
683
+ CREATE INDEX IF NOT EXISTS idx_tokens_domain ON tokens(domain);
681
684
  `;
682
685
 
683
686
  // src/kernel/db/sync-adapter.ts
@@ -1011,6 +1014,15 @@ async function runMigrations(db) {
1011
1014
  embedded_at TEXT NOT NULL DEFAULT (datetime('now'))
1012
1015
  )
1013
1016
  `);
1017
+ if (!tokenCols.some((c) => c.name === "title")) {
1018
+ await db.exec(
1019
+ `ALTER TABLE tokens ADD COLUMN title TEXT NOT NULL DEFAULT ''`
1020
+ );
1021
+ }
1022
+ await db.exec(`CREATE INDEX IF NOT EXISTS idx_tokens_title ON tokens(title)`);
1023
+ await db.exec(
1024
+ `CREATE INDEX IF NOT EXISTS idx_tokens_domain ON tokens(domain)`
1025
+ );
1014
1026
  }
1015
1027
 
1016
1028
  // src/kernel/db/snapshot.ts
@@ -1512,12 +1524,14 @@ async function createToken(db, input) {
1512
1524
  if (bloom < 1 || bloom > 5) {
1513
1525
  throw new Error(`bloom_level must be between 1 and 5, got ${bloom}`);
1514
1526
  }
1527
+ const title = input.title ?? "";
1515
1528
  await db.prepare(`
1516
- INSERT INTO tokens (id, slug, concept, domain, bloom_level, context, symbiosis_mode, source_link, question, provider, topic_id, created_at, updated_at)
1517
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1529
+ INSERT INTO tokens (id, slug, title, concept, domain, bloom_level, context, symbiosis_mode, source_link, question, provider, topic_id, created_at, updated_at)
1530
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1518
1531
  `).run(
1519
1532
  id,
1520
1533
  input.slug,
1534
+ title,
1521
1535
  input.concept,
1522
1536
  input.domain ?? "",
1523
1537
  bloom,
@@ -1560,6 +1574,10 @@ async function updateToken(db, slug, updates) {
1560
1574
  }
1561
1575
  const fields = [];
1562
1576
  const values = [];
1577
+ if (updates.title !== void 0) {
1578
+ fields.push("title = ?");
1579
+ values.push(updates.title ?? "");
1580
+ }
1563
1581
  if (updates.concept !== void 0) {
1564
1582
  fields.push("concept = ?");
1565
1583
  values.push(updates.concept);
@@ -1665,13 +1683,14 @@ async function deleteToken(db, slug) {
1665
1683
  await db.transaction(async (tx) => {
1666
1684
  const now = (/* @__PURE__ */ new Date()).toISOString();
1667
1685
  const skillRows = await tx.prepare("SELECT id, token_slugs FROM agent_skills").all();
1686
+ const skillUpdateStmt = tx.prepare(
1687
+ "UPDATE agent_skills SET token_slugs = ?, updated_at = ? WHERE id = ?"
1688
+ );
1668
1689
  for (const row of skillRows) {
1669
1690
  const tokenSlugs = JSON.parse(row.token_slugs);
1670
1691
  const filtered = tokenSlugs.filter((tokenSlug) => tokenSlug !== slug);
1671
1692
  if (filtered.length !== tokenSlugs.length) {
1672
- await tx.prepare(
1673
- "UPDATE agent_skills SET token_slugs = ?, updated_at = ? WHERE id = ?"
1674
- ).run(JSON.stringify(filtered), now, row.id);
1693
+ await skillUpdateStmt.run(JSON.stringify(filtered), now, row.id);
1675
1694
  }
1676
1695
  }
1677
1696
  await tx.prepare("DELETE FROM tokens WHERE id = ?").run(token.id);
@@ -1685,10 +1704,16 @@ async function findTokens(db, query) {
1685
1704
  const shortTerms = searchTokens.filter((t2) => t2.length <= 2);
1686
1705
  const longTerms = searchTokens.filter((t2) => t2.length > 2);
1687
1706
  const scoreMap = /* @__PURE__ */ new Map();
1688
- const likeSQL = `SELECT * FROM tokens WHERE deprecated_at IS NULL AND (lower(slug) LIKE ? OR lower(concept) LIKE ? OR lower(domain) LIKE ?)`;
1707
+ const likeSQL = `SELECT * FROM tokens WHERE deprecated_at IS NULL AND (lower(slug) LIKE ? OR lower(title) LIKE ? OR lower(concept) LIKE ? OR lower(domain) LIKE ?)`;
1708
+ const likeStmt = db.prepare(likeSQL);
1689
1709
  for (const term of longTerms) {
1690
1710
  const pattern = `%${term}%`;
1691
- const rows = await db.prepare(likeSQL).all(pattern, pattern, pattern);
1711
+ const rows = await likeStmt.all(
1712
+ pattern,
1713
+ pattern,
1714
+ pattern,
1715
+ pattern
1716
+ );
1692
1717
  for (const row of rows) {
1693
1718
  const entry = scoreMap.get(row.id);
1694
1719
  if (entry) {
@@ -1701,7 +1726,7 @@ async function findTokens(db, query) {
1701
1726
  if (shortTerms.length > 0 || longTerms.length === 0) {
1702
1727
  const allTokens = await db.prepare("SELECT * FROM tokens WHERE deprecated_at IS NULL").all();
1703
1728
  for (const token of allTokens) {
1704
- const words = `${token.slug} ${token.concept} ${token.domain}`.toLowerCase().split(/[\s,.\-_/\\:;!?()[\]{}]+/).filter(Boolean);
1729
+ const words = `${token.slug} ${token.title} ${token.concept} ${token.domain}`.toLowerCase().split(/[\s,.\-_/\\:;!?()[\]{}]+/).filter(Boolean);
1705
1730
  let matchCount = 0;
1706
1731
  for (const term of shortTerms.length > 0 ? shortTerms : searchTokens) {
1707
1732
  for (const w of words) {
@@ -1735,6 +1760,11 @@ async function listTokens(db, options) {
1735
1760
  tokens = await db.prepare(
1736
1761
  "SELECT * FROM tokens WHERE domain = ? AND deprecated_at IS NULL ORDER BY bloom_level, slug"
1737
1762
  ).all(options.domain);
1763
+ } else if (options?.domainPrefix) {
1764
+ const prefix = options.domainPrefix;
1765
+ tokens = await db.prepare(
1766
+ `SELECT * FROM tokens WHERE (domain = ? OR domain LIKE ?) AND deprecated_at IS NULL ORDER BY bloom_level, slug`
1767
+ ).all(prefix, `${prefix}/%`);
1738
1768
  } else {
1739
1769
  tokens = await db.prepare(
1740
1770
  "SELECT * FROM tokens WHERE deprecated_at IS NULL ORDER BY bloom_level, domain, slug"
@@ -1746,7 +1776,20 @@ async function listTokens(db, options) {
1746
1776
  return tokens;
1747
1777
  }
1748
1778
  function slugify(text) {
1749
- return text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
1779
+ return text.toLowerCase().replace(/ä/g, "ae").replace(/ö/g, "oe").replace(/ü/g, "ue").replace(/ß/g, "ss").replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
1780
+ }
1781
+ function getShortSlug(slug, domainPrefix) {
1782
+ if (domainPrefix) {
1783
+ const folded = domainPrefix.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
1784
+ if (folded && slug.startsWith(`${folded}-`)) {
1785
+ return slug.substring(folded.length + 1);
1786
+ }
1787
+ }
1788
+ return slug;
1789
+ }
1790
+ function getDisplayTitle(t2, activeDomainScope) {
1791
+ if (t2.title?.trim()) return t2.title.trim();
1792
+ return getShortSlug(t2.slug, activeDomainScope);
1750
1793
  }
1751
1794
  async function generateTokenSlug(db, domain, concept, question) {
1752
1795
  const baseText = question && question.trim().length > 0 ? question : concept;
@@ -1776,6 +1819,7 @@ async function listPersonalCards(db, userId, options) {
1776
1819
  SELECT
1777
1820
  t.id AS tokenId,
1778
1821
  t.slug,
1822
+ t.title,
1779
1823
  t.concept,
1780
1824
  t.domain,
1781
1825
  t.bloom_level AS bloomLevel,
@@ -1877,6 +1921,7 @@ async function importCurriculumCards(db, userId, cards) {
1877
1921
  );
1878
1922
  token = await createToken(tx, {
1879
1923
  slug: finalSlug,
1924
+ title: card.title,
1880
1925
  concept: card.concept,
1881
1926
  domain: card.domain,
1882
1927
  bloom_level: bloom,
@@ -1994,23 +2039,28 @@ async function confirmCardSplit(db, userId, originalSlug, action, originalQuesti
1994
2039
  (/* @__PURE__ */ new Date()).toISOString(),
1995
2040
  originalToken.id
1996
2041
  );
2042
+ const insertPrereqStmt = tx.prepare(
2043
+ "INSERT OR IGNORE INTO prerequisites (token_id, requires_id) VALUES (?, ?)"
2044
+ );
1997
2045
  for (const propToken of proposalTokens) {
1998
- await tx.prepare(
1999
- "INSERT OR IGNORE INTO prerequisites (token_id, requires_id) VALUES (?, ?)"
2000
- ).run(originalToken.id, propToken.id);
2046
+ await insertPrereqStmt.run(originalToken.id, propToken.id);
2001
2047
  }
2002
2048
  await tx.prepare(
2003
2049
  "UPDATE cards SET blocked = 1 WHERE token_id = ? AND user_id = ?"
2004
2050
  ).run(originalToken.id, userId);
2051
+ const checkPrereqsStmt = tx.prepare(
2052
+ "SELECT COUNT(*) as n FROM prerequisites WHERE token_id = ?"
2053
+ );
2054
+ const unblockCardStmt = tx.prepare(
2055
+ "UPDATE cards SET blocked = 0, due_at = ? WHERE id = ?"
2056
+ );
2005
2057
  for (const propToken of proposalTokens) {
2006
2058
  const card = await ensureCard(tx, propToken.id, userId);
2007
2059
  if (card.blocked === 1) {
2008
- const prereqOfPrereq = await tx.prepare(
2009
- "SELECT COUNT(*) as n FROM prerequisites WHERE token_id = ?"
2010
- ).get(propToken.id);
2060
+ const prereqOfPrereq = await checkPrereqsStmt.get(propToken.id);
2011
2061
  if (prereqOfPrereq.n === 0) {
2012
2062
  const now = (/* @__PURE__ */ new Date()).toISOString();
2013
- await tx.prepare("UPDATE cards SET blocked = 0, due_at = ? WHERE id = ?").run(now, card.id);
2063
+ await unblockCardStmt.run(now, card.id);
2014
2064
  }
2015
2065
  }
2016
2066
  }
@@ -2081,6 +2131,7 @@ async function confirmFoundations(db, userId, originalSlug, proposals) {
2081
2131
  );
2082
2132
  token = await createToken(tx, {
2083
2133
  slug: finalSlug,
2134
+ title: card.title,
2084
2135
  concept: card.concept,
2085
2136
  domain: card.domain,
2086
2137
  bloom_level: bloom,
@@ -2147,6 +2198,7 @@ async function confirmSourceImport(db, userId, sourceId, proposals) {
2147
2198
  }
2148
2199
  token = await createToken(tx, {
2149
2200
  slug: finalSlug,
2201
+ title: card.title,
2150
2202
  concept: card.concept,
2151
2203
  domain: card.domain,
2152
2204
  bloom_level: bloom,
@@ -2249,7 +2301,7 @@ async function addPrerequisite(db, tokenId, requiresId) {
2249
2301
  }
2250
2302
  async function getPrerequisites(db, tokenId) {
2251
2303
  return await db.prepare(
2252
- `SELECT p.token_id, p.requires_id, t.slug, t.concept, t.domain, t.bloom_level
2304
+ `SELECT p.token_id, p.requires_id, t.slug, t.title, t.concept, t.domain, t.bloom_level
2253
2305
  FROM prerequisites p
2254
2306
  JOIN tokens t ON t.id = p.requires_id
2255
2307
  WHERE p.token_id = ?`
@@ -2257,7 +2309,7 @@ async function getPrerequisites(db, tokenId) {
2257
2309
  }
2258
2310
  async function getDependents(db, tokenId) {
2259
2311
  return await db.prepare(
2260
- `SELECT p.token_id, p.requires_id, t.slug, t.concept, t.domain, t.bloom_level
2312
+ `SELECT p.token_id, p.requires_id, t.slug, t.title, t.concept, t.domain, t.bloom_level
2261
2313
  FROM prerequisites p
2262
2314
  JOIN tokens t ON t.id = p.token_id
2263
2315
  WHERE p.requires_id = ?`
@@ -2288,6 +2340,7 @@ async function getTokenNeighborhood(db, tokenId, userId) {
2288
2340
  const toNode = (t2, card) => ({
2289
2341
  id: t2.id,
2290
2342
  slug: t2.slug,
2343
+ title: t2.title ?? t2.slug,
2291
2344
  concept: t2.concept,
2292
2345
  domain: t2.domain,
2293
2346
  bloom_level: t2.bloom_level,
@@ -2307,6 +2360,7 @@ async function getTokenNeighborhood(db, tokenId, userId) {
2307
2360
  {
2308
2361
  id: p.requires_id,
2309
2362
  slug: p.slug,
2363
+ title: p.title,
2310
2364
  concept: p.concept,
2311
2365
  domain: p.domain,
2312
2366
  bloom_level: p.bloom_level
@@ -2319,6 +2373,7 @@ async function getTokenNeighborhood(db, tokenId, userId) {
2319
2373
  {
2320
2374
  id: d.token_id,
2321
2375
  slug: d.slug,
2376
+ title: d.title,
2322
2377
  concept: d.concept,
2323
2378
  domain: d.domain,
2324
2379
  bloom_level: d.bloom_level
@@ -2478,7 +2533,8 @@ import { createHash as createHash2 } from "crypto";
2478
2533
  function embeddingContentForToken(t2) {
2479
2534
  return `${t2.concept}
2480
2535
  ${t2.question ?? ""}
2481
- ${t2.domain}`;
2536
+ ${t2.domain}
2537
+ ${t2.title ?? ""}`;
2482
2538
  }
2483
2539
  function computeContentHash(text) {
2484
2540
  return createHash2("sha256").update(text, "utf8").digest("hex");
@@ -4662,6 +4718,7 @@ async function buildReviewQueue(db, options) {
4662
4718
  c.id AS card_id,
4663
4719
  c.token_id AS token_id,
4664
4720
  t.slug AS slug,
4721
+ t.title AS title,
4665
4722
  t.concept AS concept,
4666
4723
  t.domain AS domain,
4667
4724
  t.bloom_level AS bloom_level,
@@ -4683,6 +4740,7 @@ async function buildReviewQueue(db, options) {
4683
4740
  c.id AS card_id,
4684
4741
  c.token_id AS token_id,
4685
4742
  t.slug AS slug,
4743
+ t.title AS title,
4686
4744
  t.concept AS concept,
4687
4745
  t.domain AS domain,
4688
4746
  t.bloom_level AS bloom_level,
@@ -4742,6 +4800,7 @@ function rowToItem(row) {
4742
4800
  cardId: row.card_id,
4743
4801
  tokenId: row.token_id,
4744
4802
  slug: row.slug,
4803
+ title: getDisplayTitle(row),
4745
4804
  concept: row.concept,
4746
4805
  domain: row.domain,
4747
4806
  bloomLevel: row.bloom_level,
@@ -6005,6 +6064,7 @@ export {
6005
6064
  getDatabaseTargetInfo,
6006
6065
  getDefaultDbPath,
6007
6066
  getDependents,
6067
+ getDisplayTitle,
6008
6068
  getDomainCompetence,
6009
6069
  getDueCards,
6010
6070
  getEmbeddingCoverage,
@@ -6025,6 +6085,7 @@ export {
6025
6085
  getSessionSummary,
6026
6086
  getSessionSynthesisRecords,
6027
6087
  getSetting,
6088
+ getShortSlug,
6028
6089
  getSystemProfile,
6029
6090
  getTokenById,
6030
6091
  getTokenBySlug,