@intx/hub-sessions 0.1.2 → 0.2.2
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 +84 -1
- package/dist/agent-repo.d.ts +89 -0
- package/dist/agent-repo.js +109 -0
- package/dist/agent-state-kind.d.ts +12 -0
- package/dist/agent-state-kind.js +185 -0
- package/dist/asset-service.d.ts +123 -0
- package/dist/asset-service.js +349 -0
- package/dist/available-skills-stanza.d.ts +21 -0
- package/dist/available-skills-stanza.js +32 -0
- package/dist/credential-push.d.ts +32 -0
- package/dist/credential-push.js +85 -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 +357 -0
- package/dist/hub-session-lookups.d.ts +17 -0
- package/dist/hub-session-lookups.js +204 -0
- package/dist/hub-session-orchestrator.d.ts +25 -0
- package/dist/hub-session-orchestrator.js +122 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +16 -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 +1692 -0
- package/dist/repo-store/subscribe-kind.d.ts +53 -0
- package/dist/repo-store/subscribe-kind.js +179 -0
- package/dist/repo-store/types.d.ts +483 -0
- package/dist/repo-store/types.js +42 -0
- package/dist/session-service.d.ts +235 -0
- package/dist/session-service.js +997 -0
- package/dist/skill-kind.d.ts +41 -0
- package/dist/skill-kind.js +288 -0
- package/dist/substrate.d.ts +8 -0
- package/dist/substrate.js +21 -0
- package/dist/workflow-kind.d.ts +21 -0
- package/dist/workflow-kind.js +263 -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 +326 -0
- package/dist/workflow-run-kind.js +2646 -0
- package/dist/workflow-run-reader.d.ts +47 -0
- package/dist/workflow-run-reader.js +157 -0
- package/dist/ws/index.d.ts +3 -0
- package/dist/ws/index.js +3 -0
- package/dist/ws/sidecar-events.d.ts +134 -0
- package/dist/ws/sidecar-events.js +70 -0
- package/dist/ws/sidecar-handler.d.ts +184 -0
- package/dist/ws/sidecar-handler.js +1603 -0
- package/dist/ws/sidecar-token-authenticator.d.ts +15 -0
- package/dist/ws/sidecar-token-authenticator.js +24 -0
- package/package.json +34 -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
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { DB } from "@intx/db";
|
|
2
|
+
import type { AgentRepoStore } from "./agent-repo.js";
|
|
3
|
+
import type { SidecarLookups } from "./ws/sidecar-events.js";
|
|
4
|
+
export type HubSessionLookupsDeps = {
|
|
5
|
+
db: DB["db"];
|
|
6
|
+
agentRepoStore: AgentRepoStore;
|
|
7
|
+
};
|
|
8
|
+
export declare function createHubSessionLookups(deps: HubSessionLookupsDeps): Required<SidecarLookups>;
|
|
9
|
+
/**
|
|
10
|
+
* Extract the instance id from an `<instanceId>@<domain>` agent address.
|
|
11
|
+
* Throws on any input the `@intx/types`-owned `parseAgentAddress`
|
|
12
|
+
* rejects: missing or leading `@`, empty domain, or an instance id
|
|
13
|
+
* without the canonical `ins_` prefix.
|
|
14
|
+
*/
|
|
15
|
+
export declare function parseAgentId(agentAddress: string): string;
|
|
16
|
+
export declare function findInstance(db: DB["db"], agentAddress: string): Promise<Awaited<ReturnType<typeof db.query.agentInstance.findFirst>>>;
|
|
17
|
+
export declare function requireInstance(db: DB["db"], agentAddress: string): Promise<NonNullable<Awaited<ReturnType<typeof db.query.agentInstance.findFirst>>>>;
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
// Lookups that the sidecar wire layer issues against host state. These
|
|
2
|
+
// are queries (one answer per question) rather than events (broadcast
|
|
3
|
+
// notifications), so they live separately from the event emitter.
|
|
4
|
+
//
|
|
5
|
+
// Each lookup is a stateless DB or repo call. They are gathered into a
|
|
6
|
+
// single struct that the hub app passes to `createSidecarRouter` as
|
|
7
|
+
// `lookups`.
|
|
8
|
+
import { eq, and, isNull } from "drizzle-orm";
|
|
9
|
+
import { agentInstance, sessionMail, workflowDeployment, } from "@intx/db/schema";
|
|
10
|
+
import { getLogger } from "@intx/log";
|
|
11
|
+
import { parseAgentAddress } from "@intx/types";
|
|
12
|
+
import { isWorkflowDerivedAddress } from "@intx/workflow-deploy";
|
|
13
|
+
import { generateId } from "@intx/hub-common";
|
|
14
|
+
const logger = getLogger(["hub", "lookups"]);
|
|
15
|
+
export function createHubSessionLookups(deps) {
|
|
16
|
+
const { db, agentRepoStore } = deps;
|
|
17
|
+
return {
|
|
18
|
+
async lookupPublicKey(agentAddress) {
|
|
19
|
+
// Route by address space, not a blind two-table fallback: a
|
|
20
|
+
// workflow-derived address's key lives on its workflow_deployment
|
|
21
|
+
// row, a launched agent's on its agent_instance row, and the two
|
|
22
|
+
// spaces are disjoint. Routing (rather than falling back) means a
|
|
23
|
+
// launched agent that is missing its instance row returns null and
|
|
24
|
+
// fails its challenge visibly, instead of silently resolving against
|
|
25
|
+
// the wrong table.
|
|
26
|
+
if (isWorkflowDerivedAddress(agentAddress)) {
|
|
27
|
+
// Filter to a live ("deployed") deployment so a torn-down
|
|
28
|
+
// deployment's key can no longer satisfy a challenge. A null
|
|
29
|
+
// publicKey (deployed but not yet acked, or pre-migration) or an
|
|
30
|
+
// absent row returns null -- the challenge fails closed and the
|
|
31
|
+
// address stays unrouted rather than routing without ownership
|
|
32
|
+
// proof.
|
|
33
|
+
const row = await db
|
|
34
|
+
.select({ publicKey: workflowDeployment.publicKey })
|
|
35
|
+
.from(workflowDeployment)
|
|
36
|
+
.where(and(eq(workflowDeployment.address, agentAddress), eq(workflowDeployment.status, "deployed")))
|
|
37
|
+
.limit(1)
|
|
38
|
+
.then((rows) => rows[0]);
|
|
39
|
+
return row?.publicKey ?? null;
|
|
40
|
+
}
|
|
41
|
+
const row = await db
|
|
42
|
+
.select({ publicKey: agentInstance.publicKey })
|
|
43
|
+
.from(agentInstance)
|
|
44
|
+
.where(and(eq(agentInstance.address, agentAddress), isNull(agentInstance.endedAt)))
|
|
45
|
+
.limit(1)
|
|
46
|
+
.then((rows) => rows[0]);
|
|
47
|
+
return row?.publicKey ?? null;
|
|
48
|
+
},
|
|
49
|
+
async lookupDeployRef(agentAddress) {
|
|
50
|
+
const agentId = parseAgentId(agentAddress);
|
|
51
|
+
return agentRepoStore.getDeployRef(agentId);
|
|
52
|
+
},
|
|
53
|
+
async persistMail({ senderAddress, recipients, raw }) {
|
|
54
|
+
const senderInstance = await requireInstance(db, senderAddress);
|
|
55
|
+
if (!senderInstance.sessionId) {
|
|
56
|
+
throw new Error(`Instance ${senderInstance.id} has no session for address "${senderAddress}"`);
|
|
57
|
+
}
|
|
58
|
+
const createdAt = new Date();
|
|
59
|
+
// Outbound record on the sender's session.
|
|
60
|
+
const outboundId = generateId("sessionMail");
|
|
61
|
+
const outboundRecord = {
|
|
62
|
+
id: outboundId,
|
|
63
|
+
sessionId: senderInstance.sessionId,
|
|
64
|
+
instanceId: senderInstance.id,
|
|
65
|
+
tenantId: senderInstance.tenantId,
|
|
66
|
+
direction: "outbound",
|
|
67
|
+
status: "delivered",
|
|
68
|
+
raw,
|
|
69
|
+
createdAt,
|
|
70
|
+
};
|
|
71
|
+
// Inbound records for each recipient that has an active agent
|
|
72
|
+
// instance. Recipients that are not agent instances (e.g. human
|
|
73
|
+
// user addresses) are skipped.
|
|
74
|
+
const recipientResults = await Promise.all(recipients.map(async (addr) => {
|
|
75
|
+
const row = await findInstance(db, addr);
|
|
76
|
+
if (row === undefined) {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
if (row.sessionId === null) {
|
|
80
|
+
logger.warn `Active instance ${row.id} for "${addr}" has no session; skipping inbound record`;
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
return { addr, instance: row, sessionId: row.sessionId };
|
|
84
|
+
}));
|
|
85
|
+
const recipientInstances = recipientResults.filter((r) => r !== null);
|
|
86
|
+
const inboundEntries = recipientInstances.map(({ addr, instance, sessionId }) => {
|
|
87
|
+
const id = generateId("sessionMail");
|
|
88
|
+
return {
|
|
89
|
+
record: {
|
|
90
|
+
id,
|
|
91
|
+
sessionId,
|
|
92
|
+
instanceId: instance.id,
|
|
93
|
+
tenantId: instance.tenantId,
|
|
94
|
+
direction: "inbound",
|
|
95
|
+
status: "delivered",
|
|
96
|
+
raw,
|
|
97
|
+
createdAt,
|
|
98
|
+
},
|
|
99
|
+
result: {
|
|
100
|
+
id,
|
|
101
|
+
direction: "inbound",
|
|
102
|
+
instanceId: instance.id,
|
|
103
|
+
address: addr,
|
|
104
|
+
createdAt,
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
});
|
|
108
|
+
await db
|
|
109
|
+
.insert(sessionMail)
|
|
110
|
+
.values([outboundRecord, ...inboundEntries.map((e) => e.record)]);
|
|
111
|
+
return [
|
|
112
|
+
{
|
|
113
|
+
id: outboundId,
|
|
114
|
+
direction: "outbound",
|
|
115
|
+
instanceId: senderInstance.id,
|
|
116
|
+
address: senderInstance.address,
|
|
117
|
+
createdAt,
|
|
118
|
+
},
|
|
119
|
+
...inboundEntries.map((e) => e.result),
|
|
120
|
+
];
|
|
121
|
+
},
|
|
122
|
+
async receiveAgentStatePack(repoId, pack, ref, commitSha) {
|
|
123
|
+
if (repoId.kind !== "agent-state") {
|
|
124
|
+
throw new Error(`hub-session lookups receiveAgentStatePack received unsupported repo kind ${JSON.stringify(repoId.kind)}`);
|
|
125
|
+
}
|
|
126
|
+
const agentAddress = repoId.id;
|
|
127
|
+
const agentId = parseAgentId(agentAddress);
|
|
128
|
+
try {
|
|
129
|
+
await agentRepoStore.receiveAgentStatePack({ kind: "agent-state", id: agentId }, pack, ref, commitSha);
|
|
130
|
+
}
|
|
131
|
+
catch (err) {
|
|
132
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
133
|
+
if (msg.startsWith("path_violation")) {
|
|
134
|
+
logger.warn `State pack rejected for ${agentAddress}: ${msg}`;
|
|
135
|
+
return { accepted: false, reason: "path_violation" };
|
|
136
|
+
}
|
|
137
|
+
// Any other failure from the repo subsystem reaches the
|
|
138
|
+
// WebSocket handler as an unhandled rejection unless we catch
|
|
139
|
+
// it here. Transient failures during receivePack (the agent
|
|
140
|
+
// directory being torn down concurrently with an in-flight
|
|
141
|
+
// pack write, filesystem errors mid-rename, etc.) are
|
|
142
|
+
// recoverable from the sender's perspective — the sender can
|
|
143
|
+
// re-push. Surface every such failure as a structured pack
|
|
144
|
+
// rejection (`corrupt` is the closest existing reason — from
|
|
145
|
+
// the sender's perspective the pack failed to index) and log
|
|
146
|
+
// the underlying error so the cause stays traceable on the
|
|
147
|
+
// hub side.
|
|
148
|
+
logger.error `State pack receive failed for ${agentAddress}: ${msg}`;
|
|
149
|
+
return { accepted: false, reason: "corrupt" };
|
|
150
|
+
}
|
|
151
|
+
return { accepted: true };
|
|
152
|
+
},
|
|
153
|
+
async receiveWorkflowRunPack(repoId, pack, ref, commitSha) {
|
|
154
|
+
if (repoId.kind !== "workflow-run") {
|
|
155
|
+
throw new Error(`hub-session lookups receiveWorkflowRunPack received unsupported repo kind ${JSON.stringify(repoId.kind)}`);
|
|
156
|
+
}
|
|
157
|
+
const deploymentId = repoId.id;
|
|
158
|
+
try {
|
|
159
|
+
await agentRepoStore.receiveWorkflowRunPack({ kind: "workflow-run", id: deploymentId }, pack, ref, commitSha);
|
|
160
|
+
}
|
|
161
|
+
catch (err) {
|
|
162
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
163
|
+
if (msg.startsWith("path_violation")) {
|
|
164
|
+
logger.warn `Workflow-run pack rejected for ${deploymentId}: ${msg}`;
|
|
165
|
+
return { accepted: false, reason: "path_violation" };
|
|
166
|
+
}
|
|
167
|
+
// Mirror the agent-state branch's catch-all: any other failure
|
|
168
|
+
// from the repo subsystem (filesystem races, kind-handler
|
|
169
|
+
// diagnostics surfaced as Error messages, etc.) becomes a
|
|
170
|
+
// structured `corrupt` rejection so the sender can re-push,
|
|
171
|
+
// and the underlying error is logged so the cause stays
|
|
172
|
+
// traceable on the hub side.
|
|
173
|
+
logger.error `Workflow-run pack receive failed for ${deploymentId}: ${msg}`;
|
|
174
|
+
return { accepted: false, reason: "corrupt" };
|
|
175
|
+
}
|
|
176
|
+
return { accepted: true };
|
|
177
|
+
},
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Extract the instance id from an `<instanceId>@<domain>` agent address.
|
|
182
|
+
* Throws on any input the `@intx/types`-owned `parseAgentAddress`
|
|
183
|
+
* rejects: missing or leading `@`, empty domain, or an instance id
|
|
184
|
+
* without the canonical `ins_` prefix.
|
|
185
|
+
*/
|
|
186
|
+
export function parseAgentId(agentAddress) {
|
|
187
|
+
const parsed = parseAgentAddress(agentAddress);
|
|
188
|
+
if (parsed === null) {
|
|
189
|
+
throw new Error(`Invalid agent address: "${agentAddress}"`);
|
|
190
|
+
}
|
|
191
|
+
return parsed.instanceId;
|
|
192
|
+
}
|
|
193
|
+
export async function findInstance(db, agentAddress) {
|
|
194
|
+
return db.query.agentInstance.findFirst({
|
|
195
|
+
where: and(eq(agentInstance.address, agentAddress), isNull(agentInstance.endedAt)),
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
export async function requireInstance(db, agentAddress) {
|
|
199
|
+
const row = await findInstance(db, agentAddress);
|
|
200
|
+
if (!row) {
|
|
201
|
+
throw new Error(`No active instance found for address "${agentAddress}"`);
|
|
202
|
+
}
|
|
203
|
+
return row;
|
|
204
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { DB } from "@intx/db";
|
|
2
|
+
import type { AgentRepoStore } from "./agent-repo.js";
|
|
3
|
+
import type { EventCollectorRegistry } from "./event-collector-registry.js";
|
|
4
|
+
import type { SidecarEventEmitter } from "./ws/sidecar-events.js";
|
|
5
|
+
/** Subset of `SidecarRouter` the orchestrator drives outbound. The
|
|
6
|
+
* narrow surface keeps tests honest and decouples the orchestrator
|
|
7
|
+
* from the rest of the router API. */
|
|
8
|
+
export type HubSessionRouterFacade = {
|
|
9
|
+
sendPack(agentAddress: string, pack: Uint8Array, ref: string, commitSha: string): Promise<void>;
|
|
10
|
+
dispatchAgentEvent(agentAddress: string, event: unknown): void;
|
|
11
|
+
};
|
|
12
|
+
export type HubSessionOrchestratorDeps = {
|
|
13
|
+
events: SidecarEventEmitter;
|
|
14
|
+
router: HubSessionRouterFacade;
|
|
15
|
+
db: DB["db"];
|
|
16
|
+
eventCollectors: EventCollectorRegistry;
|
|
17
|
+
agentRepoStore: AgentRepoStore;
|
|
18
|
+
};
|
|
19
|
+
export type HubSessionOrchestrator = {
|
|
20
|
+
/** Unsubscribe all listeners. Tests use this between cases; the hub
|
|
21
|
+
* application doesn't need to dispose because the orchestrator's
|
|
22
|
+
* lifetime matches the process. */
|
|
23
|
+
dispose(): void;
|
|
24
|
+
};
|
|
25
|
+
export declare function createHubSessionOrchestrator(deps: HubSessionOrchestratorDeps): HubSessionOrchestrator;
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
// Hub-side orchestrator for the sidecar event emitter.
|
|
2
|
+
//
|
|
3
|
+
// Subscribes to the sidecar router's events and runs the host-side
|
|
4
|
+
// reactions: dispatching inference events to the event collector,
|
|
5
|
+
// restoring the event collector on reconnect, re-deploying stale
|
|
6
|
+
// agents, and forwarding mail.delivered notifications back to
|
|
7
|
+
// subscribers.
|
|
8
|
+
//
|
|
9
|
+
// The orchestrator depends on a narrow `HubSessionRouterFacade` rather
|
|
10
|
+
// than the full SidecarRouter, so tests can drive subscriber behavior
|
|
11
|
+
// with a small stub and an isolated emitter.
|
|
12
|
+
import { eq } from "drizzle-orm";
|
|
13
|
+
import { type } from "arktype";
|
|
14
|
+
import { agentInstance, workflowDeployment } from "@intx/db/schema";
|
|
15
|
+
import { parseMailToEmail } from "@intx/mime";
|
|
16
|
+
import { parseInferenceEvent } from "@intx/types/runtime";
|
|
17
|
+
import { getLogger } from "@intx/log";
|
|
18
|
+
import { isWorkflowDerivedAddress } from "@intx/workflow-deploy";
|
|
19
|
+
import { findInstance, parseAgentId, requireInstance, } from "./hub-session-lookups.js";
|
|
20
|
+
const log = getLogger(["hub", "orchestrator"]);
|
|
21
|
+
export function createHubSessionOrchestrator(deps) {
|
|
22
|
+
const { events, router, db, eventCollectors, agentRepoStore } = deps;
|
|
23
|
+
const unsubscribers = [];
|
|
24
|
+
unsubscribers.push(events.on("agent.event", ({ agentAddress, event }) => {
|
|
25
|
+
const validated = parseInferenceEvent(event);
|
|
26
|
+
if (validated instanceof type.errors) {
|
|
27
|
+
log.warn("Received invalid agent event for {agentAddress}: {summary}", {
|
|
28
|
+
agentAddress,
|
|
29
|
+
summary: validated.summary,
|
|
30
|
+
});
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
eventCollectors.dispatch(agentAddress, validated);
|
|
34
|
+
}));
|
|
35
|
+
unsubscribers.push(events.on("sidecar.disconnect", ({ ownedAddresses }) => {
|
|
36
|
+
for (const addr of ownedAddresses) {
|
|
37
|
+
eventCollectors.abandon(addr);
|
|
38
|
+
}
|
|
39
|
+
}));
|
|
40
|
+
unsubscribers.push(events.on("mail.outbound.undelivered", ({ recipients }) => {
|
|
41
|
+
// The hub has no external mail transport today. Anything that
|
|
42
|
+
// could not be delivered locally or queued for a disconnected
|
|
43
|
+
// agent is dropped; log so operators can see it.
|
|
44
|
+
log.warn("Dropping mail with no local recipient: {recipients}", {
|
|
45
|
+
recipients: recipients.join(", "),
|
|
46
|
+
});
|
|
47
|
+
}));
|
|
48
|
+
unsubscribers.push(events.on("agent.deploy.ack", async ({ agentAddress, publicKey }) => {
|
|
49
|
+
// Workflow-derived addresses (the deployment-level
|
|
50
|
+
// `ins_<deploymentId>@<domain>` and the per-step
|
|
51
|
+
// `ins_<deploymentId>-<stepId>@<domain>`) have no agent_instance row;
|
|
52
|
+
// their public key lives on the workflow_deployment projection row,
|
|
53
|
+
// keyed by address. Persist it there so the reconnect ownership
|
|
54
|
+
// challenge can verify the deployment address. Only the
|
|
55
|
+
// deployment-level address has a row, so a stray per-step ack updates
|
|
56
|
+
// nothing. (This was previously a no-op, which is what left
|
|
57
|
+
// workflow-deployment addresses un-verifiable on reconnect.)
|
|
58
|
+
if (isWorkflowDerivedAddress(agentAddress)) {
|
|
59
|
+
await db
|
|
60
|
+
.update(workflowDeployment)
|
|
61
|
+
.set({ publicKey })
|
|
62
|
+
.where(eq(workflowDeployment.address, agentAddress));
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
// A launched agent has an agent_instance row; a missing one is a bug
|
|
66
|
+
// to surface, not to drop silently.
|
|
67
|
+
const instance = await findInstance(db, agentAddress);
|
|
68
|
+
if (instance === undefined) {
|
|
69
|
+
throw new Error(`No active instance found for deploy ack on address "${agentAddress}"`);
|
|
70
|
+
}
|
|
71
|
+
await db
|
|
72
|
+
.update(agentInstance)
|
|
73
|
+
.set({ publicKey })
|
|
74
|
+
.where(eq(agentInstance.id, instance.id));
|
|
75
|
+
}));
|
|
76
|
+
unsubscribers.push(events.on("agent.reconnected", async ({ agentAddress }) => {
|
|
77
|
+
const instance = await requireInstance(db, agentAddress);
|
|
78
|
+
if (!instance.sessionId) {
|
|
79
|
+
throw new Error(`Agent "${agentAddress}" reconnected but has no active session`);
|
|
80
|
+
}
|
|
81
|
+
const sessionId = instance.sessionId;
|
|
82
|
+
// A supervised deployment carries its grants and sources in the
|
|
83
|
+
// deploy pack and refreshes them over the supervisor's IPC
|
|
84
|
+
// credentials snapshot at spawn and recycle, so reconnect does not
|
|
85
|
+
// re-push them over the wire.
|
|
86
|
+
const now = new Date();
|
|
87
|
+
if (instance.status !== "running") {
|
|
88
|
+
await db
|
|
89
|
+
.update(agentInstance)
|
|
90
|
+
.set({ status: "running", updatedAt: now })
|
|
91
|
+
.where(eq(agentInstance.id, instance.id));
|
|
92
|
+
}
|
|
93
|
+
if (!eventCollectors.has(agentAddress)) {
|
|
94
|
+
eventCollectors.create(agentAddress, instance.tenantId, sessionId, instance.id);
|
|
95
|
+
log.info("Restored event collector for reconnected agent {agentAddress}", { agentAddress });
|
|
96
|
+
}
|
|
97
|
+
}));
|
|
98
|
+
unsubscribers.push(events.on("deploy.ref.stale", async ({ agentAddress }) => {
|
|
99
|
+
const agentId = parseAgentId(agentAddress);
|
|
100
|
+
const { pack, commitSha, ref } = await agentRepoStore.createDeployPack(agentId);
|
|
101
|
+
await router.sendPack(agentAddress, pack, ref, commitSha);
|
|
102
|
+
log.info("Re-deployed stale agent {agentAddress}", { agentAddress });
|
|
103
|
+
}));
|
|
104
|
+
unsubscribers.push(events.on("mail.persisted", (row) => {
|
|
105
|
+
const parsed = parseMailToEmail(row.raw, row.id);
|
|
106
|
+
router.dispatchAgentEvent(row.address, {
|
|
107
|
+
type: "mail.delivered",
|
|
108
|
+
data: {
|
|
109
|
+
...parsed,
|
|
110
|
+
id: row.id,
|
|
111
|
+
direction: row.direction,
|
|
112
|
+
receivedAt: row.createdAt.toISOString(),
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
}));
|
|
116
|
+
return {
|
|
117
|
+
dispose() {
|
|
118
|
+
for (const off of unsubscribers)
|
|
119
|
+
off();
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export { createAgentRepoStore, type AgentRepoStore, type DeployContent, } from "./agent-repo.js";
|
|
2
|
+
export { createSessionService, SessionLaunchError, bridgeOrchestratorDeployContent, type SessionService, type DeployWorkflowDefinitionParams, type DeployWorkflowDefinitionResult, } from "./session-service.js";
|
|
3
|
+
export type { WorkflowDefinition } from "@intx/workflow/definition";
|
|
4
|
+
export { createEventCollectorRegistry, type EventCollectorRegistry, } from "./event-collector-registry.js";
|
|
5
|
+
export { createSidecarRouter, type SidecarRouter, type SidecarRouterConfig, type SidecarAuthIdentity, type SidecarAuthenticator, createSidecarTokenAuthenticator, type CreateSidecarTokenAuthenticatorDeps, type WsHandle, createSidecarEmitter, type SidecarEventEmitter, type SidecarEventMap, type SidecarEventType, type SidecarEventListener, type SidecarLookups, type SidecarMailPersistedPayload, type SidecarMailPersistedRow, } from "./ws/index.js";
|
|
6
|
+
export { createHubSessionLookups, parseAgentId, type HubSessionLookupsDeps, } from "./hub-session-lookups.js";
|
|
7
|
+
export { createHubSessionOrchestrator, type HubSessionOrchestrator, type HubSessionOrchestratorDeps, type HubSessionRouterFacade, } from "./hub-session-orchestrator.js";
|
|
8
|
+
export { pushSourceUpdates, pushSourceUpdatesSubtree } from "./credential-push.js";
|
|
9
|
+
export { skillKindHandler, skillAuthorize, skillFrontmatterSchema, getSkillIndex, type SkillIndexEntry, type SkillFrontmatter, type SkillPrincipal, type SkillHubPrincipal, type SkillSidecarPrincipal, } from "./skill-kind.js";
|
|
10
|
+
export { packageRegistryKindHandler, packageRegistryAuthorize, asTarballEntry, validateTarballPackageJSON, TARBALLS_PREFIX, TARBALL_FILENAME_PATTERN, REGISTRY_INDEX_PATH, WORKSPACE_BUILTINS_REGISTRY, } from "./package-registry-kind.js";
|
|
11
|
+
export { workflowKindHandler, workflowAuthorize, workflowDefinitionEnvelopeSchema, WORKFLOW_JSON_PATH, CAPABILITY_DECLARATIONS_JSON_PATH, WORKFLOW_GITIGNORE_PATH, type WorkflowPrincipal, type WorkflowHubPrincipal, type WorkflowSidecarPrincipal, } from "./workflow-kind.js";
|
|
12
|
+
export { workflowRunKindHandler, workflowRunAuthorize, enqueueInbox, dequeueToProcessing, readProcessingEntry, markConsumed, readOwnedMessageIds, replayProcessingToInbox, WORKFLOW_RUN_GITIGNORE_PATH, WORKFLOW_RUN_RUNS_PREFIX, WORKFLOW_RUN_EVENTS_DIR, WORKFLOW_RUN_AGENT_STATE_PREFIX, WORKFLOW_RUN_ADDRESSES_PREFIX, WORKFLOW_RUN_CONTROL_PREFIX, WORKFLOW_RUN_INBOX_DIR, WORKFLOW_RUN_PROCESSING_DIR, WORKFLOW_RUN_CONSUMED_DIR, WORKFLOW_RUN_WATERMARK_FILE, DEFAULT_CONSUMED_RETENTION_MS, type ClaimCheckEnvelope, type ConsumedEnvelope, type EnqueueInboxArgs, type EnqueueInboxResult, type DequeueToProcessingResult, type ReadProcessingEntryResult, type MarkConsumedArgs, type MarkConsumedResult, type ReplayProcessingToInboxOpts, type ReplayProcessingToInboxResult, type WorkflowRunPrincipal, type WorkflowRunHubPrincipal, type WorkflowRunSidecarPrincipal, type WorkflowRunWorkflowProcessPrincipal, type WorkflowRunSupervisorPrincipal, } from "./workflow-run-kind.js";
|
|
13
|
+
export { createAssetService, AssetServiceError, DEFAULT_ASSET_REF, type AssetService, type Asset, type AgentAsset, type AgentAssetWithAsset, type AccessMode, type CreateAssetParams, type PopulateAssetParams, type AttachAssetParams, type AssetServiceErrorReason, type ReadAssetBlobParams, type ListAssetBlobsParams, } from "./asset-service.js";
|
|
14
|
+
export { buildAvailableSkillsStanza, type AvailableSkillEntry, } from "./available-skills-stanza.js";
|
|
15
|
+
export { createWorkflowRunReader, type WorkflowRunReader, type WorkflowRunEvent, } from "./workflow-run-reader.js";
|
|
16
|
+
export { WORKFLOW_RUN_EVENTS_FILE, splitCombinedEventLog, encodeCombinedEventLog, } from "./workflow-run-event-log.js";
|
|
17
|
+
export type { AuthorizeFn, CreateRepoStoreConfig, InitRepoOpts, KindHandler, NewlyTerminalRun, Principal, RefEntry, RepoAction, RepoId, RepoStore, SubscribeKindEntry, SubscribeKindOpts, ValidatePushResult, WriteResult, } from "./repo-store/index.js";
|
|
18
|
+
export { createRepoStore, subscribeKind, UserPrincipal } from "./repo-store/index.js";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export { createAgentRepoStore, } from "./agent-repo.js";
|
|
2
|
+
export { createSessionService, SessionLaunchError, bridgeOrchestratorDeployContent, } from "./session-service.js";
|
|
3
|
+
export { createEventCollectorRegistry, } from "./event-collector-registry.js";
|
|
4
|
+
export { createSidecarRouter, createSidecarTokenAuthenticator, createSidecarEmitter, } from "./ws/index.js";
|
|
5
|
+
export { createHubSessionLookups, parseAgentId, } from "./hub-session-lookups.js";
|
|
6
|
+
export { createHubSessionOrchestrator, } from "./hub-session-orchestrator.js";
|
|
7
|
+
export { pushSourceUpdates, pushSourceUpdatesSubtree } from "./credential-push.js";
|
|
8
|
+
export { skillKindHandler, skillAuthorize, skillFrontmatterSchema, getSkillIndex, } from "./skill-kind.js";
|
|
9
|
+
export { packageRegistryKindHandler, packageRegistryAuthorize, asTarballEntry, validateTarballPackageJSON, TARBALLS_PREFIX, TARBALL_FILENAME_PATTERN, REGISTRY_INDEX_PATH, WORKSPACE_BUILTINS_REGISTRY, } from "./package-registry-kind.js";
|
|
10
|
+
export { workflowKindHandler, workflowAuthorize, workflowDefinitionEnvelopeSchema, WORKFLOW_JSON_PATH, CAPABILITY_DECLARATIONS_JSON_PATH, WORKFLOW_GITIGNORE_PATH, } from "./workflow-kind.js";
|
|
11
|
+
export { workflowRunKindHandler, workflowRunAuthorize, enqueueInbox, dequeueToProcessing, readProcessingEntry, markConsumed, readOwnedMessageIds, replayProcessingToInbox, WORKFLOW_RUN_GITIGNORE_PATH, WORKFLOW_RUN_RUNS_PREFIX, WORKFLOW_RUN_EVENTS_DIR, WORKFLOW_RUN_AGENT_STATE_PREFIX, WORKFLOW_RUN_ADDRESSES_PREFIX, WORKFLOW_RUN_CONTROL_PREFIX, WORKFLOW_RUN_INBOX_DIR, WORKFLOW_RUN_PROCESSING_DIR, WORKFLOW_RUN_CONSUMED_DIR, WORKFLOW_RUN_WATERMARK_FILE, DEFAULT_CONSUMED_RETENTION_MS, } from "./workflow-run-kind.js";
|
|
12
|
+
export { createAssetService, AssetServiceError, DEFAULT_ASSET_REF, } from "./asset-service.js";
|
|
13
|
+
export { buildAvailableSkillsStanza, } from "./available-skills-stanza.js";
|
|
14
|
+
export { createWorkflowRunReader, } from "./workflow-run-reader.js";
|
|
15
|
+
export { WORKFLOW_RUN_EVENTS_FILE, splitCombinedEventLog, encodeCombinedEventLog, } from "./workflow-run-event-log.js";
|
|
16
|
+
export { createRepoStore, subscribeKind, UserPrincipal } from "./repo-store/index.js";
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { PackageJSON } from "@intx/types/package-json";
|
|
2
|
+
import { type AuthorizeFn, type KindHandler } from "./repo-store/index.js";
|
|
3
|
+
export declare const TARBALLS_PREFIX = "tarballs/";
|
|
4
|
+
export declare const REGISTRY_INDEX_PATH = "package-registry.json";
|
|
5
|
+
/**
|
|
6
|
+
* Canonical asset name for the workspace's bundled package-registry —
|
|
7
|
+
* the in-tree `@intx/tools-*` packages live in an asset of this name
|
|
8
|
+
* under the workspace's root tenant. The hub's scope-routing config
|
|
9
|
+
* maps `@intx` to this registry, the seed script ensures the asset
|
|
10
|
+
* row exists, and the publish-tool-packages CLI defaults its target
|
|
11
|
+
* registry to this name. The constant lives at one site so a rename
|
|
12
|
+
* does not have to chase three independent string literals.
|
|
13
|
+
*
|
|
14
|
+
* Callers:
|
|
15
|
+
* - `bin/dev.ts` — orchestrator default for the registry the dev
|
|
16
|
+
* stack publishes the built-ins into.
|
|
17
|
+
* - `bin/seed.ts` — seeder that pins the workspace-builtins into
|
|
18
|
+
* the registry asset at boot.
|
|
19
|
+
* - `bin/publish-tool-packages.ts` — CLI default for the
|
|
20
|
+
* `--registry` flag.
|
|
21
|
+
*
|
|
22
|
+
* No test asserts that the seed's pinned built-ins actually land
|
|
23
|
+
* under this exact name; a mismatch between the constant and a
|
|
24
|
+
* caller would surface at apply time as a `tarball.missing`
|
|
25
|
+
* structured failure, not as a build error.
|
|
26
|
+
*/
|
|
27
|
+
export declare const WORKSPACE_BUILTINS_REGISTRY = "workspace-builtins";
|
|
28
|
+
/**
|
|
29
|
+
* Filename rule for tarballs in the repo tree. Filenames must end in
|
|
30
|
+
* `.tgz`, start with an alphanumeric / underscore / scope-marker
|
|
31
|
+
* character, and otherwise contain only filename-safe characters. The
|
|
32
|
+
* leading-character constraint forbids hidden-style names like
|
|
33
|
+
* `..tgz` or `.hidden.tgz` that the filesystem treats as dotfiles —
|
|
34
|
+
* those would be invisible to a `readdir` shell expansion and split
|
|
35
|
+
* the resolver's view of the registry tree from the operator's. The
|
|
36
|
+
* same rule is enforced at the REST upload boundary so a push and a
|
|
37
|
+
* PUT cannot produce diverging contents.
|
|
38
|
+
*/
|
|
39
|
+
export declare const TARBALL_FILENAME_PATTERN: RegExp;
|
|
40
|
+
/**
|
|
41
|
+
* Validates that `path` belongs to a tarball entry shape
|
|
42
|
+
* `tarballs/<filename>.tgz`. Returns the bare filename when valid,
|
|
43
|
+
* `null` otherwise.
|
|
44
|
+
*/
|
|
45
|
+
export declare function asTarballEntry(path: string): string | null;
|
|
46
|
+
type TarballValidationOutcome = {
|
|
47
|
+
ok: true;
|
|
48
|
+
pkg: PackageJSON;
|
|
49
|
+
} | {
|
|
50
|
+
ok: false;
|
|
51
|
+
reason: string;
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* Open an npm-style tarball, find the top-level `package.json` entry,
|
|
55
|
+
* parse and validate it. Returns the typed descriptor on success; a
|
|
56
|
+
* structured reason otherwise. The shape validation lives inside
|
|
57
|
+
* `extractTarballPackageJSON`; this wrapper only translates the
|
|
58
|
+
* outcome into the substrate's `ValidatePushResult`-shaped reason
|
|
59
|
+
* strings.
|
|
60
|
+
*/
|
|
61
|
+
export declare function validateTarballPackageJSON(filename: string, bytes: Uint8Array): Promise<TarballValidationOutcome>;
|
|
62
|
+
export declare const packageRegistryKindHandler: KindHandler;
|
|
63
|
+
/**
|
|
64
|
+
* Authorize policy for package-registry repos. Sidecars never write
|
|
65
|
+
* to package-registry repos (they consume contents through the
|
|
66
|
+
* in-process read API, not the smart-HTTP layer); only the hub and
|
|
67
|
+
* authenticated users with the right grant may push.
|
|
68
|
+
*/
|
|
69
|
+
export declare const packageRegistryAuthorize: AuthorizeFn;
|
|
70
|
+
export {};
|