@slock-ai/daemon 0.57.3 → 0.57.5-play.20260611173452
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-H2QU4LAU.js → chunk-NHANGBTA.js} +628 -364
- package/dist/cli/index.js +2040 -426
- package/dist/cli/package.json +1 -1
- package/dist/core.js +5 -1
- package/dist/index.js +5 -3
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/core.ts
|
|
2
|
-
import
|
|
3
|
-
import
|
|
2
|
+
import path16 from "path";
|
|
3
|
+
import os8 from "os";
|
|
4
4
|
import { createRequire as createRequire2 } from "module";
|
|
5
5
|
import { accessSync } from "fs";
|
|
6
6
|
import { fileURLToPath } from "url";
|
|
@@ -1058,6 +1058,143 @@ function shortMessageId(value) {
|
|
|
1058
1058
|
return value.slice(0, 8);
|
|
1059
1059
|
}
|
|
1060
1060
|
|
|
1061
|
+
// ../shared/src/externalAgentIntegration.ts
|
|
1062
|
+
import { z as z2 } from "zod";
|
|
1063
|
+
var EXTERNAL_AGENT_COMMS_PROTOCOL_VERSION = "agent-comms-core.v1";
|
|
1064
|
+
var EXTERNAL_AGENT_PROOF_SCHEMA_VERSION = "agent-proof.v1";
|
|
1065
|
+
var EXTERNAL_RUNTIME_INTEGRATION_MANIFEST_SCHEMA = "slock-external-runtime-integration.v1";
|
|
1066
|
+
var EXTERNAL_AGENT_WAKE_EVENT_SCHEMA = "slock-external-agent-wake-event.v1";
|
|
1067
|
+
var externalAgentCommsModeValues = ["spawn-core", "attach-core", "import-core"];
|
|
1068
|
+
var externalAgentIntegrationPatternValues = ["external-harness-plugin", "integrated-runtime"];
|
|
1069
|
+
var externalAgentIsolationValues = ["profile-scoped", "session-scoped"];
|
|
1070
|
+
var externalAgentWakeAdapterKindValues = ["claude-code-channels", "hermes-in-process"];
|
|
1071
|
+
var externalAgentWakeProofLevelValues = [
|
|
1072
|
+
"server_delivered",
|
|
1073
|
+
"harness_accepted",
|
|
1074
|
+
"wake_injected"
|
|
1075
|
+
];
|
|
1076
|
+
var externalAgentProofLevelValues = [
|
|
1077
|
+
...externalAgentWakeProofLevelValues,
|
|
1078
|
+
"model_seen"
|
|
1079
|
+
];
|
|
1080
|
+
var externalAgentCommsLifecycleStateValues = [
|
|
1081
|
+
"unbound",
|
|
1082
|
+
"bound_stopped",
|
|
1083
|
+
"starting",
|
|
1084
|
+
"connected_replaying",
|
|
1085
|
+
"listening_idle",
|
|
1086
|
+
"handoff_pending",
|
|
1087
|
+
"degraded_backoff",
|
|
1088
|
+
"stopping",
|
|
1089
|
+
"stopped",
|
|
1090
|
+
"auth_revoked"
|
|
1091
|
+
];
|
|
1092
|
+
var externalAgentLifecycleSourceValues = ["server_core", "comms_core", "wake_adapter", "server"];
|
|
1093
|
+
var externalAgentLifecycleProvenanceValues = [
|
|
1094
|
+
"slock_core",
|
|
1095
|
+
"adapter_observed",
|
|
1096
|
+
"agent_reported"
|
|
1097
|
+
];
|
|
1098
|
+
var externalAgentAdapterFailureValues = [
|
|
1099
|
+
"no_session",
|
|
1100
|
+
"busy",
|
|
1101
|
+
"injection_failed",
|
|
1102
|
+
"protocol_mismatch",
|
|
1103
|
+
"auth_revoked"
|
|
1104
|
+
];
|
|
1105
|
+
var externalAgentServerApiModeValues = ["interim-agent-api-events", "agent-inbox-protocol"];
|
|
1106
|
+
var nonEmptyStringSchema = z2.string().min(1);
|
|
1107
|
+
var isoTimestampSchema = z2.string().refine((value) => !Number.isNaN(Date.parse(value)), {
|
|
1108
|
+
message: "must be a parseable timestamp"
|
|
1109
|
+
});
|
|
1110
|
+
var externalRuntimeIntegrationManifestSchema = z2.object({
|
|
1111
|
+
schema: z2.literal(EXTERNAL_RUNTIME_INTEGRATION_MANIFEST_SCHEMA),
|
|
1112
|
+
runtimeId: nonEmptyStringSchema,
|
|
1113
|
+
integrationPattern: z2.enum(externalAgentIntegrationPatternValues),
|
|
1114
|
+
commsMode: z2.enum(externalAgentCommsModeValues),
|
|
1115
|
+
commsProtocolVersion: z2.literal(EXTERNAL_AGENT_COMMS_PROTOCOL_VERSION),
|
|
1116
|
+
proofSchemaVersion: z2.literal(EXTERNAL_AGENT_PROOF_SCHEMA_VERSION),
|
|
1117
|
+
minSlockCliVersion: nonEmptyStringSchema,
|
|
1118
|
+
multiplex: z2.boolean(),
|
|
1119
|
+
agentIsolation: z2.enum(externalAgentIsolationValues),
|
|
1120
|
+
bridgeLifecycle: z2.object({
|
|
1121
|
+
explicitStartOnly: z2.literal(true),
|
|
1122
|
+
oneShotCommandsBridgeIndependent: z2.literal(true),
|
|
1123
|
+
requiresBridgeFailureCodes: z2.array(z2.enum(["BRIDGE_NOT_RUNNING", "NO_CORE_SESSION"])).min(1),
|
|
1124
|
+
autoStartDefault: z2.literal(false)
|
|
1125
|
+
}).strict(),
|
|
1126
|
+
serverApi: z2.object({
|
|
1127
|
+
mode: z2.enum(externalAgentServerApiModeValues),
|
|
1128
|
+
deliveryOnly: z2.boolean(),
|
|
1129
|
+
cursorAuthority: z2.literal("model_seen_only")
|
|
1130
|
+
}).strict(),
|
|
1131
|
+
wakeAdapter: z2.object({
|
|
1132
|
+
kind: z2.enum(externalAgentWakeAdapterKindValues),
|
|
1133
|
+
protocol: nonEmptyStringSchema,
|
|
1134
|
+
requiresInteractiveSession: z2.boolean().optional()
|
|
1135
|
+
}).strict()
|
|
1136
|
+
}).strict().superRefine((value, ctx) => {
|
|
1137
|
+
if (!value.bridgeLifecycle.requiresBridgeFailureCodes.includes("BRIDGE_NOT_RUNNING")) {
|
|
1138
|
+
ctx.addIssue({
|
|
1139
|
+
code: "custom",
|
|
1140
|
+
path: ["bridgeLifecycle", "requiresBridgeFailureCodes"],
|
|
1141
|
+
message: "bridge-dependent surfaces must include BRIDGE_NOT_RUNNING"
|
|
1142
|
+
});
|
|
1143
|
+
}
|
|
1144
|
+
if (!value.bridgeLifecycle.requiresBridgeFailureCodes.includes("NO_CORE_SESSION")) {
|
|
1145
|
+
ctx.addIssue({
|
|
1146
|
+
code: "custom",
|
|
1147
|
+
path: ["bridgeLifecycle", "requiresBridgeFailureCodes"],
|
|
1148
|
+
message: "bridge-dependent surfaces must include NO_CORE_SESSION"
|
|
1149
|
+
});
|
|
1150
|
+
}
|
|
1151
|
+
if (value.serverApi.mode === "interim-agent-api-events" && value.serverApi.deliveryOnly !== true) {
|
|
1152
|
+
ctx.addIssue({
|
|
1153
|
+
code: "custom",
|
|
1154
|
+
path: ["serverApi", "deliveryOnly"],
|
|
1155
|
+
message: "interim agent-api events mode must be delivery-only"
|
|
1156
|
+
});
|
|
1157
|
+
}
|
|
1158
|
+
});
|
|
1159
|
+
var externalAgentWakeEventBaseSchema = z2.object({
|
|
1160
|
+
schema: z2.literal(EXTERNAL_AGENT_WAKE_EVENT_SCHEMA),
|
|
1161
|
+
eventId: nonEmptyStringSchema,
|
|
1162
|
+
attemptId: nonEmptyStringSchema,
|
|
1163
|
+
messageId: nonEmptyStringSchema,
|
|
1164
|
+
agentId: nonEmptyStringSchema,
|
|
1165
|
+
profile: nonEmptyStringSchema,
|
|
1166
|
+
coreSessionId: nonEmptyStringSchema,
|
|
1167
|
+
adapterInstance: nonEmptyStringSchema,
|
|
1168
|
+
runtimeSession: nonEmptyStringSchema.nullable(),
|
|
1169
|
+
occurredAt: isoTimestampSchema,
|
|
1170
|
+
lifecycleState: z2.enum(externalAgentCommsLifecycleStateValues),
|
|
1171
|
+
authority: z2.object({
|
|
1172
|
+
source: z2.enum(externalAgentLifecycleSourceValues),
|
|
1173
|
+
provenance: z2.enum(externalAgentLifecycleProvenanceValues)
|
|
1174
|
+
}).strict()
|
|
1175
|
+
}).strict();
|
|
1176
|
+
var externalAgentProofEventEnvelopeSchema = externalAgentWakeEventBaseSchema.extend({
|
|
1177
|
+
kind: z2.literal("proof"),
|
|
1178
|
+
proofLevel: z2.enum(externalAgentWakeProofLevelValues),
|
|
1179
|
+
outcome: z2.literal("ok"),
|
|
1180
|
+
failureMeta: z2.undefined().optional(),
|
|
1181
|
+
reason: z2.string().optional()
|
|
1182
|
+
});
|
|
1183
|
+
var externalAgentFailedWakeEventEnvelopeSchema = externalAgentWakeEventBaseSchema.extend({
|
|
1184
|
+
kind: z2.literal("wake_attempt"),
|
|
1185
|
+
proofLevel: z2.undefined().optional(),
|
|
1186
|
+
outcome: z2.literal("failed"),
|
|
1187
|
+
failureMeta: z2.object({
|
|
1188
|
+
failureClass: z2.enum(externalAgentAdapterFailureValues),
|
|
1189
|
+
retryAfterMs: z2.number().int().positive().optional()
|
|
1190
|
+
}).strict(),
|
|
1191
|
+
reason: nonEmptyStringSchema
|
|
1192
|
+
});
|
|
1193
|
+
var externalAgentWakeEventEnvelopeSchema = z2.discriminatedUnion("kind", [
|
|
1194
|
+
externalAgentProofEventEnvelopeSchema,
|
|
1195
|
+
externalAgentFailedWakeEventEnvelopeSchema
|
|
1196
|
+
]);
|
|
1197
|
+
|
|
1061
1198
|
// ../shared/src/translationLanguages.ts
|
|
1062
1199
|
var SUPPORTED_TRANSLATION_LANGUAGE_CODES = [
|
|
1063
1200
|
"en",
|
|
@@ -1164,6 +1301,7 @@ var RUNTIME_MODELS = {
|
|
|
1164
1301
|
{ id: "claude-opus-4-8", label: "Claude Opus 4.8" },
|
|
1165
1302
|
{ id: "claude-opus-4-7", label: "Claude Opus 4.7" },
|
|
1166
1303
|
{ id: "claude-opus-4-6", label: "Claude Opus 4.6" },
|
|
1304
|
+
{ id: "fable", label: "Claude Fable" },
|
|
1167
1305
|
{ id: "sonnet", label: "Claude Sonnet" },
|
|
1168
1306
|
{ id: "claude-sonnet-4-6", label: "Claude Sonnet 4.6" },
|
|
1169
1307
|
{ id: "haiku", label: "Claude Haiku" },
|
|
@@ -1366,11 +1504,11 @@ var DISPLAY_PLAN_CONFIG = {
|
|
|
1366
1504
|
};
|
|
1367
1505
|
|
|
1368
1506
|
// src/agentProcessManager.ts
|
|
1369
|
-
import { mkdirSync as
|
|
1507
|
+
import { mkdirSync as mkdirSync4, readdirSync as readdirSync2, statSync, writeFileSync as writeFileSync4 } from "fs";
|
|
1370
1508
|
import { mkdir, writeFile, access, readdir as readdir2, stat as stat2, readFile, rm as rm2 } from "fs/promises";
|
|
1371
1509
|
import { createHash as createHash3 } from "crypto";
|
|
1372
|
-
import
|
|
1373
|
-
import
|
|
1510
|
+
import path12 from "path";
|
|
1511
|
+
import os6 from "os";
|
|
1374
1512
|
|
|
1375
1513
|
// src/drivers/claude.ts
|
|
1376
1514
|
import { spawn } from "child_process";
|
|
@@ -1704,9 +1842,6 @@ function buildSlockCliGuideSections(options) {
|
|
|
1704
1842
|
}
|
|
1705
1843
|
|
|
1706
1844
|
// src/drivers/systemPrompt.ts
|
|
1707
|
-
function toolRef(prefix, name) {
|
|
1708
|
-
return `${prefix}${name}`;
|
|
1709
|
-
}
|
|
1710
1845
|
function runtimeContextLines(config) {
|
|
1711
1846
|
const ctx = config.runtimeContext;
|
|
1712
1847
|
if (!ctx) return [];
|
|
@@ -1728,10 +1863,8 @@ function runtimeContextLines(config) {
|
|
|
1728
1863
|
if (ctx.workspacePath) lines.push(`- Workspace: ${ctx.workspacePath}`);
|
|
1729
1864
|
return lines.length > 4 ? lines : [];
|
|
1730
1865
|
}
|
|
1731
|
-
function buildPrompt(config,
|
|
1732
|
-
const
|
|
1733
|
-
const t = (name) => toolRef(opts.toolPrefix, name);
|
|
1734
|
-
const taskClaimCmd = isCli ? "`slock task claim`" : `\`${t("claim_tasks")}\``;
|
|
1866
|
+
function buildPrompt(config, opts) {
|
|
1867
|
+
const taskClaimCmd = "`slock task claim`";
|
|
1735
1868
|
const cliGuideSections = buildSlockCliGuideSections({
|
|
1736
1869
|
// The daemon prompt audience is always managed-runner regardless of
|
|
1737
1870
|
// CLI vs MCP variant — both are daemon-spawned. The self-hosted-runner
|
|
@@ -1743,162 +1876,42 @@ function buildPrompt(config, variant, opts) {
|
|
|
1743
1876
|
},
|
|
1744
1877
|
taskClaimCmd
|
|
1745
1878
|
});
|
|
1746
|
-
const
|
|
1747
|
-
const
|
|
1748
|
-
const checkCmd = isCli ? "`slock message check`" : `\`${t("check_messages")}\``;
|
|
1749
|
-
const inboxCheckCmd = isCli ? "`slock inbox check`" : checkCmd;
|
|
1750
|
-
const taskCreateCmd = isCli ? "`slock task create`" : `\`${t("create_tasks")}\``;
|
|
1751
|
-
const taskUpdateCmd = isCli ? "`slock task update`" : `\`${t("update_task_status")}\``;
|
|
1752
|
-
const serverInfoCmd = isCli ? "`slock server info`" : `\`${t("list_server")}\``;
|
|
1753
|
-
const scheduleReminderCmd = isCli ? "`slock reminder schedule`" : `\`${t("schedule_reminder")}\``;
|
|
1754
|
-
const listRemindersCmd = isCli ? "`slock reminder list`" : `\`${t("list_reminders")}\``;
|
|
1755
|
-
const cancelReminderCmd = isCli ? "`slock reminder cancel`" : `\`${t("cancel_reminder")}\``;
|
|
1879
|
+
const checkCmd = "`slock message check`";
|
|
1880
|
+
const inboxCheckCmd = "`slock inbox check`";
|
|
1756
1881
|
const messageDeliveryText = opts.includeStdinNotificationSection ? "New messages may be delivered to you automatically while your process stays alive." : "The daemon will automatically restart you when new messages arrive.";
|
|
1757
|
-
const
|
|
1882
|
+
const noConcreteMessageStartupStep = opts.includeStdinNotificationSection ? `3. If there is no concrete incoming message to handle, but this turn includes a Slock inbox notice, treat it as a wake signal: call ${checkCmd} to read the pending message content before deciding there is no work. If there is neither a concrete message nor an inbox notice, stop and wait. ${messageDeliveryText}` : `3. If there is no concrete incoming message to handle, stop and wait. ${messageDeliveryText}`;
|
|
1883
|
+
const criticalRules = [
|
|
1758
1884
|
"- Always communicate through `slock` CLI commands. This is your only output channel: text you produce outside a `slock` command is not delivered to anyone.",
|
|
1759
1885
|
...opts.extraCriticalRules,
|
|
1760
1886
|
"- Use only the provided `slock` CLI commands for messaging.",
|
|
1761
1887
|
"- Do not combine multiple `slock` CLI commands in one shell command. Run one `slock` command per tool call, read its output, then decide the next command.",
|
|
1762
1888
|
"- Always claim a task via `slock task claim` before starting work on it. If the claim fails, move on to a different task."
|
|
1763
|
-
] : [
|
|
1764
|
-
`- Always communicate through ${sendCmd}. This is your only output channel: text you produce outside a messaging tool is not delivered to anyone.`,
|
|
1765
|
-
...opts.extraCriticalRules,
|
|
1766
|
-
"- Use only the provided MCP tools for messaging \u2014 they are already available and ready.",
|
|
1767
|
-
`- Always claim a task via ${taskClaimCmd} before starting work on it. If the claim fails, move on to a different task.`
|
|
1768
1889
|
];
|
|
1769
1890
|
const runtimeProfileControl = config.runtimeProfileControl?.kind === "daemon_release_notice" ? config.runtimeProfileControl : null;
|
|
1770
1891
|
const runtimeProfileControlStartupStep = runtimeProfileControl ? [
|
|
1771
1892
|
"0. If this system prompt contains a **Runtime Profile Control** section, read that notice first. It is informational only; no runtime control action or chat reply is required. Do not read MEMORY.md, check messages, or respond to inbox messages before reading it."
|
|
1772
1893
|
] : [];
|
|
1773
|
-
const startupSteps =
|
|
1894
|
+
const startupSteps = [
|
|
1774
1895
|
...runtimeProfileControlStartupStep,
|
|
1775
1896
|
"1. If this turn already includes a concrete incoming message, first decide whether that message needs a visible acknowledgment, blocker question, or ownership signal. If it does, send it early with `slock message send` before deep context gathering.",
|
|
1776
1897
|
"2. Read MEMORY.md (in your cwd) and then only the additional memory/files you need to handle the current turn well.",
|
|
1777
|
-
|
|
1898
|
+
noConcreteMessageStartupStep,
|
|
1778
1899
|
"4. When you receive a message, process it and reply with `slock message send`.",
|
|
1779
1900
|
"5. **Complete ALL your work before stopping.** If a task requires multi-step work (research, code changes, testing), finish everything, report results, then stop. New messages arrive automatically \u2014 you do not need to poll or wait for them."
|
|
1780
|
-
] : [
|
|
1781
|
-
...runtimeProfileControlStartupStep,
|
|
1782
|
-
`1. If this turn already includes a concrete incoming message, first decide whether that message needs a visible acknowledgment, blocker question, or ownership signal. If it does, send it early with ${sendCmd} before deep context gathering.`,
|
|
1783
|
-
"2. Read MEMORY.md (in your cwd) and then only the additional memory/files you need to handle the current turn well.",
|
|
1784
|
-
`3. If there is no concrete incoming message to handle, stop and wait. ${messageDeliveryText}`,
|
|
1785
|
-
`4. When you receive a message, process it and reply with ${sendCmd}.`,
|
|
1786
|
-
"5. **Complete ALL your work before stopping.** If a task requires multi-step work (research, code changes, testing), finish everything, report results, then stop. New messages arrive automatically \u2014 you do not need to poll or wait for them."
|
|
1787
1901
|
];
|
|
1788
|
-
const communicationSection =
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
10. **${taskClaimCmd}** \u2014 Claim tasks by number or message ID (supports batch, handles conflicts).
|
|
1802
|
-
11. **\`${t("unclaim_task")}\`** \u2014 Release your claim on a task.
|
|
1803
|
-
12. **${taskUpdateCmd}** \u2014 Change a task's status (e.g. to in_review or done).
|
|
1804
|
-
13. **\`${t("upload_file")}\`** \u2014 Upload a file up to 50MB to attach to a message. Returns an attachment ID to pass to ${sendCmd}. Videos are downloadable attachments and are not parsed by agents; large PDFs may need to be downloaded and inspected in smaller chunks.
|
|
1805
|
-
14. **\`${t("view_file")}\`** \u2014 Download an attached file by its attachment ID so you can inspect it locally.
|
|
1806
|
-
15. **${scheduleReminderCmd}** \u2014 Schedule a reminder for yourself later, at a specific time, or on a recurring cadence.
|
|
1807
|
-
16. **${listRemindersCmd}** \u2014 List your reminders.
|
|
1808
|
-
17. **${cancelReminderCmd}** \u2014 Cancel one of your reminders by ID.`;
|
|
1809
|
-
const credentialHygieneSection = isCli ? cliGuideSections.credentialHygiene : `### Credential hygiene
|
|
1810
|
-
|
|
1811
|
-
**Never paste credentials into public Slock channels, public-channel threads, or public-channel task/attachment fields.** Agent tokens (\`sk_agent_*\`), legacy machine API keys (\`sk_machine_*\`), session bearers, JWTs, \`.env\` files, or \`credential.json\` contents must not appear in public channel chat. DMs and private channels are allowed for authorized secret handoff, but verify the audience first. If you accidentally paste one into a public channel, immediately tell the credential owner so they can rotate it.
|
|
1812
|
-
|
|
1813
|
-
If a tool or error output contains credential-shaped strings, redact them to \`sk_agent_<redacted>\` / \`sk_machine_<redacted>\` shape before posting to a public channel.`;
|
|
1814
|
-
const reminderSection = isCli ? cliGuideSections.reminders : `### Reminders
|
|
1815
|
-
|
|
1816
|
-
Use reminders for follow-up that depends on future state you cannot resolve now, whether user-requested or self-driven. A reminder is an author-owned, persistent, observable, snoozable, updatable, and cancelable wake-up signal anchored to a Slock message or thread; when it fires, it wakes the author who scheduled it, not other people. If anchored to a message or thread, the receipt/fire system message is visible in that surface, but wake ownership does not transfer. To notify another human or agent later, schedule your own reminder and then @mention them when it fires. Use reminders instead of keeping the current turn alive with a long sleep or relying on MEMORY to wake you. If you expect the wait to finish within about 1 minute, you may briefly poll, but say so in the relevant thread first.
|
|
1817
|
-
When a reminder already exists, prefer \`slock reminder snooze\` to push it later, \`slock reminder update\` to change its meaning or schedule, and \`slock reminder cancel\` only when it is truly no longer needed.
|
|
1818
|
-
Use ${scheduleReminderCmd} rather than runtime-native wake or cron tools such as ScheduleWakeup or CronCreate for user-visible reminders, so reminders stay author-owned, persistent, observable, snoozable, updatable, and cancelable in Slock.
|
|
1819
|
-
Create agent reminders only after resolving the anchor message from the current conversation and passing its msgId explicitly; if no anchor can be resolved, consider posting a status update in the relevant thread so the intent is visible, then revisit when context is available.`;
|
|
1820
|
-
const sendingMessagesSection = isCli ? cliGuideSections.sendingMessages : `### Sending messages
|
|
1821
|
-
|
|
1822
|
-
- **Reply to a channel**: \`${t("send_message")}(target="#channel-name", content="...")\`
|
|
1823
|
-
- **Reply to a DM**: \`${t("send_message")}(target="dm:@peer-name", content="...")\`
|
|
1824
|
-
- **Reply in a thread**: \`${t("send_message")}(target="#channel:shortid", content="...")\` or \`${t("send_message")}(target="dm:@peer:shortid", content="...")\`
|
|
1825
|
-
- **Start a NEW DM**: \`${t("send_message")}(target="dm:@person-name", content="...")\`
|
|
1826
|
-
|
|
1827
|
-
**IMPORTANT**: To reply to any message, always reuse the exact \`target\` from the received message. This ensures your reply goes to the right place \u2014 whether it's a channel, DM, or thread.`;
|
|
1828
|
-
const threadsSection = isCli ? cliGuideSections.threads : `### Threads
|
|
1829
|
-
|
|
1830
|
-
Threads are sub-conversations attached to a specific message. They let you discuss a topic without cluttering the main channel.
|
|
1831
|
-
|
|
1832
|
-
- **Thread targets** have a colon and short ID suffix: \`#general:00000000\` (thread in #general) or \`dm:@richard:11111111\` (thread in a DM).
|
|
1833
|
-
- When you receive a message from a thread (the target has a \`:shortid\` suffix), **always reply using that same target** to keep the conversation in the thread.
|
|
1834
|
-
- **Start a new thread**: Use the \`msg=\` field from the header as the thread suffix. For example, if you see \`[target=#general msg=00000000 ...]\`, call \`${t("send_message")}(target="#general:00000000", content="...")\`. The thread will be auto-created if it doesn't exist yet. Example IDs like \`00000000\` are placeholders; real message IDs come from received messages.
|
|
1835
|
-
- When you send a message, the response includes the message ID. You can use it to start a thread on your own message.
|
|
1836
|
-
- You can read thread history via ${readCmd} with the same thread target.
|
|
1837
|
-
- Threads cannot be nested \u2014 you cannot start a thread inside a thread.`;
|
|
1838
|
-
const discoverySection = isCli ? cliGuideSections.discovery : `### Discovering people and channels
|
|
1839
|
-
|
|
1840
|
-
Call ${serverInfoCmd} to see all channels in this server, which ones you have joined, other agents, and humans.
|
|
1841
|
-
Visible public channels may appear even when \`joined=false\`. In that state you can still inspect them with ${readCmd}, but you cannot send messages there or receive ordinary channel delivery until you join with \`${t("join_channel")}\`. Private channels require a human with access to add you. To leave a regular channel you have joined, use \`${t("leave_channel")}\`.
|
|
1842
|
-
Private channels are membership-gated. If ${serverInfoCmd} shows a channel as private, treat its name, members, and content as private to that channel; do not disclose that information in other channels, DMs, summaries, or task reports unless a human explicitly asks within an authorized context. In channel member listings, human role labels such as owner/admin show server-level authority; no role label means ordinary member.`;
|
|
1843
|
-
const channelAwarenessSection = isCli ? cliGuideSections.channelAwareness : `### Channel awareness
|
|
1844
|
-
|
|
1845
|
-
Each channel has a **name** and optionally a **description** that define its purpose (visible via ${serverInfoCmd}). Respect them:
|
|
1846
|
-
- **Reply in context** \u2014 always respond in the channel/thread the message came from.
|
|
1847
|
-
- **Stay on topic** \u2014 when proactively sharing results or updates, post in the channel most relevant to the work. Don't scatter messages across unrelated channels.
|
|
1848
|
-
- If unsure where something belongs, call ${serverInfoCmd} to review channel descriptions.`;
|
|
1849
|
-
const thirdPartyIntegrationsSection = isCli ? `### Third-party integrations
|
|
1850
|
-
|
|
1851
|
-
If a built-in Slock app or registered third-party service requires login, use Slock Agent Login through the CLI instead of asking the human to copy tokens or complete human OAuth for you. If a human asks you to sign into, open, use, or fetch identity from a third-party app or built-in Slock app, first run \`slock integration list\` and match the app to a listed service before browsing the app. Use \`slock integration login --service <service>\` to provision or reuse your agent login for that service. If the service exposes an agent behavior manifest and you need to run its local CLI, run \`slock integration env --service <service>\` before invoking that CLI; if it prints exports, apply them first so service credentials stay under a per-agent profile HOME/XDG tree instead of the host user's global HOME. If it reports that no local env is required, do not invent HOME/XDG overrides. If it fails, do not run that local CLI with the host user's HOME; report that the service manifest is unsupported. Slock does not execute commands from remote manifests automatically. If the CLI reports that the \`integration\` command is unknown, the local daemon/CLI is too old for Slock Agent Login; report that the machine must be upgraded/restarted instead of calling internal HTTP endpoints yourself. When the command returns \`Agent login ready\` or \`Already logged in\`, the agent-side login is ready. If the output includes an app URL, open that URL as the service-provided app surface; it should look like the service's normal Login with Slock callback and not require you to understand Slock's internal grant/request protocol. Do not crawl third-party routes looking for a session before trying the registered-service login path. Do not open the human \`Login with Slock\` browser flow, use internal request IDs as OAuth callback codes, call internal Slock integration endpoints directly, or call third-party exchange endpoints unless a human explicitly asks you to debug that server-to-server protocol. If the service or human asks for your Slock Agent identity card, use \`slock profile show\`. Third-party pages may show \`Login with Slock\`; for agent-facing access, prefer the listed service / Slock Agent Login path.` : `### Third-party integrations
|
|
1852
|
-
|
|
1853
|
-
If a built-in Slock app or registered third-party service requires login, use Slock Agent Login through the available registered-service interface instead of asking the human to copy tokens or complete human OAuth for you. If a human asks you to sign into, open, use, or fetch identity from a third-party app or built-in Slock app, first inspect the registered-service interface and match the app to a listed service before browsing the app. Once the registered-service interface reports the agent login is ready, the agent-side login is ready. If that interface provides an app URL, use it as the service-provided app surface; it should look like the service's normal Login with Slock callback and not require you to understand Slock's internal grant/request protocol. Do not crawl third-party routes looking for a session before trying the registered-service login path. Do not open the human \`Login with Slock\` browser flow or treat internal request IDs as OAuth callback codes unless a human explicitly asks you to debug that server-to-server protocol. If the service or human asks for your Slock Agent identity card, use your Slock profile view. Third-party pages may show \`Login with Slock\`; for agent-facing access, prefer the listed service / Slock Agent Login path.`;
|
|
1854
|
-
const readingHistorySection = isCli ? cliGuideSections.readingHistory : `### Reading history
|
|
1855
|
-
|
|
1856
|
-
Use ${readCmd} with the \`channel\` parameter set to \`"#channel-name"\`, \`"dm:@peer-name"\`, or a thread target like \`"#channel:shortid"\`.
|
|
1857
|
-
|
|
1858
|
-
To jump directly to a specific hit with nearby context, pass \`around\` set to a message ID or seq number.`;
|
|
1859
|
-
const historicalReferenceSection = isCli ? cliGuideSections.historicalReferences : `### Historical references
|
|
1860
|
-
|
|
1861
|
-
When a user refers to prior Slock discussion and the relevant context is not already available, first use \`${t("search_messages")}\` and ${readCmd} to find the original thread, decision, or owner before answering. If you find it, summarize the original conclusion with the source thread/message; if you cannot find it, say that explicitly.`;
|
|
1862
|
-
const tasksSection = isCli ? cliGuideSections.tasks : `### Tasks
|
|
1863
|
-
|
|
1864
|
-
When someone sends a message that asks you to do something \u2014 fix a bug, write code, review a PR, deploy, investigate an issue \u2014 that is work. Claim it before you start.
|
|
1865
|
-
|
|
1866
|
-
**Decision rule:** if fulfilling a message requires you to take action beyond just replying (running tools, writing code, making changes), claim the message first. If you're only answering a question or having a conversation, no claim needed.
|
|
1867
|
-
|
|
1868
|
-
**What you see in messages:**
|
|
1869
|
-
- A message already marked as a task: \`@Alice: Fix the login bug [task #3 status=in_progress]\`
|
|
1870
|
-
- A regular message (no task suffix): \`@Alice: Can someone look into the login bug?\`
|
|
1871
|
-
- A system notification about task changes: \`\u{1F4CB} Alice converted a message to task #3 "Fix the login bug"\`
|
|
1872
|
-
|
|
1873
|
-
Only top-level channel / DM messages can become tasks. Messages inside threads are discussion context \u2014 reply there, but keep claims and conversions to top-level messages.
|
|
1874
|
-
|
|
1875
|
-
${readCmd} shows messages in their current state. If a message was later converted to a task, it will show the \`[task #N ...]\` suffix.
|
|
1876
|
-
|
|
1877
|
-
**Status flow:** \`todo\` \u2192 \`in_progress\` \u2192 \`in_review\` \u2192 \`done\`
|
|
1878
|
-
|
|
1879
|
-
**Assignee** is independent from status \u2014 a task can be claimed or unclaimed at any status except \`done\`.
|
|
1880
|
-
|
|
1881
|
-
**Workflow:**
|
|
1882
|
-
1. Receive a message that requires action \u2192 claim it first (by task number if already a task, or by message ID if it's a regular message)
|
|
1883
|
-
2. If the claim fails, someone else is working on it \u2014 move on to another task
|
|
1884
|
-
3. Post updates in the task's thread via ${sendCmd} with \`target="#channel:msgShortId"\`
|
|
1885
|
-
4. When done, set status to \`in_review\` so a human can validate via ${taskUpdateCmd}
|
|
1886
|
-
5. After approval (e.g. "looks good", "merge it"), set status to \`done\`
|
|
1887
|
-
|
|
1888
|
-
**What ${taskCreateCmd} really means:**
|
|
1889
|
-
- Tasks live in the same chat flow as messages. A task is just a message with task metadata, not a separate source of truth.
|
|
1890
|
-
- ${taskCreateCmd} is a convenience helper for a specific sequence: create a brand-new message, then publish that new message as a task-message.
|
|
1891
|
-
- ${taskCreateCmd} only creates the task \u2014 to own it, call ${taskClaimCmd} afterward.
|
|
1892
|
-
- Typical uses for ${taskCreateCmd} are breaking down a larger task into parallel subtasks, or batch-creating genuinely new work for others to claim.
|
|
1893
|
-
- If someone already sent the work item as a message, just claim that existing message/task instead of creating a new one.
|
|
1894
|
-
- If the work already exists as a message, reuse it via ${taskClaimCmd} with the message ID.
|
|
1895
|
-
|
|
1896
|
-
**Creating new tasks:**
|
|
1897
|
-
- The task system exists to prevent duplicate work. If you see an existing task for the work, either claim that task or leave it alone.
|
|
1898
|
-
- If a message already shows a \`[task #N ...]\` suffix, claim \`#N\` if it is yours to take; otherwise move on.
|
|
1899
|
-
- Before calling ${taskCreateCmd}, first check whether the work already exists on the task board or is already being handled.
|
|
1900
|
-
- Reuse existing tasks and threads instead of creating duplicates.
|
|
1901
|
-
- Use ${taskCreateCmd} only for genuinely new subtasks or follow-up work that does not already have a canonical task.`;
|
|
1902
|
+
const communicationSection = cliGuideSections.communication;
|
|
1903
|
+
const credentialHygieneSection = cliGuideSections.credentialHygiene;
|
|
1904
|
+
const reminderSection = cliGuideSections.reminders;
|
|
1905
|
+
const sendingMessagesSection = cliGuideSections.sendingMessages;
|
|
1906
|
+
const threadsSection = cliGuideSections.threads;
|
|
1907
|
+
const discoverySection = cliGuideSections.discovery;
|
|
1908
|
+
const channelAwarenessSection = cliGuideSections.channelAwareness;
|
|
1909
|
+
const thirdPartyIntegrationsSection = `### Third-party integrations
|
|
1910
|
+
|
|
1911
|
+
If a built-in Slock app or registered third-party service requires login, use Slock Agent Login through the CLI instead of asking the human to copy tokens or complete human OAuth for you. If a human asks you to sign into, open, use, or fetch identity from a third-party app or built-in Slock app, first run \`slock integration list\` and match the app to a listed service before browsing the app. Use \`slock integration login --service <service>\` to provision or reuse your agent login for that service. If the service exposes an agent behavior manifest and you need to run its local CLI, run \`slock integration env --service <service>\` before invoking that CLI; if it prints exports, apply them first so service credentials stay under a per-agent profile HOME/XDG tree instead of the host user's global HOME. If it reports that no local env is required, do not invent HOME/XDG overrides. If it fails, do not run that local CLI with the host user's HOME; report that the service manifest is unsupported. Slock does not execute commands from remote manifests automatically. If the CLI reports that the \`integration\` command is unknown, the local daemon/CLI is too old for Slock Agent Login; report that the machine must be upgraded/restarted instead of calling internal HTTP endpoints yourself. When the command returns \`Agent login ready\` or \`Already logged in\`, the agent-side login is ready. If the output includes an app URL, open that URL as the service-provided app surface; it should look like the service's normal Login with Slock callback and not require you to understand Slock's internal grant/request protocol. Do not crawl third-party routes looking for a session before trying the registered-service login path. Do not open the human \`Login with Slock\` browser flow, use internal request IDs as OAuth callback codes, call internal Slock integration endpoints directly, or call third-party exchange endpoints unless a human explicitly asks you to debug that server-to-server protocol. If the service or human asks for your Slock Agent identity card, use \`slock profile show\`. Third-party pages may show \`Login with Slock\`; for agent-facing access, prefer the listed service / Slock Agent Login path.`;
|
|
1912
|
+
const readingHistorySection = cliGuideSections.readingHistory;
|
|
1913
|
+
const historicalReferenceSection = cliGuideSections.historicalReferences;
|
|
1914
|
+
const tasksSection = cliGuideSections.tasks;
|
|
1902
1915
|
let prompt = `You are "${config.displayName || config.name}", an AI agent in Slock \u2014 a collaborative platform for human-AI collaboration, serving as a shared message service for humans and agents who may be running on different computers.
|
|
1903
1916
|
|
|
1904
1917
|
## Who you are
|
|
@@ -2012,10 +2025,11 @@ While you are working, the daemon may write a batched, content-free inbox update
|
|
|
2012
2025
|
|
|
2013
2026
|
How to handle these:
|
|
2014
2027
|
- 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.
|
|
2015
|
-
-
|
|
2028
|
+
- If an inbox notice is the only input that woke this turn, call ${checkCmd} to read the pending message content before deciding there is no action to take.
|
|
2029
|
+
- Keep working until a natural breakpoint. If you then choose to inspect pending targets, call ${inboxCheckCmd}; use ${checkCmd} / \`slock message read\` when you choose to inspect message content.
|
|
2016
2030
|
- If a message you explicitly read is higher priority, pivot to it. If not, continue your current work.`;
|
|
2017
2031
|
} else {
|
|
2018
|
-
const notifyExample =
|
|
2032
|
+
const notifyExample = `\`[Slock inbox notice:\\nInbox update: N unread messages total; M changed targets\\n#channel pending: K messages ...]\``;
|
|
2019
2033
|
prompt += `
|
|
2020
2034
|
|
|
2021
2035
|
## Message Notifications
|
|
@@ -2026,7 +2040,8 @@ ${notifyExample}
|
|
|
2026
2040
|
|
|
2027
2041
|
How to handle these:
|
|
2028
2042
|
- 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.
|
|
2029
|
-
-
|
|
2043
|
+
- If an inbox notice is the only input that woke this turn, call ${checkCmd} to read the pending message content before deciding there is no action to take.
|
|
2044
|
+
- 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} / \`slock message read\` when you choose to inspect message content.
|
|
2030
2045
|
- If a message you explicitly read is higher priority, you may pivot to it. If not, continue your current work.`;
|
|
2031
2046
|
}
|
|
2032
2047
|
}
|
|
@@ -2039,7 +2054,7 @@ ${config.description}. This may evolve.`;
|
|
|
2039
2054
|
return prompt;
|
|
2040
2055
|
}
|
|
2041
2056
|
function buildCliSystemPrompt(config, opts) {
|
|
2042
|
-
return buildPrompt(config,
|
|
2057
|
+
return buildPrompt(config, opts);
|
|
2043
2058
|
}
|
|
2044
2059
|
|
|
2045
2060
|
// src/slockHome.ts
|
|
@@ -2081,6 +2096,19 @@ function listLegacySlockStatePaths(slockHome = resolveSlockHome(), homeDir = os.
|
|
|
2081
2096
|
return candidates.filter((candidate) => existsSync(candidate.path));
|
|
2082
2097
|
}
|
|
2083
2098
|
|
|
2099
|
+
// src/authEnv.ts
|
|
2100
|
+
var DAEMON_API_KEY_ENV = "SLOCK_MACHINE_API_KEY";
|
|
2101
|
+
var SLOCK_AGENT_TOKEN_ENV = "SLOCK_AGENT_TOKEN";
|
|
2102
|
+
function scrubDaemonAuthEnv(env) {
|
|
2103
|
+
delete env[DAEMON_API_KEY_ENV];
|
|
2104
|
+
return env;
|
|
2105
|
+
}
|
|
2106
|
+
function scrubDaemonChildEnv(env) {
|
|
2107
|
+
delete env[DAEMON_API_KEY_ENV];
|
|
2108
|
+
delete env[SLOCK_AGENT_TOKEN_ENV];
|
|
2109
|
+
return env;
|
|
2110
|
+
}
|
|
2111
|
+
|
|
2084
2112
|
// src/agentCredentialProxy.ts
|
|
2085
2113
|
import { randomBytes } from "crypto";
|
|
2086
2114
|
import http from "http";
|
|
@@ -3567,7 +3595,9 @@ var LOOPBACK_NO_PROXY = "127.0.0.1,localhost";
|
|
|
3567
3595
|
var CLI_TRANSPORT_TRACE_DIR_ENV = "SLOCK_CLI_TRANSPORT_TRACE_DIR";
|
|
3568
3596
|
var safePathPart = (value) => value.replace(/[^a-zA-Z0-9_.-]/g, "_");
|
|
3569
3597
|
var RAW_CREDENTIAL_ENV_DENYLIST = [
|
|
3570
|
-
"
|
|
3598
|
+
"SLOCK_AGENT_TOKEN",
|
|
3599
|
+
"SLOCK_AGENT_CREDENTIAL_KEY",
|
|
3600
|
+
"SLOCK_AGENT_CREDENTIAL_KEY_FILE"
|
|
3571
3601
|
];
|
|
3572
3602
|
var cachedOpencliBinPath;
|
|
3573
3603
|
function resolveOpencliBinPath() {
|
|
@@ -3782,7 +3812,7 @@ exec ${shellSingleQuote(process.execPath)} ${shellSingleQuote(opencliBinPath)} "
|
|
|
3782
3812
|
...agentCredentialProxy ? {} : { SLOCK_AGENT_TOKEN_FILE: tokenFile },
|
|
3783
3813
|
PATH: `${slockDir}${path2.delimiter}${process.env.PATH ?? ""}`
|
|
3784
3814
|
};
|
|
3785
|
-
|
|
3815
|
+
scrubDaemonChildEnv(spawnEnv);
|
|
3786
3816
|
for (const key of RAW_CREDENTIAL_ENV_DENYLIST) {
|
|
3787
3817
|
delete spawnEnv[key];
|
|
3788
3818
|
}
|
|
@@ -4211,7 +4241,7 @@ function resolveCommandOnWindows(command, env, execFileSyncFn, existsSyncFn) {
|
|
|
4211
4241
|
}
|
|
4212
4242
|
function resolveCommandOnPath(command, deps = {}) {
|
|
4213
4243
|
const platform = deps.platform ?? process.platform;
|
|
4214
|
-
const env = withWindowsUserEnvironment(deps.env ?? process.env, deps);
|
|
4244
|
+
const env = scrubDaemonChildEnv({ ...withWindowsUserEnvironment(deps.env ?? process.env, deps) });
|
|
4215
4245
|
const execFileSyncFn = deps.execFileSyncFn ?? execFileSync;
|
|
4216
4246
|
const existsSyncFn = deps.existsSyncFn ?? existsSync2;
|
|
4217
4247
|
if (platform === "win32") {
|
|
@@ -4237,7 +4267,7 @@ function firstExistingPath(candidates, deps = {}) {
|
|
|
4237
4267
|
return null;
|
|
4238
4268
|
}
|
|
4239
4269
|
function readCommandVersion(command, args = [], deps = {}) {
|
|
4240
|
-
const env = withWindowsUserEnvironment(deps.env ?? process.env, deps);
|
|
4270
|
+
const env = scrubDaemonChildEnv({ ...withWindowsUserEnvironment(deps.env ?? process.env, deps) });
|
|
4241
4271
|
const execFileSyncFn = deps.execFileSyncFn ?? execFileSync;
|
|
4242
4272
|
try {
|
|
4243
4273
|
const output = normalizeExecOutput(execFileSyncFn(command, [...args, "--version"], {
|
|
@@ -4331,6 +4361,52 @@ function buildClaudeSpawnSpec(claudeCommand, platform = process.platform) {
|
|
|
4331
4361
|
};
|
|
4332
4362
|
}
|
|
4333
4363
|
|
|
4364
|
+
// src/drivers/claudeProviderIsolation.ts
|
|
4365
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2, symlinkSync } from "fs";
|
|
4366
|
+
import os2 from "os";
|
|
4367
|
+
import path5 from "path";
|
|
4368
|
+
function isClaudeCustomProviderConfig(config) {
|
|
4369
|
+
const launchRuntimeFields = runtimeConfigToLaunchFields(config);
|
|
4370
|
+
return launchRuntimeFields.runtime === "claude" && Boolean(launchRuntimeFields.envVars?.ANTHROPIC_BASE_URL) && Boolean(launchRuntimeFields.envVars?.ANTHROPIC_API_KEY);
|
|
4371
|
+
}
|
|
4372
|
+
function getClaudeProviderStatePaths(workingDirectory) {
|
|
4373
|
+
const root = path5.join(workingDirectory, ".slock", "claude-provider");
|
|
4374
|
+
const home = path5.join(root, "home");
|
|
4375
|
+
return {
|
|
4376
|
+
root,
|
|
4377
|
+
home,
|
|
4378
|
+
configDir: path5.join(home, ".claude")
|
|
4379
|
+
};
|
|
4380
|
+
}
|
|
4381
|
+
function linkIfPresent(source, target) {
|
|
4382
|
+
try {
|
|
4383
|
+
if (!existsSync3(source) || existsSync3(target)) return;
|
|
4384
|
+
mkdirSync2(path5.dirname(target), { recursive: true, mode: 448 });
|
|
4385
|
+
symlinkSync(source, target, "dir");
|
|
4386
|
+
} catch {
|
|
4387
|
+
}
|
|
4388
|
+
}
|
|
4389
|
+
function ensureClaudeProviderStatePaths(workingDirectory, hostClaudeHome = path5.join(os2.homedir(), ".claude")) {
|
|
4390
|
+
const paths = getClaudeProviderStatePaths(workingDirectory);
|
|
4391
|
+
mkdirSync2(paths.home, { recursive: true, mode: 448 });
|
|
4392
|
+
mkdirSync2(paths.configDir, { recursive: true, mode: 448 });
|
|
4393
|
+
linkIfPresent(path5.join(hostClaudeHome, "skills"), path5.join(paths.configDir, "skills"));
|
|
4394
|
+
linkIfPresent(path5.join(hostClaudeHome, "commands"), path5.join(paths.configDir, "commands"));
|
|
4395
|
+
return paths;
|
|
4396
|
+
}
|
|
4397
|
+
function buildClaudeProviderIsolationEnv(ctx) {
|
|
4398
|
+
if (!isClaudeCustomProviderConfig(ctx.config)) {
|
|
4399
|
+
return {};
|
|
4400
|
+
}
|
|
4401
|
+
const paths = ensureClaudeProviderStatePaths(ctx.workingDirectory);
|
|
4402
|
+
return {
|
|
4403
|
+
HOME: paths.home,
|
|
4404
|
+
USERPROFILE: paths.home,
|
|
4405
|
+
CLAUDE_CONFIG_DIR: paths.configDir,
|
|
4406
|
+
CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST: "1"
|
|
4407
|
+
};
|
|
4408
|
+
}
|
|
4409
|
+
|
|
4334
4410
|
// src/drivers/claude.ts
|
|
4335
4411
|
var ClaudeDriver = class {
|
|
4336
4412
|
id = "claude";
|
|
@@ -4351,8 +4427,6 @@ var ClaudeDriver = class {
|
|
|
4351
4427
|
toLaunchSpec: (modelId) => ({ args: ["--model", modelId] })
|
|
4352
4428
|
};
|
|
4353
4429
|
supportsStdinNotification = true;
|
|
4354
|
-
mcpToolPrefix = "";
|
|
4355
|
-
usesSlockCliForCommunication = true;
|
|
4356
4430
|
// Claude Code supports same-turn steering, but raw stdin injection at an
|
|
4357
4431
|
// arbitrary busy instant can collide with active signed thinking blocks. The
|
|
4358
4432
|
// daemon therefore gates busy delivery on Claude stream-json boundaries.
|
|
@@ -4366,7 +4440,10 @@ var ClaudeDriver = class {
|
|
|
4366
4440
|
return buildClaudeArgs(config, opts);
|
|
4367
4441
|
}
|
|
4368
4442
|
async spawn(ctx) {
|
|
4369
|
-
const { slockDir, tokenFile, spawnEnv } = await prepareCliTransport(
|
|
4443
|
+
const { slockDir, tokenFile, spawnEnv } = await prepareCliTransport(
|
|
4444
|
+
ctx,
|
|
4445
|
+
buildClaudeProviderIsolationEnv(ctx)
|
|
4446
|
+
);
|
|
4370
4447
|
const systemPromptPath = writeClaudeSystemPromptFile(ctx.standingPrompt, slockDir);
|
|
4371
4448
|
const args = this.buildClaudeArgs(ctx.config, {
|
|
4372
4449
|
standingPromptFilePath: systemPromptPath
|
|
@@ -4412,7 +4489,6 @@ var ClaudeDriver = class {
|
|
|
4412
4489
|
}
|
|
4413
4490
|
buildSystemPrompt(config, _agentId) {
|
|
4414
4491
|
return buildCliTransportSystemPrompt(config, {
|
|
4415
|
-
toolPrefix: "",
|
|
4416
4492
|
extraCriticalRules: [],
|
|
4417
4493
|
postStartupNotes: [
|
|
4418
4494
|
"**Claude runtime note:** While you are busy, Slock batches inbox-count notifications instead of injecting message content. Use `slock message check` at natural breakpoints to pull the pending messages before side-effect actions that depend on current context."
|
|
@@ -4425,9 +4501,9 @@ var ClaudeDriver = class {
|
|
|
4425
4501
|
|
|
4426
4502
|
// src/drivers/codex.ts
|
|
4427
4503
|
import { spawn as spawn2, execFileSync as execFileSync2, execSync } from "child_process";
|
|
4428
|
-
import { existsSync as
|
|
4429
|
-
import
|
|
4430
|
-
import
|
|
4504
|
+
import { existsSync as existsSync4, readFileSync as readFileSync2 } from "fs";
|
|
4505
|
+
import os3 from "os";
|
|
4506
|
+
import path6 from "path";
|
|
4431
4507
|
|
|
4432
4508
|
// src/runtimeTurnState.ts
|
|
4433
4509
|
var RuntimeTurnState = class {
|
|
@@ -4584,16 +4660,17 @@ function rawResponseItemProgressEvent(message) {
|
|
|
4584
4660
|
function nonEmptyString2(value) {
|
|
4585
4661
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
4586
4662
|
}
|
|
4663
|
+
function codexMcpToolName(item) {
|
|
4664
|
+
const tool = nonEmptyString2(item.tool) ?? "unknown";
|
|
4665
|
+
const server = nonEmptyString2(item.server);
|
|
4666
|
+
return server ? `mcp_${server}_${tool}` : `mcp_${tool}`;
|
|
4667
|
+
}
|
|
4587
4668
|
var CodexEventNormalizer = class {
|
|
4588
|
-
mcpToolPrefix;
|
|
4589
4669
|
currentThreadId = null;
|
|
4590
4670
|
sessionAnnounced = false;
|
|
4591
4671
|
streamedAgentMessageIds = /* @__PURE__ */ new Set();
|
|
4592
4672
|
streamedReasoningIds = /* @__PURE__ */ new Set();
|
|
4593
4673
|
turnState = new RuntimeTurnState();
|
|
4594
|
-
constructor(opts) {
|
|
4595
|
-
this.mcpToolPrefix = opts.mcpToolPrefix;
|
|
4596
|
-
}
|
|
4597
4674
|
reset(opts = {}) {
|
|
4598
4675
|
this.currentThreadId = opts.threadId ?? null;
|
|
4599
4676
|
this.turnState.reset();
|
|
@@ -4753,11 +4830,11 @@ var CodexEventNormalizer = class {
|
|
|
4753
4830
|
break;
|
|
4754
4831
|
case "mcpToolCall":
|
|
4755
4832
|
if (isStarted) {
|
|
4756
|
-
const toolName = item
|
|
4833
|
+
const toolName = codexMcpToolName(item);
|
|
4757
4834
|
events.push({ kind: "tool_call", name: toolName, input: item.arguments });
|
|
4758
4835
|
}
|
|
4759
4836
|
if (isCompleted) {
|
|
4760
|
-
const toolName = item
|
|
4837
|
+
const toolName = codexMcpToolName(item);
|
|
4761
4838
|
events.push({ kind: "tool_output", name: toolName });
|
|
4762
4839
|
this.turnState.markToolBoundary();
|
|
4763
4840
|
}
|
|
@@ -4813,9 +4890,9 @@ var CodexEventNormalizer = class {
|
|
|
4813
4890
|
|
|
4814
4891
|
// src/drivers/codex.ts
|
|
4815
4892
|
function ensureGitRepoForCodex(workingDirectory, deps = {}) {
|
|
4816
|
-
const existsSyncFn = deps.existsSyncFn ??
|
|
4893
|
+
const existsSyncFn = deps.existsSyncFn ?? existsSync4;
|
|
4817
4894
|
const execSyncFn = deps.execSyncFn ?? execSync;
|
|
4818
|
-
const gitDir =
|
|
4895
|
+
const gitDir = path6.join(workingDirectory, ".git");
|
|
4819
4896
|
if (existsSyncFn(gitDir)) return;
|
|
4820
4897
|
execSyncFn("git init", { cwd: workingDirectory, stdio: "pipe" });
|
|
4821
4898
|
execSyncFn(
|
|
@@ -4828,13 +4905,13 @@ function ensureGitRepoForCodex(workingDirectory, deps = {}) {
|
|
|
4828
4905
|
}
|
|
4829
4906
|
var CODEX_DESKTOP_BUNDLE_PATH = "/Applications/Codex.app/Contents/Resources/codex";
|
|
4830
4907
|
function isWindowsSandboxRunner(commandPath) {
|
|
4831
|
-
return
|
|
4908
|
+
return path6.basename(commandPath).toLowerCase().startsWith("codex-command-runner");
|
|
4832
4909
|
}
|
|
4833
4910
|
function resolveWindowsNpmCodexEntry(deps = {}) {
|
|
4834
|
-
const existsSyncFn = deps.existsSyncFn ??
|
|
4911
|
+
const existsSyncFn = deps.existsSyncFn ?? existsSync4;
|
|
4835
4912
|
const execFileSyncFn = deps.execFileSyncFn ?? execFileSync2;
|
|
4836
4913
|
const env = deps.env ?? process.env;
|
|
4837
|
-
const winPath =
|
|
4914
|
+
const winPath = path6.win32;
|
|
4838
4915
|
try {
|
|
4839
4916
|
const globalRoot = String(execFileSyncFn("npm", ["root", "-g"], {
|
|
4840
4917
|
encoding: "utf8",
|
|
@@ -4853,10 +4930,10 @@ function resolveWindowsNpmCodexEntry(deps = {}) {
|
|
|
4853
4930
|
return null;
|
|
4854
4931
|
}
|
|
4855
4932
|
function resolveWindowsCodexDesktopEntry(deps = {}) {
|
|
4856
|
-
const existsSyncFn = deps.existsSyncFn ??
|
|
4933
|
+
const existsSyncFn = deps.existsSyncFn ?? existsSync4;
|
|
4857
4934
|
const env = deps.env ?? process.env;
|
|
4858
4935
|
const homeDir = deps.homeDir;
|
|
4859
|
-
const winPath =
|
|
4936
|
+
const winPath = path6.win32;
|
|
4860
4937
|
const candidates = [
|
|
4861
4938
|
env.LOCALAPPDATA ? winPath.join(env.LOCALAPPDATA, "OpenAI", "Codex", "bin", "codex.exe") : null,
|
|
4862
4939
|
env.USERPROFILE ? winPath.join(env.USERPROFILE, "AppData", "Local", "OpenAI", "Codex", "bin", "codex.exe") : null,
|
|
@@ -4944,8 +5021,6 @@ var CodexDriver = class {
|
|
|
4944
5021
|
toLaunchSpec: (modelId) => ({ params: { model: modelId } })
|
|
4945
5022
|
};
|
|
4946
5023
|
supportsStdinNotification = true;
|
|
4947
|
-
mcpToolPrefix = "mcp_chat_";
|
|
4948
|
-
usesSlockCliForCommunication = true;
|
|
4949
5024
|
busyDeliveryMode = "direct";
|
|
4950
5025
|
supportsNativeStandingPrompt = true;
|
|
4951
5026
|
probe() {
|
|
@@ -4992,7 +5067,7 @@ var CodexDriver = class {
|
|
|
4992
5067
|
initializeRequestId = null;
|
|
4993
5068
|
pendingThreadRequest = null;
|
|
4994
5069
|
initialTurnStarted = false;
|
|
4995
|
-
normalizer = new CodexEventNormalizer(
|
|
5070
|
+
normalizer = new CodexEventNormalizer();
|
|
4996
5071
|
async spawn(ctx) {
|
|
4997
5072
|
ensureGitRepoForCodex(ctx.workingDirectory);
|
|
4998
5073
|
const { spawnEnv } = await prepareCliTransport(ctx, { NO_COLOR: "1" });
|
|
@@ -5084,7 +5159,6 @@ var CodexDriver = class {
|
|
|
5084
5159
|
}
|
|
5085
5160
|
buildSystemPrompt(config, _agentId) {
|
|
5086
5161
|
return buildCliTransportSystemPrompt(config, {
|
|
5087
|
-
toolPrefix: "",
|
|
5088
5162
|
extraCriticalRules: [],
|
|
5089
5163
|
postStartupNotes: [
|
|
5090
5164
|
"**IMPORTANT**: Your process stays alive across turns. While you are working, Slock may write batched inbox-count notifications into the current turn; call `slock message check` at natural breakpoints to read the pending messages."
|
|
@@ -5128,9 +5202,9 @@ var CodexDriver = class {
|
|
|
5128
5202
|
return detectCodexModels();
|
|
5129
5203
|
}
|
|
5130
5204
|
};
|
|
5131
|
-
function detectCodexModels(home =
|
|
5132
|
-
const cachePath =
|
|
5133
|
-
const configPath =
|
|
5205
|
+
function detectCodexModels(home = os3.homedir()) {
|
|
5206
|
+
const cachePath = path6.join(home, ".codex", "models_cache.json");
|
|
5207
|
+
const configPath = path6.join(home, ".codex", "config.toml");
|
|
5134
5208
|
let models = [];
|
|
5135
5209
|
try {
|
|
5136
5210
|
const raw = readFileSync2(cachePath, "utf8");
|
|
@@ -5224,9 +5298,7 @@ var AntigravityDriver = class {
|
|
|
5224
5298
|
toLaunchSpec: (_modelId) => ({ args: [] })
|
|
5225
5299
|
};
|
|
5226
5300
|
supportsStdinNotification = false;
|
|
5227
|
-
mcpToolPrefix = "";
|
|
5228
5301
|
busyDeliveryMode = "none";
|
|
5229
|
-
usesSlockCliForCommunication = true;
|
|
5230
5302
|
sessionId = null;
|
|
5231
5303
|
sessionAnnounced = false;
|
|
5232
5304
|
probe() {
|
|
@@ -5274,7 +5346,6 @@ var AntigravityDriver = class {
|
|
|
5274
5346
|
}
|
|
5275
5347
|
buildSystemPrompt(config, _agentId) {
|
|
5276
5348
|
return buildCliTransportSystemPrompt(config, {
|
|
5277
|
-
toolPrefix: "",
|
|
5278
5349
|
extraCriticalRules: [],
|
|
5279
5350
|
postStartupNotes: [
|
|
5280
5351
|
"**Antigravity runtime note:** Slock launches you as a per-turn process. Complete the current wake using `slock` CLI commands, then stop; the daemon will restart you when new messages arrive."
|
|
@@ -5313,9 +5384,7 @@ var CopilotDriver = class {
|
|
|
5313
5384
|
toLaunchSpec: (modelId) => ({ args: ["--model", modelId] })
|
|
5314
5385
|
};
|
|
5315
5386
|
supportsStdinNotification = false;
|
|
5316
|
-
mcpToolPrefix = "";
|
|
5317
5387
|
busyDeliveryMode = "none";
|
|
5318
|
-
usesSlockCliForCommunication = true;
|
|
5319
5388
|
sessionId = null;
|
|
5320
5389
|
sessionAnnounced = false;
|
|
5321
5390
|
async spawn(ctx) {
|
|
@@ -5421,7 +5490,6 @@ var CopilotDriver = class {
|
|
|
5421
5490
|
}
|
|
5422
5491
|
buildSystemPrompt(config, _agentId) {
|
|
5423
5492
|
return buildCliTransportSystemPrompt(config, {
|
|
5424
|
-
toolPrefix: "",
|
|
5425
5493
|
extraCriticalRules: [],
|
|
5426
5494
|
postStartupNotes: [
|
|
5427
5495
|
"**Copilot runtime note:** Slock launches you as a per-turn process. Complete the current wake using `slock` CLI commands, then stop; the daemon will restart you when new messages arrive."
|
|
@@ -5458,9 +5526,7 @@ var CursorDriver = class {
|
|
|
5458
5526
|
toLaunchSpec: (modelId) => ({ args: ["--model", modelId] })
|
|
5459
5527
|
};
|
|
5460
5528
|
supportsStdinNotification = false;
|
|
5461
|
-
mcpToolPrefix = "mcp__chat__";
|
|
5462
5529
|
busyDeliveryMode = "none";
|
|
5463
|
-
usesSlockCliForCommunication = true;
|
|
5464
5530
|
async spawn(ctx) {
|
|
5465
5531
|
const args = [
|
|
5466
5532
|
"--print",
|
|
@@ -5546,7 +5612,6 @@ var CursorDriver = class {
|
|
|
5546
5612
|
}
|
|
5547
5613
|
buildSystemPrompt(config, _agentId) {
|
|
5548
5614
|
return buildCliTransportSystemPrompt(config, {
|
|
5549
|
-
toolPrefix: "mcp__chat__",
|
|
5550
5615
|
extraCriticalRules: [],
|
|
5551
5616
|
postStartupNotes: [
|
|
5552
5617
|
"**Cursor runtime note:** Slock launches you as a per-turn process. Complete the current wake using `slock` CLI commands, then stop; the daemon will restart you when new messages arrive."
|
|
@@ -5591,11 +5656,11 @@ function detectCursorModels(runCommand = runCursorModelsCommand) {
|
|
|
5591
5656
|
return parseCursorModelsOutput(String(result.stdout || ""));
|
|
5592
5657
|
}
|
|
5593
5658
|
function buildCursorModelProbeEnv(deps = {}) {
|
|
5594
|
-
return withWindowsUserEnvironment({
|
|
5659
|
+
return scrubDaemonChildEnv(withWindowsUserEnvironment({
|
|
5595
5660
|
...deps.env ?? process.env,
|
|
5596
5661
|
FORCE_COLOR: "0",
|
|
5597
5662
|
NO_COLOR: "1"
|
|
5598
|
-
}, deps);
|
|
5663
|
+
}, deps));
|
|
5599
5664
|
}
|
|
5600
5665
|
function runCursorModelsCommand() {
|
|
5601
5666
|
return spawnSync("cursor-agent", ["models"], {
|
|
@@ -5607,8 +5672,8 @@ function runCursorModelsCommand() {
|
|
|
5607
5672
|
|
|
5608
5673
|
// src/drivers/gemini.ts
|
|
5609
5674
|
import { execFileSync as execFileSync3, spawn as spawn6 } from "child_process";
|
|
5610
|
-
import { existsSync as
|
|
5611
|
-
import
|
|
5675
|
+
import { existsSync as existsSync5 } from "fs";
|
|
5676
|
+
import path7 from "path";
|
|
5612
5677
|
async function buildGeminiSpawnEnv(ctx, platform = process.platform) {
|
|
5613
5678
|
const { spawnEnv } = await prepareCliTransport(ctx, { NO_COLOR: "1" }, platform);
|
|
5614
5679
|
const launchEnvVars = runtimeConfigToLaunchFields(ctx.config).envVars;
|
|
@@ -5650,9 +5715,9 @@ function resolveGeminiSpawn(commandArgs, deps = {}) {
|
|
|
5650
5715
|
return { command: resolveCommandOnPath("gemini", deps) ?? "gemini", args: commandArgs };
|
|
5651
5716
|
}
|
|
5652
5717
|
const execFileSyncFn = deps.execFileSyncFn ?? execFileSync3;
|
|
5653
|
-
const existsSyncFn = deps.existsSyncFn ??
|
|
5654
|
-
const env = deps.env ?? process.env;
|
|
5655
|
-
const winPath =
|
|
5718
|
+
const existsSyncFn = deps.existsSyncFn ?? existsSync5;
|
|
5719
|
+
const env = scrubDaemonChildEnv({ ...deps.env ?? process.env });
|
|
5720
|
+
const winPath = path7.win32;
|
|
5656
5721
|
let geminiEntry = null;
|
|
5657
5722
|
try {
|
|
5658
5723
|
const globalRoot = normalizeExecOutput2(execFileSyncFn("npm", ["root", "-g"], {
|
|
@@ -5706,9 +5771,7 @@ var GeminiDriver = class {
|
|
|
5706
5771
|
toLaunchSpec: (modelId) => modelId && modelId !== "default" ? { args: ["--model", modelId] } : { args: [] }
|
|
5707
5772
|
};
|
|
5708
5773
|
supportsStdinNotification = false;
|
|
5709
|
-
mcpToolPrefix = "";
|
|
5710
5774
|
busyDeliveryMode = "none";
|
|
5711
|
-
usesSlockCliForCommunication = true;
|
|
5712
5775
|
sessionId = null;
|
|
5713
5776
|
sessionAnnounced = false;
|
|
5714
5777
|
async spawn(ctx) {
|
|
@@ -5777,7 +5840,6 @@ var GeminiDriver = class {
|
|
|
5777
5840
|
}
|
|
5778
5841
|
buildSystemPrompt(config, _agentId) {
|
|
5779
5842
|
return buildCliTransportSystemPrompt(config, {
|
|
5780
|
-
toolPrefix: "",
|
|
5781
5843
|
extraCriticalRules: [],
|
|
5782
5844
|
postStartupNotes: [
|
|
5783
5845
|
"**Gemini runtime note:** Slock launches you as a per-turn process. Complete the current wake using `slock` CLI commands, then stop; the daemon will restart you when new messages arrive."
|
|
@@ -5791,12 +5853,15 @@ var GeminiDriver = class {
|
|
|
5791
5853
|
// src/drivers/kimi.ts
|
|
5792
5854
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
5793
5855
|
import { spawn as spawn7 } from "child_process";
|
|
5794
|
-
import { existsSync as
|
|
5795
|
-
import
|
|
5796
|
-
import
|
|
5856
|
+
import { chmodSync, existsSync as existsSync6, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
5857
|
+
import os4 from "os";
|
|
5858
|
+
import path8 from "path";
|
|
5797
5859
|
var KIMI_WIRE_PROTOCOL_VERSION = "1.3";
|
|
5798
5860
|
var KIMI_SYSTEM_PROMPT_FILE = ".slock-kimi-system.md";
|
|
5799
5861
|
var KIMI_AGENT_FILE = ".slock-kimi-agent.yaml";
|
|
5862
|
+
var KIMI_GENERATED_CONFIG_FILE = ".slock-kimi-config.toml";
|
|
5863
|
+
var SLOCK_KIMI_CONFIG_CONTENT_ENV = "SLOCK_KIMI_CONFIG_CONTENT";
|
|
5864
|
+
var SLOCK_KIMI_CONFIG_FILE_ENV = "SLOCK_KIMI_CONFIG_FILE";
|
|
5800
5865
|
function parseToolArguments(raw) {
|
|
5801
5866
|
if (typeof raw !== "string") return raw;
|
|
5802
5867
|
try {
|
|
@@ -5805,6 +5870,73 @@ function parseToolArguments(raw) {
|
|
|
5805
5870
|
return raw;
|
|
5806
5871
|
}
|
|
5807
5872
|
}
|
|
5873
|
+
function readKimiConfigSource(home = os4.homedir(), env = process.env) {
|
|
5874
|
+
const inlineConfig = env[SLOCK_KIMI_CONFIG_CONTENT_ENV];
|
|
5875
|
+
if (inlineConfig && inlineConfig.trim()) {
|
|
5876
|
+
return {
|
|
5877
|
+
raw: inlineConfig,
|
|
5878
|
+
explicitPath: null,
|
|
5879
|
+
sourcePath: SLOCK_KIMI_CONFIG_CONTENT_ENV
|
|
5880
|
+
};
|
|
5881
|
+
}
|
|
5882
|
+
const explicitPath = env[SLOCK_KIMI_CONFIG_FILE_ENV];
|
|
5883
|
+
const configPath = explicitPath && explicitPath.trim() ? explicitPath : path8.join(home, ".kimi", "config.toml");
|
|
5884
|
+
try {
|
|
5885
|
+
return {
|
|
5886
|
+
raw: readFileSync3(configPath, "utf8"),
|
|
5887
|
+
explicitPath: explicitPath && explicitPath.trim() ? explicitPath : null,
|
|
5888
|
+
sourcePath: configPath
|
|
5889
|
+
};
|
|
5890
|
+
} catch {
|
|
5891
|
+
return {
|
|
5892
|
+
raw: null,
|
|
5893
|
+
explicitPath: explicitPath && explicitPath.trim() ? explicitPath : null,
|
|
5894
|
+
sourcePath: configPath
|
|
5895
|
+
};
|
|
5896
|
+
}
|
|
5897
|
+
}
|
|
5898
|
+
function buildKimiSpawnEnv(env = process.env) {
|
|
5899
|
+
const spawnEnv = { ...env, FORCE_COLOR: "0", NO_COLOR: "1" };
|
|
5900
|
+
delete spawnEnv[SLOCK_KIMI_CONFIG_CONTENT_ENV];
|
|
5901
|
+
delete spawnEnv[SLOCK_KIMI_CONFIG_FILE_ENV];
|
|
5902
|
+
return scrubDaemonChildEnv(spawnEnv);
|
|
5903
|
+
}
|
|
5904
|
+
function buildKimiEffectiveEnv(ctx, overrideEnv) {
|
|
5905
|
+
return {
|
|
5906
|
+
...process.env,
|
|
5907
|
+
...ctx.config.envVars || {},
|
|
5908
|
+
...overrideEnv || {}
|
|
5909
|
+
};
|
|
5910
|
+
}
|
|
5911
|
+
function buildKimiLaunchOptions(ctx, opts = {}) {
|
|
5912
|
+
const env = buildKimiEffectiveEnv(ctx, opts.env);
|
|
5913
|
+
const source = readKimiConfigSource(opts.home ?? os4.homedir(), env);
|
|
5914
|
+
const args = [];
|
|
5915
|
+
let configFilePath = null;
|
|
5916
|
+
let configContent = null;
|
|
5917
|
+
if (source.explicitPath) {
|
|
5918
|
+
configFilePath = source.explicitPath;
|
|
5919
|
+
} else if (source.raw !== null && source.sourcePath === SLOCK_KIMI_CONFIG_CONTENT_ENV) {
|
|
5920
|
+
configFilePath = path8.join(ctx.workingDirectory, KIMI_GENERATED_CONFIG_FILE);
|
|
5921
|
+
configContent = source.raw;
|
|
5922
|
+
if (opts.writeGeneratedConfig !== false) {
|
|
5923
|
+
writeFileSync3(configFilePath, source.raw, { encoding: "utf8", mode: 384 });
|
|
5924
|
+
chmodSync(configFilePath, 384);
|
|
5925
|
+
}
|
|
5926
|
+
}
|
|
5927
|
+
if (configFilePath) {
|
|
5928
|
+
args.push("--config-file", configFilePath);
|
|
5929
|
+
}
|
|
5930
|
+
if (ctx.config.model && ctx.config.model !== "default") {
|
|
5931
|
+
args.push("--model", ctx.config.model);
|
|
5932
|
+
}
|
|
5933
|
+
return {
|
|
5934
|
+
args,
|
|
5935
|
+
env: buildKimiSpawnEnv(env),
|
|
5936
|
+
configFilePath,
|
|
5937
|
+
configContent
|
|
5938
|
+
};
|
|
5939
|
+
}
|
|
5808
5940
|
function resolveKimiSpawn(commandArgs, deps = {}) {
|
|
5809
5941
|
return {
|
|
5810
5942
|
command: resolveCommandOnPath("kimi", deps) ?? "kimi",
|
|
@@ -5828,12 +5960,28 @@ var KimiDriver = class {
|
|
|
5828
5960
|
};
|
|
5829
5961
|
model = {
|
|
5830
5962
|
detectedModelsVerifiedAs: "launchable",
|
|
5831
|
-
toLaunchSpec: (modelId) =>
|
|
5963
|
+
toLaunchSpec: (modelId, ctx, opts) => {
|
|
5964
|
+
if (!ctx) return { args: ["--model", modelId] };
|
|
5965
|
+
const launchCtx = {
|
|
5966
|
+
...ctx,
|
|
5967
|
+
config: {
|
|
5968
|
+
...ctx.config,
|
|
5969
|
+
model: modelId
|
|
5970
|
+
}
|
|
5971
|
+
};
|
|
5972
|
+
const launch = buildKimiLaunchOptions(launchCtx, {
|
|
5973
|
+
home: opts?.home,
|
|
5974
|
+
writeGeneratedConfig: false
|
|
5975
|
+
});
|
|
5976
|
+
return {
|
|
5977
|
+
args: launch.args,
|
|
5978
|
+
env: launch.env,
|
|
5979
|
+
configFiles: launch.configFilePath ? [launch.configFilePath] : void 0
|
|
5980
|
+
};
|
|
5981
|
+
}
|
|
5832
5982
|
};
|
|
5833
5983
|
supportsStdinNotification = true;
|
|
5834
|
-
mcpToolPrefix = "";
|
|
5835
5984
|
busyDeliveryMode = "direct";
|
|
5836
|
-
usesSlockCliForCommunication = true;
|
|
5837
5985
|
sessionId = null;
|
|
5838
5986
|
sessionAnnounced = false;
|
|
5839
5987
|
promptRequestId = null;
|
|
@@ -5842,9 +5990,9 @@ var KimiDriver = class {
|
|
|
5842
5990
|
this.sessionId = ctx.config.sessionId || randomUUID2();
|
|
5843
5991
|
this.sessionAnnounced = false;
|
|
5844
5992
|
this.promptRequestId = randomUUID2();
|
|
5845
|
-
const systemPromptPath =
|
|
5846
|
-
const agentFilePath =
|
|
5847
|
-
if (!isResume || !
|
|
5993
|
+
const systemPromptPath = path8.join(ctx.workingDirectory, KIMI_SYSTEM_PROMPT_FILE);
|
|
5994
|
+
const agentFilePath = path8.join(ctx.workingDirectory, KIMI_AGENT_FILE);
|
|
5995
|
+
if (!isResume || !existsSync6(systemPromptPath)) {
|
|
5848
5996
|
writeFileSync3(systemPromptPath, ctx.prompt, "utf8");
|
|
5849
5997
|
}
|
|
5850
5998
|
writeFileSync3(agentFilePath, [
|
|
@@ -5854,21 +6002,23 @@ var KimiDriver = class {
|
|
|
5854
6002
|
` system_prompt_path: ./${KIMI_SYSTEM_PROMPT_FILE}`,
|
|
5855
6003
|
""
|
|
5856
6004
|
].join("\n"), "utf8");
|
|
6005
|
+
const launch = buildKimiLaunchOptions(ctx);
|
|
5857
6006
|
const args = [
|
|
5858
6007
|
"--wire",
|
|
5859
6008
|
"--yolo",
|
|
5860
6009
|
"--agent-file",
|
|
5861
6010
|
agentFilePath,
|
|
5862
6011
|
"--session",
|
|
5863
|
-
this.sessionId
|
|
6012
|
+
this.sessionId,
|
|
6013
|
+
...launch.args
|
|
5864
6014
|
];
|
|
5865
6015
|
const launchRuntimeFields = runtimeConfigToLaunchFields(ctx.config);
|
|
5866
6016
|
if (launchRuntimeFields.model && launchRuntimeFields.model !== "default") {
|
|
5867
6017
|
args.push("--model", launchRuntimeFields.model);
|
|
5868
6018
|
}
|
|
5869
6019
|
const spawnEnv = (await prepareCliTransport(ctx, { NO_COLOR: "1" })).spawnEnv;
|
|
5870
|
-
const
|
|
5871
|
-
const proc = spawn7(
|
|
6020
|
+
const spawnTarget = resolveKimiSpawn(args);
|
|
6021
|
+
const proc = spawn7(spawnTarget.command, spawnTarget.args, {
|
|
5872
6022
|
cwd: ctx.workingDirectory,
|
|
5873
6023
|
stdio: ["pipe", "pipe", "pipe"],
|
|
5874
6024
|
env: spawnEnv,
|
|
@@ -5876,7 +6026,7 @@ var KimiDriver = class {
|
|
|
5876
6026
|
// and has an 8191-character command-line limit. Kimi's official
|
|
5877
6027
|
// installer/uv entrypoint is an executable, so launch it directly and
|
|
5878
6028
|
// keep prompts on stdin / files instead of routing through cmd.exe.
|
|
5879
|
-
shell:
|
|
6029
|
+
shell: spawnTarget.shell
|
|
5880
6030
|
});
|
|
5881
6031
|
proc.stdin?.write(JSON.stringify({
|
|
5882
6032
|
jsonrpc: "2.0",
|
|
@@ -5979,7 +6129,6 @@ var KimiDriver = class {
|
|
|
5979
6129
|
}
|
|
5980
6130
|
buildSystemPrompt(config, _agentId) {
|
|
5981
6131
|
return buildCliTransportSystemPrompt(config, {
|
|
5982
|
-
toolPrefix: "",
|
|
5983
6132
|
extraCriticalRules: [],
|
|
5984
6133
|
postStartupNotes: [],
|
|
5985
6134
|
includeStdinNotificationSection: true,
|
|
@@ -5990,14 +6139,9 @@ var KimiDriver = class {
|
|
|
5990
6139
|
return detectKimiModels();
|
|
5991
6140
|
}
|
|
5992
6141
|
};
|
|
5993
|
-
function detectKimiModels(home =
|
|
5994
|
-
const
|
|
5995
|
-
|
|
5996
|
-
try {
|
|
5997
|
-
raw = readFileSync3(configPath, "utf8");
|
|
5998
|
-
} catch {
|
|
5999
|
-
return null;
|
|
6000
|
-
}
|
|
6142
|
+
function detectKimiModels(home = os4.homedir(), opts = {}) {
|
|
6143
|
+
const raw = readKimiConfigSource(home, opts.env).raw;
|
|
6144
|
+
if (raw === null) return null;
|
|
6001
6145
|
const models = [];
|
|
6002
6146
|
const sectionRe = /^\s*\[models(?:\.([^\]]+)|"\.[^"]+"|\."[^"]+")\s*\]\s*$/gm;
|
|
6003
6147
|
const lineRe = /^\s*\[models\.(.+?)\s*\]\s*$/gm;
|
|
@@ -6018,11 +6162,9 @@ function detectKimiModels(home = os3.homedir()) {
|
|
|
6018
6162
|
|
|
6019
6163
|
// src/drivers/opencode.ts
|
|
6020
6164
|
import { spawn as spawn8, spawnSync as spawnSync2 } from "child_process";
|
|
6021
|
-
import { existsSync as
|
|
6022
|
-
import
|
|
6023
|
-
import
|
|
6024
|
-
var CHAT_MCP_SERVER_NAME = "chat";
|
|
6025
|
-
var CHAT_MCP_TOOL_PREFIX = `${CHAT_MCP_SERVER_NAME}_`;
|
|
6165
|
+
import { existsSync as existsSync7, readFileSync as readFileSync4 } from "fs";
|
|
6166
|
+
import os5 from "os";
|
|
6167
|
+
import path9 from "path";
|
|
6026
6168
|
var SLOCK_AGENT_NAME = "slock";
|
|
6027
6169
|
var NO_MESSAGE_PROMPT = "No new messages are pending. Stop now.";
|
|
6028
6170
|
var FIRST_MESSAGE_TASK_PREFIX = "First message task (system-triggered):";
|
|
@@ -6050,8 +6192,8 @@ function parseUserOpenCodeConfig(ctx) {
|
|
|
6050
6192
|
const raw = runtimeConfigToLaunchFields(ctx.config).envVars?.OPENCODE_CONFIG_CONTENT;
|
|
6051
6193
|
return parseOpenCodeConfigContent(raw);
|
|
6052
6194
|
}
|
|
6053
|
-
function readLocalOpenCodeConfig(home =
|
|
6054
|
-
const configPath =
|
|
6195
|
+
function readLocalOpenCodeConfig(home = os5.homedir()) {
|
|
6196
|
+
const configPath = path9.join(home, ".config", "opencode", "opencode.json");
|
|
6055
6197
|
try {
|
|
6056
6198
|
return parseOpenCodeConfigContent(readFileSync4(configPath, "utf8"));
|
|
6057
6199
|
} catch {
|
|
@@ -6111,7 +6253,7 @@ function mergeOpenCodeConfigs(localConfig, envConfig) {
|
|
|
6111
6253
|
}
|
|
6112
6254
|
};
|
|
6113
6255
|
}
|
|
6114
|
-
function buildOpenCodeConfig(ctx, home =
|
|
6256
|
+
function buildOpenCodeConfig(ctx, home = os5.homedir()) {
|
|
6115
6257
|
const userConfig = mergeOpenCodeConfigs(readLocalOpenCodeConfig(home), parseUserOpenCodeConfig(ctx));
|
|
6116
6258
|
const userAgents = recordField(userConfig.agent);
|
|
6117
6259
|
const userSlockAgent = recordField(userAgents[SLOCK_AGENT_NAME]);
|
|
@@ -6129,7 +6271,7 @@ function buildOpenCodeConfig(ctx, home = os4.homedir()) {
|
|
|
6129
6271
|
mcp: recordField(userConfig.mcp)
|
|
6130
6272
|
};
|
|
6131
6273
|
}
|
|
6132
|
-
async function buildOpenCodeLaunchOptions(ctx, home =
|
|
6274
|
+
async function buildOpenCodeLaunchOptions(ctx, home = os5.homedir(), version = null) {
|
|
6133
6275
|
const slock = await prepareCliTransport(ctx, { NO_COLOR: "1" });
|
|
6134
6276
|
const config = buildOpenCodeConfig(ctx, home);
|
|
6135
6277
|
const env = {
|
|
@@ -6228,7 +6370,7 @@ function formatOpenCodeLabelToken(token) {
|
|
|
6228
6370
|
if (/^\d/.test(token)) return token;
|
|
6229
6371
|
return normalized.charAt(0).toUpperCase() + normalized.slice(1);
|
|
6230
6372
|
}
|
|
6231
|
-
function detectOpenCodeModels(home =
|
|
6373
|
+
function detectOpenCodeModels(home = os5.homedir(), runCommand = runOpenCodeModelsCommand) {
|
|
6232
6374
|
const commandResult = runCommand(home);
|
|
6233
6375
|
if (commandResult.error || commandResult.status !== 0) return null;
|
|
6234
6376
|
return parseOpenCodeModelsOutput(commandResult.stdout);
|
|
@@ -6237,7 +6379,7 @@ function runOpenCodeModelsCommand(home, deps = {}) {
|
|
|
6237
6379
|
const platform = deps.platform ?? process.platform;
|
|
6238
6380
|
const spawnSyncFn = deps.spawnSyncFn ?? spawnSync2;
|
|
6239
6381
|
const result = spawnSyncFn("opencode", ["models"], {
|
|
6240
|
-
env: { ...process.env, HOME: home, FORCE_COLOR: "0", NO_COLOR: "1" },
|
|
6382
|
+
env: scrubDaemonChildEnv({ ...process.env, HOME: home, FORCE_COLOR: "0", NO_COLOR: "1" }),
|
|
6241
6383
|
encoding: "utf8",
|
|
6242
6384
|
timeout: 5e3,
|
|
6243
6385
|
shell: platform === "win32"
|
|
@@ -6249,11 +6391,11 @@ function runOpenCodeModelsCommand(home, deps = {}) {
|
|
|
6249
6391
|
};
|
|
6250
6392
|
}
|
|
6251
6393
|
function isWindowsCommandShim(commandPath) {
|
|
6252
|
-
const ext =
|
|
6394
|
+
const ext = path9.win32.extname(commandPath).toLowerCase();
|
|
6253
6395
|
return ext === ".cmd" || ext === ".bat";
|
|
6254
6396
|
}
|
|
6255
6397
|
function opencodePackageEntryCandidates(packageRoot) {
|
|
6256
|
-
const winPath =
|
|
6398
|
+
const winPath = path9.win32;
|
|
6257
6399
|
return [
|
|
6258
6400
|
winPath.join(packageRoot, "bin", "opencode.exe"),
|
|
6259
6401
|
winPath.join(packageRoot, "bin", "opencode.js"),
|
|
@@ -6262,16 +6404,16 @@ function opencodePackageEntryCandidates(packageRoot) {
|
|
|
6262
6404
|
];
|
|
6263
6405
|
}
|
|
6264
6406
|
function openCodeSpecForEntry(entry, commandArgs) {
|
|
6265
|
-
if (
|
|
6407
|
+
if (path9.win32.extname(entry).toLowerCase() === ".exe") {
|
|
6266
6408
|
return { command: entry, args: commandArgs, shell: false };
|
|
6267
6409
|
}
|
|
6268
6410
|
return { command: process.execPath, args: [entry, ...commandArgs], shell: false };
|
|
6269
6411
|
}
|
|
6270
6412
|
function resolveWindowsOpenCodePackageEntry(commandPath, deps = {}) {
|
|
6271
|
-
const existsSyncFn = deps.existsSyncFn ??
|
|
6413
|
+
const existsSyncFn = deps.existsSyncFn ?? existsSync7;
|
|
6272
6414
|
const execFileSyncFn = deps.execFileSyncFn;
|
|
6273
6415
|
const env = deps.env ?? process.env;
|
|
6274
|
-
const winPath =
|
|
6416
|
+
const winPath = path9.win32;
|
|
6275
6417
|
const candidates = [];
|
|
6276
6418
|
if (execFileSyncFn) {
|
|
6277
6419
|
try {
|
|
@@ -6299,7 +6441,7 @@ function resolveWindowsOpenCodePackageEntry(commandPath, deps = {}) {
|
|
|
6299
6441
|
function extractWindowsShimTargets(commandPath, deps = {}) {
|
|
6300
6442
|
if (!isWindowsCommandShim(commandPath)) return [];
|
|
6301
6443
|
const readFileSyncFn = deps.readFileSyncFn ?? readFileSync4;
|
|
6302
|
-
const commandDir =
|
|
6444
|
+
const commandDir = path9.win32.dirname(commandPath);
|
|
6303
6445
|
let raw;
|
|
6304
6446
|
try {
|
|
6305
6447
|
raw = String(readFileSyncFn(commandPath, "utf8"));
|
|
@@ -6310,7 +6452,7 @@ function extractWindowsShimTargets(commandPath, deps = {}) {
|
|
|
6310
6452
|
const dp0Pattern = /%~dp0\\?([^"\r\n]*?opencode\.(?:exe|js|mjs|cjs))/gi;
|
|
6311
6453
|
for (const match of raw.matchAll(dp0Pattern)) {
|
|
6312
6454
|
const relative = match[1]?.replace(/^\\+/, "");
|
|
6313
|
-
if (relative) candidates.push(
|
|
6455
|
+
if (relative) candidates.push(path9.win32.normalize(path9.win32.join(commandDir, relative)));
|
|
6314
6456
|
}
|
|
6315
6457
|
return candidates;
|
|
6316
6458
|
}
|
|
@@ -6324,7 +6466,7 @@ function resolveOpenCodeSpawn(commandArgs, deps = {}) {
|
|
|
6324
6466
|
};
|
|
6325
6467
|
}
|
|
6326
6468
|
const command = resolveCommandOnPath("opencode", deps);
|
|
6327
|
-
if (command &&
|
|
6469
|
+
if (command && path9.win32.extname(command).toLowerCase() === ".exe") {
|
|
6328
6470
|
return { command, args: commandArgs, shell: false };
|
|
6329
6471
|
}
|
|
6330
6472
|
const packageEntry = resolveWindowsOpenCodePackageEntry(command, deps);
|
|
@@ -6349,7 +6491,6 @@ function isSystemFirstMessageTask(message) {
|
|
|
6349
6491
|
}
|
|
6350
6492
|
function buildOpenCodeSystemPrompt(config) {
|
|
6351
6493
|
return buildCliTransportSystemPrompt(config, {
|
|
6352
|
-
toolPrefix: CHAT_MCP_TOOL_PREFIX,
|
|
6353
6494
|
extraCriticalRules: [],
|
|
6354
6495
|
postStartupNotes: [
|
|
6355
6496
|
"**OpenCode runtime note:** Slock launches you as a per-turn process. Complete the current wake using `slock` CLI commands, then stop; the daemon will restart you when new messages arrive."
|
|
@@ -6394,11 +6535,9 @@ var OpenCodeDriver = class {
|
|
|
6394
6535
|
}
|
|
6395
6536
|
};
|
|
6396
6537
|
supportsStdinNotification = false;
|
|
6397
|
-
mcpToolPrefix = CHAT_MCP_TOOL_PREFIX;
|
|
6398
6538
|
busyDeliveryMode = "none";
|
|
6399
6539
|
terminateProcessOnTurnEnd = true;
|
|
6400
6540
|
deferSpawnUntilMessage = true;
|
|
6401
|
-
usesSlockCliForCommunication = true;
|
|
6402
6541
|
shouldDeferWakeMessage(message) {
|
|
6403
6542
|
return isSystemFirstMessageTask(message);
|
|
6404
6543
|
}
|
|
@@ -6432,7 +6571,7 @@ var OpenCodeDriver = class {
|
|
|
6432
6571
|
if (unsupportedMessage) {
|
|
6433
6572
|
throw new Error(unsupportedMessage);
|
|
6434
6573
|
}
|
|
6435
|
-
const launch = await buildOpenCodeLaunchOptions(ctx,
|
|
6574
|
+
const launch = await buildOpenCodeLaunchOptions(ctx, os5.homedir(), version);
|
|
6436
6575
|
const spawnSpec = resolveOpenCodeSpawn(launch.args);
|
|
6437
6576
|
const proc = spawn8(spawnSpec.command, spawnSpec.args, {
|
|
6438
6577
|
cwd: ctx.workingDirectory,
|
|
@@ -6499,8 +6638,8 @@ var OpenCodeDriver = class {
|
|
|
6499
6638
|
// src/drivers/pi.ts
|
|
6500
6639
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
6501
6640
|
import { EventEmitter } from "events";
|
|
6502
|
-
import { mkdirSync as
|
|
6503
|
-
import
|
|
6641
|
+
import { mkdirSync as mkdirSync3, readdirSync } from "fs";
|
|
6642
|
+
import path10 from "path";
|
|
6504
6643
|
import {
|
|
6505
6644
|
AuthStorage,
|
|
6506
6645
|
createBashTool,
|
|
@@ -6513,6 +6652,7 @@ import {
|
|
|
6513
6652
|
VERSION as PI_SDK_VERSION
|
|
6514
6653
|
} from "@earendil-works/pi-coding-agent";
|
|
6515
6654
|
var PI_SESSION_DIR = ".pi-sessions";
|
|
6655
|
+
var PI_SDK_COMPACTION_ENABLED = true;
|
|
6516
6656
|
var PI_PROVIDER_LABELS = {
|
|
6517
6657
|
google: "Google",
|
|
6518
6658
|
openai: "OpenAI",
|
|
@@ -6526,7 +6666,7 @@ function createPiSdkEventMappingState(sessionId = null) {
|
|
|
6526
6666
|
};
|
|
6527
6667
|
}
|
|
6528
6668
|
function buildPiSessionDir(workingDirectory) {
|
|
6529
|
-
return
|
|
6669
|
+
return path10.join(workingDirectory, PI_SESSION_DIR);
|
|
6530
6670
|
}
|
|
6531
6671
|
async function buildPiSpawnEnv(ctx) {
|
|
6532
6672
|
return (await prepareCliTransport(ctx, { NO_COLOR: "1" })).spawnEnv;
|
|
@@ -6548,7 +6688,7 @@ function findPiSessionFile(sessionDir, sessionId) {
|
|
|
6548
6688
|
}
|
|
6549
6689
|
const suffix = `_${sessionId}.jsonl`;
|
|
6550
6690
|
const match = entries.find((entry) => entry.endsWith(suffix));
|
|
6551
|
-
return match ?
|
|
6691
|
+
return match ? path10.join(sessionDir, match) : null;
|
|
6552
6692
|
}
|
|
6553
6693
|
function detectPiModelsFromRegistry(modelRegistry) {
|
|
6554
6694
|
const models = [];
|
|
@@ -6701,17 +6841,17 @@ var PI_IDLE_PROMPT_RETRY_MS = 25;
|
|
|
6701
6841
|
var PI_IDLE_PROMPT_MAX_WAIT_MS = 1e3;
|
|
6702
6842
|
async function createPiAgentSessionForContext(ctx, sessionId) {
|
|
6703
6843
|
const sessionDir = buildPiSessionDir(ctx.workingDirectory);
|
|
6704
|
-
|
|
6844
|
+
mkdirSync3(sessionDir, { recursive: true });
|
|
6705
6845
|
const spawnEnv = await buildPiSpawnEnv(ctx);
|
|
6706
6846
|
const agentDir = spawnEnv.PI_CODING_AGENT_DIR || getAgentDir();
|
|
6707
|
-
const authStorage = AuthStorage.create(
|
|
6708
|
-
const modelRegistry = ModelRegistry.create(authStorage,
|
|
6847
|
+
const authStorage = AuthStorage.create(path10.join(agentDir, "auth.json"));
|
|
6848
|
+
const modelRegistry = ModelRegistry.create(authStorage, path10.join(agentDir, "models.json"));
|
|
6709
6849
|
const launchRuntimeFields = runtimeConfigToLaunchFields(ctx.config);
|
|
6710
6850
|
const model = resolvePiModelFromRegistry(launchRuntimeFields.model, modelRegistry);
|
|
6711
6851
|
if (launchRuntimeFields.model && launchRuntimeFields.model !== "default" && !model) {
|
|
6712
6852
|
throw new Error(`Pi model not found: ${launchRuntimeFields.model}`);
|
|
6713
6853
|
}
|
|
6714
|
-
const settingsManager = SettingsManager.inMemory({ compaction: { enabled:
|
|
6854
|
+
const settingsManager = SettingsManager.inMemory({ compaction: { enabled: PI_SDK_COMPACTION_ENABLED } });
|
|
6715
6855
|
const resourceLoader = new DefaultResourceLoader({
|
|
6716
6856
|
cwd: ctx.workingDirectory,
|
|
6717
6857
|
agentDir,
|
|
@@ -6948,9 +7088,7 @@ var PiDriver = class {
|
|
|
6948
7088
|
toLaunchSpec: (modelId) => ({ params: { model: modelId } })
|
|
6949
7089
|
};
|
|
6950
7090
|
supportsStdinNotification = true;
|
|
6951
|
-
mcpToolPrefix = "";
|
|
6952
7091
|
busyDeliveryMode = "direct";
|
|
6953
|
-
usesSlockCliForCommunication = true;
|
|
6954
7092
|
sessionId = null;
|
|
6955
7093
|
get currentSessionId() {
|
|
6956
7094
|
return this.sessionId;
|
|
@@ -6981,7 +7119,6 @@ var PiDriver = class {
|
|
|
6981
7119
|
}
|
|
6982
7120
|
buildSystemPrompt(config, _agentId) {
|
|
6983
7121
|
return buildCliTransportSystemPrompt(config, {
|
|
6984
|
-
toolPrefix: "",
|
|
6985
7122
|
extraCriticalRules: [],
|
|
6986
7123
|
postStartupNotes: [
|
|
6987
7124
|
"**Pi runtime note:** Slock keeps Pi running as a persistent SDK session. While you are working, Slock may send inbox-count notifications into the current turn; call `slock message check` at natural breakpoints."
|
|
@@ -7155,7 +7292,7 @@ function getDriver(runtimeId) {
|
|
|
7155
7292
|
|
|
7156
7293
|
// src/workspaces.ts
|
|
7157
7294
|
import { readdir, rm, stat } from "fs/promises";
|
|
7158
|
-
import
|
|
7295
|
+
import path11 from "path";
|
|
7159
7296
|
function isValidWorkspaceDirectoryName(directoryName) {
|
|
7160
7297
|
return !directoryName.includes("/") && !directoryName.includes("\\") && !directoryName.includes("..");
|
|
7161
7298
|
}
|
|
@@ -7163,7 +7300,7 @@ function resolveWorkspaceDirectoryPath(dataDir, directoryName) {
|
|
|
7163
7300
|
if (!isValidWorkspaceDirectoryName(directoryName)) {
|
|
7164
7301
|
return null;
|
|
7165
7302
|
}
|
|
7166
|
-
return
|
|
7303
|
+
return path11.join(dataDir, directoryName);
|
|
7167
7304
|
}
|
|
7168
7305
|
function emptyWorkspaceDirectorySummary(latestMtime = /* @__PURE__ */ new Date(0)) {
|
|
7169
7306
|
return {
|
|
@@ -7212,7 +7349,7 @@ async function summarizeWorkspaceDirectory(dirPath) {
|
|
|
7212
7349
|
return summary;
|
|
7213
7350
|
}
|
|
7214
7351
|
const childSummaries = await Promise.all(
|
|
7215
|
-
entries.map((entry) => summarizeWorkspaceEntry(
|
|
7352
|
+
entries.map((entry) => summarizeWorkspaceEntry(path11.join(dirPath, entry.name), entry))
|
|
7216
7353
|
);
|
|
7217
7354
|
for (const childSummary of childSummaries) {
|
|
7218
7355
|
summary = mergeWorkspaceDirectorySummaries(summary, childSummary);
|
|
@@ -7231,7 +7368,7 @@ async function scanWorkspaceDirectories(dataDir) {
|
|
|
7231
7368
|
if (!entry.isDirectory()) {
|
|
7232
7369
|
return null;
|
|
7233
7370
|
}
|
|
7234
|
-
const dirPath =
|
|
7371
|
+
const dirPath = path11.join(dataDir, entry.name);
|
|
7235
7372
|
try {
|
|
7236
7373
|
const summary = await summarizeWorkspaceDirectory(dirPath);
|
|
7237
7374
|
return {
|
|
@@ -7782,12 +7919,12 @@ function findSessionJsonl(root, predicate) {
|
|
|
7782
7919
|
for (const entry of entries) {
|
|
7783
7920
|
if (++visited > maxEntries) return null;
|
|
7784
7921
|
if (!entry.isFile() || !predicate(entry.name)) continue;
|
|
7785
|
-
return
|
|
7922
|
+
return path12.join(dir, entry.name);
|
|
7786
7923
|
}
|
|
7787
7924
|
for (const entry of entries) {
|
|
7788
7925
|
if (++visited > maxEntries) return null;
|
|
7789
7926
|
if (!entry.isDirectory()) continue;
|
|
7790
|
-
const found = visit(
|
|
7927
|
+
const found = visit(path12.join(dir, entry.name), depth - 1);
|
|
7791
7928
|
if (found) return found;
|
|
7792
7929
|
}
|
|
7793
7930
|
return null;
|
|
@@ -7800,9 +7937,9 @@ function safeSessionFilename(value) {
|
|
|
7800
7937
|
}
|
|
7801
7938
|
function writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir) {
|
|
7802
7939
|
try {
|
|
7803
|
-
const dir =
|
|
7804
|
-
|
|
7805
|
-
const filePath =
|
|
7940
|
+
const dir = path12.join(fallbackDir, ".slock", "runtime-sessions");
|
|
7941
|
+
mkdirSync4(dir, { recursive: true });
|
|
7942
|
+
const filePath = path12.join(dir, `${runtime}-${safeSessionFilename(sessionId)}.jsonl`);
|
|
7806
7943
|
writeFileSync4(filePath, JSON.stringify({
|
|
7807
7944
|
type: "runtime_session_handoff",
|
|
7808
7945
|
runtime,
|
|
@@ -7821,8 +7958,20 @@ function writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir) {
|
|
|
7821
7958
|
return null;
|
|
7822
7959
|
}
|
|
7823
7960
|
}
|
|
7824
|
-
function
|
|
7825
|
-
|
|
7961
|
+
function resolveRuntimeHomeDir(config, defaultHomeDir, workspacePath) {
|
|
7962
|
+
if (isClaudeCustomProviderConfig(config)) {
|
|
7963
|
+
return getClaudeProviderStatePaths(workspacePath).home;
|
|
7964
|
+
}
|
|
7965
|
+
return defaultHomeDir;
|
|
7966
|
+
}
|
|
7967
|
+
function ensureRuntimeHomeDir(config, defaultHomeDir, workspacePath) {
|
|
7968
|
+
if (isClaudeCustomProviderConfig(config)) {
|
|
7969
|
+
return ensureClaudeProviderStatePaths(workspacePath).home;
|
|
7970
|
+
}
|
|
7971
|
+
return defaultHomeDir;
|
|
7972
|
+
}
|
|
7973
|
+
function resolveRuntimeSessionRef(runtime, sessionId, homeDir = os6.homedir(), fallbackDir) {
|
|
7974
|
+
const directPath = path12.isAbsolute(sessionId) ? sessionId : null;
|
|
7826
7975
|
if (directPath) {
|
|
7827
7976
|
try {
|
|
7828
7977
|
if (statSync(directPath).isFile()) {
|
|
@@ -7831,7 +7980,7 @@ function resolveRuntimeSessionRef(runtime, sessionId, homeDir = os5.homedir(), f
|
|
|
7831
7980
|
} catch {
|
|
7832
7981
|
}
|
|
7833
7982
|
}
|
|
7834
|
-
const resolvedPath = runtime === "claude" ? findSessionJsonl(
|
|
7983
|
+
const resolvedPath = runtime === "claude" ? findSessionJsonl(path12.join(homeDir, ".claude", "projects"), (filename) => filename === `${sessionId}.jsonl`) : runtime === "codex" ? findSessionJsonl(path12.join(homeDir, ".codex", "sessions"), (filename) => filename.endsWith(".jsonl") && filename.includes(sessionId)) : null;
|
|
7835
7984
|
if (!resolvedPath && fallbackDir) {
|
|
7836
7985
|
const fallback = writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir);
|
|
7837
7986
|
if (fallback) return fallback;
|
|
@@ -7892,13 +8041,7 @@ function formatThreadContextMessage(message) {
|
|
|
7892
8041
|
const senderType = formatVisibleActorType(message.sender_type);
|
|
7893
8042
|
return `- [msg=${msgId} time=${time}${senderType}] ${formatSenderHandle(message)}: ${message.content}`;
|
|
7894
8043
|
}
|
|
7895
|
-
function
|
|
7896
|
-
return `${driver?.mcpToolPrefix ?? ""}${name}`;
|
|
7897
|
-
}
|
|
7898
|
-
function communicationCommand(driver, name) {
|
|
7899
|
-
if (!driver?.usesSlockCliForCommunication) {
|
|
7900
|
-
return mcpToolName(driver, name);
|
|
7901
|
-
}
|
|
8044
|
+
function communicationCommand(name) {
|
|
7902
8045
|
switch (name) {
|
|
7903
8046
|
case "send_message":
|
|
7904
8047
|
return "slock message send";
|
|
@@ -7912,18 +8055,18 @@ function communicationCommand(driver, name) {
|
|
|
7912
8055
|
return `slock ${name.replace(/_/g, " ")}`;
|
|
7913
8056
|
}
|
|
7914
8057
|
}
|
|
7915
|
-
function dynamicReplyInstruction(
|
|
7916
|
-
return
|
|
8058
|
+
function dynamicReplyInstruction() {
|
|
8059
|
+
return "reply using `slock message send --target <exact target>`";
|
|
7917
8060
|
}
|
|
7918
|
-
function dynamicClaimInstruction(
|
|
7919
|
-
return
|
|
8061
|
+
function dynamicClaimInstruction() {
|
|
8062
|
+
return "claim the relevant task with `slock task claim`";
|
|
7920
8063
|
}
|
|
7921
|
-
function formatIncomingMessage(message
|
|
8064
|
+
function formatIncomingMessage(message) {
|
|
7922
8065
|
const threadJoinPrefix = message.thread_join_context ? [
|
|
7923
8066
|
`[Slock thread context: you were added to a new thread via @mention.]`,
|
|
7924
8067
|
`parent: ${message.thread_join_context.parent_target}`,
|
|
7925
8068
|
`thread: ${message.thread_join_context.thread_target}`,
|
|
7926
|
-
`suggested next step:
|
|
8069
|
+
`suggested next step: slock message read --channel "${message.thread_join_context.suggested_read_history_target}"`,
|
|
7927
8070
|
"",
|
|
7928
8071
|
"Parent message:",
|
|
7929
8072
|
formatThreadContextMessage(message.thread_join_context.parent_message),
|
|
@@ -8843,6 +8986,7 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
8843
8986
|
maxConcurrentAgentStarts;
|
|
8844
8987
|
agentStartIntervalMs;
|
|
8845
8988
|
startingInboxes = /* @__PURE__ */ new Map();
|
|
8989
|
+
pendingStartRebinds = /* @__PURE__ */ new Map();
|
|
8846
8990
|
/** Cached configs for agents whose process exited normally — enables auto-restart on next message */
|
|
8847
8991
|
idleAgentConfigs = /* @__PURE__ */ new Map();
|
|
8848
8992
|
slockCliPath;
|
|
@@ -8877,7 +9021,7 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
8877
9021
|
this.daemonApiKey = daemonApiKey;
|
|
8878
9022
|
this.serverUrl = opts.serverUrl;
|
|
8879
9023
|
this.dataDir = opts.dataDir || resolveSlockHomePath("agents");
|
|
8880
|
-
this.runtimeSessionHomeDir = opts.runtimeSessionHomeDir ||
|
|
9024
|
+
this.runtimeSessionHomeDir = opts.runtimeSessionHomeDir || os6.homedir();
|
|
8881
9025
|
this.driverResolver = opts.driverResolver || getDriver;
|
|
8882
9026
|
this.defaultAgentEnvVarsProvider = opts.defaultAgentEnvVarsProvider || null;
|
|
8883
9027
|
this.tracer = opts.tracer ?? noopTracer;
|
|
@@ -9488,6 +9632,92 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
9488
9632
|
...attrs
|
|
9489
9633
|
};
|
|
9490
9634
|
}
|
|
9635
|
+
recordStartRebind(agentId, start, reason, previousLaunchId, nextLaunchId, sessionId) {
|
|
9636
|
+
this.recordDaemonTrace("daemon.agent.start.rebound", {
|
|
9637
|
+
...this.startQueueTraceAttrs(
|
|
9638
|
+
agentId,
|
|
9639
|
+
start.config,
|
|
9640
|
+
start.wakeMessage,
|
|
9641
|
+
start.unreadSummary,
|
|
9642
|
+
start.resumePrompt,
|
|
9643
|
+
start.launchId,
|
|
9644
|
+
start.wakeMessageTransient ?? false
|
|
9645
|
+
),
|
|
9646
|
+
reason,
|
|
9647
|
+
previous_launch_id_present: Boolean(previousLaunchId),
|
|
9648
|
+
next_launch_id_present: Boolean(nextLaunchId),
|
|
9649
|
+
session_id_present: Boolean(sessionId)
|
|
9650
|
+
});
|
|
9651
|
+
}
|
|
9652
|
+
sameWakeMessage(left, right) {
|
|
9653
|
+
if (!left || !right) return left === right;
|
|
9654
|
+
if (left.message_id && right.message_id) return left.message_id === right.message_id;
|
|
9655
|
+
return left === right;
|
|
9656
|
+
}
|
|
9657
|
+
rebindQueuedStart(agentId, start, reason) {
|
|
9658
|
+
const item = this.queuedAgentStarts.get(agentId);
|
|
9659
|
+
if (!item) {
|
|
9660
|
+
return false;
|
|
9661
|
+
}
|
|
9662
|
+
const previousLaunchId = item.launchId || null;
|
|
9663
|
+
const nextLaunchId = start.launchId || previousLaunchId;
|
|
9664
|
+
const previousWakeMessage = item.wakeMessage;
|
|
9665
|
+
if (previousWakeMessage && start.wakeMessage && !this.sameWakeMessage(previousWakeMessage, start.wakeMessage)) {
|
|
9666
|
+
const pending = this.startingInboxes.get(agentId) || [];
|
|
9667
|
+
pending.push(previousWakeMessage);
|
|
9668
|
+
this.startingInboxes.set(agentId, pending);
|
|
9669
|
+
}
|
|
9670
|
+
item.config = start.config;
|
|
9671
|
+
item.unreadSummary = start.unreadSummary;
|
|
9672
|
+
item.resumePrompt = start.resumePrompt;
|
|
9673
|
+
item.launchId = nextLaunchId || void 0;
|
|
9674
|
+
if (start.wakeMessage) {
|
|
9675
|
+
item.wakeMessage = start.wakeMessage;
|
|
9676
|
+
item.wakeMessageTransient = start.wakeMessageTransient;
|
|
9677
|
+
}
|
|
9678
|
+
this.recordStartRebind(
|
|
9679
|
+
agentId,
|
|
9680
|
+
{ ...start, launchId: nextLaunchId || void 0 },
|
|
9681
|
+
reason,
|
|
9682
|
+
previousLaunchId,
|
|
9683
|
+
nextLaunchId,
|
|
9684
|
+
null
|
|
9685
|
+
);
|
|
9686
|
+
return true;
|
|
9687
|
+
}
|
|
9688
|
+
rebindRunningStart(agentId, start, reason) {
|
|
9689
|
+
const ap = this.agents.get(agentId);
|
|
9690
|
+
if (!ap) {
|
|
9691
|
+
this.pendingStartRebinds.set(agentId, start);
|
|
9692
|
+
return false;
|
|
9693
|
+
}
|
|
9694
|
+
const previousLaunchId = ap.launchId;
|
|
9695
|
+
const nextLaunchId = start.launchId || ap.launchId || null;
|
|
9696
|
+
const nextSessionId = ap.sessionId || start.config.sessionId || null;
|
|
9697
|
+
ap.launchId = nextLaunchId;
|
|
9698
|
+
ap.sessionId = nextSessionId;
|
|
9699
|
+
ap.config = {
|
|
9700
|
+
...start.config,
|
|
9701
|
+
sessionId: nextSessionId
|
|
9702
|
+
};
|
|
9703
|
+
this.idleAgentConfigs.set(agentId, {
|
|
9704
|
+
config: { ...stripManagedRunnerCredential(ap.config), sessionId: nextSessionId },
|
|
9705
|
+
sessionId: nextSessionId,
|
|
9706
|
+
launchId: nextLaunchId
|
|
9707
|
+
});
|
|
9708
|
+
this.recordStartRebind(agentId, start, reason, previousLaunchId, nextLaunchId, nextSessionId);
|
|
9709
|
+
this.sendAgentStatus(agentId, "active", nextLaunchId);
|
|
9710
|
+
if (nextSessionId) {
|
|
9711
|
+
this.sendToServer({ type: "agent:session", agentId, sessionId: nextSessionId, launchId: nextLaunchId || void 0 });
|
|
9712
|
+
}
|
|
9713
|
+
if (start.wakeMessage) {
|
|
9714
|
+
const accepted = this.deliverMessage(agentId, start.wakeMessage, { transient: start.wakeMessageTransient === true });
|
|
9715
|
+
if (accepted instanceof Promise) {
|
|
9716
|
+
accepted.catch((err) => logger.error(`[Agent ${agentId}] Failed to deliver wake message after start rebind`, err));
|
|
9717
|
+
}
|
|
9718
|
+
}
|
|
9719
|
+
return true;
|
|
9720
|
+
}
|
|
9491
9721
|
async startAgent(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId, wakeMessageTransient = false) {
|
|
9492
9722
|
this.recordDaemonTrace("daemon.agent.start.requested", this.startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId, wakeMessageTransient));
|
|
9493
9723
|
if (this.agents.has(agentId)) {
|
|
@@ -9495,7 +9725,8 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
9495
9725
|
...this.startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId, wakeMessageTransient),
|
|
9496
9726
|
reason: "already_running"
|
|
9497
9727
|
});
|
|
9498
|
-
|
|
9728
|
+
this.rebindRunningStart(agentId, { config, wakeMessage, unreadSummary, resumePrompt, launchId, wakeMessageTransient }, "already_running");
|
|
9729
|
+
logger.info(`[Agent ${agentId}] Start rebound (already running)`);
|
|
9499
9730
|
return;
|
|
9500
9731
|
}
|
|
9501
9732
|
if (this.agentsStarting.has(agentId)) {
|
|
@@ -9503,7 +9734,8 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
9503
9734
|
...this.startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId, wakeMessageTransient),
|
|
9504
9735
|
reason: "already_starting"
|
|
9505
9736
|
});
|
|
9506
|
-
|
|
9737
|
+
this.pendingStartRebinds.set(agentId, { config, wakeMessage, unreadSummary, resumePrompt, launchId, wakeMessageTransient });
|
|
9738
|
+
logger.info(`[Agent ${agentId}] Start rebind deferred (startup in progress)`);
|
|
9507
9739
|
return;
|
|
9508
9740
|
}
|
|
9509
9741
|
if (this.queuedAgentStarts.has(agentId)) {
|
|
@@ -9511,7 +9743,8 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
9511
9743
|
...this.startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId, wakeMessageTransient),
|
|
9512
9744
|
reason: "already_queued"
|
|
9513
9745
|
});
|
|
9514
|
-
|
|
9746
|
+
this.rebindQueuedStart(agentId, { config, wakeMessage, unreadSummary, resumePrompt, launchId, wakeMessageTransient }, "already_queued");
|
|
9747
|
+
logger.info(`[Agent ${agentId}] Queued start rebound (startup already queued)`);
|
|
9515
9748
|
return;
|
|
9516
9749
|
}
|
|
9517
9750
|
return new Promise((resolve, reject) => {
|
|
@@ -9570,6 +9803,11 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
9570
9803
|
...this.startQueueTraceAttrs(item.agentId, item.config, item.wakeMessage, item.unreadSummary, item.resumePrompt, item.launchId, item.wakeMessageTransient),
|
|
9571
9804
|
reason: "already_running_or_starting"
|
|
9572
9805
|
});
|
|
9806
|
+
if (this.agents.has(item.agentId)) {
|
|
9807
|
+
this.rebindRunningStart(item.agentId, item, "already_running_or_starting");
|
|
9808
|
+
} else {
|
|
9809
|
+
this.pendingStartRebinds.set(item.agentId, item);
|
|
9810
|
+
}
|
|
9573
9811
|
logger.info(`[Agent ${item.agentId}] Queued start skipped (already running or starting)`);
|
|
9574
9812
|
item.resolve();
|
|
9575
9813
|
this.pumpAgentStartQueue();
|
|
@@ -9656,7 +9894,8 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
9656
9894
|
...this.startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId, wakeMessageTransient),
|
|
9657
9895
|
reason: "already_running"
|
|
9658
9896
|
});
|
|
9659
|
-
|
|
9897
|
+
this.rebindRunningStart(agentId, { config, wakeMessage, unreadSummary, resumePrompt, launchId, wakeMessageTransient }, "already_running");
|
|
9898
|
+
logger.info(`[Agent ${agentId}] Start rebound (already running)`);
|
|
9660
9899
|
return;
|
|
9661
9900
|
}
|
|
9662
9901
|
if (this.agentsStarting.has(agentId)) {
|
|
@@ -9664,7 +9903,8 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
9664
9903
|
...this.startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId, wakeMessageTransient),
|
|
9665
9904
|
reason: "already_starting"
|
|
9666
9905
|
});
|
|
9667
|
-
|
|
9906
|
+
this.pendingStartRebinds.set(agentId, { config, wakeMessage, unreadSummary, resumePrompt, launchId, wakeMessageTransient });
|
|
9907
|
+
logger.info(`[Agent ${agentId}] Start rebind deferred (startup in progress)`);
|
|
9668
9908
|
return;
|
|
9669
9909
|
}
|
|
9670
9910
|
this.agentsStarting.add(agentId);
|
|
@@ -9683,7 +9923,7 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
9683
9923
|
);
|
|
9684
9924
|
wakeMessage = void 0;
|
|
9685
9925
|
}
|
|
9686
|
-
const agentDataDir =
|
|
9926
|
+
const agentDataDir = path12.join(this.dataDir, agentId);
|
|
9687
9927
|
await mkdir(agentDataDir, { recursive: true });
|
|
9688
9928
|
let runtimeConfig = withLocalRuntimeContext(config, agentId, agentDataDir);
|
|
9689
9929
|
const legacyRuntimeProfileControl = runtimeConfig.runtimeProfileControl?.kind === "migration" ? runtimeConfig.runtimeProfileControl : null;
|
|
@@ -9697,23 +9937,23 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
9697
9937
|
);
|
|
9698
9938
|
runtimeConfig = { ...runtimeConfig, runtimeProfileControl: null };
|
|
9699
9939
|
}
|
|
9700
|
-
const memoryMdPath =
|
|
9940
|
+
const memoryMdPath = path12.join(agentDataDir, "MEMORY.md");
|
|
9701
9941
|
try {
|
|
9702
9942
|
await access(memoryMdPath);
|
|
9703
9943
|
} catch {
|
|
9704
9944
|
const initialMemoryMd = buildInitialMemoryMd(runtimeConfig);
|
|
9705
9945
|
await writeFile(memoryMdPath, initialMemoryMd);
|
|
9706
9946
|
}
|
|
9707
|
-
const notesDir =
|
|
9947
|
+
const notesDir = path12.join(agentDataDir, "notes");
|
|
9708
9948
|
await mkdir(notesDir, { recursive: true });
|
|
9709
9949
|
if (getOnboardingSeedMode(config) === FIRST_CINDY_SEED_MODE) {
|
|
9710
9950
|
const seedFiles = buildOnboardingSeedFiles();
|
|
9711
9951
|
for (const { relativePath, content } of seedFiles) {
|
|
9712
|
-
const fullPath =
|
|
9952
|
+
const fullPath = path12.join(agentDataDir, relativePath);
|
|
9713
9953
|
try {
|
|
9714
9954
|
await access(fullPath);
|
|
9715
9955
|
} catch {
|
|
9716
|
-
await mkdir(
|
|
9956
|
+
await mkdir(path12.dirname(fullPath), { recursive: true });
|
|
9717
9957
|
await writeFile(fullPath, content);
|
|
9718
9958
|
}
|
|
9719
9959
|
}
|
|
@@ -9737,7 +9977,7 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
9737
9977
|
if (transientWakeMessage) {
|
|
9738
9978
|
prompt = `System notice received:
|
|
9739
9979
|
|
|
9740
|
-
${formatIncomingMessage(wakeMessage
|
|
9980
|
+
${formatIncomingMessage(wakeMessage)}
|
|
9741
9981
|
|
|
9742
9982
|
Respond as appropriate. Complete all your work before stopping.
|
|
9743
9983
|
${RESPONSE_TARGET_HINT}`;
|
|
@@ -9771,7 +10011,7 @@ Use the inbox/read commands at a natural breakpoint if you choose to inspect tho
|
|
|
9771
10011
|
}
|
|
9772
10012
|
prompt += `
|
|
9773
10013
|
|
|
9774
|
-
Use ${communicationCommand(
|
|
10014
|
+
Use ${communicationCommand("read_history")} to catch up on the channels listed above, then stop. Read each listed channel at most once unless a read fails. Do NOT call ${communicationCommand("check_messages")} in this mode. If the history reveals a direct request, assignment, @mention, review request, or task clearly addressed to you, switch into active handling instead of stopping: ${dynamicReplyInstruction()} and ${dynamicClaimInstruction()} before starting work. Otherwise, do NOT send any message in this mode. ${getMessageDeliveryText(driver)}`;
|
|
9775
10015
|
promptSource = "resume_unread_summary";
|
|
9776
10016
|
} else if (isResume) {
|
|
9777
10017
|
prompt = `No new messages while you were away. Nothing to do \u2014 just stop. ${getMessageDeliveryText(driver)}`;
|
|
@@ -9792,7 +10032,12 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
9792
10032
|
nativeStandingPrompt: Boolean(driver.supportsNativeStandingPrompt)
|
|
9793
10033
|
});
|
|
9794
10034
|
const effectiveConfig = await this.buildSpawnConfig(agentId, runtimeConfig);
|
|
9795
|
-
const
|
|
10035
|
+
const pendingStartRebind = this.pendingStartRebinds.get(agentId);
|
|
10036
|
+
if (pendingStartRebind) {
|
|
10037
|
+
this.pendingStartRebinds.delete(agentId);
|
|
10038
|
+
}
|
|
10039
|
+
const effectiveLaunchId = pendingStartRebind?.launchId || launchId || null;
|
|
10040
|
+
const canDeferEmptyStart = driver.deferSpawnUntilMessage === true && !wakeMessage && !pendingStartRebind?.wakeMessage && !runtimeConfig.runtimeProfileControl && (!unreadSummary || Object.keys(unreadSummary).length === 0);
|
|
9796
10041
|
if (canDeferEmptyStart) {
|
|
9797
10042
|
const pendingMessages = this.startingInboxes.get(agentId) || [];
|
|
9798
10043
|
this.startingInboxes.delete(agentId);
|
|
@@ -9800,12 +10045,12 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
9800
10045
|
this.idleAgentConfigs.set(agentId, {
|
|
9801
10046
|
config: effectiveConfig,
|
|
9802
10047
|
sessionId: effectiveConfig.sessionId || null,
|
|
9803
|
-
launchId:
|
|
10048
|
+
launchId: effectiveLaunchId
|
|
9804
10049
|
});
|
|
9805
|
-
this.sendAgentStatus(agentId, "active",
|
|
10050
|
+
this.sendAgentStatus(agentId, "active", effectiveLaunchId);
|
|
9806
10051
|
this.broadcastActivity(agentId, "online", "Process idle");
|
|
9807
10052
|
this.recordDaemonTrace("daemon.agent.spawn.deferred", {
|
|
9808
|
-
...this.startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt,
|
|
10053
|
+
...this.startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, effectiveLaunchId || void 0, wakeMessageTransient),
|
|
9809
10054
|
pending_messages_count: pendingMessages.length,
|
|
9810
10055
|
reason: "defer_until_concrete_message"
|
|
9811
10056
|
});
|
|
@@ -9823,7 +10068,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
9823
10068
|
workingDirectory: agentDataDir,
|
|
9824
10069
|
slockCliPath: this.slockCliPath,
|
|
9825
10070
|
daemonApiKey: this.daemonApiKey,
|
|
9826
|
-
launchId:
|
|
10071
|
+
launchId: effectiveLaunchId,
|
|
9827
10072
|
agentCredentialProxyInboxCoordinator: this.createAgentProxyInboxCoordinator(agentId),
|
|
9828
10073
|
cliTransportTraceDir: this.cliTransportTraceDir
|
|
9829
10074
|
};
|
|
@@ -9834,7 +10079,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
9834
10079
|
inbox: wakeMessageDeliveredAsInboxUpdate && wakeMessage ? [wakeMessage, ...startingInboxMessages] : startingInboxMessages,
|
|
9835
10080
|
config: runtimeConfig,
|
|
9836
10081
|
sessionId: runtimeConfig.sessionId || null,
|
|
9837
|
-
launchId:
|
|
10082
|
+
launchId: effectiveLaunchId,
|
|
9838
10083
|
startupWakeMessage: wakeMessage,
|
|
9839
10084
|
startupUnreadSummary: unreadSummary,
|
|
9840
10085
|
startupResumePrompt: resumePrompt,
|
|
@@ -9870,8 +10115,11 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
9870
10115
|
this.idleAgentConfigs.set(agentId, {
|
|
9871
10116
|
config: { ...effectiveConfig, sessionId: effectiveConfig.sessionId || null },
|
|
9872
10117
|
sessionId: effectiveConfig.sessionId || null,
|
|
9873
|
-
launchId:
|
|
10118
|
+
launchId: effectiveLaunchId
|
|
9874
10119
|
});
|
|
10120
|
+
if (pendingStartRebind) {
|
|
10121
|
+
this.recordStartRebind(agentId, pendingStartRebind, "startup_registered", launchId || null, effectiveLaunchId, agentProcess.sessionId);
|
|
10122
|
+
}
|
|
9875
10123
|
this.startRuntimeTrace(agentId, agentProcess, "spawn", wakeMessage ? [wakeMessage] : void 0, runtimeInputTraceAttrs);
|
|
9876
10124
|
this.agentsStarting.delete(agentId);
|
|
9877
10125
|
if (runtimeConfig.runtimeProfileControl) {
|
|
@@ -10106,16 +10354,26 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
10106
10354
|
throw new Error(`Runtime session failed to start: ${startResult.reason}${startResult.error ? ` (${startResult.error})` : ""}`);
|
|
10107
10355
|
}
|
|
10108
10356
|
this.recordDaemonTrace("daemon.agent.spawn.created", {
|
|
10109
|
-
...this.startQueueTraceAttrs(agentId, effectiveConfig, wakeMessage, unreadSummary, resumePrompt, launchId, wakeMessageTransient),
|
|
10357
|
+
...this.startQueueTraceAttrs(agentId, effectiveConfig, wakeMessage, unreadSummary, resumePrompt, agentProcess.launchId || void 0, wakeMessageTransient),
|
|
10110
10358
|
detached: false,
|
|
10111
10359
|
new_session: false,
|
|
10112
10360
|
process_pid_present: typeof runtime.pid === "number"
|
|
10113
10361
|
});
|
|
10114
|
-
this.sendAgentStatus(agentId, "active", launchId
|
|
10362
|
+
this.sendAgentStatus(agentId, "active", agentProcess.launchId);
|
|
10363
|
+
if (pendingStartRebind && agentProcess.sessionId) {
|
|
10364
|
+
this.sendToServer({ type: "agent:session", agentId, sessionId: agentProcess.sessionId, launchId: agentProcess.launchId || void 0 });
|
|
10365
|
+
}
|
|
10366
|
+
if (pendingStartRebind?.wakeMessage) {
|
|
10367
|
+
const accepted = this.deliverMessage(agentId, pendingStartRebind.wakeMessage, { transient: pendingStartRebind.wakeMessageTransient === true });
|
|
10368
|
+
if (accepted instanceof Promise) {
|
|
10369
|
+
accepted.catch((err) => logger.error(`[Agent ${agentId}] Failed to deliver wake message after startup rebind`, err));
|
|
10370
|
+
}
|
|
10371
|
+
}
|
|
10115
10372
|
this.broadcastActivity(agentId, "working", "Starting\u2026");
|
|
10116
10373
|
this.startRuntimeStartupTimeout(agentId, agentProcess);
|
|
10117
10374
|
} catch (err) {
|
|
10118
10375
|
this.agentsStarting.delete(agentId);
|
|
10376
|
+
this.pendingStartRebinds.delete(agentId);
|
|
10119
10377
|
this.cleanupFailedRuntimeStart(agentId, agentProcess, err);
|
|
10120
10378
|
throw err;
|
|
10121
10379
|
}
|
|
@@ -10384,6 +10642,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
10384
10642
|
}
|
|
10385
10643
|
async stopAgent(agentId, { wait = false, silent = false } = {}) {
|
|
10386
10644
|
this.cancelQueuedAgentStart(agentId, "stop requested");
|
|
10645
|
+
this.pendingStartRebinds.delete(agentId);
|
|
10387
10646
|
this.idleAgentConfigs.delete(agentId);
|
|
10388
10647
|
const ap = this.agents.get(agentId);
|
|
10389
10648
|
if (!ap) {
|
|
@@ -10830,7 +11089,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
10830
11089
|
return true;
|
|
10831
11090
|
}
|
|
10832
11091
|
async resetWorkspace(agentId) {
|
|
10833
|
-
const agentDataDir =
|
|
11092
|
+
const agentDataDir = path12.join(this.dataDir, agentId);
|
|
10834
11093
|
try {
|
|
10835
11094
|
await rm2(agentDataDir, { recursive: true, force: true });
|
|
10836
11095
|
logger.info(`[Agent ${agentId}] Workspace reset complete (${agentDataDir})`);
|
|
@@ -10891,7 +11150,8 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
10891
11150
|
return result;
|
|
10892
11151
|
}
|
|
10893
11152
|
buildRuntimeProfileReport(agentId, config, sessionId, launchId) {
|
|
10894
|
-
const workspacePath =
|
|
11153
|
+
const workspacePath = path12.join(this.dataDir, agentId);
|
|
11154
|
+
const runtimeHomeDir = resolveRuntimeHomeDir(config, this.runtimeSessionHomeDir, workspacePath);
|
|
10895
11155
|
return {
|
|
10896
11156
|
agentId,
|
|
10897
11157
|
launchId,
|
|
@@ -10905,7 +11165,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
10905
11165
|
path: workspacePath,
|
|
10906
11166
|
reachable: true
|
|
10907
11167
|
},
|
|
10908
|
-
sessionRef: sessionId ? resolveRuntimeSessionRef(config.runtime, sessionId,
|
|
11168
|
+
sessionRef: sessionId ? resolveRuntimeSessionRef(config.runtime, sessionId, runtimeHomeDir, workspacePath) : null
|
|
10909
11169
|
}
|
|
10910
11170
|
};
|
|
10911
11171
|
}
|
|
@@ -11184,7 +11444,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
11184
11444
|
}
|
|
11185
11445
|
// Workspace file browsing
|
|
11186
11446
|
async getFileTree(agentId, dirPath) {
|
|
11187
|
-
const agentDir =
|
|
11447
|
+
const agentDir = path12.join(this.dataDir, agentId);
|
|
11188
11448
|
try {
|
|
11189
11449
|
await stat2(agentDir);
|
|
11190
11450
|
} catch {
|
|
@@ -11192,8 +11452,8 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
11192
11452
|
}
|
|
11193
11453
|
let targetDir = agentDir;
|
|
11194
11454
|
if (dirPath) {
|
|
11195
|
-
const resolved =
|
|
11196
|
-
if (!resolved.startsWith(agentDir +
|
|
11455
|
+
const resolved = path12.resolve(agentDir, dirPath);
|
|
11456
|
+
if (!resolved.startsWith(agentDir + path12.sep) && resolved !== agentDir) {
|
|
11197
11457
|
return [];
|
|
11198
11458
|
}
|
|
11199
11459
|
targetDir = resolved;
|
|
@@ -11201,14 +11461,14 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
11201
11461
|
return this.listDirectoryChildren(targetDir, agentDir);
|
|
11202
11462
|
}
|
|
11203
11463
|
async readFile(agentId, filePath) {
|
|
11204
|
-
const agentDir =
|
|
11205
|
-
const resolved =
|
|
11206
|
-
if (!resolved.startsWith(agentDir +
|
|
11464
|
+
const agentDir = path12.join(this.dataDir, agentId);
|
|
11465
|
+
const resolved = path12.resolve(agentDir, filePath);
|
|
11466
|
+
if (!resolved.startsWith(agentDir + path12.sep) && resolved !== agentDir) {
|
|
11207
11467
|
throw new Error("Access denied");
|
|
11208
11468
|
}
|
|
11209
11469
|
const info = await stat2(resolved);
|
|
11210
11470
|
if (info.isDirectory()) throw new Error("Cannot read a directory");
|
|
11211
|
-
const ext =
|
|
11471
|
+
const ext = path12.extname(resolved).toLowerCase();
|
|
11212
11472
|
if (WORKSPACE_TEXT_EXTENSIONS.has(ext) || ext === "") {
|
|
11213
11473
|
if (info.size > WORKSPACE_TEXT_FILE_MAX_BYTES) throw new Error("File too large");
|
|
11214
11474
|
const content = await readFile(resolved, "utf-8");
|
|
@@ -11241,15 +11501,17 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
11241
11501
|
};
|
|
11242
11502
|
async listSkills(agentId, runtimeHint) {
|
|
11243
11503
|
const agent = this.agents.get(agentId);
|
|
11244
|
-
const
|
|
11245
|
-
const
|
|
11246
|
-
const
|
|
11504
|
+
const idle = this.idleAgentConfigs.get(agentId);
|
|
11505
|
+
const config = agent?.config ?? idle?.config ?? null;
|
|
11506
|
+
const runtime = runtimeHint || config?.runtime || "claude";
|
|
11507
|
+
const workspaceDir = path12.join(this.dataDir, agentId);
|
|
11508
|
+
const home = config ? ensureRuntimeHomeDir(config, os6.homedir(), workspaceDir) : os6.homedir();
|
|
11247
11509
|
const paths = _AgentProcessManager.SKILL_PATHS[runtime] || _AgentProcessManager.SKILL_PATHS.claude;
|
|
11248
11510
|
const globalResults = await Promise.all(
|
|
11249
|
-
paths.global.map((p) => this.scanSkillsDir(
|
|
11511
|
+
paths.global.map((p) => this.scanSkillsDir(path12.join(home, p)))
|
|
11250
11512
|
);
|
|
11251
11513
|
const workspaceResults = await Promise.all(
|
|
11252
|
-
paths.workspace.map((p) => this.scanSkillsDir(
|
|
11514
|
+
paths.workspace.map((p) => this.scanSkillsDir(path12.join(workspaceDir, p)))
|
|
11253
11515
|
);
|
|
11254
11516
|
const dedup = (skills) => {
|
|
11255
11517
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -11278,7 +11540,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
11278
11540
|
const skills = [];
|
|
11279
11541
|
for (const entry of entries) {
|
|
11280
11542
|
if (entry.isDirectory() || entry.isSymbolicLink()) {
|
|
11281
|
-
const skillMd =
|
|
11543
|
+
const skillMd = path12.join(dir, entry.name, "SKILL.md");
|
|
11282
11544
|
try {
|
|
11283
11545
|
const content = await readFile(skillMd, "utf-8");
|
|
11284
11546
|
const skill = this.parseSkillMd(entry.name, content);
|
|
@@ -11289,7 +11551,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
11289
11551
|
} else if (entry.name.endsWith(".md")) {
|
|
11290
11552
|
const cmdName = entry.name.replace(/\.md$/, "");
|
|
11291
11553
|
try {
|
|
11292
|
-
const content = await readFile(
|
|
11554
|
+
const content = await readFile(path12.join(dir, entry.name), "utf-8");
|
|
11293
11555
|
const skill = this.parseSkillMd(cmdName, content);
|
|
11294
11556
|
skill.sourcePath = dir;
|
|
11295
11557
|
skills.push(skill);
|
|
@@ -12693,12 +12955,12 @@ ${formatAgentInboxDelta(inboxRows, { totalPendingMessages: inboxCount })}]`;
|
|
|
12693
12955
|
const traceSource = options.transient ? `stdin_${mode}_transient_delivery` : `stdin_${mode}_delivery`;
|
|
12694
12956
|
const prompt = formatRuntimeProfileControlPrompt(messages) ?? (messages.length === 1 ? `New message received:
|
|
12695
12957
|
|
|
12696
|
-
${formatIncomingMessage(messages[0]
|
|
12958
|
+
${formatIncomingMessage(messages[0])}
|
|
12697
12959
|
|
|
12698
12960
|
Respond as appropriate. Complete all your work before stopping.
|
|
12699
12961
|
${RESPONSE_TARGET_HINT}` : `New messages received:
|
|
12700
12962
|
|
|
12701
|
-
${messages.map((message) => formatIncomingMessage(message
|
|
12963
|
+
${messages.map((message) => formatIncomingMessage(message)).join("\n")}
|
|
12702
12964
|
|
|
12703
12965
|
Respond as appropriate. Complete all your work before stopping.
|
|
12704
12966
|
${RESPONSE_TARGET_HINT}`);
|
|
@@ -12769,8 +13031,8 @@ ${RESPONSE_TARGET_HINT}`);
|
|
|
12769
13031
|
const nodes = [];
|
|
12770
13032
|
for (const entry of entries) {
|
|
12771
13033
|
if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
|
|
12772
|
-
const fullPath =
|
|
12773
|
-
const relativePath =
|
|
13034
|
+
const fullPath = path12.join(dir, entry.name);
|
|
13035
|
+
const relativePath = path12.relative(rootDir, fullPath);
|
|
12774
13036
|
let info;
|
|
12775
13037
|
try {
|
|
12776
13038
|
info = await stat2(fullPath);
|
|
@@ -13144,9 +13406,9 @@ var ReminderCache = class {
|
|
|
13144
13406
|
|
|
13145
13407
|
// src/machineLock.ts
|
|
13146
13408
|
import { createHash as createHash4, randomUUID as randomUUID4 } from "crypto";
|
|
13147
|
-
import { mkdirSync as
|
|
13148
|
-
import
|
|
13149
|
-
import
|
|
13409
|
+
import { mkdirSync as mkdirSync5, readFileSync as readFileSync5, rmSync as rmSync3, statSync as statSync2, writeFileSync as writeFileSync5 } from "fs";
|
|
13410
|
+
import os7 from "os";
|
|
13411
|
+
import path13 from "path";
|
|
13150
13412
|
var INCOMPLETE_LOCK_STALE_MS = 3e4;
|
|
13151
13413
|
var DaemonMachineLockConflictError = class extends Error {
|
|
13152
13414
|
code = "DAEMON_MACHINE_LOCK_HELD";
|
|
@@ -13168,7 +13430,7 @@ function resolveDefaultMachineStateRoot() {
|
|
|
13168
13430
|
return resolveSlockHomePath("machines");
|
|
13169
13431
|
}
|
|
13170
13432
|
function ownerPath(lockDir) {
|
|
13171
|
-
return
|
|
13433
|
+
return path13.join(lockDir, "owner.json");
|
|
13172
13434
|
}
|
|
13173
13435
|
function readOwner(lockDir) {
|
|
13174
13436
|
try {
|
|
@@ -13198,17 +13460,17 @@ function acquireDaemonMachineLock(options) {
|
|
|
13198
13460
|
const rootDir = options.rootDir ?? resolveDefaultMachineStateRoot();
|
|
13199
13461
|
const fingerprint = apiKeyFingerprint(options.apiKey);
|
|
13200
13462
|
const lockId = getDaemonMachineLockId(options.apiKey);
|
|
13201
|
-
const machineDir =
|
|
13202
|
-
const lockDir =
|
|
13463
|
+
const machineDir = path13.join(rootDir, lockId);
|
|
13464
|
+
const lockDir = path13.join(machineDir, "daemon.lock");
|
|
13203
13465
|
const token = randomUUID4();
|
|
13204
|
-
|
|
13466
|
+
mkdirSync5(machineDir, { recursive: true });
|
|
13205
13467
|
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
13206
13468
|
try {
|
|
13207
|
-
|
|
13469
|
+
mkdirSync5(lockDir);
|
|
13208
13470
|
const owner = {
|
|
13209
13471
|
pid: process.pid,
|
|
13210
13472
|
token,
|
|
13211
|
-
hostname:
|
|
13473
|
+
hostname: os7.hostname(),
|
|
13212
13474
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
13213
13475
|
serverUrl: options.serverUrl,
|
|
13214
13476
|
apiKeyFingerprint: fingerprint.slice(0, 16)
|
|
@@ -13259,8 +13521,8 @@ function acquireDaemonMachineLock(options) {
|
|
|
13259
13521
|
}
|
|
13260
13522
|
|
|
13261
13523
|
// src/localTraceSink.ts
|
|
13262
|
-
import { appendFileSync, mkdirSync as
|
|
13263
|
-
import
|
|
13524
|
+
import { appendFileSync, mkdirSync as mkdirSync6, readdirSync as readdirSync3, rmSync as rmSync4, statSync as statSync3, writeFileSync as writeFileSync6 } from "fs";
|
|
13525
|
+
import path14 from "path";
|
|
13264
13526
|
var DEFAULT_MAX_FILE_BYTES = 5 * 1024 * 1024;
|
|
13265
13527
|
var DEFAULT_MAX_FILE_AGE_MS = 5 * 60 * 1e3;
|
|
13266
13528
|
var DEFAULT_MAX_FILES = 8;
|
|
@@ -13297,7 +13559,7 @@ var LocalRotatingTraceSink = class {
|
|
|
13297
13559
|
currentSize = 0;
|
|
13298
13560
|
sequence = 0;
|
|
13299
13561
|
constructor(options) {
|
|
13300
|
-
this.traceDir =
|
|
13562
|
+
this.traceDir = path14.join(options.machineDir, "traces");
|
|
13301
13563
|
this.maxFileBytes = Math.max(1024, Math.floor(options.maxFileBytes ?? DEFAULT_MAX_FILE_BYTES));
|
|
13302
13564
|
const baseAgeMs = Math.max(1e3, Math.floor(options.maxFileAgeMs ?? DEFAULT_MAX_FILE_AGE_MS));
|
|
13303
13565
|
const ageJitterMs = Math.max(0, Math.floor(options.maxFileAgeJitterMs ?? 0));
|
|
@@ -13323,11 +13585,11 @@ var LocalRotatingTraceSink = class {
|
|
|
13323
13585
|
return this.currentFile;
|
|
13324
13586
|
}
|
|
13325
13587
|
ensureFile(nextBytes) {
|
|
13326
|
-
|
|
13588
|
+
mkdirSync6(this.traceDir, { recursive: true, mode: 448 });
|
|
13327
13589
|
const nowMs = this.nowMsProvider();
|
|
13328
13590
|
const shouldRotateForAge = this.currentFileOpenedAtMs !== null && nowMs - this.currentFileOpenedAtMs >= this.maxFileAgeMs;
|
|
13329
13591
|
if (!this.currentFile || this.currentSize + nextBytes > this.maxFileBytes || shouldRotateForAge) {
|
|
13330
|
-
this.currentFile =
|
|
13592
|
+
this.currentFile = path14.join(
|
|
13331
13593
|
this.traceDir,
|
|
13332
13594
|
`daemon-trace-${safeTimestamp(nowMs)}-${process.pid}-${String(this.sequence++).padStart(4, "0")}.jsonl`
|
|
13333
13595
|
);
|
|
@@ -13342,7 +13604,7 @@ var LocalRotatingTraceSink = class {
|
|
|
13342
13604
|
const excess = files.length - this.maxFiles;
|
|
13343
13605
|
if (excess <= 0) return;
|
|
13344
13606
|
for (const file of files.slice(0, excess)) {
|
|
13345
|
-
rmSync4(
|
|
13607
|
+
rmSync4(path14.join(this.traceDir, file), { force: true });
|
|
13346
13608
|
}
|
|
13347
13609
|
}
|
|
13348
13610
|
};
|
|
@@ -13433,7 +13695,7 @@ function isDiagnosticErrorAttr(key) {
|
|
|
13433
13695
|
import { createHash as createHash6, randomUUID as randomUUID5 } from "crypto";
|
|
13434
13696
|
import { gzipSync } from "zlib";
|
|
13435
13697
|
import { mkdir as mkdir2, readFile as readFile2, readdir as readdir3, stat as stat3, writeFile as writeFile2 } from "fs/promises";
|
|
13436
|
-
import
|
|
13698
|
+
import path15 from "path";
|
|
13437
13699
|
|
|
13438
13700
|
// src/chatBridgeRequest.ts
|
|
13439
13701
|
var DEFAULT_CHAT_BRIDGE_TOOL_TIMEOUT_MS = Number.parseInt(
|
|
@@ -13533,8 +13795,8 @@ async function executeResponseRequest(url, init, {
|
|
|
13533
13795
|
}
|
|
13534
13796
|
|
|
13535
13797
|
// src/directUploadCapability.ts
|
|
13536
|
-
function joinUrl(base,
|
|
13537
|
-
return `${base.replace(/\/+$/, "")}${
|
|
13798
|
+
function joinUrl(base, path17) {
|
|
13799
|
+
return `${base.replace(/\/+$/, "")}${path17}`;
|
|
13538
13800
|
}
|
|
13539
13801
|
function jsonHeaders(apiKey) {
|
|
13540
13802
|
return {
|
|
@@ -13753,7 +14015,7 @@ var DaemonTraceBundleUploader = class {
|
|
|
13753
14015
|
}, nextMs);
|
|
13754
14016
|
}
|
|
13755
14017
|
async findUploadCandidates() {
|
|
13756
|
-
const traceDir =
|
|
14018
|
+
const traceDir = path15.join(this.options.machineDir, "traces");
|
|
13757
14019
|
let names;
|
|
13758
14020
|
try {
|
|
13759
14021
|
names = await readdir3(traceDir);
|
|
@@ -13765,8 +14027,8 @@ var DaemonTraceBundleUploader = class {
|
|
|
13765
14027
|
const currentFile = this.options.currentFileProvider?.();
|
|
13766
14028
|
const candidates = [];
|
|
13767
14029
|
for (const name of names.filter((entry) => entry.startsWith("daemon-trace-") && entry.endsWith(".jsonl")).sort()) {
|
|
13768
|
-
const file =
|
|
13769
|
-
if (currentFile &&
|
|
14030
|
+
const file = path15.join(traceDir, name);
|
|
14031
|
+
if (currentFile && path15.resolve(file) === path15.resolve(currentFile)) continue;
|
|
13770
14032
|
if (await this.isUploaded(file)) continue;
|
|
13771
14033
|
try {
|
|
13772
14034
|
const info = await stat3(file);
|
|
@@ -13840,8 +14102,8 @@ var DaemonTraceBundleUploader = class {
|
|
|
13840
14102
|
}
|
|
13841
14103
|
}
|
|
13842
14104
|
uploadStatePath(file) {
|
|
13843
|
-
const stateDir =
|
|
13844
|
-
return
|
|
14105
|
+
const stateDir = path15.join(this.options.machineDir, "trace-uploads");
|
|
14106
|
+
return path15.join(stateDir, `${path15.basename(file)}.uploaded.json`);
|
|
13845
14107
|
}
|
|
13846
14108
|
async isUploaded(file) {
|
|
13847
14109
|
try {
|
|
@@ -13853,9 +14115,9 @@ var DaemonTraceBundleUploader = class {
|
|
|
13853
14115
|
}
|
|
13854
14116
|
async markUploaded(file, metadata) {
|
|
13855
14117
|
const stateFile = this.uploadStatePath(file);
|
|
13856
|
-
await mkdir2(
|
|
14118
|
+
await mkdir2(path15.dirname(stateFile), { recursive: true, mode: 448 });
|
|
13857
14119
|
await writeFile2(stateFile, `${JSON.stringify({
|
|
13858
|
-
file:
|
|
14120
|
+
file: path15.basename(file),
|
|
13859
14121
|
uploadedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
13860
14122
|
...metadata
|
|
13861
14123
|
}, null, 2)}
|
|
@@ -13922,7 +14184,7 @@ var DAEMON_CORE_TRACE_ATTR_CONTRACTS = {
|
|
|
13922
14184
|
spanAttrs: ["running_agents_count", "idle_agents_count"]
|
|
13923
14185
|
}
|
|
13924
14186
|
};
|
|
13925
|
-
var DAEMON_CLI_USAGE =
|
|
14187
|
+
var DAEMON_CLI_USAGE = `Usage: slock-daemon --server-url <url> (--api-key <key> or ${DAEMON_API_KEY_ENV}=<key>)`;
|
|
13926
14188
|
var RunnerCredentialMintError2 = class extends Error {
|
|
13927
14189
|
code;
|
|
13928
14190
|
retryable;
|
|
@@ -13958,9 +14220,9 @@ function runnerCredentialErrorDetail2(error) {
|
|
|
13958
14220
|
async function waitForRunnerCredentialRetry2() {
|
|
13959
14221
|
await new Promise((resolve) => setTimeout(resolve, RUNNER_CREDENTIAL_MINT_RETRY_DELAY_MS2));
|
|
13960
14222
|
}
|
|
13961
|
-
function parseDaemonCliArgs(args) {
|
|
14223
|
+
function parseDaemonCliArgs(args, env = {}) {
|
|
13962
14224
|
let serverUrl = "";
|
|
13963
|
-
let apiKey = "";
|
|
14225
|
+
let apiKey = env[DAEMON_API_KEY_ENV] ?? "";
|
|
13964
14226
|
for (let i = 0; i < args.length; i++) {
|
|
13965
14227
|
if (args[i] === "--server-url" && args[i + 1]) serverUrl = args[++i];
|
|
13966
14228
|
if (args[i] === "--api-key" && args[i + 1]) apiKey = args[++i];
|
|
@@ -13977,13 +14239,13 @@ function readDaemonVersion(moduleUrl = import.meta.url) {
|
|
|
13977
14239
|
}
|
|
13978
14240
|
}
|
|
13979
14241
|
function resolveSlockCliPath(moduleUrl = import.meta.url) {
|
|
13980
|
-
const thisDir =
|
|
13981
|
-
const bundledDistPath =
|
|
14242
|
+
const thisDir = path16.dirname(fileURLToPath(moduleUrl));
|
|
14243
|
+
const bundledDistPath = path16.resolve(thisDir, "cli", "index.js");
|
|
13982
14244
|
try {
|
|
13983
14245
|
accessSync(bundledDistPath);
|
|
13984
14246
|
return bundledDistPath;
|
|
13985
14247
|
} catch {
|
|
13986
|
-
const workspaceDistPath =
|
|
14248
|
+
const workspaceDistPath = path16.resolve(thisDir, "..", "..", "cli", "dist", "index.js");
|
|
13987
14249
|
accessSync(workspaceDistPath);
|
|
13988
14250
|
return workspaceDistPath;
|
|
13989
14251
|
}
|
|
@@ -14172,7 +14434,7 @@ var DaemonCore = class {
|
|
|
14172
14434
|
}
|
|
14173
14435
|
resolveMachineStateRoot() {
|
|
14174
14436
|
if (this.options.machineStateDir) return this.options.machineStateDir;
|
|
14175
|
-
if (this.options.dataDir) return
|
|
14437
|
+
if (this.options.dataDir) return path16.join(path16.dirname(this.options.dataDir), "machines");
|
|
14176
14438
|
return resolveDefaultMachineStateRoot();
|
|
14177
14439
|
}
|
|
14178
14440
|
shouldEnableLocalTrace() {
|
|
@@ -14199,7 +14461,7 @@ var DaemonCore = class {
|
|
|
14199
14461
|
sink: this.localTraceSink
|
|
14200
14462
|
}));
|
|
14201
14463
|
this.agentManager.setTracer(this.tracer);
|
|
14202
|
-
this.agentManager.setCliTransportTraceDir(
|
|
14464
|
+
this.agentManager.setCliTransportTraceDir(path16.join(machineDir, "traces"));
|
|
14203
14465
|
}
|
|
14204
14466
|
installTraceBundleUploader(machineDir) {
|
|
14205
14467
|
if (!this.shouldEnableLocalTrace()) return;
|
|
@@ -14676,8 +14938,8 @@ var DaemonCore = class {
|
|
|
14676
14938
|
capabilities: ["agent:start", "agent:stop", "agent:deliver", "workspace:files"],
|
|
14677
14939
|
runtimes,
|
|
14678
14940
|
runningAgents: runningAgentIds,
|
|
14679
|
-
hostname: this.options.hostname ??
|
|
14680
|
-
os: this.options.osDescription ?? `${
|
|
14941
|
+
hostname: this.options.hostname ?? os8.hostname(),
|
|
14942
|
+
os: this.options.osDescription ?? `${os8.platform()} ${os8.arch()}`,
|
|
14681
14943
|
daemonVersion: this.daemonVersion,
|
|
14682
14944
|
...this.computerVersion ? { computerVersion: this.computerVersion } : {}
|
|
14683
14945
|
});
|
|
@@ -14741,6 +15003,8 @@ var DaemonCore = class {
|
|
|
14741
15003
|
};
|
|
14742
15004
|
|
|
14743
15005
|
export {
|
|
15006
|
+
DAEMON_API_KEY_ENV,
|
|
15007
|
+
scrubDaemonAuthEnv,
|
|
14744
15008
|
subscribeDaemonLogs,
|
|
14745
15009
|
resolveWorkspaceDirectoryPath,
|
|
14746
15010
|
scanWorkspaceDirectories,
|