@hydra-acp/browser 0.1.12 → 0.1.13

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.
Files changed (2) hide show
  1. package/dist/ui/index.html +116 -0
  2. package/package.json +1 -1
@@ -10634,9 +10634,69 @@ pre code { background: transparent; padding: 0; }
10634
10634
  c2.spinner = { toolCallIds: [], expanded: false };
10635
10635
  insertAboveQueued({ kind: "spinner", spinner: c2.spinner });
10636
10636
  }
10637
+ function isExitPlanModeTool(name) {
10638
+ if (!name) return false;
10639
+ return name.toLowerCase().replace(/[_\s-]/g, "") === "exitplanmode";
10640
+ }
10641
+ function readExitPlanMarkdown(update) {
10642
+ const rawInput = update.rawInput;
10643
+ if (!rawInput || typeof rawInput !== "object" || Array.isArray(rawInput)) {
10644
+ return null;
10645
+ }
10646
+ const plan = rawInput.plan;
10647
+ if (typeof plan !== "string" || plan.length === 0) return null;
10648
+ return plan;
10649
+ }
10650
+ function findExitPlanLogItem(toolCallId) {
10651
+ if (!state.current) return null;
10652
+ const log = state.current.log;
10653
+ for (let i = 0; i < log.length; i++) {
10654
+ const entry = log[i];
10655
+ if (entry.kind === "exit-plan-mode" && entry.toolCallId === toolCallId) {
10656
+ return { idx: i, item: entry };
10657
+ }
10658
+ }
10659
+ return null;
10660
+ }
10661
+ function applyExitPlanModeUpdate(update) {
10662
+ if (!state.current) return false;
10663
+ const toolCallId = String(update.toolCallId ?? "");
10664
+ if (!toolCallId) return false;
10665
+ const name = update.name ?? update.title;
10666
+ const existing = findExitPlanLogItem(toolCallId);
10667
+ if (!isExitPlanModeTool(name) && !existing) {
10668
+ return false;
10669
+ }
10670
+ const plan = readExitPlanMarkdown(update);
10671
+ const status = typeof update.status === "string" ? update.status : void 0;
10672
+ if (existing) {
10673
+ if (plan !== null) existing.item.plan = plan;
10674
+ if (status !== void 0) existing.item.status = status;
10675
+ return true;
10676
+ }
10677
+ if (plan === null) {
10678
+ return false;
10679
+ }
10680
+ closeOpenStream();
10681
+ const item = {
10682
+ kind: "exit-plan-mode",
10683
+ toolCallId,
10684
+ plan
10685
+ };
10686
+ if (status !== void 0) item.status = status;
10687
+ insertAboveQueued(item);
10688
+ return true;
10689
+ }
10637
10690
  function onToolCall(update) {
10638
10691
  if (!state.current) return;
10639
10692
  closeOpenStream();
10693
+ if (applyExitPlanModeUpdate(update)) {
10694
+ maybeResolvePermissionByToolCall(
10695
+ String(update.toolCallId),
10696
+ typeof update.status === "string" ? update.status : void 0
10697
+ );
10698
+ return;
10699
+ }
10640
10700
  const tc = {
10641
10701
  toolCallId: String(update.toolCallId),
10642
10702
  title: String(update.title ?? update.kind ?? "tool"),
@@ -10651,6 +10711,15 @@ pre code { background: transparent; padding: 0; }
10651
10711
  }
10652
10712
  function onToolCallUpdate(update) {
10653
10713
  if (!state.current) return;
10714
+ if (applyExitPlanModeUpdate(update)) {
10715
+ if (typeof update.status === "string") {
10716
+ maybeResolvePermissionByToolCall(
10717
+ String(update.toolCallId),
10718
+ update.status
10719
+ );
10720
+ }
10721
+ return;
10722
+ }
10654
10723
  const existing = state.current.toolCalls.get(String(update.toolCallId));
10655
10724
  if (!existing) return;
10656
10725
  if (typeof update.status === "string") {
@@ -12412,8 +12481,55 @@ Replace it? (Any live attach will be closed.)`
12412
12481
  if (item.kind === "plan") {
12413
12482
  return renderPlan(item.entries);
12414
12483
  }
12484
+ if (item.kind === "exit-plan-mode") {
12485
+ return renderExitPlan(item);
12486
+ }
12415
12487
  return document.createTextNode("");
12416
12488
  }
12489
+ function renderExitPlan(item) {
12490
+ const node = el("div", { class: "msg agent plan-markdown" });
12491
+ node.appendChild(el("div", { class: "plan-header" }, "\u{1F4CB} Plan"));
12492
+ const body = el("div", { class: "body" });
12493
+ body.innerHTML = renderMarkdown(item.plan);
12494
+ node.appendChild(body);
12495
+ const footer = exitPlanFooter(item.status);
12496
+ if (footer !== null) node.appendChild(footer);
12497
+ return node;
12498
+ }
12499
+ function exitPlanFooter(status) {
12500
+ if (status === void 0) return null;
12501
+ switch (status) {
12502
+ case "completed":
12503
+ case "succeeded":
12504
+ case "ok":
12505
+ return el("div", { class: "plan-status plan-status-ok" }, "\u2713 Approved");
12506
+ case "failed":
12507
+ case "error":
12508
+ case "rejected":
12509
+ return el(
12510
+ "div",
12511
+ { class: "plan-status plan-status-fail" },
12512
+ "\u2717 Rejected"
12513
+ );
12514
+ case "cancelled":
12515
+ return el(
12516
+ "div",
12517
+ { class: "plan-status plan-status-cancelled" },
12518
+ "\u229D Cancelled"
12519
+ );
12520
+ case "pending":
12521
+ case "in_progress":
12522
+ case "running":
12523
+ case "updated":
12524
+ return el(
12525
+ "div",
12526
+ { class: "plan-status plan-status-pending" },
12527
+ "awaiting approval\u2026"
12528
+ );
12529
+ default:
12530
+ return null;
12531
+ }
12532
+ }
12417
12533
  function renderQueueChip(entry) {
12418
12534
  if (entry.status === "queued" || entry.status === "pending") {
12419
12535
  const ahead = Math.max(1, entry.aheadAtEnqueue);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hydra-acp/browser",
3
- "version": "0.1.12",
3
+ "version": "0.1.13",
4
4
  "description": "Browser-based UI for hydra-acp sessions.",
5
5
  "license": "MIT",
6
6
  "type": "module",