SDK 1.1 adds Node-only release tools: deterministic offline bundles, detached provenance, reviewed updates and upstream comparison. Existing 1.0 stable APIs remain compatible. The complete export/type list is generated in API_SUPPORT.json.
SDK 0.13 adds /testing scenario/clock/fault/trace helpers, /diagnostics local
sessions and adapters, and /devtools feature templates plus planPlugin() and
generatePluginContracts(). createPluginHost({runtime}), registry runtimes,
settings/secret runtime ports, host.inspect() and operator replaceGrants()
are optional additions. See testing, diagnostics
and development tools for exact limits and ownership.
SDK 0.8 adds optional job storage, history and retry and stored result files. Existing v1 response shapes remain valid.
Workspace client helpers include connection-bound project edit sessions,
project.watchFiles(paths, options), client.watchJob(jobId, options) and
client.waitForJob(jobId, options). See observation for event
shapes, TypeScript types, cancellation and budgets. These are client helpers
over the existing workspace protocol; they grant no additional capability.
For project scaffolding, inert diagnostics and supervised local execution, see
development tools. For opt-in startCommandJob, getJob,
getJobEvents, cancelJob, readJobArtifact, job reporters and budgets, see
command jobs. Existing handler options gain an optional job reporter;
ordinary executeCommand behavior and short request deadlines are unchanged.
Import portable values and types from @altifigence/dds-plugin-sdk. The core
validates plain data, manages plugin lifetimes and dispatches diagnostics, language features,
commands and explicit host ports. It performs no Node filesystem, process or
network operations. See WORKSPACES for the separate Node server
and client, THEMES for XML themes, and PUBLISHING
and CONSENT for package review and acceptance.
definePlugin(manifest, activate) returns a frozen {manifest, activate} after
validating an independent copy. The host calls activate(context); return
undefined or a Disposable with dispose(), synchronously or asynchronously.
The host owns registration cleanup even if a plugin's disposer throws.
The diagnostics protocol remains 1. Manifest versions 1 and 2 are
separate accepted shapes; the manifest schema
contains both alternatives. Existing v1 plugins retain the original fields,
single identifier license, diagnostics capability, two diagnostics
permissions and supportedHosts: ['test-host'].
Version 2 adds runtime, source, commands and workspace permissions:
import { definePlugin } from '@altifigence/dds-plugin-sdk';
export default definePlugin({
manifestVersion: 2,
id: 'example-workspace', name: 'Example Workspace', publisher: 'example',
version: '1.0.0', protocolVersion: 1, entry: './plugin.mjs',
runtime: 'workspace', capabilities: ['commands'],
permissions: ['workspace.read', 'workspace.write', 'backend.invoke'],
supportedHosts: ['test-host', 'workspace-host'],
license: 'Apache-2.0',
source: {visibility: 'open', licenseFile: './LICENSE',
repository: 'https://github.com/example/example-workspace'},
}, context => context.registerCommand({
id: 'summarize', title: 'Summarize a saved file',
parameters: [{name: 'path', label: 'File path', type: 'string', required: true}],
}, async (input, {signal}) => {
const file = await context.workspace.readFile(input.path, {signal});
return context.backends.invoke('summarize', {content: file.content}, {signal});
}));Every field shown in this manifest is required except source.repository.
IDs and publishers use lowercase letters/digits with ., _, - separators,
starting with a letter; version follows SemVer. entry is a relative .mjs
path without traversal. createPluginHost() receives an explicitly imported
module and does not load that path.
V2 runtime is ui or workspace. UI manifests cannot request
workspace.read, workspace.write or backend.invoke. Runtime is compatibility
metadata, not an isolation mechanism. source.visibility is open or closed;
source.licenseFile is a relative file path, canonicalized without leading ./.
An optional repository must use credential-free HTTPS without a query or hash.
The parser validates metadata, not file existence or public repository access.
V2 license accepts bounded SPDX-style expression grammar, including AND,
OR, WITH, parentheses and LicenseRef- identifiers. For example,
MIT OR Apache-2.0 or LicenseRef-Proprietary. parseLicenseExpression()
checks syntax; it does not verify SPDX registry membership, copyright ownership,
license eligibility or agreement acceptance. See LICENSING.
| Activation context member | Behavior |
|---|---|
host |
{id: 'test-host' | 'workspace-host', version: '0.13.0', protocolVersion: 1} |
pluginId, scope |
Manifest ID and opaque host {projectId, sessionId} |
grants |
Frozen intersection of host grants and manifest permissions |
signal |
Aborted on deactivation, host disposal or activation timeout |
registerDiagnosticsProvider(selector, provider) |
Require declared diagnostics capability and both effective diagnostics grants |
registerLanguageProvider(kind, selector, provider) |
Require declared feature and effective document.read / language.provide grants |
registerCommand(definition, handler) |
Require declared commands capability; return a registration Disposable |
workspace |
Permission-checked saved-file read, list and CAS write APIs |
backends |
Permission-checked invocation of named, host-configured handlers |
settings |
Plugin/project-scoped effective reads and subscriptions; requires declared capability and grant |
createPluginHost() defaults to no grants. createTestHost() from
@altifigence/dds-plugin-sdk/testing preserves its v1 compatibility defaults:
test-host and both diagnostics grants. Permission declarations do not grant
themselves access. A missing optional port returns capability_unavailable.
context.registerCommand(definition, handler) takes {id, title, description?, parameters?, inputSchema?, outputSchema?, display?}. Each legacy parameter is {name, label, type, required, choices?}; types are string, number or boolean. choices is a nonempty
unique array available only for string parameters. A command supports at most
32 parameters and each string choice has at most 256 code points.
If both parameters and inputSchema are omitted, the input may be any bounded JsonValue. If parameters is present,
including an empty array, input must be an object containing only the named
parameters. Required values must be supplied, types must match and choices must
match exactly. Legacy parameters have no defaults or coercion. New schemas and
parameters are mutually exclusive; schemas support validated defaults and output
validation. See structured data, settings,
secret leases and localized display.
const registration = context.registerCommand({
id: 'greet', title: 'Say hello',
parameters: [{name: 'name', label: 'Your name', type: 'string', required: true}],
}, (input, {signal}) => {
signal.throwIfAborted();
return {message: `Hello, ${input.name}!`};
});The host lists immutable metadata with host.listCommands() and calls
host.executeCommand(pluginId, commandId, input, {signal?, timeoutMs?}).
Handlers return JsonValue or a promise of it. Input and output are copied and
bounded; class instances, functions, accessors, symbols, sparse arrays,
non-finite numbers and cycles are rejected. Registration disposal aborts its
pending calls. Language providers add completion, hover, definition,
references and document symbols. Code actions and automatic edit application are
not implemented. Client-side project edit sessions save explicitly
provided content or text edits with revision checks.
| Plugin method | Permission | Result |
|---|---|---|
workspace.readFile(path, options?) |
workspace.read |
{path, content, revision} |
workspace.listFiles(path = '', options?) |
workspace.read |
Array of {path, kind: 'file' | 'directory', revision?} |
workspace.writeFile(path, content, {expectedRevision, signal?, timeoutMs?}) |
workspace.write |
{path, revision} |
backends.invoke(id, input, options?) |
backend.invoke |
Bounded JsonValue |
options means {signal?, timeoutMs?}. Paths use workspace-relative /
separators without absolute roots, backslashes, colons, control characters,
empty segments or ./... Only listing accepts '' for the root. The portable
parser checks syntax; the host port owns containment, symlink policy and the
actual allowed root. Listings have unique relative paths, at most 1,000 entries,
and results must stay under a requested directory. File results must echo the
requested path.
Writes require an explicit compare-and-swap expectation:
const saved = await context.workspace.readFile('notes.txt', {signal});
const changed = await context.workspace.writeFile('notes.txt', 'Updated\n', {
expectedRevision: saved.revision, signal,
});
const created = await context.workspace.writeFile('new-note.txt', 'New\n', {
expectedRevision: null, signal,
});null means create an absent file only. An existing file requires the revision
returned by a read. The port must enforce CAS and return conflict after a
mismatch; the portable host cannot enforce a filesystem transaction itself.
Core revisions are opaque strings of 1–128 code points. The supplied Node
workspace uses SHA-256 of exact UTF-8 bytes and has a stricter path policy;
see WORKSPACES and the wire protocol.
The host configures backends: {name: handler}. A handler receives
(input, {signal, pluginId, scope}) and returns JSON. The plugin chooses only a
configured name; it cannot provide an executable, URL or shell command through
this API. The Node adapter separately supports operator-configured tools.
A selector is {languages: ['plaintext'], priority?: 0}. Priority ranges from
-100 to 100. Highest priority wins, followed by the earliest active registration.
provideDiagnostics(request, {signal}) returns a result or promise. The request
contains {protocolVersion: 1, requestId, scope, snapshot}, with snapshot
{uri, languageId, modelVersion, workspaceRevision, text}.
The URI identifies supplied document text; it grants no access to that resource.
modelVersion is a positive safe integer. Scope/request/revision strings have
at most 128 Unicode code points. Use createDiagnosticsResult(request, items)
to echo the identity without document text:
return createDiagnosticsResult(request, [{
range: {start: {line: 0, character: 0}, end: {line: 0, character: 4}},
severity: 'info', message: 'Resolve this TODO.', code: 'todo', source: 'example',
}]);Positions use zero-based lines and UTF-16 code-unit offsets. Severity is error,
warning, info or hint; messages are plain text. The host checks identity,
ordered ranges and endpoints against the supplied CRLF/LF/CR document. Edits,
document switches and revision changes invalidate pending results.
createPluginHost({hostId?, scope?, grants?, workspace?, backends?}) returns a
trusted in-process PluginHost. hostId defaults to test-host; scope
defaults to example-project / example-session. Methods are activate,
setDocument, requestDiagnostics, requestLanguage, listPlugins, listCommands,
executeCommand, deactivate and dispose. See host-contract
and testing for runnable host use.
parseManifest, parseDiagnosticsRequest and parseDiagnosticsResult accept
plain objects or JSON text. parseDocumentSnapshot and
parseCommandDefinition accept plain data objects. parseJsonValue validates
a JSON value rather than parsing text. parseWorkspacePath checks path syntax.
All structured outputs are independent frozen copies. Runtime checks reject
unexpected fields and unsupported object shapes without invoking getters or
caller toJSON hooks.
Exported LIMITS value |
Limit |
|---|---|
manifestBytes, commandBytes |
16,384 compact UTF-8 JSON bytes each |
requestBytes, resultBytes |
1,600,000 UTF-8 JSON bytes each |
documentBytes, jsonBytes |
262,144 UTF-8 bytes each |
maxDiagnostics, maxLanguageItems, messageLength |
500 diagnostics or language items; 2,048 code points per diagnostic message |
maxRegistrations, maxCommands |
32 active plugins/providers/backend handlers; 64 commands per host |
maxPendingRequests, maxFiles |
64 per registry/host operation group; 1,000 listed entries |
jsonDepth, jsonNodes |
Depth 16; 10,000 visited JSON nodes |
jsonArrayItems, jsonObjectProperties |
1,000 array items; 128 fields per object |
defaultTimeoutMs, maxTimeoutMs |
5,000 ms default/activation; 30,000 ms maximum request |
JSON text budgets count original whitespace. Object budgets use only validated
copies. JSON Schema 2020-12 files under schemas/ describe shapes; runtime
parsers additionally enforce UTF-8 totals, license expression grammar,
request/result identity, ranges and parameter semantics.
Check the supplied AbortSignal before work and between asynchronous steps.
Cancellation settles a host request and discards late results. It cannot stop a
blocking loop, undo a completed write or revoke ambient process APIs. Activation
has a fixed 5-second deadline; request timeoutMs must be an integer 1–30,000.
Catch PluginSdkError and inspect code:
| Code | Meaning |
|---|---|
invalid_contract |
Invalid fields, parameters, ranges, options or activation return |
permission_denied |
A required effective grant is absent |
unsupported_host, version_mismatch |
Host or diagnostics protocol incompatibility |
cancelled, stale_snapshot |
Caller cancelled, or document identity changed |
budget_exceeded |
Input/output/concurrency limit or timeout |
disposed |
Plugin, registration or host removed |
provider_failed, provider_unavailable |
Plugin failed, or no matching provider exists |
capability_unavailable |
No named command/backend or workspace port exists |
conflict |
Host port rejected a stale file revision |
Plugin exception details are masked by the host. Trusted port PluginSdkError
codes are preserved with a safe message. The Node wire protocol uses its own
WorkspaceError vocabulary; see workspace-protocol.
WorkspaceClient adds getFileCapabilities(options?), getFileRevision(path, options?)
and readFileIfChanged(path, knownRevision, options?) in 0.6.0. An unchanged
conditional result has notModified: true and no content; a changed result has
notModified: false and content. knownRevision: null requests content.
See file revisions for shapes, fallback and lifecycle rules.
SDK 0.7.0 adds job.addBinaryArtifact(), client.getBinaryArtifactCapabilities(),
listJobBinaryArtifacts(), getJobBinaryArtifact() and readJobBinaryArtifactChunk().
The Node downloadJobBinaryArtifact() helper verifies and publishes a complete
file, with explicit resumption of a partial download. Contracts and decoders are
exported through /artifacts. See binary artifacts for
operator opt-in, identity, limits, filesystem behavior and browser responsibilities.
Use /job-storage and /job-storage-node for committed recovery; /job-history
for bounded queries and explicit retry; /artifact-storage and
/artifact-storage-node for retained result contracts and bytes. The core host
adds artifactStorageCapabilities, listStoredJobArtifacts,
getStoredJobArtifact and readStoredJobArtifactChunk. The HTTP client adds the
corresponding capability/list/get/chunk methods and readStoredJobArtifactText;
/workspace-node exports downloadStoredJobArtifact. Exact parameters and
limits are in the declarations and stored-result guide.
/project-watch and /project-query export pure contracts, validators and limits.
/project-watch-node exports createNodeProjectWatcher, and /project-query-node
exports createNodeProjectQueries sharing that watcher's scope and lifetime.
The Node workspace server optionally accepts projects:{roots,fileSystem:'local'}.
The HTTP client and createWorkspaceProject expose getProjectCapabilities,
getProjectSnapshot, listTree, searchFiles, searchText, releaseProjectQuery
and watchProject. /workspace-client also exports the transport limits and
capability/poll validators. The server's revokeProjects() closes project resources
and requires a new generation to re-enable them. Edit sessions retain their own
snapshots. See project tools, observation
and tree/search.
/artifact-transfer exports browser-safe streamJobBinaryArtifact and
streamStoredJobArtifact, incremental SHA-256 and typed sink/verification contracts.
/artifact-transfer-browser supplies selected-file and OPFS writable sinks;
/artifact-resume-browser adds createOpfsArtifactSink, explicit partial names,
checkpoint validation and Worker capability detection. Recovery requires
resume: true and independently rehashed local prefix bytes.
/uploads provides contracts and uploadFile; /uploads-node provides the
private Node upload store and selected-file source; /uploads-browser adapts an
explicit File. The HTTP client adds capability/begin/query/write/commit/abort
methods. /transfer-queue exports createTransferQueue, options/snapshot validators,
limits, states and phases. See uploads, streaming,
queue and persistent recovery.
The root import adds signature help, completion resolution/snippets, rename,
formatting, code actions, semantic tokens, folding, inlay hints and symbol trees.
host.languageCapabilities() describes current features and limits;
host.validateLanguageResult(result) checks actual host ownership and currency
before painting. Features and modifiers require explicit manifest declarations.
/workspace-edits supplies proposal/preview/receipt contracts, pure preview,
reviewed application, observational recovery and explicit inverse proposals.
/workspace-edits-node adds an operator-owned intent/receipt journal outside the
workspace. Application uses existing conditional file methods and current
authorization, with no automatic rollback or retry.
See assistance, workspace edits, formatting/actions and language display for complete contracts, limits and independent examples.
See release tools for the complete bundle, Ed25519 provenance, approval/activation journal and upstream candidate APIs. The five entry points provide declarations and versioned schemas without adding SDK runtime dependencies.
See workflows and cache for DAG plans, per-step command jobs, durable checkpoints, explicit resume and deterministic incremental reuse. Tool streams covers the portable parser and optional Node process runner. LSP maps the public stdio bridge's negotiated subset to existing language contracts. Seven additional exports have declarations and eleven schemas; only the tool-output parser is portable to a browser.