@passioncode-ai/fabric-agent-adapter 0.56.0 → 0.58.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +108 -0
- package/COMMERCIAL-LICENSE.md +1 -1
- package/README.md +18 -7
- package/SECURITY.md +6 -1
- 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 +18 -4
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/references/provider-entry.md +18 -7
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/references/source-and-instance.md +154 -0
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/adapt_project.py +2 -2
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/fabric_provider.py +25 -5
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/fabric_source_check.py +995 -0
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/fabric_source_homes.py +72 -0
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/fabric_source_ignore.py +185 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/SKILL.md +4 -3
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/delegation.md +3 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/interop.md +5 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/package.md +22 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/protocol.md +5 -3
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-actions.mjs +2 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-protocols.mjs +5 -3
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_actions.py +2 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_protocols.py +4 -2
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/kit.json +185 -5
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/SKILL.md +16 -7
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/agents-that-answer-people.md +11 -5
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/source-and-instance.md +154 -0
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/SKILL.md +9 -9
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session_env.py +1 -1
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""How fabric_source_check.py reads a home the owner's list declares (`home: <name>`, FAA-188 part 1): the name in
|
|
3
|
+
every form the table names, its parts joined by any separator a tool or a language writes, and its spellings for the
|
|
4
|
+
masks of printed paths. Beside fabric_source_check.py, which imports it. Standard library only.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import re
|
|
10
|
+
from typing import Dict, Iterable, List, Set, Tuple
|
|
11
|
+
|
|
12
|
+
_SEP = r"(?:\\|/)+" # one way to read a run: two alternatives that both take a `\\` backtrack exponentially
|
|
13
|
+
|
|
14
|
+
#: How a `home: <name>` line of the owner's list is kept among the entries: a character no line can hold.
|
|
15
|
+
HOME_ENTRY = "\0"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _owner_homes(entries: List[str]) -> Set[str]:
|
|
19
|
+
"""The home names the owner's list declares (`home: <name>`), which are never neutral."""
|
|
20
|
+
return {e[1:] for e in entries if e.startswith(HOME_ENTRY)}
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
_DASHED: Dict[str, Tuple["re.Pattern[str]", ...]] = {}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
#: Where a declared home name stands in each form the table names: the slashed, drive, URL-encoded and dashed homes.
|
|
27
|
+
_DECLARED_SLASHED = (r"(?<![A-Za-z0-9_.-])(?:/var)?/home/", r"(?<![A-Za-z0-9_.-])/Users/",
|
|
28
|
+
r"(?<![A-Za-z0-9_.-])(?:/mnt)?/[A-Za-z]/Users/",
|
|
29
|
+
r"(?<![A-Za-z0-9_.-])(?i:[A-Za-z]:%sUsers%s)" % (_SEP, _SEP),
|
|
30
|
+
r"%2[Ff](?:Users|home)%2[Ff]", r"(?i:%5CUsers%5C)")
|
|
31
|
+
_DECLARED_DASHED = (r"(?:^|[^A-Za-z0-9-])(?:-Users|-home|-var-home|(?:-mnt)?-[A-Za-z]-Users)-",
|
|
32
|
+
r"(?<![A-Za-z0-9])(?i:[A-Za-z]--Users-)")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
#: One separator between the parts of a declared home name, in any spelling a tool or a language writes: a space, `.`,
|
|
36
|
+
#: `_`, `-`, `+`, `'`, an escaped `\'` or `\u0027`, an HTML `'` or `'`, or a percent-encoding of one of them.
|
|
37
|
+
_NAME_JOIN = r"(?:[ ._+'-]|\\'|\\x27|\\u0027|�?39;|&#[xX]27;|'|%2[07BbDdEe]|%5[Ff])"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
#: Where an unfound declared name starts a word: no letter or digit before it, or one of `_NAME_JOIN`'s escapes that ends
|
|
41
|
+
#: in a letter or digit (a percent-encoding, `\x27`, `\u0027`); the escapes ending in `;` or `'` already leave no letter
|
|
42
|
+
#: or digit before the name. Kept beside `_NAME_JOIN`, so a separator added to one is added to the other.
|
|
43
|
+
_NAME_START = r"(?:(?<![A-Za-z0-9])|(?<=%[0-9A-Fa-f]{2})|(?<=\\x27)|(?<=\\u0027))"
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _name_parts(name: str) -> List[str]:
|
|
47
|
+
return [part for part in re.split(r"[ ._'-]+", name) if part]
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _spellings(found: Iterable[str], owner: Iterable[str]) -> Set[str]:
|
|
51
|
+
"""The names found, and every spelling of a declared home one of them spells (its parts joined by each separator
|
|
52
|
+
a tool writes), so a printed path masks the other spellings of that home too."""
|
|
53
|
+
names = set(found)
|
|
54
|
+
for name in owner:
|
|
55
|
+
parts = _name_parts(name)
|
|
56
|
+
body = re.compile(r"(?i:%s)" % (_NAME_JOIN + "+").join(re.escape(part) for part in parts), re.ASCII)
|
|
57
|
+
if parts and any(body.fullmatch(n) for n in names):
|
|
58
|
+
names |= {name} | {sep.join(parts) for sep in (" ", ".", "_", "-", "+", "'", "%20", "%27")}
|
|
59
|
+
return names
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _dashed_home(name: str) -> Tuple["re.Pattern[str]", ...]:
|
|
63
|
+
"""A declared home name in every form, read whole, its parts joined by any run of the separators a tool or a
|
|
64
|
+
language writes (`_NAME_JOIN`: a space, `.`, `_`, `-`, `+`, `'` and their escapes and percent-encodings), since
|
|
65
|
+
the generic forms end a name at a dash or a space, so `-Users-j-doe-proj` would read `j`. It ends only where a
|
|
66
|
+
letter or digit would continue it: a `.` or `_` after it (an extension, a sentence's end) is read as the declared
|
|
67
|
+
home, failing closed."""
|
|
68
|
+
if name not in _DASHED:
|
|
69
|
+
body = (_NAME_JOIN + "+").join(re.escape(part) for part in _name_parts(name)) or re.escape(name)
|
|
70
|
+
whole = r"((?i:%s))(?![A-Za-z0-9])" % body
|
|
71
|
+
_DASHED[name] = tuple(re.compile(prefix + whole, re.ASCII) for prefix in _DECLARED_SLASHED + _DECLARED_DASHED)
|
|
72
|
+
return _DASHED[name]
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""How fabric_source_check.py reads `.fabric-source-ignore` and `--exclude` (FAA-188 part 1): a line is a path from the
|
|
3
|
+
tree's root, matched byte by byte as Git matches (`*`, `?` and `[...]` inside one folder, `**` as a whole segment
|
|
4
|
+
across folders), and a line Git would read otherwise is refused. Beside fabric_source_check.py, which imports it.
|
|
5
|
+
Standard library only.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import os
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
import re
|
|
13
|
+
import warnings
|
|
14
|
+
from typing import Dict, Iterable, List, Optional
|
|
15
|
+
|
|
16
|
+
IGNORE_FILE = ".fabric-source-ignore"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class CheckError(Exception):
|
|
20
|
+
pass
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
_GLOBS: Dict[str, "re.Pattern[str]"] = {}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _as_bytes(text: str) -> str:
|
|
27
|
+
"""A path or a line as Git compares it: its UTF-8 bytes, one character each. An unpaired surrogate (a Windows file
|
|
28
|
+
name may hold one) is written as Git for Windows writes it (WTF-8), never a crash."""
|
|
29
|
+
if os.name == "nt": # Windows names are UTF-16: every surrogate in one is a character of the name, as WTF-8
|
|
30
|
+
return text.encode("utf-8", "surrogatepass").decode("latin-1")
|
|
31
|
+
try: # POSIX names are bytes: a surrogate here is one Python escaped, and surrogateescape gives that byte back
|
|
32
|
+
return text.encode("utf-8", "surrogateescape").decode("latin-1")
|
|
33
|
+
except UnicodeEncodeError:
|
|
34
|
+
return text.encode("utf-8", "surrogatepass").decode("latin-1")
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _name_bytes(name: str) -> str:
|
|
38
|
+
"""A file name as Git compares it, one byte each: on POSIX the bytes on disk, encoded back with the file system's
|
|
39
|
+
own codec (os.walk and `_git_files` decoded it with that codec, so a name in any locale comes back exact); on
|
|
40
|
+
Windows its UTF-8 (WTF-8) bytes, as `_as_bytes` writes them. The ignore file is read as UTF-8 and stays `_as_bytes`."""
|
|
41
|
+
if os.name == "nt":
|
|
42
|
+
return _as_bytes(name)
|
|
43
|
+
try:
|
|
44
|
+
return os.fsencode(name).decode("latin-1")
|
|
45
|
+
except UnicodeEncodeError: # not a name os.walk or Git gave (a surrogate outside the escapes): read as `_as_bytes`
|
|
46
|
+
return _as_bytes(name)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _glob(glob: str) -> "re.Pattern[str]":
|
|
50
|
+
"""A line of the ignore file read from the tree's root: `*`, `?` and `[...]` stay inside one folder, and `**` as a
|
|
51
|
+
whole segment crosses folders (`**/` also matches none); any other run of stars is one `*`. Not a `.gitignore`
|
|
52
|
+
line: a line with no `/` matches at the root only, and `\\` or `//` is refused (`_line`).
|
|
53
|
+
fnmatch's `*` would cross `/`, so `vectors/*.json` would leave out `vectors/private/live.json`. Raises re.error
|
|
54
|
+
for a class Python cannot read (`[9-0]`)."""
|
|
55
|
+
key = glob
|
|
56
|
+
if key not in _GLOBS:
|
|
57
|
+
glob = _as_bytes(glob) # Git matches bytes: `?` and a class read one byte of a UTF-8 name, never a letter
|
|
58
|
+
out, i = [], 0
|
|
59
|
+
while i < len(glob):
|
|
60
|
+
c = glob[i]
|
|
61
|
+
if c == "*":
|
|
62
|
+
end = i
|
|
63
|
+
while end < len(glob) and glob[end] == "*":
|
|
64
|
+
end += 1
|
|
65
|
+
whole = end - i >= 2 and (i == 0 or glob[i - 1] == "/") and (end == len(glob) or glob[end] == "/")
|
|
66
|
+
if whole and end < len(glob):
|
|
67
|
+
out.append("(?:.*/)?")
|
|
68
|
+
end += 1
|
|
69
|
+
else:
|
|
70
|
+
out.append(".*" if whole else "[^/]*")
|
|
71
|
+
i = end
|
|
72
|
+
continue
|
|
73
|
+
elif c == "?":
|
|
74
|
+
out.append("[^/]")
|
|
75
|
+
elif c == "[":
|
|
76
|
+
j = i + 1 + (glob[i + 1:i + 2] in ("!", "^"))
|
|
77
|
+
j += glob[j:j + 1] == "]"
|
|
78
|
+
end = glob.find("]", j)
|
|
79
|
+
if end < 0 or "[:" in glob[i + 1:end + 1]:
|
|
80
|
+
# Git matches nothing for an unclosed `[` and reads `[:digit:]` as a POSIX class: refused
|
|
81
|
+
raise re.error("a class Git reads otherwise")
|
|
82
|
+
else:
|
|
83
|
+
inner = glob[i + 1:end].replace("\\", "\\\\")
|
|
84
|
+
negated = inner[:1] in ("!", "^") # Git reads both as a negated class
|
|
85
|
+
body = inner[negated:]
|
|
86
|
+
if body[:1] in ("^", "["): # a literal right after the negation
|
|
87
|
+
body = "\\" + body
|
|
88
|
+
for mark in "&~|": # literals, never the set operations a later Python may read there
|
|
89
|
+
body = body.replace(mark, "\\" + mark)
|
|
90
|
+
out.append("(?!/)[%s%s]" % ("^" if negated else "", body))
|
|
91
|
+
i = end
|
|
92
|
+
else:
|
|
93
|
+
out.append(re.escape(c))
|
|
94
|
+
i += 1
|
|
95
|
+
with warnings.catch_warnings(): # a FutureWarning (`--` in a class) would print the script's own path
|
|
96
|
+
warnings.simplefilter("error")
|
|
97
|
+
try:
|
|
98
|
+
_GLOBS[key] = re.compile("(?s:%s)\\Z" % "".join(out))
|
|
99
|
+
except Warning:
|
|
100
|
+
raise re.error("a class Python may read as a set operation")
|
|
101
|
+
return _GLOBS[key]
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _readable_glob(glob: str) -> bool:
|
|
105
|
+
"""Whether every form `_ignored` tries can be read: a reversed range (`[9-0]`) is refused on every Python."""
|
|
106
|
+
try:
|
|
107
|
+
for form in (glob, glob.rstrip("/")):
|
|
108
|
+
_glob(form) # every form, whether `_ignored` tries it for this line or not
|
|
109
|
+
except re.error:
|
|
110
|
+
return False
|
|
111
|
+
return True
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _line(glob: str, where: str) -> str:
|
|
115
|
+
"""An ignore line as it is matched: a leading `/` is the root (it changes nothing); a `!` exception is refused, since a
|
|
116
|
+
file it would bring back is left out here (it fails open), and so is a glob that cannot be read."""
|
|
117
|
+
if glob.startswith("!"):
|
|
118
|
+
raise CheckError("%s is an exception (`!`), which the check does not read: name the files to leave out" % where)
|
|
119
|
+
# `//` and `\` mean something else to Git (no match, an escape): refused, never read as a wider line
|
|
120
|
+
if "//" in glob or "\\" in glob:
|
|
121
|
+
raise CheckError("%s holds `\\` or `//`: write the path from the root with single `/`" % where)
|
|
122
|
+
glob = glob[1:] if glob.startswith("/") else glob
|
|
123
|
+
if not glob or not _readable_glob(glob):
|
|
124
|
+
raise CheckError("%s is not a glob it can read" % where)
|
|
125
|
+
return glob
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def ignore_globs(root: Path, extra: Iterable[str]) -> List[str]:
|
|
129
|
+
globs = [_line(g.rstrip(" "), "an --exclude glob") for g in extra]
|
|
130
|
+
ignore = root / IGNORE_FILE
|
|
131
|
+
try:
|
|
132
|
+
present = ignore.is_file()
|
|
133
|
+
except OSError:
|
|
134
|
+
raise CheckError("the folder cannot be read")
|
|
135
|
+
if present:
|
|
136
|
+
try:
|
|
137
|
+
data = ignore.read_bytes().decode("utf-8-sig")
|
|
138
|
+
except (OSError, UnicodeDecodeError):
|
|
139
|
+
raise CheckError("%s cannot be read as UTF-8" % IGNORE_FILE)
|
|
140
|
+
# as Git splits it: at LF, a trailing CR dropped, trailing spaces trimmed; a tab or a leading space is the line's
|
|
141
|
+
for number, line in enumerate(data.split("\n"), 1):
|
|
142
|
+
glob = (line[:-1] if line.endswith("\r") else line).rstrip(" ")
|
|
143
|
+
if glob and not glob.startswith("#"):
|
|
144
|
+
globs.append(_line(glob, "line %d of %s" % (number, IGNORE_FILE)))
|
|
145
|
+
return globs
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def _takes_folder(glob: str) -> bool:
|
|
149
|
+
"""A line that names a folder takes all it holds: one ending in `/`, or a path with no wildcard (`vendor/lib`). A
|
|
150
|
+
wildcard line takes only what it matches: `vectors/*` the files in `vectors`, never `vectors/live/config.json`."""
|
|
151
|
+
return glob.endswith("/") or not any(c in glob for c in "*?[")
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def _under(path: str, glob: str) -> bool:
|
|
155
|
+
"""Whether a folder above the path matches the line, read as a folder: `src/**/` takes `src/pkg/x`, never
|
|
156
|
+
`src/main.py` (joining the line with `/**` would read `**/` as no folder at all)."""
|
|
157
|
+
folder = _glob(glob.rstrip("/"))
|
|
158
|
+
return any(folder.match(path[:i]) for i, c in enumerate(path) if c == "/")
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def _ignored(path: str, globs: List[str], root: Optional[Path] = None) -> bool:
|
|
162
|
+
"""Whether a line leaves the path out. A folder (a nested repository, which Git lists as `vendor/sub/`, or a
|
|
163
|
+
submodule, listed as `vendor/sub`) is left out only by a line that names it as a folder (`vendor/sub/`,
|
|
164
|
+
`vendor/sub`, or a wildcard folder line such as `vendor/*/`, which takes every one it reaches): a wildcard line
|
|
165
|
+
without the `/` takes files, so `vendor/*` reports a nested repository until it is checked in its own tree and
|
|
166
|
+
named. A line ending in `/` never takes a file of that name."""
|
|
167
|
+
on_disk = path.rstrip("/")
|
|
168
|
+
name = _name_bytes(on_disk) # matched as Git matches: byte by byte, the bytes on disk
|
|
169
|
+
known: List[bool] = []
|
|
170
|
+
|
|
171
|
+
def folder() -> bool:
|
|
172
|
+
if not known:
|
|
173
|
+
known.append(path.endswith("/") or (root is not None and os.path.isdir(str(root / on_disk))
|
|
174
|
+
and not os.path.islink(str(root / on_disk))))
|
|
175
|
+
return known[0]
|
|
176
|
+
|
|
177
|
+
for g in globs:
|
|
178
|
+
if _takes_folder(g):
|
|
179
|
+
if _under(name, g):
|
|
180
|
+
return True
|
|
181
|
+
if _glob(g.rstrip("/")).match(name) and (not g.endswith("/") or folder()):
|
|
182
|
+
return True
|
|
183
|
+
elif _glob(g).match(name) and not folder():
|
|
184
|
+
return True
|
|
185
|
+
return False
|
|
@@ -14,10 +14,10 @@ license: AGPL-3.0-only OR LicenseRef-PassionCode-Commercial
|
|
|
14
14
|
compatibility: Python 3.9+ or Node.js 20+ for the kits (Node.js 22+ for a Linux service's instance lock); the probe needs Python 3.9+. launchd steps are macOS-only; Linux services install a systemd user unit and Windows ones a per-user Scheduled Task with the kits' writers. Dashboard handoff optionally uses Fabric Dashboards MCP link/host_status/open; without it, report unresolved host capability. The contract checkout is optional.
|
|
15
15
|
metadata:
|
|
16
16
|
author: PassionCode.ai
|
|
17
|
-
version: "0.
|
|
17
|
+
version: "0.58.0"
|
|
18
18
|
contract-version: "0.1.0"
|
|
19
19
|
extension: "fabric-service/0.1"
|
|
20
|
-
extension-commit: "
|
|
20
|
+
extension-commit: "3830a3353923e20f826d466610e1c479f120bec0"
|
|
21
21
|
---
|
|
22
22
|
|
|
23
23
|
# Building Fabric services
|
|
@@ -321,7 +321,8 @@ the well-known document must answer again with a new pid within about 15 seconds
|
|
|
321
321
|
|
|
322
322
|
To hand an agent to another machine or an organization, pack it as one folder (contract DEC-0050, DEC-0051). Write the
|
|
323
323
|
author's `fabric.package.json` (the descriptive fields; for an agent `resultSignal` and `evals` with the receipt of the
|
|
324
|
-
suite as packed),
|
|
324
|
+
suite as packed), run `fabric_source_check.py check <folder> --every-file` (adapting-projects-to-fabric, Python) on the clean copy
|
|
325
|
+
the package reference describes, then:
|
|
325
326
|
|
|
326
327
|
```bash
|
|
327
328
|
python3 <skill-dir>/scripts/fabric_package.py pack <folder> # or: node fabric-package.mjs, npx @passioncode-ai/fabric-agent-adapter pack
|
package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/delegation.md
CHANGED
|
@@ -110,7 +110,9 @@ issuer's fault, `issuer-unavailable` (DEC-0045); a gate without a trusted issuer
|
|
|
110
110
|
DEC-0121 adds three codes to the one set, each 403: `operator-decision-required` (a `relayed` action called without the
|
|
111
111
|
operator's decision), `decision-invalid` (a decision for another action or object, outside its 24 hours, or malformed)
|
|
112
112
|
and `human-step-required`, which carries `data.humanStep` (`sign-in`, `consent`, `payment`, `secret`,
|
|
113
|
-
`spend-above-limit`, `passkey
|
|
113
|
+
`spend-above-limit`, `passkey`, and since DEC-0142 `comparison`: a comparison of codes between two people or the choice
|
|
114
|
+
to share without one; a reader pinned before DEC-0142 refuses a whole declaration that names it, and a caller that validates the refusal
|
|
115
|
+
against a `delegation-refusal.schema.json` pinned before DEC-0142 refuses that refusal) and `data.url` (an https URL or a path on the service's origin) beside `detail`.
|
|
114
116
|
`refusal_from_action` / `refusalFromAction` turn an action decision into that refusal; a service's own fault never
|
|
115
117
|
reaches the caller and reads as `unknown-state`, but for `mandate-capability-missing` (DEC-0141 MJ-1): the caller
|
|
116
118
|
reads `operator-decision-required` with that `data.detail`, since no mandate covers the step and only a decision does. Since 0.51.0 the `operator-decision-required` message (free text in
|
|
@@ -161,7 +161,11 @@ dialog is one of those checks; an instruction that names the action satisfies it
|
|
|
161
161
|
|---|---|---|
|
|
162
162
|
| `none` | an act on the caller's own account: refresh, read, retry or cancel its own job | the caller's own act |
|
|
163
163
|
| `relayed` | a decision on another party's proposal or draft, a send, a publish, a deletion, a spend within a limit, a restore, a policy change | the operator's act when a T0 or T1 caller carries a valid decision; else a refusal below |
|
|
164
|
-
| `human` with `humanStep` | `sign-in`, `consent`, `payment`, `secret`, `spend-above-limit`, `passkey` (AP-4): they stay the person's | `human-step-required` with `data.humanStep` and `data.url`, the direct link to that step |
|
|
164
|
+
| `human` with `humanStep` | `sign-in`, `consent`, `payment`, `secret`, `spend-above-limit`, `passkey`, `comparison` (a comparison of codes between two people or the choice to share without one, DEC-0142) (AP-4): they stay the person's | `human-step-required` with `data.humanStep` and `data.url`, the direct link to that step |
|
|
165
|
+
|
|
166
|
+
Declare `comparison` only to readers at or after DEC-0142: a reader pinned before it (a 0.56.0 or older kit, Fabric
|
|
167
|
+
Dashboards before 0.8.9) refuses the whole declaration, every action in it, and FAC-SEM-142 then reads it as not declared. Fabric's hub reads no declaration and forwards the step unchanged
|
|
168
|
+
(CT-186).
|
|
165
169
|
|
|
166
170
|
```python
|
|
167
171
|
server = fi.McpToolServer("example-agent", "1.0.0", jobs=jobs, authorize=gate,
|
|
@@ -42,6 +42,28 @@ python3 scripts/fabric_package.py pack <folder> # a dry run: the packa
|
|
|
42
42
|
python3 scripts/fabric_package.py pack <folder> --apply # writes fabric.package.json when nothing is wrong
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
+
Run `adapting-projects-to-fabric/scripts/fabric_source_check.py check <folder> --every-file` on the folder you pack first (a floor under your own
|
|
46
|
+
review, not a secret scanner): a package carries
|
|
47
|
+
the source, and the instance (the owner's paths, keys, strings) stays out of it (`references/source-and-instance.md` of
|
|
48
|
+
that skill). `--every-file` reads what `pack` reads, files Git ignores included; without it the check reads only what Git
|
|
49
|
+
would commit. A path named in `.fabric-source-ignore` or `--exclude` is still carried by `pack`, unread, so name only synthetic files
|
|
50
|
+
there, and check a nested repository named there with `--every-file` in its own tree. Keep the signing key file
|
|
51
|
+
outside the folder. Pack a clean copy: what Git would commit (`git ls-files --cached --others --exclude-standard`) plus the nested
|
|
52
|
+
trees checked on their own, copied without their `.git` and with each committed symbolic link replaced or removed by where its resolved
|
|
53
|
+
target is (`pack` refuses a link): a file or folder Git would commit in the tree being packed, or in a nested tree checked
|
|
54
|
+
on its own, is copied in its place (a folder's committed files, each link inside it by the same rule; a file the ignore
|
|
55
|
+
file names needs an ignore line of its own at the new path); a link whose target is a folder already being copied on
|
|
56
|
+
the way to it (an ancestor, or a folder a link above it led into) is removed, so the copy ends; a target that leaves
|
|
57
|
+
that tree or lies in what `.gitignore` keeps out, such as an instance folder in the checkout, is removed, since it is
|
|
58
|
+
the instance or another project. Source the agent shares from outside the tree (a monorepo's common code, linked or
|
|
59
|
+
imported by a relative path) is copied into the agent's tree at the path it is reached by and committed before
|
|
60
|
+
packing. List each link removed and review it: a missing file is not seen by `pack` or `verify`. Leave out dependency
|
|
61
|
+
folders (`node_modules`, virtual environments, which hold install paths and binaries), what `.gitignore` keeps out as
|
|
62
|
+
instance (the real configuration, an instance folder in the checkout) and the files `pack` refuses by name (a
|
|
63
|
+
credential-free `.npmrc` that `.fabric-source-ignore` names for the check included). `pack` does not read
|
|
64
|
+
`.gitignore`, and an account id or a host is found only through the owner's list. The check is Python only (3.9+); a Node-only machine packs without it, and `pack` itself carries the kit's token files
|
|
65
|
+
and a private `*.jwk` by their names: keep them in the instance.
|
|
66
|
+
|
|
45
67
|
It refuses to list what must not ship, and names each: a symlink, a special file, `.git`, `__pycache__`, `.DS_Store`,
|
|
46
68
|
`*.pyc`, and files that are credentials by name (`.env` and `.env.*` but `.env.example|sample|template`, `*.pem`, `*.key`,
|
|
47
69
|
`*.p12`, `*.pfx`, `*.jks`, `*.keystore`, `id_rsa`-style keys, `.npmrc`, `.netrc`, `.pypirc`). Remove them or pack a clean
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# fabric-service/0.1: wire reference
|
|
2
2
|
|
|
3
|
-
Pinned to `fabric-agent-contract` commit `
|
|
3
|
+
Pinned to `fabric-agent-contract` commit `3830a3353923e20f826d466610e1c479f120bec0`
|
|
4
4
|
(`docs/specification/service.md`, DEC-0015; the usage report DEC-0021; Windows and Linux supervision
|
|
5
5
|
DEC-0032 and DEC-0033; the operator channel DEC-0034 and DEC-0035; the embedded dashboard DEC-0036;
|
|
6
6
|
delegation and the operator session's lifetime DEC-0039; the meter's call window DEC-0041; session handoff DEC-0042;
|
|
@@ -23,8 +23,10 @@ DEC-0124; the operator's mandate `mandate/1`, handed on narrowed with a task DEC
|
|
|
23
23
|
operator's agents, its `source` on a mandate and a decision DEC-0126; context hub keys and the organizations an estate
|
|
24
24
|
record pins DEC-0127 and DEC-0128; the family and the agent network named DEC-0130 and DEC-0137; a reply delivered to
|
|
25
25
|
the sender it answers, or to whom it names DEC-0135; a hand-over adds no naming list the scope in force leaves out
|
|
26
|
-
DEC-0136; a job's step under the mandate read under the job's own capability DEC-0141
|
|
27
|
-
|
|
26
|
+
DEC-0136; a job's step under the mandate read under the job's own capability DEC-0141; a person's comparison of codes as a human
|
|
27
|
+
step DEC-0142. The organization's member list, draft exchange, relay and the machines' report, DEC-0129, DEC-0131 to
|
|
28
|
+
DEC-0134 and DEC-0139, an organization's adaptation DEC-0138 and the peer share DEC-0142, reach the kit only as
|
|
29
|
+
protocol register entries). The contract's schemas are normative;
|
|
28
30
|
this page is the working summary. Extension key:
|
|
29
31
|
`https://fabric.passioncode.ai/agent-contract/extensions/service/0.1`.
|
|
30
32
|
|
package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-actions.mjs
CHANGED
|
@@ -37,7 +37,8 @@ export const DECISION_INVALID = 'decision-invalid';
|
|
|
37
37
|
export const HUMAN_STEP_REQUIRED = 'human-step-required';
|
|
38
38
|
export const APPROVAL_REQUIRED = 'approval-required';
|
|
39
39
|
export const GATES = Object.freeze(['none', 'relayed', 'human']);
|
|
40
|
-
|
|
40
|
+
/** AP-4's steps a person takes; `comparison` (DEC-0142) is a comparison of codes between two people. */
|
|
41
|
+
export const HUMAN_STEPS = Object.freeze(['sign-in', 'consent', 'payment', 'secret', 'spend-above-limit', 'passkey', 'comparison']);
|
|
41
42
|
export const MAX_ACTIONS = 128;
|
|
42
43
|
/** How far ahead of the service's clock a decision's `at` may be (the contract's CLOCK_SKEW_SECONDS). */
|
|
43
44
|
export const CLOCK_SKEW_SECONDS = 60;
|
package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-protocols.mjs
CHANGED
|
@@ -10,10 +10,12 @@ export const PROTOCOL_REVISIONS = new Map(Object.entries({
|
|
|
10
10
|
'access-log': ['1'], approval: ['1'], 'activity-summary': ['1'], 'agent-directory': ['1'], 'call-record': ['1'], 'coding-agents': ['1'], 'credential-decision': ['1'], 'credential-request': ['1'], 'credential-revocation': ['1'], 'enroll-token': ['1'], entitlement: ['1'], 'entitlement-key-set': ['1'], 'estate-record': ['1'], 'execution-packet': ['1'],
|
|
11
11
|
'fabric-actions': ['0.1'], 'fabric-activity': ['0.1'], 'fabric-agent-call': ['1'], 'fabric-connections': ['0.1'], 'fabric-credential-envelope': ['1'], 'fabric-load': ['0.1'], 'fabric-dashboard': ['0.1'], 'fabric-delegation': ['0.1'], 'fabric-device': ['0.1'],
|
|
12
12
|
'fabric-interop': ['0.1', '0.2'], 'fabric-local-channel': ['0.1'], 'fabric-local-runner': ['0.1'], 'fabric-machine-report': ['1'], 'fabric-operator-channel': ['0.1'],
|
|
13
|
-
'fabric-package': ['1'],
|
|
13
|
+
'fabric-package': ['1'],
|
|
14
|
+
'fabric-peer-share': ['1'], 'fabric-peer-share-envelope': ['1'], 'fabric-peer-share-report': ['1'], 'fabric-project-comms': ['0.1'], 'fabric-proposal': ['1'], 'fabric-provider': ['0.1'], 'fabric-relay': ['0.1'],
|
|
14
15
|
'fabric-secret-envelope': ['1'], 'fabric-service': ['0.1'], 'fabric-settings-backup': ['1'], heartbeat: ['1'], 'learning-note': ['1'], 'machine-report': ['1'], mandate: ['1'],
|
|
15
|
-
memory: ['0.1'], 'node-summary': ['1'], 'operator-source': ['1'], 'org-authority': ['1'], 'org-authority-draft': ['1'],
|
|
16
|
-
'org-key-set': ['1'], 'org-key-set-draft': ['1'], 'org-membership': ['1'], 'org-root-registration': ['1'], 'pairing-message': ['1'],
|
|
16
|
+
memory: ['0.1'], 'node-summary': ['1'], 'operator-source': ['1'], 'org-adaptation': ['1'], 'org-adaptation-receipt': ['1'], 'org-authority': ['1'], 'org-authority-draft': ['1'],
|
|
17
|
+
'org-key-set': ['1'], 'org-key-set-draft': ['1'], 'org-membership': ['1'], 'org-root-registration': ['1'], 'pairing-message': ['1'],
|
|
18
|
+
'peer-share-deletion': ['1'], 'peer-share-frame': ['1'], 'peer-share-revocation': ['1'], pipeline: ['0.1'], 'repair-request': ['1'], 'repair-result': ['1'],
|
|
17
19
|
'revocation-delta': ['1'], 'session-handoff': ['1'], 'service-load': ['1'],
|
|
18
20
|
'share-registration': ['1'], 'share-registration-receipt': ['1'], 'support-quality-daily': ['1'], 'window-lease': ['1'],
|
|
19
21
|
}));
|
package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_actions.py
CHANGED
|
@@ -51,7 +51,8 @@ DECISION_INVALID = "decision-invalid"
|
|
|
51
51
|
HUMAN_STEP_REQUIRED = "human-step-required"
|
|
52
52
|
APPROVAL_REQUIRED = "approval-required"
|
|
53
53
|
GATES = ("none", "relayed", "human")
|
|
54
|
-
|
|
54
|
+
#: AP-4's steps a person takes; `comparison` (DEC-0142) is a comparison of codes between two people.
|
|
55
|
+
HUMAN_STEPS = ("sign-in", "consent", "payment", "secret", "spend-above-limit", "passkey", "comparison")
|
|
55
56
|
MAX_ACTIONS = 128
|
|
56
57
|
#: How far ahead of the service's clock a decision's `at` may be (the contract's CLOCK_SKEW_SECONDS).
|
|
57
58
|
CLOCK_SKEW_SECONDS = 60
|
package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_protocols.py
CHANGED
|
@@ -17,11 +17,13 @@ PROTOCOL_REVISIONS: Dict[str, Tuple[str, ...]] = {
|
|
|
17
17
|
"entitlement-key-set": ("1",), "estate-record": ("1",), "execution-packet": ("1",),
|
|
18
18
|
"fabric-actions": ("0.1",), "fabric-activity": ("0.1",), "fabric-agent-call": ("1",), "fabric-connections": ("0.1",), "fabric-credential-envelope": ("1",), "fabric-load": ("0.1",), "fabric-dashboard": ("0.1",), "fabric-delegation": ("0.1",), "fabric-device": ("0.1",),
|
|
19
19
|
"fabric-interop": ("0.1", "0.2"), "fabric-local-channel": ("0.1",), "fabric-local-runner": ("0.1",), "fabric-machine-report": ("1",),
|
|
20
|
-
"fabric-operator-channel": ("0.1",), "fabric-package": ("1",),
|
|
20
|
+
"fabric-operator-channel": ("0.1",), "fabric-package": ("1",),
|
|
21
|
+
"fabric-peer-share": ("1",), "fabric-peer-share-envelope": ("1",), "fabric-peer-share-report": ("1",), "fabric-project-comms": ("0.1",), "fabric-proposal": ("1",),
|
|
21
22
|
"fabric-provider": ("0.1",), "fabric-relay": ("0.1",), "fabric-secret-envelope": ("1",), "fabric-service": ("0.1",),
|
|
22
23
|
"fabric-settings-backup": ("1",), "heartbeat": ("1",), "learning-note": ("1",), "machine-report": ("1",), "mandate": ("1",), "memory": ("0.1",), "node-summary": ("1",),
|
|
23
|
-
"operator-source": ("1",), "org-authority": ("1",), "org-authority-draft": ("1",), "org-key-set": ("1",), "org-key-set-draft": ("1",),
|
|
24
|
+
"operator-source": ("1",), "org-adaptation": ("1",), "org-adaptation-receipt": ("1",), "org-authority": ("1",), "org-authority-draft": ("1",), "org-key-set": ("1",), "org-key-set-draft": ("1",),
|
|
24
25
|
"org-membership": ("1",), "org-root-registration": ("1",), "pairing-message": ("1",),
|
|
26
|
+
"peer-share-deletion": ("1",), "peer-share-frame": ("1",), "peer-share-revocation": ("1",),
|
|
25
27
|
"pipeline": ("0.1",), "repair-request": ("1",), "repair-result": ("1",), "revocation-delta": ("1",), "session-handoff": ("1",), "service-load": ("1",), "share-registration": ("1",),
|
|
26
28
|
"share-registration-receipt": ("1",), "support-quality-daily": ("1",), "window-lease": ("1",),
|
|
27
29
|
}
|