@passioncode-ai/fabric-agent-adapter 0.49.3 → 0.51.1

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.
Files changed (70) hide show
  1. package/CHANGELOG.md +108 -0
  2. package/README.md +7 -2
  3. package/package.json +1 -1
  4. package/plugins/fabric-agent-adapter/.claude-plugin/plugin.json +1 -1
  5. package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/SKILL.md +29 -11
  6. package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/references/dashboard-links.md +58 -5
  7. package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/references/operator-channel-and-repository.md +11 -3
  8. package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/adapt_project.py +2 -2
  9. package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/check_dashboard_link.py +8 -6
  10. package/plugins/fabric-agent-adapter/skills/building-fabric-services/SKILL.md +50 -12
  11. package/plugins/fabric-agent-adapter/skills/building-fabric-services/assets/dashboard-kit/fabric-button.js +5 -4
  12. package/plugins/fabric-agent-adapter/skills/building-fabric-services/assets/dashboard-kit/fabric-dashboard.css +1 -1
  13. package/plugins/fabric-agent-adapter/skills/building-fabric-services/assets/dashboard-kit/fabric-dashboard.js +2 -2
  14. package/plugins/fabric-agent-adapter/skills/building-fabric-services/assets/dashboard-kit/kit.json +9 -4
  15. package/plugins/fabric-agent-adapter/skills/building-fabric-services/recipes/support-agent/README.md +5 -3
  16. package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/board.md +3 -1
  17. package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/dashboard-kit.md +3 -2
  18. package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/dashboard-links.md +58 -5
  19. package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/dashboard.md +13 -4
  20. package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/delegation.md +13 -1
  21. package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/estate-record.md +60 -0
  22. package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/interop.md +159 -2
  23. package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/kit-modules.md +8 -2
  24. package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/package.md +4 -2
  25. package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/protocol.md +14 -2
  26. package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/settings-backups.md +2 -1
  27. package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/surfaces-and-auth.md +40 -3
  28. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/check_dashboard_link.py +8 -6
  29. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/check_service.py +67 -2
  30. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/check_service_rules.py +371 -2
  31. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-a2a.mjs +170 -0
  32. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-actions.mjs +275 -0
  33. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-backups.mjs +3 -1
  34. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-context.mjs +151 -0
  35. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-delegation.mjs +38 -8
  36. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-descriptor.mjs +31 -2
  37. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-estate.mjs +6 -0
  38. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-interop-rules.mjs +68 -3
  39. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-interop.mjs +31 -1
  40. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-mandate.mjs +247 -0
  41. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-page-links.mjs +144 -0
  42. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-paths.mjs +22 -1
  43. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-protocols.mjs +2 -2
  44. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-service.mjs +1 -0
  45. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-share.mjs +30 -2
  46. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-supervisors.mjs +5 -4
  47. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_a2a.py +209 -0
  48. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_actions.py +340 -0
  49. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_backups.py +2 -1
  50. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_context.py +170 -0
  51. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_delegation.py +36 -12
  52. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_descriptor.py +36 -3
  53. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_estate.py +7 -0
  54. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_interop.py +242 -11
  55. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_interop_rules.py +70 -2
  56. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_mandate.py +324 -0
  57. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_page_links.py +559 -0
  58. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_paths.py +21 -0
  59. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_protocols.py +2 -2
  60. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_service.py +9 -0
  61. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_share.py +35 -0
  62. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_supervisors.py +6 -4
  63. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/kit.json +413 -25
  64. package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/SKILL.md +29 -11
  65. package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/dashboard-links.md +58 -5
  66. package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/operator-channel-and-repository.md +11 -3
  67. package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/scripts/check_dashboard_link.py +8 -6
  68. package/plugins/fabric-agent-adapter/skills/working-with-fabric/SKILL.md +87 -10
  69. package/plugins/fabric-agent-adapter/skills/working-with-fabric/references/project-board.md +12 -0
  70. package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session_env.py +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,113 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.51.1 (2026-10-26)
4
+
5
+ The tag `v0.51.0` was never published: its release run stopped at the Node 18 floor, so 0.51.1 is the first npm
6
+ release carrying everything listed under 0.51.0 below.
7
+
8
+ ### Fixed
9
+
10
+ - The tests of one instance per context (`test/fabric-context.test.mjs`, FAA-125) skip on Linux under a Node that
11
+ cannot bind an abstract socket name exactly (Node 18 and 20), as every other lock test does since FAA-72. The kit
12
+ itself already refused there with a sentence; only the tests lacked the guard.
13
+ - `references/package.md` no longer says the passkey countersignature waits for `org-authority/1`: DEC-0129 decided
14
+ that protocol only as the organization's member list for relay admission, and the countersignature waits for the
15
+ decision that defines the `org-countersign/1` verifier (contract finding on board thread `6c50c84f`).
16
+
17
+ ## 0.51.0 (2026-10-26)
18
+
19
+ ### Changed
20
+
21
+ - **Pinned to the contract release `v0.1.0+2026-10-26.2` (`e6c4fc16`)** (FAA-128). It carries DEC-0122 to DEC-0125;
22
+ `a3a66eb0` and `52c7d798` stay supported. Brief: `docs/evidence/specs/2026-10-26-faa-128-125-repin-a3a66eb-brief.md`.
23
+ - **Narrowings (DEC-0123, FAA-132).** `url_request` / `urlRequest` now refuses a loopback page URL (`http://127.0.0.1`
24
+ and every other spelling of a local address): hand a service's page over as Fabric Dashboards' `open_link`
25
+ (`page_answer` / `pageAnswer` builds it). `check_dashboard_link.py` refuses the legacy `open?service=` link and a
26
+ page path with whitespace, as the contract's `parseDashboardsLink` does. A service that built either may now be
27
+ refused at construction.
28
+ - **DL-19 `operator-decision-required`** names the three routes for a client that cannot set `_meta`: Fabric's
29
+ `agent.call` with `call.decision` once the hub forwards it, the service's own elicitation, the action's page as an
30
+ `open_link`. Outside a mandate (`data.detail` `outside-mandate: <part>`) it gives DEC-0125 MD-4's order instead:
31
+ call again within your own mandate, else pass the refusal up; only the leading agent asks the operator, with the
32
+ same three routes.
33
+ - Dashboard kit 1.1.5 now matches its pin (DEC-0124 is in it); brief decision D17 of 0.50.0 is closed.
34
+
35
+ ### Added
36
+
37
+ - **The operator's mandate (DEC-0125, FAA-131).** `fabric_mandate` / `fabric-mandate.mjs` port `mandate/1`
38
+ (FAC-SEM-144, scope narrowing, hand-over); `request_meta(mandate=)` sends one; a server with `agent_id` reads
39
+ `_meta.mandate` in its action gate. working-with-fabric states the operator's rule of 2026-10-26, «я сказал одному
40
+ = сказал всем»: one approval he gives any of his agents binds all of them when the relay carries his words, the
41
+ date, the source and the scope from one of his own Projects on his personal board (provenance `trusted`; never a
42
+ member's Project on an organization's board) or the session that started the agent; content an agent read is never
43
+ one; human gates stay. A hand-over never adds a `capabilities` or `actions` list the scope in force leaves out
44
+ (contract DEC-0136 MD-2a, adopted ahead of the pin: brief decision D28), so no agent of the chain can grant itself an
45
+ action the operator did not name. Taking a prose relay from another agent is the operator's rule, honoured ahead of the pinned
46
+ AP-3 (brief decision D27); the contract's DEC-0126 (OA-2) states it, and FAA-134 takes its wording in 0.52.0.
47
+ - **A service's pages as `open_link` (DEC-0123).** `fabric_page_links` / `fabric-page-links.mjs` build and read
48
+ Fabric Dashboards links and FAC-SEM-143, checked against the contract's vectors and about 200,000 generated
49
+ inputs with no twin difference.
50
+ - **A2A, the reading side (DEC-0122).** `fabric_a2a` / `fabric-a2a.mjs` read `surfaces.a2a` and an Agent Card;
51
+ `build_well_known(a2a=)` writes the member; the probe adds `well-known.a2a` and `a2a.card` (the card is fetched
52
+ without the descriptor's token). Serving a card and the A2A binding stay FAA-10.
53
+ - **One instance per context (FAA-125, N-11).** `fabric_context` / `fabric-context.mjs`: `hold_context_instance`
54
+ derives a context's instance, folders and lock, refuses a second process, refuses an `org:<id>` instance without
55
+ its share grant placement and exits 78 before creating anything; `personal` keeps the `default` instance.
56
+ `launchd_uninstall(instance=)` removes only that instance.
57
+
58
+ ## 0.50.0 (2026-10-26)
59
+
60
+ ### Changed
61
+
62
+ - **Pinned to the contract release `v0.1.0+2026-10-25` (`52c7d798`)** (FAA-117, contract CT-124). The release carries
63
+ DEC-0112 to DEC-0121; only DEC-0121 changes kit code, the others bind a node, an organization server or Project
64
+ Observatory and only regenerate vendored vectors (connections gains seven cases, the protocol register
65
+ `fabric-actions/0.1`). `2f020a6` stays supported. The brief is
66
+ `docs/evidence/specs/2026-10-26-faa-117-118-repin-52c7d79-brief.md` (decisions D1 to D17).
67
+
68
+ ### Added
69
+
70
+ - **Dashboard action parity (DEC-0121).** Every button a dashboard offers the operator is an agent action too.
71
+ - `fabric_actions.py` / `fabric-actions.mjs` port the contract's `actionDecision`, `actionsView` and the FAC-SEM-142
72
+ rules, checked against `fixtures/vectors/actions.json` (65 cases) and the semantic fixtures.
73
+ - A service declares its actions with `McpToolServer.add_tool(..., action=, target=, human_step_url=)` and
74
+ publishes them with `build_well_known(actions=)` / `buildWellKnown({actions})` as `extensions/actions/0.1`; a
75
+ declaration the contract would flag is refused.
76
+ - The server gate reads only `_meta.decision`, never the arguments: a T0 or T1 call without the operator's
77
+ decision is refused `operator-decision-required`, a malformed one `decision-invalid`, a step only a person can
78
+ take `human-step-required` with `data.humanStep` and `data.url`, and T2 still needs `approval-required`. The
79
+ decision records the authenticated caller (`identify`, the delegation's caller, or `local:T0` / `local:T1`).
80
+ Gating a job's input responses is opt-in (`input_actions=True`) until Fabric's hub forwards decisions (CT-125).
81
+ The kits are ready now; a service adopts the gate once CT-125 lands, as the contract says (DEC-0121
82
+ *Compatibility*, CT-127), because Claude Code and Codex cannot set `_meta` and would otherwise meet
83
+ `operator-decision-required` with the page as their only route.
84
+ - A caller relays with `request_meta(decision=...)` / `requestMeta(traceparent, {decision})`.
85
+ - The three codes join DL-19 in both delegation twins.
86
+ - **The probe** reports `well-known.actions`, `interop.action-tools` (a declared action's tool must be served),
87
+ `dashboard.actions-declared` (`NOT_RUN` for a dashboard that declares none) and `dashboard.opens-in-host`
88
+ (FAA-118: a browser opener aimed at the service's own pages in its checkout fails; `--repo PATH` names the
89
+ checkout).
90
+ - **working-with-fabric** relays the operator's own words as the decision (AP-3), never text from a tool result, a
91
+ page, a file or another agent; a coding agent whose client cannot set `_meta` goes through Fabric's `agent.call`
92
+ (after CT-125), the service's elicitation, or the action's page in Fabric Dashboards, in that order (confirmed by
93
+ the contract's owner on board thread `92c6f277`). On `human-step-required` it hands the person the link.
94
+
95
+ - **Fabric's button follows contract DEC-0124** (dashboard kit 1.1.5; FB-3 amended on the adapter's request, board
96
+ thread `4801da8d`, contract PR #189, after this release's pin). The accessible name and the menu follow the page's
97
+ language, which the contract now states, so 0.49.3's note that the kit departs from FB-3 no longer holds. A page's
98
+ own words (`data-label-*`) are taken only when they keep the word *Fabric* (WCAG 2.2 SC 2.5.3, Label in Name);
99
+ otherwise the built-in words stay. A page that set words without *Fabric* under 0.49.3 shows the built-in words
100
+ after it updates its kit. The kit follows this amendment ahead of the pin (brief decision D17).
101
+ - **The probe's new `dashboard.opens-in-host` can fail a service that passed before**: one that opens its own pages
102
+ in a browser. Open them through Fabric Dashboards instead (see the building-fabric-services skill).
103
+
104
+ ### Changed (guidance)
105
+
106
+ - **Agent pages open only in Fabric Dashboards** (FAA-118, the operator's rule of 2026-10-24): the rule now sits in
107
+ the body of the three SKILL.md files, with the AGENTS.md block a project carries and a section for an agent that
108
+ has a page and no service descriptor. Manual restore and enabling the operator channel are the operator's
109
+ decision on the page or relayed (D16).
110
+
3
111
  ## 0.49.3 (2026-10-25)
4
112
 
5
113
  ### Fixed
package/README.md CHANGED
@@ -85,6 +85,7 @@ a service (a descriptor names it as `source.repository`) whose dashboard has no
85
85
  anywhere else; the [kit reference](plugins/fabric-agent-adapter/skills/building-fabric-services/references/dashboard-kit.md)
86
86
  has the upgrade steps, which the agent may take itself.
87
87
 
88
+ Agent pages open only in Fabric Dashboards (the operator's rule, carried in the builder skills since 0.50.0).
88
89
  Dashboard handoffs use the host-generated `open_link` as the primary action. The
89
90
  three builder skills carry the same [consumer procedure](plugins/fabric-agent-adapter/skills/building-fabric-services/references/dashboard-links.md)
90
91
  and a portable `scripts/check_dashboard_link.py` gate for generated renderers.
@@ -98,7 +99,11 @@ is the MCP tool of its name, long work is a job with a stable id (`fabric.job.ge
98
99
  `fabric.job.cancel`), a question for a person is an elicitation, and every call carries
99
100
  one W3C trace. The kits ship the helpers (`scripts/fabric_interop.py`,
100
101
  `scripts/fabric-interop.mjs`), the sample service serves a job end to end, and the probe
101
- checks the interop rules. A job a provider limit paused or stopped, and an effect nobody saw, carry their own codes
102
+ checks the interop rules. Every dashboard button has a tool (`fabric-actions/0.1`, contract DEC-0121): the
103
+ operator's agent relays their decision in `_meta.decision` (a service adopts the gate once Fabric's hub forwards
104
+ decisions, CT-125; Claude Code and Codex, which cannot set `_meta`, relay through Fabric's `agent.call`, the service's
105
+ own elicitation or the action's page), and the refusals `operator-decision-required`,
106
+ `decision-invalid` and `human-step-required` say what is missing. A job a provider limit paused or stopped, and an effect nobody saw, carry their own codes
102
107
  (`quota-exhausted`, `outcome-unknown`) and keys per caller (contract DEC-0087). The estate kits check share grants into
103
108
  an organization (`scripts/fabric_share.py`, DEC-0086) and the estate ceremonies: a revision's record rules, a headless
104
109
  machine's enroll token, the nearby pairing by a comparison code and the hub's single use of a token
@@ -275,7 +280,7 @@ Scaffolding is non-destructive by default. It writes only the paths declared in
275
280
  The default pin for new bundles is [`fabric-contract.lock.json`](fabric-contract.lock.json):
276
281
 
277
282
  - version: `0.1.0`
278
- - commit: `2f020a696453ae299b123517f14957f6a1354379`
283
+ - commit: `e6c4fc16b415eec3190dd34d924d269405359dc9`
279
284
 
280
285
  Every other mention of the default contract revision (this section, the skills' metadata, the
281
286
  skill card, `adapt_project.py`) must equal it; `python3 test/validate.py` fails a tree
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@passioncode-ai/fabric-agent-adapter",
3
- "version": "0.49.3",
3
+ "version": "0.51.1",
4
4
  "description": "Create Fabric-compatible agents, run them as always-alive local services with dashboards (fabric-service/0.1), and adapt existing projects to the Fabric Agent Contract. This package is the installer CLI.",
5
5
  "scripts": {
6
6
  "test": "python3 test/validate.py && python3 -m unittest discover -s test -q && node --test 'test/*.test.mjs'"
@@ -3,7 +3,7 @@
3
3
  "name": "fabric-agent-adapter",
4
4
  "displayName": "Fabric Agent Adapter",
5
5
  "description": "Create new Fabric-compatible agents, build them as always-alive local services with dashboards (fabric-service/0.1), and adapt existing projects to the Fabric Agent Contract through evidence-backed MCP, A2A, or local-runner provider bundles; and teach a coding agent to work in a project through Fabric (fabric-mcp, fabric_whoami, claim and release), with Claude Code session hooks.",
6
- "version": "0.49.3",
6
+ "version": "0.51.1",
7
7
  "author": {
8
8
  "name": "PassionCode.ai",
9
9
  "url": "https://passioncode.ai/"
@@ -5,9 +5,9 @@ license: AGPL-3.0-only OR LicenseRef-PassionCode-Commercial
5
5
  compatibility: Requires filesystem access and Python 3.9+. Exact schema checks additionally need git, Node.js, pnpm, and the pinned fabric-agent-contract checkout (a public repository). Works without those tools in an explicitly degraded structural-check mode.
6
6
  metadata:
7
7
  author: PassionCode.ai
8
- version: "0.49.3"
8
+ version: "0.51.1"
9
9
  contract-version: "0.1.0"
10
- contract-commit: "2f020a696453ae299b123517f14957f6a1354379"
10
+ contract-commit: "e6c4fc16b415eec3190dd34d924d269405359dc9"
11
11
  ---
12
12
 
13
13
  # Adapting projects to Fabric
@@ -76,7 +76,7 @@ Use exactly:
76
76
 
77
77
  - contract version `0.1.0`;
78
78
  - repository `https://github.com/passioncode-ai/fabric-agent-contract`;
79
- - commit `2f020a696453ae299b123517f14957f6a1354379`.
79
+ - commit `e6c4fc16b415eec3190dd34d924d269405359dc9`.
80
80
 
81
81
  This is the default for new bundles. When checking an issued legacy bundle, preserve its
82
82
  explicit supported lock and use that exact clean checkout. Never silently upgrade or
@@ -218,9 +218,9 @@ not a connected provider.
218
218
  If the agent has a human stop (a choice, an approval, a confirmation) or emits `notify: true`
219
219
  events, propose its operator channel (`fabric-operator-channel/0.1`, OC-11) to the operator once,
220
220
  now, as [the operator channel and the repository standard](references/operator-channel-and-repository.md)
221
- describes: the operator's four steps, the answer recorded and never asked again, and the bot, the
222
- token and enabling the channel left to the operator. An agent with neither stops nor notify events
223
- skips this step.
221
+ describes: the operator's four steps, the answer recorded and never asked again, the bot and the
222
+ token left to the person, and enabling the channel to the operator's decision (on the page or
223
+ relayed, DEC-0121). An agent with neither stops nor notify events skips this step.
224
224
 
225
225
  ## The repository around the provider
226
226
 
@@ -245,8 +245,26 @@ Report:
245
245
  Never summarize the outcome as “Fabric-compatible” unless all required admission gates
246
246
  passed against a real provider and the project binding is valid.
247
247
 
248
- ## Dashboard handoff: also for consumers
249
-
250
- When handing a registered service dashboard to a person, or writing the generated agent's
251
- completion or notification instructions, read and apply [dashboard links](references/dashboard-links.md)
252
- and run its output gate before delivery, in any host. Headless agents need no dashboard.
248
+ ## Agent pages open only in Fabric Dashboards
249
+
250
+ The operator's rule (2026-10-24) for every page of an agent or a local Fabric service, in any host: a
251
+ link in an answer or a notification, `<tool> dashboard`, a completion message, a page the agent opens
252
+ itself. Hand over the `open_link` the `fabric-dashboards` MCP's `link` tool returns for the exact
253
+ registered service key (`list_services`, never guessed) and the page's path; open it only with the
254
+ host's `open` and `fallback=never`. Never `open <url>`, `open -a Safari`, `xdg-open`,
255
+ `webbrowser.open` or `shell.openExternal` aimed at the agent's pages. A browser only on a confirmed
256
+ `not_installed` host; an installed host that failed to open is reported, never replaced by HTTP.
257
+ Links carry no token or login code.
258
+
259
+ The agent's `AGENTS.md` carries this as its dashboard rule block, and its checks run the adapter's link gate
260
+ (`scripts/check_dashboard_link.py` from this skill's own directory; it is not copied into the project) on every
261
+ completion and notification action before delivery:
262
+ [dashboard links](references/dashboard-links.md) has the block, the gate's input and the remote case.
263
+ Headless agents need no dashboard.
264
+
265
+ ## An agent with a page and no service descriptor
266
+
267
+ A local page no service descriptor names cannot open in Fabric Dashboards. Never open it in a browser
268
+ instead: make the agent a `fabric-service/0.1` service with `building-fabric-services` (the descriptor,
269
+ the well-known document, the login-code routes and a declared dashboard), and until that lands file a
270
+ board row for it and say the page waits on it.
@@ -5,6 +5,19 @@ an existing Fabric service dashboard to a person. This also applies to generated
5
5
  agent instructions and completion messages. Headless agents need no dashboard;
6
6
  API origins, external docs and OAuth links are not dashboard actions.
7
7
 
8
+ The rule (the operator's, 2026-10-24): every page of an agent or a local Fabric
9
+ service opens only in Fabric Dashboards, never in Safari, Dia, Chrome or any
10
+ other browser, unless Fabric Dashboards is confirmed not installed.
11
+
12
+ ## Contents
13
+
14
+ - Resolve, then hand over
15
+ - The block a generated AGENTS.md carries
16
+ - An agent with a page and no service descriptor
17
+ - Device and channel
18
+ - Portable output gate
19
+ - Degradation
20
+
8
21
  ## Resolve, then hand over
9
22
 
10
23
  1. Discover the target computer and exact registered `service.id.instance`.
@@ -20,9 +33,12 @@ API origins, external docs and OAuth links are not dashboard actions.
20
33
  compatible host resolver or report the limitation, not presumed absence.
21
34
  4. A request for a link opens nothing. A request to open uses the host `open`
22
35
  tool/approved opener, which starts or focuses the existing Fabric Dashboards
23
- and reuses the service view. `fallback=never` forbids a browser; `if_absent`
24
- permits one only for confirmed absence. Discover schema support first: older
25
- `open` implementations may silently fall back on failure and are not strict.
36
+ and reuses the service view, with `fallback=never`: it forbids a browser.
37
+ `if_absent` permits one only for confirmed absence. Discover schema support
38
+ first: older `open` implementations may silently fall back on failure and are
39
+ not strict. Never
40
+ `open <url>`, `open -a Safari`, `xdg-open`, `start <url>`, `webbrowser.open`
41
+ or `shell.openExternal` aimed at the service's pages.
26
42
  5. An installed host that failed to open is **not absent**. Report the failure;
27
43
  do not silently launch HTTP in a browser, a second server, `npm run dev`, or
28
44
  another standalone dashboard. A stopped backend stays on its host status/Start
@@ -31,6 +47,40 @@ API origins, external docs and OAuth links are not dashboard actions.
31
47
  not page readiness, a successful login, or a completed job. Links carry no
32
48
  bearer tokens or login codes. The host handles dashboard authentication.
33
49
 
50
+ ## The block a generated AGENTS.md carries
51
+
52
+ An agent with a page carries this block in its `AGENTS.md` (its `CLAUDE.md` reads
53
+ it through `@AGENTS.md`), beside its browser rule, and its completion and
54
+ notification renderers follow it:
55
+
56
+ ```markdown
57
+ ## Pages open only in Fabric Dashboards
58
+
59
+ Every page of this agent (its dashboard, a job, an approval, a report) opens in
60
+ Fabric Dashboards, never in a browser. Resolve the registered service key with
61
+ the fabric-dashboards MCP (`list_services`, never guessed) and hand over the
62
+ `open_link` its `link` tool returns for the page's path; to open it, call the
63
+ host's `open` with `fallback=never`. Never `open <url>`, `open -a Safari`,
64
+ `xdg-open`, `start <url>`, `webbrowser.open` or `shell.openExternal` on this
65
+ agent's pages. A browser only when `host_status` confirms `not_installed`; an
66
+ installed host that failed to open is reported, not replaced by HTTP. Links
67
+ carry no token or login code. Every completion and notification action passes
68
+ the Fabric Agent Adapter's link gate before delivery: `check_dashboard_link.py`
69
+ in its `building-fabric-services/scripts`, run from that skill's directory;
70
+ where it cannot run, the action is compared by hand and the check is NOT_RUN.
71
+ ```
72
+
73
+ The project's checks run the gate below on each renderer's action, and a test
74
+ holds a raw-HTTP primary action beside an available host refused.
75
+
76
+ ## An agent with a page and no service descriptor
77
+
78
+ Fabric Dashboards opens only a page a service descriptor names. An agent that
79
+ serves a local page without one cannot be opened there, and a browser is not the
80
+ workaround. Make it a `fabric-service/0.1` service (`building-fabric-services`:
81
+ the descriptor, the well-known document, the login-code routes and a declared
82
+ dashboard); until that lands, file a board row for it and say the page waits on it.
83
+
34
84
  ## Device and channel
35
85
 
36
86
  A custom scheme opens on the device receiving it. A phone's localhost is not the
@@ -90,5 +140,8 @@ or instructions alone do not prove all final answers from every agent are filter
90
140
 
91
141
  Syntax is owned by Fabric Dashboards and `@passioncode-ai/fabric-service-host`:
92
142
  [ADR-0005](https://github.com/passioncode-ai/fabric-dashboards/blob/51a7a80acf42781d7bec304cc87c655d48cc309b/docs/adr/0005-service-links.md).
93
- The gate accepts canonical links and the legacy `open?service=` form returned by
94
- older hosts; it does not generate either dialect or redefine the Fabric wire contract.
143
+ The gate accepts only the service form, `fabric-dashboards://service/SERVICE_ID.INSTANCE`
144
+ with at most one `path`; since 0.51.0 it refuses the legacy `open?service=` form, as contract
145
+ DEC-0123 (PL-1, checker rule `FAC-SEM-143`) and its vectors do. A host that still returns the
146
+ legacy form fails the gate: report it as an installed host that did not open the page. The gate
147
+ does not generate links or redefine the Fabric wire contract.
@@ -26,9 +26,12 @@ bare `/command` in a group is not addressed), and a channel nobody receives on i
26
26
 
27
27
  Record the answer (`accepted`, `declined` or `later`) in the agent's state and in its conformance
28
28
  report, and do not ask again; an agent that serves `surfaces.operatorChannel` also shows it as
29
- `proposal` in its status document. Creating the bot, storing the token and enabling the channel
30
- are the operator's action: never create the bot, ask for or store the token's value, or enable the
31
- channel yourself. The default pin carries DEC-0034 and DEC-0035: a served status document is
29
+ `proposal` in its status document. Creating the bot and putting its token into the secret store
30
+ stay the person's (a `secret` step, contract DEC-0121 AP-4): never create the bot, or ask for or
31
+ store the token's value. Enabling the channel, and removing a webhook found on its token, are the
32
+ operator's decision, taken by them on the agent's page or relayed to its agent in their own words
33
+ with `_meta.decision` (OC-1, OC-2, DEC-0121 AP-2); never enable it on your own account. The default
34
+ pin carries DEC-0034 and DEC-0035: a served status document is
32
35
  checked against the pinned `schemas/operator-channel-status.schema.json`; the channel's runtime
33
36
  rules have no probe rule here yet, so report them `NOT_RUN`. An agent with neither stops nor notify
34
37
  events skips this.
@@ -49,6 +52,11 @@ For a PassionCode.ai repository, the agent's repository follows the organization
49
52
  - an `AGENTS.md` that opens with the template's *Read first* block and closes with *After work*,
50
53
  and a `CLAUDE.md` whose first line is `@AGENTS.md`.
51
54
 
55
+ Whoever owns the repository, an agent with a page gets the dashboard rule block in its `AGENTS.md`
56
+ (pages open only in Fabric Dashboards; the block is in `references/dashboard-links.md`), and its
57
+ checks run the adapter's link gate (`scripts/check_dashboard_link.py` from the skill's own directory, not a copy in
58
+ the project).
59
+
52
60
  org-index `scripts/check_format.py` reports 0 findings for it before it is called done. An agent
53
61
  someone builds for themselves is not a PassionCode.ai repository: its licence is its owner's
54
62
  choice, nothing about it is published or listed by the organization, and none of these files is
@@ -22,10 +22,10 @@ import fabric_secrets as _secrets # noqa: E402 - the contract's credential dete
22
22
 
23
23
  CONTRACT_VERSION = "0.1.0"
24
24
  CONTRACT_REPOSITORY = "https://github.com/passioncode-ai/fabric-agent-contract"
25
- CONTRACT_COMMIT = "2f020a696453ae299b123517f14957f6a1354379"
25
+ CONTRACT_COMMIT = "e6c4fc16b415eec3190dd34d924d269405359dc9"
26
26
  # #region supported-contract-revisions — docs: README.md#contract-pin
27
27
  # Only this declaration may name a legacy revision in live files.
28
- SUPPORTED_CONTRACT_COMMITS = (CONTRACT_COMMIT, "87d384ffce2757081b7ab227e5dad3dddd08328f", "0a4ce773c4b9e6bec40c6387eb3f988af7152c0e", "e742016dc396aa31e5a13a893489e43f15786c69", "bfa9a67aa3c6cb808c093e7d83cd59799f1ee7d0", "c55f2d5a11c9a0197e958e87212fca7ae1154769", "07042181bfae2a481aa0da1a5651f24c76669c48", "d8146cfb0a1c74fc6432284c4356bb0c864b37b5", "82e163665e156a08aa3b92e4711015582c06162c", "1636fa97262f4a26e2086572d540c2ee701e0010", "f00a7685e3861c27f7899c033fb850a145a1708f", "a046f57cf46ea9e3da57ff8ce6a3ed4d26d8774b", "fe55fc67cba1a9e34a736da7d148d4fe2a8fbfa0", "b291bf09e0a9f9fd3c16cf540832cd021203b7d1", "0cacd08b0e95b6abf131eae1206b60792138bc3c", "2eb91c00e852d9c31d804310d38fad91589fcbac", "8e49fbd5fbbf230e32c1969cd24ac3c22422f0a5", "a83bec6b47776af688402c18efec507143061f68", "52da526d7cc1e063d74c5b520dbc03d0ace15246", "78b2da018d2c4bd9ac1bde1dc3b7f1a110a175f5", "623bf61358c339cb10297807b3f024b5d9f1f327", "23f9fda4c05f8a3852246ee98d4f2adf74ed0875", "9091d3d6606b0c4b5591a38c3674356a01fd7f55", "df55c8c54a23251342a7ee57ba95642b7eb39e61", "2ce392291c6668598d12cd38327e24696b5ca15c")
28
+ SUPPORTED_CONTRACT_COMMITS = (CONTRACT_COMMIT, "a3a66eb0052043847f6556348e37d34ca873a629", "52c7d798b9f4eb784eb4e29e717038a8912c759d", "2f020a696453ae299b123517f14957f6a1354379", "87d384ffce2757081b7ab227e5dad3dddd08328f", "0a4ce773c4b9e6bec40c6387eb3f988af7152c0e", "e742016dc396aa31e5a13a893489e43f15786c69", "bfa9a67aa3c6cb808c093e7d83cd59799f1ee7d0", "c55f2d5a11c9a0197e958e87212fca7ae1154769", "07042181bfae2a481aa0da1a5651f24c76669c48", "d8146cfb0a1c74fc6432284c4356bb0c864b37b5", "82e163665e156a08aa3b92e4711015582c06162c", "1636fa97262f4a26e2086572d540c2ee701e0010", "f00a7685e3861c27f7899c033fb850a145a1708f", "a046f57cf46ea9e3da57ff8ce6a3ed4d26d8774b", "fe55fc67cba1a9e34a736da7d148d4fe2a8fbfa0", "b291bf09e0a9f9fd3c16cf540832cd021203b7d1", "0cacd08b0e95b6abf131eae1206b60792138bc3c", "2eb91c00e852d9c31d804310d38fad91589fcbac", "8e49fbd5fbbf230e32c1969cd24ac3c22422f0a5", "a83bec6b47776af688402c18efec507143061f68", "52da526d7cc1e063d74c5b520dbc03d0ace15246", "78b2da018d2c4bd9ac1bde1dc3b7f1a110a175f5", "623bf61358c339cb10297807b3f024b5d9f1f327", "23f9fda4c05f8a3852246ee98d4f2adf74ed0875", "9091d3d6606b0c4b5591a38c3674356a01fd7f55", "df55c8c54a23251342a7ee57ba95642b7eb39e61", "2ce392291c6668598d12cd38327e24696b5ca15c")
29
29
  # #endregion supported-contract-revisions
30
30
  INTEROP_KEY = "https://fabric.passioncode.ai/agent-contract/extensions/interop/0.1"
31
31
  MCP_REVISION = "2026-07-28"
@@ -74,7 +74,9 @@ def login_path(path):
74
74
 
75
75
  def safe_path(path):
76
76
  return (isinstance(path, str) and path.startswith('/') and not path.startswith('//')
77
- and len(path) <= 2048 and not re.search(r'[\\\x00-\x1f\x7f]', path))
77
+ and len(path) <= 2048 and not re.search(r'[\\\x00-\x1f\x7f]', path)
78
+ # JavaScript's whitespace (\s), which the contract's parseDashboardsLink refuses in a page (DEC-0123)
79
+ and not re.search('[ \u00a0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]', path))
78
80
 
79
81
 
80
82
  def check(data):
@@ -87,11 +89,11 @@ def check(data):
87
89
  require(not re.search(r'[\\\x00-\x20\x7f]', native + http), 'unsafe_url')
88
90
  deep, plain = urlsplit(native), urlsplit(http)
89
91
  require(deep.scheme == 'fabric-dashboards' and not deep.username and not deep.password and not deep.port and not deep.fragment, 'invalid_deep_link')
90
- query = parse_qs(deep.query, keep_blank_values=True, strict_parsing=True)
91
- if deep.netloc == 'service':
92
- require(deep.path in ('/' + service, '/' + service + '/') and set(query) <= {'path'}, 'wrong_service_or_parameter')
93
- else:
94
- require(deep.netloc == 'open' and deep.path in ('', '/') and query.get('service') == [service] and set(query) <= {'service', 'path'}, 'invalid_legacy_link')
92
+ # An empty query is no fields: Python 3.9's strict parse_qs raises on '' where 3.11 and later answer {}.
93
+ query = parse_qs(deep.query, keep_blank_values=True, strict_parsing=True) if deep.query else {}
94
+ # DEC-0123 PL-1: only the service form; the legacy open?service= link is refused (FAC-SEM-143, since 0.51.0).
95
+ require(deep.netloc == 'service', 'not_service_link')
96
+ require(deep.path in ('/' + service, '/' + service + '/') and set(query) <= {'path'}, 'wrong_service_or_parameter')
95
97
  require('path' not in query or len(query['path']) == 1, 'duplicate_path')
96
98
  page = query.get('path', [None])[0]
97
99
  require(page is None or safe_path(page), 'unsafe_path')
@@ -14,10 +14,10 @@ license: AGPL-3.0-only OR LicenseRef-PassionCode-Commercial
14
14
  compatibility: Python 3.9+ or Node.js 20+ for the kits (Node.js 22+ for a Linux service's instance lock); the probe needs Python 3.9+. launchd steps are macOS-only; Linux services install a systemd user unit and Windows ones a per-user Scheduled Task with the kits' writers. Dashboard handoff optionally uses Fabric Dashboards MCP link/host_status/open; without it, report unresolved host capability. The contract checkout is optional.
15
15
  metadata:
16
16
  author: PassionCode.ai
17
- version: "0.49.3"
17
+ version: "0.51.1"
18
18
  contract-version: "0.1.0"
19
19
  extension: "fabric-service/0.1"
20
- extension-commit: "2f020a696453ae299b123517f14957f6a1354379"
20
+ extension-commit: "e6c4fc16b415eec3190dd34d924d269405359dc9"
21
21
  ---
22
22
 
23
23
  # Building Fabric services
@@ -70,9 +70,10 @@ Answer before writing code, and record the answers in the project README:
70
70
  | Caller | Surface | Why |
71
71
  |---|---|---|
72
72
  | Another agent on this machine | MCP `2026-07-28`, `streamable-http`, at `/mcp` on the service origin, token in a header | one process serves every session; stdio-per-session once left twenty stray servers on this machine |
73
- | The operator or a script | a CLI: `<tool> service status\|start\|stop\|restart`, `<tool> dashboard`, `<tool> doctor --json` | every verb delegates to launchd and the well-known document, never to its own process table |
73
+ | The operator or a script | a CLI: `<tool> service status\|start\|stop\|restart`, `<tool> dashboard` (opens only in Fabric Dashboards, below), `<tool> doctor --json` | every verb delegates to launchd and the well-known document, never to its own process table |
74
74
  | A remote agent | A2A `1.0` over HTTPS, or MCP behind an authenticated gateway | loopback reachability is not authorization |
75
- | The dashboard page | same-origin REST under `/api`, session cookie plus a custom request header | the CSRF defence all four existing dashboards use |
75
+ | An A2A peer on this computer | `surfaces.a2a` names a public Agent Card, served without the token; its interface URLs are loopback (DEC-0122); the kits read it, serving one is FAA-10 | the card carries no credential, process member or required Fabric extension |
76
+ | The dashboard page | same-origin REST under `/api`, session cookie plus a custom request header; every button on it also a tool on the MCP surface (Step 5) | the CSRF defence all four existing dashboards use |
76
77
 
77
78
  Read [the surfaces and auth reference](references/surfaces-and-auth.md) when wiring
78
79
  MCP registration into a client config, the login flow or the CSRF header.
@@ -248,6 +249,16 @@ and declare it with `extensions=fs.dashboard_extension()`. Read
248
249
  declaration and how to upgrade a dashboard built before the kit: an upgrade you may make yourself,
249
250
  which the plugin's session-start notice offers in a repository that owns the service.
250
251
 
252
+ **Every button has a tool** (`fabric-actions/0.1`, contract DEC-0121 AP-1). Each action the page lets a
253
+ person take is a tool on the MCP surface with the same effect, checks, audit record and an idempotency key,
254
+ registered with `server.add_tool(tool, handler, action={id, title, tool, gate})` (`gate` is `none`, `relayed`
255
+ or `human` with its `humanStep`) and declared with `build_well_known(actions=server.action_table())` under
256
+ `extensions/actions/0.1`. A `relayed` action called by the operator's own agent with their decision in
257
+ `_meta.decision` is the operator's act: never answer it "operator session required" or send them to the page.
258
+ A service adopts the relayed gate once Fabric's hub forwards decisions (CT-125), as the contract says; until then its tools
259
+ answer as today. Read the interop reference's *Actions* section for when to adopt, the gate, its hooks and its
260
+ refusals.
261
+
251
262
  **Say what the agent uses** (`fabric-connections/0.1`, DEC-0096) so Fabric Dashboards draws its chain: the other
252
263
  agents, models, connectors, stores and channels, each with its state, from
253
264
  `connections_extension()` / `connectionsExtension()` in `fabric_connections`. Never a credential in it, never a call
@@ -283,10 +294,15 @@ python3 <skill-dir>/scripts/check_service.py <id>[.<instance>]
283
294
  It reports `PASS`, `FAIL` or `NOT_RUN` per rule (descriptor, port claim, well-known
284
295
  shape, identity, latency, Host/Origin/cross-site guards, loopback bind, token file,
285
296
  events auth and shape, single-use login, state outside code, instance lock, plist,
286
- launchd pid equals answering pid, `dashboard.declared` (ED-9), and the `interop.*` rules:
297
+ launchd pid equals answering pid, `dashboard.declared` (ED-9), `well-known.actions` (DEC-0121),
298
+ `dashboard.actions-declared` (`NOT_RUN` when a dashboard declares no actions), `dashboard.opens-in-host` (no browser
299
+ opener aimed at the service's pages in its checkout; `--repo PATH` names the checkout, and a service installed
300
+ from a release folder needs it), and the `interop.*` rules:
287
301
  manifest link, capability list, tools equal to the manifest, job tools, unknown job, trace
288
- propagation, event trace pairs, and `board.responder` for a service that declares the project board) and exits 1 on
289
- any `FAIL`. A `NOT_RUN` is not a pass;
302
+ propagation, event trace pairs, `interop.action-tools` (every declared action's tool is served), `well-known.a2a` and
303
+ `a2a.card` (DEC-0122: the card is fetched without the token and its interfaces are never called; the surfaces
304
+ reference's *A2A for remote agents* section), and
305
+ `board.responder` for a service that declares the project board) and exits 1 on any `FAIL`. A `NOT_RUN` is not a pass;
290
306
  name it in the report. A service with a dashboard also runs the layout probe,
291
307
  `node <skill-dir>/scripts/check_dashboard_layout.mjs --service <id>` (WebKit at 360, 480, 768
292
308
  and 1280 px; needs Playwright, else `NOT_RUN` and exit 3: nothing was measured).
@@ -328,7 +344,8 @@ first commit:
328
344
  - an `AGENTS.md` that opens with the template's *Read first* block and closes with *After work*,
329
345
  and a `CLAUDE.md` whose first line is `@AGENTS.md`.
330
346
 
331
- org-index `scripts/check_format.py` reports 0 findings for it before it is called done. **An agent
347
+ Whoever owns the repository, an agent with a page gets the dashboard rule block in its `AGENTS.md`
348
+ (below). org-index `scripts/check_format.py` reports 0 findings for it before it is called done. **An agent
332
349
  someone builds for themselves is not a PassionCode.ai repository:** its licence is its owner's
333
350
  choice, nothing about it is published or listed by the organization, and none of these files is
334
351
  required of it, though the verified MCP quick start is still how anyone learns to drive it.
@@ -418,8 +435,29 @@ the probe table verbatim with every `NOT_RUN` explained; the lock and kill-9 res
418
435
  with pids; and the one next command. Never call a service conformant while the probe
419
436
  reports a `FAIL`.
420
437
 
421
- ## Dashboard handoff: also for consumers
438
+ ## Agent pages open only in Fabric Dashboards
439
+
440
+ The operator's rule (2026-10-24) for every page of an agent or a local Fabric service, in any host: a link in an
441
+ answer or a notification, `<tool> dashboard`, a completion message, a page the agent opens itself.
442
+
443
+ 1. Resolve the exact registered service key (`id.instance`) with the `fabric-dashboards` MCP (`list_services`);
444
+ never guess it from a project name or a port.
445
+ 2. Hand over the `open_link` its `link` tool returns for the service and the page's path, as the primary action.
446
+ 3. To open it, use the host's `open` with `fallback=never`. Never `open <url>`, `open -a Safari`, `xdg-open`,
447
+ `start <url>`, `webbrowser.open` or `shell.openExternal` aimed at the service's pages.
448
+ 4. A browser only on a confirmed `not_installed` host (`host_status`); an installed host that failed to open is
449
+ reported, never replaced by HTTP.
450
+ 5. Links carry no token or login code: the host signs in.
451
+
452
+ Run `scripts/check_dashboard_link.py` (from this skill's own directory; it is not copied into the project) on an
453
+ action before it is printed or opened, and have the project's checks run it for every completion and notification
454
+ renderer. The generated `AGENTS.md` carries the rule as its dashboard
455
+ rule block; [dashboard links](references/dashboard-links.md) has the block, the gate's input and the remote case. The
456
+ probe's `dashboard.opens-in-host` fails a tracked file that opens the dashboard with a browser opener. Headless agents
457
+ need no dashboard.
458
+
459
+ ## An agent with a page and no service descriptor
422
460
 
423
- When handing a registered service dashboard to a person, or writing the generated agent's
424
- completion or notification instructions, read and apply [dashboard links](references/dashboard-links.md)
425
- and run its output gate before delivery, in any host. Headless agents need no dashboard.
461
+ A local page no service descriptor names cannot open in Fabric Dashboards. Never open it in a browser instead:
462
+ make the agent a `fabric-service/0.1` service (this skill: the descriptor, the well-known document, the login-code
463
+ routes and a declared dashboard), and until that lands file a board row for it and say the page waits on it.
@@ -1,4 +1,4 @@
1
- /* fabric-dashboard-kit@1.1.4 · Fabric's button (Fabric Agent Contract DEC-0040, FB-1…FB-8, FL-1…FL-6)
1
+ /* fabric-dashboard-kit@1.1.5 · Fabric's button (Fabric Agent Contract DEC-0040, FB-1…FB-8, FL-1…FL-6)
2
2
  Load it same-origin, after fabric-dashboard.css, with the service it opens Fabric on:
3
3
  <script src="fabric-button.js" defer data-service="example-agent.default" data-service-name="Example Agent"
4
4
  data-project="project-a" data-project-name="Project A"></script>
@@ -11,7 +11,7 @@
11
11
  (function (root) {
12
12
  'use strict';
13
13
 
14
- var VERSION = '1.1.4';
14
+ var VERSION = '1.1.5';
15
15
  var COOKIE = 'fabric_button';
16
16
  var GET_FABRIC = 'https://passioncode.ai/fabric';
17
17
  var ABSENT_AFTER_MS = 1500;
@@ -165,13 +165,14 @@
165
165
  absent: 'Fabric \u043d\u0435 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d. \u0421\u043a\u0430\u0447\u0430\u0439\u0442\u0435 \u0435\u0433\u043e \u043d\u0430' }
166
166
  };
167
167
 
168
- /** The labels for a page language, each one replaced by a non-blank string the page gives (data-label-*). */
168
+ /** The labels for a page language, each one replaced by a non-blank string the page gives (data-label-*) that keeps
169
+ the word Fabric: the visible label stays inside the name and each item (DEC-0124, WCAG 2.2 SC 2.5.3). */
169
170
  function labelsFor(lang, overrides) {
170
171
  var base = LABELS[/^ru(-|$)/i.test(String(lang || '')) ? 'ru' : 'en'];
171
172
  var out = {};
172
173
  Object.keys(base).forEach(function (k) {
173
174
  var own = overrides ? overrides[k] : undefined;
174
- out[k] = typeof own === 'string' && own.trim() ? own.trim() : base[k];
175
+ out[k] = typeof own === 'string' && own.trim() && own.indexOf('Fabric') >= 0 ? own.trim() : base[k];
175
176
  });
176
177
  return out;
177
178
  }
@@ -1,4 +1,4 @@
1
- /* fabric-dashboard-kit@1.1.4 · fabric-dashboard/0.1 (Fabric Agent Contract DEC-0036)
1
+ /* fabric-dashboard-kit@1.1.5 · fabric-dashboard/0.1 (Fabric Agent Contract DEC-0036)
2
2
  A Fabric service dashboard shown inside a host pane: usable from 360 CSS px up, navigation always
3
3
  reachable, wide content in its own scroll box, one vertical scroller, appearance from the host.
4
4
  Copy this file next to fabric-dashboard.js; do not edit it in place. Put the page's own rules in
@@ -1,4 +1,4 @@
1
- /* fabric-dashboard-kit@1.1.4 · fabric-dashboard/0.1 (Fabric Agent Contract DEC-0036)
1
+ /* fabric-dashboard-kit@1.1.5 · fabric-dashboard/0.1 (Fabric Agent Contract DEC-0036)
2
2
  Load it same-origin with <script src="fabric-dashboard.js" defer></script>: no inline script, no
3
3
  eval, nothing from a CDN. It applies the host's appearance (ED-6, ED-8), folds navigation that
4
4
  does not fit into a More menu (ED-3), makes wide boxes keyboard-reachable (ED-4) and keeps
@@ -8,7 +8,7 @@
8
8
  'use strict';
9
9
 
10
10
  var KIT = 'fabric-dashboard-kit';
11
- var VERSION = '1.1.4';
11
+ var VERSION = '1.1.5';
12
12
  /* fabric-dashboard-kit:theme-ids:begin */
13
13
  var THEMES = ['graphite', 'fabric', 'paper', 'forest', 'ocean', 'ember', 'contrast'];
14
14
  /* fabric-dashboard-kit:theme-ids:end */
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fabric-dashboard-kit",
3
- "version": "1.1.4",
3
+ "version": "1.1.5",
4
4
  "profile": "fabric-dashboard/0.1",
5
5
  "decision": "DEC-0036",
6
6
  "declarationKey": "https://fabric.passioncode.ai/agent-contract/extensions/dashboard/0.1",
@@ -15,9 +15,9 @@
15
15
  "contrast"
16
16
  ],
17
17
  "files": {
18
- "fabric-dashboard.css": "c9367aad04c662547aec61e67e10b99d80ec1d1c4618b4473bc2db882d80db94",
19
- "fabric-dashboard.js": "5f157d185d9b04e9a11e680399503bbae924520ed0c112b734bf59fc8ee6bb49",
20
- "fabric-button.js": "2906d3533635df2ed1f2f071abe00238d30ea81666ff34c3456b376526b66859",
18
+ "fabric-dashboard.css": "cc7a96fa2c5eeb03d113e7a543ed88d43ce78c2808c411de199ea0fc6d55b957",
19
+ "fabric-dashboard.js": "5df01193c590a1d8aa82a521954902fecf2d7f655df482165f90d7291b00087f",
20
+ "fabric-button.js": "e945b3252f9a246826d06dbe7bd4be66e82d539a33728c3d3043fd61d4cbd681",
21
21
  "themes.json": "511839f1a1a83eb37fadfd132791474497d5c28c5091700b9aa31ad076e359b5"
22
22
  },
23
23
  "history": {
@@ -49,6 +49,11 @@
49
49
  "fabric-dashboard.css": "c9367aad04c662547aec61e67e10b99d80ec1d1c4618b4473bc2db882d80db94",
50
50
  "fabric-dashboard.js": "5f157d185d9b04e9a11e680399503bbae924520ed0c112b734bf59fc8ee6bb49",
51
51
  "fabric-button.js": "2906d3533635df2ed1f2f071abe00238d30ea81666ff34c3456b376526b66859"
52
+ },
53
+ "1.1.5": {
54
+ "fabric-dashboard.css": "cc7a96fa2c5eeb03d113e7a543ed88d43ce78c2808c411de199ea0fc6d55b957",
55
+ "fabric-dashboard.js": "5df01193c590a1d8aa82a521954902fecf2d7f655df482165f90d7291b00087f",
56
+ "fabric-button.js": "e945b3252f9a246826d06dbe7bd4be66e82d539a33728c3d3043fd61d4cbd681"
52
57
  }
53
58
  }
54
59
  }
@@ -101,7 +101,9 @@ version is still read and never written; delete it once its messages are past th
101
101
  - Replace the example knowledge with the product's own, and run real past questions through it ([practices](../../../creating-fabric-agents/references/practices.md#judges-and-evals), *Judges
102
102
  and evals*): count handoffs and read a sample of replies.
103
103
  - Show spend: `fabric_usage.usage_report` over the ledger, or `fabric_openrouter.totals`.
104
- - One decision per message id holds in one process; a service runs one instance per context under its instance lock.
105
- Each process reads `decisions.jsonl` once and keeps the index in memory, so a second process writing the same
106
- state folder is not seen.
104
+ - One decision per message id holds in one process. Each process reads `decisions.jsonl` once and keeps the index in
105
+ memory, so a second process writing the same state folder is not seen: a service runs one instance per context with
106
+ `fabric_context.hold_context_instance` (Node: `holdContextInstance`) and points `stateDir` at that instance's data
107
+ folder, so a second copy for the same context exits 75 and an organization's copy keeps its own folder
108
+ ([one instance per context](../../references/estate-record.md#one-instance-per-context-the-helper)).
107
109
  - Set the outbox's retention and schedule `prune`.