@poncho-ai/harness 0.14.0 → 0.14.2
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/.turbo/turbo-lint.log +6 -0
- package/.turbo/turbo-test.log +137 -0
- package/CHANGELOG.md +26 -0
- package/dist/index.d.ts +79 -49
- package/dist/index.js +199 -113
- package/package.json +2 -2
- package/src/config.ts +13 -8
- package/src/harness.ts +195 -115
- package/src/latitude-capture.ts +9 -10
- package/src/memory.ts +8 -13
- package/src/model-factory.ts +13 -6
- package/src/state.ts +18 -12
- package/src/telemetry.ts +2 -2
- package/test/telemetry.test.ts +3 -3
package/dist/index.js
CHANGED
|
@@ -330,8 +330,8 @@ var resolveStateConfig = (config) => {
|
|
|
330
330
|
if (config?.storage) {
|
|
331
331
|
return {
|
|
332
332
|
provider: config.storage.provider,
|
|
333
|
-
|
|
334
|
-
|
|
333
|
+
urlEnv: config.storage.urlEnv,
|
|
334
|
+
tokenEnv: config.storage.tokenEnv,
|
|
335
335
|
table: config.storage.table,
|
|
336
336
|
region: config.storage.region,
|
|
337
337
|
ttl: resolveTtl(config.storage.ttl, "conversations")
|
|
@@ -344,8 +344,8 @@ var resolveMemoryConfig = (config) => {
|
|
|
344
344
|
return {
|
|
345
345
|
enabled: config.storage.memory?.enabled ?? config.memory?.enabled,
|
|
346
346
|
provider: config.storage.provider,
|
|
347
|
-
|
|
348
|
-
|
|
347
|
+
urlEnv: config.storage.urlEnv,
|
|
348
|
+
tokenEnv: config.storage.tokenEnv,
|
|
349
349
|
table: config.storage.table,
|
|
350
350
|
region: config.storage.region,
|
|
351
351
|
ttl: resolveTtl(config.storage.ttl, "memory"),
|
|
@@ -1121,8 +1121,10 @@ var createMemoryStore = (agentId, config, options) => {
|
|
|
1121
1121
|
return new InMemoryMemoryStore(ttl);
|
|
1122
1122
|
}
|
|
1123
1123
|
if (provider === "upstash") {
|
|
1124
|
-
const
|
|
1125
|
-
const
|
|
1124
|
+
const urlEnv = config?.urlEnv ?? (process.env.UPSTASH_REDIS_REST_URL ? "UPSTASH_REDIS_REST_URL" : "KV_REST_API_URL");
|
|
1125
|
+
const tokenEnv = config?.tokenEnv ?? (process.env.UPSTASH_REDIS_REST_TOKEN ? "UPSTASH_REDIS_REST_TOKEN" : "KV_REST_API_TOKEN");
|
|
1126
|
+
const url = process.env[urlEnv] ?? "";
|
|
1127
|
+
const token = process.env[tokenEnv] ?? "";
|
|
1126
1128
|
if (url && token) {
|
|
1127
1129
|
return new UpstashMemoryStore({
|
|
1128
1130
|
baseUrl: url,
|
|
@@ -1134,7 +1136,8 @@ var createMemoryStore = (agentId, config, options) => {
|
|
|
1134
1136
|
return new InMemoryMemoryStore(ttl);
|
|
1135
1137
|
}
|
|
1136
1138
|
if (provider === "redis") {
|
|
1137
|
-
const
|
|
1139
|
+
const urlEnv = config?.urlEnv ?? "REDIS_URL";
|
|
1140
|
+
const url = process.env[urlEnv] ?? "";
|
|
1138
1141
|
if (url) {
|
|
1139
1142
|
return new RedisMemoryStore({
|
|
1140
1143
|
url,
|
|
@@ -1763,16 +1766,18 @@ var getModelContextWindow = (modelName) => {
|
|
|
1763
1766
|
}
|
|
1764
1767
|
return best ? MODEL_CONTEXT_WINDOWS[best] : DEFAULT_CONTEXT_WINDOW;
|
|
1765
1768
|
};
|
|
1766
|
-
var createModelProvider = (provider) => {
|
|
1769
|
+
var createModelProvider = (provider, config) => {
|
|
1767
1770
|
const normalized = (provider ?? "anthropic").toLowerCase();
|
|
1768
1771
|
if (normalized === "openai") {
|
|
1772
|
+
const apiKeyEnv2 = config?.openai?.apiKeyEnv ?? "OPENAI_API_KEY";
|
|
1769
1773
|
const openai = createOpenAI({
|
|
1770
|
-
apiKey: process.env
|
|
1774
|
+
apiKey: process.env[apiKeyEnv2]
|
|
1771
1775
|
});
|
|
1772
1776
|
return (modelName) => openai(modelName);
|
|
1773
1777
|
}
|
|
1778
|
+
const apiKeyEnv = config?.anthropic?.apiKeyEnv ?? "ANTHROPIC_API_KEY";
|
|
1774
1779
|
const anthropic = createAnthropic({
|
|
1775
|
-
apiKey: process.env
|
|
1780
|
+
apiKey: process.env[apiKeyEnv]
|
|
1776
1781
|
});
|
|
1777
1782
|
return (modelName) => anthropic(modelName);
|
|
1778
1783
|
};
|
|
@@ -2812,18 +2817,51 @@ When configuring Latitude telemetry, use **exactly** these field names:
|
|
|
2812
2817
|
telemetry: {
|
|
2813
2818
|
enabled: true,
|
|
2814
2819
|
latitude: {
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
path: "your/prompt-path",
|
|
2820
|
+
apiKeyEnv: "LATITUDE_API_KEY", // env var name (default)
|
|
2821
|
+
projectIdEnv: "LATITUDE_PROJECT_ID", // env var name (default)
|
|
2822
|
+
path: "your/prompt-path", // optional, defaults to agent name
|
|
2818
2823
|
},
|
|
2819
2824
|
},
|
|
2820
2825
|
\`\`\`
|
|
2821
2826
|
|
|
2822
|
-
-
|
|
2823
|
-
-
|
|
2827
|
+
- \`apiKeyEnv\` specifies the environment variable name for the Latitude API key (defaults to \`"LATITUDE_API_KEY"\`).
|
|
2828
|
+
- \`projectIdEnv\` specifies the environment variable name for the project ID (defaults to \`"LATITUDE_PROJECT_ID"\`).
|
|
2829
|
+
- With defaults, you only need \`telemetry: { latitude: {} }\` if the env vars are already named \`LATITUDE_API_KEY\` and \`LATITUDE_PROJECT_ID\`.
|
|
2824
2830
|
- \`path\` must only contain letters, numbers, hyphens, underscores, dots, and slashes.
|
|
2825
2831
|
- For a generic OTLP endpoint instead: \`telemetry: { otlp: process.env.OTEL_EXPORTER_OTLP_ENDPOINT }\`.
|
|
2826
|
-
|
|
2832
|
+
|
|
2833
|
+
## Credential Configuration Pattern
|
|
2834
|
+
|
|
2835
|
+
All credentials in \`poncho.config.js\` use the **env var name** pattern (\`*Env\` fields). Config specifies which environment variable to read \u2014 never the secret itself. Sensible defaults mean zero config when using conventional env var names.
|
|
2836
|
+
|
|
2837
|
+
\`\`\`javascript
|
|
2838
|
+
// poncho.config.js \u2014 credentials use *Env fields with defaults
|
|
2839
|
+
export default {
|
|
2840
|
+
// Model provider API keys (optional, defaults shown)
|
|
2841
|
+
providers: {
|
|
2842
|
+
anthropic: { apiKeyEnv: "ANTHROPIC_API_KEY" },
|
|
2843
|
+
openai: { apiKeyEnv: "OPENAI_API_KEY" },
|
|
2844
|
+
},
|
|
2845
|
+
auth: {
|
|
2846
|
+
required: true,
|
|
2847
|
+
tokenEnv: "PONCHO_AUTH_TOKEN", // default
|
|
2848
|
+
},
|
|
2849
|
+
storage: {
|
|
2850
|
+
provider: "upstash",
|
|
2851
|
+
urlEnv: "UPSTASH_REDIS_REST_URL", // default (falls back to KV_REST_API_URL)
|
|
2852
|
+
tokenEnv: "UPSTASH_REDIS_REST_TOKEN", // default (falls back to KV_REST_API_TOKEN)
|
|
2853
|
+
},
|
|
2854
|
+
telemetry: {
|
|
2855
|
+
latitude: {
|
|
2856
|
+
apiKeyEnv: "LATITUDE_API_KEY", // default
|
|
2857
|
+
projectIdEnv: "LATITUDE_PROJECT_ID", // default
|
|
2858
|
+
},
|
|
2859
|
+
},
|
|
2860
|
+
messaging: [{ platform: "slack" }], // reads SLACK_BOT_TOKEN, SLACK_SIGNING_SECRET by default
|
|
2861
|
+
}
|
|
2862
|
+
\`\`\`
|
|
2863
|
+
|
|
2864
|
+
Since all fields have defaults, you only need to specify \`*Env\` when your env var name differs from the convention.
|
|
2827
2865
|
|
|
2828
2866
|
## When users ask about customization:
|
|
2829
2867
|
|
|
@@ -2838,6 +2876,7 @@ telemetry: {
|
|
|
2838
2876
|
- To scope tools to a skill: keep server config in \`poncho.config.js\`, add desired \`allowed-tools\`/ \`approval-required\` patterns in that skill's \`SKILL.md\`, and remove global \`AGENT.md\` patterns if you do not want global availability.
|
|
2839
2877
|
- Do not invent unsupported top-level config keys (for example \`model\` in \`poncho.config.js\`). Keep existing config structure unless README/spec explicitly says otherwise.
|
|
2840
2878
|
- Keep \`poncho.config.js\` valid JavaScript and preserve existing imports/types/comments. If there is a JSDoc type import, do not rewrite it to a different package name.
|
|
2879
|
+
- Credentials always use \`*Env\` fields (env var names), never raw \`process.env.*\` values. For example, use \`apiKeyEnv: "MY_KEY"\` not \`apiKey: process.env.MY_KEY\`.
|
|
2841
2880
|
- Preferred MCP config shape in \`poncho.config.js\`:
|
|
2842
2881
|
\`mcp: [{ name: "linear", url: "https://mcp.linear.app/mcp", auth: { type: "bearer", tokenEnv: "LINEAR_TOKEN" } }]\`
|
|
2843
2882
|
- If shell/CLI access exists, you can use \`poncho mcp add --url ... --name ... --auth-bearer-env ...\`, then \`poncho mcp tools list <server>\` and \`poncho mcp tools select <server>\`.
|
|
@@ -2851,7 +2890,6 @@ var AgentHarness = class {
|
|
|
2851
2890
|
modelProvider;
|
|
2852
2891
|
modelProviderInjected;
|
|
2853
2892
|
dispatcher = new ToolDispatcher();
|
|
2854
|
-
approvalHandler;
|
|
2855
2893
|
uploadStore;
|
|
2856
2894
|
skillContextWindow = "";
|
|
2857
2895
|
memoryStore;
|
|
@@ -2925,7 +2963,6 @@ var AgentHarness = class {
|
|
|
2925
2963
|
this.environment = options.environment ?? "development";
|
|
2926
2964
|
this.modelProviderInjected = !!options.modelProvider;
|
|
2927
2965
|
this.modelProvider = options.modelProvider ?? createModelProvider("anthropic");
|
|
2928
|
-
this.approvalHandler = options.approvalHandler;
|
|
2929
2966
|
this.uploadStore = options.uploadStore;
|
|
2930
2967
|
if (options.toolDefinitions?.length) {
|
|
2931
2968
|
this.dispatcher.registerMany(options.toolDefinitions);
|
|
@@ -3159,7 +3196,7 @@ var AgentHarness = class {
|
|
|
3159
3196
|
const provider = this.parsedAgent.frontmatter.model?.provider ?? "anthropic";
|
|
3160
3197
|
const memoryConfig = resolveMemoryConfig(config);
|
|
3161
3198
|
if (!this.modelProviderInjected) {
|
|
3162
|
-
this.modelProvider = createModelProvider(provider);
|
|
3199
|
+
this.modelProvider = createModelProvider(provider, config?.providers);
|
|
3163
3200
|
}
|
|
3164
3201
|
const bridge = new LocalMcpBridge(config);
|
|
3165
3202
|
this.mcpBridge = bridge;
|
|
@@ -3186,10 +3223,12 @@ var AgentHarness = class {
|
|
|
3186
3223
|
await bridge.discoverTools();
|
|
3187
3224
|
await this.refreshMcpTools("initialize");
|
|
3188
3225
|
const telemetryEnabled = config?.telemetry?.enabled !== false;
|
|
3189
|
-
const latitudeApiKey = config?.telemetry?.latitude?.apiKey;
|
|
3190
|
-
const rawProjectId = config?.telemetry?.latitude?.projectId;
|
|
3191
|
-
const latitudeProjectId = typeof rawProjectId === "string" ? parseInt(rawProjectId, 10) : rawProjectId;
|
|
3192
3226
|
const latitudeBlock = config?.telemetry?.latitude;
|
|
3227
|
+
const latApiKeyEnv = latitudeBlock?.apiKeyEnv ?? "LATITUDE_API_KEY";
|
|
3228
|
+
const latProjectIdEnv = latitudeBlock?.projectIdEnv ?? "LATITUDE_PROJECT_ID";
|
|
3229
|
+
const latitudeApiKey = process.env[latApiKeyEnv];
|
|
3230
|
+
const rawProjectId = process.env[latProjectIdEnv];
|
|
3231
|
+
const latitudeProjectId = rawProjectId ? parseInt(rawProjectId, 10) : void 0;
|
|
3193
3232
|
if (telemetryEnabled && latitudeApiKey && latitudeProjectId) {
|
|
3194
3233
|
diag.setLogger(
|
|
3195
3234
|
{
|
|
@@ -3211,14 +3250,10 @@ var AgentHarness = class {
|
|
|
3211
3250
|
this.latitudeTelemetry = new LatitudeTelemetry(latitudeApiKey, { disableBatch: true });
|
|
3212
3251
|
} else if (telemetryEnabled && latitudeBlock && (!latitudeApiKey || !latitudeProjectId)) {
|
|
3213
3252
|
const missing = [];
|
|
3214
|
-
if (!latitudeApiKey) missing.push(
|
|
3215
|
-
if (!latitudeProjectId) missing.push(
|
|
3216
|
-
const unknownKeys = Object.keys(latitudeBlock).filter(
|
|
3217
|
-
(k) => !["apiKey", "projectId", "path", "documentPath"].includes(k)
|
|
3218
|
-
);
|
|
3219
|
-
const hint = unknownKeys.length > 0 ? ` (found unknown key${unknownKeys.length > 1 ? "s" : ""}: ${unknownKeys.join(", ")} \u2013 did you mean "apiKey"?)` : "";
|
|
3253
|
+
if (!latitudeApiKey) missing.push(`${latApiKeyEnv} env var`);
|
|
3254
|
+
if (!latitudeProjectId) missing.push(`${latProjectIdEnv} env var`);
|
|
3220
3255
|
console.warn(
|
|
3221
|
-
`[poncho][telemetry] Latitude telemetry is configured but missing: ${missing.join(", ")}
|
|
3256
|
+
`[poncho][telemetry] Latitude telemetry is configured but missing: ${missing.join(", ")}. Traces will NOT be sent.`
|
|
3222
3257
|
);
|
|
3223
3258
|
}
|
|
3224
3259
|
}
|
|
@@ -3244,8 +3279,8 @@ var AgentHarness = class {
|
|
|
3244
3279
|
const config = this.loadedConfig;
|
|
3245
3280
|
const telemetry = this.latitudeTelemetry;
|
|
3246
3281
|
if (telemetry) {
|
|
3247
|
-
const
|
|
3248
|
-
const projectId =
|
|
3282
|
+
const latProjectIdEnv2 = config?.telemetry?.latitude?.projectIdEnv ?? "LATITUDE_PROJECT_ID";
|
|
3283
|
+
const projectId = parseInt(process.env[latProjectIdEnv2] ?? "", 10);
|
|
3249
3284
|
const rawPath = config?.telemetry?.latitude?.path ?? this.parsedAgent?.frontmatter.name ?? "agent";
|
|
3250
3285
|
const path = rawPath.replace(/[^\w\-./]/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "") || "agent";
|
|
3251
3286
|
const conversationUuid = input.conversationId ?? (typeof input.parameters?.__activeConversationId === "string" ? input.parameters.__activeConversationId : void 0);
|
|
@@ -3317,6 +3352,7 @@ var AgentHarness = class {
|
|
|
3317
3352
|
const platformMaxDurationSec = Number(process.env.PONCHO_MAX_DURATION) || 0;
|
|
3318
3353
|
const softDeadlineMs = platformMaxDurationSec > 0 ? platformMaxDurationSec * 800 : 0;
|
|
3319
3354
|
const messages = [...input.messages ?? []];
|
|
3355
|
+
const inputMessageCount = messages.length;
|
|
3320
3356
|
const events = [];
|
|
3321
3357
|
const systemPrompt = renderAgentPrompt(agent, {
|
|
3322
3358
|
parameters: input.parameters,
|
|
@@ -3366,41 +3402,43 @@ ${boundedMainMemory.trim()}` : "";
|
|
|
3366
3402
|
agentId: agent.frontmatter.id ?? agent.frontmatter.name,
|
|
3367
3403
|
contextWindow
|
|
3368
3404
|
});
|
|
3369
|
-
if (input.
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3405
|
+
if (input.task != null) {
|
|
3406
|
+
if (input.files && input.files.length > 0) {
|
|
3407
|
+
const parts = [
|
|
3408
|
+
{ type: "text", text: input.task }
|
|
3409
|
+
];
|
|
3410
|
+
for (const file of input.files) {
|
|
3411
|
+
if (this.uploadStore) {
|
|
3412
|
+
const buf = Buffer.from(file.data, "base64");
|
|
3413
|
+
const key = deriveUploadKey(buf, file.mediaType);
|
|
3414
|
+
const ref = await this.uploadStore.put(key, buf, file.mediaType);
|
|
3415
|
+
parts.push({
|
|
3416
|
+
type: "file",
|
|
3417
|
+
data: ref,
|
|
3418
|
+
mediaType: file.mediaType,
|
|
3419
|
+
filename: file.filename
|
|
3420
|
+
});
|
|
3421
|
+
} else {
|
|
3422
|
+
parts.push({
|
|
3423
|
+
type: "file",
|
|
3424
|
+
data: file.data,
|
|
3425
|
+
mediaType: file.mediaType,
|
|
3426
|
+
filename: file.filename
|
|
3427
|
+
});
|
|
3428
|
+
}
|
|
3391
3429
|
}
|
|
3430
|
+
messages.push({
|
|
3431
|
+
role: "user",
|
|
3432
|
+
content: parts,
|
|
3433
|
+
metadata: { timestamp: now(), id: randomUUID3() }
|
|
3434
|
+
});
|
|
3435
|
+
} else {
|
|
3436
|
+
messages.push({
|
|
3437
|
+
role: "user",
|
|
3438
|
+
content: input.task,
|
|
3439
|
+
metadata: { timestamp: now(), id: randomUUID3() }
|
|
3440
|
+
});
|
|
3392
3441
|
}
|
|
3393
|
-
messages.push({
|
|
3394
|
-
role: "user",
|
|
3395
|
-
content: parts,
|
|
3396
|
-
metadata: { timestamp: now(), id: randomUUID3() }
|
|
3397
|
-
});
|
|
3398
|
-
} else {
|
|
3399
|
-
messages.push({
|
|
3400
|
-
role: "user",
|
|
3401
|
-
content: input.task,
|
|
3402
|
-
metadata: { timestamp: now(), id: randomUUID3() }
|
|
3403
|
-
});
|
|
3404
3442
|
}
|
|
3405
3443
|
let responseText = "";
|
|
3406
3444
|
let totalInputTokens = 0;
|
|
@@ -3640,7 +3678,6 @@ ${textContent}` };
|
|
|
3640
3678
|
const modelInstance = this.modelProvider(modelName);
|
|
3641
3679
|
const cachedMessages = addPromptCacheBreakpoints(coreMessages, modelInstance);
|
|
3642
3680
|
const telemetryEnabled = this.loadedConfig?.telemetry?.enabled !== false;
|
|
3643
|
-
const latitudeApiKey = this.loadedConfig?.telemetry?.latitude?.apiKey;
|
|
3644
3681
|
const result = await streamText({
|
|
3645
3682
|
model: modelInstance,
|
|
3646
3683
|
system: integrityPrompt,
|
|
@@ -3650,7 +3687,7 @@ ${textContent}` };
|
|
|
3650
3687
|
abortSignal: input.abortSignal,
|
|
3651
3688
|
...typeof maxTokens === "number" ? { maxTokens } : {},
|
|
3652
3689
|
experimental_telemetry: {
|
|
3653
|
-
isEnabled: telemetryEnabled && !!
|
|
3690
|
+
isEnabled: telemetryEnabled && !!this.latitudeTelemetry
|
|
3654
3691
|
}
|
|
3655
3692
|
});
|
|
3656
3693
|
let fullText = "";
|
|
@@ -3843,45 +3880,34 @@ ${textContent}` };
|
|
|
3843
3880
|
input: call.input,
|
|
3844
3881
|
approvalId
|
|
3845
3882
|
});
|
|
3846
|
-
const
|
|
3883
|
+
const assistantContent2 = JSON.stringify({
|
|
3884
|
+
text: fullText,
|
|
3885
|
+
tool_calls: toolCalls.map((tc) => ({
|
|
3886
|
+
id: tc.id,
|
|
3887
|
+
name: exposedToolNames.get(tc.name) ?? tc.name,
|
|
3888
|
+
input: tc.input
|
|
3889
|
+
}))
|
|
3890
|
+
});
|
|
3891
|
+
const assistantMsg = {
|
|
3892
|
+
role: "assistant",
|
|
3893
|
+
content: assistantContent2,
|
|
3894
|
+
metadata: { timestamp: now(), id: randomUUID3(), step }
|
|
3895
|
+
};
|
|
3896
|
+
const deltaMessages = [...messages.slice(inputMessageCount), assistantMsg];
|
|
3897
|
+
yield pushEvent({
|
|
3898
|
+
type: "tool:approval:checkpoint",
|
|
3899
|
+
approvalId,
|
|
3847
3900
|
tool: runtimeToolName,
|
|
3901
|
+
toolCallId: call.id,
|
|
3848
3902
|
input: call.input,
|
|
3849
|
-
|
|
3850
|
-
|
|
3851
|
-
|
|
3852
|
-
|
|
3853
|
-
|
|
3854
|
-
|
|
3855
|
-
|
|
3856
|
-
|
|
3857
|
-
if (!approved) {
|
|
3858
|
-
if (this.insideTelemetryCapture && this.latitudeTelemetry) {
|
|
3859
|
-
const deniedSpan = this.latitudeTelemetry.span.tool({
|
|
3860
|
-
name: runtimeToolName,
|
|
3861
|
-
call: { id: call.id, arguments: call.input }
|
|
3862
|
-
});
|
|
3863
|
-
deniedSpan.end({ result: { value: "Tool execution denied by approval policy", isError: true } });
|
|
3864
|
-
}
|
|
3865
|
-
yield pushEvent({
|
|
3866
|
-
type: "tool:approval:denied",
|
|
3867
|
-
approvalId,
|
|
3868
|
-
reason: "No approval handler granted execution"
|
|
3869
|
-
});
|
|
3870
|
-
yield pushEvent({
|
|
3871
|
-
type: "tool:error",
|
|
3872
|
-
tool: call.name,
|
|
3873
|
-
error: "Tool execution denied by approval policy",
|
|
3874
|
-
recoverable: true
|
|
3875
|
-
});
|
|
3876
|
-
toolResultsForModel.push({
|
|
3877
|
-
type: "tool_result",
|
|
3878
|
-
tool_use_id: call.id,
|
|
3879
|
-
tool_name: runtimeToolName,
|
|
3880
|
-
content: "Tool error: Tool execution denied by approval policy"
|
|
3881
|
-
});
|
|
3882
|
-
continue;
|
|
3883
|
-
}
|
|
3884
|
-
yield pushEvent({ type: "tool:approval:granted", approvalId });
|
|
3903
|
+
checkpointMessages: deltaMessages,
|
|
3904
|
+
pendingToolCalls: toolCalls.map((tc) => ({
|
|
3905
|
+
id: tc.id,
|
|
3906
|
+
name: exposedToolNames.get(tc.name) ?? tc.name,
|
|
3907
|
+
input: tc.input
|
|
3908
|
+
}))
|
|
3909
|
+
});
|
|
3910
|
+
return;
|
|
3885
3911
|
}
|
|
3886
3912
|
approvedCalls.push({
|
|
3887
3913
|
id: call.id,
|
|
@@ -4015,12 +4041,64 @@ ${textContent}` };
|
|
|
4015
4041
|
});
|
|
4016
4042
|
}
|
|
4017
4043
|
}
|
|
4044
|
+
async executeTools(calls, context) {
|
|
4045
|
+
return this.dispatcher.executeBatch(calls, context);
|
|
4046
|
+
}
|
|
4047
|
+
async *continueFromToolResult(input) {
|
|
4048
|
+
const messages = [...input.messages];
|
|
4049
|
+
const lastMsg = messages[messages.length - 1];
|
|
4050
|
+
if (!lastMsg || lastMsg.role !== "assistant") {
|
|
4051
|
+
throw new Error("continueFromToolResult: last message must be an assistant message with tool calls");
|
|
4052
|
+
}
|
|
4053
|
+
let allToolCalls = [];
|
|
4054
|
+
try {
|
|
4055
|
+
const parsed = JSON.parse(typeof lastMsg.content === "string" ? lastMsg.content : "");
|
|
4056
|
+
allToolCalls = parsed.tool_calls ?? [];
|
|
4057
|
+
} catch {
|
|
4058
|
+
throw new Error("continueFromToolResult: could not parse tool calls from last assistant message");
|
|
4059
|
+
}
|
|
4060
|
+
const providedMap = new Map(
|
|
4061
|
+
input.toolResults.map((r) => [r.callId, r])
|
|
4062
|
+
);
|
|
4063
|
+
const toolResultsForModel = [];
|
|
4064
|
+
for (const tc of allToolCalls) {
|
|
4065
|
+
const provided = providedMap.get(tc.id);
|
|
4066
|
+
if (provided) {
|
|
4067
|
+
toolResultsForModel.push({
|
|
4068
|
+
type: "tool_result",
|
|
4069
|
+
tool_use_id: tc.id,
|
|
4070
|
+
tool_name: provided.toolName,
|
|
4071
|
+
content: provided.error ? `Tool error: ${provided.error}` : JSON.stringify(provided.result ?? null)
|
|
4072
|
+
});
|
|
4073
|
+
} else {
|
|
4074
|
+
toolResultsForModel.push({
|
|
4075
|
+
type: "tool_result",
|
|
4076
|
+
tool_use_id: tc.id,
|
|
4077
|
+
tool_name: tc.name,
|
|
4078
|
+
content: "Tool error: Tool execution deferred (pending approval checkpoint)"
|
|
4079
|
+
});
|
|
4080
|
+
}
|
|
4081
|
+
}
|
|
4082
|
+
messages.push({
|
|
4083
|
+
role: "tool",
|
|
4084
|
+
content: JSON.stringify(toolResultsForModel),
|
|
4085
|
+
metadata: { timestamp: Date.now(), id: randomUUID3() }
|
|
4086
|
+
});
|
|
4087
|
+
yield* this.runWithTelemetry({
|
|
4088
|
+
messages,
|
|
4089
|
+
conversationId: input.conversationId,
|
|
4090
|
+
parameters: input.parameters,
|
|
4091
|
+
abortSignal: input.abortSignal
|
|
4092
|
+
});
|
|
4093
|
+
}
|
|
4018
4094
|
async runToCompletion(input) {
|
|
4019
4095
|
const events = [];
|
|
4020
4096
|
let runId = "";
|
|
4021
4097
|
let finalResult;
|
|
4022
4098
|
const messages = [...input.messages ?? []];
|
|
4023
|
-
|
|
4099
|
+
if (input.task != null) {
|
|
4100
|
+
messages.push({ role: "user", content: input.task });
|
|
4101
|
+
}
|
|
4024
4102
|
for await (const event of this.runWithTelemetry(input)) {
|
|
4025
4103
|
events.push(event);
|
|
4026
4104
|
if (event.type === "run:started") {
|
|
@@ -4072,9 +4150,11 @@ var LatitudeCapture = class {
|
|
|
4072
4150
|
projectId;
|
|
4073
4151
|
path;
|
|
4074
4152
|
constructor(config) {
|
|
4075
|
-
|
|
4076
|
-
|
|
4077
|
-
const
|
|
4153
|
+
const apiKeyEnv = config?.apiKeyEnv ?? "LATITUDE_API_KEY";
|
|
4154
|
+
this.apiKey = process.env[apiKeyEnv];
|
|
4155
|
+
const projectIdEnv = config?.projectIdEnv ?? "LATITUDE_PROJECT_ID";
|
|
4156
|
+
const rawProjectId = process.env[projectIdEnv];
|
|
4157
|
+
const projectIdNumber = rawProjectId ? Number.parseInt(rawProjectId, 10) : Number.NaN;
|
|
4078
4158
|
this.projectId = Number.isFinite(projectIdNumber) ? projectIdNumber : void 0;
|
|
4079
4159
|
const rawPath = config?.path ?? process.env.LATITUDE_PATH ?? process.env.LATITUDE_DOCUMENT_PATH ?? config?.defaultPath;
|
|
4080
4160
|
this.path = rawPath;
|
|
@@ -4982,15 +5062,18 @@ var createStateStore = (config, options) => {
|
|
|
4982
5062
|
return new InMemoryStateStore(ttl);
|
|
4983
5063
|
}
|
|
4984
5064
|
if (provider === "upstash") {
|
|
4985
|
-
const
|
|
4986
|
-
const
|
|
5065
|
+
const urlEnv = config?.urlEnv ?? (process.env.UPSTASH_REDIS_REST_URL ? "UPSTASH_REDIS_REST_URL" : "KV_REST_API_URL");
|
|
5066
|
+
const tokenEnv = config?.tokenEnv ?? (process.env.UPSTASH_REDIS_REST_TOKEN ? "UPSTASH_REDIS_REST_TOKEN" : "KV_REST_API_TOKEN");
|
|
5067
|
+
const url = process.env[urlEnv] ?? "";
|
|
5068
|
+
const token = process.env[tokenEnv] ?? "";
|
|
4987
5069
|
if (url && token) {
|
|
4988
5070
|
return new UpstashStateStore(url, token, ttl);
|
|
4989
5071
|
}
|
|
4990
5072
|
return new InMemoryStateStore(ttl);
|
|
4991
5073
|
}
|
|
4992
5074
|
if (provider === "redis") {
|
|
4993
|
-
const
|
|
5075
|
+
const urlEnv = config?.urlEnv ?? "REDIS_URL";
|
|
5076
|
+
const url = process.env[urlEnv] ?? "";
|
|
4994
5077
|
if (url) {
|
|
4995
5078
|
return new RedisLikeStateStore(url, ttl);
|
|
4996
5079
|
}
|
|
@@ -5016,15 +5099,18 @@ var createConversationStore = (config, options) => {
|
|
|
5016
5099
|
return new InMemoryConversationStore(ttl);
|
|
5017
5100
|
}
|
|
5018
5101
|
if (provider === "upstash") {
|
|
5019
|
-
const
|
|
5020
|
-
const
|
|
5102
|
+
const urlEnv = config?.urlEnv ?? (process.env.UPSTASH_REDIS_REST_URL ? "UPSTASH_REDIS_REST_URL" : "KV_REST_API_URL");
|
|
5103
|
+
const tokenEnv = config?.tokenEnv ?? (process.env.UPSTASH_REDIS_REST_TOKEN ? "UPSTASH_REDIS_REST_TOKEN" : "KV_REST_API_TOKEN");
|
|
5104
|
+
const url = process.env[urlEnv] ?? "";
|
|
5105
|
+
const token = process.env[tokenEnv] ?? "";
|
|
5021
5106
|
if (url && token) {
|
|
5022
5107
|
return new UpstashConversationStore(url, token, workingDir, ttl, options?.agentId);
|
|
5023
5108
|
}
|
|
5024
5109
|
return new InMemoryConversationStore(ttl);
|
|
5025
5110
|
}
|
|
5026
5111
|
if (provider === "redis") {
|
|
5027
|
-
const
|
|
5112
|
+
const urlEnv = config?.urlEnv ?? "REDIS_URL";
|
|
5113
|
+
const url = process.env[urlEnv] ?? "";
|
|
5028
5114
|
if (url) {
|
|
5029
5115
|
return new RedisLikeConversationStore(url, workingDir, ttl, options?.agentId);
|
|
5030
5116
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@poncho-ai/harness",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.2",
|
|
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.0.
|
|
34
|
+
"@poncho-ai/sdk": "1.0.3"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@types/mustache": "^4.2.6",
|
package/src/config.ts
CHANGED
|
@@ -7,8 +7,8 @@ import type { StateConfig } from "./state.js";
|
|
|
7
7
|
|
|
8
8
|
export interface StorageConfig {
|
|
9
9
|
provider?: "local" | "memory" | "redis" | "upstash" | "dynamodb";
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
urlEnv?: string;
|
|
11
|
+
tokenEnv?: string;
|
|
12
12
|
table?: string;
|
|
13
13
|
region?: string;
|
|
14
14
|
ttl?:
|
|
@@ -76,6 +76,7 @@ export interface PonchoConfig extends McpConfig {
|
|
|
76
76
|
required?: boolean;
|
|
77
77
|
type?: "bearer" | "header" | "custom";
|
|
78
78
|
headerName?: string;
|
|
79
|
+
tokenEnv?: string;
|
|
79
80
|
validate?: (token: string, req?: unknown) => Promise<boolean> | boolean;
|
|
80
81
|
};
|
|
81
82
|
state?: {
|
|
@@ -85,12 +86,16 @@ export interface PonchoConfig extends McpConfig {
|
|
|
85
86
|
};
|
|
86
87
|
memory?: MemoryConfig;
|
|
87
88
|
storage?: StorageConfig;
|
|
89
|
+
providers?: {
|
|
90
|
+
openai?: { apiKeyEnv?: string };
|
|
91
|
+
anthropic?: { apiKeyEnv?: string };
|
|
92
|
+
};
|
|
88
93
|
telemetry?: {
|
|
89
94
|
enabled?: boolean;
|
|
90
95
|
otlp?: string;
|
|
91
96
|
latitude?: {
|
|
92
|
-
|
|
93
|
-
|
|
97
|
+
apiKeyEnv?: string;
|
|
98
|
+
projectIdEnv?: string;
|
|
94
99
|
path?: string;
|
|
95
100
|
documentPath?: string;
|
|
96
101
|
};
|
|
@@ -130,8 +135,8 @@ export const resolveStateConfig = (
|
|
|
130
135
|
if (config?.storage) {
|
|
131
136
|
return {
|
|
132
137
|
provider: config.storage.provider,
|
|
133
|
-
|
|
134
|
-
|
|
138
|
+
urlEnv: config.storage.urlEnv,
|
|
139
|
+
tokenEnv: config.storage.tokenEnv,
|
|
135
140
|
table: config.storage.table,
|
|
136
141
|
region: config.storage.region,
|
|
137
142
|
ttl: resolveTtl(config.storage.ttl, "conversations"),
|
|
@@ -147,8 +152,8 @@ export const resolveMemoryConfig = (
|
|
|
147
152
|
return {
|
|
148
153
|
enabled: config.storage.memory?.enabled ?? config.memory?.enabled,
|
|
149
154
|
provider: config.storage.provider,
|
|
150
|
-
|
|
151
|
-
|
|
155
|
+
urlEnv: config.storage.urlEnv,
|
|
156
|
+
tokenEnv: config.storage.tokenEnv,
|
|
152
157
|
table: config.storage.table,
|
|
153
158
|
region: config.storage.region,
|
|
154
159
|
ttl: resolveTtl(config.storage.ttl, "memory"),
|