indusagi-coding-agent 0.2.5 → 0.2.9
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 +9 -0
- package/README.md +9 -5
- package/dist/entry.js +14267 -24282
- package/dist/guardrails.js +924 -7867
- package/dist/index.js +13706 -23775
- package/package.json +3 -2
- package/src/_decls/entry.ts +18 -0
- package/src/_decls/guardrails.ts +35 -0
- package/src/_decls/index.ts +26 -0
- package/src/addons/contract.ts +236 -0
- package/src/addons/dispatch/event-dispatcher.ts +164 -0
- package/src/addons/dispatch/index.ts +25 -0
- package/src/addons/dispatch/tool-interceptor.ts +208 -0
- package/src/addons/host.ts +225 -0
- package/src/addons/index.ts +112 -0
- package/src/addons/manifest.ts +158 -0
- package/src/addons/sandbox.ts +170 -0
- package/src/addons/surface.ts +78 -0
- package/src/boot/auth-vault.ts +195 -0
- package/src/boot/boot.ts +138 -0
- package/src/boot/contract.ts +238 -0
- package/src/boot/heap.ts +59 -0
- package/src/boot/index.ts +28 -0
- package/src/boot/invocation.ts +93 -0
- package/src/boot/runners/addon-wiring.ts +153 -0
- package/src/boot/runners/checkpoint.ts +169 -0
- package/src/boot/runners/delegate-runner.ts +294 -0
- package/src/boot/runners/index.ts +13 -0
- package/src/boot/runners/link-runner.ts +45 -0
- package/src/boot/runners/memdir.ts +168 -0
- package/src/boot/runners/oneshot-runner.ts +58 -0
- package/src/boot/runners/read-state.ts +90 -0
- package/src/boot/runners/registry.ts +42 -0
- package/src/boot/runners/repl-runner.ts +143 -0
- package/src/boot/runners/server-mode.ts +121 -0
- package/src/boot/runners/session.ts +641 -0
- package/src/boot/server-token.ts +148 -0
- package/src/boot/stages.ts +167 -0
- package/src/boot/upgrade/apply.ts +94 -0
- package/src/boot/upgrade/index.ts +13 -0
- package/src/boot/upgrade/upgrades.ts +289 -0
- package/src/briefing/compose.ts +150 -0
- package/src/briefing/context-docs.ts +19 -0
- package/src/briefing/contract.ts +717 -0
- package/src/briefing/index.ts +31 -0
- package/src/briefing/macros.ts +97 -0
- package/src/briefing/skills.ts +47 -0
- package/src/capability-deck/bridge-ledger/index.ts +27 -0
- package/src/capability-deck/bridge-ledger/key.ts +67 -0
- package/src/capability-deck/bridge-ledger/ledger.ts +131 -0
- package/src/capability-deck/bridge-ledger/network.ts +117 -0
- package/src/capability-deck/builtin-bridge.ts +312 -0
- package/src/capability-deck/cards/bg-process-card.ts +335 -0
- package/src/capability-deck/cards/index.ts +115 -0
- package/src/capability-deck/cards/memory-card.ts +146 -0
- package/src/capability-deck/cards/plan-file.ts +97 -0
- package/src/capability-deck/cards/plan-tools.ts +185 -0
- package/src/capability-deck/cards/saas-card.ts +183 -0
- package/src/capability-deck/cards/task-card.ts +207 -0
- package/src/capability-deck/cards/todo-card.ts +168 -0
- package/src/capability-deck/cards/workflow-card.ts +247 -0
- package/src/capability-deck/contract.ts +388 -0
- package/src/capability-deck/index.ts +48 -0
- package/src/capability-deck/manifest.ts +109 -0
- package/src/capability-deck/provision.ts +169 -0
- package/src/channels/contract.ts +191 -0
- package/src/channels/framer.ts +50 -0
- package/src/channels/index.ts +101 -0
- package/src/channels/link/dialog.ts +129 -0
- package/src/channels/link/driver.ts +190 -0
- package/src/channels/link/index.ts +34 -0
- package/src/channels/link/server.ts +134 -0
- package/src/channels/oneshot.ts +90 -0
- package/src/channels/ops.ts +65 -0
- package/src/channels/session-ops.ts +81 -0
- package/src/conductor/bash-guard.ts +599 -0
- package/src/conductor/catalog/catalog.ts +116 -0
- package/src/conductor/catalog/index.ts +8 -0
- package/src/conductor/catalog/matcher.ts +134 -0
- package/src/conductor/conductor.ts +234 -0
- package/src/conductor/contract.ts +842 -0
- package/src/conductor/diagnostics.ts +227 -0
- package/src/conductor/index.ts +33 -0
- package/src/conductor/permissions.ts +588 -0
- package/src/conductor/quota-error.ts +49 -0
- package/src/conductor/signal-hub/hub.ts +46 -0
- package/src/conductor/signal-hub/index.ts +2 -0
- package/src/conductor/signal-hub/translate.test.ts +81 -0
- package/src/conductor/signal-hub/translate.ts +74 -0
- package/src/conductor/skill-parse/index.ts +2 -0
- package/src/conductor/skill-parse/parse.ts +108 -0
- package/src/conductor/transcript-store/index.ts +22 -0
- package/src/conductor/transcript-store/serialize.ts +116 -0
- package/src/conductor/transcript-store/store.ts +205 -0
- package/src/console/auth-status.ts +56 -0
- package/src/console/components/AgentsView.ts +165 -0
- package/src/console/components/BackgroundAgents.ts +155 -0
- package/src/console/components/Banner.ts +334 -0
- package/src/console/components/Composer.ts +94 -0
- package/src/console/components/StatusBar.ts +49 -0
- package/src/console/components/TerminalConsole.ts +1090 -0
- package/src/console/components/WorkingIndicator.ts +98 -0
- package/src/console/components/banner-sweep.ts +24 -0
- package/src/console/components/welcome.ts +74 -0
- package/src/console/contract.ts +630 -0
- package/src/console/index.ts +34 -0
- package/src/console/input/complete.ts +127 -0
- package/src/console/input/dir-reader.ts +34 -0
- package/src/console/input/index.ts +23 -0
- package/src/console/input/keymap.ts +159 -0
- package/src/console/input/paste.ts +104 -0
- package/src/console/mount.ts +56 -0
- package/src/console/overlays/approval-queue.ts +57 -0
- package/src/console/overlays/approval.ts +130 -0
- package/src/console/overlays/auth.ts +342 -0
- package/src/console/overlays/boards.ts +308 -0
- package/src/console/overlays/host.ts +36 -0
- package/src/console/overlays/index.ts +26 -0
- package/src/console/overlays/pickers.ts +258 -0
- package/src/console/overlays/sessions.ts +190 -0
- package/src/console/reducer.ts +182 -0
- package/src/console/slash/builtins.ts +81 -0
- package/src/console/slash/commands/dynamic.ts +83 -0
- package/src/console/slash/commands/integrations.ts +695 -0
- package/src/console/slash/commands/shared.ts +75 -0
- package/src/console/slash/commands/transcript.ts +263 -0
- package/src/console/slash/commands/workbench.ts +246 -0
- package/src/console/slash/index.ts +15 -0
- package/src/console/slash/registry.ts +70 -0
- package/src/console/slash/resolve.ts +63 -0
- package/src/console/startup.ts +209 -0
- package/src/console/theme/adapter.ts +45 -0
- package/src/console/theme/index.ts +7 -0
- package/src/console/theme/palette.ts +68 -0
- package/src/console/theme/resolve.ts +39 -0
- package/src/console/theme/tokens.ts +71 -0
- package/src/entry.ts +55 -0
- package/src/guardrails.ts +37 -0
- package/src/index.ts +18 -0
- package/src/insight/channel.ts +88 -0
- package/src/insight/contract.ts +185 -0
- package/src/insight/index.ts +110 -0
- package/src/insight/recorder.ts +213 -0
- package/src/insight/redaction.ts +157 -0
- package/src/insight/replay.ts +158 -0
- package/src/insight/sampling.ts +70 -0
- package/src/insight/serialize.ts +50 -0
- package/src/insight/sinks/console.ts +64 -0
- package/src/insight/sinks/file.ts +40 -0
- package/src/insight/sinks/index.ts +24 -0
- package/src/insight/sinks/stream.ts +54 -0
- package/src/integrations/sarvam/attach.ts +239 -0
- package/src/integrations/sarvam/config.ts +156 -0
- package/src/integrations/sarvam/index.ts +25 -0
- package/src/integrations/sarvam/sarvam.test.ts +60 -0
- package/src/integrations/sarvam/types.ts +27 -0
- package/src/integrations/zoho/attach.ts +342 -0
- package/src/integrations/zoho/config.ts +125 -0
- package/src/integrations/zoho/index.ts +27 -0
- package/src/integrations/zoho/types.ts +21 -0
- package/src/integrations/zoho/zoho.test.ts +50 -0
- package/src/kit/clipboard-image.ts +107 -0
- package/src/kit/external-editor.ts +48 -0
- package/src/kit/image.ts +59 -0
- package/src/kit/index.ts +51 -0
- package/src/kit/shell.ts +19 -0
- package/src/kit/tool-fetch.ts +85 -0
- package/src/launch/catalog.ts +148 -0
- package/src/launch/contract.ts +187 -0
- package/src/launch/credentials.ts +625 -0
- package/src/launch/index.ts +98 -0
- package/src/launch/invocation/attachments.ts +179 -0
- package/src/launch/invocation/flags.ts +196 -0
- package/src/launch/invocation/index.ts +25 -0
- package/src/launch/invocation/read.ts +260 -0
- package/src/launch/invocation/usage.ts +67 -0
- package/src/launch/login.ts +324 -0
- package/src/launch/oauth.test.ts +18 -0
- package/src/launch/oauth.ts +203 -0
- package/src/launch/packages.ts +194 -0
- package/src/launch/pickers.ts +189 -0
- package/src/runtime-bridge/bridges/_drive.ts +96 -0
- package/src/runtime-bridge/bridges/builtins.ts +68 -0
- package/src/runtime-bridge/bridges/claude-cli.ts +123 -0
- package/src/runtime-bridge/bridges/codex-cli.ts +142 -0
- package/src/runtime-bridge/bridges/index.ts +33 -0
- package/src/runtime-bridge/bridges/indusagi-cli.ts +155 -0
- package/src/runtime-bridge/broker.ts +227 -0
- package/src/runtime-bridge/contract.ts +122 -0
- package/src/runtime-bridge/index.ts +79 -0
- package/src/runtime-bridge/sink.ts +180 -0
- package/src/sessions/contract.ts +81 -0
- package/src/sessions/index.ts +13 -0
- package/src/sessions/library.ts +229 -0
- package/src/settings/contract.ts +114 -0
- package/src/settings/index.ts +32 -0
- package/src/settings/manager.ts +117 -0
- package/src/transcript-export/index.ts +45 -0
- package/src/transcript-export/publish.ts +260 -0
- package/src/transcript-export/sgr.ts +315 -0
- package/src/transcript-export/template.ts +272 -0
- package/src/transcript-export/theme-bridge.ts +150 -0
- package/src/window-budget/budget/estimate.ts +135 -0
- package/src/window-budget/budget/gate.ts +33 -0
- package/src/window-budget/budget/index.ts +16 -0
- package/src/window-budget/budget/slice.ts +56 -0
- package/src/window-budget/condenser.ts +58 -0
- package/src/window-budget/contract.ts +184 -0
- package/src/window-budget/index.ts +19 -0
- package/src/window-budget/microcompact.ts +95 -0
- package/src/window-budget/rehydrate.ts +136 -0
- package/src/window-budget/summarize/condense.ts +103 -0
- package/src/window-budget/summarize/index.ts +14 -0
- package/src/window-budget/summarize/prompt.ts +149 -0
- package/src/workflow-engine/agent-runner.ts +181 -0
- package/src/workflow-engine/display.ts +224 -0
- package/src/workflow-engine/engine.ts +294 -0
- package/src/workflow-engine/index.ts +23 -0
- package/src/workflow-engine/parse.ts +172 -0
- package/src/workflow-engine/structured-output.ts +35 -0
- package/src/workspace/brand.ts +29 -0
- package/src/workspace/index.ts +18 -0
- package/src/workspace/locator.ts +103 -0
- package/src/workspace/runtime-detect.ts +64 -0
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Template — the HTML page shell, inlined styles, and client renderer for the
|
|
3
|
+
* self-contained transcript export.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export const SHELL_SLOTS: Readonly<Record<string, string>> = Object.freeze({
|
|
7
|
+
themeVars: "THEME_VARS",
|
|
8
|
+
pageSurface: "PAGE_SURFACE",
|
|
9
|
+
cardSurface: "CARD_SURFACE",
|
|
10
|
+
noteSurface: "NOTE_SURFACE",
|
|
11
|
+
styles: "STYLES",
|
|
12
|
+
script: "SCRIPT",
|
|
13
|
+
payload: "PAYLOAD",
|
|
14
|
+
markedLib: "MARKED_LIB",
|
|
15
|
+
highlightLib: "HIGHLIGHT_LIB",
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const PAGE_STYLES = `
|
|
19
|
+
:root { color-scheme: dark light; }
|
|
20
|
+
* { box-sizing: border-box; }
|
|
21
|
+
html, body { margin: 0; padding: 0; }
|
|
22
|
+
body {
|
|
23
|
+
background: var(--x-page-surface);
|
|
24
|
+
color: var(--x-ink);
|
|
25
|
+
font: 15px/1.6 ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
|
|
26
|
+
-webkit-font-smoothing: antialiased;
|
|
27
|
+
}
|
|
28
|
+
.xscript {
|
|
29
|
+
max-width: 56rem;
|
|
30
|
+
margin: 0 auto;
|
|
31
|
+
padding: 2.5rem 1.25rem 6rem;
|
|
32
|
+
display: flex;
|
|
33
|
+
flex-direction: column;
|
|
34
|
+
gap: 1.25rem;
|
|
35
|
+
}
|
|
36
|
+
.xscript-head {
|
|
37
|
+
border-bottom: 1px solid var(--x-border);
|
|
38
|
+
padding-bottom: 1rem;
|
|
39
|
+
margin-bottom: 0.5rem;
|
|
40
|
+
}
|
|
41
|
+
.xscript-head h1 {
|
|
42
|
+
margin: 0;
|
|
43
|
+
font-size: 1.35rem;
|
|
44
|
+
font-weight: 650;
|
|
45
|
+
letter-spacing: -0.01em;
|
|
46
|
+
}
|
|
47
|
+
.xscript-head .xscript-meta {
|
|
48
|
+
margin-top: 0.35rem;
|
|
49
|
+
color: var(--x-ink-muted);
|
|
50
|
+
font-size: 0.85rem;
|
|
51
|
+
}
|
|
52
|
+
.turn {
|
|
53
|
+
background: var(--x-card-surface);
|
|
54
|
+
border: 1px solid var(--x-border);
|
|
55
|
+
border-radius: 0.65rem;
|
|
56
|
+
padding: 1rem 1.15rem;
|
|
57
|
+
overflow: hidden;
|
|
58
|
+
}
|
|
59
|
+
.turn[data-role="user"] { border-left: 3px solid var(--x-user-role); }
|
|
60
|
+
.turn[data-role="agent"] { border-left: 3px solid var(--x-agent-role); }
|
|
61
|
+
.turn[data-role="tool"] { border-left: 3px solid var(--x-tool-role); }
|
|
62
|
+
.turn-tag {
|
|
63
|
+
display: inline-block;
|
|
64
|
+
font-size: 0.72rem;
|
|
65
|
+
font-weight: 650;
|
|
66
|
+
text-transform: uppercase;
|
|
67
|
+
letter-spacing: 0.06em;
|
|
68
|
+
margin-bottom: 0.6rem;
|
|
69
|
+
color: var(--x-ink-muted);
|
|
70
|
+
}
|
|
71
|
+
.turn[data-role="user"] .turn-tag { color: var(--x-user-role); }
|
|
72
|
+
.turn[data-role="agent"] .turn-tag { color: var(--x-agent-role); }
|
|
73
|
+
.turn[data-role="tool"] .turn-tag { color: var(--x-tool-role); }
|
|
74
|
+
.turn-body { word-break: break-word; }
|
|
75
|
+
.turn-body > :first-child { margin-top: 0; }
|
|
76
|
+
.turn-body > :last-child { margin-bottom: 0; }
|
|
77
|
+
.turn-body a { color: var(--x-accent); text-decoration: none; }
|
|
78
|
+
.turn-body a:hover { text-decoration: underline; }
|
|
79
|
+
.turn-body p { margin: 0.6rem 0; }
|
|
80
|
+
.turn-body img { max-width: 100%; border-radius: 0.4rem; }
|
|
81
|
+
.turn-body code {
|
|
82
|
+
font-family: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, monospace;
|
|
83
|
+
font-size: 0.88em;
|
|
84
|
+
background: var(--x-note-surface);
|
|
85
|
+
padding: 0.1em 0.35em;
|
|
86
|
+
border-radius: 0.3rem;
|
|
87
|
+
}
|
|
88
|
+
.turn-body pre {
|
|
89
|
+
background: var(--x-note-surface);
|
|
90
|
+
border: 1px solid var(--x-border);
|
|
91
|
+
border-radius: 0.5rem;
|
|
92
|
+
padding: 0.9rem 1rem;
|
|
93
|
+
overflow-x: auto;
|
|
94
|
+
margin: 0.75rem 0;
|
|
95
|
+
}
|
|
96
|
+
.turn-body pre code { background: none; padding: 0; }
|
|
97
|
+
.tool-frame {
|
|
98
|
+
font-family: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, monospace;
|
|
99
|
+
font-size: 0.85rem;
|
|
100
|
+
background: var(--x-note-surface);
|
|
101
|
+
border: 1px solid var(--x-border);
|
|
102
|
+
border-radius: 0.5rem;
|
|
103
|
+
padding: 0.85rem 1rem;
|
|
104
|
+
margin: 0.5rem 0;
|
|
105
|
+
overflow-x: auto;
|
|
106
|
+
white-space: pre-wrap;
|
|
107
|
+
}
|
|
108
|
+
.aside-note {
|
|
109
|
+
background: var(--x-note-surface);
|
|
110
|
+
border: 1px dashed var(--x-border);
|
|
111
|
+
border-radius: 0.5rem;
|
|
112
|
+
padding: 0.75rem 1rem;
|
|
113
|
+
color: var(--x-ink-muted);
|
|
114
|
+
font-size: 0.9rem;
|
|
115
|
+
}
|
|
116
|
+
.sgr-bold { font-weight: 700; }
|
|
117
|
+
.sgr-dim { opacity: 0.72; }
|
|
118
|
+
.sgr-italic { font-style: italic; }
|
|
119
|
+
.sgr-underline { text-decoration: underline; }
|
|
120
|
+
.sgr-strike { text-decoration: line-through; }
|
|
121
|
+
.sgr-underline.sgr-strike { text-decoration: underline line-through; }
|
|
122
|
+
.sgr-blink { animation: x-blink 1.1s steps(2, start) infinite; }
|
|
123
|
+
.sgr-hidden { visibility: hidden; }
|
|
124
|
+
@keyframes x-blink { 50% { opacity: 0; } }
|
|
125
|
+
.xhl-keyword { color: var(--x-accent); }
|
|
126
|
+
.xhl-string { color: var(--x-user-role); }
|
|
127
|
+
.xhl-comment { color: var(--x-ink-muted); font-style: italic; }
|
|
128
|
+
.xhl-number, .xhl-literal { color: var(--x-tool-role); }
|
|
129
|
+
`.trim();
|
|
130
|
+
|
|
131
|
+
const CLIENT_SCRIPT = `
|
|
132
|
+
(function () {
|
|
133
|
+
"use strict";
|
|
134
|
+
var root = document.getElementById("xscript-root");
|
|
135
|
+
if (!root) return;
|
|
136
|
+
|
|
137
|
+
var raw;
|
|
138
|
+
try {
|
|
139
|
+
raw = JSON.parse(decodeURIComponent(escape(atob(window.__XSCRIPT_PAYLOAD__ || ""))));
|
|
140
|
+
} catch (err) {
|
|
141
|
+
root.textContent = "Unable to decode transcript payload.";
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function hl(code, lang) {
|
|
146
|
+
try {
|
|
147
|
+
if (lang && window.hljs && window.hljs.getLanguage(lang)) {
|
|
148
|
+
return window.hljs.highlight(code, { language: lang }).value;
|
|
149
|
+
}
|
|
150
|
+
if (window.hljs) return window.hljs.highlightAuto(code).value;
|
|
151
|
+
} catch (e) {}
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (window.marked && window.marked.setOptions) {
|
|
156
|
+
window.marked.setOptions({
|
|
157
|
+
gfm: true,
|
|
158
|
+
breaks: false,
|
|
159
|
+
highlight: function (code, lang) {
|
|
160
|
+
var out = hl(code, lang);
|
|
161
|
+
return out == null ? code : out;
|
|
162
|
+
},
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function md(text) {
|
|
167
|
+
try {
|
|
168
|
+
if (window.marked && window.marked.parse) {
|
|
169
|
+
return window.marked.parse(text, { async: false });
|
|
170
|
+
}
|
|
171
|
+
} catch (e) {}
|
|
172
|
+
var div = document.createElement("div");
|
|
173
|
+
div.textContent = text;
|
|
174
|
+
return div.innerHTML;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function el(tag, cls) {
|
|
178
|
+
var node = document.createElement(tag);
|
|
179
|
+
if (cls) node.className = cls;
|
|
180
|
+
return node;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function renderTurn(turn) {
|
|
184
|
+
if (turn.kind === "note") {
|
|
185
|
+
var note = el("div", "aside-note");
|
|
186
|
+
note.innerHTML = turn.html != null ? turn.html : md(turn.text || "");
|
|
187
|
+
return note;
|
|
188
|
+
}
|
|
189
|
+
var card = el("section", "turn");
|
|
190
|
+
card.setAttribute("data-role", turn.role || "agent");
|
|
191
|
+
var tag = el("span", "turn-tag");
|
|
192
|
+
tag.textContent = turn.label || turn.role || "";
|
|
193
|
+
card.appendChild(tag);
|
|
194
|
+
var body = el("div", "turn-body");
|
|
195
|
+
if (turn.html != null) {
|
|
196
|
+
body.innerHTML = turn.html;
|
|
197
|
+
} else if (turn.frame) {
|
|
198
|
+
var frame = el("div", "tool-frame");
|
|
199
|
+
frame.innerHTML = turn.frame;
|
|
200
|
+
body.appendChild(frame);
|
|
201
|
+
} else {
|
|
202
|
+
body.innerHTML = md(turn.text || "");
|
|
203
|
+
}
|
|
204
|
+
card.appendChild(body);
|
|
205
|
+
return card;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
var turns = Array.isArray(raw.turns) ? raw.turns : [];
|
|
209
|
+
for (var i = 0; i < turns.length; i++) {
|
|
210
|
+
root.appendChild(renderTurn(turns[i]));
|
|
211
|
+
}
|
|
212
|
+
})();
|
|
213
|
+
`.trim();
|
|
214
|
+
|
|
215
|
+
const PAGE_SHELL = `<!doctype html>
|
|
216
|
+
<html lang="en">
|
|
217
|
+
<head>
|
|
218
|
+
<meta charset="utf-8" />
|
|
219
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
220
|
+
<title>Session Transcript</title>
|
|
221
|
+
<style>
|
|
222
|
+
:root {
|
|
223
|
+
{{THEME_VARS}}
|
|
224
|
+
--x-page-surface: {{PAGE_SURFACE}};
|
|
225
|
+
--x-card-surface: {{CARD_SURFACE}};
|
|
226
|
+
--x-note-surface: {{NOTE_SURFACE}};
|
|
227
|
+
}
|
|
228
|
+
{{STYLES}}
|
|
229
|
+
</style>
|
|
230
|
+
</head>
|
|
231
|
+
<body>
|
|
232
|
+
<main class="xscript">
|
|
233
|
+
<header class="xscript-head">
|
|
234
|
+
<h1>Session Transcript</h1>
|
|
235
|
+
<div class="xscript-meta">Self-contained HTML export</div>
|
|
236
|
+
</header>
|
|
237
|
+
<div id="xscript-root"></div>
|
|
238
|
+
</main>
|
|
239
|
+
<script>
|
|
240
|
+
/* {{MARKED_LIB}} */
|
|
241
|
+
</script>
|
|
242
|
+
<script>
|
|
243
|
+
/* {{HIGHLIGHT_LIB}} */
|
|
244
|
+
</script>
|
|
245
|
+
<script>
|
|
246
|
+
window.__XSCRIPT_PAYLOAD__ = "{{PAYLOAD}}";
|
|
247
|
+
</script>
|
|
248
|
+
<script>
|
|
249
|
+
{{SCRIPT}}
|
|
250
|
+
</script>
|
|
251
|
+
</body>
|
|
252
|
+
</html>
|
|
253
|
+
`;
|
|
254
|
+
|
|
255
|
+
export { PAGE_STYLES, CLIENT_SCRIPT, PAGE_SHELL };
|
|
256
|
+
|
|
257
|
+
function escapeRegExp(text: string): string {
|
|
258
|
+
return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export function fill(
|
|
262
|
+
template: string,
|
|
263
|
+
values: Readonly<Record<string, string>>,
|
|
264
|
+
): string {
|
|
265
|
+
let out = template;
|
|
266
|
+
for (const token of Object.values(SHELL_SLOTS)) {
|
|
267
|
+
const value = values[token] ?? "";
|
|
268
|
+
const marker = new RegExp(`\\{\\{${escapeRegExp(token)}\\}\\}`, "g");
|
|
269
|
+
out = out.replace(marker, () => value);
|
|
270
|
+
}
|
|
271
|
+
return out;
|
|
272
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ThemeBridge — the color service behind the HTML transcript export.
|
|
3
|
+
*/
|
|
4
|
+
import { FALLBACK_EXPORT_THEME, type ExportTheme } from "../briefing/contract.js";
|
|
5
|
+
|
|
6
|
+
export function buildLuminanceLut(): Float64Array {
|
|
7
|
+
const lut = new Float64Array(256);
|
|
8
|
+
for (let v = 0; v < 256; v++) {
|
|
9
|
+
const s = v / 255;
|
|
10
|
+
lut[v] = s <= 0.03928 ? s / 12.92 : Math.pow((s + 0.055) / 1.055, 2.4);
|
|
11
|
+
}
|
|
12
|
+
return lut;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface Rgb {
|
|
16
|
+
r: number;
|
|
17
|
+
g: number;
|
|
18
|
+
b: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function expandShortHex(body: string): string {
|
|
22
|
+
if (body.length === 3 || body.length === 4) {
|
|
23
|
+
return body
|
|
24
|
+
.slice(0, 3)
|
|
25
|
+
.split("")
|
|
26
|
+
.map((c) => c + c)
|
|
27
|
+
.join("");
|
|
28
|
+
}
|
|
29
|
+
return body.slice(0, 6);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function parseColor(color: string): Rgb | null {
|
|
33
|
+
const text = color.trim().toLowerCase();
|
|
34
|
+
if (text.startsWith("#")) {
|
|
35
|
+
const body = expandShortHex(text.slice(1));
|
|
36
|
+
if (!/^[0-9a-f]{6}$/.test(body)) return null;
|
|
37
|
+
return {
|
|
38
|
+
r: parseInt(body.slice(0, 2), 16),
|
|
39
|
+
g: parseInt(body.slice(2, 4), 16),
|
|
40
|
+
b: parseInt(body.slice(4, 6), 16),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
const fn = text.match(/^rgba?\(([^)]+)\)$/);
|
|
44
|
+
if (fn) {
|
|
45
|
+
const parts = fn[1].split(/[,\s/]+/).filter((p) => p.length > 0);
|
|
46
|
+
if (parts.length < 3) return null;
|
|
47
|
+
const channel = (p: string): number => {
|
|
48
|
+
const n = p.endsWith("%") ? (parseFloat(p) / 100) * 255 : parseFloat(p);
|
|
49
|
+
return Number.isNaN(n) ? 0 : clampByte2(n);
|
|
50
|
+
};
|
|
51
|
+
return { r: channel(parts[0]), g: channel(parts[1]), b: channel(parts[2]) };
|
|
52
|
+
}
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function clampByte2(value: number): number {
|
|
57
|
+
if (value <= 0) return 0;
|
|
58
|
+
if (value >= 255) return 255;
|
|
59
|
+
return Math.round(value);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function formatColor(rgb: Rgb): string {
|
|
63
|
+
const hex = (v: number): string => clampByte2(v).toString(16).padStart(2, "0");
|
|
64
|
+
return `#${hex(rgb.r)}${hex(rgb.g)}${hex(rgb.b)}`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function luminanceOf(rgb: Rgb, lut: Float64Array): number {
|
|
68
|
+
const lr = lut[clampByte2(rgb.r)] ?? 0;
|
|
69
|
+
const lg = lut[clampByte2(rgb.g)] ?? 0;
|
|
70
|
+
const lb = lut[clampByte2(rgb.b)] ?? 0;
|
|
71
|
+
return 0.2126 * lr + 0.7152 * lg + 0.0722 * lb;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function contrastRatio(a: number, b: number): number {
|
|
75
|
+
const hi = Math.max(a, b);
|
|
76
|
+
const lo = Math.min(a, b);
|
|
77
|
+
return (hi + 0.05) / (lo + 0.05);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function stepChannels(rgb: Rgb, amount: number): Rgb {
|
|
81
|
+
return {
|
|
82
|
+
r: clampByte2(rgb.r + amount),
|
|
83
|
+
g: clampByte2(rgb.g + amount),
|
|
84
|
+
b: clampByte2(rgb.b + amount),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const LUMINANCE_LUT: Float64Array = buildLuminanceLut();
|
|
89
|
+
const MODE_THRESHOLD = 0.18;
|
|
90
|
+
const THEME_TOKENS = [
|
|
91
|
+
"pageSurface",
|
|
92
|
+
"cardSurface",
|
|
93
|
+
"noteSurface",
|
|
94
|
+
"ink",
|
|
95
|
+
"inkMuted",
|
|
96
|
+
"accent",
|
|
97
|
+
"border",
|
|
98
|
+
"userRole",
|
|
99
|
+
"agentRole",
|
|
100
|
+
"toolRole",
|
|
101
|
+
] as const;
|
|
102
|
+
|
|
103
|
+
export class DefaultThemeBridge {
|
|
104
|
+
readonly theme: ExportTheme;
|
|
105
|
+
readonly lut: Float64Array;
|
|
106
|
+
|
|
107
|
+
constructor(
|
|
108
|
+
theme: ExportTheme = FALLBACK_EXPORT_THEME,
|
|
109
|
+
lut: Float64Array = LUMINANCE_LUT,
|
|
110
|
+
) {
|
|
111
|
+
this.theme = theme;
|
|
112
|
+
this.lut = lut;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
luminance(color: string): number {
|
|
116
|
+
const rgb = parseColor(color);
|
|
117
|
+
return rgb === null ? 0 : luminanceOf(rgb, this.lut);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
mode(): "light" | "dark" {
|
|
121
|
+
return this.luminance(this.theme.pageSurface) >= MODE_THRESHOLD ? "light" : "dark";
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
deriveSurface(base: string, amount: number): string {
|
|
125
|
+
const rgb = parseColor(base);
|
|
126
|
+
if (rgb === null) return this.theme.cardSurface;
|
|
127
|
+
return formatColor(stepChannels(rgb, amount));
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
readableInk(background: string): string {
|
|
131
|
+
const bg = this.luminance(background);
|
|
132
|
+
const ink = this.luminance(this.theme.ink);
|
|
133
|
+
const muted = this.luminance(this.theme.inkMuted);
|
|
134
|
+
return contrastRatio(ink, bg) >= contrastRatio(muted, bg)
|
|
135
|
+
? this.theme.ink
|
|
136
|
+
: this.theme.inkMuted;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
toCssVars(): Record<string, string> {
|
|
140
|
+
const vars: Record<string, string> = {};
|
|
141
|
+
for (const token of THEME_TOKENS) {
|
|
142
|
+
vars[token] = this.theme[token];
|
|
143
|
+
}
|
|
144
|
+
return vars;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function createThemeBridge(theme?: ExportTheme): DefaultThemeBridge {
|
|
149
|
+
return new DefaultThemeBridge(theme ?? FALLBACK_EXPORT_THEME);
|
|
150
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token estimation — the heuristic "meter stick" for the window-budget
|
|
3
|
+
* subsystem.
|
|
4
|
+
*
|
|
5
|
+
* Before we can decide whether the transcript is over budget (gate.ts) or where
|
|
6
|
+
* to slice it (slice.ts), we need a cheap, deterministic estimate of how many
|
|
7
|
+
* tokens each {@link AgentMessage} occupies. We do *not* call a tokenizer; we
|
|
8
|
+
* walk the serialized content and apply a small, transparent weighting model.
|
|
9
|
+
*/
|
|
10
|
+
import type { AgentMessage } from "../contract.js";
|
|
11
|
+
|
|
12
|
+
const CHARS_PER_TOKEN = 3.6;
|
|
13
|
+
const IMAGE_TOKENS = 1024;
|
|
14
|
+
const MESSAGE_FRAMING_TOKENS = 4;
|
|
15
|
+
const BLOCK_FRAMING_TOKENS = 2;
|
|
16
|
+
const TOOLCALL_ENVELOPE_TOKENS = 6;
|
|
17
|
+
|
|
18
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
19
|
+
return typeof value === "object" && value !== null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function safeStringify(value: unknown): string {
|
|
23
|
+
try {
|
|
24
|
+
return JSON.stringify(value) ?? "";
|
|
25
|
+
} catch {
|
|
26
|
+
return "";
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function weighBlock(block: unknown): { chars: number; images: number } {
|
|
31
|
+
if (typeof block === "string") return { chars: block.length, images: 0 };
|
|
32
|
+
if (!isRecord(block)) return { chars: 0, images: 0 };
|
|
33
|
+
switch (block.type) {
|
|
34
|
+
case "text":
|
|
35
|
+
return { chars: typeof block.text === "string" ? block.text.length : 0, images: 0 };
|
|
36
|
+
case "thinking":
|
|
37
|
+
return { chars: typeof block.thinking === "string" ? block.thinking.length : 0, images: 0 };
|
|
38
|
+
case "image":
|
|
39
|
+
return { chars: 0, images: 1 };
|
|
40
|
+
case "toolCall": {
|
|
41
|
+
const name = typeof block.name === "string" ? block.name.length : 0;
|
|
42
|
+
const args = block.arguments === undefined ? 0 : safeStringify(block.arguments).length;
|
|
43
|
+
return { chars: name + args, images: 0 };
|
|
44
|
+
}
|
|
45
|
+
default:
|
|
46
|
+
return { chars: safeStringify(block).length, images: 0 };
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function weighMessage(message: unknown): { chars: number; images: number; framing: number } {
|
|
51
|
+
if (!isRecord(message)) {
|
|
52
|
+
return { chars: safeStringify(message).length, images: 0, framing: MESSAGE_FRAMING_TOKENS };
|
|
53
|
+
}
|
|
54
|
+
let chars = 0;
|
|
55
|
+
let images = 0;
|
|
56
|
+
let framing = MESSAGE_FRAMING_TOKENS;
|
|
57
|
+
switch (message.role) {
|
|
58
|
+
case "user": {
|
|
59
|
+
const content = message.content;
|
|
60
|
+
if (typeof content === "string") {
|
|
61
|
+
chars += content.length;
|
|
62
|
+
framing += BLOCK_FRAMING_TOKENS;
|
|
63
|
+
} else if (Array.isArray(content)) {
|
|
64
|
+
for (const block of content) {
|
|
65
|
+
const w = weighBlock(block);
|
|
66
|
+
chars += w.chars;
|
|
67
|
+
images += w.images;
|
|
68
|
+
framing += BLOCK_FRAMING_TOKENS;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return { chars, images, framing };
|
|
72
|
+
}
|
|
73
|
+
case "assistant": {
|
|
74
|
+
const content = Array.isArray(message.content) ? message.content : [];
|
|
75
|
+
for (const block of content) {
|
|
76
|
+
const w = weighBlock(block);
|
|
77
|
+
chars += w.chars;
|
|
78
|
+
images += w.images;
|
|
79
|
+
framing += BLOCK_FRAMING_TOKENS;
|
|
80
|
+
if (isRecord(block) && block.type === "toolCall") framing += TOOLCALL_ENVELOPE_TOKENS;
|
|
81
|
+
}
|
|
82
|
+
if (typeof message.errorMessage === "string") chars += message.errorMessage.length;
|
|
83
|
+
return { chars, images, framing };
|
|
84
|
+
}
|
|
85
|
+
case "toolResult": {
|
|
86
|
+
if (typeof message.toolName === "string") chars += message.toolName.length;
|
|
87
|
+
const content = Array.isArray(message.content) ? message.content : [];
|
|
88
|
+
for (const block of content) {
|
|
89
|
+
const w = weighBlock(block);
|
|
90
|
+
chars += w.chars;
|
|
91
|
+
images += w.images;
|
|
92
|
+
framing += BLOCK_FRAMING_TOKENS;
|
|
93
|
+
}
|
|
94
|
+
framing += TOOLCALL_ENVELOPE_TOKENS;
|
|
95
|
+
return { chars, images, framing };
|
|
96
|
+
}
|
|
97
|
+
default: {
|
|
98
|
+
chars += safeStringify(message).length;
|
|
99
|
+
framing += BLOCK_FRAMING_TOKENS;
|
|
100
|
+
return { chars, images, framing };
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Estimate the token cost of one message in isolation. Exposed so the slice
|
|
107
|
+
* planner can build a prefix-sum without re-deriving the per-message weight.
|
|
108
|
+
*/
|
|
109
|
+
export function estimateMessageTokens(message: AgentMessage): number {
|
|
110
|
+
const { chars, images, framing } = weighMessage(message);
|
|
111
|
+
const textTokens = Math.ceil(chars / CHARS_PER_TOKEN);
|
|
112
|
+
return textTokens + framing + images * IMAGE_TOKENS;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Estimate the total token cost of a transcript: the sum of every message's
|
|
117
|
+
* {@link estimateMessageTokens}.
|
|
118
|
+
*/
|
|
119
|
+
export function estimateTokens(messages: AgentMessage[]): number {
|
|
120
|
+
let total = 0;
|
|
121
|
+
for (const message of messages) total += estimateMessageTokens(message);
|
|
122
|
+
return total;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Build a FORWARD prefix-sum array of per-message token estimates.
|
|
127
|
+
*/
|
|
128
|
+
export function prefixTokens(messages: AgentMessage[]): number[] {
|
|
129
|
+
const prefix = new Array<number>(messages.length + 1);
|
|
130
|
+
prefix[0] = 0;
|
|
131
|
+
for (let i = 0; i < messages.length; i++) {
|
|
132
|
+
prefix[i + 1] = prefix[i] + estimateMessageTokens(messages[i]);
|
|
133
|
+
}
|
|
134
|
+
return prefix;
|
|
135
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Budget gate — the trigger predicate for the window-budget subsystem.
|
|
3
|
+
*
|
|
4
|
+
* {@link isOverBudget} answers the single question "is the transcript large
|
|
5
|
+
* enough that we should condense before the next turn?" by comparing a token
|
|
6
|
+
* estimate against a window-relative threshold derived from the model's
|
|
7
|
+
* `contextWindow` and the caller-supplied {@link BudgetPolicy}.
|
|
8
|
+
*/
|
|
9
|
+
import type { AgentMessage, BudgetPolicy, Model } from "../contract.js";
|
|
10
|
+
import type { Api } from "indusagi/ai";
|
|
11
|
+
import { estimateTokens } from "./estimate.js";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Compute the absolute token limit at which condensing should fire for a given
|
|
15
|
+
* model and policy.
|
|
16
|
+
*/
|
|
17
|
+
export function budgetLimit(model: Model<Api>, policy: BudgetPolicy): number {
|
|
18
|
+
const reserve = policy.reserveTokens ?? 0;
|
|
19
|
+
const usable = Math.max(0, model.contextWindow - reserve);
|
|
20
|
+
return usable * policy.triggerRatio;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Decide whether the transcript is over budget for the given model and policy.
|
|
25
|
+
*/
|
|
26
|
+
export function isOverBudget(
|
|
27
|
+
messages: AgentMessage[],
|
|
28
|
+
model: Model<Api>,
|
|
29
|
+
policy: BudgetPolicy,
|
|
30
|
+
): boolean {
|
|
31
|
+
const estimate = estimateTokens(messages);
|
|
32
|
+
return estimate > budgetLimit(model, policy);
|
|
33
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
// Type declarations recovered from dist/types (no runtime body)
|
|
3
|
+
/**
|
|
4
|
+
* Window-budget / budget — pure budget math.
|
|
5
|
+
*
|
|
6
|
+
* The arithmetic core of the window-budget subsystem, free of I/O, prompts, and
|
|
7
|
+
* model calls:
|
|
8
|
+
* - {@link estimateTokens} / {@link estimateMessageTokens} / {@link prefixTokens}
|
|
9
|
+
* — heuristic token measurement and the forward cumulative-token array.
|
|
10
|
+
* - {@link isOverBudget} / {@link budgetLimit} — the window-relative trigger.
|
|
11
|
+
* - {@link planSlice} — forward prefix-sum + binary-search cut planning that
|
|
12
|
+
* never splits a tool_call from its tool_result.
|
|
13
|
+
*/
|
|
14
|
+
export { estimateMessageTokens, estimateTokens, prefixTokens } from "./estimate.js";
|
|
15
|
+
export { budgetLimit, isOverBudget } from "./gate.js";
|
|
16
|
+
export { planSlice } from "./slice.js";
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slice planner — decides *where* to cut the transcript when it is over budget.
|
|
3
|
+
*/
|
|
4
|
+
import type { AgentMessage, BudgetPolicy, CondensePlan } from "../contract.js";
|
|
5
|
+
import { prefixTokens } from "./estimate.js";
|
|
6
|
+
|
|
7
|
+
function isRecord2(value: unknown): value is Record<string, unknown> {
|
|
8
|
+
return typeof value === "object" && value !== null;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function isLegalTailStart(message: AgentMessage): boolean {
|
|
12
|
+
return !(isRecord2(message) && (message as { role?: string }).role === "toolResult");
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function snapForward(messages: AgentMessage[], candidate: number): number {
|
|
16
|
+
let cut = candidate;
|
|
17
|
+
while (cut < messages.length && !isLegalTailStart(messages[cut])) cut++;
|
|
18
|
+
return cut;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function lowerBound(prefix: number[], threshold: number): number {
|
|
22
|
+
let lo = 0;
|
|
23
|
+
let hi = prefix.length - 1;
|
|
24
|
+
while (lo < hi) {
|
|
25
|
+
const mid = (lo + hi) >>> 1;
|
|
26
|
+
if (prefix[mid] >= threshold) hi = mid;
|
|
27
|
+
else lo = mid + 1;
|
|
28
|
+
}
|
|
29
|
+
return lo;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Plan the condense slice for a transcript under a {@link BudgetPolicy}.
|
|
34
|
+
*/
|
|
35
|
+
export function planSlice(messages: AgentMessage[], policy: BudgetPolicy): CondensePlan {
|
|
36
|
+
const n = messages.length;
|
|
37
|
+
if (n === 0) {
|
|
38
|
+
return { cut: 0, kept: [], dropped: [] };
|
|
39
|
+
}
|
|
40
|
+
const prefix = prefixTokens(messages);
|
|
41
|
+
const total = prefix[n];
|
|
42
|
+
if (total <= policy.keepRecent) {
|
|
43
|
+
return { cut: 0, kept: messages.slice(0), dropped: [] };
|
|
44
|
+
}
|
|
45
|
+
const threshold = total - policy.keepRecent;
|
|
46
|
+
const ideal = lowerBound(prefix, threshold);
|
|
47
|
+
const cut = snapForward(messages, ideal);
|
|
48
|
+
if (cut >= n) {
|
|
49
|
+
return { cut: 0, kept: messages.slice(0), dropped: [] };
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
cut,
|
|
53
|
+
kept: messages.slice(cut),
|
|
54
|
+
dropped: messages.slice(0, cut),
|
|
55
|
+
};
|
|
56
|
+
}
|