@passioncode-ai/fabric-agent-adapter 0.12.0 → 0.14.0

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 CHANGED
@@ -1,5 +1,48 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.14.0 - 2026-10-12
4
+
5
+ The contract pin moves to Fabric Agent Contract `main` `0cacd08`, which includes PR #46 (DEC-0046, runner and model
6
+ profiles) and PR #47 (CT-31). Bundles issued under the DEC-0045 revision and every earlier supported one stay valid
7
+ (FAA-30).
8
+
9
+ ### Changed
10
+
11
+ - **Default contract pin `2eb91c0` → `0cacd08b0e95b6abf131eae1206b60792138bc3c`.** `2eb91c0` joins
12
+ `SUPPORTED_CONTRACT_COMMITS`. The revisions between add DEC-0046's model and runner profile schemas, a model
13
+ catalogue, an optional `configuration` on the well-known document and an optional `route` on usage receipts.
14
+ They also tighten `modelClasses` in a grant to the catalogue's closed vocabulary, which the kits do not write. Later
15
+ merges change fixtures and the contract's privacy check only. The provider manifest and the MCP, A2A and
16
+ local-runner profiles are unchanged. The compiled-schema arms at `2ce3922` and `0cacd08` agree.
17
+ - **The shared delegation vectors are regenerated with the contract's verifier at the new pin.** The one case the
18
+ contract used to throw on, a signed token with no `act`, now carries the contract's own answer (`unauthenticated` /
19
+ `malformed-chain`), which is the answer the kits already gave. The other 81 delegation answers are unchanged. The
20
+ tests no longer have a `contractThrew` path; they require that the contract answers every case.
21
+ - `references/protocol.md` names DEC-0046's `configuration` and `route`. The kits do not emit them yet (FAA-19).
22
+
23
+ ## 0.13.0 - 2026-10-12
24
+
25
+ The scaffolder writes what a capability does outside itself, and what a run may cost (FAA-25; target architecture v4,
26
+ Stage 0). The contract pin is unchanged (`2eb91c0`).
27
+
28
+ ### Changed
29
+
30
+ - **`adapt_project.py scaffold` requires `--effect`.** It takes the capability's effect class (`none`, `draft`,
31
+ `publish`, `send`, `reply`, `charge`, `delete`, `merge`, `deploy`, `change-policy`) and writes it to the manifest.
32
+ Before, every capability was written as `effect: "none"`. A scaffold without an effect is refused: the class is
33
+ never defaulted. **This breaks every script that calls the scaffolder without `--effect`.**
34
+
35
+ ### Added
36
+
37
+ - **`--idempotency required|supported|none`**, written to the capability.
38
+ - **`--cost-preset ID MAX_USD LABEL`** (repeatable) and **`--default-cost-preset ID`** write the manifest's
39
+ `costPresets` (DEC-0037): fixed options a launcher offers for a run whose cost varies. The id grammar, the
40
+ amounts, the labels, duplicates and the default are checked.
41
+ - `adapting-projects-to-fabric` and `creating-fabric-agents` show the flags and say the effect is read from the code
42
+ or asked of the author, and amounts are never invented. Scenario evals `effect-declared-not-defaulted` came first
43
+ in both skills.
44
+ - A compiled-schema test shows the written effect, idempotency and presets are valid under the pin.
45
+
3
46
  ## 0.12.0 - 2026-10-12
4
47
 
5
48
  The contract pin moves to Fabric Agent Contract `main` `2eb91c0` (the merge of contract PR #45: DEC-0041…DEC-0045).
package/README.md CHANGED
@@ -189,7 +189,8 @@ python3 "$SKILL_DIR/scripts/adapt_project.py" scaffold /path/to/project \
189
189
  --provider-name "Demo provider" \
190
190
  --capability-id https://agents.example/capabilities/demo \
191
191
  --capability-name demo.run \
192
- --schema-base https://agents.example/fabric
192
+ --schema-base https://agents.example/fabric \
193
+ --effect none
193
194
  python3 "$SKILL_DIR/scripts/adapt_project.py" check /path/to/project \
194
195
  --contract /path/to/fabric-agent-contract --json
195
196
  ```
@@ -202,7 +203,7 @@ Scaffolding is non-destructive by default. It writes only the paths declared in
202
203
  The default pin for new bundles is [`fabric-contract.lock.json`](fabric-contract.lock.json):
203
204
 
204
205
  - version: `0.1.0`
205
- - commit: `2eb91c00e852d9c31d804310d38fad91589fcbac`
206
+ - commit: `0cacd08b0e95b6abf131eae1206b60792138bc3c`
206
207
 
207
208
  Every other mention of the default contract revision — this section, the skills' metadata, the
208
209
  skill card, `adapt_project.py` — must equal it; `python3 test/validate.py` fails a tree
@@ -229,8 +230,8 @@ FABRIC_CONTRACT_OLD=/path/to/legacy-contract FABRIC_CONTRACT_NEW=/path/to/curren
229
230
  ```
230
231
 
231
232
  Without both environment variables, those tests explicitly skip as `NOT_RUN`.
232
- This source checkout is 0.12.0. 0.11.1 is the latest published GitHub/npm release (2026-10-12; 0.7.0 and 0.11.0
233
- were never released); 0.12.0 is published by its `v0.12.0` tag. Source delivery does not update installed skills or live sessions.
233
+ This source checkout is 0.14.0. 0.13.0 is the latest published GitHub/npm release (2026-10-12; 0.7.0 and 0.11.0
234
+ were never released); 0.14.0 is published by its `v0.14.0` tag. Source delivery does not update installed skills or live sessions.
234
235
 
235
236
  ## License
236
237
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@passioncode-ai/fabric-agent-adapter",
3
- "version": "0.12.0",
3
+ "version": "0.14.0",
4
4
  "description": "Create Fabric-compatible agents, run them as always-alive local services with dashboards (fabric-service/0.1), and adapt existing projects to the Fabric Agent Contract. This package is the installer CLI.",
5
5
  "scripts": {
6
6
  "test": "python3 test/validate.py && python3 -m unittest discover -s test -q && node --test 'test/*.test.mjs'"
@@ -3,7 +3,7 @@
3
3
  "name": "fabric-agent-adapter",
4
4
  "displayName": "Fabric Agent Adapter",
5
5
  "description": "Create new Fabric-compatible agents, build them as always-alive local services with dashboards (fabric-service/0.1), and adapt existing projects to the Fabric Agent Contract through evidence-backed MCP, A2A, or local-runner provider bundles.",
6
- "version": "0.12.0",
6
+ "version": "0.14.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.12.0"
8
+ version: "0.14.0"
9
9
  contract-version: "0.1.0"
10
- contract-commit: "2eb91c00e852d9c31d804310d38fad91589fcbac"
10
+ contract-commit: "0cacd08b0e95b6abf131eae1206b60792138bc3c"
11
11
  ---
12
12
 
13
13
  # Adapting projects to Fabric
@@ -70,7 +70,7 @@ Use exactly:
70
70
 
71
71
  - contract version `0.1.0`;
72
72
  - repository `https://github.com/passioncode-ai/fabric-agent-contract`;
73
- - commit `2eb91c00e852d9c31d804310d38fad91589fcbac`.
73
+ - commit `0cacd08b0e95b6abf131eae1206b60792138bc3c`.
74
74
 
75
75
  This is the default for new bundles. When checking an issued legacy bundle, preserve its
76
76
  explicit supported lock and use that exact clean checkout. Never silently upgrade or
@@ -96,14 +96,21 @@ python3 <skill-dir>/scripts/adapt_project.py scaffold <project-root> \
96
96
  --provider-name "Example provider" \
97
97
  --capability-id https://agents.example/capabilities/example \
98
98
  --capability-name example.run \
99
- --schema-base https://agents.example/fabric
99
+ --schema-base https://agents.example/fabric \
100
+ --effect draft
100
101
  ```
101
102
 
102
103
  Valid profiles are `mcp`, `a2a`, and `local-runner`. For `mcp` the capability is served
103
104
  as the MCP tool of its own name (`requiredFeatures: ["tool:<capability>"]`, contract
104
105
  `fabric-interop/0.1`); add `--job` when its work may outlive one request, which writes
105
106
  `"job": true` in the capability's interop block. The helper creates only the locked
106
- target paths. It refuses any collision. Do not use `--force` unless the user explicitly
107
+ target paths. It refuses any collision.
108
+
109
+ `--effect` is required and never defaulted: the capability's class (`none`, `draft`, `publish`, `send`, `reply`,
110
+ `charge`, `delete`, `merge`, `deploy`, `change-policy`), read from what the code does or asked of the author. Choose
111
+ `--idempotency required|supported|none` for an external effect on purpose. When a run's cost varies, give the
112
+ launcher fixed options (DEC-0037): `--cost-preset ID MAX_USD LABEL` per option and `--default-cost-preset ID`; the
113
+ amounts come from the author or measured runs, never invented. Do not use `--force` unless the user explicitly
107
114
  authorizes replacement after the exact conflicting files and diff are shown.
108
115
 
109
116
  The generated zero hash, `.invalid` endpoints, `replace-me` values, generic schemas, and
@@ -6,8 +6,10 @@ from __future__ import annotations
6
6
  import argparse
7
7
  import datetime as dt
8
8
  import json
9
+ import math
9
10
  import os
10
11
  from pathlib import Path
12
+ import re
11
13
  import subprocess
12
14
  import sys
13
15
  from typing import Any, Dict, Iterable, List, Optional, Sequence, Tuple
@@ -16,10 +18,10 @@ from urllib.parse import urlparse
16
18
 
17
19
  CONTRACT_VERSION = "0.1.0"
18
20
  CONTRACT_REPOSITORY = "https://github.com/passioncode-ai/fabric-agent-contract"
19
- CONTRACT_COMMIT = "2eb91c00e852d9c31d804310d38fad91589fcbac"
21
+ CONTRACT_COMMIT = "0cacd08b0e95b6abf131eae1206b60792138bc3c"
20
22
  # #region supported-contract-revisions — docs: README.md#contract-pin
21
23
  # Only this declaration may name a legacy revision in live files.
22
- SUPPORTED_CONTRACT_COMMITS = (CONTRACT_COMMIT, "8e49fbd5fbbf230e32c1969cd24ac3c22422f0a5", "a83bec6b47776af688402c18efec507143061f68", "52da526d7cc1e063d74c5b520dbc03d0ace15246", "78b2da018d2c4bd9ac1bde1dc3b7f1a110a175f5", "623bf61358c339cb10297807b3f024b5d9f1f327", "23f9fda4c05f8a3852246ee98d4f2adf74ed0875", "9091d3d6606b0c4b5591a38c3674356a01fd7f55", "df55c8c54a23251342a7ee57ba95642b7eb39e61", "2ce392291c6668598d12cd38327e24696b5ca15c")
24
+ SUPPORTED_CONTRACT_COMMITS = (CONTRACT_COMMIT, "2eb91c00e852d9c31d804310d38fad91589fcbac", "8e49fbd5fbbf230e32c1969cd24ac3c22422f0a5", "a83bec6b47776af688402c18efec507143061f68", "52da526d7cc1e063d74c5b520dbc03d0ace15246", "78b2da018d2c4bd9ac1bde1dc3b7f1a110a175f5", "623bf61358c339cb10297807b3f024b5d9f1f327", "23f9fda4c05f8a3852246ee98d4f2adf74ed0875", "9091d3d6606b0c4b5591a38c3674356a01fd7f55", "df55c8c54a23251342a7ee57ba95642b7eb39e61", "2ce392291c6668598d12cd38327e24696b5ca15c")
23
25
  # #endregion supported-contract-revisions
24
26
  INTEROP_KEY = "https://fabric.passioncode.ai/agent-contract/extensions/interop/0.1"
25
27
  MCP_REVISION = "2026-07-28"
@@ -208,6 +210,7 @@ def _json_text(value: Any) -> str:
208
210
 
209
211
 
210
212
  def _generated_files(args: argparse.Namespace) -> Dict[str, str]:
213
+ effect, idempotency = _declared_effect(args)
211
214
  for value, label in (
212
215
  (args.provider_id, "provider ID"),
213
216
  (args.capability_id, "capability ID"),
@@ -250,8 +253,8 @@ def _generated_files(args: argparse.Namespace) -> Dict[str, str]:
250
253
  "name": args.capability_name,
251
254
  "inputSchema": _schema_uri(args.schema_base, "schemas/capability-input.schema.json"),
252
255
  "outputSchema": _schema_uri(args.schema_base, "schemas/capability-output.schema.json"),
253
- "effect": "none",
254
- "idempotency": "none",
256
+ "effect": effect,
257
+ "idempotency": idempotency,
255
258
  "dataClasses": ["public"],
256
259
  "profile": _profile(args, args.schema_base),
257
260
  }
@@ -259,6 +262,9 @@ def _generated_files(args: argparse.Namespace) -> Dict[str, str]:
259
262
  }
260
263
  if getattr(args, "job", False):
261
264
  manifest["capabilities"][0]["extensions"] = {INTEROP_KEY: {"job": True}}
265
+ presets = _cost_presets(args)
266
+ if presets:
267
+ manifest["costPresets"] = presets
262
268
  lock = {
263
269
  "contract": "fabric-agent-contract",
264
270
  "version": CONTRACT_VERSION,
@@ -320,6 +326,51 @@ placeholders. Scaffolding is not admission and must not be presented as compatib
320
326
  }
321
327
 
322
328
 
329
+ EFFECT_CLASSES = ("none", "draft", "publish", "send", "reply", "charge", "delete", "merge", "deploy", "change-policy")
330
+ IDEMPOTENCY = ("required", "supported", "none")
331
+ _PRESET_ID = re.compile(r"^[a-z][a-z0-9-]{0,39}$")
332
+
333
+
334
+ def _cost_presets(args: argparse.Namespace) -> List[Dict[str, Any]]:
335
+ """DEC-0037: the fixed options a launcher offers for a task whose cost varies, each for this capability."""
336
+ presets: List[Dict[str, Any]] = []
337
+ seen = set()
338
+ for raw in getattr(args, "cost_preset", None) or []:
339
+ preset_id, max_usd, label = (list(raw) + ["", "", ""])[:3]
340
+ if not _PRESET_ID.match(preset_id or ""):
341
+ raise AdaptationError("cost preset id %r must match ^[a-z][a-z0-9-]{0,39}$" % preset_id)
342
+ if preset_id in seen:
343
+ raise AdaptationError("cost preset %s is named twice" % preset_id)
344
+ try:
345
+ amount = float(max_usd)
346
+ except ValueError:
347
+ raise AdaptationError("cost preset %s: maxUsd %r is not a number" % (preset_id, max_usd)) from None
348
+ if not math.isfinite(amount) or amount < 0:
349
+ raise AdaptationError("cost preset %s: maxUsd must be a finite amount of at least 0" % preset_id)
350
+ if not label.strip():
351
+ raise AdaptationError("cost preset %s needs a label a person reads at launch" % preset_id)
352
+ seen.add(preset_id)
353
+ presets.append({"id": preset_id, "capability": args.capability_name, "label": label.strip(), "maxUsd": amount})
354
+ default = getattr(args, "default_cost_preset", None)
355
+ if default:
356
+ if default not in seen:
357
+ raise AdaptationError("--default-cost-preset %s names no --cost-preset" % default)
358
+ next(p for p in presets if p["id"] == default)["default"] = True
359
+ return presets
360
+
361
+
362
+ def _declared_effect(args: argparse.Namespace) -> Tuple[str, str]:
363
+ """The capability's effect class is declared by the author, never defaulted (target architecture v4, Stage 0)."""
364
+ effect = getattr(args, "effect", None)
365
+ if effect not in EFFECT_CLASSES:
366
+ raise AdaptationError("declare the capability's effect with --effect (%s); it is never defaulted"
367
+ % ", ".join(EFFECT_CLASSES) if not effect else "unknown effect class %r (%s)" % (effect, ", ".join(EFFECT_CLASSES)))
368
+ idempotency = getattr(args, "idempotency", None) or "none"
369
+ if idempotency not in IDEMPOTENCY:
370
+ raise AdaptationError("unknown idempotency %r (%s)" % (idempotency, ", ".join(IDEMPOTENCY)))
371
+ return effect, idempotency
372
+
373
+
323
374
  def scaffold_project(root: Path, args: argparse.Namespace) -> Dict[str, Any]:
324
375
  if not root.is_dir():
325
376
  raise AdaptationError("project path is not a directory: %s" % root)
@@ -544,6 +595,12 @@ def build_parser() -> argparse.ArgumentParser:
544
595
  scaffold.add_argument("--runner-kind", default="replace-me")
545
596
  scaffold.add_argument("--executable-ref", default="urn:executable:replace-me")
546
597
  scaffold.add_argument("--runtime-arg", action="append", default=[])
598
+ scaffold.add_argument("--effect", required=True, choices=EFFECT_CLASSES,
599
+ help="the capability's effect class (governance): what it does outside itself; never defaulted")
600
+ scaffold.add_argument("--idempotency", choices=IDEMPOTENCY, default="none")
601
+ scaffold.add_argument("--cost-preset", nargs=3, action="append", default=[], metavar=("ID", "MAX_USD", "LABEL"),
602
+ help="a fixed cost option a launcher offers (DEC-0037 costPresets); repeat for each")
603
+ scaffold.add_argument("--default-cost-preset", metavar="ID", help="the preset preselected at launch")
547
604
  scaffold.add_argument("--job", action="store_true", help="the capability's work may outlive one request (fabric-interop/0.1 job)")
548
605
  scaffold.add_argument("--force", action="store_true")
549
606
  scaffold.add_argument("--json", action="store_true")
@@ -16,10 +16,10 @@ license: AGPL-3.0-only OR LicenseRef-PassionCode-Commercial
16
16
  compatibility: Python 3.9+ or Node.js 20+ for the kits; the probe needs Python 3.9+. launchd steps are macOS-only (Linux services use lifecycle manager none until a systemd adapter exists). Dashboard handoff optionally uses Fabric Dashboards MCP link/host_status/open; without it, report unresolved host capability. The contract checkout is optional.
17
17
  metadata:
18
18
  author: PassionCode.ai
19
- version: "0.12.0"
19
+ version: "0.14.0"
20
20
  contract-version: "0.1.0"
21
21
  extension: "fabric-service/0.1"
22
- extension-commit: "2eb91c00e852d9c31d804310d38fad91589fcbac"
22
+ extension-commit: "0cacd08b0e95b6abf131eae1206b60792138bc3c"
23
23
  ---
24
24
 
25
25
  # Building Fabric services
@@ -1,11 +1,11 @@
1
1
  # fabric-service/0.1 — wire reference
2
2
 
3
- Pinned to `fabric-agent-contract` commit `2eb91c00e852d9c31d804310d38fad91589fcbac`
3
+ Pinned to `fabric-agent-contract` commit `0cacd08b0e95b6abf131eae1206b60792138bc3c`
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;
7
7
  canonical forms DEC-0043; `degraded[].setup` and `run.mcp.stdio.scope` DEC-0044; delegation sealed and trust tiers
8
- DEC-0045). The contract's schemas are normative;
8
+ DEC-0045; runner and model profiles DEC-0046). The contract's schemas are normative;
9
9
  this page is the working summary. Extension key:
10
10
  `https://fabric.passioncode.ai/agent-contract/extensions/service/0.1`.
11
11
 
@@ -61,6 +61,9 @@ No auth; the Host/Origin guard applies; answered from memory in under 100 ms.
61
61
  - A `degraded[]` entry may carry `setup: true` (DEC-0044): the source waits for a person to finish setting it up
62
62
  (a key not entered, a model not chosen). It is not an outage, and a watcher does not alert on it. The kits pass
63
63
  entries through unchanged.
64
+ - `configuration` (DEC-0046) MAY say which runner and model profile the service resolved and from where
65
+ (`request`, `agent`, `binding`, `estate`); a usage receipt MAY carry the `route` (`subscription` or `key`). The kits
66
+ do not emit either yet (FAA-19).
64
67
  - `build` needs `commit` or `digest` (`sha256:<64 hex>`).
65
68
  - `summary`: at most six tiles; `attention: true` counts toward the host's badge.
66
69
  - `surfaces.events` is required; `dashboard` and `mcp` when the service has them; `usage`
@@ -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.12.0"
8
+ version: "0.14.0"
9
9
  contract-version: "0.1.0"
10
- contract-commit: "2eb91c00e852d9c31d804310d38fad91589fcbac"
10
+ contract-commit: "0cacd08b0e95b6abf131eae1206b60792138bc3c"
11
11
  ---
12
12
 
13
13
  # Creating Fabric-compatible agents
@@ -116,10 +116,14 @@ python3 <plugin-dir>/skills/adapting-projects-to-fabric/scripts/adapt_project.py
116
116
  scaffold <project-root> --profile <mcp|a2a|local-runner> \
117
117
  --provider-id <stable-uri> --provider-name "<name>" \
118
118
  --capability-id <stable-uri> --capability-name <domain.action> \
119
- --schema-base <immutable-base-uri>
119
+ --schema-base <immutable-base-uri> --effect <class> \
120
+ [--idempotency required|supported|none] [--cost-preset <id> <max-usd> "<label>" ... --default-cost-preset <id>]
120
121
  ```
121
122
 
122
- Pin exactly contract `0.1.0` at commit `2eb91c00e852d9c31d804310d38fad91589fcbac` and
123
+ The effect class is declared, never defaulted: ask the author what the agent does outside itself when the request
124
+ does not say. Cost presets are only for a run whose cost varies; their amounts come from the author or measured runs.
125
+
126
+ Pin exactly contract `0.1.0` at commit `0cacd08b0e95b6abf131eae1206b60792138bc3c` and
123
127
  read the pinned guide before implementing protocol details. If this skill is installed
124
128
  without its sibling, the scaffolder is absent: create the bundle by hand from the pinned
125
129
  contract's `docs/guides/connecting-compatible-agents.md` and mark the structural check