@posthog/agent 2.3.261 → 2.3.263
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/agent.js +9 -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 +1 -1
- package/dist/posthog-api.js.map +1 -1
- package/dist/server/agent-server.d.ts +12 -0
- package/dist/server/agent-server.js +135 -5
- package/dist/server/agent-server.js.map +1 -1
- package/dist/server/bin.cjs +135 -5
- package/dist/server/bin.cjs.map +1 -1
- package/package.json +3 -3
- package/src/acp-extensions.ts +3 -0
- package/src/adapters/claude/permissions/permission-handlers.ts +11 -5
- package/src/server/agent-server.ts +176 -4
- package/src/server/schemas.test.ts +52 -0
- package/src/server/schemas.ts +16 -0
package/dist/server/bin.cjs
CHANGED
|
@@ -5683,7 +5683,7 @@ var import_hono = require("hono");
|
|
|
5683
5683
|
// package.json
|
|
5684
5684
|
var package_default = {
|
|
5685
5685
|
name: "@posthog/agent",
|
|
5686
|
-
version: "2.3.
|
|
5686
|
+
version: "2.3.263",
|
|
5687
5687
|
repository: "https://github.com/PostHog/code",
|
|
5688
5688
|
description: "TypeScript agent framework wrapping Claude Agent SDK with Git-based task execution for PostHog",
|
|
5689
5689
|
exports: {
|
|
@@ -5844,7 +5844,9 @@ var POSTHOG_NOTIFICATIONS = {
|
|
|
5844
5844
|
/** Marks a boundary for log compaction */
|
|
5845
5845
|
COMPACT_BOUNDARY: "_posthog/compact_boundary",
|
|
5846
5846
|
/** Token usage update for a session turn */
|
|
5847
|
-
USAGE_UPDATE: "_posthog/usage_update"
|
|
5847
|
+
USAGE_UPDATE: "_posthog/usage_update",
|
|
5848
|
+
/** Response to a relayed permission request (plan approval, question) */
|
|
5849
|
+
PERMISSION_RESPONSE: "_posthog/permission_response"
|
|
5848
5850
|
};
|
|
5849
5851
|
function isNotification(method, notification) {
|
|
5850
5852
|
if (!method) return false;
|
|
@@ -8281,6 +8283,11 @@ async function canUseTool(context) {
|
|
|
8281
8283
|
if (planFileResult) {
|
|
8282
8284
|
return planFileResult;
|
|
8283
8285
|
}
|
|
8286
|
+
if (session.permissionMode === "plan") {
|
|
8287
|
+
const message = `This tool is not available in plan mode. Write your plan to a file in ${getClaudePlansDir()} and call ExitPlanMode when ready.`;
|
|
8288
|
+
await emitToolDenial(context, message);
|
|
8289
|
+
return { behavior: "deny", message, interrupt: false };
|
|
8290
|
+
}
|
|
8284
8291
|
return handleDefaultPermissionFlow(context);
|
|
8285
8292
|
}
|
|
8286
8293
|
|
|
@@ -12219,13 +12226,27 @@ var userMessageParamsSchema = import_v4.z.object({
|
|
|
12219
12226
|
import_v4.z.array(import_v4.z.record(import_v4.z.string(), import_v4.z.unknown())).min(1, "Content is required")
|
|
12220
12227
|
])
|
|
12221
12228
|
});
|
|
12229
|
+
var permissionResponseParamsSchema = import_v4.z.object({
|
|
12230
|
+
requestId: import_v4.z.string().min(1, "requestId is required"),
|
|
12231
|
+
optionId: import_v4.z.string().min(1, "optionId is required"),
|
|
12232
|
+
customInput: import_v4.z.string().optional(),
|
|
12233
|
+
answers: import_v4.z.record(import_v4.z.string(), import_v4.z.string()).optional()
|
|
12234
|
+
});
|
|
12235
|
+
var setConfigOptionParamsSchema = import_v4.z.object({
|
|
12236
|
+
configId: import_v4.z.string().min(1, "configId is required"),
|
|
12237
|
+
value: import_v4.z.string().min(1, "value is required")
|
|
12238
|
+
});
|
|
12222
12239
|
var commandParamsSchemas = {
|
|
12223
12240
|
user_message: userMessageParamsSchema,
|
|
12224
12241
|
"posthog/user_message": userMessageParamsSchema,
|
|
12225
12242
|
cancel: import_v4.z.object({}).optional(),
|
|
12226
12243
|
"posthog/cancel": import_v4.z.object({}).optional(),
|
|
12227
12244
|
close: import_v4.z.object({}).optional(),
|
|
12228
|
-
"posthog/close": import_v4.z.object({}).optional()
|
|
12245
|
+
"posthog/close": import_v4.z.object({}).optional(),
|
|
12246
|
+
permission_response: permissionResponseParamsSchema,
|
|
12247
|
+
"posthog/permission_response": permissionResponseParamsSchema,
|
|
12248
|
+
set_config_option: setConfigOptionParamsSchema,
|
|
12249
|
+
"posthog/set_config_option": setConfigOptionParamsSchema
|
|
12229
12250
|
};
|
|
12230
12251
|
function validateCommandParams(method, params) {
|
|
12231
12252
|
const schema = commandParamsSchemas[method] ?? commandParamsSchemas[method.replace("posthog/", "")];
|
|
@@ -12342,6 +12363,7 @@ var AgentServer = class _AgentServer {
|
|
|
12342
12363
|
// causing a second session to be created and duplicate Slack messages to be sent.
|
|
12343
12364
|
initializationPromise = null;
|
|
12344
12365
|
pendingEvents = [];
|
|
12366
|
+
pendingPermissions = /* @__PURE__ */ new Map();
|
|
12345
12367
|
detachSseController(controller) {
|
|
12346
12368
|
if (this.session?.sseController === controller) {
|
|
12347
12369
|
this.session.sseController = null;
|
|
@@ -12379,6 +12401,9 @@ var AgentServer = class _AgentServer {
|
|
|
12379
12401
|
getEffectiveMode(payload) {
|
|
12380
12402
|
return payload.mode ?? this.config.mode;
|
|
12381
12403
|
}
|
|
12404
|
+
getSessionPermissionMode() {
|
|
12405
|
+
return this.session?.permissionMode ?? "default";
|
|
12406
|
+
}
|
|
12382
12407
|
createApp() {
|
|
12383
12408
|
const app = new import_hono.Hono();
|
|
12384
12409
|
app.get("/health", (c) => {
|
|
@@ -12423,6 +12448,7 @@ var AgentServer = class _AgentServer {
|
|
|
12423
12448
|
await this.initializeSession(payload, sseController);
|
|
12424
12449
|
} else {
|
|
12425
12450
|
this.session.sseController = sseController;
|
|
12451
|
+
this.session.hasDesktopConnected = true;
|
|
12426
12452
|
this.replayPendingEvents();
|
|
12427
12453
|
}
|
|
12428
12454
|
this.sendSseEvent(sseController, {
|
|
@@ -12662,6 +12688,43 @@ var AgentServer = class _AgentServer {
|
|
|
12662
12688
|
await this.cleanupSession();
|
|
12663
12689
|
return { closed: true };
|
|
12664
12690
|
}
|
|
12691
|
+
case "posthog/set_config_option":
|
|
12692
|
+
case "set_config_option": {
|
|
12693
|
+
const configId = params.configId;
|
|
12694
|
+
const value = params.value;
|
|
12695
|
+
this.logger.info("Set config option requested", { configId, value });
|
|
12696
|
+
const result = await this.session.clientConnection.setSessionConfigOption({
|
|
12697
|
+
sessionId: this.session.acpSessionId,
|
|
12698
|
+
configId,
|
|
12699
|
+
value
|
|
12700
|
+
});
|
|
12701
|
+
return {
|
|
12702
|
+
configOptions: result.configOptions
|
|
12703
|
+
};
|
|
12704
|
+
}
|
|
12705
|
+
case POSTHOG_NOTIFICATIONS.PERMISSION_RESPONSE:
|
|
12706
|
+
case "permission_response": {
|
|
12707
|
+
const requestId = params.requestId;
|
|
12708
|
+
const optionId = params.optionId;
|
|
12709
|
+
const customInput = params.customInput;
|
|
12710
|
+
const answers = params.answers;
|
|
12711
|
+
this.logger.info("Permission response received", {
|
|
12712
|
+
requestId,
|
|
12713
|
+
optionId
|
|
12714
|
+
});
|
|
12715
|
+
const resolved = this.resolvePermission(
|
|
12716
|
+
requestId,
|
|
12717
|
+
optionId,
|
|
12718
|
+
customInput,
|
|
12719
|
+
answers
|
|
12720
|
+
);
|
|
12721
|
+
if (!resolved) {
|
|
12722
|
+
throw new Error(
|
|
12723
|
+
`No pending permission request found for id: ${requestId}`
|
|
12724
|
+
);
|
|
12725
|
+
}
|
|
12726
|
+
return { resolved: true };
|
|
12727
|
+
}
|
|
12665
12728
|
default:
|
|
12666
12729
|
throw new Error(`Unknown method: ${method}`);
|
|
12667
12730
|
}
|
|
@@ -12780,6 +12843,8 @@ var AgentServer = class _AgentServer {
|
|
|
12780
12843
|
if (prUrl) {
|
|
12781
12844
|
this.detectedPrUrl = prUrl;
|
|
12782
12845
|
}
|
|
12846
|
+
const runState = preTaskRun?.state;
|
|
12847
|
+
const initialPermissionMode = typeof runState?.initial_permission_mode === "string" ? runState.initial_permission_mode : "bypassPermissions";
|
|
12783
12848
|
const sessionResponse = await clientConnection.newSession({
|
|
12784
12849
|
cwd: this.config.repositoryPath ?? "/tmp/workspace",
|
|
12785
12850
|
mcpServers: this.config.mcpServers ?? [],
|
|
@@ -12789,6 +12854,7 @@ var AgentServer = class _AgentServer {
|
|
|
12789
12854
|
systemPrompt: this.buildSessionSystemPrompt(prUrl),
|
|
12790
12855
|
allowedDomains: this.config.allowedDomains,
|
|
12791
12856
|
jsonSchema: preTask?.json_schema ?? null,
|
|
12857
|
+
permissionMode: initialPermissionMode,
|
|
12792
12858
|
...this.config.claudeCode?.plugins?.length && {
|
|
12793
12859
|
claudeCode: {
|
|
12794
12860
|
options: {
|
|
@@ -12811,7 +12877,9 @@ var AgentServer = class _AgentServer {
|
|
|
12811
12877
|
treeTracker,
|
|
12812
12878
|
sseController,
|
|
12813
12879
|
deviceInfo,
|
|
12814
|
-
logWriter
|
|
12880
|
+
logWriter,
|
|
12881
|
+
permissionMode: initialPermissionMode,
|
|
12882
|
+
hasDesktopConnected: sseController !== null
|
|
12815
12883
|
};
|
|
12816
12884
|
this.logger = new Logger({
|
|
12817
12885
|
debug: true,
|
|
@@ -12825,6 +12893,7 @@ var AgentServer = class _AgentServer {
|
|
|
12825
12893
|
this.logger.info(
|
|
12826
12894
|
`Agent version: ${this.config.version ?? package_default.version}`
|
|
12827
12895
|
);
|
|
12896
|
+
this.logger.info(`Initial permission mode: ${initialPermissionMode}`);
|
|
12828
12897
|
this.posthogAPI.updateTaskRun(payload.task_id, payload.run_id, {
|
|
12829
12898
|
status: "in_progress"
|
|
12830
12899
|
}).catch(
|
|
@@ -13315,14 +13384,16 @@ ${attributionInstructions}
|
|
|
13315
13384
|
this.logger.debug("Permission request", {
|
|
13316
13385
|
mode,
|
|
13317
13386
|
interactionOrigin,
|
|
13387
|
+
kind: params.toolCall?.kind,
|
|
13318
13388
|
options: params.options
|
|
13319
13389
|
});
|
|
13320
13390
|
const allowOption = params.options.find(
|
|
13321
13391
|
(o) => o.kind === "allow_once" || o.kind === "allow_always"
|
|
13322
13392
|
);
|
|
13323
13393
|
const selectedOptionId = allowOption?.optionId ?? params.options[0].optionId;
|
|
13394
|
+
const codeToolKind = params.toolCall?._meta?.codeToolKind;
|
|
13395
|
+
const isPlanApproval = params.toolCall?.kind === "switch_mode";
|
|
13324
13396
|
if (interactionOrigin === "slack") {
|
|
13325
|
-
const codeToolKind = params.toolCall?._meta?.codeToolKind;
|
|
13326
13397
|
if (codeToolKind === "question") {
|
|
13327
13398
|
return this.buildSlackQuestionRelayResponse(
|
|
13328
13399
|
payload,
|
|
@@ -13330,6 +13401,19 @@ ${attributionInstructions}
|
|
|
13330
13401
|
);
|
|
13331
13402
|
}
|
|
13332
13403
|
}
|
|
13404
|
+
{
|
|
13405
|
+
const isQuestion = codeToolKind === "question";
|
|
13406
|
+
const sessionPermissionMode = this.getSessionPermissionMode();
|
|
13407
|
+
const needsRelay = isQuestion || isPlanApproval || sessionPermissionMode === "default";
|
|
13408
|
+
if (needsRelay && this.session?.hasDesktopConnected) {
|
|
13409
|
+
this.logger.info("Relaying permission to connected client", {
|
|
13410
|
+
kind: params.toolCall?.kind,
|
|
13411
|
+
isQuestion,
|
|
13412
|
+
sessionPermissionMode
|
|
13413
|
+
});
|
|
13414
|
+
return this.relayPermissionToClient(params);
|
|
13415
|
+
}
|
|
13416
|
+
}
|
|
13333
13417
|
if (this.shouldBlockPublishPermission(params)) {
|
|
13334
13418
|
return {
|
|
13335
13419
|
outcome: { outcome: "cancelled" },
|
|
@@ -13349,6 +13433,12 @@ ${attributionInstructions}
|
|
|
13349
13433
|
this.logger.debug("Extension notification", { method, params });
|
|
13350
13434
|
},
|
|
13351
13435
|
sessionUpdate: async (params) => {
|
|
13436
|
+
if (params.update?.sessionUpdate === "current_mode_update" && typeof params.update?.currentModeId === "string" && this.session) {
|
|
13437
|
+
this.session.permissionMode = params.update.currentModeId;
|
|
13438
|
+
this.logger.info("Permission mode updated", {
|
|
13439
|
+
mode: params.update.currentModeId
|
|
13440
|
+
});
|
|
13441
|
+
}
|
|
13352
13442
|
if (params.update?.sessionUpdate === "tool_call_update") {
|
|
13353
13443
|
const meta = params.update?._meta?.claudeCode;
|
|
13354
13444
|
const toolName = meta?.toolName;
|
|
@@ -13527,6 +13617,13 @@ ${attributionInstructions}
|
|
|
13527
13617
|
} catch (error) {
|
|
13528
13618
|
this.logger.error("Failed to flush session logs", error);
|
|
13529
13619
|
}
|
|
13620
|
+
for (const [, pending] of this.pendingPermissions) {
|
|
13621
|
+
pending.resolve({
|
|
13622
|
+
outcome: { outcome: "selected", optionId: "reject" },
|
|
13623
|
+
_meta: { customInput: "Session is shutting down." }
|
|
13624
|
+
});
|
|
13625
|
+
}
|
|
13626
|
+
this.pendingPermissions.clear();
|
|
13530
13627
|
try {
|
|
13531
13628
|
await this.session.acpConnection.cleanup();
|
|
13532
13629
|
} catch (error) {
|
|
@@ -13604,6 +13701,39 @@ ${attributionInstructions}
|
|
|
13604
13701
|
this.detachSseController(controller);
|
|
13605
13702
|
}
|
|
13606
13703
|
}
|
|
13704
|
+
/**
|
|
13705
|
+
* Relay a permission request (e.g., plan approval) to the connected desktop
|
|
13706
|
+
* app via SSE and wait for a response via the `/command` endpoint.
|
|
13707
|
+
*
|
|
13708
|
+
* The promise waits indefinitely — if SSE is disconnected, the event is
|
|
13709
|
+
* buffered by broadcastEvent and replayed when the client reconnects. Session
|
|
13710
|
+
* cleanup force-resolves all pending permissions, so there is no leak.
|
|
13711
|
+
*/
|
|
13712
|
+
relayPermissionToClient(params) {
|
|
13713
|
+
const requestId = crypto.randomUUID();
|
|
13714
|
+
this.broadcastEvent({
|
|
13715
|
+
type: "permission_request",
|
|
13716
|
+
requestId,
|
|
13717
|
+
options: params.options,
|
|
13718
|
+
toolCall: params.toolCall
|
|
13719
|
+
});
|
|
13720
|
+
return new Promise((resolve4) => {
|
|
13721
|
+
this.pendingPermissions.set(requestId, { resolve: resolve4 });
|
|
13722
|
+
});
|
|
13723
|
+
}
|
|
13724
|
+
resolvePermission(requestId, optionId, customInput, answers) {
|
|
13725
|
+
const pending = this.pendingPermissions.get(requestId);
|
|
13726
|
+
if (!pending) return false;
|
|
13727
|
+
this.pendingPermissions.delete(requestId);
|
|
13728
|
+
const meta = {};
|
|
13729
|
+
if (customInput) meta.customInput = customInput;
|
|
13730
|
+
if (answers) meta.answers = answers;
|
|
13731
|
+
pending.resolve({
|
|
13732
|
+
outcome: { outcome: "selected", optionId },
|
|
13733
|
+
...Object.keys(meta).length > 0 ? { _meta: meta } : {}
|
|
13734
|
+
});
|
|
13735
|
+
return true;
|
|
13736
|
+
}
|
|
13607
13737
|
};
|
|
13608
13738
|
|
|
13609
13739
|
// src/server/bin.ts
|