@ours.network/fleet 0.16.0 → 0.17.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.
- package/README.md +8 -1
- package/dist/application/role-creation-service.d.ts +2 -2
- package/dist/config.d.ts +4 -2
- package/dist/config.js +3 -2
- package/dist/docs.d.ts +1 -1
- package/dist/docs.js +9 -4
- package/dist/fleet-proxy.js +2 -2
- package/dist/harness/codex.js +43 -6
- package/dist/harness/types.d.ts +2 -0
- package/dist/monitor.d.ts +4 -3
- package/dist/monitor.js +11 -2
- package/dist/owner-channel/channel.js +4 -1
- package/dist/permissions.d.ts +2 -0
- package/dist/permissions.js +5 -0
- package/dist/runner.js +20 -5
- package/dist/session/acp.d.ts +23 -0
- package/dist/session/acp.js +298 -17
- package/dist/session/arbiter.d.ts +5 -0
- package/dist/session/arbiter.js +8 -0
- package/dist/session/conversation-normalizer.d.ts +6 -0
- package/dist/session/conversation-normalizer.js +4 -3
- package/dist/session/conversation-types.d.ts +9 -2
- package/dist/session/types.d.ts +13 -1
- package/dist/web-app/assets/{TerminalView-Mxxypj9w.js → TerminalView-B3rnVWbo.js} +1 -1
- package/dist/web-app/assets/{index-CsHEL0f6.js → index-CliHATFt.js} +4 -4
- package/dist/web-app/index.html +1 -1
- package/package.json +1 -1
package/dist/session/acp.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
2
|
import { randomUUID } from 'node:crypto';
|
|
3
|
-
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
4
|
-
import { isAbsolute, join, relative, resolve } from 'node:path';
|
|
3
|
+
import { existsSync, lstatSync, readFileSync, readlinkSync, realpathSync, writeFileSync, } from 'node:fs';
|
|
4
|
+
import { basename, dirname, isAbsolute, join, relative, resolve } from 'node:path';
|
|
5
5
|
import { Readable, Writable } from 'node:stream';
|
|
6
6
|
import * as acp from '@agentclientprotocol/sdk';
|
|
7
7
|
import { normalizeSessionUpdate } from './conversation-normalizer.js';
|
|
@@ -13,9 +13,124 @@ const CANCEL_SETTLE_GRACE_MS = 15_000;
|
|
|
13
13
|
const PERMISSION_TIMEOUT_MS = 10 * 60_000;
|
|
14
14
|
/** Spec §4.3: 10-15 s before a vanished controller triggers the unattended policy. */
|
|
15
15
|
const CONTROLLER_GRACE_MS = 12_000;
|
|
16
|
+
/** Bound safe-boundary waiting without turning a hung tool into cancellation. */
|
|
17
|
+
export const AFTER_TOOL_BOUNDARY_TIMEOUT_MS = 120_000;
|
|
18
|
+
const TERMINAL_TOOL_STATUSES = new Set(['completed', 'failed']);
|
|
16
19
|
const SCHEDULED_LOOP_REDACTION = '[scheduled-loop content redacted]';
|
|
17
20
|
const OWNER_COMMENTARY_REDACTION = '[assistant commentary redacted]';
|
|
21
|
+
const MAX_CANONICAL_SYMLINK_DEPTH = 40;
|
|
18
22
|
const scheduledTurn = (turn) => turn?.origin?.kind === 'scheduled-loop';
|
|
23
|
+
/**
|
|
24
|
+
* Two matching realpath observations narrow the opportunity for a concurrent
|
|
25
|
+
* retarget, but are only an advisory consistency check: they do not lock the
|
|
26
|
+
* path. A mutation after the completed check remains an unavoidable TOCTOU
|
|
27
|
+
* window until ACP offers handle-based access.
|
|
28
|
+
*/
|
|
29
|
+
function stableRealpath(path) {
|
|
30
|
+
const first = realpathSync.native(path);
|
|
31
|
+
const second = realpathSync.native(path);
|
|
32
|
+
return first === second ? first : undefined;
|
|
33
|
+
}
|
|
34
|
+
/** Distinguish an absent component from a dangling symlink at that component. */
|
|
35
|
+
function inspectMissingPath(path) {
|
|
36
|
+
try {
|
|
37
|
+
const stat = lstatSync(path);
|
|
38
|
+
// realpath said ENOENT but lstat found a non-link: the path changed while
|
|
39
|
+
// inspected, so there is no coherent canonical answer to trust.
|
|
40
|
+
if (!stat.isSymbolicLink())
|
|
41
|
+
return { kind: 'unsafe' };
|
|
42
|
+
try {
|
|
43
|
+
return { kind: 'symlink', target: resolve(dirname(path), readlinkSync(path)) };
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return { kind: 'unsafe' };
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
return error.code === 'ENOENT'
|
|
51
|
+
? { kind: 'absent' }
|
|
52
|
+
: { kind: 'unsafe' };
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Canonicalize an existing path, or a not-yet-created target through its
|
|
57
|
+
* nearest existing ancestor. Dangling links are followed explicitly because
|
|
58
|
+
* realpath reports their absent target as ENOENT. Only genuine absence is
|
|
59
|
+
* recoverable; loops, permissions, races, and every other error fail closed.
|
|
60
|
+
*/
|
|
61
|
+
function canonicalTarget(path, symlinkDepth = 0) {
|
|
62
|
+
let probe = resolve(path);
|
|
63
|
+
const missing = [];
|
|
64
|
+
while (true) {
|
|
65
|
+
try {
|
|
66
|
+
let canonical = stableRealpath(probe);
|
|
67
|
+
if (!canonical)
|
|
68
|
+
return undefined;
|
|
69
|
+
// A component may have appeared while the ancestor search was in
|
|
70
|
+
// progress. Re-walk the suffix so a newly-created symlink is resolved,
|
|
71
|
+
// not treated as a lexical child of the old ancestor.
|
|
72
|
+
let logical = probe;
|
|
73
|
+
for (let i = 0; i < missing.length; i++) {
|
|
74
|
+
logical = resolve(logical, missing[i]);
|
|
75
|
+
try {
|
|
76
|
+
const appeared = stableRealpath(logical);
|
|
77
|
+
if (!appeared)
|
|
78
|
+
return undefined;
|
|
79
|
+
canonical = appeared;
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
if (error.code !== 'ENOENT')
|
|
83
|
+
return undefined;
|
|
84
|
+
const inspected = inspectMissingPath(logical);
|
|
85
|
+
if (inspected.kind === 'unsafe')
|
|
86
|
+
return undefined;
|
|
87
|
+
if (inspected.kind === 'symlink') {
|
|
88
|
+
if (symlinkDepth >= MAX_CANONICAL_SYMLINK_DEPTH)
|
|
89
|
+
return undefined;
|
|
90
|
+
return canonicalTarget(resolve(inspected.target, ...missing.slice(i + 1)), symlinkDepth + 1);
|
|
91
|
+
}
|
|
92
|
+
return resolve(canonical, ...missing.slice(i));
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return canonical;
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
if (error.code !== 'ENOENT')
|
|
99
|
+
return undefined;
|
|
100
|
+
const inspected = inspectMissingPath(probe);
|
|
101
|
+
if (inspected.kind === 'unsafe')
|
|
102
|
+
return undefined;
|
|
103
|
+
if (inspected.kind === 'symlink') {
|
|
104
|
+
if (symlinkDepth >= MAX_CANONICAL_SYMLINK_DEPTH)
|
|
105
|
+
return undefined;
|
|
106
|
+
return canonicalTarget(resolve(inspected.target, ...missing), symlinkDepth + 1);
|
|
107
|
+
}
|
|
108
|
+
const parent = dirname(probe);
|
|
109
|
+
if (parent === probe)
|
|
110
|
+
return undefined;
|
|
111
|
+
missing.unshift(basename(probe));
|
|
112
|
+
probe = parent;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
function canonicallyWithin(root, candidates) {
|
|
117
|
+
if (candidates.length === 0)
|
|
118
|
+
return false;
|
|
119
|
+
try {
|
|
120
|
+
const firstRoot = realpathSync.native(root);
|
|
121
|
+
const paths = candidates.map(canonicalTarget);
|
|
122
|
+
const secondRoot = realpathSync.native(root);
|
|
123
|
+
if (firstRoot !== secondRoot || paths.some(path => path === undefined))
|
|
124
|
+
return false;
|
|
125
|
+
return paths.every(candidate => {
|
|
126
|
+
const rel = relative(firstRoot, candidate);
|
|
127
|
+
return rel === '' || (!rel.startsWith('..') && !isAbsolute(rel));
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
19
134
|
/**
|
|
20
135
|
* Map typed prompt provenance to the conversation ledger's source vocabulary.
|
|
21
136
|
* Only operator-authored local sources may persist prompt bodies; external
|
|
@@ -95,9 +210,13 @@ export class AcpSession {
|
|
|
95
210
|
runtimeModel;
|
|
96
211
|
reasoningEffort;
|
|
97
212
|
controllerCount = 0;
|
|
213
|
+
closing = false;
|
|
98
214
|
/** Armed when the last controller detaches; unattended policy applies on fire. */
|
|
99
215
|
controllerGrace;
|
|
100
216
|
cancelEscalation;
|
|
217
|
+
/** ACP-authenticated in-flight calls, including independently reserved permissions. */
|
|
218
|
+
activeToolCalls = new Map();
|
|
219
|
+
toolBoundaryWaiters = new Set();
|
|
101
220
|
activeTurn;
|
|
102
221
|
constructor(options, child, connection) {
|
|
103
222
|
this.options = options;
|
|
@@ -215,6 +334,142 @@ export class AcpSession {
|
|
|
215
334
|
permissionMode: this.options.permissionMode,
|
|
216
335
|
};
|
|
217
336
|
}
|
|
337
|
+
toolCall(toolCallId) {
|
|
338
|
+
const existing = this.activeToolCalls.get(toolCallId);
|
|
339
|
+
if (existing)
|
|
340
|
+
return existing;
|
|
341
|
+
const created = { lifecycle: false, permissions: new Map() };
|
|
342
|
+
this.activeToolCalls.set(toolCallId, created);
|
|
343
|
+
return created;
|
|
344
|
+
}
|
|
345
|
+
reserveTool(toolCallId) {
|
|
346
|
+
if (toolCallId)
|
|
347
|
+
this.toolCall(toolCallId).lifecycle = true;
|
|
348
|
+
}
|
|
349
|
+
reservePermission(toolCallId, permissionId) {
|
|
350
|
+
if (toolCallId)
|
|
351
|
+
this.toolCall(toolCallId).permissions.set(permissionId, 'pending');
|
|
352
|
+
}
|
|
353
|
+
allowPermission(toolCallId, permissionId) {
|
|
354
|
+
if (!toolCallId)
|
|
355
|
+
return;
|
|
356
|
+
const permission = this.activeToolCalls.get(toolCallId)?.permissions;
|
|
357
|
+
if (permission?.has(permissionId))
|
|
358
|
+
permission.set(permissionId, 'allowed');
|
|
359
|
+
}
|
|
360
|
+
releasePermission(toolCallId, permissionId) {
|
|
361
|
+
const call = toolCallId && this.activeToolCalls.get(toolCallId);
|
|
362
|
+
if (!call || !call.permissions.delete(permissionId))
|
|
363
|
+
return;
|
|
364
|
+
this.releaseToolIfIdle(toolCallId, call);
|
|
365
|
+
}
|
|
366
|
+
releaseTool(toolCallId) {
|
|
367
|
+
const call = toolCallId && this.activeToolCalls.get(toolCallId);
|
|
368
|
+
if (!call)
|
|
369
|
+
return;
|
|
370
|
+
call.lifecycle = false;
|
|
371
|
+
// Terminal tool evidence consumes permissions already granted for this
|
|
372
|
+
// call, but never a separate request that is still awaiting a decision.
|
|
373
|
+
for (const [permissionId, state] of call.permissions)
|
|
374
|
+
if (state === 'allowed')
|
|
375
|
+
call.permissions.delete(permissionId);
|
|
376
|
+
this.releaseToolIfIdle(toolCallId, call);
|
|
377
|
+
}
|
|
378
|
+
releaseToolIfIdle(toolCallId, call) {
|
|
379
|
+
if (call.lifecycle || call.permissions.size > 0)
|
|
380
|
+
return;
|
|
381
|
+
if (!this.activeToolCalls.delete(toolCallId) || this.activeToolCalls.size > 0)
|
|
382
|
+
return;
|
|
383
|
+
for (const notify of [...this.toolBoundaryWaiters])
|
|
384
|
+
notify();
|
|
385
|
+
}
|
|
386
|
+
releaseAllTools() {
|
|
387
|
+
if (this.activeToolCalls.size === 0)
|
|
388
|
+
return;
|
|
389
|
+
this.activeToolCalls.clear();
|
|
390
|
+
for (const notify of [...this.toolBoundaryWaiters])
|
|
391
|
+
notify();
|
|
392
|
+
}
|
|
393
|
+
waitForToolBoundary(timeoutMs) {
|
|
394
|
+
if (this.activeToolCalls.size === 0)
|
|
395
|
+
return Promise.resolve(true);
|
|
396
|
+
return new Promise(resolve => {
|
|
397
|
+
let settled = false;
|
|
398
|
+
const finish = (atBoundary) => {
|
|
399
|
+
if (settled)
|
|
400
|
+
return;
|
|
401
|
+
settled = true;
|
|
402
|
+
clearTimeout(timer);
|
|
403
|
+
this.toolBoundaryWaiters.delete(check);
|
|
404
|
+
resolve(atBoundary);
|
|
405
|
+
};
|
|
406
|
+
const check = () => {
|
|
407
|
+
if (this.activeToolCalls.size === 0 || !this.isAlive())
|
|
408
|
+
finish(this.activeToolCalls.size === 0);
|
|
409
|
+
};
|
|
410
|
+
const timer = setTimeout(() => finish(false), Math.max(0, timeoutMs));
|
|
411
|
+
timer.unref?.();
|
|
412
|
+
this.toolBoundaryWaiters.add(check);
|
|
413
|
+
// Close the subscribe/check race without guessing about elapsed time.
|
|
414
|
+
check();
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
recordAfterToolDelivery(state, activeToolCount, waitedMs) {
|
|
418
|
+
this.events.emit('monitor_delivery', {
|
|
419
|
+
status: state, monitorPolicy: 'after_tool', activeToolCount, waitedMs,
|
|
420
|
+
});
|
|
421
|
+
this.conversation.appendSafe({
|
|
422
|
+
kind: 'monitor.delivery', sessionGeneration: this.sessionGeneration,
|
|
423
|
+
acpSessionId: this.sessionId, source: 'fleet_monitor',
|
|
424
|
+
payload: { policy: 'after_tool', state, activeToolCount, waitedMs },
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Monitor-only safe-boundary delivery. Steering is the interruption: this
|
|
429
|
+
* path never calls session/cancel and never resolves a pending permission.
|
|
430
|
+
*/
|
|
431
|
+
async submitPromptAfterTool(text, options = {}) {
|
|
432
|
+
if (this.closing || !this.isAlive())
|
|
433
|
+
return turnResult(false, 'failed', this.lastError ?? 'ACP session is closing');
|
|
434
|
+
const startedAt = Date.now();
|
|
435
|
+
const initialToolCount = this.activeToolCalls.size;
|
|
436
|
+
if (!this.steeringSupported) {
|
|
437
|
+
this.recordAfterToolDelivery('unsupported', initialToolCount, 0);
|
|
438
|
+
const result = await this.submitPrompt(text, { ...options, interrupt: false, steer: false });
|
|
439
|
+
return {
|
|
440
|
+
...result,
|
|
441
|
+
safeBoundary: { state: 'unsupported', waitedMs: 0, activeToolCount: initialToolCount },
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
if (initialToolCount === 0) {
|
|
445
|
+
this.recordAfterToolDelivery('direct', 0, 0);
|
|
446
|
+
const result = await this.steerPrompt(text);
|
|
447
|
+
return { ...result, safeBoundary: { state: 'direct', waitedMs: 0, activeToolCount: 0 } };
|
|
448
|
+
}
|
|
449
|
+
this.recordAfterToolDelivery('deferred', initialToolCount, 0);
|
|
450
|
+
const timeoutMs = this.options.afterToolBoundaryTimeoutMs ?? AFTER_TOOL_BOUNDARY_TIMEOUT_MS;
|
|
451
|
+
const deadline = startedAt + timeoutMs;
|
|
452
|
+
let atBoundary = false;
|
|
453
|
+
// Re-check after every wake: another authenticated tool event may have
|
|
454
|
+
// arrived before this continuation ran. Only an empty tracked set is safe.
|
|
455
|
+
while (this.activeToolCalls.size > 0) {
|
|
456
|
+
const remaining = deadline - Date.now();
|
|
457
|
+
if (remaining <= 0 || !(await this.waitForToolBoundary(remaining)))
|
|
458
|
+
break;
|
|
459
|
+
}
|
|
460
|
+
atBoundary = this.activeToolCalls.size === 0;
|
|
461
|
+
if (this.closing || !this.isAlive())
|
|
462
|
+
return turnResult(false, 'failed', this.lastError ?? 'ACP session closed during after_tool wait');
|
|
463
|
+
const waitedMs = Math.max(0, Date.now() - startedAt);
|
|
464
|
+
const state = atBoundary ? 'after_tool' : 'timeout';
|
|
465
|
+
const remainingToolCount = this.activeToolCalls.size;
|
|
466
|
+
this.recordAfterToolDelivery(state, remainingToolCount, waitedMs);
|
|
467
|
+
const result = await this.steerPrompt(text);
|
|
468
|
+
return {
|
|
469
|
+
...result,
|
|
470
|
+
safeBoundary: { state, waitedMs, activeToolCount: remainingToolCount },
|
|
471
|
+
};
|
|
472
|
+
}
|
|
218
473
|
/**
|
|
219
474
|
* Accept responsibility for a prompt, then return. The turn itself may run
|
|
220
475
|
* for minutes behind other queued turns; making an interactive caller wait
|
|
@@ -365,6 +620,10 @@ export class AcpSession {
|
|
|
365
620
|
clearTimeout(pending.expiry);
|
|
366
621
|
pending.resolve({ outcome: { outcome: 'selected', optionId } });
|
|
367
622
|
const decision = chosen.kind.startsWith('reject') ? 'denied' : 'allowed';
|
|
623
|
+
if (decision === 'allowed')
|
|
624
|
+
this.allowPermission(pending.toolCallId, permissionId);
|
|
625
|
+
else
|
|
626
|
+
this.releasePermission(pending.toolCallId, permissionId);
|
|
368
627
|
this.events.emit('permission', {
|
|
369
628
|
turnId: this.activeTurn?.id,
|
|
370
629
|
origin: this.activeTurn?.origin,
|
|
@@ -381,7 +640,7 @@ export class AcpSession {
|
|
|
381
640
|
promptId: this.activeTurn?.id, turnId: this.activeTurn?.id,
|
|
382
641
|
payload: { decision, decisionSource: 'manual', optionId },
|
|
383
642
|
});
|
|
384
|
-
this.readiness = 'running';
|
|
643
|
+
this.readiness = this.pendingPermissions.size > 0 ? 'awaiting_permission' : 'running';
|
|
385
644
|
return true;
|
|
386
645
|
}
|
|
387
646
|
/**
|
|
@@ -446,11 +705,12 @@ export class AcpSession {
|
|
|
446
705
|
? { outcome: { outcome: 'selected', optionId: rejectOption.optionId } }
|
|
447
706
|
: { outcome: { outcome: 'cancelled' } });
|
|
448
707
|
const settled = decision === 'denied' && !rejectOption ? 'cancelled' : decision;
|
|
708
|
+
this.releasePermission(pending.toolCallId, permissionId);
|
|
449
709
|
this.events.emit('permission', {
|
|
450
710
|
turnId: this.activeTurn?.id,
|
|
451
711
|
origin: this.activeTurn?.origin,
|
|
452
712
|
permissionId,
|
|
453
|
-
toolCallId: pending.
|
|
713
|
+
toolCallId: pending.eventToolCallId,
|
|
454
714
|
status: 'completed',
|
|
455
715
|
decision: settled === 'expired' ? 'cancelled' : settled,
|
|
456
716
|
decisionSource: 'automatic',
|
|
@@ -462,7 +722,7 @@ export class AcpSession {
|
|
|
462
722
|
kind: 'permission.resolved', sessionGeneration: this.sessionGeneration,
|
|
463
723
|
acpSessionId: this.sessionId, permissionId,
|
|
464
724
|
promptId: this.activeTurn?.id, turnId: this.activeTurn?.id,
|
|
465
|
-
toolCallId: pending.
|
|
725
|
+
toolCallId: pending.eventToolCallId,
|
|
466
726
|
payload: {
|
|
467
727
|
decision: settled, decisionSource: 'automatic',
|
|
468
728
|
...(policy ? { policy } : {}), reason,
|
|
@@ -476,6 +736,7 @@ export class AcpSession {
|
|
|
476
736
|
return this.exit;
|
|
477
737
|
}
|
|
478
738
|
async close() {
|
|
739
|
+
this.closing = true;
|
|
479
740
|
if (this.cancelEscalation)
|
|
480
741
|
clearTimeout(this.cancelEscalation);
|
|
481
742
|
this.cancelEscalation = undefined;
|
|
@@ -484,6 +745,7 @@ export class AcpSession {
|
|
|
484
745
|
this.controllerGrace = undefined;
|
|
485
746
|
for (const [permissionId, pending] of [...this.pendingPermissions])
|
|
486
747
|
this.settlePendingAutomatically(permissionId, pending, 'cancelled', undefined, 'the session closed while this request was pending');
|
|
748
|
+
this.releaseAllTools();
|
|
487
749
|
if (this.sessionId && this.capabilities?.sessionCapabilities?.close != null) {
|
|
488
750
|
await this.connection.agent.request(acp.methods.agent.session.close, { sessionId: this.sessionId }).catch(() => undefined);
|
|
489
751
|
}
|
|
@@ -627,6 +889,7 @@ export class AcpSession {
|
|
|
627
889
|
return turnResult(false, 'failed', this.lastError, this.activeTurn?.id === turnId ? this.activeTurn.output : undefined);
|
|
628
890
|
}
|
|
629
891
|
finally {
|
|
892
|
+
this.releaseAllTools();
|
|
630
893
|
if (this.activeTurn?.id === turnId) {
|
|
631
894
|
if (this.cancelEscalation)
|
|
632
895
|
clearTimeout(this.cancelEscalation);
|
|
@@ -667,9 +930,19 @@ export class AcpSession {
|
|
|
667
930
|
}
|
|
668
931
|
return undefined;
|
|
669
932
|
};
|
|
933
|
+
const toolCallId = params.toolCall.toolCallId;
|
|
934
|
+
const permissionId = randomUUID();
|
|
935
|
+
// Permission is part of the tool lifecycle. Reserve before any policy or
|
|
936
|
+
// human decision so a monitor wake cannot slip between request and answer.
|
|
937
|
+
this.reservePermission(toolCallId, permissionId);
|
|
670
938
|
if (this.options.permissions.approval === 'allow' && this.withinAutomaticBoundary(params)) {
|
|
671
939
|
const option = choose(['allow_always', 'allow_once']);
|
|
672
|
-
|
|
940
|
+
const response = this.settleAutomatically(params, option, 'allowed', 'permissions.approval=allow', `the request is inside the ${this.options.permissions.filesystem} boundary`);
|
|
941
|
+
if (option)
|
|
942
|
+
this.allowPermission(toolCallId, permissionId);
|
|
943
|
+
else
|
|
944
|
+
this.releasePermission(toolCallId, permissionId);
|
|
945
|
+
return Promise.resolve(response);
|
|
673
946
|
}
|
|
674
947
|
// A live grace window still counts as attended: the controller may be
|
|
675
948
|
// mid-reconnect, and denying instantly is exactly what the grace prevents.
|
|
@@ -680,11 +953,12 @@ export class AcpSession {
|
|
|
680
953
|
// a decision no human made, so one unattended denial would silently disable
|
|
681
954
|
// the tool for the rest of the session.
|
|
682
955
|
const option = choose(['reject_once', 'reject_always']);
|
|
683
|
-
|
|
956
|
+
const response = this.settleAutomatically(params, option, 'denied', unattended ? 'permissions.unattended=deny' : 'permissions.approval=deny', unattended
|
|
684
957
|
? 'no controller is attached, so the request cannot be shown to anyone'
|
|
685
|
-
: 'the role denies every permission request by policy')
|
|
958
|
+
: 'the role denies every permission request by policy');
|
|
959
|
+
this.releasePermission(toolCallId, permissionId);
|
|
960
|
+
return Promise.resolve(response);
|
|
686
961
|
}
|
|
687
|
-
const permissionId = randomUUID();
|
|
688
962
|
const timeoutMs = this.options.permissionTimeoutMs ?? PERMISSION_TIMEOUT_MS;
|
|
689
963
|
const expiresAt = new Date(Date.now() + timeoutMs).toISOString();
|
|
690
964
|
this.readiness = 'awaiting_permission';
|
|
@@ -723,8 +997,8 @@ export class AcpSession {
|
|
|
723
997
|
return new Promise(resolve => {
|
|
724
998
|
const pending = {
|
|
725
999
|
options: params.options, resolve,
|
|
726
|
-
toolCallId
|
|
727
|
-
|
|
1000
|
+
toolCallId,
|
|
1001
|
+
eventToolCallId: scheduledTurn(this.activeTurn) ? 'scheduled-loop-tool' : toolCallId,
|
|
728
1002
|
};
|
|
729
1003
|
pending.expiry = setTimeout(() => {
|
|
730
1004
|
this.settlePendingAutomatically(permissionId, pending, 'expired', undefined, `no decision arrived within ${Math.round(timeoutMs / 1000)}s`);
|
|
@@ -785,11 +1059,7 @@ export class AcpSession {
|
|
|
785
1059
|
if (locations.length === 0)
|
|
786
1060
|
return false;
|
|
787
1061
|
const cwd = resolve(this.options.cwd);
|
|
788
|
-
return locations.
|
|
789
|
-
const path = resolve(location.path);
|
|
790
|
-
const rel = relative(cwd, path);
|
|
791
|
-
return rel === '' || (!rel.startsWith('..') && !isAbsolute(rel));
|
|
792
|
-
});
|
|
1062
|
+
return canonicallyWithin(cwd, locations.map(location => resolve(location.path)));
|
|
793
1063
|
}
|
|
794
1064
|
recordUpdate(update) {
|
|
795
1065
|
const scheduled = this.activeTurn?.origin?.kind === 'scheduled-loop';
|
|
@@ -832,6 +1102,10 @@ export class AcpSession {
|
|
|
832
1102
|
title: scheduled ? 'scheduled-loop tool' : update.title,
|
|
833
1103
|
status: update.status,
|
|
834
1104
|
});
|
|
1105
|
+
if (TERMINAL_TOOL_STATUSES.has(update.status ?? ''))
|
|
1106
|
+
this.releaseTool(update.toolCallId);
|
|
1107
|
+
else
|
|
1108
|
+
this.reserveTool(update.toolCallId);
|
|
835
1109
|
break;
|
|
836
1110
|
case 'tool_call_update':
|
|
837
1111
|
this.events.emit('tool_update', {
|
|
@@ -841,6 +1115,10 @@ export class AcpSession {
|
|
|
841
1115
|
title: scheduled ? 'scheduled-loop tool' : update.title ?? undefined,
|
|
842
1116
|
status: update.status ?? undefined,
|
|
843
1117
|
});
|
|
1118
|
+
if (TERMINAL_TOOL_STATUSES.has(update.status ?? ''))
|
|
1119
|
+
this.releaseTool(update.toolCallId);
|
|
1120
|
+
else if (update.status !== undefined)
|
|
1121
|
+
this.reserveTool(update.toolCallId);
|
|
844
1122
|
break;
|
|
845
1123
|
default:
|
|
846
1124
|
break;
|
|
@@ -864,7 +1142,10 @@ export class AcpSession {
|
|
|
864
1142
|
}
|
|
865
1143
|
/** Normalize every ACP update losslessly into the durable ledger. */
|
|
866
1144
|
recordConversationUpdate(update, scheduled, commentary = false) {
|
|
867
|
-
const normalized = normalizeSessionUpdate(update, scheduled ? {
|
|
1145
|
+
const normalized = normalizeSessionUpdate(update, scheduled ? {
|
|
1146
|
+
redactText: SCHEDULED_LOOP_REDACTION,
|
|
1147
|
+
redactToolCallId: 'scheduled-loop-tool',
|
|
1148
|
+
}
|
|
868
1149
|
: commentary ? { redactText: OWNER_COMMENTARY_REDACTION } : {});
|
|
869
1150
|
this.conversation.appendSafe({
|
|
870
1151
|
kind: normalized.kind,
|
|
@@ -27,6 +27,11 @@ export declare class RoleTurnArbiter implements SessionHandle {
|
|
|
27
27
|
private track;
|
|
28
28
|
queuePrompt(text: string, options?: SubmitPromptOptions): Promise<QueuedPrompt>;
|
|
29
29
|
submitPrompt(text: string, options?: SubmitPromptOptions): Promise<TurnResult>;
|
|
30
|
+
/**
|
|
31
|
+
* Do not hold `exclusive` while ACP waits for a tool boundary: permission
|
|
32
|
+
* answers and explicit interrupts must remain able to pass immediately.
|
|
33
|
+
*/
|
|
34
|
+
submitPromptAfterTool(text: string, options?: SubmitPromptOptions): Promise<TurnResult>;
|
|
30
35
|
tryScheduled(text: string, origin: Extract<PromptOrigin, {
|
|
31
36
|
kind: 'scheduled-loop';
|
|
32
37
|
}>, beforeQueue?: () => void | Promise<void>): Promise<ScheduledAttempt>;
|
package/dist/session/arbiter.js
CHANGED
|
@@ -32,6 +32,14 @@ export class RoleTurnArbiter {
|
|
|
32
32
|
async submitPrompt(text, options = {}) {
|
|
33
33
|
return (await this.queuePrompt(text, options)).completion;
|
|
34
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* Do not hold `exclusive` while ACP waits for a tool boundary: permission
|
|
37
|
+
* answers and explicit interrupts must remain able to pass immediately.
|
|
38
|
+
*/
|
|
39
|
+
submitPromptAfterTool(text, options = {}) {
|
|
40
|
+
return this.session.submitPromptAfterTool?.(text, options)
|
|
41
|
+
?? this.session.submitPrompt(text, { ...options, interrupt: false, steer: false });
|
|
42
|
+
}
|
|
35
43
|
async tryScheduled(text, origin, beforeQueue) {
|
|
36
44
|
// Give owner/console/I/O callbacks already ready in this event-loop turn a
|
|
37
45
|
// chance to claim the arbiter first. Scheduled work is best-effort; humans
|
|
@@ -23,6 +23,12 @@ export interface NormalizeOptions {
|
|
|
23
23
|
* digest. Used for scheduled-loop turns whose output must not be retained.
|
|
24
24
|
*/
|
|
25
25
|
redactText?: string;
|
|
26
|
+
/**
|
|
27
|
+
* Replace ACP tool-call IDs in both the normalized event correlation field
|
|
28
|
+
* and payload. The caller may still use the original update for in-memory
|
|
29
|
+
* lifecycle tracking without persisting its private identifier.
|
|
30
|
+
*/
|
|
31
|
+
redactToolCallId?: string;
|
|
26
32
|
}
|
|
27
33
|
export interface NormalizedUpdate {
|
|
28
34
|
kind: ConversationEventKind;
|
|
@@ -203,9 +203,10 @@ function normalizeToolContent(raw, redact) {
|
|
|
203
203
|
}
|
|
204
204
|
});
|
|
205
205
|
}
|
|
206
|
-
function toolUpsert(update, snapshot,
|
|
206
|
+
function toolUpsert(update, snapshot, options) {
|
|
207
|
+
const redact = options.redactText;
|
|
207
208
|
const payload = {
|
|
208
|
-
toolCallId: asString(update.toolCallId) ?? '',
|
|
209
|
+
toolCallId: options.redactToolCallId ?? asString(update.toolCallId) ?? '',
|
|
209
210
|
snapshot,
|
|
210
211
|
};
|
|
211
212
|
if (asString(update.title) !== undefined)
|
|
@@ -271,7 +272,7 @@ export function normalizeSessionUpdate(update, options = {}) {
|
|
|
271
272
|
}
|
|
272
273
|
case 'tool_call':
|
|
273
274
|
case 'tool_call_update': {
|
|
274
|
-
const payload = toolUpsert(raw, raw.sessionUpdate === 'tool_call',
|
|
275
|
+
const payload = toolUpsert(raw, raw.sessionUpdate === 'tool_call', options);
|
|
275
276
|
return withMeta({
|
|
276
277
|
kind: 'tool.upsert', payload,
|
|
277
278
|
...(payload.toolCallId ? { toolCallId: payload.toolCallId } : {}),
|
|
@@ -7,7 +7,7 @@ import type { PromptOrigin, TurnCancellationSource, TurnOutcome } from './types.
|
|
|
7
7
|
* file touches the wire: ACP updates are reduced into these shapes by the
|
|
8
8
|
* normalizer, and the store (phase 1) assigns `seq`/`eventId`/timestamps.
|
|
9
9
|
*/
|
|
10
|
-
export type ConversationEventKind = 'prompt.admitted' | 'prompt.started' | 'prompt.interrupt_requested' | 'message.chunk' | 'message.replace' | 'thought.chunk' | 'thought.replace' | 'plan.replace' | 'tool.upsert' | 'tool.content_chunk' | 'permission.requested' | 'permission.resolved' | 'usage.updated' | 'turn.state' | 'turn.completed' | 'session.state' | 'session.info' | 'capabilities.updated' | 'error'
|
|
10
|
+
export type ConversationEventKind = 'prompt.admitted' | 'prompt.started' | 'prompt.interrupt_requested' | 'message.chunk' | 'message.replace' | 'thought.chunk' | 'thought.replace' | 'plan.replace' | 'tool.upsert' | 'tool.content_chunk' | 'permission.requested' | 'permission.resolved' | 'monitor.delivery' | 'usage.updated' | 'turn.state' | 'turn.completed' | 'session.state' | 'session.info' | 'capabilities.updated' | 'error'
|
|
11
11
|
/** A well-formed ACP update this version cannot represent. Bounded, never a crash. */
|
|
12
12
|
| 'unsupported';
|
|
13
13
|
/** Where a conversation record came from. Typed provenance, never prompt text. */
|
|
@@ -178,6 +178,13 @@ export interface PermissionResolvedPayload {
|
|
|
178
178
|
policy?: string;
|
|
179
179
|
reason?: string;
|
|
180
180
|
}
|
|
181
|
+
/** Body-free monitor evidence. Tool ids, titles, output, and wake text are never stored. */
|
|
182
|
+
export interface MonitorDeliveryPayload {
|
|
183
|
+
policy: 'after_tool';
|
|
184
|
+
state: 'deferred' | 'direct' | 'after_tool' | 'timeout' | 'unsupported';
|
|
185
|
+
activeToolCount: number;
|
|
186
|
+
waitedMs: number;
|
|
187
|
+
}
|
|
181
188
|
export interface TurnStatePayload {
|
|
182
189
|
state: 'queued' | 'running' | 'awaiting_permission' | 'interrupt_requested';
|
|
183
190
|
}
|
|
@@ -201,7 +208,7 @@ export interface BoundedJson {
|
|
|
201
208
|
digest?: string;
|
|
202
209
|
redacted?: true;
|
|
203
210
|
}
|
|
204
|
-
export type ConversationPayload = MessageChunkPayload | ThoughtChunkPayload | PlanReplacePayload | ToolUpsertPayload | UsageUpdatedPayload | SessionStatePayload | SessionInfoPayload | CapabilitiesUpdatedPayload | UnsupportedPayload | PromptAdmittedPayload | PromptStartedPayload | PromptInterruptRequestedPayload | PermissionRequestedPayload | PermissionResolvedPayload | TurnStatePayload | TurnCompletedPayload | SessionLifecyclePayload | ErrorPayload;
|
|
211
|
+
export type ConversationPayload = MessageChunkPayload | ThoughtChunkPayload | PlanReplacePayload | ToolUpsertPayload | UsageUpdatedPayload | SessionStatePayload | SessionInfoPayload | CapabilitiesUpdatedPayload | UnsupportedPayload | PromptAdmittedPayload | PromptStartedPayload | PromptInterruptRequestedPayload | PermissionRequestedPayload | PermissionResolvedPayload | TurnStatePayload | TurnCompletedPayload | MonitorDeliveryPayload | SessionLifecyclePayload | ErrorPayload;
|
|
205
212
|
export interface ConversationEventV1 {
|
|
206
213
|
schemaVersion: 1;
|
|
207
214
|
roleId: string;
|
package/dist/session/types.d.ts
CHANGED
|
@@ -49,6 +49,12 @@ export interface TurnResult {
|
|
|
49
49
|
cancellationSource?: TurnCancellationSource;
|
|
50
50
|
/** Final assistant text captured structurally by a backend, when available. */
|
|
51
51
|
output?: string;
|
|
52
|
+
/** Body-free monitor safe-boundary disposition, when this was an after_tool wake. */
|
|
53
|
+
safeBoundary?: {
|
|
54
|
+
state: 'direct' | 'after_tool' | 'timeout' | 'unsupported';
|
|
55
|
+
waitedMs: number;
|
|
56
|
+
activeToolCount: number;
|
|
57
|
+
};
|
|
52
58
|
}
|
|
53
59
|
/**
|
|
54
60
|
* Why a control operation failed. The distinctions exist because collapsing
|
|
@@ -137,7 +143,7 @@ export interface SessionSnapshot {
|
|
|
137
143
|
nativeMode: string;
|
|
138
144
|
};
|
|
139
145
|
}
|
|
140
|
-
export type SessionEventKind = 'state' | 'agent_text' | 'thought' | 'tool_call' | 'tool_update' | 'permission' | 'turn_stop' | 'error';
|
|
146
|
+
export type SessionEventKind = 'state' | 'agent_text' | 'thought' | 'tool_call' | 'tool_update' | 'permission' | 'monitor_delivery' | 'turn_stop' | 'error';
|
|
141
147
|
/** What a settled permission request resolved to. */
|
|
142
148
|
export type PermissionDecision = 'allowed' | 'denied' | 'cancelled';
|
|
143
149
|
export interface SessionEvent {
|
|
@@ -178,6 +184,10 @@ export interface SessionEvent {
|
|
|
178
184
|
reason?: string;
|
|
179
185
|
/** The option actually selected, when one was. */
|
|
180
186
|
optionId?: string;
|
|
187
|
+
/** Body-free evidence for monitor safe-boundary delivery. */
|
|
188
|
+
monitorPolicy?: 'after_tool';
|
|
189
|
+
activeToolCount?: number;
|
|
190
|
+
waitedMs?: number;
|
|
181
191
|
}
|
|
182
192
|
export interface ConversationHandlePage {
|
|
183
193
|
events: ConversationEventV1[];
|
|
@@ -206,6 +216,8 @@ export interface SessionHandle {
|
|
|
206
216
|
queuePrompt(text: string, options?: SubmitPromptOptions): Promise<QueuedPrompt>;
|
|
207
217
|
/** Queue a prompt and wait for its terminal result. */
|
|
208
218
|
submitPrompt(text: string, options?: SubmitPromptOptions): Promise<TurnResult>;
|
|
219
|
+
/** Monitor-only ACP safe-boundary delivery. Never implies human/control cancellation. */
|
|
220
|
+
submitPromptAfterTool?(text: string, options?: SubmitPromptOptions): Promise<TurnResult>;
|
|
209
221
|
interrupt(source?: TurnCancellationSource): Promise<void>;
|
|
210
222
|
respondPermission(permissionId: string, optionId: string): boolean;
|
|
211
223
|
/** Generation-bound browser decision; stale/settled/invalid all fail closed. */
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{r as le,a as Ee,j as re}from"./index-
|
|
1
|
+
import{r as le,a as Ee,j as re}from"./index-CliHATFt.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
|
|