oh-my-opencode 5.0.0-beta.31 → 5.0.0-beta.32
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/dist/cli/index.js +136 -44
- package/dist/cli-node/index.js +136 -44
- package/dist/features/background-agent/parent-wake-notifier-types.d.ts +1 -1
- package/dist/features/background-agent/parent-wake-prompt-dispatch.d.ts +1 -1
- package/dist/features/background-agent/parent-wake-session-inspector.d.ts +1 -1
- package/dist/features/monitor/manager-internals.d.ts +1 -1
- package/dist/features/monitor/output-injector-types.d.ts +1 -1
- package/dist/features/team-mode/tools/messaging-live-delivery-recipient.d.ts +1 -0
- package/dist/features/team-mode/tools/messaging-runtime.d.ts +1 -0
- package/dist/hooks/anthropic-context-window-limit-recovery/tool-result-storage.d.ts +11 -0
- package/dist/index.js +286 -99
- package/dist/skills/ultimate-browsing/references/insane-search/README.md +9 -2
- package/dist/tools/session-manager/directory-filter.d.ts +2 -0
- package/dist/tui.js +109 -23
- package/package.json +16 -15
- package/packages/lsp-core/src/lsp/client-diagnostics-freshness.integration.test.ts +47 -19
- package/packages/lsp-core/src/lsp/client-diagnostics-pull-timeout.test.ts +64 -0
- package/packages/lsp-core/src/lsp/client.ts +6 -3
- package/packages/lsp-core/src/lsp/controlled-clock-test-support.ts +61 -0
- package/packages/lsp-core/src/lsp/fixtures/workspace-edit-server.mjs +10 -3
- package/packages/lsp-core/src/lsp/timer-provider.ts +13 -0
- package/packages/lsp-core/src/lsp/transport-request-timeout.test.ts +73 -0
- package/packages/lsp-core/src/lsp/transport.ts +7 -2
- package/packages/lsp-core/src/lsp/workspace-document-state.test.ts +84 -1
- package/packages/lsp-core/src/lsp/workspace-document-state.ts +30 -11
- package/packages/lsp-core/src/tools/parameters.test.ts +51 -0
- package/packages/lsp-daemon/dist/cli.js +41 -16
- package/packages/lsp-daemon/dist/client.js +41 -16
- package/packages/lsp-daemon/dist/index.js +41 -16
- package/packages/lsp-tools-mcp/dist/cli.js +41 -16
- package/packages/lsp-tools-mcp/dist/lsp/manager.js +41 -16
- package/packages/lsp-tools-mcp/dist/mcp.js +41 -16
- package/packages/lsp-tools-mcp/dist/tools.js +41 -16
- package/packages/lsp-tools-mcp/package.json +1 -1
- package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
- package/packages/omo-codex/plugin/components/codegraph/dist/cli.js +3 -17
- package/packages/omo-codex/plugin/components/codegraph/dist/serve.js +3 -17
- package/packages/omo-codex/plugin/components/codegraph/package.json +1 -1
- package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
- package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
- package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +3 -3
- package/packages/omo-codex/plugin/components/lsp/dist/cli.js +39 -16
- package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
- package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
- package/packages/omo-codex/plugin/components/rules/package.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
- package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/AGENTS.md +5 -4
- package/packages/omo-codex/plugin/components/ultrawork/dist/cli.js +489 -4
- package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -2
- package/packages/omo-codex/plugin/components/ultrawork/scripts/sync-directive.mjs +30 -14
- package/packages/omo-codex/plugin/components/ultrawork/src/directive-content.ts +4 -0
- package/packages/omo-codex/plugin/components/ultrawork/src/directive.ts +5 -2
- package/packages/omo-codex/plugin/components/ultrawork/test/directive-source.test.ts +14 -8
- package/packages/omo-codex/plugin/components/ultrawork/test/package-smoke.test.ts +12 -1
- package/packages/omo-codex/plugin/components/ulw-execute-continuation/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/ulw-execute-continuation/package.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/AGENTS.md +4 -2
- package/packages/omo-codex/plugin/components/ulw-loop/README.md +1 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-continuation.js +9 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-reconciliation.d.ts +14 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-reconciliation.js +10 -1
- package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-template.d.ts +8 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-template.js +113 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint.js +7 -5
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-commands.js +2 -1
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-output.d.ts +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-output.js +1 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-subcommands.js +2 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli.js +775 -312
- package/packages/omo-codex/plugin/components/ulw-loop/dist/codex-goal-instruction.js +18 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/codex-goal-snapshot.d.ts +13 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/codex-goal-snapshot.js +14 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/domain-types.d.ts +17 -9
- package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-io.js +17 -2
- package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-missing-recovery.d.ts +13 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-missing-recovery.js +19 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate-aggregate.d.ts +2 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate-aggregate.js +53 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate-artifacts.d.ts +9 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate-artifacts.js +85 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate-fields.js +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate.js +41 -99
- package/packages/omo-codex/plugin/components/ulw-loop/dist/review-blockers.js +4 -2
- package/packages/omo-codex/plugin/components/ulw-loop/dist/spawn-guard.js +184 -14
- package/packages/omo-codex/plugin/components/ulw-loop/dist/status-next-actions.d.ts +6 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/status-next-actions.js +33 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/surface.d.ts +3 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/surface.js +15 -0
- package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +4 -4
- package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +17 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint-continuation.ts +7 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint-reconciliation.ts +25 -1
- package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint-template.ts +127 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint.ts +8 -12
- package/packages/omo-codex/plugin/components/ulw-loop/src/cli-commands.ts +2 -1
- package/packages/omo-codex/plugin/components/ulw-loop/src/cli-output.ts +1 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/cli-subcommands.ts +2 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/codex-goal-instruction.ts +18 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/codex-goal-snapshot.ts +23 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/domain-types.ts +19 -9
- package/packages/omo-codex/plugin/components/ulw-loop/src/plan-io.ts +18 -3
- package/packages/omo-codex/plugin/components/ulw-loop/src/plan-missing-recovery.ts +27 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/quality-gate-aggregate.ts +57 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/quality-gate-artifacts.ts +103 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/quality-gate-fields.ts +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/src/quality-gate.ts +50 -116
- package/packages/omo-codex/plugin/components/ulw-loop/src/review-blockers.ts +4 -2
- package/packages/omo-codex/plugin/components/ulw-loop/src/spawn-guard.ts +186 -12
- package/packages/omo-codex/plugin/components/ulw-loop/src/status-next-actions.ts +36 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/surface.ts +22 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/checkpoint-final.test.ts +66 -3
- package/packages/omo-codex/plugin/components/ulw-loop/test/checkpoint-template.test.ts +92 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/cli-status-next-actions.test.ts +183 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/codex-goal-instruction.test.ts +13 -3
- package/packages/omo-codex/plugin/components/ulw-loop/test/guided-recovery.test.ts +105 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/quality-gate-aggregate.test.ts +101 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/quality-gate-roles.test.ts +16 -15
- package/packages/omo-codex/plugin/components/ulw-loop/test/quality-gate-senpi-surface.test.ts +131 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/quality-gate.test.ts +5 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/spawn-guard.test.ts +300 -3
- package/packages/omo-codex/plugin/components/ulw-loop/test/ultrawork-directive.test.ts +9 -3
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-codegraph-init-guidance.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-codegraph-bootstrap.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
- package/packages/omo-codex/plugin/hooks/stop-checking-ulw-execute-continuation.json +1 -1
- package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
- package/packages/omo-codex/plugin/hooks/subagent-stop-checking-ulw-execute-continuation.json +1 -1
- package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/package-lock.json +13 -13
- package/packages/omo-codex/plugin/package.json +1 -1
- package/packages/omo-codex/plugin/scripts/sync-skills.mjs +31 -2
- package/packages/omo-codex/plugin/skills/ultimate-browsing/references/insane-search/README.md +9 -2
- package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +17 -0
- package/packages/omo-codex/plugin/test/sync-skills-test-support.mjs +11 -1
- package/packages/omo-codex/plugin/test/sync-skills.test.mjs +13 -0
- package/packages/omo-codex/plugin/test/ultrawork-skill-pointer.test.mjs +5 -2
- package/packages/omo-codex/scripts/install-dist/install-local.mjs +2 -2
- package/packages/shared-skills/skills/ultimate-browsing/references/insane-search/README.md +9 -2
- package/dist/features/builtin-skills/index.d.ts +0 -1
- package/dist/features/builtin-skills/skill-file-loader.d.ts +0 -1
- package/dist/features/builtin-skills/skills/agent-browser-skill.d.ts +0 -1
- package/dist/features/builtin-skills/skills/agent-browser-template.d.ts +0 -1
- package/dist/features/builtin-skills/skills/debugging.d.ts +0 -1
- package/dist/features/builtin-skills/skills/dev-browser.d.ts +0 -1
- package/dist/features/builtin-skills/skills/frontend.d.ts +0 -1
- package/dist/features/builtin-skills/skills/git-master-sections/commit-atomic-planning.d.ts +0 -1
- package/dist/features/builtin-skills/skills/git-master-sections/commit-context-analysis.d.ts +0 -1
- package/dist/features/builtin-skills/skills/git-master-sections/commit-execution-verification.d.ts +0 -1
- package/dist/features/builtin-skills/skills/git-master-sections/commit-workflow.d.ts +0 -1
- package/dist/features/builtin-skills/skills/git-master-sections/history-search-workflow.d.ts +0 -1
- package/dist/features/builtin-skills/skills/git-master-sections/overview.d.ts +0 -1
- package/dist/features/builtin-skills/skills/git-master-sections/quick-reference.d.ts +0 -1
- package/dist/features/builtin-skills/skills/git-master-sections/rebase-workflow.d.ts +0 -1
- package/dist/features/builtin-skills/skills/git-master-skill-metadata.d.ts +0 -1
- package/dist/features/builtin-skills/skills/git-master.d.ts +0 -1
- package/dist/features/builtin-skills/skills/index.d.ts +0 -1
- package/dist/features/builtin-skills/skills/init-deep.d.ts +0 -1
- package/dist/features/builtin-skills/skills/playwright-cli.d.ts +0 -1
- package/dist/features/builtin-skills/skills/playwright-mcp-skill.d.ts +0 -1
- package/dist/features/builtin-skills/skills/playwright.d.ts +0 -1
- package/dist/features/builtin-skills/skills/remove-ai-slops.d.ts +0 -1
- package/dist/features/builtin-skills/skills/review-work.d.ts +0 -1
- package/dist/features/builtin-skills/skills/security-research.d.ts +0 -1
- package/dist/features/builtin-skills/skills/security-review.d.ts +0 -1
- package/dist/features/builtin-skills/skills/team-mode.d.ts +0 -1
- package/dist/features/builtin-skills/skills/visual-qa.d.ts +0 -1
- package/dist/features/builtin-skills/skills.d.ts +0 -1
- package/dist/features/builtin-skills/types.d.ts +0 -1
- package/dist/features/claude-code-plugin-loader/agent-loader.d.ts +0 -1
- package/dist/features/claude-code-plugin-loader/command-loader.d.ts +0 -1
- package/dist/features/claude-code-plugin-loader/discovery-core.d.ts +0 -1
- package/dist/features/claude-code-plugin-loader/discovery-paths.d.ts +0 -1
- package/dist/features/claude-code-plugin-loader/discovery.d.ts +0 -1
- package/dist/features/claude-code-plugin-loader/hook-loader.d.ts +0 -1
- package/dist/features/claude-code-plugin-loader/index.d.ts +0 -1
- package/dist/features/claude-code-plugin-loader/install-path-resolver.d.ts +0 -1
- package/dist/features/claude-code-plugin-loader/installed-plugin-database.d.ts +0 -1
- package/dist/features/claude-code-plugin-loader/loaded-plugin.d.ts +0 -1
- package/dist/features/claude-code-plugin-loader/loader.d.ts +0 -1
- package/dist/features/claude-code-plugin-loader/mcp-server-loader.d.ts +0 -1
- package/dist/features/claude-code-plugin-loader/plugin-key.d.ts +0 -1
- package/dist/features/claude-code-plugin-loader/plugin-manifest.d.ts +0 -1
- package/dist/features/claude-code-plugin-loader/plugin-path-resolver.d.ts +0 -1
- package/dist/features/claude-code-plugin-loader/plugin-settings.d.ts +0 -1
- package/dist/features/claude-code-plugin-loader/scope-filter.d.ts +0 -1
- package/dist/features/claude-code-plugin-loader/skill-loader.d.ts +0 -1
- package/dist/features/claude-code-plugin-loader/types.d.ts +0 -1
- package/dist/shared/prompt-async-gate/message-inspection-error.d.ts +0 -1
- package/dist/shared/prompt-async-gate/pending-tool-turn.d.ts +0 -1
- package/dist/shared/prompt-async-gate/prompt-message-state.d.ts +0 -1
- package/dist/shared/prompt-async-gate/queue.d.ts +0 -1
- package/dist/shared/prompt-async-gate/recent-dispatches.d.ts +0 -1
- package/dist/shared/prompt-async-gate/reservations.d.ts +0 -1
- package/dist/shared/prompt-async-gate/semantic-dedupe.d.ts +0 -1
- package/dist/shared/prompt-async-gate/session-idle-dispatch.d.ts +0 -1
- package/dist/shared/prompt-async-gate/timing.d.ts +0 -1
- package/dist/shared/prompt-async-gate/types.d.ts +0 -1
- package/packages/omo-codex/plugin/components/ultrawork/directive.md +0 -483
- package/packages/omo-codex/plugin/components/ultrawork/skills/ultrawork/SKILL.md +0 -490
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
1. WebFetch, 즉흥 curl, 수동 헤더 조합 **시도 금지**
|
|
17
17
|
2. 즉시 다음을 실행:
|
|
18
18
|
```bash
|
|
19
|
-
python3 -m engine "<URL>" [--selector "<CSS>"] [--device auto|desktop|mobile] [--trace]
|
|
19
|
+
python3 -m engine "<URL>" [--selector "<CSS>"] [--device auto|desktop|mobile] [--trace] \
|
|
20
|
+
[--no-retry] [--no-extract]
|
|
20
21
|
```
|
|
21
22
|
3. 종료코드 0(ok) 또는 1(fail) 받은 뒤 판단. trace를 먼저 읽고 재시도 결정.
|
|
22
23
|
4. 실패 시에만 `--trace --json`으로 재호출해서 원인 진단 후 `--device` 또는 `user_hint` 조정.
|
|
@@ -135,7 +136,13 @@ result = fetch(
|
|
|
135
136
|
|
|
136
137
|
if result.ok:
|
|
137
138
|
print(result.verdict) # strong_ok | weak_ok
|
|
138
|
-
html = result.content
|
|
139
|
+
html = result.content # raw body — 단, content-rescue가 발동한 경우 구조 텍스트
|
|
140
|
+
# v0.10.0 content-rescue: PDF 응답은 pypdf 추출 텍스트, visible text가 얇은
|
|
141
|
+
# SPA 셸은 JSON-LD articleBody / 렌더된 innerText로 대체될 수 있다.
|
|
142
|
+
# result.extraction_source로 판별: "raw"(원문 그대로) | pdf | json_ld | *+inner_text.
|
|
143
|
+
# 일반 HTML 성공은 항상 raw. 끄기: enable_extraction=False / --no-extract.
|
|
144
|
+
# 429/502/503/504는 probe에서 백오프 재시도(Retry-After 반영, 총 10초 캡);
|
|
145
|
+
# 끄기: enable_retry=False / --no-retry.
|
|
139
146
|
else:
|
|
140
147
|
# Phase 3 수동 개입 (Playwright MCP) 필요 — result.trace로 원인 진단
|
|
141
148
|
pass
|
package/dist/tui.js
CHANGED
|
@@ -17180,11 +17180,7 @@ var init_memory = __esm(() => {
|
|
|
17180
17180
|
}).strict();
|
|
17181
17181
|
OmoMemoryRecallSchema = object({
|
|
17182
17182
|
enabled: boolean2().default(true),
|
|
17183
|
-
max_items: number2().int().min(1).max(5).default(2)
|
|
17184
|
-
budget_tokens: number2().int().positive().default(600),
|
|
17185
|
-
excerpt_chars: number2().int().positive().default(200),
|
|
17186
|
-
min_score: number2().optional(),
|
|
17187
|
-
exclude: array(string2()).default([])
|
|
17183
|
+
max_items: number2().int().min(1).max(5).default(2)
|
|
17188
17184
|
}).strict();
|
|
17189
17185
|
OmoMemoryNudgeSchema = object({
|
|
17190
17186
|
enabled: boolean2().default(true),
|
|
@@ -17234,11 +17230,7 @@ var init_memory = __esm(() => {
|
|
|
17234
17230
|
}).strict();
|
|
17235
17231
|
OmoMemoryRecallLayerSchema = object({
|
|
17236
17232
|
enabled: boolean2().optional(),
|
|
17237
|
-
max_items: number2().int().min(1).max(5).optional()
|
|
17238
|
-
budget_tokens: number2().int().positive().optional(),
|
|
17239
|
-
excerpt_chars: number2().int().positive().optional(),
|
|
17240
|
-
min_score: number2().optional(),
|
|
17241
|
-
exclude: array(string2()).optional()
|
|
17233
|
+
max_items: number2().int().min(1).max(5).optional()
|
|
17242
17234
|
}).strict();
|
|
17243
17235
|
OmoMemoryNudgeLayerSchema = object({
|
|
17244
17236
|
enabled: boolean2().optional(),
|
|
@@ -17309,13 +17301,7 @@ var init_memory = __esm(() => {
|
|
|
17309
17301
|
write_notice: OmoMemoryWriteNoticeSchema.default({ enabled: true }),
|
|
17310
17302
|
sync: OmoMemorySyncSchema.default({ enabled: true }),
|
|
17311
17303
|
search: OmoMemorySearchSchema.default({ enabled: true }),
|
|
17312
|
-
recall: OmoMemoryRecallSchema.default({
|
|
17313
|
-
enabled: true,
|
|
17314
|
-
max_items: 2,
|
|
17315
|
-
budget_tokens: 600,
|
|
17316
|
-
excerpt_chars: 200,
|
|
17317
|
-
exclude: []
|
|
17318
|
-
}),
|
|
17304
|
+
recall: OmoMemoryRecallSchema.default({ enabled: true, max_items: 2 }),
|
|
17319
17305
|
compile_warn_tokens: number2().int().positive().default(30000),
|
|
17320
17306
|
agents: record(string2(), OmoMemoryAgentOverridesSchema).default({})
|
|
17321
17307
|
}).strict();
|
|
@@ -20048,6 +20034,111 @@ var init_supplemental_entries = __esm(() => {
|
|
|
20048
20034
|
input: 272000,
|
|
20049
20035
|
output: 128000
|
|
20050
20036
|
}
|
|
20037
|
+
},
|
|
20038
|
+
"grok-4.5": {
|
|
20039
|
+
id: "grok-4.5",
|
|
20040
|
+
family: "grok",
|
|
20041
|
+
reasoning: false,
|
|
20042
|
+
temperature: true,
|
|
20043
|
+
toolCall: true,
|
|
20044
|
+
modalities: {
|
|
20045
|
+
input: ["text", "image"],
|
|
20046
|
+
output: ["text"]
|
|
20047
|
+
},
|
|
20048
|
+
limit: {
|
|
20049
|
+
context: 500000,
|
|
20050
|
+
output: 32768
|
|
20051
|
+
}
|
|
20052
|
+
},
|
|
20053
|
+
"xai/grok-4.5": {
|
|
20054
|
+
id: "xai/grok-4.5",
|
|
20055
|
+
family: "grok",
|
|
20056
|
+
reasoning: false,
|
|
20057
|
+
temperature: true,
|
|
20058
|
+
toolCall: true,
|
|
20059
|
+
modalities: {
|
|
20060
|
+
input: ["text", "image"],
|
|
20061
|
+
output: ["text"]
|
|
20062
|
+
},
|
|
20063
|
+
limit: {
|
|
20064
|
+
context: 500000,
|
|
20065
|
+
output: 32768
|
|
20066
|
+
}
|
|
20067
|
+
},
|
|
20068
|
+
"x-ai/grok-4.5": {
|
|
20069
|
+
id: "x-ai/grok-4.5",
|
|
20070
|
+
family: "grok",
|
|
20071
|
+
reasoning: false,
|
|
20072
|
+
temperature: true,
|
|
20073
|
+
toolCall: true,
|
|
20074
|
+
modalities: {
|
|
20075
|
+
input: ["text", "image"],
|
|
20076
|
+
output: ["text"]
|
|
20077
|
+
},
|
|
20078
|
+
limit: {
|
|
20079
|
+
context: 500000,
|
|
20080
|
+
output: 32768
|
|
20081
|
+
}
|
|
20082
|
+
},
|
|
20083
|
+
"grok-4.6": {
|
|
20084
|
+
id: "grok-4.6",
|
|
20085
|
+
family: "grok",
|
|
20086
|
+
reasoning: false,
|
|
20087
|
+
temperature: true,
|
|
20088
|
+
toolCall: true,
|
|
20089
|
+
modalities: {
|
|
20090
|
+
input: ["text", "image"],
|
|
20091
|
+
output: ["text"]
|
|
20092
|
+
},
|
|
20093
|
+
limit: {
|
|
20094
|
+
context: 500000,
|
|
20095
|
+
output: 32768
|
|
20096
|
+
}
|
|
20097
|
+
},
|
|
20098
|
+
"xai/grok-4.6": {
|
|
20099
|
+
id: "xai/grok-4.6",
|
|
20100
|
+
family: "grok",
|
|
20101
|
+
reasoning: false,
|
|
20102
|
+
temperature: true,
|
|
20103
|
+
toolCall: true,
|
|
20104
|
+
modalities: {
|
|
20105
|
+
input: ["text", "image"],
|
|
20106
|
+
output: ["text"]
|
|
20107
|
+
},
|
|
20108
|
+
limit: {
|
|
20109
|
+
context: 500000,
|
|
20110
|
+
output: 32768
|
|
20111
|
+
}
|
|
20112
|
+
},
|
|
20113
|
+
"xai/grok-build-0.1": {
|
|
20114
|
+
id: "xai/grok-build-0.1",
|
|
20115
|
+
family: "grok",
|
|
20116
|
+
reasoning: false,
|
|
20117
|
+
temperature: true,
|
|
20118
|
+
toolCall: true,
|
|
20119
|
+
modalities: {
|
|
20120
|
+
input: ["text", "image"],
|
|
20121
|
+
output: ["text"]
|
|
20122
|
+
},
|
|
20123
|
+
limit: {
|
|
20124
|
+
context: 256000,
|
|
20125
|
+
output: 32768
|
|
20126
|
+
}
|
|
20127
|
+
},
|
|
20128
|
+
"x-ai/grok-build-0.1": {
|
|
20129
|
+
id: "x-ai/grok-build-0.1",
|
|
20130
|
+
family: "grok",
|
|
20131
|
+
reasoning: false,
|
|
20132
|
+
temperature: true,
|
|
20133
|
+
toolCall: true,
|
|
20134
|
+
modalities: {
|
|
20135
|
+
input: ["text", "image"],
|
|
20136
|
+
output: ["text"]
|
|
20137
|
+
},
|
|
20138
|
+
limit: {
|
|
20139
|
+
context: 256000,
|
|
20140
|
+
output: 32768
|
|
20141
|
+
}
|
|
20051
20142
|
}
|
|
20052
20143
|
};
|
|
20053
20144
|
});
|
|
@@ -78539,14 +78630,9 @@ var init_claude_code_plugin_loader = __esm(() => {
|
|
|
78539
78630
|
init_hook_loader();
|
|
78540
78631
|
});
|
|
78541
78632
|
|
|
78542
|
-
// packages/omo-opencode/src/features/claude-code-plugin-loader/index.ts
|
|
78543
|
-
var init_claude_code_plugin_loader2 = __esm(() => {
|
|
78544
|
-
init_claude_code_plugin_loader();
|
|
78545
|
-
});
|
|
78546
|
-
|
|
78547
78633
|
// packages/omo-opencode/src/shared/plugin-command-discovery.ts
|
|
78548
78634
|
var init_plugin_command_discovery = __esm(() => {
|
|
78549
|
-
|
|
78635
|
+
init_claude_code_plugin_loader();
|
|
78550
78636
|
});
|
|
78551
78637
|
|
|
78552
78638
|
// packages/omo-opencode/src/shared/session-category-registry.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "oh-my-opencode",
|
|
3
|
-
"version": "5.0.0-beta.
|
|
3
|
+
"version": "5.0.0-beta.32",
|
|
4
4
|
"description": "The Best AI Agent Harness - Batteries-Included OpenCode Plugin with Multi-Model Orchestration, Parallel Background Agents, and Crafted LSP/AST Tools",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -113,6 +113,7 @@
|
|
|
113
113
|
"install:codex-dev": "bun run script/build-codex-install.ts && bun run script/install-codex-dev.ts",
|
|
114
114
|
"build:codex-plugin": "npm --prefix packages/omo-codex/plugin ci && bun run --cwd packages/omo-codex/plugin build",
|
|
115
115
|
"build:senpi-plugin": "bun run build:lsp-daemon && bun run build:ast-grep-mcp && bun run build:senpi-plugin:stage",
|
|
116
|
+
"build:senpi-plugin:native": "node packages/omo-senpi/plugin/scripts/stage-lsp-daemon-runtime.mjs && node packages/omo-senpi/plugin/scripts/stage-ast-grep-mcp-runtime.mjs && node packages/omo-senpi/plugin/scripts/stage-agent-toolkit.mjs && node packages/omo-senpi/plugin/scripts/build-extension.mjs && node packages/omo-senpi/plugin/scripts/sync-skills.mjs && node packages/omo-senpi/plugin/scripts/embed-directive.mjs --check && node packages/omo-senpi/plugin/scripts/build-install.mjs",
|
|
116
117
|
"build:senpi-plugin:stage": "bun run build:materialize-frontend && node packages/omo-senpi/plugin/scripts/stage-lsp-daemon-runtime.mjs && node packages/omo-senpi/plugin/scripts/stage-ast-grep-mcp-runtime.mjs && node packages/omo-senpi/plugin/scripts/stage-agent-toolkit.mjs && node packages/omo-senpi/plugin/scripts/build-extension.mjs && node packages/omo-senpi/plugin/scripts/sync-skills.mjs && node packages/omo-senpi/plugin/scripts/embed-directive.mjs --check && node packages/omo-senpi/plugin/scripts/build-install.mjs",
|
|
117
118
|
"build:materialize-frontend": "node packages/omo-codex/plugin/scripts/materialize-shared-upstreams.mjs --strict",
|
|
118
119
|
"build:shared-skills-assets": "bun run build:materialize-frontend && rm -rf dist/skills && cp -R packages/shared-skills/skills dist/skills",
|
|
@@ -181,7 +182,7 @@
|
|
|
181
182
|
"zod": "^4.4.3"
|
|
182
183
|
},
|
|
183
184
|
"devDependencies": {
|
|
184
|
-
"@code-yeongyu/senpi": "2026.
|
|
185
|
+
"@code-yeongyu/senpi": "2026.9.2",
|
|
185
186
|
"@oh-my-opencode/agents-md-core": "workspace:*",
|
|
186
187
|
"@oh-my-opencode/ast-grep-mcp": "workspace:*",
|
|
187
188
|
"@oh-my-opencode/boulder-state": "workspace:*",
|
|
@@ -222,18 +223,18 @@
|
|
|
222
223
|
"typescript": "^7.0.2"
|
|
223
224
|
},
|
|
224
225
|
"optionalDependencies": {
|
|
225
|
-
"oh-my-opencode-darwin-arm64": "5.0.0-beta.
|
|
226
|
-
"oh-my-opencode-darwin-x64": "5.0.0-beta.
|
|
227
|
-
"oh-my-opencode-darwin-x64-baseline": "5.0.0-beta.
|
|
228
|
-
"oh-my-opencode-linux-arm64": "5.0.0-beta.
|
|
229
|
-
"oh-my-opencode-linux-arm64-musl": "5.0.0-beta.
|
|
230
|
-
"oh-my-opencode-linux-x64": "5.0.0-beta.
|
|
231
|
-
"oh-my-opencode-linux-x64-baseline": "5.0.0-beta.
|
|
232
|
-
"oh-my-opencode-linux-x64-musl": "5.0.0-beta.
|
|
233
|
-
"oh-my-opencode-linux-x64-musl-baseline": "5.0.0-beta.
|
|
234
|
-
"oh-my-opencode-windows-arm64": "5.0.0-beta.
|
|
235
|
-
"oh-my-opencode-windows-x64": "5.0.0-beta.
|
|
236
|
-
"oh-my-opencode-windows-x64-baseline": "5.0.0-beta.
|
|
226
|
+
"oh-my-opencode-darwin-arm64": "5.0.0-beta.32",
|
|
227
|
+
"oh-my-opencode-darwin-x64": "5.0.0-beta.32",
|
|
228
|
+
"oh-my-opencode-darwin-x64-baseline": "5.0.0-beta.32",
|
|
229
|
+
"oh-my-opencode-linux-arm64": "5.0.0-beta.32",
|
|
230
|
+
"oh-my-opencode-linux-arm64-musl": "5.0.0-beta.32",
|
|
231
|
+
"oh-my-opencode-linux-x64": "5.0.0-beta.32",
|
|
232
|
+
"oh-my-opencode-linux-x64-baseline": "5.0.0-beta.32",
|
|
233
|
+
"oh-my-opencode-linux-x64-musl": "5.0.0-beta.32",
|
|
234
|
+
"oh-my-opencode-linux-x64-musl-baseline": "5.0.0-beta.32",
|
|
235
|
+
"oh-my-opencode-windows-arm64": "5.0.0-beta.32",
|
|
236
|
+
"oh-my-opencode-windows-x64": "5.0.0-beta.32",
|
|
237
|
+
"oh-my-opencode-windows-x64-baseline": "5.0.0-beta.32"
|
|
237
238
|
},
|
|
238
239
|
"overrides": {
|
|
239
240
|
"@earendil-works/pi-agent-core": "0.84.2",
|
|
@@ -251,6 +252,6 @@
|
|
|
251
252
|
"@code-yeongyu/comment-checker"
|
|
252
253
|
],
|
|
253
254
|
"patchedDependencies": {
|
|
254
|
-
"@code-yeongyu/senpi@2026.
|
|
255
|
+
"@code-yeongyu/senpi@2026.9.2": "patches/@code-yeongyu%2Fsenpi@2026.9.2.patch"
|
|
255
256
|
}
|
|
256
257
|
}
|
|
@@ -2,6 +2,8 @@ import { writeFileSync } from "node:fs";
|
|
|
2
2
|
|
|
3
3
|
import { afterEach, describe, expect, it } from "bun:test";
|
|
4
4
|
|
|
5
|
+
import { ControlledClock } from "./controlled-clock-test-support.js";
|
|
6
|
+
|
|
5
7
|
import {
|
|
6
8
|
createWorkspaceEditTestHarness,
|
|
7
9
|
diagnostic,
|
|
@@ -17,6 +19,7 @@ afterEach(async () => {
|
|
|
17
19
|
|
|
18
20
|
describe("LspClient diagnostics freshness", () => {
|
|
19
21
|
it("#given a versionless publish that arrives after the current change #when no newer eligible publish arrives before quiescence #then diagnostics wait for that quiescence window and return the versionless payload", async () => {
|
|
22
|
+
const clock = new ControlledClock();
|
|
20
23
|
const context = await harness.makeClient(
|
|
21
24
|
{
|
|
22
25
|
publishDiagnostics: [
|
|
@@ -27,7 +30,7 @@ describe("LspClient diagnostics freshness", () => {
|
|
|
27
30
|
},
|
|
28
31
|
],
|
|
29
32
|
},
|
|
30
|
-
{ diagnosticsFreshnessTimeoutMs: 80, versionlessPublishQuiescenceMs: 20 },
|
|
33
|
+
{ diagnosticsFreshnessTimeoutMs: 80, versionlessPublishQuiescenceMs: 20, timerProvider: clock },
|
|
31
34
|
);
|
|
32
35
|
await context.client.openFile(context.source);
|
|
33
36
|
const versionlessDelivery = waitForEventCount(
|
|
@@ -35,42 +38,59 @@ describe("LspClient diagnostics freshness", () => {
|
|
|
35
38
|
(event) => event.type === "clientResponse" && event.method === "workspace/configuration",
|
|
36
39
|
1,
|
|
37
40
|
);
|
|
38
|
-
const startedAt = Date.now();
|
|
39
41
|
writeFileSync(context.source, "const after = 1;\n", "utf-8");
|
|
40
42
|
await context.client.openFile(context.source);
|
|
41
43
|
expect(await versionlessDelivery).toHaveLength(1);
|
|
42
44
|
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
+
const pending = context.client.diagnostics(context.source);
|
|
46
|
+
await clock.waitForTimer(20);
|
|
47
|
+
expect(clock.scheduledDelays.at(-1)).toBe(20);
|
|
48
|
+
clock.advanceBy(20);
|
|
49
|
+
const result = await pending;
|
|
45
50
|
|
|
46
51
|
expect(result.items).toEqual([diagnostic("post-generation-versionless")]);
|
|
47
|
-
expect(elapsedMs).toBeGreaterThanOrEqual(15);
|
|
48
52
|
});
|
|
49
53
|
|
|
50
54
|
it("#given only a pre-generation versionless publish #when a newer local version asks for diagnostics #then the stale versionless publish is ignored and the request does not resolve clean", async () => {
|
|
55
|
+
const clock = new ControlledClock();
|
|
51
56
|
const context = await harness.makeClient(
|
|
52
57
|
{
|
|
53
58
|
publishDiagnostics: [
|
|
54
59
|
{
|
|
55
60
|
trigger: "didOpen",
|
|
56
61
|
diagnostics: [diagnostic("pre-generation-versionless")],
|
|
62
|
+
awaitClientDelivery: true,
|
|
57
63
|
},
|
|
58
64
|
],
|
|
59
65
|
},
|
|
60
|
-
{ diagnosticsFreshnessTimeoutMs: 300, versionlessPublishQuiescenceMs: 5 },
|
|
66
|
+
{ diagnosticsFreshnessTimeoutMs: 300, versionlessPublishQuiescenceMs: 5, timerProvider: clock },
|
|
61
67
|
);
|
|
62
68
|
await context.client.openFile(context.source);
|
|
63
|
-
|
|
69
|
+
await waitForEventCount(
|
|
70
|
+
context.events,
|
|
71
|
+
(event) => event.type === "clientResponse" && event.method === "workspace/configuration",
|
|
72
|
+
1,
|
|
73
|
+
);
|
|
74
|
+
const initial = context.client.diagnostics(context.source);
|
|
75
|
+
await clock.waitForTimer(5);
|
|
76
|
+
expect(clock.scheduledDelays.at(-1)).toBe(5);
|
|
77
|
+
clock.advanceBy(5);
|
|
78
|
+
expect((await initial).items).toEqual([diagnostic("pre-generation-versionless")]);
|
|
64
79
|
writeFileSync(context.source, "const after = 1;\n", "utf-8");
|
|
65
80
|
await context.client.openFile(context.source);
|
|
66
81
|
|
|
67
|
-
const
|
|
82
|
+
const pending = context.client.diagnostics(context.source);
|
|
83
|
+
await clock.waitForTimer(300);
|
|
84
|
+
expect(clock.scheduledDelays.at(-1)).toBe(300);
|
|
85
|
+
clock.advanceBy(300);
|
|
86
|
+
const result = await pending;
|
|
68
87
|
|
|
69
88
|
expect(result.items).toEqual([]);
|
|
70
89
|
expect(result.transientError?.kind).toBe("freshness_timeout");
|
|
71
90
|
});
|
|
72
91
|
|
|
73
92
|
it("#given stale and future publish versions #when diagnostics target the current version #then neither stale nor future diagnostics satisfy the request", async () => {
|
|
93
|
+
const staleClock = new ControlledClock();
|
|
74
94
|
const stale = await harness.makeClient(
|
|
75
95
|
{
|
|
76
96
|
publishDiagnostics: [
|
|
@@ -82,7 +102,7 @@ describe("LspClient diagnostics freshness", () => {
|
|
|
82
102
|
},
|
|
83
103
|
],
|
|
84
104
|
},
|
|
85
|
-
{ diagnosticsFreshnessTimeoutMs: 100, versionlessPublishQuiescenceMs: 5 },
|
|
105
|
+
{ diagnosticsFreshnessTimeoutMs: 100, versionlessPublishQuiescenceMs: 5, timerProvider: staleClock },
|
|
86
106
|
);
|
|
87
107
|
await stale.client.openFile(stale.source);
|
|
88
108
|
const staleDelivery = waitForEventCount(
|
|
@@ -94,13 +114,17 @@ describe("LspClient diagnostics freshness", () => {
|
|
|
94
114
|
await stale.client.openFile(stale.source);
|
|
95
115
|
expect(await staleDelivery).toHaveLength(1);
|
|
96
116
|
|
|
97
|
-
const
|
|
117
|
+
const stalePending = stale.client.diagnostics(stale.source);
|
|
118
|
+
await staleClock.waitForTimer(100);
|
|
119
|
+
staleClock.advanceBy(100);
|
|
120
|
+
const staleResult = await stalePending;
|
|
98
121
|
|
|
99
122
|
expect(staleResult.items).toEqual([]);
|
|
100
123
|
expect(staleResult.transientError?.kind).toBe("freshness_timeout");
|
|
101
124
|
|
|
102
125
|
await harness.cleanup();
|
|
103
126
|
|
|
127
|
+
const futureClock = new ControlledClock();
|
|
104
128
|
const future = await harness.makeClient(
|
|
105
129
|
{
|
|
106
130
|
publishDiagnostics: [
|
|
@@ -112,7 +136,7 @@ describe("LspClient diagnostics freshness", () => {
|
|
|
112
136
|
},
|
|
113
137
|
],
|
|
114
138
|
},
|
|
115
|
-
{ diagnosticsFreshnessTimeoutMs: 100, versionlessPublishQuiescenceMs: 5 },
|
|
139
|
+
{ diagnosticsFreshnessTimeoutMs: 100, versionlessPublishQuiescenceMs: 5, timerProvider: futureClock },
|
|
116
140
|
);
|
|
117
141
|
await future.client.openFile(future.source);
|
|
118
142
|
const futureDelivery = waitForEventCount(
|
|
@@ -124,7 +148,10 @@ describe("LspClient diagnostics freshness", () => {
|
|
|
124
148
|
await future.client.openFile(future.source);
|
|
125
149
|
expect(await futureDelivery).toHaveLength(1);
|
|
126
150
|
|
|
127
|
-
const
|
|
151
|
+
const futurePending = future.client.diagnostics(future.source);
|
|
152
|
+
await futureClock.waitForTimer(100);
|
|
153
|
+
futureClock.advanceBy(100);
|
|
154
|
+
const futureResult = await futurePending;
|
|
128
155
|
|
|
129
156
|
expect(futureResult.items).toEqual([]);
|
|
130
157
|
expect(futureResult.transientError?.kind).toBe("freshness_timeout");
|
|
@@ -136,7 +163,7 @@ describe("LspClient diagnostics freshness", () => {
|
|
|
136
163
|
capabilities: { diagnosticProvider: { interFileDependencies: false, workspaceDiagnostics: false } },
|
|
137
164
|
diagnosticResponses: [
|
|
138
165
|
{
|
|
139
|
-
|
|
166
|
+
releaseOnDidChange: true,
|
|
140
167
|
report: { kind: "full", resultId: "v1", items: [diagnostic("pull-stale")] },
|
|
141
168
|
},
|
|
142
169
|
{
|
|
@@ -248,19 +275,20 @@ describe("LspClient diagnostics freshness", () => {
|
|
|
248
275
|
});
|
|
249
276
|
|
|
250
277
|
it("#given a server without pull support that never publishes diagnostics #when diagnostics run on a clean file #then the request resolves clean after the freshness window instead of reporting a timeout", async () => {
|
|
278
|
+
const clock = new ControlledClock();
|
|
251
279
|
const context = await harness.makeClient(
|
|
252
280
|
{},
|
|
253
|
-
{ diagnosticsFreshnessTimeoutMs: 60, versionlessPublishQuiescenceMs: 5 },
|
|
281
|
+
{ diagnosticsFreshnessTimeoutMs: 60, versionlessPublishQuiescenceMs: 5, timerProvider: clock },
|
|
254
282
|
);
|
|
255
283
|
|
|
256
|
-
const
|
|
257
|
-
|
|
258
|
-
|
|
284
|
+
const pending = context.client.diagnostics(context.source);
|
|
285
|
+
await clock.waitForTimer(60);
|
|
286
|
+
expect(clock.scheduledDelays.at(-1)).toBe(60);
|
|
287
|
+
clock.advanceBy(60);
|
|
288
|
+
const result = await pending;
|
|
259
289
|
|
|
260
290
|
expect(result.transientError).toBeUndefined();
|
|
261
291
|
expect(result.items).toEqual([]);
|
|
262
|
-
// The full freshness window is still honored so a slow publisher can win.
|
|
263
|
-
expect(elapsedMs).toBeGreaterThanOrEqual(45);
|
|
264
292
|
});
|
|
265
293
|
|
|
266
294
|
it("#given a pull-supported server that cached diagnostics for an older document version #when the file changes and a later pull is rejected as unsupported without any publish #then the fallback resolves empty instead of returning the stale cached diagnostics", async () => {
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
|
|
5
|
+
import { afterEach, describe, expect, it } from "bun:test";
|
|
6
|
+
|
|
7
|
+
import { ControlledClock } from "./controlled-clock-test-support.js";
|
|
8
|
+
import { LspClient } from "./client.js";
|
|
9
|
+
import { LspRequestTimeoutError } from "./errors.js";
|
|
10
|
+
import type { ResolvedServer } from "./types.js";
|
|
11
|
+
|
|
12
|
+
const fakeServer: ResolvedServer = {
|
|
13
|
+
id: "pull-timeout-fake",
|
|
14
|
+
command: ["pull-timeout-fake"],
|
|
15
|
+
extensions: [".ts"],
|
|
16
|
+
priority: 0,
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
class HangingPullClient extends LspClient {
|
|
20
|
+
readonly grantedPullTimeouts: number[] = [];
|
|
21
|
+
|
|
22
|
+
constructor(root: string, private readonly clock: ControlledClock) {
|
|
23
|
+
super(root, fakeServer, { diagnosticsFreshnessTimeoutMs: 50, versionlessPublishQuiescenceMs: 5, timerProvider: clock });
|
|
24
|
+
this.setDiagnosticPullSupported(true);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
protected override async sendNotification(): Promise<void> {}
|
|
28
|
+
|
|
29
|
+
protected override async sendRequest<T>(
|
|
30
|
+
method: string,
|
|
31
|
+
...args: [] | [unknown] | [unknown, { timeoutMs?: number; signal?: AbortSignal }]
|
|
32
|
+
): Promise<T> {
|
|
33
|
+
if (method !== "textDocument/diagnostic") {
|
|
34
|
+
throw new Error(`unexpected request in hanging-pull fixture: ${method}`);
|
|
35
|
+
}
|
|
36
|
+
const timeoutMs = args[1]?.timeoutMs ?? 0;
|
|
37
|
+
this.grantedPullTimeouts.push(timeoutMs);
|
|
38
|
+
this.clock.advanceBy(timeoutMs);
|
|
39
|
+
throw new LspRequestTimeoutError(method);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
describe("LspClient diagnostics pull timeout conversion", () => {
|
|
44
|
+
let root: string | null = null;
|
|
45
|
+
|
|
46
|
+
afterEach(() => {
|
|
47
|
+
if (root !== null) rmSync(root, { recursive: true, force: true });
|
|
48
|
+
root = null;
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("#given a pull that exhausts the freshness window #when diagnostics settle #then the timeout converts to freshness_timeout instead of rejecting", async () => {
|
|
52
|
+
root = mkdtempSync(join(tmpdir(), "lsp-pull-timeout-"));
|
|
53
|
+
const source = join(root, "sample.ts");
|
|
54
|
+
writeFileSync(source, "const value = 1;\n", "utf-8");
|
|
55
|
+
const clock = new ControlledClock();
|
|
56
|
+
const client = new HangingPullClient(root, clock);
|
|
57
|
+
|
|
58
|
+
const result = await client.diagnostics(source);
|
|
59
|
+
|
|
60
|
+
expect(client.grantedPullTimeouts).toEqual([50]);
|
|
61
|
+
expect(result.items).toEqual([]);
|
|
62
|
+
expect(result.transientError?.kind).toBe("freshness_timeout");
|
|
63
|
+
});
|
|
64
|
+
});
|
|
@@ -19,6 +19,7 @@ import type {
|
|
|
19
19
|
} from "./types.js";
|
|
20
20
|
import { LspRequestTimeoutError } from "./errors.js";
|
|
21
21
|
import { WorkspaceDocumentState } from "./workspace-document-state.js";
|
|
22
|
+
import type { TimerProvider } from "./timer-provider.js";
|
|
22
23
|
import type { LspRenameResult, WorkspaceEditCommitIo } from "./workspace-edit-types.js";
|
|
23
24
|
import { WorkspaceMutationController } from "./workspace-mutation-controller.js";
|
|
24
25
|
|
|
@@ -36,6 +37,7 @@ export interface LspDiagnosticsResult {
|
|
|
36
37
|
export interface LspClientOptions extends LspClientTimeoutOptions {
|
|
37
38
|
readonly diagnosticsFreshnessTimeoutMs?: number;
|
|
38
39
|
readonly versionlessPublishQuiescenceMs?: number;
|
|
40
|
+
readonly timerProvider?: TimerProvider;
|
|
39
41
|
}
|
|
40
42
|
|
|
41
43
|
export class LspClient extends LspClientConnection {
|
|
@@ -52,6 +54,7 @@ export class LspClient extends LspClientConnection {
|
|
|
52
54
|
(method, params) => this.sendNotification(method, params),
|
|
53
55
|
(uri) => this.diagnosticsStore.delete(uri),
|
|
54
56
|
{
|
|
57
|
+
timerProvider: this.timerProvider,
|
|
55
58
|
versionlessPublishQuiescenceMs:
|
|
56
59
|
options.versionlessPublishQuiescenceMs ?? VERSIONLESS_PUBLISH_QUIESCENCE_MS,
|
|
57
60
|
},
|
|
@@ -207,7 +210,7 @@ export class LspClient extends LspClientConnection {
|
|
|
207
210
|
const absPath = this.resolveWorkspacePath(filePath);
|
|
208
211
|
const uri = pathToFileURL(absPath).href;
|
|
209
212
|
await this.openFile(absPath);
|
|
210
|
-
const deadlineAt =
|
|
213
|
+
const deadlineAt = this.timerProvider.now() + this.diagnosticsFreshnessTimeoutMs;
|
|
211
214
|
|
|
212
215
|
for (;;) {
|
|
213
216
|
signal?.throwIfAborted();
|
|
@@ -220,7 +223,7 @@ export class LspClient extends LspClientConnection {
|
|
|
220
223
|
if (!pushFallbackOnly) {
|
|
221
224
|
const cached = this.documents.getPullCache(snapshot);
|
|
222
225
|
try {
|
|
223
|
-
const remainingMs = deadlineAt -
|
|
226
|
+
const remainingMs = deadlineAt - this.timerProvider.now();
|
|
224
227
|
if (remainingMs <= 0) return this.freshnessTimeout(absPath);
|
|
225
228
|
const result = await this.sendRequest<{ items?: Diagnostic[]; kind?: string; resultId?: string }>(
|
|
226
229
|
"textDocument/diagnostic",
|
|
@@ -262,7 +265,7 @@ export class LspClient extends LspClientConnection {
|
|
|
262
265
|
|
|
263
266
|
if (!pushFallbackOnly) continue;
|
|
264
267
|
|
|
265
|
-
const remainingMs = deadlineAt -
|
|
268
|
+
const remainingMs = deadlineAt - this.timerProvider.now();
|
|
266
269
|
if (remainingMs <= 0) {
|
|
267
270
|
// A server that never advertised pull diagnostics (or rejected the pull
|
|
268
271
|
// method) and has never published for this document stays silent for a
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { TimerProvider } from "./timer-provider.js";
|
|
2
|
+
|
|
3
|
+
interface ControlledTimer {
|
|
4
|
+
readonly at: number;
|
|
5
|
+
readonly callback: () => void;
|
|
6
|
+
cancelled: boolean;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
interface TimerWaiter {
|
|
10
|
+
readonly delayMs: number | undefined;
|
|
11
|
+
readonly resolve: () => void;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Deterministic TimerProvider for tests: timers fire only through advanceBy(),
|
|
16
|
+
* and waitForTimer(delayMs) resolves once a timer with exactly that delay is
|
|
17
|
+
* scheduled, so tests order themselves behind the timer they intend to fire.
|
|
18
|
+
*/
|
|
19
|
+
export class ControlledClock implements TimerProvider {
|
|
20
|
+
private readonly timers: ControlledTimer[] = [];
|
|
21
|
+
private currentTime = 0;
|
|
22
|
+
private readonly timerWaiters = new Set<TimerWaiter>();
|
|
23
|
+
readonly scheduledDelays: number[] = [];
|
|
24
|
+
|
|
25
|
+
readonly now = (): number => this.currentTime;
|
|
26
|
+
readonly setTimeout = (callback: () => void, delayMs: number): ReturnType<typeof setTimeout> => {
|
|
27
|
+
const timer: ControlledTimer = { at: this.currentTime + delayMs, callback, cancelled: false };
|
|
28
|
+
this.scheduledDelays.push(delayMs);
|
|
29
|
+
this.timers.push(timer);
|
|
30
|
+
for (const waiter of this.timerWaiters) {
|
|
31
|
+
if (waiter.delayMs === undefined || waiter.delayMs === delayMs) {
|
|
32
|
+
this.timerWaiters.delete(waiter);
|
|
33
|
+
waiter.resolve();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return timer as unknown as ReturnType<typeof setTimeout>;
|
|
37
|
+
};
|
|
38
|
+
readonly clearTimeout = (handle: ReturnType<typeof setTimeout>): void => {
|
|
39
|
+
(handle as unknown as { cancelled: boolean }).cancelled = true;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
waitForTimer(delayMs?: number): Promise<void> {
|
|
43
|
+
const hasTimer = this.timers.some(
|
|
44
|
+
(timer) => !timer.cancelled && (delayMs === undefined || timer.at === this.currentTime + delayMs),
|
|
45
|
+
);
|
|
46
|
+
if (hasTimer) return Promise.resolve();
|
|
47
|
+
return new Promise((resolve) => {
|
|
48
|
+
this.timerWaiters.add({ delayMs, resolve });
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
advanceBy(delayMs: number): void {
|
|
53
|
+
this.currentTime += delayMs;
|
|
54
|
+
for (;;) {
|
|
55
|
+
const index = this.timers.findIndex((timer) => timer.at <= this.currentTime);
|
|
56
|
+
if (index < 0) return;
|
|
57
|
+
const timer = this.timers.splice(index, 1)[0];
|
|
58
|
+
if (timer && !timer.cancelled) timer.callback();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -8,6 +8,7 @@ let nextServerRequestId = 10_000;
|
|
|
8
8
|
let renameIndex = 0;
|
|
9
9
|
let diagnosticIndex = 0;
|
|
10
10
|
const pendingServerRequests = new Map();
|
|
11
|
+
const releasedDiagnosticResponses = [];
|
|
11
12
|
|
|
12
13
|
function record(event) {
|
|
13
14
|
appendFileSync(eventsPath, `${JSON.stringify(event)}\n`, "utf-8");
|
|
@@ -146,8 +147,7 @@ function handleRequest(message) {
|
|
|
146
147
|
setTimeout(() => process.exit(0), delay);
|
|
147
148
|
return;
|
|
148
149
|
}
|
|
149
|
-
const
|
|
150
|
-
setTimeout(() => {
|
|
150
|
+
const sendDiagnosticResponse = () => {
|
|
151
151
|
if (step?.error) {
|
|
152
152
|
record({ type: "serverError", method: "textDocument/diagnostic", error: step.error });
|
|
153
153
|
sendError(message.id, step.error.code, step.error.message);
|
|
@@ -156,7 +156,13 @@ function handleRequest(message) {
|
|
|
156
156
|
const result = step?.report ?? { items: scenario.diagnostics ?? [] };
|
|
157
157
|
record({ type: "serverResponse", method: "textDocument/diagnostic", result });
|
|
158
158
|
sendResponse(message.id, result);
|
|
159
|
-
}
|
|
159
|
+
};
|
|
160
|
+
if (step?.releaseOnDidChange === true) {
|
|
161
|
+
releasedDiagnosticResponses.push(sendDiagnosticResponse);
|
|
162
|
+
} else {
|
|
163
|
+
const delay = Number(step?.delayMs ?? 0);
|
|
164
|
+
setTimeout(sendDiagnosticResponse, delay);
|
|
165
|
+
}
|
|
160
166
|
return;
|
|
161
167
|
}
|
|
162
168
|
if (message.method === "shutdown") {
|
|
@@ -190,6 +196,7 @@ function handleNotification(message) {
|
|
|
190
196
|
}
|
|
191
197
|
if (message.method === "textDocument/didChange") {
|
|
192
198
|
applyPublishSteps("didChange", message.params.textDocument.uri, message.params.textDocument.version);
|
|
199
|
+
for (const release of releasedDiagnosticResponses.splice(0)) release();
|
|
193
200
|
}
|
|
194
201
|
if (message.method === "exit") process.exit(0);
|
|
195
202
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type TimerHandle = ReturnType<typeof setTimeout>;
|
|
2
|
+
|
|
3
|
+
export interface TimerProvider {
|
|
4
|
+
readonly now: () => number;
|
|
5
|
+
readonly setTimeout: (callback: () => void, delayMs: number) => TimerHandle;
|
|
6
|
+
readonly clearTimeout: (handle: TimerHandle) => void;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const realTimerProvider: TimerProvider = {
|
|
10
|
+
now: () => Date.now(),
|
|
11
|
+
setTimeout: (callback, delayMs) => setTimeout(callback, delayMs),
|
|
12
|
+
clearTimeout: (handle) => clearTimeout(handle),
|
|
13
|
+
};
|