@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.
Files changed (67) hide show
  1. package/dist/agent-repo.d.ts +14 -2
  2. package/dist/agent-repo.js +17 -4
  3. package/dist/agent-state-kind.js +14 -63
  4. package/dist/asset-service.js +14 -10
  5. package/dist/credential-push.d.ts +48 -4
  6. package/dist/credential-push.js +138 -6
  7. package/dist/event-collector-registry.d.ts +2 -1
  8. package/dist/event-collector-registry.js +38 -9
  9. package/dist/event-collector.d.ts +11 -1
  10. package/dist/event-collector.js +36 -3
  11. package/dist/hub-session-lookups.d.ts +1 -1
  12. package/dist/hub-session-lookups.js +68 -72
  13. package/dist/hub-session-orchestrator.d.ts +2 -3
  14. package/dist/hub-session-orchestrator.js +13 -12
  15. package/dist/index.d.ts +7 -6
  16. package/dist/index.js +7 -6
  17. package/dist/reconciliation-scheduler.d.ts +14 -0
  18. package/dist/reconciliation-scheduler.js +55 -0
  19. package/dist/repo-store/index.d.ts +1 -0
  20. package/dist/repo-store/index.js +1 -0
  21. package/dist/repo-store/user-principal-gate.d.ts +26 -0
  22. package/dist/repo-store/user-principal-gate.js +78 -0
  23. package/dist/session-service.d.ts +66 -121
  24. package/dist/session-service.js +444 -411
  25. package/dist/sidecar-allocation/capability-policy.d.ts +27 -0
  26. package/dist/sidecar-allocation/capability-policy.js +124 -0
  27. package/dist/sidecar-allocation/contracts.d.ts +29 -6
  28. package/dist/sidecar-allocation/contracts.js +7 -2
  29. package/dist/sidecar-allocation/index.d.ts +4 -3
  30. package/dist/sidecar-allocation/index.js +3 -2
  31. package/dist/sidecar-allocation/operation.d.ts +10 -0
  32. package/dist/sidecar-allocation/operation.js +54 -0
  33. package/dist/sidecar-allocation/plugin-registry.d.ts +16 -3
  34. package/dist/sidecar-allocation/plugin-registry.js +36 -12
  35. package/dist/sidecar-allocation/reconciler.d.ts +16 -4
  36. package/dist/sidecar-allocation/reconciler.js +486 -92
  37. package/dist/skill-kind.js +8 -62
  38. package/dist/substrate.d.ts +1 -1
  39. package/dist/substrate.js +1 -1
  40. package/dist/workflow-allocation-service.d.ts +21 -15
  41. package/dist/workflow-allocation-service.js +440 -125
  42. package/dist/workflow-dispatch-service.d.ts +4 -2
  43. package/dist/workflow-dispatch-service.js +89 -26
  44. package/dist/workflow-kind.d.ts +12 -0
  45. package/dist/workflow-kind.js +17 -60
  46. package/dist/workflow-probe-gate.d.ts +99 -27
  47. package/dist/workflow-probe-gate.js +196 -21
  48. package/dist/workflow-run-kind.d.ts +112 -19
  49. package/dist/workflow-run-kind.js +626 -210
  50. package/dist/workflow-run-restore.d.ts +1 -0
  51. package/dist/workflow-run-restore.js +5 -1
  52. package/dist/workflow-source-pins.d.ts +8 -0
  53. package/dist/workflow-source-pins.js +14 -0
  54. package/dist/ws/index.d.ts +1 -1
  55. package/dist/ws/index.js +1 -1
  56. package/dist/ws/pending-tracker.d.ts +93 -0
  57. package/dist/ws/pending-tracker.js +132 -0
  58. package/dist/ws/sidecar-events.d.ts +43 -29
  59. package/dist/ws/sidecar-events.js +0 -2
  60. package/dist/ws/sidecar-handler.d.ts +122 -85
  61. package/dist/ws/sidecar-handler.js +925 -878
  62. package/dist/ws/sidecar-handler.test-helpers.d.ts +38 -0
  63. package/dist/ws/sidecar-handler.test-helpers.js +95 -0
  64. package/dist/ws/sidecar-token-authenticator.js +37 -23
  65. package/package.json +13 -13
  66. package/dist/sidecar-allocation/placement-policy.d.ts +0 -11
  67. 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 === undefined)
28
- return null;
29
- if (allocation.status !== "provisioning" &&
30
- allocation.status !== "allocated") {
31
- return null;
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 anchor = await db.query.workflowRun.findFirst({
34
- columns: { address: true },
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 (anchor?.address === null || anchor?.address === undefined)
48
+ if (probe === undefined ||
49
+ (probe.status !== "provisioning" && probe.status !== "probing")) {
38
50
  return null;
51
+ }
39
52
  return {
40
- kind: "allocated",
53
+ kind: "probe",
41
54
  sidecarId: row.id,
42
- allocationId: allocation.id,
43
- tenantId: allocation.tenantId,
44
- anchorRunId: allocation.anchorRunId,
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 === "shared") {
51
- const allocation = await db.query.sidecarAllocation.findFirst({
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(sidecarAllocation.sidecarId, identity.sidecarId),
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 allocation === undefined;
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.3.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.3.0",
21
- "@intx/crypto": "0.3.0",
22
- "@intx/db": "0.3.0",
23
- "@intx/hub-common": "0.3.0",
24
- "@intx/log": "0.3.0",
25
- "@intx/mime": "0.3.0",
26
- "@intx/pack-transport": "0.3.0",
27
- "@intx/storage-isogit": "0.3.0",
28
- "@intx/tool-packaging": "0.3.0",
29
- "@intx/types": "0.3.0",
30
- "@intx/workflow": "0.3.0",
31
- "@intx/workflow-deploy": "0.3.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
- }