clementine-agent 1.18.120 → 1.18.122
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-cron.d.ts +12 -0
- package/dist/agent/run-agent-cron.js +77 -4
- package/dist/agent/run-agent.d.ts +7 -0
- package/dist/agent/run-agent.js +8 -0
- package/dist/agent/skill-extractor.d.ts +0 -9
- package/dist/agent/skill-extractor.js +0 -30
- package/dist/agent/skill-store.d.ts +0 -5
- package/dist/agent/skill-store.js +0 -4
- package/dist/cli/dashboard.js +43 -184
- package/dist/cli/index.js +30 -10
- package/dist/gateway/cron-scheduler.js +2 -2
- package/dist/gateway/router.d.ts +5 -1
- package/dist/gateway/router.js +6 -1
- package/dist/types.d.ts +19 -78
- package/package.json +1 -2
|
@@ -70,6 +70,7 @@ export interface SkillContextResult {
|
|
|
70
70
|
*/
|
|
71
71
|
export declare function buildSkillContext(jobName: string, jobPrompt: string, agentSlug: string | undefined, pinnedSkills: string[] | undefined, memoryStore?: MemoryStore | null, opts?: {
|
|
72
72
|
skipAutoMatch?: boolean;
|
|
73
|
+
projectWorkDir?: string;
|
|
73
74
|
}): Promise<SkillContextResult>;
|
|
74
75
|
/** Minimal interface for the post-task reflection + skill extraction
|
|
75
76
|
* hooks. Lets `runAgentCron` stay decoupled from the full
|
|
@@ -128,6 +129,11 @@ export interface RunAgentCronOptions {
|
|
|
128
129
|
* prior progress. The fix for fire-time memory drift. Undefined =
|
|
129
130
|
* legacy behavior (inject everything). */
|
|
130
131
|
predictable?: boolean;
|
|
132
|
+
/** Extra read+execute scope for the agent's Read/Bash/Glob tools. Maps
|
|
133
|
+
* directly to the CronJobDefinition.addDirs YAML field. Combined with
|
|
134
|
+
* every pinned-skill folder so `Bash python3 scripts/render.py` works
|
|
135
|
+
* from inside a skill bundle without the cwd being set there. */
|
|
136
|
+
addDirs?: string[];
|
|
131
137
|
}
|
|
132
138
|
export interface RunAgentCronResult extends RunAgentResult {
|
|
133
139
|
/** The final prompt that was sent to the agent (after context injection).
|
|
@@ -184,6 +190,12 @@ export interface CronExecutionPlan {
|
|
|
184
190
|
* MEMORY.md / team / delegation / auto-skills were intentionally
|
|
185
191
|
* skipped. Used by the Preview verdict line. */
|
|
186
192
|
predictable: boolean;
|
|
193
|
+
/** Merged list of extra directories the SDK should expose to the agent's
|
|
194
|
+
* Read/Bash/Glob tools. Combines `opts.addDirs` with every pinned-skill
|
|
195
|
+
* folder so a skill's `scripts/render.py` is reachable without the cwd
|
|
196
|
+
* being set inside the skill folder. Deduped + filtered to existing
|
|
197
|
+
* paths. Empty when the trick has no addDirs and no folder-form pins. */
|
|
198
|
+
additionalDirectories: string[];
|
|
187
199
|
}
|
|
188
200
|
/**
|
|
189
201
|
* Plan a cron run — assemble all context, resolve skills, intersect tool/MCP
|
|
@@ -245,14 +245,42 @@ export async function buildSkillContext(jobName, jobPrompt, agentSlug, pinnedSki
|
|
|
245
245
|
: (suppressedNamesRaw ?? undefined);
|
|
246
246
|
const prepared = [];
|
|
247
247
|
const seen = new Set();
|
|
248
|
-
// 1. Load pinned skills first via exact slug lookup.
|
|
248
|
+
// 1. Load pinned skills first via exact slug lookup. When the cron has
|
|
249
|
+
// a workDir set, we ALSO check for a project-scoped skill at
|
|
250
|
+
// <workDir>/.clementine/skills/<name>/SKILL.md before falling back
|
|
251
|
+
// to the global lookup. This closes the SDK-alignment gap from the
|
|
252
|
+
// 1.18.121 audit (project skills were silently unreachable from the
|
|
253
|
+
// cron runtime even though skill-store.getSkill supported them).
|
|
249
254
|
if (pinnedSkills?.length) {
|
|
255
|
+
const projectGetSkill = opts?.projectWorkDir
|
|
256
|
+
? (await import('./skill-store.js')).getSkill
|
|
257
|
+
: null;
|
|
250
258
|
for (const pinName of pinnedSkills) {
|
|
251
259
|
if (seen.has(pinName))
|
|
252
260
|
continue;
|
|
253
261
|
if (prepared.length >= MAX_INJECTED_SKILLS)
|
|
254
262
|
break;
|
|
255
|
-
|
|
263
|
+
// Project-scoped first when a workDir is in scope. The skill-store
|
|
264
|
+
// shape differs from the runtime's SkillMatch — adapt it here so
|
|
265
|
+
// the rest of the pipeline doesn't care which loader returned it.
|
|
266
|
+
let skill = null;
|
|
267
|
+
if (projectGetSkill && opts?.projectWorkDir) {
|
|
268
|
+
const ps = projectGetSkill(pinName, { projectWorkDir: opts.projectWorkDir });
|
|
269
|
+
if (ps && ps.scope === 'project') {
|
|
270
|
+
const ext = (ps.frontmatter.clementine ?? {});
|
|
271
|
+
const tools = ext.tools?.allow ?? [];
|
|
272
|
+
skill = {
|
|
273
|
+
name: ps.frontmatter.name,
|
|
274
|
+
title: String(ps.frontmatter.title ?? ps.frontmatter.name),
|
|
275
|
+
content: ps.body,
|
|
276
|
+
toolsUsed: Array.isArray(tools) ? tools.map(String) : [],
|
|
277
|
+
attachments: [],
|
|
278
|
+
skillDir: path.dirname(path.dirname(ps.filePath)),
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
if (!skill)
|
|
283
|
+
skill = loadSkillByName(pinName, agentSlug, { suppressedNames });
|
|
256
284
|
if (!skill) {
|
|
257
285
|
missing.push(pinName);
|
|
258
286
|
logger.warn({ jobName, pin: pinName, agentSlug }, 'cron: pinned skill not found');
|
|
@@ -403,7 +431,7 @@ export async function buildCronExecutionPlan(opts) {
|
|
|
403
431
|
const delegationContext = predictable ? '' : buildDelegationContext(agentSlug);
|
|
404
432
|
const teamContext = predictable ? '' : buildTeamContext(agentSlug);
|
|
405
433
|
const criteriaContext = buildCriteriaContext(opts.successCriteria);
|
|
406
|
-
const skillResult = await buildSkillContext(opts.jobName, opts.jobPrompt, agentSlug, opts.pinnedSkills, opts.memoryStore, { skipAutoMatch: predictable });
|
|
434
|
+
const skillResult = await buildSkillContext(opts.jobName, opts.jobPrompt, agentSlug, opts.pinnedSkills, opts.memoryStore, { skipAutoMatch: predictable, projectWorkDir: opts.workDir });
|
|
407
435
|
const skillContext = skillResult.text;
|
|
408
436
|
const howToRespond = `## How to respond\n` +
|
|
409
437
|
`You're sending this directly to ${ownerName} as a DM. ` +
|
|
@@ -448,6 +476,46 @@ export async function buildCronExecutionPlan(opts) {
|
|
|
448
476
|
const configuredCap = tier >= 2 ? BUDGET.cronT2 : BUDGET.cronT1;
|
|
449
477
|
const maxBudget = opts.maxBudgetUsd ?? (configuredCap > 0 ? configuredCap : undefined);
|
|
450
478
|
const effort = tier >= 2 ? 'high' : 'medium';
|
|
479
|
+
// 1.18.121 — assemble additionalDirectories. Combines:
|
|
480
|
+
// 1. opts.addDirs (from CronJobDefinition.addDirs YAML field)
|
|
481
|
+
// 2. Every pinned-skill folder so the skill's scripts/ + reference docs
|
|
482
|
+
// are reachable via Read/Bash without the cwd being set inside the
|
|
483
|
+
// skill folder.
|
|
484
|
+
// Deduped via Set; filtered to paths that actually exist on disk so we
|
|
485
|
+
// don't trigger SDK errors on stale references.
|
|
486
|
+
const dirSet = new Set();
|
|
487
|
+
for (const d of opts.addDirs ?? []) {
|
|
488
|
+
if (d && typeof d === 'string')
|
|
489
|
+
dirSet.add(d);
|
|
490
|
+
}
|
|
491
|
+
for (const applied of skillResult.applied) {
|
|
492
|
+
// skillResult.applied lacks the on-disk path; pull it from the prepared
|
|
493
|
+
// skill list (which we have in scope as a closure via the skillContext
|
|
494
|
+
// builder). Cheaper to reconstruct: every pinned-form skill lives at
|
|
495
|
+
// `<skillsRoot>/<name>/SKILL.md` and we want to expose `<skillsRoot>/<name>/`.
|
|
496
|
+
// Walk both global + agent-scoped roots; first hit wins.
|
|
497
|
+
const candidates = [
|
|
498
|
+
path.join(VAULT_DIR, '00-System', 'skills', applied.name),
|
|
499
|
+
];
|
|
500
|
+
if (agentSlug) {
|
|
501
|
+
candidates.unshift(path.join(VAULT_DIR, '00-System', 'agents', agentSlug, 'skills', applied.name));
|
|
502
|
+
}
|
|
503
|
+
for (const candidate of candidates) {
|
|
504
|
+
if (fs.existsSync(path.join(candidate, 'SKILL.md'))) {
|
|
505
|
+
dirSet.add(candidate);
|
|
506
|
+
break;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
// Final filter: only emit dirs that exist (the SDK errors on missing).
|
|
511
|
+
const additionalDirectories = [...dirSet].filter(d => {
|
|
512
|
+
try {
|
|
513
|
+
return fs.statSync(d).isDirectory();
|
|
514
|
+
}
|
|
515
|
+
catch {
|
|
516
|
+
return false;
|
|
517
|
+
}
|
|
518
|
+
});
|
|
451
519
|
return {
|
|
452
520
|
builtPrompt,
|
|
453
521
|
contextBlocks: {
|
|
@@ -468,6 +536,7 @@ export async function buildCronExecutionPlan(opts) {
|
|
|
468
536
|
agentSlug,
|
|
469
537
|
ownerName,
|
|
470
538
|
predictable,
|
|
539
|
+
additionalDirectories,
|
|
471
540
|
};
|
|
472
541
|
}
|
|
473
542
|
/**
|
|
@@ -483,7 +552,7 @@ export async function buildCronExecutionPlan(opts) {
|
|
|
483
552
|
*/
|
|
484
553
|
export async function runAgentCron(opts) {
|
|
485
554
|
const plan = await buildCronExecutionPlan(opts);
|
|
486
|
-
const { builtPrompt, agentSlug, effort, maxBudgetUsd: maxBudget, effectiveAllowedTools, mcpServerMap, composioConnected, externalConnected, mcpServersApplied, } = plan;
|
|
555
|
+
const { builtPrompt, agentSlug, effort, maxBudgetUsd: maxBudget, effectiveAllowedTools, mcpServerMap, composioConnected, externalConnected, mcpServersApplied, additionalDirectories, } = plan;
|
|
487
556
|
logger.info({
|
|
488
557
|
job: opts.jobName,
|
|
489
558
|
tier: plan.tier,
|
|
@@ -511,6 +580,10 @@ export async function runAgentCron(opts) {
|
|
|
511
580
|
abortSignal: opts.abortSignal,
|
|
512
581
|
...(effectiveAllowedTools ? { allowedTools: effectiveAllowedTools } : {}),
|
|
513
582
|
extraMcpServers: mcpServerMap,
|
|
583
|
+
// 1.18.121 — pipe the merged addDirs+pinned-skill folders to the SDK
|
|
584
|
+
// so a skill's bundled scripts/templates are reachable via Bash/Read
|
|
585
|
+
// without making the cwd the skill folder.
|
|
586
|
+
...(additionalDirectories.length > 0 ? { additionalDirectories } : {}),
|
|
514
587
|
});
|
|
515
588
|
// Mirror the run into transcripts so future chat recall can see it.
|
|
516
589
|
// Legacy runCronJob did this with role='cron'; canonical needs the
|
|
@@ -81,6 +81,13 @@ export interface RunAgentOptions {
|
|
|
81
81
|
allowedTools?: string[];
|
|
82
82
|
/** Optional CLAUDE.md / project setting source. Defaults to ['project']. */
|
|
83
83
|
settingSources?: ('project' | 'user' | 'local')[];
|
|
84
|
+
/** Extra directories the SDK should make available to the agent's tools
|
|
85
|
+
* (Read/Bash/Glob/Grep) beyond `cwd`. Maps directly to the SDK's
|
|
86
|
+
* `additionalDirectories` option. The cron runtime uses this to surface
|
|
87
|
+
* pinned-skill folders so the agent can `Bash python3 scripts/render.py`
|
|
88
|
+
* inside a skill bundle without the cwd being set to that folder.
|
|
89
|
+
* Captured in CronJobDefinition.addDirs and piped through buildPrompt. */
|
|
90
|
+
additionalDirectories?: string[];
|
|
84
91
|
/** Additional MCP servers to merge with the always-on clementine-tools
|
|
85
92
|
* server. Use to wire Composio + claude.ai integrations on chat-path
|
|
86
93
|
* invocations that need Outlook/Salesforce/etc. */
|
package/dist/agent/run-agent.js
CHANGED
|
@@ -299,6 +299,14 @@ export async function runAgent(prompt, opts) {
|
|
|
299
299
|
...(opts.model ? { model: opts.model } : {}),
|
|
300
300
|
...(opts.resumeSessionId ? { resume: opts.resumeSessionId } : {}),
|
|
301
301
|
...(sdkAbortController ? { abortController: sdkAbortController } : {}),
|
|
302
|
+
// 1.18.121 — pipe additionalDirectories through to the SDK so agents
|
|
303
|
+
// can Read / Bash inside pinned-skill folders, project-scoped skills,
|
|
304
|
+
// and any cron's add_dirs scope without their cwd being set to those
|
|
305
|
+
// folders. Was captured in CronJobDefinition.addDirs since 1.18.77 but
|
|
306
|
+
// never reached the SDK call site — this closes that gap.
|
|
307
|
+
...(opts.additionalDirectories && opts.additionalDirectories.length > 0
|
|
308
|
+
? { additionalDirectories: opts.additionalDirectories }
|
|
309
|
+
: {}),
|
|
302
310
|
};
|
|
303
311
|
const sdkOptions = normalizeClaudeSdkOptionsForOneMillionContext(sdkOptionsRaw);
|
|
304
312
|
logger.info({
|
|
@@ -79,15 +79,6 @@ export declare function loadSkillByName(name: string, agentSlug?: string, opts?:
|
|
|
79
79
|
* `clementine.useCount` (Anthropic-canonical frontmatter keeps top-level
|
|
80
80
|
* reserved for `name`/`description`). */
|
|
81
81
|
export declare function recordSkillUse(skillName: string, agentSlug?: string): void;
|
|
82
|
-
/** List all active skills (global + all agent-scoped). */
|
|
83
|
-
export declare function listSkills(agentSlug?: string): Array<{
|
|
84
|
-
name: string;
|
|
85
|
-
title: string;
|
|
86
|
-
source: string;
|
|
87
|
-
useCount: number;
|
|
88
|
-
updatedAt: string;
|
|
89
|
-
agentSlug?: string;
|
|
90
|
-
}>;
|
|
91
82
|
/**
|
|
92
83
|
* Move skills that were never used (useCount=0, no usage telemetry rows) and
|
|
93
84
|
* are older than `olderThanDays` to the `.archive/` subdirectory inside their
|
|
@@ -667,36 +667,6 @@ export function recordSkillUse(skillName, agentSlug) {
|
|
|
667
667
|
}
|
|
668
668
|
catch { /* non-fatal */ }
|
|
669
669
|
}
|
|
670
|
-
/** List all active skills (global + all agent-scoped). */
|
|
671
|
-
export function listSkills(agentSlug) {
|
|
672
|
-
const results = [];
|
|
673
|
-
const dirs = [];
|
|
674
|
-
if (agentSlug) {
|
|
675
|
-
dirs.push({ dir: agentSkillsDir(agentSlug), slug: agentSlug });
|
|
676
|
-
}
|
|
677
|
-
else {
|
|
678
|
-
dirs.push({ dir: GLOBAL_SKILLS_DIR });
|
|
679
|
-
}
|
|
680
|
-
for (const { dir, slug } of dirs) {
|
|
681
|
-
if (!existsSync(dir))
|
|
682
|
-
continue;
|
|
683
|
-
for (const f of readdirSync(dir).filter(f => f.endsWith('.md'))) {
|
|
684
|
-
try {
|
|
685
|
-
const parsed = matter(readFileSync(path.join(dir, f), 'utf-8'));
|
|
686
|
-
results.push({
|
|
687
|
-
name: f.replace('.md', ''),
|
|
688
|
-
title: parsed.data.title ?? f,
|
|
689
|
-
source: parsed.data.source ?? 'unknown',
|
|
690
|
-
useCount: parsed.data.useCount ?? 0,
|
|
691
|
-
updatedAt: parsed.data.updatedAt ?? '',
|
|
692
|
-
agentSlug: slug,
|
|
693
|
-
});
|
|
694
|
-
}
|
|
695
|
-
catch { /* skip */ }
|
|
696
|
-
}
|
|
697
|
-
}
|
|
698
|
-
return results;
|
|
699
|
-
}
|
|
700
670
|
// ── Stale skill archival ────────────────────────────────────────────
|
|
701
671
|
/**
|
|
702
672
|
* Move skills that were never used (useCount=0, no usage telemetry rows) and
|
|
@@ -77,10 +77,5 @@ export declare function migrateAllLegacySkills(): {
|
|
|
77
77
|
migrated: MigrationResult[];
|
|
78
78
|
skipped: MigrationResult[];
|
|
79
79
|
};
|
|
80
|
-
/** Diagnostics for the dashboard — expose where the loader looked. */
|
|
81
|
-
export declare function _skillDirsForDiagnostics(workDir?: string): {
|
|
82
|
-
global: string;
|
|
83
|
-
project: string | null;
|
|
84
|
-
};
|
|
85
80
|
export {};
|
|
86
81
|
//# sourceMappingURL=skill-store.d.ts.map
|
|
@@ -647,8 +647,4 @@ export function migrateAllLegacySkills() {
|
|
|
647
647
|
}
|
|
648
648
|
return { migrated, skipped };
|
|
649
649
|
}
|
|
650
|
-
/** Diagnostics for the dashboard — expose where the loader looked. */
|
|
651
|
-
export function _skillDirsForDiagnostics(workDir) {
|
|
652
|
-
return { global: globalSkillsDir(), project: projectSkillsDir(workDir) ?? null };
|
|
653
|
-
}
|
|
654
650
|
//# sourceMappingURL=skill-store.js.map
|
package/dist/cli/dashboard.js
CHANGED
|
@@ -14419,6 +14419,9 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
14419
14419
|
}
|
|
14420
14420
|
.toast.success { border-left: 3px solid var(--green); }
|
|
14421
14421
|
.toast.error { border-left: 3px solid var(--red); }
|
|
14422
|
+
/* 1.18.122 — variants used by callsites that lacked CSS support */
|
|
14423
|
+
.toast.warn { border-left: 3px solid var(--yellow); }
|
|
14424
|
+
.toast.info { border-left: 3px solid var(--accent); }
|
|
14422
14425
|
@keyframes toastIn {
|
|
14423
14426
|
from { transform: translateX(40px); opacity: 0; }
|
|
14424
14427
|
to { transform: translateX(0); opacity: 1; }
|
|
@@ -19466,11 +19469,11 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
19466
19469
|
clearInterval(ticker);
|
|
19467
19470
|
|
|
19468
19471
|
if (errorMsg) {
|
|
19469
|
-
manifestEl.innerHTML = '<div style="color
|
|
19472
|
+
manifestEl.innerHTML = '<div style="color:var(--red)">Error: ' + escapeHtml(errorMsg) + '</div>';
|
|
19470
19473
|
return;
|
|
19471
19474
|
}
|
|
19472
19475
|
if (!manifestData || !finalData) {
|
|
19473
|
-
manifestEl.innerHTML = '<div style="color
|
|
19476
|
+
manifestEl.innerHTML = '<div style="color:var(--red)">Preview ended without a result. Check dashboard logs.</div>';
|
|
19474
19477
|
return;
|
|
19475
19478
|
}
|
|
19476
19479
|
|
|
@@ -19480,13 +19483,13 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
19480
19483
|
const manifestRows = Object.entries(manifest.formats || {})
|
|
19481
19484
|
.map(([fmt, n]) => '<tr><td>' + escapeHtml(fmt) + '</td><td>' + n + '</td></tr>').join('');
|
|
19482
19485
|
const warnBlock = errorsList.length
|
|
19483
|
-
? '<div style="margin-top:10px;padding:10px;background
|
|
19486
|
+
? '<div style="margin-top:10px;padding:10px;background:rgba(245,158,11,0.10);border:1px solid rgba(245,158,11,0.32);border-radius:6px;color:var(--text-primary);font-size:13px">' +
|
|
19484
19487
|
'<div style="font-weight:600;margin-bottom:4px">' + errorsList.length + ' file(s) could not be ingested</div>' +
|
|
19485
19488
|
errorsList.map((e) => '<div style="font-family:monospace;font-size:12px">• ' + escapeHtml(e.error) + '</div>').join('') +
|
|
19486
19489
|
'</div>'
|
|
19487
19490
|
: '';
|
|
19488
19491
|
const emptyNote = (preview.length === 0 && !errorsList.length)
|
|
19489
|
-
? '<div style="margin-top:10px;padding:10px;background
|
|
19492
|
+
? '<div style="margin-top:10px;padding:10px;background:rgba(245,158,11,0.10);border:1px solid rgba(245,158,11,0.32);border-radius:6px;color:var(--text-primary);font-size:13px">No records extracted. The file may be empty or in an unsupported format.</div>'
|
|
19490
19493
|
: '';
|
|
19491
19494
|
manifestEl.innerHTML =
|
|
19492
19495
|
'<div class="card" style="padding:12px"><div style="font-weight:600;margin-bottom:8px">Manifest</div>' +
|
|
@@ -19501,7 +19504,7 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
19501
19504
|
'<div style="font-weight:600">#' + (i + 1) + ' ' + escapeHtml(p.title || '(untitled)') + '</div>' +
|
|
19502
19505
|
'<div style="color:var(--muted);font-size:12px;margin:4px 0">' + escapeHtml(p.targetRelPath || '') + '</div>' +
|
|
19503
19506
|
'<div style="font-size:13px">' + escapeHtml((p.body || '').slice(0, 400)) + '</div>' +
|
|
19504
|
-
(p.tags && p.tags.length ? '<div style="margin-top:6px;font-size:12px;color
|
|
19507
|
+
(p.tags && p.tags.length ? '<div style="margin-top:6px;font-size:12px;color:var(--text-muted)">tags: ' + p.tags.map(escapeHtml).join(', ') + '</div>' : '') +
|
|
19505
19508
|
'</div>').join('');
|
|
19506
19509
|
previewEl.innerHTML =
|
|
19507
19510
|
'<div style="font-weight:600;margin-bottom:8px">Preview (first ' + Math.min(preview.length, 10) + ' records, dry-run)</div>' + previewHtml;
|
|
@@ -19549,11 +19552,11 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
19549
19552
|
}
|
|
19550
19553
|
|
|
19551
19554
|
if (errorMsg) {
|
|
19552
|
-
progEl.innerHTML = '<div style="color
|
|
19555
|
+
progEl.innerHTML = '<div style="color:var(--red)">Error: ' + escapeHtml(errorMsg) + '</div>';
|
|
19553
19556
|
return;
|
|
19554
19557
|
}
|
|
19555
19558
|
if (!finalData) {
|
|
19556
|
-
progEl.innerHTML = '<div style="color
|
|
19559
|
+
progEl.innerHTML = '<div style="color:var(--red)">Ingestion ended without a result. Check dashboard logs.</div>';
|
|
19557
19560
|
return;
|
|
19558
19561
|
}
|
|
19559
19562
|
const elapsed = Math.floor((Date.now() - progress.startedAt) / 1000);
|
|
@@ -19564,7 +19567,7 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
19564
19567
|
? 'Ingestion complete'
|
|
19565
19568
|
: 'Ingestion finished, but nothing was written';
|
|
19566
19569
|
const errBlock = errList.length
|
|
19567
|
-
? '<div style="margin-top:10px;padding:10px;background
|
|
19570
|
+
? '<div style="margin-top:10px;padding:10px;background:rgba(245,158,11,0.10);border:1px solid rgba(245,158,11,0.32);border-radius:6px;color:var(--text-primary);font-size:13px">' +
|
|
19568
19571
|
'<div style="font-weight:600;margin-bottom:4px">' + errList.length + ' error(s)</div>' +
|
|
19569
19572
|
errList.map((e) => '<div style="font-family:monospace;font-size:12px">• ' + escapeHtml(e.error) + '</div>').join('') +
|
|
19570
19573
|
'</div>'
|
|
@@ -19655,8 +19658,8 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
19655
19658
|
}
|
|
19656
19659
|
el.innerHTML = data.integrations.map(function(i) {
|
|
19657
19660
|
const ok = i.connected && i.hasFeedReadyTools;
|
|
19658
|
-
const color = ok ? '
|
|
19659
|
-
const bg = ok ? '
|
|
19661
|
+
const color = ok ? 'var(--green)' : 'var(--text-primary)';
|
|
19662
|
+
const bg = ok ? 'rgba(34,197,94,0.10)' : 'rgba(245,158,11,0.10)';
|
|
19660
19663
|
const dot = ok ? '✓' : '⚠';
|
|
19661
19664
|
const source = i.kind === 'composio' ? 'Composio' : (i.kind === 'claude-desktop' ? 'Claude Desktop' : 'MCP');
|
|
19662
19665
|
const label = ok ? i.label + ' · ' + source : i.label + ' (incomplete in ' + source + ')';
|
|
@@ -19685,7 +19688,7 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
19685
19688
|
: '<span style="color:var(--muted)">no fields</span>';
|
|
19686
19689
|
return '<div class="card" style="padding:10px 12px;margin-bottom:8px;display:flex;align-items:center;gap:12px">' +
|
|
19687
19690
|
'<div style="flex:1">' +
|
|
19688
|
-
'<div style="font-weight:600">' + escapeHtml(f.name) + (f.enabled ? '' : ' <span style="color
|
|
19691
|
+
'<div style="font-weight:600">' + escapeHtml(f.name) + (f.enabled ? '' : ' <span style="color:var(--red);font-weight:normal">(disabled)</span>') + '</div>' +
|
|
19689
19692
|
'<div style="font-size:12px;color:var(--muted)">' +
|
|
19690
19693
|
'Recipe: <code>' + escapeHtml(f.recipeId) + '</code> · Schedule: <code>' + escapeHtml(f.schedule) + '</code> · Target: <code>' + escapeHtml(f.targetFolder) + '</code>' +
|
|
19691
19694
|
'</div>' +
|
|
@@ -19732,10 +19735,10 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
19732
19735
|
if (!brainFeedWizardState) return;
|
|
19733
19736
|
const s = brainFeedWizardState;
|
|
19734
19737
|
if (s.step === 0) {
|
|
19735
|
-
if (!s.pick) { document.getElementById('brain-feed-wizard-status').innerHTML = '<span style="color
|
|
19738
|
+
if (!s.pick) { document.getElementById('brain-feed-wizard-status').innerHTML = '<span style="color:var(--red)">Pick a connector.</span>'; return; }
|
|
19736
19739
|
s.step = 1;
|
|
19737
19740
|
} else if (s.step === 1) {
|
|
19738
|
-
if (!s.recipe) { document.getElementById('brain-feed-wizard-status').innerHTML = '<span style="color
|
|
19741
|
+
if (!s.recipe) { document.getElementById('brain-feed-wizard-status').innerHTML = '<span style="color:var(--red)">Pick a recipe.</span>'; return; }
|
|
19739
19742
|
s.values = {};
|
|
19740
19743
|
for (const f of (s.recipe.fields || [])) {
|
|
19741
19744
|
if (f.defaultValue) s.values[f.key] = f.defaultValue;
|
|
@@ -19752,11 +19755,11 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
19752
19755
|
const inputs = document.querySelectorAll('#brain-feed-wizard-step [data-field]');
|
|
19753
19756
|
inputs.forEach(function(inp) { s.values[inp.dataset.field] = inp.value; });
|
|
19754
19757
|
const missing = (s.recipe.fields || []).filter(function(f) { return f.required && !(s.values[f.key] || '').trim(); });
|
|
19755
|
-
if (missing.length) { document.getElementById('brain-feed-wizard-status').innerHTML = '<span style="color
|
|
19758
|
+
if (missing.length) { document.getElementById('brain-feed-wizard-status').innerHTML = '<span style="color:var(--red)">Required: ' + missing.map(function(f) { return f.label; }).join(', ') + '</span>'; return; }
|
|
19756
19759
|
if (s.recipe && s.recipe.id === 'tool-backed-memory-seed') {
|
|
19757
19760
|
const toolName = String(s.values.toolName || '').trim();
|
|
19758
19761
|
if (!/^mcp__.+__.+$/.test(toolName)) {
|
|
19759
|
-
document.getElementById('brain-feed-wizard-status').innerHTML = '<span style="color
|
|
19762
|
+
document.getElementById('brain-feed-wizard-status').innerHTML = '<span style="color:var(--red)">Pick an exact tool before continuing.</span>';
|
|
19760
19763
|
return;
|
|
19761
19764
|
}
|
|
19762
19765
|
const rawVariables = String(s.values.variablesJson || '').trim();
|
|
@@ -19764,12 +19767,12 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
19764
19767
|
try {
|
|
19765
19768
|
const parsedVariables = JSON.parse(rawVariables);
|
|
19766
19769
|
if (!parsedVariables || typeof parsedVariables !== 'object' || Array.isArray(parsedVariables)) {
|
|
19767
|
-
document.getElementById('brain-feed-wizard-status').innerHTML = '<span style="color
|
|
19770
|
+
document.getElementById('brain-feed-wizard-status').innerHTML = '<span style="color:var(--red)">Tool variables must be a JSON object, for example {}.</span>';
|
|
19768
19771
|
return;
|
|
19769
19772
|
}
|
|
19770
19773
|
} catch (err) {
|
|
19771
19774
|
void err;
|
|
19772
|
-
document.getElementById('brain-feed-wizard-status').innerHTML = '<span style="color
|
|
19775
|
+
document.getElementById('brain-feed-wizard-status').innerHTML = '<span style="color:var(--red)">Tool variables must be valid JSON, for example {}.</span>';
|
|
19773
19776
|
return;
|
|
19774
19777
|
}
|
|
19775
19778
|
}
|
|
@@ -19836,7 +19839,7 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
19836
19839
|
// can still set the value manually.
|
|
19837
19840
|
container.innerHTML =
|
|
19838
19841
|
'<input type="text" value="' + escapeHtml(currentVal) + '" placeholder="(type a value)" style="width:100%" oninput="(document.querySelector(\\'input[type=hidden][data-field=' + field.key + ']\\')||{}).value=this.value">' +
|
|
19839
|
-
'<div style="color
|
|
19842
|
+
'<div style="color:var(--text-primary);font-size:11px;margin-top:4px">Nothing returned from the probe — type a value manually' + (data.rawPreview ? ' (probe output: ' + escapeHtml(data.rawPreview.slice(0, 120)) + '…)' : '') + '</div>';
|
|
19840
19843
|
return;
|
|
19841
19844
|
}
|
|
19842
19845
|
|
|
@@ -19862,7 +19865,7 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
19862
19865
|
} catch (err) {
|
|
19863
19866
|
container.innerHTML =
|
|
19864
19867
|
'<input type="text" value="' + escapeHtml(values[field.key] || '') + '" placeholder="(probe failed — type manually)" style="width:100%" oninput="(document.querySelector(\\'input[type=hidden][data-field=' + field.key + ']\\')||{}).value=this.value">' +
|
|
19865
|
-
'<div style="color
|
|
19868
|
+
'<div style="color:var(--red);font-size:11px;margin-top:4px">Picker failed: ' + escapeHtml(String(err && err.message ? err.message : err)) + ' — type a value manually.</div>';
|
|
19866
19869
|
}
|
|
19867
19870
|
}
|
|
19868
19871
|
|
|
@@ -19888,7 +19891,7 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
19888
19891
|
const rawHint = trimmed && !isEmptyArray
|
|
19889
19892
|
? ' <span style="color:var(--muted)">Tool said: ' + escapeHtml(trimmed.slice(0, 140)) + '</span>'
|
|
19890
19893
|
: '';
|
|
19891
|
-
resultsEl.innerHTML = '<div style="color
|
|
19894
|
+
resultsEl.innerHTML = '<div style="color:var(--text-primary);font-size:12px;padding:6px">No matches for "' + escapeHtml(query) + '". The tool may be limited by macOS permissions or not support this query — use <a href="#" onclick="brainFieldPickerToggleCustom(\\'' + field.key + '\\', \\'\\');return false">type a value directly</a>.' + rawHint + '</div>';
|
|
19892
19895
|
return;
|
|
19893
19896
|
}
|
|
19894
19897
|
resultsEl.innerHTML = items.map(function(it) {
|
|
@@ -19897,7 +19900,7 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
19897
19900
|
}).join('') +
|
|
19898
19901
|
'<div style="font-size:11px;color:var(--muted);margin-top:4px">' + items.length + ' result' + (items.length === 1 ? '' : 's') + (data.cached ? ' (cached)' : '') + '</div>';
|
|
19899
19902
|
} catch (err) {
|
|
19900
|
-
resultsEl.innerHTML = '<div style="color
|
|
19903
|
+
resultsEl.innerHTML = '<div style="color:var(--red);font-size:12px;padding:6px">' + escapeHtml(String(err && err.message ? err.message : err)) + '</div>';
|
|
19901
19904
|
}
|
|
19902
19905
|
}
|
|
19903
19906
|
|
|
@@ -19963,7 +19966,7 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
19963
19966
|
let html = '';
|
|
19964
19967
|
if (s.step === 0) {
|
|
19965
19968
|
if (!s.connected.length) {
|
|
19966
|
-
html = '<div style="color
|
|
19969
|
+
html = '<div style="color:var(--text-primary)">No connectors have feed-ready tools. Connect Composio or open Claude Desktop → Connectors and sign into Google Drive, Outlook, Gmail, or Slack first.</div>';
|
|
19967
19970
|
} else {
|
|
19968
19971
|
html = '<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:8px">' +
|
|
19969
19972
|
s.connected.map(function(i) {
|
|
@@ -20026,7 +20029,7 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
20026
20029
|
} else {
|
|
20027
20030
|
control = '<input type="text" data-field="' + f.key + '" value="' + escapeHtml(val) + '" placeholder="' + escapeHtml(f.placeholder || '') + '" style="width:100%">';
|
|
20028
20031
|
}
|
|
20029
|
-
return '<label style="font-weight:500;padding-top:6px">' + escapeHtml(f.label) + (f.required ? ' <span style="color
|
|
20032
|
+
return '<label style="font-weight:500;padding-top:6px">' + escapeHtml(f.label) + (f.required ? ' <span style="color:var(--red)">*</span>' : '') + '</label>' +
|
|
20030
20033
|
'<div>' + control + help + '</div>';
|
|
20031
20034
|
}).join('') + '</div>';
|
|
20032
20035
|
|
|
@@ -20091,13 +20094,13 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
20091
20094
|
});
|
|
20092
20095
|
const data = await resp.json();
|
|
20093
20096
|
if (!resp.ok) {
|
|
20094
|
-
document.getElementById('brain-feed-wizard-status').innerHTML = '<span style="color
|
|
20097
|
+
document.getElementById('brain-feed-wizard-status').innerHTML = '<span style="color:var(--red)">' + escapeHtml(data.error || 'save failed') + '</span>';
|
|
20095
20098
|
return;
|
|
20096
20099
|
}
|
|
20097
20100
|
brainCloseFeedWizard();
|
|
20098
20101
|
brainLoadFeeds();
|
|
20099
20102
|
} catch (err) {
|
|
20100
|
-
document.getElementById('brain-feed-wizard-status').innerHTML = '<span style="color
|
|
20103
|
+
document.getElementById('brain-feed-wizard-status').innerHTML = '<span style="color:var(--red)">' + escapeHtml(String(err)) + '</span>';
|
|
20101
20104
|
}
|
|
20102
20105
|
}
|
|
20103
20106
|
|
|
@@ -20210,8 +20213,8 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
20210
20213
|
const slug = document.getElementById('brain-webhook-slug').value.trim();
|
|
20211
20214
|
const folder = document.getElementById('brain-webhook-folder').value.trim() || ('04-Ingest/' + slug);
|
|
20212
20215
|
const statusEl = document.getElementById('brain-webhook-status');
|
|
20213
|
-
if (!slug) { statusEl.innerHTML = '<span style="color
|
|
20214
|
-
if (!/^[a-z][a-z0-9_-]*$/.test(slug)) { statusEl.innerHTML = '<span style="color
|
|
20216
|
+
if (!slug) { statusEl.innerHTML = '<span style="color:var(--red)">slug required</span>'; return; }
|
|
20217
|
+
if (!/^[a-z][a-z0-9_-]*$/.test(slug)) { statusEl.innerHTML = '<span style="color:var(--red)">slug must be lowercase alphanumeric</span>'; return; }
|
|
20215
20218
|
|
|
20216
20219
|
// 1) Generate a random 32-byte secret and save it under ref "webhook_<slug>"
|
|
20217
20220
|
const bytes = new Uint8Array(32);
|
|
@@ -20226,7 +20229,7 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
20226
20229
|
});
|
|
20227
20230
|
if (!credResp.ok) {
|
|
20228
20231
|
const e = await credResp.json();
|
|
20229
|
-
statusEl.innerHTML = '<span style="color
|
|
20232
|
+
statusEl.innerHTML = '<span style="color:var(--red)">Secret save failed: ' + escapeHtml(e.error || 'unknown') + '</span>';
|
|
20230
20233
|
return;
|
|
20231
20234
|
}
|
|
20232
20235
|
|
|
@@ -20246,7 +20249,7 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
20246
20249
|
});
|
|
20247
20250
|
if (!resp.ok) {
|
|
20248
20251
|
const e = await resp.json();
|
|
20249
|
-
statusEl.innerHTML = '<span style="color
|
|
20252
|
+
statusEl.innerHTML = '<span style="color:var(--red)">' + escapeHtml(e.error || 'save failed') + '</span>';
|
|
20250
20253
|
return;
|
|
20251
20254
|
}
|
|
20252
20255
|
|
|
@@ -20271,7 +20274,7 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
20271
20274
|
const cronExpr = document.getElementById('brain-poll-cron').value.trim();
|
|
20272
20275
|
const folder = document.getElementById('brain-poll-folder').value.trim();
|
|
20273
20276
|
const statusEl = document.getElementById('brain-poll-status');
|
|
20274
|
-
if (!slug || !url) { statusEl.innerHTML = '<span style="color
|
|
20277
|
+
if (!slug || !url) { statusEl.innerHTML = '<span style="color:var(--red)">slug and URL required</span>'; return; }
|
|
20275
20278
|
|
|
20276
20279
|
const headers = brainCollectKv('headers');
|
|
20277
20280
|
const params = brainCollectKv('params');
|
|
@@ -20293,7 +20296,7 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
20293
20296
|
}),
|
|
20294
20297
|
});
|
|
20295
20298
|
const data = await resp.json();
|
|
20296
|
-
if (!resp.ok) { statusEl.innerHTML = '<span style="color
|
|
20299
|
+
if (!resp.ok) { statusEl.innerHTML = '<span style="color:var(--red)">' + escapeHtml(data.error || 'save failed') + '</span>'; return; }
|
|
20297
20300
|
statusEl.innerHTML = '<span style="color:#4ade80">✓ Saved</span>';
|
|
20298
20301
|
|
|
20299
20302
|
if (runNow) {
|
|
@@ -20302,7 +20305,7 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
20302
20305
|
const runData = await runResp.json();
|
|
20303
20306
|
statusEl.innerHTML = runResp.ok
|
|
20304
20307
|
? '<span style="color:#4ade80">✓ Saved + run: in=' + runData.recordsIn + ' written=' + runData.recordsWritten + '</span>'
|
|
20305
|
-
: '<span style="color
|
|
20308
|
+
: '<span style="color:var(--red)">Saved but run failed: ' + escapeHtml(runData.error || 'unknown') + '</span>';
|
|
20306
20309
|
}
|
|
20307
20310
|
brainLoadSources();
|
|
20308
20311
|
}
|
|
@@ -20323,7 +20326,7 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
20323
20326
|
const ref = document.getElementById('brain-cred-ref').value.trim();
|
|
20324
20327
|
const value = document.getElementById('brain-cred-val').value;
|
|
20325
20328
|
const statusEl = document.getElementById('brain-cred-status');
|
|
20326
|
-
if (!ref || !value) { statusEl.innerHTML = '<span style="color
|
|
20329
|
+
if (!ref || !value) { statusEl.innerHTML = '<span style="color:var(--red)">ref and value required</span>'; return; }
|
|
20327
20330
|
const resp = await apiFetch('/api/brain/credentials', {
|
|
20328
20331
|
method: 'POST', headers: { 'content-type': 'application/json' },
|
|
20329
20332
|
body: JSON.stringify({ ref, value }),
|
|
@@ -20331,7 +20334,7 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
20331
20334
|
const data = await resp.json();
|
|
20332
20335
|
statusEl.innerHTML = resp.ok
|
|
20333
20336
|
? '<span style="color:#4ade80">✓ Saved ' + escapeHtml(ref) + '</span>'
|
|
20334
|
-
: '<span style="color
|
|
20337
|
+
: '<span style="color:var(--red)">' + escapeHtml(data.error || 'save failed') + '</span>';
|
|
20335
20338
|
document.getElementById('brain-cred-ref').value = '';
|
|
20336
20339
|
document.getElementById('brain-cred-val').value = '';
|
|
20337
20340
|
brainShowCredsForm(); // refresh list
|
|
@@ -20425,7 +20428,7 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
20425
20428
|
});
|
|
20426
20429
|
const data = await resp.json();
|
|
20427
20430
|
if (!resp.ok) {
|
|
20428
|
-
statusEl.innerHTML = '<span style="color
|
|
20431
|
+
statusEl.innerHTML = '<span style="color:var(--red)">Upload failed: ' + escapeHtml(data.error || 'unknown') + '</span>';
|
|
20429
20432
|
return;
|
|
20430
20433
|
}
|
|
20431
20434
|
statusEl.innerHTML = '<span style="color:#4ade80">✓ Uploaded ' + data.count + ' file(s)</span>';
|
|
@@ -20433,7 +20436,7 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
20433
20436
|
// Kick off preview immediately
|
|
20434
20437
|
await brainPreviewSeed();
|
|
20435
20438
|
} catch (err) {
|
|
20436
|
-
statusEl.innerHTML = '<span style="color
|
|
20439
|
+
statusEl.innerHTML = '<span style="color:var(--red)">Upload error: ' + escapeHtml(String(err)) + '</span>';
|
|
20437
20440
|
}
|
|
20438
20441
|
}
|
|
20439
20442
|
|
|
@@ -32709,7 +32712,7 @@ async function deleteSkillFromStudio(name) {
|
|
|
32709
32712
|
await apiDelete('/api/skills/' + encodeURIComponent(name));
|
|
32710
32713
|
toast('Skill deleted', 'success');
|
|
32711
32714
|
refreshBuilderSkills();
|
|
32712
|
-
|
|
32715
|
+
refreshSkillsPage(); // also refresh the automations tab list
|
|
32713
32716
|
} catch(e) { toast('Error: ' + e, 'error'); }
|
|
32714
32717
|
}
|
|
32715
32718
|
|
|
@@ -33093,7 +33096,7 @@ async function saveBuilderArtifact() {
|
|
|
33093
33096
|
msgs.innerHTML += '<div style="text-align:center;padding:12px;color:var(--green);font-size:13px;font-weight:600">\\u2714 ' + esc(r.message || 'Saved') + '</div>';
|
|
33094
33097
|
msgs.scrollTop = msgs.scrollHeight;
|
|
33095
33098
|
document.getElementById('builder-save-btn').style.display = 'none';
|
|
33096
|
-
if (type === 'skill') {
|
|
33099
|
+
if (type === 'skill') { refreshSkillsPage(); refreshBuilderSkills(); }
|
|
33097
33100
|
if (type === 'cron') refreshCron();
|
|
33098
33101
|
if (type === 'agent') refreshTeamNav();
|
|
33099
33102
|
if (type === 'workflow') refreshWorkflows();
|
|
@@ -37299,7 +37302,7 @@ async function saveSkill() {
|
|
|
37299
37302
|
document.getElementById('skill-description').value = '';
|
|
37300
37303
|
document.getElementById('skill-triggers').value = '';
|
|
37301
37304
|
document.getElementById('skill-steps').value = '';
|
|
37302
|
-
|
|
37305
|
+
refreshSkillsPage();
|
|
37303
37306
|
} else {
|
|
37304
37307
|
toast('Failed: ' + (r.error || 'unknown'), 'error');
|
|
37305
37308
|
}
|
|
@@ -37311,7 +37314,7 @@ async function deleteSkill(name) {
|
|
|
37311
37314
|
try {
|
|
37312
37315
|
await apiDelete('/api/skills/' + encodeURIComponent(name));
|
|
37313
37316
|
toast('Skill deleted', 'success');
|
|
37314
|
-
|
|
37317
|
+
refreshSkillsPage();
|
|
37315
37318
|
} catch(e) { toast('Error: ' + e, 'error'); }
|
|
37316
37319
|
}
|
|
37317
37320
|
|
|
@@ -37643,152 +37646,8 @@ async function refreshBrokenJobs() {
|
|
|
37643
37646
|
}
|
|
37644
37647
|
}
|
|
37645
37648
|
|
|
37646
|
-
async function refreshPendingSkills() {
|
|
37647
|
-
try {
|
|
37648
|
-
var r = await apiFetch('/api/skills/pending');
|
|
37649
|
-
var d = await r.json();
|
|
37650
|
-
var pending = d.skills || [];
|
|
37651
|
-
var tabBadge = document.getElementById('tab-pending-skill-count');
|
|
37652
|
-
if (tabBadge) {
|
|
37653
|
-
tabBadge.textContent = String(pending.length);
|
|
37654
|
-
tabBadge.style.display = pending.length > 0 ? '' : 'none';
|
|
37655
|
-
}
|
|
37656
|
-
var card = document.getElementById('pending-skills-card');
|
|
37657
|
-
var countBadge = document.getElementById('pending-skills-count-badge');
|
|
37658
|
-
var container = document.getElementById('panel-pending-skills');
|
|
37659
|
-
if (!container) return;
|
|
37660
|
-
if (pending.length === 0) {
|
|
37661
|
-
if (card) card.style.display = 'none';
|
|
37662
|
-
container.innerHTML = '';
|
|
37663
|
-
return;
|
|
37664
|
-
}
|
|
37665
|
-
if (card) card.style.display = '';
|
|
37666
|
-
if (countBadge) countBadge.textContent = pending.length + ' pending';
|
|
37667
37649
|
|
|
37668
|
-
var html = '<div style="display:flex;flex-direction:column;gap:10px">';
|
|
37669
|
-
for (var s of pending) {
|
|
37670
|
-
var sourceTag = s.source === 'cron' ? '<span class="badge badge-green" style="font-size:10px">cron</span>'
|
|
37671
|
-
: s.source === 'unleashed' ? '<span class="badge badge-purple" style="font-size:10px">unleashed</span>'
|
|
37672
|
-
: s.source === 'chat' ? '<span class="badge badge-blue" style="font-size:10px">chat</span>'
|
|
37673
|
-
: '<span class="badge badge-gray" style="font-size:10px">' + esc(s.source || 'unknown') + '</span>';
|
|
37674
|
-
var age = s.createdAt ? timeAgo(s.createdAt) : '';
|
|
37675
|
-
var scopeTag = s.agentSlug
|
|
37676
|
-
? '<span style="font-size:10px;color:var(--text-muted)">for ' + esc(s.agentSlug) + '</span>'
|
|
37677
|
-
: '<span style="font-size:10px;color:var(--text-muted)">global</span>';
|
|
37678
|
-
html += '<div style="padding:12px;border:1px solid var(--border);border-radius:8px;background:var(--bg-secondary)">'
|
|
37679
|
-
+ '<div style="display:flex;align-items:center;gap:8px;margin-bottom:6px;flex-wrap:wrap">'
|
|
37680
|
-
+ '<strong>' + esc(s.title) + '</strong> ' + sourceTag + ' ' + scopeTag
|
|
37681
|
-
+ (age ? ' <span style="font-size:10px;color:var(--text-muted)">\\u00b7 learned ' + age + '</span>' : '')
|
|
37682
|
-
+ '<span style="margin-left:auto;display:flex;gap:6px">'
|
|
37683
|
-
+ '<button onclick="approvePendingSkill(\\x27' + esc(s.name) + '\\x27)" style="background:var(--accent);border:1px solid var(--accent);border-radius:4px;padding:3px 10px;font-size:11px;color:white;cursor:pointer">Approve</button>'
|
|
37684
|
-
+ '<button onclick="rejectPendingSkill(\\x27' + esc(s.name) + '\\x27)" style="background:none;border:1px solid var(--border);border-radius:4px;padding:3px 10px;font-size:11px;color:var(--red);cursor:pointer">Reject</button>'
|
|
37685
|
-
+ '</span>'
|
|
37686
|
-
+ '</div>'
|
|
37687
|
-
+ '<div style="font-size:12px;color:var(--text-secondary)">' + esc(s.description || '') + '</div>'
|
|
37688
|
-
+ '</div>';
|
|
37689
|
-
}
|
|
37690
|
-
html += '</div>';
|
|
37691
|
-
container.innerHTML = html;
|
|
37692
|
-
} catch(e) { /* non-fatal */ }
|
|
37693
|
-
}
|
|
37694
|
-
|
|
37695
|
-
async function approvePendingSkill(name) {
|
|
37696
|
-
try {
|
|
37697
|
-
var r = await apiJson('POST', '/api/skills/pending/' + encodeURIComponent(name) + '/approve', {});
|
|
37698
|
-
if (r && r.ok) {
|
|
37699
|
-
toast(r.message || 'Skill approved', 'success');
|
|
37700
|
-
refreshPendingSkills();
|
|
37701
|
-
refreshSkills();
|
|
37702
|
-
} else {
|
|
37703
|
-
toast((r && r.message) || 'Failed to approve', 'error');
|
|
37704
|
-
}
|
|
37705
|
-
} catch(e) { toast('Failed to approve skill', 'error'); }
|
|
37706
|
-
}
|
|
37707
37650
|
|
|
37708
|
-
async function rejectPendingSkill(name) {
|
|
37709
|
-
if (!confirm('Reject this pending skill? It will be deleted.')) return;
|
|
37710
|
-
try {
|
|
37711
|
-
var r = await apiJson('POST', '/api/skills/pending/' + encodeURIComponent(name) + '/reject', {});
|
|
37712
|
-
if (r && r.ok) {
|
|
37713
|
-
toast(r.message || 'Skill rejected', 'success');
|
|
37714
|
-
refreshPendingSkills();
|
|
37715
|
-
} else {
|
|
37716
|
-
toast((r && r.message) || 'Failed to reject', 'error');
|
|
37717
|
-
}
|
|
37718
|
-
} catch(e) { toast('Failed to reject skill', 'error'); }
|
|
37719
|
-
}
|
|
37720
|
-
|
|
37721
|
-
async function refreshSkills() {
|
|
37722
|
-
refreshPendingSkills();
|
|
37723
|
-
try {
|
|
37724
|
-
var r = await apiFetch('/api/skills');
|
|
37725
|
-
var d = await r.json();
|
|
37726
|
-
var skills = d.skills || [];
|
|
37727
|
-
var badge = document.getElementById('tab-skill-count');
|
|
37728
|
-
var countBadge = document.getElementById('skill-count-badge');
|
|
37729
|
-
if (badge) { badge.textContent = skills.length || ''; badge.style.display = skills.length > 0 ? '' : 'none'; }
|
|
37730
|
-
if (countBadge) countBadge.textContent = skills.length + ' skill' + (skills.length !== 1 ? 's' : '');
|
|
37731
|
-
var navSkillBadge = document.getElementById('nav-skill-count');
|
|
37732
|
-
if (navSkillBadge) { navSkillBadge.textContent = skills.length || ''; navSkillBadge.style.display = skills.length > 0 ? '' : 'none'; }
|
|
37733
|
-
var container = document.getElementById('panel-skills');
|
|
37734
|
-
if (!container) return;
|
|
37735
|
-
if (skills.length === 0) {
|
|
37736
|
-
container.innerHTML = '<div class="empty-state">No skills learned yet. Skills are auto-extracted from successful tasks or taught manually above.</div>';
|
|
37737
|
-
return;
|
|
37738
|
-
}
|
|
37739
|
-
var html = '<div style="display:flex;flex-direction:column;gap:12px">';
|
|
37740
|
-
for (var s of skills) {
|
|
37741
|
-
var sourceTag = s.source === 'manual' ? '<span class="badge badge-blue" style="font-size:10px">taught</span>'
|
|
37742
|
-
: s.source === 'cron' ? '<span class="badge badge-green" style="font-size:10px">cron</span>'
|
|
37743
|
-
: s.source === 'unleashed' ? '<span class="badge badge-purple" style="font-size:10px">unleashed</span>'
|
|
37744
|
-
: '<span class="badge badge-gray" style="font-size:10px">' + esc(s.source) + '</span>';
|
|
37745
|
-
var allTriggers = s.triggers || [];
|
|
37746
|
-
var shownTriggers = allTriggers.slice(0, 5).map(function(t) { return '<code style="font-size:11px;background:var(--bg-tertiary);padding:2px 6px;border-radius:3px">' + esc(t) + '</code>'; }).join(' ');
|
|
37747
|
-
var triggers = shownTriggers + (allTriggers.length > 5 ? ' <span style="font-size:11px;color:var(--text-muted)">+' + (allTriggers.length - 5) + ' more</span>' : '');
|
|
37748
|
-
var age = s.updatedAt ? timeAgo(s.updatedAt) : '';
|
|
37749
|
-
// Source context for auto-extracted skills
|
|
37750
|
-
var sourceCtx = '';
|
|
37751
|
-
if (s.sourceJob) sourceCtx += '<span style="font-size:10px;color:var(--text-muted)">from ' + esc(s.sourceJob) + '</span>';
|
|
37752
|
-
if (s.createdAt && (s.source === 'cron' || s.source === 'unleashed')) {
|
|
37753
|
-
sourceCtx += '<span style="font-size:10px;color:var(--text-muted)">' + (sourceCtx ? ' \\u00b7 ' : '') + 'learned ' + timeAgo(s.createdAt) + '</span>';
|
|
37754
|
-
}
|
|
37755
|
-
// Tools used pills
|
|
37756
|
-
var toolsPills = '';
|
|
37757
|
-
if (s.toolsUsed && s.toolsUsed.length > 0) {
|
|
37758
|
-
var shownTools = s.toolsUsed.slice(0, 4).map(function(t) {
|
|
37759
|
-
return '<span style="font-size:10px;background:var(--accent)15;color:var(--accent);padding:1px 6px;border-radius:3px;border:1px solid var(--accent)30">' + esc(t) + '</span>';
|
|
37760
|
-
}).join(' ');
|
|
37761
|
-
toolsPills = '<div style="display:flex;gap:3px;flex-wrap:wrap;margin-top:4px">'
|
|
37762
|
-
+ shownTools
|
|
37763
|
-
+ (s.toolsUsed.length > 4 ? ' <span style="font-size:10px;color:var(--text-muted)">+' + (s.toolsUsed.length - 4) + '</span>' : '')
|
|
37764
|
-
+ '</div>';
|
|
37765
|
-
}
|
|
37766
|
-
var retrieval7d = (typeof s.retrievals7d === 'number' && s.retrievals7d > 0)
|
|
37767
|
-
? ' \\u00b7 ' + s.retrievals7d + ' retrievals (7d)'
|
|
37768
|
-
: '';
|
|
37769
|
-
html += '<div id="skill-card-' + esc(s.name) + '" style="padding:12px;border:1px solid var(--border);border-radius:8px;background:var(--bg-secondary)">'
|
|
37770
|
-
+ '<div style="display:flex;align-items:center;gap:8px;margin-bottom:6px">'
|
|
37771
|
-
+ '<strong style="cursor:pointer" onclick="expandSkill(\\x27' + esc(s.name) + '\\x27)">' + esc(s.title) + '</strong> ' + sourceTag
|
|
37772
|
-
+ (sourceCtx ? ' ' + sourceCtx : '')
|
|
37773
|
-
+ '<span style="margin-left:auto;display:flex;align-items:center;gap:8px">'
|
|
37774
|
-
+ '<span style="font-size:11px;color:var(--text-muted)">used ' + s.useCount + 'x' + (age ? ' \\u00b7 ' + age : '') + retrieval7d + '</span>'
|
|
37775
|
-
+ '<button onclick="expandSkill(\\x27' + esc(s.name) + '\\x27)" style="background:none;border:1px solid var(--border);border-radius:4px;padding:2px 8px;font-size:11px;color:var(--text-secondary);cursor:pointer">View</button>'
|
|
37776
|
-
+ '<button onclick="editSkillInBuilder(\\x27' + esc(s.name) + '\\x27)" style="background:none;border:1px solid var(--border);border-radius:4px;padding:2px 8px;font-size:11px;color:var(--accent);cursor:pointer">Edit</button>'
|
|
37777
|
-
+ '<button onclick="deleteSkill(\\x27' + esc(s.name) + '\\x27)" style="background:none;border:1px solid var(--border);border-radius:4px;padding:2px 8px;font-size:11px;color:var(--red);cursor:pointer">Delete</button>'
|
|
37778
|
-
+ '</span>'
|
|
37779
|
-
+ '</div>'
|
|
37780
|
-
+ '<div style="font-size:12px;color:var(--text-secondary);margin-bottom:4px">' + esc(s.description) + '</div>'
|
|
37781
|
-
+ (triggers ? '<div style="display:flex;gap:4px;flex-wrap:wrap">' + triggers + '</div>' : '')
|
|
37782
|
-
+ toolsPills
|
|
37783
|
-
+ '</div>';
|
|
37784
|
-
}
|
|
37785
|
-
html += '</div>';
|
|
37786
|
-
container.innerHTML = html;
|
|
37787
|
-
} catch(e) {
|
|
37788
|
-
var c = document.getElementById('panel-skills');
|
|
37789
|
-
if (c) c.innerHTML = '<div class="empty-state" style="color:var(--red)">Failed to load skills</div>';
|
|
37790
|
-
}
|
|
37791
|
-
}
|
|
37792
37651
|
|
|
37793
37652
|
// ── Agent-scoped Skills (Training tab) ──
|
|
37794
37653
|
function toggleAgentTeachSkill() {
|
package/dist/cli/index.js
CHANGED
|
@@ -2728,27 +2728,47 @@ skillsCmd
|
|
|
2728
2728
|
const RESET = '\x1b[0m';
|
|
2729
2729
|
try {
|
|
2730
2730
|
process.env.CLEMENTINE_HOME = BASE_DIR;
|
|
2731
|
-
|
|
2732
|
-
|
|
2731
|
+
// 1.18.121 — switched from skill-extractor (flat-only loader, broken
|
|
2732
|
+
// since the 1.18.110 folder-form migration so this command was hiding
|
|
2733
|
+
// every migrated skill) to skill-store, which walks both layouts and
|
|
2734
|
+
// reads the canonical Anthropic frontmatter.
|
|
2735
|
+
const { listSkills } = await import('../agent/skill-store.js');
|
|
2736
|
+
const skills = listSkills();
|
|
2737
|
+
if (opts.agent) {
|
|
2738
|
+
console.error(` ${DIM}Note: --agent filter is informational only since the 1.19 catalog. Showing all skills.${RESET}`);
|
|
2739
|
+
}
|
|
2733
2740
|
if (opts.json) {
|
|
2734
|
-
console.log(JSON.stringify(skills
|
|
2741
|
+
console.log(JSON.stringify(skills.map(s => ({
|
|
2742
|
+
name: s.frontmatter.name,
|
|
2743
|
+
title: s.frontmatter.title ?? s.frontmatter.name,
|
|
2744
|
+
description: s.frontmatter.description ?? '',
|
|
2745
|
+
layout: s.layout,
|
|
2746
|
+
schemaVersion: s.schemaVersion,
|
|
2747
|
+
scope: s.scope,
|
|
2748
|
+
useCount: (s.frontmatter.clementine?.useCount ?? s.frontmatter.useCount ?? 0),
|
|
2749
|
+
updatedAt: (s.frontmatter.clementine?.updatedAt ?? ''),
|
|
2750
|
+
filePath: s.filePath,
|
|
2751
|
+
})), null, 2));
|
|
2735
2752
|
return;
|
|
2736
2753
|
}
|
|
2737
2754
|
if (skills.length === 0) {
|
|
2738
2755
|
console.log();
|
|
2739
|
-
console.log(` ${DIM}No
|
|
2740
|
-
console.log(`
|
|
2741
|
-
console.log(`
|
|
2756
|
+
console.log(` ${DIM}No skills found.${RESET}`);
|
|
2757
|
+
console.log(` Author one in chat ("Hey Clemmy, create a skill called X that…")`);
|
|
2758
|
+
console.log(` or in the dashboard's Skills page (${BOLD}clementine dashboard${RESET}).`);
|
|
2742
2759
|
console.log();
|
|
2743
2760
|
return;
|
|
2744
2761
|
}
|
|
2745
2762
|
console.log();
|
|
2746
|
-
console.log(` ${BOLD}${'NAME'.padEnd(
|
|
2763
|
+
console.log(` ${BOLD}${'NAME'.padEnd(40)}${'LAYOUT'.padEnd(8)}${'USES'.padEnd(8)}${'UPDATED'}${RESET}`);
|
|
2747
2764
|
console.log(` ${DIM}${'─'.repeat(80)}${RESET}`);
|
|
2748
2765
|
for (const s of skills) {
|
|
2749
|
-
const
|
|
2750
|
-
const
|
|
2751
|
-
|
|
2766
|
+
const ext = s.frontmatter.clementine ?? {};
|
|
2767
|
+
const useCount = ext.useCount ?? s.frontmatter.useCount ?? 0;
|
|
2768
|
+
const rawUpdated = ext.updatedAt ?? '';
|
|
2769
|
+
const updated = String(rawUpdated).slice(0, 10);
|
|
2770
|
+
const layoutLabel = s.layout === 'folder' ? 'folder' : 'flat';
|
|
2771
|
+
console.log(` ${s.frontmatter.name.slice(0, 38).padEnd(40)}${CYAN}${layoutLabel.padEnd(8)}${RESET}${String(useCount).padEnd(8)}${DIM}${updated}${RESET}`);
|
|
2752
2772
|
}
|
|
2753
2773
|
console.log();
|
|
2754
2774
|
console.log(` ${DIM}Total: ${skills.length} skill${skills.length === 1 ? '' : 's'}.${RESET}`);
|
|
@@ -1185,12 +1185,12 @@ export class CronScheduler {
|
|
|
1185
1185
|
const startedAt = new Date();
|
|
1186
1186
|
try {
|
|
1187
1187
|
// Standard cron jobs get a timeout via SDK AbortController (advisor may override)
|
|
1188
|
-
let response = await this.gateway.handleCronJob(job.name, jobPrompt, job.tier, job.maxTurns, job.model, job.workDir, job.mode, job.maxHours, effectiveTimeoutMs, job.successCriteria, job.agentSlug, job.skills, job.allowedTools, job.allowedMcpServers, job.predictable);
|
|
1188
|
+
let response = await this.gateway.handleCronJob(job.name, jobPrompt, job.tier, job.maxTurns, job.model, job.workDir, job.mode, job.maxHours, effectiveTimeoutMs, job.successCriteria, job.agentSlug, job.skills, job.allowedTools, job.allowedMcpServers, job.predictable, job.addDirs);
|
|
1189
1189
|
// alwaysDeliver: retry once if the response is empty/noise
|
|
1190
1190
|
if (job.alwaysDeliver && (!response || CronScheduler.isCronNoise(response))) {
|
|
1191
1191
|
logger.info({ job: job.name }, 'alwaysDeliver: empty/noise response — retrying once');
|
|
1192
1192
|
try {
|
|
1193
|
-
const retryResponse = await this.gateway.handleCronJob(job.name, jobPrompt + '\n\nYou MUST produce a brief status update. Do NOT return __NOTHING__.', job.tier, job.maxTurns, job.model, job.workDir, job.mode, job.maxHours, effectiveTimeoutMs, job.successCriteria, job.agentSlug, job.skills, job.allowedTools, job.allowedMcpServers, job.predictable);
|
|
1193
|
+
const retryResponse = await this.gateway.handleCronJob(job.name, jobPrompt + '\n\nYou MUST produce a brief status update. Do NOT return __NOTHING__.', job.tier, job.maxTurns, job.model, job.workDir, job.mode, job.maxHours, effectiveTimeoutMs, job.successCriteria, job.agentSlug, job.skills, job.allowedTools, job.allowedMcpServers, job.predictable, job.addDirs);
|
|
1194
1194
|
if (retryResponse && !CronScheduler.isCronNoise(retryResponse)) {
|
|
1195
1195
|
response = retryResponse;
|
|
1196
1196
|
}
|
package/dist/gateway/router.d.ts
CHANGED
|
@@ -182,7 +182,11 @@ export declare class Gateway {
|
|
|
182
182
|
* identically. Affects only UI display + budget heuristics elsewhere. */
|
|
183
183
|
_mode?: 'standard' | 'unleashed', maxHours?: number, timeoutMs?: number, successCriteria?: string[], agentSlug?: string, pinnedSkills?: string[], allowedTools?: string[], allowedMcpServers?: string[],
|
|
184
184
|
/** Predictable (contract) mode — runner skips memory/team/auto-skills. */
|
|
185
|
-
predictable?: boolean
|
|
185
|
+
predictable?: boolean,
|
|
186
|
+
/** Extra read+exec scope for the SDK's Read/Bash/Glob tools. From the
|
|
187
|
+
* CronJobDefinition.addDirs YAML field. Combined inside runAgentCron
|
|
188
|
+
* with each pinned-skill folder. (1.18.121) */
|
|
189
|
+
addDirs?: string[]): Promise<string>;
|
|
186
190
|
/**
|
|
187
191
|
* PRD §10 / 1.18.91 — cancel an in-flight cron run by name. Returns true if
|
|
188
192
|
* an AbortController was found and abort() was called, false if nothing was
|
package/dist/gateway/router.js
CHANGED
|
@@ -1976,7 +1976,11 @@ export class Gateway {
|
|
|
1976
1976
|
// ── Trick capabilities (optional; preserve today's behavior when omitted) ─
|
|
1977
1977
|
pinnedSkills, allowedTools, allowedMcpServers,
|
|
1978
1978
|
/** Predictable (contract) mode — runner skips memory/team/auto-skills. */
|
|
1979
|
-
predictable
|
|
1979
|
+
predictable,
|
|
1980
|
+
/** Extra read+exec scope for the SDK's Read/Bash/Glob tools. From the
|
|
1981
|
+
* CronJobDefinition.addDirs YAML field. Combined inside runAgentCron
|
|
1982
|
+
* with each pinned-skill folder. (1.18.121) */
|
|
1983
|
+
addDirs) {
|
|
1980
1984
|
const releaseLane = await lanes.acquire('cron');
|
|
1981
1985
|
// Build a wall-clock abort timer from maxHours / timeoutMs.
|
|
1982
1986
|
// Whichever is shorter wins. Defaults to 1h if neither is set.
|
|
@@ -2022,6 +2026,7 @@ export class Gateway {
|
|
|
2022
2026
|
allowedTools,
|
|
2023
2027
|
allowedMcpServers,
|
|
2024
2028
|
predictable,
|
|
2029
|
+
addDirs,
|
|
2025
2030
|
});
|
|
2026
2031
|
scanner.refreshIntegrity();
|
|
2027
2032
|
// Stash trick-capability metadata for the scheduler to read when
|
package/dist/types.d.ts
CHANGED
|
@@ -767,23 +767,21 @@ export interface Feedback {
|
|
|
767
767
|
comment?: string;
|
|
768
768
|
createdAt?: string;
|
|
769
769
|
}
|
|
770
|
-
export interface BehavioralCorrection {
|
|
771
|
-
correction: string;
|
|
772
|
-
category: 'verbosity' | 'tone' | 'workflow' | 'format' | 'accuracy' | 'proactivity' | 'scope';
|
|
773
|
-
strength: 'explicit' | 'implicit';
|
|
774
|
-
}
|
|
775
|
-
export interface PreferenceLearned {
|
|
776
|
-
preference: string;
|
|
777
|
-
confidence: 'high' | 'medium' | 'low';
|
|
778
|
-
}
|
|
779
770
|
export interface SessionReflection {
|
|
780
771
|
id?: number;
|
|
781
772
|
sessionKey: string;
|
|
782
773
|
exchangeCount: number;
|
|
783
774
|
frictionSignals: string[];
|
|
784
775
|
qualityScore: number;
|
|
785
|
-
behavioralCorrections:
|
|
786
|
-
|
|
776
|
+
behavioralCorrections: Array<{
|
|
777
|
+
correction: string;
|
|
778
|
+
category: 'verbosity' | 'tone' | 'workflow' | 'format' | 'accuracy' | 'proactivity' | 'scope';
|
|
779
|
+
strength: 'explicit' | 'implicit';
|
|
780
|
+
}>;
|
|
781
|
+
preferencesLearned: Array<{
|
|
782
|
+
preference: string;
|
|
783
|
+
confidence: 'high' | 'medium' | 'low';
|
|
784
|
+
}>;
|
|
787
785
|
agentSlug?: string;
|
|
788
786
|
createdAt?: string;
|
|
789
787
|
}
|
|
@@ -835,13 +833,6 @@ export interface ExecutionPlan {
|
|
|
835
833
|
steps: PlanStep[];
|
|
836
834
|
synthesisPrompt: string;
|
|
837
835
|
}
|
|
838
|
-
export interface PlanProgressUpdate {
|
|
839
|
-
stepId: string;
|
|
840
|
-
status: 'waiting' | 'running' | 'done' | 'failed';
|
|
841
|
-
description: string;
|
|
842
|
-
durationMs?: number;
|
|
843
|
-
resultPreview?: string;
|
|
844
|
-
}
|
|
845
836
|
export interface WorkflowInput {
|
|
846
837
|
type: 'string' | 'number';
|
|
847
838
|
default?: string;
|
|
@@ -853,13 +844,6 @@ export interface WorkflowStepMcpConfig {
|
|
|
853
844
|
tool: string;
|
|
854
845
|
inputs?: Record<string, unknown>;
|
|
855
846
|
}
|
|
856
|
-
export interface WorkflowStepCliConfig {
|
|
857
|
-
cmd: string;
|
|
858
|
-
args?: string[];
|
|
859
|
-
workDir?: string;
|
|
860
|
-
timeoutMs?: number;
|
|
861
|
-
captureStderr?: boolean;
|
|
862
|
-
}
|
|
863
847
|
export interface WorkflowStepChannelConfig {
|
|
864
848
|
channel: 'discord' | 'slack' | 'telegram' | 'whatsapp' | 'email' | 'webhook';
|
|
865
849
|
target: string;
|
|
@@ -891,7 +875,16 @@ export interface WorkflowStep {
|
|
|
891
875
|
workDir?: string;
|
|
892
876
|
kind?: WorkflowStepKind;
|
|
893
877
|
mcp?: WorkflowStepMcpConfig;
|
|
894
|
-
|
|
878
|
+
/** CLI step config — inline shape (was the standalone WorkflowStepCliConfig
|
|
879
|
+
* type that had zero external references; the field stays but the
|
|
880
|
+
* named alias was dropped in 1.18.122). */
|
|
881
|
+
cli?: {
|
|
882
|
+
cmd: string;
|
|
883
|
+
args?: string[];
|
|
884
|
+
workDir?: string;
|
|
885
|
+
timeoutMs?: number;
|
|
886
|
+
captureStderr?: boolean;
|
|
887
|
+
};
|
|
895
888
|
channel?: WorkflowStepChannelConfig;
|
|
896
889
|
transform?: WorkflowStepTransformConfig;
|
|
897
890
|
conditional?: WorkflowStepConditionalConfig;
|
|
@@ -1180,14 +1173,6 @@ export interface SessionRecord {
|
|
|
1180
1173
|
lastUsedAt: number;
|
|
1181
1174
|
userAgent?: string;
|
|
1182
1175
|
}
|
|
1183
|
-
export interface ConfigRevision {
|
|
1184
|
-
id?: number;
|
|
1185
|
-
agentSlug: string;
|
|
1186
|
-
fileName: string;
|
|
1187
|
-
content: string;
|
|
1188
|
-
changedBy?: string;
|
|
1189
|
-
createdAt?: string;
|
|
1190
|
-
}
|
|
1191
1176
|
export interface Lead {
|
|
1192
1177
|
id?: number;
|
|
1193
1178
|
agentSlug: string;
|
|
@@ -1202,16 +1187,6 @@ export interface Lead {
|
|
|
1202
1187
|
createdAt?: string;
|
|
1203
1188
|
updatedAt?: string;
|
|
1204
1189
|
}
|
|
1205
|
-
export interface SequenceEnrollment {
|
|
1206
|
-
id?: number;
|
|
1207
|
-
leadId: number;
|
|
1208
|
-
sequenceName: string;
|
|
1209
|
-
currentStep: number;
|
|
1210
|
-
status: 'active' | 'paused' | 'replied' | 'completed' | 'opted_out';
|
|
1211
|
-
nextStepDueAt?: string;
|
|
1212
|
-
startedAt?: string;
|
|
1213
|
-
updatedAt?: string;
|
|
1214
|
-
}
|
|
1215
1190
|
export interface Activity {
|
|
1216
1191
|
id?: number;
|
|
1217
1192
|
leadId?: number;
|
|
@@ -1222,40 +1197,6 @@ export interface Activity {
|
|
|
1222
1197
|
templateUsed?: string;
|
|
1223
1198
|
performedAt?: string;
|
|
1224
1199
|
}
|
|
1225
|
-
export interface SuppressionEntry {
|
|
1226
|
-
id?: number;
|
|
1227
|
-
email: string;
|
|
1228
|
-
reason: 'unsubscribe' | 'bounce' | 'manual' | 'complaint';
|
|
1229
|
-
addedAt?: string;
|
|
1230
|
-
addedBy?: string;
|
|
1231
|
-
}
|
|
1232
|
-
export interface ApprovalRequest {
|
|
1233
|
-
id?: number;
|
|
1234
|
-
agentSlug: string;
|
|
1235
|
-
actionType: 'email_send' | 'sequence_start' | 'escalation';
|
|
1236
|
-
summary: string;
|
|
1237
|
-
detail?: Record<string, unknown>;
|
|
1238
|
-
status: 'pending' | 'approved' | 'rejected';
|
|
1239
|
-
requestedAt?: string;
|
|
1240
|
-
resolvedAt?: string;
|
|
1241
|
-
resolvedBy?: string;
|
|
1242
|
-
}
|
|
1243
|
-
export interface SfSyncRecord {
|
|
1244
|
-
id?: number;
|
|
1245
|
-
localTable: 'leads' | 'activities';
|
|
1246
|
-
localId: number;
|
|
1247
|
-
sfObjectType: 'Lead' | 'Contact' | 'Opportunity' | 'Task' | 'Event';
|
|
1248
|
-
sfId: string;
|
|
1249
|
-
syncDirection: 'push' | 'pull';
|
|
1250
|
-
syncedAt?: string;
|
|
1251
|
-
syncStatus: 'success' | 'error' | 'conflict';
|
|
1252
|
-
errorMessage?: string;
|
|
1253
|
-
}
|
|
1254
|
-
export interface SfFieldMapping {
|
|
1255
|
-
localField: string;
|
|
1256
|
-
sfField: string;
|
|
1257
|
-
direction: 'bidirectional' | 'push-only' | 'pull-only';
|
|
1258
|
-
}
|
|
1259
1200
|
/** Supported v1 ingest formats detected by format-detector. */
|
|
1260
1201
|
export type DetectedFormat = 'csv' | 'json' | 'jsonl' | 'markdown' | 'pdf' | 'email' | 'docx' | 'unknown';
|
|
1261
1202
|
/** Operational mode for a source. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "clementine-agent",
|
|
3
|
-
"version": "1.18.
|
|
3
|
+
"version": "1.18.122",
|
|
4
4
|
"description": "Clementine — Personal AI Assistant (TypeScript)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -53,7 +53,6 @@
|
|
|
53
53
|
"node-cron": "^3.0.3",
|
|
54
54
|
"pdf-parse": "^1.1.1",
|
|
55
55
|
"pino": "^9.6.0",
|
|
56
|
-
"twilio": "^5.5.0",
|
|
57
56
|
"ws": "^8.19.0",
|
|
58
57
|
"zod": "^4.3.6"
|
|
59
58
|
},
|