@gaunt-sloth/agent 2.0.0-alpha.8 → 2.0.0-beta.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 (126) hide show
  1. package/README.md +73 -23
  2. package/cli-acp.js +21 -24
  3. package/dist/builtInToolsConfig.d.ts +17 -3
  4. package/dist/builtInToolsConfig.js +43 -41
  5. package/dist/builtInToolsConfig.js.map +1 -1
  6. package/dist/core/debugCapture.d.ts +7 -49
  7. package/dist/core/debugCapture.js +1 -1
  8. package/dist/core/debugCapture.js.map +1 -1
  9. package/dist/core/resolveAgentFactory.d.ts +10 -9
  10. package/dist/core/resolveAgentFactory.js +11 -12
  11. package/dist/core/resolveAgentFactory.js.map +1 -1
  12. package/dist/index.d.ts +4 -4
  13. package/dist/index.js +7 -5
  14. package/dist/index.js.map +1 -1
  15. package/dist/mcp/OAuthClientProviderImpl.js.map +1 -1
  16. package/dist/mcp/tlsTrust.d.ts +61 -0
  17. package/dist/mcp/tlsTrust.js +109 -0
  18. package/dist/mcp/tlsTrust.js.map +1 -0
  19. package/dist/middleware/binaryContentInjectionMiddleware.d.ts +8 -1
  20. package/dist/middleware/binaryContentInjectionMiddleware.js +11 -2
  21. package/dist/middleware/binaryContentInjectionMiddleware.js.map +1 -1
  22. package/dist/middleware/frontendImageInjectionMiddleware.d.ts +107 -0
  23. package/dist/middleware/frontendImageInjectionMiddleware.js +167 -0
  24. package/dist/middleware/frontendImageInjectionMiddleware.js.map +1 -0
  25. package/dist/middleware/registry.js +49 -1
  26. package/dist/middleware/registry.js.map +1 -1
  27. package/dist/middleware/types.d.ts +16 -2
  28. package/dist/modules/a2a/A2AClientWrapper.d.ts +56 -1
  29. package/dist/modules/a2a/A2AClientWrapper.js +131 -31
  30. package/dist/modules/a2a/A2AClientWrapper.js.map +1 -1
  31. package/dist/modules/acp/acpAgentApp.d.ts +57 -0
  32. package/dist/modules/acp/acpAgentApp.js +378 -0
  33. package/dist/modules/acp/acpAgentApp.js.map +1 -0
  34. package/dist/modules/acp/acpAgentAppV1.d.ts +52 -0
  35. package/dist/modules/acp/acpAgentAppV1.js +307 -0
  36. package/dist/modules/acp/acpAgentAppV1.js.map +1 -0
  37. package/dist/modules/acp/acpCommon.d.ts +167 -0
  38. package/dist/modules/acp/acpCommon.js +282 -0
  39. package/dist/modules/acp/acpCommon.js.map +1 -0
  40. package/dist/modules/acp/acpPermissions.d.ts +65 -0
  41. package/dist/modules/acp/acpPermissions.js +162 -0
  42. package/dist/modules/acp/acpPermissions.js.map +1 -0
  43. package/dist/modules/acp/acpPermissionsV1.d.ts +45 -0
  44. package/dist/modules/acp/acpPermissionsV1.js +110 -0
  45. package/dist/modules/acp/acpPermissionsV1.js.map +1 -0
  46. package/dist/modules/acp/acpRouter.d.ts +41 -0
  47. package/dist/modules/acp/acpRouter.js +48 -0
  48. package/dist/modules/acp/acpRouter.js.map +1 -0
  49. package/dist/modules/acp/acpStdio.d.ts +47 -0
  50. package/dist/modules/acp/acpStdio.js +71 -0
  51. package/dist/modules/acp/acpStdio.js.map +1 -0
  52. package/dist/modules/acp/acpToolCalls.d.ts +93 -0
  53. package/dist/modules/acp/acpToolCalls.js +193 -0
  54. package/dist/modules/acp/acpToolCalls.js.map +1 -0
  55. package/dist/modules/acp/acpUpdates.d.ts +48 -0
  56. package/dist/modules/acp/acpUpdates.js +142 -0
  57. package/dist/modules/acp/acpUpdates.js.map +1 -0
  58. package/dist/modules/acp/acpUpdatesV1.d.ts +61 -0
  59. package/dist/modules/acp/acpUpdatesV1.js +162 -0
  60. package/dist/modules/acp/acpUpdatesV1.js.map +1 -0
  61. package/dist/modules/apiAgUiModule.d.ts +87 -0
  62. package/dist/modules/apiAgUiModule.js +193 -28
  63. package/dist/modules/apiAgUiModule.js.map +1 -1
  64. package/dist/modules/interactiveSessionModule.js +497 -51
  65. package/dist/modules/interactiveSessionModule.js.map +1 -1
  66. package/dist/modules/slashCommands.d.ts +582 -0
  67. package/dist/modules/slashCommands.js +1001 -0
  68. package/dist/modules/slashCommands.js.map +1 -0
  69. package/dist/resolvers.js +119 -9
  70. package/dist/resolvers.js.map +1 -1
  71. package/dist/tools/GthCustomToolkit.js +95 -11
  72. package/dist/tools/GthCustomToolkit.js.map +1 -1
  73. package/dist/tools/GthDevToolkit.d.ts +8 -5
  74. package/dist/tools/GthDevToolkit.js +89 -43
  75. package/dist/tools/GthDevToolkit.js.map +1 -1
  76. package/dist/tools/GthFileSystemToolkit.d.ts +63 -0
  77. package/dist/tools/GthFileSystemToolkit.js +506 -105
  78. package/dist/tools/GthFileSystemToolkit.js.map +1 -1
  79. package/dist/tools/McpResourceTool.d.ts +31 -0
  80. package/dist/tools/McpResourceTool.js +106 -0
  81. package/dist/tools/McpResourceTool.js.map +1 -0
  82. package/dist/tools/gthChecklistTool.d.ts +30 -0
  83. package/dist/tools/gthChecklistTool.js +80 -0
  84. package/dist/tools/gthChecklistTool.js.map +1 -0
  85. package/dist/tools/gthGrepTool.d.ts +54 -0
  86. package/dist/tools/gthGrepTool.js +482 -0
  87. package/dist/tools/gthGrepTool.js.map +1 -0
  88. package/dist/tools/shell/env.js +1 -1
  89. package/dist/tools/shell/env.js.map +1 -1
  90. package/dist/tools/shell/outputBuffer.js.map +1 -1
  91. package/dist/tools/shell/workDir.d.ts +1 -10
  92. package/dist/tools/shell/workDir.js +10 -36
  93. package/dist/tools/shell/workDir.js.map +1 -1
  94. package/dist/utils/mcpAuthError.d.ts +36 -0
  95. package/dist/utils/mcpAuthError.js +89 -0
  96. package/dist/utils/mcpAuthError.js.map +1 -0
  97. package/dist/utils/mcpUtils.js +16 -0
  98. package/dist/utils/mcpUtils.js.map +1 -1
  99. package/package.json +13 -13
  100. package/dist/core/GthDeepAgent.d.ts +0 -181
  101. package/dist/core/GthDeepAgent.js +0 -682
  102. package/dist/core/GthDeepAgent.js.map +0 -1
  103. package/dist/core/deepAgentPermissions.d.ts +0 -115
  104. package/dist/core/deepAgentPermissions.js +0 -282
  105. package/dist/core/deepAgentPermissions.js.map +0 -1
  106. package/dist/core/gthAcpServer.d.ts +0 -27
  107. package/dist/core/gthAcpServer.js +0 -82
  108. package/dist/core/gthAcpServer.js.map +0 -1
  109. package/dist/core/gthDeepAgentFactory.d.ts +0 -8
  110. package/dist/core/gthDeepAgentFactory.js +0 -9
  111. package/dist/core/gthDeepAgentFactory.js.map +0 -1
  112. package/dist/modules/acpModule.d.ts +0 -40
  113. package/dist/modules/acpModule.js +0 -78
  114. package/dist/modules/acpModule.js.map +0 -1
  115. package/dist/tools/shell/allowlist.d.ts +0 -11
  116. package/dist/tools/shell/allowlist.js +0 -12
  117. package/dist/tools/shell/allowlist.js.map +0 -1
  118. package/dist/tools/shell/arity.d.ts +0 -11
  119. package/dist/tools/shell/arity.js +0 -12
  120. package/dist/tools/shell/arity.js.map +0 -1
  121. package/dist/tools/shell/hardline.d.ts +0 -15
  122. package/dist/tools/shell/hardline.js +0 -88
  123. package/dist/tools/shell/hardline.js.map +0 -1
  124. package/dist/tools/shell/normalize.d.ts +0 -10
  125. package/dist/tools/shell/normalize.js +0 -11
  126. package/dist/tools/shell/normalize.js.map +0 -1
@@ -0,0 +1,61 @@
1
+ /**
2
+ * @packageDocumentation
3
+ * Process-global TLS trust for outbound HTTPS, driven by the `tls` config block.
4
+ *
5
+ * Motivation: an `http`-transport MCP server behind a private/corporate CA (e.g. a dev monolith
6
+ * with a self-signed cert) makes Node's global `fetch` reject the connection with
7
+ * `TypeError: fetch failed` (`SELF_SIGNED_CERT_IN_CHAIN`). The only pre-existing workaround was to
8
+ * prepend `NODE_EXTRA_CA_CERTS=<ca.crt>` on every invocation — that env var is read ONCE at Node
9
+ * startup, so it can't be set from config after launch.
10
+ *
11
+ * Node's `fetch` runs on undici and ignores `https.globalAgent`, so the CA must be installed via an
12
+ * undici dispatcher. `@langchain/mcp-adapters` forwards only `serverName`/`headers`/`authProvider`
13
+ * to the SDK transport (it drops the SDK's `requestInit`/`fetch` hooks), so a per-server CA is not
14
+ * reachable through the adapter. The trust is therefore installed **process-globally** via
15
+ * `setGlobalDispatcher`. That is acceptable for adding a CA (additive trust weakens nothing) — but
16
+ * note the insecure latch below is global too, and so affects LLM provider calls, not only MCP.
17
+ */
18
+ import type { GthConfig } from '@gaunt-sloth/core/config.js';
19
+ type TlsConfig = GthConfig['tls'];
20
+ /** A cert path that could not be read, with the reason (surfaced by the installer). */
21
+ export interface TlsCertFailure {
22
+ path: string;
23
+ message: string;
24
+ }
25
+ /** The undici `connect` options computed from a `tls` config, plus what happened while reading. */
26
+ export interface BuiltDispatcher {
27
+ /** Node's default roots FIRST, then successfully-read user certs. `connect.ca` REPLACES the */
28
+ /** default store, so the roots must be included or every public-CA fetch breaks. */
29
+ ca: string[];
30
+ /** `false` disables verification entirely (process-global). Defaults to `true`. */
31
+ rejectUnauthorized: boolean;
32
+ /** How many configured certs were read successfully. */
33
+ loadedCount: number;
34
+ /** Configured certs that could not be read (fail-soft: skipped, not fatal). */
35
+ failures: TlsCertFailure[];
36
+ }
37
+ /**
38
+ * Compute the undici dispatcher options from a `tls` config. Pure aside from the injected
39
+ * `readCert` (so unit tests stub file reads): resolves nothing about the environment itself.
40
+ *
41
+ * Returns `null` only when NOTHING is configured — no `tls` block, or an empty `tls` with no cert
42
+ * entries and verification left at its secure default. When certs ARE configured it always returns
43
+ * the object (even if every read failed), so the caller can surface those `failures` rather than
44
+ * silently swallowing a bad cert path. Whether to actually install is the caller's call:
45
+ * `loadedCount > 0 || !rejectUnauthorized`.
46
+ *
47
+ * `readCert` receives each configured path verbatim and must return the PEM contents or throw.
48
+ */
49
+ export declare function buildDispatcherOptions(tls: TlsConfig, readCert: (path: string) => string, baseRoots?: readonly string[]): BuiltDispatcher | null;
50
+ /**
51
+ * Install the process-global undici dispatcher from `config.tls`, if any. Idempotent (installs at
52
+ * most once per process). Fail-soft: an unreadable cert is warned about and skipped, never fatal.
53
+ * Emits a loud security warning when verification is disabled.
54
+ *
55
+ * Wired into `getMcpClient` before the MCP client is created; because it is global it also covers
56
+ * LLM/tool fetches — which is desired for the CA case and is the danger to flag for the latch.
57
+ */
58
+ export declare function installMcpTlsTrust(config: GthConfig): void;
59
+ /** Test-only: reset the install-once guard between cases. */
60
+ export declare function resetMcpTlsTrustForTests(): void;
61
+ export {};
@@ -0,0 +1,109 @@
1
+ /**
2
+ * @packageDocumentation
3
+ * Process-global TLS trust for outbound HTTPS, driven by the `tls` config block.
4
+ *
5
+ * Motivation: an `http`-transport MCP server behind a private/corporate CA (e.g. a dev monolith
6
+ * with a self-signed cert) makes Node's global `fetch` reject the connection with
7
+ * `TypeError: fetch failed` (`SELF_SIGNED_CERT_IN_CHAIN`). The only pre-existing workaround was to
8
+ * prepend `NODE_EXTRA_CA_CERTS=<ca.crt>` on every invocation — that env var is read ONCE at Node
9
+ * startup, so it can't be set from config after launch.
10
+ *
11
+ * Node's `fetch` runs on undici and ignores `https.globalAgent`, so the CA must be installed via an
12
+ * undici dispatcher. `@langchain/mcp-adapters` forwards only `serverName`/`headers`/`authProvider`
13
+ * to the SDK transport (it drops the SDK's `requestInit`/`fetch` hooks), so a per-server CA is not
14
+ * reachable through the adapter. The trust is therefore installed **process-globally** via
15
+ * `setGlobalDispatcher`. That is acceptable for adding a CA (additive trust weakens nothing) — but
16
+ * note the insecure latch below is global too, and so affects LLM provider calls, not only MCP.
17
+ */
18
+ import { rootCertificates } from 'node:tls';
19
+ import { readFileSync } from 'node:fs';
20
+ import { homedir } from 'node:os';
21
+ import { resolve } from 'node:path';
22
+ import { Agent, setGlobalDispatcher } from 'undici';
23
+ import { getProjectDir } from '@gaunt-sloth/core/utils/systemUtils.js';
24
+ import { displayWarning } from '@gaunt-sloth/core/utils/consoleUtils.js';
25
+ import { debugLog } from '@gaunt-sloth/core/utils/debugUtils.js';
26
+ /**
27
+ * Compute the undici dispatcher options from a `tls` config. Pure aside from the injected
28
+ * `readCert` (so unit tests stub file reads): resolves nothing about the environment itself.
29
+ *
30
+ * Returns `null` only when NOTHING is configured — no `tls` block, or an empty `tls` with no cert
31
+ * entries and verification left at its secure default. When certs ARE configured it always returns
32
+ * the object (even if every read failed), so the caller can surface those `failures` rather than
33
+ * silently swallowing a bad cert path. Whether to actually install is the caller's call:
34
+ * `loadedCount > 0 || !rejectUnauthorized`.
35
+ *
36
+ * `readCert` receives each configured path verbatim and must return the PEM contents or throw.
37
+ */
38
+ export function buildDispatcherOptions(tls, readCert, baseRoots = rootCertificates) {
39
+ const rejectUnauthorized = tls?.rejectUnauthorized ?? true;
40
+ const certPaths = tls?.extraCaCerts ?? [];
41
+ // Nothing configured at all: no cert entries and verification not being disabled.
42
+ if (certPaths.length === 0 && rejectUnauthorized)
43
+ return null;
44
+ const userCerts = [];
45
+ const failures = [];
46
+ for (const path of certPaths) {
47
+ try {
48
+ userCerts.push(readCert(path));
49
+ }
50
+ catch (error) {
51
+ failures.push({ path, message: error instanceof Error ? error.message : String(error) });
52
+ }
53
+ }
54
+ return {
55
+ // ca REPLACES the trust store — keep the default roots or public-CA fetches (incl. the LLM
56
+ // provider) silently break.
57
+ ca: [...baseRoots, ...userCerts],
58
+ rejectUnauthorized,
59
+ loadedCount: userCerts.length,
60
+ failures,
61
+ };
62
+ }
63
+ // Install-once guard: the trust is process-global and set-once, so re-running resolveTools (re-init)
64
+ // must not re-replace the dispatcher nor re-emit the security warning every time.
65
+ let installed = false;
66
+ /**
67
+ * Install the process-global undici dispatcher from `config.tls`, if any. Idempotent (installs at
68
+ * most once per process). Fail-soft: an unreadable cert is warned about and skipped, never fatal.
69
+ * Emits a loud security warning when verification is disabled.
70
+ *
71
+ * Wired into `getMcpClient` before the MCP client is created; because it is global it also covers
72
+ * LLM/tool fetches — which is desired for the CA case and is the danger to flag for the latch.
73
+ */
74
+ export function installMcpTlsTrust(config) {
75
+ if (installed) {
76
+ debugLog('MCP TLS trust already installed this process; skipping.');
77
+ return;
78
+ }
79
+ const built = buildDispatcherOptions(config.tls, resolveAndReadCert);
80
+ if (!built)
81
+ return;
82
+ // Surface unreadable certs even if we end up installing nothing — a bad cert path must never be
83
+ // swallowed silently. Warn BEFORE the "nothing actionable" bail below.
84
+ for (const failure of built.failures) {
85
+ displayWarning(`TLS: could not read CA certificate '${failure.path}' (${failure.message}); skipping it.`);
86
+ }
87
+ // Nothing left to install: every configured cert failed to read and verification is still on.
88
+ // (Failures were already surfaced above.) Leave Node's default dispatcher untouched.
89
+ if (built.loadedCount === 0 && built.rejectUnauthorized)
90
+ return;
91
+ installed = true;
92
+ if (!built.rejectUnauthorized) {
93
+ displayWarning('TLS: certificate verification is DISABLED (tls.rejectUnauthorized: false). This is INSECURE ' +
94
+ 'and applies to ALL outbound HTTPS this process makes — MCP servers AND LLM provider calls. ' +
95
+ 'Use only against trusted dev endpoints; prefer tls.extraCaCerts to trust a specific CA instead.');
96
+ }
97
+ setGlobalDispatcher(new Agent({ connect: { ca: built.ca, rejectUnauthorized: built.rejectUnauthorized } }));
98
+ debugLog(`MCP TLS trust installed: ${built.loadedCount} extra CA cert(s), rejectUnauthorized=${built.rejectUnauthorized}.`);
99
+ }
100
+ /** Resolve a configured cert path (`~` / relative-to-project / absolute) and read it as UTF-8. */
101
+ function resolveAndReadCert(path) {
102
+ const expanded = path === '~' || path.startsWith('~/') ? homedir() + path.slice(1) : path;
103
+ return readFileSync(resolve(getProjectDir(), expanded), 'utf8');
104
+ }
105
+ /** Test-only: reset the install-once guard between cases. */
106
+ export function resetMcpTlsTrustForTests() {
107
+ installed = false;
108
+ }
109
+ //# sourceMappingURL=tlsTrust.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tlsTrust.js","sourceRoot":"","sources":["../../src/mcp/tlsTrust.ts"],"names":[],"mappings":"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"}
@@ -18,5 +18,12 @@ import { type AgentMiddleware } from 'langchain';
18
18
  import type { GthConfig } from '@gaunt-sloth/core/config.js';
19
19
  export interface BinaryContentInjectionMiddlewareSettings {
20
20
  name?: 'binary-content-injection';
21
+ /**
22
+ * The gth provider string selecting the per-provider image-block shape (see
23
+ * {@link imageBlockFor}). Derived by the registry factory via `resolveVisionProvider(gthConfig)`;
24
+ * may be `''`/absent, in which case the standard base64 image block is emitted. Only affects the
25
+ * `image` format — file/video/audio blocks are provider-agnostic.
26
+ */
27
+ provider?: string;
21
28
  }
22
- export declare function createBinaryContentInjectionMiddleware(_settings: BinaryContentInjectionMiddlewareSettings, _gthConfig: GthConfig): Promise<AgentMiddleware>;
29
+ export declare function createBinaryContentInjectionMiddleware(settings: BinaryContentInjectionMiddlewareSettings, _gthConfig: GthConfig): Promise<AgentMiddleware>;
@@ -18,6 +18,7 @@ import { createMiddleware } from 'langchain';
18
18
  import path from 'node:path';
19
19
  import { debugLog } from '@gaunt-sloth/core/utils/debugUtils.js';
20
20
  import { ToolMessage, HumanMessage } from '@langchain/core/messages';
21
+ import { imageBlockFor } from '#src/middleware/frontendImageInjectionMiddleware.js';
21
22
  /**
22
23
  * Parse the special binary format string returned by gth_read_binary tool.
23
24
  * Format: gth_read_binary;type:${type};path:${encodedPath};data:${media_type};base64,${data}
@@ -73,8 +74,10 @@ function getFormatLabel(formatType) {
73
74
  };
74
75
  return labels[formatType] || 'file';
75
76
  }
76
- export function createBinaryContentInjectionMiddleware(_settings, _gthConfig) {
77
+ export function createBinaryContentInjectionMiddleware(settings, _gthConfig) {
77
78
  debugLog('Creating binary content injection middleware');
79
+ // The provider selecting the image-block shape; '' falls to the standard base64 block.
80
+ const provider = settings.provider ?? '';
78
81
  return Promise.resolve(createMiddleware({
79
82
  name: 'binary-content-injection',
80
83
  // Before next model call, detect and inject HumanMessage with binary content
@@ -100,7 +103,13 @@ export function createBinaryContentInjectionMiddleware(_settings, _gthConfig) {
100
103
  const newMessages = [...messages];
101
104
  for (const { binaryData } of binaryMessages) {
102
105
  const formatLabel = getFormatLabel(binaryData.formatType);
103
- const contentBlock = createContentBlock(binaryData);
106
+ // Images go through the per-provider builder so OpenAI reasoning models (Responses API,
107
+ // GS2-74) get a valid `image_url` block instead of the standard `source_type` data block,
108
+ // which @langchain/openai mis-serialises to an invalid Responses image part (GS2-75).
109
+ // file/video/audio keep the standard block (they already convert correctly).
110
+ const contentBlock = binaryData.formatType === 'image'
111
+ ? imageBlockFor(provider, binaryData.media_type, binaryData.data)
112
+ : createContentBlock(binaryData);
104
113
  const humanMessage = new HumanMessage({
105
114
  content: [
106
115
  {
@@ -1 +1 @@
1
- {"version":3,"file":"binaryContentInjectionMiddleware.js","sourceRoot":"","sources":["../../src/middleware/binaryContentInjectionMiddleware.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"binaryContentInjectionMiddleware.js","sourceRoot":"","sources":["../../src/middleware/binaryContentInjectionMiddleware.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,107 @@
1
+ /**
2
+ * @packageDocumentation
3
+ * Opt-in middleware that turns a frontend "capture image" tool result into a vision message the
4
+ * model can actually see.
5
+ *
6
+ * A browser/frontend tool such as CopilotKit's `capture_image` fulfils client-side and posts its
7
+ * result back to gth's AG-UI server as a trailing `tool`-role message whose content is a JSON
8
+ * string `{"mimeType":"image/...","data":"<base64>"}`. gth hands that to the model as a plain-string
9
+ * ToolMessage — no vision block — so the model literally cannot see the photo. This middleware
10
+ * detects that ToolMessage in `beforeModel`, parses the envelope, and injects a `HumanMessage`
11
+ * carrying a provider-appropriate vision block before the next model call.
12
+ *
13
+ * Strictly opt-in: it fires ONLY when referenced by name in `config.middleware`
14
+ * (`"middleware": ["frontend-image-injection"]`), never auto-injected. Promoted from
15
+ * pukeko-robot-controller's proven middleware (RC-21), minus the robot-specific motion-tool coupling.
16
+ */
17
+ import { type AgentMiddleware } from 'langchain';
18
+ /** Default frontend capture tool name. Overridable via the `toolName` middleware setting. */
19
+ export declare const DEFAULT_CAPTURE_TOOL_NAME = "capture_image";
20
+ export interface FrontendImageInjectionOptions {
21
+ /**
22
+ * Provider selecting the vision-block shape. Derived by the registry factory from
23
+ * `gthConfig.modelProviderType` (the raw `llm.type`), falling back to `llm._llmType()`. One of
24
+ * gth's provider strings (`anthropic`, `openai`, `openrouter`, `deepseek`, `xai`, `groq`,
25
+ * `ollama`, `google-genai`, `vertexai`, `huggingface`, …); unknown values fall to the standard
26
+ * base64 block.
27
+ */
28
+ provider: string;
29
+ /**
30
+ * Tool name whose `{mimeType,data}` result is converted into a vision message. Defaults to
31
+ * {@link DEFAULT_CAPTURE_TOOL_NAME} (`capture_image`).
32
+ */
33
+ toolName?: string;
34
+ }
35
+ /**
36
+ * A vision content block the target provider's `@langchain` converter actually decodes. Verified
37
+ * against the installed converters (RC-21):
38
+ * - **ollama** → `{ type:'image_url', image_url:'<data-URL string>' }`. ChatOllama's
39
+ * `convertToOllamaMessages` only handles `image_url` blocks (extractBase64FromDataUrl); the
40
+ * LangChain standard `source_type` block throws "Unsupported content type: image".
41
+ * - **OpenAI-compatible** (`openai`, `openrouter`, `deepseek`, `xai`, `groq` — served by an
42
+ * OpenAI-compatible Chat Completions API, whichever LangChain class fronts it) →
43
+ * `{ type:'image_url', image_url:{ url:'<data-URL>' } }`.
44
+ * This native OpenAI shape is correct on BOTH the Completions API AND the Responses API (GS2-74
45
+ * flips reasoning-capable openai models to Responses). A raw `source_type` standard block
46
+ * serialises to an *invalid* image part on the Responses path, so we emit the provider-native
47
+ * shape rather than lean on `@langchain/core`'s (deprecated, internal) auto-conversion.
48
+ * - **anthropic** → the provider-native block `{ type:'image', source:{ type:'base64',
49
+ * media_type, data } }`. The LangChain standard block is NOT usable here (RC-32): in
50
+ * `@langchain/anthropic`'s `_formatContentBlocks`, the `isDataContentBlock` branch yields its
51
+ * conversion and then FALLS THROUGH — no `continue` — into the chain below, where
52
+ * `type === 'image'` matches the very same block and yields a SECOND one whose `media_type` is
53
+ * read from camelCase `mimeType` (a key the snake_case standard block never has) and so
54
+ * defaults to the literal `image/jpeg`. Every frame is therefore sent twice, and a non-JPEG
55
+ * capture 400s outright on the mislabelled copy. The native block is recognised earlier by
56
+ * `_isAnthropicImageBlockParam` and passed through untouched, exactly once.
57
+ * - **google-genai / vertexai** (and any unknown/default) → the LangChain standard base64 data
58
+ * content block `{ type:'image', source_type:'base64', mime_type, data }`, which those native
59
+ * converters decode directly and which is the most broadly decodable fallback.
60
+ *
61
+ * The through-line: emit what the target provider's converter consumes natively rather than lean on
62
+ * a generic auto-conversion — the same lesson as GS2-75 on the OpenAI Responses path.
63
+ *
64
+ * Pure and exported so each provider branch can be unit-tested directly.
65
+ */
66
+ export declare function imageBlockFor(provider: string, mimeType: string, data: string): {
67
+ type: 'image_url';
68
+ image_url: string;
69
+ source?: undefined;
70
+ source_type?: undefined;
71
+ mime_type?: undefined;
72
+ data?: undefined;
73
+ } | {
74
+ type: 'image_url';
75
+ image_url: {
76
+ url: string;
77
+ };
78
+ source?: undefined;
79
+ source_type?: undefined;
80
+ mime_type?: undefined;
81
+ data?: undefined;
82
+ } | {
83
+ image_url?: undefined;
84
+ type: 'image';
85
+ source: {
86
+ type: 'base64';
87
+ media_type: string;
88
+ data: string;
89
+ };
90
+ source_type?: undefined;
91
+ mime_type?: undefined;
92
+ data?: undefined;
93
+ } | {
94
+ image_url?: undefined;
95
+ source?: undefined;
96
+ type: 'image';
97
+ source_type: 'base64';
98
+ mime_type: string;
99
+ data: string;
100
+ };
101
+ /**
102
+ * Create the frontend-image-injection middleware.
103
+ *
104
+ * @param opts.provider - provider string selecting the vision-block shape (see {@link imageBlockFor}).
105
+ * @param opts.toolName - capture tool name (default {@link DEFAULT_CAPTURE_TOOL_NAME}).
106
+ */
107
+ export declare function createFrontendImageInjectionMiddleware(opts: FrontendImageInjectionOptions): AgentMiddleware;
@@ -0,0 +1,167 @@
1
+ /**
2
+ * @packageDocumentation
3
+ * Opt-in middleware that turns a frontend "capture image" tool result into a vision message the
4
+ * model can actually see.
5
+ *
6
+ * A browser/frontend tool such as CopilotKit's `capture_image` fulfils client-side and posts its
7
+ * result back to gth's AG-UI server as a trailing `tool`-role message whose content is a JSON
8
+ * string `{"mimeType":"image/...","data":"<base64>"}`. gth hands that to the model as a plain-string
9
+ * ToolMessage — no vision block — so the model literally cannot see the photo. This middleware
10
+ * detects that ToolMessage in `beforeModel`, parses the envelope, and injects a `HumanMessage`
11
+ * carrying a provider-appropriate vision block before the next model call.
12
+ *
13
+ * Strictly opt-in: it fires ONLY when referenced by name in `config.middleware`
14
+ * (`"middleware": ["frontend-image-injection"]`), never auto-injected. Promoted from
15
+ * pukeko-robot-controller's proven middleware (RC-21), minus the robot-specific motion-tool coupling.
16
+ */
17
+ import { createMiddleware } from 'langchain';
18
+ import { HumanMessage, isToolMessage } from '@langchain/core/messages';
19
+ /** Default frontend capture tool name. Overridable via the `toolName` middleware setting. */
20
+ export const DEFAULT_CAPTURE_TOOL_NAME = 'capture_image';
21
+ /**
22
+ * A vision content block the target provider's `@langchain` converter actually decodes. Verified
23
+ * against the installed converters (RC-21):
24
+ * - **ollama** → `{ type:'image_url', image_url:'<data-URL string>' }`. ChatOllama's
25
+ * `convertToOllamaMessages` only handles `image_url` blocks (extractBase64FromDataUrl); the
26
+ * LangChain standard `source_type` block throws "Unsupported content type: image".
27
+ * - **OpenAI-compatible** (`openai`, `openrouter`, `deepseek`, `xai`, `groq` — served by an
28
+ * OpenAI-compatible Chat Completions API, whichever LangChain class fronts it) →
29
+ * `{ type:'image_url', image_url:{ url:'<data-URL>' } }`.
30
+ * This native OpenAI shape is correct on BOTH the Completions API AND the Responses API (GS2-74
31
+ * flips reasoning-capable openai models to Responses). A raw `source_type` standard block
32
+ * serialises to an *invalid* image part on the Responses path, so we emit the provider-native
33
+ * shape rather than lean on `@langchain/core`'s (deprecated, internal) auto-conversion.
34
+ * - **anthropic** → the provider-native block `{ type:'image', source:{ type:'base64',
35
+ * media_type, data } }`. The LangChain standard block is NOT usable here (RC-32): in
36
+ * `@langchain/anthropic`'s `_formatContentBlocks`, the `isDataContentBlock` branch yields its
37
+ * conversion and then FALLS THROUGH — no `continue` — into the chain below, where
38
+ * `type === 'image'` matches the very same block and yields a SECOND one whose `media_type` is
39
+ * read from camelCase `mimeType` (a key the snake_case standard block never has) and so
40
+ * defaults to the literal `image/jpeg`. Every frame is therefore sent twice, and a non-JPEG
41
+ * capture 400s outright on the mislabelled copy. The native block is recognised earlier by
42
+ * `_isAnthropicImageBlockParam` and passed through untouched, exactly once.
43
+ * - **google-genai / vertexai** (and any unknown/default) → the LangChain standard base64 data
44
+ * content block `{ type:'image', source_type:'base64', mime_type, data }`, which those native
45
+ * converters decode directly and which is the most broadly decodable fallback.
46
+ *
47
+ * The through-line: emit what the target provider's converter consumes natively rather than lean on
48
+ * a generic auto-conversion — the same lesson as GS2-75 on the OpenAI Responses path.
49
+ *
50
+ * Pure and exported so each provider branch can be unit-tested directly.
51
+ */
52
+ export function imageBlockFor(provider, mimeType, data) {
53
+ const dataUrl = `data:${mimeType};base64,${data}`;
54
+ switch (provider) {
55
+ case 'ollama':
56
+ return { type: 'image_url', image_url: dataUrl };
57
+ case 'openai':
58
+ case 'openrouter':
59
+ case 'deepseek':
60
+ case 'xai':
61
+ case 'groq':
62
+ return { type: 'image_url', image_url: { url: dataUrl } };
63
+ case 'anthropic':
64
+ return {
65
+ type: 'image',
66
+ source: { type: 'base64', media_type: mimeType, data },
67
+ };
68
+ case 'google-genai':
69
+ case 'vertexai':
70
+ default:
71
+ return {
72
+ type: 'image',
73
+ source_type: 'base64',
74
+ mime_type: mimeType,
75
+ data,
76
+ };
77
+ }
78
+ }
79
+ /**
80
+ * Create the frontend-image-injection middleware.
81
+ *
82
+ * @param opts.provider - provider string selecting the vision-block shape (see {@link imageBlockFor}).
83
+ * @param opts.toolName - capture tool name (default {@link DEFAULT_CAPTURE_TOOL_NAME}).
84
+ */
85
+ export function createFrontendImageInjectionMiddleware(opts) {
86
+ const toolName = opts.toolName ?? DEFAULT_CAPTURE_TOOL_NAME;
87
+ // thread_id → set of tool_call_ids whose image (or error note) has already been injected. It
88
+ // scopes injection to ONE frame per capture per graph thread: `beforeModel` runs before every
89
+ // model call, so a multi-step turn would otherwise stack a copy of the same frame at each step,
90
+ // each one appended after the newest content and mispairing the assistant message with a stale
91
+ // frame (the RC-21 idempotency guard). It is deliberately keyed on the GRAPH thread rather than
92
+ // the client session — the AG-UI server gives each fresh run its own checkpoint thread (RC-31),
93
+ // and that run replays the whole history into an empty graph, so it must re-inject the frame or
94
+ // the model would lose the photo the moment the capture turn ended.
95
+ //
96
+ // Closure-scoped (one Map per middleware instance), NOT a module global as the robot had it. The
97
+ // AG-UI server caches one agent per client-toolset signature (getAgentForTools), so a thread's
98
+ // turns share this instance and idempotency-across-turns is preserved — while unrelated agents (and
99
+ // tests) get independent state instead of a process-lifetime Map that accumulates every thread_id.
100
+ const injectedByThread = new Map();
101
+ return createMiddleware({
102
+ name: 'frontend-image-injection',
103
+ beforeModel: async (state, runtime) => {
104
+ const messages = state.messages || [];
105
+ // The AG-UI server sets runConfig.configurable.thread_id (= the run's threadId) and threads it
106
+ // to the graph, so per-session idempotency keys correctly. '__default__' is only reached off
107
+ // that path (e.g. a bare invoke with no thread_id), which still behaves correctly per-instance.
108
+ const threadId = runtime?.configurable?.thread_id ?? '__default__';
109
+ let injectedIds = injectedByThread.get(threadId);
110
+ if (!injectedIds) {
111
+ injectedIds = new Set();
112
+ injectedByThread.set(threadId, injectedIds);
113
+ }
114
+ // Scan forward so injected frames stay in chronological order; skip any tool_call_id already
115
+ // injected on this thread (idempotent across a retained/replayed tail).
116
+ const injected = [];
117
+ for (let i = 0; i < messages.length; i++) {
118
+ const msg = messages[i];
119
+ if (
120
+ // RC-21 (golden fix): use the duck-typed `isToolMessage`, NEVER `msg instanceof
121
+ // ToolMessage`. A consumer (galvanized/robot) importing this middleware across a `file:`-dep
122
+ // boundary resolves a SECOND @langchain/core copy, and a capture ToolMessage constructed by
123
+ // the AG-UI pipeline's core copy is not an instance of the `ToolMessage` class we import —
124
+ // `instanceof` silently returns false across copies and no frame is ever injected.
125
+ isToolMessage(msg) &&
126
+ typeof msg.content === 'string' &&
127
+ msg.name === toolName) {
128
+ const id = msg.tool_call_id;
129
+ if (!id || injectedIds.has(id))
130
+ continue;
131
+ try {
132
+ injected.push({ payload: JSON.parse(msg.content), id });
133
+ }
134
+ catch {
135
+ // Non-JSON tool result — skip injection (leave the guard clean).
136
+ }
137
+ }
138
+ }
139
+ if (injected.length === 0)
140
+ return undefined;
141
+ const newMessages = [...messages];
142
+ for (const { payload, id } of injected) {
143
+ if (payload.error) {
144
+ // Mark injected so the error note isn't re-emitted on a later turn.
145
+ injectedIds.add(id);
146
+ newMessages.push(new HumanMessage({ content: `Camera unavailable: ${payload.error}` }));
147
+ continue;
148
+ }
149
+ if (payload.mimeType && payload.data) {
150
+ // RC-21: mark injected ONLY on a successful frame emission. A capture result that arrived
151
+ // WITHOUT its base64 `data` (dropped upstream) injects nothing and must NOT poison the
152
+ // guard — a later data-bearing result for the same tool_call_id can still recover.
153
+ injectedIds.add(id);
154
+ const block = imageBlockFor(opts.provider, payload.mimeType, payload.data);
155
+ newMessages.push(new HumanMessage({
156
+ content: [{ type: 'text', text: 'Camera frame captured:' }, block],
157
+ }));
158
+ }
159
+ // else: a capture result whose `data` is absent — inject nothing, leave the guard clean.
160
+ }
161
+ // If every candidate was data-less (nothing appended), return no state update rather than an
162
+ // identical copy — keeps beforeModel a true no-op and leaves the guard clean for recovery.
163
+ return newMessages.length > messages.length ? { messages: newMessages } : undefined;
164
+ },
165
+ });
166
+ }
167
+ //# sourceMappingURL=frontendImageInjectionMiddleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frontendImageInjectionMiddleware.js","sourceRoot":"","sources":["../../src/middleware/frontendImageInjectionMiddleware.ts"],"names":[],"mappings":"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"}
@@ -9,6 +9,41 @@ import { displayWarning } from '@gaunt-sloth/core/utils/consoleUtils.js';
9
9
  import { debugLog } from '@gaunt-sloth/core/utils/debugUtils.js';
10
10
  import { anthropicPromptCachingMiddleware, summarizationMiddleware, } from 'langchain';
11
11
  import { createBinaryContentInjectionMiddleware, } from '#src/middleware/binaryContentInjectionMiddleware.js';
12
+ import { createFrontendImageInjectionMiddleware } from '#src/middleware/frontendImageInjectionMiddleware.js';
13
+ /**
14
+ * Derive the provider string the vision middleware maps to a per-provider block shape. Prefers the
15
+ * loader-stashed raw `llm.type` ({@link GthConfig.modelProviderType}) — the exact gth provider
16
+ * namespace (`anthropic`/`openrouter`/`deepseek`/`xai`/`groq`/`ollama`/`google-genai`/`vertexai`/…)
17
+ * — and falls back to the live model's `_llmType()` only when it is absent (module configs).
18
+ * `_llmType()` is the model class's own label rather than the gth provider namespace, so the two
19
+ * only mostly agree. Where they do: `openrouter`/`deepseek`/`xai`/`groq`/`ollama`/`anthropic` each
20
+ * report their own name, which `imageBlockFor` matches directly, and both Gemini providers report
21
+ * `google`, which lands on the default branch — the same standard block its `google-genai`/
22
+ * `vertexai` cases return.
23
+ *
24
+ * Two labels do NOT agree, and this fallback is the only path either can arrive on:
25
+ * - `huggingface` reports `openai`, so the fallback emits `image_url:{url}` where the configured
26
+ * `type` would take the default standard block. Which of the two the HF router actually accepts
27
+ * is unmeasured — see [[CFG-45]]. Do not add a branch by analogy.
28
+ * - `ChatXAIResponses` reports `xai-responses`, which matches no case and so takes the default
29
+ * standard block — while `imageBlockFor`'s own docstring records that a standard block
30
+ * serialises to an INVALID image part on the Responses path. gth itself only ever constructs
31
+ * `ChatXAI`, so this is reachable only from a module config that builds the Responses class
32
+ * itself.
33
+ */
34
+ function resolveVisionProvider(gthConfig) {
35
+ if (gthConfig.modelProviderType)
36
+ return gthConfig.modelProviderType;
37
+ const llm = gthConfig.llm;
38
+ try {
39
+ if (typeof llm?._llmType === 'function')
40
+ return llm._llmType();
41
+ }
42
+ catch {
43
+ // A misbehaving _llmType must not break middleware resolution.
44
+ }
45
+ return '';
46
+ }
12
47
  const predefinedMiddlewareFactories = {
13
48
  /**
14
49
  * Anthropic prompt caching middleware. see https://docs.langchain.com/oss/javascript/langchain/middleware#anthropic-prompt-caching
@@ -24,7 +59,20 @@ const predefinedMiddlewareFactories = {
24
59
  * as HumanMessage content blocks before the next model call.
25
60
  * This works around LangChain's limitation where ToolMessage doesn't support binary content.
26
61
  */
27
- 'binary-content-injection': (settings, gthConfig) => createBinaryContentInjectionMiddleware(settings, gthConfig),
62
+ 'binary-content-injection': (settings, gthConfig) => createBinaryContentInjectionMiddleware({
63
+ ...settings,
64
+ provider: resolveVisionProvider(gthConfig),
65
+ }, gthConfig),
66
+ /**
67
+ * Frontend image injection middleware (RC-22). Converts a frontend capture tool's
68
+ * `{mimeType,data}` ToolMessage (default tool name `capture_image`) into a provider-appropriate
69
+ * vision HumanMessage the model can see. Strictly opt-in — resolved ONLY when named in
70
+ * `config.middleware` (no auto-inject branch). Optional `toolName` setting overrides the tool name.
71
+ */
72
+ 'frontend-image-injection': (settings, gthConfig) => Promise.resolve(createFrontendImageInjectionMiddleware({
73
+ provider: resolveVisionProvider(gthConfig),
74
+ toolName: typeof settings.toolName === 'string' ? settings.toolName : undefined,
75
+ })),
28
76
  };
29
77
  function isPredefinedMiddlewareName(name) {
30
78
  return name in predefinedMiddlewareFactories;
@@ -1 +1 @@
1
- {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/middleware/registry.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/middleware/registry.ts"],"names":[],"mappings":"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"}