clementine-agent 1.18.91 → 1.18.92
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/dist/agent/run-agent.js +28 -0
- package/dist/agent/subagent-backfill.d.ts +51 -0
- package/dist/agent/subagent-backfill.js +177 -0
- package/dist/cli/dashboard.js +93 -9
- package/dist/config/clementine-json.d.ts +19 -0
- package/dist/config/clementine-json.js +22 -0
- package/dist/types.d.ts +11 -1
- package/package.json +1 -1
package/dist/agent/run-agent.js
CHANGED
|
@@ -489,6 +489,34 @@ export async function runAgent(prompt, opts) {
|
|
|
489
489
|
durationMs: Date.now() - startedAt,
|
|
490
490
|
finalTextChars: finalText.length,
|
|
491
491
|
}, 'runAgent: query complete');
|
|
492
|
+
// PRD §6 Phase 4e: subagent transcript backfill (Path C). The SDK persists
|
|
493
|
+
// every subagent's full message stream to ~/.claude/projects/<encoded-cwd>/
|
|
494
|
+
// <sessionId>/subagents/agent-*.jsonl. Path A only sees the parent's Task
|
|
495
|
+
// tool_use, so subagent-internal LLM/tool calls are invisible without this.
|
|
496
|
+
// Best-effort — telemetry must never block the run from returning.
|
|
497
|
+
try {
|
|
498
|
+
const { backfillSubagentEvents } = await import('./subagent-backfill.js');
|
|
499
|
+
const projectCwd = sdkOptionsRaw.cwd || BASE_DIR;
|
|
500
|
+
const backfillResult = await backfillSubagentEvents({
|
|
501
|
+
runId,
|
|
502
|
+
sessionId,
|
|
503
|
+
cwd: projectCwd,
|
|
504
|
+
eventLog,
|
|
505
|
+
startSeq: eventSeq,
|
|
506
|
+
});
|
|
507
|
+
eventSeq += backfillResult.backfilled;
|
|
508
|
+
if (backfillResult.backfilled > 0) {
|
|
509
|
+
logger.info({
|
|
510
|
+
runId,
|
|
511
|
+
sessionId,
|
|
512
|
+
backfilled: backfillResult.backfilled,
|
|
513
|
+
agents: backfillResult.agents,
|
|
514
|
+
}, 'runAgent: subagent backfill (Path C) complete');
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
catch (err) {
|
|
518
|
+
logger.debug({ err }, 'runAgent: subagent backfill failed (non-fatal)');
|
|
519
|
+
}
|
|
492
520
|
return {
|
|
493
521
|
text: finalText,
|
|
494
522
|
totalCostUsd,
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PRD §6 / Phase 4e — Path C: subagent transcript backfill.
|
|
3
|
+
*
|
|
4
|
+
* The Claude Agent SDK persists every subagent's full message stream to
|
|
5
|
+
* ~/.claude/projects/<encoded-cwd>/<sessionId>/subagents/agent-*.jsonl
|
|
6
|
+
*
|
|
7
|
+
* The parent run's in-process tap (path A in `run-agent.ts`) only sees the
|
|
8
|
+
* top-level Task tool_use + tool_result, so subagent-internal LLM/tool calls
|
|
9
|
+
* are invisible in the Run detail waterfall. After the parent run ends, this
|
|
10
|
+
* module reads any matching agent-*.jsonl files for the run's sessionId and
|
|
11
|
+
* appends synthesized Event rows so the waterfall can render nested subagent
|
|
12
|
+
* activity.
|
|
13
|
+
*
|
|
14
|
+
* Best-effort by design — never throws back to the caller. Missing dir / parse
|
|
15
|
+
* errors / timing skew are all acceptable; the worst case is the parent run
|
|
16
|
+
* looks the same as before the backfill.
|
|
17
|
+
*/
|
|
18
|
+
import type { EventLog } from '../gateway/event-log.js';
|
|
19
|
+
/**
|
|
20
|
+
* Encode a cwd path the way the SDK does for `~/.claude/projects/<encoded>`:
|
|
21
|
+
* every slash, backslash, and whitespace character becomes `-`. Confirmed
|
|
22
|
+
* against existing on-disk dirs (e.g. paths containing spaces and `..`).
|
|
23
|
+
*/
|
|
24
|
+
export declare function encodeProjectCwd(cwd: string): string;
|
|
25
|
+
interface BackfillResult {
|
|
26
|
+
/** Number of synthesized RunEvent rows appended to the run's event log. */
|
|
27
|
+
backfilled: number;
|
|
28
|
+
/** Number of agent-*.jsonl files parsed. */
|
|
29
|
+
agents: number;
|
|
30
|
+
/** Resolved subagents directory (helpful in audit logs when nothing matches). */
|
|
31
|
+
scannedDir: string | null;
|
|
32
|
+
}
|
|
33
|
+
interface BackfillOpts {
|
|
34
|
+
runId: string;
|
|
35
|
+
sessionId: string;
|
|
36
|
+
cwd: string;
|
|
37
|
+
/** Pass the parent run's EventLog so we can append in-place. */
|
|
38
|
+
eventLog: EventLog;
|
|
39
|
+
/** Sequence number to start at. The caller already wrote N events; we
|
|
40
|
+
* continue from there to keep the file ordered. */
|
|
41
|
+
startSeq: number;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Scan ~/.claude/projects/<encoded(cwd)>/<sessionId>/subagents/agent-*.jsonl
|
|
45
|
+
* and append synthesized RunEvent rows to the parent run's event log.
|
|
46
|
+
*
|
|
47
|
+
* The function is fire-and-forget from runAgent's POV — it never rejects.
|
|
48
|
+
*/
|
|
49
|
+
export declare function backfillSubagentEvents(opts: BackfillOpts): Promise<BackfillResult>;
|
|
50
|
+
export {};
|
|
51
|
+
//# sourceMappingURL=subagent-backfill.d.ts.map
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PRD §6 / Phase 4e — Path C: subagent transcript backfill.
|
|
3
|
+
*
|
|
4
|
+
* The Claude Agent SDK persists every subagent's full message stream to
|
|
5
|
+
* ~/.claude/projects/<encoded-cwd>/<sessionId>/subagents/agent-*.jsonl
|
|
6
|
+
*
|
|
7
|
+
* The parent run's in-process tap (path A in `run-agent.ts`) only sees the
|
|
8
|
+
* top-level Task tool_use + tool_result, so subagent-internal LLM/tool calls
|
|
9
|
+
* are invisible in the Run detail waterfall. After the parent run ends, this
|
|
10
|
+
* module reads any matching agent-*.jsonl files for the run's sessionId and
|
|
11
|
+
* appends synthesized Event rows so the waterfall can render nested subagent
|
|
12
|
+
* activity.
|
|
13
|
+
*
|
|
14
|
+
* Best-effort by design — never throws back to the caller. Missing dir / parse
|
|
15
|
+
* errors / timing skew are all acceptable; the worst case is the parent run
|
|
16
|
+
* looks the same as before the backfill.
|
|
17
|
+
*/
|
|
18
|
+
import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
19
|
+
import os from 'node:os';
|
|
20
|
+
import path from 'node:path';
|
|
21
|
+
import pino from 'pino';
|
|
22
|
+
const logger = pino({ name: 'clementine.subagent-backfill' });
|
|
23
|
+
/**
|
|
24
|
+
* Encode a cwd path the way the SDK does for `~/.claude/projects/<encoded>`:
|
|
25
|
+
* every slash, backslash, and whitespace character becomes `-`. Confirmed
|
|
26
|
+
* against existing on-disk dirs (e.g. paths containing spaces and `..`).
|
|
27
|
+
*/
|
|
28
|
+
export function encodeProjectCwd(cwd) {
|
|
29
|
+
return cwd.replace(/[/\\\s.]/g, '-');
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Walk a single agent-*.jsonl file and synthesize RunEvent rows.
|
|
33
|
+
* Returns the events as a flat array; the caller appends them to the
|
|
34
|
+
* shared event log so we can offset their `seq` correctly.
|
|
35
|
+
*/
|
|
36
|
+
function synthesizeFromFile(filePath, runId) {
|
|
37
|
+
const out = [];
|
|
38
|
+
let raw;
|
|
39
|
+
try {
|
|
40
|
+
raw = readFileSync(filePath, 'utf-8');
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
logger.debug({ err, filePath }, 'subagent-backfill: read failed');
|
|
44
|
+
return out;
|
|
45
|
+
}
|
|
46
|
+
const lines = raw.split('\n');
|
|
47
|
+
// The agentId in the filename (agent-<id>.jsonl) is the stable handle for
|
|
48
|
+
// this subagent; we tag every synthesized event with it so the waterfall
|
|
49
|
+
// can group them under one swimlane.
|
|
50
|
+
const baseName = path.basename(filePath, '.jsonl'); // "agent-a333f70"
|
|
51
|
+
const agentId = baseName.replace(/^agent-/, '');
|
|
52
|
+
for (const line of lines) {
|
|
53
|
+
if (!line.trim())
|
|
54
|
+
continue;
|
|
55
|
+
let parsed;
|
|
56
|
+
try {
|
|
57
|
+
parsed = JSON.parse(line);
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
// Tolerate the rare half-flushed line at EOF.
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
const ts = parsed.timestamp || new Date().toISOString();
|
|
64
|
+
const slug = parsed.slug;
|
|
65
|
+
const subagentId = parsed.agentId || agentId;
|
|
66
|
+
const blocks = parsed.message?.content;
|
|
67
|
+
if (!Array.isArray(blocks))
|
|
68
|
+
continue;
|
|
69
|
+
for (const block of blocks) {
|
|
70
|
+
// Build the same RunEvent kinds the parent run uses, but tagged with
|
|
71
|
+
// agentId/subagentSlug + source='backfill' so the Run detail viewer
|
|
72
|
+
// can render them in a nested swimlane. seq is filled in by the
|
|
73
|
+
// caller; common fields go through to match path A's shape.
|
|
74
|
+
const common = {
|
|
75
|
+
runId,
|
|
76
|
+
ts,
|
|
77
|
+
agentId: subagentId,
|
|
78
|
+
subagentSlug: slug,
|
|
79
|
+
source: 'backfill',
|
|
80
|
+
};
|
|
81
|
+
if (block.type === 'text' && block.text) {
|
|
82
|
+
out.push({ ...common, kind: 'llm_text', text: block.text, seq: -1 });
|
|
83
|
+
}
|
|
84
|
+
else if (block.type === 'thinking' && block.thinking) {
|
|
85
|
+
out.push({ ...common, kind: 'thinking', thinking: block.thinking, seq: -1 });
|
|
86
|
+
}
|
|
87
|
+
else if (block.type === 'tool_use') {
|
|
88
|
+
out.push({
|
|
89
|
+
...common,
|
|
90
|
+
kind: 'tool_call',
|
|
91
|
+
toolName: block.name || 'unknown',
|
|
92
|
+
toolUseId: block.id,
|
|
93
|
+
toolInput: block.input,
|
|
94
|
+
seq: -1,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
else if (block.type === 'tool_result') {
|
|
98
|
+
const previewSrc = typeof block.content === 'string'
|
|
99
|
+
? block.content
|
|
100
|
+
: JSON.stringify(block.content ?? '');
|
|
101
|
+
out.push({
|
|
102
|
+
...common,
|
|
103
|
+
kind: 'tool_result',
|
|
104
|
+
toolUseId: block.tool_use_id,
|
|
105
|
+
// truncate huge tool_results to keep event-log size sane
|
|
106
|
+
toolResult: previewSrc.slice(0, 4000),
|
|
107
|
+
...(block.is_error ? { toolError: previewSrc.slice(0, 1000) } : {}),
|
|
108
|
+
seq: -1,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return out;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Scan ~/.claude/projects/<encoded(cwd)>/<sessionId>/subagents/agent-*.jsonl
|
|
117
|
+
* and append synthesized RunEvent rows to the parent run's event log.
|
|
118
|
+
*
|
|
119
|
+
* The function is fire-and-forget from runAgent's POV — it never rejects.
|
|
120
|
+
*/
|
|
121
|
+
export async function backfillSubagentEvents(opts) {
|
|
122
|
+
const { runId, sessionId, cwd, eventLog, startSeq } = opts;
|
|
123
|
+
const result = { backfilled: 0, agents: 0, scannedDir: null };
|
|
124
|
+
if (!sessionId || !cwd)
|
|
125
|
+
return result;
|
|
126
|
+
let projectsRoot;
|
|
127
|
+
try {
|
|
128
|
+
projectsRoot = path.join(os.homedir(), '.claude', 'projects');
|
|
129
|
+
if (!existsSync(projectsRoot))
|
|
130
|
+
return result;
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
return result;
|
|
134
|
+
}
|
|
135
|
+
const encoded = encodeProjectCwd(cwd);
|
|
136
|
+
const subDir = path.join(projectsRoot, encoded, sessionId, 'subagents');
|
|
137
|
+
result.scannedDir = subDir;
|
|
138
|
+
if (!existsSync(subDir))
|
|
139
|
+
return result;
|
|
140
|
+
let files;
|
|
141
|
+
try {
|
|
142
|
+
files = readdirSync(subDir).filter((f) => f.startsWith('agent-') && f.endsWith('.jsonl'));
|
|
143
|
+
}
|
|
144
|
+
catch (err) {
|
|
145
|
+
logger.debug({ err, subDir }, 'subagent-backfill: readdir failed');
|
|
146
|
+
return result;
|
|
147
|
+
}
|
|
148
|
+
if (files.length === 0)
|
|
149
|
+
return result;
|
|
150
|
+
// Aggregate across all subagent files, then sort by ts so the waterfall
|
|
151
|
+
// renders in chronological order regardless of which agent file we read first.
|
|
152
|
+
const all = [];
|
|
153
|
+
for (const f of files) {
|
|
154
|
+
const fp = path.join(subDir, f);
|
|
155
|
+
const synthesized = synthesizeFromFile(fp, runId);
|
|
156
|
+
if (synthesized.length > 0) {
|
|
157
|
+
all.push(...synthesized);
|
|
158
|
+
result.agents += 1;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
all.sort((a, b) => {
|
|
162
|
+
const ta = a.ts ?? '';
|
|
163
|
+
const tb = b.ts ?? '';
|
|
164
|
+
return ta < tb ? -1 : ta > tb ? 1 : 0;
|
|
165
|
+
});
|
|
166
|
+
// Stamp seq + append. EventLog.append swallows its own errors so we just
|
|
167
|
+
// call it in a loop. The starting seq comes from the caller (the parent
|
|
168
|
+
// run's last writeEvent counter) so backfill rows sort after live rows.
|
|
169
|
+
let seq = startSeq;
|
|
170
|
+
for (const ev of all) {
|
|
171
|
+
ev.seq = seq++;
|
|
172
|
+
eventLog.append(ev);
|
|
173
|
+
result.backfilled += 1;
|
|
174
|
+
}
|
|
175
|
+
return result;
|
|
176
|
+
}
|
|
177
|
+
//# sourceMappingURL=subagent-backfill.js.map
|
package/dist/cli/dashboard.js
CHANGED
|
@@ -5925,10 +5925,11 @@ If the tool returns nothing or errors, return an empty array \`[]\`.`,
|
|
|
5925
5925
|
}
|
|
5926
5926
|
});
|
|
5927
5927
|
// ── Available Tools ──────────────────────────────────────────
|
|
5928
|
-
app.get('/api/available-tools', (_req, res) => {
|
|
5928
|
+
app.get('/api/available-tools', async (_req, res) => {
|
|
5929
5929
|
try {
|
|
5930
|
-
const data =
|
|
5930
|
+
const data = await cachedAsync('available-tools', 60_000, async () => {
|
|
5931
5931
|
const apiStatus = getApiConnectionStatus();
|
|
5932
|
+
let composioError = null;
|
|
5932
5933
|
const categories = {
|
|
5933
5934
|
'CLI Tools': discoverCliTools(),
|
|
5934
5935
|
'Core SDK': [
|
|
@@ -6016,19 +6017,92 @@ If the tool returns nothing or errors, return an empty array \`[]\`.`,
|
|
|
6016
6017
|
if (globalMcp.length > 0) {
|
|
6017
6018
|
categories['Global MCP Servers'] = globalMcp;
|
|
6018
6019
|
}
|
|
6019
|
-
//
|
|
6020
|
-
|
|
6020
|
+
// Local Projects: surface every linked project as a single togglable
|
|
6021
|
+
// entry so an agent can be granted access to a whole project (and the
|
|
6022
|
+
// dashboard can render the project's MCP servers as nested children).
|
|
6023
|
+
// Source of truth is `cachedProjects`, populated by the background
|
|
6024
|
+
// workspace scanner.
|
|
6021
6025
|
const projects = cachedProjects ?? [];
|
|
6026
|
+
if (projects.length > 0) {
|
|
6027
|
+
categories['Local Projects'] = projects.map((p) => {
|
|
6028
|
+
const exists = (() => {
|
|
6029
|
+
try {
|
|
6030
|
+
return existsSync(p.path);
|
|
6031
|
+
}
|
|
6032
|
+
catch {
|
|
6033
|
+
return false;
|
|
6034
|
+
}
|
|
6035
|
+
})();
|
|
6036
|
+
return {
|
|
6037
|
+
name: `project:${p.path}`,
|
|
6038
|
+
description: p.description || p.path,
|
|
6039
|
+
type: 'project',
|
|
6040
|
+
path: p.path,
|
|
6041
|
+
projectName: p.name,
|
|
6042
|
+
connected: exists,
|
|
6043
|
+
mcpServers: Array.isArray(p.mcpServers) ? p.mcpServers : [],
|
|
6044
|
+
hasClaude: !!p.hasClaude,
|
|
6045
|
+
};
|
|
6046
|
+
});
|
|
6047
|
+
}
|
|
6048
|
+
// Project-scoped MCP servers — flatten what was previously a sibling
|
|
6049
|
+
// `projectMcp` map (which the renderer ignored) into one category per
|
|
6050
|
+
// server, so they show up alongside everything else in the picker.
|
|
6051
|
+
const projectMcpByServer = {};
|
|
6022
6052
|
for (const p of projects) {
|
|
6023
6053
|
if (p.mcpServers.length) {
|
|
6024
6054
|
for (const server of p.mcpServers) {
|
|
6025
|
-
if (!
|
|
6026
|
-
|
|
6027
|
-
|
|
6055
|
+
if (!projectMcpByServer[server])
|
|
6056
|
+
projectMcpByServer[server] = [];
|
|
6057
|
+
projectMcpByServer[server].push({
|
|
6058
|
+
name: `mcp__${server}`,
|
|
6059
|
+
description: `Project MCP from ${p.name}`,
|
|
6060
|
+
type: 'project-mcp',
|
|
6061
|
+
connected: true,
|
|
6062
|
+
});
|
|
6028
6063
|
}
|
|
6029
6064
|
}
|
|
6030
6065
|
}
|
|
6031
|
-
|
|
6066
|
+
for (const [server, entries] of Object.entries(projectMcpByServer)) {
|
|
6067
|
+
// De-dup if the same server appears in multiple projects.
|
|
6068
|
+
const seen = new Set();
|
|
6069
|
+
const deduped = entries.filter((e) => {
|
|
6070
|
+
if (seen.has(e.name))
|
|
6071
|
+
return false;
|
|
6072
|
+
seen.add(e.name);
|
|
6073
|
+
return true;
|
|
6074
|
+
});
|
|
6075
|
+
categories[`Project MCP — ${server}`] = deduped;
|
|
6076
|
+
}
|
|
6077
|
+
// Composio Toolkits: 1000+ third-party services. We surface every
|
|
6078
|
+
// toolkit so the agent owner can opt in; the `connected` pill tells
|
|
6079
|
+
// them whether OAuth is wired up. Failures are non-fatal — we just
|
|
6080
|
+
// omit the category and stash the error for the client.
|
|
6081
|
+
try {
|
|
6082
|
+
const composio = await import('../integrations/composio/client.js');
|
|
6083
|
+
if (composio.isComposioEnabled()) {
|
|
6084
|
+
const [catalog, connected] = await Promise.all([
|
|
6085
|
+
composio.listAllToolkits(),
|
|
6086
|
+
composio.listConnectedToolkits(),
|
|
6087
|
+
]);
|
|
6088
|
+
const connectedSet = new Set(connected.map((c) => c.slug));
|
|
6089
|
+
const toolkits = catalog.map((tk) => ({
|
|
6090
|
+
name: `composio:${tk.slug}`,
|
|
6091
|
+
description: tk.description || tk.name || tk.slug,
|
|
6092
|
+
type: 'composio',
|
|
6093
|
+
connected: connectedSet.has(tk.slug),
|
|
6094
|
+
displayName: tk.name || tk.slug,
|
|
6095
|
+
}));
|
|
6096
|
+
if (toolkits.length > 0) {
|
|
6097
|
+
categories['Composio Toolkits'] = toolkits;
|
|
6098
|
+
}
|
|
6099
|
+
}
|
|
6100
|
+
}
|
|
6101
|
+
catch (err) {
|
|
6102
|
+
composioError = err?.message ?? String(err);
|
|
6103
|
+
console.error('[available-tools] composio fetch failed:', composioError);
|
|
6104
|
+
}
|
|
6105
|
+
return { categories, composioError };
|
|
6032
6106
|
});
|
|
6033
6107
|
res.json(data);
|
|
6034
6108
|
}
|
|
@@ -24816,11 +24890,21 @@ function renderRunDetailWaterfall(events, runId, jobName) {
|
|
|
24816
24890
|
|
|
24817
24891
|
var rowId = 'run-evt-' + j;
|
|
24818
24892
|
var canExpand = !!fullContent && fullContent.length > preview.length;
|
|
24819
|
-
|
|
24893
|
+
// PRD §6 Phase 4e: backfilled subagent events get an indented row + a
|
|
24894
|
+
// pill showing the subagent slug, so the waterfall makes it obvious
|
|
24895
|
+
// which spans came from a delegated agent vs the parent task.
|
|
24896
|
+
var isSubagent = ev.source === 'backfill' || !!ev.subagentSlug;
|
|
24897
|
+
var rowBg = isSubagent ? 'background:var(--bg-secondary);' : '';
|
|
24898
|
+
var rowPad = isSubagent ? 'padding:10px 20px 10px 48px;' : 'padding:10px 20px;';
|
|
24899
|
+
var subagentBadge = isSubagent
|
|
24900
|
+
? '<span title="Backfilled from subagent transcript" style="display:inline-block;background:var(--purple,#8b5cf6)20;color:var(--purple,#8b5cf6);padding:2px 8px;border-radius:4px;font-size:10px;font-weight:500;margin-right:6px">↳ ' + esc(ev.subagentSlug || ev.agentId || 'subagent') + '</span>'
|
|
24901
|
+
: '';
|
|
24902
|
+
html += '<div style="display:grid;grid-template-columns:90px 110px 1fr;gap:14px;' + rowPad + 'border-bottom:1px solid var(--border);align-items:start;' + rowBg + '">';
|
|
24820
24903
|
html += '<div style="font-size:10px;color:var(--text-muted);font-family:\\x27JetBrains Mono\\x27,monospace;line-height:18px">' + esc(offsetLabel) + '</div>';
|
|
24821
24904
|
html += '<div><span style="display:inline-block;background:' + color + '20;color:' + color + ';padding:2px 8px;border-radius:4px;font-size:10px;font-weight:600;letter-spacing:0.04em">' + esc(label) + '</span></div>';
|
|
24822
24905
|
html += '<div style="min-width:0">';
|
|
24823
24906
|
html += '<div style="font-size:12px;color:var(--text-primary);line-height:1.45;word-break:break-word">'
|
|
24907
|
+
+ subagentBadge
|
|
24824
24908
|
+ esc(preview)
|
|
24825
24909
|
+ (pairedDuration ? '<span style="color:var(--text-muted);font-size:11px"> ' + esc(pairedDuration) + '</span>' : '')
|
|
24826
24910
|
+ '</div>';
|
|
@@ -44,6 +44,25 @@ export declare const clementineJsonSchema: z.ZodObject<{
|
|
|
44
44
|
ask_first: "ask_first";
|
|
45
45
|
act_when_safe: "act_when_safe";
|
|
46
46
|
}>>;
|
|
47
|
+
profile: z.ZodOptional<z.ZodObject<{
|
|
48
|
+
systemPrompt: z.ZodOptional<z.ZodString>;
|
|
49
|
+
model: z.ZodOptional<z.ZodString>;
|
|
50
|
+
allowedTools: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
51
|
+
allowedProjects: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
52
|
+
allowedUsers: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
53
|
+
channels: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
54
|
+
budgetMonthlyCents: z.ZodOptional<z.ZodNumber>;
|
|
55
|
+
goalSlugs: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
56
|
+
sendPolicy: z.ZodOptional<z.ZodObject<{
|
|
57
|
+
maxDailyEmails: z.ZodOptional<z.ZodNumber>;
|
|
58
|
+
requiresApproval: z.ZodOptional<z.ZodEnum<{
|
|
59
|
+
none: "none";
|
|
60
|
+
"first-in-sequence": "first-in-sequence";
|
|
61
|
+
all: "all";
|
|
62
|
+
}>>;
|
|
63
|
+
businessHoursOnly: z.ZodOptional<z.ZodBoolean>;
|
|
64
|
+
}, z.core.$strip>>;
|
|
65
|
+
}, z.core.$strip>>;
|
|
47
66
|
}, z.core.$strip>>;
|
|
48
67
|
models: z.ZodOptional<z.ZodObject<{
|
|
49
68
|
default: z.ZodOptional<z.ZodString>;
|
|
@@ -32,6 +32,28 @@ export const clementineJsonSchema = z.object({
|
|
|
32
32
|
responseStyle: z.enum(['concise', 'balanced', 'detailed']).optional(),
|
|
33
33
|
progressVisibility: z.enum(['quiet', 'normal', 'detailed']).optional(),
|
|
34
34
|
autonomy: z.enum(['ask_first', 'balanced', 'act_when_safe']).optional(),
|
|
35
|
+
/**
|
|
36
|
+
* Dashboard-managed profile for the main agent (Clementine herself).
|
|
37
|
+
* Mirrors the per-agent edit surface so Tasks → Team can edit the
|
|
38
|
+
* primary persona without forcing users into Settings or env files.
|
|
39
|
+
* Every field is optional; absent fields fall through to the existing
|
|
40
|
+
* env / compiled defaults via computeEffectiveConfig.
|
|
41
|
+
*/
|
|
42
|
+
profile: z.object({
|
|
43
|
+
systemPrompt: z.string().optional(),
|
|
44
|
+
model: z.string().optional(),
|
|
45
|
+
allowedTools: z.array(z.string()).optional(),
|
|
46
|
+
allowedProjects: z.array(z.string()).optional(),
|
|
47
|
+
allowedUsers: z.array(z.string()).optional(),
|
|
48
|
+
channels: z.array(z.string()).optional(),
|
|
49
|
+
budgetMonthlyCents: z.number().nonnegative().optional(),
|
|
50
|
+
goalSlugs: z.array(z.string()).optional(),
|
|
51
|
+
sendPolicy: z.object({
|
|
52
|
+
maxDailyEmails: z.number().nonnegative().optional(),
|
|
53
|
+
requiresApproval: z.enum(['none', 'first-in-sequence', 'all']).optional(),
|
|
54
|
+
businessHoursOnly: z.boolean().optional(),
|
|
55
|
+
}).optional(),
|
|
56
|
+
}).optional(),
|
|
35
57
|
}).optional(),
|
|
36
58
|
models: z.object({
|
|
37
59
|
default: z.string().optional(),
|
package/dist/types.d.ts
CHANGED
|
@@ -445,8 +445,18 @@ export interface RunEvent {
|
|
|
445
445
|
costUsd?: number;
|
|
446
446
|
/** Stop reason from ResultMessage when kind='session_end'. */
|
|
447
447
|
stopReason?: string;
|
|
448
|
-
/** Subagent id when kind='subagent_*'
|
|
448
|
+
/** Subagent id when kind='subagent_*' OR when an event was synthesized from
|
|
449
|
+
* a subagent transcript via Path C backfill (subagent-backfill.ts). */
|
|
449
450
|
agentId?: string;
|
|
451
|
+
/** PRD §6 Phase 4e: subagent slug ("bright-petting-kahn") for friendly
|
|
452
|
+
* display in the Run detail waterfall. Only populated for events
|
|
453
|
+
* synthesized from subagent transcripts. */
|
|
454
|
+
subagentSlug?: string;
|
|
455
|
+
/** PRD §6 Phase 4e: marks events as backfilled from a subagent transcript
|
|
456
|
+
* rather than captured live by the in-process tap. The Run detail viewer
|
|
457
|
+
* renders these in a nested swimlane and labels the source so users know
|
|
458
|
+
* the data came from disk after the run. */
|
|
459
|
+
source?: 'live' | 'backfill';
|
|
450
460
|
}
|
|
451
461
|
/**
|
|
452
462
|
* PRD §9 / 1.18.87: 11-category failure taxonomy. Replaces the existing
|