@visual-z/xeno 0.2.1 → 0.3.0
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 +106 -26
- package/docs/install.md +51 -13
- package/docs/providers.md +11 -0
- package/package.json +5 -4
- package/src/apps/approve.ts +3 -3
- package/src/apps/commands.ts +38 -5
- package/src/apps/completions.ts +73 -19
- package/src/apps/image-draft.ts +46 -0
- package/src/apps/project-trust.ts +2 -42
- package/src/apps/provider-connect.ts +346 -0
- package/src/apps/render.ts +22 -47
- package/src/apps/resources.ts +5 -2
- package/src/apps/shell.ts +298 -1526
- package/src/apps/startup-input.ts +45 -9
- package/src/apps/startup.ts +128 -86
- package/src/apps/ui/app.ts +1383 -0
- package/src/apps/ui/extension-host.ts +194 -0
- package/src/apps/ui/image-cache.ts +78 -0
- package/src/apps/ui/palette.ts +88 -0
- package/src/apps/ui/project.ts +127 -0
- package/src/apps/ui/standalone.ts +146 -0
- package/src/apps/ui/status-model.ts +68 -0
- package/src/apps/ui/surface.ts +84 -0
- package/src/apps/ui/theme.ts +86 -0
- package/src/core/acceptance.ts +107 -0
- package/src/core/action-defaults.ts +47 -295
- package/src/core/benchmark.ts +2 -2
- package/src/core/branch-receipt.ts +24 -0
- package/src/core/checkpoints.ts +64 -0
- package/src/core/churn.ts +2 -0
- package/src/core/command-artifacts.ts +94 -0
- package/src/core/command-contract.ts +35 -0
- package/src/core/completion-review.ts +21 -5
- package/src/core/context-detail.ts +37 -0
- package/src/core/context-policy.ts +74 -1
- package/src/core/continuation.ts +17 -2
- package/src/core/derive.ts +29 -3
- package/src/core/draft-images.ts +56 -0
- package/src/core/environment.ts +145 -0
- package/src/core/evaluation-coverage.ts +74 -0
- package/src/core/event-validation.ts +80 -5
- package/src/core/events.ts +97 -3
- package/src/core/evidence-links.ts +7 -0
- package/src/core/evidence.ts +13 -3
- package/src/core/execution.ts +8 -2
- package/src/core/experiment.ts +133 -0
- package/src/core/export.ts +9 -0
- package/src/core/failure-recovery.ts +173 -0
- package/src/core/file-precondition.ts +72 -0
- package/src/core/handoff.ts +65 -4
- package/src/core/history-query.ts +38 -2
- package/src/core/history.ts +38 -1
- package/src/core/integration-health.ts +34 -0
- package/src/core/keymap.ts +8 -20
- package/src/core/model-picker.ts +64 -0
- package/src/core/native-eval.ts +25 -0
- package/src/core/note-guidance.ts +34 -0
- package/src/core/observation-view.ts +49 -8
- package/src/core/permissions.ts +24 -2
- package/src/core/process-receipt.ts +105 -0
- package/src/core/process-state.ts +37 -0
- package/src/core/project.ts +45 -18
- package/src/core/prompt-policy.ts +37 -0
- package/src/core/query-eval-contract.ts +14 -0
- package/src/core/read-observation.ts +82 -0
- package/src/core/recovery-corpus.ts +17 -0
- package/src/core/recovery-evaluation.ts +60 -0
- package/src/core/recovery-sources.ts +74 -0
- package/src/core/runtime-target.ts +94 -0
- package/src/core/search-observation.ts +97 -0
- package/src/core/session-tree.ts +147 -0
- package/src/core/settings.ts +26 -10
- package/src/core/skills.ts +44 -0
- package/src/core/sql-query.ts +211 -0
- package/src/core/theme.ts +36 -58
- package/src/core/tool-contract.ts +51 -2
- package/src/core/tool-discovery.ts +87 -0
- package/src/core/tool-failure.ts +60 -0
- package/src/core/tool-mode-protocol.ts +25 -0
- package/src/core/transcript.ts +19 -4
- package/src/core/trial-lifecycle.ts +14 -0
- package/src/core/tui-mode.ts +29 -0
- package/src/core/update-check.ts +97 -0
- package/src/core/version.ts +55 -0
- package/src/core/work-state.ts +49 -3
- package/src/core/working-set.ts +91 -0
- package/src/runtime/auth.ts +2 -2
- package/src/runtime/branch-jobs.ts +36 -0
- package/src/runtime/clipboard.ts +15 -4
- package/src/runtime/command-artifacts.ts +64 -0
- package/src/runtime/environment-tools.ts +60 -0
- package/src/runtime/evidence-notes.ts +4 -2
- package/src/runtime/exec-sessions.ts +206 -61
- package/src/runtime/execution-control.ts +1 -0
- package/src/runtime/extension-ui.ts +22 -30
- package/src/runtime/extensions.ts +95 -16
- package/src/runtime/harness-comparison.ts +27 -7
- package/src/runtime/harness-eval.ts +460 -0
- package/src/runtime/history-cache.ts +57 -0
- package/src/runtime/history-tools.ts +62 -12
- package/src/runtime/integrations.ts +210 -0
- package/src/runtime/loop.ts +1021 -268
- package/src/runtime/model-discovery.ts +208 -0
- package/src/runtime/native-eval-fixtures.ts +33 -0
- package/src/runtime/native-eval.ts +143 -0
- package/src/runtime/oauth.ts +1 -1
- package/src/runtime/provider-connect.ts +109 -0
- package/src/runtime/provider-presets.ts +87 -0
- package/src/runtime/query-eval-verifier.ts +28 -0
- package/src/runtime/recovery-coverage.ts +71 -0
- package/src/runtime/recovery-evidence.ts +76 -0
- package/src/runtime/recovery-fixtures.ts +116 -0
- package/src/runtime/runtime-target.ts +149 -0
- package/src/runtime/tool-broker.ts +137 -0
- package/src/runtime/tool-discovery.ts +57 -0
- package/src/runtime/tool-mode-eval.ts +146 -0
- package/src/runtime/tool-queue.ts +77 -0
- package/src/runtime/tools.ts +628 -183
- package/src/runtime/trial-control.ts +45 -0
- package/src/runtime/update-check.ts +47 -0
- package/src/runtime/window-trial.ts +2 -1
- package/src/runtime/working-notes.ts +33 -3
- package/src/runtime/workspace-lifecycle.ts +97 -0
- package/src/store/acceptance.ts +15 -0
- package/src/store/checkpoint-history.ts +43 -0
- package/src/store/command-artifacts.ts +96 -0
- package/src/store/command.ts +65 -37
- package/src/store/file-checkpoints.ts +104 -0
- package/src/store/file-index.ts +13 -1
- package/src/store/git-checkpoints.ts +67 -0
- package/src/store/observations.ts +61 -20
- package/src/store/process-receipts.ts +34 -0
- package/src/store/query-source.ts +13 -8
- package/src/store/recovery.ts +2 -3
- package/src/store/replay.ts +16 -4
- package/src/store/runtime-target.ts +64 -0
- package/src/store/search.ts +171 -132
- package/src/store/session-archive.ts +4 -0
- package/src/store/source-cancellation.ts +6 -0
- package/src/store/update-cache.ts +36 -0
- package/src/store/workspace-observation.ts +113 -0
- package/src/tui/ansi-spans.ts +126 -0
- package/src/tui/style.ts +19 -16
- package/src/tui/ui/atomic-draft.ts +121 -0
- package/src/tui/ui/canvas.ts +96 -0
- package/src/tui/ui/colors.ts +70 -0
- package/src/tui/ui/composer.ts +199 -0
- package/src/tui/ui/dialog.ts +379 -0
- package/src/tui/ui/draft.ts +23 -0
- package/src/tui/ui/edge.ts +186 -0
- package/src/tui/ui/image-size.ts +40 -0
- package/src/tui/ui/ink.ts +210 -0
- package/src/tui/ui/items.ts +40 -0
- package/src/tui/ui/keys.ts +40 -0
- package/src/tui/ui/layout.ts +74 -0
- package/src/tui/ui/list-model.ts +86 -0
- package/src/tui/ui/list-view.ts +172 -0
- package/src/tui/ui/mascot.ts +67 -0
- package/src/tui/ui/messages.ts +518 -0
- package/src/tui/ui/motion.ts +28 -0
- package/src/tui/ui/paths.ts +10 -0
- package/src/tui/ui/pictures.ts +78 -0
- package/src/tui/ui/screen.ts +421 -0
- package/src/tui/ui/status.ts +271 -0
- package/src/tui/ui/styled.ts +85 -0
- package/src/tui/ui/testing.ts +5 -0
- package/src/tui/ui/tool-card.ts +150 -0
- package/src/apps/extension-ui.ts +0 -330
- package/src/apps/resource-browser.ts +0 -75
- package/src/apps/session-browser.ts +0 -113
- package/src/apps/session-picker.ts +0 -55
- package/src/tui/ansi.ts +0 -67
- package/src/tui/autocomplete.ts +0 -29
- package/src/tui/chat-viewport.ts +0 -47
- package/src/tui/complete.ts +0 -87
- package/src/tui/editor.ts +0 -940
- package/src/tui/host.ts +0 -264
- package/src/tui/input.ts +0 -75
- package/src/tui/keys.ts +0 -378
- package/src/tui/markdown.ts +0 -17
- package/src/tui/render.ts +0 -153
- package/src/tui/select.ts +0 -258
- package/src/tui/settings.ts +0 -44
- package/src/tui/statusline.ts +0 -162
- package/src/tui/system-theme.ts +0 -700
- package/src/tui/terminal.ts +0 -30
- package/src/tui/text.ts +0 -52
- package/src/tui/toolview.ts +0 -386
- package/src/tui/transcript.ts +0 -123
- package/src/tui/tree.ts +0 -471
- package/src/vendor/pi-tui/LICENSE.upstream +0 -21
- package/src/vendor/pi-tui/UPSTREAM.json +0 -50
- package/src/vendor/pi-tui/VENDOR.md +0 -25
- package/src/vendor/pi-tui/alt-screen-search.ts +0 -327
- package/src/vendor/pi-tui/autocomplete.ts +0 -860
- package/src/vendor/pi-tui/colors.ts +0 -367
- package/src/vendor/pi-tui/components/alt-screen-flash.ts +0 -51
- package/src/vendor/pi-tui/components/box.ts +0 -166
- package/src/vendor/pi-tui/components/cancellable-loader.ts +0 -40
- package/src/vendor/pi-tui/components/editor.ts +0 -2472
- package/src/vendor/pi-tui/components/h-stack.ts +0 -44
- package/src/vendor/pi-tui/components/image.ts +0 -127
- package/src/vendor/pi-tui/components/input.ts +0 -494
- package/src/vendor/pi-tui/components/loader.ts +0 -101
- package/src/vendor/pi-tui/components/markdown.ts +0 -1021
- package/src/vendor/pi-tui/components/mouse-region.ts +0 -33
- package/src/vendor/pi-tui/components/scroll-view.ts +0 -224
- package/src/vendor/pi-tui/components/select-list.ts +0 -273
- package/src/vendor/pi-tui/components/settings-list.ts +0 -328
- package/src/vendor/pi-tui/components/spacer.ts +0 -28
- package/src/vendor/pi-tui/components/stack.ts +0 -154
- package/src/vendor/pi-tui/components/text.ts +0 -107
- package/src/vendor/pi-tui/components/truncated-text.ts +0 -65
- package/src/vendor/pi-tui/components/v-stack.ts +0 -33
- package/src/vendor/pi-tui/editor-component.ts +0 -74
- package/src/vendor/pi-tui/fuzzy.ts +0 -138
- package/src/vendor/pi-tui/index.ts +0 -186
- package/src/vendor/pi-tui/keybindings.ts +0 -320
- package/src/vendor/pi-tui/keys.ts +0 -1401
- package/src/vendor/pi-tui/kill-ring.ts +0 -46
- package/src/vendor/pi-tui/latex.ts +0 -1506
- package/src/vendor/pi-tui/layout-node.ts +0 -51
- package/src/vendor/pi-tui/layout.ts +0 -449
- package/src/vendor/pi-tui/native/UPSTREAM.json +0 -23
- package/src/vendor/pi-tui/native/clipboard.h +0 -208
- package/src/vendor/pi-tui/native/darwin/README.md +0 -22
- package/src/vendor/pi-tui/native/darwin/build.sh +0 -69
- package/src/vendor/pi-tui/native/darwin/prebuilds/darwin-arm64/darwin-platform.node +0 -0
- package/src/vendor/pi-tui/native/darwin/prebuilds/darwin-x64/darwin-platform.node +0 -0
- package/src/vendor/pi-tui/native/darwin/src/darwin-platform.m +0 -132
- package/src/vendor/pi-tui/native/linux/README.md +0 -23
- package/src/vendor/pi-tui/native/linux/build.sh +0 -65
- package/src/vendor/pi-tui/native/linux/prebuilds/linux-arm64/linux-platform-x11.node +0 -0
- package/src/vendor/pi-tui/native/linux/prebuilds/linux-x64/linux-platform-x11.node +0 -0
- package/src/vendor/pi-tui/native/linux/src/clipboard-worker.h +0 -70
- package/src/vendor/pi-tui/native/linux/src/linux-platform-x11.c +0 -274
- package/src/vendor/pi-tui/native/napi.h +0 -91
- package/src/vendor/pi-tui/native/win32/README.md +0 -31
- package/src/vendor/pi-tui/native/win32/build.mjs +0 -232
- package/src/vendor/pi-tui/native/win32/prebuilds/win32-arm64/win32-platform.node +0 -0
- package/src/vendor/pi-tui/native/win32/prebuilds/win32-x64/win32-platform.node +0 -0
- package/src/vendor/pi-tui/native/win32/src/win32-platform.c +0 -241
- package/src/vendor/pi-tui/native-modifiers.ts +0 -13
- package/src/vendor/pi-tui/native-module-path.ts +0 -31
- package/src/vendor/pi-tui/native-platform.ts +0 -65
- package/src/vendor/pi-tui/oklab.ts +0 -233
- package/src/vendor/pi-tui/stdin-buffer.ts +0 -444
- package/src/vendor/pi-tui/terminal-colors.ts +0 -91
- package/src/vendor/pi-tui/terminal-image.ts +0 -731
- package/src/vendor/pi-tui/terminal.ts +0 -554
- package/src/vendor/pi-tui/tui-alt-screen.ts +0 -1750
- package/src/vendor/pi-tui/tui-main-screen.ts +0 -655
- package/src/vendor/pi-tui/tui.ts +0 -1493
- package/src/vendor/pi-tui/undo-stack.ts +0 -28
- package/src/vendor/pi-tui/utils.ts +0 -1385
- package/src/vendor/pi-tui/wheel-scroll.ts +0 -82
- package/src/vendor/pi-tui/word-navigation.ts +0 -117
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Startup owns raw input until the renderer can (D20). Keystrokes typed while
|
|
3
|
+
* configuration loads are held as original bytes and handed to the next owner;
|
|
4
|
+
* only Ctrl+C outside a bracketed paste is interpreted here.
|
|
5
|
+
*/
|
|
5
6
|
|
|
6
7
|
type StartupStdin = Pick<NodeJS.ReadStream, "isTTY" | "isRaw" | "setRawMode" | "setEncoding" | "on" | "off" | "resume" | "pause" | "unshift">;
|
|
7
8
|
export interface StartupInput {
|
|
@@ -31,7 +32,9 @@ export function createStartupInput(opts: {
|
|
|
31
32
|
let active = false, closed = false, announced = false;
|
|
32
33
|
let phase = "Reading configuration";
|
|
33
34
|
let pending: string[] = [];
|
|
34
|
-
let
|
|
35
|
+
let pasting = false;
|
|
36
|
+
/** An escape sequence cut across chunks; joined with the next chunk before matching. */
|
|
37
|
+
let partial = "";
|
|
35
38
|
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
36
39
|
|
|
37
40
|
const release = () => {
|
|
@@ -41,7 +44,7 @@ export function createStartupInput(opts: {
|
|
|
41
44
|
stdin.pause();
|
|
42
45
|
stdin.off("data", receive);
|
|
43
46
|
signals.off("SIGINT", interrupt); signals.off("SIGTERM", terminate); signals.off("SIGHUP", hangup);
|
|
44
|
-
|
|
47
|
+
pasting = false; partial = "";
|
|
45
48
|
stdin.setRawMode(wasRaw);
|
|
46
49
|
};
|
|
47
50
|
const restore = () => { closed = true; release(); pending = []; };
|
|
@@ -56,14 +59,17 @@ export function createStartupInput(opts: {
|
|
|
56
59
|
// Keep original chunks, including partial escapes/pastes, for Pi's own decoder.
|
|
57
60
|
// The temporary parser only recognizes cancellation outside bracketed paste.
|
|
58
61
|
pending.push(data);
|
|
59
|
-
|
|
62
|
+
const joined = partial + data;
|
|
63
|
+
const cut = trailingPartialEscape(joined);
|
|
64
|
+
const whole = joined.slice(0, joined.length - cut.length);
|
|
65
|
+
partial = cut;
|
|
66
|
+
if (interruptsStartup(whole, pasting)) interrupt();
|
|
67
|
+
pasting = pasteState(whole, pasting);
|
|
60
68
|
};
|
|
61
69
|
const announce = () => { if (active) { announced = true; report(` Starting xeno: ${phase} (Ctrl+C to cancel)`); } };
|
|
62
70
|
const capture = () => {
|
|
63
71
|
if (closed || active) return;
|
|
64
72
|
active = true; announced = false;
|
|
65
|
-
parser = new StdinBuffer({ escapeTimeout: resolveEscapeTimeoutMs() });
|
|
66
|
-
parser.on("data", (data: string) => { if (!isKeyRelease(data) && matchesKey(data, "ctrl+c")) interrupt(); });
|
|
67
73
|
stdin.setEncoding("utf8");
|
|
68
74
|
stdin.on("data", receive);
|
|
69
75
|
signals.on("SIGINT", interrupt); signals.on("SIGTERM", terminate); signals.on("SIGHUP", hangup);
|
|
@@ -92,3 +98,33 @@ export function createStartupInput(opts: {
|
|
|
92
98
|
},
|
|
93
99
|
};
|
|
94
100
|
}
|
|
101
|
+
|
|
102
|
+
/** Bracketed paste state after a chunk: a paste that started and did not end yet. */
|
|
103
|
+
export function pasteState(data: string, pasting: boolean): boolean {
|
|
104
|
+
const start = data.lastIndexOf("\x1b[200~"), end = data.lastIndexOf("\x1b[201~");
|
|
105
|
+
if (start < 0 && end < 0) return pasting;
|
|
106
|
+
return start > end;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** Ctrl+C as legacy ETX or kitty CSI-u (99;5u press, not a :3 release), outside a paste. */
|
|
110
|
+
export function interruptsStartup(data: string, pasting: boolean): boolean {
|
|
111
|
+
let outside = "";
|
|
112
|
+
let inPaste = pasting;
|
|
113
|
+
for (let i = 0; i < data.length;) {
|
|
114
|
+
if (data.startsWith("\x1b[200~", i)) { inPaste = true; i += 6; continue; }
|
|
115
|
+
if (data.startsWith("\x1b[201~", i)) { inPaste = false; i += 6; continue; }
|
|
116
|
+
if (!inPaste) outside += data[i];
|
|
117
|
+
i++;
|
|
118
|
+
}
|
|
119
|
+
return outside.includes("\x03") || /\x1b\[99;5(?::1)?u/.test(outside);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** The unfinished CSI/SS3 at the end of a chunk ("\x1b[99;" waiting for "5u"), or "". */
|
|
123
|
+
export function trailingPartialEscape(data: string): string {
|
|
124
|
+
const at = data.lastIndexOf("\x1b");
|
|
125
|
+
if (at < 0) return "";
|
|
126
|
+
const tail = data.slice(at);
|
|
127
|
+
if (tail === "\x1b") return tail;
|
|
128
|
+
if (/^\x1b\[[0-9;:?]*$/.test(tail) || /^\x1bO$/.test(tail)) return tail;
|
|
129
|
+
return "";
|
|
130
|
+
}
|
package/src/apps/startup.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { release } from "node:os";
|
|
2
|
-
import {
|
|
2
|
+
import { codingWorkingProtocol } from "../core/continuation.ts";
|
|
3
|
+
import { taskGuidance } from "../core/prompt-policy.ts";
|
|
3
4
|
import { environmentSection, type SessionEnvironment } from "../core/environment.ts";
|
|
4
5
|
/**
|
|
5
6
|
* From argv to a session that can run. Credential order, model selection,
|
|
@@ -14,6 +15,8 @@ import { Session, systemClock } from "../runtime/loop.ts";
|
|
|
14
15
|
import { DEFAULT_PROVIDER, type ProviderConfig } from "../runtime/provider.ts";
|
|
15
16
|
import { createProvider, selectModel, loadCatalog, groupedModels, parseUserCatalog, type CatalogFetch } from "../runtime/catalog.ts";
|
|
16
17
|
import { ExtensionRegistry } from "../runtime/extensions.ts";
|
|
18
|
+
import { DISCOVERY_TOOL_NAME } from "../runtime/tool-discovery.ts";
|
|
19
|
+
import { declaredToolNames } from "../core/tool-discovery.ts";
|
|
17
20
|
import { CATALOG, outputTokenBudget, type ModelSpec } from "../runtime/model.ts";
|
|
18
21
|
import { asThinkingLevel, clampThinkingLevel, supportedThinkingLevels } from "../core/thinking.ts";
|
|
19
22
|
import { modelKey } from "../core/session-position.ts";
|
|
@@ -25,10 +28,11 @@ import {
|
|
|
25
28
|
import { authPath, configDir, readJsonFile } from "../store/config.ts";
|
|
26
29
|
import { loadUiResources, loadTextResources, type UiResources } from "../store/resources.ts";
|
|
27
30
|
import { discoverResources, type ResourceIndex } from "../store/resource-index.ts";
|
|
28
|
-
import { runResourceConfig } from "./
|
|
31
|
+
import { pickProjectTrust, pickStartupSession, runResourceConfig } from "./ui/standalone.ts";
|
|
29
32
|
import { PackageManager, type PackageCommand } from "../runtime/package-manager.ts";
|
|
30
|
-
import {
|
|
31
|
-
import {
|
|
33
|
+
import { loadAcceptanceContract } from "../store/acceptance.ts";
|
|
34
|
+
import type { AcceptanceContract } from "../core/acceptance.ts";
|
|
35
|
+
import { appendSkillsToSystem, type Skill } from "../core/skills.ts";
|
|
32
36
|
import { COMMANDS, resolveSlashCatalog, type SlashCatalog } from "./commands.ts";
|
|
33
37
|
import { createStyle, IDENTITY, type Style } from "../tui/style.ts";
|
|
34
38
|
import type { Settings } from "../core/settings.ts";
|
|
@@ -38,23 +42,24 @@ import { readCursor, writeCursor, type Cursor } from "../store/log.ts";
|
|
|
38
42
|
import { resolveLogDir, findSessionByPrefix, listSessions, sessionsDir, type ResolveLogOpts } from "../store/sessions.ts";
|
|
39
43
|
import { recoverCut } from "../store/recovery.ts";
|
|
40
44
|
import { planSessionEntry, restoreSessionModel } from "../core/session-entry.ts";
|
|
41
|
-
import { pickStartupSession } from "./session-picker.ts";
|
|
42
45
|
import { loadContext, assembleSystemPrompt, formatNotice, type LoadedContext } from "../store/context-files.ts";
|
|
43
46
|
import {
|
|
44
47
|
projectTrust,
|
|
45
48
|
} from "../store/trust.ts";
|
|
46
49
|
import { stat } from "node:fs/promises";
|
|
47
50
|
import {
|
|
48
|
-
parseToolList, rulesWithAllowTools, selectTools,
|
|
51
|
+
parseToolList, rulesWithAllowTools, selectTools, isToolProfile,
|
|
52
|
+
type ToolProfile,
|
|
49
53
|
type PermissionRequest, type PermissionRule,
|
|
50
54
|
} from "../core/permissions.ts";
|
|
51
55
|
import {
|
|
52
|
-
TOOL_SCHEMAS, DEFAULT_TOOL_CONTEXT, workspaceRoot,
|
|
56
|
+
TOOL_SCHEMAS, TOOL_DEFINITIONS, modelToolSchema, DEFAULT_TOOL_CONTEXT, workspaceRoot,
|
|
53
57
|
type ApprovalChoice, type ToolContext,
|
|
54
58
|
} from "../runtime/tools.ts";
|
|
55
|
-
import {
|
|
56
|
-
import {
|
|
57
|
-
|
|
59
|
+
import { assertNever } from "../core/exhaustive.ts";
|
|
60
|
+
import {
|
|
61
|
+
contextLimits, DEFAULT_CONTEXT_POLICY, isContextPolicy, type ContextLimits, type ContextPolicy,
|
|
62
|
+
} from "../core/context-policy.ts";
|
|
58
63
|
import { colorEnabled, createPalette, describeError, stdinIsTty, stdoutIsTty, type Palette } from "./render.ts";
|
|
59
64
|
import { type ActionId, type Keymap } from "../core/keymap.ts";
|
|
60
65
|
import type { StartupInput } from "./startup-input.ts";
|
|
@@ -62,46 +67,16 @@ import type { StartupInput } from "./startup-input.ts";
|
|
|
62
67
|
export const SYSTEM_PROMPT = systemPromptFor(TOOL_SCHEMAS.map((tool) => tool.name));
|
|
63
68
|
|
|
64
69
|
/**
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
* will never work. The default prompt is left alone when every tool is visible.
|
|
70
|
+
* Tool schemas are the only capability inventory. A branch may inherit this
|
|
71
|
+
* stable policy while narrowing its tools; no parent tool recipe can leak.
|
|
68
72
|
*/
|
|
69
73
|
export function systemPromptFor(toolNames: readonly string[], env?: SessionEnvironment): string {
|
|
70
|
-
const has = (name: string) => toolNames.includes(name);
|
|
71
|
-
const toolsLine = toolNames.length === 0
|
|
72
|
-
? "No tools are available in this session."
|
|
73
|
-
: `Tools: ${toolNames.join(", ")}.` +
|
|
74
|
-
(toolNames.some(name => ["grep", "find", "ls"].includes(name)) ? " Use the available grep/find/ls tools for confined search." : "") +
|
|
75
|
-
(has("bash")
|
|
76
|
-
? " Use bash for verification commands and operations the other tools do not cover."
|
|
77
|
-
: "");
|
|
78
|
-
|
|
79
|
-
// Each line answers a failure class in the Terminal-Bench taxonomy
|
|
80
|
-
// (arXiv 2601.11868): premature termination, no verification, step
|
|
81
|
-
// repetition. The wording of the first and last follows the Codex
|
|
82
|
-
// prompting guide: persist end to end, and end on a change or a blocker.
|
|
83
|
-
const work = [
|
|
84
|
-
"How to work:",
|
|
85
|
-
"- Keep going until the request is fully resolved. When a reasonable assumption lets you proceed, make it and mention it at the end; stop to ask only when blocked on a decision only the user can make.",
|
|
86
|
-
"- Understand before changing: read the relevant code, and for a bug, reproduce it first.",
|
|
87
|
-
"- Put independent reads and searches in one response; they run in parallel.",
|
|
88
|
-
"- After a change, run the project's own tests, build or type check. When a command fails, read the end of its output first; errors and summaries are printed last.",
|
|
89
|
-
"- If the same approach fails twice, change the approach instead of retrying it.",
|
|
90
|
-
"- A claim without a passing command is not evidence. Finish with what changed and the command that verified it, or the specific blocker.",
|
|
91
|
-
];
|
|
92
|
-
if (has("bash")) work.push(
|
|
93
|
-
"- Each bash call is a fresh non-interactive shell with no TTY: use workdir or `cd dir && ...` within one call. Never open pagers or interactive editors. " +
|
|
94
|
-
"Start servers in the background with output redirected (`nohup cmd > app.log 2>&1 &`), then poll the log or port. Pass a larger timeout for long builds and installs.",
|
|
95
|
-
);
|
|
96
|
-
if (has("exec_command")) work.push("- For servers, REPLs and programs that prompt for input, use exec_command (tty=true when the program needs a terminal) and continue with write_stdin.");
|
|
97
|
-
if (has("edit")) work.push("- Prefer edit to rewriting a whole file; copy old_string from read output without the line numbers.");
|
|
98
|
-
|
|
99
74
|
return [
|
|
100
|
-
"You are
|
|
101
|
-
|
|
75
|
+
"You are an agent working in a terminal.",
|
|
76
|
+
"The declared tool schemas define available capabilities, input units and execution lifecycles. Do not assume additional tools or infer permission from a tool's visibility.",
|
|
102
77
|
...(env === undefined ? [] : [environmentSection(env)]),
|
|
103
|
-
|
|
104
|
-
|
|
78
|
+
taskGuidance(),
|
|
79
|
+
codingWorkingProtocol(toolNames),
|
|
105
80
|
"Be concise. Do not narrate what you are about to do; do it, then say what happened.",
|
|
106
81
|
].join("\n\n");
|
|
107
82
|
}
|
|
@@ -128,12 +103,12 @@ export function sessionEnvironment(workspace: string): SessionEnvironment {
|
|
|
128
103
|
* Session policy a CLI coding session differs from a benchmark run in.
|
|
129
104
|
* Benchmarks construct their own SessionConfig and keep the core defaults.
|
|
130
105
|
*/
|
|
131
|
-
export function cliSessionConfig(args: Args): { continuationAfter: number; timeBudgetMs?: number } {
|
|
106
|
+
export function cliSessionConfig(args: Args): { continuationAfter: number; workingSet: true; workingSetVersion: 2; guidanceVersion: 1; strictVerification: true; timeBudgetMs?: number } {
|
|
132
107
|
// Never within a request. A real Kimi K3 run with the reminder at reply six
|
|
133
108
|
// spent half its requests on notes after the work was already verified
|
|
134
109
|
// (fix-cli-continuation-noise). The reminder still fires where it earns its
|
|
135
110
|
// place: a resumed handoff that cannot continue, and boundary preparation.
|
|
136
|
-
return { continuationAfter: Infinity, ...(args.timeBudgetMs === undefined ? {} : { timeBudgetMs: args.timeBudgetMs }) };
|
|
111
|
+
return { continuationAfter: Infinity, workingSet: true, workingSetVersion: 2, guidanceVersion: 1, strictVerification: true, ...(args.timeBudgetMs === undefined ? {} : { timeBudgetMs: args.timeBudgetMs }) };
|
|
137
112
|
}
|
|
138
113
|
|
|
139
114
|
/**
|
|
@@ -151,6 +126,7 @@ export type AskSlot = {
|
|
|
151
126
|
};
|
|
152
127
|
|
|
153
128
|
export interface Args {
|
|
129
|
+
shellCheckpoints?: boolean;
|
|
154
130
|
workspace: string;
|
|
155
131
|
/**
|
|
156
132
|
* `--state-dir <dir>`: where every xeno write goes -- snapshots, tool
|
|
@@ -193,6 +169,9 @@ export interface Args {
|
|
|
193
169
|
systemPrompt: string | null;
|
|
194
170
|
/** `--tools` allowlist. Omitted when the flag was not passed. */
|
|
195
171
|
tools?: string[];
|
|
172
|
+
/** New sessions default to coding; restored context keeps its frozen schemas. */
|
|
173
|
+
toolProfile?: ToolProfile;
|
|
174
|
+
acceptanceFile?: string;
|
|
196
175
|
excludeTools: string[];
|
|
197
176
|
noTools: boolean;
|
|
198
177
|
/** `--no-builtin-tools`. False when the flag was not passed. */
|
|
@@ -258,6 +237,7 @@ export interface Args {
|
|
|
258
237
|
* parses; it just cannot be offered as a suggestion.
|
|
259
238
|
*/
|
|
260
239
|
const KNOWN_FLAGS: readonly string[] = [
|
|
240
|
+
"--checkpoint-shell",
|
|
261
241
|
"--workspace", "-w",
|
|
262
242
|
"--path-root",
|
|
263
243
|
"--state-dir",
|
|
@@ -283,6 +263,8 @@ const KNOWN_FLAGS: readonly string[] = [
|
|
|
283
263
|
"--system-prompt",
|
|
284
264
|
"--append-system-prompt",
|
|
285
265
|
"--tools", "-t",
|
|
266
|
+
"--tool-profile",
|
|
267
|
+
"--acceptance-file",
|
|
286
268
|
"--exclude-tools", "-xt",
|
|
287
269
|
"--no-tools", "-nt",
|
|
288
270
|
"--no-builtin-tools",
|
|
@@ -374,7 +356,8 @@ export function parseArgs(argv: string[], options: { deferExtensions?: boolean;
|
|
|
374
356
|
console.error(`${a} requires a value`);
|
|
375
357
|
process.exit(1);
|
|
376
358
|
}
|
|
377
|
-
if (a === "--
|
|
359
|
+
if (a === "--checkpoint-shell") args.shellCheckpoints = true;
|
|
360
|
+
else if (a === "--workspace" || a === "-w") args.workspace = argv[++i] ?? args.workspace;
|
|
378
361
|
else if (a === "--path-root") args.pathRoot = resolve(argv[++i] ?? "/");
|
|
379
362
|
else if (a === "--state-dir") args.stateDir = resolve(argv[++i] ?? ".");
|
|
380
363
|
else if (a === "--time-budget") {
|
|
@@ -429,6 +412,15 @@ export function parseArgs(argv: string[], options: { deferExtensions?: boolean;
|
|
|
429
412
|
else if (a === "--system-prompt") args.systemPrompt = argv[++i] ?? "";
|
|
430
413
|
else if (a === "--append-system-prompt") args.appendSystemPrompt.push(argv[++i] ?? "");
|
|
431
414
|
else if (a === "--tools" || a === "-t") args.tools = parseToolList(argv[++i] ?? "");
|
|
415
|
+
else if (a === "--acceptance-file") {
|
|
416
|
+
const path = argv[++i]; if (!path || path.startsWith("--")) throw new Error("--acceptance-file requires a JSON path");
|
|
417
|
+
args.acceptanceFile = path;
|
|
418
|
+
}
|
|
419
|
+
else if (a === "--tool-profile") {
|
|
420
|
+
const value = argv[++i] ?? "";
|
|
421
|
+
if (!isToolProfile(value)) throw new Error("unknown tool profile; choose coding, readonly, research, data or full");
|
|
422
|
+
args.toolProfile = value;
|
|
423
|
+
}
|
|
432
424
|
else if (a === "--exclude-tools" || a === "-xt") args.excludeTools = parseToolList(argv[++i] ?? "");
|
|
433
425
|
else if (a === "--no-tools" || a === "-nt") args.noTools = true;
|
|
434
426
|
else if (a === "--no-builtin-tools" || a === "-nbt") args.noBuiltinTools = true;
|
|
@@ -598,16 +590,17 @@ export function formatStartupHeader(input: {
|
|
|
598
590
|
`workspace ${input.workspace}`,
|
|
599
591
|
`session ${input.session}`,
|
|
600
592
|
];
|
|
601
|
-
const
|
|
602
|
-
|
|
603
|
-
|
|
593
|
+
const groups: [string, readonly string[]][] = [["agents", input.agents], ["skills", input.skills], ["prompts", input.prompts],
|
|
594
|
+
...(input.tools !== undefined ? [["tools", input.tools] as [string, readonly string[]]] : [])];
|
|
595
|
+
if (input.verbose) {
|
|
596
|
+
for (const [label, items] of groups) {
|
|
597
|
+
lines.push(`${label.padEnd(10)}${items.length}`);
|
|
604
598
|
for (const item of items) lines.push(` ${item}`);
|
|
605
599
|
}
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
if (input.tools !== undefined) pushCount("tools", input.tools);
|
|
600
|
+
} else {
|
|
601
|
+
// Four count rows were a third of the banner; one row says the same.
|
|
602
|
+
lines.push(`loaded ${groups.map(([label, items]) => `${items.length} ${label}`).join(" · ")}`);
|
|
603
|
+
}
|
|
611
604
|
const keys = formatShortcutLine(input.keymap);
|
|
612
605
|
if (keys.length > 0) lines.push(`keys ${keys}`);
|
|
613
606
|
return lines;
|
|
@@ -810,13 +803,14 @@ export function describeUnauthenticatedScript(catalog: readonly ModelSpec[] = CA
|
|
|
810
803
|
);
|
|
811
804
|
}
|
|
812
805
|
|
|
813
|
-
/**
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
806
|
+
/**
|
|
807
|
+
* Banner / hint for an interactive session that started without a key.
|
|
808
|
+
* Listing every provider's variable was a 20-line wall that buried the one
|
|
809
|
+
* action that works from here; the interactive routes are named instead.
|
|
810
|
+
*/
|
|
811
|
+
export function describeUnauthenticatedSession(_catalog: readonly ModelSpec[] = CATALOG): string {
|
|
812
|
+
return "no provider connected yet. Type /login to connect one — a Claude, ChatGPT or Copilot subscription, an API key, a gateway or a local server — " +
|
|
813
|
+
`or export a key such as ${["anthropic", "openai", "google", "deepseek"].map(provider => envLabel(provider).split(" or ")[0]).join(", ")}.`;
|
|
820
814
|
}
|
|
821
815
|
|
|
822
816
|
export function retryHandler(io: AppIO): (attempt: number, delayMs: number, error: unknown) => void {
|
|
@@ -833,6 +827,7 @@ export function retryHandler(io: AppIO): (attempt: number, delayMs: number, erro
|
|
|
833
827
|
|
|
834
828
|
export function toolSelectionOf(args: Args) {
|
|
835
829
|
return {
|
|
830
|
+
profile: args.toolProfile ?? "coding",
|
|
836
831
|
...(args.noTools ? { none: true as const } : {}),
|
|
837
832
|
...(args.tools !== undefined ? { only: args.tools } : {}),
|
|
838
833
|
...(args.excludeTools.length > 0 ? { exclude: args.excludeTools } : {}),
|
|
@@ -843,25 +838,40 @@ export function toolSelectionOf(args: Args) {
|
|
|
843
838
|
/**
|
|
844
839
|
* Schema assembly: every tool is tagged with a source here so
|
|
845
840
|
* `--no-builtin-tools` can drop a category without a later cross-layer
|
|
846
|
-
* recut.
|
|
847
|
-
*
|
|
841
|
+
* recut. Optional extension descriptors remain local until selected; their
|
|
842
|
+
* harness-owned discovery entry belongs to that extension-enabled capability set.
|
|
848
843
|
*
|
|
849
844
|
* `source` is stripped before the frozen prefix: the wire schema is what
|
|
850
845
|
* the provider bills, and an extra field there would look like a tool
|
|
851
846
|
* argument.
|
|
852
847
|
*/
|
|
853
|
-
export function
|
|
848
|
+
export function sessionToolCatalog(args: Args, extensions?: ExtensionRegistry, previous: readonly unknown[] = []) {
|
|
854
849
|
const custom = [...extensions?.definitions().values() ?? []].map(({ name, description, input_schema }) => ({ name, description, input_schema, source: "extension" as const }));
|
|
855
|
-
const available = [...
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
850
|
+
const available = [...TOOL_DEFINITIONS.filter(tool => !extensions?.tools.has(tool.name) && (!tool.optional || args.tools?.includes(tool.name))).map((t) => ({ ...t, source: "builtin" as const })), ...custom];
|
|
851
|
+
const selected = selectTools(available, toolSelectionOf(args));
|
|
852
|
+
const deferred = selected.filter(tool => tool.source === "extension" && extensions?.tools.get(tool.name)?.exposure === "deferred")
|
|
853
|
+
.map(({ name, description, input_schema }) => ({ name, description, input_schema }));
|
|
854
|
+
const deferredNames = new Set(deferred.map(tool => tool.name));
|
|
855
|
+
const active = new Set([...declaredToolNames(previous), ...args.tools ?? []]);
|
|
856
|
+
const visible = selected.filter(tool => (!deferredNames.has(tool.name) || active.has(tool.name)) &&
|
|
857
|
+
(tool.name !== DISCOVERY_TOOL_NAME || deferred.length > 0 || args.tools?.includes(DISCOVERY_TOOL_NAME)));
|
|
858
|
+
// A cold open must not reorder an already accepted declaration and invent a checkpoint.
|
|
859
|
+
const order = new Map(declaredToolNames(previous).map((name, index) => [name, index]));
|
|
860
|
+
visible.sort((a, b) => (order.get(a.name) ?? Infinity) - (order.get(b.name) ?? Infinity));
|
|
861
|
+
const builtins = visible.filter(tool => tool.source === "builtin").map(tool => tool.name);
|
|
862
|
+
const schemas = visible.map(tool => tool.source === "builtin" ? modelToolSchema(tool, builtins)
|
|
863
|
+
: { name: tool.name, description: tool.description, input_schema: tool.input_schema });
|
|
864
|
+
// D6: extension authors retain their registration objects and SDK catalog views.
|
|
865
|
+
// Neither may mutate the already frozen provider declaration through an alias.
|
|
866
|
+
return { schemas: JSON.parse(JSON.stringify(schemas)) as typeof schemas,
|
|
867
|
+
deferred: JSON.parse(JSON.stringify(deferred)) as typeof deferred };
|
|
868
|
+
}
|
|
869
|
+
export function visibleSchemas(args: Args, extensions?: ExtensionRegistry, previous: readonly unknown[] = []) {
|
|
870
|
+
return sessionToolCatalog(args, extensions, previous).schemas;
|
|
861
871
|
}
|
|
862
872
|
|
|
863
873
|
export function unknownToolNames(names: readonly string[]): string[] {
|
|
864
|
-
const known = new Set(
|
|
874
|
+
const known = new Set(TOOL_DEFINITIONS.map((t) => t.name));
|
|
865
875
|
return names.filter((n) => !known.has(n));
|
|
866
876
|
}
|
|
867
877
|
|
|
@@ -878,9 +888,11 @@ export function createSessionFor(opts: {
|
|
|
878
888
|
oauth?: boolean;
|
|
879
889
|
effects?: ResolveEffects;
|
|
880
890
|
extensions?: ExtensionRegistry;
|
|
891
|
+
acceptanceContract?: AcceptanceContract;
|
|
892
|
+
skills?: readonly Skill[];
|
|
881
893
|
}): Session {
|
|
882
894
|
const { args, workspace, logDir, blobDir, system, providerConfig, credential, io, askSlot } = opts;
|
|
883
|
-
const schemas =
|
|
895
|
+
const { schemas, deferred } = sessionToolCatalog(args, opts.extensions);
|
|
884
896
|
const names = schemas.map((t) => t.name);
|
|
885
897
|
const sessionRules: PermissionRule[] = [];
|
|
886
898
|
const tools: ToolContext = {
|
|
@@ -888,6 +900,7 @@ export function createSessionFor(opts: {
|
|
|
888
900
|
...DEFAULT_TOOL_CONTEXT,
|
|
889
901
|
...(args.pathRoot === undefined ? {} : { pathRoot: args.pathRoot }),
|
|
890
902
|
allow: names,
|
|
903
|
+
deferredTools: deferred,
|
|
891
904
|
rules: rulesWithAllowTools(args.allowTools),
|
|
892
905
|
sessionRules,
|
|
893
906
|
askSlot,
|
|
@@ -898,9 +911,12 @@ export function createSessionFor(opts: {
|
|
|
898
911
|
logDir,
|
|
899
912
|
frozen: { system, tools: schemas },
|
|
900
913
|
blobDir,
|
|
914
|
+
shellCheckpoints: args.shellCheckpoints === true,
|
|
901
915
|
tools,
|
|
902
916
|
maxTurns: args.maxTurns,
|
|
903
917
|
...cliSessionConfig(args),
|
|
918
|
+
...(opts.skills ? { skills: opts.skills } : {}),
|
|
919
|
+
...(opts.acceptanceContract ? { acceptanceContract: opts.acceptanceContract } : {}),
|
|
904
920
|
// Explicit legacy thresholds still select that experiment unless overridden.
|
|
905
921
|
contextPolicy: args.contextPolicy ?? (args.trigger > 0 ? "segmented" : DEFAULT_CONTEXT_POLICY),
|
|
906
922
|
...(args.trigger > 0
|
|
@@ -984,8 +1000,9 @@ export async function loadSessionResources(args: Args, workspace: string, io: Ap
|
|
|
984
1000
|
for (const line of new Set([...loaded.diagnostics, ...text.diagnostics, ...slash.diagnostics])) io.say(io.palette.yellow(` ${line}`));
|
|
985
1001
|
const system = appendSkillsToSystem(assembleSystemPrompt(systemPromptFor(visibleSchemas(args, extensions).map(tool => tool.name), sessionEnvironment(workspace)), loadedContext), text.skills);
|
|
986
1002
|
const ui = toUiState(loaded, colorEnabled());
|
|
1003
|
+
// Also reached on /resume and /reload: `auto` is resolved once per process by the Tui (apps/shell.ts), not here.
|
|
987
1004
|
if (args.tuiMode) ui.settings = { ...ui.settings, tuiMode: args.tuiMode };
|
|
988
|
-
return { loadedContext, slash: { ...slash.catalog, skillCommands: loaded.settings.enableSkillCommands !== false }, ui, index, trusted, system, extensions };
|
|
1005
|
+
return { loadedContext, slash: { ...slash.catalog, skillExecution: true, skillCommands: loaded.settings.enableSkillCommands !== false }, ui, index, trusted, system, extensions };
|
|
989
1006
|
}
|
|
990
1007
|
|
|
991
1008
|
/**
|
|
@@ -1011,7 +1028,7 @@ export async function openSessionFor(opts: OpenSessionOptions): Promise<{
|
|
|
1011
1028
|
: await loadSessionResources(opts.args, workspace, opts.io, workspace === opts.workspace ? opts.projectTrusted : undefined);
|
|
1012
1029
|
const interactive = opts.interactive && !opts.args.print && !opts.args.prompt && opts.args.mode !== "json";
|
|
1013
1030
|
const named = [...opts.args.tools ?? [], ...opts.args.excludeTools, ...opts.args.allowTools];
|
|
1014
|
-
const unknown = unknownToolNames(named).filter(name => !resources.extensions.
|
|
1031
|
+
const unknown = unknownToolNames(named).filter(name => !resources.extensions.definitions().has(name));
|
|
1015
1032
|
if (unknown.length) throw new Error(`unknown tool: ${unknown.join(", ")}`);
|
|
1016
1033
|
const saved = priorCursor === null ? undefined : restoreSessionModel(priorCursor, opts.catalog);
|
|
1017
1034
|
const flags = { ...opts.modelFlags ?? { model: "", provider: "" } };
|
|
@@ -1054,10 +1071,14 @@ export async function openSessionFor(opts: OpenSessionOptions): Promise<{
|
|
|
1054
1071
|
const restoring = saved ?? spec;
|
|
1055
1072
|
const restoringCredential = saved === undefined ? credential : opts.credentialOf(restoring);
|
|
1056
1073
|
const oauth = (model: ModelSpec, got: Resolved) => got.ok && storedIsOAuth(opts.auth, model.provider) && opts.args.apiKey.length === 0;
|
|
1074
|
+
const acceptanceContract = priorCursor === null
|
|
1075
|
+
? await loadAcceptanceContract(workspace, resources.trusted, opts.args.acceptanceFile) : undefined;
|
|
1057
1076
|
const session = createSessionFor({
|
|
1058
1077
|
...opts,
|
|
1059
1078
|
system: resources.system,
|
|
1060
1079
|
extensions: resources.extensions,
|
|
1080
|
+
skills: [...resources.slash.skills.values()],
|
|
1081
|
+
...(acceptanceContract ? { acceptanceContract } : {}),
|
|
1061
1082
|
workspace: priorCursor?.workspace ?? opts.workspace,
|
|
1062
1083
|
blobDir: priorCursor?.blob_dir ?? (priorCursor?.workspace && priorCursor.workspace !== opts.workspace
|
|
1063
1084
|
? statePaths(priorCursor.workspace, opts.args.stateDir).blobs : opts.blobDir),
|
|
@@ -1065,11 +1086,12 @@ export async function openSessionFor(opts: OpenSessionOptions): Promise<{
|
|
|
1065
1086
|
credential: restoringCredential,
|
|
1066
1087
|
...(oauth(restoring, restoringCredential) ? { oauth: true } : {}),
|
|
1067
1088
|
});
|
|
1068
|
-
opts.startupPhase?.("
|
|
1089
|
+
opts.startupPhase?.("Opening session history");
|
|
1069
1090
|
await session.start();
|
|
1091
|
+
resources.slash = { ...resources.slash, skills: new Map((session.config.skills ?? []).map(skill => [skill.name, skill])) };
|
|
1070
1092
|
if (resources.extensions.loaded.length || session.config.frozen.tools.some(tool =>
|
|
1071
1093
|
typeof tool === "object" && tool !== null && "name" in tool && typeof tool.name === "string" && unknownToolNames([tool.name]).length)) {
|
|
1072
|
-
await session.reloadResources({ system: resources.system, tools: visibleSchemas(opts.args, resources.extensions) });
|
|
1094
|
+
await session.reloadResources({ system: resources.system, tools: visibleSchemas(opts.args, resources.extensions, session.config.frozen.tools) });
|
|
1073
1095
|
}
|
|
1074
1096
|
if (restoring.id !== spec.id || restoring.provider !== spec.provider) {
|
|
1075
1097
|
await session.switchModel(createProvider(providerConfig, credential.ok ? credential.key : "", retryHandler(opts.io),
|
|
@@ -1253,7 +1275,7 @@ export async function startup(args: Args, input?: StartupInput): Promise<App> {
|
|
|
1253
1275
|
if (printMode || jsonMode || !stdinIsTty() || !stdoutIsTty()) {
|
|
1254
1276
|
throw new Error("--resume needs an interactive terminal. Pass -r <id> or --session <id> instead");
|
|
1255
1277
|
}
|
|
1256
|
-
const picked = await withTerminal(handoff => pickStartupSession({ workspace,
|
|
1278
|
+
const picked = await withTerminal(handoff => pickStartupSession({ workspace, theme: ui.theme, say: io.say, keymap: ui.keymap,
|
|
1257
1279
|
...(sessionDir === undefined ? {} : { sessionDir }) }, handoff));
|
|
1258
1280
|
if (picked === null) { io.say(C.dim("No session selected")); process.exit(0); }
|
|
1259
1281
|
sessionOpts = { explicit: picked };
|
|
@@ -1321,6 +1343,21 @@ export async function startup(args: Args, input?: StartupInput): Promise<App> {
|
|
|
1321
1343
|
};
|
|
1322
1344
|
}
|
|
1323
1345
|
|
|
1346
|
+
/** The banner's words for the shared limits; the loop acts at exactly these numbers. */
|
|
1347
|
+
export function boundaryDescription(policy: ContextPolicy, limits: ContextLimits): string {
|
|
1348
|
+
switch (limits.kind) {
|
|
1349
|
+
case "stop":
|
|
1350
|
+
case "open":
|
|
1351
|
+
return "continuous replay; stops at model window";
|
|
1352
|
+
case "window":
|
|
1353
|
+
return `windowed replay; prepare ${limits.prepare}, cut ${limits.trigger}, output reserved`;
|
|
1354
|
+
case "segment":
|
|
1355
|
+
return `${policy}${policy === "windowed" ? " (unknown window; fixed fallback)" : ""}; cut ${limits.trigger}, hard ${limits.hard}`;
|
|
1356
|
+
default:
|
|
1357
|
+
return assertNever(limits, "boundaryDescription");
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1324
1361
|
function printBanner(
|
|
1325
1362
|
args: Args,
|
|
1326
1363
|
io: AppIO,
|
|
@@ -1370,13 +1407,14 @@ function printBanner(
|
|
|
1370
1407
|
const line = formatNotice(notice);
|
|
1371
1408
|
io.say(notice.kind === "skipped" ? C.yellow(` ${line}`) : C.dim(` ${line}`));
|
|
1372
1409
|
}
|
|
1373
|
-
const { triggerTokens: trigger, hardTokens: hard } = { ...DEFAULT_BOUNDARY, ...session.config.boundary };
|
|
1374
1410
|
const policy = session.config.contextPolicy ?? DEFAULT_CONTEXT_POLICY;
|
|
1375
|
-
const
|
|
1376
|
-
|
|
1377
|
-
:
|
|
1378
|
-
:
|
|
1379
|
-
|
|
1411
|
+
const limits = contextLimits({
|
|
1412
|
+
policy,
|
|
1413
|
+
contextWindow: session.provider.config.spec.contextWindow,
|
|
1414
|
+
outputReserve: session.provider.config.maxTokens,
|
|
1415
|
+
...(session.config.boundary ? { boundary: session.config.boundary } : {}),
|
|
1416
|
+
});
|
|
1417
|
+
io.say(C.dim(`boundary ${boundaryDescription(policy, limits)} (estimated tokens)`));
|
|
1380
1418
|
if (priorCursor !== null) {
|
|
1381
1419
|
const events = session.state.events.length;
|
|
1382
1420
|
io.say(
|
|
@@ -1405,6 +1443,8 @@ export function printCliHelp(flags?: ReadonlyMap<string, ExtensionFlag>): void {
|
|
|
1405
1443
|
" [--system-prompt <text-or-file>] [--append-system-prompt <text>]\n" +
|
|
1406
1444
|
" [--no-context-files|-nc]\n" +
|
|
1407
1445
|
" [--tools names] [--exclude-tools names] [--no-tools]\n" +
|
|
1446
|
+
" [--tool-profile coding|readonly|research|data|full]\n" +
|
|
1447
|
+
" [--acceptance-file contract.json]\n" +
|
|
1408
1448
|
" [--no-builtin-tools]\n" +
|
|
1409
1449
|
" [--allow-tools names] [--approve|--no-approve]\n" +
|
|
1410
1450
|
" [--theme <name|path>] [--use-theme name] [--no-themes] [--thinking level] [--models patterns]\n" +
|
|
@@ -1426,10 +1466,12 @@ export function printCliHelp(flags?: ReadonlyMap<string, ExtensionFlag>): void {
|
|
|
1426
1466
|
" --system-prompt replaces the built-in instructions (text, or a file that exists).\n" +
|
|
1427
1467
|
" --append-system-prompt appends; repeatable. -nc skips AGENTS.md discovery.\n" +
|
|
1428
1468
|
" --tools is a strict allowlist; excluded tools leave the schema.\n" +
|
|
1469
|
+
" New sessions use coding (10 tools); full exposes default built-ins.\n" +
|
|
1429
1470
|
" --no-builtin-tools drops built-ins and keeps any other source; --tools still wins.\n" +
|
|
1430
1471
|
" bash asks before it runs. --allow-tools bash skips the ask for this process.\n" +
|
|
1431
1472
|
" --time-budget <seconds> tells the model, once, when 75% of the run's wall-clock budget is used.\n" +
|
|
1432
1473
|
" --state-dir <dir> keeps snapshots, observations and sessions out of the workspace.\n" +
|
|
1474
|
+
" --checkpoint-shell enables best-effort Git recovery for command changes; ignored files and external effects are excluded.\n" +
|
|
1433
1475
|
" --path-root <dir> lets file tools reach absolute paths under <dir> (e.g. / in a container);\n" +
|
|
1434
1476
|
" relative paths, fingerprints and /back stay in the workspace, so edits outside it are not rewound.\n" +
|
|
1435
1477
|
" --approve loads .xeno/settings.json this run; --no-approve skips it.\n" +
|