dorkos 0.17.2 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -30
- package/dist/bin/cli.js +24 -21
- package/dist/client/assets/{TopologyGraph-8CSPQVAk.js → TopologyGraph-DFBezs3i.js} +18 -34
- package/dist/client/assets/{highlighted-body-TPN3WLV5-DFPa3ej5.js → highlighted-body-TPN3WLV5-BNDjU42J.js} +1 -1
- package/dist/client/assets/index-CrIkghhQ.css +1 -0
- package/dist/client/assets/index-D56GKerz.js +957 -0
- package/dist/client/index.html +2 -2
- package/dist/drizzle/meta/0000_snapshot.json +6 -16
- package/dist/drizzle/meta/0001_snapshot.json +6 -16
- package/dist/drizzle/meta/0002_snapshot.json +7 -20
- package/dist/drizzle/meta/0003_snapshot.json +7 -20
- package/dist/drizzle/meta/0004_snapshot.json +7 -20
- package/dist/drizzle/meta/0005_snapshot.json +7 -20
- package/dist/drizzle/meta/0006_snapshot.json +7 -20
- package/dist/drizzle/meta/_journal.json +1 -1
- package/dist/server/index.js +539 -296
- package/dist/server/index.js.map +3 -3
- package/package.json +3 -3
- package/dist/client/assets/index-1b_G52ou.css +0 -1
- package/dist/client/assets/index-BktypfRN.js +0 -871
package/dist/server/index.js
CHANGED
|
@@ -17494,12 +17494,18 @@ var init_scenario_store = __esm({
|
|
|
17494
17494
|
"use strict";
|
|
17495
17495
|
BUILT_IN_SCENARIOS = {
|
|
17496
17496
|
"simple-text": async function* (content3) {
|
|
17497
|
-
yield {
|
|
17497
|
+
yield {
|
|
17498
|
+
type: "session_status",
|
|
17499
|
+
data: { sessionId: "test-mode", model: "claude-haiku-4-5" }
|
|
17500
|
+
};
|
|
17498
17501
|
yield { type: "text_delta", data: { text: `Echo: ${content3}` } };
|
|
17499
17502
|
yield { type: "done", data: { sessionId: "test-mode" } };
|
|
17500
17503
|
},
|
|
17501
17504
|
"tool-call": async function* (_content) {
|
|
17502
|
-
yield {
|
|
17505
|
+
yield {
|
|
17506
|
+
type: "session_status",
|
|
17507
|
+
data: { sessionId: "test-mode", model: "claude-haiku-4-5" }
|
|
17508
|
+
};
|
|
17503
17509
|
yield {
|
|
17504
17510
|
type: "tool_call_start",
|
|
17505
17511
|
data: { toolCallId: "tc-1", toolName: "Bash", status: "running" }
|
|
@@ -17507,7 +17513,12 @@ var init_scenario_store = __esm({
|
|
|
17507
17513
|
yield {
|
|
17508
17514
|
type: "tool_call_delta",
|
|
17509
17515
|
// tool_call_delta uses ToolCallEventSchema: toolCallId, toolName, status are required
|
|
17510
|
-
data: {
|
|
17516
|
+
data: {
|
|
17517
|
+
toolCallId: "tc-1",
|
|
17518
|
+
toolName: "Bash",
|
|
17519
|
+
input: '{"command":"echo hi"}',
|
|
17520
|
+
status: "running"
|
|
17521
|
+
}
|
|
17511
17522
|
};
|
|
17512
17523
|
yield {
|
|
17513
17524
|
type: "tool_call_end",
|
|
@@ -17517,7 +17528,10 @@ var init_scenario_store = __esm({
|
|
|
17517
17528
|
yield { type: "done", data: { sessionId: "test-mode" } };
|
|
17518
17529
|
},
|
|
17519
17530
|
"todo-write": async function* (_content) {
|
|
17520
|
-
yield {
|
|
17531
|
+
yield {
|
|
17532
|
+
type: "session_status",
|
|
17533
|
+
data: { sessionId: "test-mode", model: "claude-haiku-4-5" }
|
|
17534
|
+
};
|
|
17521
17535
|
yield {
|
|
17522
17536
|
type: "task_update",
|
|
17523
17537
|
data: { action: "create", task: { id: "1", subject: "Task one", status: "pending" } }
|
|
@@ -17533,9 +17547,15 @@ var init_scenario_store = __esm({
|
|
|
17533
17547
|
yield { type: "text_delta", data: { text: "Created 3 tasks." } };
|
|
17534
17548
|
yield { type: "done", data: { sessionId: "test-mode" } };
|
|
17535
17549
|
},
|
|
17536
|
-
|
|
17537
|
-
yield {
|
|
17538
|
-
|
|
17550
|
+
error: async function* (_content) {
|
|
17551
|
+
yield {
|
|
17552
|
+
type: "session_status",
|
|
17553
|
+
data: { sessionId: "test-mode", model: "claude-haiku-4-5" }
|
|
17554
|
+
};
|
|
17555
|
+
yield {
|
|
17556
|
+
type: "error",
|
|
17557
|
+
data: { message: "Simulated error from TestModeRuntime" }
|
|
17558
|
+
};
|
|
17539
17559
|
yield { type: "done", data: { sessionId: "test-mode" } };
|
|
17540
17560
|
}
|
|
17541
17561
|
};
|
|
@@ -55755,7 +55775,7 @@ var require_websocket = __commonJS({
|
|
|
55755
55775
|
var http = __require("http");
|
|
55756
55776
|
var net = __require("net");
|
|
55757
55777
|
var tls = __require("tls");
|
|
55758
|
-
var { randomBytes: randomBytes2, createHash:
|
|
55778
|
+
var { randomBytes: randomBytes2, createHash: createHash2 } = __require("crypto");
|
|
55759
55779
|
var { Duplex, Readable: Readable3 } = __require("stream");
|
|
55760
55780
|
var { URL: URL2 } = __require("url");
|
|
55761
55781
|
var PerMessageDeflate = require_permessage_deflate();
|
|
@@ -56415,7 +56435,7 @@ var require_websocket = __commonJS({
|
|
|
56415
56435
|
abortHandshake(websocket, socket, "Invalid Upgrade header");
|
|
56416
56436
|
return;
|
|
56417
56437
|
}
|
|
56418
|
-
const digest =
|
|
56438
|
+
const digest = createHash2("sha1").update(key + GUID).digest("base64");
|
|
56419
56439
|
if (res.headers["sec-websocket-accept"] !== digest) {
|
|
56420
56440
|
abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
|
|
56421
56441
|
return;
|
|
@@ -56782,7 +56802,7 @@ var require_websocket_server = __commonJS({
|
|
|
56782
56802
|
var EventEmitter4 = __require("events");
|
|
56783
56803
|
var http = __require("http");
|
|
56784
56804
|
var { Duplex } = __require("stream");
|
|
56785
|
-
var { createHash:
|
|
56805
|
+
var { createHash: createHash2 } = __require("crypto");
|
|
56786
56806
|
var extension2 = require_extension();
|
|
56787
56807
|
var PerMessageDeflate = require_permessage_deflate();
|
|
56788
56808
|
var subprotocol = require_subprotocol();
|
|
@@ -57083,7 +57103,7 @@ var require_websocket_server = __commonJS({
|
|
|
57083
57103
|
);
|
|
57084
57104
|
}
|
|
57085
57105
|
if (this._state > RUNNING) return abortHandshake(socket, 503);
|
|
57086
|
-
const digest =
|
|
57106
|
+
const digest = createHash2("sha1").update(key + GUID).digest("base64");
|
|
57087
57107
|
const headers = [
|
|
57088
57108
|
"HTTP/1.1 101 Switching Protocols",
|
|
57089
57109
|
"Upgrade: websocket",
|
|
@@ -68952,7 +68972,8 @@ var RateLimitEventSchema = z.object({
|
|
|
68952
68972
|
retryAfter: z.number().optional()
|
|
68953
68973
|
}).openapi("RateLimitEvent");
|
|
68954
68974
|
var DoneEventSchema = z.object({
|
|
68955
|
-
sessionId: z.string()
|
|
68975
|
+
sessionId: z.string(),
|
|
68976
|
+
messageIds: z.object({ user: z.string(), assistant: z.string() }).optional()
|
|
68956
68977
|
}).openapi("DoneEvent");
|
|
68957
68978
|
var SessionStatusEventSchema = z.object({
|
|
68958
68979
|
sessionId: z.string(),
|
|
@@ -69220,7 +69241,9 @@ var HealthResponseSchema = z.object({
|
|
|
69220
69241
|
}).openapi("HealthResponse");
|
|
69221
69242
|
var ServerConfigSchema = z.object({
|
|
69222
69243
|
version: z.string().openapi({ description: "Current server version" }),
|
|
69223
|
-
latestVersion: z.string().nullable().openapi({
|
|
69244
|
+
latestVersion: z.string().nullable().openapi({
|
|
69245
|
+
description: "Latest available version from npm, or null if dev mode or unknown"
|
|
69246
|
+
}),
|
|
69224
69247
|
isDevMode: z.boolean().openapi({ description: "Whether the server is running a development build" }),
|
|
69225
69248
|
dismissedUpgradeVersions: z.array(z.string()).openapi({ description: "Versions the user has dismissed upgrade notifications for" }),
|
|
69226
69249
|
port: z.number().int(),
|
|
@@ -70918,7 +70941,12 @@ router.post("/:id/approve", async (req, res) => {
|
|
|
70918
70941
|
const { toolCallId } = parsed.data;
|
|
70919
70942
|
const runtime = runtimeRegistry.getDefault();
|
|
70920
70943
|
const approved = runtime.approveTool(sessionId, toolCallId, true);
|
|
70921
|
-
if (!approved)
|
|
70944
|
+
if (!approved) {
|
|
70945
|
+
if (runtime.hasSession(sessionId)) {
|
|
70946
|
+
return sendError(res, 409, "Interaction already resolved", "INTERACTION_ALREADY_RESOLVED");
|
|
70947
|
+
}
|
|
70948
|
+
return sendError(res, 404, "No pending approval", "NO_PENDING_APPROVAL");
|
|
70949
|
+
}
|
|
70922
70950
|
res.json({ ok: true });
|
|
70923
70951
|
});
|
|
70924
70952
|
router.post("/:id/deny", async (req, res) => {
|
|
@@ -70931,7 +70959,12 @@ router.post("/:id/deny", async (req, res) => {
|
|
|
70931
70959
|
const { toolCallId } = parsed.data;
|
|
70932
70960
|
const runtime = runtimeRegistry.getDefault();
|
|
70933
70961
|
const denied = runtime.approveTool(sessionId, toolCallId, false);
|
|
70934
|
-
if (!denied)
|
|
70962
|
+
if (!denied) {
|
|
70963
|
+
if (runtime.hasSession(sessionId)) {
|
|
70964
|
+
return sendError(res, 409, "Interaction already resolved", "INTERACTION_ALREADY_RESOLVED");
|
|
70965
|
+
}
|
|
70966
|
+
return sendError(res, 404, "No pending approval", "NO_PENDING_APPROVAL");
|
|
70967
|
+
}
|
|
70935
70968
|
res.json({ ok: true });
|
|
70936
70969
|
});
|
|
70937
70970
|
router.post("/:id/submit-answers", async (req, res) => {
|
|
@@ -70944,7 +70977,12 @@ router.post("/:id/submit-answers", async (req, res) => {
|
|
|
70944
70977
|
const { toolCallId, answers } = parsed.data;
|
|
70945
70978
|
const runtime = runtimeRegistry.getDefault();
|
|
70946
70979
|
const ok3 = runtime.submitAnswers(sessionId, toolCallId, answers);
|
|
70947
|
-
if (!ok3)
|
|
70980
|
+
if (!ok3) {
|
|
70981
|
+
if (runtime.hasSession(sessionId)) {
|
|
70982
|
+
return sendError(res, 409, "Interaction already resolved", "INTERACTION_ALREADY_RESOLVED");
|
|
70983
|
+
}
|
|
70984
|
+
return sendError(res, 404, "No pending question", "NO_PENDING_QUESTION");
|
|
70985
|
+
}
|
|
70948
70986
|
res.json({ ok: true });
|
|
70949
70987
|
});
|
|
70950
70988
|
router.get("/:id/stream", async (req, res) => {
|
|
@@ -71071,7 +71109,7 @@ var SERVER_VERSION = resolveVersion();
|
|
|
71071
71109
|
var IS_DEV_BUILD = checkDevBuild(SERVER_VERSION);
|
|
71072
71110
|
function resolveVersion() {
|
|
71073
71111
|
if (env.DORKOS_VERSION_OVERRIDE) return env.DORKOS_VERSION_OVERRIDE;
|
|
71074
|
-
if (true) return "0.
|
|
71112
|
+
if (true) return "0.19.0";
|
|
71075
71113
|
const pkgPath = path4.join(path4.dirname(fileURLToPath2(import.meta.url)), "../../package.json");
|
|
71076
71114
|
return JSON.parse(readFileSync(pkgPath, "utf-8")).version;
|
|
71077
71115
|
}
|
|
@@ -71115,7 +71153,8 @@ router4.get("/", async (req, res) => {
|
|
|
71115
71153
|
resolved = await validateBoundary(targetPath);
|
|
71116
71154
|
} catch (err) {
|
|
71117
71155
|
if (err instanceof BoundaryError) {
|
|
71118
|
-
if (err.code === "NULL_BYTE")
|
|
71156
|
+
if (err.code === "NULL_BYTE")
|
|
71157
|
+
return res.status(400).json({ error: err.message, code: err.code });
|
|
71119
71158
|
return res.status(403).json({ error: err.message, code: err.code });
|
|
71120
71159
|
}
|
|
71121
71160
|
const code3 = err.code;
|
|
@@ -71186,10 +71225,7 @@ import path6 from "path";
|
|
|
71186
71225
|
|
|
71187
71226
|
// ../shared/dist/config-schema.js
|
|
71188
71227
|
import { z as z5 } from "zod";
|
|
71189
|
-
var SENSITIVE_CONFIG_KEYS = [
|
|
71190
|
-
"tunnel.authtoken",
|
|
71191
|
-
"tunnel.auth"
|
|
71192
|
-
];
|
|
71228
|
+
var SENSITIVE_CONFIG_KEYS = ["tunnel.authtoken", "tunnel.auth"];
|
|
71193
71229
|
var ONBOARDING_STEPS = ["discovery", "pulse", "adapters"];
|
|
71194
71230
|
var OnboardingStepSchema = z5.enum(ONBOARDING_STEPS);
|
|
71195
71231
|
var OnboardingStateSchema = z5.object({
|
|
@@ -71220,7 +71256,11 @@ var UserConfigSchema = z5.object({
|
|
|
71220
71256
|
theme: z5.enum(["light", "dark", "system"]).default("system"),
|
|
71221
71257
|
dismissedUpgradeVersions: z5.array(z5.string()).default(() => []).describe("Version strings the user has dismissed upgrade notifications for")
|
|
71222
71258
|
}).default(() => ({ theme: "system", dismissedUpgradeVersions: [] })),
|
|
71223
|
-
logging: LoggingConfigSchema.default(() => ({
|
|
71259
|
+
logging: LoggingConfigSchema.default(() => ({
|
|
71260
|
+
level: "info",
|
|
71261
|
+
maxLogSizeKb: 500,
|
|
71262
|
+
maxLogFiles: 14
|
|
71263
|
+
})),
|
|
71224
71264
|
relay: z5.object({
|
|
71225
71265
|
enabled: z5.boolean().default(true),
|
|
71226
71266
|
dataDir: z5.string().nullable().default(null)
|
|
@@ -71360,9 +71400,7 @@ var ConfigManager = class {
|
|
|
71360
71400
|
if (error instanceof z6.ZodError) {
|
|
71361
71401
|
return {
|
|
71362
71402
|
valid: false,
|
|
71363
|
-
errors: error.issues.map(
|
|
71364
|
-
(i2) => `${i2.path.join(".")}: ${i2.message}`
|
|
71365
|
-
)
|
|
71403
|
+
errors: error.issues.map((i2) => `${i2.path.join(".")}: ${i2.message}`)
|
|
71366
71404
|
};
|
|
71367
71405
|
}
|
|
71368
71406
|
throw error;
|
|
@@ -71858,12 +71896,12 @@ var SDK_TOOL_NAMES = {
|
|
|
71858
71896
|
};
|
|
71859
71897
|
|
|
71860
71898
|
// ../../apps/server/src/routes/tunnel.ts
|
|
71861
|
-
var DEV_CLIENT_PORT = Number(process.env.VITE_PORT) || 4241;
|
|
71862
71899
|
var router8 = Router8();
|
|
71863
71900
|
function resolveTunnelPort() {
|
|
71864
71901
|
if (process.env.TUNNEL_PORT) return Number(process.env.TUNNEL_PORT);
|
|
71865
71902
|
const isDev = process.env.NODE_ENV !== "production";
|
|
71866
|
-
|
|
71903
|
+
const devClientPort = Number(process.env.VITE_PORT) || 4241;
|
|
71904
|
+
return isDev ? devClientPort : Number(process.env.DORKOS_PORT) || DEFAULT_PORT;
|
|
71867
71905
|
}
|
|
71868
71906
|
router8.get("/status", (_req, res) => {
|
|
71869
71907
|
res.json(tunnelManager.status);
|
|
@@ -72276,7 +72314,9 @@ var PluginSourceSchema = z11.object({
|
|
|
72276
72314
|
package: z11.string().optional(),
|
|
72277
72315
|
/** Local file path (absolute or relative to config dir) */
|
|
72278
72316
|
path: z11.string().optional()
|
|
72279
|
-
}).refine((data) => data.package || data.path, {
|
|
72317
|
+
}).refine((data) => data.package || data.path, {
|
|
72318
|
+
message: "Plugin source must specify either package or path"
|
|
72319
|
+
}).openapi("PluginSource");
|
|
72280
72320
|
var TelegramAdapterConfigSchema = z11.object({
|
|
72281
72321
|
token: z11.string().min(1),
|
|
72282
72322
|
mode: z11.enum(["polling", "webhook"]).default("polling"),
|
|
@@ -72594,7 +72634,6 @@ var DiscoveryCandidateSchema = z13.object({
|
|
|
72594
72634
|
}).openapi("DiscoveryCandidate");
|
|
72595
72635
|
var DenialRecordSchema = z13.object({
|
|
72596
72636
|
path: z13.string().min(1),
|
|
72597
|
-
strategy: z13.string().min(1),
|
|
72598
72637
|
reason: z13.string().optional(),
|
|
72599
72638
|
deniedBy: z13.string().min(1),
|
|
72600
72639
|
deniedAt: z13.string().datetime()
|
|
@@ -73284,9 +73323,7 @@ registry.registerPath({
|
|
|
73284
73323
|
description: "Array of endpoints",
|
|
73285
73324
|
content: {
|
|
73286
73325
|
"application/json": {
|
|
73287
|
-
schema: z14.array(
|
|
73288
|
-
z14.object({ subject: z14.string(), description: z14.string().optional() })
|
|
73289
|
-
)
|
|
73326
|
+
schema: z14.array(z14.object({ subject: z14.string(), description: z14.string().optional() }))
|
|
73290
73327
|
}
|
|
73291
73328
|
}
|
|
73292
73329
|
}
|
|
@@ -73651,12 +73688,15 @@ function errorHandler(err, _req, res, _next) {
|
|
|
73651
73688
|
function requestLogger(req, res, next) {
|
|
73652
73689
|
const start2 = Date.now();
|
|
73653
73690
|
res.on("finish", () => {
|
|
73654
|
-
logger.debug(
|
|
73655
|
-
|
|
73656
|
-
|
|
73657
|
-
|
|
73658
|
-
|
|
73659
|
-
|
|
73691
|
+
logger.debug(
|
|
73692
|
+
{
|
|
73693
|
+
method: req.method,
|
|
73694
|
+
path: req.path,
|
|
73695
|
+
status: res.statusCode,
|
|
73696
|
+
ms: Date.now() - start2
|
|
73697
|
+
},
|
|
73698
|
+
"request"
|
|
73699
|
+
);
|
|
73660
73700
|
});
|
|
73661
73701
|
next();
|
|
73662
73702
|
}
|
|
@@ -76812,10 +76852,7 @@ data: ${JSON.stringify(event)}
|
|
|
76812
76852
|
data: { sessionId, timestamp: (/* @__PURE__ */ new Date()).toISOString() }
|
|
76813
76853
|
});
|
|
76814
76854
|
} catch (err) {
|
|
76815
|
-
logger.error(
|
|
76816
|
-
`[SessionBroadcaster] Callback error for session ${sessionId}:`,
|
|
76817
|
-
err
|
|
76818
|
-
);
|
|
76855
|
+
logger.error(`[SessionBroadcaster] Callback error for session ${sessionId}:`, err);
|
|
76819
76856
|
}
|
|
76820
76857
|
}
|
|
76821
76858
|
}
|
|
@@ -77129,17 +77166,25 @@ function createCanUseTool(session, logFn) {
|
|
|
77129
77166
|
"mcp__dorkos__mesh_register",
|
|
77130
77167
|
"mcp__dorkos__mesh_status",
|
|
77131
77168
|
"mcp__dorkos__mesh_query_topology",
|
|
77132
|
-
"
|
|
77169
|
+
"mcp__dorkos__get_agent"
|
|
77133
77170
|
]);
|
|
77134
77171
|
if (READ_ONLY_TOOLS.has(toolName) || DORKOS_AGENT_TOOLS.has(toolName)) {
|
|
77135
77172
|
logFn("[canUseTool] auto-allow safe tool", { toolName, toolUseID: context.toolUseID });
|
|
77136
77173
|
return { behavior: "allow", updatedInput: input };
|
|
77137
77174
|
}
|
|
77138
77175
|
if (session.permissionMode === "default") {
|
|
77139
|
-
logFn("[canUseTool] requesting approval", {
|
|
77176
|
+
logFn("[canUseTool] requesting approval", {
|
|
77177
|
+
toolName,
|
|
77178
|
+
permissionMode: "default",
|
|
77179
|
+
toolUseID: context.toolUseID
|
|
77180
|
+
});
|
|
77140
77181
|
return handleToolApproval(session, context.toolUseID, toolName, input);
|
|
77141
77182
|
}
|
|
77142
|
-
logFn("[canUseTool] auto-allow", {
|
|
77183
|
+
logFn("[canUseTool] auto-allow", {
|
|
77184
|
+
toolName,
|
|
77185
|
+
permissionMode: session.permissionMode,
|
|
77186
|
+
toolUseID: context.toolUseID
|
|
77187
|
+
});
|
|
77143
77188
|
return { behavior: "allow", updatedInput: input };
|
|
77144
77189
|
};
|
|
77145
77190
|
}
|
|
@@ -77213,11 +77258,7 @@ function buildTaskEvent(toolName, input) {
|
|
|
77213
77258
|
}
|
|
77214
77259
|
|
|
77215
77260
|
// ../../apps/server/src/services/runtimes/claude-code/sdk-event-mapper.ts
|
|
77216
|
-
var TOOL_CONTEXTUAL_HOOK_EVENTS = /* @__PURE__ */ new Set([
|
|
77217
|
-
"PreToolUse",
|
|
77218
|
-
"PostToolUse",
|
|
77219
|
-
"PostToolUseFailure"
|
|
77220
|
-
]);
|
|
77261
|
+
var TOOL_CONTEXTUAL_HOOK_EVENTS = /* @__PURE__ */ new Set(["PreToolUse", "PostToolUse", "PostToolUseFailure"]);
|
|
77221
77262
|
function mapErrorCategory(subtype) {
|
|
77222
77263
|
switch (subtype) {
|
|
77223
77264
|
case "error_max_turns":
|
|
@@ -77555,8 +77596,8 @@ DorkOS Relay is a pub/sub message bus for inter-agent communication.
|
|
|
77555
77596
|
|
|
77556
77597
|
Subject hierarchy:
|
|
77557
77598
|
relay.agent.{agentId} \u2014 activate a specific agent session
|
|
77558
|
-
relay.inbox.query.{UUID} \u2014 ephemeral inbox for
|
|
77559
|
-
relay.inbox.dispatch.{UUID} \u2014 ephemeral inbox for
|
|
77599
|
+
relay.inbox.query.{UUID} \u2014 ephemeral inbox for relay_send_and_wait (auto-managed)
|
|
77600
|
+
relay.inbox.dispatch.{UUID} \u2014 ephemeral inbox for relay_send_async (auto-expires after ~35 min)
|
|
77560
77601
|
relay.inbox.{agentId} \u2014 persistent agent reply inbox
|
|
77561
77602
|
relay.human.console.{clientId} \u2014 reach a human in the DorkOS UI
|
|
77562
77603
|
relay.system.console \u2014 system broadcast channel
|
|
@@ -77564,13 +77605,13 @@ Subject hierarchy:
|
|
|
77564
77605
|
|
|
77565
77606
|
Workflow: Query another agent \u2014 SHORT tasks (\u226410 min, PREFERRED)
|
|
77566
77607
|
1. mesh_list() to find available agents and their agent IDs
|
|
77567
|
-
2.
|
|
77608
|
+
2. relay_send_and_wait(to_subject="relay.agent.{theirAgentId}", payload={task}, from={myAgentId}, timeout_ms=600000)
|
|
77568
77609
|
\u2192 Blocks until reply (max 10 min / 600 000 ms)
|
|
77569
77610
|
\u2192 Returns: { reply, from, replyMessageId, sentMessageId, progress: ProgressEvent[] }
|
|
77570
77611
|
\u2192 progress[] contains intermediate steps: { type: "progress", step, step_type, text, done: false }
|
|
77571
77612
|
|
|
77572
77613
|
Workflow: Dispatch to another agent \u2014 LONG tasks (>10 min)
|
|
77573
|
-
1.
|
|
77614
|
+
1. relay_send_async(to_subject="relay.agent.{theirAgentId}", payload={task}, from={myAgentId})
|
|
77574
77615
|
\u2192 Returns IMMEDIATELY: { messageId, inboxSubject: "relay.inbox.dispatch.{UUID}" }
|
|
77575
77616
|
2. Poll: relay_inbox(endpoint_subject=inboxSubject, status="unread")
|
|
77576
77617
|
\u2192 Returns progress events: { type: "progress", step, step_type: "message"|"tool_result", text, done: false }
|
|
@@ -77589,13 +77630,13 @@ CONSTRAINT \u2014 Subagent MCP tools: DorkOS MCP tools (relay_*, mesh_*, pulse_*
|
|
|
77589
77630
|
inside Claude Code Task() subagents. This is an SDK architectural limitation (subprocesses do not
|
|
77590
77631
|
inherit the parent MCP server). The orchestrator pattern workaround:
|
|
77591
77632
|
WRONG: Task("use relay_send to message agent B") \u2190 tools unavailable, silent failure
|
|
77592
|
-
RIGHT: 1. Call
|
|
77633
|
+
RIGHT: 1. Call relay_send_async() in this (parent) session
|
|
77593
77634
|
2. Pass the inboxSubject into the Task() prompt if needed
|
|
77594
77635
|
3. Poll relay_inbox() in this session after Task() returns
|
|
77595
77636
|
|
|
77596
77637
|
IMPORTANT: When YOU receive a relay message, respond naturally \u2014 do NOT call relay_send.
|
|
77597
77638
|
Your response is automatically forwarded by the relay system.
|
|
77598
|
-
Only call relay_send/
|
|
77639
|
+
Only call relay_send/relay_send_and_wait/relay_send_async to INITIATE a new message.
|
|
77599
77640
|
|
|
77600
77641
|
relay_list_endpoints returns type ("dispatch"|"query"|"persistent"|"agent"|"unknown") and expiresAt
|
|
77601
77642
|
(ISO string or null) for each endpoint. Use these to identify active inboxes and their expiry.
|
|
@@ -77810,7 +77851,7 @@ var CORE_TOOLS = [
|
|
|
77810
77851
|
"mcp__dorkos__ping",
|
|
77811
77852
|
"mcp__dorkos__get_server_info",
|
|
77812
77853
|
"mcp__dorkos__get_session_count",
|
|
77813
|
-
"
|
|
77854
|
+
"mcp__dorkos__get_agent"
|
|
77814
77855
|
];
|
|
77815
77856
|
var PULSE_TOOLS = [
|
|
77816
77857
|
"mcp__dorkos__pulse_list_schedules",
|
|
@@ -77824,8 +77865,8 @@ var RELAY_TOOLS = [
|
|
|
77824
77865
|
"mcp__dorkos__relay_inbox",
|
|
77825
77866
|
"mcp__dorkos__relay_list_endpoints",
|
|
77826
77867
|
"mcp__dorkos__relay_register_endpoint",
|
|
77827
|
-
"
|
|
77828
|
-
"
|
|
77868
|
+
"mcp__dorkos__relay_send_and_wait",
|
|
77869
|
+
"mcp__dorkos__relay_send_async",
|
|
77829
77870
|
// NEW
|
|
77830
77871
|
"mcp__dorkos__relay_unregister_endpoint"
|
|
77831
77872
|
// NEW
|
|
@@ -77851,10 +77892,7 @@ var BINDING_TOOLS = [
|
|
|
77851
77892
|
"mcp__dorkos__binding_create",
|
|
77852
77893
|
"mcp__dorkos__binding_delete"
|
|
77853
77894
|
];
|
|
77854
|
-
var TRACE_TOOLS = [
|
|
77855
|
-
"mcp__dorkos__relay_get_trace",
|
|
77856
|
-
"mcp__dorkos__relay_get_metrics"
|
|
77857
|
-
];
|
|
77895
|
+
var TRACE_TOOLS = ["mcp__dorkos__relay_get_trace", "mcp__dorkos__relay_get_metrics"];
|
|
77858
77896
|
function resolveToolConfig(agentConfig, deps) {
|
|
77859
77897
|
const agent = agentConfig ?? {};
|
|
77860
77898
|
return {
|
|
@@ -77932,7 +77970,11 @@ async function* executeSdkQuery(sessionId, content3, session, opts, messageOpts,
|
|
|
77932
77970
|
pulseEnabled: isPulseEnabled(),
|
|
77933
77971
|
globalConfig
|
|
77934
77972
|
});
|
|
77935
|
-
const baseAppend = await buildSystemPromptAppend(
|
|
77973
|
+
const baseAppend = await buildSystemPromptAppend(
|
|
77974
|
+
effectiveCwd,
|
|
77975
|
+
opts.meshCore ?? void 0,
|
|
77976
|
+
toolConfig
|
|
77977
|
+
);
|
|
77936
77978
|
const systemPromptAppend = messageOpts?.systemPromptAppend ? `${baseAppend}
|
|
77937
77979
|
|
|
77938
77980
|
${messageOpts.systemPromptAppend}` : baseAppend;
|
|
@@ -77951,9 +77993,12 @@ ${messageOpts.systemPromptAppend}` : baseAppend;
|
|
|
77951
77993
|
if (session.hasStarted) {
|
|
77952
77994
|
sdkOptions.resume = session.sdkSessionId;
|
|
77953
77995
|
if (session.sdkSessionId === sessionId) {
|
|
77954
|
-
logger.debug(
|
|
77955
|
-
|
|
77956
|
-
|
|
77996
|
+
logger.debug(
|
|
77997
|
+
"[sendMessage] resuming with sdkSessionId === sessionId (expected after server restart)",
|
|
77998
|
+
{
|
|
77999
|
+
session: sessionId
|
|
78000
|
+
}
|
|
78001
|
+
);
|
|
77957
78002
|
}
|
|
77958
78003
|
}
|
|
77959
78004
|
const cwdSource = messageOpts?.cwd ? "opts.cwd" : opts.sessionCwd ? "session.cwd" : "default";
|
|
@@ -78340,32 +78385,41 @@ var ClaudeCodeRuntime = class _ClaudeCodeRuntime {
|
|
|
78340
78385
|
}
|
|
78341
78386
|
}
|
|
78342
78387
|
const session = this.sessions.get(sessionId);
|
|
78343
|
-
yield* executeSdkQuery(
|
|
78344
|
-
|
|
78345
|
-
|
|
78346
|
-
|
|
78347
|
-
|
|
78348
|
-
|
|
78349
|
-
|
|
78350
|
-
this.
|
|
78351
|
-
|
|
78352
|
-
|
|
78353
|
-
|
|
78354
|
-
|
|
78355
|
-
|
|
78356
|
-
|
|
78357
|
-
|
|
78358
|
-
|
|
78359
|
-
|
|
78360
|
-
|
|
78361
|
-
|
|
78362
|
-
|
|
78363
|
-
|
|
78364
|
-
|
|
78365
|
-
|
|
78366
|
-
|
|
78367
|
-
|
|
78368
|
-
|
|
78388
|
+
yield* executeSdkQuery(
|
|
78389
|
+
sessionId,
|
|
78390
|
+
content3,
|
|
78391
|
+
session,
|
|
78392
|
+
{
|
|
78393
|
+
cwd: this.cwd,
|
|
78394
|
+
sessionCwd: session.cwd,
|
|
78395
|
+
claudeCliPath: this.claudeCliPath,
|
|
78396
|
+
meshCore: this.meshCore,
|
|
78397
|
+
mcpServerFactory: this.mcpServerFactory,
|
|
78398
|
+
onModelsReceived: !this.cachedModels ? (models) => {
|
|
78399
|
+
this.cachedModels = models;
|
|
78400
|
+
logger.debug("[sendMessage] cached supported models", {
|
|
78401
|
+
count: models.length
|
|
78402
|
+
});
|
|
78403
|
+
} : void 0,
|
|
78404
|
+
onMcpStatusReceived: (servers) => {
|
|
78405
|
+
const key = opts?.cwd || session.cwd || this.cwd;
|
|
78406
|
+
this.cachedMcpStatus.set(key, servers);
|
|
78407
|
+
logger.debug("[sendMessage] cached MCP server status", {
|
|
78408
|
+
cwd: key,
|
|
78409
|
+
count: servers.length
|
|
78410
|
+
});
|
|
78411
|
+
},
|
|
78412
|
+
onCommandsReceived: !this.cachedSdkCommands ? (commands) => {
|
|
78413
|
+
this.cachedSdkCommands = commands;
|
|
78414
|
+
logger.debug("[sendMessage] cached supported commands", {
|
|
78415
|
+
count: commands.length
|
|
78416
|
+
});
|
|
78417
|
+
} : void 0,
|
|
78418
|
+
sdkSessionIndex: this.sdkSessionIndex,
|
|
78419
|
+
sessionMapKey: sessionId
|
|
78420
|
+
},
|
|
78421
|
+
opts
|
|
78422
|
+
);
|
|
78369
78423
|
}
|
|
78370
78424
|
// ---------------------------------------------------------------------------
|
|
78371
78425
|
// Interactive flows
|
|
@@ -78616,6 +78670,25 @@ function jsonContent(data, isError2 = false) {
|
|
|
78616
78670
|
}
|
|
78617
78671
|
|
|
78618
78672
|
// ../../apps/server/src/services/runtimes/claude-code/mcp-tools/core-tools.ts
|
|
78673
|
+
function resolveAgentCwd(deps, args) {
|
|
78674
|
+
if (!args.agent_id && !args.cwd) {
|
|
78675
|
+
throw new Error("Either agent_id or cwd must be provided to identify the agent.");
|
|
78676
|
+
}
|
|
78677
|
+
if (args.agent_id && args.cwd) {
|
|
78678
|
+
throw new Error("Provide either agent_id or cwd, not both.");
|
|
78679
|
+
}
|
|
78680
|
+
if (args.cwd) {
|
|
78681
|
+
return args.cwd;
|
|
78682
|
+
}
|
|
78683
|
+
if (!deps.meshCore) {
|
|
78684
|
+
throw new Error("Mesh is not enabled. Cannot resolve agent_id without Mesh.");
|
|
78685
|
+
}
|
|
78686
|
+
const projectPath = deps.meshCore.getProjectPath(args.agent_id);
|
|
78687
|
+
if (!projectPath) {
|
|
78688
|
+
throw new Error(`Agent not found: ${args.agent_id}`);
|
|
78689
|
+
}
|
|
78690
|
+
return projectPath;
|
|
78691
|
+
}
|
|
78619
78692
|
async function handlePing() {
|
|
78620
78693
|
return {
|
|
78621
78694
|
content: [
|
|
@@ -78649,16 +78722,18 @@ async function handleGetServerInfo(args) {
|
|
|
78649
78722
|
};
|
|
78650
78723
|
}
|
|
78651
78724
|
function createGetSessionCountHandler(deps) {
|
|
78652
|
-
return async function handleGetSessionCount() {
|
|
78725
|
+
return async function handleGetSessionCount(args) {
|
|
78653
78726
|
try {
|
|
78654
|
-
const
|
|
78727
|
+
const resolvedCwd = resolveAgentCwd(deps, args);
|
|
78728
|
+
const sessions = await deps.transcriptReader.listSessions(resolvedCwd);
|
|
78655
78729
|
return {
|
|
78656
78730
|
content: [
|
|
78657
78731
|
{
|
|
78658
78732
|
type: "text",
|
|
78659
78733
|
text: JSON.stringify({
|
|
78660
78734
|
count: sessions.length,
|
|
78661
|
-
cwd:
|
|
78735
|
+
cwd: resolvedCwd,
|
|
78736
|
+
...args.agent_id && { agent_id: args.agent_id }
|
|
78662
78737
|
})
|
|
78663
78738
|
}
|
|
78664
78739
|
]
|
|
@@ -78678,12 +78753,16 @@ function createGetSessionCountHandler(deps) {
|
|
|
78678
78753
|
}
|
|
78679
78754
|
};
|
|
78680
78755
|
}
|
|
78681
|
-
function
|
|
78682
|
-
return async () => {
|
|
78756
|
+
function createGetAgentHandler(deps) {
|
|
78757
|
+
return async (args) => {
|
|
78683
78758
|
try {
|
|
78684
|
-
const
|
|
78759
|
+
const resolvedCwd = resolveAgentCwd(deps, args);
|
|
78760
|
+
const manifest = await readManifest(resolvedCwd);
|
|
78685
78761
|
if (!manifest) {
|
|
78686
|
-
return jsonContent({
|
|
78762
|
+
return jsonContent({
|
|
78763
|
+
agent: null,
|
|
78764
|
+
message: "No agent registered for the specified directory"
|
|
78765
|
+
});
|
|
78687
78766
|
}
|
|
78688
78767
|
return jsonContent({ agent: manifest });
|
|
78689
78768
|
} catch (err) {
|
|
@@ -78694,9 +78773,13 @@ function createGetCurrentAgentHandler(deps) {
|
|
|
78694
78773
|
}
|
|
78695
78774
|
};
|
|
78696
78775
|
}
|
|
78776
|
+
var agentScopeSchema = {
|
|
78777
|
+
agent_id: z16.string().optional().describe("Agent ULID to scope the query to"),
|
|
78778
|
+
cwd: z16.string().optional().describe("Working directory path to scope the query to")
|
|
78779
|
+
};
|
|
78697
78780
|
function getCoreTools(deps) {
|
|
78698
78781
|
const handleGetSessionCount = createGetSessionCountHandler(deps);
|
|
78699
|
-
const
|
|
78782
|
+
const handleGetAgent = createGetAgentHandler(deps);
|
|
78700
78783
|
return [
|
|
78701
78784
|
tool(
|
|
78702
78785
|
"ping",
|
|
@@ -78712,15 +78795,15 @@ function getCoreTools(deps) {
|
|
|
78712
78795
|
),
|
|
78713
78796
|
tool(
|
|
78714
78797
|
"get_session_count",
|
|
78715
|
-
"Returns the number of sessions
|
|
78716
|
-
|
|
78798
|
+
"Returns the number of sessions for a specific agent. Provide either agent_id (ULID) or cwd (working directory path).",
|
|
78799
|
+
agentScopeSchema,
|
|
78717
78800
|
handleGetSessionCount
|
|
78718
78801
|
),
|
|
78719
78802
|
tool(
|
|
78720
|
-
"
|
|
78721
|
-
"Get the agent
|
|
78722
|
-
|
|
78723
|
-
|
|
78803
|
+
"get_agent",
|
|
78804
|
+
"Get the agent manifest for a specific agent. Provide either agent_id (ULID) or cwd (working directory path). Returns the agent manifest from .dork/agent.json if one exists, or null if no agent is registered.",
|
|
78805
|
+
agentScopeSchema,
|
|
78806
|
+
handleGetAgent
|
|
78724
78807
|
)
|
|
78725
78808
|
];
|
|
78726
78809
|
}
|
|
@@ -78759,7 +78842,10 @@ function createCreateScheduleHandler(deps) {
|
|
|
78759
78842
|
});
|
|
78760
78843
|
deps.pulseStore.updateSchedule(schedule.id, { status: "pending_approval" });
|
|
78761
78844
|
const updated = deps.pulseStore.getSchedule(schedule.id);
|
|
78762
|
-
return jsonContent({
|
|
78845
|
+
return jsonContent({
|
|
78846
|
+
schedule: updated,
|
|
78847
|
+
note: "Schedule created with pending_approval status. User must approve before it runs."
|
|
78848
|
+
});
|
|
78763
78849
|
};
|
|
78764
78850
|
}
|
|
78765
78851
|
function createUpdateScheduleHandler(deps) {
|
|
@@ -78879,7 +78965,11 @@ function createRelaySendHandler(deps) {
|
|
|
78879
78965
|
replyTo: args.replyTo,
|
|
78880
78966
|
budget: args.budget
|
|
78881
78967
|
});
|
|
78882
|
-
return jsonContent({
|
|
78968
|
+
return jsonContent({
|
|
78969
|
+
messageId: result2.messageId,
|
|
78970
|
+
deliveredTo: result2.deliveredTo,
|
|
78971
|
+
queued: result2.deliveredTo === 0
|
|
78972
|
+
});
|
|
78883
78973
|
} catch (e2) {
|
|
78884
78974
|
const message = e2 instanceof Error ? e2.message : "Publish failed";
|
|
78885
78975
|
const code3 = message.includes("Access denied") ? "ACCESS_DENIED" : message.includes("Invalid subject") ? "INVALID_SUBJECT" : "PUBLISH_FAILED";
|
|
@@ -78963,7 +79053,10 @@ function createRelayQueryHandler(deps) {
|
|
|
78963
79053
|
});
|
|
78964
79054
|
if (result2.deliveredTo === 0 && result2.rejected && result2.rejected.length > 0) {
|
|
78965
79055
|
const reason = result2.rejected[0]?.reason ?? "unknown";
|
|
78966
|
-
return jsonContent(
|
|
79056
|
+
return jsonContent(
|
|
79057
|
+
{ error: `Message rejected: ${reason}`, code: "REJECTED", reason },
|
|
79058
|
+
true
|
|
79059
|
+
);
|
|
78967
79060
|
}
|
|
78968
79061
|
sentMessageId = result2.messageId;
|
|
78969
79062
|
} catch (e2) {
|
|
@@ -78978,7 +79071,9 @@ function createRelayQueryHandler(deps) {
|
|
|
78978
79071
|
};
|
|
78979
79072
|
const timer = setTimeout(() => {
|
|
78980
79073
|
cleanup();
|
|
78981
|
-
reject(
|
|
79074
|
+
reject(
|
|
79075
|
+
new Error(`relay_send_and_wait timed out after ${timeoutMs}ms (sent ${sentMessageId})`)
|
|
79076
|
+
);
|
|
78982
79077
|
}, timeoutMs);
|
|
78983
79078
|
const unsub = relay.subscribe(inboxSubject, (envelope) => {
|
|
78984
79079
|
const payload = envelope.payload;
|
|
@@ -79056,7 +79151,10 @@ function createRelayUnregisterEndpointHandler(deps) {
|
|
|
79056
79151
|
try {
|
|
79057
79152
|
const removed = await deps.relayCore.unregisterEndpoint(args.subject);
|
|
79058
79153
|
if (!removed) {
|
|
79059
|
-
return jsonContent(
|
|
79154
|
+
return jsonContent(
|
|
79155
|
+
{ error: `Endpoint not found: ${args.subject}`, code: "ENDPOINT_NOT_FOUND" },
|
|
79156
|
+
true
|
|
79157
|
+
);
|
|
79060
79158
|
}
|
|
79061
79159
|
return jsonContent({ success: true, subject: args.subject });
|
|
79062
79160
|
} catch (e2) {
|
|
@@ -79111,13 +79209,15 @@ function getRelayTools(deps) {
|
|
|
79111
79209
|
createRelayRegisterEndpointHandler(deps)
|
|
79112
79210
|
),
|
|
79113
79211
|
tool3(
|
|
79114
|
-
"
|
|
79212
|
+
"relay_send_and_wait",
|
|
79115
79213
|
'Send a message to an agent and WAIT for the reply in a single call. Preferred over relay_send + relay_inbox polling for request/reply patterns. Internally registers an ephemeral inbox, sends the message with replyTo set, and blocks until the target agent replies or the timeout elapses. Response shape: { reply, progress, from, replyMessageId, sentMessageId }. progress: array of intermediate steps emitted before the final reply (empty [] for quick replies; populated for multi-step CCA tasks). Each progress step: { type: "progress", step: number, step_type: "message"|"tool_result", text: string, done: false }. Callers that only use { reply, from, replyMessageId } are unaffected \u2014 progress is additive.',
|
|
79116
79214
|
{
|
|
79117
79215
|
to_subject: z18.string().describe('Target subject for the message (e.g., "relay.agent.{agentId}")'),
|
|
79118
79216
|
payload: z18.unknown().describe("Message payload (any JSON-serializable value)"),
|
|
79119
79217
|
from: z18.string().describe("Sender subject identifier"),
|
|
79120
|
-
timeout_ms: z18.number().int().min(1e3).max(6e5).optional().describe(
|
|
79218
|
+
timeout_ms: z18.number().int().min(1e3).max(6e5).optional().describe(
|
|
79219
|
+
"Max milliseconds to wait for a reply (default: 60000, max: 600000). For tasks longer than 10 min, use relay_send_async instead."
|
|
79220
|
+
),
|
|
79121
79221
|
budget: z18.object({
|
|
79122
79222
|
maxHops: z18.number().int().min(1).optional().describe("Max hop count"),
|
|
79123
79223
|
ttl: z18.number().int().optional().describe("Unix timestamp (ms) expiry"),
|
|
@@ -79127,8 +79227,8 @@ function getRelayTools(deps) {
|
|
|
79127
79227
|
createRelayQueryHandler(deps)
|
|
79128
79228
|
),
|
|
79129
79229
|
tool3(
|
|
79130
|
-
"
|
|
79131
|
-
"Dispatch a message to an agent and return IMMEDIATELY with a dispatch inbox subject. Unlike
|
|
79230
|
+
"relay_send_async",
|
|
79231
|
+
"Dispatch a message to an agent and return IMMEDIATELY with a dispatch inbox subject. Unlike relay_send_and_wait (which blocks), relay_send_async returns { messageId, inboxSubject } at once. Agent B runs asynchronously; CCA publishes incremental progress events and a final agent_result to the inbox. Poll relay_inbox(endpoint_subject=inboxSubject) for updates. When you receive a message with done:true, call relay_unregister_endpoint(inboxSubject) to clean up.",
|
|
79132
79232
|
{
|
|
79133
79233
|
to_subject: z18.string().describe('Target subject (e.g., "relay.agent.{agentId}")'),
|
|
79134
79234
|
payload: z18.unknown().describe("Message payload"),
|
|
@@ -79143,7 +79243,7 @@ function getRelayTools(deps) {
|
|
|
79143
79243
|
),
|
|
79144
79244
|
tool3(
|
|
79145
79245
|
"relay_unregister_endpoint",
|
|
79146
|
-
"Unregister a Relay endpoint. Use to clean up dispatch inboxes after
|
|
79246
|
+
"Unregister a Relay endpoint. Use to clean up dispatch inboxes after relay_send_async completes (when done:true received).",
|
|
79147
79247
|
{
|
|
79148
79248
|
subject: z18.string().describe("Subject of the endpoint to unregister")
|
|
79149
79249
|
},
|
|
@@ -79157,7 +79257,10 @@ import { tool as tool4 } from "@anthropic-ai/claude-agent-sdk";
|
|
|
79157
79257
|
import { z as z19 } from "zod";
|
|
79158
79258
|
function requireAdapterManager(deps) {
|
|
79159
79259
|
if (!deps.adapterManager) {
|
|
79160
|
-
return jsonContent(
|
|
79260
|
+
return jsonContent(
|
|
79261
|
+
{ error: "Relay adapters are not enabled", code: "ADAPTERS_DISABLED" },
|
|
79262
|
+
true
|
|
79263
|
+
);
|
|
79161
79264
|
}
|
|
79162
79265
|
return null;
|
|
79163
79266
|
}
|
|
@@ -79244,7 +79347,10 @@ import { tool as tool5 } from "@anthropic-ai/claude-agent-sdk";
|
|
|
79244
79347
|
import { z as z20 } from "zod";
|
|
79245
79348
|
function requireBindingStore(deps) {
|
|
79246
79349
|
if (!deps.bindingStore) {
|
|
79247
|
-
return jsonContent(
|
|
79350
|
+
return jsonContent(
|
|
79351
|
+
{ error: "Relay bindings are not enabled", code: "BINDINGS_DISABLED" },
|
|
79352
|
+
true
|
|
79353
|
+
);
|
|
79248
79354
|
}
|
|
79249
79355
|
return null;
|
|
79250
79356
|
}
|
|
@@ -79289,12 +79395,7 @@ function createBindingDeleteHandler(deps) {
|
|
|
79289
79395
|
function getBindingTools(deps) {
|
|
79290
79396
|
if (!deps.bindingStore) return [];
|
|
79291
79397
|
return [
|
|
79292
|
-
tool5(
|
|
79293
|
-
"binding_list",
|
|
79294
|
-
"List all adapter-to-agent bindings.",
|
|
79295
|
-
{},
|
|
79296
|
-
createBindingListHandler(deps)
|
|
79297
|
-
),
|
|
79398
|
+
tool5("binding_list", "List all adapter-to-agent bindings.", {}, createBindingListHandler(deps)),
|
|
79298
79399
|
tool5(
|
|
79299
79400
|
"binding_create",
|
|
79300
79401
|
"Create a new adapter-to-agent binding. Maps an external adapter to a specific agent directory.",
|
|
@@ -79379,14 +79480,24 @@ function createMeshDiscoverHandler(deps) {
|
|
|
79379
79480
|
if (err) return err;
|
|
79380
79481
|
try {
|
|
79381
79482
|
const candidates = [];
|
|
79483
|
+
const autoImported = [];
|
|
79382
79484
|
for await (const event of deps.meshCore.discover(args.roots, {
|
|
79383
79485
|
maxDepth: args.maxDepth
|
|
79384
79486
|
})) {
|
|
79385
79487
|
if (event.type === "candidate") {
|
|
79386
79488
|
candidates.push(event.data);
|
|
79489
|
+
} else if (event.type === "auto-import" && args.includeRegistered) {
|
|
79490
|
+
autoImported.push(event.data);
|
|
79387
79491
|
}
|
|
79388
79492
|
}
|
|
79389
|
-
return jsonContent({
|
|
79493
|
+
return jsonContent({
|
|
79494
|
+
candidates,
|
|
79495
|
+
count: candidates.length,
|
|
79496
|
+
...args.includeRegistered && {
|
|
79497
|
+
registered: autoImported,
|
|
79498
|
+
registeredCount: autoImported.length
|
|
79499
|
+
}
|
|
79500
|
+
});
|
|
79390
79501
|
} catch (e2) {
|
|
79391
79502
|
const message = e2 instanceof Error ? e2.message : "Discovery failed";
|
|
79392
79503
|
return jsonContent({ error: message, code: "DISCOVER_FAILED" }, true);
|
|
@@ -79398,11 +79509,6 @@ function createMeshRegisterHandler(deps) {
|
|
|
79398
79509
|
const err = requireMesh(deps);
|
|
79399
79510
|
if (err) return err;
|
|
79400
79511
|
try {
|
|
79401
|
-
const overrides = {};
|
|
79402
|
-
if (args.name) overrides.name = args.name;
|
|
79403
|
-
if (args.description) overrides.description = args.description;
|
|
79404
|
-
if (args.runtime) overrides.runtime = args.runtime;
|
|
79405
|
-
if (args.capabilities) overrides.capabilities = args.capabilities;
|
|
79406
79512
|
const agent = await deps.meshCore.registerByPath(
|
|
79407
79513
|
args.path,
|
|
79408
79514
|
{
|
|
@@ -79479,7 +79585,10 @@ function createMeshInspectHandler(deps) {
|
|
|
79479
79585
|
if (err) return err;
|
|
79480
79586
|
const result2 = deps.meshCore.inspect(args.agentId);
|
|
79481
79587
|
if (!result2) {
|
|
79482
|
-
return {
|
|
79588
|
+
return {
|
|
79589
|
+
content: [{ type: "text", text: `Agent ${args.agentId} not found` }],
|
|
79590
|
+
isError: true
|
|
79591
|
+
};
|
|
79483
79592
|
}
|
|
79484
79593
|
return jsonContent(result2);
|
|
79485
79594
|
};
|
|
@@ -79497,10 +79606,13 @@ function getMeshTools(deps) {
|
|
|
79497
79606
|
return [
|
|
79498
79607
|
tool7(
|
|
79499
79608
|
"mesh_discover",
|
|
79500
|
-
"Scan directories for agent candidates.
|
|
79609
|
+
"Scan directories for agent candidates. By default returns only unregistered agents (candidates). Set includeRegistered to also see already-registered agents found during the scan.",
|
|
79501
79610
|
{
|
|
79502
79611
|
roots: z22.array(z22.string()).describe("Root directories to scan for agents"),
|
|
79503
|
-
maxDepth: z22.number().int().min(1).optional().describe("Maximum directory depth (default
|
|
79612
|
+
maxDepth: z22.number().int().min(1).optional().describe("Maximum directory depth (default: 5)"),
|
|
79613
|
+
includeRegistered: z22.boolean().optional().describe(
|
|
79614
|
+
"Include already-registered agents in results (default: false \u2014 unregistered candidates only)"
|
|
79615
|
+
)
|
|
79504
79616
|
},
|
|
79505
79617
|
createMeshDiscoverHandler(deps)
|
|
79506
79618
|
),
|
|
@@ -84981,7 +85093,9 @@ var PulseStore = class {
|
|
|
84981
85093
|
conditions.push(eq(pulseRuns.scheduleId, opts.scheduleId));
|
|
84982
85094
|
}
|
|
84983
85095
|
if (opts.status) {
|
|
84984
|
-
conditions.push(
|
|
85096
|
+
conditions.push(
|
|
85097
|
+
eq(pulseRuns.status, opts.status)
|
|
85098
|
+
);
|
|
84985
85099
|
}
|
|
84986
85100
|
const query2 = this.db.select().from(pulseRuns).orderBy(desc(pulseRuns.createdAt)).limit(limit).offset(offset);
|
|
84987
85101
|
if (conditions.length > 0) {
|
|
@@ -85953,7 +86067,9 @@ var SchedulerService = class {
|
|
|
85953
86067
|
this.store.updateRun(run.id, {
|
|
85954
86068
|
status: "running"
|
|
85955
86069
|
});
|
|
85956
|
-
logger2.info(
|
|
86070
|
+
logger2.info(
|
|
86071
|
+
`relay dispatch for run ${run.id} delivered to ${result2.deliveredTo} endpoint(s)`
|
|
86072
|
+
);
|
|
85957
86073
|
}
|
|
85958
86074
|
}
|
|
85959
86075
|
/** Execute a run directly via AgentManager — manages AbortController, streams output, updates status. */
|
|
@@ -86110,13 +86226,9 @@ async function loadPresets(dorkHome) {
|
|
|
86110
86226
|
}
|
|
86111
86227
|
|
|
86112
86228
|
// ../../apps/server/src/routes/pulse.ts
|
|
86113
|
-
function createPulseRouter(store, scheduler, meshCore2) {
|
|
86229
|
+
function createPulseRouter(store, scheduler, dorkHome, meshCore2) {
|
|
86114
86230
|
const router13 = Router14();
|
|
86115
86231
|
router13.get("/presets", async (_req, res) => {
|
|
86116
|
-
const dorkHome = env.DORK_HOME;
|
|
86117
|
-
if (!dorkHome) {
|
|
86118
|
-
return res.status(500).json({ error: "DORK_HOME not configured" });
|
|
86119
|
-
}
|
|
86120
86232
|
const presets = await loadPresets(dorkHome);
|
|
86121
86233
|
return res.json(presets);
|
|
86122
86234
|
});
|
|
@@ -86224,7 +86336,6 @@ import * as os5 from "node:os";
|
|
|
86224
86336
|
import fs14 from "node:fs";
|
|
86225
86337
|
|
|
86226
86338
|
// ../relay/dist/endpoint-registry.js
|
|
86227
|
-
import { createHash } from "node:crypto";
|
|
86228
86339
|
import { mkdir as mkdir2, rm } from "node:fs/promises";
|
|
86229
86340
|
import { join as join4 } from "node:path";
|
|
86230
86341
|
|
|
@@ -86316,10 +86427,6 @@ function matchTokens(subject, pattern, si, pi) {
|
|
|
86316
86427
|
|
|
86317
86428
|
// ../relay/dist/endpoint-registry.js
|
|
86318
86429
|
var MAILDIR_DIRS = ["tmp", "new", "cur", "failed"];
|
|
86319
|
-
var HASH_LENGTH = 12;
|
|
86320
|
-
function hashSubject(subject) {
|
|
86321
|
-
return createHash("sha256").update(subject).digest("hex").slice(0, HASH_LENGTH);
|
|
86322
|
-
}
|
|
86323
86430
|
var EndpointRegistry = class {
|
|
86324
86431
|
/** Base directory for all endpoint mailboxes (e.g. `~/.dork/relay/mailboxes`). */
|
|
86325
86432
|
mailboxesDir;
|
|
@@ -86337,8 +86444,8 @@ var EndpointRegistry = class {
|
|
|
86337
86444
|
/**
|
|
86338
86445
|
* Register a new message endpoint.
|
|
86339
86446
|
*
|
|
86340
|
-
* Validates the subject,
|
|
86341
|
-
*
|
|
86447
|
+
* Validates the subject, creates the Maildir directory structure using
|
|
86448
|
+
* the subject string as the directory name, and stores the endpoint info in memory.
|
|
86342
86449
|
*
|
|
86343
86450
|
* @param subject - The hierarchical subject for this endpoint (e.g. `relay.agent.myproject.backend`).
|
|
86344
86451
|
* Must not contain wildcards (`*` or `>`).
|
|
@@ -86356,14 +86463,13 @@ var EndpointRegistry = class {
|
|
|
86356
86463
|
if (this.endpoints.has(subject)) {
|
|
86357
86464
|
throw new Error(`Endpoint already registered: ${subject}`);
|
|
86358
86465
|
}
|
|
86359
|
-
const
|
|
86360
|
-
const maildirPath = join4(this.mailboxesDir, hash);
|
|
86466
|
+
const maildirPath = join4(this.mailboxesDir, subject);
|
|
86361
86467
|
for (const dir of MAILDIR_DIRS) {
|
|
86362
86468
|
await mkdir2(join4(maildirPath, dir), { recursive: true });
|
|
86363
86469
|
}
|
|
86364
86470
|
const info = {
|
|
86365
86471
|
subject,
|
|
86366
|
-
hash,
|
|
86472
|
+
hash: subject,
|
|
86367
86473
|
maildirPath,
|
|
86368
86474
|
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
86369
86475
|
};
|
|
@@ -88151,11 +88257,10 @@ var AdapterDelivery = class _AdapterDelivery {
|
|
|
88151
88257
|
})
|
|
88152
88258
|
]);
|
|
88153
88259
|
if (result2 && result2.success) {
|
|
88154
|
-
const subjectHash = hashSubject(subject);
|
|
88155
88260
|
this.sqliteIndex.insertMessage({
|
|
88156
88261
|
id: envelope.id,
|
|
88157
88262
|
subject,
|
|
88158
|
-
endpointHash: `adapter:${
|
|
88263
|
+
endpointHash: `adapter:${subject}`,
|
|
88159
88264
|
status: "delivered",
|
|
88160
88265
|
createdAt: envelope.createdAt,
|
|
88161
88266
|
expiresAt: null
|
|
@@ -88466,10 +88571,9 @@ var RelayPublishPipeline = class {
|
|
|
88466
88571
|
}
|
|
88467
88572
|
/** Dead-letter a message that had no delivery targets. */
|
|
88468
88573
|
async deadLetter(subject, envelope, adapterResult) {
|
|
88469
|
-
|
|
88470
|
-
await this.deps.maildirStore.ensureMaildir(subjectHash);
|
|
88574
|
+
await this.deps.maildirStore.ensureMaildir(subject);
|
|
88471
88575
|
const reason = adapterResult?.error ? `adapter delivery failed: ${adapterResult.error}` : "no matching endpoints or adapters";
|
|
88472
|
-
await this.deps.deadLetterQueue.reject(
|
|
88576
|
+
await this.deps.deadLetterQueue.reject(subject, envelope, reason);
|
|
88473
88577
|
}
|
|
88474
88578
|
/** Record a trace span for delivery tracking (best-effort). */
|
|
88475
88579
|
recordTrace(messageId, subject, deliveredTo, rejected, adapterResult, envelope) {
|
|
@@ -89046,6 +89150,7 @@ var BaseRelayAdapter = class {
|
|
|
89046
89150
|
};
|
|
89047
89151
|
|
|
89048
89152
|
// ../relay/dist/adapter-registry.js
|
|
89153
|
+
var ADAPTER_START_TIMEOUT_MS = 3e4;
|
|
89049
89154
|
var AdapterRegistry = class {
|
|
89050
89155
|
adapters = /* @__PURE__ */ new Map();
|
|
89051
89156
|
relay = null;
|
|
@@ -89082,7 +89187,19 @@ var AdapterRegistry = class {
|
|
|
89082
89187
|
throw new Error("AdapterRegistry: relay not set \u2014 call setRelay() before registering adapters");
|
|
89083
89188
|
}
|
|
89084
89189
|
const existing = this.adapters.get(adapter.id);
|
|
89085
|
-
|
|
89190
|
+
this.logger.info(`AdapterRegistry: starting adapter '${adapter.id}'`);
|
|
89191
|
+
let timer;
|
|
89192
|
+
try {
|
|
89193
|
+
await Promise.race([
|
|
89194
|
+
adapter.start(this.relay),
|
|
89195
|
+
new Promise((_4, reject) => {
|
|
89196
|
+
timer = setTimeout(() => reject(new Error(`Adapter '${adapter.id}' start timed out after ${ADAPTER_START_TIMEOUT_MS / 1e3}s`)), ADAPTER_START_TIMEOUT_MS);
|
|
89197
|
+
})
|
|
89198
|
+
]);
|
|
89199
|
+
} finally {
|
|
89200
|
+
clearTimeout(timer);
|
|
89201
|
+
}
|
|
89202
|
+
this.logger.info(`AdapterRegistry: adapter '${adapter.id}' started`);
|
|
89086
89203
|
this.adapters.set(adapter.id, adapter);
|
|
89087
89204
|
if (existing) {
|
|
89088
89205
|
try {
|
|
@@ -101978,12 +102095,18 @@ var import_grammy = __toESM(require_mod2(), 1);
|
|
|
101978
102095
|
import crypto4 from "node:crypto";
|
|
101979
102096
|
import { createServer } from "node:http";
|
|
101980
102097
|
var DEFAULT_WEBHOOK_PORT = 8443;
|
|
101981
|
-
async function startWebhookMode(bot, adapterId, webhookUrl, webhookPort, webhookSecret) {
|
|
102098
|
+
async function startWebhookMode(bot, adapterId, webhookUrl, webhookPort, webhookSecret, timeoutMs = 15e3) {
|
|
101982
102099
|
if (!webhookUrl) {
|
|
101983
102100
|
throw new Error(`TelegramAdapter(${adapterId}): webhookUrl is required when mode is 'webhook'`);
|
|
101984
102101
|
}
|
|
101985
102102
|
const secret = webhookSecret ?? crypto4.randomUUID();
|
|
101986
|
-
|
|
102103
|
+
let timer;
|
|
102104
|
+
await Promise.race([
|
|
102105
|
+
bot.api.setWebhook(webhookUrl, { secret_token: secret }),
|
|
102106
|
+
new Promise((_4, reject) => {
|
|
102107
|
+
timer = setTimeout(() => reject(new Error("Telegram bot token validation timed out \u2014 check your token and network connectivity")), timeoutMs);
|
|
102108
|
+
})
|
|
102109
|
+
]).finally(() => clearTimeout(timer));
|
|
101987
102110
|
const port = webhookPort ?? DEFAULT_WEBHOOK_PORT;
|
|
101988
102111
|
const handler = (0, import_grammy.webhookCallback)(bot, "http", { secretToken: secret });
|
|
101989
102112
|
const server = createServer(handler);
|
|
@@ -102116,6 +102239,8 @@ For local development, use a tunnel service (e.g., ngrok, Cloudflare Tunnel).`
|
|
|
102116
102239
|
setupInstructions: "Open Telegram and search for @BotFather. Send /newbot, choose a name and username. Copy the token provided."
|
|
102117
102240
|
};
|
|
102118
102241
|
var TelegramAdapter = class _TelegramAdapter extends BaseRelayAdapter {
|
|
102242
|
+
/** Timeout for bot.init() and setWebhook() calls (ms). */
|
|
102243
|
+
static INIT_TIMEOUT_MS = 15e3;
|
|
102119
102244
|
/** Reconnection delay schedule (ms) -- exponential backoff. */
|
|
102120
102245
|
static RECONNECT_DELAYS = [5e3, 1e4, 3e4, 6e4, 6e4];
|
|
102121
102246
|
config;
|
|
@@ -102135,7 +102260,7 @@ var TelegramAdapter = class _TelegramAdapter extends BaseRelayAdapter {
|
|
|
102135
102260
|
async testConnection() {
|
|
102136
102261
|
try {
|
|
102137
102262
|
const bot = new import_grammy2.Bot(this.config.token);
|
|
102138
|
-
await
|
|
102263
|
+
await this.initBotWithTimeout(bot);
|
|
102139
102264
|
return { ok: true, botUsername: bot.botInfo.username };
|
|
102140
102265
|
} catch (err) {
|
|
102141
102266
|
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
@@ -102185,7 +102310,11 @@ var TelegramAdapter = class _TelegramAdapter extends BaseRelayAdapter {
|
|
|
102185
102310
|
void handleTypingSignal(this.bot, subject, this.outboundState, signal.state);
|
|
102186
102311
|
});
|
|
102187
102312
|
if (this.config.mode === "webhook") {
|
|
102188
|
-
this.
|
|
102313
|
+
this.logger.info("starting webhook mode", {
|
|
102314
|
+
url: this.config.webhookUrl,
|
|
102315
|
+
port: this.config.webhookPort
|
|
102316
|
+
});
|
|
102317
|
+
this.webhookServer = await startWebhookMode(bot, this.id, this.config.webhookUrl, this.config.webhookPort, this.config.webhookSecret, _TelegramAdapter.INIT_TIMEOUT_MS);
|
|
102189
102318
|
} else {
|
|
102190
102319
|
await this.startPollingMode(bot);
|
|
102191
102320
|
}
|
|
@@ -102240,9 +102369,26 @@ var TelegramAdapter = class _TelegramAdapter extends BaseRelayAdapter {
|
|
|
102240
102369
|
});
|
|
102241
102370
|
}
|
|
102242
102371
|
// --- Private helpers ---
|
|
102372
|
+
/**
|
|
102373
|
+
* Call bot.init() with a timeout guard.
|
|
102374
|
+
*
|
|
102375
|
+
* Prevents indefinite hangs when the Telegram API is unreachable or the
|
|
102376
|
+
* token is invalid but the connection never closes.
|
|
102377
|
+
*/
|
|
102378
|
+
async initBotWithTimeout(bot) {
|
|
102379
|
+
let timer;
|
|
102380
|
+
await Promise.race([
|
|
102381
|
+
bot.init(),
|
|
102382
|
+
new Promise((_4, reject) => {
|
|
102383
|
+
timer = setTimeout(() => reject(new Error("Telegram bot token validation timed out \u2014 check your token and network connectivity")), _TelegramAdapter.INIT_TIMEOUT_MS);
|
|
102384
|
+
})
|
|
102385
|
+
]).finally(() => clearTimeout(timer));
|
|
102386
|
+
}
|
|
102243
102387
|
/** Start grammy bot in long-polling mode with eager token validation. */
|
|
102244
102388
|
async startPollingMode(bot) {
|
|
102245
|
-
await
|
|
102389
|
+
await this.initBotWithTimeout(bot);
|
|
102390
|
+
this.logger.info("bot validated", { username: bot.botInfo.username, mode: "polling" });
|
|
102391
|
+
this.logger.info("starting polling mode");
|
|
102246
102392
|
bot.start({
|
|
102247
102393
|
drop_pending_updates: true,
|
|
102248
102394
|
onStart: () => {
|
|
@@ -102385,6 +102531,7 @@ var WebhookAdapter = class extends BaseRelayAdapter {
|
|
|
102385
102531
|
* @param _relay - The RelayPublisher (stored by base class; unused here)
|
|
102386
102532
|
*/
|
|
102387
102533
|
async _start(_relay) {
|
|
102534
|
+
this.logger.info("webhook adapter ready", { subject: this.config.inbound.subject });
|
|
102388
102535
|
this.nonceInterval = setInterval(() => {
|
|
102389
102536
|
this.pruneExpiredNonces();
|
|
102390
102537
|
}, NONCE_PRUNE_INTERVAL_MS);
|
|
@@ -102904,7 +103051,11 @@ async function handleTextDelta(textChunk, streaming, nativeStreaming, ctx) {
|
|
|
102904
103051
|
return { success: true, durationMs: now - startTime };
|
|
102905
103052
|
} catch (err) {
|
|
102906
103053
|
callbacks.recordError(err);
|
|
102907
|
-
return {
|
|
103054
|
+
return {
|
|
103055
|
+
success: false,
|
|
103056
|
+
error: err instanceof Error ? err.message : String(err),
|
|
103057
|
+
durationMs: Date.now() - startTime
|
|
103058
|
+
};
|
|
102908
103059
|
}
|
|
102909
103060
|
}
|
|
102910
103061
|
async function flushStreamBuffer(ctx) {
|
|
@@ -103100,7 +103251,10 @@ ${inputPreview}
|
|
|
103100
103251
|
blocks: [
|
|
103101
103252
|
{
|
|
103102
103253
|
type: "section",
|
|
103103
|
-
text: {
|
|
103254
|
+
text: {
|
|
103255
|
+
type: "mrkdwn",
|
|
103256
|
+
text: ":hourglass: *Tool Approval Timed Out*\n~`" + data.toolName + "`~"
|
|
103257
|
+
}
|
|
103104
103258
|
}
|
|
103105
103259
|
]
|
|
103106
103260
|
});
|
|
@@ -103149,7 +103303,11 @@ async function deliverMessage2(opts) {
|
|
|
103149
103303
|
return { success: true, durationMs: Date.now() - startTime };
|
|
103150
103304
|
}
|
|
103151
103305
|
if (!client) {
|
|
103152
|
-
return {
|
|
103306
|
+
return {
|
|
103307
|
+
success: false,
|
|
103308
|
+
error: `SlackAdapter(${adapterId}): not started`,
|
|
103309
|
+
durationMs: Date.now() - startTime
|
|
103310
|
+
};
|
|
103153
103311
|
}
|
|
103154
103312
|
const channelId = extractChannelId(subject);
|
|
103155
103313
|
if (!channelId) {
|
|
@@ -103203,7 +103361,11 @@ async function deliverMessage2(opts) {
|
|
|
103203
103361
|
}
|
|
103204
103362
|
const text6 = truncateText(formatForPlatform(extractPayloadContent(envelope.payload), "slack"), MAX_MESSAGE_LENGTH2);
|
|
103205
103363
|
logger3.debug(`deliver: standard payload to ${channelId} (${text6.length} chars)`);
|
|
103206
|
-
return wrapSlackCall(() => client.chat.postMessage({
|
|
103364
|
+
return wrapSlackCall(() => client.chat.postMessage({
|
|
103365
|
+
channel: channelId,
|
|
103366
|
+
text: text6,
|
|
103367
|
+
...threadTs ? { thread_ts: threadTs } : {}
|
|
103368
|
+
}), callbacks, startTime, true);
|
|
103207
103369
|
}
|
|
103208
103370
|
|
|
103209
103371
|
// ../relay/dist/adapters/slack/slack-adapter.js
|
|
@@ -103265,7 +103427,14 @@ var SLACK_MANIFEST = {
|
|
|
103265
103427
|
stepId: "create-app",
|
|
103266
103428
|
title: "Create & Configure a Slack App",
|
|
103267
103429
|
description: 'Go to api.slack.com/apps \u2192 Create New App \u2192 From Scratch.\n\n1. **Socket Mode** \u2014 Enable it (Settings \u2192 Socket Mode).\n2. **Event Subscriptions** \u2014 Turn on Enable Events, then subscribe to bot events: app_mention, message.channels, message.groups, message.im, message.mpim.\n3. **OAuth & Permissions** \u2014 Add bot token scopes: app_mentions:read, channels:history, channels:read, chat:write, groups:history, groups:read, im:history, im:read, im:write, mpim:history, reactions:read, reactions:write, users:read. Then install the app to your workspace.\n4. **App-Level Token** \u2014 In Basic Information \u2192 App-Level Tokens, generate a token with the connections:write scope.\n\n\u26A0\uFE0F Do NOT enable "Agents & AI Apps" \u2014 it adds user scopes that cause install failures on most workspaces.',
|
|
103268
|
-
fields: [
|
|
103430
|
+
fields: [
|
|
103431
|
+
"botToken",
|
|
103432
|
+
"appToken",
|
|
103433
|
+
"signingSecret",
|
|
103434
|
+
"streaming",
|
|
103435
|
+
"nativeStreaming",
|
|
103436
|
+
"typingIndicator"
|
|
103437
|
+
]
|
|
103269
103438
|
}
|
|
103270
103439
|
],
|
|
103271
103440
|
configFields: [
|
|
@@ -103349,7 +103518,9 @@ var SLACK_MANIFEST = {
|
|
|
103349
103518
|
],
|
|
103350
103519
|
setupInstructions: '1. Create a Slack app at api.slack.com/apps (From Scratch, not From Manifest).\n2. Enable Socket Mode (Settings \u2192 Socket Mode).\n3. Enable Event Subscriptions and subscribe to bot events: app_mention, message.channels, message.groups, message.im, message.mpim.\n4. Add bot token scopes under OAuth & Permissions: app_mentions:read, channels:history, channels:read, chat:write, groups:history, groups:read, im:history, im:read, im:write, mpim:history, reactions:read, reactions:write, users:read.\n5. Install the app to your workspace (OAuth & Permissions \u2192 Install).\n6. Copy the Bot User OAuth Token (starts with xoxb-).\n7. Generate an App-Level Token with connections:write scope (Basic Information \u2192 App-Level Tokens).\n8. Copy the Signing Secret from Basic Information.\n\n\u26A0\uFE0F Do NOT enable "Agents & AI Apps" \u2014 it adds user-level scopes that cause invalid_scope errors on most workspace plans.'
|
|
103351
103520
|
};
|
|
103352
|
-
var SlackAdapter = class extends BaseRelayAdapter {
|
|
103521
|
+
var SlackAdapter = class _SlackAdapter extends BaseRelayAdapter {
|
|
103522
|
+
/** Timeout for auth.test() calls (ms). */
|
|
103523
|
+
static INIT_TIMEOUT_MS = 15e3;
|
|
103353
103524
|
config;
|
|
103354
103525
|
app = null;
|
|
103355
103526
|
/** Bot's own user ID — cached after auth.test for echo prevention. */
|
|
@@ -103372,7 +103543,7 @@ var SlackAdapter = class extends BaseRelayAdapter {
|
|
|
103372
103543
|
try {
|
|
103373
103544
|
const { WebClient } = await Promise.resolve().then(() => __toESM(require_dist4(), 1));
|
|
103374
103545
|
const tempClient = new WebClient(this.config.botToken);
|
|
103375
|
-
const result2 = await tempClient.auth.test();
|
|
103546
|
+
const result2 = await _SlackAdapter.withInitTimeout(tempClient.auth.test());
|
|
103376
103547
|
return { ok: true, botUsername: result2.user };
|
|
103377
103548
|
} catch (err) {
|
|
103378
103549
|
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
@@ -103387,8 +103558,12 @@ var SlackAdapter = class extends BaseRelayAdapter {
|
|
|
103387
103558
|
socketMode: true,
|
|
103388
103559
|
logLevel: import_bolt.LogLevel.WARN
|
|
103389
103560
|
});
|
|
103390
|
-
const authResult = await app.client.auth.test();
|
|
103561
|
+
const authResult = await _SlackAdapter.withInitTimeout(app.client.auth.test());
|
|
103391
103562
|
this.botUserId = authResult.user_id ?? "";
|
|
103563
|
+
this.logger.info("authenticated", {
|
|
103564
|
+
botUserId: this.botUserId,
|
|
103565
|
+
workspace: authResult.team
|
|
103566
|
+
});
|
|
103392
103567
|
app.message(async ({ event, client }) => {
|
|
103393
103568
|
await handleInboundMessage2(event, client, relay, this.botUserId, this.makeInboundCallbacks(), this.logger, this.config.typingIndicator ?? "none", this.pendingReactions);
|
|
103394
103569
|
});
|
|
@@ -103406,6 +103581,7 @@ var SlackAdapter = class extends BaseRelayAdapter {
|
|
|
103406
103581
|
app.error(async (error) => {
|
|
103407
103582
|
this.recordError(error);
|
|
103408
103583
|
});
|
|
103584
|
+
this.logger.info("connecting via Socket Mode");
|
|
103409
103585
|
await app.start();
|
|
103410
103586
|
this.app = app;
|
|
103411
103587
|
}
|
|
@@ -103450,6 +103626,21 @@ var SlackAdapter = class extends BaseRelayAdapter {
|
|
|
103450
103626
|
logger: this.logger
|
|
103451
103627
|
});
|
|
103452
103628
|
}
|
|
103629
|
+
/**
|
|
103630
|
+
* Wrap a promise with a timeout guard.
|
|
103631
|
+
*
|
|
103632
|
+
* Used for auth.test() calls in both `_start()` and `testConnection()` to
|
|
103633
|
+
* prevent indefinite hangs when the Slack API is unreachable.
|
|
103634
|
+
*/
|
|
103635
|
+
static async withInitTimeout(promise) {
|
|
103636
|
+
let timer;
|
|
103637
|
+
return Promise.race([
|
|
103638
|
+
promise,
|
|
103639
|
+
new Promise((_4, reject) => {
|
|
103640
|
+
timer = setTimeout(() => reject(new Error("Slack auth.test() timed out \u2014 check your bot token and network connectivity")), _SlackAdapter.INIT_TIMEOUT_MS);
|
|
103641
|
+
})
|
|
103642
|
+
]).finally(() => clearTimeout(timer));
|
|
103643
|
+
}
|
|
103453
103644
|
/**
|
|
103454
103645
|
* Handle a tool approval or denial action from Slack interactive buttons.
|
|
103455
103646
|
*
|
|
@@ -103582,7 +103773,11 @@ var STREAM_EVENT_TYPES = /* @__PURE__ */ new Set([
|
|
|
103582
103773
|
async function handleAgentMessage(subject, envelope, context, startTime, config, deps, relay) {
|
|
103583
103774
|
const agentId = extractAgentId(subject);
|
|
103584
103775
|
if (!agentId) {
|
|
103585
|
-
return {
|
|
103776
|
+
return {
|
|
103777
|
+
success: false,
|
|
103778
|
+
error: `Could not extract agentId from subject: ${subject}`,
|
|
103779
|
+
durationMs: Date.now() - startTime
|
|
103780
|
+
};
|
|
103586
103781
|
}
|
|
103587
103782
|
const log = deps.logger ?? console;
|
|
103588
103783
|
if (!deps.agentSessionStore) {
|
|
@@ -103674,7 +103869,11 @@ async function handleAgentMessage(subject, envelope, context, startTime, config,
|
|
|
103674
103869
|
} catch (err) {
|
|
103675
103870
|
streamError = err instanceof Error ? err.message : String(err);
|
|
103676
103871
|
log.error("[CCA] Streaming error:", err);
|
|
103677
|
-
deps.traceStore.updateSpan(envelope.id, {
|
|
103872
|
+
deps.traceStore.updateSpan(envelope.id, {
|
|
103873
|
+
status: "failed",
|
|
103874
|
+
processedAt: Date.now(),
|
|
103875
|
+
error: streamError
|
|
103876
|
+
});
|
|
103678
103877
|
} finally {
|
|
103679
103878
|
clearTimeout(timeout);
|
|
103680
103879
|
if (!streamedDone && envelope.replyTo && relay) {
|
|
@@ -104343,11 +104542,7 @@ async function loadAdapterConfig(configPath) {
|
|
|
104343
104542
|
async function saveAdapterConfig(configPath, configs) {
|
|
104344
104543
|
await mkdir4(dirname3(configPath), { recursive: true });
|
|
104345
104544
|
const tmpPath = `${configPath}.tmp`;
|
|
104346
|
-
await writeFile2(
|
|
104347
|
-
tmpPath,
|
|
104348
|
-
JSON.stringify({ adapters: configs }, null, 2),
|
|
104349
|
-
"utf-8"
|
|
104350
|
-
);
|
|
104545
|
+
await writeFile2(tmpPath, JSON.stringify({ adapters: configs }, null, 2), "utf-8");
|
|
104351
104546
|
await rename2(tmpPath, configPath);
|
|
104352
104547
|
}
|
|
104353
104548
|
async function ensureDefaultAdapterConfig(configPath) {
|
|
@@ -104371,11 +104566,7 @@ async function ensureDefaultAdapterConfig(configPath) {
|
|
|
104371
104566
|
};
|
|
104372
104567
|
try {
|
|
104373
104568
|
await mkdir4(dirname3(configPath), { recursive: true });
|
|
104374
|
-
await writeFile2(
|
|
104375
|
-
configPath,
|
|
104376
|
-
JSON.stringify(defaultConfig, null, 2),
|
|
104377
|
-
"utf-8"
|
|
104378
|
-
);
|
|
104569
|
+
await writeFile2(configPath, JSON.stringify(defaultConfig, null, 2), "utf-8");
|
|
104379
104570
|
logger.info("[AdapterConfig] Generated default adapters.json with claude-code adapter");
|
|
104380
104571
|
} catch (writeErr) {
|
|
104381
104572
|
logger.warn("[AdapterConfig] Failed to write default config:", writeErr);
|
|
@@ -104465,38 +104656,25 @@ function defaultAdapterStatus() {
|
|
|
104465
104656
|
async function createAdapter(config, deps, configPath, onPluginManifest) {
|
|
104466
104657
|
switch (config.type) {
|
|
104467
104658
|
case "telegram": {
|
|
104468
|
-
const adapter = new TelegramAdapter(
|
|
104469
|
-
config.id,
|
|
104470
|
-
config.config
|
|
104471
|
-
);
|
|
104659
|
+
const adapter = new TelegramAdapter(config.id, config.config);
|
|
104472
104660
|
adapter.setLogger(createTaggedLogger(`telegram:${config.id}`));
|
|
104473
104661
|
return adapter;
|
|
104474
104662
|
}
|
|
104475
104663
|
case "webhook":
|
|
104476
|
-
return new WebhookAdapter(
|
|
104477
|
-
config.id,
|
|
104478
|
-
config.config
|
|
104479
|
-
);
|
|
104664
|
+
return new WebhookAdapter(config.id, config.config);
|
|
104480
104665
|
case "slack": {
|
|
104481
|
-
const adapter = new SlackAdapter(
|
|
104482
|
-
config.id,
|
|
104483
|
-
config.config
|
|
104484
|
-
);
|
|
104666
|
+
const adapter = new SlackAdapter(config.id, config.config);
|
|
104485
104667
|
adapter.setLogger(createTaggedLogger(`slack:${config.id}`));
|
|
104486
104668
|
return adapter;
|
|
104487
104669
|
}
|
|
104488
104670
|
case "claude-code":
|
|
104489
|
-
return new ClaudeCodeAdapter(
|
|
104490
|
-
|
|
104491
|
-
|
|
104492
|
-
|
|
104493
|
-
|
|
104494
|
-
|
|
104495
|
-
|
|
104496
|
-
agentSessionStore: deps.agentSessionStore,
|
|
104497
|
-
logger
|
|
104498
|
-
}
|
|
104499
|
-
);
|
|
104671
|
+
return new ClaudeCodeAdapter(config.id, config.config, {
|
|
104672
|
+
agentManager: deps.agentManager,
|
|
104673
|
+
traceStore: deps.traceStore,
|
|
104674
|
+
pulseStore: deps.pulseStore,
|
|
104675
|
+
agentSessionStore: deps.agentSessionStore,
|
|
104676
|
+
logger
|
|
104677
|
+
});
|
|
104500
104678
|
case "plugin":
|
|
104501
104679
|
return loadPluginAdapter(config, configPath, onPluginManifest);
|
|
104502
104680
|
default:
|
|
@@ -104862,7 +105040,10 @@ var AgentSessionStore = class {
|
|
|
104862
105040
|
}
|
|
104863
105041
|
/** Enqueue an atomic persist, serialized to prevent concurrent tmp+rename races. */
|
|
104864
105042
|
persist() {
|
|
104865
|
-
this.writeLock = this.writeLock.then(
|
|
105043
|
+
this.writeLock = this.writeLock.then(
|
|
105044
|
+
() => this.doPersist(),
|
|
105045
|
+
() => this.doPersist()
|
|
105046
|
+
);
|
|
104866
105047
|
return this.writeLock;
|
|
104867
105048
|
}
|
|
104868
105049
|
/** Atomic tmp+rename write. Must be serialized via writeLock. */
|
|
@@ -104928,7 +105109,10 @@ var BindingRouter = class _BindingRouter {
|
|
|
104928
105109
|
try {
|
|
104929
105110
|
await this.saveSessionMap();
|
|
104930
105111
|
} catch (err) {
|
|
104931
|
-
logger.warn(
|
|
105112
|
+
logger.warn(
|
|
105113
|
+
"BindingRouter: failed to persist session map after cleanup, will retry on next write",
|
|
105114
|
+
err
|
|
105115
|
+
);
|
|
104932
105116
|
}
|
|
104933
105117
|
logger.info(`Cleaned up ${removed} orphaned session mapping(s)`);
|
|
104934
105118
|
}
|
|
@@ -104946,9 +105130,7 @@ var BindingRouter = class _BindingRouter {
|
|
|
104946
105130
|
}
|
|
104947
105131
|
const binding = this.deps.bindingStore.resolve(adapterId, chatId, channelType);
|
|
104948
105132
|
if (!binding) {
|
|
104949
|
-
logger.info(
|
|
104950
|
-
`BindingRouter: no binding for adapter=${adapterId} chat=${chatId}, skipping`
|
|
104951
|
-
);
|
|
105133
|
+
logger.info(`BindingRouter: no binding for adapter=${adapterId} chat=${chatId}, skipping`);
|
|
104952
105134
|
return;
|
|
104953
105135
|
}
|
|
104954
105136
|
if (binding.canReceive === false) {
|
|
@@ -105024,7 +105206,10 @@ var BindingRouter = class _BindingRouter {
|
|
|
105024
105206
|
try {
|
|
105025
105207
|
await this.saveSessionMap();
|
|
105026
105208
|
} catch (err) {
|
|
105027
|
-
logger.warn(
|
|
105209
|
+
logger.warn(
|
|
105210
|
+
"BindingRouter: failed to persist session map, will retry on next write",
|
|
105211
|
+
err
|
|
105212
|
+
);
|
|
105028
105213
|
}
|
|
105029
105214
|
return sessionId;
|
|
105030
105215
|
} finally {
|
|
@@ -105060,10 +105245,7 @@ var BindingRouter = class _BindingRouter {
|
|
|
105060
105245
|
agentId: binding.agentId,
|
|
105061
105246
|
projectPath
|
|
105062
105247
|
});
|
|
105063
|
-
const session = await this.deps.agentManager.createSession(
|
|
105064
|
-
projectPath,
|
|
105065
|
-
binding.permissionMode
|
|
105066
|
-
);
|
|
105248
|
+
const session = await this.deps.agentManager.createSession(projectPath, binding.permissionMode);
|
|
105067
105249
|
return session.id;
|
|
105068
105250
|
}
|
|
105069
105251
|
/**
|
|
@@ -105123,7 +105305,10 @@ var BindingRouter = class _BindingRouter {
|
|
|
105123
105305
|
}
|
|
105124
105306
|
}
|
|
105125
105307
|
saveSessionMap() {
|
|
105126
|
-
this.writeLock = this.writeLock.then(
|
|
105308
|
+
this.writeLock = this.writeLock.then(
|
|
105309
|
+
() => this.doSaveSessionMap(),
|
|
105310
|
+
() => this.doSaveSessionMap()
|
|
105311
|
+
);
|
|
105127
105312
|
return this.writeLock;
|
|
105128
105313
|
}
|
|
105129
105314
|
/** Atomic tmp+rename write of the session map. Must be serialized via writeLock. */
|
|
@@ -105256,7 +105441,9 @@ var AdapterManager = class {
|
|
|
105256
105441
|
/** Initialize the binding subsystem. Non-fatal on failure — logs and continues. */
|
|
105257
105442
|
async initBindingSubsystem() {
|
|
105258
105443
|
if (!this.deps.relayCore || !this.deps.meshCore) {
|
|
105259
|
-
logger.info(
|
|
105444
|
+
logger.info(
|
|
105445
|
+
"[AdapterManager] relayCore or meshCore not provided, skipping binding subsystem"
|
|
105446
|
+
);
|
|
105260
105447
|
return;
|
|
105261
105448
|
}
|
|
105262
105449
|
this.bindingSubsystem = await BindingSubsystem.init({
|
|
@@ -105316,7 +105503,11 @@ var AdapterManager = class {
|
|
|
105316
105503
|
config.enabled = false;
|
|
105317
105504
|
await saveAdapterConfig(this.configPath, this.configs);
|
|
105318
105505
|
await this.registry.unregister(id);
|
|
105319
|
-
this.deps.eventRecorder?.insertAdapterEvent(
|
|
105506
|
+
this.deps.eventRecorder?.insertAdapterEvent(
|
|
105507
|
+
id,
|
|
105508
|
+
"adapter.disconnected",
|
|
105509
|
+
"Disconnected from relay"
|
|
105510
|
+
);
|
|
105320
105511
|
}
|
|
105321
105512
|
/**
|
|
105322
105513
|
* List all adapter configs paired with their current runtime status.
|
|
@@ -105342,10 +105533,7 @@ var AdapterManager = class {
|
|
|
105342
105533
|
};
|
|
105343
105534
|
const maskedConfig = {
|
|
105344
105535
|
...config,
|
|
105345
|
-
config: maskSensitiveFields(
|
|
105346
|
-
config.config,
|
|
105347
|
-
manifest
|
|
105348
|
-
)
|
|
105536
|
+
config: maskSensitiveFields(config.config, manifest)
|
|
105349
105537
|
};
|
|
105350
105538
|
return { config: maskedConfig, status };
|
|
105351
105539
|
}
|
|
@@ -105401,6 +105589,7 @@ var AdapterManager = class {
|
|
|
105401
105589
|
}
|
|
105402
105590
|
/** Add a new adapter instance, persist config, and start it if enabled. */
|
|
105403
105591
|
async addAdapter(type, id, config, enabled = true, label) {
|
|
105592
|
+
logger.info("[AdapterManager] adding adapter", { type, id, enabled });
|
|
105404
105593
|
if (this.configs.some((c3) => c3.id === id)) {
|
|
105405
105594
|
throw new AdapterError(`Adapter with ID '${id}' already exists`, "DUPLICATE_ID");
|
|
105406
105595
|
}
|
|
@@ -105427,10 +105616,25 @@ var AdapterManager = class {
|
|
|
105427
105616
|
};
|
|
105428
105617
|
this.configs.push(adapterConfig);
|
|
105429
105618
|
await saveAdapterConfig(this.configPath, this.configs);
|
|
105619
|
+
logger.debug("[AdapterManager] config saved", { id });
|
|
105430
105620
|
if (enabled) {
|
|
105431
105621
|
const adapter = await this.buildAdapter(adapterConfig);
|
|
105432
105622
|
if (adapter) {
|
|
105433
|
-
|
|
105623
|
+
logger.info("[AdapterManager] starting adapter", { id });
|
|
105624
|
+
try {
|
|
105625
|
+
await this.registry.register(adapter);
|
|
105626
|
+
this.deps.eventRecorder?.insertAdapterEvent(
|
|
105627
|
+
id,
|
|
105628
|
+
"adapter.connected",
|
|
105629
|
+
"Connected to relay"
|
|
105630
|
+
);
|
|
105631
|
+
logger.info("[AdapterManager] adapter registered", { id });
|
|
105632
|
+
} catch (err) {
|
|
105633
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
105634
|
+
this.deps.eventRecorder?.insertAdapterEvent(id, "adapter.error", message);
|
|
105635
|
+
logger.error("[AdapterManager] adapter start failed", { id, error: message });
|
|
105636
|
+
throw err;
|
|
105637
|
+
}
|
|
105434
105638
|
}
|
|
105435
105639
|
}
|
|
105436
105640
|
}
|
|
@@ -105603,10 +105807,7 @@ var AdapterManager = class {
|
|
|
105603
105807
|
if (manifest.setupGuide) continue;
|
|
105604
105808
|
try {
|
|
105605
105809
|
const docsPath = this.resolveAdapterDocsPath(type);
|
|
105606
|
-
const setupGuide = await readFile8(
|
|
105607
|
-
join12(docsPath, "setup.md"),
|
|
105608
|
-
"utf-8"
|
|
105609
|
-
);
|
|
105810
|
+
const setupGuide = await readFile8(join12(docsPath, "setup.md"), "utf-8");
|
|
105610
105811
|
this.manifests.set(type, { ...manifest, setupGuide });
|
|
105611
105812
|
} catch {
|
|
105612
105813
|
}
|
|
@@ -105923,7 +106124,10 @@ function createRelayRouter(relayCore2, adapterManager2, traceStore2) {
|
|
|
105923
106124
|
return res.json(publishResult);
|
|
105924
106125
|
} catch (err) {
|
|
105925
106126
|
const message = err instanceof Error ? err.message : "Publish failed";
|
|
105926
|
-
return res.status(422).json({
|
|
106127
|
+
return res.status(422).json({
|
|
106128
|
+
error: message,
|
|
106129
|
+
code: err?.code ?? "PUBLISH_FAILED"
|
|
106130
|
+
});
|
|
105927
106131
|
}
|
|
105928
106132
|
});
|
|
105929
106133
|
router13.get("/messages", (_req, res) => {
|
|
@@ -105998,9 +106202,7 @@ function createRelayRouter(relayCore2, adapterManager2, traceStore2) {
|
|
|
105998
106202
|
});
|
|
105999
106203
|
router13.get("/dead-letters", async (_req, res) => {
|
|
106000
106204
|
const endpointHash = _req.query.endpointHash;
|
|
106001
|
-
const deadLetters = await relayCore2.getDeadLetters(
|
|
106002
|
-
endpointHash ? { endpointHash } : void 0
|
|
106003
|
-
);
|
|
106205
|
+
const deadLetters = await relayCore2.getDeadLetters(endpointHash ? { endpointHash } : void 0);
|
|
106004
106206
|
return res.json(deadLetters);
|
|
106005
106207
|
});
|
|
106006
106208
|
router13.get("/dead-letters/aggregated", async (_req, res) => {
|
|
@@ -106297,9 +106499,7 @@ var TraceStore = class {
|
|
|
106297
106499
|
const rows = this.db.select({
|
|
106298
106500
|
metadata: relayTraces.metadata,
|
|
106299
106501
|
sentAt: relayTraces.sentAt
|
|
106300
|
-
}).from(relayTraces).where(
|
|
106301
|
-
sql`json_extract(${relayTraces.metadata}, '$.adapterId') = ${adapterId}`
|
|
106302
|
-
).all();
|
|
106502
|
+
}).from(relayTraces).where(sql`json_extract(${relayTraces.metadata}, '$.adapterId') = ${adapterId}`).all();
|
|
106303
106503
|
const VALID_CHANNEL_TYPES = /* @__PURE__ */ new Set(["dm", "group", "channel", "thread"]);
|
|
106304
106504
|
const chatMap = /* @__PURE__ */ new Map();
|
|
106305
106505
|
for (const row of rows) {
|
|
@@ -106694,11 +106894,10 @@ var DenialList = class {
|
|
|
106694
106894
|
* onConflictDoUpdate on the unique `path` column.
|
|
106695
106895
|
*
|
|
106696
106896
|
* @param filePath - Absolute path to the project directory
|
|
106697
|
-
* @param strategy - Strategy name that detected the directory
|
|
106698
106897
|
* @param reason - Human-readable reason for denial (optional)
|
|
106699
106898
|
* @param denier - Identifier of the entity performing the denial (e.g., "user", "system")
|
|
106700
106899
|
*/
|
|
106701
|
-
deny(filePath,
|
|
106900
|
+
deny(filePath, reason, denier) {
|
|
106702
106901
|
const canonicalPath = this.canonicalize(filePath);
|
|
106703
106902
|
this.db.insert(agentDenials).values({
|
|
106704
106903
|
id: generateUlid4(),
|
|
@@ -106757,9 +106956,6 @@ var DenialList = class {
|
|
|
106757
106956
|
rowToRecord(row) {
|
|
106758
106957
|
return {
|
|
106759
106958
|
path: row.path,
|
|
106760
|
-
// DenialRecord requires strategy but agentDenials schema doesn't have it;
|
|
106761
|
-
// use 'manual' as default since strategy was dropped in the schema migration.
|
|
106762
|
-
strategy: "manual",
|
|
106763
106959
|
reason: row.reason ?? void 0,
|
|
106764
106960
|
deniedBy: row.denier ?? "unknown",
|
|
106765
106961
|
deniedAt: row.createdAt
|
|
@@ -107156,7 +107352,10 @@ function validateNamespace(ns) {
|
|
|
107156
107352
|
return { valid: false, reason: "Namespace must not be empty" };
|
|
107157
107353
|
}
|
|
107158
107354
|
if (ns.length > MAX_NAMESPACE_LENGTH) {
|
|
107159
|
-
return {
|
|
107355
|
+
return {
|
|
107356
|
+
valid: false,
|
|
107357
|
+
reason: `Namespace must be at most ${MAX_NAMESPACE_LENGTH} characters (got ${ns.length})`
|
|
107358
|
+
};
|
|
107160
107359
|
}
|
|
107161
107360
|
return { valid: true };
|
|
107162
107361
|
}
|
|
@@ -107247,7 +107446,6 @@ function manifestDiffersFromEntry(manifest, entry) {
|
|
|
107247
107446
|
|
|
107248
107447
|
// ../mesh/dist/discovery/unified-scanner.js
|
|
107249
107448
|
import fs18 from "fs/promises";
|
|
107250
|
-
import { realpathSync as realpathSync2 } from "fs";
|
|
107251
107449
|
import path29 from "path";
|
|
107252
107450
|
|
|
107253
107451
|
// ../mesh/dist/discovery/types.js
|
|
@@ -107313,7 +107511,7 @@ async function* unifiedScan(options, strategies, registry2, denialList) {
|
|
|
107313
107511
|
if (followSymlinks) {
|
|
107314
107512
|
let realDir;
|
|
107315
107513
|
try {
|
|
107316
|
-
realDir =
|
|
107514
|
+
realDir = await fs18.realpath(dir);
|
|
107317
107515
|
} catch {
|
|
107318
107516
|
continue;
|
|
107319
107517
|
}
|
|
@@ -107330,7 +107528,9 @@ async function* unifiedScan(options, strategies, registry2, denialList) {
|
|
|
107330
107528
|
}
|
|
107331
107529
|
let entries;
|
|
107332
107530
|
try {
|
|
107333
|
-
entries = await fs18.readdir(dir, {
|
|
107531
|
+
entries = await fs18.readdir(dir, {
|
|
107532
|
+
withFileTypes: true
|
|
107533
|
+
});
|
|
107334
107534
|
} catch (err) {
|
|
107335
107535
|
const code3 = err.code;
|
|
107336
107536
|
if (code3 === "EACCES" || code3 === "EPERM") {
|
|
@@ -107368,7 +107568,7 @@ async function* unifiedScan(options, strategies, registry2, denialList) {
|
|
|
107368
107568
|
for (const entry of entries) {
|
|
107369
107569
|
const isDir = entry.isDirectory();
|
|
107370
107570
|
const isSymlink = entry.isSymbolicLink();
|
|
107371
|
-
if (!isDir && !
|
|
107571
|
+
if (!isDir && !isSymlink)
|
|
107372
107572
|
continue;
|
|
107373
107573
|
if (isSymlink && !followSymlinks)
|
|
107374
107574
|
continue;
|
|
@@ -107653,9 +107853,8 @@ function listCrossNamespaceRules(deps) {
|
|
|
107653
107853
|
}
|
|
107654
107854
|
|
|
107655
107855
|
// ../mesh/dist/mesh-denial.js
|
|
107656
|
-
|
|
107657
|
-
|
|
107658
|
-
deps.denialList.deny(filePath, "manual", reason, denier);
|
|
107856
|
+
async function deny(deps, filePath, reason, denier = DEFAULT_REGISTRAR) {
|
|
107857
|
+
deps.denialList.deny(filePath, reason, denier);
|
|
107659
107858
|
}
|
|
107660
107859
|
async function undeny(deps, filePath) {
|
|
107661
107860
|
deps.denialList.clear(filePath);
|
|
@@ -107694,8 +107893,23 @@ var MeshCore = class {
|
|
|
107694
107893
|
this.relayBridge = relayBridge;
|
|
107695
107894
|
this.defaultScanRoot = defaultScanRoot;
|
|
107696
107895
|
this.logger = logger3;
|
|
107697
|
-
this.discoveryDeps = {
|
|
107698
|
-
|
|
107896
|
+
this.discoveryDeps = {
|
|
107897
|
+
registry: registry2,
|
|
107898
|
+
denialList,
|
|
107899
|
+
relayBridge,
|
|
107900
|
+
strategies,
|
|
107901
|
+
defaultScanRoot,
|
|
107902
|
+
logger: logger3,
|
|
107903
|
+
generateUlid: monotonicFactory()
|
|
107904
|
+
};
|
|
107905
|
+
this.agentDeps = {
|
|
107906
|
+
registry: registry2,
|
|
107907
|
+
relayBridge,
|
|
107908
|
+
topology,
|
|
107909
|
+
signalEmitter: options.signalEmitter,
|
|
107910
|
+
logger: logger3,
|
|
107911
|
+
onUnregisterCallbacks: this.onUnregisterCallbacks
|
|
107912
|
+
};
|
|
107699
107913
|
this.denialDeps = { denialList };
|
|
107700
107914
|
}
|
|
107701
107915
|
// --- Discovery & Registration ---
|
|
@@ -107897,9 +108111,7 @@ function enrichAgent(agent, namespace, deps, scheduleCounts, relayEndpoints) {
|
|
|
107897
108111
|
if (relaySubject && relayEndpoints.length > 0) {
|
|
107898
108112
|
try {
|
|
107899
108113
|
const nsPrefix = `relay.agent.${namespace}.`;
|
|
107900
|
-
const matchingEndpoints = relayEndpoints.filter(
|
|
107901
|
-
(ep) => ep.subject.startsWith(nsPrefix)
|
|
107902
|
-
);
|
|
108114
|
+
const matchingEndpoints = relayEndpoints.filter((ep) => ep.subject.startsWith(nsPrefix));
|
|
107903
108115
|
relayAdapters = matchingEndpoints.map((ep) => ep.subject.slice(nsPrefix.length)).filter(Boolean);
|
|
107904
108116
|
} catch {
|
|
107905
108117
|
}
|
|
@@ -107973,7 +108185,9 @@ function createMeshRouter(deps) {
|
|
|
107973
108185
|
const name = overrides?.name;
|
|
107974
108186
|
const runtime = overrides?.runtime;
|
|
107975
108187
|
if (!name || !runtime) {
|
|
107976
|
-
return res.status(400).json({
|
|
108188
|
+
return res.status(400).json({
|
|
108189
|
+
error: "overrides.name and overrides.runtime are required for manual registration"
|
|
108190
|
+
});
|
|
107977
108191
|
}
|
|
107978
108192
|
try {
|
|
107979
108193
|
const manifest = await meshCore2.registerByPath(
|
|
@@ -108296,7 +108510,7 @@ var import_ip_address = __toESM(require_ip_address(), 1);
|
|
|
108296
108510
|
import { isIPv6 } from "node:net";
|
|
108297
108511
|
import { isIPv6 as isIPv62 } from "node:net";
|
|
108298
108512
|
import { Buffer as Buffer2 } from "node:buffer";
|
|
108299
|
-
import { createHash
|
|
108513
|
+
import { createHash } from "node:crypto";
|
|
108300
108514
|
import { isIP } from "node:net";
|
|
108301
108515
|
function ipKeyGenerator(ip, ipv6Subnet = 56) {
|
|
108302
108516
|
if (ipv6Subnet && isIPv6(ip)) {
|
|
@@ -108460,7 +108674,7 @@ var getResetSeconds = (windowMs, resetTime) => {
|
|
|
108460
108674
|
return resetSeconds;
|
|
108461
108675
|
};
|
|
108462
108676
|
var getPartitionKey = (key) => {
|
|
108463
|
-
const hash =
|
|
108677
|
+
const hash = createHash("sha256");
|
|
108464
108678
|
hash.update(key);
|
|
108465
108679
|
const partitionKey = hash.digest("hex").slice(0, 12);
|
|
108466
108680
|
return Buffer2.from(partitionKey).toString("base64");
|
|
@@ -114790,17 +115004,21 @@ function createExternalMcpServer(deps) {
|
|
|
114790
115004
|
},
|
|
114791
115005
|
handleGetServerInfo
|
|
114792
115006
|
);
|
|
115007
|
+
const agentScopeSchema2 = {
|
|
115008
|
+
agent_id: z27.string().optional().describe("Agent ULID to scope the query to"),
|
|
115009
|
+
cwd: z27.string().optional().describe("Working directory path to scope the query to")
|
|
115010
|
+
};
|
|
114793
115011
|
server.tool(
|
|
114794
115012
|
"get_session_count",
|
|
114795
|
-
"Returns the number of sessions
|
|
114796
|
-
|
|
115013
|
+
"Returns the number of sessions for a specific agent. Provide either agent_id (ULID) or cwd (working directory path).",
|
|
115014
|
+
agentScopeSchema2,
|
|
114797
115015
|
createGetSessionCountHandler(deps)
|
|
114798
115016
|
);
|
|
114799
115017
|
server.tool(
|
|
114800
|
-
"
|
|
114801
|
-
"Get the agent
|
|
114802
|
-
|
|
114803
|
-
|
|
115018
|
+
"get_agent",
|
|
115019
|
+
"Get the agent manifest for a specific agent. Provide either agent_id (ULID) or cwd (working directory path). Returns the agent manifest from .dork/agent.json if one exists, or null if no agent is registered.",
|
|
115020
|
+
agentScopeSchema2,
|
|
115021
|
+
createGetAgentHandler(deps)
|
|
114804
115022
|
);
|
|
114805
115023
|
server.tool(
|
|
114806
115024
|
"pulse_list_schedules",
|
|
@@ -114900,14 +115118,14 @@ function createExternalMcpServer(deps) {
|
|
|
114900
115118
|
createRelayRegisterEndpointHandler(deps)
|
|
114901
115119
|
);
|
|
114902
115120
|
server.tool(
|
|
114903
|
-
"
|
|
115121
|
+
"relay_send_and_wait",
|
|
114904
115122
|
'Send a message to an agent and WAIT for the reply in a single call. Preferred over relay_send + relay_inbox polling for request/reply patterns. Internally registers an ephemeral inbox, sends the message with replyTo set, and blocks until the target agent replies or the timeout elapses. Response shape: { reply, progress, from, replyMessageId, sentMessageId }. progress: array of intermediate steps emitted before the final reply (empty [] for quick replies; populated for multi-step CCA tasks). Each progress step: { type: "progress", step: number, step_type: "message"|"tool_result", text: string, done: false }. Callers that only use { reply, from, replyMessageId } are unaffected \u2014 progress is additive.',
|
|
114905
115123
|
{
|
|
114906
115124
|
to_subject: z27.string().describe('Target subject for the message (e.g., "relay.agent.{agentId}")'),
|
|
114907
115125
|
payload: z27.unknown().describe("Message payload (any JSON-serializable value)"),
|
|
114908
115126
|
from: z27.string().describe("Sender subject identifier"),
|
|
114909
115127
|
timeout_ms: z27.number().int().min(1e3).max(6e5).optional().describe(
|
|
114910
|
-
"Max milliseconds to wait for a reply (default: 60000, max: 600000). For tasks longer than 10 min, use
|
|
115128
|
+
"Max milliseconds to wait for a reply (default: 60000, max: 600000). For tasks longer than 10 min, use relay_send_async instead."
|
|
114911
115129
|
),
|
|
114912
115130
|
budget: z27.object({
|
|
114913
115131
|
maxHops: z27.number().int().min(1).optional().describe("Max hop count"),
|
|
@@ -114918,8 +115136,8 @@ function createExternalMcpServer(deps) {
|
|
|
114918
115136
|
createRelayQueryHandler(deps)
|
|
114919
115137
|
);
|
|
114920
115138
|
server.tool(
|
|
114921
|
-
"
|
|
114922
|
-
"Dispatch a message to an agent and return IMMEDIATELY with a dispatch inbox subject. Unlike
|
|
115139
|
+
"relay_send_async",
|
|
115140
|
+
"Dispatch a message to an agent and return IMMEDIATELY with a dispatch inbox subject. Unlike relay_send_and_wait (which blocks), relay_send_async returns { messageId, inboxSubject } at once. Agent B runs asynchronously; CCA publishes incremental progress events and a final agent_result to the inbox. Poll relay_inbox(endpoint_subject=inboxSubject) for updates. When you receive a message with done:true, call relay_unregister_endpoint(inboxSubject) to clean up.",
|
|
114923
115141
|
{
|
|
114924
115142
|
to_subject: z27.string().describe('Target subject (e.g., "relay.agent.{agentId}")'),
|
|
114925
115143
|
payload: z27.unknown().describe("Message payload"),
|
|
@@ -114934,7 +115152,7 @@ function createExternalMcpServer(deps) {
|
|
|
114934
115152
|
);
|
|
114935
115153
|
server.tool(
|
|
114936
115154
|
"relay_unregister_endpoint",
|
|
114937
|
-
"Unregister a Relay endpoint. Use to clean up dispatch inboxes after
|
|
115155
|
+
"Unregister a Relay endpoint. Use to clean up dispatch inboxes after relay_send_async completes (when done:true received).",
|
|
114938
115156
|
{
|
|
114939
115157
|
subject: z27.string().describe("Subject of the endpoint to unregister")
|
|
114940
115158
|
},
|
|
@@ -116372,14 +116590,20 @@ function createMcpRouter(serverFactory) {
|
|
|
116372
116590
|
router13.get("/", (_req, res) => {
|
|
116373
116591
|
res.status(405).json({
|
|
116374
116592
|
jsonrpc: "2.0",
|
|
116375
|
-
error: {
|
|
116593
|
+
error: {
|
|
116594
|
+
code: -32e3,
|
|
116595
|
+
message: "Method not allowed. This server operates in stateless mode."
|
|
116596
|
+
},
|
|
116376
116597
|
id: null
|
|
116377
116598
|
});
|
|
116378
116599
|
});
|
|
116379
116600
|
router13.delete("/", (_req, res) => {
|
|
116380
116601
|
res.status(405).json({
|
|
116381
116602
|
jsonrpc: "2.0",
|
|
116382
|
-
error: {
|
|
116603
|
+
error: {
|
|
116604
|
+
code: -32e3,
|
|
116605
|
+
message: "Method not allowed. This server operates in stateless mode."
|
|
116606
|
+
},
|
|
116383
116607
|
id: null
|
|
116384
116608
|
});
|
|
116385
116609
|
});
|
|
@@ -116414,10 +116638,7 @@ function validateMcpOrigin(req, res, next) {
|
|
|
116414
116638
|
return;
|
|
116415
116639
|
}
|
|
116416
116640
|
const port = env.DORKOS_PORT;
|
|
116417
|
-
const allowed = [
|
|
116418
|
-
`http://localhost:${port}`,
|
|
116419
|
-
`http://127.0.0.1:${port}`
|
|
116420
|
-
];
|
|
116641
|
+
const allowed = [`http://localhost:${port}`, `http://127.0.0.1:${port}`];
|
|
116421
116642
|
const tunnelUrl = tunnelManager.status.url;
|
|
116422
116643
|
if (tunnelUrl) {
|
|
116423
116644
|
allowed.push(new URL(tunnelUrl).origin);
|
|
@@ -116488,7 +116709,10 @@ async function start() {
|
|
|
116488
116709
|
logger.info("[Runtime] ClaudeCodeRuntime registered as default");
|
|
116489
116710
|
}
|
|
116490
116711
|
const schedulerConfig = configManager.get("scheduler");
|
|
116491
|
-
const pulseEnabled =
|
|
116712
|
+
const pulseEnabled = (
|
|
116713
|
+
// eslint-disable-next-line no-restricted-syntax -- Checking presence, not value: env.ts can't distinguish "unset" from "set to false"
|
|
116714
|
+
"DORKOS_PULSE_ENABLED" in process.env ? env.DORKOS_PULSE_ENABLED : schedulerConfig.enabled
|
|
116715
|
+
);
|
|
116492
116716
|
let pulseStore;
|
|
116493
116717
|
if (pulseEnabled) {
|
|
116494
116718
|
try {
|
|
@@ -116501,8 +116725,11 @@ async function start() {
|
|
|
116501
116725
|
}
|
|
116502
116726
|
}
|
|
116503
116727
|
const relayConfig = configManager.get("relay");
|
|
116504
|
-
const relayEnabled =
|
|
116505
|
-
|
|
116728
|
+
const relayEnabled = (
|
|
116729
|
+
// eslint-disable-next-line no-restricted-syntax -- Checking presence, not value: env.ts can't distinguish "unset" from "set to false"
|
|
116730
|
+
"DORKOS_RELAY_ENABLED" in process.env ? env.DORKOS_RELAY_ENABLED : relayConfig.enabled
|
|
116731
|
+
);
|
|
116732
|
+
const relayDataDir = relayConfig.dataDir ?? path33.join(dorkHome, "relay");
|
|
116506
116733
|
if (relayEnabled) {
|
|
116507
116734
|
try {
|
|
116508
116735
|
adapterRegistry = new AdapterRegistry();
|
|
@@ -116584,23 +116811,36 @@ async function start() {
|
|
|
116584
116811
|
};
|
|
116585
116812
|
claudeRuntime.setMcpServerFactory(() => ({ dorkos: createDorkOsToolServer(mcpToolDeps) }));
|
|
116586
116813
|
const mcpAuthMode = env.MCP_API_KEY ? "auth: API key" : "auth: none";
|
|
116587
|
-
app.use(
|
|
116814
|
+
app.use(
|
|
116815
|
+
"/mcp",
|
|
116816
|
+
validateMcpOrigin,
|
|
116817
|
+
mcpApiKeyAuth,
|
|
116818
|
+
createMcpRouter(() => createExternalMcpServer(mcpToolDeps))
|
|
116819
|
+
);
|
|
116588
116820
|
logger.info(`[MCP] External MCP server mounted at /mcp (stateless, ${mcpAuthMode})`);
|
|
116589
116821
|
}
|
|
116590
116822
|
if (pulseEnabled && pulseStore && claudeRuntime) {
|
|
116591
|
-
schedulerService = new SchedulerService(
|
|
116592
|
-
|
|
116593
|
-
|
|
116594
|
-
|
|
116595
|
-
|
|
116596
|
-
|
|
116823
|
+
schedulerService = new SchedulerService(
|
|
116824
|
+
pulseStore,
|
|
116825
|
+
claudeRuntime,
|
|
116826
|
+
{
|
|
116827
|
+
maxConcurrentRuns: schedulerConfig.maxConcurrentRuns,
|
|
116828
|
+
retentionCount: schedulerConfig.retentionCount,
|
|
116829
|
+
timezone: schedulerConfig.timezone
|
|
116830
|
+
},
|
|
116831
|
+
relayCore,
|
|
116832
|
+
meshCore
|
|
116833
|
+
);
|
|
116834
|
+
app.use("/api/pulse", createPulseRouter(pulseStore, schedulerService, dorkHome, meshCore));
|
|
116597
116835
|
setPulseEnabled(true);
|
|
116598
116836
|
logger.info("[Pulse] Routes mounted and scheduler configured");
|
|
116599
116837
|
if (meshCore) {
|
|
116600
116838
|
meshCore.onUnregister((agentId) => {
|
|
116601
116839
|
const disabledCount = pulseStore.disableSchedulesByAgentId(agentId);
|
|
116602
116840
|
if (disabledCount > 0) {
|
|
116603
|
-
logger.info(
|
|
116841
|
+
logger.info(
|
|
116842
|
+
`[Pulse] Disabled ${disabledCount} schedule(s) for unregistered agent ${agentId}`
|
|
116843
|
+
);
|
|
116604
116844
|
}
|
|
116605
116845
|
});
|
|
116606
116846
|
}
|
|
@@ -116621,11 +116861,14 @@ async function start() {
|
|
|
116621
116861
|
app.use("/api/discovery", createDiscoveryRouter(meshCore));
|
|
116622
116862
|
logger.info("[Discovery] Routes mounted");
|
|
116623
116863
|
}
|
|
116624
|
-
app.use(
|
|
116625
|
-
|
|
116626
|
-
|
|
116627
|
-
|
|
116628
|
-
|
|
116864
|
+
app.use(
|
|
116865
|
+
"/api/admin",
|
|
116866
|
+
createAdminRouter({
|
|
116867
|
+
dorkHome,
|
|
116868
|
+
shutdownServices,
|
|
116869
|
+
closeDb: () => db.$client.close()
|
|
116870
|
+
})
|
|
116871
|
+
);
|
|
116629
116872
|
logger.info("[Admin] Routes mounted");
|
|
116630
116873
|
finalizeApp(app);
|
|
116631
116874
|
if (relayCore) {
|
|
@@ -116640,12 +116883,9 @@ async function start() {
|
|
|
116640
116883
|
logger.info("[Pulse] Scheduler started");
|
|
116641
116884
|
}
|
|
116642
116885
|
if (claudeRuntime) {
|
|
116643
|
-
healthCheckInterval = setInterval(
|
|
116644
|
-
()
|
|
116645
|
-
|
|
116646
|
-
},
|
|
116647
|
-
INTERVALS.HEALTH_CHECK_MS
|
|
116648
|
-
);
|
|
116886
|
+
healthCheckInterval = setInterval(() => {
|
|
116887
|
+
claudeRuntime.checkSessionHealth();
|
|
116888
|
+
}, INTERVALS.HEALTH_CHECK_MS);
|
|
116649
116889
|
}
|
|
116650
116890
|
if (env.TUNNEL_ENABLED) {
|
|
116651
116891
|
const tunnelPort = env.TUNNEL_PORT ?? PORT;
|
|
@@ -116665,7 +116905,10 @@ async function start() {
|
|
|
116665
116905
|
...isDevPort && { mode: `dev (Vite on :${tunnelPort})` }
|
|
116666
116906
|
});
|
|
116667
116907
|
} catch (err) {
|
|
116668
|
-
logger.warn(
|
|
116908
|
+
logger.warn(
|
|
116909
|
+
"[Tunnel] Failed to start ngrok tunnel \u2014 server continues without tunnel.",
|
|
116910
|
+
logError(err)
|
|
116911
|
+
);
|
|
116669
116912
|
}
|
|
116670
116913
|
}
|
|
116671
116914
|
}
|