@poncho-ai/harness 0.52.2 → 0.55.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 +73 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +131 -19
- package/package.json +2 -2
- package/src/compaction.ts +206 -13
- package/src/harness.ts +3 -1
- package/src/orchestrator/orchestrator.ts +20 -2
- package/src/state.ts +3 -0
- package/src/storage/entries.ts +204 -0
- package/src/subagent-manager.ts +4 -0
- package/src/subagent-tools.ts +1 -0
- package/src/tool-dispatcher.ts +4 -1
- package/test/compaction.test.ts +274 -0
- package/test/entries.test.ts +125 -0
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @poncho-ai/harness@0.
|
|
2
|
+
> @poncho-ai/harness@0.55.0 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
|
[34mCLI[39m tsup v8.5.1
|
|
9
9
|
[34mCLI[39m Target: es2022
|
|
10
10
|
[34mESM[39m Build start
|
|
11
|
-
[32mESM[39m [1mdist/index.js [22m[32m540.60 KB[39m
|
|
12
11
|
[32mESM[39m [1mdist/isolate-F2PPSUL6.js [22m[32m53.82 KB[39m
|
|
13
|
-
[32mESM[39m
|
|
12
|
+
[32mESM[39m [1mdist/index.js [22m[32m545.48 KB[39m
|
|
13
|
+
[32mESM[39m ⚡️ Build success in 258ms
|
|
14
14
|
[34mDTS[39m Build start
|
|
15
|
-
[32mDTS[39m ⚡️ Build success in
|
|
16
|
-
[32mDTS[39m [1mdist/index.d.ts [22m[
|
|
15
|
+
[32mDTS[39m ⚡️ Build success in 7628ms
|
|
16
|
+
[32mDTS[39m [1mdist/index.d.ts [22m[32m94.38 KB[39m
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,78 @@
|
|
|
1
1
|
# @poncho-ai/harness
|
|
2
2
|
|
|
3
|
+
## 0.55.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#149](https://github.com/cesr/poncho-ai/pull/149) [`f5a8260`](https://github.com/cesr/poncho-ai/commit/f5a8260d0515038afc1797d00507908c334115ff) Thanks [@cesr](https://github.com/cesr)! - compaction: preserve subagent context and prior summaries, harden the split
|
|
8
|
+
|
|
9
|
+
Three improvements to context compaction (fires at ~75% context):
|
|
10
|
+
- **Split safety**: `findSafeSplitPoint` now refuses a split whose compacted
|
|
11
|
+
side would end on an assistant message with unanswered `tool_calls` (its
|
|
12
|
+
answering `role:"tool"` result having moved to the preserved side), walking
|
|
13
|
+
earlier to the next clean `user` boundary. Prevents orphaning a tool-call
|
|
14
|
+
relationship inside the summary boundary. Still returns `-1` when no safe
|
|
15
|
+
point exists.
|
|
16
|
+
- **Subagent ledger**: while compacting, scans for subagent-callback records
|
|
17
|
+
(metadata `_subagentCallback`/`subagentCallback`, or text starting with
|
|
18
|
+
`[Subagent Result]`) and any `## Subagents` block embedded in a prior
|
|
19
|
+
compaction summary, then renders a combined, deduped (by `subagentId`)
|
|
20
|
+
ledger that is appended VERBATIM after the LLM summary text — so the model
|
|
21
|
+
can never paraphrase or truncate subagent results away. Cumulative across
|
|
22
|
+
successive compactions.
|
|
23
|
+
- **Cumulative summary**: when the first compacted message is itself a prior
|
|
24
|
+
compaction summary, it is passed to the summarizer in full (not truncated
|
|
25
|
+
to 1200 chars) and the prompt instructs the model to merge-and-update the
|
|
26
|
+
prior working state rather than re-summarize it from scratch. All other
|
|
27
|
+
messages keep the 1200-char truncation.
|
|
28
|
+
|
|
29
|
+
## 0.54.0
|
|
30
|
+
|
|
31
|
+
### Minor Changes
|
|
32
|
+
|
|
33
|
+
- [#147](https://github.com/cesr/poncho-ai/pull/147) [`a3eed14`](https://github.com/cesr/poncho-ai/commit/a3eed142832318b6397cd73819d3296c79d6eff0) Thanks [@cesr](https://github.com/cesr)! - storage: add append-only conversation-entry substrate (unused groundwork)
|
|
34
|
+
|
|
35
|
+
Pure entry types + rebuild functions (`buildLlmContext`,
|
|
36
|
+
`buildDisplaySnapshot`, `getPendingSubagentResults`) for the eventual
|
|
37
|
+
append-only conversation model that removes the mutable-blob clobber race
|
|
38
|
+
(the root cause behind lost subagent results). No storage-engine wiring
|
|
39
|
+
and no live callers yet — additive, deploys nothing behavioral. The
|
|
40
|
+
rebuild logic (compaction overlay, amendment folding, callback-consumption)
|
|
41
|
+
is covered by unit tests so the design is proven before the bigger
|
|
42
|
+
dual-write / migration / cutover PRs.
|
|
43
|
+
|
|
44
|
+
## 0.53.0
|
|
45
|
+
|
|
46
|
+
### Minor Changes
|
|
47
|
+
|
|
48
|
+
- [#145](https://github.com/cesr/poncho-ai/pull/145) [`bfa4976`](https://github.com/cesr/poncho-ai/commit/bfa4976ac8b05a300e22271e23c3bae4aadae2a8) Thanks [@cesr](https://github.com/cesr)! - events: add stable identity so streaming clients match instead of guess
|
|
49
|
+
|
|
50
|
+
Additive fields that let a streaming client reconstruct view-state by
|
|
51
|
+
identity rather than inferring structure from event order (the source of a
|
|
52
|
+
class of reconnect/subagent rendering bugs):
|
|
53
|
+
- `tool:started` / `tool:completed` / `tool:error` now carry `toolCallId`
|
|
54
|
+
(already in scope as `call.id` / `result.callId`). Clients match tool
|
|
55
|
+
pills by id instead of by tool name.
|
|
56
|
+
- `subagent:spawned|completed|error|stopped` now carry `parentToolCallId`
|
|
57
|
+
(the `spawn_subagent` tool call's id) and `task`; `completed`/`error`
|
|
58
|
+
also carry `resultText`. Clients attach subagent state to the spawning
|
|
59
|
+
tool's pill and render the result inline — no header-regex or
|
|
60
|
+
sequential-cursor pairing needed.
|
|
61
|
+
- `ToolContext` gains `toolCallId` so the `spawn_subagent` handler can
|
|
62
|
+
record which call produced the subagent (plumbed: tool-dispatcher →
|
|
63
|
+
spawn handler → `SubagentSpawnOptions.parentToolCallId` →
|
|
64
|
+
`subagentMeta.parentToolCallId` → the events above).
|
|
65
|
+
- `run:started` gains an optional `cause` field in the type
|
|
66
|
+
(`user|continuation|subagent_callback|approval_resume`); emission is
|
|
67
|
+
deferred to a later pass.
|
|
68
|
+
|
|
69
|
+
All fields are additive; older clients ignore them.
|
|
70
|
+
|
|
71
|
+
### Patch Changes
|
|
72
|
+
|
|
73
|
+
- Updated dependencies [[`bfa4976`](https://github.com/cesr/poncho-ai/commit/bfa4976ac8b05a300e22271e23c3bae4aadae2a8)]:
|
|
74
|
+
- @poncho-ai/sdk@1.15.0
|
|
75
|
+
|
|
3
76
|
## 0.52.2
|
|
4
77
|
|
|
5
78
|
### Patch Changes
|
package/dist/index.d.ts
CHANGED
|
@@ -96,6 +96,12 @@ declare const estimateTotalTokens: (systemPrompt: string, messages: Message[], t
|
|
|
96
96
|
* and everything from it onward is preserved. The split always lands just
|
|
97
97
|
* before a `user` message to avoid breaking assistant+tool pairs.
|
|
98
98
|
*
|
|
99
|
+
* Defensive guard: even at a `user` boundary, refuse a split whose compacted
|
|
100
|
+
* side would END on an assistant message with unanswered tool_calls (its
|
|
101
|
+
* `tool` result having moved to the preserved side). Such a split would
|
|
102
|
+
* orphan the tool_calls inside the summary boundary. When that happens we
|
|
103
|
+
* walk earlier to the next safe `user` boundary.
|
|
104
|
+
*
|
|
99
105
|
* Returns -1 if no valid split point is found.
|
|
100
106
|
*/
|
|
101
107
|
declare const findSafeSplitPoint: (messages: Message[], keepRecentMessages: number) => number;
|
|
@@ -199,6 +205,9 @@ interface Conversation {
|
|
|
199
205
|
* subagent's runs emit no telemetry (e.g. spawned from an incognito
|
|
200
206
|
* turn). Read by the orchestrator's runSubagent / continuation. */
|
|
201
207
|
suppressTelemetry?: boolean;
|
|
208
|
+
/** The parent's `spawn_subagent` tool call id — echoed onto subagent:*
|
|
209
|
+
* events so a client can attach subagent state to that tool's pill. */
|
|
210
|
+
parentToolCallId?: string;
|
|
202
211
|
};
|
|
203
212
|
channelMeta?: {
|
|
204
213
|
platform: string;
|
|
@@ -1190,6 +1199,10 @@ interface SubagentManager {
|
|
|
1190
1199
|
/** Inherit the parent run's telemetry choice — when true, the subagent
|
|
1191
1200
|
* run (and its re-runs) emit no telemetry. */
|
|
1192
1201
|
suppressTelemetry?: boolean;
|
|
1202
|
+
/** The id of the `spawn_subagent` tool call that produced this subagent,
|
|
1203
|
+
* so its events can carry `parentToolCallId` and a client can attach
|
|
1204
|
+
* subagent state to the spawning tool's pill. */
|
|
1205
|
+
parentToolCallId?: string;
|
|
1193
1206
|
}): Promise<SubagentSpawnResult>;
|
|
1194
1207
|
sendMessage(subagentId: string, message: string): Promise<SubagentSpawnResult>;
|
|
1195
1208
|
stop(subagentId: string): Promise<void>;
|
package/dist/index.js
CHANGED
|
@@ -374,6 +374,9 @@ var SUMMARIZATION_PROMPT = `Summarize the following conversation into a structur
|
|
|
374
374
|
|
|
375
375
|
Be concise but preserve all information needed to continue the task.
|
|
376
376
|
Omit any section that has no relevant content.`;
|
|
377
|
+
var CUMULATIVE_SUMMARY_PROMPT = `The FIRST message below (tagged [prior-summary]) is an existing working-state summary produced by an earlier compaction. Treat it as the authoritative prior working state: MERGE AND UPDATE it with the newer messages that follow it, carrying forward all still-relevant detail. Do NOT discard or re-compress information from the prior summary just because it is older \u2014 only drop it if the newer messages explicitly supersede it.`;
|
|
378
|
+
var SUBAGENT_DIGEST_CHARS = 500;
|
|
379
|
+
var SUBAGENT_LEDGER_HEADING = "## Subagents";
|
|
377
380
|
var resolveCompactionConfig = (explicit) => {
|
|
378
381
|
if (!explicit) return { ...DEFAULT_COMPACTION_CONFIG };
|
|
379
382
|
return {
|
|
@@ -398,32 +401,57 @@ var estimateTotalTokens = (systemPrompt, messages, toolDefinitionsJson) => {
|
|
|
398
401
|
}
|
|
399
402
|
return tokens;
|
|
400
403
|
};
|
|
404
|
+
var assistantHasToolCalls = (msg) => {
|
|
405
|
+
if (msg.role !== "assistant") return false;
|
|
406
|
+
if (typeof msg.content !== "string") return false;
|
|
407
|
+
if (!msg.content.includes('"tool_calls"')) return false;
|
|
408
|
+
try {
|
|
409
|
+
const parsed = JSON.parse(msg.content);
|
|
410
|
+
return Array.isArray(parsed.tool_calls) && parsed.tool_calls.length > 0;
|
|
411
|
+
} catch {
|
|
412
|
+
return false;
|
|
413
|
+
}
|
|
414
|
+
};
|
|
415
|
+
var splitOrphansToolCalls = (messages, idx) => {
|
|
416
|
+
if (idx <= 0 || idx >= messages.length) return false;
|
|
417
|
+
const lastCompacted = messages[idx - 1];
|
|
418
|
+
return assistantHasToolCalls(lastCompacted);
|
|
419
|
+
};
|
|
401
420
|
var findSafeSplitPoint = (messages, keepRecentMessages) => {
|
|
402
421
|
const candidateIdx = messages.length - keepRecentMessages;
|
|
403
422
|
if (candidateIdx < MIN_COMPACTABLE_MESSAGES) return -1;
|
|
404
423
|
for (let i = candidateIdx; i >= MIN_COMPACTABLE_MESSAGES; i--) {
|
|
405
|
-
if (messages[i].role === "user") {
|
|
424
|
+
if (messages[i].role === "user" && !splitOrphansToolCalls(messages, i)) {
|
|
406
425
|
return i;
|
|
407
426
|
}
|
|
408
427
|
}
|
|
409
428
|
for (let i = candidateIdx + 1; i < messages.length - 1; i++) {
|
|
410
|
-
if (messages[i].role === "user") {
|
|
429
|
+
if (messages[i].role === "user" && !splitOrphansToolCalls(messages, i)) {
|
|
411
430
|
if (i < MIN_COMPACTABLE_MESSAGES) return -1;
|
|
412
431
|
return i;
|
|
413
432
|
}
|
|
414
433
|
}
|
|
415
434
|
return -1;
|
|
416
435
|
};
|
|
436
|
+
var isCompactionSummary = (msg) => msg.metadata?.isCompactionSummary === true;
|
|
417
437
|
var buildSummarizationMessages = (messagesToCompact, instructions) => {
|
|
438
|
+
const hasPriorSummary = messagesToCompact.length > 0 && isCompactionSummary(messagesToCompact[0]);
|
|
418
439
|
const conversationLines = [];
|
|
419
|
-
for (
|
|
440
|
+
for (let i = 0; i < messagesToCompact.length; i++) {
|
|
441
|
+
const msg = messagesToCompact[i];
|
|
420
442
|
const text = getTextContent(msg);
|
|
421
|
-
const
|
|
422
|
-
|
|
443
|
+
const isPrior = i === 0 && hasPriorSummary;
|
|
444
|
+
const rendered = isPrior || text.length <= SUMMARIZATION_MESSAGE_TRUNCATION_CHARS ? text : text.slice(0, SUMMARIZATION_MESSAGE_TRUNCATION_CHARS) + "\n...[truncated]";
|
|
445
|
+
const tag = isPrior ? "prior-summary" : msg.role;
|
|
446
|
+
conversationLines.push(`[${tag}]: ${rendered}`);
|
|
423
447
|
}
|
|
424
|
-
|
|
448
|
+
let prompt = SUMMARIZATION_PROMPT;
|
|
449
|
+
if (hasPriorSummary) prompt = `${prompt}
|
|
425
450
|
|
|
426
|
-
|
|
451
|
+
${CUMULATIVE_SUMMARY_PROMPT}`;
|
|
452
|
+
if (instructions) prompt = `${prompt}
|
|
453
|
+
|
|
454
|
+
Additional focus: ${instructions}`;
|
|
427
455
|
return [
|
|
428
456
|
{
|
|
429
457
|
role: "user",
|
|
@@ -435,6 +463,67 @@ ${conversationLines.join("\n\n")}`
|
|
|
435
463
|
}
|
|
436
464
|
];
|
|
437
465
|
};
|
|
466
|
+
var SUBAGENT_RESULT_HEADER = /^\[Subagent Result\] Subagent "([^"]*)" \(([^)]*)\) (\S+):/;
|
|
467
|
+
var parseSubagentCallback = (msg) => {
|
|
468
|
+
if (msg.role !== "user") return null;
|
|
469
|
+
const meta = msg.metadata ?? {};
|
|
470
|
+
const text = getTextContent(msg);
|
|
471
|
+
const hasMetaFlag = meta._subagentCallback === true || meta.subagentCallback === true;
|
|
472
|
+
const hasTextMarker = text.startsWith("[Subagent Result]");
|
|
473
|
+
if (!hasMetaFlag && !hasTextMarker) return null;
|
|
474
|
+
const headerMatch = text.match(SUBAGENT_RESULT_HEADER);
|
|
475
|
+
const subagentId = typeof meta.subagentId === "string" && meta.subagentId ? meta.subagentId : headerMatch?.[2] ?? "";
|
|
476
|
+
if (!subagentId) return null;
|
|
477
|
+
const task = typeof meta.task === "string" && meta.task ? meta.task : headerMatch?.[1] ?? "";
|
|
478
|
+
const status = headerMatch?.[3] ?? "completed";
|
|
479
|
+
const bodyStart = text.indexOf("\n\n");
|
|
480
|
+
const body = bodyStart >= 0 ? text.slice(bodyStart + 2) : text;
|
|
481
|
+
const digest = body.length > SUBAGENT_DIGEST_CHARS ? body.slice(0, SUBAGENT_DIGEST_CHARS) + "\u2026" : body;
|
|
482
|
+
return { subagentId, task, status, digest };
|
|
483
|
+
};
|
|
484
|
+
var parsePriorLedger = (summaryText) => {
|
|
485
|
+
const headingIdx = summaryText.indexOf(SUBAGENT_LEDGER_HEADING);
|
|
486
|
+
if (headingIdx < 0) return [];
|
|
487
|
+
const block = summaryText.slice(headingIdx + SUBAGENT_LEDGER_HEADING.length);
|
|
488
|
+
const entries = [];
|
|
489
|
+
const entryRe = /^- \*\*(.*?)\*\* \((.+?)\) — (\S+)\n {2}(.*)$/gm;
|
|
490
|
+
let m;
|
|
491
|
+
while ((m = entryRe.exec(block)) !== null) {
|
|
492
|
+
entries.push({
|
|
493
|
+
task: m[1],
|
|
494
|
+
subagentId: m[2],
|
|
495
|
+
status: m[3],
|
|
496
|
+
digest: m[4]
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
return entries;
|
|
500
|
+
};
|
|
501
|
+
var collectSubagentLedger = (messagesToCompact) => {
|
|
502
|
+
const byId = /* @__PURE__ */ new Map();
|
|
503
|
+
const order = [];
|
|
504
|
+
const upsert = (entry) => {
|
|
505
|
+
if (!byId.has(entry.subagentId)) order.push(entry.subagentId);
|
|
506
|
+
byId.set(entry.subagentId, entry);
|
|
507
|
+
};
|
|
508
|
+
for (const msg of messagesToCompact) {
|
|
509
|
+
if (isCompactionSummary(msg)) {
|
|
510
|
+
for (const prior of parsePriorLedger(getTextContent(msg))) upsert(prior);
|
|
511
|
+
continue;
|
|
512
|
+
}
|
|
513
|
+
const entry = parseSubagentCallback(msg);
|
|
514
|
+
if (entry) upsert(entry);
|
|
515
|
+
}
|
|
516
|
+
return order.map((id) => byId.get(id));
|
|
517
|
+
};
|
|
518
|
+
var renderSubagentLedger = (entries) => {
|
|
519
|
+
if (entries.length === 0) return "";
|
|
520
|
+
const lines = entries.map(
|
|
521
|
+
(e) => `- **${e.task}** (${e.subagentId}) \u2014 ${e.status}
|
|
522
|
+
${e.digest.replace(/\n/g, " ")}`
|
|
523
|
+
);
|
|
524
|
+
return `${SUBAGENT_LEDGER_HEADING}
|
|
525
|
+
${lines.join("\n")}`;
|
|
526
|
+
};
|
|
438
527
|
var buildContinuationMessage = (summary) => ({
|
|
439
528
|
role: "user",
|
|
440
529
|
content: `[CONTEXT COMPACTION] This conversation was automatically compacted. The summary below covers earlier messages.
|
|
@@ -483,7 +572,11 @@ var compactMessages = async (model, messages, config, options) => {
|
|
|
483
572
|
warning: "Summarization returned empty result"
|
|
484
573
|
};
|
|
485
574
|
}
|
|
486
|
-
const
|
|
575
|
+
const ledger = renderSubagentLedger(collectSubagentLedger(toCompact));
|
|
576
|
+
const summaryWithLedger = ledger ? `${summary}
|
|
577
|
+
|
|
578
|
+
${ledger}` : summary;
|
|
579
|
+
const continuationMessage = buildContinuationMessage(summaryWithLedger);
|
|
487
580
|
const compactedMessages = [continuationMessage, ...toPreserve];
|
|
488
581
|
return {
|
|
489
582
|
compacted: true,
|
|
@@ -8323,7 +8416,8 @@ var createSubagentTools = (manager) => [
|
|
|
8323
8416
|
parentConversationId: conversationId,
|
|
8324
8417
|
ownerId,
|
|
8325
8418
|
tenantId: context.tenantId,
|
|
8326
|
-
suppressTelemetry: context.suppressTelemetry
|
|
8419
|
+
suppressTelemetry: context.suppressTelemetry,
|
|
8420
|
+
parentToolCallId: context.toolCallId
|
|
8327
8421
|
});
|
|
8328
8422
|
return { subagentId, status: "running" };
|
|
8329
8423
|
}
|
|
@@ -8585,7 +8679,7 @@ var ToolDispatcher = class {
|
|
|
8585
8679
|
};
|
|
8586
8680
|
}
|
|
8587
8681
|
try {
|
|
8588
|
-
const output = await definition.handler(call.input, context);
|
|
8682
|
+
const output = await definition.handler(call.input, { ...context, toolCallId: call.id });
|
|
8589
8683
|
if (context.abortSignal?.aborted) {
|
|
8590
8684
|
return {
|
|
8591
8685
|
callId: call.id,
|
|
@@ -11170,7 +11264,7 @@ ${textContent}` };
|
|
|
11170
11264
|
return;
|
|
11171
11265
|
}
|
|
11172
11266
|
const runtimeToolName = exposedToolNames.get(call.name) ?? call.name;
|
|
11173
|
-
yield pushEvent({ type: "tool:started", tool: runtimeToolName, input: call.input });
|
|
11267
|
+
yield pushEvent({ type: "tool:started", tool: runtimeToolName, toolCallId: call.id, input: call.input });
|
|
11174
11268
|
if (this.requiresApprovalForToolCall(runtimeToolName, call.input)) {
|
|
11175
11269
|
approvalNeeded.push({
|
|
11176
11270
|
approvalId: `approval_${randomUUID5()}`,
|
|
@@ -11363,6 +11457,7 @@ ${textContent}` };
|
|
|
11363
11457
|
yield pushEvent({
|
|
11364
11458
|
type: "tool:error",
|
|
11365
11459
|
tool: result2.tool,
|
|
11460
|
+
toolCallId: result2.callId,
|
|
11366
11461
|
error: result2.error,
|
|
11367
11462
|
recoverable: true
|
|
11368
11463
|
});
|
|
@@ -11404,6 +11499,7 @@ ${textContent}` };
|
|
|
11404
11499
|
yield pushEvent({
|
|
11405
11500
|
type: "tool:completed",
|
|
11406
11501
|
tool: result2.tool,
|
|
11502
|
+
toolCallId: result2.callId,
|
|
11407
11503
|
input: callInputMap.get(result2.callId),
|
|
11408
11504
|
output: result2.output,
|
|
11409
11505
|
duration: now() - batchStart,
|
|
@@ -12826,11 +12922,14 @@ var AgentOrchestrator = class {
|
|
|
12826
12922
|
result: { status: "completed", response: responseText, steps: 0, tokens: { input: 0, output: 0, cached: 0 }, duration: 0 },
|
|
12827
12923
|
timestamp: Date.now()
|
|
12828
12924
|
};
|
|
12829
|
-
await this.
|
|
12925
|
+
await this.appendSubagentResultReliable(conv.parentConversationId, pendingResult);
|
|
12830
12926
|
await this.eventSink(conv.parentConversationId, {
|
|
12831
12927
|
type: "subagent:completed",
|
|
12832
12928
|
subagentId,
|
|
12833
|
-
conversationId: subagentId
|
|
12929
|
+
conversationId: subagentId,
|
|
12930
|
+
task: conv.subagentMeta?.task ?? conv.title,
|
|
12931
|
+
parentToolCallId: conv.subagentMeta?.parentToolCallId,
|
|
12932
|
+
resultText: responseText
|
|
12834
12933
|
});
|
|
12835
12934
|
await this.triggerParentCallback(conv.parentConversationId);
|
|
12836
12935
|
}
|
|
@@ -12906,9 +13005,11 @@ var AgentOrchestrator = class {
|
|
|
12906
13005
|
let latestRunId = "";
|
|
12907
13006
|
let runResult;
|
|
12908
13007
|
let runError;
|
|
13008
|
+
let parentToolCallId;
|
|
12909
13009
|
try {
|
|
12910
13010
|
const conversation = await this.conversationStore.getWithArchive(childConversationId);
|
|
12911
13011
|
if (!conversation) throw new Error("Subagent conversation not found");
|
|
13012
|
+
parentToolCallId = conversation.subagentMeta?.parentToolCallId;
|
|
12912
13013
|
if (conversation.subagentMeta?.status === "stopped") return;
|
|
12913
13014
|
conversation.lastActivityAt = Date.now();
|
|
12914
13015
|
await this.conversationStore.update(conversation);
|
|
@@ -13126,7 +13227,10 @@ var AgentOrchestrator = class {
|
|
|
13126
13227
|
await this.eventSink(parentConversationId, {
|
|
13127
13228
|
type: "subagent:completed",
|
|
13128
13229
|
subagentId: childConversationId,
|
|
13129
|
-
conversationId: childConversationId
|
|
13230
|
+
conversationId: childConversationId,
|
|
13231
|
+
task,
|
|
13232
|
+
parentToolCallId,
|
|
13233
|
+
resultText: subagentResponse
|
|
13130
13234
|
});
|
|
13131
13235
|
this.triggerParentCallback(parentConversationId).catch(
|
|
13132
13236
|
(err) => console.error(`[poncho][subagent] Parent callback failed:`, err instanceof Error ? err.message : err)
|
|
@@ -13157,7 +13261,9 @@ var AgentOrchestrator = class {
|
|
|
13157
13261
|
type: "subagent:error",
|
|
13158
13262
|
subagentId: childConversationId,
|
|
13159
13263
|
conversationId: childConversationId,
|
|
13160
|
-
error: errMsg
|
|
13264
|
+
error: errMsg,
|
|
13265
|
+
task,
|
|
13266
|
+
parentToolCallId
|
|
13161
13267
|
});
|
|
13162
13268
|
this.triggerParentCallback(parentConversationId).catch(
|
|
13163
13269
|
(err2) => console.error(`[poncho][subagent] Parent callback failed:`, err2 instanceof Error ? err2.message : err2)
|
|
@@ -13486,7 +13592,10 @@ ${resultBody}`,
|
|
|
13486
13592
|
await this.eventSink(parentConversationId, {
|
|
13487
13593
|
type: "subagent:completed",
|
|
13488
13594
|
subagentId: conversationId,
|
|
13489
|
-
conversationId
|
|
13595
|
+
conversationId,
|
|
13596
|
+
task,
|
|
13597
|
+
parentToolCallId: conversation.subagentMeta?.parentToolCallId,
|
|
13598
|
+
resultText: subagentResponse
|
|
13490
13599
|
});
|
|
13491
13600
|
if (parentConv) {
|
|
13492
13601
|
if (this.isServerless) {
|
|
@@ -13530,7 +13639,9 @@ ${resultBody}`,
|
|
|
13530
13639
|
await this.eventSink(conversation.parentConversationId, {
|
|
13531
13640
|
type: "subagent:completed",
|
|
13532
13641
|
subagentId: conversationId,
|
|
13533
|
-
conversationId
|
|
13642
|
+
conversationId,
|
|
13643
|
+
task,
|
|
13644
|
+
parentToolCallId: conversation.subagentMeta?.parentToolCallId
|
|
13534
13645
|
});
|
|
13535
13646
|
if (parentConv) {
|
|
13536
13647
|
if (this.isServerless) {
|
|
@@ -13560,7 +13671,7 @@ ${resultBody}`,
|
|
|
13560
13671
|
opts.tenantId ?? null,
|
|
13561
13672
|
{
|
|
13562
13673
|
parentConversationId: opts.parentConversationId,
|
|
13563
|
-
subagentMeta: { task: opts.task, status: "running", suppressTelemetry: opts.suppressTelemetry },
|
|
13674
|
+
subagentMeta: { task: opts.task, status: "running", suppressTelemetry: opts.suppressTelemetry, parentToolCallId: opts.parentToolCallId },
|
|
13564
13675
|
messages: [{ role: "user", content: opts.task }]
|
|
13565
13676
|
}
|
|
13566
13677
|
);
|
|
@@ -13572,7 +13683,8 @@ ${resultBody}`,
|
|
|
13572
13683
|
type: "subagent:spawned",
|
|
13573
13684
|
subagentId: conversation.conversationId,
|
|
13574
13685
|
conversationId: conversation.conversationId,
|
|
13575
|
-
task: opts.task
|
|
13686
|
+
task: opts.task,
|
|
13687
|
+
parentToolCallId: opts.parentToolCallId
|
|
13576
13688
|
});
|
|
13577
13689
|
if (this.isServerless) {
|
|
13578
13690
|
this.hooks.dispatchBackground("subagent-run", conversation.conversationId);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@poncho-ai/harness",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.55.0",
|
|
4
4
|
"description": "Agent execution runtime - conversation loop, tool dispatch, streaming",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"mustache": "^4.2.0",
|
|
35
35
|
"yaml": "^2.4.0",
|
|
36
36
|
"zod": "^3.22.0",
|
|
37
|
-
"@poncho-ai/sdk": "1.
|
|
37
|
+
"@poncho-ai/sdk": "1.15.0"
|
|
38
38
|
},
|
|
39
39
|
"peerDependencies": {
|
|
40
40
|
"esbuild": ">=0.17.0",
|