@undefineds.co/models 0.2.16 → 0.2.19
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 +20 -24
- package/dist/agent.repository.d.ts +1 -1
- package/dist/ai-config/index.d.ts +3 -2
- package/dist/ai-config/index.js +46 -21
- package/dist/ai-model.schema.js +4 -3
- package/dist/ai-provider.schema.js +1 -1
- package/dist/approval.schema.d.ts +1 -1
- package/dist/approval.schema.js +3 -8
- package/dist/audit.presentation.d.ts +23 -0
- package/dist/audit.presentation.js +275 -0
- package/dist/audit.schema.d.ts +1 -1
- package/dist/audit.schema.js +3 -8
- package/dist/chat.repository.d.ts +1 -1
- package/dist/chat.utils.d.ts +9 -0
- package/dist/chat.utils.js +33 -0
- package/dist/contact.repository.d.ts +1 -1
- package/dist/contact.schema.d.ts +8 -0
- package/dist/contact.schema.js +12 -0
- package/dist/credential.schema.js +1 -1
- package/dist/grant.schema.d.ts +0 -1
- package/dist/grant.schema.js +0 -3
- package/dist/index.d.ts +9 -7
- package/dist/index.js +9 -7
- package/dist/message.repository.d.ts +1 -1
- package/dist/profile.repository.d.ts +7 -7
- package/dist/repository.d.ts +1 -99
- package/dist/repository.js +1 -189
- package/dist/session/index.d.ts +1 -1
- package/dist/session/index.js +1 -1
- package/dist/session/session.schema.d.ts +2 -1
- package/dist/session/session.schema.js +11 -7
- package/dist/session.repository.d.ts +1 -1
- package/dist/sidecar/sidecar-events.d.ts +6 -6
- package/dist/thread.repository.d.ts +1 -1
- package/dist/watch/index.js +0 -10
- package/package.json +2 -2
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
|
---
|
|
@@ -128,26 +128,26 @@ LinX 遵循 Solid 生态的最佳实践,优先使用标准 RDF 词汇表:
|
|
|
128
128
|
givenName: string; // vcard:givenName
|
|
129
129
|
familyName: string; // vcard:familyName
|
|
130
130
|
nickname: string; // foaf:nick
|
|
131
|
-
|
|
131
|
+
|
|
132
132
|
// 联系方式
|
|
133
133
|
email: string; // vcard:hasEmail
|
|
134
134
|
telephone: string; // vcard:hasTelephone
|
|
135
135
|
mobile: string;
|
|
136
|
-
|
|
136
|
+
|
|
137
137
|
// 地址
|
|
138
138
|
homeAddress: string; // vcard:hasAddress (JSON)
|
|
139
139
|
workAddress: string;
|
|
140
|
-
|
|
140
|
+
|
|
141
141
|
// 组织
|
|
142
142
|
organization: string; // vcard:organizationName
|
|
143
143
|
title: string; // vcard:title
|
|
144
|
-
|
|
144
|
+
|
|
145
145
|
// Solid 身份
|
|
146
146
|
webId: string; // foaf:weblog
|
|
147
|
-
|
|
147
|
+
|
|
148
148
|
// AI 联系人
|
|
149
149
|
aiAssistantId: string; // linx:aiAssistant
|
|
150
|
-
|
|
150
|
+
|
|
151
151
|
// 关系和标签
|
|
152
152
|
relationship: string; // vcard:hasRelated
|
|
153
153
|
tags: string; // dcterms:subject (JSON)
|
|
@@ -205,7 +205,7 @@ LinX 遵循 Solid 生态的最佳实践,优先使用标准 RDF 词汇表:
|
|
|
205
205
|
modifiedAt: timestamp; // dcterms:modified (编辑时间)
|
|
206
206
|
deletedAt: timestamp; // linx:deletedAt (软删除)
|
|
207
207
|
readBy: string; // linx:readBy (JSON)
|
|
208
|
-
|
|
208
|
+
|
|
209
209
|
// 附件(如果是文件/图片消息)
|
|
210
210
|
attachmentUri: string; // schema:about
|
|
211
211
|
attachmentName: string; // schema:name
|
|
@@ -260,12 +260,12 @@ LinX 遵循 Solid 生态的最佳实践,优先使用标准 RDF 词汇表:
|
|
|
260
260
|
description: string; // dcterms:description
|
|
261
261
|
favoriteType: string; // linx:favoriteType
|
|
262
262
|
targetUri: string; // linx:favoriteTarget ⭐ 必需
|
|
263
|
-
|
|
263
|
+
|
|
264
264
|
// 快照(避免查询原始资源)
|
|
265
265
|
snapshotContent: string; // schema:text
|
|
266
266
|
snapshotAuthor: string; // schema:author
|
|
267
267
|
snapshotCreatedAt: timestamp; // schema:dateCreated
|
|
268
|
-
|
|
268
|
+
|
|
269
269
|
owner: string; // dcterms:creator
|
|
270
270
|
folder: string; // linx:conversation
|
|
271
271
|
tags: string; // dcterms:subject (JSON)
|
|
@@ -350,37 +350,37 @@ POD_CACHE_SIZE: "pod.cacheSize"
|
|
|
350
350
|
description: string; // dcterms:description
|
|
351
351
|
avatarUrl: string; // foaf:depiction
|
|
352
352
|
assistantType: string; // dcterms:type (system, custom, shared)
|
|
353
|
-
|
|
353
|
+
|
|
354
354
|
// 模型配置
|
|
355
355
|
provider: string; // linx:aiProvider (openai, anthropic, ollama, custom)
|
|
356
356
|
modelId: string; // linx:aiModel (gpt-4, claude-3, llama2)
|
|
357
357
|
systemPrompt: string; // linx:systemPrompt
|
|
358
|
-
|
|
358
|
+
|
|
359
359
|
// 模型参数
|
|
360
360
|
temperature: float; // linx:temperature (0-2)
|
|
361
361
|
maxTokens: integer; // linx:maxTokens
|
|
362
362
|
topP: float;
|
|
363
363
|
frequencyPenalty: float;
|
|
364
364
|
presencePenalty: float;
|
|
365
|
-
|
|
365
|
+
|
|
366
366
|
// 功能配置
|
|
367
367
|
enableStreaming: boolean;
|
|
368
368
|
enableFunctionCalling: boolean;
|
|
369
369
|
allowedFunctions: string; // JSON 数组
|
|
370
|
-
|
|
370
|
+
|
|
371
371
|
// Pod 访问权限
|
|
372
372
|
podAccessLevel: string; // linx:status (read, write, full)
|
|
373
373
|
allowedContainers: string; // JSON 数组
|
|
374
|
-
|
|
374
|
+
|
|
375
375
|
// 共享
|
|
376
376
|
owner: string; // dcterms:creator
|
|
377
377
|
isPublic: boolean;
|
|
378
378
|
sharedWith: string; // JSON
|
|
379
|
-
|
|
379
|
+
|
|
380
380
|
// 统计
|
|
381
381
|
messageCount: integer;
|
|
382
382
|
lastUsedAt: timestamp;
|
|
383
|
-
|
|
383
|
+
|
|
384
384
|
status: string; // linx:status (active, disabled, archived)
|
|
385
385
|
createdAt: timestamp;
|
|
386
386
|
modifiedAt: timestamp;
|
|
@@ -407,7 +407,7 @@ yarn workspace @linq/models install
|
|
|
407
407
|
import {
|
|
408
408
|
// 词汇表
|
|
409
409
|
LINQ, SIOC, DCTerms, SCHEMA,
|
|
410
|
-
|
|
410
|
+
|
|
411
411
|
// 模型表
|
|
412
412
|
contactTable,
|
|
413
413
|
chatTable,
|
|
@@ -416,12 +416,12 @@ import {
|
|
|
416
416
|
favoriteTable,
|
|
417
417
|
settingsTable,
|
|
418
418
|
aiAssistantTable,
|
|
419
|
-
|
|
419
|
+
|
|
420
420
|
// 类型
|
|
421
421
|
type ContactRow,
|
|
422
422
|
type ChatRow,
|
|
423
423
|
type MessageRow,
|
|
424
|
-
|
|
424
|
+
|
|
425
425
|
// 常量
|
|
426
426
|
CONTACT_TYPES,
|
|
427
427
|
SETTING_KEYS,
|
|
@@ -591,7 +591,3 @@ yarn workspace @linq/models typecheck
|
|
|
591
591
|
## 许可证
|
|
592
592
|
|
|
593
593
|
MIT License
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const agentRepository: import("
|
|
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>;
|
|
@@ -53,13 +53,14 @@ export declare const UNDEFINEDS_AI_MODEL_IDS: readonly ["linx-lite", "linx"];
|
|
|
53
53
|
export declare function getAIConfigProviderCatalog(): readonly AIConfigProviderCatalogEntry[];
|
|
54
54
|
export declare function getAIConfigProviderMetadata(providerId: string): AIConfigProviderCatalogEntry;
|
|
55
55
|
export declare function normalizeAIConfigResourceId(raw?: string | null): string;
|
|
56
|
+
export declare function normalizeAIConfigModelId(raw?: string | null, providerId?: string | null): string;
|
|
56
57
|
export declare function normalizeAIConfigProviderId(raw?: string | null): string;
|
|
57
58
|
export declare function sameAIConfigProviderFamily(left?: string | null, right?: string | null): boolean;
|
|
58
59
|
export declare function getAIConfigProviderFamilyIds(providerId: string): string[];
|
|
59
60
|
export declare function getAIConfigDefaultBaseUrl(providerId: string): string | undefined;
|
|
60
61
|
export declare function getDefaultAIConfigCredentialId(providerId: string): string;
|
|
61
|
-
export declare function
|
|
62
|
-
export declare function
|
|
62
|
+
export declare function aiConfigProviderRef(providerId: string): string;
|
|
63
|
+
export declare function aiConfigModelRef(providerId: string, modelId?: string): string;
|
|
63
64
|
export declare function buildAIConfigProviderStateMap(options: BuildAIConfigProviderStateMapOptions): Record<string, AIConfigProviderState>;
|
|
64
65
|
export declare function buildAIConfigMutationPlan(input: {
|
|
65
66
|
providerId: string;
|
package/dist/ai-config/index.js
CHANGED
|
@@ -6,12 +6,6 @@ export const LINX_MODEL_ID = 'linx';
|
|
|
6
6
|
export const DEFAULT_LINX_MODEL_ID = LINX_LITE_MODEL_ID;
|
|
7
7
|
export const UNDEFINEDS_AI_MODEL_IDS = [LINX_LITE_MODEL_ID, LINX_MODEL_ID];
|
|
8
8
|
const AI_CONFIG_PROVIDER_CATALOG = [
|
|
9
|
-
{
|
|
10
|
-
id: UNDEFINEDS_AI_PROVIDER_ID,
|
|
11
|
-
displayName: UNDEFINEDS_AI_PROVIDER_DISPLAY_NAME,
|
|
12
|
-
defaultBaseUrl: UNDEFINEDS_AI_BASE_URL,
|
|
13
|
-
defaultModels: [...UNDEFINEDS_AI_MODEL_IDS],
|
|
14
|
-
},
|
|
15
9
|
{
|
|
16
10
|
id: 'openai',
|
|
17
11
|
displayName: 'OpenAI',
|
|
@@ -82,6 +76,7 @@ const AI_CONFIG_PROVIDER_CATALOG = [
|
|
|
82
76
|
},
|
|
83
77
|
];
|
|
84
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+.-]*:/;
|
|
85
80
|
function normalizeText(value) {
|
|
86
81
|
return value.trim().toLowerCase();
|
|
87
82
|
}
|
|
@@ -124,12 +119,34 @@ export function getAIConfigProviderMetadata(providerId) {
|
|
|
124
119
|
export function normalizeAIConfigResourceId(raw) {
|
|
125
120
|
if (!raw)
|
|
126
121
|
return '';
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
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
|
+
}
|
|
130
134
|
const tail = clean.split('/').pop() || clean;
|
|
131
135
|
return tail.endsWith('.ttl') ? tail.slice(0, -4) : tail;
|
|
132
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
|
+
}
|
|
133
150
|
export function normalizeAIConfigProviderId(raw) {
|
|
134
151
|
const normalized = normalizeText(normalizeAIConfigResourceId(raw));
|
|
135
152
|
if (!normalized)
|
|
@@ -141,6 +158,9 @@ export function normalizeAIConfigProviderId(raw) {
|
|
|
141
158
|
}
|
|
142
159
|
return normalized;
|
|
143
160
|
}
|
|
161
|
+
function normalizeAIConfigModelStorageId(raw, providerId) {
|
|
162
|
+
return normalizeAIConfigModelId(raw, providerId);
|
|
163
|
+
}
|
|
144
164
|
export function sameAIConfigProviderFamily(left, right) {
|
|
145
165
|
const normalizedLeft = normalizeAIConfigProviderId(left);
|
|
146
166
|
const normalizedRight = normalizeAIConfigProviderId(right);
|
|
@@ -156,11 +176,16 @@ export function getAIConfigDefaultBaseUrl(providerId) {
|
|
|
156
176
|
export function getDefaultAIConfigCredentialId(providerId) {
|
|
157
177
|
return `${normalizeAIConfigProviderId(providerId)}-default`;
|
|
158
178
|
}
|
|
159
|
-
export function
|
|
160
|
-
return
|
|
179
|
+
export function aiConfigProviderRef(providerId) {
|
|
180
|
+
return normalizeAIConfigProviderId(providerId);
|
|
161
181
|
}
|
|
162
|
-
export function
|
|
163
|
-
|
|
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;
|
|
164
189
|
}
|
|
165
190
|
export function buildAIConfigProviderStateMap(options) {
|
|
166
191
|
const catalog = options.catalog ?? AI_CONFIG_PROVIDER_CATALOG;
|
|
@@ -188,7 +213,7 @@ export function buildAIConfigProviderStateMap(options) {
|
|
|
188
213
|
const providerId = normalizeAIConfigProviderId(String(row.isProvidedBy ?? ''));
|
|
189
214
|
if (!providerId)
|
|
190
215
|
continue;
|
|
191
|
-
const modelId =
|
|
216
|
+
const modelId = normalizeAIConfigModelStorageId(String(row.id ?? row['@id'] ?? ''), providerId);
|
|
192
217
|
if (!modelId)
|
|
193
218
|
continue;
|
|
194
219
|
const list = modelMap.get(providerId) ?? [];
|
|
@@ -226,7 +251,7 @@ export function buildAIConfigProviderStateMap(options) {
|
|
|
226
251
|
enabled: true,
|
|
227
252
|
capabilities: [],
|
|
228
253
|
}));
|
|
229
|
-
const selectedModelId =
|
|
254
|
+
const selectedModelId = normalizeAIConfigModelStorageId(typeof providerRow?.hasModel === 'string' ? providerRow.hasModel : '', providerId) || preferredSelectedModelId(models);
|
|
230
255
|
states[providerId] = {
|
|
231
256
|
id: providerId,
|
|
232
257
|
enabled: (typeof credentialRow?.status === 'string' ? credentialRow.status : 'inactive') === 'active',
|
|
@@ -254,21 +279,21 @@ export function buildAIConfigMutationPlan(input) {
|
|
|
254
279
|
if (hasConfigUpdate || input.updates.models !== undefined) {
|
|
255
280
|
const selectedModelId = input.updates.models
|
|
256
281
|
? preferredSelectedModelId(input.updates.models)
|
|
257
|
-
:
|
|
282
|
+
: normalizeAIConfigModelStorageId(typeof existingProvider?.hasModel === 'string' ? existingProvider.hasModel : '', providerId);
|
|
258
283
|
providerPayload = {
|
|
259
284
|
id: providerId,
|
|
260
285
|
baseUrl: input.updates.baseUrl ??
|
|
261
286
|
(typeof existingProvider?.baseUrl === 'string' ? existingProvider.baseUrl : undefined) ??
|
|
262
287
|
metadata.defaultBaseUrl,
|
|
263
288
|
proxyUrl: typeof existingProvider?.proxyUrl === 'string' ? existingProvider.proxyUrl : undefined,
|
|
264
|
-
hasModel: selectedModelId ?
|
|
289
|
+
hasModel: selectedModelId ? aiConfigModelRef(providerId, selectedModelId) : undefined,
|
|
265
290
|
};
|
|
266
291
|
}
|
|
267
292
|
if (hasConfigUpdate) {
|
|
268
293
|
credentialPayload = {
|
|
269
294
|
id: normalizeAIConfigResourceId(typeof existingCredential?.id === 'string' ? existingCredential.id : '') ||
|
|
270
295
|
getDefaultAIConfigCredentialId(providerId),
|
|
271
|
-
provider:
|
|
296
|
+
provider: aiConfigProviderRef(providerId),
|
|
272
297
|
service: typeof existingCredential?.service === 'string' && existingCredential.service ? existingCredential.service : 'ai',
|
|
273
298
|
status: input.updates.enabled !== undefined
|
|
274
299
|
? input.updates.enabled
|
|
@@ -288,12 +313,12 @@ export function buildAIConfigMutationPlan(input) {
|
|
|
288
313
|
}
|
|
289
314
|
if (input.updates.models !== undefined) {
|
|
290
315
|
const existingById = new Map(existingModels.map((row) => [
|
|
291
|
-
|
|
316
|
+
normalizeAIConfigModelStorageId(String(row.id ?? row['@id'] ?? ''), providerId),
|
|
292
317
|
row,
|
|
293
318
|
]));
|
|
294
319
|
const nextIds = new Set();
|
|
295
320
|
for (const model of input.updates.models) {
|
|
296
|
-
const modelId =
|
|
321
|
+
const modelId = normalizeAIConfigModelStorageId(model.id, providerId);
|
|
297
322
|
if (!modelId)
|
|
298
323
|
continue;
|
|
299
324
|
nextIds.add(modelId);
|
|
@@ -303,7 +328,7 @@ export function buildAIConfigMutationPlan(input) {
|
|
|
303
328
|
id: modelId,
|
|
304
329
|
displayName: model.name || modelId,
|
|
305
330
|
modelType: 'chat',
|
|
306
|
-
isProvidedBy:
|
|
331
|
+
isProvidedBy: aiConfigProviderRef(providerId),
|
|
307
332
|
status: model.enabled ? 'active' : 'inactive',
|
|
308
333
|
createdAt: existingDate(existing?.createdAt) ?? now,
|
|
309
334
|
updatedAt: now,
|
package/dist/ai-model.schema.js
CHANGED
|
@@ -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
|
|
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
|
|
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>;
|
package/dist/approval.schema.js
CHANGED
|
@@ -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
|
|
4
|
-
|
|
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', {
|
|
@@ -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 {};
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import { extractChatThreadRef } from './chat.utils.js';
|
|
2
|
+
function formatTimestamp(value) {
|
|
3
|
+
if (!value)
|
|
4
|
+
return 0;
|
|
5
|
+
const time = new Date(String(value)).getTime();
|
|
6
|
+
return Number.isFinite(time) ? time : 0;
|
|
7
|
+
}
|
|
8
|
+
function getAuditAuthKey(audit) {
|
|
9
|
+
if (audit.action !== 'runtime.auth_required' && audit.action !== 'runtime.auth_resolved')
|
|
10
|
+
return null;
|
|
11
|
+
const method = audit.toolName || null;
|
|
12
|
+
const entry = audit.entry || null;
|
|
13
|
+
if (!method && !entry)
|
|
14
|
+
return null;
|
|
15
|
+
return `${audit.session ?? ''}:${method ?? ''}:${entry ?? ''}`;
|
|
16
|
+
}
|
|
17
|
+
function buildRuntimeSessionDescription(audit, fallback) {
|
|
18
|
+
const tool = audit.toolName ? `工具 ${audit.toolName}` : null;
|
|
19
|
+
return tool ?? fallback;
|
|
20
|
+
}
|
|
21
|
+
function buildApprovalDecisionDescription(audit, relatedApproval, decision) {
|
|
22
|
+
const toolName = audit.toolName || relatedApproval?.toolName || null;
|
|
23
|
+
const risk = relatedApproval?.risk ? `${relatedApproval.risk} 风险` : null;
|
|
24
|
+
const reason = relatedApproval?.reason?.trim() || null;
|
|
25
|
+
const lead = decision === 'approved' ? '收件箱已批准工具执行。' : '收件箱已拒绝工具执行。';
|
|
26
|
+
const parts = [toolName, risk, reason].filter(Boolean);
|
|
27
|
+
return parts.length > 0 ? `${lead} ${parts.join(' · ')}` : lead;
|
|
28
|
+
}
|
|
29
|
+
export function buildAuditDetailRecord(audit, relatedApproval) {
|
|
30
|
+
return {
|
|
31
|
+
action: audit.action,
|
|
32
|
+
actor: audit.actor,
|
|
33
|
+
actorRole: audit.actorRole,
|
|
34
|
+
onBehalfOf: audit.onBehalfOf || undefined,
|
|
35
|
+
session: audit.session || undefined,
|
|
36
|
+
entry: audit.entry || undefined,
|
|
37
|
+
toolCallId: audit.toolCallId || undefined,
|
|
38
|
+
toolName: audit.toolName || undefined,
|
|
39
|
+
approval: audit.approval || undefined,
|
|
40
|
+
policy: audit.policy || undefined,
|
|
41
|
+
policyVersion: audit.policyVersion || undefined,
|
|
42
|
+
createdAt: audit.createdAt,
|
|
43
|
+
relatedApproval: relatedApproval
|
|
44
|
+
? {
|
|
45
|
+
target: relatedApproval.target,
|
|
46
|
+
toolName: relatedApproval.toolName,
|
|
47
|
+
risk: relatedApproval.risk,
|
|
48
|
+
status: relatedApproval.status,
|
|
49
|
+
reason: relatedApproval.reason || undefined,
|
|
50
|
+
}
|
|
51
|
+
: undefined,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
export function formatInboxStatusLabel(status) {
|
|
55
|
+
if (!status)
|
|
56
|
+
return null;
|
|
57
|
+
switch (status) {
|
|
58
|
+
case 'pending':
|
|
59
|
+
return '待处理';
|
|
60
|
+
case 'resolved':
|
|
61
|
+
return '已完成';
|
|
62
|
+
case 'approved':
|
|
63
|
+
return '已批准';
|
|
64
|
+
case 'rejected':
|
|
65
|
+
return '已拒绝';
|
|
66
|
+
case 'active':
|
|
67
|
+
return '运行中';
|
|
68
|
+
case 'paused':
|
|
69
|
+
return '已暂停';
|
|
70
|
+
case 'completed':
|
|
71
|
+
return '已完成';
|
|
72
|
+
case 'error':
|
|
73
|
+
return '异常';
|
|
74
|
+
default:
|
|
75
|
+
return status;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
export function formatAuditActorRole(role) {
|
|
79
|
+
switch (role) {
|
|
80
|
+
case 'system':
|
|
81
|
+
return '系统';
|
|
82
|
+
case 'human':
|
|
83
|
+
return '人工';
|
|
84
|
+
case 'secretary':
|
|
85
|
+
return '秘书';
|
|
86
|
+
default:
|
|
87
|
+
return role || '—';
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
export function createResolvedAuthTimestampsIndex(audits) {
|
|
91
|
+
const resolvedAuthTimestampsByKey = new Map();
|
|
92
|
+
for (const audit of audits) {
|
|
93
|
+
if (audit.action !== 'runtime.auth_resolved')
|
|
94
|
+
continue;
|
|
95
|
+
const authKey = getAuditAuthKey(audit);
|
|
96
|
+
if (!authKey)
|
|
97
|
+
continue;
|
|
98
|
+
const timestamps = resolvedAuthTimestampsByKey.get(authKey) ?? [];
|
|
99
|
+
timestamps.push(formatTimestamp(audit.createdAt));
|
|
100
|
+
resolvedAuthTimestampsByKey.set(authKey, timestamps);
|
|
101
|
+
}
|
|
102
|
+
return resolvedAuthTimestampsByKey;
|
|
103
|
+
}
|
|
104
|
+
export function buildAuditPresentation(audit, resolvedAuthTimestampsByKey, relatedApproval) {
|
|
105
|
+
const thread = audit.entry || relatedApproval?.target || null;
|
|
106
|
+
const about = relatedApproval?.target ?? audit.entry ?? audit.approval ?? null;
|
|
107
|
+
const { chatId, threadId } = extractChatThreadRef(thread);
|
|
108
|
+
const actorRoleLabel = formatAuditActorRole(audit.actorRole);
|
|
109
|
+
if (audit.action === 'runtime.auth_required') {
|
|
110
|
+
const method = audit.toolName || null;
|
|
111
|
+
const authKey = getAuditAuthKey(audit);
|
|
112
|
+
const createdAtTs = formatTimestamp(audit.createdAt);
|
|
113
|
+
const resolvedAtTs = authKey ? (resolvedAuthTimestampsByKey.get(authKey) ?? []) : [];
|
|
114
|
+
const isResolved = resolvedAtTs.some((value) => value >= createdAtTs);
|
|
115
|
+
return {
|
|
116
|
+
title: method ? `认证请求 · ${method}` : '认证请求',
|
|
117
|
+
description: method ? `运行时需要完成 ${method} 认证后才能继续。` : '运行时需要额外认证后才能继续。',
|
|
118
|
+
category: 'auth_required',
|
|
119
|
+
status: isResolved ? 'resolved' : 'pending',
|
|
120
|
+
chatId,
|
|
121
|
+
threadId,
|
|
122
|
+
thread,
|
|
123
|
+
about,
|
|
124
|
+
authUrl: null,
|
|
125
|
+
authMethod: method,
|
|
126
|
+
authMessage: null,
|
|
127
|
+
actorRoleLabel,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
if (audit.action === 'runtime.auth_resolved') {
|
|
131
|
+
const method = audit.toolName || null;
|
|
132
|
+
return {
|
|
133
|
+
title: method ? `认证完成 · ${method}` : '认证完成',
|
|
134
|
+
description: '运行时认证已完成。',
|
|
135
|
+
category: 'audit',
|
|
136
|
+
status: 'resolved',
|
|
137
|
+
chatId,
|
|
138
|
+
threadId,
|
|
139
|
+
thread,
|
|
140
|
+
about,
|
|
141
|
+
authUrl: null,
|
|
142
|
+
authMethod: method,
|
|
143
|
+
authMessage: null,
|
|
144
|
+
actorRoleLabel,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
if (audit.action === 'runtime.tool_call.waiting_approval') {
|
|
148
|
+
const toolName = audit.toolName || relatedApproval?.toolName || null;
|
|
149
|
+
const risk = relatedApproval?.risk ? `${relatedApproval.risk} 风险` : null;
|
|
150
|
+
return {
|
|
151
|
+
title: toolName ? `工具请求 · ${toolName}` : '工具请求',
|
|
152
|
+
description: [risk, '已进入审批队列'].filter(Boolean).join(' · ') || '工具调用已进入审批队列。',
|
|
153
|
+
category: 'audit',
|
|
154
|
+
status: undefined,
|
|
155
|
+
chatId,
|
|
156
|
+
threadId,
|
|
157
|
+
thread,
|
|
158
|
+
about,
|
|
159
|
+
authUrl: null,
|
|
160
|
+
authMethod: null,
|
|
161
|
+
authMessage: null,
|
|
162
|
+
actorRoleLabel,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
if (audit.action === 'inbox.approval.approved') {
|
|
166
|
+
return {
|
|
167
|
+
title: '授权已批准',
|
|
168
|
+
description: buildApprovalDecisionDescription(audit, relatedApproval, 'approved'),
|
|
169
|
+
category: 'audit',
|
|
170
|
+
status: 'approved',
|
|
171
|
+
chatId,
|
|
172
|
+
threadId,
|
|
173
|
+
thread,
|
|
174
|
+
about,
|
|
175
|
+
authUrl: null,
|
|
176
|
+
authMethod: null,
|
|
177
|
+
authMessage: null,
|
|
178
|
+
actorRoleLabel,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
if (audit.action === 'inbox.approval.rejected') {
|
|
182
|
+
return {
|
|
183
|
+
title: '授权已拒绝',
|
|
184
|
+
description: buildApprovalDecisionDescription(audit, relatedApproval, 'rejected'),
|
|
185
|
+
category: 'audit',
|
|
186
|
+
status: 'rejected',
|
|
187
|
+
chatId,
|
|
188
|
+
threadId,
|
|
189
|
+
thread,
|
|
190
|
+
about,
|
|
191
|
+
authUrl: null,
|
|
192
|
+
authMethod: null,
|
|
193
|
+
authMessage: null,
|
|
194
|
+
actorRoleLabel,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
if (audit.action === 'runtime.session.active') {
|
|
198
|
+
return {
|
|
199
|
+
title: '运行时已启动',
|
|
200
|
+
description: buildRuntimeSessionDescription(audit, '运行时会话开始执行。'),
|
|
201
|
+
category: 'audit',
|
|
202
|
+
status: 'active',
|
|
203
|
+
chatId,
|
|
204
|
+
threadId,
|
|
205
|
+
thread,
|
|
206
|
+
about,
|
|
207
|
+
authUrl: null,
|
|
208
|
+
authMethod: null,
|
|
209
|
+
authMessage: null,
|
|
210
|
+
actorRoleLabel,
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
if (audit.action === 'runtime.session.paused') {
|
|
214
|
+
return {
|
|
215
|
+
title: '运行时已暂停',
|
|
216
|
+
description: buildRuntimeSessionDescription(audit, '运行时会话已暂停。'),
|
|
217
|
+
category: 'audit',
|
|
218
|
+
status: 'paused',
|
|
219
|
+
chatId,
|
|
220
|
+
threadId,
|
|
221
|
+
thread,
|
|
222
|
+
about,
|
|
223
|
+
authUrl: null,
|
|
224
|
+
authMethod: null,
|
|
225
|
+
authMessage: null,
|
|
226
|
+
actorRoleLabel,
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
if (audit.action === 'runtime.session.completed') {
|
|
230
|
+
return {
|
|
231
|
+
title: '运行时已完成',
|
|
232
|
+
description: buildRuntimeSessionDescription(audit, '运行时会话已完成。'),
|
|
233
|
+
category: 'audit',
|
|
234
|
+
status: 'completed',
|
|
235
|
+
chatId,
|
|
236
|
+
threadId,
|
|
237
|
+
thread,
|
|
238
|
+
about,
|
|
239
|
+
authUrl: null,
|
|
240
|
+
authMethod: null,
|
|
241
|
+
authMessage: null,
|
|
242
|
+
actorRoleLabel,
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
if (audit.action === 'runtime.session.error') {
|
|
246
|
+
return {
|
|
247
|
+
title: '运行时异常',
|
|
248
|
+
description: buildRuntimeSessionDescription(audit, '运行时会话执行失败。'),
|
|
249
|
+
category: 'audit',
|
|
250
|
+
status: 'error',
|
|
251
|
+
chatId,
|
|
252
|
+
threadId,
|
|
253
|
+
thread,
|
|
254
|
+
about,
|
|
255
|
+
authUrl: null,
|
|
256
|
+
authMethod: null,
|
|
257
|
+
authMessage: null,
|
|
258
|
+
actorRoleLabel,
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
return {
|
|
262
|
+
title: audit.action,
|
|
263
|
+
description: actorRoleLabel,
|
|
264
|
+
category: 'audit',
|
|
265
|
+
status: undefined,
|
|
266
|
+
chatId,
|
|
267
|
+
threadId,
|
|
268
|
+
thread,
|
|
269
|
+
about,
|
|
270
|
+
authUrl: null,
|
|
271
|
+
authMethod: null,
|
|
272
|
+
authMessage: null,
|
|
273
|
+
actorRoleLabel,
|
|
274
|
+
};
|
|
275
|
+
}
|