@stratusagent/gateway 0.14.7 → 0.14.9
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/dist/agent-source.d.ts +10 -0
- package/dist/agent-source.d.ts.map +1 -0
- package/dist/agent-source.js +7 -0
- package/dist/agent-source.js.map +1 -0
- package/dist/approvals.d.ts +38 -0
- package/dist/approvals.d.ts.map +1 -0
- package/dist/approvals.js +330 -0
- package/dist/approvals.js.map +1 -0
- package/dist/credential-requests.d.ts +24 -0
- package/dist/credential-requests.d.ts.map +1 -0
- package/dist/credential-requests.js +452 -0
- package/dist/credential-requests.js.map +1 -0
- package/dist/dispatch.d.ts +34 -0
- package/dist/dispatch.d.ts.map +1 -0
- package/dist/dispatch.js +461 -0
- package/dist/dispatch.js.map +1 -0
- package/dist/index.d.ts +4 -1290
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +202 -2546
- package/dist/index.js.map +1 -1
- package/dist/lifecycle.d.ts +130 -0
- package/dist/lifecycle.d.ts.map +1 -0
- package/dist/lifecycle.js +147 -0
- package/dist/lifecycle.js.map +1 -0
- package/dist/merge-batch.d.ts +60 -0
- package/dist/merge-batch.d.ts.map +1 -0
- package/dist/merge-batch.js +59 -0
- package/dist/merge-batch.js.map +1 -0
- package/dist/recovery.d.ts +49 -0
- package/dist/recovery.d.ts.map +1 -0
- package/dist/recovery.js +487 -0
- package/dist/recovery.js.map +1 -0
- package/dist/session-keys.d.ts +101 -0
- package/dist/session-keys.d.ts.map +1 -0
- package/dist/session-keys.js +148 -0
- package/dist/session-keys.js.map +1 -0
- package/dist/steering.d.ts +24 -0
- package/dist/steering.d.ts.map +1 -0
- package/dist/steering.js +108 -0
- package/dist/steering.js.map +1 -0
- package/dist/sub-agents.d.ts +20 -0
- package/dist/sub-agents.d.ts.map +1 -0
- package/dist/sub-agents.js +263 -0
- package/dist/sub-agents.js.map +1 -0
- package/dist/turn-dispatch.d.ts +34 -0
- package/dist/turn-dispatch.d.ts.map +1 -0
- package/dist/turn-dispatch.js +179 -0
- package/dist/turn-dispatch.js.map +1 -0
- package/dist/types.d.ts +1103 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +7 -7
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* What the gateway reserves on a session: the metadata keys a caller may not
|
|
3
|
+
* set, the id markers and keys of rolled-over and sub-agent sessions, and the
|
|
4
|
+
* errors a turn or a delegation cut short by a stopped daemon records. Moved
|
|
5
|
+
* out of `index.ts` as is; `index.ts` re-exports the public names, so the
|
|
6
|
+
* package's surface is unchanged.
|
|
7
|
+
*/
|
|
8
|
+
import { CONTEXT_FLOOR_METADATA_KEY, CONTEXT_SUMMARY_METADATA_KEY, PENDING_APPROVAL_METADATA_KEY, SESSION_TAINTED_BY_METADATA_KEY, SESSION_TRUST_METADATA_KEY, } from '@stratusagent/core';
|
|
9
|
+
import { CREDENTIAL_REQUEST_TOOL_NAME, DELEGATE_TOOL_NAME, DELEGATED_BY_METADATA_KEY, DELEGATION_DEPTH_METADATA_KEY, ROOT_SESSION_ID_METADATA_KEY, SCHEDULE_ID_METADATA_KEY, SCHEDULED_TURN_METADATA_KEY, } from '@stratusagent/agents';
|
|
10
|
+
import { FALLBACK_ACTIVE_METADATA_KEY } from '@stratusagent/state';
|
|
11
|
+
/**
|
|
12
|
+
* Recorded on a turn the previous process was still running when it died.
|
|
13
|
+
*
|
|
14
|
+
* Exported because "distinguishable" is the whole point: a surface, a
|
|
15
|
+
* test, or an operator reading `lastError` has to be able to tell a turn
|
|
16
|
+
* nobody finished apart from one a provider or a tool actually failed, and
|
|
17
|
+
* matching on prose that lives in one place beats each caller inventing
|
|
18
|
+
* its own guess at the wording.
|
|
19
|
+
*/
|
|
20
|
+
export const ABANDONED_TURN_ERROR = 'stratusd stopped while this turn was still running; it was not resumed. Send the message again.';
|
|
21
|
+
/**
|
|
22
|
+
* The longest `DispatchInput.idempotencyKey` accepted. A platform message id
|
|
23
|
+
* is far shorter; the bound is so a key cannot be used to grow every row it
|
|
24
|
+
* is stored on.
|
|
25
|
+
*/
|
|
26
|
+
/**
|
|
27
|
+
* Recorded on a delegated sub-session that was parked on a human when the
|
|
28
|
+
* previous process died.
|
|
29
|
+
*
|
|
30
|
+
* Its parent — the turn whose `agent.delegate` call was awaiting the reply
|
|
31
|
+
* — was running, so the restart settles it as abandoned (above). Recovering
|
|
32
|
+
* the child anyway would re-ask a person to approve a call whose result no
|
|
33
|
+
* turn will ever read, and then run it: a command executed, tokens spent,
|
|
34
|
+
* and a reply delivered to nobody. Distinguishable from the parent's error
|
|
35
|
+
* because the remedy differs — there is no message to send again here;
|
|
36
|
+
* the parent's is the one to repeat.
|
|
37
|
+
*/
|
|
38
|
+
/**
|
|
39
|
+
* Session metadata keys the daemon writes for itself, which no external
|
|
40
|
+
* dispatch may supply.
|
|
41
|
+
*
|
|
42
|
+
* Metadata is one bag: the keys a channel attaches to say where a turn is
|
|
43
|
+
* happening sit beside the ones the kernel, the scheduler, the fallback
|
|
44
|
+
* wrapper, and `agent.delegate` write to remember what a session *is* —
|
|
45
|
+
* and the control API forwards a caller's `metadata` into a new session as
|
|
46
|
+
* given. A caller who can write `delegatedBy` can have the restart sweep
|
|
47
|
+
* fail its own parked turn as an orphan; one who can write `fallbackActive`
|
|
48
|
+
* bills its conversation to the fallback model from the first turn. The
|
|
49
|
+
* scheduler already refused to trust its own keys for exactly this reason
|
|
50
|
+
* (see `activeFirings`); the durable ones cannot be re-derived in memory,
|
|
51
|
+
* so they are refused at the door instead.
|
|
52
|
+
*/
|
|
53
|
+
/**
|
|
54
|
+
* A rolled-over session: the fresh row records where its predecessor's
|
|
55
|
+
* transcript went, and the archived row records which live id continues
|
|
56
|
+
* the conversation. See `Gateway.rolloverSession`.
|
|
57
|
+
*/
|
|
58
|
+
export const ROLLED_OVER_FROM_METADATA_KEY = 'rolledOverFrom';
|
|
59
|
+
export const ROLLED_OVER_TO_METADATA_KEY = 'rolledOverTo';
|
|
60
|
+
/** The segment a rollover mints into the archived transcript's id. */
|
|
61
|
+
export const ROLLED_OVER_SESSION_ID_MARKER = ':rolledover:';
|
|
62
|
+
/**
|
|
63
|
+
* How long after a rollover a repeated key is still looked for in the
|
|
64
|
+
* archived transcript. A rollover moves every keyed message out of the
|
|
65
|
+
* live row, so a redelivery arriving after one would otherwise read as a
|
|
66
|
+
* new message and run again. Platforms redeliver within minutes — Slack
|
|
67
|
+
* gives up after about five — and past the window the archive is not
|
|
68
|
+
* loaded at all, since every new message to the fresh row would pay for a
|
|
69
|
+
* read that can no longer find anything.
|
|
70
|
+
*/
|
|
71
|
+
export const ROLLOVER_REPEAT_WINDOW_MS = 60 * 60 * 1000;
|
|
72
|
+
/**
|
|
73
|
+
* The one refusal for a dispatch naming an agent other than its session's.
|
|
74
|
+
* Its own type so a repeat attached to a claimant can tell the claimant was
|
|
75
|
+
* refused for who it named — the item then still has no owner — from the
|
|
76
|
+
* claimant's turn failing. Internal: callers read the message.
|
|
77
|
+
*/
|
|
78
|
+
export class CrossIdentityError extends Error {
|
|
79
|
+
constructor(sessionId, owner, claimed) {
|
|
80
|
+
super(`Session ${sessionId} belongs to agent ${owner}, not ${claimed} — sessions never cross agent identities.`);
|
|
81
|
+
this.name = 'CrossIdentityError';
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
export const crossIdentityError = (sessionId, owner, claimed) => new CrossIdentityError(sessionId, owner, claimed);
|
|
85
|
+
/** Where the session's tool calls ran: `local-command`, or a contributed executor's registered name. */
|
|
86
|
+
export const EXECUTOR_METADATA_KEY = 'executor';
|
|
87
|
+
/**
|
|
88
|
+
* Marks a sub-agent's session (roadmap 24). Reserved: it is what makes the
|
|
89
|
+
* daemon deny the session's gated calls without asking anyone, so a caller
|
|
90
|
+
* that could set it could also clear it.
|
|
91
|
+
*/
|
|
92
|
+
export const SUB_AGENT_METADATA_KEY = 'subAgent';
|
|
93
|
+
export const SUB_AGENT_SESSION_ID_MARKER = ':subagent:';
|
|
94
|
+
/** How many sub-agents one parent turn runs at once unless the host says otherwise. */
|
|
95
|
+
export const DEFAULT_SUB_AGENTS_PER_TURN = 4;
|
|
96
|
+
export const isSubAgentSession = (session) => session.metadata?.[SUB_AGENT_METADATA_KEY] === true && session.id.includes(SUB_AGENT_SESSION_ID_MARKER);
|
|
97
|
+
/**
|
|
98
|
+
* Tools a sub-agent never gets, whatever its parent holds. `agent.spawn`
|
|
99
|
+
* would fan out inside a fan-out; `agent.delegate` runs a roster agent
|
|
100
|
+
* under that agent's own allowlist, which is the way out of a narrowed
|
|
101
|
+
* slice; `memory.*` would let it persist what nobody reviewed; and
|
|
102
|
+
* `credential.request` asks a person, which a sub-agent never does.
|
|
103
|
+
*/
|
|
104
|
+
export const subAgentNeverGets = (name) => name === 'agent.spawn'
|
|
105
|
+
|| name === DELEGATE_TOOL_NAME
|
|
106
|
+
|| name.startsWith('memory.')
|
|
107
|
+
|| name === CREDENTIAL_REQUEST_TOOL_NAME;
|
|
108
|
+
export const RESERVED_SESSION_METADATA_KEYS = [
|
|
109
|
+
PENDING_APPROVAL_METADATA_KEY,
|
|
110
|
+
FALLBACK_ACTIVE_METADATA_KEY,
|
|
111
|
+
DELEGATED_BY_METADATA_KEY,
|
|
112
|
+
ROOT_SESSION_ID_METADATA_KEY,
|
|
113
|
+
DELEGATION_DEPTH_METADATA_KEY,
|
|
114
|
+
SCHEDULED_TURN_METADATA_KEY,
|
|
115
|
+
SCHEDULE_ID_METADATA_KEY,
|
|
116
|
+
// The trust label is the runner's to write and only ever goes down; a
|
|
117
|
+
// caller who could seed it would be seeding it upward.
|
|
118
|
+
SESSION_TRUST_METADATA_KEY,
|
|
119
|
+
SESSION_TAINTED_BY_METADATA_KEY,
|
|
120
|
+
ROLLED_OVER_FROM_METADATA_KEY,
|
|
121
|
+
ROLLED_OVER_TO_METADATA_KEY,
|
|
122
|
+
// Which executor ran the turn's tool calls is the daemon's record of
|
|
123
|
+
// where a command ran, and the one audit question a caller must not be
|
|
124
|
+
// able to answer for it.
|
|
125
|
+
EXECUTOR_METADATA_KEY,
|
|
126
|
+
// The context floor is an index into a specific transcript, so it means
|
|
127
|
+
// nothing next to a different one. Reserved for both halves of that: a
|
|
128
|
+
// caller cannot seed it, and a rollover drops it with the transcript it
|
|
129
|
+
// indexed — otherwise the fresh session inherits an absolute floor over
|
|
130
|
+
// no messages and, once it grows past that index, silently stops sending
|
|
131
|
+
// its own earliest turns, with no overflow and no trim event to say so.
|
|
132
|
+
CONTEXT_FLOOR_METADATA_KEY,
|
|
133
|
+
// The summary that stands in below that floor, for both of the same
|
|
134
|
+
// reasons, and one more: the model reads it as something it wrote itself,
|
|
135
|
+
// so a caller who could seed it could put words in the agent's mouth.
|
|
136
|
+
CONTEXT_SUMMARY_METADATA_KEY,
|
|
137
|
+
SUB_AGENT_METADATA_KEY,
|
|
138
|
+
];
|
|
139
|
+
/**
|
|
140
|
+
* The first reserved key `metadata` carries, or undefined when it carries
|
|
141
|
+
* none. Exported so the control API can refuse the request where the
|
|
142
|
+
* caller can see it, with the same rule the gateway applies at dispatch.
|
|
143
|
+
*/
|
|
144
|
+
export const reservedSessionMetadataKey = (metadata) => metadata === undefined
|
|
145
|
+
? undefined
|
|
146
|
+
: RESERVED_SESSION_METADATA_KEYS.find((key) => Object.prototype.hasOwnProperty.call(metadata, key));
|
|
147
|
+
export const ORPHANED_DELEGATION_ERROR = 'stratusd stopped while the turn that delegated this one was still running; the delegation was not resumed. Send the original message again.';
|
|
148
|
+
//# sourceMappingURL=session-keys.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-keys.js","sourceRoot":"","sources":["../src/session-keys.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type Session, type SteeredMessage } from '@stratusagent/core';
|
|
2
|
+
import { type MergeBatch, type MergeJoiner } from './merge-batch.ts';
|
|
3
|
+
import type { ShardedSessionStore } from './sessions.ts';
|
|
4
|
+
import type { Recovery } from './recovery.ts';
|
|
5
|
+
/** What steering closes over in `createGateway`. */
|
|
6
|
+
export interface SteeringDeps extends Pick<Recovery, 'archivedRepeat'> {
|
|
7
|
+
log: (line: string) => void;
|
|
8
|
+
store: ShardedSessionStore;
|
|
9
|
+
releaseQueued: (sessionId: string) => void;
|
|
10
|
+
waitingDispatches: Map<string, number>;
|
|
11
|
+
isStopping: () => boolean;
|
|
12
|
+
liveWorkItems: Map<string, Map<string, {
|
|
13
|
+
turn: Promise<Session>;
|
|
14
|
+
agentId: string | undefined;
|
|
15
|
+
confirmed: boolean;
|
|
16
|
+
addressed: boolean;
|
|
17
|
+
}>>;
|
|
18
|
+
openBatches: Map<string, MergeBatch>;
|
|
19
|
+
}
|
|
20
|
+
export declare const createSteering: ({ log, store, releaseQueued, waitingDispatches, isStopping, liveWorkItems, openBatches, archivedRepeat, }: SteeringDeps) => {
|
|
21
|
+
takeSteered: (sessionId: string, agentId: string | undefined, into: MergeJoiner[], admit: (parts: readonly MergeJoiner[]) => boolean) => Promise<SteeredMessage | undefined>;
|
|
22
|
+
};
|
|
23
|
+
export type Steering = ReturnType<typeof createSteering>;
|
|
24
|
+
//# sourceMappingURL=steering.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"steering.d.ts","sourceRoot":"","sources":["../src/steering.ts"],"names":[],"mappings":"AASA,OAAO,EAKL,KAAK,OAAO,EACZ,KAAK,cAAc,EAEpB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,KAAK,UAAU,EAAoB,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACvF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACzD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAE9C,oDAAoD;AACpD,MAAM,WAAW,YAAa,SAAQ,IAAI,CAAC,QAAQ,EAAE,gBAAgB,CAAC;IACpE,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5B,KAAK,EAAE,mBAAmB,CAAC;IAC3B,aAAa,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,iBAAiB,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,UAAU,EAAE,MAAM,OAAO,CAAC;IAC1B,aAAa,EAAE,GAAG,CAChB,MAAM,EACN,GAAG,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;QAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;QAAC,SAAS,EAAE,OAAO,CAAC;QAAC,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC,CAC7G,CAAC;IACF,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;CACtC;AAED,eAAO,MAAM,cAAc,GAAI,2GAS5B,YAAY;6BAeA,MAAM,WACR,MAAM,GAAG,SAAS,QACrB,WAAW,EAAE,SAOZ,CAAC,KAAK,EAAE,SAAS,WAAW,EAAE,KAAK,OAAO,KAChD,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC;CA6EvC,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC"}
|
package/dist/steering.js
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* What a running turn takes in at its next tool step (card 948): the merged
|
|
3
|
+
* batch waiting behind it. Moved out of `createGateway` in index.ts as is:
|
|
4
|
+
* the statement is unchanged, inside a factory that takes what it closed
|
|
5
|
+
* over there, and `createGateway` calls it where it was. The one kind of
|
|
6
|
+
* edit: `stopping` is a `let` set at shutdown, so it is read through
|
|
7
|
+
* `isStopping()`.
|
|
8
|
+
*/
|
|
9
|
+
import { abortErrorFor, leastTrusted, SENDER_TRUST_METADATA_KEY, senderTrustOf, workItemState, } from '@stratusagent/core';
|
|
10
|
+
import { MERGED_SEPARATOR } from "./merge-batch.js";
|
|
11
|
+
export const createSteering = ({ log, store, releaseQueued, waitingDispatches, isStopping, liveWorkItems, openBatches, archivedRepeat, }) => {
|
|
12
|
+
/**
|
|
13
|
+
* What a running turn takes in at its next tool step (card 948): the
|
|
14
|
+
* merged-turn batch waiting behind it, whole — leader and absorbed alike
|
|
15
|
+
* — when it is for the same agent. The dispatches it takes join `into`,
|
|
16
|
+
* which the running turn settles with its own outcome (`runSteerable`).
|
|
17
|
+
*
|
|
18
|
+
* A part cancelled while it waited is refused, as `runMerged` refuses it.
|
|
19
|
+
* A part whose key this session (or the archive it rolled over from)
|
|
20
|
+
* already holds is a repeat, and judging a repeat can mean settling a
|
|
21
|
+
* turn — never something to do from inside one — so such a batch is not
|
|
22
|
+
* taken: it waits and runs next, merged, where that is judged. Nor while
|
|
23
|
+
* the gateway is stopping, when the batch is what the drain refuses.
|
|
24
|
+
*/
|
|
25
|
+
const takeSteered = async (sessionId, agentId, into,
|
|
26
|
+
/**
|
|
27
|
+
* Asked last, with nothing awaited after it: whether the running turn
|
|
28
|
+
* still takes these callers, joining them to its cancellation if so. A
|
|
29
|
+
* turn cancelled while the store was read takes nothing, and the batch
|
|
30
|
+
* waits and runs next, as it would have.
|
|
31
|
+
*/
|
|
32
|
+
admit) => {
|
|
33
|
+
const batch = openBatches.get(sessionId);
|
|
34
|
+
if (isStopping() || batch === undefined || !batch.firstInLine || batch.agentId !== agentId) {
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
// Until every key in it is checked with nothing awaited since: a message
|
|
38
|
+
// can join while the store is read, and is checked on the next pass; a
|
|
39
|
+
// batch closed meanwhile is no longer this turn's to take.
|
|
40
|
+
const checked = new Set();
|
|
41
|
+
let session;
|
|
42
|
+
for (;;) {
|
|
43
|
+
if (isStopping() || openBatches.get(sessionId) !== batch) {
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
const unchecked = [batch.leader, ...batch.absorbed].flatMap((part) => (part.input.idempotencyKey !== undefined && !checked.has(part.input.idempotencyKey) ? [part.input.idempotencyKey] : []));
|
|
47
|
+
if (unchecked.length === 0) {
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
session ??= await store.get(sessionId);
|
|
51
|
+
for (const key of unchecked) {
|
|
52
|
+
checked.add(key);
|
|
53
|
+
if (session !== undefined && (workItemState(session, key) !== undefined || await archivedRepeat(session, key) !== undefined)) {
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
const all = [batch.leader, ...batch.absorbed];
|
|
59
|
+
if (!admit(all.filter((part) => part.input.signal?.aborted !== true))) {
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
openBatches.delete(sessionId);
|
|
63
|
+
batch.steered = true;
|
|
64
|
+
// Its place on the chain runs nothing now, so it is not ahead of a batch
|
|
65
|
+
// opening behind it, which can then be steered at a later step.
|
|
66
|
+
releaseQueued(sessionId);
|
|
67
|
+
// Not waiting any more; the leader's place on the chain does not count it again.
|
|
68
|
+
const left = (waitingDispatches.get(sessionId) ?? all.length) - all.length;
|
|
69
|
+
if (left > 0) {
|
|
70
|
+
waitingDispatches.set(sessionId, left);
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
waitingDispatches.delete(sessionId);
|
|
74
|
+
}
|
|
75
|
+
const runnable = [];
|
|
76
|
+
for (const part of all) {
|
|
77
|
+
if (part.input.signal?.aborted) {
|
|
78
|
+
part.reject(abortErrorFor(part.input.signal));
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
runnable.push(part);
|
|
82
|
+
}
|
|
83
|
+
if (runnable.length === 0) {
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
86
|
+
const keys = runnable.flatMap((part) => (part.input.idempotencyKey !== undefined ? [part.input.idempotencyKey] : []));
|
|
87
|
+
const items = liveWorkItems.get(sessionId);
|
|
88
|
+
for (const key of keys) {
|
|
89
|
+
const item = items?.get(key);
|
|
90
|
+
if (item !== undefined) {
|
|
91
|
+
item.confirmed = true;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
into.push(...runnable);
|
|
95
|
+
const labelled = runnable.some((part) => part.input.metadata !== undefined && SENDER_TRUST_METADATA_KEY in part.input.metadata);
|
|
96
|
+
const lowest = leastTrusted(...runnable.map((part) => senderTrustOf(part.input.metadata)));
|
|
97
|
+
const images = runnable.flatMap((part) => part.input.images ?? []);
|
|
98
|
+
log(`${sessionId}: ${runnable.length} message(s) sent during the running turn reached it at its next step`);
|
|
99
|
+
return {
|
|
100
|
+
userMessage: runnable.map((part) => part.input.userMessage).join(MERGED_SEPARATOR),
|
|
101
|
+
...(images.length > 0 ? { images } : {}),
|
|
102
|
+
...(labelled ? { metadata: { [SENDER_TRUST_METADATA_KEY]: lowest } } : {}),
|
|
103
|
+
...(keys.length > 0 ? { keys } : {}),
|
|
104
|
+
};
|
|
105
|
+
};
|
|
106
|
+
return { takeSteered };
|
|
107
|
+
};
|
|
108
|
+
//# sourceMappingURL=steering.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"steering.js","sourceRoot":"","sources":["../src/steering.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type AgentDefinition, type AgentRegistry, type Executor, type Session, type SteeredMessage, type SubAgentHandle, type ToolRegistry, type UnlistedTools } from '@stratusagent/core';
|
|
2
|
+
import type { ShardedSessionStore } from './sessions.ts';
|
|
3
|
+
import type { DispatchInput, GatewayOptions } from './types.ts';
|
|
4
|
+
import type { ApprovalBroker } from './approvals.ts';
|
|
5
|
+
/** What delegation closes over in `createGateway`. */
|
|
6
|
+
export interface SubAgentsDeps extends Pick<ApprovalBroker, 'liveSubAgentIds' | 'idAtFirstCall' | 'hostIdOf' | 'isHostSubAgent'> {
|
|
7
|
+
options: GatewayOptions;
|
|
8
|
+
store: ShardedSessionStore;
|
|
9
|
+
registry: AgentRegistry;
|
|
10
|
+
unlistedTools: () => UnlistedTools;
|
|
11
|
+
tools: ToolRegistry;
|
|
12
|
+
dispatchInternal: (input: DispatchInput, absorbedKeys?: readonly string[], subAgent?: AgentDefinition, steer?: (turnSignal: AbortSignal) => Promise<SteeredMessage | undefined>) => Promise<Session>;
|
|
13
|
+
onSessionChain: <T>(sessionId: string, work: () => Promise<T>, released?: () => boolean) => Promise<T>;
|
|
14
|
+
}
|
|
15
|
+
export declare const createSubAgents: ({ options, store, registry, unlistedTools, tools, dispatchInternal, onSessionChain, liveSubAgentIds, idAtFirstCall, hostIdOf, isHostSubAgent, }: SubAgentsDeps) => {
|
|
16
|
+
bindToolCalls: (inner: Executor) => Executor;
|
|
17
|
+
subAgents: SubAgentHandle;
|
|
18
|
+
};
|
|
19
|
+
export type SubAgents = ReturnType<typeof createSubAgents>;
|
|
20
|
+
//# sourceMappingURL=sub-agents.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sub-agents.d.ts","sourceRoot":"","sources":["../src/sub-agents.ts"],"names":[],"mappings":"AAWA,OAAO,EAEL,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,QAAQ,EAIb,KAAK,OAAO,EAGZ,KAAK,cAAc,EACnB,KAAK,cAAc,EAEnB,KAAK,YAAY,EACjB,KAAK,aAAa,EACnB,MAAM,oBAAoB,CAAC;AAQ5B,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACzD,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAShE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAErD,sDAAsD;AACtD,MAAM,WAAW,aAAc,SAAQ,IAAI,CAAC,cAAc,EAAE,iBAAiB,GAAG,eAAe,GAAG,UAAU,GAAG,gBAAgB,CAAC;IAC9H,OAAO,EAAE,cAAc,CAAC;IACxB,KAAK,EAAE,mBAAmB,CAAC;IAC3B,QAAQ,EAAE,aAAa,CAAC;IACxB,aAAa,EAAE,MAAM,aAAa,CAAC;IACnC,KAAK,EAAE,YAAY,CAAC;IACpB,gBAAgB,EAAE,CAChB,KAAK,EAAE,aAAa,EACpB,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,EAChC,QAAQ,CAAC,EAAE,eAAe,EAC1B,KAAK,CAAC,EAAE,CAAC,UAAU,EAAE,WAAW,KAAK,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,KACrE,OAAO,CAAC,OAAO,CAAC,CAAC;IACtB,cAAc,EAAE,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,EAAE,MAAM,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;CACxG;AAED,eAAO,MAAM,eAAe,GAAI,iJAY7B,aAAa;2BA0CgB,QAAQ,KAAG,QAAQ;;CAqNlD,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC"}
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Delegation: the `agent.delegate` tool, and sub-agents a plugin runs inside
|
|
3
|
+
* a turn (per-turn slots, which tools a slice may carry, the tool calls in
|
|
4
|
+
* flight they are bound to). Moved out of `createGateway` in index.ts as is:
|
|
5
|
+
* the statements are unchanged, inside a factory that takes what they closed
|
|
6
|
+
* over there, and `createGateway` calls it where they were and destructures
|
|
7
|
+
* what it returns. dispatchInternal and onSessionChain are declared later in
|
|
8
|
+
* `createGateway`, so it passes them late-bound.
|
|
9
|
+
*/
|
|
10
|
+
import { randomUUID } from 'node:crypto';
|
|
11
|
+
import { abortErrorFor, matchesToolAllowlist, resolveToolRisk, SENDER_TRUST_METADATA_KEY, sessionTrustOf, sessionWriteTrust, } from '@stratusagent/core';
|
|
12
|
+
import { createDelegateTool, DEFAULT_MAX_DELEGATION_DEPTH, DELEGATED_BY_METADATA_KEY, DELEGATION_DEPTH_METADATA_KEY, ROOT_SESSION_ID_METADATA_KEY, } from '@stratusagent/agents';
|
|
13
|
+
import { DEFAULT_SUB_AGENTS_PER_TURN, isSubAgentSession, SUB_AGENT_METADATA_KEY, SUB_AGENT_SESSION_ID_MARKER, subAgentNeverGets, } from "./session-keys.js";
|
|
14
|
+
import { FALLBACK_ACTIVE_METADATA_KEY } from '@stratusagent/state';
|
|
15
|
+
export const createSubAgents = ({ options, store, registry, unlistedTools, tools, dispatchInternal, onSessionChain, liveSubAgentIds, idAtFirstCall, hostIdOf, isHostSubAgent, }) => {
|
|
16
|
+
tools.register(createDelegateTool({
|
|
17
|
+
registry,
|
|
18
|
+
dispatch: (input) => dispatchInternal({
|
|
19
|
+
sessionId: input.sessionId,
|
|
20
|
+
agentId: input.agent.id,
|
|
21
|
+
userMessage: input.userMessage,
|
|
22
|
+
metadata: input.metadata,
|
|
23
|
+
// A cancelled parent turn cancels its delegated runs too.
|
|
24
|
+
...(input.signal ? { signal: input.signal } : {}),
|
|
25
|
+
}),
|
|
26
|
+
}));
|
|
27
|
+
// ---- sub-agents (roadmap 24) -------------------------------------------
|
|
28
|
+
/**
|
|
29
|
+
* Running sub-agents per parent session, and who waits for a slot. A
|
|
30
|
+
* parent session runs one turn at a time, so this is per turn.
|
|
31
|
+
*/
|
|
32
|
+
const maxSubAgentsPerTurn = options.subAgents?.maxConcurrentPerTurn ?? DEFAULT_SUB_AGENTS_PER_TURN;
|
|
33
|
+
// NaN, 0, a fraction or Infinity would queue every sub-agent forever or
|
|
34
|
+
// defeat the cap, so refuse it here instead of failing later in a turn.
|
|
35
|
+
if (!Number.isInteger(maxSubAgentsPerTurn) || maxSubAgentsPerTurn < 1) {
|
|
36
|
+
throw new Error(`subAgents.maxConcurrentPerTurn must be a whole number of at least 1 (got ${String(maxSubAgentsPerTurn)}).`);
|
|
37
|
+
}
|
|
38
|
+
const subAgentSlots = new Map();
|
|
39
|
+
let subAgentCount = 0;
|
|
40
|
+
/**
|
|
41
|
+
* The session object each in-flight tool call was handed, with that
|
|
42
|
+
* call's turn signal. `subAgents.run` accepts only a parent found here:
|
|
43
|
+
* the session a running tool was given, by identity, never an id, so a
|
|
44
|
+
* plugin cannot start sub-agents of another turn (another agent's
|
|
45
|
+
* included) just because that turn is running. The signal is how the
|
|
46
|
+
* parent's cancel reaches its sub-agents whether or not the plugin
|
|
47
|
+
* passes one.
|
|
48
|
+
*/
|
|
49
|
+
const toolCallsInFlight = new Map();
|
|
50
|
+
const bindToolCalls = (inner) => ({
|
|
51
|
+
async execute(call, tool, session, context) {
|
|
52
|
+
if (!idAtFirstCall.has(session)) {
|
|
53
|
+
idAtFirstCall.set(session, session.id);
|
|
54
|
+
}
|
|
55
|
+
const entry = toolCallsInFlight.get(session) ?? [];
|
|
56
|
+
toolCallsInFlight.set(session, entry);
|
|
57
|
+
const signal = context?.signal;
|
|
58
|
+
entry.push(signal);
|
|
59
|
+
try {
|
|
60
|
+
return await inner.execute(call, tool, session, context);
|
|
61
|
+
}
|
|
62
|
+
finally {
|
|
63
|
+
entry.splice(entry.indexOf(signal), 1);
|
|
64
|
+
if (entry.length === 0) {
|
|
65
|
+
toolCallsInFlight.delete(session);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
const subAgentSlot = (parentId, signal) => new Promise((resolve, reject) => {
|
|
71
|
+
const entry = subAgentSlots.get(parentId) ?? { running: 0, waiting: [] };
|
|
72
|
+
subAgentSlots.set(parentId, entry);
|
|
73
|
+
const release = () => {
|
|
74
|
+
entry.running -= 1;
|
|
75
|
+
const next = entry.waiting.shift();
|
|
76
|
+
if (next) {
|
|
77
|
+
next();
|
|
78
|
+
}
|
|
79
|
+
else if (entry.running === 0) {
|
|
80
|
+
subAgentSlots.delete(parentId);
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
const take = () => {
|
|
84
|
+
entry.running += 1;
|
|
85
|
+
resolve(release);
|
|
86
|
+
};
|
|
87
|
+
if (entry.running < maxSubAgentsPerTurn) {
|
|
88
|
+
take();
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
const onAbort = () => {
|
|
92
|
+
const at = entry.waiting.indexOf(waiter);
|
|
93
|
+
if (at >= 0) {
|
|
94
|
+
entry.waiting.splice(at, 1);
|
|
95
|
+
}
|
|
96
|
+
reject(abortErrorFor(signal));
|
|
97
|
+
};
|
|
98
|
+
const waiter = () => {
|
|
99
|
+
signal?.removeEventListener('abort', onAbort);
|
|
100
|
+
take();
|
|
101
|
+
};
|
|
102
|
+
entry.waiting.push(waiter);
|
|
103
|
+
signal?.addEventListener('abort', onAbort, { once: true });
|
|
104
|
+
});
|
|
105
|
+
/** Why this slice cannot be a sub-agent's, or undefined when it can. */
|
|
106
|
+
const subAgentSliceProblem = (parent, requested) => {
|
|
107
|
+
if (!Array.isArray(requested) || requested.some((name) => typeof name !== 'string' || name.length === 0)) {
|
|
108
|
+
return 'A sub-agent\'s tools are a list of tool names.';
|
|
109
|
+
}
|
|
110
|
+
// Read the way the runner reads it for the parent's own calls: the
|
|
111
|
+
// session's copy, else the registered agent's.
|
|
112
|
+
const allowlist = parent.agent.tools ?? registry.get(parent.agent.id)?.tools;
|
|
113
|
+
for (const name of requested) {
|
|
114
|
+
if (/[*?]/.test(name)) {
|
|
115
|
+
return `A sub-agent's tools are exact names, and ${name} is a pattern.`;
|
|
116
|
+
}
|
|
117
|
+
if (subAgentNeverGets(name)) {
|
|
118
|
+
return `${name} is never given to a sub-agent, even by an agent that holds it.`;
|
|
119
|
+
}
|
|
120
|
+
const tool = tools.get(name);
|
|
121
|
+
if (!tool) {
|
|
122
|
+
return `There is no tool named ${name}.`;
|
|
123
|
+
}
|
|
124
|
+
// The parent's allowlist as its running turn holds it: a slice can
|
|
125
|
+
// only narrow what that turn may already do. With no list, that is
|
|
126
|
+
// the tools registered as safe (every tool on a home an older build
|
|
127
|
+
// set up), and the slice is held to the same rule: naming a gated
|
|
128
|
+
// tool must not hand it to the sub-agent.
|
|
129
|
+
if (allowlist === undefined) {
|
|
130
|
+
if (unlistedTools() !== 'every' && resolveToolRisk(tool) !== 'safe') {
|
|
131
|
+
return `${name} is not a safe tool, and ${parent.agent.id}'s soul has no tools: list, so it gets only safe tools and a sub-agent of it cannot have ${name}.`;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
else if (!matchesToolAllowlist(name, allowlist)) {
|
|
135
|
+
return `${name} is not one of ${parent.agent.id}'s tools, so a sub-agent of it cannot have it.`;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return undefined;
|
|
139
|
+
};
|
|
140
|
+
const subAgents = {
|
|
141
|
+
async run(request) {
|
|
142
|
+
// Copied before the first await, and only the copy is read from here
|
|
143
|
+
// on: the plugin keeps its own objects and can change them while this
|
|
144
|
+
// request is checked or waits for a slot, and a slice changed after
|
|
145
|
+
// it was checked must not be the slice the sub-agent runs with.
|
|
146
|
+
const handed = request?.parent;
|
|
147
|
+
const callSignals = handed ? toolCallsInFlight.get(handed) : undefined;
|
|
148
|
+
if (!callSignals) {
|
|
149
|
+
throw new Error('subAgents.run starts sub-agents only for the session your tool was handed, while that call is running. Pass that session object as "parent".');
|
|
150
|
+
}
|
|
151
|
+
// The parent turn's own stop, and the caller's as an extra one: a
|
|
152
|
+
// plugin that passes no signal still has its sub-agents cancelled
|
|
153
|
+
// with the turn.
|
|
154
|
+
const stops = [...callSignals, request.signal].filter((signal) => signal !== undefined);
|
|
155
|
+
const input = {
|
|
156
|
+
parent: { id: hostIdOf(handed) },
|
|
157
|
+
instruction: request?.instruction,
|
|
158
|
+
tools: Array.isArray(request?.tools) ? [...request.tools] : request?.tools,
|
|
159
|
+
model: request?.model,
|
|
160
|
+
signal: stops.length === 0 ? undefined : stops.length === 1 ? stops[0] : AbortSignal.any(stops),
|
|
161
|
+
};
|
|
162
|
+
const parentId = typeof input.parent.id === 'string' ? input.parent.id : '';
|
|
163
|
+
if (!parentId) {
|
|
164
|
+
throw new Error('subAgents.run needs the session of the turn asking.');
|
|
165
|
+
}
|
|
166
|
+
const instruction = typeof input.instruction === 'string' ? input.instruction.trim() : '';
|
|
167
|
+
if (!instruction) {
|
|
168
|
+
throw new Error('subAgents.run needs an instruction.');
|
|
169
|
+
}
|
|
170
|
+
if (input.model !== undefined) {
|
|
171
|
+
throw new Error('A sub-agent\'s model cannot be chosen yet: it runs on its parent\'s. Leave "model" out.');
|
|
172
|
+
}
|
|
173
|
+
const parent = await store.get(parentId);
|
|
174
|
+
if (!parent) {
|
|
175
|
+
throw new Error(`There is no session ${parentId} to start a sub-agent from.`);
|
|
176
|
+
}
|
|
177
|
+
if (isHostSubAgent(handed) || isSubAgentSession(parent)) {
|
|
178
|
+
throw new Error('A sub-agent cannot start sub-agents.');
|
|
179
|
+
}
|
|
180
|
+
if (parent.status !== 'running') {
|
|
181
|
+
throw new Error(`Session ${parentId} is not running a turn, and sub-agents start only from one.`);
|
|
182
|
+
}
|
|
183
|
+
const depth = typeof parent.metadata?.[DELEGATION_DEPTH_METADATA_KEY] === 'number'
|
|
184
|
+
? parent.metadata[DELEGATION_DEPTH_METADATA_KEY]
|
|
185
|
+
: 0;
|
|
186
|
+
if (depth >= DEFAULT_MAX_DELEGATION_DEPTH) {
|
|
187
|
+
throw new Error(`Delegation depth limit reached (${DEFAULT_MAX_DELEGATION_DEPTH}).`);
|
|
188
|
+
}
|
|
189
|
+
const problem = subAgentSliceProblem(parent, input.tools);
|
|
190
|
+
if (problem) {
|
|
191
|
+
throw new Error(problem);
|
|
192
|
+
}
|
|
193
|
+
if (input.signal?.aborted) {
|
|
194
|
+
throw abortErrorFor(input.signal);
|
|
195
|
+
}
|
|
196
|
+
const release = await subAgentSlot(parent.id, input.signal);
|
|
197
|
+
subAgentCount += 1;
|
|
198
|
+
const sessionId = `${parent.id}${SUB_AGENT_SESSION_ID_MARKER}${depth + 1}:${subAgentCount}-${randomUUID().slice(0, 8)}`;
|
|
199
|
+
// The parent agent, narrowed: its id, provider and credentials, the
|
|
200
|
+
// slice for tools, and nothing else — no instructions (the persona),
|
|
201
|
+
// no skills, no delegates.
|
|
202
|
+
const definition = {
|
|
203
|
+
id: parent.agent.id,
|
|
204
|
+
name: parent.agent.name,
|
|
205
|
+
tools: [...new Set(input.tools)],
|
|
206
|
+
skills: [],
|
|
207
|
+
delegates: [],
|
|
208
|
+
...(parent.agent.credentials !== undefined ? { credentials: [...parent.agent.credentials] } : {}),
|
|
209
|
+
};
|
|
210
|
+
let session;
|
|
211
|
+
liveSubAgentIds.add(sessionId);
|
|
212
|
+
try {
|
|
213
|
+
session = await onSessionChain(sessionId, () => dispatchInternal({
|
|
214
|
+
sessionId,
|
|
215
|
+
agentId: parent.agent.id,
|
|
216
|
+
userMessage: instruction,
|
|
217
|
+
metadata: {
|
|
218
|
+
[SUB_AGENT_METADATA_KEY]: true,
|
|
219
|
+
[DELEGATION_DEPTH_METADATA_KEY]: depth + 1,
|
|
220
|
+
[DELEGATED_BY_METADATA_KEY]: parent.agent.id,
|
|
221
|
+
[ROOT_SESSION_ID_METADATA_KEY]: typeof parent.metadata?.[ROOT_SESSION_ID_METADATA_KEY] === 'string'
|
|
222
|
+
? parent.metadata[ROOT_SESSION_ID_METADATA_KEY]
|
|
223
|
+
: parent.id,
|
|
224
|
+
// The instruction is the parent's text, at the parent's label:
|
|
225
|
+
// a tainted parent must not hand a stranger's words to a
|
|
226
|
+
// session that has seen nothing.
|
|
227
|
+
[SENDER_TRUST_METADATA_KEY]: sessionWriteTrust(parent),
|
|
228
|
+
// On the runtime serving the parent's turn: a parent that has
|
|
229
|
+
// switched to its sticky fallback must not have its helpers
|
|
230
|
+
// retry the primary it left.
|
|
231
|
+
...(parent.metadata?.[FALLBACK_ACTIVE_METADATA_KEY] === true ? { [FALLBACK_ACTIVE_METADATA_KEY]: true } : {}),
|
|
232
|
+
},
|
|
233
|
+
...(input.signal ? { signal: input.signal } : {}),
|
|
234
|
+
}, [], definition));
|
|
235
|
+
}
|
|
236
|
+
catch (error) {
|
|
237
|
+
if (input.signal?.aborted) {
|
|
238
|
+
throw error;
|
|
239
|
+
}
|
|
240
|
+
// The error text may be the provider quoting what it refused,
|
|
241
|
+
// which is this sub-agent's context: labelled as nobody can vouch.
|
|
242
|
+
return { status: 'error', error: error instanceof Error ? error.message : String(error), sessionId, trust: 'unknown' };
|
|
243
|
+
}
|
|
244
|
+
finally {
|
|
245
|
+
liveSubAgentIds.delete(sessionId);
|
|
246
|
+
release();
|
|
247
|
+
}
|
|
248
|
+
const trust = sessionTrustOf(session);
|
|
249
|
+
if (session.status === 'failed') {
|
|
250
|
+
return { status: 'error', error: session.lastError ?? 'The sub-agent failed.', sessionId, trust: 'unknown' };
|
|
251
|
+
}
|
|
252
|
+
const reply = [...session.messages]
|
|
253
|
+
.reverse()
|
|
254
|
+
.find((message) => message.role === 'assistant' && message.content.trim().length > 0);
|
|
255
|
+
const outcome = reply
|
|
256
|
+
? { status: 'result', text: reply.content, sessionId, trust }
|
|
257
|
+
: { status: 'empty', sessionId, trust };
|
|
258
|
+
return outcome;
|
|
259
|
+
},
|
|
260
|
+
};
|
|
261
|
+
return { bindToolCalls, subAgents };
|
|
262
|
+
};
|
|
263
|
+
//# sourceMappingURL=sub-agents.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sub-agents.js","sourceRoot":"","sources":["../src/sub-agents.ts"],"names":[],"mappings":"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"}
|