@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.
@@ -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-C-4zFhF8.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-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 hopCount = 0;
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
- ...req.correlationId !== void 0 ? { correlationId: req.correlationId } : {},
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-Cennaxiw.js.map
218067
+ //# sourceMappingURL=run-foreground-D_sn_AMV.js.map