@visual-z/xeno 0.2.0 → 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 +15 -0
- package/README.md +106 -26
- package/docs/install.md +52 -9
- package/docs/providers.md +11 -0
- package/package.json +5 -4
- package/src/apps/approve.ts +3 -3
- package/src/apps/cli.ts +10 -11
- 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 +130 -0
- package/src/apps/startup.ts +143 -115
- 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,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
|
+
}
|
|
@@ -11,6 +11,9 @@
|
|
|
11
11
|
* turn into a loop.
|
|
12
12
|
*/
|
|
13
13
|
import type { GuidanceEvent, LogEvent } from "./events.ts";
|
|
14
|
+
import type { AcceptanceState } from "./acceptance.ts";
|
|
15
|
+
import { changedPaths } from "./checkpoints.ts";
|
|
16
|
+
import { resolveEvidence } from "./evidence.ts";
|
|
14
17
|
|
|
15
18
|
/**
|
|
16
19
|
* Wording that hands the task back instead of finishing it. Ported from
|
|
@@ -35,6 +38,7 @@ export function looksLikeHandBack(text: string): boolean {
|
|
|
35
38
|
export function completionReview(
|
|
36
39
|
events: readonly LogEvent[],
|
|
37
40
|
remainingRequests: number,
|
|
41
|
+
options?: { strictVerification?: boolean; acceptance?: AcceptanceState; taskAware?: boolean },
|
|
38
42
|
): Pick<GuidanceEvent, "reason" | "text" | "sources"> | null {
|
|
39
43
|
if (remainingRequests < 1) return null;
|
|
40
44
|
const start = events.findLastIndex((event) => event.t === "user");
|
|
@@ -46,21 +50,33 @@ export function completionReview(
|
|
|
46
50
|
|
|
47
51
|
const text = final.blocks.flatMap((block) => block.type === "text" ? [block.text] : []).join("\n").trim();
|
|
48
52
|
const usedTools = current.some((event) => event.t === "tool_result");
|
|
49
|
-
const lastEdit = current.findLastIndex((event) => event.t === "tool_result" && event.effect?.kind === "edit"
|
|
53
|
+
const lastEdit = current.findLastIndex((event) => event.t === "tool_result" && event.effect?.kind === "edit" ||
|
|
54
|
+
event.t === "checkpoint" && event.phase === "applied" && changedPaths(event.changes).length > 0);
|
|
50
55
|
const verifiedAfter = lastEdit >= 0 && current.slice(lastEdit + 1)
|
|
51
|
-
.some((event) => event.t === "tool_result" && event.effect?.kind === "command" && event.effect.exit === 0
|
|
56
|
+
.some((event) => event.t === "tool_result" && event.effect?.kind === "command" && event.effect.exit === 0 &&
|
|
57
|
+
resolveEvidence(current, { command: event.effect.cmd }).ok &&
|
|
58
|
+
(!options?.strictVerification || event.effect.gate !== undefined));
|
|
52
59
|
|
|
53
60
|
const gaps: string[] = [];
|
|
54
61
|
if (usedTools && text.length === 0) gaps.push("the final reply is empty");
|
|
55
|
-
if (lastEdit >= 0 && !verifiedAfter) gaps.push("files were changed but no command has succeeded since the last edit");
|
|
56
|
-
|
|
62
|
+
if (lastEdit >= 0 && !verifiedAfter && !options?.acceptance) gaps.push("files were changed but no command has succeeded since the last edit");
|
|
63
|
+
const acceptance = options?.acceptance;
|
|
64
|
+
if (acceptance?.status === "needs_work") gaps.push(`declared acceptance criteria need evidence: ${[
|
|
65
|
+
...acceptance.criteria.filter(item => item.kind === "command" && item.status !== "pass").map(item => `${item.id}=${item.status}`),
|
|
66
|
+
...(acceptance.targets ?? []).filter(target => target.status !== "ready").map(target => `target ${target.id}=${target.status}`),
|
|
67
|
+
].join(", ")}`);
|
|
68
|
+
if (text.length > 0 && looksLikeHandBack(text) && acceptance?.status !== "awaiting_human") gaps.push(options?.taskAware
|
|
69
|
+
? "the reply deferred the request; check whether authorized work remains or a specific blocker requires the user"
|
|
70
|
+
: "the reply handed the task back instead of finishing it; no one will answer");
|
|
57
71
|
if (gaps.length === 0) return null;
|
|
58
72
|
return {
|
|
59
73
|
reason: "completion_review",
|
|
60
74
|
sources: [current[0]!.id, final.id],
|
|
61
75
|
text: `Harness completion check: ${gaps.join("; ")}. ` +
|
|
62
76
|
`Re-read the original request (event ${current[0]!.id}) and check each requirement against evidence in this session. ` +
|
|
63
|
-
|
|
77
|
+
(options?.taskAware && lastEdit < 0
|
|
78
|
+
? "Use original sources for research and full inputs for calculations; do not invent command evidence. Required human acceptance remains pending. Finish with the result or the specific blocker. "
|
|
79
|
+
: "Run the command that verifies the result, or finish with the specific blocker that prevents verification. ") +
|
|
64
80
|
"This check runs once per request.",
|
|
65
81
|
};
|
|
66
82
|
}
|