@passioncode-ai/fabric-agent-adapter 0.62.0 → 0.64.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 +136 -0
- package/README.md +9 -5
- 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/hooks/hooks.json +13 -1
- package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/SKILL.md +6 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/SKILL.md +1 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/recipes/support-agent/README.md +48 -7
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/recipes/support-agent/channel-agent.example.json +103 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/recipes/support-agent/config.example.json +1 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/recipes/support-agent/support_agent.py +141 -17
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/package.md +2 -2
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/dashboard_kit.py +2 -0
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_auth.py +2 -2
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_estate.py +1 -1
- package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/kit.json +183 -3
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/SKILL.md +4 -3
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/agents-that-answer-people.md +5 -2
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/practices.md +52 -0
- package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/resilience-and-autonomy.md +2 -1
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/SKILL.md +21 -20
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/references/native-host-hooks.md +7 -1
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/references/on-demand-session.md +209 -0
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/references/project-board.md +34 -6
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/references/session-hooks.md +39 -6
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session.py +4 -0
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session_cli.py +455 -9
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session_env.py +35 -6
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session_hosts.py +4 -1
- package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session_mail.py +8 -1
|
@@ -39,8 +39,9 @@ import re
|
|
|
39
39
|
import secrets
|
|
40
40
|
import sys
|
|
41
41
|
import threading
|
|
42
|
+
import time
|
|
42
43
|
from pathlib import Path
|
|
43
|
-
from typing import Any, Callable, Dict, List, Mapping, Optional
|
|
44
|
+
from typing import Any, Callable, Dict, List, Mapping, Optional, Tuple, Union
|
|
44
45
|
|
|
45
46
|
if str(Path(__file__).resolve().parent) not in sys.path: # the kit installed beside it, under `python3 -I` too
|
|
46
47
|
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
@@ -128,6 +129,25 @@ def words(text: str) -> set:
|
|
|
128
129
|
return {w for w in (m.lower() for m in _WORD.findall(text)) if len(w) > 1 and w not in STOPWORDS}
|
|
129
130
|
|
|
130
131
|
|
|
132
|
+
#: How deep the knowledge file may nest (the file, its entries list, an entry, a member): far past any real entry.
|
|
133
|
+
MAX_KNOWLEDGE_DEPTH = 32
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def _depth(value: Any) -> int:
|
|
137
|
+
"""How deep a parsed JSON value nests, walked without recursion."""
|
|
138
|
+
deepest, stack = 0, [(value, 1)]
|
|
139
|
+
while stack:
|
|
140
|
+
item, level = stack.pop()
|
|
141
|
+
deepest = max(deepest, level)
|
|
142
|
+
if level > MAX_KNOWLEDGE_DEPTH:
|
|
143
|
+
return level
|
|
144
|
+
if isinstance(item, dict):
|
|
145
|
+
stack.extend((child, level + 1) for child in item.values())
|
|
146
|
+
elif isinstance(item, list):
|
|
147
|
+
stack.extend((child, level + 1) for child in item)
|
|
148
|
+
return deepest
|
|
149
|
+
|
|
150
|
+
|
|
131
151
|
class Knowledge:
|
|
132
152
|
"""Entries `{id, question, answer, tags?}` from a JSON file (`{"entries": [...]}`); ids are unique."""
|
|
133
153
|
|
|
@@ -138,14 +158,28 @@ class Knowledge:
|
|
|
138
158
|
raise RecipeError("every knowledge entry has an id of letters, digits, '.', '_', ':' or '-'")
|
|
139
159
|
if not isinstance(entry.get("answer"), str) or not entry["answer"].strip():
|
|
140
160
|
raise RecipeError("knowledge entry %s has no answer" % entry["id"])
|
|
161
|
+
if "question" in entry and not isinstance(entry["question"], str):
|
|
162
|
+
raise RecipeError("knowledge entry %s has a question that is not text" % entry["id"])
|
|
163
|
+
tags = entry.get("tags", [])
|
|
164
|
+
if not isinstance(tags, list) or not all(isinstance(tag, str) for tag in tags):
|
|
165
|
+
raise RecipeError("knowledge entry %s has tags that are not a list of text" % entry["id"])
|
|
141
166
|
if entry["id"] in self.entries:
|
|
142
167
|
raise RecipeError("knowledge entry %s appears twice" % entry["id"])
|
|
143
168
|
self.entries[entry["id"]] = dict(entry)
|
|
144
169
|
|
|
145
170
|
@classmethod
|
|
146
171
|
def load(cls, path: Path) -> "Knowledge":
|
|
147
|
-
|
|
148
|
-
|
|
172
|
+
try:
|
|
173
|
+
data = json.loads(Path(path).read_text(encoding="utf-8"))
|
|
174
|
+
except RecursionError:
|
|
175
|
+
raise RecipeError("the knowledge file is nested too deep to read") from None
|
|
176
|
+
except ValueError: # not JSON at all: refused like every other wrong shape, its content never quoted
|
|
177
|
+
raise RecipeError("the knowledge file %s is not JSON" % Path(path).name) from None
|
|
178
|
+
if not isinstance(data, dict) or not isinstance(data.get("entries"), list):
|
|
179
|
+
raise RecipeError("the knowledge file is an object with an `entries` list") # never an empty set by mistake
|
|
180
|
+
if _depth(data) > MAX_KNOWLEDGE_DEPTH: # the same verdict on every Python, whatever its parser's own limit
|
|
181
|
+
raise RecipeError("the knowledge file is nested too deep to read")
|
|
182
|
+
return cls(data["entries"])
|
|
149
183
|
|
|
150
184
|
def lookup(self, question: str, limit: int = 3) -> List[Dict[str, Any]]:
|
|
151
185
|
"""The entries sharing the most words with the question (its words against each entry's question, tags and
|
|
@@ -161,6 +195,52 @@ class Knowledge:
|
|
|
161
195
|
return [entry for _, _, entry in sorted(scored)[:limit]]
|
|
162
196
|
|
|
163
197
|
|
|
198
|
+
class KnowledgeSource:
|
|
199
|
+
"""The knowledge file as a declared source (the channel-agent standard's 7.1 and 7.4, FAA-197): read again when the
|
|
200
|
+
file changes, the last good version kept when a read fails (`degraded` until a good one), and `stale` past
|
|
201
|
+
`max_age_s`. The age is the last good version's: from its file's modification time, so a file nobody rebuilt ages
|
|
202
|
+
however often it is looked at, and a restart does not make old content new. The calling service shows `state()`
|
|
203
|
+
and opens one alert when it is stale."""
|
|
204
|
+
|
|
205
|
+
def __init__(self, path: Path, *, max_age_s: int, clock: Callable[[], float] = time.time):
|
|
206
|
+
if isinstance(max_age_s, bool) or not isinstance(max_age_s, int) or max_age_s < 1:
|
|
207
|
+
raise RecipeError("max_age_s is a whole number of seconds, at least 1")
|
|
208
|
+
self.path, self.max_age_s, self._clock = Path(path), max_age_s, clock
|
|
209
|
+
self._knowledge: Optional[Knowledge] = None
|
|
210
|
+
self._good_mtime = 0.0
|
|
211
|
+
self._seen: Optional[Tuple[int, int, int, int]] = None
|
|
212
|
+
self._degraded = False
|
|
213
|
+
|
|
214
|
+
def current(self) -> Knowledge:
|
|
215
|
+
try: # the time, the size, the inode and the status-change time, which no copy or `touch -r` can set back: a
|
|
216
|
+
# rewrite in one coarse tick, a copy that kept the time, or (on POSIX with fine timestamps) one in place
|
|
217
|
+
# with the same size still shows; on Windows st_ctime is the creation time and catches no such rewrite
|
|
218
|
+
stat = self.path.stat()
|
|
219
|
+
seen: Optional[Tuple[int, int, int, int]] = (stat.st_mtime_ns, stat.st_size, stat.st_ino, stat.st_ctime_ns)
|
|
220
|
+
except OSError:
|
|
221
|
+
seen = None
|
|
222
|
+
if self._knowledge is not None and seen is not None and seen == self._seen:
|
|
223
|
+
return self._knowledge # unchanged since this version was read (or failed to read)
|
|
224
|
+
try:
|
|
225
|
+
knowledge = Knowledge.load(self.path)
|
|
226
|
+
except (OSError, ValueError, RecursionError, RecipeError) as err: # RecursionError: JSON nested too deep
|
|
227
|
+
if self._knowledge is None:
|
|
228
|
+
if isinstance(err, RecipeError): # read, and refused: its message names the entry (ids are safe text)
|
|
229
|
+
raise
|
|
230
|
+
raise RecipeError("the knowledge file %s could not be read (%s)" % (self.path.name,
|
|
231
|
+
err.__class__.__name__))
|
|
232
|
+
self._seen, self._degraded = seen, True
|
|
233
|
+
else:
|
|
234
|
+
self._knowledge, self._seen, self._degraded = knowledge, seen, False
|
|
235
|
+
self._good_mtime = seen[0] / 1e9 if seen is not None else self._clock()
|
|
236
|
+
return self._knowledge
|
|
237
|
+
|
|
238
|
+
def state(self) -> Dict[str, Any]:
|
|
239
|
+
age = max(0, int(self._clock() - self._good_mtime)) if self._knowledge is not None else None
|
|
240
|
+
return {"id": "knowledge", "age_s": age, "max_age_s": self.max_age_s,
|
|
241
|
+
"stale": age is None or age > self.max_age_s, "degraded": self._degraded}
|
|
242
|
+
|
|
243
|
+
|
|
164
244
|
def _currency(unit: str) -> str:
|
|
165
245
|
unit = unit.lower()
|
|
166
246
|
return "rub" if unit.startswith("руб") else _CURRENCY.get(unit, unit)
|
|
@@ -241,6 +321,22 @@ SYSTEM = (
|
|
|
241
321
|
)
|
|
242
322
|
|
|
243
323
|
|
|
324
|
+
#: The channel-agent standard's 4.4 (FAA-197): a draft the checks refused is rewritten once with the refusal's reason.
|
|
325
|
+
REPAIR_NOTE = ("Your reply was refused by the checks: {reasons}. Write it again from the same entries only, keeping "
|
|
326
|
+
"every rule above, or answer exactly HANDOFF.")
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
def repairable(decision: Any) -> bool:
|
|
330
|
+
"""A hand-off the code checks made, with the draft they refused: no-knowledge, model-unsure and model:* are not."""
|
|
331
|
+
if not isinstance(decision, Mapping) or decision.get("action", "handoff") != "handoff" or decision.get("fallback"):
|
|
332
|
+
return False # a fallback already had its one repair
|
|
333
|
+
reasons = decision.get("reasons")
|
|
334
|
+
draft = decision.get("draft")
|
|
335
|
+
return (isinstance(draft, str) and bool(draft.strip()) and isinstance(reasons, list) and bool(reasons)
|
|
336
|
+
and all(isinstance(r, str) and r != "no-knowledge" and r != "model-unsure" and not r.startswith("model:")
|
|
337
|
+
for r in reasons))
|
|
338
|
+
|
|
339
|
+
|
|
244
340
|
def entry_text(entry: Mapping[str, Any]) -> str:
|
|
245
341
|
return "\n".join([str(entry.get("question", "")), str(entry["answer"]), " ".join(map(str, entry.get("tags", [])))])
|
|
246
342
|
|
|
@@ -357,7 +453,7 @@ def prune(state_dir: Path, keep_days: int, *, now: Optional[datetime] = None) ->
|
|
|
357
453
|
|
|
358
454
|
|
|
359
455
|
class SupportAgent:
|
|
360
|
-
def __init__(self, *, knowledge: Knowledge, client: Any, model: str, fallback: str, state_dir: Path,
|
|
456
|
+
def __init__(self, *, knowledge: Union[Knowledge, "KnowledgeSource"], client: Any, model: str, fallback: str, state_dir: Path,
|
|
361
457
|
max_tokens: int = 600, max_chars: int = 1200, lookup_limit: int = 3,
|
|
362
458
|
on_handoff: Optional[Callable[[Dict[str, Any]], None]] = None):
|
|
363
459
|
self.knowledge, self.client = knowledge, client
|
|
@@ -395,7 +491,8 @@ class SupportAgent:
|
|
|
395
491
|
def _outbox(self, decision: Dict[str, Any]) -> None:
|
|
396
492
|
# The outbox keeps the customer's own text until it is pruned: see `prune` and the README's retention note.
|
|
397
493
|
fabric_paths.ensure_private_dir(self.state_dir)
|
|
398
|
-
|
|
494
|
+
kept = {k: v for k, v in decision.items() if k != "draft"} # a person reads the question and the reasons
|
|
495
|
+
line = json.dumps(kept, ensure_ascii=False, separators=(",", ":")) + "\n"
|
|
399
496
|
_append_line(self.state_dir / "handoffs.jsonl", line.encode("utf-8"))
|
|
400
497
|
|
|
401
498
|
def prune(self, keep_days: int, *, now: Optional[datetime] = None) -> Dict[str, int]:
|
|
@@ -405,12 +502,22 @@ class SupportAgent:
|
|
|
405
502
|
self._log = DecisionLog(self.state_dir)
|
|
406
503
|
return removed
|
|
407
504
|
|
|
408
|
-
def answer(self, message_id: str, text: str, *, task_run_id: Optional[str] = None
|
|
409
|
-
|
|
505
|
+
def answer(self, message_id: str, text: str, *, task_run_id: Optional[str] = None,
|
|
506
|
+
repair: Optional[Mapping[str, Any]] = None) -> Dict[str, Any]:
|
|
507
|
+
"""The decision for one message: `{messageId, action: reply|handoff, reply?, citations, reasons, costUsd}`; a
|
|
508
|
+
hand-off the checks made also carries the refused `draft`. `repair=` that decision (under a new message id)
|
|
509
|
+
rewrites the draft once with the refusal's reasons: a pass is a reply with `repaired: true`, and a repair that
|
|
510
|
+
does not end in a reply (a second refusal, a model `HANDOFF`, no knowledge on the retry; not a `model:*`
|
|
511
|
+
outage) a hand-off with `fallback: true` (the channel-agent standard's 4.4 and 13.2, FAA-197)."""
|
|
410
512
|
if not isinstance(message_id, str) or not _ID.fullmatch(message_id):
|
|
411
513
|
raise RecipeError("message id: letters, digits, '.', '_', ':' or '-', up to 128")
|
|
412
514
|
if not isinstance(text, str) or not text.strip():
|
|
413
515
|
raise RecipeError("the message has no text")
|
|
516
|
+
if repair is not None and not repairable(repair):
|
|
517
|
+
raise RecipeError("repair= takes a hand-off the checks made, with its draft and reasons: the first call's "
|
|
518
|
+
"decision or the on_handoff copy (a repeat returns no draft, and a fallback had its repair)")
|
|
519
|
+
if repair is not None and repair.get("messageId") == message_id:
|
|
520
|
+
raise RecipeError("repair= runs under a new message id: the refused one returns its stored decision")
|
|
414
521
|
if getattr(self._local, "in_handoff", False):
|
|
415
522
|
raise ReentryError("answer() was called from on_handoff: queue the hand-off and call answer() again after "
|
|
416
523
|
"this call returns")
|
|
@@ -419,7 +526,7 @@ class SupportAgent:
|
|
|
419
526
|
done = self._log.get(message_id)
|
|
420
527
|
if done is not None:
|
|
421
528
|
return dict(done, repeated=True)
|
|
422
|
-
decision = self._decide(message_id, text, task_run_id)
|
|
529
|
+
decision = self._decide(message_id, text, task_run_id, repair)
|
|
423
530
|
at = _now()
|
|
424
531
|
if decision["action"] == "handoff": # before the decision is saved: a crash repeats a handoff, never loses one
|
|
425
532
|
self._local.in_handoff = True
|
|
@@ -427,27 +534,38 @@ class SupportAgent:
|
|
|
427
534
|
self.on_handoff(dict(decision, question=text, at=_stamp(at)))
|
|
428
535
|
finally:
|
|
429
536
|
self._local.in_handoff = False
|
|
430
|
-
self._log.append(decision, at)
|
|
537
|
+
self._log.append({k: v for k, v in decision.items() if k != "draft"}, at) # the draft is never stored
|
|
431
538
|
return dict(decision, repeated=False)
|
|
432
539
|
|
|
433
|
-
def _decide(self, message_id: str, text: str, task_run_id: Optional[str]
|
|
540
|
+
def _decide(self, message_id: str, text: str, task_run_id: Optional[str],
|
|
541
|
+
repair: Optional[Mapping[str, Any]] = None) -> Dict[str, Any]:
|
|
434
542
|
base = {"messageId": message_id, "citations": [], "costUsd": None}
|
|
435
|
-
|
|
543
|
+
knowledge = self.knowledge.current() if isinstance(self.knowledge, KnowledgeSource) else self.knowledge
|
|
544
|
+
entries = knowledge.lookup(text, self.lookup_limit)
|
|
436
545
|
if not entries:
|
|
437
|
-
|
|
546
|
+
gone = dict(base, action="handoff", reasons=["no-knowledge"])
|
|
547
|
+
return dict(gone, fallback=True) if repair is not None else gone
|
|
548
|
+
messages = build_messages(text, entries)
|
|
549
|
+
if repair is not None:
|
|
550
|
+
messages += [{"role": "assistant", "content": repair["draft"]},
|
|
551
|
+
{"role": "user", "content": REPAIR_NOTE.format(reasons=", ".join(repair["reasons"]))}]
|
|
438
552
|
try:
|
|
439
|
-
got = self.client.chat(
|
|
553
|
+
got = self.client.chat(messages, model=self.model, fallback=self.fallback,
|
|
440
554
|
max_tokens=self.max_tokens, temperature=0, task_run_id=task_run_id)
|
|
441
555
|
except fabric_openrouter.OpenRouterError as err:
|
|
442
556
|
return dict(base, action="handoff", reasons=["model:" + err.kind])
|
|
443
557
|
base["costUsd"] = got["receipt"].get("costUsd")
|
|
444
558
|
if got["content"].strip() == "HANDOFF":
|
|
445
|
-
|
|
559
|
+
unsure = dict(base, action="handoff", reasons=["model-unsure"])
|
|
560
|
+
return dict(unsure, fallback=True) if repair is not None else unsure # the repair was spent
|
|
446
561
|
checked = check_reply(got["content"], text, [e["id"] for e in entries], max_chars=self.max_chars,
|
|
447
562
|
sources={e["id"]: entry_text(e) for e in entries})
|
|
448
563
|
if not checked["ok"]:
|
|
449
|
-
|
|
450
|
-
|
|
564
|
+
refused = dict(base, action="handoff", reasons=checked["problems"], citations=checked["citations"],
|
|
565
|
+
draft=got["content"])
|
|
566
|
+
return dict(refused, fallback=True) if repair is not None else refused
|
|
567
|
+
replied = dict(base, action="reply", reply=checked["reply"], citations=checked["citations"], reasons=[])
|
|
568
|
+
return dict(replied, repaired=True) if repair is not None else replied
|
|
451
569
|
|
|
452
570
|
|
|
453
571
|
def from_config(config_path: Path, *, on_handoff: Optional[Callable[[Dict[str, Any]], None]] = None,
|
|
@@ -467,7 +585,13 @@ def from_config(config_path: Path, *, on_handoff: Optional[Callable[[Dict[str, A
|
|
|
467
585
|
ledger=fabric_usage.JsonlUsageLedger(state / "usage.jsonl"), prices=prices,
|
|
468
586
|
key_env=config.get("keyEnv", "OPENROUTER_API_KEY"),
|
|
469
587
|
project_id=config.get("projectId"), **overrides)
|
|
470
|
-
|
|
588
|
+
knowledge: Union[Knowledge, KnowledgeSource]
|
|
589
|
+
if config.get("knowledgeMaxAgeSeconds") is not None: # the file as a source with an age (FAA-197)
|
|
590
|
+
knowledge = KnowledgeSource(home / config["knowledge"], max_age_s=config["knowledgeMaxAgeSeconds"])
|
|
591
|
+
knowledge.current() # read once, now: a bad file fails here as a RecipeError
|
|
592
|
+
else:
|
|
593
|
+
knowledge = Knowledge.load(home / config["knowledge"])
|
|
594
|
+
return SupportAgent(knowledge=knowledge, client=client, model=config["model"],
|
|
471
595
|
fallback=config["fallback"], state_dir=state, max_tokens=int(config.get("maxTokens", 600)),
|
|
472
596
|
max_chars=int(config.get("maxChars", 1200)), on_handoff=on_handoff)
|
|
473
597
|
|
|
@@ -141,8 +141,8 @@ key, the partner and the organization contexts, and `placement_trust` / `placeme
|
|
|
141
141
|
one of those contexts and to no other (FAC-SEM-122, [the estate record reference](estate-record.md)). The partner's own
|
|
142
142
|
agents call the company's agents with the partner innermost in the delegation chain ([delegation](delegation.md)).
|
|
143
143
|
|
|
144
|
-
Still to come, each waiting on its owner: a partner's own space in a catalog and publishing to it (FAA-44,
|
|
145
|
-
PA-03).
|
|
144
|
+
Still to come, each waiting on its owner: a partner's own space in a catalog and publishing to it (FAA-44, the organization
|
|
145
|
+
edition's PA-03).
|
|
146
146
|
|
|
147
147
|
## What this version does not do
|
|
148
148
|
|
package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/dashboard_kit.py
CHANGED
|
@@ -395,6 +395,8 @@ def notice() -> int:
|
|
|
395
395
|
nothing else, when it owns none; every git call and the tree walk share NOTICE_BUDGET_S, and a notice that runs
|
|
396
396
|
out of it says nothing rather than a half-measured line (FAA-69)."""
|
|
397
397
|
until = time.monotonic() + NOTICE_BUDGET_S
|
|
398
|
+
if re.fullmatch(r"wf_[0-9a-f]{16}", os.environ.get("FABRIC_DISPATCH_WORKFLOW") or ""):
|
|
399
|
+
return 0 # a session started on a board request does that request only (FAA-201): no invitation to upgrade
|
|
398
400
|
try:
|
|
399
401
|
stdin_text = "" if sys.stdin is None or sys.stdin.isatty() else sys.stdin.read(65536)
|
|
400
402
|
root = repo_root(hook_cwd(stdin_text), until=until)
|
|
@@ -15,7 +15,7 @@ import threading
|
|
|
15
15
|
import time
|
|
16
16
|
import urllib.parse
|
|
17
17
|
from pathlib import Path
|
|
18
|
-
from typing import Any, Callable, Dict, List, Optional, Tuple
|
|
18
|
+
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
|
|
19
19
|
|
|
20
20
|
import sys as _sys # noqa: E402
|
|
21
21
|
from pathlib import Path as _Path # noqa: E402
|
|
@@ -136,7 +136,7 @@ class LoginCodes:
|
|
|
136
136
|
|
|
137
137
|
def __init__(self, state_dir: Optional[Path], ttl: int = LOGIN_CODE_TTL_SECONDS, *,
|
|
138
138
|
store: Optional["MemoryCodeStore"] = None, key: Optional[bytes] = None,
|
|
139
|
-
token: "Optional[str
|
|
139
|
+
token: "Optional[Union[str, Callable[[], str]]]" = None,
|
|
140
140
|
idle_seconds: int = OPERATOR_SESSION_IDLE_SECONDS,
|
|
141
141
|
max_seconds: int = OPERATOR_SESSION_MAX_SECONDS,
|
|
142
142
|
clock: Callable[[], float] = time.time):
|
package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_estate.py
CHANGED
|
@@ -32,7 +32,7 @@ import math
|
|
|
32
32
|
import re
|
|
33
33
|
import sys
|
|
34
34
|
from pathlib import Path
|
|
35
|
-
from typing import Any, Dict, List
|
|
35
|
+
from typing import Any, Dict, List, Optional
|
|
36
36
|
|
|
37
37
|
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
38
38
|
from fabric_crypto import ed25519_verify, is_small_order_key # noqa: E402,F401 - beside this file (FAA-65)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fabric-service-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.64.0",
|
|
4
4
|
"about": "The service kit's modules in dependency order: a module imports only modules listed before it. Written by fabric_kit.py build; edit the order and layers by hand, never the digests.",
|
|
5
5
|
"python": [
|
|
6
6
|
{
|
|
@@ -89,7 +89,7 @@
|
|
|
89
89
|
"fabric_jsvalue",
|
|
90
90
|
"fabric_time"
|
|
91
91
|
],
|
|
92
|
-
"sha256": "
|
|
92
|
+
"sha256": "0737ae1d1bb12586bae49de1f15ef1348bc207b5007cced9dad4c55bb5870874"
|
|
93
93
|
},
|
|
94
94
|
{
|
|
95
95
|
"module": "fabric_mandate",
|
|
@@ -297,7 +297,7 @@
|
|
|
297
297
|
"fabric_paths",
|
|
298
298
|
"fabric_platform"
|
|
299
299
|
],
|
|
300
|
-
"sha256": "
|
|
300
|
+
"sha256": "74ccc10239b65a3567cec5213a7b344cbd88d6eee1fd1c5f7be1d6daf985993c"
|
|
301
301
|
},
|
|
302
302
|
{
|
|
303
303
|
"module": "fabric_events",
|
|
@@ -4776,6 +4776,186 @@
|
|
|
4776
4776
|
"fabric-openrouter.mjs": "6041f3ab30f516658e1dbab4897a651cc8509b2136cfa8e769802c6b40ec2f35",
|
|
4777
4777
|
"fabric-package.schema.json": "2ba799f576fad0f608029223c4ccccd122152f1c5dbfa8ce8b31cd63ac92ad44",
|
|
4778
4778
|
"share-registration.schema.json": "34b3997eeaf936ebc861342a23045634a7c18c19f01d7ed4198310d81989f5b0"
|
|
4779
|
+
},
|
|
4780
|
+
"0.63.0": {
|
|
4781
|
+
"fabric_jsvalue.py": "052f1b40bab18600b3731dbdca27cbff17588c21d9458fa6241efd1340d0e09a",
|
|
4782
|
+
"fabric_time.py": "588f383da4ac6a243ea95b6ffc3a0aa99ca94245732dc4937734f190b7af4ae9",
|
|
4783
|
+
"fabric_identifiers.py": "0fd814d521ac3aa39907458265870753046a73f48af3d8581ab864b93b211607",
|
|
4784
|
+
"fabric_secrets.py": "3939aa8658a92ca387e9f0259538c4849fc407e429355ef9b4829460107a8597",
|
|
4785
|
+
"fabric_crypto.py": "9c34aee20dbe4d6742e4fdbaab68ceb934bbe7dbd3a904c0010824ace16cdeaa",
|
|
4786
|
+
"fabric_base.py": "f62bcc2ed9912660fa875d27a09f60867260cc4925f79873501fd9620be59611",
|
|
4787
|
+
"fabric_platform.py": "a9350dff55ef0fb3a23cdb4d0320a15868b15a8deb10d2eb74d72c58740c284b",
|
|
4788
|
+
"fabric_paths.py": "8a1ee742f80f436e9f28c61b4f049a9545de2a9e0d66bfc61faa6ee5d1ba3734",
|
|
4789
|
+
"fabric_estate_rules.py": "8b36a7f4108d9c2b1fb38c8bff344778c807bed631a99b4a563ecb0a49c10b1c",
|
|
4790
|
+
"fabric_estate.py": "0737ae1d1bb12586bae49de1f15ef1348bc207b5007cced9dad4c55bb5870874",
|
|
4791
|
+
"fabric_mandate.py": "fb19ce5359606bd6e3352ab02b2a12f49605ec6e5c49b55cce1f79c831325044",
|
|
4792
|
+
"fabric_delegation.py": "716f0ff7ac01095f54b24970beac1962128e8ff33026b243fc0467876142ab03",
|
|
4793
|
+
"fabric_share.py": "da36a466426fc29dd7dd40013167f3959a18375f8fedd228bf64f298cec1040c",
|
|
4794
|
+
"fabric_ceremonies.py": "a59e83cf4adb2669a9b98008e3d3a0cba991a35cbff97bc604fc82fe81cfd49f",
|
|
4795
|
+
"fabric_package_schema.py": "e623f0408d38e3343ed66222fb812907c6c52e1a286eb1c1176ab2e64640c9ad",
|
|
4796
|
+
"fabric_package.py": "1f2b9e73b2b32a7e1e887d4b69f87c6e21b4d1227ad924a235334223e6dd51ca",
|
|
4797
|
+
"fabric_protocols.py": "42529a7df4d753544d2e37a18ec81266d6e1e80477cb8558a3ab521416d45791",
|
|
4798
|
+
"fabric_actions.py": "ae21d1199614c4f74d1dcd1e0a87107a9d52434cd9e2d64aa24a7b1e196fb723",
|
|
4799
|
+
"fabric_connections.py": "7de55cb7d808f756d650effabfaa12027d40a5bd5d2f6d448c32969d47529ecb",
|
|
4800
|
+
"fabric_a2a.py": "8b01502131215bb4de1c01866187c56e99b791543923ed8577535573db33b919",
|
|
4801
|
+
"fabric_page_links.py": "1b57ea573c550e2f90d1ec3edcc655917ceaa0db9dd1635fccf422ec3024d25e",
|
|
4802
|
+
"fabric_lock.py": "dfaf92a7086cbb097d832a305869b7964e2d2a9da0e3365c8ec1685a7bb4fef9",
|
|
4803
|
+
"fabric_usage.py": "5cc2e03ccc5b06e81987ed7bc3eead7a7e6cbf1a7cb8e05c6c64315eed9d91b6",
|
|
4804
|
+
"fabric_backups.py": "1f3f0d7ecf361b29e9101e07f1c53e5dc304d598bf26fd59ec89f16b9fed2420",
|
|
4805
|
+
"fabric_descriptor.py": "0e7b44eef7983c2dffc433173c4e376facb181279d4bfdffa9ae8028216984c0",
|
|
4806
|
+
"fabric_context.py": "6ae311ddc7217a4aff915e7ce2646486931acd128462b7bebe4a52bbb5fbb6c7",
|
|
4807
|
+
"fabric_auth.py": "74ccc10239b65a3567cec5213a7b344cbd88d6eee1fd1c5f7be1d6daf985993c",
|
|
4808
|
+
"fabric_events.py": "cd94b186e2a8f8cb4ad2b6bd44d83ec5021933075e28c5d655f387f16dff42b8",
|
|
4809
|
+
"fabric_logs.py": "86dc55bfafa2a57264722e2403510a249b95de0e4a722b2e4bc3948e3bd93da9",
|
|
4810
|
+
"fabric_supervisors.py": "3c5b2e8c310738720b1c1b779be4710e90251013d9be4c68f9952edc0bf55d5f",
|
|
4811
|
+
"fabric_service.py": "4b13f90aa26881fbfe0d9f3077fbb3552f3ce0a3c59279d6baa92df872867d8d",
|
|
4812
|
+
"fabric_profiles.py": "26e21ec29c016000f8ca8053b7b489184854a73be1b7fdc0a18d12990203db9b",
|
|
4813
|
+
"fabric_interop_rules.py": "9bdb7989e57b9d0b0d15eb9982d396499823a5e2c528230b71c67866c736e0f5",
|
|
4814
|
+
"fabric_interop.py": "d27e31834611a37cf2a117746f2f60fc187ccc48718eb546ff923c5d0fce7dea",
|
|
4815
|
+
"fabric_hub.py": "b9ac852e601b1d54cf61351677a628afc69faeed7d1fc74ebe541c1cf38e0c2a",
|
|
4816
|
+
"fabric_board_shapes.py": "0a0e8d3d40e33dfd8c457aedb85330b2ff25f8c49a33eb6cbd5a5cc8a5007f7e",
|
|
4817
|
+
"fabric_board_link.py": "052bc3c474ece835bb428c37ca864810d04e394d991ddb9c93989a7a825073a5",
|
|
4818
|
+
"fabric_board_slots.py": "92b065e3df935547cf46653245f1129e4a1f8b59b0a41e8fa9ffdc41596ee592",
|
|
4819
|
+
"fabric_board_session.py": "1fd3c3884f3044b56a40f78b33ff88dcac9272284f6681ea359c662879826f7e",
|
|
4820
|
+
"fabric_board_fixture.py": "69761ce205ec640762ec9b591c6903e5a87f844e8d423b041681924027e39d1e",
|
|
4821
|
+
"fabric_board.py": "f86c40b8291f8c818f6924910f4022b013d5fd107abaf8825dd0aa6c5e418638",
|
|
4822
|
+
"fabric_http.py": "861e8e0978e303455dd6ff4a1b98a8b258600320f2870af0644384196fb03679",
|
|
4823
|
+
"fabric_openrouter.py": "d06a40f83f2fb8bd441ab3485ae882f1fbdd9cd7a9c1ec175eb47cb8ae2cce81",
|
|
4824
|
+
"fabric-jsvalue.mjs": "dfbdb8c41584972c9389750a41719374a1cfc5652ed438dd1623025190b0106e",
|
|
4825
|
+
"fabric-time.mjs": "3ff932069038a720df1c68c6492976906737b2e67e5c12d9c81c418597f661de",
|
|
4826
|
+
"fabric-identifiers.mjs": "a18c2a501a4aa170bfd3d99e571ad6b7662649b4d3b9d2a540c002ea42811bbf",
|
|
4827
|
+
"fabric-secrets.mjs": "5822a7e25ecf2e87b5bab4f99c1074af7379d8b49e2784283b5d7d8c25022cde",
|
|
4828
|
+
"fabric-crypto.mjs": "038e03689701e3c9721a1e7f8e0a4d02a52d6a5d1661b0dc84035edc3ebbfcf6",
|
|
4829
|
+
"fabric-base.mjs": "7804b2df7715dbbf9702a9b61b6b8cef2110f9a52b04cabd8fb0a66458d87512",
|
|
4830
|
+
"fabric-platform.mjs": "a8119d95ebba4d589a9e401f5d6fc8dda62d59d44e4c1ff811d97fac3771ac0c",
|
|
4831
|
+
"fabric-paths.mjs": "aa630d611e748a899e7043480f4a4e46b444f2e15ff945f30c2fbef21c1ceea5",
|
|
4832
|
+
"fabric-estate-rules.mjs": "7476be71174aa5b1026134f2b335abf647feaa55584e2f9de8618e3054aa8e1e",
|
|
4833
|
+
"fabric-estate.mjs": "e8a2e62d2313121e6cb502f6e6abad94d1d546a44b4d9b3ce2be29e7808e8cf3",
|
|
4834
|
+
"fabric-mandate.mjs": "b70f3de03a659e60bba7b5a0580b3582825cd7b8a8e30a28c79621919ec5b7bb",
|
|
4835
|
+
"fabric-delegation.mjs": "f1b6d156d050f8ceb9a21daaad8d39ab5de5d32e4cfe03817909388fca8ad27e",
|
|
4836
|
+
"fabric-share.mjs": "bfb26581c21ad984c2a337da02429f9f3a165cc582f8ba48de4d3dfb7d163a59",
|
|
4837
|
+
"fabric-ceremonies.mjs": "117d4374c9e95c930a38de7adbd4323774798f4fe6ec6f24afcd6f2c7298ca44",
|
|
4838
|
+
"fabric-package-schema.mjs": "cd28092375db0fc21ffab1f0bb7794f7f8979ee08c64d3b9faf4b3845357caa6",
|
|
4839
|
+
"fabric-package.mjs": "8cfb32eebbd4e1bcf28a6e4437344ae2dd54479b0e7c820f002e9f94ab9ecaad",
|
|
4840
|
+
"fabric-protocols.mjs": "5fd8765fdd3e507cc85c108492682bf5cbb4ae08873245c50374fb01d33c75e4",
|
|
4841
|
+
"fabric-actions.mjs": "06e42f5e24c96311d5417d0d1effa93479ef7128a7ab139903b2bcf59253f6be",
|
|
4842
|
+
"fabric-connections.mjs": "303432e547ce563e90ca9ffcd827f5612f51d3582929834e1add0fdaf6485dcc",
|
|
4843
|
+
"fabric-a2a.mjs": "51b72fe1d80649d659fcad3a8f96370db3476c4a7b30cbabd8522c138be69346",
|
|
4844
|
+
"fabric-page-links.mjs": "a8834926e9d4bbf7c4857f551451d2ebf6ac8873bbccaaf89c5e30cf45b17ee6",
|
|
4845
|
+
"fabric-lock.mjs": "02cf833c97a07a8b3c788f9c93f70c4b9c9066efa25318dccf0038e0fc79717f",
|
|
4846
|
+
"fabric-usage.mjs": "1c4d18004639409624ac3516a02f2b60bbf55abdb612e9a96c37e83666eb87a7",
|
|
4847
|
+
"fabric-backups.mjs": "cfd8c9085e3d597a783099c2bd2ffe2f3faa0daaadab46dedd30523894cfeb06",
|
|
4848
|
+
"fabric-descriptor.mjs": "7c0ef7d245c037eb9e859ab481f6e66fb3e5ef2cf6d1e0a8d32b6d303097ca43",
|
|
4849
|
+
"fabric-context.mjs": "1112d695e872f9825cbc94f08484974bff3880087bc29b8be59f885f03e8f7be",
|
|
4850
|
+
"fabric-auth.mjs": "10ff37a39f9e3adb4d0f05db66c45f49b10eef582e9220c213907964109d7e52",
|
|
4851
|
+
"fabric-events.mjs": "c463d6ffcae24229a5c608d8fcea4268d2ccc5024b19fa10e5a452284068fa71",
|
|
4852
|
+
"fabric-logs.mjs": "58467e5719c16e099d05bbedec47a95712506ab03c33f3cd4be1252e8498a946",
|
|
4853
|
+
"fabric-supervisors.mjs": "d45c38de0cb3ed85e083496c2d3c51dab004454accdd87f4762a512a6d442573",
|
|
4854
|
+
"fabric-service.mjs": "297fef11ac76dc831d12d24c5581b7a93587c300c20664db703b3f924c0f9f1a",
|
|
4855
|
+
"fabric-profiles.mjs": "314376cf2deb87f235fb18b11d3b532e211f585b9af0a9302b5f4cc39971b532",
|
|
4856
|
+
"fabric-interop-rules.mjs": "b4e386c359a48e49ffff8a842215760c18a7c98a0af4754a808a35fb2820d458",
|
|
4857
|
+
"fabric-interop.mjs": "989040aed33b2712a258244192bdf70e811a51076aa1b62f2adc2a6db051dbed",
|
|
4858
|
+
"fabric-hub.mjs": "c005115a864cfd67daa4862097bb14acfe5a60bf653897e5bf8a570dd80f1070",
|
|
4859
|
+
"fabric-board-shapes.mjs": "c1db7b7bd0d58d82bb6d8dfbc3cd1547a256a333052d367e82ff1b3edf2804d7",
|
|
4860
|
+
"fabric-board-link.mjs": "6e7d51f4fd9730d93455f50dd30e97907b8b9e1ca65a1d69f036f2e8ec05cc0d",
|
|
4861
|
+
"fabric-board-slots.mjs": "fc77a3c6e62aa18575568bef4f703cce2edaba8cef648813ebcdbb21bdc27a11",
|
|
4862
|
+
"fabric-board-session.mjs": "18555962e5f6d18080a3e7971c4c6640452542f6042d07917e7ebc073721c415",
|
|
4863
|
+
"fabric-board-fixture.mjs": "83c4b7e33ee47b68385faf2569d38d5c8a0c5cb0f8656090f41e1b0eed2f436e",
|
|
4864
|
+
"fabric-board.mjs": "15fbb4441fcf7d2f285375b75de64ad6cffea129c515084c110245376b07775f",
|
|
4865
|
+
"fabric-http.mjs": "96aa1730c79cb9a68dcc94642513eb1e88f0c2d99a45ca505c042e8c601d0436",
|
|
4866
|
+
"fabric-openrouter.mjs": "6041f3ab30f516658e1dbab4897a651cc8509b2136cfa8e769802c6b40ec2f35",
|
|
4867
|
+
"fabric-package.schema.json": "2ba799f576fad0f608029223c4ccccd122152f1c5dbfa8ce8b31cd63ac92ad44",
|
|
4868
|
+
"share-registration.schema.json": "34b3997eeaf936ebc861342a23045634a7c18c19f01d7ed4198310d81989f5b0"
|
|
4869
|
+
},
|
|
4870
|
+
"0.64.0": {
|
|
4871
|
+
"fabric_jsvalue.py": "052f1b40bab18600b3731dbdca27cbff17588c21d9458fa6241efd1340d0e09a",
|
|
4872
|
+
"fabric_time.py": "588f383da4ac6a243ea95b6ffc3a0aa99ca94245732dc4937734f190b7af4ae9",
|
|
4873
|
+
"fabric_identifiers.py": "0fd814d521ac3aa39907458265870753046a73f48af3d8581ab864b93b211607",
|
|
4874
|
+
"fabric_secrets.py": "3939aa8658a92ca387e9f0259538c4849fc407e429355ef9b4829460107a8597",
|
|
4875
|
+
"fabric_crypto.py": "9c34aee20dbe4d6742e4fdbaab68ceb934bbe7dbd3a904c0010824ace16cdeaa",
|
|
4876
|
+
"fabric_base.py": "f62bcc2ed9912660fa875d27a09f60867260cc4925f79873501fd9620be59611",
|
|
4877
|
+
"fabric_platform.py": "a9350dff55ef0fb3a23cdb4d0320a15868b15a8deb10d2eb74d72c58740c284b",
|
|
4878
|
+
"fabric_paths.py": "8a1ee742f80f436e9f28c61b4f049a9545de2a9e0d66bfc61faa6ee5d1ba3734",
|
|
4879
|
+
"fabric_estate_rules.py": "8b36a7f4108d9c2b1fb38c8bff344778c807bed631a99b4a563ecb0a49c10b1c",
|
|
4880
|
+
"fabric_estate.py": "0737ae1d1bb12586bae49de1f15ef1348bc207b5007cced9dad4c55bb5870874",
|
|
4881
|
+
"fabric_mandate.py": "fb19ce5359606bd6e3352ab02b2a12f49605ec6e5c49b55cce1f79c831325044",
|
|
4882
|
+
"fabric_delegation.py": "716f0ff7ac01095f54b24970beac1962128e8ff33026b243fc0467876142ab03",
|
|
4883
|
+
"fabric_share.py": "da36a466426fc29dd7dd40013167f3959a18375f8fedd228bf64f298cec1040c",
|
|
4884
|
+
"fabric_ceremonies.py": "a59e83cf4adb2669a9b98008e3d3a0cba991a35cbff97bc604fc82fe81cfd49f",
|
|
4885
|
+
"fabric_package_schema.py": "e623f0408d38e3343ed66222fb812907c6c52e1a286eb1c1176ab2e64640c9ad",
|
|
4886
|
+
"fabric_package.py": "1f2b9e73b2b32a7e1e887d4b69f87c6e21b4d1227ad924a235334223e6dd51ca",
|
|
4887
|
+
"fabric_protocols.py": "42529a7df4d753544d2e37a18ec81266d6e1e80477cb8558a3ab521416d45791",
|
|
4888
|
+
"fabric_actions.py": "ae21d1199614c4f74d1dcd1e0a87107a9d52434cd9e2d64aa24a7b1e196fb723",
|
|
4889
|
+
"fabric_connections.py": "7de55cb7d808f756d650effabfaa12027d40a5bd5d2f6d448c32969d47529ecb",
|
|
4890
|
+
"fabric_a2a.py": "8b01502131215bb4de1c01866187c56e99b791543923ed8577535573db33b919",
|
|
4891
|
+
"fabric_page_links.py": "1b57ea573c550e2f90d1ec3edcc655917ceaa0db9dd1635fccf422ec3024d25e",
|
|
4892
|
+
"fabric_lock.py": "dfaf92a7086cbb097d832a305869b7964e2d2a9da0e3365c8ec1685a7bb4fef9",
|
|
4893
|
+
"fabric_usage.py": "5cc2e03ccc5b06e81987ed7bc3eead7a7e6cbf1a7cb8e05c6c64315eed9d91b6",
|
|
4894
|
+
"fabric_backups.py": "1f3f0d7ecf361b29e9101e07f1c53e5dc304d598bf26fd59ec89f16b9fed2420",
|
|
4895
|
+
"fabric_descriptor.py": "0e7b44eef7983c2dffc433173c4e376facb181279d4bfdffa9ae8028216984c0",
|
|
4896
|
+
"fabric_context.py": "6ae311ddc7217a4aff915e7ce2646486931acd128462b7bebe4a52bbb5fbb6c7",
|
|
4897
|
+
"fabric_auth.py": "74ccc10239b65a3567cec5213a7b344cbd88d6eee1fd1c5f7be1d6daf985993c",
|
|
4898
|
+
"fabric_events.py": "cd94b186e2a8f8cb4ad2b6bd44d83ec5021933075e28c5d655f387f16dff42b8",
|
|
4899
|
+
"fabric_logs.py": "86dc55bfafa2a57264722e2403510a249b95de0e4a722b2e4bc3948e3bd93da9",
|
|
4900
|
+
"fabric_supervisors.py": "3c5b2e8c310738720b1c1b779be4710e90251013d9be4c68f9952edc0bf55d5f",
|
|
4901
|
+
"fabric_service.py": "4b13f90aa26881fbfe0d9f3077fbb3552f3ce0a3c59279d6baa92df872867d8d",
|
|
4902
|
+
"fabric_profiles.py": "26e21ec29c016000f8ca8053b7b489184854a73be1b7fdc0a18d12990203db9b",
|
|
4903
|
+
"fabric_interop_rules.py": "9bdb7989e57b9d0b0d15eb9982d396499823a5e2c528230b71c67866c736e0f5",
|
|
4904
|
+
"fabric_interop.py": "d27e31834611a37cf2a117746f2f60fc187ccc48718eb546ff923c5d0fce7dea",
|
|
4905
|
+
"fabric_hub.py": "b9ac852e601b1d54cf61351677a628afc69faeed7d1fc74ebe541c1cf38e0c2a",
|
|
4906
|
+
"fabric_board_shapes.py": "0a0e8d3d40e33dfd8c457aedb85330b2ff25f8c49a33eb6cbd5a5cc8a5007f7e",
|
|
4907
|
+
"fabric_board_link.py": "052bc3c474ece835bb428c37ca864810d04e394d991ddb9c93989a7a825073a5",
|
|
4908
|
+
"fabric_board_slots.py": "92b065e3df935547cf46653245f1129e4a1f8b59b0a41e8fa9ffdc41596ee592",
|
|
4909
|
+
"fabric_board_session.py": "1fd3c3884f3044b56a40f78b33ff88dcac9272284f6681ea359c662879826f7e",
|
|
4910
|
+
"fabric_board_fixture.py": "69761ce205ec640762ec9b591c6903e5a87f844e8d423b041681924027e39d1e",
|
|
4911
|
+
"fabric_board.py": "f86c40b8291f8c818f6924910f4022b013d5fd107abaf8825dd0aa6c5e418638",
|
|
4912
|
+
"fabric_http.py": "861e8e0978e303455dd6ff4a1b98a8b258600320f2870af0644384196fb03679",
|
|
4913
|
+
"fabric_openrouter.py": "d06a40f83f2fb8bd441ab3485ae882f1fbdd9cd7a9c1ec175eb47cb8ae2cce81",
|
|
4914
|
+
"fabric-jsvalue.mjs": "dfbdb8c41584972c9389750a41719374a1cfc5652ed438dd1623025190b0106e",
|
|
4915
|
+
"fabric-time.mjs": "3ff932069038a720df1c68c6492976906737b2e67e5c12d9c81c418597f661de",
|
|
4916
|
+
"fabric-identifiers.mjs": "a18c2a501a4aa170bfd3d99e571ad6b7662649b4d3b9d2a540c002ea42811bbf",
|
|
4917
|
+
"fabric-secrets.mjs": "5822a7e25ecf2e87b5bab4f99c1074af7379d8b49e2784283b5d7d8c25022cde",
|
|
4918
|
+
"fabric-crypto.mjs": "038e03689701e3c9721a1e7f8e0a4d02a52d6a5d1661b0dc84035edc3ebbfcf6",
|
|
4919
|
+
"fabric-base.mjs": "7804b2df7715dbbf9702a9b61b6b8cef2110f9a52b04cabd8fb0a66458d87512",
|
|
4920
|
+
"fabric-platform.mjs": "a8119d95ebba4d589a9e401f5d6fc8dda62d59d44e4c1ff811d97fac3771ac0c",
|
|
4921
|
+
"fabric-paths.mjs": "aa630d611e748a899e7043480f4a4e46b444f2e15ff945f30c2fbef21c1ceea5",
|
|
4922
|
+
"fabric-estate-rules.mjs": "7476be71174aa5b1026134f2b335abf647feaa55584e2f9de8618e3054aa8e1e",
|
|
4923
|
+
"fabric-estate.mjs": "e8a2e62d2313121e6cb502f6e6abad94d1d546a44b4d9b3ce2be29e7808e8cf3",
|
|
4924
|
+
"fabric-mandate.mjs": "b70f3de03a659e60bba7b5a0580b3582825cd7b8a8e30a28c79621919ec5b7bb",
|
|
4925
|
+
"fabric-delegation.mjs": "f1b6d156d050f8ceb9a21daaad8d39ab5de5d32e4cfe03817909388fca8ad27e",
|
|
4926
|
+
"fabric-share.mjs": "bfb26581c21ad984c2a337da02429f9f3a165cc582f8ba48de4d3dfb7d163a59",
|
|
4927
|
+
"fabric-ceremonies.mjs": "117d4374c9e95c930a38de7adbd4323774798f4fe6ec6f24afcd6f2c7298ca44",
|
|
4928
|
+
"fabric-package-schema.mjs": "cd28092375db0fc21ffab1f0bb7794f7f8979ee08c64d3b9faf4b3845357caa6",
|
|
4929
|
+
"fabric-package.mjs": "8cfb32eebbd4e1bcf28a6e4437344ae2dd54479b0e7c820f002e9f94ab9ecaad",
|
|
4930
|
+
"fabric-protocols.mjs": "5fd8765fdd3e507cc85c108492682bf5cbb4ae08873245c50374fb01d33c75e4",
|
|
4931
|
+
"fabric-actions.mjs": "06e42f5e24c96311d5417d0d1effa93479ef7128a7ab139903b2bcf59253f6be",
|
|
4932
|
+
"fabric-connections.mjs": "303432e547ce563e90ca9ffcd827f5612f51d3582929834e1add0fdaf6485dcc",
|
|
4933
|
+
"fabric-a2a.mjs": "51b72fe1d80649d659fcad3a8f96370db3476c4a7b30cbabd8522c138be69346",
|
|
4934
|
+
"fabric-page-links.mjs": "a8834926e9d4bbf7c4857f551451d2ebf6ac8873bbccaaf89c5e30cf45b17ee6",
|
|
4935
|
+
"fabric-lock.mjs": "02cf833c97a07a8b3c788f9c93f70c4b9c9066efa25318dccf0038e0fc79717f",
|
|
4936
|
+
"fabric-usage.mjs": "1c4d18004639409624ac3516a02f2b60bbf55abdb612e9a96c37e83666eb87a7",
|
|
4937
|
+
"fabric-backups.mjs": "cfd8c9085e3d597a783099c2bd2ffe2f3faa0daaadab46dedd30523894cfeb06",
|
|
4938
|
+
"fabric-descriptor.mjs": "7c0ef7d245c037eb9e859ab481f6e66fb3e5ef2cf6d1e0a8d32b6d303097ca43",
|
|
4939
|
+
"fabric-context.mjs": "1112d695e872f9825cbc94f08484974bff3880087bc29b8be59f885f03e8f7be",
|
|
4940
|
+
"fabric-auth.mjs": "10ff37a39f9e3adb4d0f05db66c45f49b10eef582e9220c213907964109d7e52",
|
|
4941
|
+
"fabric-events.mjs": "c463d6ffcae24229a5c608d8fcea4268d2ccc5024b19fa10e5a452284068fa71",
|
|
4942
|
+
"fabric-logs.mjs": "58467e5719c16e099d05bbedec47a95712506ab03c33f3cd4be1252e8498a946",
|
|
4943
|
+
"fabric-supervisors.mjs": "d45c38de0cb3ed85e083496c2d3c51dab004454accdd87f4762a512a6d442573",
|
|
4944
|
+
"fabric-service.mjs": "297fef11ac76dc831d12d24c5581b7a93587c300c20664db703b3f924c0f9f1a",
|
|
4945
|
+
"fabric-profiles.mjs": "314376cf2deb87f235fb18b11d3b532e211f585b9af0a9302b5f4cc39971b532",
|
|
4946
|
+
"fabric-interop-rules.mjs": "b4e386c359a48e49ffff8a842215760c18a7c98a0af4754a808a35fb2820d458",
|
|
4947
|
+
"fabric-interop.mjs": "989040aed33b2712a258244192bdf70e811a51076aa1b62f2adc2a6db051dbed",
|
|
4948
|
+
"fabric-hub.mjs": "c005115a864cfd67daa4862097bb14acfe5a60bf653897e5bf8a570dd80f1070",
|
|
4949
|
+
"fabric-board-shapes.mjs": "c1db7b7bd0d58d82bb6d8dfbc3cd1547a256a333052d367e82ff1b3edf2804d7",
|
|
4950
|
+
"fabric-board-link.mjs": "6e7d51f4fd9730d93455f50dd30e97907b8b9e1ca65a1d69f036f2e8ec05cc0d",
|
|
4951
|
+
"fabric-board-slots.mjs": "fc77a3c6e62aa18575568bef4f703cce2edaba8cef648813ebcdbb21bdc27a11",
|
|
4952
|
+
"fabric-board-session.mjs": "18555962e5f6d18080a3e7971c4c6640452542f6042d07917e7ebc073721c415",
|
|
4953
|
+
"fabric-board-fixture.mjs": "83c4b7e33ee47b68385faf2569d38d5c8a0c5cb0f8656090f41e1b0eed2f436e",
|
|
4954
|
+
"fabric-board.mjs": "15fbb4441fcf7d2f285375b75de64ad6cffea129c515084c110245376b07775f",
|
|
4955
|
+
"fabric-http.mjs": "96aa1730c79cb9a68dcc94642513eb1e88f0c2d99a45ca505c042e8c601d0436",
|
|
4956
|
+
"fabric-openrouter.mjs": "6041f3ab30f516658e1dbab4897a651cc8509b2136cfa8e769802c6b40ec2f35",
|
|
4957
|
+
"fabric-package.schema.json": "2ba799f576fad0f608029223c4ccccd122152f1c5dbfa8ce8b31cd63ac92ad44",
|
|
4958
|
+
"share-registration.schema.json": "34b3997eeaf936ebc861342a23045634a7c18c19f01d7ed4198310d81989f5b0"
|
|
4779
4959
|
}
|
|
4780
4960
|
},
|
|
4781
4961
|
"data": {
|
|
@@ -5,7 +5,7 @@ license: AGPL-3.0-only OR LicenseRef-PassionCode-Commercial
|
|
|
5
5
|
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.
|
|
6
6
|
metadata:
|
|
7
7
|
author: PassionCode.ai
|
|
8
|
-
version: "0.
|
|
8
|
+
version: "0.64.0"
|
|
9
9
|
contract-version: "0.1.0"
|
|
10
10
|
contract-commit: "3830a3353923e20f826d466610e1c479f120bec0"
|
|
11
11
|
---
|
|
@@ -91,7 +91,7 @@ prior art of its own:
|
|
|
91
91
|
|
|
92
92
|
- [practices.md](references/practices.md): the rules production agents taught us, each with
|
|
93
93
|
the check that catches it (judges and evals, numbers, money, people in the loop,
|
|
94
|
-
reliability, shipping). Organization members also read the knowledge base's
|
|
94
|
+
reliability, shipping, agents on a channel people write to). Organization members also read the knowledge base's
|
|
95
95
|
`knowledge/agent-practices.md` and add to it what this agent learns.
|
|
96
96
|
- [where-agents-run.md](references/where-agents-run.md): this machine, a personal cloud, an
|
|
97
97
|
organization; contexts; connecting a machine; sharing into an organization; selling.
|
|
@@ -225,7 +225,8 @@ Before the first stage that calls a model or spends money, read
|
|
|
225
225
|
a limit pauses and resumes, every model answer passes the call site's own acceptance, fallbacks
|
|
226
226
|
and checkers cross vendors, spend is a persisted ledger with idempotency keys, open questions
|
|
227
227
|
become recorded decisions, every fix has a test seen failing, and text a person reads carries no
|
|
228
|
-
em dash (U+2014), held by its prompt and a test
|
|
228
|
+
em dash (U+2014), held by its prompt and a test; and each piece of work closes with a retro
|
|
229
|
+
(`observatory_lesson` and insight/1 lines, practices.md *Reliability*). Report its checklist with a
|
|
229
230
|
file and a test per line, or `NOT_RUN` where the agent has no such stage.
|
|
230
231
|
|
|
231
232
|
## Completion format
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# Agents that answer people: what they carry from the first release
|
|
2
2
|
|
|
3
3
|
**Load this when the agent you are building answers people**: private chats, mail, store reviews, group chats, any
|
|
4
|
-
channel where a person asks and the agent writes back.
|
|
4
|
+
channel where a person asks and the agent writes back. An agent on such a channel also follows the channel-agent
|
|
5
|
+
standard: [practices](practices.md#agents-on-a-channel-people-write-to). Every item below is a part of the agent, built and tested in its
|
|
5
6
|
first release, not a later improvement. Each comes from the sources below: what the quality standard requires (A1,
|
|
6
7
|
A5), and what an audit of five production agents found working or failing (P1 to P8). The practices are field
|
|
7
8
|
evidence: one estate, five agents, no control; their numbers are that estate's, never a forecast.
|
|
@@ -18,7 +19,9 @@ per-channel targets, A3 (the rubric), A4 (the judge and its calibration), A6 (an
|
|
|
18
19
|
default) and the rest of its checks that caught real defects, and the practices' other items of P1, P3, P5, P6, P7 and
|
|
19
20
|
P8. Read both sources before you design the reply pipeline. The kit's support-agent recipe (building-fabric-services,
|
|
20
21
|
`recipes/support-agent/`) is one draft-and-check step of such an agent, not the agent: used alone it hands the message
|
|
21
|
-
to a person at the first refusal
|
|
22
|
+
to a person at the first refusal; with a repairing `on_handoff` it rewrites a refused draft once (`repair=`, a
|
|
23
|
+
spent repair being `fallback: true`: a second refusal, a declined repair or no knowledge on the retry; a `model:*`
|
|
24
|
+
outage during the repair is a plain hand-off), and research and the hold stay this page's.
|
|
22
25
|
|
|
23
26
|
## Contents
|
|
24
27
|
|