@neta-art/cohub-protocol 1.0.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/README.md ADDED
@@ -0,0 +1,30 @@
1
+ # @neta-art/cohub-protocol
2
+
3
+ Shared protocol definitions for the Cohub agent collaboration platform.
4
+
5
+ This package provides the stable type surface used across Cohub services and client SDKs — covering spaces, checkpoints, sessions, realtime events, gateway contracts, tasks, and filesystem operations.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install @neta-art/cohub-protocol
11
+ ```
12
+
13
+ ## Subpath exports
14
+
15
+ | Subpath | Contents |
16
+ |---|---|
17
+ | `@neta-art/cohub-protocol/core` | Low-level primitives — content blocks, usage, shared types |
18
+ | `@neta-art/cohub-protocol/model` | Business records and input models — sessions, spaces, checkpoints |
19
+ | `@neta-art/cohub-protocol/realtime` | WebSocket and streaming event protocol definitions |
20
+ | `@neta-art/cohub-protocol/gateway` | Stable gateway contracts for external channels |
21
+ | `@neta-art/cohub-protocol/task` | Task scheduler and worker contracts |
22
+ | `@neta-art/cohub-protocol/fs` | Space filesystem DTOs |
23
+
24
+ ## Usage
25
+
26
+ ```ts
27
+ import { type ContentBlock } from "@neta-art/cohub-protocol/core";
28
+ import { type SessionInput } from "@neta-art/cohub-protocol/model";
29
+ import { type RealtimeEvent } from "@neta-art/cohub-protocol/realtime";
30
+ ```
@@ -0,0 +1,39 @@
1
+ export type ContentBlockMeta = Record<string, unknown>;
2
+ export type ContentBlock = {
3
+ type: "text";
4
+ text: string;
5
+ _meta?: ContentBlockMeta;
6
+ } | {
7
+ type: "thinking";
8
+ thinking: string;
9
+ signature?: string;
10
+ _meta?: ContentBlockMeta;
11
+ } | {
12
+ type: "image";
13
+ source: {
14
+ type: "url";
15
+ url: string;
16
+ } | {
17
+ type: "base64";
18
+ media_type: string;
19
+ data: string;
20
+ };
21
+ _meta?: ContentBlockMeta;
22
+ } | {
23
+ type: "tool_use";
24
+ id: string;
25
+ name: string;
26
+ input: Record<string, unknown>;
27
+ _meta?: ContentBlockMeta;
28
+ } | {
29
+ type: "tool_result";
30
+ tool_use_id: string;
31
+ content: string | ContentBlock[];
32
+ is_error?: boolean;
33
+ _meta?: ContentBlockMeta;
34
+ } | {
35
+ type: "system_note";
36
+ note_type: "session_created" | "forked" | "compacted" | "info";
37
+ text: string;
38
+ _meta?: ContentBlockMeta;
39
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ export * from "./content.js";
2
+ export * from "./usage.js";
@@ -0,0 +1,2 @@
1
+ export * from "./content.js";
2
+ export * from "./usage.js";
@@ -0,0 +1,14 @@
1
+ export type Usage = {
2
+ input?: number;
3
+ output?: number;
4
+ cacheRead?: number;
5
+ cacheWrite?: number;
6
+ totalTokens?: number;
7
+ cost?: {
8
+ input?: number;
9
+ output?: number;
10
+ cacheRead?: number;
11
+ cacheWrite?: number;
12
+ total?: number;
13
+ } | null;
14
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,33 @@
1
+ export type SpaceFsEntry = {
2
+ name: string;
3
+ path: string;
4
+ type: "file" | "dir" | "symlink";
5
+ size: number;
6
+ mimeType: string | null;
7
+ mtimeMs: number;
8
+ };
9
+ export type SpaceFsTreeResponse = {
10
+ path: string;
11
+ entries: SpaceFsEntry[];
12
+ };
13
+ export type SpaceFsFileKind = "text" | "binary";
14
+ export type SpaceFsEncoding = "utf-8" | "base64";
15
+ export type SpaceFsFileResponse = {
16
+ path: string;
17
+ name: string;
18
+ size: number;
19
+ mimeType: string | null;
20
+ mtimeMs: number;
21
+ kind: SpaceFsFileKind;
22
+ encoding: SpaceFsEncoding;
23
+ content: string;
24
+ };
25
+ export type SpaceFsWriteFileInput = {
26
+ path: string;
27
+ content: string;
28
+ encoding: SpaceFsEncoding;
29
+ };
30
+ export type SpaceFsMoveInput = {
31
+ fromPath: string;
32
+ toPath: string;
33
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,114 @@
1
+ import type { ContentBlock } from "../core/content.js";
2
+ import type { MessageRecord } from "../model/session.js";
3
+ export type ChannelProvider = "web" | "websocket" | "discord" | "feishu" | "telegram" | "slack";
4
+ export interface DiscordChannelConfig {
5
+ inbound?: {
6
+ requireMentionInGuild?: boolean;
7
+ };
8
+ outbound?: {
9
+ showThinking?: boolean;
10
+ showToolCalls?: boolean;
11
+ };
12
+ }
13
+ export interface FeishuChannelConfig {
14
+ brand?: "feishu" | "lark";
15
+ inbound?: {
16
+ requireMentionInGroup?: boolean;
17
+ };
18
+ outbound?: {
19
+ renderMode?: "card" | "post";
20
+ showThinking?: boolean;
21
+ showToolCalls?: boolean;
22
+ };
23
+ }
24
+ export type ChannelConfig = DiscordChannelConfig | FeishuChannelConfig | Record<string, unknown>;
25
+ export interface GatewayInboundEvent {
26
+ eventId: string;
27
+ timestamp: number;
28
+ eventType?: "message_create" | "conversation_create";
29
+ channelId: string;
30
+ provider: ChannelProvider;
31
+ externalChatId: string;
32
+ externalMessageId: string;
33
+ bindingKey?: string;
34
+ conversation: {
35
+ id: string;
36
+ parentId?: string | null;
37
+ meta?: Record<string, unknown> | null;
38
+ };
39
+ message?: {
40
+ parentMessageId?: string | null;
41
+ meta?: Record<string, unknown> | null;
42
+ };
43
+ sender: {
44
+ id: string;
45
+ name?: string;
46
+ };
47
+ content: ContentBlock[];
48
+ meta?: Record<string, unknown> | null;
49
+ }
50
+ export interface GatewaySessionOutputBase {
51
+ type: "session.turn.progress" | "session.turn.final" | "session.turn.error" | "session.message.persisted";
52
+ spaceId: string;
53
+ sessionId: string;
54
+ }
55
+ export interface GatewaySessionTurnProgressOutput extends GatewaySessionOutputBase {
56
+ type: "session.turn.progress";
57
+ anchorUserMessageId: string | null;
58
+ content: ContentBlock[];
59
+ }
60
+ export interface GatewaySessionTurnFinalOutput extends GatewaySessionOutputBase {
61
+ type: "session.turn.final";
62
+ sessionMessageId: string | null;
63
+ anchorUserMessageId: string | null;
64
+ content: ContentBlock[];
65
+ }
66
+ export interface GatewaySessionTurnErrorOutput extends GatewaySessionOutputBase {
67
+ type: "session.turn.error";
68
+ anchorUserMessageId: string | null;
69
+ error: string;
70
+ }
71
+ export interface GatewaySessionMessagePersistedOutput extends GatewaySessionOutputBase {
72
+ type: "session.message.persisted";
73
+ message: MessageRecord;
74
+ }
75
+ export type GatewaySessionOutput = GatewaySessionTurnProgressOutput | GatewaySessionTurnFinalOutput | GatewaySessionTurnErrorOutput | GatewaySessionMessagePersistedOutput;
76
+ export interface GatewayOutboundCommand {
77
+ commandId: string;
78
+ timestamp: number;
79
+ channelId: string;
80
+ provider: ChannelProvider;
81
+ externalChatId: string;
82
+ content: ContentBlock[];
83
+ replyToExternalMessageId?: string;
84
+ spaceId?: string;
85
+ spaceSessionId?: string;
86
+ sessionMessageId?: string;
87
+ meta?: (Record<string, unknown> & {
88
+ sessionOutput?: GatewaySessionOutput | null;
89
+ }) | null;
90
+ }
91
+ export interface GatewayControlCommand {
92
+ action: "connect" | "disconnect" | "reload";
93
+ configs: {
94
+ channelId: string;
95
+ provider: ChannelProvider;
96
+ credentials: Record<string, unknown>;
97
+ }[];
98
+ }
99
+ export type GatewayLogDirection = "inbound" | "outbound";
100
+ export type GatewayLogStatus = "pending" | "success" | "failed";
101
+ export interface GatewayLogEvent {
102
+ logId: string;
103
+ timestamp: number;
104
+ direction: GatewayLogDirection;
105
+ provider: ChannelProvider;
106
+ channelId: string;
107
+ externalChatId: string;
108
+ externalMessageId?: string;
109
+ rawPayload: Record<string, unknown>;
110
+ normalizedPayload?: Record<string, unknown>;
111
+ status: GatewayLogStatus;
112
+ errorMessage?: string;
113
+ correlationId?: string;
114
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,136 @@
1
+ export type ChannelProvider = "web" | "websocket" | "discord" | "feishu" | "telegram" | "slack";
2
+ export declare function buildSessionSourceChannel(event: GatewayInboundEvent): string;
3
+ export interface DiscordChannelConfig {
4
+ inbound?: {
5
+ requireMentionInGuild?: boolean;
6
+ };
7
+ outbound?: {
8
+ showThinking?: boolean;
9
+ showToolCalls?: boolean;
10
+ };
11
+ }
12
+ export type ChannelConfig = DiscordChannelConfig | FeishuChannelConfig | Record<string, unknown>;
13
+ export interface FeishuChannelConfig {
14
+ brand?: "feishu" | "lark";
15
+ inbound?: {
16
+ requireMentionInGroup?: boolean;
17
+ };
18
+ outbound?: {
19
+ renderMode?: "card" | "post";
20
+ showThinking?: boolean;
21
+ showToolCalls?: boolean;
22
+ };
23
+ }
24
+ export interface GatewayInboundEvent {
25
+ eventId: string;
26
+ timestamp: number;
27
+ eventType?: "message_create" | "conversation_create";
28
+ channelId: string;
29
+ provider: ChannelProvider;
30
+ externalChatId: string;
31
+ externalMessageId: string;
32
+ bindingKey?: string;
33
+ conversation: {
34
+ id: string;
35
+ parentId?: string | null;
36
+ meta?: Record<string, unknown> | null;
37
+ };
38
+ message?: {
39
+ parentMessageId?: string | null;
40
+ meta?: Record<string, unknown> | null;
41
+ };
42
+ sender: {
43
+ id: string;
44
+ name?: string;
45
+ };
46
+ content: import("./session-ingestion.js").ContentBlock[];
47
+ meta?: Record<string, unknown> | null;
48
+ }
49
+ export interface GatewaySessionOutputBase {
50
+ type: "session.turn.progress" | "session.turn.final" | "session.turn.error" | "session.message.persisted";
51
+ spaceId: string;
52
+ sessionId: string;
53
+ }
54
+ export interface GatewaySessionTurnProgressOutput extends GatewaySessionOutputBase {
55
+ type: "session.turn.progress";
56
+ anchorUserMessageId: string | null;
57
+ content: import("./session-ingestion.js").ContentBlock[];
58
+ }
59
+ export interface GatewaySessionTurnFinalOutput extends GatewaySessionOutputBase {
60
+ type: "session.turn.final";
61
+ sessionMessageId: string | null;
62
+ anchorUserMessageId: string | null;
63
+ content: import("./session-ingestion.js").ContentBlock[];
64
+ }
65
+ export interface GatewaySessionTurnErrorOutput extends GatewaySessionOutputBase {
66
+ type: "session.turn.error";
67
+ anchorUserMessageId: string | null;
68
+ error: string;
69
+ }
70
+ export interface GatewaySessionMessagePersistedOutput extends GatewaySessionOutputBase {
71
+ type: "session.message.persisted";
72
+ message: import("./session-ingestion.js").MessageRecord;
73
+ }
74
+ export type GatewaySessionOutput = GatewaySessionTurnProgressOutput | GatewaySessionTurnFinalOutput | GatewaySessionTurnErrorOutput | GatewaySessionMessagePersistedOutput;
75
+ export interface DiscordDeliveryPlan {
76
+ adapter: "discord";
77
+ mode: "send" | "upsert";
78
+ primaryText: string;
79
+ continuationChunks: string[];
80
+ files: string[];
81
+ replyToExternalMessageId?: string;
82
+ turnAnchorMessageId?: string | null;
83
+ preferredEditExternalMessageId?: string | null;
84
+ }
85
+ export interface FeishuDeliveryPlan {
86
+ adapter: "feishu";
87
+ mode: "create_or_update";
88
+ renderMode: "card" | "post";
89
+ msgType: "interactive" | "post";
90
+ content: string;
91
+ imageKeys: string[];
92
+ replyToExternalMessageId?: string;
93
+ turnAnchorMessageId?: string | null;
94
+ preferredEditExternalMessageId?: string | null;
95
+ }
96
+ export type GatewayDeliveryPlan = DiscordDeliveryPlan | FeishuDeliveryPlan;
97
+ export interface GatewayOutboundCommand {
98
+ commandId: string;
99
+ timestamp: number;
100
+ channelId: string;
101
+ provider: ChannelProvider;
102
+ externalChatId: string;
103
+ content: import("./session-ingestion.js").ContentBlock[];
104
+ replyToExternalMessageId?: string;
105
+ spaceId?: string;
106
+ spaceSessionId?: string;
107
+ sessionMessageId?: string;
108
+ deliveryPlan?: GatewayDeliveryPlan | null;
109
+ meta?: (Record<string, unknown> & {
110
+ sessionOutput?: GatewaySessionOutput | null;
111
+ }) | null;
112
+ }
113
+ export interface GatewayControlCommand {
114
+ action: "connect" | "disconnect" | "reload";
115
+ configs: {
116
+ channelId: string;
117
+ provider: ChannelProvider;
118
+ credentials: Record<string, unknown>;
119
+ }[];
120
+ }
121
+ export type GatewayLogDirection = "inbound" | "outbound";
122
+ export type GatewayLogStatus = "pending" | "success" | "failed";
123
+ export interface GatewayLogEvent {
124
+ logId: string;
125
+ timestamp: number;
126
+ direction: GatewayLogDirection;
127
+ provider: ChannelProvider;
128
+ channelId: string;
129
+ externalChatId: string;
130
+ externalMessageId?: string;
131
+ rawPayload: Record<string, unknown>;
132
+ normalizedPayload?: Record<string, unknown>;
133
+ status: GatewayLogStatus;
134
+ errorMessage?: string;
135
+ correlationId?: string;
136
+ }
@@ -0,0 +1,48 @@
1
+ export function buildSessionSourceChannel(event) {
2
+ const provider = event.provider;
3
+ const meta = (event.conversation?.meta ?? event.meta ?? {});
4
+ switch (provider) {
5
+ case "discord":
6
+ return buildDiscordSourceChannel(event, meta);
7
+ case "feishu":
8
+ return buildFeishuSourceChannel(event, meta);
9
+ case "web":
10
+ return "web";
11
+ default:
12
+ return `${provider}:${event.conversation?.id?.trim() || event.externalChatId}`;
13
+ }
14
+ }
15
+ function buildDiscordSourceChannel(event, meta) {
16
+ const isDm = meta.isDm === true;
17
+ const guildName = typeof meta.guildName === "string" ? meta.guildName : null;
18
+ const channelName = typeof meta.channelName === "string" ? meta.channelName : null;
19
+ const parentChannelName = typeof meta.parentChannelName === "string" ? meta.parentChannelName : null;
20
+ const threadName = typeof meta.threadName === "string" ? meta.threadName : null;
21
+ const senderName = event.sender?.name ?? null;
22
+ if (isDm) {
23
+ return `discord:dm:${senderName ?? event.sender.id}`;
24
+ }
25
+ if (threadName && parentChannelName && guildName) {
26
+ return `discord:${guildName}:#${parentChannelName}>${threadName}`;
27
+ }
28
+ if (channelName && guildName) {
29
+ return `discord:${guildName}:#${channelName}`;
30
+ }
31
+ if (guildName) {
32
+ return `discord:${guildName}`;
33
+ }
34
+ return `discord:${event.conversation?.id?.trim() || event.externalChatId}`;
35
+ }
36
+ function buildFeishuSourceChannel(event, meta) {
37
+ const chatType = meta.chatType;
38
+ const chatName = typeof meta.chatName === "string" ? meta.chatName : null;
39
+ const senderName = event.sender?.name ?? null;
40
+ const isDm = chatType === "p2p";
41
+ if (isDm) {
42
+ return `feishu:dm:${senderName ?? event.sender.id}`;
43
+ }
44
+ if (chatName) {
45
+ return `feishu:group:${chatName}`;
46
+ }
47
+ return `feishu:${event.conversation?.id?.trim() || event.externalChatId}`;
48
+ }
@@ -0,0 +1,8 @@
1
+ export * from "./core/content.js";
2
+ export * from "./core/usage.js";
3
+ export * from "./model/session.js";
4
+ export * from "./realtime/stream.js";
5
+ export * from "./realtime/websocket.js";
6
+ export * from "./gateway/index.js";
7
+ export * from "./task/index.js";
8
+ export * from "./fs/index.js";
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ export * from "./core/content.js";
2
+ export * from "./core/usage.js";
3
+ export * from "./model/session.js";
4
+ export * from "./realtime/stream.js";
5
+ export * from "./realtime/websocket.js";
6
+ export * from "./gateway/index.js";
7
+ export * from "./task/index.js";
8
+ export * from "./fs/index.js";
@@ -0,0 +1,98 @@
1
+ import type { ContentBlock } from "../core/content.js";
2
+ import type { Usage } from "../core/usage.js";
3
+ export type SessionPromptInput = {
4
+ spaceId: string;
5
+ sessionId: string;
6
+ userMessageId?: string | null;
7
+ message: {
8
+ content: ContentBlock[];
9
+ };
10
+ meta?: {
11
+ source?: string;
12
+ intent?: "auto" | "continue" | "new_session" | "fork";
13
+ model?: string;
14
+ provider?: string;
15
+ } | null;
16
+ };
17
+ export type RegisterSessionInput = {
18
+ spaceId: string;
19
+ sessionId: string;
20
+ title?: string | null;
21
+ source?: string | null;
22
+ externalSessionId?: string | null;
23
+ meta?: Record<string, unknown> | null;
24
+ };
25
+ export type PersistMessageInput = {
26
+ spaceId: string;
27
+ sessionId: string;
28
+ previousMessageId?: string | null;
29
+ anchorUserMessageId?: string | null;
30
+ idempotencyKey: string;
31
+ message: {
32
+ role?: "user" | "assistant" | "system";
33
+ externalMessageId?: string | null;
34
+ protocolMessageId?: string | null;
35
+ content: ContentBlock[];
36
+ text?: string | null;
37
+ provider?: string | null;
38
+ model?: string | null;
39
+ stopReason?: string | null;
40
+ errorMessage?: string | null;
41
+ meta?: Record<string, unknown> | null;
42
+ usage?: Usage | null;
43
+ };
44
+ };
45
+ export type UpdateSessionInfoInput = {
46
+ spaceId: string;
47
+ sessionId: string;
48
+ title?: string | null;
49
+ updatedAt?: string | null;
50
+ meta?: Record<string, unknown> | null;
51
+ };
52
+ export type SessionBindingRecord = {
53
+ id: string;
54
+ spaceId: string;
55
+ spaceSessionId: string;
56
+ spaceChannelId: string;
57
+ provider: string;
58
+ bindingKey: string;
59
+ externalChatId: string;
60
+ status: string | null;
61
+ meta: Record<string, unknown> | null;
62
+ createdAt: string;
63
+ updatedAt: string;
64
+ lastMessageAt: string | null;
65
+ };
66
+ export type SessionRecord = {
67
+ id: string;
68
+ spaceId: string;
69
+ title: string | null;
70
+ source: string | null;
71
+ status: string | null;
72
+ externalSessionId: string | null;
73
+ meta: Record<string, unknown> | null;
74
+ parentSessionId: string | null;
75
+ forkedFromMessageId: string | null;
76
+ lineageRootSessionId: string | null;
77
+ forkDepth: number;
78
+ latestMessageText: string | null;
79
+ lastMessageAt: string | null;
80
+ lastMessageId: string | null;
81
+ createdAt: string;
82
+ updatedAt: string;
83
+ };
84
+ export type MessageRecord = {
85
+ id: string;
86
+ sessionId: string;
87
+ role: "user" | "assistant" | "system";
88
+ content: ContentBlock[];
89
+ text: string | null;
90
+ sequence: number;
91
+ provider: string | null;
92
+ model: string | null;
93
+ stopReason: string | null;
94
+ errorMessage: string | null;
95
+ usage: Usage | null;
96
+ meta: Record<string, unknown> | null;
97
+ createdAt: string;
98
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Permission level for space / session resources.
3
+ *
4
+ * - `read` — anyone can read (including anonymous users)
5
+ * - `write` — anyone can read and write
6
+ * - `private` — explicitly deny access (no fallback to parent)
7
+ */
8
+ export type ResourcePermissionLevel = "read" | "write" | "private";
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ export * from "./stream.js";
2
+ export * from "./websocket.js";
@@ -0,0 +1,2 @@
1
+ export * from "./stream.js";
2
+ export * from "./websocket.js";
@@ -0,0 +1,17 @@
1
+ import type { ContentBlock } from "../core/content.js";
2
+ export type SessionStreamEvent = {
3
+ type: "stream_update";
4
+ spaceId: string;
5
+ sessionId: string;
6
+ content: ContentBlock[];
7
+ sourceMessageId: string | null;
8
+ timestamp: number;
9
+ turnEnd?: boolean;
10
+ anchorUserMessageId?: string | null;
11
+ };
12
+ export type SessionStreamError = {
13
+ type: "error";
14
+ spaceId: string;
15
+ sessionId: string | null;
16
+ error: string;
17
+ };
@@ -0,0 +1 @@
1
+ export {};