gitclaw 0.3.0 → 0.4.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.
Files changed (45) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +54 -28
  3. package/dist/composio/adapter.d.ts +26 -0
  4. package/dist/composio/adapter.js +92 -0
  5. package/dist/composio/client.d.ts +39 -0
  6. package/dist/composio/client.js +170 -0
  7. package/dist/composio/index.d.ts +2 -0
  8. package/dist/composio/index.js +2 -0
  9. package/dist/context.d.ts +20 -0
  10. package/dist/context.js +211 -0
  11. package/dist/exports.d.ts +2 -0
  12. package/dist/exports.js +1 -0
  13. package/dist/index.js +99 -7
  14. package/dist/learning/reinforcement.d.ts +11 -0
  15. package/dist/learning/reinforcement.js +91 -0
  16. package/dist/loader.js +34 -1
  17. package/dist/sdk.js +5 -1
  18. package/dist/skills.d.ts +5 -0
  19. package/dist/skills.js +58 -7
  20. package/dist/tools/capture-photo.d.ts +3 -0
  21. package/dist/tools/capture-photo.js +91 -0
  22. package/dist/tools/index.d.ts +2 -1
  23. package/dist/tools/index.js +12 -2
  24. package/dist/tools/read.js +4 -0
  25. package/dist/tools/shared.d.ts +20 -0
  26. package/dist/tools/shared.js +24 -0
  27. package/dist/tools/skill-learner.d.ts +3 -0
  28. package/dist/tools/skill-learner.js +358 -0
  29. package/dist/tools/task-tracker.d.ts +20 -0
  30. package/dist/tools/task-tracker.js +275 -0
  31. package/dist/tools/write.js +4 -0
  32. package/dist/voice/adapter.d.ts +97 -0
  33. package/dist/voice/adapter.js +30 -0
  34. package/dist/voice/chat-history.d.ts +8 -0
  35. package/dist/voice/chat-history.js +121 -0
  36. package/dist/voice/gemini-live.d.ts +20 -0
  37. package/dist/voice/gemini-live.js +279 -0
  38. package/dist/voice/index.d.ts +4 -0
  39. package/dist/voice/index.js +3 -0
  40. package/dist/voice/openai-realtime.d.ts +27 -0
  41. package/dist/voice/openai-realtime.js +291 -0
  42. package/dist/voice/server.d.ts +2 -0
  43. package/dist/voice/server.js +2319 -0
  44. package/dist/voice/ui.html +2556 -0
  45. package/package.json +21 -7
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 GitClaw Contributors
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,3 +1,7 @@
1
+ <p align="center">
2
+ <img src="./gitclaw-logo.png" alt="GitClaw Logo" width="200" />
3
+ </p>
4
+
1
5
  <p align="center">
2
6
  <img src="https://img.shields.io/npm/v/gitclaw?style=flat-square&color=blue" alt="npm version" />
3
7
  <img src="https://img.shields.io/badge/node-%3E%3D20-brightgreen?style=flat-square" alt="node version" />
@@ -37,49 +41,57 @@ Most agent frameworks treat configuration as code scattered across your applicat
37
41
 
38
42
  Fork an agent. Branch a personality. `git log` your agent's memory. Diff its rules. This is **agents as repos**.
39
43
 
40
- ## Quick Start
41
-
42
- ### CLI
44
+ ## Install
43
45
 
44
46
  ```bash
45
47
  npm install -g gitclaw
48
+ ```
46
49
 
47
- # Set your API key
48
- export OPENAI_API_KEY="sk-..."
49
- # or: export ANTHROPIC_API_KEY="sk-ant-..."
50
+ ## Quick Start
51
+
52
+ **Run your first agent in one line:**
50
53
 
51
- # Point gitclaw at any directory — it handles everything
52
- gitclaw --dir ~/my-project
54
+ ```bash
55
+ export OPENAI_API_KEY="sk-..."
56
+ gitclaw --dir ~/my-project "Explain this project and suggest improvements"
53
57
  ```
54
58
 
55
- That's it. Gitclaw auto-scaffolds everything on first run:
56
- - `git init` if not already a repo
57
- - Creates `agent.yaml`, `SOUL.md`, `memory/MEMORY.md`
58
- - Commits the scaffold
59
- - Drops you into the REPL
59
+ That's it. Gitclaw auto-scaffolds everything on first run — `agent.yaml`, `SOUL.md`, `memory/` — and drops you into the agent.
60
60
 
61
- ```
62
- ? Repository path (. for current dir): ~/my-project
63
- Initializing git repository...
64
- Created agent.yaml (model: openai:gpt-4o-mini)
65
- my-project v0.1.0
66
- Model: openai:gpt-4o-mini
67
- Tools: cli, read, write, memory
68
- → List all files and explain the project
61
+ ### Local Repo Mode
62
+
63
+ Clone a GitHub repo, run an agent on it, auto-commit and push to a session branch:
64
+
65
+ ```bash
66
+ gitclaw --repo https://github.com/org/repo --pat ghp_xxx "Fix the login bug"
69
67
  ```
70
68
 
71
- Or run without `--dir` and it will ask you interactively:
69
+ Resume an existing session:
72
70
 
73
71
  ```bash
74
- gitclaw
72
+ gitclaw --repo https://github.com/org/repo --pat ghp_xxx --session gitclaw/session-a1b2c3d4 "Continue"
75
73
  ```
76
74
 
77
- Single-shot mode:
75
+ Token can come from env instead of `--pat`:
78
76
 
79
77
  ```bash
80
- gitclaw --dir ~/my-project -p "Create a hello world script"
78
+ export GITHUB_TOKEN=ghp_xxx
79
+ gitclaw --repo https://github.com/org/repo "Add unit tests"
81
80
  ```
82
81
 
82
+ ### CLI Options
83
+
84
+ | Flag | Short | Description |
85
+ |---|---|---|
86
+ | `--dir <path>` | `-d` | Agent directory (default: cwd) |
87
+ | `--repo <url>` | `-r` | GitHub repo URL to clone and work on |
88
+ | `--pat <token>` | | GitHub PAT (or set `GITHUB_TOKEN` / `GIT_TOKEN`) |
89
+ | `--session <branch>` | | Resume an existing session branch |
90
+ | `--model <provider:model>` | `-m` | Override model (e.g. `anthropic:claude-sonnet-4-5-20250929`) |
91
+ | `--sandbox` | `-s` | Run in sandbox VM |
92
+ | `--prompt <text>` | `-p` | Single-shot prompt (skip REPL) |
93
+ | `--env <name>` | `-e` | Environment config |
94
+
83
95
  ### SDK
84
96
 
85
97
  ```bash
@@ -87,7 +99,7 @@ npm install gitclaw
87
99
  ```
88
100
 
89
101
  ```typescript
90
- import { query, tool } from "gitclaw";
102
+ import { query } from "gitclaw";
91
103
 
92
104
  // Simple query
93
105
  for await (const msg of query({
@@ -98,6 +110,18 @@ for await (const msg of query({
98
110
  if (msg.type === "delta") process.stdout.write(msg.content);
99
111
  if (msg.type === "assistant") console.log("\n\nDone.");
100
112
  }
113
+
114
+ // Local repo mode via SDK
115
+ for await (const msg of query({
116
+ prompt: "Fix the login bug",
117
+ model: "openai:gpt-4o-mini",
118
+ repo: {
119
+ url: "https://github.com/org/repo",
120
+ token: process.env.GITHUB_TOKEN!,
121
+ },
122
+ })) {
123
+ if (msg.type === "delta") process.stdout.write(msg.content);
124
+ }
101
125
  ```
102
126
 
103
127
  ## SDK
@@ -209,6 +233,8 @@ for await (const msg of query({
209
233
  | `replaceBuiltinTools` | `boolean` | Skip cli/read/write/memory |
210
234
  | `allowedTools` | `string[]` | Tool name allowlist |
211
235
  | `disallowedTools` | `string[]` | Tool name denylist |
236
+ | `repo` | `LocalRepoOptions` | Clone a GitHub repo and work on a session branch |
237
+ | `sandbox` | `SandboxOptions \| boolean` | Run in sandbox VM (mutually exclusive with `repo`) |
212
238
  | `hooks` | `GCHooks` | Programmatic lifecycle hooks |
213
239
  | `maxTurns` | `number` | Max agent turns |
214
240
  | `abortController` | `AbortController` | Cancellation signal |
@@ -433,8 +459,8 @@ Audit logs are written to `.gitagent/audit.jsonl` with full tool invocation trac
433
459
 
434
460
  ## Contributing
435
461
 
436
- Contributions are welcome! Please open an issue or submit a pull request.
462
+ Contributions are welcome! Please see [CONTRIBUTING.md](./CONTRIBUTING.md) for guidelines.
437
463
 
438
464
  ## License
439
465
 
440
- MIT
466
+ This project is licensed under the [MIT License](./LICENSE).
@@ -0,0 +1,26 @@
1
+ import type { GCToolDefinition } from "../sdk-types.js";
2
+ import { type ComposioToolkit, type ComposioConnection } from "./client.js";
3
+ interface ComposioAdapterOptions {
4
+ apiKey: string;
5
+ userId?: string;
6
+ }
7
+ export declare class ComposioAdapter {
8
+ private client;
9
+ private userId;
10
+ private cachedTools;
11
+ private cacheExpiry;
12
+ private static CACHE_TTL;
13
+ constructor(opts: ComposioAdapterOptions);
14
+ getTools(): Promise<GCToolDefinition[]>;
15
+ getToolsForQuery(query: string, limit?: number): Promise<GCToolDefinition[]>;
16
+ getConnectedToolkitSlugs(): Promise<string[]>;
17
+ getToolkits(): Promise<ComposioToolkit[]>;
18
+ connect(toolkit: string, redirectUrl?: string): Promise<{
19
+ connectionId: string;
20
+ redirectUrl: string;
21
+ }>;
22
+ getConnections(): Promise<ComposioConnection[]>;
23
+ disconnect(connectionId: string): Promise<void>;
24
+ private toGCTool;
25
+ }
26
+ export {};
@@ -0,0 +1,92 @@
1
+ // Converts Composio tools into GCToolDefinition[] for injection into query()
2
+ import { ComposioClient } from "./client.js";
3
+ export class ComposioAdapter {
4
+ client;
5
+ userId;
6
+ cachedTools = null;
7
+ cacheExpiry = 0;
8
+ static CACHE_TTL = 30_000; // 30s
9
+ constructor(opts) {
10
+ this.client = new ComposioClient(opts.apiKey);
11
+ this.userId = opts.userId ?? "default";
12
+ }
13
+ // Core — returns all tools for connected toolkits (cached)
14
+ async getTools() {
15
+ const now = Date.now();
16
+ if (this.cachedTools && now < this.cacheExpiry)
17
+ return this.cachedTools;
18
+ const connections = await this.client.listConnections(this.userId);
19
+ if (connections.length === 0)
20
+ return [];
21
+ // Deduplicate toolkit slugs
22
+ const slugs = [...new Set(connections.map((c) => c.toolkitSlug))];
23
+ // Fetch tools for each connected toolkit in parallel
24
+ const toolsBySlug = await Promise.all(slugs.map((slug) => this.client.listTools(slug).catch(() => [])));
25
+ const tools = [];
26
+ for (const toolGroup of toolsBySlug) {
27
+ for (const t of toolGroup) {
28
+ tools.push(this.toGCTool(t));
29
+ }
30
+ }
31
+ this.cachedTools = tools;
32
+ this.cacheExpiry = now + ComposioAdapter.CACHE_TTL;
33
+ return tools;
34
+ }
35
+ // Dynamically fetch only the relevant tools for a user query (semantic search)
36
+ async getToolsForQuery(query, limit = 15) {
37
+ const connections = await this.client.listConnections(this.userId);
38
+ if (connections.length === 0)
39
+ return [];
40
+ const slugs = [...new Set(connections.map((c) => c.toolkitSlug))];
41
+ const tools = await this.client.searchTools(query, slugs, limit);
42
+ // Sort: direct-action tools first (SEND, CREATE, LIST), drafts last
43
+ tools.sort((a, b) => {
44
+ const aIsDraft = a.slug.includes("DRAFT");
45
+ const bIsDraft = b.slug.includes("DRAFT");
46
+ if (aIsDraft !== bIsDraft)
47
+ return aIsDraft ? 1 : -1;
48
+ return 0;
49
+ });
50
+ return tools.map((t) => this.toGCTool(t));
51
+ }
52
+ // Returns deduplicated slugs of all connected toolkits
53
+ async getConnectedToolkitSlugs() {
54
+ const connections = await this.client.listConnections(this.userId);
55
+ return [...new Set(connections.map((c) => c.toolkitSlug))];
56
+ }
57
+ // Management endpoints — proxied for server routes
58
+ async getToolkits() {
59
+ return this.client.listToolkits(this.userId);
60
+ }
61
+ async connect(toolkit, redirectUrl) {
62
+ return this.client.initiateConnection(toolkit, this.userId, redirectUrl);
63
+ }
64
+ async getConnections() {
65
+ return this.client.listConnections(this.userId);
66
+ }
67
+ async disconnect(connectionId) {
68
+ await this.client.deleteConnection(connectionId);
69
+ // Invalidate cache so tools refresh on next query
70
+ this.cachedTools = null;
71
+ }
72
+ // ── Private ────────────────────────────────────────────────────────
73
+ toGCTool(t) {
74
+ const safeName = `composio_${t.toolkitSlug}_${t.slug}`.replace(/[^a-zA-Z0-9_]/g, "_");
75
+ let description = `[Composio/${t.toolkitSlug}] ${t.description}`;
76
+ if (t.slug.includes("SEND_EMAIL")) {
77
+ description += " — USE THIS to send emails directly.";
78
+ }
79
+ else if (t.slug.includes("CREATE_EMAIL_DRAFT")) {
80
+ description += " — Only use when the user explicitly asks for a draft.";
81
+ }
82
+ return {
83
+ name: safeName,
84
+ description,
85
+ inputSchema: t.parameters,
86
+ handler: async (args) => {
87
+ const result = await this.client.executeTool(t.slug, this.userId, args);
88
+ return typeof result === "string" ? result : JSON.stringify(result);
89
+ },
90
+ };
91
+ }
92
+ }
@@ -0,0 +1,39 @@
1
+ export interface ComposioToolkit {
2
+ slug: string;
3
+ name: string;
4
+ description: string;
5
+ logo: string;
6
+ authSchemes: string[];
7
+ noAuth: boolean;
8
+ connected: boolean;
9
+ }
10
+ export interface ComposioConnection {
11
+ id: string;
12
+ toolkitSlug: string;
13
+ status: string;
14
+ createdAt: string;
15
+ }
16
+ export interface ComposioTool {
17
+ name: string;
18
+ slug: string;
19
+ description: string;
20
+ toolkitSlug: string;
21
+ parameters: Record<string, any>;
22
+ }
23
+ export declare class ComposioClient {
24
+ private apiKey;
25
+ private authConfigCache;
26
+ constructor(apiKey: string);
27
+ listToolkits(userId?: string): Promise<ComposioToolkit[]>;
28
+ searchTools(query: string, toolkitSlugs?: string[], limit?: number): Promise<ComposioTool[]>;
29
+ listTools(toolkitSlug: string): Promise<ComposioTool[]>;
30
+ getOrCreateAuthConfig(toolkitSlug: string): Promise<string>;
31
+ initiateConnection(toolkitSlug: string, userId: string, redirectUrl?: string): Promise<{
32
+ connectionId: string;
33
+ redirectUrl: string;
34
+ }>;
35
+ listConnections(userId: string): Promise<ComposioConnection[]>;
36
+ deleteConnection(id: string): Promise<void>;
37
+ executeTool(toolSlug: string, userId: string, params: Record<string, any>, connectedAccountId?: string): Promise<any>;
38
+ private request;
39
+ }
@@ -0,0 +1,170 @@
1
+ // Composio REST API v3 client — zero dependencies, uses native fetch()
2
+ const BASE_URL = "https://backend.composio.dev/api/v3";
3
+ // ── Client ───────────────────────────────────────────────────────────
4
+ export class ComposioClient {
5
+ apiKey;
6
+ // Cache auth config IDs so we don't recreate them every connect
7
+ authConfigCache = new Map();
8
+ constructor(apiKey) {
9
+ this.apiKey = apiKey;
10
+ }
11
+ // List available toolkits, optionally merging connection status for a user
12
+ async listToolkits(userId) {
13
+ const resp = await this.request("GET", "/toolkits");
14
+ const toolkits = Array.isArray(resp) ? resp : (resp.items ?? resp.toolkits ?? []);
15
+ let connectedSlugs = new Set();
16
+ if (userId) {
17
+ try {
18
+ const conns = await this.listConnections(userId);
19
+ connectedSlugs = new Set(conns.map((c) => c.toolkitSlug));
20
+ }
21
+ catch {
22
+ // If connections fail, just show all as disconnected
23
+ }
24
+ }
25
+ return toolkits.map((tk) => ({
26
+ slug: tk.slug ?? "",
27
+ name: tk.name ?? tk.slug ?? "",
28
+ description: tk.meta?.description ?? tk.description ?? "",
29
+ logo: tk.meta?.logo ?? tk.logo ?? "",
30
+ authSchemes: tk.auth_schemes ?? [],
31
+ noAuth: tk.no_auth ?? false,
32
+ connected: connectedSlugs.has(tk.slug ?? ""),
33
+ }));
34
+ }
35
+ // Search tools across connected toolkits by natural language query
36
+ // Makes parallel per-toolkit requests since the API doesn't support comma-separated toolkit_slug with query
37
+ async searchTools(query, toolkitSlugs, limit = 10) {
38
+ const mapTool = (t) => ({
39
+ name: t.name ?? t.enum ?? "",
40
+ slug: t.slug ?? t.enum ?? t.name ?? "",
41
+ description: t.description ?? "",
42
+ toolkitSlug: t.toolkit?.slug ?? t.toolkit_slug ?? "",
43
+ parameters: t.input_parameters ?? t.parameters ?? t.inputParameters ?? {},
44
+ });
45
+ if (!toolkitSlugs?.length) {
46
+ const params = new URLSearchParams({ query, limit: String(limit) });
47
+ const resp = await this.request("GET", `/tools?${params}`);
48
+ const tools = Array.isArray(resp) ? resp : (resp.items ?? resp.tools ?? []);
49
+ return tools.map(mapTool);
50
+ }
51
+ // Parallel per-toolkit search
52
+ const perToolkit = await Promise.all(toolkitSlugs.map(async (slug) => {
53
+ try {
54
+ const params = new URLSearchParams({ query, toolkit_slug: slug, limit: String(limit) });
55
+ const resp = await this.request("GET", `/tools?${params}`);
56
+ const tools = Array.isArray(resp) ? resp : (resp.items ?? resp.tools ?? []);
57
+ return tools.map(mapTool);
58
+ }
59
+ catch {
60
+ return [];
61
+ }
62
+ }));
63
+ return perToolkit.flat().slice(0, limit);
64
+ }
65
+ // List tools for a specific toolkit
66
+ async listTools(toolkitSlug) {
67
+ const resp = await this.request("GET", `/tools?toolkit_slug=${encodeURIComponent(toolkitSlug)}`);
68
+ const tools = Array.isArray(resp) ? resp : (resp.items ?? resp.tools ?? []);
69
+ return tools.map((t) => ({
70
+ name: t.name ?? t.enum ?? "",
71
+ slug: t.slug ?? t.enum ?? t.name ?? "",
72
+ description: t.description ?? "",
73
+ toolkitSlug,
74
+ parameters: t.input_parameters ?? t.parameters ?? t.inputParameters ?? {},
75
+ }));
76
+ }
77
+ // Get or create an auth config for a toolkit (needed before creating a connection)
78
+ async getOrCreateAuthConfig(toolkitSlug) {
79
+ // Check cache first
80
+ const cached = this.authConfigCache.get(toolkitSlug);
81
+ if (cached)
82
+ return cached;
83
+ // Check if one already exists
84
+ const existing = await this.request("GET", `/auth_configs?toolkit_slug=${encodeURIComponent(toolkitSlug)}`);
85
+ const items = existing.items ?? [];
86
+ if (items.length > 0) {
87
+ const id = items[0].id ?? items[0].auth_config?.id;
88
+ if (id) {
89
+ this.authConfigCache.set(toolkitSlug, id);
90
+ return id;
91
+ }
92
+ }
93
+ // Create a new one with Composio-managed auth
94
+ const created = await this.request("POST", "/auth_configs", {
95
+ toolkit: { slug: toolkitSlug },
96
+ auth_scheme: "OAUTH2",
97
+ use_composio_auth: true,
98
+ });
99
+ const id = created.auth_config?.id ?? created.id ?? "";
100
+ if (id)
101
+ this.authConfigCache.set(toolkitSlug, id);
102
+ return id;
103
+ }
104
+ // Start OAuth connection flow (two-step: ensure auth config, then create connection)
105
+ async initiateConnection(toolkitSlug, userId, redirectUrl) {
106
+ const authConfigId = await this.getOrCreateAuthConfig(toolkitSlug);
107
+ if (!authConfigId) {
108
+ throw new Error(`Failed to get auth config for toolkit: ${toolkitSlug}`);
109
+ }
110
+ const body = {
111
+ auth_config: { id: authConfigId },
112
+ connection: {
113
+ user_id: userId,
114
+ ...(redirectUrl ? { callback_url: redirectUrl } : {}),
115
+ },
116
+ };
117
+ const resp = await this.request("POST", "/connected_accounts", body);
118
+ return {
119
+ connectionId: resp.id ?? "",
120
+ redirectUrl: resp.redirect_url ?? resp.redirect_uri ?? resp.redirectUrl ?? resp.redirectUri ?? "",
121
+ };
122
+ }
123
+ // List active connections for a user
124
+ async listConnections(userId) {
125
+ const resp = await this.request("GET", `/connected_accounts?user_ids=${encodeURIComponent(userId)}&statuses=ACTIVE`);
126
+ const items = Array.isArray(resp) ? resp : (resp.items ?? resp.connections ?? []);
127
+ return items.map((c) => ({
128
+ id: c.id ?? "",
129
+ toolkitSlug: c.toolkit?.slug ?? c.toolkit_slug ?? c.appUniqueId ?? c.integrationId ?? "",
130
+ status: c.status ?? "ACTIVE",
131
+ createdAt: c.createdAt ?? c.created_at ?? "",
132
+ }));
133
+ }
134
+ // Delete a connection
135
+ async deleteConnection(id) {
136
+ await this.request("DELETE", `/connected_accounts/${encodeURIComponent(id)}`);
137
+ }
138
+ // Execute a tool action
139
+ async executeTool(toolSlug, userId, params, connectedAccountId) {
140
+ const body = {
141
+ arguments: params,
142
+ user_id: userId,
143
+ };
144
+ if (connectedAccountId)
145
+ body.connected_account_id = connectedAccountId;
146
+ return this.request("POST", `/tools/execute/${encodeURIComponent(toolSlug)}`, body);
147
+ }
148
+ // ── Private ────────────────────────────────────────────────────────
149
+ async request(method, path, body) {
150
+ const url = `${BASE_URL}${path}`;
151
+ const headers = {
152
+ "x-api-key": this.apiKey,
153
+ "Accept": "application/json",
154
+ };
155
+ if (body)
156
+ headers["Content-Type"] = "application/json";
157
+ const resp = await fetch(url, {
158
+ method,
159
+ headers,
160
+ body: body ? JSON.stringify(body) : undefined,
161
+ });
162
+ if (!resp.ok) {
163
+ const text = await resp.text().catch(() => "");
164
+ throw new Error(`Composio API ${method} ${path} failed (${resp.status}): ${text}`);
165
+ }
166
+ if (resp.status === 204)
167
+ return undefined;
168
+ return resp.json();
169
+ }
170
+ }
@@ -0,0 +1,2 @@
1
+ export { ComposioClient, type ComposioToolkit, type ComposioConnection, type ComposioTool } from "./client.js";
2
+ export { ComposioAdapter } from "./adapter.js";
@@ -0,0 +1,2 @@
1
+ export { ComposioClient } from "./client.js";
2
+ export { ComposioAdapter } from "./adapter.js";
@@ -0,0 +1,20 @@
1
+ export interface ContextSnapshot {
2
+ memory: string;
3
+ summary: string;
4
+ recentChat: string;
5
+ recentMood: string;
6
+ }
7
+ /** Read MEMORY.md + chat-summary + recent chat, returns raw content */
8
+ export declare function getContextSnapshot(agentDir: string, branch: string): Promise<ContextSnapshot>;
9
+ /**
10
+ * Returns context string for voice LLM system instructions.
11
+ * Includes: memory + conversation summary + recent chat history.
12
+ * Recent chat is critical — it survives page refreshes so the voice LLM
13
+ * knows what just happened even when the WebSocket reconnects.
14
+ */
15
+ export declare function getVoiceContext(agentDir: string, branch: string): Promise<string>;
16
+ /**
17
+ * Returns richer context for run_agent systemPromptSuffix.
18
+ * Includes: full memory + summary. Capped at ~2000 tokens.
19
+ */
20
+ export declare function getAgentContext(agentDir: string, branch: string): Promise<string>;