@intx/hub-sessions 0.1.2 → 0.3.0
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/LICENSE +176 -0
- package/README.md +82 -1
- package/dist/agent-repo.d.ts +93 -0
- package/dist/agent-repo.js +109 -0
- package/dist/agent-state-kind.d.ts +12 -0
- package/dist/agent-state-kind.js +189 -0
- package/dist/asset-service.d.ts +104 -0
- package/dist/asset-service.js +267 -0
- package/dist/committed-source-tree.d.ts +10 -0
- package/dist/committed-source-tree.js +35 -0
- package/dist/credential-push.d.ts +33 -0
- package/dist/credential-push.js +109 -0
- package/dist/event-collector-registry.d.ts +20 -0
- package/dist/event-collector-registry.js +115 -0
- package/dist/event-collector.d.ts +39 -0
- package/dist/event-collector.js +365 -0
- package/dist/hub-session-lookups.d.ts +135 -0
- package/dist/hub-session-lookups.js +663 -0
- package/dist/hub-session-orchestrator.d.ts +25 -0
- package/dist/hub-session-orchestrator.js +87 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.js +24 -0
- package/dist/package-registry-kind.d.ts +70 -0
- package/dist/package-registry-kind.js +260 -0
- package/dist/repo-store/index.d.ts +4 -0
- package/dist/repo-store/index.js +3 -0
- package/dist/repo-store/store.d.ts +41 -0
- package/dist/repo-store/store.js +1829 -0
- package/dist/repo-store/subscribe-kind.d.ts +56 -0
- package/dist/repo-store/subscribe-kind.js +144 -0
- package/dist/repo-store/types.d.ts +571 -0
- package/dist/repo-store/types.js +42 -0
- package/dist/session-service.d.ts +416 -0
- package/dist/session-service.js +1191 -0
- package/dist/sidecar-allocation/contracts.d.ts +78 -0
- package/dist/sidecar-allocation/contracts.js +21 -0
- package/dist/sidecar-allocation/index.d.ts +4 -0
- package/dist/sidecar-allocation/index.js +3 -0
- package/dist/sidecar-allocation/placement-policy.d.ts +11 -0
- package/dist/sidecar-allocation/placement-policy.js +21 -0
- package/dist/sidecar-allocation/plugin-registry.d.ts +11 -0
- package/dist/sidecar-allocation/plugin-registry.js +37 -0
- package/dist/sidecar-allocation/reconciler.d.ts +42 -0
- package/dist/sidecar-allocation/reconciler.js +431 -0
- package/dist/skill-kind.d.ts +41 -0
- package/dist/skill-kind.js +292 -0
- package/dist/substrate.d.ts +8 -0
- package/dist/substrate.js +21 -0
- package/dist/workflow-allocation-service.d.ts +58 -0
- package/dist/workflow-allocation-service.js +239 -0
- package/dist/workflow-closure-resolution.d.ts +106 -0
- package/dist/workflow-closure-resolution.js +123 -0
- package/dist/workflow-definition-ensure.d.ts +24 -0
- package/dist/workflow-definition-ensure.js +75 -0
- package/dist/workflow-dispatch-service.d.ts +40 -0
- package/dist/workflow-dispatch-service.js +146 -0
- package/dist/workflow-dispatch-settlement.d.ts +29 -0
- package/dist/workflow-dispatch-settlement.js +140 -0
- package/dist/workflow-kind.d.ts +37 -0
- package/dist/workflow-kind.js +310 -0
- package/dist/workflow-probe-gate.d.ts +214 -0
- package/dist/workflow-probe-gate.js +207 -0
- package/dist/workflow-run-event-log.d.ts +21 -0
- package/dist/workflow-run-event-log.js +51 -0
- package/dist/workflow-run-kind.d.ts +440 -0
- package/dist/workflow-run-kind.js +2916 -0
- package/dist/workflow-run-reader.d.ts +47 -0
- package/dist/workflow-run-reader.js +153 -0
- package/dist/workflow-run-restore.d.ts +15 -0
- package/dist/workflow-run-restore.js +26 -0
- package/dist/workflow-source-closure.d.ts +35 -0
- package/dist/workflow-source-closure.js +342 -0
- package/dist/ws/index.d.ts +3 -0
- package/dist/ws/index.js +3 -0
- package/dist/ws/sidecar-events.d.ts +222 -0
- package/dist/ws/sidecar-events.js +72 -0
- package/dist/ws/sidecar-handler.d.ts +305 -0
- package/dist/ws/sidecar-handler.js +2537 -0
- package/dist/ws/sidecar-token-authenticator.d.ts +17 -0
- package/dist/ws/sidecar-token-authenticator.js +81 -0
- package/package.json +35 -12
- package/src/agent-repo.test.ts +0 -310
- package/src/agent-repo.ts +0 -165
- package/src/agent-state-kind.test.ts +0 -247
- package/src/agent-state-kind.ts +0 -204
- package/src/asset-service.test.ts +0 -540
- package/src/asset-service.ts +0 -378
- package/src/available-skills-stanza.test.ts +0 -87
- package/src/available-skills-stanza.ts +0 -47
- package/src/credential-push.ts +0 -65
- package/src/event-collector-registry.test.ts +0 -73
- package/src/event-collector-registry.ts +0 -171
- package/src/event-collector.test.ts +0 -1387
- package/src/event-collector.ts +0 -424
- package/src/hub-session-lookups.ts +0 -206
- package/src/hub-session-orchestrator.test.ts +0 -510
- package/src/hub-session-orchestrator.ts +0 -213
- package/src/index.ts +0 -78
- package/src/repo-store/index.ts +0 -15
- package/src/repo-store/store.test.ts +0 -1169
- package/src/repo-store/store.ts +0 -428
- package/src/repo-store/types.ts +0 -253
- package/src/session-service.test.ts +0 -895
- package/src/session-service.ts +0 -464
- package/src/skill-kind.test.ts +0 -599
- package/src/skill-kind.ts +0 -350
- package/src/ws/index.ts +0 -18
- package/src/ws/sidecar-events.test.ts +0 -96
- package/src/ws/sidecar-events.ts +0 -231
- package/src/ws/sidecar-handler.test.ts +0 -2217
- package/src/ws/sidecar-handler.ts +0 -1574
- package/tsconfig.json +0 -4
- package/tsconfig.tsbuildinfo +0 -1
package/src/session-service.ts
DELETED
|
@@ -1,464 +0,0 @@
|
|
|
1
|
-
import { createHash } from "node:crypto";
|
|
2
|
-
|
|
3
|
-
import { and, eq } from "drizzle-orm";
|
|
4
|
-
|
|
5
|
-
import { getLogger } from "@intx/log";
|
|
6
|
-
import {
|
|
7
|
-
assembleMessage,
|
|
8
|
-
assembleSignedContent,
|
|
9
|
-
createDetachedSignatureFromProvider,
|
|
10
|
-
type MessageHeaders,
|
|
11
|
-
} from "@intx/mime";
|
|
12
|
-
import type { DB } from "@intx/db";
|
|
13
|
-
import { sessionAsset as sessionAssetTable } from "@intx/db/schema";
|
|
14
|
-
import type { CryptoProvider, HarnessConfig } from "@intx/types/runtime";
|
|
15
|
-
import type { AgentRepoStore, DeployContent } from "./agent-repo";
|
|
16
|
-
import type { AgentAssetWithAsset, AssetService } from "./asset-service";
|
|
17
|
-
import {
|
|
18
|
-
buildAvailableSkillsStanza,
|
|
19
|
-
type AvailableSkillEntry,
|
|
20
|
-
} from "./available-skills-stanza";
|
|
21
|
-
import { getSkillIndex } from "./skill-kind";
|
|
22
|
-
import type { SidecarRouter } from "./ws/sidecar-handler";
|
|
23
|
-
import type { Principal, RepoId } from "./repo-store";
|
|
24
|
-
|
|
25
|
-
const logger = getLogger(["interchange", "hub", "session-service"]);
|
|
26
|
-
|
|
27
|
-
export class SessionLaunchError extends Error {
|
|
28
|
-
/** Which phase failed: "write", "provision", "pack", or "start". */
|
|
29
|
-
readonly phase: string;
|
|
30
|
-
/** True if the sidecar has a provisioned agent that could not be cleaned up. */
|
|
31
|
-
readonly leakedAgent: boolean;
|
|
32
|
-
|
|
33
|
-
constructor(phase: string, cause: unknown, leakedAgent: boolean) {
|
|
34
|
-
const msg =
|
|
35
|
-
cause instanceof Error ? cause.message : "Session launch failed";
|
|
36
|
-
super(msg, { cause });
|
|
37
|
-
this.name = "SessionLaunchError";
|
|
38
|
-
this.phase = phase;
|
|
39
|
-
this.leakedAgent = leakedAgent;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export type SessionService = {
|
|
44
|
-
/**
|
|
45
|
-
* Orchestrate the full deploy lifecycle:
|
|
46
|
-
* 1. Write deploy tree to hub repo and produce packfile
|
|
47
|
-
* 2. Provision agent on sidecar (sendAgentDeploy)
|
|
48
|
-
* 3. Deliver deploy packfile (sendPack)
|
|
49
|
-
* 4. Fan-out attached asset packs: for each row returned by
|
|
50
|
-
* `assetService.listAgentAssets(agentId)`, resolve the
|
|
51
|
-
* mountPath, resolve the ref to a source commit SHA, build a
|
|
52
|
-
* pack, insert a `session_asset` row, and send the pack to the
|
|
53
|
-
* sidecar with `mountPath` set on the `repo.pack.done` frame.
|
|
54
|
-
* The manifest insert MUST precede the pack send.
|
|
55
|
-
* 5. Start session (sendSessionStart)
|
|
56
|
-
*
|
|
57
|
-
* On partial failure after provision, attempts cleanup via
|
|
58
|
-
* sendAgentUndeploy before re-throwing.
|
|
59
|
-
*/
|
|
60
|
-
launchSession(params: {
|
|
61
|
-
agentAddress: string;
|
|
62
|
-
agentId: string;
|
|
63
|
-
instanceId: string;
|
|
64
|
-
config: HarnessConfig;
|
|
65
|
-
deployContent: DeployContent;
|
|
66
|
-
}): Promise<void>;
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Compose a signed RFC 2822 message from the user and deliver it to the
|
|
70
|
-
* agent via the mail transport. Throws if the agent is unreachable.
|
|
71
|
-
* Returns the raw MIME bytes of the assembled message.
|
|
72
|
-
*/
|
|
73
|
-
sendUserMessage(params: UserMessageParams): Promise<Uint8Array>;
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Undeploy an agent and wait for the sidecar to acknowledge.
|
|
77
|
-
*/
|
|
78
|
-
endSession(agentAddress: string, reason: string): Promise<void>;
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
export type UserMessageParams = {
|
|
82
|
-
agentAddress: string;
|
|
83
|
-
from: string;
|
|
84
|
-
messageId: string;
|
|
85
|
-
date: Date;
|
|
86
|
-
content: string;
|
|
87
|
-
inReplyTo?: string;
|
|
88
|
-
references?: string[];
|
|
89
|
-
sessionId: string;
|
|
90
|
-
tenantId: string;
|
|
91
|
-
cryptoProvider: CryptoProvider;
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
export type SessionServiceDeps = {
|
|
95
|
-
sidecarRouter: SidecarRouter;
|
|
96
|
-
agentRepoStore: AgentRepoStore;
|
|
97
|
-
/**
|
|
98
|
-
* Optional asset attachment integration. When set, `launchSession`
|
|
99
|
-
* fans out per-attachment packs after the deploy pack lands and
|
|
100
|
-
* inserts a `session_asset` row per attachment. When unset, only
|
|
101
|
-
* the deploy pack is sent — the legacy single-pack path is
|
|
102
|
-
* preserved bit-for-bit.
|
|
103
|
-
*/
|
|
104
|
-
assetService?: AssetService;
|
|
105
|
-
/** DB handle used for `session_asset` manifest inserts. Required
|
|
106
|
-
* iff `assetService` is set. */
|
|
107
|
-
db?: DB["db"];
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
// Hub-side principal for reading skill repos. Skills are signed by the
|
|
111
|
-
// hub itself, and listAgentAssets is being called on the hub to assemble
|
|
112
|
-
// packs for delivery to a sidecar — so the hub principal is correct.
|
|
113
|
-
const HUB_PRINCIPAL: Principal = { kind: "hub" };
|
|
114
|
-
|
|
115
|
-
type ResolvedAttachment = {
|
|
116
|
-
agentAssetId: string;
|
|
117
|
-
/** Asset `name` column. Used to build the qualified `<asset.name>/<skill-name>`
|
|
118
|
-
* prefix in the `<available_skills>` stanza. */
|
|
119
|
-
assetName: string;
|
|
120
|
-
/** Asset `kind` column, used to gate skill-index lookups. */
|
|
121
|
-
assetKind: AgentAssetWithAsset["asset"]["kind"];
|
|
122
|
-
mountPath: string;
|
|
123
|
-
sourceCommitSha: string;
|
|
124
|
-
repoId: RepoId;
|
|
125
|
-
pack: Uint8Array;
|
|
126
|
-
ref: string;
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
function createPackSha(pack: Uint8Array): string {
|
|
130
|
-
return createHash("sha256").update(pack).digest("hex");
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* Compute the materialization path for an attachment from the asset's
|
|
135
|
-
* kind and name. v1 does not let users override the path — the path is
|
|
136
|
-
* a function of the asset, full stop. Today only `skill` has a defined
|
|
137
|
-
* mapping (`skills/<asset.name>/`); other kinds reach this code path
|
|
138
|
-
* via the `never` branch and throw, per the defensive-coding rule that
|
|
139
|
-
* we never silently invent a default for an unhandled kind.
|
|
140
|
-
*
|
|
141
|
-
* Asset names are validated lowercase-kebab at `createAsset`, which is
|
|
142
|
-
* the only entry path into this function, so the resulting path is
|
|
143
|
-
* safe under `applyAssetPack`'s per-segment validator.
|
|
144
|
-
*/
|
|
145
|
-
function resolveMountPath(row: AgentAssetWithAsset): string {
|
|
146
|
-
switch (row.asset.kind) {
|
|
147
|
-
case "skill":
|
|
148
|
-
return `skills/${row.asset.name}/`;
|
|
149
|
-
case "agent-state":
|
|
150
|
-
throw new Error(
|
|
151
|
-
`mount_path_required: agent_asset row ${row.id} references agent-state asset ${row.asset.id}; agent-state attachments are not supported`,
|
|
152
|
-
);
|
|
153
|
-
default: {
|
|
154
|
-
const exhaustive: never = row.asset.kind;
|
|
155
|
-
throw new Error(
|
|
156
|
-
`mount_path_required: no default mountPath for asset kind ${String(exhaustive)} on row ${row.id}`,
|
|
157
|
-
);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
export function createSessionService(deps: SessionServiceDeps): SessionService {
|
|
163
|
-
const { sidecarRouter, agentRepoStore, assetService, db } = deps;
|
|
164
|
-
|
|
165
|
-
if (assetService !== undefined && db === undefined) {
|
|
166
|
-
throw new Error(
|
|
167
|
-
"createSessionService: db is required when assetService is set",
|
|
168
|
-
);
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
async function launchSession(params: {
|
|
172
|
-
agentAddress: string;
|
|
173
|
-
agentId: string;
|
|
174
|
-
instanceId: string;
|
|
175
|
-
config: HarnessConfig;
|
|
176
|
-
deployContent: DeployContent;
|
|
177
|
-
}): Promise<void> {
|
|
178
|
-
const { agentAddress, agentId, instanceId, config, deployContent } = params;
|
|
179
|
-
|
|
180
|
-
// Phase 0: Resolve attached assets first so the skill index is in
|
|
181
|
-
// hand before the deploy tree is written. The `<available_skills>`
|
|
182
|
-
// stanza describing every attached skill must land in
|
|
183
|
-
// `deploy/prompt.md`, so it has to be composed before
|
|
184
|
-
// `writeDeployTree` produces the on-disk tree.
|
|
185
|
-
let attachments: ResolvedAttachment[] = [];
|
|
186
|
-
let availableSkills: AvailableSkillEntry[] = [];
|
|
187
|
-
if (assetService !== undefined) {
|
|
188
|
-
try {
|
|
189
|
-
attachments = await resolveAttachments(assetService, agentId);
|
|
190
|
-
availableSkills = collectAvailableSkills(attachments);
|
|
191
|
-
} catch (err) {
|
|
192
|
-
throw new SessionLaunchError("write", err, false);
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
const stanza = buildAvailableSkillsStanza(availableSkills);
|
|
197
|
-
const effectiveDeployContent: DeployContent =
|
|
198
|
-
stanza.length === 0
|
|
199
|
-
? deployContent
|
|
200
|
-
: {
|
|
201
|
-
...deployContent,
|
|
202
|
-
systemPrompt: `${deployContent.systemPrompt}\n\n${stanza}\n`,
|
|
203
|
-
};
|
|
204
|
-
|
|
205
|
-
// Phase 0b: Write deploy tree and produce packfile (hub-local, no
|
|
206
|
-
// sidecar state to clean up if this fails).
|
|
207
|
-
let pack: Uint8Array;
|
|
208
|
-
let commitSha: string;
|
|
209
|
-
let ref: string;
|
|
210
|
-
try {
|
|
211
|
-
await agentRepoStore.writeDeployTree(agentId, effectiveDeployContent);
|
|
212
|
-
({ pack, commitSha, ref } =
|
|
213
|
-
await agentRepoStore.createDeployPack(agentId));
|
|
214
|
-
} catch (err) {
|
|
215
|
-
throw new SessionLaunchError("write", err, false);
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
// Phase 1: Provision on sidecar.
|
|
219
|
-
try {
|
|
220
|
-
await sidecarRouter.sendAgentDeploy(agentAddress, config);
|
|
221
|
-
} catch (err) {
|
|
222
|
-
throw new SessionLaunchError("provision", err, false);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
// Phases 2-3: Pack delivery and session start. If either fails,
|
|
226
|
-
// attempt cleanup so the sidecar doesn't retain a zombie agent.
|
|
227
|
-
try {
|
|
228
|
-
await sidecarRouter.sendPack(agentAddress, pack, ref, commitSha);
|
|
229
|
-
} catch (err) {
|
|
230
|
-
await attemptCleanup(agentAddress, "pack", err);
|
|
231
|
-
throw new SessionLaunchError("pack", err, false);
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
// Phase 2b: Asset-pack fan-out. For each attached asset, build a
|
|
235
|
-
// pack, insert the manifest row, then send the pack. The manifest
|
|
236
|
-
// insert MUST happen before the pack send: if the sidecar acks
|
|
237
|
-
// but the row is missing, the session has materialization without
|
|
238
|
-
// a recorded manifest. If the row insert fails, the pack send
|
|
239
|
-
// must not happen.
|
|
240
|
-
if (assetService !== undefined && attachments.length > 0) {
|
|
241
|
-
for (const att of attachments) {
|
|
242
|
-
try {
|
|
243
|
-
await sendAttachmentPack(instanceId, agentAddress, att);
|
|
244
|
-
} catch (err) {
|
|
245
|
-
await attemptCleanup(agentAddress, "pack", err);
|
|
246
|
-
throw new SessionLaunchError("pack", err, false);
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
try {
|
|
252
|
-
await sidecarRouter.sendSessionStart(agentAddress);
|
|
253
|
-
} catch (err) {
|
|
254
|
-
await attemptCleanup(agentAddress, "start", err);
|
|
255
|
-
throw new SessionLaunchError("start", err, false);
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
async function sendAttachmentPack(
|
|
260
|
-
instanceId: string,
|
|
261
|
-
agentAddress: string,
|
|
262
|
-
attachment: ResolvedAttachment,
|
|
263
|
-
): Promise<void> {
|
|
264
|
-
if (db === undefined) {
|
|
265
|
-
// Guarded at construction; reassert defensively so the
|
|
266
|
-
// narrowing is visible to readers and a future refactor cannot
|
|
267
|
-
// accidentally invoke this without a db.
|
|
268
|
-
throw new Error("sendAttachmentPack invoked without a db handle");
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
const { agentAssetId, mountPath, sourceCommitSha, repoId, pack, ref } =
|
|
272
|
-
attachment;
|
|
273
|
-
|
|
274
|
-
const assetPackSha = createPackSha(pack);
|
|
275
|
-
|
|
276
|
-
// Insert manifest row before the pack send so we never end up in
|
|
277
|
-
// the materialized-without-manifest state.
|
|
278
|
-
await db.insert(sessionAssetTable).values({
|
|
279
|
-
instanceId,
|
|
280
|
-
agentAssetId,
|
|
281
|
-
mountPath,
|
|
282
|
-
assetPackSha,
|
|
283
|
-
sourceCommitSha,
|
|
284
|
-
materializedAt: new Date(),
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
try {
|
|
288
|
-
await sidecarRouter.sendPack(agentAddress, pack, ref, sourceCommitSha, {
|
|
289
|
-
mountPath,
|
|
290
|
-
repoId,
|
|
291
|
-
});
|
|
292
|
-
} catch (err) {
|
|
293
|
-
// Roll back the manifest row when the send fails so the manifest
|
|
294
|
-
// and the materialized state on the sidecar can never disagree.
|
|
295
|
-
// The forensic value of a manifest-without-materialization row is
|
|
296
|
-
// negligible because no agent will read against it. Wrap the
|
|
297
|
-
// rollback in its own try/catch so a rollback failure (DB gone,
|
|
298
|
-
// connection killed mid-launch) is logged rather than masking the
|
|
299
|
-
// primary sendPack error — the caller needs to see the original
|
|
300
|
-
// failure, not the secondary one.
|
|
301
|
-
try {
|
|
302
|
-
await db
|
|
303
|
-
.delete(sessionAssetTable)
|
|
304
|
-
.where(
|
|
305
|
-
and(
|
|
306
|
-
eq(sessionAssetTable.instanceId, instanceId),
|
|
307
|
-
eq(sessionAssetTable.agentAssetId, agentAssetId),
|
|
308
|
-
),
|
|
309
|
-
);
|
|
310
|
-
} catch (rollbackErr) {
|
|
311
|
-
const msg =
|
|
312
|
-
rollbackErr instanceof Error
|
|
313
|
-
? rollbackErr.message
|
|
314
|
-
: String(rollbackErr);
|
|
315
|
-
logger.warn`session_asset rollback failed for instance=${instanceId} agentAsset=${agentAssetId}: ${msg}`;
|
|
316
|
-
}
|
|
317
|
-
throw err;
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
async function resolveAttachments(
|
|
322
|
-
service: AssetService,
|
|
323
|
-
agentId: string,
|
|
324
|
-
): Promise<ResolvedAttachment[]> {
|
|
325
|
-
const rows = await service.listAgentAssets(agentId);
|
|
326
|
-
const resolved: ResolvedAttachment[] = [];
|
|
327
|
-
for (const row of rows) {
|
|
328
|
-
resolved.push(await resolveAttachment(row));
|
|
329
|
-
}
|
|
330
|
-
return resolved;
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
async function resolveAttachment(
|
|
334
|
-
row: AgentAssetWithAsset,
|
|
335
|
-
): Promise<ResolvedAttachment> {
|
|
336
|
-
const mountPath = resolveMountPath(row);
|
|
337
|
-
const repoId: RepoId = { kind: row.asset.kind, id: row.asset.id };
|
|
338
|
-
|
|
339
|
-
const sourceCommitSha = await agentRepoStore.repoStore.resolveRef(
|
|
340
|
-
HUB_PRINCIPAL,
|
|
341
|
-
repoId,
|
|
342
|
-
row.ref,
|
|
343
|
-
);
|
|
344
|
-
if (sourceCommitSha === null) {
|
|
345
|
-
throw new Error(
|
|
346
|
-
`attachment_ref_unresolved: ${row.asset.kind}/${row.asset.id} has no commit on ${row.ref}`,
|
|
347
|
-
);
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
const { pack, ref: returnedRef } =
|
|
351
|
-
await agentRepoStore.repoStore.createPack(HUB_PRINCIPAL, repoId, row.ref);
|
|
352
|
-
|
|
353
|
-
return {
|
|
354
|
-
agentAssetId: row.id,
|
|
355
|
-
assetName: row.asset.name,
|
|
356
|
-
assetKind: row.asset.kind,
|
|
357
|
-
mountPath,
|
|
358
|
-
sourceCommitSha,
|
|
359
|
-
repoId,
|
|
360
|
-
pack,
|
|
361
|
-
ref: returnedRef,
|
|
362
|
-
};
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
function collectAvailableSkills(
|
|
366
|
-
resolved: ResolvedAttachment[],
|
|
367
|
-
): AvailableSkillEntry[] {
|
|
368
|
-
const entries: AvailableSkillEntry[] = [];
|
|
369
|
-
for (const att of resolved) {
|
|
370
|
-
if (att.assetKind !== "skill") continue;
|
|
371
|
-
const index = getSkillIndex(att.repoId.id, att.ref);
|
|
372
|
-
for (const entry of index) {
|
|
373
|
-
entries.push({
|
|
374
|
-
qualifiedName: `${att.assetName}/${entry.name}`,
|
|
375
|
-
description: entry.description,
|
|
376
|
-
workspacePath: `workspace/${att.mountPath}${entry.workspaceSubpath}`,
|
|
377
|
-
});
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
return entries;
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
async function attemptCleanup(
|
|
384
|
-
agentAddress: string,
|
|
385
|
-
failedPhase: string,
|
|
386
|
-
originalErr: unknown,
|
|
387
|
-
): Promise<void> {
|
|
388
|
-
try {
|
|
389
|
-
await sidecarRouter.sendAgentUndeploy(agentAddress, failedPhase);
|
|
390
|
-
} catch (cleanupErr) {
|
|
391
|
-
logger.error`Failed to clean up agent ${agentAddress} after ${failedPhase} failure: ${String(cleanupErr)}`;
|
|
392
|
-
// Preserve the original error as cause so the root cause is not
|
|
393
|
-
// lost when the cleanup also fails.
|
|
394
|
-
throw new SessionLaunchError(failedPhase, originalErr, true);
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
async function sendUserMessage(
|
|
399
|
-
params: UserMessageParams,
|
|
400
|
-
): Promise<Uint8Array> {
|
|
401
|
-
const {
|
|
402
|
-
agentAddress,
|
|
403
|
-
from,
|
|
404
|
-
messageId,
|
|
405
|
-
date,
|
|
406
|
-
content,
|
|
407
|
-
inReplyTo,
|
|
408
|
-
references,
|
|
409
|
-
sessionId,
|
|
410
|
-
tenantId,
|
|
411
|
-
cryptoProvider,
|
|
412
|
-
} = params;
|
|
413
|
-
|
|
414
|
-
const headers: MessageHeaders = {
|
|
415
|
-
from,
|
|
416
|
-
to: [agentAddress],
|
|
417
|
-
cc: undefined,
|
|
418
|
-
date,
|
|
419
|
-
messageId,
|
|
420
|
-
subject: undefined,
|
|
421
|
-
inReplyTo,
|
|
422
|
-
references,
|
|
423
|
-
mimeVersion: "1.0",
|
|
424
|
-
interchangeType: "conversation.message",
|
|
425
|
-
interchangeCorrelationId: undefined,
|
|
426
|
-
interchangeTenantId: tenantId,
|
|
427
|
-
interchangeAgentId: undefined,
|
|
428
|
-
interchangeSessionId: sessionId,
|
|
429
|
-
interchangeOfferingId: undefined,
|
|
430
|
-
interchangeSchemaVersion: undefined,
|
|
431
|
-
traceparent: undefined,
|
|
432
|
-
tracestate: undefined,
|
|
433
|
-
};
|
|
434
|
-
|
|
435
|
-
const signedContent = assembleSignedContent({
|
|
436
|
-
kind: "conversation",
|
|
437
|
-
text: content,
|
|
438
|
-
});
|
|
439
|
-
const signature = await createDetachedSignatureFromProvider(
|
|
440
|
-
signedContent,
|
|
441
|
-
cryptoProvider,
|
|
442
|
-
);
|
|
443
|
-
const rawMessage = assembleMessage(headers, signedContent, signature);
|
|
444
|
-
const base64 = Buffer.from(rawMessage).toString("base64");
|
|
445
|
-
|
|
446
|
-
const delivered = sidecarRouter.routeMail(agentAddress, base64);
|
|
447
|
-
if (!delivered) {
|
|
448
|
-
throw new Error(
|
|
449
|
-
`Failed to deliver message to ${agentAddress}: agent is unreachable`,
|
|
450
|
-
);
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
return rawMessage;
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
async function endSession(
|
|
457
|
-
agentAddress: string,
|
|
458
|
-
reason: string,
|
|
459
|
-
): Promise<void> {
|
|
460
|
-
await sidecarRouter.sendAgentUndeploy(agentAddress, reason);
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
return { launchSession, sendUserMessage, endSession };
|
|
464
|
-
}
|