shraga 0.1.160 → 0.1.161

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.
@@ -13,7 +13,7 @@
13
13
  <link rel="preconnect" href="https://fonts.googleapis.com" />
14
14
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
15
15
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap" rel="stylesheet" />
16
- <script type="module" crossorigin src="/assets/index-yBsLZ3yV.js"></script>
16
+ <script type="module" crossorigin src="/assets/index-C46Azkf_.js"></script>
17
17
  <link rel="stylesheet" crossorigin href="/assets/index-NtN1I_dh.css">
18
18
  </head>
19
19
  <body>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shraga",
3
- "version": "0.1.160",
3
+ "version": "0.1.161",
4
4
  "description": "The teammate you delegate coding to — a self-hostable, multi-user AI coding agent web UI (Claude Code, with a pluggable engine seam).",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
@@ -313,6 +313,11 @@ async function* runTurn(opts: StreamChatOpts, guard?: TurnGuard): AsyncGenerator
313
313
 
314
314
  const sessionMeta = opts.sessionId ? getSession(opts.sessionId) : undefined;
315
315
  const directives: Directives = { ...sessionMeta?.directives, ...parsed };
316
+ // A two-model mode (delegate/advisor) is sticky like the model: `[off]` clears it, and a new mode
317
+ // replaces the other one. `modeOff` is a command, not a setting — never stored.
318
+ if (parsed.modeOff || parsed.advisor) delete directives.delegate;
319
+ if (parsed.modeOff || parsed.delegate) delete directives.advisor;
320
+ delete directives.modeOff;
316
321
  // Persist ONLY what was explicitly chosen (inline directives, or the Agent Config panel's
317
322
  // per-conversation save). Gaps stay gaps: every engine resolves `directives.x ?? config.x` at
318
323
  // send time, so the global agent-config keeps applying live to sessions nobody pinned —
@@ -6,6 +6,12 @@ export interface Directives {
6
6
  engine?: string;
7
7
  /** claude-code SDK session resume for this conversation (overrides agent-config `sdkResume`). */
8
8
  resume?: boolean;
9
+ /** agentx DELEGATE mode `[planner->worker]` (key `delegate`): the worker model; `model` (left side) plans. */
10
+ delegate?: string;
11
+ /** agentx ADVISOR mode `[worker<-advisor]` (key `advisor`): the advisor model; `model` (left side) works. */
12
+ advisor?: string;
13
+ /** `[off]` / `[mode:off]`: clears a sticky delegate/advisor mode. Consumed by streamChat, never stored. */
14
+ modeOff?: boolean;
9
15
  }
10
16
 
11
17
  export interface ParsedPrompt {
@@ -28,7 +34,15 @@ import { MODEL_ALIASES } from './model-aliases.ts';
28
34
 
29
35
  const DIRECTIVE_RE = /^\s*\[([^\]]*)\]\s*([\s\S]*)/;
30
36
 
31
- const DIRECTIVE_KEYS = ['model', 'turns', 'thinking', 'think', 'effort', 'engine', 'resume'];
37
+ const DIRECTIVE_KEYS = ['model', 'turns', 'thinking', 'think', 'effort', 'engine', 'resume', 'delegate', 'advisor', 'mode'];
38
+
39
+ /** `a->b` (delegate) / `a<-b` (advisor) — agentx's two-model mode grammar; the left side may be empty. */
40
+ function splitModePair(token: string): { left: string; op: '->' | '<-'; right: string } | null {
41
+ const i = token.search(/->|<-/);
42
+ if (i === -1) return null;
43
+ const right = token.slice(i + 2).trim();
44
+ return right ? { left: token.slice(0, i).trim(), op: token.slice(i, i + 2) as '->' | '<-', right } : null;
45
+ }
32
46
 
33
47
  /** MODEL_ALIASES only covers the bare Anthropic shorthands. A `provider/model` id
34
48
  * (`cursor/composer-2.5`, `openai/gpt-5.6`) is already concrete — gating it on the alias table
@@ -72,6 +86,9 @@ export function isDirectiveGroup(raw: string): boolean {
72
86
  const colonIdx = t.indexOf(':');
73
87
  if (colonIdx !== -1) return DIRECTIVE_KEYS.includes(t.slice(0, colonIdx).trim().toLowerCase());
74
88
  const v = t.toLowerCase();
89
+ const pair = splitModePair(v);
90
+ if (pair) return (!pair.left || !!resolveModelAlias(pair.left)) && !!resolveModelAlias(pair.right);
91
+ if (v === 'off') return true;
75
92
  return !!MODEL_ALIASES[v] || !!modelResolver?.(v) || /^\d+$/.test(v) || ['think', 'adaptive', 'nothink', 'nothinking'].includes(v);
76
93
  });
77
94
  }
@@ -132,6 +149,17 @@ export function parseDirectives(text: string): ParsedPrompt {
132
149
  unresolvedModel = applyDirective(directives, key, val) ?? unresolvedModel;
133
150
  } else {
134
151
  const val = t.toLowerCase();
152
+ const pair = splitModePair(val);
153
+ if (pair) {
154
+ // Both sides must resolve: a half-resolved pair would silently run a mode on the wrong model.
155
+ const worker = resolveModelAlias(pair.right);
156
+ if (worker && (!pair.left || resolveModelToken(directives, pair.left))) {
157
+ if (pair.op === '->') directives.delegate = worker.model; else directives.advisor = worker.model;
158
+ } else unresolvedModel ??= t;
159
+ positionalIndex++;
160
+ continue;
161
+ }
162
+ if (val === 'off') { directives.modeOff = true; positionalIndex++; continue; }
135
163
  if (positionalIndex === 0 && resolveModelToken(directives, val)) {
136
164
  // handled
137
165
  } else if (positionalIndex <= 1 && /^\d+$/.test(val)) {
@@ -179,6 +207,18 @@ function applyDirective(d: Directives, key: string, val: string): string | undef
179
207
  case 'engine':
180
208
  d.engine = val;
181
209
  break;
210
+ case 'delegate':
211
+ case 'advisor': {
212
+ const hit = resolveModelAlias(val);
213
+ if (hit) d[key] = hit.model;
214
+ else if (isQualifiedModel(val)) d[key] = val;
215
+ else return val;
216
+ break;
217
+ }
218
+ case 'mode':
219
+ if (val === 'off') d.modeOff = true;
220
+ else console.warn(`[directives] Invalid mode value: "${val}"`);
221
+ break;
182
222
  case 'resume':
183
223
  if (['on', 'true', '1'].includes(val)) d.resume = true;
184
224
  else if (['off', 'false', '0'].includes(val)) d.resume = false;