@cortexkit/opencode-magic-context 0.22.0 → 0.22.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/README.md +10 -0
- package/dist/config/agent-disable.d.ts +0 -9
- package/dist/config/agent-disable.d.ts.map +1 -1
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/schema/agent-overrides.d.ts +0 -3
- package/dist/config/schema/agent-overrides.d.ts.map +1 -1
- package/dist/config/schema/magic-context.d.ts +15 -0
- package/dist/config/schema/magic-context.d.ts.map +1 -1
- package/dist/features/builtin-commands/types.d.ts +0 -2
- package/dist/features/builtin-commands/types.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/scheduler.d.ts +0 -4
- package/dist/features/magic-context/dreamer/scheduler.d.ts.map +1 -1
- package/dist/features/magic-context/git-commits/index.d.ts +1 -0
- package/dist/features/magic-context/git-commits/index.d.ts.map +1 -1
- package/dist/features/magic-context/git-commits/storage-git-commits.d.ts.map +1 -1
- package/dist/features/magic-context/git-commits/sweep-coordinator.d.ts +48 -0
- package/dist/features/magic-context/git-commits/sweep-coordinator.d.ts.map +1 -0
- package/dist/features/magic-context/key-files/storage-key-files.d.ts +0 -5
- package/dist/features/magic-context/key-files/storage-key-files.d.ts.map +1 -1
- package/dist/features/magic-context/literal-probes.d.ts +24 -0
- package/dist/features/magic-context/literal-probes.d.ts.map +1 -0
- package/dist/features/magic-context/memory/embedding-identity.d.ts.map +1 -1
- package/dist/features/magic-context/memory/embedding-openai.d.ts +6 -0
- package/dist/features/magic-context/memory/embedding-openai.d.ts.map +1 -1
- package/dist/features/magic-context/memory/embedding-probe.d.ts +5 -0
- package/dist/features/magic-context/memory/embedding-probe.d.ts.map +1 -1
- package/dist/features/magic-context/memory/embedding.d.ts.map +1 -1
- package/dist/features/magic-context/migrations.d.ts.map +1 -1
- package/dist/features/magic-context/project-embedding-registry.d.ts.map +1 -1
- package/dist/features/magic-context/search.d.ts +7 -0
- package/dist/features/magic-context/search.d.ts.map +1 -1
- package/dist/features/magic-context/storage-db.d.ts +1 -1
- package/dist/features/magic-context/storage-db.d.ts.map +1 -1
- package/dist/features/magic-context/storage-notes.d.ts +8 -0
- package/dist/features/magic-context/storage-notes.d.ts.map +1 -1
- package/dist/hooks/auto-update-checker/cache.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-types.d.ts +14 -1
- package/dist/hooks/magic-context/compartment-runner-types.d.ts.map +1 -1
- package/dist/hooks/magic-context/derive-budgets.d.ts +3 -3
- package/dist/hooks/magic-context/event-resolvers.d.ts +1 -0
- package/dist/hooks/magic-context/event-resolvers.d.ts.map +1 -1
- package/dist/hooks/magic-context/recomp-orchestrator.d.ts +7 -2
- package/dist/hooks/magic-context/recomp-orchestrator.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +667 -266
- package/dist/plugin/dream-timer.d.ts.map +1 -1
- package/dist/plugin/event.d.ts +10 -0
- package/dist/plugin/event.d.ts.map +1 -1
- package/dist/plugin/rpc-handlers.d.ts.map +1 -1
- package/dist/shared/announcement.d.ts +16 -0
- package/dist/shared/announcement.d.ts.map +1 -1
- package/dist/shared/models-dev-cache.d.ts +54 -27
- package/dist/shared/models-dev-cache.d.ts.map +1 -1
- package/dist/shared/rpc-types.d.ts +3 -1
- package/dist/shared/rpc-types.d.ts.map +1 -1
- package/dist/tools/ctx-note/tools.d.ts.map +1 -1
- package/dist/tools/ctx-search/tools.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/shared/announcement.test.ts +23 -7
- package/src/shared/announcement.ts +24 -1
- package/src/shared/conflict-detector.test.ts +15 -2
- package/src/shared/conflict-fixer.test.ts +5 -1
- package/src/shared/models-dev-cache.test.ts +200 -300
- package/src/shared/models-dev-cache.ts +184 -176
- package/src/shared/opencode-compaction-detector.test.ts +10 -2
- package/src/shared/rpc-client.test.ts +5 -1
- package/src/shared/rpc-types.ts +3 -1
- package/src/tui/index.tsx +17 -8
- package/src/tui/slots/sidebar-content.tsx +20 -10
package/dist/index.js
CHANGED
|
@@ -14818,25 +14818,25 @@ var init_agent_overrides = __esm(() => {
|
|
|
14818
14818
|
external_directory: PermissionValueSchema.optional()
|
|
14819
14819
|
}).optional();
|
|
14820
14820
|
AgentOverrideConfigSchema = exports_external.object({
|
|
14821
|
-
model: exports_external.string().optional(),
|
|
14822
|
-
temperature: exports_external.number().min(0).max(2).optional(),
|
|
14823
|
-
top_p: exports_external.number().min(0).max(1).optional(),
|
|
14824
|
-
prompt: exports_external.string().optional(),
|
|
14825
|
-
tools: exports_external.record(exports_external.string(), exports_external.boolean()).optional(),
|
|
14826
|
-
disable: exports_external.boolean().optional(),
|
|
14827
|
-
description: exports_external.string().optional(),
|
|
14828
|
-
mode: exports_external.enum(["subagent", "primary", "all"]).optional(),
|
|
14829
|
-
color: exports_external.string().regex(/^#[0-9A-Fa-f]{6}$/).optional(),
|
|
14830
|
-
maxSteps: exports_external.number().optional(),
|
|
14831
|
-
permission: PermissionSchema,
|
|
14832
|
-
maxTokens: exports_external.number().optional(),
|
|
14833
|
-
variant: exports_external.string().optional(),
|
|
14834
|
-
fallback_models: exports_external.union([exports_external.string(), exports_external.array(exports_external.string())]).optional()
|
|
14821
|
+
model: exports_external.string().optional().describe("Primary model ID (e.g. 'claude-sonnet-4-6')"),
|
|
14822
|
+
temperature: exports_external.number().min(0).max(2).optional().describe("Sampling temperature (0-2)"),
|
|
14823
|
+
top_p: exports_external.number().min(0).max(1).optional().describe("Nucleus sampling top_p (0-1)"),
|
|
14824
|
+
prompt: exports_external.string().optional().describe("Additional system prompt text"),
|
|
14825
|
+
tools: exports_external.record(exports_external.string(), exports_external.boolean()).optional().describe("Tool enable/disable overrides"),
|
|
14826
|
+
disable: exports_external.boolean().optional().describe("Disable this agent"),
|
|
14827
|
+
description: exports_external.string().optional().describe("Agent description"),
|
|
14828
|
+
mode: exports_external.enum(["subagent", "primary", "all"]).optional().describe("Agent mode (subagent, primary, or all)"),
|
|
14829
|
+
color: exports_external.string().regex(/^#[0-9A-Fa-f]{6}$/).optional().describe("Hex color for the agent (e.g. '#a1b2c3')"),
|
|
14830
|
+
maxSteps: exports_external.number().optional().describe("Maximum tool-call steps per invocation"),
|
|
14831
|
+
permission: PermissionSchema.describe("Per-tool permission overrides"),
|
|
14832
|
+
maxTokens: exports_external.number().optional().describe("Maximum output tokens"),
|
|
14833
|
+
variant: exports_external.string().optional().describe("OpenCode reasoning variant (e.g. for extended thinking)"),
|
|
14834
|
+
fallback_models: exports_external.union([exports_external.string(), exports_external.array(exports_external.string())]).optional().describe("Fallback model IDs if primary is unavailable")
|
|
14835
14835
|
});
|
|
14836
14836
|
});
|
|
14837
14837
|
|
|
14838
14838
|
// src/config/schema/magic-context.ts
|
|
14839
|
-
var DEFAULT_NUDGE_INTERVAL_TOKENS = 1e4, DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE = 65, DEFAULT_HISTORIAN_TIMEOUT_MS = 300000, DEFAULT_HISTORY_BUDGET_PERCENTAGE = 0.15, DEFAULT_LOCAL_EMBEDDING_MODEL = "Xenova/all-MiniLM-L6-v2", DREAMER_TASKS, DreamingTaskSchema, DEFAULT_DREAMER_TASKS, PiThinkingLevelSchema, DreamerConfigSchema, SidekickConfigSchema, HistorianConfigSchema, BaseEmbeddingConfigSchema, EmbeddingConfigSchema, MagicContextConfigSchema;
|
|
14839
|
+
var DEFAULT_NUDGE_INTERVAL_TOKENS = 1e4, DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE = 65, EXECUTE_THRESHOLD_CAP_MESSAGE = "execute_threshold is capped at 80% for cache safety: a single large agent step can overflow the context window before Magic Context can compact between turns, forcing OpenCode's native compaction (hard to recover from). 80% also leaves headroom below the 85%/95% emergency bands. Use a value between 20 and 80.", DEFAULT_HISTORIAN_TIMEOUT_MS = 300000, DEFAULT_HISTORY_BUDGET_PERCENTAGE = 0.15, DEFAULT_LOCAL_EMBEDDING_MODEL = "Xenova/all-MiniLM-L6-v2", DREAMER_TASKS, DreamingTaskSchema, DEFAULT_DREAMER_TASKS, PiThinkingLevelSchema, DreamerConfigSchema, SidekickConfigSchema, HistorianConfigSchema, BaseEmbeddingConfigSchema, EmbeddingConfigSchema, MagicContextConfigSchema;
|
|
14840
14840
|
var init_magic_context = __esm(() => {
|
|
14841
14841
|
init_zod();
|
|
14842
14842
|
init_agent_overrides();
|
|
@@ -14856,36 +14856,38 @@ var init_magic_context = __esm(() => {
|
|
|
14856
14856
|
];
|
|
14857
14857
|
PiThinkingLevelSchema = exports_external.enum(["off", "minimal", "low", "medium", "high", "xhigh"]).optional();
|
|
14858
14858
|
DreamerConfigSchema = AgentOverrideConfigSchema.merge(exports_external.object({
|
|
14859
|
-
schedule: exports_external.string().default("02:00-06:00"),
|
|
14860
|
-
max_runtime_minutes: exports_external.number().min(10).default(120),
|
|
14861
|
-
tasks: exports_external.array(DreamingTaskSchema).default(DEFAULT_DREAMER_TASKS),
|
|
14862
|
-
task_timeout_minutes: exports_external.number().min(5).default(20),
|
|
14863
|
-
inject_docs: exports_external.boolean().default(true),
|
|
14859
|
+
schedule: exports_external.string().default("02:00-06:00").describe("Scheduled window for overnight dreaming (e.g. '02:00-06:00')"),
|
|
14860
|
+
max_runtime_minutes: exports_external.number().min(10).default(120).describe("Maximum runtime per dream session in minutes"),
|
|
14861
|
+
tasks: exports_external.array(DreamingTaskSchema).default(DEFAULT_DREAMER_TASKS).describe("Tasks to run during dreaming, in order"),
|
|
14862
|
+
task_timeout_minutes: exports_external.number().min(5).default(20).describe("Minutes allocated per task before moving to next"),
|
|
14863
|
+
inject_docs: exports_external.boolean().default(true).describe("Inject ARCHITECTURE.md and STRUCTURE.md into system prompt"),
|
|
14864
14864
|
user_memories: exports_external.object({
|
|
14865
|
-
enabled: exports_external.boolean().default(true),
|
|
14866
|
-
promotion_threshold: exports_external.number().min(2).max(20).default(3)
|
|
14867
|
-
}).default({ enabled: true, promotion_threshold: 3 }),
|
|
14865
|
+
enabled: exports_external.boolean().default(true).describe("Enable user memory extraction and promotion (default: true)"),
|
|
14866
|
+
promotion_threshold: exports_external.number().min(2).max(20).default(3).describe("Minimum candidate observations before dreamer considers promotion (default: 3)")
|
|
14867
|
+
}).default({ enabled: true, promotion_threshold: 3 }).describe("User memory pipeline: historian extracts behavior observations from each compartment run; dreamer reviews recurring patterns and promotes them to stable user memories injected into all sessions as <user-profile>. Requires dreamer to not be disabled for promotion to actually happen. Graduated from experimental in v0.14. Default: enabled."),
|
|
14868
14868
|
pin_key_files: exports_external.object({
|
|
14869
|
-
enabled: exports_external.boolean().default(false),
|
|
14870
|
-
token_budget: exports_external.number().min(2000).max(30000).default(1e4),
|
|
14871
|
-
min_reads: exports_external.number().min(2).max(20).default(4)
|
|
14872
|
-
}).default({ enabled: false, token_budget: 1e4, min_reads: 4 }),
|
|
14873
|
-
thinking_level: PiThinkingLevelSchema
|
|
14869
|
+
enabled: exports_external.boolean().default(false).describe("Enable key file pinning (default: false)"),
|
|
14870
|
+
token_budget: exports_external.number().min(2000).max(30000).default(1e4).describe("Total token budget for all pinned key files (min: 2000, max: 30000, default: 10000)"),
|
|
14871
|
+
min_reads: exports_external.number().min(2).max(20).default(4).describe("Minimum full-read count before a file is considered for pinning (min: 2, default: 4)")
|
|
14872
|
+
}).default({ enabled: false, token_budget: 1e4, min_reads: 4 }).describe("Pin frequently-read key files into the system prompt so the agent doesn't need to re-read them after context drops. Dreamer identifies key files per session based on read patterns. Requires dreamer to not be disabled for selection to happen. Graduated from experimental in v0.14. Default: disabled."),
|
|
14873
|
+
thinking_level: PiThinkingLevelSchema.describe("Pi only: explicit thinking level for dreamer subagent invocations. See historian.thinking_level.")
|
|
14874
14874
|
}));
|
|
14875
14875
|
SidekickConfigSchema = AgentOverrideConfigSchema.extend({
|
|
14876
|
-
timeout_ms: exports_external.number().default(30000),
|
|
14877
|
-
system_prompt: exports_external.string().optional(),
|
|
14878
|
-
thinking_level: PiThinkingLevelSchema
|
|
14876
|
+
timeout_ms: exports_external.number().default(30000).describe("Timeout for sidekick calls in milliseconds"),
|
|
14877
|
+
system_prompt: exports_external.string().optional().describe("Custom system prompt for sidekick"),
|
|
14878
|
+
thinking_level: PiThinkingLevelSchema.describe("Pi only: explicit thinking level for sidekick subagent invocations. See historian.thinking_level.")
|
|
14879
14879
|
}).optional();
|
|
14880
14880
|
HistorianConfigSchema = AgentOverrideConfigSchema.extend({
|
|
14881
|
-
two_pass: exports_external.boolean().default(false),
|
|
14882
|
-
thinking_level: PiThinkingLevelSchema
|
|
14881
|
+
two_pass: exports_external.boolean().default(false).describe("Run a second editor pass over historian output to clean low-signal U: lines and cross-compartment duplicates. Adds ~1 extra API call and ~1.3x cost per historian run. Useful for models without extended thinking support. (default: false)"),
|
|
14882
|
+
thinking_level: PiThinkingLevelSchema.describe("Pi only: explicit thinking level passed as --thinking <level> to Pi historian subagent invocations. Required when using reasoning models (e.g. github-copilot/gpt-5.4) because Pi's default thinking-level resolution can pick a value the provider rejects. OpenCode users set variant instead. Valid: off | minimal | low | medium | high | xhigh")
|
|
14883
14883
|
}).optional();
|
|
14884
14884
|
BaseEmbeddingConfigSchema = exports_external.object({
|
|
14885
|
-
provider: exports_external.enum(["local", "openai-compatible", "off"]).default("local"),
|
|
14886
|
-
model: exports_external.string().optional(),
|
|
14887
|
-
endpoint: exports_external.string().optional(),
|
|
14888
|
-
api_key: exports_external.string().optional()
|
|
14885
|
+
provider: exports_external.enum(["local", "openai-compatible", "off"]).default("local").describe("Embedding provider. 'local' uses Xenova/all-MiniLM-L6-v2, 'openai-compatible' requires endpoint and model, 'off' disables embeddings."),
|
|
14886
|
+
model: exports_external.string().optional().describe("Embedding model name. Required for openai-compatible, ignored for local."),
|
|
14887
|
+
endpoint: exports_external.string().optional().describe("API endpoint URL. Required when provider is openai-compatible."),
|
|
14888
|
+
api_key: exports_external.string().optional().describe("API key for remote embedding provider (optional)"),
|
|
14889
|
+
input_type: exports_external.string().optional().describe("Optional input_type sent in the embedding request body. Required by some openai-compatible providers (e.g. NVIDIA NIM expects 'query' or 'passage'). Omitted from the request when unset."),
|
|
14890
|
+
truncate: exports_external.string().optional().describe("Optional truncate mode sent in the embedding request body (e.g. NVIDIA NIM accepts 'NONE' | 'START' | 'END'). Omitted from the request when unset.")
|
|
14889
14891
|
}).superRefine((data, ctx) => {
|
|
14890
14892
|
if (data.provider === "openai-compatible" && !data.endpoint?.trim()) {
|
|
14891
14893
|
ctx.addIssue({
|
|
@@ -14911,76 +14913,80 @@ var init_magic_context = __esm(() => {
|
|
|
14911
14913
|
}
|
|
14912
14914
|
if (data.provider === "openai-compatible") {
|
|
14913
14915
|
const apiKey = data.api_key?.trim();
|
|
14916
|
+
const inputType = data.input_type?.trim();
|
|
14917
|
+
const truncate = data.truncate?.trim();
|
|
14914
14918
|
return {
|
|
14915
14919
|
provider: "openai-compatible",
|
|
14916
14920
|
model: data.model?.trim() ?? "",
|
|
14917
14921
|
endpoint: data.endpoint?.trim() ?? "",
|
|
14918
|
-
...apiKey ? { api_key: apiKey } : {}
|
|
14922
|
+
...apiKey ? { api_key: apiKey } : {},
|
|
14923
|
+
...inputType ? { input_type: inputType } : {},
|
|
14924
|
+
...truncate ? { truncate } : {}
|
|
14919
14925
|
};
|
|
14920
14926
|
}
|
|
14921
14927
|
return { provider: "off" };
|
|
14922
14928
|
});
|
|
14923
14929
|
MagicContextConfigSchema = exports_external.object({
|
|
14924
|
-
enabled: exports_external.boolean().default(true),
|
|
14925
|
-
auto_update: exports_external.boolean().optional(),
|
|
14926
|
-
ctx_reduce_enabled: exports_external.boolean().default(true),
|
|
14927
|
-
historian: HistorianConfigSchema,
|
|
14928
|
-
dreamer: DreamerConfigSchema.optional(),
|
|
14929
|
-
cache_ttl: exports_external.union([exports_external.string(), exports_external.object({ default: exports_external.string() }).catchall(exports_external.string())]).default("5m"),
|
|
14930
|
-
nudge_interval_tokens: exports_external.number().min(1000).default(DEFAULT_NUDGE_INTERVAL_TOKENS),
|
|
14930
|
+
enabled: exports_external.boolean().default(true).describe("Enable magic context (default: true)"),
|
|
14931
|
+
auto_update: exports_external.boolean().optional().describe("Enable automatic npm self-update checks for the OpenCode plugin. Security: USER-only in config loader, so hostile project configs cannot suppress updates."),
|
|
14932
|
+
ctx_reduce_enabled: exports_external.boolean().default(true).describe("When false, ctx_reduce tool is hidden, all nudges disabled, and prompt guidance about ctx_reduce stripped. Heuristic cleanup, compartments, memory, and other features still work. (default: true)"),
|
|
14933
|
+
historian: HistorianConfigSchema.describe("Historian agent configuration (model, fallback_models, variant, temperature, maxTokens, permission, two_pass, etc.)"),
|
|
14934
|
+
dreamer: DreamerConfigSchema.optional().describe("Dreamer agent + scheduling configuration (model, fallback_models, disable, schedule, tasks, etc.)"),
|
|
14935
|
+
cache_ttl: exports_external.union([exports_external.string(), exports_external.object({ default: exports_external.string() }).catchall(exports_external.string())]).default("5m").describe('Cache TTL: string (e.g. "5m") or per-model object ({ default: "5m", "model-id": "10m" })'),
|
|
14936
|
+
nudge_interval_tokens: exports_external.number().min(1000).default(DEFAULT_NUDGE_INTERVAL_TOKENS).describe("Minimum token growth between low-priority rolling nudges (default: DEFAULT_NUDGE_INTERVAL_TOKENS)"),
|
|
14931
14937
|
execute_threshold_percentage: exports_external.union([
|
|
14932
|
-
exports_external.number().min(20).max(80),
|
|
14933
|
-
exports_external.object({ default: exports_external.number().min(20).max(80) }).catchall(exports_external.number().min(20).max(80))
|
|
14934
|
-
]).default(DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE),
|
|
14938
|
+
exports_external.number().min(20).max(80, EXECUTE_THRESHOLD_CAP_MESSAGE),
|
|
14939
|
+
exports_external.object({ default: exports_external.number().min(20).max(80, EXECUTE_THRESHOLD_CAP_MESSAGE) }).catchall(exports_external.number().min(20).max(80, EXECUTE_THRESHOLD_CAP_MESSAGE))
|
|
14940
|
+
]).default(DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE).describe('Context percentage that forces queued operations to execute. Number or per-model object ({ default: 65, "provider/model": 45 }). Values above 80 are rejected because the runtime caps at 80% for cache safety (MAX_EXECUTE_THRESHOLD). Default: DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE'),
|
|
14935
14941
|
execute_threshold_tokens: exports_external.object({
|
|
14936
14942
|
default: exports_external.number().min(5000).max(2000000).optional()
|
|
14937
|
-
}).catchall(exports_external.number().min(5000).max(2000000)).optional(),
|
|
14938
|
-
protected_tags: exports_external.number().min(1).max(100).optional(),
|
|
14939
|
-
auto_drop_tool_age: exports_external.number().min(10).default(100),
|
|
14940
|
-
drop_tool_structure: exports_external.boolean().default(true),
|
|
14941
|
-
clear_reasoning_age: exports_external.number().min(10).default(50),
|
|
14942
|
-
iteration_nudge_threshold: exports_external.number().min(5).default(15),
|
|
14943
|
-
history_budget_percentage: exports_external.number().min(0.05).max(0.5).default(DEFAULT_HISTORY_BUDGET_PERCENTAGE),
|
|
14944
|
-
historian_timeout_ms: exports_external.number().min(60000).default(DEFAULT_HISTORIAN_TIMEOUT_MS),
|
|
14943
|
+
}).catchall(exports_external.number().min(5000).max(2000000)).optional().describe("Absolute token thresholds per model. When matched, overrides execute_threshold_percentage for that model. Accepts `default` for all models or per-model keys. Values above 80% × context_limit are clamped with a warning log. Min 5_000, max 2_000_000."),
|
|
14944
|
+
protected_tags: exports_external.number().min(1).max(100).optional().describe("Number of recent tags to protect from dropping (min: 1, max: 100, default: 20)"),
|
|
14945
|
+
auto_drop_tool_age: exports_external.number().min(10).default(100).describe("Auto-drop tool outputs older than N tags during queue execution (default: 100)"),
|
|
14946
|
+
drop_tool_structure: exports_external.boolean().default(true).describe("When true, dropped tool parts are fully removed instead of truncated in place (default: true)"),
|
|
14947
|
+
clear_reasoning_age: exports_external.number().min(10).default(50).describe("Clear reasoning/thinking blocks older than N tags (default: 50)"),
|
|
14948
|
+
iteration_nudge_threshold: exports_external.number().min(5).default(15).describe("Number of consecutive assistant messages without user input to trigger iteration nudge (default: 15)"),
|
|
14949
|
+
history_budget_percentage: exports_external.number().min(0.05).max(0.5).default(DEFAULT_HISTORY_BUDGET_PERCENTAGE).describe("Fraction of usable context (context_limit × execute_threshold) reserved for the session history block (default: 0.15)"),
|
|
14950
|
+
historian_timeout_ms: exports_external.number().min(60000).default(DEFAULT_HISTORIAN_TIMEOUT_MS).describe("Timeout for each historian prompt call in milliseconds (default: 300000)"),
|
|
14945
14951
|
commit_cluster_trigger: exports_external.object({
|
|
14946
|
-
enabled: exports_external.boolean().default(true),
|
|
14947
|
-
min_clusters: exports_external.number().min(1).default(3)
|
|
14948
|
-
}).default({ enabled: true, min_clusters: 3 }),
|
|
14952
|
+
enabled: exports_external.boolean().default(true).describe("Enable commit-cluster based historian triggering (default: true)"),
|
|
14953
|
+
min_clusters: exports_external.number().min(1).default(3).describe("Minimum commit clusters required to trigger historian (min: 1, default: 3)")
|
|
14954
|
+
}).default({ enabled: true, min_clusters: 3 }).describe("Commit-cluster trigger: fire historian when enough commit clusters accumulate in the unsummarized tail"),
|
|
14949
14955
|
system_prompt_injection: exports_external.object({
|
|
14950
|
-
enabled: exports_external.boolean().default(true),
|
|
14951
|
-
skip_signatures: exports_external.array(exports_external.string()).default(["<!-- magic-context: skip -->"])
|
|
14956
|
+
enabled: exports_external.boolean().default(true).describe("When false, NO injection happens for ANY agent — global escape hatch. (default: true)"),
|
|
14957
|
+
skip_signatures: exports_external.array(exports_external.string()).default(["<!-- magic-context: skip -->"]).describe(`Substring opt-out list. If the agent's system prompt contains any of these strings, skip ALL Magic Context injection for that call. Default "<!-- magic-context: skip -->" is meant to be added inside a user's custom agent prompt to opt that agent out.`)
|
|
14952
14958
|
}).default({
|
|
14953
14959
|
enabled: true,
|
|
14954
14960
|
skip_signatures: ["<!-- magic-context: skip -->"]
|
|
14955
|
-
}),
|
|
14961
|
+
}).describe("Controls whether and where Magic Context augments the system prompt. Lets users opt specific agents out of the Magic Context guidance and the surrounding project-docs / user-profile / key-files blocks. OpenCode's internal hidden agents — title, summary, and compaction — are always skipped automatically."),
|
|
14956
14962
|
sqlite: exports_external.object({
|
|
14957
|
-
cache_size_mb: exports_external.number().min(2).max(2048).default(64),
|
|
14958
|
-
mmap_size_mb: exports_external.number().min(0).max(8192).default(0)
|
|
14959
|
-
}).default({ cache_size_mb: 64, mmap_size_mb: 0 }),
|
|
14963
|
+
cache_size_mb: exports_external.number().min(2).max(2048).default(64).describe("Page-cache size in MiB per connection (PRAGMA cache_size). Larger keeps more hot pages resident, cutting re-reads on repeated full-table scans. (min 2, max 2048, default 64)"),
|
|
14964
|
+
mmap_size_mb: exports_external.number().min(0).max(8192).default(0).describe("Memory-mapped I/O size in MiB (PRAGMA mmap_size). 0 disables mmap (SQLite default). Raising it can cut read overhead on large DBs at the cost of address space. (min 0, max 8192, default 0)")
|
|
14965
|
+
}).default({ cache_size_mb: 64, mmap_size_mb: 0 }).describe("SQLite connection tuning for Magic Context's own context.db. These are per-connection PRAGMAs applied at open; they do not change the schema or what is stored."),
|
|
14960
14966
|
embedding: EmbeddingConfigSchema.default({
|
|
14961
14967
|
provider: "local",
|
|
14962
14968
|
model: DEFAULT_LOCAL_EMBEDDING_MODEL
|
|
14963
|
-
}),
|
|
14964
|
-
temporal_awareness: exports_external.boolean().default(true),
|
|
14969
|
+
}).describe("Embedding provider configuration"),
|
|
14970
|
+
temporal_awareness: exports_external.boolean().default(true).describe('Inject wall-clock gap markers (<!-- +Xm -->) between user messages where > 5 min elapsed since the previous message, and add start/end date attributes on compartments. Gives the agent a sense of session pacing and "how long ago" across multi-day sessions. Graduated from experimental.temporal_awareness; default: true (set false to opt out).'),
|
|
14965
14971
|
caveman_text_compression: exports_external.object({
|
|
14966
|
-
enabled: exports_external.boolean().default(false),
|
|
14967
|
-
min_chars: exports_external.number().min(100).max(1e4).default(500)
|
|
14968
|
-
}).default({ enabled: false, min_chars: 500 }),
|
|
14972
|
+
enabled: exports_external.boolean().default(false).describe("Apply deterministic caveman-style text compression to old conversation text. Only active when ctx_reduce_enabled=false. Compresses user/assistant text in oldest-first tiers: ultra (oldest 20%), full, lite, untouched (newest 40%)."),
|
|
14973
|
+
min_chars: exports_external.number().min(100).max(1e4).default(500).describe("Text parts shorter than this (characters) stay untouched. Min 100, max 10000. Default: 500.")
|
|
14974
|
+
}).default({ enabled: false, min_chars: 500 }).describe("Age-tier caveman compression for long user/assistant text parts. Only active when ctx_reduce_enabled is false. Oldest 20% of eligible tags (outside protected tail) go to ultra, next 20% to full, next 20% to lite, newest 40% untouched. Graduated from experimental.caveman_text_compression; opt-in, default off (lossy)."),
|
|
14969
14975
|
memory: exports_external.object({
|
|
14970
|
-
enabled: exports_external.boolean().default(true),
|
|
14971
|
-
injection_budget_tokens: exports_external.number().min(500).max(20000).default(4000),
|
|
14972
|
-
auto_promote: exports_external.boolean().default(true),
|
|
14973
|
-
retrieval_count_promotion_threshold: exports_external.number().min(1).default(3),
|
|
14976
|
+
enabled: exports_external.boolean().default(true).describe("Enable cross-session memory (default: true)"),
|
|
14977
|
+
injection_budget_tokens: exports_external.number().min(500).max(20000).default(4000).describe("Token budget for memory injection on session start (min: 500, max: 20000, default: 4000)"),
|
|
14978
|
+
auto_promote: exports_external.boolean().default(true).describe("Automatically promote eligible session facts into memory (default: true)"),
|
|
14979
|
+
retrieval_count_promotion_threshold: exports_external.number().min(1).default(3).describe("retrieval_count threshold for promoting memory to permanent status (min: 1, default: 3)"),
|
|
14974
14980
|
auto_search: exports_external.object({
|
|
14975
|
-
enabled: exports_external.boolean().default(true),
|
|
14976
|
-
score_threshold: exports_external.number().min(0.3).max(0.95).default(0.6),
|
|
14977
|
-
min_prompt_chars: exports_external.number().min(5).max(500).default(20)
|
|
14978
|
-
}).default({ enabled: true, score_threshold: 0.6, min_prompt_chars: 20 }),
|
|
14981
|
+
enabled: exports_external.boolean().default(true).describe("Automatically append a compact <ctx-search-hint> to eligible user messages when relevant memories, conversation, or commits are found. Graduated from experimental.auto_search; on by default (set false to opt out). Independent of memory.enabled."),
|
|
14982
|
+
score_threshold: exports_external.number().min(0.3).max(0.95).default(0.6).describe("Top hit score must exceed this threshold for the hint to fire (min: 0.3, max: 0.95, default: 0.60)"),
|
|
14983
|
+
min_prompt_chars: exports_external.number().min(5).max(500).default(20).describe("Skip hint when user message is shorter than this (min: 5, max: 500, default: 20)")
|
|
14984
|
+
}).default({ enabled: true, score_threshold: 0.6, min_prompt_chars: 20 }).describe("Auto-search hint: transform-time ctx_search on each new user message; when the top hit clears the threshold, append a compact <ctx-search-hint> block of vague fragments to that user message. Does NOT inject full content. Graduated from experimental.auto_search; enabled by default (set enabled: false to opt out). Independent of memory.enabled."),
|
|
14979
14985
|
git_commit_indexing: exports_external.object({
|
|
14980
|
-
enabled: exports_external.boolean().default(false),
|
|
14981
|
-
since_days: exports_external.number().min(7).max(3650).default(365),
|
|
14982
|
-
max_commits: exports_external.number().min(100).max(20000).default(2000)
|
|
14983
|
-
}).default({ enabled: false, since_days: 365, max_commits: 2000 })
|
|
14986
|
+
enabled: exports_external.boolean().default(false).describe("Index HEAD git commits for ctx_search (git_commit source). Graduated from experimental.git_commit_indexing; opt-in, default off. Independent of memory.enabled."),
|
|
14987
|
+
since_days: exports_external.number().min(7).max(3650).default(365).describe("Days of HEAD history to index (min: 7, max: 3650, default: 365)"),
|
|
14988
|
+
max_commits: exports_external.number().min(100).max(20000).default(2000).describe("Max commits kept per project; oldest evicted (min: 100, max: 20000, default: 2000)")
|
|
14989
|
+
}).default({ enabled: false, since_days: 365, max_commits: 2000 }).describe("Index git commit messages from HEAD into ctx_search. Commits become a 4th searchable source alongside memories and session history. Graduated from experimental.git_commit_indexing; opt-in, default off (per-project embedding cost). Independent of memory.enabled.")
|
|
14984
14990
|
}).default({
|
|
14985
14991
|
enabled: true,
|
|
14986
14992
|
injection_budget_tokens: 4000,
|
|
@@ -14988,8 +14994,8 @@ var init_magic_context = __esm(() => {
|
|
|
14988
14994
|
retrieval_count_promotion_threshold: 3,
|
|
14989
14995
|
auto_search: { enabled: true, score_threshold: 0.6, min_prompt_chars: 20 },
|
|
14990
14996
|
git_commit_indexing: { enabled: false, since_days: 365, max_commits: 2000 }
|
|
14991
|
-
}),
|
|
14992
|
-
sidekick: SidekickConfigSchema
|
|
14997
|
+
}).describe("Cross-session memory configuration"),
|
|
14998
|
+
sidekick: SidekickConfigSchema.describe("Optional sidekick agent configuration for session-start memory retrieval")
|
|
14993
14999
|
}).transform((data) => {
|
|
14994
15000
|
return {
|
|
14995
15001
|
...data,
|
|
@@ -15207,9 +15213,6 @@ function getMagicContextStorageDir() {
|
|
|
15207
15213
|
function getLegacyOpenCodeMagicContextStorageDir() {
|
|
15208
15214
|
return path2.join(getOpenCodeStorageDir(), "plugin", "magic-context");
|
|
15209
15215
|
}
|
|
15210
|
-
function getCacheDir() {
|
|
15211
|
-
return process.env.XDG_CACHE_HOME ?? path2.join(os.homedir(), ".cache");
|
|
15212
|
-
}
|
|
15213
15216
|
var init_data_path = () => {};
|
|
15214
15217
|
|
|
15215
15218
|
// src/shared/logger.ts
|
|
@@ -150721,6 +150724,17 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
150721
150724
|
updated_at INTEGER NOT NULL DEFAULT 0
|
|
150722
150725
|
);
|
|
150723
150726
|
|
|
150727
|
+
CREATE TABLE IF NOT EXISTS git_sweep_coordinator (
|
|
150728
|
+
project_path TEXT PRIMARY KEY,
|
|
150729
|
+
lease_holder TEXT,
|
|
150730
|
+
lease_expires_at INTEGER,
|
|
150731
|
+
last_swept_at INTEGER
|
|
150732
|
+
);
|
|
150733
|
+
CREATE INDEX IF NOT EXISTS idx_git_sweep_coordinator_lease_expires
|
|
150734
|
+
ON git_sweep_coordinator(lease_expires_at);
|
|
150735
|
+
CREATE INDEX IF NOT EXISTS idx_git_sweep_coordinator_last_swept
|
|
150736
|
+
ON git_sweep_coordinator(last_swept_at);
|
|
150737
|
+
|
|
150724
150738
|
CREATE TABLE IF NOT EXISTS m0_mutation_log (
|
|
150725
150739
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
150726
150740
|
session_id TEXT NOT NULL,
|
|
@@ -151250,7 +151264,7 @@ function getDatabasePersistenceError(db) {
|
|
|
151250
151264
|
return null;
|
|
151251
151265
|
return persistenceErrorByDatabase.get(db) ?? null;
|
|
151252
151266
|
}
|
|
151253
|
-
var databases, persistenceByDatabase, persistenceErrorByDatabase, lastSchemaFenceRejection = null, LATEST_SUPPORTED_VERSION =
|
|
151267
|
+
var databases, persistenceByDatabase, persistenceErrorByDatabase, lastSchemaFenceRejection = null, LATEST_SUPPORTED_VERSION = 29, sqlitePragmaConfig;
|
|
151254
151268
|
var init_storage_db = __esm(async () => {
|
|
151255
151269
|
init_data_path();
|
|
151256
151270
|
init_logger();
|
|
@@ -152041,6 +152055,38 @@ var init_migrations = __esm(async () => {
|
|
|
152041
152055
|
ON tags(session_id, entry_fingerprint)
|
|
152042
152056
|
WHERE type='message' AND entry_fingerprint IS NOT NULL`);
|
|
152043
152057
|
}
|
|
152058
|
+
},
|
|
152059
|
+
{
|
|
152060
|
+
version: 28,
|
|
152061
|
+
description: "Add git commit sweep coordinator lease/cooldown table",
|
|
152062
|
+
up: (db) => {
|
|
152063
|
+
db.exec(`
|
|
152064
|
+
CREATE TABLE IF NOT EXISTS git_sweep_coordinator (
|
|
152065
|
+
project_path TEXT PRIMARY KEY,
|
|
152066
|
+
lease_holder TEXT,
|
|
152067
|
+
lease_expires_at INTEGER,
|
|
152068
|
+
last_swept_at INTEGER
|
|
152069
|
+
);
|
|
152070
|
+
CREATE INDEX IF NOT EXISTS idx_git_sweep_coordinator_lease_expires
|
|
152071
|
+
ON git_sweep_coordinator(lease_expires_at);
|
|
152072
|
+
CREATE INDEX IF NOT EXISTS idx_git_sweep_coordinator_last_swept
|
|
152073
|
+
ON git_sweep_coordinator(last_swept_at);
|
|
152074
|
+
`);
|
|
152075
|
+
}
|
|
152076
|
+
},
|
|
152077
|
+
{
|
|
152078
|
+
version: 29,
|
|
152079
|
+
description: "Add anchor_ordinal to notes (traceback to the conversation tail)",
|
|
152080
|
+
up: (db) => {
|
|
152081
|
+
const notesExists = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='notes'").get();
|
|
152082
|
+
if (!notesExists) {
|
|
152083
|
+
return;
|
|
152084
|
+
}
|
|
152085
|
+
const columns = db.prepare("PRAGMA table_info(notes)").all();
|
|
152086
|
+
if (!columns.some((column) => column.name === "anchor_ordinal")) {
|
|
152087
|
+
db.exec("ALTER TABLE notes ADD COLUMN anchor_ordinal INTEGER");
|
|
152088
|
+
}
|
|
152089
|
+
}
|
|
152044
152090
|
}
|
|
152045
152091
|
];
|
|
152046
152092
|
LATEST_MIGRATION_VERSION = MIGRATIONS.reduce((max, m) => Math.max(max, m.version), 0);
|
|
@@ -152998,7 +153044,8 @@ function toNote(row) {
|
|
|
152998
153044
|
updatedAt: row.updated_at,
|
|
152999
153045
|
lastCheckedAt: toNullableNumber(row.last_checked_at),
|
|
153000
153046
|
readyAt: toNullableNumber(row.ready_at),
|
|
153001
|
-
readyReason: toNullableString(row.ready_reason)
|
|
153047
|
+
readyReason: toNullableString(row.ready_reason),
|
|
153048
|
+
anchorOrdinal: toNullableNumber(row.anchor_ordinal)
|
|
153002
153049
|
};
|
|
153003
153050
|
}
|
|
153004
153051
|
function getNoteById(db, noteId) {
|
|
@@ -153050,7 +153097,7 @@ function getNotes(db, options = {}) {
|
|
|
153050
153097
|
}
|
|
153051
153098
|
function addNote(db, type, options) {
|
|
153052
153099
|
const now = Date.now();
|
|
153053
|
-
const result = type === "session" ? db.prepare("INSERT INTO notes (type, status, content, session_id, created_at, updated_at, harness) VALUES ('session', 'active', ?, ?, ?, ?, ?) RETURNING *").get(options.content, options.sessionId, now, now, getHarness()) : db.prepare("INSERT INTO notes (type, status, content, session_id, project_path, surface_condition, created_at, updated_at, harness) VALUES ('smart', 'pending', ?, ?, ?, ?, ?, ?, ?) RETURNING *").get(options.content, options.sessionId ?? null, options.projectPath, options.surfaceCondition, now, now, getHarness());
|
|
153100
|
+
const result = type === "session" ? db.prepare("INSERT INTO notes (type, status, content, session_id, created_at, updated_at, harness, anchor_ordinal) VALUES ('session', 'active', ?, ?, ?, ?, ?, ?) RETURNING *").get(options.content, options.sessionId, now, now, getHarness(), options.anchorOrdinal ?? null) : db.prepare("INSERT INTO notes (type, status, content, session_id, project_path, surface_condition, created_at, updated_at, harness, anchor_ordinal) VALUES ('smart', 'pending', ?, ?, ?, ?, ?, ?, ?, ?) RETURNING *").get(options.content, options.sessionId ?? null, options.projectPath, options.surfaceCondition, now, now, getHarness(), options.anchorOrdinal ?? null);
|
|
153054
153101
|
if (!isNoteRow(result)) {
|
|
153055
153102
|
throw new Error("[notes] failed to insert note");
|
|
153056
153103
|
}
|
|
@@ -163633,7 +163680,8 @@ function getEmbeddingProviderIdentity(config2) {
|
|
|
163633
163680
|
provider: "openai-compatible",
|
|
163634
163681
|
model: config2.model.trim(),
|
|
163635
163682
|
endpoint: normalizeEndpoint(config2.endpoint),
|
|
163636
|
-
apiKeyPresent: Boolean(config2.api_key?.trim())
|
|
163683
|
+
apiKeyPresent: Boolean(config2.api_key?.trim()),
|
|
163684
|
+
inputType: config2.input_type?.trim() || ""
|
|
163637
163685
|
} : {
|
|
163638
163686
|
provider: "local",
|
|
163639
163687
|
model: config2.model?.trim() || DEFAULT_LOCAL_EMBEDDING_MODEL,
|
|
@@ -164002,6 +164050,8 @@ class OpenAICompatibleEmbeddingProvider {
|
|
|
164002
164050
|
endpoint;
|
|
164003
164051
|
model;
|
|
164004
164052
|
apiKey;
|
|
164053
|
+
inputType;
|
|
164054
|
+
truncate;
|
|
164005
164055
|
initialized = false;
|
|
164006
164056
|
failureTimes = [];
|
|
164007
164057
|
circuitOpenUntil = 0;
|
|
@@ -164011,6 +164061,8 @@ class OpenAICompatibleEmbeddingProvider {
|
|
|
164011
164061
|
this.endpoint = normalizeEndpoint2(options.endpoint);
|
|
164012
164062
|
this.model = options.model?.trim() ?? "";
|
|
164013
164063
|
this.apiKey = options.apiKey?.trim() ?? "";
|
|
164064
|
+
this.inputType = options.inputType?.trim() ?? "";
|
|
164065
|
+
this.truncate = options.truncate?.trim() ?? "";
|
|
164014
164066
|
this.modelId = getEmbeddingProviderIdentity({
|
|
164015
164067
|
provider: "openai-compatible",
|
|
164016
164068
|
endpoint: this.endpoint,
|
|
@@ -164067,7 +164119,9 @@ class OpenAICompatibleEmbeddingProvider {
|
|
|
164067
164119
|
},
|
|
164068
164120
|
body: JSON.stringify({
|
|
164069
164121
|
model: this.model,
|
|
164070
|
-
input: texts
|
|
164122
|
+
input: texts,
|
|
164123
|
+
...this.inputType ? { input_type: this.inputType } : {},
|
|
164124
|
+
...this.truncate ? { truncate: this.truncate } : {}
|
|
164071
164125
|
}),
|
|
164072
164126
|
signal: internalController.signal
|
|
164073
164127
|
});
|
|
@@ -164306,8 +164360,130 @@ var init_storage_git_commit_embeddings = __esm(() => {
|
|
|
164306
164360
|
distinctModelIdStatements = new WeakMap;
|
|
164307
164361
|
});
|
|
164308
164362
|
|
|
164363
|
+
// src/features/magic-context/git-commits/sweep-coordinator.ts
|
|
164364
|
+
function runImmediate2(db, body) {
|
|
164365
|
+
db.exec("BEGIN IMMEDIATE");
|
|
164366
|
+
let committed = false;
|
|
164367
|
+
try {
|
|
164368
|
+
const result = body();
|
|
164369
|
+
db.exec("COMMIT");
|
|
164370
|
+
committed = true;
|
|
164371
|
+
return result;
|
|
164372
|
+
} finally {
|
|
164373
|
+
if (!committed) {
|
|
164374
|
+
try {
|
|
164375
|
+
db.exec("ROLLBACK");
|
|
164376
|
+
} catch {}
|
|
164377
|
+
}
|
|
164378
|
+
}
|
|
164379
|
+
}
|
|
164380
|
+
function rowToState(row) {
|
|
164381
|
+
return {
|
|
164382
|
+
projectPath: row.project_path,
|
|
164383
|
+
leaseHolder: row.lease_holder,
|
|
164384
|
+
leaseExpiresAt: row.lease_expires_at,
|
|
164385
|
+
lastSweptAt: row.last_swept_at
|
|
164386
|
+
};
|
|
164387
|
+
}
|
|
164388
|
+
function getGitSweepCoordinatorState(db, projectPath) {
|
|
164389
|
+
const row = db.prepare(`SELECT project_path, lease_holder, lease_expires_at, last_swept_at
|
|
164390
|
+
FROM git_sweep_coordinator
|
|
164391
|
+
WHERE project_path = ?`).get(projectPath);
|
|
164392
|
+
return row ? rowToState(row) : null;
|
|
164393
|
+
}
|
|
164394
|
+
function acquireGitSweepLease(db, projectPath, holderId, options = {}) {
|
|
164395
|
+
const cooldownMs = options.cooldownMs ?? GIT_SWEEP_COOLDOWN_MS;
|
|
164396
|
+
const leaseTtlMs = options.leaseTtlMs ?? GIT_SWEEP_LEASE_TTL_MS;
|
|
164397
|
+
return runImmediate2(db, () => {
|
|
164398
|
+
const now = Date.now();
|
|
164399
|
+
const row = getGitSweepCoordinatorState(db, projectPath);
|
|
164400
|
+
if (row?.leaseHolder && row.leaseExpiresAt !== null && row.leaseExpiresAt > now) {
|
|
164401
|
+
return {
|
|
164402
|
+
acquired: false,
|
|
164403
|
+
projectPath,
|
|
164404
|
+
reason: "lease_active",
|
|
164405
|
+
leaseHolder: row.leaseHolder,
|
|
164406
|
+
leaseExpiresAt: row.leaseExpiresAt,
|
|
164407
|
+
lastSweptAt: row.lastSweptAt,
|
|
164408
|
+
nextAllowedAt: null
|
|
164409
|
+
};
|
|
164410
|
+
}
|
|
164411
|
+
if (!options.ignoreCooldown && row?.lastSweptAt !== null && row?.lastSweptAt !== undefined) {
|
|
164412
|
+
const nextAllowedAt = row.lastSweptAt + cooldownMs;
|
|
164413
|
+
if (nextAllowedAt > now) {
|
|
164414
|
+
return {
|
|
164415
|
+
acquired: false,
|
|
164416
|
+
projectPath,
|
|
164417
|
+
reason: "cooldown_active",
|
|
164418
|
+
leaseHolder: row.leaseHolder,
|
|
164419
|
+
leaseExpiresAt: row.leaseExpiresAt,
|
|
164420
|
+
lastSweptAt: row.lastSweptAt,
|
|
164421
|
+
nextAllowedAt
|
|
164422
|
+
};
|
|
164423
|
+
}
|
|
164424
|
+
}
|
|
164425
|
+
const leaseExpiresAt = now + leaseTtlMs;
|
|
164426
|
+
db.prepare(`INSERT INTO git_sweep_coordinator (
|
|
164427
|
+
project_path,
|
|
164428
|
+
lease_holder,
|
|
164429
|
+
lease_expires_at,
|
|
164430
|
+
last_swept_at
|
|
164431
|
+
) VALUES (?, ?, ?, NULL)
|
|
164432
|
+
ON CONFLICT(project_path) DO UPDATE SET
|
|
164433
|
+
lease_holder = excluded.lease_holder,
|
|
164434
|
+
lease_expires_at = excluded.lease_expires_at`).run(projectPath, holderId, leaseExpiresAt);
|
|
164435
|
+
return {
|
|
164436
|
+
acquired: true,
|
|
164437
|
+
projectPath,
|
|
164438
|
+
holderId,
|
|
164439
|
+
acquiredAt: now,
|
|
164440
|
+
leaseExpiresAt
|
|
164441
|
+
};
|
|
164442
|
+
});
|
|
164443
|
+
}
|
|
164444
|
+
function renewGitSweepLease(db, projectPath, holderId, leaseTtlMs = GIT_SWEEP_LEASE_TTL_MS) {
|
|
164445
|
+
return runImmediate2(db, () => {
|
|
164446
|
+
const now = Date.now();
|
|
164447
|
+
const leaseExpiresAt = now + leaseTtlMs;
|
|
164448
|
+
const result = db.prepare(`UPDATE git_sweep_coordinator
|
|
164449
|
+
SET lease_expires_at = ?
|
|
164450
|
+
WHERE project_path = ?
|
|
164451
|
+
AND lease_holder = ?
|
|
164452
|
+
AND lease_expires_at > ?`).run(leaseExpiresAt, projectPath, holderId, now);
|
|
164453
|
+
return result.changes === 1;
|
|
164454
|
+
});
|
|
164455
|
+
}
|
|
164456
|
+
function markGitSweepSuccessAndRelease(db, projectPath, holderId) {
|
|
164457
|
+
return runImmediate2(db, () => {
|
|
164458
|
+
const now = Date.now();
|
|
164459
|
+
const result = db.prepare(`UPDATE git_sweep_coordinator
|
|
164460
|
+
SET lease_holder = NULL,
|
|
164461
|
+
lease_expires_at = NULL,
|
|
164462
|
+
last_swept_at = ?
|
|
164463
|
+
WHERE project_path = ?
|
|
164464
|
+
AND lease_holder = ?
|
|
164465
|
+
AND lease_expires_at > ?`).run(now, projectPath, holderId, now);
|
|
164466
|
+
return result.changes === 1;
|
|
164467
|
+
});
|
|
164468
|
+
}
|
|
164469
|
+
function releaseGitSweepLease(db, projectPath, holderId) {
|
|
164470
|
+
runImmediate2(db, () => {
|
|
164471
|
+
db.prepare(`UPDATE git_sweep_coordinator
|
|
164472
|
+
SET lease_holder = NULL,
|
|
164473
|
+
lease_expires_at = NULL
|
|
164474
|
+
WHERE project_path = ?
|
|
164475
|
+
AND lease_holder = ?`).run(projectPath, holderId);
|
|
164476
|
+
});
|
|
164477
|
+
}
|
|
164478
|
+
var GIT_SWEEP_COOLDOWN_MS, GIT_SWEEP_LEASE_TTL_MS, GIT_SWEEP_LEASE_RENEWAL_MS;
|
|
164479
|
+
var init_sweep_coordinator = __esm(() => {
|
|
164480
|
+
GIT_SWEEP_COOLDOWN_MS = 10 * 60 * 1000;
|
|
164481
|
+
GIT_SWEEP_LEASE_TTL_MS = 5 * 60 * 1000;
|
|
164482
|
+
GIT_SWEEP_LEASE_RENEWAL_MS = 60 * 1000;
|
|
164483
|
+
});
|
|
164484
|
+
|
|
164309
164485
|
// src/features/magic-context/project-embedding-registry.ts
|
|
164310
|
-
import { createHash as createHash7 } from "node:crypto";
|
|
164486
|
+
import { createHash as createHash7, randomUUID } from "node:crypto";
|
|
164311
164487
|
function resolveEmbeddingConfig(config2) {
|
|
164312
164488
|
if (!config2 || config2.provider === "local") {
|
|
164313
164489
|
return {
|
|
@@ -164571,6 +164747,7 @@ var init_project_embedding_registry = __esm(() => {
|
|
|
164571
164747
|
init_magic_context();
|
|
164572
164748
|
init_logger();
|
|
164573
164749
|
init_storage_git_commit_embeddings();
|
|
164750
|
+
init_sweep_coordinator();
|
|
164574
164751
|
init_embedding_cache();
|
|
164575
164752
|
init_embedding_identity();
|
|
164576
164753
|
init_embedding_local();
|
|
@@ -164590,7 +164767,9 @@ function createProvider2(config2) {
|
|
|
164590
164767
|
return new OpenAICompatibleEmbeddingProvider({
|
|
164591
164768
|
endpoint: config2.endpoint,
|
|
164592
164769
|
model: config2.model,
|
|
164593
|
-
apiKey: config2.api_key
|
|
164770
|
+
apiKey: config2.api_key,
|
|
164771
|
+
inputType: config2.input_type,
|
|
164772
|
+
truncate: config2.truncate
|
|
164594
164773
|
});
|
|
164595
164774
|
}
|
|
164596
164775
|
return new LocalEmbeddingProvider(config2.model);
|
|
@@ -164667,32 +164846,48 @@ var init_storage_memory_fts = __esm(() => {
|
|
|
164667
164846
|
});
|
|
164668
164847
|
|
|
164669
164848
|
// src/shared/models-dev-cache.ts
|
|
164670
|
-
import {
|
|
164671
|
-
import { existsSync as existsSync14, readFileSync as readFileSync12 } from "node:fs";
|
|
164672
|
-
import { homedir as homedir9, platform as platform3 } from "node:os";
|
|
164849
|
+
import { mkdirSync as mkdirSync5, readFileSync as readFileSync12, renameSync as renameSync3, writeFileSync as writeFileSync4 } from "node:fs";
|
|
164673
164850
|
import { join as join18 } from "node:path";
|
|
164674
|
-
function
|
|
164675
|
-
return
|
|
164676
|
-
}
|
|
164677
|
-
function
|
|
164678
|
-
|
|
164679
|
-
|
|
164680
|
-
|
|
164681
|
-
|
|
164682
|
-
|
|
164683
|
-
|
|
164684
|
-
|
|
164685
|
-
|
|
164686
|
-
|
|
164687
|
-
|
|
164688
|
-
|
|
164689
|
-
|
|
164690
|
-
|
|
164691
|
-
|
|
164692
|
-
|
|
164693
|
-
|
|
164694
|
-
|
|
164695
|
-
|
|
164851
|
+
function isSaneLimit(limit) {
|
|
164852
|
+
return typeof limit === "number" && limit >= MIN_SANE_LIMIT && limit <= MAX_SANE_LIMIT;
|
|
164853
|
+
}
|
|
164854
|
+
function persistFilePath() {
|
|
164855
|
+
return join18(getMagicContextStorageDir(), `model-context-limits-${getHarness()}.json`);
|
|
164856
|
+
}
|
|
164857
|
+
function loadPersistedApiCacheOnce() {
|
|
164858
|
+
if (persistSeedLoaded || apiCache !== null)
|
|
164859
|
+
return;
|
|
164860
|
+
persistSeedLoaded = true;
|
|
164861
|
+
try {
|
|
164862
|
+
const raw = readFileSync12(persistFilePath(), "utf-8");
|
|
164863
|
+
const obj = JSON.parse(raw);
|
|
164864
|
+
const map2 = new Map;
|
|
164865
|
+
for (const [key, limit] of Object.entries(obj)) {
|
|
164866
|
+
if (isSaneLimit(limit))
|
|
164867
|
+
map2.set(key, { limit });
|
|
164868
|
+
}
|
|
164869
|
+
if (map2.size > 0) {
|
|
164870
|
+
apiCache = map2;
|
|
164871
|
+
sessionLog("global", `models-dev-cache: seeded ${map2.size} entries from persisted cache (cold start)`);
|
|
164872
|
+
}
|
|
164873
|
+
} catch {}
|
|
164874
|
+
}
|
|
164875
|
+
function persistApiCache() {
|
|
164876
|
+
if (!apiCache)
|
|
164877
|
+
return;
|
|
164878
|
+
const obj = {};
|
|
164879
|
+
for (const [key, value] of apiCache) {
|
|
164880
|
+
if (isSaneLimit(value.limit))
|
|
164881
|
+
obj[key] = value.limit;
|
|
164882
|
+
}
|
|
164883
|
+
try {
|
|
164884
|
+
const dir = getMagicContextStorageDir();
|
|
164885
|
+
mkdirSync5(dir, { recursive: true });
|
|
164886
|
+
const target = persistFilePath();
|
|
164887
|
+
const tmp = `${target}.${process.pid}.tmp`;
|
|
164888
|
+
writeFileSync4(tmp, JSON.stringify(obj), { encoding: "utf-8", mode: 384 });
|
|
164889
|
+
renameSync3(tmp, target);
|
|
164890
|
+
} catch {}
|
|
164696
164891
|
}
|
|
164697
164892
|
function resolveLimit(limit) {
|
|
164698
164893
|
if (!limit)
|
|
@@ -164705,7 +164900,7 @@ function resolveLimit(limit) {
|
|
|
164705
164900
|
}
|
|
164706
164901
|
function setCachedModelMetadata(cache, key, model) {
|
|
164707
164902
|
const limit = resolveLimit(model?.limit);
|
|
164708
|
-
if (limit
|
|
164903
|
+
if (!isSaneLimit(limit)) {
|
|
164709
164904
|
return;
|
|
164710
164905
|
}
|
|
164711
164906
|
const value = { limit };
|
|
@@ -164717,54 +164912,26 @@ function setCachedModelMetadata(cache, key, model) {
|
|
|
164717
164912
|
}
|
|
164718
164913
|
}
|
|
164719
164914
|
}
|
|
164720
|
-
function
|
|
164721
|
-
const
|
|
164722
|
-
const
|
|
164723
|
-
let
|
|
164724
|
-
|
|
164725
|
-
if (
|
|
164726
|
-
|
|
164727
|
-
|
|
164728
|
-
|
|
164729
|
-
for (const [providerId, provider2] of Object.entries(data)) {
|
|
164730
|
-
if (!provider2?.models || typeof provider2.models !== "object")
|
|
164731
|
-
continue;
|
|
164732
|
-
for (const [modelId, model] of Object.entries(provider2.models)) {
|
|
164733
|
-
setCachedModelMetadata(metadata, `${providerId}/${modelId}`, model);
|
|
164734
|
-
}
|
|
164735
|
-
}
|
|
164736
|
-
}
|
|
164737
|
-
} catch (error51) {
|
|
164738
|
-
sessionLog("global", `models-dev-cache: failed to read models.json at ${modelsJsonPath}:`, error51 instanceof Error ? error51.message : String(error51));
|
|
164739
|
-
}
|
|
164740
|
-
try {
|
|
164741
|
-
const configPath = getOpencodeConfigPath();
|
|
164742
|
-
if (configPath && existsSync14(configPath)) {
|
|
164743
|
-
const config2 = parseJsonc(readFileSync12(configPath, "utf-8"));
|
|
164744
|
-
if (config2.provider && typeof config2.provider === "object") {
|
|
164745
|
-
for (const [providerId, provider2] of Object.entries(config2.provider)) {
|
|
164746
|
-
if (!provider2?.models || typeof provider2.models !== "object")
|
|
164747
|
-
continue;
|
|
164748
|
-
for (const [modelId, model] of Object.entries(provider2.models)) {
|
|
164749
|
-
setCachedModelMetadata(metadata, `${providerId}/${modelId}`, model);
|
|
164750
|
-
}
|
|
164751
|
-
}
|
|
164752
|
-
}
|
|
164915
|
+
async function refreshModelLimitsFromApi(client, options) {
|
|
164916
|
+
const attempts = Math.max(1, (options?.retries ?? 0) + 1);
|
|
164917
|
+
const delayMs = options?.retryDelayMs ?? 1000;
|
|
164918
|
+
for (let attempt = 1;attempt <= attempts; attempt++) {
|
|
164919
|
+
const ok = await refreshModelLimitsOnce(client);
|
|
164920
|
+
if (ok)
|
|
164921
|
+
return;
|
|
164922
|
+
if (attempt < attempts) {
|
|
164923
|
+
await new Promise((resolve6) => setTimeout(resolve6, delayMs));
|
|
164753
164924
|
}
|
|
164754
|
-
} catch (error51) {
|
|
164755
|
-
sessionLog("global", "models-dev-cache: failed to read opencode config for custom models:", error51 instanceof Error ? error51.message : String(error51));
|
|
164756
164925
|
}
|
|
164757
|
-
sessionLog("global", `models-dev-cache: file-layer loaded ${metadata.size} model metadata entries (modelsJsonPath=${modelsJsonPath}, found=${fileFound})`);
|
|
164758
|
-
return metadata;
|
|
164759
164926
|
}
|
|
164760
|
-
async function
|
|
164927
|
+
async function refreshModelLimitsOnce(client) {
|
|
164761
164928
|
try {
|
|
164762
164929
|
const result = await client.config.providers();
|
|
164763
164930
|
const data = result.data;
|
|
164764
164931
|
const providers = data?.providers;
|
|
164765
|
-
if (!Array.isArray(providers)) {
|
|
164766
|
-
sessionLog("global", "models-dev-cache: API refresh returned no providers payload");
|
|
164767
|
-
return;
|
|
164932
|
+
if (!Array.isArray(providers) || providers.length === 0) {
|
|
164933
|
+
sessionLog("global", "models-dev-cache: API refresh returned no providers payload (will retry if attempts remain)");
|
|
164934
|
+
return false;
|
|
164768
164935
|
}
|
|
164769
164936
|
const map2 = new Map;
|
|
164770
164937
|
for (const entry of providers) {
|
|
@@ -164778,35 +164945,42 @@ async function refreshModelLimitsFromApi(client) {
|
|
|
164778
164945
|
const previousSize = apiCache?.size ?? null;
|
|
164779
164946
|
apiCache = map2;
|
|
164780
164947
|
apiLoadedAt = Date.now();
|
|
164948
|
+
persistApiCache();
|
|
164781
164949
|
if (previousSize === null) {
|
|
164782
164950
|
sessionLog("global", `models-dev-cache: API layer loaded ${map2.size} model metadata entries`);
|
|
164783
164951
|
} else if (previousSize !== map2.size) {
|
|
164784
164952
|
sessionLog("global", `models-dev-cache: API layer loaded ${map2.size} model metadata entries (was ${previousSize})`);
|
|
164785
164953
|
}
|
|
164954
|
+
return true;
|
|
164786
164955
|
} catch (error51) {
|
|
164787
164956
|
sessionLog("global", "models-dev-cache: API refresh failed:", error51 instanceof Error ? error51.message : String(error51));
|
|
164957
|
+
return false;
|
|
164788
164958
|
}
|
|
164789
164959
|
}
|
|
164790
|
-
function
|
|
164791
|
-
|
|
164792
|
-
|
|
164793
|
-
|
|
164794
|
-
|
|
164795
|
-
|
|
164796
|
-
|
|
164797
|
-
|
|
164798
|
-
|
|
164799
|
-
|
|
164800
|
-
|
|
164960
|
+
function getSdkContextLimit(providerID, modelID) {
|
|
164961
|
+
loadPersistedApiCacheOnce();
|
|
164962
|
+
const fromApi = lookupLimitWithTagFallback(apiCache, providerID, modelID);
|
|
164963
|
+
return isSaneLimit(fromApi) ? fromApi : undefined;
|
|
164964
|
+
}
|
|
164965
|
+
function lookupLimitWithTagFallback(cache, providerID, modelID) {
|
|
164966
|
+
if (!cache)
|
|
164967
|
+
return;
|
|
164968
|
+
const exact = cache.get(`${providerID}/${modelID}`)?.limit;
|
|
164969
|
+
if (typeof exact === "number")
|
|
164970
|
+
return exact;
|
|
164971
|
+
const colonIdx = modelID.lastIndexOf(":");
|
|
164972
|
+
if (colonIdx > 0) {
|
|
164973
|
+
const baseModel = modelID.slice(0, colonIdx);
|
|
164974
|
+
const fallback = cache.get(`${providerID}/${baseModel}`)?.limit;
|
|
164975
|
+
if (typeof fallback === "number")
|
|
164976
|
+
return fallback;
|
|
164801
164977
|
}
|
|
164802
|
-
return
|
|
164978
|
+
return;
|
|
164803
164979
|
}
|
|
164804
|
-
var
|
|
164980
|
+
var MIN_SANE_LIMIT = 20000, MAX_SANE_LIMIT = 3000000, apiCache = null, apiLoadedAt = 0, persistSeedLoaded = false;
|
|
164805
164981
|
var init_models_dev_cache = __esm(() => {
|
|
164806
164982
|
init_data_path();
|
|
164807
|
-
init_jsonc_parser();
|
|
164808
164983
|
init_logger();
|
|
164809
|
-
RELOAD_INTERVAL_MS = 5 * 60 * 1000;
|
|
164810
164984
|
});
|
|
164811
164985
|
|
|
164812
164986
|
// src/shared/rpc-notifications.ts
|
|
@@ -165532,7 +165706,7 @@ var init_compartment_runner_validation = __esm(async () => {
|
|
|
165532
165706
|
});
|
|
165533
165707
|
|
|
165534
165708
|
// src/hooks/magic-context/compartment-runner-historian.ts
|
|
165535
|
-
import { mkdirSync as
|
|
165709
|
+
import { mkdirSync as mkdirSync6, unlinkSync, writeFileSync as writeFileSync5 } from "node:fs";
|
|
165536
165710
|
import { join as join21 } from "node:path";
|
|
165537
165711
|
function historianResponseDumpDir(directory) {
|
|
165538
165712
|
return getProjectMagicContextHistorianDir(directory);
|
|
@@ -165835,11 +166009,11 @@ function cleanupHistorianDump(sessionId, dumpPath) {
|
|
|
165835
166009
|
function dumpHistorianResponse(sessionId, directory, label, text) {
|
|
165836
166010
|
try {
|
|
165837
166011
|
const dumpDir = historianResponseDumpDir(directory);
|
|
165838
|
-
|
|
166012
|
+
mkdirSync6(dumpDir, { recursive: true });
|
|
165839
166013
|
const safeSessionId = sanitizeDumpName(sessionId);
|
|
165840
166014
|
const safeLabel = sanitizeDumpName(label);
|
|
165841
166015
|
const dumpPath = join21(dumpDir, `${safeSessionId}-${safeLabel}-${Date.now()}.xml`);
|
|
165842
|
-
|
|
166016
|
+
writeFileSync5(dumpPath, text, "utf8");
|
|
165843
166017
|
sessionLog(sessionId, "compartment agent: historian response dumped", {
|
|
165844
166018
|
label,
|
|
165845
166019
|
dumpPath
|
|
@@ -165961,7 +166135,7 @@ function insertCompartmentEvents(db, sessionId, events, compartmentIds) {
|
|
|
165961
166135
|
var init_compartment_events = () => {};
|
|
165962
166136
|
|
|
165963
166137
|
// src/hooks/magic-context/historian-state-file.ts
|
|
165964
|
-
import { mkdirSync as
|
|
166138
|
+
import { mkdirSync as mkdirSync7, unlinkSync as unlinkSync2, writeFileSync as writeFileSync6 } from "node:fs";
|
|
165965
166139
|
function cleanupHistorianStateFile(path6) {
|
|
165966
166140
|
if (!path6)
|
|
165967
166141
|
return;
|
|
@@ -170797,7 +170971,7 @@ function resolveHistorianContextLimit(historianModelOverride) {
|
|
|
170797
170971
|
const [providerID, ...rest] = historianModelOverride.split("/");
|
|
170798
170972
|
const modelID = rest.join("/");
|
|
170799
170973
|
if (providerID && modelID) {
|
|
170800
|
-
const limit =
|
|
170974
|
+
const limit = getSdkContextLimit(providerID, modelID);
|
|
170801
170975
|
if (typeof limit === "number" && limit > 0)
|
|
170802
170976
|
return limit;
|
|
170803
170977
|
}
|
|
@@ -170816,7 +170990,7 @@ function resolveHistorianContextLimit(historianModelOverride) {
|
|
|
170816
170990
|
const modelID = rest.join("/");
|
|
170817
170991
|
if (!providerID || !modelID)
|
|
170818
170992
|
continue;
|
|
170819
|
-
const limit =
|
|
170993
|
+
const limit = getSdkContextLimit(providerID, modelID);
|
|
170820
170994
|
if (typeof limit !== "number" || limit <= 0)
|
|
170821
170995
|
continue;
|
|
170822
170996
|
if (minLimit === undefined || limit < minLimit)
|
|
@@ -171183,6 +171357,7 @@ __export(exports_recomp_orchestrator, {
|
|
|
171183
171357
|
setRecompNote: () => setRecompNote,
|
|
171184
171358
|
runManagedUpgrade: () => runManagedUpgrade,
|
|
171185
171359
|
runManagedRecomp: () => runManagedRecomp,
|
|
171360
|
+
isRecompSkip: () => isRecompSkip,
|
|
171186
171361
|
isRecompFailure: () => isRecompFailure,
|
|
171187
171362
|
isRecompComplete: () => isRecompComplete,
|
|
171188
171363
|
extractRecompReason: () => extractRecompReason,
|
|
@@ -171195,6 +171370,9 @@ function resolveLiveModelKey(liveSessionState, sessionId) {
|
|
|
171195
171370
|
function isRecompFailure(message) {
|
|
171196
171371
|
return /—\s*(Failed|Skipped)/.test(message);
|
|
171197
171372
|
}
|
|
171373
|
+
function isRecompSkip(message) {
|
|
171374
|
+
return /—\s*Skipped|already mutating compartment state|already running/i.test(message);
|
|
171375
|
+
}
|
|
171198
171376
|
function isRecompComplete(message) {
|
|
171199
171377
|
return /—\s*Complete/.test(message);
|
|
171200
171378
|
}
|
|
@@ -171210,9 +171388,10 @@ function contextualizeUpgradeReason(reason) {
|
|
|
171210
171388
|
}
|
|
171211
171389
|
return rewritten;
|
|
171212
171390
|
}
|
|
171213
|
-
function setRecompStarting(liveSessionState, sessionId, note) {
|
|
171391
|
+
function setRecompStarting(liveSessionState, sessionId, note, kind = "recomp") {
|
|
171214
171392
|
liveSessionState.recompProgressBySession.set(sessionId, {
|
|
171215
171393
|
sessionId,
|
|
171394
|
+
kind,
|
|
171216
171395
|
phase: "recomp",
|
|
171217
171396
|
processedMessages: 0,
|
|
171218
171397
|
totalMessages: 0,
|
|
@@ -171237,6 +171416,7 @@ function setRecompTerminal(liveSessionState, sessionId, phase, message) {
|
|
|
171237
171416
|
const existing = liveSessionState.recompProgressBySession.get(sessionId);
|
|
171238
171417
|
liveSessionState.recompProgressBySession.set(sessionId, {
|
|
171239
171418
|
sessionId,
|
|
171419
|
+
kind: existing?.kind ?? "recomp",
|
|
171240
171420
|
phase,
|
|
171241
171421
|
processedMessages: existing?.processedMessages ?? 0,
|
|
171242
171422
|
totalMessages: existing?.totalMessages ?? 0,
|
|
@@ -171246,10 +171426,10 @@ function setRecompTerminal(liveSessionState, sessionId, phase, message) {
|
|
|
171246
171426
|
updatedAt: Date.now(),
|
|
171247
171427
|
message
|
|
171248
171428
|
});
|
|
171249
|
-
if (phase === "done") {
|
|
171429
|
+
if (phase === "done" || phase === "skipped") {
|
|
171250
171430
|
const t = setTimeout(() => {
|
|
171251
171431
|
const cur = liveSessionState.recompProgressBySession.get(sessionId);
|
|
171252
|
-
if (cur?.phase ===
|
|
171432
|
+
if (cur?.phase === phase)
|
|
171253
171433
|
liveSessionState.recompProgressBySession.delete(sessionId);
|
|
171254
171434
|
}, RECOMP_DONE_GRACE_MS);
|
|
171255
171435
|
t.unref?.();
|
|
@@ -171278,7 +171458,11 @@ function buildRecompDeps(ctx, sessionId) {
|
|
|
171278
171458
|
ctx.liveSessionState.deferredHistoryRefreshSessions.add(sid);
|
|
171279
171459
|
},
|
|
171280
171460
|
onRecompProgress: (p) => {
|
|
171281
|
-
ctx.liveSessionState.recompProgressBySession.
|
|
171461
|
+
const prevKind = ctx.liveSessionState.recompProgressBySession.get(sessionId)?.kind ?? "recomp";
|
|
171462
|
+
ctx.liveSessionState.recompProgressBySession.set(sessionId, {
|
|
171463
|
+
...p,
|
|
171464
|
+
kind: p.kind ?? prevKind
|
|
171465
|
+
});
|
|
171282
171466
|
}
|
|
171283
171467
|
};
|
|
171284
171468
|
}
|
|
@@ -171297,10 +171481,11 @@ async function resolveSessionDirectory(ctx, sessionId) {
|
|
|
171297
171481
|
return ctx.directory;
|
|
171298
171482
|
}
|
|
171299
171483
|
async function runManagedRecomp(ctx, sessionId, options) {
|
|
171300
|
-
setRecompStarting(ctx.liveSessionState, sessionId, "Starting recomp…");
|
|
171484
|
+
setRecompStarting(ctx.liveSessionState, sessionId, "Starting recomp…", "recomp");
|
|
171301
171485
|
try {
|
|
171302
171486
|
const message = await executeContextRecomp(buildRecompDeps(ctx, sessionId), options);
|
|
171303
|
-
|
|
171487
|
+
const terminalPhase = isRecompSkip(message) ? "skipped" : isRecompFailure(message) ? "failed" : "done";
|
|
171488
|
+
setRecompTerminal(ctx.liveSessionState, sessionId, terminalPhase, extractRecompReason(message));
|
|
171304
171489
|
return message;
|
|
171305
171490
|
} catch (error51) {
|
|
171306
171491
|
setRecompTerminal(ctx.liveSessionState, sessionId, "failed", `Recomp crashed: ${String(error51)}`);
|
|
@@ -171310,7 +171495,7 @@ Recomp crashed: ${String(error51)}`;
|
|
|
171310
171495
|
}
|
|
171311
171496
|
}
|
|
171312
171497
|
async function runManagedUpgrade(ctx, sessionId) {
|
|
171313
|
-
setRecompStarting(ctx.liveSessionState, sessionId, "Starting upgrade…");
|
|
171498
|
+
setRecompStarting(ctx.liveSessionState, sessionId, "Starting upgrade…", "upgrade");
|
|
171314
171499
|
try {
|
|
171315
171500
|
const compartments = getCompartments(ctx.db, sessionId);
|
|
171316
171501
|
const legacyCount = compartments.filter((c) => c.legacy === 1 || !c.p1 || c.p1.trim() === "").length;
|
|
@@ -171368,6 +171553,7 @@ async function runUpgradeMemoryMigration(ctx, sessionId, migrationDirectory) {
|
|
|
171368
171553
|
const prev = ctx.liveSessionState.recompProgressBySession.get(sessionId);
|
|
171369
171554
|
ctx.liveSessionState.recompProgressBySession.set(sessionId, {
|
|
171370
171555
|
sessionId,
|
|
171556
|
+
kind: prev?.kind ?? "upgrade",
|
|
171371
171557
|
phase: "migration",
|
|
171372
171558
|
processedMessages: prev?.processedMessages ?? 0,
|
|
171373
171559
|
totalMessages: prev?.totalMessages ?? 0,
|
|
@@ -171440,7 +171626,12 @@ function markAnnouncementSeen(version2) {
|
|
|
171440
171626
|
function shouldShowAnnouncement() {
|
|
171441
171627
|
if (!ANNOUNCEMENT_VERSION || ANNOUNCEMENT_FEATURES.length === 0)
|
|
171442
171628
|
return false;
|
|
171443
|
-
|
|
171629
|
+
const lastVersion = readLastAnnouncedVersion();
|
|
171630
|
+
if (!lastVersion) {
|
|
171631
|
+
markAnnouncementSeen(ANNOUNCEMENT_VERSION);
|
|
171632
|
+
return false;
|
|
171633
|
+
}
|
|
171634
|
+
return lastVersion !== ANNOUNCEMENT_VERSION;
|
|
171444
171635
|
}
|
|
171445
171636
|
var ANNOUNCEMENT_VERSION = "0.22.0", ANNOUNCEMENT_FEATURES, ANNOUNCEMENT_FOOTER = "Join us on Discord: https://discord.gg/F2uWxjGnU", STATE_FILENAME = "last_announced_version";
|
|
171446
171637
|
var init_announcement = __esm(() => {
|
|
@@ -171459,7 +171650,7 @@ var exports_tui_config = {};
|
|
|
171459
171650
|
__export(exports_tui_config, {
|
|
171460
171651
|
ensureTuiPluginEntry: () => ensureTuiPluginEntry
|
|
171461
171652
|
});
|
|
171462
|
-
import { existsSync as
|
|
171653
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync10, readFileSync as readFileSync16, writeFileSync as writeFileSync9 } from "node:fs";
|
|
171463
171654
|
import { dirname as dirname9, join as join25 } from "node:path";
|
|
171464
171655
|
function isMagicContextEntry(entry) {
|
|
171465
171656
|
if (!entry)
|
|
@@ -171476,9 +171667,9 @@ function resolveTuiConfigPath() {
|
|
|
171476
171667
|
const configDir = getOpenCodeConfigPaths({ binary: "opencode" }).configDir;
|
|
171477
171668
|
const jsoncPath = join25(configDir, "tui.jsonc");
|
|
171478
171669
|
const jsonPath = join25(configDir, "tui.json");
|
|
171479
|
-
if (
|
|
171670
|
+
if (existsSync15(jsoncPath))
|
|
171480
171671
|
return jsoncPath;
|
|
171481
|
-
if (
|
|
171672
|
+
if (existsSync15(jsonPath))
|
|
171482
171673
|
return jsonPath;
|
|
171483
171674
|
return jsonPath;
|
|
171484
171675
|
}
|
|
@@ -171486,7 +171677,7 @@ function ensureTuiPluginEntry() {
|
|
|
171486
171677
|
try {
|
|
171487
171678
|
const configPath = resolveTuiConfigPath();
|
|
171488
171679
|
let config2 = {};
|
|
171489
|
-
if (
|
|
171680
|
+
if (existsSync15(configPath)) {
|
|
171490
171681
|
const raw = readFileSync16(configPath, "utf-8");
|
|
171491
171682
|
config2 = import_comment_json4.parse(raw) ?? {};
|
|
171492
171683
|
}
|
|
@@ -171506,8 +171697,8 @@ function ensureTuiPluginEntry() {
|
|
|
171506
171697
|
plugins.push(PLUGIN_ENTRY);
|
|
171507
171698
|
}
|
|
171508
171699
|
config2.plugin = plugins;
|
|
171509
|
-
|
|
171510
|
-
|
|
171700
|
+
mkdirSync10(dirname9(configPath), { recursive: true });
|
|
171701
|
+
writeFileSync9(configPath, `${import_comment_json4.stringify(config2, null, 2)}
|
|
171511
171702
|
`);
|
|
171512
171703
|
log(`[magic-context] updated TUI plugin entry in ${configPath}`);
|
|
171513
171704
|
return true;
|
|
@@ -171905,10 +172096,19 @@ function parsePluginConfig(rawConfig, recoveredTopLevelKeys = []) {
|
|
|
171905
172096
|
const defaults = MagicContextConfigSchema.parse({});
|
|
171906
172097
|
const warnings = [];
|
|
171907
172098
|
const errorPaths = new Set;
|
|
172099
|
+
const customMessagesByKey = new Map;
|
|
172100
|
+
const GENERIC_ZOD_PREFIXES = ["Too big", "Too small", "Invalid input", "Invalid", "Expected"];
|
|
171908
172101
|
for (const issue2 of parsed.error.issues) {
|
|
171909
172102
|
const topKey = issue2.path[0];
|
|
171910
172103
|
if (topKey !== undefined) {
|
|
171911
|
-
|
|
172104
|
+
const key = String(topKey);
|
|
172105
|
+
errorPaths.add(key);
|
|
172106
|
+
const msg = issue2.message;
|
|
172107
|
+
if (msg && !GENERIC_ZOD_PREFIXES.some((p) => msg.startsWith(p))) {
|
|
172108
|
+
if (!customMessagesByKey.has(key)) {
|
|
172109
|
+
customMessagesByKey.set(key, msg);
|
|
172110
|
+
}
|
|
172111
|
+
}
|
|
171912
172112
|
}
|
|
171913
172113
|
}
|
|
171914
172114
|
const patched = { ...rawConfig };
|
|
@@ -171921,7 +172121,8 @@ function parsePluginConfig(rawConfig, recoveredTopLevelKeys = []) {
|
|
|
171921
172121
|
} else {
|
|
171922
172122
|
delete patched[key];
|
|
171923
172123
|
const defaultVal = defaults[key];
|
|
171924
|
-
|
|
172124
|
+
const reason = customMessagesByKey.get(key);
|
|
172125
|
+
warnings.push(`"${key}": invalid value (${redactConfigValue(rawConfig[key])}), using default ${JSON.stringify(defaultVal)}.${reason ? ` ${reason}` : ""}`);
|
|
171925
172126
|
}
|
|
171926
172127
|
}
|
|
171927
172128
|
const retryMigrated = migrateLegacyExperimental(patched, preMigrationWarnings);
|
|
@@ -173154,9 +173355,18 @@ function removeInstalledPackage(installDir, packageName) {
|
|
|
173154
173355
|
const packageDir = join9(installDir, "node_modules", packageName);
|
|
173155
173356
|
if (!existsSync9(packageDir))
|
|
173156
173357
|
return false;
|
|
173157
|
-
|
|
173158
|
-
|
|
173159
|
-
|
|
173358
|
+
try {
|
|
173359
|
+
rmSync(packageDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
|
173360
|
+
log(`[auto-update-checker] Package removed: ${packageDir}`);
|
|
173361
|
+
return true;
|
|
173362
|
+
} catch (err) {
|
|
173363
|
+
const code = err.code;
|
|
173364
|
+
if (code === "EBUSY" || code === "EPERM" || code === "ENOTEMPTY") {
|
|
173365
|
+
warn2(`[auto-update-checker] Could not remove ${packageDir} (${code}); the file is locked by the running process. Continuing — npm install will overwrite it in place.`);
|
|
173366
|
+
return false;
|
|
173367
|
+
}
|
|
173368
|
+
throw err;
|
|
173369
|
+
}
|
|
173160
173370
|
}
|
|
173161
173371
|
function resolveInstallContext(runtimePackageJsonPath = getCurrentRuntimePackageJsonPath()) {
|
|
173162
173372
|
if (runtimePackageJsonPath) {
|
|
@@ -175235,6 +175445,7 @@ var insertStatements = new WeakMap;
|
|
|
175235
175445
|
var existingShasStatements = new WeakMap;
|
|
175236
175446
|
var projectCountStatements = new WeakMap;
|
|
175237
175447
|
var evictStatements = new WeakMap;
|
|
175448
|
+
var evictOverflowStatements = new WeakMap;
|
|
175238
175449
|
var latestCommitTimeStatements = new WeakMap;
|
|
175239
175450
|
function getInsertStatement(db) {
|
|
175240
175451
|
let stmt = insertStatements.get(db);
|
|
@@ -175277,17 +175488,17 @@ function getLatestCommitTimeStatement(db) {
|
|
|
175277
175488
|
}
|
|
175278
175489
|
return stmt;
|
|
175279
175490
|
}
|
|
175280
|
-
function
|
|
175281
|
-
let stmt =
|
|
175491
|
+
function getEvictOverflowStatement(db) {
|
|
175492
|
+
let stmt = evictOverflowStatements.get(db);
|
|
175282
175493
|
if (!stmt) {
|
|
175283
175494
|
stmt = db.prepare(`DELETE FROM git_commits
|
|
175284
|
-
WHERE
|
|
175285
|
-
SELECT
|
|
175495
|
+
WHERE rowid IN (
|
|
175496
|
+
SELECT rowid FROM git_commits
|
|
175286
175497
|
WHERE project_path = ?
|
|
175287
|
-
ORDER BY committed_at
|
|
175288
|
-
LIMIT ?
|
|
175498
|
+
ORDER BY committed_at DESC, sha DESC
|
|
175499
|
+
LIMIT -1 OFFSET ?
|
|
175289
175500
|
)`);
|
|
175290
|
-
|
|
175501
|
+
evictOverflowStatements.set(db, stmt);
|
|
175291
175502
|
}
|
|
175292
175503
|
return stmt;
|
|
175293
175504
|
}
|
|
@@ -175325,22 +175536,15 @@ function getLatestIndexedCommitTimeMs(db, projectPath) {
|
|
|
175325
175536
|
const row = getLatestCommitTimeStatement(db).get(projectPath);
|
|
175326
175537
|
return row?.latest ?? null;
|
|
175327
175538
|
}
|
|
175328
|
-
function evictOldestCommits(db, projectPath, excess) {
|
|
175329
|
-
if (excess <= 0)
|
|
175330
|
-
return 0;
|
|
175331
|
-
const before = getCommitCount(db, projectPath);
|
|
175332
|
-
getEvictStatement(db).run(projectPath, excess);
|
|
175333
|
-
const after = getCommitCount(db, projectPath);
|
|
175334
|
-
return Math.max(0, before - after);
|
|
175335
|
-
}
|
|
175336
175539
|
function enforceProjectCap(db, projectPath, maxCommits) {
|
|
175337
175540
|
if (maxCommits <= 0)
|
|
175338
175541
|
return 0;
|
|
175339
175542
|
const count = getCommitCount(db, projectPath);
|
|
175340
175543
|
if (count <= maxCommits)
|
|
175341
175544
|
return 0;
|
|
175342
|
-
|
|
175343
|
-
const
|
|
175545
|
+
getEvictOverflowStatement(db).run(projectPath, maxCommits);
|
|
175546
|
+
const after = getCommitCount(db, projectPath);
|
|
175547
|
+
const evicted = Math.max(0, count - after);
|
|
175344
175548
|
if (evicted > 0) {
|
|
175345
175549
|
log(`[git-commits] evicted ${evicted} oldest commits for project ${projectPath} (cap=${maxCommits}, was=${count})`);
|
|
175346
175550
|
}
|
|
@@ -175584,6 +175788,7 @@ function searchGitCommitsSync(db, projectPath, query, options) {
|
|
|
175584
175788
|
|
|
175585
175789
|
// src/features/magic-context/git-commits/index.ts
|
|
175586
175790
|
init_storage_git_commit_embeddings();
|
|
175791
|
+
init_sweep_coordinator();
|
|
175587
175792
|
|
|
175588
175793
|
// src/plugin/dream-timer.ts
|
|
175589
175794
|
init_embedding();
|
|
@@ -175697,10 +175902,31 @@ async function sweepProject(reg, origin, db, gitCommitEnabled = getProjectEmbedd
|
|
|
175697
175902
|
log(`[dreamer] timer-triggered queue processing failed for ${reg.directory}:`, error51);
|
|
175698
175903
|
}
|
|
175699
175904
|
}
|
|
175905
|
+
function startGitSweepLeaseRenewal(db, projectIdentity, holderId) {
|
|
175906
|
+
const timer = setInterval(() => {
|
|
175907
|
+
try {
|
|
175908
|
+
if (!renewGitSweepLease(db, projectIdentity, holderId)) {
|
|
175909
|
+
log(`[git-commits] sweep lease renewal failed for ${projectIdentity}`);
|
|
175910
|
+
}
|
|
175911
|
+
} catch (error51) {
|
|
175912
|
+
log(`[git-commits] sweep lease renewal errored for ${projectIdentity}: ${error51 instanceof Error ? error51.message : String(error51)}`);
|
|
175913
|
+
}
|
|
175914
|
+
}, GIT_SWEEP_LEASE_RENEWAL_MS);
|
|
175915
|
+
timer.unref?.();
|
|
175916
|
+
return () => clearInterval(timer);
|
|
175917
|
+
}
|
|
175700
175918
|
async function sweepGitCommits(args) {
|
|
175701
175919
|
const { directory, projectIdentity, db, gitCommitIndexing } = args;
|
|
175920
|
+
const holderId = crypto.randomUUID();
|
|
175921
|
+
const lease2 = acquireGitSweepLease(db, projectIdentity, holderId);
|
|
175922
|
+
if (!lease2.acquired) {
|
|
175923
|
+
const reason = lease2.reason === "cooldown_active" ? `cooldown active until ${lease2.nextAllowedAt}` : `lease held by ${lease2.leaseHolder ?? "another holder"} until ${lease2.leaseExpiresAt ?? "unknown"}`;
|
|
175924
|
+
log(`[git-commits] sweep skipped for ${projectIdentity} (${directory}): ${reason}`);
|
|
175925
|
+
return;
|
|
175926
|
+
}
|
|
175702
175927
|
const startedAt = Date.now();
|
|
175703
|
-
|
|
175928
|
+
const stopRenewal = startGitSweepLeaseRenewal(db, projectIdentity, holderId);
|
|
175929
|
+
log(`[git-commits] sweep starting for ${directory} (project=${projectIdentity} sinceDays=${gitCommitIndexing.since_days} maxCommits=${gitCommitIndexing.max_commits})`);
|
|
175704
175930
|
try {
|
|
175705
175931
|
const result = await indexCommitsForProject(db, projectIdentity, directory, {
|
|
175706
175932
|
sinceDays: gitCommitIndexing.since_days,
|
|
@@ -175710,11 +175936,19 @@ async function sweepGitCommits(args) {
|
|
|
175710
175936
|
if (result.embedded > 0) {
|
|
175711
175937
|
drainedEmbeddings = await embedUnembeddedCommits(db, projectIdentity);
|
|
175712
175938
|
}
|
|
175939
|
+
const cooldownMarked = markGitSweepSuccessAndRelease(db, projectIdentity, holderId);
|
|
175940
|
+
if (!cooldownMarked) {
|
|
175941
|
+
releaseGitSweepLease(db, projectIdentity, holderId);
|
|
175942
|
+
log(`[git-commits] sweep finished for ${projectIdentity}, but lease was no longer active; cooldown not advanced`);
|
|
175943
|
+
}
|
|
175713
175944
|
const elapsedMs = Date.now() - startedAt;
|
|
175714
175945
|
log(`[git-commits] sweep finished for ${projectIdentity} in ${elapsedMs}ms: scanned=${result.scanned} inserted=${result.inserted} updated=${result.updated} evicted=${result.evicted} embedded=${result.embedded} drained=${drainedEmbeddings}`);
|
|
175715
175946
|
} catch (error51) {
|
|
175947
|
+
releaseGitSweepLease(db, projectIdentity, holderId);
|
|
175716
175948
|
const elapsedMs = Date.now() - startedAt;
|
|
175717
175949
|
log(`[git-commits] sweep failed for ${directory} after ${elapsedMs}ms: ${error51 instanceof Error ? error51.message : String(error51)}`);
|
|
175950
|
+
} finally {
|
|
175951
|
+
stopRenewal();
|
|
175718
175952
|
}
|
|
175719
175953
|
}
|
|
175720
175954
|
|
|
@@ -175821,6 +176055,14 @@ function createEventHandler(args) {
|
|
|
175821
176055
|
return async (input) => {
|
|
175822
176056
|
await args.autoUpdateChecker?.(input);
|
|
175823
176057
|
await args.magicContext?.event?.(input);
|
|
176058
|
+
if (args.onInstanceDisposed && input.event?.type === "server.instance.disposed") {
|
|
176059
|
+
const directory = input.event.properties?.directory;
|
|
176060
|
+
if (typeof directory === "string") {
|
|
176061
|
+
try {
|
|
176062
|
+
await args.onInstanceDisposed(directory);
|
|
176063
|
+
} catch {}
|
|
176064
|
+
}
|
|
176065
|
+
}
|
|
175824
176066
|
};
|
|
175825
176067
|
}
|
|
175826
176068
|
|
|
@@ -175847,7 +176089,7 @@ init_models_dev_cache();
|
|
|
175847
176089
|
var DEFAULT_CONTEXT_LIMIT = 128000;
|
|
175848
176090
|
var MAX_EXECUTE_THRESHOLD = 80;
|
|
175849
176091
|
function resolveContextLimit(providerID, modelID, ctx) {
|
|
175850
|
-
const fromModelsDev = providerID && modelID ?
|
|
176092
|
+
const fromModelsDev = providerID && modelID ? getSdkContextLimit(providerID, modelID) : undefined;
|
|
175851
176093
|
const baseline = fromModelsDev ?? DEFAULT_CONTEXT_LIMIT;
|
|
175852
176094
|
if (ctx?.db && ctx.sessionID) {
|
|
175853
176095
|
try {
|
|
@@ -175860,7 +176102,7 @@ function resolveContextLimit(providerID, modelID, ctx) {
|
|
|
175860
176102
|
return baseline;
|
|
175861
176103
|
}
|
|
175862
176104
|
function resolveTrustedContextLimit(providerID, modelID, ctx) {
|
|
175863
|
-
const fromModelsDev = providerID && modelID ?
|
|
176105
|
+
const fromModelsDev = providerID && modelID ? getSdkContextLimit(providerID, modelID) : undefined;
|
|
175864
176106
|
let detected;
|
|
175865
176107
|
if (ctx?.db && ctx.sessionID) {
|
|
175866
176108
|
try {
|
|
@@ -175966,8 +176208,21 @@ function resolveExecuteThresholdDetail(config2, modelKey, fallback, options) {
|
|
|
175966
176208
|
if (!Number.isFinite(resolved) || resolved < 0) {
|
|
175967
176209
|
resolved = fallback;
|
|
175968
176210
|
}
|
|
176211
|
+
const cappedPercentage = Math.min(resolved, MAX_EXECUTE_THRESHOLD);
|
|
176212
|
+
if (cappedPercentage < resolved) {
|
|
176213
|
+
const dedupeKey = `pct|${options?.sessionId ?? "__global__"}|${modelKey ?? "__default__"}|${resolved}`;
|
|
176214
|
+
if (!clampWarnSeen.has(dedupeKey)) {
|
|
176215
|
+
clampWarnSeen.add(dedupeKey);
|
|
176216
|
+
const msg = `execute_threshold clamped ${resolved}% → ${MAX_EXECUTE_THRESHOLD}% for ${modelKey ?? "default"} (capped for cache safety; a large step can overflow before compaction, and 80% stays below the 85%/95% emergency bands)`;
|
|
176217
|
+
if (options?.sessionId) {
|
|
176218
|
+
sessionLog(options.sessionId, `WARN: ${msg}`);
|
|
176219
|
+
} else {
|
|
176220
|
+
log(`[magic-context] WARN: ${msg}`);
|
|
176221
|
+
}
|
|
176222
|
+
}
|
|
176223
|
+
}
|
|
175969
176224
|
return {
|
|
175970
|
-
percentage:
|
|
176225
|
+
percentage: cappedPercentage,
|
|
175971
176226
|
mode: "percentage",
|
|
175972
176227
|
matchedKey
|
|
175973
176228
|
};
|
|
@@ -179272,6 +179527,64 @@ init_embedding();
|
|
|
179272
179527
|
|
|
179273
179528
|
// src/features/magic-context/search.ts
|
|
179274
179529
|
init_logger();
|
|
179530
|
+
|
|
179531
|
+
// src/features/magic-context/literal-probes.ts
|
|
179532
|
+
var MAX_PROBES = 5;
|
|
179533
|
+
var MIN_PROBE_LENGTH = 3;
|
|
179534
|
+
var SLASH_COMMAND_RE = /\/[a-z][a-z0-9]*(?:-[a-z0-9]+)+/gi;
|
|
179535
|
+
var KEBAB_SNAKE_RE = /[a-z][a-z0-9]*(?:[-_][a-z0-9]+)+/gi;
|
|
179536
|
+
var DOTTED_RE = /[a-z0-9][a-z0-9_-]*(?:\.[a-z0-9_-]+)+/gi;
|
|
179537
|
+
var CAMEL_RE = /\b[a-zA-Z][a-z0-9]*(?:[A-Z][a-z0-9]*)+\b/g;
|
|
179538
|
+
var SHA_RE = /\b[0-9a-f]{7,40}\b/gi;
|
|
179539
|
+
var ERROR_CODE_RE = /\b(?:TS\d{4,}|ERR_[A-Z][A-Z0-9_]*)\b/g;
|
|
179540
|
+
var QUOTED_RE = /["`]([^"`]{3,80})["`]/g;
|
|
179541
|
+
function looksLikeSha(token) {
|
|
179542
|
+
return /[0-9]/.test(token) && /^[0-9a-f]{7,40}$/i.test(token);
|
|
179543
|
+
}
|
|
179544
|
+
function extractLiteralProbes(query) {
|
|
179545
|
+
const trimmed = query.trim();
|
|
179546
|
+
if (trimmed.length === 0)
|
|
179547
|
+
return [];
|
|
179548
|
+
const ordered = [];
|
|
179549
|
+
const seen = new Set;
|
|
179550
|
+
const add = (raw) => {
|
|
179551
|
+
if (!raw)
|
|
179552
|
+
return;
|
|
179553
|
+
const probe = raw.trim();
|
|
179554
|
+
if (probe.length < MIN_PROBE_LENGTH)
|
|
179555
|
+
return;
|
|
179556
|
+
const key = probe.toLowerCase();
|
|
179557
|
+
if (seen.has(key))
|
|
179558
|
+
return;
|
|
179559
|
+
seen.add(key);
|
|
179560
|
+
ordered.push(probe);
|
|
179561
|
+
};
|
|
179562
|
+
for (const m of trimmed.matchAll(QUOTED_RE))
|
|
179563
|
+
add(m[1]);
|
|
179564
|
+
for (const m of trimmed.matchAll(SLASH_COMMAND_RE))
|
|
179565
|
+
add(m[0]);
|
|
179566
|
+
for (const m of trimmed.matchAll(ERROR_CODE_RE))
|
|
179567
|
+
add(m[0]);
|
|
179568
|
+
for (const m of trimmed.matchAll(DOTTED_RE))
|
|
179569
|
+
add(m[0]);
|
|
179570
|
+
for (const m of trimmed.matchAll(KEBAB_SNAKE_RE))
|
|
179571
|
+
add(m[0]);
|
|
179572
|
+
for (const m of trimmed.matchAll(CAMEL_RE))
|
|
179573
|
+
add(m[0]);
|
|
179574
|
+
for (const m of trimmed.matchAll(SHA_RE)) {
|
|
179575
|
+
if (looksLikeSha(m[0]))
|
|
179576
|
+
add(m[0]);
|
|
179577
|
+
}
|
|
179578
|
+
return ordered.slice(0, MAX_PROBES);
|
|
179579
|
+
}
|
|
179580
|
+
function containsProbeVerbatim(text, probes) {
|
|
179581
|
+
if (probes.length === 0)
|
|
179582
|
+
return false;
|
|
179583
|
+
const haystack = text.toLowerCase();
|
|
179584
|
+
return probes.some((probe) => haystack.includes(probe.toLowerCase()));
|
|
179585
|
+
}
|
|
179586
|
+
|
|
179587
|
+
// src/features/magic-context/search.ts
|
|
179275
179588
|
init_memory();
|
|
179276
179589
|
init_embedding();
|
|
179277
179590
|
init_storage_memory_fts();
|
|
@@ -179451,36 +179764,82 @@ function linearDecayScore(rank, total) {
|
|
|
179451
179764
|
return 0;
|
|
179452
179765
|
return Math.max(0, 1 - rank / total);
|
|
179453
179766
|
}
|
|
179454
|
-
function
|
|
179455
|
-
|
|
179456
|
-
if (sanitizedQuery.length === 0) {
|
|
179767
|
+
function runMessageFtsQuery(db, sessionId, ftsQuery, fetchLimit, cutoff) {
|
|
179768
|
+
if (ftsQuery.length === 0)
|
|
179457
179769
|
return [];
|
|
179458
|
-
|
|
179459
|
-
const
|
|
179460
|
-
|
|
179461
|
-
const cutoff = args.maxOrdinal != null && args.maxOrdinal >= 0 ? args.maxOrdinal : null;
|
|
179462
|
-
const filtered = rows.map((row) => {
|
|
179770
|
+
const rows = getMessageSearchStatement(db).all(sessionId, ftsQuery, fetchLimit).map((row) => row);
|
|
179771
|
+
const result = [];
|
|
179772
|
+
for (const row of rows) {
|
|
179463
179773
|
const messageOrdinal = getMessageOrdinal(row.messageOrdinal);
|
|
179464
179774
|
if (messageOrdinal === null || typeof row.messageId !== "string" || typeof row.role !== "string" || typeof row.content !== "string") {
|
|
179465
|
-
|
|
179775
|
+
continue;
|
|
179466
179776
|
}
|
|
179467
179777
|
if (cutoff !== null && messageOrdinal > cutoff) {
|
|
179468
|
-
|
|
179778
|
+
continue;
|
|
179469
179779
|
}
|
|
179470
|
-
|
|
179780
|
+
result.push({
|
|
179471
179781
|
messageOrdinal,
|
|
179472
179782
|
messageId: row.messageId,
|
|
179473
179783
|
role: row.role,
|
|
179474
179784
|
content: row.content
|
|
179475
|
-
};
|
|
179476
|
-
}
|
|
179477
|
-
return
|
|
179785
|
+
});
|
|
179786
|
+
}
|
|
179787
|
+
return result;
|
|
179788
|
+
}
|
|
179789
|
+
var RRF_K = 60;
|
|
179790
|
+
var VERBATIM_PROBE_BONUS = 0.5;
|
|
179791
|
+
function searchMessages(args) {
|
|
179792
|
+
const cutoff = args.maxOrdinal != null && args.maxOrdinal >= 0 ? args.maxOrdinal : null;
|
|
179793
|
+
const fetchLimit = args.maxOrdinal != null && args.maxOrdinal >= 0 ? args.limit * 3 : args.limit;
|
|
179794
|
+
const baseQuery = sanitizeFtsQuery(args.query.trim());
|
|
179795
|
+
const probes = args.probes ?? [];
|
|
179796
|
+
if (probes.length === 0) {
|
|
179797
|
+
const filtered = runMessageFtsQuery(args.db, args.sessionId, baseQuery, fetchLimit, cutoff).slice(0, args.limit);
|
|
179798
|
+
return filtered.map((row, rank) => ({
|
|
179799
|
+
source: "message",
|
|
179800
|
+
content: previewText(row.content),
|
|
179801
|
+
score: linearDecayScore(rank, filtered.length),
|
|
179802
|
+
messageOrdinal: row.messageOrdinal,
|
|
179803
|
+
messageId: row.messageId,
|
|
179804
|
+
role: row.role
|
|
179805
|
+
}));
|
|
179806
|
+
}
|
|
179807
|
+
const queryLists = [];
|
|
179808
|
+
if (baseQuery.length > 0) {
|
|
179809
|
+
queryLists.push(runMessageFtsQuery(args.db, args.sessionId, baseQuery, fetchLimit, cutoff));
|
|
179810
|
+
}
|
|
179811
|
+
for (const probe of probes) {
|
|
179812
|
+
const probeQuery = sanitizeFtsQuery(probe);
|
|
179813
|
+
if (probeQuery.length === 0)
|
|
179814
|
+
continue;
|
|
179815
|
+
queryLists.push(runMessageFtsQuery(args.db, args.sessionId, probeQuery, fetchLimit, cutoff));
|
|
179816
|
+
}
|
|
179817
|
+
const fused = new Map;
|
|
179818
|
+
for (const list of queryLists) {
|
|
179819
|
+
list.forEach((row, rank) => {
|
|
179820
|
+
const rrf = 1 / (RRF_K + rank);
|
|
179821
|
+
const existing = fused.get(row.messageId);
|
|
179822
|
+
if (existing) {
|
|
179823
|
+
existing.score += rrf;
|
|
179824
|
+
} else {
|
|
179825
|
+
fused.set(row.messageId, { row, score: rrf });
|
|
179826
|
+
}
|
|
179827
|
+
});
|
|
179828
|
+
}
|
|
179829
|
+
for (const entry of fused.values()) {
|
|
179830
|
+
if (containsProbeVerbatim(entry.row.content, probes)) {
|
|
179831
|
+
entry.score += VERBATIM_PROBE_BONUS;
|
|
179832
|
+
}
|
|
179833
|
+
}
|
|
179834
|
+
const ranked = [...fused.values()].sort((a, b) => b.score !== a.score ? b.score - a.score : a.row.messageOrdinal - b.row.messageOrdinal).slice(0, args.limit);
|
|
179835
|
+
const maxScore = ranked.length > 0 ? ranked[0].score : 1;
|
|
179836
|
+
return ranked.map((entry) => ({
|
|
179478
179837
|
source: "message",
|
|
179479
|
-
content: previewText(row.content),
|
|
179480
|
-
score:
|
|
179481
|
-
messageOrdinal: row.messageOrdinal,
|
|
179482
|
-
messageId: row.messageId,
|
|
179483
|
-
role: row.role
|
|
179838
|
+
content: previewText(entry.row.content),
|
|
179839
|
+
score: maxScore > 0 ? entry.score / maxScore : 0,
|
|
179840
|
+
messageOrdinal: entry.row.messageOrdinal,
|
|
179841
|
+
messageId: entry.row.messageId,
|
|
179842
|
+
role: entry.row.role
|
|
179484
179843
|
}));
|
|
179485
179844
|
}
|
|
179486
179845
|
function getSourceBoost(result) {
|
|
@@ -179564,12 +179923,14 @@ async function unifiedSearch(db, sessionId, projectPath, query, options = {}) {
|
|
|
179564
179923
|
return null;
|
|
179565
179924
|
}) : Promise.resolve(null);
|
|
179566
179925
|
await Promise.resolve();
|
|
179926
|
+
const messageProbes = options.explicitSearch ? extractLiteralProbes(trimmedQuery) : [];
|
|
179567
179927
|
const messageResults = runMessages ? searchMessages({
|
|
179568
179928
|
db,
|
|
179569
179929
|
sessionId,
|
|
179570
179930
|
query: trimmedQuery,
|
|
179571
179931
|
limit: tierLimit,
|
|
179572
|
-
maxOrdinal: options.maxMessageOrdinal
|
|
179932
|
+
maxOrdinal: options.maxMessageOrdinal,
|
|
179933
|
+
probes: messageProbes
|
|
179573
179934
|
}) : [];
|
|
179574
179935
|
const queryEmbedding = await queryEmbeddingPromise;
|
|
179575
179936
|
const [memoryResults, gitCommitResults] = await Promise.all([
|
|
@@ -180152,7 +180513,7 @@ function isVisibleNoteReadPart(part) {
|
|
|
180152
180513
|
}
|
|
180153
180514
|
|
|
180154
180515
|
// src/hooks/magic-context/todo-view.ts
|
|
180155
|
-
import { createHash as
|
|
180516
|
+
import { createHash as createHash9 } from "node:crypto";
|
|
180156
180517
|
var TERMINAL_STATUSES = new Set(["completed", "cancelled"]);
|
|
180157
180518
|
var TITLE_DONE_STATUSES = new Set(["completed"]);
|
|
180158
180519
|
var SYNTHETIC_CALL_ID_PREFIX = "mc_synthetic_todo_";
|
|
@@ -180197,7 +180558,7 @@ function buildSyntheticTodoPart(stateJson) {
|
|
|
180197
180558
|
};
|
|
180198
180559
|
}
|
|
180199
180560
|
function computeSyntheticCallId(stateJson) {
|
|
180200
|
-
const hash2 =
|
|
180561
|
+
const hash2 = createHash9("sha256").update(stateJson).digest("hex").slice(0, 16);
|
|
180201
180562
|
return `${SYNTHETIC_CALL_ID_PREFIX}${hash2}`;
|
|
180202
180563
|
}
|
|
180203
180564
|
function parseTodoState(stateJson) {
|
|
@@ -182064,7 +182425,7 @@ function createToolExecuteAfterHook(args) {
|
|
|
182064
182425
|
init_send_session_notification();
|
|
182065
182426
|
|
|
182066
182427
|
// src/hooks/magic-context/system-prompt-hash.ts
|
|
182067
|
-
import { createHash as
|
|
182428
|
+
import { createHash as createHash10 } from "node:crypto";
|
|
182068
182429
|
|
|
182069
182430
|
// src/agents/magic-context-prompt.ts
|
|
182070
182431
|
var LONG_TERM_PARTNER_FRAME = `### You are the user's long-term partner on this project — not a one-off hire
|
|
@@ -182248,7 +182609,7 @@ function createSystemPromptHashHandler(deps) {
|
|
|
182248
182609
|
`);
|
|
182249
182610
|
if (systemContent.length === 0)
|
|
182250
182611
|
return;
|
|
182251
|
-
const currentHash =
|
|
182612
|
+
const currentHash = createHash10("md5").update(systemContent).digest("hex");
|
|
182252
182613
|
if (!sessionMetaEarly) {
|
|
182253
182614
|
return;
|
|
182254
182615
|
}
|
|
@@ -183477,6 +183838,7 @@ function buildSidebarSnapshot(db, sessionId, directory, liveSessionState, inject
|
|
|
183477
183838
|
if (!p)
|
|
183478
183839
|
return null;
|
|
183479
183840
|
return {
|
|
183841
|
+
kind: p.kind ?? "recomp",
|
|
183480
183842
|
phase: p.phase,
|
|
183481
183843
|
processedMessages: p.processedMessages,
|
|
183482
183844
|
totalMessages: p.totalMessages,
|
|
@@ -183496,6 +183858,7 @@ function buildSidebarSnapshot(db, sessionId, directory, liveSessionState, inject
|
|
|
183496
183858
|
return {
|
|
183497
183859
|
...empty,
|
|
183498
183860
|
recompProgress: {
|
|
183861
|
+
kind: p.kind ?? "recomp",
|
|
183499
183862
|
phase: p.phase,
|
|
183500
183863
|
processedMessages: p.processedMessages,
|
|
183501
183864
|
totalMessages: p.totalMessages,
|
|
@@ -184212,16 +184575,30 @@ Example: \`ctx_note(action="write", content="Implement X because Y", surface_con
|
|
|
184212
184575
|
|
|
184213
184576
|
Historian reads these notes, deduplicates them, and rewrites the remaining useful notes over time.`;
|
|
184214
184577
|
// src/tools/ctx-note/tools.ts
|
|
184215
|
-
await
|
|
184578
|
+
await __promiseAll([
|
|
184579
|
+
init_message_index(),
|
|
184580
|
+
init_storage()
|
|
184581
|
+
]);
|
|
184216
184582
|
import { tool as tool3 } from "@opencode-ai/plugin";
|
|
184583
|
+
function captureAnchorOrdinal(db, sessionId) {
|
|
184584
|
+
try {
|
|
184585
|
+
const ordinal = getLastIndexedOrdinal(db, sessionId);
|
|
184586
|
+
return ordinal > 0 ? ordinal : null;
|
|
184587
|
+
} catch {
|
|
184588
|
+
return null;
|
|
184589
|
+
}
|
|
184590
|
+
}
|
|
184591
|
+
function anchorSuffix(note) {
|
|
184592
|
+
return note.anchorOrdinal !== null ? ` ↳ @msg ${note.anchorOrdinal}` : "";
|
|
184593
|
+
}
|
|
184217
184594
|
function formatNoteLine(note) {
|
|
184218
184595
|
const statusSuffix = note.status === "active" ? "" : ` (${note.status})`;
|
|
184219
184596
|
if (note.type === "session") {
|
|
184220
|
-
return `- **#${note.id}**${statusSuffix}: ${note.content}`;
|
|
184597
|
+
return `- **#${note.id}**${statusSuffix}: ${note.content}${anchorSuffix(note)}`;
|
|
184221
184598
|
}
|
|
184222
184599
|
const conditionText = note.status === "ready" ? note.readyReason ?? note.surfaceCondition ?? "Condition satisfied" : note.surfaceCondition ?? "No condition recorded";
|
|
184223
184600
|
const conditionLabel = note.status === "ready" ? "Condition met" : "Condition";
|
|
184224
|
-
return `- **#${note.id}**${statusSuffix}: ${note.content}
|
|
184601
|
+
return `- **#${note.id}**${statusSuffix}: ${note.content}${anchorSuffix(note)}
|
|
184225
184602
|
${conditionLabel}: ${conditionText}`;
|
|
184226
184603
|
}
|
|
184227
184604
|
var DISMISS_FOOTER = `
|
|
@@ -184299,6 +184676,7 @@ function createCtxNoteTool(deps) {
|
|
|
184299
184676
|
if (!content) {
|
|
184300
184677
|
return "Error: 'content' is required when action is 'write'.";
|
|
184301
184678
|
}
|
|
184679
|
+
const anchorOrdinal = captureAnchorOrdinal(deps.db, sessionId);
|
|
184302
184680
|
if (args.surface_condition?.trim()) {
|
|
184303
184681
|
if (!deps.dreamerEnabled) {
|
|
184304
184682
|
return "Error: Smart notes require dreamer to be enabled. Enable dreamer in magic-context.jsonc to use surface_condition.";
|
|
@@ -184310,13 +184688,14 @@ function createCtxNoteTool(deps) {
|
|
|
184310
184688
|
content,
|
|
184311
184689
|
projectPath: projectIdentity,
|
|
184312
184690
|
sessionId,
|
|
184313
|
-
surfaceCondition: args.surface_condition.trim()
|
|
184691
|
+
surfaceCondition: args.surface_condition.trim(),
|
|
184692
|
+
anchorOrdinal
|
|
184314
184693
|
});
|
|
184315
184694
|
return `Created smart note #${note2.id}. Dreamer will evaluate the condition during nightly runs:
|
|
184316
184695
|
- Content: ${content}
|
|
184317
184696
|
- Condition: ${args.surface_condition.trim()}`;
|
|
184318
184697
|
}
|
|
184319
|
-
const note = addNote(deps.db, "session", { sessionId, content });
|
|
184698
|
+
const note = addNote(deps.db, "session", { sessionId, content, anchorOrdinal });
|
|
184320
184699
|
return `Saved session note #${note.id}. Historian will rewrite or deduplicate notes as needed.`;
|
|
184321
184700
|
}
|
|
184322
184701
|
if (action === "dismiss") {
|
|
@@ -184379,9 +184758,13 @@ ${parts.join(`
|
|
|
184379
184758
|
|
|
184380
184759
|
No session notes or smart notes.`;
|
|
184381
184760
|
}
|
|
184382
|
-
|
|
184761
|
+
const body = sections.join(`
|
|
184762
|
+
|
|
184763
|
+
`);
|
|
184764
|
+
const anchorHint = body.includes("↳ @msg ") ? `
|
|
184383
184765
|
|
|
184384
|
-
|
|
184766
|
+
↳ @msg N marks the conversation tail when a note was written. To see what led to it: ctx_expand(start=N-x, end=N) (pick x for how far back to look).` : "";
|
|
184767
|
+
return body + anchorHint + DISMISS_FOOTER;
|
|
184385
184768
|
}
|
|
184386
184769
|
});
|
|
184387
184770
|
}
|
|
@@ -184672,7 +185055,8 @@ function createCtxSearchTool(deps) {
|
|
|
184672
185055
|
maxMessageOrdinal: lastCompartmentEnd >= 0 ? lastCompartmentEnd : undefined,
|
|
184673
185056
|
gitCommitsEnabled,
|
|
184674
185057
|
sources: normalizeSources(args.sources),
|
|
184675
|
-
visibleMemoryIds
|
|
185058
|
+
visibleMemoryIds,
|
|
185059
|
+
explicitSearch: true
|
|
184676
185060
|
});
|
|
184677
185061
|
return formatSearchResults(query, results);
|
|
184678
185062
|
}
|
|
@@ -184773,22 +185157,22 @@ init_models_dev_cache();
|
|
|
184773
185157
|
init_logger();
|
|
184774
185158
|
import { randomBytes } from "node:crypto";
|
|
184775
185159
|
import {
|
|
184776
|
-
mkdirSync as
|
|
185160
|
+
mkdirSync as mkdirSync9,
|
|
184777
185161
|
readdirSync,
|
|
184778
185162
|
readFileSync as readFileSync15,
|
|
184779
|
-
renameSync as
|
|
185163
|
+
renameSync as renameSync4,
|
|
184780
185164
|
unlinkSync as unlinkSync3,
|
|
184781
|
-
writeFileSync as
|
|
185165
|
+
writeFileSync as writeFileSync8
|
|
184782
185166
|
} from "node:fs";
|
|
184783
185167
|
import { createServer } from "node:http";
|
|
184784
185168
|
import { dirname as dirname8 } from "node:path";
|
|
184785
185169
|
|
|
184786
185170
|
// src/shared/rpc-utils.ts
|
|
184787
|
-
import { createHash as
|
|
185171
|
+
import { createHash as createHash11 } from "node:crypto";
|
|
184788
185172
|
import { join as join24 } from "node:path";
|
|
184789
185173
|
function projectHash(directory) {
|
|
184790
185174
|
const normalized = directory.replace(/\/+$/, "");
|
|
184791
|
-
return
|
|
185175
|
+
return createHash11("sha256").update(normalized).digest("hex").slice(0, 16);
|
|
184792
185176
|
}
|
|
184793
185177
|
function rpcPortDir(storageDir, directory) {
|
|
184794
185178
|
return join24(storageDir, "rpc", projectHash(directory));
|
|
@@ -184871,15 +185255,15 @@ class MagicContextRpcServer {
|
|
|
184871
185255
|
try {
|
|
184872
185256
|
this.warnIfOtherLiveInstance();
|
|
184873
185257
|
const dir = dirname8(this.portFilePath);
|
|
184874
|
-
|
|
185258
|
+
mkdirSync9(dir, { recursive: true, mode: 448 });
|
|
184875
185259
|
const tmpPath = `${this.portFilePath}.tmp`;
|
|
184876
|
-
|
|
185260
|
+
writeFileSync8(tmpPath, JSON.stringify({
|
|
184877
185261
|
port: this.port,
|
|
184878
185262
|
pid: process.pid,
|
|
184879
185263
|
started_at: this.startedAt,
|
|
184880
185264
|
token: this.token
|
|
184881
185265
|
}), { encoding: "utf-8", mode: 384 });
|
|
184882
|
-
|
|
185266
|
+
renameSync4(tmpPath, this.portFilePath);
|
|
184883
185267
|
log(`[rpc] server listening on 127.0.0.1:${this.port}`);
|
|
184884
185268
|
} catch (err) {
|
|
184885
185269
|
log(`[rpc] failed to write port file: ${err}`);
|
|
@@ -185041,6 +185425,8 @@ var plugin = async (ctx) => {
|
|
|
185041
185425
|
}
|
|
185042
185426
|
}
|
|
185043
185427
|
const storageDir = getMagicContextStorageDir();
|
|
185428
|
+
let rpcServer = null;
|
|
185429
|
+
let stopDreamTimerRegistration;
|
|
185044
185430
|
if (pluginConfig.enabled) {
|
|
185045
185431
|
const dreamerRunnable = isDreamerRunnable(pluginConfig);
|
|
185046
185432
|
const timerRegistration = {
|
|
@@ -185066,8 +185452,8 @@ var plugin = async (ctx) => {
|
|
|
185066
185452
|
} : undefined,
|
|
185067
185453
|
ensureRegistered: ensureProjectRegisteredFromOpenCodeDirectory
|
|
185068
185454
|
};
|
|
185069
|
-
await startDreamScheduleTimer(timerRegistration);
|
|
185070
|
-
|
|
185455
|
+
stopDreamTimerRegistration = await startDreamScheduleTimer(timerRegistration);
|
|
185456
|
+
rpcServer = new MagicContextRpcServer(storageDir, ctx.directory);
|
|
185071
185457
|
registerRpcHandlers(rpcServer, {
|
|
185072
185458
|
directory: ctx.directory,
|
|
185073
185459
|
config: pluginConfig,
|
|
@@ -185077,7 +185463,7 @@ var plugin = async (ctx) => {
|
|
|
185077
185463
|
rpcServer.start().catch((err) => {
|
|
185078
185464
|
log(`[magic-context] RPC server failed to start: ${err}`);
|
|
185079
185465
|
});
|
|
185080
|
-
refreshModelLimitsFromApi(ctx.client);
|
|
185466
|
+
refreshModelLimitsFromApi(ctx.client, { retries: 3, retryDelayMs: 1000 });
|
|
185081
185467
|
}
|
|
185082
185468
|
{
|
|
185083
185469
|
const fence = getSchemaFenceRejection();
|
|
@@ -185121,6 +185507,7 @@ var plugin = async (ctx) => {
|
|
|
185121
185507
|
} catch {}
|
|
185122
185508
|
}
|
|
185123
185509
|
let lastChatContext = null;
|
|
185510
|
+
const ownProjectIdentity = resolveProjectIdentity(ctx.directory);
|
|
185124
185511
|
return {
|
|
185125
185512
|
tool: tools5,
|
|
185126
185513
|
event: createEventHandler({
|
|
@@ -185129,7 +185516,21 @@ var plugin = async (ctx) => {
|
|
|
185129
185516
|
autoUpdate: pluginConfig.auto_update !== false,
|
|
185130
185517
|
signal: autoUpdateAbort.signal,
|
|
185131
185518
|
storageDir
|
|
185132
|
-
})
|
|
185519
|
+
}),
|
|
185520
|
+
onInstanceDisposed: (disposedDirectory) => {
|
|
185521
|
+
if (resolveProjectIdentity(disposedDirectory) !== ownProjectIdentity)
|
|
185522
|
+
return;
|
|
185523
|
+
try {
|
|
185524
|
+
autoUpdateAbort.abort();
|
|
185525
|
+
} catch {}
|
|
185526
|
+
try {
|
|
185527
|
+
stopDreamTimerRegistration?.();
|
|
185528
|
+
} catch {}
|
|
185529
|
+
try {
|
|
185530
|
+
rpcServer?.stop();
|
|
185531
|
+
} catch {}
|
|
185532
|
+
log("[magic-context] instance disposed — stopped RPC server, dream timer, auto-update");
|
|
185533
|
+
}
|
|
185133
185534
|
}),
|
|
185134
185535
|
"experimental.chat.messages.transform": createMessagesTransformHandler({
|
|
185135
185536
|
magicContext: hooks.magicContext
|