@askalf/dario 3.30.6 → 3.30.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.d.ts CHANGED
@@ -9,4 +9,13 @@
9
9
  * dario refresh — Force token refresh
10
10
  * dario logout — Remove saved credentials
11
11
  */
12
- export {};
12
+ /**
13
+ * Parse --preserve-orchestration-tags (bare or =value) + env mirror.
14
+ * Exported for tests.
15
+ * dario#78.
16
+ *
17
+ * undefined — flag not passed + env unset → strip all (default)
18
+ * Set(['*']) — flag bare OR value "*" → preserve all
19
+ * Set(['thinking','env']) — value "thinking,env" → preserve listed
20
+ */
21
+ export declare function resolvePreserveOrchestrationTags(args: string[], env: string | undefined): Set<string> | undefined;
package/dist/cli.js CHANGED
@@ -229,6 +229,13 @@ async function proxy() {
229
229
  const sessionRotateJitterMs = parsePositiveIntFlag('--session-rotate-jitter=');
230
230
  const sessionMaxAgeMs = parsePositiveIntFlag('--session-max-age=');
231
231
  const sessionPerClient = args.includes('--session-per-client') || undefined;
232
+ // --preserve-orchestration-tags (bare OR =tag1,tag2,...) — opt-out for
233
+ // workflows that legitimately need <system-reminder>, <thinking>, etc.
234
+ // preserved on the wire. Default behaviour unchanged (strip all).
235
+ // dario#78 (Gemini review push-back). Env mirror:
236
+ // DARIO_PRESERVE_ORCHESTRATION_TAGS=* (preserve all)
237
+ // DARIO_PRESERVE_ORCHESTRATION_TAGS=thinking,env (preserve listed)
238
+ const preserveOrchestrationTags = resolvePreserveOrchestrationTags(args, process.env['DARIO_PRESERVE_ORCHESTRATION_TAGS']);
232
239
  // Non-loopback bind without DARIO_API_KEY turns dario into an open
233
240
  // OAuth-subscription relay for anyone on the reachable network. Refuse
234
241
  // to start rather than rely on the operator to read the startup banner.
@@ -248,7 +255,35 @@ async function proxy() {
248
255
  console.error(`[dario] Override (not recommended): pass --unsafe-no-auth if you have out-of-band network controls and accept the risk.`);
249
256
  process.exit(1);
250
257
  }
251
- await startProxy({ port, host, verbose, verboseBodies, model, passthrough, preserveTools, hybridTools, noAutoDetect, strictTls, pacingMinMs, pacingJitterMs, drainOnClose, sessionIdleRotateMs, sessionRotateJitterMs, sessionMaxAgeMs, sessionPerClient });
258
+ await startProxy({ port, host, verbose, verboseBodies, model, passthrough, preserveTools, hybridTools, noAutoDetect, strictTls, pacingMinMs, pacingJitterMs, drainOnClose, sessionIdleRotateMs, sessionRotateJitterMs, sessionMaxAgeMs, sessionPerClient, preserveOrchestrationTags });
259
+ }
260
+ /**
261
+ * Parse --preserve-orchestration-tags (bare or =value) + env mirror.
262
+ * Exported for tests.
263
+ * dario#78.
264
+ *
265
+ * undefined — flag not passed + env unset → strip all (default)
266
+ * Set(['*']) — flag bare OR value "*" → preserve all
267
+ * Set(['thinking','env']) — value "thinking,env" → preserve listed
268
+ */
269
+ export function resolvePreserveOrchestrationTags(args, env) {
270
+ // Explicit --preserve-orchestration-tags=value wins over everything.
271
+ const withValue = args.find(a => a.startsWith('--preserve-orchestration-tags='));
272
+ if (withValue)
273
+ return parsePreserveTagsValue(withValue.split('=').slice(1).join('='));
274
+ // Bare flag = preserve all.
275
+ if (args.includes('--preserve-orchestration-tags'))
276
+ return new Set(['*']);
277
+ // Env mirror — explicit flag always wins, checked last.
278
+ if (env !== undefined)
279
+ return parsePreserveTagsValue(env);
280
+ return undefined;
281
+ }
282
+ function parsePreserveTagsValue(value) {
283
+ const trimmed = value.trim();
284
+ if (trimmed === '' || trimmed === '*')
285
+ return new Set(['*']);
286
+ return new Set(trimmed.split(',').map(s => s.trim()).filter(Boolean));
252
287
  }
253
288
  function parsePositiveIntFlag(prefix) {
254
289
  const found = args.find(a => a.startsWith(prefix));
@@ -561,6 +596,16 @@ async function help() {
561
596
  header) its own rotated session id.
562
597
  Default: off (single session across all
563
598
  clients, v3.27 behaviour). (v3.28)
599
+ --preserve-orchestration-tags[=TAG,TAG]
600
+ Opt specific orchestration wrapper tags
601
+ (<system-reminder>, <env>, <thinking>,
602
+ etc.) out of the scrub. Bare flag =
603
+ preserve all. Value form = preserve only
604
+ those listed; everything else is still
605
+ stripped. Default: strip every tag in
606
+ ORCHESTRATION_TAG_NAMES. Env mirror:
607
+ DARIO_PRESERVE_ORCHESTRATION_TAGS=*
608
+ or =tag1,tag2. (v3.31, dario#78)
564
609
  --port=PORT Port to listen on (default: 3456)
565
610
  --host=ADDRESS Address to bind to (default: 127.0.0.1)
566
611
  Use 0.0.0.0 for LAN; see README for DARIO_API_KEY
package/dist/proxy.d.ts CHANGED
@@ -3,8 +3,29 @@ export declare function parseProviderPrefix(model: string): {
3
3
  provider: 'openai' | 'claude';
4
4
  model: string;
5
5
  } | null;
6
- /** Strip orchestration tags from all messages in a request body. */
7
- export declare function sanitizeMessages(body: Record<string, unknown>): void;
6
+ export declare const ORCHESTRATION_TAG_NAMES: string[];
7
+ /**
8
+ * Build the regex list that actually strips orchestration tags.
9
+ *
10
+ * `preserveTags` selects which tags to KEEP in the outbound body.
11
+ * undefined → strip every tag in ORCHESTRATION_TAG_NAMES (default)
12
+ * Set(['*']) → preserve all tags (strip none)
13
+ * Set(['thinking']) → strip everything except `<thinking>...</thinking>`
14
+ *
15
+ * Each tag produces two patterns — the wrapper form (`<tag>...</tag>`) and
16
+ * the self-closing form (`<tag ... />`) — so callers that emit either shape
17
+ * get the same treatment.
18
+ *
19
+ * dario#78 (Gemini review push-back).
20
+ */
21
+ export declare function buildOrchestrationPatterns(preserveTags?: Set<string>): RegExp[];
22
+ /**
23
+ * Strip orchestration tags from all messages in a request body.
24
+ *
25
+ * Pass `preserveTags` (a Set of tag names, or `Set(['*'])` for all) to
26
+ * opt any tag out of the scrub. dario#78.
27
+ */
28
+ export declare function sanitizeMessages(body: Record<string, unknown>, preserveTags?: Set<string>): void;
8
29
  interface ProxyOptions {
9
30
  port?: number;
10
31
  host?: string;
@@ -23,6 +44,12 @@ interface ProxyOptions {
23
44
  sessionRotateJitterMs?: number;
24
45
  sessionMaxAgeMs?: number;
25
46
  sessionPerClient?: boolean;
47
+ /**
48
+ * Opt specific orchestration tags out of the scrub. Undefined = strip all
49
+ * (default, v3.30 and earlier behaviour). Set(['*']) = preserve all.
50
+ * Set(['thinking','env']) = strip everything except those two. dario#78.
51
+ */
52
+ preserveOrchestrationTags?: Set<string>;
26
53
  }
27
54
  export declare function sanitizeError(err: unknown): string;
28
55
  /**
package/dist/proxy.js CHANGED
@@ -190,38 +190,66 @@ function filterBillableBetas(betas) {
190
190
  }
191
191
  // Orchestration tags injected by agents (Aider, Cursor, OpenCode, etc.)
192
192
  // that confuse Claude when passed through. Strip before forwarding.
193
- const ORCHESTRATION_TAG_NAMES = [
193
+ export const ORCHESTRATION_TAG_NAMES = [
194
194
  'system-reminder', 'env', 'system_information', 'current_working_directory',
195
195
  'operating_system', 'default_shell', 'home_directory', 'task_metadata',
196
196
  'directories', 'thinking',
197
197
  'agent_persona', 'agent_context', 'tool_context', 'persona', 'tool_call',
198
198
  ];
199
- const ORCHESTRATION_PATTERNS = ORCHESTRATION_TAG_NAMES.flatMap(tag => [
200
- new RegExp(`<${tag}\\b[^>]*>[\\s\\S]*?<\\/${tag}>`, 'gi'),
201
- new RegExp(`<${tag}\\b[^>]*\\/>`, 'gi'),
202
- ]);
199
+ /**
200
+ * Build the regex list that actually strips orchestration tags.
201
+ *
202
+ * `preserveTags` selects which tags to KEEP in the outbound body.
203
+ * undefined → strip every tag in ORCHESTRATION_TAG_NAMES (default)
204
+ * Set(['*']) → preserve all tags (strip none)
205
+ * Set(['thinking']) → strip everything except `<thinking>...</thinking>`
206
+ *
207
+ * Each tag produces two patterns — the wrapper form (`<tag>...</tag>`) and
208
+ * the self-closing form (`<tag ... />`) — so callers that emit either shape
209
+ * get the same treatment.
210
+ *
211
+ * dario#78 (Gemini review push-back).
212
+ */
213
+ export function buildOrchestrationPatterns(preserveTags) {
214
+ if (preserveTags?.has('*'))
215
+ return [];
216
+ const effective = preserveTags
217
+ ? ORCHESTRATION_TAG_NAMES.filter(tag => !preserveTags.has(tag))
218
+ : ORCHESTRATION_TAG_NAMES;
219
+ return effective.flatMap(tag => [
220
+ new RegExp(`<${tag}\\b[^>]*>[\\s\\S]*?<\\/${tag}>`, 'gi'),
221
+ new RegExp(`<${tag}\\b[^>]*\\/>`, 'gi'),
222
+ ]);
223
+ }
224
+ const ORCHESTRATION_PATTERNS_DEFAULT = buildOrchestrationPatterns();
203
225
  /** Strip orchestration wrapper tags from message content. */
204
- function sanitizeContent(text) {
226
+ function sanitizeContent(text, patterns) {
205
227
  let result = text;
206
- for (const pattern of ORCHESTRATION_PATTERNS) {
228
+ for (const pattern of patterns) {
207
229
  pattern.lastIndex = 0;
208
230
  result = result.replace(pattern, '');
209
231
  }
210
232
  return result.replace(/\n{3,}/g, '\n\n').trim();
211
233
  }
212
- /** Strip orchestration tags from all messages in a request body. */
213
- export function sanitizeMessages(body) {
234
+ /**
235
+ * Strip orchestration tags from all messages in a request body.
236
+ *
237
+ * Pass `preserveTags` (a Set of tag names, or `Set(['*'])` for all) to
238
+ * opt any tag out of the scrub. dario#78.
239
+ */
240
+ export function sanitizeMessages(body, preserveTags) {
214
241
  const messages = body.messages;
215
242
  if (!messages)
216
243
  return;
244
+ const patterns = preserveTags === undefined ? ORCHESTRATION_PATTERNS_DEFAULT : buildOrchestrationPatterns(preserveTags);
217
245
  for (const msg of messages) {
218
246
  if (typeof msg.content === 'string') {
219
- msg.content = sanitizeContent(msg.content);
247
+ msg.content = sanitizeContent(msg.content, patterns);
220
248
  }
221
249
  else if (Array.isArray(msg.content)) {
222
250
  for (const block of msg.content) {
223
251
  if (typeof block === 'object' && block && 'text' in block && typeof block.text === 'string') {
224
- block.text = sanitizeContent(block.text);
252
+ block.text = sanitizeContent(block.text, patterns);
225
253
  }
226
254
  }
227
255
  // Drop text blocks that became empty after orchestration-tag scrubbing.
@@ -876,7 +904,7 @@ export async function startProxy(opts = {}) {
876
904
  try {
877
905
  const parsed = JSON.parse(body.toString());
878
906
  // Strip orchestration tags from messages (Aider, Cursor, etc.)
879
- sanitizeMessages(parsed);
907
+ sanitizeMessages(parsed, opts.preserveOrchestrationTags);
880
908
  const result = isOpenAI ? openaiToAnthropic(parsed, modelOverride) : (modelOverride ? { ...parsed, model: modelOverride } : parsed);
881
909
  const r = result;
882
910
  requestModel = (r.model || '').toLowerCase();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@askalf/dario",
3
- "version": "3.30.6",
3
+ "version": "3.30.7",
4
4
  "description": "A local LLM router. One endpoint, every provider — Claude subscriptions, OpenAI, OpenRouter, Groq, local LiteLLM, any OpenAI-compat endpoint — your tools don't need to change.",
5
5
  "type": "module",
6
6
  "bin": {