@wolfx/opencode-magic-context 0.26.0 → 0.27.2
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/agents/dreamer.d.ts +19 -0
- package/dist/agents/dreamer.d.ts.map +1 -1
- package/dist/agents/hidden-agent-registrations.d.ts +67 -0
- package/dist/agents/hidden-agent-registrations.d.ts.map +1 -0
- package/dist/agents/historian.d.ts +1 -0
- package/dist/agents/historian.d.ts.map +1 -1
- package/dist/agents/permissions.d.ts +15 -44
- package/dist/agents/permissions.d.ts.map +1 -1
- package/dist/agents/smart-note-compiler.d.ts +2 -0
- package/dist/agents/smart-note-compiler.d.ts.map +1 -0
- package/dist/config/index.d.ts +1 -1
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/migrate-config-location.d.ts +97 -0
- package/dist/config/migrate-config-location.d.ts.map +1 -0
- package/dist/config/migrate-dreamer-v2.d.ts +37 -0
- package/dist/config/migrate-dreamer-v2.d.ts.map +1 -0
- package/dist/config/migrate-experimental.d.ts.map +1 -1
- package/dist/config/project-security.d.ts +3 -0
- package/dist/config/project-security.d.ts.map +1 -1
- package/dist/config/prune-config-leaf.d.ts.map +1 -1
- package/dist/config/schema/magic-context.d.ts +586 -77
- package/dist/config/schema/magic-context.d.ts.map +1 -1
- package/dist/features/magic-context/compaction-marker.d.ts +9 -3
- package/dist/features/magic-context/compaction-marker.d.ts.map +1 -1
- package/dist/features/magic-context/compartment-chunk-embedding.d.ts +1 -1
- package/dist/features/magic-context/compartment-chunk-embedding.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/classify-prompt.d.ts +50 -0
- package/dist/features/magic-context/dreamer/classify-prompt.d.ts.map +1 -0
- package/dist/features/magic-context/dreamer/classify.d.ts +22 -0
- package/dist/features/magic-context/dreamer/classify.d.ts.map +1 -0
- package/dist/features/magic-context/dreamer/cron.d.ts +72 -0
- package/dist/features/magic-context/dreamer/cron.d.ts.map +1 -0
- package/dist/features/magic-context/dreamer/evaluate-smart-notes.d.ts +30 -0
- package/dist/features/magic-context/dreamer/evaluate-smart-notes.d.ts.map +1 -0
- package/dist/features/magic-context/dreamer/index.d.ts +1 -3
- package/dist/features/magic-context/dreamer/index.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/lease.d.ts +44 -6
- package/dist/features/magic-context/dreamer/lease.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/maintain-docs-protected-enforcement.d.ts +13 -0
- package/dist/features/magic-context/dreamer/maintain-docs-protected-enforcement.d.ts.map +1 -0
- package/dist/features/magic-context/dreamer/map-memories-prompt.d.ts +36 -0
- package/dist/features/magic-context/dreamer/map-memories-prompt.d.ts.map +1 -0
- package/dist/features/magic-context/dreamer/map-memories.d.ts +22 -0
- package/dist/features/magic-context/dreamer/map-memories.d.ts.map +1 -0
- package/dist/features/magic-context/dreamer/open-opencode-db.d.ts +7 -0
- package/dist/features/magic-context/dreamer/open-opencode-db.d.ts.map +1 -0
- package/dist/features/magic-context/dreamer/primer-seed.d.ts +25 -0
- package/dist/features/magic-context/dreamer/primer-seed.d.ts.map +1 -0
- package/dist/features/magic-context/dreamer/promote-primers.d.ts +21 -0
- package/dist/features/magic-context/dreamer/promote-primers.d.ts.map +1 -0
- package/dist/features/magic-context/dreamer/protected-regions.d.ts +19 -0
- package/dist/features/magic-context/dreamer/protected-regions.d.ts.map +1 -0
- package/dist/features/magic-context/dreamer/refresh-primers.d.ts +30 -0
- package/dist/features/magic-context/dreamer/refresh-primers.d.ts.map +1 -0
- package/dist/features/magic-context/dreamer/retrospective-learnings.d.ts +47 -0
- package/dist/features/magic-context/dreamer/retrospective-learnings.d.ts.map +1 -0
- package/dist/features/magic-context/dreamer/retrospective-orphan-sweep.d.ts +48 -0
- package/dist/features/magic-context/dreamer/retrospective-orphan-sweep.d.ts.map +1 -0
- package/dist/features/magic-context/dreamer/retrospective-raw-provider.d.ts +81 -0
- package/dist/features/magic-context/dreamer/retrospective-raw-provider.d.ts.map +1 -0
- package/dist/features/magic-context/dreamer/storage-dream-runs.d.ts +8 -0
- package/dist/features/magic-context/dreamer/storage-dream-runs.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/storage-task-schedule.d.ts +82 -0
- package/dist/features/magic-context/dreamer/storage-task-schedule.d.ts.map +1 -0
- package/dist/features/magic-context/dreamer/task-config.d.ts +28 -0
- package/dist/features/magic-context/dreamer/task-config.d.ts.map +1 -0
- package/dist/features/magic-context/dreamer/task-executor.d.ts +49 -0
- package/dist/features/magic-context/dreamer/task-executor.d.ts.map +1 -0
- package/dist/features/magic-context/dreamer/task-gates.d.ts +29 -0
- package/dist/features/magic-context/dreamer/task-gates.d.ts.map +1 -0
- package/dist/features/magic-context/dreamer/task-prompts.d.ts +37 -6
- package/dist/features/magic-context/dreamer/task-prompts.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/task-registry.d.ts +48 -0
- package/dist/features/magic-context/dreamer/task-registry.d.ts.map +1 -0
- package/dist/features/magic-context/dreamer/task-scheduler.d.ts +88 -0
- package/dist/features/magic-context/dreamer/task-scheduler.d.ts.map +1 -0
- package/dist/features/magic-context/dreamer/verify-gate.d.ts +43 -0
- package/dist/features/magic-context/dreamer/verify-gate.d.ts.map +1 -0
- package/dist/features/magic-context/dreamer/verify-prompt.d.ts +41 -0
- package/dist/features/magic-context/dreamer/verify-prompt.d.ts.map +1 -0
- package/dist/features/magic-context/dreamer/verify.d.ts +43 -0
- package/dist/features/magic-context/dreamer/verify.d.ts.map +1 -0
- package/dist/features/magic-context/git-commits/search-git-commits.d.ts +2 -0
- package/dist/features/magic-context/git-commits/search-git-commits.d.ts.map +1 -1
- package/dist/features/magic-context/git-commits/storage-git-commit-embeddings.d.ts +4 -4
- package/dist/features/magic-context/git-commits/storage-git-commit-embeddings.d.ts.map +1 -1
- package/dist/features/magic-context/index.d.ts +1 -0
- package/dist/features/magic-context/index.d.ts.map +1 -1
- package/dist/features/magic-context/memory/embedding-cache.d.ts +2 -2
- package/dist/features/magic-context/memory/embedding-cache.d.ts.map +1 -1
- package/dist/features/magic-context/memory/embedding-identity.d.ts.map +1 -1
- package/dist/features/magic-context/memory/embedding-openai.d.ts +12 -5
- package/dist/features/magic-context/memory/embedding-openai.d.ts.map +1 -1
- package/dist/features/magic-context/memory/embedding.d.ts +2 -2
- package/dist/features/magic-context/memory/embedding.d.ts.map +1 -1
- package/dist/features/magic-context/memory/index.d.ts +4 -1
- package/dist/features/magic-context/memory/index.d.ts.map +1 -1
- package/dist/features/magic-context/memory/memory-migration.d.ts +1 -0
- package/dist/features/magic-context/memory/memory-migration.d.ts.map +1 -1
- package/dist/features/magic-context/memory/promotion.d.ts +16 -4
- package/dist/features/magic-context/memory/promotion.d.ts.map +1 -1
- package/dist/features/magic-context/memory/storage-memory-embeddings.d.ts +2 -2
- package/dist/features/magic-context/memory/storage-memory-embeddings.d.ts.map +1 -1
- package/dist/features/magic-context/memory/storage-memory-verifications.d.ts +31 -0
- package/dist/features/magic-context/memory/storage-memory-verifications.d.ts.map +1 -0
- package/dist/features/magic-context/memory/storage-memory.d.ts +12 -1
- package/dist/features/magic-context/memory/storage-memory.d.ts.map +1 -1
- package/dist/features/magic-context/memory/types.d.ts +4 -0
- package/dist/features/magic-context/memory/types.d.ts.map +1 -1
- package/dist/features/magic-context/memory/verification-paths.d.ts +32 -0
- package/dist/features/magic-context/memory/verification-paths.d.ts.map +1 -0
- package/dist/features/magic-context/message-index.d.ts.map +1 -1
- package/dist/features/magic-context/migrations.d.ts.map +1 -1
- package/dist/features/magic-context/overflow-detection.d.ts.map +1 -1
- package/dist/features/magic-context/primer-clustering.d.ts +29 -0
- package/dist/features/magic-context/primer-clustering.d.ts.map +1 -0
- package/dist/features/magic-context/project-embedding-registry.d.ts +25 -1
- package/dist/features/magic-context/project-embedding-registry.d.ts.map +1 -1
- package/dist/features/magic-context/search.d.ts +12 -2
- package/dist/features/magic-context/search.d.ts.map +1 -1
- package/dist/features/magic-context/sidekick/agent.d.ts.map +1 -1
- package/dist/features/magic-context/smart-notes/capabilities.d.ts +31 -0
- package/dist/features/magic-context/smart-notes/capabilities.d.ts.map +1 -0
- package/dist/features/magic-context/smart-notes/compiler-prompt.d.ts +2 -0
- package/dist/features/magic-context/smart-notes/compiler-prompt.d.ts.map +1 -0
- package/dist/features/magic-context/smart-notes/compiler.d.ts +52 -0
- package/dist/features/magic-context/smart-notes/compiler.d.ts.map +1 -0
- package/dist/features/magic-context/smart-notes/index.d.ts +10 -0
- package/dist/features/magic-context/smart-notes/index.d.ts.map +1 -0
- package/dist/features/magic-context/smart-notes/runner.d.ts +18 -0
- package/dist/features/magic-context/smart-notes/runner.d.ts.map +1 -0
- package/dist/features/magic-context/smart-notes/sandbox-runner.d.ts +22 -0
- package/dist/features/magic-context/smart-notes/sandbox-runner.d.ts.map +1 -0
- package/dist/features/magic-context/smart-notes/schedule.d.ts +9 -0
- package/dist/features/magic-context/smart-notes/schedule.d.ts.map +1 -0
- package/dist/features/magic-context/smart-notes/ssrf-guard.d.ts +49 -0
- package/dist/features/magic-context/smart-notes/ssrf-guard.d.ts.map +1 -0
- package/dist/features/magic-context/smart-notes/storage.d.ts +27 -0
- package/dist/features/magic-context/smart-notes/storage.d.ts.map +1 -0
- package/dist/features/magic-context/smart-notes/types.d.ts +63 -0
- package/dist/features/magic-context/smart-notes/types.d.ts.map +1 -0
- package/dist/features/magic-context/storage-db.d.ts +5 -1
- package/dist/features/magic-context/storage-db.d.ts.map +1 -1
- package/dist/features/magic-context/storage-meta-persisted.d.ts +8 -4
- package/dist/features/magic-context/storage-meta-persisted.d.ts.map +1 -1
- package/dist/features/magic-context/storage-meta-session.d.ts.map +1 -1
- package/dist/features/magic-context/storage-meta-shared.d.ts +3 -1
- package/dist/features/magic-context/storage-meta-shared.d.ts.map +1 -1
- package/dist/features/magic-context/storage-notes.d.ts +15 -0
- package/dist/features/magic-context/storage-notes.d.ts.map +1 -1
- package/dist/features/magic-context/storage-primers.d.ts +85 -0
- package/dist/features/magic-context/storage-primers.d.ts.map +1 -0
- package/dist/features/magic-context/storage-tags.d.ts +20 -0
- package/dist/features/magic-context/storage-tags.d.ts.map +1 -1
- package/dist/features/magic-context/storage.d.ts +2 -1
- package/dist/features/magic-context/storage.d.ts.map +1 -1
- package/dist/features/magic-context/tagger.d.ts +6 -0
- package/dist/features/magic-context/tagger.d.ts.map +1 -1
- package/dist/features/magic-context/tool-owner-backfill.d.ts.map +1 -1
- package/dist/features/magic-context/transform-decision-log.d.ts +10 -0
- package/dist/features/magic-context/transform-decision-log.d.ts.map +1 -1
- package/dist/features/magic-context/types.d.ts +2 -0
- package/dist/features/magic-context/types.d.ts.map +1 -1
- package/dist/features/magic-context/user-memory/review-user-memories.d.ts +5 -0
- package/dist/features/magic-context/user-memory/review-user-memories.d.ts.map +1 -1
- package/dist/features/magic-context/user-memory/storage-user-memory.d.ts +18 -0
- package/dist/features/magic-context/user-memory/storage-user-memory.d.ts.map +1 -1
- package/dist/features/magic-context/v22-deferred-backfill.d.ts.map +1 -1
- package/dist/hooks/magic-context/auto-search-hint.d.ts.map +1 -1
- package/dist/hooks/magic-context/command-handler.d.ts +8 -15
- package/dist/hooks/magic-context/command-handler.d.ts.map +1 -1
- package/dist/hooks/magic-context/compaction-marker-manager.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-parser.d.ts +9 -0
- package/dist/hooks/magic-context/compartment-parser.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-prompt.d.ts +4 -1
- package/dist/hooks/magic-context/compartment-prompt.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-historian.d.ts +1 -0
- package/dist/hooks/magic-context/compartment-runner-historian.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-incremental.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-partial-recomp.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-recomp.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-types.d.ts +8 -0
- package/dist/hooks/magic-context/compartment-runner-types.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-validation.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-trigger.d.ts.map +1 -1
- package/dist/hooks/magic-context/ctx-reduce-nudge.d.ts.map +1 -1
- package/dist/hooks/magic-context/event-handler.d.ts.map +1 -1
- package/dist/hooks/magic-context/event-resolvers.d.ts.map +1 -1
- package/dist/hooks/magic-context/historian-prompt.generated.d.ts +1 -1
- package/dist/hooks/magic-context/historian-prompt.generated.d.ts.map +1 -1
- package/dist/hooks/magic-context/historian-state-file.d.ts.map +1 -1
- package/dist/hooks/magic-context/hook-handlers.d.ts +2 -1
- package/dist/hooks/magic-context/hook-handlers.d.ts.map +1 -1
- package/dist/hooks/magic-context/hook.d.ts +1 -0
- package/dist/hooks/magic-context/hook.d.ts.map +1 -1
- package/dist/hooks/magic-context/inject-compartments.d.ts +0 -3
- package/dist/hooks/magic-context/inject-compartments.d.ts.map +1 -1
- package/dist/hooks/magic-context/send-session-notification.d.ts +2 -0
- package/dist/hooks/magic-context/send-session-notification.d.ts.map +1 -1
- package/dist/hooks/magic-context/system-prompt-hash.d.ts +17 -0
- package/dist/hooks/magic-context/system-prompt-hash.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform-postprocess-phase.d.ts +8 -5
- package/dist/hooks/magic-context/transform-postprocess-phase.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform.d.ts +0 -2
- package/dist/hooks/magic-context/transform.d.ts.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18059 -13232
- package/dist/plugin/dream-timer.d.ts +17 -9
- package/dist/plugin/dream-timer.d.ts.map +1 -1
- package/dist/plugin/embedding-bootstrap-helpers.d.ts +1 -1
- package/dist/plugin/embedding-bootstrap-helpers.d.ts.map +1 -1
- package/dist/plugin/embedding-bootstrap.d.ts.map +1 -1
- package/dist/plugin/hooks/create-session-hooks.d.ts +211 -0
- package/dist/plugin/hooks/create-session-hooks.d.ts.map +1 -1
- package/dist/plugin/instance-disposal.d.ts +2 -0
- package/dist/plugin/instance-disposal.d.ts.map +1 -0
- package/dist/plugin/rpc-handlers.d.ts.map +1 -1
- package/dist/shared/announcement.d.ts +1 -1
- package/dist/shared/announcement.d.ts.map +1 -1
- package/dist/shared/data-path.d.ts +30 -9
- package/dist/shared/data-path.d.ts.map +1 -1
- package/dist/shared/model-suggestion-retry.d.ts +48 -2
- package/dist/shared/model-suggestion-retry.d.ts.map +1 -1
- package/dist/shared/models-dev-cache.d.ts +23 -0
- package/dist/shared/models-dev-cache.d.ts.map +1 -1
- package/dist/shared/redaction.d.ts +7 -0
- package/dist/shared/redaction.d.ts.map +1 -0
- package/dist/shared/resolve-fallbacks.d.ts +12 -0
- package/dist/shared/resolve-fallbacks.d.ts.map +1 -1
- package/dist/shared/rpc-server.d.ts.map +1 -1
- package/dist/shared/rpc-types.d.ts +2 -0
- package/dist/shared/rpc-types.d.ts.map +1 -1
- package/dist/shared/subagent-runner.d.ts +12 -3
- package/dist/shared/subagent-runner.d.ts.map +1 -1
- package/dist/shared/tui-config.d.ts.map +1 -1
- package/dist/tools/ctx-memory/tools.d.ts.map +1 -1
- package/dist/tools/ctx-memory/types.d.ts.map +1 -1
- package/dist/tools/ctx-memory/verification-recording.d.ts +8 -0
- package/dist/tools/ctx-memory/verification-recording.d.ts.map +1 -0
- package/dist/tools/ctx-search/tools.d.ts.map +1 -1
- package/dist/tools/ctx-search/types.d.ts +1 -1
- package/dist/tools/ctx-search/types.d.ts.map +1 -1
- package/dist/tui/data/context-db.d.ts +2 -0
- package/dist/tui/data/context-db.d.ts.map +1 -1
- package/package.json +7 -3
- package/src/shared/announcement.test.ts +20 -0
- package/src/shared/announcement.ts +32 -7
- package/src/shared/data-path.test.ts +74 -9
- package/src/shared/data-path.ts +54 -10
- package/src/shared/model-suggestion-retry.test.ts +79 -2
- package/src/shared/model-suggestion-retry.ts +181 -3
- package/src/shared/models-dev-cache.test.ts +82 -0
- package/src/shared/models-dev-cache.ts +35 -0
- package/src/shared/redaction.test.ts +84 -0
- package/src/shared/redaction.ts +264 -0
- package/src/shared/resolve-fallbacks.ts +14 -0
- package/src/shared/rpc-server.ts +24 -0
- package/src/shared/rpc-types.ts +2 -0
- package/src/shared/subagent-runner.ts +12 -3
- package/src/shared/tui-config.test.ts +106 -0
- package/src/shared/tui-config.ts +75 -40
- package/src/tui/data/context-db.ts +12 -0
- package/src/tui/index.tsx +87 -17
- package/src/tui/slots/sidebar-content.tsx +4 -0
- package/dist/features/magic-context/dreamer/queue.d.ts +0 -55
- package/dist/features/magic-context/dreamer/queue.d.ts.map +0 -1
- package/dist/features/magic-context/dreamer/runner.d.ts +0 -92
- package/dist/features/magic-context/dreamer/runner.d.ts.map +0 -1
- package/dist/features/magic-context/dreamer/scheduler.d.ts +0 -29
- package/dist/features/magic-context/dreamer/scheduler.d.ts.map +0 -1
- package/dist/features/magic-context/key-files/aft-availability.d.ts +0 -11
- package/dist/features/magic-context/key-files/aft-availability.d.ts.map +0 -1
- package/dist/features/magic-context/key-files/identify-key-files.d.ts +0 -84
- package/dist/features/magic-context/key-files/identify-key-files.d.ts.map +0 -1
- package/dist/features/magic-context/key-files/project-key-files.d.ts +0 -42
- package/dist/features/magic-context/key-files/project-key-files.d.ts.map +0 -1
- package/dist/features/magic-context/key-files/read-history.d.ts +0 -26
- package/dist/features/magic-context/key-files/read-history.d.ts.map +0 -1
- package/dist/features/magic-context/key-files/read-stats.d.ts +0 -18
- package/dist/features/magic-context/key-files/read-stats.d.ts.map +0 -1
- package/dist/features/magic-context/key-files/storage-key-files.d.ts +0 -20
- package/dist/features/magic-context/key-files/storage-key-files.d.ts.map +0 -1
- package/dist/features/magic-context/memory/embedding-local.d.ts +0 -25
- package/dist/features/magic-context/memory/embedding-local.d.ts.map +0 -1
- package/dist/hooks/magic-context/key-files-block.d.ts +0 -27
- package/dist/hooks/magic-context/key-files-block.d.ts.map +0 -1
|
@@ -6,18 +6,36 @@ import { parseProviderModel } from "./resolve-fallbacks";
|
|
|
6
6
|
|
|
7
7
|
type Client = ReturnType<typeof createOpencodeClient>;
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
/** Max time to wait for the best-effort child-session abort HTTP call before
|
|
10
|
+
* giving up on its response (the abort still proceeds server-side). Keeps a
|
|
11
|
+
* wedged abort endpoint from masking the original timeout/abort error. */
|
|
12
|
+
const ABORT_CALL_TIMEOUT_MS = 3000;
|
|
13
|
+
|
|
14
|
+
export type PromptBody = {
|
|
10
15
|
model?: { providerID: string; modelID: string };
|
|
11
16
|
[key: string]: unknown;
|
|
12
17
|
};
|
|
13
18
|
|
|
14
|
-
type PromptArgs = {
|
|
19
|
+
export type PromptArgs = {
|
|
15
20
|
path: { id: string };
|
|
16
21
|
body: PromptBody;
|
|
17
22
|
signal?: AbortSignal;
|
|
18
23
|
[key: string]: unknown;
|
|
19
24
|
};
|
|
20
25
|
|
|
26
|
+
export interface PromptAttemptInfo {
|
|
27
|
+
/** Human-readable model label used in logs ("primary" or "provider/model"). */
|
|
28
|
+
label: string;
|
|
29
|
+
/** Zero-based attempt index: 0 is primary, 1+ are fallback models. */
|
|
30
|
+
attemptIndex: number;
|
|
31
|
+
/** True for configured fallback models, false for the primary attempt. */
|
|
32
|
+
isFallback: boolean;
|
|
33
|
+
/** Total attempted models including the primary and all configured fallbacks. */
|
|
34
|
+
totalAttempts: number;
|
|
35
|
+
/** Explicit model override for this attempt, when one was supplied. */
|
|
36
|
+
model?: { providerID: string; modelID: string };
|
|
37
|
+
}
|
|
38
|
+
|
|
21
39
|
export interface PromptRetryOptions {
|
|
22
40
|
timeoutMs?: number;
|
|
23
41
|
/** External abort signal — cancels the in-flight LLM prompt immediately when aborted */
|
|
@@ -47,6 +65,29 @@ export interface PromptRetryOptions {
|
|
|
47
65
|
callContext?: string;
|
|
48
66
|
}
|
|
49
67
|
|
|
68
|
+
export interface ValidatedPromptRetryOptions<TOutput, TValidated> extends PromptRetryOptions {
|
|
69
|
+
/**
|
|
70
|
+
* Fetch the output produced by the just-completed prompt attempt. This is
|
|
71
|
+
* intentionally caller-owned because OpenCode exposes results via session
|
|
72
|
+
* messages and each caller validates a different shape.
|
|
73
|
+
*/
|
|
74
|
+
fetchOutput: (args: PromptArgs, attempt: PromptAttemptInfo) => Promise<TOutput>;
|
|
75
|
+
/**
|
|
76
|
+
* Validate and optionally transform the fetched output. Throw to reject this
|
|
77
|
+
* model's output and advance to the next configured fallback model.
|
|
78
|
+
*/
|
|
79
|
+
validateOutput: (
|
|
80
|
+
output: TOutput,
|
|
81
|
+
attempt: PromptAttemptInfo,
|
|
82
|
+
) => TValidated | Promise<TValidated>;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export interface ValidatedPromptRetryResult<TOutput, TValidated> {
|
|
86
|
+
output: TOutput;
|
|
87
|
+
validated: TValidated;
|
|
88
|
+
attempt: PromptAttemptInfo;
|
|
89
|
+
}
|
|
90
|
+
|
|
50
91
|
export interface ModelSuggestionInfo {
|
|
51
92
|
providerID: string;
|
|
52
93
|
modelID: string;
|
|
@@ -171,7 +212,15 @@ async function promptWithTimeout(
|
|
|
171
212
|
*/
|
|
172
213
|
async function abortChildRun(client: Client, sessionId: string): Promise<void> {
|
|
173
214
|
try {
|
|
174
|
-
|
|
215
|
+
// Bound the abort call: it's best-effort cleanup, and if the abort
|
|
216
|
+
// endpoint itself stalls (the runner is wedged) an unbounded await here
|
|
217
|
+
// would hang the caller and MASK the original timeout/abort error that we
|
|
218
|
+
// still need to surface. Race against a short timer; the abort keeps
|
|
219
|
+
// running server-side regardless of whether we wait for its response.
|
|
220
|
+
await Promise.race([
|
|
221
|
+
client.session.abort({ path: { id: sessionId } }),
|
|
222
|
+
new Promise<void>((resolve) => setTimeout(resolve, ABORT_CALL_TIMEOUT_MS)),
|
|
223
|
+
]);
|
|
175
224
|
} catch (error) {
|
|
176
225
|
log(`[model-retry] child session abort failed for ${sessionId}: ${String(error)}`);
|
|
177
226
|
}
|
|
@@ -368,3 +417,132 @@ export async function promptSyncWithModelSuggestionRetry(
|
|
|
368
417
|
);
|
|
369
418
|
throw lastError ?? new Error("All fallback models failed");
|
|
370
419
|
}
|
|
420
|
+
|
|
421
|
+
async function attemptAndValidate<TOutput, TValidated>(
|
|
422
|
+
client: Client,
|
|
423
|
+
args: PromptArgs,
|
|
424
|
+
timeoutMs: number,
|
|
425
|
+
signal: AbortSignal | undefined,
|
|
426
|
+
callContext: string,
|
|
427
|
+
attempt: PromptAttemptInfo,
|
|
428
|
+
options: ValidatedPromptRetryOptions<TOutput, TValidated>,
|
|
429
|
+
): Promise<ValidatedPromptRetryResult<TOutput, TValidated>> {
|
|
430
|
+
await attemptOnce(client, args, timeoutMs, signal, callContext, attempt.label);
|
|
431
|
+
const output = await options.fetchOutput(args, attempt);
|
|
432
|
+
const validated = await options.validateOutput(output, attempt);
|
|
433
|
+
return { output, validated, attempt };
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Run a prompt with model fallback support, but accept an attempt only after the
|
|
438
|
+
* caller validates the model's actual output. This covers "empty success" cases
|
|
439
|
+
* where the provider/OpenCode prompt call completes successfully but the subagent
|
|
440
|
+
* produced no usable assistant text / JSON.
|
|
441
|
+
*
|
|
442
|
+
* The happy path is still one prompt + one caller-owned output fetch: callers
|
|
443
|
+
* should use the returned output instead of fetching messages a second time.
|
|
444
|
+
* Validation failures are retryable across configured fallback models. If every
|
|
445
|
+
* attempt produces invalid output (or otherwise fails retryably), the first
|
|
446
|
+
* failure is re-thrown so callers surface the original failure semantics.
|
|
447
|
+
*/
|
|
448
|
+
export async function promptSyncWithValidatedOutputRetry<TOutput, TValidated = TOutput>(
|
|
449
|
+
client: Client,
|
|
450
|
+
args: PromptArgs,
|
|
451
|
+
options: ValidatedPromptRetryOptions<TOutput, TValidated>,
|
|
452
|
+
): Promise<ValidatedPromptRetryResult<TOutput, TValidated>> {
|
|
453
|
+
const timeoutMs = options.timeoutMs ?? 300_000;
|
|
454
|
+
const callContext = options.callContext ?? "subagent";
|
|
455
|
+
const fallbacks = options.fallbackModels ?? [];
|
|
456
|
+
|
|
457
|
+
const explicitPrimaryLabel =
|
|
458
|
+
args.body.model?.providerID && args.body.model.modelID
|
|
459
|
+
? `${args.body.model.providerID}/${args.body.model.modelID}`
|
|
460
|
+
: "primary";
|
|
461
|
+
const totalAttempts = fallbacks.length + 1;
|
|
462
|
+
|
|
463
|
+
let firstError: unknown = null;
|
|
464
|
+
let lastError: unknown = null;
|
|
465
|
+
|
|
466
|
+
try {
|
|
467
|
+
return await attemptAndValidate(
|
|
468
|
+
client,
|
|
469
|
+
args,
|
|
470
|
+
timeoutMs,
|
|
471
|
+
options.signal,
|
|
472
|
+
callContext,
|
|
473
|
+
{
|
|
474
|
+
label: explicitPrimaryLabel,
|
|
475
|
+
attemptIndex: 0,
|
|
476
|
+
isFallback: false,
|
|
477
|
+
totalAttempts,
|
|
478
|
+
model: args.body.model,
|
|
479
|
+
},
|
|
480
|
+
options,
|
|
481
|
+
);
|
|
482
|
+
} catch (error) {
|
|
483
|
+
firstError = error;
|
|
484
|
+
lastError = error;
|
|
485
|
+
if (isNonRetryable(error, options.signal)) throw error;
|
|
486
|
+
|
|
487
|
+
if (fallbacks.length === 0) {
|
|
488
|
+
throw error;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
log(
|
|
492
|
+
`[${callContext}] primary (${explicitPrimaryLabel}) failed validation/prompt: ${shortErr(error)}; trying ${fallbacks.length} fallback(s)`,
|
|
493
|
+
);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
for (let i = 0; i < fallbacks.length; i += 1) {
|
|
497
|
+
const parsed = parseProviderModel(fallbacks[i]);
|
|
498
|
+
if (!parsed) {
|
|
499
|
+
log(`[${callContext}] skipping invalid fallback spec: ${fallbacks[i]}`);
|
|
500
|
+
continue;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
const label = `${parsed.providerID}/${parsed.modelID}`;
|
|
504
|
+
const attemptArgs: PromptArgs = {
|
|
505
|
+
...args,
|
|
506
|
+
body: { ...args.body, model: parsed },
|
|
507
|
+
};
|
|
508
|
+
const attempt: PromptAttemptInfo = {
|
|
509
|
+
label,
|
|
510
|
+
attemptIndex: i + 1,
|
|
511
|
+
isFallback: true,
|
|
512
|
+
totalAttempts,
|
|
513
|
+
model: parsed,
|
|
514
|
+
};
|
|
515
|
+
|
|
516
|
+
try {
|
|
517
|
+
const result = await attemptAndValidate(
|
|
518
|
+
client,
|
|
519
|
+
attemptArgs,
|
|
520
|
+
timeoutMs,
|
|
521
|
+
options.signal,
|
|
522
|
+
callContext,
|
|
523
|
+
attempt,
|
|
524
|
+
options,
|
|
525
|
+
);
|
|
526
|
+
log(
|
|
527
|
+
`[${callContext}] fallback succeeded with ${label} (attempt ${i + 2}/${fallbacks.length + 1})`,
|
|
528
|
+
);
|
|
529
|
+
return result;
|
|
530
|
+
} catch (error) {
|
|
531
|
+
if (firstError === null) firstError = error;
|
|
532
|
+
lastError = error;
|
|
533
|
+
if (isNonRetryable(error, options.signal)) throw error;
|
|
534
|
+
|
|
535
|
+
const remaining = fallbacks.length - i - 1;
|
|
536
|
+
if (remaining > 0) {
|
|
537
|
+
log(
|
|
538
|
+
`[${callContext}] ${label} failed validation/prompt: ${shortErr(error)}; ${remaining} fallback(s) left`,
|
|
539
|
+
);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
log(
|
|
545
|
+
`[${callContext}] all models exhausted; tried: ${[explicitPrimaryLabel, ...fallbacks].join(", ")}; original error: ${shortErr(firstError)}; last error: ${shortErr(lastError)}`,
|
|
546
|
+
);
|
|
547
|
+
throw firstError ?? lastError ?? new Error("All fallback models failed validation");
|
|
548
|
+
}
|
|
@@ -6,7 +6,9 @@ import {
|
|
|
6
6
|
clearModelsDevCache,
|
|
7
7
|
getModelsDevCacheState,
|
|
8
8
|
getSdkContextLimit,
|
|
9
|
+
refreshModelLimitsAfterAuthOnce,
|
|
9
10
|
refreshModelLimitsFromApi,
|
|
11
|
+
resetAuthRewarmLatchForTest,
|
|
10
12
|
} from "./models-dev-cache";
|
|
11
13
|
|
|
12
14
|
/**
|
|
@@ -198,6 +200,86 @@ describe("models-dev-cache (SDK-only)", () => {
|
|
|
198
200
|
});
|
|
199
201
|
});
|
|
200
202
|
|
|
203
|
+
describe("after-auth re-warm (once per process)", () => {
|
|
204
|
+
// The startup warm can run before provider auth is loaded, caching a raw
|
|
205
|
+
// pre-downshift limit (gpt-5.5 922k) that then survives restarts and is
|
|
206
|
+
// never corrected by the too-low-only recovery. The first usage event
|
|
207
|
+
// proves auth is live; one re-warm there overwrites the stale value.
|
|
208
|
+
function makeCountingClient(input: number) {
|
|
209
|
+
let calls = 0;
|
|
210
|
+
return {
|
|
211
|
+
client: {
|
|
212
|
+
config: {
|
|
213
|
+
providers: async () => {
|
|
214
|
+
calls++;
|
|
215
|
+
return {
|
|
216
|
+
data: {
|
|
217
|
+
providers: [
|
|
218
|
+
{
|
|
219
|
+
id: "openai",
|
|
220
|
+
models: { "gpt-5.5": { limit: { input } } },
|
|
221
|
+
},
|
|
222
|
+
],
|
|
223
|
+
},
|
|
224
|
+
};
|
|
225
|
+
},
|
|
226
|
+
},
|
|
227
|
+
},
|
|
228
|
+
calls: () => calls,
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
test("re-warm overwrites a stale pre-auth limit and runs only once per process", async () => {
|
|
233
|
+
resetAuthRewarmLatchForTest();
|
|
234
|
+
// Pre-auth startup warm cached the raw 922k.
|
|
235
|
+
await refreshModelLimitsFromApi(
|
|
236
|
+
makeClient([{ id: "openai", models: { "gpt-5.5": { limit: { input: 922000 } } } }]),
|
|
237
|
+
);
|
|
238
|
+
expect(getSdkContextLimit("openai", "gpt-5.5")).toBe(922000);
|
|
239
|
+
|
|
240
|
+
// First usage: auth is live, providers() now reports the 272k cap.
|
|
241
|
+
const { client, calls } = makeCountingClient(272000);
|
|
242
|
+
await refreshModelLimitsAfterAuthOnce(client);
|
|
243
|
+
expect(getSdkContextLimit("openai", "gpt-5.5")).toBe(272000);
|
|
244
|
+
expect(calls()).toBe(1);
|
|
245
|
+
|
|
246
|
+
// Subsequent usage events are a no-op (latch held).
|
|
247
|
+
await refreshModelLimitsAfterAuthOnce(client);
|
|
248
|
+
await refreshModelLimitsAfterAuthOnce(client);
|
|
249
|
+
expect(calls()).toBe(1);
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
test("a failed re-warm resets the latch so a later usage event retries", async () => {
|
|
253
|
+
resetAuthRewarmLatchForTest();
|
|
254
|
+
let calls = 0;
|
|
255
|
+
const flaky = {
|
|
256
|
+
config: {
|
|
257
|
+
providers: async () => {
|
|
258
|
+
calls++;
|
|
259
|
+
// First attempt: empty payload (auth still settling) → no warm.
|
|
260
|
+
if (calls === 1) return { data: { providers: [] } };
|
|
261
|
+
return {
|
|
262
|
+
data: {
|
|
263
|
+
providers: [
|
|
264
|
+
{
|
|
265
|
+
id: "openai",
|
|
266
|
+
models: { "gpt-5.5": { limit: { input: 272000 } } },
|
|
267
|
+
},
|
|
268
|
+
],
|
|
269
|
+
},
|
|
270
|
+
};
|
|
271
|
+
},
|
|
272
|
+
},
|
|
273
|
+
};
|
|
274
|
+
await refreshModelLimitsAfterAuthOnce(flaky);
|
|
275
|
+
expect(getSdkContextLimit("openai", "gpt-5.5")).toBeUndefined();
|
|
276
|
+
// Latch was reset on failure, so the next event retries and succeeds.
|
|
277
|
+
await refreshModelLimitsAfterAuthOnce(flaky);
|
|
278
|
+
expect(getSdkContextLimit("openai", "gpt-5.5")).toBe(272000);
|
|
279
|
+
expect(calls).toBe(2);
|
|
280
|
+
});
|
|
281
|
+
});
|
|
282
|
+
|
|
201
283
|
describe("startup retry", () => {
|
|
202
284
|
test("retries when the provider payload is empty, then succeeds", async () => {
|
|
203
285
|
let calls = 0;
|
|
@@ -210,6 +210,41 @@ export async function refreshModelLimitsFromApi(
|
|
|
210
210
|
}
|
|
211
211
|
}
|
|
212
212
|
|
|
213
|
+
// Once-per-process latch for the after-auth re-warm below.
|
|
214
|
+
let authRewarmDone = false;
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Re-warm the limit cache ONCE per process, after auth is provably live.
|
|
218
|
+
*
|
|
219
|
+
* The startup warm (index.ts) can run before the user's provider auth is
|
|
220
|
+
* loaded. When it does, an auth-conditional limit patch hasn't applied yet, so
|
|
221
|
+
* `config.providers()` returns the RAW catalog limit (e.g. OpenAI gpt-5.5 OAuth
|
|
222
|
+
* is downshifted to a 272k input cap by OpenCode's Codex auth plugin only when
|
|
223
|
+
* `ctx.auth.type === "oauth"`; before auth loads it reports the raw 922k). That
|
|
224
|
+
* too-high value gets cached AND persisted as last-known-good, survives
|
|
225
|
+
* restarts, and the existing recovery only re-resolves a too-LOW limit
|
|
226
|
+
* (overflow / `percentage > 100`), so a too-HIGH one never self-corrects: the
|
|
227
|
+
* sidebar shows huge headroom while the backend rejects at the real cap (#179).
|
|
228
|
+
*
|
|
229
|
+
* The first `message.updated` carrying usage tokens proves a request succeeded,
|
|
230
|
+
* so auth + providers are fully resolved. Re-warming there overwrites any stale
|
|
231
|
+
* pre-auth limit with the live auth-adjusted one. Idempotent and cheap: a single
|
|
232
|
+
* `config.providers()` round-trip, then a no-op for the rest of the process. The
|
|
233
|
+
* latch is set before the await so concurrent `message.updated` events don't
|
|
234
|
+
* stack duplicate warms; a failed warm resets it so a later message retries.
|
|
235
|
+
*/
|
|
236
|
+
export async function refreshModelLimitsAfterAuthOnce(client: OpencodeClientLike): Promise<void> {
|
|
237
|
+
if (authRewarmDone) return;
|
|
238
|
+
authRewarmDone = true;
|
|
239
|
+
const ok = await refreshModelLimitsOnce(client);
|
|
240
|
+
if (!ok) authRewarmDone = false;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/** Test-only: reset the after-auth re-warm latch between cases. */
|
|
244
|
+
export function resetAuthRewarmLatchForTest(): void {
|
|
245
|
+
authRewarmDone = false;
|
|
246
|
+
}
|
|
247
|
+
|
|
213
248
|
/** Single SDK fetch + cache rebuild. Returns true when providers were loaded. */
|
|
214
249
|
async function refreshModelLimitsOnce(client: OpencodeClientLike): Promise<boolean> {
|
|
215
250
|
try {
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/// <reference types="bun-types" />
|
|
2
|
+
|
|
3
|
+
import { describe, expect, test } from "bun:test";
|
|
4
|
+
|
|
5
|
+
import { hasShareabilitySensitiveText, redactSecretText } from "./redaction";
|
|
6
|
+
|
|
7
|
+
describe("redactSecretText — token counts and scalar diagnostics stay visible", () => {
|
|
8
|
+
test("keeps numeric/boolean values whose key merely contains a secret word", () => {
|
|
9
|
+
// These log shapes are counts/flags, not secrets, so they must stay readable.
|
|
10
|
+
expect(redactSecretText("tokens.input=45000 cache.read=0 cache.write=0")).toBe(
|
|
11
|
+
"tokens.input=45000 cache.read=0 cache.write=0",
|
|
12
|
+
);
|
|
13
|
+
expect(redactSecretText("hasUsageTokens=true")).toBe("hasUsageTokens=true");
|
|
14
|
+
expect(redactSecretText("totalInputTokens=132000")).toBe("totalInputTokens=132000");
|
|
15
|
+
expect(redactSecretText("max_tokens=4096")).toBe("max_tokens=4096");
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test("keeps quoted numeric values matched only on the key word", () => {
|
|
19
|
+
expect(redactSecretText('"max_tokens": "4096"')).toBe('"max_tokens": "4096"');
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test("still redacts real secret string values", () => {
|
|
23
|
+
// High-entropy / non-scalar values must always be redacted; only bare
|
|
24
|
+
// numeric/boolean scalars are exempt from the key-based match.
|
|
25
|
+
expect(redactSecretText("api_key=sk-abc123XYZsecretvalue")).toContain("<REDACTED:");
|
|
26
|
+
expect(redactSecretText("api_key=sk-abc123XYZsecretvalue")).not.toContain(
|
|
27
|
+
"sk-abc123XYZsecretvalue",
|
|
28
|
+
);
|
|
29
|
+
expect(redactSecretText('"auth_token": "tok_live_9f8e7d6c5b"')).toContain("<REDACTED:");
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test("value-shaped secret patterns still fire independent of key name", () => {
|
|
33
|
+
// A bearer/JWT value is caught by its own pattern even if its key is bland.
|
|
34
|
+
expect(redactSecretText("Authorization: Bearer abc123def456ghi789")).toContain(
|
|
35
|
+
"<REDACTED:bearer>",
|
|
36
|
+
);
|
|
37
|
+
expect(redactSecretText("blob=eyJhbGciOi.eyJzdWIiOiIx.SflKxwRJSMeKKF2QT4")).toContain(
|
|
38
|
+
"<JWT_REDACTED>",
|
|
39
|
+
);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
describe("hasShareabilitySensitiveText", () => {
|
|
44
|
+
test("safe project facts are shareable", () => {
|
|
45
|
+
expect(
|
|
46
|
+
hasShareabilitySensitiveText(
|
|
47
|
+
"The historian runs as a hidden subagent and never busts the prompt cache.",
|
|
48
|
+
),
|
|
49
|
+
).toBe(false);
|
|
50
|
+
expect(
|
|
51
|
+
hasShareabilitySensitiveText("Migration v45 adds the retrospective watermark column."),
|
|
52
|
+
).toBe(false);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test("flags inline key:value / key=value secrets the keyed redactor misses in prose", () => {
|
|
56
|
+
expect(hasShareabilitySensitiveText("Set api_key: sk-live-abc123 in the env.")).toBe(true);
|
|
57
|
+
expect(hasShareabilitySensitiveText("password=hunter2 for the staging box")).toBe(true);
|
|
58
|
+
expect(hasShareabilitySensitiveText("client_secret = abcdef in the OAuth app")).toBe(true);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test("flags Windows forward-slash home (sanitizePathString only rewrites backslash form)", () => {
|
|
62
|
+
expect(hasShareabilitySensitiveText("logs are under C:/Users/ufuk/AppData/mc")).toBe(true);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test("flags ~/ rooted personal paths", () => {
|
|
66
|
+
expect(hasShareabilitySensitiveText("config lives at ~/.config/opencode/x.jsonc")).toBe(
|
|
67
|
+
true,
|
|
68
|
+
);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test("flags local / private endpoints", () => {
|
|
72
|
+
expect(hasShareabilitySensitiveText("embed endpoint is http://localhost:1234/v1")).toBe(
|
|
73
|
+
true,
|
|
74
|
+
);
|
|
75
|
+
expect(hasShareabilitySensitiveText("the box answers on 127.0.0.1:8080")).toBe(true);
|
|
76
|
+
expect(hasShareabilitySensitiveText("LAN host 192.168.1.42 runs the model")).toBe(true);
|
|
77
|
+
expect(hasShareabilitySensitiveText("internal 10.0.0.5 endpoint")).toBe(true);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test("a public IP / port alone is not flagged by the private-range rules", () => {
|
|
81
|
+
// 8.8.8.8 is public; no private-range or localhost pattern should match.
|
|
82
|
+
expect(hasShareabilitySensitiveText("DNS resolver at 8.8.8.8")).toBe(false);
|
|
83
|
+
});
|
|
84
|
+
});
|