Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 1 addition & 8 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
<!-- Generated by APM CLI from .apm/ primitives -->
<!-- Build ID: 9a566a78e962 -->
<!-- APM Version: 0.14.1 -->
<!-- Build ID: 9fe84b8620b0 -->

<!-- Source: .apm/instructions/linting.instructions.md -->
# Linting (canonical contract)

The CI `Lint` job is a hard gate. Mirror it locally before `git push`
Expand Down Expand Up @@ -68,8 +66,3 @@ produce artifacts asserting green CI -- notably `pr-description-skill`
(whose "Validation evidence" row covers CI checks) -- inherit this
gate transitively. Do NOT redefine ruff or pylint commands inside
individual skills; honor this instruction before invoking them.
<!-- End source: .apm/instructions/linting.instructions.md -->

---
*This file was generated by APM CLI. Do not edit manually.*
*To regenerate: `apm compile`*
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
faithfully reflects on-disk content across Copilot, Claude, and Gemini
targets (including OS-specific `windows`/`linux`/`osx` hook keys). (by
@harshitlarl, closes #316, #1700)
- Experimental, Copilot-only `canvas` primitive: `.apm/extensions/<name>/extension.mjs` deploys to `.github/extensions/<name>/` on `apm install`/`apm pack`, gated by `apm experimental enable canvas` + `--trust-canvas-extensions` for dependencies. (by @sergio-sisternes-epam, #1689)
- Experimental canvas: `apm install --global --trust-canvas-extensions` deploys dependency canvases to `~/.copilot/extensions/<name>/`; `apm uninstall --global` prunes them. (by @sergio-sisternes-epam, #1689)
- Experimental canvas: package validation recognises canvas extensions (canvas-only packages valid, gated-executable warning); `apm audit` (text) lists deployed canvas bundles. (by @sergio-sisternes-epam, #1689)

### Changed

Expand Down
1 change: 1 addition & 0 deletions docs/astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ export default defineConfig({
{ label: 'GitHub Agentic Workflows', slug: 'integrations/gh-aw' },
{ label: 'Microsoft 365 Copilot Cowork (Experimental)', slug: 'integrations/copilot-cowork' },
{ label: 'GitHub Copilot App workflows (Experimental)', slug: 'integrations/copilot-app' },
{ label: 'Canvas extensions (Experimental)', slug: 'integrations/canvas' },
{ label: 'Hermes Agent (Experimental)', slug: 'integrations/hermes' },
{ label: 'AI runtime compatibility', slug: 'integrations/runtime-compatibility' },
{ label: 'GitHub rulesets', slug: 'integrations/github-rulesets' },
Expand Down
10 changes: 10 additions & 0 deletions docs/src/content/docs/concepts/primitives-and-targets.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,14 @@ Model Context Protocol servers declared as dependencies. APM writes the per-harn
- Source: `apm.yml` -> `dependencies.mcp:`
- Deep dive: [MCP servers](/apm/consumer/install-mcp-servers/)

### Canvas extensions (experimental)

GitHub Copilot CLI canvas extensions: a directory bundle whose entry file is `extension.mjs` (executable Node.js). Copilot-only. Behind the `canvas` experimental flag; dependency-provided canvases are blocked unless `--trust-canvas-extensions` is passed, because they are arbitrary executable code. Project scope deploys to `.github/extensions/`; `--global` deploys a dependency canvas to `~/.copilot/extensions/` (always requiring the trust flag).

- Source: `.apm/extensions/<name>/extension.mjs`
- Deploys to: `.github/extensions/<name>/` (project) or `~/.copilot/extensions/<name>/` (`--global`)
- Deep dive: [Canvas extensions](/apm/integrations/canvas/)

## Target catalogue

Each target is identified by a slug used in `apm.yml`'s `targets:` field and on the `--target` flag. The output directory is where APM writes deployed primitives. The "agent-skills" and "copilot-cowork" targets exist in the registry but are not end-user runtimes; they are covered separately in the experimental reference.
Expand Down Expand Up @@ -113,6 +121,7 @@ Rows are primitives, columns are harnesses. Cell legend:
| commands | unsupported | native | compiled | unsupported | compiled | compiled | compiled | unsupported |
| plugins | compiled | compiled | compiled | compiled | compiled | compiled | compiled | compiled |
| MCP servers | native | native | native | native | native | native | native | native |
| canvas (experimental) | gated | unsupported | unsupported | unsupported | unsupported | unsupported | unsupported | unsupported |

How to read a cell:

Expand All @@ -122,6 +131,7 @@ How to read a cell:
- `commands / copilot = unsupported` -- Copilot has no commands primitive; the same source `.prompt.md` reaches Copilot as a native prompt instead.
- `plugins / *` -- APM unpacks the plugin at install time into the primitives in the rows above; routing then follows those rows.
- `MCP servers / *` -- APM writes the harness's standard MCP config. Transitive MCP servers brought in by deep dependencies must be explicitly declared or trusted with `--trust-transitive-mcp` -- effectively `gated` for those, `native` for direct dependencies.
- `canvas / copilot = gated` -- requires the `canvas` experimental flag; a canvas shipped by a dependency is executable code, so it stays blocked until you pass `--trust-canvas-extensions`. First-party canvases in your own package deploy at project scope once the flag is on. With `--global`, a dependency canvas deploys to `~/.copilot/extensions/` and always requires the trust flag (first-party global install is not supported). Every other harness is `unsupported`: a canvas is a Copilot CLI construct only.

## Where compiled context files land

Expand Down
2 changes: 1 addition & 1 deletion docs/src/content/docs/enterprise/security.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ APM has no runtime footprint. Once `apm install` or `apm compile` completes, the

- **No runtime component.** APM generates files then terminates. It does not run alongside your application.
- **No network calls after install.** All network activity (git clone/fetch) occurs during dependency resolution. There are no callbacks, webhooks, or phone-home requests.
- **No arbitrary code execution.** APM does not execute scripts from packages, evaluate expressions in templates, or run downloaded code.
- **No arbitrary code execution.** APM does not execute scripts from packages, evaluate expressions in templates, or run downloaded code. (**Canvas exception:** the experimental `canvas` primitive deploys executable `extension.mjs` (Node.js) code to `.github/extensions/` or `~/.copilot/extensions/`; this surface is gated by both the `canvas` experimental flag and `--trust-canvas-extensions` for dependency-provided canvases. See [Canvas extensions](/apm/integrations/canvas/).)
- **No access to application data.** APM never reads databases, API responses, application state, or user data.
- **No persistent background processes.** APM does not install daemons, services, or scheduled tasks.
- **No telemetry or data collection.** APM collects no usage data, analytics, or diagnostics. Nothing is transmitted to Microsoft or any third party.
Expand Down
144 changes: 144 additions & 0 deletions docs/src/content/docs/integrations/canvas.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
---
title: "Canvas extensions (Experimental)"
description: "Ship GitHub Copilot CLI canvas extensions through APM packages (experimental, Copilot-only)."
sidebar:
order: 8
badge:
text: Experimental
variant: caution
---

:::caution[Experimental]
This feature is behind the `canvas` experimental flag and is off by default.
It is **Copilot-only**, and the CLI surface may change. Enable it explicitly
before use.
:::

A **canvas** is a GitHub Copilot CLI extension: a directory bundle whose entry
file is `extension.mjs` (executable Node.js), plus any sibling assets it needs.
Copilot CLI discovers canvases in immediate subdirectories of
`.github/extensions/<name>/` (project scope) and
`~/.copilot/extensions/<name>/` (user scope). APM lets a package carry a canvas
under `.apm/extensions/<name>/` and deploys it to the matching location at
install time so the canvas is available in your Copilot session.

Canvases are typically produced by the Copilot CLI `create-canvas` skill
(scaffolds a working extension in `.github/extensions/`). This page covers
how to ship one through an APM package.

## Enable the feature

```bash
apm experimental enable canvas
```

Default APM behaviour never changes until the flag is enabled. With the flag
off, `.apm/extensions/` is ignored entirely.

## Author a canvas

Place the bundle under your package's `.apm/` directory. The marker file is
`extension.mjs`; a directory without it is ignored.

```
.apm/
extensions/
my-canvas/
extension.mjs # required entry point (executable Node.js)
ui.js # optional sibling assets
styles.css
```

The `<name>` segment becomes both the deploy directory and the extension id, so
it is validated strictly: `[A-Za-z0-9._-]+`, no leading or trailing dot, no
`..`, no path separators, and reserved device names are rejected.

## Install

```bash
apm install --target copilot
```

APM deploys the bundle verbatim to `.github/extensions/my-canvas/`. The deploy
is **atomic**: every file in the bundle is planned and validated first, and any
unmanaged local collision skips the whole bundle (use `apm install --force` to
overwrite) so you never end up with a half-updated executable extension.

After a canvas deploys, start a new Copilot CLI session (exit and relaunch) --
Copilot CLI discovers extensions at session start, so a freshly-deployed canvas
is not picked up mid-session.

## Trust gate for dependency canvases

A canvas shipped by a **dependency** is arbitrary executable Node.js code. APM
blocks dependency-provided canvases by default. To deploy them, opt in
explicitly:

```bash
apm install --target copilot --trust-canvas-extensions
```

The trust gate is independent of the experimental flag:

- The **experimental flag** decides whether the canvas primitive is processed at
all. It is a feature-availability gate, not a security gate.
- The **`--trust-canvas-extensions` flag** decides whether *dependency*
canvases may deploy. Your own first-party canvas (in the root package you are
installing from) deploys freely once the flag is on; only dependency-provided
canvases need the trust flag.

When a dependency canvas is blocked, APM prints a diagnostic naming the package,
the canvas, the `extension.mjs` entry point, the deploy directory, and the
opt-in flag. The same gate is enforced on offline bundle install
(`apm install <bundle>`) and on `apm unpack`, so a vendored bundle cannot
smuggle an executable canvas past trust.

## Install globally (user scope)

To make a canvas available in **every** Copilot session, install it globally so
it lands in `~/.copilot/extensions/<name>/`:

```bash
apm install <package> --global --trust-canvas-extensions
```

Global canvas install is intentionally limited in this experimental release:

- **Dependency-provided only.** Only a canvas shipped by a package you install
(the `--global` flow always treats the canvas as dependency-provided) deploys
globally, so APM records it in the user lockfile and `apm uninstall --global`
can prune it. A first-party root `.apm/extensions/` canvas is **not** deployed
at user scope -- package it and install it as a dependency instead.
- **Trust is always required.** A global canvas has full-account blast radius,
so `--trust-canvas-extensions` is mandatory even though the project-scope
first-party path does not need it.
- **Default `~/.copilot` only.** If `$COPILOT_HOME` is set to a non-default
location, APM refuses the global canvas install rather than deploy to a path
Copilot will not scan.

`apm uninstall --global <package>` removes the deployed
`~/.copilot/extensions/<name>/` files and prunes the empty directories.

## Pack and uninstall

`apm pack` preserves `.apm/extensions/` in the bundle, so a packed package keeps
its canvas. `apm uninstall` removes the deployed `.github/extensions/<name>/`
files and prunes the now-empty directories; uninstall is never gated by the
experimental flag, so a previously-installed canvas can always be removed.

## Scope and limitations

- **Copilot-only.** A canvas is a Copilot CLI construct. Other targets
(`--target claude`, `cursor`, etc.) never receive it.
- **Global install is dependency-only.** User-scope (`--global`) deployment to
`~/.copilot/extensions/` supports dependency-provided canvases (always
requiring `--trust-canvas-extensions`) and the default `~/.copilot` location
only; first-party root canvases deploy at project scope only.
- **No compile/list surfacing yet.** Canvases are not yet shown by
`apm list`/`apm compile`; they are deployed at install only.
- **No policy-file control yet.** Canvas trust is controlled only by the
`--trust-canvas-extensions` CLI flag; governing it via `apm-policy.yml` is
planned but not part of this experimental release.

See the [primitives and targets](/apm/concepts/primitives-and-targets/) matrix
for where the canvas primitive sits.
1 change: 1 addition & 0 deletions docs/src/content/docs/reference/cli/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ With no arguments it installs everything from `apm.yml`. With one or more `PACKA
| `--audit <off\|warn\|block>` | (config/policy) | Run a content audit over the files this install deploys. `warn` records findings in the summary; `block` halts the install on critical findings. Overrides your `audit-on-install` config but cannot relax an org policy floor. Requires the `external-scanners` experimental flag. |
| `--no-audit` | off | Disable the install-time audit for this invocation (equivalent to `--audit off`). Cannot relax an org policy `block` floor. |
| `--trust-transitive-mcp` | off | Trust self-defined MCP servers shipped by transitive packages without re-declaring them in your `apm.yml`. |
| `--trust-canvas-extensions` | off | Trust executable canvas extensions (`extension.mjs`) shipped by dependencies. Required for dependency-provided canvases; first-party canvases deploy without it. Requires the `canvas` experimental flag. |
| `--allow-insecure` | off | Permit direct `http://` (non-TLS) dependencies. |
| `--allow-insecure-host HOSTNAME` | unset | Permit transitive `http://` dependencies from `HOSTNAME`. Repeatable. |

Expand Down
2 changes: 1 addition & 1 deletion docs/src/content/docs/reference/cli/pack.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ apm pack --archive --dry-run -v
A Claude Code plugin directory under `--output`. Contains:

- `plugin.json` -- schema-conformant manifest. Convention-dir keys are stripped because Claude Code auto-discovers them.
- Plugin-native subdirs populated from your `.apm/` content and from installed dependencies: `agents/`, `skills/`, `commands/`, `instructions/`, `hooks/`.
- Plugin-native subdirs populated from your `.apm/` content and from installed dependencies: `agents/`, `skills/`, `commands/`, `instructions/`, `hooks/`, `extensions/` (canvas extensions, when the `canvas` experimental flag is enabled).
- A merged `hooks.json` when multiple sources contribute hooks.
- `apm.lock.yaml` -- enriched copy with `pack:` metadata and a `bundle_files` map of per-file SHA-256 digests, used by `apm install` for install-time integrity verification.
- `devDependencies` are excluded.
Expand Down
1 change: 1 addition & 0 deletions docs/src/content/docs/reference/cli/unpack.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ Extraction is **additive-only**: only files listed in the bundle's lockfile are
| `--skip-verify` | off | Skip the bundle completeness check against the bundle's `apm.lock.yaml`. Useful for partial bundles. |
| `--dry-run` | off | List files that would be unpacked without writing anything. |
| `--force` | off | Deploy despite critical hidden-character findings from the security scan. Use only after independent verification. |
| `--trust-canvas-extensions` | off | Trust executable canvas extensions (`extension.mjs`) in the bundle. Without this, canvas files are stripped during extraction. Requires the `canvas` experimental flag. |
| `--verbose`, `-v` | off | Show per-file paths and full diagnostic context. |

## Examples
Expand Down
1 change: 1 addition & 0 deletions docs/src/content/docs/reference/experimental.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ apm experimental reset verbose-version
| `marketplace-authoring`| Enable marketplace authoring commands (init, build, publish, etc.). |
| `registries` | Enable REST-based APM package registries in `apm.yml`. |
| `external-scanners` | Ingest third-party SARIF scanners into `apm audit` (`--external`, including SkillSpector LLM mode and allowlisted `--external-args`), the `external.<name>.{llm,args}` config keys, and the `security.audit.scanners` policy block. See [External scanners](../integrations/external-scanners/). |
| `canvas` | Ship Copilot CLI canvas extensions (`.apm/extensions/<name>/extension.mjs`) through APM packages. Dependency-provided canvases additionally require `--trust-canvas-extensions`. See [Canvas extensions](../integrations/canvas/). |

New flags are proposed via [CONTRIBUTING.md](https://github.com/microsoft/apm/blob/main/CONTRIBUTING.md#how-to-add-an-experimental-feature-flag) and graduate to default when stable. See the contributor recipe for the full lifecycle.
See also: [Cowork integration](../integrations/copilot-cowork/).
Expand Down
Loading
Loading