@adhdev/daemon-core 0.8.21 → 0.8.23
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 +1163 -314
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1163 -314
- 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/chat-commands.ts +33 -12
- 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
|
-
|
|
4658
|
-
|
|
4659
|
-
|
|
4660
|
-
const events = [...this.events];
|
|
4661
|
-
this.events = [];
|
|
4662
|
-
return events;
|
|
4663
|
-
}
|
|
4664
|
-
resolveChatTitle(data) {
|
|
4665
|
-
const title = typeof data?.title === "string" && data.title.trim() ? data.title.trim() : this.chatTitle;
|
|
4666
|
-
return title || this.agentName || this.provider.name;
|
|
4819
|
+
return effects;
|
|
4820
|
+
}
|
|
4821
|
+
function normalizeControlValue(value) {
|
|
4822
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
4823
|
+
return value;
|
|
4667
4824
|
}
|
|
4668
|
-
|
|
4669
|
-
if (
|
|
4670
|
-
|
|
4671
|
-
|
|
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();
|
|
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;
|
|
4691
4829
|
}
|
|
4692
|
-
|
|
4830
|
+
return String(value);
|
|
4831
|
+
}
|
|
4693
4832
|
|
|
4694
4833
|
// src/config/chat-history.ts
|
|
4695
4834
|
import * as fs3 from "fs";
|
|
@@ -4942,28 +5081,357 @@ function listSavedHistorySessions(agentType, options = {}) {
|
|
|
4942
5081
|
if (parsed.role !== "system" && parsed.content.trim()) preview = parsed.content.trim();
|
|
4943
5082
|
}
|
|
4944
5083
|
}
|
|
4945
|
-
if (messageCount === 0 || !lastMessageAt) continue;
|
|
4946
|
-
summaries.push({
|
|
4947
|
-
historySessionId,
|
|
4948
|
-
sessionTitle: sessionTitle || void 0,
|
|
4949
|
-
messageCount,
|
|
4950
|
-
firstMessageAt,
|
|
4951
|
-
lastMessageAt,
|
|
4952
|
-
preview: preview || void 0
|
|
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();
|
|
5386
|
+
}
|
|
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 = [];
|
|
@@ -6170,6 +6792,24 @@ function isRecentDuplicateSend(key) {
|
|
|
6170
6792
|
recentSendByTarget.set(key, now);
|
|
6171
6793
|
return false;
|
|
6172
6794
|
}
|
|
6795
|
+
function parseMaybeJson(value) {
|
|
6796
|
+
if (typeof value !== "string") return value;
|
|
6797
|
+
try {
|
|
6798
|
+
return JSON.parse(value);
|
|
6799
|
+
} catch {
|
|
6800
|
+
return value;
|
|
6801
|
+
}
|
|
6802
|
+
}
|
|
6803
|
+
function didProviderConfirmSend(result) {
|
|
6804
|
+
const parsed = parseMaybeJson(result);
|
|
6805
|
+
if (parsed === true) return true;
|
|
6806
|
+
if (typeof parsed === "string") {
|
|
6807
|
+
const normalized = parsed.trim().toLowerCase();
|
|
6808
|
+
return normalized === "ok" || normalized === "sent" || normalized === "success" || normalized === "true";
|
|
6809
|
+
}
|
|
6810
|
+
if (!parsed || typeof parsed !== "object") return false;
|
|
6811
|
+
return parsed.sent === true || parsed.success === true || parsed.ok === true || parsed.submitted === true || parsed.dispatched === true;
|
|
6812
|
+
}
|
|
6173
6813
|
async function handleChatHistory(h, args) {
|
|
6174
6814
|
const { agentType, offset, limit } = args;
|
|
6175
6815
|
const historySessionId = getHistorySessionId(h, args);
|
|
@@ -6352,14 +6992,8 @@ async function handleSendChat(h, args) {
|
|
|
6352
6992
|
try {
|
|
6353
6993
|
const evalResult = await h.evaluateProviderScript("sendMessage", { MESSAGE: text }, 3e4);
|
|
6354
6994
|
if (evalResult?.result) {
|
|
6355
|
-
|
|
6356
|
-
if (
|
|
6357
|
-
try {
|
|
6358
|
-
parsed = JSON.parse(parsed);
|
|
6359
|
-
} catch {
|
|
6360
|
-
}
|
|
6361
|
-
}
|
|
6362
|
-
if (parsed?.sent) {
|
|
6995
|
+
const parsed = parseMaybeJson(evalResult.result);
|
|
6996
|
+
if (didProviderConfirmSend(parsed)) {
|
|
6363
6997
|
_log(`Extension script sent OK`);
|
|
6364
6998
|
return _logSendSuccess("extension-script");
|
|
6365
6999
|
}
|
|
@@ -6391,14 +7025,8 @@ async function handleSendChat(h, args) {
|
|
|
6391
7025
|
if (sendScript) {
|
|
6392
7026
|
try {
|
|
6393
7027
|
const result = await targetCdp.evaluate(sendScript, 3e4);
|
|
6394
|
-
|
|
6395
|
-
if (
|
|
6396
|
-
try {
|
|
6397
|
-
parsed = JSON.parse(result);
|
|
6398
|
-
} catch {
|
|
6399
|
-
}
|
|
6400
|
-
}
|
|
6401
|
-
if (parsed?.sent) {
|
|
7028
|
+
const parsed = parseMaybeJson(result);
|
|
7029
|
+
if (didProviderConfirmSend(parsed)) {
|
|
6402
7030
|
_log(`sendMessage script OK`);
|
|
6403
7031
|
return _logSendSuccess("script");
|
|
6404
7032
|
}
|
|
@@ -6443,14 +7071,8 @@ async function handleSendChat(h, args) {
|
|
|
6443
7071
|
const matchText = provider.webviewMatchText;
|
|
6444
7072
|
const matchFn = matchText ? (body) => body.includes(matchText) : void 0;
|
|
6445
7073
|
const wvResult = await targetCdp.evaluateInWebviewFrame(webviewScript, matchFn);
|
|
6446
|
-
|
|
6447
|
-
if (
|
|
6448
|
-
try {
|
|
6449
|
-
wvParsed = JSON.parse(wvResult);
|
|
6450
|
-
} catch {
|
|
6451
|
-
}
|
|
6452
|
-
}
|
|
6453
|
-
if (wvParsed?.sent) {
|
|
7074
|
+
const wvParsed = parseMaybeJson(wvResult);
|
|
7075
|
+
if (didProviderConfirmSend(wvParsed)) {
|
|
6454
7076
|
_log(`webviewSendMessage OK`);
|
|
6455
7077
|
return _logSendSuccess("webview-script");
|
|
6456
7078
|
}
|
|
@@ -6472,14 +7094,8 @@ async function handleSendChat(h, args) {
|
|
|
6472
7094
|
const matchText = provider.webviewMatchText;
|
|
6473
7095
|
const matchFn = matchText ? (body) => body.includes(matchText) : void 0;
|
|
6474
7096
|
const wvResult = await targetCdp.evaluateInWebviewFrame(webviewScript, matchFn);
|
|
6475
|
-
|
|
6476
|
-
if (
|
|
6477
|
-
try {
|
|
6478
|
-
wvParsed = JSON.parse(wvResult);
|
|
6479
|
-
} catch {
|
|
6480
|
-
}
|
|
6481
|
-
}
|
|
6482
|
-
if (wvParsed?.sent) {
|
|
7097
|
+
const wvParsed = parseMaybeJson(wvResult);
|
|
7098
|
+
if (didProviderConfirmSend(wvParsed)) {
|
|
6483
7099
|
_log(`webviewSendMessage OK`);
|
|
6484
7100
|
return _logSendSuccess("webview-script");
|
|
6485
7101
|
}
|
|
@@ -7365,7 +7981,56 @@ function handleSetProviderSetting(h, args) {
|
|
|
7365
7981
|
}
|
|
7366
7982
|
return { success: false, error: `Failed to set ${providerType}.${key} \u2014 invalid key, value, or not a public setting` };
|
|
7367
7983
|
}
|
|
7368
|
-
|
|
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) {
|
|
7369
8034
|
const { agentType, ideType } = args || {};
|
|
7370
8035
|
if (!agentType) return { success: false, error: "agentType is required" };
|
|
7371
8036
|
const loader = h.ctx.providerLoader;
|
|
@@ -7378,13 +8043,29 @@ async function handleExtensionScript(h, args, scriptName) {
|
|
|
7378
8043
|
if (!provider.scripts?.[actualScriptName]) {
|
|
7379
8044
|
return { success: false, error: `Script '${actualScriptName}' not available for ${agentType}` };
|
|
7380
8045
|
}
|
|
7381
|
-
const
|
|
7382
|
-
|
|
7383
|
-
|
|
7384
|
-
if (
|
|
7385
|
-
|
|
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}` };
|
|
7386
8066
|
}
|
|
7387
8067
|
}
|
|
8068
|
+
const scriptFn = provider.scripts[actualScriptName];
|
|
7388
8069
|
const scriptCode = scriptFn(normalizedArgs);
|
|
7389
8070
|
if (!scriptCode) return { success: false, error: `Script '${actualScriptName}' returned null` };
|
|
7390
8071
|
const cdpKey = provider.category === "ide" ? h.currentSession?.cdpManagerKey || h.currentManagerKey || agentType : h.currentSession?.cdpManagerKey || h.currentManagerKey || ideType;
|
|
@@ -7438,16 +8119,29 @@ async function handleExtensionScript(h, args, scriptName) {
|
|
|
7438
8119
|
if (typeof result === "string") {
|
|
7439
8120
|
try {
|
|
7440
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
|
+
}
|
|
7441
8126
|
return { success: true, ...parsed };
|
|
7442
8127
|
} catch {
|
|
7443
8128
|
return { success: true, result };
|
|
7444
8129
|
}
|
|
7445
8130
|
}
|
|
8131
|
+
applyProviderPatch(h, args, result);
|
|
7446
8132
|
return { success: true, result };
|
|
7447
8133
|
} catch (e) {
|
|
7448
8134
|
return { success: false, error: `Script execution failed: ${e.message}` };
|
|
7449
8135
|
}
|
|
7450
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
|
+
}
|
|
7451
8145
|
function handleGetIdeExtensions(h, args) {
|
|
7452
8146
|
const { ideType } = args || {};
|
|
7453
8147
|
const loader = h.ctx.providerLoader;
|
|
@@ -7947,6 +8641,8 @@ var DaemonCommandHandler = class {
|
|
|
7947
8641
|
case "set_ide_extension":
|
|
7948
8642
|
return handleSetIdeExtension(this, args);
|
|
7949
8643
|
// ─── Extension Model / Mode Control (stream-commands.ts) ──────────
|
|
8644
|
+
case "invoke_provider_script":
|
|
8645
|
+
return handleProviderScript(this, args);
|
|
7950
8646
|
case "list_extension_models":
|
|
7951
8647
|
return handleExtensionScript(this, args, "listModels");
|
|
7952
8648
|
case "set_extension_model":
|
|
@@ -8123,6 +8819,8 @@ var CliProviderInstance = class {
|
|
|
8123
8819
|
generatingDebounceTimer = null;
|
|
8124
8820
|
generatingDebouncePending = null;
|
|
8125
8821
|
lastApprovalEventAt = 0;
|
|
8822
|
+
controlValues = {};
|
|
8823
|
+
appliedEffectKeys = /* @__PURE__ */ new Set();
|
|
8126
8824
|
historyWriter;
|
|
8127
8825
|
runtimeMessages = [];
|
|
8128
8826
|
instanceId;
|
|
@@ -8135,6 +8833,7 @@ var CliProviderInstance = class {
|
|
|
8135
8833
|
async init(context) {
|
|
8136
8834
|
this.context = context;
|
|
8137
8835
|
this.settings = context.settings || {};
|
|
8836
|
+
this.adapter.updateRuntimeSettings?.(this.settings);
|
|
8138
8837
|
this.monitor.updateConfig({
|
|
8139
8838
|
approvalAlert: this.settings.approvalAlert !== false,
|
|
8140
8839
|
longGeneratingAlert: this.settings.longGeneratingAlert !== false,
|
|
@@ -8150,6 +8849,21 @@ var CliProviderInstance = class {
|
|
|
8150
8849
|
this.detectStatusTransition();
|
|
8151
8850
|
});
|
|
8152
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
|
+
}
|
|
8153
8867
|
if (this.providerSessionId && this.launchMode === "resume") {
|
|
8154
8868
|
const resumedAt = Date.now();
|
|
8155
8869
|
this.historyWriter.appendSystemMarker(
|
|
@@ -8230,6 +8944,12 @@ var CliProviderInstance = class {
|
|
|
8230
8944
|
}
|
|
8231
8945
|
const runtime = this.adapter.getRuntimeMetadata();
|
|
8232
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
|
+
}
|
|
8233
8953
|
const mergedMessages = this.mergeConversationMessages(parsedMessages);
|
|
8234
8954
|
const dirName = this.workingDir.split("/").filter(Boolean).pop() || "session";
|
|
8235
8955
|
if (parsedMessages.length > 0) {
|
|
@@ -8250,6 +8970,7 @@ var CliProviderInstance = class {
|
|
|
8250
8970
|
);
|
|
8251
8971
|
}
|
|
8252
8972
|
}
|
|
8973
|
+
this.applyProviderResponse(parsedStatus, { phase: "immediate" });
|
|
8253
8974
|
return {
|
|
8254
8975
|
type: this.type,
|
|
8255
8976
|
name: this.provider.name,
|
|
@@ -8279,8 +9000,7 @@ var CliProviderInstance = class {
|
|
|
8279
9000
|
attachedClients: runtime.attachedClients || []
|
|
8280
9001
|
} : void 0,
|
|
8281
9002
|
resume: this.provider.resume,
|
|
8282
|
-
controlValues:
|
|
8283
|
-
// CLI controls not yet wired from stream
|
|
9003
|
+
controlValues: this.controlValues,
|
|
8284
9004
|
providerControls: this.provider.controls
|
|
8285
9005
|
};
|
|
8286
9006
|
}
|
|
@@ -8291,6 +9011,15 @@ var CliProviderInstance = class {
|
|
|
8291
9011
|
getPresentationMode() {
|
|
8292
9012
|
return this.presentationMode;
|
|
8293
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
|
+
}
|
|
8294
9023
|
onEvent(event, data) {
|
|
8295
9024
|
if (event === "send_message" && data?.text) {
|
|
8296
9025
|
void this.adapter.sendMessage(data.text).catch((e) => {
|
|
@@ -8302,22 +9031,27 @@ var CliProviderInstance = class {
|
|
|
8302
9031
|
void this.adapter.resolveAction(data).catch((e) => {
|
|
8303
9032
|
LOG.warn("CLI", `[${this.type}] resolve_action failed: ${e?.message || e}`);
|
|
8304
9033
|
});
|
|
9034
|
+
} else if (event === "provider_state_patch" && data && typeof data === "object") {
|
|
9035
|
+
this.applyProviderResponse(data, { phase: "immediate" });
|
|
8305
9036
|
}
|
|
8306
9037
|
}
|
|
8307
9038
|
dispose() {
|
|
8308
9039
|
this.adapter.shutdown();
|
|
8309
9040
|
this.monitor.reset();
|
|
9041
|
+
this.appliedEffectKeys.clear();
|
|
8310
9042
|
}
|
|
8311
9043
|
completedDebounceTimer = null;
|
|
8312
9044
|
completedDebouncePending = null;
|
|
8313
9045
|
detectStatusTransition() {
|
|
8314
9046
|
const now = Date.now();
|
|
8315
9047
|
const adapterStatus = this.adapter.getStatus();
|
|
9048
|
+
const parsedStatus = this.adapter.getScriptParsedStatus?.() || null;
|
|
8316
9049
|
const newStatus = adapterStatus.status;
|
|
8317
9050
|
const dirName = this.workingDir.split("/").filter(Boolean).pop() || "session";
|
|
8318
9051
|
const chatTitle = `${this.provider.name} \xB7 ${dirName}`;
|
|
8319
9052
|
const partial = this.adapter.getPartialResponse();
|
|
8320
9053
|
const progressFingerprint = newStatus === "generating" ? `${partial || ""}::${adapterStatus.messages.at(-1)?.content || ""}`.slice(-2e3) : void 0;
|
|
9054
|
+
const previousStatus = this.lastStatus;
|
|
8321
9055
|
if (newStatus !== this.lastStatus) {
|
|
8322
9056
|
LOG.info("CLI", `[${this.type}] status: ${this.lastStatus} \u2192 ${newStatus}`);
|
|
8323
9057
|
if (this.lastStatus === "idle" && newStatus === "generating") {
|
|
@@ -8410,6 +9144,9 @@ var CliProviderInstance = class {
|
|
|
8410
9144
|
}
|
|
8411
9145
|
this.lastStatus = newStatus;
|
|
8412
9146
|
}
|
|
9147
|
+
this.applyProviderResponse(parsedStatus, {
|
|
9148
|
+
phase: newStatus === "idle" && (previousStatus === "generating" || previousStatus === "waiting_approval") ? "turn_completed" : "immediate"
|
|
9149
|
+
});
|
|
8413
9150
|
const agentKey = `${this.type}:cli`;
|
|
8414
9151
|
const monitorEvents = this.monitor.check(agentKey, newStatus, now, progressFingerprint);
|
|
8415
9152
|
for (const me of monitorEvents) {
|
|
@@ -8425,6 +9162,88 @@ var CliProviderInstance = class {
|
|
|
8425
9162
|
this.events = [];
|
|
8426
9163
|
return events;
|
|
8427
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
|
+
}
|
|
8428
9247
|
// ─── Adapter access (backward compat) ──────────────────
|
|
8429
9248
|
getAdapter() {
|
|
8430
9249
|
return this.adapter;
|
|
@@ -11202,6 +12021,22 @@ function getMacAppIdentifiers() {
|
|
|
11202
12021
|
function getWinProcessNames() {
|
|
11203
12022
|
return getProviderLoader().getWinProcessNames();
|
|
11204
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
|
+
}
|
|
11205
12040
|
async function findFreePort(ports) {
|
|
11206
12041
|
for (const port2 of ports) {
|
|
11207
12042
|
const free = await checkPortFree(port2);
|
|
@@ -11254,12 +12089,12 @@ async function killIdeProcess(ideId) {
|
|
|
11254
12089
|
try {
|
|
11255
12090
|
if (plat === "darwin" && appName) {
|
|
11256
12091
|
try {
|
|
11257
|
-
execSync4(`osascript -e 'tell application "${appName}" to quit' 2>/dev/null`, {
|
|
12092
|
+
execSync4(`osascript -e 'tell application "${escapeForAppleScript(appName)}" to quit' 2>/dev/null`, {
|
|
11258
12093
|
timeout: 5e3
|
|
11259
12094
|
});
|
|
11260
12095
|
} catch {
|
|
11261
12096
|
try {
|
|
11262
|
-
execSync4(`pkill -
|
|
12097
|
+
execSync4(`pkill -x "${appName}" 2>/dev/null`, { timeout: 5e3 });
|
|
11263
12098
|
} catch {
|
|
11264
12099
|
}
|
|
11265
12100
|
}
|
|
@@ -11289,7 +12124,7 @@ async function killIdeProcess(ideId) {
|
|
|
11289
12124
|
}
|
|
11290
12125
|
if (plat === "darwin" && appName) {
|
|
11291
12126
|
try {
|
|
11292
|
-
execSync4(`pkill -9 -
|
|
12127
|
+
execSync4(`pkill -9 -x "${appName}" 2>/dev/null`, { timeout: 5e3 });
|
|
11293
12128
|
} catch {
|
|
11294
12129
|
}
|
|
11295
12130
|
} else if (plat === "win32" && winProcesses) {
|
|
@@ -11312,8 +12147,23 @@ function isIdeRunning(ideId) {
|
|
|
11312
12147
|
if (plat === "darwin") {
|
|
11313
12148
|
const appName = getMacAppIdentifiers()[ideId];
|
|
11314
12149
|
if (!appName) return false;
|
|
11315
|
-
|
|
11316
|
-
|
|
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
|
+
}
|
|
11317
12167
|
} else if (plat === "win32") {
|
|
11318
12168
|
const winProcesses = getWinProcessNames()[ideId];
|
|
11319
12169
|
if (!winProcesses) return false;
|
|
@@ -11462,7 +12312,8 @@ async function launchWithCdp(options = {}) {
|
|
|
11462
12312
|
await launchLinux(targetIde, port, workspace, options.newWindow);
|
|
11463
12313
|
}
|
|
11464
12314
|
let cdpReady = false;
|
|
11465
|
-
|
|
12315
|
+
const waitDeadline = Date.now() + getCdpStartupTimeoutMs(targetIde.id);
|
|
12316
|
+
while (Date.now() < waitDeadline) {
|
|
11466
12317
|
await new Promise((r) => setTimeout(r, 500));
|
|
11467
12318
|
if (await isCdpActive(port)) {
|
|
11468
12319
|
cdpReady = true;
|
|
@@ -11491,14 +12342,18 @@ async function launchWithCdp(options = {}) {
|
|
|
11491
12342
|
}
|
|
11492
12343
|
async function launchMacOS(ide, port, workspace, newWindow) {
|
|
11493
12344
|
const appName = getMacAppIdentifiers()[ide.id];
|
|
12345
|
+
const preferredMethod = getPreferredLaunchMethod(ide.id, "darwin");
|
|
11494
12346
|
const args = ["--remote-debugging-port=" + port];
|
|
11495
12347
|
if (newWindow) args.push("--new-window");
|
|
11496
12348
|
if (workspace) args.push(workspace);
|
|
11497
|
-
|
|
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) {
|
|
11498
12355
|
const openArgs = ["-a", appName, "--args", ...args];
|
|
11499
12356
|
spawn2("open", openArgs, { detached: true, stdio: "ignore" }).unref();
|
|
11500
|
-
} else if (ide.cliCommand) {
|
|
11501
|
-
spawn2(ide.cliCommand, args, { detached: true, stdio: "ignore" }).unref();
|
|
11502
12357
|
} else {
|
|
11503
12358
|
throw new Error(`No app identifier or CLI for ${ide.displayName}`);
|
|
11504
12359
|
}
|
|
@@ -11809,6 +12664,7 @@ function buildStatusSnapshot(options) {
|
|
|
11809
12664
|
workspaces: wsState.workspaces,
|
|
11810
12665
|
defaultWorkspaceId: wsState.defaultWorkspaceId,
|
|
11811
12666
|
defaultWorkspacePath: wsState.defaultWorkspacePath,
|
|
12667
|
+
terminalSizingMode: cfg.terminalSizingMode || "measured",
|
|
11812
12668
|
recentLaunches: buildRecentLaunches(recentActivity),
|
|
11813
12669
|
terminalBackend,
|
|
11814
12670
|
availableProviders: buildAvailableProviders(options.providerLoader)
|
|
@@ -12656,19 +13512,10 @@ var ProviderStreamAdapter = class {
|
|
|
12656
13512
|
mode: data.mode,
|
|
12657
13513
|
activeModal: data.activeModal
|
|
12658
13514
|
};
|
|
12659
|
-
|
|
12660
|
-
|
|
12661
|
-
|
|
12662
|
-
|
|
12663
|
-
const val = data[ctrl.readFrom];
|
|
12664
|
-
if (val !== void 0 && val !== null) {
|
|
12665
|
-
cv[ctrl.id] = typeof val === "object" ? val.name || val.id || String(val) : val;
|
|
12666
|
-
}
|
|
12667
|
-
}
|
|
12668
|
-
if (data.model && !cv["model"]) cv["model"] = data.model;
|
|
12669
|
-
if (data.mode && !cv["mode"]) cv["mode"] = data.mode;
|
|
12670
|
-
if (Object.keys(cv).length > 0) state.controlValues = cv;
|
|
12671
|
-
}
|
|
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;
|
|
12672
13519
|
if (state.messages.length > 0) {
|
|
12673
13520
|
this.lastSuccessState = state;
|
|
12674
13521
|
}
|
|
@@ -13194,6 +14041,8 @@ function forwardAgentStreamsToIdeInstance(instanceManager, ideType, streams) {
|
|
|
13194
14041
|
activeModal: stream.activeModal || null,
|
|
13195
14042
|
model: stream.model || void 0,
|
|
13196
14043
|
mode: stream.mode || void 0,
|
|
14044
|
+
controlValues: stream.controlValues || void 0,
|
|
14045
|
+
effects: stream.effects || void 0,
|
|
13197
14046
|
sessionId: stream.sessionId || stream.instanceId || void 0,
|
|
13198
14047
|
title: stream.title || stream.agentName || void 0,
|
|
13199
14048
|
agentType: stream.agentType || void 0,
|