@pellux/goodvibes-sdk 0.33.27 → 0.33.29

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.
@@ -3,7 +3,7 @@
3
3
  "product": {
4
4
  "id": "goodvibes",
5
5
  "surface": "operator",
6
- "version": "0.33.27"
6
+ "version": "0.33.29"
7
7
  },
8
8
  "auth": {
9
9
  "modes": [
@@ -14,6 +14,12 @@ export type OpsEvent =
14
14
  type: 'OPS_CONTEXT_WARNING';
15
15
  usage: number;
16
16
  threshold: number;
17
+ currentTokens?: number | undefined;
18
+ contextWindow?: number | undefined;
19
+ thresholdTokens?: number | undefined;
20
+ remainingTokens?: number | undefined;
21
+ safetyBufferTokens?: number | undefined;
22
+ reason?: 'threshold' | 'safety-buffer' | undefined;
17
23
  }
18
24
  /** Cache hit-rate and token metrics snapshot. */
19
25
  | {
@@ -1 +1 @@
1
- {"version":3,"file":"ops.d.ts","sourceRoot":"","sources":["../../src/events/ops.ts"],"names":[],"mappings":"AAAA,qFAAqF;AAErF;;;;;;GAMG;AAEH,+CAA+C;AAC/C,MAAM,MAAM,qBAAqB,GAC7B,gBAAgB,GAChB,YAAY,GACZ,WAAW,GACX,YAAY,GACZ,WAAW,GACX,kBAAkB,CAAC;AAEvB,MAAM,MAAM,QAAQ;AAClB,iDAAiD;AAC/C;IACE,IAAI,EAAE,qBAAqB,CAAC;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;CACnB;AACH,iDAAiD;GAC/C;IACE,IAAI,EAAE,mBAAmB,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC;CACf;AACH,8CAA8C;GAC5C;IACE,IAAI,EAAE,kBAAkB,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;CACf;AACH,mDAAmD;GACjD;IACE,IAAI,EAAE,oBAAoB,CAAC;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,qBAAqB,CAAC;IAC9B,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC3B;AACH,+DAA+D;GAC7D;IACE,IAAI,EAAE,iBAAiB,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,qBAAqB,CAAC;IAC9B,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC3B;AACH,uCAAuC;GACrC;IACE,IAAI,EAAE,kBAAkB,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,qBAAqB,CAAC;IAC9B,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC3B;AACH,mDAAmD;GACjD;IACE,IAAI,EAAE,kBAAkB,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,qBAAqB,CAAC;IAC9B,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC3B;AACH,0CAA0C;GACxC;IACE,IAAI,EAAE,qBAAqB,CAAC;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,qBAAqB,CAAC;IAC9B,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC3B;AACH,kDAAkD;GAChD;IACE,IAAI,EAAE,WAAW,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC;IAC7B,MAAM,EAAE,qBAAqB,CAAC;IAC9B,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,OAAO,EAAE,SAAS,GAAG,UAAU,GAAG,OAAO,CAAC;IAC1C,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACnC;AACH,wFAAwF;GACtF;IACE,IAAI,EAAE,0BAA0B,CAAC;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEN,8CAA8C;AAC9C,MAAM,MAAM,YAAY,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC"}
1
+ {"version":3,"file":"ops.d.ts","sourceRoot":"","sources":["../../src/events/ops.ts"],"names":[],"mappings":"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"}
@@ -15,7 +15,7 @@
15
15
  * Public API:
16
16
  * estimateConversationTokens(messages) — rough token count for a message array
17
17
  * estimateTokens(text) — rough token count for a string
18
- * shouldAutoCompact(opts) — check if 15k token buffer threshold is exceeded
18
+ * shouldAutoCompact(opts) — check if configured usage threshold or safety buffer is exceeded
19
19
  * compactSmallWindow(messages, keepRecent) — simplified compaction for small context windows
20
20
  * compactMessages(ctx, registry) — structured compaction entry point
21
21
  * checkAndCompact(autoOpts, ctx) — check and compact if threshold exceeded
@@ -33,6 +33,21 @@ export interface AutoCompactOptions {
33
33
  contextWindow: number;
34
34
  /** Whether auto-compact is already in progress (prevent re-entry). */
35
35
  isCompacting: boolean;
36
+ /** Usage percentage that triggers compaction. Defaults to 80. */
37
+ thresholdPercent?: number | undefined;
38
+ /** Remaining-token safety buffer that also triggers compaction. Defaults to 15000. */
39
+ minRemainingTokens?: number | undefined;
40
+ }
41
+ export interface AutoCompactDecision {
42
+ readonly shouldCompact: boolean;
43
+ readonly reason: 'threshold' | 'safety-buffer' | null;
44
+ readonly currentTokens: number;
45
+ readonly contextWindow: number;
46
+ readonly usagePct: number;
47
+ readonly thresholdPercent: number;
48
+ readonly thresholdTokens: number;
49
+ readonly remainingTokens: number;
50
+ readonly safetyBufferTokens: number;
36
51
  }
37
52
  /**
38
53
  * Tokens remaining in the context window at which auto-compaction triggers.
@@ -40,6 +55,7 @@ export interface AutoCompactOptions {
40
55
  * 15k gives room for the ~6.5k compaction output + LLM extraction calls.
41
56
  */
42
57
  export declare const COMPACTION_BUFFER_TOKENS = 15000;
58
+ export declare const DEFAULT_AUTO_COMPACT_THRESHOLD_PERCENT = 80;
43
59
  /**
44
60
  * Context windows smaller than this use simplified compaction (summarize last N messages)
45
61
  * instead of the full structured output, since there isn't enough room for extraction calls.
@@ -50,13 +66,11 @@ export declare function getLastCompactionEvent(): CompactionEvent | null;
50
66
  /** Rough token estimate: 4 chars ≈ 1 token. Used for threshold checks. */
51
67
  export declare function estimateConversationTokens(messages: ProviderMessage[]): number;
52
68
  export { estimateTokens } from './compaction-types.js';
69
+ export declare function getAutoCompactDecision(opts: AutoCompactOptions): AutoCompactDecision;
53
70
  /**
54
- * Returns true if the remaining context window is within COMPACTION_BUFFER_TOKENS
55
- * and compaction has not already been triggered.
56
- *
57
- * Triggers when: contextWindow - currentTokens <= 15000
58
- * The 15k buffer gives room for the ~6.5k compaction output + LLM extraction calls
59
- * + post-compaction work before the window is exhausted.
71
+ * Returns true when context usage reaches the configured percentage threshold
72
+ * or the remaining-token safety buffer is exhausted, unless compaction is
73
+ * already active.
60
74
  */
61
75
  export declare function shouldAutoCompact(opts: AutoCompactOptions): boolean;
62
76
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"context-compaction.d.ts","sourceRoot":"","sources":["../../../src/platform/core/context-compaction.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,eAAe,EAA4B,MAAM,2BAA2B,CAAC;AAC3F,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAEjE,OAAO,KAAK,EAEV,iBAAiB,EACjB,gBAAgB,EAChB,eAAe,EAEhB,MAAM,uBAAuB,CAAC;AAkB/B,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAElG,MAAM,WAAW,kBAAkB;IACjC,wDAAwD;IACxD,aAAa,EAAE,MAAM,CAAC;IACtB,oDAAoD;IACpD,aAAa,EAAE,MAAM,CAAC;IACtB,sEAAsE;IACtE,YAAY,EAAE,OAAO,CAAC;CACvB;AAMD;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,QAAS,CAAC;AAE/C;;;GAGG;AACH,eAAO,MAAM,sBAAsB,QAAS,CAAC;AAQ7C,wBAAgB,mBAAmB,IAAI,SAAS,eAAe,EAAE,CAEhE;AAED,wBAAgB,sBAAsB,IAAI,eAAe,GAAG,IAAI,CAI/D;AAMD,0EAA0E;AAC1E,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,eAAe,EAAE,GAAG,MAAM,CAc9E;AAED,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAMvD;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAInE;AAMD;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,eAAe,EAAE,EAC3B,UAAU,SAAK,GACd,eAAe,EAAE,CAanB;AAuHD;;GAEG;AACH,wBAAsB,eAAe,CACnC,GAAG,EAAE,iBAAiB,EACtB,QAAQ,EAAE,gBAAgB,GACzB,OAAO,CAAC,gBAAgB,CAAC,CAE3B;AAmPD;;;;GAIG;AACH,wBAAsB,eAAe,CACnC,QAAQ,EAAE,kBAAkB,EAC5B,GAAG,EAAE,iBAAiB,EACtB,QAAQ,EAAE,gBAAgB,GACzB,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAOlC"}
1
+ {"version":3,"file":"context-compaction.d.ts","sourceRoot":"","sources":["../../../src/platform/core/context-compaction.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,eAAe,EAA4B,MAAM,2BAA2B,CAAC;AAC3F,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAEjE,OAAO,KAAK,EAEV,iBAAiB,EACjB,gBAAgB,EAChB,eAAe,EAEhB,MAAM,uBAAuB,CAAC;AAkB/B,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAElG,MAAM,WAAW,kBAAkB;IACjC,wDAAwD;IACxD,aAAa,EAAE,MAAM,CAAC;IACtB,oDAAoD;IACpD,aAAa,EAAE,MAAM,CAAC;IACtB,sEAAsE;IACtE,YAAY,EAAE,OAAO,CAAC;IACtB,iEAAiE;IACjE,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,sFAAsF;IACtF,kBAAkB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACzC;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC,QAAQ,CAAC,MAAM,EAAE,WAAW,GAAG,eAAe,GAAG,IAAI,CAAC;IACtD,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;CACrC;AAMD;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,QAAS,CAAC;AAC/C,eAAO,MAAM,sCAAsC,KAAK,CAAC;AAEzD;;;GAGG;AACH,eAAO,MAAM,sBAAsB,QAAS,CAAC;AAQ7C,wBAAgB,mBAAmB,IAAI,SAAS,eAAe,EAAE,CAEhE;AAED,wBAAgB,sBAAsB,IAAI,eAAe,GAAG,IAAI,CAI/D;AAMD,0EAA0E;AAC1E,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,eAAe,EAAE,GAAG,MAAM,CAc9E;AAED,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAYvD,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,kBAAkB,GAAG,mBAAmB,CA6BpF;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAEnE;AAMD;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,eAAe,EAAE,EAC3B,UAAU,SAAK,GACd,eAAe,EAAE,CAanB;AAuHD;;GAEG;AACH,wBAAsB,eAAe,CACnC,GAAG,EAAE,iBAAiB,EACtB,QAAQ,EAAE,gBAAgB,GACzB,OAAO,CAAC,gBAAgB,CAAC,CAE3B;AAmPD;;;;GAIG;AACH,wBAAsB,eAAe,CACnC,QAAQ,EAAE,kBAAkB,EAC5B,GAAG,EAAE,iBAAiB,EACtB,QAAQ,EAAE,gBAAgB,GACzB,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAOlC"}
@@ -15,7 +15,7 @@
15
15
  * Public API:
16
16
  * estimateConversationTokens(messages) — rough token count for a message array
17
17
  * estimateTokens(text) — rough token count for a string
18
- * shouldAutoCompact(opts) — check if 15k token buffer threshold is exceeded
18
+ * shouldAutoCompact(opts) — check if configured usage threshold or safety buffer is exceeded
19
19
  * compactSmallWindow(messages, keepRecent) — simplified compaction for small context windows
20
20
  * compactMessages(ctx, registry) — structured compaction entry point
21
21
  * checkAndCompact(autoOpts, ctx) — check and compact if threshold exceeded
@@ -35,6 +35,7 @@ import { buildHandoffHeader, buildSessionMemories, buildCurrentTask, buildRunnin
35
35
  * 15k gives room for the ~6.5k compaction output + LLM extraction calls.
36
36
  */
37
37
  export const COMPACTION_BUFFER_TOKENS = 15_000;
38
+ export const DEFAULT_AUTO_COMPACT_THRESHOLD_PERCENT = 80;
38
39
  /**
39
40
  * Context windows smaller than this use simplified compaction (summarize last N messages)
40
41
  * instead of the full structured output, since there isn't enough room for extraction calls.
@@ -76,19 +77,49 @@ export { estimateTokens } from './compaction-types.js';
76
77
  // ---------------------------------------------------------------------------
77
78
  // Should compact?
78
79
  // ---------------------------------------------------------------------------
80
+ function normalizeThresholdPercent(value) {
81
+ if (value === undefined)
82
+ return DEFAULT_AUTO_COMPACT_THRESHOLD_PERCENT;
83
+ if (!Number.isFinite(value))
84
+ return DEFAULT_AUTO_COMPACT_THRESHOLD_PERCENT;
85
+ return Math.max(0, Math.min(100, value));
86
+ }
87
+ export function getAutoCompactDecision(opts) {
88
+ const currentTokens = Math.max(0, opts.currentTokens);
89
+ const contextWindow = Math.max(0, opts.contextWindow);
90
+ const thresholdPercent = normalizeThresholdPercent(opts.thresholdPercent);
91
+ const safetyBufferTokens = Math.max(0, opts.minRemainingTokens ?? COMPACTION_BUFFER_TOKENS);
92
+ const usagePct = contextWindow > 0 ? (currentTokens / contextWindow) * 100 : 0;
93
+ const thresholdTokens = contextWindow > 0 ? Math.floor((contextWindow * thresholdPercent) / 100) : 0;
94
+ const remainingTokens = Math.max(0, contextWindow - currentTokens);
95
+ let reason = null;
96
+ if (!opts.isCompacting && contextWindow > 0 && thresholdPercent > 0) {
97
+ if (currentTokens >= thresholdTokens) {
98
+ reason = 'threshold';
99
+ }
100
+ else if (remainingTokens <= safetyBufferTokens) {
101
+ reason = 'safety-buffer';
102
+ }
103
+ }
104
+ return {
105
+ shouldCompact: reason !== null,
106
+ reason,
107
+ currentTokens,
108
+ contextWindow,
109
+ usagePct,
110
+ thresholdPercent,
111
+ thresholdTokens,
112
+ remainingTokens,
113
+ safetyBufferTokens,
114
+ };
115
+ }
79
116
  /**
80
- * Returns true if the remaining context window is within COMPACTION_BUFFER_TOKENS
81
- * and compaction has not already been triggered.
82
- *
83
- * Triggers when: contextWindow - currentTokens <= 15000
84
- * The 15k buffer gives room for the ~6.5k compaction output + LLM extraction calls
85
- * + post-compaction work before the window is exhausted.
117
+ * Returns true when context usage reaches the configured percentage threshold
118
+ * or the remaining-token safety buffer is exhausted, unless compaction is
119
+ * already active.
86
120
  */
87
121
  export function shouldAutoCompact(opts) {
88
- const { currentTokens, contextWindow, isCompacting } = opts;
89
- if (isCompacting || contextWindow <= 0)
90
- return false;
91
- return (contextWindow - currentTokens) <= COMPACTION_BUFFER_TOKENS;
122
+ return getAutoCompactDecision(opts).shouldCompact;
92
123
  }
93
124
  // ---------------------------------------------------------------------------
94
125
  // Small-window simplified compaction
@@ -1 +1 @@
1
- {"version":3,"file":"intent-classifier.d.ts","sourceRoot":"","sources":["../../../src/platform/core/intent-classifier.ts"],"names":[],"mappings":"AAAA,qFAAqF;AAErF;;;;;GAKG;AAEH,MAAM,MAAM,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;AAEjD,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAyBD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,oBAAoB,CA+EpE"}
1
+ {"version":3,"file":"intent-classifier.d.ts","sourceRoot":"","sources":["../../../src/platform/core/intent-classifier.ts"],"names":[],"mappings":"AAAA,qFAAqF;AAErF;;;;;GAKG;AAEH,MAAM,MAAM,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;AAEjD,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AA6BD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,oBAAoB,CAyFpE"}
@@ -10,6 +10,8 @@ const FILE_REFERENCES = /(\.ts|\.[jt]sx?|\.py|\.go|\.rs|\.json|\.yaml|\.yml|\.to
10
10
  const DELIVERABLE_SEPARATORS = /\band\b.+\band\b|;|\n-\s|\n\*\s|\n\d+\.\s/i;
11
11
  // Multiple sentences: two or more sentence-ending punctuation marks (. ! ?) followed by whitespace + capital
12
12
  const MULTI_SENTENCE = /[.!?][\s]+[A-Z].*[.!?][\s]+[A-Z]/;
13
+ const RETROSPECTIVE_DOCUMENTATION_REQUEST = /(\bthings you did\b|\bfrom start to finish\b|\bwhat (?:you|we) did\b|\bsetup steps\b|\binstruction guide\b|\beasy to follow guide\b|\bfed to llms?\b|\bworkflow to follow\b|\bsummarize(?: the)? workflow\b|\bdocument(?: the)? setup\b|\blist\b.{0,120}\b(?:things|steps|installed|installations?|workflow|setup)\b|\b(?:write|draft|create|make|produce|generate|document|summarize)\b.{0,100}\b(?:instruction guide|guide|runbook|playbook|checklist|documentation|docs|setup steps|workflow summary|workflow to follow)\b)/i;
14
+ const CONCRETE_IMPLEMENTATION_ACTION = /\b(build|implement|develop|add|refactor|migrate|scaffold|configure|integrate|deploy|fix|update|delete|remove|rename)\b/i;
13
15
  // ---------------------------------------------------------------------------
14
16
  // Classifier
15
17
  // ---------------------------------------------------------------------------
@@ -52,6 +54,14 @@ export function classifyIntent(message) {
52
54
  projectScore += 1;
53
55
  signals.push('spec_plan_reference');
54
56
  }
57
+ const isRetrospectiveDocumentationRequest = RETROSPECTIVE_DOCUMENTATION_REQUEST.test(trimmed);
58
+ if (isRetrospectiveDocumentationRequest) {
59
+ signals.push('documentation_request');
60
+ chatScore += 2;
61
+ if (!CONCRETE_IMPLEMENTATION_ACTION.test(trimmed)) {
62
+ projectScore = Math.min(projectScore, 2);
63
+ }
64
+ }
55
65
  // ── Chat signals ───────────────────────────────────────────────────────────
56
66
  if (QUESTION_WORDS.test(trimmed)) {
57
67
  chatScore += 2;
@@ -1 +1 @@
1
- {"version":3,"file":"orchestrator-context-runtime.d.ts","sourceRoot":"","sources":["../../../src/platform/core/orchestrator-context-runtime.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"orchestrator-context-runtime.d.ts","sourceRoot":"","sources":["../../../src/platform/core/orchestrator-context-runtime.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { logger } from '../utils/logger.js';
2
- import { estimateConversationTokens, COMPACTION_BUFFER_TOKENS, SMALL_WINDOW_THRESHOLD, compactSmallWindow, shouldAutoCompact } from './context-compaction.js';
2
+ import { estimateConversationTokens, COMPACTION_BUFFER_TOKENS, SMALL_WINDOW_THRESHOLD, compactSmallWindow, getAutoCompactDecision } from './context-compaction.js';
3
3
  import { emitOpsContextWarning } from '../runtime/emitters/index.js';
4
4
  import { summarizeError } from '../utils/error-display.js';
5
5
  function normalizeCatalogTier(tier) {
@@ -26,6 +26,16 @@ function findLargerContextModels(providerRegistry, minContext, tier, limit = 3)
26
26
  .slice(0, limit)
27
27
  .map(({ id, displayName, context }) => ({ id, displayName, context }));
28
28
  }
29
+ function readAutoCompactThreshold(configManager) {
30
+ const raw = Number(configManager.get('behavior.autoCompactThreshold') ?? 0);
31
+ return Number.isFinite(raw) ? raw : 0;
32
+ }
33
+ function formatAutoCompactTrigger(decision) {
34
+ if (decision.reason === 'safety-buffer') {
35
+ return `leaving ${decision.remainingTokens.toLocaleString()} tokens, inside the ${decision.safetyBufferTokens.toLocaleString()} token safety buffer`;
36
+ }
37
+ return `crossing the ${decision.thresholdPercent}% auto-compact threshold`;
38
+ }
29
39
  export async function checkContextWindowPreflight(deps, turnId, model) {
30
40
  const contextWindow = deps.providerRegistry.getContextWindowForModel(model);
31
41
  const tier = normalizeCatalogTier(model.tier);
@@ -33,18 +43,30 @@ export async function checkContextWindowPreflight(deps, turnId, model) {
33
43
  return 'ok';
34
44
  const messages = deps.conversation.getMessagesForLLM();
35
45
  const estimatedTokens = estimateConversationTokens(messages);
36
- if (estimatedTokens <= contextWindow)
37
- return 'ok';
38
- const threshold = deps.configManager.get('behavior.autoCompactThreshold');
46
+ const threshold = readAutoCompactThreshold(deps.configManager);
39
47
  const autoCompactEnabled = threshold > 0;
40
- if (autoCompactEnabled && !deps.isCompacting) {
48
+ const preflightDecision = getAutoCompactDecision({
49
+ currentTokens: estimatedTokens,
50
+ contextWindow,
51
+ isCompacting: deps.isCompacting,
52
+ thresholdPercent: threshold,
53
+ });
54
+ if (!preflightDecision.shouldCompact && estimatedTokens <= contextWindow)
55
+ return 'ok';
56
+ if (autoCompactEnabled && !deps.isCompacting && preflightDecision.shouldCompact) {
41
57
  logger.info('Orchestrator: context window pre-flight - auto-compacting before chat call', {
42
58
  modelId: model.id,
43
59
  estimatedTokens,
44
60
  contextWindow,
61
+ usagePct: Math.round(preflightDecision.usagePct),
62
+ thresholdPercent: preflightDecision.thresholdPercent,
63
+ thresholdTokens: preflightDecision.thresholdTokens,
64
+ remainingTokens: preflightDecision.remainingTokens,
65
+ safetyBufferTokens: preflightDecision.safetyBufferTokens,
66
+ reason: preflightDecision.reason,
45
67
  });
46
68
  deps.setIsCompacting(true);
47
- deps.conversation.addSystemMessage(`Context pre-check: request (~${Math.round(estimatedTokens / 1000)}K tokens) exceeds ${model.displayName} context window (${Math.round(contextWindow / 1000)}K). Auto-compacting...`);
69
+ deps.conversation.addSystemMessage(`Context pre-check: request is at ${Math.round(preflightDecision.usagePct)}% (${estimatedTokens}/${contextWindow} tokens), ${formatAutoCompactTrigger(preflightDecision)}. Auto-compacting...`);
48
70
  deps.requestRender();
49
71
  if (deps.hookDispatcher) {
50
72
  const preResult = await deps.hookDispatcher.fire({
@@ -54,7 +76,17 @@ export async function checkContextWindowPreflight(deps, turnId, model) {
54
76
  specific: 'preflight',
55
77
  sessionId: deps.sessionId,
56
78
  timestamp: Date.now(),
57
- payload: { trigger: 'preflight', estimatedTokens, contextWindow },
79
+ payload: {
80
+ trigger: 'preflight',
81
+ estimatedTokens,
82
+ contextWindow,
83
+ usagePct: Math.round(preflightDecision.usagePct),
84
+ thresholdPercent: preflightDecision.thresholdPercent,
85
+ thresholdTokens: preflightDecision.thresholdTokens,
86
+ remainingTokens: preflightDecision.remainingTokens,
87
+ safetyBufferTokens: preflightDecision.safetyBufferTokens,
88
+ reason: preflightDecision.reason,
89
+ },
58
90
  }).catch((err) => {
59
91
  logger.warn('Pre:compact:preflight hook error', { error: summarizeError(err) });
60
92
  return { ok: true };
@@ -89,7 +121,17 @@ export async function checkContextWindowPreflight(deps, turnId, model) {
89
121
  specific: 'preflight',
90
122
  sessionId: deps.sessionId,
91
123
  timestamp: Date.now(),
92
- payload: { trigger: 'preflight', estimatedTokens, contextWindow },
124
+ payload: {
125
+ trigger: 'preflight',
126
+ estimatedTokens,
127
+ contextWindow,
128
+ usagePct: Math.round(preflightDecision.usagePct),
129
+ thresholdPercent: preflightDecision.thresholdPercent,
130
+ thresholdTokens: preflightDecision.thresholdTokens,
131
+ remainingTokens: preflightDecision.remainingTokens,
132
+ safetyBufferTokens: preflightDecision.safetyBufferTokens,
133
+ reason: preflightDecision.reason,
134
+ },
93
135
  }).catch((err) => { logger.warn('Post:compact:preflight hook error', { error: summarizeError(err) }); });
94
136
  }
95
137
  }
@@ -105,7 +147,18 @@ export async function checkContextWindowPreflight(deps, turnId, model) {
105
147
  specific: 'preflight',
106
148
  sessionId: deps.sessionId,
107
149
  timestamp: Date.now(),
108
- payload: { trigger: 'preflight', estimatedTokens, contextWindow, error: msg },
150
+ payload: {
151
+ trigger: 'preflight',
152
+ estimatedTokens,
153
+ contextWindow,
154
+ usagePct: Math.round(preflightDecision.usagePct),
155
+ thresholdPercent: preflightDecision.thresholdPercent,
156
+ thresholdTokens: preflightDecision.thresholdTokens,
157
+ remainingTokens: preflightDecision.remainingTokens,
158
+ safetyBufferTokens: preflightDecision.safetyBufferTokens,
159
+ reason: preflightDecision.reason,
160
+ error: msg,
161
+ },
109
162
  }).catch((err) => { logger.warn('Fail:compact:preflight hook error', { error: summarizeError(err) }); });
110
163
  }
111
164
  }
@@ -146,23 +199,31 @@ export async function handlePostTurnContextMaintenance(deps, turnId, totalTokens
146
199
  const maxTokens = deps.providerRegistry.getContextWindowForModel(currentModel);
147
200
  if (maxTokens <= 0)
148
201
  return;
149
- const usagePct = Math.round((totalTokens / maxTokens) * 100);
150
- const configuredThreshold = deps.configManager.get('behavior.autoCompactThreshold');
202
+ const configuredThreshold = readAutoCompactThreshold(deps.configManager);
151
203
  const warningsEnabled = deps.configManager.get('behavior.staleContextWarnings');
152
204
  const autoCompactEnabled = configuredThreshold > 0;
205
+ const autoDecision = getAutoCompactDecision({
206
+ currentTokens: totalTokens,
207
+ contextWindow: maxTokens,
208
+ isCompacting: deps.isCompacting,
209
+ thresholdPercent: configuredThreshold,
210
+ });
211
+ const usagePct = Math.round(autoDecision.usagePct);
153
212
  const bracket = Math.floor(usagePct / 10) * 10;
154
213
  if (autoCompactEnabled &&
155
- shouldAutoCompact({
156
- currentTokens: totalTokens,
157
- contextWindow: maxTokens,
158
- isCompacting: deps.isCompacting,
159
- })) {
214
+ autoDecision.shouldCompact) {
160
215
  deps.setIsCompacting(true);
161
- deps.conversation.addSystemMessage(`Context usage at ${usagePct}% (${totalTokens}/${maxTokens} tokens). Auto-compacting conversation...`);
216
+ deps.conversation.addSystemMessage(`Context usage at ${usagePct}% (${totalTokens}/${maxTokens} tokens), ${formatAutoCompactTrigger(autoDecision)}. Auto-compacting conversation...`);
162
217
  if (deps.runtimeBus) {
163
218
  emitOpsContextWarning(deps.runtimeBus, deps.emitterContext(turnId), {
164
219
  usage: usagePct,
165
- threshold: COMPACTION_BUFFER_TOKENS,
220
+ threshold: autoDecision.thresholdPercent,
221
+ currentTokens: totalTokens,
222
+ contextWindow: maxTokens,
223
+ thresholdTokens: autoDecision.thresholdTokens,
224
+ remainingTokens: autoDecision.remainingTokens,
225
+ safetyBufferTokens: autoDecision.safetyBufferTokens,
226
+ reason: autoDecision.reason ?? 'threshold',
166
227
  });
167
228
  }
168
229
  deps.requestRender();
@@ -175,7 +236,17 @@ export async function handlePostTurnContextMaintenance(deps, turnId, totalTokens
175
236
  specific: 'auto',
176
237
  sessionId: deps.sessionId,
177
238
  timestamp: Date.now(),
178
- payload: { trigger: 'auto', usagePct, totalTokens, maxTokens },
239
+ payload: {
240
+ trigger: 'auto',
241
+ usagePct,
242
+ totalTokens,
243
+ maxTokens,
244
+ thresholdPercent: autoDecision.thresholdPercent,
245
+ thresholdTokens: autoDecision.thresholdTokens,
246
+ remainingTokens: autoDecision.remainingTokens,
247
+ safetyBufferTokens: autoDecision.safetyBufferTokens,
248
+ reason: autoDecision.reason,
249
+ },
179
250
  }).catch((err) => {
180
251
  logger.warn('Pre:compact:auto hook error', { error: summarizeError(err) });
181
252
  return { ok: true };
@@ -225,7 +296,17 @@ export async function handlePostTurnContextMaintenance(deps, turnId, totalTokens
225
296
  deps.setLastWarningBracket(0);
226
297
  deps.conversation.addSystemMessage('Context auto-compacted. Conversation history summarized to free context window.');
227
298
  deps.requestRender();
228
- logger.info('Orchestrator: auto-compact complete', { modelId: currentModel.registryKey, usagePct });
299
+ logger.info('Orchestrator: auto-compact complete', {
300
+ modelId: currentModel.registryKey,
301
+ usagePct,
302
+ totalTokens,
303
+ maxTokens,
304
+ thresholdPercent: autoDecision.thresholdPercent,
305
+ thresholdTokens: autoDecision.thresholdTokens,
306
+ remainingTokens: autoDecision.remainingTokens,
307
+ safetyBufferTokens: autoDecision.safetyBufferTokens,
308
+ reason: autoDecision.reason,
309
+ });
229
310
  if (deps.hookDispatcher) {
230
311
  deps.hookDispatcher.fire({
231
312
  path: 'Post:compact:auto',
@@ -234,7 +315,17 @@ export async function handlePostTurnContextMaintenance(deps, turnId, totalTokens
234
315
  specific: 'auto',
235
316
  sessionId: deps.sessionId,
236
317
  timestamp: Date.now(),
237
- payload: { trigger: 'auto', usagePct, totalTokens, maxTokens },
318
+ payload: {
319
+ trigger: 'auto',
320
+ usagePct,
321
+ totalTokens,
322
+ maxTokens,
323
+ thresholdPercent: autoDecision.thresholdPercent,
324
+ thresholdTokens: autoDecision.thresholdTokens,
325
+ remainingTokens: autoDecision.remainingTokens,
326
+ safetyBufferTokens: autoDecision.safetyBufferTokens,
327
+ reason: autoDecision.reason,
328
+ },
238
329
  }).catch((err) => { logger.warn('Post:compact:auto hook error', { error: summarizeError(err) }); });
239
330
  }
240
331
  }).catch((err) => {
@@ -251,7 +342,18 @@ export async function handlePostTurnContextMaintenance(deps, turnId, totalTokens
251
342
  specific: 'auto',
252
343
  sessionId: deps.sessionId,
253
344
  timestamp: Date.now(),
254
- payload: { trigger: 'auto', usagePct, totalTokens, maxTokens, error: msg },
345
+ payload: {
346
+ trigger: 'auto',
347
+ usagePct,
348
+ totalTokens,
349
+ maxTokens,
350
+ thresholdPercent: autoDecision.thresholdPercent,
351
+ thresholdTokens: autoDecision.thresholdTokens,
352
+ remainingTokens: autoDecision.remainingTokens,
353
+ safetyBufferTokens: autoDecision.safetyBufferTokens,
354
+ reason: autoDecision.reason,
355
+ error: msg,
356
+ },
255
357
  }).catch((err) => { logger.warn('Fail:compact:auto hook error', { error: summarizeError(err) }); });
256
358
  }
257
359
  });
@@ -266,14 +368,20 @@ export async function handlePostTurnContextMaintenance(deps, turnId, totalTokens
266
368
  }
267
369
  else if (warningsEnabled &&
268
370
  autoCompactEnabled &&
269
- (maxTokens - totalTokens) <= COMPACTION_BUFFER_TOKENS * 2 &&
371
+ usagePct >= Math.max(0, configuredThreshold - 10) &&
270
372
  bracket > deps.lastWarningBracket) {
271
373
  deps.setLastWarningBracket(bracket);
272
- deps.conversation.addSystemMessage(`Context usage at ${usagePct}% (${totalTokens}/${maxTokens} tokens). Auto-compact will trigger within ${COMPACTION_BUFFER_TOKENS.toLocaleString()} remaining tokens.`);
374
+ deps.conversation.addSystemMessage(`Context usage at ${usagePct}% (${totalTokens}/${maxTokens} tokens). Auto-compact will trigger at ${configuredThreshold}% or when the ${COMPACTION_BUFFER_TOKENS.toLocaleString()} token safety buffer is reached.`);
273
375
  if (deps.runtimeBus) {
274
376
  emitOpsContextWarning(deps.runtimeBus, deps.emitterContext(turnId), {
275
377
  usage: usagePct,
276
- threshold: COMPACTION_BUFFER_TOKENS,
378
+ threshold: configuredThreshold,
379
+ currentTokens: totalTokens,
380
+ contextWindow: maxTokens,
381
+ thresholdTokens: Math.floor((maxTokens * configuredThreshold) / 100),
382
+ remainingTokens: Math.max(0, maxTokens - totalTokens),
383
+ safetyBufferTokens: COMPACTION_BUFFER_TOKENS,
384
+ reason: 'threshold',
277
385
  });
278
386
  }
279
387
  deps.requestRender();
@@ -1 +1 @@
1
- {"version":3,"file":"bootstrap-hook-bridge.d.ts","sourceRoot":"","sources":["../../../src/platform/runtime/bootstrap-hook-bridge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAExD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACtE,OAAO,KAAK,EAAwB,eAAe,EAAiB,MAAM,mBAAmB,CAAC;AA4B9F,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,CAAC,UAAU,EAAE,eAAe,CAAC;IACrC,QAAQ,CAAC,cAAc,EAAE,cAAc,CAAC;IACxC,QAAQ,CAAC,OAAO,EAAE,mBAAmB,CAAC;CACvC;AAED,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,6BAA6B,GACrC,KAAK,CAAC,MAAM,IAAI,CAAC,CAoInB"}
1
+ {"version":3,"file":"bootstrap-hook-bridge.d.ts","sourceRoot":"","sources":["../../../src/platform/runtime/bootstrap-hook-bridge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAExD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACtE,OAAO,KAAK,EAAwB,eAAe,EAAiB,MAAM,mBAAmB,CAAC;AA4B9F,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,CAAC,UAAU,EAAE,eAAe,CAAC;IACrC,QAAQ,CAAC,cAAc,EAAE,cAAc,CAAC;IACxC,QAAQ,CAAC,OAAO,EAAE,mBAAmB,CAAC;CACvC;AAED,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,6BAA6B,GACrC,KAAK,CAAC,MAAM,IAAI,CAAC,CAqInB"}
@@ -135,9 +135,10 @@ export function registerBootstrapHookBridge(options) {
135
135
  ...(payload.toRole !== undefined ? { toRole: payload.toRole } : {}),
136
136
  });
137
137
  }));
138
- unsubs.push(runtimeBus.on('OPS_CONTEXT_WARNING', ({ payload: { usage, threshold } }) => {
139
- const specific = usage >= threshold ? 'exceeded' : 'warning';
140
- fireHook(fireOptions, `Change:budget:${specific}`, 'Change', 'budget', specific, { usage, threshold });
138
+ unsubs.push(runtimeBus.on('OPS_CONTEXT_WARNING', ({ payload }) => {
139
+ const { usage, threshold } = payload;
140
+ const specific = payload.reason === 'safety-buffer' || usage >= threshold ? 'exceeded' : 'warning';
141
+ fireHook(fireOptions, `Change:budget:${specific}`, 'Change', 'budget', specific, payload);
141
142
  }));
142
143
  return unsubs;
143
144
  }
@@ -5,6 +5,12 @@ import type { OpsInterventionReason } from '../../../events/ops.js';
5
5
  export declare function emitOpsContextWarning(bus: RuntimeEventBus, ctx: EmitterContext, data: {
6
6
  usage: number;
7
7
  threshold: number;
8
+ currentTokens?: number | undefined;
9
+ contextWindow?: number | undefined;
10
+ thresholdTokens?: number | undefined;
11
+ remainingTokens?: number | undefined;
12
+ safetyBufferTokens?: number | undefined;
13
+ reason?: 'threshold' | 'safety-buffer' | undefined;
8
14
  }): void;
9
15
  /** Emit OPS_CACHE_METRICS for cache hit-rate and token accounting snapshots. */
10
16
  export declare function emitOpsCacheMetrics(bus: RuntimeEventBus, ctx: EmitterContext, data: {
@@ -1 +1 @@
1
- {"version":3,"file":"ops.d.ts","sourceRoot":"","sources":["../../../../src/platform/runtime/emitters/ops.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAEpE,+EAA+E;AAC/E,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,IAAI,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GACzC,IAAI,CAMN;AAED,gFAAgF;AAChF,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,IAAI,EAAE;IACJ,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC;CACf,GACA,IAAI,CAKN;AAED,8EAA8E;AAC9E,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,IAAI,EAAE;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACjE,IAAI,CAKN;AAmCD,4DAA4D;AAC5D,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,qBAAqB,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,OAAO,EAAE,SAAS,GAAG,UAAU,GAAG,OAAO,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,GAC/J,IAAI,CAQN;AAED,yDAAyD;AACzD,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,qBAAqB,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,OAAO,EAAE,SAAS,GAAG,UAAU,GAAG,OAAO,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,GAC/J,IAAI,CAQN;AAED,0DAA0D;AAC1D,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,qBAAqB,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,OAAO,EAAE,SAAS,GAAG,UAAU,GAAG,OAAO,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,GAC/J,IAAI,CAQN;AAED,0DAA0D;AAC1D,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,qBAAqB,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,OAAO,EAAE,SAAS,GAAG,UAAU,GAAG,OAAO,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,GAC/J,IAAI,CAQN;AAID,6DAA6D;AAC7D,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,IAAI,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,qBAAqB,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,OAAO,EAAE,SAAS,GAAG,UAAU,GAAG,OAAO,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,GAChK,IAAI,CAQN"}
1
+ {"version":3,"file":"ops.d.ts","sourceRoot":"","sources":["../../../../src/platform/runtime/emitters/ops.ts"],"names":[],"mappings":"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"}
@@ -9,8 +9,7 @@ import { createEventEnvelope } from '../events/envelope.js';
9
9
  export function emitOpsContextWarning(bus, ctx, data) {
10
10
  bus.emit('ops', createEventEnvelope('OPS_CONTEXT_WARNING', {
11
11
  type: 'OPS_CONTEXT_WARNING',
12
- usage: data.usage,
13
- threshold: data.threshold,
12
+ ...data,
14
13
  }, ctx));
15
14
  }
16
15
  /** Emit OPS_CACHE_METRICS for cache hit-rate and token accounting snapshots. */
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../../../src/platform/tools/exec/runtime.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAGjD,OAAO,EAAqB,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC3E,OAAO,KAAK,EAA+B,iBAAiB,EAAiB,MAAM,aAAa,CAAC;AACjG,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAG9D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sCAAsC,CAAC;AAkd/E;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,iBAAiB,EACzB,OAAO,CAAC,EAAE,aAAa,CAAC,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC,GAC3D,OAAO,CA2CT;AA8ID,wBAAgB,cAAc,CAC5B,cAAc,EAAE,cAAc,EAC9B,OAAO,GAAE;IACP,QAAQ,CAAC,YAAY,CAAC,EAAE,IAAI,CAAC,kBAAkB,EAAE,WAAW,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC;IACjF,QAAQ,CAAC,eAAe,CAAC,EAAE,eAAe,GAAG,SAAS,CAAC;CACnD,GACL,IAAI,CAmFN"}
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../../../src/platform/tools/exec/runtime.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAGjD,OAAO,EAAqB,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC3E,OAAO,KAAK,EAA+B,iBAAiB,EAAiB,MAAM,aAAa,CAAC;AACjG,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAG9D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sCAAsC,CAAC;AA2e/E;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,iBAAiB,EACzB,OAAO,CAAC,EAAE,aAAa,CAAC,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC,GAC3D,OAAO,CA2CT;AA8ID,wBAAgB,cAAc,CAC5B,cAAc,EAAE,cAAc,EAC9B,OAAO,GAAE;IACP,QAAQ,CAAC,YAAY,CAAC,EAAE,IAAI,CAAC,kBAAkB,EAAE,WAAW,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC;IACjF,QAAQ,CAAC,eAAe,CAAC,EAAE,eAAe,GAAG,SAAS,CAAC;CACnD,GACL,IAAI,CAmFN"}
@@ -36,6 +36,30 @@ function requireWorkingDirectory(input) {
36
36
  }
37
37
  return input.working_dir;
38
38
  }
39
+ function normalizeExecInput(input) {
40
+ const hasTopLevelWorkingDir = Boolean(input.working_dir?.trim());
41
+ const commandWorkingDir = input.commands[0]?.working_dir?.trim();
42
+ const commandCwd = input.commands[0]?.cwd?.trim();
43
+ const promotedWorkingDir = !hasTopLevelWorkingDir && input.commands.length === 1
44
+ ? commandWorkingDir || commandCwd
45
+ : undefined;
46
+ const commands = input.commands.map((command) => {
47
+ const itemWorkingDir = command.working_dir?.trim();
48
+ const itemCwd = command.cwd?.trim();
49
+ if (itemWorkingDir) {
50
+ if (!hasTopLevelWorkingDir && input.commands.length === 1 && promotedWorkingDir === itemWorkingDir && !itemCwd) {
51
+ return command;
52
+ }
53
+ return { ...command, cwd: itemCwd || itemWorkingDir };
54
+ }
55
+ if (!hasTopLevelWorkingDir && input.commands.length === 1 && promotedWorkingDir === itemCwd) {
56
+ return { ...command, cwd: undefined };
57
+ }
58
+ return command;
59
+ });
60
+ const workingDir = hasTopLevelWorkingDir ? input.working_dir : promotedWorkingDir;
61
+ return { ...input, working_dir: workingDir, commands };
62
+ }
39
63
  function truncate(overflowHandler, s, label, maxChars = DEFAULT_MAX_CHARS) {
40
64
  const result = overflowHandler.handle(s, { maxChars, label });
41
65
  return { text: result.content, truncated: result.overflowRef !== undefined };
@@ -583,7 +607,7 @@ export function createExecTool(processManager, options = {}) {
583
607
  if (args['commands'].length > MAX_EXEC_COMMANDS) {
584
608
  return { success: false, error: `Too many commands: maximum ${MAX_EXEC_COMMANDS} per exec call` };
585
609
  }
586
- const input = args;
610
+ const input = normalizeExecInput(args);
587
611
  const workingDirectory = requireWorkingDirectory(input);
588
612
  const verbosity = input.verbosity ?? 'standard';
589
613
  const globalTimeout = input.timeout_ms ?? DEFAULT_TIMEOUT_MS;
@@ -23,7 +23,11 @@ export declare const EXEC_TOOL_SCHEMA: {
23
23
  };
24
24
  readonly cwd: {
25
25
  readonly type: "string";
26
- readonly description: "Working directory for this command. Overrides working_dir.";
26
+ readonly description: "Per-command working directory override. Prefer top-level working_dir for the project root.";
27
+ };
28
+ readonly working_dir: {
29
+ readonly type: "string";
30
+ readonly description: "Alias for cwd on a command item. For a single-command call, this can also supply the required top-level working_dir.";
27
31
  };
28
32
  readonly timeout_ms: {
29
33
  readonly type: "integer";
@@ -230,6 +234,7 @@ export interface ExecCommandInput {
230
234
  cmd?: string | undefined;
231
235
  cmd_base64?: string | undefined;
232
236
  cwd?: string | undefined;
237
+ working_dir?: string | undefined;
233
238
  timeout_ms?: number | undefined;
234
239
  env?: Record<string, string> | undefined;
235
240
  expect?: ExecExpect | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../../src/platform/tools/exec/schema.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../../src/platform/tools/exec/schema.ts"],"names":[],"mappings":"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"}
@@ -23,7 +23,11 @@ export const EXEC_TOOL_SCHEMA = {
23
23
  },
24
24
  cwd: {
25
25
  type: 'string',
26
- description: 'Working directory for this command. Overrides working_dir.',
26
+ description: 'Per-command working directory override. Prefer top-level working_dir for the project root.',
27
+ },
28
+ working_dir: {
29
+ type: 'string',
30
+ description: 'Alias for cwd on a command item. For a single-command call, this can also supply the required top-level working_dir.',
27
31
  },
28
32
  timeout_ms: {
29
33
  type: 'integer',
@@ -1,6 +1,6 @@
1
1
  import { readFileSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
- let version = '0.33.27';
3
+ let version = '0.33.29';
4
4
  try {
5
5
  const pkg = JSON.parse(readFileSync(join(import.meta.dir, '..', '..', 'package.json'), 'utf-8'));
6
6
  version = pkg.version ?? version;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pellux/goodvibes-sdk",
3
- "version": "0.33.27",
3
+ "version": "0.33.29",
4
4
  "description": "TypeScript SDK for building GoodVibes operator, peer, web, mobile, and daemon-connected apps with typed contracts, auth, realtime events, and transport layers.",
5
5
  "keywords": [
6
6
  "goodvibes",
@@ -449,14 +449,14 @@
449
449
  "sideEffects": false,
450
450
  "type": "module",
451
451
  "dependencies": {
452
- "@pellux/goodvibes-contracts": "0.33.27",
453
- "@pellux/goodvibes-daemon-sdk": "0.33.27",
454
- "@pellux/goodvibes-errors": "0.33.27",
455
- "@pellux/goodvibes-operator-sdk": "0.33.27",
456
- "@pellux/goodvibes-peer-sdk": "0.33.27",
457
- "@pellux/goodvibes-transport-core": "0.33.27",
458
- "@pellux/goodvibes-transport-http": "0.33.27",
459
- "@pellux/goodvibes-transport-realtime": "0.33.27"
452
+ "@pellux/goodvibes-contracts": "0.33.29",
453
+ "@pellux/goodvibes-daemon-sdk": "0.33.29",
454
+ "@pellux/goodvibes-errors": "0.33.29",
455
+ "@pellux/goodvibes-operator-sdk": "0.33.29",
456
+ "@pellux/goodvibes-peer-sdk": "0.33.29",
457
+ "@pellux/goodvibes-transport-core": "0.33.29",
458
+ "@pellux/goodvibes-transport-http": "0.33.29",
459
+ "@pellux/goodvibes-transport-realtime": "0.33.29"
460
460
  },
461
461
  "optionalDependencies": {
462
462
  "@agentclientprotocol/sdk": "^0.21.0",