@visual-z/xeno 0.2.1 → 0.4.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 +18 -0
- package/README.md +126 -34
- package/docs/app-server.md +84 -0
- package/docs/install.md +51 -13
- package/docs/providers.md +24 -3
- package/package.json +7 -5
- package/src/apps/app-server.ts +28 -0
- package/src/apps/approve.ts +3 -3
- package/src/apps/cli.ts +25 -10
- package/src/apps/commands.ts +45 -8
- package/src/apps/completions.ts +78 -22
- package/src/apps/control-transport.ts +89 -0
- package/src/apps/image-draft.ts +46 -0
- package/src/apps/model-catalog.ts +2 -1
- 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 +333 -1542
- package/src/apps/startup-input.ts +45 -9
- package/src/apps/startup.ts +149 -94
- package/src/apps/ui/app.ts +1553 -0
- package/src/apps/ui/control-model.ts +26 -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 +86 -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-action.ts +20 -0
- package/src/core/completion-review.ts +37 -21
- 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/control-protocol.ts +62 -0
- package/src/core/control-view.ts +42 -0
- 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 +81 -6
- package/src/core/events.ts +99 -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/harness-diagnostics.ts +206 -0
- 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/oauth-fields.ts +44 -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/program-status.ts +31 -0
- package/src/core/project.ts +70 -19
- 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/request-progress.ts +87 -0
- package/src/core/request-state.ts +114 -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 +33 -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-input.ts +6 -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 +11 -4
- package/src/runtime/branch-jobs.ts +36 -0
- package/src/runtime/catalog.ts +13 -3
- package/src/runtime/clipboard.ts +15 -4
- package/src/runtime/command-artifacts.ts +64 -0
- package/src/runtime/control-connection.ts +104 -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 +462 -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 +1088 -282
- package/src/runtime/model-discovery.ts +208 -0
- package/src/runtime/model.ts +9 -0
- package/src/runtime/native-eval-fixtures.ts +33 -0
- package/src/runtime/native-eval.ts +146 -0
- package/src/runtime/oauth.ts +80 -3
- package/src/runtime/pi-provider.ts +169 -12
- package/src/runtime/program-status.ts +38 -0
- package/src/runtime/provider-connect.ts +109 -0
- package/src/runtime/provider-presets.ts +87 -0
- package/src/runtime/provider-request.ts +13 -2
- package/src/runtime/provider.ts +23 -9
- 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/session-control.ts +198 -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 +200 -0
- package/src/tui/ui/control-panel.ts +23 -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 +279 -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,306 +1,58 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* xeno's key bindings (D20). One flat namespace:
|
|
3
|
+
* app.* global, handled before the focused component
|
|
4
|
+
* editor.* the composer (submit / newline / history / completion)
|
|
5
|
+
* list.* every list dialog (palette, model, login, resume, tree, …)
|
|
6
|
+
* Shortcuts that only exist inside one dialog are shown in that dialog's
|
|
7
|
+
* hint row instead of being configurable globals; the palette (Ctrl+P)
|
|
8
|
+
* reaches every command, so rarely used actions need no chord at all.
|
|
9
|
+
*/
|
|
2
10
|
export const ACTION_DEFAULTS = {
|
|
3
|
-
"
|
|
4
|
-
|
|
5
|
-
],
|
|
6
|
-
"
|
|
7
|
-
|
|
8
|
-
],
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
],
|
|
15
|
-
"
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
],
|
|
19
|
-
"
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
],
|
|
24
|
-
"editor.cursorWordRight": [
|
|
25
|
-
"alt+right",
|
|
26
|
-
"ctrl+right",
|
|
27
|
-
"alt+f"
|
|
28
|
-
],
|
|
29
|
-
"editor.cursorLineStart": [
|
|
30
|
-
"home",
|
|
31
|
-
"ctrl+home",
|
|
32
|
-
"ctrl+a"
|
|
33
|
-
],
|
|
34
|
-
"editor.cursorLineEnd": [
|
|
35
|
-
"end",
|
|
36
|
-
"ctrl+end",
|
|
37
|
-
"ctrl+e"
|
|
38
|
-
],
|
|
39
|
-
"tui.editor.jumpForward": [
|
|
40
|
-
"ctrl+]"
|
|
41
|
-
],
|
|
42
|
-
"tui.editor.jumpBackward": [
|
|
43
|
-
"ctrl+alt+]"
|
|
44
|
-
],
|
|
45
|
-
"tui.editor.pageUp": [
|
|
46
|
-
"pageUp",
|
|
47
|
-
"ctrl+pageUp"
|
|
48
|
-
],
|
|
49
|
-
"tui.editor.pageDown": [
|
|
50
|
-
"pageDown",
|
|
51
|
-
"ctrl+pageDown"
|
|
52
|
-
],
|
|
53
|
-
"editor.deleteCharBackward": [
|
|
54
|
-
"backspace"
|
|
55
|
-
],
|
|
56
|
-
"editor.deleteCharForward": [
|
|
57
|
-
"delete",
|
|
58
|
-
"ctrl+d"
|
|
59
|
-
],
|
|
60
|
-
"editor.deleteWordBackward": [
|
|
61
|
-
"ctrl+w",
|
|
62
|
-
"alt+backspace"
|
|
63
|
-
],
|
|
64
|
-
"editor.deleteWordForward": [
|
|
65
|
-
"alt+d",
|
|
66
|
-
"alt+delete"
|
|
67
|
-
],
|
|
68
|
-
"editor.deleteToLineStart": [
|
|
69
|
-
"ctrl+u"
|
|
70
|
-
],
|
|
71
|
-
"editor.deleteToLineEnd": [
|
|
72
|
-
"ctrl+k"
|
|
73
|
-
],
|
|
74
|
-
"editor.yank": [
|
|
75
|
-
"ctrl+y"
|
|
76
|
-
],
|
|
77
|
-
"tui.editor.yankPop": [
|
|
78
|
-
"alt+y"
|
|
79
|
-
],
|
|
80
|
-
"editor.undo": [
|
|
81
|
-
"ctrl+-",
|
|
82
|
-
"ctrl+_"
|
|
83
|
-
],
|
|
84
|
-
"editor.newLine": [
|
|
85
|
-
"shift+enter",
|
|
86
|
-
"ctrl+j"
|
|
87
|
-
],
|
|
88
|
-
"editor.submit": [
|
|
89
|
-
"enter"
|
|
90
|
-
],
|
|
91
|
-
"editor.complete": [
|
|
92
|
-
"tab"
|
|
93
|
-
],
|
|
94
|
-
"tui.input.copy": [
|
|
95
|
-
"ctrl+c"
|
|
96
|
-
],
|
|
97
|
-
"tui.select.up": [
|
|
98
|
-
"up"
|
|
99
|
-
],
|
|
100
|
-
"tui.select.down": [
|
|
101
|
-
"down"
|
|
102
|
-
],
|
|
103
|
-
"tui.select.pageUp": [
|
|
104
|
-
"pageUp"
|
|
105
|
-
],
|
|
106
|
-
"tui.select.pageDown": [
|
|
107
|
-
"pageDown"
|
|
108
|
-
],
|
|
109
|
-
"tui.select.confirm": [
|
|
110
|
-
"enter"
|
|
111
|
-
],
|
|
112
|
-
"tui.select.cancel": [
|
|
113
|
-
"escape",
|
|
114
|
-
"ctrl+c"
|
|
115
|
-
],
|
|
116
|
-
"tui.altScreen.pageUp": [
|
|
117
|
-
"pageUp"
|
|
118
|
-
],
|
|
119
|
-
"tui.altScreen.pageDown": [
|
|
120
|
-
"pageDown"
|
|
121
|
-
],
|
|
122
|
-
"tui.altScreen.halfPageUp": [],
|
|
123
|
-
"tui.altScreen.halfPageDown": [],
|
|
124
|
-
"tui.altScreen.lineUp": [],
|
|
125
|
-
"tui.altScreen.lineDown": [],
|
|
126
|
-
"tui.altScreen.previousPrompt": [
|
|
127
|
-
"ctrl+shift+up",
|
|
128
|
-
"ctrl+up"
|
|
129
|
-
],
|
|
130
|
-
"tui.altScreen.nextPrompt": [
|
|
131
|
-
"ctrl+shift+down",
|
|
132
|
-
"ctrl+down"
|
|
133
|
-
],
|
|
134
|
-
"tui.altScreen.search": [
|
|
135
|
-
"ctrl+shift+f"
|
|
136
|
-
],
|
|
137
|
-
"tui.altScreen.searchNext": [
|
|
138
|
-
"enter",
|
|
139
|
-
"ctrl+g"
|
|
140
|
-
],
|
|
141
|
-
"tui.altScreen.searchPrevious": [
|
|
142
|
-
"shift+enter",
|
|
143
|
-
"ctrl+shift+g"
|
|
144
|
-
],
|
|
145
|
-
"tui.altScreen.searchClose": [
|
|
146
|
-
"escape"
|
|
147
|
-
],
|
|
148
|
-
"tui.altScreen.top": [
|
|
149
|
-
"home"
|
|
150
|
-
],
|
|
151
|
-
"tui.altScreen.bottom": [
|
|
152
|
-
"end"
|
|
153
|
-
],
|
|
154
|
-
"app.interrupt": [
|
|
155
|
-
"escape"
|
|
156
|
-
],
|
|
157
|
-
"app.clear": [
|
|
158
|
-
"ctrl+c"
|
|
159
|
-
],
|
|
160
|
-
"app.exit": [
|
|
161
|
-
"ctrl+d"
|
|
162
|
-
],
|
|
163
|
-
"app.suspend": [
|
|
164
|
-
"ctrl+z"
|
|
165
|
-
],
|
|
166
|
-
"app.cycleThinking": [
|
|
167
|
-
"shift+tab"
|
|
168
|
-
],
|
|
169
|
-
"app.saveDefault": [
|
|
170
|
-
"ctrl+s"
|
|
171
|
-
],
|
|
172
|
-
"app.cycleModel": [
|
|
173
|
-
"ctrl+p"
|
|
174
|
-
],
|
|
175
|
-
"app.cycleModelReverse": [
|
|
176
|
-
"shift+ctrl+p"
|
|
177
|
-
],
|
|
178
|
-
"app.model": [
|
|
179
|
-
"ctrl+l"
|
|
180
|
-
],
|
|
181
|
-
"app.toggleTools": [
|
|
182
|
-
"ctrl+o"
|
|
183
|
-
],
|
|
184
|
-
"app.toggleThinking": [
|
|
185
|
-
"ctrl+t"
|
|
186
|
-
],
|
|
187
|
-
"app.session.toggleNamedFilter": [
|
|
188
|
-
"ctrl+n"
|
|
189
|
-
],
|
|
190
|
-
"app.externalEditor": [
|
|
191
|
-
"ctrl+g"
|
|
192
|
-
],
|
|
193
|
-
"app.message.copy": [
|
|
194
|
-
"ctrl+x"
|
|
195
|
-
],
|
|
196
|
-
"app.message.followUp": [
|
|
197
|
-
"alt+enter"
|
|
198
|
-
],
|
|
199
|
-
"app.message.dequeue": [
|
|
200
|
-
"alt+up"
|
|
201
|
-
],
|
|
202
|
-
"app.clipboard.pasteImage": [
|
|
203
|
-
"ctrl+v"
|
|
204
|
-
],
|
|
11
|
+
"app.palette": ["ctrl+p"],
|
|
12
|
+
"app.model": ["ctrl+l"],
|
|
13
|
+
"app.cycleModel": ["alt+m"],
|
|
14
|
+
"app.cycleModelReverse": ["alt+shift+m"],
|
|
15
|
+
"app.cycleThinking": ["shift+tab"],
|
|
16
|
+
"app.toggleTools": ["ctrl+o"],
|
|
17
|
+
"app.toggleThinking": ["ctrl+t"],
|
|
18
|
+
"app.toggleSidebar": ["ctrl+b"],
|
|
19
|
+
"app.interrupt": ["escape"],
|
|
20
|
+
"app.clear": ["ctrl+c"],
|
|
21
|
+
"app.exit": ["ctrl+d"],
|
|
22
|
+
"app.suspend": ["ctrl+z"],
|
|
23
|
+
"app.externalEditor": ["ctrl+g"],
|
|
24
|
+
"app.message.copy": ["ctrl+x"],
|
|
25
|
+
"app.message.followUp": ["alt+enter"],
|
|
26
|
+
"app.message.dequeue": ["alt+up"],
|
|
27
|
+
"app.clipboard.pasteImage": ["ctrl+v"],
|
|
28
|
+
"app.scroll.pageUp": ["pageup"],
|
|
29
|
+
"app.scroll.pageDown": ["pagedown"],
|
|
30
|
+
"app.scroll.top": ["ctrl+home"],
|
|
31
|
+
"app.scroll.bottom": ["ctrl+end"],
|
|
205
32
|
"app.session.new": [],
|
|
206
33
|
"app.session.tree": [],
|
|
207
34
|
"app.session.fork": [],
|
|
208
35
|
"app.session.resume": [],
|
|
209
|
-
"app.
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
],
|
|
213
|
-
"
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
],
|
|
217
|
-
"
|
|
218
|
-
|
|
219
|
-
],
|
|
220
|
-
"
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
"app.session.toggleSort": [
|
|
227
|
-
"ctrl+s"
|
|
228
|
-
],
|
|
229
|
-
"app.session.rename": [
|
|
230
|
-
"ctrl+r"
|
|
231
|
-
],
|
|
232
|
-
"app.session.delete": [
|
|
233
|
-
"ctrl+d"
|
|
234
|
-
],
|
|
235
|
-
"app.session.deleteNoninvasive": [
|
|
236
|
-
"ctrl+backspace"
|
|
237
|
-
],
|
|
238
|
-
"app.models.save": [
|
|
239
|
-
"ctrl+s"
|
|
240
|
-
],
|
|
241
|
-
"app.models.enableAll": [
|
|
242
|
-
"ctrl+a"
|
|
243
|
-
],
|
|
244
|
-
"app.models.clearAll": [
|
|
245
|
-
"ctrl+x"
|
|
246
|
-
],
|
|
247
|
-
"app.models.toggleProvider": [
|
|
248
|
-
"ctrl+p"
|
|
249
|
-
],
|
|
250
|
-
"app.models.reorderUp": [
|
|
251
|
-
"alt+up"
|
|
252
|
-
],
|
|
253
|
-
"app.models.reorderDown": [
|
|
254
|
-
"alt+down"
|
|
255
|
-
],
|
|
256
|
-
"app.tree.filter.default": [
|
|
257
|
-
"ctrl+d"
|
|
258
|
-
],
|
|
259
|
-
"app.tree.filter.noTools": [
|
|
260
|
-
"ctrl+t"
|
|
261
|
-
],
|
|
262
|
-
"app.tree.filter.userOnly": [
|
|
263
|
-
"ctrl+u"
|
|
264
|
-
],
|
|
265
|
-
"app.tree.filter.labeledOnly": [
|
|
266
|
-
"ctrl+l"
|
|
267
|
-
],
|
|
268
|
-
"app.tree.filter.all": [
|
|
269
|
-
"ctrl+a"
|
|
270
|
-
],
|
|
271
|
-
"app.tree.filter.cycleForward": [
|
|
272
|
-
"ctrl+o"
|
|
273
|
-
],
|
|
274
|
-
"app.tree.filter.cycleBackward": [
|
|
275
|
-
"shift+ctrl+o"
|
|
276
|
-
]
|
|
277
|
-
} as const;
|
|
278
|
-
export const ACTION_ALIASES: Readonly<Record<string,string>> = {
|
|
279
|
-
"tui.input.submit": "editor.submit",
|
|
280
|
-
"tui.input.newLine": "editor.newLine",
|
|
281
|
-
"tui.input.tab": "editor.complete",
|
|
36
|
+
"app.saveDefault": ["ctrl+s"],
|
|
37
|
+
"editor.submit": ["enter"],
|
|
38
|
+
"editor.newLine": ["shift+enter", "ctrl+j"],
|
|
39
|
+
"editor.complete": ["tab"],
|
|
40
|
+
"editor.historyPrevious": ["up"],
|
|
41
|
+
"editor.historyNext": ["down"],
|
|
42
|
+
"list.up": ["up", "ctrl+k"],
|
|
43
|
+
"list.down": ["down", "ctrl+j"],
|
|
44
|
+
"list.pageUp": ["pageup"],
|
|
45
|
+
"list.pageDown": ["pagedown"],
|
|
46
|
+
"list.confirm": ["enter"],
|
|
47
|
+
"list.cancel": ["escape"],
|
|
48
|
+
"list.toggle": ["space"],
|
|
49
|
+
} as const satisfies Record<string, readonly string[]>;
|
|
50
|
+
|
|
51
|
+
/** Renamed ids still read from a user's keybindings file. */
|
|
52
|
+
export const ACTION_ALIASES: Readonly<Record<string, string>> = {
|
|
282
53
|
"app.model.select": "app.model",
|
|
283
|
-
"app.model.cycleForward": "app.cycleModel",
|
|
284
|
-
"app.model.cycleBackward": "app.cycleModelReverse",
|
|
285
54
|
"app.thinking.cycle": "app.cycleThinking",
|
|
286
55
|
"app.thinking.toggle": "app.toggleThinking",
|
|
287
56
|
"app.tools.expand": "app.toggleTools",
|
|
288
57
|
"app.editor.external": "app.externalEditor",
|
|
289
|
-
"app.thinking.save": "app.saveDefault",
|
|
290
|
-
"tui.editor.cursorUp": "editor.cursorUp",
|
|
291
|
-
"tui.editor.cursorDown": "editor.cursorDown",
|
|
292
|
-
"tui.editor.cursorLeft": "editor.cursorLeft",
|
|
293
|
-
"tui.editor.cursorRight": "editor.cursorRight",
|
|
294
|
-
"tui.editor.cursorWordLeft": "editor.cursorWordLeft",
|
|
295
|
-
"tui.editor.cursorWordRight": "editor.cursorWordRight",
|
|
296
|
-
"tui.editor.cursorLineStart": "editor.cursorLineStart",
|
|
297
|
-
"tui.editor.cursorLineEnd": "editor.cursorLineEnd",
|
|
298
|
-
"tui.editor.deleteCharBackward": "editor.deleteCharBackward",
|
|
299
|
-
"tui.editor.deleteCharForward": "editor.deleteCharForward",
|
|
300
|
-
"tui.editor.deleteWordBackward": "editor.deleteWordBackward",
|
|
301
|
-
"tui.editor.deleteWordForward": "editor.deleteWordForward",
|
|
302
|
-
"tui.editor.deleteToLineStart": "editor.deleteToLineStart",
|
|
303
|
-
"tui.editor.deleteToLineEnd": "editor.deleteToLineEnd",
|
|
304
|
-
"tui.editor.yank": "editor.yank",
|
|
305
|
-
"tui.editor.undo": "editor.undo"
|
|
306
58
|
};
|
package/src/core/benchmark.ts
CHANGED
|
@@ -126,7 +126,7 @@ export function benchmarkPrompt(item: BenchmarkItem, chunks: number): string {
|
|
|
126
126
|
'Format your final response as follows: "The correct answer is (insert answer here)".';
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
-
export interface BenchmarkPrice { input: number; output: number; cacheRead: number }
|
|
129
|
+
export interface BenchmarkPrice { input: number; output: number; cacheRead: number; cacheWrite?: number }
|
|
130
130
|
export interface BenchmarkUsage { input_tokens: number; output_tokens: number; cache_read_input_tokens: number; cache_creation_input_tokens: number }
|
|
131
131
|
|
|
132
132
|
/** A conservative allowance, not a tokenizer: wire bytes plus framing, at uncached peak rates. */
|
|
@@ -139,7 +139,7 @@ export function measuredCharge(usage: BenchmarkUsage, price: BenchmarkPrice): nu
|
|
|
139
139
|
const counts = [usage.input_tokens, usage.output_tokens, usage.cache_read_input_tokens, usage.cache_creation_input_tokens];
|
|
140
140
|
if (counts.some((value) => !Number.isSafeInteger(value) || value < 0) ||
|
|
141
141
|
usage.input_tokens + usage.cache_read_input_tokens + usage.cache_creation_input_tokens === 0 || usage.output_tokens === 0) return null;
|
|
142
|
-
return (
|
|
142
|
+
return (usage.input_tokens * price.input + usage.cache_creation_input_tokens * (price.cacheWrite ?? price.input) +
|
|
143
143
|
usage.cache_read_input_tokens * price.cacheRead + usage.output_tokens * price.output) / 1_000_000;
|
|
144
144
|
}
|
|
145
145
|
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** O.9: references to facts, never a child's self-assessed completion. */
|
|
2
|
+
import type { LogEvent, NodeRef } from "./events.ts";
|
|
3
|
+
import type { Handoff } from "./handoff.ts";
|
|
4
|
+
import type { ExecutionOutcome } from "./outcome.ts";
|
|
5
|
+
|
|
6
|
+
export function deriveBranchReceipt(input: {
|
|
7
|
+
source: NodeRef; outcome: ExecutionOutcome; handoff: Handoff;
|
|
8
|
+
nodes: readonly { node: number; events: readonly LogEvent[] }[];
|
|
9
|
+
}) {
|
|
10
|
+
const entries = input.nodes.flatMap(part => part.events.map(event => ({ node: part.node, event })));
|
|
11
|
+
const commands = entries.flatMap(({ node, event }) => event.t === "tool_result" && event.effect?.kind === "command"
|
|
12
|
+
? [{ node, event: event.id, exit: event.effect.exit }] : []);
|
|
13
|
+
const changes = entries.flatMap(({ node, event }) => event.t === "tool_result" && event.effect?.kind === "edit"
|
|
14
|
+
? [{ node, event: event.id, path: event.effect.path }] : []);
|
|
15
|
+
const observations = entries.flatMap(({ node, event }) => event.t === "tool_result" ? [{ node, event: event.id }] : []);
|
|
16
|
+
const unfinished = (input.handoff.state.work_items ?? []).filter(item => item.status === "active" || item.status === "blocked")
|
|
17
|
+
.map(item => ({ work_id: item.id, status: item.status, next_action: item.next_action ?? null }));
|
|
18
|
+
return { version: 1, source: input.source, execution: input.outcome,
|
|
19
|
+
verification: commands.length === 0 ? "not_recorded" : commands.some(command => command.exit === 0) ? "command_succeeded" : "commands_failed",
|
|
20
|
+
task_acceptance: "not_assessed", commands, changes, observations, unfinished,
|
|
21
|
+
...(input.handoff.processes?.length ? { processes: input.handoff.processes } : {}),
|
|
22
|
+
unresolved_branches: input.handoff.branches.filter(b => b.resolution === null).map(b => b.node),
|
|
23
|
+
};
|
|
24
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/** P.3: recovery describes observed file transitions, never a complete disk image. */
|
|
2
|
+
export type FileVersion = { sha256: string; mode: number } | null;
|
|
3
|
+
export type FileChange = { before: FileVersion; after: FileVersion };
|
|
4
|
+
export type FileChanges = Record<string, FileChange>;
|
|
5
|
+
export interface CheckpointEvent {
|
|
6
|
+
t: "checkpoint";
|
|
7
|
+
id: string;
|
|
8
|
+
at: string;
|
|
9
|
+
operation: string;
|
|
10
|
+
source: "edit" | "git" | "restore";
|
|
11
|
+
phase: "prepared" | "applied" | "unavailable";
|
|
12
|
+
changes: FileChanges;
|
|
13
|
+
tree?: string;
|
|
14
|
+
reason?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface WorkspaceObservationEvent {
|
|
17
|
+
t: "workspace_observation";
|
|
18
|
+
id: string;
|
|
19
|
+
at: string;
|
|
20
|
+
/** P.3: this scope excludes ignored/build output and is not a backup. */
|
|
21
|
+
fingerprint?: string;
|
|
22
|
+
scope: "source-v1";
|
|
23
|
+
changed: boolean;
|
|
24
|
+
reason?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function sameVersion(a: FileVersion, b: FileVersion): boolean {
|
|
28
|
+
return a === null || b === null ? a === b : a.sha256 === b.sha256 && a.mode === b.mode;
|
|
29
|
+
}
|
|
30
|
+
export function changedPaths(changes: FileChanges): string[] {
|
|
31
|
+
return Object.keys(changes).filter(path => !sameVersion(changes[path]!.before, changes[path]!.after)).sort();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Undo the abandoned suffix then replay the selected suffix, touching only their paths. */
|
|
35
|
+
export function planFileTransition(from: readonly CheckpointEvent[], to: readonly CheckpointEvent[]): FileChanges {
|
|
36
|
+
const left = from.filter(event => event.phase === "applied");
|
|
37
|
+
const right = to.filter(event => event.phase === "applied");
|
|
38
|
+
let common = 0;
|
|
39
|
+
while (common < left.length && common < right.length && left[common]!.id === right[common]!.id) common++;
|
|
40
|
+
const changes: FileChanges = Object.create(null) as FileChanges;
|
|
41
|
+
const apply = (path: string, before: FileVersion, after: FileVersion) => {
|
|
42
|
+
const existing = changes[path];
|
|
43
|
+
if (existing && !sameVersion(existing.after, before)) throw new Error(`discontinuous file history: ${path}; inspect intervening external edits`);
|
|
44
|
+
changes[path] = { before: existing ? existing.before : before, after };
|
|
45
|
+
};
|
|
46
|
+
for (const event of left.slice(common).reverse()) for (const path of changedPaths(event.changes)) {
|
|
47
|
+
const change = event.changes[path]!;
|
|
48
|
+
apply(path, change.after, change.before);
|
|
49
|
+
}
|
|
50
|
+
for (const event of right.slice(common)) for (const path of changedPaths(event.changes)) {
|
|
51
|
+
const change = event.changes[path]!;
|
|
52
|
+
apply(path, change.before, change.after);
|
|
53
|
+
}
|
|
54
|
+
return Object.fromEntries(changedPaths(changes).map(path => [path, changes[path]!])) as FileChanges;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function validFileChanges(value: unknown): value is FileChanges {
|
|
58
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
|
59
|
+
const version = (v: unknown): boolean => v === null || typeof v === "object" && !Array.isArray(v) &&
|
|
60
|
+
typeof (v as Record<string, unknown>)["sha256"] === "string" && /^[a-f0-9]{64}$/.test(String((v as Record<string, unknown>)["sha256"])) &&
|
|
61
|
+
Number.isInteger((v as Record<string, unknown>)["mode"]) && Number((v as Record<string, unknown>)["mode"]) >= 0 && Number((v as Record<string, unknown>)["mode"]) <= 0o777;
|
|
62
|
+
return Object.entries(value).every(([path, change]: [string, unknown]) => path.length > 0 && typeof change === "object" && change !== null &&
|
|
63
|
+
version((change as Record<string, unknown>)["before"]) && version((change as Record<string, unknown>)["after"]));
|
|
64
|
+
}
|
package/src/core/churn.ts
CHANGED
|
@@ -34,6 +34,8 @@ export function churnReview(events: readonly LogEvent[]): Pick<GuidanceEvent, "r
|
|
|
34
34
|
if (event.t !== "tool_result" || event.effect === undefined) continue;
|
|
35
35
|
const effect = event.effect;
|
|
36
36
|
if (effect.kind === "command") {
|
|
37
|
+
// R5 handles structured failures with version/change premises; keep this path for legacy logs.
|
|
38
|
+
if (event.diagnostic?.execution !== undefined) continue;
|
|
37
39
|
const key = effect.cmd.trim();
|
|
38
40
|
if (effect.exit === 0) { failures.delete(key); continue; }
|
|
39
41
|
const seen = { count: (failures.get(key)?.count ?? 0) + 1, last: event.id };
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/** R8 / D8 / D11: an explicit observation scope is not a claim of build causality. */
|
|
2
|
+
import type { ArtifactRef } from "./events.ts";
|
|
3
|
+
import { isObservationArtifact } from "./observation-view.ts";
|
|
4
|
+
|
|
5
|
+
// Assumed observation budgets keep a model-supplied plan bounded before execution.
|
|
6
|
+
export const MAX_COMMAND_ARTIFACTS = 16;
|
|
7
|
+
export const MAX_ARTIFACT_PLAN_BYTES = 16 * 1024;
|
|
8
|
+
export const MAX_COMMAND_ARTIFACT_BYTES = 512 * 1024 * 1024;
|
|
9
|
+
export const COMMAND_ARTIFACT_SCOPE = "source-v1-minus-artifacts";
|
|
10
|
+
export type CommandArtifactPlan = { paths: string[]; output_roots: string[] };
|
|
11
|
+
export type ArtifactFileIdentity = { device: string; inode: string; mtime_ns: string; ctime_ns: string; mode: number };
|
|
12
|
+
export type ArtifactFile = { state: "observed"; sha256: string; bytes: number; identity: ArtifactFileIdentity } |
|
|
13
|
+
{ state: "missing" | "unsafe" | "changed" | "unknown"; reason: string };
|
|
14
|
+
export type ArtifactChange = "created" | "modified" | "unchanged" | "unknown";
|
|
15
|
+
export type CommandArtifactOutput = { path: string; before: ArtifactFile; after: ArtifactFile; change: ArtifactChange };
|
|
16
|
+
export type CommandArtifactReceipt = {
|
|
17
|
+
kind: "command_artifacts"; version: 1;
|
|
18
|
+
state: "pending" | "observed" | "changed" | "unknown";
|
|
19
|
+
plan: CommandArtifactPlan;
|
|
20
|
+
source: { host: string; workspace: string; cwd: string; runtime: string };
|
|
21
|
+
command: string; exit: number | null;
|
|
22
|
+
started_at: string; observed_at: string | null;
|
|
23
|
+
inputs: { scope: typeof COMMAND_ARTIFACT_SCOPE; exclusions: string[]; before: string | null; after: string | null };
|
|
24
|
+
outputs: CommandArtifactOutput[];
|
|
25
|
+
reasons: string[];
|
|
26
|
+
manifest?: ArtifactRef;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const object = (value: unknown): value is Record<string, unknown> => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
30
|
+
const digest = (value: unknown): value is string => typeof value === "string" && /^[a-f0-9]{64}$/.test(value);
|
|
31
|
+
const covers = (root: string, path: string) => root === path || path.startsWith(root + "/");
|
|
32
|
+
export function validArtifactPath(value: unknown): value is string {
|
|
33
|
+
return typeof value === "string" && value.length > 0 && new TextEncoder().encode(value).length <= 4096 &&
|
|
34
|
+
!/[\x00-\x1f\x7f\\:]/.test(value) && value.split("/").every(part => part !== "" && part !== "." && part !== ".." && part !== ".git" && part !== ".xeno");
|
|
35
|
+
}
|
|
36
|
+
export function parseCommandArtifactPlan(value: unknown): CommandArtifactPlan {
|
|
37
|
+
if (!object(value) || Object.keys(value).some(key => !["paths", "output_roots"].includes(key))) throw new Error("artifacts needs paths and optional output_roots only");
|
|
38
|
+
const paths = value["paths"], roots = value["output_roots"] ?? paths;
|
|
39
|
+
for (const list of [paths, roots]) {
|
|
40
|
+
if (!Array.isArray(list) || list.length < 1 || list.length > MAX_COMMAND_ARTIFACTS || !list.every(validArtifactPath) || new Set(list).size !== list.length)
|
|
41
|
+
throw new Error("artifacts needs 1..16 unique workspace-relative file paths; no traversal or protected paths");
|
|
42
|
+
}
|
|
43
|
+
const plan = { paths: [...paths as string[]].sort(), output_roots: [...roots as string[]].sort() };
|
|
44
|
+
if (new TextEncoder().encode(JSON.stringify(plan)).length > MAX_ARTIFACT_PLAN_BYTES) throw new Error("artifact plan exceeds 16 KiB");
|
|
45
|
+
if (plan.paths.some((path, i) => plan.paths.some((other, j) => i !== j && covers(path, other)))) throw new Error("artifact file paths cannot contain one another");
|
|
46
|
+
if (plan.output_roots.some((root, i) => !plan.paths.some(path => covers(root, path)) || plan.output_roots.some((other, j) => i !== j && covers(root, other))) ||
|
|
47
|
+
plan.paths.some(path => !plan.output_roots.some(root => covers(root, path)))) throw new Error("output_roots must be nonoverlapping and cover exactly the requested artifact paths");
|
|
48
|
+
return plan;
|
|
49
|
+
}
|
|
50
|
+
export function artifactChange(before: ArtifactFile, after: ArtifactFile): ArtifactChange {
|
|
51
|
+
if (after.state !== "observed") return "unknown";
|
|
52
|
+
if (before.state === "missing") return "created";
|
|
53
|
+
if (before.state !== "observed") return "unknown";
|
|
54
|
+
return before.sha256 === after.sha256 && before.bytes === after.bytes && sameArtifactIdentity(before.identity, after.identity) ? "unchanged" : "modified";
|
|
55
|
+
}
|
|
56
|
+
export function sameArtifactIdentity(a: ArtifactFileIdentity, b: ArtifactFileIdentity): boolean {
|
|
57
|
+
return a.device === b.device && a.inode === b.inode && a.mtime_ns === b.mtime_ns && a.ctime_ns === b.ctime_ns && a.mode === b.mode;
|
|
58
|
+
}
|
|
59
|
+
export function commandArtifactState(receipt: Pick<CommandArtifactReceipt, "exit" | "inputs" | "outputs" | "reasons">): CommandArtifactReceipt["state"] {
|
|
60
|
+
if (receipt.exit === null) return "pending";
|
|
61
|
+
if (receipt.inputs.before !== null && receipt.inputs.after !== null && receipt.inputs.before !== receipt.inputs.after ||
|
|
62
|
+
receipt.outputs.some(output => output.after.state === "changed")) return "changed";
|
|
63
|
+
if (receipt.exit !== 0 || receipt.reasons.length > 0 || receipt.inputs.before === null || receipt.inputs.after === null ||
|
|
64
|
+
receipt.outputs.length === 0 || receipt.outputs.some(output => output.after.state !== "observed")) return "unknown";
|
|
65
|
+
return "observed";
|
|
66
|
+
}
|
|
67
|
+
export function isArtifactFile(value: unknown): value is ArtifactFile {
|
|
68
|
+
if (!object(value)) return false;
|
|
69
|
+
if (value["state"] !== "observed") return ["missing", "unsafe", "changed", "unknown"].includes(String(value["state"])) && typeof value["reason"] === "string" && value["reason"].length > 0;
|
|
70
|
+
const id = value["identity"];
|
|
71
|
+
return digest(value["sha256"]) && Number.isSafeInteger(value["bytes"]) && Number(value["bytes"]) >= 0 && Number(value["bytes"]) <= MAX_COMMAND_ARTIFACT_BYTES &&
|
|
72
|
+
object(id) && ["device", "inode", "mtime_ns", "ctime_ns"].every(key => typeof id[key] === "string" && /^-?\d+$/.test(id[key])) &&
|
|
73
|
+
Number.isInteger(id["mode"]) && Number(id["mode"]) >= 0 && Number(id["mode"]) <= 0o777;
|
|
74
|
+
}
|
|
75
|
+
export function isCommandArtifactReceipt(value: unknown): value is CommandArtifactReceipt {
|
|
76
|
+
if (!object(value) || value["kind"] !== "command_artifacts" || value["version"] !== 1 || typeof value["command"] !== "string" || !value["command"].trim() ||
|
|
77
|
+
typeof value["started_at"] !== "string" || (value["exit"] !== null && !Number.isInteger(value["exit"]))) return false;
|
|
78
|
+
let plan: CommandArtifactPlan;
|
|
79
|
+
try { plan = parseCommandArtifactPlan(value["plan"]); } catch { return false; }
|
|
80
|
+
if (!object(value["plan"]) || JSON.stringify(value["plan"]["paths"]) !== JSON.stringify(plan.paths) ||
|
|
81
|
+
JSON.stringify(value["plan"]["output_roots"]) !== JSON.stringify(plan.output_roots)) return false;
|
|
82
|
+
const source = value["source"], inputs = value["inputs"], outputs = value["outputs"], reasons = value["reasons"];
|
|
83
|
+
if (!object(source) || !["host", "workspace", "cwd", "runtime"].every(key => typeof source[key] === "string" && source[key] !== "") ||
|
|
84
|
+
!object(inputs) || inputs["scope"] !== COMMAND_ARTIFACT_SCOPE || !Array.isArray(inputs["exclusions"]) ||
|
|
85
|
+
!inputs["exclusions"].every(path => typeof path === "string" && path.length > 0) || new Set(inputs["exclusions"]).size !== inputs["exclusions"].length ||
|
|
86
|
+
!plan.output_roots.every(root => (inputs["exclusions"] as unknown[]).includes(root)) ||
|
|
87
|
+
!["before", "after"].every(key => inputs[key] === null || digest(inputs[key])) || !Array.isArray(outputs) ||
|
|
88
|
+
!Array.isArray(reasons) || !reasons.every(reason => typeof reason === "string" && reason.length > 0)) return false;
|
|
89
|
+
if (value["exit"] === null) return value["state"] === "pending" && value["observed_at"] === null && inputs["after"] === null && outputs.length === 0 && value["manifest"] === undefined;
|
|
90
|
+
if (typeof value["observed_at"] !== "string" || !value["observed_at"] || outputs.length !== plan.paths.length || !outputs.every((item, index) =>
|
|
91
|
+
object(item) && item["path"] === plan.paths[index] && isArtifactFile(item["before"]) && isArtifactFile(item["after"]) && item["change"] === artifactChange(item["before"], item["after"]))) return false;
|
|
92
|
+
if (value["manifest"] !== undefined && !isObservationArtifact(value["manifest"])) return false;
|
|
93
|
+
return value["state"] === commandArtifactState(value as CommandArtifactReceipt) && (value["state"] !== "observed" || isObservationArtifact(value["manifest"]));
|
|
94
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/** R1 / D11: all command entrances agree on invocation and observed termination. */
|
|
2
|
+
import { executionOutcome } from "./outcome.ts";
|
|
3
|
+
import type { ToolDiagnostic } from "./tool-contract.ts";
|
|
4
|
+
|
|
5
|
+
/** Operational grace, not a measured optimum: ignored SIGTERM cannot defeat cancellation. */
|
|
6
|
+
export const COMMAND_TERMINATION_GRACE_MS = 200;
|
|
7
|
+
|
|
8
|
+
/** A fresh login Bash per command; interactive stdin does not change shell success semantics. */
|
|
9
|
+
export function commandArgv(command: string): string[] {
|
|
10
|
+
return ["bash", "-o", "pipefail", "-lc", command];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** Preserve inherited values except documented noninteractive defaults; never mutate the host. */
|
|
14
|
+
export function commandEnvironment(base: Readonly<Record<string, string | undefined>>): Record<string, string> {
|
|
15
|
+
const env: Record<string, string> = {};
|
|
16
|
+
for (const [key, value] of Object.entries(base)) if (value !== undefined) env[key] = value;
|
|
17
|
+
Object.assign(env, {
|
|
18
|
+
TERM: "dumb", PAGER: "cat", GIT_PAGER: "cat", NO_COLOR: "1",
|
|
19
|
+
GIT_TERMINAL_PROMPT: "0", DEBIAN_FRONTEND: "noninteractive",
|
|
20
|
+
});
|
|
21
|
+
if (env["LANG"] === undefined || env["LANG"] === "") env["LANG"] = "C.UTF-8";
|
|
22
|
+
return env;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Model seconds and host milliseconds meet once; a yield is never a command deadline. */
|
|
26
|
+
export function commandTimeout(requestedSeconds: number | undefined, defaultMs: number, maximumMs = defaultMs): number {
|
|
27
|
+
return Math.min(requestedSeconds === undefined ? defaultMs : requestedSeconds * 1000, maximumMs);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** A trap can exit zero after cancellation: successful exit alone does not erase the stop. */
|
|
31
|
+
export function commandCompletion(exit: number, stopped?: "cancelled" | "timeout", cleanup?: ToolDiagnostic) {
|
|
32
|
+
return { ok: stopped === undefined && exit === 0 && cleanup === undefined,
|
|
33
|
+
outcome: executionOutcome(stopped ?? (exit === 0 && cleanup === undefined ? "tool_completed" : "tool_failed")),
|
|
34
|
+
...(cleanup ? { warnings: [cleanup] } : {}) };
|
|
35
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { assertNever } from "./exhaustive.ts";
|
|
2
|
+
|
|
3
|
+
/** D19: confirmation follows the selected completion, never the unfinished draft. */
|
|
4
|
+
export function completionAction(kind: "command" | "argument" | "path", key: "enter" | "tab", requiresArgument: boolean): "insert" | "submit" {
|
|
5
|
+
if (key === "tab") return "insert";
|
|
6
|
+
switch (kind) {
|
|
7
|
+
case "command": return requiresArgument ? "insert" : "submit";
|
|
8
|
+
case "argument": return "submit";
|
|
9
|
+
case "path": return "insert";
|
|
10
|
+
default: return assertNever(kind, "completion kind");
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** Match the parser's exact identity before discoverable prefixes and substrings. */
|
|
15
|
+
export function commandCompletionRank(name: string, query: string): number {
|
|
16
|
+
const canonical = name.toLowerCase(), typed = query.toLowerCase();
|
|
17
|
+
if (canonical === typed) return 0;
|
|
18
|
+
if (canonical.startsWith(typed)) return 1;
|
|
19
|
+
return typed.length > 1 && canonical.includes(typed) ? 2 : Number.POSITIVE_INFINITY;
|
|
20
|
+
}
|