@passioncode-ai/passioncode 0.2.37 → 0.2.39

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 (56) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/family.json +1 -1
  3. package/package.json +1 -1
  4. package/payload/.claude-plugin/marketplace.json +2 -2
  5. package/payload/manifest.json +8 -8
  6. package/payload/plugins/fabric-agent-adapter/.claude-plugin/plugin.json +1 -1
  7. package/payload/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/SKILL.md +29 -11
  8. package/payload/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/references/dashboard-links.md +53 -3
  9. package/payload/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/references/operator-channel-and-repository.md +11 -3
  10. package/payload/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/references/provider-entry.md +3 -1
  11. package/payload/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/adapt_project.py +2 -2
  12. package/payload/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/fabric_provider.py +4 -0
  13. package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/SKILL.md +50 -14
  14. package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/assets/dashboard-kit/fabric-button.js +38 -12
  15. package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/assets/dashboard-kit/fabric-dashboard.css +1 -1
  16. package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/assets/dashboard-kit/fabric-dashboard.js +2 -2
  17. package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/assets/dashboard-kit/kit.json +14 -4
  18. package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/references/dashboard-kit.md +9 -2
  19. package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/references/dashboard-links.md +53 -3
  20. package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/references/dashboard.md +13 -4
  21. package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/references/delegation.md +10 -1
  22. package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/references/interop.md +99 -1
  23. package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/references/kit-modules.md +1 -0
  24. package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/references/protocol.md +5 -2
  25. package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/references/remote-placement.md +6 -1
  26. package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/references/settings-backups.md +2 -1
  27. package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/references/surfaces-and-auth.md +5 -3
  28. package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/check_service.py +47 -3
  29. package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/check_service_rules.py +325 -2
  30. package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/dashboard_kit.py +2 -1
  31. package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-actions.mjs +246 -0
  32. package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-auth.mjs +4 -1
  33. package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-backups.mjs +3 -1
  34. package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-delegation.mjs +22 -0
  35. package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-descriptor.mjs +45 -3
  36. package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-interop-rules.mjs +45 -3
  37. package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-interop.mjs +2 -0
  38. package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-protocols.mjs +1 -1
  39. package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-service.mjs +1 -0
  40. package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_actions.py +302 -0
  41. package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_auth.py +7 -2
  42. package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_backups.py +2 -1
  43. package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_delegation.py +22 -1
  44. package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_descriptor.py +58 -5
  45. package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_interop.py +154 -8
  46. package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_interop_rules.py +45 -2
  47. package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_protocols.py +1 -1
  48. package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_service.py +9 -0
  49. package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/kit.json +202 -18
  50. package/payload/plugins/fabric-agent-adapter/skills/creating-fabric-agents/SKILL.md +29 -11
  51. package/payload/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/dashboard-links.md +53 -3
  52. package/payload/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/operator-channel-and-repository.md +11 -3
  53. package/payload/plugins/fabric-agent-adapter/skills/working-with-fabric/SKILL.md +41 -9
  54. package/payload/plugins/fabric-agent-adapter/skills/working-with-fabric/references/project-board.md +2 -0
  55. package/payload/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session_env.py +1 -1
  56. package/payload/plugins/passioncode/.claude-plugin/plugin.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.39 - 2026-10-26
4
+
5
+ ### Changed
6
+
7
+ - **Fabric Agent Adapter is pinned at `v0.50.0`** (it was `v0.49.3`): the contract release `v0.1.0+2026-10-25` with
8
+ dashboard action parity (every dashboard button has an agent tool, and the operator's agent relays their decision),
9
+ agent pages that open only in Fabric Dashboards, and new probe rules. Notes are in the adapter's CHANGELOG.
10
+
11
+ ## 0.2.38 - 2026-10-26
12
+
13
+ ### Changed
14
+
15
+ - **Fabric Agent Adapter is pinned at `v0.49.3`** (it was `v0.49.2`): Fabric's button in the dashboard kit shows
16
+ Russian on a Russian page and takes a page's own words, with no em dash, and a remote service registers the
17
+ repository of its code. Notes are in the adapter's CHANGELOG.
18
+
3
19
  ## 0.2.37 - 2026-10-24
4
20
 
5
21
  ### Changed
package/family.json CHANGED
@@ -8,7 +8,7 @@
8
8
  "name": "fabric-agent-adapter",
9
9
  "displayName": "Fabric Agent Adapter",
10
10
  "repo": "passioncode-ai/fabric-agent-adapter",
11
- "ref": "v0.49.2",
11
+ "ref": "v0.50.0",
12
12
  "kind": "plugin",
13
13
  "path": "plugins/fabric-agent-adapter",
14
14
  "legacyPluginIds": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@passioncode-ai/passioncode",
3
- "version": "0.2.37",
3
+ "version": "0.2.39",
4
4
  "description": "Set up PassionCode.ai on a computer with one command — the apps from their verified releases, their MCP servers for every coding agent and the PassionCode.ai skills — and check and repair it with the same command or `doctor`.",
5
5
  "bin": {
6
6
  "passioncode": "bin/passioncode.js"
@@ -11,7 +11,7 @@
11
11
  "name": "fabric-agent-adapter",
12
12
  "displayName": "Fabric Agent Adapter",
13
13
  "source": "./plugins/fabric-agent-adapter",
14
- "version": "0.49.2",
14
+ "version": "0.50.0",
15
15
  "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.",
16
16
  "author": {
17
17
  "name": "PassionCode.ai",
@@ -35,7 +35,7 @@
35
35
  "name": "passioncode",
36
36
  "displayName": "PassionCode.ai",
37
37
  "source": "./plugins/passioncode",
38
- "version": "0.2.37",
38
+ "version": "0.2.39",
39
39
  "description": "Set up and check PassionCode.ai on a computer with one command (setting-up-passioncode), the organisation's working rules for every contributor's agent (working-in-passioncode), and a once-a-day check that keeps the PassionCode.ai set current.",
40
40
  "author": {
41
41
  "name": "PassionCode.ai",
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "family": "passioncode",
3
- "version": "0.2.37",
3
+ "version": "0.2.39",
4
4
  "release": true,
5
5
  "members": [
6
6
  {
7
7
  "name": "fabric-agent-adapter",
8
8
  "displayName": "Fabric Agent Adapter",
9
9
  "repo": "passioncode-ai/fabric-agent-adapter",
10
- "ref": "v0.49.2",
11
- "commit": "ee2407257bcad04f2e135a59df7899d41453b7e8",
12
- "version": "0.49.2",
10
+ "ref": "v0.50.0",
11
+ "commit": "d813a4e07a7b721619bd98b80f7dd9920d896ba0",
12
+ "version": "0.50.0",
13
13
  "via": "clone of https://***@github.com/passioncode-ai/fabric-agent-adapter.git",
14
14
  "skills": [
15
15
  "adapting-projects-to-fabric",
@@ -23,7 +23,7 @@
23
23
  "legacyMarketplaces": [
24
24
  "fabric-agent-adapter"
25
25
  ],
26
- "contentHash": "sha256:dc2ff2c25f68f9698432cc08c563ee565138b770c6b34e1cf5a95ce37ca24cd7",
26
+ "contentHash": "sha256:d2577bec959c6231e91deb459bd70fbd613914bd1a08dd4c479c21bef516d2e9",
27
27
  "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.",
28
28
  "author": {
29
29
  "name": "PassionCode.ai",
@@ -62,9 +62,9 @@
62
62
  "name": "passioncode",
63
63
  "displayName": "PassionCode.ai",
64
64
  "repo": "passioncode-ai/passioncode",
65
- "ref": "v0.2.37",
65
+ "ref": "v0.2.39",
66
66
  "commit": null,
67
- "version": "0.2.37",
67
+ "version": "0.2.39",
68
68
  "via": "this repository",
69
69
  "skills": [
70
70
  "setting-up-passioncode",
@@ -72,7 +72,7 @@
72
72
  ],
73
73
  "legacyPluginIds": [],
74
74
  "legacyMarketplaces": [],
75
- "contentHash": "sha256:bcae9796e81b691ef3c93f2d70aaec6863f7861adcf0c31a5e8f0570fc573572",
75
+ "contentHash": "sha256:9d3b6b75814363291607bf77221b1b5f0326cf65139f5ce68fef96d67df010cd",
76
76
  "description": "Set up and check PassionCode.ai on a computer with one command (setting-up-passioncode), the organisation's working rules for every contributor's agent (working-in-passioncode), and a once-a-day check that keeps the PassionCode.ai set current.",
77
77
  "author": {
78
78
  "name": "PassionCode.ai",
@@ -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.2",
6
+ "version": "0.50.0",
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.2"
8
+ version: "0.50.0"
9
9
  contract-version: "0.1.0"
10
- contract-commit: "2f020a696453ae299b123517f14957f6a1354379"
10
+ contract-commit: "52c7d798b9f4eb784eb4e29e717038a8912c759d"
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 `52c7d798b9f4eb784eb4e29e717038a8912c759d`.
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
@@ -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
@@ -53,7 +53,9 @@ It refuses, with one sentence that never quotes a value:
53
53
  1024 characters), a shell-string command, more than 32 arguments or one over 1024 characters, more than 64 env
54
54
  names, a `source.repository` that is not an absolute URI, an `installedAt` that is not an RFC 3339 date-time,
55
55
  unknown fields. The patterns are the schema's, copied (`test_provider.SchemaParityTests` compares them with a
56
- contract checkout at the pin when `FABRIC_CONTRACT_NEW` names one).
56
+ contract checkout at the pin when `FABRIC_CONTRACT_NEW` names one);
57
+ - a `source.repository` with a `user:password@` or a value shaped like a credential: it names the repository, never
58
+ reaches it (since 0.49.3, as the service kit's descriptor check).
57
59
 
58
60
  `validate` also checks that the file is named `<id>.json` (FAC-SEM-014, slug half).
59
61
  From Python: `write_provider_entry(entry)`, `remove_provider_entry(id)`,
@@ -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 = "52c7d798b9f4eb784eb4e29e717038a8912c759d"
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, "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"
@@ -47,6 +47,7 @@ ENV_MAX = 64
47
47
  # Whole-value patterns end in \Z, never `$` (R1: `$` also matches before a trailing newline).
48
48
  _ID = re.compile(r"^[a-z][a-z0-9-]{1,62}\Z")
49
49
  _ENV_NAME = re.compile(r"^[A-Z_][A-Z0-9_]*\Z")
50
+ _URI_PASSWORD = re.compile(r"^[A-Za-z][A-Za-z0-9+.-]*://[^/?#@]*:[^/?#@]*@")
50
51
  _SECRET_REF = re.compile(r"^secret-ref:([A-Za-z0-9][A-Za-z0-9._/:@-]{0,255})\Z")
51
52
  _LOCAL_PATH = re.compile(LOCAL_PATH_PATTERN)
52
53
  _URL = re.compile(URL_PATTERN)
@@ -131,6 +132,9 @@ def validate_provider_entry(entry: Any) -> List[str]:
131
132
  problems.append("source is {repository?}")
132
133
  elif "repository" in source and not _is_uri(source["repository"]):
133
134
  problems.append("source.repository is an absolute URI")
135
+ elif "repository" in source and (_URI_PASSWORD.match(source["repository"]) or _secrets.secret_argument(source["repository"])):
136
+ # FAA-120, as the service kit: a repository is named, never reached with a credential in its URI.
137
+ problems.append("source.repository carries no password or credential: it names the repository")
134
138
  if not isinstance(entry["installedAt"], str) or not _TIMESTAMP.fullmatch(entry["installedAt"]):
135
139
  problems.append("installedAt is an RFC 3339 date-time")
136
140
  if not isinstance(entry["installedBy"], str) or not 1 <= len(entry["installedBy"]) <= 200:
@@ -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.2"
17
+ version: "0.50.0"
18
18
  contract-version: "0.1.0"
19
19
  extension: "fabric-service/0.1"
20
- extension-commit: "2f020a696453ae299b123517f14957f6a1354379"
20
+ extension-commit: "52c7d798b9f4eb784eb4e29e717038a8912c759d"
21
21
  ---
22
22
 
23
23
  # Building Fabric services
@@ -70,9 +70,9 @@ 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
+ | 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
76
 
77
77
  Read [the surfaces and auth reference](references/surfaces-and-auth.md) when wiring
78
78
  MCP registration into a client config, the login flow or the CSRF header.
@@ -248,6 +248,16 @@ and declare it with `extensions=fs.dashboard_extension()`. Read
248
248
  declaration and how to upgrade a dashboard built before the kit: an upgrade you may make yourself,
249
249
  which the plugin's session-start notice offers in a repository that owns the service.
250
250
 
251
+ **Every button has a tool** (`fabric-actions/0.1`, contract DEC-0121 AP-1). Each action the page lets a
252
+ person take is a tool on the MCP surface with the same effect, checks, audit record and an idempotency key,
253
+ registered with `server.add_tool(tool, handler, action={id, title, tool, gate})` (`gate` is `none`, `relayed`
254
+ or `human` with its `humanStep`) and declared with `build_well_known(actions=server.action_table())` under
255
+ `extensions/actions/0.1`. A `relayed` action called by the operator's own agent with their decision in
256
+ `_meta.decision` is the operator's act: never answer it "operator session required" or send them to the page.
257
+ A service adopts the relayed gate once Fabric's hub forwards decisions (CT-125), as the contract says; until then its tools
258
+ answer as today. Read the interop reference's *Actions* section for when to adopt, the gate, its hooks and its
259
+ refusals.
260
+
251
261
  **Say what the agent uses** (`fabric-connections/0.1`, DEC-0096) so Fabric Dashboards draws its chain: the other
252
262
  agents, models, connectors, stores and channels, each with its state, from
253
263
  `connections_extension()` / `connectionsExtension()` in `fabric_connections`. Never a credential in it, never a call
@@ -283,10 +293,13 @@ python3 <skill-dir>/scripts/check_service.py <id>[.<instance>]
283
293
  It reports `PASS`, `FAIL` or `NOT_RUN` per rule (descriptor, port claim, well-known
284
294
  shape, identity, latency, Host/Origin/cross-site guards, loopback bind, token file,
285
295
  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:
296
+ launchd pid equals answering pid, `dashboard.declared` (ED-9), `well-known.actions` (DEC-0121),
297
+ `dashboard.actions-declared` (`NOT_RUN` when a dashboard declares no actions), `dashboard.opens-in-host` (no browser
298
+ opener aimed at the service's pages in its checkout; `--repo PATH` names the checkout, and a service installed
299
+ from a release folder needs it), and the `interop.*` rules:
287
300
  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;
301
+ propagation, event trace pairs, `interop.action-tools` (every declared action's tool is served), and
302
+ `board.responder` for a service that declares the project board) and exits 1 on any `FAIL`. A `NOT_RUN` is not a pass;
290
303
  name it in the report. A service with a dashboard also runs the layout probe,
291
304
  `node <skill-dir>/scripts/check_dashboard_layout.mjs --service <id>` (WebKit at 360, 480, 768
292
305
  and 1280 px; needs Playwright, else `NOT_RUN` and exit 3: nothing was measured).
@@ -328,7 +341,8 @@ first commit:
328
341
  - an `AGENTS.md` that opens with the template's *Read first* block and closes with *After work*,
329
342
  and a `CLAUDE.md` whose first line is `@AGENTS.md`.
330
343
 
331
- org-index `scripts/check_format.py` reports 0 findings for it before it is called done. **An agent
344
+ Whoever owns the repository, an agent with a page gets the dashboard rule block in its `AGENTS.md`
345
+ (below). org-index `scripts/check_format.py` reports 0 findings for it before it is called done. **An agent
332
346
  someone builds for themselves is not a PassionCode.ai repository:** its licence is its owner's
333
347
  choice, nothing about it is published or listed by the organization, and none of these files is
334
348
  required of it, though the verified MCP quick start is still how anyone learns to drive it.
@@ -357,8 +371,9 @@ things change and the kits implement each:
357
371
  so the platform's copy is known rather than invisible. Without Project Observatory, record
358
372
  where the key lives in the person's own secret manager.
359
373
  3. **Register it on the operator's computer**, the installer side, run there:
360
- `registerRemote({ id, name, origin, token })` / `register_remote(...)` writes the token file
361
- (0600) and the descriptor; pass the token from a file or stdin, never in argv. Public names
374
+ `registerRemote({ id, name, origin, token, source })` / `register_remote(...)` writes the token file
375
+ (0600) and the descriptor, with `source.repository` naming the code's repository; pass the token from a file or
376
+ stdin, never in argv. Public names
362
377
  only in public artifacts: a private service is registered by a local descriptor and nowhere
363
378
  else.
364
379
  4. **Verify** with the probe: `check_service.py <id>` checks TLS, the token-gated well-known
@@ -417,8 +432,29 @@ the probe table verbatim with every `NOT_RUN` explained; the lock and kill-9 res
417
432
  with pids; and the one next command. Never call a service conformant while the probe
418
433
  reports a `FAIL`.
419
434
 
420
- ## Dashboard handoff: also for consumers
435
+ ## Agent pages open only in Fabric Dashboards
436
+
437
+ 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
438
+ answer or a notification, `<tool> dashboard`, a completion message, a page the agent opens itself.
439
+
440
+ 1. Resolve the exact registered service key (`id.instance`) with the `fabric-dashboards` MCP (`list_services`);
441
+ never guess it from a project name or a port.
442
+ 2. Hand over the `open_link` its `link` tool returns for the service and the page's path, as the primary action.
443
+ 3. To open it, use the host's `open` with `fallback=never`. Never `open <url>`, `open -a Safari`, `xdg-open`,
444
+ `start <url>`, `webbrowser.open` or `shell.openExternal` aimed at the service's pages.
445
+ 4. A browser only on a confirmed `not_installed` host (`host_status`); an installed host that failed to open is
446
+ reported, never replaced by HTTP.
447
+ 5. Links carry no token or login code: the host signs in.
448
+
449
+ Run `scripts/check_dashboard_link.py` (from this skill's own directory; it is not copied into the project) on an
450
+ action before it is printed or opened, and have the project's checks run it for every completion and notification
451
+ renderer. The generated `AGENTS.md` carries the rule as its dashboard
452
+ rule block; [dashboard links](references/dashboard-links.md) has the block, the gate's input and the remote case. The
453
+ probe's `dashboard.opens-in-host` fails a tracked file that opens the dashboard with a browser opener. Headless agents
454
+ need no dashboard.
455
+
456
+ ## An agent with a page and no service descriptor
421
457
 
422
- When handing a registered service dashboard to a person, or writing the generated agent's
423
- completion or notification instructions, read and apply [dashboard links](references/dashboard-links.md)
424
- and run its output gate before delivery, in any host. Headless agents need no dashboard.
458
+ A local page no service descriptor names cannot open in Fabric Dashboards. Never open it in a browser instead:
459
+ make the agent a `fabric-service/0.1` service (this skill: the descriptor, the well-known document, the login-code
460
+ routes and a declared dashboard), and until that lands file a board row for it and say the page waits on it.
@@ -1,16 +1,17 @@
1
- /* fabric-dashboard-kit@1.1.3 · 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>
5
5
  One floating menu button: "Chat with Fabric about <service>" and, when the page knows its project, "Open a coding
6
- session in <project>". Its links carry ids only and open a view in Fabric, nothing more (FB-8). It hides itself
6
+ session in <project>", in Russian on a page whose lang is ru; data-label-chat and data-label-session ({name} is the
7
+ service or the project), data-label-open and data-label-absent set the page's own words. Its links carry ids only and open a view in Fabric, nothing more (FB-8). It hides itself
7
8
  when the host draws its own (the fabric_button cookie, FB-6) and says how to get Fabric when Fabric is absent
8
9
  (FB-5). No inline script, no eval, nothing from a CDN; the only address it names is passioncode.ai/fabric.
9
10
  Do not edit in place; `dashboard_kit.py install` replaces it on an upgrade. */
10
11
  (function (root) {
11
12
  'use strict';
12
13
 
13
- var VERSION = '1.1.3';
14
+ var VERSION = '1.1.5';
14
15
  var COOKIE = 'fabric_button';
15
16
  var GET_FABRIC = 'https://passioncode.ai/fabric';
16
17
  var ABSENT_AFTER_MS = 1500;
@@ -155,16 +156,39 @@
155
156
  return null;
156
157
  }
157
158
 
159
+ // The words a person reads (FAA-119): English, or Russian on a page whose lang is ru; the Russian is written as
160
+ // escapes so the file reads the same whatever charset it is served with. Checked with Copylot (en-US and ru-RU).
161
+ var LABELS = {
162
+ en: { chat: 'Chat with Fabric about {name}', session: 'Open a coding session in {name}', open: 'Open Fabric',
163
+ absent: 'Fabric is not installed. Get it at' },
164
+ ru: { chat: '\u0427\u0430\u0442 \u0441 Fabric \u043e \u0441\u0435\u0440\u0432\u0438\u0441\u0435 {name}', session: '\u041e\u0442\u043a\u0440\u044b\u0442\u044c \u0441\u0435\u0441\u0441\u0438\u044e \u0432 \u043f\u0440\u043e\u0435\u043a\u0442\u0435 {name}', open: '\u041e\u0442\u043a\u0440\u044b\u0442\u044c Fabric',
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
+ };
167
+
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). */
170
+ function labelsFor(lang, overrides) {
171
+ var base = LABELS[/^ru(-|$)/i.test(String(lang || '')) ? 'ru' : 'en'];
172
+ var out = {};
173
+ Object.keys(base).forEach(function (k) {
174
+ var own = overrides ? overrides[k] : undefined;
175
+ out[k] = typeof own === 'string' && own.trim() && own.indexOf('Fabric') >= 0 ? own.trim() : base[k];
176
+ });
177
+ return out;
178
+ }
179
+ function named(label, name) { return label.split('{name}').join(name); }
180
+
158
181
  /** The menu items for a page: the chat link always, the session link when the page knows its project. */
159
- function menuItems(options) {
182
+ function menuItems(options, labels) {
183
+ var words = labels || labelsFor();
160
184
  var items = [];
161
185
  var chat = { action: 'chat', service: options.service };
162
186
  if (options.project) chat.project = options.project;
163
- items.push({ label: 'Chat with Fabric about ' + (options.serviceName || options.service), href: buildFabricLink(chat) });
187
+ items.push({ label: named(words.chat, options.serviceName || options.service), href: buildFabricLink(chat) });
164
188
  if (options.project) {
165
189
  var session = { action: 'session.new', project: options.project };
166
190
  if (options.runner) session.runner = options.runner;
167
- items.push({ label: 'Open a coding session in ' + (options.projectName || options.project), href: buildFabricLink(session) });
191
+ items.push({ label: named(words.session, options.projectName || options.project), href: buildFabricLink(session) });
168
192
  }
169
193
  return items;
170
194
  }
@@ -197,11 +221,12 @@
197
221
  function mount(doc, options) {
198
222
  var cookie = parseButtonCookie(doc.cookie);
199
223
  if (cookie && cookie.owner === 'host') return null;
200
- var items = menuItems(options);
224
+ var words = labelsFor(options.lang !== undefined ? options.lang : doc.documentElement.getAttribute('lang'), options.labels);
225
+ var items = menuItems(options, words);
201
226
  var absent = options.fabric === 'absent' || (cookie && cookie.fabric === 'absent');
202
227
 
203
228
  var wrap = el(doc, 'div', { 'class': 'fd-fabric-button-wrap' });
204
- var button = el(doc, 'button', { type: 'button', 'class': 'fd-fabric-button', 'aria-haspopup': 'menu', 'aria-expanded': 'false', 'aria-label': 'Open Fabric' });
229
+ var button = el(doc, 'button', { type: 'button', 'class': 'fd-fabric-button', 'aria-haspopup': 'menu', 'aria-expanded': 'false', 'aria-label': words.open });
205
230
  button.appendChild(mark(doc));
206
231
  button.appendChild(el(doc, 'span', { 'class': 'fd-fabric-button-label', 'aria-hidden': 'true' }, 'Fabric'));
207
232
  var menu = el(doc, 'div', { role: 'menu', 'class': 'fd-fabric-button-menu', 'aria-label': 'Fabric' });
@@ -212,7 +237,7 @@
212
237
  return a;
213
238
  });
214
239
  var note = el(doc, 'p', { 'class': 'fd-fabric-button-note', role: 'note' });
215
- note.appendChild(doc.createTextNode('Fabric is not installed. Get it at '));
240
+ note.appendChild(doc.createTextNode(words.absent + ' '));
216
241
  // The way to get Fabric is a menu item too, so the arrows reach it whenever the note shows.
217
242
  var get = el(doc, 'a', { role: 'menuitem', href: GET_FABRIC, target: '_blank', rel: 'noopener', tabindex: '-1', 'class': 'fd-fabric-button-get' }, 'passioncode.ai/fabric');
218
243
  note.appendChild(get);
@@ -274,18 +299,19 @@
274
299
 
275
300
  function optionsFrom(script) {
276
301
  var d = script.dataset;
277
- return { service: d.service, serviceName: d.serviceName, project: d.project, projectName: d.projectName, runner: d.runner, fabric: d.fabric };
302
+ return { service: d.service, serviceName: d.serviceName, project: d.project, projectName: d.projectName, runner: d.runner, fabric: d.fabric,
303
+ labels: { chat: d.labelChat, session: d.labelSession, open: d.labelOpen, absent: d.labelAbsent } };
278
304
  }
279
305
 
280
306
  var api = { version: VERSION, parseFabricLink: parseFabricLink, buildFabricLink: buildFabricLink, parseButtonCookie: parseButtonCookie,
281
- menuItems: menuItems, credentialName: credentialName, secretValue: secretValue, nameWords: nameWords, mount: mount };
307
+ menuItems: menuItems, labelsFor: labelsFor, credentialName: credentialName, secretValue: secretValue, nameWords: nameWords, mount: mount };
282
308
  root.FabricButton = api;
283
309
 
284
310
  var doc = root.document;
285
311
  var script = doc && doc.currentScript;
286
312
  if (script && script.dataset && script.dataset.service) {
287
313
  var start = function () {
288
- try { mount(doc, optionsFrom(script)); } catch (err) { if (root.console) root.console.warn('fabric-button: not shown — ' + err.message); }
314
+ try { mount(doc, optionsFrom(script)); } catch (err) { if (root.console) root.console.warn('fabric-button: not shown: ' + err.message); }
289
315
  };
290
316
  if (doc.readyState === 'loading') doc.addEventListener('DOMContentLoaded', start); else start();
291
317
  }
@@ -1,4 +1,4 @@
1
- /* fabric-dashboard-kit@1.1.3 · 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.3 · 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.3';
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 */