@visual-z/xeno 0.8.4 → 0.9.1
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 +19 -0
- package/README.md +31 -746
- package/bin/xeno.exe +26 -0
- package/install.cjs +88 -0
- package/package.json +18 -48
- package/docs/app-server.md +0 -115
- package/docs/foundations.md +0 -88
- package/docs/install.md +0 -244
- package/docs/providers.md +0 -172
- package/docs/research/foundation-clients-2026-10-09.md +0 -27
- package/docs/research/foundation-editing-2026-10-09.md +0 -56
- package/docs/research/foundation-network-2026-10-09.md +0 -37
- package/docs/research/foundation-workflow-2026-10-09.md +0 -37
- package/src/apps/acp-server.ts +0 -41
- package/src/apps/agent-profiles.ts +0 -19
- package/src/apps/app-server.ts +0 -30
- package/src/apps/approve.ts +0 -49
- package/src/apps/cli.ts +0 -99
- package/src/apps/commands.ts +0 -319
- package/src/apps/completions.ts +0 -87
- package/src/apps/control-transport.ts +0 -90
- package/src/apps/export-cli.ts +0 -24
- package/src/apps/goal-flow.ts +0 -70
- package/src/apps/image-draft.ts +0 -46
- package/src/apps/input.ts +0 -312
- package/src/apps/model-catalog.ts +0 -69
- package/src/apps/preferences.ts +0 -20
- package/src/apps/project-trust.ts +0 -9
- package/src/apps/provider-connect.ts +0 -346
- package/src/apps/question-ui.ts +0 -27
- package/src/apps/render.ts +0 -181
- package/src/apps/resources.ts +0 -66
- package/src/apps/review.ts +0 -109
- package/src/apps/session-hub.ts +0 -96
- package/src/apps/session-lifecycle.ts +0 -64
- package/src/apps/shell.ts +0 -2071
- package/src/apps/startup-input.ts +0 -135
- package/src/apps/startup.ts +0 -1677
- package/src/apps/ui/app.ts +0 -1818
- package/src/apps/ui/control-model.ts +0 -43
- package/src/apps/ui/extension-host.ts +0 -194
- package/src/apps/ui/image-cache.ts +0 -121
- package/src/apps/ui/palette.ts +0 -88
- package/src/apps/ui/project.ts +0 -127
- package/src/apps/ui/standalone.ts +0 -148
- package/src/apps/ui/status-model.ts +0 -68
- package/src/apps/ui/surface.ts +0 -91
- package/src/apps/ui/theme.ts +0 -86
- package/src/apps/update-check.ts +0 -112
- package/src/apps/workflow.ts +0 -58
- package/src/core/acceptance.ts +0 -139
- package/src/core/action-defaults.ts +0 -58
- package/src/core/activity-status.ts +0 -52
- package/src/core/agent-profile.ts +0 -130
- package/src/core/attachment-input.ts +0 -17
- package/src/core/blind-checks.ts +0 -72
- package/src/core/boundary.ts +0 -174
- package/src/core/branch-receipt.ts +0 -26
- package/src/core/catalog-merge.ts +0 -19
- package/src/core/checkpoints.ts +0 -85
- package/src/core/churn.ts +0 -61
- package/src/core/command-artifacts.ts +0 -94
- package/src/core/command-contract.ts +0 -151
- package/src/core/completion-action.ts +0 -20
- package/src/core/completion-review.ts +0 -98
- package/src/core/context-detail.ts +0 -37
- package/src/core/context-policy.ts +0 -133
- package/src/core/context-window.ts +0 -54
- package/src/core/continuation.ts +0 -130
- package/src/core/control-protocol.ts +0 -69
- package/src/core/control-view.ts +0 -39
- package/src/core/cycle.ts +0 -17
- package/src/core/data-query.ts +0 -201
- package/src/core/deadline.ts +0 -40
- package/src/core/derive.ts +0 -516
- package/src/core/draft-images.ts +0 -56
- package/src/core/edit-plan.ts +0 -200
- package/src/core/environment.ts +0 -181
- package/src/core/event-validation.ts +0 -220
- package/src/core/events.ts +0 -670
- package/src/core/evidence-ladder.ts +0 -62
- package/src/core/evidence-links.ts +0 -68
- package/src/core/evidence.ts +0 -240
- package/src/core/execution.ts +0 -18
- package/src/core/exhaustive.ts +0 -18
- package/src/core/export.ts +0 -179
- package/src/core/extension-flags.ts +0 -14
- package/src/core/external-effects.ts +0 -488
- package/src/core/failure-recovery.ts +0 -173
- package/src/core/file-precondition.ts +0 -78
- package/src/core/file-references.ts +0 -35
- package/src/core/frontmatter.ts +0 -213
- package/src/core/git-status.ts +0 -29
- package/src/core/goal-view.ts +0 -64
- package/src/core/goal.ts +0 -200
- package/src/core/handoff.ts +0 -479
- package/src/core/history-query.ts +0 -139
- package/src/core/history.ts +0 -237
- package/src/core/image-limits.ts +0 -72
- package/src/core/images.ts +0 -46
- package/src/core/integration-health.ts +0 -34
- package/src/core/json-event.ts +0 -131
- package/src/core/json-pointer.ts +0 -46
- package/src/core/keymap.ts +0 -153
- package/src/core/lsp-framing.ts +0 -34
- package/src/core/mcp-config.ts +0 -68
- package/src/core/model-picker.ts +0 -64
- package/src/core/model-preferences.ts +0 -28
- package/src/core/model-replay.ts +0 -22
- package/src/core/model-scope.ts +0 -53
- package/src/core/note-guidance.ts +0 -34
- package/src/core/oauth-fields.ts +0 -44
- package/src/core/observation-view.ts +0 -152
- package/src/core/outcome.ts +0 -44
- package/src/core/overflow.ts +0 -59
- package/src/core/packages.ts +0 -47
- package/src/core/patch.ts +0 -130
- package/src/core/path-suggestions.ts +0 -59
- package/src/core/permissions.ts +0 -410
- package/src/core/process-receipt.ts +0 -105
- package/src/core/process-state.ts +0 -37
- package/src/core/program-status.ts +0 -40
- package/src/core/progress.ts +0 -150
- package/src/core/project.ts +0 -431
- package/src/core/prompt-policy.ts +0 -46
- package/src/core/prompts.ts +0 -111
- package/src/core/question.ts +0 -94
- package/src/core/queue.ts +0 -78
- package/src/core/read-bounds.ts +0 -24
- package/src/core/read-observation.ts +0 -106
- package/src/core/recovery-sources.ts +0 -74
- package/src/core/regex-dialect.ts +0 -25
- package/src/core/request-budget.ts +0 -23
- package/src/core/request-progress.ts +0 -87
- package/src/core/request-state.ts +0 -273
- package/src/core/resource-selection.ts +0 -36
- package/src/core/response-recovery.ts +0 -59
- package/src/core/resume.ts +0 -59
- package/src/core/review.ts +0 -68
- package/src/core/ripgrep-release.ts +0 -64
- package/src/core/runtime-target.ts +0 -94
- package/src/core/scheduling.ts +0 -8
- package/src/core/search-observation.ts +0 -97
- package/src/core/secrets.ts +0 -188
- package/src/core/session-archive.ts +0 -4
- package/src/core/session-browser.ts +0 -82
- package/src/core/session-entry.ts +0 -61
- package/src/core/session-exit.ts +0 -42
- package/src/core/session-format.ts +0 -169
- package/src/core/session-position.ts +0 -26
- package/src/core/session-tree.ts +0 -147
- package/src/core/settings.ts +0 -192
- package/src/core/skills.ts +0 -165
- package/src/core/sql-query.ts +0 -211
- package/src/core/stall.ts +0 -128
- package/src/core/stop-report.ts +0 -68
- package/src/core/text-normalize.ts +0 -81
- package/src/core/theme.ts +0 -166
- package/src/core/thinking.ts +0 -26
- package/src/core/tokens.ts +0 -151
- package/src/core/tool-contract.ts +0 -153
- package/src/core/tool-discovery.ts +0 -87
- package/src/core/tool-failure.ts +0 -81
- package/src/core/tool-input.ts +0 -44
- package/src/core/transcript.ts +0 -108
- package/src/core/tui-mode.ts +0 -29
- package/src/core/update-check.ts +0 -157
- package/src/core/version.ts +0 -55
- package/src/core/web-content.ts +0 -80
- package/src/core/work-state.ts +0 -154
- package/src/core/workflow-mode.ts +0 -34
- package/src/core/working-set.ts +0 -119
- package/src/core/yaml.ts +0 -282
- package/src/runtime/acp-agent.ts +0 -289
- package/src/runtime/agent-profiles.ts +0 -21
- package/src/runtime/auth.ts +0 -367
- package/src/runtime/blind-check.ts +0 -36
- package/src/runtime/branch-jobs.ts +0 -36
- package/src/runtime/catalog.ts +0 -285
- package/src/runtime/clipboard.ts +0 -125
- package/src/runtime/command-artifacts.ts +0 -64
- package/src/runtime/connection-reuse.ts +0 -40
- package/src/runtime/control-client.ts +0 -149
- package/src/runtime/control-connection.ts +0 -139
- package/src/runtime/counter-check.ts +0 -74
- package/src/runtime/environment-tools.ts +0 -60
- package/src/runtime/evidence-notes.ts +0 -35
- package/src/runtime/exec-sessions.ts +0 -456
- package/src/runtime/execution-control.ts +0 -40
- package/src/runtime/extension-loader.ts +0 -37
- package/src/runtime/extension-ui.ts +0 -47
- package/src/runtime/extensions.ts +0 -370
- package/src/runtime/formatter-tool.ts +0 -38
- package/src/runtime/formatter.ts +0 -50
- package/src/runtime/foundation.ts +0 -116
- package/src/runtime/history-cache.ts +0 -57
- package/src/runtime/history-tools.ts +0 -168
- package/src/runtime/images.ts +0 -80
- package/src/runtime/integrations.ts +0 -216
- package/src/runtime/loop.ts +0 -3108
- package/src/runtime/lsp-client.ts +0 -201
- package/src/runtime/mcp-client.ts +0 -264
- package/src/runtime/mcp-oauth.ts +0 -168
- package/src/runtime/mcp-schema.ts +0 -51
- package/src/runtime/model-config.ts +0 -232
- package/src/runtime/model-data.LICENSE +0 -27
- package/src/runtime/model-data.ts +0 -21
- package/src/runtime/model-discovery.ts +0 -208
- package/src/runtime/model-replay.ts +0 -10
- package/src/runtime/model-scope.ts +0 -20
- package/src/runtime/model-sources.ts +0 -151
- package/src/runtime/model.ts +0 -686
- package/src/runtime/models-fetch.ts +0 -57
- package/src/runtime/oauth-device.ts +0 -86
- package/src/runtime/oauth.ts +0 -853
- package/src/runtime/open-image.ts +0 -42
- package/src/runtime/openai-responses.ts +0 -304
- package/src/runtime/openai.ts +0 -697
- package/src/runtime/output-schema.ts +0 -26
- package/src/runtime/package-manager.ts +0 -125
- package/src/runtime/patch-tool.ts +0 -38
- package/src/runtime/permission-resources.ts +0 -18
- package/src/runtime/pi-provider.ts +0 -423
- package/src/runtime/program-status.ts +0 -38
- package/src/runtime/progress.ts +0 -26
- package/src/runtime/provider-access.ts +0 -21
- package/src/runtime/provider-connect.ts +0 -109
- package/src/runtime/provider-presets.ts +0 -98
- package/src/runtime/provider-protocols.ts +0 -29
- package/src/runtime/provider-request.ts +0 -87
- package/src/runtime/provider-tool-policy.ts +0 -33
- package/src/runtime/provider.ts +0 -580
- package/src/runtime/question-tool.ts +0 -62
- package/src/runtime/retry.ts +0 -447
- package/src/runtime/review-tool.ts +0 -48
- package/src/runtime/rich-text.ts +0 -36
- package/src/runtime/runtime-target.ts +0 -149
- package/src/runtime/secrets.ts +0 -79
- package/src/runtime/self-update.ts +0 -74
- package/src/runtime/session-control.ts +0 -265
- package/src/runtime/session-hub.ts +0 -85
- package/src/runtime/stream-idle.ts +0 -80
- package/src/runtime/tool-broker.ts +0 -137
- package/src/runtime/tool-discovery.ts +0 -58
- package/src/runtime/tool-queue.ts +0 -77
- package/src/runtime/tools.ts +0 -1820
- package/src/runtime/turn-effects.ts +0 -31
- package/src/runtime/update-check.ts +0 -48
- package/src/runtime/usage.ts +0 -253
- package/src/runtime/version.ts +0 -12
- package/src/runtime/web-http.ts +0 -56
- package/src/runtime/web-tools.ts +0 -88
- package/src/runtime/working-notes.ts +0 -60
- package/src/runtime/workspace-lifecycle.ts +0 -141
- package/src/store/acceptance.ts +0 -15
- package/src/store/agent-profile-files.ts +0 -13
- package/src/store/atomic.ts +0 -31
- package/src/store/auth-store.ts +0 -50
- package/src/store/blind-checks.ts +0 -32
- package/src/store/blobs.ts +0 -143
- package/src/store/checkpoint-history.ts +0 -32
- package/src/store/command-artifacts.ts +0 -96
- package/src/store/command.ts +0 -111
- package/src/store/config.ts +0 -69
- package/src/store/context-files.ts +0 -436
- package/src/store/file-checkpoints.ts +0 -108
- package/src/store/file-index.ts +0 -174
- package/src/store/foundation-config.ts +0 -24
- package/src/store/git-checkpoints.ts +0 -67
- package/src/store/git-status.ts +0 -15
- package/src/store/history-search.ts +0 -19
- package/src/store/log.ts +0 -725
- package/src/store/lsp-document.ts +0 -30
- package/src/store/mcp-credentials.ts +0 -85
- package/src/store/models.ts +0 -60
- package/src/store/observations.ts +0 -224
- package/src/store/packages.ts +0 -98
- package/src/store/patch.ts +0 -92
- package/src/store/permission-paths.ts +0 -34
- package/src/store/process-receipts.ts +0 -34
- package/src/store/query-source.ts +0 -49
- package/src/store/recovery.ts +0 -44
- package/src/store/replay.ts +0 -76
- package/src/store/resource-index.ts +0 -170
- package/src/store/resources.ts +0 -285
- package/src/store/restore.ts +0 -110
- package/src/store/review.ts +0 -109
- package/src/store/runtime-target.ts +0 -64
- package/src/store/search.ts +0 -728
- package/src/store/secrets.ts +0 -44
- package/src/store/serial.ts +0 -13
- package/src/store/session-archive.ts +0 -165
- package/src/store/session-browser.ts +0 -68
- package/src/store/session-position.ts +0 -27
- package/src/store/sessions.ts +0 -525
- package/src/store/source-cancellation.ts +0 -6
- package/src/store/tool-binaries.ts +0 -90
- package/src/store/trust.ts +0 -82
- package/src/store/update-cache.ts +0 -36
- package/src/store/user-settings.ts +0 -30
- package/src/store/workspace-clone.ts +0 -42
- package/src/store/workspace-observation.ts +0 -113
- package/src/store/workspace.ts +0 -115
- package/src/store/worktree.ts +0 -278
- package/src/tui/ansi-spans.ts +0 -126
- package/src/tui/diff.ts +0 -160
- package/src/tui/exhaustive.ts +0 -14
- package/src/tui/fuzzy.ts +0 -75
- package/src/tui/style.ts +0 -208
- package/src/tui/ui/atomic-draft.ts +0 -121
- package/src/tui/ui/branch-panel.ts +0 -47
- package/src/tui/ui/canvas.ts +0 -96
- package/src/tui/ui/colors.ts +0 -70
- package/src/tui/ui/composer.ts +0 -199
- package/src/tui/ui/control-panel.ts +0 -25
- package/src/tui/ui/control-sidebar.ts +0 -49
- package/src/tui/ui/dialog.ts +0 -398
- package/src/tui/ui/draft.ts +0 -23
- package/src/tui/ui/duration.ts +0 -16
- package/src/tui/ui/edge.ts +0 -186
- package/src/tui/ui/image-size.ts +0 -44
- package/src/tui/ui/ink.ts +0 -210
- package/src/tui/ui/items.ts +0 -42
- package/src/tui/ui/keys.ts +0 -40
- package/src/tui/ui/layout.ts +0 -74
- package/src/tui/ui/list-model.ts +0 -86
- package/src/tui/ui/list-view.ts +0 -172
- package/src/tui/ui/mascot.ts +0 -67
- package/src/tui/ui/messages.ts +0 -518
- package/src/tui/ui/motion.ts +0 -28
- package/src/tui/ui/paths.ts +0 -10
- package/src/tui/ui/pictures.ts +0 -108
- package/src/tui/ui/prompt-history.ts +0 -60
- package/src/tui/ui/screen.ts +0 -579
- package/src/tui/ui/status.ts +0 -403
- package/src/tui/ui/styled.ts +0 -85
- package/src/tui/ui/tool-card.ts +0 -150
- package/src/tui/width.ts +0 -567
- package/src/vendor/pi-rich/LICENSE.upstream +0 -21
- package/src/vendor/pi-rich/UPSTREAM.json +0 -17
- package/src/vendor/pi-rich/clipboard-command.ts +0 -44
- package/src/vendor/pi-rich/exif-orientation.ts +0 -182
- package/src/vendor/pi-rich/html.ts +0 -51
- package/src/vendor/pi-rich/image-convert.ts +0 -49
- package/src/vendor/pi-rich/image-resize-core.ts +0 -164
- package/src/vendor/pi-rich/image-resize-worker.ts +0 -42
- package/src/vendor/pi-rich/image-resize.ts +0 -123
- package/src/vendor/pi-rich/mime.ts +0 -116
- package/src/vendor/pi-rich/photon.ts +0 -139
- package/src/vendor/pi-rich/syntax-highlight.ts +0 -214
package/src/apps/input.ts
DELETED
|
@@ -1,312 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Expand @file and !cmd in a user prompt before the line is sent.
|
|
3
|
-
*
|
|
4
|
-
* These are the user's own keystrokes, not the model's: a shared path with
|
|
5
|
-
* the bash tool would either grant the model the user's authority or make
|
|
6
|
-
* the user ask permission to run their own shell.
|
|
7
|
-
*
|
|
8
|
-
* Shell commands trigger only at line start. Explicit @ references may
|
|
9
|
-
* occur inline; emails and code literals remain ordinary prompt text.
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import { readFile } from "node:fs/promises";
|
|
13
|
-
import { isAbsolute, relative, resolve, sep } from "node:path";
|
|
14
|
-
import { fuzzyFilter } from "../tui/fuzzy.ts";
|
|
15
|
-
import type { FileIndex } from "../store/file-index.ts";
|
|
16
|
-
import { fileReferences } from "../core/file-references.ts";
|
|
17
|
-
import type { ImageRef } from "../core/images.ts";
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Per-file cap for @ expansion. Assumed, not measured: 256KB of one file
|
|
21
|
-
* already crowds a turn; a silent half-file would let the model invent
|
|
22
|
-
* the rest. Hitting it truncates and names the file.
|
|
23
|
-
*/
|
|
24
|
-
export const MAX_EXPAND_FILE_BYTES = 256 * 1024;
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Bound on captured command output. Assumed, not measured: a command that
|
|
28
|
-
* dumps a build log would otherwise be an unbounded allocation. Truncated
|
|
29
|
-
* output still carries the exit code -- the exit line is appended after
|
|
30
|
-
* the body is capped, so a cut cannot hide whether the command passed.
|
|
31
|
-
*/
|
|
32
|
-
export const MAX_COMMAND_OUTPUT_BYTES = 64 * 1024;
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Timeout for !cmd. Assumed, not measured: long enough for a test suite,
|
|
36
|
-
* short enough that a hung command cannot stall the editor. On expiry the
|
|
37
|
-
* process is killed and the timeout is reported as a nonzero exit.
|
|
38
|
-
*/
|
|
39
|
-
export const COMMAND_TIMEOUT_MS = 120_000;
|
|
40
|
-
|
|
41
|
-
export interface ExpandContext {
|
|
42
|
-
readonly attachImage?: (path: string) => Promise<ImageRef | null>;
|
|
43
|
-
readonly workspace: string;
|
|
44
|
-
readonly index: FileIndex;
|
|
45
|
-
/** Terminal writer for !!cmd (and user-visible notices). */
|
|
46
|
-
readonly say: (text: string) => void;
|
|
47
|
-
readonly timeoutMs?: number;
|
|
48
|
-
readonly maxFileBytes?: number;
|
|
49
|
-
readonly maxCommandBytes?: number;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export interface ExpandResult {
|
|
53
|
-
readonly images?: readonly ImageRef[];
|
|
54
|
-
/** Text to send to the model. Empty when nothing should be sent. */
|
|
55
|
-
readonly message: string;
|
|
56
|
-
/** False when the input produced no model-facing text (a lone !!cmd). */
|
|
57
|
-
readonly send: boolean;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export function matchFiles(index: FileIndex, query: string): string[] {
|
|
61
|
-
return fuzzyFilter(index.paths, query);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export async function expandInput(text: string, ctx: ExpandContext): Promise<ExpandResult> {
|
|
65
|
-
const out: string[] = [];
|
|
66
|
-
const images: ImageRef[] = [];
|
|
67
|
-
const attachment = ctx.attachImage;
|
|
68
|
-
if (attachment) ctx = { ...ctx, attachImage: async path => { const image = await attachment(path); if (image) images.push(image); return image; } };
|
|
69
|
-
const refs = fileReferences(text);
|
|
70
|
-
let offset = 0;
|
|
71
|
-
for (const line of text.split(/\r?\n/)) {
|
|
72
|
-
const start = offset;
|
|
73
|
-
offset += line.length + (text.slice(offset + line.length).startsWith("\r\n") ? 2 : 1);
|
|
74
|
-
if (line.startsWith("!!")) {
|
|
75
|
-
ctx.say(await runUserCommand(line.slice(2), ctx));
|
|
76
|
-
continue;
|
|
77
|
-
}
|
|
78
|
-
if (line.startsWith("!")) {
|
|
79
|
-
const rendered = await runUserCommand(line.slice(1), ctx);
|
|
80
|
-
ctx.say(rendered);
|
|
81
|
-
out.push(rendered);
|
|
82
|
-
continue;
|
|
83
|
-
}
|
|
84
|
-
if (line.trim() === "@") {
|
|
85
|
-
out.push(skip(ctx, "@", "empty file reference"));
|
|
86
|
-
continue;
|
|
87
|
-
}
|
|
88
|
-
const here = refs.filter((ref) => ref.start >= start && ref.end <= start + line.length);
|
|
89
|
-
if (here.length === 0) { out.push(line); continue; }
|
|
90
|
-
let end = 0;
|
|
91
|
-
for (const ref of here) {
|
|
92
|
-
const prefix = line.slice(end, ref.start - start);
|
|
93
|
-
if (prefix.trim()) out.push(prefix.trimEnd());
|
|
94
|
-
out.push(await expandReference(ref.path, ctx));
|
|
95
|
-
end = ref.end - start;
|
|
96
|
-
}
|
|
97
|
-
if (line.slice(end).trim()) out.push(line.slice(end).trimStart());
|
|
98
|
-
}
|
|
99
|
-
const message = out.join("\n");
|
|
100
|
-
return { message, send: message.trim().length > 0 || images.length > 0, ...(images.length ? { images } : {}) };
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
async function expandReference(ref: string, ctx: ExpandContext): Promise<string> {
|
|
104
|
-
const abs = confine(ctx.workspace, ref);
|
|
105
|
-
if (abs === null) return skip(ctx, ref, "outside the workspace");
|
|
106
|
-
|
|
107
|
-
if (ctx.attachImage && /\.(png|jpe?g|gif|webp|bmp)$/i.test(abs)) {
|
|
108
|
-
const image = await ctx.attachImage(abs).catch(error => { if ((error as { code?: string }).code === "ENOENT") return null; throw error; });
|
|
109
|
-
if (image) return `[Image: ${image.name}, ${image.width}x${image.height}]`;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const got = await tryRead(abs);
|
|
113
|
-
if (got.status === "ok") return renderFile(posix(ref), got, ctx);
|
|
114
|
-
if (got.reason !== "not found") return skip(ctx, posix(ref), got.reason);
|
|
115
|
-
|
|
116
|
-
// Typed path missed; a unique fuzzy hit from the index is the query case.
|
|
117
|
-
const hits = matchFiles(ctx.index, ref);
|
|
118
|
-
if (hits.length === 1 && hits[0] !== posix(ref)) {
|
|
119
|
-
const path = hits[0]!;
|
|
120
|
-
const hitAbs = confine(ctx.workspace, path);
|
|
121
|
-
if (hitAbs === null) return skip(ctx, path, "outside the workspace");
|
|
122
|
-
if (ctx.attachImage && /\.(png|jpe?g|gif|webp|bmp)$/i.test(hitAbs)) {
|
|
123
|
-
const image = await ctx.attachImage(hitAbs);
|
|
124
|
-
if (image) return `[Image: ${image.name}, ${image.width}x${image.height}]`;
|
|
125
|
-
}
|
|
126
|
-
const hit = await tryRead(hitAbs);
|
|
127
|
-
if (hit.status === "ok") return renderFile(path, hit, ctx);
|
|
128
|
-
return skip(ctx, path, hit.reason);
|
|
129
|
-
}
|
|
130
|
-
if (hits.length > 1) return skip(ctx, ref, `ambiguous (${hits.length} matches)`);
|
|
131
|
-
return skip(ctx, posix(ref), got.reason);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
function posix(rel: string): string {
|
|
135
|
-
return rel.split(sep).join("/");
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
function confine(workspace: string, rel: string): string | null {
|
|
139
|
-
if (rel.length === 0) return null;
|
|
140
|
-
const abs = resolve(workspace, rel);
|
|
141
|
-
const inside = relative(workspace, abs);
|
|
142
|
-
if (inside === "" || inside.startsWith(`..${sep}`) || inside === ".." || isAbsolute(inside)) {
|
|
143
|
-
return null;
|
|
144
|
-
}
|
|
145
|
-
return abs;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
type ReadResult =
|
|
149
|
-
| { status: "ok"; text: string; bytes: number }
|
|
150
|
-
| { status: "skip"; reason: string };
|
|
151
|
-
|
|
152
|
-
async function tryRead(abs: string): Promise<ReadResult> {
|
|
153
|
-
let buf: Buffer;
|
|
154
|
-
try {
|
|
155
|
-
buf = await readFile(abs);
|
|
156
|
-
} catch (err) {
|
|
157
|
-
const code = (err as { code?: string }).code;
|
|
158
|
-
if (code === "ENOENT") return { status: "skip", reason: "not found" };
|
|
159
|
-
if (code === "EACCES") return { status: "skip", reason: "permission denied" };
|
|
160
|
-
if (code === "EISDIR") return { status: "skip", reason: "is a directory" };
|
|
161
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
162
|
-
return { status: "skip", reason: message };
|
|
163
|
-
}
|
|
164
|
-
try {
|
|
165
|
-
const text = new TextDecoder("utf-8", { fatal: true }).decode(buf);
|
|
166
|
-
return { status: "ok", text, bytes: buf.byteLength };
|
|
167
|
-
} catch {
|
|
168
|
-
return { status: "skip", reason: "not valid UTF-8" };
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
function renderFile(
|
|
173
|
-
path: string,
|
|
174
|
-
got: Extract<ReadResult, { status: "ok" }>,
|
|
175
|
-
ctx: ExpandContext,
|
|
176
|
-
): string {
|
|
177
|
-
const cap = ctx.maxFileBytes ?? MAX_EXPAND_FILE_BYTES;
|
|
178
|
-
let content = got.text;
|
|
179
|
-
let truncated = false;
|
|
180
|
-
let omitted = 0;
|
|
181
|
-
if (got.bytes > cap) {
|
|
182
|
-
content = truncateUtf8(got.text, cap);
|
|
183
|
-
truncated = true;
|
|
184
|
-
omitted = Math.max(0, got.bytes - Buffer.byteLength(content, "utf8"));
|
|
185
|
-
ctx.say(`[truncated ${path}: ${omitted} bytes omitted]`);
|
|
186
|
-
}
|
|
187
|
-
const inner = truncated
|
|
188
|
-
? `${content}\n[truncated: ${path} exceeded a size limit; ${omitted} bytes omitted]`
|
|
189
|
-
: content;
|
|
190
|
-
return `<file path="${escapeAttr(path)}">\n${inner}\n</file>`;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
function skip(ctx: ExpandContext, path: string, reason: string): string {
|
|
194
|
-
const notice = `[skipped ${path}: ${reason}]`;
|
|
195
|
-
ctx.say(notice);
|
|
196
|
-
return notice;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
function escapeAttr(value: string): string {
|
|
200
|
-
return value.replace(/&/g, "&").replace(/"/g, """);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
function truncateUtf8(text: string, maxBytes: number): string {
|
|
204
|
-
if (maxBytes <= 0) return "";
|
|
205
|
-
const buf = Buffer.from(text, "utf8");
|
|
206
|
-
if (buf.byteLength <= maxBytes) return text;
|
|
207
|
-
let end = maxBytes;
|
|
208
|
-
// Do not split a multibyte sequence: a dangling continuation byte is
|
|
209
|
-
// not valid UTF-8, and the model would see a replacement character.
|
|
210
|
-
while (end > 0 && (buf[end]! & 0xc0) === 0x80) end--;
|
|
211
|
-
return buf.subarray(0, end).toString("utf8");
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
async function runUserCommand(raw: string, ctx: ExpandContext): Promise<string> {
|
|
215
|
-
const cmd = raw.trim();
|
|
216
|
-
if (cmd.length === 0) return skip(ctx, "!", "empty command");
|
|
217
|
-
|
|
218
|
-
const timeoutMs = ctx.timeoutMs ?? COMMAND_TIMEOUT_MS;
|
|
219
|
-
const maxBytes = ctx.maxCommandBytes ?? MAX_COMMAND_OUTPUT_BYTES;
|
|
220
|
-
|
|
221
|
-
const proc = Bun.spawn(["bash", "-lc", cmd], {
|
|
222
|
-
cwd: ctx.workspace,
|
|
223
|
-
stdout: "pipe",
|
|
224
|
-
stderr: "pipe",
|
|
225
|
-
stdin: "ignore",
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
let timedOut = false;
|
|
229
|
-
let escalate: ReturnType<typeof setTimeout> | null = null;
|
|
230
|
-
const timer = setTimeout(() => {
|
|
231
|
-
timedOut = true;
|
|
232
|
-
proc.kill();
|
|
233
|
-
escalate = setTimeout(() => proc.kill("SIGKILL"), 1000);
|
|
234
|
-
}, timeoutMs);
|
|
235
|
-
|
|
236
|
-
let stdout = "";
|
|
237
|
-
let stderr = "";
|
|
238
|
-
let exit = 1;
|
|
239
|
-
try {
|
|
240
|
-
const [out, err] = await Promise.all([
|
|
241
|
-
readCapped(proc.stdout, maxBytes),
|
|
242
|
-
readCapped(proc.stderr, maxBytes),
|
|
243
|
-
]);
|
|
244
|
-
stdout = out.text;
|
|
245
|
-
stderr = err.text;
|
|
246
|
-
if (out.truncated) stdout = `${stdout}\n[stdout truncated]`;
|
|
247
|
-
if (err.truncated) stderr = `${stderr}\n[stderr truncated]`;
|
|
248
|
-
const rawExit = await proc.exited;
|
|
249
|
-
exit = rawExit ?? 1;
|
|
250
|
-
// A kill that somehow reports success would put the model back to
|
|
251
|
-
// guessing. GNU timeout uses 124; never report 0 after a timeout.
|
|
252
|
-
if (timedOut && exit === 0) exit = 124;
|
|
253
|
-
} finally {
|
|
254
|
-
clearTimeout(timer);
|
|
255
|
-
if (escalate !== null) clearTimeout(escalate);
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
const parts: string[] = [`$ ${cmd}`];
|
|
259
|
-
if (stdout.length > 0) parts.push(stripTrailingNewline(stdout));
|
|
260
|
-
if (stderr.length > 0) parts.push(stripTrailingNewline(stderr));
|
|
261
|
-
if (timedOut) parts.push(`[timed out after ${timeoutMs}ms]`);
|
|
262
|
-
// Exit last so a capped body cannot cut the one number D11 needs.
|
|
263
|
-
parts.push(`exit ${exit}`);
|
|
264
|
-
return parts.join("\n");
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
function stripTrailingNewline(s: string): string {
|
|
268
|
-
return s.endsWith("\n") ? s.slice(0, -1) : s;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
async function readCapped(
|
|
272
|
-
stream: ReadableStream<Uint8Array> | number | undefined | null,
|
|
273
|
-
max: number,
|
|
274
|
-
): Promise<{ text: string; truncated: boolean }> {
|
|
275
|
-
if (stream === null || stream === undefined || typeof stream === "number") {
|
|
276
|
-
return { text: "", truncated: false };
|
|
277
|
-
}
|
|
278
|
-
const reader = stream.getReader();
|
|
279
|
-
const chunks: Uint8Array[] = [];
|
|
280
|
-
let n = 0;
|
|
281
|
-
let truncated = false;
|
|
282
|
-
while (true) {
|
|
283
|
-
const { done, value } = await reader.read();
|
|
284
|
-
if (done) break;
|
|
285
|
-
if (value === undefined) continue;
|
|
286
|
-
if (n >= max) {
|
|
287
|
-
truncated = true;
|
|
288
|
-
continue;
|
|
289
|
-
}
|
|
290
|
-
const take = Math.min(value.length, max - n);
|
|
291
|
-
chunks.push(take === value.length ? value : value.subarray(0, take));
|
|
292
|
-
n += take;
|
|
293
|
-
if (take < value.length) truncated = true;
|
|
294
|
-
}
|
|
295
|
-
const buf = new Uint8Array(n);
|
|
296
|
-
let off = 0;
|
|
297
|
-
for (const c of chunks) {
|
|
298
|
-
buf.set(c, off);
|
|
299
|
-
off += c.length;
|
|
300
|
-
}
|
|
301
|
-
return { text: new TextDecoder().decode(buf), truncated };
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
/** True when the whole submission is `@` or `@query`, so the TUI can pick. */
|
|
305
|
-
export function bareAtQuery(text: string): string | null {
|
|
306
|
-
const m = /^@(\S*)$/.exec(text);
|
|
307
|
-
return m === null ? null : (m[1] ?? "");
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
export function isExactIndexedPath(index: FileIndex, query: string): boolean {
|
|
311
|
-
return query.length > 0 && index.paths.includes(query);
|
|
312
|
-
}
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import { modelKey } from "../core/session-position.ts";
|
|
2
|
-
import { asThinkingLevel, clampThinkingLevel, supportedThinkingLevels } from "../core/thinking.ts";
|
|
3
|
-
import { createProvider, providerSessionId } from "../runtime/catalog.ts";
|
|
4
|
-
import { scopedModels } from "../runtime/model-scope.ts";
|
|
5
|
-
import { storedIsOAuth, type Resolved } from "../runtime/auth.ts";
|
|
6
|
-
import { outputTokenBudget, type ModelSpec } from "../runtime/model.ts";
|
|
7
|
-
import { providerAccess, setProviderAccess } from "../runtime/provider-access.ts";
|
|
8
|
-
import { providerReplayKey } from "../runtime/model-replay.ts";
|
|
9
|
-
import type { Provider, ProviderConfig } from "../runtime/provider.ts";
|
|
10
|
-
import { retryHandler, type App } from "./startup.ts";
|
|
11
|
-
|
|
12
|
-
export interface ModelCatalogStage {
|
|
13
|
-
readonly models: readonly ModelSpec[];
|
|
14
|
-
readonly config: ProviderConfig;
|
|
15
|
-
readonly credential: Resolved;
|
|
16
|
-
readonly next?: Provider;
|
|
17
|
-
readonly needsCut: boolean;
|
|
18
|
-
readonly warning?: string;
|
|
19
|
-
}
|
|
20
|
-
export interface ModelCatalogSelection {
|
|
21
|
-
readonly model?: string;
|
|
22
|
-
readonly provider?: string;
|
|
23
|
-
readonly thinking?: string;
|
|
24
|
-
readonly requireCredential?: boolean;
|
|
25
|
-
readonly providerFactory?: typeof createProvider;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/** Resolve and construct locally; publication and model requests belong to the checkpoint owner. */
|
|
29
|
-
export function stageModelCatalog(app: App, models: readonly ModelSpec[], selection: ModelCatalogSelection = {}): ModelCatalogStage {
|
|
30
|
-
const previous = app.providerConfig.spec;
|
|
31
|
-
const requested = selection.model === undefined ? undefined : scopedModels([selection.model],
|
|
32
|
-
selection.provider ? models.filter(model => model.provider === selection.provider) : models, true).entries[0]?.model;
|
|
33
|
-
if (selection.model !== undefined && !requested) throw Error(`Unknown profile model: ${selection.provider ?? ""}/${selection.model}`);
|
|
34
|
-
const spec = requested ?? models.find(candidate => modelKey(candidate) === modelKey(previous));
|
|
35
|
-
if (!spec) {
|
|
36
|
-
const invalid = { ...previous }, warning = `${modelKey(previous)} is disabled or absent from the catalog; choose /model before sending`;
|
|
37
|
-
setProviderAccess(invalid, { invalid: warning });
|
|
38
|
-
return { models, config: { ...app.providerConfig, spec: invalid }, credential: { ok: false, reason: warning }, needsCut: false, warning };
|
|
39
|
-
}
|
|
40
|
-
const requestedThinking = selection.thinking ? asThinkingLevel(selection.thinking) : undefined;
|
|
41
|
-
if (selection.thinking && (!requestedThinking || !supportedThinkingLevels(spec).includes(requestedThinking))) throw Error(`thinking ${selection.thinking} is not supported by ${modelKey(spec)}`);
|
|
42
|
-
const effort = clampThinkingLevel(spec, requestedThinking ?? app.providerConfig.effort);
|
|
43
|
-
const unchanged = JSON.stringify([previous, providerAccess(previous)]) === JSON.stringify([spec, providerAccess(spec)]) && effort === app.providerConfig.effort;
|
|
44
|
-
const credential = selection.requireCredential || !unchanged ? app.credentialOf(spec) : app.credential;
|
|
45
|
-
if (selection.requireCredential && !credential.ok) throw Error(credential.reason);
|
|
46
|
-
if (unchanged) return { models, config: app.providerConfig, credential, needsCut: false };
|
|
47
|
-
const config = { ...app.providerConfig, spec, maxTokens: outputTokenBudget(spec), effort };
|
|
48
|
-
const next = (selection.providerFactory ?? createProvider)(config, credential.ok ? credential.key : "", retryHandler(app.io), {
|
|
49
|
-
sessionId: providerSessionId(app.session.config.logDir), effects: app.effects, oauth: !app.args.apiKey && storedIsOAuth(app.auth, spec.provider),
|
|
50
|
-
});
|
|
51
|
-
return { models, config, credential, next, needsCut: providerReplayKey(previous) !== providerReplayKey(spec) };
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/** The caller has committed any required checkpoint before publishing these process-local views. */
|
|
55
|
-
export function applyModelCatalog(app: App, stage: ModelCatalogStage): void {
|
|
56
|
-
if (stage.next) app.session.provider = stage.next;
|
|
57
|
-
app.providerConfig = stage.config; app.credential = stage.credential; app.catalog = stage.models;
|
|
58
|
-
if (stage.warning) app.io.say(app.io.palette.yellow(` ${stage.warning}`));
|
|
59
|
-
if (app.ui) app.ui.settings = { ...app.ui.settings, thinking: stage.config.effort };
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/** Publish the active profile with its directory after the shared staging path validates it. */
|
|
63
|
-
export async function publishModelCatalog(app: App, models: readonly ModelSpec[], checkpointed = false): Promise<boolean> {
|
|
64
|
-
if (app.session.running) throw Error("cannot reload model configuration during a turn");
|
|
65
|
-
const stage = stageModelCatalog(app, models);
|
|
66
|
-
if (stage.next && stage.needsCut && !checkpointed) await app.session.switchModel(stage.next);
|
|
67
|
-
applyModelCatalog(app, stage);
|
|
68
|
-
return stage.needsCut && !checkpointed;
|
|
69
|
-
}
|
package/src/apps/preferences.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
/** User intent owns personal defaults; CLI overrides and restored/extension state only own a session. */
|
|
2
|
-
import { mergeSettings, type Settings } from "../core/settings.ts";
|
|
3
|
-
import { writeUserSettings } from "../store/resources.ts";
|
|
4
|
-
import { describeError } from "./render.ts";
|
|
5
|
-
import type { App } from "./startup.ts";
|
|
6
|
-
|
|
7
|
-
export type PreferenceOrigin = "user" | "session";
|
|
8
|
-
|
|
9
|
-
export async function commitUserSettings(app: App, patch: Settings): Promise<boolean> {
|
|
10
|
-
const ui = app.ui;
|
|
11
|
-
if (!ui) return false;
|
|
12
|
-
ui.settings = mergeSettings({ builtin: ui.settings, user: patch });
|
|
13
|
-
try { await writeUserSettings(patch); }
|
|
14
|
-
catch (error) {
|
|
15
|
-
app.io.say(app.io.palette.yellow(` The change is active in this session, but could not save settings for the next startup: ${describeError(error)}`));
|
|
16
|
-
return false;
|
|
17
|
-
}
|
|
18
|
-
ui.userOverrides = mergeSettings({ builtin: ui.userOverrides, user: patch });
|
|
19
|
-
return true;
|
|
20
|
-
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
/** Project trust decisions; the picker itself is apps/ui/standalone.ts (startup) and apps/ui/app.ts (/trust). */
|
|
2
|
-
import { dirname } from "node:path";
|
|
3
|
-
import { saveTrust } from "../store/trust.ts";
|
|
4
|
-
|
|
5
|
-
export type TrustChoice = "once" | "here" | "parent" | "never";
|
|
6
|
-
export async function applyTrustChoice(workspace: string, choice: TrustChoice): Promise<boolean> {
|
|
7
|
-
if (choice !== "once") await saveTrust(choice === "parent" ? dirname(workspace) : workspace, choice !== "never");
|
|
8
|
-
return choice !== "never";
|
|
9
|
-
}
|