@poncho-ai/harness 0.37.2 → 0.39.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 +216 -0
- package/dist/index.d.ts +397 -34
- package/dist/index.js +2228 -130
- package/package.json +2 -2
- package/src/harness.ts +48 -72
- package/src/index.ts +1 -0
- package/src/local-tools.ts +4 -3
- package/src/mcp.ts +9 -6
- package/src/orchestrator/continuation.ts +13 -0
- package/src/orchestrator/history.ts +69 -0
- package/src/orchestrator/index.ts +54 -0
- package/src/orchestrator/orchestrator.ts +1535 -0
- package/src/orchestrator/subagents.ts +27 -0
- package/src/orchestrator/turn.ts +367 -0
- package/src/reminder-store.ts +183 -16
- package/src/reminder-tools.ts +102 -6
- package/src/state.ts +127 -3
- package/src/storage/engine.ts +29 -10
- package/src/storage/memory-engine.ts +74 -10
- package/src/storage/postgres-engine.ts +1 -0
- package/src/storage/schema.ts +21 -0
- package/src/storage/sql-dialect.ts +294 -23
- package/src/storage/sqlite-engine.ts +1 -0
- package/src/storage/store-adapters.ts +17 -11
- package/src/telemetry.ts +10 -7
- package/src/upload-store.ts +7 -4
- package/src/vfs/bash-manager.ts +16 -2
- package/src/vfs/edit-file-tool.ts +9 -8
- package/src/vfs/read-file-tool.ts +14 -15
- package/src/vfs/write-file-tool.ts +6 -5
- package/test/orchestrator.test.ts +176 -0
- package/test/reminder-store.test.ts +193 -4
- package/test/state.test.ts +21 -0
- package/test/storage-engine.test.ts +80 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@poncho-ai/harness",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.39.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.9.0"
|
|
38
38
|
},
|
|
39
39
|
"peerDependencies": {
|
|
40
40
|
"esbuild": ">=0.17.0",
|
package/src/harness.ts
CHANGED
|
@@ -12,7 +12,13 @@ import type {
|
|
|
12
12
|
ToolContext,
|
|
13
13
|
ToolDefinition,
|
|
14
14
|
} from "@poncho-ai/sdk";
|
|
15
|
-
import { defineTool, getTextContent } from "@poncho-ai/sdk";
|
|
15
|
+
import { defineTool, getTextContent, createLogger, formatError as fmtErr, url as urlColor } from "@poncho-ai/sdk";
|
|
16
|
+
|
|
17
|
+
const harnessLog = createLogger("harness");
|
|
18
|
+
const telemetryLog = createLogger("telemetry");
|
|
19
|
+
const costLog = createLogger("cost");
|
|
20
|
+
const mcpLog = createLogger("mcp");
|
|
21
|
+
const modelLog = createLogger("model");
|
|
16
22
|
import type { UploadStore } from "./upload-store.js";
|
|
17
23
|
import { PONCHO_UPLOAD_SCHEME, VFS_SCHEME, deriveUploadKey } from "./upload-store.js";
|
|
18
24
|
import type { StorageEngine } from "./storage/engine.js";
|
|
@@ -812,8 +818,8 @@ export class AgentHarness {
|
|
|
812
818
|
const toolResultId = typeof input.toolResultId === "string" ? input.toolResultId : "";
|
|
813
819
|
const record = archive[toolResultId];
|
|
814
820
|
if (!record) {
|
|
815
|
-
|
|
816
|
-
`
|
|
821
|
+
costLog.debug(
|
|
822
|
+
`archived tool result miss: id="${toolResultId}" conv=${conversationId.slice(0, 8)}`,
|
|
817
823
|
);
|
|
818
824
|
return {
|
|
819
825
|
error: `No archived tool result found for id "${toolResultId}" in this conversation.`,
|
|
@@ -823,8 +829,8 @@ export class AgentHarness {
|
|
|
823
829
|
const limit = Math.min(Math.max(Number(input.limit) || 6000, 1), 20_000);
|
|
824
830
|
const end = Math.min(record.payload.length, offset + limit);
|
|
825
831
|
const chunk = record.payload.slice(offset, end);
|
|
826
|
-
|
|
827
|
-
`
|
|
832
|
+
costLog.debug(
|
|
833
|
+
`archived tool result hit: id="${toolResultId}" conv=${conversationId.slice(0, 8)} ` +
|
|
828
834
|
`offset=${offset} returned=${chunk.length} total=${record.payload.length}`,
|
|
829
835
|
);
|
|
830
836
|
return {
|
|
@@ -1267,9 +1273,7 @@ export class AgentHarness {
|
|
|
1267
1273
|
this.dispatcher.unregisterMany(this.registeredMcpToolNames);
|
|
1268
1274
|
this.registeredMcpToolNames.clear();
|
|
1269
1275
|
if (requestedPatterns.length === 0) {
|
|
1270
|
-
|
|
1271
|
-
`[poncho][mcp] ${JSON.stringify({ event: "tools.cleared", reason, requestedPatterns })}`,
|
|
1272
|
-
);
|
|
1276
|
+
mcpLog.debug(`tools cleared (reason=${reason})`);
|
|
1273
1277
|
return;
|
|
1274
1278
|
}
|
|
1275
1279
|
const tools = await this.mcpBridge.loadTools(requestedPatterns);
|
|
@@ -1277,14 +1281,8 @@ export class AgentHarness {
|
|
|
1277
1281
|
for (const tool of tools) {
|
|
1278
1282
|
this.registeredMcpToolNames.add(tool.name);
|
|
1279
1283
|
}
|
|
1280
|
-
|
|
1281
|
-
`
|
|
1282
|
-
event: "tools.refreshed",
|
|
1283
|
-
reason,
|
|
1284
|
-
requestedPatterns,
|
|
1285
|
-
registeredCount: tools.length,
|
|
1286
|
-
activeSkills: this.listActiveSkills(),
|
|
1287
|
-
})}`,
|
|
1284
|
+
mcpLog.debug(
|
|
1285
|
+
`tools refreshed (reason=${reason}, registered=${tools.length}, patterns=${requestedPatterns.length})`,
|
|
1288
1286
|
);
|
|
1289
1287
|
}
|
|
1290
1288
|
|
|
@@ -1358,11 +1356,7 @@ export class AgentHarness {
|
|
|
1358
1356
|
this.agentFileFingerprint = rawContent;
|
|
1359
1357
|
return true;
|
|
1360
1358
|
} catch (error) {
|
|
1361
|
-
|
|
1362
|
-
`[poncho][agent] Failed to refresh AGENT.md in development mode: ${
|
|
1363
|
-
error instanceof Error ? error.message : String(error)
|
|
1364
|
-
}`,
|
|
1365
|
-
);
|
|
1359
|
+
createLogger("agent").warn(`failed to refresh AGENT.md in dev: ${fmtErr(error)}`);
|
|
1366
1360
|
return false;
|
|
1367
1361
|
}
|
|
1368
1362
|
}
|
|
@@ -1417,11 +1411,7 @@ export class AgentHarness {
|
|
|
1417
1411
|
await this.refreshMcpTools("skills:changed");
|
|
1418
1412
|
return true;
|
|
1419
1413
|
} catch (error) {
|
|
1420
|
-
|
|
1421
|
-
`[poncho][skills] Failed to refresh skills in development mode: ${
|
|
1422
|
-
error instanceof Error ? error.message : String(error)
|
|
1423
|
-
}`,
|
|
1424
|
-
);
|
|
1414
|
+
createLogger("skills").warn(`failed to refresh skills in dev: ${fmtErr(error)}`);
|
|
1425
1415
|
return false;
|
|
1426
1416
|
}
|
|
1427
1417
|
}
|
|
@@ -1477,9 +1467,10 @@ export class AgentHarness {
|
|
|
1477
1467
|
);
|
|
1478
1468
|
// Register VFS tools
|
|
1479
1469
|
this.registerIfMissing(createBashTool(this.bashManager));
|
|
1480
|
-
this.
|
|
1481
|
-
this.registerIfMissing(
|
|
1482
|
-
this.registerIfMissing(
|
|
1470
|
+
const getFs = (tenantId: string) => this.bashManager!.getFs(tenantId);
|
|
1471
|
+
this.registerIfMissing(createReadFileTool(getFs));
|
|
1472
|
+
this.registerIfMissing(createEditFileTool(getFs));
|
|
1473
|
+
this.registerIfMissing(createWriteFileTool(getFs));
|
|
1483
1474
|
|
|
1484
1475
|
// --- Isolate (V8 sandboxed code execution) ---
|
|
1485
1476
|
if (config?.isolate) {
|
|
@@ -1531,9 +1522,7 @@ export class AgentHarness {
|
|
|
1531
1522
|
if (config?.browser) {
|
|
1532
1523
|
await this.initBrowserTools(config)
|
|
1533
1524
|
.catch((e) => {
|
|
1534
|
-
|
|
1535
|
-
`[poncho][browser] Failed to load browser tools: ${e instanceof Error ? e.message : String(e)}`,
|
|
1536
|
-
);
|
|
1525
|
+
createLogger("browser").warn(`failed to load browser tools: ${fmtErr(e)}`);
|
|
1537
1526
|
});
|
|
1538
1527
|
}
|
|
1539
1528
|
|
|
@@ -1568,7 +1557,7 @@ export class AgentHarness {
|
|
|
1568
1557
|
provider.register();
|
|
1569
1558
|
this.otlpTracerProvider = provider;
|
|
1570
1559
|
this.hasOtlpExporter = true;
|
|
1571
|
-
|
|
1560
|
+
telemetryLog.item(`OTLP trace exporter active → ${urlColor(otlpConfig.url)}`);
|
|
1572
1561
|
}
|
|
1573
1562
|
}
|
|
1574
1563
|
|
|
@@ -1695,13 +1684,13 @@ export class AgentHarness {
|
|
|
1695
1684
|
await this.mcpBridge?.stopLocalServers();
|
|
1696
1685
|
if (this.otlpSpanProcessor) {
|
|
1697
1686
|
await this.otlpSpanProcessor.shutdown().catch((err) => {
|
|
1698
|
-
|
|
1687
|
+
telemetryLog.warn(`OTLP span processor shutdown error: ${formatOtlpError(err)}`);
|
|
1699
1688
|
});
|
|
1700
1689
|
this.otlpSpanProcessor = undefined;
|
|
1701
1690
|
}
|
|
1702
1691
|
if (this.otlpTracerProvider) {
|
|
1703
1692
|
await this.otlpTracerProvider.shutdown().catch((err) => {
|
|
1704
|
-
|
|
1693
|
+
telemetryLog.warn(`OTLP tracer provider shutdown error: ${formatOtlpError(err)}`);
|
|
1705
1694
|
});
|
|
1706
1695
|
this.otlpTracerProvider = undefined;
|
|
1707
1696
|
}
|
|
@@ -1761,7 +1750,7 @@ export class AgentHarness {
|
|
|
1761
1750
|
await this.otlpSpanProcessor?.forceFlush();
|
|
1762
1751
|
} catch (err: unknown) {
|
|
1763
1752
|
const detail = formatOtlpError(err);
|
|
1764
|
-
|
|
1753
|
+
telemetryLog.warn(`OTLP span flush failed: ${detail}`);
|
|
1765
1754
|
}
|
|
1766
1755
|
}
|
|
1767
1756
|
} else {
|
|
@@ -1825,23 +1814,17 @@ export class AgentHarness {
|
|
|
1825
1814
|
this.seedToolResultArchive(conversationId, input.parameters);
|
|
1826
1815
|
const truncationSummary = this.truncateHistoricalToolResults(messages, conversationId);
|
|
1827
1816
|
if (truncationSummary.changed) {
|
|
1828
|
-
|
|
1829
|
-
`
|
|
1830
|
-
`(
|
|
1831
|
-
`
|
|
1817
|
+
costLog.debug(
|
|
1818
|
+
`truncated ${truncationSummary.truncatedCount} historical tool result(s) ` +
|
|
1819
|
+
`(archived=${truncationSummary.archivedCount}, omitted=${truncationSummary.omittedChars} chars) ` +
|
|
1820
|
+
`conv=${conversationId.slice(0, 8)}`,
|
|
1832
1821
|
);
|
|
1833
1822
|
}
|
|
1834
1823
|
const hasFullToolResults = hasUntruncatedToolResults(messages);
|
|
1835
1824
|
if (hasFullToolResults) {
|
|
1836
|
-
|
|
1837
|
-
`[poncho][cost] Prompt cache breakpoint will be placed before untruncated ` +
|
|
1838
|
-
`tool results for run "${runId}" (stable prefix only).`,
|
|
1839
|
-
);
|
|
1825
|
+
costLog.debug(`cache breakpoint before untruncated tool results (run=${runId.slice(0, 12)})`);
|
|
1840
1826
|
} else {
|
|
1841
|
-
|
|
1842
|
-
`[poncho][cost] Prompt cache breakpoint will be placed at history tail ` +
|
|
1843
|
-
`for run "${runId}" (no untruncated tool results).`,
|
|
1844
|
-
);
|
|
1827
|
+
costLog.debug(`cache breakpoint at history tail (run=${runId.slice(0, 12)})`);
|
|
1845
1828
|
}
|
|
1846
1829
|
const inputMessageCount = messages.length;
|
|
1847
1830
|
const events: AgentEvent[] = [];
|
|
@@ -2422,7 +2405,7 @@ Code is wrapped in an async IIFE — use \`return\` to return a value to the too
|
|
|
2422
2405
|
|
|
2423
2406
|
const modelName = agent.frontmatter.model?.name ?? "claude-opus-4-5";
|
|
2424
2407
|
if (step === 1) {
|
|
2425
|
-
|
|
2408
|
+
modelLog.item(`${modelName} (provider=${agent.frontmatter.model?.provider ?? "anthropic"})`);
|
|
2426
2409
|
}
|
|
2427
2410
|
const modelInstance = this.modelProvider(modelName);
|
|
2428
2411
|
|
|
@@ -2550,8 +2533,8 @@ Code is wrapped in an async IIFE — use \`return\` to return a value to the too
|
|
|
2550
2533
|
message: `Model "${modelName}" did not respond within the ${Math.floor(timeoutMs / 1000)}s run timeout.`,
|
|
2551
2534
|
},
|
|
2552
2535
|
});
|
|
2553
|
-
|
|
2554
|
-
`
|
|
2536
|
+
harnessLog.error(
|
|
2537
|
+
`stream timeout: model=${modelName} step=${step} elapsed=${now() - start}ms`,
|
|
2555
2538
|
);
|
|
2556
2539
|
return;
|
|
2557
2540
|
}
|
|
@@ -2587,8 +2570,8 @@ Code is wrapped in an async IIFE — use \`return\` to return a value to the too
|
|
|
2587
2570
|
break;
|
|
2588
2571
|
}
|
|
2589
2572
|
const isFirstChunk = chunkCount === 0;
|
|
2590
|
-
|
|
2591
|
-
`
|
|
2573
|
+
harnessLog.error(
|
|
2574
|
+
`stream timeout waiting for ${isFirstChunk ? "first" : "next"} chunk: model=${modelName} step=${step} chunks=${chunkCount} elapsed=${now() - start}ms`,
|
|
2592
2575
|
);
|
|
2593
2576
|
if (isFirstChunk) {
|
|
2594
2577
|
throw new FirstChunkTimeoutError(modelName, FIRST_CHUNK_TIMEOUT_MS);
|
|
@@ -2645,7 +2628,7 @@ Code is wrapped in an async IIFE — use \`return\` to return a value to the too
|
|
|
2645
2628
|
contextTokens: latestContextTokens + toolOutputEstimateSinceModel,
|
|
2646
2629
|
contextWindow,
|
|
2647
2630
|
};
|
|
2648
|
-
|
|
2631
|
+
harnessLog.info(`soft deadline fired mid-stream at step ${step} (${(now() - start).toFixed(0)}ms); checkpointing with ${fullText.length} chars of partial text`);
|
|
2649
2632
|
yield pushEvent({ type: "run:completed", runId, result: result_ });
|
|
2650
2633
|
return;
|
|
2651
2634
|
}
|
|
@@ -2698,9 +2681,7 @@ Code is wrapped in an async IIFE — use \`return\` to return a value to the too
|
|
|
2698
2681
|
message: `Model "${modelName}" returned an error. This may indicate the model is not supported by the current provider SDK version, or the API returned an error response.`,
|
|
2699
2682
|
},
|
|
2700
2683
|
});
|
|
2701
|
-
|
|
2702
|
-
`[poncho][harness] Model error: finishReason="error", model="${modelName}", step=${step}`,
|
|
2703
|
-
);
|
|
2684
|
+
harnessLog.error(`model error: finishReason="error" model=${modelName} step=${step}`);
|
|
2704
2685
|
return;
|
|
2705
2686
|
}
|
|
2706
2687
|
|
|
@@ -2749,11 +2730,10 @@ Code is wrapped in an async IIFE — use \`return\` to return a value to the too
|
|
|
2749
2730
|
cacheWrite: stepCacheWriteTokens,
|
|
2750
2731
|
},
|
|
2751
2732
|
});
|
|
2752
|
-
|
|
2753
|
-
`
|
|
2754
|
-
`
|
|
2755
|
-
`
|
|
2756
|
-
`totals(input=${totalInputTokens}, output=${totalOutputTokens}, cached=${totalCachedTokens}, cacheWrite=${totalCacheWriteTokens})`,
|
|
2733
|
+
costLog.debug(
|
|
2734
|
+
`step=${step} in=${stepInputTokens} out=${usage.outputTokens ?? 0} ` +
|
|
2735
|
+
`cached=${stepCachedTokens} cw=${stepCacheWriteTokens} ` +
|
|
2736
|
+
`totals(in=${totalInputTokens} out=${totalOutputTokens} cached=${totalCachedTokens} cw=${totalCacheWriteTokens})`,
|
|
2757
2737
|
);
|
|
2758
2738
|
|
|
2759
2739
|
// Extract tool calls
|
|
@@ -2778,14 +2758,10 @@ Code is wrapped in an async IIFE — use \`return\` to return a value to the too
|
|
|
2778
2758
|
message: `Model "${modelName}" returned no content (finish reason: ${finishReason}). The model may not be supported by the current provider SDK version.`,
|
|
2779
2759
|
},
|
|
2780
2760
|
});
|
|
2781
|
-
|
|
2782
|
-
`[poncho][harness] Empty response: finishReason="${finishReason}", model="${modelName}", step=${step}`,
|
|
2783
|
-
);
|
|
2761
|
+
harnessLog.error(`empty response: finishReason="${finishReason}" model=${modelName} step=${step}`);
|
|
2784
2762
|
return;
|
|
2785
2763
|
}
|
|
2786
|
-
|
|
2787
|
-
`[poncho][harness] Model "${modelName}" returned an empty response with finishReason="stop" on step ${step}.`,
|
|
2788
|
-
);
|
|
2764
|
+
harnessLog.warn(`model "${modelName}" returned empty response with finishReason="stop" on step ${step}`);
|
|
2789
2765
|
}
|
|
2790
2766
|
if (fullText.length > 0) {
|
|
2791
2767
|
messages.push({
|
|
@@ -3217,10 +3193,10 @@ Code is wrapped in an async IIFE — use \`return\` to return a value to the too
|
|
|
3217
3193
|
if (isRetryableModelError(error) && transientStepRetryCount < MAX_TRANSIENT_STEP_RETRIES) {
|
|
3218
3194
|
transientStepRetryCount += 1;
|
|
3219
3195
|
const statusCode = getErrorStatusCode(error);
|
|
3220
|
-
|
|
3221
|
-
`
|
|
3196
|
+
harnessLog.warn(
|
|
3197
|
+
`retrying step ${step} after transient model error (attempt ${transientStepRetryCount}/${MAX_TRANSIENT_STEP_RETRIES})${
|
|
3222
3198
|
typeof statusCode === "number" ? ` status=${statusCode}` : ""
|
|
3223
|
-
}: ${
|
|
3199
|
+
}: ${fmtErr(error)}`,
|
|
3224
3200
|
);
|
|
3225
3201
|
step -= 1;
|
|
3226
3202
|
continue;
|
|
@@ -3231,7 +3207,7 @@ Code is wrapped in an async IIFE — use \`return\` to return a value to the too
|
|
|
3231
3207
|
runId,
|
|
3232
3208
|
error: runError,
|
|
3233
3209
|
});
|
|
3234
|
-
|
|
3210
|
+
harnessLog.error(`step ${step} error: ${fmtErr(error)}`);
|
|
3235
3211
|
return;
|
|
3236
3212
|
}
|
|
3237
3213
|
}
|
package/src/index.ts
CHANGED
|
@@ -27,5 +27,6 @@ export * from "./tenant-token.js";
|
|
|
27
27
|
export * from "./tool-dispatcher.js";
|
|
28
28
|
export * from "./subagent-manager.js";
|
|
29
29
|
export * from "./subagent-tools.js";
|
|
30
|
+
export * from "./orchestrator/index.js";
|
|
30
31
|
export { defineTool } from "@poncho-ai/sdk";
|
|
31
32
|
export type { ToolDefinition } from "@poncho-ai/sdk";
|
package/src/local-tools.ts
CHANGED
|
@@ -3,8 +3,11 @@ import { resolve } from "node:path";
|
|
|
3
3
|
import { pathToFileURL } from "node:url";
|
|
4
4
|
import { createJiti } from "jiti";
|
|
5
5
|
import type { ToolDefinition } from "@poncho-ai/sdk";
|
|
6
|
+
import { createLogger } from "@poncho-ai/sdk";
|
|
6
7
|
import { resolveSkillDirs } from "./skill-context.js";
|
|
7
8
|
|
|
9
|
+
const toolsLog = createLogger("tools");
|
|
10
|
+
|
|
8
11
|
const TOOL_FILE_PATTERN = /\.(?:[cm]?js|[cm]?ts)$/i;
|
|
9
12
|
|
|
10
13
|
const collectToolFiles = async (directory: string): Promise<string[]> => {
|
|
@@ -94,9 +97,7 @@ export const loadLocalSkillTools = async (
|
|
|
94
97
|
tools.push(...normalizeToolExports(loaded));
|
|
95
98
|
} catch (error) {
|
|
96
99
|
const message = error instanceof Error ? error.message : String(error);
|
|
97
|
-
|
|
98
|
-
`[poncho] Skipping skill tool module ${filePath}: ${message}\n`,
|
|
99
|
-
);
|
|
100
|
+
toolsLog.warn(`skipping skill tool module ${filePath}: ${message}`);
|
|
100
101
|
}
|
|
101
102
|
}
|
|
102
103
|
|
package/src/mcp.ts
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import type { ToolDefinition } from "@poncho-ai/sdk";
|
|
2
|
+
import { createLogger } from "@poncho-ai/sdk";
|
|
2
3
|
import {
|
|
3
4
|
matchesSlashPattern,
|
|
4
5
|
validateMcpPattern,
|
|
5
6
|
} from "./tool-policy.js";
|
|
6
7
|
|
|
8
|
+
const mcpLog = createLogger("mcp");
|
|
9
|
+
|
|
7
10
|
export interface RemoteMcpServerConfig {
|
|
8
11
|
name?: string;
|
|
9
12
|
url: string;
|
|
@@ -317,12 +320,12 @@ export class LocalMcpBridge {
|
|
|
317
320
|
}
|
|
318
321
|
|
|
319
322
|
private log(level: "info" | "warn", event: string, payload: Record<string, unknown>): void {
|
|
320
|
-
const
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
323
|
+
const summary = Object.entries(payload)
|
|
324
|
+
.map(([k, v]) => `${k}=${typeof v === "string" ? v : JSON.stringify(v)}`)
|
|
325
|
+
.join(" ");
|
|
326
|
+
const msg = summary ? `${event} ${summary}` : event;
|
|
327
|
+
if (level === "warn") mcpLog.warn(msg);
|
|
328
|
+
else mcpLog.debug(msg);
|
|
326
329
|
}
|
|
327
330
|
|
|
328
331
|
/** Set of servers where discovery was deferred (no default token, has env resolver). */
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Conversation } from "../state.js";
|
|
2
|
+
|
|
3
|
+
export const TOOL_RESULT_ARCHIVE_PARAM = "__toolResultArchive";
|
|
4
|
+
|
|
5
|
+
export const withToolResultArchiveParam = (
|
|
6
|
+
parameters: Record<string, unknown> | undefined,
|
|
7
|
+
conversation: Conversation,
|
|
8
|
+
): Record<string, unknown> => ({
|
|
9
|
+
...(parameters ?? {}),
|
|
10
|
+
[TOOL_RESULT_ARCHIVE_PARAM]: conversation._toolResultArchive ?? {},
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
export const MAX_CONTINUATION_COUNT = 20;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { Message } from "@poncho-ai/sdk";
|
|
2
|
+
import type { Conversation } from "../state.js";
|
|
3
|
+
|
|
4
|
+
export type HistorySource = "harness" | "continuation" | "messages";
|
|
5
|
+
|
|
6
|
+
export type RunRequest = {
|
|
7
|
+
conversationId: string;
|
|
8
|
+
messages: Message[];
|
|
9
|
+
preferContinuation?: boolean;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type RunOutcome = {
|
|
13
|
+
source: HistorySource;
|
|
14
|
+
shouldRebuildCanonical: boolean;
|
|
15
|
+
messages: Message[];
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const isMessageArray = (value: unknown): value is Message[] =>
|
|
19
|
+
Array.isArray(value) &&
|
|
20
|
+
value.every((entry) => {
|
|
21
|
+
if (!entry || typeof entry !== "object") return false;
|
|
22
|
+
const row = entry as Record<string, unknown>;
|
|
23
|
+
const role = row.role;
|
|
24
|
+
const content = row.content;
|
|
25
|
+
const roleOk = role === "system" || role === "user" || role === "assistant" || role === "tool";
|
|
26
|
+
const contentOk = typeof content === "string" || Array.isArray(content);
|
|
27
|
+
return roleOk && contentOk;
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
export const loadCanonicalHistory = (
|
|
31
|
+
conversation: Conversation,
|
|
32
|
+
): { messages: Message[]; source: HistorySource } => {
|
|
33
|
+
if (isMessageArray(conversation._harnessMessages) && conversation._harnessMessages.length > 0) {
|
|
34
|
+
return { messages: [...conversation._harnessMessages], source: "harness" };
|
|
35
|
+
}
|
|
36
|
+
return { messages: [...conversation.messages], source: "messages" };
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export const loadRunHistory = (
|
|
40
|
+
conversation: Conversation,
|
|
41
|
+
options?: { preferContinuation?: boolean },
|
|
42
|
+
): { messages: Message[]; source: HistorySource; shouldRebuildCanonical: boolean } => {
|
|
43
|
+
if (options?.preferContinuation && isMessageArray(conversation._continuationMessages) && conversation._continuationMessages.length > 0) {
|
|
44
|
+
return {
|
|
45
|
+
messages: [...conversation._continuationMessages],
|
|
46
|
+
source: "continuation",
|
|
47
|
+
shouldRebuildCanonical: !isMessageArray(conversation._harnessMessages) || conversation._harnessMessages.length === 0,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
const canonical = loadCanonicalHistory(conversation);
|
|
51
|
+
return {
|
|
52
|
+
...canonical,
|
|
53
|
+
shouldRebuildCanonical: canonical.source !== "harness",
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export const resolveRunRequest = (
|
|
58
|
+
conversation: Conversation,
|
|
59
|
+
request: RunRequest,
|
|
60
|
+
): RunOutcome => {
|
|
61
|
+
const resolved = loadRunHistory(conversation, {
|
|
62
|
+
preferContinuation: request.preferContinuation,
|
|
63
|
+
});
|
|
64
|
+
return {
|
|
65
|
+
source: resolved.source,
|
|
66
|
+
shouldRebuildCanonical: resolved.shouldRebuildCanonical,
|
|
67
|
+
messages: resolved.messages.length > 0 ? resolved.messages : request.messages,
|
|
68
|
+
};
|
|
69
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
export {
|
|
2
|
+
isMessageArray,
|
|
3
|
+
loadCanonicalHistory,
|
|
4
|
+
loadRunHistory,
|
|
5
|
+
resolveRunRequest,
|
|
6
|
+
type HistorySource,
|
|
7
|
+
type RunRequest,
|
|
8
|
+
type RunOutcome,
|
|
9
|
+
} from "./history.js";
|
|
10
|
+
|
|
11
|
+
export {
|
|
12
|
+
createTurnDraftState,
|
|
13
|
+
cloneSections,
|
|
14
|
+
flushTurnDraft,
|
|
15
|
+
buildToolCompletedText,
|
|
16
|
+
recordStandardTurnEvent,
|
|
17
|
+
buildAssistantMetadata,
|
|
18
|
+
executeConversationTurn,
|
|
19
|
+
normalizeApprovalCheckpoint,
|
|
20
|
+
buildApprovalCheckpoints,
|
|
21
|
+
applyTurnMetadata,
|
|
22
|
+
type StoredApproval,
|
|
23
|
+
type PendingToolCall,
|
|
24
|
+
type ApprovalEventItem,
|
|
25
|
+
type TurnSection,
|
|
26
|
+
type TurnDraftState,
|
|
27
|
+
type ExecuteTurnResult,
|
|
28
|
+
type TurnResultMetadata,
|
|
29
|
+
} from "./turn.js";
|
|
30
|
+
|
|
31
|
+
export {
|
|
32
|
+
TOOL_RESULT_ARCHIVE_PARAM,
|
|
33
|
+
withToolResultArchiveParam,
|
|
34
|
+
MAX_CONTINUATION_COUNT,
|
|
35
|
+
} from "./continuation.js";
|
|
36
|
+
|
|
37
|
+
export {
|
|
38
|
+
type ActiveSubagentRun,
|
|
39
|
+
type PendingSubagentApproval,
|
|
40
|
+
MAX_SUBAGENT_NESTING,
|
|
41
|
+
MAX_CONCURRENT_SUBAGENTS,
|
|
42
|
+
MAX_SUBAGENT_CALLBACK_COUNT,
|
|
43
|
+
CALLBACK_LOCK_STALE_MS,
|
|
44
|
+
STALE_SUBAGENT_THRESHOLD_MS,
|
|
45
|
+
} from "./subagents.js";
|
|
46
|
+
|
|
47
|
+
export {
|
|
48
|
+
AgentOrchestrator,
|
|
49
|
+
type ActiveConversationRun,
|
|
50
|
+
type EventSink,
|
|
51
|
+
type OrchestratorHooks,
|
|
52
|
+
type ContinuationHooks,
|
|
53
|
+
type OrchestratorOptions,
|
|
54
|
+
} from "./orchestrator.js";
|