clementine-agent 1.18.93 → 1.18.95
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/cli/dashboard.js +338 -89
- package/dist/config/clementine-json.js +3 -3
- package/package.json +1 -1
package/dist/cli/dashboard.js
CHANGED
|
@@ -10190,6 +10190,12 @@ If the tool returns nothing or errors, return an empty array \`[]\`.`,
|
|
|
10190
10190
|
sendPolicy: a.sendPolicy ?? null,
|
|
10191
10191
|
agentStatus: a.status ?? 'active',
|
|
10192
10192
|
budgetMonthlyCents: a.budgetMonthlyCents ?? 0,
|
|
10193
|
+
// Mission & persona — what the agent edit modal renders in
|
|
10194
|
+
// its single combined textarea.
|
|
10195
|
+
systemPromptBody: a.systemPromptBody ?? '',
|
|
10196
|
+
// Multi-project access list (separate from the legacy single
|
|
10197
|
+
// `project` binding above).
|
|
10198
|
+
projects: a.projects ?? [],
|
|
10193
10199
|
};
|
|
10194
10200
|
}));
|
|
10195
10201
|
}
|
|
@@ -14636,6 +14642,103 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
14636
14642
|
font-weight: 600;
|
|
14637
14643
|
}
|
|
14638
14644
|
|
|
14645
|
+
/* ── Agent edit modal — tabs + Equipment panel ─────────────────── */
|
|
14646
|
+
.agent-tab {
|
|
14647
|
+
background: transparent;
|
|
14648
|
+
border: 0;
|
|
14649
|
+
border-bottom: 2px solid transparent;
|
|
14650
|
+
color: var(--text-muted);
|
|
14651
|
+
cursor: pointer;
|
|
14652
|
+
font-size: 13px;
|
|
14653
|
+
padding: 8px 14px;
|
|
14654
|
+
margin-bottom: -1px;
|
|
14655
|
+
}
|
|
14656
|
+
.agent-tab:hover { color: var(--text-primary); }
|
|
14657
|
+
.agent-tab.active {
|
|
14658
|
+
color: var(--text-primary);
|
|
14659
|
+
border-bottom-color: var(--clementine);
|
|
14660
|
+
font-weight: 600;
|
|
14661
|
+
}
|
|
14662
|
+
.agent-tools-filter {
|
|
14663
|
+
background: transparent;
|
|
14664
|
+
border: 1px solid var(--border);
|
|
14665
|
+
border-radius: 4px;
|
|
14666
|
+
color: var(--text-muted);
|
|
14667
|
+
cursor: pointer;
|
|
14668
|
+
font-size: 11px;
|
|
14669
|
+
padding: 4px 8px;
|
|
14670
|
+
}
|
|
14671
|
+
.agent-tools-filter:hover { color: var(--text-primary); }
|
|
14672
|
+
.agent-tools-filter.active {
|
|
14673
|
+
background: var(--clementine);
|
|
14674
|
+
border-color: var(--clementine);
|
|
14675
|
+
color: #000;
|
|
14676
|
+
font-weight: 600;
|
|
14677
|
+
}
|
|
14678
|
+
.agent-tool-row {
|
|
14679
|
+
align-items: center;
|
|
14680
|
+
color: var(--text-primary);
|
|
14681
|
+
cursor: pointer;
|
|
14682
|
+
display: flex;
|
|
14683
|
+
font-size: 12px;
|
|
14684
|
+
gap: 6px;
|
|
14685
|
+
line-height: 1.4;
|
|
14686
|
+
padding: 4px 6px;
|
|
14687
|
+
border-radius: 4px;
|
|
14688
|
+
}
|
|
14689
|
+
.agent-tool-row:hover { background: rgba(255,255,255,0.03); }
|
|
14690
|
+
.agent-tool-row .tt-name { flex: 1; min-width: 0; }
|
|
14691
|
+
.agent-tool-row .tt-badge {
|
|
14692
|
+
background: rgba(255,255,255,0.06);
|
|
14693
|
+
border-radius: 3px;
|
|
14694
|
+
color: var(--text-muted);
|
|
14695
|
+
font-size: 9px;
|
|
14696
|
+
font-weight: 700;
|
|
14697
|
+
letter-spacing: 0.4px;
|
|
14698
|
+
padding: 1px 5px;
|
|
14699
|
+
text-transform: uppercase;
|
|
14700
|
+
}
|
|
14701
|
+
.agent-tool-row .tt-badge.cli { background: rgba(120, 200, 255, 0.12); color: #79c8ff; }
|
|
14702
|
+
.agent-tool-row .tt-badge.sdk { background: rgba(168, 200, 255, 0.12); color: #a8c8ff; }
|
|
14703
|
+
.agent-tool-row .tt-badge.mcp { background: rgba(180, 130, 255, 0.12); color: #c8a8ff; }
|
|
14704
|
+
.agent-tool-row .tt-badge.api { background: rgba(255, 200, 120, 0.12); color: #ffc878; }
|
|
14705
|
+
.agent-tool-row .tt-badge.composio { background: rgba(255, 138, 138, 0.14); color: #ff8a8a; }
|
|
14706
|
+
.agent-tool-row .tt-badge.project, .agent-tool-row .tt-badge['project-mcp'] { background: rgba(120, 255, 180, 0.12); color: #78ffb4; }
|
|
14707
|
+
.agent-tool-row .tt-status {
|
|
14708
|
+
align-items: center;
|
|
14709
|
+
color: var(--text-muted);
|
|
14710
|
+
display: inline-flex;
|
|
14711
|
+
font-size: 10px;
|
|
14712
|
+
gap: 3px;
|
|
14713
|
+
}
|
|
14714
|
+
.agent-tool-row .tt-status .tt-dot {
|
|
14715
|
+
border-radius: 50%;
|
|
14716
|
+
height: 7px;
|
|
14717
|
+
width: 7px;
|
|
14718
|
+
}
|
|
14719
|
+
.agent-tool-row .tt-status.ready .tt-dot { background: var(--green); }
|
|
14720
|
+
.agent-tool-row .tt-status.needs-setup .tt-dot { background: #f59e0b; }
|
|
14721
|
+
.agent-tool-row .tt-status.not-installed .tt-dot { background: #888; }
|
|
14722
|
+
.agent-tool-row .tt-status.blocked .tt-dot { background: #ef4444; }
|
|
14723
|
+
.agent-tool-cat {
|
|
14724
|
+
color: var(--text-muted);
|
|
14725
|
+
cursor: pointer;
|
|
14726
|
+
font-size: 11px;
|
|
14727
|
+
font-weight: 700;
|
|
14728
|
+
letter-spacing: 0.5px;
|
|
14729
|
+
margin: 10px 0 4px;
|
|
14730
|
+
text-transform: uppercase;
|
|
14731
|
+
user-select: none;
|
|
14732
|
+
}
|
|
14733
|
+
.agent-tool-cat .cat-count {
|
|
14734
|
+
color: var(--text-muted);
|
|
14735
|
+
font-weight: 500;
|
|
14736
|
+
margin-left: 6px;
|
|
14737
|
+
text-transform: none;
|
|
14738
|
+
letter-spacing: 0;
|
|
14739
|
+
}
|
|
14740
|
+
.agent-tool-row.hidden, .agent-tool-cat.hidden { display: none; }
|
|
14741
|
+
|
|
14639
14742
|
/* ── Office Hero — Clementine ─────────── */
|
|
14640
14743
|
.office-hero {
|
|
14641
14744
|
background: var(--bg-card);
|
|
@@ -19984,9 +20087,13 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
19984
20087
|
</div>
|
|
19985
20088
|
</div>
|
|
19986
20089
|
|
|
19987
|
-
<!-- Agent Create/Edit Modal
|
|
20090
|
+
<!-- Agent Create/Edit Modal — tabbed editor reused for hired agents
|
|
20091
|
+
AND for Clementine herself (see editClementine() in dashboard JS).
|
|
20092
|
+
Element IDs are stable so existing populator/submit code keeps
|
|
20093
|
+
working; new tabs (Equipment, Goals) add fields without breaking the
|
|
20094
|
+
old contract. -->
|
|
19988
20095
|
<div id="agent-modal" class="modal-overlay">
|
|
19989
|
-
<div class="modal" style="width:
|
|
20096
|
+
<div class="modal" style="width:760px;max-width:95vw">
|
|
19990
20097
|
<div class="modal-header">
|
|
19991
20098
|
<h3 id="agent-modal-title">Hire a New Team Member</h3>
|
|
19992
20099
|
<button class="btn-ghost btn-sm" onclick="hideAgentModal()">×</button>
|
|
@@ -19994,26 +20101,82 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
19994
20101
|
<div class="modal-body">
|
|
19995
20102
|
<form id="agent-form" onsubmit="submitAgentForm(event)">
|
|
19996
20103
|
<input type="hidden" id="agent-edit-slug" value="">
|
|
19997
|
-
<
|
|
19998
|
-
|
|
19999
|
-
|
|
20000
|
-
|
|
20001
|
-
|
|
20002
|
-
<
|
|
20003
|
-
<
|
|
20104
|
+
<input type="hidden" id="agent-edit-mode" value="agent"><!-- 'agent' or 'clementine' -->
|
|
20105
|
+
|
|
20106
|
+
<!-- Tab strip ---------------------------------------------------- -->
|
|
20107
|
+
<div id="agent-modal-tabs" style="display:flex;gap:2px;border-bottom:1px solid var(--border);margin-bottom:14px">
|
|
20108
|
+
<button type="button" class="agent-tab active" data-tab="identity" onclick="switchAgentTab('identity')">Identity</button>
|
|
20109
|
+
<button type="button" class="agent-tab" data-tab="equipment" onclick="switchAgentTab('equipment')">Equipment</button>
|
|
20110
|
+
<button type="button" class="agent-tab" data-tab="connections" onclick="switchAgentTab('connections')">Connections</button>
|
|
20111
|
+
<button type="button" class="agent-tab" data-tab="goals" onclick="switchAgentTab('goals')">Goals</button>
|
|
20112
|
+
<button type="button" class="agent-tab" data-tab="limits" onclick="switchAgentTab('limits')">Limits</button>
|
|
20004
20113
|
</div>
|
|
20005
|
-
|
|
20006
|
-
|
|
20007
|
-
|
|
20114
|
+
|
|
20115
|
+
<!-- Tab: Identity ------------------------------------------------ -->
|
|
20116
|
+
<div class="agent-tab-pane" data-tab-pane="identity">
|
|
20117
|
+
<div style="display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-bottom:12px">
|
|
20118
|
+
<div>
|
|
20119
|
+
<label style="display:block;color:var(--text-muted);font-size:12px;margin-bottom:4px">Name *</label>
|
|
20120
|
+
<input id="agent-name" required style="width:100%;padding:8px;background:var(--bg-input);border:1px solid var(--border);border-radius:6px;color:var(--text-primary)" placeholder="Research Agent">
|
|
20121
|
+
</div>
|
|
20122
|
+
<div>
|
|
20123
|
+
<label style="display:block;color:var(--text-muted);font-size:12px;margin-bottom:4px">Model</label>
|
|
20124
|
+
<select id="agent-model" style="width:100%;padding:8px;background:var(--bg-input);border:1px solid var(--border);border-radius:6px;color:var(--text-primary)">
|
|
20125
|
+
<option value="">Default (Sonnet)</option>
|
|
20126
|
+
<option value="haiku">Haiku</option>
|
|
20127
|
+
<option value="sonnet">Sonnet</option>
|
|
20128
|
+
<option value="opus">Opus</option>
|
|
20129
|
+
</select>
|
|
20130
|
+
</div>
|
|
20131
|
+
</div>
|
|
20132
|
+
<div id="agent-identity-row-2" style="margin-bottom:12px">
|
|
20133
|
+
<label style="display:block;color:var(--text-muted);font-size:12px;margin-bottom:4px">Profile Photo URL</label>
|
|
20134
|
+
<input id="agent-avatar-url" style="width:100%;padding:8px;background:var(--bg-input);border:1px solid var(--border);border-radius:6px;color:var(--text-primary)" placeholder="https://example.com/avatar.png">
|
|
20135
|
+
</div>
|
|
20136
|
+
<div style="margin-bottom:12px">
|
|
20137
|
+
<label style="display:block;color:var(--text-muted);font-size:12px;margin-bottom:4px">Headline <span style="opacity:0.6">(short — appears on the agent card)</span> *</label>
|
|
20138
|
+
<input id="agent-description" required style="width:100%;padding:8px;background:var(--bg-input);border:1px solid var(--border);border-radius:6px;color:var(--text-primary)" placeholder="Deep-dive research and analysis">
|
|
20139
|
+
</div>
|
|
20140
|
+
<div style="margin-bottom:12px">
|
|
20141
|
+
<label style="display:block;color:var(--text-muted);font-size:12px;margin-bottom:4px">Mission & persona <span style="opacity:0.6">(full system-prompt body — voice, expertise, working rules)</span></label>
|
|
20142
|
+
<textarea id="agent-personality" rows="8" style="width:100%;padding:8px;background:var(--bg-input);border:1px solid var(--border);border-radius:6px;color:var(--text-primary);resize:vertical;font-family:inherit" placeholder="You are a Research Agent specializing in..."></textarea>
|
|
20143
|
+
</div>
|
|
20144
|
+
<div id="agent-project-row" style="margin-bottom:12px">
|
|
20145
|
+
<label style="display:block;color:var(--text-muted);font-size:12px;margin-bottom:4px">Primary Project <span style="opacity:0.6">(optional — additional project access lives in the Equipment tab)</span></label>
|
|
20146
|
+
<select id="agent-project" style="width:100%;padding:8px;background:var(--bg-input);border:1px solid var(--border);border-radius:6px;color:var(--text-primary)">
|
|
20147
|
+
<option value="">None</option>
|
|
20148
|
+
</select>
|
|
20149
|
+
</div>
|
|
20008
20150
|
</div>
|
|
20009
|
-
|
|
20010
|
-
|
|
20011
|
-
|
|
20151
|
+
|
|
20152
|
+
<!-- Tab: Equipment ----------------------------------------------- -->
|
|
20153
|
+
<div class="agent-tab-pane" data-tab-pane="equipment" style="display:none">
|
|
20154
|
+
<div style="display:flex;gap:8px;align-items:center;margin-bottom:8px;flex-wrap:wrap">
|
|
20155
|
+
<input id="agent-tools-search" type="search" placeholder="Search tools, CLIs, MCP, projects, integrations..." oninput="filterAgentTools()" style="flex:1;min-width:240px;padding:7px 10px;background:var(--bg-input);border:1px solid var(--border);border-radius:6px;color:var(--text-primary);font-size:12px">
|
|
20156
|
+
<div id="agent-tools-filters" style="display:flex;gap:4px">
|
|
20157
|
+
<button type="button" class="agent-tools-filter active" data-filter="all" onclick="setAgentToolsFilter('all')">All</button>
|
|
20158
|
+
<button type="button" class="agent-tools-filter" data-filter="enabled" onclick="setAgentToolsFilter('enabled')">Enabled</button>
|
|
20159
|
+
<button type="button" class="agent-tools-filter" data-filter="ready" onclick="setAgentToolsFilter('ready')">Ready</button>
|
|
20160
|
+
<button type="button" class="agent-tools-filter" data-filter="needs-setup" onclick="setAgentToolsFilter('needs-setup')">Needs setup</button>
|
|
20161
|
+
</div>
|
|
20162
|
+
</div>
|
|
20163
|
+
<div id="agent-tools-summary" style="font-size:11px;color:var(--text-muted);margin-bottom:6px">Loading…</div>
|
|
20164
|
+
<div id="agent-tools-panel" style="max-height:380px;overflow-y:auto;border:1px solid var(--border);border-radius:6px;padding:8px;background:var(--bg-input)">
|
|
20165
|
+
<div style="color:var(--text-muted);font-size:12px">Loading tools...</div>
|
|
20166
|
+
</div>
|
|
20167
|
+
<div style="margin-top:8px;font-size:11px;color:var(--text-muted);line-height:1.5">
|
|
20168
|
+
<span style="display:inline-block;width:8px;height:8px;border-radius:50%;background:var(--green);vertical-align:middle;margin-right:4px"></span>Ready
|
|
20169
|
+
<span style="display:inline-block;width:8px;height:8px;border-radius:50%;background:#f59e0b;vertical-align:middle;margin-right:4px"></span>Needs setup
|
|
20170
|
+
<span style="display:inline-block;width:8px;height:8px;border-radius:50%;background:#888;vertical-align:middle;margin-right:4px"></span>Not installed
|
|
20171
|
+
<span style="display:inline-block;width:8px;height:8px;border-radius:50%;background:#ef4444;vertical-align:middle;margin-right:4px"></span>Blocked
|
|
20172
|
+
</div>
|
|
20012
20173
|
</div>
|
|
20013
|
-
|
|
20014
|
-
|
|
20015
|
-
|
|
20016
|
-
|
|
20174
|
+
|
|
20175
|
+
<!-- Tab: Connections -------------------------------------------- -->
|
|
20176
|
+
<div class="agent-tab-pane" data-tab-pane="connections" style="display:none">
|
|
20177
|
+
<div style="margin-bottom:14px">
|
|
20178
|
+
<label style="display:block;color:var(--text-muted);font-size:12px;margin-bottom:4px">Channels <span style="opacity:0.6">(Discord channels this agent will listen + post in)</span></label>
|
|
20179
|
+
<div id="agent-channel-list" style="max-height:160px;overflow-y:auto;border:1px solid var(--border);border-radius:6px;padding:6px 8px;background:var(--bg-input)">
|
|
20017
20180
|
<div style="color:var(--text-muted);font-size:12px">Loading channels...</div>
|
|
20018
20181
|
</div>
|
|
20019
20182
|
<label style="display:flex;align-items:center;gap:6px;margin-top:6px;color:var(--text-muted);font-size:12px;cursor:pointer">
|
|
@@ -20025,76 +20188,15 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
20025
20188
|
Respond to all messages <span style="opacity:0.6">(not just @mentions)</span>
|
|
20026
20189
|
</label>
|
|
20027
20190
|
</div>
|
|
20028
|
-
<div>
|
|
20029
|
-
<label style="display:block;color:var(--text-muted);font-size:12px;margin-bottom:4px">
|
|
20030
|
-
<
|
|
20031
|
-
<option value="">Default (Sonnet)</option>
|
|
20032
|
-
<option value="haiku">Haiku</option>
|
|
20033
|
-
<option value="sonnet">Sonnet</option>
|
|
20034
|
-
<option value="opus">Opus</option>
|
|
20035
|
-
</select>
|
|
20036
|
-
</div>
|
|
20037
|
-
</div>
|
|
20038
|
-
<div style="display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-bottom:12px">
|
|
20039
|
-
<div>
|
|
20040
|
-
<label style="display:block;color:var(--text-muted);font-size:12px;margin-bottom:4px">Project Binding</label>
|
|
20041
|
-
<select id="agent-project" style="width:100%;padding:8px;background:var(--bg-input);border:1px solid var(--border);border-radius:6px;color:var(--text-primary)">
|
|
20042
|
-
<option value="">None</option>
|
|
20043
|
-
</select>
|
|
20044
|
-
</div>
|
|
20045
|
-
<div>
|
|
20046
|
-
<label style="display:block;color:var(--text-muted);font-size:12px;margin-bottom:4px">Security Clearance</label>
|
|
20047
|
-
<select id="agent-tier" style="width:100%;padding:8px;background:var(--bg-input);border:1px solid var(--border);border-radius:6px;color:var(--text-primary)">
|
|
20048
|
-
<option value="2">Tier 2 (Read/Write)</option>
|
|
20049
|
-
<option value="1">Tier 1 (Read-only)</option>
|
|
20050
|
-
</select>
|
|
20051
|
-
</div>
|
|
20052
|
-
<div>
|
|
20053
|
-
<label style="display:block;color:var(--text-muted);font-size:12px;margin-bottom:4px">Monthly Budget <span style="opacity:0.6">(cents, 0 = unlimited)</span></label>
|
|
20054
|
-
<input id="agent-budget" type="number" value="0" min="0" style="width:100%;padding:8px;background:var(--bg-input);border:1px solid var(--border);border-radius:6px;color:var(--text-primary)"
|
|
20055
|
-
placeholder="e.g. 5000 = $50/month">
|
|
20056
|
-
</div>
|
|
20057
|
-
</div>
|
|
20058
|
-
<details style="margin-bottom:12px;border:1px solid var(--border);border-radius:6px;padding:8px">
|
|
20059
|
-
<summary style="cursor:pointer;color:var(--text-muted);font-size:12px;font-weight:600">Autonomous Sending Policy <span style="opacity:0.6">(for email-capable agents)</span></summary>
|
|
20060
|
-
<div style="padding:8px 0 0;display:grid;grid-template-columns:1fr 1fr;gap:8px">
|
|
20061
|
-
<div>
|
|
20062
|
-
<label style="display:block;color:var(--text-muted);font-size:11px;margin-bottom:3px">Max Emails / Day</label>
|
|
20063
|
-
<input id="agent-send-max-daily" type="number" value="50" min="0" max="500" style="width:100%;padding:6px;background:var(--bg-input);border:1px solid var(--border);border-radius:4px;color:var(--text-primary);font-size:12px">
|
|
20064
|
-
</div>
|
|
20065
|
-
<div>
|
|
20066
|
-
<label style="display:block;color:var(--text-muted);font-size:11px;margin-bottom:3px">Approval Mode</label>
|
|
20067
|
-
<select id="agent-send-approval" style="width:100%;padding:6px;background:var(--bg-input);border:1px solid var(--border);border-radius:4px;color:var(--text-primary);font-size:12px">
|
|
20068
|
-
<option value="">Disabled (no autonomous sending)</option>
|
|
20069
|
-
<option value="none">None (fully autonomous)</option>
|
|
20070
|
-
<option value="first-in-sequence">First in sequence (approve first email per lead)</option>
|
|
20071
|
-
<option value="all">All (approve every send)</option>
|
|
20072
|
-
</select>
|
|
20073
|
-
</div>
|
|
20074
|
-
<div style="grid-column:span 2">
|
|
20075
|
-
<label style="display:flex;align-items:center;gap:6px;color:var(--text-muted);font-size:12px;cursor:pointer">
|
|
20076
|
-
<input id="agent-send-biz-hours" type="checkbox"> Restrict to business hours (8am–6pm)
|
|
20077
|
-
</label>
|
|
20078
|
-
</div>
|
|
20191
|
+
<div style="margin-bottom:14px">
|
|
20192
|
+
<label style="display:block;color:var(--text-muted);font-size:12px;margin-bottom:4px">Teammates this agent can message <span style="opacity:0.6">(comma-separated slugs)</span></label>
|
|
20193
|
+
<input id="agent-canmessage" style="width:100%;padding:8px;background:var(--bg-input);border:1px solid var(--border);border-radius:6px;color:var(--text-primary)" placeholder="analyst-agent, writer-agent">
|
|
20079
20194
|
</div>
|
|
20080
|
-
|
|
20081
|
-
|
|
20082
|
-
|
|
20083
|
-
<input id="agent-canmessage" style="width:100%;padding:8px;background:var(--bg-input);border:1px solid var(--border);border-radius:6px;color:var(--text-primary)" placeholder="analyst-agent, writer-agent">
|
|
20084
|
-
</div>
|
|
20085
|
-
<div style="margin-bottom:12px">
|
|
20086
|
-
<label style="display:block;color:var(--text-muted);font-size:12px;margin-bottom:4px">Equipment & Access <span style="opacity:0.6">(click category to toggle all)</span></label>
|
|
20087
|
-
<div id="agent-tools-panel" style="max-height:200px;overflow-y:auto;border:1px solid var(--border);border-radius:6px;padding:8px;background:var(--bg-input)">
|
|
20088
|
-
<div style="color:var(--text-muted);font-size:12px">Loading tools...</div>
|
|
20195
|
+
<div style="margin-bottom:14px">
|
|
20196
|
+
<label style="display:block;color:var(--text-muted);font-size:12px;margin-bottom:4px">Allowed Users <span style="opacity:0.6">(Discord user IDs that may DM this agent — comma-separated)</span></label>
|
|
20197
|
+
<input id="agent-allowed-users" style="width:100%;padding:8px;background:var(--bg-input);border:1px solid var(--border);border-radius:6px;color:var(--text-primary)" placeholder="123456789012345678, 987654321098765432">
|
|
20089
20198
|
</div>
|
|
20090
|
-
|
|
20091
|
-
<div style="margin-bottom:12px">
|
|
20092
|
-
<label style="display:block;color:var(--text-muted);font-size:12px;margin-bottom:4px">Allowed Users <span style="opacity:0.6">(Discord user IDs, comma-separated)</span></label>
|
|
20093
|
-
<input id="agent-allowed-users" style="width:100%;padding:8px;background:var(--bg-input);border:1px solid var(--border);border-radius:6px;color:var(--text-primary)" placeholder="123456789012345678, 987654321098765432">
|
|
20094
|
-
</div>
|
|
20095
|
-
<div style="margin-bottom:16px">
|
|
20096
|
-
<div style="font-weight:600;font-size:13px;color:var(--text-primary);margin-bottom:8px;border-top:1px solid var(--border);padding-top:12px">Platform Connections</div>
|
|
20097
|
-
|
|
20199
|
+
<div style="margin-bottom:8px;font-weight:600;font-size:13px;color:var(--text-primary);border-top:1px solid var(--border);padding-top:12px">Platform Bots</div>
|
|
20098
20200
|
<details id="discord-section" style="margin-bottom:10px">
|
|
20099
20201
|
<summary style="cursor:pointer;color:var(--text-muted);font-size:12px;font-weight:600;margin-bottom:6px">Discord Bot <span id="discord-status-dot" style="display:none;width:8px;height:8px;border-radius:50%;display:inline-block;margin-left:4px"></span></summary>
|
|
20100
20202
|
<div style="padding-left:8px">
|
|
@@ -20139,8 +20241,53 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
20139
20241
|
</div>
|
|
20140
20242
|
</div>
|
|
20141
20243
|
</details>
|
|
20244
|
+
</div><!-- /agent-tab-pane connections -->
|
|
20245
|
+
|
|
20246
|
+
<!-- Tab: Goals --------------------------------------------------- -->
|
|
20247
|
+
<div class="agent-tab-pane" data-tab-pane="goals" style="display:none">
|
|
20248
|
+
<div style="font-size:12px;color:var(--text-muted);margin-bottom:8px">Long-running objectives this agent owns. Reassigning here moves the goal's <code>owner</code> field — the existing Goals tab in Team picks the change up automatically.</div>
|
|
20249
|
+
<div id="agent-goals-panel" style="max-height:380px;overflow-y:auto;border:1px solid var(--border);border-radius:6px;padding:8px;background:var(--bg-input)">
|
|
20250
|
+
<div style="color:var(--text-muted);font-size:12px">Loading goals…</div>
|
|
20251
|
+
</div>
|
|
20142
20252
|
</div>
|
|
20143
|
-
|
|
20253
|
+
|
|
20254
|
+
<!-- Tab: Limits -------------------------------------------------- -->
|
|
20255
|
+
<div class="agent-tab-pane" data-tab-pane="limits" style="display:none">
|
|
20256
|
+
<div style="display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-bottom:14px">
|
|
20257
|
+
<div id="agent-tier-row">
|
|
20258
|
+
<label style="display:block;color:var(--text-muted);font-size:12px;margin-bottom:4px">Security Clearance</label>
|
|
20259
|
+
<select id="agent-tier" style="width:100%;padding:8px;background:var(--bg-input);border:1px solid var(--border);border-radius:6px;color:var(--text-primary)">
|
|
20260
|
+
<option value="2">Tier 2 (Read/Write)</option>
|
|
20261
|
+
<option value="1">Tier 1 (Read-only)</option>
|
|
20262
|
+
</select>
|
|
20263
|
+
</div>
|
|
20264
|
+
<div>
|
|
20265
|
+
<label style="display:block;color:var(--text-muted);font-size:12px;margin-bottom:4px">Monthly Budget <span style="opacity:0.6">(cents, 0 = unlimited)</span></label>
|
|
20266
|
+
<input id="agent-budget" type="number" value="0" min="0" style="width:100%;padding:8px;background:var(--bg-input);border:1px solid var(--border);border-radius:6px;color:var(--text-primary)" placeholder="e.g. 5000 = $50/month">
|
|
20267
|
+
</div>
|
|
20268
|
+
</div>
|
|
20269
|
+
<div style="font-weight:600;font-size:13px;color:var(--text-primary);margin-bottom:8px;border-top:1px solid var(--border);padding-top:12px">Autonomous Sending Policy <span style="opacity:0.6;font-weight:400">(for email-capable agents)</span></div>
|
|
20270
|
+
<div style="display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-bottom:8px">
|
|
20271
|
+
<div>
|
|
20272
|
+
<label style="display:block;color:var(--text-muted);font-size:11px;margin-bottom:3px">Max Emails / Day</label>
|
|
20273
|
+
<input id="agent-send-max-daily" type="number" value="50" min="0" max="500" style="width:100%;padding:6px;background:var(--bg-input);border:1px solid var(--border);border-radius:4px;color:var(--text-primary);font-size:12px">
|
|
20274
|
+
</div>
|
|
20275
|
+
<div>
|
|
20276
|
+
<label style="display:block;color:var(--text-muted);font-size:11px;margin-bottom:3px">Approval Mode</label>
|
|
20277
|
+
<select id="agent-send-approval" style="width:100%;padding:6px;background:var(--bg-input);border:1px solid var(--border);border-radius:4px;color:var(--text-primary);font-size:12px">
|
|
20278
|
+
<option value="">Disabled (no autonomous sending)</option>
|
|
20279
|
+
<option value="none">None (fully autonomous)</option>
|
|
20280
|
+
<option value="first-in-sequence">First in sequence (approve first email per lead)</option>
|
|
20281
|
+
<option value="all">All (approve every send)</option>
|
|
20282
|
+
</select>
|
|
20283
|
+
</div>
|
|
20284
|
+
</div>
|
|
20285
|
+
<label style="display:flex;align-items:center;gap:6px;color:var(--text-muted);font-size:12px;cursor:pointer">
|
|
20286
|
+
<input id="agent-send-biz-hours" type="checkbox"> Restrict to business hours (8am–6pm)
|
|
20287
|
+
</label>
|
|
20288
|
+
</div>
|
|
20289
|
+
|
|
20290
|
+
<div style="display:flex;gap:8px;justify-content:flex-end;margin-top:16px;border-top:1px solid var(--border);padding-top:12px">
|
|
20144
20291
|
<button type="button" class="btn" onclick="hideAgentModal()">Cancel</button>
|
|
20145
20292
|
<button type="submit" class="btn" style="background:var(--green);color:#000;font-weight:600" id="agent-submit-btn">Complete Hiring</button>
|
|
20146
20293
|
</div>
|
|
@@ -24514,7 +24661,7 @@ function renderRunListBody(allRuns) {
|
|
|
24514
24661
|
if (catOptions.length > 1) {
|
|
24515
24662
|
html += _runListChip('Category', catOptions, 'filterCategory');
|
|
24516
24663
|
}
|
|
24517
|
-
html += '<input type="search" placeholder="Filter by task name…" value="' + esc(_runListState.filterText) + '" oninput="onRunListSearch(this.value)" style="flex:1;min-width:200px;max-width:320px;padding:6px 10px;font-size:12px;border:1px solid var(--border);border-radius:6px;background:var(--bg-secondary);color:var(--text-primary)">';
|
|
24664
|
+
html += '<input type="search" id="runlist-filter-text" placeholder="Filter by task name…" value="' + esc(_runListState.filterText) + '" oninput="onRunListSearch(this.value)" style="flex:1;min-width:200px;max-width:320px;padding:6px 10px;font-size:12px;border:1px solid var(--border);border-radius:6px;background:var(--bg-secondary);color:var(--text-primary)">';
|
|
24518
24665
|
html += '<button class="btn-sm" onclick="resetRunListFilters()" style="font-size:11px">Reset to default</button>';
|
|
24519
24666
|
html += '</div>';
|
|
24520
24667
|
if (filtered.length === 0) {
|
|
@@ -25126,6 +25273,92 @@ function openRunOrTrace(jobName, runId) {
|
|
|
25126
25273
|
return openTraceViewer(jobName);
|
|
25127
25274
|
}
|
|
25128
25275
|
|
|
25276
|
+
// ── PRD §13 / 1.18.94: Replay tooling v1 ────────────────────────────────
|
|
25277
|
+
// Three small handlers wired to the buttons in the Run detail header.
|
|
25278
|
+
// They reuse existing endpoints — no new server-side surface. Future
|
|
25279
|
+
// versions add Rerun-from-step (needs SDK resume support) + Bulk replay.
|
|
25280
|
+
async function replayRerunRun(jobName) {
|
|
25281
|
+
if (!jobName) return;
|
|
25282
|
+
if (!confirm('Rerun "' + jobName + '" with the same prompt? This fires a new run immediately.')) return;
|
|
25283
|
+
try {
|
|
25284
|
+
var r = await apiFetch('/api/cron/run/' + encodeURIComponent(jobName), { method: 'POST' });
|
|
25285
|
+
var d = await r.json().catch(function() { return {}; });
|
|
25286
|
+
if (!r.ok) {
|
|
25287
|
+
// 409 = the task is already running (concurrency lock); explain that.
|
|
25288
|
+
if (r.status === 409) {
|
|
25289
|
+
toast(d.error || (jobName + ' is already running. Cancel the in-flight run first.'), 'error');
|
|
25290
|
+
} else {
|
|
25291
|
+
toast(d.error || ('Rerun failed (HTTP ' + r.status + ')'), 'error');
|
|
25292
|
+
}
|
|
25293
|
+
return;
|
|
25294
|
+
}
|
|
25295
|
+
toast('Rerunning ' + jobName + ' — watch the run list for the new entry.', 'success');
|
|
25296
|
+
// Don't auto-close the modal — the user just confirmed they want to
|
|
25297
|
+
// see the result. The new run will appear in the run-selector dropdown
|
|
25298
|
+
// (loaded by openTraceViewer's history fetch) within a few seconds.
|
|
25299
|
+
setTimeout(function() {
|
|
25300
|
+
if (typeof refreshCron === 'function') refreshCron();
|
|
25301
|
+
if (typeof refreshRunList === 'function') refreshRunList();
|
|
25302
|
+
}, 1200);
|
|
25303
|
+
} catch (e) { toast('Rerun failed: ' + e, 'error'); }
|
|
25304
|
+
}
|
|
25305
|
+
|
|
25306
|
+
async function replayCopyPrompt(jobName) {
|
|
25307
|
+
if (!jobName) return;
|
|
25308
|
+
try {
|
|
25309
|
+
// /api/cron returns the full job list with prompt fields — pull the
|
|
25310
|
+
// matching one and stuff it onto the clipboard. Cheap and avoids a
|
|
25311
|
+
// dedicated GET-by-name endpoint.
|
|
25312
|
+
var r = await apiFetch('/api/cron');
|
|
25313
|
+
var d = await r.json();
|
|
25314
|
+
var jobs = (d && d.jobs) || [];
|
|
25315
|
+
var found = null;
|
|
25316
|
+
for (var i = 0; i < jobs.length; i++) {
|
|
25317
|
+
if (String(jobs[i].name).toLowerCase() === String(jobName).toLowerCase()) { found = jobs[i]; break; }
|
|
25318
|
+
}
|
|
25319
|
+
if (!found || !found.prompt) {
|
|
25320
|
+
toast('No prompt found for ' + jobName, 'error');
|
|
25321
|
+
return;
|
|
25322
|
+
}
|
|
25323
|
+
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
25324
|
+
await navigator.clipboard.writeText(found.prompt);
|
|
25325
|
+
toast('Prompt copied to clipboard (' + found.prompt.length + ' chars).', 'success');
|
|
25326
|
+
} else {
|
|
25327
|
+
// Fallback for clipboard-less environments (rare in modern browsers,
|
|
25328
|
+
// but the dashboard runs over plain HTTP on localhost which can hit
|
|
25329
|
+
// the secure-context restriction in some setups).
|
|
25330
|
+
var ta = document.createElement('textarea');
|
|
25331
|
+
ta.value = found.prompt;
|
|
25332
|
+
document.body.appendChild(ta);
|
|
25333
|
+
ta.select();
|
|
25334
|
+
try { document.execCommand('copy'); toast('Prompt copied (legacy mode).', 'success'); }
|
|
25335
|
+
catch (e) { toast('Copy not supported in this browser. Open the editor instead.', 'error'); }
|
|
25336
|
+
document.body.removeChild(ta);
|
|
25337
|
+
}
|
|
25338
|
+
} catch (e) { toast('Copy prompt failed: ' + e, 'error'); }
|
|
25339
|
+
}
|
|
25340
|
+
|
|
25341
|
+
function replayOpenRunList(jobName) {
|
|
25342
|
+
if (!jobName) return;
|
|
25343
|
+
// Close the trace modal, switch to Run list tab, set the text filter to
|
|
25344
|
+
// the task name. The Run list already supports free-text task-name match.
|
|
25345
|
+
try { document.getElementById('trace-modal').classList.remove('show'); } catch (e) { /* ignore */ }
|
|
25346
|
+
// The Tasks page has three top-level Build tabs; the Run list lives on
|
|
25347
|
+
// tab #3. Switch via the existing tab handler if available; otherwise
|
|
25348
|
+
// fall back to setting the URL hash so the page handler picks it up.
|
|
25349
|
+
if (typeof switchBuildTab === 'function') {
|
|
25350
|
+
switchBuildTab('runs');
|
|
25351
|
+
}
|
|
25352
|
+
if (typeof _runListState !== 'undefined') {
|
|
25353
|
+
_runListState.filterText = jobName;
|
|
25354
|
+
if (typeof refreshRunList === 'function') refreshRunList();
|
|
25355
|
+
// Mirror filter into the visible input so users see why the list is filtered.
|
|
25356
|
+
var inp = document.getElementById('runlist-filter-text');
|
|
25357
|
+
if (inp) inp.value = jobName;
|
|
25358
|
+
}
|
|
25359
|
+
toast('Filtered run list to "' + jobName + '"', 'success');
|
|
25360
|
+
}
|
|
25361
|
+
|
|
25129
25362
|
// PRD Phase 4b / 1.18.86: Run detail viewer. Renders a waterfall of
|
|
25130
25363
|
// RunEvent rows from /api/runs/:runId/events. Color-coded by kind, paired
|
|
25131
25364
|
// tool_call→tool_result by toolUseId, with expandable per-span content.
|
|
@@ -25203,6 +25436,19 @@ function renderRunDetailWaterfall(events, runId, jobName) {
|
|
|
25203
25436
|
+ '<span style="flex:1"></span>'
|
|
25204
25437
|
+ '<code style="font-size:10px;color:var(--text-muted)">runId ' + esc(String(runId).slice(0, 12)) + '…</code>'
|
|
25205
25438
|
+ '</div>'
|
|
25439
|
+
// PRD §13 / 1.18.94 — Replay tooling v1. Three actions reachable from
|
|
25440
|
+
// every Run detail: rerun the same task with the same prompt (kicks off
|
|
25441
|
+
// a new run via the existing /api/cron/run/:job endpoint with
|
|
25442
|
+
// trigger='manual'), copy the prompt to clipboard for quick edits in
|
|
25443
|
+
// the editor, and jump to the Run list filtered to this task name.
|
|
25444
|
+
// Only rendered when we know the jobName (not for orphaned runs).
|
|
25445
|
+
+ (jobName
|
|
25446
|
+
? '<div style="display:flex;gap:8px;margin-top:10px;flex-wrap:wrap">'
|
|
25447
|
+
+ '<button class="btn-sm btn-success" onclick="replayRerunRun(\\x27' + jsStr(jobName) + '\\x27)" title="Fire this task once — same prompt, fresh run">▶ Rerun task</button>'
|
|
25448
|
+
+ '<button class="btn-sm" onclick="replayCopyPrompt(\\x27' + jsStr(jobName) + '\\x27)" title="Copy the prompt for this task to your clipboard">⧉ Copy prompt</button>'
|
|
25449
|
+
+ '<button class="btn-sm" onclick="replayOpenRunList(\\x27' + jsStr(jobName) + '\\x27)" title="Open the Run list filtered to this task">↗ Open run list</button>'
|
|
25450
|
+
+ '</div>'
|
|
25451
|
+
: '')
|
|
25206
25452
|
+ '</div>';
|
|
25207
25453
|
|
|
25208
25454
|
// Waterfall rows
|
|
@@ -33265,6 +33511,9 @@ async function refreshTeam() {
|
|
|
33265
33511
|
'<div class="office-hero-stat"><div class="stat-val">' + fmtTokens(clemTokenTotal) + '</div><div class="stat-lbl">Tokens</div></div>' +
|
|
33266
33512
|
'<div class="office-hero-stat"><div class="stat-val">' + (clem.crons ? clem.crons.total : 0) + '</div><div class="stat-lbl">Cron Jobs</div></div>' +
|
|
33267
33513
|
'</div>' +
|
|
33514
|
+
'<div class="office-hero-actions" style="margin-left:auto;display:flex;gap:6px">' +
|
|
33515
|
+
'<button class="btn btn-sm" onclick="editClementine()" title="Configure Clementine\\x27s persona, tools, and limits">Edit</button>' +
|
|
33516
|
+
'</div>' +
|
|
33268
33517
|
'</div>' +
|
|
33269
33518
|
(needsDiscordSetup ?
|
|
33270
33519
|
'<div class="discord-setup-banner" id="discord-setup-banner">' +
|
|
@@ -34,9 +34,9 @@ export const clementineJsonSchema = z.object({
|
|
|
34
34
|
autonomy: z.enum(['ask_first', 'balanced', 'act_when_safe']).optional(),
|
|
35
35
|
/**
|
|
36
36
|
* Dashboard-managed profile for the main agent (Clementine herself).
|
|
37
|
-
* Mirrors the per-agent edit surface so Tasks → Team can edit
|
|
38
|
-
*
|
|
39
|
-
* Every field is optional
|
|
37
|
+
* Mirrors the per-agent edit surface so Tasks → Team can edit her
|
|
38
|
+
* persona, tools, and limits without forcing users into Settings or
|
|
39
|
+
* env files. Every field is optional — absent fields fall through to
|
|
40
40
|
* env / compiled defaults via computeEffectiveConfig.
|
|
41
41
|
*/
|
|
42
42
|
profile: z.object({
|