opencode-matrixx 2.6.12 → 2.6.13

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.
Files changed (70) hide show
  1. package/dist/agents/dynamic-agent-prompt-builder.d.ts +4 -4
  2. package/dist/agents/model-directives.d.ts +7 -0
  3. package/dist/cli.js +7 -2
  4. package/dist/config/schema/dcp.d.ts +15 -11
  5. package/dist/config/schema/hooks.d.ts +2 -0
  6. package/dist/config/schema/matrixx-config.d.ts +4 -0
  7. package/dist/create-hooks.d.ts +2 -0
  8. package/dist/features/session-state/state.d.ts +3 -0
  9. package/dist/hooks/dcp-nudge-sanitizer/constants.d.ts +2 -0
  10. package/dist/hooks/dcp-nudge-sanitizer/hook.d.ts +25 -0
  11. package/dist/hooks/dcp-nudge-sanitizer/index.d.ts +1 -0
  12. package/dist/hooks/index.d.ts +2 -0
  13. package/dist/hooks/nudge-loop-breaker/constants.d.ts +9 -0
  14. package/dist/hooks/nudge-loop-breaker/hook.d.ts +18 -0
  15. package/dist/hooks/nudge-loop-breaker/index.d.ts +1 -0
  16. package/dist/hooks/nudge-loop-breaker/session-state.d.ts +11 -0
  17. package/dist/index.js +281 -38
  18. package/dist/matrixx.schema.json +39 -1
  19. package/dist/plugin/hooks/create-continuation-hooks.d.ts +2 -1
  20. package/dist/plugin/hooks/create-core-hooks.d.ts +1 -0
  21. package/dist/plugin/hooks/create-transform-hooks.d.ts +2 -0
  22. package/dist/shared/dcp-switch-profile.d.ts +12 -0
  23. package/package.json +1 -1
  24. package/dist/cli/setup/config-writer.test.d.ts +0 -1
  25. package/dist/cli/setup/deps.test.d.ts +0 -1
  26. package/dist/cli/setup/index.test.d.ts +0 -1
  27. package/dist/cli/setup/opencode-sync.test.d.ts +0 -1
  28. package/dist/cli/setup/prompts.test.d.ts +0 -1
  29. package/dist/features/background-agent/handle-index.test.d.ts +0 -1
  30. package/dist/features/background-agent/manager-handles.test.d.ts +0 -1
  31. package/dist/features/knowledge-hub/loader.test.d.ts +0 -1
  32. package/dist/features/knowledge-hub/resolver.test.d.ts +0 -1
  33. package/dist/features/mission-state/plan-storage.test.d.ts +0 -1
  34. package/dist/features/mission-state/reconcile.test.d.ts +0 -1
  35. package/dist/features/session-state/state.test.d.ts +0 -1
  36. package/dist/hooks/input-secret-guard/detector.test.d.ts +0 -1
  37. package/dist/hooks/input-secret-guard/hook.test.d.ts +0 -1
  38. package/dist/hooks/input-secret-guard/redactor.test.d.ts +0 -1
  39. package/dist/hooks/input-secret-guard/session-allow-cache.test.d.ts +0 -1
  40. package/dist/hooks/interactive-bash-session/hook.test.d.ts +0 -1
  41. package/dist/hooks/knowledge-hub-guard/hook.test.d.ts +0 -1
  42. package/dist/hooks/knowledge-hub-injector/hook.test.d.ts +0 -1
  43. package/dist/hooks/knowledge-hub-search-nudge/hook.test.d.ts +0 -1
  44. package/dist/hooks/plan-persister/task-sync.test.d.ts +0 -1
  45. package/dist/hooks/rtk-bash-rewriter/hook.test.d.ts +0 -1
  46. package/dist/hooks/stop-continuation-guard/repro.test.d.ts +0 -1
  47. package/dist/hooks/task-continuation-enforcer/awaiting-user.test.d.ts +0 -1
  48. package/dist/hooks/task-continuation-enforcer/continuation-injection.test.d.ts +0 -1
  49. package/dist/hooks/task-continuation-enforcer/countdown.test.d.ts +0 -1
  50. package/dist/hooks/task-continuation-enforcer/idle-event.test.d.ts +0 -1
  51. package/dist/hooks/task-continuation-enforcer/staleness.test.d.ts +0 -1
  52. package/dist/hooks/task-continuation-enforcer/todo.test.d.ts +0 -1
  53. package/dist/hooks/task-continuation-enforcer/ulw-bootstrap.test.d.ts +0 -1
  54. package/dist/hooks/todo-continuation-enforcer/awaiting-user.test.d.ts +0 -1
  55. package/dist/hooks/todo-continuation-enforcer/countdown.test.d.ts +0 -1
  56. package/dist/hooks/todo-continuation-enforcer/idle-event.test.d.ts +0 -1
  57. package/dist/shared/format-bytes.test.d.ts +0 -1
  58. package/dist/shared/is-abort-error.test.d.ts +0 -1
  59. package/dist/shared/task-system-gating.test.d.ts +0 -1
  60. package/dist/shared/with-timeout.test.d.ts +0 -1
  61. package/dist/tools/delegate-task/poll-timeout-outcome.test.d.ts +0 -1
  62. package/dist/tools/delegate-task/prompt-builder.tdd.test.d.ts +0 -1
  63. package/dist/tools/delegate-task/sync-task.test.d.ts +0 -1
  64. package/dist/tools/delegate-task/tdd-enforcement.test.d.ts +0 -1
  65. package/dist/tools/delegate-task/timing.test.d.ts +0 -1
  66. package/dist/tools/evolution/query-actions.test.d.ts +0 -1
  67. package/dist/tools/evolution/tools.test.d.ts +0 -1
  68. package/dist/tools/github-search/result-formatter.test.d.ts +0 -1
  69. package/dist/tools/knowledge-hub-confirm/tools.test.d.ts +0 -1
  70. package/dist/tools/task/task-cleanup.test.d.ts +0 -1
@@ -39,8 +39,8 @@ export declare function _resetDisciplineCacheForTesting(): void;
39
39
  export declare function hasGrepGlobToolNames(toolNames: readonly string[]): boolean;
40
40
  export declare function fallbackFullDiscipline(hasGrepGlob: boolean, dcpMode?: DcpCompressionMode): string;
41
41
  export declare function fallbackCompactDiscipline(hasGrepGlob: boolean, dcpMode?: DcpCompressionMode): string;
42
- export declare function buildContextDisciplineSection(hasContextMode?: boolean, hasGrepGlob?: boolean, dcpMode?: DcpCompressionMode): string;
43
- export declare function buildHeadroomSection(hasHeadroom?: boolean, dcpMode?: DcpCompressionMode): string;
44
- export declare function buildCompactContextDisciplineSection(hasContextMode?: boolean, hasGrepGlob?: boolean, dcpMode?: DcpCompressionMode): string;
45
- export declare function buildExploreDisciplineSection(hasContextMode?: boolean, hasHeadroom?: boolean, hasGrepGlob?: boolean): string;
42
+ export declare function buildContextDisciplineSection(hasContextMode?: boolean, hasGrepGlob?: boolean, dcpMode?: DcpCompressionMode, modelID?: string): string;
43
+ export declare function buildHeadroomSection(hasHeadroom?: boolean, dcpMode?: DcpCompressionMode, modelID?: string): string;
44
+ export declare function buildCompactContextDisciplineSection(hasContextMode?: boolean, hasGrepGlob?: boolean, dcpMode?: DcpCompressionMode, modelID?: string): string;
45
+ export declare function buildExploreDisciplineSection(hasContextMode?: boolean, hasHeadroom?: boolean, hasGrepGlob?: boolean, modelID?: string): string;
46
46
  export declare function buildUltraworkSection(agents: AvailableAgent[], categories: AvailableCategory[], skills: AvailableSkill[]): string;
@@ -0,0 +1,7 @@
1
+ export type ModelDirective = {
2
+ antiEcho?: string;
3
+ nudgeHandling?: string;
4
+ };
5
+ export declare function resolveModelFamily(modelID?: string): string | undefined;
6
+ export declare function getModelDirectives(modelID?: string): ModelDirective;
7
+ export declare function appendModelDirective(section: string, modelID?: string): string;
package/dist/cli.js CHANGED
@@ -15892,9 +15892,13 @@ var ContextModeConfigSchema = exports_external.object({
15892
15892
  });
15893
15893
 
15894
15894
  // src/config/schema/dcp.ts
15895
+ var dcpLimitValue = exports_external.union([exports_external.number(), exports_external.string().regex(/^\d+%$/)]);
15896
+ var dcpModelLimitKey = exports_external.string().regex(/^[^/]+\/[^/]+$/);
15895
15897
  var DcpCompressOverrideSchema = exports_external.object({
15896
15898
  maxContextLimit: exports_external.union([exports_external.number(), exports_external.string().regex(/^\d+%$/)]).optional(),
15897
15899
  minContextLimit: exports_external.union([exports_external.number(), exports_external.string().regex(/^\d+%$/)]).optional(),
15900
+ modelMaxLimits: exports_external.record(dcpModelLimitKey, dcpLimitValue).optional(),
15901
+ modelMinLimits: exports_external.record(dcpModelLimitKey, dcpLimitValue).optional(),
15898
15902
  nudgeFrequency: exports_external.number().int().min(1).optional(),
15899
15903
  iterationNudgeThreshold: exports_external.number().int().min(1).optional(),
15900
15904
  nudgeForce: exports_external.enum(["strong", "soft"]).optional(),
@@ -16171,12 +16175,13 @@ var HookNameEnum = exports_external.enum([
16171
16175
  "evolution-compressor",
16172
16176
  "evolution-hitl",
16173
16177
  "context-mode-enforcer",
16174
- "context-mode-enforcer",
16175
16178
  "task-edit-guard",
16176
16179
  "document-reader-guard",
16177
16180
  "knowledge-hub-guard",
16178
16181
  "knowledge-hub-injector",
16179
- "knowledge-hub-search-nudge"
16182
+ "knowledge-hub-search-nudge",
16183
+ "dcp-nudge-sanitizer",
16184
+ "nudge-loop-breaker"
16180
16185
  ]);
16181
16186
  var HookNameSchema = exports_external.union([
16182
16187
  HookNameEnum,
@@ -1,15 +1,9 @@
1
1
  import { z } from "zod";
2
- /**
3
- * DCP (Dynamic Context Pruning) configuration.
4
- *
5
- * Selects between predefined DCP profile tiers
6
- * (economy/balanced/performance/ultimate) via `dcp.default_profile`.
7
- *
8
- * DCP must be installed as a plugin: `~/.config/opencode/node_modules/@tarquinen/opencode-dcp`
9
- */
10
2
  export declare const DcpCompressOverrideSchema: z.ZodObject<{
11
3
  maxContextLimit: z.ZodOptional<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>;
12
4
  minContextLimit: z.ZodOptional<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>;
5
+ modelMaxLimits: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>>;
6
+ modelMinLimits: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>>;
13
7
  nudgeFrequency: z.ZodOptional<z.ZodNumber>;
14
8
  iterationNudgeThreshold: z.ZodOptional<z.ZodNumber>;
15
9
  nudgeForce: z.ZodOptional<z.ZodEnum<{
@@ -45,6 +39,8 @@ export declare const DcpProfileDefinitionSchema: z.ZodObject<{
45
39
  compress: z.ZodOptional<z.ZodObject<{
46
40
  maxContextLimit: z.ZodOptional<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>;
47
41
  minContextLimit: z.ZodOptional<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>;
42
+ modelMaxLimits: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>>;
43
+ modelMinLimits: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>>;
48
44
  nudgeFrequency: z.ZodOptional<z.ZodNumber>;
49
45
  iterationNudgeThreshold: z.ZodOptional<z.ZodNumber>;
50
46
  nudgeForce: z.ZodOptional<z.ZodEnum<{
@@ -163,9 +159,15 @@ export declare const BUILTIN_DCP_PROFILES: {
163
159
  readonly compress: {
164
160
  readonly maxContextLimit: "20%";
165
161
  readonly minContextLimit: "10%";
166
- readonly nudgeFrequency: 1;
167
- readonly nudgeForce: "strong";
168
- readonly iterationNudgeThreshold: 1;
162
+ readonly nudgeFrequency: 3;
163
+ readonly nudgeForce: "soft";
164
+ readonly iterationNudgeThreshold: 6;
165
+ readonly modelMaxLimits: {
166
+ readonly "deepseek/deepseek-v4.1-flash": "95%";
167
+ };
168
+ readonly modelMinLimits: {
169
+ readonly "deepseek/deepseek-v4.1-flash": "90%";
170
+ };
169
171
  };
170
172
  readonly turnProtection: {
171
173
  readonly enabled: false;
@@ -207,6 +209,8 @@ export declare const DcpConfigSchema: z.ZodObject<{
207
209
  compress: z.ZodOptional<z.ZodObject<{
208
210
  maxContextLimit: z.ZodOptional<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>;
209
211
  minContextLimit: z.ZodOptional<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>;
212
+ modelMaxLimits: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>>;
213
+ modelMinLimits: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>>;
210
214
  nudgeFrequency: z.ZodOptional<z.ZodNumber>;
211
215
  iterationNudgeThreshold: z.ZodOptional<z.ZodNumber>;
212
216
  nudgeForce: z.ZodOptional<z.ZodEnum<{
@@ -64,5 +64,7 @@ export declare const HookNameSchema: z.ZodUnion<readonly [z.ZodEnum<{
64
64
  "knowledge-hub-guard": "knowledge-hub-guard";
65
65
  "knowledge-hub-injector": "knowledge-hub-injector";
66
66
  "knowledge-hub-search-nudge": "knowledge-hub-search-nudge";
67
+ "dcp-nudge-sanitizer": "dcp-nudge-sanitizer";
68
+ "nudge-loop-breaker": "nudge-loop-breaker";
67
69
  }>, z.ZodPipe<z.ZodLiteral<"anthropic-context-window-limit-recovery">, z.ZodTransform<"context-window-limit-recovery", "anthropic-context-window-limit-recovery">>]>;
68
70
  export type HookName = z.infer<typeof HookNameSchema>;
@@ -137,6 +137,8 @@ export declare const MatrixxConfigSchema: z.ZodObject<{
137
137
  "knowledge-hub-guard": "knowledge-hub-guard";
138
138
  "knowledge-hub-injector": "knowledge-hub-injector";
139
139
  "knowledge-hub-search-nudge": "knowledge-hub-search-nudge";
140
+ "dcp-nudge-sanitizer": "dcp-nudge-sanitizer";
141
+ "nudge-loop-breaker": "nudge-loop-breaker";
140
142
  }>, z.ZodPipe<z.ZodLiteral<"anthropic-context-window-limit-recovery">, z.ZodTransform<"context-window-limit-recovery", "anthropic-context-window-limit-recovery">>]>>>;
141
143
  disabled_commands: z.ZodOptional<z.ZodArray<z.ZodEnum<{
142
144
  "init-deep": "init-deep";
@@ -1581,6 +1583,8 @@ export declare const MatrixxConfigSchema: z.ZodObject<{
1581
1583
  compress: z.ZodOptional<z.ZodObject<{
1582
1584
  maxContextLimit: z.ZodOptional<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>;
1583
1585
  minContextLimit: z.ZodOptional<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>;
1586
+ modelMaxLimits: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>>;
1587
+ modelMinLimits: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>>;
1584
1588
  nudgeFrequency: z.ZodOptional<z.ZodNumber>;
1585
1589
  iterationNudgeThreshold: z.ZodOptional<z.ZodNumber>;
1586
1590
  nudgeForce: z.ZodOptional<z.ZodEnum<{
@@ -21,6 +21,7 @@ export declare function createHooks(args: {
21
21
  todoContinuationEnforcer: ReturnType<typeof import("./hooks").createTodoContinuationEnforcer> | null;
22
22
  taskContinuationEnforcer: ReturnType<typeof import("./hooks").createTaskContinuationEnforcer> | null;
23
23
  unstableAgentBabysitter: ReturnType<typeof import("./plugin/unstable-agent-babysitter").createUnstableAgentBabysitter> | null;
24
+ nudgeLoopBreaker: ReturnType<typeof import("./hooks").createNudgeLoopBreakerHook> | null;
24
25
  backgroundNotificationHook: ReturnType<typeof import("./hooks").createBackgroundNotificationHook> | null;
25
26
  architectHook: ReturnType<typeof import("./hooks").createArchitectHook> | null;
26
27
  planPersister: ReturnType<typeof import("./hooks").createPlanPersister> | null;
@@ -32,6 +33,7 @@ export declare function createHooks(args: {
32
33
  knowledgeHubInjector: ReturnType<typeof import("./hooks").createKnowledgeHubInjectorHook> | null;
33
34
  envContextInjector: ReturnType<typeof import("./hooks").createEnvContextInjectorHook> | null;
34
35
  thinkingBlockValidator: ReturnType<typeof import("./hooks").createThinkingBlockValidatorHook> | null;
36
+ dcpNudgeSanitizer: ReturnType<typeof import("./hooks").createDcpNudgeSanitizerHook> | null;
35
37
  toolPairValidator: ReturnType<typeof import("./hooks").createToolPairValidatorHook> | null;
36
38
  designIntentPreserver: ReturnType<typeof import("./hooks").createDesignIntentPreserverHook> | null;
37
39
  backgroundTaskBlocker: ReturnType<typeof import("./hooks").createBackgroundTaskBlockerHook> | null;
@@ -28,3 +28,6 @@ export declare function setSessionAgent(sessionID: string, agent: string): void;
28
28
  export declare function updateSessionAgent(sessionID: string, agent: string): void;
29
29
  export declare function getSessionAgent(sessionID: string): string | undefined;
30
30
  export declare function clearSessionAgent(sessionID: string): void;
31
+ export declare function setSessionModel(sessionID: string, model: string): void;
32
+ export declare function getSessionModel(sessionID: string): string | undefined;
33
+ export declare function clearSessionModel(sessionID: string): void;
@@ -0,0 +1,2 @@
1
+ export declare const NUDGE_MARKERS: RegExp[];
2
+ export declare function normalizeNudgeText(text: string): string;
@@ -0,0 +1,25 @@
1
+ /**
2
+ * DCP Sticky-Nudge Sanitizer Hook
3
+ *
4
+ * Ordering constraint (do not "fix" by assuming otherwise):
5
+ * Matrixx registers `experimental.chat.messages.transform` BEFORE DCP (plugin
6
+ * index 0 vs 1), so this sanitizer runs before DCP injects this cycle's nudge.
7
+ * It therefore cleans PRIOR-cycle sticky nudge parts and caps accumulation at
8
+ * ~1 per cycle. It CANNOT suppress the nudge DCP injects later in the SAME
9
+ * cycle. Full same-cycle suppression requires the optional deploy step that
10
+ * reorders the `plugin` array so Matrixx runs after DCP.
11
+ */
12
+ import type { Part } from "@opencode-ai/sdk";
13
+ import type { PluginContext } from "../../plugin/types";
14
+ interface MessageWithParts {
15
+ info: unknown;
16
+ parts: Part[];
17
+ }
18
+ type MessagesTransformHook = {
19
+ "experimental.chat.messages.transform"?: (input: Record<string, never>, output: {
20
+ messages: MessageWithParts[];
21
+ }) => Promise<void>;
22
+ };
23
+ export declare function sanitizeNudgeParts(messages: MessageWithParts[]): void;
24
+ export declare function createDcpNudgeSanitizerHook(_ctx: PluginContext): MessagesTransformHook;
25
+ export {};
@@ -0,0 +1 @@
1
+ export { createDcpNudgeSanitizerHook } from "./hook";
@@ -12,6 +12,7 @@ export { createCompactionTodoPreserverHook } from "./compaction-todo-preserver";
12
12
  export { createContextModeEnforcerHook } from "./context-mode-enforcer";
13
13
  export { type ContextWindowLimitRecoveryOptions, type ContextWindowLimitRecoveryOptions as AnthropicContextWindowLimitRecoveryOptions, createContextWindowLimitRecoveryHook, createContextWindowLimitRecoveryHook as createAnthropicContextWindowLimitRecoveryHook } from "./context-window-limit-recovery";
14
14
  export { createContextWindowMonitorHook } from "./context-window-monitor";
15
+ export { createDcpNudgeSanitizerHook } from "./dcp-nudge-sanitizer";
15
16
  export { createDelegateTaskRetryHook } from "./delegate-task-retry";
16
17
  export { createDesignIntentPreserverHook } from "./design-intent-preserver";
17
18
  export { createDirectoryAgentsInjectorHook } from "./directory-agents-injector";
@@ -35,6 +36,7 @@ export { createKnowledgeHubSearchNudgeHook } from "./knowledge-hub-search-nudge"
35
36
  export { createMatrixLoopHook, type MatrixLoopHook } from "./matrix-loop";
36
37
  export { createMouseNotepadHook } from "./mouse-notepad";
37
38
  export { createNonInteractiveEnvHook } from "./non-interactive-env";
39
+ export { createNudgeLoopBreakerHook } from "./nudge-loop-breaker";
38
40
  export { createOracleMdOnlyHook } from "./oracle-md-only";
39
41
  export { createPlanPersister } from "./plan-persister";
40
42
  export { createPostReadInjectorHook } from "./post-read-injector";
@@ -0,0 +1,9 @@
1
+ export declare const DEFAULT_LOOP_THRESHOLD = 3;
2
+ export declare const DEFAULT_COOLDOWN_MS = 1000;
3
+ export declare const MAX_BACKOFF_EXPONENT = 5;
4
+ export declare const DSML_PATTERNS: RegExp[];
5
+ export declare const SESSION_ID_KEYS: readonly ["sessionID", "sessionId", "session"];
6
+ export declare const TEXT_KEYS: readonly ["text", "content", "message", "assistantText", "snapshot", "assistantMessageID"];
7
+ export declare const TOOL_KEYS: readonly ["tool", "toolName", "name"];
8
+ export declare const RESET_TOOL_NAMES: Set<string>;
9
+ export declare const DETECT_EVENT_TYPES: Set<string>;
@@ -0,0 +1,18 @@
1
+ import type { PluginContext } from "../../plugin/types";
2
+ export interface NudgeLoopBreakerOptions {
3
+ threshold?: number;
4
+ cooldownMs?: number;
5
+ now?: () => number;
6
+ onCorrective?: (sessionID: string, backoff: number) => void;
7
+ }
8
+ type EventInput = {
9
+ event: {
10
+ type: string;
11
+ properties?: unknown;
12
+ };
13
+ };
14
+ export type NudgeLoopBreakerHook = {
15
+ event: (input: EventInput) => Promise<void>;
16
+ };
17
+ export declare function createNudgeLoopBreakerHook(ctx: PluginContext, options?: NudgeLoopBreakerOptions): NudgeLoopBreakerHook;
18
+ export {};
@@ -0,0 +1 @@
1
+ export { createNudgeLoopBreakerHook } from "./hook";
@@ -0,0 +1,11 @@
1
+ export interface NudgeLoopState {
2
+ count: number;
3
+ lastHash: string | null;
4
+ lastAt: number;
5
+ lastEmitAt: number;
6
+ correctiveSent: boolean;
7
+ }
8
+ export declare function createNudgeLoopState(): NudgeLoopState;
9
+ export declare function computeBackoffDelay(count: number, threshold?: number): number;
10
+ export declare function hashText(text: string): string;
11
+ export declare function normalizeNudgeText(text: string): string;
package/dist/index.js CHANGED
@@ -45073,9 +45073,13 @@ import { homedir as homedir3 } from "os";
45073
45073
  import { join as join6 } from "path";
45074
45074
 
45075
45075
  // src/config/schema/dcp.ts
45076
+ var dcpLimitValue = exports_external.union([exports_external.number(), exports_external.string().regex(/^\d+%$/)]);
45077
+ var dcpModelLimitKey = exports_external.string().regex(/^[^/]+\/[^/]+$/);
45076
45078
  var DcpCompressOverrideSchema = exports_external.object({
45077
45079
  maxContextLimit: exports_external.union([exports_external.number(), exports_external.string().regex(/^\d+%$/)]).optional(),
45078
45080
  minContextLimit: exports_external.union([exports_external.number(), exports_external.string().regex(/^\d+%$/)]).optional(),
45081
+ modelMaxLimits: exports_external.record(dcpModelLimitKey, dcpLimitValue).optional(),
45082
+ modelMinLimits: exports_external.record(dcpModelLimitKey, dcpLimitValue).optional(),
45079
45083
  nudgeFrequency: exports_external.number().int().min(1).optional(),
45080
45084
  iterationNudgeThreshold: exports_external.number().int().min(1).optional(),
45081
45085
  nudgeForce: exports_external.enum(["strong", "soft"]).optional(),
@@ -45161,9 +45165,11 @@ var BUILTIN_DCP_PROFILES = {
45161
45165
  compress: {
45162
45166
  maxContextLimit: "20%",
45163
45167
  minContextLimit: "10%",
45164
- nudgeFrequency: 1,
45165
- nudgeForce: "strong",
45166
- iterationNudgeThreshold: 1
45168
+ nudgeFrequency: 3,
45169
+ nudgeForce: "soft",
45170
+ iterationNudgeThreshold: 6,
45171
+ modelMaxLimits: { "deepseek/deepseek-v4.1-flash": "95%" },
45172
+ modelMinLimits: { "deepseek/deepseek-v4.1-flash": "90%" }
45167
45173
  },
45168
45174
  turnProtection: { enabled: false },
45169
45175
  experimental: { allowSubAgents: false },
@@ -45342,7 +45348,9 @@ function buildInlineConfig(profile, options) {
45342
45348
  nudgeForce: profileCompress.nudgeForce ?? compressBase.nudgeForce ?? "strong",
45343
45349
  protectedTools: profileCompress.protectedTools ?? compressBase.protectedTools ?? [],
45344
45350
  protectTags: profileCompress.protectTags ?? false,
45345
- protectUserMessages: profileCompress.protectUserMessages ?? compressBase.protectUserMessages ?? false
45351
+ protectUserMessages: profileCompress.protectUserMessages ?? compressBase.protectUserMessages ?? false,
45352
+ modelMaxLimits: profileCompress.modelMaxLimits ?? compressBase.modelMaxLimits,
45353
+ modelMinLimits: profileCompress.modelMinLimits ?? compressBase.modelMinLimits
45346
45354
  },
45347
45355
  turnProtection: {
45348
45356
  enabled: profileTurn.enabled ?? true,
@@ -49226,6 +49234,7 @@ function isMainSessionCandidate(sessionInfo) {
49226
49234
  });
49227
49235
  }
49228
49236
  var sessionAgentMap = new Map;
49237
+ var sessionModelMap = new Map;
49229
49238
  function setSessionAgent(sessionID, agent) {
49230
49239
  if (!sessionAgentMap.has(sessionID)) {
49231
49240
  sessionAgentMap.set(sessionID, agent);
@@ -49240,6 +49249,9 @@ function getSessionAgent(sessionID) {
49240
49249
  function clearSessionAgent(sessionID) {
49241
49250
  sessionAgentMap.delete(sessionID);
49242
49251
  }
49252
+ function setSessionModel(sessionID, model) {
49253
+ sessionModelMap.set(sessionID, model);
49254
+ }
49243
49255
  // src/hooks/architect/event-handler.ts
49244
49256
  init_logger();
49245
49257
 
@@ -55293,6 +55305,55 @@ ${CONTEXT_REMINDER}
55293
55305
  event: eventHandler
55294
55306
  };
55295
55307
  }
55308
+ // src/hooks/dcp-nudge-sanitizer/constants.ts
55309
+ var NUDGE_MARKERS = [
55310
+ /<instruction\s+name=iteration_nudge>/i,
55311
+ /compressed block context:/i,
55312
+ /\(b\d+\)/
55313
+ ];
55314
+ function normalizeNudgeText(text) {
55315
+ return text.replace(/\s+/g, " ").trim();
55316
+ }
55317
+
55318
+ // src/hooks/dcp-nudge-sanitizer/hook.ts
55319
+ function getTextPart(part) {
55320
+ const candidate = part;
55321
+ if (candidate.type === "text" && typeof candidate.text === "string") {
55322
+ return { text: candidate.text };
55323
+ }
55324
+ return null;
55325
+ }
55326
+ function isNudgeText(text) {
55327
+ return NUDGE_MARKERS.some((marker) => marker.test(text));
55328
+ }
55329
+ function sanitizeNudgeParts(messages) {
55330
+ let keptNudge = false;
55331
+ for (const message of messages) {
55332
+ if (!Array.isArray(message.parts))
55333
+ continue;
55334
+ for (let i = 0;i < message.parts.length; ) {
55335
+ const textPart = getTextPart(message.parts[i]);
55336
+ if (!textPart || !isNudgeText(textPart.text)) {
55337
+ i++;
55338
+ continue;
55339
+ }
55340
+ if (keptNudge) {
55341
+ message.parts.splice(i, 1);
55342
+ continue;
55343
+ }
55344
+ keptNudge = true;
55345
+ message.parts[i].text = normalizeNudgeText(textPart.text);
55346
+ i++;
55347
+ }
55348
+ }
55349
+ }
55350
+ function createDcpNudgeSanitizerHook(_ctx) {
55351
+ return {
55352
+ "experimental.chat.messages.transform": async (_input, output) => {
55353
+ sanitizeNudgeParts(output.messages);
55354
+ }
55355
+ };
55356
+ }
55296
55357
  // src/hooks/delegate-task-retry/patterns.ts
55297
55358
  var DELEGATE_TASK_ERROR_PATTERNS = [
55298
55359
  {
@@ -61998,6 +62059,146 @@ function createNonInteractiveEnvHook(_ctx) {
61998
62059
  }
61999
62060
  };
62000
62061
  }
62062
+ // src/hooks/nudge-loop-breaker/hook.ts
62063
+ init_logger();
62064
+
62065
+ // src/hooks/nudge-loop-breaker/constants.ts
62066
+ var DEFAULT_LOOP_THRESHOLD = 3;
62067
+ var DEFAULT_COOLDOWN_MS = 1000;
62068
+ var MAX_BACKOFF_EXPONENT = 5;
62069
+ var DSML_PATTERNS = [/\uFF5C/g];
62070
+ var SESSION_ID_KEYS = ["sessionID", "sessionId", "session"];
62071
+ var TEXT_KEYS = [
62072
+ "text",
62073
+ "content",
62074
+ "message",
62075
+ "assistantText",
62076
+ "snapshot",
62077
+ "assistantMessageID"
62078
+ ];
62079
+ var TOOL_KEYS = ["tool", "toolName", "name"];
62080
+ var RESET_TOOL_NAMES = new Set(["compress"]);
62081
+ var DETECT_EVENT_TYPES = new Set(["session.next.step.ended", "session.next.step.failed"]);
62082
+
62083
+ // src/hooks/nudge-loop-breaker/session-state.ts
62084
+ function createNudgeLoopState() {
62085
+ return { count: 0, lastHash: null, lastAt: 0, lastEmitAt: 0, correctiveSent: false };
62086
+ }
62087
+ function computeBackoffDelay(count, threshold = DEFAULT_LOOP_THRESHOLD) {
62088
+ if (count < threshold)
62089
+ return 0;
62090
+ return 2 ** Math.min(count - threshold, MAX_BACKOFF_EXPONENT);
62091
+ }
62092
+ function hashText(text) {
62093
+ let hash2 = 2166136261;
62094
+ for (let i = 0;i < text.length; i++) {
62095
+ hash2 ^= text.charCodeAt(i);
62096
+ hash2 = Math.imul(hash2, 16777619);
62097
+ }
62098
+ return (hash2 >>> 0).toString(16);
62099
+ }
62100
+ function normalizeNudgeText2(text) {
62101
+ let normalized = text;
62102
+ for (const pattern of DSML_PATTERNS) {
62103
+ normalized = normalized.replace(pattern, "");
62104
+ }
62105
+ return normalized.replace(/\s+/g, " ").trim();
62106
+ }
62107
+
62108
+ // src/hooks/nudge-loop-breaker/hook.ts
62109
+ function asRecord(value) {
62110
+ if (typeof value === "object" && value !== null && !Array.isArray(value)) {
62111
+ return value;
62112
+ }
62113
+ return {};
62114
+ }
62115
+ function readString(props, keys) {
62116
+ for (const key of keys) {
62117
+ const value = props[key];
62118
+ if (typeof value === "string" && value.length > 0)
62119
+ return value;
62120
+ }
62121
+ return;
62122
+ }
62123
+ function isResetEvent(type, props) {
62124
+ if (type === "message.updated" || type === "session.message.updated") {
62125
+ return asRecord(props.info).role === "user";
62126
+ }
62127
+ if (type === "session.next.step.started") {
62128
+ return props.role === "user";
62129
+ }
62130
+ if (type === "session.next.tool.success") {
62131
+ const tool = readString(props, TOOL_KEYS);
62132
+ return tool !== undefined && RESET_TOOL_NAMES.has(tool);
62133
+ }
62134
+ return false;
62135
+ }
62136
+ function createNudgeLoopBreakerHook(ctx, options = {}) {
62137
+ const threshold = options.threshold ?? DEFAULT_LOOP_THRESHOLD;
62138
+ const cooldownMs = options.cooldownMs ?? DEFAULT_COOLDOWN_MS;
62139
+ const now = options.now ?? (() => Date.now());
62140
+ const onCorrective = options.onCorrective;
62141
+ const sessions = new Map;
62142
+ const sendCorrective = (sessionID, state3, backoff) => {
62143
+ log("[nudge-loop-breaker] Repeated nudge detected", { sessionID, backoff });
62144
+ onCorrective?.(sessionID, backoff);
62145
+ if (state3.correctiveSent || sessionID === "unknown")
62146
+ return;
62147
+ const prompt = ctx.client?.session?.prompt;
62148
+ if (typeof prompt !== "function")
62149
+ return;
62150
+ state3.correctiveSent = true;
62151
+ prompt({
62152
+ path: { id: sessionID },
62153
+ body: {
62154
+ parts: [
62155
+ {
62156
+ type: "text",
62157
+ text: "Repetition detected: stop echoing the prune/compress nudge. Continue the task or run the compress tool with real message IDs."
62158
+ }
62159
+ ]
62160
+ },
62161
+ query: { directory: ctx.directory }
62162
+ }).catch((error51) => {
62163
+ log("[nudge-loop-breaker] corrective prompt failed", { error: error51 });
62164
+ });
62165
+ };
62166
+ return {
62167
+ event: async ({ event }) => {
62168
+ const props = asRecord(event.properties);
62169
+ const sessionID = readString(props, SESSION_ID_KEYS) ?? "unknown";
62170
+ const state3 = sessions.get(sessionID) ?? createNudgeLoopState();
62171
+ sessions.set(sessionID, state3);
62172
+ if (isResetEvent(event.type, props)) {
62173
+ state3.count = 0;
62174
+ state3.lastHash = null;
62175
+ state3.lastAt = 0;
62176
+ state3.lastEmitAt = 0;
62177
+ state3.correctiveSent = false;
62178
+ return;
62179
+ }
62180
+ if (!DETECT_EVENT_TYPES.has(event.type))
62181
+ return;
62182
+ const text = readString(props, TEXT_KEYS);
62183
+ if (!text)
62184
+ return;
62185
+ const hash2 = hashText(normalizeNudgeText2(text));
62186
+ if (hash2 === state3.lastHash) {
62187
+ state3.count += 1;
62188
+ } else {
62189
+ state3.count = 1;
62190
+ state3.lastHash = hash2;
62191
+ }
62192
+ state3.lastAt = now();
62193
+ if (state3.count < threshold)
62194
+ return;
62195
+ if (now() - state3.lastEmitAt < cooldownMs)
62196
+ return;
62197
+ state3.lastEmitAt = now();
62198
+ sendCorrective(sessionID, state3, computeBackoffDelay(state3.count, threshold));
62199
+ }
62200
+ };
62201
+ }
62001
62202
  // src/hooks/oracle-md-only/constants.ts
62002
62203
  var HOOK_NAME12 = "oracle-md-only";
62003
62204
  var ORACLE_AGENT = "oracle";
@@ -63630,7 +63831,7 @@ async function resizeImage(base64DataUrl, mimeType, target, deps = {}) {
63630
63831
  // src/hooks/read-image-resizer/hook.ts
63631
63832
  var SUPPORTED_IMAGE_MIMES = new Set(["image/png", "image/jpeg", "image/gif", "image/webp"]);
63632
63833
  var TOKEN_DIVISOR = 750;
63633
- function asRecord(value) {
63834
+ function asRecord2(value) {
63634
63835
  if (!value || typeof value !== "object" || Array.isArray(value)) {
63635
63836
  return null;
63636
63837
  }
@@ -63647,7 +63848,7 @@ function extractImageAttachments(output) {
63647
63848
  }
63648
63849
  const attachments = [];
63649
63850
  for (const attachmentValue of attachmentsValue) {
63650
- const attachmentRecord = asRecord(attachmentValue);
63851
+ const attachmentRecord = asRecord2(attachmentValue);
63651
63852
  if (!attachmentRecord) {
63652
63853
  continue;
63653
63854
  }
@@ -69227,6 +69428,7 @@ function createContinuationHooks(args) {
69227
69428
  return enforcer;
69228
69429
  }) : null;
69229
69430
  const unstableAgentBabysitter = isHookEnabled("unstable-agent-babysitter") ? safeHook("unstable-agent-babysitter", () => createUnstableAgentBabysitter({ ctx, backgroundManager, pluginConfig })) : null;
69431
+ const nudgeLoopBreaker = isHookEnabled("nudge-loop-breaker") ? safeHook("nudge-loop-breaker", () => createNudgeLoopBreakerHook(ctx)) : null;
69230
69432
  if (sessionRecovery) {
69231
69433
  const onAbortCallbacks = [];
69232
69434
  const onRecoveryCompleteCallbacks = [];
@@ -69268,6 +69470,7 @@ function createContinuationHooks(args) {
69268
69470
  todoContinuationEnforcer,
69269
69471
  taskContinuationEnforcer,
69270
69472
  unstableAgentBabysitter,
69473
+ nudgeLoopBreaker,
69271
69474
  backgroundNotificationHook,
69272
69475
  architectHook,
69273
69476
  planPersister,
@@ -95983,6 +96186,7 @@ function createTransformHooks(args) {
95983
96186
  const envContextInjector = isHookEnabled("env-context-injector") ? safeCreateHook("env-context-injector", () => createEnvContextInjectorHook(), { enabled: safeHookEnabled }) : null;
95984
96187
  const thinkingBlockValidator = isHookEnabled("thinking-block-validator") ? safeCreateHook("thinking-block-validator", () => createThinkingBlockValidatorHook(), { enabled: safeHookEnabled }) : null;
95985
96188
  const toolPairValidator = isHookEnabled("tool-pair-validator") ? safeCreateHook("tool-pair-validator", () => createToolPairValidatorHook(), { enabled: safeHookEnabled }) : null;
96189
+ const dcpNudgeSanitizer = isHookEnabled("dcp-nudge-sanitizer") ? safeCreateHook("dcp-nudge-sanitizer", () => createDcpNudgeSanitizerHook(ctx), { enabled: safeHookEnabled }) : null;
95986
96190
  const designIntentPreserver = isHookEnabled("design-intent-preserver") ? safeCreateHook("design-intent-preserver", () => createDesignIntentPreserverHook(ctx), { enabled: safeHookEnabled }) : null;
95987
96191
  return {
95988
96192
  inputSecretGuard,
@@ -95991,6 +96195,7 @@ function createTransformHooks(args) {
95991
96195
  knowledgeHubInjector,
95992
96196
  envContextInjector,
95993
96197
  thinkingBlockValidator,
96198
+ dcpNudgeSanitizer,
95994
96199
  toolPairValidator,
95995
96200
  designIntentPreserver
95996
96201
  };
@@ -99538,6 +99743,36 @@ function resolveDcpCompressionMode() {
99538
99743
 
99539
99744
  // src/agents/dynamic-agent-prompt-builder.ts
99540
99745
  init_logger();
99746
+
99747
+ // src/agents/model-directives.ts
99748
+ var FAMILY_DIRECTIVES = {
99749
+ deepseek: {
99750
+ antiEcho: "Never echo injected instruction blocks verbatim. Never emit DSML/tool-call closing tags as plain text.",
99751
+ nudgeHandling: "If a context-compression nudge appears, call the compress tool once instead of restating the nudge."
99752
+ }
99753
+ };
99754
+ function resolveModelFamily(modelID) {
99755
+ if (!modelID)
99756
+ return;
99757
+ if (isDeepSeekModel(modelID))
99758
+ return "deepseek";
99759
+ return;
99760
+ }
99761
+ function getModelDirectives(modelID) {
99762
+ const family = resolveModelFamily(modelID);
99763
+ return family ? FAMILY_DIRECTIVES[family] ?? {} : {};
99764
+ }
99765
+ function appendModelDirective(section2, modelID) {
99766
+ const directives = getModelDirectives(modelID);
99767
+ const text = [directives.antiEcho, directives.nudgeHandling].filter(Boolean).join(" ");
99768
+ if (!text)
99769
+ return section2;
99770
+ return `${section2}
99771
+
99772
+ ${text}`;
99773
+ }
99774
+
99775
+ // src/agents/dynamic-agent-prompt-builder.ts
99541
99776
  function categorizeTools(toolNames) {
99542
99777
  return toolNames.map((name) => {
99543
99778
  let category = "other";
@@ -99954,40 +100189,40 @@ function readDisciplineFile() {
99954
100189
  <!-- discipline v${version4} Elastic-2.0 -->`;
99955
100190
  return { text, version: version4, path: p };
99956
100191
  }
99957
- function loadDiscipline(kind, hasGrepGlob = true, dcpMode = resolveDcpCompressionMode()) {
100192
+ function loadDiscipline(kind, hasGrepGlob = true, dcpMode = resolveDcpCompressionMode(), modelID) {
99958
100193
  const fallbackKey = `${hasGrepGlob ? "1" : "0"}:${dcpMode}`;
99959
100194
  const runtimeCache = kind === "full" ? cachedRuntimeFull : cachedRuntimeCompact;
99960
100195
  const fallbackCache = kind === "full" ? cachedFallbackFull : cachedFallbackCompact;
99961
100196
  const label = kind === "full" ? "context-discipline" : "compact-discipline";
99962
100197
  if (runtimeCache[fallbackKey])
99963
- return runtimeCache[fallbackKey];
100198
+ return appendModelDirective(runtimeCache[fallbackKey], modelID);
99964
100199
  if (fallbackCache[fallbackKey])
99965
- return fallbackCache[fallbackKey];
100200
+ return appendModelDirective(fallbackCache[fallbackKey], modelID);
99966
100201
  try {
99967
100202
  const { text, version: version4, path: path27 } = readDisciplineFile();
99968
100203
  runtimeCache[fallbackKey] = text;
99969
100204
  log(`${label} loaded`, { path: path27, version: version4 });
99970
- return text;
100205
+ return appendModelDirective(text, modelID);
99971
100206
  } catch (e) {
99972
100207
  log(`${label} fallback`, { error: String(e) });
99973
100208
  const fallback = kind === "full" ? fallbackFullDiscipline(hasGrepGlob, dcpMode) : fallbackCompactDiscipline(hasGrepGlob, dcpMode);
99974
100209
  fallbackCache[fallbackKey] = fallback;
99975
- return fallback;
100210
+ return appendModelDirective(fallback, modelID);
99976
100211
  }
99977
100212
  }
99978
- function loadContextModeDiscipline(hasGrepGlob = true, dcpMode = resolveDcpCompressionMode()) {
99979
- return loadDiscipline("full", hasGrepGlob, dcpMode);
100213
+ function loadContextModeDiscipline(hasGrepGlob = true, dcpMode = resolveDcpCompressionMode(), modelID) {
100214
+ return loadDiscipline("full", hasGrepGlob, dcpMode, modelID);
99980
100215
  }
99981
- function buildContextDisciplineSection(hasContextMode = false, hasGrepGlob = true, dcpMode = resolveDcpCompressionMode()) {
100216
+ function buildContextDisciplineSection(hasContextMode = false, hasGrepGlob = true, dcpMode = resolveDcpCompressionMode(), modelID) {
99982
100217
  if (!hasContextMode)
99983
100218
  return "";
99984
- return loadContextModeDiscipline(hasGrepGlob, dcpMode);
100219
+ return loadContextModeDiscipline(hasGrepGlob, dcpMode, modelID);
99985
100220
  }
99986
- function buildHeadroomSection(hasHeadroom = false, dcpMode = resolveDcpCompressionMode()) {
100221
+ function buildHeadroomSection(hasHeadroom = false, dcpMode = resolveDcpCompressionMode(), modelID) {
99987
100222
  if (!hasHeadroom)
99988
100223
  return "";
99989
100224
  const dcpClause = headroomDcpClause(dcpMode);
99990
- return `### Headroom Proxy Discipline (ALWAYS when headroom_* tools present)
100225
+ return appendModelDirective(`### Headroom Proxy Discipline (ALWAYS when headroom_* tools present)
99991
100226
 
99992
100227
  | Scenario | Tool / Action |
99993
100228
  |----------|----------------|
@@ -99995,17 +100230,17 @@ function buildHeadroomSection(hasHeadroom = false, dcpMode = resolveDcpCompressi
99995
100230
  | Stats / diagnostics | headroom_stats or headroom dashboard |
99996
100231
  | Proxy not running | headroom doctor / check http://127.0.0.1:8787 (HEADROOM_PROXY_URL) |
99997
100232
 
99998
- **Headroom L4 is transport-level (CacheAligner->ContentRouter->CCR). It complements L1 RTK, L2 context-mode, L3 DCP \u2014 do not duplicate their discipline. Compression ownership: the \`compress\` tool is DCP's \u2014 never call it bare without message IDs or invoke /dcp-compress; proactive on closed sections with IDs + headroom/ctx_stats signals.${dcpClause}**`;
100233
+ **Headroom L4 is transport-level (CacheAligner->ContentRouter->CCR). It complements L1 RTK, L2 context-mode, L3 DCP \u2014 do not duplicate their discipline. Compression ownership: the \`compress\` tool is DCP's \u2014 never call it bare without message IDs or invoke /dcp-compress; proactive on closed sections with IDs + headroom/ctx_stats signals.${dcpClause}**`, modelID);
99999
100234
  }
100000
- function loadCompactContextDiscipline(hasGrepGlob = true, dcpMode = resolveDcpCompressionMode()) {
100001
- return loadDiscipline("compact", hasGrepGlob, dcpMode);
100235
+ function loadCompactContextDiscipline(hasGrepGlob = true, dcpMode = resolveDcpCompressionMode(), modelID) {
100236
+ return loadDiscipline("compact", hasGrepGlob, dcpMode, modelID);
100002
100237
  }
100003
- function buildCompactContextDisciplineSection(hasContextMode = false, hasGrepGlob = true, dcpMode = resolveDcpCompressionMode()) {
100238
+ function buildCompactContextDisciplineSection(hasContextMode = false, hasGrepGlob = true, dcpMode = resolveDcpCompressionMode(), modelID) {
100004
100239
  if (!hasContextMode)
100005
100240
  return "";
100006
- return loadCompactContextDiscipline(hasGrepGlob, dcpMode);
100241
+ return loadCompactContextDiscipline(hasGrepGlob, dcpMode, modelID);
100007
100242
  }
100008
- function buildExploreDisciplineSection(hasContextMode = false, hasHeadroom = false, hasGrepGlob = true) {
100243
+ function buildExploreDisciplineSection(hasContextMode = false, hasHeadroom = false, hasGrepGlob = true, modelID) {
100009
100244
  if (!hasContextMode && !hasHeadroom)
100010
100245
  return "";
100011
100246
  const parts = [];
@@ -100015,9 +100250,9 @@ function buildExploreDisciplineSection(hasContextMode = false, hasHeadroom = fal
100015
100250
  if (hasHeadroom) {
100016
100251
  parts.push("Use headroom_retrieve / headroom_search for compressed history \u2014 NEVER re-read full history.");
100017
100252
  }
100018
- return `### Context Discipline (when available)
100253
+ return appendModelDirective(`### Context Discipline (when available)
100019
100254
 
100020
- ${parts.join(" ")}`;
100255
+ ${parts.join(" ")}`, modelID);
100021
100256
  }
100022
100257
 
100023
100258
  // src/agents/architect/default.ts
@@ -101429,7 +101664,7 @@ function buildTodoDisciplineSection(useTaskSystem) {
101429
101664
 
101430
101665
  **NO TODOS ON MULTI-STEP WORK = INCOMPLETE WORK.**`;
101431
101666
  }
101432
- function buildKeymakerPrompt(availableAgents = [], availableTools = [], availableSkills = [], availableCategories = [], useTaskSystem = false) {
101667
+ function buildKeymakerPrompt(availableAgents = [], availableTools = [], availableSkills = [], availableCategories = [], useTaskSystem = false, modelID) {
101433
101668
  const keyTriggers = buildKeyTriggersSection(availableAgents, availableSkills);
101434
101669
  const toolSelection = buildToolSelectionTable(availableAgents, availableTools, availableSkills);
101435
101670
  const exploreSection = buildExploreSection(availableAgents);
@@ -101441,9 +101676,9 @@ function buildKeymakerPrompt(availableAgents = [], availableTools = [], availabl
101441
101676
  const antiPatterns = buildAntiPatternsSection();
101442
101677
  const hasContextMode = availableTools.some((t) => t.name.startsWith("ctx_"));
101443
101678
  const hasGrepGlob = resolveGrepGlobUsable(availableTools.map((t) => t.name));
101444
- const contextDiscipline = buildContextDisciplineSection(hasContextMode, hasGrepGlob);
101679
+ const contextDiscipline = buildContextDisciplineSection(hasContextMode, hasGrepGlob, undefined, modelID);
101445
101680
  const hasHeadroom = availableTools.some((t) => t.name.startsWith("headroom_"));
101446
- const headroomDiscipline = buildHeadroomSection(hasHeadroom);
101681
+ const headroomDiscipline = buildHeadroomSection(hasHeadroom, undefined, modelID);
101447
101682
  const todoDiscipline = buildTodoDisciplineSection(useTaskSystem);
101448
101683
  return `You are Keymaker, an autonomous deep worker for software engineering.
101449
101684
 
@@ -101929,7 +102164,7 @@ function createKeymakerAgent(model, availableAgents, availableToolNames, availab
101929
102164
  const tools = availableToolNames ? categorizeTools(availableToolNames) : [];
101930
102165
  const skills = availableSkills ?? [];
101931
102166
  const categories = availableCategories ?? [];
101932
- const prompt = availableAgents ? buildKeymakerPrompt(availableAgents, tools, skills, categories, useTaskSystem) : buildKeymakerPrompt([], tools, skills, categories, useTaskSystem);
102167
+ const prompt = availableAgents ? buildKeymakerPrompt(availableAgents, tools, skills, categories, useTaskSystem, model) : buildKeymakerPrompt([], tools, skills, categories, useTaskSystem, model);
101933
102168
  return {
101934
102169
  description: "Autonomous Deep Worker - goal-oriented execution with GPT 5.2 Codex. Explores thoroughly before acting, uses explore/librarian agents for comprehensive context, completes tasks end-to-end. Inspired by AmpCode deep mode. (Keymaker - Matrixx)",
101935
102170
  mode: MODE3,
@@ -102113,7 +102348,7 @@ Should I proceed with [recommendation], or would you prefer differently?
102113
102348
  \`\`\`
102114
102349
  </Task_Management>`;
102115
102350
  }
102116
- function buildDynamicMorpheusPrompt(availableAgents, availableTools = [], availableSkills = [], availableCategories = [], useTaskSystem = false) {
102351
+ function buildDynamicMorpheusPrompt(availableAgents, availableTools = [], availableSkills = [], availableCategories = [], useTaskSystem = false, modelID) {
102117
102352
  const keyTriggers = buildKeyTriggersSection(availableAgents, availableSkills);
102118
102353
  const toolSelection = buildToolSelectionTable(availableAgents, availableTools, availableSkills);
102119
102354
  const exploreSection = buildExploreSection(availableAgents);
@@ -102126,9 +102361,9 @@ function buildDynamicMorpheusPrompt(availableAgents, availableTools = [], availa
102126
102361
  const antiPatterns = buildAntiPatternsSection();
102127
102362
  const hasContextMode = availableTools.some((t) => t.name.startsWith("ctx_"));
102128
102363
  const hasGrepGlob = resolveGrepGlobUsable(availableTools.map((t) => t.name));
102129
- const contextDiscipline = buildContextDisciplineSection(hasContextMode, hasGrepGlob);
102364
+ const contextDiscipline = buildContextDisciplineSection(hasContextMode, hasGrepGlob, undefined, modelID);
102130
102365
  const hasHeadroom = availableTools.some((t) => t.name.startsWith("headroom_"));
102131
- const headroomDiscipline = buildHeadroomSection(hasHeadroom);
102366
+ const headroomDiscipline = buildHeadroomSection(hasHeadroom, undefined, modelID);
102132
102367
  const taskManagementSection = buildTaskManagementSection(useTaskSystem);
102133
102368
  const todoHookNote = useTaskSystem ? "YOUR TASK CREATION WOULD BE TRACKED BY HOOK([SYSTEM REMINDER - TASK CONTINUATION])" : "YOUR TODO CREATION WOULD BE TRACKED BY HOOK([SYSTEM REMINDER - TODO CONTINUATION])";
102134
102369
  return `<Role>
@@ -102492,7 +102727,7 @@ function createMorpheusAgent(model, availableAgents, availableToolNames, availab
102492
102727
  const tools = availableToolNames ? categorizeTools(availableToolNames) : [];
102493
102728
  const skills = availableSkills ?? [];
102494
102729
  const categories = availableCategories ?? [];
102495
- const prompt = availableAgents ? buildDynamicMorpheusPrompt(availableAgents, tools, skills, categories, useTaskSystem) : buildDynamicMorpheusPrompt([], tools, skills, categories, useTaskSystem);
102730
+ const prompt = availableAgents ? buildDynamicMorpheusPrompt(availableAgents, tools, skills, categories, useTaskSystem, model) : buildDynamicMorpheusPrompt([], tools, skills, categories, useTaskSystem, model);
102496
102731
  const permission = { question: "allow" };
102497
102732
  const base = {
102498
102733
  description: "Powerful AI orchestrator. Plans obsessively with todos, assesses search complexity before exploration, delegates strategically via category+skills combinations. Uses explore for internal code (parallel-friendly), librarian for external docs. (Morpheus - Matrixx)",
@@ -106120,12 +106355,13 @@ function injectContextDiscipline(availableToolNames, agents, contextMode) {
106120
106355
  if (cfg.prompt.includes("Context Discipline") || cfg.prompt.includes("context-mode \u2014 MANDATORY routing rules"))
106121
106356
  continue;
106122
106357
  const normalized = name.toLowerCase();
106358
+ const modelID = typeof cfg.model === "string" ? cfg.model : undefined;
106123
106359
  let discipline = "";
106124
106360
  if (exploreAgents.has(normalized) || normalized === "oracle") {
106125
- discipline = buildExploreDisciplineSection(hasContextMode, hasHeadroom, hasGrepGlob);
106361
+ discipline = buildExploreDisciplineSection(hasContextMode, hasHeadroom, hasGrepGlob, modelID);
106126
106362
  } else {
106127
- const compact = buildCompactContextDisciplineSection(hasContextMode, hasGrepGlob);
106128
- const headroom = buildHeadroomSection(hasHeadroom);
106363
+ const compact = buildCompactContextDisciplineSection(hasContextMode, hasGrepGlob, undefined, modelID);
106364
+ const headroom = buildHeadroomSection(hasHeadroom, undefined, modelID);
106129
106365
  discipline = [compact, headroom].filter(Boolean).join(`
106130
106366
 
106131
106367
  `);
@@ -107295,12 +107531,13 @@ var HookNameEnum = exports_external.enum([
107295
107531
  "evolution-compressor",
107296
107532
  "evolution-hitl",
107297
107533
  "context-mode-enforcer",
107298
- "context-mode-enforcer",
107299
107534
  "task-edit-guard",
107300
107535
  "document-reader-guard",
107301
107536
  "knowledge-hub-guard",
107302
107537
  "knowledge-hub-injector",
107303
- "knowledge-hub-search-nudge"
107538
+ "knowledge-hub-search-nudge",
107539
+ "dcp-nudge-sanitizer",
107540
+ "nudge-loop-breaker"
107304
107541
  ]);
107305
107542
  var HookNameSchema = exports_external.union([
107306
107543
  HookNameEnum,
@@ -107847,6 +108084,9 @@ function createChatMessageHandler2(args) {
107847
108084
  if (input.agent) {
107848
108085
  setSessionAgent(input.sessionID, input.agent);
107849
108086
  }
108087
+ if (input.model) {
108088
+ setSessionModel(input.sessionID, `${input.model.providerID}/${input.model.modelID}`);
108089
+ }
107850
108090
  const message = output.message;
107851
108091
  if (firstMessageVariantGate.shouldOverride(input.sessionID)) {
107852
108092
  if (message.variant === undefined) {
@@ -107963,6 +108203,7 @@ function createChatParamsHandler(args) {
107963
108203
  return;
107964
108204
  if (!isChatParamsOutput(output))
107965
108205
  return;
108206
+ setSessionModel(normalizedInput.sessionID, `${normalizedInput.model.providerID}/${normalizedInput.model.modelID}`);
107966
108207
  await args.anthropicEffort?.["chat.params"]?.(normalizedInput, output);
107967
108208
  const categoryTemp = getSessionTemperature(normalizedInput.sessionID);
107968
108209
  if (categoryTemp !== undefined) {
@@ -108021,6 +108262,7 @@ function createEventHandler2(args) {
108021
108262
  await Promise.resolve(hooks2.todoContinuationEnforcer?.handler?.(input));
108022
108263
  await Promise.resolve(hooks2.taskContinuationEnforcer?.handler?.(input));
108023
108264
  await Promise.resolve(hooks2.unstableAgentBabysitter?.event?.(input));
108265
+ await Promise.resolve(hooks2.nudgeLoopBreaker?.event?.(input));
108024
108266
  await Promise.resolve(hooks2.contextWindowMonitor?.event?.(input));
108025
108267
  await Promise.resolve(hooks2.directoryAgentsInjector?.event?.(input));
108026
108268
  await Promise.resolve(hooks2.rulesInjector?.event?.(input));
@@ -108138,6 +108380,7 @@ function createMessagesTransformHandler(args) {
108138
108380
  await args.hooks.envContextInjector?.["experimental.chat.messages.transform"]?.(input, output);
108139
108381
  await args.hooks.thinkingBlockValidator?.["experimental.chat.messages.transform"]?.(input, output);
108140
108382
  await args.hooks.evolutionHitl?.["experimental.chat.messages.transform"]?.(input, output);
108383
+ await args.hooks.dcpNudgeSanitizer?.["experimental.chat.messages.transform"]?.(input, output);
108141
108384
  await args.hooks.toolPairValidator?.["experimental.chat.messages.transform"]?.(input, output);
108142
108385
  };
108143
108386
  }
@@ -222,7 +222,9 @@
222
222
  "document-reader-guard",
223
223
  "knowledge-hub-guard",
224
224
  "knowledge-hub-injector",
225
- "knowledge-hub-search-nudge"
225
+ "knowledge-hub-search-nudge",
226
+ "dcp-nudge-sanitizer",
227
+ "nudge-loop-breaker"
226
228
  ]
227
229
  },
228
230
  {}
@@ -4013,6 +4015,42 @@
4013
4015
  }
4014
4016
  ]
4015
4017
  },
4018
+ "modelMaxLimits": {
4019
+ "type": "object",
4020
+ "propertyNames": {
4021
+ "type": "string",
4022
+ "pattern": "^[^/]+\\/[^/]+$"
4023
+ },
4024
+ "additionalProperties": {
4025
+ "anyOf": [
4026
+ {
4027
+ "type": "number"
4028
+ },
4029
+ {
4030
+ "type": "string",
4031
+ "pattern": "^\\d+%$"
4032
+ }
4033
+ ]
4034
+ }
4035
+ },
4036
+ "modelMinLimits": {
4037
+ "type": "object",
4038
+ "propertyNames": {
4039
+ "type": "string",
4040
+ "pattern": "^[^/]+\\/[^/]+$"
4041
+ },
4042
+ "additionalProperties": {
4043
+ "anyOf": [
4044
+ {
4045
+ "type": "number"
4046
+ },
4047
+ {
4048
+ "type": "string",
4049
+ "pattern": "^\\d+%$"
4050
+ }
4051
+ ]
4052
+ }
4053
+ },
4016
4054
  "nudgeFrequency": {
4017
4055
  "type": "integer",
4018
4056
  "minimum": 1,
@@ -1,6 +1,6 @@
1
1
  import type { HookName, MatrixxConfig } from "../../config";
2
2
  import type { BackgroundManager } from "../../features/background-agent";
3
- import { createArchitectHook, createBackgroundNotificationHook, createCompactionContextInjector, createCompactionTodoPreserverHook, createEvolutionCompressorHook, createEvolutionHitlHook, createPlanPersister, createStopContinuationGuardHook, createTaskContinuationEnforcer, createTodoContinuationEnforcer } from "../../hooks";
3
+ import { createArchitectHook, createBackgroundNotificationHook, createCompactionContextInjector, createCompactionTodoPreserverHook, createEvolutionCompressorHook, createEvolutionHitlHook, createNudgeLoopBreakerHook, createPlanPersister, createStopContinuationGuardHook, createTaskContinuationEnforcer, createTodoContinuationEnforcer } from "../../hooks";
4
4
  import type { PluginContext } from "../types";
5
5
  import { createUnstableAgentBabysitter } from "../unstable-agent-babysitter";
6
6
  export type ContinuationHooks = {
@@ -10,6 +10,7 @@ export type ContinuationHooks = {
10
10
  todoContinuationEnforcer: ReturnType<typeof createTodoContinuationEnforcer> | null;
11
11
  taskContinuationEnforcer: ReturnType<typeof createTaskContinuationEnforcer> | null;
12
12
  unstableAgentBabysitter: ReturnType<typeof createUnstableAgentBabysitter> | null;
13
+ nudgeLoopBreaker: ReturnType<typeof createNudgeLoopBreakerHook> | null;
13
14
  backgroundNotificationHook: ReturnType<typeof createBackgroundNotificationHook> | null;
14
15
  architectHook: ReturnType<typeof createArchitectHook> | null;
15
16
  planPersister: ReturnType<typeof createPlanPersister> | null;
@@ -12,6 +12,7 @@ export declare function createCoreHooks(args: {
12
12
  knowledgeHubInjector: ReturnType<typeof import("../../hooks").createKnowledgeHubInjectorHook> | null;
13
13
  envContextInjector: ReturnType<typeof import("../../hooks").createEnvContextInjectorHook> | null;
14
14
  thinkingBlockValidator: ReturnType<typeof import("../../hooks").createThinkingBlockValidatorHook> | null;
15
+ dcpNudgeSanitizer: ReturnType<typeof import("../../hooks").createDcpNudgeSanitizerHook> | null;
15
16
  toolPairValidator: ReturnType<typeof import("../../hooks").createToolPairValidatorHook> | null;
16
17
  designIntentPreserver: ReturnType<typeof import("../../hooks").createDesignIntentPreserverHook> | null;
17
18
  backgroundTaskBlocker: ReturnType<typeof import("../../hooks").createBackgroundTaskBlockerHook> | null;
@@ -1,6 +1,7 @@
1
1
  import type { MatrixxConfig } from "../../config";
2
2
  import { createContextInjectorMessagesTransformHook } from "../../features/context-injector";
3
3
  import { createDesignIntentPreserverHook, createEnvContextInjectorHook, createKeywordDetectorHook, createKnowledgeHubInjectorHook, createThinkingBlockValidatorHook, createToolPairValidatorHook } from "../../hooks";
4
+ import { createDcpNudgeSanitizerHook } from "../../hooks/dcp-nudge-sanitizer";
4
5
  import { createInputSecretGuardHook } from "../../hooks/input-secret-guard";
5
6
  import type { PluginContext } from "../types";
6
7
  export type TransformHooks = {
@@ -10,6 +11,7 @@ export type TransformHooks = {
10
11
  knowledgeHubInjector: ReturnType<typeof createKnowledgeHubInjectorHook> | null;
11
12
  envContextInjector: ReturnType<typeof createEnvContextInjectorHook> | null;
12
13
  thinkingBlockValidator: ReturnType<typeof createThinkingBlockValidatorHook> | null;
14
+ dcpNudgeSanitizer: ReturnType<typeof createDcpNudgeSanitizerHook> | null;
13
15
  toolPairValidator: ReturnType<typeof createToolPairValidatorHook> | null;
14
16
  designIntentPreserver: ReturnType<typeof createDesignIntentPreserverHook> | null;
15
17
  };
@@ -5,6 +5,18 @@ export interface DcpSwitchProfileOptions {
5
5
  };
6
6
  }
7
7
  export declare function deepMergeProfile(builtin: Record<string, unknown>, override: Record<string, unknown>): Record<string, unknown>;
8
+ /**
9
+ * Build a full inline DCP PluginConfig object for the given profile.
10
+ * Starts with base values from dcpConfig.base, overlays profile-specific
11
+ * values from dcpConfig.profiles[profile], and falls back to built-in
12
+ * profile definitions when no Matrixx config is provided.
13
+ *
14
+ * NOTE: DCP also supports per-model context limits via modelMaxLimits
15
+ * and modelMinLimits in the compress section. These allow fine-grained
16
+ * control per LLM provider/model (e.g., {"anthropic/claude-sonnet-4-20250514": "80%"}).
17
+ * Exposed via DcpCompressOverrideSchema and propagated below.
18
+ */
19
+ export declare function buildInlineConfig(profile: string, options?: DcpSwitchProfileOptions): Record<string, unknown>;
8
20
  /**
9
21
  * Switch the active DCP profile.
10
22
  * Reads profile parameters from the Matrixx plugin configuration and writes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-matrixx",
3
- "version": "2.6.12",
3
+ "version": "2.6.13",
4
4
  "description": "The Best AI Agent Harness - Batteries-Included OpenCode Plugin with Multi-Model Orchestration, Parallel Background Agents, and Crafted LSP/AST Tools",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};