@poncho-ai/harness 0.40.1 → 0.42.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 +137 -0
- package/dist/index.d.ts +92 -5
- package/dist/index.js +441 -36
- package/package.json +1 -1
- package/src/harness.ts +88 -18
- package/src/mcp.ts +102 -23
- package/src/orchestrator/index.ts +6 -0
- package/src/orchestrator/run-conversation-turn.ts +420 -0
- package/src/storage/engine.ts +2 -0
- package/src/storage/memory-engine.ts +1 -1
- package/src/storage/sql-dialect.ts +1 -1
- package/test/harness-config-injection.test.ts +63 -0
- package/test/harness-injection.test.ts +93 -0
- package/test/mcp-tenant-cache.test.ts +311 -0
- package/test/mcp.test.ts +174 -0
package/dist/index.js
CHANGED
|
@@ -5911,6 +5911,11 @@ var McpHttpError = class extends Error {
|
|
|
5911
5911
|
this.status = status;
|
|
5912
5912
|
}
|
|
5913
5913
|
};
|
|
5914
|
+
var McpSessionExpiredError = class extends Error {
|
|
5915
|
+
constructor() {
|
|
5916
|
+
super("MCP session expired");
|
|
5917
|
+
}
|
|
5918
|
+
};
|
|
5914
5919
|
var StreamableHttpMcpRpcClient = class {
|
|
5915
5920
|
endpoint;
|
|
5916
5921
|
timeoutMs;
|
|
@@ -5962,6 +5967,9 @@ var StreamableHttpMcpRpcClient = class {
|
|
|
5962
5967
|
if (response.status === 403) {
|
|
5963
5968
|
throw new McpHttpError(403, "MCP server forbidden");
|
|
5964
5969
|
}
|
|
5970
|
+
if (response.status === 404 && this.sessionId) {
|
|
5971
|
+
throw new McpSessionExpiredError();
|
|
5972
|
+
}
|
|
5965
5973
|
if (!response.ok) {
|
|
5966
5974
|
throw new Error(`MCP HTTP request failed with status ${response.status}`);
|
|
5967
5975
|
}
|
|
@@ -6036,20 +6044,32 @@ var StreamableHttpMcpRpcClient = class {
|
|
|
6036
6044
|
throw new Error(`MCP response missing JSON-RPC payload for id ${id}`);
|
|
6037
6045
|
}
|
|
6038
6046
|
async request(method, params) {
|
|
6039
|
-
|
|
6040
|
-
|
|
6041
|
-
|
|
6042
|
-
|
|
6043
|
-
|
|
6044
|
-
|
|
6045
|
-
|
|
6046
|
-
|
|
6047
|
-
|
|
6048
|
-
|
|
6049
|
-
|
|
6050
|
-
|
|
6047
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
6048
|
+
try {
|
|
6049
|
+
await this.ensureInitialized();
|
|
6050
|
+
const id = this.idCounter++;
|
|
6051
|
+
const payload = {
|
|
6052
|
+
jsonrpc: "2.0",
|
|
6053
|
+
id,
|
|
6054
|
+
method,
|
|
6055
|
+
params: params ?? {}
|
|
6056
|
+
};
|
|
6057
|
+
const payloads = await this.postMessage(payload);
|
|
6058
|
+
const result = this.extractResult(payloads, id);
|
|
6059
|
+
if (result.error) {
|
|
6060
|
+
throw new Error(result.error.message ?? `MCP error on ${method}`);
|
|
6061
|
+
}
|
|
6062
|
+
return result.result;
|
|
6063
|
+
} catch (error) {
|
|
6064
|
+
if (error instanceof McpSessionExpiredError && attempt === 0) {
|
|
6065
|
+
this.sessionId = void 0;
|
|
6066
|
+
this.initialized = false;
|
|
6067
|
+
continue;
|
|
6068
|
+
}
|
|
6069
|
+
throw error;
|
|
6070
|
+
}
|
|
6051
6071
|
}
|
|
6052
|
-
|
|
6072
|
+
throw new Error(`MCP request to ${method} failed after session retry`);
|
|
6053
6073
|
}
|
|
6054
6074
|
async ensureInitialized() {
|
|
6055
6075
|
if (this.initialized) {
|
|
@@ -6105,6 +6125,8 @@ var StreamableHttpMcpRpcClient = class {
|
|
|
6105
6125
|
}
|
|
6106
6126
|
}
|
|
6107
6127
|
};
|
|
6128
|
+
var TENANT_CLIENT_TTL_MS = 15 * 60 * 1e3;
|
|
6129
|
+
var tenantClientKey = (serverName, tenantId) => `${serverName}\0${tenantId}`;
|
|
6108
6130
|
var LocalMcpBridge = class {
|
|
6109
6131
|
remoteServers;
|
|
6110
6132
|
rpcClients = /* @__PURE__ */ new Map();
|
|
@@ -6112,6 +6134,18 @@ var LocalMcpBridge = class {
|
|
|
6112
6134
|
unavailableServers = /* @__PURE__ */ new Map();
|
|
6113
6135
|
authFailedServers = /* @__PURE__ */ new Set();
|
|
6114
6136
|
envResolver;
|
|
6137
|
+
/**
|
|
6138
|
+
* Per-tenant MCP client cache. For consumer/SaaS deployments where every
|
|
6139
|
+
* call resolves a different bearer token, building a fresh
|
|
6140
|
+
* `StreamableHttpMcpRpcClient` per call would force a fresh `initialize`
|
|
6141
|
+
* round-trip every time. We keep one client per `(serverName, tenantId)`
|
|
6142
|
+
* with TTL-based idle eviction; on token rotation we evict the entry
|
|
6143
|
+
* lazily and rebuild.
|
|
6144
|
+
*/
|
|
6145
|
+
tenantClients = /* @__PURE__ */ new Map();
|
|
6146
|
+
/** Test/observability hook: bumped every time a new tenant client is constructed. */
|
|
6147
|
+
tenantClientConstructions = 0;
|
|
6148
|
+
tenantClientTtlMs;
|
|
6115
6149
|
/**
|
|
6116
6150
|
* Set a resolver for per-tenant env vars (e.g. MCP auth tokens).
|
|
6117
6151
|
* Called by the harness after creating the secrets store.
|
|
@@ -6119,7 +6153,8 @@ var LocalMcpBridge = class {
|
|
|
6119
6153
|
setEnvResolver(resolver) {
|
|
6120
6154
|
this.envResolver = resolver;
|
|
6121
6155
|
}
|
|
6122
|
-
constructor(config) {
|
|
6156
|
+
constructor(config, options) {
|
|
6157
|
+
this.tenantClientTtlMs = options?.tenantClientTtlMs ?? TENANT_CLIENT_TTL_MS;
|
|
6123
6158
|
this.remoteServers = (config?.mcp ?? []).filter(
|
|
6124
6159
|
(entry) => typeof entry.url === "string"
|
|
6125
6160
|
);
|
|
@@ -6290,6 +6325,35 @@ var LocalMcpBridge = class {
|
|
|
6290
6325
|
await client.close();
|
|
6291
6326
|
}
|
|
6292
6327
|
this.rpcClients.clear();
|
|
6328
|
+
for (const [, entry] of this.tenantClients) {
|
|
6329
|
+
await entry.client.close();
|
|
6330
|
+
}
|
|
6331
|
+
this.tenantClients.clear();
|
|
6332
|
+
}
|
|
6333
|
+
getOrCreateTenantClient(serverName, tenantId, token, server) {
|
|
6334
|
+
const key = tenantClientKey(serverName, tenantId);
|
|
6335
|
+
const now2 = Date.now();
|
|
6336
|
+
const existing = this.tenantClients.get(key);
|
|
6337
|
+
if (existing) {
|
|
6338
|
+
const idle = now2 - existing.lastUsed > this.tenantClientTtlMs;
|
|
6339
|
+
const tokenChanged = existing.token !== token;
|
|
6340
|
+
if (idle || tokenChanged) {
|
|
6341
|
+
void existing.client.close();
|
|
6342
|
+
this.tenantClients.delete(key);
|
|
6343
|
+
} else {
|
|
6344
|
+
existing.lastUsed = now2;
|
|
6345
|
+
return existing.client;
|
|
6346
|
+
}
|
|
6347
|
+
}
|
|
6348
|
+
const client = new StreamableHttpMcpRpcClient(
|
|
6349
|
+
server.url,
|
|
6350
|
+
server.timeoutMs ?? 1e4,
|
|
6351
|
+
token,
|
|
6352
|
+
server.headers
|
|
6353
|
+
);
|
|
6354
|
+
this.tenantClients.set(key, { client, token, lastUsed: now2 });
|
|
6355
|
+
this.tenantClientConstructions += 1;
|
|
6356
|
+
return client;
|
|
6293
6357
|
}
|
|
6294
6358
|
listServers() {
|
|
6295
6359
|
return [...this.remoteServers];
|
|
@@ -6411,15 +6475,15 @@ var LocalMcpBridge = class {
|
|
|
6411
6475
|
try {
|
|
6412
6476
|
const tokenEnv = server?.auth?.tokenEnv;
|
|
6413
6477
|
let callClient = client;
|
|
6414
|
-
if (tokenEnv && this.envResolver && context?.tenantId) {
|
|
6478
|
+
if (tokenEnv && this.envResolver && context?.tenantId && server) {
|
|
6415
6479
|
const tenantToken = await this.envResolver(context.tenantId, tokenEnv);
|
|
6416
6480
|
const defaultToken = process.env[tokenEnv];
|
|
6417
6481
|
if (tenantToken && tenantToken !== defaultToken) {
|
|
6418
|
-
callClient =
|
|
6419
|
-
|
|
6420
|
-
|
|
6482
|
+
callClient = this.getOrCreateTenantClient(
|
|
6483
|
+
serverName,
|
|
6484
|
+
context.tenantId,
|
|
6421
6485
|
tenantToken,
|
|
6422
|
-
server
|
|
6486
|
+
server
|
|
6423
6487
|
);
|
|
6424
6488
|
}
|
|
6425
6489
|
}
|
|
@@ -8640,6 +8704,7 @@ var AgentHarness = class _AgentHarness {
|
|
|
8640
8704
|
reminderStore;
|
|
8641
8705
|
secretsStore;
|
|
8642
8706
|
loadedConfig;
|
|
8707
|
+
injectedConfig;
|
|
8643
8708
|
loadedSkills = [];
|
|
8644
8709
|
skillFingerprint = "";
|
|
8645
8710
|
lastSkillRefreshAt = 0;
|
|
@@ -8654,6 +8719,8 @@ var AgentHarness = class _AgentHarness {
|
|
|
8654
8719
|
_browserMod;
|
|
8655
8720
|
parsedAgent;
|
|
8656
8721
|
agentFileFingerprint = "";
|
|
8722
|
+
injectedAgentDefinition;
|
|
8723
|
+
injectedStorageEngine = false;
|
|
8657
8724
|
mcpBridge;
|
|
8658
8725
|
subagentManager;
|
|
8659
8726
|
archivedToolResultsByConversation = /* @__PURE__ */ new Map();
|
|
@@ -8821,6 +8888,17 @@ var AgentHarness = class _AgentHarness {
|
|
|
8821
8888
|
this.modelProviderInjected = !!options.modelProvider;
|
|
8822
8889
|
this.modelProvider = options.modelProvider ?? createModelProvider("anthropic");
|
|
8823
8890
|
this.uploadStore = options.uploadStore;
|
|
8891
|
+
this.injectedConfig = options.config;
|
|
8892
|
+
if (options.agentDefinition !== void 0 && options.storageEngine === void 0) {
|
|
8893
|
+
throw new Error(
|
|
8894
|
+
"HarnessOptions.agentDefinition requires HarnessOptions.storageEngine \u2014 construct a StorageEngine with the desired agentId and pass both."
|
|
8895
|
+
);
|
|
8896
|
+
}
|
|
8897
|
+
this.injectedAgentDefinition = options.agentDefinition;
|
|
8898
|
+
if (options.storageEngine) {
|
|
8899
|
+
this.storageEngine = options.storageEngine;
|
|
8900
|
+
this.injectedStorageEngine = true;
|
|
8901
|
+
}
|
|
8824
8902
|
if (options.toolDefinitions?.length) {
|
|
8825
8903
|
this.dispatcher.registerMany(options.toolDefinitions);
|
|
8826
8904
|
}
|
|
@@ -9237,6 +9315,9 @@ var AgentHarness = class _AgentHarness {
|
|
|
9237
9315
|
if (this.environment !== "development") {
|
|
9238
9316
|
return false;
|
|
9239
9317
|
}
|
|
9318
|
+
if (this.injectedAgentDefinition !== void 0) {
|
|
9319
|
+
return false;
|
|
9320
|
+
}
|
|
9240
9321
|
try {
|
|
9241
9322
|
const agentFilePath = resolve11(this.workingDir, "AGENT.md");
|
|
9242
9323
|
const rawContent = await readFile8(agentFilePath, "utf8");
|
|
@@ -9304,15 +9385,23 @@ var AgentHarness = class _AgentHarness {
|
|
|
9304
9385
|
}
|
|
9305
9386
|
}
|
|
9306
9387
|
async initialize() {
|
|
9307
|
-
|
|
9308
|
-
|
|
9309
|
-
|
|
9310
|
-
|
|
9311
|
-
|
|
9312
|
-
|
|
9313
|
-
|
|
9388
|
+
if (this.injectedAgentDefinition !== void 0) {
|
|
9389
|
+
this.parsedAgent = typeof this.injectedAgentDefinition === "string" ? parseAgentMarkdown(this.injectedAgentDefinition) : this.injectedAgentDefinition;
|
|
9390
|
+
this.agentFileFingerprint = "";
|
|
9391
|
+
if (this.storageEngine) {
|
|
9392
|
+
this.parsedAgent.frontmatter.id = this.storageEngine.agentId;
|
|
9393
|
+
}
|
|
9394
|
+
} else {
|
|
9395
|
+
const agentFilePath = resolve11(this.workingDir, "AGENT.md");
|
|
9396
|
+
const agentRawContent = await readFile8(agentFilePath, "utf8");
|
|
9397
|
+
this.parsedAgent = parseAgentMarkdown(agentRawContent);
|
|
9398
|
+
this.agentFileFingerprint = agentRawContent;
|
|
9399
|
+
const identity = await ensureAgentIdentity(this.workingDir);
|
|
9400
|
+
if (!this.parsedAgent.frontmatter.id) {
|
|
9401
|
+
this.parsedAgent.frontmatter.id = identity.id;
|
|
9402
|
+
}
|
|
9314
9403
|
}
|
|
9315
|
-
const config = await loadPonchoConfig(this.workingDir);
|
|
9404
|
+
const config = this.injectedConfig ?? await loadPonchoConfig(this.workingDir);
|
|
9316
9405
|
this.loadedConfig = config;
|
|
9317
9406
|
this.registerConfiguredBuiltInTools(config);
|
|
9318
9407
|
const provider = this.parsedAgent.frontmatter.model?.provider ?? "anthropic";
|
|
@@ -9328,15 +9417,21 @@ var AgentHarness = class _AgentHarness {
|
|
|
9328
9417
|
this.skillFingerprint = this.buildSkillFingerprint(skillMetadata);
|
|
9329
9418
|
this.registerSkillTools();
|
|
9330
9419
|
const agentId = this.parsedAgent.frontmatter.id ?? this.parsedAgent.frontmatter.name;
|
|
9331
|
-
|
|
9332
|
-
|
|
9333
|
-
|
|
9334
|
-
|
|
9335
|
-
|
|
9336
|
-
|
|
9337
|
-
|
|
9338
|
-
|
|
9339
|
-
|
|
9420
|
+
let engine;
|
|
9421
|
+
if (this.injectedStorageEngine && this.storageEngine) {
|
|
9422
|
+
engine = this.storageEngine;
|
|
9423
|
+
await engine.initialize();
|
|
9424
|
+
} else {
|
|
9425
|
+
const storageProvider = config?.storage?.provider ?? "sqlite";
|
|
9426
|
+
engine = createStorageEngine({
|
|
9427
|
+
provider: storageProvider,
|
|
9428
|
+
workingDir: this.workingDir,
|
|
9429
|
+
agentId,
|
|
9430
|
+
urlEnv: config?.storage?.urlEnv
|
|
9431
|
+
});
|
|
9432
|
+
await engine.initialize();
|
|
9433
|
+
this.storageEngine = engine;
|
|
9434
|
+
}
|
|
9340
9435
|
const maxFileSize = config?.storage?.limits?.maxFileSize ?? 100 * 1024 * 1024;
|
|
9341
9436
|
const maxTotalStorage = config?.storage?.limits?.maxTotalStorage ?? 1024 * 1024 * 1024;
|
|
9342
9437
|
const bashWorkingDir = this.environment === "production" ? null : this.workingDir;
|
|
@@ -9559,6 +9654,10 @@ var AgentHarness = class _AgentHarness {
|
|
|
9559
9654
|
listSkills() {
|
|
9560
9655
|
return this.loadedSkills.map((s) => ({ name: s.name, description: s.description }));
|
|
9561
9656
|
}
|
|
9657
|
+
async listSkillsForTenant(tenantId) {
|
|
9658
|
+
const skills = await this.getSkillsForTenant(tenantId);
|
|
9659
|
+
return skills.map((s) => ({ name: s.name, description: s.description }));
|
|
9660
|
+
}
|
|
9562
9661
|
/**
|
|
9563
9662
|
* Wraps the run() generator with an OTel root span (invoke_agent) so all
|
|
9564
9663
|
* child spans (LLM calls via AI SDK, tool execution) group under one trace.
|
|
@@ -12896,6 +12995,311 @@ ${resultBody}`,
|
|
|
12896
12995
|
}
|
|
12897
12996
|
};
|
|
12898
12997
|
|
|
12998
|
+
// src/orchestrator/run-conversation-turn.ts
|
|
12999
|
+
import { randomUUID as randomUUID6 } from "crypto";
|
|
13000
|
+
import { createLogger as createLogger7 } from "@poncho-ai/sdk";
|
|
13001
|
+
var log = createLogger7("orchestrator");
|
|
13002
|
+
var runConversationTurn = async (opts) => {
|
|
13003
|
+
const conversation = await opts.conversationStore.getWithArchive(opts.conversationId);
|
|
13004
|
+
if (!conversation) {
|
|
13005
|
+
throw new Error(`Conversation not found: ${opts.conversationId}`);
|
|
13006
|
+
}
|
|
13007
|
+
const canonicalHistory = resolveRunRequest(conversation, {
|
|
13008
|
+
conversationId: opts.conversationId,
|
|
13009
|
+
messages: conversation.messages
|
|
13010
|
+
});
|
|
13011
|
+
const shouldRebuildCanonical = canonicalHistory.shouldRebuildCanonical;
|
|
13012
|
+
const harnessMessages = [...canonicalHistory.messages];
|
|
13013
|
+
const historyMessages = [...conversation.messages];
|
|
13014
|
+
const preRunMessages = [...conversation.messages];
|
|
13015
|
+
let userContent = opts.task;
|
|
13016
|
+
if (opts.files && opts.files.length > 0 && opts.harness.uploadStore) {
|
|
13017
|
+
const uploadedParts = await Promise.all(
|
|
13018
|
+
opts.files.map(async (f) => {
|
|
13019
|
+
const buf = Buffer.from(f.data, "base64");
|
|
13020
|
+
const key = deriveUploadKey(buf, f.mediaType);
|
|
13021
|
+
const ref = await opts.harness.uploadStore.put(key, buf, f.mediaType);
|
|
13022
|
+
return {
|
|
13023
|
+
type: "file",
|
|
13024
|
+
data: ref,
|
|
13025
|
+
mediaType: f.mediaType,
|
|
13026
|
+
filename: f.filename
|
|
13027
|
+
};
|
|
13028
|
+
})
|
|
13029
|
+
);
|
|
13030
|
+
userContent = [
|
|
13031
|
+
{ type: "text", text: opts.task },
|
|
13032
|
+
...uploadedParts
|
|
13033
|
+
];
|
|
13034
|
+
}
|
|
13035
|
+
const turnTimestamp = Date.now();
|
|
13036
|
+
const userMessage = {
|
|
13037
|
+
role: "user",
|
|
13038
|
+
content: userContent,
|
|
13039
|
+
metadata: { id: randomUUID6(), timestamp: turnTimestamp }
|
|
13040
|
+
};
|
|
13041
|
+
const assistantId = randomUUID6();
|
|
13042
|
+
const draft = createTurnDraftState();
|
|
13043
|
+
let latestRunId = conversation.runtimeRunId ?? "";
|
|
13044
|
+
let runCancelled = false;
|
|
13045
|
+
let runContinuationMessages;
|
|
13046
|
+
let cancelHarnessMessages;
|
|
13047
|
+
let checkpointedRun = false;
|
|
13048
|
+
const buildMessages = () => {
|
|
13049
|
+
const draftSections = cloneSections(draft.sections);
|
|
13050
|
+
if (draft.currentTools.length > 0) {
|
|
13051
|
+
draftSections.push({ type: "tools", content: [...draft.currentTools] });
|
|
13052
|
+
}
|
|
13053
|
+
if (draft.currentText.length > 0) {
|
|
13054
|
+
draftSections.push({ type: "text", content: draft.currentText });
|
|
13055
|
+
}
|
|
13056
|
+
const userTurn = [userMessage];
|
|
13057
|
+
const hasDraftContent = draft.assistantResponse.length > 0 || draft.toolTimeline.length > 0 || draftSections.length > 0;
|
|
13058
|
+
if (!hasDraftContent) {
|
|
13059
|
+
return [...historyMessages, ...userTurn];
|
|
13060
|
+
}
|
|
13061
|
+
return [
|
|
13062
|
+
...historyMessages,
|
|
13063
|
+
...userTurn,
|
|
13064
|
+
{
|
|
13065
|
+
role: "assistant",
|
|
13066
|
+
content: draft.assistantResponse,
|
|
13067
|
+
metadata: buildAssistantMetadata(draft, draftSections, {
|
|
13068
|
+
id: assistantId,
|
|
13069
|
+
timestamp: turnTimestamp
|
|
13070
|
+
})
|
|
13071
|
+
}
|
|
13072
|
+
];
|
|
13073
|
+
};
|
|
13074
|
+
const persistDraft = async () => {
|
|
13075
|
+
if (draft.assistantResponse.length === 0 && draft.toolTimeline.length === 0) return;
|
|
13076
|
+
conversation.messages = buildMessages();
|
|
13077
|
+
conversation.updatedAt = Date.now();
|
|
13078
|
+
await opts.conversationStore.update(conversation);
|
|
13079
|
+
};
|
|
13080
|
+
conversation.messages = [...historyMessages, userMessage];
|
|
13081
|
+
conversation.subagentCallbackCount = 0;
|
|
13082
|
+
conversation._continuationCount = void 0;
|
|
13083
|
+
conversation.updatedAt = Date.now();
|
|
13084
|
+
opts.conversationStore.update(conversation).catch((err) => {
|
|
13085
|
+
log.error(
|
|
13086
|
+
`failed to persist user turn: ${err instanceof Error ? err.message : String(err)}`
|
|
13087
|
+
);
|
|
13088
|
+
});
|
|
13089
|
+
try {
|
|
13090
|
+
const execution = await executeConversationTurn({
|
|
13091
|
+
harness: opts.harness,
|
|
13092
|
+
runInput: {
|
|
13093
|
+
task: opts.task,
|
|
13094
|
+
conversationId: opts.conversationId,
|
|
13095
|
+
tenantId: opts.tenantId ?? void 0,
|
|
13096
|
+
parameters: withToolResultArchiveParam(
|
|
13097
|
+
{
|
|
13098
|
+
...opts.parameters ?? {},
|
|
13099
|
+
__activeConversationId: opts.conversationId,
|
|
13100
|
+
__ownerId: conversation.ownerId
|
|
13101
|
+
},
|
|
13102
|
+
conversation
|
|
13103
|
+
),
|
|
13104
|
+
messages: harnessMessages,
|
|
13105
|
+
files: opts.files && opts.files.length > 0 ? opts.files : void 0,
|
|
13106
|
+
abortSignal: opts.abortSignal
|
|
13107
|
+
},
|
|
13108
|
+
initialContextTokens: conversation.contextTokens ?? 0,
|
|
13109
|
+
initialContextWindow: conversation.contextWindow ?? 0,
|
|
13110
|
+
onEvent: async (event, eventDraft) => {
|
|
13111
|
+
draft.assistantResponse = eventDraft.assistantResponse;
|
|
13112
|
+
draft.toolTimeline = eventDraft.toolTimeline;
|
|
13113
|
+
draft.sections = eventDraft.sections;
|
|
13114
|
+
draft.currentTools = eventDraft.currentTools;
|
|
13115
|
+
draft.currentText = eventDraft.currentText;
|
|
13116
|
+
if (event.type === "run:started") {
|
|
13117
|
+
latestRunId = event.runId;
|
|
13118
|
+
}
|
|
13119
|
+
if (event.type === "run:cancelled") {
|
|
13120
|
+
runCancelled = true;
|
|
13121
|
+
if (event.messages) cancelHarnessMessages = event.messages;
|
|
13122
|
+
}
|
|
13123
|
+
if (event.type === "compaction:completed") {
|
|
13124
|
+
if (event.compactedMessages) {
|
|
13125
|
+
historyMessages.length = 0;
|
|
13126
|
+
historyMessages.push(...event.compactedMessages);
|
|
13127
|
+
const preservedFromHistory = historyMessages.length - 1;
|
|
13128
|
+
const removedCount = preRunMessages.length - Math.max(0, preservedFromHistory);
|
|
13129
|
+
const existingHistory = conversation.compactedHistory ?? [];
|
|
13130
|
+
conversation.compactedHistory = [
|
|
13131
|
+
...existingHistory,
|
|
13132
|
+
...preRunMessages.slice(0, removedCount)
|
|
13133
|
+
];
|
|
13134
|
+
}
|
|
13135
|
+
}
|
|
13136
|
+
if (event.type === "step:completed") {
|
|
13137
|
+
await persistDraft();
|
|
13138
|
+
}
|
|
13139
|
+
if (event.type === "tool:approval:required") {
|
|
13140
|
+
const toolText = `- approval required \`${event.tool}\``;
|
|
13141
|
+
draft.toolTimeline.push(toolText);
|
|
13142
|
+
draft.currentTools.push(toolText);
|
|
13143
|
+
const existing = Array.isArray(conversation.pendingApprovals) ? conversation.pendingApprovals : [];
|
|
13144
|
+
if (!existing.some((a) => a.approvalId === event.approvalId)) {
|
|
13145
|
+
conversation.pendingApprovals = [
|
|
13146
|
+
...existing,
|
|
13147
|
+
{
|
|
13148
|
+
approvalId: event.approvalId,
|
|
13149
|
+
runId: latestRunId || conversation.runtimeRunId || "",
|
|
13150
|
+
tool: event.tool,
|
|
13151
|
+
toolCallId: void 0,
|
|
13152
|
+
input: event.input ?? {},
|
|
13153
|
+
checkpointMessages: void 0,
|
|
13154
|
+
baseMessageCount: historyMessages.length,
|
|
13155
|
+
pendingToolCalls: []
|
|
13156
|
+
}
|
|
13157
|
+
];
|
|
13158
|
+
conversation.updatedAt = Date.now();
|
|
13159
|
+
await opts.conversationStore.update(conversation);
|
|
13160
|
+
}
|
|
13161
|
+
await persistDraft();
|
|
13162
|
+
}
|
|
13163
|
+
if (event.type === "tool:approval:checkpoint") {
|
|
13164
|
+
conversation.messages = buildMessages();
|
|
13165
|
+
conversation.pendingApprovals = buildApprovalCheckpoints({
|
|
13166
|
+
approvals: event.approvals,
|
|
13167
|
+
runId: latestRunId,
|
|
13168
|
+
checkpointMessages: event.checkpointMessages,
|
|
13169
|
+
baseMessageCount: historyMessages.length,
|
|
13170
|
+
pendingToolCalls: event.pendingToolCalls
|
|
13171
|
+
});
|
|
13172
|
+
conversation._toolResultArchive = opts.harness.getToolResultArchive(
|
|
13173
|
+
opts.conversationId
|
|
13174
|
+
);
|
|
13175
|
+
conversation.updatedAt = Date.now();
|
|
13176
|
+
await opts.conversationStore.update(conversation);
|
|
13177
|
+
checkpointedRun = true;
|
|
13178
|
+
}
|
|
13179
|
+
if (event.type === "run:completed") {
|
|
13180
|
+
if (event.result.continuation && event.result.continuationMessages) {
|
|
13181
|
+
runContinuationMessages = event.result.continuationMessages;
|
|
13182
|
+
conversation.messages = buildMessages();
|
|
13183
|
+
conversation._continuationMessages = runContinuationMessages;
|
|
13184
|
+
conversation._harnessMessages = runContinuationMessages;
|
|
13185
|
+
conversation._toolResultArchive = opts.harness.getToolResultArchive(
|
|
13186
|
+
opts.conversationId
|
|
13187
|
+
);
|
|
13188
|
+
conversation.runtimeRunId = latestRunId || conversation.runtimeRunId;
|
|
13189
|
+
if (!checkpointedRun) {
|
|
13190
|
+
conversation.pendingApprovals = [];
|
|
13191
|
+
}
|
|
13192
|
+
if ((event.result.contextTokens ?? 0) > 0) {
|
|
13193
|
+
conversation.contextTokens = event.result.contextTokens;
|
|
13194
|
+
}
|
|
13195
|
+
if ((event.result.contextWindow ?? 0) > 0) {
|
|
13196
|
+
conversation.contextWindow = event.result.contextWindow;
|
|
13197
|
+
}
|
|
13198
|
+
conversation.updatedAt = Date.now();
|
|
13199
|
+
await opts.conversationStore.update(conversation);
|
|
13200
|
+
}
|
|
13201
|
+
}
|
|
13202
|
+
if (opts.onEvent) {
|
|
13203
|
+
await opts.onEvent(event);
|
|
13204
|
+
}
|
|
13205
|
+
}
|
|
13206
|
+
});
|
|
13207
|
+
flushTurnDraft(draft);
|
|
13208
|
+
latestRunId = execution.latestRunId || latestRunId;
|
|
13209
|
+
if (!checkpointedRun && !runContinuationMessages) {
|
|
13210
|
+
conversation.messages = buildMessages();
|
|
13211
|
+
applyTurnMetadata(
|
|
13212
|
+
conversation,
|
|
13213
|
+
{
|
|
13214
|
+
latestRunId,
|
|
13215
|
+
contextTokens: execution.runContextTokens,
|
|
13216
|
+
contextWindow: execution.runContextWindow,
|
|
13217
|
+
harnessMessages: execution.runHarnessMessages,
|
|
13218
|
+
toolResultArchive: opts.harness.getToolResultArchive(opts.conversationId)
|
|
13219
|
+
},
|
|
13220
|
+
{ shouldRebuildCanonical }
|
|
13221
|
+
);
|
|
13222
|
+
await opts.conversationStore.update(conversation);
|
|
13223
|
+
}
|
|
13224
|
+
return {
|
|
13225
|
+
latestRunId,
|
|
13226
|
+
cancelled: runCancelled,
|
|
13227
|
+
errored: false,
|
|
13228
|
+
continuation: !!runContinuationMessages,
|
|
13229
|
+
checkpointed: checkpointedRun,
|
|
13230
|
+
contextTokens: execution.runContextTokens,
|
|
13231
|
+
contextWindow: execution.runContextWindow
|
|
13232
|
+
};
|
|
13233
|
+
} catch (error) {
|
|
13234
|
+
flushTurnDraft(draft);
|
|
13235
|
+
const aborted = opts.abortSignal?.aborted === true;
|
|
13236
|
+
if (aborted || runCancelled) {
|
|
13237
|
+
if (draft.assistantResponse.length > 0 || draft.toolTimeline.length > 0 || draft.sections.length > 0) {
|
|
13238
|
+
conversation.messages = buildMessages();
|
|
13239
|
+
applyTurnMetadata(
|
|
13240
|
+
conversation,
|
|
13241
|
+
{
|
|
13242
|
+
latestRunId,
|
|
13243
|
+
contextTokens: 0,
|
|
13244
|
+
contextWindow: 0,
|
|
13245
|
+
harnessMessages: cancelHarnessMessages,
|
|
13246
|
+
toolResultArchive: opts.harness.getToolResultArchive(opts.conversationId)
|
|
13247
|
+
},
|
|
13248
|
+
{ shouldRebuildCanonical: true }
|
|
13249
|
+
);
|
|
13250
|
+
await opts.conversationStore.update(conversation);
|
|
13251
|
+
}
|
|
13252
|
+
if (!checkpointedRun) {
|
|
13253
|
+
const fresh = await opts.conversationStore.get(opts.conversationId);
|
|
13254
|
+
if (fresh && Array.isArray(fresh.pendingApprovals) && fresh.pendingApprovals.length > 0) {
|
|
13255
|
+
fresh.pendingApprovals = [];
|
|
13256
|
+
await opts.conversationStore.update(fresh);
|
|
13257
|
+
}
|
|
13258
|
+
}
|
|
13259
|
+
return {
|
|
13260
|
+
latestRunId,
|
|
13261
|
+
cancelled: true,
|
|
13262
|
+
errored: false,
|
|
13263
|
+
continuation: false,
|
|
13264
|
+
checkpointed: checkpointedRun,
|
|
13265
|
+
contextTokens: 0,
|
|
13266
|
+
contextWindow: 0
|
|
13267
|
+
};
|
|
13268
|
+
}
|
|
13269
|
+
const errorEvent = {
|
|
13270
|
+
type: "run:error",
|
|
13271
|
+
runId: latestRunId || "run_unknown",
|
|
13272
|
+
error: {
|
|
13273
|
+
code: "RUN_ERROR",
|
|
13274
|
+
message: error instanceof Error ? error.message : "Unknown error"
|
|
13275
|
+
}
|
|
13276
|
+
};
|
|
13277
|
+
if (opts.onEvent) {
|
|
13278
|
+
try {
|
|
13279
|
+
await opts.onEvent(errorEvent);
|
|
13280
|
+
} catch (hookErr) {
|
|
13281
|
+
log.error(
|
|
13282
|
+
`onEvent threw on run:error: ${hookErr instanceof Error ? hookErr.message : String(hookErr)}`
|
|
13283
|
+
);
|
|
13284
|
+
}
|
|
13285
|
+
}
|
|
13286
|
+
if (draft.assistantResponse.length > 0 || draft.toolTimeline.length > 0 || draft.sections.length > 0) {
|
|
13287
|
+
conversation.messages = buildMessages();
|
|
13288
|
+
conversation.updatedAt = Date.now();
|
|
13289
|
+
await opts.conversationStore.update(conversation);
|
|
13290
|
+
}
|
|
13291
|
+
return {
|
|
13292
|
+
latestRunId,
|
|
13293
|
+
cancelled: false,
|
|
13294
|
+
errored: true,
|
|
13295
|
+
continuation: false,
|
|
13296
|
+
checkpointed: checkpointedRun,
|
|
13297
|
+
contextTokens: 0,
|
|
13298
|
+
contextWindow: 0
|
|
13299
|
+
};
|
|
13300
|
+
}
|
|
13301
|
+
};
|
|
13302
|
+
|
|
12899
13303
|
// src/index.ts
|
|
12900
13304
|
import { defineTool as defineTool13 } from "@poncho-ai/sdk";
|
|
12901
13305
|
export {
|
|
@@ -13003,6 +13407,7 @@ export {
|
|
|
13003
13407
|
resolveRunRequest,
|
|
13004
13408
|
resolveSkillDirs,
|
|
13005
13409
|
resolveStateConfig,
|
|
13410
|
+
runConversationTurn,
|
|
13006
13411
|
slugifyStorageComponent,
|
|
13007
13412
|
startOpenAICodexDeviceAuth,
|
|
13008
13413
|
verifyTenantToken,
|