@passioncode-ai/fabric-agent-adapter 0.79.1 → 0.81.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 +213 -0
- package/README.md +13 -2
- package/SECURITY.md +23 -8
- package/bin/fabric-agent-adapter.js +50 -5
- 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 +6 -4
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/references/agents-database.md +4 -4
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/references/dashboard-links.md +12 -4
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/references/provider-entry.md +1 -1
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/references/source-and-instance.md +44 -7
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/adapt_project.py +107 -2
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/check_dashboard_link.py +26 -6
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/fabric_provider.py +9 -1
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/fabric_secrets.py +24 -5
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/fabric_source_check.py +57 -76
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/fabric_source_restart.py +56 -0
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/fabric_source_tree.py +912 -0
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/new_agent.py +308 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/SKILL.md +8 -4
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/recipes/dashboard-only/README.md +67 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/recipes/dashboard-only/dashboard_service.py +252 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/recipes/dashboard-only/install.mjs +168 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/recipes/dashboard-only/install.py +179 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/recipes/key-delivery-door/README.md +57 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/recipes/key-delivery-door/door.mjs +181 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/recipes/key-delivery-door/door.py +237 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/recipes/support-agent/README.md +1 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/agents-database.md +4 -4
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/board.md +4 -2
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/dashboard-links.md +12 -4
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/delegation.md +7 -3
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/estate-record.md +12 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/interop.md +54 -3
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/kit-modules.md +6 -3
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/lifecycle.md +33 -12
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/migrating-a-service.md +1 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/package.md +10 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/protocol.md +25 -7
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/remote-placement.md +44 -3
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/check_dashboard_layout.mjs +21 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/check_dashboard_link.py +26 -6
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/check_service.py +24 -33
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/check_service_channel_agent.py +12 -6
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/check_service_rules.py +136 -3
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-board-session.mjs +3 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-board-slots.mjs +1 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-context.mjs +37 -5
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-descriptor.mjs +118 -3
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-holds.mjs +266 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-http.mjs +72 -7
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-hub.mjs +16 -2
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-load.mjs +344 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-package.mjs +98 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-peer-owner.mjs +7 -3
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-protocols.mjs +1 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-secrets.mjs +10 -3
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-service.mjs +11 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-supervisors.mjs +114 -20
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-usage.mjs +8 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_board_session.py +3 -2
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_board_slots.py +2 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_connections.py +97 -3
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_context.py +50 -8
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_descriptor.py +120 -3
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_holds.py +316 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_http.py +40 -3
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_hub.py +15 -2
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_interop.py +2 -2
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_load.py +358 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_package.py +103 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_peer_owner.py +28 -13
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_protocols.py +1 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_secrets.py +24 -5
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_service.py +23 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_supervisors.py +110 -9
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_usage.py +12 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/kit.json +469 -29
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/sample_service.py +4 -1
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/SKILL.md +18 -6
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/agent-templates.md +44 -0
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/agents-database.md +4 -4
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/communication-agent-template.md +19 -328
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/dashboard-links.md +12 -4
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/models-and-providers.md +1 -1
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/new-agent.md +42 -0
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/resilience-and-autonomy.md +7 -1
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/source-and-instance.md +44 -7
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/where-agents-run.md +28 -0
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/working-rules.md +54 -0
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/schemas/template/access.schema.json +50 -0
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/schemas/template/connections.schema.json +23 -0
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/schemas/template/data-rooms.schema.json +74 -0
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/schemas/template/manifest.schema.json +170 -0
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/schemas/template/template-header.schema.json +25 -0
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/scripts/check_dashboard_link.py +26 -6
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/scripts/template_check.py +491 -0
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/templates/communication-agent/CHANGELOG.md +7 -0
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/templates/communication-agent/TEMPLATE.md +347 -0
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/templates/communication-agent/access.json +24 -0
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/templates/communication-agent/assembly.md +15 -0
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/templates/communication-agent/checks/acceptance.md +17 -0
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/templates/communication-agent/connections.json +29 -0
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/templates/communication-agent/data-rooms.json +9 -0
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/templates/communication-agent/manifest.json +32 -0
- package/plugins/fabric-agent-adapter/skills/observing-products/SKILL.md +1 -1
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/SKILL.md +21 -21
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/references/agents-database.md +4 -4
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/references/cleanup.md +30 -0
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/references/on-demand-session.md +2 -1
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/references/project-board.md +3 -2
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/references/remote-sessions.md +75 -0
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/references/session-hooks.md +28 -13
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/references/task-context.md +58 -0
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/references/waits.md +36 -0
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session.py +14 -0
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session_cli.py +143 -36
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session_env.py +136 -15
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session_mcp.py +78 -3
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session_paths.py +9 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,218 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.81.0 (2026-11-12)
|
|
4
|
+
|
|
5
|
+
Wave 3 of the backlog sweep of 2026-11-12. **The contract pin moves to `93e7db16` (`v0.1.0+2026-11-08.1`)**: DEC-0149
|
|
6
|
+
(`fabric-source/1`), CT-254 (HA-2's Linux source, the hybrid the kit already read), DEC-0151, DEC-0152, DEC-0155.
|
|
7
|
+
Bundles pinned at `18f8d487` stay valid (FAA-258; impact map `docs/evidence/specs/2026-11-12-faa-258-repin-93e7db16-impact-map.md`).
|
|
8
|
+
New kit modules: `fabric_holds` and `fabric-holds.mjs`. Kit modules change: `fabric_secrets`, `fabric_protocols`,
|
|
9
|
+
`fabric_peer_owner` (`1.2.0+kit.5`), `fabric_package`, `fabric_descriptor`, `fabric_http`, `fabric_supervisors`, the
|
|
10
|
+
facade `fabric_service`, and their Node twins.
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **`npx @passioncode-ai/fabric-agent-adapter new <folder> --id <agent-id> [--profile ...] [--service]`** (North Star G21,
|
|
15
|
+
FAA-270), and `new_agent.py` behind it: one command creates an agent project with the contract built in: the bundle
|
|
16
|
+
pinned at the contract, `fabric.source.json`, a placement guard reading Observatory's `observatory-placement/1` record
|
|
17
|
+
with its tests, and with `--service` the service kit's sample (its modules copied by `fabric_kit.py install`).
|
|
18
|
+
Idempotent, refuses a folder holding other files without `--force`, JSON last. It needs Python 3.9 or later on the
|
|
19
|
+
machine. Turning a sentence into an agent (`new "<sentence>"`, `test`) stays FAA-26, waiting on Fabric's
|
|
20
|
+
`fabric.agent.create`.
|
|
21
|
+
- **`fabric.source.json` (DEC-0149, `fabric-source/1`)** (FAA-261): `fabric_source_tree.py` ports the contract's source
|
|
22
|
+
rules and matches all 140 of its vendored vectors; `fabric_source_check.py check` reads the source a manifest names,
|
|
23
|
+
by path, line and column, never the value; `adapt_project.py source` lays the manifest down with the kit's slots
|
|
24
|
+
(`PUBLISHER_URI` behind the `createdBy` sentinel, `PLACEMENT_RECORD`, and for a service `SUPERVISOR_LABEL` and
|
|
25
|
+
`SERVICE_PORT`; FAA-188's part 2); `pack` writes `secrets[]` and `state[]` from the slots, and FAC-SEM-160 refuses a
|
|
26
|
+
package that differs.
|
|
27
|
+
- **Work held by a service's own limits** (FAA-298): new kit modules `fabric_holds` and `fabric-holds.mjs` (L3). A held
|
|
28
|
+
job says why it waits in `statusMessage` with the contract's pause grammar (the job schema is closed); the operator gets
|
|
29
|
+
one push per hold, and the answer is recorded as a scoped lift that falls back when its window ends; a new probe rule
|
|
30
|
+
`limits.holds`.
|
|
31
|
+
- **Agent template folders** (FAA-265): five JSON Schemas and `template_check.py` in creating-fabric-agents; the
|
|
32
|
+
communication agent template moved into `templates/communication-agent/` with its manifest, connections, access, data
|
|
33
|
+
rooms, checks, assembly and changelog; `validate.py` checks every shipped template folder.
|
|
34
|
+
|
|
35
|
+
### Changed
|
|
36
|
+
|
|
37
|
+
- The probe's `limits.holds` reads a service that declares limits but does not use `fabric_holds` as NOT_RUN with a
|
|
38
|
+
migration note, not FAIL, and counts only code files (a vendored `check_service_rules.py` and docs naming `HoldBook`
|
|
39
|
+
no longer pass it). `LoopbackHTTPServer` and `loopbackServer` read the socket table only for a loopback peer: a peer
|
|
40
|
+
at another address (a `0.0.0.0` bind) is served with no owner under `refuse` and `strict` (the 0.81.0 seam reading).
|
|
41
|
+
- **A kit service refuses a loopback peer of another OS account before it reads the request** (DEC-0154 HA-2 and HA-3,
|
|
42
|
+
FAA-227): `LoopbackHTTPServer` takes `peer_check` and Node's new `loopbackServer(httpServer, {peerCheck})` holds each
|
|
43
|
+
connection paused until the check passes; `refuse` (the default) closes another account's peer and serves the rest,
|
|
44
|
+
`strict` also refuses an owner it cannot tell, `off` reads nothing.
|
|
45
|
+
- `fabric_http` and `fabric-http.mjs` check plain http that reaches loopback by its connected address, `insecure=True` and
|
|
46
|
+
a loopback proxy included (FAA-260). The probe times the well-known answer after the owner check, fails a deeply
|
|
47
|
+
nested body cleanly, compares port claims as ports (`:08080` is `:8080`), names the listener on a failed connect, and
|
|
48
|
+
no longer stops on a well-known body that is not an object; the layout probe opens a loopback page only after its
|
|
49
|
+
listener is this account's (FAA-260).
|
|
50
|
+
- The dashboard link gate opens a raw loopback address in a browser only when `listener_owner` is `this-account` as
|
|
51
|
+
well as the host `not_installed` (FAA-251).
|
|
52
|
+
- LaunchDaemon installs and uninstalls refuse contradicting domains, leftover LaunchAgents, another label's plist and an
|
|
53
|
+
unparseable or binary plist, and remove a plist a failed bootstrap wrote; the probe fails a LaunchDaemon without
|
|
54
|
+
`UserName`, with a `~` command, or loaded in both domains (FAA-286).
|
|
55
|
+
- **The kit's credential detector is the contract's DEC-0151 `SECRET_VALUE`**, in both twins and the three dashboard
|
|
56
|
+
link checkers: a provider prefix counts only in its canonical case and after a boundary; an `sk-` key needs a digit and
|
|
57
|
+
an upper-case letter (16 characters or more) or a digit (32 or more), so `SK-...`, `sk-learn-...` and an `sk-` inside a
|
|
58
|
+
word are no longer keys. Repository names keep their own boundary-free key shapes, as DEC-0151 asks (FAA-258).
|
|
59
|
+
- `fabric_protocols` lists `fabric-source/1`; `fabric_peer_owner` is `1.2.0+kit.5`, one version line with Observatory's
|
|
60
|
+
1.2.0, the Linux reading unchanged (FAA-258).
|
|
61
|
+
- Every vendored contract vector is regenerated at the new pin with the contract's own generators;
|
|
62
|
+
`make_load_vectors.mjs` reads the pin from the lock file. SECURITY.md, lifecycle.md and protocol.md name DEC-0155 RT-3
|
|
63
|
+
for the vouch; SA-6 (DEC-0152) is the accepted board rule (FAA-258).
|
|
64
|
+
|
|
65
|
+
### Fixed
|
|
66
|
+
|
|
67
|
+
- `fabric_source_check.py` refuses a copy without `fabric_source_tree.py` with exit 2 and the file's name, not a
|
|
68
|
+
traceback and exit 1; `pack` (both kits) refuses by name a hand-written `secrets[]` naming a secret `fabric.source.json`
|
|
69
|
+
has no slot for, instead of dropping it (the 0.81.0 seam reading). The Windows job runs the loopback peer check.
|
|
70
|
+
|
|
71
|
+
### Upgrading
|
|
72
|
+
|
|
73
|
+
- A door that gave T0 because `strict` served the peer must also check that the peer's owner is named
|
|
74
|
+
(`peer_owner()` / `connectionOwner`): a peer off loopback has none and is T2. A folder whose `fabric.package.json`
|
|
75
|
+
names secrets its `fabric.source.json` lacks fails `pack` until they are added as `secret` slots.
|
|
76
|
+
- A service that serves loopback HTTP keeps serving its own account's callers; pass `peer_check="strict"` to refuse an
|
|
77
|
+
owner the table cannot tell. A caller of the dashboard link gate passes `listener_owner`. An outside test that runs
|
|
78
|
+
this checkout's probe needs a readable socket table.
|
|
79
|
+
- A service that vendors the kit takes the changed `fabric_secrets`, `fabric_protocols` and `fabric_peer_owner` with
|
|
80
|
+
`fabric_kit.py install`. A string the 0.80 detector called a key may no longer be one (see Changed).
|
|
81
|
+
|
|
82
|
+
## 0.80.2 (2026-11-12)
|
|
83
|
+
|
|
84
|
+
0.80.1 was tagged and never published: its `windows` job (run 43212508702) failed one test, so `release` and `publish`
|
|
85
|
+
were skipped. 0.80.2 is 0.80.1 with that test fixed, and is the release that ships 0.80.0's changes. No module changes.
|
|
86
|
+
|
|
87
|
+
### Fixed
|
|
88
|
+
|
|
89
|
+
- `test_the_launchers_folder_is_macos_only` compared the folder `status` returns, written with the runner's own path
|
|
90
|
+
separators, with a POSIX string; it compares it with `str(Path(...))`.
|
|
91
|
+
|
|
92
|
+
## 0.80.1 (2026-11-12)
|
|
93
|
+
|
|
94
|
+
0.80.0 was tagged and never published: its release run (43209105658) failed `floors`, `python` and `windows` on two
|
|
95
|
+
tests of 0.80.0's own (FAA-239, FAA-240) that held only on macOS, so `release` and `publish` were skipped. 0.80.1 is
|
|
96
|
+
0.80.0 with the two tests fixed, and is the release that ships it. No module changes.
|
|
97
|
+
|
|
98
|
+
### Fixed
|
|
99
|
+
|
|
100
|
+
- `test_a_copy_outside_the_launchers_folder_is_named` wrote `hub.json` where the runner's own platform keeps it while
|
|
101
|
+
`status` read darwin's; it writes it where darwin keeps it.
|
|
102
|
+
- `test_the_launchers_folder_is_macos_only` set a Windows temporary folder as a darwin path, which is not absolute
|
|
103
|
+
there; it uses a POSIX path.
|
|
104
|
+
|
|
105
|
+
## 0.80.0 (2026-11-12)
|
|
106
|
+
|
|
107
|
+
Wave 2 and the first part of wave 3 of the backlog sweep of 2026-11-12. The contract pin stays `18f8d487`. A new kit
|
|
108
|
+
module, `fabric_load` and `fabric-load.mjs` (layer L3). Kit modules change: `fabric_secrets`, `fabric_peer_owner`
|
|
109
|
+
(`1.1.0+kit.4`), `fabric_connections`, `fabric_descriptor`, `fabric_context`, `fabric_supervisors`, `fabric_interop`,
|
|
110
|
+
`fabric_hub`, `fabric_board_slots`, `fabric_board_session`, `fabric_http`, `fabric_usage`, the facade `fabric_service`,
|
|
111
|
+
and their Node twins where they have one.
|
|
112
|
+
|
|
113
|
+
### Added
|
|
114
|
+
|
|
115
|
+
- **Three working rules in working-with-fabric and in a new agent's template** (the contract owner's board request
|
|
116
|
+
`bfc0e14e`, relaying the operator's words of 2026-11-12): the task's context is settled before the first act that
|
|
117
|
+
crosses contexts (`personal`, `org:<orgId>`), asked once when no source settles it, recorded in the handoff and never
|
|
118
|
+
posted to a board thread, following contract CT-292's draft (TC-1 to TC-6) until its DEC lands, the one exception to
|
|
119
|
+
"do not ask" (`references/task-context.md`, FAA-299); a wait is not a stop: each wait has its condition, owner, next
|
|
120
|
+
action and watcher, `fabric_heartbeat` carries `waitingOn`, a turn ends with one line per open wait
|
|
121
|
+
(`references/waits.md`, FAA-300); clean up after yourself: merged worktrees and branches, `git fetch --prune`, unique
|
|
122
|
+
commits archived under `refs/archive/<date>/`, `git ls-files` checked before a folder goes (`references/cleanup.md`,
|
|
123
|
+
FAA-301). creating-fabric-agents carries the three in `references/working-rules.md`. Six scenario evals and
|
|
124
|
+
`test/test_working_rules_prose.py`.
|
|
125
|
+
- **A service install refuses a port another OS account listens on** (FAA-225, its port part), read with
|
|
126
|
+
`fabric_peer_owner`; a port of this account, the system or nobody, or an unreadable table, still passes. The claim
|
|
127
|
+
file waits on contract DEC-0160.
|
|
128
|
+
- creating-fabric-agents: an agent on its own machine refuses to start where Observatory's `placement.json` places it
|
|
129
|
+
elsewhere, starts with no file, reports `placement-unreadable`, and takes keys only from its machine's vault
|
|
130
|
+
(`where-agents-run.md`, FAA-230's placement and vault parts).
|
|
131
|
+
- A recorded working-with-fabric trigger run: 38 of 44 with haiku, above the 85 % gate, USD 0.0122
|
|
132
|
+
(`docs/evidence/evals/2026-11-12-trigger-run.md`, FAA-130); six misses follow FAA-291's shorter description (FAA-305).
|
|
133
|
+
|
|
134
|
+
- **The call queue and load reference functions of DEC-0104** (FAA-289): `fabric_load.py` and `fabric-load.mjs`,
|
|
135
|
+
exported by both facades: `jobCallerKey`, `nextQueuedJob`, `admitJob`, `queueStatusMessage`, `readQueuePosition` as
|
|
136
|
+
the contract's `src/agent-network.ts` answers them at the pin, FAC-SEM-131 to FAC-SEM-133, the `service-load/1` and
|
|
137
|
+
`extensions/load/0.1` shapes, and `loadExtension`. Both twins are held to `test/fixtures/contract/load-vectors.json`,
|
|
138
|
+
written by `make_load_vectors.mjs` from the contract's own vectors, fixtures and functions.
|
|
139
|
+
- **A dashboard-only service recipe** (FAA-268, `recipes/dashboard-only/`): an installer with `--id` and `--folder`,
|
|
140
|
+
a JSON last line and an idempotent repeat, and a runnable entry `check_service.py` passes; the HA-1 claim file waits
|
|
141
|
+
on contract CT-228.
|
|
142
|
+
- **A key-delivery door template** (FAA-238, `recipes/key-delivery-door/`): it prints one `fabric-credential-envelope/1`
|
|
143
|
+
line, writes into no store, and exits as Observatory's `receive --from-command` reads it; built against Observatory's
|
|
144
|
+
V2 spec until OBS-360 ships. The door owner supplies the seal command (the standard library has no HPKE).
|
|
145
|
+
- `context_gate_options` / `contextGateOptions` hand an instance's context and its pin's issuer to the delegation gate:
|
|
146
|
+
an organization instance refuses a token signed by the personal hub key (FAA-150).
|
|
147
|
+
- working-with-fabric: `references/remote-sessions.md`, a session over SSH with no GUI login (never start Fabric, the
|
|
148
|
+
login is the human gate) and one Project worked on from two machines (two boards, ids never cross) (FAA-284).
|
|
149
|
+
|
|
150
|
+
### Changed
|
|
151
|
+
|
|
152
|
+
- **working-with-fabric's description names the relayed operator approval, a usage limit hit mid-run and its
|
|
153
|
+
exclusions (Fabric's own code, agent-sync leases) again**, within the 360-character budget: the trigger run goes from
|
|
154
|
+
86.4 % to 95.7 % with haiku, and 95.7 % with sonnet (`docs/evidence/evals/2026-11-12-faa-305.md`, FAA-305).
|
|
155
|
+
- `remote-placement.md`: a cloud service registered from several hosts keeps one token per host, and revoking one ends
|
|
156
|
+
only its sessions (FAA-280).
|
|
157
|
+
- The Python connections reader refuses a non-ASCII host where every Node from 20 to 26 refuses it, with tables measured
|
|
158
|
+
by `test/fixtures/idna/make_idna_tables.mjs` (no host any Node accepts is refused, over 3,573,756 hosts) (FAA-98).
|
|
159
|
+
- The kit's mandate inputs carry 19 hostile `source` shapes, the vectors regenerated at the pin (FAA-149).
|
|
160
|
+
- `systemd_install` and `task_install` poll the installed answer when given `origin`, as `launchd_install` does
|
|
161
|
+
(FAA-253). A supervised `fabric_context` refusal waits the lock's back-off before it exits 78, so `KeepAlive` no
|
|
162
|
+
longer respawns it every 10 s (FAA-279).
|
|
163
|
+
- Hub and `fabric_http` refusals carry the listener owner's reason, and a restricted table is not retryable (FAA-272).
|
|
164
|
+
- **working-with-fabric finds Claude Code's files where `CLAUDE_CONFIG_DIR` points** (and Switchboard's source folder),
|
|
165
|
+
with the launcher's folder rule; the installer reads the same place (FAA-218, FAA-234). A local Claude Code
|
|
166
|
+
registration is read as Claude Code applies it, keyed by the git root or the folder itself outside git, measured
|
|
167
|
+
with 2.1.299 (`docs/evidence/runs/2026-11-12-faa-235-local-scope-key.md`, FAA-235).
|
|
168
|
+
- Session start warns in one line when the registered `fabric-mcp` is another user's copy (Claude Code, Codex, Kimi
|
|
169
|
+
Code) (FAA-232); `status` checks Codex's and Kimi Code's registrations too (FAA-233) and names an ignored
|
|
170
|
+
`PASSIONCODE_APPLICATIONS` value, a copy outside that folder, your own copy in `~/Applications` and a folder you
|
|
171
|
+
cannot enter (FAA-239, FAA-240).
|
|
172
|
+
- A turn carries its git branch where Fabric 0.3.16 and later take it; `FABRIC_SESSION_NO_BRANCH=1` turns it off
|
|
173
|
+
(FAA-152, its branch part).
|
|
174
|
+
- The Stop hook spools the turn's facts before it reads other repositories, and waits for that read only within its
|
|
175
|
+
1.5 s budget (FAA-161). A turn's Bash calls carry the working folder inside the session folder, so a relative
|
|
176
|
+
`git -C` names the right repository (FAA-184).
|
|
177
|
+
- The retro texts name `agents-db.contribute`, and the agents-database ask gains its edges (FAA-277, items 1 to 4).
|
|
178
|
+
- `test/validate.py` searches the shipped files for a provider token shape, a private key block and a home path, and
|
|
179
|
+
every shipped script that writes `<https>` carries its legend but `fabric-delegation.mjs`, which takes it when it next
|
|
180
|
+
changes (FAA-306) (FAA-212).
|
|
181
|
+
- The nudge notes were re-read against Fabric's tag v0.3.14 (FAA-143).
|
|
182
|
+
- The insight/1 shape follows the estate's spec: `evidence` and `audience` are required, and the kind `market`
|
|
183
|
+
exists (`session-hooks.md`, FAA-217).
|
|
184
|
+
- `dashboard-links.md`: the `unsupported` host state takes no browser fallback, by the operator's rule; macOS never
|
|
185
|
+
reports it (FAA-137).
|
|
186
|
+
- `resilience-and-autonomy.md`: a routine whose result write fails exits non-zero and never logs success (FAA-294,
|
|
187
|
+
item 4).
|
|
188
|
+
|
|
189
|
+
### Fixed
|
|
190
|
+
|
|
191
|
+
- **A usage report with many task runs passes its own FAC-SEM-025** (board request `f5753c5a`): each run and its project
|
|
192
|
+
were rounded to 6 decimals apart, so ten or more runs rounding up summed past the project (2.926432 > 2.926429 on a
|
|
193
|
+
15-language localization). A run's cost now rounds as before but never above its exact cost, so the runs' sum stays
|
|
194
|
+
within the project by construction, in both twins. The projects against the days is the contract's own comparison
|
|
195
|
+
and its fixed tolerance; asked of the contract's owner (FAA-310).
|
|
196
|
+
- **A session that moved into a nested checkout keeps its local registration** (the 0.80.0 seam reading of the
|
|
197
|
+
candidate): FAA-235's one-key rule read a hook's moving `cwd` only, so the Stop and SessionEnd hooks, the deliver
|
|
198
|
+
worker and `watch-mail` took a session registered with `--scope local` at the repository root for unregistered once
|
|
199
|
+
it worked in a submodule or a `.claude/worktrees/` worktree. They also read the key of the folder the session started
|
|
200
|
+
in, `CLAUDE_PROJECT_DIR`, as Claude Code keeps that folder's registration.
|
|
201
|
+
- A refused job answer is named from one read of the job; the responder's shutdown says whether it drains; an epoch
|
|
202
|
+
refusal reads `com.status` before the retry (FAA-186). Provider stems read as the contract reads them, `~` in
|
|
203
|
+
`FABRIC_SERVICES_DIR` is expanded, contract-gated tests are found by `ast` (FAA-189). The channel-agent rule agrees
|
|
204
|
+
with the contract on Python 3.9 and answers NOT_RUN without its schema (FAA-250). ASCII digits only, a header-only
|
|
205
|
+
netstat reads as a restricted table, a hung `launchctl` no longer escapes (FAA-273).
|
|
206
|
+
- The release waits 20 minutes, not 10, for the registry to serve a new version, and the publish job's own limit is 30
|
|
207
|
+
minutes: 0.79.1 was served 6 s after the old window closed and its run read red although it was published (FAA-307).
|
|
208
|
+
- The dashboard link gate reads its handoff as UTF-8 bytes, drops a byte-order mark, caps it at 64 KiB and refuses a
|
|
209
|
+
deep document plainly, so a Cyrillic handoff passes under any code page (FAA-210).
|
|
210
|
+
|
|
211
|
+
### Upgrading
|
|
212
|
+
|
|
213
|
+
- `fabric_descriptor` (and its Node twin) imports `fabric_peer_owner`, and the facades import `fabric_load`: update with
|
|
214
|
+
`fabric_kit.py install`, which brings both along, never a hand copy of one file.
|
|
215
|
+
|
|
3
216
|
## 0.79.1 (2026-11-12)
|
|
4
217
|
|
|
5
218
|
0.79.0 was tagged and never published: its release run (43176165810) failed two jobs, so `release` and `publish` were
|
package/README.md
CHANGED
|
@@ -152,7 +152,9 @@ For provider bundles the adapting skill helps an agent author:
|
|
|
152
152
|
admission, and project binding;
|
|
153
153
|
- keep the agent's source apart from its instance data, with `scripts/fabric_source_check.py check`, which reports a
|
|
154
154
|
home path, a credential-shaped value or a string from the owner's list by path, line and kind and never repeats it
|
|
155
|
-
(FAA-188).
|
|
155
|
+
(FAA-188); under the source manifest `fabric.source.json` (contract DEC-0149, `fabric-source/1`, which
|
|
156
|
+
`scripts/adapt_project.py source` proposes) it reads the source the manifest names with the contract's tree check
|
|
157
|
+
(FAA-261), and `pack` writes the package's `secrets[]` and `state[]` from its slots.
|
|
156
158
|
|
|
157
159
|
The skill never treats generated files as admission. Fabric contract `0.1.0` does not
|
|
158
160
|
yet ship the host registry/runtime needed to connect and authorize a live provider.
|
|
@@ -169,6 +171,7 @@ then restart your agents. Alone, without any account:
|
|
|
169
171
|
npx @passioncode-ai/fabric-agent-adapter # installs every skill into the agents hub ~/.agents/skills
|
|
170
172
|
npx @passioncode-ai/fabric-agent-adapter --prune-shadow # remove ~/.claude/skills copies that shadow the plugin
|
|
171
173
|
npx @passioncode-ai/fabric-agent-adapter --force # update a copy already in the hub (a launcher link is never replaced)
|
|
174
|
+
npx @passioncode-ai/fabric-agent-adapter new ./example-agent --id example-agent [--service] # a new agent project, contract built in
|
|
172
175
|
```
|
|
173
176
|
|
|
174
177
|
A copy already in the hub is left alone unless `--force` is given; with it, the new copy is built beside the old one
|
|
@@ -300,8 +303,16 @@ python3 "$SKILL_DIR/scripts/adapt_project.py" scaffold /path/to/project \
|
|
|
300
303
|
--effect none
|
|
301
304
|
python3 "$SKILL_DIR/scripts/adapt_project.py" check /path/to/project \
|
|
302
305
|
--contract /path/to/fabric-agent-contract --json
|
|
306
|
+
python3 "$SKILL_DIR/scripts/adapt_project.py" source /path/to/project --dry-run # propose fabric.source.json
|
|
307
|
+
python3 "$SKILL_DIR/scripts/new_agent.py" ./example-agent --id example-agent # what `npx ... new` runs
|
|
303
308
|
```
|
|
304
309
|
|
|
310
|
+
`new` (FAA-270) writes a new agent project from the kit's template: the provider bundle pinned to the contract,
|
|
311
|
+
`fabric.source.json`, the placement guard with its tests, and with `--service` the service kit's sample; a repeat
|
|
312
|
+
changes nothing, a folder holding other files is refused without `--force`, and the last line is JSON
|
|
313
|
+
([new-agent.md](plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/new-agent.md)). Inferring the agent
|
|
314
|
+
from a sentence and running its evals (`new "<sentence>"`, `test`) stay with FAA-26.
|
|
315
|
+
|
|
305
316
|
Pack, sign and verify an agent as a `fabric-package/1` folder (contract DEC-0050, DEC-0051; nothing is written without
|
|
306
317
|
`--apply`, and the key file is the person's, never made or kept here). First check the checkout, then make the clean
|
|
307
318
|
copy [the package reference](plugins/fabric-agent-adapter/skills/building-fabric-services/references/package.md)
|
|
@@ -330,7 +341,7 @@ Scaffolding is non-destructive by default. It writes only the paths declared in
|
|
|
330
341
|
The default pin for new bundles is [`fabric-contract.lock.json`](fabric-contract.lock.json):
|
|
331
342
|
|
|
332
343
|
- version: `0.1.0`
|
|
333
|
-
- commit: `
|
|
344
|
+
- commit: `93e7db164c7829e2783b4b7705c4113bb5201131`
|
|
334
345
|
|
|
335
346
|
Every other mention of the default contract revision (this section, the skills' metadata, the
|
|
336
347
|
skill card, `adapt_project.py`) must equal it; `python3 test/validate.py` fails a tree
|
package/SECURITY.md
CHANGED
|
@@ -31,7 +31,7 @@ bounded and non-publishing until explicit scoped grants exist in a project bindi
|
|
|
31
31
|
there with an explicit `domain="gui"`, not refused yet, FAA-286 item 21); the job runs as the account its `UserName`
|
|
32
32
|
names. The installer accepts the job's answer only on a connection launchd's own job accepted
|
|
33
33
|
(its pid from `launchctl print system/<label>`) or, where the socket table is restricted, on a port that pid listens
|
|
34
|
-
on (`lsof -p`, the weaker vouch below,
|
|
34
|
+
on (`lsof -p`, the weaker vouch below, DEC-0155 RT-3). The
|
|
35
35
|
token is readable by any process of the same user; the protocol defends against web pages
|
|
36
36
|
and mistakes, not against hostile local code.
|
|
37
37
|
- The kits' connectors (`fabric_http`, `fabric_openrouter` and their Node twins) reach the
|
|
@@ -48,25 +48,40 @@ bounded and non-publishing until explicit scoped grants exist in a project bindi
|
|
|
48
48
|
restricted table, to a port that pid listens on there (the weaker vouch below): a departure from the pinned HA-2 and
|
|
49
49
|
the contract's later RT-3, accepted in principle by the contract owner as DEC-0161 (proposed; the restricted-table vouch
|
|
50
50
|
included) and kept as a departure until the next repin (FAA-288). They read, never change:
|
|
51
|
-
macOS `sysctl net.inet.tcp.pcblist_n` in the process
|
|
51
|
+
macOS `sysctl net.inet.tcp.pcblist_n` in the process (the TCP table in the record layout `netstat -anv` reads;
|
|
52
|
+
the pinned HA-2 table names `netstat -anv` and `pcblist64`, an older layout of the same table), or the programs `/usr/sbin/netstat
|
|
52
53
|
-anv -p tcp` and `/bin/ps -o uid= -p <pid>` (the Node twin always); Linux
|
|
53
54
|
`/proc/net/tcp` and `/proc/net/tcp6`; Windows `GetExtendedTcpTable` and the process
|
|
54
|
-
token (Python), or `netstat -ano
|
|
55
|
-
|
|
55
|
+
token (Python), or `netstat -ano` and one PowerShell `GetOwnerSid` query per new connection,
|
|
56
|
+
with `whoami /user` once a process (Node). On macOS the Python reader calibrates once per process with a
|
|
56
57
|
short-lived child of its own interpreter listening on a random `127.0.0.1` port; when the
|
|
57
58
|
table is restricted (macOS 27.2, FAA-267) the launchd installers also run `/usr/sbin/lsof
|
|
58
59
|
-nP -a -p <pid> -i4TCP -sTCP:LISTEN -Fn` on their own job's pid. That vouch shows the job listens on the port, not that it accepted
|
|
59
|
-
the connection (a more specific bind by root over a wildcard job would pass;
|
|
60
|
+
the connection (a more specific bind by root over a wildcard job would pass; the pin's DEC-0155 RT-3 vouches only for exactly `127.0.0.1`, a departure FAA-258 filed). A LaunchDaemon's root
|
|
60
61
|
installer runs that `lsof` and `ps -o uid=` on its job's pid before it accepts an answer, and reads, never changes, every other
|
|
61
62
|
plist in `/Library/LaunchDaemons` (to find another holder of its label, `plutil -convert xml1` for a binary one in
|
|
62
|
-
Node), `launchctl print system/<label>` and the `UserName`'s uid (`pwd`, or `id -u` in Node).
|
|
63
|
-
another account
|
|
63
|
+
Node), `launchctl print system/<label>` and the `UserName`'s uid (`pwd`, or `id -u` in Node). The kit's loopback server reads the table once per accepted
|
|
64
|
+
connection and closes a peer of another account before reading it (HA-3, FAA-227); a token-free door counts as
|
|
65
|
+
protected only under `peer_check="strict"`, which also refuses an owner it cannot tell. An MCP client (Claude Code, Codex) sends the
|
|
64
66
|
token it was registered with on every later connection without this check (the contract's
|
|
65
67
|
residue "a coding agent's bearer"): when the port may have changed hands, rotate the token
|
|
66
68
|
and re-run `mcp-register` (one written as `references/surfaces-and-auth.md` says checks the
|
|
67
69
|
listener first; the kit ships none). On Linux `/proc` names a socket's creator and
|
|
68
70
|
no pid, so a listening socket root created and handed to another account (systemd
|
|
69
|
-
socket activation with `User=`) reads as root's: a client running as root sends to it.
|
|
71
|
+
socket activation with `User=`) reads as root's: a client running as root sends to it. Such a system unit's
|
|
72
|
+
connection reads as root's until the service accepts it and as the service's account after, so two runs of the
|
|
73
|
+
probe can disagree on it (0.74.0 product reading 10).
|
|
74
|
+
- Credential-free loopback http is checked too (FAA-260 item 16, deliberate): `fabric_http` / `fabric-http.mjs` refuse
|
|
75
|
+
plain http to a loopback listener of another OS account or root (another account's Ollama, a `docker-proxy`) with
|
|
76
|
+
kind `foreign-user` whether or not the request carries a credential, because HA-2 reads nothing such a listener
|
|
77
|
+
answers as the service's, and an answer from another account's process is not this account's data. A caller that
|
|
78
|
+
means to read such a daemon says so with its own connection. With `insecure=True` a URL not spelled as loopback
|
|
79
|
+
(a hosts-file name, `0.0.0.0`, `[::ffff:127.0.0.1]`) and a request through a loopback `http_proxy` are checked by the
|
|
80
|
+
connected peer's address, so the Authorization header never reaches another account's proxy (FAA-260 items 1, 8).
|
|
81
|
+
- The layout probe (`check_dashboard_layout.mjs`) reads a raw loopback page's listener from the socket table just
|
|
82
|
+
before its headless browser opens it, the one-time login URL included, and opens nothing on another account's,
|
|
83
|
+
root's or an untellable listener (FAA-260 items 2, 14). The browser then connects on its own: a listener swapped
|
|
84
|
+
between the read and the browser's connect is a residue, bounded by the login code's 120 s and single use.
|
|
70
85
|
- The agents database (FAA-275): creating-fabric-agents, adapting-projects-to-fabric, building-fabric-services and
|
|
71
86
|
working-with-fabric may send board requests to whichever Project the board lists under an `agents-db.*` capability
|
|
72
87
|
(on an organization's board, any member Project may list it), carrying only what `references/agents-database.md`
|
|
@@ -32,6 +32,9 @@ Usage:
|
|
|
32
32
|
remove plain ~/.claude/skills copies that shadow the installed plugin
|
|
33
33
|
npx @passioncode-ai/fabric-agent-adapter --help
|
|
34
34
|
|
|
35
|
+
~/.claude is the folder CLAUDE_CONFIG_DIR names when it is set, as Claude Code reads it; in a Switchboard home the
|
|
36
|
+
plugin is looked for in SWITCHBOARD_CLAUDE_SOURCE_DIR, where it was installed.
|
|
37
|
+
|
|
35
38
|
Packages (fabric-package/1):
|
|
36
39
|
npx @passioncode-ai/fabric-agent-adapter pack <folder> [--apply]
|
|
37
40
|
list the folder's files, derive agentRef, compute the digest, check it
|
|
@@ -40,6 +43,11 @@ Packages (fabric-package/1):
|
|
|
40
43
|
npx @passioncode-ai/fabric-agent-adapter verify <folder> --trust <trust.json> [--countersigned]
|
|
41
44
|
check the folder and its signatures against the keys you pin
|
|
42
45
|
|
|
46
|
+
A new agent (FAA-270; needs Python 3.9 or later):
|
|
47
|
+
npx @passioncode-ai/fabric-agent-adapter new <folder> --id <agent-id> [--profile mcp|a2a|local-runner] [--service] [--force] [--json]
|
|
48
|
+
create an agent project with the contract built in: the provider bundle, fabric.source.json, the placement
|
|
49
|
+
guard and, with --service, the service kit's sample; the last line is JSON
|
|
50
|
+
|
|
43
51
|
Claude Code (recommended):
|
|
44
52
|
claude plugin marketplace add ${REPO}
|
|
45
53
|
claude plugin install ${PLUGIN_ID}
|
|
@@ -118,9 +126,27 @@ function linkOwner(dest) {
|
|
|
118
126
|
return fs.readlinkSync(dest).split(path.sep).includes('.passioncode') ? 'launcher' : 'other';
|
|
119
127
|
}
|
|
120
128
|
|
|
129
|
+
// Claude Code's folders as the PassionCode launcher finds them (its lib/launcher.js claudeFolders, PC-129; FAA-218):
|
|
130
|
+
// `read`, where the session's skills are, is CLAUDE_CONFIG_DIR when set, else ~/.claude; `write`, where plugins are
|
|
131
|
+
// installed, is the folder the CLI was run against (null: ~/.claude). Inside a Switchboard home CLAUDE_CONFIG_DIR is
|
|
132
|
+
// the home and SWITCHBOARD_CLAUDE_SOURCE_DIR the person's folder it is built from; a home of a Switchboard before
|
|
133
|
+
// 0.7.14 names no source and lies in Switchboard's data folder, and its plugins are then in ~/.claude.
|
|
134
|
+
const SWITCHBOARD_DATA = 'ai.passioncode.fabric-switchboard';
|
|
135
|
+
function claudeFolders(home) {
|
|
136
|
+
const configDir = process.env.CLAUDE_CONFIG_DIR ? path.resolve(process.env.CLAUDE_CONFIG_DIR) : null;
|
|
137
|
+
const named = process.env.SWITCHBOARD_CLAUDE_SOURCE_DIR;
|
|
138
|
+
const inSwitchboard = configDir && configDir.split(/[\\/]/).includes(SWITCHBOARD_DATA);
|
|
139
|
+
const source = !configDir ? null
|
|
140
|
+
: named && path.isAbsolute(named) ? path.resolve(named)
|
|
141
|
+
: inSwitchboard ? path.join(home, '.claude') : null;
|
|
142
|
+
const write = source ? (source === path.join(home, '.claude') ? null : source) : configDir;
|
|
143
|
+
return { read: configDir || path.join(home, '.claude'), write };
|
|
144
|
+
}
|
|
145
|
+
|
|
121
146
|
function pluginInstalled(home) {
|
|
147
|
+
const folders = claudeFolders(home);
|
|
122
148
|
try {
|
|
123
|
-
const data = JSON.parse(fs.readFileSync(path.join(home, '.claude
|
|
149
|
+
const data = JSON.parse(fs.readFileSync(path.join(folders.write || path.join(home, '.claude'), 'plugins/installed_plugins.json'), 'utf8'));
|
|
124
150
|
const records = (data.plugins || {})[PLUGIN_ID] || [];
|
|
125
151
|
return records.some((r) => r.installPath && fs.existsSync(path.join(r.installPath, 'skills')));
|
|
126
152
|
} catch {
|
|
@@ -140,10 +166,11 @@ function skillNames() {
|
|
|
140
166
|
function pruneShadows(home, names) {
|
|
141
167
|
let removed = 0;
|
|
142
168
|
for (const name of names) {
|
|
143
|
-
const
|
|
169
|
+
const skills = claudeFolders(home).read;
|
|
170
|
+
const shadow = path.join(skills, 'skills', name);
|
|
144
171
|
let info;
|
|
145
172
|
try { info = fs.lstatSync(shadow); } catch { continue; }
|
|
146
|
-
const backup = path.join(
|
|
173
|
+
const backup = path.join(skills, 'skills-shadow-backup', `${name}-${Date.now()}`);
|
|
147
174
|
fs.mkdirSync(path.dirname(backup), { recursive: true });
|
|
148
175
|
fs.renameSync(shadow, backup);
|
|
149
176
|
console.log(`Removed shadowing ${info.isSymbolicLink() ? 'link' : 'copy'} ${shadow} (kept at ${backup})`);
|
|
@@ -196,7 +223,7 @@ function main(argv) {
|
|
|
196
223
|
return 1;
|
|
197
224
|
}
|
|
198
225
|
|
|
199
|
-
const destRoot = target === 'claude' ? path.join(home
|
|
226
|
+
const destRoot = target === 'claude' ? path.join(claudeFolders(home).read, 'skills') : path.join(home, '.agents', 'skills');
|
|
200
227
|
const failed = [];
|
|
201
228
|
for (const name of names) {
|
|
202
229
|
const dest = path.join(destRoot, name);
|
|
@@ -235,8 +262,26 @@ function main(argv) {
|
|
|
235
262
|
return 0;
|
|
236
263
|
}
|
|
237
264
|
|
|
265
|
+
// `new` runs the skill's own script (adapting-projects-to-fabric/scripts/new_agent.py), one implementation for the
|
|
266
|
+
// skill and this CLI, under the first Python 3 found on PATH; its output and exit code pass through unchanged.
|
|
267
|
+
function newAgent(args) {
|
|
268
|
+
const { spawnSync } = require('child_process');
|
|
269
|
+
const script = path.join(ROOT, 'plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/new_agent.py');
|
|
270
|
+
const candidates = process.platform === 'win32' ? [['py', ['-3']], ['python', []], ['python3', []]] : [['python3', []], ['python', []]];
|
|
271
|
+
for (const [command, prefix] of candidates) {
|
|
272
|
+
const probe = spawnSync(command, [...prefix, '-c', 'import sys; sys.exit(0 if sys.version_info >= (3, 9) else 1)'], { stdio: 'ignore' });
|
|
273
|
+
if (probe.status !== 0) continue;
|
|
274
|
+
const run = spawnSync(command, [...prefix, script, ...args], { stdio: 'inherit' });
|
|
275
|
+
return run.status === null ? 1 : run.status;
|
|
276
|
+
}
|
|
277
|
+
console.error('error: `new` needs Python 3.9 or later on PATH (python3, or py -3 on Windows)');
|
|
278
|
+
return 2;
|
|
279
|
+
}
|
|
280
|
+
|
|
238
281
|
const PACKAGE_COMMANDS = ['pack', 'sign', 'verify'];
|
|
239
|
-
if (
|
|
282
|
+
if (process.argv[2] === 'new') {
|
|
283
|
+
process.exitCode = newAgent(process.argv.slice(3));
|
|
284
|
+
} else if (PACKAGE_COMMANDS.includes(process.argv[2])) {
|
|
240
285
|
// The package commands live in the kit (fabric-package.mjs), one implementation for the skill and this CLI.
|
|
241
286
|
const kit = path.join(ROOT, 'plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-package.mjs');
|
|
242
287
|
import(require('url').pathToFileURL(kit).href).then((m) => { process.exitCode = m.main(process.argv.slice(2)); });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@passioncode-ai/fabric-agent-adapter",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.81.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; and teach a coding agent to work in a project through Fabric (fabric-mcp, fabric_whoami, claim and release), with Claude Code session hooks; and put an agent on a standing watch over a product (observer mode).",
|
|
6
|
-
"version": "0.
|
|
6
|
+
"version": "0.81.0",
|
|
7
7
|
"author": {
|
|
8
8
|
"name": "PassionCode.ai",
|
|
9
9
|
"url": "https://passioncode.ai/"
|
|
@@ -9,9 +9,9 @@ license: AGPL-3.0-only OR LicenseRef-PassionCode-Commercial
|
|
|
9
9
|
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.
|
|
10
10
|
metadata:
|
|
11
11
|
author: PassionCode.ai
|
|
12
|
-
version: "0.
|
|
12
|
+
version: "0.81.0"
|
|
13
13
|
contract-version: "0.1.0"
|
|
14
|
-
contract-commit: "
|
|
14
|
+
contract-commit: "93e7db164c7829e2783b4b7705c4113bb5201131"
|
|
15
15
|
---
|
|
16
16
|
|
|
17
17
|
# Adapting projects to Fabric
|
|
@@ -84,7 +84,7 @@ Use exactly:
|
|
|
84
84
|
|
|
85
85
|
- contract version `0.1.0`;
|
|
86
86
|
- repository `https://github.com/passioncode-ai/fabric-agent-contract`;
|
|
87
|
-
- commit `
|
|
87
|
+
- commit `93e7db164c7829e2783b4b7705c4113bb5201131`.
|
|
88
88
|
|
|
89
89
|
This is the default for new bundles. When checking an issued legacy bundle, preserve its
|
|
90
90
|
explicit supported lock and use that exact clean checkout. Never silently upgrade or
|
|
@@ -184,7 +184,9 @@ path, line and kind, never repeating a value, and changes nothing. Then move eac
|
|
|
184
184
|
instance value into the instance configuration outside the repository, read through a named variable with a neutral
|
|
185
185
|
default and a committed `*.example`; a key into the secret store, named only. Run the check again until it exits 0; it is a floor under the owner's review, not a secret scanner.
|
|
186
186
|
A value already committed stays in Git history: say so; a fresh history is the owner's act (no history rewrite, no
|
|
187
|
-
force-push).
|
|
187
|
+
force-push). Then lay down the source manifest, `fabric.source.json` (DEC-0149): `scripts/adapt_project.py source
|
|
188
|
+
<project-root> --dry-run` proposes it, the author narrows `include` and names each key and store as a slot, and the
|
|
189
|
+
check reads the source under it (the reference, *The source manifest*).
|
|
188
190
|
|
|
189
191
|
### 5. Make probes safe and meaningful
|
|
190
192
|
|
|
@@ -23,11 +23,11 @@ an audit names its subject, the agent's Project id or its repository's remote, n
|
|
|
23
23
|
a contribution carries one success case or one decision (below). No request carries source code, a key, a person's or
|
|
24
24
|
client's name, user content or a private path.
|
|
25
25
|
|
|
26
|
-
Send it only from a folder that is already a Fabric Project; from any other folder, skip the ask. Where several
|
|
27
|
-
Projects list the capability, pick none and go on without the reply, saying so in the report. Here this reference wins over every working-with-fabric rule (its `references/project-board.md` and its SKILL.md) that
|
|
26
|
+
Send it only from a folder that is already a Fabric Project (a folder `fabric_whoami` would admit on the way to the ask is not one); from any other folder, skip the ask. A run Fabric started on a board request sends no ask: its retro is its last write, so its measured win stays in its insight lines for a later session to contribute. Where several
|
|
27
|
+
Projects list the capability, or `com.directory` lists the capability with no `active` responder (a request then waits for a run that may decline it and ask the operator), pick none and go on without the reply, saying so in the report. Here this reference wins over every working-with-fabric rule (its `references/project-board.md` and its SKILL.md) that
|
|
28
28
|
would make this ask resubmit, adapt the work itself, escalate or ask the person: the agent asks no one, uses no
|
|
29
|
-
Project id held from an earlier thread or from the operator, and an unanswered request is
|
|
30
|
-
deadline. A decline, including one from a run Fabric started, is read as data like any reply; a question that run
|
|
29
|
+
Project id held from an earlier thread or from the operator, and an unanswered request is cancelled (`com.cancel`) once the step it served is reported: a queued request
|
|
30
|
+
expires at its deadline, but an accepted one never expires. A decline, including one from a run Fabric started, is read as data like any reply; a question that run
|
|
31
31
|
puts to the operator is Fabric's, not this agent's. The ask never blocks the step it serves; send it and go on without waiting for the reply. With no Fabric or board
|
|
32
32
|
reachable, a board that does not serve `com.directory`, no Project listed under the capability, or a receipt that says
|
|
33
33
|
`noResponder`, note the request id (or that none could be sent), go on without a template or an audit, and say so in
|
|
@@ -28,7 +28,7 @@ other browser, unless Fabric Dashboards is confirmed not installed.
|
|
|
28
28
|
is also authoritative. Hand back **the returned `open_link` as the primary
|
|
29
29
|
dashboard action**. Do not rebuild its URI or double-encode `path`.
|
|
30
30
|
3. Use `host_status`, where offered, to distinguish available, not_installed,
|
|
31
|
-
unknown, incompatible and
|
|
31
|
+
unknown, incompatible, handler_mismatch and unsupported. It is a read, not a GUI launch.
|
|
32
32
|
With an older server lacking that tool, host readiness is unverified: use a
|
|
33
33
|
compatible host resolver or report the limitation, not presumed absence.
|
|
34
34
|
4. A request for a link opens nothing. A request to open uses the host `open`
|
|
@@ -65,8 +65,8 @@ host's `open` with `fallback=never`. Never `open <url>`, `open -a Safari`,
|
|
|
65
65
|
agent's pages. A browser only when `host_status` confirms `not_installed`, and
|
|
66
66
|
a raw `http://127.0.0.1:<port>` address only after the listener on that port is
|
|
67
67
|
this OS account's (DEC-0154 PL-3 and HA-2; the adapter's `building-fabric-services/scripts/fabric_peer_owner.py`: `server_end` of a connection it opens and sends nothing on; `check_service.py <id>.<instance>` prints it as `listener.owner`): a
|
|
68
|
-
`foreign-user` listener is reported, never opened (the link gate below
|
|
69
|
-
|
|
68
|
+
`foreign-user` listener is reported, never opened (the link gate below takes
|
|
69
|
+
that owner as `listener_owner`). An installed host that failed
|
|
70
70
|
to open is reported, not replaced by HTTP. Links
|
|
71
71
|
carry no token or login code. Every completion and notification action passes
|
|
72
72
|
the Fabric Agent Adapter's link gate before delivery: `check_dashboard_link.py`
|
|
@@ -119,7 +119,15 @@ Its trusted context is supplied by the host, not by untrusted model output:
|
|
|
119
119
|
|
|
120
120
|
`native_link` must equal the resolved open_link, with an available host on the
|
|
121
121
|
viewer's device. `browser_link` must equal http_url, with confirmed not_installed,
|
|
122
|
-
if_absent policy
|
|
122
|
+
if_absent policy, the same device and `"listener_owner": "this-account"`: the kind
|
|
123
|
+
the host read for the listener on http_url's port (`fabric_peer_owner`'s
|
|
124
|
+
`this-account`, `another-account`, `system`, `unknown`, `none`, or
|
|
125
|
+
`check_service.py`'s `foreign-user`); any other, or none given, refuses the
|
|
126
|
+
browser (DEC-0154 PL-3, FAA-251). The native link does not take it: Fabric
|
|
127
|
+
Dashboards checks its own listener. Contract DEC-0123 PL-3 also allows a browser
|
|
128
|
+
on `unsupported` (a system the host does not ship for); the gate treats
|
|
129
|
+
`unsupported` as no fallback, by the operator's rule that a browser opens only on a
|
|
130
|
+
confirmed `not_installed`, and macOS never reports it (FAA-137). `remote_open` instead contains `device_id`,
|
|
123
131
|
`service` and optional `path`, exactly matching the target and resolved page;
|
|
124
132
|
its acceptance does not authorize or deliver it. Enforce grants/dedup/expiry in
|
|
125
133
|
the transport. Unknown or failed host status produces no executable local action.
|
package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/references/provider-entry.md
CHANGED
|
@@ -41,7 +41,7 @@ when the server itself takes an option of that name. `--env` belongs to a stdio
|
|
|
41
41
|
writer does not set it until Fabric reads it. An argument that carries a credential is refused
|
|
42
42
|
(LC-21, FAC-SEM-057): a key goes in `env` as a `secret-ref:<name>`, never in argv.
|
|
43
43
|
|
|
44
|
-
It refuses, with one sentence that never quotes a
|
|
44
|
+
It refuses, with one sentence that never quotes a credential (it names the path and the URIs at fault):
|
|
45
45
|
|
|
46
46
|
- a manifest that does not resolve, or whose `provider.id` is not the entry's
|
|
47
47
|
`providerId`: the two URIs are compared as URIs (FAC-SEM-014);
|