@drisp/cli 0.5.24 → 0.5.26

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.
@@ -12,30 +12,33 @@ import {
12
12
  isToolEvent,
13
13
  isValidHookEventEnvelope,
14
14
  resolveHookSocketPath
15
- } from "./chunk-BTKQ67RE.js";
15
+ } from "./chunk-QYB6N2OT.js";
16
16
  import {
17
17
  TransportUnreachableError,
18
18
  createUdsClientTransport,
19
+ createWsClientTransport,
19
20
  generateChannelRequestId,
20
21
  openVersionedDb,
21
22
  readDashboardClientConfig,
22
23
  refreshDashboardAccessToken,
23
24
  resolveGatewayPaths,
24
- traceGatewayFrame,
25
25
  trackGatewayTransportReconnect,
26
- writeGatewayTrace
27
- } from "./chunk-YU2WMPRC.js";
26
+ writeGatewayTrace,
27
+ wsClientOptionsForEndpoint
28
+ } from "./chunk-3U37HT4T.js";
28
29
  import {
29
30
  compileWorkflowPlan,
30
31
  createWorkflowRunner,
31
32
  installWorkflowFromSource,
33
+ isMarketplaceRef,
32
34
  readConfig,
33
35
  readGlobalConfig,
34
36
  resolveActiveWorkflow,
37
+ resolveMarketplacePlugin,
35
38
  resolveWorkflow,
36
39
  resolveWorkflowInstall,
37
40
  resolveWorkflowPlugins
38
- } from "./chunk-7UUPLAP4.js";
41
+ } from "./chunk-73V7GXV6.js";
39
42
 
40
43
  // src/infra/daemon/stateDir.ts
41
44
  import fs from "fs";
@@ -238,6 +241,16 @@ var RULES = {
238
241
  defaultTimeoutMs: DEFAULT_TIMEOUT_MS,
239
242
  canBlock: false
240
243
  },
244
+ // Observation-only: the batch fires after every call in it has already
245
+ // resolved, so nothing downstream produces a decision or block for it. The
246
+ // claim is not cosmetic — hook dispatch keeps every decision-capable event
247
+ // on Claude's critical path, so a false `canBlock` would make every
248
+ // parallel tool batch wait on the forwarder for a reply never sent.
249
+ "tool.batch": {
250
+ expectsDecision: false,
251
+ defaultTimeoutMs: DEFAULT_TIMEOUT_MS,
252
+ canBlock: false
253
+ },
241
254
  "tool.delta": {
242
255
  expectsDecision: false,
243
256
  defaultTimeoutMs: DEFAULT_TIMEOUT_MS,
@@ -288,6 +301,16 @@ var RULES = {
288
301
  defaultTimeoutMs: DEFAULT_TIMEOUT_MS,
289
302
  canBlock: true
290
303
  },
304
+ // Observation-only: nothing in the pipeline produces a decision or a block
305
+ // for an expansion, so it must not claim `canBlock`. The claim is not
306
+ // cosmetic — hook dispatch keeps every decision-capable event on Claude's
307
+ // critical path, so a false `canBlock` would make every slash command wait
308
+ // on the forwarder for a reply that is never sent.
309
+ "prompt.expansion": {
310
+ expectsDecision: false,
311
+ defaultTimeoutMs: DEFAULT_TIMEOUT_MS,
312
+ canBlock: false
313
+ },
291
314
  "turn.start": {
292
315
  expectsDecision: false,
293
316
  defaultTimeoutMs: DEFAULT_TIMEOUT_MS,
@@ -420,6 +443,15 @@ function asRecord(value) {
420
443
  }
421
444
  return {};
422
445
  }
446
+ function readToolBatchCalls(value) {
447
+ if (!Array.isArray(value)) return [];
448
+ return value.filter((entry) => typeof entry === "object" && entry !== null).map((entry) => asRecord(entry)).map((call) => ({
449
+ tool_name: call["tool_name"],
450
+ tool_input: call["tool_input"],
451
+ tool_use_id: call["tool_use_id"],
452
+ tool_response: call["tool_response"]
453
+ }));
454
+ }
423
455
  function translateClaudeEnvelope(envelope) {
424
456
  const payload = asRecord(envelope.payload);
425
457
  let toolName;
@@ -444,7 +476,9 @@ function translateClaudeEnvelope(envelope) {
444
476
  kind: "session.start",
445
477
  data: {
446
478
  source: payload["source"],
447
- agent_type: payload["agent_type"]
479
+ model: payload["model"],
480
+ agent_type: payload["agent_type"],
481
+ session_title: payload["session_title"]
448
482
  }
449
483
  };
450
484
  case "SessionEnd":
@@ -458,6 +492,22 @@ function translateClaudeEnvelope(envelope) {
458
492
  return {
459
493
  kind: "user.prompt",
460
494
  data: {
495
+ prompt: payload["prompt"],
496
+ cwd: payload["cwd"],
497
+ permission_mode: payload["permission_mode"]
498
+ }
499
+ };
500
+ // Fires immediately before UserPromptSubmit, sharing its prompt_id,
501
+ // when a typed command expands into a prompt. Field names are pinned
502
+ // to the payload captured in the #116 spike.
503
+ case "UserPromptExpansion":
504
+ return {
505
+ kind: "prompt.expansion",
506
+ data: {
507
+ expansion_type: payload["expansion_type"],
508
+ command_name: payload["command_name"],
509
+ command_args: payload["command_args"],
510
+ command_source: payload["command_source"],
461
511
  prompt: payload["prompt"],
462
512
  permission_mode: payload["permission_mode"]
463
513
  }
@@ -485,6 +535,18 @@ function translateClaudeEnvelope(envelope) {
485
535
  tool_response: payload["tool_response"]
486
536
  }
487
537
  };
538
+ // Fires once per assistant tool batch, after every PostToolUse in it.
539
+ // Field names are pinned to the payload captured in the #116 spike;
540
+ // note that `tool_calls[].tool_response` is a STRING there, unlike
541
+ // PostToolUse's structured `tool_response`.
542
+ case "PostToolBatch":
543
+ return {
544
+ kind: "tool.batch",
545
+ data: {
546
+ tool_calls: readToolBatchCalls(payload["tool_calls"]),
547
+ permission_mode: payload["permission_mode"]
548
+ }
549
+ };
488
550
  case "PostToolUseFailure":
489
551
  return {
490
552
  kind: "tool.failure",
@@ -495,7 +557,10 @@ function translateClaudeEnvelope(envelope) {
495
557
  tool_input: payload["tool_input"] ?? {},
496
558
  tool_use_id: toolUseId,
497
559
  error: payload["error"],
498
- is_interrupt: payload["is_interrupt"]
560
+ is_interrupt: payload["is_interrupt"],
561
+ exit_code: payload["exit_code"],
562
+ output: payload["output"],
563
+ error_code: payload["error_code"]
499
564
  }
500
565
  };
501
566
  case "PermissionRequest":
@@ -721,6 +786,12 @@ function buildClaudeDisplay(kind, data) {
721
786
  }
722
787
 
723
788
  // src/harnesses/claude/runtime/mapper.ts
789
+ function readEffortLevel(payload) {
790
+ const effort = payload["effort"];
791
+ if (typeof effort !== "object" || effort === null) return void 0;
792
+ const level = effort["level"];
793
+ return typeof level === "string" ? level : void 0;
794
+ }
724
795
  function mapEnvelopeToRuntimeEvent(envelope) {
725
796
  const payload = envelope.payload;
726
797
  const safePayload = typeof payload === "object" && payload !== null ? payload : { value: payload };
@@ -738,6 +809,13 @@ function mapEnvelopeToRuntimeEvent(envelope) {
738
809
  data: translated.data,
739
810
  hookName: envelope.hook_event_name,
740
811
  sessionId: envelope.session_id,
812
+ // prompt_id is a Claude common input field (v2.1.196+) on every hook
813
+ // payload; carried harness-neutrally as the Prompt identity (ADR 0009).
814
+ promptId: safePayloadRecord["prompt_id"],
815
+ // `effort` is likewise a common input field ({level}), observed on
816
+ // PostToolUse-class payloads and never on SessionStart, so it is read here
817
+ // rather than in any one hook's translation.
818
+ effortLevel: readEffortLevel(safePayloadRecord),
741
819
  toolName: translated.toolName,
742
820
  toolUseId: translated.toolUseId,
743
821
  agentId: translated.agentId,
@@ -855,9 +933,23 @@ function extractResultText(content) {
855
933
  }
856
934
  return parts.join("\n");
857
935
  }
858
- function createStreamJsonToolParser(onToolResult, onToolUse) {
936
+ function extractAssistantText(content) {
937
+ if (!Array.isArray(content)) return "";
938
+ const parts = [];
939
+ for (const block of content) {
940
+ if (typeof block === "object" && block !== null) {
941
+ const rec = block;
942
+ if (rec["type"] === "text" && typeof rec["text"] === "string") {
943
+ parts.push(rec["text"]);
944
+ }
945
+ }
946
+ }
947
+ return parts.join("");
948
+ }
949
+ function createStreamJsonToolParser(onToolResult, onToolUse, onMessageDelta, onMessageComplete) {
859
950
  let buffer = "";
860
951
  const toolNameById = /* @__PURE__ */ new Map();
952
+ let currentMessageId;
861
953
  function processLine(line) {
862
954
  const trimmed = line.trim();
863
955
  if (!trimmed) return;
@@ -868,6 +960,19 @@ function createStreamJsonToolParser(onToolResult, onToolUse) {
868
960
  return;
869
961
  }
870
962
  const record = parsed.type === "stream_event" && parsed.event ? parsed.event : parsed;
963
+ if (record.type === "message_start") {
964
+ const id = record.message?.["id"];
965
+ if (typeof id === "string") currentMessageId = id;
966
+ }
967
+ if (record.type === "content_block_delta") {
968
+ const delta = record["delta"];
969
+ if (typeof delta === "object" && delta !== null) {
970
+ const d = delta;
971
+ if (d["type"] === "text_delta" && typeof d["text"] === "string") {
972
+ onMessageDelta?.({ item_id: currentMessageId, delta: d["text"] });
973
+ }
974
+ }
975
+ }
871
976
  const contentBlocks = (record.type === "assistant" ? record.message?.content : record.type === "message" && record.role === "assistant" ? record.content : void 0) ?? [];
872
977
  for (const block of contentBlocks) {
873
978
  if (typeof block !== "object" || block === null) continue;
@@ -877,6 +982,14 @@ function createStreamJsonToolParser(onToolResult, onToolUse) {
877
982
  onToolUse?.({ tool_use_id: rec["id"], tool_name: rec["name"] });
878
983
  }
879
984
  }
985
+ if (record.type === "assistant" || record.type === "message" && record.role === "assistant") {
986
+ const messageText = extractAssistantText(contentBlocks);
987
+ if (messageText) {
988
+ const idValue = record.type === "assistant" ? record.message?.["id"] : record["id"];
989
+ const itemId = typeof idValue === "string" ? idValue : currentMessageId;
990
+ onMessageComplete?.({ item_id: itemId, message: messageText });
991
+ }
992
+ }
880
993
  if (record.type === "tool_result") {
881
994
  const toolUseId = typeof record.tool_use_id === "string" ? record.tool_use_id : void 0;
882
995
  const text = extractResultText(record.content);
@@ -975,6 +1088,38 @@ function createServer2(opts) {
975
1088
  },
976
1089
  () => {
977
1090
  transportStats.streamToolUses++;
1091
+ },
1092
+ (messageDelta) => {
1093
+ emit({
1094
+ id: `stream-msg-delta-${messageDelta.item_id ?? "unknown"}-${Date.now()}`,
1095
+ timestamp: Date.now(),
1096
+ kind: "message.delta",
1097
+ data: {
1098
+ item_id: messageDelta.item_id,
1099
+ delta: messageDelta.delta
1100
+ },
1101
+ hookName: "stream-json",
1102
+ sessionId,
1103
+ context: { cwd: projectDir, transcriptPath: "" },
1104
+ interaction: getInteractionHints("message.delta"),
1105
+ payload: null
1106
+ });
1107
+ },
1108
+ (messageComplete) => {
1109
+ emit({
1110
+ id: `stream-msg-complete-${messageComplete.item_id ?? "unknown"}-${Date.now()}`,
1111
+ timestamp: Date.now(),
1112
+ kind: "message.complete",
1113
+ data: {
1114
+ item_id: messageComplete.item_id,
1115
+ message: messageComplete.message
1116
+ },
1117
+ hookName: "stream-json",
1118
+ sessionId,
1119
+ context: { cwd: projectDir, transcriptPath: "" },
1120
+ interaction: getInteractionHints("message.complete"),
1121
+ payload: null
1122
+ });
978
1123
  }
979
1124
  );
980
1125
  function emit(event) {
@@ -1375,6 +1520,10 @@ var TOOL_HOOK_EVENTS = [
1375
1520
  "PreToolUse",
1376
1521
  "PostToolUse",
1377
1522
  "PostToolUseFailure",
1523
+ // Batch-level, not per-tool, but Claude accepts (and requires) a matcher
1524
+ // here: the #116 capture registered it with `matcher: '*'` and it fired in
1525
+ // every run. Matcher-less registration is unverified.
1526
+ "PostToolBatch",
1378
1527
  "PermissionRequest",
1379
1528
  "PermissionDenied",
1380
1529
  "Elicitation",
@@ -1389,6 +1538,7 @@ var NON_TOOL_HOOK_EVENTS = [
1389
1538
  "SubagentStart",
1390
1539
  "SubagentStop",
1391
1540
  "UserPromptSubmit",
1541
+ "UserPromptExpansion",
1392
1542
  "PreCompact",
1393
1543
  "PostCompact",
1394
1544
  "Setup",
@@ -1401,6 +1551,27 @@ var NON_TOOL_HOOK_EVENTS = [
1401
1551
  "WorktreeCreate",
1402
1552
  "WorktreeRemove"
1403
1553
  ];
1554
+ var SYNC_HOOK_EVENTS = [
1555
+ // Decision/blocking — kept in step with `runtime/interactionRules.ts`.
1556
+ "PreToolUse",
1557
+ "PermissionRequest",
1558
+ "UserPromptSubmit",
1559
+ "Stop",
1560
+ "StopFailure",
1561
+ "SubagentStop",
1562
+ "Elicitation",
1563
+ "ElicitationResult",
1564
+ "TeammateIdle",
1565
+ "TaskCreated",
1566
+ "TaskCompleted",
1567
+ "ConfigChange",
1568
+ // Teardown — see (2) above.
1569
+ "SessionEnd"
1570
+ ];
1571
+ var SYNC_HOOK_EVENT_SET = new Set(SYNC_HOOK_EVENTS);
1572
+ function isAsyncHookEvent(event) {
1573
+ return !SYNC_HOOK_EVENT_SET.has(event);
1574
+ }
1404
1575
  function quoteShellArg(value) {
1405
1576
  return `'${value.replace(/'/g, `'"'"'`)}'`;
1406
1577
  }
@@ -1448,23 +1619,20 @@ function generateHookSettings(tempDir, authOverlay) {
1448
1619
  if (process.env["ATHENA_DEBUG"]) {
1449
1620
  console.error("[athena-debug] Hook forwarder path:", hookForwarder.command);
1450
1621
  }
1451
- const hookCommand = {
1452
- type: "command",
1453
- command: hookForwarder.command
1454
- };
1622
+ const hookCommandFor = (event) => isAsyncHookEvent(event) ? { type: "command", command: hookForwarder.command, async: true } : { type: "command", command: hookForwarder.command };
1455
1623
  const hooks = {};
1456
1624
  for (const event of TOOL_HOOK_EVENTS) {
1457
1625
  hooks[event] = [
1458
1626
  {
1459
1627
  matcher: "*",
1460
- hooks: [hookCommand]
1628
+ hooks: [hookCommandFor(event)]
1461
1629
  }
1462
1630
  ];
1463
1631
  }
1464
1632
  for (const event of NON_TOOL_HOOK_EVENTS) {
1465
1633
  hooks[event] = [
1466
1634
  {
1467
- hooks: [hookCommand]
1635
+ hooks: [hookCommandFor(event)]
1468
1636
  }
1469
1637
  ];
1470
1638
  }
@@ -2824,6 +2992,17 @@ import os8 from "os";
2824
2992
  import path10 from "path";
2825
2993
 
2826
2994
  // src/harnesses/claude/config/isolation.ts
2995
+ var CLAUDE_EFFORT_LEVELS = [
2996
+ "low",
2997
+ "medium",
2998
+ "high",
2999
+ "xhigh",
3000
+ "max"
3001
+ ];
3002
+ function normalizeEffort(value) {
3003
+ if (value === void 0) return void 0;
3004
+ return CLAUDE_EFFORT_LEVELS.includes(value) ? value : void 0;
3005
+ }
2827
3006
  var DISALLOWED_FIRST_PARTY_MCPS = [
2828
3007
  "mcp__claude_ai_Gmail__*",
2829
3008
  "mcp__claude_ai_Google_Calendar__*",
@@ -2934,6 +3113,7 @@ var FLAG_REGISTRY = [
2934
3113
  { field: "additionalDirectories", flag: "--add-dir", kind: "array" },
2935
3114
  // === Model & Agent ===
2936
3115
  { field: "model", flag: "--model", kind: "value" },
3116
+ { field: "effort", flag: "--effort", kind: "value" },
2937
3117
  { field: "fallbackModel", flag: "--fallback-model", kind: "value" },
2938
3118
  { field: "agent", flag: "--agent", kind: "value" },
2939
3119
  { field: "agents", flag: "--agents", kind: "json" },
@@ -3831,20 +4011,49 @@ function buildClaudeCompatibleIsolationConfig({
3831
4011
  additionalDirectories,
3832
4012
  pluginDirs,
3833
4013
  verbose,
3834
- configuredModel
4014
+ configuredModel,
4015
+ configuredEffort
3835
4016
  }) {
3836
4017
  return {
3837
4018
  preset: isolationPreset,
3838
4019
  additionalDirectories,
3839
4020
  pluginDirs: pluginDirs.length > 0 ? pluginDirs : void 0,
3840
4021
  debug: verbose,
3841
- model: configuredModel
4022
+ model: configuredModel,
4023
+ effort: normalizeEffort(configuredEffort)
3842
4024
  };
3843
4025
  }
4026
+ var CLAUDE_MODEL_OPTIONS = [
4027
+ {
4028
+ value: "sonnet",
4029
+ label: "Sonnet",
4030
+ description: "Balanced default for day-to-day coding work."
4031
+ },
4032
+ {
4033
+ value: "opus",
4034
+ label: "Opus",
4035
+ description: "Stronger reasoning for complex architecture and debugging."
4036
+ },
4037
+ {
4038
+ value: "haiku",
4039
+ label: "Haiku",
4040
+ description: "Fastest option for lighter tasks."
4041
+ },
4042
+ {
4043
+ value: "opusplan",
4044
+ label: "OpusPlan",
4045
+ description: "Uses Opus for planning and Sonnet for execution."
4046
+ }
4047
+ ];
3844
4048
  var CLAUDE_CONFIG_PROFILE = {
3845
4049
  harness: "claude-code",
3846
4050
  buildIsolationConfig: buildClaudeCompatibleIsolationConfig,
3847
- resolveModelName: ({ projectDir, configuredModel }) => resolveClaudeModel({ projectDir, configuredModel })
4051
+ resolveModelName: ({ projectDir, configuredModel }) => resolveClaudeModel({ projectDir, configuredModel }),
4052
+ pluginDelivery: {
4053
+ mergeWorkflowPluginDirs: true,
4054
+ registrationBuildsMcpConfig: true,
4055
+ workflowPluginsVia: "registration"
4056
+ }
3848
4057
  };
3849
4058
  var claudeHarnessAdapter = {
3850
4059
  id: "claude-code",
@@ -3854,7 +4063,9 @@ var claudeHarnessAdapter = {
3854
4063
  conversationModel: "fresh_per_turn",
3855
4064
  killWaitsForTurnSettlement: true,
3856
4065
  supportsEphemeralSessions: false,
3857
- supportsConfigurableIsolation: true
4066
+ supportsConfigurableIsolation: true,
4067
+ emitsStartupDiagnostics: true,
4068
+ extraAllowedTools: []
3858
4069
  },
3859
4070
  verify: () => verifyClaudeHarness(),
3860
4071
  createRuntime: (input) => createClaudeHookRuntime({
@@ -3890,7 +4101,8 @@ var claudeHarnessAdapter = {
3890
4101
  };
3891
4102
  return controller;
3892
4103
  },
3893
- resolveConfigProfile: () => CLAUDE_CONFIG_PROFILE
4104
+ resolveConfigProfile: () => CLAUDE_CONFIG_PROFILE,
4105
+ listModels: async () => CLAUDE_MODEL_OPTIONS
3894
4106
  };
3895
4107
 
3896
4108
  // src/harnesses/codex/runtime/appServerManager.ts
@@ -7029,7 +7241,12 @@ var CODEX_CONFIG_PROFILE = {
7029
7241
  preset: isolationPreset,
7030
7242
  model: configuredModel
7031
7243
  }),
7032
- resolveModelName: ({ configuredModel }) => configuredModel ?? null
7244
+ resolveModelName: ({ configuredModel }) => configuredModel ?? null,
7245
+ pluginDelivery: {
7246
+ mergeWorkflowPluginDirs: false,
7247
+ registrationBuildsMcpConfig: false,
7248
+ workflowPluginsVia: "generated-mcp"
7249
+ }
7033
7250
  };
7034
7251
  var codexHarnessAdapter = {
7035
7252
  id: "openai-codex",
@@ -7039,7 +7256,12 @@ var codexHarnessAdapter = {
7039
7256
  conversationModel: "persistent_thread",
7040
7257
  killWaitsForTurnSettlement: true,
7041
7258
  supportsEphemeralSessions: true,
7042
- supportsConfigurableIsolation: true
7259
+ supportsConfigurableIsolation: true,
7260
+ emitsStartupDiagnostics: false,
7261
+ // Codex auto-approves Bash/Edit (outside-sandbox commandExecution /
7262
+ // fileChange) and the scoped 'Permissions' capability, on top of the
7263
+ // shared 'mcp__*' baseline seeded via allowedTools (see buildInitialRules).
7264
+ extraAllowedTools: ["Permissions", "Bash", "Edit"]
7043
7265
  },
7044
7266
  verify: () => verifyCodexHarness(),
7045
7267
  createRuntime: (input) => createCodexRuntime({
@@ -7066,8 +7288,23 @@ var codexHarnessAdapter = {
7066
7288
  };
7067
7289
  return controller;
7068
7290
  },
7069
- resolveConfigProfile: () => CODEX_CONFIG_PROFILE
7291
+ resolveConfigProfile: () => CODEX_CONFIG_PROFILE,
7292
+ listModels: async (runtime) => {
7293
+ if (!isCodexRuntimeWithModels(runtime)) {
7294
+ throw new Error("Codex runtime is not available");
7295
+ }
7296
+ const models = await runtime.listModels();
7297
+ return models.map((model) => ({
7298
+ value: model.model,
7299
+ label: model.displayName,
7300
+ description: model.description,
7301
+ isDefault: model.isDefault
7302
+ }));
7303
+ }
7070
7304
  };
7305
+ function isCodexRuntimeWithModels(runtime) {
7306
+ return Boolean(runtime && "listModels" in runtime);
7307
+ }
7071
7308
 
7072
7309
  // src/harnesses/registry.ts
7073
7310
  function buildClaudeCompatibleIsolationConfig2({
@@ -7093,7 +7330,9 @@ var opencodeHarnessAdapter = {
7093
7330
  conversationModel: "fresh_per_turn",
7094
7331
  killWaitsForTurnSettlement: true,
7095
7332
  supportsEphemeralSessions: false,
7096
- supportsConfigurableIsolation: false
7333
+ supportsConfigurableIsolation: false,
7334
+ emitsStartupDiagnostics: false,
7335
+ extraAllowedTools: []
7097
7336
  },
7098
7337
  createRuntime: (input) => claudeHarnessAdapter.createRuntime(input),
7099
7338
  createSessionController: (input) => claudeHarnessAdapter.createSessionController(input),
@@ -7101,8 +7340,14 @@ var opencodeHarnessAdapter = {
7101
7340
  resolveConfigProfile: () => ({
7102
7341
  harness: "opencode",
7103
7342
  buildIsolationConfig: (input) => buildClaudeCompatibleIsolationConfig2(input),
7104
- resolveModelName: ({ configuredModel }) => configuredModel ?? null
7105
- })
7343
+ resolveModelName: ({ configuredModel }) => configuredModel ?? null,
7344
+ pluginDelivery: {
7345
+ mergeWorkflowPluginDirs: true,
7346
+ registrationBuildsMcpConfig: true,
7347
+ workflowPluginsVia: "registration"
7348
+ }
7349
+ }),
7350
+ listModels: async () => []
7106
7351
  };
7107
7352
  var HARNESS_ADAPTERS = [
7108
7353
  claudeHarnessAdapter,
@@ -7218,6 +7463,28 @@ function loadPlugin(pluginDir) {
7218
7463
  }
7219
7464
  return commands2;
7220
7465
  }
7466
+ function loadPersonalSkills(skills) {
7467
+ const commands2 = [];
7468
+ for (const skill of skills) {
7469
+ const skillPath = path12.join(skill.path, "SKILL.md");
7470
+ if (!fs14.existsSync(skillPath)) {
7471
+ console.warn(
7472
+ `Skipping personal skill '${skill.name}' [${skill.sourceLayer}]: SKILL.md not found at ${skill.path}`
7473
+ );
7474
+ continue;
7475
+ }
7476
+ try {
7477
+ const parsed = parseFrontmatter(fs14.readFileSync(skillPath, "utf-8"));
7478
+ commands2.push(skillToCommand(parsed.frontmatter, parsed.body));
7479
+ } catch (error) {
7480
+ const message = error instanceof Error ? error.message : String(error);
7481
+ console.warn(
7482
+ `Skipping personal skill '${skill.name}' [${skill.sourceLayer}]: ${message}`
7483
+ );
7484
+ }
7485
+ }
7486
+ return commands2;
7487
+ }
7221
7488
  function skillToCommand(frontmatter, body, mcpConfigPath) {
7222
7489
  const args = frontmatter["argument-hint"] ? [
7223
7490
  {
@@ -7241,8 +7508,9 @@ function skillToCommand(frontmatter, body, mcpConfigPath) {
7241
7508
  }
7242
7509
 
7243
7510
  // src/infra/plugins/register.ts
7244
- function buildPluginMcpConfig(pluginDirs, mcpServerOptions) {
7511
+ function buildPluginMcpConfig(pluginDirs, mcpServerOptions, personalMcpServers = []) {
7245
7512
  const mergedServers = {};
7513
+ const conflicts = [];
7246
7514
  for (const dir of pluginDirs) {
7247
7515
  const mcpPath = path13.join(dir, ".mcp.json");
7248
7516
  if (!fs15.existsSync(mcpPath)) {
@@ -7268,22 +7536,99 @@ function buildPluginMcpConfig(pluginDirs, mcpServerOptions) {
7268
7536
  mergedServers[serverName] = rest;
7269
7537
  }
7270
7538
  }
7539
+ for (const personal of personalMcpServers) {
7540
+ const { name, sourceLayer: _sourceLayer, ...server } = personal;
7541
+ if (name in mergedServers) {
7542
+ conflicts.push(personal);
7543
+ continue;
7544
+ }
7545
+ mergedServers[name] = server;
7546
+ }
7271
7547
  if (Object.keys(mergedServers).length === 0) {
7272
- return void 0;
7548
+ return { mcpConfig: void 0, conflicts };
7273
7549
  }
7274
7550
  const mcpConfig = path13.join(os10.tmpdir(), `athena-mcp-${process.pid}.json`);
7275
7551
  fs15.writeFileSync(mcpConfig, JSON.stringify({ mcpServers: mergedServers }));
7276
- return mcpConfig;
7552
+ return { mcpConfig, conflicts };
7277
7553
  }
7278
- function registerPlugins(pluginDirs, mcpServerOptions, includeMcpConfig = true) {
7554
+ function registerPlugins(pluginDirs, mcpServerOptions, includeMcpConfig = true, personalMcpServers = [], personalSkills = []) {
7279
7555
  for (const dir of pluginDirs) {
7280
7556
  const commands2 = loadPlugin(dir);
7281
7557
  for (const command of commands2) {
7282
7558
  register(command);
7283
7559
  }
7284
7560
  }
7285
- const mcpConfig = includeMcpConfig ? buildPluginMcpConfig(pluginDirs, mcpServerOptions) : void 0;
7286
- return { mcpConfig };
7561
+ const skillByName = new Map(personalSkills.map((skill) => [skill.name, skill]));
7562
+ const skillConflicts = [];
7563
+ for (const command of loadPersonalSkills(personalSkills)) {
7564
+ if (get(command.name)) {
7565
+ const shadowed = skillByName.get(command.name);
7566
+ if (shadowed) {
7567
+ skillConflicts.push(shadowed);
7568
+ }
7569
+ continue;
7570
+ }
7571
+ register(command);
7572
+ }
7573
+ const mcpResult = includeMcpConfig ? buildPluginMcpConfig(pluginDirs, mcpServerOptions, personalMcpServers) : { mcpConfig: void 0, conflicts: [] };
7574
+ return {
7575
+ mcpConfig: mcpResult.mcpConfig,
7576
+ conflicts: { mcpServers: mcpResult.conflicts, skills: skillConflicts }
7577
+ };
7578
+ }
7579
+
7580
+ // src/infra/plugins/pluginRefResolution.ts
7581
+ function resolvePluginDirs(entries) {
7582
+ const dirs = [];
7583
+ const warnings = [];
7584
+ for (const entry of entries) {
7585
+ if (!isMarketplaceRef(entry)) {
7586
+ dirs.push(entry);
7587
+ continue;
7588
+ }
7589
+ try {
7590
+ dirs.push(resolveMarketplacePlugin(entry));
7591
+ } catch (error) {
7592
+ warnings.push(`Skipping plugin "${entry}": ${error.message}`);
7593
+ }
7594
+ }
7595
+ return { dirs, warnings };
7596
+ }
7597
+
7598
+ // src/infra/capabilities/effective.ts
7599
+ function resolveEffectiveCapabilities(input) {
7600
+ const { globalConfig, projectConfig } = input;
7601
+ return {
7602
+ mcpServers: mergeMcpServers(
7603
+ globalConfig.mcpServers,
7604
+ projectConfig.mcpServers
7605
+ ),
7606
+ skills: mergeSkills(globalConfig.skills, projectConfig.skills)
7607
+ };
7608
+ }
7609
+ function mergeMcpServers(global, project) {
7610
+ const projectNames = new Set(Object.keys(project ?? {}));
7611
+ const result = [];
7612
+ for (const [name, server] of Object.entries(global ?? {})) {
7613
+ if (projectNames.has(name)) continue;
7614
+ result.push({ name, ...server, sourceLayer: "global" });
7615
+ }
7616
+ for (const [name, server] of Object.entries(project ?? {})) {
7617
+ result.push({ name, ...server, sourceLayer: "project" });
7618
+ }
7619
+ return result;
7620
+ }
7621
+ function mergeSkills(global, project) {
7622
+ const projectNames = new Set((project ?? []).map((s) => s.name));
7623
+ const result = [];
7624
+ for (const skill of global ?? []) {
7625
+ if (projectNames.has(skill.name)) continue;
7626
+ result.push({ ...skill, sourceLayer: "global" });
7627
+ }
7628
+ for (const skill of project ?? []) {
7629
+ result.push({ ...skill, sourceLayer: "project" });
7630
+ }
7631
+ return result;
7287
7632
  }
7288
7633
 
7289
7634
  // src/setup/shouldResolveWorkflow.ts
@@ -7339,6 +7684,9 @@ function bootstrapRuntimeConfig({
7339
7684
  const projectConfig = providedProjectConfig ?? readConfig(projectDir);
7340
7685
  const warnings = [];
7341
7686
  const harness = harnessOverride ?? projectConfig.harness ?? globalConfig.harness ?? DEFAULT_HARNESS;
7687
+ const harnessConfigProfile = resolveHarnessConfigProfile(harness);
7688
+ const pluginDelivery = harnessConfigProfile.pluginDelivery;
7689
+ const workflowPluginsAsGeneratedMcp = pluginDelivery.workflowPluginsVia === "generated-mcp";
7342
7690
  const activeWorkflowSelection = resolveActiveWorkflow({
7343
7691
  globalConfig,
7344
7692
  projectConfig,
@@ -7361,34 +7709,45 @@ function bootstrapRuntimeConfig({
7361
7709
  (plugin) => plugin.claudeArtifactDir
7362
7710
  );
7363
7711
  }
7712
+ const globalPluginResolution = resolvePluginDirs(globalConfig.plugins);
7713
+ const projectPluginResolution = resolvePluginDirs(projectConfig.plugins);
7714
+ warnings.push(
7715
+ ...globalPluginResolution.warnings,
7716
+ ...projectPluginResolution.warnings
7717
+ );
7364
7718
  const pluginDirs = mergePluginDirs({
7365
- workflowPluginDirs: harness === "openai-codex" ? [] : workflowPluginDirs,
7366
- globalPlugins: globalConfig.plugins,
7367
- projectPlugins: projectConfig.plugins,
7719
+ workflowPluginDirs: pluginDelivery.mergeWorkflowPluginDirs ? workflowPluginDirs : [],
7720
+ globalPlugins: globalPluginResolution.dirs,
7721
+ projectPlugins: projectPluginResolution.dirs,
7368
7722
  pluginFlags
7369
7723
  });
7370
- const pluginResult = pluginDirs.length > 0 ? registerPlugins(
7724
+ const effectiveCapabilities = harness === "openai-codex" ? { mcpServers: [], skills: [] } : resolveEffectiveCapabilities({ globalConfig, projectConfig });
7725
+ const personalMcpServers = effectiveCapabilities.mcpServers;
7726
+ const personalSkills = effectiveCapabilities.skills;
7727
+ const pluginResult = pluginDirs.length > 0 || personalMcpServers.length > 0 || personalSkills.length > 0 ? registerPlugins(
7371
7728
  pluginDirs,
7372
7729
  workflowToResolve ? activeWorkflowConfig.workflowSelections?.[workflowToResolve]?.mcpServerOptions : void 0,
7373
- harness !== "openai-codex"
7374
- ) : { mcpConfig: void 0 };
7375
- const workflowPluginMcpConfig = harness === "openai-codex" ? buildPluginMcpConfig(
7730
+ pluginDelivery.registrationBuildsMcpConfig,
7731
+ personalMcpServers,
7732
+ personalSkills
7733
+ ) : { mcpConfig: void 0, conflicts: { mcpServers: [], skills: [] } };
7734
+ const workflowPluginMcpConfig = workflowPluginsAsGeneratedMcp ? buildPluginMcpConfig(
7376
7735
  workflowPluginDirs,
7377
7736
  workflowToResolve ? activeWorkflowConfig.workflowSelections?.[workflowToResolve]?.mcpServerOptions : void 0
7378
- ) : void 0;
7379
- const pluginMcpConfig = harness === "openai-codex" ? void 0 : pluginResult.mcpConfig;
7737
+ ).mcpConfig : void 0;
7738
+ const pluginMcpConfig = workflowPluginsAsGeneratedMcp ? void 0 : pluginResult.mcpConfig;
7380
7739
  const activeWorkflow = resolvedWorkflow;
7381
7740
  const additionalDirectories = [
7382
7741
  ...globalConfig.additionalDirectories,
7383
7742
  ...projectConfig.additionalDirectories
7384
7743
  ];
7385
- const harnessConfigProfile = resolveHarnessConfigProfile(harness);
7386
7744
  const workflowPlan = compileWorkflowPlan({
7387
7745
  workflow: activeWorkflow,
7388
7746
  resolvedPlugins: activeWorkflow && resolvedWorkflow?.name === activeWorkflow.name ? workflowResolvedPlugins : void 0,
7389
- pluginMcpConfig: harness === "openai-codex" && activeWorkflow && resolvedWorkflow?.name === activeWorkflow.name ? workflowPluginMcpConfig : pluginResult.mcpConfig
7747
+ pluginMcpConfig: workflowPluginsAsGeneratedMcp && activeWorkflow && resolvedWorkflow?.name === activeWorkflow.name ? workflowPluginMcpConfig : pluginResult.mcpConfig
7390
7748
  });
7391
7749
  const configModel = projectConfig.model || globalConfig.model || activeWorkflow?.model;
7750
+ const configEffort = activeWorkflow?.effort;
7392
7751
  let isolationPreset = initialIsolationPreset;
7393
7752
  if (activeWorkflow?.isolation) {
7394
7753
  const presetOrder = ["strict", "minimal", "permissive"];
@@ -7407,7 +7766,8 @@ function bootstrapRuntimeConfig({
7407
7766
  additionalDirectories,
7408
7767
  pluginDirs,
7409
7768
  verbose,
7410
- configuredModel: configModel
7769
+ configuredModel: configModel,
7770
+ configuredEffort: configEffort
7411
7771
  });
7412
7772
  const modelName = harnessConfigProfile.resolveModelName({
7413
7773
  projectDir,
@@ -7423,6 +7783,9 @@ function bootstrapRuntimeConfig({
7423
7783
  workflow: activeWorkflow,
7424
7784
  workflowPlan,
7425
7785
  modelName,
7786
+ personalMcpServers,
7787
+ personalSkills,
7788
+ capabilityConflicts: pluginResult.conflicts,
7426
7789
  warnings
7427
7790
  };
7428
7791
  }
@@ -7635,8 +7998,10 @@ function generateTitle(event, ascii = false) {
7635
7998
  }
7636
7999
  function generateNeutralTitle(event, g) {
7637
8000
  switch (event.kind) {
7638
- case "session.start":
7639
- return `Session started (${event.data.source})`;
8001
+ case "session.start": {
8002
+ const sessionTitle = event.data.session_title?.trim();
8003
+ return sessionTitle ? truncate(sessionTitle) : `Session started (${event.data.source})`;
8004
+ }
7640
8005
  case "session.end":
7641
8006
  return `Session ended (${event.data.reason})`;
7642
8007
  case "run.start":
@@ -7645,6 +8010,8 @@ function generateNeutralTitle(event, g) {
7645
8010
  return `Run ${event.data.status}`;
7646
8011
  case "user.prompt":
7647
8012
  return truncate(event.data.prompt);
8013
+ case "prompt.expansion":
8014
+ return event.data.command_name ? truncate(`Expanded /${event.data.command_name}`) : "Prompt expanded";
7648
8015
  case "plan.update":
7649
8016
  return truncate(
7650
8017
  event.data.explanation || event.data.delta || "Plan updated"
@@ -7659,6 +8026,10 @@ function generateNeutralTitle(event, g) {
7659
8026
  return `${g["tool.bullet"]} ${event.data.tool_name}`;
7660
8027
  case "tool.post":
7661
8028
  return `${g["tool.gutter"]} ${event.data.tool_name} result`;
8029
+ case "tool.batch": {
8030
+ const n = event.data.tool_calls.length;
8031
+ return `Batch of ${n} tool call${n === 1 ? "" : "s"}`;
8032
+ }
7662
8033
  case "tool.failure":
7663
8034
  return truncate(
7664
8035
  `${g["status.blocked"]} ${event.data.tool_name} failed: ${event.data.error}`
@@ -7889,6 +8260,7 @@ function createRunLifecycle(boundary) {
7889
8260
  let currentRun = null;
7890
8261
  let seq = 0;
7891
8262
  let runSeq = 0;
8263
+ let currentPromptId;
7892
8264
  function getRunId() {
7893
8265
  const sessId = currentSession?.session_id ?? "unknown";
7894
8266
  return `${sessId}:R${runSeq}`;
@@ -7937,7 +8309,13 @@ function createRunLifecycle(boundary) {
7937
8309
  );
7938
8310
  }
7939
8311
  function beginRun(runtimeEvent, triggerType = "other", promptPreview) {
7940
- if (currentRun && triggerType === "other") return [];
8312
+ const promptId = runtimeEvent.promptId;
8313
+ const isExplicitContextTrigger = triggerType === "resume" || triggerType === "clear" || triggerType === "compact";
8314
+ if (promptId !== void 0 && !isExplicitContextTrigger) {
8315
+ if (currentRun && promptId === currentPromptId) return [];
8316
+ } else if (promptId === void 0 && currentRun && triggerType === "other") {
8317
+ return [];
8318
+ }
7941
8319
  const results = [];
7942
8320
  const closeEvt = closeRunIntoEvent(runtimeEvent, "completed");
7943
8321
  if (closeEvt) results.push(closeEvt);
@@ -7948,6 +8326,7 @@ function createRunLifecycle(boundary) {
7948
8326
  triggerType,
7949
8327
  promptPreview
7950
8328
  );
8329
+ currentPromptId = promptId;
7951
8330
  results.push(
7952
8331
  makeEvent(
7953
8332
  "run.start",
@@ -8027,6 +8406,7 @@ function createRunLifecycle(boundary) {
8027
8406
  if (e.kind === "tool.failure") currentRun.counters.tool_failures++;
8028
8407
  if (e.kind === "permission.request")
8029
8408
  currentRun.counters.permission_requests++;
8409
+ if (e.prompt_id !== void 0) currentPromptId = e.prompt_id;
8030
8410
  }
8031
8411
  }
8032
8412
  }
@@ -8538,6 +8918,15 @@ function toolUseCause(toolUseId, parentId) {
8538
8918
  ...parentId ? { parent_event_id: parentId } : {}
8539
8919
  };
8540
8920
  }
8921
+ function readToolBatchCalls2(value) {
8922
+ if (!Array.isArray(value)) return [];
8923
+ return value.filter((entry) => typeof entry === "object" && entry !== null).map((entry) => readObject(entry)).map((call) => ({
8924
+ tool_name: readString(call["tool_name"]),
8925
+ tool_input: readObject(call["tool_input"]),
8926
+ tool_use_id: readString(call["tool_use_id"]),
8927
+ tool_response: readString(call["tool_response"])
8928
+ }));
8929
+ }
8541
8930
  function createToolProjection(args) {
8542
8931
  const {
8543
8932
  ensureRunArray,
@@ -8594,6 +8983,21 @@ function createToolProjection(args) {
8594
8983
  return {
8595
8984
  mapToolEvent(event, data) {
8596
8985
  const results = ensureRunArray(event);
8986
+ if (event.kind === "tool.batch") {
8987
+ results.push(
8988
+ makeEvent(
8989
+ "tool.batch",
8990
+ "info",
8991
+ resolveToolActor(),
8992
+ {
8993
+ tool_calls: readToolBatchCalls2(data["tool_calls"]),
8994
+ permission_mode: readString(data["permission_mode"])
8995
+ },
8996
+ event
8997
+ )
8998
+ );
8999
+ return results;
9000
+ }
8597
9001
  const toolUseId = resolveToolUseId(event, data);
8598
9002
  const toolName = event.toolName ?? readString(data["tool_name"]) ?? "Unknown";
8599
9003
  const toolInput = readObject(data["tool_input"]);
@@ -9372,7 +9776,8 @@ function createRunSessionProjection(args) {
9372
9776
  {
9373
9777
  source,
9374
9778
  agent_type: readString(data["agent_type"]),
9375
- model: readString(data["model"])
9779
+ model: readString(data["model"]),
9780
+ session_title: readString(data["session_title"])
9376
9781
  },
9377
9782
  event
9378
9783
  )
@@ -9417,6 +9822,34 @@ function createRunSessionProjection(args) {
9417
9822
  );
9418
9823
  return results;
9419
9824
  }
9825
+ if (event.kind === "prompt.expansion") {
9826
+ if (event.promptId !== void 0) {
9827
+ results.push(
9828
+ ...ensureRunArray(
9829
+ event,
9830
+ "user_prompt_submit",
9831
+ readString(data["prompt"])?.slice(0, 80)
9832
+ )
9833
+ );
9834
+ }
9835
+ results.push(
9836
+ makeEvent(
9837
+ "prompt.expansion",
9838
+ "info",
9839
+ "user",
9840
+ {
9841
+ expansion_type: readString(data["expansion_type"]),
9842
+ command_name: readString(data["command_name"]),
9843
+ command_args: readString(data["command_args"]),
9844
+ command_source: readString(data["command_source"]),
9845
+ prompt: readString(data["prompt"]),
9846
+ permission_mode: event.context.permissionMode ?? readString(data["permission_mode"])
9847
+ },
9848
+ event
9849
+ )
9850
+ );
9851
+ return results;
9852
+ }
9420
9853
  if (event.kind === "turn.start") {
9421
9854
  agentMessageStream.clearPending();
9422
9855
  const prompt = readString(data["prompt"]);
@@ -9649,6 +10082,7 @@ var RUN_SESSION_EVENT_KINDS = /* @__PURE__ */ new Set([
9649
10082
  "session.start",
9650
10083
  "session.end",
9651
10084
  "user.prompt",
10085
+ "prompt.expansion",
9652
10086
  "turn.start",
9653
10087
  "message.delta",
9654
10088
  "message.complete",
@@ -9661,6 +10095,7 @@ var TOOL_EVENT_KINDS = /* @__PURE__ */ new Set([
9661
10095
  "tool.delta",
9662
10096
  "tool.pre",
9663
10097
  "tool.post",
10098
+ "tool.batch",
9664
10099
  "tool.failure"
9665
10100
  ]);
9666
10101
  var DECISION_EVENT_KINDS = /* @__PURE__ */ new Set([
@@ -9722,6 +10157,8 @@ function createFeedMapper(bootstrap) {
9722
10157
  ts: runtimeEvent.timestamp,
9723
10158
  session_id: runtimeEvent.sessionId,
9724
10159
  run_id: runId,
10160
+ prompt_id: runtimeEvent.promptId,
10161
+ effort_level: runtimeEvent.effortLevel,
9725
10162
  kind,
9726
10163
  level,
9727
10164
  actor_id: actorId,
@@ -9877,7 +10314,7 @@ function createFeedMapper(bootstrap) {
9877
10314
  }
9878
10315
 
9879
10316
  // src/infra/sessions/schema.ts
9880
- var SCHEMA_VERSION = 6;
10317
+ var SCHEMA_VERSION = 7;
9881
10318
  var applySessionSchema = (db, fromVersion) => {
9882
10319
  db.exec(`
9883
10320
  CREATE TABLE IF NOT EXISTS session (
@@ -9907,6 +10344,10 @@ var applySessionSchema = (db, fromVersion) => {
9907
10344
  actor_id TEXT NOT NULL,
9908
10345
  timestamp INTEGER NOT NULL,
9909
10346
  data JSON NOT NULL,
10347
+ -- Harness-native Prompt identity (Claude prompt_id): a nullable
10348
+ -- correlation key, NOT the Run identity (that stays run_id). Unset for
10349
+ -- pre-prompt bootstrap + harnesses/versions without it (ADR 0009).
10350
+ prompt_id TEXT,
9910
10351
  FOREIGN KEY (runtime_event_id) REFERENCES runtime_events(id)
9911
10352
  );
9912
10353
 
@@ -9960,6 +10401,11 @@ var applySessionSchema = (db, fromVersion) => {
9960
10401
  CREATE INDEX IF NOT EXISTS idx_workflow_runs_session ON workflow_runs(session_id);
9961
10402
  CREATE INDEX IF NOT EXISTS idx_outbox_due ON channel_outbox(next_attempt_at);
9962
10403
  `);
10404
+ if (fromVersion === void 0 || fromVersion >= 7) {
10405
+ db.exec(
10406
+ "CREATE INDEX IF NOT EXISTS idx_feed_prompt ON feed_events(prompt_id);"
10407
+ );
10408
+ }
9963
10409
  if (fromVersion === void 0) return;
9964
10410
  if (fromVersion >= SCHEMA_VERSION) return;
9965
10411
  if (fromVersion < 2) {
@@ -10022,6 +10468,14 @@ var applySessionSchema = (db, fromVersion) => {
10022
10468
  UPDATE schema_version SET version = 6;
10023
10469
  `);
10024
10470
  }
10471
+ const versionAfterV6 = db.prepare("SELECT version FROM schema_version").get().version;
10472
+ if (versionAfterV6 === 6) {
10473
+ db.exec(`
10474
+ ALTER TABLE feed_events ADD COLUMN prompt_id TEXT;
10475
+ CREATE INDEX IF NOT EXISTS idx_feed_prompt ON feed_events(prompt_id);
10476
+ UPDATE schema_version SET version = 7;
10477
+ `);
10478
+ }
10025
10479
  };
10026
10480
  var SESSION_SCHEMA = {
10027
10481
  version: SCHEMA_VERSION,
@@ -10078,8 +10532,8 @@ function createSessionStore(opts) {
10078
10532
  VALUES (?, ?, ?, ?, ?, ?)`
10079
10533
  );
10080
10534
  const insertFeedEvent = db.prepare(
10081
- `INSERT OR IGNORE INTO feed_events (event_id, runtime_event_id, seq, kind, run_id, actor_id, timestamp, data)
10082
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)`
10535
+ `INSERT OR IGNORE INTO feed_events (event_id, runtime_event_id, seq, kind, run_id, actor_id, timestamp, data, prompt_id)
10536
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`
10083
10537
  );
10084
10538
  const insertAdapterSession = db.prepare(
10085
10539
  `INSERT OR IGNORE INTO adapter_sessions (session_id, started_at)
@@ -10134,7 +10588,8 @@ function createSessionStore(opts) {
10134
10588
  fe.run_id,
10135
10589
  fe.actor_id,
10136
10590
  fe.ts,
10137
- JSON.stringify(fe)
10591
+ JSON.stringify(fe),
10592
+ fe.prompt_id ?? null
10138
10593
  );
10139
10594
  }
10140
10595
  updateEventCount.run(feedEvents.length, opts.sessionId);
@@ -10150,7 +10605,8 @@ function createSessionStore(opts) {
10150
10605
  fe.run_id,
10151
10606
  fe.actor_id,
10152
10607
  fe.ts,
10153
- JSON.stringify(fe)
10608
+ JSON.stringify(fe),
10609
+ fe.prompt_id ?? null
10154
10610
  );
10155
10611
  }
10156
10612
  updateEventCount.run(feedEvents.length, opts.sessionId);
@@ -11984,6 +12440,13 @@ var stopFailure = defaultRenderer(
11984
12440
  var permissionDenied = defaultRenderer(
11985
12441
  (event) => `${event.data.tool_name}${event.data.reason ? `: ${event.data.reason}` : ""}`
11986
12442
  );
12443
+ var promptExpansion = defaultRenderer(
12444
+ (event) => event.data.command_name ? `/${event.data.command_name}${event.data.command_args ? ` ${event.data.command_args}` : ""}` : event.data.expansion_type ?? ""
12445
+ );
12446
+ var toolBatch = defaultRenderer((event) => {
12447
+ const names = event.data.tool_calls.map((call) => call.tool_name).filter((name) => Boolean(name));
12448
+ return names.length > 0 ? names.join(", ") : "";
12449
+ });
11987
12450
  var elicitationRequest = defaultRenderer((event) => `elicitation from ${event.data.mcp_server}`);
11988
12451
  var elicitationResult = defaultRenderer(
11989
12452
  (event) => `${event.data.mcp_server} \u2192 ${event.data.action}`
@@ -12032,6 +12495,7 @@ var RENDERERS = {
12032
12495
  "tool.delta": toolDelta,
12033
12496
  "tool.pre": toolPre,
12034
12497
  "tool.post": toolPost,
12498
+ "tool.batch": toolBatch,
12035
12499
  "tool.failure": toolFailure,
12036
12500
  "permission.request": permissionRequest,
12037
12501
  "permission.decision": permissionDecision,
@@ -12071,6 +12535,7 @@ var RENDERERS = {
12071
12535
  "worktree.remove": worktreeRemove,
12072
12536
  "stop.failure": stopFailure,
12073
12537
  "permission.denied": permissionDenied,
12538
+ "prompt.expansion": promptExpansion,
12074
12539
  "elicitation.request": elicitationRequest,
12075
12540
  "elicitation.result": elicitationResult,
12076
12541
  "channel.permission.relayed": channelPermissionRelayed,
@@ -12912,6 +13377,7 @@ function resolveEventToolColumn(event) {
12912
13377
  case "tool.delta":
12913
13378
  case "tool.pre":
12914
13379
  case "tool.post":
13380
+ case "tool.batch":
12915
13381
  case "tool.failure":
12916
13382
  case "permission.decision":
12917
13383
  case "permission.denied":
@@ -12939,6 +13405,7 @@ function resolveEventToolColumn(event) {
12939
13405
  case "todo.done":
12940
13406
  case "agent.message":
12941
13407
  case "teammate.idle":
13408
+ case "prompt.expansion":
12942
13409
  case "task.created":
12943
13410
  case "task.completed":
12944
13411
  case "config.change":
@@ -13642,7 +14109,7 @@ function extractRelayQuestions(event) {
13642
14109
  }
13643
14110
 
13644
14111
  // src/app/channels/gatewayControlClient.ts
13645
- import { readFileSync as readFileSync3 } from "fs";
14112
+ import { readFileSync as readFileSync2 } from "fs";
13646
14113
 
13647
14114
  // src/gateway/control/client.ts
13648
14115
  import crypto from "crypto";
@@ -13750,6 +14217,7 @@ async function connect(opts) {
13750
14217
  preHelloResolve = (frame) => {
13751
14218
  clearTimeout(timer);
13752
14219
  preHelloAbort = null;
14220
+ helloAcked = true;
13753
14221
  resolve(frame);
13754
14222
  };
13755
14223
  preHelloAbort = (err) => {
@@ -13772,7 +14240,6 @@ async function connect(opts) {
13772
14240
  }
13773
14241
  throw new GatewayProtocolError(String(msg));
13774
14242
  }
13775
- helloAcked = true;
13776
14243
  const request = async (kind, payload, reqOpts) => {
13777
14244
  const requestId = crypto.randomUUID();
13778
14245
  const envelope = {
@@ -13793,9 +14260,6 @@ async function connect(opts) {
13793
14260
  );
13794
14261
  connection.send(envelope);
13795
14262
  const res = await responsePromise;
13796
- if (res.request_id !== requestId) {
13797
- throw new GatewayProtocolError("response request_id mismatch");
13798
- }
13799
14263
  if (!res.ok) {
13800
14264
  throw new GatewayProtocolError(
13801
14265
  `${res.error.code}: ${res.error.message}`,
@@ -13832,93 +14296,6 @@ function isStringRecord(v) {
13832
14296
  return typeof v === "object" && v !== null && !Array.isArray(v);
13833
14297
  }
13834
14298
 
13835
- // src/gateway/transport/wsClient.ts
13836
- import { WebSocket } from "ws";
13837
- import { readFileSync as readFileSync2 } from "fs";
13838
- function createWsClientTransport(opts) {
13839
- return {
13840
- kind: "ws",
13841
- connect: () => connectWs(opts)
13842
- };
13843
- }
13844
- function wsClientOptionsForEndpoint(input) {
13845
- return {
13846
- url: input.url,
13847
- ...input.timeoutMs !== void 0 ? { timeoutMs: input.timeoutMs } : {},
13848
- ...input.tlsCaPath !== void 0 ? { tlsCaPath: input.tlsCaPath } : {}
13849
- };
13850
- }
13851
- async function connectWs(opts) {
13852
- const timeoutMs = opts.timeoutMs ?? 5e3;
13853
- const wsOpts = opts.tlsCaPath ? { ca: readFileSync2(opts.tlsCaPath) } : {};
13854
- const ws = new WebSocket(opts.url, wsOpts);
13855
- await new Promise((resolve, reject) => {
13856
- const timer = setTimeout(() => {
13857
- ws.close();
13858
- reject(
13859
- new TransportUnreachableError(`connect timed out after ${timeoutMs}ms`)
13860
- );
13861
- }, timeoutMs);
13862
- ws.once("open", () => {
13863
- clearTimeout(timer);
13864
- resolve();
13865
- });
13866
- ws.once("error", (err) => {
13867
- clearTimeout(timer);
13868
- reject(
13869
- new TransportUnreachableError(
13870
- `gateway not reachable at ${opts.url}: ${err.message}`
13871
- )
13872
- );
13873
- });
13874
- });
13875
- return createWsConnection(ws, opts.url);
13876
- }
13877
- function createWsConnection(ws, peer) {
13878
- const frameHandlers = /* @__PURE__ */ new Set();
13879
- const closeHandlers = /* @__PURE__ */ new Set();
13880
- const errorHandlers = /* @__PURE__ */ new Set();
13881
- ws.on("message", (data) => {
13882
- let parsed;
13883
- try {
13884
- parsed = JSON.parse(data.toString());
13885
- } catch {
13886
- ws.close();
13887
- return;
13888
- }
13889
- traceGatewayFrame("ws-client", peer, "in", parsed);
13890
- for (const handler of frameHandlers) handler(parsed);
13891
- });
13892
- ws.on("error", (err) => {
13893
- for (const handler of errorHandlers) handler(err);
13894
- });
13895
- ws.on("close", () => {
13896
- for (const handler of closeHandlers) handler();
13897
- });
13898
- return {
13899
- kind: "ws",
13900
- peer,
13901
- send: (frame) => {
13902
- if (ws.readyState !== ws.OPEN) return;
13903
- traceGatewayFrame("ws-client", peer, "out", frame);
13904
- ws.send(JSON.stringify(frame));
13905
- },
13906
- close: () => ws.close(),
13907
- onFrame: (cb) => {
13908
- frameHandlers.add(cb);
13909
- return () => frameHandlers.delete(cb);
13910
- },
13911
- onClose: (cb) => {
13912
- closeHandlers.add(cb);
13913
- return () => closeHandlers.delete(cb);
13914
- },
13915
- onError: (cb) => {
13916
- errorHandlers.add(cb);
13917
- return () => errorHandlers.delete(cb);
13918
- }
13919
- };
13920
- }
13921
-
13922
14299
  // src/app/channels/gatewayControlClient.ts
13923
14300
  async function connectGatewayControlClient(opts) {
13924
14301
  const loadToken = opts.loadToken ?? defaultLoadToken;
@@ -13940,7 +14317,7 @@ async function connectGatewayControlClient(opts) {
13940
14317
  });
13941
14318
  }
13942
14319
  function defaultLoadToken(tokenPath) {
13943
- return readFileSync3(tokenPath, "utf8").trim();
14320
+ return readFileSync2(tokenPath, "utf8").trim();
13944
14321
  }
13945
14322
 
13946
14323
  // src/infra/config/gatewayClient.ts
@@ -15034,6 +15411,10 @@ function createExecOutputWriter(options) {
15034
15411
  if (!options.verbose) return;
15035
15412
  writeLine(options.stderr, `[athena exec] ${message}`);
15036
15413
  },
15414
+ notice(message) {
15415
+ if (options.json) return;
15416
+ writeLine(options.stderr, `[athena exec] ${message}`);
15417
+ },
15037
15418
  warn(message) {
15038
15419
  writeLine(options.stderr, `[athena exec] warning: ${message}`);
15039
15420
  },
@@ -15062,6 +15443,34 @@ var NULL_TOKENS3 = {
15062
15443
  contextSize: null,
15063
15444
  contextWindowSize: null
15064
15445
  };
15446
+ function formatPersonalCapabilityNotice(summary) {
15447
+ const parts = [];
15448
+ if (summary.mcpServers.length > 0) {
15449
+ const list = summary.mcpServers.map((server) => `${server.name} [${server.sourceLayer}]`).join(", ");
15450
+ parts.push(`mcp servers: ${list}`);
15451
+ }
15452
+ if (summary.skills.length > 0) {
15453
+ const list = summary.skills.map((skill) => `${skill.name} [${skill.sourceLayer}]`).join(", ");
15454
+ parts.push(`skills: ${list}`);
15455
+ }
15456
+ if (parts.length === 0) return null;
15457
+ return `personal capabilities active \u2014 ${parts.join("; ")}`;
15458
+ }
15459
+ function formatCapabilityConflictNotice(summary) {
15460
+ const parts = [];
15461
+ if (summary.mcpServers.length > 0) {
15462
+ const list = summary.mcpServers.map((server) => `${server.name} [${server.sourceLayer}]`).join(", ");
15463
+ parts.push(`mcp servers: ${list}`);
15464
+ }
15465
+ if (summary.skills.length > 0) {
15466
+ const list = summary.skills.map((skill) => `${skill.name} [${skill.sourceLayer}]`).join(", ");
15467
+ parts.push(`skills: ${list}`);
15468
+ }
15469
+ if (parts.length === 0) return null;
15470
+ return `personal capability conflicts \u2014 workflow plugin wins; shadowed: ${parts.join(
15471
+ "; "
15472
+ )}`;
15473
+ }
15065
15474
  function workflowFailure(state, message) {
15066
15475
  return {
15067
15476
  kind: "workflow",
@@ -15336,11 +15745,29 @@ async function runExec(options) {
15336
15745
  });
15337
15746
  }, options.timeoutMs);
15338
15747
  }
15748
+ const personalCapabilities = options.personalCapabilities ?? {
15749
+ mcpServers: [],
15750
+ skills: []
15751
+ };
15752
+ const capabilityConflicts = options.capabilityConflicts ?? {
15753
+ mcpServers: [],
15754
+ skills: []
15755
+ };
15339
15756
  output.emitJsonEvent("exec.started", {
15340
15757
  projectDir: options.projectDir,
15341
15758
  harness: options.harness,
15342
- athenaSessionId: options.ephemeral ? null : athenaSessionId
15759
+ athenaSessionId: options.ephemeral ? null : athenaSessionId,
15760
+ personalCapabilities,
15761
+ capabilityConflicts
15343
15762
  });
15763
+ const personalCapabilityNotice = formatPersonalCapabilityNotice(personalCapabilities);
15764
+ if (personalCapabilityNotice) {
15765
+ output.notice(personalCapabilityNotice);
15766
+ }
15767
+ const capabilityConflictNotice = formatCapabilityConflictNotice(capabilityConflicts);
15768
+ if (capabilityConflictNotice) {
15769
+ output.notice(capabilityConflictNotice);
15770
+ }
15344
15771
  try {
15345
15772
  await runtime.start();
15346
15773
  runtimeStarted = true;
@@ -15497,7 +15924,7 @@ async function runExec(options) {
15497
15924
  }
15498
15925
 
15499
15926
  // src/app/dashboard/instanceSocketClient.ts
15500
- import { WebSocket as WebSocket2 } from "ws";
15927
+ import { WebSocket } from "ws";
15501
15928
  var DEFAULT_HEARTBEAT_MS = 3e4;
15502
15929
  var DEFAULT_CONNECT_TIMEOUT_MS = 1e4;
15503
15930
  function instanceSocketUrl(dashboardUrl, instanceId) {
@@ -15514,7 +15941,7 @@ function createInstanceSocketClient(opts) {
15514
15941
  const log = opts.log ?? (() => {
15515
15942
  });
15516
15943
  const now = opts.now ?? (() => Date.now());
15517
- const makeWebSocket = opts.makeWebSocket ?? ((url, accessToken) => new WebSocket2(url, [accessToken]));
15944
+ const makeWebSocket = opts.makeWebSocket ?? ((url, accessToken) => new WebSocket(url, [accessToken]));
15518
15945
  const frameHandlers = /* @__PURE__ */ new Set();
15519
15946
  const closeHandlers = /* @__PURE__ */ new Set();
15520
15947
  let ws = null;
@@ -15714,7 +16141,7 @@ function normalizeHarnessOverride(value) {
15714
16141
  }
15715
16142
 
15716
16143
  // src/app/dashboard/runStreamClient.ts
15717
- import { WebSocket as WebSocket3 } from "ws";
16144
+ import { WebSocket as WebSocket2 } from "ws";
15718
16145
  var DEFAULT_RECONNECT_DELAYS_MS = [250, 1e3, 2e3, 5e3, 15e3];
15719
16146
  var DEFAULT_HEARTBEAT_MS2 = 3e4;
15720
16147
  var DEFAULT_WATCHDOG_MS = 9e4;
@@ -15727,7 +16154,7 @@ function createRunStreamClient(opts) {
15727
16154
  const heartbeatMs = opts.heartbeatIntervalMs ?? DEFAULT_HEARTBEAT_MS2;
15728
16155
  const watchdogMs = opts.watchdogTimeoutMs ?? DEFAULT_WATCHDOG_MS;
15729
16156
  const maxQueueSize = opts.maxQueueSize ?? DEFAULT_MAX_QUEUE_SIZE;
15730
- const makeWebSocket = opts.makeWebSocket ?? ((url) => new WebSocket3(url));
16157
+ const makeWebSocket = opts.makeWebSocket ?? ((url) => new WebSocket2(url));
15731
16158
  const setTimer = opts.setTimer ?? ((fn, ms) => setTimeout(fn, ms));
15732
16159
  const clearTimer = opts.clearTimer ?? ((t) => clearTimeout(t));
15733
16160
  let nextSeq = 1;
@@ -18092,8 +18519,6 @@ export {
18092
18519
  GatewayUnreachableError,
18093
18520
  GatewayUnauthorizedError,
18094
18521
  connect,
18095
- createWsClientTransport,
18096
- wsClientOptionsForEndpoint,
18097
18522
  isSupportedGatewayUrl,
18098
18523
  readGatewayClientConfig,
18099
18524
  writeGatewayClientConfig,
@@ -18121,6 +18546,8 @@ export {
18121
18546
  isBashToolResponse,
18122
18547
  resolveToolColumn,
18123
18548
  IndexedTimeline,
18549
+ parseFrontmatter,
18550
+ resolveEffectiveCapabilities,
18124
18551
  bootstrapRuntimeConfig,
18125
18552
  EXEC_EXIT_CODE,
18126
18553
  runExec,
@@ -18133,4 +18560,4 @@ export {
18133
18560
  startUdsServer,
18134
18561
  sendUdsRequest
18135
18562
  };
18136
- //# sourceMappingURL=chunk-QBMYQJFX.js.map
18563
+ //# sourceMappingURL=chunk-YZ7RCBKO.js.map