@intentic/extension-api 1.248.0 → 1.249.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/server.ts CHANGED
@@ -1,50 +1,27 @@
1
- /* THE SERVER HALF an extension programs against, the daemon-side twin of IntenticApi (api.ts).
2
- *
3
- * A manifest `server` bundle exports `activateServer(api, context)`, and the daemon's BACKEND HOST, one
4
- * separate supervised node process shared by every enabled extension with a backend, imports the bundle and
5
- * calls it. A separate process rather than the daemon itself because loaded code can never be unloaded: the
6
- * off switch, an upgrade to a new sha and a live-edited workspace extension all require the process holding
7
- * the old code to die, and that process must never be the daemon (chat, terminals and file sync live there).
8
- * A shared process rather than one per extension because the trust model is full trust (install is owner-only
9
- * and sha-pinned), isolation between extensions would buy robustness nobody is billed for.
10
- *
11
- * Full trust is also why this surface is deliberately small. The backend runs in the sandbox container as the
12
- * same user the daemon does, so the workspace is reachable with plain `node:fs`, the api hands over PATHS,
13
- * not a file service. What it does mediate is the two things a path cannot carry: the extension's route
14
- * namespace (mount), and its reach into the daemon's own routes (daemon.*, gated by the manifest's
15
- * `permissions.daemon`, the daemon refuses undeclared routes, same grammar and same honesty rule as the UI
16
- * half's `permissions.sandbox`). */
1
+ // Backend counterpart to `IntenticApi` (api.ts); a manifest `server` bundle's `activateServer` runs in a node process
2
+ // shared by every enabled extension, separate from the daemon. Mediates only the extension's route namespace (mount)
3
+ // and its reach into daemon routes (`daemon.*`, gated by `permissions.daemon`).
17
4
 
18
- // One request into this extension's namespace. The host strips the `/x/<id>` prefix before dispatch, so the
19
- // handler sees the extension's OWN paths, the same paths its contract declares and its UI half calls.
20
- // `undefined` means "not mine": the host answers 404 without the extension having to speak HTTP for it.
5
+ // One request into this extension's `/x/<id>` namespace, prefix already stripped. Return `undefined` for "not mine":
6
+ // the host answers 404.
21
7
  export type BackendRouteHandler = (request: Request) => Promise<Response | undefined>;
22
8
 
23
9
  export interface ExtensionServerApi {
24
- // The host's @intentic/extension-api version, what `engines.intentic` was checked against.
10
+ // The host's @intentic/extension-api version, checked against `engines.intentic`.
25
11
  readonly apiVersion: string;
26
- // The workspace root (absolute). The backend reads and writes under it with node's own fs, full trust
27
- // means no file service in between. Durable state belongs in workspace files (the same rule the UI half
28
- // lives by): it survives restarts, is shared across browsers, and the agent editing it out-of-band is the
29
- // product.
12
+ // Absolute workspace root; the backend reads and writes it directly via node's `fs`, no file service in between.
30
13
  readonly workspaceRoot: string;
31
14
  // This extension's own checkout (absolute), where its bundled assets sit.
32
15
  readonly extensionDir: string;
33
16
  // A line in the daemon's log, attributed to this extension.
34
17
  readonly log: (message: string) => void;
35
18
  readonly routes: {
36
- /* Serve this extension's route namespace. The daemon proxies /x/<id>/* here, through its ordinary
37
- * auth (an owner's browser, a member at the route's role floor), so a backend never sees an
38
- * unauthenticated request and never sees a credential. One handler per extension: the extension owns
39
- * its whole namespace, and how it routes inside it (an oRPC handler over its own contract, a plain
40
- * switch) is its own business. A second mount replaces the first. */
19
+ // Serves this extension's route namespace; the daemon proxies /x/<id>/* here through its ordinary auth. A
20
+ // second mount replaces the first.
41
21
  mount(handler: BackendRouteHandler): void;
42
22
  };
43
- /* The authenticated transport to the daemon's own routes, the backend's `api.sandbox`. Auth is a minted
44
- * per-extension token injected here; the daemon's gate checks every call against the manifest's
45
- * `permissions.daemon` allowlist, so a backend's reach into the core is declared, reviewable and refusable
46
- * exactly like the UI half's. The extension's own namespace needs no declaration, but there is also no
47
- * reason to dial yourself over HTTP. */
23
+ // Authenticated transport to the daemon's own routes; every call is checked against the manifest's
24
+ // `permissions.daemon` allowlist.
48
25
  readonly daemon: {
49
26
  request(path: string, init?: RequestInit): Promise<Response>;
50
27
  json<T>(path: string, init?: RequestInit): Promise<T>;
@@ -52,14 +29,12 @@ export interface ExtensionServerApi {
52
29
  }
53
30
 
54
31
  export interface ExtensionServerContext {
55
- // The extension's routing id, its /x/<id> namespace segment (the capability entry id for a git-installed
56
- // extension, publisher.name otherwise; the same id the UI half sees as ExtensionSummary.id).
32
+ // This extension's routing id, its /x/<id> namespace segment.
57
33
  readonly extensionId: string;
58
34
  }
59
35
 
60
- // The shape of the manifest `server` bundle's default export (or its named exports): `activateServer` runs
61
- // once per backend-host start, after the engines check. There is no deactivate, retirement IS the host
62
- // process ending, which is the one teardown that cannot leak.
36
+ // Shape of the manifest `server` bundle's default (or named) export; `activateServer` runs once per backend-host start.
37
+ // No deactivate: retirement is the host process ending.
63
38
  export interface ExtensionServerModule {
64
39
  activateServer(api: ExtensionServerApi, context: ExtensionServerContext): void | Promise<void>;
65
40
  }
package/src/stream.ts CHANGED
@@ -1,15 +1,11 @@
1
- /* Reading a daemon SSE/ndjson stream, the transport half of `sandbox.request().body`. The daemon emits SSE
2
- * frames (blank-line separated, each a `data: <JSON>` line, an oRPC event-iterator failure as `event: error`),
3
- * so consuming a streamed apply/plan/provision means reframing + JSON-parsing. Pure (ReadableStream in, async
4
- * records out), no deps, extensions bundle it; the shim path never touches it. */
1
+ // Reads a daemon SSE/ndjson stream: reframes blank-line-separated `data: <JSON>` frames and parses each. Pure
2
+ // (ReadableStream in, async records out), no deps.
5
3
 
6
- // A silent daemon, one that accepts the stream then sends nothing and never closes, would park reader.read()
7
- // forever, hanging the consumer. A live daemon heartbeats (≤1s) over the stream, so no bytes for this long means
8
- // the connection is dead: cancel the reader and end the generator instead of waiting indefinitely.
4
+ // A live daemon heartbeats over the stream at least this often; no bytes for this long means the connection is dead.
9
5
  const SSE_IDLE_MS = 120_000;
10
6
 
11
- // Yields each raw SSE frame (the text between blank-line separators), reassembling frames split across chunks.
12
- // Ends (cancelling the reader) if the daemon goes silent past SSE_IDLE_MS, so a half-open stream can't hang.
7
+ // Yields each raw SSE frame, reassembling frames split across chunks; ends (cancelling the reader) after SSE_IDLE_MS of
8
+ // silence.
13
9
  async function* sseFrames(body: ReadableStream<Uint8Array>): AsyncGenerator<string> {
14
10
  const reader = body.getReader();
15
11
  const decoder = new TextDecoder();
@@ -58,9 +54,8 @@ const dataOf = (frame: string): unknown => {
58
54
  }
59
55
  };
60
56
 
61
- // Reads a daemon stream as parsed ndjson records. An `event: error` frame is normalized to a
62
- // `{ kind: "error", message }` record so callers surface it and stop even when the daemon couldn't emit its
63
- // own error line; malformed frames are skipped.
57
+ // Reads a daemon stream as parsed ndjson records. An `event: error` frame becomes a `{ kind: "error", message }`
58
+ // record; malformed frames are skipped.
64
59
  export async function* readDaemonStream(body: ReadableStream<Uint8Array>): AsyncGenerator<Record<string, unknown>> {
65
60
  for await (const frame of sseFrames(body)) {
66
61
  const parsed = dataOf(frame);
package/src/surface.json CHANGED
@@ -611,5 +611,89 @@
611
611
  "repos",
612
612
  "write"
613
613
  ]
614
+ },
615
+ "2.11.0": {
616
+ "manifest": [
617
+ "$schema",
618
+ "art",
619
+ "category",
620
+ "contributes",
621
+ "engines",
622
+ "entry",
623
+ "icon",
624
+ "logo",
625
+ "name",
626
+ "permissions",
627
+ "publisher",
628
+ "server",
629
+ "version"
630
+ ],
631
+ "contributes": [
632
+ "agent",
633
+ "automationTemplates",
634
+ "bin",
635
+ "capabilities",
636
+ "commands",
637
+ "documents",
638
+ "environment",
639
+ "files",
640
+ "listener",
641
+ "processes",
642
+ "settings",
643
+ "viewers",
644
+ "views"
645
+ ],
646
+ "api": [
647
+ "apiVersion",
648
+ "chat",
649
+ "commands",
650
+ "documents",
651
+ "href",
652
+ "models",
653
+ "navigate",
654
+ "processes",
655
+ "route",
656
+ "sandbox",
657
+ "settings",
658
+ "terminal",
659
+ "theme",
660
+ "viewers",
661
+ "views",
662
+ "workspace"
663
+ ],
664
+ "listener": ["automation", "events", "provider"],
665
+ "sandboxApi": ["fetch", "json", "key", "origin", "reachable", "request", "role", "rpc"],
666
+ "moduleExports": [
667
+ "extensionApiVersion",
668
+ "flattenQuery",
669
+ "hostSlot",
670
+ "mergeQuery",
671
+ "readDaemonStream",
672
+ "resetSandboxScope",
673
+ "sandboxLedger",
674
+ "sandboxPoll",
675
+ "sandboxRef",
676
+ "sandboxScopeGuard",
677
+ "sandboxValue",
678
+ "satisfiesEngines"
679
+ ],
680
+ "workspaceApi": [
681
+ "capabilities",
682
+ "file",
683
+ "fillDiff",
684
+ "onDidChange",
685
+ "onDidChangeFiles",
686
+ "onDidChangeRefs",
687
+ "openDiff",
688
+ "readJson",
689
+ "repos",
690
+ "write"
691
+ ],
692
+ "chatApi": [
693
+ "composeLoop",
694
+ "composeWorkflow",
695
+ "openAgent",
696
+ "openSession"
697
+ ]
614
698
  }
615
699
  }
package/src/version.ts CHANGED
@@ -67,4 +67,9 @@
67
67
  // the slowest tile sat ten minutes behind the file it described. Additive, and the recorded surface grew a
68
68
  // `workspaceApi` member list with this release, the same grain `sandboxApi` got at 2.3.0 and for the same
69
69
  // reason: this is where the addition happened, and nothing could see it.
70
- export const extensionApiVersion = "2.10.0";
70
+ // 2.11.0 adds `api.chat.openAgent`: open (or focus) the docked chat for a fleet agent by its id, the same
71
+ // thing a card press on the agents board does. The agent's conversation appears in the chat panel rather
72
+ // than navigating away from the current view. Two extensions were navigating to `/agents/<id>` on a plain
73
+ // click, which left the view they were on; this is the call they should have had. The `chatApi` sub-surface
74
+ // is recorded from this release on, for the same reason `workspaceApi` was recorded from 2.10.0.
75
+ export const extensionApiVersion = "2.11.0";