@superblocksteam/vite-plugin-file-sync 2.0.123 → 2.0.124-next.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai-service/agent/prompts/build-base-system-prompt.d.ts +16 -1
- package/dist/ai-service/agent/prompts/build-base-system-prompt.d.ts.map +1 -1
- package/dist/ai-service/agent/prompts/build-base-system-prompt.js +27 -1
- package/dist/ai-service/agent/prompts/build-base-system-prompt.js.map +1 -1
- package/dist/ai-service/agent/prompts/build-security-scan-prompt.d.ts.map +1 -1
- package/dist/ai-service/agent/prompts/build-security-scan-prompt.js +3 -1
- package/dist/ai-service/agent/prompts/build-security-scan-prompt.js.map +1 -1
- package/dist/ai-service/agent/tools/apis/api-comparator.d.ts.map +1 -1
- package/dist/ai-service/agent/tools/apis/api-comparator.js +1 -4
- package/dist/ai-service/agent/tools/apis/api-comparator.js.map +1 -1
- package/dist/ai-service/agent/tools/apis/api-testing-state.js +7 -0
- package/dist/ai-service/agent/tools/apis/api-testing-state.js.map +1 -1
- package/dist/ai-service/agent/tools/apis/get-api-docs.d.ts +1 -1
- package/dist/ai-service/agent/tools/apis/write-api.d.ts +2 -2
- package/dist/ai-service/agent/tools/build-capture-screenshot.d.ts.map +1 -1
- package/dist/ai-service/agent/tools/build-capture-screenshot.js +11 -33
- package/dist/ai-service/agent/tools/build-capture-screenshot.js.map +1 -1
- package/dist/ai-service/agent/tools/build-copy-directory.d.ts +1 -1
- package/dist/ai-service/agent/tools/build-finalize.d.ts.map +1 -1
- package/dist/ai-service/agent/tools/build-finalize.js +11 -2
- package/dist/ai-service/agent/tools/build-finalize.js.map +1 -1
- package/dist/ai-service/agent/tools/build-install-packages.d.ts +41 -1
- package/dist/ai-service/agent/tools/build-install-packages.d.ts.map +1 -1
- package/dist/ai-service/agent/tools/build-install-packages.js +208 -0
- package/dist/ai-service/agent/tools/build-install-packages.js.map +1 -1
- package/dist/ai-service/agent/tools/build-lookup-npm-package.d.ts +28 -0
- package/dist/ai-service/agent/tools/build-lookup-npm-package.d.ts.map +1 -0
- package/dist/ai-service/agent/tools/build-lookup-npm-package.js +78 -0
- package/dist/ai-service/agent/tools/build-lookup-npm-package.js.map +1 -0
- package/dist/ai-service/agent/tools/build-manage-checklist.d.ts +4 -4
- package/dist/ai-service/agent/tools/build-navigate-preview.d.ts +16 -0
- package/dist/ai-service/agent/tools/build-navigate-preview.d.ts.map +1 -0
- package/dist/ai-service/agent/tools/build-navigate-preview.js +68 -0
- package/dist/ai-service/agent/tools/build-navigate-preview.js.map +1 -0
- package/dist/ai-service/agent/tools/build-write-file.d.ts +1 -1
- package/dist/ai-service/agent/tools/databases/dev-database.d.ts +9 -9
- package/dist/ai-service/agent/tools/index.d.ts +2 -0
- package/dist/ai-service/agent/tools/index.d.ts.map +1 -1
- package/dist/ai-service/agent/tools/index.js +2 -0
- package/dist/ai-service/agent/tools/index.js.map +1 -1
- package/dist/ai-service/agent/tools/integrations/execute-request.d.ts +3 -3
- package/dist/ai-service/agent/tools/report-security-findings.d.ts +11 -5
- package/dist/ai-service/agent/tools/report-security-findings.d.ts.map +1 -1
- package/dist/ai-service/agent/tools/report-security-findings.js +1 -0
- package/dist/ai-service/agent/tools/report-security-findings.js.map +1 -1
- package/dist/ai-service/agent/tools.d.ts.map +1 -1
- package/dist/ai-service/agent/tools.js +3 -1
- package/dist/ai-service/agent/tools.js.map +1 -1
- package/dist/ai-service/agent/tools2/tools/git.d.ts +2 -2
- package/dist/ai-service/agent/tools2/tools/grep.d.ts +2 -2
- package/dist/ai-service/agent/tools2/tools/update-test-case-status.d.ts +2 -2
- package/dist/ai-service/app-interface/npm-error-parser.d.ts +161 -0
- package/dist/ai-service/app-interface/npm-error-parser.d.ts.map +1 -0
- package/dist/ai-service/app-interface/npm-error-parser.js +510 -0
- package/dist/ai-service/app-interface/npm-error-parser.js.map +1 -0
- package/dist/ai-service/app-interface/npm-package-lookup.d.ts +286 -0
- package/dist/ai-service/app-interface/npm-package-lookup.d.ts.map +1 -0
- package/dist/ai-service/app-interface/npm-package-lookup.js +793 -0
- package/dist/ai-service/app-interface/npm-package-lookup.js.map +1 -0
- package/dist/ai-service/app-interface/npm-registry.d.ts +735 -47
- package/dist/ai-service/app-interface/npm-registry.d.ts.map +1 -1
- package/dist/ai-service/app-interface/npm-registry.js +1882 -153
- package/dist/ai-service/app-interface/npm-registry.js.map +1 -1
- package/dist/ai-service/app-interface/shell.d.ts +118 -1
- package/dist/ai-service/app-interface/shell.d.ts.map +1 -1
- package/dist/ai-service/app-interface/shell.js +579 -157
- package/dist/ai-service/app-interface/shell.js.map +1 -1
- package/dist/ai-service/app-skills/manager.d.ts +1 -1
- package/dist/ai-service/app-skills/manager.d.ts.map +1 -1
- package/dist/ai-service/app-skills/manager.js +39 -7
- package/dist/ai-service/app-skills/manager.js.map +1 -1
- package/dist/ai-service/checklist/api-migration-checklist-gate.d.ts +9 -0
- package/dist/ai-service/checklist/api-migration-checklist-gate.d.ts.map +1 -0
- package/dist/ai-service/checklist/api-migration-checklist-gate.js +30 -0
- package/dist/ai-service/checklist/api-migration-checklist-gate.js.map +1 -0
- package/dist/ai-service/checklist/api-migration-origins.d.ts +4 -0
- package/dist/ai-service/checklist/api-migration-origins.d.ts.map +1 -0
- package/dist/ai-service/checklist/api-migration-origins.js +8 -0
- package/dist/ai-service/checklist/api-migration-origins.js.map +1 -0
- package/dist/ai-service/checklist/persisted-checklist-store.d.ts +2 -3
- package/dist/ai-service/checklist/persisted-checklist-store.d.ts.map +1 -1
- package/dist/ai-service/checklist/persisted-checklist-store.js +6 -7
- package/dist/ai-service/checklist/persisted-checklist-store.js.map +1 -1
- package/dist/ai-service/context-archive-paths.d.ts +4 -0
- package/dist/ai-service/context-archive-paths.d.ts.map +1 -0
- package/dist/ai-service/context-archive-paths.js +28 -0
- package/dist/ai-service/context-archive-paths.js.map +1 -0
- package/dist/ai-service/context-download.d.ts +3 -1
- package/dist/ai-service/context-download.d.ts.map +1 -1
- package/dist/ai-service/context-download.js +48 -9
- package/dist/ai-service/context-download.js.map +1 -1
- package/dist/ai-service/context-upload.d.ts +3 -2
- package/dist/ai-service/context-upload.d.ts.map +1 -1
- package/dist/ai-service/context-upload.js +44 -26
- package/dist/ai-service/context-upload.js.map +1 -1
- package/dist/ai-service/dev-database-client.d.ts +3 -11
- package/dist/ai-service/dev-database-client.d.ts.map +1 -1
- package/dist/ai-service/dev-database-client.js.map +1 -1
- package/dist/ai-service/features.d.ts +4 -0
- package/dist/ai-service/features.d.ts.map +1 -1
- package/dist/ai-service/features.js +8 -0
- package/dist/ai-service/features.js.map +1 -1
- package/dist/ai-service/filter-disabled-tools-for-migration.d.ts.map +1 -1
- package/dist/ai-service/filter-disabled-tools-for-migration.js +4 -0
- package/dist/ai-service/filter-disabled-tools-for-migration.js.map +1 -1
- package/dist/ai-service/index.d.ts +94 -0
- package/dist/ai-service/index.d.ts.map +1 -1
- package/dist/ai-service/index.js +564 -17
- package/dist/ai-service/index.js.map +1 -1
- package/dist/ai-service/llm/client.d.ts +38 -3
- package/dist/ai-service/llm/client.d.ts.map +1 -1
- package/dist/ai-service/llm/client.js +73 -22
- package/dist/ai-service/llm/client.js.map +1 -1
- package/dist/ai-service/llm/context-v2/adapter.d.ts +3 -1
- package/dist/ai-service/llm/context-v2/adapter.d.ts.map +1 -1
- package/dist/ai-service/llm/context-v2/adapter.js +11 -11
- package/dist/ai-service/llm/context-v2/adapter.js.map +1 -1
- package/dist/ai-service/llm/context-v2/compaction/client-side.d.ts +44 -0
- package/dist/ai-service/llm/context-v2/compaction/client-side.d.ts.map +1 -0
- package/dist/ai-service/llm/context-v2/compaction/client-side.js +170 -0
- package/dist/ai-service/llm/context-v2/compaction/client-side.js.map +1 -0
- package/dist/ai-service/llm/context-v2/compaction/compaction-strategy.d.ts +56 -0
- package/dist/ai-service/llm/context-v2/compaction/compaction-strategy.d.ts.map +1 -0
- package/dist/ai-service/llm/context-v2/compaction/compaction-strategy.js +9 -0
- package/dist/ai-service/llm/context-v2/compaction/compaction-strategy.js.map +1 -0
- package/dist/ai-service/llm/context-v2/compaction/index.d.ts +5 -0
- package/dist/ai-service/llm/context-v2/compaction/index.d.ts.map +1 -0
- package/dist/ai-service/llm/context-v2/compaction/index.js +3 -0
- package/dist/ai-service/llm/context-v2/compaction/index.js.map +1 -0
- package/dist/ai-service/llm/context-v2/compaction/server-side.d.ts +30 -0
- package/dist/ai-service/llm/context-v2/compaction/server-side.d.ts.map +1 -0
- package/dist/ai-service/llm/context-v2/compaction/server-side.js +130 -0
- package/dist/ai-service/llm/context-v2/compaction/server-side.js.map +1 -0
- package/dist/ai-service/llm/context-v2/context-management.d.ts +203 -0
- package/dist/ai-service/llm/context-v2/context-management.d.ts.map +1 -0
- package/dist/ai-service/llm/context-v2/context-management.js +183 -0
- package/dist/ai-service/llm/context-v2/context-management.js.map +1 -0
- package/dist/ai-service/llm/context-v2/context.d.ts +64 -22
- package/dist/ai-service/llm/context-v2/context.d.ts.map +1 -1
- package/dist/ai-service/llm/context-v2/context.js +233 -157
- package/dist/ai-service/llm/context-v2/context.js.map +1 -1
- package/dist/ai-service/llm/context-v2/conversation-context.d.ts +24 -7
- package/dist/ai-service/llm/context-v2/conversation-context.d.ts.map +1 -1
- package/dist/ai-service/llm/context-v2/conversation-context.js +1 -1
- package/dist/ai-service/llm/context-v2/index.d.ts +0 -4
- package/dist/ai-service/llm/context-v2/index.d.ts.map +1 -1
- package/dist/ai-service/llm/context-v2/index.js +0 -4
- package/dist/ai-service/llm/context-v2/index.js.map +1 -1
- package/dist/ai-service/llm/context-v2/manager.d.ts +24 -3
- package/dist/ai-service/llm/context-v2/manager.d.ts.map +1 -1
- package/dist/ai-service/llm/context-v2/manager.js +146 -4
- package/dist/ai-service/llm/context-v2/manager.js.map +1 -1
- package/dist/ai-service/llm/context-v2/migrations/v1-to-v2.d.ts +2 -7
- package/dist/ai-service/llm/context-v2/migrations/v1-to-v2.d.ts.map +1 -1
- package/dist/ai-service/llm/context-v2/migrations/v1-to-v2.js +5 -73
- package/dist/ai-service/llm/context-v2/migrations/v1-to-v2.js.map +1 -1
- package/dist/ai-service/llm/context-v2/storage/event-store.d.ts +57 -0
- package/dist/ai-service/llm/context-v2/storage/event-store.d.ts.map +1 -0
- package/dist/ai-service/llm/context-v2/storage/event-store.js +10 -0
- package/dist/ai-service/llm/context-v2/storage/event-store.js.map +1 -0
- package/dist/ai-service/llm/context-v2/storage/event-types.d.ts +50 -0
- package/dist/ai-service/llm/context-v2/storage/event-types.d.ts.map +1 -0
- package/dist/ai-service/llm/context-v2/storage/event-types.js +10 -0
- package/dist/ai-service/llm/context-v2/storage/event-types.js.map +1 -0
- package/dist/ai-service/llm/context-v2/storage/jsonl-event-store.d.ts +87 -0
- package/dist/ai-service/llm/context-v2/storage/jsonl-event-store.d.ts.map +1 -0
- package/dist/ai-service/llm/context-v2/storage/jsonl-event-store.js +184 -0
- package/dist/ai-service/llm/context-v2/storage/jsonl-event-store.js.map +1 -0
- package/dist/ai-service/llm/context-v2/storage/migration.d.ts +49 -0
- package/dist/ai-service/llm/context-v2/storage/migration.d.ts.map +1 -0
- package/dist/ai-service/llm/context-v2/storage/migration.js +126 -0
- package/dist/ai-service/llm/context-v2/storage/migration.js.map +1 -0
- package/dist/ai-service/llm/context-v2/types.d.ts +16 -11
- package/dist/ai-service/llm/context-v2/types.d.ts.map +1 -1
- package/dist/ai-service/llm/context-v2/types.js +2 -2
- package/dist/ai-service/llm/context-v2/types.js.map +1 -1
- package/dist/ai-service/llm/impl/clark.d.ts +8 -0
- package/dist/ai-service/llm/impl/clark.d.ts.map +1 -1
- package/dist/ai-service/llm/impl/clark.js +8 -0
- package/dist/ai-service/llm/impl/clark.js.map +1 -1
- package/dist/ai-service/llm/interaction/adapters/vercel.d.ts.map +1 -1
- package/dist/ai-service/llm/interaction/adapters/vercel.js +17 -1
- package/dist/ai-service/llm/interaction/adapters/vercel.js.map +1 -1
- package/dist/ai-service/llm/provider.d.ts.map +1 -1
- package/dist/ai-service/llm/provider.js +1 -3
- package/dist/ai-service/llm/provider.js.map +1 -1
- package/dist/ai-service/llm/stream/observers/context.d.ts +23 -0
- package/dist/ai-service/llm/stream/observers/context.d.ts.map +1 -1
- package/dist/ai-service/llm/stream/observers/context.js +62 -0
- package/dist/ai-service/llm/stream/observers/context.js.map +1 -1
- package/dist/ai-service/llm/tool-context-integrity.d.ts +25 -0
- package/dist/ai-service/llm/tool-context-integrity.d.ts.map +1 -0
- package/dist/ai-service/llm/tool-context-integrity.js +141 -0
- package/dist/ai-service/llm/tool-context-integrity.js.map +1 -0
- package/dist/ai-service/skills/system/superblocks-migration/skill.generated.d.ts +1 -1
- package/dist/ai-service/skills/system/superblocks-migration/skill.generated.d.ts.map +1 -1
- package/dist/ai-service/skills/system/superblocks-migration/skill.generated.js +6 -1
- package/dist/ai-service/skills/system/superblocks-migration/skill.generated.js.map +1 -1
- package/dist/ai-service/skills/system/third-party-migration/skill.generated.d.ts +1 -1
- package/dist/ai-service/skills/system/third-party-migration/skill.generated.d.ts.map +1 -1
- package/dist/ai-service/skills/system/third-party-migration/skill.generated.js +3 -2
- package/dist/ai-service/skills/system/third-party-migration/skill.generated.js.map +1 -1
- package/dist/ai-service/state-machine/clark-fsm.d.ts +23 -2
- package/dist/ai-service/state-machine/clark-fsm.d.ts.map +1 -1
- package/dist/ai-service/state-machine/clark-fsm.js +37 -2
- package/dist/ai-service/state-machine/clark-fsm.js.map +1 -1
- package/dist/ai-service/state-machine/handlers/agent-planning.d.ts.map +1 -1
- package/dist/ai-service/state-machine/handlers/agent-planning.js +29 -2
- package/dist/ai-service/state-machine/handlers/agent-planning.js.map +1 -1
- package/dist/ai-service/state-machine/handlers/awaiting-user.d.ts.map +1 -1
- package/dist/ai-service/state-machine/handlers/awaiting-user.js +9 -0
- package/dist/ai-service/state-machine/handlers/awaiting-user.js.map +1 -1
- package/dist/ai-service/state-machine/handlers/llm-generating.d.ts.map +1 -1
- package/dist/ai-service/state-machine/handlers/llm-generating.js +25 -2
- package/dist/ai-service/state-machine/handlers/llm-generating.js.map +1 -1
- package/dist/ai-service/state-machine/helpers/fetch-with-reconnect-retry.d.ts +6 -0
- package/dist/ai-service/state-machine/helpers/fetch-with-reconnect-retry.d.ts.map +1 -0
- package/dist/ai-service/state-machine/helpers/fetch-with-reconnect-retry.js +36 -0
- package/dist/ai-service/state-machine/helpers/fetch-with-reconnect-retry.js.map +1 -0
- package/dist/ai-service/state-machine/helpers/stable-peer.d.ts +30 -1
- package/dist/ai-service/state-machine/helpers/stable-peer.d.ts.map +1 -1
- package/dist/ai-service/state-machine/helpers/stable-peer.js +85 -2
- package/dist/ai-service/state-machine/helpers/stable-peer.js.map +1 -1
- package/dist/ai-service/state-machine/helpers/user-preferences.d.ts +8 -0
- package/dist/ai-service/state-machine/helpers/user-preferences.d.ts.map +1 -0
- package/dist/ai-service/state-machine/helpers/user-preferences.js +11 -0
- package/dist/ai-service/state-machine/helpers/user-preferences.js.map +1 -0
- package/dist/ai-service/template-renderer.d.ts +18 -1
- package/dist/ai-service/template-renderer.d.ts.map +1 -1
- package/dist/ai-service/template-renderer.js +73 -12
- package/dist/ai-service/template-renderer.js.map +1 -1
- package/dist/ai-service/types.d.ts +157 -0
- package/dist/ai-service/types.d.ts.map +1 -1
- package/dist/ai-service/types.js +137 -0
- package/dist/ai-service/types.js.map +1 -1
- package/dist/ai-service/util/llm-config-utils.d.ts +21 -22
- package/dist/ai-service/util/llm-config-utils.d.ts.map +1 -1
- package/dist/ai-service/util/llm-config-utils.js +40 -67
- package/dist/ai-service/util/llm-config-utils.js.map +1 -1
- package/dist/file-sync-vite-plugin.d.ts.map +1 -1
- package/dist/file-sync-vite-plugin.js +5 -0
- package/dist/file-sync-vite-plugin.js.map +1 -1
- package/dist/file-system-helpers.d.ts +17 -0
- package/dist/file-system-helpers.d.ts.map +1 -1
- package/dist/file-system-helpers.js +22 -0
- package/dist/file-system-helpers.js.map +1 -1
- package/dist/git-service/index.d.ts.map +1 -1
- package/dist/git-service/index.js +15 -1
- package/dist/git-service/index.js.map +1 -1
- package/dist/migration/migration-routes.d.ts.map +1 -1
- package/dist/migration/migration-routes.js +26 -3
- package/dist/migration/migration-routes.js.map +1 -1
- package/dist/migration/translation-prompt.d.ts.map +1 -1
- package/dist/migration/translation-prompt.js +5 -0
- package/dist/migration/translation-prompt.js.map +1 -1
- package/dist/migration-templates/app-fullstack/package.json +2 -2
- package/dist/policy-gate-callback-mapper.d.ts +24 -0
- package/dist/policy-gate-callback-mapper.d.ts.map +1 -0
- package/dist/policy-gate-callback-mapper.js +61 -0
- package/dist/policy-gate-callback-mapper.js.map +1 -0
- package/dist/policy-gate-runner.d.ts +32 -0
- package/dist/policy-gate-runner.d.ts.map +1 -0
- package/dist/policy-gate-runner.js +191 -0
- package/dist/policy-gate-runner.js.map +1 -0
- package/dist/router-parser.d.ts.map +1 -1
- package/dist/router-parser.js +107 -11
- package/dist/router-parser.js.map +1 -1
- package/dist/sync-service/list-dir.d.ts.map +1 -1
- package/dist/sync-service/list-dir.js +13 -3
- package/dist/sync-service/list-dir.js.map +1 -1
- package/package.json +28 -10
package/dist/ai-service/index.js
CHANGED
|
@@ -1,24 +1,29 @@
|
|
|
1
|
-
import { randomUUID } from "node:crypto";
|
|
1
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
2
2
|
import os from "node:os";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import { generateObject, hasToolCall, } from "ai";
|
|
4
|
+
import { generateObject, hasToolCall, stepCountIs, streamText as aiStreamText, } from "ai";
|
|
5
5
|
import { z } from "zod";
|
|
6
6
|
import { AiMode, resolveAttachmentFileName, } from "@superblocksteam/library-shared/types";
|
|
7
7
|
import { addTracingToMethods, classifyAttachmentDelivery, FactAccessType, FactCreatedSource, FactEntityScope, TracedEventEmitter, } from "@superblocksteam/shared";
|
|
8
8
|
import { buildTranslationPrompt } from "../migration/translation-prompt.js";
|
|
9
9
|
import { getErrorMeta, getLogger, setDefaultLogger } from "../util/logger.js";
|
|
10
|
+
import { composeSecurityAgentPrompt, } from "./agent/prompts/build-security-scan-prompt.js";
|
|
10
11
|
import { getToolCallArguments, getToolErrorCode, getToolOutput, } from "./agent/tool-message-utils.js";
|
|
12
|
+
import { buildSecurityScanTools, } from "./agent/tools.js";
|
|
11
13
|
import { hasPendingEscalations, flushPendingEscalations, } from "./agent/tools/apis/api-validation-orchestrator.js";
|
|
12
14
|
import { safeSampleJson } from "./agent/tools/apis/sample-json.js";
|
|
13
15
|
import { resolveSdkApiEntryPoint } from "./agent/tools/apis/sdk-registry.js";
|
|
14
16
|
import { readFile } from "./agent/tools/build-read-file.js";
|
|
15
17
|
import { debugCache } from "./agent/tools/debug-cache.js";
|
|
16
18
|
import { buildReadFileToolFactory } from "./agent/tools/index.js";
|
|
19
|
+
import { securityScanResultSchema, } from "./agent/tools/report-security-findings.js";
|
|
17
20
|
import { SessionEntityPermissionStore, } from "./agent/tools2/entity-permissions.js";
|
|
18
21
|
import { ToolRegistry } from "./agent/tools2/registry.js";
|
|
19
22
|
import { explainCodeFinalizeToolFactory } from "./agent/tools2/tools/explain-code-finalize.js";
|
|
20
23
|
import { FileSystemInterface } from "./app-interface/file-system-interface.js";
|
|
21
24
|
import { createDefaultVirtualFileSystem, createPathValidator, } from "./app-interface/filesystem/index.js";
|
|
25
|
+
import { NpmPackageLookup } from "./app-interface/npm-package-lookup.js";
|
|
26
|
+
import { NpmRegistryClient, createDiskRegistryPolicyStore, } from "./app-interface/npm-registry.js";
|
|
22
27
|
import { AppShell } from "./app-interface/shell.js";
|
|
23
28
|
import { AppSkillsManager } from "./app-skills/manager.js";
|
|
24
29
|
import { isUploadedAttachment, toUploadedAttachmentContentPart, } from "./attachments/uploaded-content-part.js";
|
|
@@ -35,7 +40,9 @@ import { CsvJudgeStorage } from "./judge/storage/csv-storage.js";
|
|
|
35
40
|
import { KnowledgeManager } from "./knowledge/knowledge-manager.js";
|
|
36
41
|
import { createChaosFetch } from "./llm/chaos-fetch.js";
|
|
37
42
|
import { LLMClient } from "./llm/client.js";
|
|
43
|
+
import { selectContextManagementStrategy, } from "./llm/context-v2/context-management.js";
|
|
38
44
|
import { ContextManagerV2 } from "./llm/context-v2/manager.js";
|
|
45
|
+
import { JsonlEventStore } from "./llm/context-v2/storage/jsonl-event-store.js";
|
|
39
46
|
import { LocalContextStorageV2 } from "./llm/context-v2/storage/local.js";
|
|
40
47
|
import { createLLMProvider } from "./llm/provider.js";
|
|
41
48
|
import { traceLLM } from "./llmobs/helpers.js";
|
|
@@ -62,12 +69,145 @@ import { transitionFrom } from "./state-machine/helpers/transition.js";
|
|
|
62
69
|
import { TemplateRenderer } from "./template-renderer.js";
|
|
63
70
|
import { createJsonStreamParser } from "./util/json-stream-parser.js";
|
|
64
71
|
import { processLLMConfig } from "./util/llm-config-utils.js";
|
|
72
|
+
import { buildModeMessage } from "./util/mode-message.js";
|
|
65
73
|
import { parseJwt } from "./util/parse-jwt.js";
|
|
66
74
|
import { getToolCallSignature } from "./util/tool-signature.js";
|
|
67
75
|
export { AiServiceFeatureFlags } from "./features.js";
|
|
68
76
|
export { stripResolvedFromLockfile } from "./app-interface/npm-registry.js";
|
|
69
77
|
export { SnapshotManager } from "./recording/index.js";
|
|
70
78
|
export { isSdkApiTemplate } from "./sdk-api-templates.js";
|
|
79
|
+
/**
|
|
80
|
+
* APPS-4370. Build the disk-backed `LastKnownRegistryPolicyStore` the
|
|
81
|
+
* production `AiService` wires into its default `NpmRegistryClient`.
|
|
82
|
+
*
|
|
83
|
+
* Exported so unit tests can exercise the exact wiring path the
|
|
84
|
+
* constructor uses without standing up a full AiService — the
|
|
85
|
+
* constructor calls this function inline. Tests pass a tmp directory as
|
|
86
|
+
* `rootDir`; production callers leave the field undefined on
|
|
87
|
+
* `AiServiceConfig` and we fall back to `os.homedir()` at the call
|
|
88
|
+
* site.
|
|
89
|
+
*
|
|
90
|
+
* Kept as a thin pass-through (rather than inlining
|
|
91
|
+
* `createDiskRegistryPolicyStore`) so the production wiring shape is
|
|
92
|
+
* named and reviewable: a single function, a single call site, one
|
|
93
|
+
* place to assert about in coverage.
|
|
94
|
+
*/
|
|
95
|
+
export function buildLastKnownPolicyStore(opts) {
|
|
96
|
+
return createDiskRegistryPolicyStore(opts);
|
|
97
|
+
}
|
|
98
|
+
const MAX_POLICY_GATE_STREAM_ERRORS = 5;
|
|
99
|
+
const MAX_POLICY_GATE_STREAM_ERROR_ARRAY_LENGTH = 10;
|
|
100
|
+
const MAX_POLICY_GATE_STREAM_ERROR_FIELD_LENGTH = 500;
|
|
101
|
+
const MAX_POLICY_GATE_STREAM_ERROR_OBJECT_DEPTH = 3;
|
|
102
|
+
const MAX_POLICY_GATE_STREAM_ERROR_OBJECT_KEYS = 20;
|
|
103
|
+
const POLICY_GATE_STREAM_ERROR_REDACTED_VALUE = "[redacted]";
|
|
104
|
+
const POLICY_GATE_STREAM_ERROR_SECRET_KEY_PATTERN = /(access[-_]?key|api[-_]?key|authorization|connection[-_]?string|credential|dsn|password|private[-_]?key|secret|token)/i;
|
|
105
|
+
const POLICY_GATE_STREAM_ERROR_SECRET_VALUE_PATTERN_SOURCES = [
|
|
106
|
+
String.raw `\bbearer\s+[a-z0-9._~+/=-]+`,
|
|
107
|
+
String.raw `\bhttps?:\/\/[^@\s/:]+:[^@\s]+@\S+`,
|
|
108
|
+
String.raw `\b(?:mongodb(?:\+srv)?|mysql|postgres(?:ql)?|snowflake):\/\/\S+`,
|
|
109
|
+
String.raw `\bA[SK]IA[0-9A-Z]{16}\b`,
|
|
110
|
+
String.raw `\bgh[pousr]_[a-z0-9_]{20,}\b`,
|
|
111
|
+
String.raw `\bsk-[a-z0-9_-]{8,}\b`,
|
|
112
|
+
String.raw `\bxox[baprs]-[a-z0-9-]{10,}\b`,
|
|
113
|
+
];
|
|
114
|
+
function truncatePolicyGateStreamErrorField(value) {
|
|
115
|
+
if (value.length <= MAX_POLICY_GATE_STREAM_ERROR_FIELD_LENGTH) {
|
|
116
|
+
return value;
|
|
117
|
+
}
|
|
118
|
+
return `${value.slice(0, MAX_POLICY_GATE_STREAM_ERROR_FIELD_LENGTH)}...`;
|
|
119
|
+
}
|
|
120
|
+
function sanitizePolicyGateStreamErrorString(value) {
|
|
121
|
+
const redacted = POLICY_GATE_STREAM_ERROR_SECRET_VALUE_PATTERN_SOURCES.reduce((current, patternSource) => current.replace(new RegExp(patternSource, "gi"), POLICY_GATE_STREAM_ERROR_REDACTED_VALUE), value);
|
|
122
|
+
return truncatePolicyGateStreamErrorField(redacted);
|
|
123
|
+
}
|
|
124
|
+
function isUnhelpfulPolicyGateStreamError(serialized) {
|
|
125
|
+
return (Object.keys(serialized).length === 1 &&
|
|
126
|
+
serialized.message === "[object Object]");
|
|
127
|
+
}
|
|
128
|
+
function serializePolicyGateStreamError(error, streamPart) {
|
|
129
|
+
const attachStreamPartIfUseful = (serialized) => {
|
|
130
|
+
if (isUnhelpfulPolicyGateStreamError(serialized) &&
|
|
131
|
+
streamPart &&
|
|
132
|
+
typeof streamPart === "object") {
|
|
133
|
+
return {
|
|
134
|
+
...serialized,
|
|
135
|
+
streamPart: serializePolicyGateStreamErrorRecord(streamPart, 0),
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
return serialized;
|
|
139
|
+
};
|
|
140
|
+
if (error instanceof Error) {
|
|
141
|
+
const serialized = {
|
|
142
|
+
message: sanitizePolicyGateStreamErrorString(error.message),
|
|
143
|
+
name: error.name,
|
|
144
|
+
};
|
|
145
|
+
const extraFields = serializePolicyGateStreamErrorRecord(error, 0);
|
|
146
|
+
return attachStreamPartIfUseful({
|
|
147
|
+
...extraFields,
|
|
148
|
+
...serialized,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
if (error && typeof error === "object") {
|
|
152
|
+
const serialized = serializePolicyGateStreamErrorRecord(error, 0);
|
|
153
|
+
if (Object.keys(serialized).length > 0) {
|
|
154
|
+
return attachStreamPartIfUseful(serialized);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return attachStreamPartIfUseful({
|
|
158
|
+
message: truncatePolicyGateStreamErrorField(String(error)),
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
function serializePolicyGateStreamErrorValue(value, depth) {
|
|
162
|
+
if (typeof value === "string") {
|
|
163
|
+
return sanitizePolicyGateStreamErrorString(value);
|
|
164
|
+
}
|
|
165
|
+
if (typeof value === "number" ||
|
|
166
|
+
typeof value === "boolean" ||
|
|
167
|
+
value === null) {
|
|
168
|
+
return value;
|
|
169
|
+
}
|
|
170
|
+
if (value instanceof Error) {
|
|
171
|
+
return {
|
|
172
|
+
...serializePolicyGateStreamErrorRecord(value, depth + 1),
|
|
173
|
+
message: sanitizePolicyGateStreamErrorString(value.message),
|
|
174
|
+
name: value.name,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
if (Array.isArray(value)) {
|
|
178
|
+
if (depth >= MAX_POLICY_GATE_STREAM_ERROR_OBJECT_DEPTH) {
|
|
179
|
+
return `[array:${value.length}]`;
|
|
180
|
+
}
|
|
181
|
+
return value
|
|
182
|
+
.slice(0, MAX_POLICY_GATE_STREAM_ERROR_ARRAY_LENGTH)
|
|
183
|
+
.map((item) => serializePolicyGateStreamErrorValue(item, depth + 1));
|
|
184
|
+
}
|
|
185
|
+
if (value && typeof value === "object") {
|
|
186
|
+
if (depth >= MAX_POLICY_GATE_STREAM_ERROR_OBJECT_DEPTH) {
|
|
187
|
+
return "[object]";
|
|
188
|
+
}
|
|
189
|
+
return serializePolicyGateStreamErrorRecord(value, depth + 1);
|
|
190
|
+
}
|
|
191
|
+
if (value === undefined) {
|
|
192
|
+
return undefined;
|
|
193
|
+
}
|
|
194
|
+
return sanitizePolicyGateStreamErrorString(String(value));
|
|
195
|
+
}
|
|
196
|
+
function serializePolicyGateStreamErrorRecord(value, depth) {
|
|
197
|
+
const output = {};
|
|
198
|
+
for (const key of Object.keys(value).slice(0, MAX_POLICY_GATE_STREAM_ERROR_OBJECT_KEYS)) {
|
|
199
|
+
if (POLICY_GATE_STREAM_ERROR_SECRET_KEY_PATTERN.test(key)) {
|
|
200
|
+
output[key] = POLICY_GATE_STREAM_ERROR_REDACTED_VALUE;
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
const fieldValue = value[key];
|
|
204
|
+
const serializedValue = serializePolicyGateStreamErrorValue(fieldValue, depth);
|
|
205
|
+
if (serializedValue !== undefined) {
|
|
206
|
+
output[key] = serializedValue;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return output;
|
|
210
|
+
}
|
|
71
211
|
// Helper functions for console log and runtime error buffer management
|
|
72
212
|
export function pruneByTTL(items, ttlMs) {
|
|
73
213
|
const now = Date.now();
|
|
@@ -89,6 +229,29 @@ export function estimateSize(obj) {
|
|
|
89
229
|
return 0;
|
|
90
230
|
}
|
|
91
231
|
}
|
|
232
|
+
/**
|
|
233
|
+
* How long the npm-registry client's `getJwt` will block waiting for the
|
|
234
|
+
* first `tokenUpdated` event when no JWT is yet primed. Kept conservative —
|
|
235
|
+
* a cold prefetch that misses the window degrades to last-known-good or
|
|
236
|
+
* not-configured rather than crashing the install path.
|
|
237
|
+
*/
|
|
238
|
+
const NPM_REGISTRY_JWT_WAIT_MS = 5_000;
|
|
239
|
+
/**
|
|
240
|
+
* How long the npm-registry client will wait for a fresh `tokenUpdated`
|
|
241
|
+
* event between an HTTP 401 and its retry.
|
|
242
|
+
*
|
|
243
|
+
* Two failure modes for the refresh:
|
|
244
|
+
* - `waitForTokenUpdate` times out (push-refresh channel hung): the
|
|
245
|
+
* adapter throws, the client treats it as a refresh-channel failure
|
|
246
|
+
* and routes through `serveStaleOrUnconfigured` rather than retrying
|
|
247
|
+
* with the stale JWT — the install path gets last-known-good (or
|
|
248
|
+
* `not-configured`).
|
|
249
|
+
* - No `tokenManager` is wired (SaaS without push refresh, some test
|
|
250
|
+
* fixtures): the adapter resolves immediately on the next microtask;
|
|
251
|
+
* the retry runs with the existing JWT, and a second 401 hard-throws
|
|
252
|
+
* via the second-401 branch in `NpmRegistryClient.fetchWithRefresh`.
|
|
253
|
+
*/
|
|
254
|
+
const NPM_REGISTRY_REFRESH_WAIT_MS = 5_000;
|
|
92
255
|
export function truncateString(str, maxBytes) {
|
|
93
256
|
// Rough approximation: assume 2 bytes per character
|
|
94
257
|
const maxChars = Math.floor(maxBytes / 2);
|
|
@@ -148,6 +311,39 @@ export class AiService extends TracedEventEmitter {
|
|
|
148
311
|
entityPermissionStore = new SessionEntityPermissionStore();
|
|
149
312
|
_onGenerationCompleteCallback;
|
|
150
313
|
tokenManagerJwt;
|
|
314
|
+
/**
|
|
315
|
+
* Listener body for `tokenManager.on("tokenUpdated", ...)`. Defined as a
|
|
316
|
+
* prototype method (not a class field arrow) so tests can exercise the
|
|
317
|
+
* real implementation via `AiService.prototype.handleTokenUpdated`; the
|
|
318
|
+
* constructor binds `this` explicitly when registering with the emitter.
|
|
319
|
+
* Wrapped in try/catch because EventEmitter re-throws synchronous
|
|
320
|
+
* listener exceptions — a logger transport disconnect during JWT
|
|
321
|
+
* rotation would otherwise crash the process. `console.error` is the
|
|
322
|
+
* fallback because it cannot itself trigger the emitter chain.
|
|
323
|
+
*/
|
|
324
|
+
handleTokenUpdated(event) {
|
|
325
|
+
try {
|
|
326
|
+
this.tokenManagerJwt = event.token;
|
|
327
|
+
this.getLogger().info(`[ai-service] token updated via tokenManager`);
|
|
328
|
+
}
|
|
329
|
+
catch (err) {
|
|
330
|
+
console.error("[ai-service] tokenUpdated listener threw", err);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
npmRegistryClient;
|
|
334
|
+
/**
|
|
335
|
+
* Long-lived `NpmPackageLookup` reused across every `buildTools()` call.
|
|
336
|
+
* Constructed once in the AiService constructor and passed into the
|
|
337
|
+
* per-turn tool factory via `ClarkStateHandlerParams`. The factory used
|
|
338
|
+
* to `new NpmPackageLookup(...)` on every LLM generation turn, which
|
|
339
|
+
* leaked the `superblocks.npm.lookup.cache_size` `ObservableGauge`
|
|
340
|
+
* callback (the OTel API keeps a strong reference to every registered
|
|
341
|
+
* callback) and produced multiple same-instrument/same-attribute
|
|
342
|
+
* observations per collection cycle. Sharing one instance lifecycle-
|
|
343
|
+
* bound to the AiService ties the gauge to the process lifetime — same
|
|
344
|
+
* pattern as `npmRegistryClient` / `templateRenderer` / `appShell`.
|
|
345
|
+
*/
|
|
346
|
+
npmPackageLookup;
|
|
151
347
|
viteServer;
|
|
152
348
|
mcpServerManager;
|
|
153
349
|
recordingManager;
|
|
@@ -163,6 +359,21 @@ export class AiService extends TracedEventEmitter {
|
|
|
163
359
|
get clarkState() {
|
|
164
360
|
return this.clark.state;
|
|
165
361
|
}
|
|
362
|
+
/**
|
|
363
|
+
* Exposes the shared `NpmRegistryClient` so external SDK dev-server startup
|
|
364
|
+
* paths in `cli-replacement/dev.mts` — the home `~/.npmrc` writer
|
|
365
|
+
* (APPS-4231) and the startup `installPackages` call that honors the
|
|
366
|
+
* per-org `allow_install_scripts` policy (APPS-4251) — can share the
|
|
367
|
+
* in-memory cache, 401-refresh, and last-known-policy state already used
|
|
368
|
+
* by AppShell and TemplateRenderer. A separate client would fragment that
|
|
369
|
+
* state and double the network roundtrips against the registry-config
|
|
370
|
+
* endpoint. The client is always constructed (LD flag and empty-list
|
|
371
|
+
* cases short-circuit internally) so callers can treat this as
|
|
372
|
+
* non-optional once the AiService has been initialized.
|
|
373
|
+
*/
|
|
374
|
+
getNpmRegistryClient() {
|
|
375
|
+
return this.npmRegistryClient;
|
|
376
|
+
}
|
|
166
377
|
/**
|
|
167
378
|
* Injects git and sync services for the git tool (called by the plugin when native git is available).
|
|
168
379
|
*/
|
|
@@ -292,6 +503,71 @@ export class AiService extends TracedEventEmitter {
|
|
|
292
503
|
// Keep this opt-out alongside the injected model factory.
|
|
293
504
|
useSuperblocksAuthorizationHeader: config.providerSettings?.useSuperblocksAuthorizationHeader,
|
|
294
505
|
};
|
|
506
|
+
// Seed `tokenManagerJwt` synchronously from the manager's current
|
|
507
|
+
// value before any consumer that calls `getJwt()` is constructed. The
|
|
508
|
+
// `tokenUpdated` listener below covers FUTURE refreshes; this read
|
|
509
|
+
// covers the cold-start case where the CLI (or any earlier code) has
|
|
510
|
+
// already called `tokenManager.updateToken(...)` before this AiService
|
|
511
|
+
// was constructed. Without it, the prior emission is lost — Node
|
|
512
|
+
// `EventEmitter` has no replay — and `NpmRegistryClient.getJwt()`
|
|
513
|
+
// would time out via `waitForJwt(...)`, downgrading `syncHomeNpmrc`
|
|
514
|
+
// to `unreachable` and leaving `~/.npmrc` unwritten on cold boot.
|
|
515
|
+
this.tokenManagerJwt = this.config.tokenManager?.getCurrentToken();
|
|
516
|
+
// Resolve the npm registry client once and reuse across TemplateRenderer
|
|
517
|
+
// and AppShell so both install paths share the same in-memory cache and
|
|
518
|
+
// 401-refresh state. Explicit injection wins (tests); otherwise we
|
|
519
|
+
// construct a default that reads the LD flag from `config.features` and
|
|
520
|
+
// routes JWT through the AiService's own `getJwt()` (which prefers the
|
|
521
|
+
// tokenManager token and falls back to clark.context.jwt). Empty
|
|
522
|
+
// tokens are refused upstream of `attempt()` so the server doesn't
|
|
523
|
+
// see `Bearer ` and reply 400 (which the client would now treat as a
|
|
524
|
+
// hard fail rather than a transient outage).
|
|
525
|
+
//
|
|
526
|
+
// APPS-4370. Wire the disk-backed `LastKnownRegistryPolicyStore` here
|
|
527
|
+
// so the production client carries `everConfigured` end-to-end. Without
|
|
528
|
+
// this the unreachable short-circuit in AppShell is inert: the marker
|
|
529
|
+
// writer is fire-and-forget and `hasEverBeenConfigured()` always
|
|
530
|
+
// resolves false when no store is wired. `rootDir` is `os.homedir()`
|
|
531
|
+
// for the same reason the home-npmrc writer roots at
|
|
532
|
+
// `~/.superblocks/...`: the marker lives next to the existing
|
|
533
|
+
// CLI-managed state and survives application-directory recycles.
|
|
534
|
+
const npmRegistryClient = config.npmRegistryClient ??
|
|
535
|
+
new NpmRegistryClient({
|
|
536
|
+
isFlagEnabled: () => config.features.npmRegistryEnabled === true,
|
|
537
|
+
// The prefetch can fire before tokenManager has primed
|
|
538
|
+
// `tokenManagerJwt`. Block (with a short timeout) on the next
|
|
539
|
+
// `tokenUpdated` event rather than throwing immediately, so the
|
|
540
|
+
// first install in the shared template dir actually gets a
|
|
541
|
+
// `.npmrc` instead of silently downgrading to "not configured".
|
|
542
|
+
getJwt: () => this.waitForJwt(NPM_REGISTRY_JWT_WAIT_MS),
|
|
543
|
+
// Push-based refresh hook: on 401, wait briefly for the next
|
|
544
|
+
// `tokenUpdated` event before re-reading the JWT. Without this,
|
|
545
|
+
// a 401 retry fires immediately with the same expired token and
|
|
546
|
+
// throws `re-authenticated and still received HTTP 401` for any
|
|
547
|
+
// user whose JWT expired mid-install.
|
|
548
|
+
refreshJwt: () => this.waitForTokenUpdate(NPM_REGISTRY_REFRESH_WAIT_MS),
|
|
549
|
+
organizationId: config.organizationId,
|
|
550
|
+
baseUrl: config.superblocksBaseUrl,
|
|
551
|
+
lastKnownPolicyStore: buildLastKnownPolicyStore({
|
|
552
|
+
organizationId: config.organizationId,
|
|
553
|
+
rootDir: config.policyStoreRootDir ?? os.homedir(),
|
|
554
|
+
}),
|
|
555
|
+
});
|
|
556
|
+
this.npmRegistryClient = npmRegistryClient;
|
|
557
|
+
// One process-lifetime `NpmPackageLookup` shared across every
|
|
558
|
+
// `buildTools()` call. The `build_lookupNpmPackage` tool factory used
|
|
559
|
+
// to construct a fresh instance per LLM turn; each construction
|
|
560
|
+
// registered an `ObservableGauge` callback for
|
|
561
|
+
// `superblocks.npm.lookup.cache_size` that the OTel API keeps strongly
|
|
562
|
+
// referenced for the lifetime of the meter. `ToolRegistry.clear()`
|
|
563
|
+
// does not dispose tool resources, so every cleared instance's callback
|
|
564
|
+
// (and the cache it closed over) stuck around for the process lifetime
|
|
565
|
+
// and emitted multiple same-instrument/same-attribute observations per
|
|
566
|
+
// collection cycle. Owning the lookup here — same pattern as
|
|
567
|
+
// `npmRegistryClient` / `templateRenderer` / `appShell` — means the
|
|
568
|
+
// gauge wires up exactly once and the 60s TTL cache stays warm across
|
|
569
|
+
// turns instead of being thrown away.
|
|
570
|
+
this.npmPackageLookup = new NpmPackageLookup({ client: npmRegistryClient });
|
|
295
571
|
this.templateRenderer =
|
|
296
572
|
config.templateRenderer ??
|
|
297
573
|
new TemplateRenderer({
|
|
@@ -301,6 +577,7 @@ export class AiService extends TracedEventEmitter {
|
|
|
301
577
|
// process.cwd() — process.cwd() is fragile in CI matrix shards and
|
|
302
578
|
// monorepo sub-invocations. See PR #19282 (F3).
|
|
303
579
|
detectionCwd: config.appRootDirPath,
|
|
580
|
+
npmRegistryClient,
|
|
304
581
|
});
|
|
305
582
|
// Create KnowledgeManager before AppShell so it can be passed to VirtualFileSystem
|
|
306
583
|
this.knowledgeManager = new KnowledgeManager();
|
|
@@ -309,6 +586,7 @@ export class AiService extends TracedEventEmitter {
|
|
|
309
586
|
fsOperationQueue: config.fsOperationQueue,
|
|
310
587
|
draftInterface: config.draftInterface,
|
|
311
588
|
templateRenderer: this.templateRenderer,
|
|
589
|
+
npmRegistryClient,
|
|
312
590
|
virtualFileSystem: createDefaultVirtualFileSystem({
|
|
313
591
|
useSystemSkills: config.features.useSystemSkills,
|
|
314
592
|
knowledgeManager: this.knowledgeManager,
|
|
@@ -344,15 +622,13 @@ export class AiService extends TracedEventEmitter {
|
|
|
344
622
|
logger: config.logger,
|
|
345
623
|
});
|
|
346
624
|
if (this.config.tokenManager) {
|
|
347
|
-
this.config.tokenManager.on("tokenUpdated", (
|
|
348
|
-
this.tokenManagerJwt = event.token;
|
|
349
|
-
this.getLogger().info(`[ai-service] token updated via tokenManager`);
|
|
350
|
-
});
|
|
625
|
+
this.config.tokenManager.on("tokenUpdated", this.handleTokenUpdated.bind(this));
|
|
351
626
|
}
|
|
352
627
|
this.clarkProfiler = new ClarkProfiler(this.sessionId);
|
|
353
628
|
this.clarkProfiler.initialize(this.appShell);
|
|
354
629
|
this.contextManagerV2 = new ContextManagerV2({
|
|
355
630
|
storage: new LocalContextStorageV2(config.appRootDirPath),
|
|
631
|
+
eventStore: new JsonlEventStore(config.appRootDirPath),
|
|
356
632
|
rootDir: config.appRootDirPath,
|
|
357
633
|
getModeState: () => {
|
|
358
634
|
const mode = this.clark?.context?.currentMode;
|
|
@@ -363,7 +639,7 @@ export class AiService extends TracedEventEmitter {
|
|
|
363
639
|
planApproved: this.clark?.context?.planContext?.approved,
|
|
364
640
|
};
|
|
365
641
|
},
|
|
366
|
-
remoteRestoreFallback: async () => {
|
|
642
|
+
remoteRestoreFallback: async (id) => {
|
|
367
643
|
const jwt = this.getJwt();
|
|
368
644
|
// Prefer the live sync service commitId (set after uploads), but on
|
|
369
645
|
// fresh pods downloadDirectory doesn't set it. Fall back to the
|
|
@@ -382,6 +658,8 @@ export class AiService extends TracedEventEmitter {
|
|
|
382
658
|
superblocksBaseUrl: config.superblocksBaseUrl,
|
|
383
659
|
logger: this.getLogger(),
|
|
384
660
|
jwt,
|
|
661
|
+
contextId: id,
|
|
662
|
+
appRootDirPath: config.appRootDirPath,
|
|
385
663
|
});
|
|
386
664
|
},
|
|
387
665
|
});
|
|
@@ -553,6 +831,20 @@ export class AiService extends TracedEventEmitter {
|
|
|
553
831
|
logger: this.getLogger(),
|
|
554
832
|
templateRenderer: this.templateRenderer,
|
|
555
833
|
appShell: this.appShell,
|
|
834
|
+
// AiServiceConfig.npmRegistryClient is optional (injectable test
|
|
835
|
+
// seam); the SDK's `dev()` never passes it in production, so
|
|
836
|
+
// spreading `...this.config` above leaves it `undefined` and any
|
|
837
|
+
// state-handler / tool that reads `services.npmRegistryClient`
|
|
838
|
+
// sees nothing. The `build_lookupNpmPackage` tool's `!this.client`
|
|
839
|
+
// branch silently downgraded to public npm because of this gap.
|
|
840
|
+
// Carry the AiService's resolved instance through explicitly,
|
|
841
|
+
// same pattern as `appShell` / `templateRenderer` above.
|
|
842
|
+
npmRegistryClient: this.npmRegistryClient,
|
|
843
|
+
// Pass the AiService's long-lived lookup so `build_lookupNpmPackage`
|
|
844
|
+
// reuses one instance across LLM turns (gauge registered once,
|
|
845
|
+
// cache warm). See the `npmPackageLookup` field comment for why
|
|
846
|
+
// per-turn instantiation in the tool factory leaked OTel callbacks.
|
|
847
|
+
npmPackageLookup: this.npmPackageLookup,
|
|
556
848
|
signals: this,
|
|
557
849
|
fileSystemInterface: this.fileSystemInterface,
|
|
558
850
|
integrationStore: this.integrationStore,
|
|
@@ -627,6 +919,79 @@ export class AiService extends TracedEventEmitter {
|
|
|
627
919
|
// fallback to clark context if no token manager is provided
|
|
628
920
|
return this.clark?.context?.jwt;
|
|
629
921
|
}
|
|
922
|
+
buildContextManagementForCurrentClark(llmConfig) {
|
|
923
|
+
const modeInstructions = this.clark.context.currentMode
|
|
924
|
+
? `Preserve the following mode context in your summary: ${buildModeMessage(this.clark.context.currentMode, this.clark.context.planContext?.approved)}`
|
|
925
|
+
: undefined;
|
|
926
|
+
return selectContextManagementStrategy({
|
|
927
|
+
serverContextManagementEnabled: llmConfig?.serverContextManagement?.enabled,
|
|
928
|
+
modeInstructions,
|
|
929
|
+
thresholds: llmConfig?.serverContextManagement,
|
|
930
|
+
summarizationModel: () => createLLMProvider(this.getLLMProviderSettings(llmConfig), () => this.getJwt(), llmConfig, () => ({
|
|
931
|
+
prompt_id: this.clark.context.promptId,
|
|
932
|
+
chatmessage_id: this.clark.context.chatmessageId,
|
|
933
|
+
application_id: this.config.applicationId,
|
|
934
|
+
session_id: this.sessionId,
|
|
935
|
+
mode: this.clark.context.currentMode === AiMode.PLAN ? "plan" : "build",
|
|
936
|
+
plan_size: this.clark.context.planSize,
|
|
937
|
+
})).modelForTask("summarizeMessages"),
|
|
938
|
+
});
|
|
939
|
+
}
|
|
940
|
+
/**
|
|
941
|
+
* Resolve a JWT for the npm-registry fetch, blocking briefly on the
|
|
942
|
+
* first `tokenUpdated` event when no JWT is yet primed. The
|
|
943
|
+
* `TemplateRenderer` prefetch fires during AiService construction —
|
|
944
|
+
* before the `tokenManager` subscription has emitted — so a synchronous
|
|
945
|
+
* `getJwt()` would observe `undefined` and silently downgrade the
|
|
946
|
+
* private-registry install to public npm. Blocking here is the
|
|
947
|
+
* narrowest fix: callers that already have a primed JWT take the fast
|
|
948
|
+
* path and never await.
|
|
949
|
+
*/
|
|
950
|
+
async waitForJwt(timeoutMs) {
|
|
951
|
+
const immediate = this.getJwt();
|
|
952
|
+
if (immediate) {
|
|
953
|
+
return immediate;
|
|
954
|
+
}
|
|
955
|
+
if (!this.config.tokenManager) {
|
|
956
|
+
throw new Error("[npm-registry] no JWT available and no tokenManager wired to wait for one");
|
|
957
|
+
}
|
|
958
|
+
await this.waitForTokenUpdate(timeoutMs);
|
|
959
|
+
const after = this.getJwt();
|
|
960
|
+
if (!after) {
|
|
961
|
+
throw new Error("[npm-registry] no JWT available after waiting for tokenManager update");
|
|
962
|
+
}
|
|
963
|
+
return after;
|
|
964
|
+
}
|
|
965
|
+
/**
|
|
966
|
+
* Resolve when the next `tokenUpdated` event lands on `tokenManager`,
|
|
967
|
+
* or reject when `timeoutMs` elapses without one. When no
|
|
968
|
+
* `tokenManager` is wired (test fixtures, SaaS paths without push
|
|
969
|
+
* refresh), resolves immediately on the next microtask.
|
|
970
|
+
*
|
|
971
|
+
* Resolution only signals "a wait completed" — never that a JWT actually
|
|
972
|
+
* changed. The constructor's `tokenUpdated` listener (above) is the only
|
|
973
|
+
* code that mutates `tokenManagerJwt`. Callers (`waitForJwt`, the
|
|
974
|
+
* npm-registry `refreshJwt` adapter) must re-read the JWT after this
|
|
975
|
+
* resolves and treat an unchanged value the same as a refresh that
|
|
976
|
+
* never happened.
|
|
977
|
+
*/
|
|
978
|
+
waitForTokenUpdate(timeoutMs) {
|
|
979
|
+
const tokenManager = this.config.tokenManager;
|
|
980
|
+
if (!tokenManager) {
|
|
981
|
+
return Promise.resolve();
|
|
982
|
+
}
|
|
983
|
+
return new Promise((resolve, reject) => {
|
|
984
|
+
const onUpdate = () => {
|
|
985
|
+
clearTimeout(timer);
|
|
986
|
+
resolve();
|
|
987
|
+
};
|
|
988
|
+
const timer = setTimeout(() => {
|
|
989
|
+
tokenManager.off("tokenUpdated", onUpdate);
|
|
990
|
+
reject(new Error(`[npm-registry] timed out after ${timeoutMs}ms waiting for tokenManager update`));
|
|
991
|
+
}, timeoutMs);
|
|
992
|
+
tokenManager.once("tokenUpdated", onUpdate);
|
|
993
|
+
});
|
|
994
|
+
}
|
|
630
995
|
createClarkTagProviderFactory() {
|
|
631
996
|
return (fsm) => {
|
|
632
997
|
const clark = fsm;
|
|
@@ -712,6 +1077,12 @@ export class AiService extends TracedEventEmitter {
|
|
|
712
1077
|
if (this.clark.context.pendingToolPermissionRequest) {
|
|
713
1078
|
return { dispatched: false, reason: "pending-tool-permission" };
|
|
714
1079
|
}
|
|
1080
|
+
if (this.clark.state === ClarkStateNames.AwaitingUser) {
|
|
1081
|
+
if (this.clark.context.activeInteractiveMessage ||
|
|
1082
|
+
this.clark.context.deferredInteractiveMessage) {
|
|
1083
|
+
return { dispatched: false, reason: "awaiting-interactive-response" };
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
715
1086
|
const checklist = await readPersistedChecklist(this.appRootDirPath);
|
|
716
1087
|
if (!checklist) {
|
|
717
1088
|
this.getLogger().warn("[ai-service] Migration checklist not found or unreadable; skipping translation turn");
|
|
@@ -764,7 +1135,7 @@ export class AiService extends TracedEventEmitter {
|
|
|
764
1135
|
getMigrationSafeLlmConfig(llmConfig) {
|
|
765
1136
|
const result = filterDisabledToolsForMigration(llmConfig);
|
|
766
1137
|
if (result.removedTools.length > 0) {
|
|
767
|
-
this.getLogger().warn("[ai-service] Migration turn overrides operator-disabled tools required for migration: install/checklist/askMultiChoice", { removedTools: result.removedTools });
|
|
1138
|
+
this.getLogger().warn("[ai-service] Migration turn overrides operator-disabled tools required for migration: install/checklist/askMultiChoice/build_navigatePreview/build_captureScreenshot", { removedTools: result.removedTools });
|
|
768
1139
|
}
|
|
769
1140
|
return result.config;
|
|
770
1141
|
}
|
|
@@ -814,6 +1185,7 @@ export class AiService extends TracedEventEmitter {
|
|
|
814
1185
|
},
|
|
815
1186
|
llmConfig: request.llmConfig,
|
|
816
1187
|
partialResponse: undefined,
|
|
1188
|
+
userPreferences: request.userPreferences,
|
|
817
1189
|
};
|
|
818
1190
|
});
|
|
819
1191
|
const transitionTo = transitionFrom(this.clark);
|
|
@@ -1688,12 +2060,13 @@ export class AiService extends TracedEventEmitter {
|
|
|
1688
2060
|
}
|
|
1689
2061
|
catch (error) {
|
|
1690
2062
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1691
|
-
//
|
|
2063
|
+
// Provider-facing context expects tool failures to be encoded as
|
|
2064
|
+
// errored tool results so they still satisfy the preceding tool call.
|
|
1692
2065
|
toolResultMessage = {
|
|
1693
2066
|
role: "tool",
|
|
1694
2067
|
content: [
|
|
1695
2068
|
{
|
|
1696
|
-
type: "tool-
|
|
2069
|
+
type: "tool-result",
|
|
1697
2070
|
toolCallId,
|
|
1698
2071
|
toolName,
|
|
1699
2072
|
output: {
|
|
@@ -1712,9 +2085,7 @@ export class AiService extends TracedEventEmitter {
|
|
|
1712
2085
|
type: "tool-error",
|
|
1713
2086
|
toolCallId,
|
|
1714
2087
|
toolName,
|
|
1715
|
-
error:
|
|
1716
|
-
? errorMessage
|
|
1717
|
-
: JSON.stringify(errorMessage),
|
|
2088
|
+
error: errorMessage,
|
|
1718
2089
|
errorCode,
|
|
1719
2090
|
});
|
|
1720
2091
|
}
|
|
@@ -1749,10 +2120,8 @@ export class AiService extends TracedEventEmitter {
|
|
|
1749
2120
|
};
|
|
1750
2121
|
logger.info(`[ai-service] Tool ${toolName} permission denied, error injected into context`);
|
|
1751
2122
|
}
|
|
1752
|
-
const
|
|
1753
|
-
const
|
|
1754
|
-
const summarizationModel = llmProvider.modelForTask("summarizeMessages");
|
|
1755
|
-
const context = await this.contextManagerV2.getOrCreateContext(contextId, summarizationModel, llmConfig?.contextOptionsV2);
|
|
2123
|
+
const contextManagement = this.buildContextManagementForCurrentClark(llmConfig);
|
|
2124
|
+
const context = await this.contextManagerV2.getOrCreateContext(contextId, llmConfig?.contextOptionsV2, undefined, contextManagement);
|
|
1756
2125
|
const placeholderUsage = {
|
|
1757
2126
|
inputTokens: 0,
|
|
1758
2127
|
outputTokens: 0,
|
|
@@ -2185,6 +2554,184 @@ export class AiService extends TracedEventEmitter {
|
|
|
2185
2554
|
this.tokenManagerJwt = previousJwt;
|
|
2186
2555
|
}
|
|
2187
2556
|
}
|
|
2557
|
+
async handlePolicyGateExecution(request) {
|
|
2558
|
+
const logger = this.getLogger();
|
|
2559
|
+
logger.info(`[ai-service] Policy gate execution requested for app=${request.applicationId}`);
|
|
2560
|
+
const requestLlmConfig = request.llmConfig;
|
|
2561
|
+
const contextLlmConfig = this.clark.context.llmConfig;
|
|
2562
|
+
const llmConfig = requestLlmConfig ?? contextLlmConfig;
|
|
2563
|
+
const llmProvider = createLLMProvider(this.getLLMProviderSettings(llmConfig), () => this.getJwt(), llmConfig, () => ({
|
|
2564
|
+
ai_agent_run_id: request.aiAgentRunId,
|
|
2565
|
+
application_id: request.applicationId,
|
|
2566
|
+
session_id: this.sessionId,
|
|
2567
|
+
trigger_type: "policy_gate",
|
|
2568
|
+
}));
|
|
2569
|
+
const model = llmProvider.modelForTask("generateBroadEdit");
|
|
2570
|
+
const availableIntegrations = await this.integrationStore.availableIntegrations();
|
|
2571
|
+
const integrations = availableIntegrations.map((i) => ({
|
|
2572
|
+
name: i.name,
|
|
2573
|
+
pluginType: i.plugin?.id ?? "unknown",
|
|
2574
|
+
}));
|
|
2575
|
+
const appContext = { integrations };
|
|
2576
|
+
const systemPrompt = composeSecurityAgentPrompt(appContext, request.adminPrompt);
|
|
2577
|
+
const startedAt = Date.now();
|
|
2578
|
+
const toolCallCounts = {};
|
|
2579
|
+
const filePathsRead = new Set();
|
|
2580
|
+
const filePathsListed = new Set();
|
|
2581
|
+
const finishReasons = [];
|
|
2582
|
+
const grepPatterns = [];
|
|
2583
|
+
const grepMatchCounts = [];
|
|
2584
|
+
const grepPatternByToolCallId = new Map();
|
|
2585
|
+
const streamErrors = [];
|
|
2586
|
+
const streamPartTypeCounts = {};
|
|
2587
|
+
let textDeltaChars = 0;
|
|
2588
|
+
const promptHash = createHash("sha256").update(systemPrompt).digest("hex");
|
|
2589
|
+
const modelName = typeof model === "string" ? model : model.modelId;
|
|
2590
|
+
const scanRoot = typeof this.appShell.appRootDirPath === "string"
|
|
2591
|
+
? this.appShell.appRootDirPath
|
|
2592
|
+
: undefined;
|
|
2593
|
+
let scanResult;
|
|
2594
|
+
const buildRuntimeMetadata = (finalFailureReason) => ({
|
|
2595
|
+
diagnosticsVersion: 2,
|
|
2596
|
+
durationMs: Date.now() - startedAt,
|
|
2597
|
+
filePathsListed: Array.from(filePathsListed).slice(0, 100),
|
|
2598
|
+
filePathsRead: Array.from(filePathsRead).slice(0, 100),
|
|
2599
|
+
finalFailureReason,
|
|
2600
|
+
finishReasons,
|
|
2601
|
+
grepMatchCounts,
|
|
2602
|
+
grepPatterns,
|
|
2603
|
+
model: modelName,
|
|
2604
|
+
promptHash,
|
|
2605
|
+
reportToolCalled: Boolean(scanResult),
|
|
2606
|
+
scanComplete: scanResult?.scanComplete === true,
|
|
2607
|
+
scanRoot,
|
|
2608
|
+
streamErrors,
|
|
2609
|
+
streamPartTypeCounts,
|
|
2610
|
+
textDeltaChars,
|
|
2611
|
+
toolCallCounts,
|
|
2612
|
+
});
|
|
2613
|
+
const failPolicyGateExecution = (message) => {
|
|
2614
|
+
const error = new Error(message);
|
|
2615
|
+
error.runtimeMetadataJson = buildRuntimeMetadata(message);
|
|
2616
|
+
throw error;
|
|
2617
|
+
};
|
|
2618
|
+
const scanToolsDeps = {
|
|
2619
|
+
appShell: this.appShell,
|
|
2620
|
+
features: this.config.features ?? {},
|
|
2621
|
+
integrationStore: this.integrationStore,
|
|
2622
|
+
llmProvider: llmProvider,
|
|
2623
|
+
};
|
|
2624
|
+
const tools = await buildSecurityScanTools(this.clark, scanToolsDeps);
|
|
2625
|
+
const result = aiStreamText({
|
|
2626
|
+
messages: [
|
|
2627
|
+
{ content: systemPrompt, role: "system" },
|
|
2628
|
+
{
|
|
2629
|
+
content: "Begin your security review of this application now.",
|
|
2630
|
+
role: "user",
|
|
2631
|
+
},
|
|
2632
|
+
],
|
|
2633
|
+
model,
|
|
2634
|
+
tools,
|
|
2635
|
+
stopWhen: stepCountIs(30),
|
|
2636
|
+
});
|
|
2637
|
+
for await (const part of result.fullStream) {
|
|
2638
|
+
const partType = typeof part.type === "string"
|
|
2639
|
+
? part.type
|
|
2640
|
+
: "unknown";
|
|
2641
|
+
streamPartTypeCounts[partType] =
|
|
2642
|
+
(streamPartTypeCounts[partType] ?? 0) + 1;
|
|
2643
|
+
if (partType === "error") {
|
|
2644
|
+
const streamError = serializePolicyGateStreamError(part.error, part);
|
|
2645
|
+
if (streamErrors.length < MAX_POLICY_GATE_STREAM_ERRORS) {
|
|
2646
|
+
streamErrors.push(streamError);
|
|
2647
|
+
}
|
|
2648
|
+
logger.warn("[ai-service] Policy gate stream error", { streamError });
|
|
2649
|
+
}
|
|
2650
|
+
const textDelta = part.textDelta ??
|
|
2651
|
+
part.text ??
|
|
2652
|
+
part.delta;
|
|
2653
|
+
if (typeof textDelta === "string") {
|
|
2654
|
+
textDeltaChars += textDelta.length;
|
|
2655
|
+
}
|
|
2656
|
+
const finishReason = part.finishReason;
|
|
2657
|
+
if (typeof finishReason === "string") {
|
|
2658
|
+
finishReasons.push(finishReason);
|
|
2659
|
+
}
|
|
2660
|
+
if (part.type === "tool-call") {
|
|
2661
|
+
toolCallCounts[part.toolName] =
|
|
2662
|
+
(toolCallCounts[part.toolName] ?? 0) + 1;
|
|
2663
|
+
const input = part.input ??
|
|
2664
|
+
part.args;
|
|
2665
|
+
if (input && typeof input === "object") {
|
|
2666
|
+
const record = input;
|
|
2667
|
+
if (part.toolName === "build_readFile" &&
|
|
2668
|
+
typeof record.filePath === "string") {
|
|
2669
|
+
filePathsRead.add(record.filePath);
|
|
2670
|
+
}
|
|
2671
|
+
if (part.toolName === "grep" && typeof record.pattern === "string") {
|
|
2672
|
+
grepPatterns.push(record.pattern);
|
|
2673
|
+
const toolCallId = typeof part.toolCallId === "string"
|
|
2674
|
+
? part.toolCallId
|
|
2675
|
+
: `grep-${grepPatterns.length}`;
|
|
2676
|
+
grepPatternByToolCallId.set(toolCallId, record.pattern);
|
|
2677
|
+
}
|
|
2678
|
+
}
|
|
2679
|
+
}
|
|
2680
|
+
if (part.type === "tool-result") {
|
|
2681
|
+
const output = part.output ??
|
|
2682
|
+
part.result;
|
|
2683
|
+
if (output && typeof output === "object") {
|
|
2684
|
+
const record = output;
|
|
2685
|
+
if (part.toolName === "build_listFiles" &&
|
|
2686
|
+
Array.isArray(record.files)) {
|
|
2687
|
+
for (const file of record.files) {
|
|
2688
|
+
if (typeof file === "string") {
|
|
2689
|
+
filePathsListed.add(file);
|
|
2690
|
+
}
|
|
2691
|
+
}
|
|
2692
|
+
}
|
|
2693
|
+
if (part.toolName === "grep" && typeof record.matches === "number") {
|
|
2694
|
+
const toolCallId = typeof part.toolCallId === "string"
|
|
2695
|
+
? part.toolCallId
|
|
2696
|
+
: `grep-${grepMatchCounts.length + 1}`;
|
|
2697
|
+
grepMatchCounts.push({
|
|
2698
|
+
matches: record.matches,
|
|
2699
|
+
pattern: grepPatternByToolCallId.get(toolCallId) ??
|
|
2700
|
+
grepPatterns[grepPatterns.length - 1] ??
|
|
2701
|
+
"",
|
|
2702
|
+
});
|
|
2703
|
+
}
|
|
2704
|
+
}
|
|
2705
|
+
}
|
|
2706
|
+
if (part.type === "tool-call" && part.toolName === "reportReviewRun") {
|
|
2707
|
+
const input = part.input ??
|
|
2708
|
+
part.args;
|
|
2709
|
+
const parsedInput = securityScanResultSchema.safeParse(input);
|
|
2710
|
+
if (!parsedInput.success) {
|
|
2711
|
+
logger.error("[ai-service] Invalid policy gate reportReviewRun input", getErrorMeta(parsedInput.error));
|
|
2712
|
+
failPolicyGateExecution("Invalid policy gate reportReviewRun input");
|
|
2713
|
+
}
|
|
2714
|
+
scanResult = parsedInput.data;
|
|
2715
|
+
}
|
|
2716
|
+
}
|
|
2717
|
+
if (!scanResult) {
|
|
2718
|
+
logger.warn("[ai-service] Policy gate agent did not call reportReviewRun");
|
|
2719
|
+
failPolicyGateExecution("Policy gate agent did not complete a report");
|
|
2720
|
+
}
|
|
2721
|
+
const completedScanResult = scanResult;
|
|
2722
|
+
if (!completedScanResult.scanComplete) {
|
|
2723
|
+
logger.warn("[ai-service] Policy gate agent reported an incomplete scan", {
|
|
2724
|
+
findingCount: completedScanResult.findings.length,
|
|
2725
|
+
scannedFiles: completedScanResult.scannedFiles,
|
|
2726
|
+
});
|
|
2727
|
+
failPolicyGateExecution("Policy gate agent did not complete a report");
|
|
2728
|
+
}
|
|
2729
|
+
logger.info(`[ai-service] Policy gate execution complete: ${completedScanResult.findings.length} findings`);
|
|
2730
|
+
return {
|
|
2731
|
+
...completedScanResult,
|
|
2732
|
+
runtimeMetadataJson: buildRuntimeMetadata(),
|
|
2733
|
+
};
|
|
2734
|
+
}
|
|
2188
2735
|
handleUserChange(change) {
|
|
2189
2736
|
this.getLogger().info(`[ai-service] User changed: ${change.type}`, change);
|
|
2190
2737
|
this.clark.updateContext((context) => ({
|