@cotal-ai/core 0.1.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 (50) hide show
  1. package/LICENSE +202 -0
  2. package/dist/agent-file.d.ts +24 -0
  3. package/dist/agent-file.d.ts.map +1 -0
  4. package/dist/agent-file.js +106 -0
  5. package/dist/agent-file.js.map +1 -0
  6. package/dist/command.d.ts +15 -0
  7. package/dist/command.d.ts.map +1 -0
  8. package/dist/command.js +2 -0
  9. package/dist/command.js.map +1 -0
  10. package/dist/connector.d.ts +43 -0
  11. package/dist/connector.d.ts.map +1 -0
  12. package/dist/connector.js +2 -0
  13. package/dist/connector.js.map +1 -0
  14. package/dist/endpoint.d.ts +193 -0
  15. package/dist/endpoint.d.ts.map +1 -0
  16. package/dist/endpoint.js +628 -0
  17. package/dist/endpoint.js.map +1 -0
  18. package/dist/identity.d.ts +26 -0
  19. package/dist/identity.d.ts.map +1 -0
  20. package/dist/identity.js +29 -0
  21. package/dist/identity.js.map +1 -0
  22. package/dist/index.d.ts +12 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +12 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/link.d.ts +37 -0
  27. package/dist/link.d.ts.map +1 -0
  28. package/dist/link.js +39 -0
  29. package/dist/link.js.map +1 -0
  30. package/dist/provision.d.ts +68 -0
  31. package/dist/provision.d.ts.map +1 -0
  32. package/dist/provision.js +204 -0
  33. package/dist/provision.js.map +1 -0
  34. package/dist/registry.d.ts +24 -0
  35. package/dist/registry.d.ts.map +1 -0
  36. package/dist/registry.js +26 -0
  37. package/dist/registry.js.map +1 -0
  38. package/dist/streams.d.ts +47 -0
  39. package/dist/streams.d.ts.map +1 -0
  40. package/dist/streams.js +97 -0
  41. package/dist/streams.js.map +1 -0
  42. package/dist/subjects.d.ts +83 -0
  43. package/dist/subjects.d.ts.map +1 -0
  44. package/dist/subjects.js +186 -0
  45. package/dist/subjects.js.map +1 -0
  46. package/dist/types.d.ts +117 -0
  47. package/dist/types.d.ts.map +1 -0
  48. package/dist/types.js +9 -0
  49. package/dist/types.js.map +1 -0
  50. package/package.json +31 -0
@@ -0,0 +1,97 @@
1
+ import { jetstreamManager, AckPolicy, DeliverPolicy, RetentionPolicy, DiscardPolicy, StorageType, } from "@nats-io/jetstream";
2
+ import { connect, credsAuthenticator, nanos } from "@nats-io/transport-node";
3
+ import { Kvm } from "@nats-io/kv";
4
+ import { spacePrefix, chatStream, dmStream, dmDurable, unicastSubject, taskStream, taskDurable, anycastSubject, presenceBucket, } from "./subjects.js";
5
+ /** Default presence-bucket entry TTL (ms) — matches the endpoint's default liveness window. */
6
+ const PRESENCE_TTL_MS = 6_000;
7
+ /**
8
+ * Create (idempotently) the three backing streams for a space — CHAT (multicast backlog +
9
+ * history), DM (per-instance inboxes), TASK (anycast work queue).
10
+ *
11
+ * This is **privileged**: under auth mode `STREAM.CREATE` is denied to regular agents
12
+ * (streams are space infrastructure, not per-agent), so it runs once at setup
13
+ * (`cotal up`) or from a permissive endpoint. The single source of the stream
14
+ * definitions, shared by the endpoint and the setup path so they can't diverge.
15
+ */
16
+ export async function createSpaceStreams(jsm, space) {
17
+ const p = spacePrefix(space);
18
+ await jsm.streams.add({
19
+ name: chatStream(space),
20
+ subjects: [`${p}.chat.>`],
21
+ retention: RetentionPolicy.Limits,
22
+ storage: StorageType.File,
23
+ max_msgs_per_subject: 1000, // capped per-channel backlog (buffer + history)
24
+ discard: DiscardPolicy.Old,
25
+ });
26
+ await jsm.streams.add({
27
+ name: dmStream(space),
28
+ subjects: [`${p}.inst.>`],
29
+ retention: RetentionPolicy.Limits,
30
+ storage: StorageType.File,
31
+ });
32
+ await jsm.streams.add({
33
+ name: taskStream(space),
34
+ subjects: [`${p}.svc.>`],
35
+ retention: RetentionPolicy.Workqueue,
36
+ storage: StorageType.File,
37
+ });
38
+ }
39
+ /**
40
+ * The DM inbox durable for an instance — ONE definition, used both by the privileged
41
+ * pre-create (manager/provisioner, auth mode) and the endpoint's open-mode self-create, so
42
+ * an idempotent re-add can never error on a config delta. The `filter_subject` binds the
43
+ * durable to inst.<id>.* — only the privileged creator sets it, which is the whole point:
44
+ * an agent can't create a durable filtered to someone else's inbox.
45
+ *
46
+ * `inactive_threshold` is set ONLY when the caller passes one — i.e. the open-mode
47
+ * self-create, where the agent owns the durable and a threshold cleanly retires its inbox
48
+ * after it departs. The privileged auth pre-create OMITS it: the agent BINDS-only and is
49
+ * denied CONSUMER.CREATE, so a threshold would retire the durable before a late/relaunched
50
+ * agent binds it, and the bind would then fail permanently ("consumer not found"). Persisting
51
+ * it is the price of bind-only; explicit cleanup on agent-stop is a follow-up.
52
+ */
53
+ export function dmDurableConfig(space, id, opts = {}) {
54
+ const cfg = {
55
+ durable_name: dmDurable(id),
56
+ filter_subject: unicastSubject(space, id, "*"),
57
+ ack_policy: AckPolicy.Explicit,
58
+ ack_wait: nanos(opts.ackWaitMs ?? 60_000),
59
+ deliver_policy: DeliverPolicy.All,
60
+ };
61
+ if (opts.inactiveThresholdMs)
62
+ cfg.inactive_threshold = nanos(opts.inactiveThresholdMs);
63
+ return cfg;
64
+ }
65
+ /**
66
+ * The TASK work-queue durable for a role — ONE definition, shared by the privileged
67
+ * pre-create (auth mode) and the endpoint's open-mode self-create. The durable is shared
68
+ * across all instances of a role (queue group); the privileged creator sets the
69
+ * filter_subject to svc.<role>.* so an agent can't bind a consumer filtered to another
70
+ * role's queue (the same create-time-filter attack surface as DM). Idempotent per role.
71
+ */
72
+ export function taskDurableConfig(space, role, opts = {}) {
73
+ return {
74
+ durable_name: taskDurable(role),
75
+ filter_subject: anycastSubject(space, role, "*"),
76
+ ack_policy: AckPolicy.Explicit,
77
+ ack_wait: nanos(opts.ackWaitMs ?? 60_000),
78
+ };
79
+ }
80
+ /** Connect with the given (privileged) creds, create the space's streams, and disconnect.
81
+ * Used by `cotal up` to pre-create streams once at setup. */
82
+ export async function setupSpaceStreams(opts) {
83
+ const nc = await connect({
84
+ servers: opts.servers,
85
+ authenticator: credsAuthenticator(new TextEncoder().encode(opts.creds)),
86
+ });
87
+ try {
88
+ await createSpaceStreams(await jetstreamManager(nc), opts.space);
89
+ // The presence KV bucket is a stream too — pre-create it so agents (denied KV
90
+ // stream-create) can open it. Idempotent.
91
+ await new Kvm(nc).create(presenceBucket(opts.space), { ttl: PRESENCE_TTL_MS });
92
+ }
93
+ finally {
94
+ await nc.drain();
95
+ }
96
+ }
97
+ //# sourceMappingURL=streams.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"streams.js","sourceRoot":"","sources":["../src/streams.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Subject naming — the routing half of the wire contract (v0).
3
+ *
4
+ * cotal.<space>.chat.<channel> multicast to a channel (dotted + hierarchical: team.backend, subscribe team.>)
5
+ * cotal.<space>.svc.<service> anycast to any one instance of a service (queue group)
6
+ * cotal.<space>.inst.<instance> unicast to one specific instance
7
+ * cotal.<space>.ctl.<service> control request/reply to a service (e.g. manager)
8
+ * cotal.<space>.trace.<instance> ambient lifecycle trace (later)
9
+ * cotal.<space>.control.<instance> control-plane commands (later)
10
+ *
11
+ * Presence lives in a JetStream KV bucket, not a subject (see presenceBucket()).
12
+ */
13
+ /** Make a string safe to use as a single NATS subject token. */
14
+ export declare function token(s: string): string;
15
+ export declare const ROOT = "cotal";
16
+ export declare function spacePrefix(space: string): string;
17
+ /** Canonicalize a `mentions` list for the wire: trim, lowercase, drop empties, dedupe.
18
+ * Returns `undefined` for an empty result so the field is omitted rather than sent as `[]`.
19
+ * Presence-agnostic (no roster lookup) — validation lives in the connector. */
20
+ export declare function normalizeMentions(mentions?: string[]): string[] | undefined;
21
+ export declare function chatSubject(space: string, sender: string, channel: string): string;
22
+ /** True if a channel names a concrete sub-channel (no `*`/`>`) — i.e. it can be
23
+ * *published* to. Subscriptions may be wildcard; publishes must be concrete. */
24
+ export declare function isConcreteChannel(channel: string): boolean;
25
+ /** Drop exact duplicates and any subject subsumed by a more-general one — JetStream
26
+ * rejects a consumer whose `filter_subjects` overlap, so `[team.>, team.backend]`
27
+ * must collapse to `[team.>]` before binding the chat consumer. */
28
+ export declare function collapseFilterSubjects(subjects: string[]): string[];
29
+ /** Unicast: a specific instance's inbox, tagged with the sender. (Either position may be
30
+ * `*` for subscribe/allow rules: `inst.<myId>.*` to receive, `inst.*.<myId>` to send as me.) */
31
+ export declare function unicastSubject(space: string, target: string, sender: string): string;
32
+ /** Anycast: a service (role), tagged with the sender. Subscribers join a queue group so one instance receives. */
33
+ export declare function anycastSubject(space: string, service: string, sender: string): string;
34
+ /** Control request/reply to a service (e.g. the manager), tagged with the sender; anycast via queue group. */
35
+ export declare function controlServiceSubject(space: string, service: string, sender: string): string;
36
+ export declare function traceSubject(space: string, agentId: string): string;
37
+ export declare function controlSubject(space: string, agentId: string): string;
38
+ /** Wildcard matching every subject within a space. */
39
+ export declare function spaceWildcard(space: string): string;
40
+ /** Wildcard matching every chat (multicast) subject in a space — the read surface an
41
+ * observer is allowed (DM/anycast stay confidential). */
42
+ export declare function chatWildcard(space: string): string;
43
+ /** The three peer-message delivery modes (control/trace/presence are not deliveries). */
44
+ export type DeliveryMode = "chat" | "anycast" | "unicast";
45
+ /** A subject parsed into its routing parts. `sender` is the publishing agent's id;
46
+ * `rest` is the channel (chat) or the routed target/role/service (inst/svc/ctl). */
47
+ export interface ParsedSubject {
48
+ kind: "chat" | "inst" | "svc" | "ctl";
49
+ sender: string;
50
+ /** chat → channel (possibly hierarchical); inst → target; svc → role; ctl → service. */
51
+ rest: string;
52
+ }
53
+ /**
54
+ * The single authority on the subject layout — every reader of a wire subject goes
55
+ * through this, so the sender-position asymmetry lives in exactly one place:
56
+ * chat.<sender>.<channel…> sender at [3], channel is everything after
57
+ * inst.<target>.<sender> sender at [4]
58
+ * svc.<role>.<sender> sender at [4]
59
+ * ctl.<service>.<sender> sender at [4]
60
+ * Validates the prefix and per-kind shape first and returns `null` on anything else,
61
+ * so a malformed subject can never be read as if it carried a sender.
62
+ */
63
+ export declare function parseSubject(subject: string): ParsedSubject | null;
64
+ /**
65
+ * Classify a subject's delivery mode, or `null` for control/trace/etc. A thin map over
66
+ * {@link parseSubject}. Observers (e.g. a feed) use this instead of re-parsing the layout.
67
+ */
68
+ export declare function deliveryOf(subject: string): DeliveryMode | null;
69
+ /** Name of the KV bucket holding presence for a space. */
70
+ export declare function presenceBucket(space: string): string;
71
+ /** Stream capturing `chat.>` — multicast backlog + history. */
72
+ export declare function chatStream(space: string): string;
73
+ /** Stream capturing `inst.>` — per-instance direct-message inboxes. */
74
+ export declare function dmStream(space: string): string;
75
+ /** Stream capturing `svc.>` — anycast work queue. */
76
+ export declare function taskStream(space: string): string;
77
+ /** Durable consumer name for an instance's view of the chat stream. */
78
+ export declare function chatDurable(instance: string): string;
79
+ /** Durable consumer name for an instance's private DM inbox. */
80
+ export declare function dmDurable(instance: string): string;
81
+ /** Durable consumer name (shared across instances of a role) for the task queue. */
82
+ export declare function taskDurable(service: string): string;
83
+ //# sourceMappingURL=subjects.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subjects.d.ts","sourceRoot":"","sources":["../src/subjects.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,gEAAgE;AAChE,wBAAgB,KAAK,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAGvC;AAED,eAAO,MAAM,IAAI,UAAU,CAAC;AAE5B,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED;;gFAEgF;AAChF,wBAAgB,iBAAiB,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,GAAG,SAAS,CAI3E;AAgCD,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAElF;AAED;iFACiF;AACjF,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAE1D;AAeD;;oEAEoE;AACpE,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAGnE;AAED;iGACiG;AACjG,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAEpF;AAED,kHAAkH;AAClH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAErF;AAED,8GAA8G;AAC9G,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAE5F;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAEnE;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAErE;AAED,sDAAsD;AACtD,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED;0DAC0D;AAC1D,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAElD;AAED,yFAAyF;AACzF,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,CAAC;AAE1D;qFACqF;AACrF,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,KAAK,GAAG,KAAK,CAAC;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,wFAAwF;IACxF,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAalE;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAI/D;AAED,0DAA0D;AAC1D,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEpD;AAID,+DAA+D;AAC/D,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED,uEAAuE;AACvE,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE9C;AAED,qDAAqD;AACrD,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED,uEAAuE;AACvE,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED,gEAAgE;AAChE,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAElD;AAED,oFAAoF;AACpF,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEnD"}
@@ -0,0 +1,186 @@
1
+ /**
2
+ * Subject naming — the routing half of the wire contract (v0).
3
+ *
4
+ * cotal.<space>.chat.<channel> multicast to a channel (dotted + hierarchical: team.backend, subscribe team.>)
5
+ * cotal.<space>.svc.<service> anycast to any one instance of a service (queue group)
6
+ * cotal.<space>.inst.<instance> unicast to one specific instance
7
+ * cotal.<space>.ctl.<service> control request/reply to a service (e.g. manager)
8
+ * cotal.<space>.trace.<instance> ambient lifecycle trace (later)
9
+ * cotal.<space>.control.<instance> control-plane commands (later)
10
+ *
11
+ * Presence lives in a JetStream KV bucket, not a subject (see presenceBucket()).
12
+ */
13
+ const ILLEGAL = /[^A-Za-z0-9_-]/g;
14
+ /** Make a string safe to use as a single NATS subject token. */
15
+ export function token(s) {
16
+ const t = s.trim().replace(ILLEGAL, "_");
17
+ return t.length > 0 ? t : "_";
18
+ }
19
+ export const ROOT = "cotal";
20
+ export function spacePrefix(space) {
21
+ return `${ROOT}.${token(space)}`;
22
+ }
23
+ /** Canonicalize a `mentions` list for the wire: trim, lowercase, drop empties, dedupe.
24
+ * Returns `undefined` for an empty result so the field is omitted rather than sent as `[]`.
25
+ * Presence-agnostic (no roster lookup) — validation lives in the connector. */
26
+ export function normalizeMentions(mentions) {
27
+ if (!mentions?.length)
28
+ return undefined;
29
+ const out = [...new Set(mentions.map((m) => m.trim().toLowerCase()).filter((m) => m.length > 0))];
30
+ return out.length ? out : undefined;
31
+ }
32
+ /**
33
+ * Build the channel portion of a chat subject, preserving NATS hierarchy: split on
34
+ * `.`, sanitize each segment to a safe token, but keep whole-segment wildcards
35
+ * (`*` = one level, `>` = the rest). So `team.backend` → `team.backend` (a
36
+ * sub-channel) and `team.>` → `team.>` (its whole subtree). `>` is only legal as
37
+ * the final segment; empty segments are dropped.
38
+ */
39
+ function channelPath(channel) {
40
+ const segs = channel.split(".").map((s) => s.trim()).filter((s) => s.length > 0);
41
+ if (segs.length === 0)
42
+ return "_";
43
+ return segs
44
+ .map((s, i) => {
45
+ if (s === ">") {
46
+ if (i !== segs.length - 1)
47
+ throw new Error(`channel "${channel}": '>' is only valid as the last segment`);
48
+ return ">";
49
+ }
50
+ return s === "*" ? "*" : token(s);
51
+ })
52
+ .join(".");
53
+ }
54
+ /** A routing token (sender, target, role, service), preserving the literal `*` wildcard
55
+ * used on the subscribe/allow side but sanitizing everything else. A no-op on real ids
56
+ * (nkey public keys are base32 [A-Z0-9]) and equal to `token()` on every concrete value —
57
+ * it only additionally lets `*` through, e.g. for `inst.*.<id>` / `svc.*.<id>` allow rules. */
58
+ function routeToken(s) {
59
+ return s === "*" ? "*" : token(s);
60
+ }
61
+ export function chatSubject(space, sender, channel) {
62
+ return `${spacePrefix(space)}.chat.${routeToken(sender)}.${channelPath(channel)}`;
63
+ }
64
+ /** True if a channel names a concrete sub-channel (no `*`/`>`) — i.e. it can be
65
+ * *published* to. Subscriptions may be wildcard; publishes must be concrete. */
66
+ export function isConcreteChannel(channel) {
67
+ return !channel.split(".").some((s) => s.trim() === "*" || s.trim() === ">");
68
+ }
69
+ /** Does NATS subject `pattern` (with `*`/`>`) match `subject`? */
70
+ function subjectMatches(pattern, subject) {
71
+ const p = pattern.split(".");
72
+ const s = subject.split(".");
73
+ for (let i = 0; i < p.length; i++) {
74
+ if (p[i] === ">")
75
+ return true; // matches all remaining tokens
76
+ if (i >= s.length)
77
+ return false;
78
+ if (p[i] === "*")
79
+ continue;
80
+ if (p[i] !== s[i])
81
+ return false;
82
+ }
83
+ return p.length === s.length;
84
+ }
85
+ /** Drop exact duplicates and any subject subsumed by a more-general one — JetStream
86
+ * rejects a consumer whose `filter_subjects` overlap, so `[team.>, team.backend]`
87
+ * must collapse to `[team.>]` before binding the chat consumer. */
88
+ export function collapseFilterSubjects(subjects) {
89
+ const uniq = [...new Set(subjects)];
90
+ return uniq.filter((x) => !uniq.some((y) => y !== x && subjectMatches(y, x)));
91
+ }
92
+ /** Unicast: a specific instance's inbox, tagged with the sender. (Either position may be
93
+ * `*` for subscribe/allow rules: `inst.<myId>.*` to receive, `inst.*.<myId>` to send as me.) */
94
+ export function unicastSubject(space, target, sender) {
95
+ return `${spacePrefix(space)}.inst.${routeToken(target)}.${routeToken(sender)}`;
96
+ }
97
+ /** Anycast: a service (role), tagged with the sender. Subscribers join a queue group so one instance receives. */
98
+ export function anycastSubject(space, service, sender) {
99
+ return `${spacePrefix(space)}.svc.${routeToken(service)}.${routeToken(sender)}`;
100
+ }
101
+ /** Control request/reply to a service (e.g. the manager), tagged with the sender; anycast via queue group. */
102
+ export function controlServiceSubject(space, service, sender) {
103
+ return `${spacePrefix(space)}.ctl.${routeToken(service)}.${routeToken(sender)}`;
104
+ }
105
+ export function traceSubject(space, agentId) {
106
+ return `${spacePrefix(space)}.trace.${token(agentId)}`;
107
+ }
108
+ export function controlSubject(space, agentId) {
109
+ return `${spacePrefix(space)}.control.${token(agentId)}`;
110
+ }
111
+ /** Wildcard matching every subject within a space. */
112
+ export function spaceWildcard(space) {
113
+ return `${spacePrefix(space)}.>`;
114
+ }
115
+ /** Wildcard matching every chat (multicast) subject in a space — the read surface an
116
+ * observer is allowed (DM/anycast stay confidential). */
117
+ export function chatWildcard(space) {
118
+ return `${spacePrefix(space)}.chat.>`;
119
+ }
120
+ /**
121
+ * The single authority on the subject layout — every reader of a wire subject goes
122
+ * through this, so the sender-position asymmetry lives in exactly one place:
123
+ * chat.<sender>.<channel…> sender at [3], channel is everything after
124
+ * inst.<target>.<sender> sender at [4]
125
+ * svc.<role>.<sender> sender at [4]
126
+ * ctl.<service>.<sender> sender at [4]
127
+ * Validates the prefix and per-kind shape first and returns `null` on anything else,
128
+ * so a malformed subject can never be read as if it carried a sender.
129
+ */
130
+ export function parseSubject(subject) {
131
+ const parts = subject.split(".");
132
+ if (parts[0] !== ROOT)
133
+ return null; // cotal.<space>.<kind>.…
134
+ const kind = parts[2];
135
+ if (kind === "chat") {
136
+ if (parts.length < 5)
137
+ return null; // cotal.<space>.chat.<sender>.<channel…>
138
+ return { kind, sender: parts[3], rest: parts.slice(4).join(".") };
139
+ }
140
+ if (kind === "inst" || kind === "svc" || kind === "ctl") {
141
+ if (parts.length !== 5)
142
+ return null; // cotal.<space>.<kind>.<route>.<sender>
143
+ return { kind, sender: parts[4], rest: parts[3] };
144
+ }
145
+ return null;
146
+ }
147
+ /**
148
+ * Classify a subject's delivery mode, or `null` for control/trace/etc. A thin map over
149
+ * {@link parseSubject}. Observers (e.g. a feed) use this instead of re-parsing the layout.
150
+ */
151
+ export function deliveryOf(subject) {
152
+ const p = parseSubject(subject);
153
+ if (!p)
154
+ return null;
155
+ return p.kind === "chat" ? "chat" : p.kind === "svc" ? "anycast" : p.kind === "inst" ? "unicast" : null;
156
+ }
157
+ /** Name of the KV bucket holding presence for a space. */
158
+ export function presenceBucket(space) {
159
+ return `cotal_presence_${token(space)}`;
160
+ }
161
+ // ---- JetStream streams (the durable backing for the three delivery modes) ----
162
+ /** Stream capturing `chat.>` — multicast backlog + history. */
163
+ export function chatStream(space) {
164
+ return `CHAT_${token(space)}`;
165
+ }
166
+ /** Stream capturing `inst.>` — per-instance direct-message inboxes. */
167
+ export function dmStream(space) {
168
+ return `DM_${token(space)}`;
169
+ }
170
+ /** Stream capturing `svc.>` — anycast work queue. */
171
+ export function taskStream(space) {
172
+ return `TASK_${token(space)}`;
173
+ }
174
+ /** Durable consumer name for an instance's view of the chat stream. */
175
+ export function chatDurable(instance) {
176
+ return `chat_${token(instance)}`;
177
+ }
178
+ /** Durable consumer name for an instance's private DM inbox. */
179
+ export function dmDurable(instance) {
180
+ return `dm_${token(instance)}`;
181
+ }
182
+ /** Durable consumer name (shared across instances of a role) for the task queue. */
183
+ export function taskDurable(service) {
184
+ return `svc_${token(service)}`;
185
+ }
186
+ //# sourceMappingURL=subjects.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subjects.js","sourceRoot":"","sources":["../src/subjects.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Cotal wire types (v0).
3
+ *
4
+ * These are the shapes that travel on the mesh. They are intentionally A2A-inspired
5
+ * (AgentCard / Message / Part) but transport-agnostic. This file IS part of the
6
+ * "wire contract" — treat changes here as protocol changes.
7
+ */
8
+ export type EndpointKind = "agent" | "endpoint";
9
+ export interface AgentSkill {
10
+ id: string;
11
+ name: string;
12
+ description?: string;
13
+ }
14
+ /** A2A-inspired identity record for an endpoint or agent. */
15
+ export interface AgentCard {
16
+ /** Unique, stable for the lifetime of this connection. */
17
+ id: string;
18
+ /** Human-readable display name. */
19
+ name: string;
20
+ /** 'agent' (participates in coordination) or a plain 'endpoint' (logger, dashboard…). */
21
+ kind: EndpointKind;
22
+ /** Cotal addition: the role this participant plays (planner, reviewer, …). */
23
+ role?: string;
24
+ /** A2A-style one-line summary of what this agent does (discovery / observability). */
25
+ description?: string;
26
+ /** Cotal: free-form "what it can do" tags (A2A skill-tags, flattened) — discovery only. */
27
+ tags?: string[];
28
+ skills?: AgentSkill[];
29
+ meta?: Record<string, unknown>;
30
+ }
31
+ /**
32
+ * Lifecycle status of a participant.
33
+ * - `idle`: connected, no active task
34
+ * - `waiting`: blocked — awaiting input, approval, or a peer
35
+ * - `working`: actively executing a task / in a turn
36
+ * - `offline`: disconnected or heartbeat lapsed (derived by observers, not self-set while live)
37
+ */
38
+ export type PresenceStatus = "idle" | "waiting" | "working" | "offline";
39
+ /** Live presence record. Stored in the space's KV bucket under key = card.id. */
40
+ export interface Presence {
41
+ card: AgentCard;
42
+ status: PresenceStatus;
43
+ /** Freeform "what I'm doing right now". */
44
+ activity?: string;
45
+ /** Epoch ms of the last heartbeat. */
46
+ ts: number;
47
+ }
48
+ export type Part = {
49
+ kind: "text";
50
+ text: string;
51
+ } | {
52
+ kind: "data";
53
+ data: unknown;
54
+ };
55
+ export interface EndpointRef {
56
+ id: string;
57
+ name: string;
58
+ role?: string;
59
+ }
60
+ /** A message on the mesh (chat / direct message for now; extensible to other families). */
61
+ export interface CotalMessage {
62
+ /** Unique message id. */
63
+ id: string;
64
+ /** Epoch ms. */
65
+ ts: number;
66
+ space: string;
67
+ from: EndpointRef;
68
+ /** Channel name — multicast (broadcast to everyone on the channel). */
69
+ channel?: string;
70
+ /** Instance id — unicast (direct to one specific endpoint). */
71
+ to?: string;
72
+ /** Service / role — anycast (any one instance of the service receives it). */
73
+ toService?: string;
74
+ /** Lowercased peer names called out within a `channel` message — a priority/wake hint,
75
+ * not a routing target: the message still multicasts to the whole channel. Omitted when
76
+ * empty. */
77
+ mentions?: string[];
78
+ parts: Part[];
79
+ /** Id of the message being replied to. */
80
+ replyTo?: string;
81
+ /** Conversation / thread correlation id. */
82
+ contextId?: string;
83
+ }
84
+ export type PresenceEvent = {
85
+ type: "join";
86
+ presence: Presence;
87
+ } | {
88
+ type: "update";
89
+ presence: Presence;
90
+ } | {
91
+ type: "offline";
92
+ presence: Presence;
93
+ };
94
+ /**
95
+ * Delivery control handed to "message" listeners alongside each {@link CotalMessage}.
96
+ * The message stays on its JetStream stream until {@link Delivery.ack} is called — so
97
+ * ack ONLY once the message has actually been surfaced (printed, injected, handled).
98
+ * A crash before ack redelivers it.
99
+ */
100
+ export interface Delivery {
101
+ /** Mark the message handled; advances this reader's bookmark so it won't redeliver. */
102
+ ack(): void;
103
+ /** Decline for now; the message redelivers (e.g. couldn't surface it yet). */
104
+ nak(): void;
105
+ }
106
+ /** Control-plane request/reply (e.g. CLI → manager). */
107
+ export interface ControlRequest {
108
+ op: string;
109
+ args?: Record<string, unknown>;
110
+ from?: EndpointRef;
111
+ }
112
+ export interface ControlReply {
113
+ ok: boolean;
114
+ data?: unknown;
115
+ error?: string;
116
+ }
117
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Cotal wire types (v0).
3
+ *
4
+ * These are the shapes that travel on the mesh. They are intentionally A2A-inspired
5
+ * (AgentCard / Message / Part) but transport-agnostic. This file IS part of the
6
+ * "wire contract" — treat changes here as protocol changes.
7
+ */
8
+ export {};
9
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "@cotal-ai/core",
3
+ "version": "0.1.0",
4
+ "license": "Apache-2.0",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ }
13
+ },
14
+ "dependencies": {
15
+ "@nats-io/jetstream": "^3.4.0",
16
+ "@nats-io/jwt": "0.0.10-5",
17
+ "@nats-io/kv": "^3.4.0",
18
+ "@nats-io/nkeys": "^2.0.3",
19
+ "@nats-io/transport-node": "^3.4.0"
20
+ },
21
+ "files": [
22
+ "dist"
23
+ ],
24
+ "publishConfig": {
25
+ "access": "public"
26
+ },
27
+ "scripts": {
28
+ "typecheck": "tsc -p tsconfig.json --noEmit",
29
+ "build": "tsc -p tsconfig.json"
30
+ }
31
+ }