@ours.network/fleet 0.18.0-nightly.6 → 0.18.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/README.md +81 -43
  2. package/dist/application/fleet-query-service.js +12 -0
  3. package/dist/application/role-creation-service.js +3 -1
  4. package/dist/application/types.d.ts +11 -0
  5. package/dist/briefing.js +9 -2
  6. package/dist/build-info.json +5 -5
  7. package/dist/cli.js +37 -7
  8. package/dist/config.d.ts +6 -3
  9. package/dist/config.js +28 -14
  10. package/dist/creation.d.ts +14 -15
  11. package/dist/creation.js +19 -13
  12. package/dist/docs.d.ts +1 -1
  13. package/dist/docs.js +92 -33
  14. package/dist/doctor.d.ts +1 -5
  15. package/dist/doctor.js +11 -18
  16. package/dist/fleet-proxy.d.ts +5 -0
  17. package/dist/harness/acp-agent.js +11 -6
  18. package/dist/harness/claude-code.js +200 -11
  19. package/dist/harness/codex.d.ts +4 -1
  20. package/dist/harness/codex.js +70 -12
  21. package/dist/harness/types.d.ts +54 -4
  22. package/dist/loops/manager.d.ts +30 -1
  23. package/dist/loops/manager.js +69 -6
  24. package/dist/loops/state.d.ts +18 -0
  25. package/dist/loops/state.js +4 -0
  26. package/dist/model-env.d.ts +71 -0
  27. package/dist/model-env.js +106 -0
  28. package/dist/monitor.js +1 -1
  29. package/dist/ops.js +1 -1
  30. package/dist/owner-channel/attachments.d.ts +2 -25
  31. package/dist/owner-channel/attachments.js +5 -61
  32. package/dist/owner-channel/channel.d.ts +30 -29
  33. package/dist/owner-channel/channel.js +291 -291
  34. package/dist/owner-channel/mcp.d.ts +24 -0
  35. package/dist/owner-channel/mcp.js +145 -0
  36. package/dist/owner-channel/notices.d.ts +7 -0
  37. package/dist/owner-channel/notices.js +9 -0
  38. package/dist/runner.d.ts +48 -0
  39. package/dist/runner.js +237 -85
  40. package/dist/session/acp.d.ts +104 -0
  41. package/dist/session/acp.js +213 -10
  42. package/dist/session/activity.d.ts +31 -0
  43. package/dist/session/activity.js +48 -0
  44. package/dist/session/conversation-normalizer.d.ts +6 -0
  45. package/dist/session/conversation-normalizer.js +153 -10
  46. package/dist/session/conversation-types.d.ts +23 -4
  47. package/dist/session/types.d.ts +35 -0
  48. package/dist/spawn.js +29 -17
  49. package/dist/supervisor/systemd.js +2 -29
  50. package/dist/watchdog/briefing.js +7 -0
  51. package/dist/web-app/assets/{TerminalView-BAVk1Bot.js → TerminalView-C_G1ID2P.js} +1 -1
  52. package/dist/web-app/assets/{index-C3S-xFRU.js → index-BCBK78hw.js} +5 -5
  53. package/dist/web-app/index.html +1 -1
  54. package/dist/worklog.d.ts +7 -1
  55. package/dist/worklog.js +191 -39
  56. package/package.json +1 -3
  57. package/dist/owner-channel/message-recovery.d.ts +0 -25
  58. package/dist/owner-channel/message-recovery.js +0 -114
  59. package/dist/owner-channel/ours-client.d.ts +0 -148
  60. package/dist/owner-channel/ours-client.js +0 -231
@@ -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
  }
@@ -1,5 +1,16 @@
1
1
  import type { SessionBackendId } from '../config.js';
2
2
  import type { ConversationEventV1, ConversationSnapshot, PromptReceipt, SubmitPromptCommand } from './conversation-types.js';
3
+ /**
4
+ * TURN OCCUPANCY, and nothing else: `idle` means no fleet-tracked turn is in
5
+ * flight, which is exactly the question `arbiter.tryScheduled` asks before it
6
+ * admits a prompt. It is NOT a claim that the agent is doing nothing — a wake
7
+ * delivered through the `_session/steering` extension answers `startedNewTurn`
8
+ * and runs a whole turn that fleet never gets a `session/prompt` response for
9
+ * (ACP has no turn-end session update), so `readiness` stays `idle` for its
10
+ * entire duration. Anything reporting activity or liveness to a human must
11
+ * corroborate with `SessionSnapshot.activity` instead of reading `idle` here as
12
+ * "not working".
13
+ */
3
14
  export type SessionReadiness = 'starting' | 'idle' | 'running' | 'awaiting_permission' | 'failed';
4
15
  export type TurnOutcome = 'completed' | 'refused' | 'cancelled' | 'failed' | 'inconclusive';
5
16
  export type TurnCancellationSource = 'owner' | 'local-console' | 'fleet-monitor' | 'scheduled-loop' | 'shutdown';
@@ -99,6 +110,15 @@ export declare function interruptOutcome(result: InterruptResult): InterruptOutc
99
110
  * stop here: the session has the prompt, and waiting for the turn to finish is
100
111
  * a different question with a different, much longer, timescale.
101
112
  */
113
+ /**
114
+ * What actually happened to an admitted prompt, so a caller reporting to a
115
+ * human can be accurate instead of repeating what it asked for.
116
+ *
117
+ * `interrupted` is only ever returned when a turn was really cancelled for this
118
+ * prompt. `deferred` says the session is busy with work this prompt could not
119
+ * safely pre-empt — the prompt is admitted and will run, just not yet.
120
+ */
121
+ export type PromptDelivery = 'started' | 'queued' | 'interrupted' | 'deferred';
102
122
  export interface QueuedPrompt {
103
123
  promptId: string;
104
124
  /** Turns already queued ahead of this one. 0 means it starts immediately. */
@@ -106,6 +126,8 @@ export interface QueuedPrompt {
106
126
  origin?: PromptOrigin;
107
127
  /** The turn's terminal result. Never rejects. */
108
128
  completion: Promise<TurnResult>;
129
+ /** Observed admission outcome. Absent on backends that do not report it. */
130
+ delivery?: PromptDelivery;
109
131
  }
110
132
  /**
111
133
  * How a session's process ended.
@@ -170,6 +192,19 @@ export interface SessionSnapshot {
170
192
  /** Exact harness-native approval/permission mode used by this runner. */
171
193
  nativeMode: string;
172
194
  };
195
+ /**
196
+ * Observed agent activity, independent of turn occupancy: the evidence a
197
+ * human-facing surface needs before calling a role idle. Absent on backends
198
+ * that cannot observe the agent at all (tmux), which is itself honest — no
199
+ * evidence is not evidence of inactivity.
200
+ */
201
+ activity?: SessionActivity;
202
+ }
203
+ export interface SessionActivity {
204
+ /** ACP tool calls currently reserved (lifecycle open or permission pending). */
205
+ activeToolCalls: number;
206
+ /** When the agent last sent ANY session update, replay excluded. */
207
+ lastUpdateAt?: string;
173
208
  }
174
209
  export type SessionEventKind = 'state' | 'agent_text' | 'thought' | 'tool_call' | 'tool_update' | 'permission' | 'monitor_delivery' | 'turn_stop' | 'error';
175
210
  /** What a settled permission request resolved to. */
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,19 +223,13 @@ 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');
227
- const optionProblems = adapter.validateOptions(resolvedRole.harness_options);
232
+ const optionProblems = adapter.validateOptions(resolvedRole.harness_options, resolvedRole);
228
233
  if (optionProblems.length)
229
234
  throw new Error(optionProblems.map(problem => `${problem.path}: ${problem.message}`).join('; '));
230
235
  return {
@@ -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');
@@ -64,38 +64,11 @@ export function makeSystemdBackend(exec = realExec) {
64
64
  const unitDir = join(home(), '.config', 'systemd', 'user');
65
65
  // Lingering user units often start before a login shell imports its PATH.
66
66
  // Pin the Node runtime and persist the install-time PATH so the runner and
67
- // structured operator CLI resolve the same tools after reboot.
67
+ // children such as `ours-mcp proxy` resolve the same tools after reboot.
68
68
  const servicePath = [...new Set([
69
69
  dirname(process.execPath),
70
70
  ...(process.env.PATH ?? '').split(delimiter),
71
71
  ].filter(Boolean))].join(delimiter);
72
- // Same reason as PATH, for the other thing a lingering unit cannot inherit:
73
- // WHICH ours daemon this fleet was set up against.
74
- //
75
- // ours-fleet resolves its daemon from OURS_CONFIG / OURS_PORT / OURS_STATE_DIR
76
- // and otherwise falls back to ~/.ours and port 3050 (src/monitor.ts). A host
77
- // set up against a non-default daemon — the installer's multi-daemon profiles
78
- // do exactly this — passes that selection to `ours-fleet init` in the
79
- // environment, and it died there: nothing persisted it, so every runner
80
- // systemd started at boot resolved the default daemon again, and on a host
81
- // where only the non-default daemon exists that is a daemon that is not there.
82
- //
83
- // ONLY OURS_CONFIG is baked, deliberately. It names which daemon, and leaves
84
- // that daemon's own config file authoritative for port and state directory, so
85
- // a later edit to it still wins. Baking OURS_PORT/OURS_STATE_DIR would freeze
86
- // those into a unit file that outranks the config for ever after — the failure
87
- // mode @ours.network/cli avoids for the same reason (packages/cli/src/
88
- // service.ts: "The port is deliberately NOT baked").
89
- //
90
- // Absent from init's environment ⇒ no line, and the unit is byte-for-byte what
91
- // it has always been. This teaches fleet nothing about installer profiles; it
92
- // persists the selection fleet was initialised with.
93
- const unitEnv = ['PATH=' + servicePath];
94
- if (process.env.OURS_CONFIG)
95
- unitEnv.push('OURS_CONFIG=' + process.env.OURS_CONFIG);
96
- const environmentLines = unitEnv
97
- .map(value => `Environment="${value.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/%/g, '%%')}"`)
98
- .join('\n');
99
72
  mkdirSync(unitDir, { recursive: true });
100
73
  writeFileSync(join(unitDir, UNIT_TEMPLATE), `[Unit]
101
74
  Description=ours-fleet agent %i
@@ -103,7 +76,7 @@ After=default.target
103
76
 
104
77
  [Service]
105
78
  Type=simple
106
- ${environmentLines}
79
+ Environment="PATH=${servicePath.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/%/g, '%%')}"
107
80
  ExecStart=${unitArg(process.execPath)} ${unitArg(binPath)} _run %i
108
81
  # The RUNNER owns the child-session restart loop, with a counted, backed-off
109
82
  # circuit breaker (3.2). systemd must only recover the runner PROCESS crashing —
@@ -80,6 +80,13 @@ export function generateWatchdogBriefing(opts) {
80
80
  L.push('- `healthy` — alive, on-briefing, recent progress.');
81
81
  L.push('- `idle` — alive, nothing assigned or nothing to do. Not an anomaly.');
82
82
  L.push('- `stale` = no worklog append and no console progress for ≥ 3 intervals.');
83
+ L.push('');
84
+ L.push('`session.readiness` from `ours-fleet status` is TURN OCCUPANCY, not activity: a mail');
85
+ L.push('wake delivered by ACP steering runs an entire turn while readiness stays `idle`. Never');
86
+ L.push('report `idle` or `stale` from `readiness=idle` alone — corroborate with the');
87
+ L.push('`activity:` line of the same `status` output (`active` means the agent is working),');
88
+ L.push('the worklog, or `ours-fleet peek`. `activity: unobservable` is missing evidence, not');
89
+ L.push('an idle agent.');
83
90
  L.push('- `blocked` = waiting on a permission/prompt/modal longer than one interval.');
84
91
  L.push('- `off_briefing` — activity contradicts the briefing (wrong repo, out-of-scope work,');
85
92
  L.push(' ignored routine).');
@@ -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