@poncho-ai/cli 0.33.3 → 0.34.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.
@@ -1,5 +1,5 @@
1
1
 
2
- > @poncho-ai/cli@0.33.3 build /home/runner/work/poncho-ai/poncho-ai/packages/cli
2
+ > @poncho-ai/cli@0.34.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
  CLI Building entry: src/cli.ts, src/index.ts
@@ -7,12 +7,12 @@
7
7
  CLI tsup v8.5.1
8
8
  CLI Target: es2022
9
9
  ESM Build start
10
+ ESM dist/run-interactive-ink-DYCMDTTJ.js 56.86 KB
10
11
  ESM dist/index.js 917.00 B
11
12
  ESM dist/cli.js 94.00 B
12
- ESM dist/run-interactive-ink-XQM7OIZT.js 56.86 KB
13
- ESM dist/chunk-METMUDY6.js 528.87 KB
14
- ESM ⚡️ Build success in 65ms
13
+ ESM dist/chunk-C5XPPHBB.js 530.96 KB
14
+ ESM ⚡️ Build success in 72ms
15
15
  DTS Build start
16
- DTS ⚡️ Build success in 4296ms
16
+ DTS ⚡️ Build success in 4522ms
17
17
  DTS dist/cli.d.ts 20.00 B
18
18
  DTS dist/index.d.ts 7.07 KB
package/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # @poncho-ai/cli
2
2
 
3
+ ## 0.34.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#75](https://github.com/cesr/poncho-ai/pull/75) [`d447d0a`](https://github.com/cesr/poncho-ai/commit/d447d0a3cb77f3d097276b524b5f870dddf1899e) Thanks [@cesr](https://github.com/cesr)! - Add `maxRuns` option to cron jobs for automatic pruning of old conversations, preventing unbounded storage growth on hosted stores.
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies [[`d447d0a`](https://github.com/cesr/poncho-ai/commit/d447d0a3cb77f3d097276b524b5f870dddf1899e)]:
12
+ - @poncho-ai/harness@0.33.0
13
+
14
+ ## 0.33.4
15
+
16
+ ### Patch Changes
17
+
18
+ - [#73](https://github.com/cesr/poncho-ai/pull/73) [`f72f202`](https://github.com/cesr/poncho-ai/commit/f72f202d839dbbb8240336ec76eb6340aba20f06) Thanks [@cesr](https://github.com/cesr)! - Fix Telegram approval message ordering: send accumulated assistant text before approval buttons so the conversation reads naturally. Skip empty bridge replies when text was already sent at checkpoint.
19
+
20
+ - Updated dependencies [[`f72f202`](https://github.com/cesr/poncho-ai/commit/f72f202d839dbbb8240336ec76eb6340aba20f06)]:
21
+ - @poncho-ai/messaging@0.7.10
22
+
3
23
  ## 0.33.3
4
24
 
5
25
  ### Patch Changes
@@ -7902,6 +7902,22 @@ var appendCronTurn = (conv, task, result) => {
7902
7902
  ...result.hasContent ? [{ role: "assistant", content: result.response, metadata: result.assistantMetadata }] : []
7903
7903
  );
7904
7904
  };
7905
+ var MAX_PRUNE_PER_RUN = 25;
7906
+ var pruneCronConversations = async (store, ownerId, jobName, maxRuns) => {
7907
+ const summaries = await store.listSummaries(ownerId);
7908
+ const cronPrefix = `[cron] ${jobName} `;
7909
+ const cronSummaries = summaries.filter((s) => s.title?.startsWith(cronPrefix)).sort((a, b) => (b.updatedAt ?? 0) - (a.updatedAt ?? 0));
7910
+ if (cronSummaries.length <= maxRuns) return 0;
7911
+ const toDelete = cronSummaries.slice(maxRuns, maxRuns + MAX_PRUNE_PER_RUN);
7912
+ let deleted = 0;
7913
+ for (const s of toDelete) {
7914
+ try {
7915
+ if (await store.delete(s.conversationId)) deleted++;
7916
+ } catch {
7917
+ }
7918
+ }
7919
+ return deleted;
7920
+ };
7905
7921
  var AGENT_TEMPLATE = (name, id, options) => `---
7906
7922
  name: ${name}
7907
7923
  id: ${id}
@@ -7936,31 +7952,30 @@ var resolveLocalPackagesRoot = () => {
7936
7952
  }
7937
7953
  return null;
7938
7954
  };
7939
- var resolveCoreDeps = async (projectDir) => {
7955
+ var resolveCliDep = async (projectDir) => {
7940
7956
  const packagesRoot = resolveLocalPackagesRoot();
7941
7957
  if (packagesRoot) {
7942
- const harnessAbs = resolve3(packagesRoot, "harness");
7943
- const sdkAbs = resolve3(packagesRoot, "sdk");
7944
- return {
7945
- harness: `link:${relative(projectDir, harnessAbs)}`,
7946
- sdk: `link:${relative(projectDir, sdkAbs)}`
7947
- };
7958
+ const cliAbs = resolve3(packagesRoot, "cli");
7959
+ return `link:${relative(projectDir, cliAbs)}`;
7948
7960
  }
7949
- return {
7950
- harness: await readCliDependencyVersion("@poncho-ai/harness", "^0.6.0"),
7951
- sdk: await readCliDependencyVersion("@poncho-ai/sdk", "^0.6.0")
7952
- };
7961
+ const version = await readCliVersion();
7962
+ return `^${version}`;
7953
7963
  };
7954
7964
  var PACKAGE_TEMPLATE = async (name, projectDir) => {
7955
- const deps = await resolveCoreDeps(projectDir);
7965
+ const cliDep = await resolveCliDep(projectDir);
7956
7966
  return JSON.stringify(
7957
7967
  {
7958
7968
  name,
7959
7969
  private: true,
7960
7970
  type: "module",
7971
+ scripts: {
7972
+ dev: "poncho dev",
7973
+ start: "poncho dev",
7974
+ test: "poncho test",
7975
+ build: "poncho build"
7976
+ },
7961
7977
  dependencies: {
7962
- "@poncho-ai/harness": deps.harness,
7963
- "@poncho-ai/sdk": deps.sdk
7978
+ "@poncho-ai/cli": cliDep
7964
7979
  }
7965
7980
  },
7966
7981
  null,
@@ -10178,6 +10193,7 @@ ${resultBody}`,
10178
10193
  let latestRunId = "";
10179
10194
  const draft = createTurnDraftState();
10180
10195
  let checkpointedRun = false;
10196
+ let checkpointTextAlreadySent = false;
10181
10197
  let runContextTokens = 0;
10182
10198
  let runContextWindow = 0;
10183
10199
  let runContinuation2 = false;
@@ -10268,6 +10284,19 @@ ${resultBody}`,
10268
10284
  if (conv?.channelMeta?.platform === "telegram") {
10269
10285
  const tgAdapter = messagingAdapters.get("telegram");
10270
10286
  if (tgAdapter) {
10287
+ const threadRef = {
10288
+ channelId: conv.channelMeta.channelId,
10289
+ platformThreadId: conv.channelMeta.platformThreadId
10290
+ };
10291
+ const pendingText = draft.assistantResponse.trim();
10292
+ if (pendingText) {
10293
+ try {
10294
+ await tgAdapter.sendReply(threadRef, pendingText);
10295
+ checkpointTextAlreadySent = true;
10296
+ } catch (err) {
10297
+ console.error("[messaging-runner] failed to send pre-approval text:", err instanceof Error ? err.message : err);
10298
+ }
10299
+ }
10271
10300
  const approvals = event.approvals.map((a) => ({
10272
10301
  approvalId: a.approvalId,
10273
10302
  tool: a.tool,
@@ -10351,8 +10380,8 @@ ${resultBody}`,
10351
10380
  runOwners.delete(latestRunId);
10352
10381
  runConversations.delete(latestRunId);
10353
10382
  }
10354
- console.log("[messaging-runner] run complete, response length:", draft.assistantResponse.length, runContinuation2 ? "(continuation)" : "");
10355
- const response = draft.assistantResponse;
10383
+ const response = checkpointTextAlreadySent ? "" : draft.assistantResponse;
10384
+ console.log("[messaging-runner] run complete, response length:", response.length, checkpointTextAlreadySent ? "(text sent at checkpoint)" : "", runContinuation2 ? "(continuation)" : "");
10356
10385
  return {
10357
10386
  response,
10358
10387
  continuation: runContinuation2,
@@ -12565,6 +12594,18 @@ ${cronJob.task}`;
12565
12594
  });
12566
12595
  await conversationStore.update(freshConv);
12567
12596
  }
12597
+ const pruneWork = pruneCronConversations(
12598
+ conversationStore,
12599
+ cronOwnerId,
12600
+ jobName,
12601
+ cronJob.maxRuns ?? 5
12602
+ ).then((n) => {
12603
+ if (n > 0) process.stdout.write(`[cron] ${jobName}: pruned ${n} old conversation${n === 1 ? "" : "s"}
12604
+ `);
12605
+ }).catch(
12606
+ (err) => console.error(`[cron] ${jobName}: prune failed:`, err instanceof Error ? err.message : err)
12607
+ );
12608
+ doWaitUntil(pruneWork);
12568
12609
  if (result.continuation) {
12569
12610
  const work = selfFetchWithRetry(`/api/internal/continue/${encodeURIComponent(convId)}`).catch(
12570
12611
  (err) => console.error(`[poncho][cron] Continuation self-fetch failed:`, err instanceof Error ? err.message : err)
@@ -12924,6 +12965,12 @@ ${config.task}`;
12924
12965
  });
12925
12966
  await store.update(freshConv);
12926
12967
  }
12968
+ pruneCronConversations(store, "local-owner", jobName, config.maxRuns ?? 5).then((n) => {
12969
+ if (n > 0) process.stdout.write(`[cron] ${jobName}: pruned ${n} old conversation${n === 1 ? "" : "s"}
12970
+ `);
12971
+ }).catch(
12972
+ (err) => console.error(`[cron] ${jobName}: prune failed:`, err instanceof Error ? err.message : err)
12973
+ );
12927
12974
  const elapsed = ((Date.now() - start) / 1e3).toFixed(1);
12928
12975
  process.stdout.write(
12929
12976
  `[cron] ${jobName} completed in ${elapsed}s (${result.steps} steps)
@@ -13075,7 +13122,7 @@ var runInteractive = async (workingDir, params) => {
13075
13122
  await harness.initialize();
13076
13123
  const identity = await ensureAgentIdentity2(workingDir);
13077
13124
  try {
13078
- const { runInteractiveInk } = await import("./run-interactive-ink-XQM7OIZT.js");
13125
+ const { runInteractiveInk } = await import("./run-interactive-ink-DYCMDTTJ.js");
13079
13126
  await runInteractiveInk({
13080
13127
  harness,
13081
13128
  params,
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  main
4
- } from "./chunk-METMUDY6.js";
4
+ } from "./chunk-C5XPPHBB.js";
5
5
 
6
6
  // src/cli.ts
7
7
  void main();
package/dist/index.js CHANGED
@@ -24,7 +24,7 @@ import {
24
24
  runTests,
25
25
  startDevServer,
26
26
  updateAgentGuidance
27
- } from "./chunk-METMUDY6.js";
27
+ } from "./chunk-C5XPPHBB.js";
28
28
  export {
29
29
  __internalRunOrchestration,
30
30
  addSkill,
@@ -2,7 +2,7 @@ import {
2
2
  consumeFirstRunIntro,
3
3
  inferConversationTitle,
4
4
  resolveHarnessEnvironment
5
- } from "./chunk-METMUDY6.js";
5
+ } from "./chunk-C5XPPHBB.js";
6
6
 
7
7
  // src/run-interactive-ink.ts
8
8
  import * as readline from "readline";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@poncho-ai/cli",
3
- "version": "0.33.3",
3
+ "version": "0.34.0",
4
4
  "description": "CLI for building and deploying AI agents",
5
5
  "repository": {
6
6
  "type": "git",
@@ -27,9 +27,9 @@
27
27
  "react": "^19.2.4",
28
28
  "react-devtools-core": "^6.1.5",
29
29
  "yaml": "^2.8.1",
30
- "@poncho-ai/harness": "0.32.1",
31
- "@poncho-ai/messaging": "0.7.9",
32
- "@poncho-ai/sdk": "1.7.1"
30
+ "@poncho-ai/harness": "0.33.0",
31
+ "@poncho-ai/sdk": "1.7.1",
32
+ "@poncho-ai/messaging": "0.7.10"
33
33
  },
34
34
  "devDependencies": {
35
35
  "@types/busboy": "^1.5.4",
package/src/index.ts CHANGED
@@ -779,6 +779,33 @@ const appendCronTurn = (conv: Conversation, task: string, result: CronRunResult)
779
779
  );
780
780
  };
781
781
 
782
+ const MAX_PRUNE_PER_RUN = 25;
783
+
784
+ /** Delete old cron conversations beyond `maxRuns`, capped to avoid API storms on catch-up. */
785
+ const pruneCronConversations = async (
786
+ store: ConversationStore,
787
+ ownerId: string,
788
+ jobName: string,
789
+ maxRuns: number,
790
+ ): Promise<number> => {
791
+ const summaries = await store.listSummaries(ownerId);
792
+ const cronPrefix = `[cron] ${jobName} `;
793
+ const cronSummaries = summaries
794
+ .filter((s) => s.title?.startsWith(cronPrefix))
795
+ .sort((a, b) => (b.updatedAt ?? 0) - (a.updatedAt ?? 0));
796
+
797
+ if (cronSummaries.length <= maxRuns) return 0;
798
+
799
+ const toDelete = cronSummaries.slice(maxRuns, maxRuns + MAX_PRUNE_PER_RUN);
800
+ let deleted = 0;
801
+ for (const s of toDelete) {
802
+ try {
803
+ if (await store.delete(s.conversationId)) deleted++;
804
+ } catch { /* best-effort per entry */ }
805
+ }
806
+ return deleted;
807
+ };
808
+
782
809
  const AGENT_TEMPLATE = (
783
810
  name: string,
784
811
  id: string,
@@ -826,38 +853,35 @@ const resolveLocalPackagesRoot = (): string | null => {
826
853
  };
827
854
 
828
855
  /**
829
- * Build dependency specifiers for the scaffolded project.
830
- * In dev mode we use `file:` paths so pnpm can resolve local packages;
856
+ * Resolve the @poncho-ai/cli dependency specifier for the scaffolded project.
857
+ * In dev mode we use `link:` so pnpm can resolve the local package;
831
858
  * in production we point at the npm registry.
832
859
  */
833
- const resolveCoreDeps = async (
834
- projectDir: string,
835
- ): Promise<{ harness: string; sdk: string }> => {
860
+ const resolveCliDep = async (projectDir: string): Promise<string> => {
836
861
  const packagesRoot = resolveLocalPackagesRoot();
837
862
  if (packagesRoot) {
838
- const harnessAbs = resolve(packagesRoot, "harness");
839
- const sdkAbs = resolve(packagesRoot, "sdk");
840
- return {
841
- harness: `link:${relative(projectDir, harnessAbs)}`,
842
- sdk: `link:${relative(projectDir, sdkAbs)}`,
843
- };
863
+ const cliAbs = resolve(packagesRoot, "cli");
864
+ return `link:${relative(projectDir, cliAbs)}`;
844
865
  }
845
- return {
846
- harness: await readCliDependencyVersion("@poncho-ai/harness", "^0.6.0"),
847
- sdk: await readCliDependencyVersion("@poncho-ai/sdk", "^0.6.0"),
848
- };
866
+ const version = await readCliVersion();
867
+ return `^${version}`;
849
868
  };
850
869
 
851
870
  const PACKAGE_TEMPLATE = async (name: string, projectDir: string): Promise<string> => {
852
- const deps = await resolveCoreDeps(projectDir);
871
+ const cliDep = await resolveCliDep(projectDir);
853
872
  return JSON.stringify(
854
873
  {
855
874
  name,
856
875
  private: true,
857
876
  type: "module",
877
+ scripts: {
878
+ dev: "poncho dev",
879
+ start: "poncho dev",
880
+ test: "poncho test",
881
+ build: "poncho build",
882
+ },
858
883
  dependencies: {
859
- "@poncho-ai/harness": deps.harness,
860
- "@poncho-ai/sdk": deps.sdk,
884
+ "@poncho-ai/cli": cliDep,
861
885
  },
862
886
  },
863
887
  null,
@@ -3385,6 +3409,7 @@ export const createRequestHandler = async (options?: {
3385
3409
  let latestRunId = "";
3386
3410
  const draft = createTurnDraftState();
3387
3411
  let checkpointedRun = false;
3412
+ let checkpointTextAlreadySent = false;
3388
3413
  let runContextTokens = 0;
3389
3414
  let runContextWindow = 0;
3390
3415
  let runContinuation = false;
@@ -3479,11 +3504,27 @@ export const createRequestHandler = async (options?: {
3479
3504
  });
3480
3505
  checkpointedRun = true;
3481
3506
 
3482
- // Send inline keyboard approval buttons to Telegram
3483
3507
  const conv = await conversationStore.get(conversationId);
3484
3508
  if (conv?.channelMeta?.platform === "telegram") {
3485
3509
  const tgAdapter = messagingAdapters.get("telegram") as TelegramAdapter | undefined;
3486
3510
  if (tgAdapter) {
3511
+ const threadRef: import("@poncho-ai/messaging").ThreadRef = {
3512
+ channelId: conv.channelMeta.channelId,
3513
+ platformThreadId: conv.channelMeta.platformThreadId,
3514
+ };
3515
+
3516
+ // Send accumulated text BEFORE approval buttons so Telegram
3517
+ // shows them in the natural order (text → approval request).
3518
+ const pendingText = draft.assistantResponse.trim();
3519
+ if (pendingText) {
3520
+ try {
3521
+ await tgAdapter.sendReply(threadRef, pendingText);
3522
+ checkpointTextAlreadySent = true;
3523
+ } catch (err: unknown) {
3524
+ console.error("[messaging-runner] failed to send pre-approval text:", err instanceof Error ? err.message : err);
3525
+ }
3526
+ }
3527
+
3487
3528
  const approvals = event.approvals.map(a => ({
3488
3529
  approvalId: a.approvalId,
3489
3530
  tool: a.tool,
@@ -3571,8 +3612,8 @@ export const createRequestHandler = async (options?: {
3571
3612
  runConversations.delete(latestRunId);
3572
3613
  }
3573
3614
 
3574
- console.log("[messaging-runner] run complete, response length:", draft.assistantResponse.length, runContinuation ? "(continuation)" : "");
3575
- const response = draft.assistantResponse;
3615
+ const response = checkpointTextAlreadySent ? "" : draft.assistantResponse;
3616
+ console.log("[messaging-runner] run complete, response length:", response.length, checkpointTextAlreadySent ? "(text sent at checkpoint)" : "", runContinuation ? "(continuation)" : "");
3576
3617
 
3577
3618
  return {
3578
3619
  response,
@@ -6069,6 +6110,15 @@ export const createRequestHandler = async (options?: {
6069
6110
  await conversationStore.update(freshConv);
6070
6111
  }
6071
6112
 
6113
+ const pruneWork = pruneCronConversations(
6114
+ conversationStore, cronOwnerId, jobName, cronJob.maxRuns ?? 5,
6115
+ ).then(n => {
6116
+ if (n > 0) process.stdout.write(`[cron] ${jobName}: pruned ${n} old conversation${n === 1 ? "" : "s"}\n`);
6117
+ }).catch(err =>
6118
+ console.error(`[cron] ${jobName}: prune failed:`, err instanceof Error ? err.message : err),
6119
+ );
6120
+ doWaitUntil(pruneWork);
6121
+
6072
6122
  if (result.continuation) {
6073
6123
  const work = selfFetchWithRetry(`/api/internal/continue/${encodeURIComponent(convId)}`).catch(err =>
6074
6124
  console.error(`[poncho][cron] Continuation self-fetch failed:`, err instanceof Error ? err.message : err),
@@ -6460,6 +6510,11 @@ export const startDevServer = async (
6460
6510
  });
6461
6511
  await store.update(freshConv);
6462
6512
  }
6513
+ pruneCronConversations(store, "local-owner", jobName, config.maxRuns ?? 5).then(n => {
6514
+ if (n > 0) process.stdout.write(`[cron] ${jobName}: pruned ${n} old conversation${n === 1 ? "" : "s"}\n`);
6515
+ }).catch(err =>
6516
+ console.error(`[cron] ${jobName}: prune failed:`, err instanceof Error ? err.message : err),
6517
+ );
6463
6518
  const elapsed = ((Date.now() - start) / 1000).toFixed(1);
6464
6519
  process.stdout.write(
6465
6520
  `[cron] ${jobName} completed in ${elapsed}s (${result.steps} steps)\n`,