@runuai/host 0.9.6 → 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 +392 -126
- package/package.json +1 -1
- package/src/index.ts +15 -1
- package/src/main.ts +18 -1
- package/src/protocol.ts +41 -0
package/package.json
CHANGED
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;
|