@passioncode-ai/passioncode 0.2.43 → 0.2.44
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 +9 -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/scripts/adapt_project.py +2 -2
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/SKILL.md +2 -2
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/references/delegation.md +2 -1
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/references/interop.md +22 -7
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/references/protocol.md +2 -2
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-actions.mjs +22 -9
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-delegation.mjs +6 -1
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-interop-rules.mjs +14 -6
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_actions.py +23 -13
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_delegation.py +6 -1
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_interop.py +9 -5
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_interop_rules.py +16 -5
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/kit.json +96 -8
- package/payload/plugins/fabric-agent-adapter/skills/creating-fabric-agents/SKILL.md +3 -3
- package/payload/plugins/fabric-agent-adapter/skills/working-with-fabric/SKILL.md +6 -4
- 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,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.44 - 2026-10-27
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- **Fabric Agent Adapter is pinned at `v0.54.0`** (it was `v0.53.1`): the contract pin `v0.1.0+2026-10-27.1` (DEC-0141
|
|
8
|
+
MJ-1). A job's step under the operator's mandate is read under the job's own capability; a step no mandate can read
|
|
9
|
+
is answered `operator-decision-required` (`mandate-capability-missing`) and waits for the operator. working-with-fabric
|
|
10
|
+
names the job's capability without `actions` when it covers a job's steps.
|
|
11
|
+
|
|
3
12
|
## 0.2.43 - 2026-10-27
|
|
4
13
|
|
|
5
14
|
### 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.44",
|
|
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.54.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.
|
|
38
|
+
"version": "0.2.44",
|
|
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.44",
|
|
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.54.0",
|
|
11
|
+
"commit": "5575cc6e9ea8087deb650d26c5827be94574619d",
|
|
12
|
+
"version": "0.54.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:
|
|
26
|
+
"contentHash": "sha256:c548471c7b5f632d7d19d2b88111caea380ef59397dea574386079fce945c2be",
|
|
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.44",
|
|
66
66
|
"commit": null,
|
|
67
|
-
"version": "0.2.
|
|
67
|
+
"version": "0.2.44",
|
|
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:f69bc17e5720c2097191aeb7442471232525710ba62e422c259214c828745583",
|
|
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.54.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.
|
|
8
|
+
version: "0.54.0"
|
|
9
9
|
contract-version: "0.1.0"
|
|
10
|
-
contract-commit: "
|
|
10
|
+
contract-commit: "00e050ba7429c0c60261cec871779e0fd6aeeb28"
|
|
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 `00e050ba7429c0c60261cec871779e0fd6aeeb28`.
|
|
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
|
|
@@ -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 = "00e050ba7429c0c60261cec871779e0fd6aeeb28"
|
|
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, "e6c4fc16b415eec3190dd34d924d269405359dc9", "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")
|
|
28
|
+
SUPPORTED_CONTRACT_COMMITS = (CONTRACT_COMMIT, "1197a90dc4b57152cabcc71ad17db7559f35a27f", "e6c4fc16b415eec3190dd34d924d269405359dc9", "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"
|
|
@@ -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.54.0"
|
|
18
18
|
contract-version: "0.1.0"
|
|
19
19
|
extension: "fabric-service/0.1"
|
|
20
|
-
extension-commit: "
|
|
20
|
+
extension-commit: "00e050ba7429c0c60261cec871779e0fd6aeeb28"
|
|
21
21
|
---
|
|
22
22
|
|
|
23
23
|
# Building Fabric services
|
|
@@ -112,7 +112,8 @@ operator's decision), `decision-invalid` (a decision for another action or objec
|
|
|
112
112
|
and `human-step-required`, which carries `data.humanStep` (`sign-in`, `consent`, `payment`, `secret`,
|
|
113
113
|
`spend-above-limit`, `passkey`) and `data.url` (an https URL or a path on the service's origin) beside `detail`.
|
|
114
114
|
`refusal_from_action` / `refusalFromAction` turn an action decision into that refusal; a service's own fault never
|
|
115
|
-
reaches the caller and reads as `unknown-state
|
|
115
|
+
reaches the caller and reads as `unknown-state`, but for `mandate-capability-missing` (DEC-0141 MJ-1): the caller
|
|
116
|
+
reads `operator-decision-required` with that `data.detail`, since no mandate covers the step and only a decision does. Since 0.51.0 the `operator-decision-required` message (free text in
|
|
116
117
|
DL-19, at most 500 characters) also names, in order, the routes for a client that cannot set `_meta`: Fabric's
|
|
117
118
|
`agent.call` with `call.decision` once the hub forwards it (CT-125), the service's own elicitation, else the action's
|
|
118
119
|
page as Fabric Dashboards' `open_link`; both kits carry the same sentence. The gate that answers them is in
|
package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/references/interop.md
CHANGED
|
@@ -199,7 +199,10 @@ well_known = fs.build_well_known(..., surfaces=surfaces, actions=server.action_t
|
|
|
199
199
|
`unknown-state` (`approval-outcome-unreadable` when `approve` answers a refusal whose code or detail DL-18 does not know; an answer
|
|
200
200
|
that is neither `None` nor a dict is read as no approval, which fails closed; or
|
|
201
201
|
`caller-missing`: an action-gated server must pass `tier`, `identify` or `authorize`,
|
|
202
|
-
since `handle` defaults to T2 and a T2 call names nobody without them).
|
|
202
|
+
since `handle` defaults to T2 and a T2 call names nobody without them). One fault reaches the caller by name:
|
|
203
|
+
`mandate-capability-missing` (DEC-0141 MJ-1), read as `operator-decision-required` with that `data.detail`.
|
|
204
|
+
`on_fault` hears it from `ctx.approval_step` at a job's start under the job's tool, and at the answer under
|
|
205
|
+
`fabric.job.get`; at the start it hears any fault `job_step` answers (also `now-unreadable` from the server's clock).
|
|
203
206
|
- **Arguments with no canonical form** (a lone surrogate, a NaN or an infinity that Python's `json.loads` lets in,
|
|
204
207
|
nesting past the recursion limit) have no digest to bind: a relayed tool answers JSON-RPC `-32602`, never an exception.
|
|
205
208
|
- **The handler** reads `ctx.record` (`"caller"`, or `{by, via, caller}`) and writes it into the service's audit
|
|
@@ -286,10 +289,14 @@ server.add_tool(approve_tool, approve_reply, action=reply_action, target="reply_
|
|
|
286
289
|
when it reads `source`; a caller that sees no such entry sends the call without `source`, and it is taken as before.
|
|
287
290
|
- `action_gate(..., self_id=, effect=, caller_agent=, spend_usd=, project=)` / `actionGate({..., self, effect,
|
|
288
291
|
callerAgent, spendUsd, project})` is the same gate for a server of your own; `action_decision` / `actionDecision`
|
|
289
|
-
take `mandate`, `self`, `callerAgent`, `effect`, `spendUsd` and `
|
|
290
|
-
(
|
|
291
|
-
|
|
292
|
-
|
|
292
|
+
take `mandate`, `self`, `callerAgent`, `effect`, `spendUsd`, `project` and `mandateCapability` as the contract's
|
|
293
|
+
function does (DEC-0141 MJ-1): for an action whose tool is `fabric.job.get` (a job's input request) the mandate is
|
|
294
|
+
read under `mandateCapability`, the job's own capability, with the request key as the action; any other action reads
|
|
295
|
+
its own tool. `effect-missing`, `self-missing` and `mandate-capability-missing` (a job's step with no usable
|
|
296
|
+
capability: absent, empty, blank, not a capability name, or `fabric.job.get` itself) are the service's own faults;
|
|
297
|
+
like `effect`, `mandateCapability` is the service's own input, never the caller's. The gate answers
|
|
298
|
+
`mandate-capability-missing` to the caller as `operator-decision-required` (`data.detail` names it), the fault kept
|
|
299
|
+
beside it for the service's log, and nothing is applied, so the job stays `input_required`.
|
|
293
300
|
- **A job's approval step (interop C3.3).** A job reads the mandate its start call carried at its own approval step:
|
|
294
301
|
`ctx.approval_step(job_id, key, request, effect=, spend_usd=, project=, message=)` on the start call's context (a
|
|
295
302
|
server with `agent_id`) answers the record when the mandate covers the step `{capability: the job's, action: key,
|
|
@@ -302,10 +309,18 @@ server.add_tool(approve_tool, approve_reply, action=reply_action, target="reply_
|
|
|
302
309
|
replaces the kept steps, and an answer clears them: pass `steps=` again on every question whose answer a mandate may
|
|
303
310
|
cover. The answer's mandate is read under the job's own capability, as at the start (`action_gate(...,
|
|
304
311
|
mandate_capability=)` / `actionGate({mandateCapability})`), so the start and the answer agree whatever the tool is
|
|
305
|
-
named
|
|
312
|
+
named: a job whose tool is not a capability name has no capability a mandate can read its step under, and both its
|
|
313
|
+
start and its answer wait for the operator (`mandate-capability-missing` in the message). A mandate whose
|
|
314
|
+
`capabilities` name `fabric.job.get` covers no step of another capability.
|
|
306
315
|
Node has no tool server: a Node service reads `store.load(jobId).private.steps` and `.private.capability` BEFORE
|
|
307
316
|
`store.answer` (which clears the steps), gates the answer with `actionGate({..., mandateCapability:
|
|
308
|
-
private.capability})`, then answers.
|
|
317
|
+
private.capability})`, then answers. It logs the `fault` that `jobStep` and `actionGate` return beside their
|
|
318
|
+
answer, as `on_fault` does in Python: `mandate-capability-missing` at the start means the job's tool is not a
|
|
319
|
+
capability name; at the answer it also means `mandateCapability` was not passed. Any service that gates a job's
|
|
320
|
+
answer itself (Node `actionGate`, or Python `action_gate(..., mandate_capability=)` outside `McpToolServer`, whose
|
|
321
|
+
own gate passes it) passes the job's capability; without it every answer a mandate used to cover now waits for the
|
|
322
|
+
operator. `refusal_from_action` / `refusalFromAction` and so `actionRefusalToolResult` answer that fault as
|
|
323
|
+
`operator-decision-required` too.
|
|
309
324
|
|
|
310
325
|
**As a caller.** The agent leading a task states the mandate from the operator's own words with
|
|
311
326
|
`operator_mandate(task, instruction, scope, lead, at=, expires_at=)` / `operatorMandate({task, instruction, scope,
|
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 `00e050ba7429c0c60261cec871779e0fd6aeeb28`
|
|
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;
|
|
@@ -23,7 +23,7 @@ DEC-0124; the operator's mandate `mandate/1`, handed on narrowed with a task DEC
|
|
|
23
23
|
operator's agents, its `source` on a mandate and a decision DEC-0126; context hub keys and the organizations an estate
|
|
24
24
|
record pins DEC-0127 and DEC-0128; the family and the agent network named DEC-0130 and DEC-0137; a reply delivered to
|
|
25
25
|
the sender it answers, or to whom it names DEC-0135; a hand-over adds no naming list the scope in force leaves out
|
|
26
|
-
DEC-0136. The organization's member list, draft exchange, relay and the machines' report, DEC-0129, DEC-0131 to DEC-0134,
|
|
26
|
+
DEC-0136; a job's step under the mandate read under the job's own capability DEC-0141. The organization's member list, draft exchange, relay and the machines' report, DEC-0129, DEC-0131 to DEC-0134,
|
|
27
27
|
reach the kit only as protocol register entries). The contract's schemas are normative;
|
|
28
28
|
this page is the working summary. Extension key:
|
|
29
29
|
`https://fabric.passioncode.ai/agent-contract/extensions/service/0.1`.
|
|
@@ -3,7 +3,10 @@
|
|
|
3
3
|
//
|
|
4
4
|
// - actionDecision(inputs): whether a service takes a call of one declared action and as whose act it records it,
|
|
5
5
|
// exactly as the contract's actionDecision answers (AP-2, AP-4; DL-17, DL-18, DL-25). A `fault` is the service's
|
|
6
|
-
// own and is never sent to a caller
|
|
6
|
+
// own and is never sent to a caller as such; actionGate answers mandate-capability-missing as
|
|
7
|
+
// operator-decision-required (DEC-0141 MJ-1).
|
|
8
|
+
// - JOB_STEP_TOOL, jobCapability(value): the tool a job's answer comes through, and whether a job's capability can
|
|
9
|
+
// read its step under a mandate (a capability name other than fabric.job.get).
|
|
7
10
|
// - decisionShape(value): a decision as decision.schema.json reads it, `at` read strictly (the contract's strictTime).
|
|
8
11
|
// - actionsView(wellKnown): the host reader of the declaration, as the contract's actionsView.
|
|
9
12
|
// - actionsExtensionRules(wellKnown), actionRules(kind, value): FAC-SEM-142.
|
|
@@ -125,12 +128,12 @@ const refuse = (code) => ({ ok: false, code });
|
|
|
125
128
|
* (DEC-0126 OA-6). DEC-0125: a call with no decision that carries
|
|
126
129
|
* `mandate` (its `_meta.mandate`) is answered as mandateCovers answers, given `self` (the service's own agent id),
|
|
127
130
|
* `callerAgent` (only an agent id the service authenticated), `effect` (the action's effect class, the service's own
|
|
128
|
-
* knowledge), `spendUsd` and `project`: covered, {by: 'operator', via: 'mandate', task, chain, source?, caller}.
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
*
|
|
131
|
+
* knowledge), `spendUsd` and `project`: covered, {by: 'operator', via: 'mandate', task, chain, source?, caller}.
|
|
132
|
+
* DEC-0141 MJ-1: a job's input request (tool fabric.job.get) under a mandate is read under `mandateCapability`, the
|
|
133
|
+
* job's own capability (the tool its start call named), its action the request key; any other action reads its own
|
|
134
|
+
* tool. `{fault}` (action-invalid, caller-missing, now-unreadable, digest-missing, url-missing, effect-missing,
|
|
135
|
+
* self-missing, mandate-capability-missing) is the service's own and is never sent to a caller (actionGate answers
|
|
136
|
+
* mandate-capability-missing as operator-decision-required).
|
|
134
137
|
*/
|
|
135
138
|
export function actionDecision(inputs) {
|
|
136
139
|
const field = (key) => member(inputs, key);
|
|
@@ -166,6 +169,14 @@ export function actionDecision(inputs) {
|
|
|
166
169
|
return { ok: true, recordAs: { by: 'operator', via: 'relayed-instruction', caller, ...(has(decision, 'source') ? { source: { ...decision.source } } : {}) } };
|
|
167
170
|
}
|
|
168
171
|
|
|
172
|
+
/** The tool of a job's input request (interop C3.3, AP-2): the call that answers it is fabric.job.get (DEC-0141 MJ-1). */
|
|
173
|
+
export const JOB_STEP_TOOL = 'fabric.job.get';
|
|
174
|
+
|
|
175
|
+
/** A job's capability a mandate can read a step under (MJ-1): a capability name, never fabric.job.get itself. */
|
|
176
|
+
export function jobCapability(value) {
|
|
177
|
+
return typeof value === 'string' && CAPABILITY.test(value) && value !== JOB_STEP_TOOL;
|
|
178
|
+
}
|
|
179
|
+
|
|
169
180
|
/**
|
|
170
181
|
* A relayed action's call that carries a mandate and no decision (DEC-0125 MD-3): the mandate decides, as the contract's
|
|
171
182
|
* byMandate reads it: the service's own `effect` and agent id `self` are required (else a fault), its estimate
|
|
@@ -176,13 +187,15 @@ function byMandate(field, action, caller) {
|
|
|
176
187
|
const self = field('self');
|
|
177
188
|
if (typeof effect !== 'string') return { fault: 'effect-missing' };
|
|
178
189
|
if (typeof self !== 'string') return { fault: 'self-missing' };
|
|
190
|
+
// MJ-1: a job's step is read under the job's capability, never under the transport fabric.job.get itself.
|
|
191
|
+
const capability = action.tool === JOB_STEP_TOOL ? field('mandateCapability') : action.tool;
|
|
192
|
+
if (!jobCapability(capability)) return { fault: 'mandate-capability-missing' };
|
|
179
193
|
const spendUsd = field('spendUsd');
|
|
180
194
|
const project = field('project');
|
|
181
195
|
const answer = mandateCovers({
|
|
182
196
|
mandate: field('mandate'), tier: field('tier'), now: field('now'), self,
|
|
183
197
|
...(field('callerAgent') === undefined ? {} : { callerAgent: field('callerAgent') }),
|
|
184
|
-
|
|
185
|
-
call: { capability: typeof field('mandateCapability') === 'string' ? field('mandateCapability') : action.tool, action: action.id, effect, ...(typeof spendUsd === 'number' ? { spendUsd } : {}), ...(typeof project === 'string' ? { project } : {}) },
|
|
198
|
+
call: { capability, action: action.id, effect, ...(typeof spendUsd === 'number' ? { spendUsd } : {}), ...(typeof project === 'string' ? { project } : {}) },
|
|
186
199
|
});
|
|
187
200
|
if ('recordAs' in answer) return { ok: true, recordAs: { ...answer.recordAs, caller } };
|
|
188
201
|
return answer; // never `ignored`: the tier was read first, so a T2 call answered approval-required before (MD-5)
|
|
@@ -506,11 +506,16 @@ export function refusalFromDenial(denial) {
|
|
|
506
506
|
* DL-19 (DEC-0121): the refusal a service answers for an action call that fabric-actions.mjs's actionDecision refused,
|
|
507
507
|
* or undefined when it took the call. operator-decision-required, decision-invalid and approval-required carry their
|
|
508
508
|
* detail; human-step-required carries humanStep and url (AP-4). A `fault` is the service's own and is never sent: the
|
|
509
|
-
* caller reads unknown-state with the detail service-fault, as for anything else that is not an answer
|
|
509
|
+
* caller reads unknown-state with the detail service-fault, as for anything else that is not an answer; but
|
|
510
|
+
* mandate-capability-missing (DEC-0141 MJ-1), a job's step no mandate can be read for, is answered as
|
|
511
|
+
* operator-decision-required with that detail, so the job stays input_required.
|
|
510
512
|
*/
|
|
511
513
|
export function refusalFromAction(answer) {
|
|
512
514
|
const isRecord = answer !== null && typeof answer === 'object' && !Array.isArray(answer);
|
|
513
515
|
if (isRecord && answer.ok === true) return undefined;
|
|
516
|
+
if (isRecord && answer.fault === 'mandate-capability-missing') {
|
|
517
|
+
return refuse('operator-decision-required', ACTION_DETAILS.get('operator-decision-required'), { detail: 'mandate-capability-missing' });
|
|
518
|
+
}
|
|
514
519
|
const code = isRecord && answer.ok === false && typeof answer.code === 'string' ? answer.code : undefined;
|
|
515
520
|
if (code === undefined || !ACTION_DETAILS.has(code)) return refuse('unknown-state', 'service-fault');
|
|
516
521
|
const data = answer.data !== null && typeof answer.data === 'object' && !Array.isArray(answer.data) && Object.keys(answer.data).length ? { ...answer.data } : undefined;
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import crypto from 'node:crypto';
|
|
7
7
|
import { ServiceError } from './fabric-base.mjs';
|
|
8
8
|
import { jcs, refusalFromAction, refusalToolResult } from './fabric-delegation.mjs';
|
|
9
|
-
import { actionDecision, decisionShape } from './fabric-actions.mjs'; // DEC-0121
|
|
9
|
+
import { actionDecision, decisionShape, jobCapability } from './fabric-actions.mjs'; // DEC-0121
|
|
10
10
|
import { EFFECT_CLASSES, OPERATOR_SOURCE_PROTOCOL, mandateCovers, mandateProblem, withoutSource } from './fabric-mandate.mjs'; // DEC-0125, DEC-0126
|
|
11
11
|
import { isString } from './fabric-jsvalue.mjs'; // the core's (FAA-65)
|
|
12
12
|
import { isObject } from './fabric-jsvalue.mjs'; // the core's (FAA-65)
|
|
@@ -218,6 +218,8 @@ export function requestMeta(traceparent, { decision, mandate, targetProtocols }
|
|
|
218
218
|
return meta;
|
|
219
219
|
}
|
|
220
220
|
|
|
221
|
+
const UNREADABLE_JOB_CAPABILITY = "The operator's mandate cannot be read for this step (mandate-capability-missing): answer it, or call fabric.job.get with a decision in _meta.decision.";
|
|
222
|
+
|
|
221
223
|
/**
|
|
222
224
|
* A job's own approval step under the mandate its start call carried (interop C3.3, DEC-0125 MD-3, MD-4; conformance:
|
|
223
225
|
* "a job's approval step included"): the step is the call {capability: the job's, action: the input request's `key`,
|
|
@@ -225,7 +227,8 @@ export function requestMeta(traceparent, { decision, mandate, targetProtocols }
|
|
|
225
227
|
* agent id it authenticated for the start call. Answers {covered: true, record} (the step goes on as the operator's
|
|
226
228
|
* decision, no input request; `record` is mandateCovers' recordAs, `source` included when the mandate carries one);
|
|
227
229
|
* {covered: false, message, detail} (the step waits input_required, `message` its statusMessage naming the part not
|
|
228
|
-
* covered, or saying the mandate expired or cannot be read; never the operator's words or `source
|
|
230
|
+
* covered, or saying the mandate expired or cannot be read; never the operator's words or `source`; with `fault`
|
|
231
|
+
* mandate-capability-missing beside it when the job's capability cannot read the step, MJ-1); or {covered: false,
|
|
229
232
|
* message: null} with no mandate or a T2 caller's (MD-5), with `fault` when the service's own clock does not read.
|
|
230
233
|
*/
|
|
231
234
|
export function jobStep({ mandate, tier, now, self, capability, key, effect, callerAgent, spendUsd, project } = {}) {
|
|
@@ -237,8 +240,10 @@ export function jobStep({ mandate, tier, now, self, capability, key, effect, cal
|
|
|
237
240
|
const covers = { mandate, tier, now, self, call };
|
|
238
241
|
if (typeof callerAgent === 'string' && callerAgent.length > 0) covers.callerAgent = callerAgent;
|
|
239
242
|
const answer = mandateCovers(covers);
|
|
240
|
-
|
|
243
|
+
// A T2 caller's mandate changes nothing (MD-5), first; then MJ-1: read alike at the start and at the answer (actionGate).
|
|
241
244
|
if ('ignored' in answer || 'fault' in answer) return { covered: false, message: null, ...('fault' in answer ? { fault: answer.fault } : {}) };
|
|
245
|
+
if (!jobCapability(capability)) return { covered: false, detail: 'mandate-capability-missing', fault: 'mandate-capability-missing', message: UNREADABLE_JOB_CAPABILITY };
|
|
246
|
+
if (answer.ok === true) return { covered: true, record: answer.recordAs };
|
|
242
247
|
const { detail } = answer.data;
|
|
243
248
|
const said = detail === 'mandate-expired' ? "The operator's mandate for this task expired before this step"
|
|
244
249
|
: detail.startsWith('outside-mandate') ? `The operator's mandate does not cover this step (${detail})`
|
|
@@ -253,7 +258,8 @@ export function jobStep({ mandate, tier, now, self, capability, key, effect, cal
|
|
|
253
258
|
* elicitation), `approval` (verifyApproval's accepted outcome), `actionDigest`, `humanStepUrl` and `now` (RFC 3339).
|
|
254
259
|
* Answers {ok: true, record, decision}, `decision` being the one taken as the operator's (null otherwise), or {ok: false,
|
|
255
260
|
* refusal} in the DL-19 form (refusalToolResult sends it); a service's own fault is the refusal unknown-state with
|
|
256
|
-
* `fault` beside it for the service's log
|
|
261
|
+
* `fault` beside it for the service's log, but for mandate-capability-missing (MJ-1): operator-decision-required naming
|
|
262
|
+
* it in data.detail, `fault` still beside it.
|
|
257
263
|
*
|
|
258
264
|
* DEC-0125: a service that names its own agent id (`self`) reads the call's `_meta.mandate` when it carries no
|
|
259
265
|
* decision, given `effect` (the action's effect class, required: without it the call is the service's fault
|
|
@@ -262,8 +268,8 @@ export function jobStep({ mandate, tier, now, self, capability, key, effect, cal
|
|
|
262
268
|
* {ok: true, record: {by: 'operator', via: 'mandate', task, chain, caller}, decision: null, mandate}; without `self` the
|
|
263
269
|
* mandate is not read and the call is answered as before it existed.
|
|
264
270
|
*
|
|
265
|
-
* mandateCapability: a job's kept step's capability the mandate is read under
|
|
266
|
-
*
|
|
271
|
+
* mandateCapability: a job's kept step's capability the mandate is read under (DEC-0141 MJ-1; the service's own input,
|
|
272
|
+
* never a caller's); unusable, the refusal is operator-decision-required.
|
|
267
273
|
*/
|
|
268
274
|
export function actionGate({ action, tier, caller, meta, target, elicited = false, approval, actionDigest, humanStepUrl, now, self, effect, callerAgent, spendUsd, project, mandateCapability } = {}) {
|
|
269
275
|
const inputs = { action, tier, caller, elicited: elicited === true, now };
|
|
@@ -285,6 +291,8 @@ export function actionGate({ action, tier, caller, meta, target, elicited = fals
|
|
|
285
291
|
if (isObject(record) && record.via === 'mandate') out.mandate = inputs.mandate;
|
|
286
292
|
return out;
|
|
287
293
|
}
|
|
294
|
+
// MJ-1 (DEC-0141): refusalFromAction answers mandate-capability-missing as operator-decision-required; nothing is
|
|
295
|
+
// applied, so the job stays input_required.
|
|
288
296
|
const out = { ok: false, refusal: refusalFromAction(answer) };
|
|
289
297
|
if ('fault' in answer) out.fault = answer.fault;
|
|
290
298
|
return out;
|
|
@@ -3,7 +3,10 @@ extensions/actions/0.1 (DEC-0121, FAC-SEM-142). Part of the service kit (layer L
|
|
|
3
3
|
|
|
4
4
|
- ``action_decision(inputs)``: whether a service takes a call of one declared action and as whose act it records it,
|
|
5
5
|
exactly as the contract's ``actionDecision`` answers (AP-2, AP-4; DL-17, DL-18, DL-25). A ``fault`` is the
|
|
6
|
-
service's own and is never sent to a caller
|
|
6
|
+
service's own and is never sent to a caller as such; ``action_gate`` answers ``mandate-capability-missing`` as
|
|
7
|
+
operator-decision-required (DEC-0141 MJ-1).
|
|
8
|
+
- ``JOB_STEP_TOOL`` and ``job_capability(value)``: the tool a job's answer comes through, and whether a job's
|
|
9
|
+
capability can read its step under a mandate (a capability name other than ``fabric.job.get``).
|
|
7
10
|
- ``decision_shape(value)``: a decision as ``decision.schema.json`` reads it, ``at`` read strictly (the contract's
|
|
8
11
|
``strictTime``).
|
|
9
12
|
- ``actions_view(well_known)``: the host reader of the declaration, as the contract's ``actionsView``.
|
|
@@ -166,14 +169,12 @@ def action_decision(inputs: Any) -> Dict[str, Any]:
|
|
|
166
169
|
the service authenticated), `effect` (the action's effect class, the service's own knowledge), `spendUsd` (its
|
|
167
170
|
own estimate) and `project`: covered, `{by: operator, via: mandate, task, chain, source?, caller}`; otherwise
|
|
168
171
|
`operator-decision-required` or `decision-invalid` with `data.detail`.
|
|
172
|
+
- DEC-0141 MJ-1: a job's input request (tool `fabric.job.get`) under a mandate is read under `mandateCapability`,
|
|
173
|
+
the job's own capability (the tool its start call named), its action the request key; any other action reads
|
|
174
|
+
its own tool.
|
|
169
175
|
- `{"fault": ...}` (action-invalid, caller-missing, now-unreadable, digest-missing, url-missing, effect-missing,
|
|
170
|
-
self-missing) is the service's
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
Kit input beside the contract's: `mandateCapability`, a job's kept step's capability, read by the mandate in
|
|
175
|
-
place of the action's tool (the contract's actionDecision does not take it; the service's own input, never a
|
|
176
|
-
caller's)."""
|
|
176
|
+
self-missing, mandate-capability-missing) is the service's own: an input it owes is missing or unreadable. It is
|
|
177
|
+
never sent to a caller (action_gate answers mandate-capability-missing as operator-decision-required)."""
|
|
177
178
|
action = get(inputs, "action")
|
|
178
179
|
if not readable_action(action):
|
|
179
180
|
return {"fault": "action-invalid"}
|
|
@@ -219,6 +220,15 @@ def action_decision(inputs: Any) -> Dict[str, Any]:
|
|
|
219
220
|
return {"ok": True, "recordAs": record}
|
|
220
221
|
|
|
221
222
|
|
|
223
|
+
#: The tool of a job's input request (interop C3.3, AP-2): the call that answers it is fabric.job.get (DEC-0141 MJ-1).
|
|
224
|
+
JOB_STEP_TOOL = "fabric.job.get"
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def job_capability(value: Any) -> bool:
|
|
228
|
+
"""A job's capability a mandate can read a step under (MJ-1): a capability name, never fabric.job.get itself."""
|
|
229
|
+
return isinstance(value, str) and _CAPABILITY.fullmatch(value) is not None and value != JOB_STEP_TOOL
|
|
230
|
+
|
|
231
|
+
|
|
222
232
|
def _by_mandate(inputs: Any, action: Dict[str, Any], caller: str) -> Dict[str, Any]:
|
|
223
233
|
"""A relayed action's call that carries a mandate and no decision (DEC-0125 MD-3): the mandate decides, as the
|
|
224
234
|
contract's byMandate reads it: the service's own `effect` and agent id `self` are required (else a fault), its
|
|
@@ -228,11 +238,11 @@ def _by_mandate(inputs: Any, action: Dict[str, Any], caller: str) -> Dict[str, A
|
|
|
228
238
|
return {"fault": "effect-missing"}
|
|
229
239
|
if not isinstance(me, str):
|
|
230
240
|
return {"fault": "self-missing"}
|
|
231
|
-
#
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
241
|
+
# MJ-1: a job's step is read under the job's capability, never under the transport fabric.job.get itself.
|
|
242
|
+
capability = get(inputs, "mandateCapability") if action["tool"] == JOB_STEP_TOOL else action["tool"]
|
|
243
|
+
if not job_capability(capability):
|
|
244
|
+
return {"fault": "mandate-capability-missing"}
|
|
245
|
+
call: Dict[str, Any] = {"capability": capability, "action": action["id"], "effect": effect}
|
|
236
246
|
spend, project = get(inputs, "spendUsd"), get(inputs, "project")
|
|
237
247
|
if is_number(spend):
|
|
238
248
|
call["spendUsd"] = spend
|
|
@@ -818,9 +818,14 @@ def refusal_from_action(answer: Any) -> Optional[Dict[str, Any]]:
|
|
|
818
818
|
"""DL-19 (DEC-0121): the refusal a service answers for an action call that fabric_actions.action_decision refused,
|
|
819
819
|
or None when it took the call. operator-decision-required, decision-invalid and approval-required carry their
|
|
820
820
|
detail; human-step-required carries `humanStep` and `url` (AP-4). A `fault` is the service's own and is never
|
|
821
|
-
sent: the caller reads unknown-state with the detail `service-fault`, as for anything else that is not an answer
|
|
821
|
+
sent: the caller reads unknown-state with the detail `service-fault`, as for anything else that is not an answer;
|
|
822
|
+
but `mandate-capability-missing` (DEC-0141 MJ-1), a job's step no mandate can be read for, is answered as
|
|
823
|
+
operator-decision-required with that detail, so the job stays input_required."""
|
|
822
824
|
if isinstance(answer, dict) and answer.get("ok") is True:
|
|
823
825
|
return None
|
|
826
|
+
if isinstance(answer, dict) and answer.get("fault") == "mandate-capability-missing":
|
|
827
|
+
return _refuse("operator-decision-required", _ACTION_DETAILS["operator-decision-required"],
|
|
828
|
+
{"detail": "mandate-capability-missing"})
|
|
824
829
|
code = answer.get("code") if isinstance(answer, dict) and answer.get("ok") is False else None
|
|
825
830
|
if not isinstance(code, str) or code not in _ACTION_DETAILS:
|
|
826
831
|
return _refuse("unknown-state", "service-fault")
|
|
@@ -510,7 +510,8 @@ class CallContext:
|
|
|
510
510
|
carried (`_meta.mandate`) is read for it (job_step). Covered: answers the record (`via: mandate`, `source` when
|
|
511
511
|
the mandate carries one) and asks nothing; the job goes on as the operator's decision. Otherwise the job waits
|
|
512
512
|
input_required on `request`, its message naming the part not covered (or `message`), and answers None; the step
|
|
513
|
-
is kept so an answer's `_meta.mandate` that covers it is read when input_actions is on.
|
|
513
|
+
is kept so an answer's `_meta.mandate` that covers it is read when input_actions is on. Any fault job_step answers
|
|
514
|
+
(mandate-capability-missing, MJ-1, or the server clock's now-unreadable) goes to on_fault under the job's tool."""
|
|
514
515
|
if self.server.jobs is None:
|
|
515
516
|
raise InteropError("This server has no job store.")
|
|
516
517
|
step: Dict[str, Any] = {"message": None}
|
|
@@ -521,6 +522,7 @@ class CallContext:
|
|
|
521
522
|
spend_usd=spend_usd, project=project)
|
|
522
523
|
if step["covered"]:
|
|
523
524
|
return step["record"]
|
|
525
|
+
self.server._fault(self.tool, step) # the start hears it as the answer does (MJ-1)
|
|
524
526
|
kept = {"effect": effect, **({"spendUsd": spend_usd} if spend_usd is not None else {}),
|
|
525
527
|
**({"project": project} if project is not None else {})}
|
|
526
528
|
self.server.jobs.request_input(job_id, {key: request}, step["message"] or message, steps={key: kept})
|
|
@@ -644,7 +646,8 @@ class McpToolServer:
|
|
|
644
646
|
# approval the call presented, or None; elicited(tool, arguments, caller) is True only on the service's own
|
|
645
647
|
# record of an accepted elicitation. input_actions=True gates each answered input request as an action (D8).
|
|
646
648
|
# on_fault(tool, fault) hears a service fault the caller only sees as unknown-state (e.g. caller-missing: an
|
|
647
|
-
# action-gated server needs a tier, identify or authorize that names who calls)
|
|
649
|
+
# action-gated server needs a tier, identify or authorize that names who calls), but for mandate-capability-missing
|
|
650
|
+
# (MJ-1), which the caller reads as operator-decision-required naming it in data.detail.
|
|
648
651
|
# DEC-0125: agent_id is this service's own agent id; with it the server reads a call's _meta.mandate for a
|
|
649
652
|
# relayed action added with its effect (the chain must end in agent_id, and in the delegation's caller before
|
|
650
653
|
# it when authorize accepted a token); without it a mandate is not read, as before.
|
|
@@ -796,8 +799,8 @@ class McpToolServer:
|
|
|
796
799
|
action = {"id": key, "title": "Answer %s" % key, "tool": "fabric.job.get", "gate": "relayed"}
|
|
797
800
|
step, mandate = steps.get(key), {}
|
|
798
801
|
if self.agent_id is not None and isinstance(step, dict):
|
|
799
|
-
# A kept step's mandate is read as approval_step read it, under the job's own capability
|
|
800
|
-
# itself stays fabric.job.get, so it is readable
|
|
802
|
+
# A kept step's mandate is read as approval_step read it, under the job's own capability, which must
|
|
803
|
+
# be a capability name (MJ-1); the action itself stays fabric.job.get, so it is a readable action.
|
|
801
804
|
capability = self.jobs.private(job_id).get("capability")
|
|
802
805
|
agent = (verdict or {}).get("caller")
|
|
803
806
|
mandate = {"self_id": self.agent_id, "effect": step.get("effect"), "spend_usd": step.get("spendUsd"),
|
|
@@ -811,7 +814,8 @@ class McpToolServer:
|
|
|
811
814
|
return {"ok": True, "records": records}
|
|
812
815
|
|
|
813
816
|
def _fault(self, tool: str, gated: Dict[str, Any]) -> None:
|
|
814
|
-
"""Hand a service fault to on_fault; the caller saw
|
|
817
|
+
"""Hand a service fault to on_fault; the caller saw unknown-state (operator-decision-required naming it for
|
|
818
|
+
mandate-capability-missing, MJ-1). A hook that raises is the service's."""
|
|
815
819
|
if gated.get("fault") and self._on_fault is not None:
|
|
816
820
|
try:
|
|
817
821
|
self._on_fault(tool, str(gated["fault"]))
|
|
@@ -17,7 +17,7 @@ if str(_Path(__file__).resolve().parent) not in _sys.path: # the kit loads from
|
|
|
17
17
|
_sys.path.insert(0, str(_Path(__file__).resolve().parent))
|
|
18
18
|
|
|
19
19
|
from fabric_base import ServiceError # noqa: E402
|
|
20
|
-
from fabric_actions import action_decision, decision_shape # noqa: E402 - DEC-0121
|
|
20
|
+
from fabric_actions import action_decision, decision_shape, job_capability # noqa: E402 - DEC-0121
|
|
21
21
|
from fabric_mandate import ( # noqa: E402 - DEC-0125, DEC-0126
|
|
22
22
|
EFFECT_CLASSES, OPERATOR_SOURCE_PROTOCOL, mandate_covers, mandate_problem, without_source)
|
|
23
23
|
|
|
@@ -319,6 +319,10 @@ def request_meta(traceparent: Optional[str] = None, decision: Any = None, mandat
|
|
|
319
319
|
return meta
|
|
320
320
|
|
|
321
321
|
|
|
322
|
+
_UNREADABLE_JOB_CAPABILITY = ("The operator's mandate cannot be read for this step (mandate-capability-missing): answer it, "
|
|
323
|
+
"or call fabric.job.get with a decision in _meta.decision.")
|
|
324
|
+
|
|
325
|
+
|
|
322
326
|
def job_step(mandate: Any, *, tier: Any, now: Any, self_id: Any, capability: Any, key: Any, effect: Any,
|
|
323
327
|
caller_agent: Any = None, spend_usd: Any = None, project: Any = None) -> Dict[str, Any]:
|
|
324
328
|
"""A job's own approval step under the mandate its start call carried (interop C3.3, DEC-0125 MD-3, MD-4;
|
|
@@ -330,6 +334,7 @@ def job_step(mandate: Any, *, tier: Any, now: Any, self_id: Any, capability: Any
|
|
|
330
334
|
mandate_covers' recordAs (`via: mandate`, the task, the chain and `source` when the mandate carries one).
|
|
331
335
|
- {"covered": False, "message", "detail"}: the step waits input_required, `message` its statusMessage naming the
|
|
332
336
|
part not covered, or saying the mandate expired or cannot be read; never the operator's words or `source`.
|
|
337
|
+
With `fault` mandate-capability-missing beside it when the job's capability cannot read the step (MJ-1).
|
|
333
338
|
- {"covered": False, "message": None}: no mandate, or a T2 caller's (MD-5): ask as before; with `fault` when the
|
|
334
339
|
service's own clock does not read."""
|
|
335
340
|
if mandate is None:
|
|
@@ -345,10 +350,13 @@ def job_step(mandate: Any, *, tier: Any, now: Any, self_id: Any, capability: Any
|
|
|
345
350
|
if isinstance(caller_agent, str) and caller_agent:
|
|
346
351
|
covers["callerAgent"] = caller_agent
|
|
347
352
|
answer = mandate_covers(covers)
|
|
353
|
+
if "ignored" in answer or "fault" in answer: # a T2 caller's mandate changes nothing (MD-5), first
|
|
354
|
+
return dict({"covered": False, "message": None}, **({"fault": answer["fault"]} if "fault" in answer else {}))
|
|
355
|
+
if not job_capability(capability): # MJ-1: read alike at the start and at the answer (action_gate)
|
|
356
|
+
return {"covered": False, "detail": "mandate-capability-missing", "fault": "mandate-capability-missing",
|
|
357
|
+
"message": _UNREADABLE_JOB_CAPABILITY}
|
|
348
358
|
if answer.get("ok") is True:
|
|
349
359
|
return {"covered": True, "record": answer["recordAs"]}
|
|
350
|
-
if "ignored" in answer or "fault" in answer:
|
|
351
|
-
return dict({"covered": False, "message": None}, **({"fault": answer["fault"]} if "fault" in answer else {}))
|
|
352
360
|
detail = answer["data"]["detail"]
|
|
353
361
|
if detail == "mandate-expired":
|
|
354
362
|
said = "The operator's mandate for this task expired before this step"
|
|
@@ -371,13 +379,14 @@ def action_gate(action: Any, *, tier: Any, caller: Any, meta: Any = None, target
|
|
|
371
379
|
record of an accepted elicitation), `approval` (verify_approval's accepted outcome), `action_digest`,
|
|
372
380
|
`human_step_url` and `now` (RFC 3339). Answers {ok: True, record, decision}, `decision` being the one taken as the
|
|
373
381
|
operator's (None otherwise), or {ok: False, refusal} in the DL-19 form (fabric_delegation.refusal_tool_result
|
|
374
|
-
sends it); a service's own fault is the refusal unknown-state with `fault` beside it for the service's log
|
|
382
|
+
sends it); a service's own fault is the refusal unknown-state with `fault` beside it for the service's log, but for
|
|
383
|
+
mandate-capability-missing (MJ-1): operator-decision-required naming it in data.detail, `fault` still beside it.
|
|
375
384
|
|
|
376
385
|
DEC-0125: a service that names its own agent id (`self_id`) reads the call's `_meta.mandate` when it carries no
|
|
377
386
|
decision, given `effect` (the action's effect class, required: without it the call is the service's fault
|
|
378
387
|
effect-missing), `caller_agent` (only an agent id the service authenticated: a delegation's caller, its own
|
|
379
388
|
per-agent credential; never a name the call carries), `spend_usd` (its own estimate), `project`, and `mandate_capability` (a job's kept step: the job's capability
|
|
380
|
-
the mandate is read under,
|
|
389
|
+
the mandate is read under, DEC-0141 MJ-1; unusable, the refusal is operator-decision-required). A covered call
|
|
381
390
|
answers {ok: True, record: {by: operator, via: mandate, task, chain, caller}, decision: None, mandate}; without
|
|
382
391
|
`self_id` the mandate is not read and the call is answered as before it existed."""
|
|
383
392
|
import fabric_delegation as _fd # noqa: E402 - beside this file: DL-19
|
|
@@ -401,6 +410,8 @@ def action_gate(action: Any, *, tier: Any, caller: Any, meta: Any = None, target
|
|
|
401
410
|
if isinstance(record, dict) and record.get("via") == "mandate":
|
|
402
411
|
out["mandate"] = inputs["mandate"]
|
|
403
412
|
return out
|
|
413
|
+
# MJ-1 (DEC-0141): refusal_from_action answers mandate-capability-missing as operator-decision-required; nothing is
|
|
414
|
+
# applied, so the job stays input_required.
|
|
404
415
|
out: Dict[str, Any] = {"ok": False, "refusal": _fd.refusal_from_action(answer)}
|
|
405
416
|
if "fault" in answer:
|
|
406
417
|
out["fault"] = answer["fault"]
|
package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/kit.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fabric-service-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.54.0",
|
|
4
4
|
"about": "The service kit's modules in dependency order: a module imports only modules listed before it. Written by fabric_kit.py build; edit the order and layers by hand, never the digests.",
|
|
5
5
|
"python": [
|
|
6
6
|
{
|
|
@@ -113,7 +113,7 @@
|
|
|
113
113
|
"fabric_mandate",
|
|
114
114
|
"fabric_paths"
|
|
115
115
|
],
|
|
116
|
-
"sha256": "
|
|
116
|
+
"sha256": "212ab88df02d52c433793acb0cd7a24527a87d770b831d542f467c176b49b4c5"
|
|
117
117
|
},
|
|
118
118
|
{
|
|
119
119
|
"module": "fabric_share",
|
|
@@ -186,7 +186,7 @@
|
|
|
186
186
|
"fabric_mandate",
|
|
187
187
|
"fabric_time"
|
|
188
188
|
],
|
|
189
|
-
"sha256": "
|
|
189
|
+
"sha256": "0efe81c7b73c63963348fb4936a3816a961d3424416abd5823fbeb1aa2cb0bbf"
|
|
190
190
|
},
|
|
191
191
|
{
|
|
192
192
|
"module": "fabric_connections",
|
|
@@ -380,7 +380,7 @@
|
|
|
380
380
|
"fabric_mandate",
|
|
381
381
|
"fabric_time"
|
|
382
382
|
],
|
|
383
|
-
"sha256": "
|
|
383
|
+
"sha256": "42e88f4b3f22890022baa8cb999ee953978ab2a9a029e89e951767a4eabfd090"
|
|
384
384
|
},
|
|
385
385
|
{
|
|
386
386
|
"module": "fabric_interop",
|
|
@@ -396,7 +396,7 @@
|
|
|
396
396
|
"fabric_service",
|
|
397
397
|
"fabric_time"
|
|
398
398
|
],
|
|
399
|
-
"sha256": "
|
|
399
|
+
"sha256": "42e3f73a673f13775fbbdf476bfd5a96e4553fa3d0141e8ad223c8f88b0dd0b3"
|
|
400
400
|
},
|
|
401
401
|
{
|
|
402
402
|
"module": "fabric_hub",
|
|
@@ -592,7 +592,7 @@
|
|
|
592
592
|
"fabric-mandate",
|
|
593
593
|
"fabric-paths"
|
|
594
594
|
],
|
|
595
|
-
"sha256": "
|
|
595
|
+
"sha256": "04eaf6cd1786d4309a253a27dbdb05e27b408cb511aa41b1633a96634b775e65"
|
|
596
596
|
},
|
|
597
597
|
{
|
|
598
598
|
"module": "fabric-share",
|
|
@@ -663,7 +663,7 @@
|
|
|
663
663
|
"fabric-mandate",
|
|
664
664
|
"fabric-time"
|
|
665
665
|
],
|
|
666
|
-
"sha256": "
|
|
666
|
+
"sha256": "35f34dccb6c4feb482b0b169f53682f9690b32012481bef3869e487e9637af09"
|
|
667
667
|
},
|
|
668
668
|
{
|
|
669
669
|
"module": "fabric-connections",
|
|
@@ -855,7 +855,7 @@
|
|
|
855
855
|
"fabric-jsvalue",
|
|
856
856
|
"fabric-mandate"
|
|
857
857
|
],
|
|
858
|
-
"sha256": "
|
|
858
|
+
"sha256": "0933a8cd9567cd39f05575167d92dfef0745aaa591779419e84f94be18b01177"
|
|
859
859
|
},
|
|
860
860
|
{
|
|
861
861
|
"module": "fabric-interop",
|
|
@@ -3942,6 +3942,94 @@
|
|
|
3942
3942
|
"fabric-openrouter.mjs": "ab6c4c7251d563980d895061ee19976bde2f364c21b6354efdd9a9027b1ea139",
|
|
3943
3943
|
"fabric-package.schema.json": "2ba799f576fad0f608029223c4ccccd122152f1c5dbfa8ce8b31cd63ac92ad44",
|
|
3944
3944
|
"share-registration.schema.json": "34b3997eeaf936ebc861342a23045634a7c18c19f01d7ed4198310d81989f5b0"
|
|
3945
|
+
},
|
|
3946
|
+
"0.54.0": {
|
|
3947
|
+
"fabric_jsvalue.py": "12888a2c934a885d35999ac9782eaedc8d04946d9775213344a235a8814d3d28",
|
|
3948
|
+
"fabric_time.py": "588f383da4ac6a243ea95b6ffc3a0aa99ca94245732dc4937734f190b7af4ae9",
|
|
3949
|
+
"fabric_identifiers.py": "0fd814d521ac3aa39907458265870753046a73f48af3d8581ab864b93b211607",
|
|
3950
|
+
"fabric_secrets.py": "68a0b5e9e90c468ec9ac67b2f0fd1848e08813d6ec1ec14a2a47337eb1a98911",
|
|
3951
|
+
"fabric_crypto.py": "9c34aee20dbe4d6742e4fdbaab68ceb934bbe7dbd3a904c0010824ace16cdeaa",
|
|
3952
|
+
"fabric_base.py": "f62bcc2ed9912660fa875d27a09f60867260cc4925f79873501fd9620be59611",
|
|
3953
|
+
"fabric_platform.py": "a9350dff55ef0fb3a23cdb4d0320a15868b15a8deb10d2eb74d72c58740c284b",
|
|
3954
|
+
"fabric_paths.py": "8a1ee742f80f436e9f28c61b4f049a9545de2a9e0d66bfc61faa6ee5d1ba3734",
|
|
3955
|
+
"fabric_estate_rules.py": "8b36a7f4108d9c2b1fb38c8bff344778c807bed631a99b4a563ecb0a49c10b1c",
|
|
3956
|
+
"fabric_estate.py": "b3cd56dcb86e04d252980322ac8054da1aaef24ac64690f7aa80e7c765ee6f60",
|
|
3957
|
+
"fabric_mandate.py": "050c43d0166a7dfba82e29c4e978f05c364cf6da06d0a781bd023a566777c17b",
|
|
3958
|
+
"fabric_delegation.py": "212ab88df02d52c433793acb0cd7a24527a87d770b831d542f467c176b49b4c5",
|
|
3959
|
+
"fabric_share.py": "da36a466426fc29dd7dd40013167f3959a18375f8fedd228bf64f298cec1040c",
|
|
3960
|
+
"fabric_ceremonies.py": "a59e83cf4adb2669a9b98008e3d3a0cba991a35cbff97bc604fc82fe81cfd49f",
|
|
3961
|
+
"fabric_package_schema.py": "e623f0408d38e3343ed66222fb812907c6c52e1a286eb1c1176ab2e64640c9ad",
|
|
3962
|
+
"fabric_package.py": "ff533f1b3f92b4ea0f532fcec50b68c610d47549930eef8ad5247692a64dfb3c",
|
|
3963
|
+
"fabric_protocols.py": "54ff0f26a8bda640c7edd826cf59151b6ff0483fe72e9c0626c0ba1b79738cd4",
|
|
3964
|
+
"fabric_actions.py": "0efe81c7b73c63963348fb4936a3816a961d3424416abd5823fbeb1aa2cb0bbf",
|
|
3965
|
+
"fabric_connections.py": "77d3ccf863c70e9209d21ef45c46a6db9c06237019bc20d54f8d3946873f3f11",
|
|
3966
|
+
"fabric_a2a.py": "8b01502131215bb4de1c01866187c56e99b791543923ed8577535573db33b919",
|
|
3967
|
+
"fabric_page_links.py": "1b57ea573c550e2f90d1ec3edcc655917ceaa0db9dd1635fccf422ec3024d25e",
|
|
3968
|
+
"fabric_lock.py": "ba2399d2137c7a7791b7133b9db9cca36693f4eb7e30955b25a75ade814e95ba",
|
|
3969
|
+
"fabric_usage.py": "b1f335cab4625171a616cf68cf3fd892b18bdadcf4baec3a3a0fdcf12785c170",
|
|
3970
|
+
"fabric_backups.py": "1f3f0d7ecf361b29e9101e07f1c53e5dc304d598bf26fd59ec89f16b9fed2420",
|
|
3971
|
+
"fabric_descriptor.py": "c973def06b68dce7ef369da77186542ce3afe357d4579a7d409604f06e4bf131",
|
|
3972
|
+
"fabric_context.py": "6ae311ddc7217a4aff915e7ce2646486931acd128462b7bebe4a52bbb5fbb6c7",
|
|
3973
|
+
"fabric_auth.py": "71115d18c67b7b806cb57b64b77e01ad97fc0e63e6f49ee850f778123617d905",
|
|
3974
|
+
"fabric_events.py": "0086886c2c29dbd5c9d152be9d375c5bdf4a29f89b8095baff8490c2bcd64e47",
|
|
3975
|
+
"fabric_logs.py": "86dc55bfafa2a57264722e2403510a249b95de0e4a722b2e4bc3948e3bd93da9",
|
|
3976
|
+
"fabric_supervisors.py": "3c5b2e8c310738720b1c1b779be4710e90251013d9be4c68f9952edc0bf55d5f",
|
|
3977
|
+
"fabric_service.py": "a2e813469a47d4d3eea996628635c8903007498bdb6a1ae032a7a5ce6f49fe2f",
|
|
3978
|
+
"fabric_profiles.py": "26e21ec29c016000f8ca8053b7b489184854a73be1b7fdc0a18d12990203db9b",
|
|
3979
|
+
"fabric_interop_rules.py": "42e88f4b3f22890022baa8cb999ee953978ab2a9a029e89e951767a4eabfd090",
|
|
3980
|
+
"fabric_interop.py": "42e3f73a673f13775fbbdf476bfd5a96e4553fa3d0141e8ad223c8f88b0dd0b3",
|
|
3981
|
+
"fabric_hub.py": "b9ac852e601b1d54cf61351677a628afc69faeed7d1fc74ebe541c1cf38e0c2a",
|
|
3982
|
+
"fabric_board_shapes.py": "0a0e8d3d40e33dfd8c457aedb85330b2ff25f8c49a33eb6cbd5a5cc8a5007f7e",
|
|
3983
|
+
"fabric_board_link.py": "55b552354deb60e50268c672912647bca57951fd6a3a27fb5d6dae11ec5a4fd7",
|
|
3984
|
+
"fabric_board_session.py": "e2b45c274e6eadcb020ec075950750a4f88d6293149b19b6b78a8d41ab669918",
|
|
3985
|
+
"fabric_board_fixture.py": "69761ce205ec640762ec9b591c6903e5a87f844e8d423b041681924027e39d1e",
|
|
3986
|
+
"fabric_board.py": "0f075894b3e352194fcaf3fc7d08dee4e9f1c215e859769ed85fa5010e4a1599",
|
|
3987
|
+
"fabric_http.py": "5d7f72af3a73fdbe8c1f487ecf60cef23a7c46289eba542b03aff4706b8218d4",
|
|
3988
|
+
"fabric_openrouter.py": "309f588f9c976b1061323f3161e35d86248afd5b59b16391afbfc4e1099ecd65",
|
|
3989
|
+
"fabric-jsvalue.mjs": "dfbdb8c41584972c9389750a41719374a1cfc5652ed438dd1623025190b0106e",
|
|
3990
|
+
"fabric-time.mjs": "3ff932069038a720df1c68c6492976906737b2e67e5c12d9c81c418597f661de",
|
|
3991
|
+
"fabric-identifiers.mjs": "a18c2a501a4aa170bfd3d99e571ad6b7662649b4d3b9d2a540c002ea42811bbf",
|
|
3992
|
+
"fabric-secrets.mjs": "6ad8ab18ac899ecc2384e8ec6a3fb689b92be3b9a5ce3a59b92a37d52da5d766",
|
|
3993
|
+
"fabric-crypto.mjs": "038e03689701e3c9721a1e7f8e0a4d02a52d6a5d1661b0dc84035edc3ebbfcf6",
|
|
3994
|
+
"fabric-base.mjs": "7804b2df7715dbbf9702a9b61b6b8cef2110f9a52b04cabd8fb0a66458d87512",
|
|
3995
|
+
"fabric-platform.mjs": "a8119d95ebba4d589a9e401f5d6fc8dda62d59d44e4c1ff811d97fac3771ac0c",
|
|
3996
|
+
"fabric-paths.mjs": "aa630d611e748a899e7043480f4a4e46b444f2e15ff945f30c2fbef21c1ceea5",
|
|
3997
|
+
"fabric-estate-rules.mjs": "7476be71174aa5b1026134f2b335abf647feaa55584e2f9de8618e3054aa8e1e",
|
|
3998
|
+
"fabric-estate.mjs": "e8a2e62d2313121e6cb502f6e6abad94d1d546a44b4d9b3ce2be29e7808e8cf3",
|
|
3999
|
+
"fabric-mandate.mjs": "deaab2bfb929d1bd35ae1bad71e7b9ad9902cc23b8eb51819ed592fd80eddf95",
|
|
4000
|
+
"fabric-delegation.mjs": "04eaf6cd1786d4309a253a27dbdb05e27b408cb511aa41b1633a96634b775e65",
|
|
4001
|
+
"fabric-share.mjs": "bfb26581c21ad984c2a337da02429f9f3a165cc582f8ba48de4d3dfb7d163a59",
|
|
4002
|
+
"fabric-ceremonies.mjs": "117d4374c9e95c930a38de7adbd4323774798f4fe6ec6f24afcd6f2c7298ca44",
|
|
4003
|
+
"fabric-package-schema.mjs": "cd28092375db0fc21ffab1f0bb7794f7f8979ee08c64d3b9faf4b3845357caa6",
|
|
4004
|
+
"fabric-package.mjs": "13913ea6d12056c3a31b8222b6c4aab8395941fe519913692e1a06d8672e1c88",
|
|
4005
|
+
"fabric-protocols.mjs": "7da65440a7a08bf27d90a06e9158b1c4ee6d7f9204eecb712d452406bfe59962",
|
|
4006
|
+
"fabric-actions.mjs": "35f34dccb6c4feb482b0b169f53682f9690b32012481bef3869e487e9637af09",
|
|
4007
|
+
"fabric-connections.mjs": "303432e547ce563e90ca9ffcd827f5612f51d3582929834e1add0fdaf6485dcc",
|
|
4008
|
+
"fabric-a2a.mjs": "51b72fe1d80649d659fcad3a8f96370db3476c4a7b30cbabd8522c138be69346",
|
|
4009
|
+
"fabric-page-links.mjs": "a8834926e9d4bbf7c4857f551451d2ebf6ac8873bbccaaf89c5e30cf45b17ee6",
|
|
4010
|
+
"fabric-lock.mjs": "ac219ab0a326d5c43c7c4c255e0c3843d4794de795a60e3af661e7d5350ea052",
|
|
4011
|
+
"fabric-usage.mjs": "1c4d18004639409624ac3516a02f2b60bbf55abdb612e9a96c37e83666eb87a7",
|
|
4012
|
+
"fabric-backups.mjs": "cfd8c9085e3d597a783099c2bd2ffe2f3faa0daaadab46dedd30523894cfeb06",
|
|
4013
|
+
"fabric-descriptor.mjs": "84c35c7ba0ae09fde1dc3f6ab796cd578d008707e77815a0f7b7a80306fa4cce",
|
|
4014
|
+
"fabric-context.mjs": "1112d695e872f9825cbc94f08484974bff3880087bc29b8be59f885f03e8f7be",
|
|
4015
|
+
"fabric-auth.mjs": "782c2aa732f8320f604194660117640a7ea145e12b8155c04e88cef7c516a5b3",
|
|
4016
|
+
"fabric-events.mjs": "c463d6ffcae24229a5c608d8fcea4268d2ccc5024b19fa10e5a452284068fa71",
|
|
4017
|
+
"fabric-logs.mjs": "58467e5719c16e099d05bbedec47a95712506ab03c33f3cd4be1252e8498a946",
|
|
4018
|
+
"fabric-supervisors.mjs": "d45c38de0cb3ed85e083496c2d3c51dab004454accdd87f4762a512a6d442573",
|
|
4019
|
+
"fabric-service.mjs": "6aa673657427211b491a9b63f94b9847a7e6aa3ee1b7353e1af11f46469803a3",
|
|
4020
|
+
"fabric-profiles.mjs": "314376cf2deb87f235fb18b11d3b532e211f585b9af0a9302b5f4cc39971b532",
|
|
4021
|
+
"fabric-interop-rules.mjs": "0933a8cd9567cd39f05575167d92dfef0745aaa591779419e84f94be18b01177",
|
|
4022
|
+
"fabric-interop.mjs": "b66fccf4edc1448b3cda31cf036a789574cf07b5044d8433e4a5bf12a9c9aa7a",
|
|
4023
|
+
"fabric-hub.mjs": "c005115a864cfd67daa4862097bb14acfe5a60bf653897e5bf8a570dd80f1070",
|
|
4024
|
+
"fabric-board-shapes.mjs": "c1db7b7bd0d58d82bb6d8dfbc3cd1547a256a333052d367e82ff1b3edf2804d7",
|
|
4025
|
+
"fabric-board-link.mjs": "ed93ad15e0f48627e545b561c4a562007d1b256c55f650126333aa6566876b12",
|
|
4026
|
+
"fabric-board-session.mjs": "9a70016543dded33bbbfe9dc7705e684387abd1d889598b579f1ca26927023cb",
|
|
4027
|
+
"fabric-board-fixture.mjs": "83c4b7e33ee47b68385faf2569d38d5c8a0c5cb0f8656090f41e1b0eed2f436e",
|
|
4028
|
+
"fabric-board.mjs": "9e7bfe4d412521982224ef12bebfc2ff255eeeaf8d96cf40234ac18de68e7a59",
|
|
4029
|
+
"fabric-http.mjs": "f886bcb49c4353d039387ac9568738dda26697a94dd07fc6a6204500f0bc27bf",
|
|
4030
|
+
"fabric-openrouter.mjs": "ab6c4c7251d563980d895061ee19976bde2f364c21b6354efdd9a9027b1ea139",
|
|
4031
|
+
"fabric-package.schema.json": "2ba799f576fad0f608029223c4ccccd122152f1c5dbfa8ce8b31cd63ac92ad44",
|
|
4032
|
+
"share-registration.schema.json": "34b3997eeaf936ebc861342a23045634a7c18c19f01d7ed4198310d81989f5b0"
|
|
3945
4033
|
}
|
|
3946
4034
|
},
|
|
3947
4035
|
"data": {
|
|
@@ -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. Ships in one plugin with adapting-projects-to-fabric, whose scripts it reuses.
|
|
6
6
|
metadata:
|
|
7
7
|
author: PassionCode.ai
|
|
8
|
-
version: "0.
|
|
8
|
+
version: "0.54.0"
|
|
9
9
|
contract-version: "0.1.0"
|
|
10
|
-
contract-commit: "
|
|
10
|
+
contract-commit: "00e050ba7429c0c60261cec871779e0fd6aeeb28"
|
|
11
11
|
---
|
|
12
12
|
|
|
13
13
|
# Creating Fabric-compatible agents
|
|
@@ -136,7 +136,7 @@ python3 <plugin-dir>/skills/adapting-projects-to-fabric/scripts/adapt_project.py
|
|
|
136
136
|
The effect class is declared, never defaulted: ask the author what the agent does outside itself when the request
|
|
137
137
|
does not say. Cost presets are only for a run whose cost varies; their amounts come from the author or measured runs.
|
|
138
138
|
|
|
139
|
-
Pin exactly contract `0.1.0` at commit `
|
|
139
|
+
Pin exactly contract `0.1.0` at commit `00e050ba7429c0c60261cec871779e0fd6aeeb28` and
|
|
140
140
|
read the pinned guide before implementing protocol details. If this skill is installed
|
|
141
141
|
without its sibling, the scaffolder is absent: create the bundle by hand from the pinned
|
|
142
142
|
contract's `docs/guides/connecting-compatible-agents.md` and mark the structural check
|
|
@@ -18,7 +18,7 @@ compatibility: >-
|
|
|
18
18
|
this plugin; Codex/Kimi use command hooks; Hermes/OpenCode/OpenClaw/Pi use native context plugins after opt-in.
|
|
19
19
|
metadata:
|
|
20
20
|
author: PassionCode.ai
|
|
21
|
-
version: "0.
|
|
21
|
+
version: "0.54.0"
|
|
22
22
|
---
|
|
23
23
|
|
|
24
24
|
# Working with Fabric
|
|
@@ -238,7 +238,9 @@ already approved.
|
|
|
238
238
|
`{kind: "mandate/1", by: "operator", task, instruction, at, source?, expiresAt, scope, chain}`, `instruction` his
|
|
239
239
|
words verbatim (at most 500 characters), `at` when he said them, `source` where he said them when they reached you
|
|
240
240
|
relayed, `expiresAt` at most seven days on, `scope` no wider than his words (`capabilities` or `actions`, `effects`,
|
|
241
|
-
`perCallMaxUsd`, `projects`), `chain` starting with you.
|
|
241
|
+
`perCallMaxUsd`, `projects`), `chain` starting with you. For a job asking the operator at a step, name the job's capability in
|
|
242
|
+
`capabilities` without `actions`: the step is read with its request key as the action, unknown ahead
|
|
243
|
+
(DEC-0141 MJ-1). Each hand-over appends the agent you call to `chain`, with a
|
|
242
244
|
narrower `scope` when you hand on less, never wider; at most four agents; `source` never changes down the chain. It
|
|
243
245
|
travels in the call's `_meta.mandate` or as `call.mandate` of Fabric's `agent.call` once Fabric's hub forwards it
|
|
244
246
|
(CT-125), never in a tool's arguments; the service kits state and hand it on (`operator_mandate(source=)`,
|
|
@@ -259,7 +261,7 @@ already approved.
|
|
|
259
261
|
covers the part, else pass the refusal to the agent that called you; only the agent leading the task asks him, once,
|
|
260
262
|
about that part alone (MD-4). A
|
|
261
263
|
caller outside the machine's trust (T2) gets nothing from a mandate.
|
|
262
|
-
- The contract's decision for request `519d7b21` is DEC-0126 (OA-1 to OA-7),
|
|
264
|
+
- The contract's decision for request `519d7b21` is DEC-0126 (OA-1 to OA-7), with
|
|
263
265
|
`mandate/1` (DEC-0125) its machine form and `source` the place it names.
|
|
264
266
|
|
|
265
267
|
### A lane stopped on a limit: restart it at once
|
|
@@ -311,7 +313,7 @@ stuck work to the operator.
|
|
|
311
313
|
| denied | the operator said no | stop; it stands until cleared in Agent access |
|
|
312
314
|
| failed in the client before its answer arrived; outcome unknown | the call may have run | check its effect first (`fabric_work_list`, `fabric_tasks_list`); repeat only with the same idempotency key |
|
|
313
315
|
| Fabric restarted or closed this session; nothing was done | refused before the call ran | call it again once `fabric_whoami` answers |
|
|
314
|
-
| `operator-decision-required` | a service's action needs the operator's decision and none travelled; with `data.detail` `outside-mandate: <part>`, the mandate does not cover that part | ask the operator about this action, then relay their answer by the first route of Step 5 that carries it, the page only when none does; under a mandate, call again within your own mandate if it covers the part, else pass the refusal to the agent that called you, and only the lead asks him, once, about that part alone |
|
|
316
|
+
| `operator-decision-required` | a service's action needs the operator's decision and none travelled; with `data.detail` `outside-mandate: <part>`, the mandate does not cover that part | ask the operator about this action, then relay their answer by the first route of Step 5 that carries it, the page only when none does; under a mandate, call again within your own mandate if it covers the part, else pass the refusal to the agent that called you, and only the lead asks him, once, about that part alone; `mandate-capability-missing`: no mandate covers that step, only his decision |
|
|
315
317
|
| `decision-invalid` | the decision names another action or object, is older than 24 hours, lies over 60 s ahead of the service's clock, or is malformed; or the mandate is (`data.detail` `mandate-malformed`, `-widened`, `-expired`, `-early`, `-not-for-this-call`) | never edit, widen or re-date it; ask the operator again when their words do not cover this call |
|
|
316
318
|
| `approval-required` with `data.actionDigest` | a caller outside the machine's trust (T2): a chat decision changes nothing | an `approval/1` for that digest through the hub |
|
|
317
319
|
| `human-step-required` with `data.humanStep` and `data.url` | a sign-in, a one-time code, consent, payment or top-up, typed secret, spend above a limit or passkey stays the person's | hand the person `data.url`: a path on the service's origin as Fabric Dashboards' `open_link` (`fallback=never`), an https URL as it is; never take the step or ask for the secret; continue other work |
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
|
|
3
3
|
"name": "passioncode",
|
|
4
4
|
"displayName": "PassionCode.ai",
|
|
5
|
-
"version": "0.2.
|
|
5
|
+
"version": "0.2.44",
|
|
6
6
|
"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 at session start that keeps the PassionCode.ai skill set current.",
|
|
7
7
|
"author": {
|
|
8
8
|
"name": "PassionCode.ai",
|