@posthog/agent 2.1.29 → 2.1.45
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/claude/permissions/permission-options.js +1 -1
- package/dist/adapters/claude/permissions/permission-options.js.map +1 -1
- package/dist/adapters/claude/tools.js +1 -1
- package/dist/adapters/claude/tools.js.map +1 -1
- package/dist/agent.js +38 -21
- package/dist/agent.js.map +1 -1
- package/dist/gateway-models.js +21 -2
- package/dist/gateway-models.js.map +1 -1
- package/dist/index.js +44 -27
- package/dist/index.js.map +1 -1
- package/dist/server/agent-server.js +35 -27
- package/dist/server/agent-server.js.map +1 -1
- package/dist/server/bin.cjs +29 -21
- package/dist/server/bin.cjs.map +1 -1
- package/package.json +3 -3
- package/src/adapters/claude/claude-agent.ts +23 -11
- package/src/adapters/claude/session/options.ts +3 -7
- package/src/adapters/claude/tools.ts +1 -1
- package/src/gateway-models.ts +42 -2
|
@@ -19,7 +19,7 @@ var BASH_TOOLS = /* @__PURE__ */ new Set([
|
|
|
19
19
|
]);
|
|
20
20
|
var SEARCH_TOOLS = /* @__PURE__ */ new Set(["Glob", "Grep", "LS"]);
|
|
21
21
|
var WEB_TOOLS = /* @__PURE__ */ new Set(["WebSearch", "WebFetch"]);
|
|
22
|
-
var AGENT_TOOLS = /* @__PURE__ */ new Set(["Task", "TodoWrite"]);
|
|
22
|
+
var AGENT_TOOLS = /* @__PURE__ */ new Set(["Task", "TodoWrite", "Skill"]);
|
|
23
23
|
var BASE_ALLOWED_TOOLS = [
|
|
24
24
|
...READ_TOOLS,
|
|
25
25
|
...SEARCH_TOOLS,
|
|
@@ -1 +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":[]}
|
|
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\", \"Skill\"]);\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":[]}
|
|
@@ -57,7 +57,7 @@ var BASH_TOOLS = /* @__PURE__ */ new Set([
|
|
|
57
57
|
]);
|
|
58
58
|
var SEARCH_TOOLS = /* @__PURE__ */ new Set(["Glob", "Grep", "LS"]);
|
|
59
59
|
var WEB_TOOLS = /* @__PURE__ */ new Set(["WebSearch", "WebFetch"]);
|
|
60
|
-
var AGENT_TOOLS = /* @__PURE__ */ new Set(["Task", "TodoWrite"]);
|
|
60
|
+
var AGENT_TOOLS = /* @__PURE__ */ new Set(["Task", "TodoWrite", "Skill"]);
|
|
61
61
|
var BASE_ALLOWED_TOOLS = [
|
|
62
62
|
...READ_TOOLS,
|
|
63
63
|
...SEARCH_TOOLS,
|
|
@@ -1 +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,
|
|
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\", \"Skill\"]);\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,aAAa,OAAO,CAAC;AAE9E,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":[]}
|
package/dist/agent.js
CHANGED
|
@@ -276,7 +276,7 @@ import { v7 as uuidv7 } from "uuid";
|
|
|
276
276
|
// package.json
|
|
277
277
|
var package_default = {
|
|
278
278
|
name: "@posthog/agent",
|
|
279
|
-
version: "2.1.
|
|
279
|
+
version: "2.1.45",
|
|
280
280
|
repository: "https://github.com/PostHog/twig",
|
|
281
281
|
description: "TypeScript agent framework wrapping Claude Agent SDK with Git-based task execution for PostHog",
|
|
282
282
|
exports: {
|
|
@@ -407,11 +407,16 @@ function unreachable(value, logger) {
|
|
|
407
407
|
// src/gateway-models.ts
|
|
408
408
|
var DEFAULT_GATEWAY_MODEL = "claude-opus-4-6";
|
|
409
409
|
var BLOCKED_MODELS = /* @__PURE__ */ new Set(["gpt-5-mini", "openai/gpt-5-mini"]);
|
|
410
|
+
var CACHE_TTL = 10 * 60 * 1e3;
|
|
411
|
+
var gatewayModelsCache = null;
|
|
410
412
|
async function fetchGatewayModels(options) {
|
|
411
413
|
const gatewayUrl = options?.gatewayUrl ?? process.env.ANTHROPIC_BASE_URL;
|
|
412
414
|
if (!gatewayUrl) {
|
|
413
415
|
return [];
|
|
414
416
|
}
|
|
417
|
+
if (gatewayModelsCache && gatewayModelsCache.url === gatewayUrl && Date.now() < gatewayModelsCache.expiry) {
|
|
418
|
+
return gatewayModelsCache.models;
|
|
419
|
+
}
|
|
415
420
|
const modelsUrl = `${gatewayUrl}/v1/models`;
|
|
416
421
|
try {
|
|
417
422
|
const response = await fetch(modelsUrl);
|
|
@@ -419,8 +424,13 @@ async function fetchGatewayModels(options) {
|
|
|
419
424
|
return [];
|
|
420
425
|
}
|
|
421
426
|
const data = await response.json();
|
|
422
|
-
const models = data.data ?? [];
|
|
423
|
-
|
|
427
|
+
const models = (data.data ?? []).filter((m) => !BLOCKED_MODELS.has(m.id));
|
|
428
|
+
gatewayModelsCache = {
|
|
429
|
+
models,
|
|
430
|
+
expiry: Date.now() + CACHE_TTL,
|
|
431
|
+
url: gatewayUrl
|
|
432
|
+
};
|
|
433
|
+
return models;
|
|
424
434
|
} catch {
|
|
425
435
|
return [];
|
|
426
436
|
}
|
|
@@ -431,11 +441,15 @@ function isAnthropicModel(model) {
|
|
|
431
441
|
}
|
|
432
442
|
return model.id.startsWith("claude-") || model.id.startsWith("anthropic/");
|
|
433
443
|
}
|
|
444
|
+
var arrayModelsCache = null;
|
|
434
445
|
async function fetchArrayModels(options) {
|
|
435
446
|
const gatewayUrl = options?.gatewayUrl ?? process.env.ANTHROPIC_BASE_URL;
|
|
436
447
|
if (!gatewayUrl) {
|
|
437
448
|
return [];
|
|
438
449
|
}
|
|
450
|
+
if (arrayModelsCache && arrayModelsCache.url === gatewayUrl && Date.now() < arrayModelsCache.expiry) {
|
|
451
|
+
return arrayModelsCache.models;
|
|
452
|
+
}
|
|
439
453
|
try {
|
|
440
454
|
const base = new URL(gatewayUrl);
|
|
441
455
|
base.pathname = "/array/v1/models";
|
|
@@ -453,6 +467,11 @@ async function fetchArrayModels(options) {
|
|
|
453
467
|
if (!id) continue;
|
|
454
468
|
results.push({ id, owned_by: model?.owned_by });
|
|
455
469
|
}
|
|
470
|
+
arrayModelsCache = {
|
|
471
|
+
models: results,
|
|
472
|
+
expiry: Date.now() + CACHE_TTL,
|
|
473
|
+
url: gatewayUrl
|
|
474
|
+
};
|
|
456
475
|
return results;
|
|
457
476
|
} catch {
|
|
458
477
|
return [];
|
|
@@ -1728,7 +1747,7 @@ var BASH_TOOLS = /* @__PURE__ */ new Set([
|
|
|
1728
1747
|
]);
|
|
1729
1748
|
var SEARCH_TOOLS = /* @__PURE__ */ new Set(["Glob", "Grep", "LS"]);
|
|
1730
1749
|
var WEB_TOOLS = /* @__PURE__ */ new Set(["WebSearch", "WebFetch"]);
|
|
1731
|
-
var AGENT_TOOLS = /* @__PURE__ */ new Set(["Task", "TodoWrite"]);
|
|
1750
|
+
var AGENT_TOOLS = /* @__PURE__ */ new Set(["Task", "TodoWrite", "Skill"]);
|
|
1732
1751
|
var BASE_ALLOWED_TOOLS = [
|
|
1733
1752
|
...READ_TOOLS,
|
|
1734
1753
|
...SEARCH_TOOLS,
|
|
@@ -2355,12 +2374,8 @@ function clearStatsigCache() {
|
|
|
2355
2374
|
process.env.CLAUDE_CONFIG_DIR || path2.join(os2.homedir(), ".claude"),
|
|
2356
2375
|
"statsig"
|
|
2357
2376
|
);
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
fs.rmSync(statsigPath, { recursive: true, force: true });
|
|
2361
|
-
}
|
|
2362
|
-
} catch {
|
|
2363
|
-
}
|
|
2377
|
+
fs.rm(statsigPath, { recursive: true, force: true }, () => {
|
|
2378
|
+
});
|
|
2364
2379
|
}
|
|
2365
2380
|
|
|
2366
2381
|
// src/adapters/claude/claude-agent.ts
|
|
@@ -2422,7 +2437,7 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
|
|
|
2422
2437
|
const sessionId = uuidv7();
|
|
2423
2438
|
const permissionMode = meta?.permissionMode && TWIG_EXECUTION_MODES.includes(meta.permissionMode) ? meta.permissionMode : "default";
|
|
2424
2439
|
const mcpServers = parseMcpServers(params);
|
|
2425
|
-
|
|
2440
|
+
const mcpMetadataPromise = fetchMcpToolMetadata(mcpServers, this.logger);
|
|
2426
2441
|
const options = buildSessionOptions({
|
|
2427
2442
|
cwd: params.cwd,
|
|
2428
2443
|
mcpServers,
|
|
@@ -2456,13 +2471,14 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
|
|
|
2456
2471
|
adapter: "claude"
|
|
2457
2472
|
});
|
|
2458
2473
|
}
|
|
2459
|
-
const modelOptions = await
|
|
2474
|
+
const [modelOptions, slashCommands] = await Promise.all([
|
|
2475
|
+
this.getModelConfigOptions(),
|
|
2476
|
+
getAvailableSlashCommands(q),
|
|
2477
|
+
mcpMetadataPromise
|
|
2478
|
+
]);
|
|
2460
2479
|
session.modelId = modelOptions.currentModelId;
|
|
2461
2480
|
await this.trySetModel(q, modelOptions.currentModelId);
|
|
2462
|
-
this.sendAvailableCommandsUpdate(
|
|
2463
|
-
sessionId,
|
|
2464
|
-
await getAvailableSlashCommands(q)
|
|
2465
|
-
);
|
|
2481
|
+
this.sendAvailableCommandsUpdate(sessionId, slashCommands);
|
|
2466
2482
|
return {
|
|
2467
2483
|
sessionId,
|
|
2468
2484
|
configOptions: await this.buildConfigOptions(modelOptions)
|
|
@@ -2481,7 +2497,7 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
|
|
|
2481
2497
|
return {};
|
|
2482
2498
|
}
|
|
2483
2499
|
const mcpServers = parseMcpServers(params);
|
|
2484
|
-
|
|
2500
|
+
const mcpMetadataPromise = fetchMcpToolMetadata(mcpServers, this.logger);
|
|
2485
2501
|
const permissionMode = meta?.permissionMode && TWIG_EXECUTION_MODES.includes(meta.permissionMode) ? meta.permissionMode : "default";
|
|
2486
2502
|
const { query: q, session } = await this.initializeQuery({
|
|
2487
2503
|
cwd: params.cwd,
|
|
@@ -2495,10 +2511,11 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
|
|
|
2495
2511
|
});
|
|
2496
2512
|
session.taskRunId = meta?.taskRunId;
|
|
2497
2513
|
this.registerPersistence(sessionId, meta);
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
);
|
|
2514
|
+
const [slashCommands] = await Promise.all([
|
|
2515
|
+
getAvailableSlashCommands(q),
|
|
2516
|
+
mcpMetadataPromise
|
|
2517
|
+
]);
|
|
2518
|
+
this.sendAvailableCommandsUpdate(sessionId, slashCommands);
|
|
2502
2519
|
return {
|
|
2503
2520
|
configOptions: await this.buildConfigOptions()
|
|
2504
2521
|
};
|