Skip to content

Add install source infrastructure and package command support#2611

Merged
curtisman merged 120 commits into
microsoft:mainfrom
curtisman:installsource
Jul 8, 2026
Merged

Add install source infrastructure and package command support#2611
curtisman merged 120 commits into
microsoft:mainfrom
curtisman:installsource

Conversation

@curtisman

@curtisman curtisman commented Jul 7, 2026

Copy link
Copy Markdown
Member

Summary

This branch replaces the older ad hoc package/source plumbing with a more explicit install-source model and a host-owned app-agent lifecycle. It adds install sources for bundled catalog entries, local paths, and feed-backed packages; moves @package / @source behavior into the host/provider boundary; and documents the resulting lifecycle, source model, and update-coordination design.

What Changed

Install-source runtime in defaultAgentProvider

  • adds a dedicated installSources/ implementation for:
    • source registration and typed source-add handling
    • config persistence and runtime normalization
    • bundled catalog resolution
    • path sources and path normalization/home expansion
    • optional feed-backed sources plus auth handling
    • installed-agent materialization and registry/state handling
    • package-agent command behavior
  • reworks defaultAgentProviders.ts around installed-agent records and source-owned re-resolution
  • derives builtins from the bundled catalog and protects them from install/uninstall/update flows
  • makes feed support environment-backed and optional
  • simplifies coordination paths, install roots, and source ordering semantics

Host and dispatcher lifecycle changes

  • introduces AppAgentHost and pushes app-agent/source lifecycle ownership closer to the host boundary
  • removes the older system install command handler path in favor of host-owned command handling
  • updates dispatcher/provider interfaces so connected providers expose a narrower write surface
  • adjusts command/completion plumbing to support the new package/source command surface
  • carries loader/runtime details through the node-provider side rather than the core abstraction where appropriate
  • updates shell/api/agent-server integration points to work with the new host/provider contract

Update coordination and installed-agent behavior

  • adds version-scoped install roots and content-addressed/deduplicated feed materialization
  • reworks update/uninstall sequencing around coordinated replace/drain behavior
  • hardens connect-vs-drain, barrier, and verify-0 lifecycle cases
  • preserves enable preferences across update and refines manifest-default reconciliation
  • fixes feed resolution failure behavior and hardens npm invocation for @update <range>

User-facing command surface

  • adds/expands support for:
    • @package available
    • @package list
    • @source list
    • typed @source add ... flows
  • improves install/update status reporting so commands surface resolved source and progress
  • adds tab completion for @package commands
  • adjusts list rendering and fixes package-list line-break behavior

Documentation

  • adds architecture docs for agent lifecycle and agent sources
  • adds the embedding guide and updates dispatcher/contributing/reference docs

Feed package eligibility

  • validates the resolved feed package version's own manifest metadata before materializing it
  • requires the resolved version to keep the typeagent-agent keyword, even if package-level feed enumeration matched earlier
  • skips feed candidates whose resolved version declares an incompatible npm os constraint
  • adds focused coverage for version-specific keyword and OS filtering

Update - Complexity Baseline Exceptions

  • adds complexity baseline exception support to the complexity tooling via --exceptions-file
  • adds path normalization for exception matching so entries remain stable across report/ratchet contexts
  • updates CI complexity ratchet to use tools/scripts/code/complexity-baseline-exception.json
  • adds npm run code-complexity:ci for local CI-parity ratchet runs
  • adds npm run code-complexity:update-exceptions to regenerate the exception file from current complexity metrics
  • documents both local commands in the code-quality tooling README

curtisman added 30 commits July 2, 2026 07:16
Add design doc for unifying AppAgent install paths behind install sources
(path/catalog/feed) and a single installed-agent provider backed by
agents.json, with an InstallSourceRegistry doing ordered first-match
resolution. Covers @install/@update/@source command surfaces, post-install
live registration and uninstall teardown, feed enumeration/auth via Azure
CLI tokens, first-run pre-install of bundled-catalog agents, one-time
migration, and a decision log.
- clarify feed install location/config and host API organization in design

- restructure execution plan into milestone-based units with full/light gates

- add final branch-wide 2+2 gate and per-milestone commit requirements

- add decisions log and deferred review/test-gap log with workflow hooks
Land the new install-source substrate while the legacy providers/installer still
drive runtime — behavior-preserving, fully unit-tested (design §3, §4.1, §4.2).

- dispatcher core: pure install-source interfaces in
  agentProvider/installSource.ts (InstallSourceKind, *SourceConfig,
  ResolvedCandidate, InstalledAgentRecord, InstallSource, InstallSourceRegistry,
  ExecutionMode string-union) exported from the barrel. No impl in core, no
  cycle to default-agent-provider (§4.1 layering).
- default-agent-provider: bundled catalog data/agents.catalog.json mirroring
  config.json (every agent preinstall:true, §7/§12 Q1) + catalog loader; the 1.2
  adapter routes only the default config through the catalog.
- installSources/: path/catalog/feed sources + registry, in-process async mutex,
  feed auth (transient --userconfig, in-memory token, az login hint, §12 Q8),
  ~1h feed cache with offline/REST-error degrade (§12 Q3), keyword narrowing,
  ordered first-match resolve + explicit --source hard error (§12 Q4),
  execMode=separate for feed (§12 Q16), path omits module (§12 Q17).

Decisions (see DECISIONS_LOG.md): default-config-only catalog adapter;
ExecutionMode as a string union to avoid the node-providers cycle; corrupt user
catalog degrades to non-match while a corrupt bundled catalog fails loud.

Gate: 2 review + 2 test-gap rounds. Review fixed 2 majors (catalog corrupt
degrade vs bundled fail-loud; feed cache-file name sanitization); 3 minors
deferred with rationale (DEFERRED_REVIEW_LOG.md). Tests grown 39->66 specs;
full package suite (3806) green, full build green, prettier clean.

Behavior change for reviewers: none — the live install path is untouched.
Cut the default agent provider over from the static config `agents` map to an
agents.json-backed installed-record model resolved through the M1 install
sources (DESIGN.md §4.1, §5–§8).

- installedAgents.ts: agents.json store; seedRecordsFromBundledCatalog (sync,
  no network) and seedRecordsFromConfig; legacy externalAgentsConfig.json
  migration (path-only, builtin-collision guard, rename .migrated);
  createInstalledAppAgentProvider with per-module-root grouping behind a
  combineAppAgentProviders facade (one AppAgentProvider, Q20); loadInstalled
  records lifecycle (in-memory / named / first-run seed+write / steady-state).
- defaultAgentProviders.ts: getDefaultAppAgentProviders builds the single
  installed provider (signature unchanged); getDefaultAppAgentInstaller is
  registry-backed with a shared mutex and persists installSources/agents.json.
- config.ts: installSources block (InstanceConfig.installSources,
  getResolvedInstallSources, seed order + dev workspace catalog, installDir).
- agentProvider.ts: AppAgentInstaller now install(name, ref, sourceName?)/
  uninstall/sources(); installCommandHandlers.ts: --source/--where flags,
  AGENT_NAME_RE + isAppAgentName pre-materialize validation (§ Q7/Q18).
- agentServer/server.ts + api/webDispatcher.ts wire the agentInstaller.
- Tests: installed store/seed/migrate/load, provider build/combine + installer
  (hermetic path-only config), resolved sources, and command-handler
  validation/--where/uninstall branches.
Add the additive `@update <name>` and `@source` command groups on top of the
Milestone 2 installed-record provider (design §5, §6, §4.7, §12 Q13).

- `@update <name> [range]` re-resolves an agent against its recorded source per
  kind — feed bump (`module@range`), path re-materialize, catalog re-lookup —
  overwriting `agents.json` only after the new version materializes, so a failed
  update is a no-op (§4.7, Q13).
- `@source list|order|add {feed,catalog,path}|remove` manages the instance
  `installSources` block via `installer.sources()`. `order` treats its args as a
  priority prefix and appends the remaining configured sources (§5/§6); unknown
  names warn and are skipped. `add catalog` distinguishes an unreadable file from
  invalid JSON; `remove` warns and aborts when a source still backs installed
  records unless `--force`.
- Layering: `@update` / `@source remove` reach record data through three optional
  `AppAgentInstaller` methods (`update`, `sources`, `recordsUsingSource`) so the
  dispatcher core never reads `agents.json` (see DECISIONS_LOG). `install`/`update`
  preserve the re-resolution key in `record.ref` for renamed catalog/path agents.

Tests: installer update across kinds incl. changed-manifest re-materialize, no-op
on failure, ref-preservation, missing source/agent; `@source` list rendering,
order dedup/append/empty, add feed/catalog/path validation, remove referenced
guard. Full build green; defaultAgentProvider 3842 + dispatcher 1023 tests pass.
Make the feed-enumeration marker enforceable and retire the design draft status
(design §4.1, §12 Q12).

- New `agent-keyword` repo-policy rule (tools/scripts/policyChecks/agentKeyword.mjs):
  any package whose package.json exposes the `./agent/manifest` export — the exact
  subpath the npm agent provider loads — must carry `typeagent-agent` in its
  `keywords`, auto-fixed otherwise. Keys on the manifest export rather than the
  `@typeagent/agent-sdk` dependency to avoid over-matching infrastructure packages.
- Added the keyword to all 36 agent packages (packages/agents/*, examples). The
  rule is enforced by `check:policy`, already wired into CI (azure-build-ts.yml).
- Docs: documented `@install --source/--where`, `@source list|add|order`, and
  `@update` in the agent tutorial; flipped the design doc status to Implemented.

The pre-M2 machinery (getDefaultNpmAppAgentProvider, getExternalAppAgentProvider,
installNpm, isNpmSpecifier) is already removed; the externalAgentsConfig.json
migration shim is intentionally retained for one release (see DECISIONS_LOG).
Final branch-wide gate over the whole installsource diff (review r1/r2 +
test-gap r1/r2 against DESIGN.md §§1-12).

- catalogSource.find now fails fast when a matched catalog entry carries
  neither `path` nor `name`, instead of producing a handle-less record
  that would only fail at load time (§4.2, §12 Q17).
- test: registry sources + resolution order survive a reload from the
  persisted snapshot (§6 restart persistence, hermetic).
- test: catalog malformed-entry (neither path nor name) fails fast (Q17).
- test: remove drops the name from order(), not just list().
- docs: DECISIONS_LOG / DEFERRED_REVIEW_LOG / EXECUTION_PLAN final-gate
  outcome recorded.

Full build green; defaultAgentProvider suite 3844 passing; repo policy
7954 checks passing.
The dispatcher core no longer knows the install-source kind taxonomy or the
@source add grammar. InstallSource.kind is an opaque string; core exposes only
InstallSourceInfo { name, kind, detail } for @source list, and the core @source
table contributes list/order/remove.

The host (default-agent-provider) now contributes the @source add subcommands:
- AppAgentInstaller gains sourceCommands?(): CommandHandlerTable.
- The system agent merges that table into the @source add node dynamically via
  getSystemHandlers(systemContext), used by getCommands/executeCommand/
  getCommandCompletion and @help.
- Host authors typed feed/catalog/path handlers in
  getAddSourceCommandHandlers(registry), each a real CommandHandler with typed
  args + flags and per-kind validation calling registry.add(config).
- The kind taxonomy/config shapes moved to installSources/config.ts.

DefaultInstallSourceRegistry keeps the typed add(config) for these handlers,
seeding, and tests. Tests updated accordingly; DECISIONS_LOG records the final
approach (superseding the earlier addSource(spec)/implicitQuotes and rest-arg
explorations, both reverted).
Move the whole @source command (list/order/where/remove/add) to the host via
AppAgentInstaller.sourceCommands(); the dispatcher core keeps only the
live-session @install/@uninstall/@update handlers and merges the host table in
as @source. Replace @install --where with host @source where <ref>.

Also move the install-record types (ResolvedCandidate, InstalledAgentRecord) out
of the core into the host (installSources/config.ts): nothing in the core
consumed them. Delete core installSource.ts and drop the index.ts re-export;
repoint all host importers to the local module.

Update DESIGN.md to document the final layering. DECISIONS_LOG: fold the two
2026-06-27 entries into the design.
Compute the system command table (core commands plus the host's @source table)
once per dispatcher and store it as CommandHandlerContext.systemCommands, since
the installer is fixed for the dispatcher's lifetime. getSystemHandlers now takes
the installer directly; systemAgent and the @help handler read the cached table
instead of rebuilding it on every getCommands/getCommandCompletion/executeCommand
call.

Extract the executeCommand/getCommandCompletion bodies out of getCommandInterface
into standalone executeCommandFromHandlers/getCommandCompletionFromHandlers so
callers that already hold a table (systemAgent) can dispatch without allocating a
wrapper interface; getCommandInterface now delegates to them.

Tighten AppAgentCommandInterface.getCommandCompletion to take non-optional params:
completion only runs after the dispatcher has parsed params, and the RPC contract
and all callers already treat it as concrete. This removes a non-null assertion
and an unreachable undefined guard.
…fault

- Single ordered sources list IS the resolution order (first match wins);
  remove the redundant order() API and orderedSources() helper, merge the
  config/source maps into one entries map.
- @source list/order/where reworked around the single list.
- path source no longer defaults to process.cwd(): a relative ref without an
  explicit baseDir is a non-match (not a throw) so the ordered walk continues.
- Add excludePathSources option (seed + persisted) so remote hosts drop path
  sources; web API server enables it.
- Update DESIGN.md and config comments to match.
- Remove installDir from InstallSourcesConfig; it is now always
  <instanceDir>/installedAgents, derived at runtime and never persisted
- Drop the process.cwd() fallback; in-memory case returns undefined
- Split getResolvedInstallSources (sources) and getInstallDir (path)
  into separate functions since they share no inputs
- Installer fails fast on an unresolved installDir before resolving sources
- Update tests accordingly
Previously excludePathSources filtered path sources out of the list
returned by getResolvedInstallSources, which both seeds the registry and
gets persisted back to config.json on any @source edit. That would
permanently drop path from the persisted/seeded list on the first
mutation.

Move the filter into the registry's resolution walk (and where()), and
reject explicit --source for a path source on excluded hosts. The seeded
and persisted source lists now always retain path; only runtime
resolution skips it.
The workspace catalog source is seeded conditionally on the ambient
filesystem and recomputed each launch until persisted, so a shared
instance dir yields a different source list across dev/shipped contexts,
and the first @source edit freezes a checkout-specific catalog path that
goes stale elsewhere. Note this caveat in design 6 and above
getSeedInstallSources; docs only, no behavior change.
Replace the internal Azure DevOps org/project (msctoproj/AI_Systems) with
generic placeholders (myorg/myproject) in the install-source design doc
example and the feed/registry test fixtures. The product feed name and
@TypeAgent scope are retained; tests still pass.
curtisman and others added 5 commits July 7, 2026 19:20
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@curtisman curtisman enabled auto-merge July 8, 2026 00:46
@curtisman curtisman disabled auto-merge July 8, 2026 01:34
@curtisman curtisman enabled auto-merge July 8, 2026 01:42
@curtisman curtisman disabled auto-merge July 8, 2026 01:49
@curtisman curtisman enabled auto-merge July 8, 2026 01:54
@curtisman curtisman added this pull request to the merge queue Jul 8, 2026
Merged via the queue into microsoft:main with commit d9169b8 Jul 8, 2026
14 of 15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant