@posthog/agent 2.0.0 → 2.0.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.
Files changed (131) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +221 -219
  3. package/dist/adapters/claude/conversion/tool-use-to-acp.d.ts +21 -0
  4. package/dist/adapters/claude/conversion/tool-use-to-acp.js +547 -0
  5. package/dist/adapters/claude/conversion/tool-use-to-acp.js.map +1 -0
  6. package/dist/adapters/claude/permissions/permission-options.d.ts +13 -0
  7. package/dist/adapters/claude/permissions/permission-options.js +117 -0
  8. package/dist/adapters/claude/permissions/permission-options.js.map +1 -0
  9. package/dist/adapters/claude/questions/utils.d.ts +132 -0
  10. package/dist/adapters/claude/questions/utils.js +63 -0
  11. package/dist/adapters/claude/questions/utils.js.map +1 -0
  12. package/dist/adapters/claude/tools.d.ts +18 -0
  13. package/dist/adapters/claude/tools.js +95 -0
  14. package/dist/adapters/claude/tools.js.map +1 -0
  15. package/dist/agent-DBQY1BfC.d.ts +123 -0
  16. package/dist/agent.d.ts +5 -0
  17. package/dist/agent.js +3656 -0
  18. package/dist/agent.js.map +1 -0
  19. package/dist/claude-cli/cli.js +3695 -2746
  20. package/dist/claude-cli/vendor/ripgrep/COPYING +3 -0
  21. package/dist/claude-cli/vendor/ripgrep/arm64-darwin/rg +0 -0
  22. package/dist/claude-cli/vendor/ripgrep/arm64-darwin/ripgrep.node +0 -0
  23. package/dist/claude-cli/vendor/ripgrep/arm64-linux/rg +0 -0
  24. package/dist/claude-cli/vendor/ripgrep/arm64-linux/ripgrep.node +0 -0
  25. package/dist/claude-cli/vendor/ripgrep/x64-darwin/rg +0 -0
  26. package/dist/claude-cli/vendor/ripgrep/x64-darwin/ripgrep.node +0 -0
  27. package/dist/claude-cli/vendor/ripgrep/x64-linux/rg +0 -0
  28. package/dist/claude-cli/vendor/ripgrep/x64-linux/ripgrep.node +0 -0
  29. package/dist/claude-cli/vendor/ripgrep/x64-win32/rg.exe +0 -0
  30. package/dist/claude-cli/vendor/ripgrep/x64-win32/ripgrep.node +0 -0
  31. package/dist/gateway-models.d.ts +24 -0
  32. package/dist/gateway-models.js +93 -0
  33. package/dist/gateway-models.js.map +1 -0
  34. package/dist/index.d.ts +170 -1157
  35. package/dist/index.js +9373 -5135
  36. package/dist/index.js.map +1 -1
  37. package/dist/logger-DDBiMOOD.d.ts +24 -0
  38. package/dist/posthog-api.d.ts +40 -0
  39. package/dist/posthog-api.js +175 -0
  40. package/dist/posthog-api.js.map +1 -0
  41. package/dist/server/agent-server.d.ts +41 -0
  42. package/dist/server/agent-server.js +10503 -0
  43. package/dist/server/agent-server.js.map +1 -0
  44. package/dist/server/bin.d.ts +1 -0
  45. package/dist/server/bin.js +10558 -0
  46. package/dist/server/bin.js.map +1 -0
  47. package/dist/types.d.ts +129 -0
  48. package/dist/types.js +1 -0
  49. package/dist/types.js.map +1 -0
  50. package/package.json +65 -13
  51. package/src/acp-extensions.ts +98 -16
  52. package/src/adapters/acp-connection.ts +494 -0
  53. package/src/adapters/base-acp-agent.ts +150 -0
  54. package/src/adapters/claude/claude-agent.ts +596 -0
  55. package/src/adapters/claude/conversion/acp-to-sdk.ts +102 -0
  56. package/src/adapters/claude/conversion/sdk-to-acp.ts +571 -0
  57. package/src/adapters/claude/conversion/tool-use-to-acp.ts +618 -0
  58. package/src/adapters/claude/hooks.ts +64 -0
  59. package/src/adapters/claude/mcp/tool-metadata.ts +102 -0
  60. package/src/adapters/claude/permissions/permission-handlers.ts +433 -0
  61. package/src/adapters/claude/permissions/permission-options.ts +103 -0
  62. package/src/adapters/claude/plan/utils.ts +56 -0
  63. package/src/adapters/claude/questions/utils.ts +92 -0
  64. package/src/adapters/claude/session/commands.ts +38 -0
  65. package/src/adapters/claude/session/mcp-config.ts +37 -0
  66. package/src/adapters/claude/session/models.ts +12 -0
  67. package/src/adapters/claude/session/options.ts +236 -0
  68. package/src/adapters/claude/tool-meta.ts +143 -0
  69. package/src/adapters/claude/tools.ts +53 -688
  70. package/src/adapters/claude/types.ts +61 -0
  71. package/src/adapters/codex/spawn.ts +130 -0
  72. package/src/agent.ts +96 -587
  73. package/src/execution-mode.ts +43 -0
  74. package/src/gateway-models.ts +135 -0
  75. package/src/index.ts +79 -0
  76. package/src/otel-log-writer.test.ts +105 -0
  77. package/src/otel-log-writer.ts +94 -0
  78. package/src/posthog-api.ts +75 -235
  79. package/src/resume.ts +115 -0
  80. package/src/sagas/apply-snapshot-saga.test.ts +690 -0
  81. package/src/sagas/apply-snapshot-saga.ts +88 -0
  82. package/src/sagas/capture-tree-saga.test.ts +892 -0
  83. package/src/sagas/capture-tree-saga.ts +141 -0
  84. package/src/sagas/resume-saga.test.ts +558 -0
  85. package/src/sagas/resume-saga.ts +332 -0
  86. package/src/sagas/test-fixtures.ts +250 -0
  87. package/src/server/agent-server.test.ts +220 -0
  88. package/src/server/agent-server.ts +748 -0
  89. package/src/server/bin.ts +88 -0
  90. package/src/server/jwt.ts +65 -0
  91. package/src/server/schemas.ts +47 -0
  92. package/src/server/types.ts +13 -0
  93. package/src/server/utils/retry.test.ts +122 -0
  94. package/src/server/utils/retry.ts +61 -0
  95. package/src/server/utils/sse-parser.test.ts +93 -0
  96. package/src/server/utils/sse-parser.ts +46 -0
  97. package/src/session-log-writer.test.ts +140 -0
  98. package/src/session-log-writer.ts +137 -0
  99. package/src/test/assertions.ts +114 -0
  100. package/src/test/controllers/sse-controller.ts +107 -0
  101. package/src/test/fixtures/api.ts +111 -0
  102. package/src/test/fixtures/config.ts +33 -0
  103. package/src/test/fixtures/notifications.ts +92 -0
  104. package/src/test/mocks/claude-sdk.ts +251 -0
  105. package/src/test/mocks/msw-handlers.ts +48 -0
  106. package/src/test/setup.ts +114 -0
  107. package/src/test/wait.ts +41 -0
  108. package/src/tree-tracker.ts +173 -0
  109. package/src/types.ts +54 -137
  110. package/src/utils/acp-content.ts +58 -0
  111. package/src/utils/async-mutex.test.ts +104 -0
  112. package/src/utils/async-mutex.ts +31 -0
  113. package/src/utils/common.ts +15 -0
  114. package/src/utils/gateway.ts +9 -6
  115. package/src/utils/logger.ts +0 -30
  116. package/src/utils/streams.ts +220 -0
  117. package/CLAUDE.md +0 -331
  118. package/src/adapters/claude/claude.ts +0 -1947
  119. package/src/adapters/claude/mcp-server.ts +0 -810
  120. package/src/adapters/claude/utils.ts +0 -267
  121. package/src/adapters/connection.ts +0 -95
  122. package/src/file-manager.ts +0 -273
  123. package/src/git-manager.ts +0 -577
  124. package/src/schemas.ts +0 -241
  125. package/src/session-store.ts +0 -259
  126. package/src/task-manager.ts +0 -163
  127. package/src/todo-manager.ts +0 -180
  128. package/src/tools/registry.ts +0 -134
  129. package/src/tools/types.ts +0 -133
  130. package/src/utils/tapped-stream.ts +0 -60
  131. package/src/worktree-manager.ts +0 -974
@@ -0,0 +1,117 @@
1
+ // src/utils/common.ts
2
+ var IS_ROOT = typeof process !== "undefined" && (process.geteuid?.() ?? process.getuid?.()) === 0;
3
+
4
+ // src/adapters/claude/mcp/tool-metadata.ts
5
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
6
+ import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
7
+
8
+ // src/adapters/claude/tools.ts
9
+ var READ_TOOLS = /* @__PURE__ */ new Set(["Read", "NotebookRead"]);
10
+ var WRITE_TOOLS = /* @__PURE__ */ new Set([
11
+ "Edit",
12
+ "Write",
13
+ "NotebookEdit"
14
+ ]);
15
+ var BASH_TOOLS = /* @__PURE__ */ new Set([
16
+ "Bash",
17
+ "BashOutput",
18
+ "KillShell"
19
+ ]);
20
+ var SEARCH_TOOLS = /* @__PURE__ */ new Set(["Glob", "Grep", "LS"]);
21
+ var WEB_TOOLS = /* @__PURE__ */ new Set(["WebSearch", "WebFetch"]);
22
+ var AGENT_TOOLS = /* @__PURE__ */ new Set(["Task", "TodoWrite"]);
23
+ var BASE_ALLOWED_TOOLS = [
24
+ ...READ_TOOLS,
25
+ ...SEARCH_TOOLS,
26
+ ...WEB_TOOLS,
27
+ ...AGENT_TOOLS
28
+ ];
29
+ var AUTO_ALLOWED_TOOLS = {
30
+ default: new Set(BASE_ALLOWED_TOOLS),
31
+ acceptEdits: /* @__PURE__ */ new Set([...BASE_ALLOWED_TOOLS, ...WRITE_TOOLS]),
32
+ plan: new Set(BASE_ALLOWED_TOOLS)
33
+ };
34
+
35
+ // src/adapters/claude/permissions/permission-options.ts
36
+ function permissionOptions(allowAlwaysLabel) {
37
+ return [
38
+ { kind: "allow_once", name: "Yes", optionId: "allow" },
39
+ { kind: "allow_always", name: allowAlwaysLabel, optionId: "allow_always" },
40
+ {
41
+ kind: "reject_once",
42
+ name: "No, and tell the agent what to do differently",
43
+ optionId: "reject",
44
+ _meta: { customInput: true }
45
+ }
46
+ ];
47
+ }
48
+ function buildPermissionOptions(toolName, toolInput, cwd) {
49
+ if (BASH_TOOLS.has(toolName)) {
50
+ const command = toolInput?.command;
51
+ const cmdName = command?.split(/\s+/)[0] ?? "this command";
52
+ const cwdLabel = cwd ? ` in ${cwd}` : "";
53
+ return permissionOptions(
54
+ `Yes, and don't ask again for \`${cmdName}\` commands${cwdLabel}`
55
+ );
56
+ }
57
+ if (toolName === "BashOutput") {
58
+ return permissionOptions("Yes, allow all background process reads");
59
+ }
60
+ if (toolName === "KillShell") {
61
+ return permissionOptions("Yes, allow killing processes");
62
+ }
63
+ if (WRITE_TOOLS.has(toolName)) {
64
+ return permissionOptions("Yes, allow all edits during this session");
65
+ }
66
+ if (READ_TOOLS.has(toolName)) {
67
+ return permissionOptions("Yes, allow all reads during this session");
68
+ }
69
+ if (SEARCH_TOOLS.has(toolName)) {
70
+ return permissionOptions("Yes, allow all searches during this session");
71
+ }
72
+ if (toolName === "WebFetch") {
73
+ const url = toolInput?.url;
74
+ let domain = "";
75
+ try {
76
+ domain = url ? new URL(url).hostname : "";
77
+ } catch {
78
+ }
79
+ return permissionOptions(
80
+ domain ? `Yes, allow all fetches from ${domain}` : "Yes, allow all fetches"
81
+ );
82
+ }
83
+ if (toolName === "WebSearch") {
84
+ return permissionOptions("Yes, allow all web searches");
85
+ }
86
+ if (toolName === "Task") {
87
+ return permissionOptions("Yes, allow all sub-tasks");
88
+ }
89
+ if (toolName === "TodoWrite") {
90
+ return permissionOptions("Yes, allow all todo updates");
91
+ }
92
+ return permissionOptions("Yes, always allow");
93
+ }
94
+ function buildExitPlanModePermissionOptions() {
95
+ return [
96
+ {
97
+ kind: "allow_always",
98
+ name: "Yes, and auto-accept edits",
99
+ optionId: "acceptEdits"
100
+ },
101
+ {
102
+ kind: "allow_once",
103
+ name: "Yes, and manually approve edits",
104
+ optionId: "default"
105
+ },
106
+ {
107
+ kind: "reject_once",
108
+ name: "No, keep planning",
109
+ optionId: "plan"
110
+ }
111
+ ];
112
+ }
113
+ export {
114
+ buildExitPlanModePermissionOptions,
115
+ buildPermissionOptions
116
+ };
117
+ //# sourceMappingURL=permission-options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/utils/common.ts","../../../../src/adapters/claude/mcp/tool-metadata.ts","../../../../src/adapters/claude/tools.ts","../../../../src/adapters/claude/permissions/permission-options.ts"],"sourcesContent":["import type { Logger } from \"./logger.js\";\n\nexport const IS_ROOT =\n typeof process !== \"undefined\" &&\n (process.geteuid?.() ?? process.getuid?.()) === 0;\n\nexport function unreachable(value: never, logger: Logger): void {\n let valueAsString: string;\n try {\n valueAsString = JSON.stringify(value);\n } catch {\n valueAsString = value;\n }\n logger.error(`Unexpected case: ${valueAsString}`);\n}\n","import type { McpServerConfig } from \"@anthropic-ai/claude-agent-sdk\";\nimport { Client } from \"@modelcontextprotocol/sdk/client/index.js\";\nimport { StreamableHTTPClientTransport } from \"@modelcontextprotocol/sdk/client/streamableHttp.js\";\nimport type { Tool } from \"@modelcontextprotocol/sdk/types.js\";\nimport { Logger } from \"../../../utils/logger.js\";\n\nexport interface McpToolMetadata {\n readOnly: boolean;\n}\n\nconst mcpToolMetadataCache: Map<string, McpToolMetadata> = new Map();\n\nfunction buildToolKey(serverName: string, toolName: string): string {\n return `mcp__${serverName}__${toolName}`;\n}\n\nfunction isHttpMcpServer(\n config: McpServerConfig,\n): config is McpServerConfig & { type: \"http\"; url: string } {\n return config.type === \"http\" && typeof (config as any).url === \"string\";\n}\n\nasync function fetchToolsFromHttpServer(\n _serverName: string,\n config: McpServerConfig & { type: \"http\"; url: string },\n): Promise<Tool[]> {\n const transport = new StreamableHTTPClientTransport(new URL(config.url), {\n requestInit: {\n headers: (config as any).headers || {},\n },\n });\n\n const client = new Client({\n name: \"twig-metadata-fetcher\",\n version: \"1.0.0\",\n });\n\n try {\n await client.connect(transport);\n const result = await client.listTools();\n return result.tools;\n } finally {\n await client.close().catch(() => {});\n }\n}\n\nfunction extractToolMetadata(tool: Tool): McpToolMetadata {\n return {\n readOnly: tool.annotations?.readOnlyHint === true,\n };\n}\n\nexport async function fetchMcpToolMetadata(\n mcpServers: Record<string, McpServerConfig>,\n logger: Logger = new Logger({ debug: false, prefix: \"[McpToolMetadata]\" }),\n): Promise<void> {\n const fetchPromises: Promise<void>[] = [];\n\n for (const [serverName, config] of Object.entries(mcpServers)) {\n if (!isHttpMcpServer(config)) {\n continue;\n }\n\n const fetchPromise = fetchToolsFromHttpServer(serverName, config)\n .then((tools) => {\n const toolCount = tools.length;\n const readOnlyCount = tools.filter(\n (t) => t.annotations?.readOnlyHint === true,\n ).length;\n\n for (const tool of tools) {\n const toolKey = buildToolKey(serverName, tool.name);\n mcpToolMetadataCache.set(toolKey, extractToolMetadata(tool));\n }\n\n logger.info(\"Fetched MCP tool metadata\", {\n serverName,\n toolCount,\n readOnlyCount,\n });\n })\n .catch((error) => {\n logger.error(\"Failed to fetch MCP tool metadata\", {\n serverName,\n error: error instanceof Error ? error.message : String(error),\n });\n });\n\n fetchPromises.push(fetchPromise);\n }\n\n await Promise.all(fetchPromises);\n}\n\nexport function isMcpToolReadOnly(toolName: string): boolean {\n const metadata = mcpToolMetadataCache.get(toolName);\n return metadata?.readOnly === true;\n}\n\nexport function clearMcpToolMetadataCache(): void {\n mcpToolMetadataCache.clear();\n}\n","export {\n getAvailableModes,\n type ModeInfo,\n TWIG_EXECUTION_MODES,\n type TwigExecutionMode,\n} from \"../../execution-mode.js\";\n\nimport type { TwigExecutionMode } from \"../../execution-mode.js\";\nimport { isMcpToolReadOnly } from \"./mcp/tool-metadata.js\";\n\nexport const READ_TOOLS: Set<string> = new Set([\"Read\", \"NotebookRead\"]);\n\nexport const WRITE_TOOLS: Set<string> = new Set([\n \"Edit\",\n \"Write\",\n \"NotebookEdit\",\n]);\n\nexport const BASH_TOOLS: Set<string> = new Set([\n \"Bash\",\n \"BashOutput\",\n \"KillShell\",\n]);\n\nexport const SEARCH_TOOLS: Set<string> = new Set([\"Glob\", \"Grep\", \"LS\"]);\n\nexport const WEB_TOOLS: Set<string> = new Set([\"WebSearch\", \"WebFetch\"]);\n\nexport const AGENT_TOOLS: Set<string> = new Set([\"Task\", \"TodoWrite\"]);\n\nconst BASE_ALLOWED_TOOLS = [\n ...READ_TOOLS,\n ...SEARCH_TOOLS,\n ...WEB_TOOLS,\n ...AGENT_TOOLS,\n];\n\nconst AUTO_ALLOWED_TOOLS: Record<string, Set<string>> = {\n default: new Set(BASE_ALLOWED_TOOLS),\n acceptEdits: new Set([...BASE_ALLOWED_TOOLS, ...WRITE_TOOLS]),\n plan: new Set(BASE_ALLOWED_TOOLS),\n};\n\nexport function isToolAllowedForMode(\n toolName: string,\n mode: TwigExecutionMode,\n): boolean {\n if (mode === \"bypassPermissions\") {\n return true;\n }\n if (AUTO_ALLOWED_TOOLS[mode]?.has(toolName) === true) {\n return true;\n }\n if (isMcpToolReadOnly(toolName)) {\n return true;\n }\n return false;\n}\n","import { BASH_TOOLS, READ_TOOLS, SEARCH_TOOLS, WRITE_TOOLS } from \"../tools.js\";\n\nexport interface PermissionOption {\n kind: \"allow_once\" | \"allow_always\" | \"reject_once\" | \"reject_always\";\n name: string;\n optionId: string;\n _meta?: { description?: string; customInput?: boolean };\n}\n\nfunction permissionOptions(allowAlwaysLabel: string): PermissionOption[] {\n return [\n { kind: \"allow_once\", name: \"Yes\", optionId: \"allow\" },\n { kind: \"allow_always\", name: allowAlwaysLabel, optionId: \"allow_always\" },\n {\n kind: \"reject_once\",\n name: \"No, and tell the agent what to do differently\",\n optionId: \"reject\",\n _meta: { customInput: true },\n },\n ];\n}\n\nexport function buildPermissionOptions(\n toolName: string,\n toolInput: Record<string, unknown>,\n cwd?: string,\n): PermissionOption[] {\n if (BASH_TOOLS.has(toolName)) {\n const command = toolInput?.command as string | undefined;\n const cmdName = command?.split(/\\s+/)[0] ?? \"this command\";\n const cwdLabel = cwd ? ` in ${cwd}` : \"\";\n return permissionOptions(\n `Yes, and don't ask again for \\`${cmdName}\\` commands${cwdLabel}`,\n );\n }\n\n if (toolName === \"BashOutput\") {\n return permissionOptions(\"Yes, allow all background process reads\");\n }\n\n if (toolName === \"KillShell\") {\n return permissionOptions(\"Yes, allow killing processes\");\n }\n\n if (WRITE_TOOLS.has(toolName)) {\n return permissionOptions(\"Yes, allow all edits during this session\");\n }\n\n if (READ_TOOLS.has(toolName)) {\n return permissionOptions(\"Yes, allow all reads during this session\");\n }\n\n if (SEARCH_TOOLS.has(toolName)) {\n return permissionOptions(\"Yes, allow all searches during this session\");\n }\n\n if (toolName === \"WebFetch\") {\n const url = toolInput?.url as string | undefined;\n let domain = \"\";\n try {\n domain = url ? new URL(url).hostname : \"\";\n } catch {}\n return permissionOptions(\n domain\n ? `Yes, allow all fetches from ${domain}`\n : \"Yes, allow all fetches\",\n );\n }\n\n if (toolName === \"WebSearch\") {\n return permissionOptions(\"Yes, allow all web searches\");\n }\n\n if (toolName === \"Task\") {\n return permissionOptions(\"Yes, allow all sub-tasks\");\n }\n\n if (toolName === \"TodoWrite\") {\n return permissionOptions(\"Yes, allow all todo updates\");\n }\n\n return permissionOptions(\"Yes, always allow\");\n}\n\nexport function buildExitPlanModePermissionOptions(): PermissionOption[] {\n return [\n {\n kind: \"allow_always\",\n name: \"Yes, and auto-accept edits\",\n optionId: \"acceptEdits\",\n },\n {\n kind: \"allow_once\",\n name: \"Yes, and manually approve edits\",\n optionId: \"default\",\n },\n {\n kind: \"reject_once\",\n name: \"No, keep planning\",\n optionId: \"plan\",\n },\n ];\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,132 @@
1
+ import { ToolKind, ToolCallContent } from '@agentclientprotocol/sdk';
2
+ import { z } from 'zod';
3
+ import { PermissionOption } from '../permissions/permission-options.js';
4
+
5
+ declare const OPTION_PREFIX = "option_";
6
+ declare const QuestionOptionSchema: z.ZodObject<{
7
+ label: z.ZodString;
8
+ description: z.ZodOptional<z.ZodString>;
9
+ }, "strip", z.ZodTypeAny, {
10
+ label: string;
11
+ description?: string | undefined;
12
+ }, {
13
+ label: string;
14
+ description?: string | undefined;
15
+ }>;
16
+ declare const QuestionItemSchema: z.ZodObject<{
17
+ question: z.ZodString;
18
+ header: z.ZodOptional<z.ZodString>;
19
+ options: z.ZodArray<z.ZodObject<{
20
+ label: z.ZodString;
21
+ description: z.ZodOptional<z.ZodString>;
22
+ }, "strip", z.ZodTypeAny, {
23
+ label: string;
24
+ description?: string | undefined;
25
+ }, {
26
+ label: string;
27
+ description?: string | undefined;
28
+ }>, "many">;
29
+ multiSelect: z.ZodOptional<z.ZodBoolean>;
30
+ completed: z.ZodOptional<z.ZodBoolean>;
31
+ }, "strip", z.ZodTypeAny, {
32
+ options: {
33
+ label: string;
34
+ description?: string | undefined;
35
+ }[];
36
+ question: string;
37
+ completed?: boolean | undefined;
38
+ header?: string | undefined;
39
+ multiSelect?: boolean | undefined;
40
+ }, {
41
+ options: {
42
+ label: string;
43
+ description?: string | undefined;
44
+ }[];
45
+ question: string;
46
+ completed?: boolean | undefined;
47
+ header?: string | undefined;
48
+ multiSelect?: boolean | undefined;
49
+ }>;
50
+ declare const QuestionMetaSchema: z.ZodObject<{
51
+ questions: z.ZodArray<z.ZodObject<{
52
+ question: z.ZodString;
53
+ header: z.ZodOptional<z.ZodString>;
54
+ options: z.ZodArray<z.ZodObject<{
55
+ label: z.ZodString;
56
+ description: z.ZodOptional<z.ZodString>;
57
+ }, "strip", z.ZodTypeAny, {
58
+ label: string;
59
+ description?: string | undefined;
60
+ }, {
61
+ label: string;
62
+ description?: string | undefined;
63
+ }>, "many">;
64
+ multiSelect: z.ZodOptional<z.ZodBoolean>;
65
+ completed: z.ZodOptional<z.ZodBoolean>;
66
+ }, "strip", z.ZodTypeAny, {
67
+ options: {
68
+ label: string;
69
+ description?: string | undefined;
70
+ }[];
71
+ question: string;
72
+ completed?: boolean | undefined;
73
+ header?: string | undefined;
74
+ multiSelect?: boolean | undefined;
75
+ }, {
76
+ options: {
77
+ label: string;
78
+ description?: string | undefined;
79
+ }[];
80
+ question: string;
81
+ completed?: boolean | undefined;
82
+ header?: string | undefined;
83
+ multiSelect?: boolean | undefined;
84
+ }>, "many">;
85
+ }, "strip", z.ZodTypeAny, {
86
+ questions: {
87
+ options: {
88
+ label: string;
89
+ description?: string | undefined;
90
+ }[];
91
+ question: string;
92
+ completed?: boolean | undefined;
93
+ header?: string | undefined;
94
+ multiSelect?: boolean | undefined;
95
+ }[];
96
+ }, {
97
+ questions: {
98
+ options: {
99
+ label: string;
100
+ description?: string | undefined;
101
+ }[];
102
+ question: string;
103
+ completed?: boolean | undefined;
104
+ header?: string | undefined;
105
+ multiSelect?: boolean | undefined;
106
+ }[];
107
+ }>;
108
+ type QuestionOption = z.infer<typeof QuestionOptionSchema>;
109
+ type QuestionItem = z.infer<typeof QuestionItemSchema>;
110
+ type QuestionMeta = z.infer<typeof QuestionMetaSchema>;
111
+ interface AskUserQuestionInput {
112
+ questions?: QuestionItem[];
113
+ question?: string;
114
+ header?: string;
115
+ options?: QuestionOption[];
116
+ multiSelect?: boolean;
117
+ }
118
+ declare function normalizeAskUserQuestionInput(input: AskUserQuestionInput): QuestionItem[] | null;
119
+ interface QuestionToolCallData {
120
+ toolCallId: string;
121
+ title: string;
122
+ kind: ToolKind;
123
+ content: ToolCallContent[];
124
+ _meta: {
125
+ twigToolKind: "question";
126
+ questions: QuestionItem[];
127
+ };
128
+ }
129
+ declare function buildQuestionToolCallData(questions: QuestionItem[]): QuestionToolCallData;
130
+ declare function buildQuestionOptions(question: QuestionItem): PermissionOption[];
131
+
132
+ export { type AskUserQuestionInput, OPTION_PREFIX, type QuestionItem, QuestionItemSchema, type QuestionMeta, QuestionMetaSchema, type QuestionOption, QuestionOptionSchema, buildQuestionOptions, buildQuestionToolCallData, normalizeAskUserQuestionInput };
@@ -0,0 +1,63 @@
1
+ // src/adapters/claude/questions/utils.ts
2
+ import { z } from "zod";
3
+ var OPTION_PREFIX = "option_";
4
+ var QuestionOptionSchema = z.object({
5
+ label: z.string(),
6
+ description: z.string().optional()
7
+ });
8
+ var QuestionItemSchema = z.object({
9
+ question: z.string(),
10
+ header: z.string().optional(),
11
+ options: z.array(QuestionOptionSchema),
12
+ multiSelect: z.boolean().optional(),
13
+ completed: z.boolean().optional()
14
+ });
15
+ var QuestionMetaSchema = z.object({
16
+ questions: z.array(QuestionItemSchema)
17
+ });
18
+ function normalizeAskUserQuestionInput(input) {
19
+ if (input.questions && input.questions.length > 0) {
20
+ return input.questions;
21
+ }
22
+ if (input.question) {
23
+ return [
24
+ {
25
+ question: input.question,
26
+ header: input.header,
27
+ options: input.options || [],
28
+ multiSelect: input.multiSelect
29
+ }
30
+ ];
31
+ }
32
+ return null;
33
+ }
34
+ function buildQuestionToolCallData(questions) {
35
+ return {
36
+ toolCallId: `question-${Date.now()}`,
37
+ title: questions[0]?.question ?? "Question",
38
+ kind: "other",
39
+ content: [],
40
+ _meta: {
41
+ twigToolKind: "question",
42
+ questions
43
+ }
44
+ };
45
+ }
46
+ function buildQuestionOptions(question) {
47
+ return question.options.map((opt, idx) => ({
48
+ kind: "allow_once",
49
+ name: opt.label,
50
+ optionId: `${OPTION_PREFIX}${idx}`,
51
+ _meta: opt.description ? { description: opt.description } : void 0
52
+ }));
53
+ }
54
+ export {
55
+ OPTION_PREFIX,
56
+ QuestionItemSchema,
57
+ QuestionMetaSchema,
58
+ QuestionOptionSchema,
59
+ buildQuestionOptions,
60
+ buildQuestionToolCallData,
61
+ normalizeAskUserQuestionInput
62
+ };
63
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/adapters/claude/questions/utils.ts"],"sourcesContent":["import type { ToolCallContent, ToolKind } from \"@agentclientprotocol/sdk\";\nimport { z } from \"zod\";\nimport type { PermissionOption } from \"../permissions/permission-options.js\";\n\nexport const OPTION_PREFIX = \"option_\";\n\nexport const QuestionOptionSchema = z.object({\n label: z.string(),\n description: z.string().optional(),\n});\n\nexport const QuestionItemSchema = z.object({\n question: z.string(),\n header: z.string().optional(),\n options: z.array(QuestionOptionSchema),\n multiSelect: z.boolean().optional(),\n completed: z.boolean().optional(),\n});\n\nexport const QuestionMetaSchema = z.object({\n questions: z.array(QuestionItemSchema),\n});\n\nexport type QuestionOption = z.infer<typeof QuestionOptionSchema>;\nexport type QuestionItem = z.infer<typeof QuestionItemSchema>;\nexport type QuestionMeta = z.infer<typeof QuestionMetaSchema>;\n\nexport interface AskUserQuestionInput {\n questions?: QuestionItem[];\n question?: string;\n header?: string;\n options?: QuestionOption[];\n multiSelect?: boolean;\n}\n\nexport function normalizeAskUserQuestionInput(\n input: AskUserQuestionInput,\n): QuestionItem[] | null {\n if (input.questions && input.questions.length > 0) {\n return input.questions;\n }\n\n if (input.question) {\n return [\n {\n question: input.question,\n header: input.header,\n options: input.options || [],\n multiSelect: input.multiSelect,\n },\n ];\n }\n\n return null;\n}\n\ninterface QuestionToolCallData {\n toolCallId: string;\n title: string;\n kind: ToolKind;\n content: ToolCallContent[];\n _meta: {\n twigToolKind: \"question\";\n questions: QuestionItem[];\n };\n}\n\nexport function buildQuestionToolCallData(\n questions: QuestionItem[],\n): QuestionToolCallData {\n return {\n toolCallId: `question-${Date.now()}`,\n title: questions[0]?.question ?? \"Question\",\n kind: \"other\",\n content: [],\n _meta: {\n twigToolKind: \"question\",\n questions,\n },\n };\n}\n\nexport function buildQuestionOptions(\n question: QuestionItem,\n): PermissionOption[] {\n return question.options.map((opt, idx) => ({\n kind: \"allow_once\" as const,\n name: opt.label,\n optionId: `${OPTION_PREFIX}${idx}`,\n _meta: opt.description ? { description: opt.description } : undefined,\n }));\n}\n"],"mappings":";AACA,SAAS,SAAS;AAGX,IAAM,gBAAgB;AAEtB,IAAM,uBAAuB,EAAE,OAAO;AAAA,EAC3C,OAAO,EAAE,OAAO;AAAA,EAChB,aAAa,EAAE,OAAO,EAAE,SAAS;AACnC,CAAC;AAEM,IAAM,qBAAqB,EAAE,OAAO;AAAA,EACzC,UAAU,EAAE,OAAO;AAAA,EACnB,QAAQ,EAAE,OAAO,EAAE,SAAS;AAAA,EAC5B,SAAS,EAAE,MAAM,oBAAoB;AAAA,EACrC,aAAa,EAAE,QAAQ,EAAE,SAAS;AAAA,EAClC,WAAW,EAAE,QAAQ,EAAE,SAAS;AAClC,CAAC;AAEM,IAAM,qBAAqB,EAAE,OAAO;AAAA,EACzC,WAAW,EAAE,MAAM,kBAAkB;AACvC,CAAC;AAcM,SAAS,8BACd,OACuB;AACvB,MAAI,MAAM,aAAa,MAAM,UAAU,SAAS,GAAG;AACjD,WAAO,MAAM;AAAA,EACf;AAEA,MAAI,MAAM,UAAU;AAClB,WAAO;AAAA,MACL;AAAA,QACE,UAAU,MAAM;AAAA,QAChB,QAAQ,MAAM;AAAA,QACd,SAAS,MAAM,WAAW,CAAC;AAAA,QAC3B,aAAa,MAAM;AAAA,MACrB;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAaO,SAAS,0BACd,WACsB;AACtB,SAAO;AAAA,IACL,YAAY,YAAY,KAAK,IAAI,CAAC;AAAA,IAClC,OAAO,UAAU,CAAC,GAAG,YAAY;AAAA,IACjC,MAAM;AAAA,IACN,SAAS,CAAC;AAAA,IACV,OAAO;AAAA,MACL,cAAc;AAAA,MACd;AAAA,IACF;AAAA,EACF;AACF;AAEO,SAAS,qBACd,UACoB;AACpB,SAAO,SAAS,QAAQ,IAAI,CAAC,KAAK,SAAS;AAAA,IACzC,MAAM;AAAA,IACN,MAAM,IAAI;AAAA,IACV,UAAU,GAAG,aAAa,GAAG,GAAG;AAAA,IAChC,OAAO,IAAI,cAAc,EAAE,aAAa,IAAI,YAAY,IAAI;AAAA,EAC9D,EAAE;AACJ;","names":[]}
@@ -0,0 +1,18 @@
1
+ interface ModeInfo {
2
+ id: TwigExecutionMode;
3
+ name: string;
4
+ description: string;
5
+ }
6
+ declare const TWIG_EXECUTION_MODES: readonly ["default", "acceptEdits", "plan", "bypassPermissions"];
7
+ type TwigExecutionMode = (typeof TWIG_EXECUTION_MODES)[number];
8
+ declare function getAvailableModes(): ModeInfo[];
9
+
10
+ declare const READ_TOOLS: Set<string>;
11
+ declare const WRITE_TOOLS: Set<string>;
12
+ declare const BASH_TOOLS: Set<string>;
13
+ declare const SEARCH_TOOLS: Set<string>;
14
+ declare const WEB_TOOLS: Set<string>;
15
+ declare const AGENT_TOOLS: Set<string>;
16
+ declare function isToolAllowedForMode(toolName: string, mode: TwigExecutionMode): boolean;
17
+
18
+ export { AGENT_TOOLS, BASH_TOOLS, type ModeInfo, READ_TOOLS, SEARCH_TOOLS, TWIG_EXECUTION_MODES, type TwigExecutionMode, WEB_TOOLS, WRITE_TOOLS, getAvailableModes, isToolAllowedForMode };
@@ -0,0 +1,95 @@
1
+ // src/utils/common.ts
2
+ var IS_ROOT = typeof process !== "undefined" && (process.geteuid?.() ?? process.getuid?.()) === 0;
3
+
4
+ // src/execution-mode.ts
5
+ var MODES = [
6
+ {
7
+ id: "default",
8
+ name: "Always Ask",
9
+ description: "Prompts for permission on first use of each tool"
10
+ },
11
+ {
12
+ id: "acceptEdits",
13
+ name: "Accept Edits",
14
+ description: "Automatically accepts file edit permissions for the session"
15
+ },
16
+ {
17
+ id: "plan",
18
+ name: "Plan Mode",
19
+ description: "Claude can analyze but not modify files or execute commands"
20
+ },
21
+ {
22
+ id: "bypassPermissions",
23
+ name: "Bypass Permissions",
24
+ description: "Skips all permission prompts"
25
+ }
26
+ ];
27
+ var TWIG_EXECUTION_MODES = [
28
+ "default",
29
+ "acceptEdits",
30
+ "plan",
31
+ "bypassPermissions"
32
+ ];
33
+ function getAvailableModes() {
34
+ return IS_ROOT ? MODES.filter((m) => m.id !== "bypassPermissions") : MODES;
35
+ }
36
+
37
+ // src/adapters/claude/mcp/tool-metadata.ts
38
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
39
+ import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
40
+ var mcpToolMetadataCache = /* @__PURE__ */ new Map();
41
+ function isMcpToolReadOnly(toolName) {
42
+ const metadata = mcpToolMetadataCache.get(toolName);
43
+ return metadata?.readOnly === true;
44
+ }
45
+
46
+ // src/adapters/claude/tools.ts
47
+ var READ_TOOLS = /* @__PURE__ */ new Set(["Read", "NotebookRead"]);
48
+ var WRITE_TOOLS = /* @__PURE__ */ new Set([
49
+ "Edit",
50
+ "Write",
51
+ "NotebookEdit"
52
+ ]);
53
+ var BASH_TOOLS = /* @__PURE__ */ new Set([
54
+ "Bash",
55
+ "BashOutput",
56
+ "KillShell"
57
+ ]);
58
+ var SEARCH_TOOLS = /* @__PURE__ */ new Set(["Glob", "Grep", "LS"]);
59
+ var WEB_TOOLS = /* @__PURE__ */ new Set(["WebSearch", "WebFetch"]);
60
+ var AGENT_TOOLS = /* @__PURE__ */ new Set(["Task", "TodoWrite"]);
61
+ var BASE_ALLOWED_TOOLS = [
62
+ ...READ_TOOLS,
63
+ ...SEARCH_TOOLS,
64
+ ...WEB_TOOLS,
65
+ ...AGENT_TOOLS
66
+ ];
67
+ var AUTO_ALLOWED_TOOLS = {
68
+ default: new Set(BASE_ALLOWED_TOOLS),
69
+ acceptEdits: /* @__PURE__ */ new Set([...BASE_ALLOWED_TOOLS, ...WRITE_TOOLS]),
70
+ plan: new Set(BASE_ALLOWED_TOOLS)
71
+ };
72
+ function isToolAllowedForMode(toolName, mode) {
73
+ if (mode === "bypassPermissions") {
74
+ return true;
75
+ }
76
+ if (AUTO_ALLOWED_TOOLS[mode]?.has(toolName) === true) {
77
+ return true;
78
+ }
79
+ if (isMcpToolReadOnly(toolName)) {
80
+ return true;
81
+ }
82
+ return false;
83
+ }
84
+ export {
85
+ AGENT_TOOLS,
86
+ BASH_TOOLS,
87
+ READ_TOOLS,
88
+ SEARCH_TOOLS,
89
+ TWIG_EXECUTION_MODES,
90
+ WEB_TOOLS,
91
+ WRITE_TOOLS,
92
+ getAvailableModes,
93
+ isToolAllowedForMode
94
+ };
95
+ //# sourceMappingURL=tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/utils/common.ts","../../../src/execution-mode.ts","../../../src/adapters/claude/mcp/tool-metadata.ts","../../../src/adapters/claude/tools.ts"],"sourcesContent":["import type { Logger } from \"./logger.js\";\n\nexport const IS_ROOT =\n typeof process !== \"undefined\" &&\n (process.geteuid?.() ?? process.getuid?.()) === 0;\n\nexport function unreachable(value: never, logger: Logger): void {\n let valueAsString: string;\n try {\n valueAsString = JSON.stringify(value);\n } catch {\n valueAsString = value;\n }\n logger.error(`Unexpected case: ${valueAsString}`);\n}\n","import { IS_ROOT } from \"./utils/common.js\";\n\nexport interface ModeInfo {\n id: TwigExecutionMode;\n name: string;\n description: string;\n}\n\nconst MODES: ModeInfo[] = [\n {\n id: \"default\",\n name: \"Always Ask\",\n description: \"Prompts for permission on first use of each tool\",\n },\n {\n id: \"acceptEdits\",\n name: \"Accept Edits\",\n description: \"Automatically accepts file edit permissions for the session\",\n },\n {\n id: \"plan\",\n name: \"Plan Mode\",\n description: \"Claude can analyze but not modify files or execute commands\",\n },\n {\n id: \"bypassPermissions\",\n name: \"Bypass Permissions\",\n description: \"Skips all permission prompts\",\n },\n];\n\nexport const TWIG_EXECUTION_MODES = [\n \"default\",\n \"acceptEdits\",\n \"plan\",\n \"bypassPermissions\",\n] as const;\n\nexport type TwigExecutionMode = (typeof TWIG_EXECUTION_MODES)[number];\n\nexport function getAvailableModes(): ModeInfo[] {\n return IS_ROOT ? MODES.filter((m) => m.id !== \"bypassPermissions\") : MODES;\n}\n","import type { McpServerConfig } from \"@anthropic-ai/claude-agent-sdk\";\nimport { Client } from \"@modelcontextprotocol/sdk/client/index.js\";\nimport { StreamableHTTPClientTransport } from \"@modelcontextprotocol/sdk/client/streamableHttp.js\";\nimport type { Tool } from \"@modelcontextprotocol/sdk/types.js\";\nimport { Logger } from \"../../../utils/logger.js\";\n\nexport interface McpToolMetadata {\n readOnly: boolean;\n}\n\nconst mcpToolMetadataCache: Map<string, McpToolMetadata> = new Map();\n\nfunction buildToolKey(serverName: string, toolName: string): string {\n return `mcp__${serverName}__${toolName}`;\n}\n\nfunction isHttpMcpServer(\n config: McpServerConfig,\n): config is McpServerConfig & { type: \"http\"; url: string } {\n return config.type === \"http\" && typeof (config as any).url === \"string\";\n}\n\nasync function fetchToolsFromHttpServer(\n _serverName: string,\n config: McpServerConfig & { type: \"http\"; url: string },\n): Promise<Tool[]> {\n const transport = new StreamableHTTPClientTransport(new URL(config.url), {\n requestInit: {\n headers: (config as any).headers || {},\n },\n });\n\n const client = new Client({\n name: \"twig-metadata-fetcher\",\n version: \"1.0.0\",\n });\n\n try {\n await client.connect(transport);\n const result = await client.listTools();\n return result.tools;\n } finally {\n await client.close().catch(() => {});\n }\n}\n\nfunction extractToolMetadata(tool: Tool): McpToolMetadata {\n return {\n readOnly: tool.annotations?.readOnlyHint === true,\n };\n}\n\nexport async function fetchMcpToolMetadata(\n mcpServers: Record<string, McpServerConfig>,\n logger: Logger = new Logger({ debug: false, prefix: \"[McpToolMetadata]\" }),\n): Promise<void> {\n const fetchPromises: Promise<void>[] = [];\n\n for (const [serverName, config] of Object.entries(mcpServers)) {\n if (!isHttpMcpServer(config)) {\n continue;\n }\n\n const fetchPromise = fetchToolsFromHttpServer(serverName, config)\n .then((tools) => {\n const toolCount = tools.length;\n const readOnlyCount = tools.filter(\n (t) => t.annotations?.readOnlyHint === true,\n ).length;\n\n for (const tool of tools) {\n const toolKey = buildToolKey(serverName, tool.name);\n mcpToolMetadataCache.set(toolKey, extractToolMetadata(tool));\n }\n\n logger.info(\"Fetched MCP tool metadata\", {\n serverName,\n toolCount,\n readOnlyCount,\n });\n })\n .catch((error) => {\n logger.error(\"Failed to fetch MCP tool metadata\", {\n serverName,\n error: error instanceof Error ? error.message : String(error),\n });\n });\n\n fetchPromises.push(fetchPromise);\n }\n\n await Promise.all(fetchPromises);\n}\n\nexport function isMcpToolReadOnly(toolName: string): boolean {\n const metadata = mcpToolMetadataCache.get(toolName);\n return metadata?.readOnly === true;\n}\n\nexport function clearMcpToolMetadataCache(): void {\n mcpToolMetadataCache.clear();\n}\n","export {\n getAvailableModes,\n type ModeInfo,\n TWIG_EXECUTION_MODES,\n type TwigExecutionMode,\n} from \"../../execution-mode.js\";\n\nimport type { TwigExecutionMode } from \"../../execution-mode.js\";\nimport { isMcpToolReadOnly } from \"./mcp/tool-metadata.js\";\n\nexport const READ_TOOLS: Set<string> = new Set([\"Read\", \"NotebookRead\"]);\n\nexport const WRITE_TOOLS: Set<string> = new Set([\n \"Edit\",\n \"Write\",\n \"NotebookEdit\",\n]);\n\nexport const BASH_TOOLS: Set<string> = new Set([\n \"Bash\",\n \"BashOutput\",\n \"KillShell\",\n]);\n\nexport const SEARCH_TOOLS: Set<string> = new Set([\"Glob\", \"Grep\", \"LS\"]);\n\nexport const WEB_TOOLS: Set<string> = new Set([\"WebSearch\", \"WebFetch\"]);\n\nexport const AGENT_TOOLS: Set<string> = new Set([\"Task\", \"TodoWrite\"]);\n\nconst BASE_ALLOWED_TOOLS = [\n ...READ_TOOLS,\n ...SEARCH_TOOLS,\n ...WEB_TOOLS,\n ...AGENT_TOOLS,\n];\n\nconst AUTO_ALLOWED_TOOLS: Record<string, Set<string>> = {\n default: new Set(BASE_ALLOWED_TOOLS),\n acceptEdits: new Set([...BASE_ALLOWED_TOOLS, ...WRITE_TOOLS]),\n plan: new Set(BASE_ALLOWED_TOOLS),\n};\n\nexport function isToolAllowedForMode(\n toolName: string,\n mode: TwigExecutionMode,\n): boolean {\n if (mode === \"bypassPermissions\") {\n return true;\n }\n if (AUTO_ALLOWED_TOOLS[mode]?.has(toolName) === true) {\n return true;\n }\n if (isMcpToolReadOnly(toolName)) {\n return true;\n }\n return false;\n}\n"],"mappings":";AAEO,IAAM,UACX,OAAO,YAAY,gBAClB,QAAQ,UAAU,KAAK,QAAQ,SAAS,OAAO;;;ACIlD,IAAM,QAAoB;AAAA,EACxB;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,aAAa;AAAA,EACf;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,aAAa;AAAA,EACf;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,aAAa;AAAA,EACf;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,aAAa;AAAA,EACf;AACF;AAEO,IAAM,uBAAuB;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAIO,SAAS,oBAAgC;AAC9C,SAAO,UAAU,MAAM,OAAO,CAAC,MAAM,EAAE,OAAO,mBAAmB,IAAI;AACvE;;;ACzCA,SAAS,cAAc;AACvB,SAAS,qCAAqC;AAQ9C,IAAM,uBAAqD,oBAAI,IAAI;AAoF5D,SAAS,kBAAkB,UAA2B;AAC3D,QAAM,WAAW,qBAAqB,IAAI,QAAQ;AAClD,SAAO,UAAU,aAAa;AAChC;;;ACvFO,IAAM,aAA0B,oBAAI,IAAI,CAAC,QAAQ,cAAc,CAAC;AAEhE,IAAM,cAA2B,oBAAI,IAAI;AAAA,EAC9C;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAEM,IAAM,aAA0B,oBAAI,IAAI;AAAA,EAC7C;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAEM,IAAM,eAA4B,oBAAI,IAAI,CAAC,QAAQ,QAAQ,IAAI,CAAC;AAEhE,IAAM,YAAyB,oBAAI,IAAI,CAAC,aAAa,UAAU,CAAC;AAEhE,IAAM,cAA2B,oBAAI,IAAI,CAAC,QAAQ,WAAW,CAAC;AAErE,IAAM,qBAAqB;AAAA,EACzB,GAAG;AAAA,EACH,GAAG;AAAA,EACH,GAAG;AAAA,EACH,GAAG;AACL;AAEA,IAAM,qBAAkD;AAAA,EACtD,SAAS,IAAI,IAAI,kBAAkB;AAAA,EACnC,aAAa,oBAAI,IAAI,CAAC,GAAG,oBAAoB,GAAG,WAAW,CAAC;AAAA,EAC5D,MAAM,IAAI,IAAI,kBAAkB;AAClC;AAEO,SAAS,qBACd,UACA,MACS;AACT,MAAI,SAAS,qBAAqB;AAChC,WAAO;AAAA,EACT;AACA,MAAI,mBAAmB,IAAI,GAAG,IAAI,QAAQ,MAAM,MAAM;AACpD,WAAO;AAAA,EACT;AACA,MAAI,kBAAkB,QAAQ,GAAG;AAC/B,WAAO;AAAA,EACT;AACA,SAAO;AACT;","names":[]}
@@ -0,0 +1,123 @@
1
+ import { AgentSideConnection } from '@agentclientprotocol/sdk';
2
+ import { StoredNotification, ProcessSpawnedCallback, AgentConfig, TaskExecutionOptions } from './types.js';
3
+ import { L as Logger } from './logger-DDBiMOOD.js';
4
+ import { PostHogAPIClient } from './posthog-api.js';
5
+
6
+ interface OtelLogConfig {
7
+ /** PostHog ingest host, e.g., "https://us.i.posthog.com" */
8
+ posthogHost: string;
9
+ /** Project API key, e.g., "phc_xxx" */
10
+ apiKey: string;
11
+ /** Batch flush interval in ms (default: 500) */
12
+ flushIntervalMs?: number;
13
+ /** Override the logs endpoint path (default: /i/v1/agent-logs) */
14
+ logsPath?: string;
15
+ }
16
+ /**
17
+ * Session context for resource attributes.
18
+ * These are set once per OTEL logger instance and indexed via resource_fingerprint
19
+ */
20
+ interface SessionContext {
21
+ /** Parent task grouping - all runs for a task share this */
22
+ taskId: string;
23
+ /** Primary conversation identifier - all events in a run share this */
24
+ runId: string;
25
+ /** Deployment environment - "local" for desktop, "cloud" for cloud sandbox */
26
+ deviceType?: "local" | "cloud";
27
+ }
28
+ declare class OtelLogWriter {
29
+ private loggerProvider;
30
+ private logger;
31
+ constructor(config: OtelLogConfig, sessionContext: SessionContext, _debugLogger?: Logger);
32
+ /**
33
+ * Emit an agent event to PostHog Logs via OTEL.
34
+ */
35
+ emit(entry: {
36
+ notification: StoredNotification;
37
+ }): void;
38
+ flush(): Promise<void>;
39
+ shutdown(): Promise<void>;
40
+ }
41
+
42
+ interface SessionLogWriterOptions {
43
+ /** OTEL config for creating writers per session */
44
+ otelConfig?: OtelLogConfig;
45
+ /** PostHog API client for S3 log persistence */
46
+ posthogAPI?: PostHogAPIClient;
47
+ /** Logger instance */
48
+ logger?: Logger;
49
+ }
50
+ declare class SessionLogWriter {
51
+ private posthogAPI?;
52
+ private otelConfig?;
53
+ private pendingEntries;
54
+ private flushTimeouts;
55
+ private sessions;
56
+ private logger;
57
+ constructor(options?: SessionLogWriterOptions);
58
+ flushAll(): Promise<void>;
59
+ register(sessionId: string, context: SessionContext): void;
60
+ isRegistered(sessionId: string): boolean;
61
+ appendRawLine(sessionId: string, line: string): void;
62
+ flush(sessionId: string): Promise<void>;
63
+ private scheduleFlush;
64
+ }
65
+
66
+ type StreamPair = {
67
+ readable: globalThis.ReadableStream<Uint8Array>;
68
+ writable: globalThis.WritableStream<Uint8Array>;
69
+ };
70
+
71
+ interface CodexProcessOptions {
72
+ cwd?: string;
73
+ apiBaseUrl?: string;
74
+ apiKey?: string;
75
+ model?: string;
76
+ binaryPath?: string;
77
+ logger?: Logger;
78
+ processCallbacks?: ProcessSpawnedCallback;
79
+ }
80
+
81
+ type AgentAdapter = "claude" | "codex";
82
+ type AcpConnectionConfig = {
83
+ adapter?: AgentAdapter;
84
+ logWriter?: SessionLogWriter;
85
+ taskRunId?: string;
86
+ taskId?: string;
87
+ /** Deployment environment - "local" for desktop, "cloud" for cloud sandbox */
88
+ deviceType?: "local" | "cloud";
89
+ logger?: Logger;
90
+ processCallbacks?: ProcessSpawnedCallback;
91
+ codexOptions?: CodexProcessOptions;
92
+ allowedModelIds?: Set<string>;
93
+ };
94
+ type AcpConnection = {
95
+ agentConnection?: AgentSideConnection;
96
+ clientStreams: StreamPair;
97
+ cleanup: () => Promise<void>;
98
+ };
99
+ type InProcessAcpConnection = AcpConnection;
100
+ /**
101
+ * Creates an ACP connection with the specified agent framework.
102
+ *
103
+ * @param config - Configuration including framework selection
104
+ * @returns Connection with agent and client streams
105
+ */
106
+ declare function createAcpConnection(config?: AcpConnectionConfig): AcpConnection;
107
+
108
+ declare class Agent {
109
+ private posthogAPI?;
110
+ private logger;
111
+ private acpConnection?;
112
+ private taskRunId?;
113
+ private sessionLogWriter?;
114
+ debug: boolean;
115
+ constructor(config: AgentConfig);
116
+ private _configureLlmGateway;
117
+ run(taskId: string, taskRunId: string, options?: TaskExecutionOptions): Promise<InProcessAcpConnection>;
118
+ attachPullRequestToTask(taskId: string, prUrl: string, branchName?: string): Promise<void>;
119
+ flushAllLogs(): Promise<void>;
120
+ cleanup(): Promise<void>;
121
+ }
122
+
123
+ export { type AcpConnection as A, type CodexProcessOptions as C, type InProcessAcpConnection as I, type OtelLogConfig as O, type SessionContext as S, type AcpConnectionConfig as a, Agent as b, type AgentAdapter as c, OtelLogWriter as d, SessionLogWriter as e, type SessionLogWriterOptions as f, createAcpConnection as g };
@@ -0,0 +1,5 @@
1
+ export { b as Agent } from './agent-DBQY1BfC.js';
2
+ import './types.js';
3
+ import '@agentclientprotocol/sdk';
4
+ import './logger-DDBiMOOD.js';
5
+ import './posthog-api.js';