@undefineds.co/models 0.2.38 → 0.2.39

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 CHANGED
@@ -163,14 +163,13 @@ Contact
163
163
  entity: Agent context-root URI
164
164
 
165
165
  Agent
166
- id: /.data/agents/__secretary__/index.ttl#this
167
- root: /.data/agents/__secretary__/
168
- meta: /.data/agents/__secretary__/.meta
166
+ root: /agents/__secretary__/
167
+ meta: /agents/__secretary__/.meta
169
168
  ```
170
169
 
171
170
  External people, services, or bots may appear as Contacts without being LinX
172
171
  Agents. If something is modeled as an executable LinX Agent, the Agent resource
173
- identity is `index.ttl#this` inside the context-root container.
172
+ identity is the context-root container itself.
174
173
 
175
174
  ## Resource Ownership
176
175
 
@@ -204,7 +203,7 @@ Representative paths:
204
203
  /.data/chat/{chatId}/index.ttl#this
205
204
  /.data/chat/{chatId}/index.ttl#{threadId}
206
205
  /.data/chat/{chatId}/{yyyy}/{MM}/{dd}/messages.ttl#{messageId}
207
- /.data/agents/{agentId}/index.ttl#this
206
+ /agents/{agentId}/
208
207
  /.data/sessions/{yyyy}/{MM}/{dd}/{sessionId}.ttl
209
208
  /settings/providers/{providerId}.ttl
210
209
  /settings/providers/{providerId}.ttl#{modelId}
@@ -1,8 +1,7 @@
1
1
  import { boolean, object, podTable, string, integer, timestamp, text, real, uri, id } from "@undefineds.co/drizzle-solid";
2
2
  import { UDFS, DCTerms, FOAF, VCARD } from "./namespaces.js";
3
- import { agentResourceId } from "./resource-identity.js";
4
3
  export const agentResource = podTable("agent", {
5
- id: id("id").default((key) => agentResourceId(key)),
4
+ id: id("id").default("{key}/"),
6
5
  name: string("name").predicate(FOAF.name).notNull(),
7
6
  description: text("description").predicate(DCTerms.description),
8
7
  avatarUrl: uri("avatarUrl").predicate(VCARD.hasPhoto),
@@ -30,8 +29,8 @@ export const agentResource = podTable("agent", {
30
29
  updatedAt: timestamp("updatedAt").predicate(DCTerms.modified).notNull().defaultNow(),
31
30
  deletedAt: timestamp("deletedAt").predicate(UDFS.deletedAt),
32
31
  }, {
33
- base: '/.data/agents/',
34
- sparqlEndpoint: '/.data/agents/-/sparql',
32
+ base: '/agents/',
33
+ sparqlEndpoint: '/agents/-/sparql',
35
34
  type: FOAF.Agent,
36
35
  namespace: UDFS,
37
36
  });
@@ -1,73 +1,12 @@
1
- export declare const chatRepository: import("@undefineds.co/drizzle-solid/dist/core/repository").PodRepositoryDescriptor<import("@undefineds.co/drizzle-solid/dist/core/schema").PodTableWithColumns<import("@undefineds.co/drizzle-solid/dist/core/schema").ResolvedColumns<{
2
- id: import("@undefineds.co/drizzle-solid/dist/core/schema").PodStringColumn<false, false>;
3
- title: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, true, false>;
4
- description: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, false, false>;
5
- avatarUrl: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
6
- author: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
7
- status: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, true, true>;
8
- starred: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"boolean", null, false, true>;
9
- muted: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"boolean", null, false, true>;
10
- unreadCount: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"integer", null, false, true>;
11
- contact: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
12
- participants: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"array", "uri", false, false>;
13
- metadata: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"object", null, false, false>;
14
- lastActiveAt: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"datetime", null, false, false>;
15
- lastMessage: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
16
- lastMessagePreview: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, false, false>;
17
- createdAt: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"datetime", null, true, true>;
18
- updatedAt: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"datetime", null, true, true>;
19
- }>>, {
1
+ import { definePodRepository } from './repository';
2
+ import { chatResource, type ChatRow, type ChatInsert, type ChatUpdate } from './chat.schema';
3
+ export declare function buildChatRepositoryTarget(chatIdOrRef: string): {
20
4
  id: string;
21
- title: string;
22
- description: string;
23
- avatarUrl: string;
24
- author: string;
25
- status: string;
26
- starred: boolean;
27
- muted: boolean;
28
- unreadCount: number;
29
- contact: string;
30
- participants: string[];
31
- metadata: Record<string, unknown>;
32
- lastActiveAt: Date;
33
- lastMessage: string;
34
- lastMessagePreview: string;
35
- createdAt: Date;
36
- updatedAt: Date;
37
- }, {
38
- title: string;
39
- id?: string | undefined;
40
- description?: string | undefined;
41
- avatarUrl?: string | undefined;
42
- author?: string | undefined;
43
- status?: string | undefined;
44
- starred?: boolean | undefined;
45
- muted?: boolean | undefined;
46
- unreadCount?: number | undefined;
47
- contact?: string | undefined;
48
- participants?: string[] | undefined;
49
- metadata?: Record<string, unknown> | undefined;
50
- lastActiveAt?: Date | undefined;
51
- lastMessage?: string | undefined;
52
- lastMessagePreview?: string | undefined;
53
- createdAt?: Date | undefined;
54
- updatedAt?: Date | undefined;
55
- }, {
56
- id?: string | null | undefined;
57
- title?: string | undefined;
58
- description?: string | null | undefined;
59
- avatarUrl?: string | null | undefined;
60
- author?: string | null | undefined;
61
- status?: string | undefined;
62
- starred?: boolean | null | undefined;
63
- muted?: boolean | null | undefined;
64
- unreadCount?: number | null | undefined;
65
- contact?: string | null | undefined;
66
- participants?: string[] | null | undefined;
67
- metadata?: Record<string, unknown> | null | undefined;
68
- lastActiveAt?: Date | null | undefined;
69
- lastMessage?: string | null | undefined;
70
- lastMessagePreview?: string | null | undefined;
71
- createdAt?: Date | undefined;
72
- updatedAt?: Date | undefined;
73
- }, Record<string, unknown>>;
5
+ };
6
+ export declare function buildChatRepositoryIri(webIdOrPodUrl: string, chatIdOrRef: string): string;
7
+ export declare function readChatRepositoryId(chatRef: string | null | undefined): string | null;
8
+ export declare const chatRepository: ReturnType<typeof definePodRepository<typeof chatResource, ChatRow, ChatInsert, ChatUpdate>> & {
9
+ target: typeof buildChatRepositoryTarget;
10
+ iri: typeof buildChatRepositoryIri;
11
+ idFromRef: typeof readChatRepositoryId;
12
+ };
@@ -1,8 +1,23 @@
1
1
  import { definePodRepository } from './repository.js';
2
2
  import { chatResource } from './chat.schema.js';
3
+ import { extractChatIdFromChatRef } from './chat.utils.js';
4
+ export function buildChatRepositoryTarget(chatIdOrRef) {
5
+ return {
6
+ id: extractChatIdFromChatRef(chatIdOrRef) ?? chatIdOrRef,
7
+ };
8
+ }
9
+ export function buildChatRepositoryIri(webIdOrPodUrl, chatIdOrRef) {
10
+ return chatResource.buildIri(webIdOrPodUrl, buildChatRepositoryTarget(chatIdOrRef));
11
+ }
12
+ export function readChatRepositoryId(chatRef) {
13
+ return extractChatIdFromChatRef(chatRef);
14
+ }
3
15
  export const chatRepository = definePodRepository({
4
16
  namespace: 'chat',
5
17
  resource: chatResource,
6
18
  searchableFields: ['title', 'description'],
7
19
  defaultSort: { field: 'lastActiveAt', direction: 'desc' },
8
20
  });
21
+ chatRepository.target = buildChatRepositoryTarget;
22
+ chatRepository.iri = buildChatRepositoryIri;
23
+ chatRepository.idFromRef = readChatRepositoryId;
@@ -27,6 +27,7 @@ export interface ChatMetadata {
27
27
  */
28
28
  export declare const chatResource: import("@undefineds.co/drizzle-solid/dist/core/schema").PodTableWithColumns<import("@undefineds.co/drizzle-solid/dist/core/schema").ResolvedColumns<{
29
29
  id: import("@undefineds.co/drizzle-solid/dist/core/schema").PodStringColumn<false, false>;
30
+ rdfType: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"array", "uri", true, true>;
30
31
  title: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, true, false>;
31
32
  description: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, false, false>;
32
33
  avatarUrl: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
@@ -46,6 +47,7 @@ export declare const chatResource: import("@undefineds.co/drizzle-solid/dist/cor
46
47
  }>>;
47
48
  export declare const chatTable: import("@undefineds.co/drizzle-solid/dist/core/schema").PodTableWithColumns<import("@undefineds.co/drizzle-solid/dist/core/schema").ResolvedColumns<{
48
49
  id: import("@undefineds.co/drizzle-solid/dist/core/schema").PodStringColumn<false, false>;
50
+ rdfType: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"array", "uri", true, true>;
49
51
  title: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, true, false>;
50
52
  description: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, false, false>;
51
53
  avatarUrl: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
@@ -1,5 +1,5 @@
1
1
  import { boolean, object, podTable, string, text, timestamp, uri, id, integer } from '@undefineds.co/drizzle-solid';
2
- import { UDFS, DCTerms, SCHEMA, MEETING, WF } from './namespaces.js';
2
+ import { UDFS, DCTerms, SCHEMA, MEETING, WF, RDF, SIOC } from './namespaces.js';
3
3
  export const ChatStatus = {
4
4
  ACTIVE: 'active',
5
5
  ARCHIVED: 'archived',
@@ -24,6 +24,9 @@ export const ChatStatus = {
24
24
  */
25
25
  export const chatResource = podTable('chats', {
26
26
  id: id('id').default('{key}/index.ttl#this'),
27
+ // Resource config writes mee:LongChat; keep sioc:Container explicit so
28
+ // thread.parent can use sioc:has_parent for Chat and Task containers.
29
+ rdfType: uri('rdfType').array().predicate(RDF.type).notNull().default([SIOC.Container]),
27
30
  // Display
28
31
  title: string('title').predicate(DCTerms.title).notNull(),
29
32
  description: string('description').predicate(DCTerms.description),
@@ -13,4 +13,3 @@ export declare function buildChatTargetRef(chatIdOrRef: string): string;
13
13
  export declare function extractThreadIdFromThreadRef(threadRef: string | null | undefined): string | null;
14
14
  export declare function extractChatThreadRef(uri: string | null | undefined): ChatThreadRef;
15
15
  export declare function extractChatTargetRef(uri: string | null | undefined): ChatTargetRef;
16
- export declare function resolveThreadChatId(thread: Pick<Record<string, unknown>, 'chat'> | null | undefined): string | null;
@@ -72,6 +72,3 @@ export function extractChatTargetRef(uri) {
72
72
  }
73
73
  return { chatId: null, threadId: null, messageId: null };
74
74
  }
75
- export function resolveThreadChatId(thread) {
76
- return extractChatIdFromChatRef(typeof thread?.chat === 'string' ? thread.chat : null);
77
- }
@@ -12,7 +12,7 @@ export const fixtureContactAgentWorkspace = {
12
12
  name: 'Secretary@linx',
13
13
  rdfType: ContactClass.AGENT,
14
14
  contactType: 'agent',
15
- entity: 'https://pod.example/.data/agents/__secretary__/index.ttl#this',
15
+ entity: 'https://pod.example/agents/__secretary__/',
16
16
  isPublic: false,
17
17
  };
18
18
  export const fixtureChatDirectAI = {
@@ -28,7 +28,7 @@ export const fixtureChatDirectAI = {
28
28
  };
29
29
  export const fixtureThreadDirectAI = {
30
30
  id: 'thread-001',
31
- chat: 'https://pod.example/.data/chat/chat-direct-ai-1/index.ttl#this',
31
+ parent: 'https://pod.example/.data/chat/chat-direct-ai-1/index.ttl#this',
32
32
  title: 'Main thread',
33
33
  starred: false,
34
34
  workspace: 'https://pod.example/.data/agent-workspaces/secretary/ws-main/',
package/dist/index.d.ts CHANGED
@@ -7,9 +7,8 @@ export { ContactGender, contactResource, contactTable, ContactClass, ContactType
7
7
  export { contactRepository } from './contact.repository';
8
8
  export { ChatStatus, chatResource, chatTable, type ChatMetadata, type ChatMemberRole, type ChatStatusType, type ChatRow, type ChatInsert, type ChatUpdate, } from './chat.schema';
9
9
  export { chatRepository } from './chat.repository';
10
- export { buildChatTargetRef, extractChatIdFromChatRef, extractChatTargetRef, extractChatThreadRef, extractThreadIdFromThreadRef, resolveThreadChatId, toTimestamp, type ChatTargetRef, type ChatThreadRef, } from './chat.utils';
10
+ export { buildChatTargetRef, extractChatIdFromChatRef, extractChatTargetRef, extractChatThreadRef, extractThreadIdFromThreadRef, toTimestamp, type ChatTargetRef, type ChatThreadRef, } from './chat.utils';
11
11
  export { buildModelResourceId, buildModelResourceIri, buildModelResourceIriForDatabase, resolveModelResourceIri, resolveModelResourceIriForDatabase, type ModelResourceTarget, } from './resource-refs';
12
- export { agentHomeDirFromResourceId, agentHomePathFromResourceId, agentKeyFromResourceRef, agentKeyFromResourceId, agentResourceId, asBaseRelativeResourceId, asResourceIri, requireRowResourceId, type BaseRelativeResourceId, type ResourceIri, } from './resource-identity';
13
12
  export { ThreadStatus, threadResource, threadTable, type ThreadStatusType, type ThreadRow, type ThreadInsert, type ThreadUpdate, } from './thread.schema';
14
13
  export { threadRepository } from './thread.repository';
15
14
  export { MessageRole, MessageStatus, messageResource, messageTable, type MessageRoleType, type MessageStatusType, type MessageRow, type MessageInsert, type MessageUpdate, } from './message.schema';
package/dist/index.js CHANGED
@@ -19,9 +19,8 @@ export { contactRepository } from './contact.repository.js';
19
19
  // Chat & Message - 聊天和消息
20
20
  export { ChatStatus, chatResource, chatTable, } from './chat.schema.js';
21
21
  export { chatRepository } from './chat.repository.js';
22
- export { buildChatTargetRef, extractChatIdFromChatRef, extractChatTargetRef, extractChatThreadRef, extractThreadIdFromThreadRef, resolveThreadChatId, toTimestamp, } from './chat.utils.js';
22
+ export { buildChatTargetRef, extractChatIdFromChatRef, extractChatTargetRef, extractChatThreadRef, extractThreadIdFromThreadRef, toTimestamp, } from './chat.utils.js';
23
23
  export { buildModelResourceId, buildModelResourceIri, buildModelResourceIriForDatabase, resolveModelResourceIri, resolveModelResourceIriForDatabase, } from './resource-refs.js';
24
- export { agentHomeDirFromResourceId, agentHomePathFromResourceId, agentKeyFromResourceRef, agentKeyFromResourceId, agentResourceId, asBaseRelativeResourceId, asResourceIri, requireRowResourceId, } from './resource-identity.js';
25
24
  export { ThreadStatus, threadResource, threadTable, } from './thread.schema.js';
26
25
  export { threadRepository } from './thread.repository.js';
27
26
  export { MessageRole, MessageStatus, messageResource, messageTable, } from './message.schema.js';
@@ -1,8 +1,7 @@
1
1
  export declare const messageRepository: import("@undefineds.co/drizzle-solid/dist/core/repository").PodRepositoryDescriptor<import("@undefineds.co/drizzle-solid/dist/core/schema").PodTableWithColumns<import("@undefineds.co/drizzle-solid/dist/core/schema").ResolvedColumns<{
2
2
  id: import("@undefineds.co/drizzle-solid/dist/core/schema").PodStringColumn<false, false>;
3
- scope: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
4
3
  chat: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
5
- thread: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
4
+ thread: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, true, false>;
6
5
  maker: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
7
6
  role: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, true, true>;
8
7
  content: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, true, false>;
@@ -24,7 +23,6 @@ export declare const messageRepository: import("@undefineds.co/drizzle-solid/dis
24
23
  updatedAt: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"datetime", null, false, false>;
25
24
  }>>, {
26
25
  id: string;
27
- scope: string;
28
26
  chat: string;
29
27
  thread: string;
30
28
  maker: string;
@@ -47,11 +45,10 @@ export declare const messageRepository: import("@undefineds.co/drizzle-solid/dis
47
45
  createdAt: Date;
48
46
  updatedAt: Date;
49
47
  }, {
48
+ thread: string;
50
49
  content: string;
51
50
  id?: string | undefined;
52
- scope?: string | undefined;
53
51
  chat?: string | undefined;
54
- thread?: string | undefined;
55
52
  maker?: string | undefined;
56
53
  role?: string | undefined;
57
54
  richContent?: string | undefined;
@@ -72,9 +69,8 @@ export declare const messageRepository: import("@undefineds.co/drizzle-solid/dis
72
69
  updatedAt?: Date | undefined;
73
70
  }, {
74
71
  id?: string | null | undefined;
75
- scope?: string | null | undefined;
76
72
  chat?: string | null | undefined;
77
- thread?: string | null | undefined;
73
+ thread?: string | undefined;
78
74
  maker?: string | null | undefined;
79
75
  role?: string | undefined;
80
76
  content?: string | undefined;
@@ -15,13 +15,13 @@ export declare const MessageStatus: {
15
15
  * Message resource (aligned with xpod).
16
16
  *
17
17
  * Product semantics:
18
- * - Message belongs to a neutral product scope: usually a Chat, sometimes a
19
- * Task/thread execution timeline.
18
+ * - Message belongs to a Thread timeline. Chat messages also keep the Chat
19
+ * relation for Solid Chat compatibility and chat-bucketed storage.
20
20
  * - Chat remains the Solid Chat compatibility relation. When present, the
21
21
  * message is written under the Chat message bucket and projected through
22
22
  * wf:message.
23
- * - Thread is optional and appears when a message participates in an explicit
24
- * AI/task/branch timeline.
23
+ * - Task ownership is derived through message.thread -> thread.parent. Do not
24
+ * duplicate it as a custom Message scope relation.
25
25
  *
26
26
  * Storage structure:
27
27
  * - Location: /.data/chat/{chat|id}/{yyyy}/{MM}/{dd}/messages.ttl#{id}
@@ -30,9 +30,8 @@ export declare const MessageStatus: {
30
30
  */
31
31
  export declare const messageResource: import("@undefineds.co/drizzle-solid/dist/core/schema").PodTableWithColumns<import("@undefineds.co/drizzle-solid/dist/core/schema").ResolvedColumns<{
32
32
  id: import("@undefineds.co/drizzle-solid/dist/core/schema").PodStringColumn<false, false>;
33
- scope: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
34
33
  chat: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
35
- thread: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
34
+ thread: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, true, false>;
36
35
  maker: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
37
36
  role: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, true, true>;
38
37
  content: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, true, false>;
@@ -55,9 +54,8 @@ export declare const messageResource: import("@undefineds.co/drizzle-solid/dist/
55
54
  }>>;
56
55
  export declare const messageTable: import("@undefineds.co/drizzle-solid/dist/core/schema").PodTableWithColumns<import("@undefineds.co/drizzle-solid/dist/core/schema").ResolvedColumns<{
57
56
  id: import("@undefineds.co/drizzle-solid/dist/core/schema").PodStringColumn<false, false>;
58
- scope: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
59
57
  chat: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
60
- thread: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
58
+ thread: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, true, false>;
61
59
  maker: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
62
60
  role: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, true, true>;
63
61
  content: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, true, false>;
@@ -18,13 +18,13 @@ export const MessageStatus = {
18
18
  * Message resource (aligned with xpod).
19
19
  *
20
20
  * Product semantics:
21
- * - Message belongs to a neutral product scope: usually a Chat, sometimes a
22
- * Task/thread execution timeline.
21
+ * - Message belongs to a Thread timeline. Chat messages also keep the Chat
22
+ * relation for Solid Chat compatibility and chat-bucketed storage.
23
23
  * - Chat remains the Solid Chat compatibility relation. When present, the
24
24
  * message is written under the Chat message bucket and projected through
25
25
  * wf:message.
26
- * - Thread is optional and appears when a message participates in an explicit
27
- * AI/task/branch timeline.
26
+ * - Task ownership is derived through message.thread -> thread.parent. Do not
27
+ * duplicate it as a custom Message scope relation.
28
28
  *
29
29
  * Storage structure:
30
30
  * - Location: /.data/chat/{chat|id}/{yyyy}/{MM}/{dd}/messages.ttl#{id}
@@ -34,9 +34,7 @@ export const MessageStatus = {
34
34
  export const messageResource = podTable('chat_message', {
35
35
  id: id('id').default((key, row) => (renderDefaultIdTemplate(row?.chat
36
36
  ? 'chat/{chat.key}/{yyyy}/{MM}/{dd}/messages.ttl#{key}'
37
- : row?.thread
38
- ? '{thread.dir}/{yyyy}/{MM}/{dd}/messages.ttl#{key}'
39
- : '{scope.dir}/{yyyy}/{MM}/{dd}/messages.ttl#{key}', {
37
+ : '{thread.dir}/{yyyy}/{MM}/{dd}/messages.ttl#{key}', {
40
38
  key: resourceKey(key, 'msg'),
41
39
  row,
42
40
  links: {
@@ -44,15 +42,13 @@ export const messageResource = podTable('chat_message', {
44
42
  thread: threadResource,
45
43
  },
46
44
  }))),
47
- // Canonical product owner scope. The object can be a Chat, Task, Thread, or future command surface.
48
- scope: uri('scope').predicate(UDFS.inScope),
49
45
  // Solid Chat compatibility relation. In RDF this is an inverse link: <chat> wf:message <message>.
50
46
  chat: uri('chat').predicate(WF.message).inverse().link(chatResource),
51
- // Optional Thread relation. In RDF this is an inverse Solid Chat/SIOC link: <thread> sioc:has_member <message>.
52
- thread: uri('thread').predicate(SIOC.has_member).inverse().link(threadResource),
47
+ // Timeline relation. In RDF this is an inverse Solid Chat/SIOC link: <thread> sioc:has_member <message>.
48
+ thread: uri('thread').predicate(SIOC.has_member).inverse().notNull().link(threadResource),
53
49
  // maker is the entity URI of the message author:
54
50
  // - User: their WebID (https://user.pod/profile/card#me)
55
- // - AI: Agent URI (/.data/agents/{id}/index.ttl#this)
51
+ // - AI: Agent URI (/agents/{id}/)
56
52
  // - External: Contact URI (/.data/contacts/{id}.ttl#this)
57
53
  // No reference() constraint - accepts any valid URI.
58
54
  maker: uri('maker').predicate(FOAF.maker),
@@ -58,6 +58,7 @@ export const SCHEMA = createNamespace('schema', 'http://schema.org/', {
58
58
  url: 'url',
59
59
  });
60
60
  export const SIOC = createNamespace('sioc', 'http://rdfs.org/sioc/ns#', {
61
+ Container: 'Container',
61
62
  Post: 'Post',
62
63
  Thread: 'Thread',
63
64
  Forum: 'Forum',
@@ -132,7 +133,6 @@ export const UDFS = createNamespace('udfs', 'https://undefineds.co/ns#', {
132
133
  sortKey: 'sortKey',
133
134
  hasThread: 'hasThread',
134
135
  inThread: 'inThread',
135
- inScope: 'inScope',
136
136
  participants: 'participants',
137
137
  message: 'message',
138
138
  messageContent: 'messageContent',
package/dist/schema.d.ts CHANGED
@@ -80,6 +80,7 @@ export declare const solidResources: {
80
80
  }>>;
81
81
  chatResource: import("@undefineds.co/drizzle-solid/dist/core/schema").PodTableWithColumns<import("@undefineds.co/drizzle-solid/dist/core/schema").ResolvedColumns<{
82
82
  id: import("@undefineds.co/drizzle-solid/dist/core/schema").PodStringColumn<false, false>;
83
+ rdfType: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"array", "uri", true, true>;
83
84
  title: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, true, false>;
84
85
  description: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, false, false>;
85
86
  avatarUrl: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
@@ -116,9 +117,7 @@ export declare const solidResources: {
116
117
  }>>;
117
118
  threadResource: import("@undefineds.co/drizzle-solid/dist/core/schema").PodTableWithColumns<import("@undefineds.co/drizzle-solid/dist/core/schema").ResolvedColumns<{
118
119
  id: import("@undefineds.co/drizzle-solid/dist/core/schema").PodStringColumn<false, false>;
119
- scope: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
120
- chat: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
121
- task: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
120
+ parent: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, true, false>;
122
121
  title: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, false, false>;
123
122
  status: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, true, true>;
124
123
  starred: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"boolean", null, false, true>;
@@ -129,9 +128,8 @@ export declare const solidResources: {
129
128
  }>>;
130
129
  messageResource: import("@undefineds.co/drizzle-solid/dist/core/schema").PodTableWithColumns<import("@undefineds.co/drizzle-solid/dist/core/schema").ResolvedColumns<{
131
130
  id: import("@undefineds.co/drizzle-solid/dist/core/schema").PodStringColumn<false, false>;
132
- scope: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
133
131
  chat: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
134
- thread: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
132
+ thread: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, true, false>;
135
133
  maker: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
136
134
  role: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, true, true>;
137
135
  content: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, true, false>;
@@ -154,6 +152,7 @@ export declare const solidResources: {
154
152
  }>>;
155
153
  taskResource: import("@undefineds.co/drizzle-solid/dist/core/schema").PodTableWithColumns<import("@undefineds.co/drizzle-solid/dist/core/schema").ResolvedColumns<{
156
154
  id: import("@undefineds.co/drizzle-solid/dist/core/schema").PodStringColumn<false, false>;
155
+ rdfType: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"array", "uri", true, true>;
157
156
  title: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, false, false>;
158
157
  instruction: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, true, false>;
159
158
  prompt: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, false, false>;
@@ -592,6 +591,7 @@ export declare const solidSchema: {
592
591
  }>>;
593
592
  chatTable: import("@undefineds.co/drizzle-solid/dist/core/schema").PodTableWithColumns<import("@undefineds.co/drizzle-solid/dist/core/schema").ResolvedColumns<{
594
593
  id: import("@undefineds.co/drizzle-solid/dist/core/schema").PodStringColumn<false, false>;
594
+ rdfType: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"array", "uri", true, true>;
595
595
  title: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, true, false>;
596
596
  description: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, false, false>;
597
597
  avatarUrl: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
@@ -628,9 +628,7 @@ export declare const solidSchema: {
628
628
  }>>;
629
629
  threadTable: import("@undefineds.co/drizzle-solid/dist/core/schema").PodTableWithColumns<import("@undefineds.co/drizzle-solid/dist/core/schema").ResolvedColumns<{
630
630
  id: import("@undefineds.co/drizzle-solid/dist/core/schema").PodStringColumn<false, false>;
631
- scope: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
632
- chat: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
633
- task: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
631
+ parent: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, true, false>;
634
632
  title: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, false, false>;
635
633
  status: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, true, true>;
636
634
  starred: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"boolean", null, false, true>;
@@ -641,9 +639,8 @@ export declare const solidSchema: {
641
639
  }>>;
642
640
  messageTable: import("@undefineds.co/drizzle-solid/dist/core/schema").PodTableWithColumns<import("@undefineds.co/drizzle-solid/dist/core/schema").ResolvedColumns<{
643
641
  id: import("@undefineds.co/drizzle-solid/dist/core/schema").PodStringColumn<false, false>;
644
- scope: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
645
642
  chat: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
646
- thread: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
643
+ thread: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, true, false>;
647
644
  maker: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
648
645
  role: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, true, true>;
649
646
  content: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, true, false>;
@@ -666,6 +663,7 @@ export declare const solidSchema: {
666
663
  }>>;
667
664
  taskTable: import("@undefineds.co/drizzle-solid/dist/core/schema").PodTableWithColumns<import("@undefineds.co/drizzle-solid/dist/core/schema").ResolvedColumns<{
668
665
  id: import("@undefineds.co/drizzle-solid/dist/core/schema").PodStringColumn<false, false>;
666
+ rdfType: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"array", "uri", true, true>;
669
667
  title: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, false, false>;
670
668
  instruction: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, true, false>;
671
669
  prompt: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, false, false>;
@@ -1,8 +1,6 @@
1
1
  import { boolean, id, object, podTable, string, timestamp, uri } from '@undefineds.co/drizzle-solid';
2
2
  import { agentResource } from './agent.schema.js';
3
3
  import { DCTerms, UDFS } from './namespaces.js';
4
- import { resourceKey } from './resource-id-defaults.js';
5
- import { agentKeyFromResourceRef } from './resource-identity.js';
6
4
  /**
7
5
  * Skill binding resource.
8
6
  *
@@ -10,10 +8,7 @@ import { agentKeyFromResourceRef } from './resource-identity.js';
10
8
  * binding and lightweight load metadata; it does not duplicate full skill text.
11
9
  */
12
10
  export const skillResource = podTable('skill', {
13
- id: id('id').default((key, row) => {
14
- const agentRef = typeof row?.agent === 'string' ? row.agent : '';
15
- return `${agentKeyFromResourceRef(agentRef)}/skills/${resourceKey(key, 'skill')}/`;
16
- }),
11
+ id: id('id').default('{agent.key}/skills/{key}/'),
17
12
  agent: uri('agent').predicate(UDFS.agent).notNull().link(agentResource),
18
13
  root: uri('root').predicate(UDFS.root),
19
14
  name: string('name').predicate(UDFS.name).notNull(),
@@ -27,8 +22,8 @@ export const skillResource = podTable('skill', {
27
22
  createdAt: timestamp('createdAt').predicate(DCTerms.created).notNull().defaultNow(),
28
23
  updatedAt: timestamp('updatedAt').predicate(DCTerms.modified).notNull().defaultNow(),
29
24
  }, {
30
- base: '/.data/agents/',
31
- sparqlEndpoint: '/.data/agents/-/sparql',
25
+ base: '/agents/',
26
+ sparqlEndpoint: '/agents/-/sparql',
32
27
  type: UDFS.Skill,
33
28
  namespace: UDFS,
34
29
  });
@@ -17,6 +17,7 @@ export declare const TaskStatus: {
17
17
  */
18
18
  export declare const taskResource: import("@undefineds.co/drizzle-solid/dist/core/schema").PodTableWithColumns<import("@undefineds.co/drizzle-solid/dist/core/schema").ResolvedColumns<{
19
19
  id: import("@undefineds.co/drizzle-solid/dist/core/schema").PodStringColumn<false, false>;
20
+ rdfType: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"array", "uri", true, true>;
20
21
  title: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, false, false>;
21
22
  instruction: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, true, false>;
22
23
  prompt: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, false, false>;
@@ -33,6 +34,7 @@ export declare const taskResource: import("@undefineds.co/drizzle-solid/dist/cor
33
34
  }>>;
34
35
  export declare const taskTable: import("@undefineds.co/drizzle-solid/dist/core/schema").PodTableWithColumns<import("@undefineds.co/drizzle-solid/dist/core/schema").ResolvedColumns<{
35
36
  id: import("@undefineds.co/drizzle-solid/dist/core/schema").PodStringColumn<false, false>;
37
+ rdfType: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"array", "uri", true, true>;
36
38
  title: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, false, false>;
37
39
  instruction: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, true, false>;
38
40
  prompt: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, false, false>;
@@ -1,5 +1,5 @@
1
1
  import { id, object, podTable, string, text, timestamp, uri } from '@undefineds.co/drizzle-solid';
2
- import { DCTerms, UDFS } from './namespaces.js';
2
+ import { DCTerms, RDF, SIOC, UDFS } from './namespaces.js';
3
3
  export const TaskStatus = {
4
4
  OPEN: 'open',
5
5
  READY: 'ready',
@@ -18,6 +18,9 @@ export const TaskStatus = {
18
18
  */
19
19
  export const taskResource = podTable('task', {
20
20
  id: id('id').default('index.ttl#{key}'),
21
+ // Resource config writes udfs:Task; keep sioc:Container explicit so
22
+ // task-owned threads can use the same sioc:has_parent relation as chats.
23
+ rdfType: uri('rdfType').array().predicate(RDF.type).notNull().default([SIOC.Container]),
21
24
  title: string('title').predicate(DCTerms.title),
22
25
  instruction: text('instruction').predicate(UDFS.instruction).notNull(),
23
26
  prompt: text('prompt').predicate(UDFS.prompt),
@@ -1,49 +1,20 @@
1
- export declare const threadRepository: import("@undefineds.co/drizzle-solid/dist/core/repository").PodRepositoryDescriptor<import("@undefineds.co/drizzle-solid/dist/core/schema").PodTableWithColumns<import("@undefineds.co/drizzle-solid/dist/core/schema").ResolvedColumns<{
2
- id: import("@undefineds.co/drizzle-solid/dist/core/schema").PodStringColumn<false, false>;
3
- scope: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
4
- chat: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
5
- task: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
6
- title: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, false, false>;
7
- status: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, true, true>;
8
- starred: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"boolean", null, false, true>;
9
- workspace: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
10
- metadata: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"object", null, false, false>;
11
- createdAt: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"datetime", null, true, true>;
12
- updatedAt: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"datetime", null, true, true>;
13
- }>>, {
14
- id: string;
15
- scope: string;
16
- chat: string;
17
- task: string;
18
- title: string;
19
- status: string;
20
- starred: boolean;
21
- workspace: string;
22
- metadata: Record<string, unknown>;
23
- createdAt: Date;
24
- updatedAt: Date;
25
- }, {
26
- id?: string | undefined;
27
- scope?: string | undefined;
28
- chat?: string | undefined;
29
- task?: string | undefined;
30
- title?: string | undefined;
31
- status?: string | undefined;
32
- starred?: boolean | undefined;
33
- workspace?: string | undefined;
34
- metadata?: Record<string, unknown> | undefined;
35
- createdAt?: Date | undefined;
36
- updatedAt?: Date | undefined;
37
- }, {
38
- id?: string | null | undefined;
39
- scope?: string | null | undefined;
40
- chat?: string | null | undefined;
41
- task?: string | null | undefined;
42
- title?: string | null | undefined;
43
- status?: string | undefined;
44
- starred?: boolean | null | undefined;
45
- workspace?: string | null | undefined;
46
- metadata?: Record<string, unknown> | null | undefined;
47
- createdAt?: Date | undefined;
48
- updatedAt?: Date | undefined;
49
- }, Record<string, unknown>>;
1
+ import { definePodRepository } from './repository';
2
+ import { threadResource, type ThreadRow, type ThreadInsert, type ThreadUpdate } from './thread.schema';
3
+ export interface ChatThreadTarget extends Record<string, unknown> {
4
+ parent: string;
5
+ id?: string;
6
+ }
7
+ export declare function buildChatThreadTarget(chatIdOrRef: string, threadIdOrRef?: string): ChatThreadTarget;
8
+ export declare function extractChatIdFromThread(row: Pick<ThreadRow, 'parent'> | null | undefined): string | null;
9
+ export declare function readThreadRepositoryChatId(threadRef: string | null | undefined): string | null;
10
+ export declare function readThreadRepositoryId(threadRef: string | null | undefined): string | null;
11
+ export declare function buildChatThreadIri(webIdOrPodUrl: string, chatIdOrRef: string, threadId: string): string;
12
+ export declare function buildChatThreadResourceId(chatIdOrRef: string, threadIdOrRef: string): string;
13
+ export declare const threadRepository: ReturnType<typeof definePodRepository<typeof threadResource, ThreadRow, ThreadInsert, ThreadUpdate>> & {
14
+ targetForChat: typeof buildChatThreadTarget;
15
+ iriForChat: typeof buildChatThreadIri;
16
+ idForChat: typeof buildChatThreadResourceId;
17
+ chatId: typeof extractChatIdFromThread;
18
+ chatIdFromRef: typeof readThreadRepositoryChatId;
19
+ idFromRef: typeof readThreadRepositoryId;
20
+ };
@@ -1,8 +1,39 @@
1
1
  import { definePodRepository } from './repository.js';
2
2
  import { threadResource } from './thread.schema.js';
3
+ import { buildChatTargetRef, extractChatIdFromChatRef, extractChatThreadRef, extractThreadIdFromThreadRef, } from './chat.utils.js';
4
+ export function buildChatThreadTarget(chatIdOrRef, threadIdOrRef) {
5
+ const parsedThread = extractChatThreadRef(threadIdOrRef);
6
+ const chatRef = parsedThread.chatId ?? chatIdOrRef;
7
+ const threadId = parsedThread.threadId ?? extractThreadIdFromThreadRef(threadIdOrRef) ?? threadIdOrRef;
8
+ return {
9
+ ...(threadId ? { id: threadId } : {}),
10
+ parent: buildChatTargetRef(chatRef),
11
+ };
12
+ }
13
+ export function extractChatIdFromThread(row) {
14
+ return extractChatIdFromChatRef(row?.parent);
15
+ }
16
+ export function readThreadRepositoryChatId(threadRef) {
17
+ return extractChatThreadRef(threadRef).chatId;
18
+ }
19
+ export function readThreadRepositoryId(threadRef) {
20
+ return extractThreadIdFromThreadRef(threadRef);
21
+ }
22
+ export function buildChatThreadIri(webIdOrPodUrl, chatIdOrRef, threadId) {
23
+ return threadResource.buildIri(webIdOrPodUrl, buildChatThreadTarget(chatIdOrRef, threadId));
24
+ }
25
+ export function buildChatThreadResourceId(chatIdOrRef, threadIdOrRef) {
26
+ return threadResource.buildId(buildChatThreadTarget(chatIdOrRef, threadIdOrRef));
27
+ }
3
28
  export const threadRepository = definePodRepository({
4
29
  namespace: 'thread',
5
30
  resource: threadResource,
6
31
  searchableFields: ['title'],
7
32
  defaultSort: { field: 'updatedAt', direction: 'desc' },
8
33
  });
34
+ threadRepository.targetForChat = buildChatThreadTarget;
35
+ threadRepository.iriForChat = buildChatThreadIri;
36
+ threadRepository.idForChat = buildChatThreadResourceId;
37
+ threadRepository.chatId = extractChatIdFromThread;
38
+ threadRepository.chatIdFromRef = readThreadRepositoryChatId;
39
+ threadRepository.idFromRef = readThreadRepositoryId;
@@ -8,16 +8,16 @@ export declare const ThreadStatus: {
8
8
  * Thread resource.
9
9
  *
10
10
  * Product semantics:
11
- * - Thread is the implicit concrete timeline/place under exactly one surface:
12
- * Chat for conversation timelines, or Task for task execution timelines.
13
- * - `scope` is the neutral LinX ownership relation for that surface. When the
14
- * scope is a Chat, also write `chat` so Solid Chat clients can keep using
15
- * sioc:has_parent. When the scope is a Task, also write `task` for workflow
16
- * indexes.
11
+ * - Thread is the implicit concrete timeline/place under exactly one parent
12
+ * container: Chat for conversation timelines, or Task for task execution
13
+ * timelines.
14
+ * - `parent` uses sioc:has_parent for both Chat and Task. Chat and Task are
15
+ * both modeled as sioc:Container, so Thread does not need parallel chat/task
16
+ * ownership fields.
17
17
  * - AI product runtime sessions map to Thread when they represent a concrete
18
18
  * conversation timeline/place/run.
19
- * - Thread carries workspace/place relations and runtime metadata. Chat only
20
- * identifies the counterpart/conversation object.
19
+ * - Thread carries workspace/place relations and runtime metadata. The parent
20
+ * container identifies the command surface/counterpart.
21
21
  * - Product/runtime-specific ids should stay in metadata as `runtimeSessionId`,
22
22
  * `runtime`, etc. Do not name the generic thread id `piSessionId`.
23
23
  *
@@ -33,9 +33,7 @@ export declare const ThreadStatus: {
33
33
  */
34
34
  export declare const threadResource: import("@undefineds.co/drizzle-solid/dist/core/schema").PodTableWithColumns<import("@undefineds.co/drizzle-solid/dist/core/schema").ResolvedColumns<{
35
35
  id: import("@undefineds.co/drizzle-solid/dist/core/schema").PodStringColumn<false, false>;
36
- scope: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
37
- chat: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
38
- task: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
36
+ parent: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, true, false>;
39
37
  title: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, false, false>;
40
38
  status: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, true, true>;
41
39
  starred: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"boolean", null, false, true>;
@@ -46,9 +44,7 @@ export declare const threadResource: import("@undefineds.co/drizzle-solid/dist/c
46
44
  }>>;
47
45
  export declare const threadTable: import("@undefineds.co/drizzle-solid/dist/core/schema").PodTableWithColumns<import("@undefineds.co/drizzle-solid/dist/core/schema").ResolvedColumns<{
48
46
  id: import("@undefineds.co/drizzle-solid/dist/core/schema").PodStringColumn<false, false>;
49
- scope: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
50
- chat: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
51
- task: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
47
+ parent: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, true, false>;
52
48
  title: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, false, false>;
53
49
  status: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, true, true>;
54
50
  starred: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"boolean", null, false, true>;
@@ -1,7 +1,5 @@
1
1
  import { uri, boolean, object, podTable, string, timestamp, id, renderDefaultIdTemplate } from '@undefineds.co/drizzle-solid';
2
2
  import { UDFS, DCTerms, SIOC } from './namespaces.js';
3
- import { chatResource } from './chat.schema.js';
4
- import { taskResource } from './task.schema.js';
5
3
  import { resourceKey } from './resource-id-defaults.js';
6
4
  export const ThreadStatus = {
7
5
  ACTIVE: 'active',
@@ -12,16 +10,16 @@ export const ThreadStatus = {
12
10
  * Thread resource.
13
11
  *
14
12
  * Product semantics:
15
- * - Thread is the implicit concrete timeline/place under exactly one surface:
16
- * Chat for conversation timelines, or Task for task execution timelines.
17
- * - `scope` is the neutral LinX ownership relation for that surface. When the
18
- * scope is a Chat, also write `chat` so Solid Chat clients can keep using
19
- * sioc:has_parent. When the scope is a Task, also write `task` for workflow
20
- * indexes.
13
+ * - Thread is the implicit concrete timeline/place under exactly one parent
14
+ * container: Chat for conversation timelines, or Task for task execution
15
+ * timelines.
16
+ * - `parent` uses sioc:has_parent for both Chat and Task. Chat and Task are
17
+ * both modeled as sioc:Container, so Thread does not need parallel chat/task
18
+ * ownership fields.
21
19
  * - AI product runtime sessions map to Thread when they represent a concrete
22
20
  * conversation timeline/place/run.
23
- * - Thread carries workspace/place relations and runtime metadata. Chat only
24
- * identifies the counterpart/conversation object.
21
+ * - Thread carries workspace/place relations and runtime metadata. The parent
22
+ * container identifies the command surface/counterpart.
25
23
  * - Product/runtime-specific ids should stay in metadata as `runtimeSessionId`,
26
24
  * `runtime`, etc. Do not name the generic thread id `piSessionId`.
27
25
  *
@@ -36,24 +34,14 @@ export const ThreadStatus = {
36
34
  * container URI here and store portable metadata with that container/resource.
37
35
  */
38
36
  export const threadResource = podTable('thread', {
39
- id: id('id').default((key, row) => (renderDefaultIdTemplate(row?.chat
40
- ? 'chat/{chat.key}/index.ttl#{key}'
41
- : row?.task
42
- ? 'task/{task.key}/index.ttl#{key}'
43
- : '{scope.dir}/index.ttl#{key}', {
44
- key: resourceKey(key, 'thread'),
45
- row,
46
- links: {
47
- chat: chatResource,
48
- task: taskResource,
49
- },
50
- }))),
51
- // Canonical product owner surface. The object can be a Chat, Task, or future command surface.
52
- scope: uri('scope').predicate(UDFS.inScope),
53
- // Solid Chat compatibility owner for conversation timelines. Do not set together with `task`.
54
- chat: uri('chat').predicate(SIOC.has_parent).link(chatResource),
55
- // Workflow compatibility/index owner for task execution timelines. Do not set together with `chat`.
56
- task: uri('task').predicate(UDFS.task).link(taskResource),
37
+ id: id('id').default((key, row) => {
38
+ return renderDefaultIdTemplate('{parent.dir}/index.ttl#{key}', {
39
+ key: resourceKey(key, 'thread'),
40
+ row,
41
+ });
42
+ }),
43
+ // Parent command surface/container. Chat and Task are both sioc:Container.
44
+ parent: uri('parent').predicate(SIOC.has_parent).notNull(),
57
45
  // Display / state
58
46
  title: string('title').predicate(DCTerms.title),
59
47
  status: string('status').predicate(UDFS.status).notNull().default(ThreadStatus.ACTIVE),
@@ -1,5 +1,6 @@
1
1
  /** Chat channel vocab (thin place/container). */
2
2
  export declare const ChatBaseVocab: {
3
+ readonly rdfType: string;
3
4
  readonly title: string;
4
5
  readonly description: string;
5
6
  readonly avatarUrl: string;
@@ -1,6 +1,7 @@
1
- import { DCTerms, SCHEMA, UDFS, WF } from '../namespaces.js';
1
+ import { DCTerms, RDF, SCHEMA, UDFS, WF } from '../namespaces.js';
2
2
  /** Chat channel vocab (thin place/container). */
3
3
  export const ChatBaseVocab = {
4
+ rdfType: RDF.type,
4
5
  // Display
5
6
  title: DCTerms.title,
6
7
  description: DCTerms.description,
@@ -1,5 +1,4 @@
1
1
  export declare const MessageVocab: {
2
- readonly scope: string;
3
2
  readonly thread: string;
4
3
  readonly chat: string;
5
4
  readonly maker: string;
@@ -1,7 +1,5 @@
1
1
  import { DCTerms, FOAF, SCHEMA, SIOC, UDFS, WF } from '../namespaces.js';
2
2
  export const MessageVocab = {
3
- // Existing
4
- scope: UDFS.inScope,
5
3
  thread: SIOC.has_member,
6
4
  chat: WF.message,
7
5
  maker: FOAF.maker,
@@ -1,7 +1,5 @@
1
1
  export declare const ThreadVocab: {
2
- readonly scope: string;
3
- readonly chat: string;
4
- readonly task: string;
2
+ readonly parent: string;
5
3
  readonly title: string;
6
4
  readonly starred: string;
7
5
  readonly metadata: string;
@@ -1,8 +1,6 @@
1
1
  import { DCTerms, SIOC, UDFS } from '../namespaces.js';
2
2
  export const ThreadVocab = {
3
- scope: UDFS.inScope,
4
- chat: SIOC.has_parent,
5
- task: UDFS.task,
3
+ parent: SIOC.has_parent,
6
4
  title: DCTerms.title,
7
5
  starred: UDFS.favorite,
8
6
  metadata: UDFS.metadata,
@@ -1,4 +1,5 @@
1
1
  export declare const TaskVocab: {
2
+ readonly rdfType: string;
2
3
  readonly title: string;
3
4
  readonly instruction: string;
4
5
  readonly prompt: string;
@@ -1,5 +1,6 @@
1
- import { AS, DCTerms, SCHEMA, UDFS } from '../namespaces.js';
1
+ import { AS, DCTerms, RDF, SCHEMA, UDFS } from '../namespaces.js';
2
2
  export const TaskVocab = {
3
+ rdfType: RDF.type,
3
4
  title: DCTerms.title,
4
5
  instruction: UDFS.instruction,
5
6
  prompt: UDFS.prompt,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@undefineds.co/models",
3
- "version": "0.2.38",
3
+ "version": "0.2.39",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -138,7 +138,7 @@ Use full URI relation fields:
138
138
  message.chat
139
139
  message.thread
140
140
  message.replyTo
141
- thread.chat
141
+ thread.parent
142
142
  run.thread
143
143
  run.task
144
144
  ```
@@ -148,7 +148,7 @@ Avoid persisted relation fields such as:
148
148
  ```ts
149
149
  message.chatId
150
150
  message.threadId
151
- thread.chatId
151
+ thread.parentId
152
152
  ```
153
153
 
154
154
  unless they are intentionally opaque literal protocol fields, not RDF links.
@@ -165,6 +165,15 @@ threadRepository.listByChatId(db, { chatId })
165
165
  These helpers should derive canonical Pod IRIs or base-relative ids internally.
166
166
  Do not force UI/CLI callers to manually construct Pod IRIs in every query.
167
167
 
168
+ When an application needs to group, filter, or address records through a linked
169
+ relation, push that need into the owning `models` repository/helper first. For
170
+ example, app code should call a `threadRepository.targetForChat(...)` or
171
+ `threadRepository.chatId(row)` style API instead of parsing
172
+ `thread.parent` strings locally. If the same relation-target operation appears
173
+ across multiple resources, then promote it down into `drizzle-solid` as a
174
+ generic ORM helper. Do not add resource-specific `extractXFromY` parsing
175
+ helpers in product shells.
176
+
168
177
  ## Chat, Task, Thread, Message, Run, Step
169
178
 
170
179
  Use these concepts consistently across products:
@@ -173,11 +182,10 @@ Use these concepts consistently across products:
173
182
  user is talking with.
174
183
  - `Task`: task-style command surface, parallel to Chat. It describes recurring,
175
184
  triggered, or one-off task intent.
176
- - `Thread`: implicit concrete timeline/place under exactly one command surface:
177
- Chat for conversation timelines, or Task for task execution timelines.
178
- - `Message`: human/runtime communication item in a command scope. It usually
179
- belongs to a Chat, but task/runtime messages may belong to a Task or Thread
180
- scope without inventing a Chat.
185
+ - `Thread`: implicit concrete timeline/place under exactly one parent
186
+ container. Chat and Task are both `sioc:Container`.
187
+ - `Message`: human/runtime communication item in a Thread. Chat messages also
188
+ keep the Chat relation for Solid Chat compatibility.
181
189
  - `Run`: one concrete execution attempt by an Agent Runtime.
182
190
  - `RunStep`: append-only execution facts for a Run.
183
191
 
@@ -191,17 +199,17 @@ Align with graph semantics:
191
199
 
192
200
  | Concept | Preferred RDF direction | Typical predicate |
193
201
  |---|---|---|
194
- | resource belongs to command scope | `resource -> scope` | `udfs:inScope` |
195
202
  | chat contains message | `chat -> message` | `wf:message` / project vocabulary |
196
- | chat owns thread | `thread -> chat` | `sioc:has_parent` for compatibility |
203
+ | thread parent container | `thread -> chat-or-task` | `sioc:has_parent` |
197
204
  | thread contains message | `thread -> message` | `sioc:has_member` or equivalent |
198
205
  | reply points to original | `replyMessage -> originalMessage` | `sioc:has_reply` / project predicate |
199
206
  | author/maker | `message -> maker` | `foaf:maker` |
200
207
 
201
- Use `udfs:inScope` for LinX product semantics when a Thread or Message can
202
- belong to either Chat or Task. Preserve Solid Chat compatibility by also writing
203
- `sioc:has_parent` for Chat-scoped Threads and `wf:message` for Chat-scoped
204
- Messages. Do not stretch Solid Chat predicates to mean Task ownership.
208
+ Use `sioc:has_parent` as the canonical Thread parent relation. Chat and Task
209
+ resources must be modeled as `sioc:Container`, so Thread does not need parallel
210
+ `chat`, `task`, or custom `scope` ownership fields. Preserve Solid Chat
211
+ compatibility with `wf:message` for Chat-contained Messages and
212
+ `sioc:has_member` for Thread-contained Messages.
205
213
 
206
214
  When inverse predicates are supported, use them for read/write symmetry. If the
207
215
  ORM cannot safely express the inverse write, put the relation writer in the
@@ -1,19 +0,0 @@
1
- declare const baseRelativeResourceIdBrand: unique symbol;
2
- declare const resourceIriBrand: unique symbol;
3
- export type BaseRelativeResourceId = string & {
4
- readonly [baseRelativeResourceIdBrand]: 'BaseRelativeResourceId';
5
- };
6
- export type ResourceIri = string & {
7
- readonly [resourceIriBrand]: 'ResourceIri';
8
- };
9
- export declare function asBaseRelativeResourceId(value: string, label?: string): BaseRelativeResourceId;
10
- export declare function asResourceIri(value: string, label?: string): ResourceIri;
11
- export declare function requireRowResourceId(row: {
12
- id?: string | null;
13
- } | null | undefined, label?: string): BaseRelativeResourceId;
14
- export declare function agentResourceId(key?: string | null): BaseRelativeResourceId;
15
- export declare function agentKeyFromResourceId(resourceId: string): string;
16
- export declare function agentKeyFromResourceRef(resourceRef: string): string;
17
- export declare function agentHomeDirFromResourceId(resourceId: string): BaseRelativeResourceId;
18
- export declare function agentHomePathFromResourceId(resourceId: string): string;
19
- export {};
@@ -1,68 +0,0 @@
1
- const ABSOLUTE_IRI = /^[a-zA-Z][a-zA-Z\d+.-]*:/;
2
- const AGENT_RESOURCE_ID = /^([A-Za-z0-9_.-]+)\/index\.ttl#this$/;
3
- const AGENT_KEY = /^[A-Za-z0-9_.-]+$/;
4
- export function asBaseRelativeResourceId(value, label = 'Resource id') {
5
- if (typeof value !== 'string' || value.trim().length === 0) {
6
- throw new Error(`${label} must be a non-empty base-relative resource id.`);
7
- }
8
- const normalized = value.trim();
9
- if (ABSOLUTE_IRI.test(normalized) || normalized.startsWith('/') || normalized.startsWith('//')) {
10
- throw new Error(`${label} must be a base-relative resource id.`);
11
- }
12
- return normalized;
13
- }
14
- export function asResourceIri(value, label = 'Resource IRI') {
15
- if (typeof value !== 'string' || value.trim().length === 0) {
16
- throw new Error(`${label} must be a non-empty resource IRI.`);
17
- }
18
- const normalized = value.trim();
19
- if (!ABSOLUTE_IRI.test(normalized)) {
20
- throw new Error(`${label} must be a full resource IRI.`);
21
- }
22
- return normalized;
23
- }
24
- export function requireRowResourceId(row, label = 'Pod row') {
25
- if (!row || typeof row.id !== 'string' || row.id.trim().length === 0) {
26
- throw new Error(`${label} row is missing row.id.`);
27
- }
28
- return asBaseRelativeResourceId(row.id, `${label} row.id`);
29
- }
30
- function defaultAgentKey() {
31
- return `agent_${Math.random().toString(36).slice(2, 12)}`;
32
- }
33
- export function agentResourceId(key) {
34
- const raw = typeof key === 'string' ? key.trim() : '';
35
- const value = raw || defaultAgentKey();
36
- if (AGENT_RESOURCE_ID.test(value)) {
37
- return asBaseRelativeResourceId(value, 'Agent resource id');
38
- }
39
- if (!AGENT_KEY.test(value)) {
40
- throw new Error('Agent key must be a local key using letters, numbers, dot, underscore, or dash.');
41
- }
42
- return asBaseRelativeResourceId(`${value}/index.ttl#this`, 'Agent resource id');
43
- }
44
- export function agentKeyFromResourceId(resourceId) {
45
- const id = asBaseRelativeResourceId(resourceId, 'Agent resource id');
46
- const match = id.match(AGENT_RESOURCE_ID);
47
- if (!match?.[1]) {
48
- throw new Error('Agent resource id must use {agentKey}/index.ttl#this.');
49
- }
50
- return match[1];
51
- }
52
- export function agentKeyFromResourceRef(resourceRef) {
53
- if (typeof resourceRef !== 'string' || resourceRef.trim().length === 0) {
54
- throw new Error('Agent resource ref must be a non-empty Agent id or IRI.');
55
- }
56
- const normalized = resourceRef.trim();
57
- const iriMatch = normalized.match(/\/\.data\/agents\/([A-Za-z0-9_.-]+)\/index\.ttl#this$/);
58
- if (iriMatch?.[1]) {
59
- return iriMatch[1];
60
- }
61
- return agentKeyFromResourceId(normalized);
62
- }
63
- export function agentHomeDirFromResourceId(resourceId) {
64
- return asBaseRelativeResourceId(`${agentKeyFromResourceId(resourceId)}/`, 'Agent home dir');
65
- }
66
- export function agentHomePathFromResourceId(resourceId) {
67
- return `/.data/agents/${agentKeyFromResourceId(resourceId)}/`;
68
- }