diff --git a/.github/actions/prune-repository/prune_test.py b/.github/actions/prune-repository/prune_test.py index 5147d556bcb..fa07b4fb326 100644 --- a/.github/actions/prune-repository/prune_test.py +++ b/.github/actions/prune-repository/prune_test.py @@ -58,10 +58,10 @@ def test_sibling_or_website_job_does_not_add_brunch_extras(self) -> None: self.assertEqual(extra_paths_for_requested({TRANSPORT}), []) self.assertEqual(extra_paths_for_requested({WEBSITE}), []) - def test_transitive_core_in_the_closure_does_not_add_the_app(self) -> None: + def test_core_in_the_dependency_closure_does_not_add_the_app(self) -> None: dependencies = { - WEBSITE: frozenset({TRANSPORT}), - TRANSPORT: frozenset({CORE}), + WEBSITE: frozenset({CORE, TRANSPORT}), + TRANSPORT: frozenset(), CORE: frozenset(), } expanded = fixpoint_expand({WEBSITE}, dependencies) diff --git a/.yarnrc.yml b/.yarnrc.yml index 51acfada9eb..75546ed9dc5 100644 --- a/.yarnrc.yml +++ b/.yarnrc.yml @@ -2,6 +2,8 @@ approvedGitRepositories: [] defaultSemverRangePrefix: "" +enableConstraintsChecks: true + enableScripts: false enableTransparentWorkspaces: false diff --git a/apps/petrinaut-website/package.json b/apps/petrinaut-website/package.json index 06d57bb1b91..f8e33266dd9 100644 --- a/apps/petrinaut-website/package.json +++ b/apps/petrinaut-website/package.json @@ -19,6 +19,7 @@ }, "dependencies": { "@ai-sdk/openai": "3.0.63", + "@hashintel/brunch-agent": "workspace:*", "@hashintel/brunch-agent-transport-aisdk": "workspace:*", "@hashintel/ds-components": "workspace:*", "@hashintel/ds-helpers": "workspace:*", diff --git a/apps/petrinaut-website/src/main/app/local-storage-demo/brunch-ask-interactive-tool.tsx b/apps/petrinaut-website/src/main/app/local-storage-demo/brunch-ask-interactive-tool.tsx index 1e8bed24fc6..7c4d01b4442 100644 --- a/apps/petrinaut-website/src/main/app/local-storage-demo/brunch-ask-interactive-tool.tsx +++ b/apps/petrinaut-website/src/main/app/local-storage-demo/brunch-ask-interactive-tool.tsx @@ -6,7 +6,7 @@ import { type BrunchAskOutput, parseBrunchAskInput, parseBrunchAskOutput, -} from "@hashintel/brunch-agent-transport-aisdk/client-tools"; +} from "@hashintel/brunch-agent/client-tools"; import { css } from "@hashintel/ds-helpers/css"; import { definePetrinautAiInteractiveTool, diff --git a/apps/petrinaut-website/src/main/app/local-storage-demo/brunch-ask-mapping.ts b/apps/petrinaut-website/src/main/app/local-storage-demo/brunch-ask-mapping.ts index 833f3243c3a..a3887e6d9c4 100644 --- a/apps/petrinaut-website/src/main/app/local-storage-demo/brunch-ask-mapping.ts +++ b/apps/petrinaut-website/src/main/app/local-storage-demo/brunch-ask-mapping.ts @@ -1,7 +1,7 @@ import type { BrunchAskInput, BrunchAskOutput, -} from "@hashintel/brunch-agent-transport-aisdk/client-tools"; +} from "@hashintel/brunch-agent/client-tools"; export const brunchAskFromComposerText = ({ text, diff --git a/apps/petrinaut-website/src/main/app/local-storage-demo/brunch-panel-transport.ts b/apps/petrinaut-website/src/main/app/local-storage-demo/brunch-panel-transport.ts index 6069ee67f2a..3cdc3e19459 100644 --- a/apps/petrinaut-website/src/main/app/local-storage-demo/brunch-panel-transport.ts +++ b/apps/petrinaut-website/src/main/app/local-storage-demo/brunch-panel-transport.ts @@ -1,4 +1,4 @@ -import { SWEEP_TOOL_NAME } from "@hashintel/brunch-agent-transport-aisdk/client-tools"; +import { SWEEP_TOOL_NAME } from "@hashintel/brunch-agent/client-tools"; import { sweepOutputSchema } from "../brunch-sweep-output"; diff --git a/apps/petrinaut-website/src/main/app/voice-interview/canonical-speech.test.ts b/apps/petrinaut-website/src/main/app/voice-interview/canonical-speech.test.ts index 9e6b1c3d80f..dc12cda21c6 100644 --- a/apps/petrinaut-website/src/main/app/voice-interview/canonical-speech.test.ts +++ b/apps/petrinaut-website/src/main/app/voice-interview/canonical-speech.test.ts @@ -1,6 +1,6 @@ import { describe, expect, test } from "vitest"; -import { ASK_TOOL_NAME } from "@hashintel/brunch-agent-transport-aisdk/client-tools"; +import { ASK_TOOL_NAME } from "@hashintel/brunch-agent/client-tools"; import { hashCanonicalSpeechText, diff --git a/apps/petrinaut-website/src/main/app/voice-interview/canonical-speech.ts b/apps/petrinaut-website/src/main/app/voice-interview/canonical-speech.ts index 151db772ab5..bb111385b7e 100644 --- a/apps/petrinaut-website/src/main/app/voice-interview/canonical-speech.ts +++ b/apps/petrinaut-website/src/main/app/voice-interview/canonical-speech.ts @@ -1,7 +1,7 @@ import { ASK_TOOL_NAME, parseBrunchAskInput, -} from "@hashintel/brunch-agent-transport-aisdk/client-tools"; +} from "@hashintel/brunch-agent/client-tools"; import { hashCanonicalSpeechText } from "../../../canonical-speech-fingerprint"; diff --git a/apps/petrinaut-website/src/main/app/voice-interview/interview-coverage.test.ts b/apps/petrinaut-website/src/main/app/voice-interview/interview-coverage.test.ts index 84d329b5062..dc3846fefa4 100644 --- a/apps/petrinaut-website/src/main/app/voice-interview/interview-coverage.test.ts +++ b/apps/petrinaut-website/src/main/app/voice-interview/interview-coverage.test.ts @@ -1,6 +1,6 @@ import { describe, expect, test } from "vitest"; -import { SWEEP_TOOL_NAME } from "@hashintel/brunch-agent-transport-aisdk/client-tools"; +import { SWEEP_TOOL_NAME } from "@hashintel/brunch-agent/client-tools"; import { selectInterviewCoverage } from "./interview-coverage"; diff --git a/apps/petrinaut-website/src/main/app/voice-interview/interview-coverage.ts b/apps/petrinaut-website/src/main/app/voice-interview/interview-coverage.ts index 5f1ef5b4164..c6e9f763bbc 100644 --- a/apps/petrinaut-website/src/main/app/voice-interview/interview-coverage.ts +++ b/apps/petrinaut-website/src/main/app/voice-interview/interview-coverage.ts @@ -1,4 +1,4 @@ -import { SWEEP_TOOL_NAME } from "@hashintel/brunch-agent-transport-aisdk/client-tools"; +import { SWEEP_TOOL_NAME } from "@hashintel/brunch-agent/client-tools"; import { sweepOutputSchema } from "../brunch-sweep-output"; diff --git a/libs/@hashintel/brunch-agent/packages/core/src/client-tools.ts b/libs/@hashintel/brunch-agent/packages/core/src/client-tools.ts index 3cea4ff89a6..b866300d288 100644 --- a/libs/@hashintel/brunch-agent/packages/core/src/client-tools.ts +++ b/libs/@hashintel/brunch-agent/packages/core/src/client-tools.ts @@ -2,5 +2,22 @@ * Browser-safe source contracts for Brunch tools rendered by a host UI. */ -export { AskInput, AskSubmission } from "./ask-tool-contract"; -export { toolName, type ToolName } from "./naming"; +import * as v from "valibot"; + +import { AskInput, AskSubmission } from "./ask-tool-contract"; +import { toolName } from "./naming"; + +export { AskInput, AskSubmission, toolName }; +export type { ToolName } from "./naming"; + +export const ASK_TOOL_NAME = toolName("ask"); +export const SWEEP_TOOL_NAME = toolName("sweep"); + +export type BrunchAskInput = v.InferOutput; +export type BrunchAskOutput = v.InferOutput; + +export const parseBrunchAskInput = (input: unknown): BrunchAskInput => + v.parse(AskInput, input); + +export const parseBrunchAskOutput = (output: unknown): BrunchAskOutput => + v.parse(AskSubmission, output); diff --git a/libs/@hashintel/brunch-agent/packages/core/turbo.json b/libs/@hashintel/brunch-agent/packages/core/turbo.json index d6f41dc7c53..5da6b59f7d6 100644 --- a/libs/@hashintel/brunch-agent/packages/core/turbo.json +++ b/libs/@hashintel/brunch-agent/packages/core/turbo.json @@ -14,6 +14,15 @@ }, "linear:graph": { "cache": false + }, + "test:unit": { + "inputs": [ + "$TURBO_DEFAULT$", + "$TURBO_ROOT$/apps/brunch-agent/**", + "$TURBO_ROOT$/libs/@hashintel/brunch-agent/**", + "!$TURBO_ROOT$/apps/brunch-agent/**/{.flue,.turbo,dist,node_modules}/**", + "!$TURBO_ROOT$/libs/@hashintel/brunch-agent/**/{.flue,.turbo,dist,node_modules}/**" + ] } } } diff --git a/libs/@hashintel/brunch-agent/packages/transport-aisdk/.oxlintrc.json b/libs/@hashintel/brunch-agent/packages/transport-aisdk/.oxlintrc.json index 8cf02a4f042..d7ed5324c29 100644 --- a/libs/@hashintel/brunch-agent/packages/transport-aisdk/.oxlintrc.json +++ b/libs/@hashintel/brunch-agent/packages/transport-aisdk/.oxlintrc.json @@ -22,6 +22,10 @@ { "name": "@hashintel/petrinaut", "message": "Brunch libraries must not depend on Petrinaut implementations." + }, + { + "name": "@hashintel/brunch-agent", + "message": "Brunch transports must remain independent of Brunch core." } ], "patterns": [ @@ -38,8 +42,8 @@ "message": "Brunch transports must remain substrate-independent." }, { - "group": ["@hashintel/brunch-agent-*"], - "message": "A transport may depend inward on the harness, not on Brunch extensions." + "group": ["@hashintel/brunch-agent/*", "@hashintel/brunch-agent-*"], + "message": "Brunch transports must remain independent of Brunch core and extensions." } ] } diff --git a/libs/@hashintel/brunch-agent/packages/transport-aisdk/package.json b/libs/@hashintel/brunch-agent/packages/transport-aisdk/package.json index 2d9591e4d43..9a58144ce85 100644 --- a/libs/@hashintel/brunch-agent/packages/transport-aisdk/package.json +++ b/libs/@hashintel/brunch-agent/packages/transport-aisdk/package.json @@ -10,10 +10,6 @@ "types": "./src/index.ts", "import": "./dist/index.js" }, - "./client-tools": { - "types": "./src/client-tools.ts", - "import": "./dist/client-tools.js" - }, "./headers": { "types": "./src/headers.ts", "import": "./dist/headers.js" @@ -27,7 +23,6 @@ "test:unit": "vitest run" }, "dependencies": { - "@hashintel/brunch-agent": "workspace:*", "ai": "6.0.182", "valibot": "1.4.2" }, diff --git a/libs/@hashintel/brunch-agent/packages/transport-aisdk/src/client-tools.ts b/libs/@hashintel/brunch-agent/packages/transport-aisdk/src/client-tools.ts deleted file mode 100644 index a61e161d6dd..00000000000 --- a/libs/@hashintel/brunch-agent/packages/transport-aisdk/src/client-tools.ts +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Browser-safe wire contract for the Brunch tools a host UI executes. - */ - -import * as v from "valibot"; - -import { - AskInput, - AskSubmission, - toolName, -} from "@hashintel/brunch-agent/client-tools"; - -export const ASK_TOOL_NAME = toolName("ask"); -export const SWEEP_TOOL_NAME = toolName("sweep"); - -export type BrunchAskInput = v.InferOutput; -export type BrunchAskOutput = v.InferOutput; - -export const parseBrunchAskInput = (input: unknown): BrunchAskInput => - v.parse(AskInput, input); - -export const parseBrunchAskOutput = (output: unknown): BrunchAskOutput => - v.parse(AskSubmission, output); diff --git a/libs/@hashintel/brunch-agent/packages/transport-aisdk/vite.config.ts b/libs/@hashintel/brunch-agent/packages/transport-aisdk/vite.config.ts index 369dc937a5d..a23d137f9ac 100644 --- a/libs/@hashintel/brunch-agent/packages/transport-aisdk/vite.config.ts +++ b/libs/@hashintel/brunch-agent/packages/transport-aisdk/vite.config.ts @@ -8,9 +8,6 @@ export default defineConfig({ build: { lib: { entry: { - "client-tools": fileURLToPath( - new URL("src/client-tools.ts", import.meta.url), - ), headers: fileURLToPath(new URL("src/headers.ts", import.meta.url)), index: fileURLToPath(new URL("src/index.ts", import.meta.url)), }, @@ -18,7 +15,7 @@ export default defineConfig({ formats: ["es"], }, rolldownOptions: { - external: [/^@hashintel\/brunch-agent(?:\/.*)?$/u, "ai", "valibot"], + external: ["ai", "valibot"], }, sourcemap: true, }, diff --git a/yarn.config.cjs b/yarn.config.cjs index 4270b58385e..79fd15816d2 100644 --- a/yarn.config.cjs +++ b/yarn.config.cjs @@ -40,6 +40,10 @@ const ignoredWorkspaces = []; const allowedGitDependencies = []; +const brunchAgentCore = "@hashintel/brunch-agent"; +const brunchAgentTransportPrefix = `${brunchAgentCore}-transport-`; +const brunchSubstrateScopes = ["@earendil-works/", "@flue/"]; + /** * Lifecycle scripts which Yarn runs as part of `yarn install`. */ @@ -319,6 +323,30 @@ function enforceNoInstallScripts({ Yarn }) { } } +/** + * Keeps transport packages independent of Brunch mechanism and substrates. + * + * @param {Context} context - The Yarn constraint context. + */ +function enforceBrunchTransportBoundary({ Yarn }) { + for (const dependency of Yarn.dependencies()) { + if ( + !dependency.workspace.ident.startsWith(brunchAgentTransportPrefix) || + (dependency.ident !== brunchAgentCore && + !dependency.ident.startsWith(`${brunchAgentCore}-`) && + !brunchSubstrateScopes.some((scope) => + dependency.ident.startsWith(scope), + )) + ) { + continue; + } + + dependency.error( + `${dependency.workspace.ident} is a transport and cannot depend on ${dependency.ident}`, + ); + } +} + module.exports = defineConfig({ async constraints(context) { enforceConsistentDependenciesAcrossTheProject(context); @@ -326,5 +354,6 @@ module.exports = defineConfig({ enforceProtocols(context); enforceDevDependenciesAreProperlyDeclared(context); enforceNoInstallScripts(context); + enforceBrunchTransportBoundary(context); }, }); diff --git a/yarn.lock b/yarn.lock index 91401d202c6..6af226a8342 100644 --- a/yarn.lock +++ b/yarn.lock @@ -919,6 +919,7 @@ __metadata: dependencies: "@ai-sdk/openai": "npm:3.0.63" "@fast-check/vitest": "npm:0.4.1" + "@hashintel/brunch-agent": "workspace:*" "@hashintel/brunch-agent-transport-aisdk": "workspace:*" "@hashintel/ds-components": "workspace:*" "@hashintel/ds-helpers": "workspace:*" @@ -7437,7 +7438,6 @@ __metadata: version: 0.0.0-use.local resolution: "@hashintel/brunch-agent-transport-aisdk@workspace:libs/@hashintel/brunch-agent/packages/transport-aisdk" dependencies: - "@hashintel/brunch-agent": "workspace:*" "@types/node": "npm:22.18.13" "@typescript/native-preview": "npm:7.0.0-dev.20260511.1" ai: "npm:6.0.182"