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
485 changes: 485 additions & 0 deletions Assistant.md

Large diffs are not rendered by default.

46 changes: 46 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,52 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed

- Release automation is managed by Release Please (`.github/workflows/release-please.yml`), including automated versioning and changelog updates on release PRs.
- Added first-class network capability helpers:
- `createNetworkScopeCapability(scopeId)`
- `requireNetworkScopeCapability(scopeId, action)`
- `createNetworkCapabilityBundle({ transports, scopeId })` object signature (array signature remains supported for backward compatibility)
- Added first-class render-on-load authoring helpers:
- `defineRenderOnLoad(source, options?)`
- `defineRenderOnLoadActions({ handlers, bindings, ... })`
- `createRenderOnLoadActionsSource({ handlers, bindings, ... })`
- `resolveRenderOnLoadSource(output)`
- `getRenderOnLoadMonacoTypeDefinitions()`
- `getRenderOnLoadMonacoHints()`
- `renderOnLoad()` now supports synchronous string, function, or `defineRenderOnLoad(...)` module payloads, normalized by SDK transport serialization.
- Updated plugin-init capability validation to accept `system.network.scope.<scope-id>` IDs.
- Added `extractPrivilegedActionRequest(...)` to safely unwrap privileged requests from direct request objects, backend envelopes, or `UI_MESSAGE` success payloads shaped like `{ ok, result: { correlationId, request } }`.
- Added `requestPrivilegedActionFromEnvelope(...)` as the canonical privileged transport pipeline helper (`envelope -> extract -> request -> formatted error`), with optional `throwOnError` mode.
- Updated low-level privileged examples and operator fixtures to extract the validated request object before calling the raw `requestPrivilegedAction` host bridge.
- Deprecated passing full backend envelope objects directly into the raw `requestPrivilegedAction` bridge. Migration snippet:

```ts
const envelopeOrRequest = await window.createBackendReq("UI_MESSAGE", {
handler: "plugin.buildPrivilegedRequest",
content: {},
});

const requestPayload = extractPrivilegedActionRequest(envelopeOrRequest);
const response = await window.createBackendReq("requestPrivilegedAction", requestPayload);
```
- Added `runCapabilityPreflight(...)` for deterministic declared-vs-granted capability diagnostics and remediation generation, and integrated declared-capability preflight into `PluginRegistry` diagnostics/init logging.
- Added `createPluginDoctorReport(...)` for host-side structured diagnostics triage (severity findings, remediation strings, and normalized status).
- Added docs snippet CI verification support in `verify:docs`:
- fenced snippets tagged with `verify` are compile-checked
- fenced snippets tagged with `verify runtime` also run a sandbox runtime smoke check
- Added SDK/FDO handshake contract helpers:
- `getSdkHandshake()` with `sdkVersion`, `apiVersion`, `capabilitySchemaVersion`, and `featureFlags`
- `getSdkFeatureFlags()` for feature-flag-only checks
- Included handshake payload in diagnostics (`PluginRegistry.getDiagnostics()`), so hosts can gate compatibility without ad hoc version probing.
- Added code-based remediation template helpers for host “copy exact fix” UX:
- `getDiagnosticFixTemplate(code)`
- `listDiagnosticFixTemplates()`
- `formatDiagnosticExactFix(code)`
- Added migration codemod command surfaces for legacy plugin updates:
- `fdo-sdk migrate ...` (bin command)
- `npm run migrate -- ...` (repo script)
- Added programmatic codemod helper `applySdkMigrationCodemod(source)` and migration rules for:
- legacy privileged envelope extraction fallback chain
- deprecated `PluginRegistry.configureCapabilityPolicy(...)`

## [1.0.19] - 2025-11-11

Expand Down
179 changes: 174 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ The supported lifecycle contract is synchronous:

- `init()` performs setup
- `render()` returns a UI string
- `renderOnLoad()` optionally returns an on-load string
- `renderOnLoad()` optionally returns:
- an on-load string source
- an on-load function (`() => void`) serialized by SDK
- a `defineRenderOnLoad(...)` module payload
- the SDK serializes those values for host transport separately

DOM helper rule:
Expand All @@ -42,6 +45,8 @@ Further documentation:
- safe authoring guide: [docs/SAFE_PLUGIN_AUTHORING.md](./docs/SAFE_PLUGIN_AUTHORING.md)
- operator plugin guidance: [docs/OPERATOR_PLUGIN_PATTERNS.md](./docs/OPERATOR_PLUGIN_PATTERNS.md)
- examples and fixtures guide: [docs/EXAMPLES_AND_FIXTURES.md](./docs/EXAMPLES_AND_FIXTURES.md)
- SharePoint/generic connector host contract: [docs/SHAREPOINT_PROVIDER_HOST_CONTRACT.md](./docs/SHAREPOINT_PROVIDER_HOST_CONTRACT.md)
- plugin gap register: [docs/PLUGIN_DEVELOPMENT_GAP_REGISTER.md](docs-local/PLUGIN_DEVELOPMENT_GAP_REGISTER.md)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Remove docs-local link from public README

This link points to docs-local/..., but scripts/verify-docs.mjs now explicitly fails on any docs-local/ reference in README.md, docs, or examples; as a result, npm run verify:docs (and prepublishOnly) will fail once dependencies are present, blocking release validation. Replace it with a public docs path or remove the link.

Useful? React with 👍 / 👎.

- API stability policy: [docs/API_STABILITY.md](./docs/API_STABILITY.md)

## Features
Expand Down Expand Up @@ -165,7 +170,16 @@ The SDK uses explicit host-granted capabilities from `PLUGIN_INIT.content.capabi

Core capabilities:

- `storage.json` - required for `PluginRegistry.useStore("json")`
- `storage` - base capability family for plugin-managed persistent storage backends
- `storage.json` - JSON backend leaf capability, required with `storage` for `PluginRegistry.useStore("json")`
- `system.network` - base capability family for runtime network access
- `system.network.https` - required for outbound HTTPS requests (`fetch("https://...")`)
- `system.network.http` - required for outbound plaintext HTTP requests (`fetch("http://...")`)
- `system.network.websocket` - required for outbound `WebSocket` connections
- `system.network.tcp` - required for raw TCP socket modules
- `system.network.udp` - required for raw UDP socket modules
- `system.network.dns` - required for DNS modules/APIs
- `system.network.scope.<scope-id>` - host-defined destination scope required together with network base + transport capabilities
- `sudo.prompt` - required for `runWithSudo(...)`
- `system.clipboard.read` - required for host-mediated clipboard reads
- `system.clipboard.write` - required for host-mediated clipboard writes
Expand Down Expand Up @@ -193,18 +207,49 @@ Public helpers exported from root package:
- `createProcessExecActionRequest(...)`
- `createProcessScopeCapability(...)`
- `createPrivilegedActionBackendRequest(...)`
- `extractPrivilegedActionRequest(...)`
- `requestPrivilegedAction(...)`
- `getSdkHandshake(...)`
- `getSdkFeatureFlags(...)`
- `evaluateSdkHandshakeCompatibility(...)`
- `isSdkHandshakeCompatible(...)`
- `createPluginDoctorReport(...)`
- `createPluginDoctorPanelModel(...)`
- `getFixtureRuntimeMatrix(...)`
- `listFixtureRuntimeMatrixCases(...)`
- `getFixtureRuntimeMatrixCase(...)`
- `getDiagnosticFixTemplate(...)`
- `listDiagnosticFixTemplates(...)`
- `formatDiagnosticExactFix(...)`
- `applySdkMigrationCodemod(...)`
- `defineRenderOnLoad(...)`
- `defineRenderOnLoadActions(...)`
- `listRenderOnLoadTemplates(...)`
- `getRenderOnLoadTemplate(...)`
- `createRenderOnLoadActionsSource(...)`
- `resolveRenderOnLoadSource(...)`
- `getRenderOnLoadMonacoTypeDefinitions(...)`
- `getRenderOnLoadMonacoHints(...)`
- `formatPrivilegedActionError(...)`
- `getInlinePrivilegedActionErrorFormatterSource(...)`
- `createClipboardReadRequest(...)`
- `createClipboardWriteRequest(...)`
- `requestClipboardRead(...)`
- `requestClipboardWrite(...)`
- `createStorageCapabilityPreset(...)`
- `getStorageCapabilityPreset(...)`
- `listStorageCapabilityPresets(...)`
- `createScopedProcessExecActionRequest(...)`
- `requestScopedProcessExec(...)`
- `createProcessCapabilityBundle(...)`
- `createNetworkCapabilityBundle(...)`
- `createNetworkScopeCapability(...)`
- `createStorageCapabilityBundle(...)`
- `createWorkflowCapabilityBundle(...)`
- `createFilesystemCapabilityBundle(...)`
- `describeCapability(...)`
- `parseMissingCapabilityError(...)`
- `requireNetworkScopeCapability(...)`
- `getOperatorToolPreset(...)`
- `listOperatorToolPresets(...)`
- `createOperatorToolCapabilityPreset(...)`
Expand Down Expand Up @@ -331,19 +376,91 @@ Plugin identity is still attached in structured log metadata (`pluginId`, `compo
- The SDK exposes a reserved diagnostics handler: `PluginRegistry.DIAGNOSTICS_HANDLER` (`"__sdk.getDiagnostics"`).
- Hosts can query runtime health/capabilities/notifications via a `UI_MESSAGE` request without adding custom plugin handlers.
- Diagnostics include capability grant and usage/denial counters for permission auditing.
- Diagnostics also include a handshake contract (`sdkVersion`, `apiVersion`, `capabilitySchemaVersion`, `featureFlags`) for host compatibility gating.
- For deterministic compatibility gating, use `evaluateSdkHandshakeCompatibility(diagnostics.handshake, expectations)` before enabling host features that depend on specific SDK capability schema or feature flags.
- For host-side triage UX, use `createPluginDoctorReport(diagnostics)` to get structured findings with severity, remediation, and status (`healthy`, `needs-attention`, `degraded`).
- For host panel rendering, use `createPluginDoctorPanelModel(report)` to get prioritized findings, section grouping, and exact-fix text (`formatDiagnosticExactFix` integrated).
- For deterministic “copy exact fix” UX by error code, use `getDiagnosticFixTemplate(code)` or `formatDiagnosticExactFix(code)`.

Example host request payload:

```typescript
{
```typescript verify
const requestEnvelope = {
message: "UI_MESSAGE",
content: {
handler: "__sdk.getDiagnostics",
content: { notificationsLimit: 20 }
content: { notificationsLimit: 20 },
},
};
```

Plugin Doctor example:

```typescript verify
import { createPluginDoctorPanelModel, createPluginDoctorReport } from "@anikitenko/fdo-sdk";

async function loadDoctorReport() {
const diagnostics = await window.createBackendReq("UI_MESSAGE", {
handler: "__sdk.getDiagnostics",
content: { notificationsLimit: 20 },
});

const capabilitySchemaVersion = (diagnostics as any)?.handshake?.capabilitySchemaVersion;
if (capabilitySchemaVersion !== "1.0.0") {
console.warn("Host/SDK capability schema mismatch", { capabilitySchemaVersion });
}

const report = createPluginDoctorReport(diagnostics as any, {
includeInfo: true,
includeNotificationFindings: true,
handshake: {
expectedContractVersion: "1.0.0",
expectedApiVersion: "1.0.0",
expectedCapabilitySchemaVersion: "1.0.0",
requiredFeatureFlags: ["diagnosticsHandler", "pluginDoctorReport"],
},
});
return createPluginDoctorPanelModel(report, {
maxPrioritizedFindings: 8,
});
}
```

## Fixture Runtime Matrix (Host CI)

- The SDK exposes a versioned fixture smoke contract for host CI wiring:
- `getFixtureRuntimeMatrix()`
- `listFixtureRuntimeMatrixCases()`
- `getFixtureRuntimeMatrixCase(id)`
- Each entry defines the canonical fixture path plus expected smoke probes (`init`, `render`, `renderOnLoad`, `uiMessage` handlers).
- Use this contract in FDO CI to avoid hardcoded fixture handler lists drifting from SDK fixtures.

Example:

```typescript verify
import { getFixtureRuntimeMatrix } from "@anikitenko/fdo-sdk";

const matrix = getFixtureRuntimeMatrix();
for (const fixtureCase of matrix.cases) {
console.log(fixtureCase.id, fixtureCase.fixturePath, fixtureCase.probes.uiMessage.length);
}
```

## RenderOnLoad Templates

- Use `listRenderOnLoadTemplates()` for host/editor template pickers.
- Use `getRenderOnLoadTemplate(id)` to load one deterministic template by id.
- Template entries include `context` (`runtime-source` or `plugin-method`) and `language` to drive editor UX.

```typescript verify
import { getRenderOnLoadTemplate, listRenderOnLoadTemplates } from "@anikitenko/fdo-sdk";

const templates = listRenderOnLoadTemplates({ context: "plugin-method" });
const defaultTemplate = getRenderOnLoadTemplate("method-define-render-on-load-actions");

console.log(templates.length, defaultTemplate?.language);
```

## Development

### Building
Expand All @@ -369,6 +486,58 @@ npm run test:coverage # Run tests with coverage report
npm run coverage:open # Open coverage report in browser
```

Documentation snippet verification:

- `npm run verify:docs` validates links and banned references
- fenced code blocks tagged with `verify` are compile-checked
- fenced code blocks tagged with `verify runtime` also run a minimal sandbox runtime smoke check

### Migration Codemod

Use the SDK migration command to update legacy plugin patterns:

```bash
npm run migrate -- --target ./examples
npm run migrate -- --target ./examples --write
```

Published-package command:

```bash
fdo-sdk migrate --target ./my-plugin --write
```

FDO-wrapper command (when the host exposes SDK tooling through `fdo`):

```bash
fdo sdk migrate --target ./my-plugin --write
```

Current migration rules include:

- legacy privileged envelope fallback chain to `extractPrivilegedActionRequest(...)`
- deprecated `PluginRegistry.configureCapabilityPolicy(...)` to `configureCapabilities(...)`

### Migration + Versioning Loop (Required)

Keep migration and versioning in one loop for every behavior-affecting change:

1. Run migration dry-run in the target plugin set:
- `npm run migrate -- --target ./examples`
- or `fdo sdk migrate --target ./examples`
2. Apply migrations:
- `npm run migrate -- --target ./examples --write`
- or `fdo sdk migrate --target ./examples --write`
3. Validate:
- `npm test`
- `npm run test:examples`
- `npm run verify:docs`
4. Version correctly:
- patch: internal fix, no public behavior change
- minor: additive API/behavior or deprecation with migration note
- major: breaking contract/removal
5. Publish and confirm FDO consumes the expected SDK version before final validation.

### Publishing

The package includes strict verification gates before publishing:
Expand Down
37 changes: 37 additions & 0 deletions bin/fdo-sdk.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#!/usr/bin/env node
const path = require("node:path");
const process = require("node:process");
const { pathToFileURL } = require("node:url");

async function main() {
const [, , command, ...args] = process.argv;
if (command === "--help" || command === "-h" || !command) {
process.stdout.write([
"fdo-sdk",
"",
"Usage:",
" fdo-sdk migrate [--target <path>] [--write]",
"",
"Commands:",
" migrate Run SDK migration codemods for plugin/source files.",
].join("\n") + "\n");
return;
}

if (command !== "migrate") {
throw new Error(`Unknown command "${command}". Run "fdo-sdk --help" for usage.`);
}

const scriptPath = path.resolve(__dirname, "../scripts/fdo-sdk-migrate.mjs");
const scriptModule = await import(pathToFileURL(scriptPath).href);
if (typeof scriptModule.run !== "function") {
throw new Error("Migration script does not export run(...).");
}
await scriptModule.run(args);
}

main().catch((error) => {
const message = error instanceof Error ? error.message : String(error);
process.stderr.write(`fdo-sdk command failed: ${message}\n`);
process.exit(1);
});
32 changes: 32 additions & 0 deletions docs/API_STABILITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,23 @@ This document defines which SDK surfaces are stable contracts versus internal im
- `renderOnLoad()`
- `serializeRender()`
- `serializeRenderOnLoad()`
- Render-on-load authoring helpers:
- `defineRenderOnLoad(...)`
- `resolveRenderOnLoadSource(...)`
- `listRenderOnLoadTemplates(...)`
- `getRenderOnLoadTemplate(...)`
- `getRenderOnLoadMonacoTypeDefinitions(...)`
- `getRenderOnLoadMonacoHints(...)`
- Fixture runtime matrix helpers:
- `getFixtureRuntimeMatrix(...)`
- `listFixtureRuntimeMatrixCases(...)`
- `getFixtureRuntimeMatrixCase(...)`
- Handshake compatibility helpers:
- `evaluateSdkHandshakeCompatibility(...)`
- `isSdkHandshakeCompatible(...)`
- Plugin doctor helpers:
- `createPluginDoctorReport(...)`
- `createPluginDoctorPanelModel(...)`
- Optional declared capability surface:
- `declareCapabilities()`
- Public plugin contracts:
Expand Down Expand Up @@ -76,6 +93,21 @@ This document defines which SDK surfaces are stable contracts versus internal im
- Internal changes:
- unrestricted unless they alter documented stable behavior

## Migration And Versioning Loop

Treat migration and semver as one required release loop:

1. Run migration dry-run on real plugin targets:
- `fdo sdk migrate --target ./plugins`
- or `fdo-sdk migrate --target ./plugins`
2. Apply codemods (`--write`) and re-run tests/validation.
3. Choose semver bump by compatibility impact:
- patch for non-contract fixes
- minor for additive changes/deprecations with migration notes
- major for breaking contract changes/removals
4. Ensure changelog/release notes include explicit migration guidance.
5. Validate the host (FDO) is running the intended SDK version before sign-off.

## Guidance For AI Tooling

Treat these files as source-of-truth contracts first:
Expand Down
Loading