@adhdev/daemon-core 0.8.22 → 0.8.24
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/dist/agent-stream/types.d.ts +3 -0
- package/dist/cli-adapter-types.d.ts +3 -0
- package/dist/cli-adapters/provider-cli-adapter.d.ts +71 -11
- package/dist/commands/stream-commands.d.ts +1 -0
- package/dist/config/config.d.ts +6 -0
- package/dist/index.js +1162 -307
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1162 -307
- package/dist/index.mjs.map +1 -1
- package/dist/providers/cli-provider-instance.d.ts +6 -0
- package/dist/providers/contracts.d.ts +59 -1
- package/dist/providers/control-effects.d.ts +4 -0
- package/dist/providers/extension-provider-instance.d.ts +9 -0
- package/dist/providers/ide-provider-instance.d.ts +8 -0
- package/dist/shared-types.d.ts +2 -1
- package/node_modules/@adhdev/session-host-core/package.json +1 -1
- package/package.json +3 -2
- package/src/agent-stream/forward.ts +2 -0
- package/src/agent-stream/provider-adapter.ts +5 -15
- package/src/agent-stream/types.ts +4 -0
- package/src/cli-adapter-types.ts +3 -0
- package/src/cli-adapters/provider-cli-adapter.ts +399 -49
- package/src/commands/handler.ts +1 -0
- package/src/commands/stream-commands.ts +99 -8
- package/src/config/config.d.ts +1 -0
- package/src/config/config.ts +9 -0
- package/src/launch.ts +57 -11
- package/src/providers/cli-provider-instance.ts +148 -2
- package/src/providers/contracts.ts +65 -2
- package/src/providers/control-effects.ts +114 -0
- package/src/providers/extension-provider-instance.ts +163 -3
- package/src/providers/ide-provider-instance.ts +181 -2
- package/src/shared-types.d.ts +1 -0
- package/src/shared-types.ts +2 -1
- package/src/status/snapshot.ts +1 -0
package/dist/index.mjs
CHANGED
|
@@ -79,7 +79,8 @@ function normalizeConfig(raw) {
|
|
|
79
79
|
providerSettings: isPlainObject(parsed.providerSettings) ? parsed.providerSettings : {},
|
|
80
80
|
ideSettings: isPlainObject(parsed.ideSettings) ? parsed.ideSettings : {},
|
|
81
81
|
disableUpstream: asBoolean(parsed.disableUpstream, DEFAULT_CONFIG.disableUpstream ?? false),
|
|
82
|
-
providerDir: asOptionalString(parsed.providerDir)
|
|
82
|
+
providerDir: asOptionalString(parsed.providerDir),
|
|
83
|
+
terminalSizingMode: parsed.terminalSizingMode === "fit" ? "fit" : "measured"
|
|
83
84
|
};
|
|
84
85
|
}
|
|
85
86
|
function generateMachineId() {
|
|
@@ -223,7 +224,8 @@ var init_config = __esm({
|
|
|
223
224
|
registeredMachineId: void 0,
|
|
224
225
|
providerSettings: {},
|
|
225
226
|
ideSettings: {},
|
|
226
|
-
disableUpstream: false
|
|
227
|
+
disableUpstream: false,
|
|
228
|
+
terminalSizingMode: "measured"
|
|
227
229
|
};
|
|
228
230
|
MACHINE_ID_PREFIX = "mach_";
|
|
229
231
|
}
|
|
@@ -811,6 +813,53 @@ function stripTerminalNoise(str) {
|
|
|
811
813
|
function sanitizeTerminalText(str) {
|
|
812
814
|
return stripTerminalNoise(stripAnsi(str));
|
|
813
815
|
}
|
|
816
|
+
function splitCliScreenLines(text) {
|
|
817
|
+
return String(text || "").replace(/\u0007/g, "").replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n").map((line) => line.replace(/\s+$/, ""));
|
|
818
|
+
}
|
|
819
|
+
function isPromptLikeCliLine(line) {
|
|
820
|
+
const trimmed = String(line || "").trim();
|
|
821
|
+
if (!trimmed) return false;
|
|
822
|
+
return /^[❯›>]\s*(?:$|\S.*)$/.test(trimmed);
|
|
823
|
+
}
|
|
824
|
+
function buildCliScreenSnapshot(text) {
|
|
825
|
+
const normalizedText = String(text || "").replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
826
|
+
const rawLines = splitCliScreenLines(normalizedText);
|
|
827
|
+
const lines = rawLines.map((line, index, arr) => {
|
|
828
|
+
const trimmed = String(line || "").trim();
|
|
829
|
+
return {
|
|
830
|
+
index,
|
|
831
|
+
fromTop: index,
|
|
832
|
+
fromBottom: arr.length - index - 1,
|
|
833
|
+
text: line,
|
|
834
|
+
trimmed,
|
|
835
|
+
isEmpty: trimmed.length === 0
|
|
836
|
+
};
|
|
837
|
+
});
|
|
838
|
+
const nonEmptyLines = lines.filter((line) => !line.isEmpty);
|
|
839
|
+
const firstNonEmptyLine = nonEmptyLines[0] ?? null;
|
|
840
|
+
const lastNonEmptyLine = nonEmptyLines[nonEmptyLines.length - 1] ?? null;
|
|
841
|
+
let promptLineIndex = -1;
|
|
842
|
+
for (let i = lines.length - 1; i >= 0; i -= 1) {
|
|
843
|
+
if (isPromptLikeCliLine(lines[i].text)) {
|
|
844
|
+
promptLineIndex = i;
|
|
845
|
+
break;
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
return {
|
|
849
|
+
text: normalizedText,
|
|
850
|
+
lineCount: lines.length,
|
|
851
|
+
lines,
|
|
852
|
+
nonEmptyLines,
|
|
853
|
+
firstNonEmptyLineIndex: firstNonEmptyLine?.index ?? -1,
|
|
854
|
+
lastNonEmptyLineIndex: lastNonEmptyLine?.index ?? -1,
|
|
855
|
+
firstNonEmptyLine,
|
|
856
|
+
lastNonEmptyLine,
|
|
857
|
+
promptLineIndex,
|
|
858
|
+
promptLine: promptLineIndex >= 0 ? lines[promptLineIndex] : null,
|
|
859
|
+
linesAbovePrompt: promptLineIndex >= 0 ? lines.slice(0, promptLineIndex) : [...lines],
|
|
860
|
+
linesBelowPrompt: promptLineIndex >= 0 ? lines.slice(promptLineIndex + 1) : []
|
|
861
|
+
};
|
|
862
|
+
}
|
|
814
863
|
function computeTerminalQueryTail(buffer) {
|
|
815
864
|
const prefixes = ["\x1B[6n", "\x1B[?6n"];
|
|
816
865
|
const maxLength = prefixes.reduce((n, value) => Math.max(n, value.length), 0) - 1;
|
|
@@ -1049,7 +1098,9 @@ var init_provider_cli_adapter = __esm({
|
|
|
1049
1098
|
ready = false;
|
|
1050
1099
|
startupBuffer = "";
|
|
1051
1100
|
startupParseGate = false;
|
|
1101
|
+
startupSettleTimer = null;
|
|
1052
1102
|
spawnAt = 0;
|
|
1103
|
+
startupFirstOutputAt = 0;
|
|
1053
1104
|
// PTY I/O
|
|
1054
1105
|
onPtyDataCallback = null;
|
|
1055
1106
|
pendingOutputParseBuffer = "";
|
|
@@ -1090,6 +1141,7 @@ var init_provider_cli_adapter = __esm({
|
|
|
1090
1141
|
statusHistory = [];
|
|
1091
1142
|
// ─── CLI Scripts (script-based parsing) ───
|
|
1092
1143
|
cliScripts;
|
|
1144
|
+
runtimeSettings = {};
|
|
1093
1145
|
/** Full accumulated ANSI-stripped PTY output */
|
|
1094
1146
|
accumulatedBuffer = "";
|
|
1095
1147
|
/** Full accumulated raw PTY output (with ANSI) */
|
|
@@ -1104,14 +1156,15 @@ var init_provider_cli_adapter = __esm({
|
|
|
1104
1156
|
traceSessionId = `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
1105
1157
|
static MAX_TRACE_ENTRIES = 250;
|
|
1106
1158
|
providerResolutionMeta;
|
|
1107
|
-
static IDLE_FINISH_CONFIRM_MS =
|
|
1159
|
+
static IDLE_FINISH_CONFIRM_MS = 2e3;
|
|
1160
|
+
static STATUS_ACTIVITY_HOLD_MS = 2e3;
|
|
1108
1161
|
static FINISH_RETRY_DELAY_MS = 300;
|
|
1109
1162
|
static MAX_FINISH_RETRIES = 2;
|
|
1110
1163
|
syncMessageViews() {
|
|
1111
1164
|
this.messages = [...this.committedMessages];
|
|
1112
1165
|
this.structuredMessages = [...this.committedMessages];
|
|
1113
1166
|
}
|
|
1114
|
-
|
|
1167
|
+
hydrateParsedMessages(parsedMessages, scope) {
|
|
1115
1168
|
const referenceMessages = [...this.committedMessages];
|
|
1116
1169
|
const usedReferenceIndexes = /* @__PURE__ */ new Set();
|
|
1117
1170
|
const now = Date.now();
|
|
@@ -1144,13 +1197,30 @@ var init_provider_cli_adapter = __esm({
|
|
|
1144
1197
|
const content = typeof message.content === "string" ? message.content : String(message.content || "");
|
|
1145
1198
|
const parsedTimestamp = typeof message.timestamp === "number" && Number.isFinite(message.timestamp) ? message.timestamp : void 0;
|
|
1146
1199
|
const referenceTimestamp = parsedTimestamp ?? findReferenceTimestamp(role, content, index);
|
|
1200
|
+
const fallbackTimestamp = role === "user" ? scope?.startedAt || now : this.lastOutputAt || scope?.startedAt || now;
|
|
1201
|
+
const timestamp = referenceTimestamp ?? fallbackTimestamp;
|
|
1147
1202
|
return {
|
|
1203
|
+
...message,
|
|
1148
1204
|
role,
|
|
1149
1205
|
content,
|
|
1150
|
-
timestamp
|
|
1206
|
+
timestamp,
|
|
1207
|
+
receivedAt: typeof message.receivedAt === "number" && Number.isFinite(message.receivedAt) ? message.receivedAt : timestamp
|
|
1151
1208
|
};
|
|
1152
1209
|
});
|
|
1153
1210
|
}
|
|
1211
|
+
normalizeParsedMessages(parsedMessages, scope) {
|
|
1212
|
+
return this.hydrateParsedMessages(parsedMessages, scope).map((message) => ({
|
|
1213
|
+
role: message.role,
|
|
1214
|
+
content: message.content,
|
|
1215
|
+
timestamp: message.timestamp,
|
|
1216
|
+
receivedAt: message.receivedAt,
|
|
1217
|
+
kind: message.kind,
|
|
1218
|
+
id: message.id,
|
|
1219
|
+
index: message.index,
|
|
1220
|
+
meta: message.meta,
|
|
1221
|
+
senderName: message.senderName
|
|
1222
|
+
}));
|
|
1223
|
+
}
|
|
1154
1224
|
sliceFromOffset(text, start) {
|
|
1155
1225
|
if (!text) return "";
|
|
1156
1226
|
if (!Number.isFinite(start) || start <= 0) return text;
|
|
@@ -1160,14 +1230,20 @@ var init_provider_cli_adapter = __esm({
|
|
|
1160
1230
|
buildParseInput(baseMessages, partialResponse, scope) {
|
|
1161
1231
|
const buffer = scope ? this.sliceFromOffset(this.accumulatedBuffer, scope.bufferStart) || this.accumulatedBuffer : this.accumulatedBuffer;
|
|
1162
1232
|
const rawBuffer = scope ? this.sliceFromOffset(this.accumulatedRawBuffer, scope.rawBufferStart) || this.accumulatedRawBuffer : this.accumulatedRawBuffer;
|
|
1233
|
+
const screenText = this.terminalScreen.getText();
|
|
1234
|
+
const recentBuffer = buffer.slice(-1e3) || this.recentOutputBuffer;
|
|
1163
1235
|
return {
|
|
1164
1236
|
buffer,
|
|
1165
1237
|
rawBuffer,
|
|
1166
|
-
recentBuffer
|
|
1167
|
-
screenText
|
|
1238
|
+
recentBuffer,
|
|
1239
|
+
screenText,
|
|
1240
|
+
screen: buildCliScreenSnapshot(screenText),
|
|
1241
|
+
bufferScreen: buildCliScreenSnapshot(buffer),
|
|
1242
|
+
recentScreen: buildCliScreenSnapshot(recentBuffer),
|
|
1168
1243
|
messages: [...baseMessages],
|
|
1169
1244
|
partialResponse,
|
|
1170
|
-
promptText: scope?.prompt || ""
|
|
1245
|
+
promptText: scope?.prompt || "",
|
|
1246
|
+
settings: { ...this.runtimeSettings }
|
|
1171
1247
|
};
|
|
1172
1248
|
}
|
|
1173
1249
|
setStatus(status, trigger) {
|
|
@@ -1273,6 +1349,9 @@ var init_provider_cli_adapter = __esm({
|
|
|
1273
1349
|
const scriptNames = Object.keys(scripts).filter((k) => typeof scripts[k] === "function");
|
|
1274
1350
|
LOG.info("CLI", `[${this.cliType}] CLI scripts injected: [${scriptNames.join(", ")}]`);
|
|
1275
1351
|
}
|
|
1352
|
+
updateRuntimeSettings(settings) {
|
|
1353
|
+
this.runtimeSettings = { ...settings };
|
|
1354
|
+
}
|
|
1276
1355
|
// ─── Lifecycle ─────────────────────────────────
|
|
1277
1356
|
setServerConn(serverConn) {
|
|
1278
1357
|
this.serverConn = serverConn;
|
|
@@ -1309,7 +1388,7 @@ var init_provider_cli_adapter = __esm({
|
|
|
1309
1388
|
let shellArgs;
|
|
1310
1389
|
const useShellUnix = !isWin && (!!spawnConfig.shell || !path7.isAbsolute(binaryPath) || isScriptBinary(binaryPath) || !looksLikeMachOOrElf(binaryPath));
|
|
1311
1390
|
const isCmdShim = isWin && /\.(cmd|bat)$/i.test(binaryPath);
|
|
1312
|
-
const useShellWin = isCmdShim || !path7.isAbsolute(binaryPath) || isScriptBinary(binaryPath);
|
|
1391
|
+
const useShellWin = !!spawnConfig.shell || isCmdShim || !path7.isAbsolute(binaryPath) || isScriptBinary(binaryPath);
|
|
1313
1392
|
const useShell = isWin ? useShellWin : useShellUnix;
|
|
1314
1393
|
if (useShell) {
|
|
1315
1394
|
if (!spawnConfig.shell && !isWin) {
|
|
@@ -1407,6 +1486,11 @@ var init_provider_cli_adapter = __esm({
|
|
|
1407
1486
|
this.spawnAt = Date.now();
|
|
1408
1487
|
this.startupParseGate = true;
|
|
1409
1488
|
this.startupBuffer = "";
|
|
1489
|
+
this.startupFirstOutputAt = 0;
|
|
1490
|
+
if (this.startupSettleTimer) {
|
|
1491
|
+
clearTimeout(this.startupSettleTimer);
|
|
1492
|
+
this.startupSettleTimer = null;
|
|
1493
|
+
}
|
|
1410
1494
|
this.terminalScreen.reset(24, 80);
|
|
1411
1495
|
this.pendingTerminalQueryTail = "";
|
|
1412
1496
|
this.currentTurnScope = null;
|
|
@@ -1420,7 +1504,8 @@ var init_provider_cli_adapter = __esm({
|
|
|
1420
1504
|
this.recordTrace("ready", {
|
|
1421
1505
|
runtimeMeta: this.getRuntimeMetadata()
|
|
1422
1506
|
});
|
|
1423
|
-
this.setStatus("
|
|
1507
|
+
this.setStatus("starting", "pty_ready");
|
|
1508
|
+
this.scheduleStartupSettleCheck();
|
|
1424
1509
|
this.onStatusChange?.();
|
|
1425
1510
|
}
|
|
1426
1511
|
// ─── Output Handling ────────────────────────────
|
|
@@ -1435,6 +1520,9 @@ var init_provider_cli_adapter = __esm({
|
|
|
1435
1520
|
this.lastScreenSnapshot = normalizedScreenSnapshot;
|
|
1436
1521
|
this.lastScreenChangeAt = now;
|
|
1437
1522
|
}
|
|
1523
|
+
if (this.startupParseGate && !this.startupFirstOutputAt && (cleanData.trim() || normalizedScreenSnapshot.trim())) {
|
|
1524
|
+
this.startupFirstOutputAt = now;
|
|
1525
|
+
}
|
|
1438
1526
|
if (this.idleFinishCandidate && (rawData.length > 0 || cleanData.length > 0)) {
|
|
1439
1527
|
this.clearIdleFinishCandidate("new_output");
|
|
1440
1528
|
}
|
|
@@ -1445,6 +1533,9 @@ var init_provider_cli_adapter = __esm({
|
|
|
1445
1533
|
cleanPreview: this.summarizeTraceText(cleanData, 300),
|
|
1446
1534
|
screenText: this.summarizeTraceText(this.terminalScreen.getText(), 1200)
|
|
1447
1535
|
});
|
|
1536
|
+
if (this.startupParseGate) {
|
|
1537
|
+
this.scheduleStartupSettleCheck();
|
|
1538
|
+
}
|
|
1448
1539
|
if (this.isWaitingForResponse && cleanData) {
|
|
1449
1540
|
this.responseBuffer = (this.responseBuffer + cleanData).slice(-8e3);
|
|
1450
1541
|
}
|
|
@@ -1458,27 +1549,51 @@ var init_provider_cli_adapter = __esm({
|
|
|
1458
1549
|
this.recentOutputBuffer = (this.recentOutputBuffer + cleanData).slice(-1e3);
|
|
1459
1550
|
this.accumulatedBuffer = (this.accumulatedBuffer + cleanData).slice(-_ProviderCliAdapter.MAX_ACCUMULATED_BUFFER);
|
|
1460
1551
|
this.accumulatedRawBuffer = (this.accumulatedRawBuffer + rawData).slice(-_ProviderCliAdapter.MAX_ACCUMULATED_BUFFER);
|
|
1461
|
-
|
|
1462
|
-
this.startupBuffer += cleanData;
|
|
1463
|
-
const elapsed = Date.now() - this.spawnAt;
|
|
1464
|
-
const screenText = this.terminalScreen.getText() || "";
|
|
1465
|
-
const startupModal = this.getStartupConfirmationModal(screenText);
|
|
1466
|
-
const scriptStatus = startupModal ? "waiting_approval" : this.runDetectStatus(this.startupBuffer);
|
|
1467
|
-
const hasInteractivePrompt = this.looksLikeVisibleIdlePrompt(screenText);
|
|
1468
|
-
const startupStableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : 0;
|
|
1469
|
-
const isReady = (scriptStatus === "idle" || scriptStatus === "waiting_approval") && hasInteractivePrompt && startupStableMs >= 700 || !!startupModal && startupStableMs >= 700 || elapsed > 8e3 || this.startupBuffer.length > 12e3;
|
|
1470
|
-
if (isReady) {
|
|
1471
|
-
this.startupParseGate = false;
|
|
1472
|
-
this.ready = true;
|
|
1473
|
-
LOG.info(
|
|
1474
|
-
"CLI",
|
|
1475
|
-
`[${this.cliType}] Startup ready (${elapsed}ms, scriptStatus=${scriptStatus}, prompt=${hasInteractivePrompt}, stableMs=${startupStableMs}) providerDir=${this.providerResolutionMeta.providerDir || "-"} scriptDir=${this.providerResolutionMeta.scriptDir || "-"} scriptsPath=${this.providerResolutionMeta.scriptsPath || "-"}`
|
|
1476
|
-
);
|
|
1477
|
-
this.onStatusChange?.();
|
|
1478
|
-
}
|
|
1479
|
-
}
|
|
1552
|
+
this.resolveStartupState("output");
|
|
1480
1553
|
this.scheduleSettle();
|
|
1481
1554
|
}
|
|
1555
|
+
resolveStartupState(trigger) {
|
|
1556
|
+
if (!this.startupParseGate) return;
|
|
1557
|
+
const now = Date.now();
|
|
1558
|
+
const screenText = this.terminalScreen.getText() || "";
|
|
1559
|
+
const normalizedScreen = normalizeScreenSnapshot(screenText);
|
|
1560
|
+
const hasStartupOutput = !!this.startupFirstOutputAt || !!normalizedScreen.trim();
|
|
1561
|
+
if (!hasStartupOutput) return;
|
|
1562
|
+
const stableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : 0;
|
|
1563
|
+
if (stableMs < 2e3) return;
|
|
1564
|
+
const startupModal = this.getStartupConfirmationModal(screenText);
|
|
1565
|
+
this.startupParseGate = false;
|
|
1566
|
+
if (this.startupSettleTimer) {
|
|
1567
|
+
clearTimeout(this.startupSettleTimer);
|
|
1568
|
+
this.startupSettleTimer = null;
|
|
1569
|
+
}
|
|
1570
|
+
this.ready = true;
|
|
1571
|
+
if (startupModal) {
|
|
1572
|
+
this.activeModal = startupModal;
|
|
1573
|
+
this.setStatus("waiting_approval", `startup_ready:${trigger}`);
|
|
1574
|
+
} else {
|
|
1575
|
+
this.setStatus("idle", `startup_ready:${trigger}`);
|
|
1576
|
+
}
|
|
1577
|
+
LOG.info(
|
|
1578
|
+
"CLI",
|
|
1579
|
+
`[${this.cliType}] Startup settled (${trigger}, stableMs=${stableMs}, modal=${!!startupModal}) providerDir=${this.providerResolutionMeta.providerDir || "-"} scriptDir=${this.providerResolutionMeta.scriptDir || "-"} scriptsPath=${this.providerResolutionMeta.scriptsPath || "-"}`
|
|
1580
|
+
);
|
|
1581
|
+
this.onStatusChange?.();
|
|
1582
|
+
}
|
|
1583
|
+
scheduleStartupSettleCheck() {
|
|
1584
|
+
if (!this.startupParseGate) return;
|
|
1585
|
+
if (this.startupSettleTimer) clearTimeout(this.startupSettleTimer);
|
|
1586
|
+
const now = Date.now();
|
|
1587
|
+
const stableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : 0;
|
|
1588
|
+
const delayMs = Math.max(250, 2050 - stableMs);
|
|
1589
|
+
this.startupSettleTimer = setTimeout(() => {
|
|
1590
|
+
this.startupSettleTimer = null;
|
|
1591
|
+
this.resolveStartupState("startup_timer");
|
|
1592
|
+
if (this.startupParseGate && Date.now() - this.spawnAt < 1e4) {
|
|
1593
|
+
this.scheduleStartupSettleCheck();
|
|
1594
|
+
}
|
|
1595
|
+
}, delayMs);
|
|
1596
|
+
}
|
|
1482
1597
|
scheduleSettle() {
|
|
1483
1598
|
if (this.settleTimer) clearTimeout(this.settleTimer);
|
|
1484
1599
|
const settleEpoch = this.responseEpoch;
|
|
@@ -1520,6 +1635,43 @@ var init_provider_cli_adapter = __esm({
|
|
|
1520
1635
|
if (!text.trim()) return false;
|
|
1521
1636
|
return /(^|\n)\s*[❯›>]\s*(?:\n|$)/m.test(text) || /⏎\s+send/i.test(text) || /\?\s*for\s*shortcuts/i.test(text) || /Type your message(?:\s+or\s+@path\/to\/file)?/i.test(text) || /workspace\s*\(\/directory\)/i.test(text) || /for\s*shortcuts/i.test(text);
|
|
1522
1637
|
}
|
|
1638
|
+
findLastMatchingLineIndex(lines, predicate) {
|
|
1639
|
+
for (let index = lines.length - 1; index >= 0; index -= 1) {
|
|
1640
|
+
if (predicate(lines[index])) return index;
|
|
1641
|
+
}
|
|
1642
|
+
return -1;
|
|
1643
|
+
}
|
|
1644
|
+
looksLikeClaudeGeneratingLine(line) {
|
|
1645
|
+
const trimmed = String(line || "").trim();
|
|
1646
|
+
if (!trimmed) return false;
|
|
1647
|
+
if (/esc to (cancel|interrupt|stop)/i.test(trimmed)) return true;
|
|
1648
|
+
if (/^[✻✶✳✢✽⠂⠐⠒⠓⠦⠴⠶⠷⠿]+\s+\S+.*\b(?:thinking|thought for \d+s?)\b/i.test(trimmed)) return true;
|
|
1649
|
+
if (/^[✻✶✳✢✽⠂⠐⠒⠓⠦⠴⠶⠷⠿]+\s+[A-Z][A-Za-z-]{3,}ing\b.*(?:…|\.{3})/u.test(trimmed)) return true;
|
|
1650
|
+
if (/^[⏺•]\s+(?:Reading|Writing|Editing|Searching|Inspecting|Planning|Analyzing|Synthesizing|Drafting|Running|Listing|Scanning|Matching)\b.*(?:…|\.{3})/i.test(trimmed)) {
|
|
1651
|
+
return /ctrl\+o to expand/i.test(trimmed) || /\b\d+\s+(?:file|files|pattern|patterns|director(?:y|ies)|match|matches|result|results)\b/i.test(trimmed);
|
|
1652
|
+
}
|
|
1653
|
+
return false;
|
|
1654
|
+
}
|
|
1655
|
+
detectClaudeGeneratingOverride(screenText, tail) {
|
|
1656
|
+
if (this.cliType !== "claude-cli") return false;
|
|
1657
|
+
const source = sanitizeTerminalText(screenText || tail || "");
|
|
1658
|
+
if (!source.trim()) return false;
|
|
1659
|
+
const allLines = source.split(/\r\n|\n|\r/g).map((line) => line.trim()).filter(Boolean);
|
|
1660
|
+
if (allLines.length === 0) return false;
|
|
1661
|
+
const recentLines = allLines.slice(-12);
|
|
1662
|
+
const promptIndex = this.findLastMatchingLineIndex(recentLines, (line) => /^[❯›>]\s*$/.test(line));
|
|
1663
|
+
const activeRegion = promptIndex >= 0 ? recentLines.slice(Math.max(0, promptIndex - 2), promptIndex) : recentLines;
|
|
1664
|
+
if (activeRegion.length === 0) return false;
|
|
1665
|
+
return activeRegion.some((line) => this.looksLikeClaudeGeneratingLine(line));
|
|
1666
|
+
}
|
|
1667
|
+
refineDetectedStatus(status, tail, screenText) {
|
|
1668
|
+
if (this.startupParseGate) {
|
|
1669
|
+
return this.getStartupConfirmationModal(screenText || "") ? "waiting_approval" : "starting";
|
|
1670
|
+
}
|
|
1671
|
+
if (status === "waiting_approval") return status;
|
|
1672
|
+
if (this.detectClaudeGeneratingOverride(screenText || "", tail)) return "generating";
|
|
1673
|
+
return status;
|
|
1674
|
+
}
|
|
1523
1675
|
looksLikeVisibleAssistantCandidate(screenText) {
|
|
1524
1676
|
const lines = sanitizeTerminalText(String(screenText || "")).split(/\r\n|\n|\r/g);
|
|
1525
1677
|
for (const line of lines) {
|
|
@@ -1554,6 +1706,11 @@ var init_provider_cli_adapter = __esm({
|
|
|
1554
1706
|
const screenStableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : 0;
|
|
1555
1707
|
return quietForMs < 1200 || screenStableMs < 1200 || !commitResult.hasAssistant;
|
|
1556
1708
|
}
|
|
1709
|
+
hasRecentInteractiveActivity(now) {
|
|
1710
|
+
const quietForMs = this.lastNonEmptyOutputAt ? now - this.lastNonEmptyOutputAt : Number.MAX_SAFE_INTEGER;
|
|
1711
|
+
const screenStableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : Number.MAX_SAFE_INTEGER;
|
|
1712
|
+
return quietForMs < _ProviderCliAdapter.STATUS_ACTIVITY_HOLD_MS || screenStableMs < _ProviderCliAdapter.STATUS_ACTIVITY_HOLD_MS;
|
|
1713
|
+
}
|
|
1557
1714
|
getStartupConfirmationModal(screenText) {
|
|
1558
1715
|
const text = sanitizeTerminalText(String(screenText || ""));
|
|
1559
1716
|
if (!text.trim()) return null;
|
|
@@ -1581,13 +1738,14 @@ var init_provider_cli_adapter = __esm({
|
|
|
1581
1738
|
const startedAt = Date.now();
|
|
1582
1739
|
let loggedWait = false;
|
|
1583
1740
|
while (Date.now() - startedAt < maxWaitMs) {
|
|
1741
|
+
this.resolveStartupState("interactive_wait");
|
|
1584
1742
|
const screenText = this.terminalScreen.getText() || "";
|
|
1585
1743
|
const hasPrompt = this.looksLikeVisibleIdlePrompt(screenText);
|
|
1586
1744
|
const stableMs = this.lastScreenChangeAt ? Date.now() - this.lastScreenChangeAt : 0;
|
|
1587
1745
|
const recentlyOutput = this.lastNonEmptyOutputAt ? Date.now() - this.lastNonEmptyOutputAt : Number.MAX_SAFE_INTEGER;
|
|
1588
1746
|
const status = this.runDetectStatus(this.recentOutputBuffer) || this.currentStatus;
|
|
1589
1747
|
const startupLikelyActive = /Welcome back|Tips for getting|Recent activity|Claude Code v\d/i.test(screenText);
|
|
1590
|
-
const interactiveReady = hasPrompt && stableMs >= 700 && recentlyOutput >= 350 && status !== "
|
|
1748
|
+
const interactiveReady = hasPrompt && stableMs >= 700 && recentlyOutput >= 350 && status !== "generating";
|
|
1591
1749
|
if (interactiveReady) {
|
|
1592
1750
|
if (loggedWait) {
|
|
1593
1751
|
LOG.info(
|
|
@@ -1626,6 +1784,10 @@ var init_provider_cli_adapter = __esm({
|
|
|
1626
1784
|
}
|
|
1627
1785
|
const tail = this.settledBuffer;
|
|
1628
1786
|
const screenText = this.terminalScreen.getText() || "";
|
|
1787
|
+
this.resolveStartupState("settled");
|
|
1788
|
+
if (this.startupParseGate) {
|
|
1789
|
+
return;
|
|
1790
|
+
}
|
|
1629
1791
|
const startupModal = this.getStartupConfirmationModal(screenText);
|
|
1630
1792
|
const modal = this.runParseApproval(tail) || startupModal;
|
|
1631
1793
|
const rawScriptStatus = this.runDetectStatus(tail);
|
|
@@ -1688,6 +1850,28 @@ var init_provider_cli_adapter = __esm({
|
|
|
1688
1850
|
} else {
|
|
1689
1851
|
clearPendingScriptStatus();
|
|
1690
1852
|
}
|
|
1853
|
+
const recentInteractiveActivity = this.hasRecentInteractiveActivity(now);
|
|
1854
|
+
const shouldHoldGenerating = scriptStatus === "idle" && this.isWaitingForResponse && !modal && recentInteractiveActivity;
|
|
1855
|
+
if (shouldHoldGenerating) {
|
|
1856
|
+
this.clearIdleFinishCandidate("hold_generating_recent_activity");
|
|
1857
|
+
this.setStatus("generating", "recent_activity_hold");
|
|
1858
|
+
if (this.idleTimeout) clearTimeout(this.idleTimeout);
|
|
1859
|
+
this.idleTimeout = setTimeout(() => {
|
|
1860
|
+
if (this.isWaitingForResponse && this.currentStatus !== "waiting_approval") {
|
|
1861
|
+
this.finishResponse();
|
|
1862
|
+
}
|
|
1863
|
+
}, this.timeouts.generatingIdle);
|
|
1864
|
+
this.recordTrace("hold_generating_recent_activity", {
|
|
1865
|
+
scriptStatus,
|
|
1866
|
+
recentInteractiveActivity,
|
|
1867
|
+
lastNonEmptyOutputAt: this.lastNonEmptyOutputAt,
|
|
1868
|
+
lastScreenChangeAt: this.lastScreenChangeAt,
|
|
1869
|
+
holdMs: _ProviderCliAdapter.STATUS_ACTIVITY_HOLD_MS,
|
|
1870
|
+
...this.buildTraceParseSnapshot(this.currentTurnScope, this.responseBuffer)
|
|
1871
|
+
});
|
|
1872
|
+
this.onStatusChange?.();
|
|
1873
|
+
return;
|
|
1874
|
+
}
|
|
1691
1875
|
if (scriptStatus === "waiting_approval") {
|
|
1692
1876
|
this.clearIdleFinishCandidate("waiting_approval");
|
|
1693
1877
|
const inCooldown = this.lastApprovalResolvedAt && Date.now() - this.lastApprovalResolvedAt < this.timeouts.approvalCooldown;
|
|
@@ -1765,8 +1949,8 @@ var init_provider_cli_adapter = __esm({
|
|
|
1765
1949
|
const screenStableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : 0;
|
|
1766
1950
|
const hasAssistantTurn = !!lastParsedAssistant;
|
|
1767
1951
|
const assistantLength = lastParsedAssistant?.content?.length || 0;
|
|
1768
|
-
const idleQuietThresholdMs = Math.max(
|
|
1769
|
-
const idleStableThresholdMs =
|
|
1952
|
+
const idleQuietThresholdMs = Math.max(2e3, this.timeouts.outputSettle);
|
|
1953
|
+
const idleStableThresholdMs = 2e3;
|
|
1770
1954
|
const idleReady = visibleIdlePrompt && !modal && hasAssistantTurn && quietForMs >= idleQuietThresholdMs && screenStableMs >= idleStableThresholdMs;
|
|
1771
1955
|
const candidate = this.idleFinishCandidate;
|
|
1772
1956
|
const candidateQuiet = !!candidate && candidate.responseEpoch === this.responseEpoch && candidate.lastOutputAt === this.lastOutputAt && candidate.lastScreenChangeAt === this.lastScreenChangeAt && assistantLength >= candidate.assistantLength && now - candidate.armedAt >= _ProviderCliAdapter.IDLE_FINISH_CONFIRM_MS;
|
|
@@ -1880,7 +2064,7 @@ var init_provider_cli_adapter = __esm({
|
|
|
1880
2064
|
this.currentTurnScope
|
|
1881
2065
|
);
|
|
1882
2066
|
if (parsed && Array.isArray(parsed.messages)) {
|
|
1883
|
-
this.committedMessages = this.normalizeParsedMessages(parsed.messages);
|
|
2067
|
+
this.committedMessages = this.normalizeParsedMessages(parsed.messages, this.currentTurnScope);
|
|
1884
2068
|
const promptForTrim = this.currentTurnScope?.prompt || getLastUserPromptText(this.committedMessages);
|
|
1885
2069
|
if (promptForTrim) {
|
|
1886
2070
|
const lastAssistantForTrim = [...this.committedMessages].reverse().find((message) => message.role === "assistant");
|
|
@@ -1917,11 +2101,15 @@ var init_provider_cli_adapter = __esm({
|
|
|
1917
2101
|
runDetectStatus(text) {
|
|
1918
2102
|
if (!this.cliScripts?.detectStatus) return null;
|
|
1919
2103
|
try {
|
|
1920
|
-
|
|
2104
|
+
const screenText = this.terminalScreen.getText();
|
|
2105
|
+
const status = this.cliScripts.detectStatus({
|
|
1921
2106
|
tail: text.slice(-500),
|
|
1922
|
-
screenText
|
|
1923
|
-
rawBuffer: this.accumulatedRawBuffer
|
|
2107
|
+
screenText,
|
|
2108
|
+
rawBuffer: this.accumulatedRawBuffer,
|
|
2109
|
+
screen: buildCliScreenSnapshot(screenText),
|
|
2110
|
+
tailScreen: buildCliScreenSnapshot(text.slice(-500))
|
|
1924
2111
|
});
|
|
2112
|
+
return this.refineDetectedStatus(status, text, screenText || "");
|
|
1925
2113
|
} catch (e) {
|
|
1926
2114
|
LOG.warn("CLI", `[${this.cliType}] detectStatus error: ${e.message}`);
|
|
1927
2115
|
return null;
|
|
@@ -1930,11 +2118,16 @@ var init_provider_cli_adapter = __esm({
|
|
|
1930
2118
|
runParseApproval(tail) {
|
|
1931
2119
|
if (!this.cliScripts?.parseApproval) return null;
|
|
1932
2120
|
try {
|
|
2121
|
+
const screenText = this.terminalScreen.getText();
|
|
2122
|
+
const buffer = screenText || this.accumulatedBuffer;
|
|
1933
2123
|
return this.cliScripts.parseApproval({
|
|
1934
|
-
buffer
|
|
1935
|
-
screenText
|
|
2124
|
+
buffer,
|
|
2125
|
+
screenText,
|
|
1936
2126
|
rawBuffer: this.accumulatedRawBuffer,
|
|
1937
|
-
tail
|
|
2127
|
+
tail,
|
|
2128
|
+
screen: buildCliScreenSnapshot(screenText),
|
|
2129
|
+
bufferScreen: buildCliScreenSnapshot(buffer),
|
|
2130
|
+
tailScreen: buildCliScreenSnapshot(tail)
|
|
1938
2131
|
});
|
|
1939
2132
|
} catch (e) {
|
|
1940
2133
|
LOG.warn("CLI", `[${this.cliType}] parseApproval error: ${e.message}`);
|
|
@@ -1950,6 +2143,21 @@ var init_provider_cli_adapter = __esm({
|
|
|
1950
2143
|
activeModal: this.activeModal
|
|
1951
2144
|
};
|
|
1952
2145
|
}
|
|
2146
|
+
seedCommittedMessages(messages) {
|
|
2147
|
+
const normalized = (Array.isArray(messages) ? messages : []).filter((message) => message && (message.role === "user" || message.role === "assistant")).map((message) => ({
|
|
2148
|
+
role: message.role,
|
|
2149
|
+
content: typeof message.content === "string" ? message.content : String(message.content || ""),
|
|
2150
|
+
timestamp: typeof message.timestamp === "number" && Number.isFinite(message.timestamp) ? message.timestamp : void 0,
|
|
2151
|
+
receivedAt: typeof message.receivedAt === "number" && Number.isFinite(message.receivedAt) ? message.receivedAt : void 0,
|
|
2152
|
+
kind: typeof message.kind === "string" ? message.kind : void 0,
|
|
2153
|
+
id: typeof message.id === "string" ? message.id : void 0,
|
|
2154
|
+
index: typeof message.index === "number" ? message.index : void 0,
|
|
2155
|
+
meta: message.meta && typeof message.meta === "object" ? { ...message.meta } : void 0,
|
|
2156
|
+
senderName: typeof message.senderName === "string" ? message.senderName : void 0
|
|
2157
|
+
}));
|
|
2158
|
+
this.committedMessages = normalized;
|
|
2159
|
+
this.syncMessageViews();
|
|
2160
|
+
}
|
|
1953
2161
|
/**
|
|
1954
2162
|
* Script-based full parse — returns ReadChatResult.
|
|
1955
2163
|
* Called by command handler / dashboard for rich content rendering.
|
|
@@ -1960,12 +2168,20 @@ var init_provider_cli_adapter = __esm({
|
|
|
1960
2168
|
this.responseBuffer,
|
|
1961
2169
|
this.currentTurnScope
|
|
1962
2170
|
);
|
|
2171
|
+
const shouldPreferCommittedMessages = !this.currentTurnScope && this.currentStatus === "idle" && !this.activeModal;
|
|
1963
2172
|
if (parsed && Array.isArray(parsed.messages)) {
|
|
2173
|
+
const hydratedMessages = shouldPreferCommittedMessages ? this.committedMessages.map((message, index) => ({
|
|
2174
|
+
...message,
|
|
2175
|
+
id: message.id || `msg_${index}`,
|
|
2176
|
+
index: typeof message.index === "number" ? message.index : index,
|
|
2177
|
+
kind: message.kind || "standard",
|
|
2178
|
+
receivedAt: typeof message.receivedAt === "number" ? message.receivedAt : message.timestamp
|
|
2179
|
+
})) : this.hydrateParsedMessages(parsed.messages, this.currentTurnScope);
|
|
1964
2180
|
return {
|
|
1965
2181
|
id: parsed.id || "cli_session",
|
|
1966
2182
|
status: parsed.status || this.currentStatus,
|
|
1967
2183
|
title: parsed.title || this.cliName,
|
|
1968
|
-
messages:
|
|
2184
|
+
messages: hydratedMessages,
|
|
1969
2185
|
activeModal: parsed.activeModal ?? this.activeModal,
|
|
1970
2186
|
providerSessionId: typeof parsed.providerSessionId === "string" ? parsed.providerSessionId : void 0
|
|
1971
2187
|
};
|
|
@@ -1986,11 +2202,30 @@ var init_provider_cli_adapter = __esm({
|
|
|
1986
2202
|
activeModal: this.activeModal
|
|
1987
2203
|
};
|
|
1988
2204
|
}
|
|
2205
|
+
async invokeScript(scriptName, args) {
|
|
2206
|
+
const fn = this.cliScripts?.[scriptName];
|
|
2207
|
+
if (typeof fn !== "function") {
|
|
2208
|
+
throw new Error(`CLI script '${scriptName}' not available`);
|
|
2209
|
+
}
|
|
2210
|
+
const input = this.buildParseInput(
|
|
2211
|
+
this.committedMessages,
|
|
2212
|
+
this.responseBuffer,
|
|
2213
|
+
this.currentTurnScope
|
|
2214
|
+
);
|
|
2215
|
+
return await Promise.resolve(fn({
|
|
2216
|
+
...input,
|
|
2217
|
+
args: args && typeof args === "object" ? { ...args } : {}
|
|
2218
|
+
}));
|
|
2219
|
+
}
|
|
1989
2220
|
parseCurrentTranscript(baseMessages, partialResponse, scope) {
|
|
1990
2221
|
if (!this.cliScripts?.parseOutput) return null;
|
|
1991
2222
|
try {
|
|
1992
2223
|
const input = this.buildParseInput(baseMessages, partialResponse, scope);
|
|
1993
2224
|
const parsed = this.cliScripts.parseOutput(input);
|
|
2225
|
+
const refinedStatus = this.refineDetectedStatus(typeof parsed?.status === "string" ? parsed.status : null, input.recentBuffer, input.screenText);
|
|
2226
|
+
if (parsed && refinedStatus && parsed.status !== refinedStatus) {
|
|
2227
|
+
parsed.status = refinedStatus;
|
|
2228
|
+
}
|
|
1994
2229
|
const promptForTrim = scope?.prompt || getLastUserPromptText(baseMessages);
|
|
1995
2230
|
if (parsed && Array.isArray(parsed.messages) && promptForTrim) {
|
|
1996
2231
|
const lastAssistant = [...parsed.messages].reverse().find((message) => message?.role === "assistant" && typeof message.content === "string");
|
|
@@ -2037,12 +2272,23 @@ ${data.message || ""}`.trim();
|
|
|
2037
2272
|
if (this.startupParseGate) {
|
|
2038
2273
|
const deadline = Date.now() + 1e4;
|
|
2039
2274
|
while (this.startupParseGate && Date.now() < deadline) {
|
|
2275
|
+
this.resolveStartupState("send_wait");
|
|
2040
2276
|
await new Promise((resolve9) => setTimeout(resolve9, 50));
|
|
2041
2277
|
}
|
|
2042
2278
|
}
|
|
2279
|
+
await this.waitForInteractivePrompt();
|
|
2280
|
+
if (!this.ready) {
|
|
2281
|
+
this.resolveStartupState("send_precheck");
|
|
2282
|
+
const screenText = this.terminalScreen.getText() || "";
|
|
2283
|
+
const hasPrompt = this.looksLikeVisibleIdlePrompt(screenText);
|
|
2284
|
+
if (hasPrompt && this.currentStatus === "idle") {
|
|
2285
|
+
this.ready = true;
|
|
2286
|
+
this.startupParseGate = false;
|
|
2287
|
+
LOG.info("CLI", `[${this.cliType}] sendMessage recovered idle prompt readiness`);
|
|
2288
|
+
}
|
|
2289
|
+
}
|
|
2043
2290
|
if (!this.ready) throw new Error(`${this.cliName} not ready (status: ${this.currentStatus})`);
|
|
2044
2291
|
if (this.isWaitingForResponse) return;
|
|
2045
|
-
await this.waitForInteractivePrompt();
|
|
2046
2292
|
const blockingModal = this.activeModal || this.getStartupConfirmationModal(this.terminalScreen.getText() || "");
|
|
2047
2293
|
if (blockingModal || this.currentStatus === "waiting_approval") {
|
|
2048
2294
|
throw new Error(`${this.cliName} is awaiting confirmation before it can accept a prompt`);
|
|
@@ -2086,8 +2332,6 @@ ${data.message || ""}`.trim();
|
|
|
2086
2332
|
}
|
|
2087
2333
|
this.responseEpoch += 1;
|
|
2088
2334
|
this.responseSettleIgnoreUntil = Date.now() + submitDelayMs + this.timeouts.outputSettle + 250;
|
|
2089
|
-
this.setStatus("generating", "sendMessage");
|
|
2090
|
-
this.onStatusChange?.();
|
|
2091
2335
|
const startResponseTimeout = () => {
|
|
2092
2336
|
if (this.responseTimeout) clearTimeout(this.responseTimeout);
|
|
2093
2337
|
this.responseTimeout = setTimeout(() => {
|
|
@@ -2106,7 +2350,7 @@ ${data.message || ""}`.trim();
|
|
|
2106
2350
|
const retrySubmitIfStuck = (attempt) => {
|
|
2107
2351
|
this.submitRetryTimer = null;
|
|
2108
2352
|
if (!this.ptyProcess || !this.isWaitingForResponse || this.submitRetryUsed) return;
|
|
2109
|
-
if (this.currentStatus
|
|
2353
|
+
if (this.currentStatus === "waiting_approval") return;
|
|
2110
2354
|
if ((this.responseBuffer || "").trim()) return;
|
|
2111
2355
|
const screenText = this.terminalScreen.getText();
|
|
2112
2356
|
if (!promptLikelyVisible(screenText, normalizedPromptSnippet)) return;
|
|
@@ -2141,7 +2385,7 @@ ${data.message || ""}`.trim();
|
|
|
2141
2385
|
this.submitRetryTimer = setTimeout(() => {
|
|
2142
2386
|
this.submitRetryTimer = null;
|
|
2143
2387
|
if (!this.ptyProcess || !this.isWaitingForResponse || this.submitRetryUsed) return;
|
|
2144
|
-
if (this.currentStatus
|
|
2388
|
+
if (this.currentStatus === "waiting_approval") return;
|
|
2145
2389
|
if ((this.responseBuffer || "").trim()) return;
|
|
2146
2390
|
const screenText = this.terminalScreen.getText();
|
|
2147
2391
|
if (!promptLikelyVisible(screenText, normalizedPromptSnippet)) return;
|
|
@@ -4497,199 +4741,94 @@ var StatusMonitor = class {
|
|
|
4497
4741
|
}
|
|
4498
4742
|
};
|
|
4499
4743
|
|
|
4500
|
-
// src/providers/
|
|
4501
|
-
|
|
4502
|
-
|
|
4503
|
-
|
|
4504
|
-
|
|
4505
|
-
|
|
4506
|
-
|
|
4507
|
-
|
|
4508
|
-
|
|
4509
|
-
|
|
4510
|
-
|
|
4511
|
-
|
|
4512
|
-
|
|
4513
|
-
|
|
4514
|
-
|
|
4515
|
-
|
|
4516
|
-
|
|
4517
|
-
|
|
4518
|
-
|
|
4519
|
-
|
|
4520
|
-
|
|
4521
|
-
|
|
4522
|
-
|
|
4523
|
-
|
|
4524
|
-
|
|
4525
|
-
|
|
4526
|
-
|
|
4527
|
-
|
|
4528
|
-
|
|
4529
|
-
|
|
4530
|
-
|
|
4531
|
-
|
|
4532
|
-
|
|
4533
|
-
|
|
4534
|
-
|
|
4535
|
-
|
|
4536
|
-
|
|
4537
|
-
|
|
4538
|
-
|
|
4539
|
-
|
|
4540
|
-
|
|
4541
|
-
|
|
4542
|
-
|
|
4543
|
-
|
|
4544
|
-
}
|
|
4545
|
-
getState() {
|
|
4546
|
-
return {
|
|
4547
|
-
type: this.type,
|
|
4548
|
-
name: this.provider.name,
|
|
4549
|
-
category: "extension",
|
|
4550
|
-
status: this.currentStatus,
|
|
4551
|
-
activeChat: this.messages.length > 0 ? {
|
|
4552
|
-
id: this.chatId || this.instanceId,
|
|
4553
|
-
title: this.chatTitle || this.agentName || this.provider.name,
|
|
4554
|
-
status: this.currentStatus,
|
|
4555
|
-
messages: this.messages,
|
|
4556
|
-
activeModal: this.activeModal,
|
|
4557
|
-
inputContent: ""
|
|
4558
|
-
} : null,
|
|
4559
|
-
currentModel: this.currentModel || void 0,
|
|
4560
|
-
currentPlan: this.currentMode || void 0,
|
|
4561
|
-
controlValues: this.controlValues,
|
|
4562
|
-
providerControls: this.provider.controls,
|
|
4563
|
-
agentStreams: this.agentStreams,
|
|
4564
|
-
instanceId: this.instanceId,
|
|
4565
|
-
lastUpdated: Date.now(),
|
|
4566
|
-
settings: this.settings,
|
|
4567
|
-
pendingEvents: this.flushEvents()
|
|
4568
|
-
};
|
|
4569
|
-
}
|
|
4570
|
-
onEvent(event, data) {
|
|
4571
|
-
if (event === "stream_update") {
|
|
4572
|
-
if (data?.streams) this.agentStreams = data.streams;
|
|
4573
|
-
if (data?.messages) this.messages = data.messages;
|
|
4574
|
-
if (data?.activeModal !== void 0) this.activeModal = data.activeModal;
|
|
4575
|
-
if (data?.model) this.currentModel = data.model;
|
|
4576
|
-
if (data?.mode) this.currentMode = data.mode;
|
|
4577
|
-
if (data?.controlValues) this.controlValues = data.controlValues;
|
|
4578
|
-
if (typeof data?.sessionId === "string" && data.sessionId.trim()) this.chatId = data.sessionId;
|
|
4579
|
-
if (typeof data?.title === "string" && data.title.trim()) this.chatTitle = data.title;
|
|
4580
|
-
if (typeof data?.agentName === "string" && data.agentName.trim()) this.agentName = data.agentName;
|
|
4581
|
-
if (typeof data?.extensionId === "string" && data.extensionId.trim()) this.extensionId = data.extensionId;
|
|
4582
|
-
if (data?.status) {
|
|
4583
|
-
const newStatus = data.status;
|
|
4584
|
-
this.detectTransition(newStatus, data);
|
|
4585
|
-
this.currentStatus = newStatus;
|
|
4586
|
-
}
|
|
4587
|
-
} else if (event === "stream_reset") {
|
|
4588
|
-
this.resetStreamState();
|
|
4589
|
-
} else if (event === "extension_connected") {
|
|
4590
|
-
this.ideType = data?.ideType || "";
|
|
4744
|
+
// src/providers/control-effects.ts
|
|
4745
|
+
function extractProviderControlValues(controls, data) {
|
|
4746
|
+
if (!data || typeof data !== "object") return void 0;
|
|
4747
|
+
const values = {};
|
|
4748
|
+
const explicit = data.controlValues;
|
|
4749
|
+
if (explicit && typeof explicit === "object") {
|
|
4750
|
+
for (const [key, value] of Object.entries(explicit)) {
|
|
4751
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
4752
|
+
values[key] = value;
|
|
4753
|
+
}
|
|
4754
|
+
}
|
|
4755
|
+
}
|
|
4756
|
+
for (const ctrl of controls || []) {
|
|
4757
|
+
if (!ctrl.readFrom) continue;
|
|
4758
|
+
const rawValue = data[ctrl.readFrom];
|
|
4759
|
+
if (rawValue === void 0 || rawValue === null) continue;
|
|
4760
|
+
values[ctrl.id] = normalizeControlValue(rawValue);
|
|
4761
|
+
}
|
|
4762
|
+
if (data.model !== void 0 && values.model === void 0) values.model = normalizeControlValue(data.model);
|
|
4763
|
+
if (data.mode !== void 0 && values.mode === void 0) values.mode = normalizeControlValue(data.mode);
|
|
4764
|
+
return Object.keys(values).length > 0 ? values : void 0;
|
|
4765
|
+
}
|
|
4766
|
+
function normalizeProviderEffects(data) {
|
|
4767
|
+
const rawEffects = Array.isArray(data?.effects) ? data.effects : [];
|
|
4768
|
+
const effects = [];
|
|
4769
|
+
for (const raw of rawEffects) {
|
|
4770
|
+
if (!raw || typeof raw !== "object") continue;
|
|
4771
|
+
const type = raw.type;
|
|
4772
|
+
if (type === "message" && raw.message && typeof raw.message === "object") {
|
|
4773
|
+
const content = raw.message.content;
|
|
4774
|
+
if (typeof content !== "string" && !Array.isArray(content)) continue;
|
|
4775
|
+
effects.push({
|
|
4776
|
+
type: "message",
|
|
4777
|
+
id: typeof raw.id === "string" ? raw.id : void 0,
|
|
4778
|
+
when: raw.when === "turn_completed" ? "turn_completed" : "immediate",
|
|
4779
|
+
persist: raw.persist !== false,
|
|
4780
|
+
message: {
|
|
4781
|
+
role: raw.message.role === "assistant" || raw.message.role === "user" ? raw.message.role : "system",
|
|
4782
|
+
content,
|
|
4783
|
+
kind: typeof raw.message.kind === "string" ? raw.message.kind : void 0,
|
|
4784
|
+
senderName: typeof raw.message.senderName === "string" ? raw.message.senderName : void 0
|
|
4785
|
+
}
|
|
4786
|
+
});
|
|
4787
|
+
continue;
|
|
4591
4788
|
}
|
|
4592
|
-
|
|
4593
|
-
|
|
4594
|
-
|
|
4595
|
-
|
|
4596
|
-
|
|
4597
|
-
|
|
4598
|
-
|
|
4599
|
-
|
|
4600
|
-
|
|
4601
|
-
|
|
4602
|
-
|
|
4603
|
-
|
|
4604
|
-
const now = Date.now();
|
|
4605
|
-
const agentStatus = newStatus === "streaming" || newStatus === "generating" ? "generating" : newStatus === "waiting_approval" ? "waiting_approval" : "idle";
|
|
4606
|
-
const lastMsg = Array.isArray(data?.messages) && data.messages.length > 0 ? data.messages[data.messages.length - 1] : null;
|
|
4607
|
-
const progressFingerprint = agentStatus === "generating" ? `${lastMsg?.role || ""}:${typeof lastMsg?.content === "string" ? lastMsg.content : JSON.stringify(lastMsg?.content || "")}`.slice(-2e3) : void 0;
|
|
4608
|
-
if (agentStatus !== this.lastAgentStatus) {
|
|
4609
|
-
if (this.lastAgentStatus === "idle" && agentStatus === "generating") {
|
|
4610
|
-
this.generatingStartedAt = now;
|
|
4611
|
-
this.pushEvent({
|
|
4612
|
-
event: "agent:generating_started",
|
|
4613
|
-
chatTitle: this.resolveChatTitle(data),
|
|
4614
|
-
timestamp: now,
|
|
4615
|
-
ideType: this.ideType || this.type,
|
|
4616
|
-
agentType: this.type,
|
|
4617
|
-
agentName: this.agentName || this.provider.name,
|
|
4618
|
-
extensionId: this.extensionId || this.type
|
|
4619
|
-
});
|
|
4620
|
-
} else if (agentStatus === "waiting_approval") {
|
|
4621
|
-
if (!this.generatingStartedAt) this.generatingStartedAt = now;
|
|
4622
|
-
this.pushEvent({
|
|
4623
|
-
event: "agent:waiting_approval",
|
|
4624
|
-
chatTitle: this.resolveChatTitle(data),
|
|
4625
|
-
timestamp: now,
|
|
4626
|
-
ideType: this.ideType || this.type,
|
|
4627
|
-
agentType: this.type,
|
|
4628
|
-
agentName: this.agentName || this.provider.name,
|
|
4629
|
-
extensionId: this.extensionId || this.type,
|
|
4630
|
-
modalMessage: data?.activeModal?.message,
|
|
4631
|
-
modalButtons: data?.activeModal?.buttons
|
|
4632
|
-
});
|
|
4633
|
-
} else if (agentStatus === "idle" && (this.lastAgentStatus === "generating" || this.lastAgentStatus === "waiting_approval")) {
|
|
4634
|
-
const duration = this.generatingStartedAt ? Math.round((now - this.generatingStartedAt) / 1e3) : 0;
|
|
4635
|
-
this.pushEvent({
|
|
4636
|
-
event: "agent:generating_completed",
|
|
4637
|
-
chatTitle: this.resolveChatTitle(data),
|
|
4638
|
-
duration,
|
|
4639
|
-
timestamp: now,
|
|
4640
|
-
ideType: this.ideType || this.type,
|
|
4641
|
-
agentType: this.type,
|
|
4642
|
-
agentName: this.agentName || this.provider.name,
|
|
4643
|
-
extensionId: this.extensionId || this.type
|
|
4644
|
-
});
|
|
4645
|
-
this.generatingStartedAt = 0;
|
|
4646
|
-
}
|
|
4647
|
-
this.lastAgentStatus = agentStatus;
|
|
4789
|
+
if (type === "toast" && raw.toast && typeof raw.toast.message === "string") {
|
|
4790
|
+
effects.push({
|
|
4791
|
+
type: "toast",
|
|
4792
|
+
id: typeof raw.id === "string" ? raw.id : void 0,
|
|
4793
|
+
when: raw.when === "turn_completed" ? "turn_completed" : "immediate",
|
|
4794
|
+
persist: raw.persist !== false,
|
|
4795
|
+
toast: {
|
|
4796
|
+
level: raw.toast.level === "success" || raw.toast.level === "warning" ? raw.toast.level : "info",
|
|
4797
|
+
message: raw.toast.message
|
|
4798
|
+
}
|
|
4799
|
+
});
|
|
4800
|
+
continue;
|
|
4648
4801
|
}
|
|
4649
|
-
|
|
4650
|
-
|
|
4651
|
-
|
|
4652
|
-
|
|
4802
|
+
if (type === "notification" && raw.notification && typeof raw.notification.body === "string") {
|
|
4803
|
+
effects.push({
|
|
4804
|
+
type: "notification",
|
|
4805
|
+
id: typeof raw.id === "string" ? raw.id : void 0,
|
|
4806
|
+
when: raw.when === "turn_completed" ? "turn_completed" : "immediate",
|
|
4807
|
+
persist: raw.persist !== false,
|
|
4808
|
+
notification: {
|
|
4809
|
+
title: typeof raw.notification.title === "string" ? raw.notification.title : void 0,
|
|
4810
|
+
body: raw.notification.body,
|
|
4811
|
+
level: raw.notification.level === "success" || raw.notification.level === "warning" ? raw.notification.level : "info",
|
|
4812
|
+
channels: Array.isArray(raw.notification.channels) ? raw.notification.channels.filter((channel) => channel === "bubble" || channel === "toast" || channel === "browser") : void 0,
|
|
4813
|
+
preferenceKey: raw.notification.preferenceKey === "disconnect" || raw.notification.preferenceKey === "completion" || raw.notification.preferenceKey === "approval" || raw.notification.preferenceKey === "browser" ? raw.notification.preferenceKey : void 0,
|
|
4814
|
+
bubbleContent: typeof raw.notification.bubbleContent === "string" || Array.isArray(raw.notification.bubbleContent) ? raw.notification.bubbleContent : void 0
|
|
4815
|
+
}
|
|
4816
|
+
});
|
|
4653
4817
|
}
|
|
4654
4818
|
}
|
|
4655
|
-
|
|
4656
|
-
|
|
4657
|
-
|
|
4819
|
+
return effects;
|
|
4820
|
+
}
|
|
4821
|
+
function normalizeControlValue(value) {
|
|
4822
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
4823
|
+
return value;
|
|
4658
4824
|
}
|
|
4659
|
-
|
|
4660
|
-
|
|
4661
|
-
|
|
4662
|
-
return
|
|
4825
|
+
if (value && typeof value === "object") {
|
|
4826
|
+
if (typeof value.label === "string") return value.label;
|
|
4827
|
+
if (typeof value.name === "string") return value.name;
|
|
4828
|
+
if (typeof value.id === "string") return value.id;
|
|
4663
4829
|
}
|
|
4664
|
-
|
|
4665
|
-
|
|
4666
|
-
return title || this.agentName || this.provider.name;
|
|
4667
|
-
}
|
|
4668
|
-
resetStreamState() {
|
|
4669
|
-
if (this.currentStatus !== "idle") {
|
|
4670
|
-
this.detectTransition("idle", {
|
|
4671
|
-
title: this.chatTitle,
|
|
4672
|
-
agentName: this.agentName,
|
|
4673
|
-
extensionId: this.extensionId,
|
|
4674
|
-
messages: this.messages
|
|
4675
|
-
});
|
|
4676
|
-
}
|
|
4677
|
-
this.agentStreams = [];
|
|
4678
|
-
this.messages = [];
|
|
4679
|
-
this.activeModal = null;
|
|
4680
|
-
this.currentModel = "";
|
|
4681
|
-
this.currentMode = "";
|
|
4682
|
-
this.controlValues = {};
|
|
4683
|
-
this.currentStatus = "idle";
|
|
4684
|
-
this.chatId = null;
|
|
4685
|
-
this.chatTitle = null;
|
|
4686
|
-
this.agentName = "";
|
|
4687
|
-
this.extensionId = "";
|
|
4688
|
-
this.lastAgentStatus = "idle";
|
|
4689
|
-
this.generatingStartedAt = 0;
|
|
4690
|
-
this.monitor.reset();
|
|
4691
|
-
}
|
|
4692
|
-
};
|
|
4830
|
+
return String(value);
|
|
4831
|
+
}
|
|
4693
4832
|
|
|
4694
4833
|
// src/config/chat-history.ts
|
|
4695
4834
|
import * as fs3 from "fs";
|
|
@@ -4916,54 +5055,383 @@ function listSavedHistorySessions(agentType, options = {}) {
|
|
|
4916
5055
|
files.push(file);
|
|
4917
5056
|
groupedFiles.set(historySessionId, files);
|
|
4918
5057
|
}
|
|
4919
|
-
const summaries = [];
|
|
4920
|
-
for (const [historySessionId, files] of groupedFiles.entries()) {
|
|
4921
|
-
let messageCount = 0;
|
|
4922
|
-
let firstMessageAt = 0;
|
|
4923
|
-
let lastMessageAt = 0;
|
|
4924
|
-
let sessionTitle = "";
|
|
4925
|
-
let preview = "";
|
|
4926
|
-
for (const file of files.sort()) {
|
|
4927
|
-
const filePath = path5.join(dir, file);
|
|
4928
|
-
const content = fs3.readFileSync(filePath, "utf-8");
|
|
4929
|
-
const lines = content.split("\n").filter(Boolean);
|
|
4930
|
-
for (const line of lines) {
|
|
4931
|
-
let parsed = null;
|
|
4932
|
-
try {
|
|
4933
|
-
parsed = JSON.parse(line);
|
|
4934
|
-
} catch {
|
|
4935
|
-
parsed = null;
|
|
4936
|
-
}
|
|
4937
|
-
if (!parsed || parsed.historySessionId !== historySessionId) continue;
|
|
4938
|
-
messageCount += 1;
|
|
4939
|
-
if (!firstMessageAt || parsed.receivedAt < firstMessageAt) firstMessageAt = parsed.receivedAt;
|
|
4940
|
-
if (!lastMessageAt || parsed.receivedAt > lastMessageAt) lastMessageAt = parsed.receivedAt;
|
|
4941
|
-
if (parsed.sessionTitle) sessionTitle = parsed.sessionTitle;
|
|
4942
|
-
if (parsed.role !== "system" && parsed.content.trim()) preview = parsed.content.trim();
|
|
4943
|
-
}
|
|
5058
|
+
const summaries = [];
|
|
5059
|
+
for (const [historySessionId, files] of groupedFiles.entries()) {
|
|
5060
|
+
let messageCount = 0;
|
|
5061
|
+
let firstMessageAt = 0;
|
|
5062
|
+
let lastMessageAt = 0;
|
|
5063
|
+
let sessionTitle = "";
|
|
5064
|
+
let preview = "";
|
|
5065
|
+
for (const file of files.sort()) {
|
|
5066
|
+
const filePath = path5.join(dir, file);
|
|
5067
|
+
const content = fs3.readFileSync(filePath, "utf-8");
|
|
5068
|
+
const lines = content.split("\n").filter(Boolean);
|
|
5069
|
+
for (const line of lines) {
|
|
5070
|
+
let parsed = null;
|
|
5071
|
+
try {
|
|
5072
|
+
parsed = JSON.parse(line);
|
|
5073
|
+
} catch {
|
|
5074
|
+
parsed = null;
|
|
5075
|
+
}
|
|
5076
|
+
if (!parsed || parsed.historySessionId !== historySessionId) continue;
|
|
5077
|
+
messageCount += 1;
|
|
5078
|
+
if (!firstMessageAt || parsed.receivedAt < firstMessageAt) firstMessageAt = parsed.receivedAt;
|
|
5079
|
+
if (!lastMessageAt || parsed.receivedAt > lastMessageAt) lastMessageAt = parsed.receivedAt;
|
|
5080
|
+
if (parsed.sessionTitle) sessionTitle = parsed.sessionTitle;
|
|
5081
|
+
if (parsed.role !== "system" && parsed.content.trim()) preview = parsed.content.trim();
|
|
5082
|
+
}
|
|
5083
|
+
}
|
|
5084
|
+
if (messageCount === 0 || !lastMessageAt) continue;
|
|
5085
|
+
summaries.push({
|
|
5086
|
+
historySessionId,
|
|
5087
|
+
sessionTitle: sessionTitle || void 0,
|
|
5088
|
+
messageCount,
|
|
5089
|
+
firstMessageAt,
|
|
5090
|
+
lastMessageAt,
|
|
5091
|
+
preview: preview || void 0
|
|
5092
|
+
});
|
|
5093
|
+
}
|
|
5094
|
+
summaries.sort((a, b) => b.lastMessageAt - a.lastMessageAt);
|
|
5095
|
+
const offset = Math.max(0, options.offset || 0);
|
|
5096
|
+
const limit = Math.max(1, options.limit || 30);
|
|
5097
|
+
const sliced = summaries.slice(offset, offset + limit);
|
|
5098
|
+
return {
|
|
5099
|
+
sessions: sliced,
|
|
5100
|
+
hasMore: summaries.length > offset + limit
|
|
5101
|
+
};
|
|
5102
|
+
} catch {
|
|
5103
|
+
return { sessions: [], hasMore: false };
|
|
5104
|
+
}
|
|
5105
|
+
}
|
|
5106
|
+
|
|
5107
|
+
// src/providers/extension-provider-instance.ts
|
|
5108
|
+
var ExtensionProviderInstance = class {
|
|
5109
|
+
type;
|
|
5110
|
+
category = "extension";
|
|
5111
|
+
provider;
|
|
5112
|
+
context = null;
|
|
5113
|
+
settings = {};
|
|
5114
|
+
events = [];
|
|
5115
|
+
// status
|
|
5116
|
+
currentStatus = "idle";
|
|
5117
|
+
agentStreams = [];
|
|
5118
|
+
messages = [];
|
|
5119
|
+
activeModal = null;
|
|
5120
|
+
currentModel = "";
|
|
5121
|
+
currentMode = "";
|
|
5122
|
+
controlValues = {};
|
|
5123
|
+
appliedEffectKeys = /* @__PURE__ */ new Set();
|
|
5124
|
+
runtimeMessages = [];
|
|
5125
|
+
lastAgentStatus = "idle";
|
|
5126
|
+
generatingStartedAt = 0;
|
|
5127
|
+
monitor;
|
|
5128
|
+
historyWriter;
|
|
5129
|
+
// meta
|
|
5130
|
+
instanceId;
|
|
5131
|
+
ideType = "";
|
|
5132
|
+
chatId = null;
|
|
5133
|
+
chatTitle = null;
|
|
5134
|
+
agentName = "";
|
|
5135
|
+
extensionId = "";
|
|
5136
|
+
constructor(provider) {
|
|
5137
|
+
this.type = provider.type;
|
|
5138
|
+
this.provider = provider;
|
|
5139
|
+
this.instanceId = crypto.randomUUID();
|
|
5140
|
+
this.monitor = new StatusMonitor();
|
|
5141
|
+
this.historyWriter = new ChatHistoryWriter();
|
|
5142
|
+
}
|
|
5143
|
+
// ─── Lifecycle ──────────────────────────────────
|
|
5144
|
+
async init(context) {
|
|
5145
|
+
this.context = context;
|
|
5146
|
+
this.settings = context.settings || {};
|
|
5147
|
+
this.monitor.updateConfig({
|
|
5148
|
+
approvalAlert: this.settings.approvalAlert !== false,
|
|
5149
|
+
longGeneratingAlert: this.settings.longGeneratingAlert !== false,
|
|
5150
|
+
longGeneratingThresholdSec: this.settings.longGeneratingThresholdSec || 180
|
|
5151
|
+
});
|
|
5152
|
+
}
|
|
5153
|
+
async onTick() {
|
|
5154
|
+
if (!this.context?.cdp?.isConnected) return;
|
|
5155
|
+
}
|
|
5156
|
+
getState() {
|
|
5157
|
+
return {
|
|
5158
|
+
type: this.type,
|
|
5159
|
+
name: this.provider.name,
|
|
5160
|
+
category: "extension",
|
|
5161
|
+
status: this.currentStatus,
|
|
5162
|
+
activeChat: this.messages.length > 0 || this.runtimeMessages.length > 0 ? {
|
|
5163
|
+
id: this.chatId || this.instanceId,
|
|
5164
|
+
title: this.chatTitle || this.agentName || this.provider.name,
|
|
5165
|
+
status: this.currentStatus,
|
|
5166
|
+
messages: this.mergeConversationMessages(this.messages),
|
|
5167
|
+
activeModal: this.activeModal,
|
|
5168
|
+
inputContent: ""
|
|
5169
|
+
} : null,
|
|
5170
|
+
currentModel: this.currentModel || void 0,
|
|
5171
|
+
currentPlan: this.currentMode || void 0,
|
|
5172
|
+
controlValues: this.controlValues,
|
|
5173
|
+
providerControls: this.provider.controls,
|
|
5174
|
+
agentStreams: this.agentStreams,
|
|
5175
|
+
instanceId: this.instanceId,
|
|
5176
|
+
lastUpdated: Date.now(),
|
|
5177
|
+
settings: this.settings,
|
|
5178
|
+
pendingEvents: this.flushEvents()
|
|
5179
|
+
};
|
|
5180
|
+
}
|
|
5181
|
+
onEvent(event, data) {
|
|
5182
|
+
if (event === "stream_update") {
|
|
5183
|
+
if (data?.streams) this.agentStreams = data.streams;
|
|
5184
|
+
if (data?.messages) this.messages = data.messages;
|
|
5185
|
+
if (data?.activeModal !== void 0) this.activeModal = data.activeModal;
|
|
5186
|
+
if (data?.model) this.currentModel = data.model;
|
|
5187
|
+
if (data?.mode) this.currentMode = data.mode;
|
|
5188
|
+
const controlValues = extractProviderControlValues(this.provider.controls, data) || data?.controlValues;
|
|
5189
|
+
if (controlValues) this.controlValues = controlValues;
|
|
5190
|
+
if (typeof data?.sessionId === "string" && data.sessionId.trim()) this.chatId = data.sessionId;
|
|
5191
|
+
if (typeof data?.title === "string" && data.title.trim()) this.chatTitle = data.title;
|
|
5192
|
+
if (typeof data?.agentName === "string" && data.agentName.trim()) this.agentName = data.agentName;
|
|
5193
|
+
if (typeof data?.extensionId === "string" && data.extensionId.trim()) this.extensionId = data.extensionId;
|
|
5194
|
+
if (data?.status) {
|
|
5195
|
+
const newStatus = data.status;
|
|
5196
|
+
this.detectTransition(newStatus, data);
|
|
5197
|
+
this.currentStatus = newStatus;
|
|
5198
|
+
}
|
|
5199
|
+
} else if (event === "stream_reset") {
|
|
5200
|
+
this.resetStreamState();
|
|
5201
|
+
} else if (event === "extension_connected") {
|
|
5202
|
+
this.ideType = data?.ideType || "";
|
|
5203
|
+
} else if (event === "provider_state_patch" && data && typeof data === "object") {
|
|
5204
|
+
this.applyProviderResponse(data, { phase: "immediate" });
|
|
5205
|
+
}
|
|
5206
|
+
}
|
|
5207
|
+
dispose() {
|
|
5208
|
+
this.agentStreams = [];
|
|
5209
|
+
this.messages = [];
|
|
5210
|
+
this.monitor.reset();
|
|
5211
|
+
this.appliedEffectKeys.clear();
|
|
5212
|
+
this.runtimeMessages = [];
|
|
5213
|
+
}
|
|
5214
|
+
updateSettings(newSettings) {
|
|
5215
|
+
this.settings = { ...newSettings };
|
|
5216
|
+
this.monitor.updateConfig({
|
|
5217
|
+
approvalAlert: this.settings.approvalAlert !== false,
|
|
5218
|
+
longGeneratingAlert: this.settings.longGeneratingAlert !== false,
|
|
5219
|
+
longGeneratingThresholdSec: this.settings.longGeneratingThresholdSec || 180
|
|
5220
|
+
});
|
|
5221
|
+
}
|
|
5222
|
+
/** Query UUID instanceId */
|
|
5223
|
+
getInstanceId() {
|
|
5224
|
+
return this.instanceId;
|
|
5225
|
+
}
|
|
5226
|
+
// ─── status transition detect ──────────────────────────────
|
|
5227
|
+
detectTransition(newStatus, data) {
|
|
5228
|
+
const now = Date.now();
|
|
5229
|
+
const agentStatus = newStatus === "streaming" || newStatus === "generating" ? "generating" : newStatus === "waiting_approval" ? "waiting_approval" : "idle";
|
|
5230
|
+
const lastMsg = Array.isArray(data?.messages) && data.messages.length > 0 ? data.messages[data.messages.length - 1] : null;
|
|
5231
|
+
const progressFingerprint = agentStatus === "generating" ? `${lastMsg?.role || ""}:${typeof lastMsg?.content === "string" ? lastMsg.content : JSON.stringify(lastMsg?.content || "")}`.slice(-2e3) : void 0;
|
|
5232
|
+
const previousStatus = this.lastAgentStatus;
|
|
5233
|
+
if (agentStatus !== this.lastAgentStatus) {
|
|
5234
|
+
if (this.lastAgentStatus === "idle" && agentStatus === "generating") {
|
|
5235
|
+
this.generatingStartedAt = now;
|
|
5236
|
+
this.pushEvent({
|
|
5237
|
+
event: "agent:generating_started",
|
|
5238
|
+
chatTitle: this.resolveChatTitle(data),
|
|
5239
|
+
timestamp: now,
|
|
5240
|
+
ideType: this.ideType || this.type,
|
|
5241
|
+
agentType: this.type,
|
|
5242
|
+
agentName: this.agentName || this.provider.name,
|
|
5243
|
+
extensionId: this.extensionId || this.type
|
|
5244
|
+
});
|
|
5245
|
+
} else if (agentStatus === "waiting_approval") {
|
|
5246
|
+
if (!this.generatingStartedAt) this.generatingStartedAt = now;
|
|
5247
|
+
this.pushEvent({
|
|
5248
|
+
event: "agent:waiting_approval",
|
|
5249
|
+
chatTitle: this.resolveChatTitle(data),
|
|
5250
|
+
timestamp: now,
|
|
5251
|
+
ideType: this.ideType || this.type,
|
|
5252
|
+
agentType: this.type,
|
|
5253
|
+
agentName: this.agentName || this.provider.name,
|
|
5254
|
+
extensionId: this.extensionId || this.type,
|
|
5255
|
+
modalMessage: data?.activeModal?.message,
|
|
5256
|
+
modalButtons: data?.activeModal?.buttons
|
|
5257
|
+
});
|
|
5258
|
+
} else if (agentStatus === "idle" && (this.lastAgentStatus === "generating" || this.lastAgentStatus === "waiting_approval")) {
|
|
5259
|
+
const duration = this.generatingStartedAt ? Math.round((now - this.generatingStartedAt) / 1e3) : 0;
|
|
5260
|
+
this.pushEvent({
|
|
5261
|
+
event: "agent:generating_completed",
|
|
5262
|
+
chatTitle: this.resolveChatTitle(data),
|
|
5263
|
+
duration,
|
|
5264
|
+
timestamp: now,
|
|
5265
|
+
ideType: this.ideType || this.type,
|
|
5266
|
+
agentType: this.type,
|
|
5267
|
+
agentName: this.agentName || this.provider.name,
|
|
5268
|
+
extensionId: this.extensionId || this.type
|
|
5269
|
+
});
|
|
5270
|
+
this.generatingStartedAt = 0;
|
|
5271
|
+
}
|
|
5272
|
+
this.lastAgentStatus = agentStatus;
|
|
5273
|
+
}
|
|
5274
|
+
this.applyProviderResponse(data, {
|
|
5275
|
+
phase: agentStatus === "idle" && (previousStatus === "generating" || previousStatus === "waiting_approval") ? "turn_completed" : "immediate"
|
|
5276
|
+
});
|
|
5277
|
+
const agentKey = `${this.type}:ext`;
|
|
5278
|
+
const monitorEvents = this.monitor.check(agentKey, agentStatus, now, progressFingerprint);
|
|
5279
|
+
for (const me of monitorEvents) {
|
|
5280
|
+
this.pushEvent({ event: me.type, agentKey: me.agentKey, message: me.message, elapsedSec: me.elapsedSec, timestamp: me.timestamp });
|
|
5281
|
+
}
|
|
5282
|
+
}
|
|
5283
|
+
pushEvent(event) {
|
|
5284
|
+
this.events.push(event);
|
|
5285
|
+
if (this.events.length > 50) this.events = this.events.slice(-50);
|
|
5286
|
+
}
|
|
5287
|
+
applyProviderResponse(data, options) {
|
|
5288
|
+
if (!data || typeof data !== "object") return;
|
|
5289
|
+
const controlValues = extractProviderControlValues(this.provider.controls, data);
|
|
5290
|
+
if (controlValues) this.controlValues = { ...this.controlValues, ...controlValues };
|
|
5291
|
+
const effects = normalizeProviderEffects(data);
|
|
5292
|
+
for (const effect of effects) {
|
|
5293
|
+
const effectWhen = effect.when || "immediate";
|
|
5294
|
+
if (effectWhen === "turn_completed" && options.phase !== "turn_completed") continue;
|
|
5295
|
+
if (effectWhen === "immediate" && options.phase === "turn_completed") continue;
|
|
5296
|
+
const effectKey = this.getEffectDedupKey(effect);
|
|
5297
|
+
if (this.appliedEffectKeys.has(effectKey)) continue;
|
|
5298
|
+
this.appliedEffectKeys.add(effectKey);
|
|
5299
|
+
if (effect.persist !== false) {
|
|
5300
|
+
const persisted = this.getPersistedEffectContent(effect);
|
|
5301
|
+
if (persisted) this.appendRuntimeSystemMessage(persisted, effectKey);
|
|
5302
|
+
}
|
|
5303
|
+
if (effect.type === "message" && effect.message) {
|
|
5304
|
+
this.pushEvent({
|
|
5305
|
+
event: "provider:message",
|
|
5306
|
+
timestamp: Date.now(),
|
|
5307
|
+
content: typeof effect.message.content === "string" ? effect.message.content : JSON.stringify(effect.message.content),
|
|
5308
|
+
role: effect.message.role || "system",
|
|
5309
|
+
kind: effect.message.kind,
|
|
5310
|
+
senderName: effect.message.senderName
|
|
5311
|
+
});
|
|
5312
|
+
} else if (effect.type === "toast" && effect.toast) {
|
|
5313
|
+
this.pushEvent({
|
|
5314
|
+
event: "provider:toast",
|
|
5315
|
+
effectId: effect.id || effectKey,
|
|
5316
|
+
timestamp: Date.now(),
|
|
5317
|
+
message: effect.toast.message,
|
|
5318
|
+
level: effect.toast.level || "info"
|
|
5319
|
+
});
|
|
5320
|
+
} else if (effect.type === "notification" && effect.notification) {
|
|
5321
|
+
this.pushEvent({
|
|
5322
|
+
event: "provider:notification",
|
|
5323
|
+
effectId: effect.id || effectKey,
|
|
5324
|
+
timestamp: Date.now(),
|
|
5325
|
+
title: effect.notification.title,
|
|
5326
|
+
message: effect.notification.body,
|
|
5327
|
+
content: typeof effect.notification.bubbleContent === "string" ? effect.notification.bubbleContent : effect.notification.body,
|
|
5328
|
+
level: effect.notification.level || "info",
|
|
5329
|
+
channels: effect.notification.channels || ["toast"],
|
|
5330
|
+
preferenceKey: effect.notification.preferenceKey
|
|
5331
|
+
});
|
|
5332
|
+
}
|
|
5333
|
+
}
|
|
5334
|
+
}
|
|
5335
|
+
appendRuntimeSystemMessage(content, dedupKey, receivedAt = Date.now()) {
|
|
5336
|
+
const normalizedContent = String(content || "").trim();
|
|
5337
|
+
if (!normalizedContent) return;
|
|
5338
|
+
if (this.runtimeMessages.some((entry) => entry.key === dedupKey)) return;
|
|
5339
|
+
this.runtimeMessages.push({
|
|
5340
|
+
key: dedupKey,
|
|
5341
|
+
message: {
|
|
5342
|
+
role: "system",
|
|
5343
|
+
senderName: "System",
|
|
5344
|
+
content: normalizedContent,
|
|
5345
|
+
receivedAt,
|
|
5346
|
+
timestamp: receivedAt
|
|
5347
|
+
}
|
|
5348
|
+
});
|
|
5349
|
+
if (this.runtimeMessages.length > 50) this.runtimeMessages = this.runtimeMessages.slice(-50);
|
|
5350
|
+
this.historyWriter.appendNewMessages(
|
|
5351
|
+
this.type,
|
|
5352
|
+
[{
|
|
5353
|
+
role: "system",
|
|
5354
|
+
senderName: "System",
|
|
5355
|
+
content: normalizedContent,
|
|
5356
|
+
kind: "system",
|
|
5357
|
+
receivedAt,
|
|
5358
|
+
historyDedupKey: dedupKey
|
|
5359
|
+
}],
|
|
5360
|
+
this.chatTitle || this.agentName || this.provider.name,
|
|
5361
|
+
this.instanceId,
|
|
5362
|
+
this.chatId || this.instanceId
|
|
5363
|
+
);
|
|
5364
|
+
}
|
|
5365
|
+
mergeConversationMessages(messages) {
|
|
5366
|
+
if (this.runtimeMessages.length === 0) return messages;
|
|
5367
|
+
return [...messages, ...this.runtimeMessages.map((entry) => entry.message)].map((message, index) => ({ message, index })).sort((a, b) => {
|
|
5368
|
+
const aTime = a.message.receivedAt || a.message.timestamp || 0;
|
|
5369
|
+
const bTime = b.message.receivedAt || b.message.timestamp || 0;
|
|
5370
|
+
if (aTime !== bTime) return aTime - bTime;
|
|
5371
|
+
return a.index - b.index;
|
|
5372
|
+
}).map((entry) => entry.message);
|
|
5373
|
+
}
|
|
5374
|
+
getPersistedEffectContent(effect) {
|
|
5375
|
+
if (effect.type === "message") {
|
|
5376
|
+
return typeof effect.message?.content === "string" ? effect.message.content : JSON.stringify(effect.message?.content || "");
|
|
5377
|
+
}
|
|
5378
|
+
if (effect.type === "toast") {
|
|
5379
|
+
return effect.toast?.message || null;
|
|
5380
|
+
}
|
|
5381
|
+
if (effect.type === "notification") {
|
|
5382
|
+
if (typeof effect.notification?.bubbleContent === "string") return effect.notification.bubbleContent;
|
|
5383
|
+
if (typeof effect.notification?.title === "string" && effect.notification.title.trim()) {
|
|
5384
|
+
return `${effect.notification.title}
|
|
5385
|
+
${effect.notification.body || ""}`.trim();
|
|
4944
5386
|
}
|
|
4945
|
-
|
|
4946
|
-
|
|
4947
|
-
|
|
4948
|
-
|
|
4949
|
-
|
|
4950
|
-
|
|
4951
|
-
|
|
4952
|
-
|
|
5387
|
+
return effect.notification?.body || null;
|
|
5388
|
+
}
|
|
5389
|
+
return null;
|
|
5390
|
+
}
|
|
5391
|
+
getEffectDedupKey(effect) {
|
|
5392
|
+
if (effect.id) return `provider_effect:${effect.id}`;
|
|
5393
|
+
if (effect.type === "message") {
|
|
5394
|
+
return `provider_effect:message:${typeof effect.message?.content === "string" ? effect.message.content : JSON.stringify(effect.message?.content || "")}`;
|
|
5395
|
+
}
|
|
5396
|
+
if (effect.type === "notification") {
|
|
5397
|
+
return `provider_effect:notification:${effect.notification?.title || ""}:${effect.notification?.body || ""}`;
|
|
5398
|
+
}
|
|
5399
|
+
return `provider_effect:toast:${effect.toast?.message || ""}`;
|
|
5400
|
+
}
|
|
5401
|
+
flushEvents() {
|
|
5402
|
+
const events = [...this.events];
|
|
5403
|
+
this.events = [];
|
|
5404
|
+
return events;
|
|
5405
|
+
}
|
|
5406
|
+
resolveChatTitle(data) {
|
|
5407
|
+
const title = typeof data?.title === "string" && data.title.trim() ? data.title.trim() : this.chatTitle;
|
|
5408
|
+
return title || this.agentName || this.provider.name;
|
|
5409
|
+
}
|
|
5410
|
+
resetStreamState() {
|
|
5411
|
+
if (this.currentStatus !== "idle") {
|
|
5412
|
+
this.detectTransition("idle", {
|
|
5413
|
+
title: this.chatTitle,
|
|
5414
|
+
agentName: this.agentName,
|
|
5415
|
+
extensionId: this.extensionId,
|
|
5416
|
+
messages: this.messages
|
|
4953
5417
|
});
|
|
4954
5418
|
}
|
|
4955
|
-
|
|
4956
|
-
|
|
4957
|
-
|
|
4958
|
-
|
|
4959
|
-
|
|
4960
|
-
|
|
4961
|
-
|
|
4962
|
-
|
|
4963
|
-
|
|
4964
|
-
|
|
5419
|
+
this.agentStreams = [];
|
|
5420
|
+
this.messages = [];
|
|
5421
|
+
this.activeModal = null;
|
|
5422
|
+
this.currentModel = "";
|
|
5423
|
+
this.currentMode = "";
|
|
5424
|
+
this.controlValues = {};
|
|
5425
|
+
this.currentStatus = "idle";
|
|
5426
|
+
this.chatId = null;
|
|
5427
|
+
this.chatTitle = null;
|
|
5428
|
+
this.agentName = "";
|
|
5429
|
+
this.extensionId = "";
|
|
5430
|
+
this.lastAgentStatus = "idle";
|
|
5431
|
+
this.generatingStartedAt = 0;
|
|
5432
|
+
this.monitor.reset();
|
|
4965
5433
|
}
|
|
4966
|
-
}
|
|
5434
|
+
};
|
|
4967
5435
|
|
|
4968
5436
|
// src/providers/ide-provider-instance.ts
|
|
4969
5437
|
init_logger();
|
|
@@ -4984,6 +5452,8 @@ var IdeProviderInstance = class {
|
|
|
4984
5452
|
monitor;
|
|
4985
5453
|
historyWriter;
|
|
4986
5454
|
autoApproveBusy = false;
|
|
5455
|
+
appliedEffectKeys = /* @__PURE__ */ new Set();
|
|
5456
|
+
runtimeMessages = [];
|
|
4987
5457
|
// IDE meta
|
|
4988
5458
|
ideVersion = "";
|
|
4989
5459
|
instanceId;
|
|
@@ -5044,7 +5514,7 @@ var IdeProviderInstance = class {
|
|
|
5044
5514
|
id: this.cachedChat.id || "active_session",
|
|
5045
5515
|
title: this.cachedChat.title || this.type,
|
|
5046
5516
|
status: this.cachedChat.status || this.currentStatus,
|
|
5047
|
-
messages: this.cachedChat.messages || [],
|
|
5517
|
+
messages: this.mergeConversationMessages(this.cachedChat.messages || []),
|
|
5048
5518
|
activeModal: this.cachedChat.activeModal || null,
|
|
5049
5519
|
inputContent: this.cachedChat.inputContent || ""
|
|
5050
5520
|
} : null,
|
|
@@ -5084,6 +5554,13 @@ var IdeProviderInstance = class {
|
|
|
5084
5554
|
for (const ext of this.extensions.values()) {
|
|
5085
5555
|
ext.onEvent("stream_reset");
|
|
5086
5556
|
}
|
|
5557
|
+
} else if (event === "provider_state_patch" && data && typeof data === "object") {
|
|
5558
|
+
const extType = typeof data.extensionType === "string" ? data.extensionType : "";
|
|
5559
|
+
if (extType && this.extensions.has(extType)) {
|
|
5560
|
+
this.extensions.get(extType).onEvent("provider_state_patch", data);
|
|
5561
|
+
} else {
|
|
5562
|
+
this.applyProviderResponse(data, { phase: "immediate" });
|
|
5563
|
+
}
|
|
5087
5564
|
}
|
|
5088
5565
|
}
|
|
5089
5566
|
dispose() {
|
|
@@ -5091,11 +5568,21 @@ var IdeProviderInstance = class {
|
|
|
5091
5568
|
this.lastAgentStatuses.clear();
|
|
5092
5569
|
this.generatingStartedAt.clear();
|
|
5093
5570
|
this.monitor.reset();
|
|
5571
|
+
this.appliedEffectKeys.clear();
|
|
5572
|
+
this.runtimeMessages = [];
|
|
5094
5573
|
for (const ext of this.extensions.values()) {
|
|
5095
5574
|
ext.dispose();
|
|
5096
5575
|
}
|
|
5097
5576
|
this.extensions.clear();
|
|
5098
5577
|
}
|
|
5578
|
+
updateSettings(newSettings) {
|
|
5579
|
+
this.settings = { ...newSettings };
|
|
5580
|
+
this.monitor.updateConfig({
|
|
5581
|
+
approvalAlert: this.settings.approvalAlert !== false,
|
|
5582
|
+
longGeneratingAlert: this.settings.longGeneratingAlert !== false,
|
|
5583
|
+
longGeneratingThresholdSec: this.settings.longGeneratingThresholdSec || 180
|
|
5584
|
+
});
|
|
5585
|
+
}
|
|
5099
5586
|
// ─── Extension manage ─────────────────────────────
|
|
5100
5587
|
/** Extension Instance add */
|
|
5101
5588
|
async addExtension(provider, settings) {
|
|
@@ -5208,6 +5695,8 @@ var IdeProviderInstance = class {
|
|
|
5208
5695
|
raw.messages = raw.messages.filter((m) => !hiddenKinds.has(m.kind));
|
|
5209
5696
|
}
|
|
5210
5697
|
}
|
|
5698
|
+
const controlValues = extractProviderControlValues(this.provider.controls, raw);
|
|
5699
|
+
if (controlValues) raw.controlValues = controlValues;
|
|
5211
5700
|
this.cachedChat = { ...raw, activeModal };
|
|
5212
5701
|
this.detectAgentTransitions(raw, now);
|
|
5213
5702
|
if (raw.messages?.length > 0) {
|
|
@@ -5274,6 +5763,9 @@ var IdeProviderInstance = class {
|
|
|
5274
5763
|
}
|
|
5275
5764
|
this.lastAgentStatuses.set(agentKey, agentStatus);
|
|
5276
5765
|
}
|
|
5766
|
+
this.applyProviderResponse(chatData, {
|
|
5767
|
+
phase: agentStatus === "idle" && (lastStatus === "generating" || lastStatus === "waiting_approval") ? "turn_completed" : "immediate"
|
|
5768
|
+
});
|
|
5277
5769
|
if (agentStatus === "waiting_approval" && this.settings.autoApprove && !this.autoApproveBusy) {
|
|
5278
5770
|
this.autoApproveViaScript(chatData);
|
|
5279
5771
|
}
|
|
@@ -5286,6 +5778,136 @@ var IdeProviderInstance = class {
|
|
|
5286
5778
|
this.events.push(event);
|
|
5287
5779
|
if (this.events.length > 50) this.events = this.events.slice(-50);
|
|
5288
5780
|
}
|
|
5781
|
+
applyProviderResponse(data, options) {
|
|
5782
|
+
if (!data || typeof data !== "object") return;
|
|
5783
|
+
const controlValues = extractProviderControlValues(this.provider.controls, data);
|
|
5784
|
+
if (controlValues) {
|
|
5785
|
+
this.cachedChat = {
|
|
5786
|
+
...this.cachedChat || {},
|
|
5787
|
+
...data,
|
|
5788
|
+
controlValues: { ...this.cachedChat?.controlValues || {}, ...controlValues }
|
|
5789
|
+
};
|
|
5790
|
+
}
|
|
5791
|
+
const effects = normalizeProviderEffects(data);
|
|
5792
|
+
for (const effect of effects) {
|
|
5793
|
+
const effectWhen = effect.when || "immediate";
|
|
5794
|
+
if (effectWhen === "turn_completed" && options.phase !== "turn_completed") continue;
|
|
5795
|
+
if (effectWhen === "immediate" && options.phase === "turn_completed") continue;
|
|
5796
|
+
const effectKey = this.getEffectDedupKey(effect);
|
|
5797
|
+
if (this.appliedEffectKeys.has(effectKey)) continue;
|
|
5798
|
+
this.appliedEffectKeys.add(effectKey);
|
|
5799
|
+
if (effect.persist !== false) {
|
|
5800
|
+
const persisted = this.getPersistedEffectContent(effect);
|
|
5801
|
+
if (persisted) this.appendRuntimeSystemMessage(persisted, effectKey);
|
|
5802
|
+
}
|
|
5803
|
+
if (effect.type === "message" && effect.message) {
|
|
5804
|
+
this.pushEvent({
|
|
5805
|
+
event: "provider:message",
|
|
5806
|
+
timestamp: Date.now(),
|
|
5807
|
+
content: typeof effect.message.content === "string" ? effect.message.content : JSON.stringify(effect.message.content),
|
|
5808
|
+
role: effect.message.role || "system",
|
|
5809
|
+
kind: effect.message.kind,
|
|
5810
|
+
senderName: effect.message.senderName
|
|
5811
|
+
});
|
|
5812
|
+
} else if (effect.type === "toast" && effect.toast) {
|
|
5813
|
+
this.pushEvent({
|
|
5814
|
+
event: "provider:toast",
|
|
5815
|
+
effectId: effect.id || effectKey,
|
|
5816
|
+
timestamp: Date.now(),
|
|
5817
|
+
message: effect.toast.message,
|
|
5818
|
+
level: effect.toast.level || "info"
|
|
5819
|
+
});
|
|
5820
|
+
} else if (effect.type === "notification" && effect.notification) {
|
|
5821
|
+
this.pushEvent({
|
|
5822
|
+
event: "provider:notification",
|
|
5823
|
+
effectId: effect.id || effectKey,
|
|
5824
|
+
timestamp: Date.now(),
|
|
5825
|
+
title: effect.notification.title,
|
|
5826
|
+
message: effect.notification.body,
|
|
5827
|
+
content: typeof effect.notification.bubbleContent === "string" ? effect.notification.bubbleContent : effect.notification.body,
|
|
5828
|
+
level: effect.notification.level || "info",
|
|
5829
|
+
channels: effect.notification.channels || ["toast"],
|
|
5830
|
+
preferenceKey: effect.notification.preferenceKey
|
|
5831
|
+
});
|
|
5832
|
+
}
|
|
5833
|
+
}
|
|
5834
|
+
}
|
|
5835
|
+
appendRuntimeSystemMessage(content, dedupKey, receivedAt = Date.now()) {
|
|
5836
|
+
const normalizedContent = String(content || "").trim();
|
|
5837
|
+
if (!normalizedContent) return;
|
|
5838
|
+
if (this.runtimeMessages.some((entry) => entry.key === dedupKey)) return;
|
|
5839
|
+
if (!this.cachedChat) {
|
|
5840
|
+
this.cachedChat = {
|
|
5841
|
+
id: "active_session",
|
|
5842
|
+
title: this.provider.name,
|
|
5843
|
+
status: this.currentStatus,
|
|
5844
|
+
messages: [],
|
|
5845
|
+
activeModal: null,
|
|
5846
|
+
inputContent: ""
|
|
5847
|
+
};
|
|
5848
|
+
}
|
|
5849
|
+
this.runtimeMessages.push({
|
|
5850
|
+
key: dedupKey,
|
|
5851
|
+
message: {
|
|
5852
|
+
role: "system",
|
|
5853
|
+
senderName: "System",
|
|
5854
|
+
content: normalizedContent,
|
|
5855
|
+
receivedAt,
|
|
5856
|
+
timestamp: receivedAt
|
|
5857
|
+
}
|
|
5858
|
+
});
|
|
5859
|
+
if (this.runtimeMessages.length > 50) this.runtimeMessages = this.runtimeMessages.slice(-50);
|
|
5860
|
+
this.historyWriter.appendNewMessages(
|
|
5861
|
+
this.type,
|
|
5862
|
+
[{
|
|
5863
|
+
role: "system",
|
|
5864
|
+
senderName: "System",
|
|
5865
|
+
content: normalizedContent,
|
|
5866
|
+
kind: "system",
|
|
5867
|
+
receivedAt,
|
|
5868
|
+
historyDedupKey: dedupKey
|
|
5869
|
+
}],
|
|
5870
|
+
this.cachedChat?.title || this.provider.name,
|
|
5871
|
+
this.instanceId,
|
|
5872
|
+
this.cachedChat?.id || this.instanceId
|
|
5873
|
+
);
|
|
5874
|
+
}
|
|
5875
|
+
mergeConversationMessages(messages) {
|
|
5876
|
+
if (this.runtimeMessages.length === 0) return messages;
|
|
5877
|
+
return [...messages, ...this.runtimeMessages.map((entry) => entry.message)].map((message, index) => ({ message, index })).sort((a, b) => {
|
|
5878
|
+
const aTime = a.message.receivedAt || a.message.timestamp || 0;
|
|
5879
|
+
const bTime = b.message.receivedAt || b.message.timestamp || 0;
|
|
5880
|
+
if (aTime !== bTime) return aTime - bTime;
|
|
5881
|
+
return a.index - b.index;
|
|
5882
|
+
}).map((entry) => entry.message);
|
|
5883
|
+
}
|
|
5884
|
+
getPersistedEffectContent(effect) {
|
|
5885
|
+
if (effect.type === "message") {
|
|
5886
|
+
return typeof effect.message?.content === "string" ? effect.message.content : JSON.stringify(effect.message?.content || "");
|
|
5887
|
+
}
|
|
5888
|
+
if (effect.type === "toast") {
|
|
5889
|
+
return effect.toast?.message || null;
|
|
5890
|
+
}
|
|
5891
|
+
if (effect.type === "notification") {
|
|
5892
|
+
if (typeof effect.notification?.bubbleContent === "string") return effect.notification.bubbleContent;
|
|
5893
|
+
if (typeof effect.notification?.title === "string" && effect.notification.title.trim()) {
|
|
5894
|
+
return `${effect.notification.title}
|
|
5895
|
+
${effect.notification.body || ""}`.trim();
|
|
5896
|
+
}
|
|
5897
|
+
return effect.notification?.body || null;
|
|
5898
|
+
}
|
|
5899
|
+
return null;
|
|
5900
|
+
}
|
|
5901
|
+
getEffectDedupKey(effect) {
|
|
5902
|
+
if (effect.id) return `provider_effect:${effect.id}`;
|
|
5903
|
+
if (effect.type === "message") {
|
|
5904
|
+
return `provider_effect:message:${typeof effect.message?.content === "string" ? effect.message.content : JSON.stringify(effect.message?.content || "")}`;
|
|
5905
|
+
}
|
|
5906
|
+
if (effect.type === "notification") {
|
|
5907
|
+
return `provider_effect:notification:${effect.notification?.title || ""}:${effect.notification?.body || ""}`;
|
|
5908
|
+
}
|
|
5909
|
+
return `provider_effect:toast:${effect.toast?.message || ""}`;
|
|
5910
|
+
}
|
|
5289
5911
|
flushEvents() {
|
|
5290
5912
|
const events = [...this.events];
|
|
5291
5913
|
this.events = [];
|
|
@@ -7359,7 +7981,56 @@ function handleSetProviderSetting(h, args) {
|
|
|
7359
7981
|
}
|
|
7360
7982
|
return { success: false, error: `Failed to set ${providerType}.${key} \u2014 invalid key, value, or not a public setting` };
|
|
7361
7983
|
}
|
|
7362
|
-
|
|
7984
|
+
function normalizeProviderScriptArgs(args) {
|
|
7985
|
+
const normalizedArgs = { ...args || {} };
|
|
7986
|
+
for (const key of ["mode", "model", "message", "action", "button", "text", "sessionId", "value"]) {
|
|
7987
|
+
if (key in normalizedArgs && !(key.toUpperCase() in normalizedArgs)) {
|
|
7988
|
+
normalizedArgs[key.toUpperCase()] = normalizedArgs[key];
|
|
7989
|
+
}
|
|
7990
|
+
}
|
|
7991
|
+
return normalizedArgs;
|
|
7992
|
+
}
|
|
7993
|
+
function parseScriptResult(result) {
|
|
7994
|
+
if (typeof result === "string") {
|
|
7995
|
+
try {
|
|
7996
|
+
const parsed = JSON.parse(result);
|
|
7997
|
+
if (parsed && typeof parsed === "object" && parsed.success === false) {
|
|
7998
|
+
return { success: false, payload: parsed };
|
|
7999
|
+
}
|
|
8000
|
+
return { success: true, payload: parsed };
|
|
8001
|
+
} catch {
|
|
8002
|
+
return { success: true, payload: { result } };
|
|
8003
|
+
}
|
|
8004
|
+
}
|
|
8005
|
+
if (result && typeof result === "object" && result.success === false) {
|
|
8006
|
+
return { success: false, payload: result };
|
|
8007
|
+
}
|
|
8008
|
+
return { success: true, payload: result };
|
|
8009
|
+
}
|
|
8010
|
+
function getCliScriptCommand(payload) {
|
|
8011
|
+
if (!payload || typeof payload !== "object") return null;
|
|
8012
|
+
if (typeof payload.sendMessage === "string" && payload.sendMessage.trim()) {
|
|
8013
|
+
return { type: "send_message", text: payload.sendMessage.trim() };
|
|
8014
|
+
}
|
|
8015
|
+
const command = payload.command;
|
|
8016
|
+
if (!command || typeof command !== "object") return null;
|
|
8017
|
+
if (command.type !== "send_message") return null;
|
|
8018
|
+
const text = typeof command.text === "string" ? command.text.trim() : typeof command.message === "string" ? command.message.trim() : "";
|
|
8019
|
+
if (!text) return null;
|
|
8020
|
+
return { type: "send_message", text };
|
|
8021
|
+
}
|
|
8022
|
+
function applyProviderPatch(h, args, payload) {
|
|
8023
|
+
if (!payload || typeof payload !== "object") return;
|
|
8024
|
+
const targetSessionId = typeof args?.targetSessionId === "string" ? args.targetSessionId.trim() : "";
|
|
8025
|
+
const targetSession = targetSessionId ? h.ctx.sessionRegistry?.get(targetSessionId) : void 0;
|
|
8026
|
+
const instanceKey = targetSession?.instanceKey || targetSessionId;
|
|
8027
|
+
if (!instanceKey) return;
|
|
8028
|
+
h.ctx.instanceManager?.sendEvent(instanceKey, "provider_state_patch", {
|
|
8029
|
+
...payload,
|
|
8030
|
+
extensionType: targetSession?.transport === "cdp-webview" ? targetSession.providerType : void 0
|
|
8031
|
+
});
|
|
8032
|
+
}
|
|
8033
|
+
async function executeProviderScript(h, args, scriptName) {
|
|
7363
8034
|
const { agentType, ideType } = args || {};
|
|
7364
8035
|
if (!agentType) return { success: false, error: "agentType is required" };
|
|
7365
8036
|
const loader = h.ctx.providerLoader;
|
|
@@ -7372,13 +8043,29 @@ async function handleExtensionScript(h, args, scriptName) {
|
|
|
7372
8043
|
if (!provider.scripts?.[actualScriptName]) {
|
|
7373
8044
|
return { success: false, error: `Script '${actualScriptName}' not available for ${agentType}` };
|
|
7374
8045
|
}
|
|
7375
|
-
const
|
|
7376
|
-
|
|
7377
|
-
|
|
7378
|
-
if (
|
|
7379
|
-
|
|
8046
|
+
const normalizedArgs = normalizeProviderScriptArgs(args);
|
|
8047
|
+
if (provider.category === "cli") {
|
|
8048
|
+
const adapter = h.getCliAdapter(args?.targetSessionId || agentType);
|
|
8049
|
+
if (!adapter?.invokeScript) {
|
|
8050
|
+
return { success: false, error: `CLI adapter does not support script '${actualScriptName}'` };
|
|
8051
|
+
}
|
|
8052
|
+
try {
|
|
8053
|
+
const raw = await adapter.invokeScript(actualScriptName, normalizedArgs);
|
|
8054
|
+
const parsed = parseScriptResult(raw);
|
|
8055
|
+
if (!parsed.success) {
|
|
8056
|
+
return { success: false, ...parsed.payload || {} };
|
|
8057
|
+
}
|
|
8058
|
+
const cliCommand = getCliScriptCommand(parsed.payload);
|
|
8059
|
+
if (cliCommand?.type === "send_message" && cliCommand.text) {
|
|
8060
|
+
await adapter.sendMessage(cliCommand.text);
|
|
8061
|
+
}
|
|
8062
|
+
applyProviderPatch(h, args, parsed.payload);
|
|
8063
|
+
return { success: true, ...parsed.payload && typeof parsed.payload === "object" ? parsed.payload : { result: parsed.payload } };
|
|
8064
|
+
} catch (e) {
|
|
8065
|
+
return { success: false, error: `Script execution failed: ${e.message}` };
|
|
7380
8066
|
}
|
|
7381
8067
|
}
|
|
8068
|
+
const scriptFn = provider.scripts[actualScriptName];
|
|
7382
8069
|
const scriptCode = scriptFn(normalizedArgs);
|
|
7383
8070
|
if (!scriptCode) return { success: false, error: `Script '${actualScriptName}' returned null` };
|
|
7384
8071
|
const cdpKey = provider.category === "ide" ? h.currentSession?.cdpManagerKey || h.currentManagerKey || agentType : h.currentSession?.cdpManagerKey || h.currentManagerKey || ideType;
|
|
@@ -7432,16 +8119,29 @@ async function handleExtensionScript(h, args, scriptName) {
|
|
|
7432
8119
|
if (typeof result === "string") {
|
|
7433
8120
|
try {
|
|
7434
8121
|
const parsed = JSON.parse(result);
|
|
8122
|
+
applyProviderPatch(h, args, parsed);
|
|
8123
|
+
if (parsed && typeof parsed === "object" && parsed.success === false) {
|
|
8124
|
+
return { success: false, ...parsed };
|
|
8125
|
+
}
|
|
7435
8126
|
return { success: true, ...parsed };
|
|
7436
8127
|
} catch {
|
|
7437
8128
|
return { success: true, result };
|
|
7438
8129
|
}
|
|
7439
8130
|
}
|
|
8131
|
+
applyProviderPatch(h, args, result);
|
|
7440
8132
|
return { success: true, result };
|
|
7441
8133
|
} catch (e) {
|
|
7442
8134
|
return { success: false, error: `Script execution failed: ${e.message}` };
|
|
7443
8135
|
}
|
|
7444
8136
|
}
|
|
8137
|
+
async function handleExtensionScript(h, args, scriptName) {
|
|
8138
|
+
return executeProviderScript(h, args, scriptName);
|
|
8139
|
+
}
|
|
8140
|
+
async function handleProviderScript(h, args) {
|
|
8141
|
+
const scriptName = typeof args?.scriptName === "string" ? args.scriptName.trim() : "";
|
|
8142
|
+
if (!scriptName) return { success: false, error: "scriptName is required" };
|
|
8143
|
+
return executeProviderScript(h, args, scriptName);
|
|
8144
|
+
}
|
|
7445
8145
|
function handleGetIdeExtensions(h, args) {
|
|
7446
8146
|
const { ideType } = args || {};
|
|
7447
8147
|
const loader = h.ctx.providerLoader;
|
|
@@ -7941,6 +8641,8 @@ var DaemonCommandHandler = class {
|
|
|
7941
8641
|
case "set_ide_extension":
|
|
7942
8642
|
return handleSetIdeExtension(this, args);
|
|
7943
8643
|
// ─── Extension Model / Mode Control (stream-commands.ts) ──────────
|
|
8644
|
+
case "invoke_provider_script":
|
|
8645
|
+
return handleProviderScript(this, args);
|
|
7944
8646
|
case "list_extension_models":
|
|
7945
8647
|
return handleExtensionScript(this, args, "listModels");
|
|
7946
8648
|
case "set_extension_model":
|
|
@@ -8117,6 +8819,8 @@ var CliProviderInstance = class {
|
|
|
8117
8819
|
generatingDebounceTimer = null;
|
|
8118
8820
|
generatingDebouncePending = null;
|
|
8119
8821
|
lastApprovalEventAt = 0;
|
|
8822
|
+
controlValues = {};
|
|
8823
|
+
appliedEffectKeys = /* @__PURE__ */ new Set();
|
|
8120
8824
|
historyWriter;
|
|
8121
8825
|
runtimeMessages = [];
|
|
8122
8826
|
instanceId;
|
|
@@ -8129,6 +8833,7 @@ var CliProviderInstance = class {
|
|
|
8129
8833
|
async init(context) {
|
|
8130
8834
|
this.context = context;
|
|
8131
8835
|
this.settings = context.settings || {};
|
|
8836
|
+
this.adapter.updateRuntimeSettings?.(this.settings);
|
|
8132
8837
|
this.monitor.updateConfig({
|
|
8133
8838
|
approvalAlert: this.settings.approvalAlert !== false,
|
|
8134
8839
|
longGeneratingAlert: this.settings.longGeneratingAlert !== false,
|
|
@@ -8144,6 +8849,21 @@ var CliProviderInstance = class {
|
|
|
8144
8849
|
this.detectStatusTransition();
|
|
8145
8850
|
});
|
|
8146
8851
|
await this.adapter.spawn();
|
|
8852
|
+
if (this.providerSessionId) {
|
|
8853
|
+
const restoredHistory = readChatHistory(this.type, 0, 200, this.providerSessionId);
|
|
8854
|
+
if (restoredHistory.messages.length > 0) {
|
|
8855
|
+
this.adapter.seedCommittedMessages(
|
|
8856
|
+
restoredHistory.messages.map((message) => ({
|
|
8857
|
+
role: message.role,
|
|
8858
|
+
content: message.content,
|
|
8859
|
+
timestamp: message.receivedAt,
|
|
8860
|
+
receivedAt: message.receivedAt,
|
|
8861
|
+
kind: message.kind,
|
|
8862
|
+
senderName: message.senderName
|
|
8863
|
+
}))
|
|
8864
|
+
);
|
|
8865
|
+
}
|
|
8866
|
+
}
|
|
8147
8867
|
if (this.providerSessionId && this.launchMode === "resume") {
|
|
8148
8868
|
const resumedAt = Date.now();
|
|
8149
8869
|
this.historyWriter.appendSystemMarker(
|
|
@@ -8224,6 +8944,12 @@ var CliProviderInstance = class {
|
|
|
8224
8944
|
}
|
|
8225
8945
|
const runtime = this.adapter.getRuntimeMetadata();
|
|
8226
8946
|
const parsedMessages = Array.isArray(parsedStatus?.messages) ? parsedStatus.messages : [];
|
|
8947
|
+
const controlValues = extractProviderControlValues(this.provider.controls, parsedStatus);
|
|
8948
|
+
if (controlValues) {
|
|
8949
|
+
this.controlValues = controlValues;
|
|
8950
|
+
} else if (Object.keys(this.controlValues).length > 0) {
|
|
8951
|
+
this.controlValues = {};
|
|
8952
|
+
}
|
|
8227
8953
|
const mergedMessages = this.mergeConversationMessages(parsedMessages);
|
|
8228
8954
|
const dirName = this.workingDir.split("/").filter(Boolean).pop() || "session";
|
|
8229
8955
|
if (parsedMessages.length > 0) {
|
|
@@ -8244,6 +8970,7 @@ var CliProviderInstance = class {
|
|
|
8244
8970
|
);
|
|
8245
8971
|
}
|
|
8246
8972
|
}
|
|
8973
|
+
this.applyProviderResponse(parsedStatus, { phase: "immediate" });
|
|
8247
8974
|
return {
|
|
8248
8975
|
type: this.type,
|
|
8249
8976
|
name: this.provider.name,
|
|
@@ -8273,8 +9000,7 @@ var CliProviderInstance = class {
|
|
|
8273
9000
|
attachedClients: runtime.attachedClients || []
|
|
8274
9001
|
} : void 0,
|
|
8275
9002
|
resume: this.provider.resume,
|
|
8276
|
-
controlValues:
|
|
8277
|
-
// CLI controls not yet wired from stream
|
|
9003
|
+
controlValues: this.controlValues,
|
|
8278
9004
|
providerControls: this.provider.controls
|
|
8279
9005
|
};
|
|
8280
9006
|
}
|
|
@@ -8285,6 +9011,15 @@ var CliProviderInstance = class {
|
|
|
8285
9011
|
getPresentationMode() {
|
|
8286
9012
|
return this.presentationMode;
|
|
8287
9013
|
}
|
|
9014
|
+
updateSettings(newSettings) {
|
|
9015
|
+
this.settings = { ...newSettings };
|
|
9016
|
+
this.adapter.updateRuntimeSettings?.(this.settings);
|
|
9017
|
+
this.monitor.updateConfig({
|
|
9018
|
+
approvalAlert: this.settings.approvalAlert !== false,
|
|
9019
|
+
longGeneratingAlert: this.settings.longGeneratingAlert !== false,
|
|
9020
|
+
longGeneratingThresholdSec: this.settings.longGeneratingThresholdSec || 180
|
|
9021
|
+
});
|
|
9022
|
+
}
|
|
8288
9023
|
onEvent(event, data) {
|
|
8289
9024
|
if (event === "send_message" && data?.text) {
|
|
8290
9025
|
void this.adapter.sendMessage(data.text).catch((e) => {
|
|
@@ -8296,22 +9031,27 @@ var CliProviderInstance = class {
|
|
|
8296
9031
|
void this.adapter.resolveAction(data).catch((e) => {
|
|
8297
9032
|
LOG.warn("CLI", `[${this.type}] resolve_action failed: ${e?.message || e}`);
|
|
8298
9033
|
});
|
|
9034
|
+
} else if (event === "provider_state_patch" && data && typeof data === "object") {
|
|
9035
|
+
this.applyProviderResponse(data, { phase: "immediate" });
|
|
8299
9036
|
}
|
|
8300
9037
|
}
|
|
8301
9038
|
dispose() {
|
|
8302
9039
|
this.adapter.shutdown();
|
|
8303
9040
|
this.monitor.reset();
|
|
9041
|
+
this.appliedEffectKeys.clear();
|
|
8304
9042
|
}
|
|
8305
9043
|
completedDebounceTimer = null;
|
|
8306
9044
|
completedDebouncePending = null;
|
|
8307
9045
|
detectStatusTransition() {
|
|
8308
9046
|
const now = Date.now();
|
|
8309
9047
|
const adapterStatus = this.adapter.getStatus();
|
|
9048
|
+
const parsedStatus = this.adapter.getScriptParsedStatus?.() || null;
|
|
8310
9049
|
const newStatus = adapterStatus.status;
|
|
8311
9050
|
const dirName = this.workingDir.split("/").filter(Boolean).pop() || "session";
|
|
8312
9051
|
const chatTitle = `${this.provider.name} \xB7 ${dirName}`;
|
|
8313
9052
|
const partial = this.adapter.getPartialResponse();
|
|
8314
9053
|
const progressFingerprint = newStatus === "generating" ? `${partial || ""}::${adapterStatus.messages.at(-1)?.content || ""}`.slice(-2e3) : void 0;
|
|
9054
|
+
const previousStatus = this.lastStatus;
|
|
8315
9055
|
if (newStatus !== this.lastStatus) {
|
|
8316
9056
|
LOG.info("CLI", `[${this.type}] status: ${this.lastStatus} \u2192 ${newStatus}`);
|
|
8317
9057
|
if (this.lastStatus === "idle" && newStatus === "generating") {
|
|
@@ -8404,6 +9144,9 @@ var CliProviderInstance = class {
|
|
|
8404
9144
|
}
|
|
8405
9145
|
this.lastStatus = newStatus;
|
|
8406
9146
|
}
|
|
9147
|
+
this.applyProviderResponse(parsedStatus, {
|
|
9148
|
+
phase: newStatus === "idle" && (previousStatus === "generating" || previousStatus === "waiting_approval") ? "turn_completed" : "immediate"
|
|
9149
|
+
});
|
|
8407
9150
|
const agentKey = `${this.type}:cli`;
|
|
8408
9151
|
const monitorEvents = this.monitor.check(agentKey, newStatus, now, progressFingerprint);
|
|
8409
9152
|
for (const me of monitorEvents) {
|
|
@@ -8419,6 +9162,88 @@ var CliProviderInstance = class {
|
|
|
8419
9162
|
this.events = [];
|
|
8420
9163
|
return events;
|
|
8421
9164
|
}
|
|
9165
|
+
applyProviderResponse(data, options) {
|
|
9166
|
+
if (!data || typeof data !== "object") return;
|
|
9167
|
+
const controlValues = extractProviderControlValues(this.provider.controls, data);
|
|
9168
|
+
if (controlValues) {
|
|
9169
|
+
this.controlValues = { ...this.controlValues, ...controlValues };
|
|
9170
|
+
}
|
|
9171
|
+
const effects = normalizeProviderEffects(data);
|
|
9172
|
+
for (const effect of effects) {
|
|
9173
|
+
const effectWhen = effect.when || "immediate";
|
|
9174
|
+
if (effectWhen === "turn_completed" && options.phase !== "turn_completed") continue;
|
|
9175
|
+
if (effectWhen === "immediate" && options.phase === "turn_completed") continue;
|
|
9176
|
+
const effectKey = this.getEffectDedupKey(effect);
|
|
9177
|
+
if (this.appliedEffectKeys.has(effectKey)) continue;
|
|
9178
|
+
this.appliedEffectKeys.add(effectKey);
|
|
9179
|
+
if (effect.persist !== false) {
|
|
9180
|
+
const persisted = this.getPersistedEffectContent(effect);
|
|
9181
|
+
if (persisted) this.appendRuntimeSystemMessage(persisted, effectKey);
|
|
9182
|
+
}
|
|
9183
|
+
if (effect.type === "message" && effect.message) {
|
|
9184
|
+
const content = typeof effect.message.content === "string" ? effect.message.content : JSON.stringify(effect.message.content);
|
|
9185
|
+
this.pushEvent({
|
|
9186
|
+
event: "provider:message",
|
|
9187
|
+
timestamp: Date.now(),
|
|
9188
|
+
content,
|
|
9189
|
+
role: effect.message.role || "system",
|
|
9190
|
+
kind: effect.message.kind,
|
|
9191
|
+
senderName: effect.message.senderName
|
|
9192
|
+
});
|
|
9193
|
+
} else if (effect.type === "toast" && effect.toast) {
|
|
9194
|
+
this.pushEvent({
|
|
9195
|
+
event: "provider:toast",
|
|
9196
|
+
effectId: effect.id || effectKey,
|
|
9197
|
+
timestamp: Date.now(),
|
|
9198
|
+
message: effect.toast.message,
|
|
9199
|
+
level: effect.toast.level || "info"
|
|
9200
|
+
});
|
|
9201
|
+
} else if (effect.type === "notification" && effect.notification) {
|
|
9202
|
+
this.pushEvent({
|
|
9203
|
+
event: "provider:notification",
|
|
9204
|
+
effectId: effect.id || effectKey,
|
|
9205
|
+
timestamp: Date.now(),
|
|
9206
|
+
title: effect.notification.title,
|
|
9207
|
+
message: effect.notification.body,
|
|
9208
|
+
content: typeof effect.notification.bubbleContent === "string" ? effect.notification.bubbleContent : effect.notification.body,
|
|
9209
|
+
level: effect.notification.level || "info",
|
|
9210
|
+
channels: effect.notification.channels || ["toast"],
|
|
9211
|
+
preferenceKey: effect.notification.preferenceKey
|
|
9212
|
+
});
|
|
9213
|
+
}
|
|
9214
|
+
}
|
|
9215
|
+
if (this.appliedEffectKeys.size > 200) {
|
|
9216
|
+
this.appliedEffectKeys = new Set(Array.from(this.appliedEffectKeys).slice(-100));
|
|
9217
|
+
}
|
|
9218
|
+
}
|
|
9219
|
+
getEffectDedupKey(effect) {
|
|
9220
|
+
if (effect.id) return `provider_effect:${effect.id}`;
|
|
9221
|
+
if (effect.type === "message") {
|
|
9222
|
+
const content = typeof effect.message?.content === "string" ? effect.message.content : JSON.stringify(effect.message?.content || "");
|
|
9223
|
+
return `provider_effect:message:${content}`;
|
|
9224
|
+
}
|
|
9225
|
+
if (effect.type === "notification") {
|
|
9226
|
+
return `provider_effect:notification:${effect.notification?.title || ""}:${effect.notification?.body || ""}`;
|
|
9227
|
+
}
|
|
9228
|
+
return `provider_effect:toast:${effect.toast?.message || ""}`;
|
|
9229
|
+
}
|
|
9230
|
+
getPersistedEffectContent(effect) {
|
|
9231
|
+
if (effect.type === "message") {
|
|
9232
|
+
return typeof effect.message?.content === "string" ? effect.message.content : JSON.stringify(effect.message?.content || "");
|
|
9233
|
+
}
|
|
9234
|
+
if (effect.type === "toast") {
|
|
9235
|
+
return effect.toast?.message || null;
|
|
9236
|
+
}
|
|
9237
|
+
if (effect.type === "notification") {
|
|
9238
|
+
if (typeof effect.notification?.bubbleContent === "string") return effect.notification.bubbleContent;
|
|
9239
|
+
if (typeof effect.notification?.title === "string" && effect.notification.title.trim()) {
|
|
9240
|
+
return `${effect.notification.title}
|
|
9241
|
+
${effect.notification.body || ""}`.trim();
|
|
9242
|
+
}
|
|
9243
|
+
return effect.notification?.body || null;
|
|
9244
|
+
}
|
|
9245
|
+
return null;
|
|
9246
|
+
}
|
|
8422
9247
|
// ─── Adapter access (backward compat) ──────────────────
|
|
8423
9248
|
getAdapter() {
|
|
8424
9249
|
return this.adapter;
|
|
@@ -11196,6 +12021,22 @@ function getMacAppIdentifiers() {
|
|
|
11196
12021
|
function getWinProcessNames() {
|
|
11197
12022
|
return getProviderLoader().getWinProcessNames();
|
|
11198
12023
|
}
|
|
12024
|
+
function getProviderMeta(ideId) {
|
|
12025
|
+
return getProviderLoader().getMeta(ideId);
|
|
12026
|
+
}
|
|
12027
|
+
function getPreferredLaunchMethod(ideId, platform9) {
|
|
12028
|
+
const prefer = getProviderMeta(ideId)?.launch?.prefer;
|
|
12029
|
+
const value = prefer?.[platform9];
|
|
12030
|
+
return value === "cli" || value === "app" || value === "auto" ? value : "auto";
|
|
12031
|
+
}
|
|
12032
|
+
function getCdpStartupTimeoutMs(ideId) {
|
|
12033
|
+
const value = getProviderMeta(ideId)?.launch?.cdpStartupTimeoutMs;
|
|
12034
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return 15e3;
|
|
12035
|
+
return Math.max(1e3, Math.floor(value));
|
|
12036
|
+
}
|
|
12037
|
+
function escapeForAppleScript(value) {
|
|
12038
|
+
return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
12039
|
+
}
|
|
11199
12040
|
async function findFreePort(ports) {
|
|
11200
12041
|
for (const port2 of ports) {
|
|
11201
12042
|
const free = await checkPortFree(port2);
|
|
@@ -11248,12 +12089,12 @@ async function killIdeProcess(ideId) {
|
|
|
11248
12089
|
try {
|
|
11249
12090
|
if (plat === "darwin" && appName) {
|
|
11250
12091
|
try {
|
|
11251
|
-
execSync4(`osascript -e 'tell application "${appName}" to quit' 2>/dev/null`, {
|
|
12092
|
+
execSync4(`osascript -e 'tell application "${escapeForAppleScript(appName)}" to quit' 2>/dev/null`, {
|
|
11252
12093
|
timeout: 5e3
|
|
11253
12094
|
});
|
|
11254
12095
|
} catch {
|
|
11255
12096
|
try {
|
|
11256
|
-
execSync4(`pkill -
|
|
12097
|
+
execSync4(`pkill -x "${appName}" 2>/dev/null`, { timeout: 5e3 });
|
|
11257
12098
|
} catch {
|
|
11258
12099
|
}
|
|
11259
12100
|
}
|
|
@@ -11283,7 +12124,7 @@ async function killIdeProcess(ideId) {
|
|
|
11283
12124
|
}
|
|
11284
12125
|
if (plat === "darwin" && appName) {
|
|
11285
12126
|
try {
|
|
11286
|
-
execSync4(`pkill -9 -
|
|
12127
|
+
execSync4(`pkill -9 -x "${appName}" 2>/dev/null`, { timeout: 5e3 });
|
|
11287
12128
|
} catch {
|
|
11288
12129
|
}
|
|
11289
12130
|
} else if (plat === "win32" && winProcesses) {
|
|
@@ -11306,8 +12147,23 @@ function isIdeRunning(ideId) {
|
|
|
11306
12147
|
if (plat === "darwin") {
|
|
11307
12148
|
const appName = getMacAppIdentifiers()[ideId];
|
|
11308
12149
|
if (!appName) return false;
|
|
11309
|
-
|
|
11310
|
-
|
|
12150
|
+
try {
|
|
12151
|
+
const result = execSync4(`pgrep -x "${appName}" 2>/dev/null`, {
|
|
12152
|
+
encoding: "utf-8",
|
|
12153
|
+
timeout: 3e3
|
|
12154
|
+
});
|
|
12155
|
+
return result.trim().length > 0;
|
|
12156
|
+
} catch {
|
|
12157
|
+
const result = execSync4(
|
|
12158
|
+
`osascript -e 'tell application "System Events" to count (every process whose name is "${escapeForAppleScript(appName)}")'`,
|
|
12159
|
+
{
|
|
12160
|
+
encoding: "utf-8",
|
|
12161
|
+
timeout: 3e3,
|
|
12162
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
12163
|
+
}
|
|
12164
|
+
);
|
|
12165
|
+
return Number.parseInt(result.trim() || "0", 10) > 0;
|
|
12166
|
+
}
|
|
11311
12167
|
} else if (plat === "win32") {
|
|
11312
12168
|
const winProcesses = getWinProcessNames()[ideId];
|
|
11313
12169
|
if (!winProcesses) return false;
|
|
@@ -11456,7 +12312,8 @@ async function launchWithCdp(options = {}) {
|
|
|
11456
12312
|
await launchLinux(targetIde, port, workspace, options.newWindow);
|
|
11457
12313
|
}
|
|
11458
12314
|
let cdpReady = false;
|
|
11459
|
-
|
|
12315
|
+
const waitDeadline = Date.now() + getCdpStartupTimeoutMs(targetIde.id);
|
|
12316
|
+
while (Date.now() < waitDeadline) {
|
|
11460
12317
|
await new Promise((r) => setTimeout(r, 500));
|
|
11461
12318
|
if (await isCdpActive(port)) {
|
|
11462
12319
|
cdpReady = true;
|
|
@@ -11485,14 +12342,18 @@ async function launchWithCdp(options = {}) {
|
|
|
11485
12342
|
}
|
|
11486
12343
|
async function launchMacOS(ide, port, workspace, newWindow) {
|
|
11487
12344
|
const appName = getMacAppIdentifiers()[ide.id];
|
|
12345
|
+
const preferredMethod = getPreferredLaunchMethod(ide.id, "darwin");
|
|
11488
12346
|
const args = ["--remote-debugging-port=" + port];
|
|
11489
12347
|
if (newWindow) args.push("--new-window");
|
|
11490
12348
|
if (workspace) args.push(workspace);
|
|
11491
|
-
|
|
12349
|
+
const canUseCli = !!ide.cliCommand;
|
|
12350
|
+
const canUseAppLauncher = !!appName;
|
|
12351
|
+
const useAppLauncher = preferredMethod === "app" ? canUseAppLauncher : preferredMethod === "cli" ? false : !canUseCli && canUseAppLauncher;
|
|
12352
|
+
if (!useAppLauncher && ide.cliCommand) {
|
|
12353
|
+
spawn2(ide.cliCommand, args, { detached: true, stdio: "ignore" }).unref();
|
|
12354
|
+
} else if (appName) {
|
|
11492
12355
|
const openArgs = ["-a", appName, "--args", ...args];
|
|
11493
12356
|
spawn2("open", openArgs, { detached: true, stdio: "ignore" }).unref();
|
|
11494
|
-
} else if (ide.cliCommand) {
|
|
11495
|
-
spawn2(ide.cliCommand, args, { detached: true, stdio: "ignore" }).unref();
|
|
11496
12357
|
} else {
|
|
11497
12358
|
throw new Error(`No app identifier or CLI for ${ide.displayName}`);
|
|
11498
12359
|
}
|
|
@@ -11803,6 +12664,7 @@ function buildStatusSnapshot(options) {
|
|
|
11803
12664
|
workspaces: wsState.workspaces,
|
|
11804
12665
|
defaultWorkspaceId: wsState.defaultWorkspaceId,
|
|
11805
12666
|
defaultWorkspacePath: wsState.defaultWorkspacePath,
|
|
12667
|
+
terminalSizingMode: cfg.terminalSizingMode || "measured",
|
|
11806
12668
|
recentLaunches: buildRecentLaunches(recentActivity),
|
|
11807
12669
|
terminalBackend,
|
|
11808
12670
|
availableProviders: buildAvailableProviders(options.providerLoader)
|
|
@@ -12650,19 +13512,10 @@ var ProviderStreamAdapter = class {
|
|
|
12650
13512
|
mode: data.mode,
|
|
12651
13513
|
activeModal: data.activeModal
|
|
12652
13514
|
};
|
|
12653
|
-
|
|
12654
|
-
|
|
12655
|
-
|
|
12656
|
-
|
|
12657
|
-
const val = data[ctrl.readFrom];
|
|
12658
|
-
if (val !== void 0 && val !== null) {
|
|
12659
|
-
cv[ctrl.id] = typeof val === "object" ? val.name || val.id || String(val) : val;
|
|
12660
|
-
}
|
|
12661
|
-
}
|
|
12662
|
-
if (data.model && !cv["model"]) cv["model"] = data.model;
|
|
12663
|
-
if (data.mode && !cv["mode"]) cv["mode"] = data.mode;
|
|
12664
|
-
if (Object.keys(cv).length > 0) state.controlValues = cv;
|
|
12665
|
-
}
|
|
13515
|
+
const controlValues = extractProviderControlValues(this.provider.controls, data);
|
|
13516
|
+
if (controlValues) state.controlValues = controlValues;
|
|
13517
|
+
const effects = normalizeProviderEffects(data);
|
|
13518
|
+
if (effects.length > 0) state.effects = effects;
|
|
12666
13519
|
if (state.messages.length > 0) {
|
|
12667
13520
|
this.lastSuccessState = state;
|
|
12668
13521
|
}
|
|
@@ -13188,6 +14041,8 @@ function forwardAgentStreamsToIdeInstance(instanceManager, ideType, streams) {
|
|
|
13188
14041
|
activeModal: stream.activeModal || null,
|
|
13189
14042
|
model: stream.model || void 0,
|
|
13190
14043
|
mode: stream.mode || void 0,
|
|
14044
|
+
controlValues: stream.controlValues || void 0,
|
|
14045
|
+
effects: stream.effects || void 0,
|
|
13191
14046
|
sessionId: stream.sessionId || stream.instanceId || void 0,
|
|
13192
14047
|
title: stream.title || stream.agentName || void 0,
|
|
13193
14048
|
agentType: stream.agentType || void 0,
|