@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,53 @@
|
|
|
1
|
+
import { type Type } from "arktype";
|
|
2
|
+
import type { Principal, RepoId, RepoStore } from "./types.js";
|
|
3
|
+
export type SubscribeKindOpts = {
|
|
4
|
+
signal: AbortSignal;
|
|
5
|
+
from: "head" | {
|
|
6
|
+
seq: number;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Workflow-event kinds to surface. Matches against the inner
|
|
10
|
+
* payload's `type` field after the validator narrows the blob. The
|
|
11
|
+
* helper yields only events whose `type` appears in this list.
|
|
12
|
+
*/
|
|
13
|
+
kinds: readonly string[];
|
|
14
|
+
bufferLimit?: number;
|
|
15
|
+
};
|
|
16
|
+
export type SubscribeKindEntry<T> = {
|
|
17
|
+
/**
|
|
18
|
+
* Workflow-event seq parsed from the committed filename
|
|
19
|
+
* (`runs/<runId>/events/<seq>.json`). Distinct from the substrate's
|
|
20
|
+
* commit-level seq exposed by `RepoStore.subscribe`.
|
|
21
|
+
*/
|
|
22
|
+
seq: number;
|
|
23
|
+
/**
|
|
24
|
+
* Owning runId, parsed from the committed blob's path. The substrate
|
|
25
|
+
* stores each run's event log under `runs/<runId>/events/`; the helper
|
|
26
|
+
* surfaces the path segment so consumers can attribute each yielded
|
|
27
|
+
* event to its run without re-walking the tree. Required by the
|
|
28
|
+
* workflow-host scheduler's live-ingest path, which routes incoming
|
|
29
|
+
* `TimerSet` events to per-run queue entries.
|
|
30
|
+
*/
|
|
31
|
+
runId: string;
|
|
32
|
+
event: T;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Typed entrypoint over `RepoStore.subscribe` for the workflow-run
|
|
36
|
+
* event log. The substrate emits one ref-update envelope per commit;
|
|
37
|
+
* each commit may add one or more `runs/<runId>/events/<seq>.json`
|
|
38
|
+
* blobs. This helper loads those blobs from the new commit's tree,
|
|
39
|
+
* narrows each through the supplied arktype validator, applies the
|
|
40
|
+
* kinds filter against the inner `type` discriminator, and yields one
|
|
41
|
+
* `{ seq, event }` entry per matching blob.
|
|
42
|
+
*
|
|
43
|
+
* The path layout is owned here, not in the substrate: the substrate
|
|
44
|
+
* `subscribe` is a generic ref-tail primitive and knows nothing about
|
|
45
|
+
* workflow-runs.
|
|
46
|
+
*
|
|
47
|
+
* Diff scope: the helper enumerates event blobs present in the new
|
|
48
|
+
* commit but absent from the old commit. A commit that does not add
|
|
49
|
+
* any event blobs (e.g. a kind-handler-internal rewrite) produces no
|
|
50
|
+
* entries even if the kinds filter would otherwise match earlier
|
|
51
|
+
* events.
|
|
52
|
+
*/
|
|
53
|
+
export declare function subscribeKind<V extends Type>(store: RepoStore, principal: Principal, repoId: RepoId, ref: string, validator: V, opts: SubscribeKindOpts): AsyncGenerator<SubscribeKindEntry<V["infer"]>, void, void>;
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import git from "isomorphic-git";
|
|
3
|
+
import { type } from "arktype";
|
|
4
|
+
import { hasCode } from "@intx/types";
|
|
5
|
+
/**
|
|
6
|
+
* Path-layout constants for the workflow-run repo's event log. The
|
|
7
|
+
* workflow-run kind handler commits one JSON blob per event under
|
|
8
|
+
* `runs/<runId>/events/<seq>.json` (the layout decided in the
|
|
9
|
+
* pre-build interface decisions for the workflow-run repo). The
|
|
10
|
+
* `subscribeKind` helper owns this layout so the substrate `subscribe`
|
|
11
|
+
* stays generic and free of workflow-event vocabulary.
|
|
12
|
+
*/
|
|
13
|
+
const RUNS_PREFIX = "runs";
|
|
14
|
+
const EVENTS_DIR = "events";
|
|
15
|
+
/**
|
|
16
|
+
* Substrate envelope shape. Imported textually rather than from
|
|
17
|
+
* `types.ts` so this helper has a local, validator-checked view of
|
|
18
|
+
* what `RepoStore.subscribe` yields and does not depend on the
|
|
19
|
+
* substrate's TypeScript type for runtime safety.
|
|
20
|
+
*/
|
|
21
|
+
const SubstrateEvent = type({
|
|
22
|
+
type: "'ref.updated'",
|
|
23
|
+
ref: "string",
|
|
24
|
+
"oldSha?": "string | null",
|
|
25
|
+
newSha: "string",
|
|
26
|
+
});
|
|
27
|
+
/**
|
|
28
|
+
* Per-event filename shape. Filenames are `<seq>.json` under
|
|
29
|
+
* `runs/<runId>/events/`. `<seq>` is a non-negative decimal integer.
|
|
30
|
+
*/
|
|
31
|
+
const EVENT_FILENAME_RE = /^(0|[1-9][0-9]*)\.json$/;
|
|
32
|
+
const KindEnvelope = type({
|
|
33
|
+
type: "string",
|
|
34
|
+
});
|
|
35
|
+
/**
|
|
36
|
+
* Typed entrypoint over `RepoStore.subscribe` for the workflow-run
|
|
37
|
+
* event log. The substrate emits one ref-update envelope per commit;
|
|
38
|
+
* each commit may add one or more `runs/<runId>/events/<seq>.json`
|
|
39
|
+
* blobs. This helper loads those blobs from the new commit's tree,
|
|
40
|
+
* narrows each through the supplied arktype validator, applies the
|
|
41
|
+
* kinds filter against the inner `type` discriminator, and yields one
|
|
42
|
+
* `{ seq, event }` entry per matching blob.
|
|
43
|
+
*
|
|
44
|
+
* The path layout is owned here, not in the substrate: the substrate
|
|
45
|
+
* `subscribe` is a generic ref-tail primitive and knows nothing about
|
|
46
|
+
* workflow-runs.
|
|
47
|
+
*
|
|
48
|
+
* Diff scope: the helper enumerates event blobs present in the new
|
|
49
|
+
* commit but absent from the old commit. A commit that does not add
|
|
50
|
+
* any event blobs (e.g. a kind-handler-internal rewrite) produces no
|
|
51
|
+
* entries even if the kinds filter would otherwise match earlier
|
|
52
|
+
* events.
|
|
53
|
+
*/
|
|
54
|
+
export async function* subscribeKind(store, principal, repoId, ref, validator, opts) {
|
|
55
|
+
const kindsAllowed = new Set(opts.kinds);
|
|
56
|
+
const dir = store.getRepoDir(repoId);
|
|
57
|
+
const subscribeOpts = {
|
|
58
|
+
signal: opts.signal,
|
|
59
|
+
from: opts.from,
|
|
60
|
+
};
|
|
61
|
+
if (opts.bufferLimit !== undefined) {
|
|
62
|
+
subscribeOpts.bufferLimit = opts.bufferLimit;
|
|
63
|
+
}
|
|
64
|
+
const iter = store.subscribe(principal, repoId, ref, subscribeOpts);
|
|
65
|
+
for await (const { event } of iter) {
|
|
66
|
+
const envelope = SubstrateEvent(event);
|
|
67
|
+
if (envelope instanceof type.errors) {
|
|
68
|
+
throw new Error(`subscribe_kind_envelope_invalid: ${envelope.summary}`);
|
|
69
|
+
}
|
|
70
|
+
const candidates = await collectAddedEventBlobs(dir, envelope.oldSha ?? null, envelope.newSha);
|
|
71
|
+
for (const candidate of candidates) {
|
|
72
|
+
const raw = await readBlobAtCommit(dir, envelope.newSha, candidate.blobPath);
|
|
73
|
+
let parsedJson;
|
|
74
|
+
try {
|
|
75
|
+
parsedJson = JSON.parse(new TextDecoder().decode(raw));
|
|
76
|
+
}
|
|
77
|
+
catch (cause) {
|
|
78
|
+
throw new Error(`subscribe_kind_invalid_json: ${candidate.blobPath}`, {
|
|
79
|
+
cause,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
const kind = KindEnvelope(parsedJson);
|
|
83
|
+
if (kind instanceof type.errors) {
|
|
84
|
+
throw new Error(`subscribe_kind_missing_type: ${candidate.blobPath}: ${kind.summary}`);
|
|
85
|
+
}
|
|
86
|
+
if (!kindsAllowed.has(kind.type))
|
|
87
|
+
continue;
|
|
88
|
+
const narrowed = validator(parsedJson);
|
|
89
|
+
if (narrowed instanceof type.errors) {
|
|
90
|
+
throw new Error(`subscribe_kind_validation_failed: ${candidate.blobPath}: ${narrowed.summary}`);
|
|
91
|
+
}
|
|
92
|
+
yield { seq: candidate.seq, runId: candidate.runId, event: narrowed };
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Walk the `runs/<runId>/events/` subtree at `newSha` and at `oldSha`
|
|
98
|
+
* (when present) and return the set of event candidates present in
|
|
99
|
+
* the new commit but not in the old. Filenames that fail the
|
|
100
|
+
* `<seq>.json` shape are surfaced as errors rather than silently
|
|
101
|
+
* skipped — the workflow-run kind handler's validatePush is the
|
|
102
|
+
* authority for what may land under this prefix, so an unexpected
|
|
103
|
+
* shape here is a substrate-side invariant violation.
|
|
104
|
+
*/
|
|
105
|
+
async function collectAddedEventBlobs(dir, oldSha, newSha) {
|
|
106
|
+
const newSet = await enumerateEventBlobs(dir, newSha);
|
|
107
|
+
if (oldSha === null) {
|
|
108
|
+
return [...newSet.values()].sort(byRunThenSeq);
|
|
109
|
+
}
|
|
110
|
+
const oldSet = await enumerateEventBlobs(dir, oldSha);
|
|
111
|
+
const out = [];
|
|
112
|
+
for (const [key, candidate] of newSet) {
|
|
113
|
+
if (oldSet.has(key))
|
|
114
|
+
continue;
|
|
115
|
+
out.push(candidate);
|
|
116
|
+
}
|
|
117
|
+
out.sort(byRunThenSeq);
|
|
118
|
+
return out;
|
|
119
|
+
}
|
|
120
|
+
function byRunThenSeq(a, b) {
|
|
121
|
+
if (a.runId < b.runId)
|
|
122
|
+
return -1;
|
|
123
|
+
if (a.runId > b.runId)
|
|
124
|
+
return 1;
|
|
125
|
+
return a.seq - b.seq;
|
|
126
|
+
}
|
|
127
|
+
async function enumerateEventBlobs(dir, commitOid) {
|
|
128
|
+
const out = new Map();
|
|
129
|
+
let commit;
|
|
130
|
+
try {
|
|
131
|
+
commit = await git.readCommit({ fs, dir, oid: commitOid });
|
|
132
|
+
}
|
|
133
|
+
catch (err) {
|
|
134
|
+
if (hasCode(err) && err.code === "NotFoundError")
|
|
135
|
+
return out;
|
|
136
|
+
throw err;
|
|
137
|
+
}
|
|
138
|
+
const runsOid = await lookupSubtree(dir, commit.commit.tree, RUNS_PREFIX);
|
|
139
|
+
if (runsOid === null)
|
|
140
|
+
return out;
|
|
141
|
+
const { tree: runs } = await git.readTree({ fs, dir, oid: runsOid });
|
|
142
|
+
for (const runEntry of runs) {
|
|
143
|
+
if (runEntry.type !== "tree")
|
|
144
|
+
continue;
|
|
145
|
+
const runId = runEntry.path;
|
|
146
|
+
const eventsOid = await lookupSubtree(dir, runEntry.oid, EVENTS_DIR);
|
|
147
|
+
if (eventsOid === null)
|
|
148
|
+
continue;
|
|
149
|
+
const { tree: events } = await git.readTree({ fs, dir, oid: eventsOid });
|
|
150
|
+
for (const blob of events) {
|
|
151
|
+
if (blob.type !== "blob")
|
|
152
|
+
continue;
|
|
153
|
+
const match = EVENT_FILENAME_RE.exec(blob.path);
|
|
154
|
+
if (match === null) {
|
|
155
|
+
throw new Error(`subscribe_kind_unexpected_event_filename: ${RUNS_PREFIX}/${runId}/${EVENTS_DIR}/${blob.path}`);
|
|
156
|
+
}
|
|
157
|
+
const seqStr = match[1];
|
|
158
|
+
if (seqStr === undefined)
|
|
159
|
+
throw new Error("unreachable");
|
|
160
|
+
const seq = Number.parseInt(seqStr, 10);
|
|
161
|
+
const blobPath = `${RUNS_PREFIX}/${runId}/${EVENTS_DIR}/${blob.path}`;
|
|
162
|
+
out.set(`${runId}/${blob.path}`, { seq, runId, blobPath });
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return out;
|
|
166
|
+
}
|
|
167
|
+
async function lookupSubtree(dir, parentTreeOid, name) {
|
|
168
|
+
const { tree } = await git.readTree({ fs, dir, oid: parentTreeOid });
|
|
169
|
+
const entry = tree.find((e) => e.path === name);
|
|
170
|
+
if (entry === undefined)
|
|
171
|
+
return null;
|
|
172
|
+
if (entry.type !== "tree")
|
|
173
|
+
return null;
|
|
174
|
+
return entry.oid;
|
|
175
|
+
}
|
|
176
|
+
async function readBlobAtCommit(dir, commitOid, filepath) {
|
|
177
|
+
const { blob } = await git.readBlob({ fs, dir, oid: commitOid, filepath });
|
|
178
|
+
return blob;
|
|
179
|
+
}
|