@runeya/runeya 2.0.144 → 2.0.146

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 (148) hide show
  1. package/agent/index.js +33 -22
  2. package/agent/index.js.map +1 -1
  3. package/agent-manager-R7BPQO6D.js +11 -0
  4. package/chat-LPML22J5.js +24 -0
  5. package/{chunk-7L6AQUBR.js → chunk-4LJQIQMN.js} +6 -2
  6. package/{chunk-7L6AQUBR.js.map → chunk-4LJQIQMN.js.map} +1 -1
  7. package/{chunk-7HIPFSOA.js → chunk-57P45FNY.js} +11 -3
  8. package/chunk-57P45FNY.js.map +1 -0
  9. package/chunk-6IBPK6UE.js +1063 -0
  10. package/chunk-6IBPK6UE.js.map +1 -0
  11. package/{chunk-GXE5TLLJ.js → chunk-KV4GJKXU.js} +17 -17
  12. package/{chunk-XAFOP677.js → chunk-MZSDTA4A.js} +480 -233
  13. package/chunk-MZSDTA4A.js.map +1 -0
  14. package/{chunk-UR3HL373.js → chunk-NPMSBYXC.js} +8 -8
  15. package/{chunk-P73PTVII.js → chunk-SEPAURTQ.js} +369 -1282
  16. package/chunk-SEPAURTQ.js.map +1 -0
  17. package/{chunk-NSS47H26.js → chunk-UD42LACH.js} +348 -78
  18. package/chunk-UD42LACH.js.map +1 -0
  19. package/{chunk-IXO5KIO5.js → chunk-VPSQJOSN.js} +23 -2
  20. package/{chunk-IXO5KIO5.js.map → chunk-VPSQJOSN.js.map} +1 -1
  21. package/{companion-2LHKU577.js → companion-W3UMZWGH.js} +11 -7
  22. package/{desktop-installer-page-6JDSXK2E.js → desktop-installer-page-I5FHFDES.js} +11 -8
  23. package/desktop-installer-page-I5FHFDES.js.map +1 -0
  24. package/{desktop-launcher-YPNRQMIS.js → desktop-launcher-GD4QPM66.js} +3 -3
  25. package/external-NMKQPROK.js +387 -0
  26. package/external-NMKQPROK.js.map +1 -0
  27. package/index.js +34 -12
  28. package/index.js.map +1 -1
  29. package/{install-gate-2NNH7RM2.js → install-gate-PVMGJDWE.js} +2 -2
  30. package/mcp-server/external.d.ts +44 -0
  31. package/mcp-server/external.js +2679 -0
  32. package/mcp-server/external.js.map +1 -0
  33. package/mcp-server/index.js +142 -40
  34. package/mcp-server/index.js.map +1 -1
  35. package/mcp-server/launch.d.ts +19 -1
  36. package/mcp-server/launch.js +20 -0
  37. package/mcp-server/launch.js.map +1 -1
  38. package/package.json +1 -1
  39. package/settings-manager-XPLBO35P.js +10 -0
  40. package/src-DCCEUWE6.js +385 -0
  41. package/{src-5TEXF3SU.js → src-LEDGW5KP.js} +1029 -536
  42. package/src-LEDGW5KP.js.map +1 -0
  43. package/{vault-resolver-VK6W5J6W.js → vault-resolver-E7KPKA5C.js} +3 -2
  44. package/vault-resolver-E7KPKA5C.js.map +1 -0
  45. package/web/assets/{AgentScalarChatInterface.vue-B-SjOsdj.js → AgentScalarChatInterface.vue-BYszptaB.js} +1 -1
  46. package/web/assets/{AgentTerminalPage-JzC8DUgu.js → AgentTerminalPage-C3TJ1NL9.js} +1 -1
  47. package/web/assets/{AiChatModals-D1928AxG.js → AiChatModals-D3scRE3S.js} +1 -1
  48. package/web/assets/AppLayout-BMQUjPKe.js +1 -0
  49. package/web/assets/{AppLayout-8sBPr3od.css → AppLayout-tsw5Ow0w.css} +1 -1
  50. package/web/assets/{AuditPage-BZIJ3_Kr.js → AuditPage-CKcExBws.js} +1 -1
  51. package/web/assets/{CloudAccountPage-DBv470sT.js → CloudAccountPage-B6oPg0BZ.js} +1 -1
  52. package/web/assets/{CloudMessagingPage-qCI8-UDL.js → CloudMessagingPage-C5fwVe-F.js} +1 -1
  53. package/web/assets/DashboardPage-9RV7sx3e.js +1 -0
  54. package/web/assets/{DashboardPage-D9-PpC3F.css → DashboardPage-FvNuC_e4.css} +1 -1
  55. package/web/assets/{DirectoryPickerDialog-B5DvDfQP.js → DirectoryPickerDialog-C06BXyfJ.js} +1 -1
  56. package/web/assets/{EnvironmentForm-Bw8Djnqm.js → EnvironmentForm-CAvDoira.js} +1 -1
  57. package/web/assets/{EnvironmentFormPage-B59uiYtp.js → EnvironmentFormPage-BmAxhN-m.js} +1 -1
  58. package/web/assets/{EnvironmentListPage-W9zLDZAg.js → EnvironmentListPage-B8uATPBI.js} +1 -1
  59. package/web/assets/KanbanBoardPage-CQsOkRUf.js +4 -0
  60. package/web/assets/KanbanBoardPage-eHmsrFGA.css +1 -0
  61. package/web/assets/{KanbanListPage-Bn9k8Rpc.js → KanbanListPage-CWZqkjD6.js} +1 -1
  62. package/web/assets/{LaunchProjectPage-Dc7v1UjQ.js → LaunchProjectPage-saSmC7Pr.js} +1 -1
  63. package/web/assets/LaunchServicesPage-BkKyLMEz.js +1 -0
  64. package/web/assets/LaunchServicesPage-DoLDPqQq.css +1 -0
  65. package/web/assets/{LaunchShell-LonTJpb3.js → LaunchShell-DUrSunB4.js} +1 -1
  66. package/web/assets/{LogViewer-DkuqWYkQ.js → LogViewer-CZBYkH9J.js} +1 -1
  67. package/web/assets/{OrgKeyPrompt-By1VjlSN.js → OrgKeyPrompt-DzQ7pwbs.js} +1 -1
  68. package/web/assets/{ProcessControls-C0skzKGL.js → ProcessControls-BEeoNlNV.js} +1 -1
  69. package/web/assets/{ProcessDetailPage-BvPX6z_X.js → ProcessDetailPage-CQgu5uKm.js} +1 -1
  70. package/web/assets/{ProjectCreateDialog-C4o9u15L.js → ProjectCreateDialog-BolrhRD-.js} +1 -1
  71. package/web/assets/{ProjectLocationGate-BJ1CSQt0.js → ProjectLocationGate-DhMVdIjN.js} +1 -1
  72. package/web/assets/{ProjectPublishDialog-Cp-zXdGm.js → ProjectPublishDialog-DsZNWrfz.js} +1 -1
  73. package/web/assets/{RandomJobTestPage-DF4a1U13.js → RandomJobTestPage-BUS2Ty6-.js} +1 -1
  74. package/web/assets/ScenarioEditorPage-MIAGh2us.js +14 -0
  75. package/web/assets/{SendToChatDialog-Dg2SHkVD.js → SendToChatDialog-BxWsyv29.js} +1 -1
  76. package/web/assets/{ServiceControlsBar-q2VHWaxK.js → ServiceControlsBar-DqDPAZt7.js} +1 -1
  77. package/web/assets/ServiceDetailPage-hkSAzZ-l.js +2 -0
  78. package/web/assets/{ServiceFormPage-tT_n7WUy.js → ServiceFormPage-DXqhqXg8.js} +1 -1
  79. package/web/assets/SettingsPage-4pKUgKPm.css +1 -0
  80. package/web/assets/SettingsPage-DS2YhMIM.js +2086 -0
  81. package/web/assets/{UnlockPage-BKAdMJkb.js → UnlockPage-s8SgVcKk.js} +1 -1
  82. package/web/assets/{VaultMigrationPage-D9KUTTss.js → VaultMigrationPage-s0b44MDM.js} +1 -1
  83. package/web/assets/{VersionBadge-moKP5bAv.js → VersionBadge-BmArk24p.js} +1 -1
  84. package/web/assets/{WikiPage-hLUndDxo.js → WikiPage-DADu8WUA.js} +1 -1
  85. package/web/assets/{WikiView-rrjXRR72.js → WikiView-DtGwIt_Q.js} +1 -1
  86. package/web/assets/{add-scalar-classes-CBfRr8B2.js → add-scalar-classes-dWEWPlFD.js} +2 -2
  87. package/web/assets/{agent.store-B3dVemmT.js → agent.store-CKLAkF9a.js} +1 -1
  88. package/web/assets/app-mode-BVpTuh0i.js +1 -0
  89. package/web/assets/auth-Cg9BWTfK.js +2 -0
  90. package/web/assets/{cloud-chat.store-Wg1poFem.js → cloud-chat.store-b-BHGz7I.js} +1 -1
  91. package/web/assets/{cloud-client-DQlUaoAw.js → cloud-client-D_zCiW0s.js} +2 -2
  92. package/web/assets/{cloud-session-DRUqcSGP.js → cloud-session-DOaDpZTy.js} +2 -2
  93. package/web/assets/dist-N4nrnbnA.js +1 -0
  94. package/web/assets/{editor.store-SoH48ux2.js → editor.store-DCAZSeVG.js} +1 -1
  95. package/web/assets/i18n-D0afQEGs.js +1 -0
  96. package/web/assets/{index-D_x2s8Ut.js → index-D8VRbyoP.js} +101 -101
  97. package/web/assets/index-DsezNbSM.css +1 -0
  98. package/web/assets/{modal-CpsVK_dA.js → modal-qYgTMkYb.js} +1 -1
  99. package/web/assets/{project-launch.store-C3fZeRGb.js → project-launch.store-y7JBMNiE.js} +1 -1
  100. package/web/assets/{project.store-BEk4fciH.js → project.store-DGJr0Qr9.js} +1 -1
  101. package/web/assets/{scenario.store-D-JrRiVO.js → scenario.store-YWHL2_wu.js} +3 -3
  102. package/web/assets/{service.store-B5uYZtyM.js → service.store-5jWsJxPx.js} +1 -1
  103. package/web/assets/{settings-scope-Blnwj3rR.js → settings-scope-Bv7QGPrt.js} +1 -1
  104. package/web/assets/{settings.store-Bo3FYs1o.js → settings.store-bEsB7oKb.js} +1 -1
  105. package/web/assets/{src-CAwAaB5j.js → src-C-D_y7ou.js} +1 -1
  106. package/web/assets/{src-DValZNJU.js → src-Dnwk6NpV.js} +5 -5
  107. package/web/assets/src-g1DRo8xZ.css +1 -0
  108. package/web/assets/{trpc-DbsE5GvQ.js → trpc-Drabzwpd.js} +3 -3
  109. package/web/assets/{useCloudOrg-DygUBG1a.js → useCloudOrg-BAQEtFPl.js} +1 -1
  110. package/web/assets/{useEnvVarAutocomplete-DmYNOXgd.js → useEnvVarAutocomplete-BqGTODUv.js} +1 -1
  111. package/web/assets/{useProjectCatalog-D2mHB7dg.js → useProjectCatalog-yocjIKt6.js} +1 -1
  112. package/web/assets/{useProjectCloudOrg-avTCVold.js → useProjectCloudOrg-CEN9-_OA.js} +1 -1
  113. package/web/assets/{useServiceUrlHost-Q3fzGox3.js → useServiceUrlHost-BuoPEXZX.js} +1 -1
  114. package/web/assets/{vue-draggable-plus-WgrMXXmE.js → vue-draggable-plus-DX4rTBiu.js} +1 -1
  115. package/web/index.html +19 -18
  116. package/agent-manager-M4NA5YFD.js +0 -11
  117. package/chat-X3X527EX.js +0 -23
  118. package/chunk-7HIPFSOA.js.map +0 -1
  119. package/chunk-NSS47H26.js.map +0 -1
  120. package/chunk-P73PTVII.js.map +0 -1
  121. package/chunk-XAFOP677.js.map +0 -1
  122. package/desktop-installer-page-6JDSXK2E.js.map +0 -1
  123. package/settings-manager-32SVMAFN.js +0 -10
  124. package/src-5TEXF3SU.js.map +0 -1
  125. package/web/assets/AppLayout-CVQIBYqg.js +0 -1
  126. package/web/assets/DashboardPage-D0UZM0mr.js +0 -1
  127. package/web/assets/KanbanBoardPage-DRACMk_D.css +0 -1
  128. package/web/assets/KanbanBoardPage-DwPqoJDJ.js +0 -4
  129. package/web/assets/LaunchServicesPage-CXUS9O3y.js +0 -1
  130. package/web/assets/LaunchServicesPage-DJDIN8HU.css +0 -1
  131. package/web/assets/ScenarioEditorPage-Dl-gEkJI.js +0 -14
  132. package/web/assets/ServiceDetailPage-CJ8WaFod.js +0 -2
  133. package/web/assets/SettingsPage-CWakkv0V.js +0 -2086
  134. package/web/assets/SettingsPage-aGSxZw1n.css +0 -1
  135. package/web/assets/auth-BCrvnP1U.js +0 -2
  136. package/web/assets/dist-DoChtT9t.js +0 -1
  137. package/web/assets/i18n-BszFg0ko.js +0 -1
  138. package/web/assets/index-C_JV7fYy.css +0 -1
  139. package/web/assets/src-DuBXN1hu.css +0 -1
  140. /package/{agent-manager-M4NA5YFD.js.map → agent-manager-R7BPQO6D.js.map} +0 -0
  141. /package/{chat-X3X527EX.js.map → chat-LPML22J5.js.map} +0 -0
  142. /package/{chunk-GXE5TLLJ.js.map → chunk-KV4GJKXU.js.map} +0 -0
  143. /package/{chunk-UR3HL373.js.map → chunk-NPMSBYXC.js.map} +0 -0
  144. /package/{companion-2LHKU577.js.map → companion-W3UMZWGH.js.map} +0 -0
  145. /package/{desktop-launcher-YPNRQMIS.js.map → desktop-launcher-GD4QPM66.js.map} +0 -0
  146. /package/{install-gate-2NNH7RM2.js.map → install-gate-PVMGJDWE.js.map} +0 -0
  147. /package/{settings-manager-32SVMAFN.js.map → settings-manager-XPLBO35P.js.map} +0 -0
  148. /package/{vault-resolver-VK6W5J6W.js.map → src-DCCEUWE6.js.map} +0 -0
@@ -17,26 +17,21 @@ import {
17
17
  serviceStore,
18
18
  setProjectServices,
19
19
  setProjectVault
20
- } from "./chunk-GXE5TLLJ.js";
20
+ } from "./chunk-KV4GJKXU.js";
21
21
  import {
22
22
  atomicWriteSecure,
23
23
  fingerprintFiles,
24
+ forgetFile,
24
25
  hasChangedSinceRead,
25
26
  rememberFile,
26
27
  wasRead
27
- } from "./chunk-7L6AQUBR.js";
28
+ } from "./chunk-4LJQIQMN.js";
28
29
  import {
29
30
  findThreadDb,
30
31
  lastTurn,
31
32
  threadMessages,
32
33
  threadsStartedAfter
33
34
  } from "./chunk-MD3VW4HQ.js";
34
- import {
35
- COMPANION_RELAY_CLOSE,
36
- COMPANION_RELAY_OPEN,
37
- ProjectSchema,
38
- extractCompanionRelay
39
- } from "./chunk-XAFOP677.js";
40
35
  import {
41
36
  allVaultRoots,
42
37
  collidesWithMachineRoot,
@@ -57,14 +52,20 @@ import {
57
52
  unregisterLocalProjectDir,
58
53
  vaultFor,
59
54
  vaultIdOfRoot
60
- } from "./chunk-IXO5KIO5.js";
55
+ } from "./chunk-VPSQJOSN.js";
56
+ import {
57
+ COMPANION_RELAY_CLOSE,
58
+ COMPANION_RELAY_OPEN,
59
+ ProjectSchema,
60
+ extractCompanionRelay
61
+ } from "./chunk-MZSDTA4A.js";
61
62
 
62
63
  // ../server/src/services/companion.ts
63
64
  import { EventEmitter as EventEmitter4 } from "events";
64
65
 
65
66
  // ../server/src/services/conversation-store.ts
66
67
  import { randomUUID as randomUUID3 } from "crypto";
67
- import { readFile as readFile4, mkdir as mkdir3 } from "fs/promises";
68
+ import { readFile as readFile4, readdir, mkdir as mkdir3, rename as rename3, rm as rm2, stat as stat2 } from "fs/promises";
68
69
  import { dirname as dirname2, join as join4 } from "path";
69
70
  import { EventEmitter as EventEmitter2 } from "events";
70
71
 
@@ -989,6 +990,26 @@ registerProjectLoader(() => projectStore.list().then(() => void 0));
989
990
 
990
991
  // ../server/src/services/conversation-store.ts
991
992
  var FILENAME4 = "conversations.private.json";
993
+ var CONVERSATIONS_DIRNAME = "conversations";
994
+ var FILE_SUFFIX = ".private.json";
995
+ var SAFE_ID_RE = /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,127}$/;
996
+ function conversationDir(root) {
997
+ return join4(root, CONVERSATIONS_DIRNAME);
998
+ }
999
+ function conversationPath(root, id) {
1000
+ return join4(root, CONVERSATIONS_DIRNAME, `${id}${FILE_SUFFIX}`);
1001
+ }
1002
+ function migratedAggregateName(now) {
1003
+ return `conversations.migrated-${now.toISOString().replace(/[:.]/g, "-").substring(0, 19)}.private.json`;
1004
+ }
1005
+ async function fileStamp(path) {
1006
+ try {
1007
+ const s = await stat2(path);
1008
+ return `${s.mtimeMs}:${s.size}:${s.ino}`;
1009
+ } catch {
1010
+ return null;
1011
+ }
1012
+ }
992
1013
  async function localProjectId() {
993
1014
  const projects = await projectStore.list().catch(() => []);
994
1015
  return projects.find((p) => !p.orgId)?.id ?? null;
@@ -1020,6 +1041,27 @@ var ConversationStore = class {
1020
1041
  fingerprint = "";
1021
1042
  events = new EventEmitter2().setMaxListeners(0);
1022
1043
  disk = new StoreDiskCoordinator();
1044
+ /** Le fichier où chaque conversation est écrite aujourd'hui, s'il existe. */
1045
+ written = /* @__PURE__ */ new Map();
1046
+ /**
1047
+ * Ce qui a été lu de chaque fichier, avec son empreinte.
1048
+ *
1049
+ * Un rechargement ne relit que les fichiers dont l'empreinte a bougé :
1050
+ * déchiffrer toutes les conversations parce qu'une seule a changé dans une
1051
+ * autre fenêtre coûterait autant que l'ancien format.
1052
+ */
1053
+ parsed = /* @__PURE__ */ new Map();
1054
+ /** Les conversations modifiées depuis la dernière écriture. */
1055
+ dirty = /* @__PURE__ */ new Set();
1056
+ /** Les fichiers à retirer : conversations supprimées ou changées de coffre. */
1057
+ removed = /* @__PURE__ */ new Set();
1058
+ /** Les anciens fichiers à convertir, relevés au dernier chargement. */
1059
+ aggregates = [];
1060
+ /** Écrites telles quelles à la conversion : illisibles ici, mais pas perdues. */
1061
+ rawToWrite = /* @__PURE__ */ new Map();
1062
+ /** Plus récentes dans un ancien fichier que dans le leur : à réécrire à la conversion. */
1063
+ outdated = /* @__PURE__ */ new Set();
1064
+ migrating = null;
1023
1065
  snapshotConversation(conv) {
1024
1066
  return structuredClone(conv);
1025
1067
  }
@@ -1099,6 +1141,8 @@ ${new Error("trace").stack}`
1099
1141
  agentId: conv.agentId,
1100
1142
  companionEnabled: conv.companionEnabled,
1101
1143
  companionRunner: conv.companionRunner,
1144
+ companionModel: conv.companionModel,
1145
+ companionEffort: conv.companionEffort,
1102
1146
  companionConversationId: conv.companionConversationId,
1103
1147
  companionBaseSha: conv.companionBaseSha,
1104
1148
  isCompanion: conv.isCompanion,
@@ -1129,6 +1173,8 @@ ${new Error("trace").stack}`
1129
1173
  agentId: disk.agentId,
1130
1174
  companionEnabled: disk.companionEnabled,
1131
1175
  companionRunner: disk.companionRunner,
1176
+ companionModel: disk.companionModel,
1177
+ companionEffort: disk.companionEffort,
1132
1178
  companionConversationId: disk.companionConversationId,
1133
1179
  companionBaseSha: disk.companionBaseSha,
1134
1180
  isCompanion: disk.isCompanion,
@@ -1158,8 +1204,24 @@ ${new Error("trace").stack}`
1158
1204
  ];
1159
1205
  return all.filter((source, i) => !all.slice(i + 1).some((later) => later.path === source.path));
1160
1206
  }
1207
+ /**
1208
+ * Les répertoires où chercher des conversations : ceux des sources, sans
1209
+ * doublon. Le dossier de lancement hérité en fait partie.
1210
+ */
1211
+ async sourceRoots() {
1212
+ return Array.from(new Set((await this.sourcePaths()).map((s) => dirname2(s.path))));
1213
+ }
1214
+ /**
1215
+ * L'état du disque : les anciens fichiers, et les dossiers `conversations/`.
1216
+ *
1217
+ * La date d'un dossier bouge à chaque fichier ajouté, renommé ou retiré, et
1218
+ * toute écriture passe par un renommage (`atomicWriteSecure`). Quelques
1219
+ * `stat` suffisent donc à savoir si une autre instance a écrit.
1220
+ */
1161
1221
  async diskFingerprint() {
1162
- return fingerprintFiles((await this.sourcePaths()).map((s) => s.path));
1222
+ const paths = (await this.sourcePaths()).map((s) => s.path);
1223
+ const dirs = Array.from(new Set(paths.map((path) => conversationDir(dirname2(path)))));
1224
+ return fingerprintFiles([...paths, ...dirs]);
1163
1225
  }
1164
1226
  async load() {
1165
1227
  if (!getEncryptionKey()) {
@@ -1173,25 +1235,105 @@ ${new Error("trace").stack}`
1173
1235
  return () => {
1174
1236
  this.conversations = next.conversations;
1175
1237
  this.roots = next.roots;
1238
+ this.written = next.written;
1239
+ this.parsed = next.parsed;
1176
1240
  this.unreadable = next.unreadable;
1241
+ this.aggregates = next.aggregates;
1242
+ this.rawToWrite = next.rawToWrite;
1243
+ this.outdated = next.outdated;
1177
1244
  this.fingerprint = fingerprint;
1178
1245
  this.loaded = true;
1179
1246
  };
1180
1247
  });
1248
+ if (this.aggregates.length > 0) await this.migrateAggregates();
1249
+ }
1250
+ /**
1251
+ * Passer les anciens fichiers au format « un fichier par conversation ».
1252
+ *
1253
+ * Dans cet ordre, pour ne rien perdre si ça s'arrête en route : chaque
1254
+ * conversation qui n'a pas encore son fichier l'obtient, les illisibles
1255
+ * compris (recopiées telles quelles), puis on vérifie que tous les fichiers
1256
+ * attendus existent. Alors seulement l'ancien fichier est renommé. Il reste
1257
+ * dans le coffre comme copie de secours, sous un nom que ce store ne lit pas.
1258
+ *
1259
+ * Le fichier du dossier de lancement hérité est renommé lui aussi : relu à
1260
+ * chaque démarrage, il ferait revenir les conversations supprimées depuis.
1261
+ */
1262
+ migrateAggregates() {
1263
+ this.migrating ??= (async () => {
1264
+ const aggregates = this.aggregates;
1265
+ const expected = [];
1266
+ let incomplete = false;
1267
+ for (const [id] of this.conversations) {
1268
+ if (!this.written.has(id) || this.outdated.has(id)) this.dirty.add(id);
1269
+ }
1270
+ await this.save();
1271
+ for (const [id, conv] of this.conversations) {
1272
+ const path = this.written.get(id);
1273
+ if (path) {
1274
+ expected.push(path);
1275
+ } else {
1276
+ incomplete = true;
1277
+ console.warn(`[conversation-store] Conversation ${conv.id} non convertie : ancien fichier gard\xE9.`);
1278
+ }
1279
+ }
1280
+ const raws = Array.from(this.rawToWrite.values());
1281
+ for (const { root, item } of raws) {
1282
+ if (!SAFE_ID_RE.test(item.id)) {
1283
+ incomplete = true;
1284
+ continue;
1285
+ }
1286
+ const path = conversationPath(root, item.id);
1287
+ if (!await fileStamp(path)) await atomicWriteSecure(path, JSON.stringify(item));
1288
+ expected.push(path);
1289
+ }
1290
+ const missing = (await Promise.all(expected.map(fileStamp))).some((st) => st === null);
1291
+ this.aggregates = [];
1292
+ if (missing || incomplete) {
1293
+ console.error("[conversation-store] Conversion incompl\xE8te : les anciens fichiers restent en place.");
1294
+ return;
1295
+ }
1296
+ const now = /* @__PURE__ */ new Date();
1297
+ for (const { path } of aggregates) {
1298
+ await rename3(path, join4(dirname2(path), migratedAggregateName(now)));
1299
+ forgetFile(path);
1300
+ console.log(`[conversation-store] ${path} converti en un fichier par conversation.`);
1301
+ }
1302
+ this.rawToWrite = /* @__PURE__ */ new Map();
1303
+ this.outdated = /* @__PURE__ */ new Set();
1304
+ this.fingerprint = await this.diskFingerprint();
1305
+ })().finally(() => {
1306
+ this.migrating = null;
1307
+ });
1308
+ return this.migrating;
1181
1309
  }
1182
1310
  /** Lire toutes les sources dans des tables neuves, que `load` substitue d'un coup. */
1183
1311
  async readSources() {
1184
1312
  const conversations = /* @__PURE__ */ new Map();
1185
1313
  const roots = /* @__PURE__ */ new Map();
1314
+ const written = /* @__PURE__ */ new Map();
1315
+ const parsed = /* @__PURE__ */ new Map();
1186
1316
  const unreadable = /* @__PURE__ */ new Map();
1187
- for (const { path, legacy } of await this.sourcePaths()) {
1317
+ const aggregates = [];
1318
+ const rawToWrite = /* @__PURE__ */ new Map();
1319
+ const outdated = /* @__PURE__ */ new Set();
1320
+ const markUnreadable = (root, item) => {
1321
+ const bucket = unreadable.get(root) ?? [];
1322
+ if (!bucket.some((kept) => kept.id === item.id)) bucket.push(item);
1323
+ unreadable.set(root, bucket);
1324
+ };
1325
+ const sources = await this.sourcePaths();
1326
+ const legacyRoots = new Set(sources.filter((s) => s.legacy).map((s) => dirname2(s.path)));
1327
+ const inheritedProject = legacyRoots.size > 0 ? await localProjectId() : null;
1328
+ for (const { path, legacy } of sources) {
1188
1329
  let data;
1189
1330
  try {
1190
1331
  data = JSON.parse(await readFile4(path, "utf-8"));
1191
1332
  } catch {
1192
1333
  continue;
1193
1334
  }
1194
- const inherited = legacy ? await localProjectId() : null;
1335
+ aggregates.push({ path });
1336
+ const inherited = legacy ? inheritedProject : null;
1195
1337
  const root = dirname2(path);
1196
1338
  await rememberFile(path);
1197
1339
  for (const item of data) {
@@ -1201,13 +1343,62 @@ ${new Error("trace").stack}`
1201
1343
  conversations.set(item.id, conv);
1202
1344
  roots.set(item.id, root);
1203
1345
  } catch {
1204
- const bucket = unreadable.get(root) ?? [];
1205
- if (!bucket.some((kept) => kept.id === item.id)) bucket.push(item);
1206
- unreadable.set(root, bucket);
1346
+ markUnreadable(root, item);
1347
+ if (!rawToWrite.has(item.id)) rawToWrite.set(item.id, { root, item });
1207
1348
  }
1208
1349
  }
1209
1350
  }
1210
- return { conversations, roots, unreadable };
1351
+ for (const root of await this.sourceRoots()) {
1352
+ const dir = conversationDir(root);
1353
+ let names;
1354
+ try {
1355
+ names = (await readdir(dir)).filter((n) => n.endsWith(FILE_SUFFIX));
1356
+ } catch {
1357
+ continue;
1358
+ }
1359
+ for (const name of names) {
1360
+ const path = join4(dir, name);
1361
+ const stamp = await fileStamp(path);
1362
+ if (!stamp) continue;
1363
+ let entry = this.parsed.get(path);
1364
+ if (!entry || entry.stamp !== stamp) {
1365
+ let item2;
1366
+ try {
1367
+ item2 = JSON.parse(await readFile4(path, "utf-8"));
1368
+ } catch {
1369
+ continue;
1370
+ }
1371
+ let conv2 = null;
1372
+ try {
1373
+ conv2 = this.fromDisk(item2, vaultIdOfRoot(root));
1374
+ } catch {
1375
+ conv2 = null;
1376
+ }
1377
+ entry = { stamp, conv: conv2, item: item2 };
1378
+ }
1379
+ await rememberFile(path);
1380
+ parsed.set(path, entry);
1381
+ const { conv, item } = entry;
1382
+ rawToWrite.delete(item.id);
1383
+ if (!conv) {
1384
+ markUnreadable(root, item);
1385
+ continue;
1386
+ }
1387
+ if (!conv.projectId && legacyRoots.has(root) && inheritedProject) conv.projectId = inheritedProject;
1388
+ written.set(conv.id, path);
1389
+ const older = conversations.get(conv.id);
1390
+ if (older && older.updatedAt > conv.updatedAt) {
1391
+ outdated.add(conv.id);
1392
+ continue;
1393
+ }
1394
+ conversations.set(conv.id, conv);
1395
+ roots.set(conv.id, root);
1396
+ }
1397
+ }
1398
+ for (const [id, conv] of conversations) {
1399
+ if (written.has(id)) rawToWrite.delete(conv.id);
1400
+ }
1401
+ return { conversations, roots, written, parsed, unreadable, aggregates, rawToWrite, outdated };
1211
1402
  }
1212
1403
  /**
1213
1404
  * Oublier ce qui a été lu, pour tout relire sous la clé courante.
@@ -1222,6 +1413,12 @@ ${new Error("trace").stack}`
1222
1413
  this.conversations.clear();
1223
1414
  this.roots.clear();
1224
1415
  this.unreadable.clear();
1416
+ this.written.clear();
1417
+ this.parsed.clear();
1418
+ this.dirty.clear();
1419
+ this.aggregates = [];
1420
+ this.rawToWrite.clear();
1421
+ this.outdated.clear();
1225
1422
  this.loaded = false;
1226
1423
  this.events.emit("changed", { type: "reloaded" });
1227
1424
  }
@@ -1239,43 +1436,52 @@ ${new Error("trace").stack}`
1239
1436
  return out;
1240
1437
  }
1241
1438
  /**
1242
- * Write each vault that holds — or held — a conversation.
1439
+ * Écrire les conversations modifiées, chacune dans son fichier.
1243
1440
  *
1244
- * A vault emptied by this save is still written, as an empty list: a
1245
- * conversation that found its project must not survive in the file it was
1246
- * read from.
1441
+ * Seules celles qui ont bougé depuis la dernière écriture : c'est le but du
1442
+ * format. Une conversation dont le projet a changé de coffre est écrite dans
1443
+ * le nouveau, et son ancien fichier retiré.
1444
+ *
1445
+ * Un fichier modifié depuis sa lecture (réparation faite à la main, clé
1446
+ * adoptée) n'est pas réécrit : réémettre notre copie déferait la réparation.
1247
1447
  */
1248
1448
  async saveInternal() {
1249
1449
  if (!getEncryptionKey()) return;
1250
- const byRoot = /* @__PURE__ */ new Map();
1251
- for (const root of this.roots.values()) byRoot.set(root, []);
1252
- for (const conv of this.conversations.values()) {
1450
+ const ids = Array.from(this.dirty);
1451
+ this.dirty.clear();
1452
+ const removed = Array.from(this.removed);
1453
+ this.removed.clear();
1454
+ for (const id of ids) {
1455
+ const conv = this.conversations.get(id);
1456
+ if (!conv) continue;
1457
+ if (!SAFE_ID_RE.test(conv.id)) {
1458
+ console.error(`[conversation-store] Id de conversation refus\xE9 comme nom de fichier : ${JSON.stringify(conv.id)}`);
1459
+ continue;
1460
+ }
1253
1461
  const root = await this.rootFor(conv);
1254
1462
  this.roots.set(conv.id, root);
1255
- const bucket = byRoot.get(root) ?? [];
1256
1463
  const vault = vaultIdOfRoot(root);
1257
1464
  if (!this.isVaultNamed(root, vault)) continue;
1258
- bucket.push(this.toDisk(conv, vault));
1259
- byRoot.set(root, bucket);
1260
- }
1261
- for (const [root, items] of this.unreadable) {
1262
- byRoot.set(root, [...items, ...byRoot.get(root) ?? []]);
1263
- }
1264
- const stale = /* @__PURE__ */ new Set();
1265
- for (const root of byRoot.keys()) {
1266
- const path = join4(root, FILENAME4);
1267
- if (wasRead(path) && await hasChangedSinceRead(path)) stale.add(root);
1268
- }
1269
- for (const [root, data] of byRoot) {
1270
- const filePath = join4(root, FILENAME4);
1271
- if (stale.has(root)) {
1465
+ const path = conversationPath(root, conv.id);
1466
+ if (wasRead(path) && await hasChangedSinceRead(path)) {
1272
1467
  console.warn(
1273
- `[conversation-store] ${filePath} a chang\xE9 depuis sa lecture \u2014 \xE9criture ignor\xE9e pour ne pas \xE9craser une r\xE9paration faite en dehors du serveur.`
1468
+ `[conversation-store] ${path} a chang\xE9 depuis sa lecture \u2014 \xE9criture ignor\xE9e pour ne pas \xE9craser une r\xE9paration faite en dehors du serveur.`
1274
1469
  );
1275
1470
  continue;
1276
1471
  }
1277
- await atomicWriteSecure(filePath, JSON.stringify(data, null, 2));
1278
- await rememberFile(filePath);
1472
+ const item = this.toDisk(conv, vault);
1473
+ await atomicWriteSecure(path, JSON.stringify(item));
1474
+ await rememberFile(path);
1475
+ const stamp = await fileStamp(path);
1476
+ if (stamp) this.parsed.set(path, { stamp, conv, item });
1477
+ const previous = this.written.get(conv.id);
1478
+ this.written.set(conv.id, path);
1479
+ if (previous && previous !== path) removed.push(previous);
1480
+ }
1481
+ for (const path of removed) {
1482
+ await rm2(path, { force: true });
1483
+ forgetFile(path);
1484
+ this.parsed.delete(path);
1279
1485
  }
1280
1486
  this.fingerprint = await this.diskFingerprint();
1281
1487
  }
@@ -1311,9 +1517,11 @@ ${new Error("trace").stack}`
1311
1517
  */
1312
1518
  async relocateAll() {
1313
1519
  await this.load();
1314
- await this.save();
1520
+ await this.save(...this.conversations.keys());
1315
1521
  }
1316
- save() {
1522
+ /** Écrire ces conversations — et celles qui attendaient déjà. */
1523
+ save(...ids) {
1524
+ for (const id of ids) this.dirty.add(id);
1317
1525
  return this.disk.save(() => this.saveInternal());
1318
1526
  }
1319
1527
  async list() {
@@ -1376,6 +1584,8 @@ ${new Error("trace").stack}`
1376
1584
  isCompanion: extra?.isCompanion,
1377
1585
  companionEnabled: extra?.companionEnabled,
1378
1586
  companionRunner: extra?.companionRunner,
1587
+ companionModel: extra?.companionModel,
1588
+ companionEffort: extra?.companionEffort,
1379
1589
  createdAt: now,
1380
1590
  updatedAt: now
1381
1591
  };
@@ -1383,7 +1593,7 @@ ${new Error("trace").stack}`
1383
1593
  if (projectId && orgId !== void 0) {
1384
1594
  this.roots.set(conv.id, vaultFor({ id: projectId, orgId }).root);
1385
1595
  }
1386
- await this.save();
1596
+ await this.save(conv.id);
1387
1597
  this.events.emit("changed", { type: "created", conversation: this.snapshotConversation(conv) });
1388
1598
  return conv;
1389
1599
  }
@@ -1399,10 +1609,12 @@ ${new Error("trace").stack}`
1399
1609
  if (patch.codexThreadId !== void 0) conv.codexThreadId = patch.codexThreadId;
1400
1610
  if (patch.companionEnabled !== void 0) conv.companionEnabled = patch.companionEnabled;
1401
1611
  if (patch.companionRunner !== void 0) conv.companionRunner = patch.companionRunner;
1612
+ if (patch.companionModel !== void 0) conv.companionModel = patch.companionModel || void 0;
1613
+ if (patch.companionEffort !== void 0) conv.companionEffort = patch.companionEffort || void 0;
1402
1614
  if (patch.companionConversationId !== void 0) conv.companionConversationId = patch.companionConversationId;
1403
1615
  if (patch.companionBaseSha !== void 0) conv.companionBaseSha = patch.companionBaseSha;
1404
1616
  if (options?.touch !== false) conv.updatedAt = Date.now();
1405
- await this.save();
1617
+ await this.save(conv.id);
1406
1618
  if (!options?.silent) {
1407
1619
  this.events.emit("changed", { type: "updated", conversation: this.snapshotConversation(conv), messagesChanged: patch.messages !== void 0 });
1408
1620
  }
@@ -1420,7 +1632,7 @@ ${new Error("trace").stack}`
1420
1632
  lastMsg.content = content;
1421
1633
  if (parts !== void 0) lastMsg.parts = parts;
1422
1634
  conv.updatedAt = Date.now();
1423
- this.save().catch(() => {
1635
+ this.save(conv.id).catch(() => {
1424
1636
  });
1425
1637
  }
1426
1638
  async listByKanbanBoardId(boardId) {
@@ -1452,7 +1664,7 @@ ${new Error("trace").stack}`
1452
1664
  const ranks = Array.from(this.conversations.values()).map((c) => c.pinOrder).filter((r) => r !== void 0);
1453
1665
  conv.pinOrder = ranks.length > 0 ? Math.max(...ranks) + 1 : 0;
1454
1666
  }
1455
- await this.save();
1667
+ await this.save(conv.id);
1456
1668
  this.events.emit("changed", { type: "updated", conversation: this.snapshotConversation(conv) });
1457
1669
  return conv;
1458
1670
  }
@@ -1473,7 +1685,7 @@ ${new Error("trace").stack}`
1473
1685
  targets.forEach((conv, i) => {
1474
1686
  conv.pinOrder = ranks[i];
1475
1687
  });
1476
- await this.save();
1688
+ await this.save(...targets.map((c) => c.id));
1477
1689
  for (const conv of targets) {
1478
1690
  this.events.emit("changed", { type: "updated", conversation: this.snapshotConversation(conv) });
1479
1691
  }
@@ -1500,7 +1712,7 @@ ${new Error("trace").stack}`
1500
1712
  delete conv.pinOrder;
1501
1713
  }
1502
1714
  });
1503
- await this.save();
1715
+ await this.save(...targets.map((c) => c.id));
1504
1716
  for (const conv of targets) {
1505
1717
  this.events.emit("changed", { type: "updated", conversation: this.snapshotConversation(conv) });
1506
1718
  }
@@ -1515,6 +1727,10 @@ ${new Error("trace").stack}`
1515
1727
  async delete(id) {
1516
1728
  await this.load();
1517
1729
  const existed = this.conversations.delete(id);
1730
+ this.dirty.delete(id);
1731
+ const path = this.written.get(id);
1732
+ this.written.delete(id);
1733
+ if (path) this.removed.add(path);
1518
1734
  await this.save();
1519
1735
  if (existed) {
1520
1736
  this.events.emit("changed", { type: "deleted", id });
@@ -1607,7 +1823,7 @@ function onTurnFinished(listener) {
1607
1823
  }
1608
1824
 
1609
1825
  // ../server/src/services/claude-transcript-sync.ts
1610
- import { readdir, access, readFile as readFile5 } from "fs/promises";
1826
+ import { readdir as readdir2, access, readFile as readFile5 } from "fs/promises";
1611
1827
  import { watch } from "fs";
1612
1828
  import { homedir } from "os";
1613
1829
  import { join as join6 } from "path";
@@ -1616,7 +1832,7 @@ var DEFAULT_TITLES = /* @__PURE__ */ new Set(["", "New conversation", "Nouvelle
1616
1832
  async function findClaudeSessionFile(sessionId) {
1617
1833
  let entries;
1618
1834
  try {
1619
- entries = await readdir(PROJECTS_DIR);
1835
+ entries = await readdir2(PROJECTS_DIR);
1620
1836
  } catch {
1621
1837
  return null;
1622
1838
  }
@@ -1797,7 +2013,7 @@ async function startClaudeTranscriptWatcher() {
1797
2013
  started = true;
1798
2014
  let topLevel;
1799
2015
  try {
1800
- topLevel = await readdir(PROJECTS_DIR);
2016
+ topLevel = await readdir2(PROJECTS_DIR);
1801
2017
  } catch {
1802
2018
  return;
1803
2019
  }
@@ -1964,14 +2180,27 @@ function getJwtSecret() {
1964
2180
  if (!jwtSecret) throw new Error("JWT secret not yet generated");
1965
2181
  return jwtSecret;
1966
2182
  }
1967
- async function signJwt() {
2183
+ async function signJwt(opts = {}) {
1968
2184
  const secret = getJwtSecret();
1969
- return new SignJWT({ sub: "app" }).setProtectedHeader({ alg: "HS256" }).setIssuedAt().setExpirationTime(env.JWT_TTL).sign(secret);
2185
+ return new SignJWT(opts.mcpClientId ? { sub: "app", mcp: opts.mcpClientId } : { sub: "app" }).setProtectedHeader({ alg: "HS256" }).setIssuedAt().setExpirationTime(opts.ttl ?? env.JWT_TTL).sign(secret);
2186
+ }
2187
+ var mcpClientCheck = null;
2188
+ function setMcpClientCheck(check) {
2189
+ mcpClientCheck = check;
1970
2190
  }
1971
- async function verifyJwt(token) {
2191
+ async function verifyJwt(token, opts = {}) {
1972
2192
  const secret = getJwtSecret();
1973
2193
  const { payload } = await jwtVerify(token, secret);
1974
- return { exp: payload.exp, iat: payload.iat };
2194
+ const result = { exp: payload.exp, iat: payload.iat };
2195
+ if (payload["mcp"] !== void 0) {
2196
+ if (!opts.allowMcp) throw new Error("MCP client token not allowed here");
2197
+ const clientId = payload["mcp"];
2198
+ if (typeof clientId !== "string" || !mcpClientCheck || !await mcpClientCheck(clientId)) {
2199
+ throw new Error("MCP client is no longer paired");
2200
+ }
2201
+ result.mcpClientId = clientId;
2202
+ }
2203
+ return result;
1975
2204
  }
1976
2205
 
1977
2206
  // ../server/src/services/live-pty.ts
@@ -2256,18 +2485,45 @@ function formatExchange(messages, from) {
2256
2485
  }
2257
2486
  function fullInstructions() {
2258
2487
  return [
2259
- "Tu relis le travail d'un autre agent, dans la m\xEAme conversation. Tu ne modifies",
2260
- "rien : ni le code, ni la carte kanban, que tu peux lire mais pas d\xE9placer ni",
2261
- "commenter. Tu ne d\xE9l\xE8gues pas \xE0 un sous-agent.",
2488
+ "Tu es le compagnon de relecture de cette conversation. Un autre agent y",
2489
+ "travaille pour une personne qui suit l'\xE9change. Ton r\xF4le : v\xE9rifier que ce",
2490
+ "qu'il livre est correct et complet par rapport \xE0 ce qui a \xE9t\xE9 demand\xE9, et le",
2491
+ "lui signaler quand ce n'est pas le cas, avant que la personne ne le d\xE9couvre.",
2492
+ "",
2493
+ "Ce que tu \xE9cris entre les bornes arrive dans sa conversation, pr\xE9fix\xE9 par",
2494
+ `${COMPANION_SIGNATURE}, et le relance. Un d\xE9faut que tu laisses passer co\xFBte plus cher`,
2495
+ "qu'une remarque en trop : l'agent v\xE9rifie et \xE9carte en une phrase ce qui est",
2496
+ "faux. Dans le doute, signale-le en disant que tu n'en es pas s\xFBr.",
2497
+ "",
2498
+ "Tu ne modifies rien : ni le code, ni la carte kanban, que tu peux lire mais",
2499
+ "pas d\xE9placer ni commenter. Tu ne d\xE9l\xE8gues pas \xE0 un sous-agent. Si une carte",
2500
+ "kanban est fournie, c'est la demande : v\xE9rifie que le travail la couvre.",
2262
2501
  "",
2263
2502
  "Images : l'\xE9change ne porte que leur chemin, [Image: source: /chemin.png].",
2264
2503
  "Ouvre-le avec ton outil de lecture. Si la lecture \xE9choue, dis-le. N'invente",
2265
2504
  "jamais le contenu d'une image.",
2266
2505
  "",
2506
+ "CE QUE TU CHERCHES",
2507
+ "- La demande : le travail couvre-t-il tout ce qui a \xE9t\xE9 demand\xE9 (message de",
2508
+ " la personne, carte kanban) ? Rien d'oubli\xE9, rien d'ajout\xE9 hors sujet ?",
2509
+ "- Les r\xE8gles du projet : lis CLAUDE.md, AGENTS.md ou leur \xE9quivalent s'ils",
2510
+ " existent, et v\xE9rifie que le travail les respecte.",
2511
+ "- La justesse : cas limites, valeurs vides, erreurs non trait\xE9es, chemins que",
2512
+ " le code rend atteignables sans les g\xE9rer.",
2513
+ "- Les r\xE9gressions et effets de bord : les appelants du code modifi\xE9, les",
2514
+ " comportements qui changent sans que la demande le pr\xE9voie.",
2515
+ "- La duplication : une fonction, un composant ou une constante qui existait",
2516
+ " d\xE9j\xE0 et qu'on a r\xE9\xE9crit \xE0 c\xF4t\xE9.",
2517
+ "- La s\xE9curit\xE9 : entr\xE9es non valid\xE9es, secrets expos\xE9s, contr\xF4les d'acc\xE8s",
2518
+ " retir\xE9s ou absents.",
2519
+ "- Les tests : un test ajout\xE9 v\xE9rifie-t-il vraiment le comportement ? Un test",
2520
+ " affaibli, d\xE9sactiv\xE9 ou supprim\xE9 pour passer au vert ?",
2521
+ "- Les affirmations : \xAB les tests passent \xBB, \xAB c'est v\xE9rifi\xE9 \xBB, \xAB rien d'autre",
2522
+ " n'utilise ce code \xBB. Contr\xF4le-les dans le code ou la sortie montr\xE9e.",
2523
+ "",
2267
2524
  "QUAND PARLER",
2268
2525
  "- Si l'\xE9change te parle ou te pose une question, r\xE9ponds toujours.",
2269
- "- Sinon, parle seulement si tu as trouv\xE9 un d\xE9faut : r\xE9gression, effet de bord,",
2270
- " duplication de code existant, faille de s\xE9curit\xE9, affirmation non v\xE9rifi\xE9e.",
2526
+ "- Sinon, parle si tu as trouv\xE9 un d\xE9faut ou si tu en soup\xE7onnes un.",
2271
2527
  "- Pas d'accus\xE9 de r\xE9ception, de remerciement ni de \xAB rien \xE0 signaler \xBB : chacun",
2272
2528
  " relance l'agent. Quand il r\xE9pond \xE0 ta remarque, tais-toi, sauf s'il pose une",
2273
2529
  " question ou si sa r\xE9ponse est fausse.",
@@ -2284,9 +2540,7 @@ function fullInstructions() {
2284
2540
  " pas pu v\xE9rifier.",
2285
2541
  "- Pas de tests complets, build, typecheck global ni serveur : s'il en faut, dis",
2286
2542
  " \xE0 l'agent quoi lancer.",
2287
- "- Ne resignale pas ce qui a d\xE9j\xE0 \xE9t\xE9 signal\xE9, sauf si ce n'est pas trait\xE9.",
2288
- "",
2289
- "Ces consignes valent pour toute la session : elles ne te seront pas r\xE9p\xE9t\xE9es."
2543
+ "- Ne resignale pas ce qui a d\xE9j\xE0 \xE9t\xE9 signal\xE9, sauf si ce n'est pas trait\xE9."
2290
2544
  ].join("\n");
2291
2545
  }
2292
2546
  function responseProtocol() {
@@ -2308,7 +2562,7 @@ function responseProtocol() {
2308
2562
  }
2309
2563
  function reminder() {
2310
2564
  return [
2311
- "Tour suivant \xE0 relire. M\xEAmes r\xE8gles.",
2565
+ "Tour \xE0 relire. Tes consignes sont dans ton contexte syst\xE8me.",
2312
2566
  `Rien \xE0 dire \u2192 ${VERDICT_NOTHING}. Sinon, ton message entre ${RELAY_OPEN} et ${RELAY_CLOSE} \u2014 sans bornes, rien ne part.`,
2313
2567
  "Si l'\xE9change t'adresse quelque chose, tu r\xE9ponds \u2014 jamais " + VERDICT_NOTHING + " dans ce cas.",
2314
2568
  "Mais l'agent qui r\xE9pond \xE0 ta remarque ne te pose pas de question : sans d\xE9faut ni",
@@ -2321,9 +2575,11 @@ function companionDefaultPrompt() {
2321
2575
  function baseLine(baseSha) {
2322
2576
  return `Point de d\xE9part : le travail de la conversation, commits compris, est dans \`git diff ${baseSha}\`.`;
2323
2577
  }
2324
- function buildCompanionPrompt(exchange, isFirstTurn = true, custom, baseSha) {
2578
+ function companionInstructions(custom) {
2325
2579
  const customInstructions = custom?.trim();
2326
- const instructions = isFirstTurn ? [
2580
+ return [
2581
+ "## Tes consignes de compagnon de relecture",
2582
+ "",
2327
2583
  fullInstructions(),
2328
2584
  ...customInstructions ? [
2329
2585
  "",
@@ -2334,9 +2590,11 @@ function buildCompanionPrompt(exchange, isFirstTurn = true, custom, baseSha) {
2334
2590
  // Le protocole, lui, s'ajoute TOUJOURS : c'est par lui que Runeya lit le
2335
2591
  // verdict. Remplaçable, il rendrait le compagnon illisible.
2336
2592
  responseProtocol()
2337
- ].join("\n") : reminder();
2593
+ ].join("\n");
2594
+ }
2595
+ function buildCompanionPrompt(exchange, baseSha) {
2338
2596
  return [
2339
- instructions,
2597
+ reminder(),
2340
2598
  ...baseSha ? ["", baseLine(baseSha)] : [],
2341
2599
  "",
2342
2600
  "--- Ce que tu n'as pas encore relu de la conversation ---",
@@ -2391,13 +2649,11 @@ async function runCompanionForTurn(conversationId) {
2391
2649
  if (!hasNew) {
2392
2650
  return skip(conversationId, "nothingNew", `rien de neuf \xE0 relire (${messageCount} messages)`);
2393
2651
  }
2394
- const { startCliSession } = await import("./chat-X3X527EX.js");
2652
+ const { startCliSession } = await import("./chat-LPML22J5.js");
2395
2653
  const companionConv = await ensureCompanionConversation(conv);
2396
- const isFirstTurn = (companionConv.messages?.length ?? 0) === 0;
2397
2654
  const exchange = formatExchange(conv.messages, from);
2398
- const { prompt: customPrompt } = await companionSettings();
2399
2655
  const baseSha = await ensureCompanionBase(conv);
2400
- const prompt = buildCompanionPrompt(exchange, isFirstTurn, customPrompt, baseSha);
2656
+ const prompt = buildCompanionPrompt(exchange, baseSha);
2401
2657
  const jwt = await signJwt();
2402
2658
  const environmentId = await activeEnvironmentOf(conv);
2403
2659
  const agentId = companionAgentId(
@@ -2412,7 +2668,7 @@ async function runCompanionForTurn(conversationId) {
2412
2668
  conv.companionRunner ?? conv.runner,
2413
2669
  companionConv.id,
2414
2670
  prompt,
2415
- { jwt, environmentId, agentId }
2671
+ { jwt, environmentId, agentId, model: conv.companionModel, effort: conv.companionEffort }
2416
2672
  );
2417
2673
  await done;
2418
2674
  } catch (err) {
@@ -2512,7 +2768,7 @@ async function deliverToMainConversation(conv, message) {
2512
2768
  async function attemptDelivery(conv, message) {
2513
2769
  if (await writeToLivePty(conv.id, message)) return "pty";
2514
2770
  try {
2515
- const { startCliSession } = await import("./chat-X3X527EX.js");
2771
+ const { startCliSession } = await import("./chat-LPML22J5.js");
2516
2772
  await startCliSession(conv.runner, conv.id, message, { jwt: await signJwt() });
2517
2773
  return "headless";
2518
2774
  } catch (err) {
@@ -2533,9 +2789,19 @@ async function writeToLivePty(conversationId, message) {
2533
2789
  return false;
2534
2790
  }
2535
2791
  }
2792
+ function companionCreateDefaults(settings, convRunner) {
2793
+ const modelApplies = !!settings.runner || convRunner !== "codex";
2794
+ return {
2795
+ ...settings.enabledByDefault ? { companionEnabled: true } : {},
2796
+ ...settings.runner ? { companionRunner: settings.runner } : {},
2797
+ ...settings.model && modelApplies ? { companionModel: settings.model } : {},
2798
+ // Les deux runners comprennent les mêmes niveaux : l'effort vaut pour tous.
2799
+ ...settings.effort ? { companionEffort: settings.effort } : {}
2800
+ };
2801
+ }
2536
2802
  async function companionSettings() {
2537
2803
  try {
2538
- const { settingsManager } = await import("./settings-manager-32SVMAFN.js");
2804
+ const { settingsManager } = await import("./settings-manager-XPLBO35P.js");
2539
2805
  const settings = await settingsManager.getSettings();
2540
2806
  return settings.companion ?? { enabledByDefault: false };
2541
2807
  } catch {
@@ -2680,6 +2946,7 @@ export {
2680
2946
  generateJwtSecret,
2681
2947
  getJwtSecretGeneratedAt,
2682
2948
  signJwt,
2949
+ setMcpClientCheck,
2683
2950
  verifyJwt,
2684
2951
  ensureDataGitignore,
2685
2952
  conversationSectionStore,
@@ -2687,6 +2954,7 @@ export {
2687
2954
  projectPathStore,
2688
2955
  LocalProjectRefusedError,
2689
2956
  projectStore,
2957
+ CONVERSATIONS_DIRNAME,
2690
2958
  conversationStore,
2691
2959
  resolveWorkspaceCwd,
2692
2960
  resolveAgentCwd,
@@ -2715,6 +2983,7 @@ export {
2715
2983
  reviewWindow,
2716
2984
  formatExchange,
2717
2985
  companionDefaultPrompt,
2986
+ companionInstructions,
2718
2987
  buildCompanionPrompt,
2719
2988
  COMPANION_SIGNATURE,
2720
2989
  buildRelaunchMessage,
@@ -2730,6 +2999,7 @@ export {
2730
2999
  lastCompanionDelivery,
2731
3000
  forgetDelivery,
2732
3001
  deliverToMainConversation,
3002
+ companionCreateDefaults,
2733
3003
  companionSettings,
2734
3004
  answerOfTurn,
2735
3005
  runCompanionNow,
@@ -2737,4 +3007,4 @@ export {
2737
3007
  headCommit,
2738
3008
  initCompanion
2739
3009
  };
2740
- //# sourceMappingURL=chunk-NSS47H26.js.map
3010
+ //# sourceMappingURL=chunk-UD42LACH.js.map