@runuai/host 0.9.5 → 0.9.7
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/db/migrations/0012_agent_session_attach_key.sql +4 -0
- package/db/migrations/meta/_journal.json +8 -1
- package/db/schema.ts +4 -0
- package/lib/agent-cli.ts +50 -7
- package/lib/agents/claude.ts +274 -19
- package/lib/agents/dispatch.ts +78 -0
- package/lib/agents/mode.ts +7 -0
- package/lib/agents/transport.ts +12 -1
- package/lib/agents/types.ts +11 -0
- package/lib/orchestrator.ts +427 -126
- package/package.json +1 -1
- package/scripts/agent/task-up.sh +16 -2
- package/src/index.ts +15 -1
- package/src/main.ts +18 -1
- package/src/protocol.ts +41 -0
package/package.json
CHANGED
package/scripts/agent/task-up.sh
CHANGED
|
@@ -1244,6 +1244,21 @@ managed_clean_env=(
|
|
|
1244
1244
|
"PATH=/usr/bin:/bin"
|
|
1245
1245
|
)
|
|
1246
1246
|
|
|
1247
|
+
# OpenCode and code-server share ~/.local/share. OpenCode credentials are
|
|
1248
|
+
# copied as root below, so creating only its leaf with `mkdir -p` can leave the
|
|
1249
|
+
# shared parent root-owned (live 2026-08-04). Then node cannot create
|
|
1250
|
+
# code-server's User profile and the editor silently falls back to its light,
|
|
1251
|
+
# full-chrome defaults. Repair both explicitly on every start/resume; GNU
|
|
1252
|
+
# `install -d` also corrects an existing directory with the wrong owner.
|
|
1253
|
+
if ! docker exec -u root "${managed_exec_env[@]}" "$app_container" \
|
|
1254
|
+
/usr/bin/install -d -o node -g node -m 0755 \
|
|
1255
|
+
/home/node/.local/share \
|
|
1256
|
+
/home/node/.local/share/opencode >/dev/null; then
|
|
1257
|
+
emit_err "CONTAINER_INIT_FAILED" \
|
|
1258
|
+
"Uai could not prepare the task's node-owned application data directory. Retry the task; the editor cannot load its settings until this succeeds." \
|
|
1259
|
+
"prepare container application data"
|
|
1260
|
+
fi
|
|
1261
|
+
|
|
1247
1262
|
# Git rejects a repository owned by another uid even when its shared group is
|
|
1248
1263
|
# writable. Trust only the selected worktrees in this task container—never `*`
|
|
1249
1264
|
# and never the host cache. The task-private ~/.gitconfig makes these entries
|
|
@@ -1404,8 +1419,7 @@ docker exec -u root "$app_container" \
|
|
|
1404
1419
|
# binary is baked into the image; here we copy the arch-independent auth+config.
|
|
1405
1420
|
# ADR-076 EXTRA accounts (isolated dirs) are copied later from the host-agent
|
|
1406
1421
|
# (provisionEngineAccounts) since they live in the sealed DB, not on disk here.
|
|
1407
|
-
|
|
1408
|
-
mkdir -p /home/node/.local/share/opencode >/dev/null 2>&1 || true
|
|
1422
|
+
# The shared parent and this leaf were created with node ownership above.
|
|
1409
1423
|
for oc_item in auth.json config.json; do
|
|
1410
1424
|
if [ -e "$UAI_OWNER_HOME/.local/share/opencode/$oc_item" ]; then
|
|
1411
1425
|
docker cp "$UAI_OWNER_HOME/.local/share/opencode/$oc_item" \
|
package/src/index.ts
CHANGED
|
@@ -356,7 +356,21 @@ export const hostCommands: HostCommands = {
|
|
|
356
356
|
logCommand(ctx, "channelEnsure", input.taskId);
|
|
357
357
|
try {
|
|
358
358
|
getOrchestrator().registerChannelSpec(normalizeChannelSpec(input));
|
|
359
|
-
await getOrchestrator().ensureStarted(input.taskId);
|
|
359
|
+
const ready = await getOrchestrator().ensureStarted(input.taskId);
|
|
360
|
+
// Open mode historically treats ensure as best-effort: a stopped task or
|
|
361
|
+
// transient spawn failure still acknowledges the idempotent command.
|
|
362
|
+
// Secretary mode is different because its CLI is the only delegation
|
|
363
|
+
// path, so starting without it would create a human-facing agent that
|
|
364
|
+
// cannot hand work to the crew.
|
|
365
|
+
if (!ready && input.mode === "secretary") {
|
|
366
|
+
return {
|
|
367
|
+
ok: false,
|
|
368
|
+
code: HostErrorCode.TaskNotRunning,
|
|
369
|
+
message:
|
|
370
|
+
"Secretary channel could not start; verify the task CLI and token were materialized",
|
|
371
|
+
retryable: false,
|
|
372
|
+
};
|
|
373
|
+
}
|
|
360
374
|
return ok(undefined);
|
|
361
375
|
} catch (err) {
|
|
362
376
|
return failFromUnknown(err);
|
package/src/main.ts
CHANGED
|
@@ -71,6 +71,7 @@ import {
|
|
|
71
71
|
capabilities as agentKindCapabilities,
|
|
72
72
|
onChange as onRegistryChange,
|
|
73
73
|
} from "../lib/agents/registry";
|
|
74
|
+
import { canAdvertiseTypedSecretaryDispatch } from "../lib/agents/mode";
|
|
74
75
|
// Importing the real factory triggers the built-in adapters' register()
|
|
75
76
|
// calls (claude, codex), so the registry is populated before we advertise.
|
|
76
77
|
import "../lib/agents/factory";
|
|
@@ -78,6 +79,7 @@ import { ensureStandardImage, standardRuntimes } from "../lib/standard-image";
|
|
|
78
79
|
import { hostCommands, hostEvents } from "./index";
|
|
79
80
|
import {
|
|
80
81
|
HostErrorCode,
|
|
82
|
+
SECRETARY_TYPED_DISPATCH_PROTOCOL_FEATURE,
|
|
81
83
|
TRANSCRIPT_TARGETS_PROTOCOL_FEATURE,
|
|
82
84
|
type CloudToHost,
|
|
83
85
|
type McpOp,
|
|
@@ -221,7 +223,15 @@ async function startLocalUi(): Promise<void> {
|
|
|
221
223
|
function buildCapabilities(): HostCapabilities {
|
|
222
224
|
return {
|
|
223
225
|
version: packageVersion(),
|
|
224
|
-
protocolFeatures: [
|
|
226
|
+
protocolFeatures: [
|
|
227
|
+
TRANSCRIPT_TARGETS_PROTOCOL_FEATURE,
|
|
228
|
+
// The echo adapter cannot execute the in-task CLI. Advertising typed
|
|
229
|
+
// dispatch in mock mode would let the composer create a Secretary that
|
|
230
|
+
// has no way to wake crew.
|
|
231
|
+
...(canAdvertiseTypedSecretaryDispatch()
|
|
232
|
+
? [SECRETARY_TYPED_DISPATCH_PROTOCOL_FEATURE]
|
|
233
|
+
: []),
|
|
234
|
+
],
|
|
225
235
|
agentKinds: agentKindCapabilities(),
|
|
226
236
|
runtimes: standardRuntimes(),
|
|
227
237
|
githubUsers: connectedUserIds(),
|
|
@@ -1483,6 +1493,13 @@ function expectChannelEnsureInput(
|
|
|
1483
1493
|
if (typeof input.secretaryAgentId === "string") {
|
|
1484
1494
|
out.secretaryAgentId = input.secretaryAgentId;
|
|
1485
1495
|
}
|
|
1496
|
+
if (
|
|
1497
|
+
input.secretaryDispatchProtocol ===
|
|
1498
|
+
SECRETARY_TYPED_DISPATCH_PROTOCOL_FEATURE
|
|
1499
|
+
) {
|
|
1500
|
+
out.secretaryDispatchProtocol =
|
|
1501
|
+
SECRETARY_TYPED_DISPATCH_PROTOCOL_FEATURE;
|
|
1502
|
+
}
|
|
1486
1503
|
// ADR-053: browser testing flag (optional; tolerant of absence).
|
|
1487
1504
|
if (input.browserTesting === true) out.browserTesting = true;
|
|
1488
1505
|
// ADR-049: humans in the chat (optional; tolerant of absence for older
|
package/src/protocol.ts
CHANGED
|
@@ -33,7 +33,13 @@ export type HostCommandResult<T> =
|
|
|
33
33
|
|
|
34
34
|
/** Capability ids shared by host advertisement and fail-closed cloud gates. */
|
|
35
35
|
export const TRANSCRIPT_TARGETS_PROTOCOL_FEATURE = "transcript-targets-v1";
|
|
36
|
+
export const SECRETARY_TYPED_DISPATCH_PROTOCOL_FEATURE =
|
|
37
|
+
"secretary-typed-dispatch-v1";
|
|
36
38
|
export const COMMUNICATOR_EXECUTION_PROFILE = "communicator";
|
|
39
|
+
export const MAX_AGENT_ID_CHARS = 128;
|
|
40
|
+
export const MAX_SECRETARY_DISPATCH_RECIPIENTS = 16;
|
|
41
|
+
export const MAX_SECRETARY_DISPATCH_INSTRUCTION_CHARS = 8_000;
|
|
42
|
+
export const MAX_SECRETARY_DISPATCH_ID_CHARS = 256;
|
|
37
43
|
|
|
38
44
|
export interface CommandContext {
|
|
39
45
|
commandId: string;
|
|
@@ -250,6 +256,10 @@ export interface ChannelEnsureInput {
|
|
|
250
256
|
mode?: "open" | "secretary";
|
|
251
257
|
/** The one human-facing communicator in secretary mode. */
|
|
252
258
|
secretaryAgentId?: string;
|
|
259
|
+
/** Cloud↔host negotiation for ADR-083's structured dispatch action. A new
|
|
260
|
+
* host refuses Secretary mode when an older cloud omits it, rather than
|
|
261
|
+
* starting a role whose only delegation path that cloud cannot consume. */
|
|
262
|
+
secretaryDispatchProtocol?: typeof SECRETARY_TYPED_DISPATCH_PROTOCOL_FEATURE;
|
|
253
263
|
/** ADR-049: the humans in the chat. Optional for wire back-compat; absent
|
|
254
264
|
* or single-entry behaves exactly like the pre-ADR-049 single-human task. */
|
|
255
265
|
humans?: ChannelHuman[];
|
|
@@ -642,6 +652,37 @@ export type HostEvent =
|
|
|
642
652
|
costUsd?: number;
|
|
643
653
|
};
|
|
644
654
|
}
|
|
655
|
+
/**
|
|
656
|
+
* ADR-083 §5: the secretary raised a STRUCTURED dispatch (`#165vkk`).
|
|
657
|
+
*
|
|
658
|
+
* This exists because a lane cannot be inferred from prose. Rows are created
|
|
659
|
+
* on the first content chunk, so "who was this for?" has to be answered
|
|
660
|
+
* before any text exists — and only a tool call carries that answer
|
|
661
|
+
* structurally. v0 derived it from @mentions in the finished turn, which put
|
|
662
|
+
* every crew-directed sentence into the human's lane and copied the human's
|
|
663
|
+
* answer into the crew's.
|
|
664
|
+
*
|
|
665
|
+
* `recipients` are roster agent ids the engine's dispatch tool was given.
|
|
666
|
+
* The cloud validates them against the roster; it does not trust them.
|
|
667
|
+
*/
|
|
668
|
+
| {
|
|
669
|
+
kind: "agent.dispatch";
|
|
670
|
+
taskId: string;
|
|
671
|
+
agentId: string;
|
|
672
|
+
recipients: string[];
|
|
673
|
+
instruction: string;
|
|
674
|
+
/**
|
|
675
|
+
* The engine's own id for the tool call this came from (Claude's
|
|
676
|
+
* `tool_use` id). Optional, and the cloud does not currently dedupe on
|
|
677
|
+
* it: nothing replays host events today, which is exactly the gap
|
|
678
|
+
* ADR-083 §6 / `#dmmcbh` exists to close. Carried now because it is free
|
|
679
|
+
* while the emission point is being chosen and expensive to add to the
|
|
680
|
+
* wire afterwards — and because a dispatch row that cannot be traced
|
|
681
|
+
* back to the tool call that raised it is hard to debug precisely when
|
|
682
|
+
* something has gone wrong with waking the crew.
|
|
683
|
+
*/
|
|
684
|
+
dispatchId?: string;
|
|
685
|
+
}
|
|
645
686
|
| {
|
|
646
687
|
kind: "agent.tool_call";
|
|
647
688
|
taskId: string;
|