@posthog/agent 2.3.261 → 2.3.267
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/codex/models.d.ts +7 -0
- package/dist/adapters/codex/models.js +13 -0
- package/dist/adapters/codex/models.js.map +1 -0
- package/dist/adapters/reasoning-effort.d.ts +10 -0
- package/dist/adapters/reasoning-effort.js +51 -0
- package/dist/adapters/reasoning-effort.js.map +1 -0
- package/dist/agent.js +16 -2
- package/dist/agent.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/posthog-api.js +5 -1
- package/dist/posthog-api.js.map +1 -1
- package/dist/server/agent-server.d.ts +17 -0
- package/dist/server/agent-server.js +199 -28
- package/dist/server/agent-server.js.map +1 -1
- package/dist/server/bin.cjs +335 -128
- package/dist/server/bin.cjs.map +1 -1
- package/package.json +7 -3
- package/src/acp-extensions.ts +3 -0
- package/src/adapters/claude/permissions/permission-handlers.ts +11 -5
- package/src/adapters/codex/models.ts +16 -0
- package/src/adapters/codex/spawn.ts +5 -0
- package/src/adapters/reasoning-effort.ts +35 -0
- package/src/server/agent-server.test.ts +70 -11
- package/src/server/agent-server.ts +257 -37
- package/src/server/bin.ts +24 -0
- package/src/server/schemas.test.ts +52 -0
- package/src/server/schemas.ts +16 -0
- package/src/server/types.ts +3 -0
- package/src/test/mocks/msw-handlers.ts +24 -0
package/dist/server/bin.cjs
CHANGED
|
@@ -896,6 +896,125 @@ var require_dist2 = __commonJS({
|
|
|
896
896
|
var import_commander = require("commander");
|
|
897
897
|
var import_v42 = require("zod/v4");
|
|
898
898
|
|
|
899
|
+
// src/adapters/claude/session/models.ts
|
|
900
|
+
var DEFAULT_MODEL = "opus";
|
|
901
|
+
var GATEWAY_TO_SDK_MODEL = {
|
|
902
|
+
"claude-opus-4-5": "opus",
|
|
903
|
+
"claude-opus-4-6": "opus",
|
|
904
|
+
"claude-sonnet-4-5": "sonnet",
|
|
905
|
+
"claude-sonnet-4-6": "sonnet",
|
|
906
|
+
"claude-haiku-4-5": "haiku"
|
|
907
|
+
};
|
|
908
|
+
function toSdkModelId(modelId) {
|
|
909
|
+
return GATEWAY_TO_SDK_MODEL[modelId] ?? modelId;
|
|
910
|
+
}
|
|
911
|
+
var MODELS_WITH_1M_CONTEXT = /* @__PURE__ */ new Set([
|
|
912
|
+
"claude-opus-4-6",
|
|
913
|
+
"claude-sonnet-4-6"
|
|
914
|
+
]);
|
|
915
|
+
function supports1MContext(modelId) {
|
|
916
|
+
return MODELS_WITH_1M_CONTEXT.has(modelId);
|
|
917
|
+
}
|
|
918
|
+
var MODELS_WITH_EFFORT = /* @__PURE__ */ new Set([
|
|
919
|
+
"claude-opus-4-5",
|
|
920
|
+
"claude-opus-4-6",
|
|
921
|
+
"claude-sonnet-4-6"
|
|
922
|
+
]);
|
|
923
|
+
var MODELS_WITH_MAX_EFFORT = /* @__PURE__ */ new Set(["claude-opus-4-6"]);
|
|
924
|
+
function supportsEffort(modelId) {
|
|
925
|
+
return MODELS_WITH_EFFORT.has(modelId);
|
|
926
|
+
}
|
|
927
|
+
function supportsMaxEffort(modelId) {
|
|
928
|
+
return MODELS_WITH_MAX_EFFORT.has(modelId);
|
|
929
|
+
}
|
|
930
|
+
var MODELS_TO_EXCLUDE_MCP_TOOLS = /* @__PURE__ */ new Set(["claude-haiku-4-5"]);
|
|
931
|
+
function supportsMcpInjection(modelId) {
|
|
932
|
+
return !MODELS_TO_EXCLUDE_MCP_TOOLS.has(modelId);
|
|
933
|
+
}
|
|
934
|
+
function getEffortOptions(modelId) {
|
|
935
|
+
if (!supportsEffort(modelId)) return null;
|
|
936
|
+
const options = [
|
|
937
|
+
{ value: "low", name: "Low" },
|
|
938
|
+
{ value: "medium", name: "Medium" },
|
|
939
|
+
{ value: "high", name: "High" }
|
|
940
|
+
];
|
|
941
|
+
if (supportsMaxEffort(modelId)) {
|
|
942
|
+
options.push({ value: "max", name: "Max" });
|
|
943
|
+
}
|
|
944
|
+
return options;
|
|
945
|
+
}
|
|
946
|
+
var MODEL_CONTEXT_HINT_PATTERN = /\[(\d+m)\]$/i;
|
|
947
|
+
function tokenizeModelPreference(model) {
|
|
948
|
+
const lower = model.trim().toLowerCase();
|
|
949
|
+
const contextHint = lower.match(MODEL_CONTEXT_HINT_PATTERN)?.[1]?.toLowerCase();
|
|
950
|
+
const normalized = lower.replace(MODEL_CONTEXT_HINT_PATTERN, " $1 ");
|
|
951
|
+
const rawTokens = normalized.split(/[^a-z0-9]+/).filter(Boolean);
|
|
952
|
+
const tokens = rawTokens.map((token) => {
|
|
953
|
+
if (token === "opusplan") return "opus";
|
|
954
|
+
if (token === "best" || token === "default") return "";
|
|
955
|
+
return token;
|
|
956
|
+
}).filter((token) => token && token !== "claude").filter((token) => /[a-z]/.test(token) || token.endsWith("m"));
|
|
957
|
+
return { tokens, contextHint };
|
|
958
|
+
}
|
|
959
|
+
function scoreModelMatch(model, tokens, contextHint) {
|
|
960
|
+
const haystack = `${model.value} ${model.name ?? ""}`.toLowerCase();
|
|
961
|
+
let score = 0;
|
|
962
|
+
for (const token of tokens) {
|
|
963
|
+
if (haystack.includes(token)) {
|
|
964
|
+
score += token === contextHint ? 3 : 1;
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
return score;
|
|
968
|
+
}
|
|
969
|
+
function resolveModelPreference(preference, options) {
|
|
970
|
+
const trimmed2 = preference.trim();
|
|
971
|
+
if (!trimmed2) return null;
|
|
972
|
+
const lower = trimmed2.toLowerCase();
|
|
973
|
+
const directMatch = options.find(
|
|
974
|
+
(o) => o.value === trimmed2 || o.value.toLowerCase() === lower || o.name && o.name.toLowerCase() === lower
|
|
975
|
+
);
|
|
976
|
+
if (directMatch) return directMatch.value;
|
|
977
|
+
const includesMatch = options.find((o) => {
|
|
978
|
+
const value = o.value.toLowerCase();
|
|
979
|
+
const display = (o.name ?? "").toLowerCase();
|
|
980
|
+
return value.includes(lower) || display.includes(lower) || lower.includes(value);
|
|
981
|
+
});
|
|
982
|
+
if (includesMatch) return includesMatch.value;
|
|
983
|
+
const { tokens, contextHint } = tokenizeModelPreference(trimmed2);
|
|
984
|
+
if (tokens.length === 0) return null;
|
|
985
|
+
let bestMatch = null;
|
|
986
|
+
let bestScore = 0;
|
|
987
|
+
for (const model of options) {
|
|
988
|
+
const score = scoreModelMatch(model, tokens, contextHint);
|
|
989
|
+
if (0 < score && (!bestMatch || bestScore < score)) {
|
|
990
|
+
bestMatch = model;
|
|
991
|
+
bestScore = score;
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
return bestMatch?.value ?? null;
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
// src/adapters/codex/models.ts
|
|
998
|
+
var CODEX_REASONING_EFFORT_OPTIONS = [
|
|
999
|
+
{ value: "low", name: "Low" },
|
|
1000
|
+
{ value: "medium", name: "Medium" },
|
|
1001
|
+
{ value: "high", name: "High" }
|
|
1002
|
+
];
|
|
1003
|
+
function getReasoningEffortOptions(_modelId) {
|
|
1004
|
+
return CODEX_REASONING_EFFORT_OPTIONS;
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
// src/adapters/reasoning-effort.ts
|
|
1008
|
+
function getReasoningEffortOptions2(adapter, modelId) {
|
|
1009
|
+
const options = adapter === "codex" ? getReasoningEffortOptions(modelId) : getEffortOptions(modelId);
|
|
1010
|
+
return options;
|
|
1011
|
+
}
|
|
1012
|
+
function isSupportedReasoningEffort(adapter, modelId, value) {
|
|
1013
|
+
return getReasoningEffortOptions2(adapter, modelId)?.some(
|
|
1014
|
+
(option) => option.value === value
|
|
1015
|
+
) ?? false;
|
|
1016
|
+
}
|
|
1017
|
+
|
|
899
1018
|
// src/server/agent-server.ts
|
|
900
1019
|
var import_sdk5 = require("@agentclientprotocol/sdk");
|
|
901
1020
|
var import_node_server = require("@hono/node-server");
|
|
@@ -5683,7 +5802,7 @@ var import_hono = require("hono");
|
|
|
5683
5802
|
// package.json
|
|
5684
5803
|
var package_default = {
|
|
5685
5804
|
name: "@posthog/agent",
|
|
5686
|
-
version: "2.3.
|
|
5805
|
+
version: "2.3.267",
|
|
5687
5806
|
repository: "https://github.com/PostHog/code",
|
|
5688
5807
|
description: "TypeScript agent framework wrapping Claude Agent SDK with Git-based task execution for PostHog",
|
|
5689
5808
|
exports: {
|
|
@@ -5731,6 +5850,10 @@ var package_default = {
|
|
|
5731
5850
|
types: "./dist/adapters/claude/session/models.d.ts",
|
|
5732
5851
|
import: "./dist/adapters/claude/session/models.js"
|
|
5733
5852
|
},
|
|
5853
|
+
"./adapters/reasoning-effort": {
|
|
5854
|
+
types: "./dist/adapters/reasoning-effort.d.ts",
|
|
5855
|
+
import: "./dist/adapters/reasoning-effort.js"
|
|
5856
|
+
},
|
|
5734
5857
|
"./execution-mode": {
|
|
5735
5858
|
types: "./dist/execution-mode.d.ts",
|
|
5736
5859
|
import: "./dist/execution-mode.js"
|
|
@@ -5844,7 +5967,9 @@ var POSTHOG_NOTIFICATIONS = {
|
|
|
5844
5967
|
/** Marks a boundary for log compaction */
|
|
5845
5968
|
COMPACT_BOUNDARY: "_posthog/compact_boundary",
|
|
5846
5969
|
/** Token usage update for a session turn */
|
|
5847
|
-
USAGE_UPDATE: "_posthog/usage_update"
|
|
5970
|
+
USAGE_UPDATE: "_posthog/usage_update",
|
|
5971
|
+
/** Response to a relayed permission request (plan approval, question) */
|
|
5972
|
+
PERMISSION_RESPONSE: "_posthog/permission_response"
|
|
5848
5973
|
};
|
|
5849
5974
|
function isNotification(method, notification) {
|
|
5850
5975
|
if (!method) return false;
|
|
@@ -6113,6 +6238,7 @@ function unreachable(value, logger) {
|
|
|
6113
6238
|
|
|
6114
6239
|
// src/gateway-models.ts
|
|
6115
6240
|
var DEFAULT_GATEWAY_MODEL = "claude-opus-4-6";
|
|
6241
|
+
var DEFAULT_CODEX_MODEL = "gpt-5.4";
|
|
6116
6242
|
var BLOCKED_MODELS = /* @__PURE__ */ new Set(["gpt-5-mini", "openai/gpt-5-mini"]);
|
|
6117
6243
|
var CACHE_TTL = 10 * 60 * 1e3;
|
|
6118
6244
|
var gatewayModelsCache = null;
|
|
@@ -8281,6 +8407,11 @@ async function canUseTool(context) {
|
|
|
8281
8407
|
if (planFileResult) {
|
|
8282
8408
|
return planFileResult;
|
|
8283
8409
|
}
|
|
8410
|
+
if (session.permissionMode === "plan") {
|
|
8411
|
+
const message = `This tool is not available in plan mode. Write your plan to a file in ${getClaudePlansDir()} and call ExitPlanMode when ready.`;
|
|
8412
|
+
await emitToolDenial(context, message);
|
|
8413
|
+
return { behavior: "deny", message, interrupt: false };
|
|
8414
|
+
}
|
|
8284
8415
|
return handleDefaultPermissionFlow(context);
|
|
8285
8416
|
}
|
|
8286
8417
|
|
|
@@ -8339,104 +8470,6 @@ function parseMcpServers(params) {
|
|
|
8339
8470
|
return mcpServers;
|
|
8340
8471
|
}
|
|
8341
8472
|
|
|
8342
|
-
// src/adapters/claude/session/models.ts
|
|
8343
|
-
var DEFAULT_MODEL = "opus";
|
|
8344
|
-
var GATEWAY_TO_SDK_MODEL = {
|
|
8345
|
-
"claude-opus-4-5": "opus",
|
|
8346
|
-
"claude-opus-4-6": "opus",
|
|
8347
|
-
"claude-sonnet-4-5": "sonnet",
|
|
8348
|
-
"claude-sonnet-4-6": "sonnet",
|
|
8349
|
-
"claude-haiku-4-5": "haiku"
|
|
8350
|
-
};
|
|
8351
|
-
function toSdkModelId(modelId) {
|
|
8352
|
-
return GATEWAY_TO_SDK_MODEL[modelId] ?? modelId;
|
|
8353
|
-
}
|
|
8354
|
-
var MODELS_WITH_1M_CONTEXT = /* @__PURE__ */ new Set([
|
|
8355
|
-
"claude-opus-4-6",
|
|
8356
|
-
"claude-sonnet-4-6"
|
|
8357
|
-
]);
|
|
8358
|
-
function supports1MContext(modelId) {
|
|
8359
|
-
return MODELS_WITH_1M_CONTEXT.has(modelId);
|
|
8360
|
-
}
|
|
8361
|
-
var MODELS_WITH_EFFORT = /* @__PURE__ */ new Set([
|
|
8362
|
-
"claude-opus-4-5",
|
|
8363
|
-
"claude-opus-4-6",
|
|
8364
|
-
"claude-sonnet-4-6"
|
|
8365
|
-
]);
|
|
8366
|
-
var MODELS_WITH_MAX_EFFORT = /* @__PURE__ */ new Set(["claude-opus-4-6"]);
|
|
8367
|
-
function supportsEffort(modelId) {
|
|
8368
|
-
return MODELS_WITH_EFFORT.has(modelId);
|
|
8369
|
-
}
|
|
8370
|
-
function supportsMaxEffort(modelId) {
|
|
8371
|
-
return MODELS_WITH_MAX_EFFORT.has(modelId);
|
|
8372
|
-
}
|
|
8373
|
-
var MODELS_TO_EXCLUDE_MCP_TOOLS = /* @__PURE__ */ new Set(["claude-haiku-4-5"]);
|
|
8374
|
-
function supportsMcpInjection(modelId) {
|
|
8375
|
-
return !MODELS_TO_EXCLUDE_MCP_TOOLS.has(modelId);
|
|
8376
|
-
}
|
|
8377
|
-
function getEffortOptions(modelId) {
|
|
8378
|
-
if (!supportsEffort(modelId)) return null;
|
|
8379
|
-
const options = [
|
|
8380
|
-
{ value: "low", name: "Low" },
|
|
8381
|
-
{ value: "medium", name: "Medium" },
|
|
8382
|
-
{ value: "high", name: "High" }
|
|
8383
|
-
];
|
|
8384
|
-
if (supportsMaxEffort(modelId)) {
|
|
8385
|
-
options.push({ value: "max", name: "Max" });
|
|
8386
|
-
}
|
|
8387
|
-
return options;
|
|
8388
|
-
}
|
|
8389
|
-
var MODEL_CONTEXT_HINT_PATTERN = /\[(\d+m)\]$/i;
|
|
8390
|
-
function tokenizeModelPreference(model) {
|
|
8391
|
-
const lower = model.trim().toLowerCase();
|
|
8392
|
-
const contextHint = lower.match(MODEL_CONTEXT_HINT_PATTERN)?.[1]?.toLowerCase();
|
|
8393
|
-
const normalized = lower.replace(MODEL_CONTEXT_HINT_PATTERN, " $1 ");
|
|
8394
|
-
const rawTokens = normalized.split(/[^a-z0-9]+/).filter(Boolean);
|
|
8395
|
-
const tokens = rawTokens.map((token) => {
|
|
8396
|
-
if (token === "opusplan") return "opus";
|
|
8397
|
-
if (token === "best" || token === "default") return "";
|
|
8398
|
-
return token;
|
|
8399
|
-
}).filter((token) => token && token !== "claude").filter((token) => /[a-z]/.test(token) || token.endsWith("m"));
|
|
8400
|
-
return { tokens, contextHint };
|
|
8401
|
-
}
|
|
8402
|
-
function scoreModelMatch(model, tokens, contextHint) {
|
|
8403
|
-
const haystack = `${model.value} ${model.name ?? ""}`.toLowerCase();
|
|
8404
|
-
let score = 0;
|
|
8405
|
-
for (const token of tokens) {
|
|
8406
|
-
if (haystack.includes(token)) {
|
|
8407
|
-
score += token === contextHint ? 3 : 1;
|
|
8408
|
-
}
|
|
8409
|
-
}
|
|
8410
|
-
return score;
|
|
8411
|
-
}
|
|
8412
|
-
function resolveModelPreference(preference, options) {
|
|
8413
|
-
const trimmed2 = preference.trim();
|
|
8414
|
-
if (!trimmed2) return null;
|
|
8415
|
-
const lower = trimmed2.toLowerCase();
|
|
8416
|
-
const directMatch = options.find(
|
|
8417
|
-
(o) => o.value === trimmed2 || o.value.toLowerCase() === lower || o.name && o.name.toLowerCase() === lower
|
|
8418
|
-
);
|
|
8419
|
-
if (directMatch) return directMatch.value;
|
|
8420
|
-
const includesMatch = options.find((o) => {
|
|
8421
|
-
const value = o.value.toLowerCase();
|
|
8422
|
-
const display = (o.name ?? "").toLowerCase();
|
|
8423
|
-
return value.includes(lower) || display.includes(lower) || lower.includes(value);
|
|
8424
|
-
});
|
|
8425
|
-
if (includesMatch) return includesMatch.value;
|
|
8426
|
-
const { tokens, contextHint } = tokenizeModelPreference(trimmed2);
|
|
8427
|
-
if (tokens.length === 0) return null;
|
|
8428
|
-
let bestMatch = null;
|
|
8429
|
-
let bestScore = 0;
|
|
8430
|
-
for (const model of options) {
|
|
8431
|
-
const score = scoreModelMatch(model, tokens, contextHint);
|
|
8432
|
-
if (0 < score && (!bestMatch || bestScore < score)) {
|
|
8433
|
-
bestMatch = model;
|
|
8434
|
-
bestScore = score;
|
|
8435
|
-
}
|
|
8436
|
-
}
|
|
8437
|
-
return bestMatch?.value ?? null;
|
|
8438
|
-
}
|
|
8439
|
-
|
|
8440
8473
|
// src/adapters/claude/session/options.ts
|
|
8441
8474
|
var import_node_child_process2 = require("child_process");
|
|
8442
8475
|
var fs4 = __toESM(require("fs"), 1);
|
|
@@ -10085,6 +10118,9 @@ function buildConfigArgs(options) {
|
|
|
10085
10118
|
if (options.model) {
|
|
10086
10119
|
args.push("-c", `model="${options.model}"`);
|
|
10087
10120
|
}
|
|
10121
|
+
if (options.reasoningEffort) {
|
|
10122
|
+
args.push("-c", `model_reasoning_effort="${options.reasoningEffort}"`);
|
|
10123
|
+
}
|
|
10088
10124
|
if (options.instructions) {
|
|
10089
10125
|
const escaped = options.instructions.replace(/\\/g, "\\\\").replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/"/g, '\\"');
|
|
10090
10126
|
args.push("-c", `instructions="${escaped}"`);
|
|
@@ -12219,13 +12255,27 @@ var userMessageParamsSchema = import_v4.z.object({
|
|
|
12219
12255
|
import_v4.z.array(import_v4.z.record(import_v4.z.string(), import_v4.z.unknown())).min(1, "Content is required")
|
|
12220
12256
|
])
|
|
12221
12257
|
});
|
|
12258
|
+
var permissionResponseParamsSchema = import_v4.z.object({
|
|
12259
|
+
requestId: import_v4.z.string().min(1, "requestId is required"),
|
|
12260
|
+
optionId: import_v4.z.string().min(1, "optionId is required"),
|
|
12261
|
+
customInput: import_v4.z.string().optional(),
|
|
12262
|
+
answers: import_v4.z.record(import_v4.z.string(), import_v4.z.string()).optional()
|
|
12263
|
+
});
|
|
12264
|
+
var setConfigOptionParamsSchema = import_v4.z.object({
|
|
12265
|
+
configId: import_v4.z.string().min(1, "configId is required"),
|
|
12266
|
+
value: import_v4.z.string().min(1, "value is required")
|
|
12267
|
+
});
|
|
12222
12268
|
var commandParamsSchemas = {
|
|
12223
12269
|
user_message: userMessageParamsSchema,
|
|
12224
12270
|
"posthog/user_message": userMessageParamsSchema,
|
|
12225
12271
|
cancel: import_v4.z.object({}).optional(),
|
|
12226
12272
|
"posthog/cancel": import_v4.z.object({}).optional(),
|
|
12227
12273
|
close: import_v4.z.object({}).optional(),
|
|
12228
|
-
"posthog/close": import_v4.z.object({}).optional()
|
|
12274
|
+
"posthog/close": import_v4.z.object({}).optional(),
|
|
12275
|
+
permission_response: permissionResponseParamsSchema,
|
|
12276
|
+
"posthog/permission_response": permissionResponseParamsSchema,
|
|
12277
|
+
set_config_option: setConfigOptionParamsSchema,
|
|
12278
|
+
"posthog/set_config_option": setConfigOptionParamsSchema
|
|
12229
12279
|
};
|
|
12230
12280
|
function validateCommandParams(method, params) {
|
|
12231
12281
|
const schema = commandParamsSchemas[method] ?? commandParamsSchemas[method.replace("posthog/", "")];
|
|
@@ -12325,6 +12375,14 @@ function createTappedWritableStream2(underlying, onMessage, logger) {
|
|
|
12325
12375
|
}
|
|
12326
12376
|
});
|
|
12327
12377
|
}
|
|
12378
|
+
function getTaskRunStateString(taskRun, key) {
|
|
12379
|
+
const state = taskRun?.state;
|
|
12380
|
+
if (!state || typeof state !== "object") {
|
|
12381
|
+
return null;
|
|
12382
|
+
}
|
|
12383
|
+
const value = state[key];
|
|
12384
|
+
return typeof value === "string" ? value : null;
|
|
12385
|
+
}
|
|
12328
12386
|
var AgentServer = class _AgentServer {
|
|
12329
12387
|
config;
|
|
12330
12388
|
logger;
|
|
@@ -12342,6 +12400,7 @@ var AgentServer = class _AgentServer {
|
|
|
12342
12400
|
// causing a second session to be created and duplicate Slack messages to be sent.
|
|
12343
12401
|
initializationPromise = null;
|
|
12344
12402
|
pendingEvents = [];
|
|
12403
|
+
pendingPermissions = /* @__PURE__ */ new Map();
|
|
12345
12404
|
detachSseController(controller) {
|
|
12346
12405
|
if (this.session?.sseController === controller) {
|
|
12347
12406
|
this.session.sseController = null;
|
|
@@ -12376,9 +12435,15 @@ var AgentServer = class _AgentServer {
|
|
|
12376
12435
|
});
|
|
12377
12436
|
this.app = this.createApp();
|
|
12378
12437
|
}
|
|
12438
|
+
getRuntimeAdapter() {
|
|
12439
|
+
return this.config.runtimeAdapter ?? "claude";
|
|
12440
|
+
}
|
|
12379
12441
|
getEffectiveMode(payload) {
|
|
12380
12442
|
return payload.mode ?? this.config.mode;
|
|
12381
12443
|
}
|
|
12444
|
+
getSessionPermissionMode() {
|
|
12445
|
+
return this.session?.permissionMode ?? "default";
|
|
12446
|
+
}
|
|
12382
12447
|
createApp() {
|
|
12383
12448
|
const app = new import_hono.Hono();
|
|
12384
12449
|
app.get("/health", (c) => {
|
|
@@ -12423,6 +12488,7 @@ var AgentServer = class _AgentServer {
|
|
|
12423
12488
|
await this.initializeSession(payload, sseController);
|
|
12424
12489
|
} else {
|
|
12425
12490
|
this.session.sseController = sseController;
|
|
12491
|
+
this.session.hasDesktopConnected = true;
|
|
12426
12492
|
this.replayPendingEvents();
|
|
12427
12493
|
}
|
|
12428
12494
|
this.sendSseEvent(sseController, {
|
|
@@ -12662,6 +12728,43 @@ var AgentServer = class _AgentServer {
|
|
|
12662
12728
|
await this.cleanupSession();
|
|
12663
12729
|
return { closed: true };
|
|
12664
12730
|
}
|
|
12731
|
+
case "posthog/set_config_option":
|
|
12732
|
+
case "set_config_option": {
|
|
12733
|
+
const configId = params.configId;
|
|
12734
|
+
const value = params.value;
|
|
12735
|
+
this.logger.info("Set config option requested", { configId, value });
|
|
12736
|
+
const result = await this.session.clientConnection.setSessionConfigOption({
|
|
12737
|
+
sessionId: this.session.acpSessionId,
|
|
12738
|
+
configId,
|
|
12739
|
+
value
|
|
12740
|
+
});
|
|
12741
|
+
return {
|
|
12742
|
+
configOptions: result.configOptions
|
|
12743
|
+
};
|
|
12744
|
+
}
|
|
12745
|
+
case POSTHOG_NOTIFICATIONS.PERMISSION_RESPONSE:
|
|
12746
|
+
case "permission_response": {
|
|
12747
|
+
const requestId = params.requestId;
|
|
12748
|
+
const optionId = params.optionId;
|
|
12749
|
+
const customInput = params.customInput;
|
|
12750
|
+
const answers = params.answers;
|
|
12751
|
+
this.logger.info("Permission response received", {
|
|
12752
|
+
requestId,
|
|
12753
|
+
optionId
|
|
12754
|
+
});
|
|
12755
|
+
const resolved = this.resolvePermission(
|
|
12756
|
+
requestId,
|
|
12757
|
+
optionId,
|
|
12758
|
+
customInput,
|
|
12759
|
+
answers
|
|
12760
|
+
);
|
|
12761
|
+
if (!resolved) {
|
|
12762
|
+
throw new Error(
|
|
12763
|
+
`No pending permission request found for id: ${requestId}`
|
|
12764
|
+
);
|
|
12765
|
+
}
|
|
12766
|
+
return { resolved: true };
|
|
12767
|
+
}
|
|
12665
12768
|
default:
|
|
12666
12769
|
throw new Error(`Unknown method: ${method}`);
|
|
12667
12770
|
}
|
|
@@ -12701,6 +12804,30 @@ var AgentServer = class _AgentServer {
|
|
|
12701
12804
|
name: process.env.HOSTNAME || "cloud-sandbox"
|
|
12702
12805
|
};
|
|
12703
12806
|
this.configureEnvironment();
|
|
12807
|
+
const [preTaskRun, preTask] = await Promise.all([
|
|
12808
|
+
this.posthogAPI.getTaskRun(payload.task_id, payload.run_id).catch((err) => {
|
|
12809
|
+
this.logger.warn("Failed to fetch task run for session context", {
|
|
12810
|
+
taskId: payload.task_id,
|
|
12811
|
+
runId: payload.run_id,
|
|
12812
|
+
error: err
|
|
12813
|
+
});
|
|
12814
|
+
return null;
|
|
12815
|
+
}),
|
|
12816
|
+
this.posthogAPI.getTask(payload.task_id).catch((err) => {
|
|
12817
|
+
this.logger.warn("Failed to fetch task for session context", {
|
|
12818
|
+
taskId: payload.task_id,
|
|
12819
|
+
error: err
|
|
12820
|
+
});
|
|
12821
|
+
return null;
|
|
12822
|
+
})
|
|
12823
|
+
]);
|
|
12824
|
+
const prUrl = getTaskRunStateString(preTaskRun, "slack_notified_pr_url");
|
|
12825
|
+
if (prUrl) {
|
|
12826
|
+
this.detectedPrUrl = prUrl;
|
|
12827
|
+
}
|
|
12828
|
+
const runtimeAdapter = this.getRuntimeAdapter();
|
|
12829
|
+
const sessionSystemPrompt = this.buildSessionSystemPrompt(prUrl);
|
|
12830
|
+
const codexInstructions = runtimeAdapter === "codex" ? this.buildCodexInstructions(sessionSystemPrompt) : void 0;
|
|
12704
12831
|
const posthogAPI = new PostHogAPIClient({
|
|
12705
12832
|
apiUrl: this.config.apiUrl,
|
|
12706
12833
|
projectId: this.config.projectId,
|
|
@@ -12719,10 +12846,20 @@ var AgentServer = class _AgentServer {
|
|
|
12719
12846
|
logger: new Logger({ debug: true, prefix: "[SessionLogWriter]" })
|
|
12720
12847
|
});
|
|
12721
12848
|
const acpConnection = createAcpConnection({
|
|
12849
|
+
adapter: runtimeAdapter,
|
|
12722
12850
|
taskRunId: payload.run_id,
|
|
12723
12851
|
taskId: payload.task_id,
|
|
12724
12852
|
deviceType: deviceInfo.type,
|
|
12725
12853
|
logWriter,
|
|
12854
|
+
logger: this.logger,
|
|
12855
|
+
codexOptions: runtimeAdapter === "codex" ? {
|
|
12856
|
+
cwd: this.config.repositoryPath ?? "/tmp/workspace",
|
|
12857
|
+
apiBaseUrl: process.env.OPENAI_BASE_URL,
|
|
12858
|
+
apiKey: this.config.apiKey,
|
|
12859
|
+
model: this.config.model ?? DEFAULT_CODEX_MODEL,
|
|
12860
|
+
reasoningEffort: this.config.reasoningEffort,
|
|
12861
|
+
instructions: codexInstructions
|
|
12862
|
+
} : void 0,
|
|
12726
12863
|
onStructuredOutput: async (output) => {
|
|
12727
12864
|
await this.posthogAPI.setTaskRunOutput(
|
|
12728
12865
|
payload.task_id,
|
|
@@ -12759,40 +12896,28 @@ var AgentServer = class _AgentServer {
|
|
|
12759
12896
|
protocolVersion: import_sdk5.PROTOCOL_VERSION,
|
|
12760
12897
|
clientCapabilities: {}
|
|
12761
12898
|
});
|
|
12762
|
-
const
|
|
12763
|
-
|
|
12764
|
-
this.logger.warn("Failed to fetch task run for session context", {
|
|
12765
|
-
taskId: payload.task_id,
|
|
12766
|
-
runId: payload.run_id,
|
|
12767
|
-
error: err
|
|
12768
|
-
});
|
|
12769
|
-
return null;
|
|
12770
|
-
}),
|
|
12771
|
-
this.posthogAPI.getTask(payload.task_id).catch((err) => {
|
|
12772
|
-
this.logger.warn("Failed to fetch task for session context", {
|
|
12773
|
-
taskId: payload.task_id,
|
|
12774
|
-
error: err
|
|
12775
|
-
});
|
|
12776
|
-
return null;
|
|
12777
|
-
})
|
|
12778
|
-
]);
|
|
12779
|
-
const prUrl = typeof preTaskRun?.state?.slack_notified_pr_url === "string" ? (preTaskRun?.state).slack_notified_pr_url : null;
|
|
12780
|
-
if (prUrl) {
|
|
12781
|
-
this.detectedPrUrl = prUrl;
|
|
12782
|
-
}
|
|
12899
|
+
const runState = preTaskRun?.state;
|
|
12900
|
+
const initialPermissionMode = typeof runState?.initial_permission_mode === "string" ? runState.initial_permission_mode : "bypassPermissions";
|
|
12783
12901
|
const sessionResponse = await clientConnection.newSession({
|
|
12784
12902
|
cwd: this.config.repositoryPath ?? "/tmp/workspace",
|
|
12785
12903
|
mcpServers: this.config.mcpServers ?? [],
|
|
12786
12904
|
_meta: {
|
|
12787
12905
|
sessionId: payload.run_id,
|
|
12788
12906
|
taskRunId: payload.run_id,
|
|
12789
|
-
systemPrompt:
|
|
12907
|
+
systemPrompt: sessionSystemPrompt,
|
|
12908
|
+
...this.config.model && { model: this.config.model },
|
|
12790
12909
|
allowedDomains: this.config.allowedDomains,
|
|
12791
12910
|
jsonSchema: preTask?.json_schema ?? null,
|
|
12911
|
+
permissionMode: initialPermissionMode,
|
|
12792
12912
|
...this.config.claudeCode?.plugins?.length && {
|
|
12793
12913
|
claudeCode: {
|
|
12794
12914
|
options: {
|
|
12795
|
-
|
|
12915
|
+
...this.config.claudeCode?.plugins?.length && {
|
|
12916
|
+
plugins: this.config.claudeCode.plugins
|
|
12917
|
+
},
|
|
12918
|
+
...runtimeAdapter === "claude" && this.config.reasoningEffort && {
|
|
12919
|
+
effort: this.config.reasoningEffort
|
|
12920
|
+
}
|
|
12796
12921
|
}
|
|
12797
12922
|
}
|
|
12798
12923
|
}
|
|
@@ -12811,7 +12936,9 @@ var AgentServer = class _AgentServer {
|
|
|
12811
12936
|
treeTracker,
|
|
12812
12937
|
sseController,
|
|
12813
12938
|
deviceInfo,
|
|
12814
|
-
logWriter
|
|
12939
|
+
logWriter,
|
|
12940
|
+
permissionMode: initialPermissionMode,
|
|
12941
|
+
hasDesktopConnected: sseController !== null
|
|
12815
12942
|
};
|
|
12816
12943
|
this.logger = new Logger({
|
|
12817
12944
|
debug: true,
|
|
@@ -12825,6 +12952,7 @@ var AgentServer = class _AgentServer {
|
|
|
12825
12952
|
this.logger.info(
|
|
12826
12953
|
`Agent version: ${this.config.version ?? package_default.version}`
|
|
12827
12954
|
);
|
|
12955
|
+
this.logger.info(`Initial permission mode: ${initialPermissionMode}`);
|
|
12828
12956
|
this.posthogAPI.updateTaskRun(payload.task_id, payload.run_id, {
|
|
12829
12957
|
status: "in_progress"
|
|
12830
12958
|
}).catch(
|
|
@@ -13074,6 +13202,9 @@ ${toolSummary}`);
|
|
|
13074
13202
|
}
|
|
13075
13203
|
return { append: cloudAppend };
|
|
13076
13204
|
}
|
|
13205
|
+
buildCodexInstructions(systemPrompt) {
|
|
13206
|
+
return typeof systemPrompt === "string" ? systemPrompt : systemPrompt.append;
|
|
13207
|
+
}
|
|
13077
13208
|
getCloudInteractionOrigin() {
|
|
13078
13209
|
return process.env.POSTHOG_CODE_INTERACTION_ORIGIN ?? process.env.CODE_INTERACTION_ORIGIN ?? process.env.TWIG_INTERACTION_ORIGIN;
|
|
13079
13210
|
}
|
|
@@ -13315,14 +13446,16 @@ ${attributionInstructions}
|
|
|
13315
13446
|
this.logger.debug("Permission request", {
|
|
13316
13447
|
mode,
|
|
13317
13448
|
interactionOrigin,
|
|
13449
|
+
kind: params.toolCall?.kind,
|
|
13318
13450
|
options: params.options
|
|
13319
13451
|
});
|
|
13320
13452
|
const allowOption = params.options.find(
|
|
13321
13453
|
(o) => o.kind === "allow_once" || o.kind === "allow_always"
|
|
13322
13454
|
);
|
|
13323
13455
|
const selectedOptionId = allowOption?.optionId ?? params.options[0].optionId;
|
|
13456
|
+
const codeToolKind = params.toolCall?._meta?.codeToolKind;
|
|
13457
|
+
const isPlanApproval = params.toolCall?.kind === "switch_mode";
|
|
13324
13458
|
if (interactionOrigin === "slack") {
|
|
13325
|
-
const codeToolKind = params.toolCall?._meta?.codeToolKind;
|
|
13326
13459
|
if (codeToolKind === "question") {
|
|
13327
13460
|
return this.buildSlackQuestionRelayResponse(
|
|
13328
13461
|
payload,
|
|
@@ -13330,6 +13463,19 @@ ${attributionInstructions}
|
|
|
13330
13463
|
);
|
|
13331
13464
|
}
|
|
13332
13465
|
}
|
|
13466
|
+
{
|
|
13467
|
+
const isQuestion = codeToolKind === "question";
|
|
13468
|
+
const sessionPermissionMode = this.getSessionPermissionMode();
|
|
13469
|
+
const needsRelay = isQuestion || isPlanApproval || sessionPermissionMode === "default";
|
|
13470
|
+
if (needsRelay && this.session?.hasDesktopConnected) {
|
|
13471
|
+
this.logger.info("Relaying permission to connected client", {
|
|
13472
|
+
kind: params.toolCall?.kind,
|
|
13473
|
+
isQuestion,
|
|
13474
|
+
sessionPermissionMode
|
|
13475
|
+
});
|
|
13476
|
+
return this.relayPermissionToClient(params);
|
|
13477
|
+
}
|
|
13478
|
+
}
|
|
13333
13479
|
if (this.shouldBlockPublishPermission(params)) {
|
|
13334
13480
|
return {
|
|
13335
13481
|
outcome: { outcome: "cancelled" },
|
|
@@ -13349,6 +13495,12 @@ ${attributionInstructions}
|
|
|
13349
13495
|
this.logger.debug("Extension notification", { method, params });
|
|
13350
13496
|
},
|
|
13351
13497
|
sessionUpdate: async (params) => {
|
|
13498
|
+
if (params.update?.sessionUpdate === "current_mode_update" && typeof params.update?.currentModeId === "string" && this.session) {
|
|
13499
|
+
this.session.permissionMode = params.update.currentModeId;
|
|
13500
|
+
this.logger.info("Permission mode updated", {
|
|
13501
|
+
mode: params.update.currentModeId
|
|
13502
|
+
});
|
|
13503
|
+
}
|
|
13352
13504
|
if (params.update?.sessionUpdate === "tool_call_update") {
|
|
13353
13505
|
const meta = params.update?._meta?.claudeCode;
|
|
13354
13506
|
const toolName = meta?.toolName;
|
|
@@ -13527,6 +13679,13 @@ ${attributionInstructions}
|
|
|
13527
13679
|
} catch (error) {
|
|
13528
13680
|
this.logger.error("Failed to flush session logs", error);
|
|
13529
13681
|
}
|
|
13682
|
+
for (const [, pending] of this.pendingPermissions) {
|
|
13683
|
+
pending.resolve({
|
|
13684
|
+
outcome: { outcome: "selected", optionId: "reject" },
|
|
13685
|
+
_meta: { customInput: "Session is shutting down." }
|
|
13686
|
+
});
|
|
13687
|
+
}
|
|
13688
|
+
this.pendingPermissions.clear();
|
|
13530
13689
|
try {
|
|
13531
13690
|
await this.session.acpConnection.cleanup();
|
|
13532
13691
|
} catch (error) {
|
|
@@ -13604,6 +13763,39 @@ ${attributionInstructions}
|
|
|
13604
13763
|
this.detachSseController(controller);
|
|
13605
13764
|
}
|
|
13606
13765
|
}
|
|
13766
|
+
/**
|
|
13767
|
+
* Relay a permission request (e.g., plan approval) to the connected desktop
|
|
13768
|
+
* app via SSE and wait for a response via the `/command` endpoint.
|
|
13769
|
+
*
|
|
13770
|
+
* The promise waits indefinitely — if SSE is disconnected, the event is
|
|
13771
|
+
* buffered by broadcastEvent and replayed when the client reconnects. Session
|
|
13772
|
+
* cleanup force-resolves all pending permissions, so there is no leak.
|
|
13773
|
+
*/
|
|
13774
|
+
relayPermissionToClient(params) {
|
|
13775
|
+
const requestId = crypto.randomUUID();
|
|
13776
|
+
this.broadcastEvent({
|
|
13777
|
+
type: "permission_request",
|
|
13778
|
+
requestId,
|
|
13779
|
+
options: params.options,
|
|
13780
|
+
toolCall: params.toolCall
|
|
13781
|
+
});
|
|
13782
|
+
return new Promise((resolve4) => {
|
|
13783
|
+
this.pendingPermissions.set(requestId, { resolve: resolve4 });
|
|
13784
|
+
});
|
|
13785
|
+
}
|
|
13786
|
+
resolvePermission(requestId, optionId, customInput, answers) {
|
|
13787
|
+
const pending = this.pendingPermissions.get(requestId);
|
|
13788
|
+
if (!pending) return false;
|
|
13789
|
+
this.pendingPermissions.delete(requestId);
|
|
13790
|
+
const meta = {};
|
|
13791
|
+
if (customInput) meta.customInput = customInput;
|
|
13792
|
+
if (answers) meta.answers = answers;
|
|
13793
|
+
pending.resolve({
|
|
13794
|
+
outcome: { outcome: "selected", optionId },
|
|
13795
|
+
...Object.keys(meta).length > 0 ? { _meta: meta } : {}
|
|
13796
|
+
});
|
|
13797
|
+
return true;
|
|
13798
|
+
}
|
|
13607
13799
|
};
|
|
13608
13800
|
|
|
13609
13801
|
// src/server/bin.ts
|
|
@@ -13619,7 +13811,10 @@ var envSchema = import_v42.z.object({
|
|
|
13619
13811
|
}).min(1, "POSTHOG_PERSONAL_API_KEY cannot be empty"),
|
|
13620
13812
|
POSTHOG_PROJECT_ID: import_v42.z.string({
|
|
13621
13813
|
error: "POSTHOG_PROJECT_ID is required for routing requests to the correct project"
|
|
13622
|
-
}).regex(/^\d+$/, "POSTHOG_PROJECT_ID must be a numeric string").transform((val) => parseInt(val, 10))
|
|
13814
|
+
}).regex(/^\d+$/, "POSTHOG_PROJECT_ID must be a numeric string").transform((val) => parseInt(val, 10)),
|
|
13815
|
+
POSTHOG_CODE_RUNTIME_ADAPTER: import_v42.z.enum(["claude", "codex"]).optional(),
|
|
13816
|
+
POSTHOG_CODE_MODEL: import_v42.z.string().optional(),
|
|
13817
|
+
POSTHOG_CODE_REASONING_EFFORT: import_v42.z.enum(["low", "medium", "high", "max"]).optional()
|
|
13623
13818
|
});
|
|
13624
13819
|
var program = new import_commander.Command();
|
|
13625
13820
|
function parseBooleanOption(raw, flag) {
|
|
@@ -13679,6 +13874,15 @@ ${errors}`);
|
|
|
13679
13874
|
"--claudeCodeConfig"
|
|
13680
13875
|
);
|
|
13681
13876
|
const allowedDomains = options.allowedDomains ? options.allowedDomains.split(",").map((d) => d.trim()).filter(Boolean) : void 0;
|
|
13877
|
+
if (env.POSTHOG_CODE_RUNTIME_ADAPTER && env.POSTHOG_CODE_MODEL && env.POSTHOG_CODE_REASONING_EFFORT && !isSupportedReasoningEffort(
|
|
13878
|
+
env.POSTHOG_CODE_RUNTIME_ADAPTER,
|
|
13879
|
+
env.POSTHOG_CODE_MODEL,
|
|
13880
|
+
env.POSTHOG_CODE_REASONING_EFFORT
|
|
13881
|
+
)) {
|
|
13882
|
+
program.error(
|
|
13883
|
+
`POSTHOG_CODE_REASONING_EFFORT '${env.POSTHOG_CODE_REASONING_EFFORT}' is not supported for ${env.POSTHOG_CODE_RUNTIME_ADAPTER} model '${env.POSTHOG_CODE_MODEL}'.`
|
|
13884
|
+
);
|
|
13885
|
+
}
|
|
13682
13886
|
const server = new AgentServer({
|
|
13683
13887
|
port: parseInt(options.port, 10),
|
|
13684
13888
|
jwtPublicKey: env.JWT_PUBLIC_KEY,
|
|
@@ -13693,7 +13897,10 @@ ${errors}`);
|
|
|
13693
13897
|
mcpServers,
|
|
13694
13898
|
baseBranch: options.baseBranch,
|
|
13695
13899
|
claudeCode,
|
|
13696
|
-
allowedDomains
|
|
13900
|
+
allowedDomains,
|
|
13901
|
+
runtimeAdapter: env.POSTHOG_CODE_RUNTIME_ADAPTER,
|
|
13902
|
+
model: env.POSTHOG_CODE_MODEL,
|
|
13903
|
+
reasoningEffort: env.POSTHOG_CODE_REASONING_EFFORT
|
|
13697
13904
|
});
|
|
13698
13905
|
process.on("SIGINT", async () => {
|
|
13699
13906
|
await server.stop();
|