@adhdev/daemon-core 0.8.22 → 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 +1162 -307
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1162 -307
- package/dist/index.mjs.map +1 -1
- package/dist/providers/cli-provider-instance.d.ts +6 -0
- package/dist/providers/contracts.d.ts +59 -1
- package/dist/providers/control-effects.d.ts +4 -0
- package/dist/providers/extension-provider-instance.d.ts +9 -0
- package/dist/providers/ide-provider-instance.d.ts +8 -0
- package/dist/shared-types.d.ts +2 -1
- package/node_modules/@adhdev/session-host-core/package.json +1 -1
- package/package.json +3 -2
- package/src/agent-stream/forward.ts +2 -0
- package/src/agent-stream/provider-adapter.ts +5 -15
- package/src/agent-stream/types.ts +4 -0
- package/src/cli-adapter-types.ts +3 -0
- package/src/cli-adapters/provider-cli-adapter.ts +399 -49
- package/src/commands/handler.ts +1 -0
- package/src/commands/stream-commands.ts +99 -8
- package/src/config/config.d.ts +1 -0
- package/src/config/config.ts +9 -0
- package/src/launch.ts +57 -11
- package/src/providers/cli-provider-instance.ts +148 -2
- package/src/providers/contracts.ts +65 -2
- package/src/providers/control-effects.ts +114 -0
- package/src/providers/extension-provider-instance.ts +163 -3
- package/src/providers/ide-provider-instance.ts +181 -2
- package/src/shared-types.d.ts +1 -0
- package/src/shared-types.ts +2 -1
- package/src/status/snapshot.ts +1 -0
package/dist/index.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
|
-
|
|
4904
|
+
return effects;
|
|
4905
|
+
}
|
|
4906
|
+
function normalizeControlValue(value) {
|
|
4907
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
4908
|
+
return value;
|
|
4743
4909
|
}
|
|
4744
|
-
|
|
4745
|
-
|
|
4746
|
-
|
|
4747
|
-
return
|
|
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;
|
|
4748
4914
|
}
|
|
4749
|
-
|
|
4750
|
-
|
|
4751
|
-
return title || this.agentName || this.provider.name;
|
|
4752
|
-
}
|
|
4753
|
-
resetStreamState() {
|
|
4754
|
-
if (this.currentStatus !== "idle") {
|
|
4755
|
-
this.detectTransition("idle", {
|
|
4756
|
-
title: this.chatTitle,
|
|
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();
|
|
4776
|
-
}
|
|
4777
|
-
};
|
|
4915
|
+
return String(value);
|
|
4916
|
+
}
|
|
4778
4917
|
|
|
4779
4918
|
// src/config/chat-history.ts
|
|
4780
4919
|
var fs3 = __toESM(require("fs"));
|
|
@@ -5001,54 +5140,383 @@ function listSavedHistorySessions(agentType, options = {}) {
|
|
|
5001
5140
|
files.push(file);
|
|
5002
5141
|
groupedFiles.set(historySessionId, files);
|
|
5003
5142
|
}
|
|
5004
|
-
const summaries = [];
|
|
5005
|
-
for (const [historySessionId, files] of groupedFiles.entries()) {
|
|
5006
|
-
let messageCount = 0;
|
|
5007
|
-
let firstMessageAt = 0;
|
|
5008
|
-
let lastMessageAt = 0;
|
|
5009
|
-
let sessionTitle = "";
|
|
5010
|
-
let preview = "";
|
|
5011
|
-
for (const file of files.sort()) {
|
|
5012
|
-
const filePath = path5.join(dir, file);
|
|
5013
|
-
const content = fs3.readFileSync(filePath, "utf-8");
|
|
5014
|
-
const lines = content.split("\n").filter(Boolean);
|
|
5015
|
-
for (const line of lines) {
|
|
5016
|
-
let parsed = null;
|
|
5017
|
-
try {
|
|
5018
|
-
parsed = JSON.parse(line);
|
|
5019
|
-
} catch {
|
|
5020
|
-
parsed = null;
|
|
5021
|
-
}
|
|
5022
|
-
if (!parsed || parsed.historySessionId !== historySessionId) continue;
|
|
5023
|
-
messageCount += 1;
|
|
5024
|
-
if (!firstMessageAt || parsed.receivedAt < firstMessageAt) firstMessageAt = parsed.receivedAt;
|
|
5025
|
-
if (!lastMessageAt || parsed.receivedAt > lastMessageAt) lastMessageAt = parsed.receivedAt;
|
|
5026
|
-
if (parsed.sessionTitle) sessionTitle = parsed.sessionTitle;
|
|
5027
|
-
if (parsed.role !== "system" && parsed.content.trim()) preview = parsed.content.trim();
|
|
5028
|
-
}
|
|
5143
|
+
const summaries = [];
|
|
5144
|
+
for (const [historySessionId, files] of groupedFiles.entries()) {
|
|
5145
|
+
let messageCount = 0;
|
|
5146
|
+
let firstMessageAt = 0;
|
|
5147
|
+
let lastMessageAt = 0;
|
|
5148
|
+
let sessionTitle = "";
|
|
5149
|
+
let preview = "";
|
|
5150
|
+
for (const file of files.sort()) {
|
|
5151
|
+
const filePath = path5.join(dir, file);
|
|
5152
|
+
const content = fs3.readFileSync(filePath, "utf-8");
|
|
5153
|
+
const lines = content.split("\n").filter(Boolean);
|
|
5154
|
+
for (const line of lines) {
|
|
5155
|
+
let parsed = null;
|
|
5156
|
+
try {
|
|
5157
|
+
parsed = JSON.parse(line);
|
|
5158
|
+
} catch {
|
|
5159
|
+
parsed = null;
|
|
5160
|
+
}
|
|
5161
|
+
if (!parsed || parsed.historySessionId !== historySessionId) continue;
|
|
5162
|
+
messageCount += 1;
|
|
5163
|
+
if (!firstMessageAt || parsed.receivedAt < firstMessageAt) firstMessageAt = parsed.receivedAt;
|
|
5164
|
+
if (!lastMessageAt || parsed.receivedAt > lastMessageAt) lastMessageAt = parsed.receivedAt;
|
|
5165
|
+
if (parsed.sessionTitle) sessionTitle = parsed.sessionTitle;
|
|
5166
|
+
if (parsed.role !== "system" && parsed.content.trim()) preview = parsed.content.trim();
|
|
5167
|
+
}
|
|
5168
|
+
}
|
|
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();
|
|
5029
5471
|
}
|
|
5030
|
-
|
|
5031
|
-
|
|
5032
|
-
|
|
5033
|
-
|
|
5034
|
-
|
|
5035
|
-
|
|
5036
|
-
|
|
5037
|
-
|
|
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 = [];
|
|
@@ -7444,7 +8066,56 @@ function handleSetProviderSetting(h, args) {
|
|
|
7444
8066
|
}
|
|
7445
8067
|
return { success: false, error: `Failed to set ${providerType}.${key} \u2014 invalid key, value, or not a public setting` };
|
|
7446
8068
|
}
|
|
7447
|
-
|
|
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) {
|
|
7448
8119
|
const { agentType, ideType } = args || {};
|
|
7449
8120
|
if (!agentType) return { success: false, error: "agentType is required" };
|
|
7450
8121
|
const loader = h.ctx.providerLoader;
|
|
@@ -7457,13 +8128,29 @@ async function handleExtensionScript(h, args, scriptName) {
|
|
|
7457
8128
|
if (!provider.scripts?.[actualScriptName]) {
|
|
7458
8129
|
return { success: false, error: `Script '${actualScriptName}' not available for ${agentType}` };
|
|
7459
8130
|
}
|
|
7460
|
-
const
|
|
7461
|
-
|
|
7462
|
-
|
|
7463
|
-
if (
|
|
7464
|
-
|
|
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}` };
|
|
7465
8151
|
}
|
|
7466
8152
|
}
|
|
8153
|
+
const scriptFn = provider.scripts[actualScriptName];
|
|
7467
8154
|
const scriptCode = scriptFn(normalizedArgs);
|
|
7468
8155
|
if (!scriptCode) return { success: false, error: `Script '${actualScriptName}' returned null` };
|
|
7469
8156
|
const cdpKey = provider.category === "ide" ? h.currentSession?.cdpManagerKey || h.currentManagerKey || agentType : h.currentSession?.cdpManagerKey || h.currentManagerKey || ideType;
|
|
@@ -7517,16 +8204,29 @@ async function handleExtensionScript(h, args, scriptName) {
|
|
|
7517
8204
|
if (typeof result === "string") {
|
|
7518
8205
|
try {
|
|
7519
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
|
+
}
|
|
7520
8211
|
return { success: true, ...parsed };
|
|
7521
8212
|
} catch {
|
|
7522
8213
|
return { success: true, result };
|
|
7523
8214
|
}
|
|
7524
8215
|
}
|
|
8216
|
+
applyProviderPatch(h, args, result);
|
|
7525
8217
|
return { success: true, result };
|
|
7526
8218
|
} catch (e) {
|
|
7527
8219
|
return { success: false, error: `Script execution failed: ${e.message}` };
|
|
7528
8220
|
}
|
|
7529
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
|
+
}
|
|
7530
8230
|
function handleGetIdeExtensions(h, args) {
|
|
7531
8231
|
const { ideType } = args || {};
|
|
7532
8232
|
const loader = h.ctx.providerLoader;
|
|
@@ -8026,6 +8726,8 @@ var DaemonCommandHandler = class {
|
|
|
8026
8726
|
case "set_ide_extension":
|
|
8027
8727
|
return handleSetIdeExtension(this, args);
|
|
8028
8728
|
// ─── Extension Model / Mode Control (stream-commands.ts) ──────────
|
|
8729
|
+
case "invoke_provider_script":
|
|
8730
|
+
return handleProviderScript(this, args);
|
|
8029
8731
|
case "list_extension_models":
|
|
8030
8732
|
return handleExtensionScript(this, args, "listModels");
|
|
8031
8733
|
case "set_extension_model":
|
|
@@ -8202,6 +8904,8 @@ var CliProviderInstance = class {
|
|
|
8202
8904
|
generatingDebounceTimer = null;
|
|
8203
8905
|
generatingDebouncePending = null;
|
|
8204
8906
|
lastApprovalEventAt = 0;
|
|
8907
|
+
controlValues = {};
|
|
8908
|
+
appliedEffectKeys = /* @__PURE__ */ new Set();
|
|
8205
8909
|
historyWriter;
|
|
8206
8910
|
runtimeMessages = [];
|
|
8207
8911
|
instanceId;
|
|
@@ -8214,6 +8918,7 @@ var CliProviderInstance = class {
|
|
|
8214
8918
|
async init(context) {
|
|
8215
8919
|
this.context = context;
|
|
8216
8920
|
this.settings = context.settings || {};
|
|
8921
|
+
this.adapter.updateRuntimeSettings?.(this.settings);
|
|
8217
8922
|
this.monitor.updateConfig({
|
|
8218
8923
|
approvalAlert: this.settings.approvalAlert !== false,
|
|
8219
8924
|
longGeneratingAlert: this.settings.longGeneratingAlert !== false,
|
|
@@ -8229,6 +8934,21 @@ var CliProviderInstance = class {
|
|
|
8229
8934
|
this.detectStatusTransition();
|
|
8230
8935
|
});
|
|
8231
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
|
+
}
|
|
8232
8952
|
if (this.providerSessionId && this.launchMode === "resume") {
|
|
8233
8953
|
const resumedAt = Date.now();
|
|
8234
8954
|
this.historyWriter.appendSystemMarker(
|
|
@@ -8309,6 +9029,12 @@ var CliProviderInstance = class {
|
|
|
8309
9029
|
}
|
|
8310
9030
|
const runtime = this.adapter.getRuntimeMetadata();
|
|
8311
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
|
+
}
|
|
8312
9038
|
const mergedMessages = this.mergeConversationMessages(parsedMessages);
|
|
8313
9039
|
const dirName = this.workingDir.split("/").filter(Boolean).pop() || "session";
|
|
8314
9040
|
if (parsedMessages.length > 0) {
|
|
@@ -8329,6 +9055,7 @@ var CliProviderInstance = class {
|
|
|
8329
9055
|
);
|
|
8330
9056
|
}
|
|
8331
9057
|
}
|
|
9058
|
+
this.applyProviderResponse(parsedStatus, { phase: "immediate" });
|
|
8332
9059
|
return {
|
|
8333
9060
|
type: this.type,
|
|
8334
9061
|
name: this.provider.name,
|
|
@@ -8358,8 +9085,7 @@ var CliProviderInstance = class {
|
|
|
8358
9085
|
attachedClients: runtime.attachedClients || []
|
|
8359
9086
|
} : void 0,
|
|
8360
9087
|
resume: this.provider.resume,
|
|
8361
|
-
controlValues:
|
|
8362
|
-
// CLI controls not yet wired from stream
|
|
9088
|
+
controlValues: this.controlValues,
|
|
8363
9089
|
providerControls: this.provider.controls
|
|
8364
9090
|
};
|
|
8365
9091
|
}
|
|
@@ -8370,6 +9096,15 @@ var CliProviderInstance = class {
|
|
|
8370
9096
|
getPresentationMode() {
|
|
8371
9097
|
return this.presentationMode;
|
|
8372
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
|
+
}
|
|
8373
9108
|
onEvent(event, data) {
|
|
8374
9109
|
if (event === "send_message" && data?.text) {
|
|
8375
9110
|
void this.adapter.sendMessage(data.text).catch((e) => {
|
|
@@ -8381,22 +9116,27 @@ var CliProviderInstance = class {
|
|
|
8381
9116
|
void this.adapter.resolveAction(data).catch((e) => {
|
|
8382
9117
|
LOG.warn("CLI", `[${this.type}] resolve_action failed: ${e?.message || e}`);
|
|
8383
9118
|
});
|
|
9119
|
+
} else if (event === "provider_state_patch" && data && typeof data === "object") {
|
|
9120
|
+
this.applyProviderResponse(data, { phase: "immediate" });
|
|
8384
9121
|
}
|
|
8385
9122
|
}
|
|
8386
9123
|
dispose() {
|
|
8387
9124
|
this.adapter.shutdown();
|
|
8388
9125
|
this.monitor.reset();
|
|
9126
|
+
this.appliedEffectKeys.clear();
|
|
8389
9127
|
}
|
|
8390
9128
|
completedDebounceTimer = null;
|
|
8391
9129
|
completedDebouncePending = null;
|
|
8392
9130
|
detectStatusTransition() {
|
|
8393
9131
|
const now = Date.now();
|
|
8394
9132
|
const adapterStatus = this.adapter.getStatus();
|
|
9133
|
+
const parsedStatus = this.adapter.getScriptParsedStatus?.() || null;
|
|
8395
9134
|
const newStatus = adapterStatus.status;
|
|
8396
9135
|
const dirName = this.workingDir.split("/").filter(Boolean).pop() || "session";
|
|
8397
9136
|
const chatTitle = `${this.provider.name} \xB7 ${dirName}`;
|
|
8398
9137
|
const partial = this.adapter.getPartialResponse();
|
|
8399
9138
|
const progressFingerprint = newStatus === "generating" ? `${partial || ""}::${adapterStatus.messages.at(-1)?.content || ""}`.slice(-2e3) : void 0;
|
|
9139
|
+
const previousStatus = this.lastStatus;
|
|
8400
9140
|
if (newStatus !== this.lastStatus) {
|
|
8401
9141
|
LOG.info("CLI", `[${this.type}] status: ${this.lastStatus} \u2192 ${newStatus}`);
|
|
8402
9142
|
if (this.lastStatus === "idle" && newStatus === "generating") {
|
|
@@ -8489,6 +9229,9 @@ var CliProviderInstance = class {
|
|
|
8489
9229
|
}
|
|
8490
9230
|
this.lastStatus = newStatus;
|
|
8491
9231
|
}
|
|
9232
|
+
this.applyProviderResponse(parsedStatus, {
|
|
9233
|
+
phase: newStatus === "idle" && (previousStatus === "generating" || previousStatus === "waiting_approval") ? "turn_completed" : "immediate"
|
|
9234
|
+
});
|
|
8492
9235
|
const agentKey = `${this.type}:cli`;
|
|
8493
9236
|
const monitorEvents = this.monitor.check(agentKey, newStatus, now, progressFingerprint);
|
|
8494
9237
|
for (const me of monitorEvents) {
|
|
@@ -8504,6 +9247,88 @@ var CliProviderInstance = class {
|
|
|
8504
9247
|
this.events = [];
|
|
8505
9248
|
return events;
|
|
8506
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
|
+
}
|
|
8507
9332
|
// ─── Adapter access (backward compat) ──────────────────
|
|
8508
9333
|
getAdapter() {
|
|
8509
9334
|
return this.adapter;
|
|
@@ -11276,6 +12101,22 @@ function getMacAppIdentifiers() {
|
|
|
11276
12101
|
function getWinProcessNames() {
|
|
11277
12102
|
return getProviderLoader().getWinProcessNames();
|
|
11278
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
|
+
}
|
|
11279
12120
|
async function findFreePort(ports) {
|
|
11280
12121
|
for (const port2 of ports) {
|
|
11281
12122
|
const free = await checkPortFree(port2);
|
|
@@ -11328,12 +12169,12 @@ async function killIdeProcess(ideId) {
|
|
|
11328
12169
|
try {
|
|
11329
12170
|
if (plat === "darwin" && appName) {
|
|
11330
12171
|
try {
|
|
11331
|
-
(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`, {
|
|
11332
12173
|
timeout: 5e3
|
|
11333
12174
|
});
|
|
11334
12175
|
} catch {
|
|
11335
12176
|
try {
|
|
11336
|
-
(0, import_child_process6.execSync)(`pkill -
|
|
12177
|
+
(0, import_child_process6.execSync)(`pkill -x "${appName}" 2>/dev/null`, { timeout: 5e3 });
|
|
11337
12178
|
} catch {
|
|
11338
12179
|
}
|
|
11339
12180
|
}
|
|
@@ -11363,7 +12204,7 @@ async function killIdeProcess(ideId) {
|
|
|
11363
12204
|
}
|
|
11364
12205
|
if (plat === "darwin" && appName) {
|
|
11365
12206
|
try {
|
|
11366
|
-
(0, import_child_process6.execSync)(`pkill -9 -
|
|
12207
|
+
(0, import_child_process6.execSync)(`pkill -9 -x "${appName}" 2>/dev/null`, { timeout: 5e3 });
|
|
11367
12208
|
} catch {
|
|
11368
12209
|
}
|
|
11369
12210
|
} else if (plat === "win32" && winProcesses) {
|
|
@@ -11386,8 +12227,23 @@ function isIdeRunning(ideId) {
|
|
|
11386
12227
|
if (plat === "darwin") {
|
|
11387
12228
|
const appName = getMacAppIdentifiers()[ideId];
|
|
11388
12229
|
if (!appName) return false;
|
|
11389
|
-
|
|
11390
|
-
|
|
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
|
+
}
|
|
11391
12247
|
} else if (plat === "win32") {
|
|
11392
12248
|
const winProcesses = getWinProcessNames()[ideId];
|
|
11393
12249
|
if (!winProcesses) return false;
|
|
@@ -11536,7 +12392,8 @@ async function launchWithCdp(options = {}) {
|
|
|
11536
12392
|
await launchLinux(targetIde, port, workspace, options.newWindow);
|
|
11537
12393
|
}
|
|
11538
12394
|
let cdpReady = false;
|
|
11539
|
-
|
|
12395
|
+
const waitDeadline = Date.now() + getCdpStartupTimeoutMs(targetIde.id);
|
|
12396
|
+
while (Date.now() < waitDeadline) {
|
|
11540
12397
|
await new Promise((r) => setTimeout(r, 500));
|
|
11541
12398
|
if (await isCdpActive(port)) {
|
|
11542
12399
|
cdpReady = true;
|
|
@@ -11565,14 +12422,18 @@ async function launchWithCdp(options = {}) {
|
|
|
11565
12422
|
}
|
|
11566
12423
|
async function launchMacOS(ide, port, workspace, newWindow) {
|
|
11567
12424
|
const appName = getMacAppIdentifiers()[ide.id];
|
|
12425
|
+
const preferredMethod = getPreferredLaunchMethod(ide.id, "darwin");
|
|
11568
12426
|
const args = ["--remote-debugging-port=" + port];
|
|
11569
12427
|
if (newWindow) args.push("--new-window");
|
|
11570
12428
|
if (workspace) args.push(workspace);
|
|
11571
|
-
|
|
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) {
|
|
11572
12435
|
const openArgs = ["-a", appName, "--args", ...args];
|
|
11573
12436
|
(0, import_child_process6.spawn)("open", openArgs, { detached: true, stdio: "ignore" }).unref();
|
|
11574
|
-
} else if (ide.cliCommand) {
|
|
11575
|
-
(0, import_child_process6.spawn)(ide.cliCommand, args, { detached: true, stdio: "ignore" }).unref();
|
|
11576
12437
|
} else {
|
|
11577
12438
|
throw new Error(`No app identifier or CLI for ${ide.displayName}`);
|
|
11578
12439
|
}
|
|
@@ -11883,6 +12744,7 @@ function buildStatusSnapshot(options) {
|
|
|
11883
12744
|
workspaces: wsState.workspaces,
|
|
11884
12745
|
defaultWorkspaceId: wsState.defaultWorkspaceId,
|
|
11885
12746
|
defaultWorkspacePath: wsState.defaultWorkspacePath,
|
|
12747
|
+
terminalSizingMode: cfg.terminalSizingMode || "measured",
|
|
11886
12748
|
recentLaunches: buildRecentLaunches(recentActivity),
|
|
11887
12749
|
terminalBackend,
|
|
11888
12750
|
availableProviders: buildAvailableProviders(options.providerLoader)
|
|
@@ -12730,19 +13592,10 @@ var ProviderStreamAdapter = class {
|
|
|
12730
13592
|
mode: data.mode,
|
|
12731
13593
|
activeModal: data.activeModal
|
|
12732
13594
|
};
|
|
12733
|
-
|
|
12734
|
-
|
|
12735
|
-
|
|
12736
|
-
|
|
12737
|
-
const val = data[ctrl.readFrom];
|
|
12738
|
-
if (val !== void 0 && val !== null) {
|
|
12739
|
-
cv[ctrl.id] = typeof val === "object" ? val.name || val.id || String(val) : val;
|
|
12740
|
-
}
|
|
12741
|
-
}
|
|
12742
|
-
if (data.model && !cv["model"]) cv["model"] = data.model;
|
|
12743
|
-
if (data.mode && !cv["mode"]) cv["mode"] = data.mode;
|
|
12744
|
-
if (Object.keys(cv).length > 0) state.controlValues = cv;
|
|
12745
|
-
}
|
|
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;
|
|
12746
13599
|
if (state.messages.length > 0) {
|
|
12747
13600
|
this.lastSuccessState = state;
|
|
12748
13601
|
}
|
|
@@ -13268,6 +14121,8 @@ function forwardAgentStreamsToIdeInstance(instanceManager, ideType, streams) {
|
|
|
13268
14121
|
activeModal: stream.activeModal || null,
|
|
13269
14122
|
model: stream.model || void 0,
|
|
13270
14123
|
mode: stream.mode || void 0,
|
|
14124
|
+
controlValues: stream.controlValues || void 0,
|
|
14125
|
+
effects: stream.effects || void 0,
|
|
13271
14126
|
sessionId: stream.sessionId || stream.instanceId || void 0,
|
|
13272
14127
|
title: stream.title || stream.agentName || void 0,
|
|
13273
14128
|
agentType: stream.agentType || void 0,
|