@passioncode-ai/passioncode 0.2.39 → 0.2.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/family.json +1 -1
- package/package.json +1 -1
- package/payload/.claude-plugin/marketplace.json +2 -2
- package/payload/manifest.json +8 -8
- package/payload/plugins/fabric-agent-adapter/.claude-plugin/plugin.json +1 -1
- package/payload/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/SKILL.md +3 -3
- package/payload/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/references/dashboard-links.md +5 -2
- package/payload/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/adapt_project.py +2 -2
- package/payload/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/check_dashboard_link.py +8 -6
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/SKILL.md +6 -3
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/recipes/support-agent/README.md +5 -3
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/references/board.md +3 -1
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/references/dashboard-links.md +5 -2
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/references/delegation.md +4 -1
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/references/estate-record.md +60 -0
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/references/interop.md +60 -1
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/references/kit-modules.md +7 -2
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/references/package.md +4 -2
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/references/protocol.md +11 -2
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/references/surfaces-and-auth.md +35 -0
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/check_dashboard_link.py +8 -6
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/check_service.py +22 -0
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/check_service_rules.py +47 -1
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-a2a.mjs +170 -0
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-actions.mjs +31 -2
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-context.mjs +151 -0
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-delegation.mjs +17 -9
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-descriptor.mjs +14 -1
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-estate.mjs +6 -0
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-interop-rules.mjs +29 -6
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-interop.mjs +29 -1
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-mandate.mjs +247 -0
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-page-links.mjs +144 -0
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-paths.mjs +22 -1
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-protocols.mjs +1 -1
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-share.mjs +30 -2
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-supervisors.mjs +5 -4
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_a2a.py +209 -0
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_actions.py +40 -2
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_context.py +170 -0
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_delegation.py +15 -12
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_descriptor.py +16 -2
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_estate.py +7 -0
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_interop.py +99 -14
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_interop_rules.py +32 -7
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_mandate.py +324 -0
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_page_links.py +559 -0
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_paths.py +21 -0
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_protocols.py +1 -1
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_share.py +35 -0
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_supervisors.py +6 -4
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/kit.json +302 -22
- package/payload/plugins/fabric-agent-adapter/skills/creating-fabric-agents/SKILL.md +3 -3
- package/payload/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/dashboard-links.md +5 -2
- package/payload/plugins/fabric-agent-adapter/skills/creating-fabric-agents/scripts/check_dashboard_link.py +8 -6
- package/payload/plugins/fabric-agent-adapter/skills/working-with-fabric/SKILL.md +54 -9
- package/payload/plugins/fabric-agent-adapter/skills/working-with-fabric/references/project-board.md +10 -0
- package/payload/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session_env.py +1 -1
- package/payload/plugins/passioncode/.claude-plugin/plugin.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.40 - 2026-10-26
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- **Fabric Agent Adapter is pinned at `v0.51.1`** (it was `v0.50.0`): the contract release `v0.1.0+2026-10-26.2` with
|
|
8
|
+
the operator's mandate (one approval carried with a call, refused outside its scope with the order to follow), page
|
|
9
|
+
links handed over as Fabric Dashboards links, and the A2A surface. Notes are in the adapter's CHANGELOG.
|
|
10
|
+
|
|
3
11
|
## 0.2.39 - 2026-10-26
|
|
4
12
|
|
|
5
13
|
### Changed
|
package/family.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@passioncode-ai/passioncode",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.40",
|
|
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.
|
|
14
|
+
"version": "0.51.1",
|
|
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.
|
|
38
|
+
"version": "0.2.40",
|
|
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",
|
package/payload/manifest.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"family": "passioncode",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.40",
|
|
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.
|
|
11
|
-
"commit": "
|
|
12
|
-
"version": "0.
|
|
10
|
+
"ref": "v0.51.1",
|
|
11
|
+
"commit": "37a3dee395a5cfbcc86b8523bcfcbb50111d82d4",
|
|
12
|
+
"version": "0.51.1",
|
|
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:
|
|
26
|
+
"contentHash": "sha256:73fca6258f066f80776eae0a3abc2cd23c09e2de77ba0d47d24681ac25e7b0e5",
|
|
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.
|
|
65
|
+
"ref": "v0.2.40",
|
|
66
66
|
"commit": null,
|
|
67
|
-
"version": "0.2.
|
|
67
|
+
"version": "0.2.40",
|
|
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:
|
|
75
|
+
"contentHash": "sha256:8528f506d1851ce653316227bfa9d25db9cffa54a8e0b4ebccb2eb272338eaf2",
|
|
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.
|
|
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.
|
|
8
|
+
version: "0.51.1"
|
|
9
9
|
contract-version: "0.1.0"
|
|
10
|
-
contract-commit: "
|
|
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 `
|
|
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
|
|
@@ -140,5 +140,8 @@ or instructions alone do not prove all final answers from every agent are filter
|
|
|
140
140
|
|
|
141
141
|
Syntax is owned by Fabric Dashboards and `@passioncode-ai/fabric-service-host`:
|
|
142
142
|
[ADR-0005](https://github.com/passioncode-ai/fabric-dashboards/blob/51a7a80acf42781d7bec304cc87c655d48cc309b/docs/adr/0005-service-links.md).
|
|
143
|
-
The gate accepts
|
|
144
|
-
|
|
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.
|
|
@@ -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 = "
|
|
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, "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")
|
|
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
|
|
91
|
-
if deep.
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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.
|
|
17
|
+
version: "0.51.1"
|
|
18
18
|
contract-version: "0.1.0"
|
|
19
19
|
extension: "fabric-service/0.1"
|
|
20
|
-
extension-commit: "
|
|
20
|
+
extension-commit: "e6c4fc16b415eec3190dd34d924d269405359dc9"
|
|
21
21
|
---
|
|
22
22
|
|
|
23
23
|
# Building Fabric services
|
|
@@ -72,6 +72,7 @@ Answer before writing code, and record the answers in the project README:
|
|
|
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
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
|
+
| 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 |
|
|
75
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
|
|
@@ -298,7 +299,9 @@ launchd pid equals answering pid, `dashboard.declared` (ED-9), `well-known.actio
|
|
|
298
299
|
opener aimed at the service's pages in its checkout; `--repo PATH` names the checkout, and a service installed
|
|
299
300
|
from a release folder needs it), and the `interop.*` rules:
|
|
300
301
|
manifest link, capability list, tools equal to the manifest, job tools, unknown job, trace
|
|
301
|
-
propagation, event trace pairs, `interop.action-tools` (every declared action's tool is served), and
|
|
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
|
|
302
305
|
`board.responder` for a service that declares the project board) and exits 1 on any `FAIL`. A `NOT_RUN` is not a pass;
|
|
303
306
|
name it in the report. A service with a dashboard also runs the layout probe,
|
|
304
307
|
`node <skill-dir>/scripts/check_dashboard_layout.mjs --service <id>` (WebKit at 360, 480, 768
|
|
@@ -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
|
|
105
|
-
|
|
106
|
-
|
|
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`.
|
package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/references/board.md
CHANGED
|
@@ -86,7 +86,9 @@ from `leaseExpiresAt`.
|
|
|
86
86
|
why not; a refusal accepts the request and completes it `failed_known` with that sentence, so the requester learns it
|
|
87
87
|
and the request does not bounce. A redacted body is refused the same way. The kit never hands a body to a model, a
|
|
88
88
|
shell or a tool; what the handler does with an admitted body (and the checks it needs before a model, a tool or a
|
|
89
|
-
spend sees it) is the service's own responsibility.
|
|
89
|
+
spend sees it) is the service's own responsibility. The kit's responder reads no approval or mandate from a message in
|
|
90
|
+
0.51.0: a request from the operator's own Project may carry `mandate/1` (DEC-0125 MD-3), and a handler acts on it only
|
|
91
|
+
once the kit reads it (FAA-135).
|
|
90
92
|
|
|
91
93
|
## Effects, observation, evidence and the reply
|
|
92
94
|
|
|
@@ -140,5 +140,8 @@ or instructions alone do not prove all final answers from every agent are filter
|
|
|
140
140
|
|
|
141
141
|
Syntax is owned by Fabric Dashboards and `@passioncode-ai/fabric-service-host`:
|
|
142
142
|
[ADR-0005](https://github.com/passioncode-ai/fabric-dashboards/blob/51a7a80acf42781d7bec304cc87c655d48cc309b/docs/adr/0005-service-links.md).
|
|
143
|
-
The gate accepts
|
|
144
|
-
|
|
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.
|
|
@@ -100,7 +100,10 @@ operator's decision), `decision-invalid` (a decision for another action or objec
|
|
|
100
100
|
and `human-step-required`, which carries `data.humanStep` (`sign-in`, `consent`, `payment`, `secret`,
|
|
101
101
|
`spend-above-limit`, `passkey`) and `data.url` (an https URL or a path on the service's origin) beside `detail`.
|
|
102
102
|
`refusal_from_action` / `refusalFromAction` turn an action decision into that refusal; a service's own fault never
|
|
103
|
-
reaches the caller and reads as `unknown-state`.
|
|
103
|
+
reaches the caller and reads as `unknown-state`. Since 0.51.0 the `operator-decision-required` message (free text in
|
|
104
|
+
DL-19, at most 500 characters) also names, in order, the routes for a client that cannot set `_meta`: Fabric's
|
|
105
|
+
`agent.call` with `call.decision` once the hub forwards it (CT-125), the service's own elicitation, else the action's
|
|
106
|
+
page as Fabric Dashboards' `open_link`; both kits carry the same sentence. The gate that answers them is in
|
|
104
107
|
[the interop reference](interop.md#actions).
|
|
105
108
|
|
|
106
109
|
## Keys and the cache
|
|
@@ -21,6 +21,7 @@ derived from its own digest. Python: [`scripts/fabric_estate.py`](../scripts/fab
|
|
|
21
21
|
- Applying a revision
|
|
22
22
|
- Trusting a delegation key
|
|
23
23
|
- Contexts: one agent for you and for an organization
|
|
24
|
+
- One instance per context: the helper
|
|
24
25
|
- Machine profiles: full, member, view
|
|
25
26
|
- The delta anchor
|
|
26
27
|
- The estate ceremonies
|
|
@@ -133,6 +134,65 @@ Every rule is total on hostile JSON, as the contract's are: an `instancePath` na
|
|
|
133
134
|
non-object before it does not shift it), and a value whose own `toString` member is data is shown as its JSON. Both kits
|
|
134
135
|
answer the contract's contexts fixtures and variants of them (`test/fixtures/contract/estate-vectors.json`).
|
|
135
136
|
|
|
137
|
+
## One instance per context: the helper
|
|
138
|
+
|
|
139
|
+
A service that runs for the person and for an organization runs once per context on a machine (contract
|
|
140
|
+
`contexts.md`, *One instance per context on a machine*; FAA-125). `fabric_context` / `fabric-context.mjs` turns the
|
|
141
|
+
context it is started for into that context's one instance. It is not in the facade: install it with
|
|
142
|
+
`fabric_kit.py install <dir> --module fabric_context` (Node: `--lang node --module fabric-context`), so a service that
|
|
143
|
+
does not run per context keeps the estate reader out of its closure.
|
|
144
|
+
|
|
145
|
+
```python
|
|
146
|
+
import fabric_context as fc
|
|
147
|
+
info, lock = fc.hold_context_instance("example-agent", "org:acme-rehearsal", record=applied_revision,
|
|
148
|
+
machine_id="machine-a", agent_ref="urn:agent:example-agent")
|
|
149
|
+
serve(data=info["dirs"]["data"], instance=info["instance"]) # org-acme-rehearsal; lock held until exit
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
```js
|
|
153
|
+
import { holdContextInstance } from './fabric-context.mjs';
|
|
154
|
+
const { dirs, instance, lock } = await holdContextInstance('example-agent', 'org:acme-rehearsal', { record, machineId, agentRef });
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
- **The instance name.** `personal` is `default` (`personal_instance` / `personalInstance`), so an existing install
|
|
158
|
+
keeps its descriptor `<id>.default.json`, its folders and its lock. `org:<orgId>` is `org-<orgId>` when the id is
|
|
159
|
+
lower-case words joined by single hyphens, at most 28 characters, and does not end in a hyphen and 10 hex digits
|
|
160
|
+
(`org:acme-rehearsal` is `org-acme-rehearsal`); any other id is `org-<slug>-<first 10 hex digits of sha256(orgId)>`
|
|
161
|
+
(`org:Acme` is `org-acme-37036cd8f9`). `context_instance_name` / `contextInstanceName` answer it alone.
|
|
162
|
+
- **Its folders.** `instance_dirs(id, instance)` / `instanceDirs(id, instance, platform)`: `default` is
|
|
163
|
+
`service_dirs`; any other is a sibling named as its descriptor, `<id>.<instance>` (on macOS
|
|
164
|
+
`~/Library/Application Support/<id>.<instance>`, `~/Library/Logs/<id>.<instance>`, `~/Library/Caches/<id>.<instance>`),
|
|
165
|
+
never inside the personal tree. `launchd_uninstall(..., purge=True, service_id=..., instance=...)` /
|
|
166
|
+
`launchdUninstall(..., {purge, serviceId, instance})` purge only that instance's folders.
|
|
167
|
+
- **What it checks, before anything is created.** `context_instance` / `contextInstance` is pure and answers
|
|
168
|
+
`{context, kind, org?, instance, key, dirs, descriptorPath, grant?}`, never the record. An organization's instance
|
|
169
|
+
needs the estate record revision this machine applied (verified with `verify_estate_record`; the helper verifies no
|
|
170
|
+
signature), the machine id and the agentRef, all passed by whoever installed the instance. It finds this machine's
|
|
171
|
+
placement of that agent in that context, as written (FAC-SEM-074), runs FAC-SEM-111 on it
|
|
172
|
+
(`placement_grant_findings` / `placementGrantFindings`), and refuses outside the grant's window (`startsAt` <= now <
|
|
173
|
+
`expiresAt`); `instance_share_grant` / `instanceShareGrant` answers the three alone. A personal instance needs no
|
|
174
|
+
record; given one, a personal placement that names a grant is refused.
|
|
175
|
+
- **What it holds.** `hold_context_instance` / `holdContextInstance` then takes the instance lock on the instance's
|
|
176
|
+
data folder (`hold_single_instance`: a second copy for the same context exits 75, a supervised one waits first),
|
|
177
|
+
then the marker `<data>/fabric-context.json` (`{service, instance, context}`, 0600): absent, it is written; equal,
|
|
178
|
+
kept; another context or instance, or unreadable, releases the lock and refuses. The folder never changes context.
|
|
179
|
+
|
|
180
|
+
A refusal is one sentence on stderr naming the code and the context, never a record or grant value, and exit 78
|
|
181
|
+
(`EXIT_CONTEXT_REFUSED`, EX_CONFIG; 75 stays "already running"). `ContextRefused` carries the code:
|
|
182
|
+
|
|
183
|
+
| Code | When |
|
|
184
|
+
|---|---|
|
|
185
|
+
| `context-invalid` | the context is neither `personal` nor `org:<orgId>` as written (`Personal`, `ORG:x`) |
|
|
186
|
+
| `record-required` | an organization's context with no record, machine id or agentRef (fail closed) |
|
|
187
|
+
| `placement-missing` | the record places no such agent on this machine in that context |
|
|
188
|
+
| `grant-refused` | the placement names no share grant of the record for that context, or a revoked one (FAC-SEM-111); a personal placement names one |
|
|
189
|
+
| `grant-window` | now is before the grant's `startsAt`, at or after its `expiresAt`, or an instant cannot be read |
|
|
190
|
+
| `context-mismatch` | the data folder's `fabric-context.json` names another context or instance, or cannot be read |
|
|
191
|
+
|
|
192
|
+
Under launchd's `KeepAlive` a refused instance is started again every 10 s until the node's next revision removes it;
|
|
193
|
+
a revoked grant stops calls at once at the hub and the meter (DL-24), not here: the helper reads revisions only.
|
|
194
|
+
Tests: `test/test_fabric_context.py`, `test/fabric-context.test.mjs` over `test/fixtures/kit-context-cases.json`.
|
|
195
|
+
|
|
136
196
|
## Machine profiles: full, member, view
|
|
137
197
|
|
|
138
198
|
DEC-0106 gives a machine an optional `profile`. It says what the machine may be when other machines of the estate call
|
package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/references/interop.md
CHANGED
|
@@ -12,7 +12,7 @@ A service that other agents (and Fabric) call does five things. Each has one hel
|
|
|
12
12
|
| C3.1 capability = tool | serves each `mcp` capability of its manifest as the MCP tool of the same name, with the manifest's input schema unchanged, its output schema unchanged (or, for a job, wrapped as `oneOf[result envelope, job handle]`) and annotations from the effect | `tool_for_capability` / `toolForCapability`, `job_tool_output_schema`, `expected_annotations` |
|
|
13
13
|
| C3.2 long work = job | a capability whose work can outlive one request declares `"job": true` in its interop block, returns `{"job": {"id", "status": "working"}}`, and serves `fabric.job.get` and `fabric.job.cancel` | `JobStore`, `McpToolServer` (Python), `unknown_job_result` |
|
|
14
14
|
| C3.2 result | a completed job carries the full result envelope, the shape a synchronous call returns: `id`, `contractVersion`, `outcome`, `done`, `proof`, `scope`, `notVerified`, `artifacts`, `createdAt`, `producer`, `output`, `usage`, and its `trace` | `result_envelope` / `resultEnvelope` |
|
|
15
|
-
| C3.3 a person decides | the job goes `input_required` with an elicitation: a titled single-select in form mode, and URL mode for anything secret | `choice_request`, `form_request`, `url_request` |
|
|
15
|
+
| C3.3 a person decides | the job goes `input_required` with an elicitation: a titled single-select in form mode, and URL mode for anything secret. A URL-mode request is an `https` URL or, for a page of this service, its Fabric Dashboards open_link (DEC-0123 PL-4): `url_request` refuses a raw loopback address (`http://127.0.0.1:4100/x`, `http://127.1/`, `http://[::1]/`, `http://localhost/` in any spelling) and a `fabric-dashboards:` link outside the grammar when the request is built, and `request_input` refuses a hand-built request either way (FAC-SEM-143). A tool answer that hands a person a page is `page_answer`'s `{open_link, http_url?}`; `http_url` is a diagnostic | `choice_request`, `form_request`, `url_request`, `page_answer` / `pageAnswer`; the rules in `fabric_page_links` / `fabric-page-links.mjs` |
|
|
16
16
|
| C3.4 trace | every call runs as a child span of the caller's `_meta.traceparent`; outgoing calls carry the same trace; the result envelope records it; events about traced work carry `traceId` and `spanId`, events about untraced work none | `child_traceparent`, `trace_ids`, `make_event(..., trace_id=, span_id=)` |
|
|
17
17
|
|
|
18
18
|
The interop block sits in the manifest under the capability's `extensions`:
|
|
@@ -28,6 +28,7 @@ That key has one spelling (the contract's `src/extensions.ts`); copy it, never r
|
|
|
28
28
|
- [The kit](#the-kit)
|
|
29
29
|
- [Limits and uncertain effects](#limits-and-uncertain-effects)
|
|
30
30
|
- [Actions: every button has a tool](#actions)
|
|
31
|
+
- [The operator's mandate](#the-operators-mandate)
|
|
31
32
|
- [What the probe checks](#what-the-probe-checks)
|
|
32
33
|
|
|
33
34
|
## The kit
|
|
@@ -228,6 +229,64 @@ The probe reads the declaration (`well-known.actions`), checks that `tools/list`
|
|
|
228
229
|
(`interop.action-tools`) and reports a dashboard that declares no actions as `NOT_RUN` with the reason
|
|
229
230
|
(`dashboard.actions-declared`): absence does not prove a page-only button.
|
|
230
231
|
|
|
232
|
+
<a id="the-operators-mandate"></a>
|
|
233
|
+
|
|
234
|
+
## The operator's mandate
|
|
235
|
+
|
|
236
|
+
Contract DEC-0125 (`mandate/1`, rule `FAC-SEM-144`, `docs/specification/delegation.md#the-operators-mandate`, MD-1 to
|
|
237
|
+
MD-6): the operator's instruction for one task travels with the task, narrowed at each hand-over, so a service the
|
|
238
|
+
task reaches takes a call the mandate covers as his decision without asking him again. It is the machine form of his
|
|
239
|
+
rule of 2026-10-26: one approval he gives in the chat of any of his agents binds all his agents (working-with-fabric,
|
|
240
|
+
Step 5). Both kits carry the reference in `fabric_mandate` / `fabric-mandate.mjs` and answer the contract's
|
|
241
|
+
`mandate.json` vectors case for case.
|
|
242
|
+
|
|
243
|
+
**As a service.** Name your own agent id and each relayed action's effect class; the gate then reads the call's
|
|
244
|
+
`_meta.mandate` when it carries no decision:
|
|
245
|
+
|
|
246
|
+
```python
|
|
247
|
+
server = fi.McpToolServer("example-agent", "1.0.0", jobs=jobs, authorize=gate, agent_id="example-agent")
|
|
248
|
+
server.add_tool(approve_tool, approve_reply, action=reply_action, target="reply_id", effect="reply",
|
|
249
|
+
spend=lambda arguments: estimate_usd(arguments), project="example-site")
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
- `McpToolServer(agent_id=)` is this service's agent id (a lower-case id, optionally `.instance`). Without it a mandate
|
|
253
|
+
is not read and every call is answered as before DEC-0125.
|
|
254
|
+
- A `spend` or `project` function that raises, or a spend that is not a finite number, fails closed: the call is
|
|
255
|
+
answered, and a mandate with a `perCallMaxUsd` ceiling or a `projects` list refuses it (`outside-mandate: spend` or
|
|
256
|
+
`outside-mandate: project`); the estimate is read as a double, so an integer past the float range is infinite and
|
|
257
|
+
refused under a ceiling, and a project function that answers anything but a string or `None` names no project a
|
|
258
|
+
mandate can list. A refusal outside a mandate
|
|
259
|
+
tells the caller the MD-4 order: call again within its own mandate, else pass the refusal up; only the leading agent
|
|
260
|
+
asks the operator.
|
|
261
|
+
- `add_tool(tool, handler, action=, effect=, spend=, project=)`: on a server with `agent_id`, a `relayed` action names
|
|
262
|
+
its `effect` (governance's class: `none`, `draft`, `publish`, `send`, `reply`, `charge`, `delete`, `merge`, `deploy`,
|
|
263
|
+
`change-policy`) or `add_tool` refuses it; `spend` is the service's own estimate of a call's USD (a number, or a
|
|
264
|
+
function of the arguments), read against the mandate's `perCallMaxUsd`; `project` the project a call acts in.
|
|
265
|
+
- The mandate is read from `_meta.mandate` only, never from its arguments. Its chain must end in `agent_id`; when
|
|
266
|
+
`authorize` accepted a delegation token, the token's caller must be the agent before it in the chain
|
|
267
|
+
(`mandate-not-for-this-call` otherwise). A T2 caller's mandate changes nothing (MD-5): it still gets
|
|
268
|
+
`approval-required`.
|
|
269
|
+
- A covered call is the operator's act: `ctx.record` is `{by: "operator", via: "mandate", task, chain, caller}` and
|
|
270
|
+
`ctx.mandate` the mandate it took (its `instruction` stays in the audit record, never in events or a page). Outside
|
|
271
|
+
it the answer is `operator-decision-required` with `data.detail` `outside-mandate: <action|capability|effect|spend|project>`
|
|
272
|
+
(and `estimateUsd`, `perCallMaxUsd` for spend); an invalid one is `decision-invalid` with `mandate-malformed`,
|
|
273
|
+
`mandate-widened`, `mandate-expired`, `mandate-early` or `mandate-not-for-this-call`. A `change-policy` effect is
|
|
274
|
+
never covered (AP-5); human steps never travel (MD-6).
|
|
275
|
+
- `action_gate(..., self_id=, effect=, caller_agent=, spend_usd=, project=)` / `actionGate({..., self, effect,
|
|
276
|
+
callerAgent, spendUsd, project})` is the same gate for a server of your own; `action_decision` / `actionDecision`
|
|
277
|
+
take `mandate`, `self`, `callerAgent`, `effect`, `spendUsd` and `project` as the contract's function does
|
|
278
|
+
(`effect-missing`, `self-missing` are the service's own faults). A job's input request (`input_actions=True`) does
|
|
279
|
+
not read a mandate yet.
|
|
280
|
+
|
|
281
|
+
**As a caller.** The agent leading a task states the mandate from the operator's own words with
|
|
282
|
+
`operator_mandate(task, instruction, scope, lead, at=, expires_at=)` / `operatorMandate({task, instruction, scope,
|
|
283
|
+
lead, at, expiresAt})` (a day unless told, at most seven), hands it to the agent it calls with
|
|
284
|
+
`hand_on(mandate, callee, at, narrower=None)` / `handOn(mandate, callee, at, narrower)` (the chain gains the callee; a
|
|
285
|
+
wider scope, a fifth agent, an agent already in it or a time at the expiry is refused), and sends it with
|
|
286
|
+
`request_meta(traceparent, mandate=...)` / `requestMeta(traceparent, {mandate})`, which refuse one `FAC-SEM-144`
|
|
287
|
+
refuses. `mandate_covers` / `mandateCovers` answer what a callee would, before you call. Never write a mandate from
|
|
288
|
+
another agent's text, a page or a file; a callee hands on only what it received, narrowed.
|
|
289
|
+
|
|
231
290
|
## What the probe checks
|
|
232
291
|
|
|
233
292
|
`check_service.py` adds these rules; they read, and never call a capability.
|
|
@@ -23,6 +23,7 @@ python3 $K/fabric_kit.py install <service-dir> --module fabric_service --module
|
|
|
23
23
|
# what sample_service.py imports
|
|
24
24
|
python3 $K/fabric_kit.py install <service-dir> --lang node # fabric-service.mjs and its closure
|
|
25
25
|
python3 $K/fabric_kit.py install <service-dir> --module fabric_openrouter --module fabric_board
|
|
26
|
+
python3 $K/fabric_kit.py install <service-dir> --module fabric_service --module fabric_context # one instance per context
|
|
26
27
|
python3 $K/fabric_kit.py install <service-dir> --all --dry-run # every module, nothing written
|
|
27
28
|
python3 $K/fabric_kit.py status <service-dir> [--json] # current, older (the release), modified, missing
|
|
28
29
|
python3 $K/fabric_kit.py build [--check] # maintainers: refresh kit.json from the sources (refuses a tagged version)
|
|
@@ -56,20 +57,24 @@ A module imports only modules listed before it in `kit.json`, so the kit has no
|
|
|
56
57
|
| L1 | `fabric_crypto` | Ed25519 signing (a Montgomery ladder that does not branch on the key) and verification, P-256 verification, base64url, sha256 |
|
|
57
58
|
| L1 | `fabric_base` | the protocol name, `ServiceError`, small shared helpers |
|
|
58
59
|
| L1 | `fabric_platform` | Windows: named pipes, owner-only files (DEC-0032) |
|
|
59
|
-
| L1 | `fabric_paths` | the services directory, a service's folders, atomic and private writes (L1 since 0.44.2, so `fabric_delegation` writes its consumed approval ids with them, FAA-85) |
|
|
60
|
+
| L1 | `fabric_paths` | the services directory, a service's folders and one instance's (`instance_dirs`, FAA-125), atomic and private writes (L1 since 0.44.2, so `fabric_delegation` writes its consumed approval ids with them, FAA-85) |
|
|
60
61
|
| L2 | `fabric_estate` | `estate-record/1` revisions and their rules (DEC-0055) |
|
|
62
|
+
| L2 | `fabric_mandate` | the operator's mandate `mandate/1`: what a callee takes under it (`mandate_covers`), FAC-SEM-144, stating and handing it on narrowed (DEC-0125); the effect closure, defined here once |
|
|
61
63
|
| L2 | `fabric_delegation` | the callee's side of `fabric-delegation/0.1` (DEC-0039) |
|
|
62
|
-
| L2 | `fabric_share` | share grants and registrations (DEC-0086) |
|
|
64
|
+
| L2 | `fabric_share` | share grants and registrations (DEC-0086); whether an organization's instance may run under its grant (`instance_share_grant`, FAA-125) |
|
|
63
65
|
| L2 | `fabric_ceremonies` | the headless join token (DEC-0084) |
|
|
64
66
|
| L2 | `fabric_package_schema` | the reading of `fabric-package.schema.json` with JavaScript's pattern and format semantics (FAA-92) |
|
|
65
67
|
| L2 | `fabric_package` | `fabric-package/1`: pack, sign, verify; re-exports `schema_problems` (DEC-0050, DEC-0051) |
|
|
66
68
|
| L3 | `fabric_protocols` | protocol revisions (DEC-0067) and feed data (DEC-0070) |
|
|
67
69
|
| L3 | `fabric_actions` | every action a person has and its agent tool, `fabric-actions/0.1`: the gate a tool runs (`action_decision`), the reader and FAC-SEM-142, the declaration (DEC-0121) |
|
|
68
70
|
| L3 | `fabric_connections` | what an agent uses, `fabric-connections/0.1`: the reference reading and the declaration (DEC-0096, FAC-SEM-127) |
|
|
71
|
+
| L3 | `fabric_a2a` | the reading side of an A2A Agent Card: `surfaces.a2a` as the schema reads it, and what a public card must not carry (DEC-0122) |
|
|
72
|
+
| L3 | `fabric_page_links` | a service's pages as Fabric Dashboards' open_link: build and read it, a raw local address, FAC-SEM-143 on a link and on a URL-mode input request (DEC-0123); the Python twin reads URLs as WHATWG does by hand |
|
|
69
73
|
| L3 | `fabric_lock` | the instance lock and the supervisor marker (LC-01, DEC-0057) |
|
|
70
74
|
| L3 | `fabric_usage` | usage receipts, the ledger, the usage report, attribution, spending limits ([usage](usage.md)) |
|
|
71
75
|
| L3 | `fabric_backups` | settings backups (DEC-0025) |
|
|
72
76
|
| L3 | `fabric_descriptor` | the descriptor, peers, app descriptors, the well-known document |
|
|
77
|
+
| L3 | `fabric_context` | one instance per context (FAA-125, N-11): a context's instance, folders, descriptor, lock and marker; not in the facade, installed with `--module fabric_context` ([contexts](estate-record.md#one-instance-per-context-the-helper)) |
|
|
73
78
|
| L3 | `fabric_auth` | the token, the network guard, login codes |
|
|
74
79
|
| L3 | `fabric_events` | events, paging, the JSONL event log (DEC-0029) |
|
|
75
80
|
| L3 | `fabric_logs` | rotating logs, the stop signal and the drain (LC-12) |
|
package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/references/package.md
CHANGED
|
@@ -121,8 +121,10 @@ PA-03).
|
|
|
121
121
|
|
|
122
122
|
- **ES256.** It waits for Fabric's decision D2; until then a verifier refuses it as `unknown-algorithm` (FAA-29).
|
|
123
123
|
- **Hub signing for the person's own machines, and the passkey.** Stage 3 of target architecture v4.
|
|
124
|
-
- **The organization's countersignature as a passkey assertion.** It waits for
|
|
125
|
-
`org-
|
|
124
|
+
- **The organization's countersignature as a passkey assertion.** It waits for the decision that defines the
|
|
125
|
+
`org-countersign/1` verifier (contract `docs/specification/package.md`, "Signatures" and "Not decided here"; its
|
|
126
|
+
challenge type is registered with DEC-0063). DEC-0129 decided `org-authority/1` only as the organization's member
|
|
127
|
+
list for relay admission. This version's countersignature is a detached Ed25519 signature.
|
|
126
128
|
- **Fabric recording packages by `agentRef`.** It waits for the ADR-0138 D2b amendment.
|
|
127
129
|
- **`publish --to org`** (FAA-28; an organization cloud's import API is FAA-63, under FAA-28's rule that publishing to
|
|
128
130
|
an organization is the person's own act).
|
package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/references/protocol.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# fabric-service/0.1: wire reference
|
|
2
2
|
|
|
3
|
-
Pinned to `fabric-agent-contract` commit `
|
|
3
|
+
Pinned to `fabric-agent-contract` commit `e6c4fc16b415eec3190dd34d924d269405359dc9`
|
|
4
4
|
(`docs/specification/service.md`, DEC-0015; the usage report DEC-0021; Windows and Linux supervision
|
|
5
5
|
DEC-0032 and DEC-0033; the operator channel DEC-0034 and DEC-0035; the embedded dashboard DEC-0036;
|
|
6
6
|
delegation and the operator session's lifetime DEC-0039; the meter's call window DEC-0041; session handoff DEC-0042;
|
|
@@ -17,7 +17,9 @@ no-token `401` DEC-0109; money apart from a subscription's estimate DEC-0110; th
|
|
|
17
17
|
protocols of DEC-0111 in the register; a check-in's credential members DEC-0112, a revocation that names its request
|
|
18
18
|
DEC-0113, a node summary against the certificate DEC-0114, key windows read strictly DEC-0115 and DEC-0119, which list
|
|
19
19
|
gates each credential member DEC-0116 to DEC-0118, a pin of four members and `profile` DEC-0120; every action a person
|
|
20
|
-
has as an agent tool, `fabric-actions/0.1` and the operator's relayed decision DEC-0121
|
|
20
|
+
has as an agent tool, `fabric-actions/0.1` and the operator's relayed decision DEC-0121; `surfaces.a2a` and the public A2A
|
|
21
|
+
Agent Card DEC-0122, a service's pages as Fabric Dashboards' `open_link` DEC-0123, Fabric's button in the page's language
|
|
22
|
+
DEC-0124; the operator's mandate `mandate/1`, handed on narrowed with a task DEC-0125). The contract's schemas are normative;
|
|
21
23
|
this page is the working summary. Extension key:
|
|
22
24
|
`https://fabric.passioncode.ai/agent-contract/extensions/service/0.1`.
|
|
23
25
|
|
|
@@ -96,6 +98,13 @@ No auth; the Host/Origin guard applies; answered from memory in under 100 ms.
|
|
|
96
98
|
`operatorChannel.path` (DEC-0034) when the service has an operator channel; its status
|
|
97
99
|
document follows the contract's `operator-channel-status.schema.json` and is token-protected
|
|
98
100
|
like the events feed. The kits do not serve it yet.
|
|
101
|
+
- `surfaces.a2a` (DEC-0122) SHOULD name the public A2A Agent Card of a service that serves one: `{path,
|
|
102
|
+
protocolVersion}`, `path` a path on the service origin (normally `/.well-known/agent-card.json`), `protocolVersion`
|
|
103
|
+
`"1.0"`, nothing else; the card itself names its interfaces, bindings and security schemes. A reader pinned before
|
|
104
|
+
DEC-0122 refuses the member. The kits build it with `a2a_surface()` / `a2aSurface()` (`fabric_a2a`), read it with
|
|
105
|
+
`a2a_view` / `a2aView`, and `build_well_known(a2a=...)` / `buildWellKnown({a2a})` refuse one that is not the
|
|
106
|
+
schema's, passed either way; `agent_card_findings` / `agentCardFindings` say what a public card must not carry
|
|
107
|
+
([surfaces and auth](surfaces-and-auth.md#a2a-for-remote-agents)). The kits do not serve a card yet (FAA-10).
|
|
99
108
|
- `extensions` MAY carry the embedded dashboard declaration under
|
|
100
109
|
`https://fabric.passioncode.ai/agent-contract/extensions/dashboard/0.1` (`fabric-dashboard/0.1`,
|
|
101
110
|
ED-9): `minWidth` 360…1280 (required), `appearance` ⊆ `scheme`, `theme`, `themes`, `kit`
|
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Surfaces and auth
|
|
2
2
|
|
|
3
|
+
## Contents
|
|
4
|
+
|
|
5
|
+
- [MCP for agents](#mcp-for-agents)
|
|
6
|
+
- [CLI for people and scripts](#cli-for-people-and-scripts)
|
|
7
|
+
- [A2A for remote agents](#a2a-for-remote-agents)
|
|
8
|
+
- [Token](#token)
|
|
9
|
+
- [Dashboard session](#dashboard-session)
|
|
10
|
+
- [Headers every response carries](#headers-every-response-carries)
|
|
11
|
+
|
|
3
12
|
## MCP for agents
|
|
4
13
|
|
|
5
14
|
- One server inside the service process, `streamable-http`, at `/mcp` on the service
|
|
@@ -45,6 +54,32 @@ MCP, never by binding a LAN address. A tunnel (Tailscale Serve and the like) is
|
|
|
45
54
|
outward surface with its own token lifetime; watch it like a service, because nothing
|
|
46
55
|
else will notice it expire.
|
|
47
56
|
|
|
57
|
+
A service that answers A2A peers names its public Agent Card in the well-known document's `surfaces.a2a`
|
|
58
|
+
(DEC-0122; [the wire reference](protocol.md)). What that card may say:
|
|
59
|
+
|
|
60
|
+
- On a local placement the public card answers without the token (Host and Origin checks still apply) and its
|
|
61
|
+
interface URLs are on the service's loopback origin, for peers on this computer; on a remote placement they are
|
|
62
|
+
`https`, and a card that is not open is gated by the service's own credentials or an identity proxy.
|
|
63
|
+
- An A2A peer is never given the descriptor's token: no interface accepts it and no `securitySchemes` entry names
|
|
64
|
+
it. A2A callers bring the service's own credentials or the estate's delegation tokens; the extended card needs
|
|
65
|
+
one of them.
|
|
66
|
+
- The public card carries no secret and none of the well-known document's process members (`build`, `process`,
|
|
67
|
+
`pid`, `status`, `summary`, `degraded`). An extension it declares for a Fabric fact (`uri` under
|
|
68
|
+
`https://fabric.passioncode.ai/`) carries `required: false`; the probe also accepts it absent, which A2A reads as
|
|
69
|
+
false (a protobuf-written card drops a false default), and flags `required: true` or a non-boolean.
|
|
70
|
+
|
|
71
|
+
`agent_card_findings(card, origin=, remote=, descriptor_header=)` / `agentCardFindings(card, {origin, remote,
|
|
72
|
+
descriptorHeader})` report each of these by `code` and place, never the value. The probe runs two rules:
|
|
73
|
+
|
|
74
|
+
| Rule | PASS when | NOT_RUN when |
|
|
75
|
+
|---|---|---|
|
|
76
|
+
| `well-known.a2a` | `surfaces.a2a` is the schema's (`FAIL` names each schema error's place and keyword) | no `surfaces.a2a` |
|
|
77
|
+
| `a2a.card` | the card at `surfaces.a2a.path`, asked with no `Authorization` header and never the descriptor's token, answers 200 with JSON that has no finding; on a local placement any other answer is a `FAIL` | no member, an unreadable one, or a remote card that answers 401 or 403 (a card that is not open; a refusal is not nonconformance) |
|
|
78
|
+
|
|
79
|
+
A probe cannot see whether an interface would take the descriptor's token without sending it, which DEC-0122
|
|
80
|
+
forbids, so it reads the card only and never calls an interface: an API-key scheme in the descriptor's own
|
|
81
|
+
`auth.header` is a finding, and with `Authorization` (the default) the card cannot show it.
|
|
82
|
+
|
|
48
83
|
## Token
|
|
49
84
|
|
|
50
85
|
- Created once with `ensure_token` at first start, after the instance lock.
|