| 1489 |
1489
|
|
Flag.withDescription("How many children may run at once. The rest queue"),
|
| 1490 |
1490
|
|
);
|
| 1491 |
1491
|
|
|
|
1492
|
+ |
/**
|
|
1493
|
+ |
* The model delegated children run on.
|
|
1494
|
+ |
*
|
|
1495
|
+ |
* A child is a coding agent, and the model a session's own turns run on is
|
|
1496
|
+ |
* chosen for conversation, so children are pinned to Ox Alpha rather than
|
|
1497
|
+ |
* inheriting the parent's. It is a separate thread with its own budget, so a
|
|
1498
|
+ |
* fan-out cannot spend the authority the conversation is holding, and the
|
|
1499
|
+ |
* server issues it — no provider key reaches this process either way.
|
|
1500
|
+ |
*/
|
|
1501
|
+ |
const CHILD_THREAD_MODEL = "ox-alpha";
|
|
1502
|
+ |
|
|
1503
|
+ |
/** The children's thread, or the server's own words for why there is none. */
|
|
1504
|
+ |
type ChildThread =
|
|
1505
|
+ |
| { readonly kind: "opened"; readonly thread: ThreadReplySource }
|
|
1506
|
+ |
| { readonly kind: "refused"; readonly reason: string };
|
|
1507
|
+ |
|
|
1508
|
+ |
/**
|
|
1509
|
+ |
* Open the thread children spend.
|
|
1510
|
+ |
*
|
|
1511
|
+ |
* A refusal is reported, never absorbed. Lending children the conversation's
|
|
1512
|
+ |
* own grant instead would run them on the conversation's model while the
|
|
1513
|
+ |
* interface said Ox Alpha, so a session that cannot open this thread delegates
|
|
1514
|
+ |
* to nothing and says which refusal stopped it.
|
|
1515
|
+ |
*/
|
|
1516
|
+ |
async function openChildThread(options: {
|
|
1517
|
+ |
readonly origin: string;
|
|
1518
|
+ |
readonly token: string;
|
|
1519
|
+ |
readonly objective: string;
|
|
1520
|
+ |
}): Promise<ChildThread> {
|
|
1521
|
+ |
try {
|
|
1522
|
+ |
return {
|
|
1523
|
+ |
kind: "opened",
|
|
1524
|
+ |
thread: await openThread({
|
|
1525
|
+ |
origin: options.origin,
|
|
1526
|
+ |
token: options.token,
|
|
1527
|
+ |
objective: options.objective,
|
|
1528
|
+ |
model: process.env["OPENAGENTS_DELEGATE_THREAD_MODEL"] ?? CHILD_THREAD_MODEL,
|
|
1529
|
+ |
}),
|
|
1530
|
+ |
};
|
|
1531
|
+ |
} catch (cause) {
|
|
1532
|
+ |
const reason =
|
|
1533
|
+ |
cause instanceof ThreadUnavailable
|
|
1534
|
+ |
? `${cause.message} (${cause.code})`
|
|
1535
|
+ |
: `The thread could not be opened: ${String(cause)}`;
|
|
1536
|
+ |
return { kind: "refused", reason };
|
|
1537
|
+ |
}
|
|
1538
|
+ |
}
|
|
1539
|
+ |
|
| 1492 |
1540
|
|
/** Delegation and whatever has to be torn down with it. */
|
| 1493 |
1541
|
|
interface DelegationSetup {
|
| 1494 |
1542
|
|
readonly delegation: CoderDelegation;
|