Backend SDK and invocation context
@ambleloft/extension-sdk: register business operations, access project resources and manage extension state.
Browse contracts, signatures, fields and examples. Unspecified defaults are not inferred.
On this page
01Entry points and lifecycle
defineExtension accepts activate and optional deactivate; activate returns void or Promise<void>. The register handler name must match the manifest handler. Returned objects must satisfy outputSchema.
import { defineExtension } from '@ambleloft/extension-sdk';
export const { activate } = defineExtension({
activate(context) {
context.subscriptions.push(
context.operations.register('echo', async input => ({ text: input.text }))
);
}
});| Field / API | Contract |
|---|---|
| OperationHandler | (input: JsonObject, context: InvocationContext) → Promise<JsonObject>. |
| Disposable | dispose(): void; use subscriptions for lifecycle cleanup. |
| extensionId / packageRevision | Read-only extension identity and package revision. |
02InvocationContext
The host binds the invocation context; arbitrary input does not replace project, form or message identity.
| Field / API | Contract |
|---|---|
| requestId / caller | Request identifier; caller is page or agent. |
| projectId / signal | Optional bound project; AbortSignal propagates cancellation. |
| form? | flowInstanceId, stepId, submissionId, templateDigest, conversationId. |
| message? | invocationId, messageId, actionId. |
| resources | ProjectResources for this invocation. |
| authentication.requestSession(resourceId) | Interactive AuthenticationSession request, page calls only; declare timeoutMs: 120000 for login. |
03ProjectResources
Project IDs must match the bound invocation project. Declared permissions still require runtime grants. Creating a conversation only creates a draft; it does not run the Agent.
| Field / API | Contract |
|---|---|
| getProject(projectId): Promise<Project> | projects.read; returns id, name and description. |
| getProjectPath(projectId): Promise<string> | projects.path.read; reads the project path. |
| createConversation({projectId,title,initialPrompt?}) | conversations.create; Promise<ConversationRef> with id, projectId and state. |
| openConversation(conversationId): Promise<void> | conversations.open; page-only interactive opening, agent gets INTERACTION_REQUIRED. |
04Configuration, storage and secrets
Declare the corresponding capabilities. KV revisions provide optimistic concurrency. null is create-only for absent keys, including after deletion; revisions remain monotonic.
| Field / API | Contract |
|---|---|
| storage.get(key): Promise<KvEntry | null> | KvEntry = {value: Json, revision: number}. |
| storage.set(key,value,expectedRevision): Promise<number> | expectedRevision: number or null; returns the new revision. |
| storage.delete(key,expectedRevision): Promise<void> | Requires the current revision; reread on conflict. |
| configuration.get(): Promise<JsonObject> | Read configuration; onDidChange(listener): Disposable observes changes. |
| secrets.get / set / delete | get(key): Promise<string|null>; set(key,value), delete(key): Promise<void>. |
| context.set(key,value): Promise<void> | Value is boolean, string or number. |
| l10n.t(key,args?): string | args: Record<string,string|number|boolean>; extension string localization. |
05runtime-preview helpers
Preview capability · SDK 0.1.0-alpha.9
getResources(invocation) returns bound resources, rejects empty IDs and never creates conversations on open. observeConfiguration subscribes before reading to avoid stale initial data overwriting new events. It does not retry automatically.
getResources(invocation: InvocationContext): ProjectResources;
observeConfiguration<T>(
context: ExtensionContext,
decode: (value: JsonObject) => T,
onChange: (value: T) => void
): Disposable & { ready: Promise<void> };06Migrate to this internal contract
Preview capability · SDK 0.1.0-alpha.9
Install matching local SDK, CLI and core packages using the setup guide, with the matching host. Internal version 0.1.0-alpha.9 is not a formal release or platform certification. This contract drops five-method host compatibility: getMessages requires all seven methods and throws UNSUPPORTED if any is missing. Action records use id and effect; reportActionResult takes invocationId. A successful handler return still requires an explicit business-result report.
Prepare matching local packages07Migrate model-tool and retry handling
user-input is a types-only entry: use import type. tools.schema.json and validate-input provide static validation; execution belongs to host model tools. Preserve full UI flow results and use the new ID after updates. Input creation retries return current state; mutation retries return the original receipt. Check answer and delivery separately. Job retries retain the original serialized request and key order; reconcile the original job on unknown. Handle operation Result, Node rejection, input Result and MCP isError separately without inventing error fields.
Run the minimal input example08Run the supplied consumer checks
Run from the matching ambleloft-extension-samples root after the coordinator installs and verifies the same local package batch. The first command covers messages, page grants and cancellation, and CLI static positive/negative cases. The second checks types. Host doubles do not prove real authorization or desktop workflows. Then follow each capability guide in the matching host to check visible results and recovery. doctor still reports source matching only, with runtimeStatus=not-tested.
node --test verification/local-sdk/consumer.test.mjs verification/local-sdk/cli.test.cjs
node node_modules/typescript/bin/tsc -p verification/local-sdk/tsconfig.json