feat: add remote agent execution feature and notification for unblocked tasks
- Updated README to include a new TODO for remote agent execution via SSH. - Implemented a notification system that alerts owners of blocked tasks when their dependencies are resolved, enhancing task management and collaboration.
This commit is contained in:
parent
7c9631c1b9
commit
67f38d8029
2 changed files with 74 additions and 2 deletions
|
|
@ -237,6 +237,7 @@ pnpm dist # macOS + Windows + Linux
|
|||
## TODO
|
||||
|
||||
- [ ] CLI runtime: Run not only on a local PC but in any headless/console environment (web UI), e.g. VPS, remote server, etc.
|
||||
- [ ] Remote agent execution via SSH: launch and manage agent teams on remote machines over SSH (stream-json protocol over SSH channel, SFTP-based file monitoring for tasks/inboxes/config)
|
||||
- [ ] 2 modes: current (agent teams), and a new mode: regular subagents (no communication between them)
|
||||
- [ ] Visual workflow editor ([@xyflow/react](https://github.com/xyflow/xyflow)) for building and orchestrating agent pipelines with drag & drop
|
||||
- [ ] Planning mode to organize agent plans before execution
|
||||
|
|
|
|||
|
|
@ -231,8 +231,79 @@ function startTask(context, taskId, actor) {
|
|||
return task;
|
||||
}
|
||||
|
||||
function notifyUnblockedOwners(context, completedTask) {
|
||||
const blockedIds = Array.isArray(completedTask.blocks) ? completedTask.blocks : [];
|
||||
if (blockedIds.length === 0) return;
|
||||
|
||||
const completedLabel = `#${completedTask.displayId || completedTask.id}`;
|
||||
|
||||
for (const blockedId of blockedIds) {
|
||||
try {
|
||||
const blockedTask = taskStore.readTask(context.paths, blockedId, { includeDeleted: true });
|
||||
if (blockedTask.status === 'deleted' || blockedTask.status === 'completed') continue;
|
||||
if (!normalizeActorName(blockedTask.owner)) continue;
|
||||
|
||||
const allBlockerIds = Array.isArray(blockedTask.blockedBy) ? blockedTask.blockedBy : [];
|
||||
const pendingBlockers = allBlockerIds.filter((id) => {
|
||||
if (id === completedTask.id) return false;
|
||||
try {
|
||||
const t = taskStore.readTask(context.paths, id, { includeDeleted: true });
|
||||
return t.status !== 'completed' && t.status !== 'deleted';
|
||||
} catch { return false; }
|
||||
});
|
||||
|
||||
const allResolved = pendingBlockers.length === 0;
|
||||
const blockedLabel = `#${blockedTask.displayId || blockedTask.id}`;
|
||||
|
||||
let pendingList = '';
|
||||
if (!allResolved) {
|
||||
const refs = pendingBlockers.map((id) => {
|
||||
try {
|
||||
const t = taskStore.readTask(context.paths, id, { includeDeleted: true });
|
||||
return `#${t.displayId || t.id}`;
|
||||
} catch { return `#${id.slice(0, 8)}`; }
|
||||
});
|
||||
pendingList = refs.join(', ');
|
||||
}
|
||||
|
||||
const lines = [
|
||||
`**Dependency resolved** — task ${completedLabel} _${completedTask.subject}_ completed.`,
|
||||
``,
|
||||
allResolved
|
||||
? `All blockers for ${blockedLabel} are resolved — this task is ready to start.`
|
||||
: `${allBlockerIds.length - pendingBlockers.length} of ${allBlockerIds.length} blockers resolved. Still waiting on: ${pendingList}.`,
|
||||
];
|
||||
|
||||
if (allResolved) {
|
||||
lines.push(
|
||||
``,
|
||||
wrapAgentBlock(
|
||||
`All dependencies for this task are now resolved.\n` +
|
||||
`If you are idle, start working on it now:\n` +
|
||||
`1. Check the full context: task_get { teamName: "${context.teamName}", taskId: "${blockedTask.id}" }\n` +
|
||||
`2. Start the task: task_start { teamName: "${context.teamName}", taskId: "${blockedTask.id}" }`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
addTaskComment(context, blockedTask.id, {
|
||||
text: lines.join('\n'),
|
||||
from: 'system',
|
||||
});
|
||||
} catch {
|
||||
// Best-effort per blocked task: skip on failure
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function completeTask(context, taskId, actor) {
|
||||
return setTaskStatus(context, taskId, 'completed', actor);
|
||||
const task = setTaskStatus(context, taskId, 'completed', actor);
|
||||
try {
|
||||
notifyUnblockedOwners(context, task, actor);
|
||||
} catch {
|
||||
// Best-effort: task completion succeeded, notification failure is non-fatal
|
||||
}
|
||||
return task;
|
||||
}
|
||||
|
||||
function softDeleteTask(context, taskId, actor) {
|
||||
|
|
@ -475,7 +546,7 @@ function buildMemberTaskProtocol(teamName) {
|
|||
e) Do NOT set clarification to "user" yourself — only the team lead escalates to the user.
|
||||
13. DEPENDENCY AWARENESS:
|
||||
When your task has blockedBy dependencies, check if they are completed before starting.
|
||||
When you complete a task that blocks others, mention this in your completion message so blocked teammates can proceed.
|
||||
When you complete a task that blocks others, blocked task owners are notified automatically via a task comment.
|
||||
14. TASK QUEUE DISCIPLINE:
|
||||
- Use task_briefing as a compact queue view of your assigned tasks.
|
||||
- task_briefing may include full description/comments only for in_progress tasks; needsFix/pending/review/completed entries may be minimal on purpose.
|
||||
|
|
|
|||
Loading…
Reference in a new issue