@things-factory/ai-dashboard 10.1.213 → 10.1.215
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/client/components/card-dialogs.ts +24 -1
- package/client/components/dashboard-card.ts +24 -70
- package/client/index.ts +2 -0
- package/client/pages/my-dashboard-page.ts +6 -5
- package/client/utils/card-api.ts +3 -2
- package/client/utils/card-words.ts +11 -0
- package/client/utils/examples.ts +15 -0
- package/dist-client/components/card-dialogs.js +22 -1
- package/dist-client/components/card-dialogs.js.map +1 -1
- package/dist-client/components/dashboard-card.d.ts +6 -5
- package/dist-client/components/dashboard-card.js +23 -73
- package/dist-client/components/dashboard-card.js.map +1 -1
- package/dist-client/index.d.ts +1 -0
- package/dist-client/index.js +2 -0
- package/dist-client/index.js.map +1 -1
- package/dist-client/pages/my-dashboard-page.js +6 -5
- package/dist-client/pages/my-dashboard-page.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-client/utils/card-api.d.ts +6 -1
- package/dist-client/utils/card-api.js +1 -1
- package/dist-client/utils/card-api.js.map +1 -1
- package/dist-client/utils/card-words.d.ts +14 -0
- package/dist-client/utils/card-words.js +9 -0
- package/dist-client/utils/card-words.js.map +1 -1
- package/dist-client/utils/examples.d.ts +3 -0
- package/dist-client/utils/examples.js +14 -0
- package/dist-client/utils/examples.js.map +1 -0
- package/dist-server/service/card/card-access.d.ts +8 -2
- package/dist-server/service/card/card-access.js +9 -3
- package/dist-server/service/card/card-access.js.map +1 -1
- package/dist-server/service/card/card-definition.d.ts +42 -6
- package/dist-server/service/card/card-definition.js +172 -18
- package/dist-server/service/card/card-definition.js.map +1 -1
- package/dist-server/service/card/card-names.d.ts +5 -0
- package/dist-server/service/card/card-names.js +6 -1
- package/dist-server/service/card/card-names.js.map +1 -1
- package/dist-server/service/card/card-proposal.d.ts +6 -0
- package/dist-server/service/card/card-proposal.js +9 -0
- package/dist-server/service/card/card-proposal.js.map +1 -1
- package/dist-server/service/card/card-resolver.js +24 -5
- package/dist-server/service/card/card-resolver.js.map +1 -1
- package/dist-server/service/card/card-run.js +5 -1
- package/dist-server/service/card/card-run.js.map +1 -1
- package/dist-server/service/card/card-tool.d.ts +66 -8
- package/dist-server/service/card/card-tool.js +88 -17
- package/dist-server/service/card/card-tool.js.map +1 -1
- package/dist-server/service/card/card-types.d.ts +1 -0
- package/dist-server/service/card/card-types.js +8 -4
- package/dist-server/service/card/card-types.js.map +1 -1
- package/dist-server/service/card/dashboard-card.d.ts +2 -0
- package/dist-server/service/card/dashboard-card.js +5 -3
- package/dist-server/service/card/dashboard-card.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +5 -5
- package/server/service/card/card-access.test.ts +62 -7
- package/server/service/card/card-access.ts +12 -5
- package/server/service/card/card-definition.ts +182 -18
- package/server/service/card/card-intent.test.ts +250 -0
- package/server/service/card/card-names.ts +5 -0
- package/server/service/card/card-proposal.ts +10 -1
- package/server/service/card/card-resolver.ts +26 -8
- package/server/service/card/card-run.ts +5 -1
- package/server/service/card/card-tool.ts +91 -20
- package/server/service/card/card-types.ts +7 -4
- package/server/service/card/dashboard-card.ts +5 -3
- package/test/card-menu.test.ts +34 -0
- package/test/translations.test.ts +1 -1
- package/translations/en.json +3 -3
- package/translations/es.json +3 -3
- package/translations/ja.json +3 -3
- package/translations/ko.json +3 -3
- package/translations/ms.json +3 -3
- package/translations/zh.json +3 -3
|
@@ -2,8 +2,18 @@ import { type ToolCategory } from '@things-factory/ai-client-base';
|
|
|
2
2
|
import { type CardDefinition } from './card-definition.js';
|
|
3
3
|
import { CARD_CATEGORY, CARD_TOOL, DASHBOARD_PAGE } from './card-names.js';
|
|
4
4
|
export { CARD_CATEGORY, CARD_TOOL, DASHBOARD_PAGE };
|
|
5
|
-
/** The reads a card may name - list read
|
|
6
|
-
export declare function cardSources():
|
|
5
|
+
/** The reads a card may name - each list read target and each detail read's list, with the tool the model knows it by. */
|
|
6
|
+
export declare function cardSources(): {
|
|
7
|
+
read: string;
|
|
8
|
+
list?: string;
|
|
9
|
+
tool: string;
|
|
10
|
+
mine?: true;
|
|
11
|
+
}[];
|
|
12
|
+
/**
|
|
13
|
+
* The read a model named - its key, or the name of the tool it reads it with (`queryPlantJobOrder` is `plant.job-order`):
|
|
14
|
+
* the model knows reads by their tools, and named the tool as the card's source (2026-10-25, plant).
|
|
15
|
+
*/
|
|
16
|
+
export declare function readKeyOf(given: unknown): unknown;
|
|
7
17
|
/** A saved card as a change needs it - whose it is and its version. */
|
|
8
18
|
export type CardOf = (id: string) => Promise<{
|
|
9
19
|
ownerId?: string;
|
|
@@ -16,11 +26,17 @@ export declare function proposeDashboardCard(args: any, context: any, deps?: {
|
|
|
16
26
|
cardOf?: CardOf;
|
|
17
27
|
}): Promise<{
|
|
18
28
|
message: string;
|
|
19
|
-
sources?:
|
|
29
|
+
sources?: {
|
|
30
|
+
read: string;
|
|
31
|
+
list?: string;
|
|
32
|
+
tool: string;
|
|
33
|
+
mine?: true;
|
|
34
|
+
}[];
|
|
20
35
|
rejected: boolean;
|
|
21
36
|
error: string;
|
|
22
37
|
issues?: undefined;
|
|
23
38
|
source?: undefined;
|
|
39
|
+
cardId?: undefined;
|
|
24
40
|
current?: undefined;
|
|
25
41
|
read?: undefined;
|
|
26
42
|
label?: undefined;
|
|
@@ -35,11 +51,12 @@ export declare function proposeDashboardCard(args: any, context: any, deps?: {
|
|
|
35
51
|
issues: import("./card-definition.js").CardIssue[];
|
|
36
52
|
source: {
|
|
37
53
|
columnsAfterGroupBy?: string[];
|
|
38
|
-
description: string;
|
|
39
54
|
conditions: {
|
|
40
55
|
description?: string;
|
|
41
56
|
values?: string[];
|
|
42
57
|
required?: boolean;
|
|
58
|
+
takes?: string;
|
|
59
|
+
format?: string;
|
|
43
60
|
name: string;
|
|
44
61
|
type: string;
|
|
45
62
|
}[];
|
|
@@ -48,10 +65,28 @@ export declare function proposeDashboardCard(args: any, context: any, deps?: {
|
|
|
48
65
|
name: string;
|
|
49
66
|
type: "string" | "number" | "date";
|
|
50
67
|
}[];
|
|
68
|
+
mine?: string;
|
|
69
|
+
description: string;
|
|
51
70
|
list?: string;
|
|
52
71
|
read: string;
|
|
53
72
|
};
|
|
54
73
|
message: string;
|
|
74
|
+
cardId?: undefined;
|
|
75
|
+
current?: undefined;
|
|
76
|
+
read?: undefined;
|
|
77
|
+
label?: undefined;
|
|
78
|
+
use?: undefined;
|
|
79
|
+
proposed?: undefined;
|
|
80
|
+
kind?: undefined;
|
|
81
|
+
icon?: undefined;
|
|
82
|
+
card?: undefined;
|
|
83
|
+
} | {
|
|
84
|
+
rejected: boolean;
|
|
85
|
+
error: string;
|
|
86
|
+
cardId: any;
|
|
87
|
+
message: string;
|
|
88
|
+
issues?: undefined;
|
|
89
|
+
source?: undefined;
|
|
55
90
|
current?: undefined;
|
|
56
91
|
read?: undefined;
|
|
57
92
|
label?: undefined;
|
|
@@ -67,6 +102,7 @@ export declare function proposeDashboardCard(args: any, context: any, deps?: {
|
|
|
67
102
|
message: string;
|
|
68
103
|
issues?: undefined;
|
|
69
104
|
source?: undefined;
|
|
105
|
+
cardId?: undefined;
|
|
70
106
|
read?: undefined;
|
|
71
107
|
label?: undefined;
|
|
72
108
|
use?: undefined;
|
|
@@ -77,11 +113,12 @@ export declare function proposeDashboardCard(args: any, context: any, deps?: {
|
|
|
77
113
|
} | {
|
|
78
114
|
source: {
|
|
79
115
|
columnsAfterGroupBy?: string[];
|
|
80
|
-
description: string;
|
|
81
116
|
conditions: {
|
|
82
117
|
description?: string;
|
|
83
118
|
values?: string[];
|
|
84
119
|
required?: boolean;
|
|
120
|
+
takes?: string;
|
|
121
|
+
format?: string;
|
|
85
122
|
name: string;
|
|
86
123
|
type: string;
|
|
87
124
|
}[];
|
|
@@ -90,6 +127,8 @@ export declare function proposeDashboardCard(args: any, context: any, deps?: {
|
|
|
90
127
|
name: string;
|
|
91
128
|
type: "string" | "number" | "date";
|
|
92
129
|
}[];
|
|
130
|
+
mine?: string;
|
|
131
|
+
description: string;
|
|
93
132
|
list?: string;
|
|
94
133
|
read: string;
|
|
95
134
|
};
|
|
@@ -98,6 +137,7 @@ export declare function proposeDashboardCard(args: any, context: any, deps?: {
|
|
|
98
137
|
rejected: boolean;
|
|
99
138
|
error: string;
|
|
100
139
|
issues?: undefined;
|
|
140
|
+
cardId?: undefined;
|
|
101
141
|
current?: undefined;
|
|
102
142
|
read?: undefined;
|
|
103
143
|
label?: undefined;
|
|
@@ -112,11 +152,12 @@ export declare function proposeDashboardCard(args: any, context: any, deps?: {
|
|
|
112
152
|
read: number;
|
|
113
153
|
source: {
|
|
114
154
|
columnsAfterGroupBy?: string[];
|
|
115
|
-
description: string;
|
|
116
155
|
conditions: {
|
|
117
156
|
description?: string;
|
|
118
157
|
values?: string[];
|
|
119
158
|
required?: boolean;
|
|
159
|
+
takes?: string;
|
|
160
|
+
format?: string;
|
|
120
161
|
name: string;
|
|
121
162
|
type: string;
|
|
122
163
|
}[];
|
|
@@ -125,11 +166,14 @@ export declare function proposeDashboardCard(args: any, context: any, deps?: {
|
|
|
125
166
|
name: string;
|
|
126
167
|
type: "string" | "number" | "date";
|
|
127
168
|
}[];
|
|
169
|
+
mine?: string;
|
|
170
|
+
description: string;
|
|
128
171
|
list?: string;
|
|
129
172
|
read: string;
|
|
130
173
|
};
|
|
131
174
|
message: string;
|
|
132
175
|
issues?: undefined;
|
|
176
|
+
cardId?: undefined;
|
|
133
177
|
current?: undefined;
|
|
134
178
|
label?: undefined;
|
|
135
179
|
use?: undefined;
|
|
@@ -144,11 +188,12 @@ export declare function proposeDashboardCard(args: any, context: any, deps?: {
|
|
|
144
188
|
use: string;
|
|
145
189
|
source: {
|
|
146
190
|
columnsAfterGroupBy?: string[];
|
|
147
|
-
description: string;
|
|
148
191
|
conditions: {
|
|
149
192
|
description?: string;
|
|
150
193
|
values?: string[];
|
|
151
194
|
required?: boolean;
|
|
195
|
+
takes?: string;
|
|
196
|
+
format?: string;
|
|
152
197
|
name: string;
|
|
153
198
|
type: string;
|
|
154
199
|
}[];
|
|
@@ -157,11 +202,14 @@ export declare function proposeDashboardCard(args: any, context: any, deps?: {
|
|
|
157
202
|
name: string;
|
|
158
203
|
type: "string" | "number" | "date";
|
|
159
204
|
}[];
|
|
205
|
+
mine?: string;
|
|
206
|
+
description: string;
|
|
160
207
|
list?: string;
|
|
161
208
|
read: string;
|
|
162
209
|
};
|
|
163
210
|
message: string;
|
|
164
211
|
issues?: undefined;
|
|
212
|
+
cardId?: undefined;
|
|
165
213
|
current?: undefined;
|
|
166
214
|
read?: undefined;
|
|
167
215
|
proposed?: undefined;
|
|
@@ -174,7 +222,7 @@ export declare function proposeDashboardCard(args: any, context: any, deps?: {
|
|
|
174
222
|
icon: string;
|
|
175
223
|
label: any;
|
|
176
224
|
card: {
|
|
177
|
-
cardId?:
|
|
225
|
+
cardId?: string;
|
|
178
226
|
baseVersion?: number;
|
|
179
227
|
definition: CardDefinition;
|
|
180
228
|
description?: any;
|
|
@@ -185,6 +233,7 @@ export declare function proposeDashboardCard(args: any, context: any, deps?: {
|
|
|
185
233
|
error?: undefined;
|
|
186
234
|
issues?: undefined;
|
|
187
235
|
source?: undefined;
|
|
236
|
+
cardId?: undefined;
|
|
188
237
|
current?: undefined;
|
|
189
238
|
read?: undefined;
|
|
190
239
|
use?: undefined;
|
|
@@ -205,6 +254,15 @@ export declare function readDashboardCard(args: any, context: any, cardOf?: (id:
|
|
|
205
254
|
version?: undefined;
|
|
206
255
|
access?: undefined;
|
|
207
256
|
definition?: undefined;
|
|
257
|
+
} | {
|
|
258
|
+
rejected: boolean;
|
|
259
|
+
error: string;
|
|
260
|
+
id: any;
|
|
261
|
+
message: string;
|
|
262
|
+
name?: undefined;
|
|
263
|
+
version?: undefined;
|
|
264
|
+
access?: undefined;
|
|
265
|
+
definition?: undefined;
|
|
208
266
|
} | {
|
|
209
267
|
id: any;
|
|
210
268
|
name: any;
|
|
@@ -35,6 +35,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.cardCategory = exports.DASHBOARD_PAGE = exports.CARD_TOOL = exports.CARD_CATEGORY = void 0;
|
|
37
37
|
exports.cardSources = cardSources;
|
|
38
|
+
exports.readKeyOf = readKeyOf;
|
|
38
39
|
exports.proposeDashboardCard = proposeDashboardCard;
|
|
39
40
|
exports.readDashboardCard = readDashboardCard;
|
|
40
41
|
/*
|
|
@@ -54,6 +55,7 @@ const card_names_js_1 = require("./card-names.js");
|
|
|
54
55
|
Object.defineProperty(exports, "CARD_CATEGORY", { enumerable: true, get: function () { return card_names_js_1.CARD_CATEGORY; } });
|
|
55
56
|
Object.defineProperty(exports, "CARD_TOOL", { enumerable: true, get: function () { return card_names_js_1.CARD_TOOL; } });
|
|
56
57
|
Object.defineProperty(exports, "DASHBOARD_PAGE", { enumerable: true, get: function () { return card_names_js_1.DASHBOARD_PAGE; } });
|
|
58
|
+
const card_proposal_js_1 = require("./card-proposal.js");
|
|
57
59
|
/** What a read offers a card - for a refusal the model fixes the definition from. */
|
|
58
60
|
function sourceSheet(source, groupBy) {
|
|
59
61
|
return {
|
|
@@ -61,18 +63,35 @@ function sourceSheet(source, groupBy) {
|
|
|
61
63
|
...(source.list ? { list: source.list.key } : {}),
|
|
62
64
|
/* What the rows are - a state as of today or a ledger over time - decides whether a period is a filter at all. */
|
|
63
65
|
description: source.description,
|
|
64
|
-
|
|
66
|
+
...(source.mine ? { mine: 'the rows are the viewer\'s own, whoever looks - no filter on a person' } : {}),
|
|
67
|
+
conditions: source.conditions.map(one => ({ name: one.name, type: one.type, ...(one.format ? { format: 'YYYY-MM' } : {}), ...(one.many ? { takes: 'one value or a list' } : {}), ...(one.required ? { required: true } : {}), ...(one.values ? { values: one.values } : {}), ...(one.description ? { description: one.description } : {}) })),
|
|
65
68
|
columns: source.columns.map(one => ({ name: one.name, type: one.type, ...(one.currency ? { currency: one.currency } : {}) })),
|
|
66
69
|
...(groupBy ? { columnsAfterGroupBy: [...(0, card_run_js_1.shapedColumnNames)(source, { groupBy }).keys()] } : {})
|
|
67
70
|
};
|
|
68
71
|
}
|
|
69
|
-
/** The reads a card may name - list read
|
|
72
|
+
/** The reads a card may name - each list read target and each detail read's list, with the tool the model knows it by. */
|
|
70
73
|
function cardSources() {
|
|
71
74
|
return [
|
|
72
|
-
...(0, ai_assistant_1.readTargetKeys)(),
|
|
73
|
-
...(0, ai_assistant_1.detailReadKeys)().flatMap(key => ((0, ai_assistant_1.detailReadOf)(key)?.lists ?? []).map(list =>
|
|
75
|
+
...(0, ai_assistant_1.readTargetKeys)().map(key => ({ read: key, tool: (0, ai_assistant_1.queryToolName)(key), ...((0, ai_assistant_1.readTargetOf)(key)?.mine ? { mine: true } : {}) })),
|
|
76
|
+
...(0, ai_assistant_1.detailReadKeys)().flatMap(key => ((0, ai_assistant_1.detailReadOf)(key)?.lists ?? []).map(list => ({ read: key, list: list.key, tool: (0, ai_assistant_1.detailToolName)(key), ...((0, ai_assistant_1.detailReadOf)(key)?.mine ? { mine: true } : {}) })))
|
|
74
77
|
];
|
|
75
78
|
}
|
|
79
|
+
/**
|
|
80
|
+
* The read a model named - its key, or the name of the tool it reads it with (`queryPlantJobOrder` is `plant.job-order`):
|
|
81
|
+
* the model knows reads by their tools, and named the tool as the card's source (2026-10-25, plant).
|
|
82
|
+
*/
|
|
83
|
+
function readKeyOf(given) {
|
|
84
|
+
if (typeof given !== 'string' || (0, ai_assistant_1.readTargetOf)(given) || (0, ai_assistant_1.detailReadOf)(given))
|
|
85
|
+
return given;
|
|
86
|
+
return (0, ai_assistant_1.readTargetKeys)().find(key => (0, ai_assistant_1.queryToolName)(key) === given) ?? (0, ai_assistant_1.detailReadKeys)().find(key => (0, ai_assistant_1.detailToolName)(key) === given) ?? given;
|
|
87
|
+
}
|
|
88
|
+
/** A part of a definition without the fields the model left empty - `groupBy: null` is no grouping, not a column named null. */
|
|
89
|
+
function withoutEmpty(given) {
|
|
90
|
+
if (!given || typeof given !== 'object' || Array.isArray(given))
|
|
91
|
+
return given;
|
|
92
|
+
const kept = Object.entries(given).filter(([, value]) => value !== null && value !== undefined && value !== '');
|
|
93
|
+
return kept.length ? Object.fromEntries(kept) : undefined;
|
|
94
|
+
}
|
|
76
95
|
/** The card in the person's domain - loaded late so the rules are tested without a database. */
|
|
77
96
|
const cardInDomain = (context) => async (id) => {
|
|
78
97
|
const [{ getRepository }, { DashboardCard }] = await Promise.all([Promise.resolve().then(() => __importStar(require('@things-factory/shell'))), Promise.resolve().then(() => __importStar(require('./dashboard-card.js')))]);
|
|
@@ -83,11 +102,21 @@ async function proposeDashboardCard(args, context, deps = {}) {
|
|
|
83
102
|
const name = typeof args?.name === 'string' ? args.name.trim().slice(0, 100) : '';
|
|
84
103
|
if (!name)
|
|
85
104
|
return { rejected: true, error: 'no-name', message: 'give the card a short name in the person\'s language.' };
|
|
105
|
+
const where = withoutEmpty(args?.source?.where);
|
|
106
|
+
const rows = withoutEmpty(args?.rows);
|
|
107
|
+
/* One value, or the list `in` and `not_in` take - kept as the match's value either way. */
|
|
108
|
+
if (Array.isArray(rows?.match))
|
|
109
|
+
rows.match = rows.match.map((one) => {
|
|
110
|
+
if (!one || typeof one !== 'object' || !('values' in one))
|
|
111
|
+
return one;
|
|
112
|
+
const { values, ...match } = one;
|
|
113
|
+
return card_definition_js_1.LIST_OPS.includes(match.op) ? { ...match, value: values } : match.value === undefined ? { ...match, value: values } : match;
|
|
114
|
+
});
|
|
86
115
|
const definition = {
|
|
87
116
|
schemaVersion: card_definition_js_1.CARD_SCHEMA_VERSION,
|
|
88
|
-
source: { read: args?.source?.read, ...(args?.source?.list ? { list: args.source.list } : {}), ...(
|
|
89
|
-
...(
|
|
90
|
-
view: args?.view
|
|
117
|
+
source: { read: readKeyOf(args?.source?.read), ...(args?.source?.list ? { list: args.source.list } : {}), ...(where ? { where } : {}) },
|
|
118
|
+
...(rows ? { rows } : {}),
|
|
119
|
+
view: withoutEmpty(args?.view)
|
|
91
120
|
};
|
|
92
121
|
const { source, issue } = (0, card_definition_js_1.sourceOf)(definition.source, ai_assistant_1.readTargetOf, ai_assistant_1.detailReadOf);
|
|
93
122
|
if (!source) {
|
|
@@ -96,7 +125,7 @@ async function proposeDashboardCard(args, context, deps = {}) {
|
|
|
96
125
|
error: issue.code,
|
|
97
126
|
...issue.params,
|
|
98
127
|
...(issue.code === 'unknown-read' || issue.code === 'not-a-card-source' ? { sources: cardSources() } : {}),
|
|
99
|
-
message: '
|
|
128
|
+
message: 'source.read is one of the sources listed - its read key or its tool (a detail read also names its list). Call again now with one of them.'
|
|
100
129
|
};
|
|
101
130
|
}
|
|
102
131
|
const issues = (0, card_definition_js_1.checkDefinition)(definition, source);
|
|
@@ -110,8 +139,28 @@ async function proposeDashboardCard(args, context, deps = {}) {
|
|
|
110
139
|
'a column is filtered with rows.match; a period with no date condition is a rows.match on a date column, or left out when the source is a state as of today.'
|
|
111
140
|
};
|
|
112
141
|
}
|
|
142
|
+
/* What the card says it shows, and whose rows it shows - checked before it runs, so a wrong card is never drawn once. */
|
|
143
|
+
const description = typeof args?.description === 'string' ? args.description : undefined;
|
|
144
|
+
const user = context?.state?.user;
|
|
145
|
+
const mine = cardSources().filter(one => one.mine).map(one => (one.list ? `${one.read} (list ${one.list})` : one.read));
|
|
146
|
+
const intent = (0, card_definition_js_1.checkCardIntent)({ name, description, definition }, source, { id: user?.id, name: user?.name, email: user?.email }, mine);
|
|
147
|
+
if (intent.length) {
|
|
148
|
+
return {
|
|
149
|
+
rejected: true,
|
|
150
|
+
error: 'card-says-otherwise',
|
|
151
|
+
issues: intent,
|
|
152
|
+
source: sourceSheet(source, definition.rows?.groupBy),
|
|
153
|
+
message: 'the card would not show what it says - fix each issue and call again now, in this turn. ' +
|
|
154
|
+
'period-without-condition: put the period in source.where with relative values (this-month-start..this-month-end; a YYYY-MM condition this-month; the last 6 months -5m-start..this-month-end) or as a rows.match on a date column - or, when the source is a state as of today, take the period out of the name. ' +
|
|
155
|
+
'period-held-to-fixed-days: use relative values so the card stays current. period-unlike-condition: make the condition the period the name says. ' +
|
|
156
|
+
'viewer-filter-on-own-read: drop the filter - the read is the viewer\'s own whoever looks. viewer-filter-use-own-read: read one of the listed own reads instead, with no filter on the person.'
|
|
157
|
+
};
|
|
158
|
+
}
|
|
113
159
|
/* A change is of one's own card, from the version it was read at - a newer version is not overwritten unseen. */
|
|
114
|
-
const
|
|
160
|
+
const given = typeof args?.cardId === 'string' ? args.cardId.trim() : '';
|
|
161
|
+
const cardId = given ? (0, card_proposal_js_1.cardIdOf)(given) : undefined;
|
|
162
|
+
if (given && !cardId)
|
|
163
|
+
return { rejected: true, error: 'card-id-unreadable', cardId: given, message: `cardId is the card's uuid or its key ${card_names_js_1.CARD_AXIS}:<uuid>, as readDashboardCard gave it - call again now with it.` };
|
|
115
164
|
let baseVersion;
|
|
116
165
|
if (cardId) {
|
|
117
166
|
const saved = await (deps.cardOf ?? cardInDomain(context))(cardId);
|
|
@@ -189,8 +238,10 @@ const spec = {
|
|
|
189
238
|
doors: ['Mutation.saveDashboardCardProposal'],
|
|
190
239
|
description: 'Offer a dashboard card - a definition the person saves to 나의 대시보드, drawn afterwards from the data without you. ' +
|
|
191
240
|
'source.read is a read you have (a list target, or a detail read with source.list naming one of its lists); source.where holds its conditions or arguments - ' +
|
|
192
|
-
'for a period use relative values
|
|
193
|
-
|
|
241
|
+
'for a period use relative values so the card stays current - a day: today, -30d, this-month-start, this-month-end, last-month-start, this-year-start, -5m-start (the first day five months ago); ' +
|
|
242
|
+
'an integer year or month: this-year, last-month; a month as YYYY-MM: this-month, last-month, -5m. The last 6 months is -5m-start..this-month-end. A name that says a period has that condition. ' +
|
|
243
|
+
`rows: match [{column, op: ${card_definition_js_1.MATCH_OPS.join('|')}, value}] - every match holds (two eq of one column never do: use in [..]); not_in [..] for "not one of"; groupBy (a date groups by month), sortBy (a column, or count when grouped), desc, limit. ` +
|
|
244
|
+
'A read marked mine is the viewer\'s own rows, whoever looks - prefer it for "my ..." cards and never filter by a person\'s name or id. ' +
|
|
194
245
|
`view.kind: ${card_definition_js_1.VIEW_KINDS.join('|')}; label is the column along the bottom of a chart (the groupBy column by default); values are the number columns summed or drawn (none counts rows); columns lists a table's columns. ` +
|
|
195
246
|
'To change a saved card, pass its cardId and the version you changed as baseVersion.',
|
|
196
247
|
schema: {
|
|
@@ -201,7 +252,7 @@ const spec = {
|
|
|
201
252
|
source: {
|
|
202
253
|
type: 'object',
|
|
203
254
|
properties: {
|
|
204
|
-
read: { type: 'string', description: 'The read key
|
|
255
|
+
read: { type: 'string', description: 'The read - its key (office.receivables) or the tool that reads it (readOfficeReceivables).' },
|
|
205
256
|
list: { type: 'string', description: 'A detail read\'s list key.' },
|
|
206
257
|
where: { type: 'object', description: 'Conditions or arguments by name; dates may be relative.', additionalProperties: true }
|
|
207
258
|
},
|
|
@@ -212,7 +263,16 @@ const spec = {
|
|
|
212
263
|
properties: {
|
|
213
264
|
match: {
|
|
214
265
|
type: 'array',
|
|
215
|
-
items: {
|
|
266
|
+
items: {
|
|
267
|
+
type: 'object',
|
|
268
|
+
properties: {
|
|
269
|
+
column: COLUMN,
|
|
270
|
+
op: { type: 'string', enum: [...card_definition_js_1.MATCH_OPS] },
|
|
271
|
+
value: { type: 'string', description: 'A number, a day, a word or a relative date.' },
|
|
272
|
+
values: { type: 'array', items: { type: 'string' }, description: `The values of ${card_definition_js_1.LIST_OPS.join(' and ')}, in place of value.` }
|
|
273
|
+
},
|
|
274
|
+
required: ['column', 'op']
|
|
275
|
+
}
|
|
216
276
|
},
|
|
217
277
|
groupBy: COLUMN,
|
|
218
278
|
sortBy: COLUMN,
|
|
@@ -244,9 +304,12 @@ const spec = {
|
|
|
244
304
|
* the card; the model reads it here, then offers the changed definition with cardId and baseVersion).
|
|
245
305
|
*/
|
|
246
306
|
async function readDashboardCard(args, context, cardOf) {
|
|
247
|
-
const
|
|
248
|
-
if (!
|
|
307
|
+
const given = typeof args?.id === 'string' ? args.id.trim() : '';
|
|
308
|
+
if (!given)
|
|
249
309
|
return { rejected: true, error: 'no-id', message: 'give the card id the person picked.' };
|
|
310
|
+
const id = (0, card_proposal_js_1.cardIdOf)(given);
|
|
311
|
+
if (!id)
|
|
312
|
+
return { rejected: true, error: 'card-id-unreadable', id: given, message: `a card is named by its key ${card_names_js_1.CARD_AXIS}:<uuid> (after [keys] dashboardCards) or its uuid - call again now with it.` };
|
|
250
313
|
const found = await (cardOf ?? usableInDomain(context))(id);
|
|
251
314
|
if (!found?.card)
|
|
252
315
|
return { rejected: true, error: 'unknown-card', message: 'there is no such card - say so.' };
|
|
@@ -308,6 +371,14 @@ exports.cardCategory = {
|
|
|
308
371
|
specs: [spec, readSpec]
|
|
309
372
|
};
|
|
310
373
|
(0, ai_client_base_1.registerToolCategory)(exports.cardCategory);
|
|
311
|
-
/* In reach of every conversation, in front on the dashboard page
|
|
312
|
-
(0, ai_assistant_1.registerConversationCategory)({
|
|
374
|
+
/* In reach of every conversation, in front on the dashboard page and for a picked card (ADR-0061 decision 9). */
|
|
375
|
+
(0, ai_assistant_1.registerConversationCategory)({
|
|
376
|
+
name: card_names_js_1.CARD_CATEGORY,
|
|
377
|
+
frontPages: [card_names_js_1.DASHBOARD_PAGE],
|
|
378
|
+
frontAxes: [card_names_js_1.CARD_AXIS],
|
|
379
|
+
/* FINISH_RULE on this page: the card is what was asked for. */
|
|
380
|
+
pageRule: `This turn is asked from 나의 대시보드, where what the person asks to see - a figure, a total, a count, a list, a chart - is a dashboard card. ` +
|
|
381
|
+
`Read what it needs and end the turn with ${card_names_js_1.CARD_TOOL}, in this turn; never answer with the figure in words, and never ask whether to make it. ` +
|
|
382
|
+
`Only a question about the dashboard itself (how to share, how to move a card) is answered in words.`
|
|
383
|
+
});
|
|
313
384
|
//# sourceMappingURL=card-tool.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"card-tool.js","sourceRoot":"","sources":["../../../server/service/card/card-tool.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * proposeDashboardCard - the assistant writes a card's definition in a conversation; the person saves it by pressing\n * the card (ADR-0160 decision 9).\n *\n * The server checks the definition against the read it names and runs it once as the person, so a card that cannot\n * be drawn is sent back with what the read has - never offered. The proposal carries the definition only: the\n * preview on the card is run again by whoever looks at the conversation, as themselves (decision 6), and the model\n * is told no values - its answer is read by everyone the conversation lets in, whatever door they pass.\n */\nimport { registerToolCategory, type ToolCategory, type ToolSpec } from '@things-factory/ai-client-base'\nimport {\n detailReadKeys,\n detailReadOf,\n readTargetKeys,\n readTargetOf,\n registerConversationCategory,\n runAsCaller,\n toolAccessFor\n} from '@things-factory/ai-assistant'\n\nimport { CARD_SCHEMA_VERSION, checkDefinition, MATCH_OPS, sourceOf, todayIn, VIEW_KINDS, type CardDefinition, type CardSource } from './card-definition.js'\nimport { runCard, shapedColumnNames } from './card-run.js'\n\nimport { CARD_CATEGORY, CARD_TOOL, DASHBOARD_PAGE } from './card-names.js'\n\nexport { CARD_CATEGORY, CARD_TOOL, DASHBOARD_PAGE }\n\n/** What a read offers a card - for a refusal the model fixes the definition from. */\nfunction sourceSheet(source: CardSource, groupBy?: string) {\n return {\n read: source.key,\n ...(source.list ? { list: source.list.key } : {}),\n /* What the rows are - a state as of today or a ledger over time - decides whether a period is a filter at all. */\n description: source.description,\n conditions: source.conditions.map(one => ({ name: one.name, type: one.type, ...(one.required ? { required: true } : {}), ...(one.values ? { values: one.values } : {}), ...(one.description ? { description: one.description } : {}) })),\n columns: source.columns.map(one => ({ name: one.name, type: one.type, ...(one.currency ? { currency: one.currency } : {}) })),\n ...(groupBy ? { columnsAfterGroupBy: [...shapedColumnNames(source, { groupBy }).keys()] } : {})\n }\n}\n\n/** The reads a card may name - list read targets, and detail reads with declared lists (`key/list`). */\nexport function cardSources(): string[] {\n return [\n ...readTargetKeys(),\n ...detailReadKeys().flatMap(key => (detailReadOf(key)?.lists ?? []).map(list => `${key}/${list.key}`))\n ]\n}\n\n/** A saved card as a change needs it - whose it is and its version. */\nexport type CardOf = (id: string) => Promise<{ ownerId?: string; version: number } | undefined>\n\n/** The card in the person's domain - loaded late so the rules are tested without a database. */\nconst cardInDomain =\n (context: any): CardOf =>\n async id => {\n const [{ getRepository }, { DashboardCard }] = await Promise.all([import('@things-factory/shell'), import('./dashboard-card.js')])\n const card = await getRepository(DashboardCard).findOne({ where: { id, domain: { id: context?.state?.domain?.id } } as any })\n return card ? { ownerId: card.ownerId, version: card.version } : undefined\n }\n\nexport async function proposeDashboardCard(args: any, context: any, deps: { run?: any; passesDoor?: (door: string) => Promise<boolean>; today?: string; cardOf?: CardOf } = {}) {\n const name = typeof args?.name === 'string' ? args.name.trim().slice(0, 100) : ''\n if (!name) return { rejected: true, error: 'no-name', message: 'give the card a short name in the person\\'s language.' }\n const definition: CardDefinition = {\n schemaVersion: CARD_SCHEMA_VERSION,\n source: { read: args?.source?.read, ...(args?.source?.list ? { list: args.source.list } : {}), ...(args?.source?.where ? { where: args.source.where } : {}) },\n ...(args?.rows && Object.keys(args.rows).length ? { rows: args.rows } : {}),\n view: args?.view\n }\n const { source, issue } = sourceOf(definition.source, readTargetOf, detailReadOf)\n if (!source) {\n return {\n rejected: true,\n error: issue!.code,\n ...issue!.params,\n ...(issue!.code === 'unknown-read' || issue!.code === 'not-a-card-source' ? { sources: cardSources() } : {}),\n message: 'the card must read one of the sources listed (a detail read by `read` and `list`). Read the data with its tool first, then call again.'\n }\n }\n const issues = checkDefinition(definition, source)\n if (issues.length) {\n return {\n rejected: true,\n error: 'definition-unfit',\n issues,\n source: sourceSheet(source, definition.rows?.groupBy),\n message:\n 'fix every issue against the source below and call again now, in this turn - do not ask the person. source.where takes only the conditions listed (give every required one); ' +\n 'a column is filtered with rows.match; a period with no date condition is a rows.match on a date column, or left out when the source is a state as of today.'\n }\n }\n\n /* A change is of one's own card, from the version it was read at - a newer version is not overwritten unseen. */\n const cardId = typeof args?.cardId === 'string' && args.cardId ? args.cardId : undefined\n let baseVersion: number | undefined\n if (cardId) {\n const saved = await (deps.cardOf ?? cardInDomain(context))(cardId)\n if (!saved) return { rejected: true, error: 'unknown-card', message: 'there is no such card - leave cardId out to offer a new one.' }\n if (saved.ownerId !== context?.state?.user?.id) return { rejected: true, error: 'not-your-card', message: 'only its owner changes a card - offer a new card instead, without cardId.' }\n baseVersion = Number.isInteger(args?.baseVersion) ? args.baseVersion : saved.version\n if (baseVersion !== saved.version) return { rejected: true, error: 'stale-version', current: saved.version, message: `the card is at version ${saved.version} now - start from it and call again.` }\n }\n\n /* Run once as the person - what cannot be drawn is not offered. */\n const ran = await runCard(definition, {\n readTargetOf,\n detailReadOf,\n passesDoor: deps.passesDoor ?? (door => toolAccessFor(context)(door)),\n run: deps.run ?? runAsCaller(context),\n today: deps.today ?? todayIn(context?.state?.domain?.timezone)\n })\n if (ran.issue) {\n return { rejected: true, error: ran.issue.code, ...(ran.issue.params ? { params: ran.issue.params } : {}), source: sourceSheet(source, definition.rows?.groupBy), message: 'the card could not be drawn - fix the definition and call again in this turn; say plainly if it cannot be.' }\n }\n /*\n * A table or chart that draws nothing today is offered only when the person asked for something that may be empty - a\n * request that expects rows was most likely read wrong (2026-10-24: 「이번 달 미수금」 filtered a state of today by the\n * contract date and drew nothing).\n */\n if (ran.counts && ran.counts.shown === 0 && definition.view.kind !== 'number' && args?.allowEmpty !== true) {\n return {\n rejected: true,\n error: 'draws-nothing',\n read: ran.counts.read,\n source: sourceSheet(source, definition.rows?.groupBy),\n message:\n `no row meets these conditions today (${ran.counts.read} read). Read the source's description: a state as of today takes no period filter. ` +\n 'Fix the conditions and call again now; only if the person asked for something that may well be empty, call again with allowEmpty: true.'\n }\n }\n /*\n * Bars or a line whose names repeat cannot be read - two bars of one customer are two contracts the reader cannot tell\n * apart (2026-10-24: 「미수금 상위 5곳」 drew one customer twice). Asked when the card is offered; a saved card draws as\n * its data stands.\n */\n const x: unknown[] = Array.isArray((ran.block as any)?.x) ? (ran.block as any).x : []\n if (!definition.rows?.groupBy && new Set(x).size < x.length) {\n const label = definition.view.label!\n return {\n rejected: true,\n error: 'label-repeats',\n label,\n use: `rows.groupBy: '${label}' (and sortBy a summed value)`,\n source: sourceSheet(source, label),\n message: `the bars' names (${label}) repeat - one name stands for several rows. Group by it so each name is one bar, and call again now, in this turn.`\n }\n }\n\n return {\n proposed: true,\n kind: 'dashboard-card',\n icon: 'dashboard_customize',\n label: name,\n card: {\n name,\n ...(typeof args?.description === 'string' && args.description.trim() ? { description: args.description.trim().slice(0, 300) } : {}),\n definition,\n ...(cardId ? { cardId, baseVersion } : {})\n },\n message:\n 'the card is offered with its preview; the person saves it to their dashboard by pressing it. Say in one line what it shows. ' +\n 'Do not ask whether to save it, and do not repeat its numbers - the preview shows them to whoever looks.'\n }\n}\n\nconst COLUMN = { type: 'string' }\n\nconst spec = {\n kind: 'read',\n name: CARD_TOOL,\n /* Only someone who may save a card is offered one. The data's doors are asked when it runs, as the person. */\n doors: ['Mutation.saveDashboardCardProposal'],\n description:\n 'Offer a dashboard card - a definition the person saves to 나의 대시보드, drawn afterwards from the data without you. ' +\n 'source.read is a read you have (a list target, or a detail read with source.list naming one of its lists); source.where holds its conditions or arguments - ' +\n 'for a period use relative values (today, this-month-start, this-month-end, last-month-start, this-year-start, -30d; for an integer year or month: this-year, last-month) so the card stays current. ' +\n `rows: match [{column, op: ${MATCH_OPS.join('|')}, value}], groupBy (a date groups by month), sortBy (a column, or count when grouped), desc, limit. ` +\n `view.kind: ${VIEW_KINDS.join('|')}; label is the column along the bottom of a chart (the groupBy column by default); values are the number columns summed or drawn (none counts rows); columns lists a table's columns. ` +\n 'To change a saved card, pass its cardId and the version you changed as baseVersion.',\n schema: {\n type: 'object',\n properties: {\n name: { type: 'string', description: 'The card name, short, in the person\\'s language.' },\n description: { type: 'string', description: 'One line of what it shows.' },\n source: {\n type: 'object',\n properties: {\n read: { type: 'string', description: 'The read key - office.receivables, office.cash-transaction.' },\n list: { type: 'string', description: 'A detail read\\'s list key.' },\n where: { type: 'object', description: 'Conditions or arguments by name; dates may be relative.', additionalProperties: true }\n },\n required: ['read']\n },\n rows: {\n type: 'object',\n properties: {\n match: {\n type: 'array',\n items: { type: 'object', properties: { column: COLUMN, op: { type: 'string', enum: [...MATCH_OPS] }, value: { type: 'string', description: 'A number, a day, a word or a relative date.' } }, required: ['column', 'op', 'value'] }\n },\n groupBy: COLUMN,\n sortBy: COLUMN,\n desc: { type: 'boolean' },\n limit: { type: 'integer' }\n }\n },\n view: {\n type: 'object',\n properties: {\n kind: { type: 'string', enum: [...VIEW_KINDS] },\n label: COLUMN,\n values: { type: 'array', items: COLUMN },\n columns: { type: 'array', items: COLUMN },\n unit: { type: 'string' }\n },\n required: ['kind']\n },\n allowEmpty: { type: 'boolean', description: 'true only when the person asked for something that may be empty today (overdue items, alerts).' },\n cardId: { type: 'string', description: 'A saved card being changed.' },\n baseVersion: { type: 'integer', description: 'The version of that card the change starts from.' }\n },\n required: ['name', 'source', 'view']\n },\n builder: (args: any, context: any) => proposeDashboardCard(args, context)\n} as ToolSpec\n\n/**\n * A saved card's name, version and definition - no values - for a change asked from the dashboard (「AI로 고치기」 picks\n * the card; the model reads it here, then offers the changed definition with cardId and baseVersion).\n */\nexport async function readDashboardCard(args: any, context: any, cardOf?: (id: string) => Promise<{ card?: any; access?: string }>) {\n const id = typeof args?.id === 'string' ? args.id.trim().replace(/^dashboard-card:/, '') : ''\n if (!id) return { rejected: true, error: 'no-id', message: 'give the card id the person picked.' }\n const found = await (cardOf ?? usableInDomain(context))(id)\n if (!found?.card) return { rejected: true, error: 'unknown-card', message: 'there is no such card - say so.' }\n if (!found.access) return { rejected: true, error: 'card-not-shared', message: 'this card is not shared with the person - say so.' }\n const { card, access } = found\n return {\n id: card.id,\n name: card.name,\n version: card.version,\n access,\n definition: card.definition,\n message:\n access === 'owner'\n ? `to change it, call ${CARD_TOOL} with the whole changed definition, cardId \"${card.id}\" and baseVersion ${card.version}.`\n : `the person does not own it - offer a new card (no cardId) made from this definition.`\n }\n}\n\n/** A card in the person's domain and how they may use it - loaded late so the rules are tested without a database. */\nconst usableInDomain =\n (context: any) =>\n async (id: string): Promise<{ card?: any; access?: string }> => {\n const [{ getRepository }, { DashboardCard, DashboardCardShare }, { cardAccessOf, departmentsOfEmployees }] = await Promise.all([\n import('@things-factory/shell'),\n import('./dashboard-card.js'),\n import('./card-access.js')\n ])\n const domainId = context?.state?.domain?.id\n const card = await getRepository(DashboardCard).findOne({ where: { id, domain: { id: domainId } } as any })\n if (!card) return {}\n const shares = await getRepository(DashboardCardShare).find({ where: { card: { id: card.id } } as any })\n let records: any[] | undefined\n try {\n const answer = await runAsCaller(context)('query { myEmployees { active department { id } } }', {})\n records = answer.errors?.length ? [] : answer.data?.myEmployees\n } catch {\n records = []\n }\n const access = cardAccessOf(card, { userId: context?.state?.user?.id, domainId, departmentIds: departmentsOfEmployees(records) }, shares)\n return { card, access }\n }\n\nconst readSpec = {\n kind: 'read',\n name: 'readDashboardCard',\n doors: ['Query.dashboardCardRun'],\n description: 'Read a saved dashboard card the person picked - its name, version and definition, never its values - before changing it.',\n schema: { type: 'object', properties: { id: { type: 'string', description: 'The card id the person picked.' } }, required: ['id'] },\n builder: (args: any, context: any) => readDashboardCard(args, context)\n} as ToolSpec\n\nexport const cardCategory: ToolCategory = {\n name: CARD_CATEGORY,\n description: 'dashboard cards - definitions the person saves and the dashboard draws from the data',\n guidance: [\n `- When the person asks for a card, a tile, a chart or a table on their dashboard (대시보드 카드, 카드로 만들어 줘), read the data with its read tool to learn its columns, then call ${CARD_TOOL} in the same turn. Never ask whether to make or save it; the card is the answer.`,\n `- A card is drawn later from the data as whoever opens it - you are not there. Put periods as relative values in source.where, never today's dates; say \"this month\" as this-month-start / this-month-end.`,\n `- \"Top N\" is rows.sortBy the amount, desc: true, limit N. Counting customers, places or months (상위 5곳, 거래처별, 월별) is rows.groupBy that column with view.values - each name one bar. Amounts in two currencies are never added - group or match by the currency, or use a column already in won.`,\n `- When ${CARD_TOOL} refuses, fix the definition from what it returns and call again in the same turn; if the data cannot be drawn this way, say so in one line.`,\n `- When the person picked a dashboard card to change (its id after [keys] dashboardCards), call readDashboardCard, then ${CARD_TOOL} with the whole changed definition, cardId and baseVersion - in the same turn. When the change makes its name untrue (top 5 → top 10), give it a true name.`\n ].join('\\n'),\n specs: [spec, readSpec]\n}\n\nregisterToolCategory(cardCategory)\n/* In reach of every conversation, in front on the dashboard page only (ADR-0061 decision 9). */\nregisterConversationCategory({ name: CARD_CATEGORY, frontPages: [DASHBOARD_PAGE] })\n"]}
|
|
1
|
+
{"version":3,"file":"card-tool.js","sourceRoot":"","sources":["../../../server/service/card/card-tool.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * proposeDashboardCard - the assistant writes a card's definition in a conversation; the person saves it by pressing\n * the card (ADR-0160 decision 9).\n *\n * The server checks the definition against the read it names and runs it once as the person, so a card that cannot\n * be drawn is sent back with what the read has - never offered. The proposal carries the definition only: the\n * preview on the card is run again by whoever looks at the conversation, as themselves (decision 6), and the model\n * is told no values - its answer is read by everyone the conversation lets in, whatever door they pass.\n */\nimport { registerToolCategory, type ToolCategory, type ToolSpec } from '@things-factory/ai-client-base'\nimport {\n detailReadKeys,\n detailReadOf,\n detailToolName,\n queryToolName,\n readTargetKeys,\n readTargetOf,\n registerConversationCategory,\n runAsCaller,\n toolAccessFor\n} from '@things-factory/ai-assistant'\n\nimport { CARD_SCHEMA_VERSION, checkCardIntent, checkDefinition, LIST_OPS, MATCH_OPS, sourceOf, todayIn, VIEW_KINDS, type CardDefinition, type CardSource } from './card-definition.js'\nimport { runCard, shapedColumnNames } from './card-run.js'\n\nimport { CARD_AXIS, CARD_CATEGORY, CARD_TOOL, DASHBOARD_PAGE } from './card-names.js'\nimport { cardIdOf } from './card-proposal.js'\n\nexport { CARD_CATEGORY, CARD_TOOL, DASHBOARD_PAGE }\n\n/** What a read offers a card - for a refusal the model fixes the definition from. */\nfunction sourceSheet(source: CardSource, groupBy?: string) {\n return {\n read: source.key,\n ...(source.list ? { list: source.list.key } : {}),\n /* What the rows are - a state as of today or a ledger over time - decides whether a period is a filter at all. */\n description: source.description,\n ...(source.mine ? { mine: 'the rows are the viewer\\'s own, whoever looks - no filter on a person' } : {}),\n conditions: source.conditions.map(one => ({ name: one.name, type: one.type, ...(one.format ? { format: 'YYYY-MM' } : {}), ...(one.many ? { takes: 'one value or a list' } : {}), ...(one.required ? { required: true } : {}), ...(one.values ? { values: one.values } : {}), ...(one.description ? { description: one.description } : {}) })),\n columns: source.columns.map(one => ({ name: one.name, type: one.type, ...(one.currency ? { currency: one.currency } : {}) })),\n ...(groupBy ? { columnsAfterGroupBy: [...shapedColumnNames(source, { groupBy }).keys()] } : {})\n }\n}\n\n/** The reads a card may name - each list read target and each detail read's list, with the tool the model knows it by. */\nexport function cardSources(): { read: string; list?: string; tool: string; mine?: true }[] {\n return [\n ...readTargetKeys().map(key => ({ read: key, tool: queryToolName(key), ...(readTargetOf(key)?.mine ? { mine: true as const } : {}) })),\n ...detailReadKeys().flatMap(key => (detailReadOf(key)?.lists ?? []).map(list => ({ read: key, list: list.key, tool: detailToolName(key), ...(detailReadOf(key)?.mine ? { mine: true as const } : {}) })))\n ]\n}\n\n/**\n * The read a model named - its key, or the name of the tool it reads it with (`queryPlantJobOrder` is `plant.job-order`):\n * the model knows reads by their tools, and named the tool as the card's source (2026-10-25, plant).\n */\nexport function readKeyOf(given: unknown): unknown {\n if (typeof given !== 'string' || readTargetOf(given) || detailReadOf(given)) return given\n return readTargetKeys().find(key => queryToolName(key) === given) ?? detailReadKeys().find(key => detailToolName(key) === given) ?? given\n}\n\n/** A part of a definition without the fields the model left empty - `groupBy: null` is no grouping, not a column named null. */\nfunction withoutEmpty<T extends Record<string, any>>(given: T | undefined): T | undefined {\n if (!given || typeof given !== 'object' || Array.isArray(given)) return given\n const kept = Object.entries(given).filter(([, value]) => value !== null && value !== undefined && value !== '')\n return kept.length ? (Object.fromEntries(kept) as T) : undefined\n}\n\n/** A saved card as a change needs it - whose it is and its version. */\nexport type CardOf = (id: string) => Promise<{ ownerId?: string; version: number } | undefined>\n\n/** The card in the person's domain - loaded late so the rules are tested without a database. */\nconst cardInDomain =\n (context: any): CardOf =>\n async id => {\n const [{ getRepository }, { DashboardCard }] = await Promise.all([import('@things-factory/shell'), import('./dashboard-card.js')])\n const card = await getRepository(DashboardCard).findOne({ where: { id, domain: { id: context?.state?.domain?.id } } as any })\n return card ? { ownerId: card.ownerId, version: card.version } : undefined\n }\n\nexport async function proposeDashboardCard(args: any, context: any, deps: { run?: any; passesDoor?: (door: string) => Promise<boolean>; today?: string; cardOf?: CardOf } = {}) {\n const name = typeof args?.name === 'string' ? args.name.trim().slice(0, 100) : ''\n if (!name) return { rejected: true, error: 'no-name', message: 'give the card a short name in the person\\'s language.' }\n const where = withoutEmpty(args?.source?.where)\n const rows = withoutEmpty(args?.rows)\n /* One value, or the list `in` and `not_in` take - kept as the match's value either way. */\n if (Array.isArray(rows?.match))\n rows.match = rows.match.map((one: any) => {\n if (!one || typeof one !== 'object' || !('values' in one)) return one\n const { values, ...match } = one\n return (LIST_OPS as readonly string[]).includes(match.op) ? { ...match, value: values } : match.value === undefined ? { ...match, value: values } : match\n })\n const definition: CardDefinition = {\n schemaVersion: CARD_SCHEMA_VERSION,\n source: { read: readKeyOf(args?.source?.read) as string, ...(args?.source?.list ? { list: args.source.list } : {}), ...(where ? { where } : {}) },\n ...(rows ? { rows } : {}),\n view: withoutEmpty(args?.view) as any\n }\n const { source, issue } = sourceOf(definition.source, readTargetOf, detailReadOf)\n if (!source) {\n return {\n rejected: true,\n error: issue!.code,\n ...issue!.params,\n ...(issue!.code === 'unknown-read' || issue!.code === 'not-a-card-source' ? { sources: cardSources() } : {}),\n message: 'source.read is one of the sources listed - its read key or its tool (a detail read also names its list). Call again now with one of them.'\n }\n }\n const issues = checkDefinition(definition, source)\n if (issues.length) {\n return {\n rejected: true,\n error: 'definition-unfit',\n issues,\n source: sourceSheet(source, definition.rows?.groupBy),\n message:\n 'fix every issue against the source below and call again now, in this turn - do not ask the person. source.where takes only the conditions listed (give every required one); ' +\n 'a column is filtered with rows.match; a period with no date condition is a rows.match on a date column, or left out when the source is a state as of today.'\n }\n }\n /* What the card says it shows, and whose rows it shows - checked before it runs, so a wrong card is never drawn once. */\n const description = typeof args?.description === 'string' ? args.description : undefined\n const user = context?.state?.user\n const mine = cardSources().filter(one => one.mine).map(one => (one.list ? `${one.read} (list ${one.list})` : one.read))\n const intent = checkCardIntent({ name, description, definition }, source, { id: user?.id, name: user?.name, email: user?.email }, mine)\n if (intent.length) {\n return {\n rejected: true,\n error: 'card-says-otherwise',\n issues: intent,\n source: sourceSheet(source, definition.rows?.groupBy),\n message:\n 'the card would not show what it says - fix each issue and call again now, in this turn. ' +\n 'period-without-condition: put the period in source.where with relative values (this-month-start..this-month-end; a YYYY-MM condition this-month; the last 6 months -5m-start..this-month-end) or as a rows.match on a date column - or, when the source is a state as of today, take the period out of the name. ' +\n 'period-held-to-fixed-days: use relative values so the card stays current. period-unlike-condition: make the condition the period the name says. ' +\n 'viewer-filter-on-own-read: drop the filter - the read is the viewer\\'s own whoever looks. viewer-filter-use-own-read: read one of the listed own reads instead, with no filter on the person.'\n }\n }\n\n /* A change is of one's own card, from the version it was read at - a newer version is not overwritten unseen. */\n const given = typeof args?.cardId === 'string' ? args.cardId.trim() : ''\n const cardId = given ? cardIdOf(given) : undefined\n if (given && !cardId) return { rejected: true, error: 'card-id-unreadable', cardId: given, message: `cardId is the card's uuid or its key ${CARD_AXIS}:<uuid>, as readDashboardCard gave it - call again now with it.` }\n let baseVersion: number | undefined\n if (cardId) {\n const saved = await (deps.cardOf ?? cardInDomain(context))(cardId)\n if (!saved) return { rejected: true, error: 'unknown-card', message: 'there is no such card - leave cardId out to offer a new one.' }\n if (saved.ownerId !== context?.state?.user?.id) return { rejected: true, error: 'not-your-card', message: 'only its owner changes a card - offer a new card instead, without cardId.' }\n baseVersion = Number.isInteger(args?.baseVersion) ? args.baseVersion : saved.version\n if (baseVersion !== saved.version) return { rejected: true, error: 'stale-version', current: saved.version, message: `the card is at version ${saved.version} now - start from it and call again.` }\n }\n\n /* Run once as the person - what cannot be drawn is not offered. */\n const ran = await runCard(definition, {\n readTargetOf,\n detailReadOf,\n passesDoor: deps.passesDoor ?? (door => toolAccessFor(context)(door)),\n run: deps.run ?? runAsCaller(context),\n today: deps.today ?? todayIn(context?.state?.domain?.timezone)\n })\n if (ran.issue) {\n return { rejected: true, error: ran.issue.code, ...(ran.issue.params ? { params: ran.issue.params } : {}), source: sourceSheet(source, definition.rows?.groupBy), message: 'the card could not be drawn - fix the definition and call again in this turn; say plainly if it cannot be.' }\n }\n /*\n * A table or chart that draws nothing today is offered only when the person asked for something that may be empty - a\n * request that expects rows was most likely read wrong (2026-10-24: 「이번 달 미수금」 filtered a state of today by the\n * contract date and drew nothing).\n */\n if (ran.counts && ran.counts.shown === 0 && definition.view.kind !== 'number' && args?.allowEmpty !== true) {\n return {\n rejected: true,\n error: 'draws-nothing',\n read: ran.counts.read,\n source: sourceSheet(source, definition.rows?.groupBy),\n message:\n `no row meets these conditions today (${ran.counts.read} read). Read the source's description: a state as of today takes no period filter. ` +\n 'Fix the conditions and call again now; only if the person asked for something that may well be empty, call again with allowEmpty: true.'\n }\n }\n /*\n * Bars or a line whose names repeat cannot be read - two bars of one customer are two contracts the reader cannot tell\n * apart (2026-10-24: 「미수금 상위 5곳」 drew one customer twice). Asked when the card is offered; a saved card draws as\n * its data stands.\n */\n const x: unknown[] = Array.isArray((ran.block as any)?.x) ? (ran.block as any).x : []\n if (!definition.rows?.groupBy && new Set(x).size < x.length) {\n const label = definition.view.label!\n return {\n rejected: true,\n error: 'label-repeats',\n label,\n use: `rows.groupBy: '${label}' (and sortBy a summed value)`,\n source: sourceSheet(source, label),\n message: `the bars' names (${label}) repeat - one name stands for several rows. Group by it so each name is one bar, and call again now, in this turn.`\n }\n }\n\n return {\n proposed: true,\n kind: 'dashboard-card',\n icon: 'dashboard_customize',\n label: name,\n card: {\n name,\n ...(typeof args?.description === 'string' && args.description.trim() ? { description: args.description.trim().slice(0, 300) } : {}),\n definition,\n ...(cardId ? { cardId, baseVersion } : {})\n },\n message:\n 'the card is offered with its preview; the person saves it to their dashboard by pressing it. Say in one line what it shows. ' +\n 'Do not ask whether to save it, and do not repeat its numbers - the preview shows them to whoever looks.'\n }\n}\n\nconst COLUMN = { type: 'string' }\n\nconst spec = {\n kind: 'read',\n name: CARD_TOOL,\n /* Only someone who may save a card is offered one. The data's doors are asked when it runs, as the person. */\n doors: ['Mutation.saveDashboardCardProposal'],\n description:\n 'Offer a dashboard card - a definition the person saves to 나의 대시보드, drawn afterwards from the data without you. ' +\n 'source.read is a read you have (a list target, or a detail read with source.list naming one of its lists); source.where holds its conditions or arguments - ' +\n 'for a period use relative values so the card stays current - a day: today, -30d, this-month-start, this-month-end, last-month-start, this-year-start, -5m-start (the first day five months ago); ' +\n 'an integer year or month: this-year, last-month; a month as YYYY-MM: this-month, last-month, -5m. The last 6 months is -5m-start..this-month-end. A name that says a period has that condition. ' +\n `rows: match [{column, op: ${MATCH_OPS.join('|')}, value}] - every match holds (two eq of one column never do: use in [..]); not_in [..] for \"not one of\"; groupBy (a date groups by month), sortBy (a column, or count when grouped), desc, limit. ` +\n 'A read marked mine is the viewer\\'s own rows, whoever looks - prefer it for \"my ...\" cards and never filter by a person\\'s name or id. ' +\n `view.kind: ${VIEW_KINDS.join('|')}; label is the column along the bottom of a chart (the groupBy column by default); values are the number columns summed or drawn (none counts rows); columns lists a table's columns. ` +\n 'To change a saved card, pass its cardId and the version you changed as baseVersion.',\n schema: {\n type: 'object',\n properties: {\n name: { type: 'string', description: 'The card name, short, in the person\\'s language.' },\n description: { type: 'string', description: 'One line of what it shows.' },\n source: {\n type: 'object',\n properties: {\n read: { type: 'string', description: 'The read - its key (office.receivables) or the tool that reads it (readOfficeReceivables).' },\n list: { type: 'string', description: 'A detail read\\'s list key.' },\n where: { type: 'object', description: 'Conditions or arguments by name; dates may be relative.', additionalProperties: true }\n },\n required: ['read']\n },\n rows: {\n type: 'object',\n properties: {\n match: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n column: COLUMN,\n op: { type: 'string', enum: [...MATCH_OPS] },\n value: { type: 'string', description: 'A number, a day, a word or a relative date.' },\n values: { type: 'array', items: { type: 'string' }, description: `The values of ${LIST_OPS.join(' and ')}, in place of value.` }\n },\n required: ['column', 'op']\n }\n },\n groupBy: COLUMN,\n sortBy: COLUMN,\n desc: { type: 'boolean' },\n limit: { type: 'integer' }\n }\n },\n view: {\n type: 'object',\n properties: {\n kind: { type: 'string', enum: [...VIEW_KINDS] },\n label: COLUMN,\n values: { type: 'array', items: COLUMN },\n columns: { type: 'array', items: COLUMN },\n unit: { type: 'string' }\n },\n required: ['kind']\n },\n allowEmpty: { type: 'boolean', description: 'true only when the person asked for something that may be empty today (overdue items, alerts).' },\n cardId: { type: 'string', description: 'A saved card being changed.' },\n baseVersion: { type: 'integer', description: 'The version of that card the change starts from.' }\n },\n required: ['name', 'source', 'view']\n },\n builder: (args: any, context: any) => proposeDashboardCard(args, context)\n} as ToolSpec\n\n/**\n * A saved card's name, version and definition - no values - for a change asked from the dashboard (「AI로 고치기」 picks\n * the card; the model reads it here, then offers the changed definition with cardId and baseVersion).\n */\nexport async function readDashboardCard(args: any, context: any, cardOf?: (id: string) => Promise<{ card?: any; access?: string }>) {\n const given = typeof args?.id === 'string' ? args.id.trim() : ''\n if (!given) return { rejected: true, error: 'no-id', message: 'give the card id the person picked.' }\n const id = cardIdOf(given)\n if (!id) return { rejected: true, error: 'card-id-unreadable', id: given, message: `a card is named by its key ${CARD_AXIS}:<uuid> (after [keys] dashboardCards) or its uuid - call again now with it.` }\n const found = await (cardOf ?? usableInDomain(context))(id)\n if (!found?.card) return { rejected: true, error: 'unknown-card', message: 'there is no such card - say so.' }\n if (!found.access) return { rejected: true, error: 'card-not-shared', message: 'this card is not shared with the person - say so.' }\n const { card, access } = found\n return {\n id: card.id,\n name: card.name,\n version: card.version,\n access,\n definition: card.definition,\n message:\n access === 'owner'\n ? `to change it, call ${CARD_TOOL} with the whole changed definition, cardId \"${card.id}\" and baseVersion ${card.version}.`\n : `the person does not own it - offer a new card (no cardId) made from this definition.`\n }\n}\n\n/** A card in the person's domain and how they may use it - loaded late so the rules are tested without a database. */\nconst usableInDomain =\n (context: any) =>\n async (id: string): Promise<{ card?: any; access?: string }> => {\n const [{ getRepository }, { DashboardCard, DashboardCardShare }, { cardAccessOf, departmentsOfEmployees }] = await Promise.all([\n import('@things-factory/shell'),\n import('./dashboard-card.js'),\n import('./card-access.js')\n ])\n const domainId = context?.state?.domain?.id\n const card = await getRepository(DashboardCard).findOne({ where: { id, domain: { id: domainId } } as any })\n if (!card) return {}\n const shares = await getRepository(DashboardCardShare).find({ where: { card: { id: card.id } } as any })\n let records: any[] | undefined\n try {\n const answer = await runAsCaller(context)('query { myEmployees { active department { id } } }', {})\n records = answer.errors?.length ? [] : answer.data?.myEmployees\n } catch {\n records = []\n }\n const access = cardAccessOf(card, { userId: context?.state?.user?.id, domainId, departmentIds: departmentsOfEmployees(records) }, shares)\n return { card, access }\n }\n\nconst readSpec = {\n kind: 'read',\n name: 'readDashboardCard',\n doors: ['Query.dashboardCardRun'],\n description: 'Read a saved dashboard card the person picked - its name, version and definition, never its values - before changing it.',\n schema: { type: 'object', properties: { id: { type: 'string', description: 'The card id the person picked.' } }, required: ['id'] },\n builder: (args: any, context: any) => readDashboardCard(args, context)\n} as ToolSpec\n\nexport const cardCategory: ToolCategory = {\n name: CARD_CATEGORY,\n description: 'dashboard cards - definitions the person saves and the dashboard draws from the data',\n guidance: [\n `- When the person asks for a card, a tile, a chart or a table on their dashboard (대시보드 카드, 카드로 만들어 줘), read the data with its read tool to learn its columns, then call ${CARD_TOOL} in the same turn. Never ask whether to make or save it; the card is the answer.`,\n `- A card is drawn later from the data as whoever opens it - you are not there. Put periods as relative values in source.where, never today's dates; say \"this month\" as this-month-start / this-month-end.`,\n `- \"Top N\" is rows.sortBy the amount, desc: true, limit N. Counting customers, places or months (상위 5곳, 거래처별, 월별) is rows.groupBy that column with view.values - each name one bar. Amounts in two currencies are never added - group or match by the currency, or use a column already in won.`,\n `- When ${CARD_TOOL} refuses, fix the definition from what it returns and call again in the same turn; if the data cannot be drawn this way, say so in one line.`,\n `- When the person picked a dashboard card to change (its id after [keys] dashboardCards), call readDashboardCard, then ${CARD_TOOL} with the whole changed definition, cardId and baseVersion - in the same turn. When the change makes its name untrue (top 5 → top 10), give it a true name.`\n ].join('\\n'),\n specs: [spec, readSpec]\n}\n\nregisterToolCategory(cardCategory)\n/* In reach of every conversation, in front on the dashboard page and for a picked card (ADR-0061 decision 9). */\nregisterConversationCategory({\n name: CARD_CATEGORY,\n frontPages: [DASHBOARD_PAGE],\n frontAxes: [CARD_AXIS],\n /* FINISH_RULE on this page: the card is what was asked for. */\n pageRule:\n `This turn is asked from 나의 대시보드, where what the person asks to see - a figure, a total, a count, a list, a chart - is a dashboard card. ` +\n `Read what it needs and end the turn with ${CARD_TOOL}, in this turn; never answer with the figure in words, and never ask whether to make it. ` +\n `Only a question about the dashboard itself (how to share, how to move a card) is answered in words.`\n})\n"]}
|
|
@@ -42,6 +42,7 @@ export declare class DashboardShareOptions {
|
|
|
42
42
|
departmentsAvailable: boolean;
|
|
43
43
|
departments: DashboardShareChoice[];
|
|
44
44
|
peopleAvailable: boolean;
|
|
45
|
+
roles: DashboardShareChoice[];
|
|
45
46
|
}
|
|
46
47
|
export declare class DashboardCardShareInput {
|
|
47
48
|
scope: DashboardCardShareScope;
|
|
@@ -9,11 +9,11 @@ let DashboardCardShareItem = class DashboardCardShareItem {
|
|
|
9
9
|
};
|
|
10
10
|
exports.DashboardCardShareItem = DashboardCardShareItem;
|
|
11
11
|
tslib_1.__decorate([
|
|
12
|
-
(0, type_graphql_1.Field)(type => dashboard_card_js_1.DashboardCardShareScope, { description: 'A person, a department or the domain.' }),
|
|
12
|
+
(0, type_graphql_1.Field)(type => dashboard_card_js_1.DashboardCardShareScope, { description: 'A person, a department, a role or the domain.' }),
|
|
13
13
|
tslib_1.__metadata("design:type", String)
|
|
14
14
|
], DashboardCardShareItem.prototype, "scope", void 0);
|
|
15
15
|
tslib_1.__decorate([
|
|
16
|
-
(0, type_graphql_1.Field)({ description: 'The user, department or domain id.' }),
|
|
16
|
+
(0, type_graphql_1.Field)({ description: 'The user, department, role or domain id.' }),
|
|
17
17
|
tslib_1.__metadata("design:type", String)
|
|
18
18
|
], DashboardCardShareItem.prototype, "targetId", void 0);
|
|
19
19
|
tslib_1.__decorate([
|
|
@@ -166,6 +166,10 @@ tslib_1.__decorate([
|
|
|
166
166
|
(0, type_graphql_1.Field)({ description: 'Whether the viewer may look people up to share with them.' }),
|
|
167
167
|
tslib_1.__metadata("design:type", Boolean)
|
|
168
168
|
], DashboardShareOptions.prototype, "peopleAvailable", void 0);
|
|
169
|
+
tslib_1.__decorate([
|
|
170
|
+
(0, type_graphql_1.Field)(type => [DashboardShareChoice], { description: "The domain's roles, the ones it inherits included - a card shared with one reaches whoever holds it." }),
|
|
171
|
+
tslib_1.__metadata("design:type", Array)
|
|
172
|
+
], DashboardShareOptions.prototype, "roles", void 0);
|
|
169
173
|
exports.DashboardShareOptions = DashboardShareOptions = tslib_1.__decorate([
|
|
170
174
|
(0, type_graphql_1.ObjectType)({ description: 'What a card may be shared with by the viewer.' })
|
|
171
175
|
], DashboardShareOptions);
|
|
@@ -173,11 +177,11 @@ let DashboardCardShareInput = class DashboardCardShareInput {
|
|
|
173
177
|
};
|
|
174
178
|
exports.DashboardCardShareInput = DashboardCardShareInput;
|
|
175
179
|
tslib_1.__decorate([
|
|
176
|
-
(0, type_graphql_1.Field)(type => dashboard_card_js_1.DashboardCardShareScope, { description: 'A person, a department or the domain.' }),
|
|
180
|
+
(0, type_graphql_1.Field)(type => dashboard_card_js_1.DashboardCardShareScope, { description: 'A person, a department, a role or the domain.' }),
|
|
177
181
|
tslib_1.__metadata("design:type", String)
|
|
178
182
|
], DashboardCardShareInput.prototype, "scope", void 0);
|
|
179
183
|
tslib_1.__decorate([
|
|
180
|
-
(0, type_graphql_1.Field)({ description: 'The user, department or domain id.' }),
|
|
184
|
+
(0, type_graphql_1.Field)({ description: 'The user, department, role or domain id.' }),
|
|
181
185
|
tslib_1.__metadata("design:type", String)
|
|
182
186
|
], DashboardCardShareInput.prototype, "targetId", void 0);
|
|
183
187
|
tslib_1.__decorate([
|