@passioncode-ai/fabric-agent-adapter 0.39.1 → 0.40.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 (86) hide show
  1. package/CHANGELOG.md +79 -0
  2. package/README.md +9 -3
  3. package/package.json +1 -1
  4. package/plugins/fabric-agent-adapter/.claude-plugin/plugin.json +1 -1
  5. package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/SKILL.md +1 -1
  6. package/plugins/fabric-agent-adapter/skills/building-fabric-services/SKILL.md +15 -4
  7. package/plugins/fabric-agent-adapter/skills/building-fabric-services/recipes/support-agent/README.md +64 -0
  8. package/plugins/fabric-agent-adapter/skills/building-fabric-services/recipes/support-agent/config.example.json +15 -0
  9. package/plugins/fabric-agent-adapter/skills/building-fabric-services/recipes/support-agent/knowledge.example.json +13 -0
  10. package/plugins/fabric-agent-adapter/skills/building-fabric-services/recipes/support-agent/support_agent.py +264 -0
  11. package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/connectors.md +93 -0
  12. package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/estate-record.md +3 -2
  13. package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/kit-modules.md +128 -0
  14. package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/usage.md +2 -0
  15. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/check_service.py +10 -183
  16. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/check_service_rules.py +204 -0
  17. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-auth.mjs +286 -0
  18. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-backups.mjs +327 -0
  19. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-base.mjs +47 -0
  20. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-board-fixture.mjs +79 -0
  21. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-board-session.mjs +840 -0
  22. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-board-shapes.mjs +340 -0
  23. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-board.mjs +42 -1237
  24. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-ceremonies.mjs +1 -16
  25. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-crypto.mjs +41 -0
  26. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-delegation.mjs +13 -45
  27. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-descriptor.mjs +387 -0
  28. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-estate.mjs +8 -67
  29. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-events.mjs +52 -0
  30. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-http.mjs +231 -0
  31. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-interop.mjs +2 -2
  32. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-jsvalue.mjs +67 -0
  33. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-lock.mjs +202 -0
  34. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-logs.mjs +136 -0
  35. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-openrouter.mjs +335 -0
  36. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-package.mjs +2 -12
  37. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-paths.mjs +69 -0
  38. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-platform.mjs +164 -0
  39. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-profiles.mjs +1 -32
  40. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-protocols.mjs +122 -0
  41. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-service.mjs +177 -2541
  42. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-share.mjs +1 -8
  43. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-supervisors.mjs +400 -0
  44. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-time.mjs +46 -0
  45. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-usage.mjs +458 -0
  46. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_auth.py +402 -0
  47. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_backups.py +399 -0
  48. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_base.py +78 -0
  49. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_board.py +64 -1398
  50. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_board_fixture.py +116 -0
  51. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_board_session.py +902 -0
  52. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_board_shapes.py +457 -0
  53. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_ceremonies.py +36 -38
  54. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_crypto.py +289 -0
  55. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_delegation.py +15 -180
  56. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_descriptor.py +586 -0
  57. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_estate.py +39 -400
  58. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_events.py +137 -0
  59. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_http.py +402 -0
  60. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_interop.py +1 -6
  61. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_jsvalue.py +310 -0
  62. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_kit.py +243 -0
  63. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_lock.py +308 -0
  64. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_logs.py +202 -0
  65. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_openrouter.py +431 -0
  66. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_package.py +21 -43
  67. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_paths.py +102 -0
  68. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_platform.py +338 -0
  69. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_profiles.py +31 -38
  70. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_protocols.py +173 -0
  71. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_service.py +298 -3450
  72. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_share.py +8 -8
  73. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_supervisors.py +412 -0
  74. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_time.py +120 -0
  75. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_usage.py +697 -0
  76. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/kit.json +1526 -0
  77. package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/SKILL.md +1 -1
  78. package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/models-and-providers.md +3 -0
  79. package/plugins/fabric-agent-adapter/skills/working-with-fabric/SKILL.md +1 -1
  80. package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session.py +116 -1392
  81. package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session_cli.py +270 -0
  82. package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session_env.py +508 -0
  83. package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session_hosts.py +265 -0
  84. package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session_mail.py +155 -0
  85. package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session_mcp.py +335 -0
  86. package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/host_hooks.py +1 -0
@@ -0,0 +1,173 @@
1
+ """Protocol revisions (DEC-0067) and an event's machine-readable data (DEC-0070, the feed-data protocols): parse,
2
+ accept, negotiate and check them. Part of the service kit (FAA-65, layer L3)."""
3
+ from __future__ import annotations
4
+
5
+ import re
6
+ from typing import Any, Dict, List, Optional, Sequence, Tuple
7
+ import sys as _sys # noqa: E402
8
+ from pathlib import Path as _Path # noqa: E402
9
+ if str(_Path(__file__).resolve().parent) not in _sys.path: # the kit loads from any working directory (FAA-65)
10
+ _sys.path.insert(0, str(_Path(__file__).resolve().parent))
11
+
12
+
13
+ #: Each contract protocol's published revisions, oldest first (the contract's `PROTOCOL_REVISIONS`, DEC-0067); a test
14
+ #: holds it equal to the contract's register at the pin.
15
+ PROTOCOL_REVISIONS: Dict[str, Tuple[str, ...]] = {
16
+ "access-log": ("1",), "approval": ("1",), "activity-summary": ("1",), "coding-agents": ("1",), "enroll-token": ("1",), "entitlement": ("1",),
17
+ "entitlement-key-set": ("1",), "estate-record": ("1",), "execution-packet": ("1",),
18
+ "fabric-activity": ("0.1",), "fabric-dashboard": ("0.1",), "fabric-delegation": ("0.1",), "fabric-device": ("0.1",),
19
+ "fabric-interop": ("0.1", "0.2"), "fabric-local-channel": ("0.1",), "fabric-local-runner": ("0.1",),
20
+ "fabric-operator-channel": ("0.1",), "fabric-package": ("1",), "fabric-project-comms": ("0.1",), "fabric-proposal": ("1",),
21
+ "fabric-provider": ("0.1",), "fabric-relay": ("0.1",), "fabric-secret-envelope": ("1",), "fabric-service": ("0.1",),
22
+ "fabric-settings-backup": ("1",), "heartbeat": ("1",), "learning-note": ("1",), "memory": ("0.1",), "pairing-message": ("1",), "pipeline": ("0.1",),
23
+ "revocation-delta": ("1",), "session-handoff": ("1",), "share-registration": ("1",),
24
+ "share-registration-receipt": ("1",), "support-quality-daily": ("1",), "window-lease": ("1",),
25
+ }
26
+
27
+
28
+ MAX_PROTOCOLS = 64
29
+
30
+
31
+ _REVISION = re.compile(r"([a-z][a-z0-9.-]{0,63})/([0-9]{1,4}(?:\.[0-9]{1,4}){0,2})", re.ASCII)
32
+
33
+
34
+ def parse_revision(value: Any) -> Optional[Tuple[str, str]]:
35
+ """`<name>/<revision>` split, or None."""
36
+ match = _REVISION.fullmatch(value) if isinstance(value, str) else None
37
+ return (match.group(1), match.group(2)) if match else None
38
+
39
+
40
+ def accepted_revisions(name: str, newest: Optional[str] = None) -> List[str]:
41
+ """The revisions a receiver implementing ``name`` up to ``newest`` accepts: N and, when the register has one, N−1."""
42
+ known = PROTOCOL_REVISIONS.get(name, ())
43
+ if not known:
44
+ return []
45
+ top = len(known) - 1 if newest is None else (known.index(newest) if newest in known else -1)
46
+ if top < 0:
47
+ return []
48
+ return ["%s/%s" % (name, r) for r in reversed(known[max(0, top - 1):top + 1])]
49
+
50
+
51
+ def negotiate_revision(received: Any, accepted: Sequence[Any]) -> Dict[str, Any]:
52
+ """A receiver's answer to a document naming ``received`` (DEC-0067, the contract's `negotiateRevision`):
53
+ ``{"ok": True}``; the `schema-unsupported` refusal naming what was received (at most 80 characters) and the accepted
54
+ revisions of that protocol (all of them when it names none the receiver reads); or ``{"fault":
55
+ "no-accepted-revisions"}``, the receiver's own, when it accepts no well-formed revision. Revisions compare exactly."""
56
+ usable: List[str] = []
57
+ for entry in accepted if isinstance(accepted, (list, tuple)) else []:
58
+ if parse_revision(entry) and entry not in usable:
59
+ usable.append(entry)
60
+ usable = usable[:MAX_PROTOCOLS]
61
+ if not usable:
62
+ return {"fault": "no-accepted-revisions"}
63
+ if isinstance(received, str) and received in usable:
64
+ return {"ok": True}
65
+ parsed = parse_revision(received)
66
+ same = [e for e in usable if parsed and parse_revision(e)[0] == parsed[0]]
67
+ return {"error": {"code": "schema-unsupported", "message": "this receiver does not read that protocol revision",
68
+ "data": {"protocol": received[:80] if isinstance(received, str) else "", "accepted": same or usable}}}
69
+
70
+
71
+ def protocols_findings(protocols: Any, prefix: str = "/protocols") -> List[Dict[str, str]]:
72
+ """FAC-SEM-099 (DEC-0067): 1 to 64 entries, each `<name>/<revision>` once; at most two revisions of one protocol (an
73
+ unpublished one refused and not counted), and two of a contract protocol are N and N−1, consecutive in the register."""
74
+ flag = lambda at, message: {"code": "FAC-SEM-099", "instancePath": at, "message": message} # noqa: E731
75
+ if not isinstance(protocols, list):
76
+ return [flag(prefix, "protocols is a list of protocol revisions")]
77
+ if not 1 <= len(protocols) <= MAX_PROTOCOLS:
78
+ return [flag(prefix, "protocols lists 1 to %d revisions" % MAX_PROTOCOLS)]
79
+ findings: List[Dict[str, str]] = []
80
+ by_name: Dict[str, List[int]] = {}
81
+ seen: List[str] = []
82
+ for i, entry in enumerate(protocols):
83
+ at = "%s/%d" % (prefix, i)
84
+ parsed = parse_revision(entry)
85
+ if not parsed:
86
+ findings.append(flag(at, "a protocol revision is <name>/<revision>, such as fabric-service/0.1"))
87
+ continue
88
+ if entry in seen:
89
+ findings.append(flag(at, "each protocol revision once"))
90
+ continue
91
+ seen.append(entry)
92
+ known = PROTOCOL_REVISIONS.get(parsed[0])
93
+ if known and parsed[1] not in known:
94
+ findings.append(flag(at, "%s has no published revision %s" % parsed))
95
+ continue
96
+ indices = by_name.setdefault(parsed[0], [])
97
+ indices.append(known.index(parsed[1]) if known else -1)
98
+ if len(indices) > 2:
99
+ findings.append(flag(at, "at most 2 revisions of one protocol: N and N−1"))
100
+ elif known and len(indices) == 2 and abs(indices[0] - indices[1]) != 1:
101
+ findings.append(flag(at, "two revisions of one protocol are N and N−1, consecutive in the register"))
102
+ return findings
103
+
104
+
105
+ _SCHEMA_URI = re.compile(r"[A-Za-z][A-Za-z0-9+.-]*:[^\s]+", re.ASCII)
106
+
107
+
108
+ def _scalar(value: Any) -> bool:
109
+ return value is None or isinstance(value, (bool, int, float)) or (isinstance(value, str) and len(value) <= 500)
110
+
111
+
112
+ def feed_data_problems(data: Any) -> List[str]:
113
+ """DEC-0070's `data {schema, value}` as the schema bounds it, then FAC-SEM-101: no string in `value`, at either
114
+ level, under a credential's name or of a credential's shape (named by its place, never its value)."""
115
+ import fabric_secrets # noqa: E402 - beside this file; the contract's detector
116
+ if not isinstance(data, dict) or set(data) != {"schema", "value"}:
117
+ return ["data is {schema, value}"]
118
+ problems: List[str] = []
119
+ if not (isinstance(data["schema"], str) and _SCHEMA_URI.fullmatch(data["schema"])):
120
+ problems.append("data.schema is the absolute URI of a payload schema the service publishes")
121
+ value = data["value"]
122
+ if not isinstance(value, dict) or len(value) > 32:
123
+ return problems + ["data.value is an object of at most 32 members"]
124
+ for key, member in value.items():
125
+ if _scalar(member):
126
+ continue
127
+ if isinstance(member, list) and len(member) <= 32 and all(_scalar(m) for m in member):
128
+ continue
129
+ if isinstance(member, dict) and len(member) <= 32 and all(_scalar(m) for m in member.values()):
130
+ continue
131
+ problems.append("data.value.%s is a scalar (a string of at most 500 characters), a list of at most 32 scalars or an object of at most 32 scalar members" % key)
132
+ if problems:
133
+ return problems
134
+ for finding in feed_data_findings({"events": [{"data": data}]}):
135
+ problems.append("FAC-SEM-101: %s %s" % (finding["instancePath"].replace("/events/0", ""), finding["message"]))
136
+ return problems
137
+
138
+
139
+ def _pointer_token(name: str) -> str:
140
+ return str(name).replace("~", "~0").replace("/", "~1")
141
+
142
+
143
+ def feed_data_findings(page: Any) -> List[Dict[str, str]]:
144
+ """FAC-SEM-101 over an events page (the contract's `feedDataFindings`): a list's items and an object's members sit
145
+ under the outer name too; a number is a count, never a credential."""
146
+ import fabric_secrets # noqa: E402
147
+ findings: List[Dict[str, str]] = []
148
+
149
+ def check(named: bool, member: Any, at: str) -> None:
150
+ if not isinstance(member, str):
151
+ return
152
+ if named:
153
+ findings.append({"code": "FAC-SEM-101", "instancePath": at, "message": "an event's data puts no string under a credential's name"})
154
+ elif fabric_secrets.secret_value(member) or fabric_secrets.secret_url(member):
155
+ findings.append({"code": "FAC-SEM-101", "instancePath": at, "message": "an event's data carries no value shaped like a credential"})
156
+
157
+ events = page.get("events") if isinstance(page, dict) else None
158
+ for i, event in enumerate(events if isinstance(events, list) else []):
159
+ data = event.get("data") if isinstance(event, dict) else None
160
+ value = data.get("value") if isinstance(data, dict) else None
161
+ if not isinstance(value, dict):
162
+ continue
163
+ for key, member in value.items():
164
+ at = "/events/%d/data/value/%s" % (i, _pointer_token(key))
165
+ named = fabric_secrets.credential_name(key)
166
+ check(named, member, at)
167
+ if isinstance(member, list):
168
+ for j, item in enumerate(member):
169
+ check(named, item, "%s/%d" % (at, j))
170
+ elif isinstance(member, dict):
171
+ for inner, leaf in member.items():
172
+ check(named or fabric_secrets.credential_name(inner), leaf, "%s/%s" % (at, _pointer_token(inner)))
173
+ return findings