feat(v2/approvals): bundle install_packages + rebuild into one approval

Install approval now auto-rebuilds the image and kills the container,
replacing the prior two-card flow where the agent had to call
request_rebuild separately after install_packages was approved.

Queues a processAfter=+5s synthetic prompt so the respawned container
verifies the new packages and reports back to the user.

Adds two v2-checklist gaps found along the way:
- /remote-control and /remote-control-end are v1 host-level commands
  not ported to v2
- messaging_groups.admin_user_id is hardcoded null at registration

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Koshkoshinsk
2026-04-14 12:53:46 +00:00
parent 192a5a7569
commit 1903fab5e8
3 changed files with 27 additions and 2 deletions

View File

@@ -633,7 +633,7 @@ async function handleSystemAction(
agentGroup.name,
'install_packages',
{ apt, npm, reason },
`Agent "${agentGroup.name}" requests package installation:\n${packageList}${reason ? `\nReason: ${reason}` : ''}`,
`Agent "${agentGroup.name}" requests package install + container rebuild:\n${packageList}${reason ? `\nReason: ${reason}` : ''}`,
);
break;
}

View File

@@ -281,8 +281,31 @@ async function handleApprovalResponse(
updateAgentGroup(session.agent_group_id, { container_config: JSON.stringify(containerConfig) });
const pkgs = [...(payload.apt || []), ...(payload.npm || [])].join(', ');
notify(`Packages approved (${pkgs}). Call request_rebuild to apply them.`);
log.info('Package install approved', { approvalId: approval.approval_id, userId });
try {
await buildAgentGroupImage(session.agent_group_id);
killContainer(session.id, 'rebuild applied');
// Schedule a follow-up prompt a few seconds after kill so the host sweep
// respawns the container on the new image and the agent verifies + reports.
writeSessionMessage(session.agent_group_id, session.id, {
id: `appr-note-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
kind: 'chat',
timestamp: new Date().toISOString(),
platformId: session.agent_group_id,
channelType: 'agent',
threadId: null,
content: JSON.stringify({
text: `Packages installed (${pkgs}) and container rebuilt. Verify the new packages are available (e.g. run them or check versions) and report the result to the user.`,
sender: 'system',
senderId: 'system',
}),
processAfter: new Date(Date.now() + 5000).toISOString().replace('T', ' ').replace(/\.\d+Z$/, ''),
});
log.info('Container rebuild completed (bundled with install)', { approvalId: approval.approval_id });
} catch (e) {
notify(`Packages added to config (${pkgs}) but rebuild failed: ${e instanceof Error ? e.message : String(e)}. Call request_rebuild to retry.`);
log.error('Bundled rebuild failed after install approval', { approvalId: approval.approval_id, err: e });
}
} else if (approval.action === 'request_rebuild') {
try {
await buildAgentGroupImage(session.agent_group_id);