one-code-extension 0.4.1 → 0.5.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/README.md +16 -5
- package/agents/general-purpose.md +2 -2
- package/docs/guide/README.md +2 -1
- package/docs/guide/bring-your-claude-code-setup.md +60 -9
- package/docs/guide/configuration.md +48 -14
- package/docs/guide/differences-from-claude-code.md +32 -23
- package/docs/guide/doctor.md +20 -13
- package/docs/guide/hooks.md +18 -3
- package/docs/guide/installation.md +27 -4
- package/docs/guide/permissions-modes-and-auto-mode.md +225 -49
- package/docs/guide/providers-and-models.md +137 -54
- package/docs/guide/reference.md +27 -15
- package/docs/guide/sessions-and-context.md +69 -16
- package/docs/guide/skills-plugins-and-mcp.md +57 -20
- package/docs/guide/subagents-and-workflows.md +95 -29
- package/docs/guide/tasks-and-background-work.md +101 -26
- package/docs/guide/terminal-interface.md +24 -12
- package/docs/guide/tools.md +46 -5
- package/docs/guide/troubleshooting.md +13 -9
- package/docs/guide/windows.md +58 -31
- package/extensions/artifacts/gallery.ts +227 -0
- package/extensions/artifacts/index.ts +288 -0
- package/extensions/artifacts/store.ts +570 -0
- package/extensions/artifacts/viewer.ts +199 -0
- package/extensions/ask-user/description.ts +67 -0
- package/extensions/ask-user/dialogs.ts +80 -0
- package/extensions/ask-user/index.ts +37 -33
- package/extensions/ask-user/questions.ts +32 -7
- package/extensions/ask-user/widget.ts +45 -25
- package/extensions/auto-mode/actions.ts +6 -0
- package/extensions/auto-mode/cache.ts +88 -0
- package/extensions/auto-mode/classifier-prompt.ts +5 -4
- package/extensions/auto-mode/classifier.ts +290 -65
- package/extensions/auto-mode/config.ts +72 -20
- package/extensions/auto-mode/decision-log.ts +2 -0
- package/extensions/auto-mode/defaults.ts +1 -1
- package/extensions/auto-mode/denials.ts +123 -0
- package/extensions/auto-mode/git-checkout-programs.ts +187 -0
- package/extensions/auto-mode/git-status-meta.ts +159 -0
- package/extensions/auto-mode/history.ts +189 -0
- package/extensions/auto-mode/instructions.ts +58 -52
- package/extensions/auto-mode/model-select.ts +159 -134
- package/extensions/auto-mode/paths.ts +57 -5
- package/extensions/auto-mode/pause.ts +57 -2
- package/extensions/auto-mode/prompt.ts +37 -21
- package/extensions/auto-mode/read-only-options.ts +724 -0
- package/extensions/auto-mode/resolved-paths-meta.ts +133 -0
- package/extensions/auto-mode/rules.ts +18 -1
- package/extensions/auto-mode/safety-floor.ts +361 -18
- package/extensions/auto-mode/setup-run.ts +20 -14
- package/extensions/auto-mode/shell-analysis.ts +764 -490
- package/extensions/auto-mode/shell-parse.ts +885 -0
- package/extensions/auto-mode/system-note.ts +33 -0
- package/extensions/auto-mode/transcript.ts +80 -51
- package/extensions/background/cron.ts +515 -0
- package/extensions/background/index.ts +514 -236
- package/extensions/background/loop-fire.ts +136 -0
- package/extensions/background/loop-preamble.ts +59 -0
- package/extensions/background/loop-skill.ts +163 -0
- package/extensions/background/monitor-batch.ts +53 -0
- package/extensions/background/noop-fold.ts +128 -0
- package/extensions/background/switch-guard.ts +52 -0
- package/extensions/background/wakeup.ts +159 -55
- package/extensions/bash/background.ts +53 -17
- package/extensions/bash/child.ts +7 -0
- package/extensions/bash/description.ts +188 -0
- package/extensions/bash/guards.ts +15 -67
- package/extensions/bash/index.ts +88 -35
- package/extensions/branding/compaction-keep.ts +63 -0
- package/extensions/branding/declined.ts +22 -0
- package/extensions/branding/index.ts +133 -9
- package/extensions/branding/prompt-editor.ts +53 -6
- package/extensions/branding/queued-edit.ts +31 -0
- package/extensions/branding/replaced-builtins.ts +68 -0
- package/extensions/branding/startup.ts +33 -71
- package/extensions/btw/index.ts +229 -71
- package/extensions/btw/panel.ts +228 -100
- package/extensions/btw/prompt.ts +41 -4
- package/extensions/claude-compat/index.ts +7 -11
- package/extensions/claude-context/index.ts +446 -58
- package/extensions/compaction/cut.ts +157 -0
- package/extensions/compaction/index.ts +216 -75
- package/extensions/compaction/kept-thinking.ts +62 -0
- package/extensions/compaction/prompt.ts +15 -12
- package/extensions/context-budget/index.ts +24 -5
- package/extensions/context-management/index.ts +25 -0
- package/extensions/doctor/build.ts +59 -8
- package/extensions/doctor/builtins.ts +84 -0
- package/extensions/doctor/cli.ts +7 -1
- package/extensions/doctor/compat.ts +88 -23
- package/extensions/doctor/dependencies.ts +41 -5
- package/extensions/doctor/fix-prompt.ts +9 -2
- package/extensions/doctor/index.ts +102 -58
- package/extensions/doctor/models.ts +102 -63
- package/extensions/doctor/presets.ts +42 -27
- package/extensions/doctor/report.ts +19 -2
- package/extensions/doctor/viewer.ts +49 -27
- package/extensions/effort/index.ts +72 -19
- package/extensions/effort/settings.ts +21 -0
- package/extensions/effort/slider.ts +24 -20
- package/extensions/exit/index.ts +32 -1
- package/extensions/file-tracker/index.ts +337 -26
- package/extensions/file-tracker/replay.ts +96 -10
- package/extensions/file-tracker/restore.ts +63 -0
- package/extensions/file-tracker/results.ts +66 -0
- package/extensions/file-tracker/shell-reads.ts +211 -0
- package/extensions/file-tracker/tracker.ts +32 -15
- package/extensions/footer/footer-line.ts +45 -24
- package/extensions/footer/index.ts +47 -2
- package/extensions/footer/pr.ts +34 -5
- package/extensions/hooks/compaction.ts +7 -0
- package/extensions/hooks/executor.ts +32 -30
- package/extensions/hooks/index.ts +198 -31
- package/extensions/hooks/matcher.ts +3 -0
- package/extensions/hooks/protocol.ts +41 -3
- package/extensions/hooks/settings.ts +50 -19
- package/extensions/hooks/subagent-bridge.ts +2 -0
- package/extensions/hooks/trust.ts +13 -5
- package/extensions/idle-compact/index.ts +119 -0
- package/extensions/idle-compact/policy.ts +143 -0
- package/extensions/init/index.ts +11 -10
- package/extensions/init/prompt.ts +25 -32
- package/extensions/interrupted/index.ts +13 -1
- package/extensions/kept-thinking/index.ts +27 -0
- package/extensions/lib/agent-cron.ts +130 -0
- package/extensions/lib/agent-loader.ts +15 -1
- package/extensions/lib/agent-view.ts +85 -0
- package/extensions/lib/anthropic-payload.ts +82 -1
- package/extensions/lib/anthropic-server-call.ts +230 -0
- package/extensions/lib/anthropic-server-tools.ts +485 -0
- package/extensions/lib/app-launch.mjs +94 -0
- package/extensions/lib/argument-hints.ts +190 -0
- package/extensions/lib/atomic-write.ts +6 -2
- package/extensions/lib/auto-memory.ts +75 -0
- package/extensions/lib/bash-parser.ts +84 -0
- package/extensions/lib/btw-fork.ts +93 -0
- package/extensions/lib/child-extensions.ts +42 -0
- package/extensions/lib/child-writes.ts +45 -0
- package/extensions/lib/claude-context.ts +515 -100
- package/extensions/lib/claude-external-includes.ts +68 -0
- package/extensions/lib/claude-id.ts +22 -0
- package/extensions/lib/claude-rules.ts +323 -0
- package/extensions/lib/claude-settings.ts +76 -4
- package/extensions/lib/compaction-boundary.ts +36 -0
- package/extensions/lib/compaction-keep.mjs +83 -0
- package/extensions/lib/config-mode.ts +112 -0
- package/extensions/lib/consent-dialogs.ts +63 -0
- package/extensions/lib/consent-preview.ts +5 -1
- package/extensions/lib/consent-stores.ts +48 -0
- package/extensions/lib/context-facts.ts +78 -0
- package/extensions/lib/context-stack.ts +81 -0
- package/extensions/lib/deferred.ts +219 -25
- package/extensions/lib/dock-rows.ts +60 -0
- package/extensions/lib/empty-tool-result.ts +27 -0
- package/extensions/lib/environment-block.ts +76 -0
- package/extensions/lib/fetch-timeout.ts +24 -6
- package/extensions/{system-prompt → lib}/git-status.ts +51 -13
- package/extensions/lib/git.ts +75 -13
- package/extensions/lib/grammars/LICENSE-tree-sitter-bash +21 -0
- package/extensions/lib/grammars/README.md +2 -0
- package/extensions/lib/grammars/tree-sitter-bash.wasm +0 -0
- package/extensions/lib/guide-docs.ts +45 -0
- package/extensions/lib/headless-output.ts +46 -0
- package/extensions/lib/hook-gate.ts +7 -7
- package/extensions/lib/interrupt.ts +20 -4
- package/extensions/lib/key-input.ts +455 -0
- package/extensions/lib/keys.ts +14 -1
- package/extensions/lib/local-command.ts +47 -13
- package/extensions/lib/mcp-overrides.ts +1 -1
- package/extensions/lib/mcp-share.ts +46 -0
- package/extensions/lib/memory-content.ts +146 -0
- package/extensions/lib/memory-index.ts +18 -0
- package/extensions/lib/memory.ts +134 -123
- package/extensions/lib/model-catalog/huggingface.json +174 -0
- package/extensions/lib/model-catalog/models-dev.json +18297 -0
- package/extensions/lib/model-catalog/openrouter.json +5252 -0
- package/extensions/lib/model-catalog-data.ts +513 -0
- package/extensions/lib/model-catalog.ts +462 -0
- package/extensions/lib/model-choice-warnings.ts +137 -0
- package/extensions/lib/model-pairings.ts +152 -0
- package/extensions/{auto-mode → lib}/model-picker.ts +26 -43
- package/extensions/lib/model-policy.ts +112 -15
- package/extensions/lib/model-tier.ts +283 -355
- package/extensions/lib/model-unusable.ts +1 -1
- package/extensions/lib/newer-model.ts +100 -0
- package/extensions/lib/notices.ts +87 -0
- package/extensions/lib/notifications.ts +308 -64
- package/extensions/lib/one-code-settings.ts +74 -8
- package/extensions/lib/open-browser.ts +63 -0
- package/extensions/lib/openrouter-generation.ts +94 -0
- package/extensions/lib/operation-deadline.ts +30 -0
- package/extensions/lib/original-command.ts +4 -4
- package/extensions/lib/package-version.ts +1 -1
- package/extensions/lib/panel-focus.ts +134 -0
- package/extensions/lib/paths.ts +17 -2
- package/extensions/lib/permission-gate.ts +72 -6
- package/extensions/lib/persisted-output.ts +60 -8
- package/extensions/lib/pi-ai-estimate.ts +19 -3
- package/extensions/lib/pi-version.ts +21 -2
- package/extensions/lib/plan-mode-channels.ts +20 -5
- package/extensions/lib/plugins.ts +26 -17
- package/extensions/lib/powershell-parse-script.ts +208 -0
- package/extensions/lib/powershell-parser.ts +344 -0
- package/extensions/lib/process-tree.ts +131 -8
- package/extensions/lib/prompt-options.ts +82 -0
- package/extensions/lib/queued-delivery.ts +44 -0
- package/extensions/lib/reminders.ts +364 -58
- package/extensions/lib/replaced-builtins.mjs +199 -0
- package/extensions/lib/replay-call.ts +70 -0
- package/extensions/lib/request-replay.ts +439 -0
- package/extensions/lib/scratchpad.ts +81 -14
- package/extensions/lib/send-now-channel.ts +7 -0
- package/extensions/lib/session-model-tier.ts +51 -0
- package/extensions/lib/session-work.ts +64 -0
- package/extensions/lib/settings-channels.ts +4 -4
- package/extensions/lib/shell-exit-semantics.ts +170 -0
- package/extensions/lib/shell-quote.ts +11 -0
- package/extensions/lib/shell-result.ts +70 -0
- package/extensions/lib/shell-spawn.ts +70 -6
- package/extensions/lib/shell-tool.ts +75 -25
- package/extensions/lib/side-call-cache.ts +124 -0
- package/extensions/lib/side-call-run.ts +96 -0
- package/extensions/lib/side-call-usage.ts +38 -0
- package/extensions/lib/side-call.ts +13 -1
- package/extensions/lib/single-flight.ts +18 -0
- package/extensions/lib/skill-body.ts +42 -0
- package/extensions/lib/skill-scan.ts +63 -24
- package/extensions/lib/spill-file.ts +140 -0
- package/extensions/lib/subagent-handback.ts +113 -0
- package/extensions/lib/system-role.ts +156 -0
- package/extensions/lib/terminal-text.ts +114 -0
- package/extensions/lib/text-width.ts +47 -16
- package/extensions/lib/timer-ops.ts +20 -0
- package/extensions/lib/tool-additions.ts +149 -0
- package/extensions/lib/tool-call-corruption.ts +134 -0
- package/extensions/lib/tool-path.ts +66 -0
- package/extensions/lib/tool-variants.ts +69 -0
- package/extensions/lib/tui-render.ts +255 -27
- package/extensions/lib/turn-budget-layout.ts +143 -0
- package/extensions/lib/update-check.mjs +15 -2
- package/extensions/lib/usage-bus.ts +1 -1
- package/extensions/lib/user-denial.ts +38 -0
- package/extensions/lib/workspace-channel.ts +16 -0
- package/extensions/lib/worktree-channel.ts +31 -3
- package/extensions/lib/worktree-isolation.ts +40 -10
- package/extensions/lsp/client.ts +8 -9
- package/extensions/lsp/index.ts +70 -4
- package/extensions/lsp/keep-alive.ts +1 -1
- package/extensions/lsp/trust.ts +119 -0
- package/extensions/lsp/watcher.ts +1 -1
- package/extensions/mcp/announce.ts +87 -5
- package/extensions/mcp/client.ts +161 -48
- package/extensions/mcp/config.ts +132 -8
- package/extensions/mcp/discovery.ts +19 -0
- package/extensions/mcp/index.ts +209 -71
- package/extensions/mcp/oauth/browser.ts +38 -17
- package/extensions/mcp/oauth/flow.ts +25 -17
- package/extensions/mcp/oauth/provider.ts +14 -8
- package/extensions/mcp/oauth/store.ts +34 -16
- package/extensions/mcp/panel/keys.ts +12 -11
- package/extensions/mcp/panel/render.ts +9 -6
- package/extensions/mcp/schema.ts +104 -5
- package/extensions/mcp/trust.ts +151 -67
- package/extensions/memory/entries.ts +23 -14
- package/extensions/memory/index.ts +134 -28
- package/extensions/memory/open-external.ts +33 -18
- package/extensions/memory/panel.ts +135 -26
- package/extensions/newer-model/index.ts +31 -0
- package/extensions/notebook/description.ts +18 -0
- package/extensions/notebook/index.ts +6 -5
- package/extensions/notebook/notebook.ts +1 -1
- package/extensions/permissions/index.ts +1300 -381
- package/extensions/permissions/matcher.ts +425 -74
- package/extensions/permissions/modes.ts +6 -4
- package/extensions/permissions/panel/host.ts +130 -0
- package/extensions/permissions/panel/keys.ts +48 -0
- package/extensions/permissions/panel/render.ts +450 -0
- package/extensions/permissions/panel/state.ts +550 -0
- package/extensions/permissions/powershell-cmdlets.ts +198 -0
- package/extensions/permissions/powershell-paths.ts +197 -0
- package/extensions/permissions/powershell-rules.ts +207 -280
- package/extensions/permissions/powershell-tree.ts +557 -0
- package/extensions/permissions/project-trust.ts +27 -11
- package/extensions/permissions/protected-paths.ts +14 -2
- package/extensions/permissions/session-grant.ts +6 -0
- package/extensions/permissions/settings.ts +166 -31
- package/extensions/permissions/subagent-gate.ts +25 -0
- package/extensions/permissions/workspace.ts +78 -0
- package/extensions/plan-mode/index.ts +156 -69
- package/extensions/plan-mode/reminder.ts +89 -21
- package/extensions/plan-mode/texts.ts +177 -0
- package/extensions/plan-mode/viewer.ts +92 -27
- package/extensions/plugins/counts.ts +3 -3
- package/extensions/plugins/index.ts +61 -46
- package/extensions/plugins/install/install.ts +16 -0
- package/extensions/plugins/marketplace/sync.ts +9 -3
- package/extensions/plugins/marketplace/types.ts +3 -1
- package/extensions/plugins/panel/keys.ts +21 -24
- package/extensions/plugins/panel/render.ts +12 -6
- package/extensions/plugins/shell-expand.ts +107 -0
- package/extensions/plugins/template.ts +32 -15
- package/extensions/powershell/child.ts +7 -0
- package/extensions/powershell/description.ts +38 -6
- package/extensions/powershell/guards.ts +1 -1
- package/extensions/powershell/index.ts +12 -7
- package/extensions/recap/index.ts +65 -60
- package/extensions/recap/prompt.ts +2 -2
- package/extensions/recap/scheduler.ts +3 -5
- package/extensions/search-tools/grep-merge.ts +39 -0
- package/extensions/search-tools/index.ts +0 -0
- package/extensions/send-now/chord.ts +62 -0
- package/extensions/send-now/index.ts +133 -0
- package/extensions/session-title/index.ts +3 -0
- package/extensions/session-title/title.ts +4 -1
- package/extensions/skill/code-review.ts +97 -0
- package/extensions/skill/index.ts +421 -101
- package/extensions/skill/invoke.ts +128 -18
- package/extensions/skill/listing.ts +133 -9
- package/extensions/skill/panel/keys.ts +15 -16
- package/extensions/skill/panel/render.ts +5 -2
- package/extensions/skill/panel/state.ts +12 -1
- package/extensions/subagents/agent-args.ts +39 -0
- package/extensions/subagents/agent-description.ts +132 -0
- package/extensions/subagents/agents.ts +116 -23
- package/extensions/subagents/announce.ts +69 -0
- package/extensions/subagents/default-model.ts +2 -3
- package/extensions/subagents/delegation-steer.ts +4 -4
- package/extensions/subagents/fork-cache.ts +76 -0
- package/extensions/subagents/guide-agent.ts +189 -0
- package/extensions/subagents/hand-back-review.ts +3 -0
- package/extensions/subagents/index.ts +1493 -824
- package/extensions/subagents/live-runs.ts +26 -6
- package/extensions/subagents/model-select.ts +38 -25
- package/extensions/subagents/outcome.ts +2 -2
- package/extensions/subagents/panel-keys.ts +73 -31
- package/extensions/subagents/panel-render.ts +120 -96
- package/extensions/subagents/panel-widget.ts +60 -89
- package/extensions/subagents/runner.ts +108 -80
- package/extensions/subagents/runs.ts +39 -5
- package/extensions/subagents/send-to-main-tool.ts +1 -1
- package/extensions/subagents/session-turns.ts +32 -8
- package/extensions/subagents/shell-panel.ts +36 -178
- package/extensions/subagents/tasks-dialog.ts +170 -0
- package/extensions/subagents/viewport-mirror.ts +95 -0
- package/extensions/subagents/worktree.ts +57 -13
- package/extensions/system-prompt/environment.ts +19 -19
- package/extensions/system-prompt/idle-turn.ts +65 -0
- package/extensions/system-prompt/index.ts +131 -47
- package/extensions/system-prompt/template.ts +24 -50
- package/extensions/system-prompt/tiers/common.ts +23 -64
- package/extensions/system-prompt/tiers/frontier.ts +32 -11
- package/extensions/system-prompt/tiers/low.ts +67 -45
- package/extensions/system-prompt/tiers/mid.ts +96 -38
- package/extensions/system-reminder/index.ts +175 -13
- package/extensions/tasks/descriptions.ts +190 -0
- package/extensions/tasks/index.ts +120 -41
- package/extensions/tasks/store.ts +83 -16
- package/extensions/tool-call-corruption/child.ts +7 -0
- package/extensions/tool-call-corruption/index.ts +237 -0
- package/extensions/tool-search/announce.ts +16 -0
- package/extensions/tool-search/index.ts +189 -41
- package/extensions/tool-style/index.ts +68 -10
- package/extensions/tool-style/write-description.ts +40 -0
- package/extensions/tool-style/write-render.ts +237 -0
- package/extensions/turn-duration/index.ts +7 -3
- package/extensions/turn-duration/line.ts +47 -4
- package/extensions/web/backends.ts +6 -9
- package/extensions/web/description.ts +50 -0
- package/extensions/web/index.ts +118 -53
- package/extensions/web/native-call.ts +15 -0
- package/extensions/web/native-results.ts +41 -0
- package/extensions/web-fetch/description.ts +42 -0
- package/extensions/web-fetch/extract.ts +53 -6
- package/extensions/web-fetch/index.ts +125 -76
- package/extensions/web-fetch/page-content.ts +19 -0
- package/extensions/web-fetch/response.ts +49 -0
- package/extensions/web-fetch/summarize.ts +9 -10
- package/extensions/workflow/agent-session.ts +105 -18
- package/extensions/workflow/args.ts +24 -0
- package/extensions/workflow/description.ts +67 -0
- package/extensions/workflow/globals.ts +17 -3
- package/extensions/workflow/index.ts +145 -162
- package/extensions/workflow/records.ts +1 -0
- package/extensions/workflow/run-manager.ts +29 -6
- package/extensions/workflow/saved-workflows.ts +3 -2
- package/extensions/workflow/types.ts +22 -1
- package/extensions/workflow/viewer.ts +54 -24
- package/extensions/workflow/widget.ts +20 -0
- package/extensions/worktree/descriptions.ts +91 -0
- package/extensions/worktree/guards.ts +316 -54
- package/extensions/worktree/index.ts +121 -39
- package/extensions/worktree/powershell-guards.ts +167 -0
- package/extensions/worktree/rewrite.ts +14 -16
- package/package.json +18 -7
- package/skills/artifact-design/SKILL.md +85 -0
- package/skills/code-review/SKILL.md +20 -20
- package/skills/code-review/cells/arguments-and-flags.md +19 -0
- package/skills/code-review/cells/high-3x5-recall-verify.md +137 -0
- package/skills/code-review/cells/high-8-inline.md +122 -0
- package/skills/code-review/cells/low-1-pass-max4.md +27 -0
- package/skills/code-review/cells/low-1-pass-min-files-4.md +28 -0
- package/skills/code-review/cells/max-5x5-verify-sweep.md +162 -0
- package/skills/code-review/cells/medium-3x5-verify.md +132 -0
- package/skills/code-review/cells/minimal-prompt.md +32 -0
- package/skills/code-review/cells/xhigh-10-inline-sweep.md +149 -0
- package/skills/code-review/cells/xhigh-5x5-verify-sweep.md +162 -0
- package/skills/fewer-permission-prompts/SKILL.md +1 -1
- package/skills/loop/SKILL.md +11 -0
- package/skills/security-review/SKILL.md +2 -5
- package/skills/simplify/SKILL.md +7 -5
- package/skills/workflow-authoring/SKILL.md +143 -0
- package/themes/onecode-light.json +1 -1
- package/themes/onecode.json +1 -1
- package/types/app.d.ts +58 -0
- package/types/community-packages.d.ts +26 -1
- package/extensions/auto-mode/recoverability.ts +0 -229
- package/extensions/lib/capability-index.ts +0 -466
- package/extensions/lib/model-facts.generated.json +0 -2675
- package/extensions/lib/model-facts.ts +0 -162
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where One Code reads its configuration and context: the two modes
|
|
3
|
+
* (working-docs/features/config-modes/).
|
|
4
|
+
*
|
|
5
|
+
* - `claude-compatible` (the default): Claude Code's `~/.claude`,
|
|
6
|
+
* `<repo>/.claude`, `CLAUDE.md` and `~/.claude.json`, plus the cross-tool
|
|
7
|
+
* `AGENTS.md` and `.agents/skills` and pi's own directories.
|
|
8
|
+
* - `independent`: no Claude Code path at all; `~/.onecode`, `ONECODE.md`,
|
|
9
|
+
* `<repo>/.onecode/`, pi's directories and the cross-tool files, with
|
|
10
|
+
* auto-memory under `~/.onecode/projects/<slug>/memory/`.
|
|
11
|
+
*
|
|
12
|
+
* The choice is `configMode` in One Code's user settings
|
|
13
|
+
* (`~/.onecode/settings.json`), never read from `~/.claude`;
|
|
14
|
+
* `ONECODE_CONFIG_MODE` overrides it. Readers run at factory time and on
|
|
15
|
+
* `resources_discover`, before any session event, so the mode is read from
|
|
16
|
+
* disk, not broadcast.
|
|
17
|
+
*
|
|
18
|
+
* The mode is fixed for the life of the process: the first read pins it in a
|
|
19
|
+
* `globalThis` slot that every extension's module instance shares (jiti gives
|
|
20
|
+
* each its own copy of this file). The system prompt names the memory folder
|
|
21
|
+
* and must stay byte-stable, so a `/memory` toggle saves the setting and
|
|
22
|
+
* applies from the next start.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import os from "node:os";
|
|
26
|
+
import { join } from "node:path";
|
|
27
|
+
import { readJsonFile } from "./atomic-write.ts";
|
|
28
|
+
import { claudeUserDir, oneCodeStateDir } from "./paths.ts";
|
|
29
|
+
|
|
30
|
+
export type ConfigMode = "claude-compatible" | "independent";
|
|
31
|
+
|
|
32
|
+
const DEFAULT_CONFIG_MODE: ConfigMode = "claude-compatible";
|
|
33
|
+
|
|
34
|
+
/** How each mode is named to the user (`/memory`, `/doctor`). */
|
|
35
|
+
export const MODE_LABELS: Record<ConfigMode, string> = {
|
|
36
|
+
"claude-compatible": "Claude-compatible",
|
|
37
|
+
independent: "Independent",
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/** The key in `~/.onecode/settings.json`. */
|
|
41
|
+
export const CONFIG_MODE_KEY = "configMode";
|
|
42
|
+
const CONFIG_MODE_ENV = "ONECODE_CONFIG_MODE";
|
|
43
|
+
|
|
44
|
+
function isConfigMode(value: unknown): value is ConfigMode {
|
|
45
|
+
return value === "claude-compatible" || value === "independent";
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* The saved mode: `ONECODE_CONFIG_MODE`, else `configMode` in One Code's user
|
|
50
|
+
* settings, else the default. A missing, malformed or unknown value reads as
|
|
51
|
+
* the default (fail toward today's behaviour); an unknown env value is ignored.
|
|
52
|
+
*/
|
|
53
|
+
export function readConfigMode(home: string = os.homedir(), env: Record<string, string | undefined> = process.env): ConfigMode {
|
|
54
|
+
return configModeFromEnv(env) ?? savedConfigMode(home, env);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** The mode `ONECODE_CONFIG_MODE` sets, or undefined when it is unset or unknown. */
|
|
58
|
+
export function configModeFromEnv(env: Record<string, string | undefined> = process.env): ConfigMode | undefined {
|
|
59
|
+
const value = env[CONFIG_MODE_ENV];
|
|
60
|
+
return isConfigMode(value) ? value : undefined;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* `configMode` in `~/.onecode/settings.json` alone, ignoring the env override.
|
|
65
|
+
* The path is spelled out: importing `oneCodeSettingsPath` would cycle through
|
|
66
|
+
* `one-code-settings.ts` and `memory.ts`.
|
|
67
|
+
*/
|
|
68
|
+
export function savedConfigMode(home: string = os.homedir(), env: Record<string, string | undefined> = process.env): ConfigMode {
|
|
69
|
+
const value = readJsonFile<Record<string, unknown>>(join(oneCodeStateDir(env, home), "settings.json"))?.[CONFIG_MODE_KEY];
|
|
70
|
+
return isConfigMode(value) ? value : DEFAULT_CONFIG_MODE;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const MODE_SLOT = Symbol.for("one-code:config-mode");
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* The mode this process runs in: read once, then pinned for every extension.
|
|
77
|
+
* Tests that need a different mode call `resetConfigModeForTest` first.
|
|
78
|
+
*/
|
|
79
|
+
export function configMode(): ConfigMode {
|
|
80
|
+
const slot = globalThis as { [MODE_SLOT]?: ConfigMode };
|
|
81
|
+
return (slot[MODE_SLOT] ??= readConfigMode());
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function resetConfigModeForTest(mode?: ConfigMode): void {
|
|
85
|
+
const slot = globalThis as { [MODE_SLOT]?: ConfigMode };
|
|
86
|
+
if (mode) slot[MODE_SLOT] = mode;
|
|
87
|
+
else delete slot[MODE_SLOT];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Whether Claude Code's own locations are read: `~/.claude`, `.claude/`, `CLAUDE.md`, `~/.claude.json`. */
|
|
91
|
+
export function claudeSourcesOn(): boolean {
|
|
92
|
+
return configMode() === "claude-compatible";
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* The user-level folder that holds agents, commands, workflows and the user
|
|
97
|
+
* settings file: `~/.claude` (`CLAUDE_CONFIG_DIR`) in Claude-compatible mode,
|
|
98
|
+
* `~/.onecode` (`ONECODE_STATE_DIR`) in independent mode.
|
|
99
|
+
*/
|
|
100
|
+
export function userConfigDir(home: string, env: Record<string, string | undefined> = process.env): string {
|
|
101
|
+
return claudeSourcesOn() ? claudeUserDir(home, env) : oneCodeStateDir(env, home);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** The project-level folder's name: `.claude`, or `.onecode` in independent mode. */
|
|
105
|
+
export function projectConfigDirName(): string {
|
|
106
|
+
return claudeSourcesOn() ? ".claude" : ".onecode";
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** The project-level counterpart of `userConfigDir`: `<cwd>/.claude` or `<cwd>/.onecode`. */
|
|
110
|
+
export function projectConfigDir(cwd: string): string {
|
|
111
|
+
return join(cwd, projectConfigDirName());
|
|
112
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/** Serialize startup consent modals over the event bus (jiti modules cannot share a queue). */
|
|
2
|
+
const CHANNEL = "one-code:consent-dialog";
|
|
3
|
+
interface Events {
|
|
4
|
+
emit(channel: string, data: unknown): void;
|
|
5
|
+
on(channel: string, listener: (data: unknown) => void): unknown;
|
|
6
|
+
}
|
|
7
|
+
interface Request {
|
|
8
|
+
show: (signal?: AbortSignal) => Promise<unknown>;
|
|
9
|
+
/** External includes are the last startup consent, after hooks and MCP. */
|
|
10
|
+
afterStartup?: boolean;
|
|
11
|
+
result?: Promise<unknown>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** The early claude-context extension owns the queue and resets it with its session. */
|
|
15
|
+
export function installConsentDialogs(events: Events): () => void {
|
|
16
|
+
let controller = new AbortController();
|
|
17
|
+
let tail: Promise<unknown> = Promise.resolve();
|
|
18
|
+
let startupReady = false;
|
|
19
|
+
let waiting: Array<() => void> = [];
|
|
20
|
+
events.on(CHANNEL, (data) => {
|
|
21
|
+
if ((data as { ready?: boolean }).ready) {
|
|
22
|
+
startupReady = true;
|
|
23
|
+
const pending = waiting;
|
|
24
|
+
waiting = [];
|
|
25
|
+
for (const enqueue of pending) enqueue();
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const request = data as Request;
|
|
29
|
+
const signal = controller.signal;
|
|
30
|
+
let enqueue!: () => void;
|
|
31
|
+
request.result = new Promise((resolve, reject) => {
|
|
32
|
+
const abort = () => resolve(undefined);
|
|
33
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
34
|
+
enqueue = () => {
|
|
35
|
+
const previous = tail;
|
|
36
|
+
tail = request.result!.catch(() => {});
|
|
37
|
+
previous.then(() => signal.aborted ? undefined : request.show(signal)).then(resolve, reject)
|
|
38
|
+
.finally(() => signal.removeEventListener("abort", abort));
|
|
39
|
+
};
|
|
40
|
+
});
|
|
41
|
+
if (request.afterStartup && !startupReady) waiting.push(enqueue);
|
|
42
|
+
else enqueue();
|
|
43
|
+
});
|
|
44
|
+
return () => {
|
|
45
|
+
controller.abort();
|
|
46
|
+
controller = new AbortController();
|
|
47
|
+
tail = Promise.resolve();
|
|
48
|
+
startupReady = false;
|
|
49
|
+
waiting = [];
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** MCP finishes registering startup prompts; the first turn is the fallback when MCP is not loaded. */
|
|
54
|
+
export function startupConsentReady(events: Pick<Events, "emit">): void {
|
|
55
|
+
events.emit(CHANNEL, { ready: true });
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Without the owner (an extension loaded alone), retain the normal direct-dialog behavior. */
|
|
59
|
+
export function consentDialog<T>(events: Pick<Events, "emit">, show: (signal?: AbortSignal) => Promise<T>, afterStartup = false): Promise<T | undefined> {
|
|
60
|
+
const request: Request = { show, afterStartup };
|
|
61
|
+
events.emit(CHANNEL, request);
|
|
62
|
+
return (request.result ?? show()) as Promise<T | undefined>;
|
|
63
|
+
}
|
|
@@ -10,13 +10,17 @@
|
|
|
10
10
|
* truth the user should read before approving anything this large).
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
+
import { escapeControlText } from "./terminal-text.ts";
|
|
14
|
+
|
|
13
15
|
const PER_ITEM_CAP = 2000;
|
|
14
16
|
const TOTAL_CAP = 4000;
|
|
15
17
|
|
|
16
18
|
export function boundConsentItems(items: string[], moreHint: string): string {
|
|
17
19
|
const shown: string[] = [];
|
|
18
20
|
let used = 0;
|
|
19
|
-
|
|
21
|
+
// Control characters are shown as visible escapes: an SGR conceal or a
|
|
22
|
+
// cursor move in a command could otherwise hide the part the user approves.
|
|
23
|
+
for (const item of items.map(escapeControlText)) {
|
|
20
24
|
const capped = item.length > PER_ITEM_CAP ? `${item.slice(0, PER_ITEM_CAP)}… [truncated, ${item.length} chars]` : item;
|
|
21
25
|
// Always show the first item (so an empty modal never happens); stop once
|
|
22
26
|
// the budget is spent.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where One Code keeps the user's consent to a repository's own
|
|
3
|
+
* configuration (pure).
|
|
4
|
+
*
|
|
5
|
+
* Each store records that the user approved something a checkout ships, keyed
|
|
6
|
+
* by a hash the model can compute: its hooks, its `.mcp.json` servers, its
|
|
7
|
+
* allow rules and workspace directories, and the language servers that run
|
|
8
|
+
* its build. An entry is standing permission, so a write to a store is a
|
|
9
|
+
* gate-control write: the auto-mode safety floor stops it
|
|
10
|
+
* (`auto-mode/safety-floor.ts`), and a classifier that was talked around
|
|
11
|
+
* never gets to approve it. One home for the paths, so a new store cannot be
|
|
12
|
+
* left off the floor.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { homedir } from "node:os";
|
|
16
|
+
import { join } from "node:path";
|
|
17
|
+
import { oneCodeStateDir } from "./paths.ts";
|
|
18
|
+
|
|
19
|
+
/** Each consent store, relative to One Code's state dir. */
|
|
20
|
+
const CONSENT_STORES = {
|
|
21
|
+
hooks: ["hooks", "project-approvals.json"],
|
|
22
|
+
mcp: ["mcp", "project-approvals.json"],
|
|
23
|
+
projectAllow: ["permissions", "project-allow-approvals.json"],
|
|
24
|
+
lsp: ["lsp", "trusted-projects.json"],
|
|
25
|
+
} as const;
|
|
26
|
+
|
|
27
|
+
export type ConsentStore = keyof typeof CONSENT_STORES;
|
|
28
|
+
|
|
29
|
+
/** One consent store's file, under `ONECODE_STATE_DIR` when it is set. */
|
|
30
|
+
export function consentStorePath(store: ConsentStore, env: Record<string, string | undefined> = process.env, home: string = homedir()): string {
|
|
31
|
+
return join(oneCodeStateDir(env, home), ...CONSENT_STORES[store]);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Every consent store's file, for the safety floor. */
|
|
35
|
+
export function consentStorePaths(home: string, env: Record<string, string | undefined> = process.env): string[] {
|
|
36
|
+
return (Object.keys(CONSENT_STORES) as ConsentStore[]).map((store) => consentStorePath(store, env, home));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* A consent store under any literal `.onecode` directory, as a forward-slashed
|
|
41
|
+
* path tail: the textual floor's counterpart of `consentStorePaths`, for a
|
|
42
|
+
* store in another home or spelled through a symlink the resolver missed.
|
|
43
|
+
*/
|
|
44
|
+
export const CONSENT_STORE_TAIL = new RegExp(
|
|
45
|
+
`/\\.onecode/(${Object.values(CONSENT_STORES)
|
|
46
|
+
.map((parts) => parts.join("/").replace(/[.]/g, "\\."))
|
|
47
|
+
.join("|")})$`,
|
|
48
|
+
);
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/** Comparisons for the historical context stack. No pi imports or live-state reads. */
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import { buildClaudeMdBlock, buildContextBlock, type ContextFile } from "./claude-context.ts";
|
|
4
|
+
import type { ReminderEntry } from "./reminders.ts";
|
|
5
|
+
|
|
6
|
+
export const CONTEXT_FACT_KEYS = ["claude-context", "one-code-context", "claude-context-context", "claude-context-date"] as const;
|
|
7
|
+
export const RESUME_FACTS_KEY = "context-resume-changes";
|
|
8
|
+
export const DATE_CHANGE_KEY = "claude-context-date-change";
|
|
9
|
+
|
|
10
|
+
export interface ContextFactsBaseline {
|
|
11
|
+
instructions: string;
|
|
12
|
+
memory: string;
|
|
13
|
+
git: string;
|
|
14
|
+
/** A compaction starts a new snapshot epoch, not a new conversation. */
|
|
15
|
+
atCompaction: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* The directory the facts were read in: the session's cwd, or the entered
|
|
18
|
+
* worktree at a compaction. A resume compares in the same one. Absent in
|
|
19
|
+
* older snapshots.
|
|
20
|
+
*/
|
|
21
|
+
cwd?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const fingerprint = (value: unknown): string => createHash("sha256").update(JSON.stringify(value)).digest("hex");
|
|
25
|
+
|
|
26
|
+
/** Hash only what the model was shown, not unshown file tails or filesystem mtimes. */
|
|
27
|
+
export function contextFactsBaseline(input: {
|
|
28
|
+
contextFiles: ContextFile[];
|
|
29
|
+
memoryIndex: { path: string; content: string } | null;
|
|
30
|
+
gitStatus: string | null;
|
|
31
|
+
atCompaction: boolean;
|
|
32
|
+
cwd?: string;
|
|
33
|
+
}): ContextFactsBaseline {
|
|
34
|
+
return {
|
|
35
|
+
instructions: fingerprint(buildClaudeMdBlock({ contextFiles: input.contextFiles })),
|
|
36
|
+
memory: fingerprint(buildClaudeMdBlock({ contextFiles: [], memoryIndex: input.memoryIndex })),
|
|
37
|
+
git: fingerprint(input.gitStatus),
|
|
38
|
+
atCompaction: input.atCompaction,
|
|
39
|
+
...(input.cwd !== undefined ? { cwd: input.cwd } : {}),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function storedFactsBaseline(value: unknown): ContextFactsBaseline | undefined {
|
|
44
|
+
if (!value || typeof value !== "object") return undefined;
|
|
45
|
+
const v = value as Record<string, unknown>;
|
|
46
|
+
if (![v.instructions, v.memory, v.git].every((part) => typeof part === "string" && /^[a-f0-9]{64}$/.test(part)) || typeof v.atCompaction !== "boolean") return undefined;
|
|
47
|
+
const { cwd, ...facts } = v;
|
|
48
|
+
return { ...facts, ...(typeof cwd === "string" ? { cwd } : {}) } as unknown as ContextFactsBaseline;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** A refreshed git snapshot must not describe itself as the conversation's initial status. */
|
|
52
|
+
export function compactionGitStatus(text: string | null): string | null {
|
|
53
|
+
return text?.replace("This is the git status at the start of the conversation.", "This is the git status at the latest compaction.") ?? null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** One short factual notice; the historical blocks themselves remain byte-identical. */
|
|
57
|
+
export function resumeFactsNotice(before: ContextFactsBaseline, current: ContextFactsBaseline): string | undefined {
|
|
58
|
+
const changed: string[] = [];
|
|
59
|
+
if (before.git !== current.git) changed.push("git status changed");
|
|
60
|
+
if (before.memory !== current.memory) changed.push("the memory index changed");
|
|
61
|
+
if (before.instructions !== current.instructions) changed.push("CLAUDE.md-family instructions changed");
|
|
62
|
+
if (changed.length === 0) return undefined;
|
|
63
|
+
return `Since the saved context snapshot, ${changed.join("; ")}. The earlier context block reflects ${before.atCompaction ? "the latest compaction" : "the session's start"}, not the current workspace. Read the current files or run git status if needed.`;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Old snapshots lack separate fingerprints: do not invent which half of # claudeMd changed. */
|
|
67
|
+
export function legacyResumeFactsNotice(stack: readonly ReminderEntry[], live: {
|
|
68
|
+
instructions: string | null;
|
|
69
|
+
email: string | null;
|
|
70
|
+
gitStatus: string | null;
|
|
71
|
+
}): string | undefined {
|
|
72
|
+
const changed: string[] = [];
|
|
73
|
+
const previousContext = stack.find((entry) => entry.key === "claude-context-context")?.text ?? null;
|
|
74
|
+
const gitPart = (text: string | null) => text?.match(/# gitStatus\n[\s\S]*?(?=\n\nOne Code attached this context automatically;|$)/)?.[0] ?? null;
|
|
75
|
+
if (gitPart(previousContext) !== gitPart(buildContextBlock(live))) changed.push("git status changed");
|
|
76
|
+
if ((stack.find((entry) => entry.key === "claude-context")?.text ?? null) !== live.instructions) changed.push("CLAUDE.md-family instructions or the memory index changed");
|
|
77
|
+
return changed.length ? `Since the saved context snapshot, ${changed.join("; ")}. The earlier context block reflects the session's start, not the current workspace. Read the current files or run git status if needed.` : undefined;
|
|
78
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/** Session-file context snapshots and the bus contract. No stored value grants permissions. */
|
|
2
|
+
import type { ReminderEntry } from "./reminders.ts";
|
|
3
|
+
|
|
4
|
+
export const CONTEXT_STACK_ENTRY = "one-code:context-stack";
|
|
5
|
+
export const CONTEXT_STATE_ENTRY = "one-code:context-state";
|
|
6
|
+
export const CONTEXT_RESTORE_CHANNEL = "one-code:context-restore";
|
|
7
|
+
export const CONTEXT_BASELINE_CHANNEL = "one-code:context-baseline";
|
|
8
|
+
/** A completed compaction replaces the Claude context facts, including absent blocks. */
|
|
9
|
+
export const CONTEXT_FACTS_REFRESH_CHANNEL = "one-code:context-facts-refresh";
|
|
10
|
+
|
|
11
|
+
export interface ContextStackSnapshot {
|
|
12
|
+
version: 1;
|
|
13
|
+
stack: ReminderEntry[];
|
|
14
|
+
sticky: ReminderEntry[];
|
|
15
|
+
/** Delivered one-shots/breadcrumbs; absent in older snapshots. */
|
|
16
|
+
pinned?: ReminderEntry[];
|
|
17
|
+
/** Emitter-owned announcement baselines, not live configuration. */
|
|
18
|
+
baselines: Record<string, unknown>;
|
|
19
|
+
}
|
|
20
|
+
export interface ContextRestoreRequest { restored?: ContextStackSnapshot }
|
|
21
|
+
export interface ContextBaseline { key: string; value: unknown }
|
|
22
|
+
export interface ContextFactsRefresh { entries: ReminderEntry[]; baseline: unknown }
|
|
23
|
+
interface Bus { emit(channel: string, data: unknown): void }
|
|
24
|
+
|
|
25
|
+
/** Synchronous request/reply: system-reminder restores before the emitters' session_start hooks. */
|
|
26
|
+
export function restoredContext(events: Bus): ContextStackSnapshot | undefined {
|
|
27
|
+
const request: ContextRestoreRequest = {};
|
|
28
|
+
events.emit(CONTEXT_RESTORE_CHANNEL, request);
|
|
29
|
+
return request.restored;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Phase-1 capabilities must be re-emitted by their live owners, including removal. */
|
|
33
|
+
export const LIVE_CONTEXT_KEYS = new Set(["environment", "model-line", "one-shot", "skills"]);
|
|
34
|
+
|
|
35
|
+
/** Session facts and phase-2 emitters keep even an absent original block absent. */
|
|
36
|
+
export const RESTORED_STACK_KEYS = new Set([
|
|
37
|
+
"claude-context", "one-code-context", "claude-context-context", "claude-context-date",
|
|
38
|
+
"total-tokens-first", "auto-mode-note", "deferred-tools", "mcp-instructions", "mcp-failures",
|
|
39
|
+
"subagent-models", "subagent-agents", "subagent-delegation",
|
|
40
|
+
]);
|
|
41
|
+
|
|
42
|
+
function record(value: unknown): value is Record<string, unknown> {
|
|
43
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
44
|
+
}
|
|
45
|
+
function anchor(value: unknown): boolean {
|
|
46
|
+
return record(value) && ((value.kind === "user" && typeof value.timestamp === "number" && Number.isFinite(value.timestamp)) ||
|
|
47
|
+
(value.kind === "toolResult" && typeof value.toolCallId === "string"));
|
|
48
|
+
}
|
|
49
|
+
function entries(value: unknown, placement: "first-prepend" | "sticky-append" | "pinned"): value is ReminderEntry[] {
|
|
50
|
+
return Array.isArray(value) && value.every((e) => record(e) &&
|
|
51
|
+
(placement === "pinned" ? e.placement === "last-append" || e.placement === "user-prepend" : e.placement === placement) && typeof e.text === "string" &&
|
|
52
|
+
typeof e.order === "number" && Number.isFinite(e.order) &&
|
|
53
|
+
(e.key === undefined || typeof e.key === "string") && (e.suffix === undefined || typeof e.suffix === "string") &&
|
|
54
|
+
(e.toolCallId === undefined || placement === "sticky-append" && typeof e.toolCallId === "string") &&
|
|
55
|
+
["raw", "systemRoleOnly", "skipStackCarrier"].every((key) => e[key] === undefined || typeof e[key] === "boolean") &&
|
|
56
|
+
(e.since === undefined || typeof e.since === "number" && Number.isFinite(e.since)) &&
|
|
57
|
+
(e.until === undefined || placement === "sticky-append" && typeof e.until === "number" && Number.isFinite(e.until)) &&
|
|
58
|
+
(e.userPins === undefined || placement === "sticky-append" && Array.isArray(e.userPins) && e.userPins.every((stamp) => typeof stamp === "number" && Number.isFinite(stamp))) &&
|
|
59
|
+
(e.opener === undefined || e.opener === null || typeof e.opener === "number" && Number.isFinite(e.opener)) &&
|
|
60
|
+
(e.tailPin === undefined || anchor(e.tailPin)) && (placement === "pinned" ? anchor(e.pin) : e.pin === undefined));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Read only the selected branch. Malformed/unknown versions are ignored, never partly restored. */
|
|
64
|
+
export function contextStackOnBranch(branch: readonly { type: string; customType?: string; data?: unknown }[]): ContextStackSnapshot | undefined {
|
|
65
|
+
let snapshot: ContextStackSnapshot | undefined;
|
|
66
|
+
for (const entry of branch) {
|
|
67
|
+
if (entry.type !== "custom" || !record(entry.data) || entry.data.version !== 1) continue;
|
|
68
|
+
const data = entry.data;
|
|
69
|
+
if (data.pinned !== undefined && !entries(data.pinned, "pinned")) continue;
|
|
70
|
+
if (entry.customType === CONTEXT_STACK_ENTRY && entries(data.stack, "first-prepend") && entries(data.sticky, "sticky-append") && record(data.baselines)) {
|
|
71
|
+
snapshot = structuredClone(data) as unknown as ContextStackSnapshot;
|
|
72
|
+
} else if (snapshot && entry.customType === CONTEXT_STATE_ENTRY && entries(data.sticky, "sticky-append") && record(data.baselines) &&
|
|
73
|
+
(data.stack === undefined || entries(data.stack, "first-prepend"))) {
|
|
74
|
+
if (data.stack !== undefined) snapshot.stack = structuredClone(data.stack) as ReminderEntry[];
|
|
75
|
+
snapshot.sticky = structuredClone(data.sticky);
|
|
76
|
+
if (data.pinned !== undefined) snapshot.pinned = structuredClone(data.pinned) as ReminderEntry[];
|
|
77
|
+
snapshot.baselines = structuredClone(data.baselines);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return snapshot;
|
|
81
|
+
}
|