induscode 0.1.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.
- induscode/__init__.py +56 -0
- induscode/addons/__init__.py +176 -0
- induscode/addons/contract.py +923 -0
- induscode/addons/dispatch/__init__.py +43 -0
- induscode/addons/dispatch/event_dispatcher.py +348 -0
- induscode/addons/dispatch/tool_interceptor.py +349 -0
- induscode/addons/host.py +469 -0
- induscode/addons/loader.py +314 -0
- induscode/addons/manifest.py +232 -0
- induscode/addons/surface.py +199 -0
- induscode/boot/__init__.py +108 -0
- induscode/boot/auth_vault.py +323 -0
- induscode/boot/boot.py +210 -0
- induscode/boot/contract.py +223 -0
- induscode/boot/invocation.py +117 -0
- induscode/boot/runners/__init__.py +42 -0
- induscode/boot/runners/link_runner.py +82 -0
- induscode/boot/runners/oneshot_runner.py +85 -0
- induscode/boot/runners/registry.py +46 -0
- induscode/boot/runners/repl_runner.py +340 -0
- induscode/boot/runners/session.py +549 -0
- induscode/boot/stages.py +198 -0
- induscode/boot/upgrade/__init__.py +36 -0
- induscode/boot/upgrade/apply.py +125 -0
- induscode/boot/upgrade/upgrades.py +136 -0
- induscode/briefing/__init__.py +115 -0
- induscode/briefing/compose.py +414 -0
- induscode/briefing/contract.py +528 -0
- induscode/briefing/macros.py +721 -0
- induscode/briefing/skills.py +417 -0
- induscode/capability_deck/__init__.py +233 -0
- induscode/capability_deck/bridge_ledger/__init__.py +66 -0
- induscode/capability_deck/bridge_ledger/key.py +181 -0
- induscode/capability_deck/bridge_ledger/ledger.py +276 -0
- induscode/capability_deck/bridge_ledger/network.py +336 -0
- induscode/capability_deck/builtin_bridge.py +358 -0
- induscode/capability_deck/cards/__init__.py +116 -0
- induscode/capability_deck/cards/bg_process.py +482 -0
- induscode/capability_deck/cards/memory.py +226 -0
- induscode/capability_deck/cards/saas.py +280 -0
- induscode/capability_deck/cards/task.py +256 -0
- induscode/capability_deck/cards/todo.py +312 -0
- induscode/capability_deck/contract.py +450 -0
- induscode/capability_deck/manifest.py +126 -0
- induscode/capability_deck/provision.py +217 -0
- induscode/channels/__init__.py +146 -0
- induscode/channels/contract.py +585 -0
- induscode/channels/framer.py +132 -0
- induscode/channels/link/__init__.py +50 -0
- induscode/channels/link/dialog.py +246 -0
- induscode/channels/link/driver.py +308 -0
- induscode/channels/link/server.py +217 -0
- induscode/channels/oneshot.py +178 -0
- induscode/channels/ops.py +140 -0
- induscode/channels/session_ops.py +172 -0
- induscode/conductor/__init__.py +240 -0
- induscode/conductor/catalog.py +309 -0
- induscode/conductor/conductor.py +1084 -0
- induscode/conductor/contract.py +1035 -0
- induscode/conductor/matcher.py +291 -0
- induscode/conductor/serialize.py +575 -0
- induscode/conductor/signal_hub.py +382 -0
- induscode/conductor/skill_parse.py +294 -0
- induscode/conductor/transcript_store.py +449 -0
- induscode/console/__init__.py +236 -0
- induscode/console/app.py +1677 -0
- induscode/console/components/__init__.py +62 -0
- induscode/console/components/banner.py +499 -0
- induscode/console/components/banner_sweep.py +188 -0
- induscode/console/components/emblem.py +181 -0
- induscode/console/components/status_bar.py +102 -0
- induscode/console/contract.py +836 -0
- induscode/console/input/__init__.py +107 -0
- induscode/console/input/chord.py +197 -0
- induscode/console/input/dir_reader.py +113 -0
- induscode/console/input/intents.py +258 -0
- induscode/console/input/providers.py +469 -0
- induscode/console/mount.py +137 -0
- induscode/console/overlays/__init__.py +94 -0
- induscode/console/overlays/auth.py +503 -0
- induscode/console/overlays/pickers.py +526 -0
- induscode/console/overlays/router.py +129 -0
- induscode/console/overlays/sessions.py +232 -0
- induscode/console/reducer.py +145 -0
- induscode/console/resume_picker.py +156 -0
- induscode/console/slash_commands/__init__.py +78 -0
- induscode/console/slash_commands/builtins.py +254 -0
- induscode/console/slash_commands/dynamic.py +217 -0
- induscode/console/slash_commands/integrations.py +949 -0
- induscode/console/slash_commands/transcript.py +404 -0
- induscode/console/slash_commands/workbench.py +430 -0
- induscode/console/startup.py +434 -0
- induscode/console/theme/__init__.py +44 -0
- induscode/console/theme/adapter.py +168 -0
- induscode/console/theme/palette.py +128 -0
- induscode/console/theme/resolve.py +123 -0
- induscode/console/theme/tokens.py +185 -0
- induscode/console_slash/__init__.py +111 -0
- induscode/console_slash/contract.py +185 -0
- induscode/console_slash/registry.py +140 -0
- induscode/console_slash/resolve.py +194 -0
- induscode/console_slash/shared.py +172 -0
- induscode/entry.py +108 -0
- induscode/insight/__init__.py +153 -0
- induscode/insight/collector.py +73 -0
- induscode/insight/replay.py +305 -0
- induscode/insight/wrapper.py +1115 -0
- induscode/kit/__init__.py +82 -0
- induscode/kit/clipboard_image.py +215 -0
- induscode/kit/external_editor.py +120 -0
- induscode/kit/image.py +188 -0
- induscode/kit/shell.py +89 -0
- induscode/kit/tool_fetch.py +288 -0
- induscode/launch/__init__.py +224 -0
- induscode/launch/catalog.py +310 -0
- induscode/launch/contract.py +569 -0
- induscode/launch/credentials.py +852 -0
- induscode/launch/invocation/__init__.py +39 -0
- induscode/launch/invocation/attachments.py +281 -0
- induscode/launch/invocation/flags.py +210 -0
- induscode/launch/invocation/read.py +369 -0
- induscode/launch/invocation/usage.py +110 -0
- induscode/launch/oauth.py +808 -0
- induscode/launch/packages.py +299 -0
- induscode/launch/pickers.py +291 -0
- induscode/py.typed +0 -0
- induscode/runtime_bridge/__init__.py +166 -0
- induscode/runtime_bridge/bridges/__init__.py +66 -0
- induscode/runtime_bridge/bridges/_drive.py +268 -0
- induscode/runtime_bridge/bridges/builtins.py +177 -0
- induscode/runtime_bridge/bridges/claude_cli.py +198 -0
- induscode/runtime_bridge/bridges/codex_cli.py +203 -0
- induscode/runtime_bridge/bridges/indusagi_cli.py +217 -0
- induscode/runtime_bridge/broker.py +397 -0
- induscode/runtime_bridge/contract.py +734 -0
- induscode/runtime_bridge/sink.py +351 -0
- induscode/sessions/__init__.py +25 -0
- induscode/sessions/contract.py +119 -0
- induscode/sessions/library.py +350 -0
- induscode/settings/__init__.py +47 -0
- induscode/settings/contract.py +313 -0
- induscode/settings/manager.py +268 -0
- induscode/transcript_export/__init__.py +109 -0
- induscode/transcript_export/contract.py +522 -0
- induscode/transcript_export/publish.py +455 -0
- induscode/transcript_export/sgr.py +566 -0
- induscode/transcript_export/template.py +319 -0
- induscode/transcript_export/theme_bridge.py +325 -0
- induscode/window_budget/__init__.py +76 -0
- induscode/window_budget/budget/__init__.py +26 -0
- induscode/window_budget/budget/estimate.py +273 -0
- induscode/window_budget/budget/gate.py +60 -0
- induscode/window_budget/budget/slice.py +145 -0
- induscode/window_budget/condenser.py +170 -0
- induscode/window_budget/contract.py +329 -0
- induscode/window_budget/summarize/__init__.py +33 -0
- induscode/window_budget/summarize/condense.py +212 -0
- induscode/window_budget/summarize/prompt.py +241 -0
- induscode/workspace/__init__.py +30 -0
- induscode/workspace/brand.py +96 -0
- induscode/workspace/locator.py +269 -0
- induscode-0.1.0.dist-info/METADATA +97 -0
- induscode-0.1.0.dist-info/RECORD +167 -0
- induscode-0.1.0.dist-info/WHEEL +4 -0
- induscode-0.1.0.dist-info/entry_points.txt +3 -0
- induscode-0.1.0.dist-info/licenses/CREDITS.md +22 -0
- induscode-0.1.0.dist-info/licenses/NOTICE +7 -0
|
@@ -0,0 +1,522 @@
|
|
|
1
|
+
"""Transcript-export contract — the FROZEN type surface of the HTML publisher.
|
|
2
|
+
|
|
3
|
+
Port of the transcript-export half of TS ``src/briefing/contract.ts`` (the TS
|
|
4
|
+
build hosted these types on the briefing contract; the Python build relocates
|
|
5
|
+
them here, next to the modules that implement them — see the port note in
|
|
6
|
+
``induscode.briefing.contract``). It declares *only* shapes plus inert
|
|
7
|
+
constants — no string assembly, no theme math — so every behavior module (the
|
|
8
|
+
SGR painter, the theme bridge, the page-shell template, and the publisher) is
|
|
9
|
+
written against the names declared here. The file is intentionally small,
|
|
10
|
+
append-mostly, and stable.
|
|
11
|
+
|
|
12
|
+
Design stance:
|
|
13
|
+
|
|
14
|
+
- ANSI styling is a **table-driven SGR machine**. :class:`SgrState` is the
|
|
15
|
+
running style; an :data:`SgrToken` is one parsed unit (a text run or an SGR
|
|
16
|
+
command); an :data:`SgrMutation` is the partial state one code imposes. The
|
|
17
|
+
SGR *semantics* (which code sets which attribute) are the published
|
|
18
|
+
standard; the state shape is ours.
|
|
19
|
+
- HTML-export color is computed behind a :class:`ThemeBridge` over an
|
|
20
|
+
:class:`ExportTheme` token bag, with luminance read from a precomputed
|
|
21
|
+
:data:`LuminanceLut` (the WCAG relative-luminance formula is a standard; the
|
|
22
|
+
LUT and the fallback colors are ours).
|
|
23
|
+
- Publishing consumes :class:`PublishEntry` nodes whose
|
|
24
|
+
:class:`PublishMessage` is a *narrow structural view* of a framework
|
|
25
|
+
message — the publisher never imports the framework message union; it reads
|
|
26
|
+
``role`` / ``content`` / ``toolCallId`` / ``toolName`` and dispatches on
|
|
27
|
+
each part's ``type`` tag.
|
|
28
|
+
|
|
29
|
+
Faults stay briefing-owned: :class:`BriefingFault` (kinds ``publish`` /
|
|
30
|
+
``theme`` included) is imported from ``induscode.briefing.contract`` and
|
|
31
|
+
re-exported here, so the closed fault set lives in exactly one place.
|
|
32
|
+
|
|
33
|
+
Framework anchors (from the ``indusagi`` package):
|
|
34
|
+
|
|
35
|
+
- :class:`TextContent`, :class:`ImageContent` ← ``indusagi.ai``
|
|
36
|
+
|
|
37
|
+
This contract never re-declares those; it composes them. Wire-facing field
|
|
38
|
+
names that mirror the framework's keep their camelCase (``toolCallId``,
|
|
39
|
+
``toolName``, ``callId``, ``mimeType``), exactly as the framework's own
|
|
40
|
+
message dataclasses do; the camelCase :class:`ExportTheme` token names are
|
|
41
|
+
likewise wire-facing — the publisher hyphenates them into the ``--x-*`` CSS
|
|
42
|
+
custom properties the page shell reads.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
from __future__ import annotations
|
|
46
|
+
|
|
47
|
+
from collections.abc import Mapping, Sequence
|
|
48
|
+
from dataclasses import dataclass
|
|
49
|
+
from types import MappingProxyType
|
|
50
|
+
from typing import ClassVar, Final, Literal, Protocol, TypeAlias
|
|
51
|
+
|
|
52
|
+
from indusagi.ai import ImageContent, TextContent
|
|
53
|
+
|
|
54
|
+
from induscode.briefing.contract import (
|
|
55
|
+
BriefingFault,
|
|
56
|
+
BriefingFaultKind,
|
|
57
|
+
briefing_fault,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
__all__ = [
|
|
61
|
+
"BriefingFault",
|
|
62
|
+
"BriefingFaultKind",
|
|
63
|
+
"ExportTheme",
|
|
64
|
+
"FALLBACK_EXPORT_THEME",
|
|
65
|
+
"ImageContent",
|
|
66
|
+
"LuminanceLut",
|
|
67
|
+
"MessagePart",
|
|
68
|
+
"PublishEntry",
|
|
69
|
+
"PublishMessage",
|
|
70
|
+
"PublishOptions",
|
|
71
|
+
"PublishRole",
|
|
72
|
+
"Rgb",
|
|
73
|
+
"SGR_INITIAL_STATE",
|
|
74
|
+
"SHELL_SLOTS",
|
|
75
|
+
"SgrCommandToken",
|
|
76
|
+
"SgrMutation",
|
|
77
|
+
"SgrState",
|
|
78
|
+
"SgrTextToken",
|
|
79
|
+
"SgrToken",
|
|
80
|
+
"ShellSlot",
|
|
81
|
+
"TextContent",
|
|
82
|
+
"ThemeBridge",
|
|
83
|
+
"ThemeMode",
|
|
84
|
+
"ThinkingPart",
|
|
85
|
+
"ToolCallPart",
|
|
86
|
+
"TranscriptPart",
|
|
87
|
+
"WidgetRender",
|
|
88
|
+
"briefing_fault",
|
|
89
|
+
]
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
# ---------------------------------------------------------------------------
|
|
93
|
+
# SGR machine (table-driven ANSI styling)
|
|
94
|
+
# ---------------------------------------------------------------------------
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
@dataclass(frozen=True, slots=True, kw_only=True)
|
|
98
|
+
class SgrState:
|
|
99
|
+
"""The running display style of the table-driven SGR machine.
|
|
100
|
+
|
|
101
|
+
As the painter scans an ANSI stream it folds each SGR command into this
|
|
102
|
+
state via the mutation table, then emits a styled span whenever a text run
|
|
103
|
+
follows. Colors are stored as resolved CSS color strings (or ``None`` for
|
|
104
|
+
"inherit"); the boolean flags track the standard attribute set. The shape
|
|
105
|
+
is this rebuild's own; the SGR *semantics* (which code sets which
|
|
106
|
+
attribute) are the published standard.
|
|
107
|
+
|
|
108
|
+
- ``fg`` / ``bg`` — foreground / background color, or ``None`` for the
|
|
109
|
+
default.
|
|
110
|
+
- ``bold`` — increased weight (SGR 1).
|
|
111
|
+
- ``dim`` — decreased intensity (SGR 2).
|
|
112
|
+
- ``italic`` — italic (SGR 3).
|
|
113
|
+
- ``underline`` — underline (SGR 4).
|
|
114
|
+
- ``blink`` — blink (SGR 5/6), rendered as an attribute hook.
|
|
115
|
+
- ``inverse`` — swap fg/bg at render time (SGR 7).
|
|
116
|
+
- ``hidden`` — concealed text (SGR 8).
|
|
117
|
+
- ``strike`` — crossed-out (SGR 9).
|
|
118
|
+
"""
|
|
119
|
+
|
|
120
|
+
# Foreground color as a CSS color string, or None for the default.
|
|
121
|
+
fg: str | None = None
|
|
122
|
+
# Background color as a CSS color string, or None for the default.
|
|
123
|
+
bg: str | None = None
|
|
124
|
+
# Increased weight (SGR 1).
|
|
125
|
+
bold: bool = False
|
|
126
|
+
# Decreased intensity (SGR 2).
|
|
127
|
+
dim: bool = False
|
|
128
|
+
# Italic (SGR 3).
|
|
129
|
+
italic: bool = False
|
|
130
|
+
# Underline (SGR 4).
|
|
131
|
+
underline: bool = False
|
|
132
|
+
# Blink (SGR 5/6).
|
|
133
|
+
blink: bool = False
|
|
134
|
+
# Swap foreground and background at render time (SGR 7).
|
|
135
|
+
inverse: bool = False
|
|
136
|
+
# Concealed text (SGR 8).
|
|
137
|
+
hidden: bool = False
|
|
138
|
+
# Crossed-out (SGR 9).
|
|
139
|
+
strike: bool = False
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
#: The neutral starting style of the SGR machine — every attribute off and
|
|
143
|
+
#: both colors at their default. A reset (SGR 0) returns the state to exactly
|
|
144
|
+
#: this.
|
|
145
|
+
SGR_INITIAL_STATE: Final[SgrState] = SgrState(
|
|
146
|
+
fg=None,
|
|
147
|
+
bg=None,
|
|
148
|
+
bold=False,
|
|
149
|
+
dim=False,
|
|
150
|
+
italic=False,
|
|
151
|
+
underline=False,
|
|
152
|
+
blink=False,
|
|
153
|
+
inverse=False,
|
|
154
|
+
hidden=False,
|
|
155
|
+
strike=False,
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
@dataclass(frozen=True, slots=True)
|
|
160
|
+
class SgrTextToken:
|
|
161
|
+
"""A run of printable characters for the painter to wrap."""
|
|
162
|
+
|
|
163
|
+
kind: ClassVar[Literal["text"]] = "text"
|
|
164
|
+
text: str
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
@dataclass(frozen=True, slots=True)
|
|
168
|
+
class SgrCommandToken:
|
|
169
|
+
"""The numeric parameter list of one ``ESC[...m`` sequence (an empty list
|
|
170
|
+
means a bare ``ESC[m``, equivalent to a reset)."""
|
|
171
|
+
|
|
172
|
+
kind: ClassVar[Literal["sgr"]] = "sgr"
|
|
173
|
+
params: tuple[int, ...]
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
#: One unit produced by the SGR tokenizer.
|
|
177
|
+
#:
|
|
178
|
+
#: Scanning an ANSI stream splits it into an alternation of plain text runs
|
|
179
|
+
#: and SGR command sequences. The painter walks these tokens, applying ``sgr``
|
|
180
|
+
#: parameter lists to the running :class:`SgrState` and wrapping ``text`` runs
|
|
181
|
+
#: in a span styled by the state in force.
|
|
182
|
+
SgrToken: TypeAlias = SgrTextToken | SgrCommandToken
|
|
183
|
+
|
|
184
|
+
#: A field-level mutation an SGR code applies to the running
|
|
185
|
+
#: :class:`SgrState` — a mapping of :class:`SgrState` field names to their
|
|
186
|
+
#: new values (the Python form of the TS ``Partial<SgrState>``).
|
|
187
|
+
#:
|
|
188
|
+
#: The painter's dispatch table maps each handled SGR parameter to one of
|
|
189
|
+
#: these partial states; folding it over the current state yields the next
|
|
190
|
+
#: state. This is the data form of the SGR semantics — a table, not a switch —
|
|
191
|
+
#: so the set of handled codes is extended by adding table rows, not by
|
|
192
|
+
#: editing control flow.
|
|
193
|
+
SgrMutation: TypeAlias = Mapping[str, str | bool | None]
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
# ---------------------------------------------------------------------------
|
|
197
|
+
# Export theme + ThemeBridge (HTML transcript publishing)
|
|
198
|
+
# ---------------------------------------------------------------------------
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
@dataclass(frozen=True, slots=True, kw_only=True)
|
|
202
|
+
class ExportTheme:
|
|
203
|
+
"""The color token bag the HTML transcript is themed with.
|
|
204
|
+
|
|
205
|
+
These are this rebuild's *own* token names and default values —
|
|
206
|
+
deliberately not the legacy export's. The publisher emits them as CSS
|
|
207
|
+
custom properties and the page shell reads them. ``surface`` family tokens
|
|
208
|
+
are the large backgrounds; ``ink`` is the body text color; ``accent``
|
|
209
|
+
tints interactive affordances; the ``role`` tokens tint message
|
|
210
|
+
attribution. Values are any CSS color string. The camelCase token names
|
|
211
|
+
are wire-facing: the publisher hyphenates each into its ``--x-*`` CSS
|
|
212
|
+
custom property (``pageSurface`` → ``--x-page-surface``).
|
|
213
|
+
|
|
214
|
+
- ``pageSurface`` — the outermost page background.
|
|
215
|
+
- ``cardSurface`` — the surface of a message card.
|
|
216
|
+
- ``noteSurface`` — the surface of an informational/system note.
|
|
217
|
+
- ``ink`` — primary body text color.
|
|
218
|
+
- ``inkMuted`` — secondary/de-emphasized text color.
|
|
219
|
+
- ``accent`` — interactive affordance tint.
|
|
220
|
+
- ``border`` — hairline/separator color.
|
|
221
|
+
- ``userRole`` — attribution tint for user turns.
|
|
222
|
+
- ``agentRole`` — attribution tint for assistant turns.
|
|
223
|
+
- ``toolRole`` — attribution tint for tool turns.
|
|
224
|
+
"""
|
|
225
|
+
|
|
226
|
+
# Outermost page background.
|
|
227
|
+
pageSurface: str
|
|
228
|
+
# Surface of a message card.
|
|
229
|
+
cardSurface: str
|
|
230
|
+
# Surface of an informational/system note.
|
|
231
|
+
noteSurface: str
|
|
232
|
+
# Primary body text color.
|
|
233
|
+
ink: str
|
|
234
|
+
# Secondary/de-emphasized text color.
|
|
235
|
+
inkMuted: str
|
|
236
|
+
# Interactive affordance tint.
|
|
237
|
+
accent: str
|
|
238
|
+
# Hairline/separator color.
|
|
239
|
+
border: str
|
|
240
|
+
# Attribution tint for user turns.
|
|
241
|
+
userRole: str
|
|
242
|
+
# Attribution tint for assistant turns.
|
|
243
|
+
agentRole: str
|
|
244
|
+
# Attribution tint for tool turns.
|
|
245
|
+
toolRole: str
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
#: This rebuild's own fallback :class:`ExportTheme` — a calm dark palette used
|
|
249
|
+
#: when the active UI theme supplies no export colors.
|
|
250
|
+
#:
|
|
251
|
+
#: Chosen fresh for this rebuild (a deep slate page over a muted blue accent);
|
|
252
|
+
#: none of these values are the legacy export's magic constants. The
|
|
253
|
+
#: :class:`ThemeBridge` derives surface variants from these when a richer
|
|
254
|
+
#: source is unavailable.
|
|
255
|
+
FALLBACK_EXPORT_THEME: Final[ExportTheme] = ExportTheme(
|
|
256
|
+
pageSurface="#13161c",
|
|
257
|
+
cardSurface="#1b1f27",
|
|
258
|
+
noteSurface="#232834",
|
|
259
|
+
ink="#e6e8ee",
|
|
260
|
+
inkMuted="#9aa3b2",
|
|
261
|
+
accent="#6f9ce8",
|
|
262
|
+
border="#2c323d",
|
|
263
|
+
userRole="#7fd1b9",
|
|
264
|
+
agentRole="#6f9ce8",
|
|
265
|
+
toolRole="#d6a36b",
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
@dataclass(frozen=True, slots=True, kw_only=True)
|
|
270
|
+
class Rgb:
|
|
271
|
+
"""An RGB color decomposed into its 0–255 channels.
|
|
272
|
+
|
|
273
|
+
The neutral intermediate the :class:`ThemeBridge` parses a CSS color into
|
|
274
|
+
before computing luminance or deriving a brighter/darker variant.
|
|
275
|
+
``parse_color`` produces one; ``format_color`` renders one back to a CSS
|
|
276
|
+
string.
|
|
277
|
+
"""
|
|
278
|
+
|
|
279
|
+
# Red channel, 0–255.
|
|
280
|
+
r: int
|
|
281
|
+
# Green channel, 0–255.
|
|
282
|
+
g: int
|
|
283
|
+
# Blue channel, 0–255.
|
|
284
|
+
b: int
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
#: A precomputed lookup table for the per-channel sRGB→linear step of the
|
|
288
|
+
#: WCAG relative-luminance formula.
|
|
289
|
+
#:
|
|
290
|
+
#: The formula is a published standard; computing it per pixel is wasteful, so
|
|
291
|
+
#: the :class:`ThemeBridge` reads the linearized value of each 0–255 channel
|
|
292
|
+
#: from this table — one entry per channel value — and combines the three with
|
|
293
|
+
#: the standard weights. The table is ours (an implementation choice, expected
|
|
294
|
+
#: to be 256 entries long); the math it encodes is the standard.
|
|
295
|
+
LuminanceLut: TypeAlias = Sequence[float]
|
|
296
|
+
|
|
297
|
+
#: Whether a theme reads as light or dark, by the luminance of its page
|
|
298
|
+
#: surface.
|
|
299
|
+
#:
|
|
300
|
+
#: The :class:`ThemeBridge` branches surface derivation on this: a ``light``
|
|
301
|
+
#: base darkens its variants, a ``dark`` base lightens them. The threshold is
|
|
302
|
+
#: a fixed midpoint of the relative-luminance range.
|
|
303
|
+
ThemeMode: TypeAlias = Literal["light", "dark"]
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
class ThemeBridge(Protocol):
|
|
307
|
+
"""The color service behind the HTML transcript export.
|
|
308
|
+
|
|
309
|
+
Replaces the legacy export's loose color functions and ``{{TOKEN}}``
|
|
310
|
+
string replacement with one small typed service: it resolves an
|
|
311
|
+
:class:`ExportTheme`, reports the theme's :data:`ThemeMode`, derives
|
|
312
|
+
surface variants via luminance, computes relative luminance through a
|
|
313
|
+
:data:`LuminanceLut`, and projects the theme to the CSS custom properties
|
|
314
|
+
the page shell consumes. Implementations are pure with respect to their
|
|
315
|
+
inputs.
|
|
316
|
+
"""
|
|
317
|
+
|
|
318
|
+
# The resolved export theme this bridge serves.
|
|
319
|
+
theme: ExportTheme
|
|
320
|
+
|
|
321
|
+
def mode(self) -> ThemeMode:
|
|
322
|
+
"""Whether the theme reads as light or dark by page-surface
|
|
323
|
+
luminance."""
|
|
324
|
+
...
|
|
325
|
+
|
|
326
|
+
def luminance(self, color: str) -> float:
|
|
327
|
+
"""Compute the WCAG relative luminance (0–1) of a CSS color, reading
|
|
328
|
+
the per-channel sRGB→linear step from the bridge's
|
|
329
|
+
:data:`LuminanceLut`.
|
|
330
|
+
|
|
331
|
+
:param color: a CSS color string to measure
|
|
332
|
+
"""
|
|
333
|
+
...
|
|
334
|
+
|
|
335
|
+
def derive_surface(self, base: str, amount: float) -> str:
|
|
336
|
+
"""Derive a surface variant from a base color by stepping its
|
|
337
|
+
lightness toward or away from the theme's :data:`ThemeMode`.
|
|
338
|
+
|
|
339
|
+
:param base: the base CSS color to adjust
|
|
340
|
+
:param amount: signed lightness delta (positive lightens, negative
|
|
341
|
+
darkens)
|
|
342
|
+
"""
|
|
343
|
+
...
|
|
344
|
+
|
|
345
|
+
def to_css_vars(self) -> Mapping[str, str]:
|
|
346
|
+
"""Project the theme to the CSS custom properties the page shell
|
|
347
|
+
reads, keyed by their token name (e.g. ``"pageSurface"``) with CSS
|
|
348
|
+
color string values."""
|
|
349
|
+
...
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
# ---------------------------------------------------------------------------
|
|
353
|
+
# Transcript publishing
|
|
354
|
+
# ---------------------------------------------------------------------------
|
|
355
|
+
|
|
356
|
+
#: A message payload fragment as it appears in a transcript node — the union
|
|
357
|
+
#: the publisher renders into a message card.
|
|
358
|
+
#:
|
|
359
|
+
#: Aliases the framework content parts (:class:`TextContent` for prose,
|
|
360
|
+
#: :class:`ImageContent` for inline images) so a persisted transcript node
|
|
361
|
+
#: round-trips into the publisher without a parallel content type. The
|
|
362
|
+
#: publisher walks these and emits the corresponding HTML fragment.
|
|
363
|
+
TranscriptPart: TypeAlias = TextContent | ImageContent
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
@dataclass(frozen=True, slots=True)
|
|
367
|
+
class ThinkingPart:
|
|
368
|
+
"""An internal-reasoning content part, read structurally by the
|
|
369
|
+
publisher and rendered as a muted note rather than prose."""
|
|
370
|
+
|
|
371
|
+
type: ClassVar[Literal["thinking"]] = "thinking"
|
|
372
|
+
thinking: str
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
@dataclass(frozen=True, slots=True, kw_only=True)
|
|
376
|
+
class ToolCallPart:
|
|
377
|
+
"""A tool-call content part, read structurally so the assistant turn can
|
|
378
|
+
surface a call's name and arguments without importing the framework
|
|
379
|
+
message union. Structurally compatible with the framework's ``ToolCall``
|
|
380
|
+
(same ``type`` tag and field names), so either may appear in a
|
|
381
|
+
:class:`PublishMessage` content list."""
|
|
382
|
+
|
|
383
|
+
type: ClassVar[Literal["toolCall"]] = "toolCall"
|
|
384
|
+
id: str
|
|
385
|
+
name: str
|
|
386
|
+
arguments: Mapping[str, object]
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
#: The content-part union the publisher renders. Text and image parts alias
|
|
390
|
+
#: the framework's; thinking and tool-call parts are read structurally so the
|
|
391
|
+
#: assistant turn can surface a call's name and arguments.
|
|
392
|
+
MessagePart: TypeAlias = TextContent | ImageContent | ThinkingPart | ToolCallPart
|
|
393
|
+
|
|
394
|
+
#: The conversational role a published turn carries. Mirrors the conductor's
|
|
395
|
+
#: node roles but is declared locally so the publisher depends only on this
|
|
396
|
+
#: contract, not on the conductor subsystem.
|
|
397
|
+
PublishRole: TypeAlias = Literal["user", "assistant", "tool", "system", "condense", "note"]
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
@dataclass(frozen=True, slots=True, kw_only=True)
|
|
401
|
+
class PublishMessage:
|
|
402
|
+
"""The narrow view of a framework message the publisher reads.
|
|
403
|
+
|
|
404
|
+
Covers the three concrete message roles' content shapes plus the
|
|
405
|
+
tool-call linkage a tool result carries, without redeclaring the whole
|
|
406
|
+
framework union. ``toolCallId`` / ``toolName`` keep the framework's
|
|
407
|
+
camelCase, exactly as ``indusagi.ai.ToolResultMessage`` spells them.
|
|
408
|
+
"""
|
|
409
|
+
|
|
410
|
+
# The framework message role.
|
|
411
|
+
role: str
|
|
412
|
+
# The message content — a bare string or a list of content parts.
|
|
413
|
+
content: str | Sequence[MessagePart]
|
|
414
|
+
# Present on tool-result messages; links the node to its originating call.
|
|
415
|
+
toolCallId: str | None = None
|
|
416
|
+
# Present on tool-result messages; the invoked tool's display name.
|
|
417
|
+
toolName: str | None = None
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
@dataclass(frozen=True, slots=True, kw_only=True)
|
|
421
|
+
class PublishEntry:
|
|
422
|
+
"""One transcript node the publisher consumes.
|
|
423
|
+
|
|
424
|
+
Structurally a subset of the conductor's persisted entry: a ``role`` for
|
|
425
|
+
attribution and a ``message`` carrying the framework content this node
|
|
426
|
+
renders. The ``message`` is read with narrow structural access so the
|
|
427
|
+
publisher never imports the framework message union.
|
|
428
|
+
"""
|
|
429
|
+
|
|
430
|
+
# Attribution role for this node.
|
|
431
|
+
role: PublishRole
|
|
432
|
+
# The framework message payload to render.
|
|
433
|
+
message: PublishMessage
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
@dataclass(frozen=True, slots=True, kw_only=True)
|
|
437
|
+
class WidgetRender:
|
|
438
|
+
"""The pre-rendered HTML a custom (non-builtin) tool contributes to the
|
|
439
|
+
transcript, keyed back to its tool-call.
|
|
440
|
+
|
|
441
|
+
Custom tools render their call and result through the widget rasterizer
|
|
442
|
+
ahead of page assembly; the publisher looks the result up by ``callId``
|
|
443
|
+
when it emits that tool's card. A single ``html`` string carries the
|
|
444
|
+
rendered block (the legacy collapsed/expanded pair is intentionally
|
|
445
|
+
flattened to one).
|
|
446
|
+
"""
|
|
447
|
+
|
|
448
|
+
# The tool-call id this rendered block belongs to.
|
|
449
|
+
callId: str
|
|
450
|
+
# The rendered HTML block for the tool's call/result.
|
|
451
|
+
html: str
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
@dataclass(frozen=True, slots=True, kw_only=True)
|
|
455
|
+
class PublishOptions:
|
|
456
|
+
"""Options that configure a single transcript publish.
|
|
457
|
+
|
|
458
|
+
``theme`` overrides the export palette (otherwise the fallback is used);
|
|
459
|
+
``title`` labels the document; ``out_dir`` chooses where the file is
|
|
460
|
+
written; and ``widgets`` supplies any pre-rendered custom-tool blocks to
|
|
461
|
+
splice in. All are optional — publishing a transcript with no options
|
|
462
|
+
produces a themed, self-contained file under the default location.
|
|
463
|
+
"""
|
|
464
|
+
|
|
465
|
+
# Override export palette; defaults to FALLBACK_EXPORT_THEME.
|
|
466
|
+
theme: ExportTheme | None = None
|
|
467
|
+
# Document title for the published page.
|
|
468
|
+
title: str | None = None
|
|
469
|
+
# Directory to write the published file into.
|
|
470
|
+
out_dir: str | None = None
|
|
471
|
+
# Pre-rendered custom-tool blocks, keyed by their tool-call id.
|
|
472
|
+
widgets: Sequence[WidgetRender] | None = None
|
|
473
|
+
|
|
474
|
+
|
|
475
|
+
#: The placeholder token names the page shell template carries.
|
|
476
|
+
#:
|
|
477
|
+
#: The publisher interpolates each of these into the shell with a typed
|
|
478
|
+
#: substitution keyed off this mapping — replacing the legacy ad-hoc
|
|
479
|
+
#: ``{{TOKEN}}`` string replacement. The names are this rebuild's own; the
|
|
480
|
+
#: values are computed at publish time (theme vars, surface colors, inlined
|
|
481
|
+
#: CSS/JS, the base64 session payload, and the preserved library notices).
|
|
482
|
+
#:
|
|
483
|
+
#: Port note: the ``markedLib`` / ``highlightLib`` slot *keys* and token
|
|
484
|
+
#: strings are kept from the TS contract (the closed slot set is frozen), but
|
|
485
|
+
#: in the Python build they carry the markdown-it-py and Pygments license
|
|
486
|
+
#: notices — the libraries this port renders with — instead of marked /
|
|
487
|
+
#: highlight.js.
|
|
488
|
+
SHELL_SLOTS: Final[Mapping[str, str]] = MappingProxyType(
|
|
489
|
+
{
|
|
490
|
+
# CSS custom-property block derived from the ThemeBridge.
|
|
491
|
+
"themeVars": "THEME_VARS",
|
|
492
|
+
# Resolved page-surface color.
|
|
493
|
+
"pageSurface": "PAGE_SURFACE",
|
|
494
|
+
# Resolved card-surface color.
|
|
495
|
+
"cardSurface": "CARD_SURFACE",
|
|
496
|
+
# Resolved note-surface color.
|
|
497
|
+
"noteSurface": "NOTE_SURFACE",
|
|
498
|
+
# Inlined stylesheet body.
|
|
499
|
+
"styles": "STYLES",
|
|
500
|
+
# Inlined client renderer body.
|
|
501
|
+
"script": "SCRIPT",
|
|
502
|
+
# Base64-encoded session payload.
|
|
503
|
+
"payload": "PAYLOAD",
|
|
504
|
+
# Markdown library notice (markdown-it-py, MIT, preserved verbatim).
|
|
505
|
+
"markedLib": "MARKED_LIB",
|
|
506
|
+
# Highlighter library notice (Pygments, BSD-2-Clause, preserved verbatim).
|
|
507
|
+
"highlightLib": "HIGHLIGHT_LIB",
|
|
508
|
+
}
|
|
509
|
+
)
|
|
510
|
+
|
|
511
|
+
#: The literal slot-name type of :data:`SHELL_SLOTS`.
|
|
512
|
+
ShellSlot: TypeAlias = Literal[
|
|
513
|
+
"THEME_VARS",
|
|
514
|
+
"PAGE_SURFACE",
|
|
515
|
+
"CARD_SURFACE",
|
|
516
|
+
"NOTE_SURFACE",
|
|
517
|
+
"STYLES",
|
|
518
|
+
"SCRIPT",
|
|
519
|
+
"PAYLOAD",
|
|
520
|
+
"MARKED_LIB",
|
|
521
|
+
"HIGHLIGHT_LIB",
|
|
522
|
+
]
|