@teamclaws/teamclaw 2026.3.21 → 2026.3.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -1
- package/api.ts +2 -2
- package/cli.mjs +1185 -0
- package/index.ts +24 -7
- package/openclaw.plugin.json +326 -2
- package/package.json +6 -9
- package/src/config.ts +29 -1
- package/src/controller/controller-service.ts +1 -0
- package/src/controller/controller-tools.ts +12 -1
- package/src/controller/http-server.ts +355 -10
- package/src/controller/local-worker-manager.ts +5 -3
- package/src/controller/prompt-injector.ts +6 -1
- package/src/controller/websocket.ts +1 -0
- package/src/controller/worker-provisioning.ts +93 -4
- package/src/install-defaults.ts +1 -0
- package/src/openclaw-workspace.ts +57 -1
- package/src/roles.ts +42 -7
- package/src/state.ts +6 -0
- package/src/task-executor.ts +1 -0
- package/src/types.ts +53 -1
- package/src/ui/app.js +138 -2
- package/src/ui/index.html +10 -0
- package/src/ui/style.css +148 -0
- package/src/worker/http-handler.ts +4 -0
- package/src/worker/prompt-injector.ts +1 -0
- package/src/worker/skill-installer.ts +302 -0
package/src/ui/app.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
let ws = null;
|
|
7
7
|
let currentFilter = "all";
|
|
8
8
|
let activeTab = "tasks";
|
|
9
|
-
let teamState = { workers: [], tasks: [], messages: [], clarifications: [] };
|
|
9
|
+
let teamState = { workers: [], tasks: [], controllerRuns: [], messages: [], clarifications: [] };
|
|
10
10
|
let selectedTaskId = null;
|
|
11
11
|
let selectedTaskDetail = null;
|
|
12
12
|
let selectedTaskDetailTab = "overview";
|
|
@@ -126,6 +126,25 @@
|
|
|
126
126
|
return "#";
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
+
function normalizeSkillList(skills) {
|
|
130
|
+
if (!Array.isArray(skills)) {
|
|
131
|
+
return [];
|
|
132
|
+
}
|
|
133
|
+
return skills
|
|
134
|
+
.map(function (skill) { return String(skill || "").trim(); })
|
|
135
|
+
.filter(Boolean);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function renderSkillPills(skills, className) {
|
|
139
|
+
const items = normalizeSkillList(skills);
|
|
140
|
+
if (items.length === 0) {
|
|
141
|
+
return "";
|
|
142
|
+
}
|
|
143
|
+
return '<div class="' + escapeHtml(className || "skill-pills") + '">' + items.map(function (skill) {
|
|
144
|
+
return '<span class="skill-pill">' + escapeHtml(skill) + "</span>";
|
|
145
|
+
}).join("") + "</div>";
|
|
146
|
+
}
|
|
147
|
+
|
|
129
148
|
function renderMarkdownInline(text) {
|
|
130
149
|
const codeTokens = [];
|
|
131
150
|
let safe = escapeHtml(text || "");
|
|
@@ -613,6 +632,9 @@
|
|
|
613
632
|
: null;
|
|
614
633
|
|
|
615
634
|
switch (event.type) {
|
|
635
|
+
case "controller:run":
|
|
636
|
+
handleControllerRunEvent(event.data || {});
|
|
637
|
+
break;
|
|
616
638
|
case "task:execution":
|
|
617
639
|
handleTaskExecutionEvent(event.data || {});
|
|
618
640
|
break;
|
|
@@ -642,12 +664,14 @@
|
|
|
642
664
|
teamState = {
|
|
643
665
|
workers: statusRes.workers || [],
|
|
644
666
|
tasks: statusRes.tasks || [],
|
|
667
|
+
controllerRuns: statusRes.controllerRuns || [],
|
|
645
668
|
messages: statusRes.messages || [],
|
|
646
669
|
clarifications: statusRes.clarifications || [],
|
|
647
670
|
};
|
|
648
671
|
|
|
649
672
|
renderWorkers(teamState.workers);
|
|
650
673
|
renderTasks(teamState.tasks);
|
|
674
|
+
renderControllerRuns(teamState.controllerRuns);
|
|
651
675
|
renderClarifications(teamState.clarifications);
|
|
652
676
|
renderMessages(teamState.messages);
|
|
653
677
|
renderRoles(rolesRes.roles || []);
|
|
@@ -707,6 +731,10 @@
|
|
|
707
731
|
const assignee = task.assignedWorkerId
|
|
708
732
|
? "Assigned to " + task.assignedWorkerId
|
|
709
733
|
: (task.assignedRole ? "Role: " + task.assignedRole : "Unassigned");
|
|
734
|
+
const recommendedSkills = normalizeSkillList(task.recommendedSkills);
|
|
735
|
+
const creatorBadge = task.createdBy
|
|
736
|
+
? '<span class="task-origin-badge">' + escapeHtml(task.createdBy) + "</span>"
|
|
737
|
+
: "";
|
|
710
738
|
const note = task.progress
|
|
711
739
|
? '<div class="task-note">' + escapeHtml(task.progress).slice(0, 220) + "</div>"
|
|
712
740
|
: "";
|
|
@@ -719,8 +747,9 @@
|
|
|
719
747
|
'<div class="task-card' + liveClass + '" data-task-id="' + escapeHtml(task.id) + '" tabindex="0" role="button" aria-label="Open details for ' + escapeHtml(task.title) + '">' +
|
|
720
748
|
' <span class="task-priority ' + escapeHtml(priority) + '">' + escapeHtml(priority) + "</span>" +
|
|
721
749
|
' <div class="task-body">' +
|
|
722
|
-
' <div class="task-title">' + escapeHtml(task.title) + "</div>" +
|
|
750
|
+
' <div class="task-title-row"><div class="task-title">' + escapeHtml(task.title) + "</div>" + creatorBadge + "</div>" +
|
|
723
751
|
(task.description ? '<div class="task-desc">' + escapeHtml(task.description).slice(0, 220) + "</div>" : "") +
|
|
752
|
+
renderSkillPills(recommendedSkills, "skill-pills task-skill-pills") +
|
|
724
753
|
note +
|
|
725
754
|
' <div class="task-meta">' +
|
|
726
755
|
' <span class="task-status-badge ' + escapeHtml(status) + '">' + escapeHtml(humanizeStatus(status)) + "</span>" +
|
|
@@ -734,6 +763,76 @@
|
|
|
734
763
|
}).join("");
|
|
735
764
|
}
|
|
736
765
|
|
|
766
|
+
function renderControllerRuns(runs) {
|
|
767
|
+
const container = $("#controller-runs");
|
|
768
|
+
if (!container) return;
|
|
769
|
+
|
|
770
|
+
const recentRuns = (runs || [])
|
|
771
|
+
.slice()
|
|
772
|
+
.sort(function (left, right) { return (right.updatedAt || 0) - (left.updatedAt || 0); })
|
|
773
|
+
.slice(0, 12);
|
|
774
|
+
|
|
775
|
+
if (recentRuns.length === 0) {
|
|
776
|
+
container.innerHTML = '<div class="empty-state">No controller activity yet</div>';
|
|
777
|
+
return;
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
container.innerHTML = recentRuns.map(function (run) {
|
|
781
|
+
const execution = run.execution || {};
|
|
782
|
+
const events = Array.isArray(execution.events) ? execution.events.slice(-5) : [];
|
|
783
|
+
const status = run.status || execution.status || "pending";
|
|
784
|
+
const source = run.source === "task_follow_up"
|
|
785
|
+
? (run.sourceTaskTitle ? "Follow-up after " + run.sourceTaskTitle : "Workflow follow-up")
|
|
786
|
+
: "Human intake";
|
|
787
|
+
const createdTasks = Array.isArray(run.createdTaskIds) ? run.createdTaskIds : [];
|
|
788
|
+
const createdTaskButtons = createdTasks.length > 0
|
|
789
|
+
? '<div class="controller-run-created-tasks">' + createdTasks.map(function (taskId) {
|
|
790
|
+
return '<button type="button" class="controller-run-task-link" data-open-task-id="' + escapeHtml(taskId) + '">' + escapeHtml(taskId) + "</button>";
|
|
791
|
+
}).join("") + "</div>"
|
|
792
|
+
: "";
|
|
793
|
+
const replyBlock = run.reply
|
|
794
|
+
? '<div class="controller-run-section"><div class="controller-run-section-title">Reply</div><div class="markdown-body">' + renderMarkdownContent(run.reply) + "</div></div>"
|
|
795
|
+
: "";
|
|
796
|
+
const errorBlock = run.error
|
|
797
|
+
? '<div class="controller-run-section"><div class="controller-run-section-title">Error</div><div class="markdown-body">' + renderMarkdownContent(run.error) + "</div></div>"
|
|
798
|
+
: "";
|
|
799
|
+
const eventsBlock = events.length > 0
|
|
800
|
+
? '<div class="controller-run-events">' + events.map(function (event) {
|
|
801
|
+
const meta = [event.source || "", formatTime(event.createdAt)].filter(Boolean).join(" • ");
|
|
802
|
+
return (
|
|
803
|
+
'<div class="controller-run-event">' +
|
|
804
|
+
' <div class="controller-run-event-header">' +
|
|
805
|
+
' <span class="controller-run-event-label">' + escapeHtml(humanizeStatus(event.phase || event.type || "event")) + '</span>' +
|
|
806
|
+
' <span class="controller-run-event-meta">' + escapeHtml(meta) + "</span>" +
|
|
807
|
+
" </div>" +
|
|
808
|
+
' <div class="controller-run-event-body markdown-body">' + renderMarkdownContent(event.message || "") + "</div>" +
|
|
809
|
+
"</div>"
|
|
810
|
+
);
|
|
811
|
+
}).join("") + "</div>"
|
|
812
|
+
: "";
|
|
813
|
+
|
|
814
|
+
return (
|
|
815
|
+
'<article class="controller-run-card">' +
|
|
816
|
+
' <div class="controller-run-header">' +
|
|
817
|
+
' <div class="controller-run-heading">' +
|
|
818
|
+
' <div class="controller-run-kicker">' + escapeHtml(source) + "</div>" +
|
|
819
|
+
' <h3>' + escapeHtml(run.title || "Controller run") + "</h3>" +
|
|
820
|
+
' <div class="controller-run-meta">Session: ' + escapeHtml(run.sessionKey || "—") + ' • Updated: ' + escapeHtml(formatTime(run.updatedAt) || "—") + "</div>" +
|
|
821
|
+
" </div>" +
|
|
822
|
+
' <span class="controller-run-status ' + escapeHtml(status) + '">' + escapeHtml(humanizeStatus(status)) + "</span>" +
|
|
823
|
+
" </div>" +
|
|
824
|
+
' <div class="controller-run-section"><div class="controller-run-section-title">Request</div><div class="markdown-body">' + renderMarkdownContent(run.request || "") + "</div></div>" +
|
|
825
|
+
replyBlock +
|
|
826
|
+
errorBlock +
|
|
827
|
+
(createdTaskButtons
|
|
828
|
+
? '<div class="controller-run-section"><div class="controller-run-section-title">Created Tasks</div>' + createdTaskButtons + "</div>"
|
|
829
|
+
: "") +
|
|
830
|
+
eventsBlock +
|
|
831
|
+
"</article>"
|
|
832
|
+
);
|
|
833
|
+
}).join("");
|
|
834
|
+
}
|
|
835
|
+
|
|
737
836
|
function syncSelectedTaskSummary() {
|
|
738
837
|
if (!selectedTaskId || !selectedTaskDetail) {
|
|
739
838
|
return;
|
|
@@ -873,6 +972,9 @@
|
|
|
873
972
|
" <h3>Description</h3>" +
|
|
874
973
|
renderMarkdownCard(task.description || "No description") +
|
|
875
974
|
"</div>" +
|
|
975
|
+
(normalizeSkillList(task.recommendedSkills).length > 0
|
|
976
|
+
? '<div class="task-detail-section"><h3>Recommended Skills</h3>' + renderSkillPills(task.recommendedSkills, "skill-pills task-detail-skill-pills") + "</div>"
|
|
977
|
+
: "") +
|
|
876
978
|
(task.progress
|
|
877
979
|
? '<div class="task-detail-section"><h3>Latest Progress</h3>' + renderMarkdownCard(task.progress) + "</div>"
|
|
878
980
|
: "") +
|
|
@@ -1028,6 +1130,22 @@
|
|
|
1028
1130
|
renderTaskDetail();
|
|
1029
1131
|
}
|
|
1030
1132
|
|
|
1133
|
+
function handleControllerRunEvent(payload) {
|
|
1134
|
+
if (!payload || !payload.id) {
|
|
1135
|
+
return;
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
const runs = (teamState.controllerRuns || []).slice();
|
|
1139
|
+
const index = runs.findIndex(function (run) { return run.id === payload.id; });
|
|
1140
|
+
if (index === -1) {
|
|
1141
|
+
runs.push(payload);
|
|
1142
|
+
} else {
|
|
1143
|
+
runs[index] = Object.assign({}, runs[index], payload);
|
|
1144
|
+
}
|
|
1145
|
+
teamState.controllerRuns = runs;
|
|
1146
|
+
renderControllerRuns(teamState.controllerRuns);
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1031
1149
|
function renderClarifications(clarifications) {
|
|
1032
1150
|
const container = $("#clarifications-list");
|
|
1033
1151
|
if (!container) return;
|
|
@@ -1255,6 +1373,9 @@
|
|
|
1255
1373
|
event.preventDefault();
|
|
1256
1374
|
const title = $("#task-title").value.trim();
|
|
1257
1375
|
const desc = $("#task-desc").value.trim();
|
|
1376
|
+
const recommendedSkills = normalizeSkillList(
|
|
1377
|
+
($("#task-recommended-skills").value || "").split(","),
|
|
1378
|
+
);
|
|
1258
1379
|
const priority = $("#task-priority").value;
|
|
1259
1380
|
const role = $("#task-role").value;
|
|
1260
1381
|
|
|
@@ -1265,6 +1386,9 @@
|
|
|
1265
1386
|
if (role) {
|
|
1266
1387
|
body.assignedRole = role;
|
|
1267
1388
|
}
|
|
1389
|
+
if (recommendedSkills.length > 0) {
|
|
1390
|
+
body.recommendedSkills = recommendedSkills;
|
|
1391
|
+
}
|
|
1268
1392
|
|
|
1269
1393
|
await apiPost("/tasks", body);
|
|
1270
1394
|
taskForm.reset();
|
|
@@ -1313,6 +1437,18 @@
|
|
|
1313
1437
|
}
|
|
1314
1438
|
});
|
|
1315
1439
|
|
|
1440
|
+
const controllerRunsContainer = $("#controller-runs");
|
|
1441
|
+
if (controllerRunsContainer) {
|
|
1442
|
+
controllerRunsContainer.addEventListener("click", function (event) {
|
|
1443
|
+
const target = event.target instanceof Element ? event.target : null;
|
|
1444
|
+
const button = target ? target.closest("[data-open-task-id]") : null;
|
|
1445
|
+
const taskId = button && button.dataset ? button.dataset.openTaskId : "";
|
|
1446
|
+
if (taskId) {
|
|
1447
|
+
openTaskDetail(taskId);
|
|
1448
|
+
}
|
|
1449
|
+
});
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1316
1452
|
const cmdInput = $("#command-input");
|
|
1317
1453
|
const cmdSend = $("#command-send");
|
|
1318
1454
|
|
package/src/ui/index.html
CHANGED
|
@@ -105,6 +105,12 @@
|
|
|
105
105
|
|
|
106
106
|
<!-- Messages Tab -->
|
|
107
107
|
<div id="tab-messages" class="tab-panel">
|
|
108
|
+
<div class="panel-note">
|
|
109
|
+
Controller activity is persisted here so you can follow requirement intake, orchestration, and follow-up runs from the web UI.
|
|
110
|
+
</div>
|
|
111
|
+
<div id="controller-runs" class="controller-runs">
|
|
112
|
+
<div class="empty-state">No controller activity yet</div>
|
|
113
|
+
</div>
|
|
108
114
|
<div id="messages-feed" class="messages-feed">
|
|
109
115
|
<div class="empty-state">No messages yet</div>
|
|
110
116
|
</div>
|
|
@@ -124,6 +130,10 @@
|
|
|
124
130
|
<label for="task-desc">Description</label>
|
|
125
131
|
<textarea id="task-desc" placeholder="Execution-ready task description..." rows="4" required></textarea>
|
|
126
132
|
</div>
|
|
133
|
+
<div class="form-group">
|
|
134
|
+
<label for="task-recommended-skills">Recommended Skills</label>
|
|
135
|
+
<input type="text" id="task-recommended-skills" placeholder="Comma-separated skill slugs, e.g. find-skills, ui-ux-pro-max">
|
|
136
|
+
</div>
|
|
127
137
|
<div class="form-row">
|
|
128
138
|
<div class="form-group">
|
|
129
139
|
<label for="task-priority">Priority</label>
|
package/src/ui/style.css
CHANGED
|
@@ -275,14 +275,54 @@ body {
|
|
|
275
275
|
.task-priority.critical { background: var(--danger); color: #fff; }
|
|
276
276
|
|
|
277
277
|
.task-body { flex: 1; min-width: 0; }
|
|
278
|
+
.task-title-row {
|
|
279
|
+
display: flex;
|
|
280
|
+
align-items: center;
|
|
281
|
+
justify-content: space-between;
|
|
282
|
+
gap: 10px;
|
|
283
|
+
margin-bottom: 4px;
|
|
284
|
+
}
|
|
278
285
|
.task-title { font-weight: 600; font-size: 14px; margin-bottom: 4px; }
|
|
279
286
|
.task-desc { font-size: 12px; color: var(--text-secondary); margin-bottom: 6px; }
|
|
287
|
+
.task-origin-badge {
|
|
288
|
+
font-size: 10px;
|
|
289
|
+
font-weight: 700;
|
|
290
|
+
text-transform: uppercase;
|
|
291
|
+
color: var(--accent);
|
|
292
|
+
background: rgba(96, 165, 250, 0.12);
|
|
293
|
+
border: 1px solid rgba(96, 165, 250, 0.25);
|
|
294
|
+
border-radius: 999px;
|
|
295
|
+
padding: 3px 8px;
|
|
296
|
+
flex-shrink: 0;
|
|
297
|
+
}
|
|
280
298
|
.task-note {
|
|
281
299
|
font-size: 12px;
|
|
282
300
|
color: var(--warning);
|
|
283
301
|
margin-bottom: 6px;
|
|
284
302
|
line-height: 1.5;
|
|
285
303
|
}
|
|
304
|
+
.skill-pills {
|
|
305
|
+
display: flex;
|
|
306
|
+
flex-wrap: wrap;
|
|
307
|
+
gap: 6px;
|
|
308
|
+
}
|
|
309
|
+
.task-skill-pills {
|
|
310
|
+
margin-bottom: 8px;
|
|
311
|
+
}
|
|
312
|
+
.task-detail-skill-pills {
|
|
313
|
+
margin-top: 8px;
|
|
314
|
+
}
|
|
315
|
+
.skill-pill {
|
|
316
|
+
display: inline-flex;
|
|
317
|
+
align-items: center;
|
|
318
|
+
padding: 3px 8px;
|
|
319
|
+
border-radius: 999px;
|
|
320
|
+
background: rgba(52, 211, 153, 0.12);
|
|
321
|
+
border: 1px solid rgba(52, 211, 153, 0.25);
|
|
322
|
+
color: #bbf7d0;
|
|
323
|
+
font-size: 11px;
|
|
324
|
+
font-weight: 600;
|
|
325
|
+
}
|
|
286
326
|
.task-meta { display: flex; gap: 12px; font-size: 11px; color: var(--text-muted); }
|
|
287
327
|
|
|
288
328
|
.task-status-badge {
|
|
@@ -410,6 +450,114 @@ body {
|
|
|
410
450
|
justify-content: flex-end;
|
|
411
451
|
}
|
|
412
452
|
|
|
453
|
+
.controller-runs {
|
|
454
|
+
display: flex;
|
|
455
|
+
flex-direction: column;
|
|
456
|
+
gap: 12px;
|
|
457
|
+
margin-bottom: 16px;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
.controller-run-card {
|
|
461
|
+
background: var(--bg-secondary);
|
|
462
|
+
border: 1px solid var(--border);
|
|
463
|
+
border-radius: var(--radius);
|
|
464
|
+
padding: 14px 16px;
|
|
465
|
+
display: flex;
|
|
466
|
+
flex-direction: column;
|
|
467
|
+
gap: 12px;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
.controller-run-header {
|
|
471
|
+
display: flex;
|
|
472
|
+
align-items: flex-start;
|
|
473
|
+
justify-content: space-between;
|
|
474
|
+
gap: 16px;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
.controller-run-heading h3 {
|
|
478
|
+
margin: 0;
|
|
479
|
+
font-size: 15px;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
.controller-run-kicker,
|
|
483
|
+
.controller-run-meta,
|
|
484
|
+
.controller-run-section-title,
|
|
485
|
+
.controller-run-event-meta {
|
|
486
|
+
color: var(--text-muted);
|
|
487
|
+
font-size: 12px;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
.controller-run-section {
|
|
491
|
+
display: flex;
|
|
492
|
+
flex-direction: column;
|
|
493
|
+
gap: 6px;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
.controller-run-status {
|
|
497
|
+
font-size: 11px;
|
|
498
|
+
font-weight: 700;
|
|
499
|
+
text-transform: uppercase;
|
|
500
|
+
padding: 4px 9px;
|
|
501
|
+
border-radius: 999px;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
.controller-run-status.pending { background: var(--bg-tertiary); color: var(--text-secondary); }
|
|
505
|
+
.controller-run-status.running { background: #422006; color: var(--warning); }
|
|
506
|
+
.controller-run-status.completed { background: #14532d; color: var(--success); }
|
|
507
|
+
.controller-run-status.failed { background: #450a0a; color: var(--danger); }
|
|
508
|
+
|
|
509
|
+
.controller-run-created-tasks {
|
|
510
|
+
display: flex;
|
|
511
|
+
flex-wrap: wrap;
|
|
512
|
+
gap: 8px;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
.controller-run-task-link {
|
|
516
|
+
padding: 6px 10px;
|
|
517
|
+
border-radius: 999px;
|
|
518
|
+
border: 1px solid rgba(96, 165, 250, 0.25);
|
|
519
|
+
background: rgba(96, 165, 250, 0.12);
|
|
520
|
+
color: var(--accent);
|
|
521
|
+
cursor: pointer;
|
|
522
|
+
font-size: 12px;
|
|
523
|
+
font-weight: 600;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
.controller-run-task-link:hover {
|
|
527
|
+
border-color: var(--accent);
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
.controller-run-events {
|
|
531
|
+
display: flex;
|
|
532
|
+
flex-direction: column;
|
|
533
|
+
gap: 8px;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
.controller-run-event {
|
|
537
|
+
border-left: 3px solid var(--accent);
|
|
538
|
+
background: var(--bg-tertiary);
|
|
539
|
+
border-radius: 10px;
|
|
540
|
+
padding: 10px 12px;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
.controller-run-event-header {
|
|
544
|
+
display: flex;
|
|
545
|
+
align-items: center;
|
|
546
|
+
justify-content: space-between;
|
|
547
|
+
gap: 12px;
|
|
548
|
+
margin-bottom: 6px;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
.controller-run-event-label {
|
|
552
|
+
font-size: 12px;
|
|
553
|
+
font-weight: 700;
|
|
554
|
+
color: var(--text-primary);
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
.controller-run-event-body {
|
|
558
|
+
font-size: 13px;
|
|
559
|
+
}
|
|
560
|
+
|
|
413
561
|
.messages-feed {
|
|
414
562
|
display: flex;
|
|
415
563
|
flex-direction: column;
|
|
@@ -59,6 +59,9 @@ export function createWorkerHttpHandler(
|
|
|
59
59
|
const taskId = typeof body.taskId === "string" ? body.taskId : "";
|
|
60
60
|
const title = typeof body.title === "string" ? body.title : "";
|
|
61
61
|
const description = typeof body.description === "string" ? body.description : "";
|
|
62
|
+
const recommendedSkills = Array.isArray(body.recommendedSkills)
|
|
63
|
+
? body.recommendedSkills.map((entry) => String(entry ?? ""))
|
|
64
|
+
: undefined;
|
|
62
65
|
const repo = body.repo && typeof body.repo === "object"
|
|
63
66
|
? body.repo as TaskAssignmentPayload["repo"]
|
|
64
67
|
: undefined;
|
|
@@ -75,6 +78,7 @@ export function createWorkerHttpHandler(
|
|
|
75
78
|
taskId,
|
|
76
79
|
title,
|
|
77
80
|
description,
|
|
81
|
+
recommendedSkills,
|
|
78
82
|
repo,
|
|
79
83
|
})
|
|
80
84
|
.then((result) => {
|
|
@@ -50,6 +50,7 @@ export function createWorkerPromptInjector(
|
|
|
50
50
|
parts.push(`10. Valid TeamClaw role IDs: ${TEAMCLAW_ROLE_IDS_TEXT}.`);
|
|
51
51
|
parts.push("11. Treat file paths from documents, plans, and teammate messages as hints, not guarantees. Verify the real path exists in the current workspace before reading or editing it; if it does not exist, search for the closest real file and note the drift instead of repeatedly calling missing paths.");
|
|
52
52
|
parts.push("12. The workspace may be backed by a TeamClaw-managed git repository. Treat the current checkout as canonical project state; do not delete `.git` or replace the repo with ad-hoc archives.");
|
|
53
|
+
parts.push("13. If the assigned task includes recommended skills, use those exact skill slugs first. Missing skills should be searched/installed before execution when supported by the runtime.");
|
|
53
54
|
parts.push(`Worker ID: ${identity.workerId}`);
|
|
54
55
|
parts.push(`Controller: ${identity.controllerUrl}`);
|
|
55
56
|
|