clideck 1.30.3 → 1.30.5
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/agent-presets.json +23 -4
- package/config.js +9 -2
- package/handlers.js +77 -59
- package/package.json +1 -1
- package/public/img/clideck-agent-dark.svg +25 -0
- package/public/img/clideck-agent-light.svg +25 -0
- package/public/js/creator.js +7 -2
- package/public/js/settings.js +72 -2
- package/public/js/terminals.js +2 -0
- package/public/js/utils.js +1 -0
- package/sessions.js +2 -2
- package/skills/autonomous-session/SKILL.md +211 -0
- package/telemetry-receiver.js +8 -1
package/agent-presets.json
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"canResume": true,
|
|
11
11
|
"resumeCommand": "claude --resume {{sessionId}}",
|
|
12
12
|
"sessionIdPattern": "Session ID:\\s+([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})",
|
|
13
|
-
"outputMarker": "
|
|
13
|
+
"outputMarker": "⏺",
|
|
14
14
|
"telemetryConfigPath": "~/.claude/settings.json",
|
|
15
15
|
"telemetrySetup": "Required for working/idle status, Autopilot, notifications, and mobile remote.\n\nCliDeck will add start/stop hooks to ~/.claude/settings.json. Claude will ask for one-time approval on next launch.",
|
|
16
16
|
"telemetryAutoSetup": {
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"canResume": true,
|
|
36
36
|
"resumeCommand": "codex resume {{sessionId}}",
|
|
37
37
|
"sessionIdPattern": "Session:\\s+([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})",
|
|
38
|
-
"outputMarker": "
|
|
38
|
+
"outputMarker": "•",
|
|
39
39
|
"telemetryConfigPath": "~/.codex/config.toml",
|
|
40
40
|
"telemetryEnv": {
|
|
41
41
|
"OTEL_LOGS_EXPORTER": "otlp",
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"canResume": true,
|
|
60
60
|
"resumeCommand": "gemini --resume {{sessionId}}",
|
|
61
61
|
"sessionIdPattern": "Session ID:\\s+([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})",
|
|
62
|
-
"outputMarker": "
|
|
62
|
+
"outputMarker": "✦",
|
|
63
63
|
"telemetryConfigPath": "~/.gemini/settings.json",
|
|
64
64
|
"telemetrySetup": "Required for working/idle status, resume, Autopilot, notifications, and mobile remote.\n\nCliDeck will add BeforeAgent/AfterAgent/SessionEnd/BeforeTool hooks to ~/.gemini/settings.json.",
|
|
65
65
|
"telemetryAutoSetup": {
|
|
@@ -77,7 +77,7 @@
|
|
|
77
77
|
"canResume": true,
|
|
78
78
|
"resumeCommand": "opencode --session {{sessionId}}",
|
|
79
79
|
"sessionIdPattern": "(ses_[a-zA-Z0-9]{10,})",
|
|
80
|
-
"outputMarker": "
|
|
80
|
+
"outputMarker": "│",
|
|
81
81
|
"bridge": "opencode",
|
|
82
82
|
"pluginPath": "~/.config/opencode/plugins/clideck-bridge.js",
|
|
83
83
|
"pluginSetup": "Install the CliDeck bridge plugin to enable real-time status and resume.\n\ncp opencode-plugin/clideck-bridge.js ~/.config/opencode/plugins/",
|
|
@@ -85,6 +85,25 @@
|
|
|
85
85
|
"label": "Install plugin"
|
|
86
86
|
}
|
|
87
87
|
},
|
|
88
|
+
{
|
|
89
|
+
"presetId": "clideck-agent",
|
|
90
|
+
"name": "Clideck Agent",
|
|
91
|
+
"icon": "/img/clideck-agent.svg",
|
|
92
|
+
"command": "clideck-agent",
|
|
93
|
+
"enabledIfEnv": "CLIDECK_ENABLE_CLIDECK_AGENT",
|
|
94
|
+
"isAgent": true,
|
|
95
|
+
"canResume": true,
|
|
96
|
+
"resumeCommand": "clideck-agent --resume {{sessionId}}",
|
|
97
|
+
"sessionIdPattern": null,
|
|
98
|
+
"outputMarker": "•",
|
|
99
|
+
"telemetryEnabled": true,
|
|
100
|
+
"telemetryEnv": {
|
|
101
|
+
"OTEL_LOGS_EXPORTER": "otlp",
|
|
102
|
+
"OTEL_EXPORTER_OTLP_PROTOCOL": "http/json",
|
|
103
|
+
"OTEL_EXPORTER_OTLP_ENDPOINT": "http://localhost:{{port}}",
|
|
104
|
+
"OTEL_LOGS_EXPORT_INTERVAL": "2000"
|
|
105
|
+
}
|
|
106
|
+
},
|
|
88
107
|
{
|
|
89
108
|
"presetId": "shell",
|
|
90
109
|
"name": "Shell",
|
package/config.js
CHANGED
|
@@ -95,6 +95,12 @@ function deepCopy(obj) { return JSON.parse(JSON.stringify(obj)); }
|
|
|
95
95
|
|
|
96
96
|
const PRESETS = JSON.parse(readFileSync(join(__dirname, 'agent-presets.json'), 'utf8'));
|
|
97
97
|
for (const p of PRESETS) if (p.presetId === 'shell') p.command = defaultShell;
|
|
98
|
+
function isPresetEnabled(preset) {
|
|
99
|
+
if (!preset?.enabledIfEnv) return true;
|
|
100
|
+
const value = String(process.env[preset.enabledIfEnv] || '').trim().toLowerCase();
|
|
101
|
+
return value === '1' || value === 'true' || value === 'yes';
|
|
102
|
+
}
|
|
103
|
+
const EXPOSED_PRESETS = PRESETS.filter(isPresetEnabled);
|
|
98
104
|
|
|
99
105
|
function matchPreset(cmd) {
|
|
100
106
|
const bin = binName(cmd.command);
|
|
@@ -124,7 +130,8 @@ function migrate(cfg) {
|
|
|
124
130
|
if (cmd.sessionIdPattern === undefined) cmd.sessionIdPattern = preset?.sessionIdPattern || null;
|
|
125
131
|
if (cmd.outputMarker === undefined) cmd.outputMarker = preset?.outputMarker || null;
|
|
126
132
|
// Claude Code telemetry is built-in, always on
|
|
127
|
-
if (preset?.
|
|
133
|
+
if (preset?.telemetryEnabled === true) cmd.telemetryEnabled = true;
|
|
134
|
+
else if (preset?.presetId === 'claude-code') cmd.telemetryEnabled = true;
|
|
128
135
|
else if (cmd.telemetryEnabled === undefined) cmd.telemetryEnabled = false;
|
|
129
136
|
if (cmd.telemetryStatus === undefined) cmd.telemetryStatus = null;
|
|
130
137
|
// Sync bridge config from preset
|
|
@@ -139,7 +146,7 @@ function migrate(cfg) {
|
|
|
139
146
|
}
|
|
140
147
|
}
|
|
141
148
|
// Auto-add any shipped presets not yet in the commands list
|
|
142
|
-
for (const preset of
|
|
149
|
+
for (const preset of EXPOSED_PRESETS) {
|
|
143
150
|
const exists = cfg.commands.some(c => c.presetId === preset.presetId || matchPreset(c)?.presetId === preset.presetId);
|
|
144
151
|
if (!exists) {
|
|
145
152
|
cfg.commands.push({
|
package/handlers.js
CHANGED
|
@@ -8,6 +8,18 @@ const themes = require('./themes');
|
|
|
8
8
|
const presets = JSON.parse(readFileSync(join(__dirname, 'agent-presets.json'), 'utf8'));
|
|
9
9
|
const { listDirs, binName, defaultShell } = require('./utils');
|
|
10
10
|
for (const p of presets) if (p.presetId === 'shell') p.command = defaultShell;
|
|
11
|
+
function isPresetEnabled(preset) {
|
|
12
|
+
if (!preset?.enabledIfEnv) return true;
|
|
13
|
+
const value = String(process.env[preset.enabledIfEnv] || '').trim().toLowerCase();
|
|
14
|
+
return value === '1' || value === 'true' || value === 'yes';
|
|
15
|
+
}
|
|
16
|
+
function clientPresets() {
|
|
17
|
+
return presets.filter(isPresetEnabled);
|
|
18
|
+
}
|
|
19
|
+
function filterClientCommands(commands) {
|
|
20
|
+
const allowedPresetIds = new Set(clientPresets().map(p => p.presetId));
|
|
21
|
+
return (commands || []).filter(cmd => !cmd.presetId || allowedPresetIds.has(cmd.presetId));
|
|
22
|
+
}
|
|
11
23
|
const transcript = require('./transcript');
|
|
12
24
|
const plugins = require('./plugin-loader');
|
|
13
25
|
const { upsertCodexConfig, validateCodexConfigToml } = require('./codex-config');
|
|
@@ -87,6 +99,7 @@ function checkRemoteUpdate(ws) {
|
|
|
87
99
|
const whichCmd = process.platform === 'win32' ? 'where' : 'which';
|
|
88
100
|
function checkAvailability() {
|
|
89
101
|
for (const p of presets) {
|
|
102
|
+
if (!isPresetEnabled(p)) continue;
|
|
90
103
|
if (p.presetId === 'shell') { p.available = true; p.version = ''; p.versionOk = true; p.health = { ok: true }; continue; }
|
|
91
104
|
const bin = binName(p.command);
|
|
92
105
|
try {
|
|
@@ -139,64 +152,69 @@ function detectTelemetryConfig(c) {
|
|
|
139
152
|
const home = os.homedir();
|
|
140
153
|
const port = '4000';
|
|
141
154
|
let changed = false;
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
155
|
+
const attemptedRepairs = new Set();
|
|
156
|
+
|
|
157
|
+
for (let pass = 0; pass < 2; pass++) {
|
|
158
|
+
let repairedAny = false;
|
|
159
|
+
for (const cmd of c.commands || []) {
|
|
160
|
+
const bin = binName(cmd.command);
|
|
161
|
+
const preset = presets.find(p => binName(p.command) === bin);
|
|
162
|
+
if (!preset) continue;
|
|
163
|
+
let detected = false;
|
|
164
|
+
let reason = '';
|
|
165
|
+
if (preset.presetId === 'claude-code') {
|
|
166
|
+
try {
|
|
167
|
+
const s = JSON.parse(readFileSync(join(home, '.claude', 'settings.json'), 'utf8'));
|
|
168
|
+
const hooks = s.hooks || {};
|
|
169
|
+
detected = hasExistingHook(hooks.UserPromptSubmit, 'claude-hook.js', 'start')
|
|
170
|
+
&& hasExistingHook(hooks.Stop, 'claude-hook.js', 'stop')
|
|
171
|
+
&& hasExistingHook(hooks.StopFailure, 'claude-hook.js', 'stop')
|
|
172
|
+
&& hasExistingHook(hooks.PreToolUse, 'claude-hook.js', 'menu')
|
|
173
|
+
&& hooks.Notification?.some(h => h.matcher === 'idle_prompt' && hasExistingHook([h], 'claude-hook.js', 'idle'));
|
|
174
|
+
if (!detected) reason = 'Needs re-patch';
|
|
175
|
+
} catch {}
|
|
176
|
+
} else if (preset.presetId === 'codex') {
|
|
177
|
+
try {
|
|
178
|
+
const content = readFileSync(join(home, '.codex', 'config.toml'), 'utf8');
|
|
179
|
+
detected = codexConfigLooksHealthy(content, port);
|
|
180
|
+
if (!detected) reason = 'Needs re-patch';
|
|
181
|
+
} catch {}
|
|
182
|
+
} else if (preset.presetId === 'gemini-cli') {
|
|
183
|
+
try {
|
|
184
|
+
const s = JSON.parse(readFileSync(join(home, '.gemini', 'settings.json'), 'utf8'));
|
|
185
|
+
const hooks = s.hooks || {};
|
|
186
|
+
detected = hasExistingHook(hooks.BeforeAgent, 'gemini-hook.js', 'start')
|
|
187
|
+
&& hasExistingHook(hooks.AfterAgent, 'gemini-hook.js', 'stop')
|
|
188
|
+
&& hasExistingHook(hooks.SessionEnd, 'gemini-hook.js', 'stop')
|
|
189
|
+
&& hasExistingHook(hooks.BeforeTool, 'gemini-hook.js', 'menu');
|
|
190
|
+
if (!detected) reason = 'Needs re-patch';
|
|
191
|
+
} catch {}
|
|
192
|
+
} else if (preset.presetId === 'opencode') {
|
|
193
|
+
detected = existsSync(join(opencodePluginDir, 'clideck-bridge.js')) || existsSync(join(opencodePluginDir, 'termix-bridge.js'));
|
|
174
194
|
if (!detected) reason = 'Needs re-patch';
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
repairedAny = true;
|
|
187
|
-
continue;
|
|
195
|
+
} else { continue; }
|
|
196
|
+
if (preset.available && preset.minVersion && !preset.versionOk) {
|
|
197
|
+
detected = false;
|
|
198
|
+
reason = `Update required (${preset.minVersion}+)`;
|
|
199
|
+
} else if (!detected && cmd.telemetryEnabled && preset.telemetryAutoSetup && preset.available && preset.versionOk && !attemptedRepairs.has(preset.presetId)) {
|
|
200
|
+
attemptedRepairs.add(preset.presetId);
|
|
201
|
+
const repaired = applyTelemetryConfig(preset);
|
|
202
|
+
if (repaired.success) {
|
|
203
|
+
repairedAny = true;
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
188
206
|
}
|
|
207
|
+
const nextEnabled = detected || (!!cmd.telemetryEnabled && !reason.startsWith('Update required'));
|
|
208
|
+
const nextStatus = detected ? { ok: true } : { ok: false, error: reason || 'Needs setup' };
|
|
209
|
+
if (cmd.telemetryEnabled !== nextEnabled || JSON.stringify(cmd.telemetryStatus || null) !== JSON.stringify(nextStatus)) {
|
|
210
|
+
cmd.telemetryEnabled = nextEnabled;
|
|
211
|
+
cmd.telemetryStatus = nextStatus;
|
|
212
|
+
changed = true;
|
|
213
|
+
}
|
|
214
|
+
preset.health = detected ? { ok: true } : { ok: false, reason: reason || 'Needs setup' };
|
|
189
215
|
}
|
|
190
|
-
|
|
191
|
-
const nextStatus = detected ? { ok: true } : { ok: false, error: reason || 'Needs setup' };
|
|
192
|
-
if (cmd.telemetryEnabled !== nextEnabled || JSON.stringify(cmd.telemetryStatus || null) !== JSON.stringify(nextStatus)) {
|
|
193
|
-
cmd.telemetryEnabled = nextEnabled;
|
|
194
|
-
cmd.telemetryStatus = nextStatus;
|
|
195
|
-
changed = true;
|
|
196
|
-
}
|
|
197
|
-
preset.health = detected ? { ok: true } : { ok: false, reason: reason || 'Needs setup' };
|
|
216
|
+
if (!repairedAny) break;
|
|
198
217
|
}
|
|
199
|
-
if (repairedAny) return detectTelemetryConfig(c) || true;
|
|
200
218
|
if (changed) console.log('Config: synced telemetry/plugin state from detected config files');
|
|
201
219
|
return changed;
|
|
202
220
|
}
|
|
@@ -204,7 +222,7 @@ function detectTelemetryConfig(c) {
|
|
|
204
222
|
const appVersion = require('./package.json').version;
|
|
205
223
|
|
|
206
224
|
function configForClient() {
|
|
207
|
-
return { ...cfg, pluginsDir: plugins.PLUGINS_DIR, version: appVersion };
|
|
225
|
+
return { ...cfg, commands: filterClientCommands(cfg.commands), pluginsDir: plugins.PLUGINS_DIR, version: appVersion };
|
|
208
226
|
}
|
|
209
227
|
|
|
210
228
|
function onConnection(ws) {
|
|
@@ -212,7 +230,7 @@ function onConnection(ws) {
|
|
|
212
230
|
|
|
213
231
|
ws.send(JSON.stringify({ type: 'config', config: configForClient() }));
|
|
214
232
|
ws.send(JSON.stringify({ type: 'themes', themes }));
|
|
215
|
-
ws.send(JSON.stringify({ type: 'presets', presets }));
|
|
233
|
+
ws.send(JSON.stringify({ type: 'presets', presets: clientPresets() }));
|
|
216
234
|
ws.send(JSON.stringify({ type: 'sessions', list: sessions.list() }));
|
|
217
235
|
ws.send(JSON.stringify({ type: 'sessions.resumable', list: sessions.getResumable(cfg) }));
|
|
218
236
|
ws.send(JSON.stringify({ type: 'transcript.cache', cache: transcript.getCache() }));
|
|
@@ -251,10 +269,10 @@ function onConnection(ws) {
|
|
|
251
269
|
if (choices && sess.presetId === 'codex') {
|
|
252
270
|
const last = require('./telemetry-receiver').getLastEvent(msg.id);
|
|
253
271
|
if (!last.startsWith('codex.sse_event:response.completed')) {
|
|
254
|
-
console.log(`[codex] menu rejected — lastEvent=${last} session=${msg.id.slice(0,8)}`);
|
|
272
|
+
// console.log(`[codex] menu rejected — lastEvent=${last} session=${msg.id.slice(0,8)}`);
|
|
255
273
|
choices = null;
|
|
256
274
|
} else {
|
|
257
|
-
console.log(`[codex] menu accepted session=${msg.id.slice(0,8)}`);
|
|
275
|
+
// console.log(`[codex] menu accepted session=${msg.id.slice(0,8)}`);
|
|
258
276
|
}
|
|
259
277
|
}
|
|
260
278
|
if (choices && sess.presetId === 'claude-code' && msg.menuVersion && (sess._menuConsumedVersion || 0) >= msg.menuVersion) {
|
|
@@ -301,7 +319,7 @@ function onConnection(ws) {
|
|
|
301
319
|
case 'checkAvailability':
|
|
302
320
|
checkAvailability();
|
|
303
321
|
if (detectTelemetryConfig(cfg)) config.save(cfg);
|
|
304
|
-
ws.send(JSON.stringify({ type: 'presets', presets }));
|
|
322
|
+
ws.send(JSON.stringify({ type: 'presets', presets: clientPresets() }));
|
|
305
323
|
break;
|
|
306
324
|
|
|
307
325
|
case 'config.update':
|
package/package.json
CHANGED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="none">
|
|
2
|
+
<title>Clideck Agent</title>
|
|
3
|
+
<defs>
|
|
4
|
+
<linearGradient id="bubbleGradient" x1="64" y1="72" x2="448" y2="408" gradientUnits="userSpaceOnUse">
|
|
5
|
+
<stop offset="0" stop-color="#2C2F38"/>
|
|
6
|
+
<stop offset="1" stop-color="#171A20"/>
|
|
7
|
+
</linearGradient>
|
|
8
|
+
<linearGradient id="glyphGradient" x1="122" y1="150" x2="395" y2="355" gradientUnits="userSpaceOnUse">
|
|
9
|
+
<stop offset="0" stop-color="#FFFFFF"/>
|
|
10
|
+
<stop offset="1" stop-color="#E7EAF0"/>
|
|
11
|
+
</linearGradient>
|
|
12
|
+
<filter id="glyphShadow" x="82" y="126" width="350" height="254" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
|
13
|
+
<feOffset dx="0" dy="10"/>
|
|
14
|
+
<feGaussianBlur stdDeviation="10"/>
|
|
15
|
+
<feColorMatrix type="matrix" values="0 0 0 0 0.03 0 0 0 0 0.04 0 0 0 0 0.07 0 0 0 0.35 0"/>
|
|
16
|
+
<feBlend in2="SourceGraphic" result="shape"/>
|
|
17
|
+
</filter>
|
|
18
|
+
</defs>
|
|
19
|
+
<path d="M120 70H392C435.078 70 470 104.922 470 148V266C470 309.078 435.078 344 392 344H215.642L118.159 416.774C111.875 421.467 102.86 417.2 102.562 409.363L99.996 344H88C52.654 344 24 315.346 24 280V166C24 112.98 66.98 70 120 70Z" fill="url(#bubbleGradient)"/>
|
|
20
|
+
<path d="M120 70H392C435.078 70 470 104.922 470 148V266C470 309.078 435.078 344 392 344H215.642L118.159 416.774C111.875 421.467 102.86 417.2 102.562 409.363L99.996 344H88C52.654 344 24 315.346 24 280V166C24 112.98 66.98 70 120 70Z" stroke="#3E4350" stroke-width="2"/>
|
|
21
|
+
<g filter="url(#glyphShadow)" stroke="url(#glyphGradient)" stroke-linecap="round" stroke-linejoin="round">
|
|
22
|
+
<path d="M134 168L245 256L134 344" stroke-width="40"/>
|
|
23
|
+
<path d="M292 344H390" stroke-width="38"/>
|
|
24
|
+
</g>
|
|
25
|
+
</svg>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="none">
|
|
2
|
+
<title>Clideck Agent</title>
|
|
3
|
+
<defs>
|
|
4
|
+
<linearGradient id="bubbleGradient" x1="64" y1="72" x2="448" y2="408" gradientUnits="userSpaceOnUse">
|
|
5
|
+
<stop offset="0" stop-color="#F7F8FB"/>
|
|
6
|
+
<stop offset="1" stop-color="#E5E9F1"/>
|
|
7
|
+
</linearGradient>
|
|
8
|
+
<linearGradient id="glyphGradient" x1="122" y1="150" x2="395" y2="355" gradientUnits="userSpaceOnUse">
|
|
9
|
+
<stop offset="0" stop-color="#1D212B"/>
|
|
10
|
+
<stop offset="1" stop-color="#3D4656"/>
|
|
11
|
+
</linearGradient>
|
|
12
|
+
<filter id="glyphShadow" x="82" y="126" width="350" height="254" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
|
13
|
+
<feOffset dx="0" dy="6"/>
|
|
14
|
+
<feGaussianBlur stdDeviation="7"/>
|
|
15
|
+
<feColorMatrix type="matrix" values="0 0 0 0 0.47 0 0 0 0 0.53 0 0 0 0 0.63 0 0 0 0.18 0"/>
|
|
16
|
+
<feBlend in2="SourceGraphic" result="shape"/>
|
|
17
|
+
</filter>
|
|
18
|
+
</defs>
|
|
19
|
+
<path d="M120 70H392C435.078 70 470 104.922 470 148V266C470 309.078 435.078 344 392 344H215.642L118.159 416.774C111.875 421.467 102.86 417.2 102.562 409.363L99.996 344H88C52.654 344 24 315.346 24 280V166C24 112.98 66.98 70 120 70Z" fill="url(#bubbleGradient)"/>
|
|
20
|
+
<path d="M120 70H392C435.078 70 470 104.922 470 148V266C470 309.078 435.078 344 392 344H215.642L118.159 416.774C111.875 421.467 102.86 417.2 102.562 409.363L99.996 344H88C52.654 344 24 315.346 24 280V166C24 112.98 66.98 70 120 70Z" stroke="#C6CEDA" stroke-width="2"/>
|
|
21
|
+
<g filter="url(#glyphShadow)" stroke="url(#glyphGradient)" stroke-linecap="round" stroke-linejoin="round">
|
|
22
|
+
<path d="M134 168L245 256L134 344" stroke-width="40"/>
|
|
23
|
+
<path d="M292 344H390" stroke-width="38"/>
|
|
24
|
+
</g>
|
|
25
|
+
</svg>
|
package/public/js/creator.js
CHANGED
|
@@ -27,6 +27,11 @@ function findCommandForPreset(p) {
|
|
|
27
27
|
|| state.cfg.commands.find(c => binName(c.command) === binName(p.command));
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
function telemetryEnabledForPreset(preset, existing) {
|
|
31
|
+
if (preset?.telemetryEnabled === true) return true;
|
|
32
|
+
return !!existing?.telemetryEnabled;
|
|
33
|
+
}
|
|
34
|
+
|
|
30
35
|
// True if preset binary is missing and the configured command is unchanged from the preset default
|
|
31
36
|
function isPresetMissing(p) {
|
|
32
37
|
if (p.available !== false) return false;
|
|
@@ -115,7 +120,7 @@ function createFromPreset(preset, sessionName, cwd, projectId) {
|
|
|
115
120
|
resumeCommand: preset.resumeCommand,
|
|
116
121
|
sessionIdPattern: preset.sessionIdPattern,
|
|
117
122
|
outputMarker: preset.outputMarker || null,
|
|
118
|
-
telemetryEnabled:
|
|
123
|
+
telemetryEnabled: telemetryEnabledForPreset(preset),
|
|
119
124
|
telemetryStatus: null,
|
|
120
125
|
bridge: preset.bridge,
|
|
121
126
|
};
|
|
@@ -270,7 +275,7 @@ export function openCreator() {
|
|
|
270
275
|
if (!preset) return;
|
|
271
276
|
let cmd = findCommandForPreset(preset);
|
|
272
277
|
if (!cmd) {
|
|
273
|
-
cmd = { id: crypto.randomUUID(), presetId: preset.presetId, label: preset.name, icon: preset.icon, command: preset.command, enabled: true, defaultPath: '', isAgent: preset.isAgent, canResume: preset.canResume, resumeCommand: preset.resumeCommand, sessionIdPattern: preset.sessionIdPattern, outputMarker: preset.outputMarker || null, telemetryEnabled:
|
|
278
|
+
cmd = { id: crypto.randomUUID(), presetId: preset.presetId, label: preset.name, icon: preset.icon, command: preset.command, enabled: true, defaultPath: '', isAgent: preset.isAgent, canResume: preset.canResume, resumeCommand: preset.resumeCommand, sessionIdPattern: preset.sessionIdPattern, outputMarker: preset.outputMarker || null, telemetryEnabled: telemetryEnabledForPreset(preset), telemetryStatus: null, bridge: preset.bridge };
|
|
274
279
|
state.cfg.commands.push(cmd);
|
|
275
280
|
send({ type: 'config.update', config: state.cfg });
|
|
276
281
|
}
|
package/public/js/settings.js
CHANGED
|
@@ -24,15 +24,69 @@ document.getElementById('settings-nav').addEventListener('click', (e) => {
|
|
|
24
24
|
if (btn) switchCategory(btn.dataset.cat);
|
|
25
25
|
});
|
|
26
26
|
|
|
27
|
+
function captureSettingsFocus() {
|
|
28
|
+
const active = document.activeElement;
|
|
29
|
+
if (!active || !(active instanceof HTMLElement)) return null;
|
|
30
|
+
if (!active.closest('#panel-settings')) return null;
|
|
31
|
+
|
|
32
|
+
const snapshot = {
|
|
33
|
+
id: active.id || null,
|
|
34
|
+
selector: null,
|
|
35
|
+
cardIdx: null,
|
|
36
|
+
selectionStart: null,
|
|
37
|
+
selectionEnd: null,
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const card = active.closest('.agent-card');
|
|
41
|
+
if (card) {
|
|
42
|
+
snapshot.cardIdx = card.dataset.idx || null;
|
|
43
|
+
for (const cls of ['agent-name', 'agent-command', 'agent-enabled', 'agent-is-agent', 'agent-can-resume', 'agent-resume-cmd']) {
|
|
44
|
+
if (active.classList.contains(cls)) {
|
|
45
|
+
snapshot.selector = `.${cls}`;
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (typeof active.selectionStart === 'number' && typeof active.selectionEnd === 'number') {
|
|
52
|
+
snapshot.selectionStart = active.selectionStart;
|
|
53
|
+
snapshot.selectionEnd = active.selectionEnd;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return snapshot;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function restoreSettingsFocus(snapshot) {
|
|
60
|
+
if (!snapshot) return;
|
|
61
|
+
|
|
62
|
+
let target = null;
|
|
63
|
+
if (snapshot.id) target = document.getElementById(snapshot.id);
|
|
64
|
+
if (!target && snapshot.cardIdx != null && snapshot.selector) {
|
|
65
|
+
target = document.querySelector(`.agent-card[data-idx="${snapshot.cardIdx}"] ${snapshot.selector}`);
|
|
66
|
+
}
|
|
67
|
+
if (!(target instanceof HTMLElement)) return;
|
|
68
|
+
|
|
69
|
+
target.focus({ preventScroll: true });
|
|
70
|
+
if (
|
|
71
|
+
typeof snapshot.selectionStart === 'number' &&
|
|
72
|
+
typeof snapshot.selectionEnd === 'number' &&
|
|
73
|
+
typeof target.setSelectionRange === 'function'
|
|
74
|
+
) {
|
|
75
|
+
target.setSelectionRange(snapshot.selectionStart, snapshot.selectionEnd);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
27
79
|
// ── Render all ──
|
|
28
80
|
|
|
29
81
|
export function renderSettings() {
|
|
82
|
+
const focusSnapshot = captureSettingsFocus();
|
|
30
83
|
document.getElementById('cfg-default-path').value = state.cfg.defaultPath || '';
|
|
31
84
|
document.getElementById('cfg-confirm-close').checked = state.cfg.confirmClose !== false;
|
|
32
85
|
renderAgentList();
|
|
33
86
|
renderThemeSection();
|
|
34
87
|
renderNotifications();
|
|
35
88
|
updateVersionFooter();
|
|
89
|
+
restoreSettingsFocus(focusSnapshot);
|
|
36
90
|
}
|
|
37
91
|
|
|
38
92
|
export function updateVersionFooter() {
|
|
@@ -107,6 +161,22 @@ function telemetryPreset(cmd) {
|
|
|
107
161
|
return (state.presets || []).find(p => binName(p.command) === bin);
|
|
108
162
|
}
|
|
109
163
|
|
|
164
|
+
function presetForCommand(existing, command) {
|
|
165
|
+
const presets = state.presets || [];
|
|
166
|
+
if (existing?.presetId) {
|
|
167
|
+
const byId = presets.find(p => p.presetId === existing.presetId);
|
|
168
|
+
if (byId) return byId;
|
|
169
|
+
}
|
|
170
|
+
const bin = binName(command || existing?.command || '');
|
|
171
|
+
return presets.find(p => binName(p.command) === bin) || null;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function telemetryEnabledForCommand(existing, command) {
|
|
175
|
+
const preset = presetForCommand(existing, command);
|
|
176
|
+
if (preset?.telemetryEnabled === true) return true;
|
|
177
|
+
return !!existing?.telemetryEnabled;
|
|
178
|
+
}
|
|
179
|
+
|
|
110
180
|
function integrationSection(c) {
|
|
111
181
|
const preset = telemetryPreset(c);
|
|
112
182
|
if (!preset) return '';
|
|
@@ -217,7 +287,7 @@ function openPresetMenu(anchorEl) {
|
|
|
217
287
|
enabled: true, defaultPath: '', isAgent: p.isAgent, canResume: p.canResume,
|
|
218
288
|
resumeCommand: p.resumeCommand, sessionIdPattern: p.sessionIdPattern,
|
|
219
289
|
outputMarker: p.outputMarker || null,
|
|
220
|
-
telemetryEnabled:
|
|
290
|
+
telemetryEnabled: telemetryEnabledForCommand({ presetId: p.presetId, command: p.command }, p.command),
|
|
221
291
|
telemetryStatus: null,
|
|
222
292
|
bridge: p.bridge,
|
|
223
293
|
});
|
|
@@ -453,7 +523,7 @@ function saveConfig() {
|
|
|
453
523
|
resumeCommand: card.querySelector('.agent-resume-cmd')?.value.trim() || null,
|
|
454
524
|
sessionIdPattern: existing.sessionIdPattern || null,
|
|
455
525
|
outputMarker: existing.outputMarker || null,
|
|
456
|
-
telemetryEnabled: existing
|
|
526
|
+
telemetryEnabled: telemetryEnabledForCommand(existing, command),
|
|
457
527
|
telemetryStatus: existing.telemetryStatus || null,
|
|
458
528
|
bridge: existing.bridge,
|
|
459
529
|
};
|
package/public/js/terminals.js
CHANGED
|
@@ -185,6 +185,7 @@ function openMenu(sessionId, anchor) {
|
|
|
185
185
|
// Project submenu items
|
|
186
186
|
if (projects.length) {
|
|
187
187
|
html += `<div class="px-3 py-1 text-[10px] font-semibold uppercase tracking-wider text-slate-600">Move to project</div>`;
|
|
188
|
+
html += `<div class="tmx-scroll py-0.5" style="max-height:10rem;overflow-y:auto">`;
|
|
188
189
|
for (const p of projects) {
|
|
189
190
|
const active = entry?.projectId === p.id;
|
|
190
191
|
html += `<button class="menu-action flex items-center gap-2 w-full px-3 py-1.5 text-sm ${active ? 'text-blue-400' : 'text-slate-300'} hover:bg-slate-700 transition-colors text-left" data-action="project" data-project-id="${p.id}">
|
|
@@ -192,6 +193,7 @@ function openMenu(sessionId, anchor) {
|
|
|
192
193
|
${esc(p.name)}${active ? ' ✓' : ''}
|
|
193
194
|
</button>`;
|
|
194
195
|
}
|
|
196
|
+
html += `</div>`;
|
|
195
197
|
if (entry?.projectId) {
|
|
196
198
|
html += `<button class="menu-action flex items-center gap-2 w-full px-3 py-1.5 text-sm text-slate-500 hover:bg-slate-700 transition-colors text-left" data-action="unproject">
|
|
197
199
|
<span class="w-2 h-2 rounded-full flex-shrink-0 border border-slate-600"></span>
|
package/public/js/utils.js
CHANGED
|
@@ -29,6 +29,7 @@ const ICON_VARIANTS = {
|
|
|
29
29
|
'/img/codex.png': { dark: '/img/codex-dark.png', light: '/img/codex-light.png' },
|
|
30
30
|
'/img/gemini.png': { all: '/img/gemini-all.png' },
|
|
31
31
|
'/img/opencode.png': { all: '/img/opencode-all.png' },
|
|
32
|
+
'/img/clideck-agent.svg': { dark: '/img/clideck-agent-dark.svg', light: '/img/clideck-agent-light.svg' },
|
|
32
33
|
};
|
|
33
34
|
|
|
34
35
|
export function resolveIconPath(icon) {
|
package/sessions.js
CHANGED
|
@@ -93,7 +93,7 @@ function spawnSession(id, cmd, parts, cwd, name, themeId, commandId, savedToken,
|
|
|
93
93
|
const preset = PRESETS.find(p => binName(p.command) === bin);
|
|
94
94
|
const session = { name, themeId, commandId, cwd, pty: term, chunks: [], chunksSize: 0, sessionToken: savedToken || null, projectId: projectId || null, presetId: preset?.presetId || 'shell', working: undefined };
|
|
95
95
|
sessions.set(id, session);
|
|
96
|
-
transcript.setFinalizeOnIdle(id, ['claude-code', 'codex', 'gemini-cli', 'opencode'].includes(session.presetId) ? session.presetId : null);
|
|
96
|
+
transcript.setFinalizeOnIdle(id, ['claude-code', 'codex', 'gemini-cli', 'opencode', 'clideck-agent'].includes(session.presetId) ? session.presetId : null);
|
|
97
97
|
|
|
98
98
|
// Always watch telemetry-backed agents so OTLP fallback matching can attach
|
|
99
99
|
// early events to this session even when the agent omits clideck.session_id.
|
|
@@ -445,7 +445,7 @@ function getResumable(cfg) {
|
|
|
445
445
|
|
|
446
446
|
function sendBuffers(ws) {
|
|
447
447
|
for (const [id, s] of sessions) {
|
|
448
|
-
if (['claude-code', 'codex', 'gemini-cli', 'opencode'].includes(s.presetId) && !s.working) {
|
|
448
|
+
if (['claude-code', 'codex', 'gemini-cli', 'opencode', 'clideck-agent'].includes(s.presetId) && !s.working) {
|
|
449
449
|
const text = transcript.getReplayText(id, s.presetId);
|
|
450
450
|
if (text) {
|
|
451
451
|
ws.send(JSON.stringify({ type: 'session.history', id, text, replay: true }));
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: autonomous-session
|
|
3
|
+
description: Run one agent session autonomously until a clearly defined goal is achieved or truly blocked. Use when the user wants the agent to keep working without repeated check-ins, across coding, research, writing, analysis, marketing, planning, or other general session work.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Autonomous Session
|
|
7
|
+
|
|
8
|
+
Use this skill when the user wants one agent session to keep working toward a goal without stopping for routine confirmation.
|
|
9
|
+
|
|
10
|
+
This skill is general-purpose. It is not limited to coding. It applies to research, writing, planning, analysis, marketing, documentation, and other session-based work.
|
|
11
|
+
|
|
12
|
+
## Core Contract
|
|
13
|
+
|
|
14
|
+
You are not waiting for permission after every step.
|
|
15
|
+
|
|
16
|
+
- Understand the goal precisely.
|
|
17
|
+
- Keep working until the goal is achieved or truly blocked.
|
|
18
|
+
- Do not stop just to ask whether you should continue.
|
|
19
|
+
- Do not produce filler progress updates instead of real work.
|
|
20
|
+
- Do not redefine the goal casually once work has started.
|
|
21
|
+
|
|
22
|
+
## First Check: Can This Session Actually Run Autonomously?
|
|
23
|
+
|
|
24
|
+
Before doing substantial work, verify that the session can proceed without routine approval interruptions.
|
|
25
|
+
|
|
26
|
+
If the environment clearly requires recurring user approval for normal work, stop immediately and tell the user, in plain language, that autonomous mode cannot work until approvals are removed or auto-approval is enabled.
|
|
27
|
+
|
|
28
|
+
Do not try to fake autonomy in a session that will keep pausing for user approval.
|
|
29
|
+
|
|
30
|
+
## Goal Setup
|
|
31
|
+
|
|
32
|
+
Before starting the main work, convert the user request into a concrete internal brief.
|
|
33
|
+
|
|
34
|
+
Your internal brief must include:
|
|
35
|
+
|
|
36
|
+
- Goal: what outcome must exist at the end.
|
|
37
|
+
- Constraints: what must not happen.
|
|
38
|
+
- Definition of done: how you will recognize completion.
|
|
39
|
+
- Open questions: only the questions that would materially change the work.
|
|
40
|
+
|
|
41
|
+
If the goal is already clear enough, do not ask questions. Start working.
|
|
42
|
+
|
|
43
|
+
If the goal is not clear enough to work effectively, ask a small batch of targeted clarifying questions first. Ask only what is necessary to remove ambiguity. Do not interrogate the user unnecessarily.
|
|
44
|
+
|
|
45
|
+
Examples of when to ask questions first:
|
|
46
|
+
|
|
47
|
+
- The user wants research but has not said what decision the research is meant to support.
|
|
48
|
+
- The user wants writing but has not said audience, tone, or deliverable type.
|
|
49
|
+
- The user wants implementation but has not said what behavior should change.
|
|
50
|
+
- The user wants “improve this” but there is no clear success condition.
|
|
51
|
+
|
|
52
|
+
Examples of when not to ask questions first:
|
|
53
|
+
|
|
54
|
+
- The user already gave a concrete task, constraints, and acceptance bar.
|
|
55
|
+
- You can infer a safe, reasonable working interpretation without risking the wrong output.
|
|
56
|
+
|
|
57
|
+
## Goal Memory
|
|
58
|
+
|
|
59
|
+
If the task involves a real project or working directory, create and maintain a short session memory file early in the process.
|
|
60
|
+
|
|
61
|
+
Preferred filename:
|
|
62
|
+
|
|
63
|
+
```text
|
|
64
|
+
autonomous-session.md
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Keep it concise and practical. It should contain:
|
|
68
|
+
|
|
69
|
+
- Goal
|
|
70
|
+
- Constraints
|
|
71
|
+
- Definition of done
|
|
72
|
+
- Key decisions
|
|
73
|
+
- What has been completed
|
|
74
|
+
- Remaining gaps
|
|
75
|
+
|
|
76
|
+
If a different filename is more natural in the project, use that instead.
|
|
77
|
+
|
|
78
|
+
If there is no meaningful project directory or file-based workflow, maintain the same structure mentally and in your session responses instead of forcing a file.
|
|
79
|
+
|
|
80
|
+
Update this memory whenever the goal changes materially or when major decisions are made. Keep it short. It is a working anchor, not a diary.
|
|
81
|
+
|
|
82
|
+
## Operating Rules
|
|
83
|
+
|
|
84
|
+
### 1. Work Forward
|
|
85
|
+
|
|
86
|
+
After the goal is clear, move directly into execution.
|
|
87
|
+
|
|
88
|
+
- Prefer real progress over commentary.
|
|
89
|
+
- Prefer concrete artifacts over abstract planning.
|
|
90
|
+
- Prefer finishing a meaningful chunk before reporting back.
|
|
91
|
+
|
|
92
|
+
### 2. Do Not Stop Early
|
|
93
|
+
|
|
94
|
+
Do not ask:
|
|
95
|
+
|
|
96
|
+
- “Should I continue?”
|
|
97
|
+
- “Do you want me to keep going?”
|
|
98
|
+
- “Is this enough for now?”
|
|
99
|
+
|
|
100
|
+
The default is to continue until done or blocked.
|
|
101
|
+
|
|
102
|
+
### 3. Use Judgment, Not Blind Persistence
|
|
103
|
+
|
|
104
|
+
Autonomy does not mean mindless looping.
|
|
105
|
+
|
|
106
|
+
If an approach is not working:
|
|
107
|
+
|
|
108
|
+
- reassess,
|
|
109
|
+
- read the available context again,
|
|
110
|
+
- change tactics,
|
|
111
|
+
- keep moving.
|
|
112
|
+
|
|
113
|
+
Do not repeat the same failing pattern without learning from it.
|
|
114
|
+
|
|
115
|
+
### 4. Respect the Actual Task
|
|
116
|
+
|
|
117
|
+
Do not turn every task into implementation work.
|
|
118
|
+
|
|
119
|
+
- For research tasks, gather, compare, synthesize, and conclude.
|
|
120
|
+
- For writing tasks, produce and refine the requested deliverable.
|
|
121
|
+
- For planning tasks, produce the plan and then, if appropriate, execute it.
|
|
122
|
+
- For coding tasks, inspect, implement, verify, and refine.
|
|
123
|
+
|
|
124
|
+
Stay aligned with the actual requested outcome.
|
|
125
|
+
|
|
126
|
+
### 5. Completion Must Be Real
|
|
127
|
+
|
|
128
|
+
Only stop when one of these is true:
|
|
129
|
+
|
|
130
|
+
- The goal is achieved.
|
|
131
|
+
- The remaining blocker genuinely requires user input, approval, credentials, or a missing external dependency.
|
|
132
|
+
- The request is fundamentally ambiguous and cannot be resolved safely without clarification.
|
|
133
|
+
|
|
134
|
+
When stopping, say which of those is true.
|
|
135
|
+
|
|
136
|
+
## Blockers
|
|
137
|
+
|
|
138
|
+
A blocker is real only if you cannot reasonably move forward without the user.
|
|
139
|
+
|
|
140
|
+
Examples of real blockers:
|
|
141
|
+
|
|
142
|
+
- Missing credentials, API keys, access, or required permissions.
|
|
143
|
+
- A strategic choice the user must make because it changes the deliverable substantially.
|
|
144
|
+
- Multiple valid directions remain and choosing one would be risky or expensive without user preference.
|
|
145
|
+
- The environment prevents autonomous execution through approval prompts or missing capabilities.
|
|
146
|
+
|
|
147
|
+
Examples of non-blockers:
|
|
148
|
+
|
|
149
|
+
- You are unsure which tactic is best.
|
|
150
|
+
- The first attempt failed.
|
|
151
|
+
- More reading is needed.
|
|
152
|
+
- The work is tedious.
|
|
153
|
+
- You can make a reasonable assumption and continue.
|
|
154
|
+
|
|
155
|
+
## Interruptions And Mid-Task User Messages
|
|
156
|
+
|
|
157
|
+
If the user sends a new message during the work:
|
|
158
|
+
|
|
159
|
+
- incorporate it,
|
|
160
|
+
- update the goal brief if needed,
|
|
161
|
+
- continue from the new state.
|
|
162
|
+
|
|
163
|
+
Do not stubbornly follow an outdated plan when the user has clarified or redirected the work.
|
|
164
|
+
|
|
165
|
+
If the new message changes the goal materially, update the session memory file.
|
|
166
|
+
|
|
167
|
+
## Reporting Style
|
|
168
|
+
|
|
169
|
+
Use sparse, meaningful updates.
|
|
170
|
+
|
|
171
|
+
Good updates:
|
|
172
|
+
|
|
173
|
+
- major completed milestone,
|
|
174
|
+
- important decision,
|
|
175
|
+
- real blocker,
|
|
176
|
+
- final completion summary.
|
|
177
|
+
|
|
178
|
+
Bad updates:
|
|
179
|
+
|
|
180
|
+
- empty status chatter,
|
|
181
|
+
- repeated reassurance,
|
|
182
|
+
- asking for permission to continue.
|
|
183
|
+
|
|
184
|
+
## Completion Message
|
|
185
|
+
|
|
186
|
+
When the goal is done, report:
|
|
187
|
+
|
|
188
|
+
- what was accomplished,
|
|
189
|
+
- any important decisions or tradeoffs,
|
|
190
|
+
- any residual risks or follow-up items.
|
|
191
|
+
|
|
192
|
+
Keep it concise and outcome-focused.
|
|
193
|
+
|
|
194
|
+
## Behavior Summary
|
|
195
|
+
|
|
196
|
+
If this skill is active, your default behavior is:
|
|
197
|
+
|
|
198
|
+
1. Check whether autonomous execution is actually possible.
|
|
199
|
+
2. Clarify the goal only if necessary.
|
|
200
|
+
3. Create a short working brief or memory file when appropriate.
|
|
201
|
+
4. Work continuously toward the goal.
|
|
202
|
+
5. Adapt when an approach fails.
|
|
203
|
+
6. Stop only when done or truly blocked.
|
|
204
|
+
|
|
205
|
+
## Hard Rules
|
|
206
|
+
|
|
207
|
+
- Do not ask “should I continue?”
|
|
208
|
+
- Do not stop because of ordinary uncertainty.
|
|
209
|
+
- Do not continue blindly when the goal is unclear.
|
|
210
|
+
- Do not pretend a blocker exists when you can still make progress.
|
|
211
|
+
- Do not treat a status update as completion.
|
package/telemetry-receiver.js
CHANGED
|
@@ -71,7 +71,7 @@ function handleLogs(req, res) {
|
|
|
71
71
|
const resAttrs = parseAttrs(rl.resource?.attributes);
|
|
72
72
|
const sessionId = resAttrs['clideck.session_id'];
|
|
73
73
|
|
|
74
|
-
// service.name values: claude-code, codex_cli_rs, gemini-cli
|
|
74
|
+
// service.name values: claude-code, codex_cli_rs, gemini-cli, clideck-agent
|
|
75
75
|
const serviceName = resAttrs['service.name'] || 'unknown';
|
|
76
76
|
let resolvedId = sessionId;
|
|
77
77
|
|
|
@@ -135,6 +135,13 @@ function handleLogs(req, res) {
|
|
|
135
135
|
broadcastFn?.({ type: 'session.status', id: resolvedId, working: true, source: 'telemetry' });
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
+
if (serviceName === 'clideck-agent' && eventName === 'clideck.turn_start') {
|
|
139
|
+
broadcastFn?.({ type: 'session.status', id: resolvedId, working: true, source: 'telemetry' });
|
|
140
|
+
}
|
|
141
|
+
if (serviceName === 'clideck-agent' && eventName === 'clideck.agent_idle') {
|
|
142
|
+
broadcastFn?.({ type: 'session.status', id: resolvedId, working: false, source: 'telemetry' });
|
|
143
|
+
}
|
|
144
|
+
|
|
138
145
|
// Codex can announce a function-call phase before the later tool_decision
|
|
139
146
|
// event carries a call_id. Block idle as soon as the tool phase is known,
|
|
140
147
|
// then refine it to call-specific tracking when tool_decision arrives.
|