@agi-fans/dsh-tui 0.20.0 → 0.22.0
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/lib/CHANGELOG.md +249 -3
- package/lib/chrome/box.d.ts +47 -16
- package/lib/chrome/box.js +172 -130
- package/lib/chrome/box.js.map +1 -1
- package/lib/chrome/code-highlight.d.ts +15 -0
- package/lib/chrome/code-highlight.js +60 -4
- package/lib/chrome/code-highlight.js.map +1 -1
- package/lib/chrome/command-output.d.ts +29 -2
- package/lib/chrome/command-output.js +100 -4
- package/lib/chrome/command-output.js.map +1 -1
- package/lib/chrome/main-screen-renderer.d.ts +18 -0
- package/lib/chrome/main-screen-renderer.js +58 -5
- package/lib/chrome/main-screen-renderer.js.map +1 -1
- package/lib/chrome/markdown.js +30 -10
- package/lib/chrome/markdown.js.map +1 -1
- package/lib/chrome/relative-time.d.ts +17 -0
- package/lib/chrome/relative-time.js +36 -0
- package/lib/chrome/relative-time.js.map +1 -1
- package/lib/chrome/renderer.d.ts +9 -0
- package/lib/chrome/renderer.js +1 -1
- package/lib/chrome/renderer.js.map +1 -1
- package/lib/chrome/secret-mask.d.ts +35 -0
- package/lib/chrome/secret-mask.js +180 -0
- package/lib/chrome/secret-mask.js.map +1 -0
- package/lib/chrome/status-config.d.ts +161 -51
- package/lib/chrome/status-config.js +237 -126
- package/lib/chrome/status-config.js.map +1 -1
- package/lib/chrome/status-line.d.ts +3 -2
- package/lib/chrome/status-line.js +105 -63
- package/lib/chrome/status-line.js.map +1 -1
- package/lib/chrome/terminal-colors.d.ts +85 -0
- package/lib/chrome/terminal-colors.js +230 -0
- package/lib/chrome/terminal-colors.js.map +1 -0
- package/lib/chrome/terminal-images.d.ts +9 -1
- package/lib/chrome/terminal-images.js +27 -4
- package/lib/chrome/terminal-images.js.map +1 -1
- package/lib/chrome/theme.d.ts +32 -9
- package/lib/chrome/theme.js +616 -520
- package/lib/chrome/theme.js.map +1 -1
- package/lib/chrome/tool-args.d.ts +1 -1
- package/lib/chrome/tool-args.js +3 -1
- package/lib/chrome/tool-args.js.map +1 -1
- package/lib/chrome/tool-renderers.d.ts +2 -0
- package/lib/chrome/tool-renderers.js +69 -3
- package/lib/chrome/tool-renderers.js.map +1 -1
- package/lib/chrome/tools-list.d.ts +1 -4
- package/lib/chrome/tools-list.js +1 -41
- package/lib/chrome/tools-list.js.map +1 -1
- package/lib/chrome/welcome-tips.d.ts +20 -7
- package/lib/chrome/welcome-tips.js +22 -41
- package/lib/chrome/welcome-tips.js.map +1 -1
- package/lib/commands/auth-ids.d.ts +25 -0
- package/lib/commands/auth-ids.js +27 -0
- package/lib/commands/auth-ids.js.map +1 -0
- package/lib/commands/auth-input.d.ts +34 -0
- package/lib/commands/auth-input.js +96 -0
- package/lib/commands/auth-input.js.map +1 -0
- package/lib/commands/auth-providers.d.ts +39 -0
- package/lib/commands/auth-providers.js +125 -0
- package/lib/commands/auth-providers.js.map +1 -0
- package/lib/commands/auth.d.ts +16 -22
- package/lib/commands/auth.js +346 -299
- package/lib/commands/auth.js.map +1 -1
- package/lib/commands/btw.d.ts +27 -0
- package/lib/commands/btw.js +59 -0
- package/lib/commands/btw.js.map +1 -0
- package/lib/commands/diff.js +30 -5
- package/lib/commands/diff.js.map +1 -1
- package/lib/commands/export.d.ts +1 -1
- package/lib/commands/export.js +5 -3
- package/lib/commands/export.js.map +1 -1
- package/lib/commands/files.d.ts +17 -1
- package/lib/commands/files.js +69 -61
- package/lib/commands/files.js.map +1 -1
- package/lib/commands/integrations.js +18 -6
- package/lib/commands/integrations.js.map +1 -1
- package/lib/commands/jobs.d.ts +19 -4
- package/lib/commands/jobs.js +50 -7
- package/lib/commands/jobs.js.map +1 -1
- package/lib/commands/model.js +109 -76
- package/lib/commands/model.js.map +1 -1
- package/lib/commands/permission.js +46 -13
- package/lib/commands/permission.js.map +1 -1
- package/lib/commands/plugins.js +196 -42
- package/lib/commands/plugins.js.map +1 -1
- package/lib/commands/schedule.d.ts +29 -0
- package/lib/commands/schedule.js +228 -0
- package/lib/commands/schedule.js.map +1 -0
- package/lib/commands/session-configuration.d.ts +1 -1
- package/lib/commands/session-configuration.js +29 -28
- package/lib/commands/session-configuration.js.map +1 -1
- package/lib/commands/session.js +75 -80
- package/lib/commands/session.js.map +1 -1
- package/lib/commands/terminal.d.ts +24 -4
- package/lib/commands/terminal.js +38 -94
- package/lib/commands/terminal.js.map +1 -1
- package/lib/commands/trajectory.js +4 -1
- package/lib/commands/trajectory.js.map +1 -1
- package/lib/definition.d.ts +150 -11
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/lib/input/actions.d.ts +51 -0
- package/lib/input/actions.js +120 -0
- package/lib/input/actions.js.map +1 -0
- package/lib/input/editor-discovery.js +2 -2
- package/lib/input/editor-discovery.js.map +1 -1
- package/lib/input/editor.d.ts +0 -10
- package/lib/input/editor.js +11 -9
- package/lib/input/editor.js.map +1 -1
- package/lib/input/image-paste.d.ts +19 -1
- package/lib/input/image-paste.js +82 -1
- package/lib/input/image-paste.js.map +1 -1
- package/lib/input/keybindings-config.d.ts +15 -4
- package/lib/input/keybindings-config.js +57 -35
- package/lib/input/keybindings-config.js.map +1 -1
- package/lib/input/keys.d.ts +18 -0
- package/lib/input/keys.js +48 -3
- package/lib/input/keys.js.map +1 -1
- package/lib/input/wheel-scroll.d.ts +46 -0
- package/lib/input/wheel-scroll.js +69 -0
- package/lib/input/wheel-scroll.js.map +1 -0
- package/lib/prompt-intents.d.ts +182 -0
- package/lib/prompt-intents.js +127 -0
- package/lib/prompt-intents.js.map +1 -0
- package/lib/runtime/agent-profile.d.ts +5 -0
- package/lib/runtime/agent-profile.js +12 -0
- package/lib/runtime/agent-profile.js.map +1 -1
- package/lib/runtime/approval-rules.d.ts +59 -0
- package/lib/runtime/approval-rules.js +179 -0
- package/lib/runtime/approval-rules.js.map +1 -0
- package/lib/runtime/btw-policy.d.ts +7 -0
- package/lib/runtime/btw-policy.js +18 -0
- package/lib/runtime/btw-policy.js.map +1 -0
- package/lib/runtime/btw.d.ts +47 -0
- package/lib/runtime/btw.js +200 -0
- package/lib/runtime/btw.js.map +1 -0
- package/lib/runtime/cancellable.d.ts +75 -0
- package/lib/runtime/cancellable.js +76 -0
- package/lib/runtime/cancellable.js.map +1 -0
- package/lib/runtime/composer-images.js +5 -4
- package/lib/runtime/composer-images.js.map +1 -1
- package/lib/runtime/composer-shell.d.ts +30 -0
- package/lib/runtime/composer-shell.js +150 -0
- package/lib/runtime/composer-shell.js.map +1 -0
- package/lib/runtime/file-recovery-local.d.ts +73 -0
- package/lib/runtime/file-recovery-local.js +351 -0
- package/lib/runtime/file-recovery-local.js.map +1 -0
- package/lib/runtime/file-recovery-recording.d.ts +23 -0
- package/lib/runtime/file-recovery-recording.js +131 -0
- package/lib/runtime/file-recovery-recording.js.map +1 -0
- package/lib/runtime/file-recovery-review.d.ts +3 -0
- package/lib/runtime/file-recovery-review.js +130 -0
- package/lib/runtime/file-recovery-review.js.map +1 -0
- package/lib/runtime/file-recovery-store.d.ts +83 -0
- package/lib/runtime/file-recovery-store.js +329 -0
- package/lib/runtime/file-recovery-store.js.map +1 -0
- package/lib/runtime/file-recovery-worker.d.ts +22 -0
- package/lib/runtime/file-recovery-worker.js +153 -0
- package/lib/runtime/file-recovery-worker.js.map +1 -0
- package/lib/runtime/file-review.d.ts +6 -3
- package/lib/runtime/file-review.js +44 -7
- package/lib/runtime/file-review.js.map +1 -1
- package/lib/runtime/herdr-agent.d.ts +4 -13
- package/lib/runtime/herdr-agent.js +19 -63
- package/lib/runtime/herdr-agent.js.map +1 -1
- package/lib/runtime/plain-tui.d.ts +2 -0
- package/lib/runtime/plain-tui.js +7 -1
- package/lib/runtime/plain-tui.js.map +1 -1
- package/lib/runtime/plugin-settings.d.ts +25 -2
- package/lib/runtime/plugin-settings.js +329 -31
- package/lib/runtime/plugin-settings.js.map +1 -1
- package/lib/runtime/provider-local.d.ts +81 -24
- package/lib/runtime/provider-local.js +1807 -608
- package/lib/runtime/provider-local.js.map +1 -1
- package/lib/runtime/runner.js +52 -12
- package/lib/runtime/runner.js.map +1 -1
- package/lib/runtime/session-archive.d.ts +6 -0
- package/lib/runtime/session-archive.js +129 -3
- package/lib/runtime/session-archive.js.map +1 -1
- package/lib/runtime/session-runtime.js +1 -1
- package/lib/runtime/session-runtime.js.map +1 -1
- package/lib/runtime/settings-preferences.d.ts +5 -0
- package/lib/runtime/settings-preferences.js +34 -0
- package/lib/runtime/settings-preferences.js.map +1 -0
- package/lib/runtime/startup-notices.js +2 -0
- package/lib/runtime/startup-notices.js.map +1 -1
- package/lib/runtime/terminal-modes.d.ts +47 -0
- package/lib/runtime/terminal-modes.js +86 -0
- package/lib/runtime/terminal-modes.js.map +1 -0
- package/lib/runtime/terminal-notifications.d.ts +3 -5
- package/lib/runtime/terminal-notifications.js +4 -8
- package/lib/runtime/terminal-notifications.js.map +1 -1
- package/lib/runtime/terminal-probe.d.ts +36 -0
- package/lib/runtime/terminal-probe.js +37 -0
- package/lib/runtime/terminal-probe.js.map +1 -0
- package/lib/runtime/terminal-session.d.ts +57 -0
- package/lib/runtime/terminal-session.js +164 -0
- package/lib/runtime/terminal-session.js.map +1 -0
- package/lib/runtime/terminal-state.d.ts +29 -0
- package/lib/runtime/terminal-state.js +62 -0
- package/lib/runtime/terminal-state.js.map +1 -0
- package/lib/runtime/tool-presentation.js +50 -14
- package/lib/runtime/tool-presentation.js.map +1 -1
- package/lib/session/external-agents.d.ts +5 -0
- package/lib/session/external-agents.js +27 -0
- package/lib/session/external-agents.js.map +1 -0
- package/lib/session/feature-toggles.d.ts +1 -1
- package/lib/session/feature-toggles.js +1 -1
- package/lib/session/interaction-adapter.d.ts +1 -6
- package/lib/session/interaction-adapter.js +95 -18
- package/lib/session/interaction-adapter.js.map +1 -1
- package/lib/session/message-queue.js +1 -1
- package/lib/session/message-queue.js.map +1 -1
- package/lib/session/model-readiness.d.ts +16 -0
- package/lib/session/model-readiness.js +43 -0
- package/lib/session/model-readiness.js.map +1 -0
- package/lib/session/package-metadata.d.ts +8 -0
- package/lib/session/package-metadata.js +8 -0
- package/lib/session/package-metadata.js.map +1 -1
- package/lib/session/project-context.d.ts +5 -0
- package/lib/session/project-context.js +36 -0
- package/lib/session/project-context.js.map +1 -1
- package/lib/session/session-controller.d.ts +10 -13
- package/lib/session/session-controller.js +183 -98
- package/lib/session/session-controller.js.map +1 -1
- package/lib/session/session-library.js +2 -0
- package/lib/session/session-library.js.map +1 -1
- package/lib/session/session-tree.d.ts +3 -0
- package/lib/session/session-tree.js +3 -1
- package/lib/session/session-tree.js.map +1 -1
- package/lib/session/subagent-roster.d.ts +2 -1
- package/lib/session/subagent-roster.js +24 -3
- package/lib/session/subagent-roster.js.map +1 -1
- package/lib/session/subagent-tracker.d.ts +6 -2
- package/lib/session/subagent-tracker.js +49 -2
- package/lib/session/subagent-tracker.js.map +1 -1
- package/lib/session/tui-settings.d.ts +47 -13
- package/lib/session/tui-settings.js +76 -34
- package/lib/session/tui-settings.js.map +1 -1
- package/lib/session/working-directory.d.ts +4 -0
- package/lib/session/working-directory.js +4 -0
- package/lib/session/working-directory.js.map +1 -0
- package/lib/session/worktrees.d.ts +13 -0
- package/lib/session/worktrees.js +49 -0
- package/lib/session/worktrees.js.map +1 -0
- package/lib/views/approval-rule.d.ts +3 -0
- package/lib/views/approval-rule.js +4 -0
- package/lib/views/approval-rule.js.map +1 -0
- package/lib/views/at-complete.d.ts +8 -0
- package/lib/views/at-complete.js +64 -2
- package/lib/views/at-complete.js.map +1 -1
- package/lib/views/autocomplete.d.ts +22 -2
- package/lib/views/autocomplete.js +70 -40
- package/lib/views/autocomplete.js.map +1 -1
- package/lib/views/copy-targets.d.ts +13 -14
- package/lib/views/copy-targets.js +42 -54
- package/lib/views/copy-targets.js.map +1 -1
- package/lib/views/document-reader.js +18 -5
- package/lib/views/document-reader.js.map +1 -1
- package/lib/views/event-views.d.ts +1 -1
- package/lib/views/event-views.js +60 -6
- package/lib/views/event-views.js.map +1 -1
- package/lib/views/file-actions.d.ts +12 -0
- package/lib/views/file-actions.js +72 -0
- package/lib/views/file-actions.js.map +1 -0
- package/lib/views/history-search.js +15 -21
- package/lib/views/history-search.js.map +1 -1
- package/lib/views/hotkeys.d.ts +8 -1
- package/lib/views/hotkeys.js +53 -26
- package/lib/views/hotkeys.js.map +1 -1
- package/lib/views/list-navigation.d.ts +62 -0
- package/lib/views/list-navigation.js +73 -0
- package/lib/views/list-navigation.js.map +1 -0
- package/lib/views/list-view.d.ts +99 -0
- package/lib/views/list-view.js +165 -0
- package/lib/views/list-view.js.map +1 -0
- package/lib/views/prompt-selector.d.ts +34 -4
- package/lib/views/prompt-selector.js +270 -134
- package/lib/views/prompt-selector.js.map +1 -1
- package/lib/views/session-files.d.ts +1 -2
- package/lib/views/session-files.js +1 -11
- package/lib/views/session-files.js.map +1 -1
- package/lib/views/settings-list.d.ts +51 -10
- package/lib/views/settings-list.js +267 -315
- package/lib/views/settings-list.js.map +1 -1
- package/lib/views/trajectory-timeline.d.ts +72 -0
- package/lib/views/trajectory-timeline.js +133 -0
- package/lib/views/trajectory-timeline.js.map +1 -0
- package/lib/views/trajectory.d.ts +15 -4
- package/lib/views/trajectory.js +557 -123
- package/lib/views/trajectory.js.map +1 -1
- package/lib/views/transcript-export.js +9 -0
- package/lib/views/transcript-export.js.map +1 -1
- package/lib/views/transcript-render.d.ts +97 -25
- package/lib/views/transcript-render.js +542 -170
- package/lib/views/transcript-render.js.map +1 -1
- package/lib/views/transcript-search.d.ts +39 -6
- package/lib/views/transcript-search.js +46 -11
- package/lib/views/transcript-search.js.map +1 -1
- package/lib/views/transcript-types.d.ts +63 -5
- package/lib/views/transcript-types.js +23 -11
- package/lib/views/transcript-types.js.map +1 -1
- package/package.json +95 -65
- package/lib/commands/loop.d.ts +0 -52
- package/lib/commands/loop.js +0 -307
- package/lib/commands/loop.js.map +0 -1
- package/lib/commands/steer.d.ts +0 -5
- package/lib/commands/steer.js +0 -24
- package/lib/commands/steer.js.map +0 -1
- package/lib/runtime/terminal-reader.d.ts +0 -11
- package/lib/runtime/terminal-reader.js +0 -87
- package/lib/runtime/terminal-reader.js.map +0 -1
- package/lib/views/agent-hub.d.ts +0 -32
- package/lib/views/agent-hub.js +0 -206
- package/lib/views/agent-hub.js.map +0 -1
- package/lib/views/copy-selector.d.ts +0 -51
- package/lib/views/copy-selector.js +0 -139
- package/lib/views/copy-selector.js.map +0 -1
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/** Durable side consultations over the public Harness Agent and fork seams. */
|
|
2
|
+
import { Service } from '@deepseek-ai/cordis';
|
|
3
|
+
import { randomUUID, createHash } from 'node:crypto';
|
|
4
|
+
import { mkdirSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync } from 'node:fs';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import { Session, SessionId, SessionLogOffset, buildForkSeed, SESSION_FORMAT_VERSION } from '@deepseek-ai/dsh-session';
|
|
7
|
+
import { createUserMessage } from '@deepseek-ai/dsh-llm';
|
|
8
|
+
import { deriveTurnTokenUsage } from '@deepseek-ai/dsh-token-meter/client';
|
|
9
|
+
import { configureAgentContext } from "./agent-profile.js";
|
|
10
|
+
import { BTW_PRESET } from "./btw-policy.js";
|
|
11
|
+
import { currentWorkingDirectory } from "../session/working-directory.js";
|
|
12
|
+
import { blocksText } from "../session/content-text.js";
|
|
13
|
+
import { readColdSessionLog } from '@deepseek-ai/dsh-session-query';
|
|
14
|
+
/** Preserve the exact source prefix, then select the child's independent world. */
|
|
15
|
+
export function btwSeed(parent, id, cwd) {
|
|
16
|
+
const prefix = parent.snapshotEvents();
|
|
17
|
+
const seed = prefix.length === 0 ? [] : buildForkSeed(prefix, prefix.at(-1).seq);
|
|
18
|
+
const snapshot = Session.create(id, seed, { id, version: SESSION_FORMAT_VERSION, createdAt: Date.now(), cwd,
|
|
19
|
+
parentSession: parent.id, isSeeded: true, agentPreset: BTW_PRESET }, SessionLogOffset(prefix.length));
|
|
20
|
+
snapshot.append('agent-preset/selected', { agentPreset: BTW_PRESET });
|
|
21
|
+
return snapshot.snapshotEvents();
|
|
22
|
+
}
|
|
23
|
+
function lastTurn(events) {
|
|
24
|
+
const at = events.findLastIndex(event => event.type === 'turn/start');
|
|
25
|
+
return at < 0 ? [] : events.slice(at);
|
|
26
|
+
}
|
|
27
|
+
export class BtwService extends Service {
|
|
28
|
+
static inject = ['agents', 'agentPresets', 'sessions', 'permissionPresets'];
|
|
29
|
+
ownerContext;
|
|
30
|
+
stateDir;
|
|
31
|
+
topics = new Map();
|
|
32
|
+
active = new Map();
|
|
33
|
+
tasks = new Set();
|
|
34
|
+
closing = new AbortController();
|
|
35
|
+
constructor(ctx, config = {}) {
|
|
36
|
+
super(ctx, 'omdshBtw');
|
|
37
|
+
this.ownerContext = ctx;
|
|
38
|
+
this.stateDir = config.stateDir;
|
|
39
|
+
if (this.stateDir !== undefined) {
|
|
40
|
+
let names;
|
|
41
|
+
try {
|
|
42
|
+
names = readdirSync(this.stateDir);
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
if (error.code !== 'ENOENT')
|
|
46
|
+
throw error;
|
|
47
|
+
names = [];
|
|
48
|
+
}
|
|
49
|
+
for (const name of names.filter(name => name.endsWith('.json'))) {
|
|
50
|
+
const topic = JSON.parse(readFileSync(join(this.stateDir, name), 'utf8'));
|
|
51
|
+
if (!validTopic(topic))
|
|
52
|
+
throw new Error(`Invalid side consultation record: ${name}`);
|
|
53
|
+
this.topics.set(topic.sessionId, { ...topic, status: topic.status === 'running' ? 'cancelled' : topic.status });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
ctx.on('agent/disposed', ({ agent }) => { this.active.get(agent)?.abort(new Error('The parent session was closed.')); });
|
|
57
|
+
ctx.effect(() => async () => { this.closing.abort(new Error('Side consultations unloaded.')); await Promise.allSettled(this.tasks); }, 'side consultation lifetime');
|
|
58
|
+
}
|
|
59
|
+
list(parent) {
|
|
60
|
+
return [...this.topics.values()].filter(topic => topic.parentSessionId === parent.id)
|
|
61
|
+
.sort((left, right) => right.updatedAt - left.updatedAt).map(topic => ({ ...topic }));
|
|
62
|
+
}
|
|
63
|
+
async read(parent, id, signal) {
|
|
64
|
+
const topic = this.topics.get(id);
|
|
65
|
+
if (topic?.parentSessionId !== parent.id)
|
|
66
|
+
throw new Error('That side consultation belongs to another session or is unavailable.');
|
|
67
|
+
const persistence = this.ownerContext.get('sessionPersistence');
|
|
68
|
+
if (persistence === undefined)
|
|
69
|
+
throw new Error('Session persistence is required to read a saved side consultation.');
|
|
70
|
+
const log = await readColdSessionLog(persistence, SessionId(id), signal);
|
|
71
|
+
if (log.header.parentSession !== parent.id || log.inheritedEventCount !== topic.prefixEvents)
|
|
72
|
+
throw new Error('The saved consultation no longer matches its parent snapshot.');
|
|
73
|
+
return log.events.slice(log.inheritedEventCount).flatMap(event => event.type === 'user/message' && event.data.source.kind === 'user'
|
|
74
|
+
? ['### Question\n\n' + blocksText(event.data.content)]
|
|
75
|
+
: event.type === 'assistant/message' ? [blocksText(event.data.message.content)] : []).join('\n\n');
|
|
76
|
+
}
|
|
77
|
+
consult(parent, question, selection, signal, update, topicId) {
|
|
78
|
+
if (this.active.has(parent))
|
|
79
|
+
throw new Error('A side consultation is already running for this session.');
|
|
80
|
+
if (question.trim() === '')
|
|
81
|
+
throw new Error('Enter a side question.');
|
|
82
|
+
if (this.ownerContext.get('sessionPersistence') === undefined)
|
|
83
|
+
throw new Error('Session persistence is required for side consultations.');
|
|
84
|
+
const abort = new AbortController();
|
|
85
|
+
this.active.set(parent, abort);
|
|
86
|
+
const task = this.runQuestion(parent, question, selection, AbortSignal.any([signal, abort.signal, this.closing.signal]), update, topicId)
|
|
87
|
+
.finally(() => { this.active.delete(parent); this.tasks.delete(task); });
|
|
88
|
+
this.tasks.add(task);
|
|
89
|
+
return task;
|
|
90
|
+
}
|
|
91
|
+
async runQuestion(parent, question, selection, signal, update, topicId) {
|
|
92
|
+
const ctx = this.ownerContext;
|
|
93
|
+
const existing = topicId === undefined ? undefined : this.topics.get(topicId);
|
|
94
|
+
if (topicId !== undefined && existing?.parentSessionId !== parent.id)
|
|
95
|
+
throw new Error('That side consultation belongs to another session or is unavailable.');
|
|
96
|
+
const id = SessionId(existing?.sessionId ?? 'session-' + randomUUID());
|
|
97
|
+
const prefixEvents = existing?.prefixEvents ?? parent.session.snapshotEvents().length;
|
|
98
|
+
let topic = existing === undefined ? { version: 1, sessionId: id, parentSessionId: parent.id, prefixEvents,
|
|
99
|
+
question, createdAt: Date.now(), updatedAt: Date.now(), status: 'running' } : { ...existing, status: 'running', updatedAt: Date.now() };
|
|
100
|
+
let handle, text = '', attempt = '';
|
|
101
|
+
const ref = { current: { ...selection }, assembled: undefined };
|
|
102
|
+
const setup = async (agentCtx, agent) => {
|
|
103
|
+
const configured = await configureAgentContext(agentCtx, agent, ref);
|
|
104
|
+
if (configured.agentPreset !== BTW_PRESET)
|
|
105
|
+
throw new Error('The saved consultation no longer uses its tool-free composition.');
|
|
106
|
+
};
|
|
107
|
+
let offStream;
|
|
108
|
+
const cancel = () => { handle?.agent.cancel({ kind: 'user' }); };
|
|
109
|
+
signal.addEventListener('abort', cancel, { once: true });
|
|
110
|
+
try {
|
|
111
|
+
signal.throwIfAborted();
|
|
112
|
+
if (existing === undefined) {
|
|
113
|
+
const cwd = currentWorkingDirectory(ctx, parent.session);
|
|
114
|
+
const seed = btwSeed(parent.session, id, cwd);
|
|
115
|
+
handle = await ctx.get('agents').create({ sessionId: id, seed, inheritedEventCount: SessionLogOffset(prefixEvents),
|
|
116
|
+
meta: { cwd, parentSession: parent.id, isSeeded: true, agentPreset: BTW_PRESET }, agentOptions: { ...selection, maxTokens: 8192 }, signal, setup });
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
const stored = await ctx.get('sessionPersistence').stat(id, { signal });
|
|
120
|
+
if (stored === undefined || stored.header.parentSession !== parent.id)
|
|
121
|
+
throw new Error('The saved consultation no longer matches its parent session.');
|
|
122
|
+
if (stored.formatStatus === 'migration-required')
|
|
123
|
+
throw new Error('This consultation requires session-format migration before it can continue.');
|
|
124
|
+
handle = await ctx.get('agents').resume({ resumeSessionId: id, agentOptions: { ...selection, maxTokens: 8192 }, signal, setup });
|
|
125
|
+
}
|
|
126
|
+
const agent = handle.agent;
|
|
127
|
+
signal.throwIfAborted();
|
|
128
|
+
ctx.get('goals')?.disarm(agent);
|
|
129
|
+
const goal = ctx.get('goals')?.get(agent);
|
|
130
|
+
if (goal !== undefined)
|
|
131
|
+
ctx.get('goals').clear(agent, goal);
|
|
132
|
+
ctx.get('planMode')?.set(agent, false);
|
|
133
|
+
ctx.get('permissionPresets').set(agent.session, 'read-only');
|
|
134
|
+
if (existing === undefined)
|
|
135
|
+
ctx.get('sessionTitles')?.rename(agent.session, 'Side question: ' + question);
|
|
136
|
+
this.saveTopic(topic);
|
|
137
|
+
offStream = agent.ctx.on('agent/assistant-stream', ({ frame }) => {
|
|
138
|
+
if (frame.type === 'start') {
|
|
139
|
+
attempt = '';
|
|
140
|
+
update('');
|
|
141
|
+
}
|
|
142
|
+
if (frame.type === 'chunk' && frame.chunk.type === 'text-delta') {
|
|
143
|
+
attempt += frame.chunk.text;
|
|
144
|
+
update(attempt);
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
agent.followup(createUserMessage({ content: [{ type: 'text', text: question }], source: { kind: 'user' } }));
|
|
148
|
+
await agent.whenIdle();
|
|
149
|
+
const events = lastTurn(agent.session.ownEvents());
|
|
150
|
+
const message = events.findLast(event => event.type === 'assistant/message');
|
|
151
|
+
text = message?.type === 'assistant/message' ? blocksText(message.data.message.content) : attempt;
|
|
152
|
+
update(text);
|
|
153
|
+
const end = events.findLast(event => event.type === 'turn/end');
|
|
154
|
+
const reason = end?.type === 'turn/end' ? end.data.reason : undefined;
|
|
155
|
+
const error = reason?.kind === 'error' ? reason.error.message : reason?.kind === 'completed' || signal.aborted ? undefined : `Side question ended without completion (${reason?.kind ?? 'no turn'}).`;
|
|
156
|
+
topic = { ...topic, updatedAt: Date.now(), status: signal.aborted ? 'cancelled' : reason?.kind === 'completed' ? 'completed' : 'failed' };
|
|
157
|
+
await ctx.get('sessions').flush(agent.session);
|
|
158
|
+
this.saveTopic(topic);
|
|
159
|
+
const usage = deriveTurnTokenUsage(events);
|
|
160
|
+
return { topic, text, ...(usage === undefined ? {} : { usage }), ...(error === undefined ? {} : { error }) };
|
|
161
|
+
}
|
|
162
|
+
catch (error) {
|
|
163
|
+
topic = { ...topic, updatedAt: Date.now(), status: signal.aborted ? 'cancelled' : 'failed' };
|
|
164
|
+
if (handle !== undefined)
|
|
165
|
+
this.saveTopic(topic);
|
|
166
|
+
return { topic, text: text || attempt, ...(signal.aborted ? {} : { error: error instanceof Error ? error.message : String(error) }) };
|
|
167
|
+
}
|
|
168
|
+
finally {
|
|
169
|
+
signal.removeEventListener('abort', cancel);
|
|
170
|
+
offStream?.();
|
|
171
|
+
await handle?.dispose();
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
saveTopic(topic) {
|
|
175
|
+
if (this.stateDir !== undefined) {
|
|
176
|
+
mkdirSync(this.stateDir, { recursive: true });
|
|
177
|
+
const path = join(this.stateDir, createHash('sha256').update(topic.sessionId).digest('hex') + '.json');
|
|
178
|
+
const temp = path + '.' + randomUUID() + '.tmp';
|
|
179
|
+
try {
|
|
180
|
+
writeFileSync(temp, JSON.stringify(topic) + '\n', { mode: 0o600 });
|
|
181
|
+
renameSync(temp, path);
|
|
182
|
+
}
|
|
183
|
+
finally {
|
|
184
|
+
rmSync(temp, { force: true });
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
this.topics.set(topic.sessionId, topic);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
function validTopic(value) {
|
|
191
|
+
if (value === null || typeof value !== 'object')
|
|
192
|
+
return false;
|
|
193
|
+
const item = value;
|
|
194
|
+
return item.version === 1 && typeof item.sessionId === 'string' && typeof item.parentSessionId === 'string'
|
|
195
|
+
&& typeof item.question === 'string' && Number.isSafeInteger(item.prefixEvents) && item.prefixEvents >= 0
|
|
196
|
+
&& Number.isSafeInteger(item.createdAt) && Number.isSafeInteger(item.updatedAt)
|
|
197
|
+
&& ['running', 'completed', 'cancelled', 'failed'].includes(String(item.status));
|
|
198
|
+
}
|
|
199
|
+
export default BtwService;
|
|
200
|
+
//# sourceMappingURL=btw.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"btw.js","sourceRoot":"","sources":["../../src/runtime/btw.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One cancellable long operation with its own progress surface.
|
|
3
|
+
*
|
|
4
|
+
* A long command needs three things wired together, and every call site used to
|
|
5
|
+
* write the same wiring by hand:
|
|
6
|
+
*
|
|
7
|
+
* 1. a signal that combines the caller's own cancellation with the reader's;
|
|
8
|
+
* 2. a prompt that gives the reader a way to cancel — Enter, Escape, `Ctrl+C`;
|
|
9
|
+
* 3. a "finished" signal so that dismissing the prompt because the *work* ended
|
|
10
|
+
* is not mistaken for the reader cancelling the work.
|
|
11
|
+
*
|
|
12
|
+
* The third point is where hand-written copies went wrong: the prompt's promise
|
|
13
|
+
* settles both when the reader cancels and when the operation completes, so a
|
|
14
|
+
* naive `.then(() => controller.abort())` aborted the operation on its own
|
|
15
|
+
* success. Each site had to guard it with a second `finished` controller, and
|
|
16
|
+
* each site wrote that guard differently.
|
|
17
|
+
*
|
|
18
|
+
* `run()` owns the whole lifecycle: the body receives the combined signal, and
|
|
19
|
+
* the prompt is dismissed when the body settles either way.
|
|
20
|
+
*
|
|
21
|
+
* @module @agi-fans/dsh-tui/cancellable
|
|
22
|
+
*/
|
|
23
|
+
import type { TuiService } from '../definition.ts';
|
|
24
|
+
/** One progress surface the reader can cancel from. */
|
|
25
|
+
export interface CancellableProgress {
|
|
26
|
+
/** Title of the surface, such as `Creating worktree`. */
|
|
27
|
+
title: string;
|
|
28
|
+
/** The subject line: what is being done, in one row. */
|
|
29
|
+
question: string;
|
|
30
|
+
/** What cancelling does and does not undo. Shown under the subject. */
|
|
31
|
+
detail?: string;
|
|
32
|
+
/** Label of the cancel option. */
|
|
33
|
+
cancelLabel?: string;
|
|
34
|
+
/**
|
|
35
|
+
* Progress card, or a reading surface that updates while the work streams.
|
|
36
|
+
*
|
|
37
|
+
* A streaming operation (a side question's answer) needs a document the reader
|
|
38
|
+
* can watch and scroll; a file restore only needs to say what is happening. The
|
|
39
|
+
* lifecycle is identical, so both are one option rather than two helpers.
|
|
40
|
+
*/
|
|
41
|
+
presentation?: 'progress' | 'document';
|
|
42
|
+
/** Repaint the open document; read on each refresh. */
|
|
43
|
+
document?: () => string;
|
|
44
|
+
/**
|
|
45
|
+
* Whether to open the surface at all.
|
|
46
|
+
*
|
|
47
|
+
* The caller decides, because the three operations disagreed: a worktree and a
|
|
48
|
+
* side question are interactive-only, while a file restore reports progress even
|
|
49
|
+
* when the caller did not claim an interactive screen. Inferring this from
|
|
50
|
+
* `tui.interactive` silently dropped the restore's progress surface.
|
|
51
|
+
*/
|
|
52
|
+
surface: boolean;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Why a cancellable operation stopped early.
|
|
56
|
+
*
|
|
57
|
+
* The two are not the same event: a reader who presses cancel is watching, and
|
|
58
|
+
* knows the work stopped, while a superseded surface means the screen was taken
|
|
59
|
+
* by something else and the reader never saw the result. Callers that report a
|
|
60
|
+
* partial outcome treat them differently.
|
|
61
|
+
*/
|
|
62
|
+
export type CancellationReason = 'cancelled' | 'superseded';
|
|
63
|
+
/**
|
|
64
|
+
* Run one long operation behind a cancel surface.
|
|
65
|
+
*
|
|
66
|
+
* Returns `undefined` when the operation never started because the caller was
|
|
67
|
+
* already aborted, so callers can skip their success path without checking.
|
|
68
|
+
*
|
|
69
|
+
* A non-interactive host runs the body without a surface: there is no reader to
|
|
70
|
+
* cancel it, and opening a prompt would claim a screen the pipe does not have.
|
|
71
|
+
*/
|
|
72
|
+
export declare function cancellable<T>(tui: Pick<TuiService, 'interactive' | 'prompt'>, progress: CancellableProgress, parent: AbortSignal, run: (signal: AbortSignal) => Promise<T>): Promise<{
|
|
73
|
+
value: T;
|
|
74
|
+
reason: CancellationReason | undefined;
|
|
75
|
+
} | undefined>;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One cancellable long operation with its own progress surface.
|
|
3
|
+
*
|
|
4
|
+
* A long command needs three things wired together, and every call site used to
|
|
5
|
+
* write the same wiring by hand:
|
|
6
|
+
*
|
|
7
|
+
* 1. a signal that combines the caller's own cancellation with the reader's;
|
|
8
|
+
* 2. a prompt that gives the reader a way to cancel — Enter, Escape, `Ctrl+C`;
|
|
9
|
+
* 3. a "finished" signal so that dismissing the prompt because the *work* ended
|
|
10
|
+
* is not mistaken for the reader cancelling the work.
|
|
11
|
+
*
|
|
12
|
+
* The third point is where hand-written copies went wrong: the prompt's promise
|
|
13
|
+
* settles both when the reader cancels and when the operation completes, so a
|
|
14
|
+
* naive `.then(() => controller.abort())` aborted the operation on its own
|
|
15
|
+
* success. Each site had to guard it with a second `finished` controller, and
|
|
16
|
+
* each site wrote that guard differently.
|
|
17
|
+
*
|
|
18
|
+
* `run()` owns the whole lifecycle: the body receives the combined signal, and
|
|
19
|
+
* the prompt is dismissed when the body settles either way.
|
|
20
|
+
*
|
|
21
|
+
* @module @agi-fans/dsh-tui/cancellable
|
|
22
|
+
*/
|
|
23
|
+
/**
|
|
24
|
+
* Run one long operation behind a cancel surface.
|
|
25
|
+
*
|
|
26
|
+
* Returns `undefined` when the operation never started because the caller was
|
|
27
|
+
* already aborted, so callers can skip their success path without checking.
|
|
28
|
+
*
|
|
29
|
+
* A non-interactive host runs the body without a surface: there is no reader to
|
|
30
|
+
* cancel it, and opening a prompt would claim a screen the pipe does not have.
|
|
31
|
+
*/
|
|
32
|
+
export async function cancellable(tui, progress, parent, run) {
|
|
33
|
+
if (parent.aborted)
|
|
34
|
+
return undefined;
|
|
35
|
+
const cancel = new AbortController();
|
|
36
|
+
const finished = new AbortController();
|
|
37
|
+
const signal = AbortSignal.any([parent, cancel.signal]);
|
|
38
|
+
let superseded = false;
|
|
39
|
+
const surface = progress.surface
|
|
40
|
+
? tui.prompt({
|
|
41
|
+
title: progress.title,
|
|
42
|
+
question: progress.question,
|
|
43
|
+
...(progress.detail === undefined ? {} : { detail: progress.detail }),
|
|
44
|
+
...(progress.document === undefined ? {} : { refreshDocument: progress.document }),
|
|
45
|
+
// Both surfaces report cancellation through the `interrupt` hook instead of
|
|
46
|
+
// the returned value, because the reader may be reading while it runs; the
|
|
47
|
+
// option list is only the keyboard affordance.
|
|
48
|
+
presentation: progress.presentation ?? 'progress',
|
|
49
|
+
options: [{ label: progress.cancelLabel ?? 'Cancel', value: 'cancel' }],
|
|
50
|
+
allowCustom: false,
|
|
51
|
+
notify: false,
|
|
52
|
+
signal: AbortSignal.any([parent, finished.signal]),
|
|
53
|
+
interrupt: () => { cancel.abort(); return true; },
|
|
54
|
+
// A superseding prompt takes the screen, which ends this operation rather
|
|
55
|
+
// than letting it run behind a surface nobody can see.
|
|
56
|
+
onSuperseded: () => { superseded = true; cancel.abort(); },
|
|
57
|
+
})
|
|
58
|
+
.then(() => { if (!finished.signal.aborted)
|
|
59
|
+
cancel.abort(); }, () => { cancel.abort(); })
|
|
60
|
+
: Promise.resolve();
|
|
61
|
+
try {
|
|
62
|
+
const value = await run(signal);
|
|
63
|
+
// `cancel` also fires when the parent aborted; only a stop the reader caused
|
|
64
|
+
// is reported as one.
|
|
65
|
+
const stopped = cancel.signal.aborted && !parent.aborted;
|
|
66
|
+
return { value, reason: stopped ? (superseded ? 'superseded' : 'cancelled') : undefined };
|
|
67
|
+
}
|
|
68
|
+
finally {
|
|
69
|
+
// Dismiss the surface exactly once, whether the work finished, failed, or
|
|
70
|
+
// was cancelled. Aborting `finished` first is what tells the prompt's own
|
|
71
|
+
// `.then` that this dismissal is not a cancellation.
|
|
72
|
+
finished.abort();
|
|
73
|
+
await surface;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=cancellable.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cancellable.js","sourceRoot":"","sources":["../../src/runtime/cancellable.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AA6CH;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,GAA+C,EAC/C,QAA6B,EAC7B,MAAmB,EACnB,GAAwC;IAExC,IAAI,MAAM,CAAC,OAAO;QAAE,OAAO,SAAS,CAAA;IACpC,MAAM,MAAM,GAAG,IAAI,eAAe,EAAE,CAAA;IACpC,MAAM,QAAQ,GAAG,IAAI,eAAe,EAAE,CAAA;IACtC,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAA;IACvD,IAAI,UAAU,GAAG,KAAK,CAAA;IAEtB,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO;QAC9B,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC;YACX,KAAK,EAAE,QAAQ,CAAC,KAAK;YACrB,QAAQ,EAAE,QAAQ,CAAC,QAAQ;YAC3B,GAAG,CAAC,QAAQ,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC;YACrE,GAAG,CAAC,QAAQ,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,QAAQ,CAAC,QAAQ,EAAE,CAAC;YAClF,4EAA4E;YAC5E,2EAA2E;YAC3E,+CAA+C;YAC/C,YAAY,EAAE,QAAQ,CAAC,YAAY,IAAI,UAAU;YACjD,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC,WAAW,IAAI,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;YACvE,WAAW,EAAE,KAAK;YAClB,MAAM,EAAE,KAAK;YACb,MAAM,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;YAClD,SAAS,EAAE,GAAG,EAAE,GAAG,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,OAAO,IAAI,CAAA,CAAC,CAAC;YAChD,0EAA0E;YAC1E,uDAAuD;YACvD,YAAY,EAAE,GAAG,EAAE,GAAG,UAAU,GAAG,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,CAAA,CAAC,CAAC;SAC1D,CAAC;aACC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO;YAAE,MAAM,CAAC,KAAK,EAAE,CAAA,CAAC,CAAC,EAAE,GAAG,EAAE,GAAG,MAAM,CAAC,KAAK,EAAE,CAAA,CAAC,CAAC,CAAC;QACzF,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,CAAA;IAErB,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC,CAAA;QAC/B,6EAA6E;QAC7E,sBAAsB;QACtB,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,IAAI,CAAC,MAAM,CAAC,OAAO,CAAA;QACxD,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,CAAA;IAC3F,CAAC;YAAS,CAAC;QACT,0EAA0E;QAC1E,0EAA0E;QAC1E,qDAAqD;QACrD,QAAQ,CAAC,KAAK,EAAE,CAAA;QAChB,MAAM,OAAO,CAAA;IACf,CAAC;AACH,CAAC"}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* decoding, and the prompt/settings/search branches that decide whether a
|
|
8
8
|
* paste is an image at all stay in the provider.
|
|
9
9
|
*/
|
|
10
|
-
import { imageMarker, probeImageDimensions } from "../input/image-paste.js";
|
|
10
|
+
import { imageMarker, probeImageDimensions, submissionDraftText } from "../input/image-paste.js";
|
|
11
11
|
export class ComposerImages {
|
|
12
12
|
#deps;
|
|
13
13
|
#images = [];
|
|
@@ -139,15 +139,16 @@ export class ComposerImages {
|
|
|
139
139
|
const image = currentImages[index];
|
|
140
140
|
rebasedCurrent = rebasedCurrent.replaceAll(imageMarker(index, image), imageMarker(index + submission.images.length, image));
|
|
141
141
|
}
|
|
142
|
-
const
|
|
142
|
+
const restored = submissionDraftText(submission);
|
|
143
|
+
const separator = restored !== '' && rebasedCurrent !== '' ? '\n' : '';
|
|
143
144
|
this.#images = [...submission.images.map(image => ({ ...image })), ...currentImages];
|
|
144
|
-
editor.replaceText(
|
|
145
|
+
editor.replaceText(restored + separator + rebasedCurrent);
|
|
145
146
|
}
|
|
146
147
|
/** Replace drafts and text with a queued/edited submission. */
|
|
147
148
|
replace(submission) {
|
|
148
149
|
this.#revision++;
|
|
149
150
|
this.#images = submission.images.map(image => ({ ...image }));
|
|
150
|
-
this.#deps.editor.setText(submission
|
|
151
|
+
this.#deps.editor.setText(submissionDraftText(submission));
|
|
151
152
|
}
|
|
152
153
|
clear() {
|
|
153
154
|
this.#revision++;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"composer-images.js","sourceRoot":"","sources":["../../src/runtime/composer-images.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAA;
|
|
1
|
+
{"version":3,"file":"composer-images.js","sourceRoot":"","sources":["../../src/runtime/composer-images.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/** Composer shortcut over the same owner-scoped Shell used by `/terminal`. */
|
|
2
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
3
|
+
import type { Agent } from '@deepseek-ai/dsh-agent';
|
|
4
|
+
import type { TuiSubmission } from '../definition.ts';
|
|
5
|
+
/** Only deliberate, attachment-free composer input may execute a Shell line. */
|
|
6
|
+
export declare function composerShellCommand(submission: TuiSubmission): string | undefined;
|
|
7
|
+
/** `!!<command>` runs in the session Shell and shares its output to the conversation. */
|
|
8
|
+
export declare function composerShellShare(submission: TuiSubmission): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Run one Shell line in the session Shell, streaming its output to a live card.
|
|
11
|
+
*
|
|
12
|
+
* The card is the surface. There is no reader to enter and leave: the command
|
|
13
|
+
* streams into the transcript where the reader asked for it, and finishes when
|
|
14
|
+
* the command finishes. `!` keeps the output outside the model's context; `!!`
|
|
15
|
+
* shares it once the command settles.
|
|
16
|
+
*/
|
|
17
|
+
export declare function runComposerShell(ctx: Context, owner: Agent, command: string, upstream: AbortSignal, options?: {
|
|
18
|
+
share?: boolean;
|
|
19
|
+
}): Promise<void>;
|
|
20
|
+
/**
|
|
21
|
+
* Close the composer Shell when it is not running anything.
|
|
22
|
+
*
|
|
23
|
+
* The Shell is reused across `!` lines so a directory change or an environment
|
|
24
|
+
* variable survives, which is the whole point of it. That reuse also pins the
|
|
25
|
+
* session to the Access level it was created in: the Harness refuses to change
|
|
26
|
+
* sandbox mode while a persistent terminal is open, and a reader driving `!`
|
|
27
|
+
* cards would otherwise be stuck on one level forever with no way out. A Shell
|
|
28
|
+
* with a command still running is left alone — closing it would kill work.
|
|
29
|
+
*/
|
|
30
|
+
export declare function closeIdleComposerShell(ctx: Context, owner: Agent): Promise<boolean>;
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/** Composer shortcut over the same owner-scoped Shell used by `/terminal`. */
|
|
2
|
+
import { shareTerminalOutputToConversation } from "../commands/terminal.js";
|
|
3
|
+
import { currentWorkingDirectory } from "../session/working-directory.js";
|
|
4
|
+
/** Only deliberate, attachment-free composer input may execute a Shell line. */
|
|
5
|
+
export function composerShellCommand(submission) {
|
|
6
|
+
if (submission.literal || submission.images.length > 0 || submission.files?.length)
|
|
7
|
+
return undefined;
|
|
8
|
+
if (!submission.text.startsWith('!'))
|
|
9
|
+
return undefined;
|
|
10
|
+
return submission.text.replace(/^!{1,2}/u, '').trim();
|
|
11
|
+
}
|
|
12
|
+
/** `!!<command>` runs in the session Shell and shares its output to the conversation. */
|
|
13
|
+
export function composerShellShare(submission) {
|
|
14
|
+
if (submission.literal || submission.images.length > 0 || submission.files?.length)
|
|
15
|
+
return false;
|
|
16
|
+
return submission.text.startsWith('!!');
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Run one Shell line in the session Shell, streaming its output to a live card.
|
|
20
|
+
*
|
|
21
|
+
* The card is the surface. There is no reader to enter and leave: the command
|
|
22
|
+
* streams into the transcript where the reader asked for it, and finishes when
|
|
23
|
+
* the command finishes. `!` keeps the output outside the model's context; `!!`
|
|
24
|
+
* shares it once the command settles.
|
|
25
|
+
*/
|
|
26
|
+
export async function runComposerShell(ctx, owner, command, upstream, options = {}) {
|
|
27
|
+
if (ctx.tui.interactive !== true)
|
|
28
|
+
throw new Error('Shell shortcuts require an interactive terminal.');
|
|
29
|
+
if (command === '')
|
|
30
|
+
throw new Error('Enter a command after ! to run it in the session Shell.');
|
|
31
|
+
const terminals = ctx.get('terminals');
|
|
32
|
+
if (terminals === undefined)
|
|
33
|
+
throw new Error('The terminal provider is not enabled. Enable the terminal plugins to run Shell commands.');
|
|
34
|
+
if (ctx.tui.shellCommand === undefined)
|
|
35
|
+
throw new Error('This deployment cannot run Shell lines from the composer.');
|
|
36
|
+
const interruption = new AbortController();
|
|
37
|
+
const signal = AbortSignal.any([upstream, interruption.signal]);
|
|
38
|
+
signal.throwIfAborted();
|
|
39
|
+
// Cordis service access is context-bound; proxy identity is not a registry
|
|
40
|
+
// identity. The registry already owns exact-Agent names and liveness.
|
|
41
|
+
const previous = terminals.list(owner).find(item => item.name === 'Composer shell' && item.type === 'shell');
|
|
42
|
+
let id = previous?.sessionId;
|
|
43
|
+
if (id !== undefined && previous?.status.kind !== 'running') {
|
|
44
|
+
await terminals.kill(owner, id, 'Composer shell exited');
|
|
45
|
+
id = undefined;
|
|
46
|
+
}
|
|
47
|
+
if (id === undefined) {
|
|
48
|
+
const session = await terminals.spawn(owner, { type: 'shell', name: 'Composer shell', cwd: currentWorkingDirectory(ctx, owner.session) }, signal);
|
|
49
|
+
id = session.sessionId;
|
|
50
|
+
}
|
|
51
|
+
signal.throwIfAborted();
|
|
52
|
+
const sessionId = id;
|
|
53
|
+
let produced = '';
|
|
54
|
+
await ctx.tui.shellCommand({
|
|
55
|
+
command,
|
|
56
|
+
signal,
|
|
57
|
+
abort: () => { interruption.abort(new Error('Shell interrupted by user')); },
|
|
58
|
+
...(options.share === true ? { share: true } : {}),
|
|
59
|
+
run: async (onOutput, runSignal) => {
|
|
60
|
+
// The Shell is persistent, so its scrollback holds everything typed into it
|
|
61
|
+
// across earlier `!` lines — and the shell's own prompt echoes each line.
|
|
62
|
+
// Reading the whole snapshot into the card would repeat the command under
|
|
63
|
+
// our own `$ command` header and replay history from before this line ran.
|
|
64
|
+
// The line count is the boundary: everything above it belongs to an
|
|
65
|
+
// earlier line, everything below it is this command's output.
|
|
66
|
+
const baseline = terminals.read(owner, sessionId).totalLines;
|
|
67
|
+
const operation = terminals.startSend(owner, sessionId, { text: command, submit: true, signal: runSignal });
|
|
68
|
+
const since = () => {
|
|
69
|
+
const fresh = terminals.read(owner, sessionId).totalLines - baseline;
|
|
70
|
+
if (fresh <= 0)
|
|
71
|
+
return '';
|
|
72
|
+
return terminals.read(owner, sessionId, { offset: 0, count: fresh }).text;
|
|
73
|
+
};
|
|
74
|
+
let done = false;
|
|
75
|
+
const pump = () => {
|
|
76
|
+
if (done)
|
|
77
|
+
return;
|
|
78
|
+
onOutput(since());
|
|
79
|
+
};
|
|
80
|
+
const tick = setInterval(pump, SHELL_STREAM_TICK_MS);
|
|
81
|
+
tick.unref?.();
|
|
82
|
+
// Race the send against the abort. A provider is not obliged to settle
|
|
83
|
+
// `done` when a signal fires, and waiting on it would leave the card
|
|
84
|
+
// running after the reader already asked it to stop.
|
|
85
|
+
const aborted = new Promise((resolve) => {
|
|
86
|
+
if (runSignal.aborted)
|
|
87
|
+
resolve('aborted');
|
|
88
|
+
else
|
|
89
|
+
runSignal.addEventListener('abort', () => { resolve('aborted'); }, { once: true });
|
|
90
|
+
});
|
|
91
|
+
try {
|
|
92
|
+
const settled = await Promise.race([operation.done.then(() => 'done').catch(() => 'failed'), aborted]);
|
|
93
|
+
// A settled send means the line was delivered, not that the command
|
|
94
|
+
// exited; the card reports the delivery rather than inventing an exit
|
|
95
|
+
// code the Shell never gave us.
|
|
96
|
+
if (settled === 'done') {
|
|
97
|
+
// Publish the settled delta to the card as well: the tick that would
|
|
98
|
+
// have shown it may never fire, and the card must not stop on a frame
|
|
99
|
+
// older than the command.
|
|
100
|
+
produced = since();
|
|
101
|
+
if (produced !== '')
|
|
102
|
+
onOutput(produced);
|
|
103
|
+
return { output: produced };
|
|
104
|
+
}
|
|
105
|
+
// An interrupted line still needs the send cancelled and the shell
|
|
106
|
+
// signalled, or the command keeps running in the background while the
|
|
107
|
+
// card claims it stopped.
|
|
108
|
+
if (!operation.cancel() && terminals.list(owner).some(item => item.sessionId === sessionId && item.status.kind === 'running')) {
|
|
109
|
+
await terminals.signal(owner, sessionId, 'SIGINT');
|
|
110
|
+
}
|
|
111
|
+
return { output: since(), interrupted: true };
|
|
112
|
+
}
|
|
113
|
+
finally {
|
|
114
|
+
done = true;
|
|
115
|
+
clearInterval(tick);
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
if (options.share === true && !signal.aborted) {
|
|
120
|
+
// Share what this line produced, for the same reason the card shows that.
|
|
121
|
+
shareTerminalOutputToConversation(owner, `Shell · \`${command}\``, produced);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Close the composer Shell when it is not running anything.
|
|
126
|
+
*
|
|
127
|
+
* The Shell is reused across `!` lines so a directory change or an environment
|
|
128
|
+
* variable survives, which is the whole point of it. That reuse also pins the
|
|
129
|
+
* session to the Access level it was created in: the Harness refuses to change
|
|
130
|
+
* sandbox mode while a persistent terminal is open, and a reader driving `!`
|
|
131
|
+
* cards would otherwise be stuck on one level forever with no way out. A Shell
|
|
132
|
+
* with a command still running is left alone — closing it would kill work.
|
|
133
|
+
*/
|
|
134
|
+
export async function closeIdleComposerShell(ctx, owner) {
|
|
135
|
+
const terminals = ctx.get('terminals');
|
|
136
|
+
if (terminals === undefined)
|
|
137
|
+
return false;
|
|
138
|
+
// A persistent shell reports `running` for as long as it is alive, whether or
|
|
139
|
+
// not a command is in it, so "idle" cannot be read from that status. The
|
|
140
|
+
// composer Shell is closed unconditionally: it exists only to serve the last
|
|
141
|
+
// `!` line, and the reader is asking to change Access rather than to keep a
|
|
142
|
+
// session pinned to the old one.
|
|
143
|
+
const shells = terminals.list(owner).filter(item => item.name === 'Composer shell' && item.type === 'shell');
|
|
144
|
+
for (const item of shells)
|
|
145
|
+
await terminals.kill(owner, item.sessionId, 'Composer shell closed for an Access change');
|
|
146
|
+
return shells.length > 0;
|
|
147
|
+
}
|
|
148
|
+
/** How often the live card re-reads the terminal while a command writes. */
|
|
149
|
+
const SHELL_STREAM_TICK_MS = 120;
|
|
150
|
+
//# sourceMappingURL=composer-shell.js.map
|