@posthog/agent 2.3.351 → 2.3.354
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/dist/adapters/claude/permissions/permission-options.d.ts +1 -1
- package/dist/adapters/claude/permissions/permission-options.js +3 -3
- package/dist/adapters/claude/permissions/permission-options.js.map +1 -1
- package/dist/agent.js +5936 -135
- package/dist/agent.js.map +1 -1
- package/dist/posthog-api.js +1 -1
- package/dist/posthog-api.js.map +1 -1
- package/dist/server/agent-server.d.ts +2 -0
- package/dist/server/agent-server.js +222 -54
- package/dist/server/agent-server.js.map +1 -1
- package/dist/server/bin.cjs +220 -52
- package/dist/server/bin.cjs.map +1 -1
- package/package.json +3 -3
- package/src/adapters/claude/conversion/sdk-to-acp.ts +31 -1
- package/src/adapters/claude/permissions/permission-handlers.ts +53 -10
- package/src/adapters/claude/permissions/permission-options.ts +3 -3
- package/src/adapters/claude/session/repo-path.ts +22 -0
- package/src/adapters/claude/session/settings.test.ts +159 -0
- package/src/adapters/claude/session/settings.ts +92 -6
- package/src/server/agent-server.ts +54 -3
- package/src/server/question-relay.test.ts +124 -0
package/dist/server/bin.cjs
CHANGED
|
@@ -8688,6 +8688,32 @@ async function getCurrentBranch(baseDir, options) {
|
|
|
8688
8688
|
return branch === "HEAD" ? null : branch;
|
|
8689
8689
|
}, { signal: options?.abortSignal });
|
|
8690
8690
|
}
|
|
8691
|
+
async function listWorktrees(baseDir, options) {
|
|
8692
|
+
const manager = getGitOperationManager();
|
|
8693
|
+
return manager.executeRead(baseDir, async (git) => {
|
|
8694
|
+
const output = await git.raw(["worktree", "list", "--porcelain"]);
|
|
8695
|
+
const worktrees = [];
|
|
8696
|
+
let current2 = {};
|
|
8697
|
+
for (const line of output.split("\n")) {
|
|
8698
|
+
if (line.startsWith("worktree ")) {
|
|
8699
|
+
if (current2.path) {
|
|
8700
|
+
worktrees.push(current2);
|
|
8701
|
+
}
|
|
8702
|
+
current2 = { path: line.slice(9), branch: null };
|
|
8703
|
+
} else if (line.startsWith("HEAD ")) {
|
|
8704
|
+
current2.head = line.slice(5);
|
|
8705
|
+
} else if (line.startsWith("branch ")) {
|
|
8706
|
+
current2.branch = line.slice(7).replace("refs/heads/", "");
|
|
8707
|
+
} else if (line === "detached") {
|
|
8708
|
+
current2.branch = null;
|
|
8709
|
+
}
|
|
8710
|
+
}
|
|
8711
|
+
if (current2.path) {
|
|
8712
|
+
worktrees.push(current2);
|
|
8713
|
+
}
|
|
8714
|
+
return worktrees;
|
|
8715
|
+
}, { signal: options?.abortSignal });
|
|
8716
|
+
}
|
|
8691
8717
|
async function getHeadSha(baseDir, options) {
|
|
8692
8718
|
const manager = getGitOperationManager();
|
|
8693
8719
|
return manager.executeRead(baseDir, (git) => git.revparse(["HEAD"]), {
|
|
@@ -8697,11 +8723,12 @@ async function getHeadSha(baseDir, options) {
|
|
|
8697
8723
|
|
|
8698
8724
|
// src/server/agent-server.ts
|
|
8699
8725
|
var import_hono = require("hono");
|
|
8726
|
+
var import_zod3 = require("zod");
|
|
8700
8727
|
|
|
8701
8728
|
// package.json
|
|
8702
8729
|
var package_default = {
|
|
8703
8730
|
name: "@posthog/agent",
|
|
8704
|
-
version: "2.3.
|
|
8731
|
+
version: "2.3.354",
|
|
8705
8732
|
repository: "https://github.com/PostHog/code",
|
|
8706
8733
|
description: "TypeScript agent framework wrapping Claude Agent SDK with Git-based task execution for PostHog",
|
|
8707
8734
|
exports: {
|
|
@@ -13857,6 +13884,17 @@ async function handleSystemMessage(message, context) {
|
|
|
13857
13884
|
break;
|
|
13858
13885
|
}
|
|
13859
13886
|
}
|
|
13887
|
+
function classifyAgentError(result) {
|
|
13888
|
+
if (!result) return "agent_error";
|
|
13889
|
+
const text2 = result.trim();
|
|
13890
|
+
if (/API Error:\s*terminated\b/i.test(text2)) {
|
|
13891
|
+
return "upstream_stream_terminated";
|
|
13892
|
+
}
|
|
13893
|
+
if (/API Error:\s*Connection error\b/i.test(text2)) {
|
|
13894
|
+
return "upstream_connection_error";
|
|
13895
|
+
}
|
|
13896
|
+
return "agent_error";
|
|
13897
|
+
}
|
|
13860
13898
|
function handleResultMessage(message) {
|
|
13861
13899
|
const usage = extractUsageFromResult(message);
|
|
13862
13900
|
switch (message.subtype) {
|
|
@@ -13872,9 +13910,13 @@ function handleResultMessage(message) {
|
|
|
13872
13910
|
return { shouldStop: true, stopReason: "max_tokens", usage };
|
|
13873
13911
|
}
|
|
13874
13912
|
if (message.is_error) {
|
|
13913
|
+
const classification = classifyAgentError(message.result);
|
|
13875
13914
|
return {
|
|
13876
13915
|
shouldStop: true,
|
|
13877
|
-
error: import_sdk.RequestError.internalError(
|
|
13916
|
+
error: import_sdk.RequestError.internalError(
|
|
13917
|
+
{ classification, result: message.result },
|
|
13918
|
+
message.result
|
|
13919
|
+
),
|
|
13878
13920
|
usage
|
|
13879
13921
|
};
|
|
13880
13922
|
}
|
|
@@ -14231,16 +14273,16 @@ function permissionOptions(allowAlwaysLabel) {
|
|
|
14231
14273
|
}
|
|
14232
14274
|
];
|
|
14233
14275
|
}
|
|
14234
|
-
function buildPermissionOptions(toolName, toolInput,
|
|
14276
|
+
function buildPermissionOptions(toolName, toolInput, repoRoot, suggestions) {
|
|
14235
14277
|
if (BASH_TOOLS.has(toolName)) {
|
|
14236
14278
|
const rawRuleContent = suggestions?.flatMap((s) => "rules" in s ? s.rules : []).find((r) => r.toolName === "Bash" && r.ruleContent)?.ruleContent;
|
|
14237
14279
|
const ruleContent = rawRuleContent?.replace(/:?\*$/, "");
|
|
14238
14280
|
const command = toolInput?.command;
|
|
14239
14281
|
const cmdName = command?.split(/\s+/)[0] ?? "this command";
|
|
14240
|
-
const
|
|
14282
|
+
const scopeLabel = repoRoot ? ` in ${repoRoot}` : "";
|
|
14241
14283
|
const label = ruleContent ?? `\`${cmdName}\` commands`;
|
|
14242
14284
|
return permissionOptions(
|
|
14243
|
-
`Yes, and don't ask again for ${label}${
|
|
14285
|
+
`Yes, and don't ask again for ${label}${scopeLabel}`
|
|
14244
14286
|
);
|
|
14245
14287
|
}
|
|
14246
14288
|
if (toolName === "BashOutput") {
|
|
@@ -14526,7 +14568,7 @@ async function handleDefaultPermissionFlow(context) {
|
|
|
14526
14568
|
const options = buildPermissionOptions(
|
|
14527
14569
|
toolName,
|
|
14528
14570
|
toolInput,
|
|
14529
|
-
session
|
|
14571
|
+
session.settingsManager.getRepoRoot(),
|
|
14530
14572
|
suggestions
|
|
14531
14573
|
);
|
|
14532
14574
|
const response = await client.requestPermission({
|
|
@@ -14546,17 +14588,19 @@ async function handleDefaultPermissionFlow(context) {
|
|
|
14546
14588
|
}
|
|
14547
14589
|
if (response.outcome?.outcome === "selected" && (response.outcome.optionId === "allow" || response.outcome.optionId === "allow_always")) {
|
|
14548
14590
|
if (response.outcome.optionId === "allow_always") {
|
|
14591
|
+
const rules = extractAllowRules(suggestions, toolName);
|
|
14592
|
+
try {
|
|
14593
|
+
await session.settingsManager.addAllowRules(rules);
|
|
14594
|
+
} catch (error) {
|
|
14595
|
+
context.logger.warn(
|
|
14596
|
+
"[canUseTool] Failed to persist allow rules to repository settings",
|
|
14597
|
+
{ error: error instanceof Error ? error.message : String(error) }
|
|
14598
|
+
);
|
|
14599
|
+
}
|
|
14549
14600
|
return {
|
|
14550
14601
|
behavior: "allow",
|
|
14551
14602
|
updatedInput: toolInput,
|
|
14552
|
-
updatedPermissions: suggestions
|
|
14553
|
-
{
|
|
14554
|
-
type: "addRules",
|
|
14555
|
-
rules: [{ toolName }],
|
|
14556
|
-
behavior: "allow",
|
|
14557
|
-
destination: "localSettings"
|
|
14558
|
-
}
|
|
14559
|
-
]
|
|
14603
|
+
updatedPermissions: buildSessionPermissions(suggestions, rules)
|
|
14560
14604
|
};
|
|
14561
14605
|
}
|
|
14562
14606
|
return {
|
|
@@ -14589,6 +14633,26 @@ function handlePlanFileException(context) {
|
|
|
14589
14633
|
updatedInput: toolInput
|
|
14590
14634
|
};
|
|
14591
14635
|
}
|
|
14636
|
+
function extractAllowRules(suggestions, toolName) {
|
|
14637
|
+
if (!suggestions || suggestions.length === 0) {
|
|
14638
|
+
return [{ toolName }];
|
|
14639
|
+
}
|
|
14640
|
+
return suggestions.filter(
|
|
14641
|
+
(update) => update.type === "addRules" && update.behavior === "allow"
|
|
14642
|
+
).flatMap((update) => "rules" in update ? update.rules : []);
|
|
14643
|
+
}
|
|
14644
|
+
function buildSessionPermissions(suggestions, rules) {
|
|
14645
|
+
const passthrough = (suggestions ?? []).filter(
|
|
14646
|
+
(update) => !(update.type === "addRules" && update.behavior === "allow")
|
|
14647
|
+
).map((update) => ({ ...update, destination: "session" }));
|
|
14648
|
+
if (rules.length === 0) {
|
|
14649
|
+
return passthrough;
|
|
14650
|
+
}
|
|
14651
|
+
return [
|
|
14652
|
+
{ type: "addRules", rules, behavior: "allow", destination: "session" },
|
|
14653
|
+
...passthrough
|
|
14654
|
+
];
|
|
14655
|
+
}
|
|
14592
14656
|
function extractDomainFromUrl(url) {
|
|
14593
14657
|
try {
|
|
14594
14658
|
return new URL(url).hostname;
|
|
@@ -14991,6 +15055,47 @@ var fs7 = __toESM(require("fs"), 1);
|
|
|
14991
15055
|
var os3 = __toESM(require("os"), 1);
|
|
14992
15056
|
var path9 = __toESM(require("path"), 1);
|
|
14993
15057
|
var import_minimatch = require("minimatch");
|
|
15058
|
+
|
|
15059
|
+
// src/utils/async-mutex.ts
|
|
15060
|
+
var AsyncMutex = class {
|
|
15061
|
+
locked = false;
|
|
15062
|
+
queue = [];
|
|
15063
|
+
async acquire() {
|
|
15064
|
+
if (!this.locked) {
|
|
15065
|
+
this.locked = true;
|
|
15066
|
+
return;
|
|
15067
|
+
}
|
|
15068
|
+
return new Promise((resolve4) => {
|
|
15069
|
+
this.queue.push(resolve4);
|
|
15070
|
+
});
|
|
15071
|
+
}
|
|
15072
|
+
release() {
|
|
15073
|
+
const next = this.queue.shift();
|
|
15074
|
+
if (next) {
|
|
15075
|
+
next();
|
|
15076
|
+
} else {
|
|
15077
|
+
this.locked = false;
|
|
15078
|
+
}
|
|
15079
|
+
}
|
|
15080
|
+
isLocked() {
|
|
15081
|
+
return this.locked;
|
|
15082
|
+
}
|
|
15083
|
+
get queueLength() {
|
|
15084
|
+
return this.queue.length;
|
|
15085
|
+
}
|
|
15086
|
+
};
|
|
15087
|
+
|
|
15088
|
+
// src/adapters/claude/session/repo-path.ts
|
|
15089
|
+
async function resolveMainRepoPath(cwd) {
|
|
15090
|
+
try {
|
|
15091
|
+
const worktrees = await listWorktrees(cwd);
|
|
15092
|
+
return worktrees[0]?.path ?? cwd;
|
|
15093
|
+
} catch {
|
|
15094
|
+
return cwd;
|
|
15095
|
+
}
|
|
15096
|
+
}
|
|
15097
|
+
|
|
15098
|
+
// src/adapters/claude/session/settings.ts
|
|
14994
15099
|
var ACP_TOOL_NAME_PREFIX = "mcp__acp__";
|
|
14995
15100
|
var acpToolNames = {
|
|
14996
15101
|
read: `${ACP_TOOL_NAME_PREFIX}Read`,
|
|
@@ -15047,7 +15152,7 @@ function matchesGlob(pattern, filePath, cwd) {
|
|
|
15047
15152
|
});
|
|
15048
15153
|
}
|
|
15049
15154
|
function matchesRule(rule, toolName, toolInput, cwd) {
|
|
15050
|
-
const ruleAppliesToTool = rule.toolName === "Bash" && toolName === acpToolNames.bash || rule.toolName === "Edit" && FILE_EDITING_TOOLS.includes(toolName) || rule.toolName === "Read" && FILE_READING_TOOLS.includes(toolName);
|
|
15155
|
+
const ruleAppliesToTool = rule.toolName === "Bash" && toolName === acpToolNames.bash || rule.toolName === "Edit" && FILE_EDITING_TOOLS.includes(toolName) || rule.toolName === "Read" && FILE_READING_TOOLS.includes(toolName) || rule.toolName === toolName && !rule.argument;
|
|
15051
15156
|
if (!ruleAppliesToTool) {
|
|
15052
15157
|
return false;
|
|
15053
15158
|
}
|
|
@@ -15077,6 +15182,19 @@ function matchesRule(rule, toolName, toolInput, cwd) {
|
|
|
15077
15182
|
}
|
|
15078
15183
|
return matchesGlob(rule.argument, actualArg, cwd);
|
|
15079
15184
|
}
|
|
15185
|
+
function formatRule(rule) {
|
|
15186
|
+
return rule.ruleContent ? `${rule.toolName}(${rule.ruleContent})` : rule.toolName;
|
|
15187
|
+
}
|
|
15188
|
+
async function writeFileAtomic(filePath, data) {
|
|
15189
|
+
const tmpPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
|
|
15190
|
+
await fs7.promises.writeFile(tmpPath, data);
|
|
15191
|
+
try {
|
|
15192
|
+
await fs7.promises.rename(tmpPath, filePath);
|
|
15193
|
+
} catch (error) {
|
|
15194
|
+
await fs7.promises.rm(tmpPath, { force: true });
|
|
15195
|
+
throw error;
|
|
15196
|
+
}
|
|
15197
|
+
}
|
|
15080
15198
|
async function loadSettingsFile(filePath) {
|
|
15081
15199
|
if (!filePath) {
|
|
15082
15200
|
return {};
|
|
@@ -15095,6 +15213,17 @@ async function loadSettingsFile(filePath) {
|
|
|
15095
15213
|
return {};
|
|
15096
15214
|
}
|
|
15097
15215
|
}
|
|
15216
|
+
async function readSettingsFileForUpdate(filePath) {
|
|
15217
|
+
try {
|
|
15218
|
+
const content = await fs7.promises.readFile(filePath, "utf-8");
|
|
15219
|
+
return JSON.parse(content);
|
|
15220
|
+
} catch (error) {
|
|
15221
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
15222
|
+
return {};
|
|
15223
|
+
}
|
|
15224
|
+
throw error;
|
|
15225
|
+
}
|
|
15226
|
+
}
|
|
15098
15227
|
function getManagedSettingsPath() {
|
|
15099
15228
|
switch (process.platform) {
|
|
15100
15229
|
case "darwin":
|
|
@@ -15109,6 +15238,7 @@ function getManagedSettingsPath() {
|
|
|
15109
15238
|
}
|
|
15110
15239
|
var SettingsManager = class {
|
|
15111
15240
|
cwd;
|
|
15241
|
+
repoRoot;
|
|
15112
15242
|
userSettings = {};
|
|
15113
15243
|
projectSettings = {};
|
|
15114
15244
|
localSettings = {};
|
|
@@ -15116,8 +15246,10 @@ var SettingsManager = class {
|
|
|
15116
15246
|
mergedSettings = {};
|
|
15117
15247
|
initialized = false;
|
|
15118
15248
|
initPromise = null;
|
|
15249
|
+
writeMutex = new AsyncMutex();
|
|
15119
15250
|
constructor(cwd) {
|
|
15120
15251
|
this.cwd = cwd;
|
|
15252
|
+
this.repoRoot = cwd;
|
|
15121
15253
|
}
|
|
15122
15254
|
async initialize() {
|
|
15123
15255
|
if (this.initialized) return;
|
|
@@ -15135,10 +15267,16 @@ var SettingsManager = class {
|
|
|
15135
15267
|
getProjectSettingsPath() {
|
|
15136
15268
|
return path9.join(this.cwd, ".claude", "settings.json");
|
|
15137
15269
|
}
|
|
15270
|
+
/**
|
|
15271
|
+
* Local settings are anchored to the primary worktree so every worktree of
|
|
15272
|
+
* the same repository shares a single `.claude/settings.local.json`. This
|
|
15273
|
+
* avoids re-prompting for the same permission in every worktree.
|
|
15274
|
+
*/
|
|
15138
15275
|
getLocalSettingsPath() {
|
|
15139
|
-
return path9.join(this.
|
|
15276
|
+
return path9.join(this.repoRoot, ".claude", "settings.local.json");
|
|
15140
15277
|
}
|
|
15141
15278
|
async loadAllSettings() {
|
|
15279
|
+
this.repoRoot = await resolveMainRepoPath(this.cwd);
|
|
15142
15280
|
const [userSettings, projectSettings, localSettings, enterpriseSettings] = await Promise.all([
|
|
15143
15281
|
loadSettingsFile(this.getUserSettingsPath()),
|
|
15144
15282
|
loadSettingsFile(this.getProjectSettingsPath()),
|
|
@@ -15195,9 +15333,6 @@ var SettingsManager = class {
|
|
|
15195
15333
|
this.mergedSettings = merged;
|
|
15196
15334
|
}
|
|
15197
15335
|
checkPermission(toolName, toolInput) {
|
|
15198
|
-
if (!toolName.startsWith(ACP_TOOL_NAME_PREFIX)) {
|
|
15199
|
-
return { decision: "ask" };
|
|
15200
|
-
}
|
|
15201
15336
|
const permissions = this.mergedSettings.permissions;
|
|
15202
15337
|
if (!permissions) {
|
|
15203
15338
|
return { decision: "ask" };
|
|
@@ -15228,6 +15363,43 @@ var SettingsManager = class {
|
|
|
15228
15363
|
getCwd() {
|
|
15229
15364
|
return this.cwd;
|
|
15230
15365
|
}
|
|
15366
|
+
getRepoRoot() {
|
|
15367
|
+
return this.repoRoot;
|
|
15368
|
+
}
|
|
15369
|
+
/**
|
|
15370
|
+
* Persists allow rules to `<primary-worktree>/.claude/settings.local.json`.
|
|
15371
|
+
* Because local settings are resolved against the primary worktree, every
|
|
15372
|
+
* worktree of the same repository picks up the new rule on next load.
|
|
15373
|
+
*
|
|
15374
|
+
* Writes are serialised via `writeMutex` to prevent concurrent callers from
|
|
15375
|
+
* clobbering each other, and use a temp-file + rename to keep the file
|
|
15376
|
+
* consistent if the process dies mid-write.
|
|
15377
|
+
*/
|
|
15378
|
+
async addAllowRules(rules) {
|
|
15379
|
+
if (rules.length === 0) return;
|
|
15380
|
+
if (!this.initialized) await this.initialize();
|
|
15381
|
+
await this.writeMutex.acquire();
|
|
15382
|
+
try {
|
|
15383
|
+
const filePath = this.getLocalSettingsPath();
|
|
15384
|
+
const existing = await readSettingsFileForUpdate(filePath);
|
|
15385
|
+
const permissions = {
|
|
15386
|
+
...existing.permissions ?? {}
|
|
15387
|
+
};
|
|
15388
|
+
const current2 = new Set(permissions.allow ?? []);
|
|
15389
|
+
for (const rule of rules) {
|
|
15390
|
+
current2.add(formatRule(rule));
|
|
15391
|
+
}
|
|
15392
|
+
permissions.allow = Array.from(current2);
|
|
15393
|
+
const next = { ...existing, permissions };
|
|
15394
|
+
await fs7.promises.mkdir(path9.dirname(filePath), { recursive: true });
|
|
15395
|
+
await writeFileAtomic(filePath, `${JSON.stringify(next, null, 2)}
|
|
15396
|
+
`);
|
|
15397
|
+
this.localSettings = next;
|
|
15398
|
+
this.mergeAllSettings();
|
|
15399
|
+
} finally {
|
|
15400
|
+
this.writeMutex.release();
|
|
15401
|
+
}
|
|
15402
|
+
}
|
|
15231
15403
|
async setCwd(cwd) {
|
|
15232
15404
|
if (this.cwd === cwd) return;
|
|
15233
15405
|
if (this.initPromise) await this.initPromise;
|
|
@@ -18813,35 +18985,6 @@ var SessionLogWriter = class _SessionLogWriter {
|
|
|
18813
18985
|
}
|
|
18814
18986
|
};
|
|
18815
18987
|
|
|
18816
|
-
// src/utils/async-mutex.ts
|
|
18817
|
-
var AsyncMutex = class {
|
|
18818
|
-
locked = false;
|
|
18819
|
-
queue = [];
|
|
18820
|
-
async acquire() {
|
|
18821
|
-
if (!this.locked) {
|
|
18822
|
-
this.locked = true;
|
|
18823
|
-
return;
|
|
18824
|
-
}
|
|
18825
|
-
return new Promise((resolve4) => {
|
|
18826
|
-
this.queue.push(resolve4);
|
|
18827
|
-
});
|
|
18828
|
-
}
|
|
18829
|
-
release() {
|
|
18830
|
-
const next = this.queue.shift();
|
|
18831
|
-
if (next) {
|
|
18832
|
-
next();
|
|
18833
|
-
} else {
|
|
18834
|
-
this.locked = false;
|
|
18835
|
-
}
|
|
18836
|
-
}
|
|
18837
|
-
isLocked() {
|
|
18838
|
-
return this.locked;
|
|
18839
|
-
}
|
|
18840
|
-
get queueLength() {
|
|
18841
|
-
return this.queue.length;
|
|
18842
|
-
}
|
|
18843
|
-
};
|
|
18844
|
-
|
|
18845
18988
|
// src/server/cloud-prompt.ts
|
|
18846
18989
|
function normalizeCloudPromptContent(content) {
|
|
18847
18990
|
if (typeof content === "string") {
|
|
@@ -18987,6 +19130,14 @@ function validateCommandParams(method, params) {
|
|
|
18987
19130
|
}
|
|
18988
19131
|
|
|
18989
19132
|
// src/server/agent-server.ts
|
|
19133
|
+
var agentErrorClassificationSchema = import_zod3.z.enum([
|
|
19134
|
+
"upstream_stream_terminated",
|
|
19135
|
+
"upstream_connection_error",
|
|
19136
|
+
"agent_error"
|
|
19137
|
+
]);
|
|
19138
|
+
var errorWithClassificationSchema = import_zod3.z.object({
|
|
19139
|
+
data: import_zod3.z.object({ classification: agentErrorClassificationSchema })
|
|
19140
|
+
});
|
|
18990
19141
|
var NdJsonTap = class {
|
|
18991
19142
|
constructor(onMessage) {
|
|
18992
19143
|
this.onMessage = onMessage;
|
|
@@ -19689,6 +19840,23 @@ var AgentServer = class _AgentServer {
|
|
|
19689
19840
|
);
|
|
19690
19841
|
await this.sendInitialTaskMessage(payload, preTaskRun);
|
|
19691
19842
|
}
|
|
19843
|
+
extractErrorClassification(error) {
|
|
19844
|
+
const message = error instanceof Error ? error.message : String(error ?? "");
|
|
19845
|
+
const parsed = errorWithClassificationSchema.safeParse(error);
|
|
19846
|
+
if (parsed.success) {
|
|
19847
|
+
return { classification: parsed.data.data.classification, message };
|
|
19848
|
+
}
|
|
19849
|
+
return { classification: classifyAgentError(message), message };
|
|
19850
|
+
}
|
|
19851
|
+
classifyAndSignalFailure(payload, phase, error) {
|
|
19852
|
+
const { classification, message } = this.extractErrorClassification(error);
|
|
19853
|
+
const errorMessage = classification === "upstream_stream_terminated" ? "Upstream LLM stream terminated" : classification === "upstream_connection_error" ? "Upstream LLM connection error" : message || "Agent error";
|
|
19854
|
+
this.logger.error(`send_${phase}_task_message_failed`, {
|
|
19855
|
+
classification,
|
|
19856
|
+
message
|
|
19857
|
+
});
|
|
19858
|
+
return this.signalTaskComplete(payload, "error", errorMessage);
|
|
19859
|
+
}
|
|
19692
19860
|
async sendInitialTaskMessage(payload, prefetchedRun) {
|
|
19693
19861
|
if (!this.session) return;
|
|
19694
19862
|
let taskRun = prefetchedRun ?? null;
|
|
@@ -19781,7 +19949,7 @@ var AgentServer = class _AgentServer {
|
|
|
19781
19949
|
if (this.session) {
|
|
19782
19950
|
await this.session.logWriter.flushAll();
|
|
19783
19951
|
}
|
|
19784
|
-
await this.
|
|
19952
|
+
await this.classifyAndSignalFailure(payload, "initial", error);
|
|
19785
19953
|
}
|
|
19786
19954
|
}
|
|
19787
19955
|
async sendResumeMessage(payload, taskRun) {
|
|
@@ -19855,7 +20023,7 @@ Continue from where you left off. The user is waiting for your response.`
|
|
|
19855
20023
|
if (this.session) {
|
|
19856
20024
|
await this.session.logWriter.flushAll();
|
|
19857
20025
|
}
|
|
19858
|
-
await this.
|
|
20026
|
+
await this.classifyAndSignalFailure(payload, "resume", error);
|
|
19859
20027
|
}
|
|
19860
20028
|
}
|
|
19861
20029
|
static RESUME_HISTORY_TOKEN_BUDGET = 5e4;
|
|
@@ -20213,7 +20381,7 @@ ${attributionInstructions}
|
|
|
20213
20381
|
});
|
|
20214
20382
|
}
|
|
20215
20383
|
}
|
|
20216
|
-
async signalTaskComplete(payload, stopReason) {
|
|
20384
|
+
async signalTaskComplete(payload, stopReason, errorMessage) {
|
|
20217
20385
|
if (this.session?.payload.run_id === payload.run_id) {
|
|
20218
20386
|
try {
|
|
20219
20387
|
await this.session.logWriter.flush(payload.run_id, {
|
|
@@ -20237,7 +20405,7 @@ ${attributionInstructions}
|
|
|
20237
20405
|
try {
|
|
20238
20406
|
await this.posthogAPI.updateTaskRun(payload.task_id, payload.run_id, {
|
|
20239
20407
|
status,
|
|
20240
|
-
error_message:
|
|
20408
|
+
error_message: errorMessage ?? "Agent error"
|
|
20241
20409
|
});
|
|
20242
20410
|
this.logger.info("Task completion signaled", { status, stopReason });
|
|
20243
20411
|
} catch (error) {
|