@ours.network/fleet 1.0.5 → 1.1.0-nightly.2
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 +90 -112
- package/dist/application/capabilities.d.ts +0 -1
- package/dist/application/capabilities.js +3 -13
- package/dist/application/fleet-query-service.d.ts +0 -3
- package/dist/application/fleet-query-service.js +1 -24
- package/dist/application/role-creation-service.d.ts +5 -5
- package/dist/application/role-creation-service.js +14 -9
- package/dist/application/role-removal-service.js +6 -3
- package/dist/application/role-repository.d.ts +1 -2
- package/dist/application/role-repository.js +7 -7
- package/dist/application/session-control.d.ts +0 -12
- package/dist/application/session-control.js +0 -42
- package/dist/application/session-mutations.d.ts +5 -5
- package/dist/application/types.d.ts +1 -8
- package/dist/briefing.js +3 -6
- package/dist/build-info.json +5 -5
- package/dist/cli.js +17 -28
- package/dist/config.d.ts +29 -6
- package/dist/config.js +320 -40
- package/dist/creation.d.ts +1 -1
- package/dist/creation.js +2 -2
- package/dist/docs.d.ts +1 -1
- package/dist/docs.js +51 -95
- package/dist/doctor.js +1 -8
- package/dist/fleet-proxy.d.ts +1 -1
- package/dist/generated-agent-source.d.ts +9 -0
- package/dist/generated-agent-source.js +53 -0
- package/dist/harness/acp-session-transport.d.ts +8 -0
- package/dist/harness/acp-session-transport.js +3 -0
- package/dist/harness/agent-session.d.ts +46 -0
- package/dist/harness/agent-session.js +1 -0
- package/dist/harness/claude-code-session.d.ts +22 -0
- package/dist/harness/claude-code-session.js +32 -0
- package/dist/harness/claude-code.d.ts +2 -1
- package/dist/harness/claude-code.js +39 -81
- package/dist/harness/codex-session.d.ts +22 -0
- package/dist/harness/codex-session.js +32 -0
- package/dist/harness/codex.d.ts +2 -1
- package/dist/harness/codex.js +49 -45
- package/dist/harness/registry.js +4 -0
- package/dist/harness/types.d.ts +6 -38
- package/dist/index.d.ts +3 -5
- package/dist/index.js +1 -3
- package/dist/init-guidance.d.ts +1 -0
- package/dist/init-guidance.js +5 -0
- package/dist/isolation/resources.d.ts +3 -7
- package/dist/isolation/resources.js +3 -7
- package/dist/model-env.d.ts +2 -4
- package/dist/model-env.js +4 -7
- package/dist/monitor.d.ts +1 -24
- package/dist/monitor.js +5 -140
- package/dist/ops.js +4 -2
- package/dist/owner-channel/channel.d.ts +2 -2
- package/dist/resolved-plan.d.ts +3 -2
- package/dist/resolved-plan.js +21 -5
- package/dist/runner.d.ts +5 -15
- package/dist/runner.js +26 -102
- package/dist/sensitive-config.d.ts +2 -0
- package/dist/sensitive-config.js +4 -0
- package/dist/session/acp.d.ts +2 -2
- package/dist/session/acp.js +2 -3
- package/dist/session/activity.d.ts +2 -3
- package/dist/session/activity.js +2 -3
- package/dist/session/arbiter.d.ts +4 -4
- package/dist/session/control.d.ts +3 -3
- package/dist/session/types.d.ts +11 -5
- package/dist/spawn.d.ts +4 -4
- package/dist/spawn.js +60 -32
- package/dist/supervisor/none.d.ts +1 -1
- package/dist/supervisor/none.js +2 -2
- package/dist/tmux.d.ts +1 -14
- package/dist/tmux.js +1 -51
- package/dist/watchdog/config.js +6 -4
- package/dist/watchdog/run.js +8 -23
- package/dist/web/auth.d.ts +1 -1
- package/dist/web/fleet-config-service.d.ts +11 -8
- package/dist/web/fleet-config-service.js +383 -183
- package/dist/web/runtime.js +4 -15
- package/dist/web/server.d.ts +1 -3
- package/dist/web/server.js +3 -14
- package/dist/web/topology-promote.js +18 -18
- package/dist/web/topology.js +2 -2
- package/dist/web/yaml-document-edit.js +2 -0
- package/dist/web-app/assets/index-59GF-gsZ.css +1 -0
- package/dist/web-app/assets/index-BbE9EX6n.js +10 -0
- package/dist/web-app/index.html +2 -2
- package/examples/fleet/agents/Alice.yaml +11 -0
- package/examples/fleet/agents/FleetCoordinator.yaml +7 -0
- package/examples/fleet/brains/claude-default.yaml +5 -0
- package/examples/fleet/roles/coordinator.yaml +5 -0
- package/examples/fleet/roles/developer.yaml +3 -0
- package/examples/fleet.yaml +74 -0
- package/package.json +4 -8
- package/dist/session/tmux.d.ts +0 -28
- package/dist/session/tmux.js +0 -80
- package/dist/web/terminal/bridge.d.ts +0 -27
- package/dist/web/terminal/bridge.js +0 -317
- package/dist/web-app/assets/TerminalView-C_G1ID2P.js +0 -9
- package/dist/web-app/assets/index-BCBK78hw.js +0 -10
- package/dist/web-app/assets/index-DuC-xnX4.css +0 -1
package/dist/web/runtime.js
CHANGED
|
@@ -4,14 +4,13 @@ import { resolve } from 'node:path';
|
|
|
4
4
|
import { loadConfig } from '../config.js';
|
|
5
5
|
import { RoleRepository } from '../application/role-repository.js';
|
|
6
6
|
import { FleetQueryService } from '../application/fleet-query-service.js';
|
|
7
|
-
import { AcpRoleSessionAdapter
|
|
7
|
+
import { AcpRoleSessionAdapter } from '../application/session-control.js';
|
|
8
8
|
import { StructuredLogService } from '../application/log-service.js';
|
|
9
9
|
import { RoleCommandService } from '../application/role-command-service.js';
|
|
10
10
|
import { RoleCreationService } from '../application/role-creation-service.js';
|
|
11
11
|
import { RoleRemovalService } from '../application/role-removal-service.js';
|
|
12
12
|
import { FleetError } from '../application/errors.js';
|
|
13
13
|
import { controlRequest, controlSocketPath } from '../session/control.js';
|
|
14
|
-
import { Tmux } from '../tmux.js';
|
|
15
14
|
import { pickBackend } from '../supervisor/index.js';
|
|
16
15
|
import { realExec } from '../exec.js';
|
|
17
16
|
import { home, stateRoot } from '../paths.js';
|
|
@@ -23,7 +22,6 @@ import { mergeTopology } from './topology-model.js';
|
|
|
23
22
|
import { TopologyDraftStore } from './topology-draft-store.js';
|
|
24
23
|
import { TopologyPromoteService } from './topology-promote.js';
|
|
25
24
|
import { doctor } from '../doctor.js';
|
|
26
|
-
import { TerminalBridgeManager } from './terminal/bridge.js';
|
|
27
25
|
import { acquireWebServerLock } from './lock.js';
|
|
28
26
|
import { TrustedDeviceStore } from './device-store.js';
|
|
29
27
|
import { WebAuth } from './auth.js';
|
|
@@ -62,7 +60,6 @@ export async function startWebConsole(options) {
|
|
|
62
60
|
}
|
|
63
61
|
const access = options.access ?? new WebAccessStore(webDir).read();
|
|
64
62
|
const auth = new WebAuth(publicOrigin?.origin ?? `http://127.0.0.1:${requestedPort}`, publicOrigin?.host ?? `127.0.0.1:${requestedPort}`, Date.now, new TrustedDeviceStore(webDir), access);
|
|
65
|
-
const tmux = new Tmux();
|
|
66
63
|
const backend = pickBackend();
|
|
67
64
|
const repository = new RoleRepository({
|
|
68
65
|
configPath: options.configPath,
|
|
@@ -78,13 +75,11 @@ export async function startWebConsole(options) {
|
|
|
78
75
|
}
|
|
79
76
|
catch { /* stale socket is evidence, not reachability */ }
|
|
80
77
|
}
|
|
81
|
-
return { acp
|
|
78
|
+
return { acp };
|
|
82
79
|
},
|
|
83
80
|
});
|
|
84
81
|
const events = new FleetEventBus();
|
|
85
82
|
const audit = new AuditSink();
|
|
86
|
-
const terminals = new TerminalBridgeManager({ repository, audit, tmux });
|
|
87
|
-
const terminalAvailable = await terminals.available();
|
|
88
83
|
const watchdogConfigProvider = cachedConfigProvider(options.configPath);
|
|
89
84
|
const log = options.log ?? (() => { });
|
|
90
85
|
let loggedWatchdogFindingsError = false;
|
|
@@ -109,8 +104,8 @@ export async function startWebConsole(options) {
|
|
|
109
104
|
}
|
|
110
105
|
}, CONFIG_CACHE_TTL_MS);
|
|
111
106
|
const query = new FleetQueryService({
|
|
112
|
-
repository, supervisor: backend,
|
|
113
|
-
capabilityContext: {
|
|
107
|
+
repository, supervisor: backend,
|
|
108
|
+
capabilityContext: {},
|
|
114
109
|
watchdogFindings,
|
|
115
110
|
});
|
|
116
111
|
const ops = { backend, binPath: options.binPath, log };
|
|
@@ -162,9 +157,6 @@ export async function startWebConsole(options) {
|
|
|
162
157
|
query, repository, logs, commands, creation, removal, audit, events, watchdogs, configuration,
|
|
163
158
|
taskRooms: new TaskRoomApplicationService(options.configPath),
|
|
164
159
|
topology: readTopology, topologyDrafts, topologyPromote,
|
|
165
|
-
terminalUpgrade: terminalAvailable
|
|
166
|
-
? async (socket, _request, roleId, _ticket, hello) => terminals.connect(socket, roleId, hello)
|
|
167
|
-
: undefined,
|
|
168
160
|
async session(roleId) {
|
|
169
161
|
const role = await repository.get(roleId);
|
|
170
162
|
if (!role)
|
|
@@ -175,8 +167,6 @@ export async function startWebConsole(options) {
|
|
|
175
167
|
throw new FleetError('control_unavailable', 'role state directory is unavailable');
|
|
176
168
|
return new AcpRoleSessionAdapter(dir);
|
|
177
169
|
}
|
|
178
|
-
if (role.configuredBackend === 'tmux' || role.detectedBackend === 'tmux')
|
|
179
|
-
return new TmuxRoleSessionAdapter(roleId, tmux);
|
|
180
170
|
throw new FleetError('capability_unavailable', 'role session backend is unavailable');
|
|
181
171
|
},
|
|
182
172
|
}, { origin: `http://127.0.0.1:${requestedPort}`, host: `127.0.0.1:${requestedPort}` }, { auth });
|
|
@@ -231,7 +221,6 @@ export async function startWebConsole(options) {
|
|
|
231
221
|
async close() {
|
|
232
222
|
try {
|
|
233
223
|
await control.close();
|
|
234
|
-
await terminals.close();
|
|
235
224
|
await server.close();
|
|
236
225
|
}
|
|
237
226
|
finally {
|
package/dist/web/server.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { type FastifyInstance
|
|
2
|
-
import type { WebSocket } from 'ws';
|
|
1
|
+
import { type FastifyInstance } from 'fastify';
|
|
3
2
|
import type { FleetQueryService } from '../application/fleet-query-service.js';
|
|
4
3
|
import type { RoleRepository } from '../application/role-repository.js';
|
|
5
4
|
import type { RoleSessionControl } from '../application/session-control.js';
|
|
@@ -32,7 +31,6 @@ export interface WebServices {
|
|
|
32
31
|
topologyPromote?: TopologyPromoteService;
|
|
33
32
|
removal?: RoleRemovalService;
|
|
34
33
|
taskRooms?: TaskRoomApplicationService;
|
|
35
|
-
terminalUpgrade?: (socket: WebSocket, request: FastifyRequest, roleId: string, ticket: string, hello: Record<string, unknown>) => Promise<void>;
|
|
36
34
|
}
|
|
37
35
|
export interface WebServer {
|
|
38
36
|
app: FastifyInstance;
|
package/dist/web/server.js
CHANGED
|
@@ -101,7 +101,7 @@ export async function buildWebServer(services, boundary, options = {}) {
|
|
|
101
101
|
return {
|
|
102
102
|
version: VERSION, api: { major: 1, minor: 0 },
|
|
103
103
|
websocketProtocols: [
|
|
104
|
-
'ours-fleet-events.v1', 'ours-fleet-
|
|
104
|
+
'ours-fleet-events.v1', 'ours-fleet-conversation.v1',
|
|
105
105
|
],
|
|
106
106
|
auditDegraded: audit.degraded,
|
|
107
107
|
};
|
|
@@ -335,7 +335,7 @@ export async function buildWebServer(services, boundary, options = {}) {
|
|
|
335
335
|
const control = await services.session(request.params.id);
|
|
336
336
|
// The idempotent, durably admitted path — used whenever the caller sends a
|
|
337
337
|
// command id and the role has a conversation ledger. The legacy path stays
|
|
338
|
-
// for old clients
|
|
338
|
+
// for old clients.
|
|
339
339
|
if (control.submitPromptV2) {
|
|
340
340
|
const admittedCommandId = commandId ?? randomBytes(16).toString('hex');
|
|
341
341
|
const receipt = await control.submitPromptV2({
|
|
@@ -440,7 +440,7 @@ export async function buildWebServer(services, boundary, options = {}) {
|
|
|
440
440
|
});
|
|
441
441
|
app.post('/api/v1/ws-tickets', async (request) => {
|
|
442
442
|
const body = request.body;
|
|
443
|
-
if (!body?.purpose || !['events', '
|
|
443
|
+
if (!body?.purpose || !['events', 'conversation'].includes(body.purpose))
|
|
444
444
|
throw new FleetError('invalid_request', 'ticket purpose is required');
|
|
445
445
|
if (body.purpose === 'conversation' && !body.roleId)
|
|
446
446
|
throw new FleetError('invalid_request', 'a conversation ticket must be bound to a role');
|
|
@@ -534,17 +534,6 @@ export async function buildWebServer(services, boundary, options = {}) {
|
|
|
534
534
|
socket.on('close', () => { clearInterval(heartbeat); follow.close(); });
|
|
535
535
|
});
|
|
536
536
|
});
|
|
537
|
-
app.get('/api/v1/roles/:id/terminal', { websocket: true }, (socket, request) => {
|
|
538
|
-
requireSubprotocol(request, 'ours-fleet-terminal.v1');
|
|
539
|
-
authorizeSocket(socket, request, async (hello) => {
|
|
540
|
-
const ticket = String(hello.ticket ?? '');
|
|
541
|
-
const session = auth.consumeTicket(request, ticket, 'terminal', request.params.id);
|
|
542
|
-
auth.bindSocket(session.id, socket);
|
|
543
|
-
if (!services.terminalUpgrade)
|
|
544
|
-
throw new FleetError('capability_unavailable', 'terminal PTY support is unavailable');
|
|
545
|
-
await services.terminalUpgrade(socket, request, request.params.id, ticket, hello);
|
|
546
|
-
});
|
|
547
|
-
});
|
|
548
537
|
const staticRoot = join(dirname(fileURLToPath(import.meta.url)), '..', 'web-app');
|
|
549
538
|
if (existsSync(staticRoot)) {
|
|
550
539
|
await app.register(fastifyStatic, { root: staticRoot, prefix: '/' });
|
|
@@ -15,8 +15,9 @@ import { FleetError } from '../application/errors.js';
|
|
|
15
15
|
*/
|
|
16
16
|
/** Loop interval when the sketch did not choose one (`resolveLoops` requires it). */
|
|
17
17
|
const DEFAULT_LOOP_INTERVAL = '10m';
|
|
18
|
-
|
|
19
|
-
const
|
|
18
|
+
const ROLE_FIELDS = ['mission', 'bio', 'persona'];
|
|
19
|
+
const BRAIN_FIELDS = ['harness', 'session', 'model'];
|
|
20
|
+
const AGENT_OP_FIELDS = ['coordinator', 'identity', 'cwd'];
|
|
20
21
|
/** Scalar draft fields that are valid keys of a watchdog mapping. */
|
|
21
22
|
const WATCHDOG_FIELDS = ['coordinator', 'interval', 'enabled'];
|
|
22
23
|
/** Scalar draft fields that are valid keys of a loop mapping. */
|
|
@@ -100,15 +101,22 @@ function resolve(merged, id) {
|
|
|
100
101
|
return node;
|
|
101
102
|
}
|
|
102
103
|
function addNode(model, node, merged) {
|
|
103
|
-
const block =
|
|
104
|
+
const block = node.kind === 'agent' ? model.agents
|
|
105
|
+
: manifestSection(model, node.kind === 'watchdog' ? 'watchdogs' : 'loops');
|
|
104
106
|
if (block[node.label] !== undefined)
|
|
105
107
|
throw new FleetError('conflict', `${node.label} already exists in the configuration`);
|
|
106
108
|
block[node.label] = node.kind === 'agent' ? agentEntry(node)
|
|
107
109
|
: node.kind === 'watchdog' ? watchdogEntry(node, merged)
|
|
108
|
-
: loopEntry(node, merged
|
|
110
|
+
: loopEntry(node, merged);
|
|
109
111
|
}
|
|
110
112
|
function agentEntry(node) {
|
|
111
|
-
|
|
113
|
+
const brain = pick(node, BRAIN_FIELDS);
|
|
114
|
+
brain.harness ??= 'claude-code';
|
|
115
|
+
return {
|
|
116
|
+
role: { inline: pick(node, ROLE_FIELDS) },
|
|
117
|
+
brain: { inline: brain },
|
|
118
|
+
...pick(node, AGENT_OP_FIELDS),
|
|
119
|
+
};
|
|
112
120
|
}
|
|
113
121
|
/**
|
|
114
122
|
* A watchdog drawn on its own carries
|
|
@@ -120,16 +128,13 @@ function watchdogEntry(node, merged) {
|
|
|
120
128
|
const scoped = linked(merged, node.id, 'watches');
|
|
121
129
|
return { ...pick(node, WATCHDOG_FIELDS), ...(scoped.length ? { watch: scoped } : {}) };
|
|
122
130
|
}
|
|
123
|
-
function loopEntry(node, merged
|
|
131
|
+
function loopEntry(node, merged) {
|
|
124
132
|
const roles = linked(merged, node.id, 'targets');
|
|
125
133
|
const entry = {
|
|
126
134
|
roles, ...pick(node, LOOP_FIELDS),
|
|
127
135
|
};
|
|
128
136
|
entry.interval ??= DEFAULT_LOOP_INTERVAL;
|
|
129
|
-
|
|
130
|
-
// tmux agent would make the whole fleet unloadable, so it arrives switched off
|
|
131
|
-
// and badged instead — the owner turns it on after switching the agent to ACP.
|
|
132
|
-
if (node.enabled === false || !roles.every(role => sessionOf(model, role) === 'acp'))
|
|
137
|
+
if (node.enabled === false)
|
|
133
138
|
entry.enabled = false;
|
|
134
139
|
return entry;
|
|
135
140
|
}
|
|
@@ -139,17 +144,12 @@ function linked(merged, id, kind) {
|
|
|
139
144
|
.filter(edge => edge.kind === kind && edge.from === id && !edge.implicit && !edge.dangling)
|
|
140
145
|
.map(edge => edge.to.slice('agent:'.length)))].sort();
|
|
141
146
|
}
|
|
142
|
-
function
|
|
143
|
-
const
|
|
144
|
-
const defaults = model.defaults;
|
|
145
|
-
return String(roles?.[role]?.session ?? defaults?.session ?? 'tmux');
|
|
146
|
-
}
|
|
147
|
-
function section(model, key) {
|
|
148
|
-
const existing = model[key];
|
|
147
|
+
function manifestSection(model, key) {
|
|
148
|
+
const existing = model.manifest[key];
|
|
149
149
|
if (existing && typeof existing === 'object' && !Array.isArray(existing))
|
|
150
150
|
return existing;
|
|
151
151
|
const created = {};
|
|
152
|
-
model[key] = created;
|
|
152
|
+
model.manifest[key] = created;
|
|
153
153
|
return created;
|
|
154
154
|
}
|
|
155
155
|
/** Copy only the sketched fields that are real keys of the target mapping. */
|
package/dist/web/topology.js
CHANGED
|
@@ -11,8 +11,8 @@ export function deriveTopology(config, roles) {
|
|
|
11
11
|
const add = (kind, from, to, label) => edges.push({ id: `${kind}:${from}:${to}`, kind, from, to, label });
|
|
12
12
|
for (const role of config.roles)
|
|
13
13
|
for (const entry of role.oversee ?? [])
|
|
14
|
-
if (agentIds.has(role.name) && agentIds.has(entry.
|
|
15
|
-
add('oversees', `agent:${role.name}`, `agent:${entry.
|
|
14
|
+
if (agentIds.has(role.name) && agentIds.has(entry.agent))
|
|
15
|
+
add('oversees', `agent:${role.name}`, `agent:${entry.agent}`, `oversees · ${entry.interval}`);
|
|
16
16
|
for (const watchdog of config.watchdogs) {
|
|
17
17
|
const id = `watchdog:${watchdog.name}`;
|
|
18
18
|
nodes.push({
|
|
@@ -85,6 +85,8 @@ export function redactSourceSecrets(source, marker) {
|
|
|
85
85
|
redactEnvMap(['defaults', 'env']);
|
|
86
86
|
for (const name of mapKeys(document, ['roles']))
|
|
87
87
|
redactEnvMap(['roles', name, 'env']);
|
|
88
|
+
// A v2 Agent document is itself the role/ops mapping, so env is top-level.
|
|
89
|
+
redactEnvMap(['env']);
|
|
88
90
|
const vars = document.getIn(['vars'], true);
|
|
89
91
|
if (isMap(vars))
|
|
90
92
|
for (const item of vars.items) {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
:root{font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;color:#e9eef0;background:#080b0c;font-synthesis:none;--panel: #111617;--line: #222a2c;--muted: #849093;--mint: #7ce7b0;--amber: #f0c66c;--red: #ff827c}*{box-sizing:border-box}body{margin:0;min-width:0;min-height:100vh;background:radial-gradient(circle at 70% -20%,#183027 0,transparent 35%),#080b0c}button,input,select,textarea{font:inherit}button{color:inherit}button:focus-visible,input:focus-visible,select:focus-visible,textarea:focus-visible{outline:2px solid var(--mint);outline-offset:2px}.auth-screen{display:grid;place-content:center;text-align:center;min-height:100vh;color:var(--muted)}.auth-screen h1{color:#fff;margin-bottom:0}.brand-wordmark{color:var(--mint);font-size:21px;font-weight:820;letter-spacing:-.02em}.auth-screen .brand-wordmark{justify-self:center;font-size:32px}.shell{display:grid;grid-template-columns:224px 1fr;min-height:100vh}aside{position:fixed;inset:0 auto 0 0;width:224px;display:flex;flex-direction:column;padding:24px 14px;border-right:1px solid #1d2425;background:#080b0ceb;-webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px)}.brand{display:flex;gap:12px;align-items:center;padding:0 8px 30px}.brand div{display:flex;flex-direction:column}.brand small{color:var(--muted);font-size:11px;text-transform:uppercase;letter-spacing:1.4px}nav{display:grid;gap:5px}nav button{display:grid;grid-template-columns:24px 1fr auto;gap:8px;align-items:center;width:100%;border:0;border-radius:8px;padding:10px 12px;text-align:left;color:#98a3a5;background:transparent;text-transform:capitalize;cursor:pointer}nav button:hover,nav button.active{color:#fff;background:#151b1c}nav button.active{box-shadow:inset 2px 0 var(--mint)}nav b{background:#422f22;color:var(--amber);border-radius:10px;font-size:11px;padding:2px 7px}.sidebar-foot{margin-top:auto;display:grid;grid-template-columns:12px 1fr;align-items:center;padding:12px;color:var(--muted);font-size:12px;text-transform:capitalize}.sidebar-foot i{width:7px;height:7px;border-radius:50%;background:var(--amber)}.sidebar-foot i.live{background:var(--mint);box-shadow:0 0 9px var(--mint)}.sidebar-foot small{grid-column:2;opacity:.6;margin-top:3px}.main{grid-column:2;min-width:0}header{display:flex;justify-content:space-between;align-items:flex-end;height:124px;padding:25px 36px 23px;border-bottom:1px solid var(--line);background:#080b0c8c}h1,h2,h3,p{margin-top:0}header h1{margin:4px 0 0;font-size:29px;letter-spacing:-.8px}.eyebrow{color:var(--mint);text-transform:uppercase;font-size:10px;letter-spacing:1.8px;font-weight:700}.header-actions,.actions{display:flex;gap:9px}button.primary,button.secondary,.actions button{border-radius:7px;padding:9px 13px;cursor:pointer}button.primary{border:1px solid #83e9b5;color:#07130d;background:var(--mint);font-weight:700}button.primary:disabled{opacity:.4;cursor:default}button.secondary,.actions button{border:1px solid #30393b;background:#141a1b}button.secondary:hover,.actions button:hover{border-color:#526064}.content,.workspace{padding:28px 36px 60px}.toolbar{display:flex;align-items:center;justify-content:space-between;margin-bottom:16px;color:var(--muted);font-size:12px}.toolbar input{width:340px;padding:10px 13px;color:#fff;background:#101516;border:1px solid var(--line);border-radius:8px}.toolbar>div{display:flex;align-items:center;gap:10px}.toolbar button{padding:7px 10px}.role-row.inactive{opacity:.72}.role-row.active{background:#141a1b;border-left:2px solid var(--mint)}.watchdog-detail-layout{display:grid;gap:18px}.watchdog-report-head{display:flex;align-items:center;gap:12px}.watchdog-report-head h2{flex:1}.watchdog-roles{display:grid;gap:10px;margin-top:12px}.watchdog-role{border:1px solid var(--line);border-radius:8px;padding:12px 14px;background:#0d1213}.watchdog-role-head{display:flex;align-items:center;gap:10px}.watchdog-role-head strong{text-transform:capitalize}.watchdog-role-detail,.watchdog-role-note{margin:8px 0 0;font-size:12px;color:var(--muted)}.watchdog-role-note{color:var(--amber)}.watchdog-role details{margin-top:8px;font-size:12px}.watchdog-role summary{cursor:pointer;color:var(--muted)}.watchdog-role details ul{margin:8px 0 0;padding-left:18px;color:#c9d1d3}.watchdog-role details li{margin-bottom:4px}.status-guide{display:flex;flex-wrap:wrap;gap:7px;align-items:center;margin-bottom:15px;color:var(--muted);font-size:11px}.status-guide>span,.status-chip{display:inline-flex;align-items:center;gap:5px;border:1px solid #30393b;border-radius:12px;padding:3px 7px;white-space:nowrap}.status-guide b{color:#fff;font-size:11px}.status-guide small{flex-basis:100%;color:var(--muted)}.status-guide .ready,.status-chip.ready{border-color:#315e4a}.status-guide .busy,.status-chip.busy{border-color:#315478}.status-guide .attention,.status-chip.attention,.status-chip.unknown{border-color:#66552f}.status-guide .offline,.status-chip.offline{border-color:#693b38}.role-table{border:1px solid var(--line);border-radius:10px;overflow:hidden;background:#0e1213cc}.role-row{display:grid;grid-template-columns:minmax(260px,1.55fr) repeat(4,minmax(125px,1fr));width:100%;align-items:center;gap:15px;padding:14px 18px;border:0;border-bottom:1px solid var(--line);background:transparent;text-align:left;cursor:pointer}button.role-row:hover{background:#141a1b}.role-row.heading{padding-block:10px;color:#6f7b7e;font-size:10px;letter-spacing:1px;text-transform:uppercase;cursor:default}.role-row:last-child{border-bottom:0}.role-row>span{min-width:0;display:flex;flex-direction:column;gap:3px}.role-row strong{font-size:13px;text-transform:capitalize;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.role-row small{color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.role-name{position:relative;display:grid!important;grid-template-columns:auto 1fr auto;align-items:center}.role-name .mission-summary{display:-webkit-box;max-height:2.5em;line-height:1.25;overflow:hidden;overflow-wrap:anywhere;white-space:normal;-webkit-box-orient:vertical;-webkit-line-clamp:2}.status-chip{color:#c9d1d3;font-size:10px;padding:3px 6px}.status-chip i{width:6px;height:6px;border-radius:50%;background:#667174}.status-chip.ready i{background:var(--mint)}.status-chip.busy i{background:#71b7ff}.status-chip.attention i,.status-chip.unknown i{background:var(--amber)}.status-chip.offline i{background:var(--red)}.state.ready,.hero-state.ready i{background:var(--mint);box-shadow:0 0 7px #69dba0}.state.busy,.hero-state.busy i{background:#71b7ff}.state.attention,.state.unknown,.hero-state.attention i,.hero-state.unknown i{background:var(--amber)}.state.offline,.hero-state.offline i{background:var(--red)}.role-name em{color:#7f8a8d;font-size:10px;font-style:normal;border:1px solid #2e3739;border-radius:10px;padding:3px 6px}.role-pills{display:flex;flex-direction:column;align-items:flex-end;gap:4px}.empty{padding:60px;text-align:center;color:var(--muted)}.banner{margin:16px 36px 0;border:1px solid;border-radius:8px;padding:10px 13px;font-size:13px}.banner.error,.error{border-color:#5c302f;background:#271616;color:#ffaaa5}.banner.warning,.warning{border-color:#5a4726;background:#241e12;color:#f1ce83}.banner.info{border-color:#28463b;background:#10231c;color:#9be4bf}.workspace>.banner{margin-inline:0}.back{border:0;background:none;color:var(--muted);padding:0 0 18px;cursor:pointer}.workspace-summary{display:flex;flex-wrap:wrap;align-items:center;gap:7px;color:var(--muted);font-size:12px}.workspace-summary>span{border:1px solid var(--line);border-radius:12px;padding:4px 8px}.workspace-summary>small{margin-left:auto}.hero-state{display:flex;gap:7px;align-items:center;color:#fff;text-transform:capitalize}.hero-state i{width:7px;height:7px;border-radius:50%;background:#788}.tabs{display:flex;gap:22px;margin:26px 0 22px;border-bottom:1px solid var(--line)}.tabs button{border:0;border-bottom:2px solid transparent;padding:10px 2px;background:none;color:var(--muted);text-transform:capitalize;cursor:pointer}.tabs button.active{color:#fff;border-bottom-color:var(--mint)}.tabs button:disabled{opacity:.35;cursor:not-allowed}.dashboard-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px}.panel{border:1px solid var(--line);border-radius:10px;background:#101516db;padding:18px}.panel h2{font-size:15px;letter-spacing:-.2px}.panel.wide{grid-column:1 / -1}.evidence>div:first-child{display:flex;justify-content:space-between}.evidence>div:first-child strong{color:var(--mint);text-transform:capitalize;font-size:12px}dl{margin:0}dl div{display:grid;grid-template-columns:120px 1fr;gap:15px;padding:7px 0;border-top:1px solid #1d2426;font-size:12px}dt{color:var(--muted)}dd{margin:0;word-break:break-word}.danger{color:#ff9e99!important;border-color:#613432!important}.activity-layout{display:grid;grid-template-columns:1fr 290px;gap:14px;align-items:start}.activity{display:grid;gap:9px}.activity-filter{display:flex;align-items:center;justify-content:space-between;gap:12px;color:var(--muted);font-size:11px}.activity-filter button{padding:6px 9px}.event{border-left:2px solid #334044;padding:10px 13px;background:#0d1213}.event.agent_text{border-color:var(--mint)}.event.error,.event.permission{border-color:var(--amber)}.event p{margin:5px 0 0;white-space:pre-wrap}.event small,.muted{color:var(--muted)}.composer{position:sticky;top:16px;display:grid;gap:9px}textarea{min-height:84px;resize:vertical}.composer textarea,fieldset input,fieldset select,fieldset textarea{width:100%;color:#fff;border:1px solid #303a3c;border-radius:6px;background:#0d1112;padding:9px 10px}.composer small{color:var(--muted);line-height:1.45}pre{max-width:100%;overflow:auto;white-space:pre-wrap;word-break:break-word;font:12px/1.55 SFMono-Regular,Consolas,monospace;color:#cbd5d7}.log-panel>pre{margin:0;padding:5px 8px;border-bottom:1px solid #1b2224}.audit-list div{display:grid;grid-template-columns:180px 1fr 120px;padding:10px 0;border-top:1px solid var(--line);font-size:12px}.audit-list time{color:var(--muted)}.modal-backdrop{position:fixed;z-index:10;inset:0;display:grid;place-items:center;padding:30px;background:#000000b8;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px)}.modal{width:min(900px,95vw);max-height:92vh;overflow:auto;border:1px solid #354043;border-radius:13px;background:#0d1112;box-shadow:0 30px 100px #000}.modal-head{position:sticky;z-index:2;top:0;display:flex;justify-content:space-between;align-items:flex-start;padding:22px 26px 18px;border-bottom:1px solid var(--line);background:#0d1112f5}.modal-head h2{margin:4px 0 0}.icon{border:0;background:none;color:var(--muted);font-size:25px;cursor:pointer}.wizard,.progress{padding:22px 26px}fieldset{border:0;border-bottom:1px solid var(--line);padding:0 0 20px;margin:0 0 20px}legend{margin-bottom:13px;color:var(--mint);text-transform:uppercase;font-size:10px;letter-spacing:1.5px;font-weight:700}.form-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:13px}.form-grid.three{grid-template-columns:repeat(3,1fr)}.wake-sources{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:7px 14px}.wake-sources>small{grid-column:1 / -1;color:var(--muted)}.wake-sources label{display:flex;flex-direction:row;align-items:center;gap:7px;color:#c7d0d1;font-size:11px;text-transform:none;letter-spacing:0}.form-grid label{display:grid;gap:6px;color:#b7c0c2;font-size:12px}.form-grid label small{color:var(--muted)}.form-grid .wide{grid-column:1 / -1}.risk{display:flex;align-items:center;gap:8px;margin-top:14px;color:var(--amber);font-size:12px}.review{border:1px solid #2f493f;border-radius:8px;background:#101b17;padding:15px}.review dl{display:grid;grid-template-columns:1fr 1fr;gap:0 20px}.modal-actions{display:flex;justify-content:flex-end;gap:9px;margin-top:20px}.stage{display:flex;gap:12px;min-height:54px;position:relative}.stage:after{position:absolute;left:5px;top:14px;bottom:0;width:1px;background:#34413d;content:""}.stage:last-child:after{display:none}.stage i{z-index:1;width:11px;height:11px;margin-top:2px;border-radius:50%;background:var(--mint);box-shadow:0 0 0 4px #173026}.stage span{display:flex;flex-direction:column;text-transform:capitalize}.stage small{color:var(--muted);margin-top:3px}@media(max-width:1050px){.role-row{grid-template-columns:1.5fr repeat(3,1fr)}.role-row>span:nth-child(4){display:none}}.topology{overflow-x:auto;margin-bottom:16px;border:1px solid var(--line);border-radius:12px;background:radial-gradient(circle at 50% 50%,#13201b 0,#0b1011 58%)}.topology-canvas{position:relative;width:1000px;min-width:100%}.topology svg{position:absolute;inset:0;width:100%;height:100%}.edge line{stroke:#405052;stroke-width:1.5;vector-effect:non-scaling-stroke}.edge.spawned line{stroke:var(--mint);stroke-dasharray:5 4}.edge.watches line{stroke:#d7ad58;stroke-dasharray:2 3}.edge.targets line{stroke:#729ed6;stroke-dasharray:9 3 2 3}.topology-legend{display:grid;gap:8px;margin:0 0 12px;padding:12px 14px;border:1px solid var(--line);border-radius:10px;background:#0c1112}.topology-legend ul{display:grid;grid-template-columns:repeat(4,minmax(140px,1fr));gap:8px;margin:0;padding:0;list-style:none}.topology-legend li{display:flex;gap:9px;align-items:start;padding:7px;border-radius:7px}.topology-legend li:focus{outline:2px solid var(--mint);outline-offset:1px}.topology-legend li>span:last-child{display:grid;gap:2px}.topology-legend small{color:var(--muted);line-height:1.3}.legend-line{width:34px;margin-top:7px;border-top:2px solid #405052;flex:0 0 auto}.legend-line.spawned{border-color:var(--mint);border-top-style:dashed}.legend-line.watches{border-color:#d7ad58;border-top-style:dotted}.legend-line.targets{height:5px;border-color:#729ed6;border-bottom:1px dotted #729ed6}.topology-node{position:absolute;width:150px;min-height:62px;display:grid;gap:3px;z-index:1;padding:0;text-align:left;border:1px solid #344044;border-radius:10px;background:#111718;box-shadow:0 8px 24px #00000047;transform:translateY(-50%)}.topology-open{display:grid;gap:3px;min-width:0;padding:10px 28px 10px 12px;color:inherit;text-align:left;border:0;background:transparent;cursor:pointer}.topology-remove{position:absolute;top:5px;right:5px;width:22px;height:22px;padding:0;color:var(--muted);border:0;background:transparent}.topology-remove:hover,.topology-remove:focus{color:#ff8e85}.topology-node:hover{border-color:var(--mint);transform:translateY(-50%) translateY(-2px)}.topology-open:disabled{cursor:default;opacity:1}.topology-node small{color:var(--muted);text-transform:uppercase;font-size:9px;letter-spacing:1px}.topology-node strong{overflow:hidden;text-overflow:ellipsis}.topology-node span{color:var(--muted);overflow:hidden;white-space:nowrap;text-overflow:ellipsis;font-size:10px}.topology-node.watchdog{border-color:#6a5830}.topology-node.loop{border-color:#385778}.topology-node.temporary{border-style:dashed}.topology-node.attention,.topology-node.unknown{box-shadow:inset 3px 0 var(--amber)}.topology-node.offline{opacity:.65}.lineage-note{color:var(--muted);font-size:11px}.topology-editor{display:grid;gap:12px}.topology-toolbar{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.topology-toolbar .spacer{flex:1 1 auto}.draft-count{color:var(--amber);font-size:11px}.topology-empty{display:grid;gap:10px;justify-items:start;padding:26px;border:1px dashed var(--line);border-radius:12px;background:#0c1112}.topology-empty h3{margin:0}.topology-empty p{margin:0;color:var(--muted)}.topology-tutorial{padding:12px 14px;border:1px solid var(--line);border-radius:10px;background:#0c1112;font-size:12px}.topology-tutorial ol{display:grid;gap:6px;margin:0 0 8px;padding-left:18px;color:var(--muted)}.topology-tutorial li.current{color:#d5dddf}.banner{margin:0;padding:9px 12px;border:1px solid var(--line);border-radius:8px;background:#0c1112;color:var(--muted);font-size:12px}.banner.warning{border-color:#6a5830;color:var(--amber)}.node-badge{display:inline-block;padding:1px 6px;border-radius:999px;font-size:9px;letter-spacing:.3px}.node-badge.draft-incomplete{background:#2a2413;color:var(--amber)}.node-badge.draft-ready{background:#12261f;color:var(--mint)}.node-badge.configured{background:#16202a;color:#9fc0e0}.node-badge.running{background:#12261f;color:var(--mint)}.node-detail{display:block}.topology-node.draft-incomplete,.topology-node.draft-ready{border-style:dashed}.topology-node.selected{border-color:var(--mint);box-shadow:0 0 0 2px #58c7a059}.topology-node[data-node-id]{touch-action:none}.edge.implicit line{stroke-dasharray:1 5;opacity:.75}.edge.dangling line{stroke:var(--red);stroke-dasharray:3 3}.edge.drafted line{opacity:.7}.topology-list{width:100%;border-collapse:collapse;color:#d5dddf;text-align:left;font-size:12px}.topology-list th,.topology-list td{padding:8px;border-top:1px solid var(--line)}.topology-list tr.selected{background:#101718}.node-inspector{display:grid;gap:10px;padding:14px;border:1px solid var(--line);border-radius:12px;background:#0c1112}.node-inspector header{display:flex;align-items:center;justify-content:space-between}.node-inspector h3,.node-inspector h4{margin:0}.node-inspector label{display:grid;gap:4px;font-size:12px}.node-inspector small{color:var(--muted);font-size:10px}.missing-list{display:grid;gap:4px;margin:0;padding-left:18px;color:var(--amber);font-size:12px}.connection-list{display:grid;gap:4px;margin:0;padding-left:18px;color:var(--muted);font-size:11px}.inspector-actions{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.review-panel{display:grid;gap:10px;padding:14px;border:1px solid var(--mint);border-radius:12px;background:#0c1112}.review-panel h3{margin:0}.review-panel .row{display:flex;gap:8px}.topology-fallback{margin:0 0 18px;color:var(--muted);font-size:12px}.topology-fallback summary{cursor:pointer}.topology-fallback table{width:100%;border-collapse:collapse;margin-top:10px;color:#d5dddf;text-align:left}.topology-fallback th,.topology-fallback td{padding:8px;border-top:1px solid var(--line)}.text-button{border:0;padding:0;color:var(--mint);background:transparent;cursor:pointer;text-align:left}.setup-entry{max-width:1180px;margin-inline:auto}.setup-hero{display:grid;grid-template-columns:1fr 300px;gap:30px;align-items:center;padding:12px 0 26px}.setup-hero h2{margin:6px 0 8px;font-size:28px}.setup-hero p{max-width:680px;color:var(--muted)}.setup-safety{display:grid;gap:5px;padding:15px;border:1px solid #315e4a;border-radius:10px;background:#102019}.setup-safety span{color:var(--muted);font-size:12px;line-height:1.45}.setup-steps{display:grid;grid-template-columns:repeat(4,1fr);gap:0;margin:0 0 18px;padding:0;list-style:none}.setup-steps li{display:flex;gap:8px;align-items:center;color:#647174;border-bottom:2px solid #263033;padding:10px;font-size:12px}.setup-steps li.active{color:#fff;border-color:var(--mint)}.setup-steps li.done{color:var(--mint);border-color:#315e4a}.setup-panel{min-height:360px}.setup-panel h3{margin-bottom:6px}.mode-switch{display:flex;gap:8px;align-items:center;margin:0 0 14px;padding:8px;border:1px solid var(--line);border-radius:10px}.mode-switch button.active{color:#07110d;background:var(--mint);border-color:var(--mint)}.mode-switch span{margin-left:6px;color:var(--muted);font-size:12px}.decision-note{display:flex;gap:10px;margin:12px 0;padding:11px;border-left:3px solid var(--mint);background:#102019;color:var(--muted)}.decision-note strong{color:#dce6e2;white-space:nowrap}.setup-actions{display:grid;grid-template-columns:auto 1fr auto;gap:10px;margin-top:22px;padding-top:18px;border-top:1px solid var(--line)}.section-title{display:flex;justify-content:space-between;align-items:start;gap:20px}.entity-list{display:grid;gap:12px}.entity-card{position:relative;padding:15px;border:1px solid var(--line);border-radius:9px;background:#0c1112}.entity-card>.danger{position:absolute;right:14px;bottom:12px}.empty.compact{padding:34px}.preflight{display:grid;gap:4px;margin:12px 0 18px;padding:13px;border:1px solid #315e4a;border-radius:8px;background:#102019}.preflight.failed{border-color:#66552f;background:#211b10}.preflight span{color:var(--muted);font-size:12px}.setup-panel details{margin-top:14px}.setup-panel summary{cursor:pointer;color:#cbd4d6}.config-diff{max-height:360px;padding:14px;border-radius:8px;background:#080b0c}@media(max-width:760px){.topology-legend ul{grid-template-columns:1fr}.mode-switch{align-items:stretch;flex-wrap:wrap}.mode-switch span{flex-basis:100%;margin-left:0}}.check-list{display:grid;gap:7px;padding-left:20px;font-size:12px}.check-list .ok::marker{color:var(--mint)}.check-list .failed::marker{color:var(--red)}@media(max-width:760px){.shell{display:block;padding-bottom:68px}aside{z-index:9;inset:auto 0 0;width:auto;height:64px;padding:7px;border:1px solid var(--line);background:#080b0cf7}aside .brand,.sidebar-foot{display:none}aside nav{grid-template-columns:repeat(5,1fr);gap:2px}aside nav button{display:flex;min-width:0;flex-direction:column;gap:2px;justify-content:center;padding:6px 2px;text-align:center;font-size:9px}aside nav button span{font-size:15px}aside nav b{display:none}.main{width:100%}header{height:auto;min-height:104px;padding:18px;align-items:start;gap:12px}header h1{font-size:23px}.header-actions{flex-wrap:wrap;justify-content:flex-end}.header-actions button{padding:7px 9px;font-size:11px}.content,.workspace{padding:18px 14px 36px}.banner{margin-inline:14px}.toolbar{align-items:stretch;gap:10px;flex-direction:column}.toolbar input{width:100%}.role-table{overflow-x:auto}.role-row{min-width:720px}.setup-hero{grid-template-columns:1fr}.setup-steps li{flex-direction:column;font-size:10px}.form-grid,.form-grid.three{grid-template-columns:1fr}.form-grid .wide{grid-column:auto}.section-title{align-items:stretch;flex-direction:column}}@media(prefers-reduced-motion:reduce){*,*:before,*:after{scroll-behavior:auto!important;transition:none!important;animation:none!important}}.conversation-layout{display:grid;grid-template-rows:1fr auto;gap:14px;min-height:60vh}.conversation{display:flex;flex-direction:column;min-height:0;position:relative}.conversation-header{display:flex;flex-wrap:wrap;gap:12px;align-items:baseline;padding-bottom:10px;border-bottom:1px solid var(--line);font-size:12px}.stream-state{text-transform:uppercase;font-size:10px;letter-spacing:1.4px;font-weight:700;color:var(--amber)}.stream-state.live{color:var(--mint)}.stream-state.offline{color:var(--red)}.conversation-header .warning{color:var(--amber)}.transcript{flex:1;overflow-y:auto;display:grid;gap:14px;padding:16px 4px;max-height:62vh}.turn{display:grid;gap:8px}.bubble{border:1px solid var(--line);border-radius:10px;padding:10px 14px;background:#0d1213}.bubble small{color:var(--muted);display:block;margin-bottom:6px}.bubble.user{border-left:2px solid var(--mint);margin-left:8%;background:#10181a}.bubble.assistant{margin-right:8%}.prompt-text,.assistant-text,.thought-text{margin:0;white-space:pre-wrap;word-break:break-word;font-family:inherit;font-size:14px;line-height:1.5}.assistant-text+.assistant-text{margin-top:10px;padding-top:10px;border-top:1px dashed var(--line)}.thought-text{color:var(--muted);font-size:13px;margin-top:8px}.thoughts{margin-bottom:8px}.streaming-hint{animation:pulse 1.4s ease-in-out infinite}@media(prefers-reduced-motion:reduce){.streaming-hint{animation:none}}@keyframes pulse{50%{opacity:.45}}.tool-card{border:1px solid var(--line);border-radius:8px;padding:7px 11px;margin:6px 0;font-size:13px;background:#101516}.tool-card summary{display:flex;gap:10px;align-items:baseline;cursor:pointer}.tool-detail{display:grid;gap:9px;padding-top:10px}.tool-detail code{display:block;overflow-wrap:anywhere;color:var(--muted);margin-top:4px}.tool-content{border-top:1px dashed var(--line);padding-top:8px;min-width:0}.tool-content pre{max-height:260px;overflow:auto;white-space:pre-wrap;word-break:break-word;margin:5px 0 0;color:#d9e4e2}.diff-old{color:#d99b96!important}.diff-new{color:#9ed8c4!important}.plan-card{border:1px solid var(--line);border-radius:8px;padding:9px 12px;margin:6px 0;font-size:13px}.plan-card ol{list-style:none;margin:7px 0 0;padding:0;display:grid;gap:5px}.plan-entry{display:grid;grid-template-columns:18px 1fr auto;gap:7px;color:var(--muted)}.plan-entry.in_progress{color:var(--amber)}.plan-entry.completed{color:var(--mint)}.usage-badge{margin-top:10px;color:var(--muted)}.tool-status{text-transform:uppercase;font-size:10px;letter-spacing:1px;font-weight:700;color:var(--muted)}.tool-status.in_progress{color:var(--amber)}.tool-status.completed{color:var(--mint)}.tool-status.failed{color:var(--red)}.permission-card{border:1px solid var(--amber);border-radius:8px;padding:10px 12px;margin:8px 0;font-size:13px;display:grid;gap:8px}.permission-card.resolved{border-color:var(--line);color:var(--muted)}.permission-actions{display:flex;gap:8px;flex-wrap:wrap}.jump-latest{position:absolute;right:20px;bottom:16px}.conversation-layout .composer{display:grid;gap:8px}.conversation-layout .composer textarea{min-height:68px;max-height:200px;resize:vertical;color:#fff;background:#101516;border:1px solid var(--line);border-radius:8px;padding:10px 13px}.composer-actions{display:flex;gap:9px;justify-content:space-between}.composer-actions .danger{margin-left:auto}button.danger{border:1px solid #5b322f;color:var(--red);background:#1a1213;border-radius:7px;padding:9px 13px;cursor:pointer}button.danger:disabled{opacity:.4;cursor:default}
|