brainiac-cli 0.16.0__py3-none-any.whl

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 (77) hide show
  1. brain/__init__.py +39 -0
  2. brain/__main__.py +14 -0
  3. brain/_assets/AGENTS.md +564 -0
  4. brain/_assets/overlay/template/brand/brand-guide.md +24 -0
  5. brain/_assets/overlay/template/keywords/glossary.md +15 -0
  6. brain/_assets/overlay/template/people/roster.md +14 -0
  7. brain/_assets/overlay/template/voice/voice-profile.md +34 -0
  8. brain/_assets/routines/manifest.json +257 -0
  9. brain/_assets/scripts/brain-brief-mac.plist +59 -0
  10. brain/_assets/scripts/brain-brief.sh +74 -0
  11. brain/_assets/scripts/brain-synthesis-mac.plist +48 -0
  12. brain/_assets/scripts/brain-synthesis.sh +214 -0
  13. brain/_assets/scripts/install-brief-mac.sh +152 -0
  14. brain/_assets/scripts/install-brief-windows.ps1 +97 -0
  15. brain/_assets/scripts/register_tasks.py +386 -0
  16. brain/_assets/templates/company.md +21 -0
  17. brain/_assets/templates/concept.md +27 -0
  18. brain/_assets/templates/daily.md +20 -0
  19. brain/_assets/templates/decision.md +42 -0
  20. brain/_assets/templates/meeting.md +33 -0
  21. brain/_assets/templates/person.md +20 -0
  22. brain/_assets/templates/project.md +23 -0
  23. brain/_assets/templates/state-moc.md +40 -0
  24. brain/_version.py +12 -0
  25. brain/anchor.py +121 -0
  26. brain/audit.py +422 -0
  27. brain/backup.py +210 -0
  28. brain/brief.py +417 -0
  29. brain/capture.py +117 -0
  30. brain/chunk.py +249 -0
  31. brain/classification.py +134 -0
  32. brain/cli.py +1906 -0
  33. brain/config.py +368 -0
  34. brain/connect.py +362 -0
  35. brain/context.py +108 -0
  36. brain/core.py +3018 -0
  37. brain/doctor.py +1161 -0
  38. brain/egress.py +148 -0
  39. brain/embed.py +857 -0
  40. brain/encryption.py +217 -0
  41. brain/frontmatter.py +102 -0
  42. brain/golden_probe.py +678 -0
  43. brain/graph.py +369 -0
  44. brain/graphify.py +352 -0
  45. brain/index.py +1576 -0
  46. brain/ingest/__init__.py +19 -0
  47. brain/ingest/handlers/__init__.py +43 -0
  48. brain/ingest/handlers/base.py +95 -0
  49. brain/ingest/handlers/docx.py +78 -0
  50. brain/ingest/handlers/email.py +228 -0
  51. brain/ingest/handlers/html.py +142 -0
  52. brain/ingest/handlers/image.py +91 -0
  53. brain/ingest/handlers/pdf.py +99 -0
  54. brain/ingest/handlers/pptx.py +69 -0
  55. brain/ingest/handlers/tables.py +41 -0
  56. brain/ingest/handlers/text.py +43 -0
  57. brain/ingest/handlers/xlsx.py +100 -0
  58. brain/ingest/handlers/zip.py +163 -0
  59. brain/ingest/pipeline.py +839 -0
  60. brain/ingest/transcript.py +158 -0
  61. brain/init.py +870 -0
  62. brain/maintenance.py +2266 -0
  63. brain/mcp_adapter.py +217 -0
  64. brain/multihop.py +232 -0
  65. brain/notes.py +195 -0
  66. brain/overlay.py +183 -0
  67. brain/projection.py +79 -0
  68. brain/rerank.py +425 -0
  69. brain/snapshot.py +231 -0
  70. brain/update.py +743 -0
  71. brain/vectors.py +225 -0
  72. brainiac_cli-0.16.0.dist-info/METADATA +306 -0
  73. brainiac_cli-0.16.0.dist-info/RECORD +77 -0
  74. brainiac_cli-0.16.0.dist-info/WHEEL +5 -0
  75. brainiac_cli-0.16.0.dist-info/entry_points.txt +4 -0
  76. brainiac_cli-0.16.0.dist-info/licenses/LICENSE +202 -0
  77. brainiac_cli-0.16.0.dist-info/top_level.txt +1 -0
brain/egress.py ADDED
@@ -0,0 +1,148 @@
1
+ """The single egress chokepoint (SEC-01).
2
+
3
+ Every content-returning surface — the CLI subcommands and the optional MCP
4
+ adapter — funnels its results through ONE helper here before stdout, so the
5
+ deny-by-default classification gate cannot be silently bypassed by a *new*
6
+ subcommand that forgets to filter. This is the "force all integration through the
7
+ gated boundary" leg of the SEC-01 hardening (r2-codex): a later
8
+ ``graph-expand`` / ``bases-query`` path must not surface a tier a sibling path
9
+ already withholds.
10
+
11
+ This module is an egress *decision* mechanism, NOT containment — a file-capable
12
+ harness reads the Markdown directly and bypasses it entirely (proven by
13
+ tests/test_direct_file_read.py). Real containment of sensitive tiers is
14
+ workspace *projection* (brain.projection) + the host/VM trust split. Per the
15
+ vault's own C-3 doctrine, a CLI/prompt-layer filter is defence-in-depth, never
16
+ the gate. See docs/operations/egress-provider-posture.md.
17
+ """
18
+ from __future__ import annotations
19
+
20
+ import json
21
+ from pathlib import Path
22
+ from typing import Any, Iterable
23
+
24
+ from . import classification as cls
25
+
26
+ # CANONICAL enumeration of every content-returning subcommand (r2-codex).
27
+ # Egress/classification-gate test coverage MUST cover each of these — a content
28
+ # path that is not on this list and not gated is a posture gap. ``rerank`` is not
29
+ # a separate command: ``search --rerank`` re-orders the SAME hits, then the same
30
+ # gate fires (covered explicitly in the test).
31
+ CONTENT_RETURNING_SUBCOMMANDS: tuple[str, ...] = (
32
+ "search", # fused RRF BM25+dense
33
+ "hybrid-search", # alias of search
34
+ "grep", # lexical scan
35
+ "bases-query", # structured frontmatter view
36
+ "graph-expand", # wikilink-BFS + PPR discovery candidates
37
+ "get", # one note by id
38
+ "read", # alias of get
39
+ "recent", # recently-updated list
40
+ # CUT-03 maintenance rituals that surface note-id+classification listings
41
+ # (curate's unclassified-notes lint, integrity's near-dup pairs, promote-
42
+ # scan's raw/ candidates) route through the SAME egress.apply_gate
43
+ # chokepoint as the read verbs above — brain-cli-gaps.md G1 explicitly
44
+ # requires both members of a near-dup pair to be gated before surfacing.
45
+ "curate", "integrity", "promote-scan",
46
+ # UX-02 summary surfaces (H-1) — brief/digest build their note list from
47
+ # the SAME recent() feed as `recent` above, so they route through the
48
+ # SAME apply_gate chokepoint before that list is assembled into the
49
+ # brief/digest structure. See BrainCore.brief / BrainCore.digest.
50
+ "brief", "digest",
51
+ # GRF-01 (ADR-0003 Ruling 6/(a)) — graphify's INFERRED link candidates
52
+ # name note ids the same way graph-expand's discovery candidates do;
53
+ # gated the SAME way (both endpoints of a candidate pair must clear the
54
+ # cap) before they reach the CLI output or a maintain hot-queue entry.
55
+ "graphify",
56
+ )
57
+
58
+ # Host-broker / maintenance commands return STATUS, never note bodies — they are
59
+ # intentionally NOT gated by classification (nothing to leak) and so are NOT in
60
+ # the list above. Listed here for the audit so the split is explicit.
61
+ NON_CONTENT_SUBCOMMANDS: tuple[str, ...] = (
62
+ "draft-capture", "rebuild", "sync", "snapshot", "status", "project",
63
+ "write", "verify-audit", "anchor", "verify-anchor", "backup", "restore",
64
+ "check", "health", "maintain",
65
+ # TMP-02: two note ids + a status/audit summary — no note bodies returned.
66
+ "supersede",
67
+ # SUI-02: per-client wiring status (diff/confirm/write report) — no note
68
+ # bodies, no BrainCore construction at all. Host-only (refused at the
69
+ # VM_ALLOWED gate in cli.py before this command ever dispatches).
70
+ "connect",
71
+ )
72
+
73
+
74
+ def apply_gate(
75
+ items: Iterable[dict], max_tier: str = cls.DEFAULT_MAX_TIER,
76
+ key: str = "classification",
77
+ ) -> tuple[list[dict], dict]:
78
+ """THE chokepoint: deny-by-default filter + honest redaction report.
79
+
80
+ Returns ``(surfaced, egress_report)``. Used by the CLI for every
81
+ content-returning subcommand and by the MCP adapter — one code path, no
82
+ second egress surface to keep in sync.
83
+ """
84
+ items = list(items)
85
+ flt = cls.ClassificationFilter(max_tier=max_tier)
86
+ return flt.filter(items, key=key), flt.redaction_report(items, key=key)
87
+
88
+
89
+ # --------------------------------------------------------------------------
90
+ # Trusted-harness allowlist (SEC-01, HARDENED:claude / r2-claude)
91
+ # --------------------------------------------------------------------------
92
+ # Reconciles openness vs control. "Openness" does NOT mean "any app" — it means
93
+ # any harness that PASSES the vendor-posture bar (no-train/ZDR scope covering
94
+ # tool-call/API egress of vault content + MNPI). The bar is owned here as a
95
+ # gating checklist; val-03's cross-harness set must EQUAL this allowlist. The
96
+ # register itself is data (docs/harness-allowlist.json) so it is reviewable and
97
+ # diffable; this loader is the typed accessor + invariants.
98
+ ALLOWLIST_PATH = Path(__file__).resolve().parents[2] / "docs" / "harness-allowlist.json"
99
+
100
+ _POSTURE_STATES = frozenset({"VERIFIED", "PENDING", "REJECTED"})
101
+
102
+
103
+ def load_allowlist(path: Path | None = None) -> dict[str, Any]:
104
+ """Load the trusted-harness allowlist register. Raises on a malformed file
105
+ (fail-closed: a register we cannot parse must not be treated as 'allow all')."""
106
+ p = Path(path) if path else ALLOWLIST_PATH
107
+ data = json.loads(p.read_text(encoding="utf-8"))
108
+ if "harnesses" not in data or not isinstance(data["harnesses"], list):
109
+ raise ValueError(f"allowlist {p} missing a 'harnesses' list")
110
+ for h in data["harnesses"]:
111
+ missing = {"id", "vendor", "posture_status", "verification_step", "owner"} - set(h)
112
+ if missing:
113
+ raise ValueError(f"allowlist entry {h.get('id')!r} missing keys: {sorted(missing)}")
114
+ if h["posture_status"] not in _POSTURE_STATES:
115
+ raise ValueError(
116
+ f"allowlist entry {h['id']!r} has posture_status "
117
+ f"{h['posture_status']!r}; expected one of {sorted(_POSTURE_STATES)}"
118
+ )
119
+ return data
120
+
121
+
122
+ def is_allowed(harness_id: str, path: Path | None = None) -> bool:
123
+ """True iff ``harness_id`` is on the allowlist AND its vendor posture is
124
+ VERIFIED. PENDING/REJECTED => default-deny (the posture bar is not met).
125
+
126
+ NOTE: the brain CLI cannot reliably identify its caller, so this is a
127
+ GOVERNANCE gate (consumed by val-03 + the cyber review + deployment policy),
128
+ not a runtime per-request gate. The runtime control is the classification
129
+ gate (apply_gate) + projection. Until a vendor's no-train/ZDR scope is
130
+ contractually VERIFIED, the harness is not 'allowed' and must run only
131
+ against a projected (sensitive-tier-free) workspace.
132
+ """
133
+ data = load_allowlist(path)
134
+ for h in data["harnesses"]:
135
+ if h["id"] == harness_id:
136
+ return h["posture_status"] == "VERIFIED"
137
+ return False
138
+
139
+
140
+ def posture_summary(path: Path | None = None) -> dict[str, Any]:
141
+ """Counts by posture_status for the evidence table / CSF profile."""
142
+ data = load_allowlist(path)
143
+ out: dict[str, int] = {s: 0 for s in _POSTURE_STATES}
144
+ for h in data["harnesses"]:
145
+ out[h["posture_status"]] += 1
146
+ return {"total": len(data["harnesses"]), "by_status": out,
147
+ "verified_ids": [h["id"] for h in data["harnesses"]
148
+ if h["posture_status"] == "VERIFIED"]}