glirastes 0.3.0 → 0.3.1
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/CHANGELOG.md +63 -0
- package/README.md +146 -9
- package/codegen/package.json +5 -0
- package/dist/cjs/codegen/asset-sync.d.ts +20 -0
- package/dist/cjs/codegen/asset-sync.js +28 -0
- package/dist/cjs/codegen/coverage.d.ts +66 -0
- package/dist/cjs/codegen/coverage.js +408 -0
- package/dist/cjs/codegen/endpoint-scanner.d.ts +16 -0
- package/dist/cjs/codegen/endpoint-scanner.js +197 -0
- package/dist/cjs/codegen/format.d.ts +17 -0
- package/dist/cjs/codegen/format.js +34 -0
- package/dist/cjs/codegen/fs-utils.d.ts +45 -0
- package/dist/cjs/codegen/fs-utils.js +165 -0
- package/dist/cjs/codegen/index.d.ts +13 -0
- package/dist/cjs/codegen/index.js +33 -0
- package/dist/cjs/codegen/mcp-generator.d.ts +50 -0
- package/dist/cjs/codegen/mcp-generator.js +803 -0
- package/dist/cjs/codegen/module-generator.d.ts +11 -0
- package/dist/cjs/codegen/module-generator.js +353 -0
- package/dist/cjs/codegen/nestjs-scanner.d.ts +43 -0
- package/dist/cjs/codegen/nestjs-scanner.js +241 -0
- package/dist/cjs/codegen/output-recommender.d.ts +23 -0
- package/dist/cjs/codegen/output-recommender.js +33 -0
- package/dist/cjs/codegen/scaffolder.d.ts +36 -0
- package/dist/cjs/codegen/scaffolder.js +262 -0
- package/dist/cjs/codegen/skill-generator.d.ts +49 -0
- package/dist/cjs/codegen/skill-generator.js +967 -0
- package/dist/cjs/codegen/types.d.ts +52 -0
- package/dist/cjs/codegen/types.js +16 -0
- package/dist/cjs/codegen/ui-scanner.d.ts +24 -0
- package/dist/cjs/codegen/ui-scanner.js +202 -0
- package/dist/cjs/codegen/validator.d.ts +35 -0
- package/dist/cjs/codegen/validator.js +292 -0
- package/dist/cjs/index.d.ts +1 -0
- package/dist/cjs/index.js +23 -0
- package/dist/cjs/openapi/index.d.ts +78 -0
- package/dist/cjs/openapi/index.js +347 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/react/audio-worklet-processor.d.ts +16 -0
- package/dist/cjs/react/audio-worklet-processor.js +27 -0
- package/dist/cjs/react/bridges/rest-session-bridge.d.ts +113 -0
- package/dist/cjs/react/bridges/rest-session-bridge.js +209 -0
- package/dist/cjs/react/components/ai-chat-panel.d.ts +12 -0
- package/dist/cjs/react/components/ai-chat-panel.js +67 -0
- package/dist/cjs/react/components/ai-trigger-button.d.ts +2 -0
- package/dist/cjs/react/components/ai-trigger-button.js +239 -0
- package/dist/cjs/react/components/approval-card.d.ts +9 -0
- package/dist/cjs/react/components/approval-card.js +24 -0
- package/dist/cjs/react/components/bulk-approval-card.d.ts +9 -0
- package/dist/cjs/react/components/bulk-approval-card.js +36 -0
- package/dist/cjs/react/components/chat-input.d.ts +10 -0
- package/dist/cjs/react/components/chat-input.js +60 -0
- package/dist/cjs/react/components/chat-window.d.ts +2 -0
- package/dist/cjs/react/components/chat-window.js +134 -0
- package/dist/cjs/react/components/confirm-dialog.d.ts +32 -0
- package/dist/cjs/react/components/confirm-dialog.js +48 -0
- package/dist/cjs/react/components/floating-chat-ui.d.ts +90 -0
- package/dist/cjs/react/components/floating-chat-ui.js +163 -0
- package/dist/cjs/react/components/mention-chip.d.ts +16 -0
- package/dist/cjs/react/components/mention-chip.js +42 -0
- package/dist/cjs/react/components/mention-input.d.ts +8 -0
- package/dist/cjs/react/components/mention-input.js +117 -0
- package/dist/cjs/react/components/message-bubble.d.ts +11 -0
- package/dist/cjs/react/components/message-bubble.js +171 -0
- package/dist/cjs/react/components/message-list.d.ts +8 -0
- package/dist/cjs/react/components/message-list.js +52 -0
- package/dist/cjs/react/components/pipeline-timeline.d.ts +7 -0
- package/dist/cjs/react/components/pipeline-timeline.js +72 -0
- package/dist/cjs/react/components/recording-bar.d.ts +2 -0
- package/dist/cjs/react/components/recording-bar.js +81 -0
- package/dist/cjs/react/components/recording-controls.d.ts +18 -0
- package/dist/cjs/react/components/recording-controls.js +38 -0
- package/dist/cjs/react/components/rich-mention-input.d.ts +2 -0
- package/dist/cjs/react/components/rich-mention-input.js +571 -0
- package/dist/cjs/react/components/session-switcher.d.ts +14 -0
- package/dist/cjs/react/components/session-switcher.js +133 -0
- package/dist/cjs/react/components/suggestion-bar.d.ts +9 -0
- package/dist/cjs/react/components/suggestion-bar.js +47 -0
- package/dist/cjs/react/components/suggestion-chip.d.ts +9 -0
- package/dist/cjs/react/components/suggestion-chip.js +14 -0
- package/dist/cjs/react/components/voice-input-addon.d.ts +13 -0
- package/dist/cjs/react/components/voice-input-addon.js +31 -0
- package/dist/cjs/react/components/voice-input-button.d.ts +2 -0
- package/dist/cjs/react/components/voice-input-button.js +55 -0
- package/dist/cjs/react/components/wavesurfer-loader.d.ts +5 -0
- package/dist/cjs/react/components/wavesurfer-loader.js +36 -0
- package/dist/cjs/react/hooks/use-ai-chat.d.ts +22 -0
- package/dist/cjs/react/hooks/use-ai-chat.js +51 -0
- package/dist/cjs/react/hooks/use-approvals.d.ts +23 -0
- package/dist/cjs/react/hooks/use-approvals.js +63 -0
- package/dist/cjs/react/hooks/use-deepgram-transcription.d.ts +2 -0
- package/dist/cjs/react/hooks/use-deepgram-transcription.js +329 -0
- package/dist/cjs/react/hooks/use-drag-resize-panel.d.ts +48 -0
- package/dist/cjs/react/hooks/use-drag-resize-panel.js +308 -0
- package/dist/cjs/react/hooks/use-draggable-position.d.ts +25 -0
- package/dist/cjs/react/hooks/use-draggable-position.js +134 -0
- package/dist/cjs/react/hooks/use-keyboard-shortcut.d.ts +20 -0
- package/dist/cjs/react/hooks/use-keyboard-shortcut.js +93 -0
- package/dist/cjs/react/hooks/use-mentions.d.ts +23 -0
- package/dist/cjs/react/hooks/use-mentions.js +42 -0
- package/dist/cjs/react/hooks/use-suggestions.d.ts +22 -0
- package/dist/cjs/react/hooks/use-suggestions.js +39 -0
- package/dist/cjs/react/index.d.ts +43 -0
- package/dist/cjs/react/index.js +111 -0
- package/dist/cjs/react/provider/ai-chat-provider.d.ts +45 -0
- package/dist/cjs/react/provider/ai-chat-provider.js +1024 -0
- package/dist/cjs/react/provider/chat-context.d.ts +84 -0
- package/dist/cjs/react/provider/chat-context.js +13 -0
- package/dist/cjs/react/template.d.ts +84 -0
- package/dist/cjs/react/template.js +90 -0
- package/dist/cjs/react/themes.d.ts +24 -0
- package/dist/cjs/react/themes.js +273 -0
- package/dist/cjs/react/transports/langgraph/index.d.ts +2 -0
- package/dist/cjs/react/transports/langgraph/index.js +8 -0
- package/dist/cjs/react/transports/langgraph/langgraph-ai-chat.d.ts +62 -0
- package/dist/cjs/react/transports/langgraph/langgraph-ai-chat.js +94 -0
- package/dist/cjs/react/transports/langgraph.d.ts +71 -0
- package/dist/cjs/react/transports/langgraph.js +369 -0
- package/dist/cjs/react/transports/vercel/index.d.ts +2 -0
- package/dist/cjs/react/transports/vercel/index.js +8 -0
- package/dist/cjs/react/transports/vercel/vercel-ai-chat.d.ts +49 -0
- package/dist/cjs/react/transports/vercel/vercel-ai-chat.js +81 -0
- package/dist/cjs/react/transports/vercel.d.ts +30 -0
- package/dist/cjs/react/transports/vercel.js +69 -0
- package/dist/cjs/react/types.d.ts +760 -0
- package/dist/cjs/react/types.js +2 -0
- package/dist/cjs/react/ui.d.ts +73 -0
- package/dist/cjs/react/ui.js +183 -0
- package/dist/cjs/react/utils/create-approval-description.d.ts +53 -0
- package/dist/cjs/react/utils/create-approval-description.js +122 -0
- package/dist/cjs/react/utils/create-tool-result-handler.d.ts +90 -0
- package/dist/cjs/react/utils/create-tool-result-handler.js +168 -0
- package/dist/cjs/react/utils/mention-markup.d.ts +13 -0
- package/dist/cjs/react/utils/mention-markup.js +117 -0
- package/dist/cjs/react/utils/merge-pipeline-steps.d.ts +19 -0
- package/dist/cjs/react/utils/merge-pipeline-steps.js +81 -0
- package/dist/cjs/react/utils/sanitize-stored-messages.d.ts +23 -0
- package/dist/cjs/react/utils/sanitize-stored-messages.js +52 -0
- package/dist/cjs/server/adapters/nestjs/chat/ai-chat-auth.guard.d.ts +10 -0
- package/dist/cjs/server/adapters/nestjs/chat/ai-chat-auth.guard.js +42 -0
- package/dist/cjs/server/adapters/nestjs/chat/ai-chat-module-options.interface.d.ts +32 -0
- package/dist/cjs/server/adapters/nestjs/chat/ai-chat-module-options.interface.js +4 -0
- package/dist/cjs/server/adapters/nestjs/chat/ai-chat.controller.d.ts +11 -0
- package/dist/cjs/server/adapters/nestjs/chat/ai-chat.controller.js +80 -0
- package/dist/cjs/server/adapters/nestjs/chat/ai-chat.module.d.ts +9 -0
- package/dist/cjs/server/adapters/nestjs/chat/ai-chat.module.js +119 -0
- package/dist/cjs/server/adapters/nestjs/chat/ai-chat.service.d.ts +13 -0
- package/dist/cjs/server/adapters/nestjs/chat/ai-chat.service.js +131 -0
- package/dist/cjs/server/adapters/nestjs/chat/dto/ai-chat-request.dto.d.ts +7 -0
- package/dist/cjs/server/adapters/nestjs/chat/dto/ai-chat-request.dto.js +52 -0
- package/dist/cjs/server/adapters/nestjs/chat/dto/chat-message-part.dto.d.ts +19 -0
- package/dist/cjs/server/adapters/nestjs/chat/dto/chat-message-part.dto.js +108 -0
- package/dist/cjs/server/adapters/nestjs/chat/dto/chat-message.dto.d.ts +8 -0
- package/dist/cjs/server/adapters/nestjs/chat/dto/chat-message.dto.js +59 -0
- package/dist/cjs/server/adapters/nestjs/chat/dto/index.d.ts +3 -0
- package/dist/cjs/server/adapters/nestjs/chat/dto/index.js +9 -0
- package/dist/cjs/server/adapters/nestjs/chat/index.d.ts +3 -0
- package/dist/cjs/server/adapters/nestjs/chat/index.js +9 -0
- package/dist/cjs/server/adapters/nestjs/chat/speech-stream.gateway.d.ts +4 -0
- package/dist/cjs/server/adapters/nestjs/chat/speech-stream.gateway.js +73 -0
- package/dist/cjs/server/adapters/nestjs/chat/ws-loader.d.ts +5 -0
- package/dist/cjs/server/adapters/nestjs/chat/ws-loader.js +72 -0
- package/dist/cjs/server/adapters/nestjs/decorators/ai-module.decorator.d.ts +6 -0
- package/dist/cjs/server/adapters/nestjs/decorators/ai-module.decorator.js +13 -0
- package/dist/cjs/server/adapters/nestjs/decorators/ai-param.decorator.d.ts +5 -0
- package/dist/cjs/server/adapters/nestjs/decorators/ai-param.decorator.js +15 -0
- package/dist/cjs/server/adapters/nestjs/decorators/ai-tool.decorator.d.ts +6 -0
- package/dist/cjs/server/adapters/nestjs/decorators/ai-tool.decorator.js +22 -0
- package/dist/cjs/server/adapters/nestjs/decorators/constants.d.ts +4 -0
- package/dist/cjs/server/adapters/nestjs/decorators/constants.js +7 -0
- package/dist/cjs/server/adapters/nestjs/index.d.ts +14 -0
- package/dist/cjs/server/adapters/nestjs/index.js +42 -0
- package/dist/cjs/server/adapters/nestjs/metadata.d.ts +39 -0
- package/dist/cjs/server/adapters/nestjs/metadata.js +36 -0
- package/dist/cjs/server/adapters/nestjs/module/ai-tools-explorer.service.d.ts +36 -0
- package/dist/cjs/server/adapters/nestjs/module/ai-tools-explorer.service.js +159 -0
- package/dist/cjs/server/adapters/nestjs/module/ai-tools.module.d.ts +37 -0
- package/dist/cjs/server/adapters/nestjs/module/ai-tools.module.js +44 -0
- package/dist/cjs/server/adapters/nestjs/module/ai-tools.service.d.ts +28 -0
- package/dist/cjs/server/adapters/nestjs/module/ai-tools.service.js +41 -0
- package/dist/cjs/server/adapters/nestjs/scanner/nestjs-scanner.d.ts +16 -0
- package/dist/cjs/server/adapters/nestjs/scanner/nestjs-scanner.js +117 -0
- package/dist/cjs/server/adapters/nestjs/scanner/schema-builder.d.ts +26 -0
- package/dist/cjs/server/adapters/nestjs/scanner/schema-builder.js +259 -0
- package/dist/cjs/server/adapters/nestjs/swagger.d.ts +7 -0
- package/dist/cjs/server/adapters/nestjs/swagger.js +39 -0
- package/dist/cjs/server/adapters/nextjs/context-helpers.d.ts +22 -0
- package/dist/cjs/server/adapters/nextjs/context-helpers.js +60 -0
- package/dist/cjs/server/adapters/nextjs/create-ai-chat-handler.d.ts +94 -0
- package/dist/cjs/server/adapters/nextjs/create-ai-chat-handler.js +965 -0
- package/dist/cjs/server/adapters/nextjs/filter-fallback-error.d.ts +11 -0
- package/dist/cjs/server/adapters/nextjs/filter-fallback-error.js +41 -0
- package/dist/cjs/server/adapters/nextjs/index.d.ts +8 -0
- package/dist/cjs/server/adapters/nextjs/index.js +22 -0
- package/dist/cjs/server/adapters/nextjs/prepare-model-messages.d.ts +58 -0
- package/dist/cjs/server/adapters/nextjs/prepare-model-messages.js +114 -0
- package/dist/cjs/server/adapters/nextjs/sanitize-model-messages.d.ts +64 -0
- package/dist/cjs/server/adapters/nextjs/sanitize-model-messages.js +489 -0
- package/dist/cjs/server/adapters/nextjs/server-action-helper.d.ts +7 -0
- package/dist/cjs/server/adapters/nextjs/server-action-helper.js +40 -0
- package/dist/cjs/server/adapters/nextjs/types.d.ts +246 -0
- package/dist/cjs/server/adapters/nextjs/types.js +2 -0
- package/dist/cjs/server/adapters/nextjs/with-ai-tool.d.ts +9 -0
- package/dist/cjs/server/adapters/nextjs/with-ai-tool.js +12 -0
- package/dist/cjs/server/core/followup-tool.d.ts +9 -0
- package/dist/cjs/server/core/followup-tool.js +82 -0
- package/dist/cjs/server/core/index.d.ts +139 -0
- package/dist/cjs/server/core/index.js +818 -0
- package/dist/cjs/server/core/intent-classifier.d.ts +94 -0
- package/dist/cjs/server/core/intent-classifier.js +324 -0
- package/dist/cjs/server/core/model-selector.d.ts +12 -0
- package/dist/cjs/server/core/model-selector.js +48 -0
- package/dist/cjs/server/core/module-router.d.ts +14 -0
- package/dist/cjs/server/core/module-router.js +110 -0
- package/dist/cjs/server/core/zod-compact.d.ts +18 -0
- package/dist/cjs/server/core/zod-compact.js +129 -0
- package/dist/cjs/server/index.d.ts +6 -0
- package/dist/cjs/server/index.js +28 -0
- package/dist/cjs/server/lancer/cache.d.ts +9 -0
- package/dist/cjs/server/lancer/cache.js +33 -0
- package/dist/cjs/server/lancer/client.d.ts +2 -0
- package/dist/cjs/server/lancer/client.js +279 -0
- package/dist/cjs/server/lancer/errors.d.ts +13 -0
- package/dist/cjs/server/lancer/errors.js +21 -0
- package/dist/cjs/server/lancer/index.d.ts +3 -0
- package/dist/cjs/server/lancer/index.js +7 -0
- package/dist/cjs/server/lancer/telemetry-buffer.d.ts +15 -0
- package/dist/cjs/server/lancer/telemetry-buffer.js +64 -0
- package/dist/cjs/server/lancer/types.d.ts +201 -0
- package/dist/cjs/server/lancer/types.js +5 -0
- package/dist/cjs/server/pii-shield/aegis-detector.d.ts +26 -0
- package/dist/cjs/server/pii-shield/aegis-detector.js +26 -0
- package/dist/cjs/server/pii-shield/create-pii-shield.d.ts +2 -0
- package/dist/cjs/server/pii-shield/create-pii-shield.js +191 -0
- package/dist/cjs/server/pii-shield/deep-walk.d.ts +1 -0
- package/dist/cjs/server/pii-shield/deep-walk.js +19 -0
- package/dist/cjs/server/pii-shield/fake-data.d.ts +5 -0
- package/dist/cjs/server/pii-shield/fake-data.js +170 -0
- package/dist/cjs/server/pii-shield/index.d.ts +4 -0
- package/dist/cjs/server/pii-shield/index.js +9 -0
- package/dist/cjs/server/pii-shield/leakage.d.ts +1 -0
- package/dist/cjs/server/pii-shield/leakage.js +15 -0
- package/dist/cjs/server/pii-shield/session-mapping.d.ts +12 -0
- package/dist/cjs/server/pii-shield/session-mapping.js +70 -0
- package/dist/cjs/server/pipeline.d.ts +26 -0
- package/dist/cjs/server/pipeline.js +127 -0
- package/dist/cjs/server/testing/approvals.d.ts +11 -0
- package/dist/cjs/server/testing/approvals.js +41 -0
- package/dist/cjs/server/testing/combination.d.ts +3 -0
- package/dist/cjs/server/testing/combination.js +83 -0
- package/dist/cjs/server/testing/create-test-suite.d.ts +29 -0
- package/dist/cjs/server/testing/create-test-suite.js +81 -0
- package/dist/cjs/server/testing/edge-cases.d.ts +8 -0
- package/dist/cjs/server/testing/edge-cases.js +277 -0
- package/dist/cjs/server/testing/guardrails.d.ts +14 -0
- package/dist/cjs/server/testing/guardrails.js +152 -0
- package/dist/cjs/server/testing/helpers.d.ts +14 -0
- package/dist/cjs/server/testing/helpers.js +119 -0
- package/dist/cjs/server/testing/index.d.ts +12 -0
- package/dist/cjs/server/testing/index.js +37 -0
- package/dist/cjs/server/testing/mock-executor.d.ts +8 -0
- package/dist/cjs/server/testing/mock-executor.js +40 -0
- package/dist/cjs/server/testing/regression.d.ts +7 -0
- package/dist/cjs/server/testing/regression.js +27 -0
- package/dist/cjs/server/testing/routing.d.ts +12 -0
- package/dist/cjs/server/testing/routing.js +63 -0
- package/dist/cjs/server/testing/schema-consistency.d.ts +6 -0
- package/dist/cjs/server/testing/schema-consistency.js +56 -0
- package/dist/cjs/server/testing/smoke.d.ts +11 -0
- package/dist/cjs/server/testing/smoke.js +64 -0
- package/dist/cjs/server/testing/types.d.ts +156 -0
- package/dist/cjs/server/testing/types.js +2 -0
- package/dist/cjs/types.d.ts +696 -0
- package/dist/cjs/types.js +91 -0
- package/dist/cli/commands/install-skills.d.ts +17 -0
- package/dist/cli/commands/install-skills.js +185 -0
- package/dist/cli/commands/install-skills.js.map +1 -0
- package/dist/cli/index.js +25 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/react/components/ai-trigger-button.js +4 -6
- package/dist/react/components/ai-trigger-button.js.map +1 -1
- package/dist/react/components/recording-bar.js +4 -6
- package/dist/react/components/recording-bar.js.map +1 -1
- package/dist/react/components/wavesurfer-loader.d.ts +5 -0
- package/dist/react/components/wavesurfer-loader.js +34 -0
- package/dist/react/components/wavesurfer-loader.js.map +1 -0
- package/dist/server/adapters/nestjs/chat/ai-chat.module.d.ts +1 -1
- package/dist/server/adapters/nestjs/chat/ai-chat.module.js +3 -2
- package/dist/server/adapters/nestjs/chat/ai-chat.module.js.map +1 -1
- package/dist/server/adapters/nestjs/chat/speech-stream.gateway.d.ts +1 -1
- package/dist/server/adapters/nestjs/chat/speech-stream.gateway.js +10 -8
- package/dist/server/adapters/nestjs/chat/speech-stream.gateway.js.map +1 -1
- package/dist/server/adapters/nestjs/chat/ws-loader.d.ts +5 -0
- package/dist/server/adapters/nestjs/chat/ws-loader.js +37 -0
- package/dist/server/adapters/nestjs/chat/ws-loader.js.map +1 -0
- package/openapi/package.json +5 -0
- package/package.json +59 -16
- package/react/langgraph/package.json +5 -0
- package/react/package.json +5 -0
- package/react/vercel/package.json +5 -0
- package/server/nestjs/package.json +5 -0
- package/server/nextjs/package.json +5 -0
- package/server/package.json +5 -0
- package/server/testing/package.json +5 -0
- package/skills/README.md +107 -0
- package/skills/building-glirastes-chat-ui/SKILL.md +378 -0
- package/skills/integrating-glirastes-nestjs/SKILL.md +346 -0
- package/skills/integrating-glirastes-nextjs/SKILL.md +306 -0
- package/skills/integrating-glirastes-nextjs-with-nestjs-backend/SKILL.md +359 -0
- package/skills/maintaining-glirastes-tools/SKILL.md +463 -0
|
@@ -0,0 +1,489 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Sanitize model messages for OpenAI compatibility.
|
|
4
|
+
*
|
|
5
|
+
* Handles three classes of problems that occur after convertToModelMessages +
|
|
6
|
+
* pruneMessages from the Vercel AI SDK:
|
|
7
|
+
*
|
|
8
|
+
* 1. **Orphaned tool-calls** — assistant has tool-call but no matching
|
|
9
|
+
* tool-result anywhere (pruneMessages stripped it).
|
|
10
|
+
* 2. **Orphaned tool-approval-requests** — approval request without matching
|
|
11
|
+
* response, or whose linked tool-call was removed.
|
|
12
|
+
* 3. **Ordering violations** — OpenAI requires tool-result messages to
|
|
13
|
+
* IMMEDIATELY follow their assistant message. After pruning, the ordering
|
|
14
|
+
* can break.
|
|
15
|
+
*
|
|
16
|
+
* **Important:** The OpenAI provider silently drops `tool-approval-response`
|
|
17
|
+
* parts during serialization. A tool message containing ONLY approval-responses
|
|
18
|
+
* (no tool-result) produces zero OpenAI messages. Therefore, a tool-call is
|
|
19
|
+
* only considered "safe" when it has an adjacent `tool-result` — a
|
|
20
|
+
* `tool-approval-response` alone does NOT satisfy this requirement.
|
|
21
|
+
*
|
|
22
|
+
* **Exception:** When the LAST message is a tool message containing
|
|
23
|
+
* `tool-approval-response` parts, those tool-calls are preserved. The Vercel
|
|
24
|
+
* AI SDK's `streamText` runs `collectToolApprovals()` on the last message
|
|
25
|
+
* and executes approved tools BEFORE sending anything to OpenAI. Stripping
|
|
26
|
+
* these would break the approval flow (causing an infinite loop where the
|
|
27
|
+
* model re-generates the same tool call).
|
|
28
|
+
*
|
|
29
|
+
* The public API is {@link sanitizeModelMessages}, which runs two passes:
|
|
30
|
+
* - Primary pass: adjacency-based orphan removal
|
|
31
|
+
* - Safety-net pass: strict re-check for any remaining orphans
|
|
32
|
+
*
|
|
33
|
+
* @module
|
|
34
|
+
*/
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.sanitizeModelMessages = sanitizeModelMessages;
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
// Helpers
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
function isRecord(value) {
|
|
41
|
+
return typeof value === 'object' && value !== null;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Build a mapping from approvalId → toolCallId from assistant messages.
|
|
45
|
+
*/
|
|
46
|
+
function buildApprovalIdToCallIdMap(messages) {
|
|
47
|
+
const map = new Map();
|
|
48
|
+
for (const msg of messages) {
|
|
49
|
+
if (!isRecord(msg) || msg.role !== 'assistant')
|
|
50
|
+
continue;
|
|
51
|
+
const content = msg.content;
|
|
52
|
+
if (!Array.isArray(content))
|
|
53
|
+
continue;
|
|
54
|
+
for (const part of content) {
|
|
55
|
+
if (isRecord(part) &&
|
|
56
|
+
part.type === 'tool-approval-request' &&
|
|
57
|
+
typeof part.approvalId === 'string' &&
|
|
58
|
+
typeof part.toolCallId === 'string') {
|
|
59
|
+
map.set(part.approvalId, part.toolCallId);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return map;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Find tool-call IDs that have a pending `tool-approval-response` in the
|
|
67
|
+
* LAST tool message. These are safe because `streamText`'s
|
|
68
|
+
* `collectToolApprovals()` will execute them before calling the model.
|
|
69
|
+
*/
|
|
70
|
+
function findPendingApprovalCallIds(messages, approvalIdToCallId) {
|
|
71
|
+
const pendingCallIds = new Set();
|
|
72
|
+
// collectToolApprovals in streamText only looks at the very last message
|
|
73
|
+
const lastMsg = messages.at(-1);
|
|
74
|
+
if (!isRecord(lastMsg) || lastMsg.role !== 'tool')
|
|
75
|
+
return pendingCallIds;
|
|
76
|
+
const content = lastMsg.content;
|
|
77
|
+
if (!Array.isArray(content))
|
|
78
|
+
return pendingCallIds;
|
|
79
|
+
// Only consider this a pending approval if the tool message has NO
|
|
80
|
+
// tool-result parts (otherwise it's a completed step, not a pending approval)
|
|
81
|
+
const hasToolResult = content.some((part) => isRecord(part) && part.type === 'tool-result');
|
|
82
|
+
if (hasToolResult)
|
|
83
|
+
return pendingCallIds;
|
|
84
|
+
for (const part of content) {
|
|
85
|
+
if (isRecord(part) &&
|
|
86
|
+
part.type === 'tool-approval-response' &&
|
|
87
|
+
typeof part.approvalId === 'string') {
|
|
88
|
+
const linkedCallId = approvalIdToCallId.get(part.approvalId);
|
|
89
|
+
if (linkedCallId) {
|
|
90
|
+
pendingCallIds.add(linkedCallId);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return pendingCallIds;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Remove duplicate assistant tool-call occurrences for the same toolCallId.
|
|
98
|
+
*
|
|
99
|
+
* OpenAI requires each assistant `tool-call` to be followed by tool results
|
|
100
|
+
* for that call in the immediate next tool message block. If the same
|
|
101
|
+
* `toolCallId` appears in multiple assistant messages, one historical copy
|
|
102
|
+
* becomes orphaned and triggers a 400 error.
|
|
103
|
+
*
|
|
104
|
+
* Strategy:
|
|
105
|
+
* - Keep only the LAST assistant occurrence of each toolCallId.
|
|
106
|
+
* - **Exception:** Preserve tool-calls with pending approval-responses at the
|
|
107
|
+
* conversation tail, as these will be executed by streamText before the
|
|
108
|
+
* model call.
|
|
109
|
+
* - Remove matching `tool-approval-request` parts from older occurrences.
|
|
110
|
+
* - Drop assistant messages that become empty.
|
|
111
|
+
*/
|
|
112
|
+
function dedupeAssistantToolCalls(messages) {
|
|
113
|
+
// Build approval mapping and find pending approvals
|
|
114
|
+
const approvalIdToCallId = buildApprovalIdToCallIdMap(messages);
|
|
115
|
+
const pendingApprovalCallIds = findPendingApprovalCallIds(messages, approvalIdToCallId);
|
|
116
|
+
const lastAssistantIndexByCallId = new Map();
|
|
117
|
+
for (let i = 0; i < messages.length; i++) {
|
|
118
|
+
const message = messages[i];
|
|
119
|
+
if (!isRecord(message) || message.role !== 'assistant')
|
|
120
|
+
continue;
|
|
121
|
+
const content = message.content;
|
|
122
|
+
if (!Array.isArray(content))
|
|
123
|
+
continue;
|
|
124
|
+
for (const part of content) {
|
|
125
|
+
if (isRecord(part) &&
|
|
126
|
+
part.type === 'tool-call' &&
|
|
127
|
+
typeof part.toolCallId === 'string') {
|
|
128
|
+
lastAssistantIndexByCallId.set(part.toolCallId, i);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return messages
|
|
133
|
+
.map((message, messageIndex) => {
|
|
134
|
+
if (!isRecord(message) || message.role !== 'assistant')
|
|
135
|
+
return message;
|
|
136
|
+
const content = message.content;
|
|
137
|
+
if (!Array.isArray(content))
|
|
138
|
+
return message;
|
|
139
|
+
const seenCallIdsInMessage = new Set();
|
|
140
|
+
const filtered = content.filter((part) => {
|
|
141
|
+
if (!isRecord(part))
|
|
142
|
+
return true;
|
|
143
|
+
if (part.type === 'tool-call' && typeof part.toolCallId === 'string') {
|
|
144
|
+
const isLastOccurrence = lastAssistantIndexByCallId.get(part.toolCallId) === messageIndex;
|
|
145
|
+
const isDuplicateInSameMessage = seenCallIdsInMessage.has(part.toolCallId);
|
|
146
|
+
const hasPendingApproval = pendingApprovalCallIds.has(part.toolCallId);
|
|
147
|
+
// Preserve if: last occurrence, OR has pending approval
|
|
148
|
+
// (pending approval means streamText will execute it - must not remove)
|
|
149
|
+
if ((!isLastOccurrence && !hasPendingApproval) ||
|
|
150
|
+
isDuplicateInSameMessage) {
|
|
151
|
+
console.info(`[glirastes] Removing duplicate assistant tool call: ${part.toolCallId}`);
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
seenCallIdsInMessage.add(part.toolCallId);
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
157
|
+
if (part.type === 'tool-approval-request' &&
|
|
158
|
+
typeof part.toolCallId === 'string') {
|
|
159
|
+
const isLastOccurrence = lastAssistantIndexByCallId.get(part.toolCallId) === messageIndex;
|
|
160
|
+
const hasPendingApproval = pendingApprovalCallIds.has(part.toolCallId);
|
|
161
|
+
// Keep approval-request if it's for the last occurrence OR has pending approval
|
|
162
|
+
return isLastOccurrence || hasPendingApproval;
|
|
163
|
+
}
|
|
164
|
+
return true;
|
|
165
|
+
});
|
|
166
|
+
if (filtered.length !== content.length) {
|
|
167
|
+
return { ...message, content: filtered };
|
|
168
|
+
}
|
|
169
|
+
return message;
|
|
170
|
+
})
|
|
171
|
+
.filter((message) => {
|
|
172
|
+
if (!isRecord(message) || message.role !== 'assistant')
|
|
173
|
+
return true;
|
|
174
|
+
const content = message.content;
|
|
175
|
+
return Array.isArray(content) ? content.length > 0 : true;
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
// ---------------------------------------------------------------------------
|
|
179
|
+
// Primary pass — removeOrphanedToolCalls
|
|
180
|
+
// ---------------------------------------------------------------------------
|
|
181
|
+
/**
|
|
182
|
+
* Remove orphaned tool-calls whose results are not immediately adjacent.
|
|
183
|
+
*
|
|
184
|
+
* Strategy:
|
|
185
|
+
* - For each assistant message with tool-calls, check that the NEXT message(s)
|
|
186
|
+
* are tool messages containing all required results.
|
|
187
|
+
* - If not, strip the orphaned tool-call (and any related approval-request).
|
|
188
|
+
* - Exception: tool-calls with pending approval-responses at the conversation
|
|
189
|
+
* tail are preserved (streamText will execute them).
|
|
190
|
+
* - Remove empty assistant/tool messages.
|
|
191
|
+
*/
|
|
192
|
+
function removeOrphanedToolCalls(messages) {
|
|
193
|
+
// --- Build approval mapping and find pending approvals ---
|
|
194
|
+
const approvalIdToCallId = buildApprovalIdToCallIdMap(messages);
|
|
195
|
+
const pendingApprovalCallIds = findPendingApprovalCallIds(messages, approvalIdToCallId);
|
|
196
|
+
// --- Collect all tool-result IDs in tool-role messages ---
|
|
197
|
+
const adjacentResultIds = new Set();
|
|
198
|
+
for (let i = 0; i < messages.length; i++) {
|
|
199
|
+
const msg = messages[i];
|
|
200
|
+
if (!isRecord(msg) || msg.role !== 'assistant')
|
|
201
|
+
continue;
|
|
202
|
+
const content = msg.content;
|
|
203
|
+
if (!Array.isArray(content))
|
|
204
|
+
continue;
|
|
205
|
+
const callIds = new Set();
|
|
206
|
+
for (const part of content) {
|
|
207
|
+
if (isRecord(part) &&
|
|
208
|
+
part.type === 'tool-call' &&
|
|
209
|
+
typeof part.toolCallId === 'string') {
|
|
210
|
+
callIds.add(part.toolCallId);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
if (callIds.size === 0)
|
|
214
|
+
continue;
|
|
215
|
+
// Walk forward: only consecutive tool messages count
|
|
216
|
+
for (let j = i + 1; j < messages.length; j++) {
|
|
217
|
+
const next = messages[j];
|
|
218
|
+
if (!isRecord(next) || next.role !== 'tool')
|
|
219
|
+
break;
|
|
220
|
+
const nextContent = next.content;
|
|
221
|
+
if (!Array.isArray(nextContent))
|
|
222
|
+
break;
|
|
223
|
+
for (const part of nextContent) {
|
|
224
|
+
if (isRecord(part) &&
|
|
225
|
+
part.type === 'tool-result' &&
|
|
226
|
+
typeof part.toolCallId === 'string') {
|
|
227
|
+
if (callIds.has(part.toolCallId)) {
|
|
228
|
+
adjacentResultIds.add(part.toolCallId);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
// A tool-call is safe if it has an adjacent result OR a pending approval
|
|
235
|
+
const safeCallIds = new Set([
|
|
236
|
+
...adjacentResultIds,
|
|
237
|
+
...pendingApprovalCallIds,
|
|
238
|
+
]);
|
|
239
|
+
// --- Filter assistant content ---
|
|
240
|
+
return (messages
|
|
241
|
+
.map((message) => {
|
|
242
|
+
if (!isRecord(message) || message.role !== 'assistant')
|
|
243
|
+
return message;
|
|
244
|
+
const content = message.content;
|
|
245
|
+
if (!Array.isArray(content))
|
|
246
|
+
return message;
|
|
247
|
+
const filteredContent = content.filter((part) => {
|
|
248
|
+
if (!isRecord(part))
|
|
249
|
+
return true;
|
|
250
|
+
// Remove orphaned tool-calls (no adjacent tool-result and no pending approval)
|
|
251
|
+
if (part.type === 'tool-call' &&
|
|
252
|
+
typeof part.toolCallId === 'string') {
|
|
253
|
+
const isSafe = safeCallIds.has(part.toolCallId);
|
|
254
|
+
if (!isSafe) {
|
|
255
|
+
console.info(`[glirastes] Removing orphaned tool call: ${part.toolCallId}`);
|
|
256
|
+
}
|
|
257
|
+
return isSafe;
|
|
258
|
+
}
|
|
259
|
+
// Remove approval-requests whose tool-call was removed
|
|
260
|
+
if (part.type === 'tool-approval-request' &&
|
|
261
|
+
typeof part.toolCallId === 'string') {
|
|
262
|
+
return safeCallIds.has(part.toolCallId);
|
|
263
|
+
}
|
|
264
|
+
return true;
|
|
265
|
+
});
|
|
266
|
+
if (filteredContent.length !== content.length) {
|
|
267
|
+
return { ...message, content: filteredContent };
|
|
268
|
+
}
|
|
269
|
+
return message;
|
|
270
|
+
})
|
|
271
|
+
// Remove empty assistant messages
|
|
272
|
+
.filter((message) => {
|
|
273
|
+
if (!isRecord(message) || message.role !== 'assistant')
|
|
274
|
+
return true;
|
|
275
|
+
const content = message.content;
|
|
276
|
+
if (!Array.isArray(content))
|
|
277
|
+
return true;
|
|
278
|
+
return content.length > 0;
|
|
279
|
+
})
|
|
280
|
+
// Remove tool messages whose parts are all orphaned
|
|
281
|
+
.map((message) => {
|
|
282
|
+
if (!isRecord(message) || message.role !== 'tool')
|
|
283
|
+
return message;
|
|
284
|
+
const content = message.content;
|
|
285
|
+
if (!Array.isArray(content))
|
|
286
|
+
return message;
|
|
287
|
+
const filtered = content.filter((part) => {
|
|
288
|
+
if (!isRecord(part))
|
|
289
|
+
return true;
|
|
290
|
+
if (part.type === 'tool-result' &&
|
|
291
|
+
typeof part.toolCallId === 'string') {
|
|
292
|
+
return adjacentResultIds.has(part.toolCallId);
|
|
293
|
+
}
|
|
294
|
+
// Approval-responses survive if their linked tool-call survived
|
|
295
|
+
if (part.type === 'tool-approval-response' &&
|
|
296
|
+
typeof part.approvalId === 'string') {
|
|
297
|
+
const linkedCallId = approvalIdToCallId.get(part.approvalId);
|
|
298
|
+
return linkedCallId ? safeCallIds.has(linkedCallId) : true;
|
|
299
|
+
}
|
|
300
|
+
return true;
|
|
301
|
+
});
|
|
302
|
+
if (filtered.length !== content.length) {
|
|
303
|
+
return { ...message, content: filtered };
|
|
304
|
+
}
|
|
305
|
+
return message;
|
|
306
|
+
})
|
|
307
|
+
.filter((message) => {
|
|
308
|
+
if (!isRecord(message) || message.role !== 'tool')
|
|
309
|
+
return true;
|
|
310
|
+
const content = message.content;
|
|
311
|
+
if (!Array.isArray(content))
|
|
312
|
+
return true;
|
|
313
|
+
// Keep tool message if it has at least one surviving tool-result
|
|
314
|
+
// OR a pending approval-response for a surviving tool-call
|
|
315
|
+
return content.some((part) => {
|
|
316
|
+
if (!isRecord(part))
|
|
317
|
+
return false;
|
|
318
|
+
if (part.type === 'tool-result' &&
|
|
319
|
+
typeof part.toolCallId === 'string') {
|
|
320
|
+
return adjacentResultIds.has(part.toolCallId);
|
|
321
|
+
}
|
|
322
|
+
if (part.type === 'tool-approval-response' &&
|
|
323
|
+
typeof part.approvalId === 'string') {
|
|
324
|
+
const linkedCallId = approvalIdToCallId.get(part.approvalId);
|
|
325
|
+
return linkedCallId
|
|
326
|
+
? pendingApprovalCallIds.has(linkedCallId)
|
|
327
|
+
: false;
|
|
328
|
+
}
|
|
329
|
+
return false;
|
|
330
|
+
});
|
|
331
|
+
}));
|
|
332
|
+
}
|
|
333
|
+
// ---------------------------------------------------------------------------
|
|
334
|
+
// Safety-net pass — stripRemainingOrphanedToolCalls
|
|
335
|
+
// ---------------------------------------------------------------------------
|
|
336
|
+
/**
|
|
337
|
+
* Strict re-check: strip any tool-call that still lacks an adjacent
|
|
338
|
+
* tool-result after the primary pass.
|
|
339
|
+
*
|
|
340
|
+
* Catches edge cases from pruneMessages / convertToModelMessages that the
|
|
341
|
+
* adjacency-based pass may miss (e.g. step-start block splits, incomplete
|
|
342
|
+
* tool states producing calls without results).
|
|
343
|
+
*
|
|
344
|
+
* Preserves tool-calls with pending approval-responses at the tail.
|
|
345
|
+
*/
|
|
346
|
+
function stripRemainingOrphanedToolCalls(messages) {
|
|
347
|
+
// Build approval mapping and find pending approvals
|
|
348
|
+
const approvalIdToCallId = buildApprovalIdToCallIdMap(messages);
|
|
349
|
+
const pendingApprovalCallIds = findPendingApprovalCallIds(messages, approvalIdToCallId);
|
|
350
|
+
// Build set of "safe" tool-call IDs (have adjacent tool-result OR pending approval)
|
|
351
|
+
const safeCallIds = new Set(pendingApprovalCallIds);
|
|
352
|
+
for (let i = 0; i < messages.length; i++) {
|
|
353
|
+
const msg = messages[i];
|
|
354
|
+
if (!isRecord(msg) || msg.role !== 'assistant')
|
|
355
|
+
continue;
|
|
356
|
+
const content = msg.content;
|
|
357
|
+
if (!Array.isArray(content))
|
|
358
|
+
continue;
|
|
359
|
+
const callIds = new Set();
|
|
360
|
+
for (const part of content) {
|
|
361
|
+
if (isRecord(part) &&
|
|
362
|
+
part.type === 'tool-call' &&
|
|
363
|
+
typeof part.toolCallId === 'string') {
|
|
364
|
+
callIds.add(part.toolCallId);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
if (callIds.size === 0)
|
|
368
|
+
continue;
|
|
369
|
+
for (let j = i + 1; j < messages.length; j++) {
|
|
370
|
+
const next = messages[j];
|
|
371
|
+
if (!isRecord(next) || next.role !== 'tool')
|
|
372
|
+
break;
|
|
373
|
+
const nextContent = next.content;
|
|
374
|
+
if (!Array.isArray(nextContent))
|
|
375
|
+
break;
|
|
376
|
+
for (const part of nextContent) {
|
|
377
|
+
if (isRecord(part) &&
|
|
378
|
+
part.type === 'tool-result' &&
|
|
379
|
+
typeof part.toolCallId === 'string') {
|
|
380
|
+
if (callIds.has(part.toolCallId)) {
|
|
381
|
+
safeCallIds.add(part.toolCallId);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
// Strip unsafe tool-calls
|
|
388
|
+
let changed = false;
|
|
389
|
+
let result = messages
|
|
390
|
+
.map((message) => {
|
|
391
|
+
if (!isRecord(message) || message.role !== 'assistant')
|
|
392
|
+
return message;
|
|
393
|
+
const content = message.content;
|
|
394
|
+
if (!Array.isArray(content))
|
|
395
|
+
return message;
|
|
396
|
+
const filtered = content.filter((part) => {
|
|
397
|
+
if (!isRecord(part))
|
|
398
|
+
return true;
|
|
399
|
+
if (part.type === 'tool-call' && typeof part.toolCallId === 'string') {
|
|
400
|
+
const safe = safeCallIds.has(part.toolCallId);
|
|
401
|
+
if (!safe) {
|
|
402
|
+
console.warn(`[glirastes] Safety-net removing orphaned tool call: ${part.toolCallId}`);
|
|
403
|
+
changed = true;
|
|
404
|
+
}
|
|
405
|
+
return safe;
|
|
406
|
+
}
|
|
407
|
+
if (part.type === 'tool-approval-request' &&
|
|
408
|
+
typeof part.toolCallId === 'string') {
|
|
409
|
+
return safeCallIds.has(part.toolCallId);
|
|
410
|
+
}
|
|
411
|
+
return true;
|
|
412
|
+
});
|
|
413
|
+
if (filtered.length !== content.length)
|
|
414
|
+
return { ...message, content: filtered };
|
|
415
|
+
return message;
|
|
416
|
+
})
|
|
417
|
+
.filter((message) => {
|
|
418
|
+
if (!isRecord(message) || message.role !== 'assistant')
|
|
419
|
+
return true;
|
|
420
|
+
const content = message.content;
|
|
421
|
+
return Array.isArray(content) ? content.length > 0 : true;
|
|
422
|
+
});
|
|
423
|
+
// Remove tool messages whose results reference no surviving call
|
|
424
|
+
if (changed) {
|
|
425
|
+
result = result.filter((message) => {
|
|
426
|
+
if (!isRecord(message) || message.role !== 'tool')
|
|
427
|
+
return true;
|
|
428
|
+
const content = message.content;
|
|
429
|
+
if (!Array.isArray(content))
|
|
430
|
+
return true;
|
|
431
|
+
return content.some((part) => {
|
|
432
|
+
if (!isRecord(part))
|
|
433
|
+
return false;
|
|
434
|
+
if (part.type === 'tool-result' &&
|
|
435
|
+
typeof part.toolCallId === 'string') {
|
|
436
|
+
return safeCallIds.has(part.toolCallId);
|
|
437
|
+
}
|
|
438
|
+
// Keep approval-response if its linked call is a pending approval
|
|
439
|
+
if (part.type === 'tool-approval-response' &&
|
|
440
|
+
typeof part.approvalId === 'string') {
|
|
441
|
+
const linkedCallId = approvalIdToCallId.get(part.approvalId);
|
|
442
|
+
return linkedCallId
|
|
443
|
+
? pendingApprovalCallIds.has(linkedCallId)
|
|
444
|
+
: false;
|
|
445
|
+
}
|
|
446
|
+
return true;
|
|
447
|
+
});
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
return result;
|
|
451
|
+
}
|
|
452
|
+
// ---------------------------------------------------------------------------
|
|
453
|
+
// Public API
|
|
454
|
+
// ---------------------------------------------------------------------------
|
|
455
|
+
/**
|
|
456
|
+
* Sanitize model messages by removing orphaned tool-calls and
|
|
457
|
+
* tool-approval-requests.
|
|
458
|
+
*
|
|
459
|
+
* A tool-call is considered orphaned when it does not have an adjacent
|
|
460
|
+
* `tool-result` in the immediately following tool message(s). A
|
|
461
|
+
* `tool-approval-response` alone does NOT count — the OpenAI provider
|
|
462
|
+
* silently drops these during serialization.
|
|
463
|
+
*
|
|
464
|
+
* **Exception:** Tool-calls with a `tool-approval-response` in the LAST
|
|
465
|
+
* message are preserved. The Vercel AI SDK's `streamText` runs
|
|
466
|
+
* `collectToolApprovals()` to execute approved tools before calling the
|
|
467
|
+
* model provider, so these are safe to keep.
|
|
468
|
+
*
|
|
469
|
+
* Call this on the output of `pruneMessages()` (from the `ai` SDK) before
|
|
470
|
+
* passing messages to `streamText()`. Or use {@link prepareModelMessages}
|
|
471
|
+
* which calls this automatically.
|
|
472
|
+
*
|
|
473
|
+
* @example
|
|
474
|
+
* ```ts
|
|
475
|
+
* import { sanitizeModelMessages } from './index.js';
|
|
476
|
+
*
|
|
477
|
+
* const raw = await convertToModelMessages(uiMessages, {
|
|
478
|
+
* ignoreIncompleteToolCalls: true,
|
|
479
|
+
* });
|
|
480
|
+
* const pruned = pruneMessages({ messages: raw.slice(-12), ... });
|
|
481
|
+
* const clean = sanitizeModelMessages(pruned);
|
|
482
|
+
* // clean is safe to pass to streamText()
|
|
483
|
+
* ```
|
|
484
|
+
*/
|
|
485
|
+
function sanitizeModelMessages(messages) {
|
|
486
|
+
const deduped = dedupeAssistantToolCalls(messages);
|
|
487
|
+
const afterPrimary = removeOrphanedToolCalls(deduped);
|
|
488
|
+
return stripRemainingOrphanedToolCalls(afterPrimary);
|
|
489
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ExecuteToolOptions, ExecuteToolResult } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Execute a single tool from a Server Action context.
|
|
4
|
+
* The SDK does not perform authorization — permission checks happen in the
|
|
5
|
+
* backend API that each tool calls.
|
|
6
|
+
*/
|
|
7
|
+
export declare function executeTool(options: ExecuteToolOptions): Promise<ExecuteToolResult>;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.executeTool = executeTool;
|
|
4
|
+
const index_js_1 = require("../../core/index.js");
|
|
5
|
+
/**
|
|
6
|
+
* Execute a single tool from a Server Action context.
|
|
7
|
+
* The SDK does not perform authorization — permission checks happen in the
|
|
8
|
+
* backend API that each tool calls.
|
|
9
|
+
*/
|
|
10
|
+
async function executeTool(options) {
|
|
11
|
+
const { toolName, input, tools, locale = 'en-US', uiActionSchema, } = options;
|
|
12
|
+
const tool = tools[toolName];
|
|
13
|
+
if (!tool) {
|
|
14
|
+
return { success: false, error: `Tool "${toolName}" not found.` };
|
|
15
|
+
}
|
|
16
|
+
const context = {
|
|
17
|
+
currentDate: new Date(),
|
|
18
|
+
locale,
|
|
19
|
+
};
|
|
20
|
+
try {
|
|
21
|
+
const rawResult = await tool.execute(input, context);
|
|
22
|
+
const result = uiActionSchema
|
|
23
|
+
? (0, index_js_1.normalizeResultUiAction)(rawResult, uiActionSchema)
|
|
24
|
+
: rawResult;
|
|
25
|
+
const record = typeof result === 'object' && result !== null
|
|
26
|
+
? result
|
|
27
|
+
: { data: result };
|
|
28
|
+
return {
|
|
29
|
+
success: record.success !== false,
|
|
30
|
+
data: record,
|
|
31
|
+
uiAction: record.uiAction,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
return {
|
|
36
|
+
success: false,
|
|
37
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
}
|