@askexenow/exe-os 0.9.203 → 0.9.205

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 (222) hide show
  1. package/deploy/compose/init-db.sql +162 -0
  2. package/deploy/stack-manifests/v0.9.json +45 -1
  3. package/dist/{active-agent-EFWC3HGP.js → active-agent-4RUP4VYK.js} +2 -2
  4. package/dist/{active-agent-NXX5Z3LX.js → active-agent-VEML3XMS.js} +2 -2
  5. package/dist/{agentic-ontology-JS6DPKQB.js → agentic-ontology-FNJAKZF5.js} +1 -1
  6. package/dist/{backfill-metadata-PTACRLYK.js → backfill-metadata-BT4RLVMZ.js} +3 -3
  7. package/dist/{behaviors-WDIKLZA3.js → behaviors-LCPNHP5Q.js} +2 -2
  8. package/dist/bin/agentic-ontology-backfill.js +4 -4
  9. package/dist/bin/agentic-reflection-backfill.js +5 -5
  10. package/dist/bin/agentic-semantic-label.js +4 -4
  11. package/dist/bin/backfill-conversations.js +3 -3
  12. package/dist/bin/backfill-responses.js +3 -3
  13. package/dist/bin/backfill-vectors.js +4 -4
  14. package/dist/bin/bulk-sync-postgres.js +5 -5
  15. package/dist/bin/cleanup-stale-review-tasks.js +8 -8
  16. package/dist/bin/cli.js +12 -12
  17. package/dist/bin/exe-agent-config.js +1 -1
  18. package/dist/bin/exe-agent.js +3 -3
  19. package/dist/bin/exe-assign.js +4 -4
  20. package/dist/bin/exe-boot.js +15 -15
  21. package/dist/bin/exe-call.js +3 -3
  22. package/dist/bin/exe-cloud.js +3 -3
  23. package/dist/bin/exe-dispatch.js +8 -8
  24. package/dist/bin/exe-doctor.js +1 -1
  25. package/dist/bin/exe-export-behaviors.js +6 -6
  26. package/dist/bin/exe-forget.js +5 -5
  27. package/dist/bin/exe-gateway.js +5 -5
  28. package/dist/bin/exe-heartbeat.js +8 -8
  29. package/dist/bin/exe-kill.js +11 -11
  30. package/dist/bin/exe-launch-agent.js +9 -9
  31. package/dist/bin/exe-new-employee.js +5 -5
  32. package/dist/bin/exe-pending-messages.js +9 -9
  33. package/dist/bin/exe-pending-notifications.js +8 -8
  34. package/dist/bin/exe-pending-reviews.js +8 -8
  35. package/dist/bin/exe-rename.js +3 -3
  36. package/dist/bin/exe-review.js +10 -10
  37. package/dist/bin/exe-search.js +4 -4
  38. package/dist/bin/exe-session-cleanup.js +13 -13
  39. package/dist/bin/exe-settings.js +3 -3
  40. package/dist/bin/exe-start-codex.js +10 -10
  41. package/dist/bin/exe-start-opencode.js +7 -7
  42. package/dist/bin/exe-status.js +9 -9
  43. package/dist/bin/exe-team.js +2 -2
  44. package/dist/bin/git-sweep.js +8 -8
  45. package/dist/bin/graph-backfill.js +3 -3
  46. package/dist/bin/graph-export.js +4 -4
  47. package/dist/bin/import-history.js +4 -4
  48. package/dist/bin/install.js +5 -5
  49. package/dist/bin/intercom-check.js +4 -4
  50. package/dist/bin/orchestration-metrics.js +4 -3
  51. package/dist/bin/postgres-agentic-reflection-backfill.js +2 -2
  52. package/dist/bin/postgres-agentic-semantic-backfill.js +1 -1
  53. package/dist/bin/scan-tasks.js +8 -8
  54. package/dist/bin/setup.js +1 -1
  55. package/dist/bin/shard-migrate.js +3 -3
  56. package/dist/{capacity-monitor-TGSIL7W4.js → capacity-monitor-EPHJTSKF.js} +9 -9
  57. package/dist/{catchup-brief-ABL4NSAE.js → catchup-brief-HVITENL5.js} +10 -10
  58. package/dist/{chunk-25K6KNZ5.js → chunk-22GPB6FO.js} +58 -6
  59. package/dist/chunk-2THPPZF2.js +81 -0
  60. package/dist/{chunk-K3O4MWMS.js → chunk-3MHKTBHZ.js} +1 -0
  61. package/dist/{chunk-UY6RQK5F.js → chunk-3N4ZEPZI.js} +1 -1
  62. package/dist/{chunk-SN6Z3CBF.js → chunk-3PDZQICZ.js} +1 -1
  63. package/dist/{chunk-CGM6BIK3.js → chunk-3S3ERUUH.js} +1 -1
  64. package/dist/{chunk-5EW4GKGZ.js → chunk-434A3L6O.js} +1 -1
  65. package/dist/{chunk-BQ4U2SYC.js → chunk-4FNJPA5O.js} +1 -1
  66. package/dist/{chunk-O5RJH6GU.js → chunk-4RJD3GXL.js} +1 -1
  67. package/dist/{chunk-3K2BCD5Y.js → chunk-5HTEMMOJ.js} +21 -4
  68. package/dist/{chunk-SZDSQ3AW.js → chunk-6JORPKMR.js} +3 -3
  69. package/dist/{chunk-DAFEIA2M.js → chunk-6MOFCQFH.js} +124 -65
  70. package/dist/{chunk-K62M5FIM.js → chunk-6VVSXO3M.js} +4 -4
  71. package/dist/{chunk-SPMVTZ7Z.js → chunk-723DSUG3.js} +3 -3
  72. package/dist/{chunk-5POKNLFV.js → chunk-7DTEJAIC.js} +2 -2
  73. package/dist/{chunk-E66FQ7IN.js → chunk-7HIWJLXK.js} +1 -1
  74. package/dist/{chunk-IGRQE5Y5.js → chunk-BGLXECHT.js} +2 -2
  75. package/dist/{chunk-BYHFW5CT.js → chunk-COOX4FXM.js} +1 -1
  76. package/dist/{chunk-GZLGB72W.js → chunk-CXBLK3ZA.js} +1 -1
  77. package/dist/{chunk-MJNI3S5H.js → chunk-EDQ4GH2D.js} +2 -2
  78. package/dist/{chunk-GPK2WJXA.js → chunk-EF57I73W.js} +1 -1
  79. package/dist/{chunk-HXYL4QX6.js → chunk-EUMLEWXO.js} +2 -2
  80. package/dist/{chunk-RJCZY4BY.js → chunk-FMKD6N33.js} +86 -10
  81. package/dist/{chunk-RN6XYY7U.js → chunk-FYJCMGTD.js} +71 -0
  82. package/dist/{chunk-USQF2GGF.js → chunk-H7U7FPQF.js} +82 -6
  83. package/dist/{chunk-FMI7SCZJ.js → chunk-HLYNKMAG.js} +1 -1
  84. package/dist/{chunk-BGOQFJ2H.js → chunk-HMGIQNME.js} +3 -3
  85. package/dist/chunk-IR4L6K5L.js +147 -0
  86. package/dist/chunk-ITYWLFYC.js +609 -0
  87. package/dist/{chunk-FFECDA24.js → chunk-JUB74GFQ.js} +2 -2
  88. package/dist/{chunk-D4B6SVEC.js → chunk-MKOW4EXB.js} +1 -1
  89. package/dist/{chunk-XWM23YJO.js → chunk-MQX47XMP.js} +2 -2
  90. package/dist/{chunk-IXFE5R6T.js → chunk-MZ2BL3JP.js} +2 -2
  91. package/dist/{chunk-WGMHQVGX.js → chunk-O2JGR7EU.js} +83 -6
  92. package/dist/{chunk-BV3L2S54.js → chunk-P2ZIM765.js} +1 -1
  93. package/dist/{chunk-DEGQESR7.js → chunk-PHCK4KQW.js} +3 -3
  94. package/dist/{chunk-Y2W54BET.js → chunk-PXDR3UTB.js} +8 -8
  95. package/dist/{chunk-ETHQJXO3.js → chunk-PYLNOBNW.js} +1 -1
  96. package/dist/{chunk-HXNTQP4B.js → chunk-RN6DEMC7.js} +1 -1
  97. package/dist/{chunk-3WMXVABU.js → chunk-S4AXKNPT.js} +1 -1
  98. package/dist/{chunk-X2BTDYHZ.js → chunk-SEAFDIKF.js} +13 -1
  99. package/dist/{chunk-QLKRYO7O.js → chunk-TCBUBBTL.js} +8 -8
  100. package/dist/{chunk-ZQMOF6E7.js → chunk-THFFC43M.js} +1 -1
  101. package/dist/{chunk-XXHDXAZV.js → chunk-VG3VVFF5.js} +1 -1
  102. package/dist/{chunk-P4JQGT4Q.js → chunk-VH6WU3GK.js} +2 -2
  103. package/dist/{chunk-AH6MV6CC.js → chunk-W4BFBCZA.js} +3 -3
  104. package/dist/{chunk-PVVA5SY5.js → chunk-XSOKVFQ6.js} +14 -3
  105. package/dist/{chunk-JUTI2DXY.js → chunk-YCL4RHFY.js} +1 -1
  106. package/dist/{chunk-UXD5KOND.js → chunk-YGUWCJN2.js} +2 -2
  107. package/dist/{chunk-AUPGTF7I.js → chunk-YOXXZAOR.js} +2 -2
  108. package/dist/{chunk-GPNUPCTZ.js → chunk-YXHY5F34.js} +2 -2
  109. package/dist/{chunk-6FJ2TJYB.js → chunk-ZDK3M72Z.js} +1 -1
  110. package/dist/{chunk-6H6Q4MJC.js → chunk-ZIBSTUGU.js} +1 -1
  111. package/dist/{chunk-LIHR5DQF.js → chunk-ZXZJLVGD.js} +1 -1
  112. package/dist/{co-occurrence-A2SJL5AH.js → co-occurrence-TJRQ5V7W.js} +1 -1
  113. package/dist/{crdt-sync-SGAW6DJQ.js → crdt-sync-RI7XPXTI.js} +1 -1
  114. package/dist/{crm-webhook-FBENY6FW.js → crm-webhook-AVG4F2NB.js} +2 -2
  115. package/dist/{cto-delegation-gate-A53RLTJZ.js → cto-delegation-gate-ZTNJLSPO.js} +7 -7
  116. package/dist/{daemon-orchestration-3U3XXFG7.js → daemon-orchestration-AKICH7C5.js} +10 -10
  117. package/dist/{exe-drift-3OUOHWAI.js → exe-drift-BPVS4MOJ.js} +2 -2
  118. package/dist/{exe-export-AFXTJ63F.js → exe-export-LL4CXNLT.js} +4 -4
  119. package/dist/{exe-import-QRZBTBBG.js → exe-import-JNKAAJ5T.js} +4 -4
  120. package/dist/{exe-key-BXN7WPOX.js → exe-key-C5UQ77AT.js} +1 -1
  121. package/dist/{fast-db-init-YVJD4JT2.js → fast-db-init-UNK4BEEO.js} +1 -1
  122. package/dist/gateway/index.js +6 -6
  123. package/dist/{git-staleness-DBFCOX65.js → git-staleness-WTJFIPHH.js} +1 -1
  124. package/dist/{git-task-sweep-O5CH3MKA.js → git-task-sweep-PT3RIE6W.js} +8 -8
  125. package/dist/{global-procedures-SL2TTCED.js → global-procedures-ZPN7KZH5.js} +2 -2
  126. package/dist/{graph-auto-extract-JGNJ3BB7.js → graph-auto-extract-4M4J42NA.js} +1 -1
  127. package/dist/hooks/bug-report-worker.js +9 -9
  128. package/dist/hooks/codex-stop-task-finalizer.js +9 -9
  129. package/dist/hooks/commit-complete.js +10 -10
  130. package/dist/hooks/error-recall.js +5 -5
  131. package/dist/hooks/exe-heartbeat-hook.js +2 -2
  132. package/dist/hooks/ingest.js +5 -5
  133. package/dist/hooks/instructions-loaded.js +3 -3
  134. package/dist/hooks/manifest.json +19 -19
  135. package/dist/hooks/notification.js +3 -3
  136. package/dist/hooks/post-compact.js +9 -9
  137. package/dist/hooks/post-tool-combined.js +4 -4
  138. package/dist/hooks/pre-compact.js +10 -10
  139. package/dist/hooks/pre-tool-use.js +13 -13
  140. package/dist/hooks/prompt-submit.js +29 -18
  141. package/dist/hooks/session-end.js +13 -13
  142. package/dist/hooks/session-start.js +30 -7
  143. package/dist/hooks/stop.js +13 -13
  144. package/dist/hooks/subagent-stop.js +9 -9
  145. package/dist/hooks/summary-worker.js +13 -13
  146. package/dist/index.js +15 -15
  147. package/dist/{installer-TAPAEWRV.js → installer-3DE2IDUZ.js} +4 -4
  148. package/dist/{installer-5FI6CX62.js → installer-IYELX3KG.js} +4 -4
  149. package/dist/{installer-HNOVAUW6.js → installer-SCWIMZT7.js} +4 -4
  150. package/dist/lib/cloud-sync.js +3 -3
  151. package/dist/lib/consolidation.js +4 -4
  152. package/dist/lib/database.js +1 -1
  153. package/dist/lib/db.js +1 -1
  154. package/dist/lib/employee-templates.js +3 -3
  155. package/dist/lib/employees.js +1 -1
  156. package/dist/lib/exe-daemon.js +102 -27
  157. package/dist/lib/hybrid-search.js +4 -4
  158. package/dist/lib/identity.js +1 -1
  159. package/dist/lib/messaging.js +8 -8
  160. package/dist/lib/reminders.js +2 -2
  161. package/dist/lib/schedules.js +4 -4
  162. package/dist/lib/session-registry.js +9 -1
  163. package/dist/lib/skill-learning.js +3 -3
  164. package/dist/lib/store.js +3 -3
  165. package/dist/lib/task-router.js +2 -2
  166. package/dist/lib/tasks.js +8 -8
  167. package/dist/lib/tmux-routing.js +7 -7
  168. package/dist/lib/token-spend.js +2 -2
  169. package/dist/mcp/register-tools.js +46 -46
  170. package/dist/mcp/server.js +47 -47
  171. package/dist/mcp/tools/complete-reminder.js +3 -3
  172. package/dist/mcp/tools/create-reminder.js +3 -3
  173. package/dist/mcp/tools/create-task.js +10 -10
  174. package/dist/mcp/tools/deactivate-behavior.js +4 -4
  175. package/dist/mcp/tools/list-reminders.js +3 -3
  176. package/dist/mcp/tools/list-tasks.js +10 -10
  177. package/dist/mcp/tools/send-message.js +10 -10
  178. package/dist/mcp/tools/update-task.js +9 -9
  179. package/dist/{mcp-http-config-6UAI2FAV.js → mcp-http-config-EWIT2DEN.js} +2 -2
  180. package/dist/{memory-cards-PVPUQ7MX.js → memory-cards-W4VTTFYM.js} +1 -1
  181. package/dist/{memory-graph-extractor-RP7ZXDTE.js → memory-graph-extractor-A3HRDXY6.js} +2 -2
  182. package/dist/{memory-poisoning-defense-DDBUHHGB.js → memory-poisoning-defense-MH6QBXVT.js} +1 -1
  183. package/dist/{memory-reflection-HLR26FXG.js → memory-reflection-RUCTWMQ5.js} +1 -1
  184. package/dist/{notifications-GHNYYITD.js → notifications-6GRSVIP6.js} +7 -7
  185. package/dist/{orchestration-events-2SVPXMK2.js → orchestration-events-MSJVUR4T.js} +6 -2
  186. package/dist/{orchestrator-3BDZ6OUC.js → orchestrator-QQKU4VM2.js} +9 -9
  187. package/dist/{plan-limits-QBNFIU45.js → plan-limits-NKUSDF3I.js} +2 -2
  188. package/dist/{projection-worker-B7HHFYEG.js → projection-worker-UEG6I5DV.js} +1 -1
  189. package/dist/{reranker-B3BOGR3A.js → reranker-JTDM3MV2.js} +1 -1
  190. package/dist/{review-polling-RUXU45AK.js → review-polling-MWBVXHG3.js} +8 -8
  191. package/dist/runtime/index.js +10 -10
  192. package/dist/{session-events-ICZPUGJN.js → session-events-FRB3UYJG.js} +8 -8
  193. package/dist/{session-kill-telemetry-OGFLFTYX.js → session-kill-telemetry-GPZUHBCT.js} +2 -2
  194. package/dist/{session-scope-UJI35BLD.js → session-scope-7BZTNB2X.js} +7 -7
  195. package/dist/{setup-wizard-M6ZZ2VNM.js → setup-wizard-2INM4CSL.js} +1 -1
  196. package/dist/{skill-refinement-WZL35CMY.js → skill-refinement-7C7X42Z5.js} +1 -1
  197. package/dist/{task-enforcement-AYHKGR45.js → task-enforcement-VB5P45ZM.js} +7 -7
  198. package/dist/{task-scope-EYAOY6YX.js → task-scope-ULC62J6G.js} +7 -7
  199. package/dist/{tasks-crud-K3UGPCPT.js → tasks-crud-IBSTM6ID.js} +9 -7
  200. package/dist/{tasks-review-J6LTVVO5.js → tasks-review-P4MCKBDT.js} +7 -7
  201. package/dist/telemetry-upload-Y3CPHI7M.js +214 -0
  202. package/dist/{token-budget-XU2QZEWX.js → token-budget-CO74EBTY.js} +1 -1
  203. package/dist/{tool-telemetry-OE57ERK7.js → tool-telemetry-5PX7A6TX.js} +3 -1
  204. package/dist/tui/App.js +15 -15
  205. package/dist/{tui-data-32OEC3DH.js → tui-data-YAT6A36V.js} +7 -7
  206. package/dist/{worker-gate-UIGQCEUE.js → worker-gate-27O2WEYU.js} +1 -1
  207. package/dist/{workflow-engine-IY7KO7DU.js → workflow-engine-4T36KCTQ.js} +2 -2
  208. package/dist/{worktree-VIXR2Z4J.js → worktree-QTCOX6RV.js} +7 -1
  209. package/package.json +1 -1
  210. package/release-notes.json +95 -95
  211. package/dist/chunk-64WZEXXA.js +0 -64
  212. package/dist/chunk-L63IOJ5N.js +0 -69
  213. package/dist/chunk-ZGQH3V2F.js +0 -254
  214. /package/dist/{chunk-3GFR2TX6.js → chunk-AF7A5IQ5.js} +0 -0
  215. /package/dist/{chunk-7SY5JOLR.js → chunk-ATKMIQXP.js} +0 -0
  216. /package/dist/{chunk-KCWIOR55.js → chunk-GONOOIFL.js} +0 -0
  217. /package/dist/{chunk-QQN7MHQG.js → chunk-ONCFSN5C.js} +0 -0
  218. /package/dist/{chunk-Q3MRTJLA.js → chunk-WVNWAV36.js} +0 -0
  219. /package/dist/{chunk-RH5NYRQH.js → chunk-XJQASQPO.js} +0 -0
  220. /package/dist/{chunk-LJA6RQ3L.js → chunk-YKCH4T4T.js} +0 -0
  221. /package/dist/{core-memory-VZ4YJVMA.js → core-memory-3QGHBERR.js} +0 -0
  222. /package/dist/{wiki-acl-NUUPHYWC.js → wiki-acl-WCBKBLGQ.js} +0 -0
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  vectorToBlob
3
- } from "./chunk-QLKRYO7O.js";
3
+ } from "./chunk-TCBUBBTL.js";
4
4
  import {
5
5
  extractKeywords,
6
6
  keywordsToString
7
7
  } from "./chunk-CHCA3ZM2.js";
8
8
  import {
9
9
  isCoordinatorName
10
- } from "./chunk-K3O4MWMS.js";
10
+ } from "./chunk-3MHKTBHZ.js";
11
11
 
12
12
  // src/lib/consolidation.ts
13
13
  import { randomUUID } from "crypto";
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  ensureWorktree
3
- } from "./chunk-RN6XYY7U.js";
3
+ } from "./chunk-FYJCMGTD.js";
4
4
  import {
5
5
  queueIntercom
6
6
  } from "./chunk-QI4IXJN7.js";
7
7
  import {
8
8
  registerSession
9
- } from "./chunk-64WZEXXA.js";
9
+ } from "./chunk-IR4L6K5L.js";
10
10
  import {
11
11
  getTransport
12
12
  } from "./chunk-5YDHBZCO.js";
@@ -15,7 +15,7 @@ import {
15
15
  } from "./chunk-GZIAQYGI.js";
16
16
  import {
17
17
  recordOrchestrationEventBestEffort
18
- } from "./chunk-ZGQH3V2F.js";
18
+ } from "./chunk-ITYWLFYC.js";
19
19
  import {
20
20
  DEFAULT_PROVIDER,
21
21
  PROVIDER_TABLE,
@@ -33,7 +33,7 @@ import {
33
33
  import {
34
34
  PlanLimitError,
35
35
  assertEmployeeLimitSync
36
- } from "./chunk-SN6Z3CBF.js";
36
+ } from "./chunk-3PDZQICZ.js";
37
37
  import {
38
38
  getSessionKey
39
39
  } from "./chunk-CVYC6DUW.js";
@@ -48,7 +48,7 @@ import {
48
48
  } from "./chunk-MP2AFCGL.js";
49
49
  import {
50
50
  ensureAgentSymlink
51
- } from "./chunk-HXNTQP4B.js";
51
+ } from "./chunk-RN6DEMC7.js";
52
52
  import {
53
53
  expandDualPrefixTools
54
54
  } from "./chunk-HYZV25LY.js";
@@ -60,7 +60,7 @@ import {
60
60
  isCoordinatorName,
61
61
  loadEmployees,
62
62
  loadEmployeesSync
63
- } from "./chunk-K3O4MWMS.js";
63
+ } from "./chunk-3MHKTBHZ.js";
64
64
  import {
65
65
  loadDeviceId
66
66
  } from "./chunk-72DJ42IE.js";
@@ -374,7 +374,7 @@ async function createTaskCore(input) {
374
374
  if (isCoordinatorSession) {
375
375
  earlySessionScope = resolved;
376
376
  } else {
377
- const { getSessionProject } = await import("./session-scope-UJI35BLD.js");
377
+ const { getSessionProject } = await import("./session-scope-7BZTNB2X.js");
378
378
  const sessionProject = getSessionProject(resolved);
379
379
  if (sessionProject && sessionProject !== input.projectName) {
380
380
  scopeMismatchWarning = `session/project mismatch: session "${resolved}" owns "${sessionProject}" but task targets "${input.projectName}". Routed to default scope.`;
@@ -823,7 +823,7 @@ async function updateTaskStatus(input) {
823
823
  } catch {
824
824
  }
825
825
  try {
826
- const { writeNotification: writeNotification2 } = await import("./notifications-GHNYYITD.js");
826
+ const { writeNotification: writeNotification2 } = await import("./notifications-6GRSVIP6.js");
827
827
  await writeNotification2({
828
828
  agentId: reviewer,
829
829
  agentRole: isCoordinatorName(reviewer) ? "COO" : "manager",
@@ -1146,13 +1146,36 @@ ${input.result}` : `\u26A0\uFE0F ${warning}`;
1146
1146
  projectName: eventProjectName
1147
1147
  });
1148
1148
  }
1149
+ if (input.status === "closed" && previousStatus === "needs_review") {
1150
+ recordOrchestrationEventBestEffort({
1151
+ eventType: "review.approved",
1152
+ source: "tasks-crud.updateTaskStatus",
1153
+ taskId,
1154
+ agentId: eventAgentId,
1155
+ reviewer: row.reviewer ? String(row.reviewer) : null,
1156
+ sessionScope: eventSessionScope,
1157
+ projectName: eventProjectName
1158
+ });
1159
+ }
1160
+ if (previousStatus === "needs_review" && (input.status === "open" || input.status === "in_progress")) {
1161
+ recordOrchestrationEventBestEffort({
1162
+ eventType: "review.rejected",
1163
+ source: "tasks-crud.updateTaskStatus",
1164
+ taskId,
1165
+ agentId: eventAgentId,
1166
+ reviewer: row.reviewer ? String(row.reviewer) : null,
1167
+ sessionScope: eventSessionScope,
1168
+ projectName: eventProjectName,
1169
+ result: input.status
1170
+ });
1171
+ }
1149
1172
  try {
1150
1173
  await client.execute("PRAGMA wal_checkpoint(PASSIVE)");
1151
1174
  } catch {
1152
1175
  }
1153
1176
  if (input.status === "done" || input.status === "needs_review") {
1154
1177
  try {
1155
- const { incrementSkillSuccess } = await import("./skill-refinement-WZL35CMY.js");
1178
+ const { incrementSkillSuccess } = await import("./skill-refinement-7C7X42Z5.js");
1156
1179
  await incrementSkillSuccess(
1157
1180
  String(row.assigned_to),
1158
1181
  row.project_name ? String(row.project_name) : null
@@ -1272,6 +1295,15 @@ async function cleanOrphanedTaskFiles() {
1272
1295
  if (ageMs > 24 * 60 * 60 * 1e3) {
1273
1296
  await unlink(fullPath);
1274
1297
  cleaned++;
1298
+ recordOrchestrationEventBestEffort({
1299
+ eventType: "task_file.orphan_cleaned",
1300
+ source: "tasks-crud.cleanOrphanedTaskFiles",
1301
+ severity: "warn",
1302
+ payload: {
1303
+ ageHours: Math.round(ageMs / 36e5),
1304
+ type: "task_file_missing_db"
1305
+ }
1306
+ });
1275
1307
  process.stderr.write(`[task-cleanup] Removed orphan task file: ${relativePath}
1276
1308
  `);
1277
1309
  }
@@ -1285,6 +1317,39 @@ async function cleanOrphanedTaskFiles() {
1285
1317
  await walkDir(EXE_TASKS_DIR);
1286
1318
  return cleaned;
1287
1319
  }
1320
+ async function checkTaskFileConsistency() {
1321
+ const client = getClient();
1322
+ let mismatches = 0;
1323
+ try {
1324
+ const result = await client.execute({
1325
+ sql: `SELECT id, title, task_file, status, assigned_to, session_scope, project_name
1326
+ FROM tasks
1327
+ WHERE status IN ('open', 'in_progress', 'needs_review', 'blocked')
1328
+ AND task_file IS NOT NULL AND task_file != ''`,
1329
+ args: []
1330
+ });
1331
+ for (const row of result.rows) {
1332
+ const taskFile = String(row.task_file ?? "");
1333
+ if (!taskFile) continue;
1334
+ const fullPath = taskFile.startsWith("/") ? taskFile : path2.join(os2.homedir(), ".exe-os", taskFile);
1335
+ if (!existsSync2(fullPath)) {
1336
+ mismatches++;
1337
+ recordOrchestrationEventBestEffort({
1338
+ eventType: "consistency.mismatch",
1339
+ source: "tasks-crud.checkTaskFileConsistency",
1340
+ severity: "warn",
1341
+ taskId: String(row.id),
1342
+ agentId: row.assigned_to ? String(row.assigned_to) : null,
1343
+ sessionScope: row.session_scope ? String(row.session_scope) : null,
1344
+ projectName: row.project_name ? String(row.project_name) : null,
1345
+ payload: { taskFile, status: String(row.status), type: "db_row_missing_file" }
1346
+ });
1347
+ }
1348
+ }
1349
+ } catch {
1350
+ }
1351
+ return mismatches;
1352
+ }
1288
1353
 
1289
1354
  // src/lib/notifications.ts
1290
1355
  import crypto2 from "crypto";
@@ -2239,7 +2304,7 @@ async function verifyPaneAtCapacity(sessionName) {
2239
2304
  reason: `capture-pane failed: ${err instanceof Error ? err.message : String(err)}`
2240
2305
  };
2241
2306
  }
2242
- const { isAtCapacity } = await import("./capacity-monitor-TGSIL7W4.js");
2307
+ const { isAtCapacity } = await import("./capacity-monitor-EPHJTSKF.js");
2243
2308
  if (!isAtCapacity(pane)) {
2244
2309
  return {
2245
2310
  atCapacity: false,
@@ -2576,6 +2641,16 @@ function ensureEmployee(employeeName, exeSession, projectDir, opts) {
2576
2641
  };
2577
2642
  }
2578
2643
  effectiveInstance = free === 0 ? void 0 : free;
2644
+ if (free !== null) {
2645
+ recordOrchestrationEventBestEffort({
2646
+ eventType: "worker.instance.selected",
2647
+ source: "tmux-routing.ensureEmployee",
2648
+ agentId: employeeName,
2649
+ sessionScope: exeSession,
2650
+ instanceId: String(free),
2651
+ payload: { maxInstances: opts.maxAutoInstances ?? 10 }
2652
+ });
2653
+ }
2579
2654
  }
2580
2655
  const sessionName = employeeSessionName(employeeName, exeSession, effectiveInstance);
2581
2656
  if (isEmployeeAlive(sessionName)) {
@@ -3023,6 +3098,7 @@ export {
3023
3098
  ensureArchitectureDoc,
3024
3099
  ensureGitignoreExe,
3025
3100
  cleanOrphanedTaskFiles,
3101
+ checkTaskFileConsistency,
3026
3102
  formatAge,
3027
3103
  isStale,
3028
3104
  countPendingReviews,
@@ -1,3 +1,7 @@
1
+ import {
2
+ recordOrchestrationEventBestEffort
3
+ } from "./chunk-ITYWLFYC.js";
4
+
1
5
  // src/lib/worktree.ts
2
6
  import { execSync } from "child_process";
3
7
  import { existsSync, readFileSync, appendFileSync, mkdirSync, realpathSync } from "fs";
@@ -48,6 +52,13 @@ function ensureWorktree(projectDir, employeeName, instance) {
48
52
  const wtPath = worktreePath(repoRoot, employeeName, instance);
49
53
  const branch = worktreeBranch(employeeName, instance);
50
54
  if (existsSync(path.join(wtPath, ".git"))) {
55
+ recordOrchestrationEventBestEffort({
56
+ eventType: "worktree.reused",
57
+ source: "worktree.ensureWorktree",
58
+ agentId: employeeName,
59
+ instanceId: instance != null ? String(instance) : null,
60
+ payload: { branch }
61
+ });
51
62
  return wtPath;
52
63
  }
53
64
  const worktreesDir = path.join(repoRoot, ".worktrees");
@@ -79,8 +90,24 @@ function ensureWorktree(projectDir, employeeName, instance) {
79
90
  stdio: ["pipe", "pipe", "pipe"]
80
91
  });
81
92
  }
93
+ recordOrchestrationEventBestEffort({
94
+ eventType: "worktree.created",
95
+ source: "worktree.ensureWorktree",
96
+ agentId: employeeName,
97
+ instanceId: instance != null ? String(instance) : null,
98
+ payload: { branch, reusedBranch: branchExists }
99
+ });
82
100
  return wtPath;
83
101
  } catch (err) {
102
+ recordOrchestrationEventBestEffort({
103
+ eventType: "worktree.failed",
104
+ source: "worktree.ensureWorktree",
105
+ severity: "warn",
106
+ agentId: employeeName,
107
+ instanceId: instance != null ? String(instance) : null,
108
+ errorCode: err instanceof Error ? err.name : "worktree_create_failed",
109
+ payload: { branch }
110
+ });
84
111
  process.stderr.write(
85
112
  `[worktree] Failed to create worktree for ${employeeName}: ${err instanceof Error ? err.message : String(err)}
86
113
  `
@@ -107,16 +134,44 @@ function cleanupWorktree(projectDir, employeeName, instance) {
107
134
  const wtPath = worktreePath(repoRoot, employeeName, instance);
108
135
  const branch = worktreeBranch(employeeName, instance);
109
136
  if (!existsSync(wtPath)) {
137
+ recordOrchestrationEventBestEffort({
138
+ eventType: "worktree.skipped",
139
+ source: "worktree.cleanupWorktree",
140
+ agentId: employeeName,
141
+ instanceId: instance != null ? String(instance) : null,
142
+ payload: { reason: "worktree does not exist", branch }
143
+ });
110
144
  return { cleaned: false, reason: "worktree does not exist" };
111
145
  }
112
146
  if (isWorktreeDirty(wtPath)) {
147
+ recordOrchestrationEventBestEffort({
148
+ eventType: "worktree.skipped",
149
+ source: "worktree.cleanupWorktree",
150
+ agentId: employeeName,
151
+ instanceId: instance != null ? String(instance) : null,
152
+ payload: { reason: "worktree has uncommitted changes", branch }
153
+ });
113
154
  return { cleaned: false, reason: "worktree has uncommitted changes" };
114
155
  }
115
156
  const mainBranch = detectMainBranch(repoRoot);
116
157
  if (!mainBranch) {
158
+ recordOrchestrationEventBestEffort({
159
+ eventType: "worktree.skipped",
160
+ source: "worktree.cleanupWorktree",
161
+ agentId: employeeName,
162
+ instanceId: instance != null ? String(instance) : null,
163
+ payload: { reason: "could not determine main branch", branch }
164
+ });
117
165
  return { cleaned: false, reason: "could not determine main branch" };
118
166
  }
119
167
  if (!isBranchMerged(repoRoot, branch, mainBranch)) {
168
+ recordOrchestrationEventBestEffort({
169
+ eventType: "worktree.skipped",
170
+ source: "worktree.cleanupWorktree",
171
+ agentId: employeeName,
172
+ instanceId: instance != null ? String(instance) : null,
173
+ payload: { reason: "branch not merged", branch, mainBranch }
174
+ });
120
175
  return { cleaned: false, reason: `branch ${branch} not merged into ${mainBranch}` };
121
176
  }
122
177
  try {
@@ -127,6 +182,15 @@ function cleanupWorktree(projectDir, employeeName, instance) {
127
182
  stdio: ["pipe", "pipe", "pipe"]
128
183
  });
129
184
  } catch (err) {
185
+ recordOrchestrationEventBestEffort({
186
+ eventType: "worktree.failed",
187
+ source: "worktree.cleanupWorktree",
188
+ severity: "warn",
189
+ agentId: employeeName,
190
+ instanceId: instance != null ? String(instance) : null,
191
+ errorCode: err instanceof Error ? err.name : "worktree_remove_failed",
192
+ payload: { branch, mainBranch }
193
+ });
130
194
  return {
131
195
  cleaned: false,
132
196
  reason: `git worktree remove failed: ${err instanceof Error ? err.message : String(err)}`
@@ -141,6 +205,13 @@ function cleanupWorktree(projectDir, employeeName, instance) {
141
205
  });
142
206
  } catch {
143
207
  }
208
+ recordOrchestrationEventBestEffort({
209
+ eventType: "worktree.pruned",
210
+ source: "worktree.cleanupWorktree",
211
+ agentId: employeeName,
212
+ instanceId: instance != null ? String(instance) : null,
213
+ payload: { branch, mainBranch }
214
+ });
144
215
  return { cleaned: true, reason: `worktree and branch ${branch} removed (merged into ${mainBranch})` };
145
216
  }
146
217
  function branchExistsLocally(repoRoot, branch) {
@@ -3,30 +3,44 @@ import {
3
3
  } from "./chunk-MLXJ5EZG.js";
4
4
  import {
5
5
  createTask
6
- } from "./chunk-WGMHQVGX.js";
6
+ } from "./chunk-O2JGR7EU.js";
7
7
  import {
8
8
  getActiveAgent
9
- } from "./chunk-UY6RQK5F.js";
9
+ } from "./chunk-3N4ZEPZI.js";
10
10
  import {
11
11
  ensureEmployee,
12
12
  logTaskDispatch,
13
13
  resolveExeSession
14
- } from "./chunk-RJCZY4BY.js";
14
+ } from "./chunk-FMKD6N33.js";
15
+ import {
16
+ recordOrchestrationEventBestEffort
17
+ } from "./chunk-ITYWLFYC.js";
18
+ import {
19
+ getAgentRuntime
20
+ } from "./chunk-6RX2XEET.js";
15
21
  import {
16
22
  getLicenseSync
17
- } from "./chunk-SN6Z3CBF.js";
23
+ } from "./chunk-3PDZQICZ.js";
18
24
  import {
19
25
  getAgentContext
20
26
  } from "./chunk-GJV3WDWM.js";
21
27
  import {
22
28
  isCoordinatorName
23
- } from "./chunk-K3O4MWMS.js";
29
+ } from "./chunk-3MHKTBHZ.js";
24
30
 
25
31
  // src/mcp/tools/create-task.ts
26
32
  import { z } from "zod";
27
33
  import { existsSync, writeFileSync, mkdirSync } from "fs";
28
34
  import { join } from "path";
29
35
  import { homedir } from "os";
36
+ function resolveDispatchRuntime(agentId, override) {
37
+ if (override) return override;
38
+ try {
39
+ return getAgentRuntime(agentId).runtime ?? null;
40
+ } catch {
41
+ return null;
42
+ }
43
+ }
30
44
  async function resolveProjectDir(projectName, callerSession) {
31
45
  const home = homedir();
32
46
  if (callerSession) {
@@ -197,19 +211,39 @@ function registerCreateTask(server) {
197
211
  session_scope: sessionScope,
198
212
  created_at: (/* @__PURE__ */ new Date()).toISOString()
199
213
  }), "utf-8");
214
+ const { recordOrchestrationEventBestEffort: recordOE } = await import("./orchestration-events-MSJVUR4T.js");
215
+ recordOE({
216
+ eventType: "signal.created",
217
+ source: "create-task.mcp",
218
+ taskId: task.id,
219
+ agentId: assigned_to,
220
+ sessionScope,
221
+ payload: { priority }
222
+ });
200
223
  } catch {
201
224
  }
202
225
  let dispatchStatus = "";
203
226
  if (task.status !== "blocked" && !process.env.VITEST) {
204
227
  try {
205
228
  const exeSession = callerRoot || resolveExeSession();
229
+ const dispatchRuntime = resolveDispatchRuntime(assigned_to, spawn_runtime ?? null);
230
+ recordOrchestrationEventBestEffort({
231
+ eventType: "dispatch.attempted",
232
+ source: "create-task.mcp",
233
+ taskId: task.id,
234
+ agentId: assigned_to,
235
+ sessionScope: task.sessionScope || callerRoot || exeSession || null,
236
+ projectName: project_name,
237
+ runtime: dispatchRuntime,
238
+ payload: { priority }
239
+ });
206
240
  if (exeSession) {
207
241
  const { shouldAutoInstance } = await import("./lib/employees.js");
208
242
  const autoOpts = shouldAutoInstance(assigned_to);
209
243
  let projectConflictOpts = {};
210
244
  try {
211
245
  const { getClient } = await import("./lib/database.js");
212
- const { sessionScopeFilter } = await import("./task-scope-EYAOY6YX.js");
246
+ const { sessionScopeFilter } = await import("./task-scope-ULC62J6G.js");
213
247
  const client = getClient();
214
248
  const scope = sessionScopeFilter();
215
249
  const existing = await client.execute({
@@ -243,6 +277,10 @@ function registerCreateTask(server) {
243
277
  dispatchStatus = `
244
278
  Dispatched: intercom sent to ${result.sessionName}`;
245
279
  break;
280
+ case "intercom_unprocessed":
281
+ dispatchStatus = `
282
+ Dispatched: intercom delivered to ${result.sessionName}`;
283
+ break;
246
284
  case "spawned":
247
285
  dispatchStatus = `
248
286
  Dispatched: spawned ${result.sessionName}`;
@@ -252,6 +290,20 @@ Dispatched: spawned ${result.sessionName}`;
252
290
  Dispatch failed: ${result.error ?? "unknown"}`;
253
291
  break;
254
292
  }
293
+ recordOrchestrationEventBestEffort({
294
+ eventType: result.status === "failed" ? "dispatch.failed" : "dispatch.completed",
295
+ source: "create-task.mcp",
296
+ severity: result.status === "failed" ? "warn" : "info",
297
+ taskId: task.id,
298
+ agentId: assigned_to,
299
+ sessionScope: task.sessionScope || callerRoot || exeSession,
300
+ projectName: project_name,
301
+ tmuxSession: result.sessionName || null,
302
+ runtime: dispatchRuntime,
303
+ result: result.status,
304
+ errorCode: result.status === "failed" ? "ensure_employee_failed" : null,
305
+ payload: { priority, error: result.error ?? null }
306
+ });
255
307
  try {
256
308
  const { sendIntercom: si } = await import("./lib/tmux-routing.js");
257
309
  const { listTmuxSessions } = await import("./lib/tmux-status.js");
@@ -287,8 +339,32 @@ Dispatch failed: ${result.error ?? "unknown"}`;
287
339
  sessionName: result.sessionName,
288
340
  error: result.error
289
341
  });
342
+ } else {
343
+ recordOrchestrationEventBestEffort({
344
+ eventType: "dispatch.failed",
345
+ source: "create-task.mcp",
346
+ severity: "warn",
347
+ taskId: task.id,
348
+ agentId: assigned_to,
349
+ sessionScope: task.sessionScope || callerRoot || null,
350
+ projectName: project_name,
351
+ runtime: dispatchRuntime,
352
+ errorCode: "no_exe_session",
353
+ payload: { priority }
354
+ });
290
355
  }
291
356
  } catch (dispatchErr) {
357
+ recordOrchestrationEventBestEffort({
358
+ eventType: "dispatch.failed",
359
+ source: "create-task.mcp",
360
+ severity: "warn",
361
+ taskId: task.id,
362
+ agentId: assigned_to,
363
+ sessionScope: task.sessionScope || callerRoot || null,
364
+ projectName: project_name,
365
+ runtime: resolveDispatchRuntime(assigned_to, spawn_runtime ?? null),
366
+ errorCode: "exception"
367
+ });
292
368
  process.stderr.write(`[create-task] dispatch error for ${assigned_to}: ${dispatchErr instanceof Error ? dispatchErr.message : String(dispatchErr)}
293
369
  ${dispatchErr instanceof Error ? dispatchErr.stack : ""}
294
370
  `);
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  processCRMEvent
3
- } from "./chunk-QQN7MHQG.js";
3
+ } from "./chunk-ONCFSN5C.js";
4
4
 
5
5
  // src/gateway/adapters/crm-webhook.ts
6
6
  function parseTwentyWebhook(payload) {
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  fastDbInit
3
- } from "./chunk-L63IOJ5N.js";
3
+ } from "./chunk-2THPPZF2.js";
4
4
  import {
5
5
  sessionScopeFilter
6
- } from "./chunk-RJCZY4BY.js";
6
+ } from "./chunk-FMKD6N33.js";
7
7
  import {
8
8
  formatStatusAll,
9
9
  formatStatusDeep,
@@ -14,7 +14,7 @@ import {
14
14
  getClient,
15
15
  isCoordinatorRole,
16
16
  loadEmployees
17
- } from "./chunk-K3O4MWMS.js";
17
+ } from "./chunk-3MHKTBHZ.js";
18
18
  import {
19
19
  isMainModule
20
20
  } from "./chunk-6Y4B3QF6.js";
@@ -0,0 +1,147 @@
1
+ import {
2
+ recordOrchestrationEventBestEffort
3
+ } from "./chunk-ITYWLFYC.js";
4
+
5
+ // src/lib/session-registry.ts
6
+ import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
7
+ import { execSync } from "child_process";
8
+ import path from "path";
9
+ import os from "os";
10
+ var REGISTRY_PATH = path.join(os.homedir(), ".exe-os", "session-registry.json");
11
+ function registerSession(entry) {
12
+ const dir = path.dirname(REGISTRY_PATH);
13
+ if (!existsSync(dir)) {
14
+ mkdirSync(dir, { recursive: true });
15
+ }
16
+ const sessions = listSessions();
17
+ const idx = sessions.findIndex((s) => s.windowName === entry.windowName);
18
+ if (idx >= 0) {
19
+ sessions[idx] = entry;
20
+ } else {
21
+ sessions.push(entry);
22
+ }
23
+ writeFileSync(REGISTRY_PATH, JSON.stringify(sessions, null, 2));
24
+ recordOrchestrationEventBestEffort({
25
+ eventType: "registry.registered",
26
+ source: "session-registry.registerSession",
27
+ agentId: entry.agentId,
28
+ sessionScope: entry.parentExe,
29
+ tmuxSession: entry.windowName,
30
+ payload: { hasProjectDir: Boolean(entry.projectDir) }
31
+ });
32
+ }
33
+ function refreshSessionProject(windowName, projectDir) {
34
+ const sessions = listSessions();
35
+ const entry = sessions.find((s) => s.windowName === windowName);
36
+ if (!entry || entry.projectDir === projectDir) return;
37
+ entry.projectDir = projectDir;
38
+ try {
39
+ writeFileSync(REGISTRY_PATH, JSON.stringify(sessions, null, 2));
40
+ } catch {
41
+ }
42
+ }
43
+ function listSessions() {
44
+ try {
45
+ const raw = readFileSync(REGISTRY_PATH, "utf8");
46
+ return JSON.parse(raw);
47
+ } catch {
48
+ return [];
49
+ }
50
+ }
51
+ function pruneStaleSessions() {
52
+ const sessions = listSessions();
53
+ if (sessions.length === 0) return 0;
54
+ let liveSessions = [];
55
+ try {
56
+ liveSessions = execSync("tmux list-sessions -F '#{session_name}' 2>/dev/null", {
57
+ encoding: "utf8"
58
+ }).trim().split("\n").filter(Boolean);
59
+ } catch {
60
+ return 0;
61
+ }
62
+ const liveSet = new Set(liveSessions);
63
+ const alive = sessions.filter((s) => liveSet.has(s.windowName));
64
+ const staleSessions = sessions.filter((s) => !liveSet.has(s.windowName));
65
+ if (staleSessions.length > 0) {
66
+ recordOrchestrationEventBestEffort({
67
+ eventType: "registry.missing_tmux",
68
+ source: "session-registry.pruneStaleSessions",
69
+ severity: "warn",
70
+ attempt: staleSessions.length,
71
+ payload: {
72
+ count: staleSessions.length,
73
+ examples: staleSessions.slice(0, 5).map((s) => s.windowName)
74
+ }
75
+ });
76
+ }
77
+ const pruned = sessions.length - alive.length;
78
+ if (pruned > 0) {
79
+ writeFileSync(REGISTRY_PATH, JSON.stringify(alive, null, 2));
80
+ }
81
+ return pruned;
82
+ }
83
+ function checkSessionRegistryConsistency(liveSessionsOverride) {
84
+ const sessions = listSessions();
85
+ const result = { missingTmux: 0, missingRegistry: 0 };
86
+ let liveSessions = liveSessionsOverride;
87
+ if (!liveSessions) {
88
+ try {
89
+ liveSessions = execSync("tmux list-sessions -F '#{session_name}' 2>/dev/null", {
90
+ encoding: "utf8"
91
+ }).trim().split("\n").filter(Boolean);
92
+ } catch {
93
+ return result;
94
+ }
95
+ }
96
+ const liveSet = new Set(liveSessions);
97
+ const registeredSet = new Set(sessions.map((s) => s.windowName));
98
+ const missingTmuxExamples = [];
99
+ const missingRegistryExamples = [];
100
+ for (const entry of sessions) {
101
+ if (liveSet.has(entry.windowName)) continue;
102
+ result.missingTmux++;
103
+ if (missingTmuxExamples.length < 5) missingTmuxExamples.push(entry.windowName);
104
+ }
105
+ for (const live of liveSessions) {
106
+ if (registeredSet.has(live)) continue;
107
+ if (!looksLikeExeOsSession(live)) continue;
108
+ result.missingRegistry++;
109
+ if (missingRegistryExamples.length < 5) missingRegistryExamples.push(live);
110
+ }
111
+ if (result.missingTmux > 0) {
112
+ recordOrchestrationEventBestEffort({
113
+ eventType: "registry.missing_tmux",
114
+ source: "session-registry.checkSessionRegistryConsistency",
115
+ severity: "warn",
116
+ attempt: result.missingTmux,
117
+ payload: {
118
+ count: result.missingTmux,
119
+ examples: missingTmuxExamples
120
+ }
121
+ });
122
+ }
123
+ if (result.missingRegistry > 0) {
124
+ recordOrchestrationEventBestEffort({
125
+ eventType: "registry.missing_entry",
126
+ source: "session-registry.checkSessionRegistryConsistency",
127
+ severity: "warn",
128
+ attempt: result.missingRegistry,
129
+ payload: {
130
+ count: result.missingRegistry,
131
+ examples: missingRegistryExamples
132
+ }
133
+ });
134
+ }
135
+ return result;
136
+ }
137
+ function looksLikeExeOsSession(sessionName) {
138
+ return /^exe\d*$/.test(sessionName) || /-exe\d*$/.test(sessionName);
139
+ }
140
+
141
+ export {
142
+ registerSession,
143
+ refreshSessionProject,
144
+ listSessions,
145
+ pruneStaleSessions,
146
+ checkSessionRegistryConsistency
147
+ };