@poncho-ai/harness 0.16.1 → 0.18.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 +25 -0
- package/dist/index.d.ts +59 -14
- package/dist/index.js +274 -29
- package/package.json +2 -2
- package/src/harness.ts +50 -25
- package/src/index.ts +2 -0
- package/src/latitude-capture.ts +3 -13
- package/src/mcp.ts +11 -1
- package/src/state.ts +123 -5
- package/src/subagent-manager.ts +30 -0
- package/src/subagent-tools.ts +160 -0
- package/test/mcp.test.ts +83 -0
- package/.turbo/turbo-lint.log +0 -6
- package/.turbo/turbo-test.log +0 -139
package/dist/index.js
CHANGED
|
@@ -466,7 +466,7 @@ var createWriteTool = (workingDir) => defineTool({
|
|
|
466
466
|
|
|
467
467
|
// src/harness.ts
|
|
468
468
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
469
|
-
import { getTextContent } from "@poncho-ai/sdk";
|
|
469
|
+
import { getTextContent as getTextContent2 } from "@poncho-ai/sdk";
|
|
470
470
|
|
|
471
471
|
// src/upload-store.ts
|
|
472
472
|
import { createHash as createHash2 } from "crypto";
|
|
@@ -1306,16 +1306,19 @@ var StreamableHttpMcpRpcClient = class {
|
|
|
1306
1306
|
endpoint;
|
|
1307
1307
|
timeoutMs;
|
|
1308
1308
|
bearerToken;
|
|
1309
|
+
customHeaders;
|
|
1309
1310
|
idCounter = 1;
|
|
1310
1311
|
initialized = false;
|
|
1311
1312
|
sessionId;
|
|
1312
|
-
constructor(endpoint, timeoutMs = 1e4, bearerToken) {
|
|
1313
|
+
constructor(endpoint, timeoutMs = 1e4, bearerToken, customHeaders) {
|
|
1313
1314
|
this.endpoint = endpoint;
|
|
1314
1315
|
this.timeoutMs = timeoutMs;
|
|
1315
1316
|
this.bearerToken = bearerToken;
|
|
1317
|
+
this.customHeaders = customHeaders ?? {};
|
|
1316
1318
|
}
|
|
1317
1319
|
buildHeaders(accept) {
|
|
1318
1320
|
const headers = {
|
|
1321
|
+
...this.customHeaders,
|
|
1319
1322
|
"Content-Type": "application/json",
|
|
1320
1323
|
Accept: accept
|
|
1321
1324
|
};
|
|
@@ -1596,7 +1599,8 @@ var LocalMcpBridge = class {
|
|
|
1596
1599
|
new StreamableHttpMcpRpcClient(
|
|
1597
1600
|
server.url,
|
|
1598
1601
|
server.timeoutMs ?? 1e4,
|
|
1599
|
-
server.auth?.tokenEnv ? process.env[server.auth.tokenEnv] : void 0
|
|
1602
|
+
server.auth?.tokenEnv ? process.env[server.auth.tokenEnv] : void 0,
|
|
1603
|
+
server.headers
|
|
1600
1604
|
)
|
|
1601
1605
|
);
|
|
1602
1606
|
}
|
|
@@ -2447,6 +2451,138 @@ var extractRunnableFunction = (value) => {
|
|
|
2447
2451
|
return void 0;
|
|
2448
2452
|
};
|
|
2449
2453
|
|
|
2454
|
+
// src/subagent-tools.ts
|
|
2455
|
+
import { defineTool as defineTool4, getTextContent } from "@poncho-ai/sdk";
|
|
2456
|
+
var LAST_MESSAGES_TO_RETURN = 10;
|
|
2457
|
+
var summarizeResult = (r) => {
|
|
2458
|
+
const summary = {
|
|
2459
|
+
subagentId: r.subagentId,
|
|
2460
|
+
status: r.status
|
|
2461
|
+
};
|
|
2462
|
+
if (r.result) {
|
|
2463
|
+
summary.result = {
|
|
2464
|
+
status: r.result.status,
|
|
2465
|
+
response: r.result.response,
|
|
2466
|
+
steps: r.result.steps,
|
|
2467
|
+
duration: r.result.duration
|
|
2468
|
+
};
|
|
2469
|
+
}
|
|
2470
|
+
if (r.error) {
|
|
2471
|
+
summary.error = r.error;
|
|
2472
|
+
}
|
|
2473
|
+
if (r.latestMessages && r.latestMessages.length > 0) {
|
|
2474
|
+
summary.latestMessages = r.latestMessages.slice(-LAST_MESSAGES_TO_RETURN).map((m) => ({
|
|
2475
|
+
role: m.role,
|
|
2476
|
+
content: getTextContent(m).slice(0, 2e3)
|
|
2477
|
+
}));
|
|
2478
|
+
}
|
|
2479
|
+
return summary;
|
|
2480
|
+
};
|
|
2481
|
+
var createSubagentTools = (manager, getConversationId, getOwnerId) => [
|
|
2482
|
+
defineTool4({
|
|
2483
|
+
name: "spawn_subagent",
|
|
2484
|
+
description: "Spawn a subagent to work on a task and wait for it to finish. The subagent is a full copy of yourself running in its own conversation context with access to the same tools (except memory writes). This call blocks until the subagent completes and returns its result.\n\nGuidelines:\n- Use subagents to parallelize work: call spawn_subagent multiple times in one response for independent sub-tasks -- they run concurrently.\n- Prefer doing work yourself for simple or quick tasks. Spawn subagents for substantial, self-contained work.\n- The subagent has no memory of your conversation -- write thorough, self-contained instructions in the task.",
|
|
2485
|
+
inputSchema: {
|
|
2486
|
+
type: "object",
|
|
2487
|
+
properties: {
|
|
2488
|
+
task: {
|
|
2489
|
+
type: "string",
|
|
2490
|
+
description: "Thorough, self-contained instructions for the subagent. Include all relevant context, goals, and constraints -- the subagent starts with zero prior conversation history."
|
|
2491
|
+
}
|
|
2492
|
+
},
|
|
2493
|
+
required: ["task"],
|
|
2494
|
+
additionalProperties: false
|
|
2495
|
+
},
|
|
2496
|
+
handler: async (input) => {
|
|
2497
|
+
const task = typeof input.task === "string" ? input.task : "";
|
|
2498
|
+
if (!task.trim()) {
|
|
2499
|
+
return { error: "task is required" };
|
|
2500
|
+
}
|
|
2501
|
+
const conversationId = getConversationId();
|
|
2502
|
+
if (!conversationId) {
|
|
2503
|
+
return { error: "no active conversation to spawn subagent from" };
|
|
2504
|
+
}
|
|
2505
|
+
const result = await manager.spawn({
|
|
2506
|
+
task: task.trim(),
|
|
2507
|
+
parentConversationId: conversationId,
|
|
2508
|
+
ownerId: getOwnerId()
|
|
2509
|
+
});
|
|
2510
|
+
return summarizeResult(result);
|
|
2511
|
+
}
|
|
2512
|
+
}),
|
|
2513
|
+
defineTool4({
|
|
2514
|
+
name: "message_subagent",
|
|
2515
|
+
description: "Send a follow-up message to a completed or stopped subagent and wait for it to finish. This restarts the subagent with the new message and blocks until it completes. Only works when the subagent is not currently running.",
|
|
2516
|
+
inputSchema: {
|
|
2517
|
+
type: "object",
|
|
2518
|
+
properties: {
|
|
2519
|
+
subagent_id: {
|
|
2520
|
+
type: "string",
|
|
2521
|
+
description: "The subagent ID (from spawn_subagent result or list_subagents)."
|
|
2522
|
+
},
|
|
2523
|
+
message: {
|
|
2524
|
+
type: "string",
|
|
2525
|
+
description: "The follow-up instructions or message to send."
|
|
2526
|
+
}
|
|
2527
|
+
},
|
|
2528
|
+
required: ["subagent_id", "message"],
|
|
2529
|
+
additionalProperties: false
|
|
2530
|
+
},
|
|
2531
|
+
handler: async (input) => {
|
|
2532
|
+
const subagentId = typeof input.subagent_id === "string" ? input.subagent_id : "";
|
|
2533
|
+
const message = typeof input.message === "string" ? input.message : "";
|
|
2534
|
+
if (!subagentId || !message.trim()) {
|
|
2535
|
+
return { error: "subagent_id and message are required" };
|
|
2536
|
+
}
|
|
2537
|
+
const result = await manager.sendMessage(subagentId, message.trim());
|
|
2538
|
+
return summarizeResult(result);
|
|
2539
|
+
}
|
|
2540
|
+
}),
|
|
2541
|
+
defineTool4({
|
|
2542
|
+
name: "stop_subagent",
|
|
2543
|
+
description: "Stop a running subagent. The subagent's conversation is preserved but it will stop processing. Use this to cancel work that is no longer needed.",
|
|
2544
|
+
inputSchema: {
|
|
2545
|
+
type: "object",
|
|
2546
|
+
properties: {
|
|
2547
|
+
subagent_id: {
|
|
2548
|
+
type: "string",
|
|
2549
|
+
description: "The subagent ID (from spawn_subagent result or list_subagents)."
|
|
2550
|
+
}
|
|
2551
|
+
},
|
|
2552
|
+
required: ["subagent_id"],
|
|
2553
|
+
additionalProperties: false
|
|
2554
|
+
},
|
|
2555
|
+
handler: async (input) => {
|
|
2556
|
+
const subagentId = typeof input.subagent_id === "string" ? input.subagent_id : "";
|
|
2557
|
+
if (!subagentId) {
|
|
2558
|
+
return { error: "subagent_id is required" };
|
|
2559
|
+
}
|
|
2560
|
+
await manager.stop(subagentId);
|
|
2561
|
+
return { message: `Subagent "${subagentId}" has been stopped.` };
|
|
2562
|
+
}
|
|
2563
|
+
}),
|
|
2564
|
+
defineTool4({
|
|
2565
|
+
name: "list_subagents",
|
|
2566
|
+
description: "List all subagents that have been spawned in this conversation. Returns each subagent's ID, original task, current status, and message count. Use this to look up subagent IDs before calling message_subagent or stop_subagent.",
|
|
2567
|
+
inputSchema: {
|
|
2568
|
+
type: "object",
|
|
2569
|
+
properties: {},
|
|
2570
|
+
additionalProperties: false
|
|
2571
|
+
},
|
|
2572
|
+
handler: async () => {
|
|
2573
|
+
const conversationId = getConversationId();
|
|
2574
|
+
if (!conversationId) {
|
|
2575
|
+
return { error: "no active conversation" };
|
|
2576
|
+
}
|
|
2577
|
+
const subagents = await manager.list(conversationId);
|
|
2578
|
+
if (subagents.length === 0) {
|
|
2579
|
+
return { message: "No subagents have been spawned in this conversation." };
|
|
2580
|
+
}
|
|
2581
|
+
return { subagents };
|
|
2582
|
+
}
|
|
2583
|
+
})
|
|
2584
|
+
];
|
|
2585
|
+
|
|
2450
2586
|
// src/harness.ts
|
|
2451
2587
|
import { LatitudeTelemetry } from "@latitude-data/telemetry";
|
|
2452
2588
|
import { diag, DiagLogLevel } from "@opentelemetry/api";
|
|
@@ -2928,6 +3064,7 @@ var AgentHarness = class {
|
|
|
2928
3064
|
_browserMod;
|
|
2929
3065
|
parsedAgent;
|
|
2930
3066
|
mcpBridge;
|
|
3067
|
+
subagentManager;
|
|
2931
3068
|
resolveToolAccess(toolName) {
|
|
2932
3069
|
const tools = this.loadedConfig?.tools;
|
|
2933
3070
|
if (!tools) return true;
|
|
@@ -2964,6 +3101,19 @@ var AgentHarness = class {
|
|
|
2964
3101
|
this.dispatcher.register(tool);
|
|
2965
3102
|
}
|
|
2966
3103
|
}
|
|
3104
|
+
unregisterTools(names) {
|
|
3105
|
+
this.dispatcher.unregisterMany(names);
|
|
3106
|
+
}
|
|
3107
|
+
setSubagentManager(manager) {
|
|
3108
|
+
this.subagentManager = manager;
|
|
3109
|
+
this.dispatcher.registerMany(
|
|
3110
|
+
createSubagentTools(
|
|
3111
|
+
manager,
|
|
3112
|
+
() => this._currentRunConversationId,
|
|
3113
|
+
() => this._currentRunOwnerId ?? "anonymous"
|
|
3114
|
+
)
|
|
3115
|
+
);
|
|
3116
|
+
}
|
|
2967
3117
|
registerConfiguredBuiltInTools(config) {
|
|
2968
3118
|
for (const tool of createDefaultTools(this.workingDir)) {
|
|
2969
3119
|
if (this.isToolEnabled(tool.name)) {
|
|
@@ -3418,6 +3568,8 @@ var AgentHarness = class {
|
|
|
3418
3568
|
}
|
|
3419
3569
|
/** Conversation ID of the currently executing run (set during run, cleared after). */
|
|
3420
3570
|
_currentRunConversationId;
|
|
3571
|
+
/** Owner ID of the currently executing run (used by subagent tools). */
|
|
3572
|
+
_currentRunOwnerId;
|
|
3421
3573
|
get browserSession() {
|
|
3422
3574
|
return this._browserSession;
|
|
3423
3575
|
}
|
|
@@ -3515,6 +3667,10 @@ var AgentHarness = class {
|
|
|
3515
3667
|
}
|
|
3516
3668
|
await this.refreshSkillsIfChanged();
|
|
3517
3669
|
this._currentRunConversationId = input.conversationId;
|
|
3670
|
+
const ownerParam = input.parameters?.__ownerId;
|
|
3671
|
+
if (typeof ownerParam === "string") {
|
|
3672
|
+
this._currentRunOwnerId = ownerParam;
|
|
3673
|
+
}
|
|
3518
3674
|
const agent = this.parsedAgent;
|
|
3519
3675
|
const runId = `run_${randomUUID3()}`;
|
|
3520
3676
|
const start = now();
|
|
@@ -3715,7 +3871,7 @@ ${boundedMainMemory.trim()}` : "";
|
|
|
3715
3871
|
if (rich && rich.length > 0) {
|
|
3716
3872
|
return [{ role: "tool", content: rich }];
|
|
3717
3873
|
}
|
|
3718
|
-
const textContent = typeof msg.content === "string" ? msg.content :
|
|
3874
|
+
const textContent = typeof msg.content === "string" ? msg.content : getTextContent2(msg);
|
|
3719
3875
|
try {
|
|
3720
3876
|
const parsed = JSON.parse(textContent);
|
|
3721
3877
|
if (!Array.isArray(parsed)) {
|
|
@@ -3765,7 +3921,7 @@ ${boundedMainMemory.trim()}` : "";
|
|
|
3765
3921
|
}
|
|
3766
3922
|
}
|
|
3767
3923
|
if (msg.role === "assistant") {
|
|
3768
|
-
const assistantText = typeof msg.content === "string" ? msg.content :
|
|
3924
|
+
const assistantText = typeof msg.content === "string" ? msg.content : getTextContent2(msg);
|
|
3769
3925
|
try {
|
|
3770
3926
|
const parsed = JSON.parse(assistantText);
|
|
3771
3927
|
if (typeof parsed === "object" && parsed !== null) {
|
|
@@ -3804,7 +3960,7 @@ ${boundedMainMemory.trim()}` : "";
|
|
|
3804
3960
|
if (msg.role === "system") {
|
|
3805
3961
|
return [{
|
|
3806
3962
|
role: "system",
|
|
3807
|
-
content: typeof msg.content === "string" ? msg.content :
|
|
3963
|
+
content: typeof msg.content === "string" ? msg.content : getTextContent2(msg)
|
|
3808
3964
|
}];
|
|
3809
3965
|
}
|
|
3810
3966
|
if (msg.role === "user") {
|
|
@@ -3911,7 +4067,7 @@ ${textContent}` };
|
|
|
3911
4067
|
let chunkCount = 0;
|
|
3912
4068
|
const hasRunTimeout = timeoutMs > 0;
|
|
3913
4069
|
const streamDeadline = hasRunTimeout ? start + timeoutMs : 0;
|
|
3914
|
-
const
|
|
4070
|
+
const fullStreamIterator = result.fullStream[Symbol.asyncIterator]();
|
|
3915
4071
|
try {
|
|
3916
4072
|
while (true) {
|
|
3917
4073
|
if (isCancelled()) {
|
|
@@ -3937,20 +4093,20 @@ ${textContent}` };
|
|
|
3937
4093
|
}
|
|
3938
4094
|
const remaining = hasRunTimeout ? streamDeadline - now() : Infinity;
|
|
3939
4095
|
const timeout = chunkCount === 0 ? Math.min(remaining, FIRST_CHUNK_TIMEOUT_MS) : hasRunTimeout ? remaining : 0;
|
|
3940
|
-
let
|
|
4096
|
+
let nextPart;
|
|
3941
4097
|
if (timeout <= 0 && chunkCount > 0) {
|
|
3942
|
-
|
|
4098
|
+
nextPart = await fullStreamIterator.next();
|
|
3943
4099
|
} else {
|
|
3944
4100
|
let timer;
|
|
3945
|
-
|
|
3946
|
-
|
|
4101
|
+
nextPart = await Promise.race([
|
|
4102
|
+
fullStreamIterator.next(),
|
|
3947
4103
|
new Promise((resolve10) => {
|
|
3948
4104
|
timer = setTimeout(() => resolve10(null), timeout);
|
|
3949
4105
|
})
|
|
3950
4106
|
]);
|
|
3951
4107
|
clearTimeout(timer);
|
|
3952
4108
|
}
|
|
3953
|
-
if (
|
|
4109
|
+
if (nextPart === null) {
|
|
3954
4110
|
const isFirstChunk = chunkCount === 0;
|
|
3955
4111
|
console.error(
|
|
3956
4112
|
`[poncho][harness] Stream timeout waiting for ${isFirstChunk ? "first" : "next"} chunk: model="${modelName}", step=${step}, chunks=${chunkCount}, elapsed=${now() - start}ms`
|
|
@@ -3968,13 +4124,19 @@ ${textContent}` };
|
|
|
3968
4124
|
});
|
|
3969
4125
|
return;
|
|
3970
4126
|
}
|
|
3971
|
-
if (
|
|
3972
|
-
|
|
3973
|
-
|
|
3974
|
-
|
|
4127
|
+
if (nextPart.done) break;
|
|
4128
|
+
const part = nextPart.value;
|
|
4129
|
+
if (part.type === "text-delta") {
|
|
4130
|
+
chunkCount += 1;
|
|
4131
|
+
fullText += part.text;
|
|
4132
|
+
yield pushEvent({ type: "model:chunk", content: part.text });
|
|
4133
|
+
} else if (part.type === "tool-input-start") {
|
|
4134
|
+
chunkCount += 1;
|
|
4135
|
+
yield pushEvent({ type: "tool:generating", tool: part.toolName, toolCallId: part.id });
|
|
4136
|
+
}
|
|
3975
4137
|
}
|
|
3976
4138
|
} finally {
|
|
3977
|
-
|
|
4139
|
+
fullStreamIterator.return?.(void 0)?.catch?.(() => {
|
|
3978
4140
|
});
|
|
3979
4141
|
}
|
|
3980
4142
|
if (isCancelled()) {
|
|
@@ -4074,7 +4236,8 @@ ${textContent}` };
|
|
|
4074
4236
|
step,
|
|
4075
4237
|
workingDir: this.workingDir,
|
|
4076
4238
|
parameters: input.parameters ?? {},
|
|
4077
|
-
abortSignal: input.abortSignal
|
|
4239
|
+
abortSignal: input.abortSignal,
|
|
4240
|
+
conversationId: input.conversationId
|
|
4078
4241
|
};
|
|
4079
4242
|
const toolResultsForModel = [];
|
|
4080
4243
|
const richToolResults = [];
|
|
@@ -4179,11 +4342,14 @@ ${textContent}` };
|
|
|
4179
4342
|
});
|
|
4180
4343
|
} else {
|
|
4181
4344
|
span?.end({ result: { value: result2.output ?? null, isError: false } });
|
|
4345
|
+
const serialized = JSON.stringify(result2.output ?? null);
|
|
4346
|
+
const outputTokenEstimate = Math.ceil(serialized.length / 4);
|
|
4182
4347
|
yield pushEvent({
|
|
4183
4348
|
type: "tool:completed",
|
|
4184
4349
|
tool: result2.tool,
|
|
4185
4350
|
output: result2.output,
|
|
4186
|
-
duration: now() - batchStart
|
|
4351
|
+
duration: now() - batchStart,
|
|
4352
|
+
outputTokenEstimate
|
|
4187
4353
|
});
|
|
4188
4354
|
const { mediaItems, strippedOutput } = extractMediaFromToolOutput(result2.output);
|
|
4189
4355
|
toolResultsForModel.push({
|
|
@@ -4537,9 +4703,22 @@ var InMemoryConversationStore = class {
|
|
|
4537
4703
|
}
|
|
4538
4704
|
}
|
|
4539
4705
|
}
|
|
4540
|
-
async list(ownerId
|
|
4706
|
+
async list(ownerId) {
|
|
4541
4707
|
this.purgeExpired();
|
|
4542
|
-
return Array.from(this.conversations.values()).filter((conversation) => conversation.ownerId === ownerId).sort((a, b) => b.updatedAt - a.updatedAt);
|
|
4708
|
+
return Array.from(this.conversations.values()).filter((conversation) => !ownerId || conversation.ownerId === ownerId).sort((a, b) => b.updatedAt - a.updatedAt);
|
|
4709
|
+
}
|
|
4710
|
+
async listSummaries(ownerId) {
|
|
4711
|
+
this.purgeExpired();
|
|
4712
|
+
return Array.from(this.conversations.values()).filter((c) => !ownerId || c.ownerId === ownerId).sort((a, b) => b.updatedAt - a.updatedAt).map((c) => ({
|
|
4713
|
+
conversationId: c.conversationId,
|
|
4714
|
+
title: c.title,
|
|
4715
|
+
updatedAt: c.updatedAt,
|
|
4716
|
+
createdAt: c.createdAt,
|
|
4717
|
+
ownerId: c.ownerId,
|
|
4718
|
+
parentConversationId: c.parentConversationId,
|
|
4719
|
+
messageCount: c.messages.length,
|
|
4720
|
+
hasPendingApprovals: Array.isArray(c.pendingApprovals) && c.pendingApprovals.length > 0
|
|
4721
|
+
}));
|
|
4543
4722
|
}
|
|
4544
4723
|
async get(conversationId) {
|
|
4545
4724
|
this.purgeExpired();
|
|
@@ -4642,8 +4821,12 @@ var FileConversationStore = class {
|
|
|
4642
4821
|
conversationId: conversation.conversationId,
|
|
4643
4822
|
title: conversation.title,
|
|
4644
4823
|
updatedAt: conversation.updatedAt,
|
|
4824
|
+
createdAt: conversation.createdAt,
|
|
4645
4825
|
ownerId: conversation.ownerId,
|
|
4646
|
-
fileName: entry.name
|
|
4826
|
+
fileName: entry.name,
|
|
4827
|
+
parentConversationId: conversation.parentConversationId,
|
|
4828
|
+
messageCount: conversation.messages.length,
|
|
4829
|
+
hasPendingApprovals: Array.isArray(conversation.pendingApprovals) && conversation.pendingApprovals.length > 0
|
|
4647
4830
|
});
|
|
4648
4831
|
}
|
|
4649
4832
|
} catch {
|
|
@@ -4666,6 +4849,16 @@ var FileConversationStore = class {
|
|
|
4666
4849
|
for (const conversation of parsed.conversations ?? []) {
|
|
4667
4850
|
this.conversations.set(conversation.conversationId, conversation);
|
|
4668
4851
|
}
|
|
4852
|
+
let needsRebuild = false;
|
|
4853
|
+
for (const entry of this.conversations.values()) {
|
|
4854
|
+
if (entry.messageCount === void 0) {
|
|
4855
|
+
needsRebuild = true;
|
|
4856
|
+
break;
|
|
4857
|
+
}
|
|
4858
|
+
}
|
|
4859
|
+
if (needsRebuild) {
|
|
4860
|
+
await this.rebuildIndexFromFiles();
|
|
4861
|
+
}
|
|
4669
4862
|
} catch {
|
|
4670
4863
|
await this.rebuildIndexFromFiles();
|
|
4671
4864
|
}
|
|
@@ -4681,16 +4874,20 @@ var FileConversationStore = class {
|
|
|
4681
4874
|
conversationId: conversation.conversationId,
|
|
4682
4875
|
title: conversation.title,
|
|
4683
4876
|
updatedAt: conversation.updatedAt,
|
|
4877
|
+
createdAt: conversation.createdAt,
|
|
4684
4878
|
ownerId: conversation.ownerId,
|
|
4685
|
-
fileName
|
|
4879
|
+
fileName,
|
|
4880
|
+
parentConversationId: conversation.parentConversationId,
|
|
4881
|
+
messageCount: conversation.messages.length,
|
|
4882
|
+
hasPendingApprovals: Array.isArray(conversation.pendingApprovals) && conversation.pendingApprovals.length > 0
|
|
4686
4883
|
});
|
|
4687
4884
|
await this.writeIndex();
|
|
4688
4885
|
});
|
|
4689
4886
|
await this.writing;
|
|
4690
4887
|
}
|
|
4691
|
-
async list(ownerId
|
|
4888
|
+
async list(ownerId) {
|
|
4692
4889
|
await this.ensureLoaded();
|
|
4693
|
-
const summaries = Array.from(this.conversations.values()).filter((conversation) => conversation.ownerId === ownerId).sort((a, b) => b.updatedAt - a.updatedAt);
|
|
4890
|
+
const summaries = Array.from(this.conversations.values()).filter((conversation) => !ownerId || conversation.ownerId === ownerId).sort((a, b) => b.updatedAt - a.updatedAt);
|
|
4694
4891
|
const conversations = [];
|
|
4695
4892
|
for (const summary of summaries) {
|
|
4696
4893
|
const loaded = await this.readConversationFile(summary.fileName);
|
|
@@ -4700,6 +4897,19 @@ var FileConversationStore = class {
|
|
|
4700
4897
|
}
|
|
4701
4898
|
return conversations;
|
|
4702
4899
|
}
|
|
4900
|
+
async listSummaries(ownerId) {
|
|
4901
|
+
await this.ensureLoaded();
|
|
4902
|
+
return Array.from(this.conversations.values()).filter((c) => !ownerId || c.ownerId === ownerId).sort((a, b) => b.updatedAt - a.updatedAt).map((c) => ({
|
|
4903
|
+
conversationId: c.conversationId,
|
|
4904
|
+
title: c.title,
|
|
4905
|
+
updatedAt: c.updatedAt,
|
|
4906
|
+
createdAt: c.createdAt,
|
|
4907
|
+
ownerId: c.ownerId,
|
|
4908
|
+
parentConversationId: c.parentConversationId,
|
|
4909
|
+
messageCount: c.messageCount,
|
|
4910
|
+
hasPendingApprovals: c.hasPendingApprovals
|
|
4911
|
+
}));
|
|
4912
|
+
}
|
|
4703
4913
|
async get(conversationId) {
|
|
4704
4914
|
await this.ensureLoaded();
|
|
4705
4915
|
const summary = this.conversations.get(conversationId);
|
|
@@ -4915,11 +5125,14 @@ var KeyValueConversationStoreBase = class {
|
|
|
4915
5125
|
return void 0;
|
|
4916
5126
|
}
|
|
4917
5127
|
}
|
|
4918
|
-
async list(ownerId
|
|
5128
|
+
async list(ownerId) {
|
|
4919
5129
|
const kv = await this.client();
|
|
4920
5130
|
if (!kv) {
|
|
4921
5131
|
return await this.memoryFallback.list(ownerId);
|
|
4922
5132
|
}
|
|
5133
|
+
if (!ownerId) {
|
|
5134
|
+
return [];
|
|
5135
|
+
}
|
|
4923
5136
|
const ids = await this.getOwnerConversationIds(ownerId);
|
|
4924
5137
|
const conversations = [];
|
|
4925
5138
|
for (const id of ids) {
|
|
@@ -4934,6 +5147,33 @@ var KeyValueConversationStoreBase = class {
|
|
|
4934
5147
|
}
|
|
4935
5148
|
return conversations.sort((a, b) => b.updatedAt - a.updatedAt);
|
|
4936
5149
|
}
|
|
5150
|
+
async listSummaries(ownerId) {
|
|
5151
|
+
const kv = await this.client();
|
|
5152
|
+
if (!kv) {
|
|
5153
|
+
return await this.memoryFallback.listSummaries(ownerId);
|
|
5154
|
+
}
|
|
5155
|
+
if (!ownerId) {
|
|
5156
|
+
return [];
|
|
5157
|
+
}
|
|
5158
|
+
const ids = await this.getOwnerConversationIds(ownerId);
|
|
5159
|
+
const summaries = [];
|
|
5160
|
+
for (const id of ids) {
|
|
5161
|
+
const meta = await this.getConversationMeta(id);
|
|
5162
|
+
if (meta && meta.ownerId === ownerId) {
|
|
5163
|
+
summaries.push({
|
|
5164
|
+
conversationId: meta.conversationId,
|
|
5165
|
+
title: meta.title,
|
|
5166
|
+
updatedAt: meta.updatedAt,
|
|
5167
|
+
createdAt: meta.createdAt,
|
|
5168
|
+
ownerId: meta.ownerId,
|
|
5169
|
+
parentConversationId: meta.parentConversationId,
|
|
5170
|
+
messageCount: meta.messageCount,
|
|
5171
|
+
hasPendingApprovals: meta.hasPendingApprovals
|
|
5172
|
+
});
|
|
5173
|
+
}
|
|
5174
|
+
}
|
|
5175
|
+
return summaries.sort((a, b) => b.updatedAt - a.updatedAt);
|
|
5176
|
+
}
|
|
4937
5177
|
async get(conversationId) {
|
|
4938
5178
|
const kv = await this.client();
|
|
4939
5179
|
if (!kv) {
|
|
@@ -4983,7 +5223,11 @@ var KeyValueConversationStoreBase = class {
|
|
|
4983
5223
|
conversationId: nextConversation.conversationId,
|
|
4984
5224
|
title: nextConversation.title,
|
|
4985
5225
|
updatedAt: nextConversation.updatedAt,
|
|
4986
|
-
|
|
5226
|
+
createdAt: nextConversation.createdAt,
|
|
5227
|
+
ownerId: nextConversation.ownerId,
|
|
5228
|
+
parentConversationId: nextConversation.parentConversationId,
|
|
5229
|
+
messageCount: nextConversation.messages.length,
|
|
5230
|
+
hasPendingApprovals: Array.isArray(nextConversation.pendingApprovals) && nextConversation.pendingApprovals.length > 0
|
|
4987
5231
|
}),
|
|
4988
5232
|
this.ttl
|
|
4989
5233
|
);
|
|
@@ -5449,7 +5693,7 @@ var TelemetryEmitter = class {
|
|
|
5449
5693
|
};
|
|
5450
5694
|
|
|
5451
5695
|
// src/index.ts
|
|
5452
|
-
import { defineTool as
|
|
5696
|
+
import { defineTool as defineTool5 } from "@poncho-ai/sdk";
|
|
5453
5697
|
export {
|
|
5454
5698
|
AgentHarness,
|
|
5455
5699
|
InMemoryConversationStore,
|
|
@@ -5472,9 +5716,10 @@ export {
|
|
|
5472
5716
|
createModelProvider,
|
|
5473
5717
|
createSkillTools,
|
|
5474
5718
|
createStateStore,
|
|
5719
|
+
createSubagentTools,
|
|
5475
5720
|
createUploadStore,
|
|
5476
5721
|
createWriteTool,
|
|
5477
|
-
|
|
5722
|
+
defineTool5 as defineTool,
|
|
5478
5723
|
deriveUploadKey,
|
|
5479
5724
|
ensureAgentIdentity,
|
|
5480
5725
|
generateAgentId,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@poncho-ai/harness",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"description": "Agent execution runtime - conversation loop, tool dispatch, streaming",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"redis": "^5.10.0",
|
|
32
32
|
"yaml": "^2.4.0",
|
|
33
33
|
"zod": "^3.22.0",
|
|
34
|
-
"@poncho-ai/sdk": "1.
|
|
34
|
+
"@poncho-ai/sdk": "1.3.0"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@types/mustache": "^4.2.6",
|