@xopcai/xopc 0.0.219 → 0.0.221

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 (172) hide show
  1. package/dist/browser-ext/manifest.json +1 -1
  2. package/dist/extensions/telegram/xopc.extension.json +1 -1
  3. package/dist/gateway/static/root/assets/{activity-store-slG02hSm.js → activity-store-NItGQmvR.js} +1 -1
  4. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-DZ6ETNIk.js → agent-avatar-dicebear-B8SBTGg3.js} +1 -1
  5. package/dist/gateway/static/root/assets/{agents-DxaIH0Ep.js → agents-Brs2-m6t.js} +1 -1
  6. package/dist/gateway/static/root/assets/{api-Tz57QxiN.js → api-53ZXsuyr.js} +1 -1
  7. package/dist/gateway/static/root/assets/{app-management-settings-panel-DdR1L4LR.js → app-management-settings-panel-BtmPXaqu.js} +1 -1
  8. package/dist/gateway/static/root/assets/{appearance-settings-wYbwqmee.js → appearance-settings-t7HoJyNN.js} +1 -1
  9. package/dist/gateway/static/root/assets/{apps-page-CyWr6KyO.js → apps-page-DJ_Dsb1j.js} +1 -1
  10. package/dist/gateway/static/root/assets/{archive-plugin-DKXa160O.js → archive-plugin-D8o8CP9Y.js} +1 -1
  11. package/dist/gateway/static/root/assets/{attachment-load-CzwA9g2y.js → attachment-load-CsLXzas8.js} +1 -1
  12. package/dist/gateway/static/root/assets/{automations-page-DwAXyWAY.js → automations-page-D-zgoAKz.js} +1 -1
  13. package/dist/gateway/static/root/assets/{automations-workspace-CL7ieYIt.js → automations-workspace-DehLRXfd.js} +1 -1
  14. package/dist/gateway/static/root/assets/{binary-plugins-DoQ_DnT7.js → binary-plugins-B-MjP_22.js} +1 -1
  15. package/dist/gateway/static/root/assets/{block-editor-Bj_Uxxng.js → block-editor-i4CfDqLG.js} +1 -1
  16. package/dist/gateway/static/root/assets/{browser-settings-page-BYa_FNC1.js → browser-settings-page-C3QIO4Ti.js} +1 -1
  17. package/dist/gateway/static/root/assets/{browser-workflow-inputs-D4roXE6r.js → browser-workflow-inputs-Bi6RSSpD.js} +1 -1
  18. package/dist/gateway/static/root/assets/{browser-workflows-page-CleKTAKD.js → browser-workflows-page-EEFHUm1i.js} +1 -1
  19. package/dist/gateway/static/root/assets/{capabilities-settings-panel-DYG37N0k.js → capabilities-settings-panel-BHhUp7FW.js} +1 -1
  20. package/dist/gateway/static/root/assets/{capability-presets-api-wtEN6Ss6.js → capability-presets-api-pC2fno7r.js} +1 -1
  21. package/dist/gateway/static/root/assets/{capability-presets-settings-panel-C0Q4dFq-.js → capability-presets-settings-panel-DoClNVLz.js} +1 -1
  22. package/dist/gateway/static/root/assets/{channel-recipient-api-DhuS2RJ3.js → channel-recipient-api-B5Szgu_z.js} +1 -1
  23. package/dist/gateway/static/root/assets/{channels-settings-CpO6Xg0n.js → channels-settings-Je-9a5ZC.js} +1 -1
  24. package/dist/gateway/static/root/assets/channels-status-swr-ByRbxC6z.js +1 -0
  25. package/dist/gateway/static/root/assets/{chat-terminal-dock-ClQMDe2n.js → chat-terminal-dock-DT-OFl4r.js} +1 -1
  26. package/dist/gateway/static/root/assets/{connectors-page-BvyiPdKU.js → connectors-page-D8z9Trw4.js} +1 -1
  27. package/dist/gateway/static/root/assets/{cron-expression-4DvCFs-v.js → cron-expression-MZ_AJqrm.js} +1 -1
  28. package/dist/gateway/static/root/assets/{date-picker-B6FSjt4Z.js → date-picker-Bv9hZLuD.js} +1 -1
  29. package/dist/gateway/static/root/assets/{dependency-picker-Za18eMp1.js → dependency-picker-D-6oO_Vk.js} +1 -1
  30. package/dist/gateway/static/root/assets/{desktop-pet-DUUf3yyJ.js → desktop-pet-CiI5caKj.js} +1 -1
  31. package/dist/gateway/static/root/assets/{desktop-pet-settings-Dwx3GfB8.js → desktop-pet-settings-DhkSOy4K.js} +1 -1
  32. package/dist/gateway/static/root/assets/{directory-picker-path-field-C6dfTCsl.js → directory-picker-path-field-C4s4Pz8I.js} +1 -1
  33. package/dist/gateway/static/root/assets/{extension-debug-page-Cordpg4X.js → extension-debug-page-DE7J1-6W.js} +1 -1
  34. package/dist/gateway/static/root/assets/{extension-page-Dw0dGyMl.js → extension-page-DSnACD7E.js} +1 -1
  35. package/dist/gateway/static/root/assets/{extension-settings-page-B2pUMaXm.js → extension-settings-page-Dk7k2Pok.js} +1 -1
  36. package/dist/gateway/static/root/assets/{gateway-config-api-DAZUOEQh.js → gateway-config-api-IOA1OlHx.js} +1 -1
  37. package/dist/gateway/static/root/assets/{gateway-config-swr-BIqRg-zo.js → gateway-config-swr-BzTbnu4O.js} +1 -1
  38. package/dist/gateway/static/root/assets/{gateway-settings-ik2f-pmV.js → gateway-settings-FX7QsgSQ.js} +1 -1
  39. package/dist/gateway/static/root/assets/{gateway-startup-retry-BFjWYOCS.js → gateway-startup-retry-B2zpkNvi.js} +1 -1
  40. package/dist/gateway/static/root/assets/{heartbeat-settings-BE81XJ8F.js → heartbeat-settings-D5CGVyci.js} +1 -1
  41. package/dist/gateway/static/root/assets/{home-page-BTWXJu_m.js → home-page-DzfZFZx5.js} +1 -1
  42. package/dist/gateway/static/root/assets/{image-generation-api-HXAWlgm6.js → image-generation-api-BXRjz-iY.js} +1 -1
  43. package/dist/gateway/static/root/assets/index-Dcj5KJRg.css +2 -0
  44. package/dist/gateway/static/root/assets/{index-B6gWYy08.js → index-EvA8c7fo.js} +11 -45
  45. package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-ROqQ_Xlo.js → keyboard-shortcuts-settings-BjYiM4xq.js} +1 -1
  46. package/dist/gateway/static/root/assets/{local-app-workbench-page-Dcaf190G.js → local-app-workbench-page-hPyTaVYU.js} +1 -1
  47. package/dist/gateway/static/root/assets/{local-apps-page-Be79cyMG.js → local-apps-page-7DHvwSgA.js} +1 -1
  48. package/dist/gateway/static/root/assets/{locale-store-DfK-Hofd.js → locale-store-iKjqemWG.js} +1 -1
  49. package/dist/gateway/static/root/assets/{logs-page-C2pjbXno.js → logs-page-DW0xMKWP.js} +1 -1
  50. package/dist/gateway/static/root/assets/{management-settings-CRspx0oY.js → management-settings-BSxppo9Z.js} +1 -1
  51. package/dist/gateway/static/root/assets/{markdown-split-G3PTpGw3.js → markdown-split-D4oDjZtl.js} +1 -1
  52. package/dist/gateway/static/root/assets/{markdown-view-D4FPJZ91.js → markdown-view-DYkxMtJA.js} +1 -1
  53. package/dist/gateway/static/root/assets/{media-plugins-DT0P7j7u.js → media-plugins-BPNTGROA.js} +1 -1
  54. package/dist/gateway/static/root/assets/messages-kx5W39dO.js +3 -0
  55. package/dist/gateway/static/root/assets/{notes-api-q7HjYO07.js → notes-api-DJizatxh.js} +1 -1
  56. package/dist/gateway/static/root/assets/{notes-page-Cgl2h7AR.js → notes-page-DU5tKPTq.js} +1 -1
  57. package/dist/gateway/static/root/assets/{notes-workbench-DFMQYyGJ.js → notes-workbench-D669d98r.js} +2 -2
  58. package/dist/gateway/static/root/assets/pcm-capture-worklet-uW_SEACy.js +1 -0
  59. package/dist/gateway/static/root/assets/{product-open-page-ComKLO4D.js → product-open-page-CPoQvD6P.js} +1 -1
  60. package/dist/gateway/static/root/assets/{project-detail-page-DUFmQscH.js → project-detail-page-CpsQ9PTA.js} +1 -1
  61. package/dist/gateway/static/root/assets/{projects-page-CTWxt3dv.js → projects-page-CitO1LMc.js} +1 -1
  62. package/dist/gateway/static/root/assets/{registry-api-BBoJ7khF.js → registry-api-Cx3JxXSL.js} +1 -1
  63. package/dist/gateway/static/root/assets/{remote-access-hub-qmG-3qjj.js → remote-access-hub-C9-bL4V6.js} +1 -1
  64. package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-CXHQc53b.js → runtime-tools-settings-panel-xqnWcvIv.js} +1 -1
  65. package/dist/gateway/static/root/assets/{schema-form-3jTDF2lJ.js → schema-form-CuIDPYu6.js} +1 -1
  66. package/dist/gateway/static/root/assets/{sessions-page-D_We4_MV.js → sessions-page-CPSARi4y.js} +1 -1
  67. package/dist/gateway/static/root/assets/settings-advanced-gate-BxT80Fmr.js +1 -0
  68. package/dist/gateway/static/root/assets/{settings-form-section-CUDozALU.js → settings-form-section-DkAv9ncE.js} +1 -1
  69. package/dist/gateway/static/root/assets/{settings-loading-skeleton-H3ZH6a6l.js → settings-loading-skeleton-BJUQiEKb.js} +1 -1
  70. package/dist/gateway/static/root/assets/{settings-page-tx29z21o.js → settings-page-KHRbLwmw.js} +1 -1
  71. package/dist/gateway/static/root/assets/{setup-status-panel-VunfXpTX.js → setup-status-panel-C8jd20Db.js} +1 -1
  72. package/dist/gateway/static/root/assets/{share-preview-page-MyThymEx.js → share-preview-page-WCVtJ3sX.js} +1 -1
  73. package/dist/gateway/static/root/assets/{shares-settings-DkZvY1cr.js → shares-settings-DrQ_cOXh.js} +1 -1
  74. package/dist/gateway/static/root/assets/skill-reload-api-C_4cG30x.js +1 -0
  75. package/dist/gateway/static/root/assets/{skills-page-BOOyMnno.js → skills-page-CKhR9J81.js} +1 -1
  76. package/dist/gateway/static/root/assets/{skills-page.utils-Bl5Yv3Cx.js → skills-page.utils-AYX-e5sD.js} +1 -1
  77. package/dist/gateway/static/root/assets/{system-settings-panel-DwQ3O1ta.js → system-settings-panel-IIF-K9ML.js} +1 -1
  78. package/dist/gateway/static/root/assets/{task-detail-page-BwOyrADL.js → task-detail-page-CYjsecEr.js} +1 -1
  79. package/dist/gateway/static/root/assets/{theme-store-Br4BtK4x.js → theme-store-BOD2D0dJ.js} +1 -1
  80. package/dist/gateway/static/root/assets/{use-autosave-DRS3RwS9.js → use-autosave-DcktF1j1.js} +1 -1
  81. package/dist/gateway/static/root/assets/{user-context-page-ClV2r3Cr.js → user-context-page-Cy1D2hkt.js} +1 -1
  82. package/dist/gateway/static/root/assets/{voice-api-key-field-CGTE5156.js → voice-api-key-field-BMkNTHHE.js} +1 -1
  83. package/dist/gateway/static/root/assets/{work-discovery-overlay-CW-HKD7h.js → work-discovery-overlay-DVoiXekA.js} +1 -1
  84. package/dist/gateway/static/root/assets/{workflow-run-setup-panel-Dtr9fDC8.js → workflow-run-setup-panel-Cw4LQjuM.js} +1 -1
  85. package/dist/gateway/static/root/assets/{workflows-page-CDOyNgZf.js → workflows-page-DKKH1_Yt.js} +1 -1
  86. package/dist/gateway/static/root/index.html +12 -12
  87. package/dist/package.js +1 -1
  88. package/dist/src/agent/agent-manager.js +3 -3
  89. package/dist/src/agent/embedded/model-runtime.js +1 -1
  90. package/dist/src/agent/embedded/run-for-session.js +1 -1
  91. package/dist/src/agent/image/tool-model-config.js +1 -1
  92. package/dist/src/agent/ipc/inbox.js +1 -1
  93. package/dist/src/agent/service.js +2 -2
  94. package/dist/src/agent/session/session-inspector.js +1 -1
  95. package/dist/src/agent/skills/hub-lock.js +1 -1
  96. package/dist/src/agent/tasks/task-judge-service.js +3 -3
  97. package/dist/src/ai-assist/text-assist.js +1 -1
  98. package/dist/src/auth/credentials.js +2 -2
  99. package/dist/src/auth/profiles/store.js +1 -1
  100. package/dist/src/auth/sync-provider-auth.js +2 -2
  101. package/dist/src/browser/providers/browser-ext-install.js +1 -1
  102. package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
  103. package/dist/src/cli/commands/doctor/checks/provider-auth.js +1 -1
  104. package/dist/src/cli/commands/extension-marketplace.js +1 -1
  105. package/dist/src/cli/commands/extension.js +1 -1
  106. package/dist/src/cli/commands/models.js +1 -1
  107. package/dist/src/config/index.js +4 -4
  108. package/dist/src/config/loader.js +1 -1
  109. package/dist/src/config/models-json.js +1 -1
  110. package/dist/src/connectors/connected-source-understanding.js +6 -5
  111. package/dist/src/connectors/learning-coordinator.js +11 -2
  112. package/dist/src/extensions/lockfile.js +1 -1
  113. package/dist/src/extensions/update.js +1 -1
  114. package/dist/src/gateway/agents-admin.js +1 -1
  115. package/dist/src/gateway/file-path-classifier.js +1 -1
  116. package/dist/src/gateway/hono/lib/config-payload.js +1 -1
  117. package/dist/src/gateway/hono/routes/auth-registry-extensions.js +1 -1
  118. package/dist/src/gateway/hono/routes/models.js +1 -1
  119. package/dist/src/gateway/hono/routes/understanding-sources.d.ts +9 -0
  120. package/dist/src/gateway/hono/routes/understanding-sources.js +47 -14
  121. package/dist/src/gateway/hono/routes/work-discovery.js +13 -3
  122. package/dist/src/gateway/service/agent-runner.d.ts +4 -0
  123. package/dist/src/gateway/service/agent-runner.js +6 -0
  124. package/dist/src/gateway/service/config-coordinator.js +2 -2
  125. package/dist/src/gateway/service/run-gateway-agent.js +13 -8
  126. package/dist/src/gateway/service/sessions-api.d.ts +9 -0
  127. package/dist/src/gateway/service/sessions-api.js +3 -0
  128. package/dist/src/gateway/service.js +5 -4
  129. package/dist/src/infra/update-runner.js +1 -1
  130. package/dist/src/knowledge/connected-knowledge-pipeline.d.ts +6 -0
  131. package/dist/src/knowledge/connected-knowledge-pipeline.js +35 -4
  132. package/dist/src/providers/auth-runtime/resolve-auth.js +1 -1
  133. package/dist/src/providers/index.js +2 -2
  134. package/dist/src/providers/model-registry.js +1 -1
  135. package/dist/src/providers/register-runtime-providers.js +1 -1
  136. package/dist/src/retrieval/queryProfile.d.ts +2 -0
  137. package/dist/src/retrieval/queryProfile.js +11 -1
  138. package/dist/src/storage/sqlite/index.d.ts +2 -2
  139. package/dist/src/storage/sqlite/index.js +3 -3
  140. package/dist/src/storage/sqlite/knowledge-repository.d.ts +5 -1
  141. package/dist/src/storage/sqlite/knowledge-repository.js +16 -1
  142. package/dist/src/storage/sqlite/memory-records-repository.d.ts +2 -0
  143. package/dist/src/storage/sqlite/memory-records-repository.js +21 -1
  144. package/dist/src/storage/sqlite/migrations/133_archive_legacy_work_discovery_profile_facts.sql +11 -0
  145. package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
  146. package/dist/src/storage/sqlite/migrations/runner.js +3 -3
  147. package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
  148. package/dist/src/storage/sqlite/schema.js +1 -1
  149. package/dist/src/storage/sqlite/session-repository.js +2 -2
  150. package/dist/src/tasks/home-query-service.d.ts +14 -0
  151. package/dist/src/tasks/home-query-service.js +19 -2
  152. package/dist/src/tui/tui-startup-resources.js +1 -1
  153. package/dist/src/tui/tui.js +1 -1
  154. package/dist/src/user-context/consolidation.d.ts +1 -0
  155. package/dist/src/user-context/consolidation.js +62 -2
  156. package/dist/src/user-context/focus-lifecycle.d.ts +2 -0
  157. package/dist/src/user-context/focus-lifecycle.js +26 -0
  158. package/dist/src/user-context/planner.js +46 -13
  159. package/dist/src/work-discovery/analyzer.js +6 -4
  160. package/dist/src/work-discovery/index.js +2 -2
  161. package/dist/src/work-discovery/service.d.ts +12 -3
  162. package/dist/src/work-discovery/service.js +164 -83
  163. package/dist/src/work-discovery/source-repository.d.ts +1 -0
  164. package/dist/src/work-discovery/source-repository.js +10 -4
  165. package/dist/src/work-discovery/types.d.ts +2 -1
  166. package/dist/src/workflows/service/workflow-run-service.js +1 -1
  167. package/package.json +3 -3
  168. package/dist/gateway/static/root/assets/channels-status-swr-CXhPeiNj.js +0 -1
  169. package/dist/gateway/static/root/assets/index-CDkuz_YC.css +0 -2
  170. package/dist/gateway/static/root/assets/messages-B701TEZ5.js +0 -3
  171. package/dist/gateway/static/root/assets/settings-advanced-gate-B_QhPgU0.js +0 -1
  172. package/dist/gateway/static/root/assets/skill-reload-api-DVDOh1iY.js +0 -1
@@ -423,6 +423,26 @@ function deleteMemoryRecord(recordId) {
423
423
  return db.prepare(`DELETE FROM memory_records WHERE record_id = ?`).run(recordId).changes > 0;
424
424
  });
425
425
  }
426
+ function countMemoryRecordsBySourceInstanceId(sourceInstanceId) {
427
+ const row = getSqliteDatabase().prepare(`SELECT COUNT(*) AS count FROM memory_records
428
+ WHERE json_extract(source_json, '$.sourceInstanceId') = ?`).get(sourceInstanceId);
429
+ return Number(row.count);
430
+ }
431
+ function deleteMemoryRecordsBySourceInstanceId(sourceInstanceId) {
432
+ return runSqliteWriteTransaction((db) => {
433
+ const recordIds = db.prepare(`SELECT record_id FROM memory_records
434
+ WHERE json_extract(source_json, '$.sourceInstanceId') = ?`).all(sourceInstanceId).map((row) => row.record_id);
435
+ if (!recordIds.length) return 0;
436
+ const removeFts = db.prepare("DELETE FROM memory_records_fts WHERE record_id = ?");
437
+ const removeRecord = db.prepare("DELETE FROM memory_records WHERE record_id = ?");
438
+ let deleted = 0;
439
+ for (const recordId of recordIds) {
440
+ removeFts.run(recordId);
441
+ deleted += Number(removeRecord.run(recordId).changes);
442
+ }
443
+ return deleted;
444
+ });
445
+ }
426
446
  /** Change lifecycle status without reconstructing or weakening the record's provenance. */
427
447
  function setMemoryRecordStatus(recordId, status, nowMs = Date.now()) {
428
448
  return runSqliteWriteTransaction((db) => {
@@ -658,4 +678,4 @@ function parseJsonValue(json, fallback) {
658
678
  }
659
679
  }
660
680
  //#endregion
661
- export { appendMemorySignal, appendMemoryTraceEvent, deleteMemoryRecord, getMemoryProviderState, getMemoryRecord, getMemoryTurnFeedback, hasUnresolvedMemoryConflict, listMemoryRecords, listMemorySignals, listMemoryTraceEvents, markMemoryRecordsConflicted, searchMemoryRecords, setMemoryProviderState, setMemoryRecordStatus, setMemoryTurnFeedback, summarizeMemoryRecallFeedback, upsertMemoryRecord };
681
+ export { appendMemorySignal, appendMemoryTraceEvent, countMemoryRecordsBySourceInstanceId, deleteMemoryRecord, deleteMemoryRecordsBySourceInstanceId, getMemoryProviderState, getMemoryRecord, getMemoryTurnFeedback, hasUnresolvedMemoryConflict, listMemoryRecords, listMemorySignals, listMemoryTraceEvents, markMemoryRecordsConflicted, searchMemoryRecords, setMemoryProviderState, setMemoryRecordStatus, setMemoryTurnFeedback, summarizeMemoryRecallFeedback, upsertMemoryRecord };
@@ -0,0 +1,11 @@
1
+ -- Project stack, repository workflow, and current focus now live in work discovery
2
+ -- summaries/threads/focuses instead of the user's durable profile.
3
+ UPDATE user_understandings
4
+ SET status = 'archived',
5
+ updated_at = CAST(strftime('%s', 'now') AS INTEGER) * 1000
6
+ WHERE status IN ('candidate', 'active', 'needs_review', 'stale')
7
+ AND (
8
+ canonical_key LIKE 'work-discovery:technology:%'
9
+ OR canonical_key LIKE 'work-discovery:workflow:%'
10
+ OR canonical_key LIKE 'work-discovery:focus:%'
11
+ );
@@ -3,7 +3,7 @@ import type { ApplyMigrationsOptions } from './types.js';
3
3
  /** Baseline schema version applied from schema.sql on first open. */
4
4
  export declare const XOPC_DB_BASELINE_SCHEMA_VERSION = 11;
5
5
  /** Latest schema version this release supports (increment when adding migrations). */
6
- export declare const XOPC_DB_SCHEMA_VERSION = 132;
6
+ export declare const XOPC_DB_SCHEMA_VERSION = 133;
7
7
  export declare function resolveMigrationsDir(override?: string): string;
8
8
  /**
9
9
  * Apply sequential SQL migrations from (currentVersion + 1) through targetVersion.
@@ -12,7 +12,7 @@ const log = createLogger("Sqlite:Migrations");
12
12
  /** Baseline schema version applied from schema.sql on first open. */
13
13
  const XOPC_DB_BASELINE_SCHEMA_VERSION = 11;
14
14
  /** Latest schema version this release supports (increment when adding migrations). */
15
- const XOPC_DB_SCHEMA_VERSION = 132;
15
+ const XOPC_DB_SCHEMA_VERSION = 133;
16
16
  const MODULE_DIR = dirname(fileURLToPath(import.meta.url));
17
17
  function backupBeforeTaskCutover(db, databasePath) {
18
18
  db.exec("PRAGMA wal_checkpoint(FULL)");
@@ -57,7 +57,7 @@ function applySingleMigration(db, migration) {
57
57
  * Each step runs in its own transaction and updates schema_meta on success.
58
58
  */
59
59
  function applyPendingMigrations(db, options = {}) {
60
- const targetVersion = options.targetVersion ?? 132;
60
+ const targetVersion = options.targetVersion ?? 133;
61
61
  let currentVersion = readSchemaVersion(db);
62
62
  if (currentVersion > targetVersion) throw new DatabaseSchemaTooNewError(currentVersion, targetVersion);
63
63
  if (currentVersion >= targetVersion) return currentVersion;
@@ -94,7 +94,7 @@ function applyPendingMigrations(db, options = {}) {
94
94
  }
95
95
  /** Inspect schema version without mutating the database. */
96
96
  function inspectSchemaMigrationStatus(db, options = {}) {
97
- const appVersion = options.targetVersion ?? 132;
97
+ const appVersion = options.targetVersion ?? 133;
98
98
  const dbVersion = readSchemaVersion(db);
99
99
  if (dbVersion > appVersion) return {
100
100
  dbVersion,
@@ -19,7 +19,7 @@ const log = createLogger('Sqlite:Migrations');
19
19
  export const XOPC_DB_BASELINE_SCHEMA_VERSION = 11;
20
20
 
21
21
  /** Latest schema version this release supports (increment when adding migrations). */
22
- export const XOPC_DB_SCHEMA_VERSION = 132;
22
+ export const XOPC_DB_SCHEMA_VERSION = 133;
23
23
 
24
24
  const MODULE_DIR = dirname(fileURLToPath(import.meta.url));
25
25
 
@@ -32,7 +32,7 @@ function ensureXopcDatabaseSchema(db, options = {}) {
32
32
  currentVersion = 11;
33
33
  }
34
34
  applyPendingMigrations(db, {
35
- targetVersion: 132,
35
+ targetVersion: 133,
36
36
  databasePath: options.databasePath
37
37
  });
38
38
  }
@@ -1,5 +1,5 @@
1
1
  import { getSqliteDatabase, runSqliteWriteTransaction } from "./transaction.js";
2
- import { escapeFts5Query } from "./fts.js";
2
+ import { buildFts5SearchQuery } from "./fts.js";
3
3
  import { buildDefaultSessionMetadata } from "./session-metadata.js";
4
4
  import { buildGlobalSessionStats, metadataToSessionInsert, sessionRowToMetadata } from "./row-mappers.js";
5
5
  import { validateSessionId } from "../../session/session-id.js";
@@ -129,7 +129,7 @@ function listSessionMetadata(query = {}) {
129
129
  let searchKeys = null;
130
130
  if (query.search?.trim()) {
131
131
  const rawSearch = query.search.trim();
132
- const ftsQuery = escapeFts5Query(rawSearch);
132
+ const ftsQuery = buildFts5SearchQuery(rawSearch);
133
133
  const ftsRows = db.prepare(`SELECT DISTINCT session_key FROM transcript_fts WHERE transcript_fts MATCH ? LIMIT 500`).all(ftsQuery);
134
134
  const ftsKeys = new Set(ftsRows.map((r) => r.session_key));
135
135
  const like = `%${rawSearch.toLowerCase()}%`;
@@ -30,6 +30,20 @@ interface HomeGatewayPort {
30
30
  readonly automationServiceInstance: AutomationService;
31
31
  readonly projects: ProjectService;
32
32
  readonly proactiveInbox: ProactiveInboxService;
33
+ readonly sessions: {
34
+ listActiveRuns(): Array<{
35
+ sessionKey: string;
36
+ runId: string;
37
+ }>;
38
+ getSession(key: string): Promise<{
39
+ name?: string;
40
+ updatedAt: string;
41
+ lastInteractionAt?: string;
42
+ routing?: {
43
+ agentId?: string;
44
+ };
45
+ } | null>;
46
+ };
33
47
  createWorkflowRunService(): WorkflowRunService;
34
48
  }
35
49
  export declare function decisionFromTask(model: TaskReadModel, projectName?: string, locale?: string): HomeDecision | null;
@@ -294,7 +294,8 @@ var HomeQueryService = class {
294
294
  const workflowRunStore = this.service.createWorkflowRunService().createRunStore(defaultAgent?.id ?? agents.defaultId);
295
295
  const nowMs = Date.now();
296
296
  const tasks = this.#tasks.list({ limit: 60 });
297
- const [workflowRuns, automations, automationRuns, projects, connectorApprovals] = await Promise.all([
297
+ const activeRuns = this.service.sessions.listActiveRuns();
298
+ const [workflowRuns, automations, automationRuns, projects, connectorApprovals, activeRunSessions] = await Promise.all([
298
299
  workflowRunStore.listRunSummaries(20),
299
300
  this.service.automationServiceInstance.list(),
300
301
  this.service.automationServiceInstance.listRuns({ limit: 10 }),
@@ -303,8 +304,23 @@ var HomeQueryService = class {
303
304
  principalId: "local-owner",
304
305
  status: "pending",
305
306
  limit: 100
306
- }))
307
+ })),
308
+ Promise.all(activeRuns.map(async (run) => ({
309
+ ...run,
310
+ session: await this.service.sessions.getSession(run.sessionKey)
311
+ })))
307
312
  ]);
313
+ const runningConversations = activeRunSessions.filter((item) => Boolean(item.session)).map((item) => {
314
+ const session = item.session;
315
+ const updatedAt = Date.parse(session.lastInteractionAt ?? session.updatedAt);
316
+ return {
317
+ sessionKey: item.sessionKey,
318
+ runId: item.runId,
319
+ ...session.name?.trim() ? { title: session.name.trim() } : {},
320
+ ...session.routing?.agentId ? { agentId: session.routing.agentId } : {},
321
+ updatedAt: Number.isFinite(updatedAt) ? updatedAt : nowMs
322
+ };
323
+ }).sort((left, right) => right.updatedAt - left.updatedAt);
308
324
  const activeTasks = tasks.filter((task) => task.phase !== "closed");
309
325
  const projectsById = new Map(projects.items.map((project) => [project.id, project]));
310
326
  const activeWorkflowRuns = workflowRuns.filter((run) => run.status === "queued" || run.status === "running").map(toHomeWorkflowRun);
@@ -395,6 +411,7 @@ var HomeQueryService = class {
395
411
  scheduled: upcomingAutomations,
396
412
  nowMs
397
413
  }),
414
+ runningConversations,
398
415
  decisions,
399
416
  attentionPolicy: governed.policy
400
417
  };
@@ -1,6 +1,6 @@
1
1
  import { resolveStateDir } from "../config/paths-state.js";
2
- import { resolveEffectiveAgentProfileForSession } from "../config/agent-profile.js";
3
2
  import { init_paths, resolveAgentProfileDir, resolveBundledSkillsDir } from "../config/paths.js";
3
+ import { resolveEffectiveAgentProfileForSession } from "../config/agent-profile.js";
4
4
  import { resolveBootstrapFilesSync } from "../agent/bootstrap/bootstrap-files.js";
5
5
  import { createSkillConfigManager, isSkillEnabled } from "../agent/skills/config.js";
6
6
  import { loadSkills } from "../agent/skills/index.js";
@@ -2,10 +2,10 @@ import { version } from "../../package.js";
2
2
  import { resolveStateDir, resolveXopcDatabasePath } from "../config/paths-state.js";
3
3
  import { parseAgentSessionKey } from "../routing/session-key-utils.js";
4
4
  import { init_agent_session_key, normalizeAgentId } from "../routing/agent-session-key.js";
5
- import { resolveEffectiveAgentProfileForSession } from "../config/agent-profile.js";
6
5
  import { init_paths } from "../config/paths.js";
7
6
  import { loadConfig } from "../config/loader.js";
8
7
  import { getAllModels, getAllProviders, getApiKey, init_providers, isProviderConfiguredSync } from "../providers/index.js";
8
+ import { resolveEffectiveAgentProfileForSession } from "../config/agent-profile.js";
9
9
  import { ProjectTrustStore, hasTrustRequiringProjectResources } from "../project-trust/trust-store.js";
10
10
  import { renderWorkflowFinalSummary, renderWorkflowPanel } from "../agent/workflow/snapshot.js";
11
11
  import { ensureStarterAgentsInitialized } from "../agent/starter-agents.js";
@@ -6,6 +6,7 @@ type ConsolidationMetrics = {
6
6
  scanned: number;
7
7
  needsReview: number;
8
8
  stale: number;
9
+ globalCandidates: number;
9
10
  };
10
11
  export declare function resolveContextConsolidationConfig(config: Config): {
11
12
  enabled: boolean;
@@ -1,9 +1,67 @@
1
1
  import { finishContextConsolidationRun, recordContextConsolidationDecision, startContextConsolidationRun } from "../storage/sqlite/context-consolidation-repository.js";
2
- import { listUnderstandingEvidence, listUnderstandings, setUnderstandingStatus } from "../storage/sqlite/user-context-repository.js";
2
+ import { createUnderstanding, linkUnderstandingEvidence, listUnderstandingEvidence, listUnderstandings, setUnderstandingStatus } from "../storage/sqlite/user-context-repository.js";
3
3
  import "../storage/sqlite/index.js";
4
4
  //#region src/user-context/consolidation.ts
5
5
  const USER_CONTEXT_CONSOLIDATION_TOKEN = "__xopc_user_context_consolidation__";
6
6
  const USER_CONTEXT_CONSOLIDATION_AUTOMATION_ID = "system-user-context-consolidation";
7
+ const GLOBAL_CANDIDATE_KINDS = new Set(["preference", "routine"]);
8
+ function stricterSensitivity(items) {
9
+ const order = [
10
+ "normal",
11
+ "personal",
12
+ "secret",
13
+ "regulated"
14
+ ];
15
+ return order[Math.max(...items.map((item) => order.indexOf(item.sensitivity)))] ?? "personal";
16
+ }
17
+ function stricterDisclosurePolicy(items) {
18
+ if (items.some((item) => item.disclosurePolicy === "silent")) return "silent";
19
+ if (items.some((item) => item.disclosurePolicy === "ask_before_reference")) return "ask_before_reference";
20
+ return "referenceable";
21
+ }
22
+ function createCrossProjectGlobalCandidates(minProjects) {
23
+ const all = listUnderstandings();
24
+ const existingGlobalKeys = new Set(all.filter((item) => item.scope.type === "global" && item.status !== "archived" && item.status !== "rejected").map((item) => item.canonicalKey));
25
+ const groups = /* @__PURE__ */ new Map();
26
+ for (const item of all) {
27
+ if (item.status !== "active" || item.scope.type !== "project" || !item.scope.id || !GLOBAL_CANDIDATE_KINDS.has(item.kind)) continue;
28
+ const normalizedStatement = item.statement.normalize("NFKC").toLocaleLowerCase().replace(/\s+/g, " ").trim();
29
+ const groupKey = `${item.canonicalKey}\u0000${normalizedStatement}`;
30
+ const group = groups.get(groupKey) ?? [];
31
+ group.push(item);
32
+ groups.set(groupKey, group);
33
+ }
34
+ let created = 0;
35
+ for (const items of groups.values()) {
36
+ const canonicalKey = items[0].canonicalKey;
37
+ const projectCount = new Set(items.map((item) => item.scope.id)).size;
38
+ if (projectCount < Math.max(2, minProjects) || existingGlobalKeys.has(canonicalKey)) continue;
39
+ const exemplar = [...items].sort((left, right) => right.confidence - left.confidence)[0];
40
+ const global = createUnderstanding({
41
+ kind: exemplar.kind,
42
+ canonicalKey,
43
+ status: "candidate",
44
+ scope: { type: "global" },
45
+ explicitness: "inferred",
46
+ durability: exemplar.durability,
47
+ sensitivity: stricterSensitivity(items),
48
+ disclosurePolicy: stricterDisclosurePolicy(items),
49
+ confidence: items.reduce((sum, item) => sum + item.confidence, 0) / items.length,
50
+ statement: exemplar.statement,
51
+ createdBy: "consolidation",
52
+ changeReason: `Repeated across ${projectCount} projects`
53
+ });
54
+ const linked = /* @__PURE__ */ new Set();
55
+ for (const item of items) for (const evidence of listUnderstandingEvidence(item.id, "supports")) {
56
+ if (linked.has(evidence.id)) continue;
57
+ linkUnderstandingEvidence(global.versionId, evidence.id, "supports", item.confidence);
58
+ linked.add(evidence.id);
59
+ }
60
+ existingGlobalKeys.add(canonicalKey);
61
+ created += 1;
62
+ }
63
+ return created;
64
+ }
7
65
  function distinctEvidenceCount(understandingId, relation) {
8
66
  return new Set(listUnderstandingEvidence(understandingId, relation).map((evidence) => `${evidence.sourceType}:${evidence.sourceInstanceId ?? ""}:${evidence.sourceRef}`)).size;
9
67
  }
@@ -29,7 +87,8 @@ async function runContextConsolidation(input) {
29
87
  const metrics = {
30
88
  scanned: 0,
31
89
  needsReview: 0,
32
- stale: 0
90
+ stale: 0,
91
+ globalCandidates: 0
33
92
  };
34
93
  try {
35
94
  const understandings = listUnderstandings(["candidate", "active"]).slice(0, resolved.limit);
@@ -89,6 +148,7 @@ async function runContextConsolidation(input) {
89
148
  metrics.needsReview += 1;
90
149
  }
91
150
  }
151
+ metrics.globalCandidates = createCrossProjectGlobalCandidates(resolved.minEvidenceSources);
92
152
  return {
93
153
  run: finishContextConsolidationRun({
94
154
  runId: run.runId,
@@ -0,0 +1,2 @@
1
+ import type { UserFocus } from './sources/types.js';
2
+ export declare function focusLifecycle(horizon: UserFocus['horizon'], now?: number): Pick<UserFocus, 'validFrom' | 'validTo' | 'reviewAt'>;
@@ -0,0 +1,26 @@
1
+ //#region src/user-context/focus-lifecycle.ts
2
+ const DAY_MS = 1440 * 60 * 1e3;
3
+ const HORIZON_DAYS = {
4
+ current: {
5
+ review: 14,
6
+ valid: 30
7
+ },
8
+ ongoing: {
9
+ review: 30,
10
+ valid: 120
11
+ },
12
+ long_term: {
13
+ review: 90,
14
+ valid: 365
15
+ }
16
+ };
17
+ function focusLifecycle(horizon, now = Date.now()) {
18
+ const days = HORIZON_DAYS[horizon];
19
+ return {
20
+ validFrom: now,
21
+ reviewAt: now + days.review * DAY_MS,
22
+ validTo: now + days.valid * DAY_MS
23
+ };
24
+ }
25
+ //#endregion
26
+ export { focusLifecycle };
@@ -1,8 +1,9 @@
1
- import { consumeContextConsent, ensureContextConsent, getUserProfile, listCollaborationRules, listUnderstandingEvidence, recordContextRun } from "../storage/sqlite/user-context-repository.js";
1
+ import { consumeContextConsent, ensureContextConsent, getUserProfile, listCollaborationRules, listUnderstandingEvidence, listUnderstandings, recordContextRun } from "../storage/sqlite/user-context-repository.js";
2
2
  import { retrievalLexicalSimilarity } from "../retrieval/textFeatures.js";
3
3
  import { buildRetrievalQueryProfile } from "../retrieval/queryProfile.js";
4
4
  import "../storage/sqlite/index.js";
5
5
  import { init_session_key, parseSessionKey } from "../routing/session-key.js";
6
+ import { ProjectStore } from "../projects/project-store.js";
6
7
  import { readAgentMessageContent } from "../agent/memory/agent-message-access.js";
7
8
  import { buildUserContextBlock } from "../agent/memory/context-fence.js";
8
9
  import { getUnderstandingSourceGrant, getUnderstandingSourceRun, listUserFocuses } from "./sources/repository.js";
@@ -13,8 +14,21 @@ import { randomUUID } from "node:crypto";
13
14
  init_session_key();
14
15
  const DEFAULT_MAX_CONTEXT_CHARS = 6e3;
15
16
  const DEFAULT_MAX_RESULTS = 12;
17
+ const SELF_REVIEW_MAX_CONTEXT_CHARS = 16e3;
18
+ const SELF_REVIEW_MAX_RESULTS = 50;
16
19
  const MAX_FOCUS_RESULTS = 3;
17
20
  const MIN_RELEVANCE_SCORE = .25;
21
+ function selfReviewScore(item) {
22
+ const authority = item.explicitness === "explicit" ? .2 : item.explicitness === "observed" ? .1 : 0;
23
+ const durable = item.durability === "durable" ? .1 : item.durability === "recurring" ? .05 : 0;
24
+ return Math.min(1, item.confidence * .7 + authority + durable);
25
+ }
26
+ function scopeLabel(scope, projects) {
27
+ if (scope.type === "global") return "Global";
28
+ if (scope.type === "project") return `Project: ${scope.id ? projects.get(scope.id)?.name ?? scope.id : "unknown"}`;
29
+ if (scope.type === "workspace") return `Workspace: ${scope.id ?? "unknown"}`;
30
+ return "This conversation";
31
+ }
18
32
  function prependAgentContext(message, block) {
19
33
  if (!block) return message;
20
34
  const prefix = `${block}\n\n`;
@@ -121,8 +135,15 @@ function focusScore(focus, query, scopeInput) {
121
135
  function renderContextBlock(params) {
122
136
  const understandingItems = params.items.filter((item) => item.objectType === "understanding");
123
137
  return [
138
+ params.selfReview ? [
139
+ "<user-context-review>",
140
+ "The user explicitly asked what xopc understands about them. This is a review of active, non-sensitive structured understanding across scopes.",
141
+ "Scope labels describe where each item applies. Do not present project facts as global personality traits.",
142
+ "An empty memory_search result does not mean structured user understanding is empty.",
143
+ "</user-context-review>"
144
+ ].join("\n") : "",
124
145
  params.profileLines.length ? `<user-profile>\n${params.profileLines.join("\n")}\n</user-profile>` : "",
125
- params.focuses.length ? `<active-focuses>\n${params.focuses.map((focus) => `- ${focus.title}: ${focus.summary} (${focus.horizon})`).join("\n")}\n</active-focuses>` : "",
146
+ params.focuses.length ? `<active-focuses>\n${params.focuses.map((focus) => `- ${params.selfReview ? `[${scopeLabel(focus.scope, params.projects)}] ` : ""}${focus.title}: ${focus.summary} (${focus.horizon})`).join("\n")}\n</active-focuses>` : "",
126
147
  params.rules.length ? `<collaboration-contract>\n${params.rules.map((rule) => `- ${rule.statement}`).join("\n")}\n</collaboration-contract>` : "",
127
148
  understandingItems.length ? buildUserContextBlock(understandingItems.map((item) => `- ${item.content}\n Evidence: ${params.understandingSources.get(item.recordId) ?? "Unknown source"}`).join("\n")) : ""
128
149
  ].filter(Boolean).join("\n\n");
@@ -143,14 +164,18 @@ var UserContextPlanner = class {
143
164
  };
144
165
  const started = Date.now();
145
166
  const now = Date.now();
146
- const maxResults = params.allocation?.maxResults ?? DEFAULT_MAX_RESULTS;
147
- const maxChars = params.allocation?.maxChars ?? DEFAULT_MAX_CONTEXT_CHARS;
167
+ const requestedMaxResults = params.allocation?.maxResults;
168
+ const requestedMaxChars = params.allocation?.maxChars;
148
169
  const scopeInput = {
149
170
  sessionKey: params.sessionKey,
150
171
  workspaceId: params.workspaceId,
151
172
  ...params.projectId ? { projectId: params.projectId } : {}
152
173
  };
153
174
  const queryProfile = buildRetrievalQueryProfile(query, scopeInput);
175
+ const selfReview = queryProfile.selfReview;
176
+ const maxResults = requestedMaxResults ?? (selfReview ? SELF_REVIEW_MAX_RESULTS : DEFAULT_MAX_RESULTS);
177
+ const maxChars = requestedMaxChars ?? (selfReview ? SELF_REVIEW_MAX_CONTEXT_CHARS : DEFAULT_MAX_CONTEXT_CHARS);
178
+ const projects = new ProjectStore();
154
179
  const excluded = new Set(params.excludedRecordIds ?? []);
155
180
  const rejected = [];
156
181
  const consentRequests = [];
@@ -166,6 +191,8 @@ var UserContextPlanner = class {
166
191
  rules: selectedRules,
167
192
  items,
168
193
  understandingSources,
194
+ selfReview,
195
+ projects,
169
196
  ...overrides
170
197
  });
171
198
  const profile = getUserProfile();
@@ -211,14 +238,16 @@ var UserContextPlanner = class {
211
238
  const rankedFocuses = listUserFocuses(["active"]).map((focus) => ({
212
239
  focus,
213
240
  score: focusScore(focus, query, scopeInput)
214
- })).sort((left, right) => right.score - left.score || right.focus.updatedAt - left.focus.updatedAt);
241
+ })).sort((left, right) => selfReview ? right.focus.confidence - left.focus.confidence || right.focus.updatedAt - left.focus.updatedAt : right.score - left.score || right.focus.updatedAt - left.focus.updatedAt);
215
242
  for (const { focus, score } of rankedFocuses) {
216
243
  const source = focusSourceDescription(focus);
217
244
  let reason = focusRejectionReason(focus, scopeInput, now);
218
- if (!reason && score <= MIN_RELEVANCE_SCORE) reason = "low_score";
219
- if (!reason && selectedFocuses.length >= MAX_FOCUS_RESULTS) reason = "budget";
245
+ if (selfReview && reason === "scope_mismatch") reason = void 0;
246
+ if (!reason && !selfReview && score <= MIN_RELEVANCE_SCORE) reason = "low_score";
247
+ if (!reason && selectedFocuses.length >= (selfReview ? maxResults : MAX_FOCUS_RESULTS)) reason = "budget";
220
248
  if (!reason && items.length >= maxResults) reason = "budget";
221
- const content = `${focus.title}: ${focus.summary} (${focus.horizon})`;
249
+ const focusScope = scopeLabel(focus.scope, projects);
250
+ const content = `${selfReview ? `[${focusScope}] ` : ""}${focus.title}: ${focus.summary} (${focus.horizon})`;
222
251
  const nextChars = render({ focuses: [...selectedFocuses, focus] }).length;
223
252
  if (!reason && nextChars > maxChars) reason = "budget";
224
253
  if (reason) {
@@ -306,16 +335,19 @@ var UserContextPlanner = class {
306
335
  injectedChars: addedChars
307
336
  });
308
337
  }
309
- const ranked = this.retriever.retrieve({
338
+ const ranked = (selfReview ? listUnderstandings(["active"]).map((item) => ({
339
+ item,
340
+ score: selfReviewScore(item)
341
+ })).sort((left, right) => right.score - left.score || right.item.updatedAt - left.item.updatedAt) : this.retriever.retrieve({
310
342
  query,
311
343
  sessionKey: params.sessionKey,
312
344
  workspaceId: params.workspaceId,
313
345
  ...params.projectId ? { projectId: params.projectId } : {},
314
346
  maxCandidates: Math.max(maxResults * 5, 50)
315
- }).filter((result) => !excluded.has(result.understanding.id)).map((result) => ({
347
+ }).map((result) => ({
316
348
  item: result.understanding,
317
349
  score: result.score
318
- }));
350
+ }))).filter(({ item }) => !excluded.has(item.id));
319
351
  for (const { item, score } of ranked) {
320
352
  const source = sourceDescription(item);
321
353
  if (items.length >= maxResults) {
@@ -338,7 +370,8 @@ var UserContextPlanner = class {
338
370
  continue;
339
371
  }
340
372
  let reason = rejectionReason(item, scopeInput, now, queryProfile.timeHints);
341
- if (!reason && score <= MIN_RELEVANCE_SCORE) reason = "low_score";
373
+ if (selfReview && reason === "scope_mismatch") reason = void 0;
374
+ if (!reason && !selfReview && score <= MIN_RELEVANCE_SCORE) reason = "low_score";
342
375
  if (!reason && item.disclosurePolicy === "ask_before_reference") {
343
376
  const consentStatus = consumeContextConsent(item.id, params.sessionKey);
344
377
  if (consentStatus !== "granted") {
@@ -377,7 +410,7 @@ var UserContextPlanner = class {
377
410
  recordId: item.id,
378
411
  objectType: "understanding",
379
412
  versionId: item.versionId,
380
- content: item.statement,
413
+ content: selfReview ? `[${scopeLabel(item.scope, projects)}] ${item.statement}` : item.statement,
381
414
  score,
382
415
  section: sectionFor(item),
383
416
  citation: `user-context://${item.id}`,
@@ -82,7 +82,7 @@ function validateSuggestion(value, allowedPaths) {
82
82
  function validateProfileCandidate(value, allowedRefs) {
83
83
  if (!value || typeof value !== "object") return null;
84
84
  const item = value;
85
- const category = item.category === "role" || item.category === "focus" || item.category === "technology" || item.category === "workflow" || item.category === "preference" ? item.category : void 0;
85
+ const category = item.category === "role" || item.category === "responsibility" || item.category === "preference" || item.category === "routine" ? item.category : void 0;
86
86
  if (!category || typeof item.statement !== "string") return null;
87
87
  const statement = item.statement.trim().slice(0, 500);
88
88
  if (statement.length < 4) return null;
@@ -181,8 +181,9 @@ async function analyzeWorkContext(input) {
181
181
  "You help a user resume real work in one explicitly selected local folder.",
182
182
  "Analyze only the supplied bounded snapshot. Never claim that you ran commands, tests, or inspected anything absent from it.",
183
183
  "Return only one JSON object with projectSummary, currentState, uncertainties, suggestions, profileCandidates, workThreads, conversationStarter, lowConfidence, and contextQuestion.",
184
- "profileCandidates contains stable, useful facts about the user inferred from the supplied project evidence. Include every distinct fact with direct support; do not pad the list.",
185
- "Each profile candidate has category (role, focus, technology, workflow, or preference), statement, confidence, and evidence.",
184
+ "profileCandidates contains only user-specific role, responsibility, preference, or routine facts that are directly supported and would remain useful outside this repository snapshot.",
185
+ "Do not put the project stack, packages, architecture, deployment setup, repository conventions, or current task in profileCandidates. Put project facts in projectSummary/currentState and current work in workThreads.",
186
+ "Each profile candidate has category (role, responsibility, preference, or routine), statement, confidence, and evidence.",
186
187
  USER_FACING_UNDERSTANDING_WRITING_GUIDANCE,
187
188
  "Do not infer sensitive traits, identity, health, finances, political views, or anything not directly supported by the work evidence.",
188
189
  "workThreads contains every distinct evidence-backed work stream with topicKey, title, summary, horizon, status, confidence, and evidenceRefs. Do not merge unrelated streams to force a fixed count.",
@@ -286,7 +287,8 @@ async function analyzeUnderstandingBatch(input) {
286
287
  "Analyze one bounded batch from a source the user explicitly chose to connect.",
287
288
  "Return only one JSON object with profileCandidates and workThreads.",
288
289
  "Return at most 8 profileCandidates and at most 8 workThreads. Prefer the strongest distinct findings.",
289
- "Each profile candidate has category (role, focus, technology, workflow, or preference), statement, confidence, evidence, and evidenceRefs.",
290
+ "profileCandidates contains only user-specific role, responsibility, preference, or routine facts with direct support. Current tasks belong in workThreads; project technologies, packages, architecture, and repository conventions are not user facts.",
291
+ "Each profile candidate has category (role, responsibility, preference, or routine), statement, confidence, evidence, and evidenceRefs.",
290
292
  USER_FACING_UNDERSTANDING_WRITING_GUIDANCE,
291
293
  "Each work thread has topicKey, title, summary, status, horizon, confidence, and evidenceRefs.",
292
294
  "Every evidenceRefs value must be one of the supplied refs. Omit anything without direct support.",
@@ -8,6 +8,6 @@ import { appendWorkUnderstandingEvidence, createWorkUnderstandingInvestigation,
8
8
  import { DEFAULT_WORK_INVESTIGATION_BUDGET, investigateWorkContext } from "./investigator.js";
9
9
  import { persistWorkThreadsFromDiscovery } from "./thread-service.js";
10
10
  import { getWorkDiscoveryDirectorySource, listWorkDiscoveryDirectorySources, revokeWorkDiscoveryDirectorySource, upsertWorkDiscoveryDirectorySource } from "./source-repository.js";
11
- import { WorkDiscoveryService } from "./service.js";
11
+ import { WorkDiscoveryService, normalizeUnderstandingSourceItems } from "./service.js";
12
12
  import { evaluateQuickUnderstandingCase, evaluateWorkUnderstandingCase, meetsQuickUnderstandingGate } from "./evaluation.js";
13
- export { DEFAULT_WORK_INVESTIGATION_BUDGET, WORK_DISCOVERY_SCAN_POLICY_VERSION, WorkDiscoveryService, analyzeUnderstandingSources, analyzeWorkContext, appendWorkUnderstandingEvidence, canonicalWorkDiscoveryRoot, createWorkDiscoveryRun, createWorkUnderstandingInvestigation, discoverWorkCandidates, evaluateQuickUnderstandingCase, evaluateWorkUnderstandingCase, findActiveWorkDiscoverySourceRefresh, getWorkDiscoveryDirectorySource, getWorkDiscoveryOnboardingState, getWorkDiscoveryRun, getWorkDiscoveryRunByIdempotencyKey, getWorkUnderstandingInvestigation, getWorkUnderstandingInvestigationForRun, investigateWorkContext, listWorkDiscoveryDirectorySources, listWorkDiscoverySourceRefreshes, listWorkUnderstandingEvidence, meetsQuickUnderstandingGate, persistWorkThreadsFromDiscovery, previewWorkDiscoveryRoot, probeWorkDiscoveryRoot, readWorkDiscoveryTextExcerpt, recordWorkDiscoverySourceRefresh, resetWorkUnderstandingInvestigation, resolvePersonalWorkRoots, revokeWorkDiscoveryDirectorySource, searchWorkDiscoveryText, setWorkDiscoveryFeedback, setWorkDiscoveryOnboardingState, summarizeWorkContextSnapshot, updateWorkDiscoveryRun, updateWorkDiscoverySourceRefreshForRun, updateWorkUnderstandingEvidenceObservation, updateWorkUnderstandingInvestigation, upsertWorkDiscoveryDirectorySource, workDiscoveryFingerprintsEqual, workDiscoveryResultMarkdown };
13
+ export { DEFAULT_WORK_INVESTIGATION_BUDGET, WORK_DISCOVERY_SCAN_POLICY_VERSION, WorkDiscoveryService, analyzeUnderstandingSources, analyzeWorkContext, appendWorkUnderstandingEvidence, canonicalWorkDiscoveryRoot, createWorkDiscoveryRun, createWorkUnderstandingInvestigation, discoverWorkCandidates, evaluateQuickUnderstandingCase, evaluateWorkUnderstandingCase, findActiveWorkDiscoverySourceRefresh, getWorkDiscoveryDirectorySource, getWorkDiscoveryOnboardingState, getWorkDiscoveryRun, getWorkDiscoveryRunByIdempotencyKey, getWorkUnderstandingInvestigation, getWorkUnderstandingInvestigationForRun, investigateWorkContext, listWorkDiscoveryDirectorySources, listWorkDiscoverySourceRefreshes, listWorkUnderstandingEvidence, meetsQuickUnderstandingGate, normalizeUnderstandingSourceItems, persistWorkThreadsFromDiscovery, previewWorkDiscoveryRoot, probeWorkDiscoveryRoot, readWorkDiscoveryTextExcerpt, recordWorkDiscoverySourceRefresh, resetWorkUnderstandingInvestigation, resolvePersonalWorkRoots, revokeWorkDiscoveryDirectorySource, searchWorkDiscoveryText, setWorkDiscoveryFeedback, setWorkDiscoveryOnboardingState, summarizeWorkContextSnapshot, updateWorkDiscoveryRun, updateWorkDiscoverySourceRefreshForRun, updateWorkUnderstandingEvidenceObservation, updateWorkUnderstandingInvestigation, upsertWorkDiscoveryDirectorySource, workDiscoveryFingerprintsEqual, workDiscoveryResultMarkdown };
@@ -1,7 +1,11 @@
1
1
  import type { Config } from '../config/schema.js';
2
2
  import type { ProjectService } from '../projects/project-service.js';
3
3
  import type { SessionIndex } from '../session/manager.js';
4
+ import type { UnderstandingSourceItem } from '../user-context/sources/types.js';
4
5
  import type { WorkDiscoveryCandidate, WorkDiscoveryOnboardingState, WorkDiscoveryRecognitionDecision, WorkDiscoveryRun, WorkDiscoverySource } from './types.js';
6
+ export type ModelProcessingPolicy = 'local_only' | 'remote_allowed';
7
+ /** Sanitize and round-robin local sources so one verbose source cannot starve the rest. */
8
+ export declare function normalizeUnderstandingSourceItems(rawItems: unknown[]): UnderstandingSourceItem[];
5
9
  export interface WorkDiscoveryServiceOptions {
6
10
  projects: ProjectService;
7
11
  sessions: SessionIndex;
@@ -24,7 +28,11 @@ export declare class WorkDiscoveryService {
24
28
  };
25
29
  discoverCandidates(signal?: AbortSignal): Promise<WorkDiscoveryCandidate[]>;
26
30
  listDirectorySources(): import("./types.js").WorkDiscoveryDirectorySource[];
27
- grantDirectorySource(rootPath: string): Promise<import("./types.js").WorkDiscoveryDirectorySource>;
31
+ getModelProcessingTarget(): {
32
+ provider: string;
33
+ remoteModel: boolean;
34
+ };
35
+ grantDirectorySource(rootPath: string, processingPolicy: ModelProcessingPolicy): Promise<import("./types.js").WorkDiscoveryDirectorySource>;
28
36
  checkDirectorySources(): Promise<any[]>;
29
37
  refreshDirectorySourceIfChanged(input: {
30
38
  id: string;
@@ -46,7 +54,7 @@ export declare class WorkDiscoveryService {
46
54
  reused?: undefined;
47
55
  }>;
48
56
  revokeDirectorySource(id: string): import("./types.js").WorkDiscoveryDirectorySource;
49
- importUnderstandingSources(rawItems: unknown[], signal?: AbortSignal, runId?: string, rawCheckpoints?: Record<string, unknown>): Promise<{
57
+ importUnderstandingSources(rawItems: unknown[], processingPolicy: ModelProcessingPolicy, signal?: AbortSignal, runId?: string, rawCheckpoints?: Record<string, unknown>): Promise<{
50
58
  profileCandidates: any[];
51
59
  workThreads: any[];
52
60
  focuses: any[];
@@ -59,7 +67,7 @@ export declare class WorkDiscoveryService {
59
67
  status: "rejected" | "pending";
60
68
  understandingId: string;
61
69
  id: string;
62
- category: "role" | "focus" | "technology" | "workflow" | "preference";
70
+ category: "role" | "responsibility" | "preference" | "routine";
63
71
  statement: string;
64
72
  confidence: "high" | "medium" | "low";
65
73
  evidence: string[];
@@ -86,6 +94,7 @@ export declare class WorkDiscoveryService {
86
94
  }): Promise<WorkDiscoveryRun>;
87
95
  startQuickRun(input: {
88
96
  idempotencyKey: string;
97
+ processingPolicy: ModelProcessingPolicy;
89
98
  }): Promise<WorkDiscoveryRun>;
90
99
  startRun(input: {
91
100
  rootPath: string;