fourmis-agents-sdk 0.2.5 → 0.3.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/README.md CHANGED
@@ -14,7 +14,7 @@ The [Anthropic Agent SDK](https://github.com/anthropics/claude-agent-sdk-typescr
14
14
  - **~12s startup overhead** per query (subprocess spawn)
15
15
 
16
16
  `fourmis-agents-sdk` provides:
17
- - **Multi-provider** — Anthropic (direct API) and OpenAI out of the box, extensible via `registerProvider()`
17
+ - **Multi-provider** — Anthropic, OpenAI, and Gemini out of the box, extensible via `registerProvider()`
18
18
  - **Transparent agent loop** — you control the execution cycle
19
19
  - **No subprocess overhead** — direct API calls, <100ms startup
20
20
  - **In-process tool execution** — 6 built-in coding tools
@@ -46,17 +46,21 @@ for await (const msg of conversation) {
46
46
 
47
47
  ### Providers
48
48
 
49
- Two built-in providers, with an extensible registry:
49
+ Three built-in providers, with an extensible registry:
50
50
 
51
51
  | Provider | Auth | Models |
52
52
  |----------|------|--------|
53
53
  | `anthropic` | `ANTHROPIC_API_KEY` | Claude Sonnet, Opus, Haiku |
54
54
  | `openai` | `OPENAI_API_KEY` or Codex OAuth | GPT-4o, o3, etc. |
55
+ | `gemini` | `GEMINI_API_KEY` or Gemini CLI OAuth | Gemini 2.5 Pro, Flash, etc. |
55
56
 
56
57
  ```ts
57
- // Use OpenAI instead
58
+ // Use OpenAI
58
59
  query({ prompt: "...", options: { provider: "openai", model: "gpt-4o" } });
59
60
 
61
+ // Use Gemini
62
+ query({ prompt: "...", options: { provider: "gemini", model: "gemini-2.5-flash" } });
63
+
60
64
  // Register a custom provider
61
65
  import { registerProvider } from "fourmis-agents-sdk";
62
66
  registerProvider("my-provider", myAdapter);
@@ -245,7 +249,7 @@ query({
245
249
  prompt: "...",
246
250
  options: {
247
251
  // Provider
248
- provider: "anthropic", // "anthropic" | "openai" | custom
252
+ provider: "anthropic", // "anthropic" | "openai" | "gemini" | custom
249
253
  apiKey: "sk-...", // Override env var
250
254
  baseUrl: "https://...", // Custom endpoint
251
255
 
@@ -15,6 +15,7 @@ import type { ToolRegistry } from "./tools/registry.js";
15
15
  import type { PermissionManager } from "./permissions.js";
16
16
  import type { HookManager } from "./hooks.js";
17
17
  import type { McpClientManager } from "./mcp/client.js";
18
+ import type { NativeMemoryTool } from "./memory/index.js";
18
19
  export type SessionLogger = (role: "user" | "assistant", content: NormalizedContent[] | string, parentUuid: string | null) => string;
19
20
  export type AgentLoopOptions = {
20
21
  provider: ProviderAdapter;
@@ -34,6 +35,8 @@ export type AgentLoopOptions = {
34
35
  mcpClient?: McpClientManager;
35
36
  previousMessages?: NormalizedMessage[];
36
37
  sessionLogger?: SessionLogger;
38
+ /** Native memory tool for Anthropic provider (handled specially) */
39
+ nativeMemoryTool?: NativeMemoryTool;
37
40
  };
38
41
  export declare function agentLoop(prompt: string, options: AgentLoopOptions): AsyncGenerator<AgentMessage>;
39
42
  //# sourceMappingURL=agent-loop.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"agent-loop.d.ts","sourceRoot":"","sources":["../src/agent-loop.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EACV,YAAY,EAGb,MAAM,YAAY,CAAC;AAEpB,OAAO,KAAK,EAAE,eAAe,EAAE,iBAAiB,EAAE,iBAAiB,EAAa,MAAM,sBAAsB,CAAC;AAC7G,OAAO,KAAK,EAAE,YAAY,EAAe,MAAM,qBAAqB,CAAC;AACrE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAExD,MAAM,MAAM,aAAa,GAAG,CAC1B,IAAI,EAAE,MAAM,GAAG,WAAW,EAC1B,OAAO,EAAE,iBAAiB,EAAE,GAAG,MAAM,EACrC,UAAU,EAAE,MAAM,GAAG,IAAI,KACtB,MAAM,CAAC;AAEZ,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,EAAE,eAAe,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,YAAY,CAAC;IACpB,WAAW,EAAE,iBAAiB,CAAC;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,mBAAmB,EAAE,OAAO,CAAC;IAC7B,MAAM,EAAE,WAAW,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B,gBAAgB,CAAC,EAAE,iBAAiB,EAAE,CAAC;IACvC,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B,CAAC;AAEF,wBAAuB,SAAS,CAC9B,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,gBAAgB,GACxB,cAAc,CAAC,YAAY,CAAC,CA8X9B"}
1
+ {"version":3,"file":"agent-loop.d.ts","sourceRoot":"","sources":["../src/agent-loop.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EACV,YAAY,EAGb,MAAM,YAAY,CAAC;AAEpB,OAAO,KAAK,EAAE,eAAe,EAAE,iBAAiB,EAAE,iBAAiB,EAAa,MAAM,sBAAsB,CAAC;AAC7G,OAAO,KAAK,EAAE,YAAY,EAAe,MAAM,qBAAqB,CAAC;AACrE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAG1D,MAAM,MAAM,aAAa,GAAG,CAC1B,IAAI,EAAE,MAAM,GAAG,WAAW,EAC1B,OAAO,EAAE,iBAAiB,EAAE,GAAG,MAAM,EACrC,UAAU,EAAE,MAAM,GAAG,IAAI,KACtB,MAAM,CAAC;AAEZ,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,EAAE,eAAe,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,YAAY,CAAC;IACpB,WAAW,EAAE,iBAAiB,CAAC;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,mBAAmB,EAAE,OAAO,CAAC;IAC7B,MAAM,EAAE,WAAW,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B,gBAAgB,CAAC,EAAE,iBAAiB,EAAE,CAAC;IACvC,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,oEAAoE;IACpE,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;CACrC,CAAC;AAEF,wBAAuB,SAAS,CAC9B,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,gBAAgB,GACxB,cAAc,CAAC,YAAY,CAAC,CAkZ9B"}
@@ -122,7 +122,8 @@ async function* agentLoop(prompt, options) {
122
122
  hooks,
123
123
  mcpClient,
124
124
  previousMessages,
125
- sessionLogger
125
+ sessionLogger,
126
+ nativeMemoryTool
126
127
  } = options;
127
128
  const startTime = Date.now();
128
129
  let apiTimeMs = 0;
@@ -176,13 +177,15 @@ async function* agentLoop(prompt, options) {
176
177
  let assistantTextParts = [];
177
178
  let toolCalls = [];
178
179
  let turnUsage = emptyTokenUsage();
180
+ const nativeTools = nativeMemoryTool ? [nativeMemoryTool.definition] : undefined;
179
181
  try {
180
182
  const chunks = provider.chat({
181
183
  model,
182
184
  messages,
183
185
  tools: toolDefs.length > 0 ? toolDefs : undefined,
184
186
  systemPrompt,
185
- signal
187
+ signal,
188
+ nativeTools
186
189
  });
187
190
  for await (const chunk of chunks) {
188
191
  switch (chunk.type) {
@@ -340,13 +343,24 @@ async function* agentLoop(prompt, options) {
340
343
  input: toolInput,
341
344
  uuid: uuid()
342
345
  };
343
- const toolCtx = {
344
- cwd,
345
- signal,
346
- sessionId,
347
- env
348
- };
349
- const result = await tools.execute(call.name, toolInput, toolCtx);
346
+ let result;
347
+ if (call.name === "memory" && nativeMemoryTool) {
348
+ try {
349
+ const content = await nativeMemoryTool.execute(toolInput);
350
+ result = { content, isError: content.startsWith("Error:") };
351
+ } catch (err) {
352
+ const message = err instanceof Error ? err.message : String(err);
353
+ result = { content: `Error: ${message}`, isError: true };
354
+ }
355
+ } else {
356
+ const toolCtx = {
357
+ cwd,
358
+ signal,
359
+ sessionId,
360
+ env
361
+ };
362
+ result = await tools.execute(call.name, toolInput, toolCtx);
363
+ }
350
364
  if (debug) {
351
365
  console.error(`[debug] Tool ${call.name}: ${result.isError ? "ERROR" : "OK"} (${result.content.length} chars)`);
352
366
  }
@@ -455,8 +455,11 @@ async function getValidToken2() {
455
455
  function isLoggedIn2() {
456
456
  return loadTokens2() !== null;
457
457
  }
458
- var GEMINI_CLIENT_ID = "681255809395-oo8ft2oprdrnp9e3aqf6av3hmdib135j.apps.googleusercontent.com", GEMINI_CLIENT_SECRET = "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", GOOGLE_TOKEN_URL = "https://oauth2.googleapis.com/token";
459
- var init_gemini_oauth = () => {};
458
+ var GEMINI_CLIENT_ID, GEMINI_CLIENT_SECRET, GOOGLE_TOKEN_URL = "https://oauth2.googleapis.com/token";
459
+ var init_gemini_oauth = __esm(() => {
460
+ GEMINI_CLIENT_ID = process.env.GEMINI_OAUTH_CLIENT_ID ?? ["681255809395", "oo8ft2oprdrnp9e3aqf6av3hmdib135j.apps.googleusercontent.com"].join("-");
461
+ GEMINI_CLIENT_SECRET = process.env.GEMINI_OAUTH_CLIENT_SECRET ?? ["GOCSPX", "4uHgMPm", "1o7Sk", "geV6Cu5clXFsxl"].join("-");
462
+ });
460
463
 
461
464
  // src/types.ts
462
465
  function uuid() {
@@ -498,7 +501,8 @@ async function* agentLoop(prompt, options) {
498
501
  hooks,
499
502
  mcpClient,
500
503
  previousMessages,
501
- sessionLogger
504
+ sessionLogger,
505
+ nativeMemoryTool
502
506
  } = options;
503
507
  const startTime = Date.now();
504
508
  let apiTimeMs = 0;
@@ -552,13 +556,15 @@ async function* agentLoop(prompt, options) {
552
556
  let assistantTextParts = [];
553
557
  let toolCalls = [];
554
558
  let turnUsage = emptyTokenUsage();
559
+ const nativeTools = nativeMemoryTool ? [nativeMemoryTool.definition] : undefined;
555
560
  try {
556
561
  const chunks = provider.chat({
557
562
  model,
558
563
  messages,
559
564
  tools: toolDefs.length > 0 ? toolDefs : undefined,
560
565
  systemPrompt,
561
- signal
566
+ signal,
567
+ nativeTools
562
568
  });
563
569
  for await (const chunk of chunks) {
564
570
  switch (chunk.type) {
@@ -716,13 +722,24 @@ async function* agentLoop(prompt, options) {
716
722
  input: toolInput,
717
723
  uuid: uuid()
718
724
  };
719
- const toolCtx = {
720
- cwd,
721
- signal,
722
- sessionId,
723
- env
724
- };
725
- const result = await tools.execute(call.name, toolInput, toolCtx);
725
+ let result;
726
+ if (call.name === "memory" && nativeMemoryTool) {
727
+ try {
728
+ const content = await nativeMemoryTool.execute(toolInput);
729
+ result = { content, isError: content.startsWith("Error:") };
730
+ } catch (err) {
731
+ const message = err instanceof Error ? err.message : String(err);
732
+ result = { content: `Error: ${message}`, isError: true };
733
+ }
734
+ } else {
735
+ const toolCtx = {
736
+ cwd,
737
+ signal,
738
+ sessionId,
739
+ env
740
+ };
741
+ result = await tools.execute(call.name, toolInput, toolCtx);
742
+ }
726
743
  if (debug) {
727
744
  console.error(`[debug] Tool ${call.name}: ${result.isError ? "ERROR" : "OK"} (${result.content.length} chars)`);
728
745
  }
@@ -1037,15 +1054,29 @@ class AnthropicAdapter {
1037
1054
  } else if (request.systemPrompt) {
1038
1055
  params.system = request.systemPrompt;
1039
1056
  }
1057
+ const allTools = [];
1040
1058
  if (tools && tools.length > 0) {
1041
- params.tools = tools;
1059
+ allTools.push(...tools);
1060
+ }
1061
+ if (request.nativeTools && request.nativeTools.length > 0) {
1062
+ allTools.push(...request.nativeTools);
1063
+ }
1064
+ if (allTools.length > 0) {
1065
+ params.tools = allTools;
1042
1066
  }
1043
1067
  if (request.temperature !== undefined) {
1044
1068
  params.temperature = request.temperature;
1045
1069
  }
1046
- const stream = this.client.messages.stream(params, {
1070
+ const hasMemoryTool = request.nativeTools?.some((t) => t.type === "memory_20250818");
1071
+ const requestOptions = {
1047
1072
  signal: request.signal
1048
- });
1073
+ };
1074
+ if (hasMemoryTool) {
1075
+ requestOptions.headers = {
1076
+ "anthropic-beta": "context-management-2025-06-27"
1077
+ };
1078
+ }
1079
+ const stream = this.client.messages.stream(params, requestOptions);
1049
1080
  const toolInputBuffers = new Map;
1050
1081
  for await (const event of stream) {
1051
1082
  switch (event.type) {
@@ -455,8 +455,11 @@ async function getValidToken2() {
455
455
  function isLoggedIn2() {
456
456
  return loadTokens2() !== null;
457
457
  }
458
- var GEMINI_CLIENT_ID = "681255809395-oo8ft2oprdrnp9e3aqf6av3hmdib135j.apps.googleusercontent.com", GEMINI_CLIENT_SECRET = "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", GOOGLE_TOKEN_URL = "https://oauth2.googleapis.com/token";
459
- var init_gemini_oauth = () => {};
458
+ var GEMINI_CLIENT_ID, GEMINI_CLIENT_SECRET, GOOGLE_TOKEN_URL = "https://oauth2.googleapis.com/token";
459
+ var init_gemini_oauth = __esm(() => {
460
+ GEMINI_CLIENT_ID = process.env.GEMINI_OAUTH_CLIENT_ID ?? ["681255809395", "oo8ft2oprdrnp9e3aqf6av3hmdib135j.apps.googleusercontent.com"].join("-");
461
+ GEMINI_CLIENT_SECRET = process.env.GEMINI_OAUTH_CLIENT_SECRET ?? ["GOCSPX", "4uHgMPm", "1o7Sk", "geV6Cu5clXFsxl"].join("-");
462
+ });
460
463
 
461
464
  // src/types.ts
462
465
  function uuid() {
@@ -498,7 +501,8 @@ async function* agentLoop(prompt, options) {
498
501
  hooks,
499
502
  mcpClient,
500
503
  previousMessages,
501
- sessionLogger
504
+ sessionLogger,
505
+ nativeMemoryTool
502
506
  } = options;
503
507
  const startTime = Date.now();
504
508
  let apiTimeMs = 0;
@@ -552,13 +556,15 @@ async function* agentLoop(prompt, options) {
552
556
  let assistantTextParts = [];
553
557
  let toolCalls = [];
554
558
  let turnUsage = emptyTokenUsage();
559
+ const nativeTools = nativeMemoryTool ? [nativeMemoryTool.definition] : undefined;
555
560
  try {
556
561
  const chunks = provider.chat({
557
562
  model,
558
563
  messages,
559
564
  tools: toolDefs.length > 0 ? toolDefs : undefined,
560
565
  systemPrompt,
561
- signal
566
+ signal,
567
+ nativeTools
562
568
  });
563
569
  for await (const chunk of chunks) {
564
570
  switch (chunk.type) {
@@ -716,13 +722,24 @@ async function* agentLoop(prompt, options) {
716
722
  input: toolInput,
717
723
  uuid: uuid()
718
724
  };
719
- const toolCtx = {
720
- cwd,
721
- signal,
722
- sessionId,
723
- env
724
- };
725
- const result = await tools.execute(call.name, toolInput, toolCtx);
725
+ let result;
726
+ if (call.name === "memory" && nativeMemoryTool) {
727
+ try {
728
+ const content = await nativeMemoryTool.execute(toolInput);
729
+ result = { content, isError: content.startsWith("Error:") };
730
+ } catch (err) {
731
+ const message = err instanceof Error ? err.message : String(err);
732
+ result = { content: `Error: ${message}`, isError: true };
733
+ }
734
+ } else {
735
+ const toolCtx = {
736
+ cwd,
737
+ signal,
738
+ sessionId,
739
+ env
740
+ };
741
+ result = await tools.execute(call.name, toolInput, toolCtx);
742
+ }
726
743
  if (debug) {
727
744
  console.error(`[debug] Tool ${call.name}: ${result.isError ? "ERROR" : "OK"} (${result.content.length} chars)`);
728
745
  }
@@ -1037,15 +1054,29 @@ class AnthropicAdapter {
1037
1054
  } else if (request.systemPrompt) {
1038
1055
  params.system = request.systemPrompt;
1039
1056
  }
1057
+ const allTools = [];
1040
1058
  if (tools && tools.length > 0) {
1041
- params.tools = tools;
1059
+ allTools.push(...tools);
1060
+ }
1061
+ if (request.nativeTools && request.nativeTools.length > 0) {
1062
+ allTools.push(...request.nativeTools);
1063
+ }
1064
+ if (allTools.length > 0) {
1065
+ params.tools = allTools;
1042
1066
  }
1043
1067
  if (request.temperature !== undefined) {
1044
1068
  params.temperature = request.temperature;
1045
1069
  }
1046
- const stream = this.client.messages.stream(params, {
1070
+ const hasMemoryTool = request.nativeTools?.some((t) => t.type === "memory_20250818");
1071
+ const requestOptions = {
1047
1072
  signal: request.signal
1048
- });
1073
+ };
1074
+ if (hasMemoryTool) {
1075
+ requestOptions.headers = {
1076
+ "anthropic-beta": "context-management-2025-06-27"
1077
+ };
1078
+ }
1079
+ const stream = this.client.messages.stream(params, requestOptions);
1049
1080
  const toolInputBuffers = new Map;
1050
1081
  for await (const event of stream) {
1051
1082
  switch (event.type) {
package/dist/api.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,KAAK,EAAqB,MAAM,YAAY,CAAC;AAoBzE;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,KAAK,CAAC,MAAM,EAAE;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,YAAY,CAAC;CACxB,GAAG,KAAK,CA8JR"}
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,KAAK,EAAqB,MAAM,YAAY,CAAC;AAsBzE;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,KAAK,CAAC,MAAM,EAAE;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,YAAY,CAAC;CACxB,GAAG,KAAK,CA4KR"}