@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.
Files changed (120) hide show
  1. package/CHANGELOG.md +213 -0
  2. package/README.md +13 -2
  3. package/SECURITY.md +23 -8
  4. package/bin/fabric-agent-adapter.js +50 -5
  5. package/package.json +1 -1
  6. package/plugins/fabric-agent-adapter/.claude-plugin/plugin.json +1 -1
  7. package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/SKILL.md +6 -4
  8. package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/references/agents-database.md +4 -4
  9. package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/references/dashboard-links.md +12 -4
  10. package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/references/provider-entry.md +1 -1
  11. package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/references/source-and-instance.md +44 -7
  12. package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/adapt_project.py +107 -2
  13. package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/check_dashboard_link.py +26 -6
  14. package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/fabric_provider.py +9 -1
  15. package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/fabric_secrets.py +24 -5
  16. package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/fabric_source_check.py +57 -76
  17. package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/fabric_source_restart.py +56 -0
  18. package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/fabric_source_tree.py +912 -0
  19. package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/new_agent.py +308 -0
  20. package/plugins/fabric-agent-adapter/skills/building-fabric-services/SKILL.md +8 -4
  21. package/plugins/fabric-agent-adapter/skills/building-fabric-services/recipes/dashboard-only/README.md +67 -0
  22. package/plugins/fabric-agent-adapter/skills/building-fabric-services/recipes/dashboard-only/dashboard_service.py +252 -0
  23. package/plugins/fabric-agent-adapter/skills/building-fabric-services/recipes/dashboard-only/install.mjs +168 -0
  24. package/plugins/fabric-agent-adapter/skills/building-fabric-services/recipes/dashboard-only/install.py +179 -0
  25. package/plugins/fabric-agent-adapter/skills/building-fabric-services/recipes/key-delivery-door/README.md +57 -0
  26. package/plugins/fabric-agent-adapter/skills/building-fabric-services/recipes/key-delivery-door/door.mjs +181 -0
  27. package/plugins/fabric-agent-adapter/skills/building-fabric-services/recipes/key-delivery-door/door.py +237 -0
  28. package/plugins/fabric-agent-adapter/skills/building-fabric-services/recipes/support-agent/README.md +1 -1
  29. package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/agents-database.md +4 -4
  30. package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/board.md +4 -2
  31. package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/dashboard-links.md +12 -4
  32. package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/delegation.md +7 -3
  33. package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/estate-record.md +12 -1
  34. package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/interop.md +54 -3
  35. package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/kit-modules.md +6 -3
  36. package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/lifecycle.md +33 -12
  37. package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/migrating-a-service.md +1 -0
  38. package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/package.md +10 -0
  39. package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/protocol.md +25 -7
  40. package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/remote-placement.md +44 -3
  41. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/check_dashboard_layout.mjs +21 -0
  42. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/check_dashboard_link.py +26 -6
  43. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/check_service.py +24 -33
  44. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/check_service_channel_agent.py +12 -6
  45. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/check_service_rules.py +136 -3
  46. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-board-session.mjs +3 -0
  47. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-board-slots.mjs +1 -0
  48. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-context.mjs +37 -5
  49. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-descriptor.mjs +118 -3
  50. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-holds.mjs +266 -0
  51. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-http.mjs +72 -7
  52. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-hub.mjs +16 -2
  53. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-load.mjs +344 -0
  54. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-package.mjs +98 -1
  55. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-peer-owner.mjs +7 -3
  56. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-protocols.mjs +1 -1
  57. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-secrets.mjs +10 -3
  58. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-service.mjs +11 -0
  59. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-supervisors.mjs +114 -20
  60. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-usage.mjs +8 -1
  61. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_board_session.py +3 -2
  62. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_board_slots.py +2 -0
  63. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_connections.py +97 -3
  64. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_context.py +50 -8
  65. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_descriptor.py +120 -3
  66. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_holds.py +316 -0
  67. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_http.py +40 -3
  68. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_hub.py +15 -2
  69. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_interop.py +2 -2
  70. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_load.py +358 -0
  71. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_package.py +103 -1
  72. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_peer_owner.py +28 -13
  73. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_protocols.py +1 -1
  74. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_secrets.py +24 -5
  75. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_service.py +23 -0
  76. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_supervisors.py +110 -9
  77. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_usage.py +12 -1
  78. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/kit.json +469 -29
  79. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/sample_service.py +4 -1
  80. package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/SKILL.md +18 -6
  81. package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/agent-templates.md +44 -0
  82. package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/agents-database.md +4 -4
  83. package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/communication-agent-template.md +19 -328
  84. package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/dashboard-links.md +12 -4
  85. package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/models-and-providers.md +1 -1
  86. package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/new-agent.md +42 -0
  87. package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/resilience-and-autonomy.md +7 -1
  88. package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/source-and-instance.md +44 -7
  89. package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/where-agents-run.md +28 -0
  90. package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/working-rules.md +54 -0
  91. package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/schemas/template/access.schema.json +50 -0
  92. package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/schemas/template/connections.schema.json +23 -0
  93. package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/schemas/template/data-rooms.schema.json +74 -0
  94. package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/schemas/template/manifest.schema.json +170 -0
  95. package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/schemas/template/template-header.schema.json +25 -0
  96. package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/scripts/check_dashboard_link.py +26 -6
  97. package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/scripts/template_check.py +491 -0
  98. package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/templates/communication-agent/CHANGELOG.md +7 -0
  99. package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/templates/communication-agent/TEMPLATE.md +347 -0
  100. package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/templates/communication-agent/access.json +24 -0
  101. package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/templates/communication-agent/assembly.md +15 -0
  102. package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/templates/communication-agent/checks/acceptance.md +17 -0
  103. package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/templates/communication-agent/connections.json +29 -0
  104. package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/templates/communication-agent/data-rooms.json +9 -0
  105. package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/templates/communication-agent/manifest.json +32 -0
  106. package/plugins/fabric-agent-adapter/skills/observing-products/SKILL.md +1 -1
  107. package/plugins/fabric-agent-adapter/skills/working-with-fabric/SKILL.md +21 -21
  108. package/plugins/fabric-agent-adapter/skills/working-with-fabric/references/agents-database.md +4 -4
  109. package/plugins/fabric-agent-adapter/skills/working-with-fabric/references/cleanup.md +30 -0
  110. package/plugins/fabric-agent-adapter/skills/working-with-fabric/references/on-demand-session.md +2 -1
  111. package/plugins/fabric-agent-adapter/skills/working-with-fabric/references/project-board.md +3 -2
  112. package/plugins/fabric-agent-adapter/skills/working-with-fabric/references/remote-sessions.md +75 -0
  113. package/plugins/fabric-agent-adapter/skills/working-with-fabric/references/session-hooks.md +28 -13
  114. package/plugins/fabric-agent-adapter/skills/working-with-fabric/references/task-context.md +58 -0
  115. package/plugins/fabric-agent-adapter/skills/working-with-fabric/references/waits.md +36 -0
  116. package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session.py +14 -0
  117. package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session_cli.py +143 -36
  118. package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session_env.py +136 -15
  119. package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session_mcp.py +78 -3
  120. 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: `18f8d48762edc5bfb1433657cbfe1c16196df1a6`
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, CT-261). The
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, or the programs `/usr/sbin/netstat
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`, `whoami /user` and one PowerShell `GetOwnerSid` query
55
- per new connection (Node). On macOS the Python reader calibrates once per process with a
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; contract CT-261). A LaunchDaemon's root
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). A token-free loopback door is not yet protected against
63
- another account's process (HA-3, FAA-227). An MCP client (Claude Code, Codex) sends the
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/plugins/installed_plugins.json'), 'utf8'));
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 shadow = path.join(home, '.claude', 'skills', name);
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(home, '.claude', 'skills-shadow-backup', `${name}-${Date.now()}`);
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, '.claude', 'skills') : path.join(home, '.agents', 'skills');
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 (PACKAGE_COMMANDS.includes(process.argv[2])) {
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.79.1",
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.79.1",
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.79.1"
12
+ version: "0.81.0"
13
13
  contract-version: "0.1.0"
14
- contract-commit: "18f8d48762edc5bfb1433657cbfe1c16196df1a6"
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 `18f8d48762edc5bfb1433657cbfe1c16196df1a6`.
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 left to expire at its
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 handler_mismatch. It is a read, not a GUI launch.
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 does not
69
- read the owner yet, FAA-251). An installed host that failed
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 and the same device. `remote_open` instead contains `device_id`,
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.
@@ -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 value:
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);