@slock-ai/daemon 0.46.2 → 0.47.0-staging.20260511151721
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/chat-bridge.js +4 -4
- package/dist/{chunk-FG5JGA67.js → chunk-77TJIZSE.js} +360 -41
- package/dist/{chunk-Z3PCMYZO.js → chunk-B7XIMLOT.js} +44 -1
- package/dist/cli/index.js +14162 -228
- package/dist/core.js +2 -2
- package/dist/index.js +2 -2
- package/package.json +1 -1
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DEFAULT_CHAT_BRIDGE_TOOL_TIMEOUT_MS,
|
|
3
|
+
SLOCK_HOME_ENV,
|
|
3
4
|
buildWebSocketOptions,
|
|
4
5
|
executeJsonRequest,
|
|
5
6
|
executeResponseRequest,
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
listLegacySlockStatePaths,
|
|
8
|
+
logger,
|
|
9
|
+
resolveSlockHome,
|
|
10
|
+
resolveSlockHomePath
|
|
11
|
+
} from "./chunk-B7XIMLOT.js";
|
|
8
12
|
|
|
9
13
|
// src/core.ts
|
|
10
14
|
import path15 from "path";
|
|
@@ -576,6 +580,66 @@ function summarizeToolInput(toolName, input) {
|
|
|
576
580
|
// ../shared/src/attachmentPreview.ts
|
|
577
581
|
var CSV_PREVIEW_MAX_FILE_SIZE_BYTES = 5 * 1024 * 1024;
|
|
578
582
|
|
|
583
|
+
// ../shared/src/actionCards.ts
|
|
584
|
+
import { z } from "zod";
|
|
585
|
+
var uuidSchema = z.uuid();
|
|
586
|
+
var idOrHandleSchema = z.string().min(1).max(120);
|
|
587
|
+
var draftHintSchema = z.string().trim().max(2e3).optional().describe(
|
|
588
|
+
"Why the agent prepared this for you. Shows below the form on the card; not the action itself."
|
|
589
|
+
);
|
|
590
|
+
var channelCreateOperationSchema = z.object({
|
|
591
|
+
type: z.literal("channel:create"),
|
|
592
|
+
name: z.string().trim().min(1).max(80),
|
|
593
|
+
visibility: z.enum(["public", "private"]).default("public"),
|
|
594
|
+
description: z.string().trim().max(500).optional(),
|
|
595
|
+
/**
|
|
596
|
+
* Humans to add to the channel on creation. Each entry is a handle
|
|
597
|
+
* (`@alice` or bare `alice`) or a UUID. Server resolves via
|
|
598
|
+
* `resolveUserByName` at prepare time; resolved UUIDs are stored.
|
|
599
|
+
*/
|
|
600
|
+
initialHumans: z.array(idOrHandleSchema).max(64).optional(),
|
|
601
|
+
/**
|
|
602
|
+
* Agents to add to the channel on creation. Each entry is a handle
|
|
603
|
+
* (`@scout` or bare `scout`) or a UUID. Server resolves via
|
|
604
|
+
* `resolveAgentByName` at prepare time; resolved UUIDs are stored.
|
|
605
|
+
*/
|
|
606
|
+
initialAgents: z.array(idOrHandleSchema).max(64).optional(),
|
|
607
|
+
draftHint: draftHintSchema
|
|
608
|
+
});
|
|
609
|
+
var agentCreateOperationSchema = z.object({
|
|
610
|
+
type: z.literal("agent:create"),
|
|
611
|
+
name: z.string().trim().min(1).max(60),
|
|
612
|
+
description: z.string().trim().max(500).optional(),
|
|
613
|
+
/**
|
|
614
|
+
* Agent can only suggest semantic intent (name + description). Technical
|
|
615
|
+
* configuration (which computer / runtime / model / reasoning effort) is
|
|
616
|
+
* a user prerogative — the human picks those in the create dialog when
|
|
617
|
+
* they click "Create Agent" on the card. Per stdrc 2026-05-10
|
|
618
|
+
* #proj-approval msg=ae4ecedd: "为什么 computer、runtime 和 model 还是
|
|
619
|
+
* 帮用户选了?" — don't let the agent prefill those.
|
|
620
|
+
*/
|
|
621
|
+
draftHint: draftHintSchema
|
|
622
|
+
});
|
|
623
|
+
var channelAddMemberOperationSchema = z.object({
|
|
624
|
+
type: z.literal("channel:add_member"),
|
|
625
|
+
/**
|
|
626
|
+
* Target channel. Handle (`#general` or bare `general`) or UUID.
|
|
627
|
+
* Server resolves via `resolveChannelByName` at prepare time; the
|
|
628
|
+
* resolved UUID is stored in the card metadata.
|
|
629
|
+
*/
|
|
630
|
+
channel: idOrHandleSchema,
|
|
631
|
+
/** Same resolution rule as `channelCreateOperationSchema.initialHumans`. */
|
|
632
|
+
humans: z.array(idOrHandleSchema).max(64).optional(),
|
|
633
|
+
/** Same resolution rule as `channelCreateOperationSchema.initialAgents`. */
|
|
634
|
+
agents: z.array(idOrHandleSchema).max(64).optional(),
|
|
635
|
+
draftHint: draftHintSchema
|
|
636
|
+
});
|
|
637
|
+
var actionCardActionSchema = z.discriminatedUnion("type", [
|
|
638
|
+
channelCreateOperationSchema,
|
|
639
|
+
agentCreateOperationSchema,
|
|
640
|
+
channelAddMemberOperationSchema
|
|
641
|
+
]);
|
|
642
|
+
|
|
579
643
|
// ../shared/src/testing/failpoints.ts
|
|
580
644
|
var NoopFailpointRegistry = class {
|
|
581
645
|
get enabled() {
|
|
@@ -688,6 +752,7 @@ var DISPLAY_PLAN_CONFIG = {
|
|
|
688
752
|
// src/agentProcessManager.ts
|
|
689
753
|
import { mkdirSync as mkdirSync4, readdirSync as readdirSync2, statSync as statSync2, writeFileSync as writeFileSync7 } from "fs";
|
|
690
754
|
import { mkdir, writeFile, access, readdir as readdir2, stat as stat2, readFile, rm as rm2 } from "fs/promises";
|
|
755
|
+
import { createHash as createHash2 } from "crypto";
|
|
691
756
|
import path11 from "path";
|
|
692
757
|
import os5 from "os";
|
|
693
758
|
|
|
@@ -796,6 +861,7 @@ Use the \`slock\` CLI for chat / task / attachment operations. The daemon inject
|
|
|
796
861
|
21. **\`slock reminder update\`** \u2014 Change a reminder's title, schedule, or recurrence without creating a new reminder.
|
|
797
862
|
22. **\`slock reminder cancel\`** \u2014 Cancel one of your reminders by ID.
|
|
798
863
|
23. **\`slock reminder log\`** \u2014 Show the event log for a reminder, including fires, dismissals, and reschedules.
|
|
864
|
+
24. **\`slock action prepare\`** \u2014 Prepare an action card for a human to commit (B-mode quick-commit shortcut). Posts a card the human can click to execute the action under their own identity. Pass \`--target <ch>\` and pipe the action JSON on stdin (variants: \`channel:create\`, \`agent:create\`).
|
|
799
865
|
|
|
800
866
|
The CLI prints human-readable canonical text on success (matching the format you see in received messages and history). On failure it prints JSON to stderr:
|
|
801
867
|
- failure \u2192 stderr \`{"ok":false,"code":"...","message":"..."}\` with non-zero exit
|
|
@@ -1272,6 +1338,7 @@ exec ${shellSingleQuote(process.execPath)} ${shellSingleQuote(ctx.slockCliPath)}
|
|
|
1272
1338
|
...ctx.config.envVars || {},
|
|
1273
1339
|
...extraEnv,
|
|
1274
1340
|
...runtimeContextEnv(ctx.config),
|
|
1341
|
+
[SLOCK_HOME_ENV]: resolveSlockHome(),
|
|
1275
1342
|
SLOCK_AGENT_ID: ctx.agentId,
|
|
1276
1343
|
...ctx.launchId ? { SLOCK_AGENT_LAUNCH_ID: ctx.launchId } : {},
|
|
1277
1344
|
SLOCK_SERVER_URL: ctx.config.serverUrl,
|
|
@@ -2228,6 +2295,15 @@ function detectCodexModels(home = os2.homedir()) {
|
|
|
2228
2295
|
import { spawn as spawn3 } from "child_process";
|
|
2229
2296
|
import path5 from "path";
|
|
2230
2297
|
import { writeFileSync as writeFileSync3 } from "fs";
|
|
2298
|
+
function buildCopilotSpawnEnv(ctx) {
|
|
2299
|
+
return {
|
|
2300
|
+
...process.env,
|
|
2301
|
+
FORCE_COLOR: "0",
|
|
2302
|
+
NO_COLOR: "1",
|
|
2303
|
+
...ctx.config.envVars || {},
|
|
2304
|
+
[SLOCK_HOME_ENV]: resolveSlockHome()
|
|
2305
|
+
};
|
|
2306
|
+
}
|
|
2231
2307
|
var CopilotDriver = class {
|
|
2232
2308
|
id = "copilot";
|
|
2233
2309
|
lifecycle = {
|
|
@@ -2286,7 +2362,7 @@ var CopilotDriver = class {
|
|
|
2286
2362
|
if (ctx.config.sessionId) {
|
|
2287
2363
|
args.push(`--resume=${ctx.config.sessionId}`);
|
|
2288
2364
|
}
|
|
2289
|
-
const spawnEnv =
|
|
2365
|
+
const spawnEnv = buildCopilotSpawnEnv(ctx);
|
|
2290
2366
|
const proc = spawn3("copilot", args, {
|
|
2291
2367
|
cwd: ctx.workingDirectory,
|
|
2292
2368
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -2383,6 +2459,15 @@ var CopilotDriver = class {
|
|
|
2383
2459
|
import { spawn as spawn4, spawnSync } from "child_process";
|
|
2384
2460
|
import { writeFileSync as writeFileSync4, mkdirSync as mkdirSync2, existsSync as existsSync4 } from "fs";
|
|
2385
2461
|
import path6 from "path";
|
|
2462
|
+
function buildCursorSpawnEnv(ctx) {
|
|
2463
|
+
return {
|
|
2464
|
+
...process.env,
|
|
2465
|
+
FORCE_COLOR: "0",
|
|
2466
|
+
NO_COLOR: "1",
|
|
2467
|
+
...ctx.config.envVars || {},
|
|
2468
|
+
[SLOCK_HOME_ENV]: resolveSlockHome()
|
|
2469
|
+
};
|
|
2470
|
+
}
|
|
2386
2471
|
var CursorDriver = class {
|
|
2387
2472
|
id = "cursor";
|
|
2388
2473
|
lifecycle = {
|
|
@@ -2437,7 +2522,7 @@ var CursorDriver = class {
|
|
|
2437
2522
|
args.push("--resume", ctx.config.sessionId);
|
|
2438
2523
|
}
|
|
2439
2524
|
args.push(ctx.prompt);
|
|
2440
|
-
const spawnEnv =
|
|
2525
|
+
const spawnEnv = buildCursorSpawnEnv(ctx);
|
|
2441
2526
|
const proc = spawn4("cursor-agent", args, {
|
|
2442
2527
|
cwd: ctx.workingDirectory,
|
|
2443
2528
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -3527,8 +3612,86 @@ async function deleteWorkspaceDirectory(dataDir, directoryName) {
|
|
|
3527
3612
|
}
|
|
3528
3613
|
}
|
|
3529
3614
|
|
|
3615
|
+
// src/runtimeErrorDiagnostics.ts
|
|
3616
|
+
import { createHash } from "crypto";
|
|
3617
|
+
var MAX_RUNTIME_ERROR_MESSAGE_EXCERPT_CHARS = 4096;
|
|
3618
|
+
function buildRuntimeErrorDiagnosticEnvelope(message) {
|
|
3619
|
+
const rawMessage = String(message || "");
|
|
3620
|
+
const scrubbed = scrubRuntimeErrorDiagnosticText(rawMessage);
|
|
3621
|
+
const { value: excerpt, truncated } = truncateDiagnosticText(scrubbed, MAX_RUNTIME_ERROR_MESSAGE_EXCERPT_CHARS);
|
|
3622
|
+
const httpStatus = extractHttpStatus(rawMessage);
|
|
3623
|
+
const runtimeErrorClass = classifyRuntimeError(rawMessage, httpStatus);
|
|
3624
|
+
const fingerprint = fingerprintRuntimeError(scrubbed);
|
|
3625
|
+
const spanAttrs = {
|
|
3626
|
+
runtime_error_class: runtimeErrorClass,
|
|
3627
|
+
runtime_error_fingerprint: fingerprint,
|
|
3628
|
+
runtime_error_message_present: rawMessage.length > 0,
|
|
3629
|
+
runtime_error_message_length_bucket: bucketLength(rawMessage.length),
|
|
3630
|
+
runtime_error_message_truncated: truncated
|
|
3631
|
+
};
|
|
3632
|
+
if (httpStatus !== null) {
|
|
3633
|
+
spanAttrs.runtime_error_http_status = httpStatus;
|
|
3634
|
+
}
|
|
3635
|
+
return {
|
|
3636
|
+
spanAttrs,
|
|
3637
|
+
eventAttrs: {
|
|
3638
|
+
...spanAttrs,
|
|
3639
|
+
runtime_error_message_excerpt: excerpt
|
|
3640
|
+
}
|
|
3641
|
+
};
|
|
3642
|
+
}
|
|
3643
|
+
function scrubRuntimeErrorDiagnosticText(value) {
|
|
3644
|
+
return value.replace(/\bBearer\s+[A-Za-z0-9._~+/=-]{8,}/gi, "Bearer [REDACTED_TOKEN]").replace(/\b(?:sk|sk-ant|sk-proj|xox[baprs]?)-[A-Za-z0-9_-]{8,}\b/g, "[REDACTED_TOKEN]").replace(/\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b/g, "[REDACTED_EMAIL]").replace(/https?:\/\/[^\s"'<>]+/gi, (url) => redactUrlQuery(url)).replace(/\/Users\/[^\s"'<>:]+(?:\/[^\s"'<>:]+)*/g, "[REDACTED_PATH]").replace(/\/home\/[^\s"'<>:]+(?:\/[^\s"'<>:]+)*/g, "[REDACTED_PATH]").replace(/[A-Za-z]:\\Users\\[^\s"'<>:]+(?:\\[^\s"'<>:]+)*/g, "[REDACTED_PATH]");
|
|
3645
|
+
}
|
|
3646
|
+
function truncateDiagnosticText(value, maxChars) {
|
|
3647
|
+
if (value.length <= maxChars) return { value, truncated: false };
|
|
3648
|
+
return { value: value.slice(0, maxChars), truncated: true };
|
|
3649
|
+
}
|
|
3650
|
+
function extractHttpStatus(message) {
|
|
3651
|
+
const match = /\b(?:HTTP|status(?:\s+code)?|API\s+Error)[:\s]+([45]\d{2})\b/i.exec(message) ?? /\b([45]\d{2})\s+(?:Bad Request|Unauthorized|Forbidden|Not Found|Conflict|Too Many Requests|Internal Server Error|Service Unavailable)\b/i.exec(message);
|
|
3652
|
+
if (!match) return null;
|
|
3653
|
+
const status = Number(match[1]);
|
|
3654
|
+
return Number.isInteger(status) ? status : null;
|
|
3655
|
+
}
|
|
3656
|
+
function classifyRuntimeError(message, httpStatus) {
|
|
3657
|
+
const explicit = /\b([A-Z][A-Za-z0-9_]*(?:Error|Exception))\b/.exec(message);
|
|
3658
|
+
if (explicit) return explicit[1];
|
|
3659
|
+
if (httpStatus !== null) {
|
|
3660
|
+
if (httpStatus === 429) return "RateLimitError";
|
|
3661
|
+
if (httpStatus === 401 || httpStatus === 403) return "AuthError";
|
|
3662
|
+
if (httpStatus === 404) return "NotFoundError";
|
|
3663
|
+
if (httpStatus >= 500) return "ProviderServerError";
|
|
3664
|
+
return "ProviderApiError";
|
|
3665
|
+
}
|
|
3666
|
+
if (/\btimeout|timed out\b/i.test(message)) return "TimeoutError";
|
|
3667
|
+
if (/\brate.?limit|too many requests\b/i.test(message)) return "RateLimitError";
|
|
3668
|
+
if (/\bnot found\b/i.test(message)) return "NotFoundError";
|
|
3669
|
+
return "RuntimeError";
|
|
3670
|
+
}
|
|
3671
|
+
function fingerprintRuntimeError(value) {
|
|
3672
|
+
const normalized = value.toLowerCase().replace(/[0-9a-f]{12,}/g, "<hex>").replace(/\b\d+\b/g, "<num>").replace(/\s+/g, " ").trim();
|
|
3673
|
+
return createHash("sha256").update(normalized).digest("hex").slice(0, 16);
|
|
3674
|
+
}
|
|
3675
|
+
function bucketLength(length) {
|
|
3676
|
+
if (length === 0) return "0";
|
|
3677
|
+
if (length < 1024) return "<1k";
|
|
3678
|
+
if (length < 4096) return "1k-4k";
|
|
3679
|
+
if (length < 16384) return "4k-16k";
|
|
3680
|
+
return "16k+";
|
|
3681
|
+
}
|
|
3682
|
+
function redactUrlQuery(value) {
|
|
3683
|
+
try {
|
|
3684
|
+
const url = new URL(value);
|
|
3685
|
+
if (url.search) url.search = "?[REDACTED_QUERY]";
|
|
3686
|
+
if (url.username) url.username = "[REDACTED_USER]";
|
|
3687
|
+
if (url.password) url.password = "[REDACTED_PASSWORD]";
|
|
3688
|
+
return url.toString();
|
|
3689
|
+
} catch {
|
|
3690
|
+
return value.replace(/\?.*$/, "?[REDACTED_QUERY]");
|
|
3691
|
+
}
|
|
3692
|
+
}
|
|
3693
|
+
|
|
3530
3694
|
// src/agentProcessManager.ts
|
|
3531
|
-
var DATA_DIR = path11.join(os5.homedir(), ".slock", "agents");
|
|
3532
3695
|
var DEFAULT_MAX_CONCURRENT_AGENT_STARTS = 5;
|
|
3533
3696
|
var DEFAULT_AGENT_START_INTERVAL_MS = 500;
|
|
3534
3697
|
var WORKSPACE_TEXT_FILE_MAX_BYTES = 1048576;
|
|
@@ -3857,15 +4020,27 @@ After confirming language preference, do not give a generic product introduction
|
|
|
3857
4020
|
- Use a low-pressure prompt and guide to one concrete starter action.
|
|
3858
4021
|
|
|
3859
4022
|
### Starter Plan Output
|
|
3860
|
-
A starter plan should make the next action executable, not just descriptive
|
|
3861
|
-
|
|
3862
|
-
|
|
3863
|
-
-
|
|
3864
|
-
-
|
|
4023
|
+
A starter plan should make the next action executable, not just descriptive.
|
|
4024
|
+
For new channels, new agents, and adding members to an existing channel, post an **action card** rather than a copyable spec:
|
|
4025
|
+
|
|
4026
|
+
- Use \`slock action prepare --target <onboarding-channel>\` and pipe an \`ActionCardAction\` JSON. Identity references are handles (\`@alice\` / \`@scout\` / \`#general\` \u2014 bare names work too), never UUIDs. Server resolves at prepare time.
|
|
4027
|
+
- \`{type: "channel:create", name, visibility: "public" | "private", description?, initialHumans?: ["@alice"], initialAgents?: ["@scout"], draftHint?}\`
|
|
4028
|
+
- \`{type: "agent:create", name, description?, draftHint?}\`
|
|
4029
|
+
- \`{type: "channel:add_member", channel: "#existing-channel", humans?: ["@alice"], agents?: ["@scout"], draftHint?}\` \u2014 at least one of humans / agents must be non-empty
|
|
4030
|
+
- The owner clicks the button on the card; the matching dialog opens **prefilled with your values** (editable, deselectable for add_member). They review, adjust, and submit; the action is committed under their identity.
|
|
4031
|
+
- Technical fields the owner must pick themselves are NOT yours to prefill on \`agent:create\`: computer, runtime, model, reasoning effort. Stay on semantic intent (name + description).
|
|
4032
|
+
- For \`channel:add_member\`, only suggest people who are actually likely candidates (already in the server, relevant to the channel's topic). The owner will deselect anyone they don't want \u2014 make their default-yes list useful, not exhaustive.
|
|
4033
|
+
- Do not just describe or list copyable specs once action cards are available \u2014 the human input cost should land at "click the card, review, submit", not "copy this name into the dialog yourself".
|
|
4034
|
+
- Do not imply the resource has been created or members added until the card flips to "Done".
|
|
4035
|
+
|
|
4036
|
+
Other plan elements still apply:
|
|
4037
|
+
- suggested channel or workstream pairing
|
|
4038
|
+
- first task to send after the card is committed
|
|
4039
|
+
- who to pull in once the channel exists (often a follow-up \`channel:add_member\` card)
|
|
3865
4040
|
|
|
3866
4041
|
Do not use a rigid keyword routing table. Use examples as inspiration, then adapt to the user's context.
|
|
3867
|
-
If details are missing but not blocking, state reasonable defaults and invite correction.
|
|
3868
|
-
Only ask one blocking question first if the answer is required before any useful
|
|
4042
|
+
If details are missing but not blocking, state reasonable defaults inside the action card payload (the owner can edit) and invite correction in chat.
|
|
4043
|
+
Only ask one blocking question first if the answer is required before any useful card can be prepared.
|
|
3869
4044
|
Do not imply you have already created agents or channels unless the action has actually happened.
|
|
3870
4045
|
|
|
3871
4046
|
### Capability Boundary Pivot
|
|
@@ -4103,16 +4278,22 @@ Do not copy these answers verbatim.
|
|
|
4103
4278
|
|
|
4104
4279
|
## FAQ 15: How do I create agents or channels?
|
|
4105
4280
|
### Answer idea
|
|
4106
|
-
-
|
|
4107
|
-
-
|
|
4108
|
-
-
|
|
4281
|
+
- When the owner agrees to a new agent or channel, **post an action card** with \`slock action prepare\`. The card lives inline in chat; the owner clicks the action button, the matching create dialog opens prefilled with your values (editable), and the resource is created under their identity when they submit.
|
|
4282
|
+
- v1 supports three action types via \`slock action prepare --target '<channel>' <<EOF { ... } EOF\`:
|
|
4283
|
+
- \`{type: "channel:create", name, visibility: "public" | "private", description?, initialHumans?: ["@alice"], initialAgents?: ["@scout"], draftHint?}\`
|
|
4284
|
+
- \`{type: "agent:create", name, description?, draftHint?}\` \u2014 runtime / model / computer are the owner's call, not yours
|
|
4285
|
+
- \`{type: "channel:add_member", channel: "#existing-channel", humans?: ["@alice"], agents?: ["@scout"], draftHint?}\` \u2014 at least one of humans / agents must be non-empty. The owner clicks "Add Members" on the card; an AddMembers dialog opens with your suggested list (each row toggleable) and the owner submits to actually add them.
|
|
4286
|
+
|
|
4287
|
+
- **Identity references are handles, not UUIDs.** Use \`@alice\` / \`@scout\` / \`#general\` (or bare \`alice\` / \`scout\` / \`general\`). The server resolves to UUIDs at prepare time. If a handle doesn't match a real human / agent / channel in this server you get a 422 INVALID_HANDLE error pointing at the field \u2014 fix the handle and retry. You should never see or write UUIDs in action card payloads.
|
|
4288
|
+
- Manual fallback (only when the owner explicitly wants to do it themselves or asks how to repeat it): the + buttons in the Agents and Channels sidebar sections. Lead with the action card; mention the + button only on request.
|
|
4109
4289
|
|
|
4110
4290
|
### Next step
|
|
4111
|
-
-
|
|
4291
|
+
- Prefill the values you have, post the action card with a short \`draftHint\` explaining why these values, and tell the owner: "click the button on the card to review and commit." Then propose the first task to send once the card flips to Done.
|
|
4112
4292
|
|
|
4113
4293
|
### Guardrail
|
|
4114
|
-
- Do not imply you already created agents or channels unless
|
|
4115
|
-
-
|
|
4294
|
+
- Do not imply you already created agents or channels unless the card state is \`executed\`.
|
|
4295
|
+
- Do not prefill technical fields on \`agent:create\` (runtime / model / computer / reasoning effort). The owner picks those in the dialog.
|
|
4296
|
+
- If the action type the user wants is not yet supported (e.g. \`channel:add_member\`), say so plainly and offer the manual UI path; do not invent action types the schema does not accept.
|
|
4116
4297
|
`;
|
|
4117
4298
|
}
|
|
4118
4299
|
function buildOnboardingSeedFiles() {
|
|
@@ -4212,6 +4393,20 @@ function runtimeProfileNotificationFromMessage(message) {
|
|
|
4212
4393
|
function runtimeProfileNotificationTitle(kind) {
|
|
4213
4394
|
return kind === "migration" ? "Runtime Profile migration" : "Runtime Profile notice";
|
|
4214
4395
|
}
|
|
4396
|
+
function hashRuntimeProfileKey(key) {
|
|
4397
|
+
if (!key) return void 0;
|
|
4398
|
+
return createHash2("sha256").update(key).digest("hex").slice(0, 16);
|
|
4399
|
+
}
|
|
4400
|
+
function runtimeProfileTurnControl(kind, key, source) {
|
|
4401
|
+
return {
|
|
4402
|
+
kind,
|
|
4403
|
+
source,
|
|
4404
|
+
keyHash: hashRuntimeProfileKey(key) ?? null,
|
|
4405
|
+
keyPresent: Boolean(key),
|
|
4406
|
+
injectedAtMs: Date.now(),
|
|
4407
|
+
migrationDoneToolObserved: false
|
|
4408
|
+
};
|
|
4409
|
+
}
|
|
4215
4410
|
function pushRecentLines(lines, chunk, maxLines, maxLineLength) {
|
|
4216
4411
|
const next = [...lines];
|
|
4217
4412
|
for (const rawLine of chunk.split(/\r?\n/)) {
|
|
@@ -4400,13 +4595,14 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
4400
4595
|
defaultAgentEnvVarsProvider;
|
|
4401
4596
|
tracer;
|
|
4402
4597
|
deliveryTraceContexts = /* @__PURE__ */ new WeakMap();
|
|
4598
|
+
processExitTraceAttrs = /* @__PURE__ */ new WeakMap();
|
|
4403
4599
|
constructor(chatBridgePath, sendToServer, daemonApiKey, opts) {
|
|
4404
4600
|
this.chatBridgePath = chatBridgePath;
|
|
4405
4601
|
this.slockCliPath = opts.slockCliPath ?? "";
|
|
4406
4602
|
this.sendToServer = sendToServer;
|
|
4407
4603
|
this.daemonApiKey = daemonApiKey;
|
|
4408
4604
|
this.serverUrl = opts.serverUrl;
|
|
4409
|
-
this.dataDir = opts.dataDir ||
|
|
4605
|
+
this.dataDir = opts.dataDir || resolveSlockHomePath("agents");
|
|
4410
4606
|
this.runtimeSessionHomeDir = opts.runtimeSessionHomeDir || os5.homedir();
|
|
4411
4607
|
this.driverResolver = opts.driverResolver || getDriver;
|
|
4412
4608
|
this.defaultAgentEnvVarsProvider = opts.defaultAgentEnvVarsProvider || null;
|
|
@@ -4798,6 +4994,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
4798
4994
|
exitCode: null,
|
|
4799
4995
|
exitSignal: null,
|
|
4800
4996
|
expectedTerminationReason: null,
|
|
4997
|
+
runtimeProfileTurnControl: runtimeConfig.runtimeProfileControl ? runtimeProfileTurnControl(runtimeConfig.runtimeProfileControl.kind, runtimeConfig.runtimeProfileControl.key, "agent_config") : null,
|
|
4801
4998
|
pendingTrajectory: null,
|
|
4802
4999
|
gatedSteering: createGatedSteeringState()
|
|
4803
5000
|
};
|
|
@@ -4872,7 +5069,8 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
4872
5069
|
clean_exit: code === 0,
|
|
4873
5070
|
runtime_trace_active: Boolean(current?.runtimeTraceSpan),
|
|
4874
5071
|
inbox_count: current?.inbox.length ?? 0,
|
|
4875
|
-
pending_notification_count: current?.pendingNotificationCount ?? 0
|
|
5072
|
+
pending_notification_count: current?.pendingNotificationCount ?? 0,
|
|
5073
|
+
...this.processExitTraceAttrs.get(proc)
|
|
4876
5074
|
});
|
|
4877
5075
|
logger.info(`[Agent ${agentId}] Process exited with code ${code}${signal ? ` (signal ${signal})` : ""}`);
|
|
4878
5076
|
});
|
|
@@ -4900,7 +5098,8 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
4900
5098
|
outcome: processEndedCleanly ? "process-exit" : "process-crash",
|
|
4901
5099
|
expectedTerminationReason: ap.expectedTerminationReason || void 0,
|
|
4902
5100
|
exitCode: finalCode,
|
|
4903
|
-
exitSignal: finalSignal
|
|
5101
|
+
exitSignal: finalSignal,
|
|
5102
|
+
...this.finalizeRuntimeProfileTurnControl(agentId, ap, "process_exit")
|
|
4904
5103
|
});
|
|
4905
5104
|
if (processEndedCleanly) {
|
|
4906
5105
|
this.finishCompactionIfActive(agentId, "Context compaction finished (inferred from process exit)");
|
|
@@ -5063,6 +5262,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
5063
5262
|
agentId,
|
|
5064
5263
|
kind,
|
|
5065
5264
|
key_present: Boolean(key),
|
|
5265
|
+
key_hash: hashRuntimeProfileKey(key),
|
|
5066
5266
|
outcome: ap.sessionId ? "queued_busy" : "queued_before_session",
|
|
5067
5267
|
runtime: ap.config.runtime,
|
|
5068
5268
|
session_id_present: Boolean(ap.sessionId),
|
|
@@ -5085,6 +5285,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
5085
5285
|
agentId,
|
|
5086
5286
|
kind,
|
|
5087
5287
|
key_present: Boolean(key),
|
|
5288
|
+
key_hash: hashRuntimeProfileKey(key),
|
|
5088
5289
|
outcome: "queued_during_start",
|
|
5089
5290
|
startup_pending: true,
|
|
5090
5291
|
starting_inbox_count: pending.length,
|
|
@@ -5121,6 +5322,11 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
5121
5322
|
}
|
|
5122
5323
|
this.clearCompactionWatchdog(ap);
|
|
5123
5324
|
this.agents.delete(agentId);
|
|
5325
|
+
this.processExitTraceAttrs.set(ap.process, {
|
|
5326
|
+
stop_source: silent ? "daemon_internal" : "explicit_request",
|
|
5327
|
+
stop_wait_requested: wait,
|
|
5328
|
+
stop_silent: silent
|
|
5329
|
+
});
|
|
5124
5330
|
ap.process.kill("SIGTERM");
|
|
5125
5331
|
if (!silent) {
|
|
5126
5332
|
this.sendRuntimeProfileReportFor(agentId, ap.config, ap.sessionId, ap.launchId);
|
|
@@ -5411,7 +5617,8 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
5411
5617
|
attrs: {
|
|
5412
5618
|
agentId,
|
|
5413
5619
|
control_kind: kind,
|
|
5414
|
-
key_present: Boolean(key)
|
|
5620
|
+
key_present: Boolean(key),
|
|
5621
|
+
key_hash: hashRuntimeProfileKey(key)
|
|
5415
5622
|
}
|
|
5416
5623
|
});
|
|
5417
5624
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -5444,7 +5651,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
5444
5651
|
}
|
|
5445
5652
|
if (ap?.sessionId && ap.driver.supportsStdinNotification && ap.isIdle) {
|
|
5446
5653
|
ap.isIdle = false;
|
|
5447
|
-
this.startRuntimeTrace(agentId, ap, "runtime-profile");
|
|
5654
|
+
this.startRuntimeTrace(agentId, ap, "runtime-profile", [message]);
|
|
5448
5655
|
const written = this.deliverMessagesViaStdin(agentId, ap, [message], "idle");
|
|
5449
5656
|
span.end(written ? "ok" : "error", {
|
|
5450
5657
|
attrs: {
|
|
@@ -5539,6 +5746,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
5539
5746
|
agentId,
|
|
5540
5747
|
control_kind: control.kind,
|
|
5541
5748
|
key_present: Boolean(control.key),
|
|
5749
|
+
key_hash: hashRuntimeProfileKey(control.key),
|
|
5542
5750
|
launchId: launchId || void 0,
|
|
5543
5751
|
source: "agent_config"
|
|
5544
5752
|
}
|
|
@@ -5903,7 +6111,10 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
5903
6111
|
deliveryId: context.deliveryId,
|
|
5904
6112
|
delivery_correlation_id: context.deliveryId,
|
|
5905
6113
|
control_kind: notification.kind,
|
|
5906
|
-
key_present: Boolean(notification.key)
|
|
6114
|
+
key_present: Boolean(notification.key),
|
|
6115
|
+
runtime_profile_control_kind: notification.kind,
|
|
6116
|
+
runtime_profile_key_hash: hashRuntimeProfileKey(notification.key),
|
|
6117
|
+
runtime_profile_key_present: Boolean(notification.key)
|
|
5907
6118
|
};
|
|
5908
6119
|
}
|
|
5909
6120
|
return {
|
|
@@ -5914,8 +6125,63 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
5914
6125
|
delivery_correlation_id: context.deliveryId ?? first.message_id
|
|
5915
6126
|
};
|
|
5916
6127
|
}
|
|
6128
|
+
runtimeProfileTurnControlTraceAttrs(control) {
|
|
6129
|
+
if (!control) return {};
|
|
6130
|
+
const pendingAgeMs = Math.max(0, Date.now() - control.injectedAtMs);
|
|
6131
|
+
return {
|
|
6132
|
+
runtime_profile_control_kind: control.kind,
|
|
6133
|
+
runtime_profile_control_source: control.source,
|
|
6134
|
+
runtime_profile_key_hash: control.keyHash || void 0,
|
|
6135
|
+
runtime_profile_key_present: control.keyPresent,
|
|
6136
|
+
runtime_profile_pending_age_ms: pendingAgeMs,
|
|
6137
|
+
runtime_profile_requires_ack: control.kind === "migration",
|
|
6138
|
+
runtime_profile_migration_done_observed: control.kind === "migration" ? control.migrationDoneToolObserved : void 0
|
|
6139
|
+
};
|
|
6140
|
+
}
|
|
6141
|
+
activateRuntimeProfileTurnControl(ap, control) {
|
|
6142
|
+
ap.runtimeProfileTurnControl = control;
|
|
6143
|
+
}
|
|
6144
|
+
runtimeProfileTurnControlFromMessages(messages, source = "message") {
|
|
6145
|
+
const notifications = messages?.map((message) => runtimeProfileNotificationFromMessage(message)).filter((candidate) => Boolean(candidate)) ?? [];
|
|
6146
|
+
const notification = notifications.find((candidate) => candidate.kind === "migration") ?? notifications[0];
|
|
6147
|
+
if (!notification) return null;
|
|
6148
|
+
return runtimeProfileTurnControl(notification.kind, notification.key, source);
|
|
6149
|
+
}
|
|
6150
|
+
noteRuntimeProfileToolCall(agentId, ap, toolName) {
|
|
6151
|
+
const control = ap.runtimeProfileTurnControl;
|
|
6152
|
+
if (!control || control.kind !== "migration") return;
|
|
6153
|
+
if (!toolName.includes("runtime_profile_migration_done")) return;
|
|
6154
|
+
control.migrationDoneToolObserved = true;
|
|
6155
|
+
this.recordRuntimeTraceEvent(agentId, ap, "runtime_profile.migration_done_tool.observed", {
|
|
6156
|
+
...this.runtimeProfileTurnControlTraceAttrs(control),
|
|
6157
|
+
tool: toolName
|
|
6158
|
+
});
|
|
6159
|
+
}
|
|
6160
|
+
finalizeRuntimeProfileTurnControl(agentId, ap, terminal) {
|
|
6161
|
+
const control = ap.runtimeProfileTurnControl;
|
|
6162
|
+
if (!control) return {};
|
|
6163
|
+
const attrs = this.runtimeProfileTurnControlTraceAttrs(control);
|
|
6164
|
+
if (control.kind === "migration" && !control.migrationDoneToolObserved) {
|
|
6165
|
+
this.recordRuntimeTraceEvent(agentId, ap, "runtime_profile.migration.turn_without_ack", {
|
|
6166
|
+
...attrs,
|
|
6167
|
+
terminal,
|
|
6168
|
+
inbox_count: ap.inbox.length,
|
|
6169
|
+
pending_notification_count: ap.pendingNotificationCount
|
|
6170
|
+
});
|
|
6171
|
+
}
|
|
6172
|
+
ap.runtimeProfileTurnControl = null;
|
|
6173
|
+
return {
|
|
6174
|
+
...attrs,
|
|
6175
|
+
runtime_profile_turn_terminal: terminal,
|
|
6176
|
+
runtime_profile_turn_outcome: control.kind === "migration" ? control.migrationDoneToolObserved ? "migration_done_observed" : "missing_migration_done" : "notice_only"
|
|
6177
|
+
};
|
|
6178
|
+
}
|
|
5917
6179
|
startRuntimeTrace(agentId, ap, reason, messages) {
|
|
5918
6180
|
if (ap.runtimeTraceSpan) return ap.runtimeTraceSpan;
|
|
6181
|
+
const messageControl = this.runtimeProfileTurnControlFromMessages(messages);
|
|
6182
|
+
if (messageControl) {
|
|
6183
|
+
this.activateRuntimeProfileTurnControl(ap, messageControl);
|
|
6184
|
+
}
|
|
5919
6185
|
const span = this.tracer.startSpan("daemon.runtime.turn", {
|
|
5920
6186
|
surface: "daemon",
|
|
5921
6187
|
kind: "internal",
|
|
@@ -5925,10 +6191,15 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
5925
6191
|
model: ap.config.model,
|
|
5926
6192
|
reason,
|
|
5927
6193
|
hasSession: Boolean(ap.sessionId),
|
|
5928
|
-
...this.messagesTraceAttrs(messages)
|
|
6194
|
+
...this.messagesTraceAttrs(messages),
|
|
6195
|
+
...this.runtimeProfileTurnControlTraceAttrs(ap.runtimeProfileTurnControl)
|
|
5929
6196
|
}
|
|
5930
6197
|
});
|
|
5931
|
-
span.addEvent("daemon.turn.started", {
|
|
6198
|
+
span.addEvent("daemon.turn.started", {
|
|
6199
|
+
reason,
|
|
6200
|
+
...this.messagesTraceAttrs(messages),
|
|
6201
|
+
...this.runtimeProfileTurnControlTraceAttrs(ap.runtimeProfileTurnControl)
|
|
6202
|
+
});
|
|
5932
6203
|
ap.runtimeTraceSpan = span;
|
|
5933
6204
|
return span;
|
|
5934
6205
|
}
|
|
@@ -6034,7 +6305,8 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6034
6305
|
ageMs: staleForMs,
|
|
6035
6306
|
lastActivity: ap.lastActivity,
|
|
6036
6307
|
lastActivityDetailPresent: Boolean(ap.lastActivityDetail),
|
|
6037
|
-
lastActivityDetailKind: classifyActivityDetailForTrace(ap.lastActivityDetail)
|
|
6308
|
+
lastActivityDetailKind: classifyActivityDetailForTrace(ap.lastActivityDetail),
|
|
6309
|
+
...this.finalizeRuntimeProfileTurnControl(agentId, ap, "runtime_stalled")
|
|
6038
6310
|
});
|
|
6039
6311
|
this.broadcastActivity(agentId, "error", diagnostic.detail);
|
|
6040
6312
|
return true;
|
|
@@ -6065,7 +6337,8 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6065
6337
|
lastActivityDetailPresent: Boolean(ap.lastActivityDetail),
|
|
6066
6338
|
lastActivityDetailKind: classifyActivityDetailForTrace(ap.lastActivityDetail),
|
|
6067
6339
|
pendingMessages: ap.inbox.length,
|
|
6068
|
-
recovery: "terminate_for_queued_message"
|
|
6340
|
+
recovery: "terminate_for_queued_message",
|
|
6341
|
+
...this.finalizeRuntimeProfileTurnControl(agentId, ap, "runtime_stalled")
|
|
6069
6342
|
});
|
|
6070
6343
|
ap.expectedTerminationReason = "stalled_recovery";
|
|
6071
6344
|
const runtimeLabel = ap.driver.id === "opencode" ? "OpenCode" : ap.driver.id;
|
|
@@ -6074,6 +6347,11 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6074
6347
|
);
|
|
6075
6348
|
this.broadcastActivity(agentId, "working", `Restarting stalled ${runtimeLabel} runtime for queued message`);
|
|
6076
6349
|
try {
|
|
6350
|
+
this.processExitTraceAttrs.set(ap.process, {
|
|
6351
|
+
stop_source: "stalled_recovery",
|
|
6352
|
+
expectedTerminationReason: "stalled_recovery",
|
|
6353
|
+
queued_messages_count: ap.inbox.length
|
|
6354
|
+
});
|
|
6077
6355
|
ap.process.kill("SIGTERM");
|
|
6078
6356
|
} catch (err) {
|
|
6079
6357
|
const reason = err instanceof Error ? err.message : String(err);
|
|
@@ -6122,6 +6400,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6122
6400
|
if (ap) {
|
|
6123
6401
|
ap.gatedSteering.outstandingToolUses++;
|
|
6124
6402
|
this.clearGatedInFlightBatch(agentId, ap, "non_error_progress");
|
|
6403
|
+
this.noteRuntimeProfileToolCall(agentId, ap, invocation.toolName);
|
|
6125
6404
|
this.recordRuntimeTraceEvent(agentId, ap, "tool.call.started", { tool: invocation.toolName });
|
|
6126
6405
|
this.setGatedSteeringPhase(agentId, ap, "tool_wait", {
|
|
6127
6406
|
event: "tool_call",
|
|
@@ -6211,11 +6490,18 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6211
6490
|
this.broadcastActivity(agentId, "online", "Idle");
|
|
6212
6491
|
}
|
|
6213
6492
|
}
|
|
6214
|
-
this.endRuntimeTrace(ap, "ok", {
|
|
6493
|
+
this.endRuntimeTrace(ap, "ok", {
|
|
6494
|
+
outcome: "turn-completed",
|
|
6495
|
+
...this.finalizeRuntimeProfileTurnControl(agentId, ap, "turn_end")
|
|
6496
|
+
});
|
|
6215
6497
|
if (ap.driver.terminateProcessOnTurnEnd) {
|
|
6216
6498
|
ap.expectedTerminationReason = "turn_end";
|
|
6217
6499
|
logger.info(`[Agent ${agentId}] Turn completed; terminating ${ap.driver.id} process`);
|
|
6218
6500
|
try {
|
|
6501
|
+
this.processExitTraceAttrs.set(ap.process, {
|
|
6502
|
+
stop_source: "turn_end",
|
|
6503
|
+
expectedTerminationReason: "turn_end"
|
|
6504
|
+
});
|
|
6219
6505
|
ap.process.kill("SIGTERM");
|
|
6220
6506
|
} catch (err) {
|
|
6221
6507
|
const reason = err instanceof Error ? err.message : String(err);
|
|
@@ -6233,6 +6519,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6233
6519
|
this.flushPendingTrajectory(agentId);
|
|
6234
6520
|
if (ap) ap.lastRuntimeError = event.message;
|
|
6235
6521
|
if (ap) {
|
|
6522
|
+
const runtimeErrorDiagnostics = buildRuntimeErrorDiagnosticEnvelope(event.message);
|
|
6236
6523
|
this.setGatedSteeringPhase(agentId, ap, "error", { event: "error" });
|
|
6237
6524
|
if (ap.driver.busyDeliveryMode === "gated" && this.isThinkingBlockMutationError(event.message)) {
|
|
6238
6525
|
this.requeueGatedInFlightBatch(agentId, ap, "thinking_block_mutation_error");
|
|
@@ -6246,8 +6533,12 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6246
6533
|
`[Agent ${agentId}] Disabled Claude tool-boundary gated steering after thinking-block mutation error; lastFlushReason=${ap.gatedSteering.lastFlushReason || "none"}`
|
|
6247
6534
|
);
|
|
6248
6535
|
}
|
|
6249
|
-
this.recordRuntimeTraceEvent(agentId, ap, "runtime.error",
|
|
6250
|
-
this.endRuntimeTrace(ap, "error", {
|
|
6536
|
+
this.recordRuntimeTraceEvent(agentId, ap, "runtime.error", runtimeErrorDiagnostics.eventAttrs);
|
|
6537
|
+
this.endRuntimeTrace(ap, "error", {
|
|
6538
|
+
outcome: "runtime-error",
|
|
6539
|
+
...runtimeErrorDiagnostics.spanAttrs,
|
|
6540
|
+
...this.finalizeRuntimeProfileTurnControl(agentId, ap, "runtime_error")
|
|
6541
|
+
});
|
|
6251
6542
|
if (ap.driver.supportsStdinNotification && classifyTerminalFailure(ap)) {
|
|
6252
6543
|
ap.isIdle = true;
|
|
6253
6544
|
ap.pendingNotificationCount = 0;
|
|
@@ -6721,11 +7012,10 @@ var ReminderCache = class {
|
|
|
6721
7012
|
};
|
|
6722
7013
|
|
|
6723
7014
|
// src/machineLock.ts
|
|
6724
|
-
import { createHash, randomUUID as randomUUID2 } from "crypto";
|
|
7015
|
+
import { createHash as createHash3, randomUUID as randomUUID2 } from "crypto";
|
|
6725
7016
|
import { mkdirSync as mkdirSync5, readFileSync as readFileSync5, rmSync as rmSync2, statSync as statSync3, writeFileSync as writeFileSync8 } from "fs";
|
|
6726
7017
|
import os6 from "os";
|
|
6727
7018
|
import path12 from "path";
|
|
6728
|
-
var DEFAULT_MACHINE_STATE_ROOT = path12.join(os6.homedir(), ".slock", "machines");
|
|
6729
7019
|
var INCOMPLETE_LOCK_STALE_MS = 3e4;
|
|
6730
7020
|
var DaemonMachineLockConflictError = class extends Error {
|
|
6731
7021
|
code = "DAEMON_MACHINE_LOCK_HELD";
|
|
@@ -6738,11 +7028,14 @@ var DaemonMachineLockConflictError = class extends Error {
|
|
|
6738
7028
|
}
|
|
6739
7029
|
};
|
|
6740
7030
|
function apiKeyFingerprint(apiKey) {
|
|
6741
|
-
return
|
|
7031
|
+
return createHash3("sha256").update(apiKey).digest("hex");
|
|
6742
7032
|
}
|
|
6743
7033
|
function getDaemonMachineLockId(apiKey) {
|
|
6744
7034
|
return `machine-${apiKeyFingerprint(apiKey).slice(0, 16)}`;
|
|
6745
7035
|
}
|
|
7036
|
+
function resolveDefaultMachineStateRoot() {
|
|
7037
|
+
return resolveSlockHomePath("machines");
|
|
7038
|
+
}
|
|
6746
7039
|
function ownerPath(lockDir) {
|
|
6747
7040
|
return path12.join(lockDir, "owner.json");
|
|
6748
7041
|
}
|
|
@@ -6771,7 +7064,7 @@ function isProcessAlive(pid) {
|
|
|
6771
7064
|
}
|
|
6772
7065
|
}
|
|
6773
7066
|
function acquireDaemonMachineLock(options) {
|
|
6774
|
-
const rootDir = options.rootDir ??
|
|
7067
|
+
const rootDir = options.rootDir ?? resolveDefaultMachineStateRoot();
|
|
6775
7068
|
const fingerprint = apiKeyFingerprint(options.apiKey);
|
|
6776
7069
|
const lockId = getDaemonMachineLockId(options.apiKey);
|
|
6777
7070
|
const machineDir = path12.join(rootDir, lockId);
|
|
@@ -6844,6 +7137,15 @@ var DIAGNOSTIC_ID_ATTRS = /* @__PURE__ */ new Set([
|
|
|
6844
7137
|
"deliveryCorrelationId",
|
|
6845
7138
|
"delivery_correlation_id"
|
|
6846
7139
|
]);
|
|
7140
|
+
var DIAGNOSTIC_ERROR_ATTRS = /* @__PURE__ */ new Set([
|
|
7141
|
+
"runtime_error_class",
|
|
7142
|
+
"runtime_error_fingerprint",
|
|
7143
|
+
"runtime_error_http_status",
|
|
7144
|
+
"runtime_error_message_present",
|
|
7145
|
+
"runtime_error_message_length_bucket",
|
|
7146
|
+
"runtime_error_message_truncated",
|
|
7147
|
+
"runtime_error_message_excerpt"
|
|
7148
|
+
]);
|
|
6847
7149
|
var LocalRotatingTraceSink = class {
|
|
6848
7150
|
traceDir;
|
|
6849
7151
|
maxFileBytes;
|
|
@@ -6941,6 +7243,11 @@ function sanitizeAttrs(attrs) {
|
|
|
6941
7243
|
sanitized[key] = sanitizeValue(value);
|
|
6942
7244
|
continue;
|
|
6943
7245
|
}
|
|
7246
|
+
if (isDiagnosticErrorAttr(key)) {
|
|
7247
|
+
if (value === null || value === void 0 || value === "") continue;
|
|
7248
|
+
sanitized[key] = sanitizeValue(value);
|
|
7249
|
+
continue;
|
|
7250
|
+
}
|
|
6944
7251
|
if (shouldDropAttr(key)) continue;
|
|
6945
7252
|
sanitized[key] = sanitizeValue(value);
|
|
6946
7253
|
}
|
|
@@ -6974,9 +7281,12 @@ function shouldDropAttr(key) {
|
|
|
6974
7281
|
function isDiagnosticIdAttr(key) {
|
|
6975
7282
|
return DIAGNOSTIC_ID_ATTRS.has(key);
|
|
6976
7283
|
}
|
|
7284
|
+
function isDiagnosticErrorAttr(key) {
|
|
7285
|
+
return DIAGNOSTIC_ERROR_ATTRS.has(key);
|
|
7286
|
+
}
|
|
6977
7287
|
|
|
6978
7288
|
// src/traceBundleUpload.ts
|
|
6979
|
-
import { createHash as
|
|
7289
|
+
import { createHash as createHash5, randomUUID as randomUUID3 } from "crypto";
|
|
6980
7290
|
import { gzipSync } from "zlib";
|
|
6981
7291
|
import { mkdir as mkdir2, readFile as readFile2, readdir as readdir3, stat as stat3, writeFile as writeFile2 } from "fs/promises";
|
|
6982
7292
|
import path14 from "path";
|
|
@@ -7106,12 +7416,12 @@ async function uploadWithSignedCapability({
|
|
|
7106
7416
|
}
|
|
7107
7417
|
|
|
7108
7418
|
// src/traceJitter.ts
|
|
7109
|
-
import { createHash as
|
|
7419
|
+
import { createHash as createHash4 } from "crypto";
|
|
7110
7420
|
var INITIAL_UPLOAD_DELAY_SPAN_MS = 3e4;
|
|
7111
7421
|
var UPLOAD_INTERVAL_JITTER_SPAN_MS = 6e4;
|
|
7112
7422
|
var MAX_FILE_AGE_JITTER_SPAN_MS = 6e4;
|
|
7113
7423
|
function computeTraceJitter(lockId) {
|
|
7114
|
-
const seed =
|
|
7424
|
+
const seed = createHash4("sha256").update(lockId).digest();
|
|
7115
7425
|
return {
|
|
7116
7426
|
initialUploadDelayMs: seed.readUInt32BE(0) % INITIAL_UPLOAD_DELAY_SPAN_MS,
|
|
7117
7427
|
uploadIntervalJitterMs: seed.readUInt32BE(4) % UPLOAD_INTERVAL_JITTER_SPAN_MS,
|
|
@@ -7312,7 +7622,7 @@ var DaemonTraceBundleUploader = class {
|
|
|
7312
7622
|
}
|
|
7313
7623
|
};
|
|
7314
7624
|
function sha256Hex(body) {
|
|
7315
|
-
return
|
|
7625
|
+
return createHash5("sha256").update(body).digest("hex");
|
|
7316
7626
|
}
|
|
7317
7627
|
function readPositiveIntegerEnv2(name, fallback) {
|
|
7318
7628
|
const value = process.env[name];
|
|
@@ -7491,6 +7801,7 @@ var DaemonCore = class {
|
|
|
7491
7801
|
daemonVersion;
|
|
7492
7802
|
chatBridgePath;
|
|
7493
7803
|
slockCliPath;
|
|
7804
|
+
slockHome;
|
|
7494
7805
|
runtimeDetector;
|
|
7495
7806
|
agentManager;
|
|
7496
7807
|
connection;
|
|
@@ -7505,6 +7816,8 @@ var DaemonCore = class {
|
|
|
7505
7816
|
this.daemonVersion = options.daemonVersion ?? readDaemonVersion();
|
|
7506
7817
|
this.chatBridgePath = options.chatBridgePath ?? resolveChatBridgePath();
|
|
7507
7818
|
this.slockCliPath = options.slockCliPath ?? resolveSlockCliPath();
|
|
7819
|
+
this.slockHome = resolveSlockHome();
|
|
7820
|
+
process.env[SLOCK_HOME_ENV] = this.slockHome;
|
|
7508
7821
|
this.injectedTracer = Boolean(options.tracer);
|
|
7509
7822
|
this.tracer = options.tracer ?? noopTracer;
|
|
7510
7823
|
this.runtimeDetector = options.runtimeDetector ?? (() => detectRuntimes(this.tracer));
|
|
@@ -7514,7 +7827,7 @@ var DaemonCore = class {
|
|
|
7514
7827
|
});
|
|
7515
7828
|
let connection;
|
|
7516
7829
|
const agentManagerOptions = {
|
|
7517
|
-
dataDir: options.dataDir,
|
|
7830
|
+
dataDir: options.dataDir ?? resolveSlockHomePath("agents", this.slockHome),
|
|
7518
7831
|
serverUrl: options.serverUrl,
|
|
7519
7832
|
defaultAgentEnvVarsProvider: options.defaultAgentEnvVarsProvider,
|
|
7520
7833
|
slockCliPath: this.slockCliPath,
|
|
@@ -7536,7 +7849,7 @@ var DaemonCore = class {
|
|
|
7536
7849
|
resolveMachineStateRoot() {
|
|
7537
7850
|
if (this.options.machineStateDir) return this.options.machineStateDir;
|
|
7538
7851
|
if (this.options.dataDir) return path15.join(path15.dirname(this.options.dataDir), "machines");
|
|
7539
|
-
return
|
|
7852
|
+
return resolveDefaultMachineStateRoot();
|
|
7540
7853
|
}
|
|
7541
7854
|
shouldEnableLocalTrace() {
|
|
7542
7855
|
if (this.injectedTracer) return false;
|
|
@@ -7580,6 +7893,12 @@ var DaemonCore = class {
|
|
|
7580
7893
|
}
|
|
7581
7894
|
start() {
|
|
7582
7895
|
logger.info("[Slock Daemon] Starting...");
|
|
7896
|
+
logger.info(`[Slock Daemon] ${SLOCK_HOME_ENV}=${this.slockHome}`);
|
|
7897
|
+
for (const legacy of listLegacySlockStatePaths(this.slockHome)) {
|
|
7898
|
+
logger.warn(
|
|
7899
|
+
`[Slock Daemon] Legacy Slock state exists outside ${SLOCK_HOME_ENV}: ${legacy.path}. This daemon will use ${legacy.destination}; migrate manually if that ${legacy.description} should move with this installation.`
|
|
7900
|
+
);
|
|
7901
|
+
}
|
|
7583
7902
|
if (!this.machineLock) {
|
|
7584
7903
|
this.machineLock = acquireDaemonMachineLock({
|
|
7585
7904
|
apiKey: this.options.apiKey,
|