@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.js
CHANGED
|
@@ -80,7 +80,8 @@ function normalizeConfig(raw) {
|
|
|
80
80
|
providerSettings: isPlainObject(parsed.providerSettings) ? parsed.providerSettings : {},
|
|
81
81
|
ideSettings: isPlainObject(parsed.ideSettings) ? parsed.ideSettings : {},
|
|
82
82
|
disableUpstream: asBoolean(parsed.disableUpstream, DEFAULT_CONFIG.disableUpstream ?? false),
|
|
83
|
-
providerDir: asOptionalString(parsed.providerDir)
|
|
83
|
+
providerDir: asOptionalString(parsed.providerDir),
|
|
84
|
+
terminalSizingMode: parsed.terminalSizingMode === "fit" ? "fit" : "measured"
|
|
84
85
|
};
|
|
85
86
|
}
|
|
86
87
|
function generateMachineId() {
|
|
@@ -228,7 +229,8 @@ var init_config = __esm({
|
|
|
228
229
|
registeredMachineId: void 0,
|
|
229
230
|
providerSettings: {},
|
|
230
231
|
ideSettings: {},
|
|
231
|
-
disableUpstream: false
|
|
232
|
+
disableUpstream: false,
|
|
233
|
+
terminalSizingMode: "measured"
|
|
232
234
|
};
|
|
233
235
|
MACHINE_ID_PREFIX = "mach_";
|
|
234
236
|
}
|
|
@@ -810,6 +812,53 @@ function stripTerminalNoise(str) {
|
|
|
810
812
|
function sanitizeTerminalText(str) {
|
|
811
813
|
return stripTerminalNoise(stripAnsi(str));
|
|
812
814
|
}
|
|
815
|
+
function splitCliScreenLines(text) {
|
|
816
|
+
return String(text || "").replace(/\u0007/g, "").replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n").map((line) => line.replace(/\s+$/, ""));
|
|
817
|
+
}
|
|
818
|
+
function isPromptLikeCliLine(line) {
|
|
819
|
+
const trimmed = String(line || "").trim();
|
|
820
|
+
if (!trimmed) return false;
|
|
821
|
+
return /^[❯›>]\s*(?:$|\S.*)$/.test(trimmed);
|
|
822
|
+
}
|
|
823
|
+
function buildCliScreenSnapshot(text) {
|
|
824
|
+
const normalizedText = String(text || "").replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
825
|
+
const rawLines = splitCliScreenLines(normalizedText);
|
|
826
|
+
const lines = rawLines.map((line, index, arr) => {
|
|
827
|
+
const trimmed = String(line || "").trim();
|
|
828
|
+
return {
|
|
829
|
+
index,
|
|
830
|
+
fromTop: index,
|
|
831
|
+
fromBottom: arr.length - index - 1,
|
|
832
|
+
text: line,
|
|
833
|
+
trimmed,
|
|
834
|
+
isEmpty: trimmed.length === 0
|
|
835
|
+
};
|
|
836
|
+
});
|
|
837
|
+
const nonEmptyLines = lines.filter((line) => !line.isEmpty);
|
|
838
|
+
const firstNonEmptyLine = nonEmptyLines[0] ?? null;
|
|
839
|
+
const lastNonEmptyLine = nonEmptyLines[nonEmptyLines.length - 1] ?? null;
|
|
840
|
+
let promptLineIndex = -1;
|
|
841
|
+
for (let i = lines.length - 1; i >= 0; i -= 1) {
|
|
842
|
+
if (isPromptLikeCliLine(lines[i].text)) {
|
|
843
|
+
promptLineIndex = i;
|
|
844
|
+
break;
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
return {
|
|
848
|
+
text: normalizedText,
|
|
849
|
+
lineCount: lines.length,
|
|
850
|
+
lines,
|
|
851
|
+
nonEmptyLines,
|
|
852
|
+
firstNonEmptyLineIndex: firstNonEmptyLine?.index ?? -1,
|
|
853
|
+
lastNonEmptyLineIndex: lastNonEmptyLine?.index ?? -1,
|
|
854
|
+
firstNonEmptyLine,
|
|
855
|
+
lastNonEmptyLine,
|
|
856
|
+
promptLineIndex,
|
|
857
|
+
promptLine: promptLineIndex >= 0 ? lines[promptLineIndex] : null,
|
|
858
|
+
linesAbovePrompt: promptLineIndex >= 0 ? lines.slice(0, promptLineIndex) : [...lines],
|
|
859
|
+
linesBelowPrompt: promptLineIndex >= 0 ? lines.slice(promptLineIndex + 1) : []
|
|
860
|
+
};
|
|
861
|
+
}
|
|
813
862
|
function computeTerminalQueryTail(buffer) {
|
|
814
863
|
const prefixes = ["\x1B[6n", "\x1B[?6n"];
|
|
815
864
|
const maxLength = prefixes.reduce((n, value) => Math.max(n, value.length), 0) - 1;
|
|
@@ -1051,7 +1100,9 @@ var init_provider_cli_adapter = __esm({
|
|
|
1051
1100
|
ready = false;
|
|
1052
1101
|
startupBuffer = "";
|
|
1053
1102
|
startupParseGate = false;
|
|
1103
|
+
startupSettleTimer = null;
|
|
1054
1104
|
spawnAt = 0;
|
|
1105
|
+
startupFirstOutputAt = 0;
|
|
1055
1106
|
// PTY I/O
|
|
1056
1107
|
onPtyDataCallback = null;
|
|
1057
1108
|
pendingOutputParseBuffer = "";
|
|
@@ -1092,6 +1143,7 @@ var init_provider_cli_adapter = __esm({
|
|
|
1092
1143
|
statusHistory = [];
|
|
1093
1144
|
// ─── CLI Scripts (script-based parsing) ───
|
|
1094
1145
|
cliScripts;
|
|
1146
|
+
runtimeSettings = {};
|
|
1095
1147
|
/** Full accumulated ANSI-stripped PTY output */
|
|
1096
1148
|
accumulatedBuffer = "";
|
|
1097
1149
|
/** Full accumulated raw PTY output (with ANSI) */
|
|
@@ -1106,14 +1158,15 @@ var init_provider_cli_adapter = __esm({
|
|
|
1106
1158
|
traceSessionId = `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
1107
1159
|
static MAX_TRACE_ENTRIES = 250;
|
|
1108
1160
|
providerResolutionMeta;
|
|
1109
|
-
static IDLE_FINISH_CONFIRM_MS =
|
|
1161
|
+
static IDLE_FINISH_CONFIRM_MS = 2e3;
|
|
1162
|
+
static STATUS_ACTIVITY_HOLD_MS = 2e3;
|
|
1110
1163
|
static FINISH_RETRY_DELAY_MS = 300;
|
|
1111
1164
|
static MAX_FINISH_RETRIES = 2;
|
|
1112
1165
|
syncMessageViews() {
|
|
1113
1166
|
this.messages = [...this.committedMessages];
|
|
1114
1167
|
this.structuredMessages = [...this.committedMessages];
|
|
1115
1168
|
}
|
|
1116
|
-
|
|
1169
|
+
hydrateParsedMessages(parsedMessages, scope) {
|
|
1117
1170
|
const referenceMessages = [...this.committedMessages];
|
|
1118
1171
|
const usedReferenceIndexes = /* @__PURE__ */ new Set();
|
|
1119
1172
|
const now = Date.now();
|
|
@@ -1146,13 +1199,30 @@ var init_provider_cli_adapter = __esm({
|
|
|
1146
1199
|
const content = typeof message.content === "string" ? message.content : String(message.content || "");
|
|
1147
1200
|
const parsedTimestamp = typeof message.timestamp === "number" && Number.isFinite(message.timestamp) ? message.timestamp : void 0;
|
|
1148
1201
|
const referenceTimestamp = parsedTimestamp ?? findReferenceTimestamp(role, content, index);
|
|
1202
|
+
const fallbackTimestamp = role === "user" ? scope?.startedAt || now : this.lastOutputAt || scope?.startedAt || now;
|
|
1203
|
+
const timestamp = referenceTimestamp ?? fallbackTimestamp;
|
|
1149
1204
|
return {
|
|
1205
|
+
...message,
|
|
1150
1206
|
role,
|
|
1151
1207
|
content,
|
|
1152
|
-
timestamp
|
|
1208
|
+
timestamp,
|
|
1209
|
+
receivedAt: typeof message.receivedAt === "number" && Number.isFinite(message.receivedAt) ? message.receivedAt : timestamp
|
|
1153
1210
|
};
|
|
1154
1211
|
});
|
|
1155
1212
|
}
|
|
1213
|
+
normalizeParsedMessages(parsedMessages, scope) {
|
|
1214
|
+
return this.hydrateParsedMessages(parsedMessages, scope).map((message) => ({
|
|
1215
|
+
role: message.role,
|
|
1216
|
+
content: message.content,
|
|
1217
|
+
timestamp: message.timestamp,
|
|
1218
|
+
receivedAt: message.receivedAt,
|
|
1219
|
+
kind: message.kind,
|
|
1220
|
+
id: message.id,
|
|
1221
|
+
index: message.index,
|
|
1222
|
+
meta: message.meta,
|
|
1223
|
+
senderName: message.senderName
|
|
1224
|
+
}));
|
|
1225
|
+
}
|
|
1156
1226
|
sliceFromOffset(text, start) {
|
|
1157
1227
|
if (!text) return "";
|
|
1158
1228
|
if (!Number.isFinite(start) || start <= 0) return text;
|
|
@@ -1162,14 +1232,20 @@ var init_provider_cli_adapter = __esm({
|
|
|
1162
1232
|
buildParseInput(baseMessages, partialResponse, scope) {
|
|
1163
1233
|
const buffer = scope ? this.sliceFromOffset(this.accumulatedBuffer, scope.bufferStart) || this.accumulatedBuffer : this.accumulatedBuffer;
|
|
1164
1234
|
const rawBuffer = scope ? this.sliceFromOffset(this.accumulatedRawBuffer, scope.rawBufferStart) || this.accumulatedRawBuffer : this.accumulatedRawBuffer;
|
|
1235
|
+
const screenText = this.terminalScreen.getText();
|
|
1236
|
+
const recentBuffer = buffer.slice(-1e3) || this.recentOutputBuffer;
|
|
1165
1237
|
return {
|
|
1166
1238
|
buffer,
|
|
1167
1239
|
rawBuffer,
|
|
1168
|
-
recentBuffer
|
|
1169
|
-
screenText
|
|
1240
|
+
recentBuffer,
|
|
1241
|
+
screenText,
|
|
1242
|
+
screen: buildCliScreenSnapshot(screenText),
|
|
1243
|
+
bufferScreen: buildCliScreenSnapshot(buffer),
|
|
1244
|
+
recentScreen: buildCliScreenSnapshot(recentBuffer),
|
|
1170
1245
|
messages: [...baseMessages],
|
|
1171
1246
|
partialResponse,
|
|
1172
|
-
promptText: scope?.prompt || ""
|
|
1247
|
+
promptText: scope?.prompt || "",
|
|
1248
|
+
settings: { ...this.runtimeSettings }
|
|
1173
1249
|
};
|
|
1174
1250
|
}
|
|
1175
1251
|
setStatus(status, trigger) {
|
|
@@ -1275,6 +1351,9 @@ var init_provider_cli_adapter = __esm({
|
|
|
1275
1351
|
const scriptNames = Object.keys(scripts).filter((k) => typeof scripts[k] === "function");
|
|
1276
1352
|
LOG.info("CLI", `[${this.cliType}] CLI scripts injected: [${scriptNames.join(", ")}]`);
|
|
1277
1353
|
}
|
|
1354
|
+
updateRuntimeSettings(settings) {
|
|
1355
|
+
this.runtimeSettings = { ...settings };
|
|
1356
|
+
}
|
|
1278
1357
|
// ─── Lifecycle ─────────────────────────────────
|
|
1279
1358
|
setServerConn(serverConn) {
|
|
1280
1359
|
this.serverConn = serverConn;
|
|
@@ -1311,7 +1390,7 @@ var init_provider_cli_adapter = __esm({
|
|
|
1311
1390
|
let shellArgs;
|
|
1312
1391
|
const useShellUnix = !isWin && (!!spawnConfig.shell || !path7.isAbsolute(binaryPath) || isScriptBinary(binaryPath) || !looksLikeMachOOrElf(binaryPath));
|
|
1313
1392
|
const isCmdShim = isWin && /\.(cmd|bat)$/i.test(binaryPath);
|
|
1314
|
-
const useShellWin = isCmdShim || !path7.isAbsolute(binaryPath) || isScriptBinary(binaryPath);
|
|
1393
|
+
const useShellWin = !!spawnConfig.shell || isCmdShim || !path7.isAbsolute(binaryPath) || isScriptBinary(binaryPath);
|
|
1315
1394
|
const useShell = isWin ? useShellWin : useShellUnix;
|
|
1316
1395
|
if (useShell) {
|
|
1317
1396
|
if (!spawnConfig.shell && !isWin) {
|
|
@@ -1409,6 +1488,11 @@ var init_provider_cli_adapter = __esm({
|
|
|
1409
1488
|
this.spawnAt = Date.now();
|
|
1410
1489
|
this.startupParseGate = true;
|
|
1411
1490
|
this.startupBuffer = "";
|
|
1491
|
+
this.startupFirstOutputAt = 0;
|
|
1492
|
+
if (this.startupSettleTimer) {
|
|
1493
|
+
clearTimeout(this.startupSettleTimer);
|
|
1494
|
+
this.startupSettleTimer = null;
|
|
1495
|
+
}
|
|
1412
1496
|
this.terminalScreen.reset(24, 80);
|
|
1413
1497
|
this.pendingTerminalQueryTail = "";
|
|
1414
1498
|
this.currentTurnScope = null;
|
|
@@ -1422,7 +1506,8 @@ var init_provider_cli_adapter = __esm({
|
|
|
1422
1506
|
this.recordTrace("ready", {
|
|
1423
1507
|
runtimeMeta: this.getRuntimeMetadata()
|
|
1424
1508
|
});
|
|
1425
|
-
this.setStatus("
|
|
1509
|
+
this.setStatus("starting", "pty_ready");
|
|
1510
|
+
this.scheduleStartupSettleCheck();
|
|
1426
1511
|
this.onStatusChange?.();
|
|
1427
1512
|
}
|
|
1428
1513
|
// ─── Output Handling ────────────────────────────
|
|
@@ -1437,6 +1522,9 @@ var init_provider_cli_adapter = __esm({
|
|
|
1437
1522
|
this.lastScreenSnapshot = normalizedScreenSnapshot;
|
|
1438
1523
|
this.lastScreenChangeAt = now;
|
|
1439
1524
|
}
|
|
1525
|
+
if (this.startupParseGate && !this.startupFirstOutputAt && (cleanData.trim() || normalizedScreenSnapshot.trim())) {
|
|
1526
|
+
this.startupFirstOutputAt = now;
|
|
1527
|
+
}
|
|
1440
1528
|
if (this.idleFinishCandidate && (rawData.length > 0 || cleanData.length > 0)) {
|
|
1441
1529
|
this.clearIdleFinishCandidate("new_output");
|
|
1442
1530
|
}
|
|
@@ -1447,6 +1535,9 @@ var init_provider_cli_adapter = __esm({
|
|
|
1447
1535
|
cleanPreview: this.summarizeTraceText(cleanData, 300),
|
|
1448
1536
|
screenText: this.summarizeTraceText(this.terminalScreen.getText(), 1200)
|
|
1449
1537
|
});
|
|
1538
|
+
if (this.startupParseGate) {
|
|
1539
|
+
this.scheduleStartupSettleCheck();
|
|
1540
|
+
}
|
|
1450
1541
|
if (this.isWaitingForResponse && cleanData) {
|
|
1451
1542
|
this.responseBuffer = (this.responseBuffer + cleanData).slice(-8e3);
|
|
1452
1543
|
}
|
|
@@ -1460,27 +1551,51 @@ var init_provider_cli_adapter = __esm({
|
|
|
1460
1551
|
this.recentOutputBuffer = (this.recentOutputBuffer + cleanData).slice(-1e3);
|
|
1461
1552
|
this.accumulatedBuffer = (this.accumulatedBuffer + cleanData).slice(-_ProviderCliAdapter.MAX_ACCUMULATED_BUFFER);
|
|
1462
1553
|
this.accumulatedRawBuffer = (this.accumulatedRawBuffer + rawData).slice(-_ProviderCliAdapter.MAX_ACCUMULATED_BUFFER);
|
|
1463
|
-
|
|
1464
|
-
this.startupBuffer += cleanData;
|
|
1465
|
-
const elapsed = Date.now() - this.spawnAt;
|
|
1466
|
-
const screenText = this.terminalScreen.getText() || "";
|
|
1467
|
-
const startupModal = this.getStartupConfirmationModal(screenText);
|
|
1468
|
-
const scriptStatus = startupModal ? "waiting_approval" : this.runDetectStatus(this.startupBuffer);
|
|
1469
|
-
const hasInteractivePrompt = this.looksLikeVisibleIdlePrompt(screenText);
|
|
1470
|
-
const startupStableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : 0;
|
|
1471
|
-
const isReady = (scriptStatus === "idle" || scriptStatus === "waiting_approval") && hasInteractivePrompt && startupStableMs >= 700 || !!startupModal && startupStableMs >= 700 || elapsed > 8e3 || this.startupBuffer.length > 12e3;
|
|
1472
|
-
if (isReady) {
|
|
1473
|
-
this.startupParseGate = false;
|
|
1474
|
-
this.ready = true;
|
|
1475
|
-
LOG.info(
|
|
1476
|
-
"CLI",
|
|
1477
|
-
`[${this.cliType}] Startup ready (${elapsed}ms, scriptStatus=${scriptStatus}, prompt=${hasInteractivePrompt}, stableMs=${startupStableMs}) providerDir=${this.providerResolutionMeta.providerDir || "-"} scriptDir=${this.providerResolutionMeta.scriptDir || "-"} scriptsPath=${this.providerResolutionMeta.scriptsPath || "-"}`
|
|
1478
|
-
);
|
|
1479
|
-
this.onStatusChange?.();
|
|
1480
|
-
}
|
|
1481
|
-
}
|
|
1554
|
+
this.resolveStartupState("output");
|
|
1482
1555
|
this.scheduleSettle();
|
|
1483
1556
|
}
|
|
1557
|
+
resolveStartupState(trigger) {
|
|
1558
|
+
if (!this.startupParseGate) return;
|
|
1559
|
+
const now = Date.now();
|
|
1560
|
+
const screenText = this.terminalScreen.getText() || "";
|
|
1561
|
+
const normalizedScreen = normalizeScreenSnapshot(screenText);
|
|
1562
|
+
const hasStartupOutput = !!this.startupFirstOutputAt || !!normalizedScreen.trim();
|
|
1563
|
+
if (!hasStartupOutput) return;
|
|
1564
|
+
const stableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : 0;
|
|
1565
|
+
if (stableMs < 2e3) return;
|
|
1566
|
+
const startupModal = this.getStartupConfirmationModal(screenText);
|
|
1567
|
+
this.startupParseGate = false;
|
|
1568
|
+
if (this.startupSettleTimer) {
|
|
1569
|
+
clearTimeout(this.startupSettleTimer);
|
|
1570
|
+
this.startupSettleTimer = null;
|
|
1571
|
+
}
|
|
1572
|
+
this.ready = true;
|
|
1573
|
+
if (startupModal) {
|
|
1574
|
+
this.activeModal = startupModal;
|
|
1575
|
+
this.setStatus("waiting_approval", `startup_ready:${trigger}`);
|
|
1576
|
+
} else {
|
|
1577
|
+
this.setStatus("idle", `startup_ready:${trigger}`);
|
|
1578
|
+
}
|
|
1579
|
+
LOG.info(
|
|
1580
|
+
"CLI",
|
|
1581
|
+
`[${this.cliType}] Startup settled (${trigger}, stableMs=${stableMs}, modal=${!!startupModal}) providerDir=${this.providerResolutionMeta.providerDir || "-"} scriptDir=${this.providerResolutionMeta.scriptDir || "-"} scriptsPath=${this.providerResolutionMeta.scriptsPath || "-"}`
|
|
1582
|
+
);
|
|
1583
|
+
this.onStatusChange?.();
|
|
1584
|
+
}
|
|
1585
|
+
scheduleStartupSettleCheck() {
|
|
1586
|
+
if (!this.startupParseGate) return;
|
|
1587
|
+
if (this.startupSettleTimer) clearTimeout(this.startupSettleTimer);
|
|
1588
|
+
const now = Date.now();
|
|
1589
|
+
const stableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : 0;
|
|
1590
|
+
const delayMs = Math.max(250, 2050 - stableMs);
|
|
1591
|
+
this.startupSettleTimer = setTimeout(() => {
|
|
1592
|
+
this.startupSettleTimer = null;
|
|
1593
|
+
this.resolveStartupState("startup_timer");
|
|
1594
|
+
if (this.startupParseGate && Date.now() - this.spawnAt < 1e4) {
|
|
1595
|
+
this.scheduleStartupSettleCheck();
|
|
1596
|
+
}
|
|
1597
|
+
}, delayMs);
|
|
1598
|
+
}
|
|
1484
1599
|
scheduleSettle() {
|
|
1485
1600
|
if (this.settleTimer) clearTimeout(this.settleTimer);
|
|
1486
1601
|
const settleEpoch = this.responseEpoch;
|
|
@@ -1522,6 +1637,43 @@ var init_provider_cli_adapter = __esm({
|
|
|
1522
1637
|
if (!text.trim()) return false;
|
|
1523
1638
|
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);
|
|
1524
1639
|
}
|
|
1640
|
+
findLastMatchingLineIndex(lines, predicate) {
|
|
1641
|
+
for (let index = lines.length - 1; index >= 0; index -= 1) {
|
|
1642
|
+
if (predicate(lines[index])) return index;
|
|
1643
|
+
}
|
|
1644
|
+
return -1;
|
|
1645
|
+
}
|
|
1646
|
+
looksLikeClaudeGeneratingLine(line) {
|
|
1647
|
+
const trimmed = String(line || "").trim();
|
|
1648
|
+
if (!trimmed) return false;
|
|
1649
|
+
if (/esc to (cancel|interrupt|stop)/i.test(trimmed)) return true;
|
|
1650
|
+
if (/^[✻✶✳✢✽⠂⠐⠒⠓⠦⠴⠶⠷⠿]+\s+\S+.*\b(?:thinking|thought for \d+s?)\b/i.test(trimmed)) return true;
|
|
1651
|
+
if (/^[✻✶✳✢✽⠂⠐⠒⠓⠦⠴⠶⠷⠿]+\s+[A-Z][A-Za-z-]{3,}ing\b.*(?:…|\.{3})/u.test(trimmed)) return true;
|
|
1652
|
+
if (/^[⏺•]\s+(?:Reading|Writing|Editing|Searching|Inspecting|Planning|Analyzing|Synthesizing|Drafting|Running|Listing|Scanning|Matching)\b.*(?:…|\.{3})/i.test(trimmed)) {
|
|
1653
|
+
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);
|
|
1654
|
+
}
|
|
1655
|
+
return false;
|
|
1656
|
+
}
|
|
1657
|
+
detectClaudeGeneratingOverride(screenText, tail) {
|
|
1658
|
+
if (this.cliType !== "claude-cli") return false;
|
|
1659
|
+
const source = sanitizeTerminalText(screenText || tail || "");
|
|
1660
|
+
if (!source.trim()) return false;
|
|
1661
|
+
const allLines = source.split(/\r\n|\n|\r/g).map((line) => line.trim()).filter(Boolean);
|
|
1662
|
+
if (allLines.length === 0) return false;
|
|
1663
|
+
const recentLines = allLines.slice(-12);
|
|
1664
|
+
const promptIndex = this.findLastMatchingLineIndex(recentLines, (line) => /^[❯›>]\s*$/.test(line));
|
|
1665
|
+
const activeRegion = promptIndex >= 0 ? recentLines.slice(Math.max(0, promptIndex - 2), promptIndex) : recentLines;
|
|
1666
|
+
if (activeRegion.length === 0) return false;
|
|
1667
|
+
return activeRegion.some((line) => this.looksLikeClaudeGeneratingLine(line));
|
|
1668
|
+
}
|
|
1669
|
+
refineDetectedStatus(status, tail, screenText) {
|
|
1670
|
+
if (this.startupParseGate) {
|
|
1671
|
+
return this.getStartupConfirmationModal(screenText || "") ? "waiting_approval" : "starting";
|
|
1672
|
+
}
|
|
1673
|
+
if (status === "waiting_approval") return status;
|
|
1674
|
+
if (this.detectClaudeGeneratingOverride(screenText || "", tail)) return "generating";
|
|
1675
|
+
return status;
|
|
1676
|
+
}
|
|
1525
1677
|
looksLikeVisibleAssistantCandidate(screenText) {
|
|
1526
1678
|
const lines = sanitizeTerminalText(String(screenText || "")).split(/\r\n|\n|\r/g);
|
|
1527
1679
|
for (const line of lines) {
|
|
@@ -1556,6 +1708,11 @@ var init_provider_cli_adapter = __esm({
|
|
|
1556
1708
|
const screenStableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : 0;
|
|
1557
1709
|
return quietForMs < 1200 || screenStableMs < 1200 || !commitResult.hasAssistant;
|
|
1558
1710
|
}
|
|
1711
|
+
hasRecentInteractiveActivity(now) {
|
|
1712
|
+
const quietForMs = this.lastNonEmptyOutputAt ? now - this.lastNonEmptyOutputAt : Number.MAX_SAFE_INTEGER;
|
|
1713
|
+
const screenStableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : Number.MAX_SAFE_INTEGER;
|
|
1714
|
+
return quietForMs < _ProviderCliAdapter.STATUS_ACTIVITY_HOLD_MS || screenStableMs < _ProviderCliAdapter.STATUS_ACTIVITY_HOLD_MS;
|
|
1715
|
+
}
|
|
1559
1716
|
getStartupConfirmationModal(screenText) {
|
|
1560
1717
|
const text = sanitizeTerminalText(String(screenText || ""));
|
|
1561
1718
|
if (!text.trim()) return null;
|
|
@@ -1583,13 +1740,14 @@ var init_provider_cli_adapter = __esm({
|
|
|
1583
1740
|
const startedAt = Date.now();
|
|
1584
1741
|
let loggedWait = false;
|
|
1585
1742
|
while (Date.now() - startedAt < maxWaitMs) {
|
|
1743
|
+
this.resolveStartupState("interactive_wait");
|
|
1586
1744
|
const screenText = this.terminalScreen.getText() || "";
|
|
1587
1745
|
const hasPrompt = this.looksLikeVisibleIdlePrompt(screenText);
|
|
1588
1746
|
const stableMs = this.lastScreenChangeAt ? Date.now() - this.lastScreenChangeAt : 0;
|
|
1589
1747
|
const recentlyOutput = this.lastNonEmptyOutputAt ? Date.now() - this.lastNonEmptyOutputAt : Number.MAX_SAFE_INTEGER;
|
|
1590
1748
|
const status = this.runDetectStatus(this.recentOutputBuffer) || this.currentStatus;
|
|
1591
1749
|
const startupLikelyActive = /Welcome back|Tips for getting|Recent activity|Claude Code v\d/i.test(screenText);
|
|
1592
|
-
const interactiveReady = hasPrompt && stableMs >= 700 && recentlyOutput >= 350 && status !== "
|
|
1750
|
+
const interactiveReady = hasPrompt && stableMs >= 700 && recentlyOutput >= 350 && status !== "generating";
|
|
1593
1751
|
if (interactiveReady) {
|
|
1594
1752
|
if (loggedWait) {
|
|
1595
1753
|
LOG.info(
|
|
@@ -1628,6 +1786,10 @@ var init_provider_cli_adapter = __esm({
|
|
|
1628
1786
|
}
|
|
1629
1787
|
const tail = this.settledBuffer;
|
|
1630
1788
|
const screenText = this.terminalScreen.getText() || "";
|
|
1789
|
+
this.resolveStartupState("settled");
|
|
1790
|
+
if (this.startupParseGate) {
|
|
1791
|
+
return;
|
|
1792
|
+
}
|
|
1631
1793
|
const startupModal = this.getStartupConfirmationModal(screenText);
|
|
1632
1794
|
const modal = this.runParseApproval(tail) || startupModal;
|
|
1633
1795
|
const rawScriptStatus = this.runDetectStatus(tail);
|
|
@@ -1690,6 +1852,28 @@ var init_provider_cli_adapter = __esm({
|
|
|
1690
1852
|
} else {
|
|
1691
1853
|
clearPendingScriptStatus();
|
|
1692
1854
|
}
|
|
1855
|
+
const recentInteractiveActivity = this.hasRecentInteractiveActivity(now);
|
|
1856
|
+
const shouldHoldGenerating = scriptStatus === "idle" && this.isWaitingForResponse && !modal && recentInteractiveActivity;
|
|
1857
|
+
if (shouldHoldGenerating) {
|
|
1858
|
+
this.clearIdleFinishCandidate("hold_generating_recent_activity");
|
|
1859
|
+
this.setStatus("generating", "recent_activity_hold");
|
|
1860
|
+
if (this.idleTimeout) clearTimeout(this.idleTimeout);
|
|
1861
|
+
this.idleTimeout = setTimeout(() => {
|
|
1862
|
+
if (this.isWaitingForResponse && this.currentStatus !== "waiting_approval") {
|
|
1863
|
+
this.finishResponse();
|
|
1864
|
+
}
|
|
1865
|
+
}, this.timeouts.generatingIdle);
|
|
1866
|
+
this.recordTrace("hold_generating_recent_activity", {
|
|
1867
|
+
scriptStatus,
|
|
1868
|
+
recentInteractiveActivity,
|
|
1869
|
+
lastNonEmptyOutputAt: this.lastNonEmptyOutputAt,
|
|
1870
|
+
lastScreenChangeAt: this.lastScreenChangeAt,
|
|
1871
|
+
holdMs: _ProviderCliAdapter.STATUS_ACTIVITY_HOLD_MS,
|
|
1872
|
+
...this.buildTraceParseSnapshot(this.currentTurnScope, this.responseBuffer)
|
|
1873
|
+
});
|
|
1874
|
+
this.onStatusChange?.();
|
|
1875
|
+
return;
|
|
1876
|
+
}
|
|
1693
1877
|
if (scriptStatus === "waiting_approval") {
|
|
1694
1878
|
this.clearIdleFinishCandidate("waiting_approval");
|
|
1695
1879
|
const inCooldown = this.lastApprovalResolvedAt && Date.now() - this.lastApprovalResolvedAt < this.timeouts.approvalCooldown;
|
|
@@ -1767,8 +1951,8 @@ var init_provider_cli_adapter = __esm({
|
|
|
1767
1951
|
const screenStableMs = this.lastScreenChangeAt ? now - this.lastScreenChangeAt : 0;
|
|
1768
1952
|
const hasAssistantTurn = !!lastParsedAssistant;
|
|
1769
1953
|
const assistantLength = lastParsedAssistant?.content?.length || 0;
|
|
1770
|
-
const idleQuietThresholdMs = Math.max(
|
|
1771
|
-
const idleStableThresholdMs =
|
|
1954
|
+
const idleQuietThresholdMs = Math.max(2e3, this.timeouts.outputSettle);
|
|
1955
|
+
const idleStableThresholdMs = 2e3;
|
|
1772
1956
|
const idleReady = visibleIdlePrompt && !modal && hasAssistantTurn && quietForMs >= idleQuietThresholdMs && screenStableMs >= idleStableThresholdMs;
|
|
1773
1957
|
const candidate = this.idleFinishCandidate;
|
|
1774
1958
|
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;
|
|
@@ -1882,7 +2066,7 @@ var init_provider_cli_adapter = __esm({
|
|
|
1882
2066
|
this.currentTurnScope
|
|
1883
2067
|
);
|
|
1884
2068
|
if (parsed && Array.isArray(parsed.messages)) {
|
|
1885
|
-
this.committedMessages = this.normalizeParsedMessages(parsed.messages);
|
|
2069
|
+
this.committedMessages = this.normalizeParsedMessages(parsed.messages, this.currentTurnScope);
|
|
1886
2070
|
const promptForTrim = this.currentTurnScope?.prompt || getLastUserPromptText(this.committedMessages);
|
|
1887
2071
|
if (promptForTrim) {
|
|
1888
2072
|
const lastAssistantForTrim = [...this.committedMessages].reverse().find((message) => message.role === "assistant");
|
|
@@ -1919,11 +2103,15 @@ var init_provider_cli_adapter = __esm({
|
|
|
1919
2103
|
runDetectStatus(text) {
|
|
1920
2104
|
if (!this.cliScripts?.detectStatus) return null;
|
|
1921
2105
|
try {
|
|
1922
|
-
|
|
2106
|
+
const screenText = this.terminalScreen.getText();
|
|
2107
|
+
const status = this.cliScripts.detectStatus({
|
|
1923
2108
|
tail: text.slice(-500),
|
|
1924
|
-
screenText
|
|
1925
|
-
rawBuffer: this.accumulatedRawBuffer
|
|
2109
|
+
screenText,
|
|
2110
|
+
rawBuffer: this.accumulatedRawBuffer,
|
|
2111
|
+
screen: buildCliScreenSnapshot(screenText),
|
|
2112
|
+
tailScreen: buildCliScreenSnapshot(text.slice(-500))
|
|
1926
2113
|
});
|
|
2114
|
+
return this.refineDetectedStatus(status, text, screenText || "");
|
|
1927
2115
|
} catch (e) {
|
|
1928
2116
|
LOG.warn("CLI", `[${this.cliType}] detectStatus error: ${e.message}`);
|
|
1929
2117
|
return null;
|
|
@@ -1932,11 +2120,16 @@ var init_provider_cli_adapter = __esm({
|
|
|
1932
2120
|
runParseApproval(tail) {
|
|
1933
2121
|
if (!this.cliScripts?.parseApproval) return null;
|
|
1934
2122
|
try {
|
|
2123
|
+
const screenText = this.terminalScreen.getText();
|
|
2124
|
+
const buffer = screenText || this.accumulatedBuffer;
|
|
1935
2125
|
return this.cliScripts.parseApproval({
|
|
1936
|
-
buffer
|
|
1937
|
-
screenText
|
|
2126
|
+
buffer,
|
|
2127
|
+
screenText,
|
|
1938
2128
|
rawBuffer: this.accumulatedRawBuffer,
|
|
1939
|
-
tail
|
|
2129
|
+
tail,
|
|
2130
|
+
screen: buildCliScreenSnapshot(screenText),
|
|
2131
|
+
bufferScreen: buildCliScreenSnapshot(buffer),
|
|
2132
|
+
tailScreen: buildCliScreenSnapshot(tail)
|
|
1940
2133
|
});
|
|
1941
2134
|
} catch (e) {
|
|
1942
2135
|
LOG.warn("CLI", `[${this.cliType}] parseApproval error: ${e.message}`);
|
|
@@ -1952,6 +2145,21 @@ var init_provider_cli_adapter = __esm({
|
|
|
1952
2145
|
activeModal: this.activeModal
|
|
1953
2146
|
};
|
|
1954
2147
|
}
|
|
2148
|
+
seedCommittedMessages(messages) {
|
|
2149
|
+
const normalized = (Array.isArray(messages) ? messages : []).filter((message) => message && (message.role === "user" || message.role === "assistant")).map((message) => ({
|
|
2150
|
+
role: message.role,
|
|
2151
|
+
content: typeof message.content === "string" ? message.content : String(message.content || ""),
|
|
2152
|
+
timestamp: typeof message.timestamp === "number" && Number.isFinite(message.timestamp) ? message.timestamp : void 0,
|
|
2153
|
+
receivedAt: typeof message.receivedAt === "number" && Number.isFinite(message.receivedAt) ? message.receivedAt : void 0,
|
|
2154
|
+
kind: typeof message.kind === "string" ? message.kind : void 0,
|
|
2155
|
+
id: typeof message.id === "string" ? message.id : void 0,
|
|
2156
|
+
index: typeof message.index === "number" ? message.index : void 0,
|
|
2157
|
+
meta: message.meta && typeof message.meta === "object" ? { ...message.meta } : void 0,
|
|
2158
|
+
senderName: typeof message.senderName === "string" ? message.senderName : void 0
|
|
2159
|
+
}));
|
|
2160
|
+
this.committedMessages = normalized;
|
|
2161
|
+
this.syncMessageViews();
|
|
2162
|
+
}
|
|
1955
2163
|
/**
|
|
1956
2164
|
* Script-based full parse — returns ReadChatResult.
|
|
1957
2165
|
* Called by command handler / dashboard for rich content rendering.
|
|
@@ -1962,12 +2170,20 @@ var init_provider_cli_adapter = __esm({
|
|
|
1962
2170
|
this.responseBuffer,
|
|
1963
2171
|
this.currentTurnScope
|
|
1964
2172
|
);
|
|
2173
|
+
const shouldPreferCommittedMessages = !this.currentTurnScope && this.currentStatus === "idle" && !this.activeModal;
|
|
1965
2174
|
if (parsed && Array.isArray(parsed.messages)) {
|
|
2175
|
+
const hydratedMessages = shouldPreferCommittedMessages ? this.committedMessages.map((message, index) => ({
|
|
2176
|
+
...message,
|
|
2177
|
+
id: message.id || `msg_${index}`,
|
|
2178
|
+
index: typeof message.index === "number" ? message.index : index,
|
|
2179
|
+
kind: message.kind || "standard",
|
|
2180
|
+
receivedAt: typeof message.receivedAt === "number" ? message.receivedAt : message.timestamp
|
|
2181
|
+
})) : this.hydrateParsedMessages(parsed.messages, this.currentTurnScope);
|
|
1966
2182
|
return {
|
|
1967
2183
|
id: parsed.id || "cli_session",
|
|
1968
2184
|
status: parsed.status || this.currentStatus,
|
|
1969
2185
|
title: parsed.title || this.cliName,
|
|
1970
|
-
messages:
|
|
2186
|
+
messages: hydratedMessages,
|
|
1971
2187
|
activeModal: parsed.activeModal ?? this.activeModal,
|
|
1972
2188
|
providerSessionId: typeof parsed.providerSessionId === "string" ? parsed.providerSessionId : void 0
|
|
1973
2189
|
};
|
|
@@ -1988,11 +2204,30 @@ var init_provider_cli_adapter = __esm({
|
|
|
1988
2204
|
activeModal: this.activeModal
|
|
1989
2205
|
};
|
|
1990
2206
|
}
|
|
2207
|
+
async invokeScript(scriptName, args) {
|
|
2208
|
+
const fn = this.cliScripts?.[scriptName];
|
|
2209
|
+
if (typeof fn !== "function") {
|
|
2210
|
+
throw new Error(`CLI script '${scriptName}' not available`);
|
|
2211
|
+
}
|
|
2212
|
+
const input = this.buildParseInput(
|
|
2213
|
+
this.committedMessages,
|
|
2214
|
+
this.responseBuffer,
|
|
2215
|
+
this.currentTurnScope
|
|
2216
|
+
);
|
|
2217
|
+
return await Promise.resolve(fn({
|
|
2218
|
+
...input,
|
|
2219
|
+
args: args && typeof args === "object" ? { ...args } : {}
|
|
2220
|
+
}));
|
|
2221
|
+
}
|
|
1991
2222
|
parseCurrentTranscript(baseMessages, partialResponse, scope) {
|
|
1992
2223
|
if (!this.cliScripts?.parseOutput) return null;
|
|
1993
2224
|
try {
|
|
1994
2225
|
const input = this.buildParseInput(baseMessages, partialResponse, scope);
|
|
1995
2226
|
const parsed = this.cliScripts.parseOutput(input);
|
|
2227
|
+
const refinedStatus = this.refineDetectedStatus(typeof parsed?.status === "string" ? parsed.status : null, input.recentBuffer, input.screenText);
|
|
2228
|
+
if (parsed && refinedStatus && parsed.status !== refinedStatus) {
|
|
2229
|
+
parsed.status = refinedStatus;
|
|
2230
|
+
}
|
|
1996
2231
|
const promptForTrim = scope?.prompt || getLastUserPromptText(baseMessages);
|
|
1997
2232
|
if (parsed && Array.isArray(parsed.messages) && promptForTrim) {
|
|
1998
2233
|
const lastAssistant = [...parsed.messages].reverse().find((message) => message?.role === "assistant" && typeof message.content === "string");
|
|
@@ -2039,12 +2274,23 @@ ${data.message || ""}`.trim();
|
|
|
2039
2274
|
if (this.startupParseGate) {
|
|
2040
2275
|
const deadline = Date.now() + 1e4;
|
|
2041
2276
|
while (this.startupParseGate && Date.now() < deadline) {
|
|
2277
|
+
this.resolveStartupState("send_wait");
|
|
2042
2278
|
await new Promise((resolve9) => setTimeout(resolve9, 50));
|
|
2043
2279
|
}
|
|
2044
2280
|
}
|
|
2281
|
+
await this.waitForInteractivePrompt();
|
|
2282
|
+
if (!this.ready) {
|
|
2283
|
+
this.resolveStartupState("send_precheck");
|
|
2284
|
+
const screenText = this.terminalScreen.getText() || "";
|
|
2285
|
+
const hasPrompt = this.looksLikeVisibleIdlePrompt(screenText);
|
|
2286
|
+
if (hasPrompt && this.currentStatus === "idle") {
|
|
2287
|
+
this.ready = true;
|
|
2288
|
+
this.startupParseGate = false;
|
|
2289
|
+
LOG.info("CLI", `[${this.cliType}] sendMessage recovered idle prompt readiness`);
|
|
2290
|
+
}
|
|
2291
|
+
}
|
|
2045
2292
|
if (!this.ready) throw new Error(`${this.cliName} not ready (status: ${this.currentStatus})`);
|
|
2046
2293
|
if (this.isWaitingForResponse) return;
|
|
2047
|
-
await this.waitForInteractivePrompt();
|
|
2048
2294
|
const blockingModal = this.activeModal || this.getStartupConfirmationModal(this.terminalScreen.getText() || "");
|
|
2049
2295
|
if (blockingModal || this.currentStatus === "waiting_approval") {
|
|
2050
2296
|
throw new Error(`${this.cliName} is awaiting confirmation before it can accept a prompt`);
|
|
@@ -2088,8 +2334,6 @@ ${data.message || ""}`.trim();
|
|
|
2088
2334
|
}
|
|
2089
2335
|
this.responseEpoch += 1;
|
|
2090
2336
|
this.responseSettleIgnoreUntil = Date.now() + submitDelayMs + this.timeouts.outputSettle + 250;
|
|
2091
|
-
this.setStatus("generating", "sendMessage");
|
|
2092
|
-
this.onStatusChange?.();
|
|
2093
2337
|
const startResponseTimeout = () => {
|
|
2094
2338
|
if (this.responseTimeout) clearTimeout(this.responseTimeout);
|
|
2095
2339
|
this.responseTimeout = setTimeout(() => {
|
|
@@ -2108,7 +2352,7 @@ ${data.message || ""}`.trim();
|
|
|
2108
2352
|
const retrySubmitIfStuck = (attempt) => {
|
|
2109
2353
|
this.submitRetryTimer = null;
|
|
2110
2354
|
if (!this.ptyProcess || !this.isWaitingForResponse || this.submitRetryUsed) return;
|
|
2111
|
-
if (this.currentStatus
|
|
2355
|
+
if (this.currentStatus === "waiting_approval") return;
|
|
2112
2356
|
if ((this.responseBuffer || "").trim()) return;
|
|
2113
2357
|
const screenText = this.terminalScreen.getText();
|
|
2114
2358
|
if (!promptLikelyVisible(screenText, normalizedPromptSnippet)) return;
|
|
@@ -2143,7 +2387,7 @@ ${data.message || ""}`.trim();
|
|
|
2143
2387
|
this.submitRetryTimer = setTimeout(() => {
|
|
2144
2388
|
this.submitRetryTimer = null;
|
|
2145
2389
|
if (!this.ptyProcess || !this.isWaitingForResponse || this.submitRetryUsed) return;
|
|
2146
|
-
if (this.currentStatus
|
|
2390
|
+
if (this.currentStatus === "waiting_approval") return;
|
|
2147
2391
|
if ((this.responseBuffer || "").trim()) return;
|
|
2148
2392
|
const screenText = this.terminalScreen.getText();
|
|
2149
2393
|
if (!promptLikelyVisible(screenText, normalizedPromptSnippet)) return;
|
|
@@ -4582,199 +4826,94 @@ var StatusMonitor = class {
|
|
|
4582
4826
|
}
|
|
4583
4827
|
};
|
|
4584
4828
|
|
|
4585
|
-
// src/providers/
|
|
4586
|
-
|
|
4587
|
-
|
|
4588
|
-
|
|
4589
|
-
|
|
4590
|
-
|
|
4591
|
-
|
|
4592
|
-
|
|
4593
|
-
|
|
4594
|
-
|
|
4595
|
-
|
|
4596
|
-
|
|
4597
|
-
|
|
4598
|
-
|
|
4599
|
-
|
|
4600
|
-
|
|
4601
|
-
|
|
4602
|
-
|
|
4603
|
-
|
|
4604
|
-
|
|
4605
|
-
|
|
4606
|
-
|
|
4607
|
-
|
|
4608
|
-
|
|
4609
|
-
|
|
4610
|
-
|
|
4611
|
-
|
|
4612
|
-
|
|
4613
|
-
|
|
4614
|
-
|
|
4615
|
-
|
|
4616
|
-
|
|
4617
|
-
|
|
4618
|
-
|
|
4619
|
-
|
|
4620
|
-
|
|
4621
|
-
|
|
4622
|
-
|
|
4623
|
-
|
|
4624
|
-
|
|
4625
|
-
|
|
4626
|
-
|
|
4627
|
-
|
|
4628
|
-
|
|
4629
|
-
}
|
|
4630
|
-
getState() {
|
|
4631
|
-
return {
|
|
4632
|
-
type: this.type,
|
|
4633
|
-
name: this.provider.name,
|
|
4634
|
-
category: "extension",
|
|
4635
|
-
status: this.currentStatus,
|
|
4636
|
-
activeChat: this.messages.length > 0 ? {
|
|
4637
|
-
id: this.chatId || this.instanceId,
|
|
4638
|
-
title: this.chatTitle || this.agentName || this.provider.name,
|
|
4639
|
-
status: this.currentStatus,
|
|
4640
|
-
messages: this.messages,
|
|
4641
|
-
activeModal: this.activeModal,
|
|
4642
|
-
inputContent: ""
|
|
4643
|
-
} : null,
|
|
4644
|
-
currentModel: this.currentModel || void 0,
|
|
4645
|
-
currentPlan: this.currentMode || void 0,
|
|
4646
|
-
controlValues: this.controlValues,
|
|
4647
|
-
providerControls: this.provider.controls,
|
|
4648
|
-
agentStreams: this.agentStreams,
|
|
4649
|
-
instanceId: this.instanceId,
|
|
4650
|
-
lastUpdated: Date.now(),
|
|
4651
|
-
settings: this.settings,
|
|
4652
|
-
pendingEvents: this.flushEvents()
|
|
4653
|
-
};
|
|
4654
|
-
}
|
|
4655
|
-
onEvent(event, data) {
|
|
4656
|
-
if (event === "stream_update") {
|
|
4657
|
-
if (data?.streams) this.agentStreams = data.streams;
|
|
4658
|
-
if (data?.messages) this.messages = data.messages;
|
|
4659
|
-
if (data?.activeModal !== void 0) this.activeModal = data.activeModal;
|
|
4660
|
-
if (data?.model) this.currentModel = data.model;
|
|
4661
|
-
if (data?.mode) this.currentMode = data.mode;
|
|
4662
|
-
if (data?.controlValues) this.controlValues = data.controlValues;
|
|
4663
|
-
if (typeof data?.sessionId === "string" && data.sessionId.trim()) this.chatId = data.sessionId;
|
|
4664
|
-
if (typeof data?.title === "string" && data.title.trim()) this.chatTitle = data.title;
|
|
4665
|
-
if (typeof data?.agentName === "string" && data.agentName.trim()) this.agentName = data.agentName;
|
|
4666
|
-
if (typeof data?.extensionId === "string" && data.extensionId.trim()) this.extensionId = data.extensionId;
|
|
4667
|
-
if (data?.status) {
|
|
4668
|
-
const newStatus = data.status;
|
|
4669
|
-
this.detectTransition(newStatus, data);
|
|
4670
|
-
this.currentStatus = newStatus;
|
|
4671
|
-
}
|
|
4672
|
-
} else if (event === "stream_reset") {
|
|
4673
|
-
this.resetStreamState();
|
|
4674
|
-
} else if (event === "extension_connected") {
|
|
4675
|
-
this.ideType = data?.ideType || "";
|
|
4829
|
+
// src/providers/control-effects.ts
|
|
4830
|
+
function extractProviderControlValues(controls, data) {
|
|
4831
|
+
if (!data || typeof data !== "object") return void 0;
|
|
4832
|
+
const values = {};
|
|
4833
|
+
const explicit = data.controlValues;
|
|
4834
|
+
if (explicit && typeof explicit === "object") {
|
|
4835
|
+
for (const [key, value] of Object.entries(explicit)) {
|
|
4836
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
4837
|
+
values[key] = value;
|
|
4838
|
+
}
|
|
4839
|
+
}
|
|
4840
|
+
}
|
|
4841
|
+
for (const ctrl of controls || []) {
|
|
4842
|
+
if (!ctrl.readFrom) continue;
|
|
4843
|
+
const rawValue = data[ctrl.readFrom];
|
|
4844
|
+
if (rawValue === void 0 || rawValue === null) continue;
|
|
4845
|
+
values[ctrl.id] = normalizeControlValue(rawValue);
|
|
4846
|
+
}
|
|
4847
|
+
if (data.model !== void 0 && values.model === void 0) values.model = normalizeControlValue(data.model);
|
|
4848
|
+
if (data.mode !== void 0 && values.mode === void 0) values.mode = normalizeControlValue(data.mode);
|
|
4849
|
+
return Object.keys(values).length > 0 ? values : void 0;
|
|
4850
|
+
}
|
|
4851
|
+
function normalizeProviderEffects(data) {
|
|
4852
|
+
const rawEffects = Array.isArray(data?.effects) ? data.effects : [];
|
|
4853
|
+
const effects = [];
|
|
4854
|
+
for (const raw of rawEffects) {
|
|
4855
|
+
if (!raw || typeof raw !== "object") continue;
|
|
4856
|
+
const type = raw.type;
|
|
4857
|
+
if (type === "message" && raw.message && typeof raw.message === "object") {
|
|
4858
|
+
const content = raw.message.content;
|
|
4859
|
+
if (typeof content !== "string" && !Array.isArray(content)) continue;
|
|
4860
|
+
effects.push({
|
|
4861
|
+
type: "message",
|
|
4862
|
+
id: typeof raw.id === "string" ? raw.id : void 0,
|
|
4863
|
+
when: raw.when === "turn_completed" ? "turn_completed" : "immediate",
|
|
4864
|
+
persist: raw.persist !== false,
|
|
4865
|
+
message: {
|
|
4866
|
+
role: raw.message.role === "assistant" || raw.message.role === "user" ? raw.message.role : "system",
|
|
4867
|
+
content,
|
|
4868
|
+
kind: typeof raw.message.kind === "string" ? raw.message.kind : void 0,
|
|
4869
|
+
senderName: typeof raw.message.senderName === "string" ? raw.message.senderName : void 0
|
|
4870
|
+
}
|
|
4871
|
+
});
|
|
4872
|
+
continue;
|
|
4676
4873
|
}
|
|
4677
|
-
|
|
4678
|
-
|
|
4679
|
-
|
|
4680
|
-
|
|
4681
|
-
|
|
4682
|
-
|
|
4683
|
-
|
|
4684
|
-
|
|
4685
|
-
|
|
4686
|
-
|
|
4687
|
-
|
|
4688
|
-
|
|
4689
|
-
const now = Date.now();
|
|
4690
|
-
const agentStatus = newStatus === "streaming" || newStatus === "generating" ? "generating" : newStatus === "waiting_approval" ? "waiting_approval" : "idle";
|
|
4691
|
-
const lastMsg = Array.isArray(data?.messages) && data.messages.length > 0 ? data.messages[data.messages.length - 1] : null;
|
|
4692
|
-
const progressFingerprint = agentStatus === "generating" ? `${lastMsg?.role || ""}:${typeof lastMsg?.content === "string" ? lastMsg.content : JSON.stringify(lastMsg?.content || "")}`.slice(-2e3) : void 0;
|
|
4693
|
-
if (agentStatus !== this.lastAgentStatus) {
|
|
4694
|
-
if (this.lastAgentStatus === "idle" && agentStatus === "generating") {
|
|
4695
|
-
this.generatingStartedAt = now;
|
|
4696
|
-
this.pushEvent({
|
|
4697
|
-
event: "agent:generating_started",
|
|
4698
|
-
chatTitle: this.resolveChatTitle(data),
|
|
4699
|
-
timestamp: now,
|
|
4700
|
-
ideType: this.ideType || this.type,
|
|
4701
|
-
agentType: this.type,
|
|
4702
|
-
agentName: this.agentName || this.provider.name,
|
|
4703
|
-
extensionId: this.extensionId || this.type
|
|
4704
|
-
});
|
|
4705
|
-
} else if (agentStatus === "waiting_approval") {
|
|
4706
|
-
if (!this.generatingStartedAt) this.generatingStartedAt = now;
|
|
4707
|
-
this.pushEvent({
|
|
4708
|
-
event: "agent:waiting_approval",
|
|
4709
|
-
chatTitle: this.resolveChatTitle(data),
|
|
4710
|
-
timestamp: now,
|
|
4711
|
-
ideType: this.ideType || this.type,
|
|
4712
|
-
agentType: this.type,
|
|
4713
|
-
agentName: this.agentName || this.provider.name,
|
|
4714
|
-
extensionId: this.extensionId || this.type,
|
|
4715
|
-
modalMessage: data?.activeModal?.message,
|
|
4716
|
-
modalButtons: data?.activeModal?.buttons
|
|
4717
|
-
});
|
|
4718
|
-
} else if (agentStatus === "idle" && (this.lastAgentStatus === "generating" || this.lastAgentStatus === "waiting_approval")) {
|
|
4719
|
-
const duration = this.generatingStartedAt ? Math.round((now - this.generatingStartedAt) / 1e3) : 0;
|
|
4720
|
-
this.pushEvent({
|
|
4721
|
-
event: "agent:generating_completed",
|
|
4722
|
-
chatTitle: this.resolveChatTitle(data),
|
|
4723
|
-
duration,
|
|
4724
|
-
timestamp: now,
|
|
4725
|
-
ideType: this.ideType || this.type,
|
|
4726
|
-
agentType: this.type,
|
|
4727
|
-
agentName: this.agentName || this.provider.name,
|
|
4728
|
-
extensionId: this.extensionId || this.type
|
|
4729
|
-
});
|
|
4730
|
-
this.generatingStartedAt = 0;
|
|
4731
|
-
}
|
|
4732
|
-
this.lastAgentStatus = agentStatus;
|
|
4874
|
+
if (type === "toast" && raw.toast && typeof raw.toast.message === "string") {
|
|
4875
|
+
effects.push({
|
|
4876
|
+
type: "toast",
|
|
4877
|
+
id: typeof raw.id === "string" ? raw.id : void 0,
|
|
4878
|
+
when: raw.when === "turn_completed" ? "turn_completed" : "immediate",
|
|
4879
|
+
persist: raw.persist !== false,
|
|
4880
|
+
toast: {
|
|
4881
|
+
level: raw.toast.level === "success" || raw.toast.level === "warning" ? raw.toast.level : "info",
|
|
4882
|
+
message: raw.toast.message
|
|
4883
|
+
}
|
|
4884
|
+
});
|
|
4885
|
+
continue;
|
|
4733
4886
|
}
|
|
4734
|
-
|
|
4735
|
-
|
|
4736
|
-
|
|
4737
|
-
|
|
4887
|
+
if (type === "notification" && raw.notification && typeof raw.notification.body === "string") {
|
|
4888
|
+
effects.push({
|
|
4889
|
+
type: "notification",
|
|
4890
|
+
id: typeof raw.id === "string" ? raw.id : void 0,
|
|
4891
|
+
when: raw.when === "turn_completed" ? "turn_completed" : "immediate",
|
|
4892
|
+
persist: raw.persist !== false,
|
|
4893
|
+
notification: {
|
|
4894
|
+
title: typeof raw.notification.title === "string" ? raw.notification.title : void 0,
|
|
4895
|
+
body: raw.notification.body,
|
|
4896
|
+
level: raw.notification.level === "success" || raw.notification.level === "warning" ? raw.notification.level : "info",
|
|
4897
|
+
channels: Array.isArray(raw.notification.channels) ? raw.notification.channels.filter((channel) => channel === "bubble" || channel === "toast" || channel === "browser") : void 0,
|
|
4898
|
+
preferenceKey: raw.notification.preferenceKey === "disconnect" || raw.notification.preferenceKey === "completion" || raw.notification.preferenceKey === "approval" || raw.notification.preferenceKey === "browser" ? raw.notification.preferenceKey : void 0,
|
|
4899
|
+
bubbleContent: typeof raw.notification.bubbleContent === "string" || Array.isArray(raw.notification.bubbleContent) ? raw.notification.bubbleContent : void 0
|
|
4900
|
+
}
|
|
4901
|
+
});
|
|
4738
4902
|
}
|
|
4739
4903
|
}
|
|
4740
|
-
|
|
4741
|
-
|
|
4742
|
-
|
|
4743
|
-
|
|
4744
|
-
|
|
4745
|
-
const events = [...this.events];
|
|
4746
|
-
this.events = [];
|
|
4747
|
-
return events;
|
|
4748
|
-
}
|
|
4749
|
-
resolveChatTitle(data) {
|
|
4750
|
-
const title = typeof data?.title === "string" && data.title.trim() ? data.title.trim() : this.chatTitle;
|
|
4751
|
-
return title || this.agentName || this.provider.name;
|
|
4904
|
+
return effects;
|
|
4905
|
+
}
|
|
4906
|
+
function normalizeControlValue(value) {
|
|
4907
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
4908
|
+
return value;
|
|
4752
4909
|
}
|
|
4753
|
-
|
|
4754
|
-
if (
|
|
4755
|
-
|
|
4756
|
-
|
|
4757
|
-
agentName: this.agentName,
|
|
4758
|
-
extensionId: this.extensionId,
|
|
4759
|
-
messages: this.messages
|
|
4760
|
-
});
|
|
4761
|
-
}
|
|
4762
|
-
this.agentStreams = [];
|
|
4763
|
-
this.messages = [];
|
|
4764
|
-
this.activeModal = null;
|
|
4765
|
-
this.currentModel = "";
|
|
4766
|
-
this.currentMode = "";
|
|
4767
|
-
this.controlValues = {};
|
|
4768
|
-
this.currentStatus = "idle";
|
|
4769
|
-
this.chatId = null;
|
|
4770
|
-
this.chatTitle = null;
|
|
4771
|
-
this.agentName = "";
|
|
4772
|
-
this.extensionId = "";
|
|
4773
|
-
this.lastAgentStatus = "idle";
|
|
4774
|
-
this.generatingStartedAt = 0;
|
|
4775
|
-
this.monitor.reset();
|
|
4910
|
+
if (value && typeof value === "object") {
|
|
4911
|
+
if (typeof value.label === "string") return value.label;
|
|
4912
|
+
if (typeof value.name === "string") return value.name;
|
|
4913
|
+
if (typeof value.id === "string") return value.id;
|
|
4776
4914
|
}
|
|
4777
|
-
|
|
4915
|
+
return String(value);
|
|
4916
|
+
}
|
|
4778
4917
|
|
|
4779
4918
|
// src/config/chat-history.ts
|
|
4780
4919
|
var fs3 = __toESM(require("fs"));
|
|
@@ -5027,28 +5166,357 @@ function listSavedHistorySessions(agentType, options = {}) {
|
|
|
5027
5166
|
if (parsed.role !== "system" && parsed.content.trim()) preview = parsed.content.trim();
|
|
5028
5167
|
}
|
|
5029
5168
|
}
|
|
5030
|
-
if (messageCount === 0 || !lastMessageAt) continue;
|
|
5031
|
-
summaries.push({
|
|
5032
|
-
historySessionId,
|
|
5033
|
-
sessionTitle: sessionTitle || void 0,
|
|
5034
|
-
messageCount,
|
|
5035
|
-
firstMessageAt,
|
|
5036
|
-
lastMessageAt,
|
|
5037
|
-
preview: preview || void 0
|
|
5169
|
+
if (messageCount === 0 || !lastMessageAt) continue;
|
|
5170
|
+
summaries.push({
|
|
5171
|
+
historySessionId,
|
|
5172
|
+
sessionTitle: sessionTitle || void 0,
|
|
5173
|
+
messageCount,
|
|
5174
|
+
firstMessageAt,
|
|
5175
|
+
lastMessageAt,
|
|
5176
|
+
preview: preview || void 0
|
|
5177
|
+
});
|
|
5178
|
+
}
|
|
5179
|
+
summaries.sort((a, b) => b.lastMessageAt - a.lastMessageAt);
|
|
5180
|
+
const offset = Math.max(0, options.offset || 0);
|
|
5181
|
+
const limit = Math.max(1, options.limit || 30);
|
|
5182
|
+
const sliced = summaries.slice(offset, offset + limit);
|
|
5183
|
+
return {
|
|
5184
|
+
sessions: sliced,
|
|
5185
|
+
hasMore: summaries.length > offset + limit
|
|
5186
|
+
};
|
|
5187
|
+
} catch {
|
|
5188
|
+
return { sessions: [], hasMore: false };
|
|
5189
|
+
}
|
|
5190
|
+
}
|
|
5191
|
+
|
|
5192
|
+
// src/providers/extension-provider-instance.ts
|
|
5193
|
+
var ExtensionProviderInstance = class {
|
|
5194
|
+
type;
|
|
5195
|
+
category = "extension";
|
|
5196
|
+
provider;
|
|
5197
|
+
context = null;
|
|
5198
|
+
settings = {};
|
|
5199
|
+
events = [];
|
|
5200
|
+
// status
|
|
5201
|
+
currentStatus = "idle";
|
|
5202
|
+
agentStreams = [];
|
|
5203
|
+
messages = [];
|
|
5204
|
+
activeModal = null;
|
|
5205
|
+
currentModel = "";
|
|
5206
|
+
currentMode = "";
|
|
5207
|
+
controlValues = {};
|
|
5208
|
+
appliedEffectKeys = /* @__PURE__ */ new Set();
|
|
5209
|
+
runtimeMessages = [];
|
|
5210
|
+
lastAgentStatus = "idle";
|
|
5211
|
+
generatingStartedAt = 0;
|
|
5212
|
+
monitor;
|
|
5213
|
+
historyWriter;
|
|
5214
|
+
// meta
|
|
5215
|
+
instanceId;
|
|
5216
|
+
ideType = "";
|
|
5217
|
+
chatId = null;
|
|
5218
|
+
chatTitle = null;
|
|
5219
|
+
agentName = "";
|
|
5220
|
+
extensionId = "";
|
|
5221
|
+
constructor(provider) {
|
|
5222
|
+
this.type = provider.type;
|
|
5223
|
+
this.provider = provider;
|
|
5224
|
+
this.instanceId = crypto.randomUUID();
|
|
5225
|
+
this.monitor = new StatusMonitor();
|
|
5226
|
+
this.historyWriter = new ChatHistoryWriter();
|
|
5227
|
+
}
|
|
5228
|
+
// ─── Lifecycle ──────────────────────────────────
|
|
5229
|
+
async init(context) {
|
|
5230
|
+
this.context = context;
|
|
5231
|
+
this.settings = context.settings || {};
|
|
5232
|
+
this.monitor.updateConfig({
|
|
5233
|
+
approvalAlert: this.settings.approvalAlert !== false,
|
|
5234
|
+
longGeneratingAlert: this.settings.longGeneratingAlert !== false,
|
|
5235
|
+
longGeneratingThresholdSec: this.settings.longGeneratingThresholdSec || 180
|
|
5236
|
+
});
|
|
5237
|
+
}
|
|
5238
|
+
async onTick() {
|
|
5239
|
+
if (!this.context?.cdp?.isConnected) return;
|
|
5240
|
+
}
|
|
5241
|
+
getState() {
|
|
5242
|
+
return {
|
|
5243
|
+
type: this.type,
|
|
5244
|
+
name: this.provider.name,
|
|
5245
|
+
category: "extension",
|
|
5246
|
+
status: this.currentStatus,
|
|
5247
|
+
activeChat: this.messages.length > 0 || this.runtimeMessages.length > 0 ? {
|
|
5248
|
+
id: this.chatId || this.instanceId,
|
|
5249
|
+
title: this.chatTitle || this.agentName || this.provider.name,
|
|
5250
|
+
status: this.currentStatus,
|
|
5251
|
+
messages: this.mergeConversationMessages(this.messages),
|
|
5252
|
+
activeModal: this.activeModal,
|
|
5253
|
+
inputContent: ""
|
|
5254
|
+
} : null,
|
|
5255
|
+
currentModel: this.currentModel || void 0,
|
|
5256
|
+
currentPlan: this.currentMode || void 0,
|
|
5257
|
+
controlValues: this.controlValues,
|
|
5258
|
+
providerControls: this.provider.controls,
|
|
5259
|
+
agentStreams: this.agentStreams,
|
|
5260
|
+
instanceId: this.instanceId,
|
|
5261
|
+
lastUpdated: Date.now(),
|
|
5262
|
+
settings: this.settings,
|
|
5263
|
+
pendingEvents: this.flushEvents()
|
|
5264
|
+
};
|
|
5265
|
+
}
|
|
5266
|
+
onEvent(event, data) {
|
|
5267
|
+
if (event === "stream_update") {
|
|
5268
|
+
if (data?.streams) this.agentStreams = data.streams;
|
|
5269
|
+
if (data?.messages) this.messages = data.messages;
|
|
5270
|
+
if (data?.activeModal !== void 0) this.activeModal = data.activeModal;
|
|
5271
|
+
if (data?.model) this.currentModel = data.model;
|
|
5272
|
+
if (data?.mode) this.currentMode = data.mode;
|
|
5273
|
+
const controlValues = extractProviderControlValues(this.provider.controls, data) || data?.controlValues;
|
|
5274
|
+
if (controlValues) this.controlValues = controlValues;
|
|
5275
|
+
if (typeof data?.sessionId === "string" && data.sessionId.trim()) this.chatId = data.sessionId;
|
|
5276
|
+
if (typeof data?.title === "string" && data.title.trim()) this.chatTitle = data.title;
|
|
5277
|
+
if (typeof data?.agentName === "string" && data.agentName.trim()) this.agentName = data.agentName;
|
|
5278
|
+
if (typeof data?.extensionId === "string" && data.extensionId.trim()) this.extensionId = data.extensionId;
|
|
5279
|
+
if (data?.status) {
|
|
5280
|
+
const newStatus = data.status;
|
|
5281
|
+
this.detectTransition(newStatus, data);
|
|
5282
|
+
this.currentStatus = newStatus;
|
|
5283
|
+
}
|
|
5284
|
+
} else if (event === "stream_reset") {
|
|
5285
|
+
this.resetStreamState();
|
|
5286
|
+
} else if (event === "extension_connected") {
|
|
5287
|
+
this.ideType = data?.ideType || "";
|
|
5288
|
+
} else if (event === "provider_state_patch" && data && typeof data === "object") {
|
|
5289
|
+
this.applyProviderResponse(data, { phase: "immediate" });
|
|
5290
|
+
}
|
|
5291
|
+
}
|
|
5292
|
+
dispose() {
|
|
5293
|
+
this.agentStreams = [];
|
|
5294
|
+
this.messages = [];
|
|
5295
|
+
this.monitor.reset();
|
|
5296
|
+
this.appliedEffectKeys.clear();
|
|
5297
|
+
this.runtimeMessages = [];
|
|
5298
|
+
}
|
|
5299
|
+
updateSettings(newSettings) {
|
|
5300
|
+
this.settings = { ...newSettings };
|
|
5301
|
+
this.monitor.updateConfig({
|
|
5302
|
+
approvalAlert: this.settings.approvalAlert !== false,
|
|
5303
|
+
longGeneratingAlert: this.settings.longGeneratingAlert !== false,
|
|
5304
|
+
longGeneratingThresholdSec: this.settings.longGeneratingThresholdSec || 180
|
|
5305
|
+
});
|
|
5306
|
+
}
|
|
5307
|
+
/** Query UUID instanceId */
|
|
5308
|
+
getInstanceId() {
|
|
5309
|
+
return this.instanceId;
|
|
5310
|
+
}
|
|
5311
|
+
// ─── status transition detect ──────────────────────────────
|
|
5312
|
+
detectTransition(newStatus, data) {
|
|
5313
|
+
const now = Date.now();
|
|
5314
|
+
const agentStatus = newStatus === "streaming" || newStatus === "generating" ? "generating" : newStatus === "waiting_approval" ? "waiting_approval" : "idle";
|
|
5315
|
+
const lastMsg = Array.isArray(data?.messages) && data.messages.length > 0 ? data.messages[data.messages.length - 1] : null;
|
|
5316
|
+
const progressFingerprint = agentStatus === "generating" ? `${lastMsg?.role || ""}:${typeof lastMsg?.content === "string" ? lastMsg.content : JSON.stringify(lastMsg?.content || "")}`.slice(-2e3) : void 0;
|
|
5317
|
+
const previousStatus = this.lastAgentStatus;
|
|
5318
|
+
if (agentStatus !== this.lastAgentStatus) {
|
|
5319
|
+
if (this.lastAgentStatus === "idle" && agentStatus === "generating") {
|
|
5320
|
+
this.generatingStartedAt = now;
|
|
5321
|
+
this.pushEvent({
|
|
5322
|
+
event: "agent:generating_started",
|
|
5323
|
+
chatTitle: this.resolveChatTitle(data),
|
|
5324
|
+
timestamp: now,
|
|
5325
|
+
ideType: this.ideType || this.type,
|
|
5326
|
+
agentType: this.type,
|
|
5327
|
+
agentName: this.agentName || this.provider.name,
|
|
5328
|
+
extensionId: this.extensionId || this.type
|
|
5329
|
+
});
|
|
5330
|
+
} else if (agentStatus === "waiting_approval") {
|
|
5331
|
+
if (!this.generatingStartedAt) this.generatingStartedAt = now;
|
|
5332
|
+
this.pushEvent({
|
|
5333
|
+
event: "agent:waiting_approval",
|
|
5334
|
+
chatTitle: this.resolveChatTitle(data),
|
|
5335
|
+
timestamp: now,
|
|
5336
|
+
ideType: this.ideType || this.type,
|
|
5337
|
+
agentType: this.type,
|
|
5338
|
+
agentName: this.agentName || this.provider.name,
|
|
5339
|
+
extensionId: this.extensionId || this.type,
|
|
5340
|
+
modalMessage: data?.activeModal?.message,
|
|
5341
|
+
modalButtons: data?.activeModal?.buttons
|
|
5342
|
+
});
|
|
5343
|
+
} else if (agentStatus === "idle" && (this.lastAgentStatus === "generating" || this.lastAgentStatus === "waiting_approval")) {
|
|
5344
|
+
const duration = this.generatingStartedAt ? Math.round((now - this.generatingStartedAt) / 1e3) : 0;
|
|
5345
|
+
this.pushEvent({
|
|
5346
|
+
event: "agent:generating_completed",
|
|
5347
|
+
chatTitle: this.resolveChatTitle(data),
|
|
5348
|
+
duration,
|
|
5349
|
+
timestamp: now,
|
|
5350
|
+
ideType: this.ideType || this.type,
|
|
5351
|
+
agentType: this.type,
|
|
5352
|
+
agentName: this.agentName || this.provider.name,
|
|
5353
|
+
extensionId: this.extensionId || this.type
|
|
5354
|
+
});
|
|
5355
|
+
this.generatingStartedAt = 0;
|
|
5356
|
+
}
|
|
5357
|
+
this.lastAgentStatus = agentStatus;
|
|
5358
|
+
}
|
|
5359
|
+
this.applyProviderResponse(data, {
|
|
5360
|
+
phase: agentStatus === "idle" && (previousStatus === "generating" || previousStatus === "waiting_approval") ? "turn_completed" : "immediate"
|
|
5361
|
+
});
|
|
5362
|
+
const agentKey = `${this.type}:ext`;
|
|
5363
|
+
const monitorEvents = this.monitor.check(agentKey, agentStatus, now, progressFingerprint);
|
|
5364
|
+
for (const me of monitorEvents) {
|
|
5365
|
+
this.pushEvent({ event: me.type, agentKey: me.agentKey, message: me.message, elapsedSec: me.elapsedSec, timestamp: me.timestamp });
|
|
5366
|
+
}
|
|
5367
|
+
}
|
|
5368
|
+
pushEvent(event) {
|
|
5369
|
+
this.events.push(event);
|
|
5370
|
+
if (this.events.length > 50) this.events = this.events.slice(-50);
|
|
5371
|
+
}
|
|
5372
|
+
applyProviderResponse(data, options) {
|
|
5373
|
+
if (!data || typeof data !== "object") return;
|
|
5374
|
+
const controlValues = extractProviderControlValues(this.provider.controls, data);
|
|
5375
|
+
if (controlValues) this.controlValues = { ...this.controlValues, ...controlValues };
|
|
5376
|
+
const effects = normalizeProviderEffects(data);
|
|
5377
|
+
for (const effect of effects) {
|
|
5378
|
+
const effectWhen = effect.when || "immediate";
|
|
5379
|
+
if (effectWhen === "turn_completed" && options.phase !== "turn_completed") continue;
|
|
5380
|
+
if (effectWhen === "immediate" && options.phase === "turn_completed") continue;
|
|
5381
|
+
const effectKey = this.getEffectDedupKey(effect);
|
|
5382
|
+
if (this.appliedEffectKeys.has(effectKey)) continue;
|
|
5383
|
+
this.appliedEffectKeys.add(effectKey);
|
|
5384
|
+
if (effect.persist !== false) {
|
|
5385
|
+
const persisted = this.getPersistedEffectContent(effect);
|
|
5386
|
+
if (persisted) this.appendRuntimeSystemMessage(persisted, effectKey);
|
|
5387
|
+
}
|
|
5388
|
+
if (effect.type === "message" && effect.message) {
|
|
5389
|
+
this.pushEvent({
|
|
5390
|
+
event: "provider:message",
|
|
5391
|
+
timestamp: Date.now(),
|
|
5392
|
+
content: typeof effect.message.content === "string" ? effect.message.content : JSON.stringify(effect.message.content),
|
|
5393
|
+
role: effect.message.role || "system",
|
|
5394
|
+
kind: effect.message.kind,
|
|
5395
|
+
senderName: effect.message.senderName
|
|
5396
|
+
});
|
|
5397
|
+
} else if (effect.type === "toast" && effect.toast) {
|
|
5398
|
+
this.pushEvent({
|
|
5399
|
+
event: "provider:toast",
|
|
5400
|
+
effectId: effect.id || effectKey,
|
|
5401
|
+
timestamp: Date.now(),
|
|
5402
|
+
message: effect.toast.message,
|
|
5403
|
+
level: effect.toast.level || "info"
|
|
5404
|
+
});
|
|
5405
|
+
} else if (effect.type === "notification" && effect.notification) {
|
|
5406
|
+
this.pushEvent({
|
|
5407
|
+
event: "provider:notification",
|
|
5408
|
+
effectId: effect.id || effectKey,
|
|
5409
|
+
timestamp: Date.now(),
|
|
5410
|
+
title: effect.notification.title,
|
|
5411
|
+
message: effect.notification.body,
|
|
5412
|
+
content: typeof effect.notification.bubbleContent === "string" ? effect.notification.bubbleContent : effect.notification.body,
|
|
5413
|
+
level: effect.notification.level || "info",
|
|
5414
|
+
channels: effect.notification.channels || ["toast"],
|
|
5415
|
+
preferenceKey: effect.notification.preferenceKey
|
|
5416
|
+
});
|
|
5417
|
+
}
|
|
5418
|
+
}
|
|
5419
|
+
}
|
|
5420
|
+
appendRuntimeSystemMessage(content, dedupKey, receivedAt = Date.now()) {
|
|
5421
|
+
const normalizedContent = String(content || "").trim();
|
|
5422
|
+
if (!normalizedContent) return;
|
|
5423
|
+
if (this.runtimeMessages.some((entry) => entry.key === dedupKey)) return;
|
|
5424
|
+
this.runtimeMessages.push({
|
|
5425
|
+
key: dedupKey,
|
|
5426
|
+
message: {
|
|
5427
|
+
role: "system",
|
|
5428
|
+
senderName: "System",
|
|
5429
|
+
content: normalizedContent,
|
|
5430
|
+
receivedAt,
|
|
5431
|
+
timestamp: receivedAt
|
|
5432
|
+
}
|
|
5433
|
+
});
|
|
5434
|
+
if (this.runtimeMessages.length > 50) this.runtimeMessages = this.runtimeMessages.slice(-50);
|
|
5435
|
+
this.historyWriter.appendNewMessages(
|
|
5436
|
+
this.type,
|
|
5437
|
+
[{
|
|
5438
|
+
role: "system",
|
|
5439
|
+
senderName: "System",
|
|
5440
|
+
content: normalizedContent,
|
|
5441
|
+
kind: "system",
|
|
5442
|
+
receivedAt,
|
|
5443
|
+
historyDedupKey: dedupKey
|
|
5444
|
+
}],
|
|
5445
|
+
this.chatTitle || this.agentName || this.provider.name,
|
|
5446
|
+
this.instanceId,
|
|
5447
|
+
this.chatId || this.instanceId
|
|
5448
|
+
);
|
|
5449
|
+
}
|
|
5450
|
+
mergeConversationMessages(messages) {
|
|
5451
|
+
if (this.runtimeMessages.length === 0) return messages;
|
|
5452
|
+
return [...messages, ...this.runtimeMessages.map((entry) => entry.message)].map((message, index) => ({ message, index })).sort((a, b) => {
|
|
5453
|
+
const aTime = a.message.receivedAt || a.message.timestamp || 0;
|
|
5454
|
+
const bTime = b.message.receivedAt || b.message.timestamp || 0;
|
|
5455
|
+
if (aTime !== bTime) return aTime - bTime;
|
|
5456
|
+
return a.index - b.index;
|
|
5457
|
+
}).map((entry) => entry.message);
|
|
5458
|
+
}
|
|
5459
|
+
getPersistedEffectContent(effect) {
|
|
5460
|
+
if (effect.type === "message") {
|
|
5461
|
+
return typeof effect.message?.content === "string" ? effect.message.content : JSON.stringify(effect.message?.content || "");
|
|
5462
|
+
}
|
|
5463
|
+
if (effect.type === "toast") {
|
|
5464
|
+
return effect.toast?.message || null;
|
|
5465
|
+
}
|
|
5466
|
+
if (effect.type === "notification") {
|
|
5467
|
+
if (typeof effect.notification?.bubbleContent === "string") return effect.notification.bubbleContent;
|
|
5468
|
+
if (typeof effect.notification?.title === "string" && effect.notification.title.trim()) {
|
|
5469
|
+
return `${effect.notification.title}
|
|
5470
|
+
${effect.notification.body || ""}`.trim();
|
|
5471
|
+
}
|
|
5472
|
+
return effect.notification?.body || null;
|
|
5473
|
+
}
|
|
5474
|
+
return null;
|
|
5475
|
+
}
|
|
5476
|
+
getEffectDedupKey(effect) {
|
|
5477
|
+
if (effect.id) return `provider_effect:${effect.id}`;
|
|
5478
|
+
if (effect.type === "message") {
|
|
5479
|
+
return `provider_effect:message:${typeof effect.message?.content === "string" ? effect.message.content : JSON.stringify(effect.message?.content || "")}`;
|
|
5480
|
+
}
|
|
5481
|
+
if (effect.type === "notification") {
|
|
5482
|
+
return `provider_effect:notification:${effect.notification?.title || ""}:${effect.notification?.body || ""}`;
|
|
5483
|
+
}
|
|
5484
|
+
return `provider_effect:toast:${effect.toast?.message || ""}`;
|
|
5485
|
+
}
|
|
5486
|
+
flushEvents() {
|
|
5487
|
+
const events = [...this.events];
|
|
5488
|
+
this.events = [];
|
|
5489
|
+
return events;
|
|
5490
|
+
}
|
|
5491
|
+
resolveChatTitle(data) {
|
|
5492
|
+
const title = typeof data?.title === "string" && data.title.trim() ? data.title.trim() : this.chatTitle;
|
|
5493
|
+
return title || this.agentName || this.provider.name;
|
|
5494
|
+
}
|
|
5495
|
+
resetStreamState() {
|
|
5496
|
+
if (this.currentStatus !== "idle") {
|
|
5497
|
+
this.detectTransition("idle", {
|
|
5498
|
+
title: this.chatTitle,
|
|
5499
|
+
agentName: this.agentName,
|
|
5500
|
+
extensionId: this.extensionId,
|
|
5501
|
+
messages: this.messages
|
|
5038
5502
|
});
|
|
5039
5503
|
}
|
|
5040
|
-
|
|
5041
|
-
|
|
5042
|
-
|
|
5043
|
-
|
|
5044
|
-
|
|
5045
|
-
|
|
5046
|
-
|
|
5047
|
-
|
|
5048
|
-
|
|
5049
|
-
|
|
5504
|
+
this.agentStreams = [];
|
|
5505
|
+
this.messages = [];
|
|
5506
|
+
this.activeModal = null;
|
|
5507
|
+
this.currentModel = "";
|
|
5508
|
+
this.currentMode = "";
|
|
5509
|
+
this.controlValues = {};
|
|
5510
|
+
this.currentStatus = "idle";
|
|
5511
|
+
this.chatId = null;
|
|
5512
|
+
this.chatTitle = null;
|
|
5513
|
+
this.agentName = "";
|
|
5514
|
+
this.extensionId = "";
|
|
5515
|
+
this.lastAgentStatus = "idle";
|
|
5516
|
+
this.generatingStartedAt = 0;
|
|
5517
|
+
this.monitor.reset();
|
|
5050
5518
|
}
|
|
5051
|
-
}
|
|
5519
|
+
};
|
|
5052
5520
|
|
|
5053
5521
|
// src/providers/ide-provider-instance.ts
|
|
5054
5522
|
init_logger();
|
|
@@ -5069,6 +5537,8 @@ var IdeProviderInstance = class {
|
|
|
5069
5537
|
monitor;
|
|
5070
5538
|
historyWriter;
|
|
5071
5539
|
autoApproveBusy = false;
|
|
5540
|
+
appliedEffectKeys = /* @__PURE__ */ new Set();
|
|
5541
|
+
runtimeMessages = [];
|
|
5072
5542
|
// IDE meta
|
|
5073
5543
|
ideVersion = "";
|
|
5074
5544
|
instanceId;
|
|
@@ -5129,7 +5599,7 @@ var IdeProviderInstance = class {
|
|
|
5129
5599
|
id: this.cachedChat.id || "active_session",
|
|
5130
5600
|
title: this.cachedChat.title || this.type,
|
|
5131
5601
|
status: this.cachedChat.status || this.currentStatus,
|
|
5132
|
-
messages: this.cachedChat.messages || [],
|
|
5602
|
+
messages: this.mergeConversationMessages(this.cachedChat.messages || []),
|
|
5133
5603
|
activeModal: this.cachedChat.activeModal || null,
|
|
5134
5604
|
inputContent: this.cachedChat.inputContent || ""
|
|
5135
5605
|
} : null,
|
|
@@ -5169,6 +5639,13 @@ var IdeProviderInstance = class {
|
|
|
5169
5639
|
for (const ext of this.extensions.values()) {
|
|
5170
5640
|
ext.onEvent("stream_reset");
|
|
5171
5641
|
}
|
|
5642
|
+
} else if (event === "provider_state_patch" && data && typeof data === "object") {
|
|
5643
|
+
const extType = typeof data.extensionType === "string" ? data.extensionType : "";
|
|
5644
|
+
if (extType && this.extensions.has(extType)) {
|
|
5645
|
+
this.extensions.get(extType).onEvent("provider_state_patch", data);
|
|
5646
|
+
} else {
|
|
5647
|
+
this.applyProviderResponse(data, { phase: "immediate" });
|
|
5648
|
+
}
|
|
5172
5649
|
}
|
|
5173
5650
|
}
|
|
5174
5651
|
dispose() {
|
|
@@ -5176,11 +5653,21 @@ var IdeProviderInstance = class {
|
|
|
5176
5653
|
this.lastAgentStatuses.clear();
|
|
5177
5654
|
this.generatingStartedAt.clear();
|
|
5178
5655
|
this.monitor.reset();
|
|
5656
|
+
this.appliedEffectKeys.clear();
|
|
5657
|
+
this.runtimeMessages = [];
|
|
5179
5658
|
for (const ext of this.extensions.values()) {
|
|
5180
5659
|
ext.dispose();
|
|
5181
5660
|
}
|
|
5182
5661
|
this.extensions.clear();
|
|
5183
5662
|
}
|
|
5663
|
+
updateSettings(newSettings) {
|
|
5664
|
+
this.settings = { ...newSettings };
|
|
5665
|
+
this.monitor.updateConfig({
|
|
5666
|
+
approvalAlert: this.settings.approvalAlert !== false,
|
|
5667
|
+
longGeneratingAlert: this.settings.longGeneratingAlert !== false,
|
|
5668
|
+
longGeneratingThresholdSec: this.settings.longGeneratingThresholdSec || 180
|
|
5669
|
+
});
|
|
5670
|
+
}
|
|
5184
5671
|
// ─── Extension manage ─────────────────────────────
|
|
5185
5672
|
/** Extension Instance add */
|
|
5186
5673
|
async addExtension(provider, settings) {
|
|
@@ -5293,6 +5780,8 @@ var IdeProviderInstance = class {
|
|
|
5293
5780
|
raw.messages = raw.messages.filter((m) => !hiddenKinds.has(m.kind));
|
|
5294
5781
|
}
|
|
5295
5782
|
}
|
|
5783
|
+
const controlValues = extractProviderControlValues(this.provider.controls, raw);
|
|
5784
|
+
if (controlValues) raw.controlValues = controlValues;
|
|
5296
5785
|
this.cachedChat = { ...raw, activeModal };
|
|
5297
5786
|
this.detectAgentTransitions(raw, now);
|
|
5298
5787
|
if (raw.messages?.length > 0) {
|
|
@@ -5359,6 +5848,9 @@ var IdeProviderInstance = class {
|
|
|
5359
5848
|
}
|
|
5360
5849
|
this.lastAgentStatuses.set(agentKey, agentStatus);
|
|
5361
5850
|
}
|
|
5851
|
+
this.applyProviderResponse(chatData, {
|
|
5852
|
+
phase: agentStatus === "idle" && (lastStatus === "generating" || lastStatus === "waiting_approval") ? "turn_completed" : "immediate"
|
|
5853
|
+
});
|
|
5362
5854
|
if (agentStatus === "waiting_approval" && this.settings.autoApprove && !this.autoApproveBusy) {
|
|
5363
5855
|
this.autoApproveViaScript(chatData);
|
|
5364
5856
|
}
|
|
@@ -5371,6 +5863,136 @@ var IdeProviderInstance = class {
|
|
|
5371
5863
|
this.events.push(event);
|
|
5372
5864
|
if (this.events.length > 50) this.events = this.events.slice(-50);
|
|
5373
5865
|
}
|
|
5866
|
+
applyProviderResponse(data, options) {
|
|
5867
|
+
if (!data || typeof data !== "object") return;
|
|
5868
|
+
const controlValues = extractProviderControlValues(this.provider.controls, data);
|
|
5869
|
+
if (controlValues) {
|
|
5870
|
+
this.cachedChat = {
|
|
5871
|
+
...this.cachedChat || {},
|
|
5872
|
+
...data,
|
|
5873
|
+
controlValues: { ...this.cachedChat?.controlValues || {}, ...controlValues }
|
|
5874
|
+
};
|
|
5875
|
+
}
|
|
5876
|
+
const effects = normalizeProviderEffects(data);
|
|
5877
|
+
for (const effect of effects) {
|
|
5878
|
+
const effectWhen = effect.when || "immediate";
|
|
5879
|
+
if (effectWhen === "turn_completed" && options.phase !== "turn_completed") continue;
|
|
5880
|
+
if (effectWhen === "immediate" && options.phase === "turn_completed") continue;
|
|
5881
|
+
const effectKey = this.getEffectDedupKey(effect);
|
|
5882
|
+
if (this.appliedEffectKeys.has(effectKey)) continue;
|
|
5883
|
+
this.appliedEffectKeys.add(effectKey);
|
|
5884
|
+
if (effect.persist !== false) {
|
|
5885
|
+
const persisted = this.getPersistedEffectContent(effect);
|
|
5886
|
+
if (persisted) this.appendRuntimeSystemMessage(persisted, effectKey);
|
|
5887
|
+
}
|
|
5888
|
+
if (effect.type === "message" && effect.message) {
|
|
5889
|
+
this.pushEvent({
|
|
5890
|
+
event: "provider:message",
|
|
5891
|
+
timestamp: Date.now(),
|
|
5892
|
+
content: typeof effect.message.content === "string" ? effect.message.content : JSON.stringify(effect.message.content),
|
|
5893
|
+
role: effect.message.role || "system",
|
|
5894
|
+
kind: effect.message.kind,
|
|
5895
|
+
senderName: effect.message.senderName
|
|
5896
|
+
});
|
|
5897
|
+
} else if (effect.type === "toast" && effect.toast) {
|
|
5898
|
+
this.pushEvent({
|
|
5899
|
+
event: "provider:toast",
|
|
5900
|
+
effectId: effect.id || effectKey,
|
|
5901
|
+
timestamp: Date.now(),
|
|
5902
|
+
message: effect.toast.message,
|
|
5903
|
+
level: effect.toast.level || "info"
|
|
5904
|
+
});
|
|
5905
|
+
} else if (effect.type === "notification" && effect.notification) {
|
|
5906
|
+
this.pushEvent({
|
|
5907
|
+
event: "provider:notification",
|
|
5908
|
+
effectId: effect.id || effectKey,
|
|
5909
|
+
timestamp: Date.now(),
|
|
5910
|
+
title: effect.notification.title,
|
|
5911
|
+
message: effect.notification.body,
|
|
5912
|
+
content: typeof effect.notification.bubbleContent === "string" ? effect.notification.bubbleContent : effect.notification.body,
|
|
5913
|
+
level: effect.notification.level || "info",
|
|
5914
|
+
channels: effect.notification.channels || ["toast"],
|
|
5915
|
+
preferenceKey: effect.notification.preferenceKey
|
|
5916
|
+
});
|
|
5917
|
+
}
|
|
5918
|
+
}
|
|
5919
|
+
}
|
|
5920
|
+
appendRuntimeSystemMessage(content, dedupKey, receivedAt = Date.now()) {
|
|
5921
|
+
const normalizedContent = String(content || "").trim();
|
|
5922
|
+
if (!normalizedContent) return;
|
|
5923
|
+
if (this.runtimeMessages.some((entry) => entry.key === dedupKey)) return;
|
|
5924
|
+
if (!this.cachedChat) {
|
|
5925
|
+
this.cachedChat = {
|
|
5926
|
+
id: "active_session",
|
|
5927
|
+
title: this.provider.name,
|
|
5928
|
+
status: this.currentStatus,
|
|
5929
|
+
messages: [],
|
|
5930
|
+
activeModal: null,
|
|
5931
|
+
inputContent: ""
|
|
5932
|
+
};
|
|
5933
|
+
}
|
|
5934
|
+
this.runtimeMessages.push({
|
|
5935
|
+
key: dedupKey,
|
|
5936
|
+
message: {
|
|
5937
|
+
role: "system",
|
|
5938
|
+
senderName: "System",
|
|
5939
|
+
content: normalizedContent,
|
|
5940
|
+
receivedAt,
|
|
5941
|
+
timestamp: receivedAt
|
|
5942
|
+
}
|
|
5943
|
+
});
|
|
5944
|
+
if (this.runtimeMessages.length > 50) this.runtimeMessages = this.runtimeMessages.slice(-50);
|
|
5945
|
+
this.historyWriter.appendNewMessages(
|
|
5946
|
+
this.type,
|
|
5947
|
+
[{
|
|
5948
|
+
role: "system",
|
|
5949
|
+
senderName: "System",
|
|
5950
|
+
content: normalizedContent,
|
|
5951
|
+
kind: "system",
|
|
5952
|
+
receivedAt,
|
|
5953
|
+
historyDedupKey: dedupKey
|
|
5954
|
+
}],
|
|
5955
|
+
this.cachedChat?.title || this.provider.name,
|
|
5956
|
+
this.instanceId,
|
|
5957
|
+
this.cachedChat?.id || this.instanceId
|
|
5958
|
+
);
|
|
5959
|
+
}
|
|
5960
|
+
mergeConversationMessages(messages) {
|
|
5961
|
+
if (this.runtimeMessages.length === 0) return messages;
|
|
5962
|
+
return [...messages, ...this.runtimeMessages.map((entry) => entry.message)].map((message, index) => ({ message, index })).sort((a, b) => {
|
|
5963
|
+
const aTime = a.message.receivedAt || a.message.timestamp || 0;
|
|
5964
|
+
const bTime = b.message.receivedAt || b.message.timestamp || 0;
|
|
5965
|
+
if (aTime !== bTime) return aTime - bTime;
|
|
5966
|
+
return a.index - b.index;
|
|
5967
|
+
}).map((entry) => entry.message);
|
|
5968
|
+
}
|
|
5969
|
+
getPersistedEffectContent(effect) {
|
|
5970
|
+
if (effect.type === "message") {
|
|
5971
|
+
return typeof effect.message?.content === "string" ? effect.message.content : JSON.stringify(effect.message?.content || "");
|
|
5972
|
+
}
|
|
5973
|
+
if (effect.type === "toast") {
|
|
5974
|
+
return effect.toast?.message || null;
|
|
5975
|
+
}
|
|
5976
|
+
if (effect.type === "notification") {
|
|
5977
|
+
if (typeof effect.notification?.bubbleContent === "string") return effect.notification.bubbleContent;
|
|
5978
|
+
if (typeof effect.notification?.title === "string" && effect.notification.title.trim()) {
|
|
5979
|
+
return `${effect.notification.title}
|
|
5980
|
+
${effect.notification.body || ""}`.trim();
|
|
5981
|
+
}
|
|
5982
|
+
return effect.notification?.body || null;
|
|
5983
|
+
}
|
|
5984
|
+
return null;
|
|
5985
|
+
}
|
|
5986
|
+
getEffectDedupKey(effect) {
|
|
5987
|
+
if (effect.id) return `provider_effect:${effect.id}`;
|
|
5988
|
+
if (effect.type === "message") {
|
|
5989
|
+
return `provider_effect:message:${typeof effect.message?.content === "string" ? effect.message.content : JSON.stringify(effect.message?.content || "")}`;
|
|
5990
|
+
}
|
|
5991
|
+
if (effect.type === "notification") {
|
|
5992
|
+
return `provider_effect:notification:${effect.notification?.title || ""}:${effect.notification?.body || ""}`;
|
|
5993
|
+
}
|
|
5994
|
+
return `provider_effect:toast:${effect.toast?.message || ""}`;
|
|
5995
|
+
}
|
|
5374
5996
|
flushEvents() {
|
|
5375
5997
|
const events = [...this.events];
|
|
5376
5998
|
this.events = [];
|
|
@@ -6255,6 +6877,24 @@ function isRecentDuplicateSend(key) {
|
|
|
6255
6877
|
recentSendByTarget.set(key, now);
|
|
6256
6878
|
return false;
|
|
6257
6879
|
}
|
|
6880
|
+
function parseMaybeJson(value) {
|
|
6881
|
+
if (typeof value !== "string") return value;
|
|
6882
|
+
try {
|
|
6883
|
+
return JSON.parse(value);
|
|
6884
|
+
} catch {
|
|
6885
|
+
return value;
|
|
6886
|
+
}
|
|
6887
|
+
}
|
|
6888
|
+
function didProviderConfirmSend(result) {
|
|
6889
|
+
const parsed = parseMaybeJson(result);
|
|
6890
|
+
if (parsed === true) return true;
|
|
6891
|
+
if (typeof parsed === "string") {
|
|
6892
|
+
const normalized = parsed.trim().toLowerCase();
|
|
6893
|
+
return normalized === "ok" || normalized === "sent" || normalized === "success" || normalized === "true";
|
|
6894
|
+
}
|
|
6895
|
+
if (!parsed || typeof parsed !== "object") return false;
|
|
6896
|
+
return parsed.sent === true || parsed.success === true || parsed.ok === true || parsed.submitted === true || parsed.dispatched === true;
|
|
6897
|
+
}
|
|
6258
6898
|
async function handleChatHistory(h, args) {
|
|
6259
6899
|
const { agentType, offset, limit } = args;
|
|
6260
6900
|
const historySessionId = getHistorySessionId(h, args);
|
|
@@ -6437,14 +7077,8 @@ async function handleSendChat(h, args) {
|
|
|
6437
7077
|
try {
|
|
6438
7078
|
const evalResult = await h.evaluateProviderScript("sendMessage", { MESSAGE: text }, 3e4);
|
|
6439
7079
|
if (evalResult?.result) {
|
|
6440
|
-
|
|
6441
|
-
if (
|
|
6442
|
-
try {
|
|
6443
|
-
parsed = JSON.parse(parsed);
|
|
6444
|
-
} catch {
|
|
6445
|
-
}
|
|
6446
|
-
}
|
|
6447
|
-
if (parsed?.sent) {
|
|
7080
|
+
const parsed = parseMaybeJson(evalResult.result);
|
|
7081
|
+
if (didProviderConfirmSend(parsed)) {
|
|
6448
7082
|
_log(`Extension script sent OK`);
|
|
6449
7083
|
return _logSendSuccess("extension-script");
|
|
6450
7084
|
}
|
|
@@ -6476,14 +7110,8 @@ async function handleSendChat(h, args) {
|
|
|
6476
7110
|
if (sendScript) {
|
|
6477
7111
|
try {
|
|
6478
7112
|
const result = await targetCdp.evaluate(sendScript, 3e4);
|
|
6479
|
-
|
|
6480
|
-
if (
|
|
6481
|
-
try {
|
|
6482
|
-
parsed = JSON.parse(result);
|
|
6483
|
-
} catch {
|
|
6484
|
-
}
|
|
6485
|
-
}
|
|
6486
|
-
if (parsed?.sent) {
|
|
7113
|
+
const parsed = parseMaybeJson(result);
|
|
7114
|
+
if (didProviderConfirmSend(parsed)) {
|
|
6487
7115
|
_log(`sendMessage script OK`);
|
|
6488
7116
|
return _logSendSuccess("script");
|
|
6489
7117
|
}
|
|
@@ -6528,14 +7156,8 @@ async function handleSendChat(h, args) {
|
|
|
6528
7156
|
const matchText = provider.webviewMatchText;
|
|
6529
7157
|
const matchFn = matchText ? (body) => body.includes(matchText) : void 0;
|
|
6530
7158
|
const wvResult = await targetCdp.evaluateInWebviewFrame(webviewScript, matchFn);
|
|
6531
|
-
|
|
6532
|
-
if (
|
|
6533
|
-
try {
|
|
6534
|
-
wvParsed = JSON.parse(wvResult);
|
|
6535
|
-
} catch {
|
|
6536
|
-
}
|
|
6537
|
-
}
|
|
6538
|
-
if (wvParsed?.sent) {
|
|
7159
|
+
const wvParsed = parseMaybeJson(wvResult);
|
|
7160
|
+
if (didProviderConfirmSend(wvParsed)) {
|
|
6539
7161
|
_log(`webviewSendMessage OK`);
|
|
6540
7162
|
return _logSendSuccess("webview-script");
|
|
6541
7163
|
}
|
|
@@ -6557,14 +7179,8 @@ async function handleSendChat(h, args) {
|
|
|
6557
7179
|
const matchText = provider.webviewMatchText;
|
|
6558
7180
|
const matchFn = matchText ? (body) => body.includes(matchText) : void 0;
|
|
6559
7181
|
const wvResult = await targetCdp.evaluateInWebviewFrame(webviewScript, matchFn);
|
|
6560
|
-
|
|
6561
|
-
if (
|
|
6562
|
-
try {
|
|
6563
|
-
wvParsed = JSON.parse(wvResult);
|
|
6564
|
-
} catch {
|
|
6565
|
-
}
|
|
6566
|
-
}
|
|
6567
|
-
if (wvParsed?.sent) {
|
|
7182
|
+
const wvParsed = parseMaybeJson(wvResult);
|
|
7183
|
+
if (didProviderConfirmSend(wvParsed)) {
|
|
6568
7184
|
_log(`webviewSendMessage OK`);
|
|
6569
7185
|
return _logSendSuccess("webview-script");
|
|
6570
7186
|
}
|
|
@@ -7450,7 +8066,56 @@ function handleSetProviderSetting(h, args) {
|
|
|
7450
8066
|
}
|
|
7451
8067
|
return { success: false, error: `Failed to set ${providerType}.${key} \u2014 invalid key, value, or not a public setting` };
|
|
7452
8068
|
}
|
|
7453
|
-
|
|
8069
|
+
function normalizeProviderScriptArgs(args) {
|
|
8070
|
+
const normalizedArgs = { ...args || {} };
|
|
8071
|
+
for (const key of ["mode", "model", "message", "action", "button", "text", "sessionId", "value"]) {
|
|
8072
|
+
if (key in normalizedArgs && !(key.toUpperCase() in normalizedArgs)) {
|
|
8073
|
+
normalizedArgs[key.toUpperCase()] = normalizedArgs[key];
|
|
8074
|
+
}
|
|
8075
|
+
}
|
|
8076
|
+
return normalizedArgs;
|
|
8077
|
+
}
|
|
8078
|
+
function parseScriptResult(result) {
|
|
8079
|
+
if (typeof result === "string") {
|
|
8080
|
+
try {
|
|
8081
|
+
const parsed = JSON.parse(result);
|
|
8082
|
+
if (parsed && typeof parsed === "object" && parsed.success === false) {
|
|
8083
|
+
return { success: false, payload: parsed };
|
|
8084
|
+
}
|
|
8085
|
+
return { success: true, payload: parsed };
|
|
8086
|
+
} catch {
|
|
8087
|
+
return { success: true, payload: { result } };
|
|
8088
|
+
}
|
|
8089
|
+
}
|
|
8090
|
+
if (result && typeof result === "object" && result.success === false) {
|
|
8091
|
+
return { success: false, payload: result };
|
|
8092
|
+
}
|
|
8093
|
+
return { success: true, payload: result };
|
|
8094
|
+
}
|
|
8095
|
+
function getCliScriptCommand(payload) {
|
|
8096
|
+
if (!payload || typeof payload !== "object") return null;
|
|
8097
|
+
if (typeof payload.sendMessage === "string" && payload.sendMessage.trim()) {
|
|
8098
|
+
return { type: "send_message", text: payload.sendMessage.trim() };
|
|
8099
|
+
}
|
|
8100
|
+
const command = payload.command;
|
|
8101
|
+
if (!command || typeof command !== "object") return null;
|
|
8102
|
+
if (command.type !== "send_message") return null;
|
|
8103
|
+
const text = typeof command.text === "string" ? command.text.trim() : typeof command.message === "string" ? command.message.trim() : "";
|
|
8104
|
+
if (!text) return null;
|
|
8105
|
+
return { type: "send_message", text };
|
|
8106
|
+
}
|
|
8107
|
+
function applyProviderPatch(h, args, payload) {
|
|
8108
|
+
if (!payload || typeof payload !== "object") return;
|
|
8109
|
+
const targetSessionId = typeof args?.targetSessionId === "string" ? args.targetSessionId.trim() : "";
|
|
8110
|
+
const targetSession = targetSessionId ? h.ctx.sessionRegistry?.get(targetSessionId) : void 0;
|
|
8111
|
+
const instanceKey = targetSession?.instanceKey || targetSessionId;
|
|
8112
|
+
if (!instanceKey) return;
|
|
8113
|
+
h.ctx.instanceManager?.sendEvent(instanceKey, "provider_state_patch", {
|
|
8114
|
+
...payload,
|
|
8115
|
+
extensionType: targetSession?.transport === "cdp-webview" ? targetSession.providerType : void 0
|
|
8116
|
+
});
|
|
8117
|
+
}
|
|
8118
|
+
async function executeProviderScript(h, args, scriptName) {
|
|
7454
8119
|
const { agentType, ideType } = args || {};
|
|
7455
8120
|
if (!agentType) return { success: false, error: "agentType is required" };
|
|
7456
8121
|
const loader = h.ctx.providerLoader;
|
|
@@ -7463,13 +8128,29 @@ async function handleExtensionScript(h, args, scriptName) {
|
|
|
7463
8128
|
if (!provider.scripts?.[actualScriptName]) {
|
|
7464
8129
|
return { success: false, error: `Script '${actualScriptName}' not available for ${agentType}` };
|
|
7465
8130
|
}
|
|
7466
|
-
const
|
|
7467
|
-
|
|
7468
|
-
|
|
7469
|
-
if (
|
|
7470
|
-
|
|
8131
|
+
const normalizedArgs = normalizeProviderScriptArgs(args);
|
|
8132
|
+
if (provider.category === "cli") {
|
|
8133
|
+
const adapter = h.getCliAdapter(args?.targetSessionId || agentType);
|
|
8134
|
+
if (!adapter?.invokeScript) {
|
|
8135
|
+
return { success: false, error: `CLI adapter does not support script '${actualScriptName}'` };
|
|
8136
|
+
}
|
|
8137
|
+
try {
|
|
8138
|
+
const raw = await adapter.invokeScript(actualScriptName, normalizedArgs);
|
|
8139
|
+
const parsed = parseScriptResult(raw);
|
|
8140
|
+
if (!parsed.success) {
|
|
8141
|
+
return { success: false, ...parsed.payload || {} };
|
|
8142
|
+
}
|
|
8143
|
+
const cliCommand = getCliScriptCommand(parsed.payload);
|
|
8144
|
+
if (cliCommand?.type === "send_message" && cliCommand.text) {
|
|
8145
|
+
await adapter.sendMessage(cliCommand.text);
|
|
8146
|
+
}
|
|
8147
|
+
applyProviderPatch(h, args, parsed.payload);
|
|
8148
|
+
return { success: true, ...parsed.payload && typeof parsed.payload === "object" ? parsed.payload : { result: parsed.payload } };
|
|
8149
|
+
} catch (e) {
|
|
8150
|
+
return { success: false, error: `Script execution failed: ${e.message}` };
|
|
7471
8151
|
}
|
|
7472
8152
|
}
|
|
8153
|
+
const scriptFn = provider.scripts[actualScriptName];
|
|
7473
8154
|
const scriptCode = scriptFn(normalizedArgs);
|
|
7474
8155
|
if (!scriptCode) return { success: false, error: `Script '${actualScriptName}' returned null` };
|
|
7475
8156
|
const cdpKey = provider.category === "ide" ? h.currentSession?.cdpManagerKey || h.currentManagerKey || agentType : h.currentSession?.cdpManagerKey || h.currentManagerKey || ideType;
|
|
@@ -7523,16 +8204,29 @@ async function handleExtensionScript(h, args, scriptName) {
|
|
|
7523
8204
|
if (typeof result === "string") {
|
|
7524
8205
|
try {
|
|
7525
8206
|
const parsed = JSON.parse(result);
|
|
8207
|
+
applyProviderPatch(h, args, parsed);
|
|
8208
|
+
if (parsed && typeof parsed === "object" && parsed.success === false) {
|
|
8209
|
+
return { success: false, ...parsed };
|
|
8210
|
+
}
|
|
7526
8211
|
return { success: true, ...parsed };
|
|
7527
8212
|
} catch {
|
|
7528
8213
|
return { success: true, result };
|
|
7529
8214
|
}
|
|
7530
8215
|
}
|
|
8216
|
+
applyProviderPatch(h, args, result);
|
|
7531
8217
|
return { success: true, result };
|
|
7532
8218
|
} catch (e) {
|
|
7533
8219
|
return { success: false, error: `Script execution failed: ${e.message}` };
|
|
7534
8220
|
}
|
|
7535
8221
|
}
|
|
8222
|
+
async function handleExtensionScript(h, args, scriptName) {
|
|
8223
|
+
return executeProviderScript(h, args, scriptName);
|
|
8224
|
+
}
|
|
8225
|
+
async function handleProviderScript(h, args) {
|
|
8226
|
+
const scriptName = typeof args?.scriptName === "string" ? args.scriptName.trim() : "";
|
|
8227
|
+
if (!scriptName) return { success: false, error: "scriptName is required" };
|
|
8228
|
+
return executeProviderScript(h, args, scriptName);
|
|
8229
|
+
}
|
|
7536
8230
|
function handleGetIdeExtensions(h, args) {
|
|
7537
8231
|
const { ideType } = args || {};
|
|
7538
8232
|
const loader = h.ctx.providerLoader;
|
|
@@ -8032,6 +8726,8 @@ var DaemonCommandHandler = class {
|
|
|
8032
8726
|
case "set_ide_extension":
|
|
8033
8727
|
return handleSetIdeExtension(this, args);
|
|
8034
8728
|
// ─── Extension Model / Mode Control (stream-commands.ts) ──────────
|
|
8729
|
+
case "invoke_provider_script":
|
|
8730
|
+
return handleProviderScript(this, args);
|
|
8035
8731
|
case "list_extension_models":
|
|
8036
8732
|
return handleExtensionScript(this, args, "listModels");
|
|
8037
8733
|
case "set_extension_model":
|
|
@@ -8208,6 +8904,8 @@ var CliProviderInstance = class {
|
|
|
8208
8904
|
generatingDebounceTimer = null;
|
|
8209
8905
|
generatingDebouncePending = null;
|
|
8210
8906
|
lastApprovalEventAt = 0;
|
|
8907
|
+
controlValues = {};
|
|
8908
|
+
appliedEffectKeys = /* @__PURE__ */ new Set();
|
|
8211
8909
|
historyWriter;
|
|
8212
8910
|
runtimeMessages = [];
|
|
8213
8911
|
instanceId;
|
|
@@ -8220,6 +8918,7 @@ var CliProviderInstance = class {
|
|
|
8220
8918
|
async init(context) {
|
|
8221
8919
|
this.context = context;
|
|
8222
8920
|
this.settings = context.settings || {};
|
|
8921
|
+
this.adapter.updateRuntimeSettings?.(this.settings);
|
|
8223
8922
|
this.monitor.updateConfig({
|
|
8224
8923
|
approvalAlert: this.settings.approvalAlert !== false,
|
|
8225
8924
|
longGeneratingAlert: this.settings.longGeneratingAlert !== false,
|
|
@@ -8235,6 +8934,21 @@ var CliProviderInstance = class {
|
|
|
8235
8934
|
this.detectStatusTransition();
|
|
8236
8935
|
});
|
|
8237
8936
|
await this.adapter.spawn();
|
|
8937
|
+
if (this.providerSessionId) {
|
|
8938
|
+
const restoredHistory = readChatHistory(this.type, 0, 200, this.providerSessionId);
|
|
8939
|
+
if (restoredHistory.messages.length > 0) {
|
|
8940
|
+
this.adapter.seedCommittedMessages(
|
|
8941
|
+
restoredHistory.messages.map((message) => ({
|
|
8942
|
+
role: message.role,
|
|
8943
|
+
content: message.content,
|
|
8944
|
+
timestamp: message.receivedAt,
|
|
8945
|
+
receivedAt: message.receivedAt,
|
|
8946
|
+
kind: message.kind,
|
|
8947
|
+
senderName: message.senderName
|
|
8948
|
+
}))
|
|
8949
|
+
);
|
|
8950
|
+
}
|
|
8951
|
+
}
|
|
8238
8952
|
if (this.providerSessionId && this.launchMode === "resume") {
|
|
8239
8953
|
const resumedAt = Date.now();
|
|
8240
8954
|
this.historyWriter.appendSystemMarker(
|
|
@@ -8315,6 +9029,12 @@ var CliProviderInstance = class {
|
|
|
8315
9029
|
}
|
|
8316
9030
|
const runtime = this.adapter.getRuntimeMetadata();
|
|
8317
9031
|
const parsedMessages = Array.isArray(parsedStatus?.messages) ? parsedStatus.messages : [];
|
|
9032
|
+
const controlValues = extractProviderControlValues(this.provider.controls, parsedStatus);
|
|
9033
|
+
if (controlValues) {
|
|
9034
|
+
this.controlValues = controlValues;
|
|
9035
|
+
} else if (Object.keys(this.controlValues).length > 0) {
|
|
9036
|
+
this.controlValues = {};
|
|
9037
|
+
}
|
|
8318
9038
|
const mergedMessages = this.mergeConversationMessages(parsedMessages);
|
|
8319
9039
|
const dirName = this.workingDir.split("/").filter(Boolean).pop() || "session";
|
|
8320
9040
|
if (parsedMessages.length > 0) {
|
|
@@ -8335,6 +9055,7 @@ var CliProviderInstance = class {
|
|
|
8335
9055
|
);
|
|
8336
9056
|
}
|
|
8337
9057
|
}
|
|
9058
|
+
this.applyProviderResponse(parsedStatus, { phase: "immediate" });
|
|
8338
9059
|
return {
|
|
8339
9060
|
type: this.type,
|
|
8340
9061
|
name: this.provider.name,
|
|
@@ -8364,8 +9085,7 @@ var CliProviderInstance = class {
|
|
|
8364
9085
|
attachedClients: runtime.attachedClients || []
|
|
8365
9086
|
} : void 0,
|
|
8366
9087
|
resume: this.provider.resume,
|
|
8367
|
-
controlValues:
|
|
8368
|
-
// CLI controls not yet wired from stream
|
|
9088
|
+
controlValues: this.controlValues,
|
|
8369
9089
|
providerControls: this.provider.controls
|
|
8370
9090
|
};
|
|
8371
9091
|
}
|
|
@@ -8376,6 +9096,15 @@ var CliProviderInstance = class {
|
|
|
8376
9096
|
getPresentationMode() {
|
|
8377
9097
|
return this.presentationMode;
|
|
8378
9098
|
}
|
|
9099
|
+
updateSettings(newSettings) {
|
|
9100
|
+
this.settings = { ...newSettings };
|
|
9101
|
+
this.adapter.updateRuntimeSettings?.(this.settings);
|
|
9102
|
+
this.monitor.updateConfig({
|
|
9103
|
+
approvalAlert: this.settings.approvalAlert !== false,
|
|
9104
|
+
longGeneratingAlert: this.settings.longGeneratingAlert !== false,
|
|
9105
|
+
longGeneratingThresholdSec: this.settings.longGeneratingThresholdSec || 180
|
|
9106
|
+
});
|
|
9107
|
+
}
|
|
8379
9108
|
onEvent(event, data) {
|
|
8380
9109
|
if (event === "send_message" && data?.text) {
|
|
8381
9110
|
void this.adapter.sendMessage(data.text).catch((e) => {
|
|
@@ -8387,22 +9116,27 @@ var CliProviderInstance = class {
|
|
|
8387
9116
|
void this.adapter.resolveAction(data).catch((e) => {
|
|
8388
9117
|
LOG.warn("CLI", `[${this.type}] resolve_action failed: ${e?.message || e}`);
|
|
8389
9118
|
});
|
|
9119
|
+
} else if (event === "provider_state_patch" && data && typeof data === "object") {
|
|
9120
|
+
this.applyProviderResponse(data, { phase: "immediate" });
|
|
8390
9121
|
}
|
|
8391
9122
|
}
|
|
8392
9123
|
dispose() {
|
|
8393
9124
|
this.adapter.shutdown();
|
|
8394
9125
|
this.monitor.reset();
|
|
9126
|
+
this.appliedEffectKeys.clear();
|
|
8395
9127
|
}
|
|
8396
9128
|
completedDebounceTimer = null;
|
|
8397
9129
|
completedDebouncePending = null;
|
|
8398
9130
|
detectStatusTransition() {
|
|
8399
9131
|
const now = Date.now();
|
|
8400
9132
|
const adapterStatus = this.adapter.getStatus();
|
|
9133
|
+
const parsedStatus = this.adapter.getScriptParsedStatus?.() || null;
|
|
8401
9134
|
const newStatus = adapterStatus.status;
|
|
8402
9135
|
const dirName = this.workingDir.split("/").filter(Boolean).pop() || "session";
|
|
8403
9136
|
const chatTitle = `${this.provider.name} \xB7 ${dirName}`;
|
|
8404
9137
|
const partial = this.adapter.getPartialResponse();
|
|
8405
9138
|
const progressFingerprint = newStatus === "generating" ? `${partial || ""}::${adapterStatus.messages.at(-1)?.content || ""}`.slice(-2e3) : void 0;
|
|
9139
|
+
const previousStatus = this.lastStatus;
|
|
8406
9140
|
if (newStatus !== this.lastStatus) {
|
|
8407
9141
|
LOG.info("CLI", `[${this.type}] status: ${this.lastStatus} \u2192 ${newStatus}`);
|
|
8408
9142
|
if (this.lastStatus === "idle" && newStatus === "generating") {
|
|
@@ -8495,6 +9229,9 @@ var CliProviderInstance = class {
|
|
|
8495
9229
|
}
|
|
8496
9230
|
this.lastStatus = newStatus;
|
|
8497
9231
|
}
|
|
9232
|
+
this.applyProviderResponse(parsedStatus, {
|
|
9233
|
+
phase: newStatus === "idle" && (previousStatus === "generating" || previousStatus === "waiting_approval") ? "turn_completed" : "immediate"
|
|
9234
|
+
});
|
|
8498
9235
|
const agentKey = `${this.type}:cli`;
|
|
8499
9236
|
const monitorEvents = this.monitor.check(agentKey, newStatus, now, progressFingerprint);
|
|
8500
9237
|
for (const me of monitorEvents) {
|
|
@@ -8510,6 +9247,88 @@ var CliProviderInstance = class {
|
|
|
8510
9247
|
this.events = [];
|
|
8511
9248
|
return events;
|
|
8512
9249
|
}
|
|
9250
|
+
applyProviderResponse(data, options) {
|
|
9251
|
+
if (!data || typeof data !== "object") return;
|
|
9252
|
+
const controlValues = extractProviderControlValues(this.provider.controls, data);
|
|
9253
|
+
if (controlValues) {
|
|
9254
|
+
this.controlValues = { ...this.controlValues, ...controlValues };
|
|
9255
|
+
}
|
|
9256
|
+
const effects = normalizeProviderEffects(data);
|
|
9257
|
+
for (const effect of effects) {
|
|
9258
|
+
const effectWhen = effect.when || "immediate";
|
|
9259
|
+
if (effectWhen === "turn_completed" && options.phase !== "turn_completed") continue;
|
|
9260
|
+
if (effectWhen === "immediate" && options.phase === "turn_completed") continue;
|
|
9261
|
+
const effectKey = this.getEffectDedupKey(effect);
|
|
9262
|
+
if (this.appliedEffectKeys.has(effectKey)) continue;
|
|
9263
|
+
this.appliedEffectKeys.add(effectKey);
|
|
9264
|
+
if (effect.persist !== false) {
|
|
9265
|
+
const persisted = this.getPersistedEffectContent(effect);
|
|
9266
|
+
if (persisted) this.appendRuntimeSystemMessage(persisted, effectKey);
|
|
9267
|
+
}
|
|
9268
|
+
if (effect.type === "message" && effect.message) {
|
|
9269
|
+
const content = typeof effect.message.content === "string" ? effect.message.content : JSON.stringify(effect.message.content);
|
|
9270
|
+
this.pushEvent({
|
|
9271
|
+
event: "provider:message",
|
|
9272
|
+
timestamp: Date.now(),
|
|
9273
|
+
content,
|
|
9274
|
+
role: effect.message.role || "system",
|
|
9275
|
+
kind: effect.message.kind,
|
|
9276
|
+
senderName: effect.message.senderName
|
|
9277
|
+
});
|
|
9278
|
+
} else if (effect.type === "toast" && effect.toast) {
|
|
9279
|
+
this.pushEvent({
|
|
9280
|
+
event: "provider:toast",
|
|
9281
|
+
effectId: effect.id || effectKey,
|
|
9282
|
+
timestamp: Date.now(),
|
|
9283
|
+
message: effect.toast.message,
|
|
9284
|
+
level: effect.toast.level || "info"
|
|
9285
|
+
});
|
|
9286
|
+
} else if (effect.type === "notification" && effect.notification) {
|
|
9287
|
+
this.pushEvent({
|
|
9288
|
+
event: "provider:notification",
|
|
9289
|
+
effectId: effect.id || effectKey,
|
|
9290
|
+
timestamp: Date.now(),
|
|
9291
|
+
title: effect.notification.title,
|
|
9292
|
+
message: effect.notification.body,
|
|
9293
|
+
content: typeof effect.notification.bubbleContent === "string" ? effect.notification.bubbleContent : effect.notification.body,
|
|
9294
|
+
level: effect.notification.level || "info",
|
|
9295
|
+
channels: effect.notification.channels || ["toast"],
|
|
9296
|
+
preferenceKey: effect.notification.preferenceKey
|
|
9297
|
+
});
|
|
9298
|
+
}
|
|
9299
|
+
}
|
|
9300
|
+
if (this.appliedEffectKeys.size > 200) {
|
|
9301
|
+
this.appliedEffectKeys = new Set(Array.from(this.appliedEffectKeys).slice(-100));
|
|
9302
|
+
}
|
|
9303
|
+
}
|
|
9304
|
+
getEffectDedupKey(effect) {
|
|
9305
|
+
if (effect.id) return `provider_effect:${effect.id}`;
|
|
9306
|
+
if (effect.type === "message") {
|
|
9307
|
+
const content = typeof effect.message?.content === "string" ? effect.message.content : JSON.stringify(effect.message?.content || "");
|
|
9308
|
+
return `provider_effect:message:${content}`;
|
|
9309
|
+
}
|
|
9310
|
+
if (effect.type === "notification") {
|
|
9311
|
+
return `provider_effect:notification:${effect.notification?.title || ""}:${effect.notification?.body || ""}`;
|
|
9312
|
+
}
|
|
9313
|
+
return `provider_effect:toast:${effect.toast?.message || ""}`;
|
|
9314
|
+
}
|
|
9315
|
+
getPersistedEffectContent(effect) {
|
|
9316
|
+
if (effect.type === "message") {
|
|
9317
|
+
return typeof effect.message?.content === "string" ? effect.message.content : JSON.stringify(effect.message?.content || "");
|
|
9318
|
+
}
|
|
9319
|
+
if (effect.type === "toast") {
|
|
9320
|
+
return effect.toast?.message || null;
|
|
9321
|
+
}
|
|
9322
|
+
if (effect.type === "notification") {
|
|
9323
|
+
if (typeof effect.notification?.bubbleContent === "string") return effect.notification.bubbleContent;
|
|
9324
|
+
if (typeof effect.notification?.title === "string" && effect.notification.title.trim()) {
|
|
9325
|
+
return `${effect.notification.title}
|
|
9326
|
+
${effect.notification.body || ""}`.trim();
|
|
9327
|
+
}
|
|
9328
|
+
return effect.notification?.body || null;
|
|
9329
|
+
}
|
|
9330
|
+
return null;
|
|
9331
|
+
}
|
|
8513
9332
|
// ─── Adapter access (backward compat) ──────────────────
|
|
8514
9333
|
getAdapter() {
|
|
8515
9334
|
return this.adapter;
|
|
@@ -11282,6 +12101,22 @@ function getMacAppIdentifiers() {
|
|
|
11282
12101
|
function getWinProcessNames() {
|
|
11283
12102
|
return getProviderLoader().getWinProcessNames();
|
|
11284
12103
|
}
|
|
12104
|
+
function getProviderMeta(ideId) {
|
|
12105
|
+
return getProviderLoader().getMeta(ideId);
|
|
12106
|
+
}
|
|
12107
|
+
function getPreferredLaunchMethod(ideId, platform9) {
|
|
12108
|
+
const prefer = getProviderMeta(ideId)?.launch?.prefer;
|
|
12109
|
+
const value = prefer?.[platform9];
|
|
12110
|
+
return value === "cli" || value === "app" || value === "auto" ? value : "auto";
|
|
12111
|
+
}
|
|
12112
|
+
function getCdpStartupTimeoutMs(ideId) {
|
|
12113
|
+
const value = getProviderMeta(ideId)?.launch?.cdpStartupTimeoutMs;
|
|
12114
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return 15e3;
|
|
12115
|
+
return Math.max(1e3, Math.floor(value));
|
|
12116
|
+
}
|
|
12117
|
+
function escapeForAppleScript(value) {
|
|
12118
|
+
return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
12119
|
+
}
|
|
11285
12120
|
async function findFreePort(ports) {
|
|
11286
12121
|
for (const port2 of ports) {
|
|
11287
12122
|
const free = await checkPortFree(port2);
|
|
@@ -11334,12 +12169,12 @@ async function killIdeProcess(ideId) {
|
|
|
11334
12169
|
try {
|
|
11335
12170
|
if (plat === "darwin" && appName) {
|
|
11336
12171
|
try {
|
|
11337
|
-
(0, import_child_process6.execSync)(`osascript -e 'tell application "${appName}" to quit' 2>/dev/null`, {
|
|
12172
|
+
(0, import_child_process6.execSync)(`osascript -e 'tell application "${escapeForAppleScript(appName)}" to quit' 2>/dev/null`, {
|
|
11338
12173
|
timeout: 5e3
|
|
11339
12174
|
});
|
|
11340
12175
|
} catch {
|
|
11341
12176
|
try {
|
|
11342
|
-
(0, import_child_process6.execSync)(`pkill -
|
|
12177
|
+
(0, import_child_process6.execSync)(`pkill -x "${appName}" 2>/dev/null`, { timeout: 5e3 });
|
|
11343
12178
|
} catch {
|
|
11344
12179
|
}
|
|
11345
12180
|
}
|
|
@@ -11369,7 +12204,7 @@ async function killIdeProcess(ideId) {
|
|
|
11369
12204
|
}
|
|
11370
12205
|
if (plat === "darwin" && appName) {
|
|
11371
12206
|
try {
|
|
11372
|
-
(0, import_child_process6.execSync)(`pkill -9 -
|
|
12207
|
+
(0, import_child_process6.execSync)(`pkill -9 -x "${appName}" 2>/dev/null`, { timeout: 5e3 });
|
|
11373
12208
|
} catch {
|
|
11374
12209
|
}
|
|
11375
12210
|
} else if (plat === "win32" && winProcesses) {
|
|
@@ -11392,8 +12227,23 @@ function isIdeRunning(ideId) {
|
|
|
11392
12227
|
if (plat === "darwin") {
|
|
11393
12228
|
const appName = getMacAppIdentifiers()[ideId];
|
|
11394
12229
|
if (!appName) return false;
|
|
11395
|
-
|
|
11396
|
-
|
|
12230
|
+
try {
|
|
12231
|
+
const result = (0, import_child_process6.execSync)(`pgrep -x "${appName}" 2>/dev/null`, {
|
|
12232
|
+
encoding: "utf-8",
|
|
12233
|
+
timeout: 3e3
|
|
12234
|
+
});
|
|
12235
|
+
return result.trim().length > 0;
|
|
12236
|
+
} catch {
|
|
12237
|
+
const result = (0, import_child_process6.execSync)(
|
|
12238
|
+
`osascript -e 'tell application "System Events" to count (every process whose name is "${escapeForAppleScript(appName)}")'`,
|
|
12239
|
+
{
|
|
12240
|
+
encoding: "utf-8",
|
|
12241
|
+
timeout: 3e3,
|
|
12242
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
12243
|
+
}
|
|
12244
|
+
);
|
|
12245
|
+
return Number.parseInt(result.trim() || "0", 10) > 0;
|
|
12246
|
+
}
|
|
11397
12247
|
} else if (plat === "win32") {
|
|
11398
12248
|
const winProcesses = getWinProcessNames()[ideId];
|
|
11399
12249
|
if (!winProcesses) return false;
|
|
@@ -11542,7 +12392,8 @@ async function launchWithCdp(options = {}) {
|
|
|
11542
12392
|
await launchLinux(targetIde, port, workspace, options.newWindow);
|
|
11543
12393
|
}
|
|
11544
12394
|
let cdpReady = false;
|
|
11545
|
-
|
|
12395
|
+
const waitDeadline = Date.now() + getCdpStartupTimeoutMs(targetIde.id);
|
|
12396
|
+
while (Date.now() < waitDeadline) {
|
|
11546
12397
|
await new Promise((r) => setTimeout(r, 500));
|
|
11547
12398
|
if (await isCdpActive(port)) {
|
|
11548
12399
|
cdpReady = true;
|
|
@@ -11571,14 +12422,18 @@ async function launchWithCdp(options = {}) {
|
|
|
11571
12422
|
}
|
|
11572
12423
|
async function launchMacOS(ide, port, workspace, newWindow) {
|
|
11573
12424
|
const appName = getMacAppIdentifiers()[ide.id];
|
|
12425
|
+
const preferredMethod = getPreferredLaunchMethod(ide.id, "darwin");
|
|
11574
12426
|
const args = ["--remote-debugging-port=" + port];
|
|
11575
12427
|
if (newWindow) args.push("--new-window");
|
|
11576
12428
|
if (workspace) args.push(workspace);
|
|
11577
|
-
|
|
12429
|
+
const canUseCli = !!ide.cliCommand;
|
|
12430
|
+
const canUseAppLauncher = !!appName;
|
|
12431
|
+
const useAppLauncher = preferredMethod === "app" ? canUseAppLauncher : preferredMethod === "cli" ? false : !canUseCli && canUseAppLauncher;
|
|
12432
|
+
if (!useAppLauncher && ide.cliCommand) {
|
|
12433
|
+
(0, import_child_process6.spawn)(ide.cliCommand, args, { detached: true, stdio: "ignore" }).unref();
|
|
12434
|
+
} else if (appName) {
|
|
11578
12435
|
const openArgs = ["-a", appName, "--args", ...args];
|
|
11579
12436
|
(0, import_child_process6.spawn)("open", openArgs, { detached: true, stdio: "ignore" }).unref();
|
|
11580
|
-
} else if (ide.cliCommand) {
|
|
11581
|
-
(0, import_child_process6.spawn)(ide.cliCommand, args, { detached: true, stdio: "ignore" }).unref();
|
|
11582
12437
|
} else {
|
|
11583
12438
|
throw new Error(`No app identifier or CLI for ${ide.displayName}`);
|
|
11584
12439
|
}
|
|
@@ -11889,6 +12744,7 @@ function buildStatusSnapshot(options) {
|
|
|
11889
12744
|
workspaces: wsState.workspaces,
|
|
11890
12745
|
defaultWorkspaceId: wsState.defaultWorkspaceId,
|
|
11891
12746
|
defaultWorkspacePath: wsState.defaultWorkspacePath,
|
|
12747
|
+
terminalSizingMode: cfg.terminalSizingMode || "measured",
|
|
11892
12748
|
recentLaunches: buildRecentLaunches(recentActivity),
|
|
11893
12749
|
terminalBackend,
|
|
11894
12750
|
availableProviders: buildAvailableProviders(options.providerLoader)
|
|
@@ -12736,19 +13592,10 @@ var ProviderStreamAdapter = class {
|
|
|
12736
13592
|
mode: data.mode,
|
|
12737
13593
|
activeModal: data.activeModal
|
|
12738
13594
|
};
|
|
12739
|
-
|
|
12740
|
-
|
|
12741
|
-
|
|
12742
|
-
|
|
12743
|
-
const val = data[ctrl.readFrom];
|
|
12744
|
-
if (val !== void 0 && val !== null) {
|
|
12745
|
-
cv[ctrl.id] = typeof val === "object" ? val.name || val.id || String(val) : val;
|
|
12746
|
-
}
|
|
12747
|
-
}
|
|
12748
|
-
if (data.model && !cv["model"]) cv["model"] = data.model;
|
|
12749
|
-
if (data.mode && !cv["mode"]) cv["mode"] = data.mode;
|
|
12750
|
-
if (Object.keys(cv).length > 0) state.controlValues = cv;
|
|
12751
|
-
}
|
|
13595
|
+
const controlValues = extractProviderControlValues(this.provider.controls, data);
|
|
13596
|
+
if (controlValues) state.controlValues = controlValues;
|
|
13597
|
+
const effects = normalizeProviderEffects(data);
|
|
13598
|
+
if (effects.length > 0) state.effects = effects;
|
|
12752
13599
|
if (state.messages.length > 0) {
|
|
12753
13600
|
this.lastSuccessState = state;
|
|
12754
13601
|
}
|
|
@@ -13274,6 +14121,8 @@ function forwardAgentStreamsToIdeInstance(instanceManager, ideType, streams) {
|
|
|
13274
14121
|
activeModal: stream.activeModal || null,
|
|
13275
14122
|
model: stream.model || void 0,
|
|
13276
14123
|
mode: stream.mode || void 0,
|
|
14124
|
+
controlValues: stream.controlValues || void 0,
|
|
14125
|
+
effects: stream.effects || void 0,
|
|
13277
14126
|
sessionId: stream.sessionId || stream.instanceId || void 0,
|
|
13278
14127
|
title: stream.title || stream.agentName || void 0,
|
|
13279
14128
|
agentType: stream.agentType || void 0,
|