@things-factory/worklist 10.1.271 → 10.1.272
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/dist-client/bootstrap.d.ts +1 -0
- package/dist-client/bootstrap.js +1 -0
- package/dist-client/bootstrap.js.map +1 -1
- package/dist-client/components/person-question-todo.d.ts +16 -0
- package/dist-client/components/person-question-todo.js +75 -0
- package/dist-client/components/person-question-todo.js.map +1 -0
- package/dist-client/components/source-link.d.ts +1 -1
- package/dist-client/pages/activity-instance/activity-instance-view.d.ts +1 -1
- package/dist-client/pages/activity-thread/activity-thread-page.d.ts +1 -1
- package/dist-client/route.d.ts +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/controllers/activity-instance/complete.js +4 -0
- package/dist-server/controllers/activity-instance/complete.js.map +1 -1
- package/dist-server/controllers/activity-instance/hand-out.d.ts +2 -0
- package/dist-server/controllers/activity-instance/hand-out.js +7 -0
- package/dist-server/controllers/activity-instance/hand-out.js.map +1 -1
- package/dist-server/service/activity/index.d.ts +1 -1
- package/dist-server/service/activity-instance/index.d.ts +1 -1
- package/dist-server/service/activity-template/index.d.ts +1 -1
- package/dist-server/service/activity-thread/index.d.ts +1 -1
- package/dist-server/service/approval-notice/approval-notice-rules.d.ts +9 -0
- package/dist-server/service/approval-notice/approval-notice-rules.js +25 -1
- package/dist-server/service/approval-notice/approval-notice-rules.js.map +1 -1
- package/dist-server/service/approval-notice/approval-notice.d.ts +10 -0
- package/dist-server/service/approval-notice/approval-notice.js +27 -0
- package/dist-server/service/approval-notice/approval-notice.js.map +1 -1
- package/dist-server/service/index.d.ts +5 -3
- package/dist-server/service/index.js +12 -2
- package/dist-server/service/index.js.map +1 -1
- package/dist-server/service/person-question/index.d.ts +4 -0
- package/dist-server/service/person-question/index.js +19 -0
- package/dist-server/service/person-question/index.js.map +1 -0
- package/dist-server/service/person-question/person-question-activity.d.ts +67 -0
- package/dist-server/service/person-question/person-question-activity.js +46 -0
- package/dist-server/service/person-question/person-question-activity.js.map +1 -0
- package/dist-server/service/person-question/person-question-card.d.ts +1 -0
- package/dist-server/service/person-question/person-question-card.js +36 -0
- package/dist-server/service/person-question/person-question-card.js.map +1 -0
- package/dist-server/service/person-question/person-question-resolver.d.ts +10 -0
- package/dist-server/service/person-question/person-question-resolver.js +54 -0
- package/dist-server/service/person-question/person-question-resolver.js.map +1 -0
- package/dist-server/service/person-question/person-question-service.d.ts +17 -0
- package/dist-server/service/person-question/person-question-service.js +86 -0
- package/dist-server/service/person-question/person-question-service.js.map +1 -0
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +5 -4
- package/tests/complete-db.test.ts +2 -1
- package/tests/hand-out-notice-db.test.ts +99 -0
- package/tests/person-question-db.test.ts +158 -0
- package/translations/en.json +21 -1
- package/translations/es.json +21 -1
- package/translations/ja.json +21 -1
- package/translations/ko.json +21 -1
- package/translations/ms.json +21 -1
- package/translations/zh.json +21 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"person-question-activity.js","sourceRoot":"","sources":["../../../server/service/person-question/person-question-activity.ts"],"names":[],"mappings":";;;AAAA;;;;;GAKG;AACU,QAAA,wBAAwB,GAAG,iBAAiB,CAAA;AAE5C,QAAA,0BAA0B,GAAG;IACxC;QACE,IAAI,EAAE,gCAAwB;QAC9B,KAAK,EAAE,sBAAsB;QAC7B,WAAW,EAAE,yFAAyF;QACtG,KAAK,EAAE;YACL,0FAA0F;YAC1F,+GAA+G;YAC/G,wCAAwC;SACzC;QACD,OAAO,EAAE,OAAO;QAChB,QAAQ,EAAE,cAAc;QACxB,QAAQ,EAAE,eAAe;QACzB,YAAY,EAAE,MAAM;QACpB,QAAQ,EAAE,CAAC;QACX,YAAY,EAAE,OAAO;QACrB,QAAQ,EAAE,QAAQ;QAClB,MAAM,EAAE,gBAAgB;QACxB,QAAQ,EAAE,sBAAsB;QAChC,UAAU,EAAE,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,WAAW,EAAE,cAAc,EAAE,QAAQ,EAAE,UAAU,EAAE,IAAI,EAAE,eAAe,EAAE,CAAC;QAC5G,YAAY,EAAE,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,IAAI,EAAE,oBAAoB,EAAE,CAAC;QACvF,QAAQ,EAAE,EAAE;QACZ,UAAU,EAAE,EAAE;QACd,KAAK,EAAE;YACL,EAAE,IAAI,EAAE,WAAW,EAAE,WAAW,EAAE,yCAAyC,EAAE,GAAG,EAAE,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE;YAC/M,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE;YACzK,EAAE,IAAI,EAAE,UAAU,EAAE,WAAW,EAAE,cAAc,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE;YAClL,EAAE,IAAI,EAAE,UAAU,EAAE,WAAW,EAAE,mCAAmC,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE;YACtL,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,iCAAiC,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE;YACjM,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,yCAAyC,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE;YACtL,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,8CAA8C,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE;SACjL;QACD,YAAY,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE;KAC/B;CACF,CAAA","sourcesContent":["/*\n * 본인 확인 요청 (ADR-0200 decision 3) - a question asked about someone in a conversation that their records could not\n * answer: whether something can be done, by when, whether they agree. It goes to them as a to-do; the to-do shows the\n * question, what the records already answered, what is theirs to decide and a draft drawn from their records, and opens\n * the conversation. Their answer there ends it.\n */\nexport const PERSON_QUESTION_ACTIVITY = 'Person Question'\n\nexport const PERSON_QUESTION_ACTIVITIES = [\n {\n name: PERSON_QUESTION_ACTIVITY,\n label: 'confirmation request',\n description: 'A question about a person that only they can answer, handed to them from a conversation',\n guide: [\n 'the assistant offers it in a conversation about a person, and the one asking presses it.',\n 'it shows the question, what the records already answered, what is the person\\'s to decide and a draft answer.',\n 'answering in the conversation ends it.'\n ],\n release: '1.0.0',\n provider: 'hatiolab.com',\n category: 'collaboration',\n activityType: 'user',\n priority: 1,\n startingType: 'issue',\n multiple: 'single',\n uiType: 'custom-element',\n uiSource: 'person-question-todo',\n searchKeys: [{ name: 'question', description: 'The question', inputKey: 'question', tKey: 'field.subject' }],\n decisionKeys: [{ role: 'headline', inputPath: 'question', name: 'the question asked' }],\n viewType: '',\n viewSource: '',\n model: [\n { name: 'sessionId', description: 'The conversation the question came from', tag: 'sessionId', hidden: true, mandatory: true, inout: 'in', type: 'string', options: {}, unit: null, quantifier: [1], spec: {} },\n { name: 'asker', description: 'Who asked', tag: 'asker', hidden: true, mandatory: true, inout: 'in', type: 'string', options: {}, unit: null, quantifier: [1], spec: {} },\n { name: 'question', description: 'The question', tag: 'question', hidden: true, mandatory: true, inout: 'in', type: 'string', options: {}, unit: null, quantifier: [1], spec: {} },\n { name: 'answered', description: 'What the records already answered', tag: 'answered', hidden: true, inout: 'in', type: 'string', options: {}, unit: null, quantifier: [1], spec: {} },\n { name: 'decide', description: 'What is the person\\'s to decide', tag: 'decide', hidden: true, mandatory: true, inout: 'in', type: 'string', options: {}, unit: null, quantifier: [1], spec: {} },\n { name: 'draft', description: 'A draft answer drawn from their records', tag: 'draft', hidden: true, inout: 'in', type: 'string', options: {}, unit: null, quantifier: [1], spec: {} },\n { name: 'answer', description: 'What the person answered in the conversation', tag: 'answer', inout: 'out', type: 'string', options: {}, unit: null, quantifier: [1], spec: {} }\n ],\n standardTime: 2 * 24 * 60 * 60\n }\n]\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function registerPersonQuestionCard(): void;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerPersonQuestionCard = registerPersonQuestionCard;
|
|
4
|
+
/*
|
|
5
|
+
* The confirmation card the assistant offers in a conversation about someone (ADR-0200 decision 3), and the closing of
|
|
6
|
+
* what it handed on when the person answers. The card carries no person and no conversation of its own: both are the
|
|
7
|
+
* conversation it is pressed in.
|
|
8
|
+
*/
|
|
9
|
+
const ai_assistant_1 = require("@things-factory/ai-assistant");
|
|
10
|
+
const person_question_service_js_1 = require("./person-question-service.js");
|
|
11
|
+
function registerPersonQuestionCard() {
|
|
12
|
+
(0, ai_assistant_1.registerEntryTarget)({
|
|
13
|
+
key: ai_assistant_1.PERSON_QUESTION_KEY,
|
|
14
|
+
labelKey: 'label.person-question',
|
|
15
|
+
/* In front only in a conversation about someone; elsewhere there is nobody to hand it to. */
|
|
16
|
+
axes: [ai_assistant_1.PERSON_AXIS],
|
|
17
|
+
description: 'a question handed to the person this conversation is about, as their to-do - for what only they can say: whether something can be done, a deadline, an agreement, their opinion. One row per question. Fill the question as asked; answered with the facts their records already gave, each with where it comes from; decide with what is theirs to say; draft with an answer drawn only from their records. Never invent a fact the records do not show.',
|
|
18
|
+
mutation: 'mutation ($question: PersonQuestionInput!) { askPersonToConfirm(question: $question) }',
|
|
19
|
+
variables: (row, card) => {
|
|
20
|
+
const { question, answered, decide, draft } = row.values;
|
|
21
|
+
return { question: { sessionId: card?.sessionId, question, answered, decide, draft } };
|
|
22
|
+
},
|
|
23
|
+
appliedOf: (data, row) => ({ id: String(data?.askPersonToConfirm ?? ''), label: String(row.values.question ?? '').slice(0, 60) }),
|
|
24
|
+
maxRows: 5,
|
|
25
|
+
fields: [
|
|
26
|
+
{ name: 'question', type: 'text', required: true, labelKey: 'field.person-question', description: 'the question, as the person asking said it' },
|
|
27
|
+
{ name: 'answered', type: 'text', labelKey: 'field.person-question-answered', description: 'what their records already answered, each fact with its record and when it was last changed' },
|
|
28
|
+
{ name: 'decide', type: 'text', required: true, labelKey: 'field.person-question-decide', description: 'what only they can say' },
|
|
29
|
+
{ name: 'draft', type: 'text', labelKey: 'field.person-question-draft', description: 'a draft answer drawn only from their records - they edit or replace it' }
|
|
30
|
+
]
|
|
31
|
+
});
|
|
32
|
+
(0, ai_assistant_1.onRoomTurn)(async (turn) => {
|
|
33
|
+
await (0, person_question_service_js_1.closePersonQuestions)(turn);
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=person-question-card.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"person-question-card.js","sourceRoot":"","sources":["../../../server/service/person-question/person-question-card.ts"],"names":[],"mappings":";;AASA,gEA0BC;AAnCD;;;;GAIG;AACH,+DAAgH;AAEhH,6EAAmE;AAEnE,SAAgB,0BAA0B;IACxC,IAAA,kCAAmB,EAAC;QAClB,GAAG,EAAE,kCAAmB;QACxB,QAAQ,EAAE,uBAAuB;QACjC,6FAA6F;QAC7F,IAAI,EAAE,CAAC,0BAAW,CAAC;QACnB,WAAW,EACT,2bAA2b;QAC7b,QAAQ,EAAE,wFAAwF;QAClG,SAAS,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE;YACvB,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,CAAC,MAAa,CAAA;YAC/D,OAAO,EAAE,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,CAAA;QACxF,CAAC;QACD,SAAS,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,IAAI,EAAE,kBAAkB,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,CAAE,GAAG,CAAC,MAAc,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;QAC1I,OAAO,EAAE,CAAC;QACV,MAAM,EAAE;YACN,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,uBAAuB,EAAE,WAAW,EAAE,4CAA4C,EAAE;YAChJ,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,gCAAgC,EAAE,WAAW,EAAE,6FAA6F,EAAE;YAC1L,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,8BAA8B,EAAE,WAAW,EAAE,wBAAwB,EAAE;YACjI,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,6BAA6B,EAAE,WAAW,EAAE,wEAAwE,EAAE;SAChK;KACF,CAAC,CAAA;IAEF,IAAA,yBAAU,EAAC,KAAK,EAAC,IAAI,EAAC,EAAE;QACtB,MAAM,IAAA,iDAAoB,EAAC,IAAI,CAAC,CAAA;IAClC,CAAC,CAAC,CAAA;AACJ,CAAC","sourcesContent":["/*\n * The confirmation card the assistant offers in a conversation about someone (ADR-0200 decision 3), and the closing of\n * what it handed on when the person answers. The card carries no person and no conversation of its own: both are the\n * conversation it is pressed in.\n */\nimport { onRoomTurn, PERSON_AXIS, PERSON_QUESTION_KEY, registerEntryTarget } from '@things-factory/ai-assistant'\n\nimport { closePersonQuestions } from './person-question-service.js'\n\nexport function registerPersonQuestionCard(): void {\n registerEntryTarget({\n key: PERSON_QUESTION_KEY,\n labelKey: 'label.person-question',\n /* In front only in a conversation about someone; elsewhere there is nobody to hand it to. */\n axes: [PERSON_AXIS],\n description:\n 'a question handed to the person this conversation is about, as their to-do - for what only they can say: whether something can be done, a deadline, an agreement, their opinion. One row per question. Fill the question as asked; answered with the facts their records already gave, each with where it comes from; decide with what is theirs to say; draft with an answer drawn only from their records. Never invent a fact the records do not show.',\n mutation: 'mutation ($question: PersonQuestionInput!) { askPersonToConfirm(question: $question) }',\n variables: (row, card) => {\n const { question, answered, decide, draft } = row.values as any\n return { question: { sessionId: card?.sessionId, question, answered, decide, draft } }\n },\n appliedOf: (data, row) => ({ id: String(data?.askPersonToConfirm ?? ''), label: String((row.values as any).question ?? '').slice(0, 60) }),\n maxRows: 5,\n fields: [\n { name: 'question', type: 'text', required: true, labelKey: 'field.person-question', description: 'the question, as the person asking said it' },\n { name: 'answered', type: 'text', labelKey: 'field.person-question-answered', description: 'what their records already answered, each fact with its record and when it was last changed' },\n { name: 'decide', type: 'text', required: true, labelKey: 'field.person-question-decide', description: 'what only they can say' },\n { name: 'draft', type: 'text', labelKey: 'field.person-question-draft', description: 'a draft answer drawn only from their records - they edit or replace it' }\n ]\n })\n\n onRoomTurn(async turn => {\n await closePersonQuestions(turn)\n })\n}\n"]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare class PersonQuestionInput {
|
|
2
|
+
sessionId: string;
|
|
3
|
+
question: string;
|
|
4
|
+
answered?: string;
|
|
5
|
+
decide: string;
|
|
6
|
+
draft?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare class PersonQuestionMutation {
|
|
9
|
+
askPersonToConfirm(question: PersonQuestionInput, context: ResolverContext): Promise<string>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PersonQuestionMutation = exports.PersonQuestionInput = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const type_graphql_1 = require("type-graphql");
|
|
6
|
+
const person_question_service_js_1 = require("./person-question-service.js");
|
|
7
|
+
let PersonQuestionInput = class PersonQuestionInput {
|
|
8
|
+
};
|
|
9
|
+
exports.PersonQuestionInput = PersonQuestionInput;
|
|
10
|
+
tslib_1.__decorate([
|
|
11
|
+
(0, type_graphql_1.Field)({ description: 'The conversation about the person - it says who is asked.' }),
|
|
12
|
+
tslib_1.__metadata("design:type", String)
|
|
13
|
+
], PersonQuestionInput.prototype, "sessionId", void 0);
|
|
14
|
+
tslib_1.__decorate([
|
|
15
|
+
(0, type_graphql_1.Field)({ description: 'The question, as asked.' }),
|
|
16
|
+
tslib_1.__metadata("design:type", String)
|
|
17
|
+
], PersonQuestionInput.prototype, "question", void 0);
|
|
18
|
+
tslib_1.__decorate([
|
|
19
|
+
(0, type_graphql_1.Field)({ nullable: true, description: 'What the records already answered, each with where it comes from.' }),
|
|
20
|
+
tslib_1.__metadata("design:type", String)
|
|
21
|
+
], PersonQuestionInput.prototype, "answered", void 0);
|
|
22
|
+
tslib_1.__decorate([
|
|
23
|
+
(0, type_graphql_1.Field)({ description: 'What is the person\'s to decide.' }),
|
|
24
|
+
tslib_1.__metadata("design:type", String)
|
|
25
|
+
], PersonQuestionInput.prototype, "decide", void 0);
|
|
26
|
+
tslib_1.__decorate([
|
|
27
|
+
(0, type_graphql_1.Field)({ nullable: true, description: 'A draft answer drawn only from their records.' }),
|
|
28
|
+
tslib_1.__metadata("design:type", String)
|
|
29
|
+
], PersonQuestionInput.prototype, "draft", void 0);
|
|
30
|
+
exports.PersonQuestionInput = PersonQuestionInput = tslib_1.__decorate([
|
|
31
|
+
(0, type_graphql_1.InputType)({ description: 'A question about a person that only they can answer, handed to them from a conversation about them (ADR-0200).' })
|
|
32
|
+
], PersonQuestionInput);
|
|
33
|
+
let PersonQuestionMutation = class PersonQuestionMutation {
|
|
34
|
+
async askPersonToConfirm(question, context) {
|
|
35
|
+
return await (0, person_question_service_js_1.askPerson)(question, context);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
exports.PersonQuestionMutation = PersonQuestionMutation;
|
|
39
|
+
tslib_1.__decorate([
|
|
40
|
+
(0, type_graphql_1.Directive)('@transaction'),
|
|
41
|
+
(0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "mutation", domainOwnerGranted: true)'),
|
|
42
|
+
(0, type_graphql_1.Mutation)(() => String, {
|
|
43
|
+
description: 'Hand a question to the person a conversation is about, as their to-do. Only someone seated in that conversation may; the person is the one the conversation is about. Their answer in the conversation ends it. Answers the to-do id.'
|
|
44
|
+
}),
|
|
45
|
+
tslib_1.__param(0, (0, type_graphql_1.Arg)('question', () => PersonQuestionInput)),
|
|
46
|
+
tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
|
|
47
|
+
tslib_1.__metadata("design:type", Function),
|
|
48
|
+
tslib_1.__metadata("design:paramtypes", [PersonQuestionInput, Object]),
|
|
49
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
50
|
+
], PersonQuestionMutation.prototype, "askPersonToConfirm", null);
|
|
51
|
+
exports.PersonQuestionMutation = PersonQuestionMutation = tslib_1.__decorate([
|
|
52
|
+
(0, type_graphql_1.Resolver)()
|
|
53
|
+
], PersonQuestionMutation);
|
|
54
|
+
//# sourceMappingURL=person-question-resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"person-question-resolver.js","sourceRoot":"","sources":["../../../server/service/person-question/person-question-resolver.ts"],"names":[],"mappings":";;;;AAAA,+CAAwF;AAExF,6EAAwD;AAGjD,IAAM,mBAAmB,GAAzB,MAAM,mBAAmB;CAe/B,CAAA;AAfY,kDAAmB;AAE9B;IADC,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,2DAA2D,EAAE,CAAC;;sDACnE;AAGjB;IADC,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,yBAAyB,EAAE,CAAC;;qDAClC;AAGhB;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,mEAAmE,EAAE,CAAC;;qDAC3F;AAGjB;IADC,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,kCAAkC,EAAE,CAAC;;mDAC7C;AAGd;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,+CAA+C,EAAE,CAAC;;kDAC1E;8BAdH,mBAAmB;IAD/B,IAAA,wBAAS,EAAC,EAAE,WAAW,EAAE,gHAAgH,EAAE,CAAC;GAChI,mBAAmB,CAe/B;AAGM,IAAM,sBAAsB,GAA5B,MAAM,sBAAsB;IAO3B,AAAN,KAAK,CAAC,kBAAkB,CAA6C,QAA6B,EAAS,OAAwB;QACjI,OAAO,MAAM,IAAA,sCAAS,EAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;IAC3C,CAAC;CACF,CAAA;AAVY,wDAAsB;AAO3B;IANL,IAAA,wBAAS,EAAC,cAAc,CAAC;IACzB,IAAA,wBAAS,EAAC,uFAAuF,CAAC;IAClG,IAAA,uBAAQ,EAAC,GAAG,EAAE,CAAC,MAAM,EAAE;QACtB,WAAW,EACT,uOAAuO;KAC1O,CAAC;IACwB,mBAAA,IAAA,kBAAG,EAAC,UAAU,EAAE,GAAG,EAAE,CAAC,mBAAmB,CAAC,CAAA;IAAiC,mBAAA,IAAA,kBAAG,GAAE,CAAA;;6CAA3B,mBAAmB;;gEAEjG;iCATU,sBAAsB;IADlC,IAAA,uBAAQ,GAAE;GACE,sBAAsB,CAUlC","sourcesContent":["import { Arg, Ctx, Directive, Field, InputType, Mutation, Resolver } from 'type-graphql'\n\nimport { askPerson } from './person-question-service.js'\n\n@InputType({ description: 'A question about a person that only they can answer, handed to them from a conversation about them (ADR-0200).' })\nexport class PersonQuestionInput {\n @Field({ description: 'The conversation about the person - it says who is asked.' })\n sessionId: string\n\n @Field({ description: 'The question, as asked.' })\n question: string\n\n @Field({ nullable: true, description: 'What the records already answered, each with where it comes from.' })\n answered?: string\n\n @Field({ description: 'What is the person\\'s to decide.' })\n decide: string\n\n @Field({ nullable: true, description: 'A draft answer drawn only from their records.' })\n draft?: string\n}\n\n@Resolver()\nexport class PersonQuestionMutation {\n @Directive('@transaction')\n @Directive('@privilege(category: \"ai-assistant\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(() => String, {\n description:\n 'Hand a question to the person a conversation is about, as their to-do. Only someone seated in that conversation may; the person is the one the conversation is about. Their answer in the conversation ends it. Answers the to-do id.'\n })\n async askPersonToConfirm(@Arg('question', () => PersonQuestionInput) question: PersonQuestionInput, @Ctx() context: ResolverContext): Promise<string> {\n return await askPerson(question, context)\n }\n}\n"]}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type RoomTurn } from '@things-factory/ai-assistant';
|
|
2
|
+
export interface PersonQuestion {
|
|
3
|
+
sessionId: string;
|
|
4
|
+
question: string;
|
|
5
|
+
answered?: string | null;
|
|
6
|
+
decide: string;
|
|
7
|
+
draft?: string | null;
|
|
8
|
+
}
|
|
9
|
+
/** The longest a part of the question may be - a to-do says one question, not a report. */
|
|
10
|
+
export declare const PERSON_QUESTION_LIMIT = 2000;
|
|
11
|
+
/** Hands the question to the person the conversation is about; answers the to-do's id. */
|
|
12
|
+
export declare function askPerson(question: PersonQuestion, context: ResolverContext): Promise<string>;
|
|
13
|
+
/**
|
|
14
|
+
* The person spoke to the people of a conversation - every question handed to them from it is answered by what they
|
|
15
|
+
* said. Only their own open to-dos from this conversation close.
|
|
16
|
+
*/
|
|
17
|
+
export declare function closePersonQuestions(turn: RoomTurn): Promise<number>;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PERSON_QUESTION_LIMIT = void 0;
|
|
4
|
+
exports.askPerson = askPerson;
|
|
5
|
+
exports.closePersonQuestions = closePersonQuestions;
|
|
6
|
+
/*
|
|
7
|
+
* A question handed to the person a conversation is about, and closed by their answer there (ADR-0200 decision 3).
|
|
8
|
+
*
|
|
9
|
+
* The conversation decides everything: who is asked is the person it is about (its anchor, never a value the model
|
|
10
|
+
* wrote), and who may ask is someone seated in it. Their answer is the next thing they say to the people of that
|
|
11
|
+
* conversation - what they ask the assistant on the way (polish my draft) is not it.
|
|
12
|
+
*/
|
|
13
|
+
const typeorm_1 = require("typeorm");
|
|
14
|
+
const ai_assistant_1 = require("@things-factory/ai-assistant");
|
|
15
|
+
const auth_base_1 = require("@things-factory/auth-base");
|
|
16
|
+
const complete_js_1 = require("../../controllers/activity-instance/complete.js");
|
|
17
|
+
const hand_out_js_1 = require("../../controllers/activity-instance/hand-out.js");
|
|
18
|
+
const activity_js_1 = require("../activity/activity.js");
|
|
19
|
+
const activity_thread_js_1 = require("../activity-thread/activity-thread.js");
|
|
20
|
+
const person_question_activity_js_1 = require("./person-question-activity.js");
|
|
21
|
+
/** The longest a part of the question may be - a to-do says one question, not a report. */
|
|
22
|
+
exports.PERSON_QUESTION_LIMIT = 2000;
|
|
23
|
+
const OPEN = [activity_thread_js_1.ActivityThreadStatus.Unassigned, activity_thread_js_1.ActivityThreadStatus.Assigned, activity_thread_js_1.ActivityThreadStatus.Started];
|
|
24
|
+
/** A refusal in the asker's words. */
|
|
25
|
+
function refused(context, code, fallback) {
|
|
26
|
+
return new Error(context.t ? context.t(`error.${code}`, { defaultValue: fallback }) : fallback);
|
|
27
|
+
}
|
|
28
|
+
const text = (value) => (typeof value === 'string' ? value.trim() : '');
|
|
29
|
+
/** Hands the question to the person the conversation is about; answers the to-do's id. */
|
|
30
|
+
async function askPerson(question, context) {
|
|
31
|
+
const { domain, user, tx } = context.state;
|
|
32
|
+
const said = { question: text(question.question), answered: text(question.answered), decide: text(question.decide), draft: text(question.draft) };
|
|
33
|
+
if (!said.question || !said.decide)
|
|
34
|
+
throw refused(context, 'person-question-incomplete', 'say the question and what the person must decide.');
|
|
35
|
+
for (const [field, value] of Object.entries(said)) {
|
|
36
|
+
if (value.length > exports.PERSON_QUESTION_LIMIT)
|
|
37
|
+
throw refused(context, 'person-question-too-long', `the ${field} is longer than ${exports.PERSON_QUESTION_LIMIT} characters.`);
|
|
38
|
+
}
|
|
39
|
+
const session = await tx.getRepository(ai_assistant_1.ChatSession).findOne({ where: { id: question.sessionId, domain: { id: domain.id } } });
|
|
40
|
+
const personId = (0, ai_assistant_1.personOfAnchor)(session?.anchorType, session?.anchorId);
|
|
41
|
+
if (!session || !personId || personId === user.id) {
|
|
42
|
+
throw refused(context, 'person-question-not-about-someone', 'a question is handed on only from a conversation about someone else.');
|
|
43
|
+
}
|
|
44
|
+
const seat = await tx.getRepository(ai_assistant_1.ChatSessionParticipant).findOne({ where: { domain: { id: domain.id }, session: { id: session.id }, user: { id: user.id } } });
|
|
45
|
+
if (!seat || !['owner', 'member'].includes(seat.role))
|
|
46
|
+
throw refused(context, 'person-question-not-seated', 'only someone in this conversation can hand its question on.');
|
|
47
|
+
const activity = await tx.getRepository(activity_js_1.Activity).findOne({ where: { domain: { id: domain.id }, name: person_question_activity_js_1.PERSON_QUESTION_ACTIVITY } });
|
|
48
|
+
if (!activity)
|
|
49
|
+
throw refused(context, 'person-question-not-installed', 'confirmation requests are not installed on this site.');
|
|
50
|
+
const assignee = await tx.getRepository(auth_base_1.User).findOne({ where: { id: personId } });
|
|
51
|
+
if (!assignee)
|
|
52
|
+
throw refused(context, 'person-question-not-about-someone', 'a question is handed on only from a conversation about someone else.');
|
|
53
|
+
const instance = await (0, hand_out_js_1.handOut)({
|
|
54
|
+
activityId: activity.id,
|
|
55
|
+
name: said.question.length > 80 ? `${said.question.slice(0, 80)}…` : said.question,
|
|
56
|
+
description: said.decide,
|
|
57
|
+
input: { sessionId: session.id, asker: user.name || user.email, askerId: user.id, ...said },
|
|
58
|
+
assignee
|
|
59
|
+
}, context);
|
|
60
|
+
return instance.id;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* The person spoke to the people of a conversation - every question handed to them from it is answered by what they
|
|
64
|
+
* said. Only their own open to-dos from this conversation close.
|
|
65
|
+
*/
|
|
66
|
+
async function closePersonQuestions(turn) {
|
|
67
|
+
const { domain, user, session, message, tx } = turn;
|
|
68
|
+
if (!tx || (0, ai_assistant_1.personOfAnchor)(session.anchorType, session.anchorId) !== user.id)
|
|
69
|
+
return 0;
|
|
70
|
+
const threads = await tx.getRepository(activity_thread_js_1.ActivityThread).find({
|
|
71
|
+
where: { domain: { id: domain.id }, assignee: { id: user.id }, state: (0, typeorm_1.In)(OPEN) },
|
|
72
|
+
relations: ['activityInstance', 'activityInstance.activity']
|
|
73
|
+
});
|
|
74
|
+
const instances = new Map();
|
|
75
|
+
for (const thread of threads) {
|
|
76
|
+
const instance = thread.activityInstance;
|
|
77
|
+
if (instance?.activity?.name === person_question_activity_js_1.PERSON_QUESTION_ACTIVITY && instance.input?.sessionId === session.id)
|
|
78
|
+
instances.set(instance.id, instance);
|
|
79
|
+
}
|
|
80
|
+
const context = { state: { domain, user, tx }, t: turn.t ?? ((key, options) => String(options?.defaultValue ?? key)) };
|
|
81
|
+
for (const instance of instances.values()) {
|
|
82
|
+
await (0, complete_js_1.complete)({ id: instance.id, output: { answer: (0, ai_assistant_1.withoutMentionIds)(message.content).slice(0, exports.PERSON_QUESTION_LIMIT) } }, context);
|
|
83
|
+
}
|
|
84
|
+
return instances.size;
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=person-question-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"person-question-service.js","sourceRoot":"","sources":["../../../server/service/person-question/person-question-service.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * A question handed to the person a conversation is about, and closed by their answer there (ADR-0200 decision 3).\n *\n * The conversation decides everything: who is asked is the person it is about (its anchor, never a value the model\n * wrote), and who may ask is someone seated in it. Their answer is the next thing they say to the people of that\n * conversation - what they ask the assistant on the way (polish my draft) is not it.\n */\nimport { In } from 'typeorm'\n\nimport { ChatSession, ChatSessionParticipant, personOfAnchor, withoutMentionIds, type RoomTurn } from '@things-factory/ai-assistant'\nimport { User } from '@things-factory/auth-base'\n\nimport { complete } from '../../controllers/activity-instance/complete.js'\nimport { handOut } from '../../controllers/activity-instance/hand-out.js'\nimport { Activity } from '../activity/activity.js'\nimport { ActivityInstance } from '../activity-instance/activity-instance.js'\nimport { ActivityThread, ActivityThreadStatus } from '../activity-thread/activity-thread.js'\nimport { PERSON_QUESTION_ACTIVITY } from './person-question-activity.js'\n\nexport interface PersonQuestion {\n sessionId: string\n question: string\n answered?: string | null\n decide: string\n draft?: string | null\n}\n\n/** The longest a part of the question may be - a to-do says one question, not a report. */\nexport const PERSON_QUESTION_LIMIT = 2000\n\nconst OPEN = [ActivityThreadStatus.Unassigned, ActivityThreadStatus.Assigned, ActivityThreadStatus.Started]\n\n/** A refusal in the asker's words. */\nfunction refused(context: ResolverContext, code: string, fallback: string): Error {\n return new Error(context.t ? context.t(`error.${code}`, { defaultValue: fallback }) : fallback)\n}\n\nconst text = (value: unknown) => (typeof value === 'string' ? value.trim() : '')\n\n/** Hands the question to the person the conversation is about; answers the to-do's id. */\nexport async function askPerson(question: PersonQuestion, context: ResolverContext): Promise<string> {\n const { domain, user, tx } = context.state\n const said = { question: text(question.question), answered: text(question.answered), decide: text(question.decide), draft: text(question.draft) }\n if (!said.question || !said.decide) throw refused(context, 'person-question-incomplete', 'say the question and what the person must decide.')\n for (const [field, value] of Object.entries(said)) {\n if (value.length > PERSON_QUESTION_LIMIT) throw refused(context, 'person-question-too-long', `the ${field} is longer than ${PERSON_QUESTION_LIMIT} characters.`)\n }\n\n const session: any = await tx.getRepository(ChatSession).findOne({ where: { id: question.sessionId, domain: { id: domain.id } } as any })\n const personId = personOfAnchor(session?.anchorType, session?.anchorId)\n if (!session || !personId || personId === user.id) {\n throw refused(context, 'person-question-not-about-someone', 'a question is handed on only from a conversation about someone else.')\n }\n\n const seat: any = await tx.getRepository(ChatSessionParticipant).findOne({ where: { domain: { id: domain.id }, session: { id: session.id }, user: { id: user.id } } as any })\n if (!seat || !['owner', 'member'].includes(seat.role)) throw refused(context, 'person-question-not-seated', 'only someone in this conversation can hand its question on.')\n\n const activity = await tx.getRepository(Activity).findOne({ where: { domain: { id: domain.id }, name: PERSON_QUESTION_ACTIVITY } as any })\n if (!activity) throw refused(context, 'person-question-not-installed', 'confirmation requests are not installed on this site.')\n\n const assignee = await tx.getRepository(User).findOne({ where: { id: personId } })\n if (!assignee) throw refused(context, 'person-question-not-about-someone', 'a question is handed on only from a conversation about someone else.')\n\n const instance = await handOut(\n {\n activityId: activity.id,\n name: said.question.length > 80 ? `${said.question.slice(0, 80)}…` : said.question,\n description: said.decide,\n input: { sessionId: session.id, asker: user.name || user.email, askerId: user.id, ...said },\n assignee\n },\n context\n )\n return instance.id\n}\n\n/**\n * The person spoke to the people of a conversation - every question handed to them from it is answered by what they\n * said. Only their own open to-dos from this conversation close.\n */\nexport async function closePersonQuestions(turn: RoomTurn): Promise<number> {\n const { domain, user, session, message, tx } = turn\n if (!tx || personOfAnchor(session.anchorType, session.anchorId) !== user.id) return 0\n\n const threads = await tx.getRepository(ActivityThread).find({\n where: { domain: { id: domain.id }, assignee: { id: user.id }, state: In(OPEN) } as any,\n relations: ['activityInstance', 'activityInstance.activity']\n })\n const instances = new Map<string, ActivityInstance>()\n for (const thread of threads) {\n const instance = thread.activityInstance\n if (instance?.activity?.name === PERSON_QUESTION_ACTIVITY && (instance.input as any)?.sessionId === session.id) instances.set(instance.id, instance)\n }\n\n const context = { state: { domain, user, tx }, t: turn.t ?? ((key: string, options?: any) => String(options?.defaultValue ?? key)) } as any\n for (const instance of instances.values()) {\n await complete({ id: instance.id, output: { answer: withoutMentionIds(message.content).slice(0, PERSON_QUESTION_LIMIT) } }, context)\n }\n return instances.size\n}\n"]}
|