@adhdev/daemon-standalone 1.0.18-rc.2 → 1.0.18-rc.20

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/public/index.html CHANGED
@@ -7,9 +7,9 @@
7
7
  <meta name="description" content="ADHDev self-hosted dashboard for controlling AI agents" />
8
8
  <link rel="icon" href="/otter-logo.png" />
9
9
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet" />
10
- <script type="module" crossorigin src="/assets/index-D8z4TEna.js"></script>
10
+ <script type="module" crossorigin src="/assets/index-BeWlq-WP.js"></script>
11
11
  <link rel="modulepreload" crossorigin href="/assets/vendor-DyCWA2YZ.js">
12
- <link rel="stylesheet" crossorigin href="/assets/index-DU4BBoUD.css">
12
+ <link rel="stylesheet" crossorigin href="/assets/index-CXZe3Zxn.css">
13
13
  </head>
14
14
  <body>
15
15
  <!-- Apply theme immediately to prevent FOIT (Flash of Incorrect Theme) -->
@@ -391,6 +391,7 @@ var CANONICAL_MESH_TOOL_NAMES = [
391
391
  "mesh_restart_daemon",
392
392
  "mesh_checkpoint",
393
393
  "mesh_approve",
394
+ "mesh_answer_question",
394
395
  "mesh_list_pending_approvals",
395
396
  "mesh_clone_node",
396
397
  "mesh_remove_node",
@@ -1075,6 +1076,38 @@ var MESH_APPROVE_TOOL = {
1075
1076
  required: ["node_id", "session_id", "action"]
1076
1077
  }
1077
1078
  };
1079
+ var MESH_ANSWER_QUESTION_TOOL = {
1080
+ name: "mesh_answer_question",
1081
+ description: 'Answer a multi-choice QUESTION (AskUserQuestion) a delegated agent session is waiting on. This is the counterpart to mesh_approve: a QUESTION (surfaced as an agent:waiting_choice event / status "awaiting_choice") is NOT a yes/no approval \u2014 it offers labelled options (optionally multi-select, optionally a freeform "Type something") and must be answered here, never with mesh_approve. Supply the promptId from the waiting_choice event and one answer per question. Each answer selects option(s) by their exact label OR 1-based index; for a multi-select question pass an array of selections; a freeform answer passes text instead. The daemon drives the correct keystrokes into the provider TUI to submit the selection.',
1082
+ inputSchema: {
1083
+ type: "object",
1084
+ properties: {
1085
+ node_id: { type: "string", description: "Target node ID (from the waiting_choice event / mesh_list_nodes)." },
1086
+ session_id: { type: "string", description: "Agent session ID that is awaiting the question answer." },
1087
+ promptId: { type: "string", description: "The InteractivePrompt promptId from the agent:waiting_choice event. Ensures the answer matches the active prompt." },
1088
+ answers: {
1089
+ type: "array",
1090
+ description: "One entry per question in the prompt (in question order). Each entry answers a single question by selecting option label(s)/index(es), or by supplying freeform text.",
1091
+ items: {
1092
+ type: "object",
1093
+ properties: {
1094
+ questionId: { type: "string", description: "Optional question id from the prompt payload. When omitted, entries are matched to the prompt questions by array position." },
1095
+ select: {
1096
+ description: "The chosen option(s): an option label (string), a 1-based option index (number), or an array of labels/indices for a multi-select question.",
1097
+ oneOf: [
1098
+ { type: "string" },
1099
+ { type: "number" },
1100
+ { type: "array", items: { type: ["string", "number"] } }
1101
+ ]
1102
+ },
1103
+ freeform: { type: "string", description: 'Freeform text answer (for a "Type something" option). Mutually exclusive with select.' }
1104
+ }
1105
+ }
1106
+ }
1107
+ },
1108
+ required: ["node_id", "session_id", "promptId", "answers"]
1109
+ }
1110
+ };
1078
1111
  var MESH_LIST_PENDING_APPROVALS_TOOL = {
1079
1112
  name: "mesh_list_pending_approvals",
1080
1113
  description: "List every session across the mesh that is currently awaiting an approval decision (status awaiting_approval) \u2014 the mesh-wide approval inbox. mesh_approve resolves ONE (node_id, session_id) at a time; this read-only tool enumerates the full pending set so you can see all blocked sessions at once and drive a mesh_approve for each. Each row carries nodeId, sessionId, providerType, taskTitle, and how long it has been waiting (waitingSince/waitingMs), longest-waiting first. Does not mutate anything.",
@@ -1171,7 +1204,23 @@ var MESH_RECORD_NOTE_TOOL = {
1171
1204
  category: {
1172
1205
  type: "string",
1173
1206
  enum: ["provider_quirk", "pattern_to_avoid", "recovery_lesson"],
1174
- description: "Optional classification: provider_quirk (a provider/runtime behaves unexpectedly), pattern_to_avoid (an approach that caused problems), recovery_lesson (how a failure was recovered)."
1207
+ description: "Optional classification: provider_quirk (a provider/runtime behaves unexpectedly), pattern_to_avoid (an approach that caused problems), recovery_lesson (how a failure was recovered). Category also governs default read-side retention: recovery_lesson ages out of the injected prompt after ~14 days, pattern_to_avoid after ~30, provider_quirk and uncategorized are durable (never age out). The ledger entry is always kept for audit regardless."
1208
+ },
1209
+ pinned: {
1210
+ type: "boolean",
1211
+ description: "Pin this note so it ALWAYS rides into every coordinator prompt: never dropped by TTL expiry and kept ahead of unpinned notes when the injection cap is hit. Use for durable, high-value operating knowledge you never want to lose from the prompt."
1212
+ },
1213
+ ttl_days: {
1214
+ type: "number",
1215
+ description: "Optional explicit read-side lifespan in days. Resolved to an absolute expiry at record time; after it passes an UNPINNED note is hidden from the injected prompt (but retained in the ledger for audit). Overrides the category default TTL. Ignored when pinned is true."
1216
+ },
1217
+ supersedes: {
1218
+ type: "string",
1219
+ description: "Optional version-supersede: the note_id of an earlier note this one replaces, OR a subject_key shared with earlier notes. At injection any earlier LIVE note matching this id/subject is hidden from the prompt (its ledger entry is retained for audit). Use when you record an updated lesson that makes a prior one obsolete. Pinned notes are never hidden by supersede."
1220
+ },
1221
+ subject_key: {
1222
+ type: "string",
1223
+ description: "Optional stable subject key grouping notes about the same subject. Drives version-supersede targeting and read-side same-class folding (multiple live notes with the same category AND subject_key collapse to one injected entry, newest kept, older ids listed). When omitted, folding falls back to a leading [tag] bracket in the text."
1175
1224
  }
1176
1225
  },
1177
1226
  required: ["text"]
@@ -1492,6 +1541,7 @@ var ALL_MESH_TOOLS = [
1492
1541
  MESH_RESTART_DAEMON_TOOL,
1493
1542
  MESH_CHECKPOINT_TOOL,
1494
1543
  MESH_APPROVE_TOOL,
1544
+ MESH_ANSWER_QUESTION_TOOL,
1495
1545
  MESH_LIST_PENDING_APPROVALS_TOOL,
1496
1546
  MESH_CLONE_NODE_TOOL,
1497
1547
  MESH_REMOVE_NODE_TOOL,
@@ -4353,6 +4403,15 @@ async function meshRecordNote(ctx, args) {
4353
4403
  }
4354
4404
  const category = args.category === "provider_quirk" || args.category === "pattern_to_avoid" || args.category === "recovery_lesson" ? args.category : void 0;
4355
4405
  const createdAt = (/* @__PURE__ */ new Date()).toISOString();
4406
+ const pinned = args.pinned === true ? true : void 0;
4407
+ let expiresAt;
4408
+ if (typeof args.expiresAt === "string" && !Number.isNaN(new Date(args.expiresAt).getTime())) {
4409
+ expiresAt = new Date(args.expiresAt).toISOString();
4410
+ } else if (typeof args.ttl_days === "number" && Number.isFinite(args.ttl_days) && args.ttl_days > 0) {
4411
+ expiresAt = new Date(new Date(createdAt).getTime() + args.ttl_days * 24 * 60 * 60 * 1e3).toISOString();
4412
+ }
4413
+ const supersedes = typeof args.supersedes === "string" && args.supersedes.trim() ? args.supersedes.trim() : void 0;
4414
+ const subjectKey = typeof args.subject_key === "string" && args.subject_key.trim() ? args.subject_key.trim() : void 0;
4356
4415
  const sourceCoordinator = ctx.coordinatorSessionId || ctx.localDaemonId || ctx.coordinatorHostname || void 0;
4357
4416
  const entry = (0, import_daemon_core4.appendLedgerEntry)(mesh.id, {
4358
4417
  kind: "coordinator_operating_note",
@@ -4361,14 +4420,26 @@ async function meshRecordNote(ctx, args) {
4361
4420
  text,
4362
4421
  ...category ? { category } : {},
4363
4422
  createdAt,
4364
- ...sourceCoordinator ? { sourceCoordinator } : {}
4423
+ ...sourceCoordinator ? { sourceCoordinator } : {},
4424
+ ...pinned ? { pinned } : {},
4425
+ ...expiresAt ? { expiresAt } : {},
4426
+ ...supersedes ? { supersedes } : {},
4427
+ ...subjectKey ? { subjectKey } : {}
4365
4428
  }
4366
4429
  });
4367
4430
  return JSON.stringify({
4368
4431
  success: true,
4369
4432
  meshId: mesh.id,
4370
4433
  noteId: entry.id,
4371
- recorded: { text, category: category ?? null, createdAt },
4434
+ recorded: {
4435
+ text,
4436
+ category: category ?? null,
4437
+ createdAt,
4438
+ pinned: pinned ?? false,
4439
+ expiresAt: expiresAt ?? null,
4440
+ supersedes: supersedes ?? null,
4441
+ subjectKey: subjectKey ?? null
4442
+ },
4372
4443
  note: 'Recorded to the mesh ledger. Future coordinators on this mesh will see it under "## Operating Notes" at launch.'
4373
4444
  }, null, 2);
4374
4445
  }
@@ -6922,6 +6993,19 @@ async function meshLaunchSession(ctx, args) {
6922
6993
  const coordinatorDaemonId = resolveCoordinatorDaemonId(ctx);
6923
6994
  const spawnedSessionVisibility = readSpawnedSessionVisibility(ctx.mesh.policy);
6924
6995
  const delegatedWorkerAutoApprove = (0, import_daemon_core4.resolveDelegatedWorkerAutoApprove)(ctx.mesh.policy, node.policy);
6996
+ let requestedAutoApproveMode;
6997
+ const delegatedWorkerDangerousModeAllow = (0, import_daemon_core4.resolveDelegatedWorkerDangerousModeAllow)(ctx.mesh.policy, node.policy);
6998
+ if (delegatedWorkerAutoApprove !== false) {
6999
+ try {
7000
+ const ws = typeof node.workspace === "string" && node.workspace.trim() ? node.workspace.trim() : "";
7001
+ if (ws) {
7002
+ const repo = (0, import_daemon_core4.loadRepoMeshJsonConfig)(ws);
7003
+ const repoMode = repo.sourceType === "repo_file" ? repo.config?.providerDefaults?.autoApproveModes?.[resolvedProviderType] : void 0;
7004
+ if (typeof repoMode === "string" && repoMode.trim()) requestedAutoApproveMode = repoMode.trim();
7005
+ }
7006
+ } catch {
7007
+ }
7008
+ }
6925
7009
  const isLocalNode = isLocalControlPlaneNode(ctx, node);
6926
7010
  if (node.daemonId && !isLocalNode && !coordinatorDaemonId) {
6927
7011
  return JSON.stringify(buildMissingCoordinatorDaemonIdFailure(ctx, node, resolvedProviderType), null, 2);
@@ -6977,7 +7061,14 @@ async function meshLaunchSession(ctx, args) {
6977
7061
  spawnedSessionVisibility,
6978
7062
  // Delegated worker auto-approval (see resolveDelegatedWorkerAutoApprove).
6979
7063
  // Lands in settingsOverride and beats the global per-provider autoApprove.
7064
+ // When a repo-requested mode ID is present AND auto-approve is enabled,
7065
+ // stamp the mode ID (validated daemon-side) alongside the boolean; the
7066
+ // adapter's resolveProviderAutoApproveMode prefers the mode and fails
7067
+ // closed on an unknown ID. delegatedWorkerDangerousModeAllow rides along
7068
+ // so the adapter can honor/deny a dangerous requested mode.
6980
7069
  autoApprove: delegatedWorkerAutoApprove,
7070
+ ...requestedAutoApproveMode ? { autoApproveMode: requestedAutoApproveMode } : {},
7071
+ delegatedWorkerDangerousModeAllow,
6981
7072
  ...coordinatorDaemonId ? { meshCoordinatorDaemonId: coordinatorDaemonId } : {},
6982
7073
  // (3) Stamp the originating coordinator SESSION at launch too, so a worker
6983
7074
  // launched via mesh_launch_session routes its completions back to the exact
@@ -7038,6 +7129,25 @@ async function meshApprove(ctx, args) {
7038
7129
  });
7039
7130
  return JSON.stringify(result, null, 2);
7040
7131
  }
7132
+ async function meshAnswerQuestion(ctx, args) {
7133
+ const node = await findNodeWithRefresh(ctx, args.node_id);
7134
+ if (!args.promptId || typeof args.promptId !== "string") {
7135
+ return JSON.stringify({ success: false, error: "promptId is required (from the agent:waiting_choice event)." }, null, 2);
7136
+ }
7137
+ if (!Array.isArray(args.answers)) {
7138
+ return JSON.stringify({ success: false, error: "answers must be an array (one entry per question)." }, null, 2);
7139
+ }
7140
+ const result = await commandForNode(ctx, node, "interactive_prompt_response", {
7141
+ targetSessionId: args.session_id,
7142
+ sessionId: args.session_id,
7143
+ workspace: node.workspace,
7144
+ response: {
7145
+ promptId: args.promptId,
7146
+ answers: args.answers
7147
+ }
7148
+ });
7149
+ return JSON.stringify(result, null, 2);
7150
+ }
7041
7151
  async function meshListPendingApprovals(ctx, _args = {}) {
7042
7152
  (0, import_daemon_core4.recordMeshToolCall)({ meshId: ctx.mesh.id, tool: "mesh_list_pending_approvals" });
7043
7153
  await refreshMeshFromDaemon(ctx);
@@ -8575,7 +8685,7 @@ async function startMcpServer(opts) {
8575
8685
  const meshCtx = { mesh, transport, ...localDaemonId ? { localDaemonId } : {}, ...localMachineId ? { localMachineId } : {}, ...coordinatorHostname ? { coordinatorHostname } : {}, ...coordinatorSessionId ? { coordinatorSessionId } : {} };
8576
8686
  const coordinatorPrompt = await buildMeshModeCoordinatorPrompt(mesh);
8577
8687
  const server2 = new import_server.Server(
8578
- { name: "adhdev-mcp-server", version: "1.0.17" },
8688
+ { name: "adhdev-mcp-server", version: "1.0.20" },
8579
8689
  { capabilities: { tools: {}, resources: {} } }
8580
8690
  );
8581
8691
  const { ListResourcesRequestSchema, ReadResourceRequestSchema } = await import("@modelcontextprotocol/sdk/types.js");
@@ -8654,6 +8764,9 @@ async function startMcpServer(opts) {
8654
8764
  case "mesh_approve":
8655
8765
  text = await meshApprove(meshCtx, a);
8656
8766
  break;
8767
+ case "mesh_answer_question":
8768
+ text = await meshAnswerQuestion(meshCtx, a);
8769
+ break;
8657
8770
  case "mesh_list_pending_approvals":
8658
8771
  text = await meshListPendingApprovals(meshCtx, a);
8659
8772
  break;