@undefineds.co/models 0.2.36 → 0.2.38

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,13 +163,14 @@ Contact
163
163
  entity: Agent context-root URI
164
164
 
165
165
  Agent
166
- root: /agents/__secretary__/
167
- meta: /agents/__secretary__/.meta
166
+ id: /.data/agents/__secretary__/index.ttl#this
167
+ root: /.data/agents/__secretary__/
168
+ meta: /.data/agents/__secretary__/.meta
168
169
  ```
169
170
 
170
171
  External people, services, or bots may appear as Contacts without being LinX
171
172
  Agents. If something is modeled as an executable LinX Agent, the Agent resource
172
- identity is the context-root container itself.
173
+ identity is `index.ttl#this` inside the context-root container.
173
174
 
174
175
  ## Resource Ownership
175
176
 
@@ -203,7 +204,7 @@ Representative paths:
203
204
  /.data/chat/{chatId}/index.ttl#this
204
205
  /.data/chat/{chatId}/index.ttl#{threadId}
205
206
  /.data/chat/{chatId}/{yyyy}/{MM}/{dd}/messages.ttl#{messageId}
206
- /agents/{agentId}/
207
+ /.data/agents/{agentId}/index.ttl#this
207
208
  /.data/sessions/{yyyy}/{MM}/{dd}/{sessionId}.ttl
208
209
  /settings/providers/{providerId}.ttl
209
210
  /settings/providers/{providerId}.ttl#{modelId}
@@ -1,7 +1,8 @@
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";
3
4
  export const agentResource = podTable("agent", {
4
- id: id("id").default("{key}/"),
5
+ id: id("id").default((key) => agentResourceId(key)),
5
6
  name: string("name").predicate(FOAF.name).notNull(),
6
7
  description: text("description").predicate(DCTerms.description),
7
8
  avatarUrl: uri("avatarUrl").predicate(VCARD.hasPhoto),
@@ -29,8 +30,8 @@ export const agentResource = podTable("agent", {
29
30
  updatedAt: timestamp("updatedAt").predicate(DCTerms.modified).notNull().defaultNow(),
30
31
  deletedAt: timestamp("deletedAt").predicate(UDFS.deletedAt),
31
32
  }, {
32
- base: '/agents/',
33
- sparqlEndpoint: '/agents/-/sparql',
33
+ base: '/.data/agents/',
34
+ sparqlEndpoint: '/.data/agents/-/sparql',
34
35
  type: FOAF.Agent,
35
36
  namespace: UDFS,
36
37
  });
@@ -13,9 +13,11 @@ export declare const EvidenceKind: {
13
13
  /**
14
14
  * Evidence resource.
15
15
  *
16
- * Evidence is append-only proof or finding. The resource subject is the
17
- * evidence file/record itself; `about` points to the work/control object it
18
- * supports. It is not a status owner by itself.
16
+ * Evidence is append-only proof or finding metadata. Long evidence bodies
17
+ * such as logs, patches, screenshots, transcripts, and reports are Pod files;
18
+ * this TTL record stores the queryable meta and `source` points to the
19
+ * evidence file when one exists. `about` points to the work/control object it
20
+ * supports. Evidence is not a status owner by itself.
19
21
  */
20
22
  export declare const evidenceResource: import("@undefineds.co/drizzle-solid/dist/core/schema").PodTableWithColumns<import("@undefineds.co/drizzle-solid/dist/core/schema").ResolvedColumns<{
21
23
  id: import("@undefineds.co/drizzle-solid/dist/core/schema").PodStringColumn<false, false>;
@@ -19,9 +19,11 @@ export const EvidenceKind = {
19
19
  /**
20
20
  * Evidence resource.
21
21
  *
22
- * Evidence is append-only proof or finding. The resource subject is the
23
- * evidence file/record itself; `about` points to the work/control object it
24
- * supports. It is not a status owner by itself.
22
+ * Evidence is append-only proof or finding metadata. Long evidence bodies
23
+ * such as logs, patches, screenshots, transcripts, and reports are Pod files;
24
+ * this TTL record stores the queryable meta and `source` points to the
25
+ * evidence file when one exists. `about` points to the work/control object it
26
+ * supports. Evidence is not a status owner by itself.
25
27
  */
26
28
  export const evidenceResource = podTable('evidence', {
27
29
  id: id('id').default('evidence/{yyyy}/{MM}/{dd}.ttl#{key}'),
@@ -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/agents/__secretary__/',
15
+ entity: 'https://pod.example/.data/agents/__secretary__/index.ttl#this',
16
16
  isPublic: false,
17
17
  };
18
18
  export const fixtureChatDirectAI = {
package/dist/index.d.ts CHANGED
@@ -9,6 +9,7 @@ export { ChatStatus, chatResource, chatTable, type ChatMetadata, type ChatMember
9
9
  export { chatRepository } from './chat.repository';
10
10
  export { buildChatTargetRef, extractChatIdFromChatRef, extractChatTargetRef, extractChatThreadRef, extractThreadIdFromThreadRef, resolveThreadChatId, 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';
12
13
  export { ThreadStatus, threadResource, threadTable, type ThreadStatusType, type ThreadRow, type ThreadInsert, type ThreadUpdate, } from './thread.schema';
13
14
  export { threadRepository } from './thread.repository';
14
15
  export { MessageRole, MessageStatus, messageResource, messageTable, type MessageRoleType, type MessageStatusType, type MessageRow, type MessageInsert, type MessageUpdate, } from './message.schema';
package/dist/index.js CHANGED
@@ -21,6 +21,7 @@ export { ChatStatus, chatResource, chatTable, } from './chat.schema.js';
21
21
  export { chatRepository } from './chat.repository.js';
22
22
  export { buildChatTargetRef, extractChatIdFromChatRef, extractChatTargetRef, extractChatThreadRef, extractThreadIdFromThreadRef, resolveThreadChatId, 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';
24
25
  export { ThreadStatus, threadResource, threadTable, } from './thread.schema.js';
25
26
  export { threadRepository } from './thread.repository.js';
26
27
  export { MessageRole, MessageStatus, messageResource, messageTable, } from './message.schema.js';
@@ -1,6 +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
- chat: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, true, 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>;
4
5
  thread: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
5
6
  maker: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
6
7
  role: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, true, true>;
@@ -23,6 +24,7 @@ export declare const messageRepository: import("@undefineds.co/drizzle-solid/dis
23
24
  updatedAt: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"datetime", null, false, false>;
24
25
  }>>, {
25
26
  id: string;
27
+ scope: string;
26
28
  chat: string;
27
29
  thread: string;
28
30
  maker: string;
@@ -45,9 +47,10 @@ export declare const messageRepository: import("@undefineds.co/drizzle-solid/dis
45
47
  createdAt: Date;
46
48
  updatedAt: Date;
47
49
  }, {
48
- chat: string;
49
50
  content: string;
50
51
  id?: string | undefined;
52
+ scope?: string | undefined;
53
+ chat?: string | undefined;
51
54
  thread?: string | undefined;
52
55
  maker?: string | undefined;
53
56
  role?: string | undefined;
@@ -69,7 +72,8 @@ export declare const messageRepository: import("@undefineds.co/drizzle-solid/dis
69
72
  updatedAt?: Date | undefined;
70
73
  }, {
71
74
  id?: string | null | undefined;
72
- chat?: string | undefined;
75
+ scope?: string | null | undefined;
76
+ chat?: string | null | undefined;
73
77
  thread?: string | null | undefined;
74
78
  maker?: string | null | undefined;
75
79
  role?: string | undefined;
@@ -15,9 +15,13 @@ export declare const MessageStatus: {
15
15
  * Message resource (aligned with xpod).
16
16
  *
17
17
  * Product semantics:
18
- * - Message belongs to a Chat counterpart.
19
- * - Thread is optional and appears only when a message participates in an
20
- * explicit AI/task/branch timeline.
18
+ * - Message belongs to a neutral product scope: usually a Chat, sometimes a
19
+ * Task/thread execution timeline.
20
+ * - Chat remains the Solid Chat compatibility relation. When present, the
21
+ * message is written under the Chat message bucket and projected through
22
+ * wf:message.
23
+ * - Thread is optional and appears when a message participates in an explicit
24
+ * AI/task/branch timeline.
21
25
  *
22
26
  * Storage structure:
23
27
  * - Location: /.data/chat/{chat|id}/{yyyy}/{MM}/{dd}/messages.ttl#{id}
@@ -26,7 +30,8 @@ export declare const MessageStatus: {
26
30
  */
27
31
  export declare const messageResource: import("@undefineds.co/drizzle-solid/dist/core/schema").PodTableWithColumns<import("@undefineds.co/drizzle-solid/dist/core/schema").ResolvedColumns<{
28
32
  id: import("@undefineds.co/drizzle-solid/dist/core/schema").PodStringColumn<false, false>;
29
- chat: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, true, false>;
33
+ scope: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
34
+ chat: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
30
35
  thread: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
31
36
  maker: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
32
37
  role: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, true, true>;
@@ -50,7 +55,8 @@ export declare const messageResource: import("@undefineds.co/drizzle-solid/dist/
50
55
  }>>;
51
56
  export declare const messageTable: import("@undefineds.co/drizzle-solid/dist/core/schema").PodTableWithColumns<import("@undefineds.co/drizzle-solid/dist/core/schema").ResolvedColumns<{
52
57
  id: import("@undefineds.co/drizzle-solid/dist/core/schema").PodStringColumn<false, false>;
53
- chat: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, true, false>;
58
+ scope: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
59
+ chat: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
54
60
  thread: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
55
61
  maker: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
56
62
  role: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, true, true>;
@@ -1,7 +1,8 @@
1
- import { object, podTable, uri, string, text, timestamp, id } from '@undefineds.co/drizzle-solid';
1
+ import { object, podTable, uri, string, text, timestamp, id, renderDefaultIdTemplate } from '@undefineds.co/drizzle-solid';
2
2
  import { UDFS, DCTerms, FOAF, MEETING, SCHEMA, SIOC, WF } from './namespaces.js';
3
3
  import { chatResource } from './chat.schema.js';
4
4
  import { threadResource } from './thread.schema.js';
5
+ import { resourceKey } from './resource-id-defaults.js';
5
6
  export const MessageRole = {
6
7
  USER: 'user',
7
8
  ASSISTANT: 'assistant',
@@ -17,9 +18,13 @@ export const MessageStatus = {
17
18
  * Message resource (aligned with xpod).
18
19
  *
19
20
  * Product semantics:
20
- * - Message belongs to a Chat counterpart.
21
- * - Thread is optional and appears only when a message participates in an
22
- * explicit AI/task/branch timeline.
21
+ * - Message belongs to a neutral product scope: usually a Chat, sometimes a
22
+ * Task/thread execution timeline.
23
+ * - Chat remains the Solid Chat compatibility relation. When present, the
24
+ * message is written under the Chat message bucket and projected through
25
+ * wf:message.
26
+ * - Thread is optional and appears when a message participates in an explicit
27
+ * AI/task/branch timeline.
23
28
  *
24
29
  * Storage structure:
25
30
  * - Location: /.data/chat/{chat|id}/{yyyy}/{MM}/{dd}/messages.ttl#{id}
@@ -27,14 +32,27 @@ export const MessageStatus = {
27
32
  * - chat/thread are RDF URI relations. Short ids remain accepted at API/query call sites via ORM URI template resolution.
28
33
  */
29
34
  export const messageResource = podTable('chat_message', {
30
- id: id('id').default('chat/{chat.key}/{yyyy}/{MM}/{dd}/messages.ttl#{key}'),
31
- // Chat relation. In RDF this is an inverse Solid Chat link: <chat> wf:message <message>.
32
- chat: uri('chat').predicate(WF.message).inverse().notNull().link(chatResource),
35
+ id: id('id').default((key, row) => (renderDefaultIdTemplate(row?.chat
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}', {
40
+ key: resourceKey(key, 'msg'),
41
+ row,
42
+ links: {
43
+ chat: chatResource,
44
+ thread: threadResource,
45
+ },
46
+ }))),
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
+ // Solid Chat compatibility relation. In RDF this is an inverse link: <chat> wf:message <message>.
50
+ chat: uri('chat').predicate(WF.message).inverse().link(chatResource),
33
51
  // Optional Thread relation. In RDF this is an inverse Solid Chat/SIOC link: <thread> sioc:has_member <message>.
34
52
  thread: uri('thread').predicate(SIOC.has_member).inverse().link(threadResource),
35
53
  // maker is the entity URI of the message author:
36
54
  // - User: their WebID (https://user.pod/profile/card#me)
37
- // - AI: Agent URI (/agents/{id}/)
55
+ // - AI: Agent URI (/.data/agents/{id}/index.ttl#this)
38
56
  // - External: Contact URI (/.data/contacts/{id}.ttl#this)
39
57
  // No reference() constraint - accepts any valid URI.
40
58
  maker: uri('maker').predicate(FOAF.maker),
@@ -132,6 +132,7 @@ export const UDFS = createNamespace('udfs', 'https://undefineds.co/ns#', {
132
132
  sortKey: 'sortKey',
133
133
  hasThread: 'hasThread',
134
134
  inThread: 'inThread',
135
+ inScope: 'inScope',
135
136
  participants: 'participants',
136
137
  message: 'message',
137
138
  messageContent: 'messageContent',
@@ -314,9 +315,7 @@ export const UDFS = createNamespace('udfs', 'https://undefineds.co/ns#', {
314
315
  evidenceKind: 'evidenceKind',
315
316
  report: 'report',
316
317
  reportKind: 'reportKind',
317
- subject: 'subject',
318
318
  outcome: 'outcome',
319
- artifact: 'artifact',
320
319
  metricFacts: 'metricFacts',
321
320
  publishedAt: 'publishedAt',
322
321
  reviewer: 'reviewer',
@@ -0,0 +1,19 @@
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 {};
@@ -0,0 +1,68 @@
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
+ }
package/dist/schema.d.ts CHANGED
@@ -116,6 +116,7 @@ export declare const solidResources: {
116
116
  }>>;
117
117
  threadResource: import("@undefineds.co/drizzle-solid/dist/core/schema").PodTableWithColumns<import("@undefineds.co/drizzle-solid/dist/core/schema").ResolvedColumns<{
118
118
  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>;
119
120
  chat: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
120
121
  task: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
121
122
  title: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, false, false>;
@@ -128,7 +129,8 @@ export declare const solidResources: {
128
129
  }>>;
129
130
  messageResource: import("@undefineds.co/drizzle-solid/dist/core/schema").PodTableWithColumns<import("@undefineds.co/drizzle-solid/dist/core/schema").ResolvedColumns<{
130
131
  id: import("@undefineds.co/drizzle-solid/dist/core/schema").PodStringColumn<false, false>;
131
- chat: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, true, false>;
132
+ scope: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
133
+ chat: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
132
134
  thread: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
133
135
  maker: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
134
136
  role: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, true, true>;
@@ -626,6 +628,7 @@ export declare const solidSchema: {
626
628
  }>>;
627
629
  threadTable: import("@undefineds.co/drizzle-solid/dist/core/schema").PodTableWithColumns<import("@undefineds.co/drizzle-solid/dist/core/schema").ResolvedColumns<{
628
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>;
629
632
  chat: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
630
633
  task: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
631
634
  title: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, false, false>;
@@ -638,7 +641,8 @@ export declare const solidSchema: {
638
641
  }>>;
639
642
  messageTable: import("@undefineds.co/drizzle-solid/dist/core/schema").PodTableWithColumns<import("@undefineds.co/drizzle-solid/dist/core/schema").ResolvedColumns<{
640
643
  id: import("@undefineds.co/drizzle-solid/dist/core/schema").PodStringColumn<false, false>;
641
- chat: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, true, false>;
644
+ scope: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
645
+ chat: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
642
646
  thread: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
643
647
  maker: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
644
648
  role: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, true, true>;
@@ -1,6 +1,8 @@
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';
4
6
  /**
5
7
  * Skill binding resource.
6
8
  *
@@ -8,7 +10,10 @@ import { DCTerms, UDFS } from './namespaces.js';
8
10
  * binding and lightweight load metadata; it does not duplicate full skill text.
9
11
  */
10
12
  export const skillResource = podTable('skill', {
11
- id: id('id').default('{agent.key}/skills/{key}/'),
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
+ }),
12
17
  agent: uri('agent').predicate(UDFS.agent).notNull().link(agentResource),
13
18
  root: uri('root').predicate(UDFS.root),
14
19
  name: string('name').predicate(UDFS.name).notNull(),
@@ -22,8 +27,8 @@ export const skillResource = podTable('skill', {
22
27
  createdAt: timestamp('createdAt').predicate(DCTerms.created).notNull().defaultNow(),
23
28
  updatedAt: timestamp('updatedAt').predicate(DCTerms.modified).notNull().defaultNow(),
24
29
  }, {
25
- base: '/agents/',
26
- sparqlEndpoint: '/agents/-/sparql',
30
+ base: '/.data/agents/',
31
+ sparqlEndpoint: '/.data/agents/-/sparql',
27
32
  type: UDFS.Skill,
28
33
  namespace: UDFS,
29
34
  });
@@ -1,5 +1,6 @@
1
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
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>;
3
4
  chat: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
4
5
  task: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
5
6
  title: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, false, false>;
@@ -11,6 +12,7 @@ export declare const threadRepository: import("@undefineds.co/drizzle-solid/dist
11
12
  updatedAt: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"datetime", null, true, true>;
12
13
  }>>, {
13
14
  id: string;
15
+ scope: string;
14
16
  chat: string;
15
17
  task: string;
16
18
  title: string;
@@ -22,6 +24,7 @@ export declare const threadRepository: import("@undefineds.co/drizzle-solid/dist
22
24
  updatedAt: Date;
23
25
  }, {
24
26
  id?: string | undefined;
27
+ scope?: string | undefined;
25
28
  chat?: string | undefined;
26
29
  task?: string | undefined;
27
30
  title?: string | undefined;
@@ -33,6 +36,7 @@ export declare const threadRepository: import("@undefineds.co/drizzle-solid/dist
33
36
  updatedAt?: Date | undefined;
34
37
  }, {
35
38
  id?: string | null | undefined;
39
+ scope?: string | null | undefined;
36
40
  chat?: string | null | undefined;
37
41
  task?: string | null | undefined;
38
42
  title?: string | null | undefined;
@@ -10,6 +10,10 @@ export declare const ThreadStatus: {
10
10
  * Product semantics:
11
11
  * - Thread is the implicit concrete timeline/place under exactly one surface:
12
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.
13
17
  * - AI product runtime sessions map to Thread when they represent a concrete
14
18
  * conversation timeline/place/run.
15
19
  * - Thread carries workspace/place relations and runtime metadata. Chat only
@@ -29,6 +33,7 @@ export declare const ThreadStatus: {
29
33
  */
30
34
  export declare const threadResource: import("@undefineds.co/drizzle-solid/dist/core/schema").PodTableWithColumns<import("@undefineds.co/drizzle-solid/dist/core/schema").ResolvedColumns<{
31
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>;
32
37
  chat: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
33
38
  task: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
34
39
  title: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, false, false>;
@@ -41,6 +46,7 @@ export declare const threadResource: import("@undefineds.co/drizzle-solid/dist/c
41
46
  }>>;
42
47
  export declare const threadTable: import("@undefineds.co/drizzle-solid/dist/core/schema").PodTableWithColumns<import("@undefineds.co/drizzle-solid/dist/core/schema").ResolvedColumns<{
43
48
  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>;
44
50
  chat: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
45
51
  task: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
46
52
  title: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, false, false>;
@@ -14,6 +14,10 @@ export const ThreadStatus = {
14
14
  * Product semantics:
15
15
  * - Thread is the implicit concrete timeline/place under exactly one surface:
16
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.
17
21
  * - AI product runtime sessions map to Thread when they represent a concrete
18
22
  * conversation timeline/place/run.
19
23
  * - Thread carries workspace/place relations and runtime metadata. Chat only
@@ -32,9 +36,11 @@ export const ThreadStatus = {
32
36
  * container URI here and store portable metadata with that container/resource.
33
37
  */
34
38
  export const threadResource = podTable('thread', {
35
- id: id('id').default((key, row) => (renderDefaultIdTemplate(row?.task
36
- ? 'task/{task.key}/index.ttl#{key}'
37
- : 'chat/{chat.key}/index.ttl#{key}', {
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}', {
38
44
  key: resourceKey(key, 'thread'),
39
45
  row,
40
46
  links: {
@@ -42,9 +48,11 @@ export const threadResource = podTable('thread', {
42
48
  task: taskResource,
43
49
  },
44
50
  }))),
45
- // Owner surface for conversation timelines. Do not set together with `task`.
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`.
46
54
  chat: uri('chat').predicate(SIOC.has_parent).link(chatResource),
47
- // Owner surface for task execution timelines. Do not set together with `chat`.
55
+ // Workflow compatibility/index owner for task execution timelines. Do not set together with `chat`.
48
56
  task: uri('task').predicate(UDFS.task).link(taskResource),
49
57
  // Display / state
50
58
  title: string('title').predicate(DCTerms.title),
@@ -1,4 +1,5 @@
1
1
  export declare const MessageVocab: {
2
+ readonly scope: string;
2
3
  readonly thread: string;
3
4
  readonly chat: string;
4
5
  readonly maker: string;
@@ -1,6 +1,7 @@
1
1
  import { DCTerms, FOAF, SCHEMA, SIOC, UDFS, WF } from '../namespaces.js';
2
2
  export const MessageVocab = {
3
3
  // Existing
4
+ scope: UDFS.inScope,
4
5
  thread: SIOC.has_member,
5
6
  chat: WF.message,
6
7
  maker: FOAF.maker,
@@ -1,4 +1,5 @@
1
1
  export declare const ThreadVocab: {
2
+ readonly scope: string;
2
3
  readonly chat: string;
3
4
  readonly task: string;
4
5
  readonly title: string;
@@ -1,5 +1,6 @@
1
1
  import { DCTerms, SIOC, UDFS } from '../namespaces.js';
2
2
  export const ThreadVocab = {
3
+ scope: UDFS.inScope,
3
4
  chat: SIOC.has_parent,
4
5
  task: UDFS.task,
5
6
  title: DCTerms.title,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@undefineds.co/models",
3
- "version": "0.2.36",
3
+ "version": "0.2.38",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -36,9 +36,14 @@ Before adding a schema, decide what the resource fundamentally is.
36
36
  - File-primary resources have a meaningful body: markdown, JSONL, source file,
37
37
  artifact, uploaded file, or generated report. Examples include `Spec`,
38
38
  `Plan`, `Report`, and most `Evidence`.
39
- - For file-primary resources, model the queryable metadata: subject, kind,
40
- status or outcome, actor/reviewer, evidence links, revision, body/artifact
41
- URI, and relations to the controlling issue/task/run.
39
+ - For file-primary resources, model only queryable metadata: aboutness, kind,
40
+ status or outcome, abstract/summary when it is needed for indexing,
41
+ actor/reviewer, evidence links, revision, and relations to the controlling
42
+ issue/task/run.
43
+ - Evidence is file-primary when the proof body is a log, diff, patch,
44
+ transcript, screenshot, or report. Keep the body as a Pod file and store only
45
+ queryable metadata in TTL; use `schema:about` for the control object and
46
+ `dcterms:source` for the concrete evidence file when needed.
42
47
  - Do not create a parallel model just because a file needs metadata. Add schema
43
48
  fields only for facts that must be searched, joined, synchronized, audited, or
44
49
  used for routing/recovery across clients.
@@ -170,8 +175,9 @@ Use these concepts consistently across products:
170
175
  triggered, or one-off task intent.
171
176
  - `Thread`: implicit concrete timeline/place under exactly one command surface:
172
177
  Chat for conversation timelines, or Task for task execution timelines.
173
- - `Message`: human/runtime communication item in a Chat, optionally linked to a
174
- Thread when it participates in an AI/task/branch timeline.
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.
175
181
  - `Run`: one concrete execution attempt by an Agent Runtime.
176
182
  - `RunStep`: append-only execution facts for a Run.
177
183
 
@@ -185,11 +191,18 @@ Align with graph semantics:
185
191
 
186
192
  | Concept | Preferred RDF direction | Typical predicate |
187
193
  |---|---|---|
194
+ | resource belongs to command scope | `resource -> scope` | `udfs:inScope` |
188
195
  | chat contains message | `chat -> message` | `wf:message` / project vocabulary |
196
+ | chat owns thread | `thread -> chat` | `sioc:has_parent` for compatibility |
189
197
  | thread contains message | `thread -> message` | `sioc:has_member` or equivalent |
190
198
  | reply points to original | `replyMessage -> originalMessage` | `sioc:has_reply` / project predicate |
191
199
  | author/maker | `message -> maker` | `foaf:maker` |
192
200
 
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.
205
+
193
206
  When inverse predicates are supported, use them for read/write symmetry. If the
194
207
  ORM cannot safely express the inverse write, put the relation writer in the
195
208
  repository and cover it with integration tests.