@botiverse/raft-daemon 0.61.1 → 0.62.0
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/{chunk-5JCAPBBM.js → chunk-H3G23UPM.js} +1884 -718
- package/dist/cli/index.js +22 -2
- package/dist/core.js +1 -1
- package/dist/{dist-KVBO6CH7.js → dist-DSRBN3VD.js} +22 -2
- package/dist/index.js +1 -1
- package/package.json +3 -1
|
@@ -1288,6 +1288,9 @@ var SERVER_CAPABILITY_MATRIX = {
|
|
|
1288
1288
|
|
|
1289
1289
|
// ../shared/src/index.ts
|
|
1290
1290
|
var RUNTIME_CONFIG_VERSION = 1;
|
|
1291
|
+
var PI_BUILTIN_PROVIDER_ENV_KEYS = {
|
|
1292
|
+
deepseek: "DEEPSEEK_API_KEY"
|
|
1293
|
+
};
|
|
1291
1294
|
var AGENT_ACTIVITIES = ["online", "thinking", "working", "error", "offline"];
|
|
1292
1295
|
var isAgentActivity = makeIsMember(AGENT_ACTIVITIES);
|
|
1293
1296
|
var VALID_ACTIVITIES = new Set(AGENT_ACTIVITIES);
|
|
@@ -1392,7 +1395,12 @@ function getDefaultModel(runtimeId) {
|
|
|
1392
1395
|
return models?.[0]?.id ?? "sonnet";
|
|
1393
1396
|
}
|
|
1394
1397
|
var CONTROLLED_RUNTIME_ENV_KEYS = {
|
|
1395
|
-
claude: ["ANTHROPIC_BASE_URL", "ANTHROPIC_API_KEY", "ANTHROPIC_CUSTOM_MODEL_OPTION"]
|
|
1398
|
+
claude: ["ANTHROPIC_BASE_URL", "ANTHROPIC_API_KEY", "ANTHROPIC_CUSTOM_MODEL_OPTION"],
|
|
1399
|
+
// Pi-runtime builtin-provider env vars (e.g. DEEPSEEK_API_KEY). Owned by
|
|
1400
|
+
// RuntimeProviderConfig.pi-builtin → runtimeConfigToLaunchFields, not by
|
|
1401
|
+
// user-supplied envVars: reading from PI_BUILTIN_PROVIDER_ENV_KEYS keeps
|
|
1402
|
+
// this list in sync as new providers are added.
|
|
1403
|
+
pi: Object.values(PI_BUILTIN_PROVIDER_ENV_KEYS)
|
|
1396
1404
|
};
|
|
1397
1405
|
function isPlainRecord(value) {
|
|
1398
1406
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
@@ -1426,15 +1434,24 @@ function modelConfigFromLegacy(runtime, model) {
|
|
|
1426
1434
|
return isPresetRuntimeModel(runtime, model) ? { kind: "preset", id: model } : { kind: "custom", name: model };
|
|
1427
1435
|
}
|
|
1428
1436
|
function parseProviderConfig(runtime, value, legacyApiUrl, legacyApiKey) {
|
|
1429
|
-
if (runtime
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1437
|
+
if (runtime === "claude") {
|
|
1438
|
+
if (!isPlainRecord(value)) return { kind: "default" };
|
|
1439
|
+
if (value.kind === "custom" && typeof value.apiUrl === "string" && value.apiUrl.trim() && typeof value.apiKey === "string" && value.apiKey.trim()) {
|
|
1440
|
+
return { kind: "custom", apiUrl: value.apiUrl.trim(), apiKey: value.apiKey.trim() };
|
|
1441
|
+
}
|
|
1442
|
+
if (value.kind === "custom" && legacyApiUrl?.trim() && legacyApiKey?.trim()) {
|
|
1443
|
+
return { kind: "custom", apiUrl: legacyApiUrl.trim(), apiKey: legacyApiKey.trim() };
|
|
1444
|
+
}
|
|
1445
|
+
return { kind: "default" };
|
|
1433
1446
|
}
|
|
1434
|
-
if (
|
|
1435
|
-
return { kind: "
|
|
1447
|
+
if (runtime === "pi") {
|
|
1448
|
+
if (!isPlainRecord(value)) return { kind: "default" };
|
|
1449
|
+
if (value.kind === "pi-builtin" && typeof value.providerId === "string" && value.providerId.trim() && typeof value.apiKey === "string" && value.apiKey.trim() && Object.prototype.hasOwnProperty.call(PI_BUILTIN_PROVIDER_ENV_KEYS, value.providerId.trim())) {
|
|
1450
|
+
return { kind: "pi-builtin", providerId: value.providerId.trim(), apiKey: value.apiKey.trim() };
|
|
1451
|
+
}
|
|
1452
|
+
return { kind: "default" };
|
|
1436
1453
|
}
|
|
1437
|
-
return
|
|
1454
|
+
return void 0;
|
|
1438
1455
|
}
|
|
1439
1456
|
function parseModelConfig(runtime, value, fallback, provider, legacyCustomModel) {
|
|
1440
1457
|
if (!isPlainRecord(value)) return modelConfigFromLegacy(runtime, fallback);
|
|
@@ -1496,6 +1513,10 @@ function runtimeConfigToLaunchFields(config) {
|
|
|
1496
1513
|
generatedEnvVars.ANTHROPIC_CUSTOM_MODEL_OPTION = normalized.model.name;
|
|
1497
1514
|
}
|
|
1498
1515
|
}
|
|
1516
|
+
if (normalized.runtime === "pi" && normalized.provider?.kind === "pi-builtin") {
|
|
1517
|
+
const envKey = PI_BUILTIN_PROVIDER_ENV_KEYS[normalized.provider.providerId];
|
|
1518
|
+
if (envKey) generatedEnvVars[envKey] = normalized.provider.apiKey;
|
|
1519
|
+
}
|
|
1499
1520
|
const envVars = {
|
|
1500
1521
|
...normalized.envVars ?? {},
|
|
1501
1522
|
...generatedEnvVars
|
|
@@ -1512,7 +1533,7 @@ function runtimeConfigToLaunchFields(config) {
|
|
|
1512
1533
|
var PLAN_CONFIG = {
|
|
1513
1534
|
free: {
|
|
1514
1535
|
displayName: "Free",
|
|
1515
|
-
limits: { maxMachines:
|
|
1536
|
+
limits: { maxMachines: -1, maxAgents: -1, maxChannels: -1, messageHistoryDays: 30, includedAgents: -1 },
|
|
1516
1537
|
comingSoon: false,
|
|
1517
1538
|
price: 0,
|
|
1518
1539
|
extraAgentPrice: 0,
|
|
@@ -1532,13 +1553,20 @@ var PLAN_CONFIG = {
|
|
|
1532
1553
|
comingSoon: false,
|
|
1533
1554
|
price: 0,
|
|
1534
1555
|
extraAgentPrice: 0
|
|
1556
|
+
},
|
|
1557
|
+
pro: {
|
|
1558
|
+
displayName: "Pro",
|
|
1559
|
+
limits: { maxMachines: -1, maxAgents: 10, maxChannels: -1, messageHistoryDays: -1, includedAgents: 10 },
|
|
1560
|
+
comingSoon: false,
|
|
1561
|
+
price: 20,
|
|
1562
|
+
extraAgentPrice: 0
|
|
1535
1563
|
}
|
|
1536
1564
|
};
|
|
1537
1565
|
var DISPLAY_PLAN_CONFIG = {
|
|
1538
1566
|
free: PLAN_CONFIG.free,
|
|
1539
1567
|
pro: {
|
|
1540
1568
|
displayName: "Pro",
|
|
1541
|
-
limits: { maxMachines: -1, maxAgents:
|
|
1569
|
+
limits: { maxMachines: -1, maxAgents: -1, maxChannels: -1, messageHistoryDays: -1, includedAgents: -1 },
|
|
1542
1570
|
comingSoon: false,
|
|
1543
1571
|
price: 20,
|
|
1544
1572
|
priceCadence: "/ seat pack / month",
|
|
@@ -1568,14 +1596,406 @@ var DISPLAY_PLAN_CONFIG = {
|
|
|
1568
1596
|
]
|
|
1569
1597
|
}
|
|
1570
1598
|
};
|
|
1599
|
+
var FREE_MONTHLY_FILE_UPLOAD_LIMIT_BYTES = 100 * 1024 * 1024;
|
|
1571
1600
|
|
|
1572
1601
|
// src/agentProcessManager.ts
|
|
1573
|
-
import { mkdirSync as mkdirSync5, readdirSync as readdirSync3, statSync, writeFileSync as writeFileSync4 } from "fs";
|
|
1574
|
-
import { mkdir, writeFile, access, readdir as readdir2, stat as stat2, readFile, rm as rm2 } from "fs/promises";
|
|
1575
|
-
import { createHash as createHash3 } from "crypto";
|
|
1602
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync5, readFileSync as readFileSync6, readdirSync as readdirSync3, statSync, writeFileSync as writeFileSync4 } from "fs";
|
|
1603
|
+
import { mkdir, writeFile, access, readdir as readdir2, stat as stat2, readFile, rm as rm2, lstat, realpath, open } from "fs/promises";
|
|
1604
|
+
import { createHash as createHash3, randomUUID as randomUUID5 } from "crypto";
|
|
1576
1605
|
import path13 from "path";
|
|
1606
|
+
import { gzipSync } from "zlib";
|
|
1577
1607
|
import os6 from "os";
|
|
1578
1608
|
|
|
1609
|
+
// src/proxy.ts
|
|
1610
|
+
import { HttpsProxyAgent } from "https-proxy-agent";
|
|
1611
|
+
import { ProxyAgent } from "undici";
|
|
1612
|
+
var fetchDispatcherCache = /* @__PURE__ */ new Map();
|
|
1613
|
+
function getFetchPreResponseTimeoutMs(env) {
|
|
1614
|
+
const parsed = Number.parseInt(env.SLOCK_DAEMON_FETCH_PRE_RESPONSE_TIMEOUT_MS || "", 10);
|
|
1615
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : 3e4;
|
|
1616
|
+
}
|
|
1617
|
+
function getDefaultPort(protocol) {
|
|
1618
|
+
switch (protocol) {
|
|
1619
|
+
case "https:":
|
|
1620
|
+
case "wss:":
|
|
1621
|
+
return "443";
|
|
1622
|
+
case "http:":
|
|
1623
|
+
case "ws:":
|
|
1624
|
+
return "80";
|
|
1625
|
+
default:
|
|
1626
|
+
return "";
|
|
1627
|
+
}
|
|
1628
|
+
}
|
|
1629
|
+
function hostMatchesNoProxyEntry(hostname, ruleHost) {
|
|
1630
|
+
if (!ruleHost) return false;
|
|
1631
|
+
const normalizedRule = ruleHost.replace(/^\*\./, ".").replace(/^\./, "").toLowerCase();
|
|
1632
|
+
const normalizedHost = hostname.toLowerCase();
|
|
1633
|
+
return normalizedHost === normalizedRule || normalizedHost.endsWith(`.${normalizedRule}`);
|
|
1634
|
+
}
|
|
1635
|
+
function getProxyUrlForTarget(targetUrl, env) {
|
|
1636
|
+
const protocol = new URL(targetUrl).protocol;
|
|
1637
|
+
switch (protocol) {
|
|
1638
|
+
case "wss:":
|
|
1639
|
+
return env.WSS_PROXY || env.wss_proxy || env.HTTPS_PROXY || env.https_proxy || env.ALL_PROXY || env.all_proxy;
|
|
1640
|
+
case "ws:":
|
|
1641
|
+
return env.WS_PROXY || env.ws_proxy || env.HTTP_PROXY || env.http_proxy || env.ALL_PROXY || env.all_proxy;
|
|
1642
|
+
case "https:":
|
|
1643
|
+
return env.HTTPS_PROXY || env.https_proxy || env.ALL_PROXY || env.all_proxy;
|
|
1644
|
+
case "http:":
|
|
1645
|
+
return env.HTTP_PROXY || env.http_proxy || env.ALL_PROXY || env.all_proxy;
|
|
1646
|
+
default:
|
|
1647
|
+
return env.ALL_PROXY || env.all_proxy;
|
|
1648
|
+
}
|
|
1649
|
+
}
|
|
1650
|
+
function shouldBypassProxy(targetUrl, env) {
|
|
1651
|
+
const rawNoProxy = env.NO_PROXY || env.no_proxy;
|
|
1652
|
+
if (!rawNoProxy) return false;
|
|
1653
|
+
const url = new URL(targetUrl);
|
|
1654
|
+
const hostname = url.hostname.toLowerCase();
|
|
1655
|
+
const port = url.port || getDefaultPort(url.protocol);
|
|
1656
|
+
return rawNoProxy.split(",").map((entry) => entry.trim()).filter(Boolean).some((entry) => {
|
|
1657
|
+
if (entry === "*") return true;
|
|
1658
|
+
const [ruleHost, rulePort] = entry.split(":", 2);
|
|
1659
|
+
if (rulePort && rulePort !== port) return false;
|
|
1660
|
+
return hostMatchesNoProxyEntry(hostname, ruleHost);
|
|
1661
|
+
});
|
|
1662
|
+
}
|
|
1663
|
+
function buildWebSocketOptions(wsUrl, env) {
|
|
1664
|
+
const proxyUrl = getProxyUrlForTarget(wsUrl, env);
|
|
1665
|
+
if (!proxyUrl) return void 0;
|
|
1666
|
+
if (shouldBypassProxy(wsUrl, env)) return void 0;
|
|
1667
|
+
return {
|
|
1668
|
+
agent: new HttpsProxyAgent(proxyUrl)
|
|
1669
|
+
};
|
|
1670
|
+
}
|
|
1671
|
+
function resolveProxyUrl(targetUrl, env) {
|
|
1672
|
+
const proxyUrl = getProxyUrlForTarget(targetUrl, env);
|
|
1673
|
+
if (!proxyUrl) return void 0;
|
|
1674
|
+
if (shouldBypassProxy(targetUrl, env)) return void 0;
|
|
1675
|
+
return proxyUrl;
|
|
1676
|
+
}
|
|
1677
|
+
function buildFetchDispatcher(targetUrl, env) {
|
|
1678
|
+
const proxyUrl = resolveProxyUrl(targetUrl, env);
|
|
1679
|
+
if (!proxyUrl) return void 0;
|
|
1680
|
+
const cached = fetchDispatcherCache.get(proxyUrl);
|
|
1681
|
+
if (cached) return cached;
|
|
1682
|
+
const timeoutMs = getFetchPreResponseTimeoutMs(env);
|
|
1683
|
+
const dispatcher = new ProxyAgent({
|
|
1684
|
+
uri: proxyUrl,
|
|
1685
|
+
// All three are pre-response and body-agnostic (see getFetchPreResponseTimeoutMs):
|
|
1686
|
+
// headersTimeout = headers-hang leg; requestTls.timeout = CONNECT-tunnel
|
|
1687
|
+
// establish leg; connect.timeout = socket to the proxy itself (defensive).
|
|
1688
|
+
connect: { timeout: timeoutMs },
|
|
1689
|
+
requestTls: { timeout: timeoutMs },
|
|
1690
|
+
headersTimeout: timeoutMs
|
|
1691
|
+
});
|
|
1692
|
+
fetchDispatcherCache.set(proxyUrl, dispatcher);
|
|
1693
|
+
return dispatcher;
|
|
1694
|
+
}
|
|
1695
|
+
function evictFetchDispatcher(targetUrl, env) {
|
|
1696
|
+
const proxyUrl = resolveProxyUrl(targetUrl, env);
|
|
1697
|
+
if (!proxyUrl) return false;
|
|
1698
|
+
const cached = fetchDispatcherCache.get(proxyUrl);
|
|
1699
|
+
if (!cached) return false;
|
|
1700
|
+
fetchDispatcherCache.delete(proxyUrl);
|
|
1701
|
+
void Promise.resolve().then(() => cached.close()).catch(() => cached.destroy?.(new Error("evicted"))).catch(() => {
|
|
1702
|
+
});
|
|
1703
|
+
return true;
|
|
1704
|
+
}
|
|
1705
|
+
|
|
1706
|
+
// src/daemonFetch.ts
|
|
1707
|
+
function withDaemonFetchProxy(input, init = {}, env = process.env) {
|
|
1708
|
+
const dispatcher = buildFetchDispatcher(input.toString(), env);
|
|
1709
|
+
return dispatcher ? { ...init, dispatcher } : init;
|
|
1710
|
+
}
|
|
1711
|
+
async function daemonFetch(input, init, env = process.env) {
|
|
1712
|
+
try {
|
|
1713
|
+
return await fetch(input, withDaemonFetchProxy(input, init, env));
|
|
1714
|
+
} catch (err) {
|
|
1715
|
+
evictFetchDispatcher(input.toString(), env);
|
|
1716
|
+
throw err;
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1719
|
+
|
|
1720
|
+
// src/attachmentFormatting.ts
|
|
1721
|
+
function attachmentDownloadHint(style = "slock_cli") {
|
|
1722
|
+
switch (style) {
|
|
1723
|
+
case "slock_cli":
|
|
1724
|
+
return "use `raft attachment view --id <attachmentId> --output <path>` to download";
|
|
1725
|
+
case "mcp_tool":
|
|
1726
|
+
return "use view_file to download";
|
|
1727
|
+
}
|
|
1728
|
+
}
|
|
1729
|
+
function formatAttachmentSuffix(attachments, style = "slock_cli") {
|
|
1730
|
+
if (!attachments?.length) return "";
|
|
1731
|
+
const attachmentList = attachments.map((attachment) => `${attachment.filename} (id:${attachment.id})`).join(", ");
|
|
1732
|
+
return ` [${attachments.length} attachment${attachments.length > 1 ? "s" : ""}: ${attachmentList} \u2014 ${attachmentDownloadHint(style)}]`;
|
|
1733
|
+
}
|
|
1734
|
+
|
|
1735
|
+
// src/logger.ts
|
|
1736
|
+
var listeners = /* @__PURE__ */ new Set();
|
|
1737
|
+
function timestamp() {
|
|
1738
|
+
const d = /* @__PURE__ */ new Date();
|
|
1739
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
1740
|
+
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
|
|
1741
|
+
}
|
|
1742
|
+
function format(level, msg) {
|
|
1743
|
+
return `${timestamp()} [${level}] ${msg}`;
|
|
1744
|
+
}
|
|
1745
|
+
function emit(event) {
|
|
1746
|
+
for (const listener of listeners) {
|
|
1747
|
+
listener(event);
|
|
1748
|
+
}
|
|
1749
|
+
}
|
|
1750
|
+
function subscribeDaemonLogs(listener) {
|
|
1751
|
+
listeners.add(listener);
|
|
1752
|
+
return () => {
|
|
1753
|
+
listeners.delete(listener);
|
|
1754
|
+
};
|
|
1755
|
+
}
|
|
1756
|
+
var logger = {
|
|
1757
|
+
info(msg) {
|
|
1758
|
+
const line = format("INFO", msg);
|
|
1759
|
+
console.log(line);
|
|
1760
|
+
emit({ level: "INFO", line, message: msg });
|
|
1761
|
+
},
|
|
1762
|
+
warn(msg) {
|
|
1763
|
+
const line = format("WARN", msg);
|
|
1764
|
+
console.warn(line);
|
|
1765
|
+
emit({ level: "WARN", line, message: msg });
|
|
1766
|
+
},
|
|
1767
|
+
error(msg, err) {
|
|
1768
|
+
const line = format("ERROR", msg);
|
|
1769
|
+
if (err) {
|
|
1770
|
+
console.error(line, err);
|
|
1771
|
+
} else {
|
|
1772
|
+
console.error(line);
|
|
1773
|
+
}
|
|
1774
|
+
emit({ level: "ERROR", line, message: msg, error: err });
|
|
1775
|
+
}
|
|
1776
|
+
};
|
|
1777
|
+
|
|
1778
|
+
// src/chatBridgeRequest.ts
|
|
1779
|
+
var DEFAULT_CHAT_BRIDGE_TOOL_TIMEOUT_MS = Number.parseInt(
|
|
1780
|
+
process.env.SLOCK_CHAT_BRIDGE_TOOL_TIMEOUT_MS || "",
|
|
1781
|
+
10
|
|
1782
|
+
) || 6e4;
|
|
1783
|
+
var ChatBridgeToolTimeoutError = class extends Error {
|
|
1784
|
+
toolName;
|
|
1785
|
+
target;
|
|
1786
|
+
timeoutMs;
|
|
1787
|
+
durationMs;
|
|
1788
|
+
constructor(toolName, target, timeoutMs, durationMs) {
|
|
1789
|
+
super(`${toolName} timed out after ${timeoutMs}ms${target ? ` (target: ${target})` : ""}`);
|
|
1790
|
+
this.name = "ChatBridgeToolTimeoutError";
|
|
1791
|
+
this.toolName = toolName;
|
|
1792
|
+
this.target = target;
|
|
1793
|
+
this.timeoutMs = timeoutMs;
|
|
1794
|
+
this.durationMs = durationMs;
|
|
1795
|
+
}
|
|
1796
|
+
};
|
|
1797
|
+
function describeError(err) {
|
|
1798
|
+
if (err instanceof Error) return `${err.name}: ${err.message}`;
|
|
1799
|
+
return String(err);
|
|
1800
|
+
}
|
|
1801
|
+
async function executeJsonRequest(url, init, {
|
|
1802
|
+
toolName,
|
|
1803
|
+
target = null,
|
|
1804
|
+
timeoutMs = DEFAULT_CHAT_BRIDGE_TOOL_TIMEOUT_MS,
|
|
1805
|
+
fetchImpl,
|
|
1806
|
+
now = () => Date.now(),
|
|
1807
|
+
warn = (message) => logger.warn(message)
|
|
1808
|
+
}) {
|
|
1809
|
+
const startedAt = now();
|
|
1810
|
+
const timeoutController = new AbortController();
|
|
1811
|
+
const signals = [timeoutController.signal];
|
|
1812
|
+
if (init.signal) signals.push(init.signal);
|
|
1813
|
+
const signal = signals.length === 1 ? signals[0] : AbortSignal.any(signals);
|
|
1814
|
+
const timeout = setTimeout(() => {
|
|
1815
|
+
timeoutController.abort();
|
|
1816
|
+
}, timeoutMs);
|
|
1817
|
+
timeout.unref?.();
|
|
1818
|
+
try {
|
|
1819
|
+
const response = await fetchImpl(url, { ...init, signal });
|
|
1820
|
+
const data = await response.json();
|
|
1821
|
+
return { response, data, durationMs: now() - startedAt };
|
|
1822
|
+
} catch (err) {
|
|
1823
|
+
const durationMs = now() - startedAt;
|
|
1824
|
+
if (timeoutController.signal.aborted && !init.signal?.aborted) {
|
|
1825
|
+
warn(
|
|
1826
|
+
`[ChatBridgeTimeout] tool=${toolName} target=${target ?? "-"} duration_ms=${durationMs} timeout_ms=${timeoutMs} outcome=timeout`
|
|
1827
|
+
);
|
|
1828
|
+
throw new ChatBridgeToolTimeoutError(toolName, target, timeoutMs, durationMs);
|
|
1829
|
+
}
|
|
1830
|
+
warn(
|
|
1831
|
+
`[ChatBridgeError] tool=${toolName} target=${target ?? "-"} duration_ms=${durationMs} outcome=error error=${describeError(err)}`
|
|
1832
|
+
);
|
|
1833
|
+
throw err;
|
|
1834
|
+
} finally {
|
|
1835
|
+
clearTimeout(timeout);
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1838
|
+
async function executeResponseRequest(url, init, {
|
|
1839
|
+
toolName,
|
|
1840
|
+
target = null,
|
|
1841
|
+
timeoutMs = DEFAULT_CHAT_BRIDGE_TOOL_TIMEOUT_MS,
|
|
1842
|
+
fetchImpl,
|
|
1843
|
+
now = () => Date.now(),
|
|
1844
|
+
warn = (message) => logger.warn(message)
|
|
1845
|
+
}) {
|
|
1846
|
+
const startedAt = now();
|
|
1847
|
+
const timeoutController = new AbortController();
|
|
1848
|
+
const signals = [timeoutController.signal];
|
|
1849
|
+
if (init.signal) signals.push(init.signal);
|
|
1850
|
+
const signal = signals.length === 1 ? signals[0] : AbortSignal.any(signals);
|
|
1851
|
+
const timeout = setTimeout(() => {
|
|
1852
|
+
timeoutController.abort();
|
|
1853
|
+
}, timeoutMs);
|
|
1854
|
+
timeout.unref?.();
|
|
1855
|
+
try {
|
|
1856
|
+
const response = await fetchImpl(url, { ...init, signal });
|
|
1857
|
+
return { response, durationMs: now() - startedAt };
|
|
1858
|
+
} catch (err) {
|
|
1859
|
+
const durationMs = now() - startedAt;
|
|
1860
|
+
if (timeoutController.signal.aborted && !init.signal?.aborted) {
|
|
1861
|
+
warn(
|
|
1862
|
+
`[ChatBridgeTimeout] tool=${toolName} target=${target ?? "-"} duration_ms=${durationMs} timeout_ms=${timeoutMs} outcome=timeout`
|
|
1863
|
+
);
|
|
1864
|
+
throw new ChatBridgeToolTimeoutError(toolName, target, timeoutMs, durationMs);
|
|
1865
|
+
}
|
|
1866
|
+
warn(
|
|
1867
|
+
`[ChatBridgeError] tool=${toolName} target=${target ?? "-"} duration_ms=${durationMs} outcome=error error=${describeError(err)}`
|
|
1868
|
+
);
|
|
1869
|
+
throw err;
|
|
1870
|
+
} finally {
|
|
1871
|
+
clearTimeout(timeout);
|
|
1872
|
+
}
|
|
1873
|
+
}
|
|
1874
|
+
|
|
1875
|
+
// src/directUploadCapability.ts
|
|
1876
|
+
function joinUrl(base, path18) {
|
|
1877
|
+
return `${base.replace(/\/+$/, "")}${path18}`;
|
|
1878
|
+
}
|
|
1879
|
+
function jsonHeaders(apiKey) {
|
|
1880
|
+
return {
|
|
1881
|
+
"Content-Type": "application/json",
|
|
1882
|
+
...apiKey ? { Authorization: `Bearer ${apiKey}` } : {}
|
|
1883
|
+
};
|
|
1884
|
+
}
|
|
1885
|
+
async function requestDaemonScopeAttestation({
|
|
1886
|
+
serverUrl,
|
|
1887
|
+
apiKey,
|
|
1888
|
+
scope,
|
|
1889
|
+
metadata,
|
|
1890
|
+
fetchImpl = daemonFetch,
|
|
1891
|
+
timeoutMs = DEFAULT_CHAT_BRIDGE_TOOL_TIMEOUT_MS
|
|
1892
|
+
}) {
|
|
1893
|
+
const { response, data } = await executeJsonRequest(
|
|
1894
|
+
joinUrl(serverUrl, "/internal/machine/scope-attestation"),
|
|
1895
|
+
{
|
|
1896
|
+
method: "POST",
|
|
1897
|
+
headers: jsonHeaders(apiKey),
|
|
1898
|
+
body: JSON.stringify({
|
|
1899
|
+
scope,
|
|
1900
|
+
...metadata ? { metadata } : {}
|
|
1901
|
+
})
|
|
1902
|
+
},
|
|
1903
|
+
{
|
|
1904
|
+
toolName: "daemon_direct_upload.scope_attestation",
|
|
1905
|
+
target: scope,
|
|
1906
|
+
timeoutMs,
|
|
1907
|
+
fetchImpl
|
|
1908
|
+
}
|
|
1909
|
+
);
|
|
1910
|
+
if (!response.ok) {
|
|
1911
|
+
throw new Error(`Failed to request daemon scope attestation (${response.status})`);
|
|
1912
|
+
}
|
|
1913
|
+
return data;
|
|
1914
|
+
}
|
|
1915
|
+
async function createDirectUploadSession({
|
|
1916
|
+
serverUrl,
|
|
1917
|
+
apiKey,
|
|
1918
|
+
workerUrl,
|
|
1919
|
+
scope,
|
|
1920
|
+
createPath = "/api/uploads",
|
|
1921
|
+
body,
|
|
1922
|
+
attestationMetadata,
|
|
1923
|
+
fetchImpl = daemonFetch,
|
|
1924
|
+
timeoutMs = DEFAULT_CHAT_BRIDGE_TOOL_TIMEOUT_MS
|
|
1925
|
+
}) {
|
|
1926
|
+
const capability = await requestDaemonScopeAttestation({
|
|
1927
|
+
serverUrl,
|
|
1928
|
+
apiKey,
|
|
1929
|
+
scope,
|
|
1930
|
+
metadata: attestationMetadata,
|
|
1931
|
+
fetchImpl,
|
|
1932
|
+
timeoutMs
|
|
1933
|
+
});
|
|
1934
|
+
const { response, data } = await executeJsonRequest(
|
|
1935
|
+
joinUrl(workerUrl, createPath),
|
|
1936
|
+
{
|
|
1937
|
+
method: "POST",
|
|
1938
|
+
headers: jsonHeaders(),
|
|
1939
|
+
body: JSON.stringify({
|
|
1940
|
+
...body,
|
|
1941
|
+
attestation: capability.attestation
|
|
1942
|
+
})
|
|
1943
|
+
},
|
|
1944
|
+
{
|
|
1945
|
+
toolName: "daemon_direct_upload.create",
|
|
1946
|
+
target: capability.audience,
|
|
1947
|
+
timeoutMs,
|
|
1948
|
+
fetchImpl
|
|
1949
|
+
}
|
|
1950
|
+
);
|
|
1951
|
+
if (!response.ok) {
|
|
1952
|
+
throw new Error(`Failed to create direct upload session (${response.status})`);
|
|
1953
|
+
}
|
|
1954
|
+
return { capability, response: data };
|
|
1955
|
+
}
|
|
1956
|
+
async function uploadWithSignedCapability({
|
|
1957
|
+
serverUrl,
|
|
1958
|
+
apiKey,
|
|
1959
|
+
workerUrl,
|
|
1960
|
+
scope,
|
|
1961
|
+
createPath = "/api/uploads",
|
|
1962
|
+
createBody,
|
|
1963
|
+
attestationMetadata,
|
|
1964
|
+
uploadBody,
|
|
1965
|
+
fetchImpl = daemonFetch,
|
|
1966
|
+
timeoutMs = DEFAULT_CHAT_BRIDGE_TOOL_TIMEOUT_MS
|
|
1967
|
+
}) {
|
|
1968
|
+
const { capability, response: session } = await createDirectUploadSession({
|
|
1969
|
+
serverUrl,
|
|
1970
|
+
apiKey,
|
|
1971
|
+
workerUrl,
|
|
1972
|
+
scope,
|
|
1973
|
+
createPath,
|
|
1974
|
+
body: createBody,
|
|
1975
|
+
attestationMetadata,
|
|
1976
|
+
fetchImpl,
|
|
1977
|
+
timeoutMs
|
|
1978
|
+
});
|
|
1979
|
+
const { response: uploadResponse } = await executeResponseRequest(
|
|
1980
|
+
session.upload.url,
|
|
1981
|
+
{
|
|
1982
|
+
method: session.upload.method,
|
|
1983
|
+
headers: session.upload.headers ?? {},
|
|
1984
|
+
body: uploadBody
|
|
1985
|
+
},
|
|
1986
|
+
{
|
|
1987
|
+
toolName: "daemon_direct_upload.put",
|
|
1988
|
+
target: capability.audience,
|
|
1989
|
+
timeoutMs,
|
|
1990
|
+
fetchImpl
|
|
1991
|
+
}
|
|
1992
|
+
);
|
|
1993
|
+
if (!uploadResponse.ok) {
|
|
1994
|
+
throw new Error(`Failed to upload with signed capability (${uploadResponse.status})`);
|
|
1995
|
+
}
|
|
1996
|
+
return { capability, session, uploadResponse };
|
|
1997
|
+
}
|
|
1998
|
+
|
|
1579
1999
|
// src/drivers/claude.ts
|
|
1580
2000
|
import { spawn } from "child_process";
|
|
1581
2001
|
|
|
@@ -2170,6 +2590,9 @@ import { URL as URL2 } from "url";
|
|
|
2170
2590
|
// src/apmStateMachine.ts
|
|
2171
2591
|
import { createHash } from "crypto";
|
|
2172
2592
|
var MAX_APM_GATED_STEERING_EVENTS = 12;
|
|
2593
|
+
function reviewStatePatch(state) {
|
|
2594
|
+
return state.reviewing !== void 0 ? { reviewing: state.reviewing } : {};
|
|
2595
|
+
}
|
|
2173
2596
|
function createInitialApmGatedSteeringState() {
|
|
2174
2597
|
return {
|
|
2175
2598
|
isIdle: false,
|
|
@@ -2182,6 +2605,22 @@ function createInitialApmGatedSteeringState() {
|
|
|
2182
2605
|
recentEvents: []
|
|
2183
2606
|
};
|
|
2184
2607
|
}
|
|
2608
|
+
function commitApmGatedSteeringDecisionState(nextState) {
|
|
2609
|
+
const committed = {
|
|
2610
|
+
isIdle: nextState.isIdle,
|
|
2611
|
+
expectedTerminationReason: nextState.expectedTerminationReason,
|
|
2612
|
+
phase: nextState.phase,
|
|
2613
|
+
outstandingToolUses: nextState.outstandingToolUses,
|
|
2614
|
+
compacting: nextState.compacting,
|
|
2615
|
+
toolBoundaryFlushDisabled: nextState.toolBoundaryFlushDisabled,
|
|
2616
|
+
lastFlushReason: nextState.lastFlushReason,
|
|
2617
|
+
recentEvents: [...nextState.recentEvents]
|
|
2618
|
+
};
|
|
2619
|
+
if (nextState.reviewing !== void 0) {
|
|
2620
|
+
committed.reviewing = nextState.reviewing;
|
|
2621
|
+
}
|
|
2622
|
+
return committed;
|
|
2623
|
+
}
|
|
2185
2624
|
function reduceApmIdleState(state, input) {
|
|
2186
2625
|
return {
|
|
2187
2626
|
nextState: {
|
|
@@ -2199,6 +2638,7 @@ function reduceApmGatedToolUse(state, input) {
|
|
|
2199
2638
|
phase: "tool_wait",
|
|
2200
2639
|
outstandingToolUses: state.outstandingToolUses + 1,
|
|
2201
2640
|
compacting: state.compacting,
|
|
2641
|
+
...reviewStatePatch(state),
|
|
2202
2642
|
toolBoundaryFlushDisabled: state.toolBoundaryFlushDisabled,
|
|
2203
2643
|
lastFlushReason: state.lastFlushReason,
|
|
2204
2644
|
recentEvents: state.recentEvents
|
|
@@ -2216,6 +2656,7 @@ function reduceApmGatedToolUse(state, input) {
|
|
|
2216
2656
|
phase: "tool_boundary",
|
|
2217
2657
|
outstandingToolUses,
|
|
2218
2658
|
compacting: state.compacting,
|
|
2659
|
+
...reviewStatePatch(state),
|
|
2219
2660
|
toolBoundaryFlushDisabled: state.toolBoundaryFlushDisabled,
|
|
2220
2661
|
lastFlushReason: state.lastFlushReason,
|
|
2221
2662
|
recentEvents: state.recentEvents
|
|
@@ -2233,20 +2674,52 @@ function reduceApmGatedCompaction(state, input) {
|
|
|
2233
2674
|
phase: "compacting",
|
|
2234
2675
|
outstandingToolUses: state.outstandingToolUses,
|
|
2235
2676
|
compacting: true,
|
|
2677
|
+
...reviewStatePatch(state),
|
|
2678
|
+
toolBoundaryFlushDisabled: state.toolBoundaryFlushDisabled,
|
|
2679
|
+
lastFlushReason: state.lastFlushReason,
|
|
2680
|
+
recentEvents: state.recentEvents
|
|
2681
|
+
}
|
|
2682
|
+
};
|
|
2683
|
+
}
|
|
2684
|
+
if (input.kind === "compaction_interrupted") {
|
|
2685
|
+
return {
|
|
2686
|
+
nextState: {
|
|
2687
|
+
isIdle: false,
|
|
2688
|
+
expectedTerminationReason: state.expectedTerminationReason,
|
|
2689
|
+
phase: state.phase,
|
|
2690
|
+
outstandingToolUses: state.outstandingToolUses,
|
|
2691
|
+
compacting: false,
|
|
2692
|
+
...reviewStatePatch(state),
|
|
2236
2693
|
toolBoundaryFlushDisabled: state.toolBoundaryFlushDisabled,
|
|
2237
2694
|
lastFlushReason: state.lastFlushReason,
|
|
2238
2695
|
recentEvents: state.recentEvents
|
|
2239
2696
|
}
|
|
2240
2697
|
};
|
|
2241
2698
|
}
|
|
2242
|
-
|
|
2699
|
+
return {
|
|
2700
|
+
nextState: {
|
|
2701
|
+
isIdle: false,
|
|
2702
|
+
expectedTerminationReason: state.expectedTerminationReason,
|
|
2703
|
+
phase: "assistant_continuation",
|
|
2704
|
+
outstandingToolUses: state.outstandingToolUses,
|
|
2705
|
+
compacting: false,
|
|
2706
|
+
...reviewStatePatch(state),
|
|
2707
|
+
toolBoundaryFlushDisabled: state.toolBoundaryFlushDisabled,
|
|
2708
|
+
lastFlushReason: state.lastFlushReason,
|
|
2709
|
+
recentEvents: state.recentEvents
|
|
2710
|
+
}
|
|
2711
|
+
};
|
|
2712
|
+
}
|
|
2713
|
+
function reduceApmGatedReview(state, input) {
|
|
2714
|
+
if (input.kind === "review_started") {
|
|
2243
2715
|
return {
|
|
2244
2716
|
nextState: {
|
|
2245
2717
|
isIdle: false,
|
|
2246
2718
|
expectedTerminationReason: state.expectedTerminationReason,
|
|
2247
|
-
phase:
|
|
2719
|
+
phase: "reviewing",
|
|
2248
2720
|
outstandingToolUses: state.outstandingToolUses,
|
|
2249
|
-
compacting:
|
|
2721
|
+
compacting: state.compacting,
|
|
2722
|
+
reviewing: true,
|
|
2250
2723
|
toolBoundaryFlushDisabled: state.toolBoundaryFlushDisabled,
|
|
2251
2724
|
lastFlushReason: state.lastFlushReason,
|
|
2252
2725
|
recentEvents: state.recentEvents
|
|
@@ -2259,7 +2732,8 @@ function reduceApmGatedCompaction(state, input) {
|
|
|
2259
2732
|
expectedTerminationReason: state.expectedTerminationReason,
|
|
2260
2733
|
phase: "assistant_continuation",
|
|
2261
2734
|
outstandingToolUses: state.outstandingToolUses,
|
|
2262
|
-
compacting:
|
|
2735
|
+
compacting: state.compacting,
|
|
2736
|
+
reviewing: false,
|
|
2263
2737
|
toolBoundaryFlushDisabled: state.toolBoundaryFlushDisabled,
|
|
2264
2738
|
lastFlushReason: state.lastFlushReason,
|
|
2265
2739
|
recentEvents: state.recentEvents
|
|
@@ -2280,6 +2754,20 @@ function reduceApmGatedCompactionBoundaryFlush(_state, input) {
|
|
|
2280
2754
|
}]
|
|
2281
2755
|
};
|
|
2282
2756
|
}
|
|
2757
|
+
function reduceApmGatedReviewBoundaryFlush(_state, input) {
|
|
2758
|
+
if (!input.hasSession || !input.supportsStdinNotification || input.inboxLength === 0) {
|
|
2759
|
+
return { effects: [] };
|
|
2760
|
+
}
|
|
2761
|
+
if (input.pendingNotificationCount === 0) return { effects: [] };
|
|
2762
|
+
return {
|
|
2763
|
+
effects: [{
|
|
2764
|
+
kind: "notify_stdin",
|
|
2765
|
+
reason: "review_finished",
|
|
2766
|
+
stdinMode: "busy",
|
|
2767
|
+
clauseId: "SMR-002"
|
|
2768
|
+
}]
|
|
2769
|
+
};
|
|
2770
|
+
}
|
|
2283
2771
|
function reduceApmGatedTurnEnd(_state, input = {}) {
|
|
2284
2772
|
const shouldDeliverQueuedMessages = Boolean(
|
|
2285
2773
|
input.inboxLength && input.inboxLength > 0 && input.supportsStdinNotification && input.hasSession
|
|
@@ -2291,6 +2779,7 @@ function reduceApmGatedTurnEnd(_state, input = {}) {
|
|
|
2291
2779
|
phase: "idle",
|
|
2292
2780
|
outstandingToolUses: 0,
|
|
2293
2781
|
compacting: false,
|
|
2782
|
+
..._state.reviewing !== void 0 ? { reviewing: false } : {},
|
|
2294
2783
|
toolBoundaryFlushDisabled: _state.toolBoundaryFlushDisabled,
|
|
2295
2784
|
lastFlushReason: _state.lastFlushReason,
|
|
2296
2785
|
recentEvents: _state.recentEvents
|
|
@@ -2312,6 +2801,7 @@ function reduceApmGatedError(state, input = {}) {
|
|
|
2312
2801
|
phase: "error",
|
|
2313
2802
|
outstandingToolUses: state.outstandingToolUses,
|
|
2314
2803
|
compacting: false,
|
|
2804
|
+
...state.reviewing !== void 0 ? { reviewing: false } : {},
|
|
2315
2805
|
toolBoundaryFlushDisabled: state.toolBoundaryFlushDisabled || shouldDisableToolBoundaryFlush,
|
|
2316
2806
|
lastFlushReason: state.lastFlushReason,
|
|
2317
2807
|
recentEvents: state.recentEvents
|
|
@@ -2327,6 +2817,7 @@ function reduceApmGatedAssistantContinuation(state) {
|
|
|
2327
2817
|
phase: "assistant_continuation",
|
|
2328
2818
|
outstandingToolUses: state.outstandingToolUses,
|
|
2329
2819
|
compacting: state.compacting,
|
|
2820
|
+
...reviewStatePatch(state),
|
|
2330
2821
|
toolBoundaryFlushDisabled: state.toolBoundaryFlushDisabled,
|
|
2331
2822
|
lastFlushReason: state.lastFlushReason,
|
|
2332
2823
|
recentEvents: state.recentEvents
|
|
@@ -2377,6 +2868,17 @@ function reduceApmStartupTimeoutTermination(state, input) {
|
|
|
2377
2868
|
blockedReason: null
|
|
2378
2869
|
};
|
|
2379
2870
|
}
|
|
2871
|
+
function reduceApmStartupRequestErrorTermination(state) {
|
|
2872
|
+
return {
|
|
2873
|
+
nextState: {
|
|
2874
|
+
...state,
|
|
2875
|
+
isIdle: false,
|
|
2876
|
+
expectedTerminationReason: "startup_request_error",
|
|
2877
|
+
phase: "error"
|
|
2878
|
+
},
|
|
2879
|
+
shouldTerminate: true
|
|
2880
|
+
};
|
|
2881
|
+
}
|
|
2380
2882
|
function reduceApmGatedFlush(state, input) {
|
|
2381
2883
|
return {
|
|
2382
2884
|
nextState: {
|
|
@@ -2386,7 +2888,8 @@ function reduceApmGatedFlush(state, input) {
|
|
|
2386
2888
|
};
|
|
2387
2889
|
}
|
|
2388
2890
|
function reduceApmGatedRecentEvent(state, input) {
|
|
2389
|
-
const
|
|
2891
|
+
const reviewSuffix = state.reviewing !== void 0 ? `:review=${state.reviewing === true}` : "";
|
|
2892
|
+
const summary = `${input.event}:${state.phase}:tools=${state.outstandingToolUses}:compact=${state.compacting}${reviewSuffix}`;
|
|
2390
2893
|
return {
|
|
2391
2894
|
nextState: {
|
|
2392
2895
|
...state,
|
|
@@ -2394,6 +2897,154 @@ function reduceApmGatedRecentEvent(state, input) {
|
|
|
2394
2897
|
}
|
|
2395
2898
|
};
|
|
2396
2899
|
}
|
|
2900
|
+
function projectApmRuntimeTerminationTrace(input) {
|
|
2901
|
+
if (input.reason === "startup_timeout") {
|
|
2902
|
+
const attrs = {
|
|
2903
|
+
turn_outcome: "failed",
|
|
2904
|
+
turn_subtype: "runtime_stalled",
|
|
2905
|
+
turn_reason: "no_runtime_events",
|
|
2906
|
+
runtime_start_failure_kind: "runtime_start_timeout",
|
|
2907
|
+
timeout_ms: input.timeoutMs
|
|
2908
|
+
};
|
|
2909
|
+
return {
|
|
2910
|
+
reason: input.reason,
|
|
2911
|
+
runtimeEventName: "runtime.start.timeout",
|
|
2912
|
+
runtimeEventAttrs: attrs,
|
|
2913
|
+
runtimeSpanAttrs: attrs,
|
|
2914
|
+
processExitAttrs: {
|
|
2915
|
+
stop_source: "startup_timeout",
|
|
2916
|
+
expectedTerminationReason: "startup_timeout",
|
|
2917
|
+
timeout_ms: input.timeoutMs
|
|
2918
|
+
},
|
|
2919
|
+
runtimeStopReason: "startup_timeout"
|
|
2920
|
+
};
|
|
2921
|
+
}
|
|
2922
|
+
if (input.reason === "turn_end") {
|
|
2923
|
+
return {
|
|
2924
|
+
reason: input.reason,
|
|
2925
|
+
processExitAttrs: {
|
|
2926
|
+
stop_source: "turn_end",
|
|
2927
|
+
expectedTerminationReason: "turn_end"
|
|
2928
|
+
},
|
|
2929
|
+
runtimeStopReason: "turn_end"
|
|
2930
|
+
};
|
|
2931
|
+
}
|
|
2932
|
+
const eventAttrs = {
|
|
2933
|
+
turn_outcome: "failed",
|
|
2934
|
+
turn_subtype: "runtime_stalled",
|
|
2935
|
+
turn_reason: input.turnReason,
|
|
2936
|
+
pendingMessages: input.pendingMessages,
|
|
2937
|
+
recovery: input.recoveryAction
|
|
2938
|
+
};
|
|
2939
|
+
return {
|
|
2940
|
+
reason: input.reason,
|
|
2941
|
+
runtimeEventName: "runtime.progress.stalled",
|
|
2942
|
+
runtimeEventAttrs: eventAttrs,
|
|
2943
|
+
runtimeSpanAttrs: {
|
|
2944
|
+
...eventAttrs,
|
|
2945
|
+
ageMs: input.staleForMs,
|
|
2946
|
+
lastActivity: input.lastActivity,
|
|
2947
|
+
lastActivityDetailPresent: input.lastActivityDetailPresent,
|
|
2948
|
+
lastActivityDetailKind: input.lastActivityDetailKind
|
|
2949
|
+
},
|
|
2950
|
+
processExitAttrs: {
|
|
2951
|
+
stop_source: "stalled_recovery",
|
|
2952
|
+
expectedTerminationReason: "stalled_recovery",
|
|
2953
|
+
queued_messages_count: input.pendingMessages
|
|
2954
|
+
},
|
|
2955
|
+
runtimeStopReason: "stalled_recovery"
|
|
2956
|
+
};
|
|
2957
|
+
}
|
|
2958
|
+
function projectApmRuntimeProgressStalledTrace(input) {
|
|
2959
|
+
const eventAttrs = {
|
|
2960
|
+
turn_outcome: "failed",
|
|
2961
|
+
turn_subtype: "runtime_stalled",
|
|
2962
|
+
turn_reason: input.turnReason
|
|
2963
|
+
};
|
|
2964
|
+
return {
|
|
2965
|
+
runtimeEventName: "runtime.progress.stalled",
|
|
2966
|
+
runtimeEventAttrs: eventAttrs,
|
|
2967
|
+
runtimeSpanAttrs: {
|
|
2968
|
+
...eventAttrs,
|
|
2969
|
+
ageMs: input.staleForMs,
|
|
2970
|
+
lastActivity: input.lastActivity,
|
|
2971
|
+
lastActivityDetailPresent: input.lastActivityDetailPresent,
|
|
2972
|
+
lastActivityDetailKind: input.lastActivityDetailKind
|
|
2973
|
+
}
|
|
2974
|
+
};
|
|
2975
|
+
}
|
|
2976
|
+
function projectApmRuntimeStallDiagnostic(input) {
|
|
2977
|
+
const context = [];
|
|
2978
|
+
const lastActivityDetailKind = classifyApmRuntimeStallActivityDetail(input.lastActivityDetail);
|
|
2979
|
+
if (input.lastActivityDetail) {
|
|
2980
|
+
context.push(`after ${input.lastActivityDetail}`);
|
|
2981
|
+
}
|
|
2982
|
+
if (input.runtimeDescriptorBusyDelivery === "gated") {
|
|
2983
|
+
context.push(`phase=${input.gatedPhase}`);
|
|
2984
|
+
}
|
|
2985
|
+
if (input.outstandingToolUses > 0) {
|
|
2986
|
+
context.push(`tools=${input.outstandingToolUses}`);
|
|
2987
|
+
}
|
|
2988
|
+
if (input.compacting) {
|
|
2989
|
+
context.push("compacting");
|
|
2990
|
+
}
|
|
2991
|
+
if (input.reviewing) {
|
|
2992
|
+
context.push("reviewing");
|
|
2993
|
+
}
|
|
2994
|
+
if (input.inboxCount > 0) {
|
|
2995
|
+
context.push(`queued=${input.inboxCount}`);
|
|
2996
|
+
}
|
|
2997
|
+
const detail = [
|
|
2998
|
+
`Runtime stalled: no runtime events for ${input.staleForMinutes}m`,
|
|
2999
|
+
context.length > 0 ? ` (${context.join(", ")})` : ""
|
|
3000
|
+
].join("");
|
|
3001
|
+
const turnReason = input.runtimeProgressLastEventKind === "tool_output" && input.outstandingToolUses === 0 ? "harness_post_tool_silent_wedge" : "no_runtime_events";
|
|
3002
|
+
return {
|
|
3003
|
+
detail,
|
|
3004
|
+
turnReason,
|
|
3005
|
+
lastActivityDetailPresent: Boolean(input.lastActivityDetail),
|
|
3006
|
+
lastActivityDetailKind,
|
|
3007
|
+
traceAttrs: {
|
|
3008
|
+
ageMs: input.staleForMs,
|
|
3009
|
+
staleForMinutes: input.staleForMinutes,
|
|
3010
|
+
lastActivity: input.lastActivity,
|
|
3011
|
+
lastActivityDetailPresent: Boolean(input.lastActivityDetail),
|
|
3012
|
+
lastActivityDetailKind,
|
|
3013
|
+
runtime: input.runtime,
|
|
3014
|
+
model: input.model,
|
|
3015
|
+
platform: input.platform,
|
|
3016
|
+
arch: input.arch,
|
|
3017
|
+
launchId: input.launchId || void 0,
|
|
3018
|
+
sessionIdPresent: input.sessionIdPresent,
|
|
3019
|
+
inboxCount: input.inboxCount,
|
|
3020
|
+
pendingNotificationCount: input.pendingNotificationCount,
|
|
3021
|
+
processPidPresent: input.processPidPresent,
|
|
3022
|
+
busyDeliveryMode: input.driverBusyDeliveryMode,
|
|
3023
|
+
supportsStdinNotification: input.supportsStdinNotification,
|
|
3024
|
+
gatedPhase: input.runtimeDescriptorBusyDelivery === "gated" ? input.gatedPhase : void 0,
|
|
3025
|
+
outstandingToolUses: input.outstandingToolUses,
|
|
3026
|
+
compacting: input.compacting,
|
|
3027
|
+
reviewing: input.reviewing === true ? true : void 0,
|
|
3028
|
+
recentStderrCount: input.recentStderrCount,
|
|
3029
|
+
recentStdoutCount: input.recentStdoutCount,
|
|
3030
|
+
...input.runtimeTraceCounterAttrs ?? {}
|
|
3031
|
+
}
|
|
3032
|
+
};
|
|
3033
|
+
}
|
|
3034
|
+
function classifyApmRuntimeStallActivityDetail(detail) {
|
|
3035
|
+
if (!detail) return void 0;
|
|
3036
|
+
if (detail === "Message received") return "message_received";
|
|
3037
|
+
if (detail === "Starting\u2026") return "starting";
|
|
3038
|
+
if (detail === "Running command\u2026") return "running_command";
|
|
3039
|
+
if (detail === "Checking messages\u2026") return "checking_messages";
|
|
3040
|
+
if (detail === "Compacting context") return "compacting_context";
|
|
3041
|
+
if (detail === "Context compaction finished") return "compaction_finished";
|
|
3042
|
+
if (detail === "Context compaction still running; no finish event observed") return "compaction_stale";
|
|
3043
|
+
if (detail === "Idle" || detail === "Process idle") return "idle";
|
|
3044
|
+
if (detail.startsWith("Restarting stalled ") && detail.endsWith(" runtime for queued message")) return "stalled_recovery";
|
|
3045
|
+
if (detail.startsWith("Runtime stalled: no runtime events for ")) return "runtime_stalled";
|
|
3046
|
+
return "other";
|
|
3047
|
+
}
|
|
2397
3048
|
function reduceApmGatedFlushReadiness(state, input) {
|
|
2398
3049
|
if (!input.isGated) return { shouldNotify: false, blockedReason: "non_gated", effects: [] };
|
|
2399
3050
|
if (!input.hasSession) return { shouldNotify: false, blockedReason: "missing_session", effects: [] };
|
|
@@ -2402,6 +3053,7 @@ function reduceApmGatedFlushReadiness(state, input) {
|
|
|
2402
3053
|
return { shouldNotify: false, blockedReason: "tool_boundary_flush_disabled", effects: [] };
|
|
2403
3054
|
}
|
|
2404
3055
|
if (state.compacting) return { shouldNotify: false, blockedReason: "compacting", effects: [] };
|
|
3056
|
+
if (state.reviewing) return { shouldNotify: false, blockedReason: "reviewing", effects: [] };
|
|
2405
3057
|
if (state.outstandingToolUses > 0) {
|
|
2406
3058
|
return { shouldNotify: false, blockedReason: "outstanding_tool_uses", effects: [] };
|
|
2407
3059
|
}
|
|
@@ -2894,162 +3546,8 @@ function stripUndefined(value) {
|
|
|
2894
3546
|
for (const key of Object.keys(value)) {
|
|
2895
3547
|
if (value[key] === void 0) delete value[key];
|
|
2896
3548
|
}
|
|
2897
|
-
return value;
|
|
2898
|
-
}
|
|
2899
|
-
|
|
2900
|
-
// src/proxy.ts
|
|
2901
|
-
import { HttpsProxyAgent } from "https-proxy-agent";
|
|
2902
|
-
import { ProxyAgent } from "undici";
|
|
2903
|
-
var fetchDispatcherCache = /* @__PURE__ */ new Map();
|
|
2904
|
-
function getFetchPreResponseTimeoutMs(env) {
|
|
2905
|
-
const parsed = Number.parseInt(env.SLOCK_DAEMON_FETCH_PRE_RESPONSE_TIMEOUT_MS || "", 10);
|
|
2906
|
-
return Number.isFinite(parsed) && parsed > 0 ? parsed : 3e4;
|
|
2907
|
-
}
|
|
2908
|
-
function getDefaultPort(protocol) {
|
|
2909
|
-
switch (protocol) {
|
|
2910
|
-
case "https:":
|
|
2911
|
-
case "wss:":
|
|
2912
|
-
return "443";
|
|
2913
|
-
case "http:":
|
|
2914
|
-
case "ws:":
|
|
2915
|
-
return "80";
|
|
2916
|
-
default:
|
|
2917
|
-
return "";
|
|
2918
|
-
}
|
|
2919
|
-
}
|
|
2920
|
-
function hostMatchesNoProxyEntry(hostname, ruleHost) {
|
|
2921
|
-
if (!ruleHost) return false;
|
|
2922
|
-
const normalizedRule = ruleHost.replace(/^\*\./, ".").replace(/^\./, "").toLowerCase();
|
|
2923
|
-
const normalizedHost = hostname.toLowerCase();
|
|
2924
|
-
return normalizedHost === normalizedRule || normalizedHost.endsWith(`.${normalizedRule}`);
|
|
2925
|
-
}
|
|
2926
|
-
function getProxyUrlForTarget(targetUrl, env) {
|
|
2927
|
-
const protocol = new URL(targetUrl).protocol;
|
|
2928
|
-
switch (protocol) {
|
|
2929
|
-
case "wss:":
|
|
2930
|
-
return env.WSS_PROXY || env.wss_proxy || env.HTTPS_PROXY || env.https_proxy || env.ALL_PROXY || env.all_proxy;
|
|
2931
|
-
case "ws:":
|
|
2932
|
-
return env.WS_PROXY || env.ws_proxy || env.HTTP_PROXY || env.http_proxy || env.ALL_PROXY || env.all_proxy;
|
|
2933
|
-
case "https:":
|
|
2934
|
-
return env.HTTPS_PROXY || env.https_proxy || env.ALL_PROXY || env.all_proxy;
|
|
2935
|
-
case "http:":
|
|
2936
|
-
return env.HTTP_PROXY || env.http_proxy || env.ALL_PROXY || env.all_proxy;
|
|
2937
|
-
default:
|
|
2938
|
-
return env.ALL_PROXY || env.all_proxy;
|
|
2939
|
-
}
|
|
2940
|
-
}
|
|
2941
|
-
function shouldBypassProxy(targetUrl, env) {
|
|
2942
|
-
const rawNoProxy = env.NO_PROXY || env.no_proxy;
|
|
2943
|
-
if (!rawNoProxy) return false;
|
|
2944
|
-
const url = new URL(targetUrl);
|
|
2945
|
-
const hostname = url.hostname.toLowerCase();
|
|
2946
|
-
const port = url.port || getDefaultPort(url.protocol);
|
|
2947
|
-
return rawNoProxy.split(",").map((entry) => entry.trim()).filter(Boolean).some((entry) => {
|
|
2948
|
-
if (entry === "*") return true;
|
|
2949
|
-
const [ruleHost, rulePort] = entry.split(":", 2);
|
|
2950
|
-
if (rulePort && rulePort !== port) return false;
|
|
2951
|
-
return hostMatchesNoProxyEntry(hostname, ruleHost);
|
|
2952
|
-
});
|
|
2953
|
-
}
|
|
2954
|
-
function buildWebSocketOptions(wsUrl, env) {
|
|
2955
|
-
const proxyUrl = getProxyUrlForTarget(wsUrl, env);
|
|
2956
|
-
if (!proxyUrl) return void 0;
|
|
2957
|
-
if (shouldBypassProxy(wsUrl, env)) return void 0;
|
|
2958
|
-
return {
|
|
2959
|
-
agent: new HttpsProxyAgent(proxyUrl)
|
|
2960
|
-
};
|
|
2961
|
-
}
|
|
2962
|
-
function resolveProxyUrl(targetUrl, env) {
|
|
2963
|
-
const proxyUrl = getProxyUrlForTarget(targetUrl, env);
|
|
2964
|
-
if (!proxyUrl) return void 0;
|
|
2965
|
-
if (shouldBypassProxy(targetUrl, env)) return void 0;
|
|
2966
|
-
return proxyUrl;
|
|
2967
|
-
}
|
|
2968
|
-
function buildFetchDispatcher(targetUrl, env) {
|
|
2969
|
-
const proxyUrl = resolveProxyUrl(targetUrl, env);
|
|
2970
|
-
if (!proxyUrl) return void 0;
|
|
2971
|
-
const cached = fetchDispatcherCache.get(proxyUrl);
|
|
2972
|
-
if (cached) return cached;
|
|
2973
|
-
const timeoutMs = getFetchPreResponseTimeoutMs(env);
|
|
2974
|
-
const dispatcher = new ProxyAgent({
|
|
2975
|
-
uri: proxyUrl,
|
|
2976
|
-
// All three are pre-response and body-agnostic (see getFetchPreResponseTimeoutMs):
|
|
2977
|
-
// headersTimeout = headers-hang leg; requestTls.timeout = CONNECT-tunnel
|
|
2978
|
-
// establish leg; connect.timeout = socket to the proxy itself (defensive).
|
|
2979
|
-
connect: { timeout: timeoutMs },
|
|
2980
|
-
requestTls: { timeout: timeoutMs },
|
|
2981
|
-
headersTimeout: timeoutMs
|
|
2982
|
-
});
|
|
2983
|
-
fetchDispatcherCache.set(proxyUrl, dispatcher);
|
|
2984
|
-
return dispatcher;
|
|
2985
|
-
}
|
|
2986
|
-
function evictFetchDispatcher(targetUrl, env) {
|
|
2987
|
-
const proxyUrl = resolveProxyUrl(targetUrl, env);
|
|
2988
|
-
if (!proxyUrl) return false;
|
|
2989
|
-
const cached = fetchDispatcherCache.get(proxyUrl);
|
|
2990
|
-
if (!cached) return false;
|
|
2991
|
-
fetchDispatcherCache.delete(proxyUrl);
|
|
2992
|
-
void Promise.resolve().then(() => cached.close()).catch(() => cached.destroy?.(new Error("evicted"))).catch(() => {
|
|
2993
|
-
});
|
|
2994
|
-
return true;
|
|
2995
|
-
}
|
|
2996
|
-
|
|
2997
|
-
// src/daemonFetch.ts
|
|
2998
|
-
function withDaemonFetchProxy(input, init = {}, env = process.env) {
|
|
2999
|
-
const dispatcher = buildFetchDispatcher(input.toString(), env);
|
|
3000
|
-
return dispatcher ? { ...init, dispatcher } : init;
|
|
3001
|
-
}
|
|
3002
|
-
async function daemonFetch(input, init, env = process.env) {
|
|
3003
|
-
try {
|
|
3004
|
-
return await fetch(input, withDaemonFetchProxy(input, init, env));
|
|
3005
|
-
} catch (err) {
|
|
3006
|
-
evictFetchDispatcher(input.toString(), env);
|
|
3007
|
-
throw err;
|
|
3008
|
-
}
|
|
3009
|
-
}
|
|
3010
|
-
|
|
3011
|
-
// src/logger.ts
|
|
3012
|
-
var listeners = /* @__PURE__ */ new Set();
|
|
3013
|
-
function timestamp() {
|
|
3014
|
-
const d = /* @__PURE__ */ new Date();
|
|
3015
|
-
const pad = (n) => String(n).padStart(2, "0");
|
|
3016
|
-
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
|
|
3017
|
-
}
|
|
3018
|
-
function format(level, msg) {
|
|
3019
|
-
return `${timestamp()} [${level}] ${msg}`;
|
|
3020
|
-
}
|
|
3021
|
-
function emit(event) {
|
|
3022
|
-
for (const listener of listeners) {
|
|
3023
|
-
listener(event);
|
|
3024
|
-
}
|
|
3025
|
-
}
|
|
3026
|
-
function subscribeDaemonLogs(listener) {
|
|
3027
|
-
listeners.add(listener);
|
|
3028
|
-
return () => {
|
|
3029
|
-
listeners.delete(listener);
|
|
3030
|
-
};
|
|
3031
|
-
}
|
|
3032
|
-
var logger = {
|
|
3033
|
-
info(msg) {
|
|
3034
|
-
const line = format("INFO", msg);
|
|
3035
|
-
console.log(line);
|
|
3036
|
-
emit({ level: "INFO", line, message: msg });
|
|
3037
|
-
},
|
|
3038
|
-
warn(msg) {
|
|
3039
|
-
const line = format("WARN", msg);
|
|
3040
|
-
console.warn(line);
|
|
3041
|
-
emit({ level: "WARN", line, message: msg });
|
|
3042
|
-
},
|
|
3043
|
-
error(msg, err) {
|
|
3044
|
-
const line = format("ERROR", msg);
|
|
3045
|
-
if (err) {
|
|
3046
|
-
console.error(line, err);
|
|
3047
|
-
} else {
|
|
3048
|
-
console.error(line);
|
|
3049
|
-
}
|
|
3050
|
-
emit({ level: "ERROR", line, message: msg, error: err });
|
|
3051
|
-
}
|
|
3052
|
-
};
|
|
3549
|
+
return value;
|
|
3550
|
+
}
|
|
3053
3551
|
|
|
3054
3552
|
// src/agentCredentialProxy.ts
|
|
3055
3553
|
var registrations = /* @__PURE__ */ new Map();
|
|
@@ -3869,7 +4367,8 @@ async function prepareCliTransport(ctx, extraEnv = {}, platform = process.platfo
|
|
|
3869
4367
|
}
|
|
3870
4368
|
const posixWrapper = path2.join(slockDir, "slock");
|
|
3871
4369
|
const posixRaftWrapper = path2.join(slockDir, "raft");
|
|
3872
|
-
const
|
|
4370
|
+
const posixIdentityPrefix = `SLOCK_AGENT_ID=${shellSingleQuote(ctx.agentId)} SLOCK_SERVER_URL=${shellSingleQuote(ctx.config.serverUrl)} `;
|
|
4371
|
+
const posixCredentialPrefix = posixIdentityPrefix + (agentCredentialProxy ? `SLOCK_AGENT_PROXY_URL=${shellSingleQuote(agentCredentialProxy.proxyUrl)} SLOCK_AGENT_PROXY_TOKEN_FILE=${shellSingleQuote(agentCredentialProxyTokenFile)} SLOCK_AGENT_ACTIVE_CAPABILITIES=${shellSingleQuote(DEFAULT_ACTIVE_CAPABILITIES)} ` : `SLOCK_AGENT_TOKEN_FILE=${shellSingleQuote(tokenFile)} `);
|
|
3873
4372
|
const posixCliFallbackBlock = cliPath === "__cli" || cliFallbackCandidates.length === 0 ? "" : `if [ ! -e "$SLOCK_CLI" ]; then
|
|
3874
4373
|
${cliFallbackCandidates.map((candidate, i) => ` ${i === 0 ? "if" : "elif"} [ -e ${shellSingleQuote(candidate)} ]; then SLOCK_CLI=${shellSingleQuote(candidate)};`).join("\n")}
|
|
3875
4374
|
fi
|
|
@@ -3885,10 +4384,14 @@ ${posixCliFallbackBlock}${posixCredentialPrefix}exec ${shellSingleQuote(process.
|
|
|
3885
4384
|
if (platform === "win32") {
|
|
3886
4385
|
const cmdWrapper = path2.join(slockDir, "slock.cmd");
|
|
3887
4386
|
const cmdRaftWrapper = path2.join(slockDir, "raft.cmd");
|
|
3888
|
-
const
|
|
4387
|
+
const cmdIdentityLines = `set "SLOCK_AGENT_ID=${ctx.agentId}"\r
|
|
4388
|
+
set "SLOCK_SERVER_URL=${ctx.config.serverUrl}"\r
|
|
4389
|
+
`;
|
|
4390
|
+
const cmdCredentialLine = cmdIdentityLines + (agentCredentialProxy ? `set "SLOCK_AGENT_PROXY_URL=${agentCredentialProxy.proxyUrl}"\r
|
|
3889
4391
|
set "SLOCK_AGENT_PROXY_TOKEN_FILE=${agentCredentialProxyTokenFile}"\r
|
|
3890
4392
|
set "SLOCK_AGENT_ACTIVE_CAPABILITIES=${DEFAULT_ACTIVE_CAPABILITIES}"\r
|
|
3891
|
-
` : ""
|
|
4393
|
+
` : `set "SLOCK_AGENT_TOKEN_FILE=${tokenFile}"\r
|
|
4394
|
+
`);
|
|
3892
4395
|
const cmdCliFallbackLines = cliPath === "__cli" ? [] : cliFallbackCandidates.map((candidate) => `if not exist "%SLOCK_CLI%" set "SLOCK_CLI=${candidate}"`);
|
|
3893
4396
|
const cmdBody = [
|
|
3894
4397
|
"@echo off",
|
|
@@ -3908,11 +4411,20 @@ set "SLOCK_AGENT_ACTIVE_CAPABILITIES=${DEFAULT_ACTIVE_CAPABILITIES}"\r
|
|
|
3908
4411
|
writeFileSync(cmdRaftWrapper, cmdBody);
|
|
3909
4412
|
const psWrapper = path2.join(slockDir, "slock.ps1");
|
|
3910
4413
|
const psRaftWrapper = path2.join(slockDir, "raft.ps1");
|
|
3911
|
-
const
|
|
3912
|
-
`$env:
|
|
3913
|
-
`$env:
|
|
3914
|
-
|
|
3915
|
-
|
|
4414
|
+
const psIdentityLines = [
|
|
4415
|
+
`$env:SLOCK_AGENT_ID=${powershellSingleQuote(ctx.agentId)}`,
|
|
4416
|
+
`$env:SLOCK_SERVER_URL=${powershellSingleQuote(ctx.config.serverUrl)}`
|
|
4417
|
+
];
|
|
4418
|
+
const psCredentialLines = [
|
|
4419
|
+
...psIdentityLines,
|
|
4420
|
+
...agentCredentialProxy ? [
|
|
4421
|
+
`$env:SLOCK_AGENT_PROXY_URL=${powershellSingleQuote(agentCredentialProxy.proxyUrl)}`,
|
|
4422
|
+
`$env:SLOCK_AGENT_PROXY_TOKEN_FILE=${powershellSingleQuote(agentCredentialProxyTokenFile)}`,
|
|
4423
|
+
`$env:SLOCK_AGENT_ACTIVE_CAPABILITIES=${powershellSingleQuote(DEFAULT_ACTIVE_CAPABILITIES)}`
|
|
4424
|
+
] : [
|
|
4425
|
+
`$env:SLOCK_AGENT_TOKEN_FILE=${powershellSingleQuote(tokenFile)}`
|
|
4426
|
+
]
|
|
4427
|
+
];
|
|
3916
4428
|
const psBody = [
|
|
3917
4429
|
"$ErrorActionPreference = 'Stop'",
|
|
3918
4430
|
"$utf8NoBom = [System.Text.UTF8Encoding]::new($false)",
|
|
@@ -4663,7 +5175,7 @@ var ClaudeDriver = class {
|
|
|
4663
5175
|
};
|
|
4664
5176
|
|
|
4665
5177
|
// src/drivers/codex.ts
|
|
4666
|
-
import { spawn as spawn2, execFileSync as execFileSync2
|
|
5178
|
+
import { spawn as spawn2, execFileSync as execFileSync2 } from "child_process";
|
|
4667
5179
|
import { existsSync as existsSync5, readFileSync as readFileSync2 } from "fs";
|
|
4668
5180
|
import os3 from "os";
|
|
4669
5181
|
import path6 from "path";
|
|
@@ -4671,6 +5183,7 @@ import path6 from "path";
|
|
|
4671
5183
|
// src/runtimeTurnState.ts
|
|
4672
5184
|
var RuntimeTurnState = class {
|
|
4673
5185
|
currentTurnId = null;
|
|
5186
|
+
pendingTurnId = null;
|
|
4674
5187
|
/**
|
|
4675
5188
|
* Post-tool window where the app-server may not yet accept stdin steering.
|
|
4676
5189
|
* Gate busy-mode delivery until turn/completed or next progress.
|
|
@@ -4678,22 +5191,25 @@ var RuntimeTurnState = class {
|
|
|
4678
5191
|
steeringGateActive = false;
|
|
4679
5192
|
reset() {
|
|
4680
5193
|
this.currentTurnId = null;
|
|
5194
|
+
this.pendingTurnId = null;
|
|
4681
5195
|
this.steeringGateActive = false;
|
|
4682
5196
|
}
|
|
4683
5197
|
get activeTurnId() {
|
|
4684
5198
|
return this.currentTurnId;
|
|
4685
5199
|
}
|
|
4686
5200
|
get canSteerBusy() {
|
|
4687
|
-
return Boolean(this.currentTurnId && !this.steeringGateActive);
|
|
5201
|
+
return Boolean(this.currentTurnId && !this.pendingTurnId && !this.steeringGateActive);
|
|
4688
5202
|
}
|
|
4689
5203
|
markTurnStarted(turnId) {
|
|
4690
|
-
|
|
4691
|
-
|
|
5204
|
+
const startedTurnId = turnId ?? this.pendingTurnId;
|
|
5205
|
+
if (startedTurnId) {
|
|
5206
|
+
this.currentTurnId = startedTurnId;
|
|
4692
5207
|
}
|
|
5208
|
+
this.pendingTurnId = null;
|
|
4693
5209
|
this.steeringGateActive = false;
|
|
4694
5210
|
}
|
|
4695
|
-
|
|
4696
|
-
this.
|
|
5211
|
+
noteTurnAccepted(turnId) {
|
|
5212
|
+
this.pendingTurnId = turnId;
|
|
4697
5213
|
}
|
|
4698
5214
|
markProgress() {
|
|
4699
5215
|
this.steeringGateActive = false;
|
|
@@ -4703,6 +5219,7 @@ var RuntimeTurnState = class {
|
|
|
4703
5219
|
}
|
|
4704
5220
|
markTurnCompleted() {
|
|
4705
5221
|
this.currentTurnId = null;
|
|
5222
|
+
this.pendingTurnId = null;
|
|
4706
5223
|
this.steeringGateActive = false;
|
|
4707
5224
|
}
|
|
4708
5225
|
};
|
|
@@ -4797,27 +5314,79 @@ function getCodexNotificationErrorMessage(params) {
|
|
|
4797
5314
|
}
|
|
4798
5315
|
return null;
|
|
4799
5316
|
}
|
|
4800
|
-
function
|
|
5317
|
+
function payloadBytes(value) {
|
|
5318
|
+
try {
|
|
5319
|
+
return Buffer.byteLength(JSON.stringify(value), "utf8");
|
|
5320
|
+
} catch {
|
|
5321
|
+
return void 0;
|
|
5322
|
+
}
|
|
5323
|
+
}
|
|
5324
|
+
function codexNotificationProgressEvent(itemType, payload) {
|
|
5325
|
+
return {
|
|
5326
|
+
kind: "internal_progress",
|
|
5327
|
+
source: "codex_app_server_notification",
|
|
5328
|
+
itemType,
|
|
5329
|
+
payloadBytes: payload === void 0 ? void 0 : payloadBytes(payload)
|
|
5330
|
+
};
|
|
5331
|
+
}
|
|
5332
|
+
function boundedString(value, limit = 1e3) {
|
|
5333
|
+
if (typeof value !== "string") return void 0;
|
|
5334
|
+
const trimmed = value.trim();
|
|
5335
|
+
if (!trimmed) return void 0;
|
|
5336
|
+
if (trimmed.length <= limit) return trimmed;
|
|
5337
|
+
return `${trimmed.slice(0, limit - 1)}\u2026`;
|
|
5338
|
+
}
|
|
5339
|
+
function codexNotificationDiagnosticEvent(message) {
|
|
5340
|
+
const params = message.params ?? {};
|
|
5341
|
+
let diagnosticMessage;
|
|
5342
|
+
let details;
|
|
5343
|
+
switch (message.method) {
|
|
5344
|
+
case "configWarning":
|
|
5345
|
+
diagnosticMessage = boundedString(params.summary) ?? boundedString(params.details) ?? "Codex configuration warning";
|
|
5346
|
+
details = boundedString(params.details);
|
|
5347
|
+
break;
|
|
5348
|
+
case "warning":
|
|
5349
|
+
diagnosticMessage = boundedString(params.message) ?? "Codex warning";
|
|
5350
|
+
break;
|
|
5351
|
+
case "guardianWarning":
|
|
5352
|
+
diagnosticMessage = boundedString(params.message) ?? "Codex guardian warning";
|
|
5353
|
+
break;
|
|
5354
|
+
case "deprecationNotice":
|
|
5355
|
+
diagnosticMessage = boundedString(params.summary) ?? boundedString(params.details) ?? "Codex deprecation notice";
|
|
5356
|
+
details = boundedString(params.details);
|
|
5357
|
+
break;
|
|
5358
|
+
default:
|
|
5359
|
+
return null;
|
|
5360
|
+
}
|
|
5361
|
+
const sessionId = codexMessageThreadId(message);
|
|
5362
|
+
const path18 = boundedString(params.path);
|
|
5363
|
+
return {
|
|
5364
|
+
kind: "runtime_diagnostic",
|
|
5365
|
+
severity: "warning",
|
|
5366
|
+
source: "codex_app_server_notification",
|
|
5367
|
+
itemType: message.method,
|
|
5368
|
+
message: diagnosticMessage,
|
|
5369
|
+
...details ? { details } : {},
|
|
5370
|
+
...path18 ? { path: path18 } : {},
|
|
5371
|
+
...params.range !== void 0 ? { range: params.range } : {},
|
|
5372
|
+
payloadBytes: payloadBytes(params),
|
|
5373
|
+
...sessionId ? { sessionId } : {}
|
|
5374
|
+
};
|
|
5375
|
+
}
|
|
5376
|
+
function joinReasoningSummaryText(item) {
|
|
4801
5377
|
const summary = Array.isArray(item.summary) ? item.summary.filter((entry) => typeof entry === "string") : [];
|
|
4802
|
-
|
|
4803
|
-
return [...summary, ...content].join("\n").trim();
|
|
5378
|
+
return summary.join("\n").trim();
|
|
4804
5379
|
}
|
|
4805
5380
|
function rawResponseItemProgressEvent(message) {
|
|
4806
5381
|
if (message.method !== "rawResponseItem/completed") return null;
|
|
4807
5382
|
const item = message.params?.item ?? message.params?.responseItem ?? message.params?.rawItem ?? message.params;
|
|
4808
5383
|
if (!item || typeof item !== "object") return null;
|
|
4809
5384
|
const itemType = typeof item.type === "string" ? item.type : void 0;
|
|
4810
|
-
let payloadBytes;
|
|
4811
|
-
try {
|
|
4812
|
-
payloadBytes = Buffer.byteLength(JSON.stringify(item), "utf8");
|
|
4813
|
-
} catch {
|
|
4814
|
-
payloadBytes = void 0;
|
|
4815
|
-
}
|
|
4816
5385
|
return {
|
|
4817
5386
|
kind: "internal_progress",
|
|
4818
5387
|
source: "codex_raw_response_item",
|
|
4819
5388
|
itemType,
|
|
4820
|
-
payloadBytes
|
|
5389
|
+
payloadBytes: payloadBytes(item)
|
|
4821
5390
|
};
|
|
4822
5391
|
}
|
|
4823
5392
|
function nonEmptyString2(value) {
|
|
@@ -4828,11 +5397,15 @@ function codexMcpToolName(item) {
|
|
|
4828
5397
|
const server = nonEmptyString2(item.server);
|
|
4829
5398
|
return server ? `mcp_${server}_${tool}` : `mcp_${tool}`;
|
|
4830
5399
|
}
|
|
5400
|
+
function codexMessageThreadId(message) {
|
|
5401
|
+
return nonEmptyString2(message.params?.threadId) ?? nonEmptyString2(message.params?.thread?.id) ?? nonEmptyString2(message.params?.sessionId);
|
|
5402
|
+
}
|
|
4831
5403
|
var CodexEventNormalizer = class {
|
|
4832
5404
|
currentThreadId = null;
|
|
4833
5405
|
sessionAnnounced = false;
|
|
4834
5406
|
streamedAgentMessageIds = /* @__PURE__ */ new Set();
|
|
4835
5407
|
streamedReasoningIds = /* @__PURE__ */ new Set();
|
|
5408
|
+
fileChangeToolCallCounts = /* @__PURE__ */ new Map();
|
|
4836
5409
|
turnState = new RuntimeTurnState();
|
|
4837
5410
|
reset(opts = {}) {
|
|
4838
5411
|
this.currentThreadId = opts.threadId ?? null;
|
|
@@ -4840,6 +5413,7 @@ var CodexEventNormalizer = class {
|
|
|
4840
5413
|
this.sessionAnnounced = false;
|
|
4841
5414
|
this.streamedAgentMessageIds.clear();
|
|
4842
5415
|
this.streamedReasoningIds.clear();
|
|
5416
|
+
this.fileChangeToolCallCounts.clear();
|
|
4843
5417
|
}
|
|
4844
5418
|
get threadId() {
|
|
4845
5419
|
return this.currentThreadId;
|
|
@@ -4862,11 +5436,11 @@ var CodexEventNormalizer = class {
|
|
|
4862
5436
|
}
|
|
4863
5437
|
const turn = message.result.turn;
|
|
4864
5438
|
if (turn && typeof turn.id === "string") {
|
|
4865
|
-
this.turnState.
|
|
5439
|
+
this.turnState.noteTurnAccepted(turn.id);
|
|
4866
5440
|
return { events };
|
|
4867
5441
|
}
|
|
4868
5442
|
if (typeof message.result.turnId === "string") {
|
|
4869
|
-
this.turnState.
|
|
5443
|
+
this.turnState.noteTurnAccepted(message.result.turnId);
|
|
4870
5444
|
return { events };
|
|
4871
5445
|
}
|
|
4872
5446
|
}
|
|
@@ -4874,9 +5448,12 @@ var CodexEventNormalizer = class {
|
|
|
4874
5448
|
events.push({ kind: "error", message: message.error.message || "Codex app-server request failed" });
|
|
4875
5449
|
return { events };
|
|
4876
5450
|
}
|
|
5451
|
+
if (this.isSecondaryThreadId(codexMessageThreadId(message))) {
|
|
5452
|
+
return { events };
|
|
5453
|
+
}
|
|
4877
5454
|
const telemetry = parseCodexTelemetryEvent(message);
|
|
4878
5455
|
if (telemetry) {
|
|
4879
|
-
const telemetrySessionId =
|
|
5456
|
+
const telemetrySessionId = codexMessageThreadId(message);
|
|
4880
5457
|
const telemetryTurnId = nonEmptyString2(message.params?.turnId) ?? nonEmptyString2(message.params?.turn?.id);
|
|
4881
5458
|
if (telemetrySessionId) {
|
|
4882
5459
|
this.currentThreadId = telemetrySessionId;
|
|
@@ -4907,7 +5484,7 @@ var CodexEventNormalizer = class {
|
|
|
4907
5484
|
const turnId = message.params?.turn?.id;
|
|
4908
5485
|
this.turnState.markTurnStarted(typeof turnId === "string" ? turnId : null);
|
|
4909
5486
|
events.push({ kind: "thinking", text: "" });
|
|
4910
|
-
|
|
5487
|
+
return { events, turnStarted: true };
|
|
4911
5488
|
}
|
|
4912
5489
|
case "item/agentMessage/delta": {
|
|
4913
5490
|
const delta = message.params?.delta;
|
|
@@ -4921,8 +5498,7 @@ var CodexEventNormalizer = class {
|
|
|
4921
5498
|
}
|
|
4922
5499
|
break;
|
|
4923
5500
|
}
|
|
4924
|
-
case "item/reasoning/summaryTextDelta":
|
|
4925
|
-
case "item/reasoning/textDelta": {
|
|
5501
|
+
case "item/reasoning/summaryTextDelta": {
|
|
4926
5502
|
const delta = message.params?.delta;
|
|
4927
5503
|
const itemId = message.params?.itemId;
|
|
4928
5504
|
if (typeof itemId === "string") {
|
|
@@ -4934,6 +5510,41 @@ var CodexEventNormalizer = class {
|
|
|
4934
5510
|
}
|
|
4935
5511
|
break;
|
|
4936
5512
|
}
|
|
5513
|
+
case "item/reasoning/textDelta": {
|
|
5514
|
+
const delta = message.params?.delta;
|
|
5515
|
+
if (typeof delta === "string" && delta.length > 0) {
|
|
5516
|
+
this.turnState.markProgress();
|
|
5517
|
+
events.push(codexNotificationProgressEvent("reasoning_text_delta", {
|
|
5518
|
+
itemId: message.params?.itemId,
|
|
5519
|
+
bytes: Buffer.byteLength(delta, "utf8")
|
|
5520
|
+
}));
|
|
5521
|
+
}
|
|
5522
|
+
break;
|
|
5523
|
+
}
|
|
5524
|
+
case "item/commandExecution/outputDelta":
|
|
5525
|
+
case "item/mcpToolCall/progress":
|
|
5526
|
+
case "item/plan/delta":
|
|
5527
|
+
case "turn/plan/updated":
|
|
5528
|
+
case "turn/diff/updated":
|
|
5529
|
+
case "item/fileChange/patchUpdated":
|
|
5530
|
+
case "item/fileChange/outputDelta":
|
|
5531
|
+
case "command/exec/outputDelta":
|
|
5532
|
+
case "process/outputDelta":
|
|
5533
|
+
case "process/exited": {
|
|
5534
|
+
this.turnState.markProgress();
|
|
5535
|
+
events.push(codexNotificationProgressEvent(message.method, message.params));
|
|
5536
|
+
break;
|
|
5537
|
+
}
|
|
5538
|
+
case "configWarning":
|
|
5539
|
+
case "warning":
|
|
5540
|
+
case "guardianWarning":
|
|
5541
|
+
case "deprecationNotice": {
|
|
5542
|
+
const diagnostic = codexNotificationDiagnosticEvent(message);
|
|
5543
|
+
if (diagnostic) {
|
|
5544
|
+
events.push(diagnostic);
|
|
5545
|
+
}
|
|
5546
|
+
break;
|
|
5547
|
+
}
|
|
4937
5548
|
case "item/started":
|
|
4938
5549
|
case "item/completed": {
|
|
4939
5550
|
const item = message.params?.item;
|
|
@@ -4944,7 +5555,7 @@ var CodexEventNormalizer = class {
|
|
|
4944
5555
|
switch (itemType) {
|
|
4945
5556
|
case "reasoning":
|
|
4946
5557
|
if (isCompleted && typeof item.id === "string" && !this.streamedReasoningIds.has(item.id)) {
|
|
4947
|
-
const text =
|
|
5558
|
+
const text = joinReasoningSummaryText(item);
|
|
4948
5559
|
if (text) {
|
|
4949
5560
|
this.turnState.markProgress();
|
|
4950
5561
|
events.push({ kind: "thinking", text });
|
|
@@ -4980,14 +5591,45 @@ var CodexEventNormalizer = class {
|
|
|
4980
5591
|
events.push({ kind: "compaction_finished" });
|
|
4981
5592
|
}
|
|
4982
5593
|
break;
|
|
5594
|
+
case "enteredReviewMode":
|
|
5595
|
+
if (isStarted) {
|
|
5596
|
+
events.push({ kind: "review_started" });
|
|
5597
|
+
}
|
|
5598
|
+
break;
|
|
5599
|
+
case "exitedReviewMode":
|
|
5600
|
+
if (isCompleted) {
|
|
5601
|
+
events.push({ kind: "review_finished" });
|
|
5602
|
+
}
|
|
5603
|
+
break;
|
|
4983
5604
|
case "fileChange":
|
|
4984
5605
|
if (isStarted && Array.isArray(item.changes)) {
|
|
5606
|
+
let outputCount = 0;
|
|
4985
5607
|
for (const change of item.changes) {
|
|
4986
5608
|
events.push({
|
|
4987
5609
|
kind: "tool_call",
|
|
4988
5610
|
name: "file_change",
|
|
4989
5611
|
input: { path: change?.path, kind: change?.kind }
|
|
4990
5612
|
});
|
|
5613
|
+
outputCount += 1;
|
|
5614
|
+
}
|
|
5615
|
+
if (outputCount > 0 && typeof item.id === "string") {
|
|
5616
|
+
this.fileChangeToolCallCounts.set(item.id, outputCount);
|
|
5617
|
+
}
|
|
5618
|
+
}
|
|
5619
|
+
if (isCompleted) {
|
|
5620
|
+
let outputCount = 0;
|
|
5621
|
+
if (typeof item.id === "string") {
|
|
5622
|
+
outputCount = this.fileChangeToolCallCounts.get(item.id) ?? 0;
|
|
5623
|
+
this.fileChangeToolCallCounts.delete(item.id);
|
|
5624
|
+
}
|
|
5625
|
+
if (outputCount === 0 && Array.isArray(item.changes)) {
|
|
5626
|
+
outputCount = item.changes.length;
|
|
5627
|
+
}
|
|
5628
|
+
for (let index = 0; index < outputCount; index += 1) {
|
|
5629
|
+
events.push({ kind: "tool_output", name: "file_change" });
|
|
5630
|
+
}
|
|
5631
|
+
if (outputCount > 0) {
|
|
5632
|
+
this.turnState.markToolBoundary();
|
|
4991
5633
|
}
|
|
4992
5634
|
}
|
|
4993
5635
|
break;
|
|
@@ -5007,6 +5649,7 @@ var CodexEventNormalizer = class {
|
|
|
5007
5649
|
events.push({ kind: "tool_call", name: "collab_tool_call", input: { tool: item.tool, prompt: item.prompt } });
|
|
5008
5650
|
}
|
|
5009
5651
|
if (isCompleted) {
|
|
5652
|
+
events.push({ kind: "tool_output", name: "collab_tool_call" });
|
|
5010
5653
|
this.turnState.markToolBoundary();
|
|
5011
5654
|
}
|
|
5012
5655
|
break;
|
|
@@ -5015,6 +5658,7 @@ var CodexEventNormalizer = class {
|
|
|
5015
5658
|
events.push({ kind: "tool_call", name: "web_search", input: { query: item.query } });
|
|
5016
5659
|
}
|
|
5017
5660
|
if (isCompleted) {
|
|
5661
|
+
events.push({ kind: "tool_output", name: "web_search" });
|
|
5018
5662
|
this.turnState.markToolBoundary();
|
|
5019
5663
|
}
|
|
5020
5664
|
break;
|
|
@@ -5023,24 +5667,37 @@ var CodexEventNormalizer = class {
|
|
|
5023
5667
|
}
|
|
5024
5668
|
case "turn/completed": {
|
|
5025
5669
|
const turn = message.params?.turn;
|
|
5026
|
-
if (turn?.status === "failed"
|
|
5027
|
-
events.push({ kind: "error", message: turn.error
|
|
5670
|
+
if (turn?.status === "failed") {
|
|
5671
|
+
events.push({ kind: "error", message: turn.error?.message || "Codex turn failed" });
|
|
5672
|
+
}
|
|
5673
|
+
if (turn?.status === "interrupted") {
|
|
5674
|
+
const detail = typeof turn?.error?.message === "string" && turn.error.message.length > 0 ? `: ${turn.error.message}` : "";
|
|
5675
|
+
events.push({ kind: "error", message: `Codex turn interrupted${detail}` });
|
|
5028
5676
|
}
|
|
5029
5677
|
this.turnState.markTurnCompleted();
|
|
5030
5678
|
this.streamedAgentMessageIds.clear();
|
|
5031
5679
|
this.streamedReasoningIds.clear();
|
|
5680
|
+
this.fileChangeToolCallCounts.clear();
|
|
5032
5681
|
events.push({ kind: "turn_end", sessionId: this.currentThreadId || void 0 });
|
|
5033
5682
|
break;
|
|
5034
5683
|
}
|
|
5035
5684
|
case "error":
|
|
5036
|
-
|
|
5037
|
-
|
|
5038
|
-
|
|
5039
|
-
}
|
|
5685
|
+
if (message.params?.willRetry === true) {
|
|
5686
|
+
this.turnState.markProgress();
|
|
5687
|
+
events.push(codexNotificationProgressEvent("retryable_error", message.params));
|
|
5688
|
+
} else {
|
|
5689
|
+
events.push({
|
|
5690
|
+
kind: "error",
|
|
5691
|
+
message: getCodexNotificationErrorMessage(message.params) || "Unknown Codex app-server error"
|
|
5692
|
+
});
|
|
5693
|
+
}
|
|
5040
5694
|
break;
|
|
5041
5695
|
}
|
|
5042
5696
|
return { events };
|
|
5043
5697
|
}
|
|
5698
|
+
isSecondaryThreadId(threadId) {
|
|
5699
|
+
return Boolean(threadId && this.currentThreadId && threadId !== this.currentThreadId);
|
|
5700
|
+
}
|
|
5044
5701
|
handleThreadReady(threadId, events) {
|
|
5045
5702
|
this.currentThreadId = threadId;
|
|
5046
5703
|
if (!this.sessionAnnounced) {
|
|
@@ -5052,20 +5709,6 @@ var CodexEventNormalizer = class {
|
|
|
5052
5709
|
};
|
|
5053
5710
|
|
|
5054
5711
|
// src/drivers/codex.ts
|
|
5055
|
-
function ensureGitRepoForCodex(workingDirectory, deps = {}) {
|
|
5056
|
-
const existsSyncFn = deps.existsSyncFn ?? existsSync5;
|
|
5057
|
-
const execSyncFn = deps.execSyncFn ?? execSync;
|
|
5058
|
-
const gitDir = path6.join(workingDirectory, ".git");
|
|
5059
|
-
if (existsSyncFn(gitDir)) return;
|
|
5060
|
-
execSyncFn("git init", { cwd: workingDirectory, stdio: "pipe" });
|
|
5061
|
-
execSyncFn(
|
|
5062
|
-
"git -c user.name=slock -c user.email=slock@local -c commit.gpgsign=false add -A && git -c user.name=slock -c user.email=slock@local -c commit.gpgsign=false commit --allow-empty -m 'init'",
|
|
5063
|
-
{
|
|
5064
|
-
cwd: workingDirectory,
|
|
5065
|
-
stdio: "pipe"
|
|
5066
|
-
}
|
|
5067
|
-
);
|
|
5068
|
-
}
|
|
5069
5712
|
var CODEX_DESKTOP_BUNDLE_PATH = "/Applications/Codex.app/Contents/Resources/codex";
|
|
5070
5713
|
function isWindowsSandboxRunner(commandPath) {
|
|
5071
5714
|
return path6.basename(commandPath).toLowerCase().startsWith("codex-command-runner");
|
|
@@ -5144,27 +5787,68 @@ function probeCodex(deps = {}) {
|
|
|
5144
5787
|
}
|
|
5145
5788
|
function resolveCodexSpawn(commandArgs, deps = {}) {
|
|
5146
5789
|
if ((deps.platform ?? process.platform) !== "win32") {
|
|
5147
|
-
return { command: resolveCodexCommand(deps) ?? "codex", args: commandArgs };
|
|
5790
|
+
return { command: resolveCodexCommand(deps) ?? "codex", args: commandArgs, shell: false };
|
|
5148
5791
|
}
|
|
5149
5792
|
const codexEntry = resolveWindowsNpmCodexEntry(deps);
|
|
5150
5793
|
if (codexEntry) {
|
|
5151
5794
|
return {
|
|
5152
5795
|
command: process.execPath,
|
|
5153
|
-
args: [codexEntry, ...commandArgs]
|
|
5796
|
+
args: [codexEntry, ...commandArgs],
|
|
5797
|
+
shell: false
|
|
5154
5798
|
};
|
|
5155
5799
|
}
|
|
5156
5800
|
const command = resolveCommandOnPath("codex", deps);
|
|
5157
5801
|
if (command && !isWindowsSandboxRunner(command)) {
|
|
5158
|
-
return { command, args: commandArgs };
|
|
5802
|
+
return { command, args: commandArgs, shell: requiresWindowsShell(command, deps.platform) };
|
|
5159
5803
|
}
|
|
5160
5804
|
const desktopEntry = resolveWindowsCodexDesktopEntry(deps);
|
|
5161
5805
|
if (desktopEntry) {
|
|
5162
|
-
return { command: desktopEntry, args: commandArgs };
|
|
5806
|
+
return { command: desktopEntry, args: commandArgs, shell: false };
|
|
5163
5807
|
}
|
|
5164
5808
|
throw new Error(
|
|
5165
5809
|
"Cannot resolve Codex CLI entry point on Windows. Install Codex Desktop or install @openai/codex globally via npm (npm i -g @openai/codex). Ignoring .codex/.sandbox-bin/codex-command-runner because it is a sandbox helper, not the Codex CLI."
|
|
5166
5810
|
);
|
|
5167
5811
|
}
|
|
5812
|
+
function isCodexMissingRolloutError(message) {
|
|
5813
|
+
return /\bno\s+rollout\s+found\b/i.test(message) || /\bmissing\s+rollout\b/i.test(message) || /\brollout\b.*\b(not found|missing)\b/i.test(message) || /\bthread\b.*\b(not found|missing)\b/i.test(message) || /\bmissing\s+thread\b/i.test(message);
|
|
5814
|
+
}
|
|
5815
|
+
function classifyCodexResumeError(message) {
|
|
5816
|
+
if (isCodexMissingRolloutError(message)) {
|
|
5817
|
+
return {
|
|
5818
|
+
kind: "missing_rollout",
|
|
5819
|
+
resumeErrorClass: "missing_rollout",
|
|
5820
|
+
recoveryAction: "fallback_fresh_thread",
|
|
5821
|
+
telemetry: {
|
|
5822
|
+
kind: "telemetry",
|
|
5823
|
+
name: "recovery",
|
|
5824
|
+
source: "codex_resume_missing_rollout",
|
|
5825
|
+
attrs: {
|
|
5826
|
+
resume_error_class: "missing_rollout",
|
|
5827
|
+
recovery_action: "fallback_fresh_thread"
|
|
5828
|
+
}
|
|
5829
|
+
}
|
|
5830
|
+
};
|
|
5831
|
+
}
|
|
5832
|
+
if (/\b(no\s+permission|permission\s+denied|forbidden|unauthorized)\b/i.test(message)) {
|
|
5833
|
+
return {
|
|
5834
|
+
kind: "terminal_error",
|
|
5835
|
+
resumeErrorClass: "permission_denied"
|
|
5836
|
+
};
|
|
5837
|
+
}
|
|
5838
|
+
return {
|
|
5839
|
+
kind: "terminal_error",
|
|
5840
|
+
resumeErrorClass: "unknown"
|
|
5841
|
+
};
|
|
5842
|
+
}
|
|
5843
|
+
function hasJsonRpcField(message, field) {
|
|
5844
|
+
return Object.prototype.hasOwnProperty.call(message, field);
|
|
5845
|
+
}
|
|
5846
|
+
function isJsonRpcResponse(message) {
|
|
5847
|
+
return message.id !== void 0 && (hasJsonRpcField(message, "result") || hasJsonRpcField(message, "error"));
|
|
5848
|
+
}
|
|
5849
|
+
function isCodexServerRequest(message) {
|
|
5850
|
+
return message.id !== void 0 && typeof message.method === "string" && !isJsonRpcResponse(message);
|
|
5851
|
+
}
|
|
5168
5852
|
var CodexDriver = class {
|
|
5169
5853
|
id = "codex";
|
|
5170
5854
|
lifecycle = {
|
|
@@ -5176,6 +5860,7 @@ var CodexDriver = class {
|
|
|
5176
5860
|
chat: "slock_cli",
|
|
5177
5861
|
runtimeControl: "none"
|
|
5178
5862
|
};
|
|
5863
|
+
stdoutChannel = "structured_protocol";
|
|
5179
5864
|
session = {
|
|
5180
5865
|
recovery: "resume_or_fresh"
|
|
5181
5866
|
};
|
|
@@ -5183,6 +5868,8 @@ var CodexDriver = class {
|
|
|
5183
5868
|
detectedModelsVerifiedAs: "launchable",
|
|
5184
5869
|
toLaunchSpec: (modelId) => ({ params: { model: modelId } })
|
|
5185
5870
|
};
|
|
5871
|
+
startupReadiness = "initial_turn";
|
|
5872
|
+
requiresSessionInitForDelivery = true;
|
|
5186
5873
|
supportsStdinNotification = true;
|
|
5187
5874
|
busyDeliveryMode = "direct";
|
|
5188
5875
|
supportsNativeStandingPrompt = true;
|
|
@@ -5229,25 +5916,32 @@ var CodexDriver = class {
|
|
|
5229
5916
|
pendingInitialPrompt = null;
|
|
5230
5917
|
initializeRequestId = null;
|
|
5231
5918
|
pendingThreadRequest = null;
|
|
5919
|
+
pendingThreadRequestId = null;
|
|
5920
|
+
pendingThreadRequestMethod = null;
|
|
5921
|
+
pendingResumeFallbackParams = null;
|
|
5922
|
+
pendingInitialTurnRequestId = null;
|
|
5232
5923
|
initialTurnStarted = false;
|
|
5233
5924
|
normalizer = new CodexEventNormalizer();
|
|
5234
5925
|
async spawn(ctx) {
|
|
5235
|
-
ensureGitRepoForCodex(ctx.workingDirectory);
|
|
5236
5926
|
const { spawnEnv } = await prepareCliTransport(ctx, { NO_COLOR: "1" });
|
|
5237
5927
|
this.process = null;
|
|
5238
5928
|
this.requestId = 0;
|
|
5239
5929
|
this.pendingInitialPrompt = ctx.prompt;
|
|
5240
5930
|
this.initializeRequestId = null;
|
|
5241
5931
|
this.pendingThreadRequest = null;
|
|
5932
|
+
this.pendingThreadRequestId = null;
|
|
5933
|
+
this.pendingThreadRequestMethod = null;
|
|
5934
|
+
this.pendingResumeFallbackParams = null;
|
|
5935
|
+
this.pendingInitialTurnRequestId = null;
|
|
5242
5936
|
this.initialTurnStarted = false;
|
|
5243
|
-
this.normalizer.reset(
|
|
5937
|
+
this.normalizer.reset();
|
|
5244
5938
|
const args = ["app-server", "--listen", "stdio://"];
|
|
5245
|
-
const { command, args: spawnArgs } = resolveCodexSpawn(args);
|
|
5939
|
+
const { command, args: spawnArgs, shell } = resolveCodexSpawn(args);
|
|
5246
5940
|
const proc = spawn2(command, spawnArgs, {
|
|
5247
5941
|
cwd: ctx.workingDirectory,
|
|
5248
5942
|
stdio: ["pipe", "pipe", "pipe"],
|
|
5249
5943
|
env: spawnEnv,
|
|
5250
|
-
shell
|
|
5944
|
+
shell
|
|
5251
5945
|
});
|
|
5252
5946
|
this.process = proc;
|
|
5253
5947
|
queueMicrotask(() => {
|
|
@@ -5265,24 +5959,77 @@ var CodexDriver = class {
|
|
|
5265
5959
|
return [];
|
|
5266
5960
|
}
|
|
5267
5961
|
const events = [];
|
|
5268
|
-
if (message
|
|
5962
|
+
if (isCodexServerRequest(message)) {
|
|
5963
|
+
this.sendErrorResponse(
|
|
5964
|
+
message.id,
|
|
5965
|
+
`Codex app-server request "${message.method}" is not supported by the non-interactive Slock daemon`
|
|
5966
|
+
);
|
|
5967
|
+
return events;
|
|
5968
|
+
}
|
|
5969
|
+
const isResponse = isJsonRpcResponse(message);
|
|
5970
|
+
if (isResponse && hasJsonRpcField(message, "result")) {
|
|
5269
5971
|
if (message.id === this.initializeRequestId) {
|
|
5270
5972
|
this.initializeRequestId = null;
|
|
5271
5973
|
this.sendNotification("initialized", {});
|
|
5272
5974
|
if (this.pendingThreadRequest) {
|
|
5273
|
-
this.
|
|
5975
|
+
this.sendThreadRequest(this.pendingThreadRequest.method, this.pendingThreadRequest.params);
|
|
5274
5976
|
this.pendingThreadRequest = null;
|
|
5275
5977
|
}
|
|
5276
5978
|
return events;
|
|
5277
5979
|
}
|
|
5278
5980
|
}
|
|
5279
|
-
if (message
|
|
5981
|
+
if (isResponse && hasJsonRpcField(message, "error") && message.id === this.initializeRequestId) {
|
|
5280
5982
|
this.initializeRequestId = null;
|
|
5281
5983
|
this.pendingThreadRequest = null;
|
|
5282
|
-
|
|
5984
|
+
this.pendingThreadRequestId = null;
|
|
5985
|
+
this.pendingThreadRequestMethod = null;
|
|
5986
|
+
this.pendingResumeFallbackParams = null;
|
|
5987
|
+
events.push({
|
|
5988
|
+
kind: "error",
|
|
5989
|
+
message: message.error?.message || "Codex app-server request failed",
|
|
5990
|
+
startupRequestMethod: "initialize"
|
|
5991
|
+
});
|
|
5992
|
+
return events;
|
|
5993
|
+
}
|
|
5994
|
+
if (isResponse && message.id === this.pendingThreadRequestId) {
|
|
5995
|
+
if (hasJsonRpcField(message, "error")) {
|
|
5996
|
+
const errorMessage = message.error?.message || "Codex app-server request failed";
|
|
5997
|
+
const requestMethod = this.pendingThreadRequestMethod;
|
|
5998
|
+
const resumeErrorClassification = requestMethod === "thread/resume" ? classifyCodexResumeError(errorMessage) : null;
|
|
5999
|
+
if (this.pendingResumeFallbackParams && resumeErrorClassification?.kind === "missing_rollout") {
|
|
6000
|
+
events.push(resumeErrorClassification.telemetry);
|
|
6001
|
+
this.sendThreadRequest("thread/start", this.pendingResumeFallbackParams);
|
|
6002
|
+
this.pendingResumeFallbackParams = null;
|
|
6003
|
+
return events;
|
|
6004
|
+
}
|
|
6005
|
+
this.pendingThreadRequestId = null;
|
|
6006
|
+
this.pendingThreadRequestMethod = null;
|
|
6007
|
+
this.pendingResumeFallbackParams = null;
|
|
6008
|
+
events.push(requestMethod ? { kind: "error", message: errorMessage, startupRequestMethod: requestMethod } : { kind: "error", message: errorMessage });
|
|
6009
|
+
return events;
|
|
6010
|
+
}
|
|
6011
|
+
this.pendingThreadRequestId = null;
|
|
6012
|
+
this.pendingThreadRequestMethod = null;
|
|
6013
|
+
this.pendingResumeFallbackParams = null;
|
|
6014
|
+
}
|
|
6015
|
+
if (isResponse && message.id === this.pendingInitialTurnRequestId) {
|
|
6016
|
+
this.pendingInitialTurnRequestId = null;
|
|
6017
|
+
if (hasJsonRpcField(message, "error")) {
|
|
6018
|
+
events.push({
|
|
6019
|
+
kind: "error",
|
|
6020
|
+
message: message.error?.message || "Codex app-server request failed",
|
|
6021
|
+
startupRequestMethod: "turn/start"
|
|
6022
|
+
});
|
|
6023
|
+
} else {
|
|
6024
|
+
this.pendingInitialPrompt = null;
|
|
6025
|
+
}
|
|
5283
6026
|
return events;
|
|
5284
6027
|
}
|
|
5285
6028
|
const result = this.normalizer.normalizeMessage(message);
|
|
6029
|
+
if (result.turnStarted) {
|
|
6030
|
+
this.pendingInitialTurnRequestId = null;
|
|
6031
|
+
this.pendingInitialPrompt = null;
|
|
6032
|
+
}
|
|
5286
6033
|
if (result.threadReady) {
|
|
5287
6034
|
this.startInitialTurn();
|
|
5288
6035
|
}
|
|
@@ -5291,10 +6038,7 @@ var CodexDriver = class {
|
|
|
5291
6038
|
get currentSessionId() {
|
|
5292
6039
|
return this.normalizer.threadId;
|
|
5293
6040
|
}
|
|
5294
|
-
encodeStdinMessage(text,
|
|
5295
|
-
if (!this.normalizer.threadId && sessionId) {
|
|
5296
|
-
this.normalizer.adoptThreadId(sessionId);
|
|
5297
|
-
}
|
|
6041
|
+
encodeStdinMessage(text, _sessionId, opts) {
|
|
5298
6042
|
if (!this.normalizer.threadId) return null;
|
|
5299
6043
|
const mode = opts?.mode || "busy";
|
|
5300
6044
|
if (mode === "busy") {
|
|
@@ -5335,11 +6079,10 @@ var CodexDriver = class {
|
|
|
5335
6079
|
return this.requestId;
|
|
5336
6080
|
}
|
|
5337
6081
|
startInitialTurn() {
|
|
5338
|
-
if (this.initialTurnStarted || !this.pendingInitialPrompt || !this.normalizer.threadId) return;
|
|
6082
|
+
if (this.initialTurnStarted || this.pendingInitialTurnRequestId !== null || !this.pendingInitialPrompt || !this.normalizer.threadId) return;
|
|
5339
6083
|
this.initialTurnStarted = true;
|
|
5340
6084
|
const prompt = this.pendingInitialPrompt;
|
|
5341
|
-
this.
|
|
5342
|
-
this.sendRequest("turn/start", {
|
|
6085
|
+
this.pendingInitialTurnRequestId = this.sendRequest("turn/start", {
|
|
5343
6086
|
threadId: this.normalizer.threadId,
|
|
5344
6087
|
input: [{ type: "text", text: prompt }]
|
|
5345
6088
|
});
|
|
@@ -5354,6 +6097,27 @@ var CodexDriver = class {
|
|
|
5354
6097
|
}) + "\n");
|
|
5355
6098
|
return id;
|
|
5356
6099
|
}
|
|
6100
|
+
sendErrorResponse(id, message) {
|
|
6101
|
+
this.process?.stdin?.write(JSON.stringify({
|
|
6102
|
+
jsonrpc: "2.0",
|
|
6103
|
+
id,
|
|
6104
|
+
error: {
|
|
6105
|
+
code: -32601,
|
|
6106
|
+
message
|
|
6107
|
+
}
|
|
6108
|
+
}) + "\n");
|
|
6109
|
+
}
|
|
6110
|
+
sendThreadRequest(method, params) {
|
|
6111
|
+
const id = this.sendRequest(method, params);
|
|
6112
|
+
this.pendingThreadRequestId = id;
|
|
6113
|
+
this.pendingThreadRequestMethod = method;
|
|
6114
|
+
this.pendingResumeFallbackParams = null;
|
|
6115
|
+
if (method === "thread/resume") {
|
|
6116
|
+
const { threadId: _threadId, ...freshParams } = params;
|
|
6117
|
+
this.pendingResumeFallbackParams = freshParams;
|
|
6118
|
+
}
|
|
6119
|
+
return id;
|
|
6120
|
+
}
|
|
5357
6121
|
sendNotification(method, params) {
|
|
5358
6122
|
this.process?.stdin?.write(JSON.stringify({
|
|
5359
6123
|
jsonrpc: "2.0",
|
|
@@ -5376,7 +6140,7 @@ function detectCodexModels(home = os3.homedir()) {
|
|
|
5376
6140
|
for (const entry of entries) {
|
|
5377
6141
|
const slug = typeof entry?.slug === "string" ? entry.slug : null;
|
|
5378
6142
|
if (!slug) continue;
|
|
5379
|
-
if (entry?.visibility && entry.visibility !== "public") continue;
|
|
6143
|
+
if (entry?.visibility && entry.visibility !== "public" && entry.visibility !== "list") continue;
|
|
5380
6144
|
if (entry?.supported_in_api === false) continue;
|
|
5381
6145
|
const label = typeof entry?.display_name === "string" && entry.display_name.length > 0 ? entry.display_name : slug;
|
|
5382
6146
|
models.push({ id: slug, label, verified: "launchable" });
|
|
@@ -6374,6 +7138,9 @@ function mapKimiSdkEventToParsedEvents(event, state) {
|
|
|
6374
7138
|
var KIMI_SDK_RUNTIME_SESSION_DESCRIPTOR = {
|
|
6375
7139
|
transport: "sdk",
|
|
6376
7140
|
lifecycle: "sdk_session",
|
|
7141
|
+
stdout: {
|
|
7142
|
+
channel: "diagnostic"
|
|
7143
|
+
},
|
|
6377
7144
|
input: {
|
|
6378
7145
|
initial: "start",
|
|
6379
7146
|
idle: "sdk_prompt",
|
|
@@ -7217,7 +7984,8 @@ var PI_SDK_COMPACTION_ENABLED = true;
|
|
|
7217
7984
|
var PI_PROVIDER_LABELS = {
|
|
7218
7985
|
google: "Google",
|
|
7219
7986
|
openai: "OpenAI",
|
|
7220
|
-
openrouter: "OpenRouter"
|
|
7987
|
+
openrouter: "OpenRouter",
|
|
7988
|
+
deepseek: "DeepSeek"
|
|
7221
7989
|
};
|
|
7222
7990
|
function createPiSdkEventMappingState(sessionId = null) {
|
|
7223
7991
|
return {
|
|
@@ -7448,6 +8216,9 @@ function mapPiSdkEventToParsedEvents(event, state) {
|
|
|
7448
8216
|
var PI_RUNTIME_SESSION_DESCRIPTOR = {
|
|
7449
8217
|
transport: "sdk",
|
|
7450
8218
|
lifecycle: "sdk_session",
|
|
8219
|
+
stdout: {
|
|
8220
|
+
channel: "diagnostic"
|
|
8221
|
+
},
|
|
7451
8222
|
input: {
|
|
7452
8223
|
initial: "start",
|
|
7453
8224
|
idle: "sdk_prompt",
|
|
@@ -7838,6 +8609,7 @@ function delay(ms) {
|
|
|
7838
8609
|
|
|
7839
8610
|
// src/drivers/runtimeSession.ts
|
|
7840
8611
|
import { EventEmitter as EventEmitter3 } from "events";
|
|
8612
|
+
import { StringDecoder } from "string_decoder";
|
|
7841
8613
|
function descriptorFromDriver(driver) {
|
|
7842
8614
|
const lifecycle = driver.lifecycle.kind === "per_turn" ? "turn_based" : "persistent_stream";
|
|
7843
8615
|
const idle = driver.supportsStdinNotification ? "stdin" : "unsupported";
|
|
@@ -7845,6 +8617,9 @@ function descriptorFromDriver(driver) {
|
|
|
7845
8617
|
return {
|
|
7846
8618
|
transport: "child_process",
|
|
7847
8619
|
lifecycle,
|
|
8620
|
+
stdout: {
|
|
8621
|
+
channel: driver.stdoutChannel ?? "diagnostic"
|
|
8622
|
+
},
|
|
7848
8623
|
input: {
|
|
7849
8624
|
initial: "start",
|
|
7850
8625
|
idle,
|
|
@@ -7869,6 +8644,7 @@ var ChildProcessRuntimeSession = class {
|
|
|
7869
8644
|
process = null;
|
|
7870
8645
|
started = false;
|
|
7871
8646
|
stdoutBuffer = "";
|
|
8647
|
+
stdoutDecoder = new StringDecoder("utf8");
|
|
7872
8648
|
requestedStopReason;
|
|
7873
8649
|
get pid() {
|
|
7874
8650
|
return this.process?.pid;
|
|
@@ -7931,7 +8707,7 @@ var ChildProcessRuntimeSession = class {
|
|
|
7931
8707
|
}
|
|
7932
8708
|
attachProcess(process2) {
|
|
7933
8709
|
process2.stdout?.on("data", (chunk) => {
|
|
7934
|
-
const chunkText =
|
|
8710
|
+
const chunkText = this.stdoutDecoder.write(chunk);
|
|
7935
8711
|
this.events.emit("stdout", chunkText);
|
|
7936
8712
|
this.stdoutBuffer += chunkText;
|
|
7937
8713
|
const lines = this.stdoutBuffer.split("\n");
|
|
@@ -8611,6 +9387,73 @@ function getMessageShortId(messageId2) {
|
|
|
8611
9387
|
return messageId2.startsWith("thread-") ? messageId2.slice(7) : messageId2.slice(0, 8);
|
|
8612
9388
|
}
|
|
8613
9389
|
var RESPONSE_TARGET_HINT = "Reply in the channel or create/reply in a thread as appropriate; use each message's `target` and `msg` fields to choose the exact target.";
|
|
9390
|
+
var SESSION_TRANSCRIPT_MAX_BYTES = 10 * 1024 * 1024;
|
|
9391
|
+
function runtimeTier(runtime) {
|
|
9392
|
+
const tier1 = /* @__PURE__ */ new Set(["claude", "codex", "kimi-sdk", "kimi", "pi"]);
|
|
9393
|
+
if (tier1.has(runtime)) return "tier1";
|
|
9394
|
+
return "tier2_or_fallback";
|
|
9395
|
+
}
|
|
9396
|
+
function redactTranscript(text) {
|
|
9397
|
+
return text.replace(/sk_(?:agent|machine|computer)_[A-Za-z0-9_-]+/g, "sk_[redacted]").replace(/sap_[A-Za-z0-9_-]+/g, "sap_[redacted]").replace(/Bearer\s+[A-Za-z0-9_\-./+=]+/g, "Bearer [redacted]").replace(/["']?auth[_-]?token["']?\s*[:=]\s*["'][^"']+["']/gi, "[redacted]").replace(/https?:\/\/[^\s\"]+/g, "[url]");
|
|
9398
|
+
}
|
|
9399
|
+
function allowedTranscriptRootsForRuntime(runtime, homeDir, workspaceDir) {
|
|
9400
|
+
const roots = [workspaceDir];
|
|
9401
|
+
switch (runtime) {
|
|
9402
|
+
case "claude":
|
|
9403
|
+
roots.push(path13.join(homeDir, ".claude"));
|
|
9404
|
+
break;
|
|
9405
|
+
case "codex":
|
|
9406
|
+
roots.push(path13.join(homeDir, ".codex"));
|
|
9407
|
+
break;
|
|
9408
|
+
case "kimi":
|
|
9409
|
+
case "kimi-sdk":
|
|
9410
|
+
roots.push(path13.join(homeDir, ".kimi"));
|
|
9411
|
+
break;
|
|
9412
|
+
case "pi":
|
|
9413
|
+
roots.push(path13.join(homeDir, ".pi"), path13.join(homeDir, ".pi", "agent"));
|
|
9414
|
+
break;
|
|
9415
|
+
}
|
|
9416
|
+
return roots;
|
|
9417
|
+
}
|
|
9418
|
+
async function isPathWithinAllowedRoots(filePath, roots) {
|
|
9419
|
+
const real = await realpath(filePath).catch(() => null);
|
|
9420
|
+
if (!real) return false;
|
|
9421
|
+
for (const root of roots) {
|
|
9422
|
+
const realRoot = await realpath(root).catch(() => null);
|
|
9423
|
+
if (!realRoot) continue;
|
|
9424
|
+
const rel = path13.relative(realRoot, real);
|
|
9425
|
+
if (!rel.startsWith("..") && !path13.isAbsolute(rel)) return true;
|
|
9426
|
+
}
|
|
9427
|
+
return false;
|
|
9428
|
+
}
|
|
9429
|
+
async function readBoundedTranscriptFile(filePath, maxBytes) {
|
|
9430
|
+
const info = await lstat(filePath);
|
|
9431
|
+
if (info.isSymbolicLink()) {
|
|
9432
|
+
throw new Error("symbolic links are not allowed");
|
|
9433
|
+
}
|
|
9434
|
+
if (!info.isFile()) {
|
|
9435
|
+
throw new Error(`not a regular file: ${filePath}`);
|
|
9436
|
+
}
|
|
9437
|
+
const fd = await open(filePath, "r");
|
|
9438
|
+
try {
|
|
9439
|
+
const toRead = Math.min(info.size, maxBytes);
|
|
9440
|
+
const buf = Buffer.alloc(toRead);
|
|
9441
|
+
await fd.read(buf, 0, toRead, 0);
|
|
9442
|
+
const truncated = info.size > maxBytes;
|
|
9443
|
+
const text = buf.toString("utf8");
|
|
9444
|
+
return { text, sizeBytes: Buffer.byteLength(text, "utf8"), truncated };
|
|
9445
|
+
} finally {
|
|
9446
|
+
await fd.close();
|
|
9447
|
+
}
|
|
9448
|
+
}
|
|
9449
|
+
async function readAndRedact(filePath, maxBytes) {
|
|
9450
|
+
try {
|
|
9451
|
+
const { text, truncated } = await readBoundedTranscriptFile(filePath, maxBytes);
|
|
9452
|
+
return { text: redactTranscript(text), truncated };
|
|
9453
|
+
} catch {
|
|
9454
|
+
return null;
|
|
9455
|
+
}
|
|
9456
|
+
}
|
|
8614
9457
|
function findSessionJsonl(root, predicate) {
|
|
8615
9458
|
let visited = 0;
|
|
8616
9459
|
const maxEntries = 1e4;
|
|
@@ -8638,6 +9481,53 @@ function findSessionJsonl(root, predicate) {
|
|
|
8638
9481
|
};
|
|
8639
9482
|
return visit(root, maxDepth);
|
|
8640
9483
|
}
|
|
9484
|
+
function findKimiSdkSessionDir(sessionId, agentId, homeDir) {
|
|
9485
|
+
const indexPath = path13.join(homeDir, ".kimi", "session_index.jsonl");
|
|
9486
|
+
try {
|
|
9487
|
+
const index = readFileSync6(indexPath, "utf8");
|
|
9488
|
+
for (const line of index.split("\n")) {
|
|
9489
|
+
if (!line.trim()) continue;
|
|
9490
|
+
try {
|
|
9491
|
+
const entry = JSON.parse(line);
|
|
9492
|
+
if (entry.sessionId === sessionId && entry.sessionDir && existsSync9(entry.sessionDir)) {
|
|
9493
|
+
return entry.sessionDir;
|
|
9494
|
+
}
|
|
9495
|
+
} catch {
|
|
9496
|
+
}
|
|
9497
|
+
}
|
|
9498
|
+
} catch {
|
|
9499
|
+
}
|
|
9500
|
+
const sessionsRoot = path13.join(homeDir, ".kimi", "sessions");
|
|
9501
|
+
try {
|
|
9502
|
+
const prefix = agentId ? `wd_${agentId}_` : `wd_`;
|
|
9503
|
+
for (const entry of readdirSync3(sessionsRoot, { withFileTypes: true })) {
|
|
9504
|
+
if (!entry.isDirectory() || !entry.name.startsWith(prefix)) continue;
|
|
9505
|
+
const candidate = path13.join(sessionsRoot, entry.name, `session_${sessionId}`);
|
|
9506
|
+
if (existsSync9(candidate)) return candidate;
|
|
9507
|
+
}
|
|
9508
|
+
} catch {
|
|
9509
|
+
}
|
|
9510
|
+
return null;
|
|
9511
|
+
}
|
|
9512
|
+
function findPiSessionFile2(sessionId, workingDirectory, homeDir) {
|
|
9513
|
+
if (workingDirectory) {
|
|
9514
|
+
const piSessionsDir = path13.join(workingDirectory, ".pi-sessions");
|
|
9515
|
+
try {
|
|
9516
|
+
const files = readdirSync3(piSessionsDir, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => ({ name: e.name, path: path13.join(piSessionsDir, e.name), stat: statSync(path13.join(piSessionsDir, e.name)) })).filter((f) => f.stat.isFile() && f.name.includes(sessionId)).sort((a, b) => b.stat.mtimeMs - a.stat.mtimeMs);
|
|
9517
|
+
if (files[0]) return files[0].path;
|
|
9518
|
+
} catch {
|
|
9519
|
+
}
|
|
9520
|
+
}
|
|
9521
|
+
const legacyRoots = [
|
|
9522
|
+
path13.join(homeDir, ".pi", "agent"),
|
|
9523
|
+
path13.join(homeDir, ".pi")
|
|
9524
|
+
];
|
|
9525
|
+
for (const root of legacyRoots) {
|
|
9526
|
+
const found = findSessionJsonl(root, (filename) => filename.endsWith(".jsonl") && filename.includes(sessionId));
|
|
9527
|
+
if (found) return found;
|
|
9528
|
+
}
|
|
9529
|
+
return null;
|
|
9530
|
+
}
|
|
8641
9531
|
function safeSessionFilename(value) {
|
|
8642
9532
|
const normalized = value.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
8643
9533
|
return normalized || "unknown-session";
|
|
@@ -8677,20 +9567,30 @@ function ensureRuntimeHomeDir(config, defaultHomeDir, workspacePath) {
|
|
|
8677
9567
|
}
|
|
8678
9568
|
return defaultHomeDir;
|
|
8679
9569
|
}
|
|
8680
|
-
function resolveRuntimeSessionRef(runtime, sessionId, homeDir = os6.homedir(), fallbackDir) {
|
|
8681
|
-
|
|
8682
|
-
|
|
8683
|
-
|
|
8684
|
-
|
|
8685
|
-
|
|
8686
|
-
|
|
8687
|
-
|
|
8688
|
-
|
|
9570
|
+
function resolveRuntimeSessionRef(runtime, sessionId, homeDir = os6.homedir(), fallbackDir, opts) {
|
|
9571
|
+
let resolvedPath = null;
|
|
9572
|
+
let lookupMethod = "none";
|
|
9573
|
+
if (runtime === "claude") {
|
|
9574
|
+
lookupMethod = "claude_jsonl";
|
|
9575
|
+
resolvedPath = findSessionJsonl(path13.join(homeDir, ".claude", "projects"), (filename) => filename === `${sessionId}.jsonl`);
|
|
9576
|
+
} else if (runtime === "codex") {
|
|
9577
|
+
lookupMethod = "codex_jsonl";
|
|
9578
|
+
resolvedPath = findSessionJsonl(path13.join(homeDir, ".codex", "sessions"), (filename) => filename.endsWith(".jsonl") && filename.includes(sessionId));
|
|
9579
|
+
} else if (runtime === "kimi-sdk" || runtime === "kimi") {
|
|
9580
|
+
lookupMethod = "kimi_sdk_index";
|
|
9581
|
+
resolvedPath = findKimiSdkSessionDir(sessionId, opts?.agentId, homeDir);
|
|
9582
|
+
} else if (runtime === "pi") {
|
|
9583
|
+
lookupMethod = "pi_jsonl";
|
|
9584
|
+
resolvedPath = findPiSessionFile2(sessionId, opts?.workingDirectory, homeDir);
|
|
8689
9585
|
}
|
|
8690
|
-
const resolvedPath = runtime === "claude" ? findSessionJsonl(path13.join(homeDir, ".claude", "projects"), (filename) => filename === `${sessionId}.jsonl`) : runtime === "codex" ? findSessionJsonl(path13.join(homeDir, ".codex", "sessions"), (filename) => filename.endsWith(".jsonl") && filename.includes(sessionId)) : null;
|
|
8691
9586
|
if (!resolvedPath && fallbackDir) {
|
|
8692
9587
|
const fallback = writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir);
|
|
8693
|
-
if (fallback)
|
|
9588
|
+
if (fallback) {
|
|
9589
|
+
return {
|
|
9590
|
+
...fallback,
|
|
9591
|
+
reason: `${fallback.reason}; attempted_lookup=${lookupMethod}`
|
|
9592
|
+
};
|
|
9593
|
+
}
|
|
8694
9594
|
}
|
|
8695
9595
|
const ref = {
|
|
8696
9596
|
label: sessionId,
|
|
@@ -8699,7 +9599,7 @@ function resolveRuntimeSessionRef(runtime, sessionId, homeDir = os6.homedir(), f
|
|
|
8699
9599
|
reachable: Boolean(resolvedPath)
|
|
8700
9600
|
};
|
|
8701
9601
|
if (!resolvedPath) {
|
|
8702
|
-
ref.reason =
|
|
9602
|
+
ref.reason = `session file path not found; attempted_lookup=${lookupMethod}`;
|
|
8703
9603
|
}
|
|
8704
9604
|
return ref;
|
|
8705
9605
|
}
|
|
@@ -8768,7 +9668,7 @@ function dynamicReplyInstruction() {
|
|
|
8768
9668
|
function dynamicClaimInstruction() {
|
|
8769
9669
|
return "claim the relevant task with `raft task claim`";
|
|
8770
9670
|
}
|
|
8771
|
-
function formatIncomingMessage(message) {
|
|
9671
|
+
function formatIncomingMessage(message, options = {}) {
|
|
8772
9672
|
const threadJoinPrefix = message.thread_join_context ? [
|
|
8773
9673
|
`[Slock thread context: you were added to a new thread via @mention.]`,
|
|
8774
9674
|
`parent: ${message.thread_join_context.parent_target}`,
|
|
@@ -8786,13 +9686,19 @@ function formatIncomingMessage(message) {
|
|
|
8786
9686
|
const msgId = message.message_id ? getMessageShortId(message.message_id) : "-";
|
|
8787
9687
|
const time = message.timestamp ? toLocalTime(message.timestamp) : "-";
|
|
8788
9688
|
const senderType = formatVisibleActorType(message.sender_type);
|
|
8789
|
-
const attachSuffix = message.attachments
|
|
9689
|
+
const attachSuffix = formatAttachmentSuffix(message.attachments, options.attachmentHint);
|
|
8790
9690
|
const taskSuffix = message.task_status ? ` [task #${message.task_number} status=${message.task_status}${message.task_assignee_id ? ` assignee=${formatTaskAssigneeType(message.task_assignee_type)}:${message.task_assignee_id}` : ""}]` : "";
|
|
8791
9691
|
const lineageSuffix = formatProducerFactLineageBracket(message.producerFactId);
|
|
8792
9692
|
const body = `[target=${target} msg=${msgId} time=${time}${senderType}] ${formatSenderHandle(message)}: ${message.content}${attachSuffix}${taskSuffix}${lineageSuffix}`;
|
|
8793
9693
|
return threadJoinPrefix ? `${threadJoinPrefix}
|
|
8794
9694
|
${body}` : body;
|
|
8795
9695
|
}
|
|
9696
|
+
function incomingMessageFormatOptionsForDriver(driver) {
|
|
9697
|
+
switch (driver.communication.chat) {
|
|
9698
|
+
case "slock_cli":
|
|
9699
|
+
return { attachmentHint: "slock_cli" };
|
|
9700
|
+
}
|
|
9701
|
+
}
|
|
8796
9702
|
function formatRuntimeProfileControlPrompt(messages) {
|
|
8797
9703
|
const controls = messages.map((message) => ({
|
|
8798
9704
|
message,
|
|
@@ -9263,7 +10169,6 @@ function cleanupAgentCredentialProxy(agentId, launchId) {
|
|
|
9263
10169
|
unregisterAgentCredentialProxyForLaunch({ agentId, launchId });
|
|
9264
10170
|
}
|
|
9265
10171
|
function stripManagedRunnerCredential(config) {
|
|
9266
|
-
if (!config.agentCredentialKey && !config.agentCredentialId) return config;
|
|
9267
10172
|
const { agentCredentialKey: _agentCredentialKey, agentCredentialId: _agentCredentialId, ...rest } = config;
|
|
9268
10173
|
return rest;
|
|
9269
10174
|
}
|
|
@@ -9345,6 +10250,68 @@ function summarizeCrash(code, signal) {
|
|
|
9345
10250
|
if (typeof code === "number") return `exit code ${code}`;
|
|
9346
10251
|
return "unknown exit";
|
|
9347
10252
|
}
|
|
10253
|
+
function closeSatisfiedTurnBoundary(ap, expectedTermination) {
|
|
10254
|
+
if (expectedTermination) return true;
|
|
10255
|
+
if (ap.runtime.descriptor.turnBoundary === "process_exit") return true;
|
|
10256
|
+
return !ap.runtimeTraceSpan;
|
|
10257
|
+
}
|
|
10258
|
+
function isStartupRequestErrorEvent(event) {
|
|
10259
|
+
return event.kind === "error" && !!event.startupRequestMethod;
|
|
10260
|
+
}
|
|
10261
|
+
function runtimeDiagnosticTitle(event) {
|
|
10262
|
+
switch (event.itemType) {
|
|
10263
|
+
case "configWarning":
|
|
10264
|
+
return "Codex config warning";
|
|
10265
|
+
case "guardianWarning":
|
|
10266
|
+
return "Codex guardian warning";
|
|
10267
|
+
case "deprecationNotice":
|
|
10268
|
+
return "Codex deprecation notice";
|
|
10269
|
+
default:
|
|
10270
|
+
return "Codex warning";
|
|
10271
|
+
}
|
|
10272
|
+
}
|
|
10273
|
+
function summarizeDiagnosticRange(range) {
|
|
10274
|
+
if (range === void 0 || range === null) return null;
|
|
10275
|
+
try {
|
|
10276
|
+
const json = JSON.stringify(range);
|
|
10277
|
+
if (!json) return null;
|
|
10278
|
+
return json.length <= 300 ? json : `${json.slice(0, 299)}\u2026`;
|
|
10279
|
+
} catch {
|
|
10280
|
+
return null;
|
|
10281
|
+
}
|
|
10282
|
+
}
|
|
10283
|
+
function runtimeDiagnosticTrajectoryEntry(event) {
|
|
10284
|
+
const lines = [event.message];
|
|
10285
|
+
if (event.details && event.details !== event.message) {
|
|
10286
|
+
lines.push(event.details);
|
|
10287
|
+
}
|
|
10288
|
+
if (event.path) {
|
|
10289
|
+
lines.push(`Path: ${event.path}`);
|
|
10290
|
+
}
|
|
10291
|
+
const range = summarizeDiagnosticRange(event.range);
|
|
10292
|
+
if (range) {
|
|
10293
|
+
lines.push(`Range: ${range}`);
|
|
10294
|
+
}
|
|
10295
|
+
return {
|
|
10296
|
+
kind: "system",
|
|
10297
|
+
title: runtimeDiagnosticTitle(event),
|
|
10298
|
+
text: lines.join("\n")
|
|
10299
|
+
};
|
|
10300
|
+
}
|
|
10301
|
+
function runtimeDiagnosticTraceAttrs(event) {
|
|
10302
|
+
return {
|
|
10303
|
+
kind: event.kind,
|
|
10304
|
+
severity: event.severity,
|
|
10305
|
+
source: event.source,
|
|
10306
|
+
itemType: event.itemType,
|
|
10307
|
+
payloadBytes: event.payloadBytes,
|
|
10308
|
+
message_present: Boolean(event.message),
|
|
10309
|
+
details_present: Boolean(event.details),
|
|
10310
|
+
path_present: Boolean(event.path),
|
|
10311
|
+
range_present: event.range !== void 0,
|
|
10312
|
+
session_id_present: Boolean(event.sessionId)
|
|
10313
|
+
};
|
|
10314
|
+
}
|
|
9348
10315
|
function currentErrorCandidates(ap) {
|
|
9349
10316
|
return [
|
|
9350
10317
|
ap.runtimeErrorSinceProgress ? ap.lastRuntimeError : null,
|
|
@@ -9429,79 +10396,39 @@ function isPiReplayRejectedByProvider(text) {
|
|
|
9429
10396
|
return /Cannot continue from message role:\s*assistant/i.test(text);
|
|
9430
10397
|
}
|
|
9431
10398
|
function resumeSessionRuntimeLabel(runtimeId) {
|
|
10399
|
+
if (runtimeId === "codex") return "Codex";
|
|
9432
10400
|
if (runtimeId === "opencode") return "OpenCode";
|
|
9433
10401
|
if (runtimeId === "gemini") return "Gemini";
|
|
9434
10402
|
if (runtimeId === "pi") return "Pi";
|
|
9435
10403
|
return "Claude";
|
|
9436
10404
|
}
|
|
9437
|
-
function classifyActivityDetailForTrace(detail) {
|
|
9438
|
-
if (!detail) return void 0;
|
|
9439
|
-
if (detail === "Message received") return "message_received";
|
|
9440
|
-
if (detail === "Starting\u2026") return "starting";
|
|
9441
|
-
if (detail === "Running command\u2026") return "running_command";
|
|
9442
|
-
if (detail === "Checking messages\u2026") return "checking_messages";
|
|
9443
|
-
if (detail === "Compacting context") return "compacting_context";
|
|
9444
|
-
if (detail === "Context compaction finished") return "compaction_finished";
|
|
9445
|
-
if (detail === "Context compaction still running; no finish event observed") return "compaction_stale";
|
|
9446
|
-
if (detail === "Idle" || detail === "Process idle") return "idle";
|
|
9447
|
-
if (detail.startsWith("Restarting stalled ") && detail.endsWith(" runtime for queued message")) return "stalled_recovery";
|
|
9448
|
-
if (detail.startsWith("Runtime stalled: no runtime events for ")) return "runtime_stalled";
|
|
9449
|
-
return "other";
|
|
9450
|
-
}
|
|
9451
10405
|
function buildRuntimeStallDiagnostic(ap, staleForMs, staleForMinutes) {
|
|
9452
|
-
|
|
9453
|
-
|
|
9454
|
-
|
|
9455
|
-
|
|
9456
|
-
|
|
9457
|
-
|
|
9458
|
-
|
|
9459
|
-
|
|
9460
|
-
|
|
9461
|
-
|
|
9462
|
-
|
|
9463
|
-
|
|
9464
|
-
|
|
9465
|
-
|
|
9466
|
-
|
|
9467
|
-
|
|
9468
|
-
|
|
9469
|
-
|
|
9470
|
-
|
|
9471
|
-
|
|
9472
|
-
|
|
9473
|
-
|
|
9474
|
-
|
|
9475
|
-
|
|
9476
|
-
|
|
9477
|
-
|
|
9478
|
-
lastActivityDetailPresent: Boolean(ap.lastActivityDetail),
|
|
9479
|
-
lastActivityDetailKind: classifyActivityDetailForTrace(ap.lastActivityDetail),
|
|
9480
|
-
runtime: ap.config.runtime,
|
|
9481
|
-
model: ap.config.model,
|
|
9482
|
-
platform: process.platform,
|
|
9483
|
-
arch: process.arch,
|
|
9484
|
-
launchId: ap.launchId || void 0,
|
|
9485
|
-
sessionIdPresent: Boolean(ap.sessionId),
|
|
9486
|
-
inboxCount: ap.inbox.length,
|
|
9487
|
-
pendingNotificationCount: ap.notifications.pendingCount,
|
|
9488
|
-
processPidPresent: typeof ap.runtime.pid === "number",
|
|
9489
|
-
busyDeliveryMode: ap.driver.busyDeliveryMode,
|
|
9490
|
-
supportsStdinNotification: ap.driver.supportsStdinNotification,
|
|
9491
|
-
gatedPhase: ap.runtime.descriptor.busyDelivery === "gated" ? ap.gatedSteering.phase : void 0,
|
|
9492
|
-
outstandingToolUses: ap.gatedSteering.outstandingToolUses,
|
|
9493
|
-
compacting: ap.gatedSteering.compacting,
|
|
9494
|
-
recentStderrCount: ap.recentStderr.length,
|
|
9495
|
-
recentStdoutCount: ap.recentStdout.length,
|
|
9496
|
-
...runtimeTraceCounterAttrs(ap)
|
|
9497
|
-
}
|
|
9498
|
-
};
|
|
9499
|
-
}
|
|
9500
|
-
function classifyRuntimeStallReason(ap) {
|
|
9501
|
-
if (ap.runtimeProgress.lastEventKind === "tool_output" && ap.gatedSteering.outstandingToolUses === 0) {
|
|
9502
|
-
return "harness_post_tool_silent_wedge";
|
|
9503
|
-
}
|
|
9504
|
-
return "no_runtime_events";
|
|
10406
|
+
return projectApmRuntimeStallDiagnostic({
|
|
10407
|
+
staleForMs,
|
|
10408
|
+
staleForMinutes,
|
|
10409
|
+
lastActivity: ap.lastActivity,
|
|
10410
|
+
lastActivityDetail: ap.lastActivityDetail,
|
|
10411
|
+
runtimeDescriptorBusyDelivery: ap.runtime.descriptor.busyDelivery,
|
|
10412
|
+
runtimeProgressLastEventKind: ap.runtimeProgress.lastEventKind,
|
|
10413
|
+
runtime: ap.config.runtime,
|
|
10414
|
+
model: ap.config.model,
|
|
10415
|
+
platform: process.platform,
|
|
10416
|
+
arch: process.arch,
|
|
10417
|
+
launchId: ap.launchId,
|
|
10418
|
+
sessionIdPresent: Boolean(ap.sessionId),
|
|
10419
|
+
inboxCount: ap.inbox.length,
|
|
10420
|
+
pendingNotificationCount: ap.notifications.pendingCount,
|
|
10421
|
+
processPidPresent: typeof ap.runtime.pid === "number",
|
|
10422
|
+
driverBusyDeliveryMode: ap.driver.busyDeliveryMode,
|
|
10423
|
+
supportsStdinNotification: ap.driver.supportsStdinNotification,
|
|
10424
|
+
gatedPhase: ap.gatedSteering.phase,
|
|
10425
|
+
outstandingToolUses: ap.gatedSteering.outstandingToolUses,
|
|
10426
|
+
compacting: ap.gatedSteering.compacting,
|
|
10427
|
+
reviewing: ap.gatedSteering.reviewing === true ? true : void 0,
|
|
10428
|
+
recentStderrCount: ap.recentStderr.length,
|
|
10429
|
+
recentStdoutCount: ap.recentStdout.length,
|
|
10430
|
+
runtimeTraceCounterAttrs: runtimeTraceCounterAttrs(ap)
|
|
10431
|
+
});
|
|
9505
10432
|
}
|
|
9506
10433
|
function bucketBytes(value) {
|
|
9507
10434
|
const bytes = typeof value === "string" ? Buffer.byteLength(value, "utf8") : Math.max(0, Math.floor(value ?? 0));
|
|
@@ -9730,6 +10657,8 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
9730
10657
|
agentSpawnFailBackoff = /* @__PURE__ */ new Map();
|
|
9731
10658
|
daemonVersion;
|
|
9732
10659
|
computerVersion;
|
|
10660
|
+
workerUrl;
|
|
10661
|
+
fetchImpl;
|
|
9733
10662
|
constructor(sendToServer, daemonApiKey, opts) {
|
|
9734
10663
|
this.slockCliPath = opts.slockCliPath ?? "";
|
|
9735
10664
|
this.sendToServer = sendToServer;
|
|
@@ -9742,6 +10671,8 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
9742
10671
|
this.tracer = opts.tracer ?? noopTracer;
|
|
9743
10672
|
this.daemonVersion = opts.daemonVersion?.trim() || null;
|
|
9744
10673
|
this.computerVersion = opts.computerVersion?.trim() || null;
|
|
10674
|
+
this.workerUrl = opts.workerUrl?.trim() || null;
|
|
10675
|
+
this.fetchImpl = opts.fetchImpl ?? daemonFetch;
|
|
9745
10676
|
this.stdinNotificationRetryMs = Math.max(
|
|
9746
10677
|
0,
|
|
9747
10678
|
Math.floor(opts.stdinNotificationRetryMs ?? STDIN_NOTIFICATION_RETRY_DELAY_MS)
|
|
@@ -9863,13 +10794,17 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
9863
10794
|
this.sendStdinNotification(agentId);
|
|
9864
10795
|
}, delayMs);
|
|
9865
10796
|
}
|
|
10797
|
+
isApmIdle(ap) {
|
|
10798
|
+
return ap.gatedSteering.isIdle;
|
|
10799
|
+
}
|
|
9866
10800
|
flushPendingDirectStdinNotificationOnRuntimeProgress(agentId, ap, source) {
|
|
9867
10801
|
if (ap.notifications.pendingCount === 0) return false;
|
|
9868
|
-
if (ap
|
|
10802
|
+
if (this.isApmIdle(ap)) return false;
|
|
9869
10803
|
if (!ap.sessionId) return false;
|
|
9870
10804
|
if (!ap.driver.supportsStdinNotification) return false;
|
|
9871
10805
|
if (ap.runtime.descriptor.busyDelivery !== "direct") return false;
|
|
9872
10806
|
if (ap.gatedSteering.compacting) return false;
|
|
10807
|
+
if (ap.gatedSteering.reviewing) return false;
|
|
9873
10808
|
this.recordDaemonTrace("daemon.agent.stdin_notification.retry_signal", {
|
|
9874
10809
|
agentId,
|
|
9875
10810
|
runtime: ap.config.runtime,
|
|
@@ -9975,8 +10910,9 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
9975
10910
|
queueDeliveryForRuntimeErrorBackoff(agentId, ap, message) {
|
|
9976
10911
|
const remainingMs = this.runtimeErrorDeliveryBackoffRemainingMs(ap);
|
|
9977
10912
|
if (remainingMs <= 0) return false;
|
|
10913
|
+
const isIdle = this.isApmIdle(ap);
|
|
9978
10914
|
ap.inbox.push(message);
|
|
9979
|
-
if (!
|
|
10915
|
+
if (!isIdle && ap.driver.supportsStdinNotification && ap.sessionId) {
|
|
9980
10916
|
ap.notifications.add();
|
|
9981
10917
|
}
|
|
9982
10918
|
const scheduled = this.scheduleRuntimeErrorDeliveryBackoffFlush(agentId, ap);
|
|
@@ -9987,7 +10923,7 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
9987
10923
|
runtime: ap.config.runtime,
|
|
9988
10924
|
session_id_present: Boolean(ap.sessionId),
|
|
9989
10925
|
launchId: ap.launchId || void 0,
|
|
9990
|
-
is_idle:
|
|
10926
|
+
is_idle: isIdle,
|
|
9991
10927
|
inbox_count: ap.inbox.length,
|
|
9992
10928
|
pending_notification_count: ap.notifications.pendingCount,
|
|
9993
10929
|
runtime_error_backoff_remaining_ms: remainingMs,
|
|
@@ -10007,7 +10943,7 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
10007
10943
|
}
|
|
10008
10944
|
if (ap.inbox.length === 0) return false;
|
|
10009
10945
|
const reason = ap.runtimeErrorDeliveryBackoff.reason || "runtime_error_backoff";
|
|
10010
|
-
if (ap
|
|
10946
|
+
if (this.isApmIdle(ap) && ap.driver.supportsStdinNotification && ap.sessionId) {
|
|
10011
10947
|
ap.notifications.pruneContributedToPending(ap.inbox, ap.sessionId);
|
|
10012
10948
|
const messages = ap.notifications.filterUncontributedMessages(ap.inbox, ap.sessionId);
|
|
10013
10949
|
ap.notifications.clearPending();
|
|
@@ -10299,6 +11235,23 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
10299
11235
|
clientSeq: ap ? this.nextActivityClientSeq(agentId) : void 0
|
|
10300
11236
|
});
|
|
10301
11237
|
}
|
|
11238
|
+
recordRuntimeDiagnosticActivity(agentId, ap, event) {
|
|
11239
|
+
this.sendToServer({
|
|
11240
|
+
type: "agent:activity",
|
|
11241
|
+
agentId,
|
|
11242
|
+
activity: ap.lastActivity || "online",
|
|
11243
|
+
detail: ap.lastActivityDetail || "",
|
|
11244
|
+
entries: [runtimeDiagnosticTrajectoryEntry(event)],
|
|
11245
|
+
launchId: ap.launchId || void 0,
|
|
11246
|
+
clientSeq: this.nextActivityClientSeq(agentId)
|
|
11247
|
+
});
|
|
11248
|
+
this.recordDaemonTrace("daemon.runtime.diagnostic", {
|
|
11249
|
+
agentId,
|
|
11250
|
+
launchId: ap.launchId || void 0,
|
|
11251
|
+
runtime: ap.config.runtime,
|
|
11252
|
+
...runtimeDiagnosticTraceAttrs(event)
|
|
11253
|
+
});
|
|
11254
|
+
}
|
|
10302
11255
|
recordDaemonTrace(name, attrs, status = "ok", parentTraceparent) {
|
|
10303
11256
|
const span = this.tracer.startSpan(name, {
|
|
10304
11257
|
parent: parseTraceparent(parentTraceparent),
|
|
@@ -10364,6 +11317,13 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
10364
11317
|
session_id_present: Boolean(sessionId)
|
|
10365
11318
|
});
|
|
10366
11319
|
}
|
|
11320
|
+
initialAgentProcessSessionId(driver, config) {
|
|
11321
|
+
if (driver.requiresSessionInitForDelivery) return null;
|
|
11322
|
+
return config.sessionId || null;
|
|
11323
|
+
}
|
|
11324
|
+
restartSafeSessionId(ap) {
|
|
11325
|
+
return ap.sessionId || (ap.driver.requiresSessionInitForDelivery ? ap.config.sessionId || null : null);
|
|
11326
|
+
}
|
|
10367
11327
|
sameWakeMessage(left, right) {
|
|
10368
11328
|
if (!left || !right) return left === right;
|
|
10369
11329
|
if (left.message_id && right.message_id) return left.message_id === right.message_id;
|
|
@@ -10408,16 +11368,18 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
10408
11368
|
}
|
|
10409
11369
|
const previousLaunchId = ap.launchId;
|
|
10410
11370
|
const nextLaunchId = start.launchId || ap.launchId || null;
|
|
10411
|
-
const
|
|
11371
|
+
const requiresSessionInit = ap.driver.requiresSessionInitForDelivery === true;
|
|
11372
|
+
const nextSessionId = ap.sessionId || (requiresSessionInit ? null : start.config.sessionId || null);
|
|
11373
|
+
const nextConfigSessionId = nextSessionId || (requiresSessionInit ? start.config.sessionId || null : ap.config.sessionId || start.config.sessionId || null);
|
|
10412
11374
|
ap.launchId = nextLaunchId;
|
|
10413
11375
|
ap.sessionId = nextSessionId;
|
|
10414
11376
|
ap.config = {
|
|
10415
11377
|
...start.config,
|
|
10416
|
-
sessionId:
|
|
11378
|
+
sessionId: nextConfigSessionId
|
|
10417
11379
|
};
|
|
10418
11380
|
this.idleAgentConfigs.set(agentId, {
|
|
10419
|
-
config:
|
|
10420
|
-
sessionId:
|
|
11381
|
+
config: this.buildRestartSafeConfig(ap.config, nextConfigSessionId),
|
|
11382
|
+
sessionId: nextConfigSessionId,
|
|
10421
11383
|
launchId: nextLaunchId
|
|
10422
11384
|
});
|
|
10423
11385
|
this.recordStartRebind(agentId, start, reason, previousLaunchId, nextLaunchId, nextSessionId);
|
|
@@ -10623,9 +11585,53 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
10623
11585
|
return;
|
|
10624
11586
|
}
|
|
10625
11587
|
this.agentsStarting.add(agentId);
|
|
10626
|
-
this.recordDaemonTrace("daemon.agent.spawn.started", this.startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId, wakeMessageTransient));
|
|
10627
11588
|
let agentProcess = null;
|
|
11589
|
+
let pendingStartRebind;
|
|
11590
|
+
const originalLaunchId = launchId || null;
|
|
10628
11591
|
try {
|
|
11592
|
+
const agentDataDir = path13.join(this.dataDir, agentId);
|
|
11593
|
+
await mkdir(agentDataDir, { recursive: true });
|
|
11594
|
+
const initialRuntimeConfig = withLocalRuntimeContext(config, agentId, agentDataDir);
|
|
11595
|
+
const memoryMdPath = path13.join(agentDataDir, "MEMORY.md");
|
|
11596
|
+
try {
|
|
11597
|
+
await access(memoryMdPath);
|
|
11598
|
+
} catch {
|
|
11599
|
+
const initialMemoryMd = buildInitialMemoryMd(initialRuntimeConfig);
|
|
11600
|
+
await writeFile(memoryMdPath, initialMemoryMd);
|
|
11601
|
+
}
|
|
11602
|
+
const notesDir = path13.join(agentDataDir, "notes");
|
|
11603
|
+
await mkdir(notesDir, { recursive: true });
|
|
11604
|
+
if (getOnboardingSeedMode(config) === FIRST_CINDY_SEED_MODE) {
|
|
11605
|
+
const seedFiles = buildOnboardingSeedFiles();
|
|
11606
|
+
for (const { relativePath, content } of seedFiles) {
|
|
11607
|
+
const fullPath = path13.join(agentDataDir, relativePath);
|
|
11608
|
+
try {
|
|
11609
|
+
await access(fullPath);
|
|
11610
|
+
} catch {
|
|
11611
|
+
await mkdir(path13.dirname(fullPath), { recursive: true });
|
|
11612
|
+
await writeFile(fullPath, content);
|
|
11613
|
+
}
|
|
11614
|
+
}
|
|
11615
|
+
}
|
|
11616
|
+
pendingStartRebind = this.pendingStartRebinds.get(agentId);
|
|
11617
|
+
if (pendingStartRebind) {
|
|
11618
|
+
this.pendingStartRebinds.delete(agentId);
|
|
11619
|
+
const previousWakeMessage = wakeMessage;
|
|
11620
|
+
if (previousWakeMessage && pendingStartRebind.wakeMessage && !this.sameWakeMessage(previousWakeMessage, pendingStartRebind.wakeMessage)) {
|
|
11621
|
+
const pending = this.startingInboxes.get(agentId) || [];
|
|
11622
|
+
pending.push(previousWakeMessage);
|
|
11623
|
+
this.startingInboxes.set(agentId, pending);
|
|
11624
|
+
}
|
|
11625
|
+
config = pendingStartRebind.config;
|
|
11626
|
+
unreadSummary = pendingStartRebind.unreadSummary;
|
|
11627
|
+
resumePrompt = pendingStartRebind.resumePrompt;
|
|
11628
|
+
launchId = pendingStartRebind.launchId || launchId;
|
|
11629
|
+
if (pendingStartRebind.wakeMessage) {
|
|
11630
|
+
wakeMessage = pendingStartRebind.wakeMessage;
|
|
11631
|
+
wakeMessageTransient = pendingStartRebind.wakeMessageTransient === true;
|
|
11632
|
+
}
|
|
11633
|
+
}
|
|
11634
|
+
this.recordDaemonTrace("daemon.agent.spawn.started", this.startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId, wakeMessageTransient));
|
|
10629
11635
|
const driver = this.driverResolver(config.runtime || "claude");
|
|
10630
11636
|
const legacyWakeRuntimeProfile = wakeMessage ? runtimeProfileNotificationFromMessage(wakeMessage) : null;
|
|
10631
11637
|
if (legacyWakeRuntimeProfile?.kind === "migration") {
|
|
@@ -10638,8 +11644,6 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
10638
11644
|
);
|
|
10639
11645
|
wakeMessage = void 0;
|
|
10640
11646
|
}
|
|
10641
|
-
const agentDataDir = path13.join(this.dataDir, agentId);
|
|
10642
|
-
await mkdir(agentDataDir, { recursive: true });
|
|
10643
11647
|
let runtimeConfig = withLocalRuntimeContext(config, agentId, agentDataDir);
|
|
10644
11648
|
const legacyRuntimeProfileControl = runtimeConfig.runtimeProfileControl?.kind === "migration" ? runtimeConfig.runtimeProfileControl : null;
|
|
10645
11649
|
if (legacyRuntimeProfileControl) {
|
|
@@ -10652,27 +11656,6 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
10652
11656
|
);
|
|
10653
11657
|
runtimeConfig = { ...runtimeConfig, runtimeProfileControl: null };
|
|
10654
11658
|
}
|
|
10655
|
-
const memoryMdPath = path13.join(agentDataDir, "MEMORY.md");
|
|
10656
|
-
try {
|
|
10657
|
-
await access(memoryMdPath);
|
|
10658
|
-
} catch {
|
|
10659
|
-
const initialMemoryMd = buildInitialMemoryMd(runtimeConfig);
|
|
10660
|
-
await writeFile(memoryMdPath, initialMemoryMd);
|
|
10661
|
-
}
|
|
10662
|
-
const notesDir = path13.join(agentDataDir, "notes");
|
|
10663
|
-
await mkdir(notesDir, { recursive: true });
|
|
10664
|
-
if (getOnboardingSeedMode(config) === FIRST_CINDY_SEED_MODE) {
|
|
10665
|
-
const seedFiles = buildOnboardingSeedFiles();
|
|
10666
|
-
for (const { relativePath, content } of seedFiles) {
|
|
10667
|
-
const fullPath = path13.join(agentDataDir, relativePath);
|
|
10668
|
-
try {
|
|
10669
|
-
await access(fullPath);
|
|
10670
|
-
} catch {
|
|
10671
|
-
await mkdir(path13.dirname(fullPath), { recursive: true });
|
|
10672
|
-
await writeFile(fullPath, content);
|
|
10673
|
-
}
|
|
10674
|
-
}
|
|
10675
|
-
}
|
|
10676
11659
|
const isResume = !!runtimeConfig.sessionId;
|
|
10677
11660
|
const standingPrompt = driver.buildSystemPrompt(runtimeConfig, agentId);
|
|
10678
11661
|
let prompt;
|
|
@@ -10692,7 +11675,7 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
10692
11675
|
if (transientWakeMessage) {
|
|
10693
11676
|
prompt = `System notice received:
|
|
10694
11677
|
|
|
10695
|
-
${formatIncomingMessage(wakeMessage)}
|
|
11678
|
+
${formatIncomingMessage(wakeMessage, incomingMessageFormatOptionsForDriver(driver))}
|
|
10696
11679
|
|
|
10697
11680
|
Respond as appropriate. Complete all your work before stopping.
|
|
10698
11681
|
${RESPONSE_TARGET_HINT}`;
|
|
@@ -10746,20 +11729,15 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
|
|
|
10746
11729
|
sessionIdPresent: isResume,
|
|
10747
11730
|
nativeStandingPrompt: Boolean(driver.supportsNativeStandingPrompt)
|
|
10748
11731
|
});
|
|
10749
|
-
const
|
|
10750
|
-
const
|
|
10751
|
-
if (pendingStartRebind) {
|
|
10752
|
-
this.pendingStartRebinds.delete(agentId);
|
|
10753
|
-
}
|
|
10754
|
-
const effectiveLaunchId = pendingStartRebind?.launchId || launchId || null;
|
|
10755
|
-
const canDeferEmptyStart = driver.deferSpawnUntilMessage === true && !wakeMessage && !pendingStartRebind?.wakeMessage && !runtimeConfig.runtimeProfileControl && (!unreadSummary || Object.keys(unreadSummary).length === 0);
|
|
11732
|
+
const effectiveLaunchId = launchId || null;
|
|
11733
|
+
const canDeferEmptyStart = driver.deferSpawnUntilMessage === true && !wakeMessage && !runtimeConfig.runtimeProfileControl && (!unreadSummary || Object.keys(unreadSummary).length === 0);
|
|
10756
11734
|
if (canDeferEmptyStart) {
|
|
10757
11735
|
const pendingMessages = this.startingInboxes.get(agentId) || [];
|
|
10758
11736
|
this.startingInboxes.delete(agentId);
|
|
10759
11737
|
this.agentsStarting.delete(agentId);
|
|
10760
11738
|
this.idleAgentConfigs.set(agentId, {
|
|
10761
|
-
config:
|
|
10762
|
-
sessionId:
|
|
11739
|
+
config: this.buildRestartSafeConfig(runtimeConfig, runtimeConfig.sessionId || null),
|
|
11740
|
+
sessionId: runtimeConfig.sessionId || null,
|
|
10763
11741
|
launchId: effectiveLaunchId
|
|
10764
11742
|
});
|
|
10765
11743
|
this.sendAgentStatus(agentId, "active", effectiveLaunchId);
|
|
@@ -10775,6 +11753,7 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
|
|
|
10775
11753
|
}
|
|
10776
11754
|
return;
|
|
10777
11755
|
}
|
|
11756
|
+
const effectiveConfig = await this.buildSpawnConfig(agentId, runtimeConfig);
|
|
10778
11757
|
const runtimeContext = {
|
|
10779
11758
|
agentId,
|
|
10780
11759
|
config: effectiveConfig,
|
|
@@ -10789,20 +11768,28 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
|
|
|
10789
11768
|
tracer: this.tracer
|
|
10790
11769
|
};
|
|
10791
11770
|
const runtime = driver.createSession?.(runtimeContext) ?? createChildProcessRuntimeSession(driver, runtimeContext);
|
|
11771
|
+
const liveProcessConfig = {
|
|
11772
|
+
...runtimeConfig,
|
|
11773
|
+
serverUrl: effectiveConfig.serverUrl,
|
|
11774
|
+
agentCredentialKey: effectiveConfig.agentCredentialKey,
|
|
11775
|
+
agentCredentialId: effectiveConfig.agentCredentialId
|
|
11776
|
+
};
|
|
11777
|
+
const initialSessionId = this.initialAgentProcessSessionId(driver, liveProcessConfig);
|
|
11778
|
+
const restartSessionId = initialSessionId || (driver.requiresSessionInitForDelivery ? liveProcessConfig.sessionId || null : null);
|
|
10792
11779
|
agentProcess = {
|
|
10793
11780
|
runtime,
|
|
10794
11781
|
driver,
|
|
10795
11782
|
inbox: wakeMessageDeliveredAsInboxUpdate && wakeMessage ? [wakeMessage, ...startingInboxMessages] : startingInboxMessages,
|
|
10796
|
-
config:
|
|
10797
|
-
sessionId:
|
|
11783
|
+
config: liveProcessConfig,
|
|
11784
|
+
sessionId: initialSessionId,
|
|
10798
11785
|
launchId: effectiveLaunchId,
|
|
10799
11786
|
startupWakeMessage: wakeMessage,
|
|
10800
11787
|
startupUnreadSummary: unreadSummary,
|
|
10801
11788
|
startupResumePrompt: resumePrompt,
|
|
10802
|
-
isIdle: false,
|
|
10803
11789
|
notifications: new RuntimeNotificationState(),
|
|
10804
11790
|
activityHeartbeat: null,
|
|
10805
11791
|
startupTimeoutTimer: null,
|
|
11792
|
+
startupReadinessSatisfied: false,
|
|
10806
11793
|
compactionWatchdog: null,
|
|
10807
11794
|
compactionStartedAt: null,
|
|
10808
11795
|
runtimeProgress: new RuntimeProgressState(Date.now()),
|
|
@@ -10820,21 +11807,20 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
|
|
|
10820
11807
|
spawnError: null,
|
|
10821
11808
|
exitCode: null,
|
|
10822
11809
|
exitSignal: null,
|
|
10823
|
-
expectedTerminationReason: null,
|
|
10824
11810
|
stalledRecoverySigtermTimer: null,
|
|
10825
|
-
runtimeProfileTurnControl:
|
|
11811
|
+
runtimeProfileTurnControl: liveProcessConfig.runtimeProfileControl ? runtimeProfileTurnControl(liveProcessConfig.runtimeProfileControl.kind, liveProcessConfig.runtimeProfileControl.key, "agent_config") : null,
|
|
10826
11812
|
pendingTrajectory: null,
|
|
10827
11813
|
gatedSteering: createGatedSteeringState()
|
|
10828
11814
|
};
|
|
10829
11815
|
this.startingInboxes.delete(agentId);
|
|
10830
11816
|
this.agents.set(agentId, agentProcess);
|
|
10831
11817
|
this.idleAgentConfigs.set(agentId, {
|
|
10832
|
-
config:
|
|
10833
|
-
sessionId:
|
|
11818
|
+
config: this.buildRestartSafeConfig(runtimeConfig, restartSessionId),
|
|
11819
|
+
sessionId: restartSessionId,
|
|
10834
11820
|
launchId: effectiveLaunchId
|
|
10835
11821
|
});
|
|
10836
11822
|
if (pendingStartRebind) {
|
|
10837
|
-
this.recordStartRebind(agentId, pendingStartRebind, "startup_registered",
|
|
11823
|
+
this.recordStartRebind(agentId, pendingStartRebind, "startup_registered", originalLaunchId, effectiveLaunchId, agentProcess.sessionId);
|
|
10838
11824
|
}
|
|
10839
11825
|
this.startRuntimeTrace(agentId, agentProcess, "spawn", wakeMessage ? [wakeMessage] : void 0, runtimeInputTraceAttrs);
|
|
10840
11826
|
this.agentsStarting.delete(agentId);
|
|
@@ -10848,6 +11834,7 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
|
|
|
10848
11834
|
this.ackInjectedRuntimeProfileMessages(agentId, [wakeMessage], agentProcess.launchId);
|
|
10849
11835
|
}
|
|
10850
11836
|
runtime.on("stdout", (chunkText) => {
|
|
11837
|
+
if (runtime.descriptor.stdout.channel === "structured_protocol") return;
|
|
10851
11838
|
const current = this.agents.get(agentId);
|
|
10852
11839
|
if (current) {
|
|
10853
11840
|
current.recentStdout = pushRecentStdout(current.recentStdout, chunkText);
|
|
@@ -10935,17 +11922,24 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
|
|
|
10935
11922
|
this.clearStalledRecoverySigtermWatchdog(ap);
|
|
10936
11923
|
const finalCode = ap.exitCode ?? code;
|
|
10937
11924
|
const finalSignal = ap.exitSignal ?? signal;
|
|
10938
|
-
const
|
|
10939
|
-
const
|
|
11925
|
+
const expectedTerminationReason = ap.gatedSteering.expectedTerminationReason;
|
|
11926
|
+
const startupTimeoutTermination = expectedTerminationReason === "startup_timeout";
|
|
11927
|
+
const startupRequestErrorTermination = expectedTerminationReason === "startup_request_error";
|
|
11928
|
+
const expectedTermination = Boolean(expectedTerminationReason);
|
|
10940
11929
|
const stickyTerminalFailureDetail = classifyStickyTerminalFailure(ap);
|
|
10941
|
-
const
|
|
11930
|
+
const turnBoundarySatisfied = closeSatisfiedTurnBoundary(ap, expectedTermination);
|
|
11931
|
+
const closeBeforeTurnBoundary = !turnBoundarySatisfied;
|
|
11932
|
+
const processEndedCleanly = !stickyTerminalFailureDetail && !startupTimeoutTermination && !startupRequestErrorTermination && (finalCode === 0 && turnBoundarySatisfied || expectedTermination && !ap.lastRuntimeError);
|
|
10942
11933
|
const terminalFailureDetail = processEndedCleanly ? null : stickyTerminalFailureDetail ?? classifyTerminalFailure(ap);
|
|
10943
11934
|
const resumeRecoveryReason = resumeSessionRecoveryReason(ap);
|
|
10944
11935
|
const shouldColdStartResumeSession = resumeRecoveryReason !== null;
|
|
10945
11936
|
const summary = summarizeCrash(finalCode, finalSignal);
|
|
10946
11937
|
this.endRuntimeTrace(ap, processEndedCleanly ? "ok" : "error", {
|
|
10947
11938
|
outcome: processEndedCleanly ? "process-exit" : "process-crash",
|
|
10948
|
-
expectedTerminationReason:
|
|
11939
|
+
expectedTerminationReason: expectedTerminationReason || void 0,
|
|
11940
|
+
runtime_turn_boundary: ap.runtime.descriptor.turnBoundary,
|
|
11941
|
+
runtime_turn_boundary_satisfied: turnBoundarySatisfied,
|
|
11942
|
+
runtime_exit_before_turn_boundary: closeBeforeTurnBoundary || void 0,
|
|
10949
11943
|
exitCode: finalCode,
|
|
10950
11944
|
exitSignal: finalSignal,
|
|
10951
11945
|
...runtimeTraceCounterAttrs(ap),
|
|
@@ -10958,7 +11952,7 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
|
|
|
10958
11952
|
if (shouldColdStartResumeSession) {
|
|
10959
11953
|
const staleSessionId = ap.sessionId;
|
|
10960
11954
|
const runtimeLabel = resumeSessionRuntimeLabel(ap.driver.id);
|
|
10961
|
-
const restartConfig =
|
|
11955
|
+
const restartConfig = this.buildRestartSafeConfig(ap.config, null);
|
|
10962
11956
|
const reasonText = resumeRecoveryReason === "provider_replay_rejected" ? "was rejected by the provider during replay" : "is unavailable locally";
|
|
10963
11957
|
const activityText = resumeRecoveryReason === "provider_replay_rejected" ? `Stored ${runtimeLabel} session replay rejected; cold-starting a new session\u2026` : `Stored ${runtimeLabel} session missing; cold-starting a new session\u2026`;
|
|
10964
11958
|
logger.warn(
|
|
@@ -10989,7 +11983,7 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
|
|
|
10989
11983
|
}
|
|
10990
11984
|
if (processEndedCleanly) {
|
|
10991
11985
|
let queuedWakeMessage;
|
|
10992
|
-
if (!ap.driver.supportsStdinNotification ||
|
|
11986
|
+
if (!ap.driver.supportsStdinNotification || expectedTerminationReason === "stalled_recovery") {
|
|
10993
11987
|
while (ap.inbox.length > 0) {
|
|
10994
11988
|
const candidate = ap.inbox.shift();
|
|
10995
11989
|
if (this.shouldDeferWakeMessage(agentId, ap.driver, candidate)) continue;
|
|
@@ -11000,7 +11994,7 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
|
|
|
11000
11994
|
const unreadSummary2 = queuedWakeMessage ? buildUnreadSummary(ap.inbox, formatChannelLabel(queuedWakeMessage)) : void 0;
|
|
11001
11995
|
if (queuedWakeMessage) {
|
|
11002
11996
|
logger.info(`[Agent ${agentId}] Turn completed; restarting immediately for queued message`);
|
|
11003
|
-
const nextConfig =
|
|
11997
|
+
const nextConfig = this.buildRestartSafeConfig(ap.config, ap.sessionId);
|
|
11004
11998
|
this.idleAgentConfigs.set(agentId, {
|
|
11005
11999
|
config: nextConfig,
|
|
11006
12000
|
sessionId: ap.sessionId,
|
|
@@ -11023,7 +12017,7 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
|
|
|
11023
12017
|
return;
|
|
11024
12018
|
}
|
|
11025
12019
|
this.idleAgentConfigs.set(agentId, {
|
|
11026
|
-
config:
|
|
12020
|
+
config: this.buildRestartSafeConfig(ap.config, ap.sessionId),
|
|
11027
12021
|
sessionId: ap.sessionId,
|
|
11028
12022
|
launchId: ap.launchId
|
|
11029
12023
|
});
|
|
@@ -11035,7 +12029,7 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
|
|
|
11035
12029
|
const reason = formatCrashReason(finalCode, finalSignal, ap);
|
|
11036
12030
|
if (terminalFailureDetail && isProviderStreamFailureText(terminalFailureDetail.detail)) {
|
|
11037
12031
|
this.idleAgentConfigs.set(agentId, {
|
|
11038
|
-
config:
|
|
12032
|
+
config: this.buildRestartSafeConfig(ap.config, ap.sessionId),
|
|
11039
12033
|
sessionId: ap.sessionId,
|
|
11040
12034
|
launchId: ap.launchId
|
|
11041
12035
|
});
|
|
@@ -11044,13 +12038,16 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
|
|
|
11044
12038
|
} else if (startupTimeoutTermination) {
|
|
11045
12039
|
this.cacheStartupTimeoutRetryConfig(agentId, ap);
|
|
11046
12040
|
logger.warn(`[Agent ${agentId}] Startup timeout cleanup completed (${reason})`);
|
|
12041
|
+
} else if (startupRequestErrorTermination) {
|
|
12042
|
+
this.idleAgentConfigs.delete(agentId);
|
|
12043
|
+
logger.warn(`[Agent ${agentId}] Startup request failure cleanup completed (${reason})`);
|
|
11047
12044
|
} else {
|
|
11048
12045
|
this.idleAgentConfigs.delete(agentId);
|
|
11049
12046
|
logger.error(`[Agent ${agentId}] Process crashed (${reason}) \u2014 marking inactive`);
|
|
11050
12047
|
this.sendAgentStatus(agentId, "inactive", ap.launchId);
|
|
11051
12048
|
}
|
|
11052
12049
|
if (terminalFailureDetail) {
|
|
11053
|
-
if (!startupTimeoutTermination) {
|
|
12050
|
+
if (!startupTimeoutTermination && !startupRequestErrorTermination) {
|
|
11054
12051
|
this.broadcastActivity(
|
|
11055
12052
|
agentId,
|
|
11056
12053
|
"error",
|
|
@@ -11059,7 +12056,7 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
|
|
|
11059
12056
|
ap.launchId
|
|
11060
12057
|
);
|
|
11061
12058
|
}
|
|
11062
|
-
} else {
|
|
12059
|
+
} else if (!startupRequestErrorTermination) {
|
|
11063
12060
|
this.broadcastActivity(agentId, "offline", `Crashed (${summary})`, [], ap.launchId);
|
|
11064
12061
|
}
|
|
11065
12062
|
}
|
|
@@ -11093,12 +12090,6 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
|
|
|
11093
12090
|
if (pendingStartRebind && agentProcess.sessionId) {
|
|
11094
12091
|
this.sendToServer({ type: "agent:session", agentId, sessionId: agentProcess.sessionId, launchId: agentProcess.launchId || void 0 });
|
|
11095
12092
|
}
|
|
11096
|
-
if (pendingStartRebind?.wakeMessage) {
|
|
11097
|
-
const accepted = this.deliverMessage(agentId, pendingStartRebind.wakeMessage, { transient: pendingStartRebind.wakeMessageTransient === true });
|
|
11098
|
-
if (accepted instanceof Promise) {
|
|
11099
|
-
accepted.catch((err) => logger.error(`[Agent ${agentId}] Failed to deliver wake message after startup rebind`, err));
|
|
11100
|
-
}
|
|
11101
|
-
}
|
|
11102
12093
|
this.broadcastActivity(agentId, "working", "Starting\u2026");
|
|
11103
12094
|
this.startRuntimeStartupTimeout(agentId, agentProcess);
|
|
11104
12095
|
} catch (err) {
|
|
@@ -11184,13 +12175,11 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
|
|
|
11184
12175
|
});
|
|
11185
12176
|
}
|
|
11186
12177
|
cacheStartupTimeoutRetryConfig(agentId, ap) {
|
|
11187
|
-
const
|
|
11188
|
-
|
|
11189
|
-
sessionId: ap.sessionId
|
|
11190
|
-
};
|
|
12178
|
+
const retrySessionId = this.restartSafeSessionId(ap);
|
|
12179
|
+
const retryConfig = this.buildRestartSafeConfig(ap.config, retrySessionId);
|
|
11191
12180
|
this.idleAgentConfigs.set(agentId, {
|
|
11192
12181
|
config: retryConfig,
|
|
11193
|
-
sessionId:
|
|
12182
|
+
sessionId: retrySessionId,
|
|
11194
12183
|
launchId: ap.launchId
|
|
11195
12184
|
});
|
|
11196
12185
|
this.recordDaemonTrace("daemon.agent.startup_timeout.retry_config_cached", {
|
|
@@ -11200,6 +12189,14 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
|
|
|
11200
12189
|
session_id_present: Boolean(ap.sessionId)
|
|
11201
12190
|
});
|
|
11202
12191
|
}
|
|
12192
|
+
buildRestartSafeConfig(config, sessionId) {
|
|
12193
|
+
const stripped = stripManagedRunnerCredential(config);
|
|
12194
|
+
return {
|
|
12195
|
+
...stripped,
|
|
12196
|
+
serverUrl: this.serverUrl,
|
|
12197
|
+
sessionId
|
|
12198
|
+
};
|
|
12199
|
+
}
|
|
11203
12200
|
async buildSpawnConfig(agentId, config) {
|
|
11204
12201
|
const baseConfig = config.serverUrl === this.serverUrl ? config : { ...config, serverUrl: this.serverUrl };
|
|
11205
12202
|
const runnerConfig = await this.ensureManagedRunnerCredential(agentId, baseConfig);
|
|
@@ -11647,6 +12644,7 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
|
|
|
11647
12644
|
this.broadcastActivity(agentId, "offline", "Process unavailable; restart required");
|
|
11648
12645
|
return false;
|
|
11649
12646
|
}
|
|
12647
|
+
const isIdle = this.isApmIdle(ap);
|
|
11650
12648
|
if (!transientDelivery && this.shouldDeferWakeMessage(agentId, ap.driver, message)) {
|
|
11651
12649
|
this.recordDaemonTrace("daemon.agent.delivery.routed", this.deliveryTraceAttrs(agentId, message, {
|
|
11652
12650
|
outcome: "deferred_wake_message",
|
|
@@ -11655,7 +12653,7 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
|
|
|
11655
12653
|
runtime: ap.config.runtime,
|
|
11656
12654
|
session_id_present: Boolean(ap.sessionId),
|
|
11657
12655
|
launchId: ap.launchId || void 0,
|
|
11658
|
-
is_idle:
|
|
12656
|
+
is_idle: isIdle,
|
|
11659
12657
|
inbox_count: ap.inbox.length
|
|
11660
12658
|
}));
|
|
11661
12659
|
return true;
|
|
@@ -11670,7 +12668,7 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
|
|
|
11670
12668
|
runtime: ap.config.runtime,
|
|
11671
12669
|
session_id_present: Boolean(ap.sessionId),
|
|
11672
12670
|
launchId: ap.launchId || void 0,
|
|
11673
|
-
is_idle:
|
|
12671
|
+
is_idle: isIdle,
|
|
11674
12672
|
inbox_count: ap.inbox.length
|
|
11675
12673
|
}));
|
|
11676
12674
|
return true;
|
|
@@ -11687,7 +12685,7 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
|
|
|
11687
12685
|
runtime: ap.config.runtime,
|
|
11688
12686
|
session_id_present: Boolean(ap.sessionId),
|
|
11689
12687
|
launchId: ap.launchId || void 0,
|
|
11690
|
-
is_idle:
|
|
12688
|
+
is_idle: isIdle,
|
|
11691
12689
|
inbox_count: ap.inbox.length
|
|
11692
12690
|
}));
|
|
11693
12691
|
} else {
|
|
@@ -11699,7 +12697,7 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
|
|
|
11699
12697
|
runtime: ap.config.runtime,
|
|
11700
12698
|
session_id_present: Boolean(ap.sessionId),
|
|
11701
12699
|
launchId: ap.launchId || void 0,
|
|
11702
|
-
is_idle:
|
|
12700
|
+
is_idle: isIdle,
|
|
11703
12701
|
inbox_count: ap.inbox.length
|
|
11704
12702
|
}));
|
|
11705
12703
|
this.sendAgentStatus(agentId, "inactive", ap.launchId);
|
|
@@ -11710,7 +12708,7 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
|
|
|
11710
12708
|
if (!transientDelivery && this.queueDeliveryForRuntimeErrorBackoff(agentId, ap, message)) {
|
|
11711
12709
|
return true;
|
|
11712
12710
|
}
|
|
11713
|
-
if (
|
|
12711
|
+
if (isIdle && ap.driver.supportsStdinNotification && ap.sessionId) {
|
|
11714
12712
|
if (transientDelivery) {
|
|
11715
12713
|
this.commitApmIdleState(agentId, ap, false);
|
|
11716
12714
|
this.startRuntimeTrace(agentId, ap, "stdin-idle-delivery", [message]);
|
|
@@ -11747,7 +12745,7 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
|
|
|
11747
12745
|
runtime: ap.config.runtime,
|
|
11748
12746
|
session_id_present: true,
|
|
11749
12747
|
launchId: ap.launchId || void 0,
|
|
11750
|
-
is_idle:
|
|
12748
|
+
is_idle: isIdle,
|
|
11751
12749
|
inbox_count: ap.inbox.length,
|
|
11752
12750
|
pending_notification_count: ap.notifications.pendingCount
|
|
11753
12751
|
}));
|
|
@@ -11786,7 +12784,7 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
|
|
|
11786
12784
|
runtime: ap.config.runtime,
|
|
11787
12785
|
session_id_present: Boolean(ap.sessionId),
|
|
11788
12786
|
launchId: ap.launchId || void 0,
|
|
11789
|
-
is_idle:
|
|
12787
|
+
is_idle: isIdle,
|
|
11790
12788
|
inbox_count: ap.inbox.length
|
|
11791
12789
|
}));
|
|
11792
12790
|
return true;
|
|
@@ -11856,6 +12854,34 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
|
|
|
11856
12854
|
}));
|
|
11857
12855
|
return true;
|
|
11858
12856
|
}
|
|
12857
|
+
if (ap.gatedSteering.reviewing) {
|
|
12858
|
+
ap.notifications.add();
|
|
12859
|
+
ap.notifications.clearTimer();
|
|
12860
|
+
if (ap.runtime.descriptor.busyDelivery === "gated") {
|
|
12861
|
+
this.recordGatedSteeringEvent(agentId, ap, "buffer", {
|
|
12862
|
+
reason: "review_boundary",
|
|
12863
|
+
pendingMessages: ap.inbox.length
|
|
12864
|
+
});
|
|
12865
|
+
}
|
|
12866
|
+
this.recordRuntimeTraceEvent(agentId, ap, "runtime.review_boundary.delivery_buffered", {
|
|
12867
|
+
pendingNotificationCount: ap.notifications.pendingCount,
|
|
12868
|
+
pendingMessages: ap.inbox.length,
|
|
12869
|
+
busyDeliveryMode: ap.driver.busyDeliveryMode
|
|
12870
|
+
});
|
|
12871
|
+
this.recordDaemonTrace("daemon.agent.delivery.routed", this.deliveryTraceAttrs(agentId, message, {
|
|
12872
|
+
outcome: "queued_review_boundary",
|
|
12873
|
+
accepted: true,
|
|
12874
|
+
process_present: true,
|
|
12875
|
+
runtime: ap.config.runtime,
|
|
12876
|
+
session_id_present: true,
|
|
12877
|
+
launchId: ap.launchId || void 0,
|
|
12878
|
+
inbox_count: ap.inbox.length,
|
|
12879
|
+
pending_notification_count: ap.notifications.pendingCount,
|
|
12880
|
+
busy_delivery_mode: ap.driver.busyDeliveryMode,
|
|
12881
|
+
notification_timer_present: false
|
|
12882
|
+
}));
|
|
12883
|
+
return true;
|
|
12884
|
+
}
|
|
11859
12885
|
if (ap.runtime.descriptor.busyDelivery === "gated") {
|
|
11860
12886
|
ap.notifications.add();
|
|
11861
12887
|
if (!ap.notifications.hasTimer) {
|
|
@@ -11971,7 +12997,7 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
|
|
|
11971
12997
|
path: workspacePath,
|
|
11972
12998
|
reachable: true
|
|
11973
12999
|
},
|
|
11974
|
-
sessionRef: sessionId ? resolveRuntimeSessionRef(config.runtime, sessionId, runtimeHomeDir, workspacePath) : null
|
|
13000
|
+
sessionRef: sessionId ? resolveRuntimeSessionRef(config.runtime, sessionId, runtimeHomeDir, workspacePath, { agentId, workingDirectory: workspacePath }) : null
|
|
11975
13001
|
}
|
|
11976
13002
|
};
|
|
11977
13003
|
}
|
|
@@ -12040,7 +13066,8 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
|
|
|
12040
13066
|
traceparent: formatTraceparent(span.context)
|
|
12041
13067
|
};
|
|
12042
13068
|
const ap = this.agents.get(agentId);
|
|
12043
|
-
|
|
13069
|
+
const isIdle = ap ? this.isApmIdle(ap) : false;
|
|
13070
|
+
if (ap && !(ap.sessionId && ap.driver.supportsStdinNotification && isIdle) && !(ap.sessionId && ap.runtime.descriptor.busyDelivery === "direct")) {
|
|
12044
13071
|
this.enqueueRuntimeProfileNotification(agentId, ap, message, kind, key);
|
|
12045
13072
|
span.end("ok", {
|
|
12046
13073
|
attrs: {
|
|
@@ -12054,7 +13081,7 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
|
|
|
12054
13081
|
});
|
|
12055
13082
|
return true;
|
|
12056
13083
|
}
|
|
12057
|
-
if (ap?.sessionId && ap.driver.supportsStdinNotification &&
|
|
13084
|
+
if (ap?.sessionId && ap.driver.supportsStdinNotification && isIdle) {
|
|
12058
13085
|
this.commitApmIdleState(agentId, ap, false);
|
|
12059
13086
|
this.startRuntimeTrace(agentId, ap, "runtime-profile", [message]);
|
|
12060
13087
|
const written = this.deliverMessagesViaStdin(agentId, ap, [message], "idle");
|
|
@@ -12305,6 +13332,183 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
|
|
|
12305
13332
|
workspace: [".codex/skills", ".agents/skills"]
|
|
12306
13333
|
}
|
|
12307
13334
|
};
|
|
13335
|
+
async getSessionTranscript(agentId) {
|
|
13336
|
+
const agent = this.agents.get(agentId);
|
|
13337
|
+
const idle = this.idleAgentConfigs.get(agentId);
|
|
13338
|
+
const config = agent?.config ?? idle?.config ?? null;
|
|
13339
|
+
const actualSessionId = agent?.sessionId || idle?.sessionId || null;
|
|
13340
|
+
if (!config) {
|
|
13341
|
+
return {
|
|
13342
|
+
runtime: "unknown",
|
|
13343
|
+
sessionId: actualSessionId || "unknown",
|
|
13344
|
+
reachable: false,
|
|
13345
|
+
path: null,
|
|
13346
|
+
fallbackReason: "agent not found or no config",
|
|
13347
|
+
transcript: null,
|
|
13348
|
+
sizeBytes: 0,
|
|
13349
|
+
truncated: false,
|
|
13350
|
+
redacted: false,
|
|
13351
|
+
tier: "unknown"
|
|
13352
|
+
};
|
|
13353
|
+
}
|
|
13354
|
+
const runtime = config.runtime;
|
|
13355
|
+
const workspaceDir = path13.join(this.dataDir, agentId);
|
|
13356
|
+
const homeDir = ensureRuntimeHomeDir(config, this.runtimeSessionHomeDir, workspaceDir);
|
|
13357
|
+
if (!actualSessionId) {
|
|
13358
|
+
return {
|
|
13359
|
+
runtime,
|
|
13360
|
+
sessionId: "unknown",
|
|
13361
|
+
reachable: false,
|
|
13362
|
+
path: null,
|
|
13363
|
+
fallbackReason: "no session id available",
|
|
13364
|
+
transcript: null,
|
|
13365
|
+
sizeBytes: 0,
|
|
13366
|
+
truncated: false,
|
|
13367
|
+
redacted: false,
|
|
13368
|
+
tier: runtimeTier(runtime)
|
|
13369
|
+
};
|
|
13370
|
+
}
|
|
13371
|
+
const ref = resolveRuntimeSessionRef(runtime, actualSessionId, homeDir, workspaceDir, {
|
|
13372
|
+
agentId,
|
|
13373
|
+
workingDirectory: workspaceDir
|
|
13374
|
+
});
|
|
13375
|
+
const tier = runtimeTier(runtime);
|
|
13376
|
+
const span = this.tracer.startSpan("daemon.session_transcript.read", {
|
|
13377
|
+
surface: "daemon",
|
|
13378
|
+
kind: "internal",
|
|
13379
|
+
attrs: {
|
|
13380
|
+
agentId,
|
|
13381
|
+
runtime,
|
|
13382
|
+
sessionId: actualSessionId,
|
|
13383
|
+
reachable: ref.reachable ?? false,
|
|
13384
|
+
tier,
|
|
13385
|
+
lookup_method: ref.reason?.includes("attempted_lookup=") ? ref.reason.split("attempted_lookup=")[1]?.split(";")[0] : "unknown"
|
|
13386
|
+
}
|
|
13387
|
+
});
|
|
13388
|
+
let transcript = null;
|
|
13389
|
+
let sizeBytes = 0;
|
|
13390
|
+
let truncated = false;
|
|
13391
|
+
let redacted = false;
|
|
13392
|
+
if (ref.reachable && ref.path) {
|
|
13393
|
+
try {
|
|
13394
|
+
const resolved = path13.resolve(ref.path);
|
|
13395
|
+
const allowedRoots = allowedTranscriptRootsForRuntime(runtime, homeDir, workspaceDir);
|
|
13396
|
+
if (!await isPathWithinAllowedRoots(resolved, allowedRoots)) {
|
|
13397
|
+
throw new Error("resolved session path is outside allowed runtime directories");
|
|
13398
|
+
}
|
|
13399
|
+
let targetPath = resolved;
|
|
13400
|
+
const info = await lstat(resolved);
|
|
13401
|
+
if (info.isSymbolicLink()) {
|
|
13402
|
+
throw new Error("symbolic links are not allowed");
|
|
13403
|
+
}
|
|
13404
|
+
if (info.isDirectory()) {
|
|
13405
|
+
targetPath = path13.join(resolved, "state.json");
|
|
13406
|
+
}
|
|
13407
|
+
if (!await isPathWithinAllowedRoots(targetPath, allowedRoots)) {
|
|
13408
|
+
throw new Error("resolved session state path is outside allowed runtime directories");
|
|
13409
|
+
}
|
|
13410
|
+
const redactedResult = await readAndRedact(targetPath, SESSION_TRANSCRIPT_MAX_BYTES);
|
|
13411
|
+
if (redactedResult !== null) {
|
|
13412
|
+
transcript = redactedResult.text;
|
|
13413
|
+
sizeBytes = Buffer.byteLength(transcript, "utf8");
|
|
13414
|
+
redacted = true;
|
|
13415
|
+
truncated = redactedResult.truncated;
|
|
13416
|
+
}
|
|
13417
|
+
span.end("ok", { attrs: { transcript_present: Boolean(transcript), size_bytes: sizeBytes, truncated, redacted } });
|
|
13418
|
+
} catch (err) {
|
|
13419
|
+
span.end("error", { attrs: { error_class: err instanceof Error ? err.name : "Error" } });
|
|
13420
|
+
}
|
|
13421
|
+
} else {
|
|
13422
|
+
span.end("ok", { attrs: { transcript_present: false, reason: ref.reason } });
|
|
13423
|
+
}
|
|
13424
|
+
return {
|
|
13425
|
+
runtime,
|
|
13426
|
+
sessionId: actualSessionId,
|
|
13427
|
+
reachable: ref.reachable ?? false,
|
|
13428
|
+
path: ref.path ?? null,
|
|
13429
|
+
fallbackReason: ref.reason ?? void 0,
|
|
13430
|
+
transcript,
|
|
13431
|
+
sizeBytes,
|
|
13432
|
+
truncated,
|
|
13433
|
+
redacted,
|
|
13434
|
+
tier
|
|
13435
|
+
};
|
|
13436
|
+
}
|
|
13437
|
+
/**
|
|
13438
|
+
* Collect the agent's current session transcript and upload it as a trace
|
|
13439
|
+
* bundle linked to a feedback report. The transcript is read using only the
|
|
13440
|
+
* sessionId bound to the agent; no caller-supplied session id is accepted.
|
|
13441
|
+
*/
|
|
13442
|
+
async collectFeedbackTranscript(agentId, feedbackReportId) {
|
|
13443
|
+
const transcriptResult = await this.getSessionTranscript(agentId);
|
|
13444
|
+
if (!transcriptResult.reachable || !transcriptResult.transcript) {
|
|
13445
|
+
return {
|
|
13446
|
+
reachable: false,
|
|
13447
|
+
fallbackReason: transcriptResult.fallbackReason ?? "transcript not reachable"
|
|
13448
|
+
};
|
|
13449
|
+
}
|
|
13450
|
+
if (!this.workerUrl) {
|
|
13451
|
+
return {
|
|
13452
|
+
reachable: true,
|
|
13453
|
+
fallbackReason: "daemon worker URL is not configured"
|
|
13454
|
+
};
|
|
13455
|
+
}
|
|
13456
|
+
const span = this.tracer.startSpan("daemon.feedback_transcript.upload", {
|
|
13457
|
+
surface: "daemon",
|
|
13458
|
+
kind: "producer",
|
|
13459
|
+
attrs: {
|
|
13460
|
+
agentId,
|
|
13461
|
+
feedbackReportId,
|
|
13462
|
+
runtime: transcriptResult.runtime,
|
|
13463
|
+
sessionId: transcriptResult.sessionId,
|
|
13464
|
+
transcript_size_bytes: transcriptResult.sizeBytes
|
|
13465
|
+
}
|
|
13466
|
+
});
|
|
13467
|
+
try {
|
|
13468
|
+
const raw = Buffer.from(transcriptResult.transcript, "utf8");
|
|
13469
|
+
const gzipped = gzipSync(raw);
|
|
13470
|
+
const bundleSha256 = createHash3("sha256").update(gzipped).digest("hex");
|
|
13471
|
+
const bundleSizeBytes = gzipped.byteLength;
|
|
13472
|
+
const bundleId = randomUUID5();
|
|
13473
|
+
const uploadResult = await uploadWithSignedCapability({
|
|
13474
|
+
serverUrl: this.serverUrl,
|
|
13475
|
+
apiKey: this.daemonApiKey,
|
|
13476
|
+
workerUrl: this.workerUrl,
|
|
13477
|
+
scope: "daemon-trace-bundle:create",
|
|
13478
|
+
createPath: "/api/trace-bundles",
|
|
13479
|
+
createBody: {
|
|
13480
|
+
bundleSha256,
|
|
13481
|
+
bundleSizeBytes
|
|
13482
|
+
},
|
|
13483
|
+
attestationMetadata: {
|
|
13484
|
+
bundleId,
|
|
13485
|
+
bundleSha256,
|
|
13486
|
+
bundleSizeBytes,
|
|
13487
|
+
bundleContentType: "application/json",
|
|
13488
|
+
bundleContentEncoding: "gzip",
|
|
13489
|
+
feedbackReportId,
|
|
13490
|
+
agentId
|
|
13491
|
+
},
|
|
13492
|
+
uploadBody: new Blob([new Uint8Array(gzipped)], { type: "application/json" }),
|
|
13493
|
+
fetchImpl: this.fetchImpl
|
|
13494
|
+
});
|
|
13495
|
+
const traceBundleId = typeof uploadResult.session.id === "string" ? uploadResult.session.id : bundleId;
|
|
13496
|
+
logger.info(`[FeedbackTranscript] uploaded for report=${feedbackReportId} agent=${agentId} traceBundleId=${traceBundleId} size=${bundleSizeBytes}`);
|
|
13497
|
+
span.end("ok", { attrs: { traceBundleId, bundleSizeBytes } });
|
|
13498
|
+
return {
|
|
13499
|
+
reachable: true,
|
|
13500
|
+
traceBundleId
|
|
13501
|
+
};
|
|
13502
|
+
} catch (err) {
|
|
13503
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
13504
|
+
logger.warn(`[FeedbackTranscript] upload failed for report=${feedbackReportId} agent=${agentId}: ${message}`);
|
|
13505
|
+
span.end("error", { attrs: { error_class: err instanceof Error ? err.name : "Error", error_message: message } });
|
|
13506
|
+
return {
|
|
13507
|
+
reachable: true,
|
|
13508
|
+
error: message
|
|
13509
|
+
};
|
|
13510
|
+
}
|
|
13511
|
+
}
|
|
12308
13512
|
async listSkills(agentId, runtimeHint) {
|
|
12309
13513
|
const agent = this.agents.get(agentId);
|
|
12310
13514
|
const idle = this.idleAgentConfigs.get(agentId);
|
|
@@ -12415,6 +13619,17 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
|
|
|
12415
13619
|
launchId: launchIdOverride || ap?.launchId || void 0,
|
|
12416
13620
|
clientSeq: ap ? this.nextActivityClientSeq(agentId) : void 0
|
|
12417
13621
|
});
|
|
13622
|
+
this.recordDaemonTrace("daemon.agent.activity.produced", {
|
|
13623
|
+
agentId,
|
|
13624
|
+
activity,
|
|
13625
|
+
detail_present: Boolean(detail),
|
|
13626
|
+
entry_kinds: entries.map((e) => e.kind).join(","),
|
|
13627
|
+
ap_present: Boolean(ap),
|
|
13628
|
+
launch_id_present: Boolean(launchIdOverride || ap?.launchId),
|
|
13629
|
+
client_seq_present: ap ? true : false,
|
|
13630
|
+
session_id_present: Boolean(ap?.sessionId),
|
|
13631
|
+
runtime: ap?.config.runtime
|
|
13632
|
+
});
|
|
12418
13633
|
if (ap) {
|
|
12419
13634
|
ap.lastActivity = activity;
|
|
12420
13635
|
ap.lastActivityDetail = detail;
|
|
@@ -12494,7 +13709,15 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
|
|
|
12494
13709
|
}
|
|
12495
13710
|
queueTrajectoryText(agentId, kind, text) {
|
|
12496
13711
|
const ap = this.agents.get(agentId);
|
|
12497
|
-
if (!ap)
|
|
13712
|
+
if (!ap) {
|
|
13713
|
+
this.recordDaemonTrace("daemon.agent.activity.skipped", {
|
|
13714
|
+
agentId,
|
|
13715
|
+
event_kind: kind,
|
|
13716
|
+
reason: "agent_process_missing",
|
|
13717
|
+
text_length: text.length
|
|
13718
|
+
});
|
|
13719
|
+
return;
|
|
13720
|
+
}
|
|
12498
13721
|
if (!text) {
|
|
12499
13722
|
this.broadcastActivity(agentId, "thinking", "");
|
|
12500
13723
|
return;
|
|
@@ -12541,7 +13764,7 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
|
|
|
12541
13764
|
ap.stalledRecoverySigtermTimer = setTimeout(() => {
|
|
12542
13765
|
ap.stalledRecoverySigtermTimer = null;
|
|
12543
13766
|
const current = this.agents.get(agentId);
|
|
12544
|
-
if (!current || current !== ap || current.runtime !== runtimeAtSignal || current.expectedTerminationReason !== "stalled_recovery") {
|
|
13767
|
+
if (!current || current !== ap || current.runtime !== runtimeAtSignal || current.gatedSteering.expectedTerminationReason !== "stalled_recovery") {
|
|
12545
13768
|
return;
|
|
12546
13769
|
}
|
|
12547
13770
|
this.mergeRuntimeExitTraceAttrs(runtimeAtSignal, {
|
|
@@ -12617,6 +13840,25 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
|
|
|
12617
13840
|
const reduction = reduceApmGatedCompaction(ap.gatedSteering, { kind: "compaction_interrupted" });
|
|
12618
13841
|
this.commitGatedSteeringDecisionState(agentId, ap, reduction.nextState);
|
|
12619
13842
|
}
|
|
13843
|
+
flushReviewBoundaryMessages(agentId, ap) {
|
|
13844
|
+
const reduction = reduceApmGatedReviewBoundaryFlush(ap.gatedSteering, {
|
|
13845
|
+
hasSession: Boolean(ap.sessionId),
|
|
13846
|
+
supportsStdinNotification: ap.driver.supportsStdinNotification,
|
|
13847
|
+
inboxLength: ap.inbox.length,
|
|
13848
|
+
pendingNotificationCount: ap.notifications.pendingCount
|
|
13849
|
+
});
|
|
13850
|
+
for (const effect of reduction.effects) {
|
|
13851
|
+
this.executeApmGatedSteeringEffect(agentId, ap, effect);
|
|
13852
|
+
}
|
|
13853
|
+
}
|
|
13854
|
+
interruptReviewIfActive(agentId) {
|
|
13855
|
+
const ap = this.agents.get(agentId);
|
|
13856
|
+
if (!ap?.gatedSteering.reviewing) return;
|
|
13857
|
+
const reduction = reduceApmGatedReview(ap.gatedSteering, { kind: "review_finished" });
|
|
13858
|
+
this.commitGatedSteeringDecisionState(agentId, ap, reduction.nextState, {
|
|
13859
|
+
event: "review_interrupted"
|
|
13860
|
+
});
|
|
13861
|
+
}
|
|
12620
13862
|
messagesTraceAttrs(messages) {
|
|
12621
13863
|
if (!messages || messages.length === 0) return {};
|
|
12622
13864
|
const first = messages[0];
|
|
@@ -12768,28 +14010,20 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
|
|
|
12768
14010
|
phase: ap.gatedSteering.phase,
|
|
12769
14011
|
outstandingToolUses: ap.gatedSteering.outstandingToolUses,
|
|
12770
14012
|
compacting: ap.gatedSteering.compacting,
|
|
14013
|
+
reviewing: ap.gatedSteering.reviewing === true ? true : void 0,
|
|
12771
14014
|
toolBoundaryFlushDisabled: ap.gatedSteering.toolBoundaryFlushDisabled,
|
|
12772
14015
|
pendingMessages: ap.inbox.length,
|
|
12773
14016
|
...attrs
|
|
12774
14017
|
});
|
|
12775
14018
|
}
|
|
12776
14019
|
commitGatedSteeringDecisionState(agentId, ap, nextState, phaseEventAttrs) {
|
|
12777
|
-
ap.gatedSteering
|
|
12778
|
-
ap.gatedSteering.outstandingToolUses = nextState.outstandingToolUses;
|
|
12779
|
-
ap.gatedSteering.compacting = nextState.compacting;
|
|
12780
|
-
ap.gatedSteering.toolBoundaryFlushDisabled = nextState.toolBoundaryFlushDisabled;
|
|
12781
|
-
ap.gatedSteering.lastFlushReason = nextState.lastFlushReason;
|
|
12782
|
-
ap.gatedSteering.recentEvents = nextState.recentEvents;
|
|
12783
|
-
ap.gatedSteering.isIdle = nextState.isIdle;
|
|
12784
|
-
ap.gatedSteering.expectedTerminationReason = nextState.expectedTerminationReason;
|
|
12785
|
-
ap.isIdle = nextState.isIdle;
|
|
12786
|
-
ap.expectedTerminationReason = nextState.expectedTerminationReason;
|
|
14020
|
+
ap.gatedSteering = commitApmGatedSteeringDecisionState(nextState);
|
|
12787
14021
|
if (phaseEventAttrs) {
|
|
12788
14022
|
this.recordGatedSteeringEvent(agentId, ap, "phase", phaseEventAttrs);
|
|
12789
14023
|
}
|
|
12790
14024
|
}
|
|
12791
|
-
commitApmIdleState(agentId, ap,
|
|
12792
|
-
const reduction = reduceApmIdleState(ap.gatedSteering, { isIdle });
|
|
14025
|
+
commitApmIdleState(agentId, ap, nextIsIdle) {
|
|
14026
|
+
const reduction = reduceApmIdleState(ap.gatedSteering, { isIdle: nextIsIdle });
|
|
12793
14027
|
this.commitGatedSteeringDecisionState(agentId, ap, reduction.nextState);
|
|
12794
14028
|
}
|
|
12795
14029
|
notifyGatedSteeringBoundary(agentId, ap, reason) {
|
|
@@ -12932,11 +14166,17 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
|
|
|
12932
14166
|
clearTimeout(ap.startupTimeoutTimer);
|
|
12933
14167
|
ap.startupTimeoutTimer = null;
|
|
12934
14168
|
}
|
|
14169
|
+
runtimeStartupReadinessSatisfiedByEvent(ap, event) {
|
|
14170
|
+
if ((ap.driver.startupReadiness ?? "first_event") !== "initial_turn") {
|
|
14171
|
+
return true;
|
|
14172
|
+
}
|
|
14173
|
+
return event.kind !== "session_init" && event.kind !== "internal_progress" && event.kind !== "runtime_diagnostic";
|
|
14174
|
+
}
|
|
12935
14175
|
handleRuntimeStartupTimeout(agentId, ap, timeoutMs) {
|
|
12936
14176
|
const current = this.agents.get(agentId);
|
|
12937
14177
|
if (current !== ap) return;
|
|
12938
14178
|
const reduction = reduceApmStartupTimeoutTermination(ap.gatedSteering, {
|
|
12939
|
-
hasRuntimeProgressEvent:
|
|
14179
|
+
hasRuntimeProgressEvent: ap.startupReadinessSatisfied
|
|
12940
14180
|
});
|
|
12941
14181
|
if (!reduction.shouldTerminate) {
|
|
12942
14182
|
this.clearRuntimeStartupTimeout(ap);
|
|
@@ -12951,37 +14191,86 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
|
|
|
12951
14191
|
ap.runtimeProgress.markStale();
|
|
12952
14192
|
const staleForMs = Math.max(timeoutMs, ap.runtimeProgress.ageMs());
|
|
12953
14193
|
const diagnostic = buildRuntimeStallDiagnostic(ap, staleForMs, Math.max(1, Math.floor(staleForMs / 6e4)));
|
|
12954
|
-
|
|
12955
|
-
|
|
12956
|
-
|
|
12957
|
-
|
|
12958
|
-
|
|
12959
|
-
|
|
14194
|
+
const projection = projectApmRuntimeTerminationTrace({
|
|
14195
|
+
reason: "startup_timeout",
|
|
14196
|
+
timeoutMs
|
|
14197
|
+
});
|
|
14198
|
+
this.recordRuntimeTraceEvent(agentId, ap, projection.runtimeEventName, {
|
|
14199
|
+
...projection.runtimeEventAttrs,
|
|
12960
14200
|
...diagnostic.traceAttrs
|
|
12961
14201
|
});
|
|
12962
14202
|
this.endRuntimeTrace(ap, "error", {
|
|
12963
|
-
|
|
12964
|
-
turn_subtype: "runtime_stalled",
|
|
12965
|
-
turn_reason: "no_runtime_events",
|
|
12966
|
-
runtime_start_failure_kind: "runtime_start_timeout",
|
|
12967
|
-
timeout_ms: timeoutMs,
|
|
14203
|
+
...projection.runtimeSpanAttrs,
|
|
12968
14204
|
...runtimeTraceCounterAttrs(ap),
|
|
12969
14205
|
...this.finalizeRuntimeProfileTurnControl(agentId, ap, "runtime_stalled")
|
|
12970
14206
|
});
|
|
12971
|
-
logger.warn(`[Agent ${agentId}] ${ap.driver.id} did not
|
|
14207
|
+
logger.warn(`[Agent ${agentId}] ${ap.driver.id} did not reach startup readiness within ${timeoutMs}ms; terminating process`);
|
|
12972
14208
|
this.broadcastActivity(agentId, "error", detail, [{ kind: "text", text: `Error: ${detail}` }], ap.launchId);
|
|
12973
14209
|
this.sendAgentStatus(agentId, "inactive", ap.launchId);
|
|
12974
14210
|
this.cacheStartupTimeoutRetryConfig(agentId, ap);
|
|
14211
|
+
try {
|
|
14212
|
+
this.runtimeExitTraceAttrs.set(ap.runtime, projection.processExitAttrs);
|
|
14213
|
+
void ap.runtime.stop({ signal: "SIGTERM", reason: projection.runtimeStopReason });
|
|
14214
|
+
} catch (err) {
|
|
14215
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
14216
|
+
logger.warn(`[Agent ${agentId}] Failed to terminate startup-timed-out ${ap.driver.id} process: ${reason}`);
|
|
14217
|
+
}
|
|
14218
|
+
}
|
|
14219
|
+
handleRuntimeStartupRequestError(agentId, ap, event) {
|
|
14220
|
+
const current = this.agents.get(agentId);
|
|
14221
|
+
if (current !== ap) return;
|
|
14222
|
+
this.clearRuntimeStartupTimeout(ap);
|
|
14223
|
+
this.interruptCompactionIfActive(agentId);
|
|
14224
|
+
this.interruptReviewIfActive(agentId);
|
|
14225
|
+
this.flushPendingTrajectory(agentId);
|
|
14226
|
+
const reduction = reduceApmStartupRequestErrorTermination(ap.gatedSteering);
|
|
14227
|
+
this.commitGatedSteeringDecisionState(agentId, ap, reduction.nextState, { event: "startup_request_error" });
|
|
14228
|
+
ap.lastRuntimeError = event.message;
|
|
14229
|
+
ap.runtimeErrorSinceProgress = true;
|
|
14230
|
+
ap.runtimeProgress.markStale();
|
|
14231
|
+
ap.notifications.clearPending();
|
|
14232
|
+
ap.notifications.clearTimer();
|
|
14233
|
+
const diagnostics = buildRuntimeErrorDiagnosticEnvelope(event.message);
|
|
14234
|
+
const visibleErrorMessage = diagnostics.spanAttrs.runtime_error_action_required === true ? formatRuntimeLoginRequiredMessage(ap.driver.id) : event.message;
|
|
14235
|
+
const failureAttrs = {
|
|
14236
|
+
turn_outcome: "failed",
|
|
14237
|
+
turn_subtype: "runtime_start_failed",
|
|
14238
|
+
turn_reason: "startup_request_error",
|
|
14239
|
+
runtime_start_failure_kind: "startup_request_error",
|
|
14240
|
+
startup_request_method: event.startupRequestMethod,
|
|
14241
|
+
...diagnostics.eventAttrs
|
|
14242
|
+
};
|
|
14243
|
+
this.noteRuntimeTraceCounter(ap, event);
|
|
14244
|
+
this.recordRuntimeTraceEvent(agentId, ap, "runtime.event.received", {
|
|
14245
|
+
kind: event.kind,
|
|
14246
|
+
startup_request_method: event.startupRequestMethod
|
|
14247
|
+
});
|
|
14248
|
+
this.recordRuntimeTraceEvent(agentId, ap, "runtime.start.request_failed", failureAttrs);
|
|
14249
|
+
this.endRuntimeTrace(ap, "error", {
|
|
14250
|
+
...failureAttrs,
|
|
14251
|
+
...diagnostics.spanAttrs,
|
|
14252
|
+
...runtimeTraceCounterAttrs(ap),
|
|
14253
|
+
...this.finalizeRuntimeProfileTurnControl(agentId, ap, "runtime_error")
|
|
14254
|
+
});
|
|
14255
|
+
logger.warn(
|
|
14256
|
+
`[Agent ${agentId}] ${ap.driver.id} startup request ${event.startupRequestMethod} failed; terminating unusable runtime process`
|
|
14257
|
+
);
|
|
14258
|
+
this.broadcastActivity(agentId, "error", visibleErrorMessage, [
|
|
14259
|
+
{ kind: "text", text: `Error: ${visibleErrorMessage}` }
|
|
14260
|
+
], ap.launchId);
|
|
14261
|
+
this.sendAgentStatus(agentId, "inactive", ap.launchId);
|
|
14262
|
+
this.idleAgentConfigs.delete(agentId);
|
|
12975
14263
|
try {
|
|
12976
14264
|
this.runtimeExitTraceAttrs.set(ap.runtime, {
|
|
12977
|
-
stop_source: "
|
|
12978
|
-
expectedTerminationReason: "
|
|
12979
|
-
|
|
14265
|
+
stop_source: "startup_request_error",
|
|
14266
|
+
expectedTerminationReason: "startup_request_error",
|
|
14267
|
+
startup_request_method: event.startupRequestMethod,
|
|
14268
|
+
runtime_error_class: diagnostics.spanAttrs.runtime_error_class
|
|
12980
14269
|
});
|
|
12981
|
-
void ap.runtime.stop({ signal: "SIGTERM", reason: "
|
|
14270
|
+
void ap.runtime.stop({ signal: "SIGTERM", reason: "startup_request_error" });
|
|
12982
14271
|
} catch (err) {
|
|
12983
14272
|
const reason = err instanceof Error ? err.message : String(err);
|
|
12984
|
-
logger.warn(`[Agent ${agentId}] Failed to terminate startup-
|
|
14273
|
+
logger.warn(`[Agent ${agentId}] Failed to terminate startup-failed ${ap.driver.id} process: ${reason}`);
|
|
12985
14274
|
}
|
|
12986
14275
|
}
|
|
12987
14276
|
isThinkingBlockMutationError(message) {
|
|
@@ -12995,21 +14284,19 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
|
|
|
12995
14284
|
ap.runtimeProgress.markStale();
|
|
12996
14285
|
const staleForMinutes = Math.max(1, Math.floor(staleForMs / 6e4));
|
|
12997
14286
|
const diagnostic = buildRuntimeStallDiagnostic(ap, staleForMs, staleForMinutes);
|
|
12998
|
-
const
|
|
12999
|
-
|
|
13000
|
-
|
|
13001
|
-
|
|
13002
|
-
|
|
14287
|
+
const projection = projectApmRuntimeProgressStalledTrace({
|
|
14288
|
+
turnReason: diagnostic.turnReason,
|
|
14289
|
+
staleForMs,
|
|
14290
|
+
lastActivity: ap.lastActivity,
|
|
14291
|
+
lastActivityDetailPresent: diagnostic.lastActivityDetailPresent,
|
|
14292
|
+
lastActivityDetailKind: diagnostic.lastActivityDetailKind
|
|
14293
|
+
});
|
|
14294
|
+
this.recordRuntimeTraceEvent(agentId, ap, projection.runtimeEventName, {
|
|
14295
|
+
...projection.runtimeEventAttrs,
|
|
13003
14296
|
...diagnostic.traceAttrs
|
|
13004
14297
|
});
|
|
13005
14298
|
this.endRuntimeTrace(ap, "error", {
|
|
13006
|
-
|
|
13007
|
-
turn_subtype: "runtime_stalled",
|
|
13008
|
-
turn_reason: turnReason,
|
|
13009
|
-
ageMs: staleForMs,
|
|
13010
|
-
lastActivity: ap.lastActivity,
|
|
13011
|
-
lastActivityDetailPresent: Boolean(ap.lastActivityDetail),
|
|
13012
|
-
lastActivityDetailKind: classifyActivityDetailForTrace(ap.lastActivityDetail),
|
|
14299
|
+
...projection.runtimeSpanAttrs,
|
|
13013
14300
|
...runtimeTraceCounterAttrs(ap),
|
|
13014
14301
|
...this.finalizeRuntimeProfileTurnControl(agentId, ap, "runtime_stalled")
|
|
13015
14302
|
});
|
|
@@ -13036,25 +14323,22 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
|
|
|
13036
14323
|
const staleForMinutes = Math.max(1, Math.floor(staleForMs / 6e4));
|
|
13037
14324
|
ap.runtimeProgress.markStale();
|
|
13038
14325
|
const diagnostic = buildRuntimeStallDiagnostic(ap, staleForMs, staleForMinutes);
|
|
13039
|
-
const
|
|
13040
|
-
|
|
13041
|
-
|
|
13042
|
-
|
|
13043
|
-
|
|
13044
|
-
|
|
14326
|
+
const projection = projectApmRuntimeTerminationTrace({
|
|
14327
|
+
reason: "stalled_recovery",
|
|
14328
|
+
turnReason: diagnostic.turnReason,
|
|
14329
|
+
staleForMs,
|
|
14330
|
+
lastActivity: ap.lastActivity,
|
|
14331
|
+
lastActivityDetailPresent: diagnostic.lastActivityDetailPresent,
|
|
14332
|
+
lastActivityDetailKind: diagnostic.lastActivityDetailKind,
|
|
13045
14333
|
pendingMessages: ap.inbox.length,
|
|
13046
|
-
|
|
14334
|
+
recoveryAction: "terminate_for_queued_message"
|
|
14335
|
+
});
|
|
14336
|
+
this.recordRuntimeTraceEvent(agentId, ap, projection.runtimeEventName, {
|
|
14337
|
+
...projection.runtimeEventAttrs,
|
|
14338
|
+
...diagnostic.traceAttrs
|
|
13047
14339
|
});
|
|
13048
14340
|
this.endRuntimeTrace(ap, "error", {
|
|
13049
|
-
|
|
13050
|
-
turn_subtype: "runtime_stalled",
|
|
13051
|
-
turn_reason: turnReason,
|
|
13052
|
-
ageMs: staleForMs,
|
|
13053
|
-
lastActivity: ap.lastActivity,
|
|
13054
|
-
lastActivityDetailPresent: Boolean(ap.lastActivityDetail),
|
|
13055
|
-
lastActivityDetailKind: classifyActivityDetailForTrace(ap.lastActivityDetail),
|
|
13056
|
-
pendingMessages: ap.inbox.length,
|
|
13057
|
-
recovery: "terminate_for_queued_message",
|
|
14341
|
+
...projection.runtimeSpanAttrs,
|
|
13058
14342
|
...runtimeTraceCounterAttrs(ap),
|
|
13059
14343
|
...this.finalizeRuntimeProfileTurnControl(agentId, ap, "runtime_stalled")
|
|
13060
14344
|
});
|
|
@@ -13064,13 +14348,9 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
|
|
|
13064
14348
|
);
|
|
13065
14349
|
this.broadcastActivity(agentId, "working", `Restarting stalled ${runtimeLabel} runtime for queued message`);
|
|
13066
14350
|
try {
|
|
13067
|
-
this.runtimeExitTraceAttrs.set(ap.runtime,
|
|
13068
|
-
stop_source: "stalled_recovery",
|
|
13069
|
-
expectedTerminationReason: "stalled_recovery",
|
|
13070
|
-
queued_messages_count: ap.inbox.length
|
|
13071
|
-
});
|
|
14351
|
+
this.runtimeExitTraceAttrs.set(ap.runtime, projection.processExitAttrs);
|
|
13072
14352
|
this.startStalledRecoverySigtermWatchdog(agentId, ap, runtimeLabel, ap.inbox.length, staleForMs);
|
|
13073
|
-
void ap.runtime.stop({ signal: "SIGTERM", reason:
|
|
14353
|
+
void ap.runtime.stop({ signal: "SIGTERM", reason: projection.runtimeStopReason });
|
|
13074
14354
|
} catch (err) {
|
|
13075
14355
|
this.clearStalledRecoverySigtermWatchdog(ap);
|
|
13076
14356
|
const reason = err instanceof Error ? err.message : String(err);
|
|
@@ -13086,16 +14366,23 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
|
|
|
13086
14366
|
if (ap) this.recordRuntimeTelemetry(agentId, ap, event);
|
|
13087
14367
|
return;
|
|
13088
14368
|
}
|
|
14369
|
+
if (ap && isStartupRequestErrorEvent(event)) {
|
|
14370
|
+
this.handleRuntimeStartupRequestError(agentId, ap, event);
|
|
14371
|
+
return;
|
|
14372
|
+
}
|
|
13089
14373
|
if (ap) {
|
|
13090
14374
|
const wasStalled = ap.runtimeProgress.isStale;
|
|
13091
|
-
this.
|
|
14375
|
+
if (this.runtimeStartupReadinessSatisfiedByEvent(ap, event)) {
|
|
14376
|
+
ap.startupReadinessSatisfied = true;
|
|
14377
|
+
this.clearRuntimeStartupTimeout(ap);
|
|
14378
|
+
}
|
|
13092
14379
|
this.noteRuntimeTraceCounter(ap, event);
|
|
13093
14380
|
const eventAttrs = event.kind === "internal_progress" ? {
|
|
13094
14381
|
kind: event.kind,
|
|
13095
14382
|
source: event.source,
|
|
13096
14383
|
itemType: event.itemType,
|
|
13097
14384
|
payloadBytes: event.payloadBytes
|
|
13098
|
-
} : { kind: event.kind };
|
|
14385
|
+
} : event.kind === "runtime_diagnostic" ? runtimeDiagnosticTraceAttrs(event) : { kind: event.kind };
|
|
13099
14386
|
this.recordRuntimeTraceEvent(agentId, ap, "runtime.event.received", eventAttrs);
|
|
13100
14387
|
if (wasStalled) {
|
|
13101
14388
|
this.recordRuntimeTraceEvent(agentId, ap, "runtime.progress.observed", { afterStall: true });
|
|
@@ -13116,7 +14403,19 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
|
|
|
13116
14403
|
});
|
|
13117
14404
|
return;
|
|
13118
14405
|
}
|
|
14406
|
+
if (event.kind === "runtime_diagnostic") {
|
|
14407
|
+
this.noteRuntimeProgress(ap, event.kind);
|
|
14408
|
+
this.clearRuntimeErrorDeliveryBackoffAfterProgress(agentId, ap, event.kind);
|
|
14409
|
+
this.recordRuntimeDiagnosticActivity(agentId, ap, event);
|
|
14410
|
+
return;
|
|
14411
|
+
}
|
|
13119
14412
|
this.noteRuntimeProgress(ap, event.kind);
|
|
14413
|
+
} else if (event.kind !== "internal_progress") {
|
|
14414
|
+
this.recordDaemonTrace("daemon.agent.event.received_without_process", {
|
|
14415
|
+
agentId,
|
|
14416
|
+
event_kind: event.kind,
|
|
14417
|
+
runtime: driver.id
|
|
14418
|
+
});
|
|
13120
14419
|
}
|
|
13121
14420
|
switch (event.kind) {
|
|
13122
14421
|
case "session_init":
|
|
@@ -13206,6 +14505,25 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
|
|
|
13206
14505
|
this.flushCompactionBoundaryMessages(agentId, ap);
|
|
13207
14506
|
}
|
|
13208
14507
|
break;
|
|
14508
|
+
case "review_started":
|
|
14509
|
+
this.flushPendingTrajectory(agentId);
|
|
14510
|
+
if (ap) this.recordRuntimeTraceEvent(agentId, ap, "runtime.review_mode.started");
|
|
14511
|
+
this.broadcastActivity(agentId, "working", "Reviewing changes");
|
|
14512
|
+
if (ap) {
|
|
14513
|
+
const reduction = reduceApmGatedReview(ap.gatedSteering, { kind: "review_started" });
|
|
14514
|
+
this.commitGatedSteeringDecisionState(agentId, ap, reduction.nextState, { event: "review_started" });
|
|
14515
|
+
}
|
|
14516
|
+
break;
|
|
14517
|
+
case "review_finished":
|
|
14518
|
+
this.flushPendingTrajectory(agentId);
|
|
14519
|
+
if (ap) this.recordRuntimeTraceEvent(agentId, ap, "runtime.review_mode.finished");
|
|
14520
|
+
this.broadcastActivity(agentId, "working", "Review finished");
|
|
14521
|
+
if (ap) {
|
|
14522
|
+
const reduction = reduceApmGatedReview(ap.gatedSteering, { kind: "review_finished" });
|
|
14523
|
+
this.commitGatedSteeringDecisionState(agentId, ap, reduction.nextState, { event: "review_finished" });
|
|
14524
|
+
this.flushReviewBoundaryMessages(agentId, ap);
|
|
14525
|
+
}
|
|
14526
|
+
break;
|
|
13209
14527
|
case "turn_end":
|
|
13210
14528
|
if (ap) this.recordRuntimeTraceEvent(agentId, ap, "runtime.turn.completed");
|
|
13211
14529
|
this.completeCompactionIfActive(agentId, "Context compaction finished (inferred from turn end)", {
|
|
@@ -13257,12 +14575,10 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
|
|
|
13257
14575
|
});
|
|
13258
14576
|
if (ap.driver.terminateProcessOnTurnEnd) {
|
|
13259
14577
|
logger.info(`[Agent ${agentId}] Turn completed; terminating ${ap.driver.id} process`);
|
|
14578
|
+
const projection = projectApmRuntimeTerminationTrace({ reason: "turn_end" });
|
|
13260
14579
|
try {
|
|
13261
|
-
this.runtimeExitTraceAttrs.set(ap.runtime,
|
|
13262
|
-
|
|
13263
|
-
expectedTerminationReason: "turn_end"
|
|
13264
|
-
});
|
|
13265
|
-
void ap.runtime.stop({ signal: "SIGTERM", reason: "turn_end" });
|
|
14580
|
+
this.runtimeExitTraceAttrs.set(ap.runtime, projection.processExitAttrs);
|
|
14581
|
+
void ap.runtime.stop({ signal: "SIGTERM", reason: projection.runtimeStopReason });
|
|
13266
14582
|
} catch (err) {
|
|
13267
14583
|
const reason = err instanceof Error ? err.message : String(err);
|
|
13268
14584
|
logger.warn(`[Agent ${agentId}] Failed to terminate ${ap.driver.id} after turn_end: ${reason}`);
|
|
@@ -13276,6 +14592,7 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
|
|
|
13276
14592
|
break;
|
|
13277
14593
|
case "error": {
|
|
13278
14594
|
this.interruptCompactionIfActive(agentId);
|
|
14595
|
+
this.interruptReviewIfActive(agentId);
|
|
13279
14596
|
this.flushPendingTrajectory(agentId);
|
|
13280
14597
|
if (ap) {
|
|
13281
14598
|
ap.lastRuntimeError = event.message;
|
|
@@ -13459,7 +14776,7 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
|
|
|
13459
14776
|
if (!ap) return false;
|
|
13460
14777
|
const count = ap.notifications.takePendingAndClearTimer();
|
|
13461
14778
|
if (count === 0) return false;
|
|
13462
|
-
if (ap
|
|
14779
|
+
if (this.isApmIdle(ap)) return false;
|
|
13463
14780
|
if (!ap.sessionId) return false;
|
|
13464
14781
|
const runtimeErrorBackoffRemainingMs = this.runtimeErrorDeliveryBackoffRemainingMs(ap);
|
|
13465
14782
|
if (runtimeErrorBackoffRemainingMs > 0) {
|
|
@@ -13494,6 +14811,18 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
|
|
|
13494
14811
|
);
|
|
13495
14812
|
return false;
|
|
13496
14813
|
}
|
|
14814
|
+
if (ap.gatedSteering.reviewing) {
|
|
14815
|
+
this.recordRuntimeTraceEvent(agentId, ap, "runtime.review_boundary.delivery_suppressed", {
|
|
14816
|
+
pendingNotificationCount: count,
|
|
14817
|
+
pendingMessages: ap.inbox.length,
|
|
14818
|
+
busyDeliveryMode: ap.driver.busyDeliveryMode
|
|
14819
|
+
});
|
|
14820
|
+
ap.notifications.add(count);
|
|
14821
|
+
logger.info(
|
|
14822
|
+
`[Agent ${agentId}] Suppressing stdin delivery until review mode finishes; pending=${ap.inbox.length}`
|
|
14823
|
+
);
|
|
14824
|
+
return false;
|
|
14825
|
+
}
|
|
13497
14826
|
const inboxCount = ap.inbox.length;
|
|
13498
14827
|
if (inboxCount === 0) return false;
|
|
13499
14828
|
ap.notifications.pruneContributedToPending(ap.inbox, ap.sessionId);
|
|
@@ -13761,12 +15090,12 @@ ${formatAgentInboxDelta(inboxRows, { totalPendingMessages: inboxCount })}]`;
|
|
|
13761
15090
|
const traceSource = options.transient ? `stdin_${mode}_transient_delivery` : `stdin_${mode}_delivery`;
|
|
13762
15091
|
const prompt = formatRuntimeProfileControlPrompt(messages) ?? (messages.length === 1 ? `New message received:
|
|
13763
15092
|
|
|
13764
|
-
${formatIncomingMessage(messages[0])}
|
|
15093
|
+
${formatIncomingMessage(messages[0], incomingMessageFormatOptionsForDriver(ap.driver))}
|
|
13765
15094
|
|
|
13766
15095
|
Respond as appropriate. Complete all your work before stopping.
|
|
13767
15096
|
${RESPONSE_TARGET_HINT}` : `New messages received:
|
|
13768
15097
|
|
|
13769
|
-
${messages.map((message) => formatIncomingMessage(message)).join("\n")}
|
|
15098
|
+
${messages.map((message) => formatIncomingMessage(message, incomingMessageFormatOptionsForDriver(ap.driver))).join("\n")}
|
|
13770
15099
|
|
|
13771
15100
|
Respond as appropriate. Complete all your work before stopping.
|
|
13772
15101
|
${RESPONSE_TARGET_HINT}`);
|
|
@@ -14211,8 +15540,8 @@ var ReminderCache = class {
|
|
|
14211
15540
|
};
|
|
14212
15541
|
|
|
14213
15542
|
// src/machineLock.ts
|
|
14214
|
-
import { createHash as createHash4, randomUUID as
|
|
14215
|
-
import { mkdirSync as mkdirSync6, readFileSync as
|
|
15543
|
+
import { createHash as createHash4, randomUUID as randomUUID6 } from "crypto";
|
|
15544
|
+
import { mkdirSync as mkdirSync6, readFileSync as readFileSync7, rmSync as rmSync3, statSync as statSync2, writeFileSync as writeFileSync5 } from "fs";
|
|
14216
15545
|
import os7 from "os";
|
|
14217
15546
|
import path14 from "path";
|
|
14218
15547
|
var INCOMPLETE_LOCK_STALE_MS = 3e4;
|
|
@@ -14240,7 +15569,7 @@ function ownerPath(lockDir) {
|
|
|
14240
15569
|
}
|
|
14241
15570
|
function readOwner(lockDir) {
|
|
14242
15571
|
try {
|
|
14243
|
-
return JSON.parse(
|
|
15572
|
+
return JSON.parse(readFileSync7(ownerPath(lockDir), "utf8"));
|
|
14244
15573
|
} catch {
|
|
14245
15574
|
return null;
|
|
14246
15575
|
}
|
|
@@ -14268,7 +15597,7 @@ function acquireDaemonMachineLock(options) {
|
|
|
14268
15597
|
const lockId = getDaemonMachineLockId(options.apiKey);
|
|
14269
15598
|
const machineDir = path14.join(rootDir, lockId);
|
|
14270
15599
|
const lockDir = path14.join(machineDir, "daemon.lock");
|
|
14271
|
-
const token =
|
|
15600
|
+
const token = randomUUID6();
|
|
14272
15601
|
mkdirSync6(machineDir, { recursive: true });
|
|
14273
15602
|
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
14274
15603
|
try {
|
|
@@ -14498,232 +15827,11 @@ function isDiagnosticErrorAttr(key) {
|
|
|
14498
15827
|
}
|
|
14499
15828
|
|
|
14500
15829
|
// src/traceBundleUpload.ts
|
|
14501
|
-
import { createHash as createHash6, randomUUID as
|
|
14502
|
-
import { gzipSync } from "zlib";
|
|
15830
|
+
import { createHash as createHash6, randomUUID as randomUUID7 } from "crypto";
|
|
15831
|
+
import { gzipSync as gzipSync2 } from "zlib";
|
|
14503
15832
|
import { mkdir as mkdir2, readFile as readFile2, readdir as readdir3, stat as stat3, writeFile as writeFile2 } from "fs/promises";
|
|
14504
15833
|
import path16 from "path";
|
|
14505
15834
|
|
|
14506
|
-
// src/chatBridgeRequest.ts
|
|
14507
|
-
var DEFAULT_CHAT_BRIDGE_TOOL_TIMEOUT_MS = Number.parseInt(
|
|
14508
|
-
process.env.SLOCK_CHAT_BRIDGE_TOOL_TIMEOUT_MS || "",
|
|
14509
|
-
10
|
|
14510
|
-
) || 6e4;
|
|
14511
|
-
var ChatBridgeToolTimeoutError = class extends Error {
|
|
14512
|
-
toolName;
|
|
14513
|
-
target;
|
|
14514
|
-
timeoutMs;
|
|
14515
|
-
durationMs;
|
|
14516
|
-
constructor(toolName, target, timeoutMs, durationMs) {
|
|
14517
|
-
super(`${toolName} timed out after ${timeoutMs}ms${target ? ` (target: ${target})` : ""}`);
|
|
14518
|
-
this.name = "ChatBridgeToolTimeoutError";
|
|
14519
|
-
this.toolName = toolName;
|
|
14520
|
-
this.target = target;
|
|
14521
|
-
this.timeoutMs = timeoutMs;
|
|
14522
|
-
this.durationMs = durationMs;
|
|
14523
|
-
}
|
|
14524
|
-
};
|
|
14525
|
-
function describeError(err) {
|
|
14526
|
-
if (err instanceof Error) return `${err.name}: ${err.message}`;
|
|
14527
|
-
return String(err);
|
|
14528
|
-
}
|
|
14529
|
-
async function executeJsonRequest(url, init, {
|
|
14530
|
-
toolName,
|
|
14531
|
-
target = null,
|
|
14532
|
-
timeoutMs = DEFAULT_CHAT_BRIDGE_TOOL_TIMEOUT_MS,
|
|
14533
|
-
fetchImpl,
|
|
14534
|
-
now = () => Date.now(),
|
|
14535
|
-
warn = (message) => logger.warn(message)
|
|
14536
|
-
}) {
|
|
14537
|
-
const startedAt = now();
|
|
14538
|
-
const timeoutController = new AbortController();
|
|
14539
|
-
const signals = [timeoutController.signal];
|
|
14540
|
-
if (init.signal) signals.push(init.signal);
|
|
14541
|
-
const signal = signals.length === 1 ? signals[0] : AbortSignal.any(signals);
|
|
14542
|
-
const timeout = setTimeout(() => {
|
|
14543
|
-
timeoutController.abort();
|
|
14544
|
-
}, timeoutMs);
|
|
14545
|
-
timeout.unref?.();
|
|
14546
|
-
try {
|
|
14547
|
-
const response = await fetchImpl(url, { ...init, signal });
|
|
14548
|
-
const data = await response.json();
|
|
14549
|
-
return { response, data, durationMs: now() - startedAt };
|
|
14550
|
-
} catch (err) {
|
|
14551
|
-
const durationMs = now() - startedAt;
|
|
14552
|
-
if (timeoutController.signal.aborted && !init.signal?.aborted) {
|
|
14553
|
-
warn(
|
|
14554
|
-
`[ChatBridgeTimeout] tool=${toolName} target=${target ?? "-"} duration_ms=${durationMs} timeout_ms=${timeoutMs} outcome=timeout`
|
|
14555
|
-
);
|
|
14556
|
-
throw new ChatBridgeToolTimeoutError(toolName, target, timeoutMs, durationMs);
|
|
14557
|
-
}
|
|
14558
|
-
warn(
|
|
14559
|
-
`[ChatBridgeError] tool=${toolName} target=${target ?? "-"} duration_ms=${durationMs} outcome=error error=${describeError(err)}`
|
|
14560
|
-
);
|
|
14561
|
-
throw err;
|
|
14562
|
-
} finally {
|
|
14563
|
-
clearTimeout(timeout);
|
|
14564
|
-
}
|
|
14565
|
-
}
|
|
14566
|
-
async function executeResponseRequest(url, init, {
|
|
14567
|
-
toolName,
|
|
14568
|
-
target = null,
|
|
14569
|
-
timeoutMs = DEFAULT_CHAT_BRIDGE_TOOL_TIMEOUT_MS,
|
|
14570
|
-
fetchImpl,
|
|
14571
|
-
now = () => Date.now(),
|
|
14572
|
-
warn = (message) => logger.warn(message)
|
|
14573
|
-
}) {
|
|
14574
|
-
const startedAt = now();
|
|
14575
|
-
const timeoutController = new AbortController();
|
|
14576
|
-
const signals = [timeoutController.signal];
|
|
14577
|
-
if (init.signal) signals.push(init.signal);
|
|
14578
|
-
const signal = signals.length === 1 ? signals[0] : AbortSignal.any(signals);
|
|
14579
|
-
const timeout = setTimeout(() => {
|
|
14580
|
-
timeoutController.abort();
|
|
14581
|
-
}, timeoutMs);
|
|
14582
|
-
timeout.unref?.();
|
|
14583
|
-
try {
|
|
14584
|
-
const response = await fetchImpl(url, { ...init, signal });
|
|
14585
|
-
return { response, durationMs: now() - startedAt };
|
|
14586
|
-
} catch (err) {
|
|
14587
|
-
const durationMs = now() - startedAt;
|
|
14588
|
-
if (timeoutController.signal.aborted && !init.signal?.aborted) {
|
|
14589
|
-
warn(
|
|
14590
|
-
`[ChatBridgeTimeout] tool=${toolName} target=${target ?? "-"} duration_ms=${durationMs} timeout_ms=${timeoutMs} outcome=timeout`
|
|
14591
|
-
);
|
|
14592
|
-
throw new ChatBridgeToolTimeoutError(toolName, target, timeoutMs, durationMs);
|
|
14593
|
-
}
|
|
14594
|
-
warn(
|
|
14595
|
-
`[ChatBridgeError] tool=${toolName} target=${target ?? "-"} duration_ms=${durationMs} outcome=error error=${describeError(err)}`
|
|
14596
|
-
);
|
|
14597
|
-
throw err;
|
|
14598
|
-
} finally {
|
|
14599
|
-
clearTimeout(timeout);
|
|
14600
|
-
}
|
|
14601
|
-
}
|
|
14602
|
-
|
|
14603
|
-
// src/directUploadCapability.ts
|
|
14604
|
-
function joinUrl(base, path18) {
|
|
14605
|
-
return `${base.replace(/\/+$/, "")}${path18}`;
|
|
14606
|
-
}
|
|
14607
|
-
function jsonHeaders(apiKey) {
|
|
14608
|
-
return {
|
|
14609
|
-
"Content-Type": "application/json",
|
|
14610
|
-
...apiKey ? { Authorization: `Bearer ${apiKey}` } : {}
|
|
14611
|
-
};
|
|
14612
|
-
}
|
|
14613
|
-
async function requestDaemonScopeAttestation({
|
|
14614
|
-
serverUrl,
|
|
14615
|
-
apiKey,
|
|
14616
|
-
scope,
|
|
14617
|
-
metadata,
|
|
14618
|
-
fetchImpl = daemonFetch,
|
|
14619
|
-
timeoutMs = DEFAULT_CHAT_BRIDGE_TOOL_TIMEOUT_MS
|
|
14620
|
-
}) {
|
|
14621
|
-
const { response, data } = await executeJsonRequest(
|
|
14622
|
-
joinUrl(serverUrl, "/internal/machine/scope-attestation"),
|
|
14623
|
-
{
|
|
14624
|
-
method: "POST",
|
|
14625
|
-
headers: jsonHeaders(apiKey),
|
|
14626
|
-
body: JSON.stringify({
|
|
14627
|
-
scope,
|
|
14628
|
-
...metadata ? { metadata } : {}
|
|
14629
|
-
})
|
|
14630
|
-
},
|
|
14631
|
-
{
|
|
14632
|
-
toolName: "daemon_direct_upload.scope_attestation",
|
|
14633
|
-
target: scope,
|
|
14634
|
-
timeoutMs,
|
|
14635
|
-
fetchImpl
|
|
14636
|
-
}
|
|
14637
|
-
);
|
|
14638
|
-
if (!response.ok) {
|
|
14639
|
-
throw new Error(`Failed to request daemon scope attestation (${response.status})`);
|
|
14640
|
-
}
|
|
14641
|
-
return data;
|
|
14642
|
-
}
|
|
14643
|
-
async function createDirectUploadSession({
|
|
14644
|
-
serverUrl,
|
|
14645
|
-
apiKey,
|
|
14646
|
-
workerUrl,
|
|
14647
|
-
scope,
|
|
14648
|
-
createPath = "/api/uploads",
|
|
14649
|
-
body,
|
|
14650
|
-
attestationMetadata,
|
|
14651
|
-
fetchImpl = daemonFetch,
|
|
14652
|
-
timeoutMs = DEFAULT_CHAT_BRIDGE_TOOL_TIMEOUT_MS
|
|
14653
|
-
}) {
|
|
14654
|
-
const capability = await requestDaemonScopeAttestation({
|
|
14655
|
-
serverUrl,
|
|
14656
|
-
apiKey,
|
|
14657
|
-
scope,
|
|
14658
|
-
metadata: attestationMetadata,
|
|
14659
|
-
fetchImpl,
|
|
14660
|
-
timeoutMs
|
|
14661
|
-
});
|
|
14662
|
-
const { response, data } = await executeJsonRequest(
|
|
14663
|
-
joinUrl(workerUrl, createPath),
|
|
14664
|
-
{
|
|
14665
|
-
method: "POST",
|
|
14666
|
-
headers: jsonHeaders(),
|
|
14667
|
-
body: JSON.stringify({
|
|
14668
|
-
...body,
|
|
14669
|
-
attestation: capability.attestation
|
|
14670
|
-
})
|
|
14671
|
-
},
|
|
14672
|
-
{
|
|
14673
|
-
toolName: "daemon_direct_upload.create",
|
|
14674
|
-
target: capability.audience,
|
|
14675
|
-
timeoutMs,
|
|
14676
|
-
fetchImpl
|
|
14677
|
-
}
|
|
14678
|
-
);
|
|
14679
|
-
if (!response.ok) {
|
|
14680
|
-
throw new Error(`Failed to create direct upload session (${response.status})`);
|
|
14681
|
-
}
|
|
14682
|
-
return { capability, response: data };
|
|
14683
|
-
}
|
|
14684
|
-
async function uploadWithSignedCapability({
|
|
14685
|
-
serverUrl,
|
|
14686
|
-
apiKey,
|
|
14687
|
-
workerUrl,
|
|
14688
|
-
scope,
|
|
14689
|
-
createPath = "/api/uploads",
|
|
14690
|
-
createBody,
|
|
14691
|
-
attestationMetadata,
|
|
14692
|
-
uploadBody,
|
|
14693
|
-
fetchImpl = daemonFetch,
|
|
14694
|
-
timeoutMs = DEFAULT_CHAT_BRIDGE_TOOL_TIMEOUT_MS
|
|
14695
|
-
}) {
|
|
14696
|
-
const { capability, response: session } = await createDirectUploadSession({
|
|
14697
|
-
serverUrl,
|
|
14698
|
-
apiKey,
|
|
14699
|
-
workerUrl,
|
|
14700
|
-
scope,
|
|
14701
|
-
createPath,
|
|
14702
|
-
body: createBody,
|
|
14703
|
-
attestationMetadata,
|
|
14704
|
-
fetchImpl,
|
|
14705
|
-
timeoutMs
|
|
14706
|
-
});
|
|
14707
|
-
const { response: uploadResponse } = await executeResponseRequest(
|
|
14708
|
-
session.upload.url,
|
|
14709
|
-
{
|
|
14710
|
-
method: session.upload.method,
|
|
14711
|
-
headers: session.upload.headers ?? {},
|
|
14712
|
-
body: uploadBody
|
|
14713
|
-
},
|
|
14714
|
-
{
|
|
14715
|
-
toolName: "daemon_direct_upload.put",
|
|
14716
|
-
target: capability.audience,
|
|
14717
|
-
timeoutMs,
|
|
14718
|
-
fetchImpl
|
|
14719
|
-
}
|
|
14720
|
-
);
|
|
14721
|
-
if (!uploadResponse.ok) {
|
|
14722
|
-
throw new Error(`Failed to upload with signed capability (${uploadResponse.status})`);
|
|
14723
|
-
}
|
|
14724
|
-
return { capability, session, uploadResponse };
|
|
14725
|
-
}
|
|
14726
|
-
|
|
14727
15835
|
// src/traceJitter.ts
|
|
14728
15836
|
import { createHash as createHash5 } from "crypto";
|
|
14729
15837
|
var INITIAL_UPLOAD_DELAY_SPAN_MS = 3e4;
|
|
@@ -14864,9 +15972,9 @@ var DaemonTraceBundleUploader = class {
|
|
|
14864
15972
|
span?.end("cancelled", { attrs: { outcome: "empty" } });
|
|
14865
15973
|
return false;
|
|
14866
15974
|
}
|
|
14867
|
-
const gzipped =
|
|
15975
|
+
const gzipped = gzipSync2(raw);
|
|
14868
15976
|
const bundleSha256 = sha256Hex(gzipped);
|
|
14869
|
-
const bundleId =
|
|
15977
|
+
const bundleId = randomUUID7();
|
|
14870
15978
|
await uploadWithSignedCapability({
|
|
14871
15979
|
serverUrl: this.options.serverUrl,
|
|
14872
15980
|
apiKey: this.options.apiKey,
|
|
@@ -15006,6 +16114,15 @@ var DAEMON_CORE_TRACE_ATTR_CONTRACTS = {
|
|
|
15006
16114
|
},
|
|
15007
16115
|
"daemon.connection.local_disconnect_observed": {
|
|
15008
16116
|
spanAttrs: ["running_agents_count", "idle_agents_count"]
|
|
16117
|
+
},
|
|
16118
|
+
"daemon.agent.activity.produced": {
|
|
16119
|
+
spanAttrs: ["agentId", "activity", "detail_present", "entry_kinds", "ap_present", "launch_id_present", "client_seq_present", "session_id_present", "runtime"]
|
|
16120
|
+
},
|
|
16121
|
+
"daemon.agent.activity.skipped": {
|
|
16122
|
+
spanAttrs: ["agentId", "event_kind", "reason", "text_length"]
|
|
16123
|
+
},
|
|
16124
|
+
"daemon.agent.event.received_without_process": {
|
|
16125
|
+
spanAttrs: ["agentId", "event_kind", "runtime"]
|
|
15009
16126
|
}
|
|
15010
16127
|
};
|
|
15011
16128
|
var DAEMON_CLI_USAGE = "Usage: slock-daemon --server-url <url> --api-key <key>";
|
|
@@ -15083,7 +16200,7 @@ function resolveSlockCliPathOrEmpty(moduleUrl = import.meta.url) {
|
|
|
15083
16200
|
}
|
|
15084
16201
|
async function runBundledSlockCli(argv) {
|
|
15085
16202
|
process.argv = [process.execPath, "slock", ...argv];
|
|
15086
|
-
await import("./dist-
|
|
16203
|
+
await import("./dist-DSRBN3VD.js");
|
|
15087
16204
|
}
|
|
15088
16205
|
function detectRuntimes(tracer = noopTracer) {
|
|
15089
16206
|
const ids = [];
|
|
@@ -15193,6 +16310,10 @@ function summarizeIncomingMessage(msg) {
|
|
|
15193
16310
|
return `(agent=${msg.agentId}, path=${msg.path})`;
|
|
15194
16311
|
case "agent:skills:list":
|
|
15195
16312
|
return `(agent=${msg.agentId}, runtime=${msg.runtime || "auto"})`;
|
|
16313
|
+
case "agent:diagnostic:session_transcript":
|
|
16314
|
+
return `(agent=${msg.agentId})`;
|
|
16315
|
+
case "agent:diagnostic:feedback_transcript":
|
|
16316
|
+
return `(agent=${msg.agentId}, feedbackReportId=${msg.feedbackReportId})`;
|
|
15196
16317
|
case "agent:activity_probe":
|
|
15197
16318
|
return `(agent=${msg.agentId}, probe=${msg.probeId.slice(0, 8)}, purpose=${msg.purpose})`;
|
|
15198
16319
|
case "machine:workspace:delete":
|
|
@@ -15251,6 +16372,7 @@ var DaemonCore = class {
|
|
|
15251
16372
|
});
|
|
15252
16373
|
let connection;
|
|
15253
16374
|
this.agentsDataDir = options.dataDir ?? resolveSlockHomePath("agents", this.slockHome);
|
|
16375
|
+
const traceUploadDisabled = process.env.SLOCK_DAEMON_TRACE_UPLOAD_DISABLED === "1";
|
|
15254
16376
|
const agentManagerOptions = {
|
|
15255
16377
|
dataDir: this.agentsDataDir,
|
|
15256
16378
|
serverUrl: options.serverUrl,
|
|
@@ -15258,7 +16380,8 @@ var DaemonCore = class {
|
|
|
15258
16380
|
slockCliPath: this.slockCliPath,
|
|
15259
16381
|
tracer: this.tracer,
|
|
15260
16382
|
daemonVersion: this.daemonVersion,
|
|
15261
|
-
computerVersion: this.computerVersion
|
|
16383
|
+
computerVersion: this.computerVersion,
|
|
16384
|
+
workerUrl: traceUploadDisabled ? void 0 : process.env.SLOCK_DAEMON_TRACE_UPLOAD_URL || DEFAULT_TRACE_UPLOAD_URL
|
|
15262
16385
|
};
|
|
15263
16386
|
this.agentManager = options.agentManagerFactory ? options.agentManagerFactory((msg) => connection.send(msg), options.apiKey, agentManagerOptions) : new AgentProcessManager((msg) => connection.send(msg), options.apiKey, agentManagerOptions);
|
|
15264
16387
|
const connectionFactory = options.connectionFactory ?? ((connOptions) => new DaemonConnection(connOptions));
|
|
@@ -15687,6 +16810,49 @@ var DaemonCore = class {
|
|
|
15687
16810
|
this.connection.send({ type: "agent:skills:list_result", agentId: msg.agentId, global: [], workspace: [] });
|
|
15688
16811
|
});
|
|
15689
16812
|
break;
|
|
16813
|
+
case "agent:diagnostic:session_transcript":
|
|
16814
|
+
this.agentManager.getSessionTranscript(msg.agentId).then((result) => {
|
|
16815
|
+
this.connection.send({ type: "agent:diagnostic:session_transcript_result", agentId: msg.agentId, requestId: msg.requestId, ...result });
|
|
16816
|
+
}).catch((err) => {
|
|
16817
|
+
logger.error(`[Daemon] Failed to get session transcript for ${msg.agentId}`, err);
|
|
16818
|
+
this.connection.send({
|
|
16819
|
+
type: "agent:diagnostic:session_transcript_result",
|
|
16820
|
+
agentId: msg.agentId,
|
|
16821
|
+
requestId: msg.requestId,
|
|
16822
|
+
runtime: "unknown",
|
|
16823
|
+
sessionId: "unknown",
|
|
16824
|
+
reachable: false,
|
|
16825
|
+
path: null,
|
|
16826
|
+
transcript: null,
|
|
16827
|
+
sizeBytes: 0,
|
|
16828
|
+
truncated: false,
|
|
16829
|
+
redacted: false,
|
|
16830
|
+
tier: "unknown",
|
|
16831
|
+
error: err instanceof Error ? err.message : String(err)
|
|
16832
|
+
});
|
|
16833
|
+
});
|
|
16834
|
+
break;
|
|
16835
|
+
case "agent:diagnostic:feedback_transcript":
|
|
16836
|
+
this.agentManager.collectFeedbackTranscript(msg.agentId, msg.feedbackReportId).then((result) => {
|
|
16837
|
+
this.connection.send({
|
|
16838
|
+
type: "agent:diagnostic:feedback_transcript_result",
|
|
16839
|
+
agentId: msg.agentId,
|
|
16840
|
+
feedbackReportId: msg.feedbackReportId,
|
|
16841
|
+
requestId: msg.requestId,
|
|
16842
|
+
...result
|
|
16843
|
+
});
|
|
16844
|
+
}).catch((err) => {
|
|
16845
|
+
logger.error(`[Daemon] Failed to collect feedback transcript for ${msg.agentId}`, err);
|
|
16846
|
+
this.connection.send({
|
|
16847
|
+
type: "agent:diagnostic:feedback_transcript_result",
|
|
16848
|
+
agentId: msg.agentId,
|
|
16849
|
+
feedbackReportId: msg.feedbackReportId,
|
|
16850
|
+
requestId: msg.requestId,
|
|
16851
|
+
reachable: false,
|
|
16852
|
+
error: err instanceof Error ? err.message : String(err)
|
|
16853
|
+
});
|
|
16854
|
+
});
|
|
16855
|
+
break;
|
|
15690
16856
|
case "agent:activity_probe":
|
|
15691
16857
|
this.agentManager.respondToActivityProbe(msg.agentId, msg.probeId);
|
|
15692
16858
|
break;
|