@visual-z/xeno 0.8.4 → 0.9.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 +8 -0
- package/README.md +31 -746
- package/bin/xeno.exe +26 -0
- package/install.cjs +88 -0
- package/package.json +18 -48
- package/docs/app-server.md +0 -115
- package/docs/foundations.md +0 -88
- package/docs/install.md +0 -244
- package/docs/providers.md +0 -172
- package/docs/research/foundation-clients-2026-10-09.md +0 -27
- package/docs/research/foundation-editing-2026-10-09.md +0 -56
- package/docs/research/foundation-network-2026-10-09.md +0 -37
- package/docs/research/foundation-workflow-2026-10-09.md +0 -37
- package/src/apps/acp-server.ts +0 -41
- package/src/apps/agent-profiles.ts +0 -19
- package/src/apps/app-server.ts +0 -30
- package/src/apps/approve.ts +0 -49
- package/src/apps/cli.ts +0 -99
- package/src/apps/commands.ts +0 -319
- package/src/apps/completions.ts +0 -87
- package/src/apps/control-transport.ts +0 -90
- package/src/apps/export-cli.ts +0 -24
- package/src/apps/goal-flow.ts +0 -70
- package/src/apps/image-draft.ts +0 -46
- package/src/apps/input.ts +0 -312
- package/src/apps/model-catalog.ts +0 -69
- package/src/apps/preferences.ts +0 -20
- package/src/apps/project-trust.ts +0 -9
- package/src/apps/provider-connect.ts +0 -346
- package/src/apps/question-ui.ts +0 -27
- package/src/apps/render.ts +0 -181
- package/src/apps/resources.ts +0 -66
- package/src/apps/review.ts +0 -109
- package/src/apps/session-hub.ts +0 -96
- package/src/apps/session-lifecycle.ts +0 -64
- package/src/apps/shell.ts +0 -2071
- package/src/apps/startup-input.ts +0 -135
- package/src/apps/startup.ts +0 -1677
- package/src/apps/ui/app.ts +0 -1818
- package/src/apps/ui/control-model.ts +0 -43
- package/src/apps/ui/extension-host.ts +0 -194
- package/src/apps/ui/image-cache.ts +0 -121
- package/src/apps/ui/palette.ts +0 -88
- package/src/apps/ui/project.ts +0 -127
- package/src/apps/ui/standalone.ts +0 -148
- package/src/apps/ui/status-model.ts +0 -68
- package/src/apps/ui/surface.ts +0 -91
- package/src/apps/ui/theme.ts +0 -86
- package/src/apps/update-check.ts +0 -112
- package/src/apps/workflow.ts +0 -58
- package/src/core/acceptance.ts +0 -139
- package/src/core/action-defaults.ts +0 -58
- package/src/core/activity-status.ts +0 -52
- package/src/core/agent-profile.ts +0 -130
- package/src/core/attachment-input.ts +0 -17
- package/src/core/blind-checks.ts +0 -72
- package/src/core/boundary.ts +0 -174
- package/src/core/branch-receipt.ts +0 -26
- package/src/core/catalog-merge.ts +0 -19
- package/src/core/checkpoints.ts +0 -85
- package/src/core/churn.ts +0 -61
- package/src/core/command-artifacts.ts +0 -94
- package/src/core/command-contract.ts +0 -151
- package/src/core/completion-action.ts +0 -20
- package/src/core/completion-review.ts +0 -98
- package/src/core/context-detail.ts +0 -37
- package/src/core/context-policy.ts +0 -133
- package/src/core/context-window.ts +0 -54
- package/src/core/continuation.ts +0 -130
- package/src/core/control-protocol.ts +0 -69
- package/src/core/control-view.ts +0 -39
- package/src/core/cycle.ts +0 -17
- package/src/core/data-query.ts +0 -201
- package/src/core/deadline.ts +0 -40
- package/src/core/derive.ts +0 -516
- package/src/core/draft-images.ts +0 -56
- package/src/core/edit-plan.ts +0 -200
- package/src/core/environment.ts +0 -181
- package/src/core/event-validation.ts +0 -220
- package/src/core/events.ts +0 -670
- package/src/core/evidence-ladder.ts +0 -62
- package/src/core/evidence-links.ts +0 -68
- package/src/core/evidence.ts +0 -240
- package/src/core/execution.ts +0 -18
- package/src/core/exhaustive.ts +0 -18
- package/src/core/export.ts +0 -179
- package/src/core/extension-flags.ts +0 -14
- package/src/core/external-effects.ts +0 -488
- package/src/core/failure-recovery.ts +0 -173
- package/src/core/file-precondition.ts +0 -78
- package/src/core/file-references.ts +0 -35
- package/src/core/frontmatter.ts +0 -213
- package/src/core/git-status.ts +0 -29
- package/src/core/goal-view.ts +0 -64
- package/src/core/goal.ts +0 -200
- package/src/core/handoff.ts +0 -479
- package/src/core/history-query.ts +0 -139
- package/src/core/history.ts +0 -237
- package/src/core/image-limits.ts +0 -72
- package/src/core/images.ts +0 -46
- package/src/core/integration-health.ts +0 -34
- package/src/core/json-event.ts +0 -131
- package/src/core/json-pointer.ts +0 -46
- package/src/core/keymap.ts +0 -153
- package/src/core/lsp-framing.ts +0 -34
- package/src/core/mcp-config.ts +0 -68
- package/src/core/model-picker.ts +0 -64
- package/src/core/model-preferences.ts +0 -28
- package/src/core/model-replay.ts +0 -22
- package/src/core/model-scope.ts +0 -53
- package/src/core/note-guidance.ts +0 -34
- package/src/core/oauth-fields.ts +0 -44
- package/src/core/observation-view.ts +0 -152
- package/src/core/outcome.ts +0 -44
- package/src/core/overflow.ts +0 -59
- package/src/core/packages.ts +0 -47
- package/src/core/patch.ts +0 -130
- package/src/core/path-suggestions.ts +0 -59
- package/src/core/permissions.ts +0 -410
- package/src/core/process-receipt.ts +0 -105
- package/src/core/process-state.ts +0 -37
- package/src/core/program-status.ts +0 -40
- package/src/core/progress.ts +0 -150
- package/src/core/project.ts +0 -431
- package/src/core/prompt-policy.ts +0 -46
- package/src/core/prompts.ts +0 -111
- package/src/core/question.ts +0 -94
- package/src/core/queue.ts +0 -78
- package/src/core/read-bounds.ts +0 -24
- package/src/core/read-observation.ts +0 -106
- package/src/core/recovery-sources.ts +0 -74
- package/src/core/regex-dialect.ts +0 -25
- package/src/core/request-budget.ts +0 -23
- package/src/core/request-progress.ts +0 -87
- package/src/core/request-state.ts +0 -273
- package/src/core/resource-selection.ts +0 -36
- package/src/core/response-recovery.ts +0 -59
- package/src/core/resume.ts +0 -59
- package/src/core/review.ts +0 -68
- package/src/core/ripgrep-release.ts +0 -64
- package/src/core/runtime-target.ts +0 -94
- package/src/core/scheduling.ts +0 -8
- package/src/core/search-observation.ts +0 -97
- package/src/core/secrets.ts +0 -188
- package/src/core/session-archive.ts +0 -4
- package/src/core/session-browser.ts +0 -82
- package/src/core/session-entry.ts +0 -61
- package/src/core/session-exit.ts +0 -42
- package/src/core/session-format.ts +0 -169
- package/src/core/session-position.ts +0 -26
- package/src/core/session-tree.ts +0 -147
- package/src/core/settings.ts +0 -192
- package/src/core/skills.ts +0 -165
- package/src/core/sql-query.ts +0 -211
- package/src/core/stall.ts +0 -128
- package/src/core/stop-report.ts +0 -68
- package/src/core/text-normalize.ts +0 -81
- package/src/core/theme.ts +0 -166
- package/src/core/thinking.ts +0 -26
- package/src/core/tokens.ts +0 -151
- package/src/core/tool-contract.ts +0 -153
- package/src/core/tool-discovery.ts +0 -87
- package/src/core/tool-failure.ts +0 -81
- package/src/core/tool-input.ts +0 -44
- package/src/core/transcript.ts +0 -108
- package/src/core/tui-mode.ts +0 -29
- package/src/core/update-check.ts +0 -157
- package/src/core/version.ts +0 -55
- package/src/core/web-content.ts +0 -80
- package/src/core/work-state.ts +0 -154
- package/src/core/workflow-mode.ts +0 -34
- package/src/core/working-set.ts +0 -119
- package/src/core/yaml.ts +0 -282
- package/src/runtime/acp-agent.ts +0 -289
- package/src/runtime/agent-profiles.ts +0 -21
- package/src/runtime/auth.ts +0 -367
- package/src/runtime/blind-check.ts +0 -36
- package/src/runtime/branch-jobs.ts +0 -36
- package/src/runtime/catalog.ts +0 -285
- package/src/runtime/clipboard.ts +0 -125
- package/src/runtime/command-artifacts.ts +0 -64
- package/src/runtime/connection-reuse.ts +0 -40
- package/src/runtime/control-client.ts +0 -149
- package/src/runtime/control-connection.ts +0 -139
- package/src/runtime/counter-check.ts +0 -74
- package/src/runtime/environment-tools.ts +0 -60
- package/src/runtime/evidence-notes.ts +0 -35
- package/src/runtime/exec-sessions.ts +0 -456
- package/src/runtime/execution-control.ts +0 -40
- package/src/runtime/extension-loader.ts +0 -37
- package/src/runtime/extension-ui.ts +0 -47
- package/src/runtime/extensions.ts +0 -370
- package/src/runtime/formatter-tool.ts +0 -38
- package/src/runtime/formatter.ts +0 -50
- package/src/runtime/foundation.ts +0 -116
- package/src/runtime/history-cache.ts +0 -57
- package/src/runtime/history-tools.ts +0 -168
- package/src/runtime/images.ts +0 -80
- package/src/runtime/integrations.ts +0 -216
- package/src/runtime/loop.ts +0 -3108
- package/src/runtime/lsp-client.ts +0 -201
- package/src/runtime/mcp-client.ts +0 -264
- package/src/runtime/mcp-oauth.ts +0 -168
- package/src/runtime/mcp-schema.ts +0 -51
- package/src/runtime/model-config.ts +0 -232
- package/src/runtime/model-data.LICENSE +0 -27
- package/src/runtime/model-data.ts +0 -21
- package/src/runtime/model-discovery.ts +0 -208
- package/src/runtime/model-replay.ts +0 -10
- package/src/runtime/model-scope.ts +0 -20
- package/src/runtime/model-sources.ts +0 -151
- package/src/runtime/model.ts +0 -686
- package/src/runtime/models-fetch.ts +0 -57
- package/src/runtime/oauth-device.ts +0 -86
- package/src/runtime/oauth.ts +0 -853
- package/src/runtime/open-image.ts +0 -42
- package/src/runtime/openai-responses.ts +0 -304
- package/src/runtime/openai.ts +0 -697
- package/src/runtime/output-schema.ts +0 -26
- package/src/runtime/package-manager.ts +0 -125
- package/src/runtime/patch-tool.ts +0 -38
- package/src/runtime/permission-resources.ts +0 -18
- package/src/runtime/pi-provider.ts +0 -423
- package/src/runtime/program-status.ts +0 -38
- package/src/runtime/progress.ts +0 -26
- package/src/runtime/provider-access.ts +0 -21
- package/src/runtime/provider-connect.ts +0 -109
- package/src/runtime/provider-presets.ts +0 -98
- package/src/runtime/provider-protocols.ts +0 -29
- package/src/runtime/provider-request.ts +0 -87
- package/src/runtime/provider-tool-policy.ts +0 -33
- package/src/runtime/provider.ts +0 -580
- package/src/runtime/question-tool.ts +0 -62
- package/src/runtime/retry.ts +0 -447
- package/src/runtime/review-tool.ts +0 -48
- package/src/runtime/rich-text.ts +0 -36
- package/src/runtime/runtime-target.ts +0 -149
- package/src/runtime/secrets.ts +0 -79
- package/src/runtime/self-update.ts +0 -74
- package/src/runtime/session-control.ts +0 -265
- package/src/runtime/session-hub.ts +0 -85
- package/src/runtime/stream-idle.ts +0 -80
- package/src/runtime/tool-broker.ts +0 -137
- package/src/runtime/tool-discovery.ts +0 -58
- package/src/runtime/tool-queue.ts +0 -77
- package/src/runtime/tools.ts +0 -1820
- package/src/runtime/turn-effects.ts +0 -31
- package/src/runtime/update-check.ts +0 -48
- package/src/runtime/usage.ts +0 -253
- package/src/runtime/version.ts +0 -12
- package/src/runtime/web-http.ts +0 -56
- package/src/runtime/web-tools.ts +0 -88
- package/src/runtime/working-notes.ts +0 -60
- package/src/runtime/workspace-lifecycle.ts +0 -141
- package/src/store/acceptance.ts +0 -15
- package/src/store/agent-profile-files.ts +0 -13
- package/src/store/atomic.ts +0 -31
- package/src/store/auth-store.ts +0 -50
- package/src/store/blind-checks.ts +0 -32
- package/src/store/blobs.ts +0 -143
- package/src/store/checkpoint-history.ts +0 -32
- package/src/store/command-artifacts.ts +0 -96
- package/src/store/command.ts +0 -111
- package/src/store/config.ts +0 -69
- package/src/store/context-files.ts +0 -436
- package/src/store/file-checkpoints.ts +0 -108
- package/src/store/file-index.ts +0 -174
- package/src/store/foundation-config.ts +0 -24
- package/src/store/git-checkpoints.ts +0 -67
- package/src/store/git-status.ts +0 -15
- package/src/store/history-search.ts +0 -19
- package/src/store/log.ts +0 -725
- package/src/store/lsp-document.ts +0 -30
- package/src/store/mcp-credentials.ts +0 -85
- package/src/store/models.ts +0 -60
- package/src/store/observations.ts +0 -224
- package/src/store/packages.ts +0 -98
- package/src/store/patch.ts +0 -92
- package/src/store/permission-paths.ts +0 -34
- package/src/store/process-receipts.ts +0 -34
- package/src/store/query-source.ts +0 -49
- package/src/store/recovery.ts +0 -44
- package/src/store/replay.ts +0 -76
- package/src/store/resource-index.ts +0 -170
- package/src/store/resources.ts +0 -285
- package/src/store/restore.ts +0 -110
- package/src/store/review.ts +0 -109
- package/src/store/runtime-target.ts +0 -64
- package/src/store/search.ts +0 -728
- package/src/store/secrets.ts +0 -44
- package/src/store/serial.ts +0 -13
- package/src/store/session-archive.ts +0 -165
- package/src/store/session-browser.ts +0 -68
- package/src/store/session-position.ts +0 -27
- package/src/store/sessions.ts +0 -525
- package/src/store/source-cancellation.ts +0 -6
- package/src/store/tool-binaries.ts +0 -90
- package/src/store/trust.ts +0 -82
- package/src/store/update-cache.ts +0 -36
- package/src/store/user-settings.ts +0 -30
- package/src/store/workspace-clone.ts +0 -42
- package/src/store/workspace-observation.ts +0 -113
- package/src/store/workspace.ts +0 -115
- package/src/store/worktree.ts +0 -278
- package/src/tui/ansi-spans.ts +0 -126
- package/src/tui/diff.ts +0 -160
- package/src/tui/exhaustive.ts +0 -14
- package/src/tui/fuzzy.ts +0 -75
- package/src/tui/style.ts +0 -208
- package/src/tui/ui/atomic-draft.ts +0 -121
- package/src/tui/ui/branch-panel.ts +0 -47
- package/src/tui/ui/canvas.ts +0 -96
- package/src/tui/ui/colors.ts +0 -70
- package/src/tui/ui/composer.ts +0 -199
- package/src/tui/ui/control-panel.ts +0 -25
- package/src/tui/ui/control-sidebar.ts +0 -49
- package/src/tui/ui/dialog.ts +0 -398
- package/src/tui/ui/draft.ts +0 -23
- package/src/tui/ui/duration.ts +0 -16
- package/src/tui/ui/edge.ts +0 -186
- package/src/tui/ui/image-size.ts +0 -44
- package/src/tui/ui/ink.ts +0 -210
- package/src/tui/ui/items.ts +0 -42
- package/src/tui/ui/keys.ts +0 -40
- package/src/tui/ui/layout.ts +0 -74
- package/src/tui/ui/list-model.ts +0 -86
- package/src/tui/ui/list-view.ts +0 -172
- package/src/tui/ui/mascot.ts +0 -67
- package/src/tui/ui/messages.ts +0 -518
- package/src/tui/ui/motion.ts +0 -28
- package/src/tui/ui/paths.ts +0 -10
- package/src/tui/ui/pictures.ts +0 -108
- package/src/tui/ui/prompt-history.ts +0 -60
- package/src/tui/ui/screen.ts +0 -579
- package/src/tui/ui/status.ts +0 -403
- package/src/tui/ui/styled.ts +0 -85
- package/src/tui/ui/tool-card.ts +0 -150
- package/src/tui/width.ts +0 -567
- package/src/vendor/pi-rich/LICENSE.upstream +0 -21
- package/src/vendor/pi-rich/UPSTREAM.json +0 -17
- package/src/vendor/pi-rich/clipboard-command.ts +0 -44
- package/src/vendor/pi-rich/exif-orientation.ts +0 -182
- package/src/vendor/pi-rich/html.ts +0 -51
- package/src/vendor/pi-rich/image-convert.ts +0 -49
- package/src/vendor/pi-rich/image-resize-core.ts +0 -164
- package/src/vendor/pi-rich/image-resize-worker.ts +0 -42
- package/src/vendor/pi-rich/image-resize.ts +0 -123
- package/src/vendor/pi-rich/mime.ts +0 -116
- package/src/vendor/pi-rich/photon.ts +0 -139
- package/src/vendor/pi-rich/syntax-highlight.ts +0 -214
|
@@ -1,273 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The request ledger (S.1, Q23 / D8 / D11): what the current request has
|
|
3
|
-
* evidence for, derived only from the selected log ancestry.
|
|
4
|
-
*
|
|
5
|
-
* Every view of "is this request done" -- the completion check, the goal
|
|
6
|
-
* panel, terminal status, branch receipts -- maps from this one derivation,
|
|
7
|
-
* so they cannot disagree. Only harness-observed facts move it: command
|
|
8
|
-
* receipts with their workspace fingerprints, file versions, and the user's
|
|
9
|
-
* acceptance decisions. A model note can describe a blocker; it never changes
|
|
10
|
-
* the status.
|
|
11
|
-
*/
|
|
12
|
-
import type { LogEvent } from "./events.ts";
|
|
13
|
-
import { counterVerdict, deriveAcceptance, type AcceptanceState, type CounterVerdict } from "./acceptance.ts";
|
|
14
|
-
import { changedPaths, workspaceChanged } from "./checkpoints.ts";
|
|
15
|
-
import { evidenceStale, isCommandResult, resolveEvidence } from "./evidence.ts";
|
|
16
|
-
import { executionOutcome, modelOutcome, type ExecutionOutcome } from "./outcome.ts";
|
|
17
|
-
import { deriveWorkItems, type WorkItem } from "./work-state.ts";
|
|
18
|
-
import { deriveRequestProgress, type RequestProgress, type RequestRecord, type RequestSource } from "./request-progress.ts";
|
|
19
|
-
|
|
20
|
-
export type RequestPart = { node: number; events: readonly LogEvent[] };
|
|
21
|
-
export type RequestCommand = {
|
|
22
|
-
source: RequestSource; command: string; exit: number; status: "pass" | "fail" | "stale" | "superseded";
|
|
23
|
-
gate?: string; workspace_sha256?: string; workspace_scope?: "source-v1"; criteria: string[];
|
|
24
|
-
};
|
|
25
|
-
export type RequestArtifact = { source: RequestSource; path: string; sha256: string | null;
|
|
26
|
-
kind: "read" | "edit" | "checkpoint" | "command_output" };
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* One requirement this request must meet. With a declared contract these are
|
|
30
|
-
* its criteria. Without one, a request that changed the workspace still has
|
|
31
|
-
* one requirement: a successful check after its last change.
|
|
32
|
-
*/
|
|
33
|
-
export type RequestCriterion = {
|
|
34
|
-
id: string; kind: "command" | "human" | "fresh_check"; description: string;
|
|
35
|
-
status: "pass" | "fail" | "stale" | "not_run" | "awaiting_human" | "accepted";
|
|
36
|
-
command?: string; evidence?: string;
|
|
37
|
-
/** A declared guard: it must pass before and after the work, rather than fail before it (S.7). */
|
|
38
|
-
guard?: true;
|
|
39
|
-
/** The latest counter-check of this criterion in the request, when one ran, read against its role. */
|
|
40
|
-
counter?: CounterVerdict;
|
|
41
|
-
/**
|
|
42
|
-
* Consecutive runs of this exact command that failed with the same exit code
|
|
43
|
-
* against the same workspace. Two or more mean rerunning it unchanged will
|
|
44
|
-
* not change the answer: the work, or the check itself, must change.
|
|
45
|
-
*/
|
|
46
|
-
repeated_failures?: number;
|
|
47
|
-
/** A fresh check under strict verification: only a configured gate counts (O.11). */
|
|
48
|
-
gate_only?: true;
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
/** The rungs of the evidence ladder this derivation can establish. */
|
|
52
|
-
export type RequestEvidence = {
|
|
53
|
-
/** Every required check ran successfully against the current workspace. */
|
|
54
|
-
ran: "pass" | "fail" | "stale" | "missing" | "not_applicable";
|
|
55
|
-
/** Every required human criterion was accepted by the user. */
|
|
56
|
-
human: "accepted" | "pending" | "not_required";
|
|
57
|
-
/**
|
|
58
|
-
* Every passing check meant to test the change failed once this request's
|
|
59
|
-
* changes were undone (feat-counter-check). `missing`: every check is a guard
|
|
60
|
-
* that held, so nothing shows the work changed anything (S.7).
|
|
61
|
-
*/
|
|
62
|
-
discriminating: "pass" | "vacuous" | "unavailable" | "not_run" | "missing" | "not_applicable";
|
|
63
|
-
/** Blind checks the model never touched passed on the final result (feat-blind-checks). */
|
|
64
|
-
independent: "pass" | "fail" | "exposed" | "unavailable" | "not_run" | "not_applicable";
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
export type RequestStatus = "not_applicable" | "unchecked" | "checked" | "needs_work" | "awaiting_human" | "verified";
|
|
68
|
-
|
|
69
|
-
export type RequestState = {
|
|
70
|
-
request: RequestSource; inputs: RequestSource[];
|
|
71
|
-
/**
|
|
72
|
-
* Without a contract: not_applicable (nothing changed), unchecked, or
|
|
73
|
-
* checked. With one: needs_work, awaiting_human, verified, or
|
|
74
|
-
* not_applicable for a changes-scoped contract over an unchanged workspace.
|
|
75
|
-
*/
|
|
76
|
-
status: RequestStatus;
|
|
77
|
-
criteria: RequestCriterion[]; evidence: RequestEvidence; unmet: string[];
|
|
78
|
-
execution?: ExecutionOutcome; acceptance?: AcceptanceState;
|
|
79
|
-
commands: RequestCommand[]; artifacts: RequestArtifact[]; work: WorkItem[];
|
|
80
|
-
/** What the model said blocks it. Shown to the user; never a verdict (D11). */
|
|
81
|
-
reported_blockers: { source: RequestSource; reason: string }[];
|
|
82
|
-
progress: RequestProgress;
|
|
83
|
-
changed: boolean; used_tools: boolean; reviewed: boolean;
|
|
84
|
-
final?: { source: RequestSource; text: string; has_tools: boolean };
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
export type LedgerOptions = {
|
|
88
|
-
/** The CLI's strict verification: only a configured gate command counts as the fresh check. */
|
|
89
|
-
strict?: boolean;
|
|
90
|
-
/** The session declares blind checks, so their absence means "not run" rather than "not applicable". */
|
|
91
|
-
blind?: boolean;
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
/** New steering events refer to their original user event. A user event without that link opens a request. */
|
|
95
|
-
export function currentRequestRecords(parts: readonly RequestPart[]): RequestRecord[] {
|
|
96
|
-
const records = parts.flatMap(part => part.events.map(event => ({ node: part.node, event })));
|
|
97
|
-
const last = records.findLastIndex(({ event }) => event.t === "user");
|
|
98
|
-
if (last < 0) return [];
|
|
99
|
-
const user = records[last]!.event;
|
|
100
|
-
if (user.t !== "user") return [];
|
|
101
|
-
const start = user.request === undefined ? last : records.findIndex(({ event }) => event.t === "user" && event.id === user.request);
|
|
102
|
-
// Never reach through an unrelated user request because of an invalid reference.
|
|
103
|
-
if (start < 0 || records.slice(start + 1, last + 1).some(({ event }) => event.t === "user" && event.request !== user.request)) return records.slice(last);
|
|
104
|
-
return records.slice(start);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* The current request's events in one flat stream: the one slicer every
|
|
109
|
-
* per-request notice uses, so steering never restarts a request's budget,
|
|
110
|
-
* notices or failure counts. Empty when the stream holds no user event.
|
|
111
|
-
*/
|
|
112
|
-
export function currentRequestEvents(events: readonly LogEvent[]): LogEvent[] {
|
|
113
|
-
return currentRequestRecords([{ node: 0, events }]).map(record => record.event);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
const FRESH_CHECK = "a successful check after the last change";
|
|
117
|
-
|
|
118
|
-
export function deriveRequestState(parts: readonly RequestPart[], options: LedgerOptions = {}): RequestState | undefined {
|
|
119
|
-
const records = currentRequestRecords(parts);
|
|
120
|
-
const first = records[0];
|
|
121
|
-
if (!first) return undefined;
|
|
122
|
-
const events = records.map(record => record.event);
|
|
123
|
-
const source = ({ node, event }: RequestRecord): RequestSource => ({ node, event: event.id });
|
|
124
|
-
const request = source(first);
|
|
125
|
-
const acceptance = deriveAcceptance(undefined, events.filter(event => event.t !== "acceptance_contract" || event.request === request.event));
|
|
126
|
-
const commands: (RequestCommand & { index: number })[] = [], artifacts: RequestArtifact[] = [];
|
|
127
|
-
const lastCommands = new Map<string, string>();
|
|
128
|
-
for (const event of events) if (isCommandResult(event)) lastCommands.set(event.effect.cmd, event.id);
|
|
129
|
-
let work: WorkItem[] = [];
|
|
130
|
-
const ids = new Set(events.map(event => event.id)), completed = new Set<string>();
|
|
131
|
-
// Keep the existing work-item same-segment evidence contract (D11/O.2).
|
|
132
|
-
for (const part of parts) {
|
|
133
|
-
work = deriveWorkItems(work, part.events);
|
|
134
|
-
for (let index = 0; index < part.events.length; index++) {
|
|
135
|
-
const event = part.events[index]!;
|
|
136
|
-
// Untyped completed claims supersede a same-named blocker only when the
|
|
137
|
-
// original same-segment evidence resolves, just as in deriveHandoff.
|
|
138
|
-
if (event.t === "claim" && event.work_id === undefined && ids.has(event.id) &&
|
|
139
|
-
resolveEvidence(part.events.slice(0, index), event.evidence).ok) completed.add(event.what);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
work = work.filter(item => ids.has(item.opened_at) || ids.has(item.updated_at));
|
|
143
|
-
const reported_blockers: RequestState["reported_blockers"] = [];
|
|
144
|
-
let execution: ExecutionOutcome | undefined, final: RequestState["final"], changed = false, lastChange = -1;
|
|
145
|
-
records.forEach((record, index) => {
|
|
146
|
-
const event = record.event, ref = source(record);
|
|
147
|
-
if (event.t === "user") { execution = undefined; final = undefined; }
|
|
148
|
-
else if (event.t === "assistant") {
|
|
149
|
-
final = { source: ref, text: event.blocks.flatMap(block => block.type === "text" ? [block.text] : []).join("\n").trim(),
|
|
150
|
-
has_tools: event.blocks.some(block => block.type === "tool_use") };
|
|
151
|
-
execution = final.has_tools ? undefined : modelOutcome(event.stop_reason ?? null);
|
|
152
|
-
} else if (event.t === "outcome") execution = { ...event.outcome };
|
|
153
|
-
else if (event.t === "interrupt") execution = executionOutcome("cancelled");
|
|
154
|
-
else if (event.t === "guidance" && (event.reason === "completion_review" || event.reason === "output_recovery")) execution = undefined;
|
|
155
|
-
if (event.t === "blocked" && (event.work_id ? work.some(item => item.id === event.work_id && item.status === "blocked") : !completed.has(event.what))) {
|
|
156
|
-
reported_blockers.push({ source: ref, reason: event.reason });
|
|
157
|
-
}
|
|
158
|
-
if (workspaceChanged(event)) { changed = true; lastChange = index; }
|
|
159
|
-
if (isCommandResult(event)) {
|
|
160
|
-
const effect = event.effect;
|
|
161
|
-
const evidence = { command: effect.cmd, exit: effect.exit, event_id: event.id, at: event.at,
|
|
162
|
-
...(effect.workspace_sha256 === undefined ? {} : { workspace_sha256: effect.workspace_sha256 }),
|
|
163
|
-
...(effect.workspace_scope === undefined ? {} : { workspace_scope: effect.workspace_scope }) };
|
|
164
|
-
commands.push({ index, source: ref, command: effect.cmd, exit: effect.exit,
|
|
165
|
-
status: !event.ok || effect.exit !== 0 ? "fail" : effect.workspace_observed === false || evidenceStale(events, evidence) ? "stale"
|
|
166
|
-
: lastCommands.get(effect.cmd) !== event.id ? "superseded" : "pass",
|
|
167
|
-
...(effect.gate === undefined ? {} : { gate: effect.gate }),
|
|
168
|
-
...(effect.workspace_sha256 === undefined ? {} : { workspace_sha256: effect.workspace_sha256 }),
|
|
169
|
-
...(effect.workspace_scope === undefined ? {} : { workspace_scope: effect.workspace_scope }),
|
|
170
|
-
criteria: acceptance?.criteria.filter(item => item.kind === "command" && item.command === effect.cmd).map(item => item.id) ?? [] });
|
|
171
|
-
for (const output of effect.artifacts?.outputs ?? []) if (output.after.state === "observed") artifacts.push({
|
|
172
|
-
source: ref, path: output.path, sha256: output.after.sha256, kind: "command_output" });
|
|
173
|
-
} else if (event.t === "tool_result" && (event.effect?.kind === "read" || event.effect?.kind === "edit")) {
|
|
174
|
-
const effect = event.effect;
|
|
175
|
-
artifacts.push({ source: ref, path: effect.path, sha256: effect.kind === "read" ? effect.sha256 : effect.sha256_after, kind: effect.kind });
|
|
176
|
-
} else if (event.t === "checkpoint" && event.phase === "applied") {
|
|
177
|
-
for (const path of changedPaths(event.changes)) artifacts.push({ source: ref, path, sha256: event.changes[path]!.after?.sha256 ?? null, kind: "checkpoint" });
|
|
178
|
-
}
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
const ledger = acceptance ? contractLedger(acceptance) : freshCheckLedger(commands, changed, lastChange, options);
|
|
182
|
-
const { criteria, status } = ledger;
|
|
183
|
-
for (const event of events) if (event.t === "counter_check" && event.request === request.event) {
|
|
184
|
-
const criterion = criteria.find(item => item.id === event.criterion && item.kind === "command");
|
|
185
|
-
if (criterion) criterion.counter = counterVerdict(event);
|
|
186
|
-
}
|
|
187
|
-
for (const criterion of criteria) if (criterion.kind === "command" && criterion.command && criterion.status === "fail") {
|
|
188
|
-
const runs = commands.filter(command => command.command === criterion.command);
|
|
189
|
-
const last = runs.at(-1);
|
|
190
|
-
let streak = 0;
|
|
191
|
-
for (let i = runs.length - 1; i >= 0; i--) {
|
|
192
|
-
const run = runs[i]!;
|
|
193
|
-
if (run.status !== "fail" || run.exit !== last!.exit || run.workspace_sha256 === undefined || run.workspace_sha256 !== last!.workspace_sha256) break;
|
|
194
|
-
streak++;
|
|
195
|
-
}
|
|
196
|
-
if (streak >= 2) criterion.repeated_failures = streak;
|
|
197
|
-
}
|
|
198
|
-
const blind = new Map<string, Extract<LogEvent, { t: "blind_check" }>>();
|
|
199
|
-
for (const event of events) if (event.t === "blind_check" && event.request === request.event) blind.set(event.check, event);
|
|
200
|
-
const blindRuns = [...blind.values()];
|
|
201
|
-
const independent: RequestEvidence["independent"] = blindRuns.length === 0 ? (options.blind ? "not_run" : "not_applicable")
|
|
202
|
-
: blindRuns.some(run => "exit" in run && run.exit !== 0) ? "fail" : blindRuns.some(run => "reason" in run) ? "unavailable"
|
|
203
|
-
: blindRuns.some(run => run.exposed) ? "exposed" : "pass";
|
|
204
|
-
const evidence: RequestEvidence = { ...ledger.evidence, independent, discriminating: testsTheChange(criteria) };
|
|
205
|
-
const unmet = criteria.filter(item => item.status !== "pass" && item.status !== "accepted").map(item => `${item.description}: ${item.status}`);
|
|
206
|
-
return { request, inputs: records.filter(record => record.event.t === "user").map(source),
|
|
207
|
-
status, criteria, evidence, unmet,
|
|
208
|
-
...(execution ? { execution } : {}), ...(acceptance ? { acceptance } : {}),
|
|
209
|
-
commands: commands.map(({ index: _index, ...command }) => command), artifacts, work, reported_blockers,
|
|
210
|
-
progress: deriveRequestProgress(records), changed,
|
|
211
|
-
used_tools: events.some(event => event.t === "tool_result"),
|
|
212
|
-
reviewed: events.some(event => event.t === "guidance" && event.reason === "completion_review"),
|
|
213
|
-
...(final ? { final } : {}) };
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
/**
|
|
217
|
-
* The discriminating rung (S.1, S.7), decided by the checks meant to test the
|
|
218
|
-
* change: each must fail once the request's changes are undone. A guard that
|
|
219
|
-
* held is what a guard should do, so it neither fails the rung nor holds it
|
|
220
|
-
* back. A guard that did not hold does test the change -- the observation
|
|
221
|
-
* outranks the declaration (D11) -- so it counts for the rung. With only
|
|
222
|
-
* guards, all held, nothing shows the work changed anything: missing. A
|
|
223
|
-
* passing fresh check is a check like any declared command and must test the
|
|
224
|
-
* change too.
|
|
225
|
-
*/
|
|
226
|
-
function testsTheChange(criteria: readonly RequestCriterion[]): RequestEvidence["discriminating"] {
|
|
227
|
-
const tests = criteria.filter(item => item.kind === "command" && !item.guard || item.kind === "fresh_check" && item.status === "pass");
|
|
228
|
-
const guards = criteria.filter(item => item.kind === "command" && item.guard);
|
|
229
|
-
if (tests.length === 0 && guards.length === 0) return "not_applicable";
|
|
230
|
-
if (tests.some(item => item.counter === "vacuous")) return "vacuous";
|
|
231
|
-
if (tests.some(item => item.counter === "unavailable")) return "unavailable";
|
|
232
|
-
if (tests.some(item => item.counter !== "discriminating")) return "not_run";
|
|
233
|
-
if (tests.length > 0 || guards.some(item => item.counter === "not_held")) return "pass";
|
|
234
|
-
if (guards.some(item => item.counter === "unavailable")) return "unavailable";
|
|
235
|
-
return guards.every(item => item.counter === "held") ? "missing" : "not_run";
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
/** A declared contract defines what this request must finish (O.11); nothing is inferred beyond it. */
|
|
239
|
-
type LedgerCore = Pick<RequestState, "criteria" | "status"> & { evidence: Omit<RequestEvidence, "discriminating" | "independent"> };
|
|
240
|
-
|
|
241
|
-
function contractLedger(acceptance: AcceptanceState): LedgerCore {
|
|
242
|
-
const criteria: RequestCriterion[] = acceptance.criteria.map(item => ({ id: item.id, kind: item.kind, description: item.description,
|
|
243
|
-
status: item.status, ...(item.kind === "command" ? { command: item.command, ...(item.guard ? { guard: true as const } : {}) } : {}),
|
|
244
|
-
...(item.evidence ? { evidence: item.evidence.event_id } : item.accepted_event ? { evidence: item.accepted_event } : {}) }));
|
|
245
|
-
const checks = criteria.filter(item => item.kind === "command"), humans = criteria.filter(item => item.kind === "human");
|
|
246
|
-
const targetsReady = (acceptance.targets ?? []).every(target => target.status === "ready");
|
|
247
|
-
const ran: RequestEvidence["ran"] = checks.length === 0 ? "not_applicable"
|
|
248
|
-
: checks.every(item => item.status === "pass") && targetsReady ? "pass"
|
|
249
|
-
: checks.some(item => item.status === "fail") ? "fail" : checks.some(item => item.status === "stale") ? "stale" : "missing";
|
|
250
|
-
return { criteria, status: acceptance.status,
|
|
251
|
-
evidence: { ran, human: humans.length === 0 ? "not_required" : humans.every(item => item.status === "accepted") ? "accepted" : "pending" } };
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
/**
|
|
255
|
-
* Without a contract, a request that changed the workspace still owes one
|
|
256
|
-
* thing: a successful check against the changed workspace. That is all an
|
|
257
|
-
* exit code can say -- not that the request's words were met -- so the status
|
|
258
|
-
* is "checked", never "verified".
|
|
259
|
-
*/
|
|
260
|
-
function freshCheckLedger(commands: readonly (RequestCommand & { index: number })[], changed: boolean, lastChange: number,
|
|
261
|
-
options: LedgerOptions): LedgerCore {
|
|
262
|
-
const none = { human: "not_required" as const };
|
|
263
|
-
if (!changed) return { criteria: [], status: "not_applicable", evidence: { ran: "not_applicable", ...none } };
|
|
264
|
-
const after = commands.filter(command => command.index > lastChange && (!options.strict || command.gate !== undefined));
|
|
265
|
-
const pass = after.find(command => command.status === "pass");
|
|
266
|
-
const status: RequestCriterion["status"] = pass ? "pass" : after.some(command => command.status === "fail") ? "fail"
|
|
267
|
-
: after.some(command => command.status === "stale") ? "stale" : "not_run";
|
|
268
|
-
const criterion: RequestCriterion = { id: "fresh-check", kind: "fresh_check",
|
|
269
|
-
description: options.strict ? `${FRESH_CHECK} (a configured gate)` : FRESH_CHECK, status, ...(options.strict ? { gate_only: true as const } : {}),
|
|
270
|
-
...(pass ? { command: pass.command, evidence: pass.source.event } : {}) };
|
|
271
|
-
return { criteria: [criterion], status: pass ? "checked" : "unchecked",
|
|
272
|
-
evidence: { ran: status === "pass" ? "pass" : status === "not_run" ? "missing" : status, ...none } };
|
|
273
|
-
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
/** Pi +/-/! resource choices; discovery and path resolution belong to store. */
|
|
2
|
-
export type ResourceKind = "skills" | "prompts" | "themes" | "extensions";
|
|
3
|
-
export type ResourceScope = "user" | "project" | "cli";
|
|
4
|
-
export type ResourceOverride = "inherit" | "load" | "skip";
|
|
5
|
-
export interface ResourceEntry {
|
|
6
|
-
kind: ResourceKind;
|
|
7
|
-
path: string;
|
|
8
|
-
name: string;
|
|
9
|
-
scope: ResourceScope;
|
|
10
|
-
source: string;
|
|
11
|
-
enabled: boolean;
|
|
12
|
-
inherited: boolean;
|
|
13
|
-
override: ResourceOverride;
|
|
14
|
-
trusted: boolean;
|
|
15
|
-
package?: { source: string; identity: string; root: string };
|
|
16
|
-
}
|
|
17
|
-
export interface ResourcePattern { pattern: string; enabled: boolean; explicit: boolean }
|
|
18
|
-
export function parseResourcePattern(raw: string): ResourcePattern {
|
|
19
|
-
const first = raw[0];
|
|
20
|
-
return { pattern: ["+", "-", "!"].includes(first ?? "") ? raw.slice(1) : raw,
|
|
21
|
-
enabled: first !== "-" && first !== "!", explicit: first === "+" || first === "-" };
|
|
22
|
-
}
|
|
23
|
-
export function resourceOverride(patterns: readonly string[], matches: (pattern: string) => boolean): ResourceOverride {
|
|
24
|
-
let result: ResourceOverride = "inherit";
|
|
25
|
-
for (const raw of patterns) { const choice = parseResourcePattern(raw); if (matches(choice.pattern)) result = choice.enabled ? "load" : "skip"; }
|
|
26
|
-
return result;
|
|
27
|
-
}
|
|
28
|
-
export function updateResourcePatterns(patterns: readonly string[], path: string, state: ResourceOverride,
|
|
29
|
-
same: (pattern: string) => boolean): string[] {
|
|
30
|
-
const next = patterns.filter(raw => !same(parseResourcePattern(raw).pattern));
|
|
31
|
-
if (state !== "inherit") next.push((state === "load" ? "+" : "-") + path);
|
|
32
|
-
return next;
|
|
33
|
-
}
|
|
34
|
-
export function nextResourceOverride(state: ResourceOverride): ResourceOverride {
|
|
35
|
-
return state === "inherit" ? "load" : state === "load" ? "skip" : "inherit";
|
|
36
|
-
}
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
/** N.12: a returned response is durable evidence, not necessarily a completed action. */
|
|
2
|
-
import type { AssistantBlock, GuidanceEvent, LogEvent } from "./events.ts";
|
|
3
|
-
import { currentRequestEvents } from "./request-state.ts";
|
|
4
|
-
|
|
5
|
-
// One appended attempt is an operational assumption, not a measured optimum.
|
|
6
|
-
// It shares the existing request ceiling and never replays a tool automatically.
|
|
7
|
-
export const DEFAULT_OUTPUT_RECOVERY_LIMIT = 1;
|
|
8
|
-
|
|
9
|
-
export function validateOutputRecoveryLimit(limit: number): void {
|
|
10
|
-
if (!Number.isSafeInteger(limit) || limit < 0) throw new RangeError("output recovery limit must be a nonnegative safe integer");
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/** Both wire dialects have explicit terminal reasons; an unknown reason is not success. */
|
|
14
|
-
export function isCompleteResponse(reason: string | null | undefined): boolean {
|
|
15
|
-
return reason !== undefined && reason !== null && ["stop", "end_turn", "stop_sequence", "tool_calls", "tool_use"].includes(reason);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* A stop that asks for tool execution must carry a tool call. One that does not
|
|
20
|
-
* is a malformed provider response, not the model's answer: taken as a final
|
|
21
|
-
* reply it ends the request with outcome unknown and nothing on screen
|
|
22
|
-
* (session mv28chr8-oc3x, an Anthropic-compatible gateway returning
|
|
23
|
-
* `stop_reason: tool_use` with no content). Adapters reject it inside their
|
|
24
|
-
* retry so the loop only sees a coherent turn or a definite failure.
|
|
25
|
-
*/
|
|
26
|
-
export function isToolStopWithoutCall(stopReason: string | null | undefined, blocks: readonly AssistantBlock[]): boolean {
|
|
27
|
-
return (stopReason === "tool_use" || stopReason === "tool_calls") && !blocks.some(block => block.type === "tool_use");
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export function isOutputLimit(reason: string | null | undefined): boolean {
|
|
31
|
-
return reason === "length" || reason === "max_tokens";
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export function incompleteToolText(reason: string | null | undefined): string {
|
|
35
|
-
return `not executed: the model response ${isOutputLimit(reason) ? "reached its output limit" : "did not finish successfully"}; ` +
|
|
36
|
-
"no tools from that response were run. Regenerate a complete call only if the action is still needed.";
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/** Explicit metadata proves non-execution; legacy pending calls remain unknown (D11). */
|
|
40
|
-
export function knownUnexecutedTool(events: readonly LogEvent[], callId: string): string | null {
|
|
41
|
-
const owner = events.findLast(event => event.t === "assistant" && event.blocks.some(block => block.type === "tool_use" && block.call_id === callId));
|
|
42
|
-
return owner?.t === "assistant" && owner.stop_reason !== undefined && !isCompleteResponse(owner.stop_reason)
|
|
43
|
-
? incompleteToolText(owner.stop_reason) : null;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/** Supply effective ancestry, not only the current segment: cutting must not reset this limit. */
|
|
47
|
-
export function outputRecoveryNotice(events: readonly LogEvent[], limit: number, remainingRequests: number):
|
|
48
|
-
Pick<GuidanceEvent, "reason" | "sources" | "text"> | null {
|
|
49
|
-
validateOutputRecoveryLimit(limit);
|
|
50
|
-
const current = currentRequestEvents(events).slice(1);
|
|
51
|
-
const assistant = current.findLast(event => event.t === "assistant");
|
|
52
|
-
const notices = current.filter(event => event.t === "guidance" && event.reason === "output_recovery");
|
|
53
|
-
if (assistant?.t !== "assistant" || !isOutputLimit(assistant.stop_reason) || notices.length >= limit || remainingRequests < 1 ||
|
|
54
|
-
notices.some(event => event.t === "guidance" && event.sources.includes(assistant.id))) return null;
|
|
55
|
-
return { reason: "output_recovery", sources: [assistant.id], text:
|
|
56
|
-
"Harness: the previous response hit its output token limit and is incomplete. All tool calls in that response were left unexecuted. " +
|
|
57
|
-
"Continue from the existing evidence, avoid repeating the earlier analysis, and give a concise result or the next necessary complete tool call. " +
|
|
58
|
-
"Do not claim unexecuted actions succeeded. This recovery consumes the existing request allowance." };
|
|
59
|
-
}
|
package/src/core/resume.ts
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Picking a session back up where it stopped.
|
|
3
|
-
*
|
|
4
|
-
* Pure: it decides, given a recorded cursor and the model about to run, what
|
|
5
|
-
* should happen. Doing the reading and the refusing in separate places keeps
|
|
6
|
-
* the one interesting rule -- when a resume is unsafe -- testable without a
|
|
7
|
-
* filesystem.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import type { SavedModel } from "./session-entry.ts";
|
|
11
|
-
|
|
12
|
-
export type ResumePlan<T extends SavedModel> =
|
|
13
|
-
| { kind: "fresh" }
|
|
14
|
-
| { kind: "resume"; cursor: T }
|
|
15
|
-
| { kind: "refuse"; reason: string };
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* A session's thinking blocks belong to the model that produced them.
|
|
19
|
-
*
|
|
20
|
-
* Same-model resume preserves the original projection. An explicit model
|
|
21
|
-
* change uses Session.switchModel after opening with the recorded identity
|
|
22
|
-
* (N.8); it never reinterprets signed or opaque thinking during replay.
|
|
23
|
-
*/
|
|
24
|
-
export function planResume<T extends SavedModel>(
|
|
25
|
-
cursor: T | null,
|
|
26
|
-
model: string,
|
|
27
|
-
hasNodes: boolean,
|
|
28
|
-
provider?: string,
|
|
29
|
-
): ResumePlan<T> {
|
|
30
|
-
if (cursor === null) {
|
|
31
|
-
// No cursor and no nodes is simply a new session. No cursor but nodes on
|
|
32
|
-
// disk is a log this build cannot place itself in -- written before the
|
|
33
|
-
// cursor existed, or with a manifest that was lost. Refused rather than
|
|
34
|
-
// guessed: the whole reason the cursor is recorded is that the tree shape
|
|
35
|
-
// cannot be read backwards to find it, and inventing an answer here would
|
|
36
|
-
// reintroduce exactly the ambiguity it exists to remove.
|
|
37
|
-
return hasNodes
|
|
38
|
-
? {
|
|
39
|
-
kind: "refuse",
|
|
40
|
-
reason:
|
|
41
|
-
"this log has nodes but no recorded cursor, so there is no way to " +
|
|
42
|
-
"know which one the session was in. It is intact and nothing will " +
|
|
43
|
-
"be touched -- start a separate session with --fresh",
|
|
44
|
-
}
|
|
45
|
-
: { kind: "fresh" };
|
|
46
|
-
}
|
|
47
|
-
if (cursor.model !== model || (cursor.provider !== undefined && cursor.provider !== provider)) {
|
|
48
|
-
const saved = cursor.provider === undefined ? cursor.model : `${cursor.provider}/${cursor.model}`;
|
|
49
|
-
const target = provider === undefined ? model : `${provider}/${model}`;
|
|
50
|
-
return {
|
|
51
|
-
kind: "refuse",
|
|
52
|
-
reason:
|
|
53
|
-
`this session ran under ${saved}, and ${target} cannot replay its ` +
|
|
54
|
-
`thinking blocks. Resume it with --model ${saved}, or start a ` +
|
|
55
|
-
`separate session with --fresh`,
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
return { kind: "resume", cursor };
|
|
59
|
-
}
|
package/src/core/review.ts
DELETED
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
/** Review scopes and checkpoint projection are data, never authorization (D8 / D11). */
|
|
2
|
-
import type { LogEvent } from "./events.ts";
|
|
3
|
-
import { sameVersion, changedPaths, type FileChanges } from "./checkpoints.ts";
|
|
4
|
-
export type ReviewScope = { kind: "working" } | { kind: "branch"; base: string } |
|
|
5
|
-
{ kind: "commit"; commit: string } | { kind: "last-turn" };
|
|
6
|
-
export interface ReviewSnapshot {
|
|
7
|
-
scope: ReviewScope;
|
|
8
|
-
label: string;
|
|
9
|
-
diff: string;
|
|
10
|
-
paths: string[];
|
|
11
|
-
limitations: string[];
|
|
12
|
-
/** Resolved commits are immutable review premises, never raw shell expressions. */
|
|
13
|
-
base?: string;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/** Fold observed operations after the latest request; uncaptured effects remain unknown. */
|
|
17
|
-
export function lastTurnChanges(events: readonly LogEvent[]): { changes: FileChanges; limitations: string[] } {
|
|
18
|
-
let start = 0;
|
|
19
|
-
for (let i = events.length - 1; i >= 0; i--) if (events[i]!.t === "user" || events[i]!.t === "user_bash") { start = i + 1; break; }
|
|
20
|
-
const changes: FileChanges = Object.create(null) as FileChanges;
|
|
21
|
-
const limitations = new Set<string>(["Last-turn diff covers durable file checkpoints; uncaptured command or external changes are unknown."]);
|
|
22
|
-
const suffix = events.slice(start);
|
|
23
|
-
const finalPhases = new Map(suffix.filter(event => event.t === "checkpoint").map(event => [event.operation, event.phase]));
|
|
24
|
-
for (const event of suffix) {
|
|
25
|
-
if (event.t !== "checkpoint") continue;
|
|
26
|
-
if (event.phase === "unavailable" && finalPhases.get(event.operation) !== "applied") limitations.add(event.reason ?? "A file checkpoint is unavailable.");
|
|
27
|
-
if (event.phase !== "applied" || finalPhases.get(event.operation) !== "applied") continue;
|
|
28
|
-
for (const path of changedPaths(event.changes)) {
|
|
29
|
-
const next = event.changes[path]!, previous = changes[path];
|
|
30
|
-
if (previous && !sameVersion(previous.after, next.before)) throw new Error(`discontinuous last-turn checkpoint: ${path}; external changes cannot be attributed`);
|
|
31
|
-
changes[path] = { before: previous ? previous.before : next.before, after: next.after };
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
return { changes: Object.fromEntries(changedPaths(changes).map(path => [path, changes[path]!])), limitations: [...limitations] };
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/** A linear contiguous hunk avoids quadratic whole-file diff memory. No bytes are omitted. */
|
|
38
|
-
export function unifiedFileDiff(path: string, before: string | null, after: string | null, destination = path): string {
|
|
39
|
-
if (before === after && path === destination) return "";
|
|
40
|
-
const lines = (text: string | null): { text: string; newline: boolean }[] => {
|
|
41
|
-
if (!text) return [];
|
|
42
|
-
const result = text.split("\n");
|
|
43
|
-
if (text.endsWith("\n")) result.pop();
|
|
44
|
-
return result.map((line, index) => ({ text: line, newline: index < result.length - 1 || text.endsWith("\n") }));
|
|
45
|
-
};
|
|
46
|
-
const oldLines = lines(before), newLines = lines(after);
|
|
47
|
-
const equal = (left: typeof oldLines[number] | undefined, right: typeof oldLines[number] | undefined) =>
|
|
48
|
-
left !== undefined && right !== undefined && left.text === right.text && left.newline === right.newline;
|
|
49
|
-
let prefix = 0, suffix = 0;
|
|
50
|
-
while (prefix < oldLines.length && prefix < newLines.length && equal(oldLines[prefix], newLines[prefix])) prefix++;
|
|
51
|
-
while (suffix < oldLines.length - prefix && suffix < newLines.length - prefix && equal(oldLines.at(-suffix - 1), newLines.at(-suffix - 1))) suffix++;
|
|
52
|
-
// Assumed familiar Git default: retain three lines of unchanged context per side.
|
|
53
|
-
const start = Math.max(0, prefix - 3), oldEnd = Math.min(oldLines.length, oldLines.length - suffix + 3), newEnd = Math.min(newLines.length, newLines.length - suffix + 3);
|
|
54
|
-
const quoted = (value: string) => /[\s"\\]/.test(value) ? JSON.stringify(value) : value;
|
|
55
|
-
const emit = (entries: typeof oldLines, marker: string) => entries.flatMap(line =>
|
|
56
|
-
[marker + line.text, ...(line.newline ? [] : ["\"])]);
|
|
57
|
-
const body = [...emit(oldLines.slice(start, prefix), " "),
|
|
58
|
-
...emit(oldLines.slice(prefix, oldLines.length - suffix), "-"),
|
|
59
|
-
...emit(newLines.slice(prefix, newLines.length - suffix), "+"),
|
|
60
|
-
...emit(oldLines.slice(oldLines.length - suffix, oldEnd), " ")];
|
|
61
|
-
const range = (count: number) => count ? start + 1 : 0;
|
|
62
|
-
const oldCount = oldEnd - start, newCount = newEnd - start;
|
|
63
|
-
const headers = [`diff --git ${quoted("a/" + path)} ${quoted("b/" + destination)}`,
|
|
64
|
-
...(path === destination ? [] : [`rename from ${quoted(path)}`, `rename to ${quoted(destination)}`]),
|
|
65
|
-
`--- ${before === null ? "/dev/null" : quoted("a/" + path)}`, `+++ ${after === null ? "/dev/null" : quoted("b/" + destination)}`];
|
|
66
|
-
if (body.length) headers.push(`@@ -${range(oldCount)},${oldCount} +${range(newCount)},${newCount} @@`, ...body);
|
|
67
|
-
return headers.join("\n") + "\n";
|
|
68
|
-
}
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The one ripgrep build xeno will install, per platform.
|
|
3
|
-
*
|
|
4
|
-
* Models write `rg` in shell commands because the harnesses they were trained
|
|
5
|
-
* in ship it (Claude Code bundles it; Pi downloads it on first use). On a
|
|
6
|
-
* machine without it every such command exits 127 and costs a request. xeno
|
|
7
|
-
* installs it only when the user has none, and only this exact archive:
|
|
8
|
-
* running a binary fetched from the network needs proof that it is the one
|
|
9
|
-
* reviewed, which a pinned digest gives and "latest" (Pi's choice) does not.
|
|
10
|
-
*
|
|
11
|
-
* Digests are the upstream release's published `.sha256` files for 14.1.1;
|
|
12
|
-
* the aarch64-apple-darwin archive was also downloaded and hashed locally on
|
|
13
|
-
* 2026-10-11 and matched. Upgrading means changing this table and releasing.
|
|
14
|
-
*/
|
|
15
|
-
import { truthy } from "./update-check.ts";
|
|
16
|
-
|
|
17
|
-
export const RIPGREP_VERSION = "14.1.1";
|
|
18
|
-
|
|
19
|
-
export interface RipgrepAsset {
|
|
20
|
-
readonly url: string;
|
|
21
|
-
readonly sha256: string;
|
|
22
|
-
/** Archive member holding the executable; nothing else is extracted. */
|
|
23
|
-
readonly member: string;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const DIGESTS: Readonly<Record<string, string>> = {
|
|
27
|
-
"aarch64-apple-darwin": "24ad76777745fbff131c8fbc466742b011f925bfa4fffa2ded6def23b5b937be",
|
|
28
|
-
"x86_64-apple-darwin": "fc87e78f7cb3fea12d69072e7ef3b21509754717b746368fd40d88963630e2b3",
|
|
29
|
-
// musl is static, so it runs on glibc and musl distributions alike.
|
|
30
|
-
"x86_64-unknown-linux-musl": "4cf9f2741e6c465ffdb7c26f38056a59e2a2544b51f7cc128ef28337eeae4d8e",
|
|
31
|
-
// 14.1.1 publishes no aarch64 musl build; this one needs glibc.
|
|
32
|
-
"aarch64-unknown-linux-gnu": "c827481c4ff4ea10c9dc7a4022c8de5db34a5737cb74484d62eb94a95841ab2f",
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
function target(platform: string, arch: string): string | null {
|
|
36
|
-
if (platform === "darwin") return arch === "arm64" ? "aarch64-apple-darwin" : arch === "x64" ? "x86_64-apple-darwin" : null;
|
|
37
|
-
if (platform === "linux") return arch === "arm64" ? "aarch64-unknown-linux-gnu" : arch === "x64" ? "x86_64-unknown-linux-musl" : null;
|
|
38
|
-
return null;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/** Null for platforms without a pinned build: those keep the TypeScript search. */
|
|
42
|
-
export function ripgrepAsset(platform: string, arch: string): RipgrepAsset | null {
|
|
43
|
-
const triple = target(platform, arch);
|
|
44
|
-
if (triple === null) return null;
|
|
45
|
-
const name = `ripgrep-${RIPGREP_VERSION}-${triple}`;
|
|
46
|
-
return {
|
|
47
|
-
url: `https://github.com/BurntSushi/ripgrep/releases/download/${RIPGREP_VERSION}/${name}.tar.gz`,
|
|
48
|
-
sha256: DIGESTS[triple]!,
|
|
49
|
-
member: `${name}/rg`,
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Whether xeno may fetch the binary on its own, in the background.
|
|
55
|
-
*
|
|
56
|
-
* The same occasions the update check stays off the network (core/update-check.ts):
|
|
57
|
-
* offline, CI, and a source checkout -- the last is also what keeps test
|
|
58
|
-
* subprocesses, which run the CLI from this repository, from downloading.
|
|
59
|
-
* XENO_NO_TOOL_INSTALL opts out explicitly; the user's own rg makes it moot.
|
|
60
|
-
*/
|
|
61
|
-
export function toolInstallAllowed(input: { env: Readonly<Record<string, string | undefined>>; offline: boolean; install: string }): boolean {
|
|
62
|
-
const on = (name: string) => truthy(input.env[name]);
|
|
63
|
-
return !input.offline && input.install !== "checkout" && !on("XENO_OFFLINE") && !on("XENO_NO_TOOL_INSTALL") && !on("CI") && !on("CONTINUOUS_INTEGRATION");
|
|
64
|
-
}
|