@poncho-ai/harness 0.39.0 → 0.39.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.
@@ -1,5 +1,5 @@
1
1
 
2
- > @poncho-ai/harness@0.39.0 build /home/runner/work/poncho-ai/poncho-ai/packages/harness
2
+ > @poncho-ai/harness@0.39.1 build /home/runner/work/poncho-ai/poncho-ai/packages/harness
3
3
  > node scripts/embed-docs.js && tsup src/index.ts --format esm --dts
4
4
 
5
5
  [embed-docs] Generated poncho-docs.ts with 4 topics
@@ -8,9 +8,9 @@
8
8
  CLI tsup v8.5.1
9
9
  CLI Target: es2022
10
10
  ESM Build start
11
- ESM dist/index.js 479.07 KB
12
11
  ESM dist/isolate-TCWTUVG4.js 47.34 KB
13
- ESM ⚡️ Build success in 251ms
12
+ ESM dist/index.js 480.10 KB
13
+ ESM ⚡️ Build success in 223ms
14
14
  DTS Build start
15
- DTS ⚡️ Build success in 8269ms
15
+ DTS ⚡️ Build success in 7012ms
16
16
  DTS dist/index.d.ts 73.95 KB
package/CHANGELOG.md CHANGED
@@ -1,5 +1,38 @@
1
1
  # @poncho-ai/harness
2
2
 
3
+ ## 0.39.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [`244a3a3`](https://github.com/cesr/poncho-ai/commit/244a3a310c6c52f9e8535b28fb25d77829583d3f) Thanks [@cesr](https://github.com/cesr)! - fix(harness): don't archive `browser_screenshot` / `browser_snapshot` payloads
8
+
9
+ The per-conversation `_toolResultArchive` had no size cap or eviction, and
10
+ browser tool results were being archived in full — base64 JPEG screenshots
11
+ (~50-500KB each) and accessibility-tree snapshots accumulated for the lifetime
12
+ of a conversation. Heavy browser sessions OOM'd `poncho dev` after ~80 minutes.
13
+
14
+ Skip archiving for view-once tool results (`browser_screenshot`,
15
+ `browser_snapshot`). The model consumes them in-step; they're never retrieved
16
+ after-the-fact, so archiving them only burns memory.
17
+
18
+ - [`d6248c8`](https://github.com/cesr/poncho-ai/commit/d6248c8b6d22e0fd0becde9e31dff7c12c724d84) Thanks [@cesr](https://github.com/cesr)! - fix(cli, harness): unify turn-parameter assembly so `conversation_recall` works everywhere
19
+
20
+ The recall tool relies on three context parameters (`__conversationRecallCorpus`,
21
+ `__conversationListFn`, `__conversationFetchFn`) that were only injected for
22
+ user-initiated HTTP turns. Cron, reminder, messaging-adapter, chat-continuation,
23
+ subagent-callback, and tool-approval-resume runs all built their own
24
+ `runInput.parameters` object and silently omitted these — causing
25
+ `conversation_recall` to throw "not available in this environment" or return
26
+ empty results depending on the call mode.
27
+
28
+ Introduces a single `buildTurnParameters(conversation, opts)` helper in the CLI
29
+ that owns context-parameter assembly (recall functions, `__activeConversationId`,
30
+ `__ownerId`, messaging metadata, tool-result archive). HTTP, messaging, and
31
+ cron/reminder paths now go through it. The harness orchestrator's three
32
+ internal turn sites (chat continuation, subagent-callback resume, tool-approval
33
+ resume) now call the existing `hooks.buildRecallParams` so they pick up the
34
+ recall functions too.
35
+
3
36
  ## 0.39.0
4
37
 
5
38
  ### Minor Changes
package/dist/index.js CHANGED
@@ -8039,6 +8039,10 @@ var SKILL_TOOL_NAMES = [
8039
8039
  "list_skill_scripts",
8040
8040
  "run_skill_script"
8041
8041
  ];
8042
+ var NON_ARCHIVABLE_TOOL_NAMES = /* @__PURE__ */ new Set([
8043
+ "browser_screenshot",
8044
+ "browser_snapshot"
8045
+ ]);
8042
8046
  var isAbortError = (error) => {
8043
8047
  if (!error || typeof error !== "object") {
8044
8048
  return false;
@@ -8743,7 +8747,7 @@ var AgentHarness = class _AgentHarness {
8743
8747
  if (row.content.startsWith(TOOL_RESULT_TRUNCATED_PREFIX)) return row;
8744
8748
  if (this.shouldPreserveSkillToolResult(row)) return row;
8745
8749
  const toolResultId = row.tool_use_id;
8746
- if (!archive[toolResultId]) {
8750
+ if (!archive[toolResultId] && !NON_ARCHIVABLE_TOOL_NAMES.has(row.tool_name)) {
8747
8751
  archive[toolResultId] = {
8748
8752
  toolResultId,
8749
8753
  conversationId,
@@ -10506,7 +10510,7 @@ ${textContent}` };
10506
10510
  });
10507
10511
  {
10508
10512
  const archive = this.archivedToolResultsByConversation.get(conversationId);
10509
- if (archive) {
10513
+ if (archive && !NON_ARCHIVABLE_TOOL_NAMES.has(result2.tool)) {
10510
10514
  const payload = JSON.stringify(result2.output ?? null);
10511
10515
  archive[result2.callId] = {
10512
10516
  toolResultId: result2.callId,
@@ -11500,12 +11504,18 @@ var AgentOrchestrator = class {
11500
11504
  }
11501
11505
  }
11502
11506
  async runChatContinuation(conversationId, conversation, continuationMessages, onYield) {
11507
+ const recallParams = this.hooks?.buildRecallParams?.({
11508
+ ownerId: conversation.ownerId,
11509
+ tenantId: conversation.tenantId,
11510
+ excludeConversationId: conversationId
11511
+ }) ?? {};
11503
11512
  const execution = await executeConversationTurn({
11504
11513
  harness: this.harness,
11505
11514
  runInput: {
11506
11515
  conversationId,
11507
11516
  tenantId: conversation.tenantId ?? void 0,
11508
11517
  parameters: withToolResultArchiveParam({
11518
+ ...recallParams,
11509
11519
  __activeConversationId: conversationId,
11510
11520
  __ownerId: conversation.ownerId
11511
11521
  }, conversation),
@@ -11618,6 +11628,11 @@ var AgentOrchestrator = class {
11618
11628
  const fullCheckpointWithResults = resumeToolResultMsg ? [...fullCheckpointMessages, resumeToolResultMsg] : fullCheckpointMessages;
11619
11629
  let draftRef;
11620
11630
  let execution;
11631
+ const resumeRecallParams = this.hooks?.buildRecallParams?.({
11632
+ ownerId: conversation.ownerId,
11633
+ tenantId: conversation.tenantId,
11634
+ excludeConversationId: conversationId
11635
+ }) ?? {};
11621
11636
  try {
11622
11637
  execution = await executeConversationTurn({
11623
11638
  harness: this.harness,
@@ -11625,6 +11640,11 @@ var AgentOrchestrator = class {
11625
11640
  messages: fullCheckpointMessages,
11626
11641
  toolResults,
11627
11642
  conversationId,
11643
+ parameters: withToolResultArchiveParam({
11644
+ ...resumeRecallParams,
11645
+ __activeConversationId: conversationId,
11646
+ __ownerId: conversation.ownerId
11647
+ }, conversation),
11628
11648
  abortSignal: abortController.signal
11629
11649
  }),
11630
11650
  initialContextTokens: conversation.contextTokens ?? 0,
@@ -12260,6 +12280,11 @@ ${resultBody}`,
12260
12280
  `[poncho][subagent-callback] conversation="${conversationId}" history_source=${historySelection.source}`
12261
12281
  );
12262
12282
  let execution;
12283
+ const recallParams = this.hooks?.buildRecallParams?.({
12284
+ ownerId: conversation.ownerId,
12285
+ tenantId: conversation.tenantId,
12286
+ excludeConversationId: conversationId
12287
+ }) ?? {};
12263
12288
  try {
12264
12289
  execution = await executeConversationTurn({
12265
12290
  harness: this.harness,
@@ -12268,6 +12293,7 @@ ${resultBody}`,
12268
12293
  conversationId,
12269
12294
  tenantId: conversation.tenantId ?? void 0,
12270
12295
  parameters: withToolResultArchiveParam({
12296
+ ...recallParams,
12271
12297
  __activeConversationId: conversationId,
12272
12298
  __ownerId: conversation.ownerId
12273
12299
  }, conversation),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@poncho-ai/harness",
3
- "version": "0.39.0",
3
+ "version": "0.39.1",
4
4
  "description": "Agent execution runtime - conversation loop, tool dispatch, streaming",
5
5
  "repository": {
6
6
  "type": "git",
package/src/harness.ts CHANGED
@@ -142,6 +142,15 @@ const SKILL_TOOL_NAMES = [
142
142
  "run_skill_script",
143
143
  ] as const;
144
144
 
145
+ // Tools whose results are "view-once" — the model consumes the payload in the
146
+ // step where it's returned and never needs it retrieved later. Archiving them
147
+ // caused unbounded heap growth on browser-heavy sessions (each screenshot
148
+ // is ~50-500KB base64 and accumulated for the lifetime of the conversation).
149
+ const NON_ARCHIVABLE_TOOL_NAMES = new Set<string>([
150
+ "browser_screenshot",
151
+ "browser_snapshot",
152
+ ]);
153
+
145
154
  const isAbortError = (error: unknown): boolean => {
146
155
  if (!error || typeof error !== "object") {
147
156
  return false;
@@ -969,7 +978,7 @@ export class AgentHarness {
969
978
  if (row.content.startsWith(TOOL_RESULT_TRUNCATED_PREFIX)) return row;
970
979
  if (this.shouldPreserveSkillToolResult(row)) return row;
971
980
  const toolResultId = row.tool_use_id;
972
- if (!archive[toolResultId]) {
981
+ if (!archive[toolResultId] && !NON_ARCHIVABLE_TOOL_NAMES.has(row.tool_name)) {
973
982
  archive[toolResultId] = {
974
983
  toolResultId,
975
984
  conversationId,
@@ -3068,7 +3077,7 @@ Code is wrapped in an async IIFE — use \`return\` to return a value to the too
3068
3077
  });
3069
3078
  {
3070
3079
  const archive = this.archivedToolResultsByConversation.get(conversationId);
3071
- if (archive) {
3080
+ if (archive && !NON_ARCHIVABLE_TOOL_NAMES.has(result.tool)) {
3072
3081
  const payload = JSON.stringify(result.output ?? null);
3073
3082
  archive[result.callId] = {
3074
3083
  toolResultId: result.callId,
@@ -190,12 +190,18 @@ export class AgentOrchestrator {
190
190
  continuationMessages: Message[],
191
191
  onYield?: (event: AgentEvent) => void | Promise<void>,
192
192
  ): Promise<void> {
193
+ const recallParams = this.hooks?.buildRecallParams?.({
194
+ ownerId: conversation.ownerId,
195
+ tenantId: conversation.tenantId,
196
+ excludeConversationId: conversationId,
197
+ }) ?? {};
193
198
  const execution = await executeConversationTurn({
194
199
  harness: this.harness,
195
200
  runInput: {
196
201
  conversationId,
197
202
  tenantId: conversation.tenantId ?? undefined,
198
203
  parameters: withToolResultArchiveParam({
204
+ ...recallParams,
199
205
  __activeConversationId: conversationId,
200
206
  __ownerId: conversation.ownerId,
201
207
  }, conversation),
@@ -332,6 +338,11 @@ export class AgentOrchestrator {
332
338
 
333
339
  let draftRef: TurnDraftState | undefined;
334
340
  let execution: ExecuteTurnResult | undefined;
341
+ const resumeRecallParams = this.hooks?.buildRecallParams?.({
342
+ ownerId: conversation.ownerId,
343
+ tenantId: conversation.tenantId,
344
+ excludeConversationId: conversationId,
345
+ }) ?? {};
335
346
 
336
347
  try {
337
348
  execution = await executeConversationTurn({
@@ -340,6 +351,11 @@ export class AgentOrchestrator {
340
351
  messages: fullCheckpointMessages,
341
352
  toolResults,
342
353
  conversationId,
354
+ parameters: withToolResultArchiveParam({
355
+ ...resumeRecallParams,
356
+ __activeConversationId: conversationId,
357
+ __ownerId: conversation.ownerId,
358
+ }, conversation),
343
359
  abortSignal: abortController.signal,
344
360
  }),
345
361
  initialContextTokens: conversation.contextTokens ?? 0,
@@ -1073,6 +1089,11 @@ export class AgentOrchestrator {
1073
1089
  `[poncho][subagent-callback] conversation="${conversationId}" history_source=${historySelection.source}`,
1074
1090
  );
1075
1091
  let execution: ExecuteTurnResult | undefined;
1092
+ const recallParams = this.hooks?.buildRecallParams?.({
1093
+ ownerId: conversation.ownerId,
1094
+ tenantId: conversation.tenantId,
1095
+ excludeConversationId: conversationId,
1096
+ }) ?? {};
1076
1097
 
1077
1098
  try {
1078
1099
  execution = await executeConversationTurn({
@@ -1082,6 +1103,7 @@ export class AgentOrchestrator {
1082
1103
  conversationId,
1083
1104
  tenantId: conversation.tenantId ?? undefined,
1084
1105
  parameters: withToolResultArchiveParam({
1106
+ ...recallParams,
1085
1107
  __activeConversationId: conversationId,
1086
1108
  __ownerId: conversation.ownerId,
1087
1109
  }, conversation),