@poncho-ai/cli 0.29.0 → 0.30.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.
- package/.turbo/turbo-build.log +6 -6
- package/CHANGELOG.md +24 -0
- package/dist/{chunk-3WODYQAB.js → chunk-UYZOJWGL.js} +584 -253
- package/dist/cli.js +1 -1
- package/dist/index.d.ts +10 -0
- package/dist/index.js +1 -1
- package/dist/{run-interactive-ink-V4KWIUHB.js → run-interactive-ink-ZSIGWFLZ.js} +4 -1
- package/package.json +4 -4
- package/src/index.ts +275 -153
- package/src/run-interactive-ink.ts +3 -0
- package/src/web-ui-client.ts +193 -14
- package/src/web-ui-styles.ts +59 -0
|
@@ -442,6 +442,41 @@ var WEB_UI_STYLES = `
|
|
|
442
442
|
.conversation-item .delete-btn.confirming:hover {
|
|
443
443
|
color: var(--error-alt);
|
|
444
444
|
}
|
|
445
|
+
.cron-section-header {
|
|
446
|
+
display: flex;
|
|
447
|
+
align-items: center;
|
|
448
|
+
gap: 6px;
|
|
449
|
+
padding: 8px 10px 4px;
|
|
450
|
+
cursor: pointer;
|
|
451
|
+
font-size: 11px;
|
|
452
|
+
font-weight: 600;
|
|
453
|
+
color: var(--fg-7);
|
|
454
|
+
text-transform: uppercase;
|
|
455
|
+
letter-spacing: 0.04em;
|
|
456
|
+
user-select: none;
|
|
457
|
+
transition: color 0.15s;
|
|
458
|
+
}
|
|
459
|
+
.cron-section-header:hover { color: var(--fg-5); }
|
|
460
|
+
.cron-section-caret {
|
|
461
|
+
display: inline-flex;
|
|
462
|
+
transition: transform 0.15s;
|
|
463
|
+
}
|
|
464
|
+
.cron-section-caret.open { transform: rotate(90deg); }
|
|
465
|
+
.cron-section-count {
|
|
466
|
+
font-weight: 400;
|
|
467
|
+
color: var(--fg-8);
|
|
468
|
+
font-size: 11px;
|
|
469
|
+
}
|
|
470
|
+
.cron-view-more {
|
|
471
|
+
padding: 6px 10px;
|
|
472
|
+
font-size: 12px;
|
|
473
|
+
color: var(--fg-7);
|
|
474
|
+
cursor: pointer;
|
|
475
|
+
text-align: center;
|
|
476
|
+
transition: color 0.15s;
|
|
477
|
+
user-select: none;
|
|
478
|
+
}
|
|
479
|
+
.cron-view-more:hover { color: var(--fg-3); }
|
|
445
480
|
.sidebar-footer {
|
|
446
481
|
margin-top: auto;
|
|
447
482
|
padding-top: 8px;
|
|
@@ -1592,6 +1627,15 @@ var WEB_UI_STYLES = `
|
|
|
1592
1627
|
.subagent-link:hover {
|
|
1593
1628
|
text-decoration: underline;
|
|
1594
1629
|
}
|
|
1630
|
+
.tool-link {
|
|
1631
|
+
color: var(--accent);
|
|
1632
|
+
text-decoration: none;
|
|
1633
|
+
font-size: 11px;
|
|
1634
|
+
margin-left: 4px;
|
|
1635
|
+
}
|
|
1636
|
+
.tool-link:hover {
|
|
1637
|
+
text-decoration: underline;
|
|
1638
|
+
}
|
|
1595
1639
|
.subagent-callback-wrap {
|
|
1596
1640
|
padding: 0;
|
|
1597
1641
|
}
|
|
@@ -1604,6 +1648,8 @@ var WEB_UI_STYLES = `
|
|
|
1604
1648
|
line-height: 1.45;
|
|
1605
1649
|
color: var(--fg-tool-code);
|
|
1606
1650
|
width: 100%;
|
|
1651
|
+
min-width: 0;
|
|
1652
|
+
overflow: hidden;
|
|
1607
1653
|
}
|
|
1608
1654
|
.subagent-result-summary {
|
|
1609
1655
|
list-style: none;
|
|
@@ -1648,6 +1694,19 @@ var WEB_UI_STYLES = `
|
|
|
1648
1694
|
display: grid;
|
|
1649
1695
|
gap: 6px;
|
|
1650
1696
|
padding: 0 12px 10px;
|
|
1697
|
+
min-width: 0;
|
|
1698
|
+
overflow-x: auto;
|
|
1699
|
+
overflow-wrap: break-word;
|
|
1700
|
+
word-break: break-word;
|
|
1701
|
+
}
|
|
1702
|
+
.subagent-result-body pre {
|
|
1703
|
+
max-width: 100%;
|
|
1704
|
+
overflow-x: auto;
|
|
1705
|
+
}
|
|
1706
|
+
.subagent-result-body table {
|
|
1707
|
+
max-width: 100%;
|
|
1708
|
+
overflow-x: auto;
|
|
1709
|
+
display: block;
|
|
1651
1710
|
}
|
|
1652
1711
|
|
|
1653
1712
|
/* Todo panel \u2014 inside composer-inner, above the input shell */
|
|
@@ -1800,6 +1859,8 @@ var getWebUiClientScript = (markedSource2) => `
|
|
|
1800
1859
|
parentConversationId: null,
|
|
1801
1860
|
todos: [],
|
|
1802
1861
|
todoPanelCollapsed: false,
|
|
1862
|
+
cronSectionCollapsed: true,
|
|
1863
|
+
cronShowAll: false,
|
|
1803
1864
|
};
|
|
1804
1865
|
|
|
1805
1866
|
const agentInitial = document.body.dataset.agentInitial || "A";
|
|
@@ -2112,14 +2173,24 @@ var getWebUiClientScript = (markedSource2) => `
|
|
|
2112
2173
|
return "";
|
|
2113
2174
|
}
|
|
2114
2175
|
const subagentLinkRe = /\\s*\\[subagent:([^\\]]+)\\]$/;
|
|
2176
|
+
const toolLinkRe = /\\s*\\[link:(https?:\\/\\/[^\\]]+)\\]$/;
|
|
2115
2177
|
const renderActivityItem = (item) => {
|
|
2116
|
-
const
|
|
2117
|
-
if (
|
|
2178
|
+
const subMatch = item.match(subagentLinkRe);
|
|
2179
|
+
if (subMatch) {
|
|
2118
2180
|
const cleaned = escapeHtml(item.replace(subagentLinkRe, ""));
|
|
2119
|
-
const subId = escapeHtml(
|
|
2181
|
+
const subId = escapeHtml(subMatch[1]);
|
|
2120
2182
|
return '<div class="tool-activity-item">' + cleaned +
|
|
2121
2183
|
' <a class="subagent-link" href="javascript:void(0)" data-subagent-id="' + subId + '">View subagent</a></div>';
|
|
2122
2184
|
}
|
|
2185
|
+
const linkMatch = item.match(toolLinkRe);
|
|
2186
|
+
if (linkMatch) {
|
|
2187
|
+
const cleaned = escapeHtml(item.replace(toolLinkRe, ""));
|
|
2188
|
+
const href = escapeHtml(linkMatch[1]);
|
|
2189
|
+
var displayUrl = linkMatch[1].replace(/^https?:\\/\\//, "");
|
|
2190
|
+
if (displayUrl.length > 55) displayUrl = displayUrl.slice(0, 52) + "...";
|
|
2191
|
+
return '<div class="tool-activity-item">' + cleaned +
|
|
2192
|
+
' <a class="tool-link" href="' + href + '" target="_blank" rel="noopener">' + escapeHtml(displayUrl) + '</a></div>';
|
|
2193
|
+
}
|
|
2123
2194
|
return '<div class="tool-activity-item">' + escapeHtml(item) + "</div>";
|
|
2124
2195
|
};
|
|
2125
2196
|
const chips = hasItems
|
|
@@ -2533,11 +2604,86 @@ var getWebUiClientScript = (markedSource2) => `
|
|
|
2533
2604
|
}
|
|
2534
2605
|
};
|
|
2535
2606
|
|
|
2607
|
+
const cronCaretSvg = '<svg viewBox="0 0 12 12" fill="none" width="10" height="10"><path d="M4.5 2.75L8 6L4.5 9.25" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path></svg>';
|
|
2608
|
+
|
|
2609
|
+
const parseCronTitle = (title) => {
|
|
2610
|
+
const rest = title.replace(/^[cron]s*/, "");
|
|
2611
|
+
const isoMatch = rest.match(/s(d{4}-d{2}-d{2}T[d:.]+Z?)$/);
|
|
2612
|
+
if (isoMatch) {
|
|
2613
|
+
return { jobName: rest.slice(0, isoMatch.index).trim(), timestamp: isoMatch[1] };
|
|
2614
|
+
}
|
|
2615
|
+
return { jobName: rest, timestamp: "" };
|
|
2616
|
+
};
|
|
2617
|
+
|
|
2618
|
+
const formatCronTimestamp = (isoStr) => {
|
|
2619
|
+
if (!isoStr) return "";
|
|
2620
|
+
try {
|
|
2621
|
+
const d = new Date(isoStr);
|
|
2622
|
+
if (isNaN(d.getTime())) return isoStr;
|
|
2623
|
+
return d.toLocaleString(undefined, { month: "short", day: "numeric", hour: "numeric", minute: "2-digit" });
|
|
2624
|
+
} catch { return isoStr; }
|
|
2625
|
+
};
|
|
2626
|
+
|
|
2627
|
+
const CRON_PAGE_SIZE = 20;
|
|
2628
|
+
|
|
2629
|
+
const appendCronSection = (cronConvs, needsDivider) => {
|
|
2630
|
+
if (needsDivider) {
|
|
2631
|
+
const divider = document.createElement("div");
|
|
2632
|
+
divider.className = "sidebar-section-divider";
|
|
2633
|
+
elements.list.appendChild(divider);
|
|
2634
|
+
}
|
|
2635
|
+
|
|
2636
|
+
cronConvs.sort((a, b) => (b.updatedAt || b.createdAt || 0) - (a.updatedAt || a.createdAt || 0));
|
|
2637
|
+
|
|
2638
|
+
const isOpen = !state.cronSectionCollapsed;
|
|
2639
|
+
const header = document.createElement("div");
|
|
2640
|
+
header.className = "cron-section-header";
|
|
2641
|
+
header.innerHTML =
|
|
2642
|
+
'<span class="cron-section-caret' + (isOpen ? ' open' : '') + '">' + cronCaretSvg + '</span>' +
|
|
2643
|
+
'<span>Cron jobs</span>' +
|
|
2644
|
+
'<span class="cron-section-count">' + cronConvs.length + '</span>';
|
|
2645
|
+
header.onclick = () => {
|
|
2646
|
+
state.cronSectionCollapsed = !state.cronSectionCollapsed;
|
|
2647
|
+
state.cronShowAll = false;
|
|
2648
|
+
renderConversationList();
|
|
2649
|
+
};
|
|
2650
|
+
elements.list.appendChild(header);
|
|
2651
|
+
|
|
2652
|
+
if (state.cronSectionCollapsed) return;
|
|
2653
|
+
|
|
2654
|
+
const limit = state.cronShowAll ? cronConvs.length : CRON_PAGE_SIZE;
|
|
2655
|
+
const visible = cronConvs.slice(0, limit);
|
|
2656
|
+
|
|
2657
|
+
for (const c of visible) {
|
|
2658
|
+
const { jobName, timestamp } = parseCronTitle(c.title);
|
|
2659
|
+
const fmtTime = formatCronTimestamp(timestamp);
|
|
2660
|
+
const displayTitle = fmtTime ? jobName + " \\u00b7 " + fmtTime : c.title;
|
|
2661
|
+
elements.list.appendChild(buildConversationItem(Object.assign({}, c, { title: displayTitle })));
|
|
2662
|
+
appendSubagentsIfActive(c.conversationId);
|
|
2663
|
+
}
|
|
2664
|
+
|
|
2665
|
+
if (!state.cronShowAll && cronConvs.length > CRON_PAGE_SIZE) {
|
|
2666
|
+
const remaining = cronConvs.length - CRON_PAGE_SIZE;
|
|
2667
|
+
const viewMore = document.createElement("div");
|
|
2668
|
+
viewMore.className = "cron-view-more";
|
|
2669
|
+
viewMore.textContent = "View " + remaining + " more\\u2026";
|
|
2670
|
+
viewMore.onclick = () => {
|
|
2671
|
+
state.cronShowAll = true;
|
|
2672
|
+
renderConversationList();
|
|
2673
|
+
};
|
|
2674
|
+
elements.list.appendChild(viewMore);
|
|
2675
|
+
}
|
|
2676
|
+
};
|
|
2677
|
+
|
|
2536
2678
|
const renderConversationList = () => {
|
|
2537
2679
|
elements.list.innerHTML = "";
|
|
2538
2680
|
const pending = state.conversations.filter(c => c.hasPendingApprovals);
|
|
2539
2681
|
const rest = state.conversations.filter(c => !c.hasPendingApprovals);
|
|
2540
2682
|
|
|
2683
|
+
const isCron = (c) => c.title && c.title.startsWith("[cron]");
|
|
2684
|
+
const cronConvs = rest.filter(isCron);
|
|
2685
|
+
const nonCron = rest.filter(c => !isCron(c));
|
|
2686
|
+
|
|
2541
2687
|
if (pending.length > 0) {
|
|
2542
2688
|
const label = document.createElement("div");
|
|
2543
2689
|
label.className = "sidebar-section-label";
|
|
@@ -2556,7 +2702,7 @@ var getWebUiClientScript = (markedSource2) => `
|
|
|
2556
2702
|
const latest = [];
|
|
2557
2703
|
const previous7 = [];
|
|
2558
2704
|
const older = [];
|
|
2559
|
-
for (const c of
|
|
2705
|
+
for (const c of nonCron) {
|
|
2560
2706
|
const ts = c.updatedAt || c.createdAt || 0;
|
|
2561
2707
|
if (ts >= startOfToday) {
|
|
2562
2708
|
latest.push(c);
|
|
@@ -2568,6 +2714,12 @@ var getWebUiClientScript = (markedSource2) => `
|
|
|
2568
2714
|
}
|
|
2569
2715
|
|
|
2570
2716
|
let sectionRendered = pending.length > 0;
|
|
2717
|
+
|
|
2718
|
+
if (cronConvs.length > 0) {
|
|
2719
|
+
appendCronSection(cronConvs, sectionRendered);
|
|
2720
|
+
sectionRendered = true;
|
|
2721
|
+
}
|
|
2722
|
+
|
|
2571
2723
|
const appendSection = (items, labelText) => {
|
|
2572
2724
|
if (items.length === 0) return;
|
|
2573
2725
|
if (sectionRendered) {
|
|
@@ -3365,10 +3517,17 @@ var getWebUiClientScript = (markedSource2) => `
|
|
|
3365
3517
|
);
|
|
3366
3518
|
const duration =
|
|
3367
3519
|
typeof payload.duration === "number" ? payload.duration : null;
|
|
3368
|
-
|
|
3520
|
+
var detail =
|
|
3369
3521
|
activeActivity && typeof activeActivity.detail === "string"
|
|
3370
3522
|
? activeActivity.detail.trim()
|
|
3371
3523
|
: "";
|
|
3524
|
+
const out = payload.output && typeof payload.output === "object" ? payload.output : {};
|
|
3525
|
+
if (!detail && toolName === "web_search" && typeof out.query === "string") {
|
|
3526
|
+
detail = "\\x22" + (out.query.length > 60 ? out.query.slice(0, 57) + "..." : out.query) + "\\x22";
|
|
3527
|
+
}
|
|
3528
|
+
if (!detail && toolName === "web_fetch" && typeof out.url === "string") {
|
|
3529
|
+
detail = out.url;
|
|
3530
|
+
}
|
|
3372
3531
|
const meta = [];
|
|
3373
3532
|
if (duration !== null) meta.push(duration + "ms");
|
|
3374
3533
|
if (detail) meta.push(detail);
|
|
@@ -3380,6 +3539,12 @@ var getWebUiClientScript = (markedSource2) => `
|
|
|
3380
3539
|
if (toolName === "spawn_subagent" && payload.output && typeof payload.output === "object" && payload.output.subagentId) {
|
|
3381
3540
|
toolText += " [subagent:" + payload.output.subagentId + "]";
|
|
3382
3541
|
}
|
|
3542
|
+
if (toolName === "web_fetch" && typeof out.url === "string") {
|
|
3543
|
+
toolText += " [link:" + out.url + "]";
|
|
3544
|
+
}
|
|
3545
|
+
if (toolName === "web_search" && Array.isArray(out.results)) {
|
|
3546
|
+
toolText += " \\u2014 " + out.results.length + " result" + (out.results.length !== 1 ? "s" : "");
|
|
3547
|
+
}
|
|
3383
3548
|
assistantMessage._currentTools.push(toolText);
|
|
3384
3549
|
assistantMessage.metadata.toolActivity.push(toolText);
|
|
3385
3550
|
if (typeof payload.outputTokenEstimate === "number" && payload.outputTokenEstimate > 0 && state.contextWindow > 0) {
|
|
@@ -3819,6 +3984,28 @@ var getWebUiClientScript = (markedSource2) => `
|
|
|
3819
3984
|
};
|
|
3820
3985
|
}
|
|
3821
3986
|
|
|
3987
|
+
if (toolName === "web_search") {
|
|
3988
|
+
const query = getStringInputField(input, "query");
|
|
3989
|
+
const short = query && query.length > 60 ? query.slice(0, 57) + "..." : query;
|
|
3990
|
+
return {
|
|
3991
|
+
kind: "tool",
|
|
3992
|
+
tool: toolName,
|
|
3993
|
+
label: "Searching" + (short ? " \\x22" + short + "\\x22" : ""),
|
|
3994
|
+
detail: short ? "\\x22" + short + "\\x22" : "",
|
|
3995
|
+
};
|
|
3996
|
+
}
|
|
3997
|
+
|
|
3998
|
+
if (toolName === "web_fetch") {
|
|
3999
|
+
const url = getStringInputField(input, "url");
|
|
4000
|
+
const short = url && url.length > 60 ? url.slice(0, 57) + "..." : url;
|
|
4001
|
+
return {
|
|
4002
|
+
kind: "tool",
|
|
4003
|
+
tool: toolName,
|
|
4004
|
+
label: "Fetching " + (short || "page"),
|
|
4005
|
+
detail: url || "",
|
|
4006
|
+
};
|
|
4007
|
+
}
|
|
4008
|
+
|
|
3822
4009
|
return {
|
|
3823
4010
|
kind: "tool",
|
|
3824
4011
|
tool: toolName,
|
|
@@ -4206,10 +4393,17 @@ var getWebUiClientScript = (markedSource2) => `
|
|
|
4206
4393
|
toolName,
|
|
4207
4394
|
);
|
|
4208
4395
|
const duration = typeof payload.duration === "number" ? payload.duration : null;
|
|
4209
|
-
|
|
4396
|
+
var detail =
|
|
4210
4397
|
activeActivity && typeof activeActivity.detail === "string"
|
|
4211
4398
|
? activeActivity.detail.trim()
|
|
4212
4399
|
: "";
|
|
4400
|
+
const out = payload.output && typeof payload.output === "object" ? payload.output : {};
|
|
4401
|
+
if (!detail && toolName === "web_search" && typeof out.query === "string") {
|
|
4402
|
+
detail = "\\x22" + (out.query.length > 60 ? out.query.slice(0, 57) + "..." : out.query) + "\\x22";
|
|
4403
|
+
}
|
|
4404
|
+
if (!detail && toolName === "web_fetch" && typeof out.url === "string") {
|
|
4405
|
+
detail = out.url;
|
|
4406
|
+
}
|
|
4213
4407
|
const meta = [];
|
|
4214
4408
|
if (duration !== null) meta.push(duration + "ms");
|
|
4215
4409
|
if (detail) meta.push(detail);
|
|
@@ -4218,6 +4412,12 @@ var getWebUiClientScript = (markedSource2) => `
|
|
|
4218
4412
|
if (toolName === "spawn_subagent" && payload.output && typeof payload.output === "object" && payload.output.subagentId) {
|
|
4219
4413
|
toolText += " [subagent:" + payload.output.subagentId + "]";
|
|
4220
4414
|
}
|
|
4415
|
+
if (toolName === "web_fetch" && typeof out.url === "string") {
|
|
4416
|
+
toolText += " [link:" + out.url + "]";
|
|
4417
|
+
}
|
|
4418
|
+
if (toolName === "web_search" && Array.isArray(out.results)) {
|
|
4419
|
+
toolText += " \\u2014 " + out.results.length + " result" + (out.results.length !== 1 ? "s" : "");
|
|
4420
|
+
}
|
|
4221
4421
|
assistantMessage._currentTools.push(toolText);
|
|
4222
4422
|
if (!assistantMessage.metadata) assistantMessage.metadata = {};
|
|
4223
4423
|
if (!assistantMessage.metadata.toolActivity) assistantMessage.metadata.toolActivity = [];
|
|
@@ -4689,7 +4889,7 @@ var getWebUiClientScript = (markedSource2) => `
|
|
|
4689
4889
|
state: "resolved",
|
|
4690
4890
|
resolvedDecision: decision,
|
|
4691
4891
|
}));
|
|
4692
|
-
api("/api/approvals/" + encodeURIComponent(approvalId), {
|
|
4892
|
+
return api("/api/approvals/" + encodeURIComponent(approvalId), {
|
|
4693
4893
|
method: "POST",
|
|
4694
4894
|
body: JSON.stringify({ approved: decision === "approve" }),
|
|
4695
4895
|
}).catch((error) => {
|
|
@@ -4737,16 +4937,54 @@ var getWebUiClientScript = (markedSource2) => `
|
|
|
4737
4937
|
if (pending.length === 0) return;
|
|
4738
4938
|
const wasStreaming = state.isStreaming;
|
|
4739
4939
|
if (!wasStreaming) setStreaming(true);
|
|
4740
|
-
|
|
4940
|
+
// Mark all items as resolved in the UI immediately
|
|
4941
|
+
for (const aid of pending) {
|
|
4942
|
+
state.approvalRequestsInFlight[aid] = true;
|
|
4943
|
+
updatePendingApproval(aid, (request) => ({
|
|
4944
|
+
...request,
|
|
4945
|
+
state: "resolved",
|
|
4946
|
+
resolvedDecision: decision,
|
|
4947
|
+
}));
|
|
4948
|
+
}
|
|
4741
4949
|
renderMessages(state.activeMessages, state.isStreaming);
|
|
4742
4950
|
loadConversations();
|
|
4743
|
-
|
|
4744
|
-
|
|
4745
|
-
|
|
4746
|
-
|
|
4747
|
-
|
|
4748
|
-
|
|
4951
|
+
const streamCid = !wasStreaming && state.activeConversationId
|
|
4952
|
+
? state.activeConversationId
|
|
4953
|
+
: null;
|
|
4954
|
+
if (streamCid) {
|
|
4955
|
+
streamConversationEvents(streamCid, { liveOnly: true }).finally(() => {
|
|
4956
|
+
if (state.activeConversationId === streamCid) {
|
|
4957
|
+
pollUntilRunIdle(streamCid);
|
|
4958
|
+
}
|
|
4959
|
+
});
|
|
4749
4960
|
}
|
|
4961
|
+
// Send API calls sequentially so each store write completes
|
|
4962
|
+
// before the next read (avoids last-writer-wins in serverless).
|
|
4963
|
+
void (async () => {
|
|
4964
|
+
for (const aid of pending) {
|
|
4965
|
+
await api("/api/approvals/" + encodeURIComponent(aid), {
|
|
4966
|
+
method: "POST",
|
|
4967
|
+
body: JSON.stringify({ approved: decision === "approve" }),
|
|
4968
|
+
}).catch((error) => {
|
|
4969
|
+
const isStale = error && error.payload && error.payload.code === "APPROVAL_NOT_FOUND";
|
|
4970
|
+
if (isStale) {
|
|
4971
|
+
updatePendingApproval(aid, () => null);
|
|
4972
|
+
} else {
|
|
4973
|
+
const errMsg = error instanceof Error ? error.message : String(error);
|
|
4974
|
+
updatePendingApproval(aid, (request) => ({
|
|
4975
|
+
...request,
|
|
4976
|
+
state: "pending",
|
|
4977
|
+
pendingDecision: null,
|
|
4978
|
+
resolvedDecision: null,
|
|
4979
|
+
_error: errMsg,
|
|
4980
|
+
}));
|
|
4981
|
+
}
|
|
4982
|
+
renderMessages(state.activeMessages, state.isStreaming);
|
|
4983
|
+
}).finally(() => {
|
|
4984
|
+
delete state.approvalRequestsInFlight[aid];
|
|
4985
|
+
});
|
|
4986
|
+
}
|
|
4987
|
+
})();
|
|
4750
4988
|
return;
|
|
4751
4989
|
}
|
|
4752
4990
|
|
|
@@ -7291,14 +7529,10 @@ ${name}/
|
|
|
7291
7529
|
\u251C\u2500\u2500 tests/
|
|
7292
7530
|
\u2502 \u2514\u2500\u2500 basic.yaml # Test suite
|
|
7293
7531
|
\u2514\u2500\u2500 skills/
|
|
7294
|
-
\
|
|
7295
|
-
\u2502 \u251C\u2500\u2500 SKILL.md
|
|
7296
|
-
\u2502 \u2514\u2500\u2500 scripts/
|
|
7297
|
-
\u2502 \u2514\u2500\u2500 starter-echo.ts
|
|
7298
|
-
\u2514\u2500\u2500 fetch-page/
|
|
7532
|
+
\u2514\u2500\u2500 starter/
|
|
7299
7533
|
\u251C\u2500\u2500 SKILL.md
|
|
7300
7534
|
\u2514\u2500\u2500 scripts/
|
|
7301
|
-
\u2514\u2500\u2500
|
|
7535
|
+
\u2514\u2500\u2500 starter-echo.ts
|
|
7302
7536
|
\`\`\`
|
|
7303
7537
|
|
|
7304
7538
|
## Cron Jobs
|
|
@@ -7476,67 +7710,6 @@ var SKILL_TOOL_TEMPLATE = `export default async function run(input) {
|
|
|
7476
7710
|
return { echoed: message };
|
|
7477
7711
|
}
|
|
7478
7712
|
`;
|
|
7479
|
-
var FETCH_PAGE_SKILL_TEMPLATE = `---
|
|
7480
|
-
name: fetch-page
|
|
7481
|
-
description: Fetch a web page and return its text content
|
|
7482
|
-
allowed-tools:
|
|
7483
|
-
- ./scripts/fetch-page.ts
|
|
7484
|
-
---
|
|
7485
|
-
|
|
7486
|
-
# Fetch Page
|
|
7487
|
-
|
|
7488
|
-
Fetches a URL and returns the page body as plain text (HTML tags stripped).
|
|
7489
|
-
|
|
7490
|
-
## Usage
|
|
7491
|
-
|
|
7492
|
-
Call \`run_skill_script\` with:
|
|
7493
|
-
- **skill**: \`fetch-page\`
|
|
7494
|
-
- **script**: \`./scripts/fetch-page.ts\`
|
|
7495
|
-
- **input**: \`{ "url": "https://example.com" }\`
|
|
7496
|
-
|
|
7497
|
-
The script returns \`{ url, status, content }\` where \`content\` is the
|
|
7498
|
-
text-only body (capped at ~32 000 chars to stay context-friendly).
|
|
7499
|
-
`;
|
|
7500
|
-
var FETCH_PAGE_SCRIPT_TEMPLATE = `export default async function run(input) {
|
|
7501
|
-
const url = typeof input?.url === "string" ? input.url.trim() : "";
|
|
7502
|
-
if (!url) {
|
|
7503
|
-
return { error: "A \\"url\\" string is required." };
|
|
7504
|
-
}
|
|
7505
|
-
|
|
7506
|
-
const MAX_LENGTH = 32_000;
|
|
7507
|
-
|
|
7508
|
-
const response = await fetch(url, {
|
|
7509
|
-
headers: { "User-Agent": "poncho-fetch-page/1.0" },
|
|
7510
|
-
redirect: "follow",
|
|
7511
|
-
});
|
|
7512
|
-
|
|
7513
|
-
if (!response.ok) {
|
|
7514
|
-
return { url, status: response.status, error: response.statusText };
|
|
7515
|
-
}
|
|
7516
|
-
|
|
7517
|
-
const html = await response.text();
|
|
7518
|
-
|
|
7519
|
-
// Lightweight HTML-to-text: strip tags, collapse whitespace.
|
|
7520
|
-
const text = html
|
|
7521
|
-
.replace(/<script[\\s\\S]*?<\\/script>/gi, "")
|
|
7522
|
-
.replace(/<style[\\s\\S]*?<\\/style>/gi, "")
|
|
7523
|
-
.replace(/<[^>]+>/g, " ")
|
|
7524
|
-
.replace(/ /gi, " ")
|
|
7525
|
-
.replace(/&/gi, "&")
|
|
7526
|
-
.replace(/</gi, "<")
|
|
7527
|
-
.replace(/>/gi, ">")
|
|
7528
|
-
.replace(/"/gi, '"')
|
|
7529
|
-
.replace(/'/gi, "'")
|
|
7530
|
-
.replace(/\\s+/g, " ")
|
|
7531
|
-
.trim();
|
|
7532
|
-
|
|
7533
|
-
const content = text.length > MAX_LENGTH
|
|
7534
|
-
? text.slice(0, MAX_LENGTH) + "\u2026 (truncated)"
|
|
7535
|
-
: text;
|
|
7536
|
-
|
|
7537
|
-
return { url, status: response.status, content };
|
|
7538
|
-
}
|
|
7539
|
-
`;
|
|
7540
7713
|
var ensureFile = async (path, content) => {
|
|
7541
7714
|
await mkdir3(dirname4(path), { recursive: true });
|
|
7542
7715
|
await writeFile3(path, content, { encoding: "utf8", flag: "wx" });
|
|
@@ -8003,9 +8176,7 @@ var initProject = async (projectName, options) => {
|
|
|
8003
8176
|
{ path: ".gitignore", content: GITIGNORE_TEMPLATE },
|
|
8004
8177
|
{ path: "tests/basic.yaml", content: TEST_TEMPLATE },
|
|
8005
8178
|
{ path: "skills/starter/SKILL.md", content: SKILL_TEMPLATE },
|
|
8006
|
-
{ path: "skills/starter/scripts/starter-echo.ts", content: SKILL_TOOL_TEMPLATE }
|
|
8007
|
-
{ path: "skills/fetch-page/SKILL.md", content: FETCH_PAGE_SKILL_TEMPLATE },
|
|
8008
|
-
{ path: "skills/fetch-page/scripts/fetch-page.ts", content: FETCH_PAGE_SCRIPT_TEMPLATE }
|
|
8179
|
+
{ path: "skills/starter/scripts/starter-echo.ts", content: SKILL_TOOL_TEMPLATE }
|
|
8009
8180
|
];
|
|
8010
8181
|
if (onboarding.envFile) {
|
|
8011
8182
|
scaffoldFiles.push({ path: ".env", content: onboarding.envFile });
|
|
@@ -8229,6 +8400,7 @@ data: ${JSON.stringify(statusPayload)}
|
|
|
8229
8400
|
const MAX_CONCURRENT_SUBAGENTS = 5;
|
|
8230
8401
|
const activeSubagentRuns = /* @__PURE__ */ new Map();
|
|
8231
8402
|
const pendingSubagentApprovals = /* @__PURE__ */ new Map();
|
|
8403
|
+
const approvalDecisionTracker = /* @__PURE__ */ new Map();
|
|
8232
8404
|
const getSubagentDepth = async (conversationId) => {
|
|
8233
8405
|
let depth = 0;
|
|
8234
8406
|
let current = await conversationStore.get(conversationId);
|
|
@@ -8363,6 +8535,9 @@ data: ${JSON.stringify(statusPayload)}
|
|
|
8363
8535
|
if (currentTools.length > 0) {
|
|
8364
8536
|
sections.push({ type: "tools", content: currentTools });
|
|
8365
8537
|
currentTools = [];
|
|
8538
|
+
if (assistantResponse.length > 0 && !/\s$/.test(assistantResponse)) {
|
|
8539
|
+
assistantResponse += " ";
|
|
8540
|
+
}
|
|
8366
8541
|
}
|
|
8367
8542
|
assistantResponse += event.content;
|
|
8368
8543
|
currentText += event.content;
|
|
@@ -8478,6 +8653,9 @@ data: ${JSON.stringify(statusPayload)}
|
|
|
8478
8653
|
if (currentTools.length > 0) {
|
|
8479
8654
|
sections.push({ type: "tools", content: currentTools });
|
|
8480
8655
|
currentTools = [];
|
|
8656
|
+
if (assistantResponse.length > 0 && !/\s$/.test(assistantResponse)) {
|
|
8657
|
+
assistantResponse += " ";
|
|
8658
|
+
}
|
|
8481
8659
|
}
|
|
8482
8660
|
assistantResponse += resumeEvent.content;
|
|
8483
8661
|
currentText += resumeEvent.content;
|
|
@@ -8640,8 +8818,10 @@ data: ${JSON.stringify(statusPayload)}
|
|
|
8640
8818
|
}
|
|
8641
8819
|
};
|
|
8642
8820
|
const MAX_SUBAGENT_CALLBACK_COUNT = 20;
|
|
8821
|
+
const pendingCallbackNeeded = /* @__PURE__ */ new Set();
|
|
8643
8822
|
const triggerParentCallback = async (parentConversationId) => {
|
|
8644
8823
|
if (activeConversationRuns.has(parentConversationId)) {
|
|
8824
|
+
pendingCallbackNeeded.add(parentConversationId);
|
|
8645
8825
|
return;
|
|
8646
8826
|
}
|
|
8647
8827
|
if (isServerless) {
|
|
@@ -8653,12 +8833,12 @@ data: ${JSON.stringify(statusPayload)}
|
|
|
8653
8833
|
await processSubagentCallback(parentConversationId);
|
|
8654
8834
|
};
|
|
8655
8835
|
const CALLBACK_LOCK_STALE_MS = 5 * 60 * 1e3;
|
|
8656
|
-
const processSubagentCallback = async (conversationId) => {
|
|
8836
|
+
const processSubagentCallback = async (conversationId, skipLockCheck = false) => {
|
|
8657
8837
|
const conversation = await conversationStore.get(conversationId);
|
|
8658
8838
|
if (!conversation) return;
|
|
8659
8839
|
const pendingResults = conversation.pendingSubagentResults ?? [];
|
|
8660
8840
|
if (pendingResults.length === 0) return;
|
|
8661
|
-
if (conversation.runningCallbackSince) {
|
|
8841
|
+
if (!skipLockCheck && conversation.runningCallbackSince) {
|
|
8662
8842
|
const elapsed = Date.now() - conversation.runningCallbackSince;
|
|
8663
8843
|
if (elapsed < CALLBACK_LOCK_STALE_MS) {
|
|
8664
8844
|
return;
|
|
@@ -8696,11 +8876,24 @@ ${resultBody}`,
|
|
|
8696
8876
|
abortController,
|
|
8697
8877
|
runId: null
|
|
8698
8878
|
});
|
|
8879
|
+
const prevStream = conversationEventStreams.get(conversationId);
|
|
8880
|
+
if (prevStream) {
|
|
8881
|
+
prevStream.finished = false;
|
|
8882
|
+
prevStream.buffer = [];
|
|
8883
|
+
} else {
|
|
8884
|
+
conversationEventStreams.set(conversationId, {
|
|
8885
|
+
buffer: [],
|
|
8886
|
+
subscribers: /* @__PURE__ */ new Set(),
|
|
8887
|
+
finished: false
|
|
8888
|
+
});
|
|
8889
|
+
}
|
|
8699
8890
|
const historyMessages = [...conversation.messages];
|
|
8700
8891
|
let assistantResponse = "";
|
|
8701
8892
|
let latestRunId = "";
|
|
8702
8893
|
let runContinuation = false;
|
|
8703
8894
|
let runContinuationMessages;
|
|
8895
|
+
let runContextTokens = conversation.contextTokens ?? 0;
|
|
8896
|
+
let runContextWindow = conversation.contextWindow ?? 0;
|
|
8704
8897
|
const toolTimeline = [];
|
|
8705
8898
|
const sections = [];
|
|
8706
8899
|
let currentTools = [];
|
|
@@ -8725,6 +8918,9 @@ ${resultBody}`,
|
|
|
8725
8918
|
if (currentTools.length > 0) {
|
|
8726
8919
|
sections.push({ type: "tools", content: currentTools });
|
|
8727
8920
|
currentTools = [];
|
|
8921
|
+
if (assistantResponse.length > 0 && !/\s$/.test(assistantResponse)) {
|
|
8922
|
+
assistantResponse += " ";
|
|
8923
|
+
}
|
|
8728
8924
|
}
|
|
8729
8925
|
assistantResponse += event.content;
|
|
8730
8926
|
currentText += event.content;
|
|
@@ -8752,6 +8948,8 @@ ${resultBody}`,
|
|
|
8752
8948
|
if (assistantResponse.length === 0 && event.result.response) {
|
|
8753
8949
|
assistantResponse = event.result.response;
|
|
8754
8950
|
}
|
|
8951
|
+
runContextTokens = event.result.contextTokens ?? runContextTokens;
|
|
8952
|
+
runContextWindow = event.result.contextWindow ?? runContextWindow;
|
|
8755
8953
|
if (event.result.continuation) {
|
|
8756
8954
|
runContinuation = true;
|
|
8757
8955
|
if (event.result.continuationMessages) {
|
|
@@ -8778,6 +8976,8 @@ ${resultBody}`,
|
|
|
8778
8976
|
}
|
|
8779
8977
|
freshConv.runtimeRunId = latestRunId || freshConv.runtimeRunId;
|
|
8780
8978
|
freshConv.runningCallbackSince = void 0;
|
|
8979
|
+
if (runContextTokens > 0) freshConv.contextTokens = runContextTokens;
|
|
8980
|
+
if (runContextWindow > 0) freshConv.contextWindow = runContextWindow;
|
|
8781
8981
|
freshConv.updatedAt = Date.now();
|
|
8782
8982
|
await conversationStore.update(freshConv);
|
|
8783
8983
|
if (freshConv.channelMeta && assistantResponse.length > 0) {
|
|
@@ -8815,23 +9015,32 @@ ${resultBody}`,
|
|
|
8815
9015
|
} finally {
|
|
8816
9016
|
activeConversationRuns.delete(conversationId);
|
|
8817
9017
|
finishConversationStream(conversationId);
|
|
9018
|
+
const hadDeferredTrigger = pendingCallbackNeeded.delete(conversationId);
|
|
8818
9019
|
const freshConv = await conversationStore.get(conversationId);
|
|
8819
|
-
|
|
8820
|
-
|
|
8821
|
-
freshConv.runningCallbackSince = void 0;
|
|
8822
|
-
await conversationStore.update(freshConv);
|
|
8823
|
-
}
|
|
8824
|
-
}
|
|
8825
|
-
if (freshConv?.pendingSubagentResults?.length) {
|
|
9020
|
+
const hasPendingInStore = !!freshConv?.pendingSubagentResults?.length;
|
|
9021
|
+
if (hadDeferredTrigger || hasPendingInStore) {
|
|
8826
9022
|
if (isServerless) {
|
|
8827
9023
|
selfFetchWithRetry(`/api/internal/conversations/${encodeURIComponent(conversationId)}/subagent-callback`).catch(
|
|
8828
9024
|
(err) => console.error(`[poncho][subagent-callback] Recursive callback self-fetch failed:`, err instanceof Error ? err.message : err)
|
|
8829
9025
|
);
|
|
8830
9026
|
} else {
|
|
8831
|
-
processSubagentCallback(conversationId).catch(
|
|
9027
|
+
processSubagentCallback(conversationId, true).catch(
|
|
8832
9028
|
(err) => console.error(`[poncho][subagent-callback] Recursive callback failed:`, err instanceof Error ? err.message : err)
|
|
8833
9029
|
);
|
|
8834
9030
|
}
|
|
9031
|
+
} else if (freshConv?.runningCallbackSince) {
|
|
9032
|
+
const afterClear = await conversationStore.clearCallbackLock(conversationId);
|
|
9033
|
+
if (afterClear?.pendingSubagentResults?.length) {
|
|
9034
|
+
if (isServerless) {
|
|
9035
|
+
selfFetchWithRetry(`/api/internal/conversations/${encodeURIComponent(conversationId)}/subagent-callback`).catch(
|
|
9036
|
+
(err) => console.error(`[poncho][subagent-callback] Post-clear callback self-fetch failed:`, err instanceof Error ? err.message : err)
|
|
9037
|
+
);
|
|
9038
|
+
} else {
|
|
9039
|
+
processSubagentCallback(conversationId, true).catch(
|
|
9040
|
+
(err) => console.error(`[poncho][subagent-callback] Post-clear callback failed:`, err instanceof Error ? err.message : err)
|
|
9041
|
+
);
|
|
9042
|
+
}
|
|
9043
|
+
}
|
|
8835
9044
|
}
|
|
8836
9045
|
}
|
|
8837
9046
|
};
|
|
@@ -8998,19 +9207,14 @@ ${resultBody}`,
|
|
|
8998
9207
|
if (active && active.abortController === abortController) {
|
|
8999
9208
|
active.runId = event.runId;
|
|
9000
9209
|
}
|
|
9001
|
-
if (typeof event.contextWindow === "number" && event.contextWindow > 0) {
|
|
9002
|
-
runContextWindow = event.contextWindow;
|
|
9003
|
-
}
|
|
9004
|
-
}
|
|
9005
|
-
if (event.type === "model:response") {
|
|
9006
|
-
if (typeof event.usage?.input === "number") {
|
|
9007
|
-
runContextTokens = event.usage.input;
|
|
9008
|
-
}
|
|
9009
9210
|
}
|
|
9010
9211
|
if (event.type === "model:chunk") {
|
|
9011
9212
|
if (currentTools.length > 0) {
|
|
9012
9213
|
sections.push({ type: "tools", content: currentTools });
|
|
9013
9214
|
currentTools = [];
|
|
9215
|
+
if (assistantResponse.length > 0 && !/\s$/.test(assistantResponse)) {
|
|
9216
|
+
assistantResponse += " ";
|
|
9217
|
+
}
|
|
9014
9218
|
}
|
|
9015
9219
|
assistantResponse += event.content;
|
|
9016
9220
|
currentText += event.content;
|
|
@@ -9067,8 +9271,12 @@ ${resultBody}`,
|
|
|
9067
9271
|
}
|
|
9068
9272
|
checkpointedRun = true;
|
|
9069
9273
|
}
|
|
9070
|
-
if (event.type === "run:completed"
|
|
9071
|
-
assistantResponse
|
|
9274
|
+
if (event.type === "run:completed") {
|
|
9275
|
+
if (assistantResponse.length === 0 && event.result.response) {
|
|
9276
|
+
assistantResponse = event.result.response;
|
|
9277
|
+
}
|
|
9278
|
+
runContextTokens = event.result.contextTokens ?? runContextTokens;
|
|
9279
|
+
runContextWindow = event.result.contextWindow ?? runContextWindow;
|
|
9072
9280
|
}
|
|
9073
9281
|
if (event.type === "run:error") {
|
|
9074
9282
|
assistantResponse = assistantResponse || `[Error: ${event.error.message}]`;
|
|
@@ -9151,6 +9359,13 @@ ${resultBody}`,
|
|
|
9151
9359
|
runConversations.delete(latestRunId);
|
|
9152
9360
|
}
|
|
9153
9361
|
console.log("[resume-run] complete for", conversationId);
|
|
9362
|
+
const hadDeferred = pendingCallbackNeeded.delete(conversationId);
|
|
9363
|
+
const postConv = await conversationStore.get(conversationId);
|
|
9364
|
+
if (hadDeferred || postConv?.pendingSubagentResults?.length) {
|
|
9365
|
+
processSubagentCallback(conversationId, true).catch(
|
|
9366
|
+
(err) => console.error(`[poncho][subagent-callback] Post-resume callback failed:`, err instanceof Error ? err.message : err)
|
|
9367
|
+
);
|
|
9368
|
+
}
|
|
9154
9369
|
};
|
|
9155
9370
|
const messagingRoutes = /* @__PURE__ */ new Map();
|
|
9156
9371
|
const messagingRouteRegistrar = (method, path, routeHandler) => {
|
|
@@ -9281,19 +9496,14 @@ ${resultBody}`,
|
|
|
9281
9496
|
latestRunId = event.runId;
|
|
9282
9497
|
runOwners.set(event.runId, "local-owner");
|
|
9283
9498
|
runConversations.set(event.runId, conversationId);
|
|
9284
|
-
if (typeof event.contextWindow === "number" && event.contextWindow > 0) {
|
|
9285
|
-
runContextWindow = event.contextWindow;
|
|
9286
|
-
}
|
|
9287
|
-
}
|
|
9288
|
-
if (event.type === "model:response") {
|
|
9289
|
-
if (typeof event.usage?.input === "number") {
|
|
9290
|
-
runContextTokens = event.usage.input;
|
|
9291
|
-
}
|
|
9292
9499
|
}
|
|
9293
9500
|
if (event.type === "model:chunk") {
|
|
9294
9501
|
if (currentTools.length > 0) {
|
|
9295
9502
|
sections.push({ type: "tools", content: currentTools });
|
|
9296
9503
|
currentTools = [];
|
|
9504
|
+
if (assistantResponse.length > 0 && !/\s$/.test(assistantResponse)) {
|
|
9505
|
+
assistantResponse += " ";
|
|
9506
|
+
}
|
|
9297
9507
|
}
|
|
9298
9508
|
assistantResponse += event.content;
|
|
9299
9509
|
currentText += event.content;
|
|
@@ -9384,6 +9594,8 @@ ${resultBody}`,
|
|
|
9384
9594
|
}
|
|
9385
9595
|
runSteps = event.result.steps;
|
|
9386
9596
|
if (typeof event.result.maxSteps === "number") runMaxSteps = event.result.maxSteps;
|
|
9597
|
+
runContextTokens = event.result.contextTokens ?? runContextTokens;
|
|
9598
|
+
runContextWindow = event.result.contextWindow ?? runContextWindow;
|
|
9387
9599
|
}
|
|
9388
9600
|
if (event.type === "run:error") {
|
|
9389
9601
|
assistantResponse = assistantResponse || `[Error: ${event.error.message}]`;
|
|
@@ -10172,18 +10384,29 @@ data: ${JSON.stringify(frame)}
|
|
|
10172
10384
|
});
|
|
10173
10385
|
return;
|
|
10174
10386
|
}
|
|
10387
|
+
let batchDecisions = approvalDecisionTracker.get(conversationId);
|
|
10388
|
+
if (!batchDecisions) {
|
|
10389
|
+
batchDecisions = /* @__PURE__ */ new Map();
|
|
10390
|
+
approvalDecisionTracker.set(conversationId, batchDecisions);
|
|
10391
|
+
}
|
|
10392
|
+
batchDecisions.set(approvalId, approved);
|
|
10175
10393
|
foundApproval.decision = approved ? "approved" : "denied";
|
|
10176
10394
|
broadcastEvent(
|
|
10177
10395
|
conversationId,
|
|
10178
10396
|
approved ? { type: "tool:approval:granted", approvalId } : { type: "tool:approval:denied", approvalId }
|
|
10179
10397
|
);
|
|
10180
10398
|
const allApprovals = foundConversation.pendingApprovals ?? [];
|
|
10181
|
-
const allDecided = allApprovals.length > 0 && allApprovals.every((a) => a.
|
|
10399
|
+
const allDecided = allApprovals.length > 0 && allApprovals.every((a) => batchDecisions.has(a.approvalId));
|
|
10182
10400
|
if (!allDecided) {
|
|
10183
10401
|
await conversationStore.update(foundConversation);
|
|
10184
10402
|
writeJson(response, 200, { ok: true, approvalId, approved, batchComplete: false });
|
|
10185
10403
|
return;
|
|
10186
10404
|
}
|
|
10405
|
+
for (const a of allApprovals) {
|
|
10406
|
+
const d = batchDecisions.get(a.approvalId);
|
|
10407
|
+
if (d != null) a.decision = d ? "approved" : "denied";
|
|
10408
|
+
}
|
|
10409
|
+
approvalDecisionTracker.delete(conversationId);
|
|
10187
10410
|
foundConversation.pendingApprovals = [];
|
|
10188
10411
|
foundConversation.runStatus = "running";
|
|
10189
10412
|
await conversationStore.update(foundConversation);
|
|
@@ -10805,14 +11028,6 @@ data: ${JSON.stringify(frame)}
|
|
|
10805
11028
|
if (active && active.abortController === abortController) {
|
|
10806
11029
|
active.runId = event.runId;
|
|
10807
11030
|
}
|
|
10808
|
-
if (typeof event.contextWindow === "number" && event.contextWindow > 0) {
|
|
10809
|
-
runContextWindow = event.contextWindow;
|
|
10810
|
-
}
|
|
10811
|
-
}
|
|
10812
|
-
if (event.type === "model:response") {
|
|
10813
|
-
if (typeof event.usage?.input === "number") {
|
|
10814
|
-
runContextTokens = event.usage.input;
|
|
10815
|
-
}
|
|
10816
11031
|
}
|
|
10817
11032
|
if (event.type === "run:cancelled") {
|
|
10818
11033
|
runCancelled = true;
|
|
@@ -10821,6 +11036,9 @@ data: ${JSON.stringify(frame)}
|
|
|
10821
11036
|
if (currentTools.length > 0) {
|
|
10822
11037
|
sections.push({ type: "tools", content: currentTools });
|
|
10823
11038
|
currentTools = [];
|
|
11039
|
+
if (assistantResponse.length > 0 && !/\s$/.test(assistantResponse)) {
|
|
11040
|
+
assistantResponse += " ";
|
|
11041
|
+
}
|
|
10824
11042
|
}
|
|
10825
11043
|
assistantResponse += event.content;
|
|
10826
11044
|
currentText += event.content;
|
|
@@ -10902,6 +11120,8 @@ data: ${JSON.stringify(frame)}
|
|
|
10902
11120
|
if (assistantResponse.length === 0 && event.result.response) {
|
|
10903
11121
|
assistantResponse = event.result.response;
|
|
10904
11122
|
}
|
|
11123
|
+
runContextTokens = event.result.contextTokens ?? runContextTokens;
|
|
11124
|
+
runContextWindow = event.result.contextWindow ?? runContextWindow;
|
|
10905
11125
|
if (event.result.continuation && event.result.continuationMessages) {
|
|
10906
11126
|
runContinuationMessages = event.result.continuationMessages;
|
|
10907
11127
|
conversation._continuationMessages = runContinuationMessages;
|
|
@@ -11041,9 +11261,10 @@ data: ${JSON.stringify(frame)}
|
|
|
11041
11261
|
response.end();
|
|
11042
11262
|
} catch {
|
|
11043
11263
|
}
|
|
11264
|
+
const hadDeferred = pendingCallbackNeeded.delete(conversationId);
|
|
11044
11265
|
const freshConv = await conversationStore.get(conversationId);
|
|
11045
|
-
if (freshConv?.pendingSubagentResults?.length) {
|
|
11046
|
-
processSubagentCallback(conversationId).catch(
|
|
11266
|
+
if (hadDeferred || freshConv?.pendingSubagentResults?.length) {
|
|
11267
|
+
processSubagentCallback(conversationId, true).catch(
|
|
11047
11268
|
(err) => console.error(`[poncho][subagent-callback] Post-run callback failed:`, err instanceof Error ? err.message : err)
|
|
11048
11269
|
);
|
|
11049
11270
|
}
|
|
@@ -11191,122 +11412,157 @@ ${cronJob.task}`;
|
|
|
11191
11412
|
`[cron] ${jobName} ${timestamp}`
|
|
11192
11413
|
);
|
|
11193
11414
|
}
|
|
11194
|
-
const
|
|
11195
|
-
|
|
11196
|
-
|
|
11197
|
-
|
|
11198
|
-
|
|
11199
|
-
|
|
11200
|
-
|
|
11201
|
-
|
|
11202
|
-
|
|
11203
|
-
|
|
11204
|
-
|
|
11205
|
-
|
|
11206
|
-
|
|
11207
|
-
|
|
11208
|
-
|
|
11209
|
-
|
|
11210
|
-
|
|
11211
|
-
|
|
11212
|
-
|
|
11213
|
-
|
|
11214
|
-
|
|
11215
|
-
|
|
11216
|
-
|
|
11217
|
-
|
|
11218
|
-
|
|
11219
|
-
|
|
11220
|
-
|
|
11415
|
+
const convId = conversation.conversationId;
|
|
11416
|
+
activeConversationRuns.set(convId, {
|
|
11417
|
+
ownerId: conversation.ownerId,
|
|
11418
|
+
abortController: new AbortController(),
|
|
11419
|
+
runId: null
|
|
11420
|
+
});
|
|
11421
|
+
try {
|
|
11422
|
+
const abortController = new AbortController();
|
|
11423
|
+
let assistantResponse = "";
|
|
11424
|
+
let latestRunId = "";
|
|
11425
|
+
const toolTimeline = [];
|
|
11426
|
+
const sections = [];
|
|
11427
|
+
let currentTools = [];
|
|
11428
|
+
let currentText = "";
|
|
11429
|
+
let runResult = {
|
|
11430
|
+
status: "completed",
|
|
11431
|
+
steps: 0
|
|
11432
|
+
};
|
|
11433
|
+
const platformMaxDurationSec = Number(process.env.PONCHO_MAX_DURATION) || 0;
|
|
11434
|
+
const softDeadlineMs = platformMaxDurationSec > 0 ? platformMaxDurationSec * 800 : 0;
|
|
11435
|
+
for await (const event of harness.runWithTelemetry({
|
|
11436
|
+
task: cronJob.task,
|
|
11437
|
+
conversationId: convId,
|
|
11438
|
+
parameters: { __activeConversationId: convId },
|
|
11439
|
+
messages: historyMessages,
|
|
11440
|
+
abortSignal: abortController.signal
|
|
11441
|
+
})) {
|
|
11442
|
+
if (event.type === "run:started") {
|
|
11443
|
+
latestRunId = event.runId;
|
|
11221
11444
|
}
|
|
11222
|
-
|
|
11223
|
-
|
|
11224
|
-
|
|
11225
|
-
|
|
11226
|
-
|
|
11227
|
-
|
|
11228
|
-
|
|
11445
|
+
if (event.type === "model:chunk") {
|
|
11446
|
+
if (currentTools.length > 0) {
|
|
11447
|
+
sections.push({ type: "tools", content: currentTools });
|
|
11448
|
+
currentTools = [];
|
|
11449
|
+
if (assistantResponse.length > 0 && !/\s$/.test(assistantResponse)) {
|
|
11450
|
+
assistantResponse += " ";
|
|
11451
|
+
}
|
|
11452
|
+
}
|
|
11453
|
+
assistantResponse += event.content;
|
|
11454
|
+
currentText += event.content;
|
|
11229
11455
|
}
|
|
11230
|
-
|
|
11231
|
-
|
|
11232
|
-
|
|
11456
|
+
if (event.type === "tool:started") {
|
|
11457
|
+
if (currentText.length > 0) {
|
|
11458
|
+
sections.push({ type: "text", content: currentText });
|
|
11459
|
+
currentText = "";
|
|
11460
|
+
}
|
|
11461
|
+
const toolText = `- start \`${event.tool}\``;
|
|
11462
|
+
toolTimeline.push(toolText);
|
|
11463
|
+
currentTools.push(toolText);
|
|
11464
|
+
}
|
|
11465
|
+
if (event.type === "tool:completed") {
|
|
11466
|
+
const toolText = `- done \`${event.tool}\` (${event.duration}ms)`;
|
|
11467
|
+
toolTimeline.push(toolText);
|
|
11468
|
+
currentTools.push(toolText);
|
|
11469
|
+
}
|
|
11470
|
+
if (event.type === "tool:error") {
|
|
11471
|
+
const toolText = `- error \`${event.tool}\`: ${event.error}`;
|
|
11472
|
+
toolTimeline.push(toolText);
|
|
11473
|
+
currentTools.push(toolText);
|
|
11474
|
+
}
|
|
11475
|
+
if (event.type === "run:completed") {
|
|
11476
|
+
runResult = {
|
|
11477
|
+
status: event.result.status,
|
|
11478
|
+
steps: event.result.steps,
|
|
11479
|
+
continuation: event.result.continuation,
|
|
11480
|
+
contextTokens: event.result.contextTokens,
|
|
11481
|
+
contextWindow: event.result.contextWindow
|
|
11482
|
+
};
|
|
11483
|
+
if (!assistantResponse && event.result.response) {
|
|
11484
|
+
assistantResponse = event.result.response;
|
|
11485
|
+
}
|
|
11486
|
+
}
|
|
11487
|
+
broadcastEvent(convId, event);
|
|
11488
|
+
await telemetry.emit(event);
|
|
11233
11489
|
}
|
|
11234
|
-
|
|
11235
|
-
|
|
11236
|
-
|
|
11237
|
-
currentTools.push(toolText);
|
|
11490
|
+
finishConversationStream(convId);
|
|
11491
|
+
if (currentTools.length > 0) {
|
|
11492
|
+
sections.push({ type: "tools", content: currentTools });
|
|
11238
11493
|
}
|
|
11239
|
-
if (
|
|
11240
|
-
|
|
11241
|
-
|
|
11242
|
-
currentTools.push(toolText);
|
|
11494
|
+
if (currentText.length > 0) {
|
|
11495
|
+
sections.push({ type: "text", content: currentText });
|
|
11496
|
+
currentText = "";
|
|
11243
11497
|
}
|
|
11244
|
-
|
|
11245
|
-
|
|
11246
|
-
|
|
11247
|
-
|
|
11248
|
-
|
|
11249
|
-
|
|
11250
|
-
|
|
11251
|
-
|
|
11498
|
+
const hasContent = assistantResponse.length > 0 || toolTimeline.length > 0;
|
|
11499
|
+
const assistantMetadata = toolTimeline.length > 0 || sections.length > 0 ? {
|
|
11500
|
+
toolActivity: [...toolTimeline],
|
|
11501
|
+
sections: sections.length > 0 ? sections : void 0
|
|
11502
|
+
} : void 0;
|
|
11503
|
+
const messages = [
|
|
11504
|
+
...historyMessages,
|
|
11505
|
+
...continueConversationId ? [] : [{ role: "user", content: cronJob.task }],
|
|
11506
|
+
...hasContent ? [{ role: "assistant", content: assistantResponse, metadata: assistantMetadata }] : []
|
|
11507
|
+
];
|
|
11508
|
+
const freshConv = await conversationStore.get(convId);
|
|
11509
|
+
if (freshConv) {
|
|
11510
|
+
freshConv.messages = messages;
|
|
11511
|
+
freshConv.runtimeRunId = latestRunId || freshConv.runtimeRunId;
|
|
11512
|
+
if (runResult.contextTokens) freshConv.contextTokens = runResult.contextTokens;
|
|
11513
|
+
if (runResult.contextWindow) freshConv.contextWindow = runResult.contextWindow;
|
|
11514
|
+
freshConv.updatedAt = Date.now();
|
|
11515
|
+
await conversationStore.update(freshConv);
|
|
11516
|
+
}
|
|
11517
|
+
if (runResult.continuation && softDeadlineMs > 0) {
|
|
11518
|
+
const selfUrl = `http://${request.headers.host ?? "localhost"}${pathname}?continue=${encodeURIComponent(convId)}&continuation=${continuationCount + 1}`;
|
|
11519
|
+
try {
|
|
11520
|
+
const selfRes = await fetch(selfUrl, {
|
|
11521
|
+
method: "GET",
|
|
11522
|
+
headers: request.headers.authorization ? { authorization: request.headers.authorization } : {}
|
|
11523
|
+
});
|
|
11524
|
+
const selfBody = await selfRes.json();
|
|
11525
|
+
writeJson(response, 200, {
|
|
11526
|
+
conversationId: convId,
|
|
11527
|
+
status: "continued",
|
|
11528
|
+
continuations: continuationCount + 1,
|
|
11529
|
+
finalResult: selfBody,
|
|
11530
|
+
duration: Date.now() - start
|
|
11531
|
+
});
|
|
11532
|
+
} catch (continueError) {
|
|
11533
|
+
writeJson(response, 200, {
|
|
11534
|
+
conversationId: convId,
|
|
11535
|
+
status: "continuation_failed",
|
|
11536
|
+
error: continueError instanceof Error ? continueError.message : "Unknown error",
|
|
11537
|
+
duration: Date.now() - start,
|
|
11538
|
+
steps: runResult.steps
|
|
11539
|
+
});
|
|
11252
11540
|
}
|
|
11541
|
+
return;
|
|
11253
11542
|
}
|
|
11254
|
-
|
|
11255
|
-
|
|
11256
|
-
|
|
11257
|
-
|
|
11258
|
-
|
|
11259
|
-
|
|
11260
|
-
|
|
11261
|
-
|
|
11262
|
-
|
|
11263
|
-
|
|
11264
|
-
|
|
11265
|
-
|
|
11266
|
-
|
|
11267
|
-
|
|
11268
|
-
|
|
11269
|
-
|
|
11270
|
-
|
|
11271
|
-
|
|
11272
|
-
|
|
11273
|
-
|
|
11274
|
-
|
|
11275
|
-
conversation.updatedAt = Date.now();
|
|
11276
|
-
await conversationStore.update(conversation);
|
|
11277
|
-
if (runResult.continuation && softDeadlineMs > 0) {
|
|
11278
|
-
const selfUrl = `http://${request.headers.host ?? "localhost"}${pathname}?continue=${encodeURIComponent(conversation.conversationId)}&continuation=${continuationCount + 1}`;
|
|
11279
|
-
try {
|
|
11280
|
-
const selfRes = await fetch(selfUrl, {
|
|
11281
|
-
method: "GET",
|
|
11282
|
-
headers: request.headers.authorization ? { authorization: request.headers.authorization } : {}
|
|
11283
|
-
});
|
|
11284
|
-
const selfBody = await selfRes.json();
|
|
11285
|
-
writeJson(response, 200, {
|
|
11286
|
-
conversationId: conversation.conversationId,
|
|
11287
|
-
status: "continued",
|
|
11288
|
-
continuations: continuationCount + 1,
|
|
11289
|
-
finalResult: selfBody,
|
|
11290
|
-
duration: Date.now() - start
|
|
11291
|
-
});
|
|
11292
|
-
} catch (continueError) {
|
|
11293
|
-
writeJson(response, 200, {
|
|
11294
|
-
conversationId: conversation.conversationId,
|
|
11295
|
-
status: "continuation_failed",
|
|
11296
|
-
error: continueError instanceof Error ? continueError.message : "Unknown error",
|
|
11297
|
-
duration: Date.now() - start,
|
|
11298
|
-
steps: runResult.steps
|
|
11299
|
-
});
|
|
11543
|
+
writeJson(response, 200, {
|
|
11544
|
+
conversationId: convId,
|
|
11545
|
+
status: runResult.status,
|
|
11546
|
+
response: assistantResponse.slice(0, 500),
|
|
11547
|
+
duration: Date.now() - start,
|
|
11548
|
+
steps: runResult.steps
|
|
11549
|
+
});
|
|
11550
|
+
} finally {
|
|
11551
|
+
activeConversationRuns.delete(convId);
|
|
11552
|
+
const hadDeferred = pendingCallbackNeeded.delete(convId);
|
|
11553
|
+
const checkConv = await conversationStore.get(convId);
|
|
11554
|
+
if (hadDeferred || checkConv?.pendingSubagentResults?.length) {
|
|
11555
|
+
if (isServerless) {
|
|
11556
|
+
selfFetchWithRetry(`/api/internal/conversations/${encodeURIComponent(convId)}/subagent-callback`).catch(
|
|
11557
|
+
(err) => console.error(`[cron] subagent callback self-fetch failed:`, err instanceof Error ? err.message : err)
|
|
11558
|
+
);
|
|
11559
|
+
} else {
|
|
11560
|
+
processSubagentCallback(convId, true).catch(
|
|
11561
|
+
(err) => console.error(`[cron] subagent callback failed:`, err instanceof Error ? err.message : err)
|
|
11562
|
+
);
|
|
11563
|
+
}
|
|
11300
11564
|
}
|
|
11301
|
-
return;
|
|
11302
11565
|
}
|
|
11303
|
-
writeJson(response, 200, {
|
|
11304
|
-
conversationId: conversation.conversationId,
|
|
11305
|
-
status: runResult.status,
|
|
11306
|
-
response: assistantResponse.slice(0, 500),
|
|
11307
|
-
duration: Date.now() - start,
|
|
11308
|
-
steps: runResult.steps
|
|
11309
|
-
});
|
|
11310
11566
|
} catch (error) {
|
|
11311
11567
|
writeJson(response, 500, {
|
|
11312
11568
|
code: "CRON_RUN_ERROR",
|
|
@@ -11321,6 +11577,11 @@ ${cronJob.task}`;
|
|
|
11321
11577
|
handler._cronJobs = cronJobs;
|
|
11322
11578
|
handler._conversationStore = conversationStore;
|
|
11323
11579
|
handler._messagingAdapters = messagingAdapters;
|
|
11580
|
+
handler._activeConversationRuns = activeConversationRuns;
|
|
11581
|
+
handler._pendingCallbackNeeded = pendingCallbackNeeded;
|
|
11582
|
+
handler._processSubagentCallback = processSubagentCallback;
|
|
11583
|
+
handler._broadcastEvent = broadcastEvent;
|
|
11584
|
+
handler._finishConversationStream = finishConversationStream;
|
|
11324
11585
|
const STALE_SUBAGENT_THRESHOLD_MS = 5 * 60 * 1e3;
|
|
11325
11586
|
try {
|
|
11326
11587
|
const allSummaries = await conversationStore.listSummaries();
|
|
@@ -11371,9 +11632,11 @@ var startDevServer = async (port, options) => {
|
|
|
11371
11632
|
await checkVercelCronDrift(workingDir);
|
|
11372
11633
|
const { Cron } = await import("croner");
|
|
11373
11634
|
let activeJobs = [];
|
|
11374
|
-
const runCronAgent = async (harnessRef, task, conversationId, historyMessages) => {
|
|
11635
|
+
const runCronAgent = async (harnessRef, task, conversationId, historyMessages, onEvent) => {
|
|
11375
11636
|
let assistantResponse = "";
|
|
11376
11637
|
let steps = 0;
|
|
11638
|
+
let contextTokens = 0;
|
|
11639
|
+
let contextWindow = 0;
|
|
11377
11640
|
const toolTimeline = [];
|
|
11378
11641
|
const sections = [];
|
|
11379
11642
|
let currentTools = [];
|
|
@@ -11384,10 +11647,14 @@ var startDevServer = async (port, options) => {
|
|
|
11384
11647
|
parameters: { __activeConversationId: conversationId },
|
|
11385
11648
|
messages: historyMessages
|
|
11386
11649
|
})) {
|
|
11650
|
+
onEvent?.(event);
|
|
11387
11651
|
if (event.type === "model:chunk") {
|
|
11388
11652
|
if (currentTools.length > 0) {
|
|
11389
11653
|
sections.push({ type: "tools", content: currentTools });
|
|
11390
11654
|
currentTools = [];
|
|
11655
|
+
if (assistantResponse.length > 0 && !/\s$/.test(assistantResponse)) {
|
|
11656
|
+
assistantResponse += " ";
|
|
11657
|
+
}
|
|
11391
11658
|
}
|
|
11392
11659
|
assistantResponse += event.content;
|
|
11393
11660
|
currentText += event.content;
|
|
@@ -11413,6 +11680,8 @@ var startDevServer = async (port, options) => {
|
|
|
11413
11680
|
}
|
|
11414
11681
|
if (event.type === "run:completed") {
|
|
11415
11682
|
steps = event.result.steps;
|
|
11683
|
+
contextTokens = event.result.contextTokens ?? 0;
|
|
11684
|
+
contextWindow = event.result.contextWindow ?? 0;
|
|
11416
11685
|
if (!assistantResponse && event.result.response) {
|
|
11417
11686
|
assistantResponse = event.result.response;
|
|
11418
11687
|
}
|
|
@@ -11429,7 +11698,7 @@ var startDevServer = async (port, options) => {
|
|
|
11429
11698
|
toolActivity: [...toolTimeline],
|
|
11430
11699
|
sections: sections.length > 0 ? sections : void 0
|
|
11431
11700
|
} : void 0;
|
|
11432
|
-
return { response: assistantResponse, steps, assistantMetadata, hasContent };
|
|
11701
|
+
return { response: assistantResponse, steps, assistantMetadata, hasContent, contextTokens, contextWindow };
|
|
11433
11702
|
};
|
|
11434
11703
|
const buildCronMessages = (task, historyMessages, result) => [
|
|
11435
11704
|
...historyMessages,
|
|
@@ -11446,6 +11715,9 @@ var startDevServer = async (port, options) => {
|
|
|
11446
11715
|
const harnessRef = handler._harness;
|
|
11447
11716
|
const store = handler._conversationStore;
|
|
11448
11717
|
const adapters = handler._messagingAdapters;
|
|
11718
|
+
const activeRuns = handler._activeConversationRuns;
|
|
11719
|
+
const deferredCallbacks = handler._pendingCallbackNeeded;
|
|
11720
|
+
const runCallback = handler._processSubagentCallback;
|
|
11449
11721
|
if (!harnessRef || !store) return;
|
|
11450
11722
|
for (const [jobName, config] of entries) {
|
|
11451
11723
|
const job = new Cron(
|
|
@@ -11486,24 +11758,43 @@ var startDevServer = async (port, options) => {
|
|
|
11486
11758
|
const task = `[Scheduled: ${jobName}]
|
|
11487
11759
|
${config.task}`;
|
|
11488
11760
|
const historyMessages = [...conversation.messages];
|
|
11761
|
+
const convId = conversation.conversationId;
|
|
11762
|
+
activeRuns?.set(convId, {
|
|
11763
|
+
ownerId: "local-owner",
|
|
11764
|
+
abortController: new AbortController(),
|
|
11765
|
+
runId: null
|
|
11766
|
+
});
|
|
11489
11767
|
try {
|
|
11490
|
-
const
|
|
11491
|
-
|
|
11492
|
-
|
|
11493
|
-
|
|
11494
|
-
|
|
11495
|
-
|
|
11496
|
-
|
|
11497
|
-
|
|
11498
|
-
|
|
11499
|
-
|
|
11500
|
-
|
|
11501
|
-
|
|
11502
|
-
|
|
11503
|
-
|
|
11504
|
-
|
|
11505
|
-
|
|
11768
|
+
const broadcastCh = handler._broadcastEvent;
|
|
11769
|
+
const result = await runCronAgent(
|
|
11770
|
+
harnessRef,
|
|
11771
|
+
task,
|
|
11772
|
+
convId,
|
|
11773
|
+
historyMessages,
|
|
11774
|
+
broadcastCh ? (ev) => broadcastCh(convId, ev) : void 0
|
|
11775
|
+
);
|
|
11776
|
+
handler._finishConversationStream?.(convId);
|
|
11777
|
+
const freshConv = await store.get(convId);
|
|
11778
|
+
if (freshConv) {
|
|
11779
|
+
freshConv.messages = buildCronMessages(task, historyMessages, result);
|
|
11780
|
+
if (result.contextTokens > 0) freshConv.contextTokens = result.contextTokens;
|
|
11781
|
+
if (result.contextWindow > 0) freshConv.contextWindow = result.contextWindow;
|
|
11782
|
+
freshConv.updatedAt = Date.now();
|
|
11783
|
+
await store.update(freshConv);
|
|
11784
|
+
if (result.response) {
|
|
11785
|
+
try {
|
|
11786
|
+
await adapter.sendReply(
|
|
11787
|
+
{
|
|
11788
|
+
channelId: chatId,
|
|
11789
|
+
platformThreadId: freshConv.channelMeta?.platformThreadId ?? chatId
|
|
11790
|
+
},
|
|
11791
|
+
result.response
|
|
11792
|
+
);
|
|
11793
|
+
} catch (sendError) {
|
|
11794
|
+
const sendMsg = sendError instanceof Error ? sendError.message : String(sendError);
|
|
11795
|
+
process.stderr.write(`[cron] ${jobName}: send to ${chatId} failed: ${sendMsg}
|
|
11506
11796
|
`);
|
|
11797
|
+
}
|
|
11507
11798
|
}
|
|
11508
11799
|
}
|
|
11509
11800
|
totalChats++;
|
|
@@ -11511,6 +11802,15 @@ ${config.task}`;
|
|
|
11511
11802
|
const runMsg = runError instanceof Error ? runError.message : String(runError);
|
|
11512
11803
|
process.stderr.write(`[cron] ${jobName}: run for chat ${chatId} failed: ${runMsg}
|
|
11513
11804
|
`);
|
|
11805
|
+
} finally {
|
|
11806
|
+
activeRuns?.delete(convId);
|
|
11807
|
+
const hadDeferred = deferredCallbacks?.delete(convId) ?? false;
|
|
11808
|
+
const checkConv = await store.get(convId);
|
|
11809
|
+
if (hadDeferred || checkConv?.pendingSubagentResults?.length) {
|
|
11810
|
+
runCallback?.(convId, true).catch(
|
|
11811
|
+
(err) => console.error(`[cron] ${jobName}: subagent callback for ${chatId} failed:`, err instanceof Error ? err.message : err)
|
|
11812
|
+
);
|
|
11813
|
+
}
|
|
11514
11814
|
}
|
|
11515
11815
|
}
|
|
11516
11816
|
const elapsed = ((Date.now() - start) / 1e3).toFixed(1);
|
|
@@ -11524,15 +11824,35 @@ ${config.task}`;
|
|
|
11524
11824
|
}
|
|
11525
11825
|
return;
|
|
11526
11826
|
}
|
|
11827
|
+
let cronConvId;
|
|
11527
11828
|
try {
|
|
11528
11829
|
const conversation = await store.create(
|
|
11529
11830
|
"local-owner",
|
|
11530
11831
|
`[cron] ${jobName} ${timestamp}`
|
|
11531
11832
|
);
|
|
11532
|
-
|
|
11533
|
-
|
|
11534
|
-
|
|
11535
|
-
|
|
11833
|
+
cronConvId = conversation.conversationId;
|
|
11834
|
+
activeRuns?.set(cronConvId, {
|
|
11835
|
+
ownerId: "local-owner",
|
|
11836
|
+
abortController: new AbortController(),
|
|
11837
|
+
runId: null
|
|
11838
|
+
});
|
|
11839
|
+
const broadcast = handler._broadcastEvent;
|
|
11840
|
+
const result = await runCronAgent(
|
|
11841
|
+
harnessRef,
|
|
11842
|
+
config.task,
|
|
11843
|
+
cronConvId,
|
|
11844
|
+
[],
|
|
11845
|
+
broadcast ? (ev) => broadcast(cronConvId, ev) : void 0
|
|
11846
|
+
);
|
|
11847
|
+
handler._finishConversationStream?.(cronConvId);
|
|
11848
|
+
const freshConv = await store.get(cronConvId);
|
|
11849
|
+
if (freshConv) {
|
|
11850
|
+
freshConv.messages = buildCronMessages(config.task, [], result);
|
|
11851
|
+
if (result.contextTokens > 0) freshConv.contextTokens = result.contextTokens;
|
|
11852
|
+
if (result.contextWindow > 0) freshConv.contextWindow = result.contextWindow;
|
|
11853
|
+
freshConv.updatedAt = Date.now();
|
|
11854
|
+
await store.update(freshConv);
|
|
11855
|
+
}
|
|
11536
11856
|
const elapsed = ((Date.now() - start) / 1e3).toFixed(1);
|
|
11537
11857
|
process.stdout.write(
|
|
11538
11858
|
`[cron] ${jobName} completed in ${elapsed}s (${result.steps} steps)
|
|
@@ -11545,6 +11865,17 @@ ${config.task}`;
|
|
|
11545
11865
|
`[cron] ${jobName} failed after ${elapsed}s: ${msg}
|
|
11546
11866
|
`
|
|
11547
11867
|
);
|
|
11868
|
+
} finally {
|
|
11869
|
+
if (cronConvId) {
|
|
11870
|
+
activeRuns?.delete(cronConvId);
|
|
11871
|
+
const hadDeferred = deferredCallbacks?.delete(cronConvId) ?? false;
|
|
11872
|
+
const checkConv = await store.get(cronConvId);
|
|
11873
|
+
if (hadDeferred || checkConv?.pendingSubagentResults?.length) {
|
|
11874
|
+
runCallback?.(cronConvId, true).catch(
|
|
11875
|
+
(err) => console.error(`[cron] ${jobName}: subagent callback failed:`, err instanceof Error ? err.message : err)
|
|
11876
|
+
);
|
|
11877
|
+
}
|
|
11878
|
+
}
|
|
11548
11879
|
}
|
|
11549
11880
|
}
|
|
11550
11881
|
);
|
|
@@ -11652,7 +11983,7 @@ var runInteractive = async (workingDir, params) => {
|
|
|
11652
11983
|
await harness.initialize();
|
|
11653
11984
|
const identity = await ensureAgentIdentity2(workingDir);
|
|
11654
11985
|
try {
|
|
11655
|
-
const { runInteractiveInk } = await import("./run-interactive-ink-
|
|
11986
|
+
const { runInteractiveInk } = await import("./run-interactive-ink-ZSIGWFLZ.js");
|
|
11656
11987
|
await runInteractiveInk({
|
|
11657
11988
|
harness,
|
|
11658
11989
|
params,
|