@undefineds.co/models 0.2.15 → 0.2.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/README.md +26 -28
  2. package/dist/agent.providers.js +11 -0
  3. package/dist/agent.repository.d.ts +1 -1
  4. package/dist/ai-config/index.d.ts +10 -2
  5. package/dist/ai-config/index.js +53 -15
  6. package/dist/ai-model.schema.js +4 -3
  7. package/dist/ai-provider.schema.js +1 -1
  8. package/dist/approval.schema.d.ts +7 -1
  9. package/dist/approval.schema.js +6 -8
  10. package/dist/audit.presentation.d.ts +23 -0
  11. package/dist/audit.presentation.js +275 -0
  12. package/dist/audit.schema.d.ts +1 -1
  13. package/dist/audit.schema.js +3 -8
  14. package/dist/chat.repository.d.ts +1 -1
  15. package/dist/chat.utils.d.ts +9 -0
  16. package/dist/chat.utils.js +33 -0
  17. package/dist/contact.repository.d.ts +1 -1
  18. package/dist/contact.schema.d.ts +8 -0
  19. package/dist/contact.schema.js +12 -0
  20. package/dist/credential.schema.js +1 -1
  21. package/dist/discovery/models.json +29 -0
  22. package/dist/discovery/providers.json +10 -0
  23. package/dist/grant.schema.d.ts +28 -1
  24. package/dist/grant.schema.js +21 -7
  25. package/dist/index.d.ts +10 -8
  26. package/dist/index.js +9 -7
  27. package/dist/message.repository.d.ts +1 -1
  28. package/dist/namespaces.js +11 -0
  29. package/dist/profile.repository.d.ts +7 -7
  30. package/dist/repository.d.ts +1 -99
  31. package/dist/repository.js +1 -189
  32. package/dist/schema.d.ts +34 -0
  33. package/dist/session/index.d.ts +1 -1
  34. package/dist/session/index.js +1 -1
  35. package/dist/session/session.schema.d.ts +2 -1
  36. package/dist/session/session.schema.js +11 -7
  37. package/dist/session.repository.d.ts +1 -1
  38. package/dist/sidecar/persistence-mapping.d.ts +2 -2
  39. package/dist/sidecar/sidecar-events.d.ts +192 -6
  40. package/dist/sidecar/sidecar-events.js +10 -0
  41. package/dist/thread.repository.d.ts +1 -1
  42. package/dist/vocab/sidecar.vocab.d.ts +17 -0
  43. package/dist/vocab/sidecar.vocab.js +17 -0
  44. package/dist/watch/index.d.ts +55 -0
  45. package/dist/watch/index.js +349 -12
  46. package/package.json +4 -4
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # LinX 数据模型
2
2
 
3
3
  > 基于 Solid Pod 标准的 LinX 核心数据模型定义
4
- >
4
+ >
5
5
  > 使用 `drizzle-solid` ORM,兼容标准 RDF 词汇表(VCARD, FOAF, SIOC, DCTerms 等)
6
6
 
7
7
  ---
@@ -56,6 +56,12 @@ LinX 的所有数据存储在 Solid Pod 中,使用标准的 RDF 格式。本
56
56
  - 新增 shared model 代码优先使用 `chatResource`、`threadResource`、`messageResource`、`sessionResource` 等 Solid resource 命名;`*Table` 只作为兼容 alias 逐步退出。
57
57
  - 如果壳层需要新的查询、upsert、resolve-by-uri、审计或审批状态变更能力,优先在本包新增 repository/helper 和 tests;不要在 CLI/App 中复制 predicate、subject template、Turtle 读写或 shared 状态机。
58
58
  - `approval` / `grant` / `audit` / `inboxNotification` / `session` 等跨端控制面也属于本包的 shared resource 语义。CLI/App 只负责把 Pi/Codex/Claude 等运行时事件映射成本包定义的 insert/update DTO,不能另建一套存储路径或审批策略。
59
+ - approval 的倒计时和可选决策也属于 shared resource 语义:使用 `approvalResource.expiresAt` 表示截止时间,使用 `approvalResource.approvalOptions` 存储上游原生协议给出的可选决策(例如 `allow_once` / `allow_always` / `reject_once`)。CLI/App 不得各自用私有 predicate 或本地状态推断这些字段。
60
+ - grant 是可维护的 LLM Wiki 文档资源,不是一次请求的 fingerprint。`grantResource` 使用 `/settings/autonomy/grants/{id}.ttl`,文档 URI 本身就是 RDF subject;通过 `title/summary/body/schema/pageKind/wikiStatus/tags/source/sourceHash/compiledAt/compiledFrom/related/context` 描述页面语义、来源和上下文。
61
+ - `grant.schema` 使用 `dcterms:conformsTo` 指向 Solid schema/shape URI,例如 `/settings/autonomy/schema/grant.ttl#GrantWikiPage`;它不是 `path`/`wikiPath` 字符串,TTL wiki page 也不需要 `.meta` subject。
62
+ - `grant.target`、`grant.action`、`grant.riskCeiling` 只用于候选筛选或排序,不得作为最终自动审批判定。最终是否覆盖必须看 wiki page 的 `body/summary/tags/source/provenance/context` 与当前请求的语义匹配结果。
63
+ - CLI/App 不得为 shared 控制面字段自定义业务 predicate。新增 approval/grant/audit 字段必须先在本包的 namespace、vocab、schema 和 tests 中定义。
64
+ - structured user-input 与 approval 一样属于 watch 共享协议:AI secretary 只能在答案能从请求上下文明确推出时代答,否则必须回到人工输入。
59
65
 
60
66
  ---
61
67
 
@@ -122,26 +128,26 @@ LinX 遵循 Solid 生态的最佳实践,优先使用标准 RDF 词汇表:
122
128
  givenName: string; // vcard:givenName
123
129
  familyName: string; // vcard:familyName
124
130
  nickname: string; // foaf:nick
125
-
131
+
126
132
  // 联系方式
127
133
  email: string; // vcard:hasEmail
128
134
  telephone: string; // vcard:hasTelephone
129
135
  mobile: string;
130
-
136
+
131
137
  // 地址
132
138
  homeAddress: string; // vcard:hasAddress (JSON)
133
139
  workAddress: string;
134
-
140
+
135
141
  // 组织
136
142
  organization: string; // vcard:organizationName
137
143
  title: string; // vcard:title
138
-
144
+
139
145
  // Solid 身份
140
146
  webId: string; // foaf:weblog
141
-
147
+
142
148
  // AI 联系人
143
149
  aiAssistantId: string; // linx:aiAssistant
144
-
150
+
145
151
  // 关系和标签
146
152
  relationship: string; // vcard:hasRelated
147
153
  tags: string; // dcterms:subject (JSON)
@@ -199,7 +205,7 @@ LinX 遵循 Solid 生态的最佳实践,优先使用标准 RDF 词汇表:
199
205
  modifiedAt: timestamp; // dcterms:modified (编辑时间)
200
206
  deletedAt: timestamp; // linx:deletedAt (软删除)
201
207
  readBy: string; // linx:readBy (JSON)
202
-
208
+
203
209
  // 附件(如果是文件/图片消息)
204
210
  attachmentUri: string; // schema:about
205
211
  attachmentName: string; // schema:name
@@ -254,12 +260,12 @@ LinX 遵循 Solid 生态的最佳实践,优先使用标准 RDF 词汇表:
254
260
  description: string; // dcterms:description
255
261
  favoriteType: string; // linx:favoriteType
256
262
  targetUri: string; // linx:favoriteTarget ⭐ 必需
257
-
263
+
258
264
  // 快照(避免查询原始资源)
259
265
  snapshotContent: string; // schema:text
260
266
  snapshotAuthor: string; // schema:author
261
267
  snapshotCreatedAt: timestamp; // schema:dateCreated
262
-
268
+
263
269
  owner: string; // dcterms:creator
264
270
  folder: string; // linx:conversation
265
271
  tags: string; // dcterms:subject (JSON)
@@ -344,37 +350,37 @@ POD_CACHE_SIZE: "pod.cacheSize"
344
350
  description: string; // dcterms:description
345
351
  avatarUrl: string; // foaf:depiction
346
352
  assistantType: string; // dcterms:type (system, custom, shared)
347
-
353
+
348
354
  // 模型配置
349
355
  provider: string; // linx:aiProvider (openai, anthropic, ollama, custom)
350
356
  modelId: string; // linx:aiModel (gpt-4, claude-3, llama2)
351
357
  systemPrompt: string; // linx:systemPrompt
352
-
358
+
353
359
  // 模型参数
354
360
  temperature: float; // linx:temperature (0-2)
355
361
  maxTokens: integer; // linx:maxTokens
356
362
  topP: float;
357
363
  frequencyPenalty: float;
358
364
  presencePenalty: float;
359
-
365
+
360
366
  // 功能配置
361
367
  enableStreaming: boolean;
362
368
  enableFunctionCalling: boolean;
363
369
  allowedFunctions: string; // JSON 数组
364
-
370
+
365
371
  // Pod 访问权限
366
372
  podAccessLevel: string; // linx:status (read, write, full)
367
373
  allowedContainers: string; // JSON 数组
368
-
374
+
369
375
  // 共享
370
376
  owner: string; // dcterms:creator
371
377
  isPublic: boolean;
372
378
  sharedWith: string; // JSON
373
-
379
+
374
380
  // 统计
375
381
  messageCount: integer;
376
382
  lastUsedAt: timestamp;
377
-
383
+
378
384
  status: string; // linx:status (active, disabled, archived)
379
385
  createdAt: timestamp;
380
386
  modifiedAt: timestamp;
@@ -401,7 +407,7 @@ yarn workspace @linq/models install
401
407
  import {
402
408
  // 词汇表
403
409
  LINQ, SIOC, DCTerms, SCHEMA,
404
-
410
+
405
411
  // 模型表
406
412
  contactTable,
407
413
  chatTable,
@@ -410,12 +416,12 @@ import {
410
416
  favoriteTable,
411
417
  settingsTable,
412
418
  aiAssistantTable,
413
-
419
+
414
420
  // 类型
415
421
  type ContactRow,
416
422
  type ChatRow,
417
423
  type MessageRow,
418
-
424
+
419
425
  // 常量
420
426
  CONTACT_TYPES,
421
427
  SETTING_KEYS,
@@ -585,11 +591,3 @@ yarn workspace @linq/models typecheck
585
591
  ## 许可证
586
592
 
587
593
  MIT License
588
-
589
-
590
-
591
-
592
-
593
-
594
-
595
-
@@ -1,4 +1,15 @@
1
1
  export const DEFAULT_AGENT_PROVIDERS = [
2
+ {
3
+ slug: 'undefineds',
4
+ displayName: 'undefineds',
5
+ baseUrl: 'https://api.undefineds.co/v1',
6
+ homepage: 'https://undefineds.co/linx',
7
+ logoUrl: 'https://undefineds.co/favicon.ico',
8
+ models: [
9
+ { id: 'linx-lite', displayName: 'LinX Lite' },
10
+ { id: 'linx', displayName: 'LinX' },
11
+ ],
12
+ },
2
13
  {
3
14
  slug: 'openai',
4
15
  displayName: 'OpenAI',
@@ -1,4 +1,4 @@
1
- export declare const agentRepository: import("./repository").PodRepositoryDescriptor<import("@undefineds.co/drizzle-solid/dist/core/schema").PodTableWithColumns<import("@undefineds.co/drizzle-solid/dist/core/schema").ResolvedColumns<{
1
+ export declare const agentRepository: 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
3
  name: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, true, false>;
4
4
  description: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, false, false>;
@@ -43,16 +43,24 @@ export interface AIConfigMutationPlan {
43
43
  modelUpserts: AIModelInsert[];
44
44
  modelDeleteIds: string[];
45
45
  }
46
+ export declare const UNDEFINEDS_AI_PROVIDER_ID = "undefineds";
47
+ export declare const UNDEFINEDS_AI_PROVIDER_DISPLAY_NAME = "undefineds";
48
+ export declare const UNDEFINEDS_AI_BASE_URL = "https://api.undefineds.co/v1";
49
+ export declare const LINX_LITE_MODEL_ID = "linx-lite";
50
+ export declare const LINX_MODEL_ID = "linx";
51
+ export declare const DEFAULT_LINX_MODEL_ID = "linx-lite";
52
+ export declare const UNDEFINEDS_AI_MODEL_IDS: readonly ["linx-lite", "linx"];
46
53
  export declare function getAIConfigProviderCatalog(): readonly AIConfigProviderCatalogEntry[];
47
54
  export declare function getAIConfigProviderMetadata(providerId: string): AIConfigProviderCatalogEntry;
48
55
  export declare function normalizeAIConfigResourceId(raw?: string | null): string;
56
+ export declare function normalizeAIConfigModelId(raw?: string | null, providerId?: string | null): string;
49
57
  export declare function normalizeAIConfigProviderId(raw?: string | null): string;
50
58
  export declare function sameAIConfigProviderFamily(left?: string | null, right?: string | null): boolean;
51
59
  export declare function getAIConfigProviderFamilyIds(providerId: string): string[];
52
60
  export declare function getAIConfigDefaultBaseUrl(providerId: string): string | undefined;
53
61
  export declare function getDefaultAIConfigCredentialId(providerId: string): string;
54
- export declare function aiConfigProviderUri(providerId: string): string;
55
- export declare function aiConfigModelUri(modelId: string): string;
62
+ export declare function aiConfigProviderRef(providerId: string): string;
63
+ export declare function aiConfigModelRef(providerId: string, modelId?: string): string;
56
64
  export declare function buildAIConfigProviderStateMap(options: BuildAIConfigProviderStateMapOptions): Record<string, AIConfigProviderState>;
57
65
  export declare function buildAIConfigMutationPlan(input: {
58
66
  providerId: string;
@@ -1,3 +1,10 @@
1
+ export const UNDEFINEDS_AI_PROVIDER_ID = 'undefineds';
2
+ export const UNDEFINEDS_AI_PROVIDER_DISPLAY_NAME = 'undefineds';
3
+ export const UNDEFINEDS_AI_BASE_URL = 'https://api.undefineds.co/v1';
4
+ export const LINX_LITE_MODEL_ID = 'linx-lite';
5
+ export const LINX_MODEL_ID = 'linx';
6
+ export const DEFAULT_LINX_MODEL_ID = LINX_LITE_MODEL_ID;
7
+ export const UNDEFINEDS_AI_MODEL_IDS = [LINX_LITE_MODEL_ID, LINX_MODEL_ID];
1
8
  const AI_CONFIG_PROVIDER_CATALOG = [
2
9
  {
3
10
  id: 'openai',
@@ -69,6 +76,7 @@ const AI_CONFIG_PROVIDER_CATALOG = [
69
76
  },
70
77
  ];
71
78
  const AI_CONFIG_PROVIDER_MAP = new Map(AI_CONFIG_PROVIDER_CATALOG.map((entry) => [entry.id, entry]));
79
+ const ABSOLUTE_IRI = /^[a-zA-Z][a-zA-Z\d+.-]*:/;
72
80
  function normalizeText(value) {
73
81
  return value.trim().toLowerCase();
74
82
  }
@@ -111,12 +119,34 @@ export function getAIConfigProviderMetadata(providerId) {
111
119
  export function normalizeAIConfigResourceId(raw) {
112
120
  if (!raw)
113
121
  return '';
114
- if (raw.includes('#'))
115
- return raw.split('#').pop() || raw;
116
- const clean = raw.replace(/\/$/, '');
122
+ const value = raw.trim();
123
+ if (!value)
124
+ return '';
125
+ if (value.includes('#'))
126
+ return value.split('#').pop() || value;
127
+ if (!ABSOLUTE_IRI.test(value) && !value.endsWith('.ttl')) {
128
+ return value;
129
+ }
130
+ const clean = value.replace(/\/$/, '');
131
+ if (!ABSOLUTE_IRI.test(value)) {
132
+ return clean.endsWith('.ttl') ? clean.slice(0, -4) : clean;
133
+ }
117
134
  const tail = clean.split('/').pop() || clean;
118
135
  return tail.endsWith('.ttl') ? tail.slice(0, -4) : tail;
119
136
  }
137
+ export function normalizeAIConfigModelId(raw, providerId) {
138
+ const modelId = normalizeAIConfigResourceId(raw);
139
+ if (!modelId.includes('/'))
140
+ return modelId;
141
+ const [prefix, ...rest] = modelId.split('/');
142
+ if (rest.length === 0)
143
+ return modelId;
144
+ if (!providerId)
145
+ return modelId;
146
+ return normalizeAIConfigProviderId(prefix) === normalizeAIConfigProviderId(providerId)
147
+ ? rest.join('/')
148
+ : modelId;
149
+ }
120
150
  export function normalizeAIConfigProviderId(raw) {
121
151
  const normalized = normalizeText(normalizeAIConfigResourceId(raw));
122
152
  if (!normalized)
@@ -128,6 +158,9 @@ export function normalizeAIConfigProviderId(raw) {
128
158
  }
129
159
  return normalized;
130
160
  }
161
+ function normalizeAIConfigModelStorageId(raw, providerId) {
162
+ return normalizeAIConfigModelId(raw, providerId);
163
+ }
131
164
  export function sameAIConfigProviderFamily(left, right) {
132
165
  const normalizedLeft = normalizeAIConfigProviderId(left);
133
166
  const normalizedRight = normalizeAIConfigProviderId(right);
@@ -143,11 +176,16 @@ export function getAIConfigDefaultBaseUrl(providerId) {
143
176
  export function getDefaultAIConfigCredentialId(providerId) {
144
177
  return `${normalizeAIConfigProviderId(providerId)}-default`;
145
178
  }
146
- export function aiConfigProviderUri(providerId) {
147
- return `/settings/ai/providers.ttl#${normalizeAIConfigProviderId(providerId)}`;
179
+ export function aiConfigProviderRef(providerId) {
180
+ return normalizeAIConfigProviderId(providerId);
148
181
  }
149
- export function aiConfigModelUri(modelId) {
150
- return `/settings/ai/models.ttl#${normalizeAIConfigResourceId(modelId)}`;
182
+ export function aiConfigModelRef(providerId, modelId) {
183
+ if (modelId === undefined) {
184
+ return normalizeAIConfigResourceId(providerId);
185
+ }
186
+ const provider = normalizeAIConfigProviderId(providerId);
187
+ const model = normalizeAIConfigModelStorageId(modelId, provider);
188
+ return provider && model ? `/settings/ai/models/${provider}.ttl#${model}` : model;
151
189
  }
152
190
  export function buildAIConfigProviderStateMap(options) {
153
191
  const catalog = options.catalog ?? AI_CONFIG_PROVIDER_CATALOG;
@@ -175,7 +213,7 @@ export function buildAIConfigProviderStateMap(options) {
175
213
  const providerId = normalizeAIConfigProviderId(String(row.isProvidedBy ?? ''));
176
214
  if (!providerId)
177
215
  continue;
178
- const modelId = normalizeAIConfigResourceId(String(row.id ?? row['@id'] ?? ''));
216
+ const modelId = normalizeAIConfigModelStorageId(String(row.id ?? row['@id'] ?? ''), providerId);
179
217
  if (!modelId)
180
218
  continue;
181
219
  const list = modelMap.get(providerId) ?? [];
@@ -213,7 +251,7 @@ export function buildAIConfigProviderStateMap(options) {
213
251
  enabled: true,
214
252
  capabilities: [],
215
253
  }));
216
- const selectedModelId = normalizeAIConfigResourceId(typeof providerRow?.hasModel === 'string' ? providerRow.hasModel : '') || preferredSelectedModelId(models);
254
+ const selectedModelId = normalizeAIConfigModelStorageId(typeof providerRow?.hasModel === 'string' ? providerRow.hasModel : '', providerId) || preferredSelectedModelId(models);
217
255
  states[providerId] = {
218
256
  id: providerId,
219
257
  enabled: (typeof credentialRow?.status === 'string' ? credentialRow.status : 'inactive') === 'active',
@@ -241,21 +279,21 @@ export function buildAIConfigMutationPlan(input) {
241
279
  if (hasConfigUpdate || input.updates.models !== undefined) {
242
280
  const selectedModelId = input.updates.models
243
281
  ? preferredSelectedModelId(input.updates.models)
244
- : normalizeAIConfigResourceId(typeof existingProvider?.hasModel === 'string' ? existingProvider.hasModel : '');
282
+ : normalizeAIConfigModelStorageId(typeof existingProvider?.hasModel === 'string' ? existingProvider.hasModel : '', providerId);
245
283
  providerPayload = {
246
284
  id: providerId,
247
285
  baseUrl: input.updates.baseUrl ??
248
286
  (typeof existingProvider?.baseUrl === 'string' ? existingProvider.baseUrl : undefined) ??
249
287
  metadata.defaultBaseUrl,
250
288
  proxyUrl: typeof existingProvider?.proxyUrl === 'string' ? existingProvider.proxyUrl : undefined,
251
- hasModel: selectedModelId ? aiConfigModelUri(selectedModelId) : undefined,
289
+ hasModel: selectedModelId ? aiConfigModelRef(providerId, selectedModelId) : undefined,
252
290
  };
253
291
  }
254
292
  if (hasConfigUpdate) {
255
293
  credentialPayload = {
256
294
  id: normalizeAIConfigResourceId(typeof existingCredential?.id === 'string' ? existingCredential.id : '') ||
257
295
  getDefaultAIConfigCredentialId(providerId),
258
- provider: aiConfigProviderUri(providerId),
296
+ provider: aiConfigProviderRef(providerId),
259
297
  service: typeof existingCredential?.service === 'string' && existingCredential.service ? existingCredential.service : 'ai',
260
298
  status: input.updates.enabled !== undefined
261
299
  ? input.updates.enabled
@@ -275,12 +313,12 @@ export function buildAIConfigMutationPlan(input) {
275
313
  }
276
314
  if (input.updates.models !== undefined) {
277
315
  const existingById = new Map(existingModels.map((row) => [
278
- normalizeAIConfigResourceId(String(row.id ?? row['@id'] ?? '')),
316
+ normalizeAIConfigModelStorageId(String(row.id ?? row['@id'] ?? ''), providerId),
279
317
  row,
280
318
  ]));
281
319
  const nextIds = new Set();
282
320
  for (const model of input.updates.models) {
283
- const modelId = normalizeAIConfigResourceId(model.id);
321
+ const modelId = normalizeAIConfigModelStorageId(model.id, providerId);
284
322
  if (!modelId)
285
323
  continue;
286
324
  nextIds.add(modelId);
@@ -290,7 +328,7 @@ export function buildAIConfigMutationPlan(input) {
290
328
  id: modelId,
291
329
  displayName: model.name || modelId,
292
330
  modelType: 'chat',
293
- isProvidedBy: aiConfigProviderUri(providerId),
331
+ isProvidedBy: aiConfigProviderRef(providerId),
294
332
  status: model.enabled ? 'active' : 'inactive',
295
333
  createdAt: existingDate(existing?.createdAt) ?? now,
296
334
  updatedAt: now,
@@ -1,17 +1,18 @@
1
1
  import { id, integer, podTable, string, timestamp, uri } from "@undefineds.co/drizzle-solid";
2
+ import { aiProviderTable } from "./ai-provider.schema.js";
2
3
  import { XPOD_AI } from "./namespaces.js";
3
4
  export const aiModelTable = podTable("aiModel", {
4
5
  id: id("id"),
5
6
  displayName: string("displayName").predicate(XPOD_AI.displayName),
6
7
  modelType: string("modelType").predicate(XPOD_AI.modelType).default("chat"),
7
- isProvidedBy: uri("isProvidedBy").predicate(XPOD_AI.isProvidedBy),
8
+ isProvidedBy: uri("isProvidedBy").predicate(XPOD_AI.isProvidedBy).link(aiProviderTable),
8
9
  dimension: integer("dimension").predicate(XPOD_AI.dimension),
9
10
  status: string("status").predicate(XPOD_AI.status).default("active"),
10
11
  createdAt: timestamp("createdAt").predicate(XPOD_AI.createdAt).notNull().defaultNow(),
11
12
  updatedAt: timestamp("updatedAt").predicate(XPOD_AI.updatedAt).notNull().defaultNow(),
12
13
  }, {
13
- base: "/settings/ai/models.ttl",
14
+ base: "/settings/ai/models/",
14
15
  type: XPOD_AI.Model,
15
16
  namespace: XPOD_AI,
16
- subjectTemplate: "#{id}",
17
+ subjectTemplate: "{isProvidedBy|id}.ttl#{id}",
17
18
  });
@@ -4,7 +4,7 @@ export const aiProviderTable = podTable("aiProvider", {
4
4
  id: id("id"),
5
5
  baseUrl: string("baseUrl").predicate(XPOD_AI.baseUrl),
6
6
  proxyUrl: string("proxyUrl").predicate(XPOD_AI.proxyUrl),
7
- hasModel: uri("hasModel").predicate(XPOD_AI.hasModel),
7
+ hasModel: uri("hasModel").predicate(XPOD_AI.hasModel).link("aiModel"),
8
8
  }, {
9
9
  base: "/settings/ai/providers.ttl",
10
10
  type: XPOD_AI.Provider,
@@ -1,4 +1,4 @@
1
- export declare function buildApprovalSubjectPath(approvalId: string, createdAt?: Date | string | number): string;
1
+ export declare function extractApprovalIdFromApprovalRef(approvalRef: string | null | undefined): string | null;
2
2
  export declare const approvalResource: import("@undefineds.co/drizzle-solid/dist/core/schema").PodTableWithColumns<import("@undefineds.co/drizzle-solid/dist/core/schema").ResolvedColumns<{
3
3
  id: import("@undefineds.co/drizzle-solid/dist/core/schema").PodStringColumn<false, false>;
4
4
  session: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, true, false>;
@@ -13,8 +13,11 @@ export declare const approvalResource: import("@undefineds.co/drizzle-solid/dist
13
13
  decisionRole: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, false, false>;
14
14
  onBehalfOf: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
15
15
  reason: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, false, false>;
16
+ context: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, false, false>;
17
+ approvalOptions: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, false, false>;
16
18
  policyVersion: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, false, false>;
17
19
  createdAt: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"datetime", null, true, true>;
20
+ expiresAt: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"datetime", null, false, false>;
18
21
  resolvedAt: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"datetime", null, false, false>;
19
22
  }>>;
20
23
  export declare const approvalTable: import("@undefineds.co/drizzle-solid/dist/core/schema").PodTableWithColumns<import("@undefineds.co/drizzle-solid/dist/core/schema").ResolvedColumns<{
@@ -31,8 +34,11 @@ export declare const approvalTable: import("@undefineds.co/drizzle-solid/dist/co
31
34
  decisionRole: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, false, false>;
32
35
  onBehalfOf: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"uri", null, false, false>;
33
36
  reason: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, false, false>;
37
+ context: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, false, false>;
38
+ approvalOptions: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, false, false>;
34
39
  policyVersion: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"string", null, false, false>;
35
40
  createdAt: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"datetime", null, true, true>;
41
+ expiresAt: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"datetime", null, false, false>;
36
42
  resolvedAt: import("@undefineds.co/drizzle-solid/dist/core/schema").ColumnBuilder<"datetime", null, false, false>;
37
43
  }>>;
38
44
  export type ApprovalRow = typeof approvalResource.$inferSelect;
@@ -1,12 +1,7 @@
1
- import { podTable, uri, string, text, timestamp, id } from '@undefineds.co/drizzle-solid';
1
+ import { extractPodResourceTemplateValue, podTable, uri, string, text, timestamp, id } from '@undefineds.co/drizzle-solid';
2
2
  import { ODRL, UDFS, DCTerms } from './namespaces.js';
3
- export function buildApprovalSubjectPath(approvalId, createdAt = new Date()) {
4
- const date = createdAt instanceof Date ? createdAt : new Date(createdAt);
5
- const safeDate = Number.isFinite(date.getTime()) ? date : new Date();
6
- const yyyy = String(safeDate.getUTCFullYear());
7
- const mm = String(safeDate.getUTCMonth() + 1).padStart(2, '0');
8
- const dd = String(safeDate.getUTCDate()).padStart(2, '0');
9
- return `/.data/approvals/${yyyy}/${mm}/${dd}.ttl#${encodeURIComponent(approvalId)}`;
3
+ export function extractApprovalIdFromApprovalRef(approvalRef) {
4
+ return extractPodResourceTemplateValue(approvalResource, approvalRef);
10
5
  }
11
6
  // Approval request resource (separate from Solid inbox notifications).
12
7
  export const approvalResource = podTable('approval', {
@@ -28,9 +23,12 @@ export const approvalResource = podTable('approval', {
28
23
  decisionRole: string('decisionRole').predicate(UDFS.decisionRole),
29
24
  onBehalfOf: uri('onBehalfOf').predicate(UDFS.onBehalfOf),
30
25
  reason: text('reason').predicate(UDFS.reason),
26
+ context: text('context').predicate(UDFS.context),
27
+ approvalOptions: text('approvalOptions').predicate(UDFS.approvalOptions),
31
28
  policyVersion: string('policyVersion').predicate(UDFS.policyVersion),
32
29
  // Timestamps
33
30
  createdAt: timestamp('createdAt').predicate(DCTerms.created).notNull().defaultNow(),
31
+ expiresAt: timestamp('expiresAt').predicate(UDFS.expiresAt),
34
32
  resolvedAt: timestamp('resolvedAt').predicate(UDFS.resolvedAt),
35
33
  }, {
36
34
  base: '/.data/approvals/',
@@ -0,0 +1,23 @@
1
+ import type { ApprovalRow } from './approval.schema';
2
+ import type { AuditRow } from './audit.schema';
3
+ export interface AuditPresentation {
4
+ title: string;
5
+ description: string;
6
+ category: 'auth_required' | 'audit';
7
+ status?: string;
8
+ chatId: string | null;
9
+ threadId: string | null;
10
+ thread: string | null;
11
+ about: string | null;
12
+ authUrl: string | null;
13
+ authMethod: string | null;
14
+ authMessage: string | null;
15
+ actorRoleLabel: string;
16
+ }
17
+ type RelatedApproval = Pick<ApprovalRow, 'target' | 'toolName' | 'risk' | 'reason' | 'status' | 'context'> | null | undefined;
18
+ export declare function buildAuditDetailRecord(audit: AuditRow, relatedApproval?: RelatedApproval): Record<string, unknown>;
19
+ export declare function formatInboxStatusLabel(status?: string | null): string | null;
20
+ export declare function formatAuditActorRole(role?: string | null): string;
21
+ export declare function createResolvedAuthTimestampsIndex(audits: AuditRow[]): Map<string, number[]>;
22
+ export declare function buildAuditPresentation(audit: AuditRow, resolvedAuthTimestampsByKey: Map<string, number[]>, relatedApproval?: RelatedApproval): AuditPresentation;
23
+ export {};