@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.
- package/agent/index.js +33 -22
- package/agent/index.js.map +1 -1
- package/agent-manager-R7BPQO6D.js +11 -0
- package/chat-LPML22J5.js +24 -0
- package/{chunk-7L6AQUBR.js → chunk-4LJQIQMN.js} +6 -2
- package/{chunk-7L6AQUBR.js.map → chunk-4LJQIQMN.js.map} +1 -1
- package/{chunk-7HIPFSOA.js → chunk-57P45FNY.js} +11 -3
- package/chunk-57P45FNY.js.map +1 -0
- package/chunk-6IBPK6UE.js +1063 -0
- package/chunk-6IBPK6UE.js.map +1 -0
- package/{chunk-GXE5TLLJ.js → chunk-KV4GJKXU.js} +17 -17
- package/{chunk-XAFOP677.js → chunk-MZSDTA4A.js} +480 -233
- package/chunk-MZSDTA4A.js.map +1 -0
- package/{chunk-UR3HL373.js → chunk-NPMSBYXC.js} +8 -8
- package/{chunk-P73PTVII.js → chunk-SEPAURTQ.js} +369 -1282
- package/chunk-SEPAURTQ.js.map +1 -0
- package/{chunk-NSS47H26.js → chunk-UD42LACH.js} +348 -78
- package/chunk-UD42LACH.js.map +1 -0
- package/{chunk-IXO5KIO5.js → chunk-VPSQJOSN.js} +23 -2
- package/{chunk-IXO5KIO5.js.map → chunk-VPSQJOSN.js.map} +1 -1
- package/{companion-2LHKU577.js → companion-W3UMZWGH.js} +11 -7
- package/{desktop-installer-page-6JDSXK2E.js → desktop-installer-page-I5FHFDES.js} +11 -8
- package/desktop-installer-page-I5FHFDES.js.map +1 -0
- package/{desktop-launcher-YPNRQMIS.js → desktop-launcher-GD4QPM66.js} +3 -3
- package/external-NMKQPROK.js +387 -0
- package/external-NMKQPROK.js.map +1 -0
- package/index.js +34 -12
- package/index.js.map +1 -1
- package/{install-gate-2NNH7RM2.js → install-gate-PVMGJDWE.js} +2 -2
- package/mcp-server/external.d.ts +44 -0
- package/mcp-server/external.js +2679 -0
- package/mcp-server/external.js.map +1 -0
- package/mcp-server/index.js +142 -40
- package/mcp-server/index.js.map +1 -1
- package/mcp-server/launch.d.ts +19 -1
- package/mcp-server/launch.js +20 -0
- package/mcp-server/launch.js.map +1 -1
- package/package.json +1 -1
- package/settings-manager-XPLBO35P.js +10 -0
- package/src-DCCEUWE6.js +385 -0
- package/{src-5TEXF3SU.js → src-LEDGW5KP.js} +1029 -536
- package/src-LEDGW5KP.js.map +1 -0
- package/{vault-resolver-VK6W5J6W.js → vault-resolver-E7KPKA5C.js} +3 -2
- package/vault-resolver-E7KPKA5C.js.map +1 -0
- package/web/assets/{AgentScalarChatInterface.vue-B-SjOsdj.js → AgentScalarChatInterface.vue-BYszptaB.js} +1 -1
- package/web/assets/{AgentTerminalPage-JzC8DUgu.js → AgentTerminalPage-C3TJ1NL9.js} +1 -1
- package/web/assets/{AiChatModals-D1928AxG.js → AiChatModals-D3scRE3S.js} +1 -1
- package/web/assets/AppLayout-BMQUjPKe.js +1 -0
- package/web/assets/{AppLayout-8sBPr3od.css → AppLayout-tsw5Ow0w.css} +1 -1
- package/web/assets/{AuditPage-BZIJ3_Kr.js → AuditPage-CKcExBws.js} +1 -1
- package/web/assets/{CloudAccountPage-DBv470sT.js → CloudAccountPage-B6oPg0BZ.js} +1 -1
- package/web/assets/{CloudMessagingPage-qCI8-UDL.js → CloudMessagingPage-C5fwVe-F.js} +1 -1
- package/web/assets/DashboardPage-9RV7sx3e.js +1 -0
- package/web/assets/{DashboardPage-D9-PpC3F.css → DashboardPage-FvNuC_e4.css} +1 -1
- package/web/assets/{DirectoryPickerDialog-B5DvDfQP.js → DirectoryPickerDialog-C06BXyfJ.js} +1 -1
- package/web/assets/{EnvironmentForm-Bw8Djnqm.js → EnvironmentForm-CAvDoira.js} +1 -1
- package/web/assets/{EnvironmentFormPage-B59uiYtp.js → EnvironmentFormPage-BmAxhN-m.js} +1 -1
- package/web/assets/{EnvironmentListPage-W9zLDZAg.js → EnvironmentListPage-B8uATPBI.js} +1 -1
- package/web/assets/KanbanBoardPage-CQsOkRUf.js +4 -0
- package/web/assets/KanbanBoardPage-eHmsrFGA.css +1 -0
- package/web/assets/{KanbanListPage-Bn9k8Rpc.js → KanbanListPage-CWZqkjD6.js} +1 -1
- package/web/assets/{LaunchProjectPage-Dc7v1UjQ.js → LaunchProjectPage-saSmC7Pr.js} +1 -1
- package/web/assets/LaunchServicesPage-BkKyLMEz.js +1 -0
- package/web/assets/LaunchServicesPage-DoLDPqQq.css +1 -0
- package/web/assets/{LaunchShell-LonTJpb3.js → LaunchShell-DUrSunB4.js} +1 -1
- package/web/assets/{LogViewer-DkuqWYkQ.js → LogViewer-CZBYkH9J.js} +1 -1
- package/web/assets/{OrgKeyPrompt-By1VjlSN.js → OrgKeyPrompt-DzQ7pwbs.js} +1 -1
- package/web/assets/{ProcessControls-C0skzKGL.js → ProcessControls-BEeoNlNV.js} +1 -1
- package/web/assets/{ProcessDetailPage-BvPX6z_X.js → ProcessDetailPage-CQgu5uKm.js} +1 -1
- package/web/assets/{ProjectCreateDialog-C4o9u15L.js → ProjectCreateDialog-BolrhRD-.js} +1 -1
- package/web/assets/{ProjectLocationGate-BJ1CSQt0.js → ProjectLocationGate-DhMVdIjN.js} +1 -1
- package/web/assets/{ProjectPublishDialog-Cp-zXdGm.js → ProjectPublishDialog-DsZNWrfz.js} +1 -1
- package/web/assets/{RandomJobTestPage-DF4a1U13.js → RandomJobTestPage-BUS2Ty6-.js} +1 -1
- package/web/assets/ScenarioEditorPage-MIAGh2us.js +14 -0
- package/web/assets/{SendToChatDialog-Dg2SHkVD.js → SendToChatDialog-BxWsyv29.js} +1 -1
- package/web/assets/{ServiceControlsBar-q2VHWaxK.js → ServiceControlsBar-DqDPAZt7.js} +1 -1
- package/web/assets/ServiceDetailPage-hkSAzZ-l.js +2 -0
- package/web/assets/{ServiceFormPage-tT_n7WUy.js → ServiceFormPage-DXqhqXg8.js} +1 -1
- package/web/assets/SettingsPage-4pKUgKPm.css +1 -0
- package/web/assets/SettingsPage-DS2YhMIM.js +2086 -0
- package/web/assets/{UnlockPage-BKAdMJkb.js → UnlockPage-s8SgVcKk.js} +1 -1
- package/web/assets/{VaultMigrationPage-D9KUTTss.js → VaultMigrationPage-s0b44MDM.js} +1 -1
- package/web/assets/{VersionBadge-moKP5bAv.js → VersionBadge-BmArk24p.js} +1 -1
- package/web/assets/{WikiPage-hLUndDxo.js → WikiPage-DADu8WUA.js} +1 -1
- package/web/assets/{WikiView-rrjXRR72.js → WikiView-DtGwIt_Q.js} +1 -1
- package/web/assets/{add-scalar-classes-CBfRr8B2.js → add-scalar-classes-dWEWPlFD.js} +2 -2
- package/web/assets/{agent.store-B3dVemmT.js → agent.store-CKLAkF9a.js} +1 -1
- package/web/assets/app-mode-BVpTuh0i.js +1 -0
- package/web/assets/auth-Cg9BWTfK.js +2 -0
- package/web/assets/{cloud-chat.store-Wg1poFem.js → cloud-chat.store-b-BHGz7I.js} +1 -1
- package/web/assets/{cloud-client-DQlUaoAw.js → cloud-client-D_zCiW0s.js} +2 -2
- package/web/assets/{cloud-session-DRUqcSGP.js → cloud-session-DOaDpZTy.js} +2 -2
- package/web/assets/dist-N4nrnbnA.js +1 -0
- package/web/assets/{editor.store-SoH48ux2.js → editor.store-DCAZSeVG.js} +1 -1
- package/web/assets/i18n-D0afQEGs.js +1 -0
- package/web/assets/{index-D_x2s8Ut.js → index-D8VRbyoP.js} +101 -101
- package/web/assets/index-DsezNbSM.css +1 -0
- package/web/assets/{modal-CpsVK_dA.js → modal-qYgTMkYb.js} +1 -1
- package/web/assets/{project-launch.store-C3fZeRGb.js → project-launch.store-y7JBMNiE.js} +1 -1
- package/web/assets/{project.store-BEk4fciH.js → project.store-DGJr0Qr9.js} +1 -1
- package/web/assets/{scenario.store-D-JrRiVO.js → scenario.store-YWHL2_wu.js} +3 -3
- package/web/assets/{service.store-B5uYZtyM.js → service.store-5jWsJxPx.js} +1 -1
- package/web/assets/{settings-scope-Blnwj3rR.js → settings-scope-Bv7QGPrt.js} +1 -1
- package/web/assets/{settings.store-Bo3FYs1o.js → settings.store-bEsB7oKb.js} +1 -1
- package/web/assets/{src-CAwAaB5j.js → src-C-D_y7ou.js} +1 -1
- package/web/assets/{src-DValZNJU.js → src-Dnwk6NpV.js} +5 -5
- package/web/assets/src-g1DRo8xZ.css +1 -0
- package/web/assets/{trpc-DbsE5GvQ.js → trpc-Drabzwpd.js} +3 -3
- package/web/assets/{useCloudOrg-DygUBG1a.js → useCloudOrg-BAQEtFPl.js} +1 -1
- package/web/assets/{useEnvVarAutocomplete-DmYNOXgd.js → useEnvVarAutocomplete-BqGTODUv.js} +1 -1
- package/web/assets/{useProjectCatalog-D2mHB7dg.js → useProjectCatalog-yocjIKt6.js} +1 -1
- package/web/assets/{useProjectCloudOrg-avTCVold.js → useProjectCloudOrg-CEN9-_OA.js} +1 -1
- package/web/assets/{useServiceUrlHost-Q3fzGox3.js → useServiceUrlHost-BuoPEXZX.js} +1 -1
- package/web/assets/{vue-draggable-plus-WgrMXXmE.js → vue-draggable-plus-DX4rTBiu.js} +1 -1
- package/web/index.html +19 -18
- package/agent-manager-M4NA5YFD.js +0 -11
- package/chat-X3X527EX.js +0 -23
- package/chunk-7HIPFSOA.js.map +0 -1
- package/chunk-NSS47H26.js.map +0 -1
- package/chunk-P73PTVII.js.map +0 -1
- package/chunk-XAFOP677.js.map +0 -1
- package/desktop-installer-page-6JDSXK2E.js.map +0 -1
- package/settings-manager-32SVMAFN.js +0 -10
- package/src-5TEXF3SU.js.map +0 -1
- package/web/assets/AppLayout-CVQIBYqg.js +0 -1
- package/web/assets/DashboardPage-D0UZM0mr.js +0 -1
- package/web/assets/KanbanBoardPage-DRACMk_D.css +0 -1
- package/web/assets/KanbanBoardPage-DwPqoJDJ.js +0 -4
- package/web/assets/LaunchServicesPage-CXUS9O3y.js +0 -1
- package/web/assets/LaunchServicesPage-DJDIN8HU.css +0 -1
- package/web/assets/ScenarioEditorPage-Dl-gEkJI.js +0 -14
- package/web/assets/ServiceDetailPage-CJ8WaFod.js +0 -2
- package/web/assets/SettingsPage-CWakkv0V.js +0 -2086
- package/web/assets/SettingsPage-aGSxZw1n.css +0 -1
- package/web/assets/auth-BCrvnP1U.js +0 -2
- package/web/assets/dist-DoChtT9t.js +0 -1
- package/web/assets/i18n-BszFg0ko.js +0 -1
- package/web/assets/index-C_JV7fYy.css +0 -1
- package/web/assets/src-DuBXN1hu.css +0 -1
- /package/{agent-manager-M4NA5YFD.js.map → agent-manager-R7BPQO6D.js.map} +0 -0
- /package/{chat-X3X527EX.js.map → chat-LPML22J5.js.map} +0 -0
- /package/{chunk-GXE5TLLJ.js.map → chunk-KV4GJKXU.js.map} +0 -0
- /package/{chunk-UR3HL373.js.map → chunk-NPMSBYXC.js.map} +0 -0
- /package/{companion-2LHKU577.js.map → companion-W3UMZWGH.js.map} +0 -0
- /package/{desktop-launcher-YPNRQMIS.js.map → desktop-launcher-GD4QPM66.js.map} +0 -0
- /package/{install-gate-2NNH7RM2.js.map → install-gate-PVMGJDWE.js.map} +0 -0
- /package/{settings-manager-32SVMAFN.js.map → settings-manager-XPLBO35P.js.map} +0 -0
- /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-
|
|
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-
|
|
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-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1205
|
-
if (!
|
|
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
|
-
|
|
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
|
-
*
|
|
1439
|
+
* Écrire les conversations modifiées, chacune dans son fichier.
|
|
1243
1440
|
*
|
|
1244
|
-
*
|
|
1245
|
-
*
|
|
1246
|
-
*
|
|
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
|
|
1251
|
-
|
|
1252
|
-
|
|
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
|
-
|
|
1259
|
-
|
|
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] ${
|
|
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
|
-
|
|
1278
|
-
await
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
2260
|
-
"
|
|
2261
|
-
"
|
|
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
|
|
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
|
|
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
|
|
2578
|
+
function companionInstructions(custom) {
|
|
2325
2579
|
const customInstructions = custom?.trim();
|
|
2326
|
-
|
|
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")
|
|
2593
|
+
].join("\n");
|
|
2594
|
+
}
|
|
2595
|
+
function buildCompanionPrompt(exchange, baseSha) {
|
|
2338
2596
|
return [
|
|
2339
|
-
|
|
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-
|
|
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,
|
|
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-
|
|
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-
|
|
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-
|
|
3010
|
+
//# sourceMappingURL=chunk-UD42LACH.js.map
|