@xopcai/xopc 0.0.147 → 0.0.149

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 (143) 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-DXeSSlw1.js → activity-store-B4s0ipx2.js} +1 -1
  4. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-CFdJKSjY.js → agent-avatar-dicebear-BSccACje.js} +1 -1
  5. package/dist/gateway/static/root/assets/agents-DZDOcgz5.js +2 -0
  6. package/dist/gateway/static/root/assets/{api-Bs69B5zA.js → api-BMdUsNCV.js} +1 -1
  7. package/dist/gateway/static/root/assets/{app-management-settings-panel-ag1ViXT_.js → app-management-settings-panel-DrLGhYkI.js} +1 -1
  8. package/dist/gateway/static/root/assets/{appearance-settings-C6iMpwOM.js → appearance-settings-BNasnY9D.js} +1 -1
  9. package/dist/gateway/static/root/assets/apps-page-Cmr-Hvm9.js +1 -0
  10. package/dist/gateway/static/root/assets/{archive-plugin-Dwi3mIBy.js → archive-plugin-BQ17IK9x.js} +1 -1
  11. package/dist/gateway/static/root/assets/{attachment-load-BaCDoq40.js → attachment-load-HXvgWHXu.js} +1 -1
  12. package/dist/gateway/static/root/assets/{automation-suggestion-card-CwqtFgnw.js → automation-suggestion-card-CjIWpsch.js} +1 -1
  13. package/dist/gateway/static/root/assets/automations-page-TWgUAI8t.js +6 -0
  14. package/dist/gateway/static/root/assets/{binary-plugins-DoAYhsxa.js → binary-plugins-DifWJzNZ.js} +1 -1
  15. package/dist/gateway/static/root/assets/{block-editor-Bw72pO7S.js → block-editor-DtRzIkPU.js} +4 -4
  16. package/dist/gateway/static/root/assets/browser-settings-page-DBsFItW_.js +8 -0
  17. package/dist/gateway/static/root/assets/{capability-presets-api-vdbAR65Q.js → capability-presets-api-DazKjWW1.js} +1 -1
  18. package/dist/gateway/static/root/assets/{capability-presets-settings-panel-BAsKbJgm.js → capability-presets-settings-panel-Cvrw4fD1.js} +2 -2
  19. package/dist/gateway/static/root/assets/{channel-recipient-api-rTG26jpV.js → channel-recipient-api-Bea6if_6.js} +1 -1
  20. package/dist/gateway/static/root/assets/channels-settings-BBkzrBYy.js +1 -0
  21. package/dist/gateway/static/root/assets/channels-status-swr-CuU6ez3y.js +1 -0
  22. package/dist/gateway/static/root/assets/{connector-logo-DIMP4NWY.js → connector-logo-BjbYbJjQ.js} +1 -1
  23. package/dist/gateway/static/root/assets/connectors-page-DLsZCAw7.js +2 -0
  24. package/dist/gateway/static/root/assets/{desktop-pet-kQq2e0BN.js → desktop-pet-C6V-PHwi.js} +1 -1
  25. package/dist/gateway/static/root/assets/{desktop-pet-settings-B6vtozMY.js → desktop-pet-settings-5Fcz47XQ.js} +1 -1
  26. package/dist/gateway/static/root/assets/{directory-picker-path-field-Dn5-e3v4.js → directory-picker-path-field-C8zZNUzC.js} +1 -1
  27. package/dist/gateway/static/root/assets/{dreaming-settings-BzwIujPG.js → dreaming-settings-gJSw7Adk.js} +3 -3
  28. package/dist/gateway/static/root/assets/{extension-debug-page-D_e_hsEy.js → extension-debug-page-DFE5ADJ0.js} +1 -1
  29. package/dist/gateway/static/root/assets/{extension-page-D15CWHBd.js → extension-page-CfR-aW8G.js} +1 -1
  30. package/dist/gateway/static/root/assets/{extension-settings-page-D4P0n1om.js → extension-settings-page-CFIEbs2L.js} +1 -1
  31. package/dist/gateway/static/root/assets/{gateway-config-api-B4qZctjG.js → gateway-config-api-B6M_ra8U.js} +1 -1
  32. package/dist/gateway/static/root/assets/gateway-settings-BrxzyVW2.js +1 -0
  33. package/dist/gateway/static/root/assets/{gateway-startup-retry-BpgMunEb.js → gateway-startup-retry-DNA3OOzV.js} +1 -1
  34. package/dist/gateway/static/root/assets/{goal-create-dialog-DrVHKXHq.js → goal-create-dialog-DRVRLc5Z.js} +1 -1
  35. package/dist/gateway/static/root/assets/goal-detail-page-8F7NElzP.js +4 -0
  36. package/dist/gateway/static/root/assets/{goals-config-api-B6YJB_ct.js → goals-config-api-D88opgcs.js} +1 -1
  37. package/dist/gateway/static/root/assets/goals-page-BaoLiV8G.js +2 -0
  38. package/dist/gateway/static/root/assets/{goals-settings-BROWCIcL.js → goals-settings-D07UL8Jo.js} +1 -1
  39. package/dist/gateway/static/root/assets/{heartbeat-settings-CoCwIOIg.js → heartbeat-settings-YCSNOcXC.js} +1 -1
  40. package/dist/gateway/static/root/assets/{index-CxkNzw3e.js → index-CH7XN6KY.js} +43 -43
  41. package/dist/gateway/static/root/assets/index-Dfnl9tp_.css +1 -0
  42. package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-Bm_AIYQ7.js → keyboard-shortcuts-settings-BHnh5OoY.js} +1 -1
  43. package/dist/gateway/static/root/assets/local-app-workbench-page-Boe8n4zH.js +2 -0
  44. package/dist/gateway/static/root/assets/local-apps-page-CKvtLzsF.js +1 -0
  45. package/dist/gateway/static/root/assets/{locale-store-Bp8joZck.js → locale-store-BneNAR8R.js} +1 -1
  46. package/dist/gateway/static/root/assets/logs-page-Cb4Uptct.js +7 -0
  47. package/dist/gateway/static/root/assets/{markdown-split-Dy7QpaAz.js → markdown-split-BVSFG99p.js} +1 -1
  48. package/dist/gateway/static/root/assets/{media-plugins-3Lxliy9D.js → media-plugins-DdOjIVk3.js} +1 -1
  49. package/dist/gateway/static/root/assets/{messages-CQGpD-dC.js → messages-t1yPaVCt.js} +5 -5
  50. package/dist/gateway/static/root/assets/models-hub-panel-CNiUKZUV.js +2 -0
  51. package/dist/gateway/static/root/assets/{notes-page-BJtT4vSf.js → notes-page-ZbLR3awn.js} +1 -1
  52. package/dist/gateway/static/root/assets/notes-workbench-C9__VkD6.js +4 -0
  53. package/dist/gateway/static/root/assets/{page-tabs-DKB6RDre.js → page-tabs-DMlQERNg.js} +1 -1
  54. package/dist/gateway/static/root/assets/{preview-open-alternatives-Br_YhYHp.js → preview-open-alternatives-CbiJRPxK.js} +1 -1
  55. package/dist/gateway/static/root/assets/project-detail-page-CZhuB5kO.js +1 -0
  56. package/dist/gateway/static/root/assets/projects-page-BOq4WVdR.js +1 -0
  57. package/dist/gateway/static/root/assets/remote-access-hub-BxGq0ebQ.js +1 -0
  58. package/dist/gateway/static/root/assets/{save-bar-controls-BgIE9BBh.js → save-bar-controls-BZTj3hxl.js} +1 -1
  59. package/dist/gateway/static/root/assets/{schedule-field-BXDP-oMz.js → schedule-field-CxYDObjT.js} +1 -1
  60. package/dist/gateway/static/root/assets/{schema-form-Dhv-SmjG.js → schema-form-CBbXgpKQ.js} +1 -1
  61. package/dist/gateway/static/root/assets/sessions-page-B6gSU_Au.js +1 -0
  62. package/dist/gateway/static/root/assets/settings-advanced-gate-DwuBL834.js +1 -0
  63. package/dist/gateway/static/root/assets/{settings-collapsible-section-CUTExnlK.js → settings-collapsible-section-BySKZ8rp.js} +1 -1
  64. package/dist/gateway/static/root/assets/{settings-form-section-CmnDhsf0.js → settings-form-section-CjINay6t.js} +1 -1
  65. package/dist/gateway/static/root/assets/{settings-loading-skeleton-B79dq-XY.js → settings-loading-skeleton-vitElc-n.js} +1 -1
  66. package/dist/gateway/static/root/assets/{settings-page-Db8uAv3X.js → settings-page-BNrVwuL7.js} +1 -1
  67. package/dist/gateway/static/root/assets/{settings-page-layout-DaFwstQw.js → settings-page-layout-HdlQOOqp.js} +1 -1
  68. package/dist/gateway/static/root/assets/setup-status-panel-BJeA4mmk.js +1 -0
  69. package/dist/gateway/static/root/assets/{share-preview-page-Bi00uXzV.js → share-preview-page-DC-eQONB.js} +1 -1
  70. package/dist/gateway/static/root/assets/shares-settings-DB2KYA2_.js +1 -0
  71. package/dist/gateway/static/root/assets/skill-reload-api-DQtZiejw.js +1 -0
  72. package/dist/gateway/static/root/assets/skills-page-Pm0fNe_p.js +2 -0
  73. package/dist/gateway/static/root/assets/{system-settings-panel-Cxaph7hW.js → system-settings-panel-CVa90q2f.js} +1 -1
  74. package/dist/gateway/static/root/assets/{theme-store-eCTN1RpG.js → theme-store-D1cOpgsy.js} +1 -1
  75. package/dist/gateway/static/root/assets/{typed-models-lib-CcZ_tyFY.js → typed-models-lib-DIKF9vpz.js} +1 -1
  76. package/dist/gateway/static/root/assets/{use-channel-catalog-C4PWbNoi.js → use-channel-catalog-DnvpI0Ys.js} +1 -1
  77. package/dist/gateway/static/root/assets/user-context-page-DnLqQyRG.js +1 -0
  78. package/dist/gateway/static/root/assets/{user-profile-fields-editor-CPOO9Jz5.js → user-profile-fields-editor-y8gTmkPX.js} +1 -1
  79. package/dist/gateway/static/root/assets/{user-profile-settings-panel-C8cgtSF7.js → user-profile-settings-panel-C3pvqe28.js} +1 -1
  80. package/dist/gateway/static/root/assets/{utils-CiZK2KwH.js → utils-BK2vwu_3.js} +1 -1
  81. package/dist/gateway/static/root/assets/voice-api-key-field-BiFIIWC4.js +1 -0
  82. package/dist/gateway/static/root/assets/work-discovery-page-D2-_bC0J.js +1 -0
  83. package/dist/gateway/static/root/assets/work-item-detail-page-B0FPG4eJ.js +1 -0
  84. package/dist/gateway/static/root/assets/work-page-BDeVxts_.js +1 -0
  85. package/dist/gateway/static/root/assets/workflow-run-setup-panel-BPzS67fU.js +3 -0
  86. package/dist/gateway/static/root/assets/workflows-page-xPhmrRLs.js +11 -0
  87. package/dist/gateway/static/root/index.html +7 -7
  88. package/dist/package.js +1 -1
  89. package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
  90. package/dist/src/gateway/hono/routes/work-discovery.js +32 -0
  91. package/dist/src/gateway/hono/routes/work-discovery.js.map +1 -1
  92. package/dist/src/storage/sqlite/migrations/046_work_discovery_feedback.sql +13 -0
  93. package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
  94. package/dist/src/storage/sqlite/migrations/runner.js +3 -3
  95. package/dist/src/storage/sqlite/migrations/runner.js.map +1 -1
  96. package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
  97. package/dist/src/storage/sqlite/schema.js +1 -1
  98. package/dist/src/work-discovery/analyzer.js +23 -4
  99. package/dist/src/work-discovery/analyzer.js.map +1 -1
  100. package/dist/src/work-discovery/index.js +2 -2
  101. package/dist/src/work-discovery/probe.d.ts +6 -0
  102. package/dist/src/work-discovery/probe.js +12 -1
  103. package/dist/src/work-discovery/probe.js.map +1 -1
  104. package/dist/src/work-discovery/repository.d.ts +7 -1
  105. package/dist/src/work-discovery/repository.js +35 -2
  106. package/dist/src/work-discovery/repository.js.map +1 -1
  107. package/dist/src/work-discovery/service.d.ts +6 -1
  108. package/dist/src/work-discovery/service.js +64 -5
  109. package/dist/src/work-discovery/service.js.map +1 -1
  110. package/dist/src/work-discovery/types.d.ts +21 -0
  111. package/dist/src/workflows/service/workflow-run-service.js +1 -1
  112. package/package.json +1 -1
  113. package/dist/gateway/static/root/assets/agents-DKjbRpbA.js +0 -2
  114. package/dist/gateway/static/root/assets/apps-page-CpX3lBFF.js +0 -1
  115. package/dist/gateway/static/root/assets/automations-page-DlupOxFQ.js +0 -6
  116. package/dist/gateway/static/root/assets/browser-settings-page-BwLoUBcZ.js +0 -8
  117. package/dist/gateway/static/root/assets/channels-settings-CxFWy0cE.js +0 -1
  118. package/dist/gateway/static/root/assets/channels-status-swr-OKlqTtKJ.js +0 -1
  119. package/dist/gateway/static/root/assets/connectors-page-BjQQwX62.js +0 -2
  120. package/dist/gateway/static/root/assets/gateway-settings-BvndV7TP.js +0 -1
  121. package/dist/gateway/static/root/assets/goal-detail-page-PHlCwv68.js +0 -4
  122. package/dist/gateway/static/root/assets/goals-page-Hg9uyO6e.js +0 -2
  123. package/dist/gateway/static/root/assets/index-DjCSOyaN.css +0 -1
  124. package/dist/gateway/static/root/assets/local-app-workbench-page-BJWFcxwG.js +0 -2
  125. package/dist/gateway/static/root/assets/local-apps-page-BsiZMrEf.js +0 -1
  126. package/dist/gateway/static/root/assets/logs-page-DOmK61ey.js +0 -7
  127. package/dist/gateway/static/root/assets/models-hub-panel-DGCZom4o.js +0 -2
  128. package/dist/gateway/static/root/assets/notes-workbench-B_bN7cOR.js +0 -4
  129. package/dist/gateway/static/root/assets/project-detail-page-Dq8JouJj.js +0 -1
  130. package/dist/gateway/static/root/assets/projects-page-DvfL7fSe.js +0 -1
  131. package/dist/gateway/static/root/assets/remote-access-hub-DqwMXSXP.js +0 -1
  132. package/dist/gateway/static/root/assets/sessions-page-B_mOZkQk.js +0 -1
  133. package/dist/gateway/static/root/assets/settings-advanced-gate-BHGmH6YS.js +0 -1
  134. package/dist/gateway/static/root/assets/setup-status-panel-BUSEj0KK.js +0 -1
  135. package/dist/gateway/static/root/assets/shares-settings-DzsTYfuX.js +0 -1
  136. package/dist/gateway/static/root/assets/skill-reload-api-DSsdU081.js +0 -1
  137. package/dist/gateway/static/root/assets/skills-page-DeB1tQZL.js +0 -2
  138. package/dist/gateway/static/root/assets/user-context-page-QTVMx_hG.js +0 -1
  139. package/dist/gateway/static/root/assets/voice-api-key-field-b-YZtvUu.js +0 -1
  140. package/dist/gateway/static/root/assets/work-discovery-page-4zVDOl2G.js +0 -1
  141. package/dist/gateway/static/root/assets/work-item-detail-page-CHe2tfkc.js +0 -1
  142. package/dist/gateway/static/root/assets/workflow-run-setup-panel-8SLCYeXE.js +0 -3
  143. package/dist/gateway/static/root/assets/workflows-page-DEOFqiWn.js +0 -11
@@ -1 +1 @@
1
- {"version":3,"file":"repository.js","names":[],"sources":["../../../src/work-discovery/repository.ts"],"sourcesContent":["import type { DatabaseSync } from 'node:sqlite';\n\nimport { requireXopcDatabase } from '../storage/sqlite/connection.js';\nimport { runSqliteWriteTransaction } from '../storage/sqlite/transaction.js';\n\nimport type {\n WorkDiscoveryOnboardingState,\n WorkDiscoveryOnboardingStatus,\n WorkDiscoveryRun,\n} from './types.js';\n\ntype OnboardingRow = {\n status: string;\n active_run_id: string | null;\n completed_at: number | null;\n dismissed_at: number | null;\n updated_at: number;\n};\n\ntype RunRow = {\n id: string;\n idempotency_key: string;\n source: string;\n status: string;\n stage: string | null;\n root_path: string;\n project_id: string;\n session_key: string;\n agent_id: string;\n model_ref: string;\n scan_policy_version: number;\n snapshot_summary_json: string | null;\n result_json: string | null;\n error_code: string | null;\n error_message: string | null;\n created_at: number;\n started_at: number | null;\n completed_at: number | null;\n canceled_at: number | null;\n};\n\nfunction parseJson<T>(value: string | null): T | undefined {\n if (!value) return undefined;\n try {\n return JSON.parse(value) as T;\n } catch {\n return undefined;\n }\n}\n\nfunction onboardingFromRow(row: OnboardingRow): WorkDiscoveryOnboardingState {\n return {\n status: row.status as WorkDiscoveryOnboardingStatus,\n ...(row.active_run_id ? { activeRunId: row.active_run_id } : {}),\n ...(row.completed_at != null ? { completedAt: row.completed_at } : {}),\n ...(row.dismissed_at != null ? { dismissedAt: row.dismissed_at } : {}),\n updatedAt: row.updated_at,\n };\n}\n\nfunction runFromRow(row: RunRow): WorkDiscoveryRun {\n return {\n id: row.id,\n idempotencyKey: row.idempotency_key,\n source: row.source as WorkDiscoveryRun['source'],\n status: row.status as WorkDiscoveryRun['status'],\n ...(row.stage ? { stage: row.stage as WorkDiscoveryRun['stage'] } : {}),\n rootPath: row.root_path,\n projectId: row.project_id,\n sessionKey: row.session_key,\n agentId: row.agent_id,\n modelRef: row.model_ref,\n scanPolicyVersion: row.scan_policy_version,\n ...(parseJson<WorkDiscoveryRun['snapshot']>(row.snapshot_summary_json)\n ? { snapshot: parseJson<WorkDiscoveryRun['snapshot']>(row.snapshot_summary_json) }\n : {}),\n ...(parseJson<WorkDiscoveryRun['result']>(row.result_json)\n ? { result: parseJson<WorkDiscoveryRun['result']>(row.result_json) }\n : {}),\n ...(row.error_code ? { errorCode: row.error_code as WorkDiscoveryRun['errorCode'] } : {}),\n ...(row.error_message ? { errorMessage: row.error_message } : {}),\n createdAt: row.created_at,\n ...(row.started_at != null ? { startedAt: row.started_at } : {}),\n ...(row.completed_at != null ? { completedAt: row.completed_at } : {}),\n ...(row.canceled_at != null ? { canceledAt: row.canceled_at } : {}),\n };\n}\n\nfunction readRun(db: DatabaseSync, clause: string, value: string): WorkDiscoveryRun | null {\n const row = db.prepare(`SELECT * FROM work_discovery_runs WHERE ${clause} = ?`).get(value) as RunRow | undefined;\n return row ? runFromRow(row) : null;\n}\n\nexport function getWorkDiscoveryOnboardingState(): WorkDiscoveryOnboardingState {\n const { db } = requireXopcDatabase();\n const row = db.prepare('SELECT * FROM work_discovery_onboarding WHERE singleton_id = 1').get() as OnboardingRow;\n return onboardingFromRow(row);\n}\n\nexport function setWorkDiscoveryOnboardingState(input: {\n status: WorkDiscoveryOnboardingStatus;\n activeRunId?: string | null;\n nowMs?: number;\n}): WorkDiscoveryOnboardingState {\n const now = input.nowMs ?? Date.now();\n runSqliteWriteTransaction((db) => {\n db.prepare(\n `UPDATE work_discovery_onboarding\n SET status = ?, active_run_id = ?,\n completed_at = CASE WHEN ? = 'completed' THEN ? ELSE completed_at END,\n dismissed_at = CASE WHEN ? = 'dismissed' THEN ? ELSE dismissed_at END,\n updated_at = ?\n WHERE singleton_id = 1`,\n ).run(input.status, input.activeRunId ?? null, input.status, now, input.status, now, now);\n });\n return getWorkDiscoveryOnboardingState();\n}\n\nexport function createWorkDiscoveryRun(run: WorkDiscoveryRun): WorkDiscoveryRun {\n runSqliteWriteTransaction((db) => {\n db.prepare(\n `INSERT INTO work_discovery_runs (\n id, idempotency_key, source, status, stage, root_path, project_id, session_key,\n agent_id, model_ref, scan_policy_version, snapshot_summary_json, result_json,\n error_code, error_message, created_at, started_at, completed_at, canceled_at\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n ).run(\n run.id,\n run.idempotencyKey,\n run.source,\n run.status,\n run.stage ?? null,\n run.rootPath,\n run.projectId,\n run.sessionKey,\n run.agentId,\n run.modelRef,\n run.scanPolicyVersion,\n run.snapshot ? JSON.stringify(run.snapshot) : null,\n run.result ? JSON.stringify(run.result) : null,\n run.errorCode ?? null,\n run.errorMessage ?? null,\n run.createdAt,\n run.startedAt ?? null,\n run.completedAt ?? null,\n run.canceledAt ?? null,\n );\n });\n return getWorkDiscoveryRun(run.id)!;\n}\n\nexport function getWorkDiscoveryRun(id: string): WorkDiscoveryRun | null {\n const { db } = requireXopcDatabase();\n return readRun(db, 'id', id);\n}\n\nexport function getWorkDiscoveryRunByIdempotencyKey(key: string): WorkDiscoveryRun | null {\n const { db } = requireXopcDatabase();\n return readRun(db, 'idempotency_key', key);\n}\n\nexport function updateWorkDiscoveryRun(\n id: string,\n patch: Partial<Pick<\n WorkDiscoveryRun,\n | 'status'\n | 'stage'\n | 'snapshot'\n | 'result'\n | 'errorCode'\n | 'errorMessage'\n | 'startedAt'\n | 'completedAt'\n | 'canceledAt'\n >>,\n): WorkDiscoveryRun | null {\n const existing = getWorkDiscoveryRun(id);\n if (!existing) return null;\n const next = { ...existing, ...patch };\n runSqliteWriteTransaction((db) => {\n db.prepare(\n `UPDATE work_discovery_runs SET\n status = ?, stage = ?, snapshot_summary_json = ?, result_json = ?,\n error_code = ?, error_message = ?, started_at = ?, completed_at = ?, canceled_at = ?\n WHERE id = ?`,\n ).run(\n next.status,\n next.stage ?? null,\n next.snapshot ? JSON.stringify(next.snapshot) : null,\n next.result ? JSON.stringify(next.result) : null,\n next.errorCode ?? null,\n next.errorMessage ?? null,\n next.startedAt ?? null,\n next.completedAt ?? null,\n next.canceledAt ?? null,\n id,\n );\n });\n return getWorkDiscoveryRun(id);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"repository.js","names":[],"sources":["../../../src/work-discovery/repository.ts"],"sourcesContent":["import type { DatabaseSync } from 'node:sqlite';\n\nimport { requireXopcDatabase } from '../storage/sqlite/connection.js';\nimport { runSqliteWriteTransaction } from '../storage/sqlite/transaction.js';\n\nimport type {\n WorkDiscoveryOnboardingState,\n WorkDiscoveryOnboardingStatus,\n WorkDiscoveryFeedback,\n WorkDiscoveryRecognitionDecision,\n WorkDiscoveryRun,\n} from './types.js';\n\ntype OnboardingRow = {\n status: string;\n active_run_id: string | null;\n completed_at: number | null;\n dismissed_at: number | null;\n updated_at: number;\n};\n\ntype RunRow = {\n id: string;\n idempotency_key: string;\n source: string;\n status: string;\n stage: string | null;\n root_path: string;\n project_id: string;\n session_key: string;\n agent_id: string;\n model_ref: string;\n scan_policy_version: number;\n snapshot_summary_json: string | null;\n result_json: string | null;\n error_code: string | null;\n error_message: string | null;\n created_at: number;\n started_at: number | null;\n completed_at: number | null;\n canceled_at: number | null;\n};\n\ntype FeedbackRow = {\n run_id: string;\n recognition_decision: string;\n corrected_intent: string | null;\n created_at: number;\n updated_at: number;\n};\n\nfunction parseJson<T>(value: string | null): T | undefined {\n if (!value) return undefined;\n try {\n return JSON.parse(value) as T;\n } catch {\n return undefined;\n }\n}\n\nfunction onboardingFromRow(row: OnboardingRow): WorkDiscoveryOnboardingState {\n return {\n status: row.status as WorkDiscoveryOnboardingStatus,\n ...(row.active_run_id ? { activeRunId: row.active_run_id } : {}),\n ...(row.completed_at != null ? { completedAt: row.completed_at } : {}),\n ...(row.dismissed_at != null ? { dismissedAt: row.dismissed_at } : {}),\n updatedAt: row.updated_at,\n };\n}\n\nfunction runFromRow(row: RunRow): WorkDiscoveryRun {\n return {\n id: row.id,\n idempotencyKey: row.idempotency_key,\n source: row.source as WorkDiscoveryRun['source'],\n status: row.status as WorkDiscoveryRun['status'],\n ...(row.stage ? { stage: row.stage as WorkDiscoveryRun['stage'] } : {}),\n rootPath: row.root_path,\n projectId: row.project_id,\n sessionKey: row.session_key,\n agentId: row.agent_id,\n modelRef: row.model_ref,\n scanPolicyVersion: row.scan_policy_version,\n ...(parseJson<WorkDiscoveryRun['snapshot']>(row.snapshot_summary_json)\n ? { snapshot: parseJson<WorkDiscoveryRun['snapshot']>(row.snapshot_summary_json) }\n : {}),\n ...(parseJson<WorkDiscoveryRun['result']>(row.result_json)\n ? { result: parseJson<WorkDiscoveryRun['result']>(row.result_json) }\n : {}),\n ...(row.error_code ? { errorCode: row.error_code as WorkDiscoveryRun['errorCode'] } : {}),\n ...(row.error_message ? { errorMessage: row.error_message } : {}),\n createdAt: row.created_at,\n ...(row.started_at != null ? { startedAt: row.started_at } : {}),\n ...(row.completed_at != null ? { completedAt: row.completed_at } : {}),\n ...(row.canceled_at != null ? { canceledAt: row.canceled_at } : {}),\n };\n}\n\nfunction readRun(db: DatabaseSync, clause: string, value: string): WorkDiscoveryRun | null {\n const row = db.prepare(`SELECT * FROM work_discovery_runs WHERE ${clause} = ?`).get(value) as RunRow | undefined;\n if (!row) return null;\n const run = runFromRow(row);\n const feedback = getWorkDiscoveryFeedbackFromDb(db, run.id);\n return feedback ? { ...run, feedback } : run;\n}\n\nfunction feedbackFromRow(row: FeedbackRow): WorkDiscoveryFeedback {\n return {\n runId: row.run_id,\n recognitionDecision: row.recognition_decision as WorkDiscoveryRecognitionDecision,\n ...(row.corrected_intent ? { correctedIntent: row.corrected_intent } : {}),\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n };\n}\n\nfunction getWorkDiscoveryFeedbackFromDb(db: DatabaseSync, runId: string): WorkDiscoveryFeedback | null {\n const row = db.prepare('SELECT * FROM work_discovery_feedback WHERE run_id = ?').get(runId) as FeedbackRow | undefined;\n return row ? feedbackFromRow(row) : null;\n}\n\nexport function getWorkDiscoveryOnboardingState(): WorkDiscoveryOnboardingState {\n const { db } = requireXopcDatabase();\n const row = db.prepare('SELECT * FROM work_discovery_onboarding WHERE singleton_id = 1').get() as OnboardingRow;\n return onboardingFromRow(row);\n}\n\nexport function setWorkDiscoveryOnboardingState(input: {\n status: WorkDiscoveryOnboardingStatus;\n activeRunId?: string | null;\n nowMs?: number;\n}): WorkDiscoveryOnboardingState {\n const now = input.nowMs ?? Date.now();\n runSqliteWriteTransaction((db) => {\n db.prepare(\n `UPDATE work_discovery_onboarding\n SET status = ?, active_run_id = ?,\n completed_at = CASE WHEN ? = 'completed' THEN ? ELSE completed_at END,\n dismissed_at = CASE WHEN ? = 'dismissed' THEN ? ELSE dismissed_at END,\n updated_at = ?\n WHERE singleton_id = 1`,\n ).run(input.status, input.activeRunId ?? null, input.status, now, input.status, now, now);\n });\n return getWorkDiscoveryOnboardingState();\n}\n\nexport function createWorkDiscoveryRun(run: WorkDiscoveryRun): WorkDiscoveryRun {\n runSqliteWriteTransaction((db) => {\n db.prepare(\n `INSERT INTO work_discovery_runs (\n id, idempotency_key, source, status, stage, root_path, project_id, session_key,\n agent_id, model_ref, scan_policy_version, snapshot_summary_json, result_json,\n error_code, error_message, created_at, started_at, completed_at, canceled_at\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n ).run(\n run.id,\n run.idempotencyKey,\n run.source,\n run.status,\n run.stage ?? null,\n run.rootPath,\n run.projectId,\n run.sessionKey,\n run.agentId,\n run.modelRef,\n run.scanPolicyVersion,\n run.snapshot ? JSON.stringify(run.snapshot) : null,\n run.result ? JSON.stringify(run.result) : null,\n run.errorCode ?? null,\n run.errorMessage ?? null,\n run.createdAt,\n run.startedAt ?? null,\n run.completedAt ?? null,\n run.canceledAt ?? null,\n );\n });\n return getWorkDiscoveryRun(run.id)!;\n}\n\nexport function getWorkDiscoveryRun(id: string): WorkDiscoveryRun | null {\n const { db } = requireXopcDatabase();\n return readRun(db, 'id', id);\n}\n\nexport function getWorkDiscoveryRunByIdempotencyKey(key: string): WorkDiscoveryRun | null {\n const { db } = requireXopcDatabase();\n return readRun(db, 'idempotency_key', key);\n}\n\nexport function setWorkDiscoveryFeedback(input: {\n runId: string;\n recognitionDecision: WorkDiscoveryRecognitionDecision;\n correctedIntent?: string;\n nowMs?: number;\n}): WorkDiscoveryFeedback {\n const now = input.nowMs ?? Date.now();\n runSqliteWriteTransaction((db) => {\n db.prepare(\n `INSERT INTO work_discovery_feedback (\n run_id, recognition_decision, corrected_intent, created_at, updated_at\n ) VALUES (?, ?, ?, ?, ?)\n ON CONFLICT(run_id) DO UPDATE SET\n recognition_decision = excluded.recognition_decision,\n corrected_intent = excluded.corrected_intent,\n updated_at = excluded.updated_at`,\n ).run(\n input.runId,\n input.recognitionDecision,\n input.correctedIntent ?? null,\n now,\n now,\n );\n });\n const { db } = requireXopcDatabase();\n return getWorkDiscoveryFeedbackFromDb(db, input.runId)!;\n}\n\nexport function updateWorkDiscoveryRun(\n id: string,\n patch: Partial<Pick<\n WorkDiscoveryRun,\n | 'status'\n | 'stage'\n | 'snapshot'\n | 'result'\n | 'errorCode'\n | 'errorMessage'\n | 'startedAt'\n | 'completedAt'\n | 'canceledAt'\n >>,\n): WorkDiscoveryRun | null {\n const existing = getWorkDiscoveryRun(id);\n if (!existing) return null;\n const next = { ...existing, ...patch };\n runSqliteWriteTransaction((db) => {\n db.prepare(\n `UPDATE work_discovery_runs SET\n status = ?, stage = ?, snapshot_summary_json = ?, result_json = ?,\n error_code = ?, error_message = ?, started_at = ?, completed_at = ?, canceled_at = ?\n WHERE id = ?`,\n ).run(\n next.status,\n next.stage ?? null,\n next.snapshot ? JSON.stringify(next.snapshot) : null,\n next.result ? JSON.stringify(next.result) : null,\n next.errorCode ?? null,\n next.errorMessage ?? null,\n next.startedAt ?? null,\n next.completedAt ?? null,\n next.canceledAt ?? null,\n id,\n );\n });\n return getWorkDiscoveryRun(id);\n}\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
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 { WorkDiscoveryOnboardingState, WorkDiscoveryRun, WorkDiscoverySource } from './types.js';
4
+ import type { WorkDiscoveryOnboardingState, WorkDiscoveryRecognitionDecision, WorkDiscoveryRun, WorkDiscoverySource } from './types.js';
5
5
  export interface WorkDiscoveryServiceOptions {
6
6
  projects: ProjectService;
7
7
  sessions: SessionIndex;
@@ -26,5 +26,10 @@ export declare class WorkDiscoveryService {
26
26
  private execute;
27
27
  cancelRun(id: string): WorkDiscoveryRun | null;
28
28
  retryRun(id: string): WorkDiscoveryRun | null;
29
+ submitRecognitionFeedback(input: {
30
+ runId: string;
31
+ decision: WorkDiscoveryRecognitionDecision;
32
+ correctedIntent?: string;
33
+ }): Promise<WorkDiscoveryRun | null>;
29
34
  selectSuggestion(runId: string, suggestionId: string): WorkDiscoveryRun | null;
30
35
  }
@@ -6,7 +6,7 @@ import { buildSessionKey, init_session_key } from "../routing/session-key.js";
6
6
  import { getDefaultAgentId, init_resolve_route } from "../routing/resolve-route.js";
7
7
  import { previewWorkDiscoveryRoot, probeWorkDiscoveryRoot, summarizeWorkContextSnapshot } from "./probe.js";
8
8
  import { analyzeWorkContext, workDiscoveryResultMarkdown } from "./analyzer.js";
9
- import { createWorkDiscoveryRun, getWorkDiscoveryOnboardingState, getWorkDiscoveryRun, getWorkDiscoveryRunByIdempotencyKey, setWorkDiscoveryOnboardingState, updateWorkDiscoveryRun } from "./repository.js";
9
+ import { createWorkDiscoveryRun, getWorkDiscoveryOnboardingState, getWorkDiscoveryRun, getWorkDiscoveryRunByIdempotencyKey, setWorkDiscoveryFeedback, setWorkDiscoveryOnboardingState, updateWorkDiscoveryRun } from "./repository.js";
10
10
  import { randomUUID } from "node:crypto";
11
11
  //#region src/work-discovery/service.ts
12
12
  init_schema();
@@ -215,10 +215,6 @@ var WorkDiscoveryService = class {
215
215
  result: analysis.result,
216
216
  completedAt: Date.now()
217
217
  });
218
- if (run.source === "onboarding_selected_directory") setWorkDiscoveryOnboardingState({
219
- status: "completed",
220
- activeRunId: run.id
221
- });
222
218
  this.activity.record({
223
219
  type: "work_discovery.completed",
224
220
  primaryObject: {
@@ -346,6 +342,69 @@ var WorkDiscoveryService = class {
346
342
  this.execute(queued, controller.signal);
347
343
  return queued;
348
344
  }
345
+ async submitRecognitionFeedback(input) {
346
+ const run = getWorkDiscoveryRun(input.runId);
347
+ if (!run || run.status !== "completed" || !run.result) return null;
348
+ const correctedIntent = input.correctedIntent?.trim().slice(0, 2e3);
349
+ if ((input.decision === "corrected" || input.decision === "different_goal") && !correctedIntent) throw new Error("A corrected intent is required for this decision");
350
+ const feedback = setWorkDiscoveryFeedback({
351
+ runId: run.id,
352
+ recognitionDecision: input.decision,
353
+ ...correctedIntent ? { correctedIntent } : {}
354
+ });
355
+ if (run.source === "onboarding_selected_directory") setWorkDiscoveryOnboardingState({
356
+ status: input.decision === "dismissed" ? "dismissed" : "completed",
357
+ activeRunId: input.decision === "dismissed" ? null : run.id
358
+ });
359
+ const feedbackText = correctedIntent ? `The user corrected the work discovery understanding: ${correctedIntent}` : input.decision === "confirmed" ? "The user confirmed the work discovery understanding." : "The user left work discovery without confirming the suggested understanding.";
360
+ await this.options.sessions.appendTranscriptContextEntry(run.sessionKey, {
361
+ text: feedbackText,
362
+ data: {
363
+ type: "work_discovery_recognition_feedback",
364
+ runId: run.id,
365
+ decision: input.decision,
366
+ ...correctedIntent ? { correctedIntent } : {}
367
+ }
368
+ });
369
+ this.activity.record({
370
+ type: `work_discovery.recognition_${input.decision}`,
371
+ primaryObject: {
372
+ kind: "project",
373
+ id: run.projectId
374
+ },
375
+ actor: {
376
+ kind: "user",
377
+ sessionKey: run.sessionKey
378
+ },
379
+ source: {
380
+ kind: "gateway_api",
381
+ runId: run.id
382
+ },
383
+ payload: {
384
+ decision: input.decision,
385
+ corrected: Boolean(correctedIntent)
386
+ },
387
+ scopes: [{
388
+ scopeKind: "project",
389
+ scopeId: run.projectId,
390
+ reason: "object_owner"
391
+ }, {
392
+ scopeKind: "session",
393
+ scopeId: run.sessionKey,
394
+ reason: "runtime_context"
395
+ }]
396
+ });
397
+ this.options.emit("work-discovery.recognition-feedback", {
398
+ runId: run.id,
399
+ projectId: run.projectId,
400
+ sessionKey: run.sessionKey,
401
+ decision: input.decision
402
+ });
403
+ return {
404
+ ...run,
405
+ feedback
406
+ };
407
+ }
349
408
  selectSuggestion(runId, suggestionId) {
350
409
  const run = getWorkDiscoveryRun(runId);
351
410
  const suggestion = run?.result?.suggestions.find((item) => item.id === suggestionId);
@@ -1 +1 @@
1
- {"version":3,"file":"service.js","names":[],"sources":["../../../src/work-discovery/service.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\n\nimport type { Config } from '../config/schema.js';\nimport { getAgentDefaultModelRef } from '../config/schema.js';\nimport { ActivityService } from '../activity/service.js';\nimport type { ProjectService } from '../projects/project-service.js';\nimport { buildSessionKey } from '../routing/session-key.js';\nimport { getDefaultAgentId } from '../routing/resolve-route.js';\nimport type { SessionIndex } from '../session/manager.js';\nimport type { SessionMetadataSeed } from '../storage/sqlite/index.js';\nimport { createLogger } from '../utils/logger.js';\n\nimport { analyzeWorkContext, workDiscoveryResultMarkdown } from './analyzer.js';\nimport {\n createWorkDiscoveryRun,\n getWorkDiscoveryOnboardingState,\n getWorkDiscoveryRun,\n getWorkDiscoveryRunByIdempotencyKey,\n setWorkDiscoveryOnboardingState,\n updateWorkDiscoveryRun,\n} from './repository.js';\nimport {\n probeWorkDiscoveryRoot,\n previewWorkDiscoveryRoot,\n summarizeWorkContextSnapshot,\n WORK_DISCOVERY_SCAN_POLICY_VERSION,\n} from './probe.js';\nimport type {\n WorkDiscoveryErrorCode,\n WorkDiscoveryOnboardingState,\n WorkDiscoveryRun,\n WorkDiscoverySource,\n} from './types.js';\n\nconst log = createLogger('WorkDiscovery');\n\nexport interface WorkDiscoveryServiceOptions {\n projects: ProjectService;\n sessions: SessionIndex;\n getConfig: () => Config;\n emit: (type: string, payload: unknown) => void;\n}\n\nfunction sessionMetadata(agentId: string, peerId: string): SessionMetadataSeed {\n return {\n sourceChannel: 'webchat',\n sourceChatId: ['default', 'direct', peerId].join(':'),\n sessionType: 'chat',\n hiddenFromSessionList: true,\n routing: {\n agentId,\n source: 'webchat',\n accountId: 'default',\n peerKind: 'direct',\n peerId,\n },\n customData: {\n workDiscovery: true,\n genericNewChatShell: false,\n },\n };\n}\n\nfunction errorCode(error: unknown): WorkDiscoveryErrorCode {\n if (error instanceof DOMException && error.name === 'AbortError') return 'canceled';\n const message = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();\n if (message.includes('api key') || message.includes('model') || message.includes('provider')) return 'model_unavailable';\n if (message.includes('directory') || message.includes('folder') || message.includes('enoent')) return 'folder_unavailable';\n if (message.includes('permission') || message.includes('eacces')) return 'folder_not_readable';\n if (message.includes('json') || message.includes('analysis result')) return 'analysis_invalid';\n if (message.includes('timeout')) return 'analysis_timeout';\n return 'internal_error';\n}\n\nexport class WorkDiscoveryService {\n private readonly abortControllers = new Map<string, AbortController>();\n private readonly activity = new ActivityService();\n\n constructor(private readonly options: WorkDiscoveryServiceOptions) {}\n\n isEnabled(): boolean {\n return this.options.getConfig().experimental?.workDiscoveryOnboarding === true;\n }\n\n getOnboardingState(): WorkDiscoveryOnboardingState {\n return getWorkDiscoveryOnboardingState();\n }\n\n dismissOnboarding(): WorkDiscoveryOnboardingState {\n return setWorkDiscoveryOnboardingState({ status: 'dismissed', activeRunId: null });\n }\n\n getRun(id: string): WorkDiscoveryRun | null {\n return getWorkDiscoveryRun(id);\n }\n\n async startRun(input: {\n rootPath: string;\n source: WorkDiscoverySource;\n idempotencyKey: string;\n }): Promise<WorkDiscoveryRun> {\n const existing = getWorkDiscoveryRunByIdempotencyKey(input.idempotencyKey);\n if (existing) return existing;\n const config = this.options.getConfig();\n const modelRef = getAgentDefaultModelRef(config);\n if (!modelRef) throw new Error('No default model configured');\n\n const preview = await previewWorkDiscoveryRoot(input.rootPath);\n const rootPath = preview.canonicalRootPath;\n const agentId = getDefaultAgentId(config);\n const match = this.options.projects.resolveOrCreateForWorkspacePath({\n workspacePath: rootPath,\n defaultAgentId: agentId,\n autoCreate: true,\n });\n if (!match) throw new Error('Selected folder cannot be used as a project workspace');\n const projectAgentId = match.project.defaultAgentId || agentId;\n const peerId = `work-discovery-${randomUUID()}`;\n const sessionKey = buildSessionKey({\n agentId: projectAgentId,\n source: 'webchat',\n accountId: 'default',\n peerKind: 'direct',\n peerId,\n });\n await this.options.sessions.saveMessages(sessionKey, [], {\n metadata: sessionMetadata(projectAgentId, peerId),\n });\n this.options.projects.attachSession(sessionKey, match.project.id);\n const now = Date.now();\n const run = createWorkDiscoveryRun({\n id: randomUUID(),\n idempotencyKey: input.idempotencyKey,\n source: input.source,\n status: 'queued',\n rootPath,\n projectId: match.project.id,\n sessionKey,\n agentId: projectAgentId,\n modelRef,\n scanPolicyVersion: WORK_DISCOVERY_SCAN_POLICY_VERSION,\n createdAt: now,\n });\n await this.options.sessions.appendTranscriptContextEntry(sessionKey, {\n text: 'Work discovery was explicitly requested for the selected folder.',\n data: {\n type: 'work_discovery',\n runId: run.id,\n rootPath,\n scanPolicyVersion: WORK_DISCOVERY_SCAN_POLICY_VERSION,\n source: input.source,\n },\n });\n if (input.source === 'onboarding_selected_directory') {\n setWorkDiscoveryOnboardingState({ status: 'in_progress', activeRunId: run.id });\n }\n this.activity.record({\n type: 'work_discovery.started',\n primaryObject: { kind: 'project', id: match.project.id, title: match.project.name },\n actor: { kind: 'system' },\n initiator: { kind: 'user', sessionKey },\n source: { kind: 'gateway_api', runId: run.id },\n visibility: 'audit',\n payload: { policyVersion: WORK_DISCOVERY_SCAN_POLICY_VERSION, source: input.source },\n scopes: [\n { scopeKind: 'project', scopeId: match.project.id, reason: 'object_owner' },\n { scopeKind: 'session', scopeId: sessionKey, reason: 'runtime_context' },\n ],\n });\n const controller = new AbortController();\n this.abortControllers.set(run.id, controller);\n void this.execute(run, controller.signal);\n return run;\n }\n\n private publish(run: WorkDiscoveryRun): void {\n const suffix = run.status === 'completed'\n ? 'completed'\n : run.status === 'failed'\n ? 'failed'\n : run.status === 'canceled'\n ? 'canceled'\n : 'progress';\n this.options.emit(`work-discovery.${suffix}`, {\n runId: run.id,\n projectId: run.projectId,\n sessionKey: run.sessionKey,\n status: run.status,\n stage: run.stage,\n ...(run.status === 'completed' ? { result: run.result } : {}),\n });\n }\n\n private async execute(run: WorkDiscoveryRun, signal: AbortSignal): Promise<void> {\n const startedAt = Date.now();\n try {\n let current = updateWorkDiscoveryRun(run.id, {\n status: 'probing',\n stage: 'folder_structure',\n startedAt,\n })!;\n this.publish(current);\n const snapshot = await probeWorkDiscoveryRoot(run.rootPath, signal);\n if (signal.aborted) throw new DOMException('Work discovery canceled', 'AbortError');\n current = updateWorkDiscoveryRun(run.id, {\n status: 'analyzing',\n stage: 'recent_progress',\n snapshot: summarizeWorkContextSnapshot(snapshot),\n })!;\n this.publish(current);\n const analysis = await analyzeWorkContext({ config: this.options.getConfig(), snapshot, signal });\n current = updateWorkDiscoveryRun(run.id, { status: 'analyzing', stage: 'next_steps' })!;\n this.publish(current);\n await this.options.sessions.appendTranscriptCustomMessageEntry(run.sessionKey, {\n customType: 'work-discovery-result',\n content: workDiscoveryResultMarkdown(analysis.result),\n display: true,\n details: { runId: run.id, result: analysis.result },\n });\n const project = this.options.projects.get(run.projectId);\n await this.options.sessions.updateSessionMetadata(run.sessionKey, {\n name: project ? `Continue work on ${project.name}` : 'Continue recent work',\n hiddenFromSessionList: false,\n });\n current = updateWorkDiscoveryRun(run.id, {\n status: 'completed',\n stage: 'next_steps',\n result: analysis.result,\n completedAt: Date.now(),\n })!;\n if (run.source === 'onboarding_selected_directory') {\n setWorkDiscoveryOnboardingState({ status: 'completed', activeRunId: run.id });\n }\n this.activity.record({\n type: 'work_discovery.completed',\n primaryObject: { kind: 'project', id: run.projectId, title: project?.name },\n actor: { kind: 'agent', agentId: run.agentId, sessionKey: run.sessionKey },\n initiator: { kind: 'user', sessionKey: run.sessionKey },\n source: { kind: 'system', runId: run.id },\n payload: {\n policyVersion: run.scanPolicyVersion,\n suggestionCount: analysis.result.suggestions.length,\n lowConfidence: analysis.result.lowConfidence === true,\n },\n scopes: [\n { scopeKind: 'project', scopeId: run.projectId, reason: 'object_owner' },\n { scopeKind: 'session', scopeId: run.sessionKey, reason: 'runtime_context' },\n ],\n });\n this.publish(current);\n this.options.emit('session.transcript_updated', { key: run.sessionKey });\n log.info({ runId: run.id, projectId: run.projectId, sessionKey: run.sessionKey, durationMs: Date.now() - startedAt }, 'Work discovery completed');\n } catch (error) {\n const canceled = signal.aborted || (error instanceof DOMException && error.name === 'AbortError');\n const code = canceled ? 'canceled' : errorCode(error);\n const message = canceled ? 'Analysis canceled' : error instanceof Error ? error.message : String(error);\n const current = updateWorkDiscoveryRun(run.id, {\n status: canceled ? 'canceled' : 'failed',\n errorCode: code,\n errorMessage: message,\n ...(canceled ? { canceledAt: Date.now() } : { completedAt: Date.now() }),\n });\n if (current) this.publish(current);\n this.activity.record({\n type: canceled ? 'work_discovery.canceled' : 'work_discovery.failed',\n primaryObject: { kind: 'project', id: run.projectId },\n actor: { kind: 'system' },\n initiator: { kind: 'user', sessionKey: run.sessionKey },\n source: { kind: 'system', runId: run.id },\n visibility: 'audit',\n payload: { errorCode: code },\n scopes: [\n { scopeKind: 'project', scopeId: run.projectId, reason: 'object_owner' },\n { scopeKind: 'session', scopeId: run.sessionKey, reason: 'runtime_context' },\n ],\n });\n log.warn({ err: error, runId: run.id, projectId: run.projectId, phase: 'analysis', errorCode: code }, `Work discovery failed: ${message}`);\n } finally {\n this.abortControllers.delete(run.id);\n }\n }\n\n cancelRun(id: string): WorkDiscoveryRun | null {\n const run = getWorkDiscoveryRun(id);\n if (!run) return null;\n if (run.status === 'completed' || run.status === 'failed' || run.status === 'canceled') return run;\n this.abortControllers.get(id)?.abort();\n return updateWorkDiscoveryRun(id, {\n status: 'canceled',\n errorCode: 'canceled',\n errorMessage: 'Analysis canceled',\n canceledAt: Date.now(),\n });\n }\n\n retryRun(id: string): WorkDiscoveryRun | null {\n const run = getWorkDiscoveryRun(id);\n if (!run) return null;\n if (run.status !== 'failed' && run.status !== 'canceled') return run;\n const queued = updateWorkDiscoveryRun(id, {\n status: 'queued',\n stage: 'folder_structure',\n errorCode: undefined,\n errorMessage: undefined,\n completedAt: undefined,\n canceledAt: undefined,\n });\n if (!queued) return null;\n const controller = new AbortController();\n this.abortControllers.set(id, controller);\n if (run.source === 'onboarding_selected_directory') {\n setWorkDiscoveryOnboardingState({ status: 'in_progress', activeRunId: id });\n }\n this.publish(queued);\n void this.execute(queued, controller.signal);\n return queued;\n }\n\n selectSuggestion(runId: string, suggestionId: string): WorkDiscoveryRun | null {\n const run = getWorkDiscoveryRun(runId);\n const suggestion = run?.result?.suggestions.find((item) => item.id === suggestionId);\n if (!run || !suggestion) return null;\n this.activity.record({\n type: 'work_discovery.suggestion_selected',\n primaryObject: { kind: 'project', id: run.projectId },\n actor: { kind: 'user', sessionKey: run.sessionKey },\n source: { kind: 'gateway_api', runId },\n payload: { suggestionId, title: suggestion.title },\n scopes: [\n { scopeKind: 'project', scopeId: run.projectId, reason: 'object_owner' },\n { scopeKind: 'session', scopeId: run.sessionKey, reason: 'runtime_context' },\n ],\n });\n return run;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"service.js","names":[],"sources":["../../../src/work-discovery/service.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\n\nimport type { Config } from '../config/schema.js';\nimport { getAgentDefaultModelRef } from '../config/schema.js';\nimport { ActivityService } from '../activity/service.js';\nimport type { ProjectService } from '../projects/project-service.js';\nimport { buildSessionKey } from '../routing/session-key.js';\nimport { getDefaultAgentId } from '../routing/resolve-route.js';\nimport type { SessionIndex } from '../session/manager.js';\nimport type { SessionMetadataSeed } from '../storage/sqlite/index.js';\nimport { createLogger } from '../utils/logger.js';\n\nimport { analyzeWorkContext, workDiscoveryResultMarkdown } from './analyzer.js';\nimport {\n createWorkDiscoveryRun,\n getWorkDiscoveryOnboardingState,\n getWorkDiscoveryRun,\n getWorkDiscoveryRunByIdempotencyKey,\n setWorkDiscoveryFeedback,\n setWorkDiscoveryOnboardingState,\n updateWorkDiscoveryRun,\n} from './repository.js';\nimport {\n probeWorkDiscoveryRoot,\n previewWorkDiscoveryRoot,\n summarizeWorkContextSnapshot,\n WORK_DISCOVERY_SCAN_POLICY_VERSION,\n} from './probe.js';\nimport type {\n WorkDiscoveryErrorCode,\n WorkDiscoveryOnboardingState,\n WorkDiscoveryRecognitionDecision,\n WorkDiscoveryRun,\n WorkDiscoverySource,\n} from './types.js';\n\nconst log = createLogger('WorkDiscovery');\n\nexport interface WorkDiscoveryServiceOptions {\n projects: ProjectService;\n sessions: SessionIndex;\n getConfig: () => Config;\n emit: (type: string, payload: unknown) => void;\n}\n\nfunction sessionMetadata(agentId: string, peerId: string): SessionMetadataSeed {\n return {\n sourceChannel: 'webchat',\n sourceChatId: ['default', 'direct', peerId].join(':'),\n sessionType: 'chat',\n hiddenFromSessionList: true,\n routing: {\n agentId,\n source: 'webchat',\n accountId: 'default',\n peerKind: 'direct',\n peerId,\n },\n customData: {\n workDiscovery: true,\n genericNewChatShell: false,\n },\n };\n}\n\nfunction errorCode(error: unknown): WorkDiscoveryErrorCode {\n if (error instanceof DOMException && error.name === 'AbortError') return 'canceled';\n const message = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();\n if (message.includes('api key') || message.includes('model') || message.includes('provider')) return 'model_unavailable';\n if (message.includes('directory') || message.includes('folder') || message.includes('enoent')) return 'folder_unavailable';\n if (message.includes('permission') || message.includes('eacces')) return 'folder_not_readable';\n if (message.includes('json') || message.includes('analysis result')) return 'analysis_invalid';\n if (message.includes('timeout')) return 'analysis_timeout';\n return 'internal_error';\n}\n\nexport class WorkDiscoveryService {\n private readonly abortControllers = new Map<string, AbortController>();\n private readonly activity = new ActivityService();\n\n constructor(private readonly options: WorkDiscoveryServiceOptions) {}\n\n isEnabled(): boolean {\n return this.options.getConfig().experimental?.workDiscoveryOnboarding === true;\n }\n\n getOnboardingState(): WorkDiscoveryOnboardingState {\n return getWorkDiscoveryOnboardingState();\n }\n\n dismissOnboarding(): WorkDiscoveryOnboardingState {\n return setWorkDiscoveryOnboardingState({ status: 'dismissed', activeRunId: null });\n }\n\n getRun(id: string): WorkDiscoveryRun | null {\n return getWorkDiscoveryRun(id);\n }\n\n async startRun(input: {\n rootPath: string;\n source: WorkDiscoverySource;\n idempotencyKey: string;\n }): Promise<WorkDiscoveryRun> {\n const existing = getWorkDiscoveryRunByIdempotencyKey(input.idempotencyKey);\n if (existing) return existing;\n const config = this.options.getConfig();\n const modelRef = getAgentDefaultModelRef(config);\n if (!modelRef) throw new Error('No default model configured');\n\n const preview = await previewWorkDiscoveryRoot(input.rootPath);\n const rootPath = preview.canonicalRootPath;\n const agentId = getDefaultAgentId(config);\n const match = this.options.projects.resolveOrCreateForWorkspacePath({\n workspacePath: rootPath,\n defaultAgentId: agentId,\n autoCreate: true,\n });\n if (!match) throw new Error('Selected folder cannot be used as a project workspace');\n const projectAgentId = match.project.defaultAgentId || agentId;\n const peerId = `work-discovery-${randomUUID()}`;\n const sessionKey = buildSessionKey({\n agentId: projectAgentId,\n source: 'webchat',\n accountId: 'default',\n peerKind: 'direct',\n peerId,\n });\n await this.options.sessions.saveMessages(sessionKey, [], {\n metadata: sessionMetadata(projectAgentId, peerId),\n });\n this.options.projects.attachSession(sessionKey, match.project.id);\n const now = Date.now();\n const run = createWorkDiscoveryRun({\n id: randomUUID(),\n idempotencyKey: input.idempotencyKey,\n source: input.source,\n status: 'queued',\n rootPath,\n projectId: match.project.id,\n sessionKey,\n agentId: projectAgentId,\n modelRef,\n scanPolicyVersion: WORK_DISCOVERY_SCAN_POLICY_VERSION,\n createdAt: now,\n });\n await this.options.sessions.appendTranscriptContextEntry(sessionKey, {\n text: 'Work discovery was explicitly requested for the selected folder.',\n data: {\n type: 'work_discovery',\n runId: run.id,\n rootPath,\n scanPolicyVersion: WORK_DISCOVERY_SCAN_POLICY_VERSION,\n source: input.source,\n },\n });\n if (input.source === 'onboarding_selected_directory') {\n setWorkDiscoveryOnboardingState({ status: 'in_progress', activeRunId: run.id });\n }\n this.activity.record({\n type: 'work_discovery.started',\n primaryObject: { kind: 'project', id: match.project.id, title: match.project.name },\n actor: { kind: 'system' },\n initiator: { kind: 'user', sessionKey },\n source: { kind: 'gateway_api', runId: run.id },\n visibility: 'audit',\n payload: { policyVersion: WORK_DISCOVERY_SCAN_POLICY_VERSION, source: input.source },\n scopes: [\n { scopeKind: 'project', scopeId: match.project.id, reason: 'object_owner' },\n { scopeKind: 'session', scopeId: sessionKey, reason: 'runtime_context' },\n ],\n });\n const controller = new AbortController();\n this.abortControllers.set(run.id, controller);\n void this.execute(run, controller.signal);\n return run;\n }\n\n private publish(run: WorkDiscoveryRun): void {\n const suffix = run.status === 'completed'\n ? 'completed'\n : run.status === 'failed'\n ? 'failed'\n : run.status === 'canceled'\n ? 'canceled'\n : 'progress';\n this.options.emit(`work-discovery.${suffix}`, {\n runId: run.id,\n projectId: run.projectId,\n sessionKey: run.sessionKey,\n status: run.status,\n stage: run.stage,\n ...(run.status === 'completed' ? { result: run.result } : {}),\n });\n }\n\n private async execute(run: WorkDiscoveryRun, signal: AbortSignal): Promise<void> {\n const startedAt = Date.now();\n try {\n let current = updateWorkDiscoveryRun(run.id, {\n status: 'probing',\n stage: 'folder_structure',\n startedAt,\n })!;\n this.publish(current);\n const snapshot = await probeWorkDiscoveryRoot(run.rootPath, signal);\n if (signal.aborted) throw new DOMException('Work discovery canceled', 'AbortError');\n current = updateWorkDiscoveryRun(run.id, {\n status: 'analyzing',\n stage: 'recent_progress',\n snapshot: summarizeWorkContextSnapshot(snapshot),\n })!;\n this.publish(current);\n const analysis = await analyzeWorkContext({ config: this.options.getConfig(), snapshot, signal });\n current = updateWorkDiscoveryRun(run.id, { status: 'analyzing', stage: 'next_steps' })!;\n this.publish(current);\n await this.options.sessions.appendTranscriptCustomMessageEntry(run.sessionKey, {\n customType: 'work-discovery-result',\n content: workDiscoveryResultMarkdown(analysis.result),\n display: true,\n details: { runId: run.id, result: analysis.result },\n });\n const project = this.options.projects.get(run.projectId);\n await this.options.sessions.updateSessionMetadata(run.sessionKey, {\n name: project ? `Continue work on ${project.name}` : 'Continue recent work',\n hiddenFromSessionList: false,\n });\n current = updateWorkDiscoveryRun(run.id, {\n status: 'completed',\n stage: 'next_steps',\n result: analysis.result,\n completedAt: Date.now(),\n })!;\n this.activity.record({\n type: 'work_discovery.completed',\n primaryObject: { kind: 'project', id: run.projectId, title: project?.name },\n actor: { kind: 'agent', agentId: run.agentId, sessionKey: run.sessionKey },\n initiator: { kind: 'user', sessionKey: run.sessionKey },\n source: { kind: 'system', runId: run.id },\n payload: {\n policyVersion: run.scanPolicyVersion,\n suggestionCount: analysis.result.suggestions.length,\n lowConfidence: analysis.result.lowConfidence === true,\n },\n scopes: [\n { scopeKind: 'project', scopeId: run.projectId, reason: 'object_owner' },\n { scopeKind: 'session', scopeId: run.sessionKey, reason: 'runtime_context' },\n ],\n });\n this.publish(current);\n this.options.emit('session.transcript_updated', { key: run.sessionKey });\n log.info({ runId: run.id, projectId: run.projectId, sessionKey: run.sessionKey, durationMs: Date.now() - startedAt }, 'Work discovery completed');\n } catch (error) {\n const canceled = signal.aborted || (error instanceof DOMException && error.name === 'AbortError');\n const code = canceled ? 'canceled' : errorCode(error);\n const message = canceled ? 'Analysis canceled' : error instanceof Error ? error.message : String(error);\n const current = updateWorkDiscoveryRun(run.id, {\n status: canceled ? 'canceled' : 'failed',\n errorCode: code,\n errorMessage: message,\n ...(canceled ? { canceledAt: Date.now() } : { completedAt: Date.now() }),\n });\n if (current) this.publish(current);\n this.activity.record({\n type: canceled ? 'work_discovery.canceled' : 'work_discovery.failed',\n primaryObject: { kind: 'project', id: run.projectId },\n actor: { kind: 'system' },\n initiator: { kind: 'user', sessionKey: run.sessionKey },\n source: { kind: 'system', runId: run.id },\n visibility: 'audit',\n payload: { errorCode: code },\n scopes: [\n { scopeKind: 'project', scopeId: run.projectId, reason: 'object_owner' },\n { scopeKind: 'session', scopeId: run.sessionKey, reason: 'runtime_context' },\n ],\n });\n log.warn({ err: error, runId: run.id, projectId: run.projectId, phase: 'analysis', errorCode: code }, `Work discovery failed: ${message}`);\n } finally {\n this.abortControllers.delete(run.id);\n }\n }\n\n cancelRun(id: string): WorkDiscoveryRun | null {\n const run = getWorkDiscoveryRun(id);\n if (!run) return null;\n if (run.status === 'completed' || run.status === 'failed' || run.status === 'canceled') return run;\n this.abortControllers.get(id)?.abort();\n return updateWorkDiscoveryRun(id, {\n status: 'canceled',\n errorCode: 'canceled',\n errorMessage: 'Analysis canceled',\n canceledAt: Date.now(),\n });\n }\n\n retryRun(id: string): WorkDiscoveryRun | null {\n const run = getWorkDiscoveryRun(id);\n if (!run) return null;\n if (run.status !== 'failed' && run.status !== 'canceled') return run;\n const queued = updateWorkDiscoveryRun(id, {\n status: 'queued',\n stage: 'folder_structure',\n errorCode: undefined,\n errorMessage: undefined,\n completedAt: undefined,\n canceledAt: undefined,\n });\n if (!queued) return null;\n const controller = new AbortController();\n this.abortControllers.set(id, controller);\n if (run.source === 'onboarding_selected_directory') {\n setWorkDiscoveryOnboardingState({ status: 'in_progress', activeRunId: id });\n }\n this.publish(queued);\n void this.execute(queued, controller.signal);\n return queued;\n }\n\n async submitRecognitionFeedback(input: {\n runId: string;\n decision: WorkDiscoveryRecognitionDecision;\n correctedIntent?: string;\n }): Promise<WorkDiscoveryRun | null> {\n const run = getWorkDiscoveryRun(input.runId);\n if (!run || run.status !== 'completed' || !run.result) return null;\n const correctedIntent = input.correctedIntent?.trim().slice(0, 2_000);\n if ((input.decision === 'corrected' || input.decision === 'different_goal') && !correctedIntent) {\n throw new Error('A corrected intent is required for this decision');\n }\n const feedback = setWorkDiscoveryFeedback({\n runId: run.id,\n recognitionDecision: input.decision,\n ...(correctedIntent ? { correctedIntent } : {}),\n });\n if (run.source === 'onboarding_selected_directory') {\n setWorkDiscoveryOnboardingState({\n status: input.decision === 'dismissed' ? 'dismissed' : 'completed',\n activeRunId: input.decision === 'dismissed' ? null : run.id,\n });\n }\n const feedbackText = correctedIntent\n ? `The user corrected the work discovery understanding: ${correctedIntent}`\n : input.decision === 'confirmed'\n ? 'The user confirmed the work discovery understanding.'\n : 'The user left work discovery without confirming the suggested understanding.';\n await this.options.sessions.appendTranscriptContextEntry(run.sessionKey, {\n text: feedbackText,\n data: {\n type: 'work_discovery_recognition_feedback',\n runId: run.id,\n decision: input.decision,\n ...(correctedIntent ? { correctedIntent } : {}),\n },\n });\n this.activity.record({\n type: `work_discovery.recognition_${input.decision}`,\n primaryObject: { kind: 'project', id: run.projectId },\n actor: { kind: 'user', sessionKey: run.sessionKey },\n source: { kind: 'gateway_api', runId: run.id },\n payload: {\n decision: input.decision,\n corrected: Boolean(correctedIntent),\n },\n scopes: [\n { scopeKind: 'project', scopeId: run.projectId, reason: 'object_owner' },\n { scopeKind: 'session', scopeId: run.sessionKey, reason: 'runtime_context' },\n ],\n });\n this.options.emit('work-discovery.recognition-feedback', {\n runId: run.id,\n projectId: run.projectId,\n sessionKey: run.sessionKey,\n decision: input.decision,\n });\n return { ...run, feedback };\n }\n\n selectSuggestion(runId: string, suggestionId: string): WorkDiscoveryRun | null {\n const run = getWorkDiscoveryRun(runId);\n const suggestion = run?.result?.suggestions.find((item) => item.id === suggestionId);\n if (!run || !suggestion) return null;\n this.activity.record({\n type: 'work_discovery.suggestion_selected',\n primaryObject: { kind: 'project', id: run.projectId },\n actor: { kind: 'user', sessionKey: run.sessionKey },\n source: { kind: 'gateway_api', runId },\n payload: { suggestionId, title: suggestion.title },\n scopes: [\n { scopeKind: 'project', scopeId: run.projectId, reason: 'object_owner' },\n { scopeKind: 'session', scopeId: run.sessionKey, reason: 'runtime_context' },\n ],\n });\n return run;\n }\n}\n"],"mappings":"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"}
@@ -16,20 +16,34 @@ export interface WorkDiscoveryEvidence {
16
16
  }
17
17
  export interface WorkDiscoverySuggestion {
18
18
  id: string;
19
+ actionType: 'summarize_recent_work' | 'inspect_related_tests' | 'plan_next_step';
19
20
  title: string;
20
21
  rationale: string;
21
22
  evidence: WorkDiscoveryEvidence[];
22
23
  actionPrompt: string;
23
24
  confidence: 'high' | 'medium' | 'low';
25
+ expectedOutcome: string;
26
+ estimatedMinutes: number;
27
+ risk: 'analysis' | 'command' | 'file_write';
28
+ verification: string[];
24
29
  }
25
30
  export interface WorkDiscoveryResult {
26
31
  projectSummary: string;
27
32
  currentState: string;
28
33
  uncertainties: string[];
29
34
  suggestions: WorkDiscoverySuggestion[];
35
+ primarySuggestionId?: string;
30
36
  lowConfidence?: boolean;
31
37
  contextQuestion?: string;
32
38
  }
39
+ export type WorkDiscoveryRecognitionDecision = 'confirmed' | 'corrected' | 'different_goal' | 'dismissed';
40
+ export interface WorkDiscoveryFeedback {
41
+ runId: string;
42
+ recognitionDecision: WorkDiscoveryRecognitionDecision;
43
+ correctedIntent?: string;
44
+ createdAt: number;
45
+ updatedAt: number;
46
+ }
33
47
  export interface WorkContextDocument {
34
48
  relativePath: string;
35
49
  modifiedAt?: number;
@@ -93,6 +107,7 @@ export interface WorkDiscoveryRun {
93
107
  scanPolicyVersion: number;
94
108
  snapshot?: WorkContextSnapshotSummary;
95
109
  result?: WorkDiscoveryResult;
110
+ feedback?: WorkDiscoveryFeedback;
96
111
  errorCode?: WorkDiscoveryErrorCode;
97
112
  errorMessage?: string;
98
113
  createdAt: number;
@@ -108,6 +123,12 @@ export interface WorkDiscoveryPreview {
108
123
  projectKind: 'coding' | 'general' | 'unknown';
109
124
  projectKindConfidence: number;
110
125
  markerReasons: string[];
126
+ fingerprint: {
127
+ branch?: string;
128
+ changedFileCount: number;
129
+ recentAreas: string[];
130
+ generatedAt: number;
131
+ };
111
132
  provider: string;
112
133
  remoteModel: boolean;
113
134
  policyVersion: number;
@@ -4,8 +4,8 @@ import { publishAutomationProductEvent } from "../../automations/product-events.
4
4
  import { GoalService } from "../../goals/goal-service.js";
5
5
  import "../../goals/index.js";
6
6
  import { DelegateSubagentRunner } from "../../agent/workflow/subagent-runner.js";
7
- import { resolveModelRef } from "../../config/agent-typed-models.js";
8
7
  import { preflightWorkflowConnectors } from "../../connectors/workflow-preflight.js";
8
+ import { resolveModelRef } from "../../config/agent-typed-models.js";
9
9
  import { CatalogWorkflowDefinitionRegistry } from "../registry/catalog-workflow-definition-registry.js";
10
10
  import { validateWorkflowJsonSchema } from "../domain/schema-validation.js";
11
11
  import { isTerminalWorkflowRunStatus } from "../domain/run.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xopcai/xopc",
3
- "version": "0.0.147",
3
+ "version": "0.0.149",
4
4
  "description": "Local-first AI system that remembers context, coordinates AI, and sustains long-term progress.",
5
5
  "type": "module",
6
6
  "main": "dist/src/index.js",