@rackbops/ac-plugin-contract 2.0.0-alpha.57 → 2.0.0-alpha.59

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/AUTHORING.md CHANGED
@@ -39,6 +39,7 @@ key is itself a hard validation error, not a silently-ignored field.
39
39
  "kind": "in-process",
40
40
  "server": "dist/server.js",
41
41
  "frontend": "dist/ui.js",
42
+ "capabilities": ["agent.git.read"],
42
43
  "contributes": {
43
44
  "panels": [{ "id": "hello-panel", "title": "Hello", "route": "/hello", "export": "HelloPanel" }],
44
45
  "cards": [{ "id": "hello-card", "title": "Hello", "sizes": ["2x1"], "export": "HelloCard" }],
@@ -56,8 +57,7 @@ key is itself a hard validation error, not a silently-ignored field.
56
57
  { "id": "hello.machines", "schedule": "17 3 * * *", "timeoutSec": 30, "runOn": "agent" }
57
58
  ],
58
59
  "routes": [{ "method": "GET", "path": "/ping" }],
59
- "settings": [{ "key": "greeting", "type": "string", "default": "hello" }],
60
- "attention": true
60
+ "settings": [{ "key": "greeting", "type": "string", "default": "hello" }]
61
61
  }
62
62
  }
63
63
  ```
@@ -69,24 +69,28 @@ missing any of them fails `pnpm run check`. The complete shape (the `acPlugin` b
69
69
  ```jsonc
70
70
  {
71
71
  "name": "@ac/plugin-hello",
72
- "version": "0.1.0",
72
+ "version": "0.1.1",
73
73
  "private": true,
74
74
  "type": "module",
75
75
  "exports": { ".": { "default": "./dist/server.js" } },
76
76
  "acPlugin": { /* the block shown above */ },
77
77
  "scripts": {
78
78
  "typecheck": "tsc --noEmit && tsc -p tsconfig.ui.json --noEmit",
79
- "build": "tsc && tsc -p tsconfig.ui.json",
79
+ "build": "tsc -p tsconfig.build.json && tsc -p tsconfig.ui.build.json",
80
80
  "test": "vitest run"
81
81
  },
82
82
  "dependencies": {
83
83
  "@ac/contract": "workspace:*",
84
- "@rackbops/ui-react": "^0.2.36",
84
+ "@rackbops/ui-react": "^0.2.43",
85
85
  "react": "^19.2.0"
86
86
  },
87
87
  "devDependencies": {
88
+ "@testing-library/dom": "^10.4.2",
89
+ "@testing-library/react": "^16.1.0",
88
90
  "@types/node": "catalog:",
89
91
  "@types/react": "catalog:",
92
+ "@types/react-dom": "catalog:",
93
+ "jsdom": "^30.1.1",
90
94
  "typescript": "catalog:",
91
95
  "vitest": "catalog:"
92
96
  }
@@ -144,12 +148,26 @@ no host I/O, so the host can construct every plugin before any side effect. All
144
148
 
145
149
  ```ts
146
150
  import type { CreatePlugin } from "@ac/contract"
151
+ import { checkRow } from "./stored-row.js"
152
+
153
+ export const DEFAULT_GREETING = "hello"
154
+
155
+ function isString(value: unknown): value is string {
156
+ return typeof value === "string"
157
+ }
147
158
 
148
159
  export const createPlugin: CreatePlugin = (host) => ({
149
160
  activate() {
150
161
  // GET /api/x/hello/ping → the greeting setting (its default comes from the manifest `settings[]`).
162
+ // A stored greeting that is not a string reads as unset (#900; see "Setting" below).
151
163
  host.routes.register("GET", "/ping", async () => {
152
- const greeting = (await host.settings.get("greeting")) ?? "hello"
164
+ const greeting =
165
+ checkRow(await host.settings.get("greeting"), isString, {
166
+ log: host.log,
167
+ store: "plugin-settings",
168
+ key: "greeting",
169
+ expected: "string",
170
+ }) ?? DEFAULT_GREETING
153
171
  return Response.json({ greeting })
154
172
  })
155
173
  // An action: the button, its flags and the fields come from the manifest; the handler is here.
@@ -254,6 +272,27 @@ is never left open). Register last whatever must not exist when a later step fai
254
272
  carries it. See §7.
255
273
  - **Setting** — read/write with `host.settings.get(key)` / `host.settings.set(key, value)`. Both are async;
256
274
  `get` returns the manifest `default` when unset.
275
+ - **Storage** — `host.storage.get(key)` / `host.storage.set(key, value)` / `host.storage.delete(key)`, async,
276
+ any JSON value, scoped to your plugin.
277
+ - **Check the shape of what you read back** (#900). `get<T>` on either store is only a cast: the host
278
+ guarantees a row is valid JSON (a row that is not reads as unset, #879), never that it is the shape your
279
+ code expects. A hand edit, a restore from a damaged copy or an older version of your plugin can leave a
280
+ number where you wrote an object. So read untyped (`get(key)` answers `unknown`), check the fields you
281
+ dereference at the read site, and treat a row of the wrong shape as unset: your default applies (for a
282
+ setting, the manifest default), and log a warn line naming the key, never the value. Two refinements:
283
+ - if unset would **widen** what your plugin acts on (a deny list, a guard, a limit), refuse the row
284
+ instead and fail closed: an unreadable exclusion list must never mean "exclude nothing";
285
+ - for a row that keeps a list (a queue, a history), keep the good entries and drop only the bad ones,
286
+ and before you write such a row back over a value you could not read whole, keep the raw value
287
+ somewhere (`<key>/unreadable`), or one malformed entry, or a history saved in another shape, is lost
288
+ for good. Never write over a kept copy: if it already holds a different value, refuse the write
289
+ (fail the job or action, naming both keys) until the operator repairs the row or deletes the copy.
290
+
291
+ `plugins/hello`'s `/ping` is the worked example, and its `src/stored-row.ts` (`checkRow`, `requireRow`,
292
+ `checkList`, `checkMap`, `preserveUnreadable`) is the helper every bundled plugin copies: copy it into
293
+ yours, since plugins share no runtime code. It writes each line at most once an hour per row, so a
294
+ polled route over a bad row does not flood the log. The bundled plugins' lines and fields are in
295
+ `docs/identifiers.md` (#900's entry).
257
296
  - **Console settings** (#506) — `host.consoleSettings?.get(key)`, read-only, optional. Unlike `Setting`
258
297
  above (per-plugin), this reads the console-WIDE settings an operator edits in Admin's Settings card
259
298
  (`repo_owners`, `layout_file`, `tab_order`, `plan_default_machine`, `repo_pins`) -- but only a key on
@@ -261,7 +300,7 @@ is never left open). Register last whatever must not exist when a later step fai
261
300
  is the reference consumer (`repo_owners`/`repo_pins`, its own repo-set resolution).
262
301
 
263
302
  `createPlugin` must import from `@ac/contract` **type-only** (`import type`), so the compiled `dist/server.js`
264
- has no runtime imports — see §6.
303
+ has no runtime import of it — see §6 (a sibling module of your own, such as `./stored-row.js`, is fine).
265
304
 
266
305
  ### Rejecting a bad action input — `ActionInputError` (#440)
267
306
 
@@ -616,6 +655,16 @@ A `hint` (optional, e.g. a category or the plugin id) is searched the same as `l
616
655
  `>hel` finds an item whose label doesn't contain "hel" but whose hint does — and renders as muted secondary
617
656
  text in the row.
618
657
 
658
+ **What the chrome tolerates (#761).** A plugin bundle is built separately, so the shell does not trust the
659
+ shapes it hands to the chrome. A quickcast source that throws or returns a non-array is skipped, and an item
660
+ without a string `label` (or a `custom` item without a function `run`) is dropped, each logged with
661
+ `console.warn` at most once a minute per source. A toast message that is not a string is shown as its
662
+ `message` (an `Error`) or its `String()` form, and an unknown `severity` shows as `info`. A header button
663
+ label is shown as `String(label)`, and a button whose `onClick` is not a function is not added. If one of the
664
+ command palette, the toasts or the header buttons still fails to render, only that piece is replaced by a
665
+ one-line note and the rest of the console stays. The types in `@ac/contract` remain the contract; this is a
666
+ backstop, not a license.
667
+
619
668
  **Ctrl/Cmd+K from inside a same-origin iframe (#369).** A key pressed while an iframe has focus lands in the
620
669
  iframe's own document, never the shell's, so the palette does not open by itself. A panel that hosts a
621
670
  same-origin iframe (the Artifacts report preview does) forwards the hotkey: bind a `keydown` listener on the
@@ -695,34 +744,70 @@ Build it with `tsc` and `"jsx": "react-jsx"` — `tsc` under `nodenext` preserve
695
744
  import and injects `import { jsx } from "react/jsx-runtime"`, emitting exactly the servable ESM the import map
696
745
  resolves. Do **not** bundle (no rollup/esbuild bundling of React) — that would ship a second React copy.
697
746
 
698
- hello uses two tsconfigs so the server (no JSX) and the frontend (JSX) build separately:
747
+ hello uses four tsconfigs. The server (no JSX) and the frontend (JSX) build separately, and each has a typecheck config that covers its tests and a build-only config that does not, so `build` never emits a compiled test into `dist/` (#350; `pnpm run check`'s `check-no-test-emit.mjs` fails otherwise, and `check-typecheck-covers-tests.mjs` fails if a test file is in no typecheck config):
699
748
 
700
- `tsconfig.json` (server):
749
+ `tsconfig.json` (typecheck, server):
701
750
 
702
751
  ```json
703
752
  {
704
753
  "extends": "../../tsconfig.base.json",
705
754
  "compilerOptions": { "rootDir": "src", "outDir": "dist" },
706
- "include": ["src/server.ts", "src/server.test.ts"]
755
+ "include": [
756
+ "src/server.ts",
757
+ "src/server.test.ts",
758
+ "src/stored-row.ts",
759
+ "src/stored-row.test.ts",
760
+ "src/ac-host-drift.test.ts"
761
+ ]
707
762
  }
708
763
  ```
709
764
 
710
- `tsconfig.ui.json` (frontend):
765
+ `tsconfig.ui.json` (typecheck, frontend):
711
766
 
712
767
  ```json
713
768
  {
714
769
  "extends": "../../tsconfig.base.json",
715
- "compilerOptions": { "rootDir": "src", "outDir": "dist", "jsx": "react-jsx" },
716
- "include": ["src/ui.tsx", "src/ui.test.tsx"]
770
+ "compilerOptions": {
771
+ "rootDir": "src",
772
+ "outDir": "dist",
773
+ "jsx": "react-jsx",
774
+ "lib": ["ES2023", "DOM", "DOM.Iterable"]
775
+ },
776
+ "include": ["src/ui.tsx", "src/ui.test.tsx", "src/quickcast.test.tsx", "src/ac-host.d.ts"]
717
777
  }
718
778
  ```
719
779
 
720
- and `package.json` builds + typechecks both:
780
+ `tsconfig.build.json` (build, server):
781
+
782
+ ```json
783
+ {
784
+ "extends": "../../tsconfig.base.json",
785
+ "compilerOptions": { "rootDir": "src", "outDir": "dist" },
786
+ "include": ["src/server.ts", "src/stored-row.ts"]
787
+ }
788
+ ```
789
+
790
+ `tsconfig.ui.build.json` (build, frontend):
791
+
792
+ ```json
793
+ {
794
+ "extends": "../../tsconfig.base.json",
795
+ "compilerOptions": {
796
+ "rootDir": "src",
797
+ "outDir": "dist",
798
+ "jsx": "react-jsx",
799
+ "lib": ["ES2023", "DOM", "DOM.Iterable"]
800
+ },
801
+ "include": ["src/ui.tsx", "src/ac-host.d.ts"]
802
+ }
803
+ ```
804
+
805
+ and `package.json` builds with the build-only pair and typechecks with the other two:
721
806
 
722
807
  ```json
723
808
  "scripts": {
724
809
  "typecheck": "tsc --noEmit && tsc -p tsconfig.ui.json --noEmit",
725
- "build": "tsc && tsc -p tsconfig.ui.json",
810
+ "build": "tsc -p tsconfig.build.json && tsc -p tsconfig.ui.build.json",
726
811
  "test": "vitest run"
727
812
  }
728
813
  ```
@@ -874,12 +959,21 @@ if (outcome.ok) {
874
959
  }
875
960
  ```
876
961
 
962
+ **Treat every string union in the contract as open** (`error.kind` here, `ToastSeverity`, `TaskStatus`,
963
+ `PreflightStatus`, `OverviewOwner`, `ActionFieldType`, a filter token's `kind`): a later minor release may add a
964
+ member, which the contract calls additive. Keep a default branch (no `assertNever`) in any `switch` over one.
965
+ The unions most likely to grow carry a `MAY GROW` tag in their doc comment (`ActionFieldType` has grown before, #463, and may again; its doc comment is in the published schema, so it carries no tag).
966
+
877
967
  `outcome.error.kind === "grant"` is the one worth showing to the person almost verbatim -- it
878
968
  carries the agent's own refusal text (`"<capability> is not granted to <plugin> on <machine>"`). The
879
969
  others are the console's problem to describe differently: `unknown-machine` (a stale machine id),
880
- `refused` (the path isn't a `/v1/*` verb, or is `/v1/pair`/`/v1/grants` -- the control plane, never
970
+ `refused` (the path isn't a `/v1/*` verb, or is `/v1/pair`/`/v1/grants`/`/v1/preflight`/`/v1/update` -- the control plane, never
881
971
  reachable this way, whatever the path spelling), `pin` (the agent's certificate changed since
882
- pairing), `network`, `http` (an unexpected status the console doesn't special-case).
972
+ pairing), `network`, `http` (an unexpected status the console doesn't special-case). `ok: true` means the
973
+ agent answered a 2xx and its body was read (#778): a 2xx whose body could not be read (a timeout or a reset
974
+ mid-body, a body that is not JSON) is `ok: false`, `kind: "http"`, with that 2xx as `status` and the reason
975
+ `agent answered <status> but its body could not be read: <cause>`. The agent may have acted on such a call,
976
+ so a verb that starts something should not be retried blindly. A `204` is `ok: true` with no body.
883
977
 
884
978
  An agent job declares the capabilities its own verbs need, in the job's `capabilities` (#843). Each one
885
979
  must also be in the top-level list and match the capability grammar (`agent.` and lowercase segments).
@@ -952,10 +1046,12 @@ drawer shows: `{ status: "done" }` only for the agent's `done` frame with `statu
952
1046
  `stream ended before done: <message>`, the drawer's last line without its brackets, when the stream
953
1047
  closed or errored first or the `done` frame did not parse, so the job's own failure and a lost stream
954
1048
  read differently. It never rejects. It never settles when the relay sees no end at all: the host
955
- stopping while the job runs (the relay stops with the process), or a hung job. It does not settle
956
- while the connection is silently gone, and never if it does not come back (a reboot, a lost NAT
957
- mapping): no FIN or RST reaches the host, and the stream has no body timeout, no keepalive and no
958
- agent heartbeat.
1049
+ stopping while the job runs (the relay stops with the process), or a hung job (the stream has no body
1050
+ timeout and the agent sends no heartbeat). A connection silently gone (no FIN or RST reaches the host:
1051
+ the machine asleep or off the network, a reboot, a lost NAT mapping) settles `failed` with
1052
+ `stream ended before done: terminated` once TCP keep-alive's probes go unanswered (#777), about 70 s
1053
+ after the last byte on the console's Linux host; a machine asleep for longer than that mid-job reads
1054
+ `failed` even if the job finishes after it wakes.
959
1055
 
960
1056
  ```ts
961
1057
  const { jobId } = await execResult // however the plugin's own verb call started the job
@@ -987,7 +1083,7 @@ by `${source}:${id}`.
987
1083
  interface AttentionItem {
988
1084
  id: string // unique within YOUR plugin; the rollup's dedupe key is `${source}:${id}`
989
1085
  severity: "danger" | "warn" | "info"
990
- source: string // your own plugin id -- the rollup shows it as the row's source
1086
+ source: string // your own plugin id -- the dedupe key and draft scope; the rollup does not display it
991
1087
  text: string // always a counted sentence: "3 open actions in the ledger", never "some actions"
992
1088
  badge?: string
993
1089
  href?: string // an external link; wins over `target` when both are set. Linked only
package/README.md CHANGED
@@ -18,11 +18,20 @@ convention.
18
18
 
19
19
  - `import type { HostApi, Plugin, ... } from "@rackbops/ac-plugin-contract"` — the server-side types.
20
20
  - `@rackbops/ac-plugin-contract/schema.json` — the manifest JSON Schema, for a vendored drift check
21
- against the live console (§5 of AUTHORING.md).
21
+ (§9 of AUTHORING.md, "Vendoring `schema.json` with a drift check"). The check compares your
22
+ committed copy with the one in the installed package, so it detects a version bump that changed
23
+ the schema; it does not look at a running console.
22
24
  - `@rackbops/ac-plugin-contract/host` — a types-only ambient declaration of `@ac/host` (the browser
23
25
  API a plugin's frontend imports at runtime, resolved through the console's own import map). Add it
24
26
  to your tsconfig's `"types"` array or a triple-slash reference — it carries no runtime export.
25
27
 
28
+ ## Unions are open
29
+
30
+ Treat every string union this package exports (`AgentCallOutcome`'s `error.kind`, `ToastSeverity`,
31
+ `TaskStatus`, `PreflightStatus`, `OverviewOwner`, `ActionFieldType` and the like) as open: a later minor
32
+ release may add a member, so keep a default branch in any `switch` over one. The unions most likely to grow
33
+ carry a `MAY GROW` tag in their doc comment.
34
+
26
35
  ## Version
27
36
 
28
37
  This package's version always equals the published `artifact-console` host's own version — pin your
@@ -46,4 +46,3 @@ export type ActionOptionsResponse = {
46
46
  ok: false;
47
47
  error: string;
48
48
  };
49
- //# sourceMappingURL=action-options.d.ts.map
@@ -6,4 +6,3 @@
6
6
  * nothing here changes an existing field.
7
7
  */
8
8
  export {};
9
- //# sourceMappingURL=action-options.js.map
@@ -3,8 +3,8 @@
3
3
  * `contributes.attention: true` in its manifest and serves `GET /api/x/<id>/attention` ->
4
4
  * `{ ok: true, items: AttentionItem[] }`. The `overview` plugin's rollup card polls every
5
5
  * contributor (discovered via `GET /api/plugins`'s `PluginView.attention`), merges, sorts by
6
- * severity, and dedupes by `${source}:${id}`. Ported from 1.x's `attentionRows` (`R:\repos\
7
- * artifacts-console\artifact-console\frontend\src\overview\attention.ts`) -- "own the fact, own
6
+ * severity, and dedupes by `${source}:${id}`. Ported from 1.x's `attentionRows` (the `artifacts-console`
7
+ * repo's overview `attention.ts`) -- "own the fact, own
8
8
  * the row": each plugin knows its own facts best, so the rollup itself derives nothing, it only
9
9
  * merges what contributors already computed.
10
10
  */
@@ -28,7 +28,7 @@ export interface AttentionItem {
28
28
  /** Unique within the contributing plugin; the rollup's dedupe key is `${source}:${id}`. */
29
29
  id: string;
30
30
  severity: AttentionSeverity;
31
- /** The contributing plugin's id, e.g. "scans" -- the rollup shows it as the row's source. */
31
+ /** The contributing plugin's id, e.g. "scans" -- identifies the contributing plugin (dedupe key, draft scope); the rollup does not display it. */
32
32
  source: string;
33
33
  /** Always a counted sentence (1.x's `attCount`, e.g. "1 scheduled job run failed"). */
34
34
  text: string;
@@ -53,4 +53,3 @@ export interface AttentionResponse {
53
53
  ok: true;
54
54
  items: AttentionItem[];
55
55
  }
56
- //# sourceMappingURL=attention.d.ts.map
package/dist/attention.js CHANGED
@@ -3,10 +3,9 @@
3
3
  * `contributes.attention: true` in its manifest and serves `GET /api/x/<id>/attention` ->
4
4
  * `{ ok: true, items: AttentionItem[] }`. The `overview` plugin's rollup card polls every
5
5
  * contributor (discovered via `GET /api/plugins`'s `PluginView.attention`), merges, sorts by
6
- * severity, and dedupes by `${source}:${id}`. Ported from 1.x's `attentionRows` (`R:\repos\
7
- * artifacts-console\artifact-console\frontend\src\overview\attention.ts`) -- "own the fact, own
6
+ * severity, and dedupes by `${source}:${id}`. Ported from 1.x's `attentionRows` (the `artifacts-console`
7
+ * repo's overview `attention.ts`) -- "own the fact, own
8
8
  * the row": each plugin knows its own facts best, so the rollup itself derives nothing, it only
9
9
  * merges what contributors already computed.
10
10
  */
11
11
  export {};
12
- //# sourceMappingURL=attention.js.map
@@ -28,7 +28,9 @@ export interface SettingsService {
28
28
  export interface ConsoleSettingsService {
29
29
  get<T = unknown>(key: string): Promise<T | undefined>;
30
30
  }
31
- /** Per-plugin durable storage: a SQLite table scoped to the plugin, with atomic JSON helpers. */
31
+ /** Per-plugin durable storage: the plugin's rows in the host's shared `plugin_storage` table, keyed by
32
+ * (plugin id, key), with plain `get`/`set`/`delete` over JSON values (an `undefined` value is stored as
33
+ * `null`). */
32
34
  export interface StorageService {
33
35
  get<T = unknown>(key: string): Promise<T | undefined>;
34
36
  set(key: string, value: unknown): Promise<void>;
@@ -73,7 +75,9 @@ export interface JobDeclaration {
73
75
  id: string;
74
76
  runOn: "host" | "agent";
75
77
  }
76
- /** The typed event bus (e.g. `issue.changed`, `job.finished`, `artifact.rendered`). */
78
+ /** The event bus: `emit(type: string, payload: unknown)` is untyped, so a type name is a convention between
79
+ * emitter and subscriber (the host emits `job.finished`; core plugins emit `artifacts.rendered` and
80
+ * `claude.run`; `issue.changed` is the documented example of a plugin-to-plugin event). */
77
81
  export interface EventsService {
78
82
  /** Calls every subscriber of `type` synchronously, in subscription order, and never awaits one.
79
83
  * A subscriber that throws, or returns a promise that rejects, is logged by the host and stops
@@ -158,9 +162,11 @@ export interface AgentService {
158
162
  * tasks drawer's last line without its brackets, when the stream closed or errored before its
159
163
  * `done` frame, or that frame did not parse. It never rejects. It never settles when the relay sees
160
164
  * no end at all: when the host stops while the job runs (the relay stops with the process), or when
161
- * the job hangs. It does not settle while the connection is silently gone, and never if it does not
162
- * come back (a reboot, a lost NAT mapping): with the machine asleep or the network gone, no FIN or
163
- * RST reaches the host, and the stream has no body timeout, no keepalive and no agent heartbeat.
165
+ * the job hangs (the stream has no body timeout and the agent sends no heartbeat). A connection
166
+ * silently gone (the machine asleep or off the network, so no FIN or RST reaches the host) settles
167
+ * `failed` with `stream ended before done: terminated` once TCP keep-alive's probes go unanswered
168
+ * (#777), about 70 s after the last byte on the console's Linux host; a machine asleep for longer than
169
+ * that mid-job reads `failed` even if the job finishes after it wakes.
164
170
  */
165
171
  streamJob(machineId: string, jobId: string, opts: {
166
172
  label: string;
@@ -326,7 +332,7 @@ export interface Plugin {
326
332
  }
327
333
  /** A plugin's server entry: `dist/server.js` exports this as `createPlugin`. */
328
334
  export type CreatePlugin = (host: HostApi) => Plugin;
329
- /** Toast severity, matching notify. */
335
+ /** Toast severity, matching notify. MAY GROW: keep a default branch. */
330
336
  export type ToastSeverity = "info" | "warning" | "error";
331
337
  /**
332
338
  * The browser-side `@ac/host` a frontend bundle imports. v1 (implemented in #32) is `theme` tokens +
@@ -454,4 +460,3 @@ export interface ActionSettledEvent {
454
460
  ok: boolean;
455
461
  error?: string;
456
462
  }
457
- //# sourceMappingURL=host-api.d.ts.map
package/dist/host-api.js CHANGED
@@ -7,4 +7,3 @@
7
7
  * `HOST_API_VERSION`.
8
8
  */
9
9
  export {};
10
- //# sourceMappingURL=host-api.js.map
@@ -33,4 +33,3 @@ export interface PluginIndex {
33
33
  generatedAt?: string;
34
34
  plugins: PluginIndexEntry[];
35
35
  }
36
- //# sourceMappingURL=index-envelope.d.ts.map
@@ -1,2 +1 @@
1
1
  export {};
2
- //# sourceMappingURL=index-envelope.js.map
package/dist/index.d.ts CHANGED
@@ -13,4 +13,3 @@ export * from "./ov-widgets.js";
13
13
  export type * from "./slots.js";
14
14
  export { HOST_API_VERSION, SLOTS } from "./version.js";
15
15
  export type * from "./views.js";
16
- //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -6,4 +6,3 @@
6
6
  export * from "./issue-filter.js";
7
7
  export * from "./ov-widgets.js";
8
8
  export { HOST_API_VERSION, SLOTS } from "./version.js";
9
- //# sourceMappingURL=index.js.map
@@ -76,8 +76,9 @@ export interface AgeFilter {
76
76
  * Every axis is optional; an absent axis imposes no constraint. `priority`/`effort`/`type`/`tags` are
77
77
  * all "the record has a label whose name is in the set" -- the UI groups labels into these ladders
78
78
  * (from `labels.json`, #58), but for a plain value the predicate treats all four identically: a plain
79
- * label-name test. A token (#464a) adds `@any` and `@upto:<rung>` on the ladder axes, resolved against
80
- * `MatchContext.ladders`; `tags` has no ladder, so a token there matches nothing.
79
+ * label-name test. A token (#464a) adds `@any` (any ladder axis) and `@upto:<rung>` (priority and effort
80
+ * only; it matches nothing on `type`), resolved against `MatchContext.ladders`; `tags` has no ladder, so
81
+ * a token there matches nothing.
81
82
  */
82
83
  export interface IssueFilter {
83
84
  priority?: SetFilter;
@@ -119,4 +120,3 @@ export interface SavedView {
119
120
  * exactly as it always did, while a token that needs `ctx` matches nothing.
120
121
  */
121
122
  export declare function matchesIssue(record: IssueRecord, filter: IssueFilter, now: number, ctx?: MatchContext): boolean;
122
- //# sourceMappingURL=issue-filter.d.ts.map
@@ -149,4 +149,3 @@ export function matchesIssue(record, filter, now, ctx) {
149
149
  matchesAge(ageBucket(record.updatedAt, now), filter.activity) &&
150
150
  matchesSearch(record, filter.search));
151
151
  }
152
- //# sourceMappingURL=issue-filter.js.map
@@ -253,4 +253,3 @@ export interface PluginStateEntry {
253
253
  state: "healthy" | "degraded";
254
254
  };
255
255
  }
256
- //# sourceMappingURL=manifest.d.ts.map
package/dist/manifest.js CHANGED
@@ -6,4 +6,3 @@
6
6
  * (`hostApiVersion`, `env[]`).
7
7
  */
8
8
  export {};
9
- //# sourceMappingURL=manifest.js.map
@@ -8,6 +8,7 @@
8
8
  * to round-trip. RENAMING an `id` here orphans every stored layout that references it, the same as
9
9
  * 1.x's own comment warns; adding/reordering rows is safe.
10
10
  */
11
+ /** MAY GROW: keep a default branch. */
11
12
  export type OverviewOwner = "overview" | "metrics-claude" | "repos" | "artifacts" | "provider-github";
12
13
  export interface OverviewWidget {
13
14
  id: string;
@@ -18,4 +19,3 @@ export interface OverviewWidget {
18
19
  export declare const OV_WIDGETS: readonly OverviewWidget[];
19
20
  /** The registry id `HomeCards` actually renders for one `OV_WIDGETS` row: `plugin:<owner>:<id>`. */
20
21
  export declare function ovRegistryId(widget: OverviewWidget): string;
21
- //# sourceMappingURL=ov-widgets.d.ts.map
@@ -30,4 +30,3 @@ export const OV_WIDGETS = [
30
30
  export function ovRegistryId(widget) {
31
31
  return `plugin:${widget.owner}:${widget.id}`;
32
32
  }
33
- //# sourceMappingURL=ov-widgets.js.map
package/dist/slots.d.ts CHANGED
@@ -214,4 +214,3 @@ export interface NotifyProvider {
214
214
  signal?: AbortSignal;
215
215
  }): Promise<void>;
216
216
  }
217
- //# sourceMappingURL=slots.d.ts.map
package/dist/slots.js CHANGED
@@ -14,4 +14,3 @@
14
14
  * built later in E5), so completing the v1 shape here is not a breaking change.
15
15
  */
16
16
  export {};
17
- //# sourceMappingURL=slots.js.map
package/dist/version.d.ts CHANGED
@@ -28,4 +28,3 @@ export declare const SLOTS: {
28
28
  readonly version: 1;
29
29
  };
30
30
  };
31
- //# sourceMappingURL=version.d.ts.map
package/dist/version.js CHANGED
@@ -16,4 +16,3 @@ export const SLOTS = {
16
16
  auth: { name: "auth", version: 1 },
17
17
  notify: { name: "notify", version: 1 },
18
18
  };
19
- //# sourceMappingURL=version.js.map
package/dist/views.d.ts CHANGED
@@ -142,7 +142,8 @@ export interface DeclarativeCardDef {
142
142
  }
143
143
  /** What started a task: a manually-triggered job (#31), an agent run (E4), or a sidecar run (E4/E6). */
144
144
  export type TaskKind = "job" | "agent" | "sidecar";
145
- /** `queued` (#51): an agent-job task waiting on an unreachable machine, badged red in the drawer. */
145
+ /** `queued` (#51): an agent-job task waiting on an unreachable machine, badged red in the drawer.
146
+ * MAY GROW: keep a default branch. */
146
147
  export type TaskStatus = "running" | "done" | "failed" | "queued";
147
148
  /**
148
149
  * The `GET /api/tasks` list item (#42) — the host's in-memory task registry's view of one run. Not a
@@ -180,9 +181,11 @@ export interface JobMachineRun {
180
181
  * plugin every capability the job declares (#843; reason `not granted: <capability>[, ...]`, the
181
182
  * missing ones in declared order), or its paired agent version compares lower than the job's
182
183
  * declared `minAgentVersion` (#372, #80; reason `agent <version> < <min>`). A missing grant wins
183
- * when both hold. Computed live every call from the machine's CURRENT grants and version, never
184
- * persisted, so it always reflects "would a dispatch right now skip this machine", not a stale
185
- * historical fact. Independent of `lastRun`/`queued`. Either can be set from before the skip
184
+ * when both hold. Computed on every read, never persisted: the grant half from the machine's
185
+ * CURRENT grants, the version half from the LAST RECORDED agent version (what the last probe, ping,
186
+ * preflight or update poll stored). A dispatch re-probes the agent and judges the floor on the version
187
+ * that probe just reported, so after an upgrade or downgrade this field can differ from what a dispatch
188
+ * right now would do until the stored version is refreshed. Independent of `lastRun`/`queued`. Either can be set from before the skip
186
189
  * applied, and a `queued` row with a version cause can also be written WHILE the skip applies: an
187
190
  * unreachable machine is queued before any floor is judged, since the floor is judged only on a
188
191
  * version a probe just reported. A grant skip is judged before the unreachable branch, so it never
@@ -209,7 +212,8 @@ export interface DashLayout {
209
212
  /**
210
213
  * `GET /api/agents` (#48): one paired machine, as the console's `machines` table sees it. Never
211
214
  * carries the bearer token the console sends it (`packages/host/src/agents/store.ts`'s `list`/`get`
212
- * don't select it) -- the row is safe to render straight into the Agents panel.
215
+ * read the whole row, but `toView` is the allow-list that builds this shape and leaves the token out)
216
+ * -- the row is safe to render straight into the Agents panel.
213
217
  */
214
218
  export interface MachineView {
215
219
  id: string;
@@ -237,7 +241,10 @@ export interface MachineView {
237
241
  * The outcome of `HostApi.agent.call` (#50). `kind` tells a plugin which refusals are its own to
238
242
  * show verbatim (`grant`: the agent's 403 text) from ones that are the console's problem to
239
243
  * describe differently (`unknown-machine`; `refused`: not a verb the service will forward; `pin`:
240
- * the agent's certificate no longer matches; `network`; `http`: an unexpected status).
244
+ * the agent's certificate no longer matches; `network`; `http`: an unexpected status). `ok: true` means
245
+ * the agent answered a 2xx and its body was read (#778): a 2xx whose body could not be read (a timeout
246
+ * or reset mid-body, a body that is not JSON) is `ok: false` with `kind: "http"` and that 2xx `status`,
247
+ * since the agent may have acted on the call. A `204` is `ok: true` with `body` undefined.
241
248
  */
242
249
  export type AgentCallOutcome<T> = {
243
250
  ok: true;
@@ -246,6 +253,7 @@ export type AgentCallOutcome<T> = {
246
253
  } | {
247
254
  ok: false;
248
255
  error: {
256
+ /** MAY GROW: a new kind is an additive change, so keep a default branch. */
249
257
  kind: "unknown-machine" | "refused" | "grant" | "pin" | "network" | "http";
250
258
  status?: number;
251
259
  reason: string;
@@ -260,7 +268,7 @@ export type AgentCallOutcome<T> = {
260
268
  * reinstall. `unauthenticated` is `gh`-only: present and running, but `gh auth status` failed.
261
269
  * Independently defined here from the agent's own (identically-shaped) internal type -- the host
262
270
  * never depends on `@rackbops/ac-agent` at runtime, only as a devDependency for its integration
263
- * tests, matching `grants.ts`'s own id/capability pattern.
271
+ * tests, matching `grants.ts`'s own id/capability pattern. MAY GROW: keep a default branch.
264
272
  */
265
273
  export type PreflightStatus = "ok" | "missing" | "broken" | "off-path" | "unauthenticated";
266
274
  export interface PreflightRow {
@@ -279,4 +287,3 @@ export interface PreflightReport {
279
287
  searchPath: "registry" | "process";
280
288
  rows: PreflightRow[];
281
289
  }
282
- //# sourceMappingURL=views.d.ts.map
package/dist/views.js CHANGED
@@ -1,2 +1 @@
1
1
  export {};
2
- //# sourceMappingURL=views.js.map
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "@rackbops/ac-plugin-contract",
3
- "version": "2.0.0-alpha.57",
3
+ "version": "2.0.0-alpha.59",
4
4
  "private": false,
5
+ "license": "Apache-2.0",
5
6
  "type": "module",
6
7
  "description": "The artifact-console plugin ABI for out-of-repo plugin authors: HostApi, the acPlugin manifest contribution points, the generated manifest JSON Schema, and the @ac/host ambient browser declaration.",
7
8
  "repository": {