@intx/hub-sessions 0.3.0 → 0.4.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/dist/agent-repo.d.ts +14 -2
- package/dist/agent-repo.js +17 -4
- package/dist/agent-state-kind.js +14 -63
- package/dist/asset-service.js +14 -10
- package/dist/credential-push.d.ts +48 -4
- package/dist/credential-push.js +138 -6
- package/dist/event-collector-registry.d.ts +2 -1
- package/dist/event-collector-registry.js +38 -9
- package/dist/event-collector.d.ts +11 -1
- package/dist/event-collector.js +36 -3
- package/dist/hub-session-lookups.d.ts +1 -1
- package/dist/hub-session-lookups.js +68 -72
- package/dist/hub-session-orchestrator.d.ts +2 -3
- package/dist/hub-session-orchestrator.js +13 -12
- package/dist/index.d.ts +7 -6
- package/dist/index.js +7 -6
- package/dist/reconciliation-scheduler.d.ts +14 -0
- package/dist/reconciliation-scheduler.js +55 -0
- package/dist/repo-store/index.d.ts +1 -0
- package/dist/repo-store/index.js +1 -0
- package/dist/repo-store/user-principal-gate.d.ts +26 -0
- package/dist/repo-store/user-principal-gate.js +78 -0
- package/dist/session-service.d.ts +66 -121
- package/dist/session-service.js +444 -411
- package/dist/sidecar-allocation/capability-policy.d.ts +27 -0
- package/dist/sidecar-allocation/capability-policy.js +124 -0
- package/dist/sidecar-allocation/contracts.d.ts +29 -6
- package/dist/sidecar-allocation/contracts.js +7 -2
- package/dist/sidecar-allocation/index.d.ts +4 -3
- package/dist/sidecar-allocation/index.js +3 -2
- package/dist/sidecar-allocation/operation.d.ts +10 -0
- package/dist/sidecar-allocation/operation.js +54 -0
- package/dist/sidecar-allocation/plugin-registry.d.ts +16 -3
- package/dist/sidecar-allocation/plugin-registry.js +36 -12
- package/dist/sidecar-allocation/reconciler.d.ts +16 -4
- package/dist/sidecar-allocation/reconciler.js +486 -92
- package/dist/skill-kind.js +8 -62
- package/dist/substrate.d.ts +1 -1
- package/dist/substrate.js +1 -1
- package/dist/workflow-allocation-service.d.ts +21 -15
- package/dist/workflow-allocation-service.js +440 -125
- package/dist/workflow-dispatch-service.d.ts +4 -2
- package/dist/workflow-dispatch-service.js +89 -26
- package/dist/workflow-kind.d.ts +12 -0
- package/dist/workflow-kind.js +17 -60
- package/dist/workflow-probe-gate.d.ts +99 -27
- package/dist/workflow-probe-gate.js +196 -21
- package/dist/workflow-run-kind.d.ts +112 -19
- package/dist/workflow-run-kind.js +626 -210
- package/dist/workflow-run-restore.d.ts +1 -0
- package/dist/workflow-run-restore.js +5 -1
- package/dist/workflow-source-pins.d.ts +8 -0
- package/dist/workflow-source-pins.js +14 -0
- package/dist/ws/index.d.ts +1 -1
- package/dist/ws/index.js +1 -1
- package/dist/ws/pending-tracker.d.ts +93 -0
- package/dist/ws/pending-tracker.js +132 -0
- package/dist/ws/sidecar-events.d.ts +43 -29
- package/dist/ws/sidecar-events.js +0 -2
- package/dist/ws/sidecar-handler.d.ts +122 -85
- package/dist/ws/sidecar-handler.js +925 -878
- package/dist/ws/sidecar-handler.test-helpers.d.ts +38 -0
- package/dist/ws/sidecar-handler.test-helpers.js +95 -0
- package/dist/ws/sidecar-token-authenticator.js +37 -23
- package/package.json +13 -13
- package/dist/sidecar-allocation/placement-policy.d.ts +0 -11
- package/dist/sidecar-allocation/placement-policy.js +0 -21
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { HarnessConfig } from "@intx/types/runtime";
|
|
2
|
+
import { createSidecarRouter, type AllocatedSidecarTarget, type SidecarAuthIdentity, type SidecarRouterConfig, type WsHandle } from "./sidecar-handler.js";
|
|
3
|
+
export declare const TEST_IDENTITY: Extract<SidecarAuthIdentity, {
|
|
4
|
+
kind: "allocated";
|
|
5
|
+
}>;
|
|
6
|
+
export declare const TEST_TARGET: AllocatedSidecarTarget;
|
|
7
|
+
export declare const TEST_CONFIG: HarnessConfig;
|
|
8
|
+
export declare function createMockWs(): WsHandle & {
|
|
9
|
+
sent: string[];
|
|
10
|
+
closed: boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Resolve once `predicate` holds over the frames sent so far, re-checking
|
|
13
|
+
* on each send.
|
|
14
|
+
*
|
|
15
|
+
* Several of the router's paths write to the socket from a fire-and-forget
|
|
16
|
+
* continuation -- the mail redelivery retry is one -- so a test that
|
|
17
|
+
* triggers one has no return value to await. The send is the event.
|
|
18
|
+
*/
|
|
19
|
+
awaitSent(predicate: (sent: readonly string[]) => boolean): Promise<void>;
|
|
20
|
+
};
|
|
21
|
+
export declare function createAllocatedRouter(config?: Partial<SidecarRouterConfig>): import("./sidecar-handler.js").SidecarRouter & import("./sidecar-handler.js").SidecarAllocationRouter;
|
|
22
|
+
export declare function connectAllocated(router: ReturnType<typeof createSidecarRouter>, agentAddresses?: string[], frameType?: "register" | "reconnect"): Promise<WsHandle & {
|
|
23
|
+
sent: string[];
|
|
24
|
+
closed: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Resolve once `predicate` holds over the frames sent so far, re-checking
|
|
27
|
+
* on each send.
|
|
28
|
+
*
|
|
29
|
+
* Several of the router's paths write to the socket from a fire-and-forget
|
|
30
|
+
* continuation -- the mail redelivery retry is one -- so a test that
|
|
31
|
+
* triggers one has no return value to await. The send is the event.
|
|
32
|
+
*/
|
|
33
|
+
awaitSent(predicate: (sent: readonly string[]) => boolean): Promise<void>;
|
|
34
|
+
}>;
|
|
35
|
+
export declare function parsedFrames(ws: {
|
|
36
|
+
sent: string[];
|
|
37
|
+
}): unknown[];
|
|
38
|
+
export declare function tick(): Promise<void>;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { createSidecarRouter, } from "./sidecar-handler.js";
|
|
2
|
+
export const TEST_IDENTITY = {
|
|
3
|
+
kind: "allocated",
|
|
4
|
+
sidecarId: "sc-allocated",
|
|
5
|
+
allocationId: "alloc-1",
|
|
6
|
+
tenantId: "tenant-1",
|
|
7
|
+
anchorRunId: "run_anchor",
|
|
8
|
+
workflowRunAddress: "run_anchor@tenant.example",
|
|
9
|
+
generation: 1,
|
|
10
|
+
};
|
|
11
|
+
export const TEST_TARGET = {
|
|
12
|
+
allocationId: TEST_IDENTITY.allocationId,
|
|
13
|
+
generation: TEST_IDENTITY.generation,
|
|
14
|
+
};
|
|
15
|
+
export const TEST_CONFIG = {
|
|
16
|
+
sessionId: "ses-router-test",
|
|
17
|
+
agentId: "workflow",
|
|
18
|
+
tenantId: TEST_IDENTITY.tenantId,
|
|
19
|
+
principalId: "principal-1",
|
|
20
|
+
agentAddress: TEST_IDENTITY.workflowRunAddress,
|
|
21
|
+
systemPrompt: "test",
|
|
22
|
+
tools: [],
|
|
23
|
+
grants: [],
|
|
24
|
+
sources: [
|
|
25
|
+
{
|
|
26
|
+
id: "test-source",
|
|
27
|
+
provider: "test",
|
|
28
|
+
baseURL: "https://api.example.test",
|
|
29
|
+
credentialId: "test-credential",
|
|
30
|
+
model: "test",
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
defaultSource: "test-source",
|
|
34
|
+
};
|
|
35
|
+
export function createMockWs() {
|
|
36
|
+
let waiters = [];
|
|
37
|
+
return {
|
|
38
|
+
sent: [],
|
|
39
|
+
closed: false,
|
|
40
|
+
send(data) {
|
|
41
|
+
this.sent.push(data);
|
|
42
|
+
const waking = waiters;
|
|
43
|
+
waiters = [];
|
|
44
|
+
for (const wake of waking)
|
|
45
|
+
wake();
|
|
46
|
+
},
|
|
47
|
+
close() {
|
|
48
|
+
this.closed = true;
|
|
49
|
+
},
|
|
50
|
+
async awaitSent(predicate) {
|
|
51
|
+
for (;;) {
|
|
52
|
+
// Re-checked on every pass, so a frame sent before this call resolves
|
|
53
|
+
// it rather than leaving it waiting for another send.
|
|
54
|
+
const sent = new Promise((resolve) => {
|
|
55
|
+
waiters.push(resolve);
|
|
56
|
+
});
|
|
57
|
+
if (predicate(this.sent))
|
|
58
|
+
return;
|
|
59
|
+
await sent;
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
export function createAllocatedRouter(config = {}) {
|
|
65
|
+
const router = createSidecarRouter({
|
|
66
|
+
authenticateSidecar: async () => TEST_IDENTITY,
|
|
67
|
+
validateSidecarIdentity: async () => true,
|
|
68
|
+
hubPublicKey: "a".repeat(64),
|
|
69
|
+
requestTimeoutMs: 500,
|
|
70
|
+
...config,
|
|
71
|
+
});
|
|
72
|
+
router.fenceAllocation(TEST_TARGET.allocationId, TEST_TARGET.generation);
|
|
73
|
+
return router;
|
|
74
|
+
}
|
|
75
|
+
export async function connectAllocated(router, agentAddresses = [], frameType = "register") {
|
|
76
|
+
const ws = createMockWs();
|
|
77
|
+
router.handleOpen(ws);
|
|
78
|
+
router.handleMessage(ws, JSON.stringify({
|
|
79
|
+
type: frameType,
|
|
80
|
+
sidecarId: TEST_IDENTITY.sidecarId,
|
|
81
|
+
token: "token",
|
|
82
|
+
agentAddresses,
|
|
83
|
+
}));
|
|
84
|
+
await tick();
|
|
85
|
+
return ws;
|
|
86
|
+
}
|
|
87
|
+
export function parsedFrames(ws) {
|
|
88
|
+
return ws.sent.map((raw) => {
|
|
89
|
+
const parsed = JSON.parse(raw);
|
|
90
|
+
return parsed;
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
export function tick() {
|
|
94
|
+
return new Promise((resolve) => setTimeout(resolve, 0));
|
|
95
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { and, eq, inArray } from "drizzle-orm";
|
|
2
2
|
import { sha256 } from "@intx/crypto";
|
|
3
|
-
import { sidecar, sidecarAllocation, workflowRun } from "@intx/db/schema";
|
|
3
|
+
import { sidecar, sidecarAllocation, workflowProbe, workflowRun, } from "@intx/db/schema";
|
|
4
4
|
/**
|
|
5
5
|
* Builds an authenticator that verifies a sidecar's presented token
|
|
6
6
|
* against the per-sidecar hash stored on the `sidecar` table. The token
|
|
@@ -18,41 +18,55 @@ export function createSidecarCredentialResolver({ db, }) {
|
|
|
18
18
|
});
|
|
19
19
|
if (row === undefined)
|
|
20
20
|
return null;
|
|
21
|
-
if (row.credentialScope === "shared") {
|
|
22
|
-
return { kind: "shared", sidecarId: row.id };
|
|
23
|
-
}
|
|
24
21
|
const allocation = await db.query.sidecarAllocation.findFirst({
|
|
25
22
|
where: eq(sidecarAllocation.sidecarId, row.id),
|
|
26
23
|
});
|
|
27
|
-
if (allocation
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
24
|
+
if (allocation !== undefined) {
|
|
25
|
+
if (allocation.status !== "provisioning" &&
|
|
26
|
+
allocation.status !== "allocated") {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
const anchor = await db.query.workflowRun.findFirst({
|
|
30
|
+
columns: { address: true },
|
|
31
|
+
where: eq(workflowRun.id, allocation.anchorRunId),
|
|
32
|
+
});
|
|
33
|
+
if (anchor?.address === null || anchor?.address === undefined)
|
|
34
|
+
return null;
|
|
35
|
+
return {
|
|
36
|
+
kind: "allocated",
|
|
37
|
+
sidecarId: row.id,
|
|
38
|
+
allocationId: allocation.id,
|
|
39
|
+
tenantId: allocation.tenantId,
|
|
40
|
+
anchorRunId: allocation.anchorRunId,
|
|
41
|
+
workflowRunAddress: anchor.address,
|
|
42
|
+
generation: allocation.generation,
|
|
43
|
+
};
|
|
32
44
|
}
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
where: eq(workflowRun.id, allocation.anchorRunId),
|
|
45
|
+
const probe = await db.query.workflowProbe.findFirst({
|
|
46
|
+
where: eq(workflowProbe.sidecarId, row.id),
|
|
36
47
|
});
|
|
37
|
-
if (
|
|
48
|
+
if (probe === undefined ||
|
|
49
|
+
(probe.status !== "provisioning" && probe.status !== "probing")) {
|
|
38
50
|
return null;
|
|
51
|
+
}
|
|
39
52
|
return {
|
|
40
|
-
kind: "
|
|
53
|
+
kind: "probe",
|
|
41
54
|
sidecarId: row.id,
|
|
42
|
-
allocationId:
|
|
43
|
-
tenantId:
|
|
44
|
-
|
|
45
|
-
workflowRunAddress: anchor.address,
|
|
46
|
-
generation: allocation.generation,
|
|
55
|
+
allocationId: probe.id,
|
|
56
|
+
tenantId: probe.tenantId,
|
|
57
|
+
generation: probe.generation,
|
|
47
58
|
};
|
|
48
59
|
}
|
|
49
60
|
async function isCurrent(identity, use) {
|
|
50
|
-
if (identity.kind === "
|
|
51
|
-
const
|
|
61
|
+
if (identity.kind === "probe") {
|
|
62
|
+
const statuses = use === "registration"
|
|
63
|
+
? ["provisioning", "probing"]
|
|
64
|
+
: ["probing"];
|
|
65
|
+
const probe = await db.query.workflowProbe.findFirst({
|
|
52
66
|
columns: { id: true },
|
|
53
|
-
where: eq(
|
|
67
|
+
where: and(eq(workflowProbe.id, identity.allocationId), eq(workflowProbe.sidecarId, identity.sidecarId), eq(workflowProbe.tenantId, identity.tenantId), eq(workflowProbe.generation, identity.generation), inArray(workflowProbe.status, statuses)),
|
|
54
68
|
});
|
|
55
|
-
return
|
|
69
|
+
return probe !== undefined;
|
|
56
70
|
}
|
|
57
71
|
const statuses = use === "registration"
|
|
58
72
|
? ["provisioning", "allocated"]
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intx/hub-sessions",
|
|
3
3
|
"description": "Session-orchestration substrate for the hub: sidecar router, session, and asset services",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.4.0",
|
|
5
5
|
"license": "LGPL-2.1-only",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"exports": {
|
|
@@ -17,18 +17,18 @@
|
|
|
17
17
|
}
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@intx/agent": "0.
|
|
21
|
-
"@intx/crypto": "0.
|
|
22
|
-
"@intx/db": "0.
|
|
23
|
-
"@intx/hub-common": "0.
|
|
24
|
-
"@intx/log": "0.
|
|
25
|
-
"@intx/mime": "0.
|
|
26
|
-
"@intx/pack-transport": "0.
|
|
27
|
-
"@intx/storage-isogit": "0.
|
|
28
|
-
"@intx/tool-packaging": "0.
|
|
29
|
-
"@intx/types": "0.
|
|
30
|
-
"@intx/workflow": "0.
|
|
31
|
-
"@intx/workflow-deploy": "0.
|
|
20
|
+
"@intx/agent": "0.4.0",
|
|
21
|
+
"@intx/crypto": "0.4.0",
|
|
22
|
+
"@intx/db": "0.4.0",
|
|
23
|
+
"@intx/hub-common": "0.4.0",
|
|
24
|
+
"@intx/log": "0.4.0",
|
|
25
|
+
"@intx/mime": "0.4.0",
|
|
26
|
+
"@intx/pack-transport": "0.4.0",
|
|
27
|
+
"@intx/storage-isogit": "0.4.0",
|
|
28
|
+
"@intx/tool-packaging": "0.4.0",
|
|
29
|
+
"@intx/types": "0.4.0",
|
|
30
|
+
"@intx/workflow": "0.4.0",
|
|
31
|
+
"@intx/workflow-deploy": "0.4.0",
|
|
32
32
|
"arktype": "^2.1.29",
|
|
33
33
|
"drizzle-orm": "^0.45.1",
|
|
34
34
|
"isomorphic-git": "^1.27.2",
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import type { SidecarPlacementRequirement, TenantConfig } from "@intx/types";
|
|
2
|
-
export type ResolveEffectiveSidecarPlacementOpts = {
|
|
3
|
-
/** Tenant configs ordered from the workflow tenant through its ancestors. */
|
|
4
|
-
readonly tenantConfigs: readonly TenantConfig[];
|
|
5
|
-
};
|
|
6
|
-
/**
|
|
7
|
-
* Resolves the placement fixed onto a new workflow run. An exclusive
|
|
8
|
-
* requirement at any tenant ancestor forces exclusive placement; no tenant
|
|
9
|
-
* configuration can weaken it.
|
|
10
|
-
*/
|
|
11
|
-
export declare function resolveEffectiveSidecarPlacement({ tenantConfigs, }: ResolveEffectiveSidecarPlacementOpts): SidecarPlacementRequirement | null;
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
const EXCLUSIVE_PLACEMENT = Object.freeze({
|
|
2
|
-
sharing: "exclusive",
|
|
3
|
-
reuse: "never",
|
|
4
|
-
});
|
|
5
|
-
/**
|
|
6
|
-
* Resolves the placement fixed onto a new workflow run. An exclusive
|
|
7
|
-
* requirement at any tenant ancestor forces exclusive placement; no tenant
|
|
8
|
-
* configuration can weaken it.
|
|
9
|
-
*/
|
|
10
|
-
export function resolveEffectiveSidecarPlacement({ tenantConfigs, }) {
|
|
11
|
-
const tenantPlacements = tenantConfigs.flatMap((config) => config.sidecarPlacement?.sharing === "exclusive"
|
|
12
|
-
? [config.sidecarPlacement]
|
|
13
|
-
: []);
|
|
14
|
-
if (tenantPlacements.length === 0) {
|
|
15
|
-
return null;
|
|
16
|
-
}
|
|
17
|
-
const tenantRequiresFreshCapacity = tenantPlacements.some((placement) => placement.reuse !== "same-deployment");
|
|
18
|
-
return tenantRequiresFreshCapacity
|
|
19
|
-
? EXCLUSIVE_PLACEMENT
|
|
20
|
-
: { sharing: "exclusive", reuse: "same-deployment" };
|
|
21
|
-
}
|