@jmoyers/harness 0.1.8 → 0.1.10
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 +33 -155
- package/package.json +5 -1
- package/packages/harness-ai/src/anthropic-client.ts +99 -0
- package/packages/harness-ai/src/anthropic-protocol.ts +581 -0
- package/packages/harness-ai/src/anthropic-provider.ts +82 -0
- package/packages/harness-ai/src/async-iterable-stream.ts +65 -0
- package/packages/harness-ai/src/index.ts +36 -0
- package/packages/harness-ai/src/json-parse.ts +66 -0
- package/packages/harness-ai/src/sse.ts +80 -0
- package/packages/harness-ai/src/stream-object.ts +96 -0
- package/packages/harness-ai/src/stream-text.ts +1340 -0
- package/packages/harness-ai/src/types.ts +330 -0
- package/packages/harness-ai/src/ui-stream.ts +217 -0
- package/scripts/codex-live-mux-runtime.ts +123 -7
- package/scripts/control-plane-daemon.ts +20 -3
- package/scripts/harness.ts +566 -133
- package/src/cli/gateway-record.ts +16 -1
- package/src/control-plane/agent-realtime-api.ts +4 -0
- package/src/control-plane/prompt/agent-prompt-extractor.ts +191 -0
- package/src/control-plane/prompt/extractors/claude-prompt-extractor.ts +53 -0
- package/src/control-plane/prompt/extractors/codex-prompt-extractor.ts +50 -0
- package/src/control-plane/prompt/extractors/cursor-prompt-extractor.ts +56 -0
- package/src/control-plane/prompt/session-prompt-engine.ts +69 -0
- package/src/control-plane/prompt/thread-title-namer.ts +290 -0
- package/src/control-plane/stream-command-parser.ts +12 -0
- package/src/control-plane/stream-protocol.ts +109 -0
- package/src/control-plane/stream-server-command.ts +14 -0
- package/src/control-plane/stream-server-session-runtime.ts +12 -0
- package/src/control-plane/stream-server.ts +485 -19
- package/src/mux/input-shortcuts.ts +9 -0
- package/src/mux/live-mux/critique-review.ts +5 -1
- package/src/mux/live-mux/git-parsing.ts +24 -0
- package/src/mux/live-mux/global-shortcut-handlers.ts +8 -0
- package/src/mux/render-frame.ts +1 -1
- package/src/pty/pty_host.ts +46 -1
- package/src/services/control-plane.ts +22 -0
- package/src/services/runtime-control-actions.ts +69 -0
- package/src/services/runtime-navigation-input.ts +4 -0
- package/src/services/runtime-rail-input.ts +4 -0
- package/src/services/runtime-workspace-actions.ts +5 -0
- package/src/ui/global-shortcut-input.ts +2 -0
|
@@ -9,6 +9,7 @@ type MuxGlobalShortcutAction =
|
|
|
9
9
|
| 'mux.conversation.critique.open-or-create'
|
|
10
10
|
| 'mux.conversation.next'
|
|
11
11
|
| 'mux.conversation.previous'
|
|
12
|
+
| 'mux.conversation.titles.refresh-all'
|
|
12
13
|
| 'mux.conversation.interrupt'
|
|
13
14
|
| 'mux.conversation.archive'
|
|
14
15
|
| 'mux.conversation.takeover'
|
|
@@ -47,6 +48,7 @@ const ACTION_ORDER: readonly MuxGlobalShortcutAction[] = [
|
|
|
47
48
|
'mux.conversation.critique.open-or-create',
|
|
48
49
|
'mux.conversation.next',
|
|
49
50
|
'mux.conversation.previous',
|
|
51
|
+
'mux.conversation.titles.refresh-all',
|
|
50
52
|
'mux.conversation.interrupt',
|
|
51
53
|
'mux.conversation.archive',
|
|
52
54
|
'mux.conversation.takeover',
|
|
@@ -68,6 +70,7 @@ const DEFAULT_MUX_SHORTCUT_BINDINGS_RAW: Readonly<
|
|
|
68
70
|
'mux.conversation.critique.open-or-create': ['ctrl+g'],
|
|
69
71
|
'mux.conversation.next': ['ctrl+j'],
|
|
70
72
|
'mux.conversation.previous': ['ctrl+k'],
|
|
73
|
+
'mux.conversation.titles.refresh-all': ['ctrl+r'],
|
|
71
74
|
'mux.conversation.interrupt': [],
|
|
72
75
|
'mux.conversation.archive': [],
|
|
73
76
|
'mux.conversation.takeover': ['ctrl+l'],
|
|
@@ -580,6 +583,9 @@ function withDefaultBindings(
|
|
|
580
583
|
'mux.conversation.previous':
|
|
581
584
|
overrides?.['mux.conversation.previous'] ??
|
|
582
585
|
DEFAULT_MUX_SHORTCUT_BINDINGS_RAW['mux.conversation.previous'],
|
|
586
|
+
'mux.conversation.titles.refresh-all':
|
|
587
|
+
overrides?.['mux.conversation.titles.refresh-all'] ??
|
|
588
|
+
DEFAULT_MUX_SHORTCUT_BINDINGS_RAW['mux.conversation.titles.refresh-all'],
|
|
583
589
|
'mux.conversation.interrupt':
|
|
584
590
|
overrides?.['mux.conversation.interrupt'] ??
|
|
585
591
|
DEFAULT_MUX_SHORTCUT_BINDINGS_RAW['mux.conversation.interrupt'],
|
|
@@ -625,6 +631,9 @@ export function resolveMuxShortcutBindings(
|
|
|
625
631
|
),
|
|
626
632
|
'mux.conversation.next': parseBindingsForAction(rawByAction['mux.conversation.next']),
|
|
627
633
|
'mux.conversation.previous': parseBindingsForAction(rawByAction['mux.conversation.previous']),
|
|
634
|
+
'mux.conversation.titles.refresh-all': parseBindingsForAction(
|
|
635
|
+
rawByAction['mux.conversation.titles.refresh-all'],
|
|
636
|
+
),
|
|
628
637
|
'mux.conversation.interrupt': parseBindingsForAction(
|
|
629
638
|
rawByAction['mux.conversation.interrupt'],
|
|
630
639
|
),
|
|
@@ -8,6 +8,10 @@ interface CritiqueReviewAgentAvailability {
|
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
type CritiqueReviewCommandInput =
|
|
11
|
+
| {
|
|
12
|
+
readonly mode: 'unstaged';
|
|
13
|
+
readonly agent: CritiqueReviewAgent | null;
|
|
14
|
+
}
|
|
11
15
|
| {
|
|
12
16
|
readonly mode: 'staged';
|
|
13
17
|
readonly agent: CritiqueReviewAgent | null;
|
|
@@ -54,7 +58,7 @@ export function buildCritiqueReviewCommand(input: CritiqueReviewCommandInput): s
|
|
|
54
58
|
const tokens = ['critique', 'review'];
|
|
55
59
|
if (input.mode === 'staged') {
|
|
56
60
|
tokens.push('--staged');
|
|
57
|
-
} else {
|
|
61
|
+
} else if (input.mode === 'base-branch') {
|
|
58
62
|
const normalizedBaseBranch = normalizeBranchName(input.baseBranch) ?? 'main';
|
|
59
63
|
tokens.push(normalizedBaseBranch, 'HEAD');
|
|
60
64
|
}
|
|
@@ -98,6 +98,30 @@ export function shouldShowGitHubPrActions(input: {
|
|
|
98
98
|
return normalizedTrackedBranch !== 'main';
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
+
function normalizeTrackedBranchForActions(value: string | null): string | null {
|
|
102
|
+
const trimmed = value?.trim() ?? '';
|
|
103
|
+
if (
|
|
104
|
+
trimmed.length === 0 ||
|
|
105
|
+
trimmed === '(detached)' ||
|
|
106
|
+
trimmed === '(loading)' ||
|
|
107
|
+
trimmed === 'HEAD'
|
|
108
|
+
) {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
return trimmed;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function resolveGitHubTrackedBranchForActions(input: {
|
|
115
|
+
projectTrackedBranch: string | null;
|
|
116
|
+
currentBranch: string | null;
|
|
117
|
+
}): string | null {
|
|
118
|
+
const trackedBranch = normalizeTrackedBranchForActions(input.projectTrackedBranch);
|
|
119
|
+
if (trackedBranch !== null) {
|
|
120
|
+
return trackedBranch;
|
|
121
|
+
}
|
|
122
|
+
return normalizeTrackedBranchForActions(input.currentBranch);
|
|
123
|
+
}
|
|
124
|
+
|
|
101
125
|
export function parseCommitCount(output: string): number | null {
|
|
102
126
|
const trimmed = output.trim();
|
|
103
127
|
if (trimmed.length === 0 || !/^\d+$/u.test(trimmed)) {
|
|
@@ -14,6 +14,7 @@ interface HandleGlobalShortcutOptions {
|
|
|
14
14
|
conversationsHas: (sessionId: string) => boolean;
|
|
15
15
|
queueControlPlaneOp: (task: () => Promise<void>, label: string) => void;
|
|
16
16
|
archiveConversation: (sessionId: string) => Promise<void>;
|
|
17
|
+
refreshAllConversationTitles: () => Promise<void>;
|
|
17
18
|
interruptConversation: (sessionId: string) => Promise<void>;
|
|
18
19
|
takeoverConversation: (sessionId: string) => Promise<void>;
|
|
19
20
|
openAddDirectoryPrompt: () => void;
|
|
@@ -37,6 +38,7 @@ export function handleGlobalShortcut(options: HandleGlobalShortcutOptions): bool
|
|
|
37
38
|
conversationsHas,
|
|
38
39
|
queueControlPlaneOp,
|
|
39
40
|
archiveConversation,
|
|
41
|
+
refreshAllConversationTitles,
|
|
40
42
|
interruptConversation,
|
|
41
43
|
takeoverConversation,
|
|
42
44
|
openAddDirectoryPrompt,
|
|
@@ -104,6 +106,12 @@ export function handleGlobalShortcut(options: HandleGlobalShortcutOptions): bool
|
|
|
104
106
|
}
|
|
105
107
|
return true;
|
|
106
108
|
}
|
|
109
|
+
if (shortcut === 'mux.conversation.titles.refresh-all') {
|
|
110
|
+
queueControlPlaneOp(async () => {
|
|
111
|
+
await refreshAllConversationTitles();
|
|
112
|
+
}, 'shortcut-refresh-conversation-titles');
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
107
115
|
if (shortcut === 'mux.conversation.interrupt') {
|
|
108
116
|
const targetConversationId = resolveConversationForAction();
|
|
109
117
|
if (targetConversationId !== null && conversationsHas(targetConversationId)) {
|
package/src/mux/render-frame.ts
CHANGED
|
@@ -51,7 +51,7 @@ export function buildRenderRows(
|
|
|
51
51
|
const statusText =
|
|
52
52
|
statusRowDetailText === undefined || statusRowDetailText.length === 0
|
|
53
53
|
? defaultStatus
|
|
54
|
-
: `${
|
|
54
|
+
: `${statusRowDetailText} ${defaultStatus}`;
|
|
55
55
|
const status = padOrTrimDisplay(statusText, layout.cols);
|
|
56
56
|
rows.push(status);
|
|
57
57
|
return rows;
|
package/src/pty/pty_host.ts
CHANGED
|
@@ -68,6 +68,8 @@ class PtySession extends EventEmitter {
|
|
|
68
68
|
private nextProbeId = 1;
|
|
69
69
|
private outputWindow = Buffer.alloc(0);
|
|
70
70
|
private static readonly MAX_OUTPUT_WINDOW_BYTES = 8192;
|
|
71
|
+
private static readonly MAX_PENDING_ROUNDTRIP_PROBES = 64;
|
|
72
|
+
private static readonly ROUNDTRIP_PROBE_MAX_AGE_NS = 5_000_000_000n;
|
|
71
73
|
|
|
72
74
|
constructor(child: ChildProcessWithoutNullStreams) {
|
|
73
75
|
super();
|
|
@@ -101,6 +103,12 @@ class PtySession extends EventEmitter {
|
|
|
101
103
|
matchPayloads: PtySession.buildMatchPayloads(payload),
|
|
102
104
|
startedAtNs: perfNowNs(),
|
|
103
105
|
});
|
|
106
|
+
if (this.pendingRoundtripProbes.length > PtySession.MAX_PENDING_ROUNDTRIP_PROBES) {
|
|
107
|
+
this.pendingRoundtripProbes.splice(
|
|
108
|
+
0,
|
|
109
|
+
this.pendingRoundtripProbes.length - PtySession.MAX_PENDING_ROUNDTRIP_PROBES,
|
|
110
|
+
);
|
|
111
|
+
}
|
|
104
112
|
this.nextProbeId += 1;
|
|
105
113
|
}
|
|
106
114
|
|
|
@@ -148,12 +156,22 @@ class PtySession extends EventEmitter {
|
|
|
148
156
|
);
|
|
149
157
|
}
|
|
150
158
|
|
|
159
|
+
const maxMatchPayloadLength = this.compactPendingRoundtripProbes(perfNowNs());
|
|
160
|
+
if (this.pendingRoundtripProbes.length === 0) {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
const searchWindowLength = Math.max(
|
|
164
|
+
1,
|
|
165
|
+
Math.min(this.outputWindow.length, chunk.length + Math.max(1, maxMatchPayloadLength) - 1),
|
|
166
|
+
);
|
|
167
|
+
const searchWindow = this.outputWindow.subarray(this.outputWindow.length - searchWindowLength);
|
|
168
|
+
|
|
151
169
|
let idx = 0;
|
|
152
170
|
while (idx < this.pendingRoundtripProbes.length) {
|
|
153
171
|
const probe = this.pendingRoundtripProbes[idx];
|
|
154
172
|
if (
|
|
155
173
|
probe !== undefined &&
|
|
156
|
-
probe.matchPayloads.some((matchPayload) =>
|
|
174
|
+
probe.matchPayloads.some((matchPayload) => searchWindow.includes(matchPayload))
|
|
157
175
|
) {
|
|
158
176
|
recordPerfDuration('pty.keystroke.roundtrip', probe.startedAtNs, {
|
|
159
177
|
'probe-id': probe.probeId,
|
|
@@ -166,6 +184,33 @@ class PtySession extends EventEmitter {
|
|
|
166
184
|
}
|
|
167
185
|
}
|
|
168
186
|
|
|
187
|
+
private compactPendingRoundtripProbes(nowNs: bigint): number {
|
|
188
|
+
if (this.pendingRoundtripProbes.length > PtySession.MAX_PENDING_ROUNDTRIP_PROBES) {
|
|
189
|
+
this.pendingRoundtripProbes.splice(
|
|
190
|
+
0,
|
|
191
|
+
this.pendingRoundtripProbes.length - PtySession.MAX_PENDING_ROUNDTRIP_PROBES,
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
let maxMatchPayloadLength = 1;
|
|
195
|
+
let idx = 0;
|
|
196
|
+
while (idx < this.pendingRoundtripProbes.length) {
|
|
197
|
+
const probe = this.pendingRoundtripProbes[idx];
|
|
198
|
+
if (probe === undefined) {
|
|
199
|
+
idx += 1;
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
if (nowNs - probe.startedAtNs > PtySession.ROUNDTRIP_PROBE_MAX_AGE_NS) {
|
|
203
|
+
this.pendingRoundtripProbes.splice(idx, 1);
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
for (const matchPayload of probe.matchPayloads) {
|
|
207
|
+
maxMatchPayloadLength = Math.max(maxMatchPayloadLength, matchPayload.length);
|
|
208
|
+
}
|
|
209
|
+
idx += 1;
|
|
210
|
+
}
|
|
211
|
+
return maxMatchPayloadLength;
|
|
212
|
+
}
|
|
213
|
+
|
|
169
214
|
private static buildMatchPayloads(payload: Buffer): Buffer[] {
|
|
170
215
|
if (!payload.includes(0x0a)) {
|
|
171
216
|
return [payload];
|
|
@@ -245,6 +245,28 @@ export class ControlPlaneService {
|
|
|
245
245
|
return parseConversationRecord(result['conversation']);
|
|
246
246
|
}
|
|
247
247
|
|
|
248
|
+
async refreshConversationTitle(conversationId: string): Promise<{
|
|
249
|
+
status: 'updated' | 'unchanged' | 'skipped';
|
|
250
|
+
reason: string | null;
|
|
251
|
+
}> {
|
|
252
|
+
const result = await this.client.sendCommand({
|
|
253
|
+
type: 'conversation.title.refresh',
|
|
254
|
+
conversationId,
|
|
255
|
+
});
|
|
256
|
+
const status = result['status'];
|
|
257
|
+
if (status !== 'updated' && status !== 'unchanged' && status !== 'skipped') {
|
|
258
|
+
throw new Error('control-plane conversation.title.refresh returned malformed status');
|
|
259
|
+
}
|
|
260
|
+
const reason = result['reason'];
|
|
261
|
+
if (reason !== null && reason !== undefined && typeof reason !== 'string') {
|
|
262
|
+
throw new Error('control-plane conversation.title.refresh returned malformed reason');
|
|
263
|
+
}
|
|
264
|
+
return {
|
|
265
|
+
status,
|
|
266
|
+
reason: reason ?? null,
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
|
|
248
270
|
async archiveConversation(conversationId: string): Promise<void> {
|
|
249
271
|
await this.client.sendCommand({
|
|
250
272
|
type: 'conversation.archive',
|
|
@@ -21,6 +21,13 @@ interface RuntimeGatewayRenderTraceResult {
|
|
|
21
21
|
readonly message: string;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
interface RuntimeConversationTitleRefreshResult {
|
|
25
|
+
readonly status: 'updated' | 'unchanged' | 'skipped';
|
|
26
|
+
readonly reason: string | null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const THREAD_TITLE_AGENT_TYPES = new Set(['codex', 'claude', 'cursor']);
|
|
30
|
+
|
|
24
31
|
interface RuntimeControlActionsOptions<TConversation extends RuntimeConversationControlState> {
|
|
25
32
|
readonly conversationById: (sessionId: string) => TConversation | undefined;
|
|
26
33
|
readonly interruptSession: (sessionId: string) => Promise<RuntimeInterruptResult>;
|
|
@@ -43,6 +50,11 @@ interface RuntimeControlActionsOptions<TConversation extends RuntimeConversation
|
|
|
43
50
|
readonly sessionName: string | null;
|
|
44
51
|
readonly setTaskPaneNotice: (message: string) => void;
|
|
45
52
|
readonly setDebugFooterNotice: (message: string) => void;
|
|
53
|
+
readonly listConversationIdsForTitleRefresh?: () => readonly string[];
|
|
54
|
+
readonly conversationAgentTypeForTitleRefresh?: (sessionId: string) => string | null;
|
|
55
|
+
readonly refreshConversationTitle?: (
|
|
56
|
+
sessionId: string,
|
|
57
|
+
) => Promise<RuntimeConversationTitleRefreshResult>;
|
|
46
58
|
}
|
|
47
59
|
|
|
48
60
|
export class RuntimeControlActions<TConversation extends RuntimeConversationControlState> {
|
|
@@ -109,6 +121,63 @@ export class RuntimeControlActions<TConversation extends RuntimeConversationCont
|
|
|
109
121
|
}
|
|
110
122
|
}
|
|
111
123
|
|
|
124
|
+
async refreshAllConversationTitles(): Promise<void> {
|
|
125
|
+
const listConversationIds = this.options.listConversationIdsForTitleRefresh;
|
|
126
|
+
const resolveAgentType = this.options.conversationAgentTypeForTitleRefresh;
|
|
127
|
+
const refreshConversationTitle = this.options.refreshConversationTitle;
|
|
128
|
+
if (
|
|
129
|
+
listConversationIds === undefined ||
|
|
130
|
+
resolveAgentType === undefined ||
|
|
131
|
+
refreshConversationTitle === undefined
|
|
132
|
+
) {
|
|
133
|
+
this.setNotices(this.scopeMessage('thread-title', 'refresh unavailable'));
|
|
134
|
+
this.options.markDirty();
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
const allConversationIds = listConversationIds();
|
|
138
|
+
const eligibleConversationIds = allConversationIds.filter((sessionId) => {
|
|
139
|
+
const agentType = resolveAgentType(sessionId)?.trim().toLowerCase();
|
|
140
|
+
return agentType !== undefined && THREAD_TITLE_AGENT_TYPES.has(agentType);
|
|
141
|
+
});
|
|
142
|
+
if (eligibleConversationIds.length === 0) {
|
|
143
|
+
this.setNotices(this.scopeMessage('thread-title', 'no agent threads to refresh'));
|
|
144
|
+
this.options.markDirty();
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
const total = eligibleConversationIds.length;
|
|
148
|
+
let updated = 0;
|
|
149
|
+
let unchanged = 0;
|
|
150
|
+
let skipped = 0;
|
|
151
|
+
this.setNotices(this.scopeMessage('thread-title', `refreshing names 0/${String(total)}`));
|
|
152
|
+
this.options.markDirty();
|
|
153
|
+
for (let index = 0; index < eligibleConversationIds.length; index += 1) {
|
|
154
|
+
const sessionId = eligibleConversationIds[index]!;
|
|
155
|
+
try {
|
|
156
|
+
const result = await refreshConversationTitle(sessionId);
|
|
157
|
+
if (result.status === 'updated') {
|
|
158
|
+
updated += 1;
|
|
159
|
+
} else if (result.status === 'unchanged') {
|
|
160
|
+
unchanged += 1;
|
|
161
|
+
} else {
|
|
162
|
+
skipped += 1;
|
|
163
|
+
}
|
|
164
|
+
} catch {
|
|
165
|
+
skipped += 1;
|
|
166
|
+
}
|
|
167
|
+
this.setNotices(
|
|
168
|
+
this.scopeMessage('thread-title', `refreshing names ${String(index + 1)}/${String(total)}`),
|
|
169
|
+
);
|
|
170
|
+
this.options.markDirty();
|
|
171
|
+
}
|
|
172
|
+
this.setNotices(
|
|
173
|
+
this.scopeMessage(
|
|
174
|
+
'thread-title',
|
|
175
|
+
`refreshed ${String(updated)} updated ${String(unchanged)} unchanged ${String(skipped)} skipped`,
|
|
176
|
+
),
|
|
177
|
+
);
|
|
178
|
+
this.options.markDirty();
|
|
179
|
+
}
|
|
180
|
+
|
|
112
181
|
private scopeMessage(prefix: string, message: string): string {
|
|
113
182
|
if (this.options.sessionName === null) {
|
|
114
183
|
return `[${prefix}] ${message}`;
|
|
@@ -17,6 +17,7 @@ interface RuntimeNavigationWorkspaceActions {
|
|
|
17
17
|
toggleGatewayStatusTimeline(): Promise<void>;
|
|
18
18
|
toggleGatewayRenderTrace(conversationId: string | null): Promise<void>;
|
|
19
19
|
archiveConversation(sessionId: string): Promise<void>;
|
|
20
|
+
refreshAllConversationTitles(): Promise<void>;
|
|
20
21
|
interruptConversation(sessionId: string): Promise<void>;
|
|
21
22
|
takeoverConversation(sessionId: string): Promise<void>;
|
|
22
23
|
closeDirectory(directoryId: string): Promise<void>;
|
|
@@ -157,6 +158,9 @@ export class RuntimeNavigationInput {
|
|
|
157
158
|
archiveConversation: async (sessionId) => {
|
|
158
159
|
await options.workspaceActions.archiveConversation(sessionId);
|
|
159
160
|
},
|
|
161
|
+
refreshAllConversationTitles: async () => {
|
|
162
|
+
await options.workspaceActions.refreshAllConversationTitles();
|
|
163
|
+
},
|
|
160
164
|
interruptConversation: async (sessionId) => {
|
|
161
165
|
await options.workspaceActions.interruptConversation(sessionId);
|
|
162
166
|
},
|
|
@@ -15,6 +15,7 @@ interface RuntimeRailWorkspaceActions {
|
|
|
15
15
|
toggleGatewayStatusTimeline(): Promise<void>;
|
|
16
16
|
toggleGatewayRenderTrace(conversationId: string | null): Promise<void>;
|
|
17
17
|
archiveConversation(sessionId: string): Promise<void>;
|
|
18
|
+
refreshAllConversationTitles(): Promise<void>;
|
|
18
19
|
interruptConversation(sessionId: string): Promise<void>;
|
|
19
20
|
takeoverConversation(sessionId: string): Promise<void>;
|
|
20
21
|
closeDirectory(directoryId: string): Promise<void>;
|
|
@@ -142,6 +143,9 @@ export class RuntimeRailInput {
|
|
|
142
143
|
archiveConversation: async (sessionId) => {
|
|
143
144
|
await options.runtimeWorkspaceActions.archiveConversation(sessionId);
|
|
144
145
|
},
|
|
146
|
+
refreshAllConversationTitles: async () => {
|
|
147
|
+
await options.runtimeWorkspaceActions.refreshAllConversationTitles();
|
|
148
|
+
},
|
|
145
149
|
interruptConversation: async (sessionId) => {
|
|
146
150
|
await options.runtimeWorkspaceActions.interruptConversation(sessionId);
|
|
147
151
|
},
|
|
@@ -30,6 +30,7 @@ interface RuntimeWorkspaceControlActions {
|
|
|
30
30
|
toggleGatewayProfiler(): Promise<void>;
|
|
31
31
|
toggleGatewayStatusTimeline(): Promise<void>;
|
|
32
32
|
toggleGatewayRenderTrace(conversationId: string | null): Promise<void>;
|
|
33
|
+
refreshAllConversationTitles(): Promise<void>;
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
interface RuntimeWorkspaceTaskPaneActions {
|
|
@@ -135,6 +136,10 @@ export class RuntimeWorkspaceActions {
|
|
|
135
136
|
await this.options.controlActions.toggleGatewayRenderTrace(conversationId);
|
|
136
137
|
}
|
|
137
138
|
|
|
139
|
+
async refreshAllConversationTitles(): Promise<void> {
|
|
140
|
+
await this.options.controlActions.refreshAllConversationTitles();
|
|
141
|
+
}
|
|
142
|
+
|
|
138
143
|
runTaskPaneAction(action: TaskPaneAction): void {
|
|
139
144
|
this.options.taskPaneActions.runTaskPaneAction(action);
|
|
140
145
|
}
|
|
@@ -23,6 +23,7 @@ interface GlobalShortcutInputOptions {
|
|
|
23
23
|
readonly conversationsHas: (sessionId: string) => boolean;
|
|
24
24
|
readonly queueControlPlaneOp: (task: () => Promise<void>, label: string) => void;
|
|
25
25
|
readonly archiveConversation: (sessionId: string) => Promise<void>;
|
|
26
|
+
readonly refreshAllConversationTitles: () => Promise<void>;
|
|
26
27
|
readonly interruptConversation: (sessionId: string) => Promise<void>;
|
|
27
28
|
readonly takeoverConversation: (sessionId: string) => Promise<void>;
|
|
28
29
|
readonly openAddDirectoryPrompt: () => void;
|
|
@@ -70,6 +71,7 @@ export class GlobalShortcutInput {
|
|
|
70
71
|
conversationsHas: this.options.conversationsHas,
|
|
71
72
|
queueControlPlaneOp: this.options.queueControlPlaneOp,
|
|
72
73
|
archiveConversation: this.options.archiveConversation,
|
|
74
|
+
refreshAllConversationTitles: this.options.refreshAllConversationTitles,
|
|
73
75
|
interruptConversation: this.options.interruptConversation,
|
|
74
76
|
takeoverConversation: this.options.takeoverConversation,
|
|
75
77
|
openAddDirectoryPrompt: this.options.openAddDirectoryPrompt,
|