@agentconnect.md/daemon 1.0.0-rc.283 → 1.0.0-rc.284
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{acp-host-C-4zFhF8.js → acp-host-CDuL4q1C.js} +78 -31
- package/dist/acp-host-CDuL4q1C.js.map +1 -0
- package/dist/{chat-B8VEWuix.js → chat-lDAUzWEJ.js} +2 -2
- package/dist/{chat-B8VEWuix.js.map → chat-lDAUzWEJ.js.map} +1 -1
- package/dist/index.js +3 -3
- package/dist/{login-eP4k_Gf_.js → login-1aQsRYZ9.js} +2 -2
- package/dist/{login-eP4k_Gf_.js.map → login-1aQsRYZ9.js.map} +1 -1
- package/dist/{run-foreground-Cennaxiw.js → run-foreground-D_sn_AMV.js} +450 -4
- package/dist/{run-foreground-Cennaxiw.js.map → run-foreground-D_sn_AMV.js.map} +1 -1
- package/dist/run-foreground-bNQ9crEV.js +2 -0
- package/package.json +1 -1
- package/dist/acp-host-C-4zFhF8.js.map +0 -1
- package/dist/run-foreground-BFk_9xwW.js +0 -2
|
@@ -5,7 +5,7 @@ import { t as DAEMON_VERSION } from "./version-DGaRXjBK.js";
|
|
|
5
5
|
import { C as _coercedNumber, T as NEVER, a as array, d as literal, f as looseObject, i as any, m as object, o as boolean, p as number$1, t as ZodNumber, v as string, x as url$4 } from "./schemas-CQkO9hC5.js";
|
|
6
6
|
import { C as ZodIssueCode, S as RESERVED_MCP_SERVER_NAME, _ as buildEnvelope, a as agentChildEnv, b as MAX_FRAME_BYTES, c as persistRelays, d as normalizeGitUrl, f as RELAY_DAEMON_SUBPROTOCOL, g as SHARED_CONFIG_ACTION_ID, h as decodeRelayDaemonFrame, i as selectAgent, m as buildRelayDaemonFrame, n as findAgentFiles, o as loadConfig, p as RELAY_DAEMON_WS_PATH, r as loadAgents, s as persistDaemonId, v as decodeEnvelope, y as encode } from "./load-agents-DzCnR0cj.js";
|
|
7
7
|
import { l as resolveRoot, o as mcpSocketPath, u as statePath } from "./paths-DEcUO8lC.js";
|
|
8
|
-
import { S as resolveRuntimes, _ as writeMemoryFile, a as initGitInjection, b as cachedRuntimeNames, c as simpleGit, d as memoryProviderFor, f as MemoryConflictError, g as resolveInMemoryDir, h as listMemory, i as prepareWorkspace, l as createMemoryProvider, m as MemoryTooLargeError, n as installedRuntimes, o as probeGitVersion, p as MemoryPathError, r as prefetchWorkspace, s as sessionGitEnv, t as AcpHost, u as memoryKindOf, v as MEMORY_TOOL_NAMES, x as cachedRuntimeVersions, y as toolsForIntegrations } from "./acp-host-
|
|
8
|
+
import { S as resolveRuntimes, _ as writeMemoryFile, a as initGitInjection, b as cachedRuntimeNames, c as simpleGit, d as memoryProviderFor, f as MemoryConflictError, g as resolveInMemoryDir, h as listMemory, i as prepareWorkspace, l as createMemoryProvider, m as MemoryTooLargeError, n as installedRuntimes, o as probeGitVersion, p as MemoryPathError, r as prefetchWorkspace, s as sessionGitEnv, t as AcpHost, u as memoryKindOf, v as MEMORY_TOOL_NAMES, x as cachedRuntimeVersions, y as toolsForIntegrations } from "./acp-host-CDuL4q1C.js";
|
|
9
9
|
import { i as GitCredentialCache, n as gitcredSocketPath, r as writeGitcredShim, t as GitCredServer } from "./gitcred-server-CMC80O_1.js";
|
|
10
10
|
import { B as ToolListChangedNotificationSchema, D as ListPromptsResultSchema, E as ListChangedOptionsBaseSchema, F as PromptListChangedNotificationSchema, G as isZ4Schema, H as isJSONRPCRequest, I as ReadResourceResultSchema, J as encodeFrame, K as safeParse, L as ResourceListChangedNotificationSchema, M as ListToolsResultSchema, O as ListResourceTemplatesResultSchema, P as McpError, R as SUPPORTED_PROTOCOL_VERSIONS, S as InitializeResultSchema, T as LATEST_PROTOCOL_VERSION, U as isJSONRPCResultResponse, V as isInitializedNotification, W as getObjectShape, _ as ElicitResultSchema, a as AjvJsonSchemaValidator, b as GetPromptResultSchema, c as mergeCapabilities, d as CompleteResultSchema, f as CreateMessageRequestSchema, g as ElicitRequestSchema, h as CreateTaskResultSchema, i as assertToolsCallTaskCapability, k as ListResourcesResultSchema, m as CreateMessageResultWithToolsSchema, n as serializeMessage, o as require_fast_deep_equal, p as CreateMessageResultSchema, q as decodeFrames, r as assertClientRequestTaskCapability, s as Protocol, t as ReadBuffer, u as CallToolResultSchema, v as EmptyResultSchema, w as JSONRPCMessageSchema, y as ErrorCode } from "./stdio-DnS8WcXi.js";
|
|
11
11
|
import { URL as URL$1 } from "url";
|
|
@@ -6393,6 +6393,48 @@ var LocalStore = class {
|
|
|
6393
6393
|
enqueuedAt TEXT NOT NULL
|
|
6394
6394
|
);
|
|
6395
6395
|
CREATE INDEX IF NOT EXISTS inbox_fifo ON inbox (sessionKey, enqueuedAt);
|
|
6396
|
+
-- §3.4/§6.8 main-agent orchestration: a main agent fans out N subtasks to
|
|
6397
|
+
-- worker agents, then waits asynchronously and summarizes. The record is
|
|
6398
|
+
-- persisted BEFORE any delivery (record-first — a fast worker's reply must
|
|
6399
|
+
-- never arrive before the record exists, else §3.3 correlation drops it).
|
|
6400
|
+
-- daemon-local (never on the CP hot path); the deadline epoch is the durable
|
|
6401
|
+
-- SoT for the one-shot cron re-armed on startup. status: active|done|cancelled.
|
|
6402
|
+
CREATE TABLE IF NOT EXISTS orchestration (
|
|
6403
|
+
orchestrationId TEXT PRIMARY KEY,
|
|
6404
|
+
mainSessionKey TEXT NOT NULL,
|
|
6405
|
+
mainAgentId TEXT NOT NULL,
|
|
6406
|
+
-- The main's session coords, stored explicitly (not parsed from mainSessionKey,
|
|
6407
|
+
-- whose channel/thread may contain ':') so the deadline fire wakes the exact session.
|
|
6408
|
+
platform TEXT NOT NULL,
|
|
6409
|
+
channel TEXT NOT NULL,
|
|
6410
|
+
thread TEXT NOT NULL,
|
|
6411
|
+
integrationId TEXT,
|
|
6412
|
+
replyTarget TEXT,
|
|
6413
|
+
deadline INTEGER,
|
|
6414
|
+
status TEXT NOT NULL,
|
|
6415
|
+
createdAt INTEGER NOT NULL,
|
|
6416
|
+
updatedAt INTEGER NOT NULL
|
|
6417
|
+
);
|
|
6418
|
+
CREATE INDEX IF NOT EXISTS orchestration_active ON orchestration (status);
|
|
6419
|
+
-- One row per subtask. Stable correlationId = "<orchestrationId>.<idx>" is the
|
|
6420
|
+
-- delivery/report correlation key (§3.3). State machine (§6.8):
|
|
6421
|
+
-- pending → sending → delivered → succeeded | worker_error | timed_out
|
|
6422
|
+
-- busy/offline are RETRYABLE attempt states folded back to a delivery failure.
|
|
6423
|
+
-- Transitions are CAS + idempotent on (orchestrationId, correlationId).
|
|
6424
|
+
CREATE TABLE IF NOT EXISTS orchestration_subtask (
|
|
6425
|
+
orchestrationId TEXT NOT NULL,
|
|
6426
|
+
correlationId TEXT NOT NULL,
|
|
6427
|
+
idx INTEGER NOT NULL,
|
|
6428
|
+
toAgentId TEXT NOT NULL,
|
|
6429
|
+
text TEXT NOT NULL,
|
|
6430
|
+
status TEXT NOT NULL,
|
|
6431
|
+
result TEXT,
|
|
6432
|
+
deliveryReason TEXT,
|
|
6433
|
+
updatedAt TEXT NOT NULL,
|
|
6434
|
+
PRIMARY KEY (orchestrationId, correlationId)
|
|
6435
|
+
);
|
|
6436
|
+
CREATE INDEX IF NOT EXISTS orchestration_subtask_by_orch
|
|
6437
|
+
ON orchestration_subtask (orchestrationId, idx);
|
|
6396
6438
|
`);
|
|
6397
6439
|
this.migrateSessionUsage();
|
|
6398
6440
|
this.migrateTranscriptToolBody();
|
|
@@ -6773,6 +6815,99 @@ var LocalStore = class {
|
|
|
6773
6815
|
listInboxBySessionKeyFifo() {
|
|
6774
6816
|
return this.db.prepare("SELECT * FROM inbox ORDER BY sessionKey ASC, enqueuedAt ASC").all();
|
|
6775
6817
|
}
|
|
6818
|
+
/**
|
|
6819
|
+
* RECORD-FIRST (§3.4): persist the orchestration header + all subtask rows (status
|
|
6820
|
+
* 'pending') in ONE transaction, BEFORE any delivery. A fast worker's reply that
|
|
6821
|
+
* arrives before this returns has nothing to correlate against and is dropped — so
|
|
6822
|
+
* this MUST complete before startOrchestration delivers anything. Idempotent via
|
|
6823
|
+
* INSERT OR IGNORE on the stable ids (replay-safe).
|
|
6824
|
+
*/
|
|
6825
|
+
createOrchestration(orch, subtasks) {
|
|
6826
|
+
const insertOrch = this.db.prepare(`INSERT OR IGNORE INTO orchestration
|
|
6827
|
+
(orchestrationId, mainSessionKey, mainAgentId, platform, channel, thread,
|
|
6828
|
+
integrationId, replyTarget, deadline, status, createdAt, updatedAt)
|
|
6829
|
+
VALUES (@orchestrationId, @mainSessionKey, @mainAgentId, @platform, @channel, @thread,
|
|
6830
|
+
@integrationId, @replyTarget, @deadline, @status, @createdAt, @updatedAt)`);
|
|
6831
|
+
const insertSub = this.db.prepare(`INSERT OR IGNORE INTO orchestration_subtask
|
|
6832
|
+
(orchestrationId, correlationId, idx, toAgentId, text, status, result, deliveryReason, updatedAt)
|
|
6833
|
+
VALUES (@orchestrationId, @correlationId, @idx, @toAgentId, @text, @status, @result, @deliveryReason, @updatedAt)`);
|
|
6834
|
+
this.db.exec("BEGIN");
|
|
6835
|
+
try {
|
|
6836
|
+
insertOrch.run({
|
|
6837
|
+
orchestrationId: orch.orchestrationId,
|
|
6838
|
+
mainSessionKey: orch.mainSessionKey,
|
|
6839
|
+
mainAgentId: orch.mainAgentId,
|
|
6840
|
+
platform: orch.platform,
|
|
6841
|
+
channel: orch.channel,
|
|
6842
|
+
thread: orch.thread,
|
|
6843
|
+
integrationId: orch.integrationId ?? null,
|
|
6844
|
+
replyTarget: orch.replyTarget ?? null,
|
|
6845
|
+
deadline: orch.deadline ?? null,
|
|
6846
|
+
status: orch.status,
|
|
6847
|
+
createdAt: orch.createdAt,
|
|
6848
|
+
updatedAt: orch.updatedAt
|
|
6849
|
+
});
|
|
6850
|
+
for (const s of subtasks) insertSub.run({
|
|
6851
|
+
orchestrationId: s.orchestrationId,
|
|
6852
|
+
correlationId: s.correlationId,
|
|
6853
|
+
idx: s.idx,
|
|
6854
|
+
toAgentId: s.toAgentId,
|
|
6855
|
+
text: s.text,
|
|
6856
|
+
status: s.status,
|
|
6857
|
+
result: s.result ?? null,
|
|
6858
|
+
deliveryReason: s.deliveryReason ?? null,
|
|
6859
|
+
updatedAt: s.updatedAt
|
|
6860
|
+
});
|
|
6861
|
+
this.db.exec("COMMIT");
|
|
6862
|
+
} catch (err) {
|
|
6863
|
+
this.db.exec("ROLLBACK");
|
|
6864
|
+
throw err;
|
|
6865
|
+
}
|
|
6866
|
+
}
|
|
6867
|
+
getOrchestration(orchestrationId) {
|
|
6868
|
+
return this.db.prepare("SELECT * FROM orchestration WHERE orchestrationId = ?").get(orchestrationId);
|
|
6869
|
+
}
|
|
6870
|
+
getSubtasks(orchestrationId) {
|
|
6871
|
+
return this.db.prepare("SELECT * FROM orchestration_subtask WHERE orchestrationId = ? ORDER BY idx ASC").all(orchestrationId);
|
|
6872
|
+
}
|
|
6873
|
+
getSubtaskByCorrelation(orchestrationId, correlationId) {
|
|
6874
|
+
return this.db.prepare("SELECT * FROM orchestration_subtask WHERE orchestrationId = ? AND correlationId = ?").get(orchestrationId, correlationId);
|
|
6875
|
+
}
|
|
6876
|
+
/** All still-`active` orchestrations — for startup re-arm of deadlines + re-drive
|
|
6877
|
+
* of non-terminal subtasks (§6.8). */
|
|
6878
|
+
listActiveOrchestrations() {
|
|
6879
|
+
return this.db.prepare("SELECT * FROM orchestration WHERE status = 'active' ORDER BY createdAt ASC").all();
|
|
6880
|
+
}
|
|
6881
|
+
/** CAS subtask status — only advances when the current status is one of `from`.
|
|
6882
|
+
* Idempotent + monotonic: a stale/duplicate transition (current status not in
|
|
6883
|
+
* `from`) is a no-op and returns false. */
|
|
6884
|
+
setSubtaskStatus(orchestrationId, correlationId, from, to, updatedAt, extra) {
|
|
6885
|
+
const placeholders = from.map((_, i) => `@from${i}`).join(", ");
|
|
6886
|
+
const params = {
|
|
6887
|
+
orchestrationId,
|
|
6888
|
+
correlationId,
|
|
6889
|
+
to,
|
|
6890
|
+
updatedAt
|
|
6891
|
+
};
|
|
6892
|
+
from.forEach((f, i) => params[`from${i}`] = f);
|
|
6893
|
+
let setResult = "";
|
|
6894
|
+
if (extra && "result" in extra) {
|
|
6895
|
+
setResult += ", result=@result";
|
|
6896
|
+
params.result = extra.result ?? null;
|
|
6897
|
+
}
|
|
6898
|
+
if (extra && "deliveryReason" in extra) {
|
|
6899
|
+
setResult += ", deliveryReason=@deliveryReason";
|
|
6900
|
+
params.deliveryReason = extra.deliveryReason ?? null;
|
|
6901
|
+
}
|
|
6902
|
+
return this.db.prepare(`UPDATE orchestration_subtask SET status=@to, updatedAt=@updatedAt${setResult}
|
|
6903
|
+
WHERE orchestrationId=@orchestrationId AND correlationId=@correlationId AND status IN (${placeholders})`).run(params).changes > 0;
|
|
6904
|
+
}
|
|
6905
|
+
setOrchestrationStatus(orchestrationId, status, updatedAt) {
|
|
6906
|
+
this.db.prepare("UPDATE orchestration SET status=?, updatedAt=? WHERE orchestrationId=?").run(status, updatedAt, orchestrationId);
|
|
6907
|
+
}
|
|
6908
|
+
setOrchestrationDeadline(orchestrationId, deadline, updatedAt) {
|
|
6909
|
+
this.db.prepare("UPDATE orchestration SET deadline=?, updatedAt=? WHERE orchestrationId=?").run(deadline, updatedAt, orchestrationId);
|
|
6910
|
+
}
|
|
6776
6911
|
close() {
|
|
6777
6912
|
this.db.close();
|
|
6778
6913
|
}
|
|
@@ -7174,6 +7309,8 @@ async function executeTool(ctx, name, args, deps) {
|
|
|
7174
7309
|
return await deps.messageAgent({
|
|
7175
7310
|
callerAgentId: ctx.agentId,
|
|
7176
7311
|
platform: ctx.platform,
|
|
7312
|
+
callerChannel: ctx.channel,
|
|
7313
|
+
callerThread: ctx.thread,
|
|
7177
7314
|
toAgentId,
|
|
7178
7315
|
text,
|
|
7179
7316
|
channel,
|
|
@@ -7181,6 +7318,50 @@ async function executeTool(ctx, name, args, deps) {
|
|
|
7181
7318
|
...correlationId !== void 0 ? { correlationId } : {}
|
|
7182
7319
|
});
|
|
7183
7320
|
}
|
|
7321
|
+
if (name === "startOrchestration") {
|
|
7322
|
+
const rawSubtasks = args.subtasks;
|
|
7323
|
+
if (!Array.isArray(rawSubtasks) || rawSubtasks.length === 0) throw new Error("missing required argument: subtasks (non-empty array)");
|
|
7324
|
+
const subtasks = rawSubtasks.map((s, i) => {
|
|
7325
|
+
if (typeof s !== "object" || s === null) throw new Error(`subtasks[${i}] must be an object`);
|
|
7326
|
+
const so = s;
|
|
7327
|
+
return {
|
|
7328
|
+
toAgentId: requireString(so, "toAgentId"),
|
|
7329
|
+
text: requireString(so, "text")
|
|
7330
|
+
};
|
|
7331
|
+
});
|
|
7332
|
+
const deadlineMs = optionalNumber(args, "deadlineMs");
|
|
7333
|
+
const replyTarget = optionalString(args, "replyTarget");
|
|
7334
|
+
return await deps.startOrchestration({
|
|
7335
|
+
mainAgentId: ctx.agentId,
|
|
7336
|
+
platform: ctx.platform,
|
|
7337
|
+
channel: ctx.channel,
|
|
7338
|
+
thread: ctx.thread,
|
|
7339
|
+
...ctx.integrationId !== void 0 ? { integrationId: ctx.integrationId } : {},
|
|
7340
|
+
subtasks,
|
|
7341
|
+
...deadlineMs !== void 0 ? { deadlineMs } : {},
|
|
7342
|
+
...replyTarget !== void 0 ? { replyTarget } : {}
|
|
7343
|
+
});
|
|
7344
|
+
}
|
|
7345
|
+
if (name === "getOrchestration" || name === "cancelOrchestration") {
|
|
7346
|
+
const orchestrationId = requireString(args, "orchestrationId");
|
|
7347
|
+
const req = {
|
|
7348
|
+
mainAgentId: ctx.agentId,
|
|
7349
|
+
platform: ctx.platform,
|
|
7350
|
+
channel: ctx.channel,
|
|
7351
|
+
thread: ctx.thread,
|
|
7352
|
+
orchestrationId
|
|
7353
|
+
};
|
|
7354
|
+
if (name === "getOrchestration") {
|
|
7355
|
+
const rec = await deps.getOrchestration(req);
|
|
7356
|
+
if (rec === null) throw new Error(`no orchestration ${orchestrationId} owned by this session`);
|
|
7357
|
+
return rec;
|
|
7358
|
+
}
|
|
7359
|
+
if (!await deps.cancelOrchestration(req)) throw new Error(`no orchestration ${orchestrationId} owned by this session`);
|
|
7360
|
+
return {
|
|
7361
|
+
orchestrationId,
|
|
7362
|
+
cancelled: true
|
|
7363
|
+
};
|
|
7364
|
+
}
|
|
7184
7365
|
const gw = ctx.integrationId ? deps.gatewayFor(ctx.integrationId) : void 0;
|
|
7185
7366
|
if (!gw) throw new Error(`no live platform connection for integration ${ctx.integrationId ?? "(none)"}`);
|
|
7186
7367
|
switch (name) {
|
|
@@ -7261,6 +7442,12 @@ function optionalString(args, key) {
|
|
|
7261
7442
|
if (typeof v !== "string") throw new Error(`argument ${key} must be a string`);
|
|
7262
7443
|
return v;
|
|
7263
7444
|
}
|
|
7445
|
+
function optionalNumber(args, key) {
|
|
7446
|
+
const v = args[key];
|
|
7447
|
+
if (v === void 0 || v === null) return void 0;
|
|
7448
|
+
if (typeof v !== "number" || !Number.isFinite(v)) throw new Error(`argument ${key} must be a finite number`);
|
|
7449
|
+
return v;
|
|
7450
|
+
}
|
|
7264
7451
|
//#endregion
|
|
7265
7452
|
//#region src/mcp/control-server.ts
|
|
7266
7453
|
/**
|
|
@@ -214227,6 +214414,9 @@ var Daemon = class Daemon {
|
|
|
214227
214414
|
return client.channelAgents(req);
|
|
214228
214415
|
},
|
|
214229
214416
|
messageAgent: (req) => this.messageAgent(req),
|
|
214417
|
+
startOrchestration: (req) => this.startOrchestration(req),
|
|
214418
|
+
getOrchestration: (req) => Promise.resolve(this.getOrchestrationForOwner(req)),
|
|
214419
|
+
cancelOrchestration: (req) => Promise.resolve(this.cancelOrchestrationForOwner(req)),
|
|
214230
214420
|
memory: this.memory,
|
|
214231
214421
|
recordOutbound: (ctx, channel, thread, text, ts) => this.store.appendTranscript({
|
|
214232
214422
|
channel,
|
|
@@ -214342,6 +214532,7 @@ var Daemon = class Daemon {
|
|
|
214342
214532
|
this.watcher.on("add", debounced).on("change", debounced).on("unlink", debounced);
|
|
214343
214533
|
this.log.info(`watching ${this.agentsDir} for agent changes`);
|
|
214344
214534
|
this.replayInbox();
|
|
214535
|
+
this.rearmOrchestrationDeadlines();
|
|
214345
214536
|
this.startCpClient(root);
|
|
214346
214537
|
this.armIdleSweep();
|
|
214347
214538
|
this.log.info("daemon ready");
|
|
@@ -215105,6 +215296,7 @@ var Daemon = class Daemon {
|
|
|
215105
215296
|
}
|
|
215106
215297
|
relayMsgAcks = /* @__PURE__ */ new Map();
|
|
215107
215298
|
agentCallDeliveries = /* @__PURE__ */ new Map();
|
|
215299
|
+
orchestrationDeadlines = /* @__PURE__ */ new Map();
|
|
215108
215300
|
/**
|
|
215109
215301
|
* Dispatch one inbound relay item (`rd/msg` — a webchat op or a hook fire) and
|
|
215110
215302
|
* return the `rd/ack` verdict — the relay-path entry, reusing the SAME turn
|
|
@@ -215272,10 +215464,19 @@ var Daemon = class Daemon {
|
|
|
215272
215464
|
reason: "not_allowed"
|
|
215273
215465
|
});
|
|
215274
215466
|
}
|
|
215275
|
-
const
|
|
215467
|
+
const callerKey = sessionKey(platform, req.callerChannel, req.callerThread, req.callerAgentId);
|
|
215468
|
+
const inbound = this.activeTurnCallMeta.get(callerKey);
|
|
215469
|
+
const hopCount = inbound ? inbound.hopCount + 1 : 0;
|
|
215470
|
+
if (hopCount > MAX_AGENT_CALL_HOPS) return record({
|
|
215471
|
+
delivered: false,
|
|
215472
|
+
targetSession,
|
|
215473
|
+
reason: "hop_limit"
|
|
215474
|
+
});
|
|
215475
|
+
const isReply = inbound !== void 0 && req.toAgentId === inbound.callFrom;
|
|
215476
|
+
const correlationId = req.correlationId !== void 0 ? req.correlationId : isReply ? inbound.correlationId : void 0;
|
|
215276
215477
|
const callMeta = {
|
|
215277
215478
|
callFrom: req.callerAgentId,
|
|
215278
|
-
...
|
|
215479
|
+
...correlationId !== void 0 ? { correlationId } : {},
|
|
215279
215480
|
hopCount,
|
|
215280
215481
|
deliveryId
|
|
215281
215482
|
};
|
|
@@ -215358,6 +215559,235 @@ var Daemon = class Daemon {
|
|
|
215358
215559
|
return p === "telegram" || p === "webchat" || p === "discord" || p === "hook" ? p : "slack";
|
|
215359
215560
|
}
|
|
215360
215561
|
/**
|
|
215562
|
+
* Start an orchestration (§3.4/§6.8). ATOMIC ordering per §3.4:
|
|
215563
|
+
* (a) RECORD-FIRST — persist the orchestration header + every subtask (status
|
|
215564
|
+
* 'pending') in one transaction BEFORE any delivery, so a fast worker's reply can
|
|
215565
|
+
* never arrive before the record exists (§3.3 would otherwise drop it);
|
|
215566
|
+
* (b) deliver each subtask via the existing `messageAgent` path, CAS-recording
|
|
215567
|
+
* pending→sending→delivered | pending→sending→(delivered rollback)failed per subtask;
|
|
215568
|
+
* (c) schedule the one-shot, session-anchored, cancelable cron deadline (if requested)
|
|
215569
|
+
* — but only when at least one subtask actually delivered (all-failed ⇒ no wait).
|
|
215570
|
+
* The main identity + coords are the TRUSTED SessionContext (never tool input).
|
|
215571
|
+
*/
|
|
215572
|
+
async startOrchestration(req) {
|
|
215573
|
+
const orchestrationId = randomUUID();
|
|
215574
|
+
const platform = this.narrowPlatform(req.platform);
|
|
215575
|
+
const mainSessionKey = sessionKey(platform, req.channel, req.thread, req.mainAgentId);
|
|
215576
|
+
const now = this.clock.now();
|
|
215577
|
+
const deadline = req.deadlineMs !== void 0 && req.deadlineMs > 0 ? now + Math.min(req.deadlineMs, 2147483647) : null;
|
|
215578
|
+
const subtaskRows = req.subtasks.map((s, idx) => ({
|
|
215579
|
+
orchestrationId,
|
|
215580
|
+
correlationId: `${orchestrationId}.${idx}`,
|
|
215581
|
+
idx,
|
|
215582
|
+
toAgentId: s.toAgentId,
|
|
215583
|
+
text: s.text,
|
|
215584
|
+
status: "pending",
|
|
215585
|
+
updatedAt: monotonicTs()
|
|
215586
|
+
}));
|
|
215587
|
+
const orch = {
|
|
215588
|
+
orchestrationId,
|
|
215589
|
+
mainSessionKey,
|
|
215590
|
+
mainAgentId: req.mainAgentId,
|
|
215591
|
+
platform,
|
|
215592
|
+
channel: req.channel,
|
|
215593
|
+
thread: req.thread,
|
|
215594
|
+
integrationId: req.integrationId ?? null,
|
|
215595
|
+
replyTarget: req.replyTarget ?? null,
|
|
215596
|
+
deadline,
|
|
215597
|
+
status: "active",
|
|
215598
|
+
createdAt: now,
|
|
215599
|
+
updatedAt: now
|
|
215600
|
+
};
|
|
215601
|
+
this.store.createOrchestration(orch, subtaskRows);
|
|
215602
|
+
const delivered = [];
|
|
215603
|
+
const failed = [];
|
|
215604
|
+
for (const s of subtaskRows) {
|
|
215605
|
+
this.store.setSubtaskStatus(orchestrationId, s.correlationId, ["pending"], "sending", monotonicTs());
|
|
215606
|
+
let result;
|
|
215607
|
+
try {
|
|
215608
|
+
result = await this.messageAgent({
|
|
215609
|
+
callerAgentId: req.mainAgentId,
|
|
215610
|
+
platform: req.platform,
|
|
215611
|
+
callerChannel: req.channel,
|
|
215612
|
+
callerThread: req.thread,
|
|
215613
|
+
toAgentId: s.toAgentId,
|
|
215614
|
+
text: s.text,
|
|
215615
|
+
channel: req.channel,
|
|
215616
|
+
thread: req.thread,
|
|
215617
|
+
correlationId: s.correlationId
|
|
215618
|
+
});
|
|
215619
|
+
} catch (err) {
|
|
215620
|
+
result = {
|
|
215621
|
+
delivered: false,
|
|
215622
|
+
targetSession: "",
|
|
215623
|
+
reason: "error"
|
|
215624
|
+
};
|
|
215625
|
+
this.log.warn(`orchestration ${orchestrationId}: delivery of ${s.correlationId} threw: ${formatErr(err)}`);
|
|
215626
|
+
}
|
|
215627
|
+
if (result.delivered) {
|
|
215628
|
+
this.store.setSubtaskStatus(orchestrationId, s.correlationId, ["sending"], "delivered", monotonicTs());
|
|
215629
|
+
delivered.push(s.correlationId);
|
|
215630
|
+
} else {
|
|
215631
|
+
const reason = result.reason ?? "undeliverable";
|
|
215632
|
+
this.store.setSubtaskStatus(orchestrationId, s.correlationId, ["sending"], "worker_error", monotonicTs(), { deliveryReason: reason });
|
|
215633
|
+
failed.push({
|
|
215634
|
+
correlationId: s.correlationId,
|
|
215635
|
+
reason
|
|
215636
|
+
});
|
|
215637
|
+
}
|
|
215638
|
+
}
|
|
215639
|
+
if (deadline !== null && delivered.length > 0) this.armOrchestrationDeadline(orchestrationId, deadline);
|
|
215640
|
+
else if (deadline !== null) this.store.setOrchestrationDeadline(orchestrationId, null, this.clock.now());
|
|
215641
|
+
this.log.info(`orchestration ${orchestrationId}: ${delivered.length} delivered, ${failed.length} failed` + (deadline !== null && delivered.length > 0 ? `, deadline in ${deadline - now}ms` : ""));
|
|
215642
|
+
return {
|
|
215643
|
+
orchestrationId,
|
|
215644
|
+
delivered,
|
|
215645
|
+
failed
|
|
215646
|
+
};
|
|
215647
|
+
}
|
|
215648
|
+
/** Arm (or re-arm) the one-shot, session-anchored deadline for an orchestration. On fire
|
|
215649
|
+
* it wakes the main's session DIRECTLY (dispatch to mainSessionKey) — NOT via fireTrigger
|
|
215650
|
+
* (which would post a channel anchor / open a new thread). Idempotent: replaces any
|
|
215651
|
+
* existing timer for this id. Clamped to setTimeout's 32-bit ceiling. */
|
|
215652
|
+
armOrchestrationDeadline(orchestrationId, deadlineEpoch) {
|
|
215653
|
+
const existing = this.orchestrationDeadlines.get(orchestrationId);
|
|
215654
|
+
if (existing !== void 0) this.clock.clearTimeout(existing);
|
|
215655
|
+
const delay = Math.min(Math.max(0, deadlineEpoch - this.clock.now()), 2147483647);
|
|
215656
|
+
const handle = this.clock.setTimeout(() => {
|
|
215657
|
+
this.orchestrationDeadlines.delete(orchestrationId);
|
|
215658
|
+
this.fireOrchestrationDeadline(orchestrationId);
|
|
215659
|
+
}, delay);
|
|
215660
|
+
this.orchestrationDeadlines.set(orchestrationId, handle);
|
|
215661
|
+
}
|
|
215662
|
+
/** Deadline fired (§3.5): mark every still-open subtask timed_out, then WAKE the main's
|
|
215663
|
+
* session so it re-reads getOrchestration and summarizes the partial result. The wake is
|
|
215664
|
+
* a direct dispatch to the stored session coords — no platform post, no new thread. */
|
|
215665
|
+
fireOrchestrationDeadline(orchestrationId) {
|
|
215666
|
+
const orch = this.store.getOrchestration(orchestrationId);
|
|
215667
|
+
if (!orch || orch.status !== "active") return;
|
|
215668
|
+
for (const s of this.store.getSubtasks(orchestrationId)) this.store.setSubtaskStatus(orchestrationId, s.correlationId, [
|
|
215669
|
+
"pending",
|
|
215670
|
+
"sending",
|
|
215671
|
+
"delivered"
|
|
215672
|
+
], "timed_out", monotonicTs());
|
|
215673
|
+
this.store.setOrchestrationDeadline(orchestrationId, null, this.clock.now());
|
|
215674
|
+
this.wakeOrchestrationMain(orch, `orchestration ${orchestrationId} deadline reached — summarize what you have`);
|
|
215675
|
+
}
|
|
215676
|
+
/** Wake the orchestration's owning main session with a synthetic agent-source turn keyed
|
|
215677
|
+
* to the exact stored coords (so it lands in the same session that started it). Headless
|
|
215678
|
+
* is NOT set — the main needs its reply transport to post the summary. */
|
|
215679
|
+
wakeOrchestrationMain(orch, text) {
|
|
215680
|
+
const platform = this.narrowPlatform(orch.platform);
|
|
215681
|
+
const msg = {
|
|
215682
|
+
msgId: `orchestration:${orch.orchestrationId}:${monotonicTs()}`,
|
|
215683
|
+
traceId: orch.orchestrationId,
|
|
215684
|
+
source: "agent",
|
|
215685
|
+
platform,
|
|
215686
|
+
channel: orch.channel,
|
|
215687
|
+
thread: orch.thread,
|
|
215688
|
+
sender: {
|
|
215689
|
+
id: `orchestration:${orch.orchestrationId}`,
|
|
215690
|
+
isBot: true
|
|
215691
|
+
},
|
|
215692
|
+
text,
|
|
215693
|
+
mentionedBots: [],
|
|
215694
|
+
isDm: false
|
|
215695
|
+
};
|
|
215696
|
+
this.dispatch(orch.mainAgentId, msg, orch.integrationId ?? void 0).catch((err) => this.log.error(`orchestration ${orch.orchestrationId}: deadline wake dispatch failed: ${formatErr(err)}`));
|
|
215697
|
+
}
|
|
215698
|
+
/** §3.3 correlation-recording hook, called from dispatchOne when the MAIN receives a
|
|
215699
|
+
* messageAgent turn carrying a correlationId. Records the worker's result into the
|
|
215700
|
+
* orchestration ONLY if ALL four safety checks hold; any failure drops the report
|
|
215701
|
+
* (debug log) and never corrupts completion. Uses ONLY trusted values (callMeta.callFrom,
|
|
215702
|
+
* the receiving session key) — never a frame/prompt field. */
|
|
215703
|
+
recordWorkerReport(receivingSessionKey, callMeta, reportText) {
|
|
215704
|
+
const correlationId = callMeta.correlationId;
|
|
215705
|
+
if (correlationId === void 0) return;
|
|
215706
|
+
const dot = correlationId.lastIndexOf(".");
|
|
215707
|
+
if (dot <= 0) return;
|
|
215708
|
+
const orchestrationId = correlationId.slice(0, dot);
|
|
215709
|
+
const orch = this.store.getOrchestration(orchestrationId);
|
|
215710
|
+
if (!orch) return;
|
|
215711
|
+
if (orch.mainSessionKey !== receivingSessionKey) {
|
|
215712
|
+
this.log.debug(`orchestration ${orchestrationId}: report dropped — session mismatch`);
|
|
215713
|
+
return;
|
|
215714
|
+
}
|
|
215715
|
+
if (orch.status !== "active") {
|
|
215716
|
+
this.log.debug(`orchestration ${orchestrationId}: report dropped — orchestration ${orch.status}`);
|
|
215717
|
+
return;
|
|
215718
|
+
}
|
|
215719
|
+
const sub = this.store.getSubtaskByCorrelation(orchestrationId, correlationId);
|
|
215720
|
+
if (!sub) return;
|
|
215721
|
+
if (callMeta.callFrom !== sub.toAgentId) {
|
|
215722
|
+
this.log.debug(`orchestration ${orchestrationId}: report for ${correlationId} dropped — reporter "${callMeta.callFrom}" ≠ tasked worker "${sub.toAgentId}"`);
|
|
215723
|
+
return;
|
|
215724
|
+
}
|
|
215725
|
+
if (this.store.setSubtaskStatus(orchestrationId, correlationId, [
|
|
215726
|
+
"sending",
|
|
215727
|
+
"delivered",
|
|
215728
|
+
"timed_out"
|
|
215729
|
+
], "succeeded", monotonicTs(), { result: reportText })) this.log.info(`orchestration ${orchestrationId}: recorded result for ${correlationId} from ${callMeta.callFrom}`);
|
|
215730
|
+
else this.log.debug(`orchestration ${orchestrationId}: duplicate/late report for ${correlationId} — no-op`);
|
|
215731
|
+
}
|
|
215732
|
+
getOrchestrationForOwner(req) {
|
|
215733
|
+
const orch = this.ownedOrchestration(req);
|
|
215734
|
+
if (!orch) return null;
|
|
215735
|
+
return {
|
|
215736
|
+
orchestrationId: orch.orchestrationId,
|
|
215737
|
+
status: orch.status,
|
|
215738
|
+
deadline: orch.deadline ?? null,
|
|
215739
|
+
replyTarget: orch.replyTarget ?? null,
|
|
215740
|
+
createdAt: orch.createdAt,
|
|
215741
|
+
subtasks: this.store.getSubtasks(orch.orchestrationId).map((s) => ({
|
|
215742
|
+
correlationId: s.correlationId,
|
|
215743
|
+
toAgentId: s.toAgentId,
|
|
215744
|
+
status: s.status,
|
|
215745
|
+
result: s.result ?? null,
|
|
215746
|
+
...s.deliveryReason ? { deliveryReason: s.deliveryReason } : {}
|
|
215747
|
+
}))
|
|
215748
|
+
};
|
|
215749
|
+
}
|
|
215750
|
+
cancelOrchestrationForOwner(req) {
|
|
215751
|
+
const orch = this.ownedOrchestration(req);
|
|
215752
|
+
if (!orch) return false;
|
|
215753
|
+
const handle = this.orchestrationDeadlines.get(orch.orchestrationId);
|
|
215754
|
+
if (handle !== void 0) {
|
|
215755
|
+
this.clock.clearTimeout(handle);
|
|
215756
|
+
this.orchestrationDeadlines.delete(orch.orchestrationId);
|
|
215757
|
+
}
|
|
215758
|
+
this.store.setOrchestrationDeadline(orch.orchestrationId, null, this.clock.now());
|
|
215759
|
+
this.store.setOrchestrationStatus(orch.orchestrationId, "cancelled", this.clock.now());
|
|
215760
|
+
return true;
|
|
215761
|
+
}
|
|
215762
|
+
/** Resolve an orchestration IFF the requesting main+session OWNS it (§3.5a owner check).
|
|
215763
|
+
* Returns undefined on unknown id or any owner mismatch. */
|
|
215764
|
+
ownedOrchestration(req) {
|
|
215765
|
+
const orch = this.store.getOrchestration(req.orchestrationId);
|
|
215766
|
+
if (!orch) return void 0;
|
|
215767
|
+
const requesterKey = sessionKey(this.narrowPlatform(req.platform), req.channel, req.thread, req.mainAgentId);
|
|
215768
|
+
if (orch.mainSessionKey !== requesterKey || orch.mainAgentId !== req.mainAgentId) return void 0;
|
|
215769
|
+
return orch;
|
|
215770
|
+
}
|
|
215771
|
+
/** Startup re-arm (§3.5): re-arm the one-shot deadline for every still-active orchestration
|
|
215772
|
+
* from the durable `deadline` epoch. A deadline already in the past fires ~immediately.
|
|
215773
|
+
* Mirrors replayInbox's read-active-rows/re-schedule pattern; idempotent. */
|
|
215774
|
+
rearmOrchestrationDeadlines() {
|
|
215775
|
+
let active;
|
|
215776
|
+
try {
|
|
215777
|
+
active = this.store.listActiveOrchestrations();
|
|
215778
|
+
} catch (err) {
|
|
215779
|
+
this.log.warn(`orchestration: deadline re-arm read failed: ${err.message}`);
|
|
215780
|
+
return;
|
|
215781
|
+
}
|
|
215782
|
+
let armed = 0;
|
|
215783
|
+
for (const orch of active) {
|
|
215784
|
+
if (orch.deadline == null) continue;
|
|
215785
|
+
this.armOrchestrationDeadline(orch.orchestrationId, orch.deadline);
|
|
215786
|
+
armed++;
|
|
215787
|
+
}
|
|
215788
|
+
if (armed) this.log.info(`orchestration: re-armed ${armed} deadline(s) on startup`);
|
|
215789
|
+
}
|
|
215790
|
+
/**
|
|
215361
215791
|
* Ack-verdict gate for one hook fire (`rd/msg` hook member) — the mirror of
|
|
215362
215792
|
* {@link dispatchWebchatTurn}'s synchronous gates: the relay's rc/run-report
|
|
215363
215793
|
* needs a REASONED rejection now, not a silently dropped fire-and-forget
|
|
@@ -216073,6 +216503,7 @@ var Daemon = class Daemon {
|
|
|
216073
216503
|
}
|
|
216074
216504
|
async dispatchOne(entry, key) {
|
|
216075
216505
|
const { agentId, msg, integrationId, webchat, callMeta } = entry;
|
|
216506
|
+
if (callMeta?.correlationId !== void 0) this.recordWorkerReport(key, callMeta, msg.text);
|
|
216076
216507
|
const agent = this.agents.get(agentId);
|
|
216077
216508
|
const rec = new TranscriptRecorder();
|
|
216078
216509
|
const replyConn = msg.headless || webchat ? void 0 : this.replyConnFor(agentId, integrationId);
|
|
@@ -216129,6 +216560,7 @@ var Daemon = class Daemon {
|
|
|
216129
216560
|
} } : {}
|
|
216130
216561
|
};
|
|
216131
216562
|
this.pending.set(sessionId, p);
|
|
216563
|
+
if (callMeta) this.activeTurnCallMeta.set(key, callMeta);
|
|
216132
216564
|
let finalPhase = "end";
|
|
216133
216565
|
try {
|
|
216134
216566
|
const host = await this.ensureHostAsync(agentId);
|
|
@@ -216204,6 +216636,7 @@ var Daemon = class Daemon {
|
|
|
216204
216636
|
this.releasePermissions(sessionId);
|
|
216205
216637
|
this.releaseElicits(sessionId);
|
|
216206
216638
|
this.pending.delete(sessionId);
|
|
216639
|
+
if (callMeta) this.activeTurnCallMeta.delete(key);
|
|
216207
216640
|
this.store.setSessionState(key, "idle", this.clock.now());
|
|
216208
216641
|
this.emitSessionMetadataSnapshot({
|
|
216209
216642
|
sessionId,
|
|
@@ -216693,6 +217126,17 @@ var Daemon = class Daemon {
|
|
|
216693
217126
|
p.idleTimer = void 0;
|
|
216694
217127
|
}
|
|
216695
217128
|
pending = /* @__PURE__ */ new Map();
|
|
217129
|
+
/**
|
|
217130
|
+
* DAEMON-PRIVATE trusted call metadata of the CURRENTLY in-flight turn, keyed by the
|
|
217131
|
+
* turn's LOGICAL sessionKey (§6.7). Installed at the start of a `dispatchOne` turn that
|
|
217132
|
+
* carries `callMeta` and cleared in its `finally`; the tool is keyed by the static
|
|
217133
|
+
* session-level MCP token, so a nested `messageAgent` resolves the caller's sessionKey
|
|
217134
|
+
* (from its trusted SessionContext coords + agentId) and reads THIS map to auto-inherit
|
|
217135
|
+
* hop/origin (all child calls) and correlationId (replies only) — never trusting the
|
|
217136
|
+
* agent to pass them. A plain human/platform turn has no entry, so a `messageAgent` it
|
|
217137
|
+
* makes is a fresh call (hopCount 0, no inherited correlation).
|
|
217138
|
+
*/
|
|
217139
|
+
activeTurnCallMeta = /* @__PURE__ */ new Map();
|
|
216696
217140
|
permSeq = 0;
|
|
216697
217141
|
pendingPerms = /* @__PURE__ */ new Map();
|
|
216698
217142
|
/**
|
|
@@ -217568,6 +218012,8 @@ var Daemon = class Daemon {
|
|
|
217568
218012
|
this.cancelTimers.clear();
|
|
217569
218013
|
for (const t of this.slackRetryTimers.values()) this.clock.clearTimeout(t);
|
|
217570
218014
|
this.slackRetryTimers.clear();
|
|
218015
|
+
for (const t of this.orchestrationDeadlines.values()) this.clock.clearTimeout(t);
|
|
218016
|
+
this.orchestrationDeadlines.clear();
|
|
217571
218017
|
this.metrics?.stop();
|
|
217572
218018
|
await this.watcher?.close();
|
|
217573
218019
|
await this.drainForShutdown();
|
|
@@ -217618,4 +218064,4 @@ async function runForeground(opts, deps = {}) {
|
|
|
217618
218064
|
//#endregion
|
|
217619
218065
|
export { ClientTransport as i, CP_SUBPROTOCOL as n, CP_WS_PATH as r, runForeground as t };
|
|
217620
218066
|
|
|
217621
|
-
//# sourceMappingURL=run-foreground-
|
|
218067
|
+
//# sourceMappingURL=run-foreground-D_sn_AMV.js.map
|