Skip to content

[A1] Move flows, integrations and utilities out of the package root into subpaths #236

Description

@LinuxDevil

Goal

The package root exports 705 names (265 values, 440 types) while the docs use about 123 of them. Flows, the credentialed Jira / GitHub / Slack / email tools, encryption, StorageService and the small validators are not agent functionality a new user needs from the root, and they make up 97 of those names. Moving them to ./flows, ./integrations and ./utils shrinks the root's type surface without removing anything. Audit evidence: .agent-loop/audit2/our-report.md section 3 (705 root exports) and section 5 (rows Flows, EncryptionUtils, StorageService, Jira, GitHub, Slack and email, Validators).

Current state

Verified on main at cc5ddb8:

  • src/index.ts is export * over 26 modules, including ./types (line 15), ./tools (21), ./flows (24), ./execution (31), ./security (37), ./storage (40) and ./utils (50).
  • Flows: src/flows/index.ts re-exports FlowBuilder, FlowExecutor, inputs, validators (18 names); the flow types are in src/types/flow.ts (33 names, re-exported by src/types/index.ts:4, so they are in the root and in ./types, but not in ./flows). ./flows already exists in package.json exports and in tsup.config.ts ('flows/index': 'src/flows/index.ts').
  • Integrations: src/tools/built-in/index.ts:9-12 re-exports ./email, ./jira, ./github, ./slack, so they are in the root and in ./tools. 24 names: createEmailTool, EmailToolOptions, createJiraTools, JiraTools, JiraConfig, JiraTicket, JiraComment, JiraTransition, createGitHubTools, GitHubTools, GitHubConfig, GitHubFile, GitHubSearchResult, GitHubPullRequest, GitHubBranch, createSlackTool, slackTool, postSlackAlert, postSlackAlertViaSandbox, buildSlackAlertPayload, SLACK_WEBHOOK_URL_ENV_KEY, SlackBlock, SlackAlertPayload, SlackToolOptions.
  • Utilities (20 names): src/security/crypto.ts (EncryptionUtils, DTOEncryptionFilter, DecryptionError, generatePassword, sha256), src/security/types.ts (EncryptionConfig, DTOEncryptionSettings, AuthorizationContext; only crypto.ts uses them), src/storage/StorageService.ts (StorageService, FileSystemAdapter, PathAdapter), src/storage/types.ts (IStorageService, StorageConfig), src/utils/validators.ts (validateWithSchema, safeValidate, isValidEmail, isValidUrl, isValidJson, sanitizeString, hasRequiredKeys). src/security/index.ts:8,11 re-exports ./types and ./crypto next to ./sandbox and ./credentialBroker. src/utils/index.ts re-exports ./errors, ./validators, ./errorCodes.
  • The two stores built on StorageService: StorageServiceApprovalStore (src/execution/ApprovalGate.ts:171) and LocalStorageCheckpointStore (src/execution/checkpoint.ts:238), both reaching the root through export * from './ApprovalGate' and export * from './checkpoint' in src/execution/index.ts:9,12. Their only purpose is to wrap a StorageService, so they move with it.
  • Internal users that must keep working: src/execution/ApprovalGate.ts:8 and src/execution/checkpoint.ts:7 import StorageService from '../storage'; src/triggers/adapters/SlackTriggerAdapter.ts:27 imports SLACK_WEBHOOK_URL_ENV_KEY from '../../tools/built-in/slack'; src/spec/specToAgent.ts:76-80 builds the error text "see src/tools/built-in/${name}.ts's create...Tools(config)", a path that does not exist for a user.
  • scripts/verify-docs-snippets.ts:66 declares the placeholder storage: import('@lousho/build-ai-agent').StorageService. Snippet paths are derived from exports (sourcePaths(), line 296-304: ./dist/X.d.ts maps to src/X.ts), so a new subpath's source file must sit at the path its types entry mirrors.
  • ./core and ./types subpaths are unaffected by this ticket except that ./types loses the flow types (they move to ./flows).

Consumers that import a moving name from the root today (grep of every import { ... } from '@lousho/build-ai-agent'):

  • apps/agent-forge/server/buildAgent.ts:17 (type AgentFlow)
  • apps/agent-forge/server/logEntries.ts:6 (FlowExecutionEvent, FlowExecutionEventOf)
  • apps/agent-forge/server/runRegistry.ts:13 (FlowExecutor)
  • apps/agent-forge/src/graph/graphToFlow.ts:1 (AgentFlow, EditorStep)
  • apps/agent-forge/src/graph/__tests__/graphToFlow.test.ts:2 (FlowExecutor)
  • docs/flows.md:9 (FlowBuilder, FlowExecutor, EditorStep)
  • docs/utilities.md:10 (EncryptionUtils, sha256) and :31 (StorageService)
  • docs/approvals.md:331 (StorageServiceApprovalStore, with AgentExecutor), table row at :362
  • docs/sessions.md:239-245 (LocalStorageCheckpointStore, StorageServiceApprovalStore), table row at :230
  • docs/durable-execution.md:11 (LocalStorageCheckpointStore)
  • examples/: none. The examples import deep source paths (examples/ops-pipeline/mocks/mockSlackTool.ts:10 from src/tools/built-in/slack), which do not change.
    Prose that names the moved exports as root exports: docs/api-overview.md:50, :590, :631-632; docs/tools.md:128; docs/errors.md:228-232; README.md:206 (utilities row).

Scope

In:

  • ./flows: src/flows/index.ts also re-exports ../types/flow (all 33 flow types and FlowChunkType). src/types/index.ts drops export * from './flow' (so the root and ./types no longer export them). src/types/agent.ts keeps importing AgentFlow from ./flow internally. Root drops export * from './flows'.
  • ./integrations (new): src/integrations/index.ts re-exports ../tools/built-in/email, jira, github, slack. The files stay where they are. src/tools/built-in/index.ts drops lines 9-12, so ./tools and the root no longer export them. Name decided: ./integrations (the plan's name; it says what they are: third-party services that need credentials).
  • ./utils (new): src/utils/index.ts becomes the subpath barrel and exports exactly: ../security/crypto, the three types of ../security/types, ../storage/StorageService, ../storage/types, StorageServiceApprovalStore from ../execution/ApprovalGate, LocalStorageCheckpointStore from ../execution/checkpoint, and ./validators. It no longer re-exports ./errors or ./errorCodes.
  • Root src/index.ts: replace export * from './utils' with export * from './utils/errors' and export * from './utils/errorCodes'; replace export * from './security' by keeping src/security/index.ts but dropping its ./types and ./crypto lines; remove export * from './storage' and delete src/storage/index.ts (change ApprovalGate.ts:8 and checkpoint.ts:7 to import from '../storage/StorageService').
  • src/execution/index.ts: replace export * from './ApprovalGate' and export * from './checkpoint' with explicit named lists that leave out StorageServiceApprovalStore and LocalStorageCheckpointStore (every other current export stays).
  • package.json exports: add "./integrations" (./dist/integrations/index.{d.ts,mjs,js}) and "./utils" (./dist/utils/index.{d.ts,mjs,js}), same shape as the existing entries. No entry is removed.
  • tsup.config.ts entry: add 'integrations/index': 'src/integrations/index.ts' and 'utils/index': 'src/utils/index.ts'.
  • src/spec/specToAgent.ts:76-80: the message names the import a user can use: "...needs credentials: build it with createGitHubTools(config) from '@lousho/build-ai-agent/integrations' and pass it to createAgent() ...". Keep the error code LOUSHO_TOOL_NEEDS_CREDENTIALS.
  • src/providers/importGraph.test.ts:29: add 'integrations/index' and 'utils/index' to ENTRY_POINTS, so the new entries are checked for loading no optional peer at import time.
  • scripts/verify-docs-snippets.ts:66: storage placeholder becomes import('@lousho/build-ai-agent/utils').StorageService.
  • Agent Forge: change the five files above to import the flow names from @lousho/build-ai-agent/flows (split the import statements; keep the other names on the root import).
  • Docs: update every consumer and prose line listed under Current state to the new subpath. Add a table of entry points to docs/installation.md at the end of the existing ## Entry points share code (ESM and CJS) section (line 114): one row per subpath in exports and what it holds. Add no heading: the Arabic pages are matched by heading position, and a new heading here would shift every heading after it.
  • A test that pins the move: src/publicSurface.test.ts imports * as root from './index', * as flows from './flows', * as integrations from './integrations', * as utils from './utils' and asserts each moved value name is absent from root and present in its subpath; src/publicSurface.test-d.ts does the same for the moved type names with expectTypeOf / @ts-expect-error on import('./index'). Later tickets (A2a, A2b, A2c, A3) append their names to these two files.
  • CHANGELOG under ## [Unreleased], a ### Breaking entry with the migration note below.

Out:

  • Removing anything: A2a, A2b, A2c remove dead exports; this ticket only moves.
  • AgentBuilder, AgentExecutor, ToolRegistry, resumeAfterApproval: A3.
  • Renaming the patch guardrails: A5.
  • FlowAttr / FLOW_NODE_SPAN_NAME (src/execution/semconv.ts) and FlowExecutionError (src/execution/errors.ts:92) stay in the root: they are telemetry constants and an error class, not the flow engine.
  • The docs site (LinuxDevil/agent-sdk-docs): no page is added or renamed, so no navigation change; the R5 sync picks up the text changes.

Acceptance criteria

  • src/publicSurface.test.ts and src/publicSurface.test-d.ts exist and pass (npm test, npm run test:types).
  • The root exports exactly 97 fewer names than on main before this PR (51 flow names, 24 integration names, 20 utility names, the 2 StorageService stores). Measure with a TypeScript checker over src/index.ts (count the exports of the module symbol) before and after, and put both numbers in the pull request.
  • ./flows, ./integrations and ./utils resolve from the packed tarball in ESM and CJS: npm run pack-smoke passes (it walks every exports entry).
  • ./tools no longer exports the 24 integration names; ./types no longer exports the 33 flow types.
  • Agent Forge checks pass: typecheck, typecheck:server, test -- --run, test:server (run npm run build first; Forge resolves the SDK through file:../..).
  • npm run docs:verify-snippets -- --skip-build passes with the updated imports.
  • docs/installation.md has the entry-point table (no new heading); docs/flows.md, docs/utilities.md, docs/approvals.md, docs/sessions.md, docs/durable-execution.md, docs/api-overview.md, docs/tools.md, docs/errors.md and README.md name the new import paths. npm run docs:llms re-run and committed.
  • CHANGELOG ### Breaking entry with this migration note:

    Flows, the Jira / GitHub / Slack / email tools, encryption, StorageService and the validators moved out of the package root. Import them from their subpath: FlowBuilder, FlowExecutor, validateFlow and every flow type (AgentFlow, EditorStep, FlowExecutionEvent, ...) from @lousho/build-ai-agent/flows; createJiraTools, createGitHubTools, createSlackTool, slackTool, postSlackAlert, createEmailTool and their types from @lousho/build-ai-agent/integrations; EncryptionUtils, DTOEncryptionFilter, DecryptionError, sha256, generatePassword, StorageService, StorageServiceApprovalStore, LocalStorageCheckpointStore and the validators (validateWithSchema, safeValidate, isValidEmail, ...) from @lousho/build-ai-agent/utils. The integrations are no longer in @lousho/build-ai-agent/tools and the flow types are no longer in @lousho/build-ai-agent/types. Nothing else changed: the classes and functions are the same. For file-backed stores with createAgent(), prefer fileStore(dir).

Live test

None: this ticket spends nothing.

Dependencies

  • R2 must merge first: the migration note points StorageService users to fileStore(dir).
  • Owner decision required before starting (see Notes).
  • Every wave-4 breaking ticket edits src/index.ts, src/execution/index.ts and CHANGELOG.md: run A1, A2a, A2b, A2c, A3, A5 one after another, not in parallel. A1 creates src/publicSurface.test.ts that the others extend.
  • Conflicts: G7 splits docs/api-overview.md; if G7 has merged, update the moved text wherever it now lives. N-wave tickets that add exports to src/index.ts may conflict textually; rebase.

Notes for the implementer

  • Owner question (record the answer in the issue before starting): "The package is public at 1.0.0-alpha.8. May A1 move flows, integrations and utilities out of the root (no removal, two new exports entries ./integrations and ./utils) in the next alpha?"
  • export * collisions: after the change, run npx tsc --noEmit and npm run build; tsup's dts build reports ambiguous re-exports. src/flows/index.ts re-exporting ../types/flow adds names that ./flows did not have; check none clashes with inputs.ts (FlowDefinitionNode is distinct).
  • Keep identity across entry points: tsup splitting: true puts shared classes in chunks, so StorageService from ./utils and the one createAgent() uses internally are the same class. Do not copy code into the new barrels; re-export only.
  • src/utils/index.ts currently has no internal importers other than src/index.ts:50 (verified with grep for from '../utils'), so turning it into the subpath barrel is safe; internal code imports ../utils/errors, ../utils/zodCompat and so on directly.
  • fallow treats exports entries as entry points, so the new barrels are not flagged; if it flags src/storage/index.ts before you delete it, that is expected.
  • Do not touch docs/research/ or docs/plan/ (history).

Round 2 ticket A1. Before starting, read the agent brief (worktree rules, verification list, live-test budget) and the plan. One ticket is one pull request; put Closes #<this issue> in it.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    breakingBreaking change: CHANGELOG entry with migration notemodel:sonnetWell specified; a Sonnet agent can take itowner-decisionNeeds the owner's answer before work startsround-2Round 2 plan ticketwave-4Round 2, wave 4

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions