@ours.network/fleet 0.17.9 → 0.17.11

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/runner.d.ts CHANGED
@@ -35,6 +35,15 @@ export interface RunnerDeps {
35
35
  }
36
36
  /** Environment injected only into the managed harness process. */
37
37
  export declare function managedFleetProxyEnv(role: ResolvedRole, stateDir: string): Record<string, string>;
38
+ /**
39
+ * The environment a managed harness child actually receives, checked at the one
40
+ * point where it is composed. `role.env` deliberately wins over harness prep,
41
+ * which is exactly how a stale fleet-wide model pin used to outrank the model
42
+ * the role was spawned with — so the model pin is verified here rather than
43
+ * trusted, and a disagreement stops the launch instead of being reported as a
44
+ * success (see src/model-env.ts).
45
+ */
46
+ export declare function harnessChildEnv(role: ResolvedRole, launchEnv: Record<string, string> | undefined, stateDir: string): Record<string, string>;
38
47
  /**
39
48
  * Record who owns wake delivery for this run. Returning true means a fleet
40
49
  * monitor is taking ownership back from a native harness and must start at the
package/dist/runner.js CHANGED
@@ -24,6 +24,7 @@ import { RoleTurnArbiter } from './session/arbiter.js';
24
24
  import { ScheduledLoopManager, } from './loops/manager.js';
25
25
  import { FLEET_PROXY_CALLER_ENV, FLEET_PROXY_STATE_DIR_ENV, inheritCallerSpawnDefaults, } from './fleet-proxy.js';
26
26
  import { effectivePermissionMode } from './permissions.js';
27
+ import { assertModelPinReachesChild, effectiveRoleModel, repinModelEnv } from './model-env.js';
27
28
  import { archiveTempState, markTempSupervisorActive, requestedTempStopReason, } from './temp-lifecycle.js';
28
29
  const defaultDeps = () => ({
29
30
  tmux: new Tmux(),
@@ -72,6 +73,19 @@ export function managedFleetProxyEnv(role, stateDir) {
72
73
  [FLEET_PROXY_CALLER_ENV]: role.name,
73
74
  };
74
75
  }
76
+ /**
77
+ * The environment a managed harness child actually receives, checked at the one
78
+ * point where it is composed. `role.env` deliberately wins over harness prep,
79
+ * which is exactly how a stale fleet-wide model pin used to outrank the model
80
+ * the role was spawned with — so the model pin is verified here rather than
81
+ * trusted, and a disagreement stops the launch instead of being reported as a
82
+ * success (see src/model-env.ts).
83
+ */
84
+ export function harnessChildEnv(role, launchEnv, stateDir) {
85
+ const env = { ...(launchEnv ?? {}), ...managedFleetProxyEnv(role, stateDir) };
86
+ assertModelPinReachesChild(role, env);
87
+ return env;
88
+ }
75
89
  /**
76
90
  * Execute a typed proxy request in the caller's supervisor. Dynamic imports
77
91
  * avoid a runner↔spawn initialization cycle (spawn imports runner constants).
@@ -109,7 +123,9 @@ async function executeManagedSpawn(caller, configPath, requested, log) {
109
123
  statePath,
110
124
  harness: preview.harness,
111
125
  session: preview.session,
112
- ...(preview.model ? { model: preview.model } : {}),
126
+ // Read back from the resolved environment, not from the request: the banner
127
+ // must name the model the child will run, not the one that was asked for.
128
+ ...(effectiveRoleModel(preview) ? { model: effectiveRoleModel(preview) } : {}),
113
129
  monitor: { mode: preview.monitor.mode, interrupt: preview.monitor.interrupt },
114
130
  permissionMode: effectivePermissionMode(preview),
115
131
  inherited,
@@ -117,6 +133,7 @@ async function executeManagedSpawn(caller, configPath, requested, log) {
117
133
  };
118
134
  log(`[${caller.name}] managed fleet proxy spawned ${result.lifetime} role ${result.role} `
119
135
  + `harness=${result.harness} session=${result.session} `
136
+ + `model=${result.model ?? '(harness default)'} `
120
137
  + `permission=${result.permissionMode.fleetMode} `
121
138
  + `native=${result.permissionMode.nativeMode}`);
122
139
  return result;
@@ -458,11 +475,18 @@ export async function runOnce(name, opts = {}, partialDeps = {}) {
458
475
  const effectiveModel = effectiveModelForRole(dir, role);
459
476
  if (effectiveModel !== role.model) {
460
477
  deps.log(`[${name}] model recovery drift: declared=${role.model ?? '(none)'} effective=${effectiveModel}`);
461
- role = { ...role, model: effectiveModel };
478
+ // The env pin has to move with it. A down-shift that changed only
479
+ // `role.model` was reported as a model change while the child kept running
480
+ // the model that had just failed, because the pin is what the harness reads.
481
+ role = { ...role, model: effectiveModel, env: repinModelEnv(role, effectiveModel) };
462
482
  }
463
483
  if (modelRecoveryHeld(dir))
464
484
  throw new Error(`[${name}] model chain exhausted — held down until config changes or recovery reset`);
465
485
  const adapter = getAdapter(role.harness);
486
+ // Say the running model out loud, once, from the resolved environment. The
487
+ // spawn banner is a claim made before the process exists; this is the log line
488
+ // that can be checked against the session afterwards.
489
+ deps.log(`[${name}] model: ${effectiveRoleModel(role) ?? '(harness default)'}`);
466
490
  mkdirSync(dir, { recursive: true });
467
491
  const rotation = rotateWorklog(join(dir, 'WORKLOG.md'), role.worklog);
468
492
  if (rotation.deferred)
@@ -603,7 +627,7 @@ export async function runOnce(name, opts = {}, partialDeps = {}) {
603
627
  name,
604
628
  argv: wrappedArgv,
605
629
  cwd: runCwd,
606
- env: { ...launch.env, ...managedFleetProxyEnv(role, dir) },
630
+ env: harnessChildEnv(role, launch.env, dir),
607
631
  stateDir: dir,
608
632
  mode,
609
633
  permissions: perms,
@@ -83,6 +83,8 @@ export declare class AcpSession implements SessionHandle {
83
83
  private readonly child;
84
84
  private readonly events;
85
85
  private readonly conversation;
86
+ /** Cursor before this runner generation began; older durable events stay off the live console. */
87
+ private readonly conversationStartCursor?;
86
88
  /** New on every runner start; permission/turn IDs from prior generations are stale. */
87
89
  private readonly sessionGeneration;
88
90
  /** True while `session/load` replays history as ordinary updates. */
@@ -300,5 +302,6 @@ export declare class AcpSession implements SessionHandle {
300
302
  }): ConversationHandlePage;
301
303
  conversationSnapshot(): ConversationSnapshot;
302
304
  subscribeConversation(listener: Parameters<ConversationEventStore['subscribe']>[0]): () => void;
305
+ private isCurrentConversationEvent;
303
306
  private fail;
304
307
  }
@@ -208,6 +208,8 @@ export class AcpSession {
208
208
  child;
209
209
  events;
210
210
  conversation;
211
+ /** Cursor before this runner generation began; older durable events stay off the live console. */
212
+ conversationStartCursor;
211
213
  /** New on every runner start; permission/turn IDs from prior generations are stale. */
212
214
  sessionGeneration = randomUUID();
213
215
  /** True while `session/load` replays history as ordinary updates. */
@@ -265,6 +267,7 @@ export class AcpSession {
265
267
  this.conversation = new ConversationEventStore(join(options.stateDir, '.conversation'), {
266
268
  roleId: options.name, log: line => options.log(`[${options.name}] ${line}`),
267
269
  });
270
+ this.conversationStartCursor = this.conversation.lastCursor();
268
271
  this.sessionFile = join(options.stateDir, '.acp-session-id');
269
272
  this.terminated = new Promise((_resolve, reject) => { this.terminate = reject; });
270
273
  // Nothing awaits this promise until a request races it; an unobserved
@@ -1464,7 +1467,25 @@ export class AcpSession {
1464
1467
  }
1465
1468
  // ── conversation ledger access (SessionHandle) ─────────────────────────────
1466
1469
  conversationPage(request = {}) {
1467
- return { ...this.conversation.page(request), snapshot: this.conversationSnapshot() };
1470
+ const floor = Number(this.conversationStartCursor ?? 0);
1471
+ const requested = Number(request.after ?? 0);
1472
+ let after = String(Math.max(Number.isSafeInteger(floor) ? floor : 0, Number.isSafeInteger(requested) ? requested : 0));
1473
+ const limit = Math.min(Math.max(request.limit ?? 200, 1), 1_000);
1474
+ let page = this.conversation.page({ after, limit });
1475
+ let visible = page.events.filter(event => this.isCurrentConversationEvent(event));
1476
+ // A resumed adapter may replay a page made entirely of prior session/load
1477
+ // history. Advance over it without exposing it or making the browser stop
1478
+ // before later current-session records.
1479
+ while (!visible.length && page.hasMore && page.nextCursor && page.nextCursor !== after) {
1480
+ after = page.nextCursor;
1481
+ page = this.conversation.page({ after, limit });
1482
+ visible = page.events.filter(event => this.isCurrentConversationEvent(event));
1483
+ }
1484
+ return {
1485
+ ...page,
1486
+ events: visible,
1487
+ snapshot: this.conversationSnapshot(),
1488
+ };
1468
1489
  }
1469
1490
  conversationSnapshot() {
1470
1491
  return {
@@ -1476,7 +1497,13 @@ export class AcpSession {
1476
1497
  };
1477
1498
  }
1478
1499
  subscribeConversation(listener) {
1479
- return this.conversation.subscribe(listener);
1500
+ return this.conversation.subscribe(event => {
1501
+ if (this.isCurrentConversationEvent(event))
1502
+ listener(event);
1503
+ });
1504
+ }
1505
+ isCurrentConversationEvent(event) {
1506
+ return event.sessionGeneration === this.sessionGeneration && event.source !== 'agent_replay';
1480
1507
  }
1481
1508
  fail(error) {
1482
1509
  this.lastError = error?.message ?? String(error);
@@ -11,6 +11,12 @@ import type { AdapterMeta, ConversationEventKind, ConversationPayload } from './
11
11
  */
12
12
  /** Cap for any single normalized text payload (spec §5.3). */
13
13
  export declare const MAX_TEXT_BYTES: number;
14
+ /** Cap for each retained side of an oversized snapshot-style file diff. */
15
+ export declare const MAX_DIFF_TEXT_BYTES: number;
16
+ /** Cap for attacker-controlled filesystem paths while retaining their useful basename tail. */
17
+ export declare const MAX_PATH_BYTES: number;
18
+ /** Hard cap for the complete normalized update before the durable event envelope is added. */
19
+ export declare const MAX_NORMALIZED_UPDATE_BYTES: number;
14
20
  /** Cap for one adapter `_meta` namespace value. */
15
21
  export declare const MAX_META_BYTES: number;
16
22
  /** Cap for serialized raw tool input/output retained as structured JSON. */
@@ -10,6 +10,12 @@ import { createHash } from 'node:crypto';
10
10
  */
11
11
  /** Cap for any single normalized text payload (spec §5.3). */
12
12
  export const MAX_TEXT_BYTES = 256 * 1024;
13
+ /** Cap for each retained side of an oversized snapshot-style file diff. */
14
+ export const MAX_DIFF_TEXT_BYTES = 64 * 1024;
15
+ /** Cap for attacker-controlled filesystem paths while retaining their useful basename tail. */
16
+ export const MAX_PATH_BYTES = 4 * 1024;
17
+ /** Hard cap for the complete normalized update before the durable event envelope is added. */
18
+ export const MAX_NORMALIZED_UPDATE_BYTES = 320 * 1024;
13
19
  /** Cap for one adapter `_meta` namespace value. */
14
20
  export const MAX_META_BYTES = 16 * 1024;
15
21
  /** Cap for serialized raw tool input/output retained as structured JSON. */
@@ -27,6 +33,28 @@ function truncateUtf8(text, maxBytes) {
27
33
  const buffer = Buffer.from(text).subarray(0, maxBytes);
28
34
  return buffer.toString('utf8').replace(/�+$/u, '');
29
35
  }
36
+ /** Keep a UTF-8-safe suffix. Paths and identifiers have no line semantics. */
37
+ function truncateUtf8Tail(text, maxBytes) {
38
+ const buffer = Buffer.from(text);
39
+ let start = Math.max(0, buffer.length - maxBytes);
40
+ while (start < buffer.length && (buffer[start] & 0xc0) === 0x80)
41
+ start++;
42
+ return { text: buffer.subarray(start).toString('utf8'), omittedPrefixBytes: start };
43
+ }
44
+ function boundedPath(raw) {
45
+ const path = asString(raw) ?? '';
46
+ const pathBytes = Buffer.byteLength(path);
47
+ if (pathBytes <= MAX_PATH_BYTES)
48
+ return { path };
49
+ const retained = truncateUtf8Tail(path, MAX_PATH_BYTES);
50
+ return {
51
+ path: retained.text,
52
+ pathBytes,
53
+ pathTruncated: true,
54
+ pathDigest: digest24(path),
55
+ pathOmittedPrefixBytes: retained.omittedPrefixBytes,
56
+ };
57
+ }
30
58
  function cappedText(raw, redact) {
31
59
  const text = asString(raw) ?? '';
32
60
  const bytes = Buffer.byteLength(text);
@@ -39,6 +67,95 @@ function cappedText(raw, redact) {
39
67
  truncated: true, digest: digest24(text),
40
68
  };
41
69
  }
70
+ /** Keep the newest UTF-8 tail, aligning to a whole line whenever one fits. */
71
+ function cappedTextTail(text, maxBytes) {
72
+ const buffer = Buffer.from(text);
73
+ const bytes = buffer.length;
74
+ if (bytes <= maxBytes)
75
+ return { text, bytes };
76
+ let start = bytes - maxBytes;
77
+ while (start < bytes && (buffer[start] & 0xc0) === 0x80)
78
+ start++;
79
+ let startsMidLine = start > 0 && buffer[start - 1] !== 0x0a;
80
+ if (startsMidLine) {
81
+ const newline = buffer.indexOf(0x0a, start);
82
+ if (newline >= 0 && newline + 1 < bytes) {
83
+ start = newline + 1;
84
+ startsMidLine = false;
85
+ }
86
+ }
87
+ return {
88
+ text: buffer.subarray(start).toString('utf8'), bytes,
89
+ truncated: true, digest: digest24(text), omittedPrefixBytes: start,
90
+ ...(startsMidLine ? { startsMidLine: true } : {}),
91
+ };
92
+ }
93
+ /** Common unchanged edges in UTF-16 indices, adjusted away from split surrogates. */
94
+ function commonEdges(oldText, newText) {
95
+ const limit = Math.min(oldText.length, newText.length);
96
+ let prefix = 0;
97
+ while (prefix < limit && oldText.charCodeAt(prefix) === newText.charCodeAt(prefix))
98
+ prefix++;
99
+ if (prefix > 0 && prefix < limit
100
+ && oldText.charCodeAt(prefix) >= 0xdc00 && oldText.charCodeAt(prefix) <= 0xdfff)
101
+ prefix--;
102
+ let oldEnd = oldText.length;
103
+ let newEnd = newText.length;
104
+ while (oldEnd > prefix && newEnd > prefix
105
+ && oldText.charCodeAt(oldEnd - 1) === newText.charCodeAt(newEnd - 1)) {
106
+ oldEnd--;
107
+ newEnd--;
108
+ }
109
+ // A suffix must never begin at the low half of a surrogate pair.
110
+ if (oldEnd < oldText.length && oldText.charCodeAt(oldEnd) >= 0xdc00
111
+ && oldText.charCodeAt(oldEnd) <= 0xdfff) {
112
+ oldEnd++;
113
+ newEnd++;
114
+ }
115
+ return { prefix, suffix: oldText.length - oldEnd };
116
+ }
117
+ function normalizedDiff(item, redact) {
118
+ const path = boundedPath(item.path);
119
+ const oldText = asString(item.oldText);
120
+ const newText = asString(item.newText) ?? '';
121
+ // Preserve the established small-diff contract exactly. Redacted turns also
122
+ // retain their established placeholder shape and never derive private text.
123
+ if (redact !== undefined || oldText === undefined
124
+ || (Buffer.byteLength(oldText) <= MAX_TEXT_BYTES
125
+ && Buffer.byteLength(newText) <= MAX_TEXT_BYTES)) {
126
+ return {
127
+ type: 'diff', ...path,
128
+ newText: cappedText(newText, redact),
129
+ ...(oldText !== undefined ? { oldText: cappedText(oldText, redact) } : {}),
130
+ };
131
+ }
132
+ // ACP adapters may describe an append by sending two complete file snapshots.
133
+ // Persist only the changed region: otherwise a multi-megabyte historical file
134
+ // contributes its prefix twice while the current append disappears past the cap.
135
+ const { prefix, suffix } = commonEdges(oldText, newText);
136
+ const oldEnd = oldText.length - suffix;
137
+ const newEnd = newText.length - suffix;
138
+ const oldDelta = oldText.slice(prefix, oldEnd);
139
+ const newDelta = newText.slice(prefix, newEnd);
140
+ const beforeBytes = Buffer.byteLength(oldText);
141
+ const afterBytes = Buffer.byteLength(newText);
142
+ const commonPrefixBytes = Buffer.byteLength(oldText.slice(0, prefix));
143
+ const commonSuffixBytes = Buffer.byteLength(oldText.slice(oldEnd));
144
+ const operation = oldDelta.length === 0 && newDelta.length === 0
145
+ ? 'noop'
146
+ : oldDelta.length === 0 && prefix === oldText.length
147
+ ? 'append'
148
+ : newDelta.length === 0
149
+ ? 'delete'
150
+ : prefix === 0 && suffix === 0 ? 'replace' : 'edit';
151
+ return {
152
+ type: 'diff', ...path, operation, beforeBytes, afterBytes,
153
+ commonPrefixBytes, commonSuffixBytes, bounded: true,
154
+ newText: cappedTextTail(newDelta, MAX_DIFF_TEXT_BYTES),
155
+ ...(oldDelta.length > 0
156
+ ? { oldText: cappedTextTail(oldDelta, MAX_DIFF_TEXT_BYTES) } : {}),
157
+ };
158
+ }
42
159
  function normalizedText(raw, redact) {
43
160
  const text = asString(raw) ?? '';
44
161
  const bytes = Buffer.byteLength(text);
@@ -189,12 +306,7 @@ function normalizeToolContent(raw, redact) {
189
306
  return raw.filter(isRecord).map((item) => {
190
307
  switch (item.type) {
191
308
  case 'diff':
192
- return {
193
- type: 'diff',
194
- path: asString(item.path) ?? '',
195
- newText: cappedText(item.newText, redact),
196
- ...(item.oldText != null ? { oldText: cappedText(item.oldText, redact) } : {}),
197
- };
309
+ return normalizedDiff(item, redact);
198
310
  case 'terminal':
199
311
  return { type: 'terminal', terminalId: asString(item.terminalId) ?? '' };
200
312
  case 'content':
@@ -220,7 +332,7 @@ function toolUpsert(update, snapshot, options) {
220
332
  payload.content = content;
221
333
  if (Array.isArray(update.locations)) {
222
334
  payload.locations = update.locations.filter(isRecord).map(location => ({
223
- path: asString(location.path) ?? '',
335
+ ...boundedPath(location.path),
224
336
  ...(asFiniteNumber(location.line) !== undefined ? { line: asFiniteNumber(location.line) } : {}),
225
337
  }));
226
338
  }
@@ -240,18 +352,49 @@ function unsupported(update) {
240
352
  }
241
353
  const kind = isRecord(update) ? asString(update.sessionUpdate) : undefined;
242
354
  return {
243
- sessionUpdate: kind ?? 'unknown',
355
+ sessionUpdate: truncateUtf8(kind ?? 'unknown', 256),
244
356
  bytes: Buffer.byteLength(serialized),
245
357
  preview: serialized.slice(0, MAX_UNSUPPORTED_PREVIEW_CHARS),
358
+ ...(Buffer.byteLength(serialized) > Buffer.byteLength(serialized.slice(0, MAX_UNSUPPORTED_PREVIEW_CHARS))
359
+ ? { digest: digest24(serialized) } : {}),
360
+ };
361
+ }
362
+ function capNormalizedUpdate(result, sessionUpdate) {
363
+ let serialized;
364
+ try {
365
+ serialized = JSON.stringify(result);
366
+ }
367
+ catch {
368
+ return {
369
+ kind: 'unsupported',
370
+ payload: {
371
+ sessionUpdate: truncateUtf8(sessionUpdate, 256),
372
+ bytes: 0,
373
+ preview: '[normalized update was not serializable]',
374
+ },
375
+ };
376
+ }
377
+ const bytes = Buffer.byteLength(serialized);
378
+ if (bytes <= MAX_NORMALIZED_UPDATE_BYTES)
379
+ return result;
380
+ return {
381
+ kind: 'unsupported',
382
+ payload: {
383
+ sessionUpdate: truncateUtf8(sessionUpdate, 256),
384
+ bytes,
385
+ digest: digest24(serialized),
386
+ preview: `[normalized update exceeded ${MAX_NORMALIZED_UPDATE_BYTES}-byte durable-event cap]`,
387
+ },
246
388
  };
247
389
  }
248
390
  export function normalizeSessionUpdate(update, options = {}) {
249
391
  const redact = options.redactText;
250
392
  const raw = update;
251
393
  if (!isRecord(raw) || typeof raw.sessionUpdate !== 'string')
252
- return { kind: 'unsupported', payload: unsupported(raw) };
394
+ return capNormalizedUpdate({ kind: 'unsupported', payload: unsupported(raw) }, 'unknown');
395
+ const sessionUpdate = raw.sessionUpdate;
253
396
  const adapterMeta = quarantineMeta(raw._meta);
254
- const withMeta = (result) => adapterMeta ? { ...result, adapterMeta } : result;
397
+ const withMeta = (result) => capNormalizedUpdate(adapterMeta ? { ...result, adapterMeta } : result, sessionUpdate);
255
398
  switch (raw.sessionUpdate) {
256
399
  case 'user_message_chunk':
257
400
  case 'agent_message_chunk': {
@@ -48,15 +48,33 @@ export interface CappedText {
48
48
  bytes: number;
49
49
  truncated?: true;
50
50
  digest?: string;
51
+ /** Bytes omitted from the front when the retained fragment is a tail. */
52
+ omittedPrefixBytes?: number;
53
+ /** The retained tail starts inside one logical line because that line alone exceeded the cap. */
54
+ startsMidLine?: true;
55
+ }
56
+ /** A path kept as a UTF-8-safe tail, with additive provenance only when capped. */
57
+ export interface BoundedPath {
58
+ path: string;
59
+ pathBytes?: number;
60
+ pathTruncated?: true;
61
+ pathDigest?: string;
62
+ pathOmittedPrefixBytes?: number;
51
63
  }
52
64
  export type NormalizedToolContent = {
53
65
  type: 'content';
54
66
  content: NormalizedContentBlock;
55
- } | {
67
+ } | BoundedPath & {
56
68
  type: 'diff';
57
- path: string;
58
69
  newText: CappedText;
59
70
  oldText?: CappedText;
71
+ /** Additive provenance for oversized snapshot diffs reduced to their changed region. */
72
+ operation?: 'append' | 'edit' | 'delete' | 'noop' | 'replace';
73
+ beforeBytes?: number;
74
+ afterBytes?: number;
75
+ commonPrefixBytes?: number;
76
+ commonSuffixBytes?: number;
77
+ bounded?: true;
60
78
  }
61
79
  /** A tool-owned display terminal reference — never a PTY attachment. */
62
80
  | {
@@ -105,8 +123,7 @@ export interface ToolUpsertPayload {
105
123
  kind?: string;
106
124
  status?: string;
107
125
  content?: NormalizedToolContent[];
108
- locations?: Array<{
109
- path: string;
126
+ locations?: Array<BoundedPath & {
110
127
  line?: number;
111
128
  }>;
112
129
  rawInput?: BoundedJson;
@@ -140,6 +157,8 @@ export interface UnsupportedPayload {
140
157
  /** The wire discriminant (or 'unknown' when even that was absent). */
141
158
  sessionUpdate: string;
142
159
  bytes: number;
160
+ /** Digest of the omitted original/normalized representation when it was bounded. */
161
+ digest?: string;
143
162
  /** Sanitized JSON preview, capped; enough to diagnose, never to exhaust. */
144
163
  preview?: string;
145
164
  }
package/dist/spawn.js CHANGED
@@ -5,6 +5,7 @@ import { parse, stringify } from 'yaml';
5
5
  import { agentDir, fleetDDir } from './paths.js';
6
6
  import { validateIsolationConfig } from './isolation/policy.js';
7
7
  import { loadConfig, resolveAuthProxy, resolveModelChain, resolveMonitorConfig, resolveOwnerChannelConfig, resolvePermissions, resolveRoleModel, resolveWorklogPolicy, validateMonitorConfig, } from './config.js';
8
+ import { resolveRoleModelEnv } from './model-env.js';
8
9
  import { applyRole, up } from './ops.js';
9
10
  import { START_STAGGER_FILE } from './runner.js';
10
11
  import { buildProvenance, daemonIdentityProvisioner, ensureIdentity, provenanceOf, withCreationTransaction, writeProvenance, writeRoleFile, } from './creation.js';
@@ -191,7 +192,17 @@ export function spawnDryRun(o) {
191
192
  const harness = raw.harness ?? cfg.defaults.harness ?? 'claude-code';
192
193
  const defaultHarness = cfg.defaults.harness ?? 'claude-code';
193
194
  const inheritsModelDefaults = harness === defaultHarness && raw.model !== null;
194
- const model = resolveRoleModel(raw.model, raw.harness, cfg.defaults);
195
+ const authProxy = resolveAuthProxy(cfg.defaults.auth_proxy, raw.auth_proxy);
196
+ // One resolution for the environment and the model it pins (src/model-env.ts).
197
+ const modelEnv = resolveRoleModelEnv({
198
+ harness,
199
+ model: resolveRoleModel(raw.model, raw.harness, cfg.defaults),
200
+ modelWasExplicit: raw.model !== undefined,
201
+ defaultsEnv: (cfg.defaults.env ?? {}),
202
+ roleEnv: raw.env,
203
+ ...(authProxy ? { authProxyBaseUrl: authProxy.base_url } : {}),
204
+ });
205
+ const model = modelEnv.model;
195
206
  const session = raw.session ?? cfg.defaults.session ?? 'tmux';
196
207
  const resolvedRole = {
197
208
  ...raw,
@@ -212,15 +223,9 @@ export function spawnDryRun(o) {
212
223
  monitor: resolveMonitorConfig(cfg.defaults.monitor, raw.monitor),
213
224
  owner_channel: resolveOwnerChannelConfig(cfg.defaults.owner_channel, raw.owner_channel, session),
214
225
  worklog: resolveWorklogPolicy(cfg.defaults.worklog, raw.worklog),
215
- auth_proxy: resolveAuthProxy(cfg.defaults.auth_proxy, raw.auth_proxy),
216
- };
217
- resolvedRole.env = {
218
- ...(cfg.defaults.env ?? {}),
219
- ...(raw.env ?? {}),
220
- ...(resolvedRole.auth_proxy
221
- ? { ANTHROPIC_BASE_URL: resolvedRole.auth_proxy.base_url }
222
- : {}),
226
+ auth_proxy: authProxy,
223
227
  };
228
+ resolvedRole.env = modelEnv.env;
224
229
  const adapter = getAdapter(resolvedRole.harness);
225
230
  if (resolvedRole.auth_proxy && resolvedRole.harness !== 'claude-code')
226
231
  throw new Error('auth_proxy is supported only by claude-code');
@@ -403,7 +408,18 @@ async function spawnTempInner(o, binPath, launch, tx, guarantee, onStage) {
403
408
  };
404
409
  const harness = o.harness ?? defaultHarness ?? 'claude-code';
405
410
  const inheritsModelDefaults = harness === (defaultHarness ?? 'claude-code') && o.model !== null;
406
- const model = resolveRoleModel(o.model, o.harness, cfg.defaults);
411
+ const tempAuthProxy = resolveAuthProxy(cfg.defaults.auth_proxy, fromOpts.auth_proxy);
412
+ // An explicitly requested --model must reach the child, not just the banner
413
+ // (src/model-env.ts).
414
+ const modelEnv = resolveRoleModelEnv({
415
+ harness,
416
+ model: resolveRoleModel(o.model, o.harness, cfg.defaults),
417
+ modelWasExplicit: o.model !== undefined,
418
+ defaultsEnv: (cfg.defaults.env ?? {}),
419
+ roleEnv: fromOpts.env,
420
+ ...(tempAuthProxy ? { authProxyBaseUrl: tempAuthProxy.base_url } : {}),
421
+ });
422
+ const model = modelEnv.model;
407
423
  const session = o.session ?? cfg.defaults.session ?? 'tmux';
408
424
  const role = {
409
425
  ...fromOpts, // includes `isolation` when --isolation-file was given
@@ -422,14 +438,10 @@ async function spawnTempInner(o, binPath, launch, tx, guarantee, onStage) {
422
438
  monitor: resolveMonitorConfig(cfg.defaults.monitor, fromOpts.monitor),
423
439
  owner_channel: resolveOwnerChannelConfig(cfg.defaults.owner_channel, fromOpts.owner_channel, session),
424
440
  worklog: resolveWorklogPolicy(cfg.defaults.worklog, fromOpts.worklog),
425
- auth_proxy: resolveAuthProxy(cfg.defaults.auth_proxy, fromOpts.auth_proxy),
441
+ auth_proxy: tempAuthProxy,
426
442
  sourceFile: '(temp)',
427
443
  };
428
- role.env = {
429
- ...(cfg.defaults.env ?? {}),
430
- ...(fromOpts.env ?? {}),
431
- ...(role.auth_proxy ? { ANTHROPIC_BASE_URL: role.auth_proxy.base_url } : {}),
432
- };
444
+ role.env = modelEnv.env;
433
445
  if (role.auth_proxy && role.harness !== 'claude-code')
434
446
  throw new Error('auth_proxy is supported only by claude-code');
435
447
  onStage?.('writing_role');
@@ -1,4 +1,4 @@
1
- import{r as le,a as Ee,j as re}from"./index-C3S-xFRU.js";var ge={exports:{}},Se;function ke(){return Se||(Se=1,(function(se,ne){(function(Q,X){se.exports=X()})(globalThis,(()=>(()=>{var Q={4567:function(B,r,o){var l=this&&this.__decorate||function(e,i,a,v){var f,g=arguments.length,c=g<3?i:v===null?v=Object.getOwnPropertyDescriptor(i,a):v;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")c=Reflect.decorate(e,i,a,v);else for(var m=e.length-1;m>=0;m--)(f=e[m])&&(c=(g<3?f(c):g>3?f(i,a,c):f(i,a))||c);return g>3&&c&&Object.defineProperty(i,a,c),c},_=this&&this.__param||function(e,i){return function(a,v){i(a,v,e)}};Object.defineProperty(r,"__esModule",{value:!0}),r.AccessibilityManager=void 0;const n=o(9042),d=o(9924),u=o(844),p=o(4725),h=o(2585),t=o(3656);let s=r.AccessibilityManager=class extends u.Disposable{constructor(e,i,a,v){super(),this._terminal=e,this._coreBrowserService=a,this._renderService=v,this._rowColumns=new WeakMap,this._liveRegionLineCount=0,this._charsToConsume=[],this._charsToAnnounce="",this._accessibilityContainer=this._coreBrowserService.mainDocument.createElement("div"),this._accessibilityContainer.classList.add("xterm-accessibility"),this._rowContainer=this._coreBrowserService.mainDocument.createElement("div"),this._rowContainer.setAttribute("role","list"),this._rowContainer.classList.add("xterm-accessibility-tree"),this._rowElements=[];for(let f=0;f<this._terminal.rows;f++)this._rowElements[f]=this._createAccessibilityTreeNode(),this._rowContainer.appendChild(this._rowElements[f]);if(this._topBoundaryFocusListener=f=>this._handleBoundaryFocus(f,0),this._bottomBoundaryFocusListener=f=>this._handleBoundaryFocus(f,1),this._rowElements[0].addEventListener("focus",this._topBoundaryFocusListener),this._rowElements[this._rowElements.length-1].addEventListener("focus",this._bottomBoundaryFocusListener),this._refreshRowsDimensions(),this._accessibilityContainer.appendChild(this._rowContainer),this._liveRegion=this._coreBrowserService.mainDocument.createElement("div"),this._liveRegion.classList.add("live-region"),this._liveRegion.setAttribute("aria-live","assertive"),this._accessibilityContainer.appendChild(this._liveRegion),this._liveRegionDebouncer=this.register(new d.TimeBasedDebouncer(this._renderRows.bind(this))),!this._terminal.element)throw new Error("Cannot enable accessibility before Terminal.open");this._terminal.element.insertAdjacentElement("afterbegin",this._accessibilityContainer),this.register(this._terminal.onResize((f=>this._handleResize(f.rows)))),this.register(this._terminal.onRender((f=>this._refreshRows(f.start,f.end)))),this.register(this._terminal.onScroll((()=>this._refreshRows()))),this.register(this._terminal.onA11yChar((f=>this._handleChar(f)))),this.register(this._terminal.onLineFeed((()=>this._handleChar(`
1
+ import{r as le,a as Ee,j as re}from"./index-BCBK78hw.js";var ge={exports:{}},Se;function ke(){return Se||(Se=1,(function(se,ne){(function(Q,X){se.exports=X()})(globalThis,(()=>(()=>{var Q={4567:function(B,r,o){var l=this&&this.__decorate||function(e,i,a,v){var f,g=arguments.length,c=g<3?i:v===null?v=Object.getOwnPropertyDescriptor(i,a):v;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")c=Reflect.decorate(e,i,a,v);else for(var m=e.length-1;m>=0;m--)(f=e[m])&&(c=(g<3?f(c):g>3?f(i,a,c):f(i,a))||c);return g>3&&c&&Object.defineProperty(i,a,c),c},_=this&&this.__param||function(e,i){return function(a,v){i(a,v,e)}};Object.defineProperty(r,"__esModule",{value:!0}),r.AccessibilityManager=void 0;const n=o(9042),d=o(9924),u=o(844),p=o(4725),h=o(2585),t=o(3656);let s=r.AccessibilityManager=class extends u.Disposable{constructor(e,i,a,v){super(),this._terminal=e,this._coreBrowserService=a,this._renderService=v,this._rowColumns=new WeakMap,this._liveRegionLineCount=0,this._charsToConsume=[],this._charsToAnnounce="",this._accessibilityContainer=this._coreBrowserService.mainDocument.createElement("div"),this._accessibilityContainer.classList.add("xterm-accessibility"),this._rowContainer=this._coreBrowserService.mainDocument.createElement("div"),this._rowContainer.setAttribute("role","list"),this._rowContainer.classList.add("xterm-accessibility-tree"),this._rowElements=[];for(let f=0;f<this._terminal.rows;f++)this._rowElements[f]=this._createAccessibilityTreeNode(),this._rowContainer.appendChild(this._rowElements[f]);if(this._topBoundaryFocusListener=f=>this._handleBoundaryFocus(f,0),this._bottomBoundaryFocusListener=f=>this._handleBoundaryFocus(f,1),this._rowElements[0].addEventListener("focus",this._topBoundaryFocusListener),this._rowElements[this._rowElements.length-1].addEventListener("focus",this._bottomBoundaryFocusListener),this._refreshRowsDimensions(),this._accessibilityContainer.appendChild(this._rowContainer),this._liveRegion=this._coreBrowserService.mainDocument.createElement("div"),this._liveRegion.classList.add("live-region"),this._liveRegion.setAttribute("aria-live","assertive"),this._accessibilityContainer.appendChild(this._liveRegion),this._liveRegionDebouncer=this.register(new d.TimeBasedDebouncer(this._renderRows.bind(this))),!this._terminal.element)throw new Error("Cannot enable accessibility before Terminal.open");this._terminal.element.insertAdjacentElement("afterbegin",this._accessibilityContainer),this.register(this._terminal.onResize((f=>this._handleResize(f.rows)))),this.register(this._terminal.onRender((f=>this._refreshRows(f.start,f.end)))),this.register(this._terminal.onScroll((()=>this._refreshRows()))),this.register(this._terminal.onA11yChar((f=>this._handleChar(f)))),this.register(this._terminal.onLineFeed((()=>this._handleChar(`
2
2
  `)))),this.register(this._terminal.onA11yTab((f=>this._handleTab(f)))),this.register(this._terminal.onKey((f=>this._handleKey(f.key)))),this.register(this._terminal.onBlur((()=>this._clearLiveRegion()))),this.register(this._renderService.onDimensionsChange((()=>this._refreshRowsDimensions()))),this.register((0,t.addDisposableDomListener)(document,"selectionchange",(()=>this._handleSelectionChange()))),this.register(this._coreBrowserService.onDprChange((()=>this._refreshRowsDimensions()))),this._refreshRows(),this.register((0,u.toDisposable)((()=>{this._accessibilityContainer.remove(),this._rowElements.length=0})))}_handleTab(e){for(let i=0;i<e;i++)this._handleChar(" ")}_handleChar(e){this._liveRegionLineCount<21&&(this._charsToConsume.length>0?this._charsToConsume.shift()!==e&&(this._charsToAnnounce+=e):this._charsToAnnounce+=e,e===`
3
3
  `&&(this._liveRegionLineCount++,this._liveRegionLineCount===21&&(this._liveRegion.textContent+=n.tooMuchOutput)))}_clearLiveRegion(){this._liveRegion.textContent="",this._liveRegionLineCount=0}_handleKey(e){this._clearLiveRegion(),new RegExp("\\p{Control}","u").test(e)||this._charsToConsume.push(e)}_refreshRows(e,i){this._liveRegionDebouncer.refresh(e,i,this._terminal.rows)}_renderRows(e,i){const a=this._terminal.buffer,v=a.lines.length.toString();for(let f=e;f<=i;f++){const g=a.lines.get(a.ydisp+f),c=[],m=g?.translateToString(!0,void 0,void 0,c)||"",E=(a.ydisp+f+1).toString(),k=this._rowElements[f];k&&(m.length===0?(k.innerText=" ",this._rowColumns.set(k,[0,1])):(k.textContent=m,this._rowColumns.set(k,c)),k.setAttribute("aria-posinset",E),k.setAttribute("aria-setsize",v))}this._announceCharacters()}_announceCharacters(){this._charsToAnnounce.length!==0&&(this._liveRegion.textContent+=this._charsToAnnounce,this._charsToAnnounce="")}_handleBoundaryFocus(e,i){const a=e.target,v=this._rowElements[i===0?1:this._rowElements.length-2];if(a.getAttribute("aria-posinset")===(i===0?"1":`${this._terminal.buffer.lines.length}`)||e.relatedTarget!==v)return;let f,g;if(i===0?(f=a,g=this._rowElements.pop(),this._rowContainer.removeChild(g)):(f=this._rowElements.shift(),g=a,this._rowContainer.removeChild(f)),f.removeEventListener("focus",this._topBoundaryFocusListener),g.removeEventListener("focus",this._bottomBoundaryFocusListener),i===0){const c=this._createAccessibilityTreeNode();this._rowElements.unshift(c),this._rowContainer.insertAdjacentElement("afterbegin",c)}else{const c=this._createAccessibilityTreeNode();this._rowElements.push(c),this._rowContainer.appendChild(c)}this._rowElements[0].addEventListener("focus",this._topBoundaryFocusListener),this._rowElements[this._rowElements.length-1].addEventListener("focus",this._bottomBoundaryFocusListener),this._terminal.scrollLines(i===0?-1:1),this._rowElements[i===0?1:this._rowElements.length-2].focus(),e.preventDefault(),e.stopImmediatePropagation()}_handleSelectionChange(){if(this._rowElements.length===0)return;const e=document.getSelection();if(!e)return;if(e.isCollapsed)return void(this._rowContainer.contains(e.anchorNode)&&this._terminal.clearSelection());if(!e.anchorNode||!e.focusNode)return void console.error("anchorNode and/or focusNode are null");let i={node:e.anchorNode,offset:e.anchorOffset},a={node:e.focusNode,offset:e.focusOffset};if((i.node.compareDocumentPosition(a.node)&Node.DOCUMENT_POSITION_PRECEDING||i.node===a.node&&i.offset>a.offset)&&([i,a]=[a,i]),i.node.compareDocumentPosition(this._rowElements[0])&(Node.DOCUMENT_POSITION_CONTAINED_BY|Node.DOCUMENT_POSITION_FOLLOWING)&&(i={node:this._rowElements[0].childNodes[0],offset:0}),!this._rowContainer.contains(i.node))return;const v=this._rowElements.slice(-1)[0];if(a.node.compareDocumentPosition(v)&(Node.DOCUMENT_POSITION_CONTAINED_BY|Node.DOCUMENT_POSITION_PRECEDING)&&(a={node:v,offset:v.textContent?.length??0}),!this._rowContainer.contains(a.node))return;const f=({node:m,offset:E})=>{const k=m instanceof Text?m.parentNode:m;let D=parseInt(k?.getAttribute("aria-posinset"),10)-1;if(isNaN(D))return console.warn("row is invalid. Race condition?"),null;const b=this._rowColumns.get(k);if(!b)return console.warn("columns is null. Race condition?"),null;let x=E<b.length?b[E]:b.slice(-1)[0]+1;return x>=this._terminal.cols&&(++D,x=0),{row:D,column:x}},g=f(i),c=f(a);if(g&&c){if(g.row>c.row||g.row===c.row&&g.column>=c.column)throw new Error("invalid range");this._terminal.select(g.column,g.row,(c.row-g.row)*this._terminal.cols-g.column+c.column)}}_handleResize(e){this._rowElements[this._rowElements.length-1].removeEventListener("focus",this._bottomBoundaryFocusListener);for(let i=this._rowContainer.children.length;i<this._terminal.rows;i++)this._rowElements[i]=this._createAccessibilityTreeNode(),this._rowContainer.appendChild(this._rowElements[i]);for(;this._rowElements.length>e;)this._rowContainer.removeChild(this._rowElements.pop());this._rowElements[this._rowElements.length-1].addEventListener("focus",this._bottomBoundaryFocusListener),this._refreshRowsDimensions()}_createAccessibilityTreeNode(){const e=this._coreBrowserService.mainDocument.createElement("div");return e.setAttribute("role","listitem"),e.tabIndex=-1,this._refreshRowDimensions(e),e}_refreshRowsDimensions(){if(this._renderService.dimensions.css.cell.height){this._accessibilityContainer.style.width=`${this._renderService.dimensions.css.canvas.width}px`,this._rowElements.length!==this._terminal.rows&&this._handleResize(this._terminal.rows);for(let e=0;e<this._terminal.rows;e++)this._refreshRowDimensions(this._rowElements[e])}}_refreshRowDimensions(e){e.style.height=`${this._renderService.dimensions.css.cell.height}px`}};r.AccessibilityManager=s=l([_(1,h.IInstantiationService),_(2,p.ICoreBrowserService),_(3,p.IRenderService)],s)},3614:(B,r)=>{function o(d){return d.replace(/\r?\n/g,"\r")}function l(d,u){return u?"\x1B[200~"+d+"\x1B[201~":d}function _(d,u,p,h){d=l(d=o(d),p.decPrivateModes.bracketedPasteMode&&h.rawOptions.ignoreBracketedPasteMode!==!0),p.triggerDataEvent(d,!0),u.value=""}function n(d,u,p){const h=p.getBoundingClientRect(),t=d.clientX-h.left-10,s=d.clientY-h.top-10;u.style.width="20px",u.style.height="20px",u.style.left=`${t}px`,u.style.top=`${s}px`,u.style.zIndex="1000",u.focus()}Object.defineProperty(r,"__esModule",{value:!0}),r.rightClickHandler=r.moveTextAreaUnderMouseCursor=r.paste=r.handlePasteEvent=r.copyHandler=r.bracketTextForPaste=r.prepareTextForTerminal=void 0,r.prepareTextForTerminal=o,r.bracketTextForPaste=l,r.copyHandler=function(d,u){d.clipboardData&&d.clipboardData.setData("text/plain",u.selectionText),d.preventDefault()},r.handlePasteEvent=function(d,u,p,h){d.stopPropagation(),d.clipboardData&&_(d.clipboardData.getData("text/plain"),u,p,h)},r.paste=_,r.moveTextAreaUnderMouseCursor=n,r.rightClickHandler=function(d,u,p,h,t){n(d,u,p),t&&h.rightClickSelect(d),u.value=h.selectionText,u.select()}},7239:(B,r,o)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.ColorContrastCache=void 0;const l=o(1505);r.ColorContrastCache=class{constructor(){this._color=new l.TwoKeyMap,this._css=new l.TwoKeyMap}setCss(_,n,d){this._css.set(_,n,d)}getCss(_,n){return this._css.get(_,n)}setColor(_,n,d){this._color.set(_,n,d)}getColor(_,n){return this._color.get(_,n)}clear(){this._color.clear(),this._css.clear()}}},3656:(B,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.addDisposableDomListener=void 0,r.addDisposableDomListener=function(o,l,_,n){o.addEventListener(l,_,n);let d=!1;return{dispose:()=>{d||(d=!0,o.removeEventListener(l,_,n))}}}},3551:function(B,r,o){var l=this&&this.__decorate||function(s,e,i,a){var v,f=arguments.length,g=f<3?e:a===null?a=Object.getOwnPropertyDescriptor(e,i):a;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")g=Reflect.decorate(s,e,i,a);else for(var c=s.length-1;c>=0;c--)(v=s[c])&&(g=(f<3?v(g):f>3?v(e,i,g):v(e,i))||g);return f>3&&g&&Object.defineProperty(e,i,g),g},_=this&&this.__param||function(s,e){return function(i,a){e(i,a,s)}};Object.defineProperty(r,"__esModule",{value:!0}),r.Linkifier=void 0;const n=o(3656),d=o(8460),u=o(844),p=o(2585),h=o(4725);let t=r.Linkifier=class extends u.Disposable{get currentLink(){return this._currentLink}constructor(s,e,i,a,v){super(),this._element=s,this._mouseService=e,this._renderService=i,this._bufferService=a,this._linkProviderService=v,this._linkCacheDisposables=[],this._isMouseOut=!0,this._wasResized=!1,this._activeLine=-1,this._onShowLinkUnderline=this.register(new d.EventEmitter),this.onShowLinkUnderline=this._onShowLinkUnderline.event,this._onHideLinkUnderline=this.register(new d.EventEmitter),this.onHideLinkUnderline=this._onHideLinkUnderline.event,this.register((0,u.getDisposeArrayDisposable)(this._linkCacheDisposables)),this.register((0,u.toDisposable)((()=>{this._lastMouseEvent=void 0,this._activeProviderReplies?.clear()}))),this.register(this._bufferService.onResize((()=>{this._clearCurrentLink(),this._wasResized=!0}))),this.register((0,n.addDisposableDomListener)(this._element,"mouseleave",(()=>{this._isMouseOut=!0,this._clearCurrentLink()}))),this.register((0,n.addDisposableDomListener)(this._element,"mousemove",this._handleMouseMove.bind(this))),this.register((0,n.addDisposableDomListener)(this._element,"mousedown",this._handleMouseDown.bind(this))),this.register((0,n.addDisposableDomListener)(this._element,"mouseup",this._handleMouseUp.bind(this)))}_handleMouseMove(s){this._lastMouseEvent=s;const e=this._positionFromMouseEvent(s,this._element,this._mouseService);if(!e)return;this._isMouseOut=!1;const i=s.composedPath();for(let a=0;a<i.length;a++){const v=i[a];if(v.classList.contains("xterm"))break;if(v.classList.contains("xterm-hover"))return}this._lastBufferCell&&e.x===this._lastBufferCell.x&&e.y===this._lastBufferCell.y||(this._handleHover(e),this._lastBufferCell=e)}_handleHover(s){if(this._activeLine!==s.y||this._wasResized)return this._clearCurrentLink(),this._askForLink(s,!1),void(this._wasResized=!1);this._currentLink&&this._linkAtPosition(this._currentLink.link,s)||(this._clearCurrentLink(),this._askForLink(s,!0))}_askForLink(s,e){this._activeProviderReplies&&e||(this._activeProviderReplies?.forEach((a=>{a?.forEach((v=>{v.link.dispose&&v.link.dispose()}))})),this._activeProviderReplies=new Map,this._activeLine=s.y);let i=!1;for(const[a,v]of this._linkProviderService.linkProviders.entries())e?this._activeProviderReplies?.get(a)&&(i=this._checkLinkProviderResult(a,s,i)):v.provideLinks(s.y,(f=>{if(this._isMouseOut)return;const g=f?.map((c=>({link:c})));this._activeProviderReplies?.set(a,g),i=this._checkLinkProviderResult(a,s,i),this._activeProviderReplies?.size===this._linkProviderService.linkProviders.length&&this._removeIntersectingLinks(s.y,this._activeProviderReplies)}))}_removeIntersectingLinks(s,e){const i=new Set;for(let a=0;a<e.size;a++){const v=e.get(a);if(v)for(let f=0;f<v.length;f++){const g=v[f],c=g.link.range.start.y<s?0:g.link.range.start.x,m=g.link.range.end.y>s?this._bufferService.cols:g.link.range.end.x;for(let E=c;E<=m;E++){if(i.has(E)){v.splice(f--,1);break}i.add(E)}}}}_checkLinkProviderResult(s,e,i){if(!this._activeProviderReplies)return i;const a=this._activeProviderReplies.get(s);let v=!1;for(let f=0;f<s;f++)this._activeProviderReplies.has(f)&&!this._activeProviderReplies.get(f)||(v=!0);if(!v&&a){const f=a.find((g=>this._linkAtPosition(g.link,e)));f&&(i=!0,this._handleNewLink(f))}if(this._activeProviderReplies.size===this._linkProviderService.linkProviders.length&&!i)for(let f=0;f<this._activeProviderReplies.size;f++){const g=this._activeProviderReplies.get(f)?.find((c=>this._linkAtPosition(c.link,e)));if(g){i=!0,this._handleNewLink(g);break}}return i}_handleMouseDown(){this._mouseDownLink=this._currentLink}_handleMouseUp(s){if(!this._currentLink)return;const e=this._positionFromMouseEvent(s,this._element,this._mouseService);e&&this._mouseDownLink===this._currentLink&&this._linkAtPosition(this._currentLink.link,e)&&this._currentLink.link.activate(s,this._currentLink.link.text)}_clearCurrentLink(s,e){this._currentLink&&this._lastMouseEvent&&(!s||!e||this._currentLink.link.range.start.y>=s&&this._currentLink.link.range.end.y<=e)&&(this._linkLeave(this._element,this._currentLink.link,this._lastMouseEvent),this._currentLink=void 0,(0,u.disposeArray)(this._linkCacheDisposables))}_handleNewLink(s){if(!this._lastMouseEvent)return;const e=this._positionFromMouseEvent(this._lastMouseEvent,this._element,this._mouseService);e&&this._linkAtPosition(s.link,e)&&(this._currentLink=s,this._currentLink.state={decorations:{underline:s.link.decorations===void 0||s.link.decorations.underline,pointerCursor:s.link.decorations===void 0||s.link.decorations.pointerCursor},isHovered:!0},this._linkHover(this._element,s.link,this._lastMouseEvent),s.link.decorations={},Object.defineProperties(s.link.decorations,{pointerCursor:{get:()=>this._currentLink?.state?.decorations.pointerCursor,set:i=>{this._currentLink?.state&&this._currentLink.state.decorations.pointerCursor!==i&&(this._currentLink.state.decorations.pointerCursor=i,this._currentLink.state.isHovered&&this._element.classList.toggle("xterm-cursor-pointer",i))}},underline:{get:()=>this._currentLink?.state?.decorations.underline,set:i=>{this._currentLink?.state&&this._currentLink?.state?.decorations.underline!==i&&(this._currentLink.state.decorations.underline=i,this._currentLink.state.isHovered&&this._fireUnderlineEvent(s.link,i))}}}),this._linkCacheDisposables.push(this._renderService.onRenderedViewportChange((i=>{if(!this._currentLink)return;const a=i.start===0?0:i.start+1+this._bufferService.buffer.ydisp,v=this._bufferService.buffer.ydisp+1+i.end;if(this._currentLink.link.range.start.y>=a&&this._currentLink.link.range.end.y<=v&&(this._clearCurrentLink(a,v),this._lastMouseEvent)){const f=this._positionFromMouseEvent(this._lastMouseEvent,this._element,this._mouseService);f&&this._askForLink(f,!1)}}))))}_linkHover(s,e,i){this._currentLink?.state&&(this._currentLink.state.isHovered=!0,this._currentLink.state.decorations.underline&&this._fireUnderlineEvent(e,!0),this._currentLink.state.decorations.pointerCursor&&s.classList.add("xterm-cursor-pointer")),e.hover&&e.hover(i,e.text)}_fireUnderlineEvent(s,e){const i=s.range,a=this._bufferService.buffer.ydisp,v=this._createLinkUnderlineEvent(i.start.x-1,i.start.y-a-1,i.end.x,i.end.y-a-1,void 0);(e?this._onShowLinkUnderline:this._onHideLinkUnderline).fire(v)}_linkLeave(s,e,i){this._currentLink?.state&&(this._currentLink.state.isHovered=!1,this._currentLink.state.decorations.underline&&this._fireUnderlineEvent(e,!1),this._currentLink.state.decorations.pointerCursor&&s.classList.remove("xterm-cursor-pointer")),e.leave&&e.leave(i,e.text)}_linkAtPosition(s,e){const i=s.range.start.y*this._bufferService.cols+s.range.start.x,a=s.range.end.y*this._bufferService.cols+s.range.end.x,v=e.y*this._bufferService.cols+e.x;return i<=v&&v<=a}_positionFromMouseEvent(s,e,i){const a=i.getCoords(s,e,this._bufferService.cols,this._bufferService.rows);if(a)return{x:a[0],y:a[1]+this._bufferService.buffer.ydisp}}_createLinkUnderlineEvent(s,e,i,a,v){return{x1:s,y1:e,x2:i,y2:a,cols:this._bufferService.cols,fg:v}}};r.Linkifier=t=l([_(1,h.IMouseService),_(2,h.IRenderService),_(3,p.IBufferService),_(4,h.ILinkProviderService)],t)},9042:(B,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.tooMuchOutput=r.promptLabel=void 0,r.promptLabel="Terminal input",r.tooMuchOutput="Too much output to announce, navigate to rows manually to read"},3730:function(B,r,o){var l=this&&this.__decorate||function(h,t,s,e){var i,a=arguments.length,v=a<3?t:e===null?e=Object.getOwnPropertyDescriptor(t,s):e;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")v=Reflect.decorate(h,t,s,e);else for(var f=h.length-1;f>=0;f--)(i=h[f])&&(v=(a<3?i(v):a>3?i(t,s,v):i(t,s))||v);return a>3&&v&&Object.defineProperty(t,s,v),v},_=this&&this.__param||function(h,t){return function(s,e){t(s,e,h)}};Object.defineProperty(r,"__esModule",{value:!0}),r.OscLinkProvider=void 0;const n=o(511),d=o(2585);let u=r.OscLinkProvider=class{constructor(h,t,s){this._bufferService=h,this._optionsService=t,this._oscLinkService=s}provideLinks(h,t){const s=this._bufferService.buffer.lines.get(h-1);if(!s)return void t(void 0);const e=[],i=this._optionsService.rawOptions.linkHandler,a=new n.CellData,v=s.getTrimmedLength();let f=-1,g=-1,c=!1;for(let m=0;m<v;m++)if(g!==-1||s.hasContent(m)){if(s.loadCell(m,a),a.hasExtendedAttrs()&&a.extended.urlId){if(g===-1){g=m,f=a.extended.urlId;continue}c=a.extended.urlId!==f}else g!==-1&&(c=!0);if(c||g!==-1&&m===v-1){const E=this._oscLinkService.getLinkData(f)?.uri;if(E){const k={start:{x:g+1,y:h},end:{x:m+(c||m!==v-1?0:1),y:h}};let D=!1;if(!i?.allowNonHttpProtocols)try{const b=new URL(E);["http:","https:"].includes(b.protocol)||(D=!0)}catch{D=!0}D||e.push({text:E,range:k,activate:(b,x)=>i?i.activate(b,x,k):p(0,x),hover:(b,x)=>i?.hover?.(b,x,k),leave:(b,x)=>i?.leave?.(b,x,k)})}c=!1,a.hasExtendedAttrs()&&a.extended.urlId?(g=m,f=a.extended.urlId):(g=-1,f=-1)}}t(e)}};function p(h,t){if(confirm(`Do you want to navigate to ${t}?
4
4