@slock-ai/daemon 0.55.7-play.20260602150229 → 0.56.1
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-OV4XD66N.js → chunk-RIBO24KM.js} +975 -538
- package/dist/core.js +2 -6
- package/dist/index.js +3 -5
- package/package.json +1 -1
- package/dist/drivers/piSdkRunner.js +0 -96
|
@@ -939,10 +939,12 @@ var actionCardActionSchema = z.discriminatedUnion("type", [
|
|
|
939
939
|
]);
|
|
940
940
|
|
|
941
941
|
// ../shared/src/agentInbox.ts
|
|
942
|
-
function formatAgentInboxDelta(rows) {
|
|
943
|
-
|
|
942
|
+
function formatAgentInboxDelta(rows, options = {}) {
|
|
943
|
+
const totalPendingMessages = options.totalPendingMessages;
|
|
944
|
+
const header = typeof totalPendingMessages === "number" ? `Inbox update: ${totalPendingMessages} unread message${totalPendingMessages === 1 ? "" : "s"} total; ${rows.length === 0 ? "no" : rows.length} changed target${rows.length === 1 ? "" : "s"}` : `Inbox update: ${rows.length} changed target${rows.length === 1 ? "" : "s"}`;
|
|
945
|
+
if (rows.length === 0) return typeof totalPendingMessages === "number" ? header : "Inbox update: no pending targets";
|
|
944
946
|
return [
|
|
945
|
-
|
|
947
|
+
header,
|
|
946
948
|
...rows.map((row) => `${row.target} ${formatAgentInboxRowDetails(row)}`)
|
|
947
949
|
].join("\n");
|
|
948
950
|
}
|
|
@@ -1305,6 +1307,7 @@ function buildPrompt(config, variant, opts) {
|
|
|
1305
1307
|
const sendCmd = isCli ? "`slock message send`" : `\`${t("send_message")}\``;
|
|
1306
1308
|
const readCmd = isCli ? "`slock message read`" : `\`${t("read_history")}\``;
|
|
1307
1309
|
const checkCmd = isCli ? "`slock message check`" : `\`${t("check_messages")}\``;
|
|
1310
|
+
const inboxCheckCmd = isCli ? "`slock inbox check`" : checkCmd;
|
|
1308
1311
|
const taskClaimCmd = isCli ? "`slock task claim`" : `\`${t("claim_tasks")}\``;
|
|
1309
1312
|
const taskCreateCmd = isCli ? "`slock task create`" : `\`${t("create_tasks")}\``;
|
|
1310
1313
|
const taskUpdateCmd = isCli ? "`slock task update`" : `\`${t("update_task_status")}\``;
|
|
@@ -1785,14 +1788,14 @@ You may develop a specialized role over time through your interactions. Embrace
|
|
|
1785
1788
|
|
|
1786
1789
|
## Message Notifications
|
|
1787
1790
|
|
|
1788
|
-
While you are working, the daemon may write a batched
|
|
1791
|
+
While you are working, the daemon may write a batched, content-free inbox update into your current turn.
|
|
1789
1792
|
|
|
1790
1793
|
How to handle these:
|
|
1791
|
-
- Treat the notification as a signal that new Slock messages are waiting; it does not include the message content.
|
|
1792
|
-
-
|
|
1793
|
-
- If
|
|
1794
|
+
- Treat the notification as a non-urgent signal that new Slock messages are waiting; it does not include the message content and does not require an immediate interruption.
|
|
1795
|
+
- Keep working until a natural breakpoint. If you then choose to inspect pending targets, call ${inboxCheckCmd}; use ${checkCmd} / ${readCmd} when you choose to inspect message content.
|
|
1796
|
+
- If a message you explicitly read is higher priority, pivot to it. If not, continue your current work.`;
|
|
1794
1797
|
} else {
|
|
1795
|
-
const notifyExample = isCli ? `\`[Slock inbox notice:
|
|
1798
|
+
const notifyExample = isCli ? `\`[Slock inbox notice:\\nInbox update: N unread messages total; M changed targets\\n#channel pending: K messages ...]\`` : `\`[Slock inbox notice:\\nInbox update: N unread messages total; M changed targets\\n#channel pending: K messages ...]\``;
|
|
1796
1799
|
prompt += `
|
|
1797
1800
|
|
|
1798
1801
|
## Message Notifications
|
|
@@ -1802,9 +1805,9 @@ While you are busy (executing tools, thinking, etc.), new messages may arrive. W
|
|
|
1802
1805
|
${notifyExample}
|
|
1803
1806
|
|
|
1804
1807
|
How to handle these:
|
|
1805
|
-
-
|
|
1806
|
-
-
|
|
1807
|
-
-
|
|
1808
|
+
- The notice is target-scoped and content-free. Its header may show total unread/pending count, while detail rows list the targets changed by this update; it never includes message bodies.
|
|
1809
|
+
- Do not interrupt the current step just because a notice arrived. At a natural breakpoint, call ${inboxCheckCmd} if you choose to inspect pending targets, or use ${checkCmd} / ${readCmd} when you choose to inspect message content.
|
|
1810
|
+
- If a message you explicitly read is higher priority, you may pivot to it. If not, continue your current work.`;
|
|
1808
1811
|
}
|
|
1809
1812
|
}
|
|
1810
1813
|
if (config.description) {
|
|
@@ -1858,19 +1861,6 @@ function listLegacySlockStatePaths(slockHome = resolveSlockHome(), homeDir = os.
|
|
|
1858
1861
|
return candidates.filter((candidate) => existsSync(candidate.path));
|
|
1859
1862
|
}
|
|
1860
1863
|
|
|
1861
|
-
// src/authEnv.ts
|
|
1862
|
-
var DAEMON_API_KEY_ENV = "SLOCK_MACHINE_API_KEY";
|
|
1863
|
-
var SLOCK_AGENT_TOKEN_ENV = "SLOCK_AGENT_TOKEN";
|
|
1864
|
-
function scrubDaemonAuthEnv(env) {
|
|
1865
|
-
delete env[DAEMON_API_KEY_ENV];
|
|
1866
|
-
return env;
|
|
1867
|
-
}
|
|
1868
|
-
function scrubDaemonChildEnv(env) {
|
|
1869
|
-
delete env[DAEMON_API_KEY_ENV];
|
|
1870
|
-
delete env[SLOCK_AGENT_TOKEN_ENV];
|
|
1871
|
-
return env;
|
|
1872
|
-
}
|
|
1873
|
-
|
|
1874
1864
|
// src/agentCredentialProxy.ts
|
|
1875
1865
|
import { randomBytes } from "crypto";
|
|
1876
1866
|
import http from "http";
|
|
@@ -3026,9 +3016,7 @@ var LOOPBACK_NO_PROXY = "127.0.0.1,localhost";
|
|
|
3026
3016
|
var CLI_TRANSPORT_TRACE_DIR_ENV = "SLOCK_CLI_TRANSPORT_TRACE_DIR";
|
|
3027
3017
|
var safePathPart = (value) => value.replace(/[^a-zA-Z0-9_.-]/g, "_");
|
|
3028
3018
|
var RAW_CREDENTIAL_ENV_DENYLIST = [
|
|
3029
|
-
"
|
|
3030
|
-
"SLOCK_AGENT_CREDENTIAL_KEY",
|
|
3031
|
-
"SLOCK_AGENT_CREDENTIAL_KEY_FILE"
|
|
3019
|
+
"SLOCK_AGENT_CREDENTIAL_KEY"
|
|
3032
3020
|
];
|
|
3033
3021
|
var cachedOpencliBinPath;
|
|
3034
3022
|
function resolveOpencliBinPath() {
|
|
@@ -3243,7 +3231,7 @@ exec ${shellSingleQuote(process.execPath)} ${shellSingleQuote(opencliBinPath)} "
|
|
|
3243
3231
|
...agentCredentialProxy ? {} : { SLOCK_AGENT_TOKEN_FILE: tokenFile },
|
|
3244
3232
|
PATH: `${slockDir}${path2.delimiter}${process.env.PATH ?? ""}`
|
|
3245
3233
|
};
|
|
3246
|
-
|
|
3234
|
+
delete spawnEnv.SLOCK_AGENT_TOKEN;
|
|
3247
3235
|
for (const key of RAW_CREDENTIAL_ENV_DENYLIST) {
|
|
3248
3236
|
delete spawnEnv[key];
|
|
3249
3237
|
}
|
|
@@ -3672,7 +3660,7 @@ function resolveCommandOnWindows(command, env, execFileSyncFn, existsSyncFn) {
|
|
|
3672
3660
|
}
|
|
3673
3661
|
function resolveCommandOnPath(command, deps = {}) {
|
|
3674
3662
|
const platform = deps.platform ?? process.platform;
|
|
3675
|
-
const env =
|
|
3663
|
+
const env = withWindowsUserEnvironment(deps.env ?? process.env, deps);
|
|
3676
3664
|
const execFileSyncFn = deps.execFileSyncFn ?? execFileSync;
|
|
3677
3665
|
const existsSyncFn = deps.existsSyncFn ?? existsSync2;
|
|
3678
3666
|
if (platform === "win32") {
|
|
@@ -3698,7 +3686,7 @@ function firstExistingPath(candidates, deps = {}) {
|
|
|
3698
3686
|
return null;
|
|
3699
3687
|
}
|
|
3700
3688
|
function readCommandVersion(command, args = [], deps = {}) {
|
|
3701
|
-
const env =
|
|
3689
|
+
const env = withWindowsUserEnvironment(deps.env ?? process.env, deps);
|
|
3702
3690
|
const execFileSyncFn = deps.execFileSyncFn ?? execFileSync;
|
|
3703
3691
|
try {
|
|
3704
3692
|
const output = normalizeExecOutput(execFileSyncFn(command, [...args, "--version"], {
|
|
@@ -5161,11 +5149,11 @@ function detectCursorModels(runCommand = runCursorModelsCommand) {
|
|
|
5161
5149
|
return parseCursorModelsOutput(String(result.stdout || ""));
|
|
5162
5150
|
}
|
|
5163
5151
|
function buildCursorModelProbeEnv(deps = {}) {
|
|
5164
|
-
return
|
|
5152
|
+
return withWindowsUserEnvironment({
|
|
5165
5153
|
...deps.env ?? process.env,
|
|
5166
5154
|
FORCE_COLOR: "0",
|
|
5167
5155
|
NO_COLOR: "1"
|
|
5168
|
-
}, deps)
|
|
5156
|
+
}, deps);
|
|
5169
5157
|
}
|
|
5170
5158
|
function runCursorModelsCommand() {
|
|
5171
5159
|
return spawnSync("cursor-agent", ["models"], {
|
|
@@ -5221,7 +5209,7 @@ function resolveGeminiSpawn(commandArgs, deps = {}) {
|
|
|
5221
5209
|
}
|
|
5222
5210
|
const execFileSyncFn = deps.execFileSyncFn ?? execFileSync3;
|
|
5223
5211
|
const existsSyncFn = deps.existsSyncFn ?? existsSync5;
|
|
5224
|
-
const env =
|
|
5212
|
+
const env = deps.env ?? process.env;
|
|
5225
5213
|
const winPath = path8.win32;
|
|
5226
5214
|
let geminiEntry = null;
|
|
5227
5215
|
try {
|
|
@@ -5393,16 +5381,13 @@ var GeminiDriver = class {
|
|
|
5393
5381
|
// src/drivers/kimi.ts
|
|
5394
5382
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
5395
5383
|
import { spawn as spawn7 } from "child_process";
|
|
5396
|
-
import {
|
|
5384
|
+
import { existsSync as existsSync6, readFileSync as readFileSync3, writeFileSync as writeFileSync6 } from "fs";
|
|
5397
5385
|
import os3 from "os";
|
|
5398
5386
|
import path9 from "path";
|
|
5399
5387
|
var KIMI_WIRE_PROTOCOL_VERSION = "1.3";
|
|
5400
5388
|
var KIMI_SYSTEM_PROMPT_FILE = ".slock-kimi-system.md";
|
|
5401
5389
|
var KIMI_AGENT_FILE = ".slock-kimi-agent.yaml";
|
|
5402
5390
|
var KIMI_MCP_FILE = ".slock-kimi-mcp.json";
|
|
5403
|
-
var KIMI_GENERATED_CONFIG_FILE = ".slock-kimi-config.toml";
|
|
5404
|
-
var SLOCK_KIMI_CONFIG_CONTENT_ENV = "SLOCK_KIMI_CONFIG_CONTENT";
|
|
5405
|
-
var SLOCK_KIMI_CONFIG_FILE_ENV = "SLOCK_KIMI_CONFIG_FILE";
|
|
5406
5391
|
function parseToolArguments(raw) {
|
|
5407
5392
|
if (typeof raw !== "string") return raw;
|
|
5408
5393
|
try {
|
|
@@ -5411,73 +5396,6 @@ function parseToolArguments(raw) {
|
|
|
5411
5396
|
return raw;
|
|
5412
5397
|
}
|
|
5413
5398
|
}
|
|
5414
|
-
function readKimiConfigSource(home = os3.homedir(), env = process.env) {
|
|
5415
|
-
const inlineConfig = env[SLOCK_KIMI_CONFIG_CONTENT_ENV];
|
|
5416
|
-
if (inlineConfig && inlineConfig.trim()) {
|
|
5417
|
-
return {
|
|
5418
|
-
raw: inlineConfig,
|
|
5419
|
-
explicitPath: null,
|
|
5420
|
-
sourcePath: SLOCK_KIMI_CONFIG_CONTENT_ENV
|
|
5421
|
-
};
|
|
5422
|
-
}
|
|
5423
|
-
const explicitPath = env[SLOCK_KIMI_CONFIG_FILE_ENV];
|
|
5424
|
-
const configPath = explicitPath && explicitPath.trim() ? explicitPath : path9.join(home, ".kimi", "config.toml");
|
|
5425
|
-
try {
|
|
5426
|
-
return {
|
|
5427
|
-
raw: readFileSync3(configPath, "utf8"),
|
|
5428
|
-
explicitPath: explicitPath && explicitPath.trim() ? explicitPath : null,
|
|
5429
|
-
sourcePath: configPath
|
|
5430
|
-
};
|
|
5431
|
-
} catch {
|
|
5432
|
-
return {
|
|
5433
|
-
raw: null,
|
|
5434
|
-
explicitPath: explicitPath && explicitPath.trim() ? explicitPath : null,
|
|
5435
|
-
sourcePath: configPath
|
|
5436
|
-
};
|
|
5437
|
-
}
|
|
5438
|
-
}
|
|
5439
|
-
function buildKimiSpawnEnv(env = process.env) {
|
|
5440
|
-
const spawnEnv = { ...env, FORCE_COLOR: "0", NO_COLOR: "1" };
|
|
5441
|
-
delete spawnEnv[SLOCK_KIMI_CONFIG_CONTENT_ENV];
|
|
5442
|
-
delete spawnEnv[SLOCK_KIMI_CONFIG_FILE_ENV];
|
|
5443
|
-
return scrubDaemonChildEnv(spawnEnv);
|
|
5444
|
-
}
|
|
5445
|
-
function buildKimiEffectiveEnv(ctx, overrideEnv) {
|
|
5446
|
-
return {
|
|
5447
|
-
...process.env,
|
|
5448
|
-
...ctx.config.envVars || {},
|
|
5449
|
-
...overrideEnv || {}
|
|
5450
|
-
};
|
|
5451
|
-
}
|
|
5452
|
-
function buildKimiLaunchOptions(ctx, opts = {}) {
|
|
5453
|
-
const env = buildKimiEffectiveEnv(ctx, opts.env);
|
|
5454
|
-
const source = readKimiConfigSource(opts.home ?? os3.homedir(), env);
|
|
5455
|
-
const args = [];
|
|
5456
|
-
let configFilePath = null;
|
|
5457
|
-
let configContent = null;
|
|
5458
|
-
if (source.explicitPath) {
|
|
5459
|
-
configFilePath = source.explicitPath;
|
|
5460
|
-
} else if (source.raw !== null && source.sourcePath === SLOCK_KIMI_CONFIG_CONTENT_ENV) {
|
|
5461
|
-
configFilePath = path9.join(ctx.workingDirectory, KIMI_GENERATED_CONFIG_FILE);
|
|
5462
|
-
configContent = source.raw;
|
|
5463
|
-
if (opts.writeGeneratedConfig !== false) {
|
|
5464
|
-
writeFileSync6(configFilePath, source.raw, { encoding: "utf8", mode: 384 });
|
|
5465
|
-
chmodSync(configFilePath, 384);
|
|
5466
|
-
}
|
|
5467
|
-
}
|
|
5468
|
-
if (configFilePath) {
|
|
5469
|
-
args.push("--config-file", configFilePath);
|
|
5470
|
-
}
|
|
5471
|
-
if (ctx.config.model && ctx.config.model !== "default") {
|
|
5472
|
-
args.push("--model", ctx.config.model);
|
|
5473
|
-
}
|
|
5474
|
-
return {
|
|
5475
|
-
args,
|
|
5476
|
-
env: buildKimiSpawnEnv(env),
|
|
5477
|
-
configFilePath,
|
|
5478
|
-
configContent
|
|
5479
|
-
};
|
|
5480
|
-
}
|
|
5481
5399
|
function resolveKimiSpawn(commandArgs, deps = {}) {
|
|
5482
5400
|
return {
|
|
5483
5401
|
command: resolveCommandOnPath("kimi", deps) ?? "kimi",
|
|
@@ -5501,25 +5419,7 @@ var KimiDriver = class {
|
|
|
5501
5419
|
};
|
|
5502
5420
|
model = {
|
|
5503
5421
|
detectedModelsVerifiedAs: "launchable",
|
|
5504
|
-
toLaunchSpec: (modelId
|
|
5505
|
-
if (!ctx) return { args: ["--model", modelId] };
|
|
5506
|
-
const launchCtx = {
|
|
5507
|
-
...ctx,
|
|
5508
|
-
config: {
|
|
5509
|
-
...ctx.config,
|
|
5510
|
-
model: modelId
|
|
5511
|
-
}
|
|
5512
|
-
};
|
|
5513
|
-
const launch = buildKimiLaunchOptions(launchCtx, {
|
|
5514
|
-
home: opts?.home,
|
|
5515
|
-
writeGeneratedConfig: false
|
|
5516
|
-
});
|
|
5517
|
-
return {
|
|
5518
|
-
args: launch.args,
|
|
5519
|
-
env: launch.env,
|
|
5520
|
-
configFiles: launch.configFilePath ? [launch.configFilePath] : void 0
|
|
5521
|
-
};
|
|
5522
|
-
}
|
|
5422
|
+
toLaunchSpec: (modelId) => ({ args: ["--model", modelId] })
|
|
5523
5423
|
};
|
|
5524
5424
|
supportsStdinNotification = true;
|
|
5525
5425
|
mcpToolPrefix = "";
|
|
@@ -5573,7 +5473,6 @@ var KimiDriver = class {
|
|
|
5573
5473
|
}
|
|
5574
5474
|
}
|
|
5575
5475
|
}), "utf8");
|
|
5576
|
-
const launch = buildKimiLaunchOptions(ctx);
|
|
5577
5476
|
const args = [
|
|
5578
5477
|
"--wire",
|
|
5579
5478
|
"--yolo",
|
|
@@ -5582,16 +5481,15 @@ var KimiDriver = class {
|
|
|
5582
5481
|
"--mcp-config-file",
|
|
5583
5482
|
mcpConfigPath,
|
|
5584
5483
|
"--session",
|
|
5585
|
-
this.sessionId
|
|
5586
|
-
...launch.args
|
|
5484
|
+
this.sessionId
|
|
5587
5485
|
];
|
|
5588
5486
|
const launchRuntimeFields = runtimeConfigToLaunchFields(ctx.config);
|
|
5589
5487
|
if (launchRuntimeFields.model && launchRuntimeFields.model !== "default") {
|
|
5590
5488
|
args.push("--model", launchRuntimeFields.model);
|
|
5591
5489
|
}
|
|
5592
5490
|
const spawnEnv = (await prepareCliTransport(ctx, { NO_COLOR: "1" })).spawnEnv;
|
|
5593
|
-
const
|
|
5594
|
-
const proc = spawn7(
|
|
5491
|
+
const launch = resolveKimiSpawn(args);
|
|
5492
|
+
const proc = spawn7(launch.command, launch.args, {
|
|
5595
5493
|
cwd: ctx.workingDirectory,
|
|
5596
5494
|
stdio: ["pipe", "pipe", "pipe"],
|
|
5597
5495
|
env: spawnEnv,
|
|
@@ -5599,7 +5497,7 @@ var KimiDriver = class {
|
|
|
5599
5497
|
// and has an 8191-character command-line limit. Kimi's official
|
|
5600
5498
|
// installer/uv entrypoint is an executable, so launch it directly and
|
|
5601
5499
|
// keep prompts on stdin / files instead of routing through cmd.exe.
|
|
5602
|
-
shell:
|
|
5500
|
+
shell: launch.shell
|
|
5603
5501
|
});
|
|
5604
5502
|
proc.stdin?.write(JSON.stringify({
|
|
5605
5503
|
jsonrpc: "2.0",
|
|
@@ -5713,9 +5611,14 @@ var KimiDriver = class {
|
|
|
5713
5611
|
return detectKimiModels();
|
|
5714
5612
|
}
|
|
5715
5613
|
};
|
|
5716
|
-
function detectKimiModels(home = os3.homedir()
|
|
5717
|
-
const
|
|
5718
|
-
|
|
5614
|
+
function detectKimiModels(home = os3.homedir()) {
|
|
5615
|
+
const configPath = path9.join(home, ".kimi", "config.toml");
|
|
5616
|
+
let raw;
|
|
5617
|
+
try {
|
|
5618
|
+
raw = readFileSync3(configPath, "utf8");
|
|
5619
|
+
} catch {
|
|
5620
|
+
return null;
|
|
5621
|
+
}
|
|
5719
5622
|
const models = [];
|
|
5720
5623
|
const sectionRe = /^\s*\[models(?:\.([^\]]+)|"\.[^"]+"|\."[^"]+")\s*\]\s*$/gm;
|
|
5721
5624
|
const lineRe = /^\s*\[models\.(.+?)\s*\]\s*$/gm;
|
|
@@ -5979,7 +5882,7 @@ function runOpenCodeModelsCommand(home, deps = {}) {
|
|
|
5979
5882
|
const platform = deps.platform ?? process.platform;
|
|
5980
5883
|
const spawnSyncFn = deps.spawnSyncFn ?? spawnSync2;
|
|
5981
5884
|
const result = spawnSyncFn("opencode", ["models"], {
|
|
5982
|
-
env:
|
|
5885
|
+
env: { ...process.env, HOME: home, FORCE_COLOR: "0", NO_COLOR: "1" },
|
|
5983
5886
|
encoding: "utf8",
|
|
5984
5887
|
timeout: 5e3,
|
|
5985
5888
|
shell: platform === "win32"
|
|
@@ -6242,7 +6145,6 @@ var OpenCodeDriver = class {
|
|
|
6242
6145
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
6243
6146
|
import { EventEmitter } from "events";
|
|
6244
6147
|
import { mkdirSync as mkdirSync4, readdirSync } from "fs";
|
|
6245
|
-
import { PassThrough, Writable } from "stream";
|
|
6246
6148
|
import path11 from "path";
|
|
6247
6149
|
import {
|
|
6248
6150
|
AuthStorage,
|
|
@@ -6261,6 +6163,13 @@ var PI_PROVIDER_LABELS = {
|
|
|
6261
6163
|
openai: "OpenAI",
|
|
6262
6164
|
openrouter: "OpenRouter"
|
|
6263
6165
|
};
|
|
6166
|
+
function createPiSdkEventMappingState(sessionId = null) {
|
|
6167
|
+
return {
|
|
6168
|
+
sessionId,
|
|
6169
|
+
sessionAnnounced: false,
|
|
6170
|
+
sawTextDelta: false
|
|
6171
|
+
};
|
|
6172
|
+
}
|
|
6264
6173
|
function buildPiSessionDir(workingDirectory) {
|
|
6265
6174
|
return path11.join(workingDirectory, PI_SESSION_DIR);
|
|
6266
6175
|
}
|
|
@@ -6286,12 +6195,6 @@ function findPiSessionFile(sessionDir, sessionId) {
|
|
|
6286
6195
|
const match = entries.find((entry) => entry.endsWith(suffix));
|
|
6287
6196
|
return match ? path11.join(sessionDir, match) : null;
|
|
6288
6197
|
}
|
|
6289
|
-
function piSdkEventToJsonLine(event) {
|
|
6290
|
-
if (event.type === "agent_end") {
|
|
6291
|
-
return JSON.stringify({ type: "agent_end" });
|
|
6292
|
-
}
|
|
6293
|
-
return JSON.stringify(event);
|
|
6294
|
-
}
|
|
6295
6198
|
function detectPiModelsFromRegistry(modelRegistry) {
|
|
6296
6199
|
const models = [];
|
|
6297
6200
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -6341,114 +6244,309 @@ function piErrorMessage(error) {
|
|
|
6341
6244
|
}
|
|
6342
6245
|
return "Unknown Pi error";
|
|
6343
6246
|
}
|
|
6344
|
-
|
|
6345
|
-
|
|
6346
|
-
|
|
6347
|
-
|
|
6348
|
-
|
|
6349
|
-
|
|
6350
|
-
|
|
6351
|
-
|
|
6352
|
-
|
|
6353
|
-
|
|
6354
|
-
|
|
6355
|
-
|
|
6356
|
-
|
|
6247
|
+
function pushSessionInitIfNeeded(state, events) {
|
|
6248
|
+
if (!state.sessionAnnounced && state.sessionId) {
|
|
6249
|
+
events.push({ kind: "session_init", sessionId: state.sessionId });
|
|
6250
|
+
state.sessionAnnounced = true;
|
|
6251
|
+
}
|
|
6252
|
+
}
|
|
6253
|
+
function mapPiAssistantMessageEvent(assistantEvent, state) {
|
|
6254
|
+
switch (assistantEvent.type) {
|
|
6255
|
+
case "thinking_delta":
|
|
6256
|
+
return typeof assistantEvent.delta === "string" && assistantEvent.delta.length > 0 ? [{ kind: "thinking", text: assistantEvent.delta }] : [];
|
|
6257
|
+
case "text_delta":
|
|
6258
|
+
if (typeof assistantEvent.delta === "string" && assistantEvent.delta.length > 0) {
|
|
6259
|
+
state.sawTextDelta = true;
|
|
6260
|
+
return [{ kind: "text", text: assistantEvent.delta }];
|
|
6357
6261
|
}
|
|
6358
|
-
|
|
6359
|
-
|
|
6360
|
-
|
|
6361
|
-
|
|
6262
|
+
return [];
|
|
6263
|
+
case "text_end":
|
|
6264
|
+
return !state.sawTextDelta && typeof assistantEvent.content === "string" && assistantEvent.content.length > 0 ? [{ kind: "text", text: assistantEvent.content }] : [];
|
|
6265
|
+
case "error":
|
|
6266
|
+
return [{ kind: "error", message: piErrorMessage(assistantEvent.error.errorMessage || assistantEvent.error) }];
|
|
6267
|
+
case "thinking_start":
|
|
6268
|
+
case "thinking_end":
|
|
6269
|
+
case "text_start":
|
|
6270
|
+
case "toolcall_start":
|
|
6271
|
+
case "toolcall_delta":
|
|
6272
|
+
case "toolcall_end":
|
|
6273
|
+
case "start":
|
|
6274
|
+
case "done":
|
|
6275
|
+
return [];
|
|
6276
|
+
default: {
|
|
6277
|
+
const _exhaustive = assistantEvent;
|
|
6278
|
+
return _exhaustive;
|
|
6279
|
+
}
|
|
6280
|
+
}
|
|
6281
|
+
}
|
|
6282
|
+
function mapPiSdkEventToParsedEvents(event, state) {
|
|
6283
|
+
const events = [];
|
|
6284
|
+
pushSessionInitIfNeeded(state, events);
|
|
6285
|
+
switch (event.type) {
|
|
6286
|
+
case "agent_start":
|
|
6287
|
+
case "turn_start":
|
|
6288
|
+
case "turn_end":
|
|
6289
|
+
case "message_end":
|
|
6290
|
+
case "tool_execution_update":
|
|
6291
|
+
case "queue_update":
|
|
6292
|
+
case "session_info_changed":
|
|
6293
|
+
case "thinking_level_changed":
|
|
6294
|
+
case "auto_retry_start":
|
|
6295
|
+
case "auto_retry_end":
|
|
6296
|
+
return events;
|
|
6297
|
+
case "message_start":
|
|
6298
|
+
if (event.message.role === "assistant") {
|
|
6299
|
+
state.sawTextDelta = false;
|
|
6300
|
+
}
|
|
6301
|
+
return events;
|
|
6302
|
+
case "message_update":
|
|
6303
|
+
events.push(...mapPiAssistantMessageEvent(event.assistantMessageEvent, state));
|
|
6304
|
+
return events;
|
|
6305
|
+
case "tool_execution_start":
|
|
6306
|
+
events.push({
|
|
6307
|
+
kind: "tool_call",
|
|
6308
|
+
name: event.toolName || "unknown_tool",
|
|
6309
|
+
input: event.args ?? {}
|
|
6310
|
+
});
|
|
6311
|
+
return events;
|
|
6312
|
+
case "tool_execution_end":
|
|
6313
|
+
events.push({ kind: "tool_output", name: event.toolName || "unknown_tool" });
|
|
6314
|
+
return events;
|
|
6315
|
+
case "compaction_start":
|
|
6316
|
+
events.push({ kind: "compaction_started" });
|
|
6317
|
+
return events;
|
|
6318
|
+
case "compaction_end":
|
|
6319
|
+
events.push({ kind: "compaction_finished" });
|
|
6320
|
+
return events;
|
|
6321
|
+
case "agent_end":
|
|
6322
|
+
events.push({ kind: "turn_end", sessionId: state.sessionId || void 0 });
|
|
6323
|
+
return events;
|
|
6324
|
+
default: {
|
|
6325
|
+
const _exhaustive = event;
|
|
6326
|
+
return _exhaustive;
|
|
6327
|
+
}
|
|
6362
6328
|
}
|
|
6363
|
-
|
|
6364
|
-
|
|
6365
|
-
|
|
6366
|
-
|
|
6367
|
-
|
|
6368
|
-
|
|
6369
|
-
|
|
6370
|
-
|
|
6371
|
-
|
|
6372
|
-
|
|
6373
|
-
|
|
6374
|
-
|
|
6375
|
-
|
|
6376
|
-
|
|
6377
|
-
|
|
6329
|
+
}
|
|
6330
|
+
var PI_RUNTIME_SESSION_DESCRIPTOR = {
|
|
6331
|
+
transport: "sdk",
|
|
6332
|
+
lifecycle: "sdk_session",
|
|
6333
|
+
input: {
|
|
6334
|
+
initial: "start",
|
|
6335
|
+
idle: "sdk_prompt",
|
|
6336
|
+
busy: "sdk_steer"
|
|
6337
|
+
},
|
|
6338
|
+
readiness: "sdk_ready",
|
|
6339
|
+
turnBoundary: "sdk_event",
|
|
6340
|
+
startPolicy: "immediate",
|
|
6341
|
+
inFlightWake: "steer",
|
|
6342
|
+
busyDelivery: "direct",
|
|
6343
|
+
postTurn: "keep_alive"
|
|
6344
|
+
};
|
|
6345
|
+
async function createPiAgentSessionForContext(ctx, sessionId) {
|
|
6346
|
+
const sessionDir = buildPiSessionDir(ctx.workingDirectory);
|
|
6347
|
+
mkdirSync4(sessionDir, { recursive: true });
|
|
6348
|
+
const spawnEnv = await buildPiSpawnEnv(ctx);
|
|
6349
|
+
const agentDir = spawnEnv.PI_CODING_AGENT_DIR || getAgentDir();
|
|
6350
|
+
const authStorage = AuthStorage.create(path11.join(agentDir, "auth.json"));
|
|
6351
|
+
const modelRegistry = ModelRegistry.create(authStorage, path11.join(agentDir, "models.json"));
|
|
6352
|
+
const launchRuntimeFields = runtimeConfigToLaunchFields(ctx.config);
|
|
6353
|
+
const model = resolvePiModelFromRegistry(launchRuntimeFields.model, modelRegistry);
|
|
6354
|
+
if (launchRuntimeFields.model && launchRuntimeFields.model !== "default" && !model) {
|
|
6355
|
+
throw new Error(`Pi model not found: ${launchRuntimeFields.model}`);
|
|
6356
|
+
}
|
|
6357
|
+
const settingsManager = SettingsManager.inMemory({ compaction: { enabled: false } });
|
|
6358
|
+
const resourceLoader = new DefaultResourceLoader({
|
|
6359
|
+
cwd: ctx.workingDirectory,
|
|
6360
|
+
agentDir,
|
|
6361
|
+
settingsManager,
|
|
6362
|
+
systemPromptOverride: () => ctx.standingPrompt
|
|
6363
|
+
});
|
|
6364
|
+
await resourceLoader.reload();
|
|
6365
|
+
const existingSessionFile = ctx.config.sessionId ? findPiSessionFile(sessionDir, ctx.config.sessionId) : null;
|
|
6366
|
+
const sessionManager = existingSessionFile ? SessionManager.open(existingSessionFile, sessionDir, ctx.workingDirectory) : SessionManager.create(ctx.workingDirectory, sessionDir, { id: sessionId });
|
|
6367
|
+
const { session } = await createAgentSession({
|
|
6368
|
+
cwd: ctx.workingDirectory,
|
|
6369
|
+
agentDir,
|
|
6370
|
+
model,
|
|
6371
|
+
thinkingLevel: launchRuntimeFields.reasoningEffort,
|
|
6372
|
+
authStorage,
|
|
6373
|
+
modelRegistry,
|
|
6374
|
+
resourceLoader,
|
|
6375
|
+
customTools: [
|
|
6376
|
+
createBashTool(ctx.workingDirectory, {
|
|
6377
|
+
spawnHook: (spawnContext) => ({
|
|
6378
|
+
...spawnContext,
|
|
6379
|
+
env: {
|
|
6380
|
+
...spawnContext.env,
|
|
6381
|
+
...spawnEnv
|
|
6382
|
+
}
|
|
6383
|
+
})
|
|
6384
|
+
})
|
|
6385
|
+
],
|
|
6386
|
+
sessionManager,
|
|
6387
|
+
settingsManager
|
|
6388
|
+
});
|
|
6389
|
+
return session;
|
|
6390
|
+
}
|
|
6391
|
+
var PiSdkRuntimeSession = class {
|
|
6392
|
+
constructor(ctx, setCurrentSessionId, sessionFactory = createPiAgentSessionForContext) {
|
|
6393
|
+
this.ctx = ctx;
|
|
6394
|
+
this.setCurrentSessionId = setCurrentSessionId;
|
|
6395
|
+
this.sessionFactory = sessionFactory;
|
|
6396
|
+
this.mappingState = createPiSdkEventMappingState(ctx.config.sessionId || null);
|
|
6397
|
+
}
|
|
6398
|
+
descriptor = PI_RUNTIME_SESSION_DESCRIPTOR;
|
|
6399
|
+
events = new EventEmitter();
|
|
6400
|
+
mappingState;
|
|
6401
|
+
session = null;
|
|
6402
|
+
unsubscribe = null;
|
|
6403
|
+
started = false;
|
|
6404
|
+
didClose = false;
|
|
6405
|
+
requestedStopReason;
|
|
6406
|
+
exitInfo = null;
|
|
6407
|
+
get pid() {
|
|
6408
|
+
return void 0;
|
|
6378
6409
|
}
|
|
6379
|
-
|
|
6380
|
-
return this;
|
|
6410
|
+
get currentSessionId() {
|
|
6411
|
+
return this.mappingState.sessionId;
|
|
6381
6412
|
}
|
|
6382
|
-
|
|
6383
|
-
return this;
|
|
6413
|
+
get exitCode() {
|
|
6414
|
+
return this.exitInfo?.code ?? null;
|
|
6384
6415
|
}
|
|
6385
|
-
|
|
6386
|
-
this.
|
|
6387
|
-
const lines = this.buffer.split("\n");
|
|
6388
|
-
this.buffer = lines.pop() || "";
|
|
6389
|
-
for (const line of lines) {
|
|
6390
|
-
if (!line.trim() || this.closed) continue;
|
|
6391
|
-
await this.handleCommand(line);
|
|
6392
|
-
}
|
|
6416
|
+
get signalCode() {
|
|
6417
|
+
return this.exitInfo?.signal ?? null;
|
|
6393
6418
|
}
|
|
6394
|
-
|
|
6395
|
-
|
|
6396
|
-
|
|
6397
|
-
|
|
6398
|
-
|
|
6399
|
-
|
|
6400
|
-
|
|
6419
|
+
get closed() {
|
|
6420
|
+
return this.didClose;
|
|
6421
|
+
}
|
|
6422
|
+
on(event, cb) {
|
|
6423
|
+
this.events.on(event, cb);
|
|
6424
|
+
}
|
|
6425
|
+
async start(input) {
|
|
6426
|
+
if (this.started) {
|
|
6427
|
+
return { ok: false, reason: "runtime_error", error: "runtime session already started" };
|
|
6401
6428
|
}
|
|
6402
|
-
|
|
6403
|
-
|
|
6404
|
-
|
|
6405
|
-
|
|
6406
|
-
|
|
6407
|
-
|
|
6408
|
-
|
|
6409
|
-
|
|
6410
|
-
|
|
6411
|
-
|
|
6429
|
+
if (this.didClose) return { ok: false, reason: "closed" };
|
|
6430
|
+
this.started = true;
|
|
6431
|
+
const sessionId = input.sessionId || this.ctx.config.sessionId || randomUUID3();
|
|
6432
|
+
this.mappingState.sessionId = sessionId;
|
|
6433
|
+
this.setCurrentSessionId(sessionId);
|
|
6434
|
+
const session = await this.sessionFactory({
|
|
6435
|
+
...this.ctx,
|
|
6436
|
+
config: {
|
|
6437
|
+
...this.ctx.config,
|
|
6438
|
+
sessionId
|
|
6412
6439
|
}
|
|
6413
|
-
}
|
|
6414
|
-
|
|
6415
|
-
|
|
6416
|
-
|
|
6417
|
-
|
|
6418
|
-
|
|
6419
|
-
|
|
6420
|
-
}
|
|
6440
|
+
}, sessionId);
|
|
6441
|
+
this.session = session;
|
|
6442
|
+
this.mappingState.sessionId = session.sessionId;
|
|
6443
|
+
this.setCurrentSessionId(session.sessionId);
|
|
6444
|
+
this.unsubscribe = session.subscribe((event) => {
|
|
6445
|
+
for (const parsed of mapPiSdkEventToParsedEvents(event, this.mappingState)) {
|
|
6446
|
+
this.events.emit("runtime_event", parsed);
|
|
6447
|
+
}
|
|
6448
|
+
});
|
|
6449
|
+
this.emitSessionInit();
|
|
6450
|
+
this.launchPrompt(input.text);
|
|
6451
|
+
return { ok: true, acceptedAs: "prompt" };
|
|
6452
|
+
}
|
|
6453
|
+
send(input) {
|
|
6454
|
+
if (this.didClose) return { ok: false, reason: "closed" };
|
|
6455
|
+
const session = this.session;
|
|
6456
|
+
if (!session) return { ok: false, reason: "closed" };
|
|
6457
|
+
if (input.mode === "busy") {
|
|
6458
|
+
this.deferSdkCall(() => session.steer(input.text));
|
|
6459
|
+
return { ok: true, acceptedAs: "steer" };
|
|
6460
|
+
}
|
|
6461
|
+
if (session.isStreaming) {
|
|
6462
|
+
return { ok: false, reason: "busy_rejected", error: "Pi session is still streaming" };
|
|
6463
|
+
}
|
|
6464
|
+
this.launchPrompt(input.text);
|
|
6465
|
+
return { ok: true, acceptedAs: "prompt" };
|
|
6466
|
+
}
|
|
6467
|
+
async stop(opts) {
|
|
6468
|
+
if (this.didClose) return;
|
|
6469
|
+
this.requestedStopReason = opts?.reason;
|
|
6470
|
+
const signal = opts?.signal ?? "SIGTERM";
|
|
6471
|
+
const session = this.session;
|
|
6472
|
+
if (session?.isStreaming) {
|
|
6473
|
+
try {
|
|
6474
|
+
await session.abort();
|
|
6475
|
+
} catch (error) {
|
|
6476
|
+
this.events.emit("stderr", piErrorMessage(error));
|
|
6477
|
+
}
|
|
6478
|
+
}
|
|
6479
|
+
await this.disposeSession();
|
|
6480
|
+
this.emitExitAndClose(null, signal);
|
|
6481
|
+
}
|
|
6482
|
+
async dispose() {
|
|
6483
|
+
if (this.didClose) return;
|
|
6484
|
+
await this.disposeSession();
|
|
6485
|
+
this.emitExitAndClose(0, null);
|
|
6486
|
+
}
|
|
6487
|
+
emitSessionInit() {
|
|
6488
|
+
const sessionId = this.mappingState.sessionId;
|
|
6489
|
+
if (!sessionId || this.mappingState.sessionAnnounced) return;
|
|
6490
|
+
this.mappingState.sessionAnnounced = true;
|
|
6491
|
+
this.events.emit("runtime_event", { kind: "session_init", sessionId });
|
|
6492
|
+
}
|
|
6493
|
+
launchPrompt(text) {
|
|
6494
|
+
const session = this.session;
|
|
6495
|
+
if (!session) {
|
|
6496
|
+
this.events.emit("runtime_event", {
|
|
6497
|
+
kind: "error",
|
|
6498
|
+
message: "Pi SDK session is not started"
|
|
6499
|
+
});
|
|
6500
|
+
return;
|
|
6421
6501
|
}
|
|
6502
|
+
this.deferSdkCall(() => session.prompt(text));
|
|
6422
6503
|
}
|
|
6423
|
-
|
|
6424
|
-
|
|
6425
|
-
|
|
6426
|
-
|
|
6427
|
-
|
|
6428
|
-
|
|
6429
|
-
|
|
6504
|
+
deferSdkCall(invoke) {
|
|
6505
|
+
setImmediate(() => {
|
|
6506
|
+
if (this.didClose) return;
|
|
6507
|
+
try {
|
|
6508
|
+
void invoke().catch((error) => {
|
|
6509
|
+
if (this.didClose) return;
|
|
6510
|
+
this.events.emit("runtime_event", {
|
|
6511
|
+
kind: "error",
|
|
6512
|
+
message: piErrorMessage(error)
|
|
6513
|
+
});
|
|
6514
|
+
});
|
|
6515
|
+
} catch (error) {
|
|
6516
|
+
if (this.didClose) return;
|
|
6517
|
+
this.events.emit("runtime_event", {
|
|
6518
|
+
kind: "error",
|
|
6519
|
+
message: piErrorMessage(error)
|
|
6520
|
+
});
|
|
6521
|
+
}
|
|
6522
|
+
});
|
|
6430
6523
|
}
|
|
6431
|
-
async
|
|
6432
|
-
|
|
6433
|
-
this.
|
|
6434
|
-
this.exitCode = code;
|
|
6435
|
-
this.signalCode = signal;
|
|
6524
|
+
async disposeSession() {
|
|
6525
|
+
const unsubscribe = this.unsubscribe;
|
|
6526
|
+
this.unsubscribe = null;
|
|
6436
6527
|
try {
|
|
6437
|
-
|
|
6438
|
-
|
|
6439
|
-
}
|
|
6440
|
-
} catch (error) {
|
|
6441
|
-
this.stderr.write(piErrorMessage(error) + "\n");
|
|
6528
|
+
unsubscribe?.();
|
|
6529
|
+
} catch {
|
|
6442
6530
|
}
|
|
6531
|
+
const session = this.session;
|
|
6532
|
+
this.session = null;
|
|
6443
6533
|
try {
|
|
6444
|
-
|
|
6445
|
-
} catch {
|
|
6534
|
+
session?.dispose();
|
|
6535
|
+
} catch (error) {
|
|
6536
|
+
this.events.emit("stderr", piErrorMessage(error));
|
|
6446
6537
|
}
|
|
6447
|
-
|
|
6448
|
-
|
|
6449
|
-
this.
|
|
6450
|
-
this.
|
|
6451
|
-
|
|
6538
|
+
}
|
|
6539
|
+
emitExitAndClose(code, signal) {
|
|
6540
|
+
if (this.didClose) return;
|
|
6541
|
+
this.didClose = true;
|
|
6542
|
+
const info = {
|
|
6543
|
+
code,
|
|
6544
|
+
signal,
|
|
6545
|
+
reason: this.requestedStopReason ? "requested" : "runtime_exit"
|
|
6546
|
+
};
|
|
6547
|
+
this.exitInfo = info;
|
|
6548
|
+
this.events.emit("exit", info);
|
|
6549
|
+
this.events.emit("close", info);
|
|
6452
6550
|
}
|
|
6453
6551
|
};
|
|
6454
6552
|
var PiDriver = class {
|
|
@@ -6475,10 +6573,9 @@ var PiDriver = class {
|
|
|
6475
6573
|
busyDeliveryMode = "direct";
|
|
6476
6574
|
usesSlockCliForCommunication = true;
|
|
6477
6575
|
sessionId = null;
|
|
6478
|
-
|
|
6479
|
-
|
|
6480
|
-
|
|
6481
|
-
process = null;
|
|
6576
|
+
get currentSessionId() {
|
|
6577
|
+
return this.sessionId;
|
|
6578
|
+
}
|
|
6482
6579
|
probe() {
|
|
6483
6580
|
return {
|
|
6484
6581
|
available: true,
|
|
@@ -6488,144 +6585,20 @@ var PiDriver = class {
|
|
|
6488
6585
|
async detectModels() {
|
|
6489
6586
|
return detectPiModels();
|
|
6490
6587
|
}
|
|
6491
|
-
|
|
6492
|
-
this.sessionId = ctx.config.sessionId ||
|
|
6493
|
-
|
|
6494
|
-
|
|
6495
|
-
this.requestId = 0;
|
|
6496
|
-
const sessionDir = buildPiSessionDir(ctx.workingDirectory);
|
|
6497
|
-
mkdirSync4(sessionDir, { recursive: true });
|
|
6498
|
-
const spawnEnv = await buildPiSpawnEnv(ctx);
|
|
6499
|
-
const agentDir = spawnEnv.PI_CODING_AGENT_DIR || getAgentDir();
|
|
6500
|
-
const authStorage = AuthStorage.create(path11.join(agentDir, "auth.json"));
|
|
6501
|
-
const modelRegistry = ModelRegistry.create(authStorage, path11.join(agentDir, "models.json"));
|
|
6502
|
-
const launchRuntimeFields = runtimeConfigToLaunchFields(ctx.config);
|
|
6503
|
-
const model = resolvePiModelFromRegistry(launchRuntimeFields.model, modelRegistry);
|
|
6504
|
-
if (launchRuntimeFields.model && launchRuntimeFields.model !== "default" && !model) {
|
|
6505
|
-
throw new Error(`Pi model not found: ${launchRuntimeFields.model}`);
|
|
6506
|
-
}
|
|
6507
|
-
const settingsManager = SettingsManager.inMemory({ compaction: { enabled: false } });
|
|
6508
|
-
const resourceLoader = new DefaultResourceLoader({
|
|
6509
|
-
cwd: ctx.workingDirectory,
|
|
6510
|
-
agentDir,
|
|
6511
|
-
settingsManager,
|
|
6512
|
-
systemPromptOverride: () => ctx.standingPrompt
|
|
6513
|
-
});
|
|
6514
|
-
await resourceLoader.reload();
|
|
6515
|
-
const existingSessionFile = ctx.config.sessionId ? findPiSessionFile(sessionDir, ctx.config.sessionId) : null;
|
|
6516
|
-
const sessionManager = existingSessionFile ? SessionManager.open(existingSessionFile, sessionDir, ctx.workingDirectory) : SessionManager.create(ctx.workingDirectory, sessionDir, { id: this.sessionId });
|
|
6517
|
-
const { session } = await createAgentSession({
|
|
6518
|
-
cwd: ctx.workingDirectory,
|
|
6519
|
-
agentDir,
|
|
6520
|
-
model,
|
|
6521
|
-
thinkingLevel: launchRuntimeFields.reasoningEffort,
|
|
6522
|
-
authStorage,
|
|
6523
|
-
modelRegistry,
|
|
6524
|
-
resourceLoader,
|
|
6525
|
-
customTools: [
|
|
6526
|
-
createBashTool(ctx.workingDirectory, {
|
|
6527
|
-
spawnHook: (spawnContext) => ({
|
|
6528
|
-
...spawnContext,
|
|
6529
|
-
env: {
|
|
6530
|
-
...spawnContext.env,
|
|
6531
|
-
...spawnEnv
|
|
6532
|
-
}
|
|
6533
|
-
})
|
|
6534
|
-
})
|
|
6535
|
-
],
|
|
6536
|
-
sessionManager,
|
|
6537
|
-
settingsManager
|
|
6538
|
-
});
|
|
6539
|
-
this.sessionId = session.sessionId;
|
|
6540
|
-
const proc = new PiSdkProcess(session);
|
|
6541
|
-
this.process = proc;
|
|
6542
|
-
setImmediate(() => {
|
|
6543
|
-
if (this.process === proc && !proc.killed) {
|
|
6544
|
-
this.sendRpcCommand("prompt", { message: ctx.prompt });
|
|
6545
|
-
}
|
|
6588
|
+
createSession(ctx) {
|
|
6589
|
+
this.sessionId = ctx.config.sessionId || null;
|
|
6590
|
+
return new PiSdkRuntimeSession(ctx, (sessionId) => {
|
|
6591
|
+
this.sessionId = sessionId;
|
|
6546
6592
|
});
|
|
6547
|
-
return { process: proc };
|
|
6548
6593
|
}
|
|
6549
|
-
|
|
6550
|
-
|
|
6551
|
-
try {
|
|
6552
|
-
event = JSON.parse(line);
|
|
6553
|
-
} catch {
|
|
6554
|
-
return [];
|
|
6555
|
-
}
|
|
6556
|
-
const events = [];
|
|
6557
|
-
if (event.type === "session" && event.id) {
|
|
6558
|
-
this.sessionId = event.id;
|
|
6559
|
-
if (!this.sessionAnnounced) {
|
|
6560
|
-
this.sessionAnnounced = true;
|
|
6561
|
-
events.push({ kind: "session_init", sessionId: event.id });
|
|
6562
|
-
}
|
|
6563
|
-
return events;
|
|
6564
|
-
}
|
|
6565
|
-
if (!this.sessionAnnounced && this.sessionId) {
|
|
6566
|
-
events.push({ kind: "session_init", sessionId: this.sessionId });
|
|
6567
|
-
this.sessionAnnounced = true;
|
|
6568
|
-
}
|
|
6569
|
-
if (event.type === "response") {
|
|
6570
|
-
if (event.data?.sessionId && event.data.sessionId !== this.sessionId) {
|
|
6571
|
-
this.sessionId = event.data.sessionId;
|
|
6572
|
-
}
|
|
6573
|
-
if (event.success === false) {
|
|
6574
|
-
events.push({ kind: "error", message: piErrorMessage(event.error) });
|
|
6575
|
-
}
|
|
6576
|
-
return events;
|
|
6577
|
-
}
|
|
6578
|
-
if (event.type === "message_start" && event.message?.role === "assistant") {
|
|
6579
|
-
this.sawTextDelta = false;
|
|
6580
|
-
return events;
|
|
6581
|
-
}
|
|
6582
|
-
const assistantEvent = event.assistantMessageEvent;
|
|
6583
|
-
if (event.type === "message_update" && assistantEvent) {
|
|
6584
|
-
switch (assistantEvent.type) {
|
|
6585
|
-
case "thinking_delta":
|
|
6586
|
-
if (typeof assistantEvent.delta === "string" && assistantEvent.delta.length > 0) {
|
|
6587
|
-
events.push({ kind: "thinking", text: assistantEvent.delta });
|
|
6588
|
-
}
|
|
6589
|
-
break;
|
|
6590
|
-
case "text_delta":
|
|
6591
|
-
if (typeof assistantEvent.delta === "string" && assistantEvent.delta.length > 0) {
|
|
6592
|
-
this.sawTextDelta = true;
|
|
6593
|
-
events.push({ kind: "text", text: assistantEvent.delta });
|
|
6594
|
-
}
|
|
6595
|
-
break;
|
|
6596
|
-
case "thinking_start":
|
|
6597
|
-
case "text_start":
|
|
6598
|
-
break;
|
|
6599
|
-
case "tool_use":
|
|
6600
|
-
case "tool_call":
|
|
6601
|
-
case "tool_start":
|
|
6602
|
-
events.push({
|
|
6603
|
-
kind: "tool_call",
|
|
6604
|
-
name: assistantEvent.name || assistantEvent.toolName || "unknown_tool",
|
|
6605
|
-
input: assistantEvent.input ?? assistantEvent.parameters ?? {}
|
|
6606
|
-
});
|
|
6607
|
-
break;
|
|
6608
|
-
case "text_end":
|
|
6609
|
-
if (!this.sawTextDelta && typeof assistantEvent.content === "string" && assistantEvent.content.length > 0) {
|
|
6610
|
-
events.push({ kind: "text", text: assistantEvent.content });
|
|
6611
|
-
}
|
|
6612
|
-
break;
|
|
6613
|
-
}
|
|
6614
|
-
return events;
|
|
6615
|
-
}
|
|
6616
|
-
if (event.type === "agent_end") {
|
|
6617
|
-
events.push({ kind: "turn_end", sessionId: this.sessionId || void 0 });
|
|
6618
|
-
} else if (event.type === "error") {
|
|
6619
|
-
events.push({ kind: "error", message: piErrorMessage(event.error ?? event.message) });
|
|
6620
|
-
}
|
|
6621
|
-
return events;
|
|
6594
|
+
async spawn(_ctx) {
|
|
6595
|
+
throw new Error("PiDriver uses a native RuntimeSession; child-process spawn is unsupported");
|
|
6622
6596
|
}
|
|
6623
|
-
|
|
6624
|
-
return
|
|
6625
|
-
|
|
6626
|
-
|
|
6627
|
-
|
|
6628
|
-
});
|
|
6597
|
+
parseLine(_line) {
|
|
6598
|
+
return [];
|
|
6599
|
+
}
|
|
6600
|
+
encodeStdinMessage(_text, _sessionId, _opts) {
|
|
6601
|
+
return null;
|
|
6629
6602
|
}
|
|
6630
6603
|
buildSystemPrompt(config, _agentId) {
|
|
6631
6604
|
return buildCliTransportSystemPrompt(config, {
|
|
@@ -6638,18 +6611,141 @@ var PiDriver = class {
|
|
|
6638
6611
|
messageNotificationStyle: "direct"
|
|
6639
6612
|
});
|
|
6640
6613
|
}
|
|
6641
|
-
|
|
6642
|
-
|
|
6643
|
-
|
|
6614
|
+
};
|
|
6615
|
+
|
|
6616
|
+
// src/drivers/runtimeSession.ts
|
|
6617
|
+
import { EventEmitter as EventEmitter2 } from "events";
|
|
6618
|
+
function descriptorFromDriver(driver) {
|
|
6619
|
+
const lifecycle = driver.lifecycle.kind === "per_turn" ? "turn_based" : "persistent_stream";
|
|
6620
|
+
const idle = driver.supportsStdinNotification ? "stdin" : "unsupported";
|
|
6621
|
+
const busy = driver.supportsStdinNotification ? "stdin_steer" : "unsupported";
|
|
6622
|
+
return {
|
|
6623
|
+
transport: "child_process",
|
|
6624
|
+
lifecycle,
|
|
6625
|
+
input: {
|
|
6626
|
+
initial: "start",
|
|
6627
|
+
idle,
|
|
6628
|
+
busy
|
|
6629
|
+
},
|
|
6630
|
+
readiness: "spawned",
|
|
6631
|
+
turnBoundary: driver.lifecycle.kind === "per_turn" ? "process_exit" : "parsed_event",
|
|
6632
|
+
startPolicy: driver.lifecycle.kind === "per_turn" ? driver.lifecycle.start : "immediate",
|
|
6633
|
+
inFlightWake: driver.lifecycle.inFlightWake,
|
|
6634
|
+
busyDelivery: driver.busyDeliveryMode,
|
|
6635
|
+
postTurn: driver.terminateProcessOnTurnEnd ? "terminate_process" : driver.endStdinOnTurnEnd ? "close_stdin" : "keep_alive"
|
|
6636
|
+
};
|
|
6637
|
+
}
|
|
6638
|
+
var ChildProcessRuntimeSession = class {
|
|
6639
|
+
constructor(driver, ctx) {
|
|
6640
|
+
this.driver = driver;
|
|
6641
|
+
this.ctx = ctx;
|
|
6642
|
+
this.descriptor = descriptorFromDriver(driver);
|
|
6644
6643
|
}
|
|
6645
|
-
|
|
6646
|
-
|
|
6647
|
-
|
|
6648
|
-
|
|
6649
|
-
|
|
6650
|
-
|
|
6644
|
+
descriptor;
|
|
6645
|
+
events = new EventEmitter2();
|
|
6646
|
+
process = null;
|
|
6647
|
+
started = false;
|
|
6648
|
+
stdoutBuffer = "";
|
|
6649
|
+
requestedStopReason;
|
|
6650
|
+
get pid() {
|
|
6651
|
+
return this.process?.pid;
|
|
6652
|
+
}
|
|
6653
|
+
get currentSessionId() {
|
|
6654
|
+
return this.driver.currentSessionId;
|
|
6655
|
+
}
|
|
6656
|
+
get exitCode() {
|
|
6657
|
+
return this.process?.exitCode ?? null;
|
|
6658
|
+
}
|
|
6659
|
+
get signalCode() {
|
|
6660
|
+
return this.process?.signalCode ?? null;
|
|
6661
|
+
}
|
|
6662
|
+
get closed() {
|
|
6663
|
+
return this.process ? this.process.exitCode != null || this.process.signalCode != null : false;
|
|
6664
|
+
}
|
|
6665
|
+
on(event, cb) {
|
|
6666
|
+
this.events.on(event, cb);
|
|
6667
|
+
}
|
|
6668
|
+
async start(input) {
|
|
6669
|
+
if (this.started) {
|
|
6670
|
+
return { ok: false, reason: "runtime_error", error: "runtime session already started" };
|
|
6671
|
+
}
|
|
6672
|
+
this.started = true;
|
|
6673
|
+
const launchCtx = {
|
|
6674
|
+
...this.ctx,
|
|
6675
|
+
prompt: input.text,
|
|
6676
|
+
config: {
|
|
6677
|
+
...this.ctx.config,
|
|
6678
|
+
sessionId: input.sessionId ?? this.ctx.config.sessionId
|
|
6679
|
+
}
|
|
6680
|
+
};
|
|
6681
|
+
const { process: process2 } = await this.driver.spawn(launchCtx);
|
|
6682
|
+
this.process = process2;
|
|
6683
|
+
this.attachProcess(process2);
|
|
6684
|
+
return { ok: true, acceptedAs: "prompt" };
|
|
6685
|
+
}
|
|
6686
|
+
send(input) {
|
|
6687
|
+
const proc = this.process;
|
|
6688
|
+
if (!proc || this.closed) return { ok: false, reason: "closed" };
|
|
6689
|
+
const encoded = this.driver.encodeStdinMessage(input.text, input.sessionId ?? null, { mode: input.mode });
|
|
6690
|
+
if (!encoded) return { ok: false, reason: "unsupported" };
|
|
6691
|
+
proc.stdin?.write(encoded + "\n");
|
|
6692
|
+
return { ok: true, acceptedAs: input.mode === "busy" ? "steer" : "prompt" };
|
|
6693
|
+
}
|
|
6694
|
+
async stop(opts) {
|
|
6695
|
+
const proc = this.process;
|
|
6696
|
+
if (!proc || this.closed) return;
|
|
6697
|
+
this.requestedStopReason = opts?.reason;
|
|
6698
|
+
proc.kill(opts?.signal ?? "SIGTERM");
|
|
6699
|
+
if (!opts?.forceAfterMs) return;
|
|
6700
|
+
setTimeout(() => {
|
|
6701
|
+
if (!this.closed) {
|
|
6702
|
+
try {
|
|
6703
|
+
proc.kill("SIGKILL");
|
|
6704
|
+
} catch {
|
|
6705
|
+
}
|
|
6706
|
+
}
|
|
6707
|
+
}, opts.forceAfterMs).unref?.();
|
|
6708
|
+
}
|
|
6709
|
+
attachProcess(process2) {
|
|
6710
|
+
process2.stdout?.on("data", (chunk) => {
|
|
6711
|
+
const chunkText = chunk.toString();
|
|
6712
|
+
this.events.emit("stdout", chunkText);
|
|
6713
|
+
this.stdoutBuffer += chunkText;
|
|
6714
|
+
const lines = this.stdoutBuffer.split("\n");
|
|
6715
|
+
this.stdoutBuffer = lines.pop() || "";
|
|
6716
|
+
for (const line of lines) {
|
|
6717
|
+
if (!line.trim()) continue;
|
|
6718
|
+
for (const event of this.driver.parseLine(line)) {
|
|
6719
|
+
this.events.emit("runtime_event", event);
|
|
6720
|
+
}
|
|
6721
|
+
}
|
|
6722
|
+
});
|
|
6723
|
+
process2.stderr?.on("data", (chunk) => {
|
|
6724
|
+
const text = chunk.toString().trim();
|
|
6725
|
+
if (text) this.events.emit("stderr", text);
|
|
6726
|
+
});
|
|
6727
|
+
process2.on("error", (err) => {
|
|
6728
|
+
this.events.emit("error", err);
|
|
6729
|
+
});
|
|
6730
|
+
process2.on("exit", (code, signal) => {
|
|
6731
|
+
this.events.emit("exit", {
|
|
6732
|
+
code,
|
|
6733
|
+
signal,
|
|
6734
|
+
reason: this.requestedStopReason ? "requested" : "runtime_exit"
|
|
6735
|
+
});
|
|
6736
|
+
});
|
|
6737
|
+
process2.on("close", (code, signal) => {
|
|
6738
|
+
this.events.emit("close", {
|
|
6739
|
+
code,
|
|
6740
|
+
signal,
|
|
6741
|
+
reason: this.requestedStopReason ? "requested" : "runtime_exit"
|
|
6742
|
+
});
|
|
6743
|
+
});
|
|
6651
6744
|
}
|
|
6652
6745
|
};
|
|
6746
|
+
function createChildProcessRuntimeSession(driver, ctx) {
|
|
6747
|
+
return new ChildProcessRuntimeSession(driver, ctx);
|
|
6748
|
+
}
|
|
6653
6749
|
|
|
6654
6750
|
// src/drivers/index.ts
|
|
6655
6751
|
var driverFactories = {
|
|
@@ -6786,7 +6882,9 @@ import { createHash as createHash2 } from "crypto";
|
|
|
6786
6882
|
var MAX_RUNTIME_ERROR_MESSAGE_EXCERPT_CHARS = 4096;
|
|
6787
6883
|
var RUNTIME_AUTH_ACTION_REQUIRED_PATTERNS = [
|
|
6788
6884
|
/access token could not be refreshed/i,
|
|
6885
|
+
/\btoken_(?:revoked|invalidated)\b/i,
|
|
6789
6886
|
/refresh token was already used/i,
|
|
6887
|
+
/access token.*invalidated/i,
|
|
6790
6888
|
/authentication token has been invalidated/i,
|
|
6791
6889
|
/logged out or signed in to another account/i,
|
|
6792
6890
|
/not logged in/i,
|
|
@@ -6868,6 +6966,9 @@ function classifyRuntimeError(message, httpStatus) {
|
|
|
6868
6966
|
return "ProviderApiError";
|
|
6869
6967
|
}
|
|
6870
6968
|
if (isRuntimeAuthActionRequiredText(message)) return "AuthError";
|
|
6969
|
+
if (/\bmodel\b.*\bnot supported\b/i.test(message) || /\bunsupported\b.*\bmodel\b/i.test(message) || /\bmodel\b.*\bnot available\b/i.test(message)) {
|
|
6970
|
+
return "ModelConfigError";
|
|
6971
|
+
}
|
|
6871
6972
|
if (/\b(?:ETIMEDOUT|timeout|timed out)\b/i.test(message)) return "TimeoutError";
|
|
6872
6973
|
if (/\b(?:ECONNRESET|EPIPE|ECONNREFUSED|ENOTFOUND|EAI_AGAIN)\b/i.test(message) || /\bUnable to connect to API\b/i.test(message)) {
|
|
6873
6974
|
return "ProviderConnectionError";
|
|
@@ -6900,6 +7001,8 @@ function classifyRuntimeErrorReason(runtimeErrorClass) {
|
|
|
6900
7001
|
return "auth_failed";
|
|
6901
7002
|
case "NotFoundError":
|
|
6902
7003
|
return "not_found";
|
|
7004
|
+
case "ModelConfigError":
|
|
7005
|
+
return "model_config_error";
|
|
6903
7006
|
case "ProviderServerError":
|
|
6904
7007
|
return "provider_server_error";
|
|
6905
7008
|
case "ProviderApiError":
|
|
@@ -7043,6 +7146,15 @@ var RUNNER_CREDENTIAL_MINT_MAX_ATTEMPTS = 3;
|
|
|
7043
7146
|
function assertNeverApmEffect(effect) {
|
|
7044
7147
|
throw new Error(`Unhandled APM gated steering effect: ${String(effect)}`);
|
|
7045
7148
|
}
|
|
7149
|
+
function requireImmediateRuntimeSendResult(result, context) {
|
|
7150
|
+
if (typeof result.then === "function") {
|
|
7151
|
+
throw new Error(`RuntimeSession.send returned async result in synchronous APM path: ${context}`);
|
|
7152
|
+
}
|
|
7153
|
+
return result;
|
|
7154
|
+
}
|
|
7155
|
+
function runtimeSendFailureOutcome(result) {
|
|
7156
|
+
return !result.ok && result.reason === "unsupported" ? "encode_failed" : "send_failed";
|
|
7157
|
+
}
|
|
7046
7158
|
var RUNNER_CREDENTIAL_MINT_RETRY_DELAY_MS = 250;
|
|
7047
7159
|
var WORKSPACE_TEXT_FILE_MAX_BYTES = 1048576;
|
|
7048
7160
|
var WORKSPACE_IMAGE_PREVIEW_MAX_BYTES = 5 * 1024 * 1024;
|
|
@@ -7901,7 +8013,7 @@ function classifyTerminalFailure(ap) {
|
|
|
7901
8013
|
for (const text of candidates) {
|
|
7902
8014
|
const diagnostics = buildRuntimeErrorDiagnosticEnvelope(text);
|
|
7903
8015
|
const lower = text.toLowerCase();
|
|
7904
|
-
if (lower.includes("usage limit") || lower.includes("quota exceeded") || lower.includes("quota limit") || lower.includes("budget limit exceeded") || lower.includes("usage not included in your plan") || lower.includes("modelnotfounderror") || lower.includes("requested entity was not found") || lower.includes("model deprecated") || lower.includes("model not found") || diagnostics.spanAttrs.runtime_error_action_required === true || isProviderStreamFailureText(text) || isRuntimeStartTimeoutText(text)) {
|
|
8016
|
+
if (lower.includes("usage limit") || lower.includes("quota exceeded") || lower.includes("quota limit") || lower.includes("budget limit exceeded") || lower.includes("usage not included in your plan") || lower.includes("modelnotfounderror") || lower.includes("requested entity was not found") || lower.includes("model deprecated") || lower.includes("model not found") || lower.includes("model is not supported") || lower.includes("unsupported model") || /\bmodel\b.*\bnot supported\b/i.test(text) || diagnostics.spanAttrs.runtime_error_action_required === true || isProviderStreamFailureText(text) || isRuntimeStartTimeoutText(text)) {
|
|
7905
8017
|
const actionRequired = diagnostics.spanAttrs.runtime_error_action_required === true;
|
|
7906
8018
|
return {
|
|
7907
8019
|
detail: actionRequired ? formatRuntimeLoginRequiredMessage(ap.driver.id) : text,
|
|
@@ -7911,6 +8023,16 @@ function classifyTerminalFailure(ap) {
|
|
|
7911
8023
|
}
|
|
7912
8024
|
return null;
|
|
7913
8025
|
}
|
|
8026
|
+
function classifyStickyTerminalFailure(ap) {
|
|
8027
|
+
const terminalFailure = classifyTerminalFailure(ap);
|
|
8028
|
+
if (!terminalFailure) return null;
|
|
8029
|
+
if (terminalFailure.actionRequired) return terminalFailure;
|
|
8030
|
+
if (/\bmodel\b.*\bnot supported\b/i.test(terminalFailure.detail)) return terminalFailure;
|
|
8031
|
+
if (/\bunsupported\b.*\bmodel\b/i.test(terminalFailure.detail)) return terminalFailure;
|
|
8032
|
+
if (isProviderStreamFailureText(terminalFailure.detail)) return null;
|
|
8033
|
+
if (isRuntimeStartTimeoutText(terminalFailure.detail)) return null;
|
|
8034
|
+
return null;
|
|
8035
|
+
}
|
|
7914
8036
|
function isProviderStreamFailureText(text) {
|
|
7915
8037
|
return /stream closed before response\.completed|error decoding response body/i.test(text);
|
|
7916
8038
|
}
|
|
@@ -7980,7 +8102,7 @@ function buildRuntimeStallDiagnostic(ap, staleForMs, staleForMinutes) {
|
|
|
7980
8102
|
if (ap.lastActivityDetail) {
|
|
7981
8103
|
context.push(`after ${ap.lastActivityDetail}`);
|
|
7982
8104
|
}
|
|
7983
|
-
if (ap.
|
|
8105
|
+
if (ap.runtime.descriptor.busyDelivery === "gated") {
|
|
7984
8106
|
context.push(`phase=${ap.gatedSteering.phase}`);
|
|
7985
8107
|
}
|
|
7986
8108
|
if (ap.gatedSteering.outstandingToolUses > 0) {
|
|
@@ -8012,10 +8134,10 @@ function buildRuntimeStallDiagnostic(ap, staleForMs, staleForMinutes) {
|
|
|
8012
8134
|
sessionIdPresent: Boolean(ap.sessionId),
|
|
8013
8135
|
inboxCount: ap.inbox.length,
|
|
8014
8136
|
pendingNotificationCount: ap.notifications.pendingCount,
|
|
8015
|
-
processPidPresent: typeof ap.
|
|
8137
|
+
processPidPresent: typeof ap.runtime.pid === "number",
|
|
8016
8138
|
busyDeliveryMode: ap.driver.busyDeliveryMode,
|
|
8017
8139
|
supportsStdinNotification: ap.driver.supportsStdinNotification,
|
|
8018
|
-
gatedPhase: ap.
|
|
8140
|
+
gatedPhase: ap.runtime.descriptor.busyDelivery === "gated" ? ap.gatedSteering.phase : void 0,
|
|
8019
8141
|
outstandingToolUses: ap.gatedSteering.outstandingToolUses,
|
|
8020
8142
|
compacting: ap.gatedSteering.compacting,
|
|
8021
8143
|
recentStderrCount: ap.recentStderr.length,
|
|
@@ -8158,8 +8280,10 @@ var RUNTIME_TELEMETRY_RESERVED_ATTR_KEYS = /* @__PURE__ */ new Set([
|
|
|
8158
8280
|
"runtimeResultId",
|
|
8159
8281
|
"daemonVersion",
|
|
8160
8282
|
"daemon_version",
|
|
8283
|
+
"daemon_version_present",
|
|
8161
8284
|
"computerVersion",
|
|
8162
|
-
"computer_version"
|
|
8285
|
+
"computer_version",
|
|
8286
|
+
"computer_version_present"
|
|
8163
8287
|
]);
|
|
8164
8288
|
function sanitizeRuntimeTelemetryPayloadAttrs(attrs) {
|
|
8165
8289
|
const sanitized = {};
|
|
@@ -8170,17 +8294,17 @@ function sanitizeRuntimeTelemetryPayloadAttrs(attrs) {
|
|
|
8170
8294
|
return sanitized;
|
|
8171
8295
|
}
|
|
8172
8296
|
function getMessageDeliveryText(driver) {
|
|
8173
|
-
return driver.supportsStdinNotification ? "New
|
|
8297
|
+
return driver.supportsStdinNotification ? "New inbox updates may be delivered to you automatically while your process stays alive." : "The daemon will automatically restart you with an inbox update when new messages arrive.";
|
|
8174
8298
|
}
|
|
8175
8299
|
function getBusyDeliveryNote(driver) {
|
|
8176
8300
|
if (!driver.supportsStdinNotification) return "";
|
|
8177
8301
|
if (driver.busyDeliveryMode === "direct") {
|
|
8178
|
-
return "\n\nNote: While you are busy, the daemon may write batched
|
|
8302
|
+
return "\n\nNote: While you are busy, the daemon may write batched content-free inbox updates into your active turn. Finish your current step, then check the inbox or read messages at a natural breakpoint.";
|
|
8179
8303
|
}
|
|
8180
8304
|
if (driver.busyDeliveryMode === "gated") {
|
|
8181
|
-
return "\n\nNote: While you are busy, the daemon may write batched
|
|
8305
|
+
return "\n\nNote: While you are busy, the daemon may write batched content-free inbox updates into your active turn at runtime-observed safe boundaries. Finish your current step, then check the inbox or read messages at a natural breakpoint.";
|
|
8182
8306
|
}
|
|
8183
|
-
return "\n\nNote: While you are busy, you may receive [Slock inbox notice: ...] messages. Finish your current step, then
|
|
8307
|
+
return "\n\nNote: While you are busy, you may receive [Slock inbox notice: ...] messages. Finish your current step, then check the inbox or read messages at a natural breakpoint.";
|
|
8184
8308
|
}
|
|
8185
8309
|
var NATIVE_STANDING_PROMPT_STARTUP_INPUT = (
|
|
8186
8310
|
// Claude Code 2.1.114 treats "follow your system prompt" style user turns as
|
|
@@ -8216,9 +8340,11 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
8216
8340
|
stdinNotificationRetryMs;
|
|
8217
8341
|
cliTransportTraceDir = null;
|
|
8218
8342
|
deliveryTraceContexts = /* @__PURE__ */ new WeakMap();
|
|
8219
|
-
|
|
8343
|
+
runtimeExitTraceAttrs = /* @__PURE__ */ new WeakMap();
|
|
8220
8344
|
agentVisibleBoundaries = /* @__PURE__ */ new Map();
|
|
8221
8345
|
agentVisibleMessageIds = /* @__PURE__ */ new Map();
|
|
8346
|
+
daemonVersion;
|
|
8347
|
+
computerVersion;
|
|
8222
8348
|
constructor(chatBridgePath, sendToServer, daemonApiKey, opts) {
|
|
8223
8349
|
this.chatBridgePath = chatBridgePath;
|
|
8224
8350
|
this.slockCliPath = opts.slockCliPath ?? "";
|
|
@@ -8230,6 +8356,8 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
8230
8356
|
this.driverResolver = opts.driverResolver || getDriver;
|
|
8231
8357
|
this.defaultAgentEnvVarsProvider = opts.defaultAgentEnvVarsProvider || null;
|
|
8232
8358
|
this.tracer = opts.tracer ?? noopTracer;
|
|
8359
|
+
this.daemonVersion = opts.daemonVersion?.trim() || null;
|
|
8360
|
+
this.computerVersion = opts.computerVersion?.trim() || null;
|
|
8233
8361
|
this.stdinNotificationRetryMs = Math.max(
|
|
8234
8362
|
0,
|
|
8235
8363
|
Math.floor(opts.stdinNotificationRetryMs ?? STDIN_NOTIFICATION_RETRY_DELAY_MS)
|
|
@@ -8470,7 +8598,7 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
8470
8598
|
});
|
|
8471
8599
|
span.end(status);
|
|
8472
8600
|
}
|
|
8473
|
-
startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId) {
|
|
8601
|
+
startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId, wakeMessageTransient = false) {
|
|
8474
8602
|
return {
|
|
8475
8603
|
agentId,
|
|
8476
8604
|
launchId,
|
|
@@ -8479,6 +8607,7 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
8479
8607
|
session_id_present: Boolean(config.sessionId),
|
|
8480
8608
|
launch_id_present: Boolean(launchId),
|
|
8481
8609
|
wake_message_present: Boolean(wakeMessage),
|
|
8610
|
+
wake_message_transient: Boolean(wakeMessage && wakeMessageTransient),
|
|
8482
8611
|
unread_channels_count: unreadSummary ? Object.keys(unreadSummary).length : 0,
|
|
8483
8612
|
resume_prompt_present: Boolean(resumePrompt),
|
|
8484
8613
|
queue_depth: this.agentStartQueue.length,
|
|
@@ -8490,6 +8619,9 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
8490
8619
|
getDeliveryTraceContext(message) {
|
|
8491
8620
|
return this.deliveryTraceContexts.get(message) ?? {};
|
|
8492
8621
|
}
|
|
8622
|
+
isTransientDelivery(message) {
|
|
8623
|
+
return this.getDeliveryTraceContext(message).transient === true;
|
|
8624
|
+
}
|
|
8493
8625
|
deliveryTraceAttrs(agentId, message, attrs = {}) {
|
|
8494
8626
|
const context = this.getDeliveryTraceContext(message);
|
|
8495
8627
|
const deliveryCorrelationId = context.deliveryId ?? message.message_id;
|
|
@@ -8501,14 +8633,15 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
8501
8633
|
sender_type: message.sender_type,
|
|
8502
8634
|
messageId: message.message_id,
|
|
8503
8635
|
message_id_present: Boolean(message.message_id),
|
|
8636
|
+
transient_delivery: context.transient === true,
|
|
8504
8637
|
...attrs
|
|
8505
8638
|
};
|
|
8506
8639
|
}
|
|
8507
|
-
async startAgent(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId) {
|
|
8508
|
-
this.recordDaemonTrace("daemon.agent.start.requested", this.startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId));
|
|
8640
|
+
async startAgent(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId, wakeMessageTransient = false) {
|
|
8641
|
+
this.recordDaemonTrace("daemon.agent.start.requested", this.startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId, wakeMessageTransient));
|
|
8509
8642
|
if (this.agents.has(agentId)) {
|
|
8510
8643
|
this.recordDaemonTrace("daemon.agent.start.ignored", {
|
|
8511
|
-
...this.startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId),
|
|
8644
|
+
...this.startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId, wakeMessageTransient),
|
|
8512
8645
|
reason: "already_running"
|
|
8513
8646
|
});
|
|
8514
8647
|
logger.info(`[Agent ${agentId}] Start ignored (already running)`);
|
|
@@ -8516,7 +8649,7 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
8516
8649
|
}
|
|
8517
8650
|
if (this.agentsStarting.has(agentId)) {
|
|
8518
8651
|
this.recordDaemonTrace("daemon.agent.start.ignored", {
|
|
8519
|
-
...this.startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId),
|
|
8652
|
+
...this.startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId, wakeMessageTransient),
|
|
8520
8653
|
reason: "already_starting"
|
|
8521
8654
|
});
|
|
8522
8655
|
logger.info(`[Agent ${agentId}] Start ignored (startup in progress)`);
|
|
@@ -8524,7 +8657,7 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
8524
8657
|
}
|
|
8525
8658
|
if (this.queuedAgentStarts.has(agentId)) {
|
|
8526
8659
|
this.recordDaemonTrace("daemon.agent.start.ignored", {
|
|
8527
|
-
...this.startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId),
|
|
8660
|
+
...this.startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId, wakeMessageTransient),
|
|
8528
8661
|
reason: "already_queued"
|
|
8529
8662
|
});
|
|
8530
8663
|
logger.info(`[Agent ${agentId}] Start ignored (startup already queued)`);
|
|
@@ -8535,6 +8668,7 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
8535
8668
|
agentId,
|
|
8536
8669
|
config,
|
|
8537
8670
|
wakeMessage,
|
|
8671
|
+
wakeMessageTransient,
|
|
8538
8672
|
unreadSummary,
|
|
8539
8673
|
resumePrompt,
|
|
8540
8674
|
launchId,
|
|
@@ -8543,7 +8677,7 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
8543
8677
|
};
|
|
8544
8678
|
this.agentStartQueue.push(item);
|
|
8545
8679
|
this.queuedAgentStarts.set(agentId, item);
|
|
8546
|
-
this.recordDaemonTrace("daemon.agent.start.queued", this.startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId));
|
|
8680
|
+
this.recordDaemonTrace("daemon.agent.start.queued", this.startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId, wakeMessageTransient));
|
|
8547
8681
|
logger.info(
|
|
8548
8682
|
`[Agent ${agentId}] Start queued (queue=${this.agentStartQueue.length}, active=${this.activeAgentStartCount}, max=${this.maxConcurrentAgentStarts}, interval=${this.agentStartIntervalMs}ms)`
|
|
8549
8683
|
);
|
|
@@ -8560,7 +8694,7 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
8560
8694
|
const waitMs = shouldRateLimit ? Math.max(0, this.agentStartIntervalMs - elapsed) : 0;
|
|
8561
8695
|
if (waitMs > 0) {
|
|
8562
8696
|
this.recordDaemonTrace("daemon.agent.start.rate_limited", {
|
|
8563
|
-
...this.startQueueTraceAttrs(next.agentId, next.config, next.wakeMessage, next.unreadSummary, next.resumePrompt, next.launchId),
|
|
8697
|
+
...this.startQueueTraceAttrs(next.agentId, next.config, next.wakeMessage, next.unreadSummary, next.resumePrompt, next.launchId, next.wakeMessageTransient),
|
|
8564
8698
|
wait_ms: waitMs
|
|
8565
8699
|
});
|
|
8566
8700
|
this.agentStartPumpTimer = setTimeout(() => {
|
|
@@ -8573,7 +8707,7 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
8573
8707
|
if (!item) return;
|
|
8574
8708
|
if (this.queuedAgentStarts.get(item.agentId) !== item) {
|
|
8575
8709
|
this.recordDaemonTrace("daemon.agent.start.skipped", {
|
|
8576
|
-
...this.startQueueTraceAttrs(item.agentId, item.config, item.wakeMessage, item.unreadSummary, item.resumePrompt, item.launchId),
|
|
8710
|
+
...this.startQueueTraceAttrs(item.agentId, item.config, item.wakeMessage, item.unreadSummary, item.resumePrompt, item.launchId, item.wakeMessageTransient),
|
|
8577
8711
|
reason: "stale_queue_item"
|
|
8578
8712
|
});
|
|
8579
8713
|
this.pumpAgentStartQueue();
|
|
@@ -8582,7 +8716,7 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
8582
8716
|
this.queuedAgentStarts.delete(item.agentId);
|
|
8583
8717
|
if (this.agents.has(item.agentId) || this.agentsStarting.has(item.agentId)) {
|
|
8584
8718
|
this.recordDaemonTrace("daemon.agent.start.skipped", {
|
|
8585
|
-
...this.startQueueTraceAttrs(item.agentId, item.config, item.wakeMessage, item.unreadSummary, item.resumePrompt, item.launchId),
|
|
8719
|
+
...this.startQueueTraceAttrs(item.agentId, item.config, item.wakeMessage, item.unreadSummary, item.resumePrompt, item.launchId, item.wakeMessageTransient),
|
|
8586
8720
|
reason: "already_running_or_starting"
|
|
8587
8721
|
});
|
|
8588
8722
|
logger.info(`[Agent ${item.agentId}] Queued start skipped (already running or starting)`);
|
|
@@ -8596,14 +8730,15 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
8596
8730
|
logger.info(
|
|
8597
8731
|
`[Agent ${item.agentId}] Dequeued start (remaining=${this.agentStartQueue.length}, active=${this.activeAgentStartCount})`
|
|
8598
8732
|
);
|
|
8599
|
-
this.recordDaemonTrace("daemon.agent.start.dequeued", this.startQueueTraceAttrs(item.agentId, item.config, item.wakeMessage, item.unreadSummary, item.resumePrompt, item.launchId));
|
|
8733
|
+
this.recordDaemonTrace("daemon.agent.start.dequeued", this.startQueueTraceAttrs(item.agentId, item.config, item.wakeMessage, item.unreadSummary, item.resumePrompt, item.launchId, item.wakeMessageTransient));
|
|
8600
8734
|
this.startAgentNow(
|
|
8601
8735
|
item.agentId,
|
|
8602
8736
|
item.config,
|
|
8603
8737
|
item.wakeMessage,
|
|
8604
8738
|
item.unreadSummary,
|
|
8605
8739
|
item.resumePrompt,
|
|
8606
|
-
item.launchId
|
|
8740
|
+
item.launchId,
|
|
8741
|
+
item.wakeMessageTransient ?? false
|
|
8607
8742
|
).then(() => {
|
|
8608
8743
|
this.releaseAgentStartSlot(item.agentId, "spawn attempted");
|
|
8609
8744
|
item.resolve();
|
|
@@ -8638,7 +8773,7 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
8638
8773
|
this.agentStartPumpTimer = null;
|
|
8639
8774
|
}
|
|
8640
8775
|
this.recordDaemonTrace("daemon.agent.start.cancelled", {
|
|
8641
|
-
...this.startQueueTraceAttrs(agentId, item.config, item.wakeMessage, item.unreadSummary, item.resumePrompt, item.launchId),
|
|
8776
|
+
...this.startQueueTraceAttrs(agentId, item.config, item.wakeMessage, item.unreadSummary, item.resumePrompt, item.launchId, item.wakeMessageTransient),
|
|
8642
8777
|
reason
|
|
8643
8778
|
}, "cancelled");
|
|
8644
8779
|
logger.info(`[Agent ${agentId}] Queued start cancelled (${reason})`);
|
|
@@ -8649,7 +8784,7 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
8649
8784
|
for (const item of this.agentStartQueue) {
|
|
8650
8785
|
if (this.queuedAgentStarts.get(item.agentId) === item) {
|
|
8651
8786
|
this.recordDaemonTrace("daemon.agent.start.cancelled", {
|
|
8652
|
-
...this.startQueueTraceAttrs(item.agentId, item.config, item.wakeMessage, item.unreadSummary, item.resumePrompt, item.launchId),
|
|
8787
|
+
...this.startQueueTraceAttrs(item.agentId, item.config, item.wakeMessage, item.unreadSummary, item.resumePrompt, item.launchId, item.wakeMessageTransient),
|
|
8653
8788
|
reason
|
|
8654
8789
|
}, "cancelled");
|
|
8655
8790
|
logger.info(`[Agent ${item.agentId}] Queued start cancelled (${reason})`);
|
|
@@ -8664,10 +8799,10 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
8664
8799
|
this.agentStartPumpTimer = null;
|
|
8665
8800
|
}
|
|
8666
8801
|
}
|
|
8667
|
-
async startAgentNow(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId) {
|
|
8802
|
+
async startAgentNow(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId, wakeMessageTransient = false) {
|
|
8668
8803
|
if (this.agents.has(agentId)) {
|
|
8669
8804
|
this.recordDaemonTrace("daemon.agent.spawn.skipped", {
|
|
8670
|
-
...this.startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId),
|
|
8805
|
+
...this.startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId, wakeMessageTransient),
|
|
8671
8806
|
reason: "already_running"
|
|
8672
8807
|
});
|
|
8673
8808
|
logger.info(`[Agent ${agentId}] Start ignored (already running)`);
|
|
@@ -8675,14 +8810,15 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
8675
8810
|
}
|
|
8676
8811
|
if (this.agentsStarting.has(agentId)) {
|
|
8677
8812
|
this.recordDaemonTrace("daemon.agent.spawn.skipped", {
|
|
8678
|
-
...this.startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId),
|
|
8813
|
+
...this.startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId, wakeMessageTransient),
|
|
8679
8814
|
reason: "already_starting"
|
|
8680
8815
|
});
|
|
8681
8816
|
logger.info(`[Agent ${agentId}] Start ignored (startup in progress)`);
|
|
8682
8817
|
return;
|
|
8683
8818
|
}
|
|
8684
8819
|
this.agentsStarting.add(agentId);
|
|
8685
|
-
this.recordDaemonTrace("daemon.agent.spawn.started", this.startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId));
|
|
8820
|
+
this.recordDaemonTrace("daemon.agent.spawn.started", this.startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId, wakeMessageTransient));
|
|
8821
|
+
let agentProcess = null;
|
|
8686
8822
|
try {
|
|
8687
8823
|
const driver = this.driverResolver(config.runtime || "claude");
|
|
8688
8824
|
const legacyWakeRuntimeProfile = wakeMessage ? runtimeProfileNotificationFromMessage(wakeMessage) : null;
|
|
@@ -8735,6 +8871,8 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
8735
8871
|
const standingPrompt = driver.buildSystemPrompt(runtimeConfig, agentId);
|
|
8736
8872
|
let prompt;
|
|
8737
8873
|
let promptSource;
|
|
8874
|
+
let wakeMessageDeliveredAsInboxUpdate = false;
|
|
8875
|
+
const startingInboxMessages = this.startingInboxes.get(agentId) || [];
|
|
8738
8876
|
if (runtimeConfig.runtimeProfileControl && !wakeMessage) {
|
|
8739
8877
|
prompt = driver.supportsNativeStandingPrompt ? NATIVE_STANDING_PROMPT_STARTUP_INPUT : formatRuntimeProfileControlStartupInput(runtimeConfig.runtimeProfileControl, driver);
|
|
8740
8878
|
promptSource = "runtime_profile_control";
|
|
@@ -8743,14 +8881,24 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
8743
8881
|
prompt += getBusyDeliveryNote(driver);
|
|
8744
8882
|
promptSource = "resume_prompt";
|
|
8745
8883
|
} else if (wakeMessage) {
|
|
8746
|
-
const
|
|
8747
|
-
const
|
|
8748
|
-
|
|
8749
|
-
|
|
8750
|
-
|
|
8751
|
-
|
|
8752
|
-
|
|
8753
|
-
|
|
8884
|
+
const transientWakeMessage = wakeMessageTransient === true;
|
|
8885
|
+
const runtimeProfileControlPrompt = transientWakeMessage ? null : formatRuntimeProfileControlPrompt([wakeMessage]);
|
|
8886
|
+
if (transientWakeMessage) {
|
|
8887
|
+
prompt = `System notice received:
|
|
8888
|
+
|
|
8889
|
+
${formatIncomingMessage(wakeMessage, driver)}
|
|
8890
|
+
|
|
8891
|
+
Respond as appropriate. Complete all your work before stopping.
|
|
8892
|
+
${RESPONSE_TARGET_HINT}`;
|
|
8893
|
+
} else if (runtimeProfileControlPrompt) {
|
|
8894
|
+
prompt = runtimeProfileControlPrompt;
|
|
8895
|
+
} else {
|
|
8896
|
+
wakeMessageDeliveredAsInboxUpdate = true;
|
|
8897
|
+
prompt = this.formatInboxUpdateRuntimeInput([wakeMessage, ...startingInboxMessages], driver);
|
|
8898
|
+
}
|
|
8899
|
+
promptSource = transientWakeMessage ? "transient_wake_message" : runtimeProfileControlPrompt ? "runtime_profile_control_message" : "wake_inbox_update";
|
|
8900
|
+
if (!transientWakeMessage && !runtimeProfileControlPrompt && unreadSummary && Object.keys(unreadSummary).length > 0) {
|
|
8901
|
+
const otherUnread = Object.entries(unreadSummary);
|
|
8754
8902
|
if (otherUnread.length > 0) {
|
|
8755
8903
|
prompt += `
|
|
8756
8904
|
|
|
@@ -8761,18 +8909,9 @@ You also have unread messages in other channels:`;
|
|
|
8761
8909
|
}
|
|
8762
8910
|
prompt += `
|
|
8763
8911
|
|
|
8764
|
-
Use
|
|
8912
|
+
Use the inbox/read commands at a natural breakpoint if you choose to inspect those targets.`;
|
|
8765
8913
|
}
|
|
8766
8914
|
}
|
|
8767
|
-
if (!runtimeProfileControlPrompt) {
|
|
8768
|
-
prompt += `
|
|
8769
|
-
|
|
8770
|
-
Respond as appropriate \u2014 ${dynamicReplyInstruction(driver)}, or take action as needed. Complete ALL your work before stopping.
|
|
8771
|
-
${RESPONSE_TARGET_HINT}
|
|
8772
|
-
|
|
8773
|
-
IMPORTANT: If the message requires multi-step work (e.g. research, code changes, testing), complete ALL steps before stopping. Sending a progress update does NOT mean your task is done \u2014 only stop when you have NO more work to do. ${getMessageDeliveryText(driver)}`;
|
|
8774
|
-
prompt += getBusyDeliveryNote(driver);
|
|
8775
|
-
}
|
|
8776
8915
|
} else if (isResume && unreadSummary && Object.keys(unreadSummary).length > 0) {
|
|
8777
8916
|
prompt = `You have unread messages from while you were offline:`;
|
|
8778
8917
|
for (const [ch, count] of Object.entries(unreadSummary)) {
|
|
@@ -8815,7 +8954,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8815
8954
|
this.sendAgentStatus(agentId, "active", launchId || null);
|
|
8816
8955
|
this.broadcastActivity(agentId, "online", "Process idle");
|
|
8817
8956
|
this.recordDaemonTrace("daemon.agent.spawn.deferred", {
|
|
8818
|
-
...this.startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId),
|
|
8957
|
+
...this.startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId, wakeMessageTransient),
|
|
8819
8958
|
pending_messages_count: pendingMessages.length,
|
|
8820
8959
|
reason: "defer_until_concrete_message"
|
|
8821
8960
|
});
|
|
@@ -8825,7 +8964,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8825
8964
|
}
|
|
8826
8965
|
return;
|
|
8827
8966
|
}
|
|
8828
|
-
const
|
|
8967
|
+
const runtimeContext = {
|
|
8829
8968
|
agentId,
|
|
8830
8969
|
config: effectiveConfig,
|
|
8831
8970
|
standingPrompt,
|
|
@@ -8837,17 +8976,12 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8837
8976
|
launchId: launchId || null,
|
|
8838
8977
|
agentCredentialProxyInboxCoordinator: this.createAgentProxyInboxCoordinator(agentId),
|
|
8839
8978
|
cliTransportTraceDir: this.cliTransportTraceDir
|
|
8840
|
-
}
|
|
8841
|
-
|
|
8842
|
-
|
|
8843
|
-
|
|
8844
|
-
new_session: false,
|
|
8845
|
-
process_pid_present: typeof proc.pid === "number"
|
|
8846
|
-
});
|
|
8847
|
-
const agentProcess = {
|
|
8848
|
-
process: proc,
|
|
8979
|
+
};
|
|
8980
|
+
const runtime = driver.createSession?.(runtimeContext) ?? createChildProcessRuntimeSession(driver, runtimeContext);
|
|
8981
|
+
agentProcess = {
|
|
8982
|
+
runtime,
|
|
8849
8983
|
driver,
|
|
8850
|
-
inbox:
|
|
8984
|
+
inbox: wakeMessageDeliveredAsInboxUpdate && wakeMessage ? [wakeMessage, ...startingInboxMessages] : startingInboxMessages,
|
|
8851
8985
|
config: runtimeConfig,
|
|
8852
8986
|
sessionId: runtimeConfig.sessionId || null,
|
|
8853
8987
|
launchId: launchId || null,
|
|
@@ -8890,30 +9024,22 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8890
9024
|
if (runtimeConfig.runtimeProfileControl) {
|
|
8891
9025
|
this.ackInjectedRuntimeProfileControl(agentId, runtimeConfig.runtimeProfileControl, agentProcess.launchId);
|
|
8892
9026
|
}
|
|
8893
|
-
if (
|
|
9027
|
+
if (wakeMessageDeliveredAsInboxUpdate) {
|
|
9028
|
+
this.recordInboxUpdateProjection(agentId, agentProcess, agentProcess.inbox, "spawn_wake_inbox_update", "wake", prompt);
|
|
9029
|
+
} else if (wakeMessage && !wakeMessageTransient) {
|
|
8894
9030
|
this.consumeVisibleMessages(agentId, { messages: [wakeMessage], source: "spawn_wake_message" });
|
|
8895
9031
|
this.ackInjectedRuntimeProfileMessages(agentId, [wakeMessage], agentProcess.launchId);
|
|
8896
9032
|
}
|
|
8897
|
-
|
|
8898
|
-
proc.stdout?.on("data", (chunk) => {
|
|
8899
|
-
const chunkText = chunk.toString();
|
|
9033
|
+
runtime.on("stdout", (chunkText) => {
|
|
8900
9034
|
const current = this.agents.get(agentId);
|
|
8901
9035
|
if (current) {
|
|
8902
9036
|
current.recentStdout = pushRecentStdout(current.recentStdout, chunkText);
|
|
8903
9037
|
}
|
|
8904
|
-
buffer += chunkText;
|
|
8905
|
-
const lines = buffer.split("\n");
|
|
8906
|
-
buffer = lines.pop() || "";
|
|
8907
|
-
for (const line of lines) {
|
|
8908
|
-
if (!line.trim()) continue;
|
|
8909
|
-
const events = driver.parseLine(line);
|
|
8910
|
-
for (const event of events) {
|
|
8911
|
-
this.handleParsedEvent(agentId, event, driver);
|
|
8912
|
-
}
|
|
8913
|
-
}
|
|
8914
9038
|
});
|
|
8915
|
-
|
|
8916
|
-
|
|
9039
|
+
runtime.on("runtime_event", (event) => {
|
|
9040
|
+
this.handleParsedEvent(agentId, event, driver);
|
|
9041
|
+
});
|
|
9042
|
+
runtime.on("stderr", (text) => {
|
|
8917
9043
|
if (!text) return;
|
|
8918
9044
|
const current = this.agents.get(agentId);
|
|
8919
9045
|
if (driver.id === "codex" && isCodexProviderReconnectLog(text)) {
|
|
@@ -8938,7 +9064,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8938
9064
|
}
|
|
8939
9065
|
logger.error(`[Agent ${agentId} stderr]: ${text}`);
|
|
8940
9066
|
});
|
|
8941
|
-
|
|
9067
|
+
runtime.on("error", (err) => {
|
|
8942
9068
|
const current = this.agents.get(agentId);
|
|
8943
9069
|
if (current) {
|
|
8944
9070
|
current.spawnError = err.message;
|
|
@@ -8953,9 +9079,9 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8953
9079
|
}, "error");
|
|
8954
9080
|
logger.error(`[Agent ${agentId}] Process error: ${err.message}`);
|
|
8955
9081
|
});
|
|
8956
|
-
|
|
9082
|
+
runtime.on("exit", ({ code, signal }) => {
|
|
8957
9083
|
const current = this.agents.get(agentId);
|
|
8958
|
-
if (current && current.
|
|
9084
|
+
if (current && current.runtime === runtime) {
|
|
8959
9085
|
current.exitCode = code;
|
|
8960
9086
|
current.exitSignal = signal;
|
|
8961
9087
|
}
|
|
@@ -8970,14 +9096,14 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8970
9096
|
runtime_trace_active: Boolean(current?.runtimeTraceSpan),
|
|
8971
9097
|
inbox_count: current?.inbox.length ?? 0,
|
|
8972
9098
|
pending_notification_count: current?.notifications.pendingCount ?? 0,
|
|
8973
|
-
...this.
|
|
9099
|
+
...this.runtimeExitTraceAttrs.get(runtime)
|
|
8974
9100
|
});
|
|
8975
9101
|
logger.info(`[Agent ${agentId}] Process exited with code ${code}${signal ? ` (signal ${signal})` : ""}`);
|
|
8976
9102
|
});
|
|
8977
|
-
|
|
9103
|
+
runtime.on("close", ({ code, signal }) => {
|
|
8978
9104
|
if (this.agents.has(agentId)) {
|
|
8979
9105
|
const ap = this.agents.get(agentId);
|
|
8980
|
-
if (ap.
|
|
9106
|
+
if (ap.runtime !== runtime) return;
|
|
8981
9107
|
ap.notifications.clearTimer();
|
|
8982
9108
|
if (ap.pendingTrajectory?.timer) {
|
|
8983
9109
|
clearTimeout(ap.pendingTrajectory.timer);
|
|
@@ -8991,8 +9117,9 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8991
9117
|
const finalSignal = ap.exitSignal ?? signal;
|
|
8992
9118
|
const startupTimeoutTermination = ap.expectedTerminationReason === "startup_timeout";
|
|
8993
9119
|
const expectedTermination = Boolean(ap.expectedTerminationReason);
|
|
8994
|
-
const
|
|
8995
|
-
const
|
|
9120
|
+
const stickyTerminalFailureDetail = classifyStickyTerminalFailure(ap);
|
|
9121
|
+
const processEndedCleanly = !stickyTerminalFailureDetail && (finalCode === 0 || expectedTermination && !ap.lastRuntimeError);
|
|
9122
|
+
const terminalFailureDetail = processEndedCleanly ? null : stickyTerminalFailureDetail ?? classifyTerminalFailure(ap);
|
|
8996
9123
|
const resumeRecoveryReason = resumeSessionRecoveryReason(ap);
|
|
8997
9124
|
const shouldColdStartResumeSession = resumeRecoveryReason !== null;
|
|
8998
9125
|
const summary = summarizeCrash(finalCode, finalSignal);
|
|
@@ -9117,14 +9244,54 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
9117
9244
|
}
|
|
9118
9245
|
}
|
|
9119
9246
|
});
|
|
9247
|
+
const startResult = await runtime.start({ text: prompt, sessionId: effectiveConfig.sessionId || null });
|
|
9248
|
+
if (!startResult.ok) {
|
|
9249
|
+
throw new Error(`Runtime session failed to start: ${startResult.reason}${startResult.error ? ` (${startResult.error})` : ""}`);
|
|
9250
|
+
}
|
|
9251
|
+
this.recordDaemonTrace("daemon.agent.spawn.created", {
|
|
9252
|
+
...this.startQueueTraceAttrs(agentId, effectiveConfig, wakeMessage, unreadSummary, resumePrompt, launchId, wakeMessageTransient),
|
|
9253
|
+
detached: false,
|
|
9254
|
+
new_session: false,
|
|
9255
|
+
process_pid_present: typeof runtime.pid === "number"
|
|
9256
|
+
});
|
|
9120
9257
|
this.sendAgentStatus(agentId, "active", launchId || null);
|
|
9121
9258
|
this.broadcastActivity(agentId, "working", "Starting\u2026");
|
|
9122
9259
|
this.startRuntimeStartupTimeout(agentId, agentProcess);
|
|
9123
9260
|
} catch (err) {
|
|
9124
9261
|
this.agentsStarting.delete(agentId);
|
|
9262
|
+
this.cleanupFailedRuntimeStart(agentId, agentProcess, err);
|
|
9125
9263
|
throw err;
|
|
9126
9264
|
}
|
|
9127
9265
|
}
|
|
9266
|
+
cleanupFailedRuntimeStart(agentId, ap, err) {
|
|
9267
|
+
if (!ap) return;
|
|
9268
|
+
if (this.agents.get(agentId) !== ap) return;
|
|
9269
|
+
ap.notifications.clearTimer();
|
|
9270
|
+
if (ap.pendingTrajectory?.timer) {
|
|
9271
|
+
clearTimeout(ap.pendingTrajectory.timer);
|
|
9272
|
+
ap.pendingTrajectory.timer = null;
|
|
9273
|
+
}
|
|
9274
|
+
if (ap.activityHeartbeat) {
|
|
9275
|
+
clearInterval(ap.activityHeartbeat);
|
|
9276
|
+
ap.activityHeartbeat = null;
|
|
9277
|
+
}
|
|
9278
|
+
if (ap.compactionWatchdog) {
|
|
9279
|
+
clearTimeout(ap.compactionWatchdog);
|
|
9280
|
+
ap.compactionWatchdog = null;
|
|
9281
|
+
}
|
|
9282
|
+
this.clearRuntimeStartupTimeout(ap);
|
|
9283
|
+
this.clearStalledRecoverySigtermWatchdog(ap);
|
|
9284
|
+
this.endRuntimeTrace(ap, "error", {
|
|
9285
|
+
outcome: "runtime-start-failed",
|
|
9286
|
+
failure_detail: err instanceof Error ? err.message : String(err),
|
|
9287
|
+
...runtimeTraceCounterAttrs(ap),
|
|
9288
|
+
...this.finalizeRuntimeProfileTurnControl(agentId, ap, "runtime_error")
|
|
9289
|
+
});
|
|
9290
|
+
cleanupAgentCredentialProxy(agentId, ap.launchId);
|
|
9291
|
+
this.revokeManagedRunnerCredential(agentId, ap.config, ap.launchId);
|
|
9292
|
+
this.agents.delete(agentId);
|
|
9293
|
+
this.idleAgentConfigs.delete(agentId);
|
|
9294
|
+
}
|
|
9128
9295
|
async buildSpawnConfig(agentId, config) {
|
|
9129
9296
|
const baseConfig = config.serverUrl === this.serverUrl ? config : { ...config, serverUrl: this.serverUrl };
|
|
9130
9297
|
const runnerConfig = await this.ensureManagedRunnerCredential(agentId, baseConfig);
|
|
@@ -9293,7 +9460,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
9293
9460
|
ap.inbox.push(message);
|
|
9294
9461
|
if (ap.driver.supportsStdinNotification && ap.sessionId) {
|
|
9295
9462
|
ap.notifications.add();
|
|
9296
|
-
if (ap.
|
|
9463
|
+
if (ap.runtime.descriptor.busyDelivery === "gated") {
|
|
9297
9464
|
this.recordGatedSteeringEvent(agentId, ap, "buffer", {
|
|
9298
9465
|
reason: "runtime_profile",
|
|
9299
9466
|
kind,
|
|
@@ -9360,12 +9527,16 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
9360
9527
|
cleanupAgentCredentialProxy(agentId, ap.launchId);
|
|
9361
9528
|
this.revokeManagedRunnerCredential(agentId, ap.config, ap.launchId);
|
|
9362
9529
|
this.agents.delete(agentId);
|
|
9363
|
-
this.
|
|
9530
|
+
this.runtimeExitTraceAttrs.set(ap.runtime, {
|
|
9364
9531
|
stop_source: silent ? "daemon_internal" : "explicit_request",
|
|
9365
9532
|
stop_wait_requested: wait,
|
|
9366
9533
|
stop_silent: silent
|
|
9367
9534
|
});
|
|
9368
|
-
ap.
|
|
9535
|
+
await ap.runtime.stop({
|
|
9536
|
+
signal: "SIGTERM",
|
|
9537
|
+
forceAfterMs: wait ? 5e3 : void 0,
|
|
9538
|
+
reason: silent ? "daemon_internal" : "explicit_request"
|
|
9539
|
+
});
|
|
9369
9540
|
if (!silent) {
|
|
9370
9541
|
this.sendRuntimeProfileReportFor(agentId, ap.config, ap.sessionId, ap.launchId);
|
|
9371
9542
|
this.sendAgentStatus(agentId, "inactive", ap.launchId);
|
|
@@ -9374,22 +9545,18 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
9374
9545
|
}
|
|
9375
9546
|
if (wait) {
|
|
9376
9547
|
await new Promise((resolve) => {
|
|
9377
|
-
const
|
|
9548
|
+
const timeoutTimer = setTimeout(() => {
|
|
9378
9549
|
if (!silent) {
|
|
9379
9550
|
logger.warn(`[Agent ${agentId}] Stop timed out; force killing`);
|
|
9380
9551
|
}
|
|
9381
|
-
try {
|
|
9382
|
-
ap.process.kill("SIGKILL");
|
|
9383
|
-
} catch {
|
|
9384
|
-
}
|
|
9385
9552
|
resolve();
|
|
9386
9553
|
}, 5e3);
|
|
9387
|
-
ap.
|
|
9388
|
-
clearTimeout(
|
|
9554
|
+
ap.runtime.on("exit", () => {
|
|
9555
|
+
clearTimeout(timeoutTimer);
|
|
9389
9556
|
resolve();
|
|
9390
9557
|
});
|
|
9391
|
-
if (ap.
|
|
9392
|
-
clearTimeout(
|
|
9558
|
+
if (ap.runtime.closed) {
|
|
9559
|
+
clearTimeout(timeoutTimer);
|
|
9393
9560
|
resolve();
|
|
9394
9561
|
}
|
|
9395
9562
|
});
|
|
@@ -9399,9 +9566,24 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
9399
9566
|
if (traceContext.deliveryId) {
|
|
9400
9567
|
this.deliveryTraceContexts.set(message, traceContext);
|
|
9401
9568
|
}
|
|
9569
|
+
if (traceContext.transient) {
|
|
9570
|
+
this.deliveryTraceContexts.set(message, traceContext);
|
|
9571
|
+
}
|
|
9572
|
+
const transientDelivery = this.isTransientDelivery(message);
|
|
9402
9573
|
const ap = this.agents.get(agentId);
|
|
9403
9574
|
if (!ap) {
|
|
9404
9575
|
if (this.agentsStarting.has(agentId) || this.queuedAgentStarts.has(agentId)) {
|
|
9576
|
+
if (transientDelivery) {
|
|
9577
|
+
const queuedStart2 = this.queuedAgentStarts.get(agentId);
|
|
9578
|
+
this.recordDaemonTrace("daemon.agent.delivery.routed", this.deliveryTraceAttrs(agentId, message, {
|
|
9579
|
+
outcome: "transient_dropped_during_start",
|
|
9580
|
+
accepted: true,
|
|
9581
|
+
process_present: false,
|
|
9582
|
+
startup_pending: true,
|
|
9583
|
+
launchId: queuedStart2?.launchId
|
|
9584
|
+
}));
|
|
9585
|
+
return true;
|
|
9586
|
+
}
|
|
9405
9587
|
const queuedStart = this.queuedAgentStarts.get(agentId);
|
|
9406
9588
|
const pending = this.startingInboxes.get(agentId) || [];
|
|
9407
9589
|
pending.push(message);
|
|
@@ -9419,7 +9601,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
9419
9601
|
const cached = this.idleAgentConfigs.get(agentId);
|
|
9420
9602
|
if (cached) {
|
|
9421
9603
|
const driver = this.driverResolver(cached.config.runtime || "claude");
|
|
9422
|
-
if (this.shouldDeferWakeMessage(agentId, driver, message)) {
|
|
9604
|
+
if (!transientDelivery && this.shouldDeferWakeMessage(agentId, driver, message)) {
|
|
9423
9605
|
this.recordDaemonTrace("daemon.agent.delivery.routed", this.deliveryTraceAttrs(agentId, message, {
|
|
9424
9606
|
outcome: "deferred_wake_message",
|
|
9425
9607
|
accepted: true,
|
|
@@ -9442,7 +9624,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
9442
9624
|
session_id_present: Boolean(cached.sessionId),
|
|
9443
9625
|
launchId: cached.launchId || void 0
|
|
9444
9626
|
}));
|
|
9445
|
-
return this.startAgent(agentId, cached.config, message, void 0, void 0, cached.launchId || void 0).then(() => true, (err) => {
|
|
9627
|
+
return this.startAgent(agentId, cached.config, message, void 0, void 0, cached.launchId || void 0, transientDelivery).then(() => true, (err) => {
|
|
9446
9628
|
logger.error(`[Agent ${agentId}] Failed to auto-restart`, err);
|
|
9447
9629
|
if (this.reportRunnerCredentialMintFailure(agentId, err, cached.launchId, "idle_auto_restart")) {
|
|
9448
9630
|
return false;
|
|
@@ -9452,6 +9634,15 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
9452
9634
|
});
|
|
9453
9635
|
}
|
|
9454
9636
|
logger.warn(`[Agent ${agentId}] Delivery received but no running process or cached idle config exists`);
|
|
9637
|
+
if (transientDelivery) {
|
|
9638
|
+
this.recordDaemonTrace("daemon.agent.delivery.routed", this.deliveryTraceAttrs(agentId, message, {
|
|
9639
|
+
outcome: "transient_dropped_no_process",
|
|
9640
|
+
accepted: true,
|
|
9641
|
+
process_present: false,
|
|
9642
|
+
cached_idle_config_present: false
|
|
9643
|
+
}));
|
|
9644
|
+
return true;
|
|
9645
|
+
}
|
|
9455
9646
|
this.recordDaemonTrace("daemon.agent.delivery.routed", this.deliveryTraceAttrs(agentId, message, {
|
|
9456
9647
|
outcome: "rejected_no_process",
|
|
9457
9648
|
accepted: false,
|
|
@@ -9462,7 +9653,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
9462
9653
|
this.broadcastActivity(agentId, "offline", "Process unavailable; restart required");
|
|
9463
9654
|
return false;
|
|
9464
9655
|
}
|
|
9465
|
-
if (this.shouldDeferWakeMessage(agentId, ap.driver, message)) {
|
|
9656
|
+
if (!transientDelivery && this.shouldDeferWakeMessage(agentId, ap.driver, message)) {
|
|
9466
9657
|
this.recordDaemonTrace("daemon.agent.delivery.routed", this.deliveryTraceAttrs(agentId, message, {
|
|
9467
9658
|
outcome: "deferred_wake_message",
|
|
9468
9659
|
accepted: true,
|
|
@@ -9475,13 +9666,73 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
9475
9666
|
}));
|
|
9476
9667
|
return true;
|
|
9477
9668
|
}
|
|
9669
|
+
const stickyTerminalFailure = classifyStickyTerminalFailure(ap);
|
|
9670
|
+
if (stickyTerminalFailure) {
|
|
9671
|
+
if (transientDelivery) {
|
|
9672
|
+
this.recordDaemonTrace("daemon.agent.delivery.routed", this.deliveryTraceAttrs(agentId, message, {
|
|
9673
|
+
outcome: "transient_dropped_terminal_runtime_error",
|
|
9674
|
+
accepted: true,
|
|
9675
|
+
process_present: true,
|
|
9676
|
+
runtime: ap.config.runtime,
|
|
9677
|
+
session_id_present: Boolean(ap.sessionId),
|
|
9678
|
+
launchId: ap.launchId || void 0,
|
|
9679
|
+
is_idle: ap.isIdle,
|
|
9680
|
+
inbox_count: ap.inbox.length
|
|
9681
|
+
}));
|
|
9682
|
+
return true;
|
|
9683
|
+
}
|
|
9684
|
+
ap.inbox.push(message);
|
|
9685
|
+
this.recordDaemonTrace("daemon.agent.delivery.routed", this.deliveryTraceAttrs(agentId, message, {
|
|
9686
|
+
outcome: "queued_terminal_runtime_error",
|
|
9687
|
+
accepted: true,
|
|
9688
|
+
process_present: true,
|
|
9689
|
+
runtime: ap.config.runtime,
|
|
9690
|
+
session_id_present: Boolean(ap.sessionId),
|
|
9691
|
+
launchId: ap.launchId || void 0,
|
|
9692
|
+
is_idle: ap.isIdle,
|
|
9693
|
+
inbox_count: ap.inbox.length
|
|
9694
|
+
}));
|
|
9695
|
+
this.sendAgentStatus(agentId, "inactive", ap.launchId);
|
|
9696
|
+
this.broadcastActivity(agentId, "error", stickyTerminalFailure.detail);
|
|
9697
|
+
return true;
|
|
9698
|
+
}
|
|
9478
9699
|
if (ap.isIdle && ap.driver.supportsStdinNotification && ap.sessionId) {
|
|
9479
|
-
|
|
9480
|
-
|
|
9700
|
+
if (transientDelivery) {
|
|
9701
|
+
this.commitApmIdleState(agentId, ap, false);
|
|
9702
|
+
this.startRuntimeTrace(agentId, ap, "stdin-idle-delivery", [message]);
|
|
9703
|
+
this.broadcastActivity(agentId, "working", "Message received");
|
|
9704
|
+
const stdinAccepted2 = this.deliverMessagesViaStdin(
|
|
9705
|
+
agentId,
|
|
9706
|
+
ap,
|
|
9707
|
+
[message],
|
|
9708
|
+
"idle",
|
|
9709
|
+
{ transient: true }
|
|
9710
|
+
);
|
|
9711
|
+
this.recordDaemonTrace("daemon.agent.delivery.routed", this.deliveryTraceAttrs(agentId, message, {
|
|
9712
|
+
outcome: "stdin_idle_transient_delivery",
|
|
9713
|
+
accepted: true,
|
|
9714
|
+
process_present: true,
|
|
9715
|
+
runtime: ap.config.runtime,
|
|
9716
|
+
session_id_present: true,
|
|
9717
|
+
launchId: ap.launchId || void 0,
|
|
9718
|
+
stdin_delivery_accepted: stdinAccepted2,
|
|
9719
|
+
delivered_messages_count: 1,
|
|
9720
|
+
inbox_count: ap.inbox.length
|
|
9721
|
+
}));
|
|
9722
|
+
return true;
|
|
9723
|
+
}
|
|
9724
|
+
ap.inbox.push(message);
|
|
9725
|
+
const nextMessages = [...ap.inbox];
|
|
9481
9726
|
this.commitApmIdleState(agentId, ap, false);
|
|
9482
|
-
this.startRuntimeTrace(agentId, ap, "stdin-idle-delivery",
|
|
9727
|
+
this.startRuntimeTrace(agentId, ap, "stdin-idle-delivery", [message]);
|
|
9483
9728
|
this.broadcastActivity(agentId, "working", "Message received");
|
|
9484
|
-
const stdinAccepted = this.
|
|
9729
|
+
const stdinAccepted = this.deliverInboxUpdateViaStdin(
|
|
9730
|
+
agentId,
|
|
9731
|
+
ap,
|
|
9732
|
+
[message],
|
|
9733
|
+
"idle",
|
|
9734
|
+
"stdin_idle_delivery"
|
|
9735
|
+
);
|
|
9485
9736
|
this.recordDaemonTrace("daemon.agent.delivery.routed", this.deliveryTraceAttrs(agentId, message, {
|
|
9486
9737
|
outcome: "stdin_idle_delivery",
|
|
9487
9738
|
accepted: true,
|
|
@@ -9494,6 +9745,19 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
9494
9745
|
}));
|
|
9495
9746
|
return true;
|
|
9496
9747
|
}
|
|
9748
|
+
if (transientDelivery) {
|
|
9749
|
+
this.recordDaemonTrace("daemon.agent.delivery.routed", this.deliveryTraceAttrs(agentId, message, {
|
|
9750
|
+
outcome: "transient_dropped_busy",
|
|
9751
|
+
accepted: true,
|
|
9752
|
+
process_present: true,
|
|
9753
|
+
runtime: ap.config.runtime,
|
|
9754
|
+
session_id_present: Boolean(ap.sessionId),
|
|
9755
|
+
launchId: ap.launchId || void 0,
|
|
9756
|
+
is_idle: ap.isIdle,
|
|
9757
|
+
inbox_count: ap.inbox.length
|
|
9758
|
+
}));
|
|
9759
|
+
return true;
|
|
9760
|
+
}
|
|
9497
9761
|
ap.inbox.push(message);
|
|
9498
9762
|
if (this.recoverStaleProcessForQueuedMessageIfNeeded(agentId, ap)) {
|
|
9499
9763
|
this.recordDaemonTrace("daemon.agent.delivery.routed", this.deliveryTraceAttrs(agentId, message, {
|
|
@@ -9534,7 +9798,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
9534
9798
|
if (ap.gatedSteering.compacting) {
|
|
9535
9799
|
ap.notifications.add();
|
|
9536
9800
|
ap.notifications.clearTimer();
|
|
9537
|
-
if (ap.
|
|
9801
|
+
if (ap.runtime.descriptor.busyDelivery === "gated") {
|
|
9538
9802
|
this.recordGatedSteeringEvent(agentId, ap, "buffer", {
|
|
9539
9803
|
reason: "compaction_boundary",
|
|
9540
9804
|
pendingMessages: ap.inbox.length
|
|
@@ -9559,7 +9823,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
9559
9823
|
}));
|
|
9560
9824
|
return true;
|
|
9561
9825
|
}
|
|
9562
|
-
if (ap.
|
|
9826
|
+
if (ap.runtime.descriptor.busyDelivery === "gated") {
|
|
9563
9827
|
ap.notifications.add();
|
|
9564
9828
|
if (!ap.notifications.hasTimer) {
|
|
9565
9829
|
this.scheduleStdinNotification(agentId, ap, STDIN_NOTIFICATION_INITIAL_DELAY_MS);
|
|
@@ -9742,7 +10006,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
9742
10006
|
traceparent: formatTraceparent(span.context)
|
|
9743
10007
|
};
|
|
9744
10008
|
const ap = this.agents.get(agentId);
|
|
9745
|
-
if (ap && !(ap.sessionId && ap.driver.supportsStdinNotification && ap.isIdle) && !(ap.sessionId && ap.
|
|
10009
|
+
if (ap && !(ap.sessionId && ap.driver.supportsStdinNotification && ap.isIdle) && !(ap.sessionId && ap.runtime.descriptor.busyDelivery === "direct")) {
|
|
9746
10010
|
this.enqueueRuntimeProfileNotification(agentId, ap, message, kind, key);
|
|
9747
10011
|
span.end("ok", {
|
|
9748
10012
|
attrs: {
|
|
@@ -9772,7 +10036,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
9772
10036
|
});
|
|
9773
10037
|
return written;
|
|
9774
10038
|
}
|
|
9775
|
-
if (ap?.sessionId && ap.
|
|
10039
|
+
if (ap?.sessionId && ap.runtime.descriptor.busyDelivery === "direct") {
|
|
9776
10040
|
const written = this.deliverMessagesViaStdin(agentId, ap, [message], "busy");
|
|
9777
10041
|
span.end(written ? "ok" : "error", {
|
|
9778
10042
|
attrs: {
|
|
@@ -10195,23 +10459,23 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
10195
10459
|
clearTimeout(ap.stalledRecoverySigtermTimer);
|
|
10196
10460
|
ap.stalledRecoverySigtermTimer = null;
|
|
10197
10461
|
}
|
|
10198
|
-
|
|
10199
|
-
this.
|
|
10200
|
-
...this.
|
|
10462
|
+
mergeRuntimeExitTraceAttrs(runtime, attrs) {
|
|
10463
|
+
this.runtimeExitTraceAttrs.set(runtime, {
|
|
10464
|
+
...this.runtimeExitTraceAttrs.get(runtime) ?? {},
|
|
10201
10465
|
...attrs
|
|
10202
10466
|
});
|
|
10203
10467
|
}
|
|
10204
10468
|
startStalledRecoverySigtermWatchdog(agentId, ap, runtimeLabel, queuedMessagesAtSignal, staleForMs) {
|
|
10205
10469
|
this.clearStalledRecoverySigtermWatchdog(ap);
|
|
10206
10470
|
const timeoutMs = stalledRecoverySigtermTimeoutMs();
|
|
10207
|
-
const
|
|
10471
|
+
const runtimeAtSignal = ap.runtime;
|
|
10208
10472
|
ap.stalledRecoverySigtermTimer = setTimeout(() => {
|
|
10209
10473
|
ap.stalledRecoverySigtermTimer = null;
|
|
10210
10474
|
const current = this.agents.get(agentId);
|
|
10211
|
-
if (!current || current !== ap || current.
|
|
10475
|
+
if (!current || current !== ap || current.runtime !== runtimeAtSignal || current.expectedTerminationReason !== "stalled_recovery") {
|
|
10212
10476
|
return;
|
|
10213
10477
|
}
|
|
10214
|
-
this.
|
|
10478
|
+
this.mergeRuntimeExitTraceAttrs(runtimeAtSignal, {
|
|
10215
10479
|
stalled_recovery_sigterm_timeout: true,
|
|
10216
10480
|
stalled_recovery_sigterm_timeout_ms: timeoutMs
|
|
10217
10481
|
});
|
|
@@ -10225,7 +10489,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
10225
10489
|
queued_messages_at_signal: queuedMessagesAtSignal,
|
|
10226
10490
|
stale_age_ms_at_signal: staleForMs,
|
|
10227
10491
|
timeout_ms: timeoutMs,
|
|
10228
|
-
process_pid_present: typeof
|
|
10492
|
+
process_pid_present: typeof runtimeAtSignal.pid === "number",
|
|
10229
10493
|
session_id_present: Boolean(current.sessionId),
|
|
10230
10494
|
supports_stdin_notification: current.driver.supportsStdinNotification,
|
|
10231
10495
|
busy_delivery_mode: current.driver.busyDeliveryMode
|
|
@@ -10234,7 +10498,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
10234
10498
|
`[Agent ${agentId}] Stalled ${runtimeLabel} runtime did not exit after SIGTERM within ${timeoutMs}ms; force killing`
|
|
10235
10499
|
);
|
|
10236
10500
|
try {
|
|
10237
|
-
|
|
10501
|
+
void runtimeAtSignal.stop({ signal: "SIGKILL", reason: "stalled_recovery_sigterm_timeout" });
|
|
10238
10502
|
} catch (err) {
|
|
10239
10503
|
const reason = err instanceof Error ? err.message : String(err);
|
|
10240
10504
|
this.recordDaemonTrace("daemon.agent.stalled_recovery.sigkill_failed", {
|
|
@@ -10420,7 +10684,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
10420
10684
|
ap.runtimeProgress.noteRuntimeEvent(eventKind);
|
|
10421
10685
|
}
|
|
10422
10686
|
recordGatedSteeringEvent(agentId, ap, event, attrs = {}) {
|
|
10423
|
-
if (ap.
|
|
10687
|
+
if (ap.runtime.descriptor.busyDelivery !== "gated") return;
|
|
10424
10688
|
const reduction = reduceApmGatedRecentEvent(ap.gatedSteering, { event });
|
|
10425
10689
|
this.commitGatedSteeringDecisionState(agentId, ap, reduction.nextState);
|
|
10426
10690
|
this.recordRuntimeTraceEvent(agentId, ap, `runtime.gated_steering.${event}`, {
|
|
@@ -10455,7 +10719,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
10455
10719
|
}
|
|
10456
10720
|
notifyGatedSteeringBoundary(agentId, ap, reason) {
|
|
10457
10721
|
const readiness = reduceApmGatedFlushReadiness(ap.gatedSteering, {
|
|
10458
|
-
isGated: ap.
|
|
10722
|
+
isGated: ap.runtime.descriptor.busyDelivery === "gated",
|
|
10459
10723
|
hasSession: Boolean(ap.sessionId),
|
|
10460
10724
|
inboxLength: ap.inbox.length,
|
|
10461
10725
|
reason
|
|
@@ -10496,7 +10760,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
10496
10760
|
return written;
|
|
10497
10761
|
}
|
|
10498
10762
|
case "deliver_stdin": {
|
|
10499
|
-
const messages = ap.inbox
|
|
10763
|
+
const messages = [...ap.inbox];
|
|
10500
10764
|
ap.notifications.clear();
|
|
10501
10765
|
if (messages.length === 0) {
|
|
10502
10766
|
this.recordApmGatedSteeringEffectTrace(agentId, ap, effect, {
|
|
@@ -10505,7 +10769,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
10505
10769
|
});
|
|
10506
10770
|
return true;
|
|
10507
10771
|
}
|
|
10508
|
-
if (ap.
|
|
10772
|
+
if (ap.runtime.descriptor.busyDelivery === "gated") {
|
|
10509
10773
|
const flushReduction = reduceApmGatedFlush(ap.gatedSteering, { reason: effect.reason });
|
|
10510
10774
|
this.commitGatedSteeringDecisionState(agentId, ap, flushReduction.nextState);
|
|
10511
10775
|
this.recordGatedSteeringEvent(agentId, ap, "flush", {
|
|
@@ -10514,7 +10778,22 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
10514
10778
|
});
|
|
10515
10779
|
}
|
|
10516
10780
|
this.broadcastActivity(agentId, "working", "Message received");
|
|
10517
|
-
const
|
|
10781
|
+
const runtimeProfileMessages = messages.filter((message) => runtimeProfileNotificationFromMessage(message));
|
|
10782
|
+
const ordinaryMessages = messages.filter((message) => !runtimeProfileNotificationFromMessage(message));
|
|
10783
|
+
let accepted = true;
|
|
10784
|
+
if (runtimeProfileMessages.length > 0) {
|
|
10785
|
+
ap.inbox.splice(0, ap.inbox.length, ...ordinaryMessages);
|
|
10786
|
+
accepted = this.deliverMessagesViaStdin(agentId, ap, runtimeProfileMessages, effect.stdinMode);
|
|
10787
|
+
}
|
|
10788
|
+
if (ordinaryMessages.length > 0) {
|
|
10789
|
+
accepted = this.deliverInboxUpdateViaStdin(
|
|
10790
|
+
agentId,
|
|
10791
|
+
ap,
|
|
10792
|
+
ordinaryMessages,
|
|
10793
|
+
effect.stdinMode,
|
|
10794
|
+
`stdin_${effect.stdinMode}_delivery`
|
|
10795
|
+
) && accepted;
|
|
10796
|
+
}
|
|
10518
10797
|
this.recordApmGatedSteeringEffectTrace(agentId, ap, effect, {
|
|
10519
10798
|
outcome: accepted ? "written" : "not_written",
|
|
10520
10799
|
delivered_messages_count: messages.length
|
|
@@ -10592,12 +10871,12 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
10592
10871
|
this.sendAgentStatus(agentId, "inactive", ap.launchId);
|
|
10593
10872
|
this.idleAgentConfigs.delete(agentId);
|
|
10594
10873
|
try {
|
|
10595
|
-
this.
|
|
10874
|
+
this.runtimeExitTraceAttrs.set(ap.runtime, {
|
|
10596
10875
|
stop_source: "startup_timeout",
|
|
10597
10876
|
expectedTerminationReason: "startup_timeout",
|
|
10598
10877
|
timeout_ms: timeoutMs
|
|
10599
10878
|
});
|
|
10600
|
-
ap.
|
|
10879
|
+
void ap.runtime.stop({ signal: "SIGTERM", reason: "startup_timeout" });
|
|
10601
10880
|
} catch (err) {
|
|
10602
10881
|
const reason = err instanceof Error ? err.message : String(err);
|
|
10603
10882
|
logger.warn(`[Agent ${agentId}] Failed to terminate startup-timed-out ${ap.driver.id} process: ${reason}`);
|
|
@@ -10683,13 +10962,13 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
10683
10962
|
);
|
|
10684
10963
|
this.broadcastActivity(agentId, "working", `Restarting stalled ${runtimeLabel} runtime for queued message`);
|
|
10685
10964
|
try {
|
|
10686
|
-
this.
|
|
10965
|
+
this.runtimeExitTraceAttrs.set(ap.runtime, {
|
|
10687
10966
|
stop_source: "stalled_recovery",
|
|
10688
10967
|
expectedTerminationReason: "stalled_recovery",
|
|
10689
10968
|
queued_messages_count: ap.inbox.length
|
|
10690
10969
|
});
|
|
10691
10970
|
this.startStalledRecoverySigtermWatchdog(agentId, ap, runtimeLabel, ap.inbox.length, staleForMs);
|
|
10692
|
-
ap.
|
|
10971
|
+
void ap.runtime.stop({ signal: "SIGTERM", reason: "stalled_recovery" });
|
|
10693
10972
|
} catch (err) {
|
|
10694
10973
|
this.clearStalledRecoverySigtermWatchdog(ap);
|
|
10695
10974
|
const reason = err instanceof Error ? err.message : String(err);
|
|
@@ -10826,39 +11105,50 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
10826
11105
|
this.flushPendingTrajectory(agentId);
|
|
10827
11106
|
if (ap) {
|
|
10828
11107
|
if (event.sessionId) ap.sessionId = event.sessionId;
|
|
11108
|
+
const stickyTerminalFailure = classifyStickyTerminalFailure(ap);
|
|
10829
11109
|
const reduction = reduceApmGatedTurnEnd(ap.gatedSteering, {
|
|
10830
|
-
inboxLength: ap.inbox.length,
|
|
11110
|
+
inboxLength: stickyTerminalFailure ? 0 : ap.inbox.length,
|
|
10831
11111
|
supportsStdinNotification: ap.driver.supportsStdinNotification,
|
|
10832
11112
|
hasSession: Boolean(ap.sessionId),
|
|
10833
11113
|
terminateProcessOnTurnEnd: ap.driver.terminateProcessOnTurnEnd === true
|
|
10834
11114
|
});
|
|
10835
11115
|
this.commitGatedSteeringDecisionState(agentId, ap, reduction.nextState, { event: "turn_end" });
|
|
10836
11116
|
const deliverStdinEffect = reduction.effects.find((effect) => effect.kind === "deliver_stdin");
|
|
11117
|
+
if (stickyTerminalFailure) {
|
|
11118
|
+
this.sendAgentStatus(agentId, "inactive", ap.launchId);
|
|
11119
|
+
}
|
|
10837
11120
|
if (deliverStdinEffect) {
|
|
10838
11121
|
if (!this.executeApmGatedSteeringEffect(agentId, ap, deliverStdinEffect)) {
|
|
10839
11122
|
this.commitApmIdleState(agentId, ap, true);
|
|
10840
|
-
|
|
11123
|
+
if (stickyTerminalFailure) {
|
|
11124
|
+
this.broadcastActivity(agentId, "error", stickyTerminalFailure.detail);
|
|
11125
|
+
} else {
|
|
11126
|
+
this.broadcastActivity(agentId, "online", "Idle");
|
|
11127
|
+
}
|
|
10841
11128
|
}
|
|
10842
11129
|
} else {
|
|
10843
|
-
if (
|
|
11130
|
+
if (stickyTerminalFailure) {
|
|
11131
|
+
this.broadcastActivity(agentId, "error", stickyTerminalFailure.detail);
|
|
11132
|
+
} else if (ap.lastRuntimeError) {
|
|
10844
11133
|
this.broadcastActivity(agentId, "error", ap.lastRuntimeError);
|
|
10845
11134
|
} else {
|
|
10846
11135
|
this.broadcastActivity(agentId, "online", "Idle");
|
|
10847
11136
|
}
|
|
10848
11137
|
}
|
|
10849
|
-
this.endRuntimeTrace(ap, "ok", {
|
|
10850
|
-
outcome: "turn-completed",
|
|
11138
|
+
this.endRuntimeTrace(ap, stickyTerminalFailure ? "error" : "ok", {
|
|
11139
|
+
outcome: stickyTerminalFailure ? "runtime-error-with-turn-end" : "turn-completed",
|
|
11140
|
+
...stickyTerminalFailure ? { terminalRuntimeError: true } : {},
|
|
10851
11141
|
...runtimeTraceCounterAttrs(ap),
|
|
10852
|
-
...this.finalizeRuntimeProfileTurnControl(agentId, ap, "turn_end")
|
|
11142
|
+
...this.finalizeRuntimeProfileTurnControl(agentId, ap, stickyTerminalFailure ? "runtime_error" : "turn_end")
|
|
10853
11143
|
});
|
|
10854
11144
|
if (ap.driver.terminateProcessOnTurnEnd) {
|
|
10855
11145
|
logger.info(`[Agent ${agentId}] Turn completed; terminating ${ap.driver.id} process`);
|
|
10856
11146
|
try {
|
|
10857
|
-
this.
|
|
11147
|
+
this.runtimeExitTraceAttrs.set(ap.runtime, {
|
|
10858
11148
|
stop_source: "turn_end",
|
|
10859
11149
|
expectedTerminationReason: "turn_end"
|
|
10860
11150
|
});
|
|
10861
|
-
ap.
|
|
11151
|
+
void ap.runtime.stop({ signal: "SIGTERM", reason: "turn_end" });
|
|
10862
11152
|
} catch (err) {
|
|
10863
11153
|
const reason = err instanceof Error ? err.message : String(err);
|
|
10864
11154
|
logger.warn(`[Agent ${agentId}] Failed to terminate ${ap.driver.id} after turn_end: ${reason}`);
|
|
@@ -10880,7 +11170,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
10880
11170
|
if (runtimeErrorDiagnostics.spanAttrs.runtime_error_action_required === true) {
|
|
10881
11171
|
visibleErrorMessage = formatRuntimeLoginRequiredMessage(ap.driver.id);
|
|
10882
11172
|
}
|
|
10883
|
-
const shouldDisableToolBoundaryFlush = ap.
|
|
11173
|
+
const shouldDisableToolBoundaryFlush = ap.runtime.descriptor.busyDelivery === "gated" && this.isThinkingBlockMutationError(event.message);
|
|
10884
11174
|
const terminalFailure = classifyTerminalFailure(ap);
|
|
10885
11175
|
const reduction = reduceApmGatedError(ap.gatedSteering, {
|
|
10886
11176
|
disableToolBoundaryFlush: shouldDisableToolBoundaryFlush,
|
|
@@ -10907,18 +11197,23 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
10907
11197
|
...this.finalizeRuntimeProfileTurnControl(agentId, ap, "runtime_error")
|
|
10908
11198
|
});
|
|
10909
11199
|
if (ap.driver.supportsStdinNotification && terminalFailure) {
|
|
11200
|
+
const stickyTerminalFailure = classifyStickyTerminalFailure(ap);
|
|
10910
11201
|
if (terminalFailure.actionRequired) {
|
|
10911
11202
|
logger.warn(`[Agent ${agentId}] ${ap.driver.id} auth requires user action; terminating runtime process`);
|
|
10912
11203
|
try {
|
|
10913
|
-
this.
|
|
11204
|
+
this.runtimeExitTraceAttrs.set(ap.runtime, {
|
|
10914
11205
|
stop_source: "runtime_auth_error",
|
|
10915
11206
|
runtime_error_class: "AuthError"
|
|
10916
11207
|
});
|
|
10917
|
-
ap.
|
|
11208
|
+
void ap.runtime.stop({ signal: "SIGTERM", reason: "runtime_auth_error" });
|
|
10918
11209
|
} catch (err) {
|
|
10919
11210
|
const reason = err instanceof Error ? err.message : String(err);
|
|
10920
11211
|
logger.warn(`[Agent ${agentId}] Failed to terminate ${ap.driver.id} after auth error: ${reason}`);
|
|
10921
11212
|
}
|
|
11213
|
+
} else if (stickyTerminalFailure) {
|
|
11214
|
+
ap.notifications.clear();
|
|
11215
|
+
this.sendAgentStatus(agentId, "inactive", ap.launchId);
|
|
11216
|
+
logger.warn(`[Agent ${agentId}] ${ap.driver.id} terminal runtime error requires explicit recovery`);
|
|
10922
11217
|
} else {
|
|
10923
11218
|
ap.notifications.clear();
|
|
10924
11219
|
logger.info(`[Agent ${agentId}] Marked ${ap.driver.id} wakeable after terminal runtime error`);
|
|
@@ -10935,8 +11230,10 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
10935
11230
|
recordRuntimeTelemetry(agentId, ap, event) {
|
|
10936
11231
|
const sessionId = ap.driver.currentSessionId ?? event.sessionId;
|
|
10937
11232
|
const payloadAttrs = sanitizeRuntimeTelemetryPayloadAttrs(event.attrs);
|
|
11233
|
+
const versionAttrs = this.runtimeTelemetryVersionAttrs();
|
|
10938
11234
|
const telemetryAttrs = {
|
|
10939
11235
|
...payloadAttrs,
|
|
11236
|
+
...versionAttrs,
|
|
10940
11237
|
...event.source ? { source: event.source } : {},
|
|
10941
11238
|
...event.usageKind ? { usageKind: event.usageKind } : {},
|
|
10942
11239
|
...sessionId ? { sessionId } : {},
|
|
@@ -10954,6 +11251,24 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
10954
11251
|
ap.runtimeTraceSpan?.addEvent(`runtime.telemetry.${event.name}`, telemetryAttrs);
|
|
10955
11252
|
this.recordDaemonTrace(`daemon.runtime.telemetry.${event.name}`, attrs);
|
|
10956
11253
|
}
|
|
11254
|
+
runtimeTelemetryVersionAttrs() {
|
|
11255
|
+
return {
|
|
11256
|
+
...this.daemonVersion ? {
|
|
11257
|
+
daemonVersion: this.daemonVersion,
|
|
11258
|
+
daemon_version: this.daemonVersion,
|
|
11259
|
+
daemon_version_present: true
|
|
11260
|
+
} : {
|
|
11261
|
+
daemon_version_present: false
|
|
11262
|
+
},
|
|
11263
|
+
...this.computerVersion ? {
|
|
11264
|
+
computerVersion: this.computerVersion,
|
|
11265
|
+
computer_version: this.computerVersion,
|
|
11266
|
+
computer_version_present: true
|
|
11267
|
+
} : {
|
|
11268
|
+
computer_version_present: false
|
|
11269
|
+
}
|
|
11270
|
+
};
|
|
11271
|
+
}
|
|
10957
11272
|
sendAgentStatus(agentId, status, launchId) {
|
|
10958
11273
|
this.sendToServer({ type: "agent:status", agentId, status, launchId: launchId || void 0 });
|
|
10959
11274
|
}
|
|
@@ -11025,9 +11340,10 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
11025
11340
|
}
|
|
11026
11341
|
const inboxCount = ap.inbox.length;
|
|
11027
11342
|
if (inboxCount === 0) return false;
|
|
11028
|
-
const
|
|
11343
|
+
const changedMessages = ap.inbox.slice(Math.max(0, ap.inbox.length - count));
|
|
11344
|
+
const inboxRows = projectAgentInboxSnapshot(changedMessages);
|
|
11029
11345
|
const notification = `[Slock inbox notice:
|
|
11030
|
-
${formatAgentInboxDelta(inboxRows)}]`;
|
|
11346
|
+
${formatAgentInboxDelta(inboxRows, { totalPendingMessages: inboxCount })}]`;
|
|
11031
11347
|
const notificationByteCount = Buffer.byteLength(notification, "utf8");
|
|
11032
11348
|
const projectionAttrs = this.inboxProjectionTraceAttrs(inboxRows, inboxCount);
|
|
11033
11349
|
this.recordDaemonTrace("daemon.agent.inbox_projection.delta", {
|
|
@@ -11036,9 +11352,11 @@ ${formatAgentInboxDelta(inboxRows)}]`;
|
|
|
11036
11352
|
...projectionAttrs
|
|
11037
11353
|
});
|
|
11038
11354
|
logger.info(`[Agent ${agentId}] Sending stdin inbox update: ${inboxRows.length} changed target(s), ${inboxCount} pending message(s)`);
|
|
11039
|
-
const
|
|
11040
|
-
|
|
11041
|
-
|
|
11355
|
+
const sendResult = requireImmediateRuntimeSendResult(
|
|
11356
|
+
ap.runtime.send({ mode: "busy", text: notification, sessionId: ap.sessionId }),
|
|
11357
|
+
"busy inbox notification"
|
|
11358
|
+
);
|
|
11359
|
+
if (sendResult.ok) {
|
|
11042
11360
|
this.recordDaemonTrace("daemon.agent.inbox_update.pushed", {
|
|
11043
11361
|
agentId,
|
|
11044
11362
|
runtime: ap.config.runtime,
|
|
@@ -11063,14 +11381,17 @@ ${formatAgentInboxDelta(inboxRows)}]`;
|
|
|
11063
11381
|
return true;
|
|
11064
11382
|
} else {
|
|
11065
11383
|
ap.notifications.add(count);
|
|
11066
|
-
const retryScheduled = ap.
|
|
11384
|
+
const retryScheduled = ap.runtime.descriptor.busyDelivery === "direct" ? this.scheduleStdinNotification(agentId, ap, this.stdinNotificationRetryMs) : false;
|
|
11385
|
+
const outcome = runtimeSendFailureOutcome(sendResult);
|
|
11067
11386
|
this.recordDaemonTrace("daemon.agent.stdin_notification", {
|
|
11068
11387
|
agentId,
|
|
11069
11388
|
runtime: ap.config.runtime,
|
|
11070
11389
|
model: ap.config.model,
|
|
11071
11390
|
launchId: ap.launchId || void 0,
|
|
11072
|
-
outcome
|
|
11391
|
+
outcome,
|
|
11073
11392
|
mode: "busy",
|
|
11393
|
+
failure_reason: sendResult.reason,
|
|
11394
|
+
failure_error: sendResult.error,
|
|
11074
11395
|
pending_notification_count: count,
|
|
11075
11396
|
retry_scheduled: retryScheduled,
|
|
11076
11397
|
notification_timer_present: ap.notifications.hasTimer,
|
|
@@ -11081,8 +11402,110 @@ ${formatAgentInboxDelta(inboxRows)}]`;
|
|
|
11081
11402
|
return false;
|
|
11082
11403
|
}
|
|
11083
11404
|
}
|
|
11405
|
+
formatInboxUpdateRuntimeInput(messages, driver, totalPendingMessages = messages.length) {
|
|
11406
|
+
const rows = projectAgentInboxSnapshot(messages);
|
|
11407
|
+
return [
|
|
11408
|
+
"[Slock inbox notice:",
|
|
11409
|
+
formatAgentInboxDelta(rows, { totalPendingMessages }),
|
|
11410
|
+
"]"
|
|
11411
|
+
].join("\n");
|
|
11412
|
+
}
|
|
11413
|
+
recordInboxUpdateProjection(agentId, ap, messages, source, mode, renderedInput, totalPendingMessages = messages.length) {
|
|
11414
|
+
const rows = projectAgentInboxSnapshot(messages);
|
|
11415
|
+
const projectionAttrs = this.inboxProjectionTraceAttrs(rows, totalPendingMessages);
|
|
11416
|
+
this.recordDaemonTrace("daemon.agent.inbox_projection.delta", {
|
|
11417
|
+
agentId,
|
|
11418
|
+
source,
|
|
11419
|
+
...projectionAttrs
|
|
11420
|
+
});
|
|
11421
|
+
this.recordDaemonTrace("daemon.agent.inbox_update.pushed", {
|
|
11422
|
+
agentId,
|
|
11423
|
+
runtime: ap.config.runtime,
|
|
11424
|
+
model: ap.config.model,
|
|
11425
|
+
launchId: ap.launchId || void 0,
|
|
11426
|
+
mode,
|
|
11427
|
+
notification_byte_count: Buffer.byteLength(renderedInput, "utf8"),
|
|
11428
|
+
cursors_advanced: "none",
|
|
11429
|
+
...projectionAttrs
|
|
11430
|
+
});
|
|
11431
|
+
return projectionAttrs;
|
|
11432
|
+
}
|
|
11433
|
+
deliverInboxUpdateViaStdin(agentId, ap, messages, mode, source) {
|
|
11434
|
+
if (messages.length === 0) return true;
|
|
11435
|
+
const prompt = this.formatInboxUpdateRuntimeInput(messages, ap.driver, ap.inbox.length);
|
|
11436
|
+
const projectionAttrs = this.recordInboxUpdateProjection(agentId, ap, messages, source, mode, prompt, ap.inbox.length);
|
|
11437
|
+
const inputTraceAttrs = buildRuntimeInputTraceAttrs({
|
|
11438
|
+
source,
|
|
11439
|
+
prompt,
|
|
11440
|
+
messages,
|
|
11441
|
+
sessionIdPresent: Boolean(ap.sessionId),
|
|
11442
|
+
nativeStandingPrompt: Boolean(ap.driver.supportsNativeStandingPrompt)
|
|
11443
|
+
});
|
|
11444
|
+
this.recordRuntimeTraceEvent(agentId, ap, "runtime.input.prepared", inputTraceAttrs);
|
|
11445
|
+
const sendResult = requireImmediateRuntimeSendResult(
|
|
11446
|
+
ap.runtime.send({ mode, text: prompt, sessionId: ap.sessionId }),
|
|
11447
|
+
`${mode} inbox update`
|
|
11448
|
+
);
|
|
11449
|
+
if (!sendResult.ok) {
|
|
11450
|
+
if (mode === "idle") {
|
|
11451
|
+
this.commitApmIdleState(agentId, ap, true);
|
|
11452
|
+
}
|
|
11453
|
+
logger.warn(
|
|
11454
|
+
`[Agent ${agentId}] Failed to encode ${mode} inbox update; ${messages.length === 1 ? "message remains" : "messages remain"} pending`
|
|
11455
|
+
);
|
|
11456
|
+
this.recordDaemonTrace("daemon.agent.stdin_delivery", {
|
|
11457
|
+
agentId,
|
|
11458
|
+
launchId: ap.launchId || void 0,
|
|
11459
|
+
runtime: ap.config.runtime,
|
|
11460
|
+
model: ap.config.model,
|
|
11461
|
+
mode,
|
|
11462
|
+
messages_count: messages.length,
|
|
11463
|
+
session_id_present: Boolean(ap.sessionId),
|
|
11464
|
+
inbox_count: ap.inbox.length,
|
|
11465
|
+
pending_notification_count: ap.notifications.pendingCount,
|
|
11466
|
+
busy_delivery_mode: ap.driver.busyDeliveryMode,
|
|
11467
|
+
supports_stdin_notification: ap.driver.supportsStdinNotification,
|
|
11468
|
+
...this.messagesTraceAttrs(messages),
|
|
11469
|
+
...inputTraceAttrs,
|
|
11470
|
+
...projectionAttrs,
|
|
11471
|
+
outcome: runtimeSendFailureOutcome(sendResult),
|
|
11472
|
+
failure_reason: sendResult.reason,
|
|
11473
|
+
failure_error: sendResult.error,
|
|
11474
|
+
requeued_messages_count: 0,
|
|
11475
|
+
cursors_advanced: "none"
|
|
11476
|
+
}, "error");
|
|
11477
|
+
return false;
|
|
11478
|
+
}
|
|
11479
|
+
const senders = [...new Set(messages.map((message) => `@${message.sender_name}`))].join(", ");
|
|
11480
|
+
logger.info(
|
|
11481
|
+
`[Agent ${agentId}] Delivering ${mode} inbox update for ${messages.length === 1 ? "message" : `${messages.length} messages`} from ${senders}`
|
|
11482
|
+
);
|
|
11483
|
+
if (this.containsOrdinaryInboxMessage(messages)) {
|
|
11484
|
+
ap.lastRuntimeError = null;
|
|
11485
|
+
}
|
|
11486
|
+
this.recordDaemonTrace("daemon.agent.stdin_delivery", {
|
|
11487
|
+
agentId,
|
|
11488
|
+
launchId: ap.launchId || void 0,
|
|
11489
|
+
runtime: ap.config.runtime,
|
|
11490
|
+
model: ap.config.model,
|
|
11491
|
+
mode,
|
|
11492
|
+
messages_count: messages.length,
|
|
11493
|
+
session_id_present: Boolean(ap.sessionId),
|
|
11494
|
+
inbox_count: ap.inbox.length,
|
|
11495
|
+
pending_notification_count: ap.notifications.pendingCount,
|
|
11496
|
+
busy_delivery_mode: ap.driver.busyDeliveryMode,
|
|
11497
|
+
supports_stdin_notification: ap.driver.supportsStdinNotification,
|
|
11498
|
+
...this.messagesTraceAttrs(messages),
|
|
11499
|
+
...inputTraceAttrs,
|
|
11500
|
+
...projectionAttrs,
|
|
11501
|
+
outcome: "written",
|
|
11502
|
+
stdin_write_attempted: true,
|
|
11503
|
+
cursors_advanced: "none"
|
|
11504
|
+
});
|
|
11505
|
+
return true;
|
|
11506
|
+
}
|
|
11084
11507
|
/** Deliver a message to an agent via stdin, formatting it the same way as the MCP bridge */
|
|
11085
|
-
deliverMessagesViaStdin(agentId, ap, messages, mode) {
|
|
11508
|
+
deliverMessagesViaStdin(agentId, ap, messages, mode, options = {}) {
|
|
11086
11509
|
if (messages.length === 0) return true;
|
|
11087
11510
|
const runtimeProfileMigrationMessages = messages.filter((message) => runtimeProfileNotificationFromMessage(message)?.kind === "migration");
|
|
11088
11511
|
if (runtimeProfileMigrationMessages.length > 0) {
|
|
@@ -11120,8 +11543,10 @@ ${formatAgentInboxDelta(inboxRows)}]`;
|
|
|
11120
11543
|
pending_notification_count: ap.notifications.pendingCount,
|
|
11121
11544
|
busy_delivery_mode: ap.driver.busyDeliveryMode,
|
|
11122
11545
|
supports_stdin_notification: ap.driver.supportsStdinNotification,
|
|
11546
|
+
transient_delivery: options.transient === true,
|
|
11123
11547
|
...this.messagesTraceAttrs(messages)
|
|
11124
11548
|
};
|
|
11549
|
+
const traceSource = options.transient ? `stdin_${mode}_transient_delivery` : `stdin_${mode}_delivery`;
|
|
11125
11550
|
const prompt = formatRuntimeProfileControlPrompt(messages) ?? (messages.length === 1 ? `New message received:
|
|
11126
11551
|
|
|
11127
11552
|
${formatIncomingMessage(messages[0], ap.driver)}
|
|
@@ -11134,16 +11559,21 @@ ${messages.map((message) => formatIncomingMessage(message, ap.driver)).join("\n"
|
|
|
11134
11559
|
Respond as appropriate. Complete all your work before stopping.
|
|
11135
11560
|
${RESPONSE_TARGET_HINT}`);
|
|
11136
11561
|
const inputTraceAttrs = buildRuntimeInputTraceAttrs({
|
|
11137
|
-
source:
|
|
11562
|
+
source: traceSource,
|
|
11138
11563
|
prompt,
|
|
11139
11564
|
messages,
|
|
11140
11565
|
sessionIdPresent: Boolean(ap.sessionId),
|
|
11141
11566
|
nativeStandingPrompt: Boolean(ap.driver.supportsNativeStandingPrompt)
|
|
11142
11567
|
});
|
|
11143
11568
|
this.recordRuntimeTraceEvent(agentId, ap, "runtime.input.prepared", inputTraceAttrs);
|
|
11144
|
-
const
|
|
11145
|
-
|
|
11146
|
-
|
|
11569
|
+
const sendResult = requireImmediateRuntimeSendResult(
|
|
11570
|
+
ap.runtime.send({ mode, text: prompt, sessionId: ap.sessionId }),
|
|
11571
|
+
`${mode} message delivery`
|
|
11572
|
+
);
|
|
11573
|
+
if (!sendResult.ok) {
|
|
11574
|
+
if (!options.transient) {
|
|
11575
|
+
ap.inbox.unshift(...messages);
|
|
11576
|
+
}
|
|
11147
11577
|
if (mode === "idle") {
|
|
11148
11578
|
this.commitApmIdleState(agentId, ap, true);
|
|
11149
11579
|
}
|
|
@@ -11153,12 +11583,16 @@ ${RESPONSE_TARGET_HINT}`);
|
|
|
11153
11583
|
this.recordDaemonTrace("daemon.agent.stdin_delivery", {
|
|
11154
11584
|
...traceAttrs,
|
|
11155
11585
|
...inputTraceAttrs,
|
|
11156
|
-
outcome:
|
|
11157
|
-
|
|
11586
|
+
outcome: runtimeSendFailureOutcome(sendResult),
|
|
11587
|
+
failure_reason: sendResult.reason,
|
|
11588
|
+
failure_error: sendResult.error,
|
|
11589
|
+
requeued_messages_count: options.transient ? 0 : messages.length
|
|
11158
11590
|
}, "error");
|
|
11159
11591
|
return false;
|
|
11160
11592
|
}
|
|
11161
|
-
|
|
11593
|
+
if (!options.transient) {
|
|
11594
|
+
this.consumeVisibleMessages(agentId, { messages, source: traceSource });
|
|
11595
|
+
}
|
|
11162
11596
|
const senders = [...new Set(messages.map((message) => `@${message.sender_name}`))].join(", ");
|
|
11163
11597
|
logger.info(
|
|
11164
11598
|
`[Agent ${agentId}] Delivering ${mode} ${messages.length === 1 ? "message" : `${messages.length} messages`} via stdin from ${senders}`
|
|
@@ -11166,7 +11600,6 @@ ${RESPONSE_TARGET_HINT}`);
|
|
|
11166
11600
|
if (this.containsOrdinaryInboxMessage(messages)) {
|
|
11167
11601
|
ap.lastRuntimeError = null;
|
|
11168
11602
|
}
|
|
11169
|
-
ap.process.stdin?.write(encoded + "\n");
|
|
11170
11603
|
this.ackInjectedRuntimeProfileMessages(agentId, messages, ap.launchId);
|
|
11171
11604
|
this.recordDaemonTrace("daemon.agent.stdin_delivery", {
|
|
11172
11605
|
...traceAttrs,
|
|
@@ -12134,7 +12567,7 @@ var DEFAULT_TRACE_UPLOAD_URL = "https://slock-trace-upload.botiverse.dev";
|
|
|
12134
12567
|
var RUNNER_CREDENTIAL_SCOPES = ["send", "read", "mentions", "tasks", "reactions", "server", "channels", "knowledge"];
|
|
12135
12568
|
var RUNNER_CREDENTIAL_MINT_MAX_ATTEMPTS2 = 3;
|
|
12136
12569
|
var RUNNER_CREDENTIAL_MINT_RETRY_DELAY_MS2 = 250;
|
|
12137
|
-
var DAEMON_CLI_USAGE =
|
|
12570
|
+
var DAEMON_CLI_USAGE = "Usage: slock-daemon --server-url <url> --api-key <key>";
|
|
12138
12571
|
var RunnerCredentialMintError2 = class extends Error {
|
|
12139
12572
|
code;
|
|
12140
12573
|
retryable;
|
|
@@ -12170,9 +12603,9 @@ function runnerCredentialErrorDetail2(error) {
|
|
|
12170
12603
|
async function waitForRunnerCredentialRetry2() {
|
|
12171
12604
|
await new Promise((resolve) => setTimeout(resolve, RUNNER_CREDENTIAL_MINT_RETRY_DELAY_MS2));
|
|
12172
12605
|
}
|
|
12173
|
-
function parseDaemonCliArgs(args
|
|
12606
|
+
function parseDaemonCliArgs(args) {
|
|
12174
12607
|
let serverUrl = "";
|
|
12175
|
-
let apiKey =
|
|
12608
|
+
let apiKey = "";
|
|
12176
12609
|
for (let i = 0; i < args.length; i++) {
|
|
12177
12610
|
if (args[i] === "--server-url" && args[i + 1]) serverUrl = args[++i];
|
|
12178
12611
|
if (args[i] === "--api-key" && args[i + 1]) apiKey = args[++i];
|
|
@@ -12373,7 +12806,9 @@ var DaemonCore = class {
|
|
|
12373
12806
|
serverUrl: options.serverUrl,
|
|
12374
12807
|
defaultAgentEnvVarsProvider: options.defaultAgentEnvVarsProvider,
|
|
12375
12808
|
slockCliPath: this.slockCliPath,
|
|
12376
|
-
tracer: this.tracer
|
|
12809
|
+
tracer: this.tracer,
|
|
12810
|
+
daemonVersion: this.daemonVersion,
|
|
12811
|
+
computerVersion: this.computerVersion
|
|
12377
12812
|
};
|
|
12378
12813
|
this.agentManager = options.agentManagerFactory ? options.agentManagerFactory(this.chatBridgePath, (msg) => connection.send(msg), options.apiKey, agentManagerOptions) : new AgentProcessManager(this.chatBridgePath, (msg) => connection.send(msg), options.apiKey, agentManagerOptions);
|
|
12379
12814
|
const connectionFactory = options.connectionFactory ?? ((connOptions) => new DaemonConnection(connOptions));
|
|
@@ -12624,7 +13059,8 @@ var DaemonCore = class {
|
|
|
12624
13059
|
msg.wakeMessage,
|
|
12625
13060
|
msg.unreadSummary,
|
|
12626
13061
|
msg.resumePrompt,
|
|
12627
|
-
msg.launchId
|
|
13062
|
+
msg.launchId,
|
|
13063
|
+
msg.wakeMessageTransient ?? false
|
|
12628
13064
|
);
|
|
12629
13065
|
}
|
|
12630
13066
|
handleMessage(msg) {
|
|
@@ -12675,7 +13111,10 @@ var DaemonCore = class {
|
|
|
12675
13111
|
logger.info(`[Agent ${msg.agentId}] Delivery received (seq=${msg.seq}, from=@${msg.message.sender_name}, target=${formatChannelTarget(msg)})`);
|
|
12676
13112
|
try {
|
|
12677
13113
|
span.addEvent("daemon.receive", { seq: msg.seq, deliveryId: msg.deliveryId });
|
|
12678
|
-
const acceptedOrPromise = this.agentManager.deliverMessage(msg.agentId, msg.message, {
|
|
13114
|
+
const acceptedOrPromise = this.agentManager.deliverMessage(msg.agentId, msg.message, {
|
|
13115
|
+
deliveryId: msg.deliveryId,
|
|
13116
|
+
transient: msg.transient ?? false
|
|
13117
|
+
});
|
|
12679
13118
|
Promise.resolve(acceptedOrPromise).then((accepted) => {
|
|
12680
13119
|
span.addEvent("daemon.deliver_to_agent_manager", { accepted });
|
|
12681
13120
|
if (!accepted) {
|
|
@@ -12939,8 +13378,6 @@ var DaemonCore = class {
|
|
|
12939
13378
|
};
|
|
12940
13379
|
|
|
12941
13380
|
export {
|
|
12942
|
-
DAEMON_API_KEY_ENV,
|
|
12943
|
-
scrubDaemonAuthEnv,
|
|
12944
13381
|
resolveWorkspaceDirectoryPath,
|
|
12945
13382
|
scanWorkspaceDirectories,
|
|
12946
13383
|
deleteWorkspaceDirectory,
|