@poncho-ai/cli 0.14.0 → 0.14.1

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.
@@ -339,6 +339,8 @@ var renderWebUiHtml = (options) => {
339
339
  --tool-done: #6a9955;
340
340
  --tool-error: #f48771;
341
341
 
342
+ --warning: #e8a735;
343
+
342
344
  --approve: #78e7a6;
343
345
  --approve-border: rgba(58,208,122,0.45);
344
346
  --deny: #f59b9b;
@@ -416,6 +418,8 @@ var renderWebUiHtml = (options) => {
416
418
  --tool-done: #16a34a;
417
419
  --tool-error: #dc2626;
418
420
 
421
+ --warning: #ca8a04;
422
+
419
423
  --approve: #16a34a;
420
424
  --approve-border: rgba(22,163,74,0.35);
421
425
  --deny: #dc2626;
@@ -561,6 +565,20 @@ var renderWebUiHtml = (options) => {
561
565
  flex-direction: column;
562
566
  gap: 2px;
563
567
  }
568
+ .sidebar-section-label {
569
+ font-size: 11px;
570
+ font-weight: 600;
571
+ color: var(--fg-7);
572
+ text-transform: uppercase;
573
+ letter-spacing: 0.04em;
574
+ padding: 10px 10px 4px;
575
+ }
576
+ .sidebar-section-label:first-child { padding-top: 4px; }
577
+ .sidebar-section-divider {
578
+ height: 1px;
579
+ background: var(--border);
580
+ margin: 6px 10px;
581
+ }
564
582
  .conversation-item {
565
583
  height: 36px;
566
584
  min-height: 36px;
@@ -578,6 +596,16 @@ var renderWebUiHtml = (options) => {
578
596
  position: relative;
579
597
  transition: color 0.15s;
580
598
  }
599
+ .conversation-item .approval-dot {
600
+ display: inline-block;
601
+ width: 6px;
602
+ height: 6px;
603
+ border-radius: 50%;
604
+ background: var(--warning, #e8a735);
605
+ margin-right: 6px;
606
+ flex-shrink: 0;
607
+ vertical-align: middle;
608
+ }
581
609
  .conversation-item:hover { color: var(--fg-3); }
582
610
  .conversation-item.active {
583
611
  color: var(--fg);
@@ -801,7 +829,7 @@ var renderWebUiHtml = (options) => {
801
829
  .tool-error {
802
830
  color: var(--tool-error);
803
831
  }
804
- .assistant-content table {
832
+ .assistant-content table:not(.approval-request-table) {
805
833
  border-collapse: collapse;
806
834
  width: 100%;
807
835
  margin: 14px 0;
@@ -814,7 +842,7 @@ var renderWebUiHtml = (options) => {
814
842
  overflow-x: auto;
815
843
  white-space: nowrap;
816
844
  }
817
- .assistant-content th {
845
+ .assistant-content table:not(.approval-request-table) th {
818
846
  background: var(--surface-4);
819
847
  padding: 10px 12px;
820
848
  text-align: left;
@@ -823,16 +851,16 @@ var renderWebUiHtml = (options) => {
823
851
  color: var(--fg-strong);
824
852
  min-width: 100px;
825
853
  }
826
- .assistant-content td {
854
+ .assistant-content table:not(.approval-request-table) td {
827
855
  padding: 10px 12px;
828
856
  border-bottom: 1px solid var(--border-1);
829
857
  width: 100%;
830
858
  min-width: 100px;
831
859
  }
832
- .assistant-content tr:last-child td {
860
+ .assistant-content table:not(.approval-request-table) tr:last-child td {
833
861
  border-bottom: none;
834
862
  }
835
- .assistant-content tbody tr:hover {
863
+ .assistant-content table:not(.approval-request-table) tbody tr:hover {
836
864
  background: var(--surface-1);
837
865
  }
838
866
  .assistant-content hr {
@@ -850,6 +878,10 @@ var renderWebUiHtml = (options) => {
850
878
  line-height: 1.45;
851
879
  color: var(--fg-tool-code);
852
880
  width: 300px;
881
+ transition: width 0.2s ease;
882
+ }
883
+ .tool-activity.has-approvals {
884
+ width: 100%;
853
885
  }
854
886
  .assistant-content > .tool-activity:first-child {
855
887
  margin-top: 0;
@@ -909,40 +941,60 @@ var renderWebUiHtml = (options) => {
909
941
  border-top: 1px solid var(--border-2);
910
942
  padding: 10px 12px 12px;
911
943
  display: grid;
912
- gap: 8px;
913
- background: var(--inset-1);
944
+ gap: 10px;
914
945
  }
915
946
  .approval-requests-label {
916
- font-size: 11px;
947
+ font-size: 12px;
917
948
  text-transform: uppercase;
918
949
  letter-spacing: 0.06em;
919
950
  color: var(--fg-approval-label);
920
951
  font-weight: 600;
921
952
  }
953
+ .approval-requests-label code {
954
+ font-family: ui-monospace, "SF Mono", "Fira Code", monospace;
955
+ text-transform: none;
956
+ letter-spacing: 0;
957
+ color: var(--fg-strong);
958
+ }
922
959
  .approval-request-item {
923
- border: 1px solid var(--border-3);
924
- background: var(--surface-2);
925
- border-radius: 8px;
926
- padding: 8px;
927
960
  display: grid;
928
- gap: 6px;
961
+ gap: 8px;
929
962
  }
930
- .approval-request-tool {
931
- font-size: 12px;
932
- color: var(--fg-strong);
963
+ .approval-request-table {
964
+ width: 100%;
965
+ border-collapse: collapse;
966
+ border: none;
967
+ font-size: 14px;
968
+ line-height: 1.5;
969
+ }
970
+ .approval-request-table tr,
971
+ .approval-request-table td {
972
+ border: none;
973
+ background: none;
974
+ }
975
+ .approval-request-table td {
976
+ padding: 4px 0;
977
+ vertical-align: top;
978
+ }
979
+ .approval-request-table .ak {
933
980
  font-weight: 600;
934
- overflow-wrap: anywhere;
981
+ color: var(--fg-approval-label);
982
+ white-space: nowrap;
983
+ width: 1%;
984
+ padding-right: 20px;
935
985
  }
936
- .approval-request-input {
937
- font-family: ui-monospace, "SF Mono", "Fira Code", monospace;
938
- font-size: 11px;
939
- color: var(--fg-approval-input);
940
- background: var(--inset-2);
941
- border-radius: 6px;
942
- padding: 6px;
986
+ .approval-request-table .av,
987
+ .approval-request-table .av-complex {
988
+ color: var(--fg);
943
989
  overflow-wrap: anywhere;
944
- max-height: 80px;
990
+ white-space: pre-wrap;
991
+ max-height: 200px;
945
992
  overflow-y: auto;
993
+ display: block;
994
+ }
995
+ .approval-request-table .av-complex {
996
+ font-family: ui-monospace, "SF Mono", "Fira Code", monospace;
997
+ font-size: 12px;
946
998
  }
947
999
  .approval-request-actions {
948
1000
  display: flex;
@@ -983,6 +1035,7 @@ var renderWebUiHtml = (options) => {
983
1035
  line-height: 1.5;
984
1036
  overflow-wrap: break-word;
985
1037
  word-break: break-word;
1038
+ white-space: pre-wrap;
986
1039
  }
987
1040
  .empty-state {
988
1041
  display: flex;
@@ -1656,18 +1709,20 @@ var renderWebUiHtml = (options) => {
1656
1709
  .map((req) => {
1657
1710
  const approvalId = typeof req.approvalId === "string" ? req.approvalId : "";
1658
1711
  const tool = typeof req.tool === "string" ? req.tool : "tool";
1659
- const inputPreview = typeof req.inputPreview === "string" ? req.inputPreview : "{}";
1712
+ const input = req.input != null ? req.input : {};
1660
1713
  const submitting = req.state === "submitting";
1661
1714
  const approveLabel = submitting && req.pendingDecision === "approve" ? "Approving..." : "Approve";
1662
1715
  const denyLabel = submitting && req.pendingDecision === "deny" ? "Denying..." : "Deny";
1716
+ const errorHtml = req._error
1717
+ ? '<div style="color: var(--deny); font-size: 11px; margin-top: 4px;">Submit failed: ' + escapeHtml(req._error) + "</div>"
1718
+ : "";
1663
1719
  return (
1664
1720
  '<div class="approval-request-item">' +
1665
- '<div class="approval-request-tool">' +
1721
+ '<div class="approval-requests-label">Approval required: <code>' +
1666
1722
  escapeHtml(tool) +
1667
- "</div>" +
1668
- '<div class="approval-request-input">' +
1669
- escapeHtml(inputPreview) +
1670
- "</div>" +
1723
+ "</code></div>" +
1724
+ renderInputTable(input) +
1725
+ errorHtml +
1671
1726
  '<div class="approval-request-actions">' +
1672
1727
  '<button class="approval-action-btn approve" data-approval-id="' +
1673
1728
  escapeHtml(approvalId) +
@@ -1690,7 +1745,6 @@ var renderWebUiHtml = (options) => {
1690
1745
  .join("");
1691
1746
  return (
1692
1747
  '<div class="approval-requests">' +
1693
- '<div class="approval-requests-label">Approval required</div>' +
1694
1748
  rows +
1695
1749
  "</div>"
1696
1750
  );
@@ -1720,22 +1774,46 @@ var renderWebUiHtml = (options) => {
1720
1774
  "</details>"
1721
1775
  )
1722
1776
  : "";
1777
+ const cls = "tool-activity" + (hasApprovals ? " has-approvals" : "");
1723
1778
  return (
1724
- '<div class="tool-activity">' +
1779
+ '<div class="' + cls + '">' +
1725
1780
  disclosure +
1726
1781
  renderApprovalRequests(approvalRequests) +
1727
1782
  "</div>"
1728
1783
  );
1729
1784
  };
1730
1785
 
1731
- const safeJsonPreview = (value) => {
1732
- try {
1733
- return JSON.stringify(value, (_, nestedValue) =>
1734
- typeof nestedValue === "bigint" ? String(nestedValue) : nestedValue,
1735
- );
1736
- } catch {
1737
- return "[unserializable input]";
1786
+ const renderInputTable = (input) => {
1787
+ if (!input || typeof input !== "object") {
1788
+ return '<div class="av-complex">' + escapeHtml(String(input ?? "{}")) + "</div>";
1738
1789
  }
1790
+ const keys = Object.keys(input);
1791
+ if (keys.length === 0) {
1792
+ return '<div class="av-complex">{}</div>';
1793
+ }
1794
+ const formatValue = (val) => {
1795
+ if (val === null || val === undefined) return escapeHtml("null");
1796
+ if (typeof val === "boolean" || typeof val === "number") return escapeHtml(String(val));
1797
+ if (typeof val === "string") return escapeHtml(val);
1798
+ try {
1799
+ const replacer = (_, v) => typeof v === "bigint" ? String(v) : v;
1800
+ return escapeHtml(JSON.stringify(val, replacer, 2));
1801
+ } catch {
1802
+ return escapeHtml("[unserializable]");
1803
+ }
1804
+ };
1805
+ const rows = keys.map((key) => {
1806
+ const val = input[key];
1807
+ const isComplex = val !== null && typeof val === "object";
1808
+ const cls = isComplex ? "av-complex" : "av";
1809
+ return (
1810
+ "<tr>" +
1811
+ '<td class="ak">' + escapeHtml(key) + "</td>" +
1812
+ '<td><div class="' + cls + '">' + formatValue(val) + "</div></td>" +
1813
+ "</tr>"
1814
+ );
1815
+ }).join("");
1816
+ return '<table class="approval-request-table">' + rows + "</table>";
1739
1817
  };
1740
1818
 
1741
1819
  const updatePendingApproval = (approvalId, updater) => {
@@ -1774,12 +1852,10 @@ var renderWebUiHtml = (options) => {
1774
1852
  return null;
1775
1853
  }
1776
1854
  const toolName = item && typeof item.tool === "string" ? item.tool : "tool";
1777
- const preview = safeJsonPreview(item?.input ?? {});
1778
- const inputPreview = preview.length > 600 ? preview.slice(0, 600) + "..." : preview;
1779
1855
  return {
1780
1856
  approvalId,
1781
1857
  tool: toolName,
1782
- inputPreview,
1858
+ input: item?.input ?? {},
1783
1859
  state: "pending",
1784
1860
  };
1785
1861
  })
@@ -1857,53 +1933,87 @@ var renderWebUiHtml = (options) => {
1857
1933
  elements.shell.classList.toggle("sidebar-open", open);
1858
1934
  };
1859
1935
 
1860
- const renderConversationList = () => {
1861
- elements.list.innerHTML = "";
1862
- for (const c of state.conversations) {
1863
- const item = document.createElement("div");
1864
- item.className = "conversation-item" + (c.conversationId === state.activeConversationId ? " active" : "");
1865
- item.textContent = c.title;
1866
-
1867
- const isConfirming = state.confirmDeleteId === c.conversationId;
1868
- const deleteBtn = document.createElement("button");
1869
- deleteBtn.className = "delete-btn" + (isConfirming ? " confirming" : "");
1870
- deleteBtn.textContent = isConfirming ? "sure?" : "\\u00d7";
1871
- deleteBtn.onclick = async (e) => {
1872
- e.stopPropagation();
1873
- if (!isConfirming) {
1874
- state.confirmDeleteId = c.conversationId;
1875
- renderConversationList();
1876
- return;
1877
- }
1878
- await api("/api/conversations/" + c.conversationId, { method: "DELETE" });
1879
- if (state.activeConversationId === c.conversationId) {
1880
- state.activeConversationId = null;
1881
- state.activeMessages = [];
1882
- state.contextTokens = 0;
1883
- state.contextWindow = 0;
1884
- updateContextRing();
1885
- pushConversationUrl(null);
1886
- elements.chatTitle.textContent = "";
1887
- renderMessages([]);
1888
- }
1889
- state.confirmDeleteId = null;
1890
- await loadConversations();
1891
- };
1892
- item.appendChild(deleteBtn);
1936
+ const buildConversationItem = (c) => {
1937
+ const item = document.createElement("div");
1938
+ item.className = "conversation-item" + (c.conversationId === state.activeConversationId ? " active" : "");
1893
1939
 
1894
- item.onclick = async () => {
1895
- // Clear any delete confirmation, but still navigate
1896
- if (state.confirmDeleteId) {
1897
- state.confirmDeleteId = null;
1898
- }
1899
- state.activeConversationId = c.conversationId;
1900
- pushConversationUrl(c.conversationId);
1940
+ if (c.hasPendingApprovals) {
1941
+ const dot = document.createElement("span");
1942
+ dot.className = "approval-dot";
1943
+ item.appendChild(dot);
1944
+ }
1945
+
1946
+ const titleSpan = document.createElement("span");
1947
+ titleSpan.textContent = c.title;
1948
+ item.appendChild(titleSpan);
1949
+
1950
+ const isConfirming = state.confirmDeleteId === c.conversationId;
1951
+ const deleteBtn = document.createElement("button");
1952
+ deleteBtn.className = "delete-btn" + (isConfirming ? " confirming" : "");
1953
+ deleteBtn.textContent = isConfirming ? "sure?" : "\\u00d7";
1954
+ deleteBtn.onclick = async (e) => {
1955
+ e.stopPropagation();
1956
+ if (!isConfirming) {
1957
+ state.confirmDeleteId = c.conversationId;
1901
1958
  renderConversationList();
1902
- await loadConversation(c.conversationId);
1903
- if (isMobile()) setSidebarOpen(false);
1904
- };
1959
+ return;
1960
+ }
1961
+ await api("/api/conversations/" + c.conversationId, { method: "DELETE" });
1962
+ if (state.activeConversationId === c.conversationId) {
1963
+ state.activeConversationId = null;
1964
+ state.activeMessages = [];
1965
+ state.contextTokens = 0;
1966
+ state.contextWindow = 0;
1967
+ updateContextRing();
1968
+ pushConversationUrl(null);
1969
+ elements.chatTitle.textContent = "";
1970
+ renderMessages([]);
1971
+ }
1972
+ state.confirmDeleteId = null;
1973
+ await loadConversations();
1974
+ };
1975
+ item.appendChild(deleteBtn);
1976
+
1977
+ item.onclick = async () => {
1978
+ if (state.confirmDeleteId) {
1979
+ state.confirmDeleteId = null;
1980
+ }
1981
+ state.activeConversationId = c.conversationId;
1982
+ pushConversationUrl(c.conversationId);
1983
+ renderConversationList();
1984
+ await loadConversation(c.conversationId);
1985
+ if (isMobile()) setSidebarOpen(false);
1986
+ };
1905
1987
 
1906
- elements.list.appendChild(item);
1988
+ return item;
1989
+ };
1990
+
1991
+ const renderConversationList = () => {
1992
+ elements.list.innerHTML = "";
1993
+ const pending = state.conversations.filter(c => c.hasPendingApprovals);
1994
+ const rest = state.conversations.filter(c => !c.hasPendingApprovals);
1995
+
1996
+ if (pending.length > 0) {
1997
+ const label = document.createElement("div");
1998
+ label.className = "sidebar-section-label";
1999
+ label.textContent = "Awaiting approval";
2000
+ elements.list.appendChild(label);
2001
+ for (const c of pending) {
2002
+ elements.list.appendChild(buildConversationItem(c));
2003
+ }
2004
+ if (rest.length > 0) {
2005
+ const divider = document.createElement("div");
2006
+ divider.className = "sidebar-section-divider";
2007
+ elements.list.appendChild(divider);
2008
+ const recentLabel = document.createElement("div");
2009
+ recentLabel.className = "sidebar-section-label";
2010
+ recentLabel.textContent = "Recent";
2011
+ elements.list.appendChild(recentLabel);
2012
+ }
2013
+ }
2014
+
2015
+ for (const c of rest) {
2016
+ elements.list.appendChild(buildConversationItem(c));
1907
2017
  }
1908
2018
  };
1909
2019
 
@@ -2332,8 +2442,6 @@ var renderWebUiHtml = (options) => {
2332
2442
  const approvalId =
2333
2443
  typeof payload.approvalId === "string" ? payload.approvalId : "";
2334
2444
  if (approvalId) {
2335
- const preview = safeJsonPreview(payload.input ?? {});
2336
- const inputPreview = preview.length > 600 ? preview.slice(0, 600) + "..." : preview;
2337
2445
  if (!Array.isArray(assistantMessage._pendingApprovals)) {
2338
2446
  assistantMessage._pendingApprovals = [];
2339
2447
  }
@@ -2344,7 +2452,7 @@ var renderWebUiHtml = (options) => {
2344
2452
  assistantMessage._pendingApprovals.push({
2345
2453
  approvalId,
2346
2454
  tool: toolName,
2347
- inputPreview,
2455
+ input: payload.input ?? {},
2348
2456
  state: "pending",
2349
2457
  });
2350
2458
  }
@@ -2992,8 +3100,6 @@ var renderWebUiHtml = (options) => {
2992
3100
  const approvalId =
2993
3101
  typeof payload.approvalId === "string" ? payload.approvalId : "";
2994
3102
  if (approvalId) {
2995
- const preview = safeJsonPreview(payload.input ?? {});
2996
- const inputPreview = preview.length > 600 ? preview.slice(0, 600) + "..." : preview;
2997
3103
  if (!Array.isArray(assistantMessage._pendingApprovals)) {
2998
3104
  assistantMessage._pendingApprovals = [];
2999
3105
  }
@@ -3004,7 +3110,7 @@ var renderWebUiHtml = (options) => {
3004
3110
  assistantMessage._pendingApprovals.push({
3005
3111
  approvalId,
3006
3112
  tool: toolName,
3007
- inputPreview,
3113
+ input: payload.input ?? {},
3008
3114
  state: "pending",
3009
3115
  });
3010
3116
  }
@@ -3353,17 +3459,23 @@ var renderWebUiHtml = (options) => {
3353
3459
  });
3354
3460
  updatePendingApproval(approvalId, () => null);
3355
3461
  renderMessages(state.activeMessages, state.isStreaming);
3462
+ loadConversations();
3356
3463
  if (!wasStreaming && state.activeConversationId) {
3357
- await streamConversationEvents(state.activeConversationId);
3464
+ await streamConversationEvents(state.activeConversationId, { liveOnly: true });
3358
3465
  }
3359
3466
  } catch (error) {
3360
- const errMsg = error instanceof Error ? error.message : String(error);
3361
- updatePendingApproval(approvalId, (request) => ({
3362
- ...request,
3363
- state: "pending",
3364
- pendingDecision: null,
3365
- inputPreview: String(request.inputPreview || "") + " (submit failed: " + errMsg + ")",
3366
- }));
3467
+ const isStale = error && error.payload && error.payload.code === "APPROVAL_NOT_FOUND";
3468
+ if (isStale) {
3469
+ updatePendingApproval(approvalId, () => null);
3470
+ } else {
3471
+ const errMsg = error instanceof Error ? error.message : String(error);
3472
+ updatePendingApproval(approvalId, (request) => ({
3473
+ ...request,
3474
+ state: "pending",
3475
+ pendingDecision: null,
3476
+ _error: errMsg,
3477
+ }));
3478
+ }
3367
3479
  renderMessages(state.activeMessages, state.isStreaming);
3368
3480
  } finally {
3369
3481
  if (!wasStreaming) {
@@ -5893,7 +6005,6 @@ var createRequestHandler = async (options) => {
5893
6005
  const runOwners = /* @__PURE__ */ new Map();
5894
6006
  const runConversations = /* @__PURE__ */ new Map();
5895
6007
  const activeConversationRuns = /* @__PURE__ */ new Map();
5896
- const pendingApprovals = /* @__PURE__ */ new Map();
5897
6008
  const conversationEventStreams = /* @__PURE__ */ new Map();
5898
6009
  const broadcastEvent = (conversationId, event) => {
5899
6010
  let stream = conversationEventStreams.get(conversationId);
@@ -5925,51 +6036,19 @@ var createRequestHandler = async (options) => {
5925
6036
  setTimeout(() => conversationEventStreams.delete(conversationId), 3e4);
5926
6037
  }
5927
6038
  };
5928
- const persistConversationPendingApprovals = async (conversationId) => {
5929
- const conversation = await conversationStore.get(conversationId);
5930
- if (!conversation) {
5931
- return;
5932
- }
5933
- conversation.pendingApprovals = Array.from(pendingApprovals.entries()).filter(
5934
- ([, pending]) => pending.ownerId === conversation.ownerId && pending.conversationId === conversationId
5935
- ).map(([approvalId, pending]) => ({
5936
- approvalId,
5937
- runId: pending.runId,
5938
- tool: pending.tool,
5939
- input: pending.input
5940
- }));
5941
- await conversationStore.update(conversation);
5942
- };
5943
6039
  const clearPendingApprovalsForConversation = async (conversationId) => {
5944
- for (const [approvalId, pending] of pendingApprovals.entries()) {
5945
- if (pending.conversationId !== conversationId) {
5946
- continue;
5947
- }
5948
- pendingApprovals.delete(approvalId);
5949
- pending.resolve(false);
6040
+ const conversation = await conversationStore.get(conversationId);
6041
+ if (!conversation) return;
6042
+ if (Array.isArray(conversation.pendingApprovals) && conversation.pendingApprovals.length > 0) {
6043
+ conversation.pendingApprovals = [];
6044
+ await conversationStore.update(conversation);
5950
6045
  }
5951
- await persistConversationPendingApprovals(conversationId);
5952
6046
  };
5953
6047
  const uploadStore = await createUploadStore(config?.uploads, workingDir);
5954
6048
  const harness = new AgentHarness({
5955
6049
  workingDir,
5956
6050
  environment: resolveHarnessEnvironment(),
5957
- uploadStore,
5958
- approvalHandler: async (request) => new Promise((resolveApproval) => {
5959
- const ownerIdForRun = runOwners.get(request.runId) ?? "local-owner";
5960
- const conversationIdForRun = runConversations.get(request.runId) ?? null;
5961
- pendingApprovals.set(request.approvalId, {
5962
- ownerId: ownerIdForRun,
5963
- runId: request.runId,
5964
- conversationId: conversationIdForRun,
5965
- tool: request.tool,
5966
- input: request.input,
5967
- resolve: resolveApproval
5968
- });
5969
- if (conversationIdForRun) {
5970
- void persistConversationPendingApprovals(conversationIdForRun);
5971
- }
5972
- })
6051
+ uploadStore
5973
6052
  });
5974
6053
  await harness.initialize();
5975
6054
  const telemetry = new TelemetryEmitter(config?.telemetry);
@@ -5978,6 +6057,162 @@ var createRequestHandler = async (options) => {
5978
6057
  workingDir,
5979
6058
  agentId: identity.id
5980
6059
  });
6060
+ const resumeRunFromCheckpoint = async (conversationId, conversation, checkpoint, toolResults) => {
6061
+ const abortController = new AbortController();
6062
+ activeConversationRuns.set(conversationId, {
6063
+ ownerId: conversation.ownerId,
6064
+ abortController,
6065
+ runId: null
6066
+ });
6067
+ let latestRunId = conversation.runtimeRunId ?? "";
6068
+ let assistantResponse = "";
6069
+ const toolTimeline = [];
6070
+ const sections = [];
6071
+ let currentText = "";
6072
+ let currentTools = [];
6073
+ let checkpointedRun = false;
6074
+ const baseMessages = checkpoint.baseMessageCount != null ? conversation.messages.slice(0, checkpoint.baseMessageCount) : [];
6075
+ const fullCheckpointMessages = [...baseMessages, ...checkpoint.checkpointMessages];
6076
+ try {
6077
+ for await (const event of harness.continueFromToolResult({
6078
+ messages: fullCheckpointMessages,
6079
+ toolResults,
6080
+ conversationId,
6081
+ abortSignal: abortController.signal
6082
+ })) {
6083
+ if (event.type === "run:started") {
6084
+ latestRunId = event.runId;
6085
+ runOwners.set(event.runId, conversation.ownerId);
6086
+ runConversations.set(event.runId, conversationId);
6087
+ const active = activeConversationRuns.get(conversationId);
6088
+ if (active && active.abortController === abortController) {
6089
+ active.runId = event.runId;
6090
+ }
6091
+ }
6092
+ if (event.type === "model:chunk") {
6093
+ if (currentTools.length > 0) {
6094
+ sections.push({ type: "tools", content: currentTools });
6095
+ currentTools = [];
6096
+ }
6097
+ assistantResponse += event.content;
6098
+ currentText += event.content;
6099
+ }
6100
+ if (event.type === "tool:started") {
6101
+ if (currentText.length > 0) {
6102
+ sections.push({ type: "text", content: currentText });
6103
+ currentText = "";
6104
+ }
6105
+ const toolText = `- start \`${event.tool}\``;
6106
+ toolTimeline.push(toolText);
6107
+ currentTools.push(toolText);
6108
+ }
6109
+ if (event.type === "tool:completed") {
6110
+ const toolText = `- done \`${event.tool}\` (${event.duration}ms)`;
6111
+ toolTimeline.push(toolText);
6112
+ currentTools.push(toolText);
6113
+ }
6114
+ if (event.type === "tool:error") {
6115
+ const toolText = `- error \`${event.tool}\`: ${event.error}`;
6116
+ toolTimeline.push(toolText);
6117
+ currentTools.push(toolText);
6118
+ }
6119
+ if (event.type === "tool:approval:required") {
6120
+ const toolText = `- approval required \`${event.tool}\``;
6121
+ toolTimeline.push(toolText);
6122
+ currentTools.push(toolText);
6123
+ }
6124
+ if (event.type === "tool:approval:checkpoint") {
6125
+ const conv = await conversationStore.get(conversationId);
6126
+ if (conv) {
6127
+ conv.pendingApprovals = [{
6128
+ approvalId: event.approvalId,
6129
+ runId: latestRunId,
6130
+ tool: event.tool,
6131
+ toolCallId: event.toolCallId,
6132
+ input: event.input,
6133
+ checkpointMessages: [...fullCheckpointMessages, ...event.checkpointMessages],
6134
+ baseMessageCount: 0,
6135
+ pendingToolCalls: event.pendingToolCalls
6136
+ }];
6137
+ conv.updatedAt = Date.now();
6138
+ await conversationStore.update(conv);
6139
+ }
6140
+ checkpointedRun = true;
6141
+ }
6142
+ if (event.type === "run:completed" && assistantResponse.length === 0 && event.result.response) {
6143
+ assistantResponse = event.result.response;
6144
+ }
6145
+ if (event.type === "run:error") {
6146
+ assistantResponse = assistantResponse || `[Error: ${event.error.message}]`;
6147
+ }
6148
+ await telemetry.emit(event);
6149
+ broadcastEvent(conversationId, event);
6150
+ }
6151
+ } catch (err) {
6152
+ console.error("[resume-run] error:", err instanceof Error ? err.message : err);
6153
+ assistantResponse = assistantResponse || `[Error: ${err instanceof Error ? err.message : "Unknown error"}]`;
6154
+ }
6155
+ if (currentTools.length > 0) {
6156
+ sections.push({ type: "tools", content: currentTools });
6157
+ }
6158
+ if (currentText.length > 0) {
6159
+ sections.push({ type: "text", content: currentText });
6160
+ }
6161
+ if (!checkpointedRun) {
6162
+ const conv = await conversationStore.get(conversationId);
6163
+ if (conv) {
6164
+ const prevMessages = conv.messages;
6165
+ const hasAssistantContent = assistantResponse.length > 0 || toolTimeline.length > 0 || sections.length > 0;
6166
+ if (hasAssistantContent) {
6167
+ const lastMsg = prevMessages[prevMessages.length - 1];
6168
+ if (lastMsg && lastMsg.role === "assistant" && lastMsg.metadata) {
6169
+ const existingToolActivity = lastMsg.metadata.toolActivity;
6170
+ const existingSections = lastMsg.metadata.sections;
6171
+ const mergedTimeline = [
6172
+ ...Array.isArray(existingToolActivity) ? existingToolActivity : [],
6173
+ ...toolTimeline
6174
+ ];
6175
+ const mergedSections = [
6176
+ ...Array.isArray(existingSections) ? existingSections : [],
6177
+ ...sections
6178
+ ];
6179
+ const mergedText = (typeof lastMsg.content === "string" ? lastMsg.content : "") + assistantResponse;
6180
+ conv.messages = [
6181
+ ...prevMessages.slice(0, -1),
6182
+ {
6183
+ role: "assistant",
6184
+ content: mergedText,
6185
+ metadata: {
6186
+ toolActivity: mergedTimeline,
6187
+ sections: mergedSections.length > 0 ? mergedSections : void 0
6188
+ }
6189
+ }
6190
+ ];
6191
+ } else {
6192
+ conv.messages = [
6193
+ ...prevMessages,
6194
+ {
6195
+ role: "assistant",
6196
+ content: assistantResponse,
6197
+ metadata: toolTimeline.length > 0 || sections.length > 0 ? { toolActivity: toolTimeline, sections: sections.length > 0 ? sections : void 0 } : void 0
6198
+ }
6199
+ ];
6200
+ }
6201
+ }
6202
+ conv.runtimeRunId = latestRunId || conv.runtimeRunId;
6203
+ conv.pendingApprovals = [];
6204
+ conv.updatedAt = Date.now();
6205
+ await conversationStore.update(conv);
6206
+ }
6207
+ }
6208
+ finishConversationStream(conversationId);
6209
+ activeConversationRuns.delete(conversationId);
6210
+ if (latestRunId) {
6211
+ runOwners.delete(latestRunId);
6212
+ runConversations.delete(latestRunId);
6213
+ }
6214
+ console.log("[resume-run] complete for", conversationId);
6215
+ };
5981
6216
  const messagingRoutes = /* @__PURE__ */ new Map();
5982
6217
  const messagingRouteRegistrar = (method, path, routeHandler) => {
5983
6218
  let byMethod = messagingRoutes.get(path);
@@ -6026,6 +6261,7 @@ var createRequestHandler = async (options) => {
6026
6261
  const sections = [];
6027
6262
  let currentTools = [];
6028
6263
  let currentText = "";
6264
+ let checkpointedRun = false;
6029
6265
  const buildMessages = () => {
6030
6266
  const draftSections = [
6031
6267
  ...sections.map((s) => ({
@@ -6116,19 +6352,22 @@ var createRequestHandler = async (options) => {
6116
6352
  toolTimeline.push(toolText);
6117
6353
  currentTools.push(toolText);
6118
6354
  await persistDraftAssistantTurn();
6119
- await persistConversationPendingApprovals(conversationId);
6120
6355
  }
6121
- if (event.type === "tool:approval:granted") {
6122
- const toolText = `- approval granted (${event.approvalId})`;
6123
- toolTimeline.push(toolText);
6124
- currentTools.push(toolText);
6125
- await persistDraftAssistantTurn();
6126
- }
6127
- if (event.type === "tool:approval:denied") {
6128
- const toolText = `- approval denied (${event.approvalId})`;
6129
- toolTimeline.push(toolText);
6130
- currentTools.push(toolText);
6131
- await persistDraftAssistantTurn();
6356
+ if (event.type === "tool:approval:checkpoint") {
6357
+ await updateConversation((c) => {
6358
+ c.messages = buildMessages();
6359
+ c.pendingApprovals = [{
6360
+ approvalId: event.approvalId,
6361
+ runId: latestRunId,
6362
+ tool: event.tool,
6363
+ toolCallId: event.toolCallId,
6364
+ input: event.input,
6365
+ checkpointMessages: event.checkpointMessages,
6366
+ baseMessageCount: historyMessages.length,
6367
+ pendingToolCalls: event.pendingToolCalls
6368
+ }];
6369
+ });
6370
+ checkpointedRun = true;
6132
6371
  }
6133
6372
  if (event.type === "run:completed" && assistantResponse.length === 0 && event.result.response) {
6134
6373
  assistantResponse = event.result.response;
@@ -6150,13 +6389,14 @@ var createRequestHandler = async (options) => {
6150
6389
  sections.push({ type: "text", content: currentText });
6151
6390
  currentText = "";
6152
6391
  }
6153
- await updateConversation((c) => {
6154
- c.messages = buildMessages();
6155
- c.runtimeRunId = latestRunId || c.runtimeRunId;
6156
- c.pendingApprovals = [];
6157
- });
6392
+ if (!checkpointedRun) {
6393
+ await updateConversation((c) => {
6394
+ c.messages = buildMessages();
6395
+ c.runtimeRunId = latestRunId || c.runtimeRunId;
6396
+ c.pendingApprovals = [];
6397
+ });
6398
+ }
6158
6399
  finishConversationStream(conversationId);
6159
- await persistConversationPendingApprovals(conversationId);
6160
6400
  if (latestRunId) {
6161
6401
  runOwners.delete(latestRunId);
6162
6402
  runConversations.delete(latestRunId);
@@ -6418,7 +6658,8 @@ var createRequestHandler = async (options) => {
6418
6658
  tenantId: conversation.tenantId,
6419
6659
  createdAt: conversation.createdAt,
6420
6660
  updatedAt: conversation.updatedAt,
6421
- messageCount: conversation.messages.length
6661
+ messageCount: conversation.messages.length,
6662
+ hasPendingApprovals: Array.isArray(conversation.pendingApprovals) && conversation.pendingApprovals.length > 0
6422
6663
  }))
6423
6664
  });
6424
6665
  return;
@@ -6442,36 +6683,77 @@ var createRequestHandler = async (options) => {
6442
6683
  const approvalMatch = pathname.match(/^\/api\/approvals\/([^/]+)$/);
6443
6684
  if (approvalMatch && request.method === "POST") {
6444
6685
  const approvalId = decodeURIComponent(approvalMatch[1] ?? "");
6445
- const pending = pendingApprovals.get(approvalId);
6446
- if (!pending || pending.ownerId !== ownerId) {
6447
- const conversations = await conversationStore.list(ownerId);
6448
- let prunedStale = false;
6449
- for (const conversation of conversations) {
6450
- if (!Array.isArray(conversation.pendingApprovals)) {
6451
- continue;
6452
- }
6453
- const next = conversation.pendingApprovals.filter(
6454
- (approval) => approval.approvalId !== approvalId
6455
- );
6456
- if (next.length !== conversation.pendingApprovals.length) {
6457
- conversation.pendingApprovals = next;
6458
- await conversationStore.update(conversation);
6459
- prunedStale = true;
6460
- }
6686
+ const body = await readRequestBody(request);
6687
+ const approved = body.approved === true;
6688
+ const conversations = await conversationStore.list(ownerId);
6689
+ let foundConversation;
6690
+ let foundApproval;
6691
+ for (const conv of conversations) {
6692
+ if (!Array.isArray(conv.pendingApprovals)) continue;
6693
+ const match = conv.pendingApprovals.find((a) => a.approvalId === approvalId);
6694
+ if (match) {
6695
+ foundConversation = conv;
6696
+ foundApproval = match;
6697
+ break;
6461
6698
  }
6699
+ }
6700
+ if (!foundConversation || !foundApproval) {
6462
6701
  writeJson(response, 404, {
6463
6702
  code: "APPROVAL_NOT_FOUND",
6464
- message: prunedStale ? "Approval request is no longer active" : "Approval request not found"
6703
+ message: "Approval request not found"
6465
6704
  });
6466
6705
  return;
6467
6706
  }
6468
- const body = await readRequestBody(request);
6469
- const approved = body.approved === true;
6470
- pendingApprovals.delete(approvalId);
6471
- if (pending.conversationId) {
6472
- await persistConversationPendingApprovals(pending.conversationId);
6707
+ const conversationId = foundConversation.conversationId;
6708
+ if (!foundApproval.checkpointMessages || !foundApproval.toolCallId) {
6709
+ foundConversation.pendingApprovals = (foundConversation.pendingApprovals ?? []).filter((a) => a.approvalId !== approvalId);
6710
+ await conversationStore.update(foundConversation);
6711
+ writeJson(response, 404, {
6712
+ code: "APPROVAL_NOT_FOUND",
6713
+ message: "Approval request is no longer active (no checkpoint data)"
6714
+ });
6715
+ return;
6473
6716
  }
6474
- pending.resolve(approved);
6717
+ foundConversation.pendingApprovals = (foundConversation.pendingApprovals ?? []).filter((a) => a.approvalId !== approvalId);
6718
+ await conversationStore.update(foundConversation);
6719
+ broadcastEvent(
6720
+ conversationId,
6721
+ approved ? { type: "tool:approval:granted", approvalId } : { type: "tool:approval:denied", approvalId }
6722
+ );
6723
+ void (async () => {
6724
+ let toolResults;
6725
+ if (approved) {
6726
+ const toolContext = {
6727
+ runId: foundApproval.runId,
6728
+ agentId: identity.id,
6729
+ step: 0,
6730
+ workingDir,
6731
+ parameters: {}
6732
+ };
6733
+ const execResults = await harness.executeTools(
6734
+ [{ id: foundApproval.toolCallId, name: foundApproval.tool, input: foundApproval.input }],
6735
+ toolContext
6736
+ );
6737
+ toolResults = execResults.map((r) => ({
6738
+ callId: r.callId,
6739
+ toolName: r.tool,
6740
+ result: r.output,
6741
+ error: r.error
6742
+ }));
6743
+ } else {
6744
+ toolResults = [{
6745
+ callId: foundApproval.toolCallId,
6746
+ toolName: foundApproval.tool,
6747
+ error: "Tool execution denied by user"
6748
+ }];
6749
+ }
6750
+ await resumeRunFromCheckpoint(
6751
+ conversationId,
6752
+ foundConversation,
6753
+ foundApproval,
6754
+ toolResults
6755
+ );
6756
+ })();
6475
6757
  writeJson(response, 200, { ok: true, approvalId, approved });
6476
6758
  return;
6477
6759
  }
@@ -6533,30 +6815,18 @@ var createRequestHandler = async (options) => {
6533
6815
  return;
6534
6816
  }
6535
6817
  if (request.method === "GET") {
6536
- const storedPending = Array.isArray(conversation.pendingApprovals) ? conversation.pendingApprovals : [];
6537
- const livePending = Array.from(pendingApprovals.entries()).filter(
6538
- ([, pending]) => pending.ownerId === ownerId && pending.conversationId === conversationId
6539
- ).map(([approvalId, pending]) => ({
6540
- approvalId,
6541
- runId: pending.runId,
6542
- tool: pending.tool,
6543
- input: pending.input
6544
- }));
6545
- const mergedPendingById = /* @__PURE__ */ new Map();
6546
- for (const approval of storedPending) {
6547
- if (approval && typeof approval.approvalId === "string") {
6548
- mergedPendingById.set(approval.approvalId, approval);
6549
- }
6550
- }
6551
- for (const approval of livePending) {
6552
- mergedPendingById.set(approval.approvalId, approval);
6553
- }
6818
+ const storedPending = Array.isArray(conversation.pendingApprovals) ? conversation.pendingApprovals.map((a) => ({
6819
+ approvalId: a.approvalId,
6820
+ runId: a.runId,
6821
+ tool: a.tool,
6822
+ input: a.input
6823
+ })) : [];
6554
6824
  const activeStream = conversationEventStreams.get(conversationId);
6555
6825
  const hasActiveRun = !!activeStream && !activeStream.finished;
6556
6826
  writeJson(response, 200, {
6557
6827
  conversation: {
6558
6828
  ...conversation,
6559
- pendingApprovals: Array.from(mergedPendingById.values())
6829
+ pendingApprovals: storedPending
6560
6830
  },
6561
6831
  hasActiveRun
6562
6832
  });
@@ -6740,6 +7010,7 @@ var createRequestHandler = async (options) => {
6740
7010
  let currentText = "";
6741
7011
  let currentTools = [];
6742
7012
  let runCancelled = false;
7013
+ let checkpointedRun = false;
6743
7014
  let userContent = messageText;
6744
7015
  if (files.length > 0) {
6745
7016
  try {
@@ -6877,17 +7148,36 @@ var createRequestHandler = async (options) => {
6877
7148
  currentTools.push(toolText);
6878
7149
  await persistDraftAssistantTurn();
6879
7150
  }
6880
- if (event.type === "tool:approval:granted") {
6881
- const toolText = `- approval granted (${event.approvalId})`;
6882
- toolTimeline.push(toolText);
6883
- currentTools.push(toolText);
6884
- await persistDraftAssistantTurn();
6885
- }
6886
- if (event.type === "tool:approval:denied") {
6887
- const toolText = `- approval denied (${event.approvalId})`;
6888
- toolTimeline.push(toolText);
6889
- currentTools.push(toolText);
6890
- await persistDraftAssistantTurn();
7151
+ if (event.type === "tool:approval:checkpoint") {
7152
+ const checkpointSections = [...sections];
7153
+ if (currentTools.length > 0) {
7154
+ checkpointSections.push({ type: "tools", content: [...currentTools] });
7155
+ }
7156
+ if (currentText.length > 0) {
7157
+ checkpointSections.push({ type: "text", content: currentText });
7158
+ }
7159
+ conversation.messages = [
7160
+ ...historyMessages,
7161
+ { role: "user", content: userContent },
7162
+ ...assistantResponse.length > 0 || toolTimeline.length > 0 || checkpointSections.length > 0 ? [{
7163
+ role: "assistant",
7164
+ content: assistantResponse,
7165
+ metadata: toolTimeline.length > 0 || checkpointSections.length > 0 ? { toolActivity: [...toolTimeline], sections: checkpointSections.length > 0 ? checkpointSections : void 0 } : void 0
7166
+ }] : []
7167
+ ];
7168
+ conversation.pendingApprovals = [{
7169
+ approvalId: event.approvalId,
7170
+ runId: latestRunId,
7171
+ tool: event.tool,
7172
+ toolCallId: event.toolCallId,
7173
+ input: event.input,
7174
+ checkpointMessages: event.checkpointMessages,
7175
+ baseMessageCount: historyMessages.length,
7176
+ pendingToolCalls: event.pendingToolCalls
7177
+ }];
7178
+ conversation.updatedAt = Date.now();
7179
+ await conversationStore.update(conversation);
7180
+ checkpointedRun = true;
6891
7181
  }
6892
7182
  if (event.type === "run:completed" && assistantResponse.length === 0 && event.result.response) {
6893
7183
  assistantResponse = event.result.response;
@@ -6905,23 +7195,25 @@ var createRequestHandler = async (options) => {
6905
7195
  if (currentText.length > 0) {
6906
7196
  sections.push({ type: "text", content: currentText });
6907
7197
  }
6908
- const hasAssistantContent = assistantResponse.length > 0 || toolTimeline.length > 0 || sections.length > 0;
6909
- conversation.messages = hasAssistantContent ? [
6910
- ...historyMessages,
6911
- { role: "user", content: userContent },
6912
- {
6913
- role: "assistant",
6914
- content: assistantResponse,
6915
- metadata: toolTimeline.length > 0 || sections.length > 0 ? {
6916
- toolActivity: toolTimeline,
6917
- sections: sections.length > 0 ? sections : void 0
6918
- } : void 0
6919
- }
6920
- ] : [...historyMessages, { role: "user", content: userContent }];
6921
- conversation.runtimeRunId = latestRunId || conversation.runtimeRunId;
6922
- conversation.pendingApprovals = [];
6923
- conversation.updatedAt = Date.now();
6924
- await conversationStore.update(conversation);
7198
+ if (!checkpointedRun) {
7199
+ const hasAssistantContent = assistantResponse.length > 0 || toolTimeline.length > 0 || sections.length > 0;
7200
+ conversation.messages = hasAssistantContent ? [
7201
+ ...historyMessages,
7202
+ { role: "user", content: userContent },
7203
+ {
7204
+ role: "assistant",
7205
+ content: assistantResponse,
7206
+ metadata: toolTimeline.length > 0 || sections.length > 0 ? {
7207
+ toolActivity: toolTimeline,
7208
+ sections: sections.length > 0 ? sections : void 0
7209
+ } : void 0
7210
+ }
7211
+ ] : [...historyMessages, { role: "user", content: userContent }];
7212
+ conversation.runtimeRunId = latestRunId || conversation.runtimeRunId;
7213
+ conversation.pendingApprovals = [];
7214
+ conversation.updatedAt = Date.now();
7215
+ await conversationStore.update(conversation);
7216
+ }
6925
7217
  } catch (error) {
6926
7218
  if (abortController.signal.aborted || runCancelled) {
6927
7219
  const fallbackSections = [...sections];
@@ -6992,7 +7284,6 @@ var createRequestHandler = async (options) => {
6992
7284
  activeConversationRuns.delete(conversationId);
6993
7285
  }
6994
7286
  finishConversationStream(conversationId);
6995
- await persistConversationPendingApprovals(conversationId);
6996
7287
  if (latestRunId) {
6997
7288
  runOwners.delete(latestRunId);
6998
7289
  runConversations.delete(latestRunId);
@@ -7391,33 +7682,16 @@ Error: ${event.error.message}
7391
7682
  var runInteractive = async (workingDir, params) => {
7392
7683
  dotenv.config({ path: resolve3(workingDir, ".env") });
7393
7684
  const config = await loadPonchoConfig(workingDir);
7394
- let pendingApproval = null;
7395
- let onApprovalRequest = null;
7396
- const approvalHandler = async (request) => {
7397
- return new Promise((resolveApproval) => {
7398
- const req = {
7399
- tool: request.tool,
7400
- input: request.input,
7401
- approvalId: request.approvalId,
7402
- resolve: resolveApproval
7403
- };
7404
- pendingApproval = req;
7405
- if (onApprovalRequest) {
7406
- onApprovalRequest(req);
7407
- }
7408
- });
7409
- };
7410
7685
  const uploadStore = await createUploadStore(config?.uploads, workingDir);
7411
7686
  const harness = new AgentHarness({
7412
7687
  workingDir,
7413
7688
  environment: resolveHarnessEnvironment(),
7414
- approvalHandler,
7415
7689
  uploadStore
7416
7690
  });
7417
7691
  await harness.initialize();
7418
7692
  const identity = await ensureAgentIdentity2(workingDir);
7419
7693
  try {
7420
- const { runInteractiveInk } = await import("./run-interactive-ink-SLWDVTDX.js");
7694
+ const { runInteractiveInk } = await import("./run-interactive-ink-7ULE5JJI.js");
7421
7695
  await runInteractiveInk({
7422
7696
  harness,
7423
7697
  params,
@@ -7426,13 +7700,7 @@ var runInteractive = async (workingDir, params) => {
7426
7700
  conversationStore: createConversationStore(resolveStateConfig(config), {
7427
7701
  workingDir,
7428
7702
  agentId: identity.id
7429
- }),
7430
- onSetApprovalCallback: (cb) => {
7431
- onApprovalRequest = cb;
7432
- if (pendingApproval) {
7433
- cb(pendingApproval);
7434
- }
7435
- }
7703
+ })
7436
7704
  });
7437
7705
  } finally {
7438
7706
  await harness.shutdown();