@composio/experimental 0.1.0 → 0.2.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Sampark Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,190 +1,20 @@
1
1
  # `@composio/experimental`
2
2
 
3
- Experimental Composio integrations and helpers.
3
+ Experimental additions to the Composio TypeScript SDK. Features ship here before they are ready for [`@composio/core`](../core), so APIs can change or be removed between releases.
4
4
 
5
- This package currently includes a Pi provider for [`@earendil-works/pi-coding-agent`](https://www.npmjs.com/package/@earendil-works/pi-coding-agent). It lets Composio tools be passed to Pi SDK sessions as `customTools` and includes a dynamic Tool Router session toolset modeled after the Slack bot integration in `~/composio/slack-bot`.
6
-
7
- ## Install
5
+ ## Installation
8
6
 
9
7
  ```bash
10
- pnpm add @composio/core @composio/experimental @earendil-works/pi-coding-agent
11
- ```
12
-
13
- ## Static tool wrapping
14
-
15
- Use this when you already know the exact Composio tools to expose to Pi.
16
-
17
- ```ts
18
- import { Composio } from '@composio/core';
19
- import { PiProvider } from '@composio/experimental';
20
- import { createAgentSession, SessionManager } from '@earendil-works/pi-coding-agent';
21
-
22
- const provider = new PiProvider();
23
- const composio = new Composio({
24
- apiKey: process.env.COMPOSIO_API_KEY,
25
- provider,
26
- });
27
-
28
- const tools = await composio.tools.get('default', {
29
- tools: ['GITHUB_CREATE_ISSUE'],
30
- });
31
-
32
- const { session } = await createAgentSession({
33
- cwd: process.cwd(),
34
- sessionManager: SessionManager.inMemory(process.cwd()),
35
- customTools: tools,
36
- tools: ['read', 'bash', ...tools.map(tool => tool.name)],
37
- });
38
-
39
- await session.prompt('Create a GitHub issue for the failing test.');
8
+ npm install @composio/core @composio/experimental
40
9
  ```
41
10
 
42
- ## Dynamic session helpers
43
-
44
- Use this when Pi should search and execute tools dynamically inside one Tool Router session. Prefer the capability form so your app owns connection management and uses one Pi-style `hooks` object for interception/result transforms.
45
-
46
- ```ts
47
- import { Composio } from '@composio/core';
48
- import { PiProvider, createPiComposioSystemPrompt } from '@composio/experimental';
49
- import {
50
- createAgentSession,
51
- DefaultResourceLoader,
52
- getAgentDir,
53
- SessionManager,
54
- } from '@earendil-works/pi-coding-agent';
55
-
56
- const provider = new PiProvider();
57
- const composio = new Composio({ apiKey: process.env.COMPOSIO_API_KEY });
58
-
59
- const composioSession = await composio.sessions.create('slack:T123:U456', {
60
- toolkits: ['github', 'gmail'],
61
- manageConnections: true,
62
- workbench: { enable: true },
63
- });
64
-
65
- const composioTools = provider.createSessionTools({
66
- sessionId: composioSession.sessionId,
67
- search: composioSession.search.bind(composioSession),
68
- execute: composioSession.execute.bind(composioSession),
69
- callbackUrl: 'https://your-app.example.com/auth/callback',
70
- includeWorkbenchTools: true,
71
- connections: {
72
- getToolkitStates: toolkits => composioSession.toolkits({ toolkits }),
73
- authorizeToolkit: (toolkit, options) => composioSession.authorize(toolkit, options),
74
- isConnected: state => state.connection?.isActive === true,
75
- },
76
- hooks: {
77
- search: (ctx, next) => {
78
- ctx.request.toolkits = ctx.request.toolkits?.map(toolkit =>
79
- toolkit === 'slack' ? 'slackbot' : toolkit
80
- );
81
- return next();
82
- },
83
- execute: (ctx, next) => {
84
- if (ctx.request.toolSlug === 'COMPOSIO_MANAGE_CONNECTIONS') {
85
- return ctx.deny('Meta tools are blocked.');
86
- }
87
- return next();
88
- },
89
- remoteWorkbench: async (ctx, next) => {
90
- const result = await next();
91
- await auditWorkbenchRun({ code: ctx.request.code_to_execute, result });
92
- return result;
93
- },
94
- remoteBash: async (ctx, next) => {
95
- if (ctx.request.command.includes('rm -rf')) {
96
- return ctx.deny('Destructive bash commands are blocked.');
97
- }
98
- return next();
99
- },
100
- onAuthLink: async (ctx, next) => {
101
- // DM the user, store the continuation, or redact public output.
102
- await sendConnectionLinkToUser({ url: ctx.url, toolkit: ctx.toolkit });
103
- return { message: 'Connection link sent out-of-band.' };
104
- // To also send the original result/link to the model, use: return next();
105
- },
106
- },
107
- transformResult: async ({ value }) => value,
108
- });
109
-
110
- const loader = new DefaultResourceLoader({
111
- cwd: process.cwd(),
112
- agentDir: getAgentDir(),
113
- systemPromptOverride: () =>
114
- createPiComposioSystemPrompt(composioSession.sessionId, { includeWorkbenchTools: true }),
115
- });
116
- await loader.reload();
117
-
118
- const { session } = await createAgentSession({
119
- cwd: process.cwd(),
120
- resourceLoader: loader,
121
- sessionManager: SessionManager.inMemory(process.cwd()),
122
- customTools: composioTools,
123
- tools: [
124
- 'read',
125
- 'bash',
126
- 'composio_search_tools',
127
- 'composio_manage_connections',
128
- 'composio_execute_tool',
129
- 'composio_remote_workbench',
130
- 'composio_remote_bash',
131
- ],
132
- });
11
+ ## What's inside
133
12
 
134
- await session.prompt('Find my recent GitHub issues and summarize the blockers.');
135
- ```
136
-
137
- `composio_manage_connections` uses your `connections.getToolkitStates()` and `connections.authorizeToolkit()` handlers. It does **not** call `session.execute('COMPOSIO_MANAGE_CONNECTIONS', ...)` internally.
138
-
139
- The dynamic helpers are:
140
-
141
- - `composio_search_tools` — search Tool Router for exact tool slugs and schemas.
142
- - `composio_manage_connections` — check/initiate user app connections.
143
- - `composio_execute_tool` — execute exact Composio tool slugs in the session.
144
- - `composio_remote_workbench` — execute Python in the Composio remote workbench for large outputs, remote files, and session-authenticated scripting.
145
- - `composio_remote_bash` — run short bash commands in the Composio remote workbench filesystem.
146
-
147
- Workbench helpers are opt-in because the Tool Router session must be created with workbench enabled.
148
-
149
- ## Hooks
150
-
151
- `hooks` follows Pi's extension-event style with middleware semantics. Each hook receives a mutable `ctx.request`, `ctx.deny('reason')`, and a typed `next()` function. Calling `await next()` runs the default Composio behavior and returns the result before anything is sent back to the model. Return that result to pass it through, return a replacement value to control what the model sees, or skip `next()` entirely to divert/deny the operation.
152
-
153
- Available hooks:
154
-
155
- - `search(ctx, next)` — rewrite query/toolkit filters, log search results, or return custom search output.
156
- - `manageConnections(ctx, next)` — rewrite requested toolkits, force reauth, log connection state, or return custom connection output.
157
- - `execute(ctx, next)` — block/rewrite tools, route to another session/execute handler, call `ctx.manageConnections(...)`, log outputs, or return a file/workbench reference instead of inline data.
158
- - `remoteWorkbench(ctx, next)` — rewrite Python code/session metadata, audit workbench runs, or replace large outputs with file/workbench references. Calls through the generic `execute` hook when `next()` is used.
159
- - `remoteBash(ctx, next)` — rewrite/block shell commands, enforce safety policy, audit filesystem access, or replace output. Calls through the generic `execute` hook when `next()` is used.
160
- - `onAuthLink(ctx, next)` — send/redact/resume auth links out-of-band. `return next()` keeps the original model-visible result; returning another value replaces it.
161
-
162
- ## Auth link handling
163
-
164
- Embedded agents often need to keep Composio connection URLs out of the public transcript. Use `hooks.onAuthLink()` to choose whether the model sees the original result or a redacted replacement:
165
-
166
- ```ts
167
- const tools = provider.createSessionTools({
168
- search: composioSession.search.bind(composioSession),
169
- execute: composioSession.execute.bind(composioSession),
170
- connections: {
171
- getToolkitStates: toolkits => composioSession.toolkits({ toolkits }),
172
- authorizeToolkit: (toolkit, options) => composioSession.authorize(toolkit, options),
173
- },
174
- hooks: {
175
- onAuthLink: async (ctx, next) => {
176
- await sendConnectionLinkToUser({ url: ctx.url, toolkit: ctx.toolkit });
177
-
178
- if (shouldAlsoShowLinkToModel(ctx)) {
179
- return next();
180
- }
181
-
182
- return { message: 'Connection link sent out-of-band.' };
183
- },
184
- },
185
- });
186
- ```
13
+ - `PiProvider` (root export): adapts Composio tools for [`@earendil-works/pi-coding-agent`](https://www.npmjs.com/package/@earendil-works/pi-coding-agent), including dynamic session helpers that let Pi search, connect, and execute tools at runtime. See the [Pi provider docs](https://docs.composio.dev/docs/providers/pi) for the full guide.
14
+ - `EveProvider` (from `@composio/experimental/eve`): adapts Composio tools for the [eve](https://github.com/vercel/eve) agent framework, with hook-based interception and per-tool approval helpers.
15
+ - `experimental_createLocalWorkbenchSession` (from `@composio/experimental/workbench`): run a session's sandbox on your local machine instead of the hosted workbench.
187
16
 
188
- ## Status
17
+ ## Links
189
18
 
190
- Experimental. The dynamic helper names and session helper API may change.
19
+ - Pi provider docs: https://docs.composio.dev/docs/providers/pi
20
+ - Composio docs: https://docs.composio.dev
@@ -0,0 +1,67 @@
1
+ import { configure } from "safe-stable-stringify";
2
+
3
+ //#region src/auth-links.ts
4
+ const HTTPS_PREFIX = "https://";
5
+ const CONNECT_LINK_PREFIX = "https://connect.composio.dev/";
6
+ const COMPOSIO_MARKER = "composio";
7
+ const LINK_PATH_MARKER = "/link/";
8
+ const URL_BOUNDARIES = /* @__PURE__ */ new Set([
9
+ "<",
10
+ ">",
11
+ ")",
12
+ "\"",
13
+ "'"
14
+ ]);
15
+ const TRAILING_PUNCTUATION = /* @__PURE__ */ new Set([
16
+ ".",
17
+ ",",
18
+ ";",
19
+ ":",
20
+ "!",
21
+ "?"
22
+ ]);
23
+ const stringify = configure({ deterministic: false });
24
+ const stringifyUnknown = (value) => {
25
+ if (typeof value === "string") return value;
26
+ try {
27
+ const serialized = stringify(value);
28
+ if (serialized === void 0) return "";
29
+ return serialized;
30
+ } catch {
31
+ return "";
32
+ }
33
+ };
34
+ const isUrlBoundary = (character) => URL_BOUNDARIES.has(character) || character.trim().length === 0;
35
+ const trimTrailingPunctuation = (url) => {
36
+ let end = url.length;
37
+ while (end > 0 && TRAILING_PUNCTUATION.has(url[end - 1])) end -= 1;
38
+ return url.slice(0, end);
39
+ };
40
+ const classifyToken = (token, connectLinks, genericLinks) => {
41
+ const normalizedToken = token.toLowerCase();
42
+ const httpsStart = normalizedToken.indexOf(HTTPS_PREFIX);
43
+ if (httpsStart === -1) return;
44
+ const connectStart = normalizedToken.indexOf(CONNECT_LINK_PREFIX, httpsStart);
45
+ if (connectStart !== -1 && connectStart + 29 < token.length) connectLinks.add(trimTrailingPunctuation(token.slice(connectStart)));
46
+ const composioStart = normalizedToken.indexOf(COMPOSIO_MARKER, httpsStart + 8);
47
+ if (composioStart === -1) return;
48
+ const linkPathStart = normalizedToken.indexOf(LINK_PATH_MARKER, composioStart + 8);
49
+ if (linkPathStart === -1 || linkPathStart + 6 >= token.length) return;
50
+ genericLinks.add(trimTrailingPunctuation(token.slice(httpsStart)));
51
+ };
52
+ const extractComposioConnectLinks = (value) => {
53
+ const text = stringifyUnknown(value);
54
+ const connectLinks = /* @__PURE__ */ new Set();
55
+ const genericLinks = /* @__PURE__ */ new Set();
56
+ let tokenStart = 0;
57
+ for (let index = 0; index <= text.length; index += 1) {
58
+ const character = text[index];
59
+ if (character !== void 0 && !isUrlBoundary(character)) continue;
60
+ if (index > tokenStart) classifyToken(text.slice(tokenStart, index), connectLinks, genericLinks);
61
+ tokenStart = index + 1;
62
+ }
63
+ return Array.from(/* @__PURE__ */ new Set([...connectLinks, ...genericLinks]));
64
+ };
65
+
66
+ //#endregion
67
+ export { extractComposioConnectLinks as t };
@@ -0,0 +1,67 @@
1
+ import { BaseAgenticProvider, ExecuteToolFn, McpServerGetResponse, McpUrlResponse, Tool, ToolExecuteResponse } from "@composio/core";
2
+ import { DynamicResolveContext, NeedsApprovalContext, ToolContext, ToolDefinition } from "eve/tools";
3
+
4
+ //#region src/eve/hooks.d.ts
5
+ type MaybePromise<T> = T | Promise<T>;
6
+ type Next = () => Promise<ToolExecuteResponse>;
7
+ interface EveHookControls {
8
+ deny(reason: string): ToolExecuteResponse;
9
+ }
10
+ interface EveHookContext extends EveHookControls {
11
+ request: {
12
+ slug: string;
13
+ args: Record<string, unknown>;
14
+ };
15
+ readonly context: {
16
+ readonly slug: string;
17
+ readonly eve: ToolContext;
18
+ };
19
+ }
20
+ interface EveAuthLinkContext extends EveHookControls {
21
+ readonly url: string;
22
+ readonly result: ToolExecuteResponse;
23
+ readonly context: {
24
+ readonly slug: string;
25
+ readonly eve: ToolContext;
26
+ };
27
+ }
28
+ type EveHook = (ctx: EveHookContext, next: Next) => MaybePromise<ToolExecuteResponse | void>;
29
+ type EveAuthLinkHook = (ctx: EveAuthLinkContext, next: Next) => MaybePromise<ToolExecuteResponse | void>;
30
+ interface EveProviderHooks {
31
+ search?: EveHook;
32
+ manageConnections?: EveHook;
33
+ execute?: EveHook;
34
+ remoteWorkbench?: EveHook;
35
+ remoteBash?: EveHook;
36
+ onAuthLink?: EveAuthLinkHook;
37
+ }
38
+ declare const denyEveToolCall: (reason: string) => ToolExecuteResponse;
39
+ //#endregion
40
+ //#region src/eve/provider.d.ts
41
+ type EveTool = ToolDefinition<Record<string, unknown>, ToolExecuteResponse>;
42
+ type EveToolCollection = Record<string, EveTool>;
43
+ type EveNeedsApproval = (tool: Tool, context: NeedsApprovalContext<Record<string, unknown>>) => boolean;
44
+ /** Require approval for direct calls and matching entries inside a multi-execute call. */
45
+ declare const requireApprovalForTools: (...toolSlugs: string[]) => EveNeedsApproval;
46
+ interface EveProviderOptions {
47
+ strict?: boolean;
48
+ hooks?: EveProviderHooks;
49
+ needsApproval?: EveNeedsApproval;
50
+ }
51
+ declare class EveProvider extends BaseAgenticProvider<EveToolCollection, EveTool, McpServerGetResponse> {
52
+ private readonly options;
53
+ readonly name = "eve";
54
+ constructor(options?: EveProviderOptions);
55
+ wrapTool(tool: Tool, executeTool: ExecuteToolFn): EveTool;
56
+ wrapTools(tools: Tool[], executeTool: ExecuteToolFn): EveToolCollection;
57
+ wrapMcpServerResponse(data: McpUrlResponse): McpServerGetResponse;
58
+ }
59
+ //#endregion
60
+ //#region src/eve/resolver.d.ts
61
+ type EveSession = {
62
+ tools: () => Promise<EveToolCollection>;
63
+ };
64
+ type EveSessionSource<S extends EveSession> = S | Promise<S> | ((context: DynamicResolveContext) => S | Promise<S>);
65
+ declare function defineComposioTools<S extends EveSession>(source: EveSessionSource<S>): import("eve/tools").DynamicSentinel;
66
+ //#endregion
67
+ export { type EveAuthLinkContext, type EveAuthLinkHook, type EveHook, type EveHookContext, type EveHookControls, type EveNeedsApproval, EveProvider, type EveProviderHooks, type EveProviderOptions, type EveTool, type EveToolCollection, defineComposioTools, denyEveToolCall, requireApprovalForTools };
@@ -0,0 +1,157 @@
1
+ import { t as extractComposioConnectLinks } from "../auth-links-BsKdD1dc.mjs";
2
+ import { BaseAgenticProvider, normalizeToolArguments, removeNonRequiredProperties } from "@composio/core";
3
+ import { defineDynamic, defineTool } from "eve/tools";
4
+
5
+ //#region src/eve/hooks.ts
6
+ const denyEveToolCall = (reason) => ({
7
+ data: {},
8
+ error: reason,
9
+ successful: false
10
+ });
11
+ const HOOK_BY_SLUG = {
12
+ COMPOSIO_SEARCH_TOOLS: "search",
13
+ COMPOSIO_MANAGE_CONNECTIONS: "manageConnections",
14
+ COMPOSIO_MULTI_EXECUTE_TOOL: "execute",
15
+ COMPOSIO_EXECUTE_TOOL: "execute",
16
+ COMPOSIO_REMOTE_WORKBENCH: "remoteWorkbench",
17
+ COMPOSIO_REMOTE_BASH_TOOL: "remoteBash"
18
+ };
19
+ const hookForSlug = (hooks, slug) => {
20
+ const hookName = HOOK_BY_SLUG[slug];
21
+ if (!hookName) return void 0;
22
+ return hooks[hookName];
23
+ };
24
+ const extractAuthLinks = (result) => {
25
+ const links = new Set(extractComposioConnectLinks(result.data));
26
+ for (const link of extractComposioConnectLinks(result.error)) links.add(link);
27
+ return Array.from(links);
28
+ };
29
+ const runHook = async (hook, ctx, getDefault) => {
30
+ if (!hook) return getDefault();
31
+ let pending;
32
+ const next = () => {
33
+ if (!pending) pending = getDefault();
34
+ return pending;
35
+ };
36
+ const hookResult = await hook(ctx, next);
37
+ if (hookResult !== void 0 && hookResult !== null) return hookResult;
38
+ if (pending) return pending;
39
+ return next();
40
+ };
41
+ async function applyHooks(hooks, slug, args, executeTool, eveContext) {
42
+ const context = {
43
+ slug,
44
+ eve: eveContext
45
+ };
46
+ const ctx = {
47
+ request: {
48
+ slug,
49
+ args
50
+ },
51
+ context,
52
+ deny: denyEveToolCall
53
+ };
54
+ const hook = hookForSlug(hooks, slug);
55
+ const result = await runHook(hook, ctx, () => executeTool(ctx.request.slug, ctx.request.args));
56
+ const { onAuthLink } = hooks;
57
+ if (!onAuthLink) return result;
58
+ let current = result;
59
+ for (const url of extractAuthLinks(result)) {
60
+ const previous = current;
61
+ current = await runHook(onAuthLink, {
62
+ url,
63
+ result: previous,
64
+ context,
65
+ deny: denyEveToolCall
66
+ }, async () => previous);
67
+ }
68
+ return current;
69
+ }
70
+
71
+ //#endregion
72
+ //#region src/eve/provider.ts
73
+ const MULTI_EXECUTE_TOOL_SLUG = "COMPOSIO_MULTI_EXECUTE_TOOL";
74
+ const toEveInputSchema = (tool, strict) => {
75
+ const params = tool.inputParameters;
76
+ if (!params) return {
77
+ type: "object",
78
+ properties: {}
79
+ };
80
+ if (!strict || params.type !== "object") return params;
81
+ return removeNonRequiredProperties({
82
+ ...params,
83
+ properties: { ...params.properties }
84
+ });
85
+ };
86
+ const toEveApprovalPolicy = (tool, approvalPolicy) => {
87
+ if (!approvalPolicy) return void 0;
88
+ return (context) => approvalPolicy(tool, context);
89
+ };
90
+ const isProtectedToolItem = (item, protectedSlugs) => {
91
+ if (typeof item !== "object" || item === null) return false;
92
+ const toolSlug = item.tool_slug;
93
+ if (typeof toolSlug !== "string") return false;
94
+ return protectedSlugs.has(toolSlug.toUpperCase());
95
+ };
96
+ /** Require approval for direct calls and matching entries inside a multi-execute call. */
97
+ const requireApprovalForTools = (...toolSlugs) => {
98
+ const protectedSlugs = new Set(toolSlugs.map((slug) => slug.toUpperCase()));
99
+ return (tool, context) => {
100
+ const normalizedToolSlug = tool.slug.toUpperCase();
101
+ if (protectedSlugs.has(normalizedToolSlug)) return true;
102
+ if (normalizedToolSlug !== MULTI_EXECUTE_TOOL_SLUG) return false;
103
+ const requestedTools = context.toolInput?.tools;
104
+ if (!Array.isArray(requestedTools)) return false;
105
+ return requestedTools.some((item) => isProtectedToolItem(item, protectedSlugs));
106
+ };
107
+ };
108
+ var EveProvider = class extends BaseAgenticProvider {
109
+ options;
110
+ name = "eve";
111
+ constructor(options = {}) {
112
+ super();
113
+ this.options = options;
114
+ }
115
+ wrapTool(tool, executeTool) {
116
+ const inputSchema = toEveInputSchema(tool, this.options.strict);
117
+ const needsApproval = toEveApprovalPolicy(tool, this.options.needsApproval);
118
+ return defineTool({
119
+ description: tool.description ?? tool.name,
120
+ inputSchema,
121
+ needsApproval,
122
+ execute: (input, context) => applyHooks(this.options.hooks ?? {}, tool.slug, normalizeToolArguments(input, tool.slug), executeTool, context)
123
+ });
124
+ }
125
+ wrapTools(tools, executeTool) {
126
+ return Object.fromEntries(tools.map((tool) => [tool.slug, this.wrapTool(tool, executeTool)]));
127
+ }
128
+ wrapMcpServerResponse(data) {
129
+ return data.map((item) => ({
130
+ url: new URL(item.url),
131
+ name: item.name
132
+ }));
133
+ }
134
+ };
135
+
136
+ //#endregion
137
+ //#region src/eve/resolver.ts
138
+ function defineComposioTools(source) {
139
+ const cache = /* @__PURE__ */ new WeakMap();
140
+ const resolveTools = async (context) => {
141
+ const session = await (typeof source === "function" ? source(context) : source);
142
+ const cached = cache.get(session);
143
+ if (cached) return cached;
144
+ const pending = session.tools();
145
+ cache.set(session, pending);
146
+ try {
147
+ return await pending;
148
+ } catch (error) {
149
+ if (cache.get(session) === pending) cache.delete(session);
150
+ throw error;
151
+ }
152
+ };
153
+ return defineDynamic({ events: { "step.started": (_event, context) => resolveTools(context) } });
154
+ }
155
+
156
+ //#endregion
157
+ export { EveProvider, defineComposioTools, denyEveToolCall, requireApprovalForTools };
package/dist/index.d.mts CHANGED
@@ -228,7 +228,7 @@ type PiConnectionManagementResult<TState = unknown, TAuthorizeResult = unknown>
228
228
  error: null;
229
229
  };
230
230
  //#endregion
231
- //#region src/pi/auth-links.d.ts
231
+ //#region src/auth-links.d.ts
232
232
  declare const extractComposioConnectLinks: (value: unknown) => string[];
233
233
  //#endregion
234
234
  //#region src/pi/provider.d.ts
package/dist/index.mjs CHANGED
@@ -1,3 +1,4 @@
1
+ import { t as extractComposioConnectLinks } from "./auth-links-BsKdD1dc.mjs";
1
2
  import { BaseAgenticProvider, normalizeToolArguments } from "@composio/core";
2
3
  import { defineTool } from "@earendil-works/pi-coding-agent";
3
4
  import { Type } from "typebox";
@@ -60,20 +61,6 @@ const toPiErrorResult = (error, formatter, details = {}) => {
60
61
 
61
62
  //#endregion
62
63
  //#region src/pi/auth-links.ts
63
- const extractComposioConnectLinks = (value) => {
64
- const text = stringifyUnknown(value);
65
- const connectLinks = text.match(/https:\/\/connect\.composio\.dev\/[^\s<>)"']+/gi) ?? [];
66
- const genericLinks = text.match(/https:\/\/[^\s<>)"']*composio[^\s<>)"']*\/link\/[^\s<>)"']+/gi) ?? [];
67
- return [...new Set([...connectLinks, ...genericLinks].map((url) => url.replace(/[.,;:!?]+$/g, "")))];
68
- };
69
- const stringifyUnknown = (value) => {
70
- if (typeof value === "string") return value;
71
- try {
72
- return JSON.stringify(value);
73
- } catch {
74
- return String(value);
75
- }
76
- };
77
64
  const applyAuthLinkHandlers = async (capabilities, value, context) => {
78
65
  const links = extractComposioConnectLinks(value);
79
66
  const run = async (index, currentValue) => {
@@ -535,15 +522,16 @@ const createPiSessionTools = (input, providerOptions, options = {}) => {
535
522
  };
536
523
  const authLinks = [];
537
524
  const details = {};
525
+ const value = await runHook(capabilities.hooks?.remoteWorkbench, hookContext, async () => {
526
+ const executed = await executeWithPolicy(toolCallId, names.remoteWorkbench, params, "COMPOSIO_REMOTE_WORKBENCH", hookContext.request);
527
+ authLinks.push(...executed.authLinks);
528
+ details.denied = executed.denied;
529
+ hookContext.context = executed.context;
530
+ return executed.value;
531
+ });
538
532
  return toPiResult(await maybeTransform(capabilities, {
539
533
  tool: "remoteWorkbench",
540
- value: await runHook(capabilities.hooks?.remoteWorkbench, hookContext, async () => {
541
- const executed = await executeWithPolicy(toolCallId, names.remoteWorkbench, params, "COMPOSIO_REMOTE_WORKBENCH", hookContext.request);
542
- authLinks.push(...executed.authLinks);
543
- details.denied = executed.denied;
544
- hookContext.context = executed.context;
545
- return executed.value;
546
- }),
534
+ value,
547
535
  context: hookContext.context
548
536
  }), formatter, {
549
537
  slug: hookContext.context.toolSlug,
@@ -584,15 +572,16 @@ const createPiSessionTools = (input, providerOptions, options = {}) => {
584
572
  };
585
573
  const authLinks = [];
586
574
  const details = {};
575
+ const value = await runHook(capabilities.hooks?.remoteBash, hookContext, async () => {
576
+ const executed = await executeWithPolicy(toolCallId, names.remoteBash, params, "COMPOSIO_REMOTE_BASH_TOOL", hookContext.request);
577
+ authLinks.push(...executed.authLinks);
578
+ details.denied = executed.denied;
579
+ hookContext.context = executed.context;
580
+ return executed.value;
581
+ });
587
582
  return toPiResult(await maybeTransform(capabilities, {
588
583
  tool: "remoteBash",
589
- value: await runHook(capabilities.hooks?.remoteBash, hookContext, async () => {
590
- const executed = await executeWithPolicy(toolCallId, names.remoteBash, params, "COMPOSIO_REMOTE_BASH_TOOL", hookContext.request);
591
- authLinks.push(...executed.authLinks);
592
- details.denied = executed.denied;
593
- hookContext.context = executed.context;
594
- return executed.value;
595
- }),
584
+ value,
596
585
  context: hookContext.context
597
586
  }), formatter, {
598
587
  slug: hookContext.context.toolSlug,
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@composio/experimental",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Experimental Composio integrations and helpers",
5
- "main": "src/index.ts",
5
+ "main": "dist/index.mjs",
6
6
  "type": "module",
7
7
  "repository": {
8
8
  "type": "git",
@@ -12,11 +12,9 @@
12
12
  "bugs": {
13
13
  "url": "https://github.com/ComposioHQ/composio/issues"
14
14
  },
15
- "homepage": "https://github.com/ComposioHQ/composio/tree/main/ts/packages/experimental#readme",
15
+ "homepage": "https://github.com/ComposioHQ/composio/tree/next/ts/packages/experimental#readme",
16
16
  "publishConfig": {
17
- "access": "public",
18
- "main": "dist/index.mjs",
19
- "types": "dist/index.d.mts"
17
+ "access": "public"
20
18
  },
21
19
  "exports": {
22
20
  ".": {
@@ -30,43 +28,60 @@
30
28
  "./workbench": {
31
29
  "types": "./dist/workbench/index.d.mts",
32
30
  "default": "./dist/workbench/index.mjs"
31
+ },
32
+ "./eve": {
33
+ "types": "./dist/eve/index.d.mts",
34
+ "default": "./dist/eve/index.mjs"
33
35
  }
34
36
  },
35
37
  "files": [
36
38
  "README.md",
37
39
  "dist"
38
40
  ],
39
- "scripts": {
40
- "clean": "git clean -xdf node_modules",
41
- "build:python-helpers": "tsx scripts/build-python-helpers.ts",
42
- "check:python-helpers": "tsx scripts/build-python-helpers.ts && git diff --exit-code -- src/workbench/python-helpers.generated.ts",
43
- "build": "pnpm run build:python-helpers && pnpm exec tsdown",
44
- "test": "vitest run",
45
- "typecheck": "tsc --noEmit -p tsconfig.json"
46
- },
47
41
  "keywords": [
48
42
  "composio",
49
43
  "experimental",
50
44
  "pi",
51
45
  "coding-agent",
46
+ "eve",
52
47
  "tools",
53
48
  "agent"
54
49
  ],
55
50
  "author": "",
56
51
  "license": "ISC",
57
52
  "dependencies": {
58
- "typebox": "1.1.38"
53
+ "safe-stable-stringify": "^2.5.0",
54
+ "typebox": "1.3.3"
59
55
  },
60
56
  "peerDependencies": {
61
57
  "@composio/core": ">=0.10.0 <1.0.0",
62
- "@earendil-works/pi-coding-agent": ">=0.79.0 <1.0.0"
58
+ "@earendil-works/pi-coding-agent": ">=0.79.0 <1.0.0",
59
+ "eve": ">=0.12.0 <1.0.0"
60
+ },
61
+ "peerDependenciesMeta": {
62
+ "@earendil-works/pi-coding-agent": {
63
+ "optional": true
64
+ },
65
+ "eve": {
66
+ "optional": true
67
+ }
63
68
  },
64
69
  "devDependencies": {
65
- "@composio/core": "workspace:*",
66
- "@earendil-works/pi-coding-agent": "0.79.8",
67
- "tsdown": "catalog:",
68
- "tsx": "catalog:",
69
- "typescript": "catalog:",
70
- "vitest": "catalog:"
71
- }
72
- }
70
+ "@earendil-works/pi-coding-agent": "^0.80.3",
71
+ "eve": "^0.12.0",
72
+ "tsdown": "^0.22.3",
73
+ "tsx": "^4.23.0",
74
+ "typescript": "^6.0.3",
75
+ "vitest": "^4.1.10",
76
+ "@composio/core": "0.14.0"
77
+ },
78
+ "scripts": {
79
+ "clean": "git clean -xdf node_modules",
80
+ "build:python-helpers": "tsx scripts/build-python-helpers.ts",
81
+ "check:python-helpers": "tsx scripts/build-python-helpers.ts && git diff --exit-code -- src/workbench/python-helpers.generated.ts",
82
+ "build": "pnpm run build:python-helpers && pnpm exec tsdown",
83
+ "test": "vitest run",
84
+ "typecheck": "tsc --noEmit -p tsconfig.json"
85
+ },
86
+ "types": "dist/index.d.mts"
87
+ }
package/src/index.ts DELETED
@@ -1,10 +0,0 @@
1
- /**
2
- * Experimental Composio integrations and helpers.
3
- *
4
- * Currently includes the experimental Pi provider for
5
- * @earendil-works/pi-coding-agent.
6
- *
7
- * @packageDocumentation
8
- * @module experimental
9
- */
10
- export * from './pi/index';