@passioncode-ai/fabric-agent-adapter 0.82.0 → 0.84.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +169 -3
- package/README.md +7 -3
- package/SECURITY.md +4 -3
- 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 +7 -2
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/references/agents-database.md +10 -0
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/references/provider-entry.md +4 -2
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/references/source-and-instance.md +18 -4
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/adapt_project.py +16 -0
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/fabric_identifiers.py +1 -0
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/fabric_provider.py +31 -15
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/fabric_secrets.py +38 -12
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/fabric_source_check.py +21 -148
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/fabric_source_check_cli.py +164 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/SKILL.md +15 -8
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/assets/dashboard-kit/fabric-button.js +4 -3
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/assets/dashboard-kit/fabric-dashboard.css +2 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/assets/dashboard-kit/fabric-dashboard.js +3 -2
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/assets/dashboard-kit/kit.json +14 -4
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/recipes/data-agent/README.md +76 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/recipes/data-agent/config.example.json +28 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/recipes/data-agent/data_agent.py +366 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/recipes/support-agent/README.md +32 -17
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/recipes/support-agent/config.example.json +4 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/recipes/support-agent/support-agent.mjs +115 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/recipes/support-agent/support_agent.py +63 -11
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/a2a.md +98 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/agents-database.md +10 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/board.md +28 -12
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/connectors.md +2 -2
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/dashboard-kit.md +1 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/interop.md +14 -2
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/kit-modules.md +13 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/lifecycle.md +11 -7
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/ops-digest.md +75 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/package.md +7 -5
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/protocol.md +25 -12
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/remote-placement.md +10 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/surfaces-and-auth.md +70 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/check_service_a2a.py +158 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-a2a-server.mjs +275 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-a2a.mjs +2 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-actions.mjs +1 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-auth.mjs +5 -2
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-backups.mjs +1 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-base.mjs +1 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-board-fixture.mjs +15 -2
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-board-link.mjs +55 -11
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-board-session.mjs +47 -31
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-board-shapes.mjs +1 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-board-slots.mjs +25 -7
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-board.mjs +1 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-ceremonies.mjs +1 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-connections.mjs +27 -5
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-context.mjs +1 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-crypto.mjs +1 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-delegation.mjs +4 -2
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-descriptor.mjs +19 -12
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-estate-rules.mjs +1 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-estate.mjs +1 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-events.mjs +7 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-holds.mjs +22 -7
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-http.mjs +4 -5
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-hub.mjs +7 -2
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-identifiers.mjs +1 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-interop-rules.mjs +21 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-interop.mjs +6 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-jsvalue.mjs +1 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-load.mjs +1 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-lock.mjs +16 -2
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-logs.mjs +1 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-mandate.mjs +6 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-openrouter.mjs +25 -3
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-ops-digest.mjs +456 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-package-schema.mjs +1 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-package.mjs +18 -2
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-page-links.mjs +1 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-paths.mjs +19 -5
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-peer-owner.mjs +56 -19
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-platform.mjs +1 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-profiles.mjs +1 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-protocols.mjs +1 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-secrets.mjs +16 -2
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-service.mjs +1 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-share.mjs +1 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-socket-proof.mjs +181 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-supervisors.mjs +20 -15
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-tailnet-identity.mjs +469 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-tailnet.mjs +1 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-time.mjs +1 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-usage.mjs +1 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_a2a.py +3 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_a2a_server.py +405 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_actions.py +1 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_auth.py +7 -2
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_backups.py +1 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_base.py +1 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_board.py +1 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_board_fixture.py +17 -3
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_board_link.py +98 -9
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_board_session.py +37 -48
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_board_shapes.py +1 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_board_slots.py +25 -8
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_ceremonies.py +1 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_connections.py +28 -6
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_context.py +1 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_crypto.py +1 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_delegation.py +19 -13
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_descriptor.py +24 -14
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_estate.py +1 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_estate_rules.py +1 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_events.py +4 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_holds.py +32 -7
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_http.py +19 -16
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_hub.py +10 -5
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_identifiers.py +1 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_interop.py +56 -96
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_interop_rules.py +99 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_jsvalue.py +1 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_load.py +1 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_lock.py +16 -6
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_logs.py +1 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_mandate.py +1 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_openrouter.py +44 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_ops_digest.py +648 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_package.py +17 -4
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_package_schema.py +1 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_page_links.py +1 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_paths.py +21 -5
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_peer_owner.py +108 -108
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_platform.py +1 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_profiles.py +1 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_protocols.py +1 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_secrets.py +38 -12
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_service.py +1 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_share.py +1 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_socket_proof.py +391 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_supervisors.py +19 -16
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_tailnet.py +1 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_tailnet_identity.py +493 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_time.py +1 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_usage.py +1 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/kit.json +632 -104
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/sample-remote-service.mjs +2 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/sample_service.py +17 -7
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/tailnet-groups.schema.json +69 -0
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/SKILL.md +6 -5
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/agent-templates.md +16 -0
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/agents-database.md +10 -0
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/agents-that-answer-people.md +14 -9
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/models-and-providers.md +4 -2
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/source-and-instance.md +18 -4
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/where-agents-run.md +4 -3
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/scripts/admit.py +182 -0
- package/plugins/fabric-agent-adapter/skills/observing-products/SKILL.md +1 -1
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/SKILL.md +1 -1
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/references/agents-database.md +10 -0
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/references/on-demand-session.md +14 -9
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/references/project-board.md +7 -4
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/references/session-hooks.md +10 -5
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session.py +2 -0
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session_cli.py +16 -5
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session_env.py +79 -8
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session_paths.py +147 -45
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
// `x-forwarded-proto` must say https or the request is refused.
|
|
9
9
|
//
|
|
10
10
|
// node sample-remote-service.mjs --origin https://agent.example.com --port 8443 \
|
|
11
|
-
// --token-file token --session-key-file session.key [--tls-cert c.pem --tls-key k.pem]
|
|
11
|
+
// --token-file service.token --session-key-file session.key [--tls-cert c.pem --tls-key k.pem]
|
|
12
|
+
// (service.token, as the Python sample names it: the source check reads a *.token file as a credential, FAA-190.)
|
|
12
13
|
//
|
|
13
14
|
// Secrets come from files (or from the platform as FABRIC_SERVICE_TOKEN / FABRIC_SESSION_KEY),
|
|
14
15
|
// never from an argument vector.
|
package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/sample_service.py
CHANGED
|
@@ -154,7 +154,8 @@ class Service:
|
|
|
154
154
|
# After the lock, like every other piece of state: the responder's checkpoint lives in the data directory.
|
|
155
155
|
self.board = fbd.Responder(hub=fhb.HubClient(), project_id=self.board_project, capabilities=[self.board_capability],
|
|
156
156
|
credential_file=self.board_credential, checkpoint_file=self.data / "board-checkpoint.json",
|
|
157
|
-
handler=self.board_answer, admit=self.board_admit, emit=self.log.append, service=self.id
|
|
157
|
+
handler=self.board_answer, admit=self.board_admit, emit=self.log.append, service=self.id,
|
|
158
|
+
links={"board.slot.moved": "/"}) # the page with Take back (FAA-187 (21))
|
|
158
159
|
# Every button has a tool (DEC-0121): the page's Take back is this relayed action, the operator's decision.
|
|
159
160
|
cap = TAKE_BACK_CAPABILITY
|
|
160
161
|
self.mcp.add_tool(fi.tool_for_capability(cap, cap["input"], cap["output"]), self.board_take_back_tool,
|
|
@@ -205,13 +206,21 @@ class Service:
|
|
|
205
206
|
# (events-and-notifications.md, "Writing an event"; 0.56.0 product r34).
|
|
206
207
|
# The route in words; the tokens by and via, with the caller, in data (product r35).
|
|
207
208
|
act = record if isinstance(record, dict) else {}
|
|
209
|
+
via = act.get("via") if isinstance(act.get("via"), str) else None # a via that is no string names none (FAA-187 (25))
|
|
208
210
|
how = {"dashboard": " on this service's page", "relayed-instruction": " through an agent they told",
|
|
209
|
-
"mandate": " under a standing mandate"
|
|
211
|
+
"mandate": " under a standing mandate", "approval": " with an approval they signed",
|
|
212
|
+
"elicitation": " answering this service's question"}.get(via or "", "")
|
|
213
|
+
who = "An approver" if via == "approval" else "The operator" # an approval is the approver's act (FAA-187 (28))
|
|
210
214
|
value = {"capability": capability, **{k: act[k] for k in ("by", "via", "caller") if isinstance(act.get(k), str)}}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
+
extra = {}
|
|
216
|
+
for kept in (value, {k: v for k, v in value.items() if k != "caller"}): # the caller alone dropped first (24)
|
|
217
|
+
data = {"schema": fbd.EVENT_SCHEMA, "value": kept}
|
|
218
|
+
if not fs.feed_data_problems(data): # data the feed refuses never undoes the answer
|
|
219
|
+
extra = {"data": data}
|
|
220
|
+
break
|
|
221
|
+
# Its own kind (FAA-325): the slot is still away; the kit says board.slot.returned once it is held again.
|
|
222
|
+
self.log.append("board.slot.taken_back", "info", "%s took the %s slot back%s; the service enrolls once the "
|
|
223
|
+
"other session lets it go." % (who, capability, how),
|
|
215
224
|
subject={"type": "capability", "id": capability}, **extra)
|
|
216
225
|
return {"capability": capability, "moved": False}
|
|
217
226
|
|
|
@@ -437,8 +446,9 @@ def make_handler(svc: Service):
|
|
|
437
446
|
try:
|
|
438
447
|
body = json.loads(self.rfile.read(length) or b"{}")
|
|
439
448
|
# The event names who decided, as the tool's does: the operator, at the dashboard (0.56.0 seam r7).
|
|
449
|
+
# caller: a machine id, as the tool records one (FAA-187 (31)): the operator's signed-in dashboard session
|
|
440
450
|
return self._send(200, svc.board_take_back(body.get("capability") if isinstance(body, dict) else None,
|
|
441
|
-
{"by": "operator", "via": "dashboard", "caller": "
|
|
451
|
+
{"by": "operator", "via": "dashboard", "caller": "dashboard:session"}))
|
|
442
452
|
except (ValueError, RecursionError, fs.ServiceError, fbd.BoardError) as exc:
|
|
443
453
|
return self._send(400, {"error": str(exc)})
|
|
444
454
|
if url.path == "/api/emit":
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "urn:passioncode-ai:fabric-agent-adapter:tailnet-groups:1",
|
|
4
|
+
"title": "tailnet-groups/1: who may open which door of a service behind tailscale serve (FAA-324)",
|
|
5
|
+
"description": "The groups file an organization writes for a service that reads Tailscale-User-Login through fabric_tailnet_identity (Python) or fabric-tailnet-identity.mjs (Node). A login is allowed at a door for a capability when it belongs to a group that has a rule for that door listing the capability, or listing \"*\". Logins are compared lowercased as ASCII. One rule this schema cannot say and the module enforces: every rule's group is a key of groups. A file that is unreadable or breaks this schema closes every door of the service until it is fixed.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"required": ["schema", "groups", "rules"],
|
|
9
|
+
"properties": {
|
|
10
|
+
"schema": {
|
|
11
|
+
"const": "tailnet-groups/1"
|
|
12
|
+
},
|
|
13
|
+
"about": {
|
|
14
|
+
"type": "string",
|
|
15
|
+
"maxLength": 1000,
|
|
16
|
+
"description": "Free text for the people who keep the file; the service never reads it."
|
|
17
|
+
},
|
|
18
|
+
"groups": {
|
|
19
|
+
"type": "object",
|
|
20
|
+
"description": "Group name to its members' Tailscale login names.",
|
|
21
|
+
"propertyNames": {
|
|
22
|
+
"pattern": "^[a-z][a-z0-9._:/-]{0,127}$"
|
|
23
|
+
},
|
|
24
|
+
"additionalProperties": {
|
|
25
|
+
"type": "array",
|
|
26
|
+
"items": {
|
|
27
|
+
"type": "string",
|
|
28
|
+
"pattern": "^[A-Za-z0-9._%+-]{1,128}@[A-Za-z0-9.-]{1,125}$"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"rules": {
|
|
33
|
+
"type": "array",
|
|
34
|
+
"items": {
|
|
35
|
+
"type": "object",
|
|
36
|
+
"additionalProperties": false,
|
|
37
|
+
"required": ["group", "door", "capabilities"],
|
|
38
|
+
"properties": {
|
|
39
|
+
"group": {
|
|
40
|
+
"type": "string",
|
|
41
|
+
"pattern": "^[a-z][a-z0-9._:/-]{0,127}$",
|
|
42
|
+
"description": "A key of groups."
|
|
43
|
+
},
|
|
44
|
+
"door": {
|
|
45
|
+
"enum": ["dashboard", "mcp", "service"],
|
|
46
|
+
"description": "dashboard: the service's pages; mcp: its MCP endpoint; service: its own door (an API, a webhook)."
|
|
47
|
+
},
|
|
48
|
+
"capabilities": {
|
|
49
|
+
"oneOf": [
|
|
50
|
+
{
|
|
51
|
+
"const": "*",
|
|
52
|
+
"description": "Every capability of the door."
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"type": "array",
|
|
56
|
+
"minItems": 1,
|
|
57
|
+
"items": {
|
|
58
|
+
"type": "string",
|
|
59
|
+
"pattern": "^[a-z][a-z0-9._:/-]{0,127}$"
|
|
60
|
+
},
|
|
61
|
+
"description": "The capability names the service asks for at this door (a page, a tool, an action)."
|
|
62
|
+
}
|
|
63
|
+
]
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -9,7 +9,7 @@ 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. Ships in one plugin with adapting-projects-to-fabric, whose scripts it reuses.
|
|
10
10
|
metadata:
|
|
11
11
|
author: PassionCode.ai
|
|
12
|
-
version: "0.
|
|
12
|
+
version: "0.84.1"
|
|
13
13
|
contract-version: "0.1.0"
|
|
14
14
|
contract-commit: "93e7db164c7829e2783b4b7705c4113bb5201131"
|
|
15
15
|
---
|
|
@@ -70,10 +70,10 @@ board (or, with a grant, its scan tool over the source projects), and build from
|
|
|
70
70
|
| **Which profile?** | `mcp` when Fabric owns planning and retries and the capability has a schema; `a2a` when the provider owns its task lifecycle end to end; `local-runner` when it is a terminal agent Fabric drives. When unsure, the sibling skill's profile-selection reference is the decision record |
|
|
71
71
|
| **What effects?** | declare anything money-, deletion-, or publication-adjacent now; the host's floor and grants are designed against these declarations, and one discovered late invalidates the admission |
|
|
72
72
|
| **Where does it run, for whom?** | this machine, the person's cloud or an organization's, and in which contexts (`personal`, `org:<id>`): one instance per context, its secrets, spend and journal never crossing into another. Record which capabilities need the person's machine and what they answer when it is off (references/where-agents-run.md) |
|
|
73
|
-
| **Does it run around the clock?** | the uptime category, decided now: **A** when a capability must answer while the owner's machine is off or asleep (a schedule, a chat, callers outside the machine): the agent gets a cloud host from its first release, Heroku unless a reason names another (the person's or an organization's cloud, or PassionCode's for a paid agent), built as an online service (building-fabric-services, the remote placement) and started from the cloud Fabric-server template
|
|
73
|
+
| **Does it run around the clock?** | the uptime category, decided now: **A** when a capability must answer while the owner's machine is off or asleep (a schedule, a chat, callers outside the machine): the agent gets a cloud host from its first release, Heroku unless a reason names another (the person's or an organization's cloud, or PassionCode's for a paid agent), built as an online service (building-fabric-services, the remote placement) and started from the cloud Fabric-server template, whose repository an agent reads from the instance's settings or the estate's agent map, never from this skill; **B** when nothing needs it then: local only, no cloud (a local service, or a CLI or local runner when nothing calls it while the owner is away), reading an A agent's data through that agent's MCP or API. The README names the category (references/where-agents-run.md) |
|
|
74
74
|
| **Whose data does it keep?** | the kind of its data, named in the README: **U** when it keeps data of people outside the owner's team (messages, support requests, reviews, customers' answers), which never enters the source, a copy of it or its tests: the source holds the schema and synthetic fixtures, and a copy starts with an empty store of its own; **O** when it keeps none (a tool or service for other agents, the owner's reports, runs and media). In doubt, U. Either way the data lives in `<agent>-data` (a kit service: the kit's folders; on a cloud host: a durable store), apart from the source, from the first commit (references/source-and-instance.md) |
|
|
75
75
|
| **Who holds the copyright?** | the holder the agent's `LICENSE` names: a new private agent (built for its owner or for a client) gets a `LICENSE` naming that holder in its first commit, all rights reserved unless the owner gives other terms; a public product keeps its own licence (a PassionCode.ai repository: the organization's, step 2) |
|
|
76
|
-
| **Which models?** | per lane (planning, replies, volume work, media): a primary, a fallback of another vendor, an open-vendor model where it reads what users send, the input kinds each reads,
|
|
76
|
+
| **Which models?** | per lane (planning, replies, volume work, media): a primary, a fallback of another vendor, an open-vendor model where it reads what users send, the input kinds each reads, named as catalogue models in the profile, narrowed by classes on the spend policy or grant (references/models-and-providers.md) |
|
|
77
77
|
| **Who pays?** | one key per agent and context with its own ceiling (USD 100 a month by default, more only where the operator names it), from the estate's key manager, read from the environment; checked before every call, the agent's own stop under the key's limit; a personal key never pays for an organization |
|
|
78
78
|
| **How does it learn?** | decisions recorded with reasons, one trace per turn, every failure a planted fixture first, history replayed, learnings proposed and never self-approved (references/orchestration-and-learning.md) |
|
|
79
79
|
| **Text for people?** | if it writes anything a person reads (a reply, a message, a post, a report, a UI string), its prompt forbids the em dash (U+2014) and any substitute for it, and a test fails on U+2014 in new output, from the first commit (the resilience reference, *Text for people*) |
|
|
@@ -139,7 +139,7 @@ lists them. An agent someone builds for themselves, or a private agent built for
|
|
|
139
139
|
is its owner's choice and none of those files is required of it, but its first commit carries a `LICENSE` naming
|
|
140
140
|
the copyright holder the grill recorded (all rights reserved unless the owner gives other terms); it covers the
|
|
141
141
|
holder's own files, and the kit files the adapter copies in keep their own licence (AGPL-3.0-only OR
|
|
142
|
-
LicenseRef-PassionCode-Commercial).
|
|
142
|
+
LicenseRef-PassionCode-Commercial), each service kit module in its own SPDX line.
|
|
143
143
|
|
|
144
144
|
**Every credential the agent uses is read by name, never held as a value**, from its first commit:
|
|
145
145
|
the README's Configure section lists key NAMES. With Project Observatory installed, the agent finds
|
|
@@ -147,7 +147,8 @@ them with `observatory_credentials` and runs under
|
|
|
147
147
|
`python3 "$(project-observatory full-path)/tools/use_secret.py" run --vault-only <project> <NAME> -- <command>`
|
|
148
148
|
(a long-running service: `use_secret.py serve`), with `OBSERVATORY_VAULT_ONLY=1` in its environment.
|
|
149
149
|
Without it, the person's OS keychain or secret manager injects the key into the agent's environment
|
|
150
|
-
at start.
|
|
150
|
+
at start. An uptime A agent's keys are its platform's secrets, set from Observatory or the person's secret manager,
|
|
151
|
+
never a file in the deploy. Either way: no `.env` of its own, no key file beside its data, no value in argv, code,
|
|
151
152
|
configuration or a commit. A workflow names the keys it needs in its checkpoint (`credentials`),
|
|
152
153
|
never their values.
|
|
153
154
|
|
package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/agent-templates.md
CHANGED
|
@@ -42,3 +42,19 @@ has, 2 on a usage error; `--json` prints `{ok, folders: {<folder>: [{level, file
|
|
|
42
42
|
`never_*` keys, `block`, `spend`, `checkedAgainst`) and names each as a warning with its canonical form; a missing
|
|
43
43
|
`direction` or manifest `version` is a warning there and an error otherwise. Use it to migrate a folder, then fix the
|
|
44
44
|
warnings: a template is done when the check passes without it.
|
|
45
|
+
|
|
46
|
+
## The admission check
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
python3 <skill>/scripts/admit.py <project-root> [--service ID[.INSTANCE]] [--services-dir DIR] [--template FOLDER ...]
|
|
50
|
+
[--private-terms FILE] [--json]
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
One command for the checks a new agent passes before it runs (FAA-271): the source check
|
|
54
|
+
(adapting-projects-to-fabric's `fabric_source_check.py check`), the probe (`check_service.py`, with `--service`), this
|
|
55
|
+
template check, strict, on each `--template` folder, and the tracked files against the operator's private-terms list
|
|
56
|
+
(`--private-terms`, else `$PASSIONCODE_PRIVATE_TERMS`, else `~/.config/passioncode/private-terms.txt`). Each step reads
|
|
57
|
+
PASS, FAIL or NOT_RUN; a step with nothing to read (no service, no template, no list) is NOT_RUN, which is not a pass.
|
|
58
|
+
It reads only and prints paths, lines and kinds, never a value or a term. Exit 0: no FAIL and the source check read
|
|
59
|
+
every file; 1: a FAIL; 2: a usage error or a source check that could not run. Which role runs it, and when, is
|
|
60
|
+
Observatory's to decide.
|
package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/agents-database.md
CHANGED
|
@@ -48,6 +48,16 @@ repository-relative path at a commit, an eval report). A decision without a numb
|
|
|
48
48
|
| building-fabric-services | at each release | contribute the release's measured wins as success cases and its new technical decisions |
|
|
49
49
|
| working-with-fabric | the retro after work | a lesson with a measured effect is also a success case: contribute it beside the lesson it records |
|
|
50
50
|
|
|
51
|
+
## A template and the communication prompt
|
|
52
|
+
|
|
53
|
+
For an agent that talks with people on a channel, the database's template and creating-fabric-agents' communication
|
|
54
|
+
agent prompt (`references/communication-agent-template.md`) are read together, never as two templates (the database's
|
|
55
|
+
own rule). Where they disagree, the template wins, in every part and not only the first-release ones: it is the merged
|
|
56
|
+
practice of the agents of its kind. A part the prompt carries and the template lacks (a first-release part, a check, a
|
|
57
|
+
pipeline step) is built too, unless the template rules it out, and goes back to the database as feedback on that
|
|
58
|
+
template (`agents-db.contribute`); the completion report names each such part. Without a template for the kind, the
|
|
59
|
+
prompt is the recipe.
|
|
60
|
+
|
|
51
61
|
## Recording the template
|
|
52
62
|
|
|
53
63
|
An agent built from a template declares it: `provenance.template` with its id and version, so a later template
|
|
@@ -17,8 +17,9 @@ practices arrive with [PR #70](https://github.com/ssheleg/agent-stack/pull/70),
|
|
|
17
17
|
merges. The standard says how such an agent is measured; the practices say what worked and what failed. This page
|
|
18
18
|
lists parts that have a check, and the check that proves each; it leaves to the sources the standard's A2 families and
|
|
19
19
|
per-channel targets, A3 (the rubric), A4 (the judge and its calibration), A6 (answer first, the queue's SLA, a safe
|
|
20
|
-
default) and the rest of its checks that caught real defects,
|
|
21
|
-
|
|
20
|
+
default) and the rest of its checks that caught real defects, its weekly assessment loop, P4's other lane rules (the
|
|
21
|
+
classifier's closed output schema, the writer as the strongest model the latency allows, the fallback writer's
|
|
22
|
+
triggers, the switch rule), and the practices' other items of P1, P3, P5, P6, P7 and P8. Read both sources before you design the reply pipeline. The kit's support-agent recipe (building-fabric-services,
|
|
22
23
|
`recipes/support-agent/`) is one draft-and-check step of such an agent, not the agent: used alone it hands the message
|
|
23
24
|
to a person at the first refusal; with a repairing `on_handoff` it rewrites a refused draft once (`repair=`, a
|
|
24
25
|
spent repair being `fallback: true`: a second refusal, a declined repair or no knowledge on the retry; a `model:*`
|
|
@@ -44,7 +45,8 @@ outage during the repair is a plain hand-off), and research and the hold stay th
|
|
|
44
45
|
on its log line and on any copy a person reads. The turn stores its outcome (answered, general guidance, not covered,
|
|
45
46
|
handed off, small talk, limit, error), the hand-off reason and category, the latency, the knowledge revision the
|
|
46
47
|
answer used, and whether the channel accepted the reply. Conversation text stays inside the agent, redacted, for at
|
|
47
|
-
most 30 days. Check: a test that a turn without a trace id, or with an outcome outside the list, is refused
|
|
48
|
+
most 30 days. Check: a test that a turn without a trace id, or with an outcome outside the list, is refused, and a
|
|
49
|
+
test that conversation text past 30 days is gone and that what is kept carries no address, number or key.
|
|
48
50
|
- **The agent publishes one `support.quality_daily` per closed UTC day and channel** (A5) on its events feed
|
|
49
51
|
(`/fabric/v1/events`; building-fabric-services, `references/events-and-notifications.md`): counts and codes only, the
|
|
50
52
|
judge model and rubric version, and `notMeasured` naming every family not computed that day. A metric not measured is
|
|
@@ -58,7 +60,10 @@ outage during the repair is a plain hand-off), and research and the hold stay th
|
|
|
58
60
|
every reader of the feed it knows of has adopted DEC-0144: the producer then re-publishes its copy from DEC-0144's
|
|
59
61
|
bytes (CT-188), and leaves the calibration out when the judge or the rubric changed after the labelled week. A
|
|
60
62
|
reader validates against its own pinned copy, never one fetched by `$id`: the bytes under the unchanged `$id`
|
|
61
|
-
widened.
|
|
63
|
+
widened. At the contract repin to DEC-0144 (FAA-176), a producer that pinned its own copy keeps its URI in
|
|
64
|
+
`data.schema` and re-publishes that URI's bytes from DEC-0144's: since the `$id` did not change and the bytes only
|
|
65
|
+
widened, every day the older copy accepted still validates, so a reader needs no window of two URIs (this page's
|
|
66
|
+
rule, not the contract's words). Check: the producer's day passes `make_event` and validates against the producer's pinned copy of the
|
|
62
67
|
schema; a planted day that writes 0 for a family not computed fails; and a test runs every `notMeasured` name the
|
|
63
68
|
producer can emit through the published name pattern, iterating the producer's own list.
|
|
64
69
|
|
|
@@ -66,8 +71,8 @@ outage during the repair is a plain hand-off), and research and the hold stay th
|
|
|
66
71
|
|
|
67
72
|
- **A table of lanes** (P4): classifier, writer, fallback writer, checker, research, judge. The checker is another
|
|
68
73
|
vendor than the writer; the judge is another vendor than both, at temperature 0, on an open-vendor model where it
|
|
69
|
-
reads what people wrote.
|
|
70
|
-
primary ([models and providers](models-and-providers.md)); per turn the router picks the checker against the model
|
|
74
|
+
reads what people wrote. Name each lane's catalogue models in the profile, narrowed by the classes the spend policy
|
|
75
|
+
or grant holds (`modelClasses`), where each lane's fallback is another vendor than its primary ([models and providers](models-and-providers.md)); per turn the router picks the checker against the model
|
|
71
76
|
that actually drafted, a fallback writer included ([resilience and
|
|
72
77
|
autonomy](resilience-and-autonomy.md#model-answers-are-untrusted-until-accepted), item 3). A lane that reads what
|
|
73
78
|
people wrote has an open-vendor route (DEC-0083). Check: the config loader refuses a fallback that shares its own
|
|
@@ -110,8 +115,8 @@ outage during the repair is a plain hand-off), and research and the hold stay th
|
|
|
110
115
|
the judge's sample. Check: the daily report counts a hand-off under hand-offs, and the judge's sample builder leaves
|
|
111
116
|
templated replies out.
|
|
112
117
|
- **Hand-offs are counted by cause** (P3): `policy`, `inability` and `infrastructure` apart, so the largest number names
|
|
113
|
-
what to fix. A hold after a checker or model outage, or at the research time cap, is `infrastructure
|
|
114
|
-
after a refusal on the material is `inability`. Check: a checker outage produces `infrastructure` hand-offs and holds,
|
|
118
|
+
what to fix. A hold after a checker or model outage, or at the research time cap, is `infrastructure` (this page's
|
|
119
|
+
reading of P3, not the sources' words); only a hold after a refusal on the material is `inability`. Check: a checker outage produces `infrastructure` hand-offs and holds,
|
|
115
120
|
never `inability`.
|
|
116
121
|
- **A commitments ledger** (P3): every "I will pass this on and come back" is a row with an owner and a due date; the
|
|
117
122
|
week's report counts promises made and closed. Check: a reply that promises to come back without a ledger row is
|
|
@@ -126,7 +131,7 @@ outage during the repair is a plain hand-off), and research and the hold stay th
|
|
|
126
131
|
- **Every abstention and every inability, a hold on the material included, becomes a gap in a queue** (P7), restated
|
|
127
132
|
without personal data and grouped by meaning. When a gap closes, the client asks again and rewrites the held answer
|
|
128
133
|
only once the new answer is confident. Check: a closed gap whose new answer comes back with `answer: false` leaves the
|
|
129
|
-
hold in place, and a hold after a checker outage opens no gap.
|
|
134
|
+
hold in place, and a hold after a checker outage opens no gap (this page's reading of P7, not the sources' words).
|
|
130
135
|
|
|
131
136
|
## Reading the events feed
|
|
132
137
|
|
|
@@ -30,8 +30,10 @@ model, FAC-SEM-112) and `openrouter-attribution.md` (FAC-SEM-098). The kit that
|
|
|
30
30
|
primary is closed (DEC-0083: declare `reads: ["user-content"]` on the profile; FAC-SEM-112 refuses a route without
|
|
31
31
|
one). Closed vendors refuse or hedge on whole topics; the lane must still handle the worst message it receives.
|
|
32
32
|
- **A judge is another vendor than the writer.** A model grading its own family rates its own habits.
|
|
33
|
-
- **
|
|
34
|
-
|
|
33
|
+
- **Name catalogue models in the profile, and classes where the contract keeps them**: at the pin a model profile names
|
|
34
|
+
catalogue model ids per lane, and `modelClasses` lives on the project's spend policy or a grant, which narrow the
|
|
35
|
+
profile's models (building-fabric-services, `references/profiles.md`); the host resolves the model. A hard-coded model
|
|
36
|
+
name in code is a release to change it.
|
|
35
37
|
|
|
36
38
|
## A starting choice
|
|
37
39
|
|
package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/source-and-instance.md
CHANGED
|
@@ -54,7 +54,11 @@ source under it.
|
|
|
54
54
|
|
|
55
55
|
- **The data has a home of its own from the first commit: `<agent>-data`.** For an agent that is not a kit service it
|
|
56
56
|
is a folder beside the checkout, never inside it, and `<AGENT>_DATA_DIR` names it (its default is
|
|
57
|
-
`../<agent>-data
|
|
57
|
+
`../<agent>-data`, resolved against the checkout's root, the folder that holds the agent's source, never the working
|
|
58
|
+
directory). Two sibling checkouts, and a second context's instance from the same checkout (an organization's beside
|
|
59
|
+
the personal one), would share that default: each sets its own `<AGENT>_DATA_DIR` (`../<agent>-data.<instance>`), so
|
|
60
|
+
no copy reads another's data. A kit service keeps the kit's folders above, and on a host whose disk does not persist
|
|
61
|
+
`FABRIC_DATA_HOME` moves its data folder onto a durable volume (building-fabric-services, the remote placement). When the owner keeps the data in Git, it goes to a
|
|
58
62
|
separate private repository named `<agent>-data`, never to the source's repository. On a cloud host that does not
|
|
59
63
|
keep its disk across a restart (Heroku and the like, an uptime A agent's default), the data home is a durable store
|
|
60
64
|
the same variable names (a database, an object store, that private repository); the folder and the kit's folders
|
|
@@ -114,13 +118,22 @@ untracked ones that are not ignored, as they are in the work tree; a staged copy
|
|
|
114
118
|
| Kind | Finds |
|
|
115
119
|
|---|---|
|
|
116
120
|
| `home-path` | an absolute path inside a person's home folder (a web route under `/home/` is reported too; a route under `/users/` is not): `/Users/<name>/`, `/home/<name>/`, `C:\Users\<name>\`, Git Bash's `/c/Users/<name>/`, WSL's `/mnt/c/Users/<name>/`, and the dashed (`-Users-<name>-`, `C--Users-<name>-`) and URL-encoded forms a tool makes of one. The name is ASCII, and in the dashed form it ends at the next dash: an owner whose home name holds a space, a dash, a dot, an underscore or an apostrophe (each a dash in the dashed form; a slashed name also ends at a space) declares it with `home: <name>` in the owner's list, which reads it whole in every form, and one whose home name is not ASCII, or holds another character, lists it as a plain string (a typographic apostrophe a keyboard typed, which the folder's name does not hold, is written as `'` in `home:`; a folder whose name holds `’` is listed as a plain string with `’` kept and declared too with `home: <name>` written with `'`, which reads the dashed forms; its URL-encoded `’` is not read; a name outside ASCII of one or two characters cannot be checked) |
|
|
117
|
-
| `secret` | a value shaped like a credential by the contract's detector (DEC-0044), read line by line: a provider key starting a word, a JWT, a bearer token, a long random value assigned to a credential's name (but a whole Ed25519 signature under a JSON `"sig"`, `"signature"` or `"signatures"` member, as `sign` writes it, which is public); or a private key: a PEM, OpenSSH or PGP block, or a JWK's `d` as a JSON member, escaped JSON or a Python dict; a URL with a password of 8 or more characters mixing letters and digits (a password without a digit or without a letter is not read) or a credential in its query, percent-encoded or not, JSON-escaped (`\u0026`, `\/`) or nested in another URL's parameter (two levels deep); a file that is a credential by its name, as `pack` refuses it (`.env`, `*.pem`, `*.key`, `.npmrc` and the rest), or a kit credential file (`*.token`, `board.credential`) |
|
|
121
|
+
| `secret` | a value shaped like a credential by the contract's detector (DEC-0044), read line by line: a provider key starting a word, a JWT, a bearer token, a long random value assigned to a credential's name (a run of 40 or more characters mixing upper case, lower case and digits, so a shorter key under `X_API_KEY=` is not read; but a whole Ed25519 signature under a JSON `"sig"`, `"signature"` or `"signatures"` member, as `sign` writes it, which is public); or a private key: a PEM, OpenSSH or PGP block, or a JWK's `d` as a JSON member, escaped JSON or a Python dict; a URL with a password of 8 or more characters mixing letters and digits (a password without a digit or without a letter is not read) or a credential in its query, percent-encoded or not, JSON-escaped (`\u0026`, `\/`) or nested in another URL's parameter (two levels deep); a file that is a credential by its name, as `pack` refuses it (`.env`, `*.pem`, `*.key`, `.npmrc` and the rest), or a kit credential file (`*.token`, `board.credential`) |
|
|
118
122
|
| `instance-string` | a string from the owner's list, in a file, in a file's path or in a link's target |
|
|
119
123
|
|
|
120
124
|
```bash
|
|
121
125
|
python3 <plugin-dir>/skills/adapting-projects-to-fabric/scripts/fabric_source_check.py check <project-root>
|
|
122
126
|
```
|
|
123
127
|
|
|
128
|
+
Run it by path, with its scripts folder whole: it loads its siblings (`fabric_source_ignore.py`,
|
|
129
|
+
`fabric_source_homes.py`, `fabric_source_restart.py`, `fabric_source_check_cli.py`, `fabric_source_tree.py` and the
|
|
130
|
+
contract's detector `fabric_secrets.py`) and refuses with exit `2` when one is missing or from another version, a
|
|
131
|
+
detector without one of the public names it reads included (FAA-190). `python3 -m fabric_source_check` writes its own
|
|
132
|
+
`.pyc` before the check turns bytecode off; run by path, it writes nothing. `ROOT` defaults to the current folder.
|
|
133
|
+
`--json` prints one object: `mode` (`git` or a walk), `files`, `findings` (`path`, `line`, `kind`, `rule`),
|
|
134
|
+
`notRead`, `skipped`, `dependencies`, `instanceStrings`, `ownerHomes`, `instanceList`, `blocking` (a finding) and
|
|
135
|
+
`complete` (every file read); `--help` names each option.
|
|
136
|
+
|
|
124
137
|
- It **never repeats** a value it found: no value, no line holding one, no entry of the owner's list and not the
|
|
125
138
|
folder it checks reaches its output. A file's path is checked like its text (line `0` of a finding), and printed
|
|
126
139
|
with an entry of the list as `<line N>`, a home folder's name as `<home>` and each segment holding a
|
|
@@ -167,7 +180,7 @@ python3 <plugin-dir>/skills/adapting-projects-to-fabric/scripts/fabric_source_ch
|
|
|
167
180
|
percent-encodings or an escaped apostrophe (`\'`, `\x27`, `\u0027`, `'`, `'`, `'`), the same separators it is found with in every form; a home that is also your name goes in the list
|
|
168
181
|
as a plain string too, to be found in text. It is `--instance-strings FILE`, else the file
|
|
169
182
|
`FABRIC_INSTANCE_STRINGS` names, else `passioncode-fabric/instance-strings.txt` in the user's configuration folder
|
|
170
|
-
(`$XDG_CONFIG_HOME` when it is absolute, else `~/.config`; `%APPDATA%`
|
|
183
|
+
(`$XDG_CONFIG_HOME` when it is absolute, else `~/.config`; on Windows `%APPDATA%` when it is absolute, else `~\AppData\Roaming`). A list inside the tree is refused, because it would
|
|
171
184
|
travel with the source. A person who already keeps private terms for org-index's `check_private.py`
|
|
172
185
|
(`~/.config/passioncode/private-terms.txt`) passes that file with `--instance-strings`; this check matches a term
|
|
173
186
|
anywhere in a word, and it does not look for e-mail addresses or account ids by their shape.
|
|
@@ -183,7 +196,8 @@ python3 <plugin-dir>/skills/adapting-projects-to-fabric/scripts/fabric_source_ch
|
|
|
183
196
|
repository is not read; `--every-file` and a folder outside Git read its files, its `.git` left out. An exception line (`!`) is refused, since what it would
|
|
184
197
|
bring back would go unread. Name the files, or a folder that holds
|
|
185
198
|
only synthetic vectors, never a whole source folder (a nested repository checked in its own tree is the one
|
|
186
|
-
exception), and say why on the line above. The
|
|
199
|
+
exception), and say why on the line above. The one owner's file named this way is the `LICENSE` (a file, never a
|
|
200
|
+
folder exception): its copyright line names the holder, which is
|
|
187
201
|
authorship and travels with the source, so when the holder is on the owner's list the licence file is named in the ignore
|
|
188
202
|
file by its own path (`LICENSE`, `LICENSE.md`) with that reason (`# the copyright holder is authorship and travels with the source`); no other file of the
|
|
189
203
|
owner's is. A file `pack` refuses by its name cannot ship in a package: a public
|
package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/where-agents-run.md
CHANGED
|
@@ -106,11 +106,12 @@ Every new agent is given an uptime category on its first day, and the README nam
|
|
|
106
106
|
(the places above), and a paid agent stays in PassionCode's cloud (selling an agent, below). On a host with no
|
|
107
107
|
Fabric hub it answers what reaches its own origin (its schedule, its chat, its API); a call Fabric admits needs a
|
|
108
108
|
cloud hub, as running while the laptop is closed says. The agent is an online service (building-fabric-services, the remote placement), often
|
|
109
|
-
alongside its local instance, and starts from the cloud Fabric-server template
|
|
110
|
-
|
|
109
|
+
alongside its local instance, and starts from the cloud Fabric-server template, whose repository an agent reads from
|
|
110
|
+
the instance's settings or the estate's agent map; this public skill names no repository or address for it. Its data lives where the cloud reaches it, in a durable store (a database, an object store, the private
|
|
111
111
|
`<agent>-data` repository), never the host's own folder, which a platform such as Heroku does not keep across a
|
|
112
112
|
restart.
|
|
113
|
-
- **B, local only.** Nothing needs it while the machine sleeps:
|
|
113
|
+
- **B, local only.** Nothing needs it while the machine sleeps: no cloud, and a local service only when other
|
|
114
|
+
agents call it or it shows a page (building-fabric-services Step 0), else a CLI or a local runner. It reads an A agent's
|
|
114
115
|
data through that agent's MCP or API, never from its store.
|
|
115
116
|
|
|
116
117
|
A B agent that gains a capability that must answer around the clock becomes A for that capability; record the change
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""The admission check a new agent passes before it runs (FAA-271, North Star G27, the adapter's part): one command
|
|
3
|
+
that chains the checks this plugin ships and reports each as PASS, FAIL or NOT_RUN.
|
|
4
|
+
|
|
5
|
+
python3 admit.py <project-root> [--service ID[.INSTANCE]] [--services-dir DIR] [--template FOLDER ...]
|
|
6
|
+
[--private-terms FILE] [--json]
|
|
7
|
+
|
|
8
|
+
1. `source`: adapting-projects-to-fabric's `fabric_source_check.py check <project-root>`: no instance data in what
|
|
9
|
+
Git would commit (exit 1 a finding, 2 a file not read; both are no admission).
|
|
10
|
+
2. `probe`: building-fabric-services' `check_service.py <service>` with `--service`: the running service against
|
|
11
|
+
fabric-service/0.1; NOT_RUN for an agent that is no service.
|
|
12
|
+
3. `template`: this skill's `template_check.py` (strict) on each `--template` folder; NOT_RUN with none.
|
|
13
|
+
4. `private-terms`: the tracked files against the operator's private-terms list (the organization's rule P1:
|
|
14
|
+
`--private-terms`, else `$PASSIONCODE_PRIVATE_TERMS`, else `~/.config/passioncode/private-terms.txt`); NOT_RUN where
|
|
15
|
+
there is no list, which is not a pass.
|
|
16
|
+
|
|
17
|
+
It reads only and writes nothing; it never prints a value a check found, a term of the list or a line holding one (the
|
|
18
|
+
checks it runs print paths, lines and kinds). Exit 0: no FAIL and the source check read every file; 1: a FAIL; 2: a
|
|
19
|
+
usage error or a check that could not run. Who runs it and when (Observatory's security role) is not decided here.
|
|
20
|
+
Standard library only.
|
|
21
|
+
"""
|
|
22
|
+
import argparse
|
|
23
|
+
import json
|
|
24
|
+
import os
|
|
25
|
+
from pathlib import Path
|
|
26
|
+
import re
|
|
27
|
+
import subprocess
|
|
28
|
+
import sys
|
|
29
|
+
from typing import Dict, List, Optional
|
|
30
|
+
|
|
31
|
+
SKILLS = Path(__file__).resolve().parent.parent.parent
|
|
32
|
+
SOURCE_CHECK = SKILLS / "adapting-projects-to-fabric/scripts/fabric_source_check.py"
|
|
33
|
+
PROBE = SKILLS / "building-fabric-services/scripts/check_service.py"
|
|
34
|
+
TEMPLATE_CHECK = Path(__file__).resolve().parent / "template_check.py"
|
|
35
|
+
PRIVATE_TERMS_ENV = "PASSIONCODE_PRIVATE_TERMS"
|
|
36
|
+
SKIP = {".git", "node_modules", "__pycache__", ".venv", "venv"}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _run(command: List[str]) -> "subprocess.CompletedProcess[str]":
|
|
40
|
+
return subprocess.run([sys.executable, "-B"] + command, capture_output=True, text=True, encoding="utf-8",
|
|
41
|
+
errors="replace", check=False)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _result(step: str, verdict: str, evidence: str, lines: Optional[List[str]] = None) -> Dict[str, object]:
|
|
45
|
+
return {"step": step, "verdict": verdict, "evidence": evidence, "lines": lines or []}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def source_step(root: Path) -> Dict[str, object]:
|
|
49
|
+
done = _run([str(SOURCE_CHECK), "check", str(root)])
|
|
50
|
+
lines = [line for line in done.stdout.splitlines() if line.strip()]
|
|
51
|
+
summary = lines[-1] if lines else (done.stderr.strip().splitlines() or ["no output"])[-1]
|
|
52
|
+
if done.returncode == 0:
|
|
53
|
+
return _result("source", "PASS", summary)
|
|
54
|
+
if done.returncode == 1:
|
|
55
|
+
return _result("source", "FAIL", summary, lines[:-1])
|
|
56
|
+
return _result("source", "NOT_RUN", "the source check did not read every file or could not run: %s" % summary,
|
|
57
|
+
lines[:-1])
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def probe_step(service: Optional[str], services_dir: Optional[str]) -> Dict[str, object]:
|
|
61
|
+
if not service:
|
|
62
|
+
return _result("probe", "NOT_RUN", "no --service: an agent that is no service has no descriptor to probe")
|
|
63
|
+
command = [str(PROBE), service, "--json"] + (["--services-dir", services_dir] if services_dir else [])
|
|
64
|
+
done = _run(command)
|
|
65
|
+
try:
|
|
66
|
+
report = json.loads(done.stdout)
|
|
67
|
+
except ValueError:
|
|
68
|
+
reason = (done.stderr.strip().splitlines() or ["no report"])[-1]
|
|
69
|
+
return _result("probe", "FAIL", "the probe gave no report for %s: %s" % (service, reason))
|
|
70
|
+
failed = [r for r in report.get("results", []) if r.get("verdict") == "FAIL"]
|
|
71
|
+
not_run = sum(r.get("verdict") == "NOT_RUN" for r in report.get("results", []))
|
|
72
|
+
evidence = "%d rule(s), %d FAIL, %d NOT_RUN" % (len(report.get("results", [])), len(failed), not_run)
|
|
73
|
+
return _result("probe", "FAIL" if failed else "PASS", evidence, ["%s: %s" % (r["rule"], r["evidence"]) for r in failed])
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def template_step(folders: List[str]) -> Dict[str, object]:
|
|
77
|
+
if not folders:
|
|
78
|
+
return _result("template", "NOT_RUN", "no --template folder named")
|
|
79
|
+
done = _run([str(TEMPLATE_CHECK), "--json"] + folders)
|
|
80
|
+
try:
|
|
81
|
+
report = json.loads(done.stdout)
|
|
82
|
+
except ValueError:
|
|
83
|
+
return _result("template", "FAIL", "the template check gave no report (exit %d)" % done.returncode)
|
|
84
|
+
errors = ["%s: %s %s: %s" % (folder, f["file"], f["path"], f["message"])
|
|
85
|
+
for folder, findings in report.get("folders", {}).items() for f in findings if f.get("level") == "error"]
|
|
86
|
+
return _result("template", "PASS" if report.get("ok") else "FAIL",
|
|
87
|
+
"%d folder(s), %d error(s)" % (len(folders), len(errors)), errors)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
# The one private-terms reader (FAA-328): test/validate.py, the repository's own check, reads the list with these
|
|
91
|
+
# functions too, so the two never drift. They match as org-index's check_private.py does: case-insensitive, not inside a
|
|
92
|
+
# run of ASCII letters and digits, longest term first. Callers report paths and lines only, never a term.
|
|
93
|
+
def terms_file(named: Optional[str] = None) -> Path:
|
|
94
|
+
"""The list: `named`, else `$PASSIONCODE_PRIVATE_TERMS`, else ~/.config/passioncode/private-terms.txt; `~` expanded."""
|
|
95
|
+
named = named or os.environ.get(PRIVATE_TERMS_ENV)
|
|
96
|
+
return Path(named).expanduser() if named else Path.home() / ".config/passioncode/private-terms.txt"
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def terms_pattern(lines: List[str]) -> "Optional[re.Pattern[str]]":
|
|
100
|
+
"""One alternation of the list's terms (a line that is blank or starts with # is none), longest first; None when
|
|
101
|
+
the list holds no term."""
|
|
102
|
+
terms = sorted({t.strip() for t in lines if t.strip() and not t.strip().startswith("#")}, key=len, reverse=True)
|
|
103
|
+
if not terms:
|
|
104
|
+
return None
|
|
105
|
+
return re.compile(r"(?<![A-Za-z0-9])(?:" + "|".join(re.escape(t) for t in terms) + r")(?![A-Za-z0-9])", re.IGNORECASE)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def term_lines(text: str, pattern: "re.Pattern[str]") -> List[int]:
|
|
109
|
+
"""The 1-based numbers of the lines of `text` that hold a term."""
|
|
110
|
+
return [n for n, line in enumerate(text.splitlines(), 1) if pattern.search(line)]
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def listed_files(root: Path) -> List[str]:
|
|
114
|
+
"""Every tracked file and every untracked one Git does not ignore, as check_private.py reads a work tree; outside a
|
|
115
|
+
work tree, or where git cannot run, every file but the usual generated folders, never nothing."""
|
|
116
|
+
if (root / ".git").exists():
|
|
117
|
+
try:
|
|
118
|
+
listed = subprocess.run(["git", "-C", str(root), "ls-files", "-z", "--cached", "--others", "--exclude-standard"],
|
|
119
|
+
capture_output=True, check=False)
|
|
120
|
+
except OSError:
|
|
121
|
+
listed = None
|
|
122
|
+
if listed is not None and listed.returncode == 0:
|
|
123
|
+
return sorted({rel for rel in listed.stdout.decode("utf-8", "replace").split("\0") if rel})
|
|
124
|
+
return sorted(p.relative_to(root).as_posix() for p in root.rglob("*")
|
|
125
|
+
if p.is_file() and not SKIP.intersection(p.relative_to(root).parts))
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def private_terms_step(root: Path, named: Optional[str]) -> Dict[str, object]:
|
|
129
|
+
path = terms_file(named)
|
|
130
|
+
if not path.is_file():
|
|
131
|
+
return _result("private-terms", "NOT_RUN", "no private-terms list on this machine (set %s): not a pass"
|
|
132
|
+
% PRIVATE_TERMS_ENV)
|
|
133
|
+
pattern = terms_pattern(path.read_text(encoding="utf-8").splitlines())
|
|
134
|
+
if pattern is None:
|
|
135
|
+
return _result("private-terms", "PASS", "the list holds no term")
|
|
136
|
+
hits: List[str] = []
|
|
137
|
+
files = listed_files(root)
|
|
138
|
+
for rel in files:
|
|
139
|
+
try:
|
|
140
|
+
data = (root / rel).read_bytes()
|
|
141
|
+
except OSError:
|
|
142
|
+
continue
|
|
143
|
+
if b"\0" in data[:8192]:
|
|
144
|
+
continue
|
|
145
|
+
found = [str(n) for n in term_lines(data.decode("utf-8", "replace"), pattern)]
|
|
146
|
+
if found:
|
|
147
|
+
hits.append("%s: line%s %s" % (rel, "s" if len(found) > 1 else "", ", ".join(found)))
|
|
148
|
+
return _result("private-terms", "FAIL" if hits else "PASS",
|
|
149
|
+
"%d file(s) read, %d naming a term of the list (paths and lines only)" % (len(files), len(hits)), hits)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def main(argv: Optional[List[str]] = None) -> int:
|
|
153
|
+
parser = argparse.ArgumentParser(prog="admit.py", description="The admission check a new agent passes before it runs.")
|
|
154
|
+
parser.add_argument("root", help="the agent's project root")
|
|
155
|
+
parser.add_argument("--service", help="the service to probe, ID or ID.INSTANCE")
|
|
156
|
+
parser.add_argument("--services-dir", help="the services folder the probe reads (default: Fabric's)")
|
|
157
|
+
parser.add_argument("--template", action="append", default=[], metavar="FOLDER", help="a template folder to check")
|
|
158
|
+
parser.add_argument("--private-terms", metavar="FILE", help="the operator's private-terms list")
|
|
159
|
+
parser.add_argument("--json", action="store_true", help="one JSON object: steps, admitted")
|
|
160
|
+
args = parser.parse_args(argv)
|
|
161
|
+
root = Path(args.root).expanduser()
|
|
162
|
+
if not root.is_dir():
|
|
163
|
+
print("admit.py: %s is not a folder" % root, file=sys.stderr)
|
|
164
|
+
return 2
|
|
165
|
+
steps = [source_step(root), probe_step(args.service, args.services_dir), template_step(args.template),
|
|
166
|
+
private_terms_step(root, args.private_terms)]
|
|
167
|
+
failed = any(s["verdict"] == "FAIL" for s in steps)
|
|
168
|
+
source_ran = steps[0]["verdict"] != "NOT_RUN"
|
|
169
|
+
admitted = not failed and source_ran
|
|
170
|
+
if args.json:
|
|
171
|
+
print(json.dumps({"steps": steps, "admitted": admitted}, indent=2, ensure_ascii=False))
|
|
172
|
+
else:
|
|
173
|
+
for step in steps:
|
|
174
|
+
print("%-8s %-14s %s" % (step["verdict"], step["step"], step["evidence"]))
|
|
175
|
+
for line in step["lines"]: # type: ignore[union-attr]
|
|
176
|
+
print(" %s" % line)
|
|
177
|
+
print("admitted" if admitted else "not admitted")
|
|
178
|
+
return 1 if failed else (0 if source_ran else 2)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
if __name__ == "__main__":
|
|
182
|
+
raise SystemExit(main())
|
package/plugins/fabric-agent-adapter/skills/working-with-fabric/references/agents-database.md
CHANGED
|
@@ -48,6 +48,16 @@ repository-relative path at a commit, an eval report). A decision without a numb
|
|
|
48
48
|
| building-fabric-services | at each release | contribute the release's measured wins as success cases and its new technical decisions |
|
|
49
49
|
| working-with-fabric | the retro after work | a lesson with a measured effect is also a success case: contribute it beside the lesson it records |
|
|
50
50
|
|
|
51
|
+
## A template and the communication prompt
|
|
52
|
+
|
|
53
|
+
For an agent that talks with people on a channel, the database's template and creating-fabric-agents' communication
|
|
54
|
+
agent prompt (`references/communication-agent-template.md`) are read together, never as two templates (the database's
|
|
55
|
+
own rule). Where they disagree, the template wins, in every part and not only the first-release ones: it is the merged
|
|
56
|
+
practice of the agents of its kind. A part the prompt carries and the template lacks (a first-release part, a check, a
|
|
57
|
+
pipeline step) is built too, unless the template rules it out, and goes back to the database as feedback on that
|
|
58
|
+
template (`agents-db.contribute`); the completion report names each such part. Without a template for the kind, the
|
|
59
|
+
prompt is the recipe.
|
|
60
|
+
|
|
51
61
|
## Recording the template
|
|
52
62
|
|
|
53
63
|
An agent built from a template declares it: `provenance.template` with its id and version, so a later template
|