@passioncode-ai/fabric-agent-adapter 0.32.0 → 0.32.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +26 -0
- package/package.json +1 -1
- package/plugins/fabric-agent-adapter/.claude-plugin/plugin.json +1 -1
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/SKILL.md +1 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/SKILL.md +1 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/lifecycle.md +58 -16
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-service.mjs +2 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_service.py +4 -3
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/SKILL.md +1 -1
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/SKILL.md +1 -1
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/references/project-board.md +4 -3
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session.py +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.32.1 - 2026-10-14
|
|
4
|
+
|
|
5
|
+
The `building-fabric-services` lifecycle reference is now the rule its readers follow (release iteration 1, N-12;
|
|
6
|
+
operator decision O6). It used to link the organization's private lifecycle contract and say "the contract wins and
|
|
7
|
+
this page is the defect", which left a public reader with a rule they could not open.
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- **`references/lifecycle.md` is normative.** A new *The rules* section states the lifecycle itself: the supervisor
|
|
12
|
+
owns the process (never kill a service; start, stop and restart only through launchd, systemd, the Task Scheduler
|
|
13
|
+
or the node; state is read from the supervisor; restart no sooner than 10 s; off stays off), the descriptor, and
|
|
14
|
+
readiness through the well-known document. A table states each lifecycle rule the kits serve (LC-01, -02, -03, -04,
|
|
15
|
+
-05, -09, -11, -12, -14, -15, -21, -22) in one sentence, with the section and the kit function that serve it. It
|
|
16
|
+
links only public sources: the Fabric Agent Contract's service specification, *Lifecycle*. The `LC-` ids remain
|
|
17
|
+
for provenance.
|
|
18
|
+
- **`working-with-fabric` `references/project-board.md`** no longer says the private page "wins": it is the rule a
|
|
19
|
+
coding agent with this plugin follows, and cites the knowledge base for provenance.
|
|
20
|
+
- The two kits' lifecycle comments (`fabric_service.py`, `fabric-service.mjs`) point at the reference and the
|
|
21
|
+
public specification instead of the private page.
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
|
|
25
|
+
- **`test/validate.py` refuses a skill page that defers to a private source**: a paragraph that names
|
|
26
|
+
`fabric-workspace` and says it "wins", "takes precedence", "prevails" or "is authoritative", or that this page "is
|
|
27
|
+
the defect". The validator's self-test plants one; `test/test_validate.py` covers the cases.
|
|
28
|
+
|
|
3
29
|
## 0.32.0 - 2026-10-14
|
|
4
30
|
|
|
5
31
|
The adapter is pinned at contract `1636fa9`. The board kits now read the operator's hold (DEC-0079) and the owner's
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@passioncode-ai/fabric-agent-adapter",
|
|
3
|
-
"version": "0.32.
|
|
3
|
+
"version": "0.32.1",
|
|
4
4
|
"description": "Create Fabric-compatible agents, run them as always-alive local services with dashboards (fabric-service/0.1), and adapt existing projects to the Fabric Agent Contract. This package is the installer CLI.",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "python3 test/validate.py && python3 -m unittest discover -s test -q && node --test 'test/*.test.mjs'"
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"name": "fabric-agent-adapter",
|
|
4
4
|
"displayName": "Fabric Agent Adapter",
|
|
5
5
|
"description": "Create new Fabric-compatible agents, build them as always-alive local services with dashboards (fabric-service/0.1), and adapt existing projects to the Fabric Agent Contract through evidence-backed MCP, A2A, or local-runner provider bundles; and teach a coding agent to work in a project through Fabric (fabric-mcp, fabric_whoami, claim and release), with Claude Code session hooks.",
|
|
6
|
-
"version": "0.32.
|
|
6
|
+
"version": "0.32.1",
|
|
7
7
|
"author": {
|
|
8
8
|
"name": "PassionCode.ai",
|
|
9
9
|
"url": "https://passioncode.ai/"
|
|
@@ -5,7 +5,7 @@ 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.32.
|
|
8
|
+
version: "0.32.1"
|
|
9
9
|
contract-version: "0.1.0"
|
|
10
10
|
contract-commit: "1636fa97262f4a26e2086572d540c2ee701e0010"
|
|
11
11
|
---
|
|
@@ -18,7 +18,7 @@ license: AGPL-3.0-only OR LicenseRef-PassionCode-Commercial
|
|
|
18
18
|
compatibility: Python 3.9+ or Node.js 20+ for the kits; 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.
|
|
19
19
|
metadata:
|
|
20
20
|
author: PassionCode.ai
|
|
21
|
-
version: "0.32.
|
|
21
|
+
version: "0.32.1"
|
|
22
22
|
contract-version: "0.1.0"
|
|
23
23
|
extension: "fabric-service/0.1"
|
|
24
24
|
extension-commit: "1636fa97262f4a26e2086572d540c2ee701e0010"
|
package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/lifecycle.md
CHANGED
|
@@ -1,31 +1,73 @@
|
|
|
1
1
|
# Lifecycle — supervisor, install, release layout, state
|
|
2
2
|
|
|
3
|
-
This page
|
|
4
|
-
|
|
5
|
-
(
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
| LC-01 quit reaches exit within a deadline | [Shutdown](#shutdown) | `Drain` / `Drain` |
|
|
11
|
-
| LC-03 a job is bounded and exclusive | [one copy under a supervisor](#one-copy-under-a-supervisor) | `hold_single_instance` / `holdSingleInstance` |
|
|
12
|
-
| LC-09 the footprint is declared | the descriptor and the plist, unit or task; the service's own `AGENTS.md` names its label (unit, task) and port | `write_descriptor`, `launchd_plist`, `systemd_unit` / `systemdUnit`, `task_xml` / `taskXml` |
|
|
13
|
-
| LC-11 previous release kept, older pruned | [Install, upgrade, uninstall](#install-upgrade-uninstall) | `prune_releases` / `pruneReleases` |
|
|
14
|
-
| LC-12 every file is bounded | [Logs](#logs) | `RotatingLog`, `cap_stdout_log` / `RotatingLog`, `capStdoutLog` |
|
|
15
|
-
| LC-14 operator intent wins; uninstall is symmetric | [Install, upgrade, uninstall](#install-upgrade-uninstall), [Linux and Windows](#linux-and-windows) | `launchd_install` / `launchdInstall`, `launchd_uninstall` / `launchdUninstall`; `systemd_install` / `systemdInstall`, `task_install` / `taskInstall` and their uninstalls |
|
|
16
|
-
| LC-15 builds clean up after themselves | release pruning runs inside the installer, not later | `prune_releases` / `pruneReleases` |
|
|
3
|
+
**This page is the rule** for anyone who builds, installs or runs a `fabric-service/0.1` service with this
|
|
4
|
+
skill. Everything you must follow is stated here or in the public Fabric Agent Contract's
|
|
5
|
+
[service specification, *Lifecycle*](https://github.com/passioncode-ai/fabric-agent-contract/blob/main/docs/specification/service.md#lifecycle), which this page
|
|
6
|
+
implements and links; where the two disagree, the specification is right and this page is the defect (report it in
|
|
7
|
+
this repository). Inside
|
|
8
|
+
PassionCode.ai the same rules make up the organization's product lifecycle contract, kept in its knowledge base. The
|
|
9
|
+
`LC-` ids below are that contract's, given for provenance only: nothing behind them is needed to follow this page.
|
|
17
10
|
|
|
18
11
|
## Contents
|
|
19
12
|
|
|
20
|
-
- [
|
|
13
|
+
- [The rules](#the-rules)
|
|
14
|
+
- [launchd plist](#launchd-plist-generate-it-with-launchd_plist-or-launchdplist-both-kits-write-the-same-bytes)
|
|
21
15
|
- [Install, upgrade, uninstall](#install-upgrade-uninstall)
|
|
22
16
|
- [One copy under a supervisor](#one-copy-under-a-supervisor)
|
|
17
|
+
- [An app's own descriptor and the node](#an-apps-own-descriptor-and-the-node)
|
|
23
18
|
- [Directories](#directories)
|
|
24
19
|
- [Logs](#logs)
|
|
25
20
|
- [Shutdown](#shutdown)
|
|
26
21
|
- [Being managed by a host lifecycle broker](#being-managed-by-a-host-lifecycle-broker)
|
|
27
22
|
- [Linux and Windows](#linux-and-windows)
|
|
28
23
|
|
|
24
|
+
## The rules
|
|
25
|
+
|
|
26
|
+
**The supervisor owns the process.** A local service runs under exactly one supervisor, named in its descriptor's
|
|
27
|
+
`lifecycle.manager`: `launchd` (macOS), `systemd` (Linux), `task-scheduler` (Windows) or `node` (Project
|
|
28
|
+
Observatory's node supervising its children). An app a person starts says `person` and has no supervisor; where a host
|
|
29
|
+
runs a lifecycle broker, the broker is the one that quits it ([below](#being-managed-by-a-host-lifecycle-broker)).
|
|
30
|
+
Starting, stopping and restarting go through the supervisor and nothing else:
|
|
31
|
+
|
|
32
|
+
- **Never kill a service.** A host, an agent, an installer or the service's own CLI never starts a service process
|
|
33
|
+
directly and never kills or signals one. It asks the supervisor: on macOS `bootout` + `disable` to stop and keep
|
|
34
|
+
off, `enable` + `bootstrap` to start, `kickstart -k` to restart; [Linux and Windows](#linux-and-windows) have
|
|
35
|
+
their own verbs; a node's child is started, stopped and restarted only through the node.
|
|
36
|
+
- **State comes from the supervisor**: `launchctl print`, `systemctl --user show`, the task's state and last run,
|
|
37
|
+
or the node's record. Compare the supervisor's pid with the well-known `process.pid` to see a second copy.
|
|
38
|
+
- **Restart is the supervisor's.** A service that exits is started again, no sooner than 10 s after its last start
|
|
39
|
+
(`KeepAlive` with `ThrottleInterval` 10, `Restart=always` with `RestartSec=10`, the node likewise; a scheduled task
|
|
40
|
+
restarts on failure every minute). A restart onto new code shows as a new `service.build`.
|
|
41
|
+
- **Off stays off.** The operator's off survives a login, an upgrade and a reinstall (LC-14).
|
|
42
|
+
|
|
43
|
+
**The descriptor is the installation.** The installer writes one descriptor per instance (`<id>.<instance>.json`,
|
|
44
|
+
atomic, mode 0600) and the uninstaller removes it; a running service never writes its own, except an app
|
|
45
|
+
([below](#an-apps-own-descriptor-and-the-node)). It names the supervisor and its file (`label` and `plist`, `unit`,
|
|
46
|
+
or `task`; none for `node` and `person`), the `origin` `http://127.0.0.1:<port>` (a port is a claim: an installer
|
|
47
|
+
refuses a port another instance declares), the service's directories, and commands as argument arrays that never
|
|
48
|
+
carry a credential. Only `doctor` and `update` are commands a host may run.
|
|
49
|
+
|
|
50
|
+
**Started is not ready.** Readiness is the well-known document, `GET /.well-known/fabric-service`: answered without
|
|
51
|
+
authentication, from memory, in under 100 ms, with `service.id` and `service.instance` equal to the descriptor's.
|
|
52
|
+
`status` is `starting`, `ready`, `degraded` or `stopping`; `degraded` is always present, and an empty list asserts
|
|
53
|
+
full health. A process that cannot serve does not answer, and a host derives `down`. An install or upgrade polls
|
|
54
|
+
that document until `service.id` matches (at most 40 s) before it calls the service started.
|
|
55
|
+
|
|
56
|
+
| Id | Rule | Here | Kit (Python / Node) |
|
|
57
|
+
|---|---|---|---|
|
|
58
|
+
| LC-01 | **Quit reaches exit within a deadline.** Every trigger (`SIGTERM`, `SIGINT`, logout, the app's own quit) ends the process: within 5 s idle, within 10 s with work in flight, then a hard exit. Nothing starts after quit begins | [Shutdown](#shutdown) | `Drain` / `Drain` |
|
|
59
|
+
| LC-02 | **Every child has an owner, a deadline and a process group.** No subprocess without a timeout; on timeout or the parent's exit the whole group goes; no orphan survives. Locks and shared resources are released in `finally` | your own code | — |
|
|
60
|
+
| LC-03 | **A job is bounded and exclusive.** One copy holds `service.lock`; a scheduled job has a watchdog shorter than its interval, shares its lock with every manual entry point, bounds every network call and records each run's outcome. Long work is journalled before it starts | [One copy under a supervisor](#one-copy-under-a-supervisor) | `hold_single_instance` / `holdSingleInstance` |
|
|
61
|
+
| LC-04 | **Credentials are silent after setup.** No background prompt; a credential read that is denied or absent is cached and backs off until a person acts | [Directories](#directories) (secret files 0600) | — |
|
|
62
|
+
| LC-05 | **Identity is stable across updates.** The supervisor runs a stable executable inside a release directory, never an interpreter by a versioned package-manager path; signed builds are pinned by team and identifier | [launchd plist](#launchd-plist-generate-it-with-launchd_plist-or-launchdplist-both-kits-write-the-same-bytes) | — |
|
|
63
|
+
| LC-09 | **The footprint is declared.** The service's `AGENTS.md` names every resident process, its supervisor label, unit or task and its port, who starts each and who stops it | the descriptor and the supervisor file | `write_descriptor`, `launchd_plist`, `systemd_unit` / `systemdUnit`, `task_xml` / `taskXml` |
|
|
64
|
+
| LC-11 | **An update is a lifecycle event.** An upgrade writes a new immutable release and restarts through the supervisor; the previous release is kept for rollback, older ones are pruned | [Install, upgrade, uninstall](#install-upgrade-uninstall) | `prune_releases` / `pruneReleases` |
|
|
65
|
+
| LC-12 | **Every file is bounded.** Logs rotate by size (5 × 5 MB), mode 0600, never in `/tmp`; temporary leftovers are swept at start | [Logs](#logs) | `RotatingLog`, `cap_stdout_log` / `RotatingLog`, `capStdoutLog` |
|
|
66
|
+
| LC-14 | **The operator's intent wins; uninstall is symmetric.** No install or upgrade re-enables what the operator turned off; uninstall removes what install added (supervisor file, override, descriptor), and data goes only on an explicit purge | [Install, upgrade, uninstall](#install-upgrade-uninstall), [Linux and Windows](#linux-and-windows) | `launchd_install` / `launchdInstall`, `launchd_uninstall` / `launchdUninstall`; `systemd_install` / `systemdInstall`, `task_install` / `taskInstall` and their uninstalls |
|
|
67
|
+
| LC-15 | **Builds clean up after themselves.** At most the current release and the one before it remain; the prune runs inside the installer, not later | [Install, upgrade, uninstall](#install-upgrade-uninstall) | `prune_releases` / `pruneReleases` |
|
|
68
|
+
| LC-21 | **A secret never travels in a command line.** Keys reach the process through its environment, a 0600 file or a vault, never argv; a supervisor file carries no secret | [launchd plist](#launchd-plist-generate-it-with-launchd_plist-or-launchdplist-both-kits-write-the-same-bytes), [Linux and Windows](#linux-and-windows) | the supervisor-file writers refuse a secret-shaped value |
|
|
69
|
+
| LC-22 | **Work survives a restart.** Work started but not finished is journalled on disk, without secrets, before it starts; the restarted service takes it back or cleans it up | [Shutdown](#shutdown) | — (the service's own journal) |
|
|
70
|
+
|
|
29
71
|
## launchd plist (generate it with `launchd_plist` or `launchdPlist`; both kits write the same bytes)
|
|
30
72
|
|
|
31
73
|
| Key | Value | Why |
|
|
@@ -168,7 +210,7 @@ has to survive being driven from outside:
|
|
|
168
210
|
|
|
169
211
|
- **Quit reaches exit through both doors.** A broker asks the platform's own quit first — on
|
|
170
212
|
macOS `NSRunningApplication.terminate()`, the path Cmd+Q and the Dock take — and `SIGTERM` a few
|
|
171
|
-
seconds later. Handle both and exit inside the deadline (
|
|
213
|
+
seconds later. Handle both and exit inside the deadline (LC-01). A desktop app
|
|
172
214
|
that answers `SIGTERM` by closing its window and staying alive is stopped late or not at all.
|
|
173
215
|
- **A background launch stays in the background.** A broker opens the app non-activating
|
|
174
216
|
(`NSWorkspace.OpenConfiguration.activates = false`); do not call `app.focus()`, `show()` on a
|
package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-service.mjs
CHANGED
|
@@ -20,7 +20,8 @@ export const DASHBOARD_EXTENSION_KEY = 'https://fabric.passioncode.ai/agent-cont
|
|
|
20
20
|
export const DASHBOARD_KIT = 'fabric-dashboard-kit@1.1.0';
|
|
21
21
|
export const REFERENCE_THEMES = ['graphite', 'fabric', 'paper', 'forest', 'ocean', 'ember', 'contrast'];
|
|
22
22
|
export const EXIT_ALREADY_RUNNING = 75;
|
|
23
|
-
// Lifecycle
|
|
23
|
+
// Lifecycle rules: references/lifecycle.md (the rule for this kit's users; it implements the public
|
|
24
|
+
// contract's service specification, Lifecycle), the same values as fabric_service.py.
|
|
24
25
|
// A launchd-supervised copy that finds the lock held backs off in-process before its exit 75 (LC-03).
|
|
25
26
|
export const SUPERVISOR_ENV = 'FABRIC_SERVICE_SUPERVISOR';
|
|
26
27
|
export const LOCK_WAIT_SUPERVISED_SECONDS = 300;
|
package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_service.py
CHANGED
|
@@ -49,9 +49,10 @@ DASHBOARD_EXTENSION_KEY = "https://fabric.passioncode.ai/agent-contract/extensio
|
|
|
49
49
|
DASHBOARD_KIT = "fabric-dashboard-kit@1.1.0"
|
|
50
50
|
REFERENCE_THEMES = ("graphite", "fabric", "paper", "forest", "ocean", "ember", "contrast")
|
|
51
51
|
EXIT_ALREADY_RUNNING = 75
|
|
52
|
-
# Lifecycle
|
|
53
|
-
#
|
|
54
|
-
#
|
|
52
|
+
# Lifecycle rules: references/lifecycle.md, the rule for this kit's users, which implements the public
|
|
53
|
+
# contract's service specification (Lifecycle). A launchd-supervised copy that finds the lock held backs
|
|
54
|
+
# off in-process for this long before its exit 75 (LC-03, F8): under KeepAlive with ThrottleInterval 10
|
|
55
|
+
# that is a few starts an hour instead of one every 10 s.
|
|
55
56
|
SUPERVISOR_ENV = "FABRIC_SERVICE_SUPERVISOR"
|
|
56
57
|
LOCK_WAIT_SUPERVISED_SECONDS = 300.0
|
|
57
58
|
LOCK_BACKOFF_FIRST_SECONDS = 0.5
|
|
@@ -5,7 +5,7 @@ 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.32.
|
|
8
|
+
version: "0.32.1"
|
|
9
9
|
contract-version: "0.1.0"
|
|
10
10
|
contract-commit: "1636fa97262f4a26e2086572d540c2ee701e0010"
|
|
11
11
|
---
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
# The project board (`fabric-project-comms/0.1`, the `com.*` tools)
|
|
2
2
|
|
|
3
3
|
Read this when the session starts in a project a Fabric hub serves, when the person asks about board mail, when you
|
|
4
|
-
need something from another Project, or when you wait for an answer.
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
need something from another Project, or when you wait for an answer. This page is the rule a coding agent with this
|
|
5
|
+
plugin follows. Inside PassionCode.ai it implements the knowledge base's
|
|
6
|
+
[agents-first.md, "The project board"](https://github.com/passioncode-ai/fabric-workspace/blob/main/knowledge/agents-first.md#the-project-board)
|
|
7
|
+
(private to the organization), and is kept in step with it; nothing behind that link is needed to follow this page.
|
|
7
8
|
|
|
8
9
|
## Contents
|
|
9
10
|
|
|
@@ -60,7 +60,7 @@ WATCH_MAX_BACKOFF_S = 900
|
|
|
60
60
|
SESSION_ID = re.compile(r"[A-Za-z0-9._:-]{1,128}")
|
|
61
61
|
LOOPBACK_ORIGIN = re.compile(r"http://127\.0\.0\.1:(\d{1,5})")
|
|
62
62
|
LOCAL_ORIGIN = re.compile(r'"origin": "http://(?:127\.0\.0\.1|localhost)')
|
|
63
|
-
VERSION = "0.32.
|
|
63
|
+
VERSION = "0.32.1"
|
|
64
64
|
HOSTS = {"claude": "Claude Code", "codex": "Codex", "kimi": "Kimi Code"}
|
|
65
65
|
# A session Fabric launched. FABRIC_LAUNCHED_SESSION=1 is the marker Fabric is asked to set (need D6); the others are
|
|
66
66
|
# what Fabric already gives a launched session (fabric sessionBundle.ts): only their NAMES are looked at, never a value
|