@iworkforces/tracelattice 2.0.7 → 2.0.8

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.
Files changed (64) hide show
  1. package/README.md +1 -1
  2. package/dist/cli.js +205 -181
  3. package/dist/contracts/summary.d.ts +0 -1
  4. package/dist/contracts/summary.d.ts.map +1 -1
  5. package/dist/core/PersistenceRestore.d.ts +1 -1
  6. package/dist/core/PersistenceRestore.d.ts.map +1 -1
  7. package/dist/core/PersistenceRestore.js.map +1 -1
  8. package/dist/core/ThoughtEvaluator.d.ts.map +1 -1
  9. package/dist/core/ThoughtEvaluator.js.map +1 -1
  10. package/dist/core/ThoughtFormatter.js.map +1 -1
  11. package/dist/core/ThoughtProcessor.d.ts.map +1 -1
  12. package/dist/core/ThoughtProcessor.js.map +1 -1
  13. package/dist/core/compression/CompressionService.d.ts +2 -1
  14. package/dist/core/compression/CompressionService.d.ts.map +1 -1
  15. package/dist/core/compression/CompressionService.js.map +1 -1
  16. package/dist/core/compression/DehydrationPolicy.d.ts.map +1 -1
  17. package/dist/core/compression/DehydrationPolicy.js.map +1 -1
  18. package/dist/core/compression/InMemorySummaryStore.d.ts +2 -1
  19. package/dist/core/compression/InMemorySummaryStore.d.ts.map +1 -1
  20. package/dist/core/compression/InMemorySummaryStore.js.map +1 -1
  21. package/dist/core/evaluator/Calibrator.d.ts +1 -1
  22. package/dist/core/evaluator/Calibrator.d.ts.map +1 -1
  23. package/dist/core/evaluator/Calibrator.js.map +1 -1
  24. package/dist/core/evaluator/SignalComputer.d.ts +2 -1
  25. package/dist/core/evaluator/SignalComputer.d.ts.map +1 -1
  26. package/dist/core/evaluator/SignalComputer.js.map +1 -1
  27. package/dist/core/evaluator/internals.d.ts +1 -1
  28. package/dist/core/evaluator/internals.d.ts.map +1 -1
  29. package/dist/core/evaluator/internals.js.map +1 -1
  30. package/dist/core/graph/GraphView.d.ts +13 -1
  31. package/dist/core/graph/GraphView.d.ts.map +1 -1
  32. package/dist/core/graph/GraphView.js +34 -0
  33. package/dist/core/graph/GraphView.js.map +1 -1
  34. package/dist/core/reasoning/strategies/TreeOfThoughtStrategy.d.ts +6 -6
  35. package/dist/core/reasoning/strategies/TreeOfThoughtStrategy.d.ts.map +1 -1
  36. package/dist/core/reasoning/strategies/TreeOfThoughtStrategy.js +16 -2
  37. package/dist/core/reasoning/strategies/TreeOfThoughtStrategy.js.map +1 -1
  38. package/dist/core/reasoning/strategies/totScoring.d.ts.map +1 -1
  39. package/dist/core/reasoning/strategies/totScoring.js.map +1 -1
  40. package/dist/core/reasoning.d.ts +1 -3
  41. package/dist/core/reasoning.d.ts.map +1 -1
  42. package/dist/core/thought.d.ts +1 -1
  43. package/dist/core/thought.d.ts.map +1 -1
  44. package/dist/lib.d.ts +18 -4
  45. package/dist/lib.d.ts.map +1 -1
  46. package/dist/lib.js +51 -3
  47. package/dist/lib.js.map +1 -1
  48. package/dist/transport/HttpHelpers.d.ts +4 -1
  49. package/dist/transport/HttpHelpers.d.ts.map +1 -1
  50. package/dist/transport/HttpHelpers.js +61 -11
  51. package/dist/transport/HttpHelpers.js.map +1 -1
  52. package/dist/transport/HttpRequestLifecycle.d.ts +15 -1
  53. package/dist/transport/HttpRequestLifecycle.d.ts.map +1 -1
  54. package/dist/transport/HttpRequestLifecycle.js +66 -4
  55. package/dist/transport/HttpRequestLifecycle.js.map +1 -1
  56. package/dist/transport/HttpTransport.d.ts +2 -0
  57. package/dist/transport/HttpTransport.d.ts.map +1 -1
  58. package/dist/transport/HttpTransport.js +75 -38
  59. package/dist/transport/HttpTransport.js.map +1 -1
  60. package/dist/transport/StreamableHttpTransport.d.ts +5 -29
  61. package/dist/transport/StreamableHttpTransport.d.ts.map +1 -1
  62. package/dist/transport/StreamableHttpTransport.js +74 -116
  63. package/dist/transport/StreamableHttpTransport.js.map +1 -1
  64. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"core/compression/InMemorySummaryStore.js","sources":["../../../src/core/compression/InMemorySummaryStore.ts"],"sourcesContent":["/**\n * InMemorySummaryStore — in-memory implementation of {@link ISummaryStore}.\n *\n * Stores compressed thought summaries in three indexes for O(1) point lookups\n * and O(n) bucket reads (n = result size). Per-session and per-branch isolation\n * is enforced by maintaining independent buckets keyed by session and\n * `${sessionId}:${branchId}`.\n *\n * The store is intentionally synchronous and pure in-memory — no I/O. Persistence\n * adapters may wrap this implementation if durability is required.\n *\n * @module core/compression/InMemorySummaryStore\n */\n\nimport { SequentialThinkingError } from '../../errors.js';\nimport type { ISummaryStore, Summary } from '../../contracts/summary.js';\nimport {\n\tasBranchId,\n\tasSessionId,\n\tasSummaryId,\n\ttype BranchId,\n\ttype SessionId,\n\ttype SummaryId,\n} from '../../contracts/ids.js';\n\n/**\n * Compose the composite branch key used by the per-branch index.\n *\n * The colon separator mirrors namespace conventions in the rest of the codebase\n * and is safe because session/branch ids are constrained to alphanumeric,\n * hyphen and underscore characters.\n */\nfunction branchKey(sessionId: SessionId, branchId: BranchId): BranchId {\n\treturn asBranchId(`${sessionId}:${branchId}`);\n}\n\n/**\n * In-memory implementation of {@link ISummaryStore}.\n *\n * Maintains three indexes:\n * - `_byId`: id → summary (global lookup)\n * - `_bySession`: sessionId → summaries[] (sorted by `createdAt` ascending)\n * - `_byBranch`: `${sessionId}:${branchId}` → summaries[] (sorted by `createdAt`)\n *\n * @example\n * ```typescript\n * const store = new InMemorySummaryStore();\n * store.add({\n * id: '01HX...',\n * sessionId: 's1',\n * rootThoughtId: 't5',\n * coveredIds: ['t5', 't6'],\n * coveredRange: [5, 6],\n * topics: ['cache', 'lookup'],\n * aggregateConfidence: 0.8,\n * createdAt: Date.now(),\n * });\n * const all = store.forSession('s1');\n * ```\n */\nexport class InMemorySummaryStore implements ISummaryStore {\n\tprivate readonly _byId: Map<SessionId, Map<SummaryId, Summary>> = new Map();\n\tprivate readonly _bySession: Map<SessionId, Summary[]> = new Map();\n\tprivate readonly _byBranch: Map<BranchId, Summary[]> = new Map();\n\n\t/**\n\t * Add a summary to the store.\n\t *\n\t * Inserts the summary into all three indexes, maintaining ascending\n\t * `createdAt` order in the per-session and per-branch buckets via binary\n\t * search insertion (O(log n)).\n\t *\n\t * @param summary - The summary record to add\n\t * @throws {SequentialThinkingError} When a summary with the same id already exists\n\t *\n\t * @example\n\t * ```typescript\n\t * store.add(summary);\n\t * ```\n\t */\n\tadd(summary: Summary): void {\n\t\tlet sessionIds = this._byId.get(summary.sessionId);\n\t\tif (sessionIds === undefined) {\n\t\t\tsessionIds = new Map<SummaryId, Summary>();\n\t\t\tthis._byId.set(summary.sessionId, sessionIds);\n\t\t}\n\t\tif (sessionIds.has(asSummaryId(summary.id))) {\n\t\t\tthrow new SequentialThinkingError(`Duplicate summary id: ${summary.id}`, 'DUPLICATE_SUMMARY');\n\t\t}\n\n\t\tsessionIds.set(asSummaryId(summary.id), summary);\n\t\tthis._insertSorted(this._bySession, summary.sessionId, summary);\n\t\tif (summary.branchId !== undefined) {\n\t\t\tthis._insertSorted(\n\t\t\t\tthis._byBranch,\n\t\t\t\tbranchKey(asSessionId(summary.sessionId), summary.branchId),\n\t\t\t\tsummary\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Retrieve a summary by its unique id.\n\t *\n\t * @param id - The summary's unique identifier\n\t * @returns The summary, or `undefined` if not found\n\t *\n\t * @example\n\t * ```typescript\n\t * const s = store.get('01HX...');\n\t * ```\n\t */\n\tget(id: string): Summary | undefined {\n\t\tfor (const sessionIds of this._byId.values()) {\n\t\t\tconst summary = sessionIds.get(asSummaryId(id));\n\t\t\tif (summary !== undefined) return summary;\n\t\t}\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * Get all summaries for a session, sorted by `createdAt` ascending.\n\t *\n\t * @param sessionId - Session to query\n\t * @returns Read-only array of summaries (empty if none)\n\t *\n\t * @example\n\t * ```typescript\n\t * for (const s of store.forSession('s1')) console.log(s.topics);\n\t * ```\n\t */\n\tforSession(sessionId: string): readonly Summary[] {\n\t\treturn this._bySession.get(asSessionId(sessionId)) ?? [];\n\t}\n\n\t/**\n\t * Get all summaries on a specific branch within a session.\n\t *\n\t * @param sessionId - Session to query\n\t * @param branchId - Branch identifier\n\t * @returns Read-only array of summaries (empty if none)\n\t *\n\t * @example\n\t * ```typescript\n\t * const branch = store.forBranch('s1', 'alt-1');\n\t * ```\n\t */\n\tforBranch(sessionId: string, branchId: BranchId): readonly Summary[] {\n\t\treturn this._byBranch.get(branchKey(asSessionId(sessionId), branchId)) ?? [];\n\t}\n\n\t/**\n\t * Remove all summaries for a session. Other sessions are unaffected.\n\t *\n\t * Removes entries from all three indexes in a single pass over the\n\t * session's summary list.\n\t *\n\t * @param sessionId - Session to clear\n\t *\n\t * @example\n\t * ```typescript\n\t * store.clearSession('s1');\n\t * ```\n\t */\n\tclearSession(sessionId: string): void {\n\t\tconst summaries = this._bySession.get(asSessionId(sessionId));\n\t\tif (!summaries) {\n\t\t\treturn;\n\t\t}\n\n\t\tfor (const summary of summaries) {\n\t\t\tif (summary.branchId !== undefined) {\n\t\t\t\tthis._byBranch.delete(branchKey(asSessionId(sessionId), summary.branchId));\n\t\t\t}\n\t\t}\n\t\tthis._byId.delete(asSessionId(sessionId));\n\t\tthis._bySession.delete(asSessionId(sessionId));\n\t}\n\n\tclearAll(): void {\n\t\tthis._byId.clear();\n\t\tthis._bySession.clear();\n\t\tthis._byBranch.clear();\n\t}\n\n\t/**\n\t * Count summaries.\n\t *\n\t * @param sessionId - If provided, count for that session only;\n\t * otherwise return the total across all sessions\n\t * @returns The number of summaries\n\t *\n\t * @example\n\t * ```typescript\n\t * const total = store.size();\n\t * const perSession = store.size('s1');\n\t * ```\n\t */\n\tsize(sessionId?: string): number {\n\t\tif (sessionId !== undefined) {\n\t\t\treturn this._bySession.get(asSessionId(sessionId))?.length ?? 0;\n\t\t}\n\t\tlet total = 0;\n\t\tfor (const sessionIds of this._byId.values()) total += sessionIds.size;\n\t\treturn total;\n\t}\n\n\t/**\n\t * Insert a summary into a bucket maintaining ascending `createdAt` order\n\t * via binary search (O(log n) search, O(n) splice).\n\t */\n\tprivate _insertSorted<K>(index: Map<K, Summary[]>, key: K, summary: Summary): void {\n\t\tlet bucket = index.get(key);\n\t\tif (!bucket) {\n\t\t\tbucket = [];\n\t\t\tindex.set(key, bucket);\n\t\t}\n\n\t\tlet lo = 0;\n\t\tlet hi = bucket.length;\n\t\twhile (lo < hi) {\n\t\t\tconst mid = (lo + hi) >>> 1;\n\t\t\tif (bucket[mid]!.createdAt <= summary.createdAt) {\n\t\t\t\tlo = mid + 1;\n\t\t\t} else {\n\t\t\t\thi = mid;\n\t\t\t}\n\t\t}\n\t\tbucket.splice(lo, 0, summary);\n\t}\n}\n"],"names":["branchKey","sessionId","branchId","asBranchId","InMemorySummaryStore","Map","summary","sessionIds","undefined","asSummaryId","SequentialThinkingError","asSessionId","id","summaries","total","index","key","bucket","lo","hi","mid"],"mappings":"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"}
1
+ {"version":3,"file":"core/compression/InMemorySummaryStore.js","sources":["../../../src/core/compression/InMemorySummaryStore.ts"],"sourcesContent":["/**\n * InMemorySummaryStore — in-memory implementation of {@link ISummaryStore}.\n *\n * Stores compressed thought summaries in three indexes for O(1) point lookups\n * and O(n) bucket reads (n = result size). Per-session and per-branch isolation\n * is enforced by maintaining independent buckets keyed by session and\n * `${sessionId}:${branchId}`.\n *\n * The store is intentionally synchronous and pure in-memory — no I/O. Persistence\n * adapters may wrap this implementation if durability is required.\n *\n * @module core/compression/InMemorySummaryStore\n */\n\nimport { SequentialThinkingError } from '../../errors.js';\nimport type { ISummaryStore } from '../../contracts/summary.js';\nimport type { Summary } from './Summary.js';\nimport {\n\tasBranchId,\n\tasSessionId,\n\tasSummaryId,\n\ttype BranchId,\n\ttype SessionId,\n\ttype SummaryId,\n} from '../../contracts/ids.js';\n\n/**\n * Compose the composite branch key used by the per-branch index.\n *\n * The colon separator mirrors namespace conventions in the rest of the codebase\n * and is safe because session/branch ids are constrained to alphanumeric,\n * hyphen and underscore characters.\n */\nfunction branchKey(sessionId: SessionId, branchId: BranchId): BranchId {\n\treturn asBranchId(`${sessionId}:${branchId}`);\n}\n\n/**\n * In-memory implementation of {@link ISummaryStore}.\n *\n * Maintains three indexes:\n * - `_byId`: id → summary (global lookup)\n * - `_bySession`: sessionId → summaries[] (sorted by `createdAt` ascending)\n * - `_byBranch`: `${sessionId}:${branchId}` → summaries[] (sorted by `createdAt`)\n *\n * @example\n * ```typescript\n * const store = new InMemorySummaryStore();\n * store.add({\n * id: '01HX...',\n * sessionId: 's1',\n * rootThoughtId: 't5',\n * coveredIds: ['t5', 't6'],\n * coveredRange: [5, 6],\n * topics: ['cache', 'lookup'],\n * aggregateConfidence: 0.8,\n * createdAt: Date.now(),\n * });\n * const all = store.forSession('s1');\n * ```\n */\nexport class InMemorySummaryStore implements ISummaryStore {\n\tprivate readonly _byId: Map<SessionId, Map<SummaryId, Summary>> = new Map();\n\tprivate readonly _bySession: Map<SessionId, Summary[]> = new Map();\n\tprivate readonly _byBranch: Map<BranchId, Summary[]> = new Map();\n\n\t/**\n\t * Add a summary to the store.\n\t *\n\t * Inserts the summary into all three indexes, maintaining ascending\n\t * `createdAt` order in the per-session and per-branch buckets via binary\n\t * search insertion (O(log n)).\n\t *\n\t * @param summary - The summary record to add\n\t * @throws {SequentialThinkingError} When a summary with the same id already exists\n\t *\n\t * @example\n\t * ```typescript\n\t * store.add(summary);\n\t * ```\n\t */\n\tadd(summary: Summary): void {\n\t\tlet sessionIds = this._byId.get(summary.sessionId);\n\t\tif (sessionIds === undefined) {\n\t\t\tsessionIds = new Map<SummaryId, Summary>();\n\t\t\tthis._byId.set(summary.sessionId, sessionIds);\n\t\t}\n\t\tif (sessionIds.has(asSummaryId(summary.id))) {\n\t\t\tthrow new SequentialThinkingError(`Duplicate summary id: ${summary.id}`, 'DUPLICATE_SUMMARY');\n\t\t}\n\n\t\tsessionIds.set(asSummaryId(summary.id), summary);\n\t\tthis._insertSorted(this._bySession, summary.sessionId, summary);\n\t\tif (summary.branchId !== undefined) {\n\t\t\tthis._insertSorted(\n\t\t\t\tthis._byBranch,\n\t\t\t\tbranchKey(asSessionId(summary.sessionId), summary.branchId),\n\t\t\t\tsummary\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Retrieve a summary by its unique id.\n\t *\n\t * @param id - The summary's unique identifier\n\t * @returns The summary, or `undefined` if not found\n\t *\n\t * @example\n\t * ```typescript\n\t * const s = store.get('01HX...');\n\t * ```\n\t */\n\tget(id: string): Summary | undefined {\n\t\tfor (const sessionIds of this._byId.values()) {\n\t\t\tconst summary = sessionIds.get(asSummaryId(id));\n\t\t\tif (summary !== undefined) return summary;\n\t\t}\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * Get all summaries for a session, sorted by `createdAt` ascending.\n\t *\n\t * @param sessionId - Session to query\n\t * @returns Read-only array of summaries (empty if none)\n\t *\n\t * @example\n\t * ```typescript\n\t * for (const s of store.forSession('s1')) console.log(s.topics);\n\t * ```\n\t */\n\tforSession(sessionId: string): readonly Summary[] {\n\t\treturn this._bySession.get(asSessionId(sessionId)) ?? [];\n\t}\n\n\t/**\n\t * Get all summaries on a specific branch within a session.\n\t *\n\t * @param sessionId - Session to query\n\t * @param branchId - Branch identifier\n\t * @returns Read-only array of summaries (empty if none)\n\t *\n\t * @example\n\t * ```typescript\n\t * const branch = store.forBranch('s1', 'alt-1');\n\t * ```\n\t */\n\tforBranch(sessionId: string, branchId: BranchId): readonly Summary[] {\n\t\treturn this._byBranch.get(branchKey(asSessionId(sessionId), branchId)) ?? [];\n\t}\n\n\t/**\n\t * Remove all summaries for a session. Other sessions are unaffected.\n\t *\n\t * Removes entries from all three indexes in a single pass over the\n\t * session's summary list.\n\t *\n\t * @param sessionId - Session to clear\n\t *\n\t * @example\n\t * ```typescript\n\t * store.clearSession('s1');\n\t * ```\n\t */\n\tclearSession(sessionId: string): void {\n\t\tconst summaries = this._bySession.get(asSessionId(sessionId));\n\t\tif (!summaries) {\n\t\t\treturn;\n\t\t}\n\n\t\tfor (const summary of summaries) {\n\t\t\tif (summary.branchId !== undefined) {\n\t\t\t\tthis._byBranch.delete(branchKey(asSessionId(sessionId), summary.branchId));\n\t\t\t}\n\t\t}\n\t\tthis._byId.delete(asSessionId(sessionId));\n\t\tthis._bySession.delete(asSessionId(sessionId));\n\t}\n\n\tclearAll(): void {\n\t\tthis._byId.clear();\n\t\tthis._bySession.clear();\n\t\tthis._byBranch.clear();\n\t}\n\n\t/**\n\t * Count summaries.\n\t *\n\t * @param sessionId - If provided, count for that session only;\n\t * otherwise return the total across all sessions\n\t * @returns The number of summaries\n\t *\n\t * @example\n\t * ```typescript\n\t * const total = store.size();\n\t * const perSession = store.size('s1');\n\t * ```\n\t */\n\tsize(sessionId?: string): number {\n\t\tif (sessionId !== undefined) {\n\t\t\treturn this._bySession.get(asSessionId(sessionId))?.length ?? 0;\n\t\t}\n\t\tlet total = 0;\n\t\tfor (const sessionIds of this._byId.values()) total += sessionIds.size;\n\t\treturn total;\n\t}\n\n\t/**\n\t * Insert a summary into a bucket maintaining ascending `createdAt` order\n\t * via binary search (O(log n) search, O(n) splice).\n\t */\n\tprivate _insertSorted<K>(index: Map<K, Summary[]>, key: K, summary: Summary): void {\n\t\tlet bucket = index.get(key);\n\t\tif (!bucket) {\n\t\t\tbucket = [];\n\t\t\tindex.set(key, bucket);\n\t\t}\n\n\t\tlet lo = 0;\n\t\tlet hi = bucket.length;\n\t\twhile (lo < hi) {\n\t\t\tconst mid = (lo + hi) >>> 1;\n\t\t\tif (bucket[mid]!.createdAt <= summary.createdAt) {\n\t\t\t\tlo = mid + 1;\n\t\t\t} else {\n\t\t\t\thi = mid;\n\t\t\t}\n\t\t}\n\t\tbucket.splice(lo, 0, summary);\n\t}\n}\n"],"names":["branchKey","sessionId","branchId","asBranchId","InMemorySummaryStore","Map","summary","sessionIds","undefined","asSummaryId","SequentialThinkingError","asSessionId","id","summaries","total","index","key","bucket","lo","hi","mid"],"mappings":";;AAiCA,SAASA,UAAUC,SAAoB,EAAEC,QAAkB;IAC1D,OAAOC,WAAW,GAAGF,UAAU,CAAC,EAAEC,UAAU;AAC7C;AA0BO,MAAME;IACK,QAAiD,IAAIC,MAAM;IAC3D,aAAwC,IAAIA,MAAM;IAClD,YAAsC,IAAIA,MAAM;IAiBjE,IAAIC,OAAgB,EAAQ;QAC3B,IAAIC,aAAa,IAAI,CAAC,KAAK,CAAC,GAAG,CAACD,QAAQ,SAAS;QACjD,IAAIC,AAAeC,WAAfD,YAA0B;YAC7BA,aAAa,IAAIF;YACjB,IAAI,CAAC,KAAK,CAAC,GAAG,CAACC,QAAQ,SAAS,EAAEC;QACnC;QACA,IAAIA,WAAW,GAAG,CAACE,YAAYH,QAAQ,EAAE,IACxC,MAAM,IAAII,wBAAwB,CAAC,sBAAsB,EAAEJ,QAAQ,EAAE,EAAE,EAAE;QAG1EC,WAAW,GAAG,CAACE,YAAYH,QAAQ,EAAE,GAAGA;QACxC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,UAAU,EAAEA,QAAQ,SAAS,EAAEA;QACvD,IAAIA,AAAqBE,WAArBF,QAAQ,QAAQ,EACnB,IAAI,CAAC,aAAa,CACjB,IAAI,CAAC,SAAS,EACdN,UAAUW,YAAYL,QAAQ,SAAS,GAAGA,QAAQ,QAAQ,GAC1DA;IAGH;IAaA,IAAIM,EAAU,EAAuB;QACpC,KAAK,MAAML,cAAc,IAAI,CAAC,KAAK,CAAC,MAAM,GAAI;YAC7C,MAAMD,UAAUC,WAAW,GAAG,CAACE,YAAYG;YAC3C,IAAIN,AAAYE,WAAZF,SAAuB,OAAOA;QACnC;IAED;IAaA,WAAWL,SAAiB,EAAsB;QACjD,OAAO,IAAI,CAAC,UAAU,CAAC,GAAG,CAACU,YAAYV,eAAe,EAAE;IACzD;IAcA,UAAUA,SAAiB,EAAEC,QAAkB,EAAsB;QACpE,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAACF,UAAUW,YAAYV,YAAYC,cAAc,EAAE;IAC7E;IAeA,aAAaD,SAAiB,EAAQ;QACrC,MAAMY,YAAY,IAAI,CAAC,UAAU,CAAC,GAAG,CAACF,YAAYV;QAClD,IAAI,CAACY,WACJ;QAGD,KAAK,MAAMP,WAAWO,UACrB,IAAIP,AAAqBE,WAArBF,QAAQ,QAAQ,EACnB,IAAI,CAAC,SAAS,CAAC,MAAM,CAACN,UAAUW,YAAYV,YAAYK,QAAQ,QAAQ;QAG1E,IAAI,CAAC,KAAK,CAAC,MAAM,CAACK,YAAYV;QAC9B,IAAI,CAAC,UAAU,CAAC,MAAM,CAACU,YAAYV;IACpC;IAEA,WAAiB;QAChB,IAAI,CAAC,KAAK,CAAC,KAAK;QAChB,IAAI,CAAC,UAAU,CAAC,KAAK;QACrB,IAAI,CAAC,SAAS,CAAC,KAAK;IACrB;IAeA,KAAKA,SAAkB,EAAU;QAChC,IAAIA,AAAcO,WAAdP,WACH,OAAO,IAAI,CAAC,UAAU,CAAC,GAAG,CAACU,YAAYV,aAAa,UAAU;QAE/D,IAAIa,QAAQ;QACZ,KAAK,MAAMP,cAAc,IAAI,CAAC,KAAK,CAAC,MAAM,GAAIO,SAASP,WAAW,IAAI;QACtE,OAAOO;IACR;IAMQ,cAAiBC,KAAwB,EAAEC,GAAM,EAAEV,OAAgB,EAAQ;QAClF,IAAIW,SAASF,MAAM,GAAG,CAACC;QACvB,IAAI,CAACC,QAAQ;YACZA,SAAS,EAAE;YACXF,MAAM,GAAG,CAACC,KAAKC;QAChB;QAEA,IAAIC,KAAK;QACT,IAAIC,KAAKF,OAAO,MAAM;QACtB,MAAOC,KAAKC,GAAI;YACf,MAAMC,MAAOF,KAAKC,OAAQ;YAC1B,IAAIF,MAAM,CAACG,IAAI,CAAE,SAAS,IAAId,QAAQ,SAAS,EAC9CY,KAAKE,MAAM;iBAEXD,KAAKC;QAEP;QACAH,OAAO,MAAM,CAACC,IAAI,GAAGZ;IACtB;AACD"}
@@ -22,7 +22,7 @@
22
22
  */
23
23
  import type { CalibrationMetrics, CalibrationResult, ICalibrator } from '../../contracts/calibrator.js';
24
24
  import type { IOutcomeRecorder } from '../../contracts/interfaces.js';
25
- import type { ThoughtType } from '../reasoning.js';
25
+ import type { ThoughtType } from '../../contracts/reasoning-types.js';
26
26
  import type { SessionId } from '../../contracts/ids.js';
27
27
  /**
28
28
  * Confidence calibration service.
@@ -1 +1 @@
1
- {"version":3,"file":"Calibrator.d.ts","sourceRoot":"","sources":["../../../src/core/evaluator/Calibrator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EACX,kBAAkB,EAClB,iBAAiB,EACjB,WAAW,EACX,MAAM,+BAA+B,CAAC;AACvC,OAAO,KAAK,EAAE,gBAAgB,EAAuB,MAAM,+BAA+B,CAAC;AAC3F,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AA4HxD;;;;;;;;;;;;;GAaG;AACH,qBAAa,UAAW,YAAW,WAAW;IAC7C,SAAgB,OAAO,EAAE,OAAO,CAAC;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAmB;IAC7C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAoD;gBAEtE,eAAe,EAAE,gBAAgB,EAAE,OAAO,EAAE,OAAO;IAKxD,SAAS,CACf,aAAa,EAAE,MAAM,EACrB,IAAI,EAAE,WAAW,EACjB,SAAS,EAAE,SAAS,GAClB,iBAAiB;IAqBb,OAAO,CAAC,SAAS,CAAC,EAAE,SAAS,GAAG,kBAAkB;IAclD,KAAK,CAAC,SAAS,CAAC,EAAE,SAAS,GAAG,IAAI;IAUlC,YAAY,CAAC,SAAS,EAAE,SAAS,GAAG,IAAI;IAKxC,QAAQ,IAAI,IAAI;CAIvB"}
1
+ {"version":3,"file":"Calibrator.d.ts","sourceRoot":"","sources":["../../../src/core/evaluator/Calibrator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EACX,kBAAkB,EAClB,iBAAiB,EACjB,WAAW,EACX,MAAM,+BAA+B,CAAC;AACvC,OAAO,KAAK,EAAE,gBAAgB,EAAuB,MAAM,+BAA+B,CAAC;AAC3F,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oCAAoC,CAAC;AACtE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AA4HxD;;;;;;;;;;;;;GAaG;AACH,qBAAa,UAAW,YAAW,WAAW;IAC7C,SAAgB,OAAO,EAAE,OAAO,CAAC;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAmB;IAC7C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAoD;gBAEtE,eAAe,EAAE,gBAAgB,EAAE,OAAO,EAAE,OAAO;IAKxD,SAAS,CACf,aAAa,EAAE,MAAM,EACrB,IAAI,EAAE,WAAW,EACjB,SAAS,EAAE,SAAS,GAClB,iBAAiB;IAqBb,OAAO,CAAC,SAAS,CAAC,EAAE,SAAS,GAAG,kBAAkB;IAclD,KAAK,CAAC,SAAS,CAAC,EAAE,SAAS,GAAG,IAAI;IAUlC,YAAY,CAAC,SAAS,EAAE,SAAS,GAAG,IAAI;IAKxC,QAAQ,IAAI,IAAI;CAIvB"}
@@ -1 +1 @@
1
- {"version":3,"file":"core/evaluator/Calibrator.js","sources":["../../../src/core/evaluator/Calibrator.ts"],"sourcesContent":["/**\n * Confidence calibration for sequential thinking reasoning.\n *\n * Provides the {@link Calibrator} class — maps raw model confidence values\n * to calibrated probabilities using per-type Beta(2, 2) priors and\n * grid-searched temperature scaling. Reports calibration quality through\n * Brier score and 10-bin Expected Calibration Error (ECE).\n *\n * Math summary:\n * - **Per-type prior**: Beta(α=2, β=2), prior mean = 0.5. As outcomes accumulate\n * for a type, the calibrated value shrinks toward the observed mean of that\n * type with weight `priorWeight = 1 / (1 + n / 10)` where `n` is the type's\n * sample count.\n * - **Temperature scaling**: Grid search T ∈ {0.5, 0.75, 1.0, 1.25, 1.5, 2.0}\n * minimizing negative log-likelihood (NLL) over recorded outcomes. Default T=1.0.\n * Requires ≥10 outcomes; below that threshold only prior shrinkage is applied.\n * - **Brier score**: `mean((predicted - actual)^2)` over recorded outcomes.\n * - **ECE (10-bin)**: bucket predictions in 0.1 increments, compute weighted\n * absolute deviation between bin mean confidence and bin accuracy.\n *\n * @module core/evaluator/Calibrator\n */\n\nimport type {\n\tCalibrationMetrics,\n\tCalibrationResult,\n\tICalibrator,\n} from '../../contracts/calibrator.js';\nimport type { IOutcomeRecorder, VerificationOutcome } from '../../contracts/interfaces.js';\nimport type { ThoughtType } from '../reasoning.js';\nimport type { SessionId } from '../../contracts/ids.js';\nimport {\n\tEPSILON,\n\tMIN_OUTCOMES_FOR_TEMPERATURE,\n\tapplyTemperature,\n\tfitTemperature,\n} from './calibration-math.js';\nimport { ALL_THOUGHT_TYPES } from './internals.js';\n\n/** Number of bins used by ECE (10-bin → 0.1 increments). */\nconst ECE_BINS = 10;\n\n/** Sentinel used in the per-session temperature map for global state. */\nconst GLOBAL_KEY: unique symbol = Symbol('global calibration temperature');\n\n/**\n * Build per-type empirical means + counts from a list of outcomes.\n *\n * @param outcomes - Outcomes to aggregate.\n * @returns Map of thought type to `{ mean, count }`. Types with no outcomes\n * are absent from the map.\n */\nfunction aggregatePerType(\n\toutcomes: readonly VerificationOutcome[]\n): Map<string, { mean: number; count: number }> {\n\tconst sums = new Map<string, { sum: number; count: number }>();\n\tfor (const o of outcomes) {\n\t\tconst prev = sums.get(o.type) ?? { sum: 0, count: 0 };\n\t\tprev.sum += o.actual;\n\t\tprev.count += 1;\n\t\tsums.set(o.type, prev);\n\t}\n\tconst result = new Map<string, { mean: number; count: number }>();\n\tfor (const [type, { sum, count }] of sums) {\n\t\tresult.set(type, { mean: sum / count, count });\n\t}\n\treturn result;\n}\n\n/**\n * Compute the Brier score over a list of outcomes.\n *\n * @param outcomes - Outcomes to score.\n * @returns Mean squared error between predicted probability and actual label,\n * or `null` if `outcomes` is empty.\n */\nfunction brierScore(outcomes: readonly VerificationOutcome[]): number | null {\n\tif (outcomes.length === 0) return null;\n\tlet sum = 0;\n\tfor (const o of outcomes) {\n\t\tconst diff = o.predicted - o.actual;\n\t\tsum += diff * diff;\n\t}\n\treturn sum / outcomes.length;\n}\n\n/**\n * Compute 10-bin Expected Calibration Error.\n *\n * Buckets predictions in 0.1-wide bins, then sums weighted absolute\n * differences between bin mean confidence and bin accuracy.\n *\n * @param outcomes - Outcomes to evaluate.\n * @returns ECE in `[0, 1]`, or `null` if `outcomes` is empty.\n */\nfunction expectedCalibrationError(outcomes: readonly VerificationOutcome[]): number | null {\n\tif (outcomes.length === 0) return null;\n\tconst binConfSum = new Array<number>(ECE_BINS).fill(0);\n\tconst binAccSum = new Array<number>(ECE_BINS).fill(0);\n\tconst binCount = new Array<number>(ECE_BINS).fill(0);\n\tfor (const o of outcomes) {\n\t\tconst p = Math.min(1 - EPSILON, Math.max(0, o.predicted));\n\t\tconst idx = Math.min(ECE_BINS - 1, Math.floor(p * ECE_BINS));\n\t\tbinConfSum[idx] = (binConfSum[idx] ?? 0) + p;\n\t\tbinAccSum[idx] = (binAccSum[idx] ?? 0) + o.actual;\n\t\tbinCount[idx] = (binCount[idx] ?? 0) + 1;\n\t}\n\tconst total = outcomes.length;\n\tlet ece = 0;\n\tfor (let i = 0; i < ECE_BINS; i++) {\n\t\tconst n = binCount[i] ?? 0;\n\t\tif (n === 0) continue;\n\t\tconst meanConf = (binConfSum[i] ?? 0) / n;\n\t\tconst meanAcc = (binAccSum[i] ?? 0) / n;\n\t\tece += (n / total) * Math.abs(meanConf - meanAcc);\n\t}\n\treturn ece;\n}\n\n/**\n * Build the per-type Brier score breakdown for {@link CalibrationMetrics}.\n *\n * @param outcomes - Outcomes to bucket by type.\n * @returns Record keyed by every {@link ThoughtType}; `null` for types with\n * no recorded outcomes.\n */\nfunction perTypeBrier(\n\toutcomes: readonly VerificationOutcome[]\n): Record<ThoughtType, number | null> {\n\tconst buckets = new Map<string, VerificationOutcome[]>();\n\tfor (const o of outcomes) {\n\t\tconst list = buckets.get(o.type) ?? [];\n\t\tlist.push(o);\n\t\tbuckets.set(o.type, list);\n\t}\n\tconst result = {} as Record<ThoughtType, number | null>;\n\tfor (const t of ALL_THOUGHT_TYPES) {\n\t\tresult[t] = brierScore(buckets.get(t) ?? []);\n\t}\n\treturn result;\n}\n\n/** Empty CalibrationMetrics returned when calibration is disabled. */\nfunction emptyMetrics(): CalibrationMetrics {\n\tconst perType = {} as Record<ThoughtType, number | null>;\n\tfor (const t of ALL_THOUGHT_TYPES) perType[t] = null;\n\treturn {\n\t\tbrierScore: null,\n\t\tece: null,\n\t\tsampleCount: 0,\n\t\tperTypeBrier: perType,\n\t};\n}\n\n/**\n * Confidence calibration service.\n *\n * Stateless w.r.t. outcome storage (delegated to {@link IOutcomeRecorder}),\n * but maintains a small per-session temperature cache that is recomputed\n * via {@link Calibrator.refit}.\n *\n * @example\n * ```typescript\n * const calibrator = new Calibrator(outcomeRecorder, true);\n * const result = calibrator.calibrate(0.9, 'hypothesis', 'session-1');\n * console.log(result.calibrated, result.temperature, result.priorWeight);\n * ```\n */\nexport class Calibrator implements ICalibrator {\n\tpublic readonly enabled: boolean;\n\tprivate readonly _recorder: IOutcomeRecorder;\n\tprivate readonly _temperatures = new Map<SessionId | typeof GLOBAL_KEY, number>();\n\n\tconstructor(outcomeRecorder: IOutcomeRecorder, enabled: boolean) {\n\t\tthis._recorder = outcomeRecorder;\n\t\tthis.enabled = enabled;\n\t}\n\n\tpublic calibrate(\n\t\trawConfidence: number,\n\t\ttype: ThoughtType,\n\t\tsessionId: SessionId\n\t): CalibrationResult {\n\t\tconst raw = Math.min(1, Math.max(0, rawConfidence));\n\t\tif (!this.enabled) {\n\t\t\treturn { raw, calibrated: raw, temperature: 1.0, priorWeight: 0 };\n\t\t}\n\t\tconst outcomes = this._recorder.getOutcomes(sessionId);\n\t\tconst perType = aggregatePerType(outcomes);\n\t\tconst typeStats = perType.get(type);\n\t\tconst n = typeStats?.count ?? 0;\n\t\tconst observedMean = typeStats?.mean ?? 0.5;\n\t\tconst priorWeight = 1 / (1 + n / 10);\n\t\tconst shrunk = priorWeight * observedMean + (1 - priorWeight) * raw;\n\t\tconst temperature =\n\t\t\tthis._temperatures.get(sessionId) ?? this._temperatures.get(GLOBAL_KEY) ?? 1.0;\n\t\tconst calibrated =\n\t\t\toutcomes.length >= MIN_OUTCOMES_FOR_TEMPERATURE\n\t\t\t\t? applyTemperature(shrunk, temperature)\n\t\t\t\t: shrunk;\n\t\treturn { raw, calibrated, temperature, priorWeight };\n\t}\n\n\tpublic metrics(sessionId?: SessionId): CalibrationMetrics {\n\t\tif (!this.enabled) return emptyMetrics();\n\t\tconst outcomes =\n\t\t\tsessionId === undefined\n\t\t\t\t? this._recorder.getAllOutcomes()\n\t\t\t\t: this._recorder.getOutcomes(sessionId);\n\t\treturn {\n\t\t\tbrierScore: brierScore(outcomes),\n\t\t\tece: expectedCalibrationError(outcomes),\n\t\t\tsampleCount: outcomes.length,\n\t\t\tperTypeBrier: perTypeBrier(outcomes),\n\t\t};\n\t}\n\n\tpublic refit(sessionId?: SessionId): void {\n\t\tif (!this.enabled) return;\n\t\tconst key = sessionId ?? GLOBAL_KEY;\n\t\tconst outcomes =\n\t\t\tsessionId === undefined\n\t\t\t\t? this._recorder.getAllOutcomes()\n\t\t\t\t: this._recorder.getOutcomes(sessionId);\n\t\tthis._temperatures.set(key, fitTemperature(outcomes));\n\t}\n\n\tpublic clearSession(sessionId: SessionId): void {\n\t\tif (!this.enabled) return;\n\t\tthis._temperatures.delete(sessionId);\n\t}\n\n\tpublic clearAll(): void {\n\t\tif (!this.enabled) return;\n\t\tthis._temperatures.clear();\n\t}\n}\n"],"names":["ECE_BINS","GLOBAL_KEY","Symbol","aggregatePerType","outcomes","sums","Map","o","prev","result","type","sum","count","brierScore","diff","expectedCalibrationError","binConfSum","Array","binAccSum","binCount","p","Math","EPSILON","idx","total","ece","i","n","meanConf","meanAcc","perTypeBrier","buckets","list","t","ALL_THOUGHT_TYPES","emptyMetrics","perType","Calibrator","outcomeRecorder","enabled","rawConfidence","sessionId","raw","typeStats","observedMean","priorWeight","shrunk","temperature","calibrated","MIN_OUTCOMES_FOR_TEMPERATURE","applyTemperature","undefined","key","fitTemperature"],"mappings":"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"}
1
+ {"version":3,"file":"core/evaluator/Calibrator.js","sources":["../../../src/core/evaluator/Calibrator.ts"],"sourcesContent":["/**\n * Confidence calibration for sequential thinking reasoning.\n *\n * Provides the {@link Calibrator} class — maps raw model confidence values\n * to calibrated probabilities using per-type Beta(2, 2) priors and\n * grid-searched temperature scaling. Reports calibration quality through\n * Brier score and 10-bin Expected Calibration Error (ECE).\n *\n * Math summary:\n * - **Per-type prior**: Beta(α=2, β=2), prior mean = 0.5. As outcomes accumulate\n * for a type, the calibrated value shrinks toward the observed mean of that\n * type with weight `priorWeight = 1 / (1 + n / 10)` where `n` is the type's\n * sample count.\n * - **Temperature scaling**: Grid search T ∈ {0.5, 0.75, 1.0, 1.25, 1.5, 2.0}\n * minimizing negative log-likelihood (NLL) over recorded outcomes. Default T=1.0.\n * Requires ≥10 outcomes; below that threshold only prior shrinkage is applied.\n * - **Brier score**: `mean((predicted - actual)^2)` over recorded outcomes.\n * - **ECE (10-bin)**: bucket predictions in 0.1 increments, compute weighted\n * absolute deviation between bin mean confidence and bin accuracy.\n *\n * @module core/evaluator/Calibrator\n */\n\nimport type {\n\tCalibrationMetrics,\n\tCalibrationResult,\n\tICalibrator,\n} from '../../contracts/calibrator.js';\nimport type { IOutcomeRecorder, VerificationOutcome } from '../../contracts/interfaces.js';\nimport type { ThoughtType } from '../../contracts/reasoning-types.js';\nimport type { SessionId } from '../../contracts/ids.js';\nimport {\n\tEPSILON,\n\tMIN_OUTCOMES_FOR_TEMPERATURE,\n\tapplyTemperature,\n\tfitTemperature,\n} from './calibration-math.js';\nimport { ALL_THOUGHT_TYPES } from './internals.js';\n\n/** Number of bins used by ECE (10-bin → 0.1 increments). */\nconst ECE_BINS = 10;\n\n/** Sentinel used in the per-session temperature map for global state. */\nconst GLOBAL_KEY: unique symbol = Symbol('global calibration temperature');\n\n/**\n * Build per-type empirical means + counts from a list of outcomes.\n *\n * @param outcomes - Outcomes to aggregate.\n * @returns Map of thought type to `{ mean, count }`. Types with no outcomes\n * are absent from the map.\n */\nfunction aggregatePerType(\n\toutcomes: readonly VerificationOutcome[]\n): Map<string, { mean: number; count: number }> {\n\tconst sums = new Map<string, { sum: number; count: number }>();\n\tfor (const o of outcomes) {\n\t\tconst prev = sums.get(o.type) ?? { sum: 0, count: 0 };\n\t\tprev.sum += o.actual;\n\t\tprev.count += 1;\n\t\tsums.set(o.type, prev);\n\t}\n\tconst result = new Map<string, { mean: number; count: number }>();\n\tfor (const [type, { sum, count }] of sums) {\n\t\tresult.set(type, { mean: sum / count, count });\n\t}\n\treturn result;\n}\n\n/**\n * Compute the Brier score over a list of outcomes.\n *\n * @param outcomes - Outcomes to score.\n * @returns Mean squared error between predicted probability and actual label,\n * or `null` if `outcomes` is empty.\n */\nfunction brierScore(outcomes: readonly VerificationOutcome[]): number | null {\n\tif (outcomes.length === 0) return null;\n\tlet sum = 0;\n\tfor (const o of outcomes) {\n\t\tconst diff = o.predicted - o.actual;\n\t\tsum += diff * diff;\n\t}\n\treturn sum / outcomes.length;\n}\n\n/**\n * Compute 10-bin Expected Calibration Error.\n *\n * Buckets predictions in 0.1-wide bins, then sums weighted absolute\n * differences between bin mean confidence and bin accuracy.\n *\n * @param outcomes - Outcomes to evaluate.\n * @returns ECE in `[0, 1]`, or `null` if `outcomes` is empty.\n */\nfunction expectedCalibrationError(outcomes: readonly VerificationOutcome[]): number | null {\n\tif (outcomes.length === 0) return null;\n\tconst binConfSum = new Array<number>(ECE_BINS).fill(0);\n\tconst binAccSum = new Array<number>(ECE_BINS).fill(0);\n\tconst binCount = new Array<number>(ECE_BINS).fill(0);\n\tfor (const o of outcomes) {\n\t\tconst p = Math.min(1 - EPSILON, Math.max(0, o.predicted));\n\t\tconst idx = Math.min(ECE_BINS - 1, Math.floor(p * ECE_BINS));\n\t\tbinConfSum[idx] = (binConfSum[idx] ?? 0) + p;\n\t\tbinAccSum[idx] = (binAccSum[idx] ?? 0) + o.actual;\n\t\tbinCount[idx] = (binCount[idx] ?? 0) + 1;\n\t}\n\tconst total = outcomes.length;\n\tlet ece = 0;\n\tfor (let i = 0; i < ECE_BINS; i++) {\n\t\tconst n = binCount[i] ?? 0;\n\t\tif (n === 0) continue;\n\t\tconst meanConf = (binConfSum[i] ?? 0) / n;\n\t\tconst meanAcc = (binAccSum[i] ?? 0) / n;\n\t\tece += (n / total) * Math.abs(meanConf - meanAcc);\n\t}\n\treturn ece;\n}\n\n/**\n * Build the per-type Brier score breakdown for {@link CalibrationMetrics}.\n *\n * @param outcomes - Outcomes to bucket by type.\n * @returns Record keyed by every {@link ThoughtType}; `null` for types with\n * no recorded outcomes.\n */\nfunction perTypeBrier(\n\toutcomes: readonly VerificationOutcome[]\n): Record<ThoughtType, number | null> {\n\tconst buckets = new Map<string, VerificationOutcome[]>();\n\tfor (const o of outcomes) {\n\t\tconst list = buckets.get(o.type) ?? [];\n\t\tlist.push(o);\n\t\tbuckets.set(o.type, list);\n\t}\n\tconst result = {} as Record<ThoughtType, number | null>;\n\tfor (const t of ALL_THOUGHT_TYPES) {\n\t\tresult[t] = brierScore(buckets.get(t) ?? []);\n\t}\n\treturn result;\n}\n\n/** Empty CalibrationMetrics returned when calibration is disabled. */\nfunction emptyMetrics(): CalibrationMetrics {\n\tconst perType = {} as Record<ThoughtType, number | null>;\n\tfor (const t of ALL_THOUGHT_TYPES) perType[t] = null;\n\treturn {\n\t\tbrierScore: null,\n\t\tece: null,\n\t\tsampleCount: 0,\n\t\tperTypeBrier: perType,\n\t};\n}\n\n/**\n * Confidence calibration service.\n *\n * Stateless w.r.t. outcome storage (delegated to {@link IOutcomeRecorder}),\n * but maintains a small per-session temperature cache that is recomputed\n * via {@link Calibrator.refit}.\n *\n * @example\n * ```typescript\n * const calibrator = new Calibrator(outcomeRecorder, true);\n * const result = calibrator.calibrate(0.9, 'hypothesis', 'session-1');\n * console.log(result.calibrated, result.temperature, result.priorWeight);\n * ```\n */\nexport class Calibrator implements ICalibrator {\n\tpublic readonly enabled: boolean;\n\tprivate readonly _recorder: IOutcomeRecorder;\n\tprivate readonly _temperatures = new Map<SessionId | typeof GLOBAL_KEY, number>();\n\n\tconstructor(outcomeRecorder: IOutcomeRecorder, enabled: boolean) {\n\t\tthis._recorder = outcomeRecorder;\n\t\tthis.enabled = enabled;\n\t}\n\n\tpublic calibrate(\n\t\trawConfidence: number,\n\t\ttype: ThoughtType,\n\t\tsessionId: SessionId\n\t): CalibrationResult {\n\t\tconst raw = Math.min(1, Math.max(0, rawConfidence));\n\t\tif (!this.enabled) {\n\t\t\treturn { raw, calibrated: raw, temperature: 1.0, priorWeight: 0 };\n\t\t}\n\t\tconst outcomes = this._recorder.getOutcomes(sessionId);\n\t\tconst perType = aggregatePerType(outcomes);\n\t\tconst typeStats = perType.get(type);\n\t\tconst n = typeStats?.count ?? 0;\n\t\tconst observedMean = typeStats?.mean ?? 0.5;\n\t\tconst priorWeight = 1 / (1 + n / 10);\n\t\tconst shrunk = priorWeight * observedMean + (1 - priorWeight) * raw;\n\t\tconst temperature =\n\t\t\tthis._temperatures.get(sessionId) ?? this._temperatures.get(GLOBAL_KEY) ?? 1.0;\n\t\tconst calibrated =\n\t\t\toutcomes.length >= MIN_OUTCOMES_FOR_TEMPERATURE\n\t\t\t\t? applyTemperature(shrunk, temperature)\n\t\t\t\t: shrunk;\n\t\treturn { raw, calibrated, temperature, priorWeight };\n\t}\n\n\tpublic metrics(sessionId?: SessionId): CalibrationMetrics {\n\t\tif (!this.enabled) return emptyMetrics();\n\t\tconst outcomes =\n\t\t\tsessionId === undefined\n\t\t\t\t? this._recorder.getAllOutcomes()\n\t\t\t\t: this._recorder.getOutcomes(sessionId);\n\t\treturn {\n\t\t\tbrierScore: brierScore(outcomes),\n\t\t\tece: expectedCalibrationError(outcomes),\n\t\t\tsampleCount: outcomes.length,\n\t\t\tperTypeBrier: perTypeBrier(outcomes),\n\t\t};\n\t}\n\n\tpublic refit(sessionId?: SessionId): void {\n\t\tif (!this.enabled) return;\n\t\tconst key = sessionId ?? GLOBAL_KEY;\n\t\tconst outcomes =\n\t\t\tsessionId === undefined\n\t\t\t\t? this._recorder.getAllOutcomes()\n\t\t\t\t: this._recorder.getOutcomes(sessionId);\n\t\tthis._temperatures.set(key, fitTemperature(outcomes));\n\t}\n\n\tpublic clearSession(sessionId: SessionId): void {\n\t\tif (!this.enabled) return;\n\t\tthis._temperatures.delete(sessionId);\n\t}\n\n\tpublic clearAll(): void {\n\t\tif (!this.enabled) return;\n\t\tthis._temperatures.clear();\n\t}\n}\n"],"names":["ECE_BINS","GLOBAL_KEY","Symbol","aggregatePerType","outcomes","sums","Map","o","prev","result","type","sum","count","brierScore","diff","expectedCalibrationError","binConfSum","Array","binAccSum","binCount","p","Math","EPSILON","idx","total","ece","i","n","meanConf","meanAcc","perTypeBrier","buckets","list","t","ALL_THOUGHT_TYPES","emptyMetrics","perType","Calibrator","outcomeRecorder","enabled","rawConfidence","sessionId","raw","typeStats","observedMean","priorWeight","shrunk","temperature","calibrated","MIN_OUTCOMES_FOR_TEMPERATURE","applyTemperature","undefined","key","fitTemperature"],"mappings":"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"}
@@ -10,7 +10,8 @@
10
10
  *
11
11
  * @module core/evaluator/SignalComputer
12
12
  */
13
- import type { ConfidenceSignals, ThoughtType } from '../reasoning.js';
13
+ import type { ThoughtType } from '../../contracts/reasoning-types.js';
14
+ import type { ConfidenceSignals } from '../reasoning.js';
14
15
  import type { ThoughtData } from '../thought.js';
15
16
  /** Result of {@link SignalComputer.computeStructuralQuality}. */
16
17
  interface StructuralQualityResult {
@@ -1 +1 @@
1
- {"version":3,"file":"SignalComputer.d.ts","sourceRoot":"","sources":["../../../src/core/evaluator/SignalComputer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACtE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAgBjD,iEAAiE;AACjE,UAAU,uBAAuB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE;QACX,cAAc,EAAE,MAAM,CAAC;QACvB,qBAAqB,EAAE,MAAM,CAAC;QAC9B,gBAAgB,EAAE,MAAM,CAAC;QACzB,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;KACpC,CAAC;IACF,cAAc,EAAE;QACf,cAAc,EAAE,MAAM,CAAC;QACvB,qBAAqB,EAAE,MAAM,CAAC;QAC9B,gBAAgB,EAAE,MAAM,CAAC;QACzB,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;KACpC,CAAC;CACF;AAED;;;;;;GAMG;AACH,qBAAa,cAAc;IAC1B;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2BG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,eAAe,CAK5B;IAEX;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,qBAAqB,CAIlC;IAEX;;;;;;OAMG;IACI,wBAAwB,CAC9B,OAAO,EAAE,WAAW,EAAE,EACtB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC,GACrC,iBAAiB;IAiCpB;;;;;;;;;;;;;;;OAeG;IACI,wBAAwB,CAC9B,OAAO,EAAE,WAAW,EAAE,EACtB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC,EACvC,gBAAgB,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,EAC7C,WAAW,EAAE,MAAM,EAAE,GACnB,uBAAuB,GAAG,IAAI;IA+EjC;;;;;;OAMG;IACI,0BAA0B,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,IAAI;IAUvE;;;;;;OAMG;IACI,6BAA6B,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,IAAI;CAS1E"}
1
+ {"version":3,"file":"SignalComputer.d.ts","sourceRoot":"","sources":["../../../src/core/evaluator/SignalComputer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oCAAoC,CAAC;AACtE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAgBjD,iEAAiE;AACjE,UAAU,uBAAuB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE;QACX,cAAc,EAAE,MAAM,CAAC;QACvB,qBAAqB,EAAE,MAAM,CAAC;QAC9B,gBAAgB,EAAE,MAAM,CAAC;QACzB,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;KACpC,CAAC;IACF,cAAc,EAAE;QACf,cAAc,EAAE,MAAM,CAAC;QACvB,qBAAqB,EAAE,MAAM,CAAC;QAC9B,gBAAgB,EAAE,MAAM,CAAC;QACzB,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;KACpC,CAAC;CACF;AAED;;;;;;GAMG;AACH,qBAAa,cAAc;IAC1B;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2BG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,eAAe,CAK5B;IAEX;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,qBAAqB,CAIlC;IAEX;;;;;;OAMG;IACI,wBAAwB,CAC9B,OAAO,EAAE,WAAW,EAAE,EACtB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC,GACrC,iBAAiB;IAiCpB;;;;;;;;;;;;;;;OAeG;IACI,wBAAwB,CAC9B,OAAO,EAAE,WAAW,EAAE,EACtB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC,EACvC,gBAAgB,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,EAC7C,WAAW,EAAE,MAAM,EAAE,GACnB,uBAAuB,GAAG,IAAI;IA+EjC;;;;;;OAMG;IACI,0BAA0B,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,IAAI;IAUvE;;;;;;OAMG;IACI,6BAA6B,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,IAAI;CAS1E"}
@@ -1 +1 @@
1
- {"version":3,"file":"core/evaluator/SignalComputer.js","sources":["../../../src/core/evaluator/SignalComputer.ts"],"sourcesContent":["/**\n * Confidence signal computation for sequential thinking.\n *\n * Provides {@link SignalComputer} — a pure stateless service that computes\n * {@link ConfidenceSignals} (including the composite `structural_quality`\n * score and its components) from thought history and branch data.\n *\n * Extracted from `ThoughtEvaluator` as part of the evaluator decomposition.\n * All methods are pure: no side effects, no I/O, no internal state.\n *\n * @module core/evaluator/SignalComputer\n */\n\nimport type { ConfidenceSignals, ThoughtType } from '../reasoning.js';\nimport type { ThoughtData } from '../thought.js';\nimport { ALL_THOUGHT_TYPES, _computeChainDepth, _countByType } from './internals.js';\n\n/** Floor value applied to each quality component to prevent geometric mean collapse. */\nconst FLOOR = 0.01;\n\n/**\n * Round a numeric value to a fixed number of decimal places to mitigate\n * IEEE 754 floating-point accumulation errors (e.g. 0.9 + 0.8 averaging to\n * 0.8500000000000001 instead of 0.85).\n */\nfunction roundToPrecision(value: number, decimals: number = 10): number {\n\tconst factor = Math.pow(10, decimals);\n\treturn Math.round(value * factor) / factor;\n}\n\n/** Result of {@link SignalComputer.computeStructuralQuality}. */\ninterface StructuralQualityResult {\n\tscore: number;\n\tcomponents: {\n\t\ttype_diversity: number;\n\t\tverification_coverage: number;\n\t\tdepth_efficiency: number;\n\t\tconfidence_stability: number | null;\n\t};\n\traw_components: {\n\t\ttype_diversity: number;\n\t\tverification_coverage: number;\n\t\tdepth_efficiency: number;\n\t\tconfidence_stability: number | null;\n\t};\n}\n\n/**\n * Stateless service that computes {@link ConfidenceSignals} from thought history.\n *\n * @remarks\n * Pure computation — no side effects, no I/O, no internal state.\n * Safe to register as singleton or transient in DI.\n */\nexport class SignalComputer {\n\t/**\n\t * Structural quality weight configuration.\n\t *\n\t * Weights reflect the relative importance of each quality dimension:\n\t * - type_diversity (0.3): Shannon entropy of thought types — rewards reasoning that uses\n\t * multiple cognitive modes (hypothesis, verification, critique, synthesis) rather than\n\t * monotonic sequences. Based on information theory: higher entropy = more information.\n\t *\n\t * - verification_coverage (0.3): Ratio of verified to total hypotheses — rewards scientific\n\t * rigor. Hypotheses without verification are speculation; verification grounds reasoning.\n\t * Equal weight with diversity because unverified reasoning is a common failure mode.\n\t *\n\t * - depth_efficiency (0.2): Ratio of structural depth to total thoughts — rewards efficient\n\t * reasoning. Deep chains (many revisions/branches) are good; but only if proportionally\n\t * dense. Padding with low-value thoughts penalizes this metric.\n\t *\n\t * - confidence_stability (0.2): Low variance in self-assessed confidence — rewards calibrated\n\t * reasoning. Wildly fluctuating confidence suggests uncertainty about the approach.\n\t * Lower weight because confidence values are LLM self-reports (inherently noisy).\n\t *\n\t * Design rationale:\n\t * - Diversity + Verification (0.6) > Depth + Stability (0.4): Structural properties of the\n\t * reasoning DAG are more important than behavioral signals.\n\t * - All weights are positive (no dimension is penalizing).\n\t * - Weights sum to 1.0 for normalized scoring.\n\t * - When confidence_stability is unavailable (n<2), remaining weights redistribute\n\t * proportionally: td→0.375, vc→0.375, de→0.25 (preserving relative ratios).\n\t */\n\tprivate static readonly QUALITY_WEIGHTS = {\n\t\ttypeDiversity: 0.3,\n\t\tverificationCoverage: 0.3,\n\t\tdepthEfficiency: 0.2,\n\t\tconfidenceStability: 0.2,\n\t} as const;\n\n\t/**\n\t * Redistributed weights when confidence_stability is excluded (n<2).\n\t * Preserves relative ratios of remaining components (sum = 1.0).\n\t */\n\tprivate static readonly QUALITY_WEIGHTS_NO_CS = {\n\t\ttypeDiversity: 0.375,\n\t\tverificationCoverage: 0.375,\n\t\tdepthEfficiency: 0.25,\n\t} as const;\n\n\t/**\n\t * Compute confidence signals from history context.\n\t *\n\t * @param history - All thoughts in the current session\n\t * @param branches - Map of branch IDs to their thought arrays\n\t * @returns Computed confidence signals reflecting reasoning quality\n\t */\n\tpublic computeConfidenceSignals(\n\t\thistory: ThoughtData[],\n\t\tbranches: Record<string, ThoughtData[]>\n\t): ConfidenceSignals {\n\t\tconst typeDistribution = _countByType(history);\n\t\tconst allConfidences = history\n\t\t\t.map((t) => t.confidence)\n\t\t\t.filter((c): c is number => c !== undefined);\n\n\t\t// Compute structural quality components\n\t\tconst structuralResult = this.computeStructuralQuality(\n\t\t\thistory,\n\t\t\tbranches,\n\t\t\ttypeDistribution,\n\t\t\tallConfidences\n\t\t);\n\n\t\treturn {\n\t\t\treasoning_depth: history.length,\n\t\t\trevision_count: history.filter((t) => t.is_revision).length,\n\t\t\tbranch_count: Object.keys(branches).length,\n\t\t\tthought_type_distribution: typeDistribution,\n\t\t\thas_hypothesis: history.some((t) => t.thought_type === 'hypothesis'),\n\t\t\thas_verification: history.some((t) => t.thought_type === 'verification'),\n\t\t\taverage_confidence:\n\t\t\t\tallConfidences.length > 0\n\t\t\t\t\t? roundToPrecision(allConfidences.reduce((a, b) => a + b, 0) / allConfidences.length)\n\t\t\t\t\t: null,\n\t\t\t...(structuralResult !== null && {\n\t\t\t\tstructural_quality: structuralResult.score,\n\t\t\t\tquality_components: structuralResult.components,\n\t\t\t\tquality_components_raw: structuralResult.raw_components,\n\t\t\t}),\n\t\t};\n\t}\n\n\t/**\n\t * Compute the composite structural quality score and its components.\n\t *\n\t * Algorithm (all components floored at {@link FLOOR} = 0.01):\n\t * - `type_diversity` = Shannon entropy of thought_type distribution / log2(6)\n\t * - `verification_coverage` = verified_hypotheses / max(total_hypotheses, 1) (1.0 if none)\n\t * - `depth_efficiency` = max(chain_depth, branch_count + 1) / total_thoughts, clamped to 1.0\n\t * - `confidence_stability` = 1 - stddev(confidences), default 0.5 when empty, null when single value\n\t * - `structural_quality` = td^0.3 * vc^0.3 * de^0.2 * cs^0.2 (weighted geometric mean)\n\t *\n\t * Note: When confidence_stability is null (fewer than 2 confidence values), it is\n\t * excluded from the geometric mean and the remaining weights are redistributed\n\t * proportionally (td→0.375, vc→0.375, de→0.25).\n\t *\n\t * @returns Score + components, or `null` when history is empty\n\t */\n\tpublic computeStructuralQuality(\n\t\thistory: ThoughtData[],\n\t\tbranches: Record<string, ThoughtData[]>,\n\t\ttypeDistribution: Record<ThoughtType, number>,\n\t\tconfidences: number[]\n\t): StructuralQualityResult | null {\n\t\tif (history.length === 0) return null;\n\n\t\t// 1. type_diversity: Shannon entropy / log2(6)\n\t\tconst total = history.length;\n\t\tlet entropy = 0;\n\t\tfor (const type of ALL_THOUGHT_TYPES) {\n\t\t\tconst count = typeDistribution[type];\n\t\t\tif (count > 0) {\n\t\t\t\tconst pk = count / total;\n\t\t\t\tentropy -= pk * Math.log2(pk);\n\t\t\t}\n\t\t}\n\t\tconst rawTypeDiversity = entropy / Math.log2(6);\n\t\tconst typeDiversity = Math.max(rawTypeDiversity, FLOOR);\n\n\t\t// 2. verification_coverage: verified / total hypotheses (1.0 if none)\n\t\tconst hypotheses = history.filter((t) => t.thought_type === 'hypothesis');\n\t\tconst hypothesisIds = new Set(hypotheses.map((t) => t.hypothesis_id).filter(Boolean));\n\t\tconst verifiedIds = new Set(\n\t\t\thistory\n\t\t\t\t.filter((t) => t.thought_type === 'verification' && t.hypothesis_id)\n\t\t\t\t.map((t) => t.hypothesis_id)\n\t\t);\n\t\tconst rawVerificationCoverage =\n\t\t\thypothesisIds.size === 0\n\t\t\t\t? 1.0\n\t\t\t\t: [...hypothesisIds].filter((id) => verifiedIds.has(id)).length / hypothesisIds.size;\n\t\tconst verificationCoverage = Math.max(rawVerificationCoverage, FLOOR);\n\n\t\t// 3. depth_efficiency: max(chain_depth, branch_count + 1) / total_thoughts, clamped to 1.0\n\t\t// NOTE (Metis H3): Branching is desirable — treat branches as depth-equivalent.\n\t\tconst chainDepth = _computeChainDepth(history);\n\t\tconst branchCount = Object.keys(branches).length;\n\t\tconst effectiveDepth = Math.max(chainDepth, branchCount + 1);\n\t\tconst rawDepthEfficiency = Math.min(effectiveDepth / total, 1.0);\n\t\tconst depthEfficiency = Math.max(rawDepthEfficiency, FLOOR);\n\n\t\t// 4. confidence_stability: 1 - stddev(confidences), default 0.5\n\t\tconst confidenceStability = this.computeConfidenceStability(confidences);\n\t\tconst rawConfidenceStability = this.computeRawConfidenceStability(confidences);\n\n\t\tconst components: StructuralQualityResult['components'] = {\n\t\t\ttype_diversity: typeDiversity,\n\t\t\tverification_coverage: verificationCoverage,\n\t\t\tdepth_efficiency: depthEfficiency,\n\t\t\tconfidence_stability: confidenceStability,\n\t\t};\n\n\t\tconst rawComponents: StructuralQualityResult['raw_components'] = {\n\t\t\ttype_diversity: rawTypeDiversity,\n\t\t\tverification_coverage: rawVerificationCoverage,\n\t\t\tdepth_efficiency: rawDepthEfficiency,\n\t\t\tconfidence_stability: rawConfidenceStability,\n\t\t};\n\n\t\t// Weighted geometric mean: td^0.3 * vc^0.3 * de^0.2 * cs^0.2\n\t\t// When confidence_stability is null (n<2), exclude it from the geomean\n\t\t// and redistribute its weight proportionally to remaining components\n\t\t// (normalized td=0.375, vc=0.375, de=0.25)\n\t\tlet score: number;\n\t\tif (confidenceStability !== null) {\n\t\t\tconst w = SignalComputer.QUALITY_WEIGHTS;\n\t\t\tscore =\n\t\t\t\tMath.pow(typeDiversity, w.typeDiversity) *\n\t\t\t\tMath.pow(verificationCoverage, w.verificationCoverage) *\n\t\t\t\tMath.pow(depthEfficiency, w.depthEfficiency) *\n\t\t\t\tMath.pow(confidenceStability, w.confidenceStability);\n\t\t} else {\n\t\t\tconst w3 = SignalComputer.QUALITY_WEIGHTS_NO_CS;\n\t\t\tscore =\n\t\t\t\tMath.pow(typeDiversity, w3.typeDiversity) *\n\t\t\t\tMath.pow(verificationCoverage, w3.verificationCoverage) *\n\t\t\t\tMath.pow(depthEfficiency, w3.depthEfficiency);\n\t\t}\n\n\t\treturn { score, components, raw_components: rawComponents };\n\t}\n\n\t/**\n\t * Compute confidence stability: 1 - stddev(confidences).\n\t *\n\t * - Empty input → 0.5 (neutral default)\n\t * - Single value → null (insufficient data, excluded from structural quality)\n\t * - Otherwise → max(1 - stddev, FLOOR)\n\t */\n\tpublic computeConfidenceStability(confidences: number[]): number | null {\n\t\tif (confidences.length === 0) return 0.5;\n\t\tif (confidences.length === 1) return null;\n\n\t\tconst mean = confidences.reduce((a, b) => a + b, 0) / confidences.length;\n\t\tconst variance = confidences.reduce((sum, c) => sum + (c - mean) ** 2, 0) / confidences.length;\n\t\tconst stddev = Math.sqrt(variance);\n\t\treturn Math.max(1 - stddev, FLOOR);\n\t}\n\n\t/**\n\t * Compute raw (unfloored) confidence stability: 1 - stddev(confidences).\n\t *\n\t * - Empty input → 0.5 (neutral default)\n\t * - Single value → null (insufficient data)\n\t * - Otherwise → 1 - stddev (may be below FLOOR)\n\t */\n\tpublic computeRawConfidenceStability(confidences: number[]): number | null {\n\t\tif (confidences.length === 0) return 0.5;\n\t\tif (confidences.length === 1) return null;\n\n\t\tconst mean = confidences.reduce((a, b) => a + b, 0) / confidences.length;\n\t\tconst variance = confidences.reduce((sum, c) => sum + (c - mean) ** 2, 0) / confidences.length;\n\t\tconst stddev = Math.sqrt(variance);\n\t\treturn 1 - stddev;\n\t}\n}\n"],"names":["FLOOR","roundToPrecision","value","decimals","factor","Math","SignalComputer","history","branches","typeDistribution","_countByType","allConfidences","t","c","undefined","structuralResult","Object","a","b","confidences","total","entropy","type","ALL_THOUGHT_TYPES","count","pk","rawTypeDiversity","typeDiversity","hypotheses","hypothesisIds","Set","Boolean","verifiedIds","rawVerificationCoverage","id","verificationCoverage","chainDepth","_computeChainDepth","branchCount","effectiveDepth","rawDepthEfficiency","depthEfficiency","confidenceStability","rawConfidenceStability","components","rawComponents","score","w","w3","mean","variance","sum","stddev"],"mappings":"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"}
1
+ {"version":3,"file":"core/evaluator/SignalComputer.js","sources":["../../../src/core/evaluator/SignalComputer.ts"],"sourcesContent":["/**\n * Confidence signal computation for sequential thinking.\n *\n * Provides {@link SignalComputer} — a pure stateless service that computes\n * {@link ConfidenceSignals} (including the composite `structural_quality`\n * score and its components) from thought history and branch data.\n *\n * Extracted from `ThoughtEvaluator` as part of the evaluator decomposition.\n * All methods are pure: no side effects, no I/O, no internal state.\n *\n * @module core/evaluator/SignalComputer\n */\n\nimport type { ThoughtType } from '../../contracts/reasoning-types.js';\nimport type { ConfidenceSignals } from '../reasoning.js';\nimport type { ThoughtData } from '../thought.js';\nimport { ALL_THOUGHT_TYPES, _computeChainDepth, _countByType } from './internals.js';\n\n/** Floor value applied to each quality component to prevent geometric mean collapse. */\nconst FLOOR = 0.01;\n\n/**\n * Round a numeric value to a fixed number of decimal places to mitigate\n * IEEE 754 floating-point accumulation errors (e.g. 0.9 + 0.8 averaging to\n * 0.8500000000000001 instead of 0.85).\n */\nfunction roundToPrecision(value: number, decimals: number = 10): number {\n\tconst factor = Math.pow(10, decimals);\n\treturn Math.round(value * factor) / factor;\n}\n\n/** Result of {@link SignalComputer.computeStructuralQuality}. */\ninterface StructuralQualityResult {\n\tscore: number;\n\tcomponents: {\n\t\ttype_diversity: number;\n\t\tverification_coverage: number;\n\t\tdepth_efficiency: number;\n\t\tconfidence_stability: number | null;\n\t};\n\traw_components: {\n\t\ttype_diversity: number;\n\t\tverification_coverage: number;\n\t\tdepth_efficiency: number;\n\t\tconfidence_stability: number | null;\n\t};\n}\n\n/**\n * Stateless service that computes {@link ConfidenceSignals} from thought history.\n *\n * @remarks\n * Pure computation — no side effects, no I/O, no internal state.\n * Safe to register as singleton or transient in DI.\n */\nexport class SignalComputer {\n\t/**\n\t * Structural quality weight configuration.\n\t *\n\t * Weights reflect the relative importance of each quality dimension:\n\t * - type_diversity (0.3): Shannon entropy of thought types — rewards reasoning that uses\n\t * multiple cognitive modes (hypothesis, verification, critique, synthesis) rather than\n\t * monotonic sequences. Based on information theory: higher entropy = more information.\n\t *\n\t * - verification_coverage (0.3): Ratio of verified to total hypotheses — rewards scientific\n\t * rigor. Hypotheses without verification are speculation; verification grounds reasoning.\n\t * Equal weight with diversity because unverified reasoning is a common failure mode.\n\t *\n\t * - depth_efficiency (0.2): Ratio of structural depth to total thoughts — rewards efficient\n\t * reasoning. Deep chains (many revisions/branches) are good; but only if proportionally\n\t * dense. Padding with low-value thoughts penalizes this metric.\n\t *\n\t * - confidence_stability (0.2): Low variance in self-assessed confidence — rewards calibrated\n\t * reasoning. Wildly fluctuating confidence suggests uncertainty about the approach.\n\t * Lower weight because confidence values are LLM self-reports (inherently noisy).\n\t *\n\t * Design rationale:\n\t * - Diversity + Verification (0.6) > Depth + Stability (0.4): Structural properties of the\n\t * reasoning DAG are more important than behavioral signals.\n\t * - All weights are positive (no dimension is penalizing).\n\t * - Weights sum to 1.0 for normalized scoring.\n\t * - When confidence_stability is unavailable (n<2), remaining weights redistribute\n\t * proportionally: td→0.375, vc→0.375, de→0.25 (preserving relative ratios).\n\t */\n\tprivate static readonly QUALITY_WEIGHTS = {\n\t\ttypeDiversity: 0.3,\n\t\tverificationCoverage: 0.3,\n\t\tdepthEfficiency: 0.2,\n\t\tconfidenceStability: 0.2,\n\t} as const;\n\n\t/**\n\t * Redistributed weights when confidence_stability is excluded (n<2).\n\t * Preserves relative ratios of remaining components (sum = 1.0).\n\t */\n\tprivate static readonly QUALITY_WEIGHTS_NO_CS = {\n\t\ttypeDiversity: 0.375,\n\t\tverificationCoverage: 0.375,\n\t\tdepthEfficiency: 0.25,\n\t} as const;\n\n\t/**\n\t * Compute confidence signals from history context.\n\t *\n\t * @param history - All thoughts in the current session\n\t * @param branches - Map of branch IDs to their thought arrays\n\t * @returns Computed confidence signals reflecting reasoning quality\n\t */\n\tpublic computeConfidenceSignals(\n\t\thistory: ThoughtData[],\n\t\tbranches: Record<string, ThoughtData[]>\n\t): ConfidenceSignals {\n\t\tconst typeDistribution = _countByType(history);\n\t\tconst allConfidences = history\n\t\t\t.map((t) => t.confidence)\n\t\t\t.filter((c): c is number => c !== undefined);\n\n\t\t// Compute structural quality components\n\t\tconst structuralResult = this.computeStructuralQuality(\n\t\t\thistory,\n\t\t\tbranches,\n\t\t\ttypeDistribution,\n\t\t\tallConfidences\n\t\t);\n\n\t\treturn {\n\t\t\treasoning_depth: history.length,\n\t\t\trevision_count: history.filter((t) => t.is_revision).length,\n\t\t\tbranch_count: Object.keys(branches).length,\n\t\t\tthought_type_distribution: typeDistribution,\n\t\t\thas_hypothesis: history.some((t) => t.thought_type === 'hypothesis'),\n\t\t\thas_verification: history.some((t) => t.thought_type === 'verification'),\n\t\t\taverage_confidence:\n\t\t\t\tallConfidences.length > 0\n\t\t\t\t\t? roundToPrecision(allConfidences.reduce((a, b) => a + b, 0) / allConfidences.length)\n\t\t\t\t\t: null,\n\t\t\t...(structuralResult !== null && {\n\t\t\t\tstructural_quality: structuralResult.score,\n\t\t\t\tquality_components: structuralResult.components,\n\t\t\t\tquality_components_raw: structuralResult.raw_components,\n\t\t\t}),\n\t\t};\n\t}\n\n\t/**\n\t * Compute the composite structural quality score and its components.\n\t *\n\t * Algorithm (all components floored at {@link FLOOR} = 0.01):\n\t * - `type_diversity` = Shannon entropy of thought_type distribution / log2(6)\n\t * - `verification_coverage` = verified_hypotheses / max(total_hypotheses, 1) (1.0 if none)\n\t * - `depth_efficiency` = max(chain_depth, branch_count + 1) / total_thoughts, clamped to 1.0\n\t * - `confidence_stability` = 1 - stddev(confidences), default 0.5 when empty, null when single value\n\t * - `structural_quality` = td^0.3 * vc^0.3 * de^0.2 * cs^0.2 (weighted geometric mean)\n\t *\n\t * Note: When confidence_stability is null (fewer than 2 confidence values), it is\n\t * excluded from the geometric mean and the remaining weights are redistributed\n\t * proportionally (td→0.375, vc→0.375, de→0.25).\n\t *\n\t * @returns Score + components, or `null` when history is empty\n\t */\n\tpublic computeStructuralQuality(\n\t\thistory: ThoughtData[],\n\t\tbranches: Record<string, ThoughtData[]>,\n\t\ttypeDistribution: Record<ThoughtType, number>,\n\t\tconfidences: number[]\n\t): StructuralQualityResult | null {\n\t\tif (history.length === 0) return null;\n\n\t\t// 1. type_diversity: Shannon entropy / log2(6)\n\t\tconst total = history.length;\n\t\tlet entropy = 0;\n\t\tfor (const type of ALL_THOUGHT_TYPES) {\n\t\t\tconst count = typeDistribution[type];\n\t\t\tif (count > 0) {\n\t\t\t\tconst pk = count / total;\n\t\t\t\tentropy -= pk * Math.log2(pk);\n\t\t\t}\n\t\t}\n\t\tconst rawTypeDiversity = entropy / Math.log2(6);\n\t\tconst typeDiversity = Math.max(rawTypeDiversity, FLOOR);\n\n\t\t// 2. verification_coverage: verified / total hypotheses (1.0 if none)\n\t\tconst hypotheses = history.filter((t) => t.thought_type === 'hypothesis');\n\t\tconst hypothesisIds = new Set(hypotheses.map((t) => t.hypothesis_id).filter(Boolean));\n\t\tconst verifiedIds = new Set(\n\t\t\thistory\n\t\t\t\t.filter((t) => t.thought_type === 'verification' && t.hypothesis_id)\n\t\t\t\t.map((t) => t.hypothesis_id)\n\t\t);\n\t\tconst rawVerificationCoverage =\n\t\t\thypothesisIds.size === 0\n\t\t\t\t? 1.0\n\t\t\t\t: [...hypothesisIds].filter((id) => verifiedIds.has(id)).length / hypothesisIds.size;\n\t\tconst verificationCoverage = Math.max(rawVerificationCoverage, FLOOR);\n\n\t\t// 3. depth_efficiency: max(chain_depth, branch_count + 1) / total_thoughts, clamped to 1.0\n\t\t// NOTE (Metis H3): Branching is desirable — treat branches as depth-equivalent.\n\t\tconst chainDepth = _computeChainDepth(history);\n\t\tconst branchCount = Object.keys(branches).length;\n\t\tconst effectiveDepth = Math.max(chainDepth, branchCount + 1);\n\t\tconst rawDepthEfficiency = Math.min(effectiveDepth / total, 1.0);\n\t\tconst depthEfficiency = Math.max(rawDepthEfficiency, FLOOR);\n\n\t\t// 4. confidence_stability: 1 - stddev(confidences), default 0.5\n\t\tconst confidenceStability = this.computeConfidenceStability(confidences);\n\t\tconst rawConfidenceStability = this.computeRawConfidenceStability(confidences);\n\n\t\tconst components: StructuralQualityResult['components'] = {\n\t\t\ttype_diversity: typeDiversity,\n\t\t\tverification_coverage: verificationCoverage,\n\t\t\tdepth_efficiency: depthEfficiency,\n\t\t\tconfidence_stability: confidenceStability,\n\t\t};\n\n\t\tconst rawComponents: StructuralQualityResult['raw_components'] = {\n\t\t\ttype_diversity: rawTypeDiversity,\n\t\t\tverification_coverage: rawVerificationCoverage,\n\t\t\tdepth_efficiency: rawDepthEfficiency,\n\t\t\tconfidence_stability: rawConfidenceStability,\n\t\t};\n\n\t\t// Weighted geometric mean: td^0.3 * vc^0.3 * de^0.2 * cs^0.2\n\t\t// When confidence_stability is null (n<2), exclude it from the geomean\n\t\t// and redistribute its weight proportionally to remaining components\n\t\t// (normalized td=0.375, vc=0.375, de=0.25)\n\t\tlet score: number;\n\t\tif (confidenceStability !== null) {\n\t\t\tconst w = SignalComputer.QUALITY_WEIGHTS;\n\t\t\tscore =\n\t\t\t\tMath.pow(typeDiversity, w.typeDiversity) *\n\t\t\t\tMath.pow(verificationCoverage, w.verificationCoverage) *\n\t\t\t\tMath.pow(depthEfficiency, w.depthEfficiency) *\n\t\t\t\tMath.pow(confidenceStability, w.confidenceStability);\n\t\t} else {\n\t\t\tconst w3 = SignalComputer.QUALITY_WEIGHTS_NO_CS;\n\t\t\tscore =\n\t\t\t\tMath.pow(typeDiversity, w3.typeDiversity) *\n\t\t\t\tMath.pow(verificationCoverage, w3.verificationCoverage) *\n\t\t\t\tMath.pow(depthEfficiency, w3.depthEfficiency);\n\t\t}\n\n\t\treturn { score, components, raw_components: rawComponents };\n\t}\n\n\t/**\n\t * Compute confidence stability: 1 - stddev(confidences).\n\t *\n\t * - Empty input → 0.5 (neutral default)\n\t * - Single value → null (insufficient data, excluded from structural quality)\n\t * - Otherwise → max(1 - stddev, FLOOR)\n\t */\n\tpublic computeConfidenceStability(confidences: number[]): number | null {\n\t\tif (confidences.length === 0) return 0.5;\n\t\tif (confidences.length === 1) return null;\n\n\t\tconst mean = confidences.reduce((a, b) => a + b, 0) / confidences.length;\n\t\tconst variance = confidences.reduce((sum, c) => sum + (c - mean) ** 2, 0) / confidences.length;\n\t\tconst stddev = Math.sqrt(variance);\n\t\treturn Math.max(1 - stddev, FLOOR);\n\t}\n\n\t/**\n\t * Compute raw (unfloored) confidence stability: 1 - stddev(confidences).\n\t *\n\t * - Empty input → 0.5 (neutral default)\n\t * - Single value → null (insufficient data)\n\t * - Otherwise → 1 - stddev (may be below FLOOR)\n\t */\n\tpublic computeRawConfidenceStability(confidences: number[]): number | null {\n\t\tif (confidences.length === 0) return 0.5;\n\t\tif (confidences.length === 1) return null;\n\n\t\tconst mean = confidences.reduce((a, b) => a + b, 0) / confidences.length;\n\t\tconst variance = confidences.reduce((sum, c) => sum + (c - mean) ** 2, 0) / confidences.length;\n\t\tconst stddev = Math.sqrt(variance);\n\t\treturn 1 - stddev;\n\t}\n}\n"],"names":["FLOOR","roundToPrecision","value","decimals","factor","Math","SignalComputer","history","branches","typeDistribution","_countByType","allConfidences","t","c","undefined","structuralResult","Object","a","b","confidences","total","entropy","type","ALL_THOUGHT_TYPES","count","pk","rawTypeDiversity","typeDiversity","hypotheses","hypothesisIds","Set","Boolean","verifiedIds","rawVerificationCoverage","id","verificationCoverage","chainDepth","_computeChainDepth","branchCount","effectiveDepth","rawDepthEfficiency","depthEfficiency","confidenceStability","rawConfidenceStability","components","rawComponents","score","w","w3","mean","variance","sum","stddev"],"mappings":"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"}
@@ -7,7 +7,7 @@
7
7
  *
8
8
  * @module core/evaluator/internals
9
9
  */
10
- import type { ThoughtType } from '../reasoning.js';
10
+ import type { ThoughtType } from '../../contracts/reasoning-types.js';
11
11
  import type { ThoughtData } from '../thought.js';
12
12
  /** All valid thought types for distribution counting. */
13
13
  export declare const ALL_THOUGHT_TYPES: ThoughtType[];
@@ -1 +1 @@
1
- {"version":3,"file":"internals.d.ts","sourceRoot":"","sources":["../../../src/core/evaluator/internals.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjD,yDAAyD;AACzD,eAAO,MAAM,iBAAiB,EAAE,WAAW,EAY1C,CAAC;AAEF;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,CAchF;AAED,mEAAmE;AACnE,eAAO,MAAM,WAAW,qBAAe,CAAC;AAExC;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,MAAM,CAgBjE;AAED,yEAAyE;AACzE,eAAO,MAAM,iBAAiB,2BAAqB,CAAC"}
1
+ {"version":3,"file":"internals.d.ts","sourceRoot":"","sources":["../../../src/core/evaluator/internals.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oCAAoC,CAAC;AACtE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjD,yDAAyD;AACzD,eAAO,MAAM,iBAAiB,EAAE,WAAW,EAY1C,CAAC;AAEF;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,CAchF;AAED,mEAAmE;AACnE,eAAO,MAAM,WAAW,qBAAe,CAAC;AAExC;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,MAAM,CAgBjE;AAED,yEAAyE;AACzE,eAAO,MAAM,iBAAiB,2BAAqB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"core/evaluator/internals.js","sources":["../../../src/core/evaluator/internals.ts"],"sourcesContent":["/**\n * Shared internal helpers for evaluator submodules.\n *\n * Pure utility functions used by SignalComputer (and future evaluator\n * components such as StatsComputer / PatternDetector). All helpers are\n * stateless and have no side effects.\n *\n * @module core/evaluator/internals\n */\n\nimport type { ThoughtType } from '../reasoning.js';\nimport type { ThoughtData } from '../thought.js';\n\n/** All valid thought types for distribution counting. */\nexport const ALL_THOUGHT_TYPES: ThoughtType[] = [\n\t'regular',\n\t'hypothesis',\n\t'verification',\n\t'critique',\n\t'synthesis',\n\t'meta',\n\t'tool_call',\n\t'tool_observation',\n\t'assumption',\n\t'decomposition',\n\t'backtrack',\n];\n\n/**\n * Count thoughts in `history` grouped by {@link ThoughtType}.\n *\n * Thoughts without an explicit `thought_type` are counted as `'regular'`.\n *\n * @param history - Thoughts to count\n * @returns Record mapping each ThoughtType to its occurrence count\n */\nexport function _countByType(history: ThoughtData[]): Record<ThoughtType, number> {\n\tconst counts = Object.fromEntries(ALL_THOUGHT_TYPES.map((t) => [t, 0])) as Record<\n\t\tThoughtType,\n\t\tnumber\n\t>;\n\n\tfor (const thought of history) {\n\t\tconst type = thought.thought_type ?? 'regular';\n\t\tif (type in counts) {\n\t\t\tcounts[type]++;\n\t\t}\n\t}\n\n\treturn counts;\n}\n\n/** Alias of {@link _countByType} without the underscore prefix. */\nexport const countByType = _countByType;\n\n/**\n * Compute the longest contiguous chain depth (sequence of thoughts without branching).\n *\n * A new chain starts whenever a thought has `branch_from_thought` set.\n *\n * @param history - Thoughts in chronological order\n * @returns Length of the longest non-branching chain (0 for empty history)\n */\nexport function _computeChainDepth(history: ThoughtData[]): number {\n\tif (history.length === 0) return 0;\n\tlet maxDepth = 1;\n\tlet currentDepth = 1;\n\n\tfor (let i = 1; i < history.length; i++) {\n\t\tconst thought = history[i]!;\n\t\tif (!thought.branch_from_thought) {\n\t\t\tcurrentDepth++;\n\t\t\tmaxDepth = Math.max(maxDepth, currentDepth);\n\t\t} else {\n\t\t\tcurrentDepth = 1;\n\t\t}\n\t}\n\n\treturn maxDepth;\n}\n\n/** Alias of {@link _computeChainDepth} without the underscore prefix. */\nexport const computeChainDepth = _computeChainDepth;\n"],"names":["ALL_THOUGHT_TYPES","_countByType","history","counts","Object","t","thought","type","countByType","_computeChainDepth","maxDepth","currentDepth","i","Math","computeChainDepth"],"mappings":"AAcO,MAAMA,oBAAmC;IAC/C;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;CACA;AAUM,SAASC,aAAaC,OAAsB;IAClD,MAAMC,SAASC,OAAO,WAAW,CAACJ,kBAAkB,GAAG,CAAC,CAACK,IAAM;YAACA;YAAG;SAAE;IAKrE,KAAK,MAAMC,WAAWJ,QAAS;QAC9B,MAAMK,OAAOD,QAAQ,YAAY,IAAI;QACrC,IAAIC,QAAQJ,QACXA,MAAM,CAACI,KAAK;IAEd;IAEA,OAAOJ;AACR;AAGO,MAAMK,cAAcP;AAUpB,SAASQ,mBAAmBP,OAAsB;IACxD,IAAIA,AAAmB,MAAnBA,QAAQ,MAAM,EAAQ,OAAO;IACjC,IAAIQ,WAAW;IACf,IAAIC,eAAe;IAEnB,IAAK,IAAIC,IAAI,GAAGA,IAAIV,QAAQ,MAAM,EAAEU,IAAK;QACxC,MAAMN,UAAUJ,OAAO,CAACU,EAAE;QAC1B,IAAKN,QAAQ,mBAAmB,EAI/BK,eAAe;aAJkB;YACjCA;YACAD,WAAWG,KAAK,GAAG,CAACH,UAAUC;QAC/B;IAGD;IAEA,OAAOD;AACR;AAGO,MAAMI,oBAAoBL"}
1
+ {"version":3,"file":"core/evaluator/internals.js","sources":["../../../src/core/evaluator/internals.ts"],"sourcesContent":["/**\n * Shared internal helpers for evaluator submodules.\n *\n * Pure utility functions used by SignalComputer (and future evaluator\n * components such as StatsComputer / PatternDetector). All helpers are\n * stateless and have no side effects.\n *\n * @module core/evaluator/internals\n */\n\nimport type { ThoughtType } from '../../contracts/reasoning-types.js';\nimport type { ThoughtData } from '../thought.js';\n\n/** All valid thought types for distribution counting. */\nexport const ALL_THOUGHT_TYPES: ThoughtType[] = [\n\t'regular',\n\t'hypothesis',\n\t'verification',\n\t'critique',\n\t'synthesis',\n\t'meta',\n\t'tool_call',\n\t'tool_observation',\n\t'assumption',\n\t'decomposition',\n\t'backtrack',\n];\n\n/**\n * Count thoughts in `history` grouped by {@link ThoughtType}.\n *\n * Thoughts without an explicit `thought_type` are counted as `'regular'`.\n *\n * @param history - Thoughts to count\n * @returns Record mapping each ThoughtType to its occurrence count\n */\nexport function _countByType(history: ThoughtData[]): Record<ThoughtType, number> {\n\tconst counts = Object.fromEntries(ALL_THOUGHT_TYPES.map((t) => [t, 0])) as Record<\n\t\tThoughtType,\n\t\tnumber\n\t>;\n\n\tfor (const thought of history) {\n\t\tconst type = thought.thought_type ?? 'regular';\n\t\tif (type in counts) {\n\t\t\tcounts[type]++;\n\t\t}\n\t}\n\n\treturn counts;\n}\n\n/** Alias of {@link _countByType} without the underscore prefix. */\nexport const countByType = _countByType;\n\n/**\n * Compute the longest contiguous chain depth (sequence of thoughts without branching).\n *\n * A new chain starts whenever a thought has `branch_from_thought` set.\n *\n * @param history - Thoughts in chronological order\n * @returns Length of the longest non-branching chain (0 for empty history)\n */\nexport function _computeChainDepth(history: ThoughtData[]): number {\n\tif (history.length === 0) return 0;\n\tlet maxDepth = 1;\n\tlet currentDepth = 1;\n\n\tfor (let i = 1; i < history.length; i++) {\n\t\tconst thought = history[i]!;\n\t\tif (!thought.branch_from_thought) {\n\t\t\tcurrentDepth++;\n\t\t\tmaxDepth = Math.max(maxDepth, currentDepth);\n\t\t} else {\n\t\t\tcurrentDepth = 1;\n\t\t}\n\t}\n\n\treturn maxDepth;\n}\n\n/** Alias of {@link _computeChainDepth} without the underscore prefix. */\nexport const computeChainDepth = _computeChainDepth;\n"],"names":["ALL_THOUGHT_TYPES","_countByType","history","counts","Object","t","thought","type","countByType","_computeChainDepth","maxDepth","currentDepth","i","Math","computeChainDepth"],"mappings":"AAcO,MAAMA,oBAAmC;IAC/C;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;CACA;AAUM,SAASC,aAAaC,OAAsB;IAClD,MAAMC,SAASC,OAAO,WAAW,CAACJ,kBAAkB,GAAG,CAAC,CAACK,IAAM;YAACA;YAAG;SAAE;IAKrE,KAAK,MAAMC,WAAWJ,QAAS;QAC9B,MAAMK,OAAOD,QAAQ,YAAY,IAAI;QACrC,IAAIC,QAAQJ,QACXA,MAAM,CAACI,KAAK;IAEd;IAEA,OAAOJ;AACR;AAGO,MAAMK,cAAcP;AAUpB,SAASQ,mBAAmBP,OAAsB;IACxD,IAAIA,AAAmB,MAAnBA,QAAQ,MAAM,EAAQ,OAAO;IACjC,IAAIQ,WAAW;IACf,IAAIC,eAAe;IAEnB,IAAK,IAAIC,IAAI,GAAGA,IAAIV,QAAQ,MAAM,EAAEU,IAAK;QACxC,MAAMN,UAAUJ,OAAO,CAACU,EAAE;QAC1B,IAAKN,QAAQ,mBAAmB,EAI/BK,eAAe;aAJkB;YACjCA;YACAD,WAAWG,KAAK,GAAG,CAACH,UAAUC;QAC/B;IAGD;IAEA,OAAOD;AACR;AAGO,MAAMI,oBAAoBL"}
@@ -2,7 +2,7 @@
2
2
  * GraphView — read-only graph traversal queries over an {@link IEdgeStore}.
3
3
  *
4
4
  * Provides BFS / topological / ancestor / descendant queries over the
5
- * thought DAG. All methods return arrays of thought ids (ulid strings),
5
+ * thought DAG. Queries return thought ids or graph distances,
6
6
  * never `ThoughtData` objects — the view is intentionally decoupled from
7
7
  * `HistoryManager` so it can be reused by strategies that work purely in
8
8
  * terms of graph structure.
@@ -54,6 +54,18 @@ export declare class GraphView {
54
54
  * ```
55
55
  */
56
56
  chronological(sessionId: SessionId): readonly ThoughtId[];
57
+ /**
58
+ * Return the shortest directed distance from any retained root to a thought.
59
+ *
60
+ * Nodes are derived from the current edge store, so isolated thoughts are
61
+ * invisible and pruning can expose a retained node as a new root. Every edge
62
+ * kind participates in the traversal.
63
+ *
64
+ * @param sessionId - Session to query
65
+ * @param thoughtId - Thought whose root distance to resolve
66
+ * @returns Zero for a root, or `undefined` when no root can reach the thought
67
+ */
68
+ depthFromRoots(sessionId: SessionId, thoughtId: ThoughtId): number | undefined;
57
69
  /**
58
70
  * Return all thought ids reachable from `rootThoughtId` via `'branch'`
59
71
  * edges, including the root itself.
@@ -1 +1 @@
1
- {"version":3,"file":"GraphView.d.ts","sourceRoot":"","sources":["../../../src/core/graph/GraphView.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAInE;;;;;;;;;;GAUG;AACH,qBAAa,SAAS;IACrB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAa;IAEpC;;;;;;;;;OASG;gBACS,KAAK,EAAE,UAAU;IAI7B;;;;;;;;;;;;;;OAcG;IACH,aAAa,CAAC,SAAS,EAAE,SAAS,GAAG,SAAS,SAAS,EAAE;IAUzD;;;;;;;;;;;;;;;OAeG;IACH,cAAc,CAAC,SAAS,EAAE,SAAS,EAAE,aAAa,EAAE,SAAS,GAAG,SAAS,SAAS,EAAE;IAkBpF;;;;;;;;;;;;OAYG;IACH,WAAW,CAAC,SAAS,EAAE,SAAS,GAAG,SAAS,SAAS,EAAE;IA4BvD;;;;;;;;;;;;;;OAcG;IACH,SAAS,CAAC,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,SAAS,EAAE;IAI9F;;;;;;;;;;;;;;OAcG;IACH,WAAW,CAAC,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,SAAS,EAAE;IAIhG;;;;;;;;;;OAUG;IACH,MAAM,CAAC,SAAS,EAAE,SAAS,GAAG,SAAS,SAAS,EAAE;IAelD;;;OAGG;IACH,OAAO,CAAC,aAAa;IAcrB;;;;OAIG;IACH,OAAO,CAAC,UAAU;IAmBlB;;OAEG;IACH,OAAO,CAAC,aAAa;IAsBrB;;OAEG;IACH,OAAO,CAAC,cAAc;IAStB;;;OAGG;IACH,OAAO,CAAC,WAAW;CA+BnB"}
1
+ {"version":3,"file":"GraphView.d.ts","sourceRoot":"","sources":["../../../src/core/graph/GraphView.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAInE;;;;;;;;;;GAUG;AACH,qBAAa,SAAS;IACrB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAa;IAEpC;;;;;;;;;OASG;gBACS,KAAK,EAAE,UAAU;IAI7B;;;;;;;;;;;;;;OAcG;IACH,aAAa,CAAC,SAAS,EAAE,SAAS,GAAG,SAAS,SAAS,EAAE;IAUzD;;;;;;;;;;OAUG;IACH,cAAc,CAAC,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,GAAG,MAAM,GAAG,SAAS;IAuC9E;;;;;;;;;;;;;;;OAeG;IACH,cAAc,CAAC,SAAS,EAAE,SAAS,EAAE,aAAa,EAAE,SAAS,GAAG,SAAS,SAAS,EAAE;IAkBpF;;;;;;;;;;;;OAYG;IACH,WAAW,CAAC,SAAS,EAAE,SAAS,GAAG,SAAS,SAAS,EAAE;IA4BvD;;;;;;;;;;;;;;OAcG;IACH,SAAS,CAAC,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,SAAS,EAAE;IAI9F;;;;;;;;;;;;;;OAcG;IACH,WAAW,CAAC,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,SAAS,EAAE;IAIhG;;;;;;;;;;OAUG;IACH,MAAM,CAAC,SAAS,EAAE,SAAS,GAAG,SAAS,SAAS,EAAE;IAelD;;;OAGG;IACH,OAAO,CAAC,aAAa;IAcrB;;;;OAIG;IACH,OAAO,CAAC,UAAU;IAmBlB;;OAEG;IACH,OAAO,CAAC,aAAa;IAsBrB;;OAEG;IACH,OAAO,CAAC,cAAc;IAStB;;;OAGG;IACH,OAAO,CAAC,WAAW;CA+BnB"}
@@ -11,6 +11,40 @@ class GraphView {
11
11
  const roots = this._findRoots(nodes, hasIncoming, edges);
12
12
  return this._bfsFromRoots(sessionId, roots);
13
13
  }
14
+ depthFromRoots(sessionId, thoughtId) {
15
+ const edges = this._store.edgesForSession(sessionId);
16
+ if (0 === edges.length) return;
17
+ const nodes = new Set();
18
+ const hasIncoming = new Set();
19
+ const outgoing = new Map();
20
+ for (const edge of edges){
21
+ nodes.add(edge.from);
22
+ nodes.add(edge.to);
23
+ hasIncoming.add(edge.to);
24
+ const children = outgoing.get(edge.from);
25
+ if (void 0 === children) outgoing.set(edge.from, [
26
+ edge.to
27
+ ]);
28
+ else children.push(edge.to);
29
+ }
30
+ if (!nodes.has(thoughtId)) return;
31
+ let frontier = [];
32
+ for (const node of nodes)if (!hasIncoming.has(node)) frontier.push(node);
33
+ const visited = new Set(frontier);
34
+ let depth = 0;
35
+ while(frontier.length > 0){
36
+ const next = [];
37
+ for (const node of frontier){
38
+ if (node === thoughtId) return depth;
39
+ for (const child of outgoing.get(node) ?? [])if (!visited.has(child)) {
40
+ visited.add(child);
41
+ next.push(child);
42
+ }
43
+ }
44
+ frontier = next;
45
+ depth++;
46
+ }
47
+ }
14
48
  branchThoughts(sessionId, rootThoughtId) {
15
49
  const visited = new Set([
16
50
  rootThoughtId
@@ -1 +1 @@
1
- {"version":3,"file":"core/graph/GraphView.js","sources":["../../../src/core/graph/GraphView.ts"],"sourcesContent":["/**\n * GraphView — read-only graph traversal queries over an {@link IEdgeStore}.\n *\n * Provides BFS / topological / ancestor / descendant queries over the\n * thought DAG. All methods return arrays of thought ids (ulid strings),\n * never `ThoughtData` objects — the view is intentionally decoupled from\n * `HistoryManager` so it can be reused by strategies that work purely in\n * terms of graph structure.\n *\n * Construction is cheap (stores a reference to the underlying store).\n * Each query reads the latest store state — no caching, no snapshots.\n *\n * @module core/graph/GraphView\n */\n\nimport type { IEdgeStore } from '../../contracts/interfaces.js';\nimport type { SessionId, ThoughtId } from '../../contracts/ids.js';\nimport { CycleDetectedError } from '../../errors.js';\nimport type { Edge } from './Edge.js';\n\n/**\n * Read-only traversal queries over an {@link IEdgeStore}.\n *\n * @example\n * ```typescript\n * const store = new EdgeStore();\n * // ... populate store ...\n * const view = new GraphView(store);\n * const order = view.topological('s1');\n * ```\n */\nexport class GraphView {\n\tprivate readonly _store: IEdgeStore;\n\n\t/**\n\t * Create a new GraphView backed by the given edge store.\n\t *\n\t * @param store - The edge store to query (held by reference, read-only access)\n\t *\n\t * @example\n\t * ```typescript\n\t * const view = new GraphView(edgeStore);\n\t * ```\n\t */\n\tconstructor(store: IEdgeStore) {\n\t\tthis._store = store;\n\t}\n\n\t/**\n\t * Return all thought ids in the session, ordered by BFS from roots.\n\t *\n\t * Roots are nodes with no incoming edges. Within a layer, neighbours\n\t * are visited in `outgoing` order (which the underlying store keeps\n\t * sorted by `createdAt` ascending).\n\t *\n\t * @param sessionId - Session to query\n\t * @returns Thought ids in chronological BFS order\n\t *\n\t * @example\n\t * ```typescript\n\t * const ids = view.chronological('s1');\n\t * ```\n\t */\n\tchronological(sessionId: SessionId): readonly ThoughtId[] {\n\t\tconst edges = this._store.edgesForSession(sessionId);\n\t\tif (edges.length === 0) {\n\t\t\treturn [];\n\t\t}\n\t\tconst { nodes, hasIncoming } = this._collectNodes(edges);\n\t\tconst roots = this._findRoots(nodes, hasIncoming, edges);\n\t\treturn this._bfsFromRoots(sessionId, roots) as readonly ThoughtId[];\n\t}\n\n\t/**\n\t * Return all thought ids reachable from `rootThoughtId` via `'branch'`\n\t * edges, including the root itself.\n\t *\n\t * Non-branch edges are ignored. Cycles (if any) do not cause infinite\n\t * loops because each node is visited at most once.\n\t *\n\t * @param sessionId - Session to query\n\t * @param rootThoughtId - The starting thought id (typically the branch root)\n\t * @returns Thought ids forming the branch (includes root)\n\t *\n\t * @example\n\t * ```typescript\n\t * const branchIds = view.branchThoughts('s1', 'thought-root');\n\t * ```\n\t */\n\tbranchThoughts(sessionId: SessionId, rootThoughtId: ThoughtId): readonly ThoughtId[] {\n\t\tconst visited = new Set<string>([rootThoughtId]);\n\t\tconst order: string[] = [rootThoughtId];\n\t\tconst queue: string[] = [rootThoughtId];\n\t\twhile (queue.length > 0) {\n\t\t\tconst current = queue.shift()!;\n\t\t\tconst out = this._store.outgoing(sessionId, current as ThoughtId);\n\t\t\tfor (const edge of out) {\n\t\t\t\tif (edge.kind !== 'branch') continue;\n\t\t\t\tif (visited.has(edge.to)) continue;\n\t\t\t\tvisited.add(edge.to);\n\t\t\t\torder.push(edge.to);\n\t\t\t\tqueue.push(edge.to);\n\t\t\t}\n\t\t}\n\t\treturn order as ThoughtId[];\n\t}\n\n\t/**\n\t * Return a topological ordering of all thought ids in the session\n\t * using Kahn's algorithm.\n\t *\n\t * @param sessionId - Session to query\n\t * @returns Thought ids in topological order\n\t * @throws {CycleDetectedError} If the graph contains a cycle\n\t *\n\t * @example\n\t * ```typescript\n\t * const order = view.topological('s1');\n\t * ```\n\t */\n\ttopological(sessionId: SessionId): readonly ThoughtId[] {\n\t\tconst edges = this._store.edgesForSession(sessionId);\n\t\tif (edges.length === 0) {\n\t\t\treturn [];\n\t\t}\n\t\tconst inDegree = this._buildInDegree(edges);\n\t\tconst queue: string[] = [];\n\t\tfor (const [node, deg] of inDegree) {\n\t\t\tif (deg === 0) queue.push(node);\n\t\t}\n\t\tconst order: string[] = [];\n\t\twhile (queue.length > 0) {\n\t\t\tconst node = queue.shift()!;\n\t\t\torder.push(node);\n\t\t\tfor (const edge of this._store.outgoing(sessionId, node as ThoughtId)) {\n\t\t\t\tconst next = (inDegree.get(edge.to) ?? 0) - 1;\n\t\t\t\tinDegree.set(edge.to, next);\n\t\t\t\tif (next === 0) queue.push(edge.to);\n\t\t\t}\n\t\t}\n\t\tif (order.length !== inDegree.size) {\n\t\t\tthrow new CycleDetectedError(\n\t\t\t\t`Cycle detected in session '${sessionId}': topological sort incomplete (${order.length}/${inDegree.size})`\n\t\t\t);\n\t\t}\n\t\treturn order as ThoughtId[];\n\t}\n\n\t/**\n\t * Return all ancestors of `thoughtId` via incoming-edge BFS.\n\t *\n\t * The starting node is NOT included. Cycles do not cause infinite loops.\n\t *\n\t * @param sessionId - Session to query\n\t * @param thoughtId - The thought to traverse from\n\t * @param maxDepth - Optional maximum traversal depth (1 = direct parents only)\n\t * @returns Ancestor thought ids in BFS order\n\t *\n\t * @example\n\t * ```typescript\n\t * const parents = view.ancestors('s1', 'thought-id', 1);\n\t * ```\n\t */\n\tancestors(sessionId: SessionId, thoughtId: ThoughtId, maxDepth?: number): readonly ThoughtId[] {\n\t\treturn this._bfsClosure(sessionId, thoughtId, maxDepth, 'incoming') as readonly ThoughtId[];\n\t}\n\n\t/**\n\t * Return all descendants of `thoughtId` via outgoing-edge BFS.\n\t *\n\t * The starting node is NOT included. Cycles do not cause infinite loops.\n\t *\n\t * @param sessionId - Session to query\n\t * @param thoughtId - The thought to traverse from\n\t * @param maxDepth - Optional maximum traversal depth (1 = direct children only)\n\t * @returns Descendant thought ids in BFS order\n\t *\n\t * @example\n\t * ```typescript\n\t * const children = view.descendants('s1', 'thought-id');\n\t * ```\n\t */\n\tdescendants(sessionId: SessionId, thoughtId: ThoughtId, maxDepth?: number): readonly ThoughtId[] {\n\t\treturn this._bfsClosure(sessionId, thoughtId, maxDepth, 'outgoing') as readonly ThoughtId[];\n\t}\n\n\t/**\n\t * Return all thought ids in the session that have no outgoing edges.\n\t *\n\t * @param sessionId - Session to query\n\t * @returns Thought ids that are graph leaves\n\t *\n\t * @example\n\t * ```typescript\n\t * const tips = view.leaves('s1');\n\t * ```\n\t */\n\tleaves(sessionId: SessionId): readonly ThoughtId[] {\n\t\tconst edges = this._store.edgesForSession(sessionId);\n\t\tif (edges.length === 0) {\n\t\t\treturn [];\n\t\t}\n\t\tconst { nodes } = this._collectNodes(edges);\n\t\tconst result: string[] = [];\n\t\tfor (const node of nodes) {\n\t\t\tif (this._store.outgoing(sessionId, node as ThoughtId).length === 0) {\n\t\t\t\tresult.push(node);\n\t\t\t}\n\t\t}\n\t\treturn result as ThoughtId[];\n\t}\n\n\t/**\n\t * Collect every node id referenced by the edges and a set of nodes\n\t * that have at least one incoming edge.\n\t */\n\tprivate _collectNodes(edges: readonly Edge[]): {\n\t\tnodes: Set<string>;\n\t\thasIncoming: Set<string>;\n\t} {\n\t\tconst nodes = new Set<string>();\n\t\tconst hasIncoming = new Set<string>();\n\t\tfor (const edge of edges) {\n\t\t\tnodes.add(edge.from);\n\t\t\tnodes.add(edge.to);\n\t\t\thasIncoming.add(edge.to);\n\t\t}\n\t\treturn { nodes, hasIncoming };\n\t}\n\n\t/**\n\t * Find roots (no incoming) ordered by the earliest outgoing edge\n\t * `createdAt`. Nodes with no outgoing edges fall back to the order\n\t * they appear among edges (already sorted by `createdAt`).\n\t */\n\tprivate _findRoots(\n\t\tnodes: Set<string>,\n\t\thasIncoming: Set<string>,\n\t\tedges: readonly Edge[]\n\t): string[] {\n\t\tconst earliest = new Map<string, number>();\n\t\tfor (const edge of edges) {\n\t\t\tif (!earliest.has(edge.from)) {\n\t\t\t\tearliest.set(edge.from, edge.createdAt);\n\t\t\t}\n\t\t}\n\t\tconst roots: string[] = [];\n\t\tfor (const node of nodes) {\n\t\t\tif (!hasIncoming.has(node)) roots.push(node);\n\t\t}\n\t\troots.sort((a, b) => (earliest.get(a) ?? Infinity) - (earliest.get(b) ?? Infinity));\n\t\treturn roots;\n\t}\n\n\t/**\n\t * BFS from a list of root ids, visiting each node at most once.\n\t */\n\tprivate _bfsFromRoots(sessionId: SessionId, roots: readonly string[]): readonly string[] {\n\t\tconst visited = new Set<string>();\n\t\tconst order: string[] = [];\n\t\tconst queue: string[] = [];\n\t\tfor (const root of roots) {\n\t\t\tif (visited.has(root)) continue;\n\t\t\tvisited.add(root);\n\t\t\torder.push(root);\n\t\t\tqueue.push(root);\n\t\t}\n\t\twhile (queue.length > 0) {\n\t\t\tconst node = queue.shift()!;\n\t\t\tfor (const edge of this._store.outgoing(sessionId, node as ThoughtId)) {\n\t\t\t\tif (visited.has(edge.to)) continue;\n\t\t\t\tvisited.add(edge.to);\n\t\t\t\torder.push(edge.to);\n\t\t\t\tqueue.push(edge.to);\n\t\t\t}\n\t\t}\n\t\treturn order;\n\t}\n\n\t/**\n\t * Build an in-degree map covering every node referenced by the edges.\n\t */\n\tprivate _buildInDegree(edges: readonly Edge[]): Map<string, number> {\n\t\tconst inDegree = new Map<string, number>();\n\t\tfor (const edge of edges) {\n\t\t\tif (!inDegree.has(edge.from)) inDegree.set(edge.from, 0);\n\t\t\tinDegree.set(edge.to, (inDegree.get(edge.to) ?? 0) + 1);\n\t\t}\n\t\treturn inDegree;\n\t}\n\n\t/**\n\t * Generic BFS closure in either direction. Excludes the start node\n\t * from the result and respects an optional depth cap.\n\t */\n\tprivate _bfsClosure(\n\t\tsessionId: SessionId,\n\t\tstartId: string,\n\t\tmaxDepth: number | undefined,\n\t\tdirection: 'incoming' | 'outgoing'\n\t): readonly string[] {\n\t\tconst cap = maxDepth ?? Number.POSITIVE_INFINITY;\n\t\tconst visited = new Set<string>([startId]);\n\t\tconst order: string[] = [];\n\t\tlet frontier: string[] = [startId];\n\t\tlet depth = 0;\n\t\twhile (frontier.length > 0 && depth < cap) {\n\t\t\tconst next: string[] = [];\n\t\t\tfor (const node of frontier) {\n\t\t\t\tconst edges =\n\t\t\t\t\tdirection === 'outgoing'\n\t\t\t\t\t\t? this._store.outgoing(sessionId, node as ThoughtId)\n\t\t\t\t\t\t: this._store.incoming(sessionId, node as ThoughtId);\n\t\t\t\tfor (const edge of edges) {\n\t\t\t\t\tconst neighbour = direction === 'outgoing' ? edge.to : edge.from;\n\t\t\t\t\tif (visited.has(neighbour)) continue;\n\t\t\t\t\tvisited.add(neighbour);\n\t\t\t\t\torder.push(neighbour);\n\t\t\t\t\tnext.push(neighbour);\n\t\t\t\t}\n\t\t\t}\n\t\t\tfrontier = next;\n\t\t\tdepth++;\n\t\t}\n\t\treturn order;\n\t}\n}\n"],"names":["GraphView","store","sessionId","edges","nodes","hasIncoming","roots","rootThoughtId","visited","Set","order","queue","current","out","edge","inDegree","node","deg","next","CycleDetectedError","thoughtId","maxDepth","result","earliest","Map","a","b","Infinity","root","startId","direction","cap","Number","frontier","depth","neighbour"],"mappings":";AA+BO,MAAMA;IACK,OAAmB;IAYpC,YAAYC,KAAiB,CAAE;QAC9B,IAAI,CAAC,MAAM,GAAGA;IACf;IAiBA,cAAcC,SAAoB,EAAwB;QACzD,MAAMC,QAAQ,IAAI,CAAC,MAAM,CAAC,eAAe,CAACD;QAC1C,IAAIC,AAAiB,MAAjBA,MAAM,MAAM,EACf,OAAO,EAAE;QAEV,MAAM,EAAEC,KAAK,EAAEC,WAAW,EAAE,GAAG,IAAI,CAAC,aAAa,CAACF;QAClD,MAAMG,QAAQ,IAAI,CAAC,UAAU,CAACF,OAAOC,aAAaF;QAClD,OAAO,IAAI,CAAC,aAAa,CAACD,WAAWI;IACtC;IAkBA,eAAeJ,SAAoB,EAAEK,aAAwB,EAAwB;QACpF,MAAMC,UAAU,IAAIC,IAAY;YAACF;SAAc;QAC/C,MAAMG,QAAkB;YAACH;SAAc;QACvC,MAAMI,QAAkB;YAACJ;SAAc;QACvC,MAAOI,MAAM,MAAM,GAAG,EAAG;YACxB,MAAMC,UAAUD,MAAM,KAAK;YAC3B,MAAME,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAACX,WAAWU;YAC5C,KAAK,MAAME,QAAQD,IAClB,IAAIC,AAAc,aAAdA,KAAK,IAAI,EACb;gBAAA,KAAIN,QAAQ,GAAG,CAACM,KAAK,EAAE;oBACvBN,QAAQ,GAAG,CAACM,KAAK,EAAE;oBACnBJ,MAAM,IAAI,CAACI,KAAK,EAAE;oBAClBH,MAAM,IAAI,CAACG,KAAK,EAAE;;YAHgB;QAKpC;QACA,OAAOJ;IACR;IAeA,YAAYR,SAAoB,EAAwB;QACvD,MAAMC,QAAQ,IAAI,CAAC,MAAM,CAAC,eAAe,CAACD;QAC1C,IAAIC,AAAiB,MAAjBA,MAAM,MAAM,EACf,OAAO,EAAE;QAEV,MAAMY,WAAW,IAAI,CAAC,cAAc,CAACZ;QACrC,MAAMQ,QAAkB,EAAE;QAC1B,KAAK,MAAM,CAACK,MAAMC,IAAI,IAAIF,SACzB,IAAIE,AAAQ,MAARA,KAAWN,MAAM,IAAI,CAACK;QAE3B,MAAMN,QAAkB,EAAE;QAC1B,MAAOC,MAAM,MAAM,GAAG,EAAG;YACxB,MAAMK,OAAOL,MAAM,KAAK;YACxBD,MAAM,IAAI,CAACM;YACX,KAAK,MAAMF,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAACZ,WAAWc,MAAoB;gBACtE,MAAME,OAAQH,AAAAA,CAAAA,SAAS,GAAG,CAACD,KAAK,EAAE,KAAK,KAAK;gBAC5CC,SAAS,GAAG,CAACD,KAAK,EAAE,EAAEI;gBACtB,IAAIA,AAAS,MAATA,MAAYP,MAAM,IAAI,CAACG,KAAK,EAAE;YACnC;QACD;QACA,IAAIJ,MAAM,MAAM,KAAKK,SAAS,IAAI,EACjC,MAAM,IAAII,mBACT,CAAC,2BAA2B,EAAEjB,UAAU,gCAAgC,EAAEQ,MAAM,MAAM,CAAC,CAAC,EAAEK,SAAS,IAAI,CAAC,CAAC,CAAC;QAG5G,OAAOL;IACR;IAiBA,UAAUR,SAAoB,EAAEkB,SAAoB,EAAEC,QAAiB,EAAwB;QAC9F,OAAO,IAAI,CAAC,WAAW,CAACnB,WAAWkB,WAAWC,UAAU;IACzD;IAiBA,YAAYnB,SAAoB,EAAEkB,SAAoB,EAAEC,QAAiB,EAAwB;QAChG,OAAO,IAAI,CAAC,WAAW,CAACnB,WAAWkB,WAAWC,UAAU;IACzD;IAaA,OAAOnB,SAAoB,EAAwB;QAClD,MAAMC,QAAQ,IAAI,CAAC,MAAM,CAAC,eAAe,CAACD;QAC1C,IAAIC,AAAiB,MAAjBA,MAAM,MAAM,EACf,OAAO,EAAE;QAEV,MAAM,EAAEC,KAAK,EAAE,GAAG,IAAI,CAAC,aAAa,CAACD;QACrC,MAAMmB,SAAmB,EAAE;QAC3B,KAAK,MAAMN,QAAQZ,MAClB,IAAI,AAA8D,MAA9D,IAAI,CAAC,MAAM,CAAC,QAAQ,CAACF,WAAWc,MAAmB,MAAM,EAC5DM,OAAO,IAAI,CAACN;QAGd,OAAOM;IACR;IAMQ,cAAcnB,KAAsB,EAG1C;QACD,MAAMC,QAAQ,IAAIK;QAClB,MAAMJ,cAAc,IAAII;QACxB,KAAK,MAAMK,QAAQX,MAAO;YACzBC,MAAM,GAAG,CAACU,KAAK,IAAI;YACnBV,MAAM,GAAG,CAACU,KAAK,EAAE;YACjBT,YAAY,GAAG,CAACS,KAAK,EAAE;QACxB;QACA,OAAO;YAAEV;YAAOC;QAAY;IAC7B;IAOQ,WACPD,KAAkB,EAClBC,WAAwB,EACxBF,KAAsB,EACX;QACX,MAAMoB,WAAW,IAAIC;QACrB,KAAK,MAAMV,QAAQX,MAClB,IAAI,CAACoB,SAAS,GAAG,CAACT,KAAK,IAAI,GAC1BS,SAAS,GAAG,CAACT,KAAK,IAAI,EAAEA,KAAK,SAAS;QAGxC,MAAMR,QAAkB,EAAE;QAC1B,KAAK,MAAMU,QAAQZ,MAClB,IAAI,CAACC,YAAY,GAAG,CAACW,OAAOV,MAAM,IAAI,CAACU;QAExCV,MAAM,IAAI,CAAC,CAACmB,GAAGC,IAAOH,AAAAA,CAAAA,SAAS,GAAG,CAACE,MAAME,KAAO,IAAMJ,CAAAA,SAAS,GAAG,CAACG,MAAMC,KAAO;QAChF,OAAOrB;IACR;IAKQ,cAAcJ,SAAoB,EAAEI,KAAwB,EAAqB;QACxF,MAAME,UAAU,IAAIC;QACpB,MAAMC,QAAkB,EAAE;QAC1B,MAAMC,QAAkB,EAAE;QAC1B,KAAK,MAAMiB,QAAQtB,MAClB,KAAIE,QAAQ,GAAG,CAACoB;YAChBpB,QAAQ,GAAG,CAACoB;YACZlB,MAAM,IAAI,CAACkB;YACXjB,MAAM,IAAI,CAACiB;;QAEZ,MAAOjB,MAAM,MAAM,GAAG,EAAG;YACxB,MAAMK,OAAOL,MAAM,KAAK;YACxB,KAAK,MAAMG,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAACZ,WAAWc,MAClD,KAAIR,QAAQ,GAAG,CAACM,KAAK,EAAE;gBACvBN,QAAQ,GAAG,CAACM,KAAK,EAAE;gBACnBJ,MAAM,IAAI,CAACI,KAAK,EAAE;gBAClBH,MAAM,IAAI,CAACG,KAAK,EAAE;;QAEpB;QACA,OAAOJ;IACR;IAKQ,eAAeP,KAAsB,EAAuB;QACnE,MAAMY,WAAW,IAAIS;QACrB,KAAK,MAAMV,QAAQX,MAAO;YACzB,IAAI,CAACY,SAAS,GAAG,CAACD,KAAK,IAAI,GAAGC,SAAS,GAAG,CAACD,KAAK,IAAI,EAAE;YACtDC,SAAS,GAAG,CAACD,KAAK,EAAE,EAAGC,AAAAA,CAAAA,SAAS,GAAG,CAACD,KAAK,EAAE,KAAK,KAAK;QACtD;QACA,OAAOC;IACR;IAMQ,YACPb,SAAoB,EACpB2B,OAAe,EACfR,QAA4B,EAC5BS,SAAkC,EACd;QACpB,MAAMC,MAAMV,YAAYW,KAAwB;QAChD,MAAMxB,UAAU,IAAIC,IAAY;YAACoB;SAAQ;QACzC,MAAMnB,QAAkB,EAAE;QAC1B,IAAIuB,WAAqB;YAACJ;SAAQ;QAClC,IAAIK,QAAQ;QACZ,MAAOD,SAAS,MAAM,GAAG,KAAKC,QAAQH,IAAK;YAC1C,MAAMb,OAAiB,EAAE;YACzB,KAAK,MAAMF,QAAQiB,SAAU;gBAC5B,MAAM9B,QACL2B,AAAc,eAAdA,YACG,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC5B,WAAWc,QAChC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAACd,WAAWc;gBACpC,KAAK,MAAMF,QAAQX,MAAO;oBACzB,MAAMgC,YAAYL,AAAc,eAAdA,YAA2BhB,KAAK,EAAE,GAAGA,KAAK,IAAI;oBAChE,KAAIN,QAAQ,GAAG,CAAC2B;wBAChB3B,QAAQ,GAAG,CAAC2B;wBACZzB,MAAM,IAAI,CAACyB;wBACXjB,KAAK,IAAI,CAACiB;;gBACX;YACD;YACAF,WAAWf;YACXgB;QACD;QACA,OAAOxB;IACR;AACD"}
1
+ {"version":3,"file":"core/graph/GraphView.js","sources":["../../../src/core/graph/GraphView.ts"],"sourcesContent":["/**\n * GraphView — read-only graph traversal queries over an {@link IEdgeStore}.\n *\n * Provides BFS / topological / ancestor / descendant queries over the\n * thought DAG. Queries return thought ids or graph distances,\n * never `ThoughtData` objects — the view is intentionally decoupled from\n * `HistoryManager` so it can be reused by strategies that work purely in\n * terms of graph structure.\n *\n * Construction is cheap (stores a reference to the underlying store).\n * Each query reads the latest store state — no caching, no snapshots.\n *\n * @module core/graph/GraphView\n */\n\nimport type { IEdgeStore } from '../../contracts/interfaces.js';\nimport type { SessionId, ThoughtId } from '../../contracts/ids.js';\nimport { CycleDetectedError } from '../../errors.js';\nimport type { Edge } from './Edge.js';\n\n/**\n * Read-only traversal queries over an {@link IEdgeStore}.\n *\n * @example\n * ```typescript\n * const store = new EdgeStore();\n * // ... populate store ...\n * const view = new GraphView(store);\n * const order = view.topological('s1');\n * ```\n */\nexport class GraphView {\n\tprivate readonly _store: IEdgeStore;\n\n\t/**\n\t * Create a new GraphView backed by the given edge store.\n\t *\n\t * @param store - The edge store to query (held by reference, read-only access)\n\t *\n\t * @example\n\t * ```typescript\n\t * const view = new GraphView(edgeStore);\n\t * ```\n\t */\n\tconstructor(store: IEdgeStore) {\n\t\tthis._store = store;\n\t}\n\n\t/**\n\t * Return all thought ids in the session, ordered by BFS from roots.\n\t *\n\t * Roots are nodes with no incoming edges. Within a layer, neighbours\n\t * are visited in `outgoing` order (which the underlying store keeps\n\t * sorted by `createdAt` ascending).\n\t *\n\t * @param sessionId - Session to query\n\t * @returns Thought ids in chronological BFS order\n\t *\n\t * @example\n\t * ```typescript\n\t * const ids = view.chronological('s1');\n\t * ```\n\t */\n\tchronological(sessionId: SessionId): readonly ThoughtId[] {\n\t\tconst edges = this._store.edgesForSession(sessionId);\n\t\tif (edges.length === 0) {\n\t\t\treturn [];\n\t\t}\n\t\tconst { nodes, hasIncoming } = this._collectNodes(edges);\n\t\tconst roots = this._findRoots(nodes, hasIncoming, edges);\n\t\treturn this._bfsFromRoots(sessionId, roots) as readonly ThoughtId[];\n\t}\n\n\t/**\n\t * Return the shortest directed distance from any retained root to a thought.\n\t *\n\t * Nodes are derived from the current edge store, so isolated thoughts are\n\t * invisible and pruning can expose a retained node as a new root. Every edge\n\t * kind participates in the traversal.\n\t *\n\t * @param sessionId - Session to query\n\t * @param thoughtId - Thought whose root distance to resolve\n\t * @returns Zero for a root, or `undefined` when no root can reach the thought\n\t */\n\tdepthFromRoots(sessionId: SessionId, thoughtId: ThoughtId): number | undefined {\n\t\tconst edges = this._store.edgesForSession(sessionId);\n\t\tif (edges.length === 0) return undefined;\n\n\t\tconst nodes = new Set<ThoughtId>();\n\t\tconst hasIncoming = new Set<ThoughtId>();\n\t\tconst outgoing = new Map<ThoughtId, ThoughtId[]>();\n\t\tfor (const edge of edges) {\n\t\t\tnodes.add(edge.from);\n\t\t\tnodes.add(edge.to);\n\t\t\thasIncoming.add(edge.to);\n\t\t\tconst children = outgoing.get(edge.from);\n\t\t\tif (children === undefined) outgoing.set(edge.from, [edge.to]);\n\t\t\telse children.push(edge.to);\n\t\t}\n\t\tif (!nodes.has(thoughtId)) return undefined;\n\n\t\tlet frontier: ThoughtId[] = [];\n\t\tfor (const node of nodes) {\n\t\t\tif (!hasIncoming.has(node)) frontier.push(node);\n\t\t}\n\t\tconst visited = new Set<ThoughtId>(frontier);\n\t\tlet depth = 0;\n\t\twhile (frontier.length > 0) {\n\t\t\tconst next: ThoughtId[] = [];\n\t\t\tfor (const node of frontier) {\n\t\t\t\tif (node === thoughtId) return depth;\n\t\t\t\tfor (const child of outgoing.get(node) ?? []) {\n\t\t\t\t\tif (visited.has(child)) continue;\n\t\t\t\t\tvisited.add(child);\n\t\t\t\t\tnext.push(child);\n\t\t\t\t}\n\t\t\t}\n\t\t\tfrontier = next;\n\t\t\tdepth++;\n\t\t}\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * Return all thought ids reachable from `rootThoughtId` via `'branch'`\n\t * edges, including the root itself.\n\t *\n\t * Non-branch edges are ignored. Cycles (if any) do not cause infinite\n\t * loops because each node is visited at most once.\n\t *\n\t * @param sessionId - Session to query\n\t * @param rootThoughtId - The starting thought id (typically the branch root)\n\t * @returns Thought ids forming the branch (includes root)\n\t *\n\t * @example\n\t * ```typescript\n\t * const branchIds = view.branchThoughts('s1', 'thought-root');\n\t * ```\n\t */\n\tbranchThoughts(sessionId: SessionId, rootThoughtId: ThoughtId): readonly ThoughtId[] {\n\t\tconst visited = new Set<string>([rootThoughtId]);\n\t\tconst order: string[] = [rootThoughtId];\n\t\tconst queue: string[] = [rootThoughtId];\n\t\twhile (queue.length > 0) {\n\t\t\tconst current = queue.shift()!;\n\t\t\tconst out = this._store.outgoing(sessionId, current as ThoughtId);\n\t\t\tfor (const edge of out) {\n\t\t\t\tif (edge.kind !== 'branch') continue;\n\t\t\t\tif (visited.has(edge.to)) continue;\n\t\t\t\tvisited.add(edge.to);\n\t\t\t\torder.push(edge.to);\n\t\t\t\tqueue.push(edge.to);\n\t\t\t}\n\t\t}\n\t\treturn order as ThoughtId[];\n\t}\n\n\t/**\n\t * Return a topological ordering of all thought ids in the session\n\t * using Kahn's algorithm.\n\t *\n\t * @param sessionId - Session to query\n\t * @returns Thought ids in topological order\n\t * @throws {CycleDetectedError} If the graph contains a cycle\n\t *\n\t * @example\n\t * ```typescript\n\t * const order = view.topological('s1');\n\t * ```\n\t */\n\ttopological(sessionId: SessionId): readonly ThoughtId[] {\n\t\tconst edges = this._store.edgesForSession(sessionId);\n\t\tif (edges.length === 0) {\n\t\t\treturn [];\n\t\t}\n\t\tconst inDegree = this._buildInDegree(edges);\n\t\tconst queue: string[] = [];\n\t\tfor (const [node, deg] of inDegree) {\n\t\t\tif (deg === 0) queue.push(node);\n\t\t}\n\t\tconst order: string[] = [];\n\t\twhile (queue.length > 0) {\n\t\t\tconst node = queue.shift()!;\n\t\t\torder.push(node);\n\t\t\tfor (const edge of this._store.outgoing(sessionId, node as ThoughtId)) {\n\t\t\t\tconst next = (inDegree.get(edge.to) ?? 0) - 1;\n\t\t\t\tinDegree.set(edge.to, next);\n\t\t\t\tif (next === 0) queue.push(edge.to);\n\t\t\t}\n\t\t}\n\t\tif (order.length !== inDegree.size) {\n\t\t\tthrow new CycleDetectedError(\n\t\t\t\t`Cycle detected in session '${sessionId}': topological sort incomplete (${order.length}/${inDegree.size})`\n\t\t\t);\n\t\t}\n\t\treturn order as ThoughtId[];\n\t}\n\n\t/**\n\t * Return all ancestors of `thoughtId` via incoming-edge BFS.\n\t *\n\t * The starting node is NOT included. Cycles do not cause infinite loops.\n\t *\n\t * @param sessionId - Session to query\n\t * @param thoughtId - The thought to traverse from\n\t * @param maxDepth - Optional maximum traversal depth (1 = direct parents only)\n\t * @returns Ancestor thought ids in BFS order\n\t *\n\t * @example\n\t * ```typescript\n\t * const parents = view.ancestors('s1', 'thought-id', 1);\n\t * ```\n\t */\n\tancestors(sessionId: SessionId, thoughtId: ThoughtId, maxDepth?: number): readonly ThoughtId[] {\n\t\treturn this._bfsClosure(sessionId, thoughtId, maxDepth, 'incoming') as readonly ThoughtId[];\n\t}\n\n\t/**\n\t * Return all descendants of `thoughtId` via outgoing-edge BFS.\n\t *\n\t * The starting node is NOT included. Cycles do not cause infinite loops.\n\t *\n\t * @param sessionId - Session to query\n\t * @param thoughtId - The thought to traverse from\n\t * @param maxDepth - Optional maximum traversal depth (1 = direct children only)\n\t * @returns Descendant thought ids in BFS order\n\t *\n\t * @example\n\t * ```typescript\n\t * const children = view.descendants('s1', 'thought-id');\n\t * ```\n\t */\n\tdescendants(sessionId: SessionId, thoughtId: ThoughtId, maxDepth?: number): readonly ThoughtId[] {\n\t\treturn this._bfsClosure(sessionId, thoughtId, maxDepth, 'outgoing') as readonly ThoughtId[];\n\t}\n\n\t/**\n\t * Return all thought ids in the session that have no outgoing edges.\n\t *\n\t * @param sessionId - Session to query\n\t * @returns Thought ids that are graph leaves\n\t *\n\t * @example\n\t * ```typescript\n\t * const tips = view.leaves('s1');\n\t * ```\n\t */\n\tleaves(sessionId: SessionId): readonly ThoughtId[] {\n\t\tconst edges = this._store.edgesForSession(sessionId);\n\t\tif (edges.length === 0) {\n\t\t\treturn [];\n\t\t}\n\t\tconst { nodes } = this._collectNodes(edges);\n\t\tconst result: string[] = [];\n\t\tfor (const node of nodes) {\n\t\t\tif (this._store.outgoing(sessionId, node as ThoughtId).length === 0) {\n\t\t\t\tresult.push(node);\n\t\t\t}\n\t\t}\n\t\treturn result as ThoughtId[];\n\t}\n\n\t/**\n\t * Collect every node id referenced by the edges and a set of nodes\n\t * that have at least one incoming edge.\n\t */\n\tprivate _collectNodes(edges: readonly Edge[]): {\n\t\tnodes: Set<string>;\n\t\thasIncoming: Set<string>;\n\t} {\n\t\tconst nodes = new Set<string>();\n\t\tconst hasIncoming = new Set<string>();\n\t\tfor (const edge of edges) {\n\t\t\tnodes.add(edge.from);\n\t\t\tnodes.add(edge.to);\n\t\t\thasIncoming.add(edge.to);\n\t\t}\n\t\treturn { nodes, hasIncoming };\n\t}\n\n\t/**\n\t * Find roots (no incoming) ordered by the earliest outgoing edge\n\t * `createdAt`. Nodes with no outgoing edges fall back to the order\n\t * they appear among edges (already sorted by `createdAt`).\n\t */\n\tprivate _findRoots(\n\t\tnodes: Set<string>,\n\t\thasIncoming: Set<string>,\n\t\tedges: readonly Edge[]\n\t): string[] {\n\t\tconst earliest = new Map<string, number>();\n\t\tfor (const edge of edges) {\n\t\t\tif (!earliest.has(edge.from)) {\n\t\t\t\tearliest.set(edge.from, edge.createdAt);\n\t\t\t}\n\t\t}\n\t\tconst roots: string[] = [];\n\t\tfor (const node of nodes) {\n\t\t\tif (!hasIncoming.has(node)) roots.push(node);\n\t\t}\n\t\troots.sort((a, b) => (earliest.get(a) ?? Infinity) - (earliest.get(b) ?? Infinity));\n\t\treturn roots;\n\t}\n\n\t/**\n\t * BFS from a list of root ids, visiting each node at most once.\n\t */\n\tprivate _bfsFromRoots(sessionId: SessionId, roots: readonly string[]): readonly string[] {\n\t\tconst visited = new Set<string>();\n\t\tconst order: string[] = [];\n\t\tconst queue: string[] = [];\n\t\tfor (const root of roots) {\n\t\t\tif (visited.has(root)) continue;\n\t\t\tvisited.add(root);\n\t\t\torder.push(root);\n\t\t\tqueue.push(root);\n\t\t}\n\t\twhile (queue.length > 0) {\n\t\t\tconst node = queue.shift()!;\n\t\t\tfor (const edge of this._store.outgoing(sessionId, node as ThoughtId)) {\n\t\t\t\tif (visited.has(edge.to)) continue;\n\t\t\t\tvisited.add(edge.to);\n\t\t\t\torder.push(edge.to);\n\t\t\t\tqueue.push(edge.to);\n\t\t\t}\n\t\t}\n\t\treturn order;\n\t}\n\n\t/**\n\t * Build an in-degree map covering every node referenced by the edges.\n\t */\n\tprivate _buildInDegree(edges: readonly Edge[]): Map<string, number> {\n\t\tconst inDegree = new Map<string, number>();\n\t\tfor (const edge of edges) {\n\t\t\tif (!inDegree.has(edge.from)) inDegree.set(edge.from, 0);\n\t\t\tinDegree.set(edge.to, (inDegree.get(edge.to) ?? 0) + 1);\n\t\t}\n\t\treturn inDegree;\n\t}\n\n\t/**\n\t * Generic BFS closure in either direction. Excludes the start node\n\t * from the result and respects an optional depth cap.\n\t */\n\tprivate _bfsClosure(\n\t\tsessionId: SessionId,\n\t\tstartId: string,\n\t\tmaxDepth: number | undefined,\n\t\tdirection: 'incoming' | 'outgoing'\n\t): readonly string[] {\n\t\tconst cap = maxDepth ?? Number.POSITIVE_INFINITY;\n\t\tconst visited = new Set<string>([startId]);\n\t\tconst order: string[] = [];\n\t\tlet frontier: string[] = [startId];\n\t\tlet depth = 0;\n\t\twhile (frontier.length > 0 && depth < cap) {\n\t\t\tconst next: string[] = [];\n\t\t\tfor (const node of frontier) {\n\t\t\t\tconst edges =\n\t\t\t\t\tdirection === 'outgoing'\n\t\t\t\t\t\t? this._store.outgoing(sessionId, node as ThoughtId)\n\t\t\t\t\t\t: this._store.incoming(sessionId, node as ThoughtId);\n\t\t\t\tfor (const edge of edges) {\n\t\t\t\t\tconst neighbour = direction === 'outgoing' ? edge.to : edge.from;\n\t\t\t\t\tif (visited.has(neighbour)) continue;\n\t\t\t\t\tvisited.add(neighbour);\n\t\t\t\t\torder.push(neighbour);\n\t\t\t\t\tnext.push(neighbour);\n\t\t\t\t}\n\t\t\t}\n\t\t\tfrontier = next;\n\t\t\tdepth++;\n\t\t}\n\t\treturn order;\n\t}\n}\n"],"names":["GraphView","store","sessionId","edges","nodes","hasIncoming","roots","thoughtId","Set","outgoing","Map","edge","children","undefined","frontier","node","visited","depth","next","child","rootThoughtId","order","queue","current","out","inDegree","deg","CycleDetectedError","maxDepth","result","earliest","a","b","Infinity","root","startId","direction","cap","Number","neighbour"],"mappings":"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"}
@@ -3,8 +3,8 @@
3
3
  *
4
4
  * Observes the frontier (graph leaves), scores each leaf via
5
5
  * {@link scoreThought}, and decides whether to continue, branch (when the
6
- * current thought falls outside the beam), or terminate (on confidence
7
- * threshold or score plateau).
6
+ * current thought falls outside the beam), or terminate (on depth cap,
7
+ * confidence threshold, or score plateau).
8
8
  *
9
9
  * Pure policy: configuration lives in a module-level {@link WeakMap}, not
10
10
  * on the instance, so `Object.getOwnPropertyNames(strategy)` only ever
@@ -46,13 +46,13 @@ export declare class TreeOfThoughtStrategy implements IReasoningStrategy {
46
46
  /**
47
47
  * Compute the next action for the chain.
48
48
  *
49
- * Order of checks: termination by confidence → termination by plateau →
50
- * branch when the current thought is outside the beam → continue.
49
+ * Order of checks: depth cap → confidence → plateau → branch when the
50
+ * current thought is outside the beam → continue. Missing graphs continue.
51
51
  */
52
52
  decide(ctx: StrategyContext): StrategyDecision;
53
- /** True when the frontier is wider than the beam (diverse exploration). */
53
+ /** True below the depth cap when the frontier is wider than the beam. */
54
54
  shouldBranch(ctx: StrategyContext): boolean;
55
- /** True when the best frontier score crosses the threshold OR scores plateau. */
55
+ /** True at the depth cap, confidence threshold, or score plateau. */
56
56
  shouldTerminate(ctx: StrategyContext): boolean;
57
57
  }
58
58
  //# sourceMappingURL=TreeOfThoughtStrategy.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"TreeOfThoughtStrategy.d.ts","sourceRoot":"","sources":["../../../../src/core/reasoning/strategies/TreeOfThoughtStrategy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EACX,kBAAkB,EAClB,eAAe,EACf,gBAAgB,EAChB,MAAM,gCAAgC,CAAC;AAKxC;;;GAGG;AACH,MAAM,WAAW,SAAS;IACzB,2DAA2D;IAC3D,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,0EAA0E;IAC1E,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,kEAAkE;IAClE,QAAQ,CAAC,qBAAqB,CAAC,EAAE,MAAM,CAAC;IACxC,uDAAuD;IACvD,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,8EAA8E;IAC9E,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;CACjC;AAgED;;;;;;;;GAQG;AACH,qBAAa,qBAAsB,YAAW,kBAAkB;IAC/D,QAAQ,CAAC,IAAI,EAAG,KAAK,CAAU;IAE/B,6CAA6C;gBACjC,MAAM,CAAC,EAAE,SAAS;IAI9B;;;;;OAKG;IACH,MAAM,CAAC,GAAG,EAAE,eAAe,GAAG,gBAAgB;IAiC9C,2EAA2E;IAC3E,YAAY,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO;IAO3C,iFAAiF;IACjF,eAAe,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO;CAY9C"}
1
+ {"version":3,"file":"TreeOfThoughtStrategy.d.ts","sourceRoot":"","sources":["../../../../src/core/reasoning/strategies/TreeOfThoughtStrategy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EACX,kBAAkB,EAClB,eAAe,EACf,gBAAgB,EAChB,MAAM,gCAAgC,CAAC;AAMxC;;;GAGG;AACH,MAAM,WAAW,SAAS;IACzB,2DAA2D;IAC3D,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,0EAA0E;IAC1E,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,kEAAkE;IAClE,QAAQ,CAAC,qBAAqB,CAAC,EAAE,MAAM,CAAC;IACxC,uDAAuD;IACvD,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,8EAA8E;IAC9E,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;CACjC;AAsED;;;;;;;;GAQG;AACH,qBAAa,qBAAsB,YAAW,kBAAkB;IAC/D,QAAQ,CAAC,IAAI,EAAG,KAAK,CAAU;IAE/B,6CAA6C;gBACjC,MAAM,CAAC,EAAE,SAAS;IAQ9B;;;;;OAKG;IACH,MAAM,CAAC,GAAG,EAAE,eAAe,GAAG,gBAAgB;IAoC9C,yEAAyE;IACzE,YAAY,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO;IAQ3C,qEAAqE;IACrE,eAAe,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO;CAa9C"}
@@ -1,3 +1,4 @@
1
+ import { asThoughtId } from "../../../contracts/ids.js";
1
2
  import { scoreThought, selectBeam } from "./totScoring.js";
2
3
  import { detectPlateau } from "./plateau.js";
3
4
  const DEFAULTS = {
@@ -12,7 +13,7 @@ function configOf(s) {
12
13
  return CONFIGS.get(s) ?? DEFAULTS;
13
14
  }
14
15
  function thoughtKey(t) {
15
- return t.id ?? String(t.thought_number);
16
+ return t.id ?? asThoughtId(String(t.thought_number));
16
17
  }
17
18
  function indexHistory(history) {
18
19
  const out = new Map();
@@ -41,12 +42,19 @@ function recentScores(history, window) {
41
42
  for(let i = start; i < history.length; i++)out.push(scoreThought(history[i]));
42
43
  return out;
43
44
  }
45
+ function isAtDepthCap(ctx, depthCap) {
46
+ const depth = ctx.graph?.depthFromRoots(ctx.sessionId, thoughtKey(ctx.currentThought));
47
+ return void 0 !== depth && depth >= depthCap;
48
+ }
44
49
  class TreeOfThoughtStrategy {
45
50
  name = 'tot';
46
51
  constructor(config){
52
+ const depthCap = config?.depthCap ?? DEFAULTS.depthCap;
53
+ if (!Number.isFinite(depthCap) || !Number.isInteger(depthCap) || depthCap < 0) throw new TypeError('depthCap must be a finite nonnegative integer');
47
54
  CONFIGS.set(this, {
48
55
  ...DEFAULTS,
49
- ...config ?? {}
56
+ ...config,
57
+ depthCap
50
58
  });
51
59
  }
52
60
  decide(ctx) {
@@ -54,6 +62,10 @@ class TreeOfThoughtStrategy {
54
62
  if (!ctx.graph) return {
55
63
  action: 'continue'
56
64
  };
65
+ if (isAtDepthCap(ctx, cfg.depthCap)) return {
66
+ action: 'terminate',
67
+ reason: 'depth cap'
68
+ };
57
69
  const frontier = ctx.graph.leaves(ctx.sessionId);
58
70
  const byKey = indexHistory(ctx.history);
59
71
  const scored = scoreFrontier(frontier, byKey);
@@ -84,12 +96,14 @@ class TreeOfThoughtStrategy {
84
96
  shouldBranch(ctx) {
85
97
  const cfg = configOf(this);
86
98
  if (!ctx.graph) return false;
99
+ if (isAtDepthCap(ctx, cfg.depthCap)) return false;
87
100
  const frontier = ctx.graph.leaves(ctx.sessionId);
88
101
  return frontier.length > cfg.beamWidth;
89
102
  }
90
103
  shouldTerminate(ctx) {
91
104
  const cfg = configOf(this);
92
105
  if (!ctx.graph) return false;
106
+ if (isAtDepthCap(ctx, cfg.depthCap)) return true;
93
107
  const frontier = ctx.graph.leaves(ctx.sessionId);
94
108
  const byKey = indexHistory(ctx.history);
95
109
  const scored = scoreFrontier(frontier, byKey);
@@ -1 +1 @@
1
- {"version":3,"file":"core/reasoning/strategies/TreeOfThoughtStrategy.js","sources":["../../../../src/core/reasoning/strategies/TreeOfThoughtStrategy.ts"],"sourcesContent":["/**\n * Tree-of-Thought reasoning strategy — beam search over the thought DAG.\n *\n * Observes the frontier (graph leaves), scores each leaf via\n * {@link scoreThought}, and decides whether to continue, branch (when the\n * current thought falls outside the beam), or terminate (on confidence\n * threshold or score plateau).\n *\n * Pure policy: configuration lives in a module-level {@link WeakMap}, not\n * on the instance, so `Object.getOwnPropertyNames(strategy)` only ever\n * surfaces the readonly `name` discriminator. All other state is derived\n * from the {@link StrategyContext} on every call.\n *\n * @module core/reasoning/strategies/TreeOfThoughtStrategy\n */\n\nimport type {\n\tIReasoningStrategy,\n\tStrategyContext,\n\tStrategyDecision,\n} from '../../../contracts/strategy.js';\nimport type { ThoughtData } from '../../thought.js';\nimport { scoreThought, selectBeam, type ScoredCandidate } from './totScoring.js';\nimport { detectPlateau } from './plateau.js';\n\n/**\n * Configuration knobs for {@link TreeOfThoughtStrategy}. All fields are\n * optional; defaults are applied in the constructor.\n */\nexport interface TotConfig {\n\t/** Top-K candidates kept on the frontier (default `3`). */\n\treadonly beamWidth?: number;\n\t/** Maximum exploration depth before forcing termination (default `8`). */\n\treadonly depthCap?: number;\n\t/** Score at/above which the chain terminates (default `0.85`). */\n\treadonly terminationConfidence?: number;\n\t/** Window size for plateau detection (default `3`). */\n\treadonly plateauWindow?: number;\n\t/** Minimum meaningful score change for plateau detection (default `0.02`). */\n\treadonly plateauEpsilon?: number;\n}\n\n/** Defaults applied when a {@link TotConfig} field is omitted. */\nconst DEFAULTS: Required<TotConfig> = {\n\tbeamWidth: 3,\n\tdepthCap: 8,\n\tterminationConfidence: 0.85,\n\tplateauWindow: 3,\n\tplateauEpsilon: 0.02,\n};\n\n/**\n * Module-private config storage. Keyed by strategy instance so that no\n * own-properties leak onto `this` (preserving the purity contract).\n */\nconst CONFIGS: WeakMap<TreeOfThoughtStrategy, Required<TotConfig>> = new WeakMap();\n\n/** Resolve the per-instance config (constructor always populates the map). */\nfunction configOf(s: TreeOfThoughtStrategy): Required<TotConfig> {\n\treturn CONFIGS.get(s) ?? DEFAULTS;\n}\n\n/** Stable identifier for a thought: `id` if present, else its sequence number. */\nfunction thoughtKey(t: ThoughtData): string {\n\treturn t.id ?? String(t.thought_number);\n}\n\n/** Index history by stable key for O(1) leaf-id → ThoughtData lookup. */\nfunction indexHistory(history: readonly ThoughtData[]): Map<string, ThoughtData> {\n\tconst out = new Map<string, ThoughtData>();\n\tfor (const t of history) out.set(thoughtKey(t), t);\n\treturn out;\n}\n\n/** Score the frontier (graph leaves), skipping ids absent from history. */\nfunction scoreFrontier(\n\tfrontier: readonly string[],\n\tbyKey: Map<string, ThoughtData>\n): ScoredCandidate[] {\n\tconst out: ScoredCandidate[] = [];\n\tfor (const id of frontier) {\n\t\tconst t = byKey.get(id);\n\t\tif (t !== undefined) out.push({ id, score: scoreThought(t) });\n\t}\n\treturn out;\n}\n\n/** Highest score in a candidate set, or `-Infinity` when empty. */\nfunction bestScore(scored: readonly ScoredCandidate[]): number {\n\tlet best = Number.NEGATIVE_INFINITY;\n\tfor (const c of scored) {\n\t\tif (c.score > best) best = c.score;\n\t}\n\treturn best;\n}\n\n/** Recent per-thought scores, used for plateau detection. */\nfunction recentScores(history: readonly ThoughtData[], window: number): number[] {\n\tconst start = history.length > window ? history.length - window : 0;\n\tconst out: number[] = [];\n\tfor (let i = start; i < history.length; i++) out.push(scoreThought(history[i]!));\n\treturn out;\n}\n\n/**\n * Tree-of-Thought strategy: beam search over the thought DAG. Pure policy.\n *\n * @example\n * ```typescript\n * const strategy = new TreeOfThoughtStrategy({ beamWidth: 4 });\n * const decision = strategy.decide(ctx);\n * ```\n */\nexport class TreeOfThoughtStrategy implements IReasoningStrategy {\n\treadonly name = 'tot' as const;\n\n\t/** @param config - See {@link TotConfig}. */\n\tconstructor(config?: TotConfig) {\n\t\tCONFIGS.set(this, { ...DEFAULTS, ...(config ?? {}) });\n\t}\n\n\t/**\n\t * Compute the next action for the chain.\n\t *\n\t * Order of checks: termination by confidence → termination by plateau →\n\t * branch when the current thought is outside the beam → continue.\n\t */\n\tdecide(ctx: StrategyContext): StrategyDecision {\n\t\tconst cfg = configOf(this);\n\t\tif (!ctx.graph) {\n\t\t\treturn { action: 'continue' };\n\t\t}\n\t\tconst frontier = ctx.graph.leaves(ctx.sessionId);\n\t\tconst byKey = indexHistory(ctx.history);\n\t\tconst scored = scoreFrontier(frontier, byKey);\n\t\tif (scored.length > 0 && bestScore(scored) >= cfg.terminationConfidence) {\n\t\t\treturn { action: 'terminate', reason: 'confidence threshold' };\n\t\t}\n\n\t\tconst recent = recentScores(ctx.history, cfg.plateauWindow);\n\t\tif (detectPlateau(recent, cfg.plateauWindow, cfg.plateauEpsilon)) {\n\t\t\treturn { action: 'terminate', reason: 'plateau' };\n\t\t}\n\n\t\tif (scored.length > cfg.beamWidth) {\n\t\t\tconst beam = selectBeam(scored, cfg.beamWidth);\n\t\t\tconst currentKey = thoughtKey(ctx.currentThought);\n\t\t\tif (!beam.includes(currentKey)) {\n\t\t\t\treturn {\n\t\t\t\t\taction: 'branch',\n\t\t\t\t\tbranchId: `tot-${ctx.currentThought.thought_number}`,\n\t\t\t\t\tfromThought: ctx.currentThought.thought_number,\n\t\t\t\t\treason: 'outside beam',\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\treturn { action: 'continue', nextHint: 'explore frontier' };\n\t}\n\n\t/** True when the frontier is wider than the beam (diverse exploration). */\n\tshouldBranch(ctx: StrategyContext): boolean {\n\t\tconst cfg = configOf(this);\n\t\tif (!ctx.graph) return false;\n\t\tconst frontier = ctx.graph.leaves(ctx.sessionId);\n\t\treturn frontier.length > cfg.beamWidth;\n\t}\n\n\t/** True when the best frontier score crosses the threshold OR scores plateau. */\n\tshouldTerminate(ctx: StrategyContext): boolean {\n\t\tconst cfg = configOf(this);\n\t\tif (!ctx.graph) return false;\n\t\tconst frontier = ctx.graph.leaves(ctx.sessionId);\n\t\tconst byKey = indexHistory(ctx.history);\n\t\tconst scored = scoreFrontier(frontier, byKey);\n\t\tif (scored.length > 0 && bestScore(scored) >= cfg.terminationConfidence) {\n\t\t\treturn true;\n\t\t}\n\t\tconst recent = recentScores(ctx.history, cfg.plateauWindow);\n\t\treturn detectPlateau(recent, cfg.plateauWindow, cfg.plateauEpsilon);\n\t}\n}\n"],"names":["DEFAULTS","CONFIGS","WeakMap","configOf","s","thoughtKey","t","String","indexHistory","history","out","Map","scoreFrontier","frontier","byKey","id","undefined","scoreThought","bestScore","scored","best","Number","c","recentScores","window","start","i","TreeOfThoughtStrategy","config","ctx","cfg","recent","detectPlateau","beam","selectBeam","currentKey"],"mappings":"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"}
1
+ {"version":3,"file":"core/reasoning/strategies/TreeOfThoughtStrategy.js","sources":["../../../../src/core/reasoning/strategies/TreeOfThoughtStrategy.ts"],"sourcesContent":["/**\n * Tree-of-Thought reasoning strategy — beam search over the thought DAG.\n *\n * Observes the frontier (graph leaves), scores each leaf via\n * {@link scoreThought}, and decides whether to continue, branch (when the\n * current thought falls outside the beam), or terminate (on depth cap,\n * confidence threshold, or score plateau).\n *\n * Pure policy: configuration lives in a module-level {@link WeakMap}, not\n * on the instance, so `Object.getOwnPropertyNames(strategy)` only ever\n * surfaces the readonly `name` discriminator. All other state is derived\n * from the {@link StrategyContext} on every call.\n *\n * @module core/reasoning/strategies/TreeOfThoughtStrategy\n */\n\nimport type {\n\tIReasoningStrategy,\n\tStrategyContext,\n\tStrategyDecision,\n} from '../../../contracts/strategy.js';\nimport { asThoughtId, type ThoughtId } from '../../../contracts/ids.js';\nimport type { ThoughtData } from '../../thought.js';\nimport { scoreThought, selectBeam, type ScoredCandidate } from './totScoring.js';\nimport { detectPlateau } from './plateau.js';\n\n/**\n * Configuration knobs for {@link TreeOfThoughtStrategy}. All fields are\n * optional; defaults are applied in the constructor.\n */\nexport interface TotConfig {\n\t/** Top-K candidates kept on the frontier (default `3`). */\n\treadonly beamWidth?: number;\n\t/** Maximum exploration depth before forcing termination (default `8`). */\n\treadonly depthCap?: number;\n\t/** Score at/above which the chain terminates (default `0.85`). */\n\treadonly terminationConfidence?: number;\n\t/** Window size for plateau detection (default `3`). */\n\treadonly plateauWindow?: number;\n\t/** Minimum meaningful score change for plateau detection (default `0.02`). */\n\treadonly plateauEpsilon?: number;\n}\n\n/** Defaults applied when a {@link TotConfig} field is omitted. */\nconst DEFAULTS: Required<TotConfig> = {\n\tbeamWidth: 3,\n\tdepthCap: 8,\n\tterminationConfidence: 0.85,\n\tplateauWindow: 3,\n\tplateauEpsilon: 0.02,\n};\n\n/**\n * Module-private config storage. Keyed by strategy instance so that no\n * own-properties leak onto `this` (preserving the purity contract).\n */\nconst CONFIGS: WeakMap<TreeOfThoughtStrategy, Required<TotConfig>> = new WeakMap();\n\n/** Resolve the per-instance config (constructor always populates the map). */\nfunction configOf(s: TreeOfThoughtStrategy): Required<TotConfig> {\n\treturn CONFIGS.get(s) ?? DEFAULTS;\n}\n\n/** Stable identifier for a thought: `id` if present, else its sequence number. */\nfunction thoughtKey(t: ThoughtData): ThoughtId {\n\treturn t.id ?? asThoughtId(String(t.thought_number));\n}\n\n/** Index history by stable key for O(1) leaf-id → ThoughtData lookup. */\nfunction indexHistory(history: readonly ThoughtData[]): Map<string, ThoughtData> {\n\tconst out = new Map<string, ThoughtData>();\n\tfor (const t of history) out.set(thoughtKey(t), t);\n\treturn out;\n}\n\n/** Score the frontier (graph leaves), skipping ids absent from history. */\nfunction scoreFrontier(\n\tfrontier: readonly string[],\n\tbyKey: Map<string, ThoughtData>\n): ScoredCandidate[] {\n\tconst out: ScoredCandidate[] = [];\n\tfor (const id of frontier) {\n\t\tconst t = byKey.get(id);\n\t\tif (t !== undefined) out.push({ id, score: scoreThought(t) });\n\t}\n\treturn out;\n}\n\n/** Highest score in a candidate set, or `-Infinity` when empty. */\nfunction bestScore(scored: readonly ScoredCandidate[]): number {\n\tlet best = Number.NEGATIVE_INFINITY;\n\tfor (const c of scored) {\n\t\tif (c.score > best) best = c.score;\n\t}\n\treturn best;\n}\n\n/** Recent per-thought scores, used for plateau detection. */\nfunction recentScores(history: readonly ThoughtData[], window: number): number[] {\n\tconst start = history.length > window ? history.length - window : 0;\n\tconst out: number[] = [];\n\tfor (let i = start; i < history.length; i++) out.push(scoreThought(history[i]!));\n\treturn out;\n}\n\n/** Whether the current graph-visible thought has reached the configured depth cap. */\nfunction isAtDepthCap(ctx: StrategyContext, depthCap: number): boolean {\n\tconst depth = ctx.graph?.depthFromRoots(ctx.sessionId, thoughtKey(ctx.currentThought));\n\treturn depth !== undefined && depth >= depthCap;\n}\n\n/**\n * Tree-of-Thought strategy: beam search over the thought DAG. Pure policy.\n *\n * @example\n * ```typescript\n * const strategy = new TreeOfThoughtStrategy({ beamWidth: 4 });\n * const decision = strategy.decide(ctx);\n * ```\n */\nexport class TreeOfThoughtStrategy implements IReasoningStrategy {\n\treadonly name = 'tot' as const;\n\n\t/** @param config - See {@link TotConfig}. */\n\tconstructor(config?: TotConfig) {\n\t\tconst depthCap = config?.depthCap ?? DEFAULTS.depthCap;\n\t\tif (!Number.isFinite(depthCap) || !Number.isInteger(depthCap) || depthCap < 0) {\n\t\t\tthrow new TypeError('depthCap must be a finite nonnegative integer');\n\t\t}\n\t\tCONFIGS.set(this, { ...DEFAULTS, ...config, depthCap });\n\t}\n\n\t/**\n\t * Compute the next action for the chain.\n\t *\n\t * Order of checks: depth cap → confidence → plateau → branch when the\n\t * current thought is outside the beam → continue. Missing graphs continue.\n\t */\n\tdecide(ctx: StrategyContext): StrategyDecision {\n\t\tconst cfg = configOf(this);\n\t\tif (!ctx.graph) {\n\t\t\treturn { action: 'continue' };\n\t\t}\n\t\tif (isAtDepthCap(ctx, cfg.depthCap)) {\n\t\t\treturn { action: 'terminate', reason: 'depth cap' };\n\t\t}\n\t\tconst frontier = ctx.graph.leaves(ctx.sessionId);\n\t\tconst byKey = indexHistory(ctx.history);\n\t\tconst scored = scoreFrontier(frontier, byKey);\n\t\tif (scored.length > 0 && bestScore(scored) >= cfg.terminationConfidence) {\n\t\t\treturn { action: 'terminate', reason: 'confidence threshold' };\n\t\t}\n\n\t\tconst recent = recentScores(ctx.history, cfg.plateauWindow);\n\t\tif (detectPlateau(recent, cfg.plateauWindow, cfg.plateauEpsilon)) {\n\t\t\treturn { action: 'terminate', reason: 'plateau' };\n\t\t}\n\n\t\tif (scored.length > cfg.beamWidth) {\n\t\t\tconst beam = selectBeam(scored, cfg.beamWidth);\n\t\t\tconst currentKey = thoughtKey(ctx.currentThought);\n\t\t\tif (!beam.includes(currentKey)) {\n\t\t\t\treturn {\n\t\t\t\t\taction: 'branch',\n\t\t\t\t\tbranchId: `tot-${ctx.currentThought.thought_number}`,\n\t\t\t\t\tfromThought: ctx.currentThought.thought_number,\n\t\t\t\t\treason: 'outside beam',\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\treturn { action: 'continue', nextHint: 'explore frontier' };\n\t}\n\n\t/** True below the depth cap when the frontier is wider than the beam. */\n\tshouldBranch(ctx: StrategyContext): boolean {\n\t\tconst cfg = configOf(this);\n\t\tif (!ctx.graph) return false;\n\t\tif (isAtDepthCap(ctx, cfg.depthCap)) return false;\n\t\tconst frontier = ctx.graph.leaves(ctx.sessionId);\n\t\treturn frontier.length > cfg.beamWidth;\n\t}\n\n\t/** True at the depth cap, confidence threshold, or score plateau. */\n\tshouldTerminate(ctx: StrategyContext): boolean {\n\t\tconst cfg = configOf(this);\n\t\tif (!ctx.graph) return false;\n\t\tif (isAtDepthCap(ctx, cfg.depthCap)) return true;\n\t\tconst frontier = ctx.graph.leaves(ctx.sessionId);\n\t\tconst byKey = indexHistory(ctx.history);\n\t\tconst scored = scoreFrontier(frontier, byKey);\n\t\tif (scored.length > 0 && bestScore(scored) >= cfg.terminationConfidence) {\n\t\t\treturn true;\n\t\t}\n\t\tconst recent = recentScores(ctx.history, cfg.plateauWindow);\n\t\treturn detectPlateau(recent, cfg.plateauWindow, cfg.plateauEpsilon);\n\t}\n}\n"],"names":["DEFAULTS","CONFIGS","WeakMap","configOf","s","thoughtKey","t","asThoughtId","String","indexHistory","history","out","Map","scoreFrontier","frontier","byKey","id","undefined","scoreThought","bestScore","scored","best","Number","c","recentScores","window","start","i","isAtDepthCap","ctx","depthCap","depth","TreeOfThoughtStrategy","config","TypeError","cfg","recent","detectPlateau","beam","selectBeam","currentKey"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"totScoring.d.ts","sourceRoot":"","sources":["../../../../src/core/reasoning/strategies/totScoring.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,KAAK,EAAE,SAAS,EAAa,MAAM,2BAA2B,CAAC;AAYtE;;GAEG;AACH,MAAM,WAAW,eAAe;IAC/B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACvB;AAuCD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,WAAW,GAAG,MAAM,CAQnD;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,UAAU,CACzB,UAAU,EAAE,SAAS,eAAe,EAAE,EACtC,KAAK,EAAE,MAAM,GACX,SAAS,MAAM,EAAE,CAcnB;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,oBAAoB,CACnC,KAAK,EAAE,SAAS,EAChB,SAAS,EAAE,SAAS,EACpB,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,QAAQ,EAAE,MAAM,GACd,SAAS,MAAM,EAAE,CAqDnB"}
1
+ {"version":3,"file":"totScoring.d.ts","sourceRoot":"","sources":["../../../../src/core/reasoning/strategies/totScoring.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAGpD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,KAAK,EAAE,SAAS,EAAa,MAAM,2BAA2B,CAAC;AAYtE;;GAEG;AACH,MAAM,WAAW,eAAe;IAC/B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACvB;AAuCD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,WAAW,GAAG,MAAM,CAQnD;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,UAAU,CACzB,UAAU,EAAE,SAAS,eAAe,EAAE,EACtC,KAAK,EAAE,MAAM,GACX,SAAS,MAAM,EAAE,CAcnB;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,oBAAoB,CACnC,KAAK,EAAE,SAAS,EAChB,SAAS,EAAE,SAAS,EACpB,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,QAAQ,EAAE,MAAM,GACd,SAAS,MAAM,EAAE,CAqDnB"}