triflux 10.3.2 β 10.3.4
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/.claude-plugin/plugin.json +22 -22
- package/LICENSE +21 -21
- package/README.ko.md +16 -0
- package/README.md +8 -0
- package/hooks/hook-registry.json +256 -256
- package/hub/adaptive-inject.mjs +1 -1
- package/hub/assign-callbacks.mjs +120 -120
- package/hub/delegator/index.mjs +14 -14
- package/hub/delegator/tool-definitions.mjs +35 -35
- package/hub/hitl.mjs +143 -143
- package/hub/lib/path-utils.mjs +167 -0
- package/hub/router.mjs +791 -791
- package/hub/session-fingerprint.mjs +1 -1
- package/hub/team/cli/commands/attach.mjs +37 -37
- package/hub/team/cli/commands/debug.mjs +74 -74
- package/hub/team/cli/commands/focus.mjs +53 -53
- package/hub/team/cli/commands/list.mjs +24 -24
- package/hub/team/cli/commands/start/start-in-process.mjs +40 -40
- package/hub/team/cli/commands/start/start-mux.mjs +73 -73
- package/hub/team/cli/commands/start/start-wt.mjs +69 -69
- package/hub/team/cli/commands/tasks.mjs +13 -13
- package/hub/team/cli/render.mjs +30 -30
- package/hub/team/cli/services/attach-fallback.mjs +54 -54
- package/hub/team/cli/services/member-selector.mjs +30 -30
- package/hub/team/cli/services/native-control.mjs +116 -116
- package/hub/team/cli/services/task-model.mjs +30 -30
- package/hub/team/notify.mjs +1 -1
- package/hub/team/orchestrator.mjs +161 -161
- package/hub/team/runtime-strategy.mjs +74 -0
- package/hub/team/session.mjs +611 -611
- package/hub/team/shared.mjs +13 -13
- package/hub/team/worktree-lifecycle.mjs +61 -2
- package/hub/tray.mjs +368 -368
- package/hub/workers/codex-mcp.mjs +507 -507
- package/hub/workers/factory.mjs +21 -21
- package/hud/hud-qos-status.mjs +17 -3
- package/hud/mission-board.mjs +53 -0
- package/hud/providers/claude.mjs +95 -22
- package/hud/renderers.mjs +39 -5
- package/mesh/index.mjs +63 -0
- package/mesh/mesh-budget.mjs +128 -0
- package/mesh/mesh-heartbeat.mjs +100 -0
- package/mesh/mesh-protocol.mjs +96 -0
- package/mesh/mesh-queue.mjs +165 -0
- package/mesh/mesh-registry.mjs +78 -0
- package/mesh/mesh-router.mjs +76 -0
- package/package.json +2 -1
- package/scripts/completions/tfx.bash +47 -47
- package/scripts/completions/tfx.fish +44 -44
- package/scripts/completions/tfx.zsh +83 -83
- package/scripts/demo.mjs +169 -0
- package/scripts/headless-guard.mjs +16 -4
- package/scripts/hub-ensure.mjs +120 -120
- package/scripts/keyword-detector.mjs +272 -272
- package/scripts/keyword-rules-expander.mjs +521 -521
- package/scripts/lib/mcp-server-catalog.mjs +118 -118
- package/scripts/lib/skill-state.mjs +220 -0
- package/scripts/notion-read.mjs +553 -553
- package/scripts/test-tfx-route-no-claude-native.mjs +57 -57
- package/scripts/tfx-batch-stats.mjs +96 -96
- package/skills/.omc/state/agent-replay-8f0e10a9-9693-4410-96f5-a6b07e8ed995.jsonl +0 -1
- package/skills/.omc/state/idle-notif-cooldown.json +0 -3
- package/skills/.omc/state/last-tool-error.json +0 -7
- package/skills/.omc/state/subagent-tracking.json +0 -7
- package/skills/tfx-remote-spawn/references/hosts.json +0 -16
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
export function buildTasks(subtasks, workers) {
|
|
2
|
-
return subtasks.map((subtask, index) => ({
|
|
3
|
-
id: `T${index + 1}`,
|
|
4
|
-
title: subtask,
|
|
5
|
-
owner: workers[index]?.name || null,
|
|
6
|
-
status: "pending",
|
|
7
|
-
depends_on: index === 0 ? [] : [`T${index}`],
|
|
8
|
-
}));
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export function normalizeTaskStatus(action) {
|
|
12
|
-
const value = String(action || "").toLowerCase();
|
|
13
|
-
if (value === "done" || value === "complete" || value === "completed") return "completed";
|
|
14
|
-
if (value === "progress" || value === "in-progress" || value === "in_progress") return "in_progress";
|
|
15
|
-
if (value === "pending") return "pending";
|
|
16
|
-
return null;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function updateTaskStatus(tasks = [], taskId, nextStatus) {
|
|
20
|
-
const normalizedId = String(taskId || "").toUpperCase();
|
|
21
|
-
const target = tasks.find((task) => String(task.id).toUpperCase() === normalizedId);
|
|
22
|
-
if (!target) return { tasks, target: null };
|
|
23
|
-
|
|
24
|
-
return {
|
|
25
|
-
target: { ...target, status: nextStatus },
|
|
26
|
-
tasks: tasks.map((task) => (
|
|
27
|
-
String(task.id).toUpperCase() === normalizedId ? { ...task, status: nextStatus } : task
|
|
28
|
-
)),
|
|
29
|
-
};
|
|
30
|
-
}
|
|
1
|
+
export function buildTasks(subtasks, workers) {
|
|
2
|
+
return subtasks.map((subtask, index) => ({
|
|
3
|
+
id: `T${index + 1}`,
|
|
4
|
+
title: subtask,
|
|
5
|
+
owner: workers[index]?.name || null,
|
|
6
|
+
status: "pending",
|
|
7
|
+
depends_on: index === 0 ? [] : [`T${index}`],
|
|
8
|
+
}));
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function normalizeTaskStatus(action) {
|
|
12
|
+
const value = String(action || "").toLowerCase();
|
|
13
|
+
if (value === "done" || value === "complete" || value === "completed") return "completed";
|
|
14
|
+
if (value === "progress" || value === "in-progress" || value === "in_progress") return "in_progress";
|
|
15
|
+
if (value === "pending") return "pending";
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function updateTaskStatus(tasks = [], taskId, nextStatus) {
|
|
20
|
+
const normalizedId = String(taskId || "").toUpperCase();
|
|
21
|
+
const target = tasks.find((task) => String(task.id).toUpperCase() === normalizedId);
|
|
22
|
+
if (!target) return { tasks, target: null };
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
target: { ...target, status: nextStatus },
|
|
26
|
+
tasks: tasks.map((task) => (
|
|
27
|
+
String(task.id).toUpperCase() === normalizedId ? { ...task, status: nextStatus } : task
|
|
28
|
+
)),
|
|
29
|
+
};
|
|
30
|
+
}
|
package/hub/team/notify.mjs
CHANGED
|
@@ -1,161 +1,161 @@
|
|
|
1
|
-
// hub/team/orchestrator.mjs β μμ
λΆλ°° + ν둬ννΈ κ΅¬μ±
|
|
2
|
-
// μμ‘΄μ±: pane.mjsλ§ μ¬μ©
|
|
3
|
-
import { injectPrompt } from "./pane.mjs";
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* μμ
λΆν΄ (LLM μμ΄ κ΅¬λΆμ κΈ°λ°)
|
|
7
|
-
* @param {string} taskDescription β μ 체 μμ
μ€λͺ
|
|
8
|
-
* @param {number} agentCount β μμ΄μ νΈ μ
|
|
9
|
-
* @returns {string[]} κ° μμ΄μ νΈμ μλΈνμ€ν¬
|
|
10
|
-
*/
|
|
11
|
-
export function decomposeTask(taskDescription, agentCount) {
|
|
12
|
-
if (agentCount <= 0) return [];
|
|
13
|
-
if (agentCount === 1) return [taskDescription];
|
|
14
|
-
|
|
15
|
-
// '+', ',', '\n' κΈ°μ€μΌλ‘ λΆλ¦¬
|
|
16
|
-
const parts = taskDescription
|
|
17
|
-
.split(/[+,\n]+/)
|
|
18
|
-
.map((s) => s.trim())
|
|
19
|
-
.filter(Boolean);
|
|
20
|
-
|
|
21
|
-
if (parts.length === 0) return [taskDescription];
|
|
22
|
-
|
|
23
|
-
// μμ΄μ νΈλ³΄λ€ μλΈνμ€ν¬κ° μ μΌλ©΄ λ§μ§λ§ μμ΄μ νΈμ μ 체 νμ€ν¬ λΆμ¬
|
|
24
|
-
if (parts.length < agentCount) {
|
|
25
|
-
const result = [...parts];
|
|
26
|
-
while (result.length < agentCount) {
|
|
27
|
-
result.push(taskDescription);
|
|
28
|
-
}
|
|
29
|
-
return result;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// μμ΄μ νΈλ³΄λ€ μλΈνμ€ν¬κ° λ§μΌλ©΄ μμμλΆν° Nκ°, λλ¨Έμ§λ λ§μ§λ§μ ν©μΉ¨
|
|
33
|
-
if (parts.length > agentCount) {
|
|
34
|
-
const result = parts.slice(0, agentCount - 1);
|
|
35
|
-
result.push(parts.slice(agentCount - 1).join(" + "));
|
|
36
|
-
return result;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
return parts;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* 리λ(λ³΄ν΅ claude) μ΄κΈ° ν둬ννΈ μμ±
|
|
44
|
-
* @param {string} taskDescription
|
|
45
|
-
* @param {object} config
|
|
46
|
-
* @param {string} config.agentId
|
|
47
|
-
* @param {string} config.hubUrl
|
|
48
|
-
* @param {string} config.teammateMode
|
|
49
|
-
* @param {Array<{agentId:string, cli:string, subtask:string}>} config.workers
|
|
50
|
-
* @returns {string}
|
|
51
|
-
*/
|
|
52
|
-
export function buildLeadPrompt(taskDescription, config) {
|
|
53
|
-
const { agentId, teammateMode = "tmux", workers = [] } = config;
|
|
54
|
-
|
|
55
|
-
const roster = workers
|
|
56
|
-
.map((w, i) => `${i + 1}. ${w.agentId} (${w.cli}) β ${w.subtask}`)
|
|
57
|
-
.join("\n") || "- (μ컀 μμ)";
|
|
58
|
-
|
|
59
|
-
const workerIds = workers.map((w) => w.agentId).join(", ");
|
|
60
|
-
|
|
61
|
-
const bridgePath = "node hub/bridge.mjs";
|
|
62
|
-
|
|
63
|
-
return `리λ μμ΄μ νΈ: ${agentId}
|
|
64
|
-
|
|
65
|
-
λͺ©ν: ${taskDescription}
|
|
66
|
-
λͺ¨λ: ${teammateMode}
|
|
67
|
-
|
|
68
|
-
μ컀:
|
|
69
|
-
${roster}
|
|
70
|
-
|
|
71
|
-
κ·μΉ:
|
|
72
|
-
- κ°λ₯ν μ§§κ³ ν΅μ¬λ§ μ§μ/μμ½(ν ν° μ μ½)
|
|
73
|
-
- μ컀 μ μ΄:
|
|
74
|
-
${bridgePath} result --agent ${agentId} --topic lead.control
|
|
75
|
-
- μ컀 κ²°κ³Ό μμ§:
|
|
76
|
-
${bridgePath} context --agent ${agentId} --max 20
|
|
77
|
-
- μ΅μ’
κ²°κ³Όλ topic="task.result"λ₯Ό λͺ¨μ ν΅ν©
|
|
78
|
-
|
|
79
|
-
μ컀 ID: ${workerIds || "(μμ)"}
|
|
80
|
-
μ§κΈ μ¦μ μ컀λ₯Ό λ°°μ νκ³ λ³λ ¬ μ§νμ κ΄λ¦¬νλΌ.`;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* μ컀 μ΄κΈ° ν둬ννΈ μμ±
|
|
85
|
-
* @param {string} subtask β μ΄ μμ΄μ νΈμ μλΈνμ€ν¬
|
|
86
|
-
* @param {object} config
|
|
87
|
-
* @param {string} config.cli β codex/gemini/claude
|
|
88
|
-
* @param {string} config.agentId β μμ΄μ νΈ μλ³μ
|
|
89
|
-
* @param {string} config.hubUrl β Hub URL
|
|
90
|
-
* @returns {string}
|
|
91
|
-
*/
|
|
92
|
-
export function buildPrompt(subtask, config) {
|
|
93
|
-
const { cli, agentId, hubUrl } = config;
|
|
94
|
-
|
|
95
|
-
const _hubBase = hubUrl.replace("/mcp", "");
|
|
96
|
-
|
|
97
|
-
const bridgePath = "node hub/bridge.mjs";
|
|
98
|
-
|
|
99
|
-
return `μ컀: ${agentId} (${cli})
|
|
100
|
-
μμ
: ${subtask}
|
|
101
|
-
|
|
102
|
-
νμ κ·μΉ:
|
|
103
|
-
1) κ°κ²°νκ² μμ
(λΆνμν μ₯λ¬Έ μ€λͺ
κΈμ§)
|
|
104
|
-
2) μμ μ¦μ λ±λ‘:
|
|
105
|
-
${bridgePath} register --agent ${agentId} --cli ${cli} --topics lead.control,task.result
|
|
106
|
-
3) μ£ΌκΈ°μ μΌλ‘ μμ ν¨ νμΈ:
|
|
107
|
-
${bridgePath} context --agent ${agentId} --max 10
|
|
108
|
-
4) lead.control μμ μ μ¦μ λ°μ (interrupt/stop/pause/resume)
|
|
109
|
-
5) μλ£ μ κ²°κ³Ό λ°ν:
|
|
110
|
-
${bridgePath} result --agent ${agentId} --topic task.result --file <μΆλ ₯νμΌ>
|
|
111
|
-
|
|
112
|
-
μ§κΈ μμ
μ μμνλΌ.`;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* ν μ€μΌμ€νΈλ μ΄μ
μ€ν β κ° paneμ ν둬ννΈ μ£Όμ
|
|
117
|
-
* @param {string} sessionName β tmux μΈμ
μ΄λ¦
|
|
118
|
-
* @param {Array<{target: string, cli: string, subtask: string}>} assignments
|
|
119
|
-
* @param {object} opts
|
|
120
|
-
* @param {string} opts.hubUrl β Hub URL
|
|
121
|
-
* @param {{target:string, cli:string, task:string}|null} opts.lead
|
|
122
|
-
* @param {string} opts.teammateMode
|
|
123
|
-
* @returns {Promise<void>}
|
|
124
|
-
*/
|
|
125
|
-
export async function orchestrate(sessionName, assignments, opts = {}) {
|
|
126
|
-
const {
|
|
127
|
-
hubUrl = "http://127.0.0.1:27888/mcp",
|
|
128
|
-
lead = null,
|
|
129
|
-
teammateMode = "tmux",
|
|
130
|
-
} = opts;
|
|
131
|
-
|
|
132
|
-
const workers = assignments.map(({ target, cli, subtask }) => ({
|
|
133
|
-
target,
|
|
134
|
-
cli,
|
|
135
|
-
subtask,
|
|
136
|
-
agentId: `${cli}-${target.split(".").pop()}`,
|
|
137
|
-
}));
|
|
138
|
-
|
|
139
|
-
if (lead?.target) {
|
|
140
|
-
const leadAgentId = `${lead.cli || "claude"}-${lead.target.split(".").pop()}`;
|
|
141
|
-
const leadPrompt = buildLeadPrompt(lead.task || "ν μμ
μ‘°μ¨", {
|
|
142
|
-
agentId: leadAgentId,
|
|
143
|
-
hubUrl,
|
|
144
|
-
teammateMode,
|
|
145
|
-
workers: workers.map((w) => ({ agentId: w.agentId, cli: w.cli, subtask: w.subtask })),
|
|
146
|
-
});
|
|
147
|
-
injectPrompt(lead.target, leadPrompt, { useFileRef: true });
|
|
148
|
-
await new Promise((r) => setTimeout(r, 100));
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
for (const worker of workers) {
|
|
152
|
-
const prompt = buildPrompt(worker.subtask, {
|
|
153
|
-
cli: worker.cli,
|
|
154
|
-
agentId: worker.agentId,
|
|
155
|
-
hubUrl,
|
|
156
|
-
sessionName,
|
|
157
|
-
});
|
|
158
|
-
injectPrompt(worker.target, prompt, { useFileRef: true });
|
|
159
|
-
await new Promise((r) => setTimeout(r, 100));
|
|
160
|
-
}
|
|
161
|
-
}
|
|
1
|
+
// hub/team/orchestrator.mjs β μμ
λΆλ°° + ν둬ννΈ κ΅¬μ±
|
|
2
|
+
// μμ‘΄μ±: pane.mjsλ§ μ¬μ©
|
|
3
|
+
import { injectPrompt } from "./pane.mjs";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* μμ
λΆν΄ (LLM μμ΄ κ΅¬λΆμ κΈ°λ°)
|
|
7
|
+
* @param {string} taskDescription β μ 체 μμ
μ€λͺ
|
|
8
|
+
* @param {number} agentCount β μμ΄μ νΈ μ
|
|
9
|
+
* @returns {string[]} κ° μμ΄μ νΈμ μλΈνμ€ν¬
|
|
10
|
+
*/
|
|
11
|
+
export function decomposeTask(taskDescription, agentCount) {
|
|
12
|
+
if (agentCount <= 0) return [];
|
|
13
|
+
if (agentCount === 1) return [taskDescription];
|
|
14
|
+
|
|
15
|
+
// '+', ',', '\n' κΈ°μ€μΌλ‘ λΆλ¦¬
|
|
16
|
+
const parts = taskDescription
|
|
17
|
+
.split(/[+,\n]+/)
|
|
18
|
+
.map((s) => s.trim())
|
|
19
|
+
.filter(Boolean);
|
|
20
|
+
|
|
21
|
+
if (parts.length === 0) return [taskDescription];
|
|
22
|
+
|
|
23
|
+
// μμ΄μ νΈλ³΄λ€ μλΈνμ€ν¬κ° μ μΌλ©΄ λ§μ§λ§ μμ΄μ νΈμ μ 체 νμ€ν¬ λΆμ¬
|
|
24
|
+
if (parts.length < agentCount) {
|
|
25
|
+
const result = [...parts];
|
|
26
|
+
while (result.length < agentCount) {
|
|
27
|
+
result.push(taskDescription);
|
|
28
|
+
}
|
|
29
|
+
return result;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// μμ΄μ νΈλ³΄λ€ μλΈνμ€ν¬κ° λ§μΌλ©΄ μμμλΆν° Nκ°, λλ¨Έμ§λ λ§μ§λ§μ ν©μΉ¨
|
|
33
|
+
if (parts.length > agentCount) {
|
|
34
|
+
const result = parts.slice(0, agentCount - 1);
|
|
35
|
+
result.push(parts.slice(agentCount - 1).join(" + "));
|
|
36
|
+
return result;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return parts;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* 리λ(λ³΄ν΅ claude) μ΄κΈ° ν둬ννΈ μμ±
|
|
44
|
+
* @param {string} taskDescription
|
|
45
|
+
* @param {object} config
|
|
46
|
+
* @param {string} config.agentId
|
|
47
|
+
* @param {string} config.hubUrl
|
|
48
|
+
* @param {string} config.teammateMode
|
|
49
|
+
* @param {Array<{agentId:string, cli:string, subtask:string}>} config.workers
|
|
50
|
+
* @returns {string}
|
|
51
|
+
*/
|
|
52
|
+
export function buildLeadPrompt(taskDescription, config) {
|
|
53
|
+
const { agentId, teammateMode = "tmux", workers = [] } = config;
|
|
54
|
+
|
|
55
|
+
const roster = workers
|
|
56
|
+
.map((w, i) => `${i + 1}. ${w.agentId} (${w.cli}) β ${w.subtask}`)
|
|
57
|
+
.join("\n") || "- (μ컀 μμ)";
|
|
58
|
+
|
|
59
|
+
const workerIds = workers.map((w) => w.agentId).join(", ");
|
|
60
|
+
|
|
61
|
+
const bridgePath = "node hub/bridge.mjs";
|
|
62
|
+
|
|
63
|
+
return `리λ μμ΄μ νΈ: ${agentId}
|
|
64
|
+
|
|
65
|
+
λͺ©ν: ${taskDescription}
|
|
66
|
+
λͺ¨λ: ${teammateMode}
|
|
67
|
+
|
|
68
|
+
μ컀:
|
|
69
|
+
${roster}
|
|
70
|
+
|
|
71
|
+
κ·μΉ:
|
|
72
|
+
- κ°λ₯ν μ§§κ³ ν΅μ¬λ§ μ§μ/μμ½(ν ν° μ μ½)
|
|
73
|
+
- μ컀 μ μ΄:
|
|
74
|
+
${bridgePath} result --agent ${agentId} --topic lead.control
|
|
75
|
+
- μ컀 κ²°κ³Ό μμ§:
|
|
76
|
+
${bridgePath} context --agent ${agentId} --max 20
|
|
77
|
+
- μ΅μ’
κ²°κ³Όλ topic="task.result"λ₯Ό λͺ¨μ ν΅ν©
|
|
78
|
+
|
|
79
|
+
μ컀 ID: ${workerIds || "(μμ)"}
|
|
80
|
+
μ§κΈ μ¦μ μ컀λ₯Ό λ°°μ νκ³ λ³λ ¬ μ§νμ κ΄λ¦¬νλΌ.`;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* μ컀 μ΄κΈ° ν둬ννΈ μμ±
|
|
85
|
+
* @param {string} subtask β μ΄ μμ΄μ νΈμ μλΈνμ€ν¬
|
|
86
|
+
* @param {object} config
|
|
87
|
+
* @param {string} config.cli β codex/gemini/claude
|
|
88
|
+
* @param {string} config.agentId β μμ΄μ νΈ μλ³μ
|
|
89
|
+
* @param {string} config.hubUrl β Hub URL
|
|
90
|
+
* @returns {string}
|
|
91
|
+
*/
|
|
92
|
+
export function buildPrompt(subtask, config) {
|
|
93
|
+
const { cli, agentId, hubUrl } = config;
|
|
94
|
+
|
|
95
|
+
const _hubBase = hubUrl.replace("/mcp", "");
|
|
96
|
+
|
|
97
|
+
const bridgePath = "node hub/bridge.mjs";
|
|
98
|
+
|
|
99
|
+
return `μ컀: ${agentId} (${cli})
|
|
100
|
+
μμ
: ${subtask}
|
|
101
|
+
|
|
102
|
+
νμ κ·μΉ:
|
|
103
|
+
1) κ°κ²°νκ² μμ
(λΆνμν μ₯λ¬Έ μ€λͺ
κΈμ§)
|
|
104
|
+
2) μμ μ¦μ λ±λ‘:
|
|
105
|
+
${bridgePath} register --agent ${agentId} --cli ${cli} --topics lead.control,task.result
|
|
106
|
+
3) μ£ΌκΈ°μ μΌλ‘ μμ ν¨ νμΈ:
|
|
107
|
+
${bridgePath} context --agent ${agentId} --max 10
|
|
108
|
+
4) lead.control μμ μ μ¦μ λ°μ (interrupt/stop/pause/resume)
|
|
109
|
+
5) μλ£ μ κ²°κ³Ό λ°ν:
|
|
110
|
+
${bridgePath} result --agent ${agentId} --topic task.result --file <μΆλ ₯νμΌ>
|
|
111
|
+
|
|
112
|
+
μ§κΈ μμ
μ μμνλΌ.`;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* ν μ€μΌμ€νΈλ μ΄μ
μ€ν β κ° paneμ ν둬ννΈ μ£Όμ
|
|
117
|
+
* @param {string} sessionName β tmux μΈμ
μ΄λ¦
|
|
118
|
+
* @param {Array<{target: string, cli: string, subtask: string}>} assignments
|
|
119
|
+
* @param {object} opts
|
|
120
|
+
* @param {string} opts.hubUrl β Hub URL
|
|
121
|
+
* @param {{target:string, cli:string, task:string}|null} opts.lead
|
|
122
|
+
* @param {string} opts.teammateMode
|
|
123
|
+
* @returns {Promise<void>}
|
|
124
|
+
*/
|
|
125
|
+
export async function orchestrate(sessionName, assignments, opts = {}) {
|
|
126
|
+
const {
|
|
127
|
+
hubUrl = "http://127.0.0.1:27888/mcp",
|
|
128
|
+
lead = null,
|
|
129
|
+
teammateMode = "tmux",
|
|
130
|
+
} = opts;
|
|
131
|
+
|
|
132
|
+
const workers = assignments.map(({ target, cli, subtask }) => ({
|
|
133
|
+
target,
|
|
134
|
+
cli,
|
|
135
|
+
subtask,
|
|
136
|
+
agentId: `${cli}-${target.split(".").pop()}`,
|
|
137
|
+
}));
|
|
138
|
+
|
|
139
|
+
if (lead?.target) {
|
|
140
|
+
const leadAgentId = `${lead.cli || "claude"}-${lead.target.split(".").pop()}`;
|
|
141
|
+
const leadPrompt = buildLeadPrompt(lead.task || "ν μμ
μ‘°μ¨", {
|
|
142
|
+
agentId: leadAgentId,
|
|
143
|
+
hubUrl,
|
|
144
|
+
teammateMode,
|
|
145
|
+
workers: workers.map((w) => ({ agentId: w.agentId, cli: w.cli, subtask: w.subtask })),
|
|
146
|
+
});
|
|
147
|
+
injectPrompt(lead.target, leadPrompt, { useFileRef: true });
|
|
148
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
for (const worker of workers) {
|
|
152
|
+
const prompt = buildPrompt(worker.subtask, {
|
|
153
|
+
cli: worker.cli,
|
|
154
|
+
agentId: worker.agentId,
|
|
155
|
+
hubUrl,
|
|
156
|
+
sessionName,
|
|
157
|
+
});
|
|
158
|
+
injectPrompt(worker.target, prompt, { useFileRef: true });
|
|
159
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
160
|
+
}
|
|
161
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createPsmuxSession,
|
|
3
|
+
killPsmuxSession,
|
|
4
|
+
psmuxSessionExists,
|
|
5
|
+
} from "./psmux.mjs";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @typedef {object} RuntimeStatus
|
|
9
|
+
* @property {string} name
|
|
10
|
+
* @property {string} sessionName
|
|
11
|
+
* @property {boolean} alive
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @typedef {object} TeamRuntime
|
|
16
|
+
* @property {(sessionName: string, opts?: object) => unknown} start
|
|
17
|
+
* @property {(sessionName: string) => void} stop
|
|
18
|
+
* @property {(sessionName: string) => boolean} isAlive
|
|
19
|
+
* @property {(sessionName: string) => RuntimeStatus} getStatus
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
const defaultPsmuxAdapter = {
|
|
23
|
+
createSession: createPsmuxSession,
|
|
24
|
+
killSession: killPsmuxSession,
|
|
25
|
+
hasSession: psmuxSessionExists,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @param {{
|
|
30
|
+
* createSession: typeof createPsmuxSession,
|
|
31
|
+
* killSession: typeof killPsmuxSession,
|
|
32
|
+
* hasSession: typeof psmuxSessionExists,
|
|
33
|
+
* }} [adapter]
|
|
34
|
+
* @returns {TeamRuntime & { name: "psmux" }}
|
|
35
|
+
*/
|
|
36
|
+
export function createPsmuxRuntime(adapter = defaultPsmuxAdapter) {
|
|
37
|
+
return {
|
|
38
|
+
name: "psmux",
|
|
39
|
+
start(sessionName, opts = {}) {
|
|
40
|
+
return adapter.createSession(sessionName, opts);
|
|
41
|
+
},
|
|
42
|
+
stop(sessionName) {
|
|
43
|
+
adapter.killSession(sessionName);
|
|
44
|
+
},
|
|
45
|
+
isAlive(sessionName) {
|
|
46
|
+
return adapter.hasSession(sessionName);
|
|
47
|
+
},
|
|
48
|
+
getStatus(sessionName) {
|
|
49
|
+
return {
|
|
50
|
+
name: "psmux",
|
|
51
|
+
sessionName,
|
|
52
|
+
alive: adapter.hasSession(sessionName),
|
|
53
|
+
};
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* @param {string} mode
|
|
60
|
+
* @returns {TeamRuntime & { name: string }}
|
|
61
|
+
*/
|
|
62
|
+
export function createRuntime(mode) {
|
|
63
|
+
const normalizedMode = String(mode || "").trim().toLowerCase();
|
|
64
|
+
|
|
65
|
+
if (normalizedMode === "psmux") {
|
|
66
|
+
return createPsmuxRuntime();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (normalizedMode === "native" || normalizedMode === "wt") {
|
|
70
|
+
throw new Error(`Runtime mode "${normalizedMode}" is not implemented yet.`);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
throw new Error(`Unsupported runtime mode: ${mode}`);
|
|
74
|
+
}
|