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,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Overlays subsystem — public barrel.
|
|
3
|
+
*/
|
|
4
|
+
export { OverlayHost, type OverlayHostProps } from "./host.js";
|
|
5
|
+
export { PickerOverlays, type OverlayGroupProps } from "./pickers.js";
|
|
6
|
+
export { SessionOverlays } from "./sessions.js";
|
|
7
|
+
export { AuthOverlays } from "./auth.js";
|
|
8
|
+
export {
|
|
9
|
+
ApprovalOverlays,
|
|
10
|
+
choiceFromId,
|
|
11
|
+
readApprovalPayload,
|
|
12
|
+
summarizeInput,
|
|
13
|
+
APPROVAL_CHOICES,
|
|
14
|
+
DISMISS_CHOICE,
|
|
15
|
+
type ApprovalRequest,
|
|
16
|
+
type ApprovalPayload
|
|
17
|
+
} from "./approval.js";
|
|
18
|
+
export {
|
|
19
|
+
approvalQueueReducer,
|
|
20
|
+
activeApproval,
|
|
21
|
+
modeReleasesApprovals,
|
|
22
|
+
EMPTY_APPROVAL_QUEUE,
|
|
23
|
+
type ApprovalEntry,
|
|
24
|
+
type ApprovalQueueState,
|
|
25
|
+
type ApprovalQueueEvent
|
|
26
|
+
} from "./approval-queue.js";
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Picker overlays — the selection dialogs raised over the transcript.
|
|
3
|
+
*/
|
|
4
|
+
import { useEffect, useState } from "indusagi/react-host";
|
|
5
|
+
import { ScopedModelsDialog, ThemeDialog, type Model } from "indusagi/react-ink";
|
|
6
|
+
import { jsx } from "indusagi/react-host/jsx-runtime";
|
|
7
|
+
import type { ConsoleDispatch, InkThemeAdapter, ModalState, OverlayServices } from "../contract.js";
|
|
8
|
+
import { DEFAULT_SCHEME, isThemeScheme } from "../contract.js";
|
|
9
|
+
import { useAuthenticatedProviders } from "../auth-status.js";
|
|
10
|
+
import { isDeliveryMode, DELIVERY_MODES } from "../../settings/index.js";
|
|
11
|
+
import { THEME_SCHEMES } from "../theme/index.js";
|
|
12
|
+
import { ModelBoard, SettingsBoard } from "./boards.js";
|
|
13
|
+
|
|
14
|
+
const TOGGLE_VALUES = ["on", "off"];
|
|
15
|
+
|
|
16
|
+
function refToModel(ref: any): Model<any> {
|
|
17
|
+
return {
|
|
18
|
+
id: ref.id,
|
|
19
|
+
name: ref.name,
|
|
20
|
+
api: "",
|
|
21
|
+
provider: ref.provider,
|
|
22
|
+
baseUrl: "",
|
|
23
|
+
reasoning: ref.reasoning,
|
|
24
|
+
input: ["text"],
|
|
25
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
26
|
+
contextWindow: 0,
|
|
27
|
+
maxTokens: 0
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function listModels(services: OverlayServices): Model<any>[] {
|
|
32
|
+
try {
|
|
33
|
+
return services.conductor.availableModels().map(refToModel);
|
|
34
|
+
} catch {
|
|
35
|
+
return [];
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function ModelPicker(props: OverlayGroupProps) {
|
|
40
|
+
const { services, theme, closeModal } = props;
|
|
41
|
+
const authed = useAuthenticatedProviders(services);
|
|
42
|
+
if (!services) return null;
|
|
43
|
+
const all = listModels(services);
|
|
44
|
+
const models = authed === null ? all : all.filter((model) => authed.has(model.provider));
|
|
45
|
+
return jsx(ModelBoard, {
|
|
46
|
+
theme,
|
|
47
|
+
models,
|
|
48
|
+
onSelect: (model: Model<any>) => {
|
|
49
|
+
try {
|
|
50
|
+
services.conductor.selectModel(model.id);
|
|
51
|
+
} catch {
|
|
52
|
+
// ignore
|
|
53
|
+
}
|
|
54
|
+
closeModal();
|
|
55
|
+
},
|
|
56
|
+
onClose: closeModal
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function readScopedPayload(payload: unknown): { focus?: string; intent?: string } {
|
|
61
|
+
if (payload === null || typeof payload !== "object") return {};
|
|
62
|
+
const record = payload as any;
|
|
63
|
+
const out: any = {};
|
|
64
|
+
if (record.focus === "summary") out.focus = "summary";
|
|
65
|
+
if (record.intent === "reset") out.intent = "reset";
|
|
66
|
+
return out;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function ScopedModelPicker(props: OverlayGroupProps) {
|
|
70
|
+
const { services, dispatch, closeModal, modal } = props;
|
|
71
|
+
const payload = readScopedPayload(modal.payload);
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
if (!services || payload.intent !== "reset") return;
|
|
74
|
+
try {
|
|
75
|
+
services.settings.set("enabledModels", []);
|
|
76
|
+
services.settings.save();
|
|
77
|
+
dispatch({
|
|
78
|
+
type: "status:set",
|
|
79
|
+
status: { kind: "info", text: "Per-scope model overrides cleared." }
|
|
80
|
+
});
|
|
81
|
+
} catch {
|
|
82
|
+
dispatch({
|
|
83
|
+
type: "status:set",
|
|
84
|
+
status: { kind: "error", text: "Could not clear per-scope model overrides." }
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
closeModal();
|
|
88
|
+
}, []);
|
|
89
|
+
if (!services) return null;
|
|
90
|
+
if (payload.intent === "reset") return null;
|
|
91
|
+
const models = listModels(services);
|
|
92
|
+
let enabled: string[] = [];
|
|
93
|
+
try {
|
|
94
|
+
enabled = services.settings.get("enabledModels");
|
|
95
|
+
} catch {
|
|
96
|
+
enabled = [];
|
|
97
|
+
}
|
|
98
|
+
const selectedIds = enabled.length > 0 ? enabled : models.map((model) => model.id);
|
|
99
|
+
return jsx(ScopedModelsDialog, {
|
|
100
|
+
models,
|
|
101
|
+
selectedIds,
|
|
102
|
+
onSave: (ids: string[]) => {
|
|
103
|
+
try {
|
|
104
|
+
const all = models.map((model) => model.id);
|
|
105
|
+
const next = ids.length === all.length ? [] : ids;
|
|
106
|
+
services.settings.set("enabledModels", next);
|
|
107
|
+
services.settings.save();
|
|
108
|
+
} catch {
|
|
109
|
+
// ignore
|
|
110
|
+
}
|
|
111
|
+
closeModal();
|
|
112
|
+
},
|
|
113
|
+
onClose: closeModal
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function currentScheme(services: OverlayServices): string {
|
|
118
|
+
try {
|
|
119
|
+
const stored = services.settings.get("colourScheme");
|
|
120
|
+
if (typeof stored === "string" && isThemeScheme(stored)) return stored;
|
|
121
|
+
} catch {
|
|
122
|
+
// ignore
|
|
123
|
+
}
|
|
124
|
+
return DEFAULT_SCHEME;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function ThemePicker(props: OverlayGroupProps) {
|
|
128
|
+
const { services, dispatch, closeModal } = props;
|
|
129
|
+
const [originalScheme] = useState(() => (services ? currentScheme(services) : null));
|
|
130
|
+
if (!services) return null;
|
|
131
|
+
const revert = () => {
|
|
132
|
+
if (originalScheme) dispatch({ type: "scheme:set", scheme: originalScheme });
|
|
133
|
+
closeModal();
|
|
134
|
+
};
|
|
135
|
+
return jsx(ThemeDialog, {
|
|
136
|
+
themes: [...THEME_SCHEMES],
|
|
137
|
+
onSelect: (name: string) => {
|
|
138
|
+
if (isThemeScheme(name)) {
|
|
139
|
+
dispatch({ type: "scheme:set", scheme: name });
|
|
140
|
+
try {
|
|
141
|
+
services.settings.set("colourScheme", name);
|
|
142
|
+
services.settings.save();
|
|
143
|
+
} catch {
|
|
144
|
+
// ignore
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
closeModal();
|
|
148
|
+
},
|
|
149
|
+
onClose: revert
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function SettingsPicker(props: OverlayGroupProps) {
|
|
154
|
+
const { services, theme, dispatch, closeModal } = props;
|
|
155
|
+
if (!services) return null;
|
|
156
|
+
const settings = services.settings;
|
|
157
|
+
const [, setVersion] = useState(0);
|
|
158
|
+
const bump = () => setVersion((n) => n + 1);
|
|
159
|
+
const onOff = (flag: boolean) => (flag ? "on" : "off");
|
|
160
|
+
const toggleRow = (id: string, key: string, label: string, description: string, live?: () => void) => ({
|
|
161
|
+
id,
|
|
162
|
+
label,
|
|
163
|
+
description,
|
|
164
|
+
value: onOff(settings.get(key) as boolean),
|
|
165
|
+
values: [...TOGGLE_VALUES],
|
|
166
|
+
onChange: (value: string) => {
|
|
167
|
+
settings.set(key, value === "on");
|
|
168
|
+
settings.save();
|
|
169
|
+
live?.();
|
|
170
|
+
bump();
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
const deliveryRow = (id: string, key: string, label: string, description: string) => ({
|
|
174
|
+
id,
|
|
175
|
+
label,
|
|
176
|
+
description,
|
|
177
|
+
value: settings.get(key) as string,
|
|
178
|
+
values: [...DELIVERY_MODES],
|
|
179
|
+
onChange: (value: string) => {
|
|
180
|
+
if (isDeliveryMode(value)) {
|
|
181
|
+
settings.set(key, value);
|
|
182
|
+
settings.save();
|
|
183
|
+
}
|
|
184
|
+
bump();
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
const items: any[] = [];
|
|
188
|
+
try {
|
|
189
|
+
items.push({
|
|
190
|
+
id: "colour-scheme",
|
|
191
|
+
label: "Colour scheme",
|
|
192
|
+
description: "The terminal colour scheme the console renders in.",
|
|
193
|
+
value: settings.get("colourScheme") as string,
|
|
194
|
+
values: [...THEME_SCHEMES],
|
|
195
|
+
onChange: (value: string) => {
|
|
196
|
+
settings.set("colourScheme", value);
|
|
197
|
+
settings.save();
|
|
198
|
+
if (isThemeScheme(value)) dispatch({ type: "scheme:set", scheme: value });
|
|
199
|
+
bump();
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
items.push(toggleRow("show-images", "showImages", "Show images", "Render inline image content in the transcript.", () => dispatch({ type: "toggle:images" })));
|
|
203
|
+
items.push(toggleRow("image-auto-resize", "imageAutoResize", "Auto-resize images", "Shrink oversized images to fit before handing them to a provider."));
|
|
204
|
+
items.push(toggleRow("block-images", "blockImages", "Block images", "Withhold image content from providers entirely."));
|
|
205
|
+
items.push(toggleRow("show-reasoning", "showReasoning", "Show reasoning", "Stream the model's reasoning / thinking text as it arrives.", () => dispatch({ type: "toggle:reasoning" })));
|
|
206
|
+
items.push(toggleRow("skill-commands", "enableSkillCommands", "Skill commands", "Surface discovered skills as their own slash commands."));
|
|
207
|
+
items.push(deliveryRow("steering-mode", "steeringMode", "Steering mode", "Whether queued steering corrections release one at a time or all at once."));
|
|
208
|
+
items.push(deliveryRow("follow-up-mode", "followUpMode", "Follow-up mode", "Whether queued follow-up prompts release one at a time or all at once."));
|
|
209
|
+
items.push(toggleRow("auto-compact", "autoCompact", "Auto-compact", "Condense the transcript automatically as it nears the window."));
|
|
210
|
+
items.push(toggleRow("collapse-changelog", "collapseChangelog", "Collapse changelog", "Prefer a condensed changelog after the console updates itself."));
|
|
211
|
+
items.push(toggleRow("quiet-startup", "quietStartup", "Quiet startup", "Suppress the banner and tips shown on a normal interactive launch."));
|
|
212
|
+
items.push(toggleRow("logo-sweep", "logoSweep", "Logo sweep", "Tint the startup wordmark and emblem along a static colour gradient."));
|
|
213
|
+
items.push(toggleRow("reduced-motion", "reducedMotion", "Reduced motion", "Suppress motion-flavoured flourishes such as the logo colour sweep."));
|
|
214
|
+
items.push(toggleRow("hardware-cursor", "showHardwareCursor", "Hardware cursor", "Reveal the terminal's own cursor instead of the software-drawn caret."));
|
|
215
|
+
items.push({
|
|
216
|
+
id: "editor-padding",
|
|
217
|
+
label: "Editor padding",
|
|
218
|
+
description: "Horizontal padding, in columns, around the prompt editor.",
|
|
219
|
+
value: String(settings.get("editorPaddingX")),
|
|
220
|
+
values: ["0", "1", "2", "3"],
|
|
221
|
+
onChange: (value: string) => {
|
|
222
|
+
const parsed = Number.parseInt(value, 10);
|
|
223
|
+
if (Number.isFinite(parsed)) {
|
|
224
|
+
settings.set("editorPaddingX", parsed);
|
|
225
|
+
settings.save();
|
|
226
|
+
}
|
|
227
|
+
bump();
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
} catch {
|
|
231
|
+
return null;
|
|
232
|
+
}
|
|
233
|
+
return jsx(SettingsBoard, { theme, items, onClose: closeModal });
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/** The props every overlay group receives. */
|
|
237
|
+
export interface OverlayGroupProps {
|
|
238
|
+
readonly modal: ModalState;
|
|
239
|
+
readonly services?: OverlayServices;
|
|
240
|
+
readonly theme: InkThemeAdapter;
|
|
241
|
+
readonly dispatch: ConsoleDispatch;
|
|
242
|
+
readonly closeModal: () => void;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export function PickerOverlays(props: OverlayGroupProps) {
|
|
246
|
+
switch (props.modal.kind) {
|
|
247
|
+
case "models":
|
|
248
|
+
return jsx(ModelPicker, { ...props });
|
|
249
|
+
case "scopedModels":
|
|
250
|
+
return jsx(ScopedModelPicker, { ...props });
|
|
251
|
+
case "theme":
|
|
252
|
+
return jsx(ThemePicker, { ...props });
|
|
253
|
+
case "settings":
|
|
254
|
+
return jsx(SettingsPicker, { ...props });
|
|
255
|
+
default:
|
|
256
|
+
return null;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session overlays — the transcript navigation dialogs.
|
|
3
|
+
*/
|
|
4
|
+
import { useEffect, useState } from "indusagi/react-host";
|
|
5
|
+
import { SessionDialog, TreeDialog, UserMessageDialog } from "indusagi/react-ink";
|
|
6
|
+
import { jsx } from "indusagi/react-host/jsx-runtime";
|
|
7
|
+
import type { OverlayGroupProps } from "./pickers.js";
|
|
8
|
+
|
|
9
|
+
function toSessionInfo(row: any) {
|
|
10
|
+
const lastModified = row.lastModified ?? 0;
|
|
11
|
+
return {
|
|
12
|
+
id: row.id,
|
|
13
|
+
path: row.path,
|
|
14
|
+
name: row.name,
|
|
15
|
+
modified: new Date(lastModified),
|
|
16
|
+
lastModified,
|
|
17
|
+
size: row.size,
|
|
18
|
+
messageCount: row.messageCount,
|
|
19
|
+
firstMessage: row.preview
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function SessionPicker(props: OverlayGroupProps) {
|
|
24
|
+
const { services, closeModal } = props;
|
|
25
|
+
const [sessions, setSessions] = useState<any[]>([]);
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
let live = true;
|
|
28
|
+
void (async () => {
|
|
29
|
+
try {
|
|
30
|
+
const rows = await services!.sessions.list();
|
|
31
|
+
if (live) setSessions(rows.map(toSessionInfo));
|
|
32
|
+
} catch {
|
|
33
|
+
if (live) setSessions([]);
|
|
34
|
+
}
|
|
35
|
+
})();
|
|
36
|
+
return () => {
|
|
37
|
+
live = false;
|
|
38
|
+
};
|
|
39
|
+
}, [services]);
|
|
40
|
+
const currentPath = services!.sessions.pathOf(services!.conductor.snapshot().head.sessionId);
|
|
41
|
+
return jsx(SessionDialog, {
|
|
42
|
+
currentSessions: sessions,
|
|
43
|
+
currentSessionPath: currentPath,
|
|
44
|
+
onSelect: async (session: any) => {
|
|
45
|
+
try {
|
|
46
|
+
await services!.conductor.resume(session.id);
|
|
47
|
+
} catch {
|
|
48
|
+
// ignore
|
|
49
|
+
}
|
|
50
|
+
closeModal();
|
|
51
|
+
},
|
|
52
|
+
onLoadAllSessions: async () => {
|
|
53
|
+
try {
|
|
54
|
+
const rows = await services!.sessions.list({ deep: true });
|
|
55
|
+
const mapped = rows.map(toSessionInfo);
|
|
56
|
+
setSessions(mapped);
|
|
57
|
+
return mapped;
|
|
58
|
+
} catch {
|
|
59
|
+
return [];
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
onDeleteSession: async (session: any) => {
|
|
63
|
+
try {
|
|
64
|
+
await services!.sessions.remove(session.id);
|
|
65
|
+
setSessions((prev) => prev.filter((row) => row.id !== session.id));
|
|
66
|
+
} catch {
|
|
67
|
+
// ignore
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
onRenameSession: async (session: any, name: string) => {
|
|
71
|
+
try {
|
|
72
|
+
await services!.sessions.rename(session.id, name);
|
|
73
|
+
const rows = await services!.sessions.list();
|
|
74
|
+
setSessions(rows.map(toSessionInfo));
|
|
75
|
+
} catch {
|
|
76
|
+
// ignore
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
onClose: closeModal
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function BranchTree(props: OverlayGroupProps) {
|
|
84
|
+
const { services, closeModal } = props;
|
|
85
|
+
const [items, setItems] = useState<any[]>([]);
|
|
86
|
+
useEffect(() => {
|
|
87
|
+
let live = true;
|
|
88
|
+
void (async () => {
|
|
89
|
+
try {
|
|
90
|
+
const sessionId = services!.conductor.snapshot().head.sessionId;
|
|
91
|
+
const store = await services!.sessions.open(sessionId);
|
|
92
|
+
if (store === null) {
|
|
93
|
+
if (live) setItems([]);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const nodes = services!.sessions.tree(store).map((node: any) => ({
|
|
97
|
+
id: node.id,
|
|
98
|
+
label: node.label,
|
|
99
|
+
depth: node.depth,
|
|
100
|
+
isLeaf: node.isLeaf,
|
|
101
|
+
isCurrent: node.isCurrent
|
|
102
|
+
}));
|
|
103
|
+
if (live) setItems(nodes);
|
|
104
|
+
} catch {
|
|
105
|
+
if (live) setItems([]);
|
|
106
|
+
}
|
|
107
|
+
})();
|
|
108
|
+
return () => {
|
|
109
|
+
live = false;
|
|
110
|
+
};
|
|
111
|
+
}, [services]);
|
|
112
|
+
return jsx(TreeDialog, {
|
|
113
|
+
items,
|
|
114
|
+
onSelect: async (node: any) => {
|
|
115
|
+
if (node.id !== null) {
|
|
116
|
+
try {
|
|
117
|
+
await services!.conductor.navigateTree(node.id);
|
|
118
|
+
const info = await services!.conductor.codeRestoreInfo(node.id);
|
|
119
|
+
if (info.canRestore) {
|
|
120
|
+
await services!.conductor.restoreCode(node.id);
|
|
121
|
+
}
|
|
122
|
+
} catch {
|
|
123
|
+
// ignore
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
closeModal();
|
|
127
|
+
},
|
|
128
|
+
onClose: closeModal
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function PriorTurns(props: OverlayGroupProps) {
|
|
133
|
+
const { services, closeModal } = props;
|
|
134
|
+
const [items, setItems] = useState<any[]>([]);
|
|
135
|
+
useEffect(() => {
|
|
136
|
+
let live = true;
|
|
137
|
+
void (async () => {
|
|
138
|
+
try {
|
|
139
|
+
const sessionId = services!.conductor.snapshot().head.sessionId;
|
|
140
|
+
const store = await services!.sessions.open(sessionId);
|
|
141
|
+
if (store === null) {
|
|
142
|
+
if (live) setItems([]);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
const turns = services!.sessions.priorTurns(store).map((turn: any) => ({
|
|
146
|
+
entryId: turn.entryId,
|
|
147
|
+
text: turn.text,
|
|
148
|
+
preview: turn.preview
|
|
149
|
+
}));
|
|
150
|
+
if (live) setItems(turns);
|
|
151
|
+
} catch {
|
|
152
|
+
if (live) setItems([]);
|
|
153
|
+
}
|
|
154
|
+
})();
|
|
155
|
+
return () => {
|
|
156
|
+
live = false;
|
|
157
|
+
};
|
|
158
|
+
}, [services]);
|
|
159
|
+
return jsx(UserMessageDialog, {
|
|
160
|
+
items,
|
|
161
|
+
onSelect: async (turn: any) => {
|
|
162
|
+
try {
|
|
163
|
+
await services!.conductor.fork(turn.entryId);
|
|
164
|
+
const info = await services!.conductor.codeRestoreInfo(turn.entryId);
|
|
165
|
+
if (info.canRestore) {
|
|
166
|
+
await services!.conductor.restoreCode(turn.entryId);
|
|
167
|
+
}
|
|
168
|
+
} catch {
|
|
169
|
+
// ignore
|
|
170
|
+
}
|
|
171
|
+
closeModal();
|
|
172
|
+
},
|
|
173
|
+
onClose: closeModal
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export function SessionOverlays(props: OverlayGroupProps) {
|
|
178
|
+
const { modal, services, closeModal } = props;
|
|
179
|
+
if (services === void 0) return null;
|
|
180
|
+
switch (modal.kind) {
|
|
181
|
+
case "sessions":
|
|
182
|
+
return jsx(SessionPicker, { ...props, services, closeModal });
|
|
183
|
+
case "tree":
|
|
184
|
+
return jsx(BranchTree, { ...props, services, closeModal });
|
|
185
|
+
case "userTurns":
|
|
186
|
+
return jsx(PriorTurns, { ...props, services, closeModal });
|
|
187
|
+
default:
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Console reducer — the single pure fold over {@link ConsoleState}.
|
|
3
|
+
*
|
|
4
|
+
* The whole terminal surface is reduced from one immutable {@link ConsoleState}
|
|
5
|
+
* value, mutated only by dispatching a {@link ConsoleEvent} into
|
|
6
|
+
* {@link consoleReducer}. This module is the one place those transitions live:
|
|
7
|
+
* it is a pure function (no React, no I/O, no timers) that, given a prior state
|
|
8
|
+
* and an event, returns a *fresh* state — it never mutates its input. That makes
|
|
9
|
+
* every transition unit-testable in isolation and keeps the surface component a
|
|
10
|
+
* thin shell that only wires events in and renders the result out.
|
|
11
|
+
*/
|
|
12
|
+
import { EMPTY_CONSOLE_STATE, NO_MODAL, transitionModal, type ConsoleEvent, type ConsoleState } from "./contract.js";
|
|
13
|
+
|
|
14
|
+
function clamp(offset: number, max: number) {
|
|
15
|
+
if (offset < 0) return 0;
|
|
16
|
+
if (offset > max) return max;
|
|
17
|
+
return offset;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function spliceAt(text: string, at: number, insert: string) {
|
|
21
|
+
return text.slice(0, at) + insert + text.slice(at);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function withBuffer(state: ConsoleState, text: string, caret: number) {
|
|
25
|
+
return { ...state, buffer: text, caret: clamp(caret, text.length) };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function setBuffer(state: ConsoleState, text: string) {
|
|
29
|
+
return withBuffer(state, text, text.length);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function insertBuffer(state: ConsoleState, text: string) {
|
|
33
|
+
if (text.length === 0) return state;
|
|
34
|
+
const at = clamp(state.caret, state.buffer.length);
|
|
35
|
+
return withBuffer(state, spliceAt(state.buffer, at, text), at + text.length);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function erasePrev(state: ConsoleState) {
|
|
39
|
+
const at = clamp(state.caret, state.buffer.length);
|
|
40
|
+
if (at === 0) return state;
|
|
41
|
+
const next = state.buffer.slice(0, at - 1) + state.buffer.slice(at);
|
|
42
|
+
return withBuffer(state, next, at - 1);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function eraseNext(state: ConsoleState) {
|
|
46
|
+
const at = clamp(state.caret, state.buffer.length);
|
|
47
|
+
if (at >= state.buffer.length) return state;
|
|
48
|
+
const next = state.buffer.slice(0, at) + state.buffer.slice(at + 1);
|
|
49
|
+
return withBuffer(state, next, at);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function pushHistory(state: ConsoleState, text: string) {
|
|
53
|
+
const trimmed = text.trim();
|
|
54
|
+
const base = { ...state, historyAt: null, stash: "" };
|
|
55
|
+
if (trimmed.length === 0) return base;
|
|
56
|
+
const last = state.history[state.history.length - 1];
|
|
57
|
+
if (last === text) return base;
|
|
58
|
+
return { ...base, history: [...state.history, text] };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function historyBack(state: ConsoleState) {
|
|
62
|
+
if (state.history.length === 0) return state;
|
|
63
|
+
const atEnd = state.historyAt === null;
|
|
64
|
+
const index = atEnd ? state.history.length - 1 : Math.max(0, state.historyAt - 1);
|
|
65
|
+
const stash = atEnd ? state.buffer : state.stash;
|
|
66
|
+
const recalled = state.history[index];
|
|
67
|
+
return { ...withBuffer(state, recalled, recalled.length), historyAt: index, stash };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function historyForward(state: ConsoleState) {
|
|
71
|
+
if (state.historyAt === null) return state;
|
|
72
|
+
const next = state.historyAt + 1;
|
|
73
|
+
if (next >= state.history.length) {
|
|
74
|
+
const stash = state.stash;
|
|
75
|
+
return { ...withBuffer(state, stash, stash.length), historyAt: null, stash: "" };
|
|
76
|
+
}
|
|
77
|
+
const recalled = state.history[next];
|
|
78
|
+
return { ...withBuffer(state, recalled, recalled.length), historyAt: next };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function appendRow(state: ConsoleState, row: ConsoleState["rows"][number]) {
|
|
82
|
+
return { ...state, rows: [...state.rows, row] };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function patchRow(state: ConsoleState, id: string, text: string) {
|
|
86
|
+
let found = false;
|
|
87
|
+
const rows = state.rows.map((row) => {
|
|
88
|
+
if (row.id !== id) return row;
|
|
89
|
+
found = true;
|
|
90
|
+
return { ...row, text };
|
|
91
|
+
});
|
|
92
|
+
return found ? { ...state, rows } : state;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Fold a {@link ConsoleEvent} into a {@link ConsoleState}, returning a fresh state.
|
|
97
|
+
*/
|
|
98
|
+
export function consoleReducer(state: ConsoleState, event: ConsoleEvent): ConsoleState {
|
|
99
|
+
switch (event.type) {
|
|
100
|
+
// Composer buffer & caret
|
|
101
|
+
case "buffer:set":
|
|
102
|
+
return setBuffer(state, event.text);
|
|
103
|
+
case "buffer:insert":
|
|
104
|
+
return insertBuffer(state, event.text);
|
|
105
|
+
case "buffer:replaceSpan": {
|
|
106
|
+
const at = state.buffer.indexOf(event.find);
|
|
107
|
+
if (at < 0) return state;
|
|
108
|
+
const buffer = state.buffer.slice(0, at) + event.replaceWith + state.buffer.slice(at + event.find.length);
|
|
109
|
+
const spanEnd = at + event.find.length;
|
|
110
|
+
const caret = state.caret >= spanEnd
|
|
111
|
+
? state.caret + (event.replaceWith.length - event.find.length)
|
|
112
|
+
: state.caret > at
|
|
113
|
+
? at + event.replaceWith.length
|
|
114
|
+
: state.caret;
|
|
115
|
+
return { ...state, buffer, caret: clamp(caret, buffer.length) };
|
|
116
|
+
}
|
|
117
|
+
case "buffer:erasePrev":
|
|
118
|
+
return erasePrev(state);
|
|
119
|
+
case "buffer:eraseNext":
|
|
120
|
+
return eraseNext(state);
|
|
121
|
+
case "caret:move":
|
|
122
|
+
return { ...state, caret: clamp(state.caret + event.delta, state.buffer.length) };
|
|
123
|
+
case "caret:jump":
|
|
124
|
+
return { ...state, caret: clamp(event.offset, state.buffer.length) };
|
|
125
|
+
// History ring
|
|
126
|
+
case "history:push":
|
|
127
|
+
return pushHistory(state, event.text);
|
|
128
|
+
case "history:back":
|
|
129
|
+
return historyBack(state);
|
|
130
|
+
case "history:forward":
|
|
131
|
+
return historyForward(state);
|
|
132
|
+
// Transcript view
|
|
133
|
+
case "rows:set":
|
|
134
|
+
return { ...state, rows: [...event.rows] };
|
|
135
|
+
case "rows:append":
|
|
136
|
+
return appendRow(state, event.row);
|
|
137
|
+
case "rows:patch":
|
|
138
|
+
return patchRow(state, event.id, event.text);
|
|
139
|
+
case "block:append":
|
|
140
|
+
return { ...state, blocks: [...state.blocks, event.block] };
|
|
141
|
+
case "blocks:clear":
|
|
142
|
+
return state.blocks.length === 0 ? state : { ...state, blocks: [] };
|
|
143
|
+
// Overlays, status, theme, toggles, busy
|
|
144
|
+
case "modal:open":
|
|
145
|
+
return { ...state, modal: transitionModal(event.kind, event.payload) };
|
|
146
|
+
case "modal:close":
|
|
147
|
+
return { ...state, modal: NO_MODAL };
|
|
148
|
+
case "status:set":
|
|
149
|
+
return { ...state, status: event.status };
|
|
150
|
+
case "status:clear": {
|
|
151
|
+
if (state.status === void 0) return state;
|
|
152
|
+
const { status: _drop, ...rest } = state;
|
|
153
|
+
return rest;
|
|
154
|
+
}
|
|
155
|
+
case "scheme:set":
|
|
156
|
+
return { ...state, scheme: event.scheme };
|
|
157
|
+
case "toggle:reasoning":
|
|
158
|
+
return { ...state, showReasoning: !state.showReasoning };
|
|
159
|
+
case "toggle:images":
|
|
160
|
+
return { ...state, showImages: !state.showImages };
|
|
161
|
+
case "busy:set": {
|
|
162
|
+
if (event.busy) {
|
|
163
|
+
const { status: _drop, ...rest } = state;
|
|
164
|
+
return { ...rest, busy: true };
|
|
165
|
+
}
|
|
166
|
+
return { ...state, busy: false };
|
|
167
|
+
}
|
|
168
|
+
case "tick":
|
|
169
|
+
return { ...state, tick: state.tick + 1 };
|
|
170
|
+
default: {
|
|
171
|
+
const _never = event;
|
|
172
|
+
return _never;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Build the initial {@link ConsoleState} a console mounts with.
|
|
179
|
+
*/
|
|
180
|
+
export function initConsoleState(overrides?: Partial<ConsoleState>): ConsoleState {
|
|
181
|
+
return overrides === void 0 ? EMPTY_CONSOLE_STATE : { ...EMPTY_CONSOLE_STATE, ...overrides };
|
|
182
|
+
}
|