@gaunt-sloth/agent 0.1.8 → 2.0.0-alpha.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 (80) hide show
  1. package/README.md +14 -5
  2. package/cli-acp.js +35 -0
  3. package/cli.js +35 -0
  4. package/dist/builtInToolsConfig.d.ts +26 -0
  5. package/dist/builtInToolsConfig.js +148 -0
  6. package/dist/builtInToolsConfig.js.map +1 -0
  7. package/dist/core/GthDeepAgent.d.ts +76 -0
  8. package/dist/core/GthDeepAgent.js +368 -0
  9. package/dist/core/GthDeepAgent.js.map +1 -0
  10. package/dist/core/debugCapture.d.ts +51 -0
  11. package/dist/core/debugCapture.js +2 -0
  12. package/dist/core/debugCapture.js.map +1 -0
  13. package/dist/core/deepAgentPermissions.d.ts +55 -0
  14. package/dist/core/deepAgentPermissions.js +120 -0
  15. package/dist/core/deepAgentPermissions.js.map +1 -0
  16. package/dist/core/gthAcpServer.d.ts +27 -0
  17. package/dist/core/gthAcpServer.js +71 -0
  18. package/dist/core/gthAcpServer.js.map +1 -0
  19. package/dist/core/gthDeepAgentFactory.d.ts +8 -0
  20. package/dist/core/gthDeepAgentFactory.js +9 -0
  21. package/dist/core/gthDeepAgentFactory.js.map +1 -0
  22. package/dist/index.d.ts +13 -0
  23. package/dist/index.js +16 -0
  24. package/dist/index.js.map +1 -0
  25. package/dist/mcp/OAuthClientProviderImpl.d.ts +37 -0
  26. package/dist/mcp/OAuthClientProviderImpl.js +223 -0
  27. package/dist/mcp/OAuthClientProviderImpl.js.map +1 -0
  28. package/dist/middleware/binaryContentInjectionMiddleware.d.ts +22 -0
  29. package/dist/middleware/binaryContentInjectionMiddleware.js +125 -0
  30. package/dist/middleware/binaryContentInjectionMiddleware.js.map +1 -0
  31. package/dist/middleware/registry.d.ts +34 -0
  32. package/dist/middleware/registry.js +124 -0
  33. package/dist/middleware/registry.js.map +1 -0
  34. package/dist/middleware/types.d.ts +89 -0
  35. package/dist/middleware/types.js +9 -0
  36. package/dist/middleware/types.js.map +1 -0
  37. package/dist/modules/a2a/A2AClientWrapper.d.ts +22 -0
  38. package/dist/modules/a2a/A2AClientWrapper.js +63 -0
  39. package/dist/modules/a2a/A2AClientWrapper.js.map +1 -0
  40. package/dist/modules/acpModule.d.ts +37 -0
  41. package/dist/modules/acpModule.js +67 -0
  42. package/dist/modules/acpModule.js.map +1 -0
  43. package/dist/modules/apiAgUiModule.d.ts +2 -0
  44. package/dist/modules/apiAgUiModule.js +417 -0
  45. package/dist/modules/apiAgUiModule.js.map +1 -0
  46. package/dist/modules/interactiveSessionModule.d.ts +9 -0
  47. package/dist/modules/interactiveSessionModule.js +99 -0
  48. package/dist/modules/interactiveSessionModule.js.map +1 -0
  49. package/dist/resolvers.d.ts +18 -0
  50. package/dist/resolvers.js +140 -0
  51. package/dist/resolvers.js.map +1 -0
  52. package/dist/tools/A2AAgentTool.d.ts +24 -0
  53. package/dist/tools/A2AAgentTool.js +37 -0
  54. package/dist/tools/A2AAgentTool.js.map +1 -0
  55. package/dist/tools/GthCustomToolkit.d.ts +41 -0
  56. package/dist/tools/GthCustomToolkit.js +289 -0
  57. package/dist/tools/GthCustomToolkit.js.map +1 -0
  58. package/dist/tools/GthDevToolkit.d.ts +24 -0
  59. package/dist/tools/GthDevToolkit.js +189 -0
  60. package/dist/tools/GthDevToolkit.js.map +1 -0
  61. package/dist/tools/GthFileSystemToolkit.d.ts +36 -0
  62. package/dist/tools/GthFileSystemToolkit.js +775 -0
  63. package/dist/tools/GthFileSystemToolkit.js.map +1 -0
  64. package/dist/tools/binaryUtils.d.ts +13 -0
  65. package/dist/tools/binaryUtils.js +55 -0
  66. package/dist/tools/binaryUtils.js.map +1 -0
  67. package/dist/tools/gthStatusUpdateTool.d.ts +2 -0
  68. package/dist/tools/gthStatusUpdateTool.js +15 -0
  69. package/dist/tools/gthStatusUpdateTool.js.map +1 -0
  70. package/dist/tools/gthWebFetchTool.d.ts +2 -0
  71. package/dist/tools/gthWebFetchTool.js +47 -0
  72. package/dist/tools/gthWebFetchTool.js.map +1 -0
  73. package/dist/tools/showA2UISurfaceTool.d.ts +9 -0
  74. package/dist/tools/showA2UISurfaceTool.js +25 -0
  75. package/dist/tools/showA2UISurfaceTool.js.map +1 -0
  76. package/dist/utils/mcpUtils.d.ts +13 -0
  77. package/dist/utils/mcpUtils.js +225 -0
  78. package/dist/utils/mcpUtils.js.map +1 -0
  79. package/package.json +49 -10
  80. package/index.js +0 -6
@@ -0,0 +1,27 @@
1
+ /**
2
+ * @packageDocumentation
3
+ * Thin wrapper over `deepagents-acp`'s `DeepAgentsServer` that fixes how its filesystem backend is
4
+ * rooted, since 0.1.12 gets both the location and the path semantics wrong for an IDE-hosted agent.
5
+ *
6
+ * 1. **Per-session root.** deepagents-acp roots its backend once at `workspaceRoot ?? process.cwd()`
7
+ * and IGNORES the `cwd` carried by every ACP `session/new` request. An ACP host (Zed, JetBrains)
8
+ * spawns one long-lived agent subprocess and passes the real project root per session, so we
9
+ * re-root the backend to `session/new.cwd`.
10
+ * 2. **Virtual-fs semantics.** deepagents-acp builds its backend WITHOUT `virtualMode`, so `/`
11
+ * resolves to the OS root and `ls /` lists the whole machine — but deepagents' filesystem prompt
12
+ * tells the model `/` is the workspace root (the convention the local runner uses via
13
+ * `FilesystemBackend({ virtualMode: true })`). We switch the backend into virtual-fs mode so
14
+ * `'/'`-rooted paths are workspace-relative.
15
+ *
16
+ * The server's connection handler dispatches `this.handleNewSession(params, conn)` dynamically, so
17
+ * we patch that instance method before `start()`. Both fixes reach into deepagents-acp internals and
18
+ * are guarded so a shape change degrades to a no-op rather than throwing; if a future deepagents-acp
19
+ * honors session cwd / virtual mode itself, this becomes a harmless re-assert.
20
+ */
21
+ import { DeepAgentsServer, type DeepAgentsServerOptions } from 'deepagents-acp';
22
+ /**
23
+ * Construct and start a deepagents-acp server that roots its filesystem backend at each ACP
24
+ * session's `cwd` and runs it in virtual-fs mode (so `/` is the workspace, not the OS root).
25
+ * Resolves once the stdio transport is listening.
26
+ */
27
+ export declare function startGthAcpServer(options: DeepAgentsServerOptions): Promise<DeepAgentsServer>;
@@ -0,0 +1,71 @@
1
+ /**
2
+ * @packageDocumentation
3
+ * Thin wrapper over `deepagents-acp`'s `DeepAgentsServer` that fixes how its filesystem backend is
4
+ * rooted, since 0.1.12 gets both the location and the path semantics wrong for an IDE-hosted agent.
5
+ *
6
+ * 1. **Per-session root.** deepagents-acp roots its backend once at `workspaceRoot ?? process.cwd()`
7
+ * and IGNORES the `cwd` carried by every ACP `session/new` request. An ACP host (Zed, JetBrains)
8
+ * spawns one long-lived agent subprocess and passes the real project root per session, so we
9
+ * re-root the backend to `session/new.cwd`.
10
+ * 2. **Virtual-fs semantics.** deepagents-acp builds its backend WITHOUT `virtualMode`, so `/`
11
+ * resolves to the OS root and `ls /` lists the whole machine — but deepagents' filesystem prompt
12
+ * tells the model `/` is the workspace root (the convention the local runner uses via
13
+ * `FilesystemBackend({ virtualMode: true })`). We switch the backend into virtual-fs mode so
14
+ * `'/'`-rooted paths are workspace-relative.
15
+ *
16
+ * The server's connection handler dispatches `this.handleNewSession(params, conn)` dynamically, so
17
+ * we patch that instance method before `start()`. Both fixes reach into deepagents-acp internals and
18
+ * are guarded so a shape change degrades to a no-op rather than throwing; if a future deepagents-acp
19
+ * honors session cwd / virtual mode itself, this becomes a harmless re-assert.
20
+ */
21
+ import { DeepAgentsServer } from 'deepagents-acp';
22
+ import { resolve } from 'node:path';
23
+ import { debugLog } from '@gaunt-sloth/core/utils/debugUtils.js';
24
+ /**
25
+ * Put an ACP filesystem backend into deepagents' virtual-fs mode so `'/'`-rooted paths are
26
+ * workspace-relative (clamped under `cwd`), matching deepagents' prompt and the local runner.
27
+ *
28
+ * `virtualMode` covers the methods that route through `resolvePath` (ls) and the inline checks
29
+ * (glob/grep). The ACP read/write proxy uses its own `resolveAbsPath`, which ignores `virtualMode`,
30
+ * so override that too so proxied reads/writes hit the workspace, not the OS root.
31
+ */
32
+ function configureVirtualFs(backend) {
33
+ backend.virtualMode = true;
34
+ backend.resolveAbsPath = (filePath) => resolve(backend.cwd ?? process.cwd(), String(filePath).replace(/^\/+/, ''));
35
+ }
36
+ /**
37
+ * Construct and start a deepagents-acp server that roots its filesystem backend at each ACP
38
+ * session's `cwd` and runs it in virtual-fs mode (so `/` is the workspace, not the OS root).
39
+ * Resolves once the stdio transport is listening.
40
+ */
41
+ export async function startGthAcpServer(options) {
42
+ const server = new DeepAgentsServer(options);
43
+ const internals = server;
44
+ const original = internals.handleNewSession;
45
+ if (typeof original === 'function') {
46
+ const bound = original.bind(server);
47
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
48
+ internals.handleNewSession = async (params, conn) => {
49
+ const result = await bound(params, conn);
50
+ const backends = internals.acpBackends;
51
+ const cwd = typeof params?.cwd === 'string' && params.cwd.length > 0 ? resolve(params.cwd) : undefined;
52
+ if (backends && backends.size > 0) {
53
+ for (const backend of backends.values()) {
54
+ if (!backend || typeof backend !== 'object')
55
+ continue;
56
+ if (cwd)
57
+ backend.cwd = cwd;
58
+ configureVirtualFs(backend);
59
+ }
60
+ debugLog(`ACP session: virtual fs, root ${cwd ?? '[startup workspace]'}`);
61
+ }
62
+ return result;
63
+ };
64
+ }
65
+ else {
66
+ debugLog('deepagents-acp handleNewSession not found to patch; session fs rooting disabled');
67
+ }
68
+ await server.start();
69
+ return server;
70
+ }
71
+ //# sourceMappingURL=gthAcpServer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gthAcpServer.js","sourceRoot":"","sources":["../../src/core/gthAcpServer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,gBAAgB,EAAgC,MAAM,gBAAgB,CAAC;AAChF,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,QAAQ,EAAE,MAAM,uCAAuC,CAAC;AAiBjE;;;;;;;GAOG;AACH,SAAS,kBAAkB,CAAC,OAA0B;IACpD,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAC3B,OAAO,CAAC,cAAc,GAAG,CAAC,QAAgB,EAAU,EAAE,CACpD,OAAO,CAAC,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC;AAChF,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,OAAgC;IAEhC,MAAM,MAAM,GAAG,IAAI,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAC7C,MAAM,SAAS,GAAG,MAAuC,CAAC;IAE1D,MAAM,QAAQ,GAAG,SAAS,CAAC,gBAAgB,CAAC;IAC5C,IAAI,OAAO,QAAQ,KAAK,UAAU,EAAE,CAAC;QACnC,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACpC,8DAA8D;QAC9D,SAAS,CAAC,gBAAgB,GAAG,KAAK,EAAE,MAAW,EAAE,IAAS,EAAE,EAAE;YAC5D,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;YACzC,MAAM,QAAQ,GAAG,SAAS,CAAC,WAAW,CAAC;YACvC,MAAM,GAAG,GACP,OAAO,MAAM,EAAE,GAAG,KAAK,QAAQ,IAAI,MAAM,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YAC7F,IAAI,QAAQ,IAAI,QAAQ,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;gBAClC,KAAK,MAAM,OAAO,IAAI,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;oBACxC,IAAI,CAAC,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ;wBAAE,SAAS;oBACtD,IAAI,GAAG;wBAAE,OAAO,CAAC,GAAG,GAAG,GAAG,CAAC;oBAC3B,kBAAkB,CAAC,OAAO,CAAC,CAAC;gBAC9B,CAAC;gBACD,QAAQ,CAAC,iCAAiC,GAAG,IAAI,qBAAqB,EAAE,CAAC,CAAC;YAC5E,CAAC;YACD,OAAO,MAAM,CAAC;QAChB,CAAC,CAAC;IACJ,CAAC;SAAM,CAAC;QACN,QAAQ,CAAC,iFAAiF,CAAC,CAAC;IAC9F,CAAC;IAED,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;IACrB,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,8 @@
1
+ import type { GthAgentFactory } from '@gaunt-sloth/core/core/types.js';
2
+ /**
3
+ * A {@link GthAgentFactory} that produces a {@link GthDeepAgent}. Pass this to
4
+ * `GthAgentRunner` (its optional 3rd ctor arg) so the same runner drives a
5
+ * `createDeepAgent` graph instead of the lean default — without `core` ever
6
+ * importing `deepagents`.
7
+ */
8
+ export declare const gthDeepAgentFactory: GthAgentFactory;
@@ -0,0 +1,9 @@
1
+ import { GthDeepAgent } from '#src/core/GthDeepAgent.js';
2
+ /**
3
+ * A {@link GthAgentFactory} that produces a {@link GthDeepAgent}. Pass this to
4
+ * `GthAgentRunner` (its optional 3rd ctor arg) so the same runner drives a
5
+ * `createDeepAgent` graph instead of the lean default — without `core` ever
6
+ * importing `deepagents`.
7
+ */
8
+ export const gthDeepAgentFactory = (statusUpdate, resolvers) => new GthDeepAgent(statusUpdate, resolvers);
9
+ //# sourceMappingURL=gthDeepAgentFactory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gthDeepAgentFactory.js","sourceRoot":"","sources":["../../src/core/gthDeepAgentFactory.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAEzD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAoB,CAAC,YAAY,EAAE,SAAS,EAAE,EAAE,CAC9E,IAAI,YAAY,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC"}
@@ -0,0 +1,13 @@
1
+ export * from '#src/modules/apiAgUiModule.js';
2
+ export * from '#src/modules/acpModule.js';
3
+ export * from '#src/modules/interactiveSessionModule.js';
4
+ export * from '#src/modules/a2a/A2AClientWrapper.js';
5
+ export * from '#src/tools/A2AAgentTool.js';
6
+ export * from '#src/mcp/OAuthClientProviderImpl.js';
7
+ export * from '#src/utils/mcpUtils.js';
8
+ export { createResolvers } from '#src/resolvers.js';
9
+ export * from '#src/builtInToolsConfig.js';
10
+ export { resolveMiddleware } from '#src/middleware/registry.js';
11
+ export { GthDeepAgent, type GthDeepAgentParams } from '#src/core/GthDeepAgent.js';
12
+ export * from '#src/core/deepAgentPermissions.js';
13
+ export { gthDeepAgentFactory } from '#src/core/gthDeepAgentFactory.js';
package/dist/index.js ADDED
@@ -0,0 +1,16 @@
1
+ export * from '#src/modules/apiAgUiModule.js';
2
+ export * from '#src/modules/acpModule.js';
3
+ export * from '#src/modules/interactiveSessionModule.js';
4
+ export * from '#src/modules/a2a/A2AClientWrapper.js';
5
+ export * from '#src/tools/A2AAgentTool.js';
6
+ export * from '#src/mcp/OAuthClientProviderImpl.js';
7
+ export * from '#src/utils/mcpUtils.js';
8
+ export { createResolvers } from '#src/resolvers.js';
9
+ // Tools + middleware (formerly @gaunt-sloth/tools)
10
+ export * from '#src/builtInToolsConfig.js';
11
+ export { resolveMiddleware } from '#src/middleware/registry.js';
12
+ // Deep agent (createDeepAgent / deepagents) + the GthConfig→permission mapping
13
+ export { GthDeepAgent } from '#src/core/GthDeepAgent.js';
14
+ export * from '#src/core/deepAgentPermissions.js';
15
+ export { gthDeepAgentFactory } from '#src/core/gthDeepAgentFactory.js';
16
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,+BAA+B,CAAC;AAC9C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,0CAA0C,CAAC;AACzD,cAAc,sCAAsC,CAAC;AACrD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,qCAAqC,CAAC;AACpD,cAAc,wBAAwB,CAAC;AACvC,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpD,mDAAmD;AACnD,cAAc,4BAA4B,CAAC;AAC3C,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAEhE,+EAA+E;AAC/E,OAAO,EAAE,YAAY,EAA2B,MAAM,2BAA2B,CAAC;AAClF,cAAc,mCAAmC,CAAC;AAClD,OAAO,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC"}
@@ -0,0 +1,37 @@
1
+ import type { OAuthClientProvider } from '@modelcontextprotocol/sdk/client/auth.js';
2
+ import type { OAuthClientInformationFull, OAuthClientMetadata, OAuthTokens } from '@modelcontextprotocol/sdk/shared/auth.js';
3
+ import { StreamableHTTPConnection } from '@langchain/mcp-adapters';
4
+ import http from 'http';
5
+ interface OAuthClientProviderConfig {
6
+ redirectUrl: string;
7
+ serverUrl: string;
8
+ }
9
+ /**
10
+ * Please note most of these "unused" methods are part of {@link OAuthClientProvider}
11
+ */
12
+ export declare class OAuthClientProviderImpl implements OAuthClientProvider {
13
+ private config;
14
+ private innerState;
15
+ private storagePath;
16
+ private storageCache?;
17
+ constructor(config: OAuthClientProviderConfig);
18
+ private loadStorageData;
19
+ private saveStorageData;
20
+ state(): string | Promise<string>;
21
+ get redirectUrl(): string;
22
+ get clientMetadata(): OAuthClientMetadata;
23
+ saveClientInformation(clientInformation: OAuthClientInformationFull): Promise<void>;
24
+ clientInformation(): Promise<OAuthClientInformationFull | undefined>;
25
+ saveTokens(tokens: OAuthTokens): Promise<void>;
26
+ tokens(): OAuthTokens | undefined;
27
+ saveCodeVerifier(codeVerifier: string): Promise<void>;
28
+ codeVerifier(): Promise<string>;
29
+ redirectToAuthorization(authUrl: URL): Promise<void>;
30
+ }
31
+ export declare function createAuthProviderAndAuthenticate(mcpServer: StreamableHTTPConnection): Promise<OAuthClientProviderImpl>;
32
+ export declare function createOAuthRedirectServer(path: string, portParam?: number): Promise<{
33
+ port: number;
34
+ server: http.Server;
35
+ codePromise: Promise<string>;
36
+ }>;
37
+ export {};
@@ -0,0 +1,223 @@
1
+ import { auth } from '@modelcontextprotocol/sdk/client/auth.js';
2
+ import express from 'express';
3
+ import * as crypto from 'node:crypto';
4
+ import { platform } from 'node:os';
5
+ import { spawnSync } from 'node:child_process';
6
+ import { displayInfo } from '@gaunt-sloth/core/utils/consoleUtils.js';
7
+ import { readFileSync, writeFileSync, existsSync, unlinkSync } from 'node:fs';
8
+ import { getOAuthStoragePath } from '@gaunt-sloth/core/utils/globalConfigUtils.js';
9
+ /**
10
+ * Please note most of these "unused" methods are part of {@link OAuthClientProvider}
11
+ */
12
+ export class OAuthClientProviderImpl {
13
+ config;
14
+ innerState;
15
+ storagePath;
16
+ storageCache;
17
+ constructor(config) {
18
+ this.config = config;
19
+ this.innerState = crypto.randomUUID();
20
+ if (!this.config.redirectUrl) {
21
+ throw new Error('No redirect URL provided');
22
+ }
23
+ if (!this.config.serverUrl) {
24
+ throw new Error('No server URL provided');
25
+ }
26
+ this.storagePath = getOAuthStoragePath(this.config.serverUrl);
27
+ }
28
+ loadStorageData() {
29
+ // Return cached data if available
30
+ if (this.storageCache !== undefined) {
31
+ return this.storageCache;
32
+ }
33
+ // Read from file if cache is empty
34
+ if (existsSync(this.storagePath)) {
35
+ try {
36
+ const data = readFileSync(this.storagePath, 'utf-8');
37
+ const parsedData = JSON.parse(data);
38
+ // Wipe data if the in-memory cache is empty and file info is missing tokens
39
+ if (parsedData.clientInformation && !parsedData.tokens) {
40
+ try {
41
+ unlinkSync(this.storagePath);
42
+ displayInfo('Deleted OAuth storage file (missing tokens)');
43
+ this.storageCache = {};
44
+ return this.storageCache;
45
+ }
46
+ catch (error) {
47
+ displayInfo('Failed to delete OAuth storage file: ' + error);
48
+ }
49
+ }
50
+ // Cache the loaded data
51
+ this.storageCache = parsedData;
52
+ return parsedData;
53
+ }
54
+ catch (error) {
55
+ displayInfo('Failed to load OAuth storage data: ' + error);
56
+ this.storageCache = {};
57
+ return this.storageCache;
58
+ }
59
+ }
60
+ // No file exists, cache empty object
61
+ this.storageCache = {};
62
+ return this.storageCache;
63
+ }
64
+ saveStorageData(data) {
65
+ try {
66
+ writeFileSync(this.storagePath, JSON.stringify(data, null, 2), 'utf-8');
67
+ // Update the cache when saving
68
+ this.storageCache = data;
69
+ }
70
+ catch (error) {
71
+ console.error('Failed to save OAuth storage data:', error);
72
+ }
73
+ }
74
+ state() {
75
+ return this.innerState;
76
+ }
77
+ // noinspection JSUnusedGlobalSymbols
78
+ get redirectUrl() {
79
+ return this.config.redirectUrl;
80
+ }
81
+ // noinspection JSUnusedGlobalSymbols
82
+ get clientMetadata() {
83
+ return {
84
+ redirect_uris: [this.config.redirectUrl],
85
+ client_name: 'Gaunt Sloth Assistant',
86
+ client_uri: 'https://gaunt-sloth-assistant.github.io/',
87
+ software_id: '1dd38b83-946b-4631-8855-66ee467bfd68',
88
+ scope: 'mcp:read mcp:write',
89
+ token_endpoint_auth_method: 'none',
90
+ grant_types: ['authorization_code', 'refresh_token'],
91
+ response_types: ['code'],
92
+ };
93
+ }
94
+ // noinspection JSUnusedGlobalSymbols
95
+ saveClientInformation(clientInformation) {
96
+ const data = this.loadStorageData();
97
+ data.clientInformation = clientInformation;
98
+ this.saveStorageData(data);
99
+ return Promise.resolve();
100
+ }
101
+ // noinspection JSUnusedGlobalSymbols
102
+ async clientInformation() {
103
+ const data = this.loadStorageData();
104
+ return Promise.resolve(data.clientInformation);
105
+ }
106
+ // noinspection JSUnusedGlobalSymbols
107
+ async saveTokens(tokens) {
108
+ const data = this.loadStorageData();
109
+ data.tokens = tokens;
110
+ this.saveStorageData(data);
111
+ }
112
+ // noinspection JSUnusedGlobalSymbols
113
+ tokens() {
114
+ const data = this.loadStorageData();
115
+ return data.tokens;
116
+ }
117
+ // noinspection JSUnusedGlobalSymbols
118
+ saveCodeVerifier(codeVerifier) {
119
+ const data = this.loadStorageData();
120
+ data.codeVerifier = codeVerifier;
121
+ this.saveStorageData(data);
122
+ return Promise.resolve();
123
+ }
124
+ // noinspection JSUnusedGlobalSymbols
125
+ codeVerifier() {
126
+ const data = this.loadStorageData();
127
+ if (!data.codeVerifier) {
128
+ throw new Error('No code verifier stored');
129
+ }
130
+ return Promise.resolve(data.codeVerifier);
131
+ }
132
+ // noinspection JSUnusedGlobalSymbols
133
+ async redirectToAuthorization(authUrl) {
134
+ displayInfo('Auth url: ' + authUrl.toString());
135
+ try {
136
+ const url = authUrl.toString();
137
+ displayInfo('Trying to open browser');
138
+ // Handle different platforms
139
+ const platformName = platform();
140
+ if (platformName === 'win32') {
141
+ // Windows - 'start' is a cmd.exe built-in, so we must invoke it via cmd
142
+ spawnSync('cmd', ['/c', 'start', '', url], { stdio: 'ignore' });
143
+ }
144
+ else if (platformName === 'darwin') {
145
+ // macOS
146
+ spawnSync('open', [url], { stdio: 'ignore' });
147
+ }
148
+ else {
149
+ // Linux and others
150
+ spawnSync('xdg-open', [url], { stdio: 'ignore' });
151
+ }
152
+ }
153
+ catch (error) {
154
+ displayInfo(`Failed to open browser: ${error}`);
155
+ displayInfo(`Please open ${authUrl.toString()} in your browser`);
156
+ }
157
+ }
158
+ }
159
+ export async function createAuthProviderAndAuthenticate(mcpServer) {
160
+ const { port, server, codePromise } = await createOAuthRedirectServer('/oauth/callback');
161
+ const authProvider = new OAuthClientProviderImpl({
162
+ redirectUrl: `http://localhost:${port}/oauth/callback`,
163
+ serverUrl: mcpServer.url,
164
+ });
165
+ const outcome = await auth(authProvider, { serverUrl: mcpServer.url });
166
+ if (outcome == 'REDIRECT') {
167
+ const authorizationCode = await codePromise;
168
+ await auth(authProvider, { serverUrl: mcpServer.url, authorizationCode });
169
+ }
170
+ else if (outcome == 'AUTHORIZED') {
171
+ try {
172
+ server.close();
173
+ }
174
+ catch { }
175
+ displayInfo('Authorized');
176
+ }
177
+ else {
178
+ throw new Error(`Unexpected Auth outcome: ${outcome}`);
179
+ }
180
+ return authProvider;
181
+ }
182
+ export function createOAuthRedirectServer(path, portParam = 0) {
183
+ const redirectApp = express();
184
+ return new Promise((resolve, reject) => {
185
+ const codePromise = new Promise((resolveCode, rejectCode) => {
186
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
187
+ redirectApp.get(path, (req, res) => {
188
+ const code = req.query.code;
189
+ if (!code) {
190
+ res.status(400).send('Error: No auth code received');
191
+ rejectCode('Error: No auth code received');
192
+ return;
193
+ }
194
+ res.send(`<div style="height: 80vh;text-align: center;display: flex;justify-content: center;align-items: center;">
195
+ <div>
196
+ <h1>Auth successful!</h1>
197
+ You may close this window and return to the Gaunt Sloth.
198
+ <div>
199
+ <img src="data:image/bmp;base64,Qk02AwAAAAAAADYAAAAoAAAAEAAAABAAAAABABgAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAnD98ZBtDTQ8sey9YOQobRA8nZx9OdyxahzZrMwERdCtShjZqLQUVLwgZKwUWKQYXsFCRq06KciJPnEh/n0GEmjuClDl9lzl/uFGglz56dChOm0h0SBInLgQVKwgZKgUVgzZchDRfqEeLsUmavFWjvV6joEGFizFvsU+Yw2CowWCiu1qdbCRNKgEQMAofMAcebSRIgC9bt1KdqEqNnD6BokGJqEaOvVmjqEWMu12ftFeYwGCjpUOMUxc8KQIRLAQWhjVmo0qGs1CYqEmOqEmPmjyArk2UrEyQr02TsU2WwV6pvF2eo0CKlDl+OgsiJwQSijZthS9qnDuAo0GFqESMmz2BijZtpUOLnT2CuVicyWmtsVCUjDh0p0KQbiVYJAQSkzx3kzZ7iS5rv2mn3Y3Gq0+Pgi1laCFOfyxmmTx/slCVmT55eihemzyGjTd6NwgfgSxokzl9qkuY4pLR+Lvg1H3EjTRzZB1IjTV3cCNWmjmAgC1diCxpjC1ulDd8RhAtdihbljl/vmK1v2WuuWGgtVWdjDVwgS5ldCVZcydckjl5kzh3ynGwxHOooT6DSA8sQQwkpUeTyGrBlTp8eCNZkzh1rEeSmDx9ZB9JcyNYkzh2tlek5pjT8q3bqU2PPQokJgEPhTVxy2nBkDd6kDV2qEaOwGOvvWGxuViqoUSNlDd6nj6FvmKq13zKnkiJMgUZKwUWPg4poUaOkzp+kTh7nkCExG60033Jym7C03zKsVOdiC9tjjR10W7IeThrKAAOMAUYJgUULgUZdCZdqkqYu1ersk+fvFyww2q4z3fDqUmXhy5ulTp/yWi/Txo6JgEPLgYZLQUXKQUVJwkaSxExq1GTxWW3t1SpsU+gv1mvnUOLjTJ3izh3dTRkJwEQKgQWLQYaLAgcLQUYLQYZLAERezZaiTtmXyFJgjp0ijt1iTlqOAsjNQ0pKQASJwUVKQYYLgUZLAQZLAgcLgUZMAUYZyVRfCthPQoiIQAJOgcgYyBLKAIRLAUZNwsoMQcfKAQV"
200
+ width="16" height="16">
201
+ </div>
202
+ </div>
203
+ </div>`);
204
+ resolveCode(code);
205
+ if (server) {
206
+ displayInfo('Cleaning auth redirect server...');
207
+ server.close();
208
+ }
209
+ });
210
+ });
211
+ const server = redirectApp.listen(portParam, () => {
212
+ const addressInfo = server.address();
213
+ const port = addressInfo.port;
214
+ displayInfo(`OAuth callback server listening at ${port}`);
215
+ resolve({ port, server, codePromise });
216
+ });
217
+ server.on('error', (err) => {
218
+ // If the server fails to start, reject the outer promise.
219
+ reject(err);
220
+ });
221
+ });
222
+ }
223
+ //# sourceMappingURL=OAuthClientProviderImpl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OAuthClientProviderImpl.js","sourceRoot":"","sources":["../../src/mcp/OAuthClientProviderImpl.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * @packageDocumentation
3
+ * Middleware to inject binary content (images, PDFs, audio) as HumanMessage.
4
+ *
5
+ * The gth_read_binary tool returns binary data as a special string format:
6
+ * gth_read_binary;type:${type};path:${encodedPath};data:${media_type};base64,${data}
7
+ * where path is URL-encoded to handle special characters like semicolons.
8
+ *
9
+ * This middleware:
10
+ * 1. Detects the gth_read_binary tool calls
11
+ * 2. Parses the special string format from ToolMessage content
12
+ * 3. Injects HumanMessage with binary content blocks before next model call
13
+ *
14
+ * This works around LangChain's limitation where ToolMessage doesn't properly
15
+ * support binary content blocks for most providers.
16
+ */
17
+ import { type AgentMiddleware } from 'langchain';
18
+ import type { GthConfig } from '@gaunt-sloth/core/config.js';
19
+ export interface BinaryContentInjectionMiddlewareSettings {
20
+ name?: 'binary-content-injection';
21
+ }
22
+ export declare function createBinaryContentInjectionMiddleware(_settings: BinaryContentInjectionMiddlewareSettings, _gthConfig: GthConfig): Promise<AgentMiddleware>;
@@ -0,0 +1,125 @@
1
+ /**
2
+ * @packageDocumentation
3
+ * Middleware to inject binary content (images, PDFs, audio) as HumanMessage.
4
+ *
5
+ * The gth_read_binary tool returns binary data as a special string format:
6
+ * gth_read_binary;type:${type};path:${encodedPath};data:${media_type};base64,${data}
7
+ * where path is URL-encoded to handle special characters like semicolons.
8
+ *
9
+ * This middleware:
10
+ * 1. Detects the gth_read_binary tool calls
11
+ * 2. Parses the special string format from ToolMessage content
12
+ * 3. Injects HumanMessage with binary content blocks before next model call
13
+ *
14
+ * This works around LangChain's limitation where ToolMessage doesn't properly
15
+ * support binary content blocks for most providers.
16
+ */
17
+ import { createMiddleware } from 'langchain';
18
+ import path from 'node:path';
19
+ import { debugLog } from '@gaunt-sloth/core/utils/debugUtils.js';
20
+ import { ToolMessage, HumanMessage } from '@langchain/core/messages';
21
+ /**
22
+ * Parse the special binary format string returned by gth_read_binary tool.
23
+ * Format: gth_read_binary;type:${type};path:${encodedPath};data:${media_type};base64,${data}
24
+ * Path is URL-encoded to handle special characters.
25
+ */
26
+ function parseBinaryContent(content) {
27
+ if (!content.startsWith('gth_read_binary;')) {
28
+ return null;
29
+ }
30
+ try {
31
+ const parts = content.split(';');
32
+ if (parts.length < 4) {
33
+ return null;
34
+ }
35
+ const typeMatch = parts[1]?.match(/^type:(.+)$/);
36
+ const pathMatch = parts[2]?.match(/^path:(.+)$/);
37
+ const dataMatch = parts[3]?.match(/^data:(.+)$/);
38
+ const base64Match = content.match(/;base64,(.+)$/);
39
+ if (!typeMatch || !pathMatch || !dataMatch || !base64Match) {
40
+ return null;
41
+ }
42
+ // Decode the URL-encoded path
43
+ const decodedPath = decodeURIComponent(pathMatch[1]);
44
+ return {
45
+ formatType: typeMatch[1],
46
+ path: decodedPath,
47
+ media_type: dataMatch[1],
48
+ data: base64Match[1],
49
+ };
50
+ }
51
+ catch {
52
+ return null;
53
+ }
54
+ }
55
+ function createContentBlock(binaryData) {
56
+ const { formatType, media_type, data } = binaryData;
57
+ return {
58
+ type: formatType,
59
+ source_type: 'base64',
60
+ mime_type: media_type,
61
+ data,
62
+ metadata: {
63
+ filename: path.basename(binaryData.path),
64
+ },
65
+ };
66
+ }
67
+ function getFormatLabel(formatType) {
68
+ const labels = {
69
+ image: 'image',
70
+ video: 'video',
71
+ audio: 'audio',
72
+ file: 'file',
73
+ };
74
+ return labels[formatType] || 'file';
75
+ }
76
+ export function createBinaryContentInjectionMiddleware(_settings, _gthConfig) {
77
+ debugLog('Creating binary content injection middleware');
78
+ return Promise.resolve(createMiddleware({
79
+ name: 'binary-content-injection',
80
+ // Before next model call, detect and inject HumanMessage with binary content
81
+ beforeModel: async (state) => {
82
+ const messages = state.messages || [];
83
+ // Find recent ToolMessages from gth_read_binary
84
+ const binaryMessages = [];
85
+ // Check last few messages (usually just need to check the most recent)
86
+ for (let i = messages.length - 1; i >= Math.max(0, messages.length - 5); i--) {
87
+ const msg = messages[i];
88
+ if (msg instanceof ToolMessage &&
89
+ msg.name === 'gth_read_binary' &&
90
+ typeof msg.content === 'string') {
91
+ const parsedContent = parseBinaryContent(msg.content);
92
+ if (parsedContent) {
93
+ binaryMessages.push({ message: msg, binaryData: parsedContent });
94
+ }
95
+ }
96
+ }
97
+ // If we found binary content, inject HumanMessage(s)
98
+ if (binaryMessages.length > 0) {
99
+ debugLog(`Injecting ${binaryMessages.length} HumanMessage(s) with binary content`);
100
+ const newMessages = [...messages];
101
+ for (const { binaryData } of binaryMessages) {
102
+ const formatLabel = getFormatLabel(binaryData.formatType);
103
+ const contentBlock = createContentBlock(binaryData);
104
+ const humanMessage = new HumanMessage({
105
+ content: [
106
+ {
107
+ type: 'text',
108
+ text: `Here is the ${formatLabel} content from the file:`,
109
+ },
110
+ contentBlock,
111
+ ],
112
+ });
113
+ newMessages.push(humanMessage);
114
+ }
115
+ // Return the modified state with new messages
116
+ return {
117
+ messages: newMessages,
118
+ };
119
+ }
120
+ // No binary content, pass through
121
+ return undefined;
122
+ },
123
+ }));
124
+ }
125
+ //# sourceMappingURL=binaryContentInjectionMiddleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"binaryContentInjectionMiddleware.js","sourceRoot":"","sources":["../../src/middleware/binaryContentInjectionMiddleware.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * @packageDocumentation
3
+ * Middleware registry for Gaunt Sloth Assistant.
4
+ *
5
+ * This module provides factory functions for creating predefined middleware instances
6
+ * and a resolver to convert middleware configurations into middleware objects.
7
+ */
8
+ import type { GthConfig } from '@gaunt-sloth/core/config.js';
9
+ import type { AnthropicPromptCachingConfig, MiddlewareConfig, SummarizationConfig } from '#src/middleware/types.js';
10
+ import { type AgentMiddleware } from 'langchain';
11
+ /**
12
+ * Create Anthropic prompt caching middleware.
13
+ * This middleware adds cache control headers to reduce API costs.
14
+ */
15
+ export declare function createAnthropicPromptCachingMiddleware(config: AnthropicPromptCachingConfig, _: GthConfig): Promise<AgentMiddleware>;
16
+ /**
17
+ * Create summarization middleware.
18
+ * This middleware automatically condenses conversation history when approaching token limits.
19
+ *
20
+ * @param config - Configuration for the middleware
21
+ * @param gthConfig - Full Gaunt Sloth configuration
22
+ * @returns Middleware object
23
+ */
24
+ export declare function createSummarizationMiddleware(config: SummarizationConfig, gthConfig: GthConfig): Promise<AgentMiddleware>;
25
+ /**
26
+ * Resolve middleware configuration into middleware instances.
27
+ * Converts string identifiers and config objects into actual middleware.
28
+ * Automatically injects binary-content-injection middleware if binaryFormats is enabled.
29
+ *
30
+ * @param configs - Array of middleware configurations
31
+ * @param gthConfig - Full Gaunt Sloth configuration
32
+ * @returns Array of middleware instances
33
+ */
34
+ export declare function resolveMiddleware(configs: MiddlewareConfig[] | undefined, gthConfig: GthConfig): Promise<AgentMiddleware[]>;