@poncho-ai/cli 0.36.8 → 0.37.0
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 +5 -5
- package/CHANGELOG.md +27 -0
- package/dist/{chunk-GG6IZSWT.js → chunk-GUGBKAIM.js} +34 -17
- package/dist/cli.js +1 -1
- package/dist/index.js +1 -1
- package/dist/{run-interactive-ink-3OIMW3XL.js → run-interactive-ink-75GKYSEC.js} +1 -1
- package/package.json +2 -2
- package/src/index.ts +34 -8
- package/src/web-ui-client.ts +8 -7
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @poncho-ai/cli@0.
|
|
2
|
+
> @poncho-ai/cli@0.37.0 build /home/runner/work/poncho-ai/poncho-ai/packages/cli
|
|
3
3
|
> tsup src/index.ts src/cli.ts --format esm --dts
|
|
4
4
|
|
|
5
5
|
[34mCLI[39m Building entry: src/cli.ts, src/index.ts
|
|
@@ -8,11 +8,11 @@
|
|
|
8
8
|
[34mCLI[39m Target: es2022
|
|
9
9
|
[34mESM[39m Build start
|
|
10
10
|
[32mESM[39m [1mdist/cli.js [22m[32m94.00 B[39m
|
|
11
|
-
[32mESM[39m [1mdist/run-interactive-ink-3OIMW3XL.js [22m[32m56.86 KB[39m
|
|
12
11
|
[32mESM[39m [1mdist/index.js [22m[32m917.00 B[39m
|
|
13
|
-
[32mESM[39m [1mdist/
|
|
14
|
-
[32mESM[39m
|
|
12
|
+
[32mESM[39m [1mdist/run-interactive-ink-75GKYSEC.js [22m[32m56.86 KB[39m
|
|
13
|
+
[32mESM[39m [1mdist/chunk-GUGBKAIM.js [22m[32m572.60 KB[39m
|
|
14
|
+
[32mESM[39m ⚡️ Build success in 68ms
|
|
15
15
|
[34mDTS[39m Build start
|
|
16
|
-
[32mDTS[39m ⚡️ Build success in
|
|
16
|
+
[32mDTS[39m ⚡️ Build success in 4119ms
|
|
17
17
|
[32mDTS[39m [1mdist/cli.d.ts [22m[32m20.00 B[39m
|
|
18
18
|
[32mDTS[39m [1mdist/index.d.ts [22m[32m7.07 KB[39m
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
# @poncho-ai/cli
|
|
2
2
|
|
|
3
|
+
## 0.37.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [`620a0c8`](https://github.com/cesr/poncho-ai/commit/620a0c89efaafce28968fca5cbde2e2b19bd1595) Thanks [@cesr](https://github.com/cesr)! - feat: add recurrent reminders (daily, weekly, monthly, cron)
|
|
8
|
+
|
|
9
|
+
The `set_reminder` tool now accepts an optional `recurrence` parameter that makes reminders repeat on a schedule instead of firing once. Supports daily, weekly (with specific days-of-week), monthly, and cron expressions. Recurring reminders are rescheduled after each firing and can be bounded by `maxOccurrences` or `endsAt`. Cancel a recurring reminder to stop all future occurrences.
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- [`6486de2`](https://github.com/cesr/poncho-ai/commit/6486de2242a2976068e4bd09f7c0f2d978c35c96) Thanks [@cesr](https://github.com/cesr)! - fix: persist subagent `parentConversationId` atomically so children never appear top-level in the sidebar.
|
|
14
|
+
|
|
15
|
+
`SubagentManager.spawn` previously did a two-step write: `conversationStore.create(...)` followed by `conversationStore.update(...)` to attach `parentConversationId`, `subagentMeta`, and the initial user message. If the follow-up update was interrupted (serverless timeout, transient DB error), the child row was left in the database with `parent_conversation_id = NULL`, so it slipped past the `!c.parentConversationId` filter on `/api/conversations` and showed up as a top-level conversation. This was especially visible with cron-driven research subagents.
|
|
16
|
+
|
|
17
|
+
`ConversationStore.create` now accepts an optional `init` bag (`parentConversationId`, `subagentMeta`, `messages`, `channelMeta`) that is written in the single INSERT — both into the `data` blob and into the dedicated `parent_conversation_id` column. `spawn` passes those fields through and drops the redundant update, eliminating the orphan window. All existing `create(ownerId, title, tenantId)` callers keep working since `init` is optional.
|
|
18
|
+
|
|
19
|
+
- Updated dependencies [[`6486de2`](https://github.com/cesr/poncho-ai/commit/6486de2242a2976068e4bd09f7c0f2d978c35c96), [`0d0578f`](https://github.com/cesr/poncho-ai/commit/0d0578fbc97a3d2644c4e22cab14ff02a79f805f), [`620a0c8`](https://github.com/cesr/poncho-ai/commit/620a0c89efaafce28968fca5cbde2e2b19bd1595)]:
|
|
20
|
+
- @poncho-ai/harness@0.38.0
|
|
21
|
+
|
|
22
|
+
## 0.36.9
|
|
23
|
+
|
|
24
|
+
### Patch Changes
|
|
25
|
+
|
|
26
|
+
- [`af5b449`](https://github.com/cesr/poncho-ai/commit/af5b449b46c9994b5b7335c5d64e4c66d5d8f3d8) Thanks [@cesr](https://github.com/cesr)! - perf(web-ui): parallelize conversation and todos fetches when selecting a conversation.
|
|
27
|
+
|
|
28
|
+
Selecting a conversation in the sidebar previously issued `/api/conversations/:id` and `/api/conversations/:id/todos` sequentially, so the todos round-trip was paid on top of the (usually larger) conversation round-trip. Todos only needs the conversation id, so both requests now fire in parallel and the todos response is awaited just before the todo panel renders. The result is roughly one RTT shaved off every sidebar click, which is very noticeable on non-local connections.
|
|
29
|
+
|
|
3
30
|
## 0.36.8
|
|
4
31
|
|
|
5
32
|
### Patch Changes
|
|
@@ -22,7 +22,8 @@ import {
|
|
|
22
22
|
parseAgentMarkdown,
|
|
23
23
|
resolveStateConfig,
|
|
24
24
|
verifyTenantToken,
|
|
25
|
-
createSecretsStore
|
|
25
|
+
createSecretsStore,
|
|
26
|
+
computeNextOccurrence
|
|
26
27
|
} from "@poncho-ai/harness";
|
|
27
28
|
import { getTextContent } from "@poncho-ai/sdk";
|
|
28
29
|
import {
|
|
@@ -3505,7 +3506,12 @@ var getWebUiClientScript = (markedSource2) => `
|
|
|
3505
3506
|
|
|
3506
3507
|
const loadConversation = async (conversationId) => {
|
|
3507
3508
|
if (window._resetBrowserPanel) window._resetBrowserPanel();
|
|
3508
|
-
|
|
3509
|
+
// Kick off conversation + todos fetches in parallel \u2014 todos only needs
|
|
3510
|
+
// the id, so there's no reason to wait for the conversation response.
|
|
3511
|
+
const conversationPromise = api("/api/conversations/" + encodeURIComponent(conversationId));
|
|
3512
|
+
const todosPromise = api("/api/conversations/" + encodeURIComponent(conversationId) + "/todos")
|
|
3513
|
+
.catch(() => ({ todos: [] }));
|
|
3514
|
+
const payload = await conversationPromise;
|
|
3509
3515
|
elements.chatTitle.textContent = payload.conversation.title;
|
|
3510
3516
|
// Merge own pending approvals + subagent pending approvals
|
|
3511
3517
|
var allPendingApprovals = [].concat(
|
|
@@ -3553,12 +3559,8 @@ var getWebUiClientScript = (markedSource2) => `
|
|
|
3553
3559
|
loadSubagents(subagentParentId);
|
|
3554
3560
|
}
|
|
3555
3561
|
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
state.todos = todosPayload.todos || [];
|
|
3559
|
-
} catch (_e) {
|
|
3560
|
-
state.todos = [];
|
|
3561
|
-
}
|
|
3562
|
+
const todosPayload = await todosPromise;
|
|
3563
|
+
state.todos = todosPayload.todos || [];
|
|
3562
3564
|
_autoCollapseTodos();
|
|
3563
3565
|
renderTodoPanel();
|
|
3564
3566
|
|
|
@@ -10611,12 +10613,16 @@ ${resultBody}`,
|
|
|
10611
10613
|
if (getRunningSubagentCountForParent(opts.parentConversationId) >= MAX_CONCURRENT_SUBAGENTS) {
|
|
10612
10614
|
throw new Error(`Maximum concurrent subagents (${MAX_CONCURRENT_SUBAGENTS}) per parent reached. Wait for running subagents to complete or stop some first.`);
|
|
10613
10615
|
}
|
|
10614
|
-
const conversation = await conversationStore.create(
|
|
10615
|
-
|
|
10616
|
-
|
|
10617
|
-
|
|
10618
|
-
|
|
10619
|
-
|
|
10616
|
+
const conversation = await conversationStore.create(
|
|
10617
|
+
opts.ownerId,
|
|
10618
|
+
opts.task.slice(0, 80),
|
|
10619
|
+
opts.tenantId ?? null,
|
|
10620
|
+
{
|
|
10621
|
+
parentConversationId: opts.parentConversationId,
|
|
10622
|
+
subagentMeta: { task: opts.task, status: "running" },
|
|
10623
|
+
messages: [{ role: "user", content: opts.task }]
|
|
10624
|
+
}
|
|
10625
|
+
);
|
|
10620
10626
|
recentlySpawnedParents.set(
|
|
10621
10627
|
opts.parentConversationId,
|
|
10622
10628
|
(recentlySpawnedParents.get(opts.parentConversationId) ?? 0) + 1
|
|
@@ -13593,13 +13599,24 @@ ${cronJob.task}`;
|
|
|
13593
13599
|
const due = reminders.filter((r) => r.status === "pending" && r.scheduledAt <= cutoff);
|
|
13594
13600
|
for (const reminder of due) {
|
|
13595
13601
|
try {
|
|
13596
|
-
|
|
13602
|
+
const nextScheduledAt = computeNextOccurrence(reminder);
|
|
13603
|
+
if (nextScheduledAt) {
|
|
13604
|
+
await reminderStore.update(reminder.id, {
|
|
13605
|
+
scheduledAt: nextScheduledAt,
|
|
13606
|
+
occurrenceCount: (reminder.occurrenceCount ?? 0) + 1
|
|
13607
|
+
});
|
|
13608
|
+
} else {
|
|
13609
|
+
await reminderStore.delete(reminder.id);
|
|
13610
|
+
}
|
|
13597
13611
|
const originConv = reminder.conversationId ? await conversationStore.get(reminder.conversationId) : void 0;
|
|
13598
13612
|
const channelMeta = originConv?.channelMeta;
|
|
13613
|
+
const isRecurring = !!reminder.recurrence;
|
|
13614
|
+
const recurrenceNote = isRecurring && nextScheduledAt ? `
|
|
13615
|
+
Next occurrence: ${new Date(nextScheduledAt).toISOString()}` : isRecurring ? "\nThis was the final occurrence." : "";
|
|
13599
13616
|
const framedMessage = `[Reminder] A reminder you previously set has fired.
|
|
13600
13617
|
Task: "${reminder.task}"
|
|
13601
13618
|
Originally set at: ${new Date(reminder.createdAt).toISOString()}
|
|
13602
|
-
Scheduled for: ${new Date(reminder.scheduledAt).toISOString()}
|
|
13619
|
+
Scheduled for: ${new Date(reminder.scheduledAt).toISOString()}` + recurrenceNote;
|
|
13603
13620
|
if (channelMeta) {
|
|
13604
13621
|
const adapter = messagingAdapters.get(channelMeta.platform);
|
|
13605
13622
|
if (adapter && originConv) {
|
|
@@ -14047,7 +14064,7 @@ var runInteractive = async (workingDir, params) => {
|
|
|
14047
14064
|
await harness.initialize();
|
|
14048
14065
|
const identity = await ensureAgentIdentity2(workingDir);
|
|
14049
14066
|
try {
|
|
14050
|
-
const { runInteractiveInk } = await import("./run-interactive-ink-
|
|
14067
|
+
const { runInteractiveInk } = await import("./run-interactive-ink-75GKYSEC.js");
|
|
14051
14068
|
await runInteractiveInk({
|
|
14052
14069
|
harness,
|
|
14053
14070
|
params,
|
package/dist/cli.js
CHANGED
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@poncho-ai/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.37.0",
|
|
4
4
|
"description": "CLI for building and deploying AI agents",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"react": "^19.2.4",
|
|
29
29
|
"react-devtools-core": "^6.1.5",
|
|
30
30
|
"yaml": "^2.8.1",
|
|
31
|
-
"@poncho-ai/harness": "0.
|
|
31
|
+
"@poncho-ai/harness": "0.38.0",
|
|
32
32
|
"@poncho-ai/messaging": "0.8.3",
|
|
33
33
|
"@poncho-ai/sdk": "1.8.1"
|
|
34
34
|
},
|
package/src/index.ts
CHANGED
|
@@ -35,6 +35,7 @@ import {
|
|
|
35
35
|
type UploadStore,
|
|
36
36
|
verifyTenantToken,
|
|
37
37
|
createSecretsStore,
|
|
38
|
+
computeNextOccurrence,
|
|
38
39
|
} from "@poncho-ai/harness";
|
|
39
40
|
import type { AgentEvent, FileInput, Message, RunInput } from "@poncho-ai/sdk";
|
|
40
41
|
import { getTextContent } from "@poncho-ai/sdk";
|
|
@@ -3110,12 +3111,19 @@ export const createRequestHandler = async (options?: {
|
|
|
3110
3111
|
throw new Error(`Maximum concurrent subagents (${MAX_CONCURRENT_SUBAGENTS}) per parent reached. Wait for running subagents to complete or stop some first.`);
|
|
3111
3112
|
}
|
|
3112
3113
|
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
conversation
|
|
3117
|
-
|
|
3118
|
-
|
|
3114
|
+
// Atomic create: parent_conversation_id and initial state are persisted
|
|
3115
|
+
// in the single INSERT. This prevents orphaned top-level-looking
|
|
3116
|
+
// subagents if a follow-up update is interrupted.
|
|
3117
|
+
const conversation = await conversationStore.create(
|
|
3118
|
+
opts.ownerId,
|
|
3119
|
+
opts.task.slice(0, 80),
|
|
3120
|
+
opts.tenantId ?? null,
|
|
3121
|
+
{
|
|
3122
|
+
parentConversationId: opts.parentConversationId,
|
|
3123
|
+
subagentMeta: { task: opts.task, status: "running" },
|
|
3124
|
+
messages: [{ role: "user", content: opts.task }],
|
|
3125
|
+
},
|
|
3126
|
+
);
|
|
3119
3127
|
|
|
3120
3128
|
// Register this parent as having a pending spawn BEFORE the async
|
|
3121
3129
|
// runSubagent starts, so hasPendingSubagentWorkForParent sees it
|
|
@@ -6486,18 +6494,36 @@ export const createRequestHandler = async (options?: {
|
|
|
6486
6494
|
|
|
6487
6495
|
for (const reminder of due) {
|
|
6488
6496
|
try {
|
|
6489
|
-
|
|
6497
|
+
// For recurring reminders, compute the next occurrence before any
|
|
6498
|
+
// state changes so we can reschedule. For one-off reminders, delete.
|
|
6499
|
+
const nextScheduledAt = computeNextOccurrence(reminder);
|
|
6500
|
+
if (nextScheduledAt) {
|
|
6501
|
+
await reminderStore.update(reminder.id, {
|
|
6502
|
+
scheduledAt: nextScheduledAt,
|
|
6503
|
+
occurrenceCount: (reminder.occurrenceCount ?? 0) + 1,
|
|
6504
|
+
});
|
|
6505
|
+
} else {
|
|
6506
|
+
await reminderStore.delete(reminder.id);
|
|
6507
|
+
}
|
|
6490
6508
|
|
|
6491
6509
|
const originConv = reminder.conversationId
|
|
6492
6510
|
? await conversationStore.get(reminder.conversationId)
|
|
6493
6511
|
: undefined;
|
|
6494
6512
|
const channelMeta = originConv?.channelMeta;
|
|
6495
6513
|
|
|
6514
|
+
const isRecurring = !!reminder.recurrence;
|
|
6515
|
+
const recurrenceNote = isRecurring && nextScheduledAt
|
|
6516
|
+
? `\nNext occurrence: ${new Date(nextScheduledAt).toISOString()}`
|
|
6517
|
+
: isRecurring
|
|
6518
|
+
? "\nThis was the final occurrence."
|
|
6519
|
+
: "";
|
|
6520
|
+
|
|
6496
6521
|
const framedMessage =
|
|
6497
6522
|
`[Reminder] A reminder you previously set has fired.\n` +
|
|
6498
6523
|
`Task: "${reminder.task}"\n` +
|
|
6499
6524
|
`Originally set at: ${new Date(reminder.createdAt).toISOString()}\n` +
|
|
6500
|
-
`Scheduled for: ${new Date(reminder.scheduledAt).toISOString()}
|
|
6525
|
+
`Scheduled for: ${new Date(reminder.scheduledAt).toISOString()}` +
|
|
6526
|
+
recurrenceNote;
|
|
6501
6527
|
|
|
6502
6528
|
if (channelMeta) {
|
|
6503
6529
|
const adapter = messagingAdapters.get(channelMeta.platform);
|
package/src/web-ui-client.ts
CHANGED
|
@@ -1459,7 +1459,12 @@ export const getWebUiClientScript = (markedSource: string): string => `
|
|
|
1459
1459
|
|
|
1460
1460
|
const loadConversation = async (conversationId) => {
|
|
1461
1461
|
if (window._resetBrowserPanel) window._resetBrowserPanel();
|
|
1462
|
-
|
|
1462
|
+
// Kick off conversation + todos fetches in parallel — todos only needs
|
|
1463
|
+
// the id, so there's no reason to wait for the conversation response.
|
|
1464
|
+
const conversationPromise = api("/api/conversations/" + encodeURIComponent(conversationId));
|
|
1465
|
+
const todosPromise = api("/api/conversations/" + encodeURIComponent(conversationId) + "/todos")
|
|
1466
|
+
.catch(() => ({ todos: [] }));
|
|
1467
|
+
const payload = await conversationPromise;
|
|
1463
1468
|
elements.chatTitle.textContent = payload.conversation.title;
|
|
1464
1469
|
// Merge own pending approvals + subagent pending approvals
|
|
1465
1470
|
var allPendingApprovals = [].concat(
|
|
@@ -1507,12 +1512,8 @@ export const getWebUiClientScript = (markedSource: string): string => `
|
|
|
1507
1512
|
loadSubagents(subagentParentId);
|
|
1508
1513
|
}
|
|
1509
1514
|
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
state.todos = todosPayload.todos || [];
|
|
1513
|
-
} catch (_e) {
|
|
1514
|
-
state.todos = [];
|
|
1515
|
-
}
|
|
1515
|
+
const todosPayload = await todosPromise;
|
|
1516
|
+
state.todos = todosPayload.todos || [];
|
|
1516
1517
|
_autoCollapseTodos();
|
|
1517
1518
|
renderTodoPanel();
|
|
1518
1519
|
|