@noodleseed/one 0.177.0 → 0.178.0
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/commands/solutions-activity.d.ts.map +1 -1
- package/dist/commands/solutions-activity.js +9 -35
- package/dist/commands/solutions-activity.js.map +1 -1
- package/dist/commands/solutions-conversations.d.ts.map +1 -1
- package/dist/commands/solutions-conversations.js +60 -6
- package/dist/commands/solutions-conversations.js.map +1 -1
- package/dist/commands/solutions-history.d.ts +9 -0
- package/dist/commands/solutions-history.d.ts.map +1 -0
- package/dist/commands/solutions-history.js +166 -0
- package/dist/commands/solutions-history.js.map +1 -0
- package/dist/commands/solutions-ops.d.ts.map +1 -1
- package/dist/commands/solutions-ops.js +3 -0
- package/dist/commands/solutions-ops.js.map +1 -1
- package/dist/commands/whatsapp-ops.d.ts.map +1 -1
- package/dist/commands/whatsapp-ops.js +0 -7
- package/dist/commands/whatsapp-ops.js.map +1 -1
- package/node_modules/@noodle-borg/agent-kit/dist/generated/example-files.js +1 -1
- package/node_modules/@noodle-borg/agent-kit/dist/skill-embedded-assistant-ref.js +14 -0
- package/node_modules/@noodle-borg/agent-kit/package.json +1 -1
- package/node_modules/@noodle-borg/assistant-gateway/dist/public-surface.d.ts +6 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/public-surface.js +5 -2
- package/node_modules/@noodle-borg/assistant-gateway/package.json +1 -1
- package/node_modules/@noodle-borg/authoring/dist/assistant.d.ts +18 -0
- package/node_modules/@noodle-borg/authoring/dist/assistant.js +14 -1
- package/node_modules/@noodle-borg/cli-catalog/dist/catalog-data-channels.js +0 -3
- package/node_modules/@noodle-borg/cli-catalog/dist/catalog-data-solution-conversations.js +50 -5
- package/node_modules/@noodle-borg/cli-catalog/dist/catalog-data-solution-history.js +82 -0
- package/node_modules/@noodle-borg/cli-catalog/dist/catalog-data-solutions.js +3 -42
- package/node_modules/@noodle-borg/compiler/dist/manifest/schema.d.ts +8 -0
- package/node_modules/@noodle-borg/compiler/dist/manifest/schema.js +7 -0
- package/node_modules/@noodle-borg/service/dist/application-activity.js +76 -21
- package/node_modules/@noodle-borg/service/dist/application-history-settings.js +108 -0
- package/node_modules/@noodle-borg/service/dist/assistant-composition.js +11 -2
- package/node_modules/@noodle-borg/service/dist/channels/composition.js +10 -1
- package/node_modules/@noodle-borg/service/dist/channels/provider.js +13 -13
- package/node_modules/@noodle-borg/service/dist/channels/runtime.js +31 -5
- package/node_modules/@noodle-borg/service/dist/conversation-history/capture.js +63 -7
- package/node_modules/@noodle-borg/service/dist/conversation-history/contracts.js +13 -0
- package/node_modules/@noodle-borg/service/dist/conversation-history/customer.js +118 -0
- package/node_modules/@noodle-borg/service/dist/conversation-history/operator.js +133 -24
- package/node_modules/@noodle-borg/service/dist/routes/assistant-conversations.js +52 -0
- package/node_modules/@noodle-borg/service/dist/routes/assistant-dispatch.js +6 -0
- package/node_modules/@noodle-borg/service/dist/routes/assistant-elevation.js +3 -0
- package/node_modules/@noodle-borg/service/dist/routes/assistant-interaction-stream.js +2 -1
- package/node_modules/@noodle-borg/service/dist/routes/assistant-interactions.js +6 -11
- package/node_modules/@noodle-borg/service/dist/routes/assistant-public-session.js +6 -0
- package/node_modules/@noodle-borg/service/dist/routes/assistant-route-http.js +16 -0
- package/node_modules/@noodle-borg/service/dist/routes/assistant.js +5 -17
- package/node_modules/@noodle-borg/service/dist/routes/business-information-activity.js +6 -5
- package/node_modules/@noodle-borg/service/dist/routes/business-information-conversations.js +36 -11
- package/node_modules/@noodle-borg/service/dist/routes/business-information-dispatch.js +4 -2
- package/node_modules/@noodle-borg/service/dist/routes/business-information-paths.js +4 -2
- package/node_modules/@noodle-borg/service/dist/routes/business-information.js +4 -0
- package/node_modules/@noodle-borg/service/dist/routes/whatsapp.js +0 -10
- package/node_modules/@noodle-borg/service/dist/service-history-projections.js +32 -4
- package/node_modules/@noodle-borg/service/dist/service-resource-cleanup.js +32 -0
- package/node_modules/@noodle-borg/service/dist/service.js +4 -3
- package/node_modules/@noodle-borg/service/package.json +1 -1
- package/node_modules/@noodle-borg/wire-contracts/dist/application-activity.d.ts +0 -28
- package/node_modules/@noodle-borg/wire-contracts/dist/application-activity.js +0 -18
- package/node_modules/@noodle-borg/wire-contracts/dist/application-history-settings.d.ts +190 -0
- package/node_modules/@noodle-borg/wire-contracts/dist/application-history-settings.js +92 -0
- package/node_modules/@noodle-borg/wire-contracts/dist/assistant.d.ts +3 -0
- package/node_modules/@noodle-borg/wire-contracts/dist/assistant.js +6 -0
- package/node_modules/@noodle-borg/wire-contracts/dist/conversation-history.d.ts +176 -0
- package/node_modules/@noodle-borg/wire-contracts/dist/conversation-history.js +74 -2
- package/node_modules/@noodle-borg/wire-contracts/dist/index.d.ts +1 -0
- package/node_modules/@noodle-borg/wire-contracts/dist/index.js +1 -0
- package/node_modules/@noodleseed/assistant/README.md +19 -0
- package/node_modules/@noodleseed/assistant/package.json +1 -1
- package/package.json +2 -2
|
@@ -5,7 +5,7 @@ export function publicMessaging(input) {
|
|
|
5
5
|
throw new Error('publicMessaging requires an explicit capability allowlist');
|
|
6
6
|
}
|
|
7
7
|
for (const key of Object.keys(input)) {
|
|
8
|
-
if (!['channel', 'capabilities', 'instructions'].includes(key)) {
|
|
8
|
+
if (!['channel', 'capabilities', 'instructions', 'history'].includes(key)) {
|
|
9
9
|
throw new Error(`publicMessaging does not accept ${key}`);
|
|
10
10
|
}
|
|
11
11
|
}
|
|
@@ -17,6 +17,7 @@ export function publicMessaging(input) {
|
|
|
17
17
|
channel: input.channel,
|
|
18
18
|
capabilities: normalizeCapabilities(input.capabilities),
|
|
19
19
|
...(input.instructions === undefined ? {} : { instructions: input.instructions }),
|
|
20
|
+
...historyOptOut(input.history),
|
|
20
21
|
};
|
|
21
22
|
}
|
|
22
23
|
export function authenticatedWebsite(input) {
|
|
@@ -27,6 +28,7 @@ export function authenticatedWebsite(input) {
|
|
|
27
28
|
...(input.sessionClaims ? { sessionClaims: structuredClone(input.sessionClaims) } : {}),
|
|
28
29
|
...(input.capabilities ? { capabilities: [...input.capabilities] } : {}),
|
|
29
30
|
...(input.webmcp === undefined ? {} : { webmcp: { ...input.webmcp } }),
|
|
31
|
+
...historyOptOut(input.history),
|
|
30
32
|
};
|
|
31
33
|
}
|
|
32
34
|
export function publicWebsite(input) {
|
|
@@ -37,8 +39,18 @@ export function publicWebsite(input) {
|
|
|
37
39
|
...(input.instructions === undefined ? {} : { instructions: input.instructions }),
|
|
38
40
|
...(input.webmcp === undefined ? {} : { webmcp: { ...input.webmcp } }),
|
|
39
41
|
...(input.continuity === undefined ? {} : { continuity: { ...input.continuity } }),
|
|
42
|
+
...historyOptOut(input.history),
|
|
40
43
|
};
|
|
41
44
|
}
|
|
45
|
+
/** Refuses anything but the literal `false`, because days are operator state (ADR 0212). */
|
|
46
|
+
function historyOptOut(history) {
|
|
47
|
+
if (history === undefined)
|
|
48
|
+
return {};
|
|
49
|
+
if (history !== false) {
|
|
50
|
+
throw new Error('history accepts only false (this surface never keeps chats); how many days a recorded surface keeps is the business setting in noodle solutions history settings');
|
|
51
|
+
}
|
|
52
|
+
return { history: false };
|
|
53
|
+
}
|
|
42
54
|
function serializeOrigins(origins, path) {
|
|
43
55
|
return origins.map((origin, index) => typeof origin === 'string' ? origin : serializeVariableRef(origin, `${path}.${index}`));
|
|
44
56
|
}
|
|
@@ -88,6 +100,7 @@ export function embeddedAssistant(input) {
|
|
|
88
100
|
...(surface.mode !== 'authenticated' && surface.continuity !== undefined
|
|
89
101
|
? { continuity: { ...surface.continuity } }
|
|
90
102
|
: {}),
|
|
103
|
+
...(surface.history === false ? { history: false } : {}),
|
|
91
104
|
}),
|
|
92
105
|
allowedOrigins: surfaces.flatMap((surface) => surface.kind === 'messaging' ? [] : [...surface.origins]),
|
|
93
106
|
...structuredClone(ui),
|
|
@@ -114,9 +114,6 @@ export const CHANNELS_COMMAND = {
|
|
|
114
114
|
...mutation,
|
|
115
115
|
]),
|
|
116
116
|
]),
|
|
117
|
-
group('conversation', 'Erase conversation content.', [
|
|
118
|
-
leaf('forget', 'Erase participant context and message content while preserving safeguards.', [participant, ...mutation]),
|
|
119
|
-
]),
|
|
120
117
|
]),
|
|
121
118
|
],
|
|
122
119
|
};
|
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
/** Staff conversation history operations (ADR 0241); imported by the solutions family. Data only. */
|
|
2
2
|
import { INSTALLATION_ARGUMENT, OPTIONAL_FLAG, SOLUTION_COMMON_FLAGS, } from './catalog-data-solution-flags.js';
|
|
3
|
+
const STATUS_CHOICES = ['new', 'needs-attention', 'reviewed'];
|
|
4
|
+
const CONVERSATION_FLAG = {
|
|
5
|
+
...OPTIONAL_FLAG,
|
|
6
|
+
required: true,
|
|
7
|
+
name: 'conversation',
|
|
8
|
+
type: 'string',
|
|
9
|
+
value: '<id>',
|
|
10
|
+
summary: 'Conversation id from conversations list.',
|
|
11
|
+
};
|
|
3
12
|
const paging = (maximum) => [
|
|
4
13
|
{
|
|
5
14
|
...OPTIONAL_FLAG,
|
|
@@ -24,6 +33,14 @@ const paging = (maximum) => [
|
|
|
24
33
|
summary: 'Only conversations from this channel.',
|
|
25
34
|
constraints: { choices: ['website', 'whatsapp'] },
|
|
26
35
|
},
|
|
36
|
+
{
|
|
37
|
+
...OPTIONAL_FLAG,
|
|
38
|
+
name: 'status',
|
|
39
|
+
type: 'string',
|
|
40
|
+
value: '<status>',
|
|
41
|
+
summary: 'Only conversations with this review status.',
|
|
42
|
+
constraints: { choices: STATUS_CHOICES },
|
|
43
|
+
},
|
|
27
44
|
];
|
|
28
45
|
const SELECTORS = ['conversation', 'customer', 'participant'];
|
|
29
46
|
const SELECTOR_SUMMARIES = {
|
|
@@ -33,7 +50,7 @@ const SELECTOR_SUMMARIES = {
|
|
|
33
50
|
};
|
|
34
51
|
export const SOLUTION_CONVERSATIONS = {
|
|
35
52
|
name: 'conversations',
|
|
36
|
-
summary: 'Read, export and forget customer conversation history.',
|
|
53
|
+
summary: 'Read, review, note, export and forget customer conversation history.',
|
|
37
54
|
arguments: [],
|
|
38
55
|
flags: [],
|
|
39
56
|
subcommands: [
|
|
@@ -46,17 +63,45 @@ export const SOLUTION_CONVERSATIONS = {
|
|
|
46
63
|
},
|
|
47
64
|
{
|
|
48
65
|
name: 'show',
|
|
49
|
-
summary: 'Show one conversation with its messages; the read is audited.',
|
|
66
|
+
summary: 'Show one conversation with its messages and private notes; the read is audited.',
|
|
67
|
+
arguments: [INSTALLATION_ARGUMENT],
|
|
68
|
+
flags: [...SOLUTION_COMMON_FLAGS, CONVERSATION_FLAG],
|
|
69
|
+
jsonOutput: { mode: 'single' },
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
name: 'review',
|
|
73
|
+
summary: 'Set the review status of one conversation (Operator or above); audited.',
|
|
74
|
+
arguments: [INSTALLATION_ARGUMENT],
|
|
75
|
+
flags: [
|
|
76
|
+
...SOLUTION_COMMON_FLAGS,
|
|
77
|
+
CONVERSATION_FLAG,
|
|
78
|
+
{
|
|
79
|
+
...OPTIONAL_FLAG,
|
|
80
|
+
required: true,
|
|
81
|
+
name: 'status',
|
|
82
|
+
type: 'string',
|
|
83
|
+
value: '<status>',
|
|
84
|
+
summary: 'New review status.',
|
|
85
|
+
constraints: { choices: STATUS_CHOICES },
|
|
86
|
+
},
|
|
87
|
+
],
|
|
88
|
+
jsonOutput: { mode: 'single' },
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
name: 'note',
|
|
92
|
+
summary: 'Add a private staff note the customer never sees (Operator or above); audited.',
|
|
50
93
|
arguments: [INSTALLATION_ARGUMENT],
|
|
51
94
|
flags: [
|
|
52
95
|
...SOLUTION_COMMON_FLAGS,
|
|
96
|
+
CONVERSATION_FLAG,
|
|
53
97
|
{
|
|
54
98
|
...OPTIONAL_FLAG,
|
|
55
99
|
required: true,
|
|
56
|
-
name: '
|
|
100
|
+
name: 'text',
|
|
57
101
|
type: 'string',
|
|
58
|
-
value: '<
|
|
59
|
-
summary: '
|
|
102
|
+
value: '<text>',
|
|
103
|
+
summary: 'Note text, up to 2000 characters.',
|
|
104
|
+
constraints: { maxLength: 2000 },
|
|
60
105
|
},
|
|
61
106
|
],
|
|
62
107
|
jsonOutput: { mode: 'single' },
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/** The installation history setting (ADR 0241 decision 6); imported by the solutions family. Data only. */
|
|
2
|
+
import { INSTALLATION_ARGUMENT, OPTIONAL_FLAG, SOLUTION_COMMON_FLAGS, } from './catalog-data-solution-flags.js';
|
|
3
|
+
const days = (name, summary, conflictsWith = []) => ({
|
|
4
|
+
...OPTIONAL_FLAG,
|
|
5
|
+
name,
|
|
6
|
+
type: 'integer',
|
|
7
|
+
value: '<days>',
|
|
8
|
+
summary,
|
|
9
|
+
constraints: { minimum: 1, maximum: 365 },
|
|
10
|
+
conflictsWith,
|
|
11
|
+
});
|
|
12
|
+
const channel = (name, summary) => ({
|
|
13
|
+
...OPTIONAL_FLAG,
|
|
14
|
+
name,
|
|
15
|
+
type: 'string',
|
|
16
|
+
value: '<on|off>',
|
|
17
|
+
summary,
|
|
18
|
+
constraints: { choices: ['on', 'off'] },
|
|
19
|
+
});
|
|
20
|
+
export const SOLUTION_HISTORY = {
|
|
21
|
+
name: 'history',
|
|
22
|
+
summary: 'Inspect or change how long Activity and conversations are kept.',
|
|
23
|
+
arguments: [],
|
|
24
|
+
flags: [],
|
|
25
|
+
subcommands: [
|
|
26
|
+
{
|
|
27
|
+
name: 'settings',
|
|
28
|
+
summary: 'Read or change the installation history setting within plan limits.',
|
|
29
|
+
arguments: [],
|
|
30
|
+
flags: [],
|
|
31
|
+
subcommands: [
|
|
32
|
+
{
|
|
33
|
+
name: 'get',
|
|
34
|
+
summary: 'Read Activity and conversation durations, recording switches, edit permission and revision.',
|
|
35
|
+
arguments: [INSTALLATION_ARGUMENT],
|
|
36
|
+
flags: SOLUTION_COMMON_FLAGS,
|
|
37
|
+
jsonOutput: { mode: 'single' },
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: 'set',
|
|
41
|
+
summary: 'Change durations or recording switches with a revision check; shortening or turning off conversations needs --confirm.',
|
|
42
|
+
arguments: [INSTALLATION_ARGUMENT],
|
|
43
|
+
flags: [
|
|
44
|
+
...SOLUTION_COMMON_FLAGS,
|
|
45
|
+
days('activity-days', 'Days to keep Activity, within the plan maximum.'),
|
|
46
|
+
days('conversation-days', 'Days to keep conversations; turns recording on.', [
|
|
47
|
+
'conversations',
|
|
48
|
+
]),
|
|
49
|
+
{
|
|
50
|
+
...OPTIONAL_FLAG,
|
|
51
|
+
name: 'conversations',
|
|
52
|
+
type: 'string',
|
|
53
|
+
value: 'off',
|
|
54
|
+
summary: 'Turn conversation recording off.',
|
|
55
|
+
constraints: { choices: ['off'] },
|
|
56
|
+
conflictsWith: ['conversation-days'],
|
|
57
|
+
},
|
|
58
|
+
channel('website-visitors', 'Record anonymous website visitors.'),
|
|
59
|
+
channel('signed-in-customers', 'Record signed-in customers.'),
|
|
60
|
+
channel('whatsapp', 'Record WhatsApp conversations.'),
|
|
61
|
+
{
|
|
62
|
+
...OPTIONAL_FLAG,
|
|
63
|
+
required: true,
|
|
64
|
+
name: 'expected-revision',
|
|
65
|
+
type: 'string',
|
|
66
|
+
value: '<hash>',
|
|
67
|
+
summary: 'Revision returned by history settings get.',
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
...OPTIONAL_FLAG,
|
|
71
|
+
name: 'confirm',
|
|
72
|
+
type: 'boolean',
|
|
73
|
+
summary: 'Confirm a change that shortens or turns off conversation history; without it the effect is shown and nothing is saved.',
|
|
74
|
+
},
|
|
75
|
+
],
|
|
76
|
+
jsonOutput: { mode: 'single' },
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
},
|
|
80
|
+
],
|
|
81
|
+
};
|
|
82
|
+
//# sourceMappingURL=catalog-data-solution-history.js.map
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { SOLUTION_CONVERSATIONS } from './catalog-data-solution-conversations.js';
|
|
3
3
|
import { SOLUTION_DRAFTS } from './catalog-data-solution-drafts.js';
|
|
4
4
|
import { AUTH_TOKEN, EXPECTED_REVISION, IDEMPOTENCY_KEY, INSTALLATION_ARGUMENT, JSON_FLAG, OPTIONAL_FLAG, PAGING_FLAGS, REQUIRED_ARGUMENT, SERVICE, SOLUTION_COMMON_FLAGS, } from './catalog-data-solution-flags.js';
|
|
5
|
+
import { SOLUTION_HISTORY } from './catalog-data-solution-history.js';
|
|
5
6
|
import { SOLUTION_PAGE } from './catalog-data-solution-page.js';
|
|
6
7
|
import { COLLECTION_ARGUMENT, SOLUTION_RECORDS } from './catalog-data-solution-records.js';
|
|
7
8
|
import { SOLUTION_WORKSPACE } from './catalog-data-solution-workspace.js';
|
|
@@ -173,9 +174,10 @@ export const CATALOG_SOLUTIONS = {
|
|
|
173
174
|
],
|
|
174
175
|
},
|
|
175
176
|
SOLUTION_CONVERSATIONS,
|
|
177
|
+
SOLUTION_HISTORY,
|
|
176
178
|
{
|
|
177
179
|
name: 'activity',
|
|
178
|
-
summary: 'Inspect payload-free operation evidence and
|
|
180
|
+
summary: 'Inspect payload-free operation evidence and preview plan history impact.',
|
|
179
181
|
arguments: [],
|
|
180
182
|
flags: [],
|
|
181
183
|
subcommands: [
|
|
@@ -212,47 +214,6 @@ export const CATALOG_SOLUTIONS = {
|
|
|
212
214
|
flags: SOLUTION_COMMON_FLAGS,
|
|
213
215
|
jsonOutput: { mode: 'single' },
|
|
214
216
|
},
|
|
215
|
-
{
|
|
216
|
-
name: 'settings',
|
|
217
|
-
summary: 'Inspect or change Activity retention within plan limits.',
|
|
218
|
-
arguments: [],
|
|
219
|
-
flags: [],
|
|
220
|
-
subcommands: [
|
|
221
|
-
{
|
|
222
|
-
name: 'get',
|
|
223
|
-
summary: 'Read retention, plan maximum, edit permission and current revision.',
|
|
224
|
-
arguments: [INSTALLATION_ARGUMENT],
|
|
225
|
-
flags: SOLUTION_COMMON_FLAGS,
|
|
226
|
-
jsonOutput: { mode: 'single' },
|
|
227
|
-
},
|
|
228
|
-
{
|
|
229
|
-
name: 'set',
|
|
230
|
-
summary: 'Change retention with an explicit revision check.',
|
|
231
|
-
arguments: [INSTALLATION_ARGUMENT],
|
|
232
|
-
flags: [
|
|
233
|
-
...SOLUTION_COMMON_FLAGS,
|
|
234
|
-
{
|
|
235
|
-
...OPTIONAL_FLAG,
|
|
236
|
-
required: true,
|
|
237
|
-
name: 'retention-days',
|
|
238
|
-
type: 'integer',
|
|
239
|
-
value: '<days>',
|
|
240
|
-
summary: 'Desired retention within the current plan maximum.',
|
|
241
|
-
constraints: { minimum: 1, maximum: 365 },
|
|
242
|
-
},
|
|
243
|
-
{
|
|
244
|
-
...OPTIONAL_FLAG,
|
|
245
|
-
required: true,
|
|
246
|
-
name: 'expected-revision',
|
|
247
|
-
type: 'string',
|
|
248
|
-
value: '<hash>',
|
|
249
|
-
summary: 'Revision returned by activity settings get.',
|
|
250
|
-
},
|
|
251
|
-
],
|
|
252
|
-
jsonOutput: { mode: 'single' },
|
|
253
|
-
},
|
|
254
|
-
],
|
|
255
|
-
},
|
|
256
217
|
],
|
|
257
218
|
},
|
|
258
219
|
{
|
|
@@ -372,6 +372,7 @@ export declare const manifestV1Schema: z.ZodObject<{
|
|
|
372
372
|
name: z.ZodString;
|
|
373
373
|
}, z.core.$strict>>;
|
|
374
374
|
instructions: z.ZodOptional<z.ZodString>;
|
|
375
|
+
history: z.ZodOptional<z.ZodLiteral<false>>;
|
|
375
376
|
}, z.core.$strict>, z.ZodObject<{
|
|
376
377
|
kind: z.ZodOptional<z.ZodLiteral<"website">>;
|
|
377
378
|
mode: z.ZodEnum<{
|
|
@@ -401,6 +402,7 @@ export declare const manifestV1Schema: z.ZodObject<{
|
|
|
401
402
|
windowSeconds: z.ZodOptional<z.ZodNumber>;
|
|
402
403
|
maxRestores: z.ZodOptional<z.ZodNumber>;
|
|
403
404
|
}, z.core.$strict>>;
|
|
405
|
+
history: z.ZodOptional<z.ZodLiteral<false>>;
|
|
404
406
|
}, z.core.$strict>]>>>;
|
|
405
407
|
sessionClaims: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
406
408
|
exposeToModel: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -876,6 +878,7 @@ export declare const manifestV2Schema: z.ZodObject<{
|
|
|
876
878
|
name: z.ZodString;
|
|
877
879
|
}, z.core.$strict>>;
|
|
878
880
|
instructions: z.ZodOptional<z.ZodString>;
|
|
881
|
+
history: z.ZodOptional<z.ZodLiteral<false>>;
|
|
879
882
|
}, z.core.$strict>, z.ZodObject<{
|
|
880
883
|
kind: z.ZodOptional<z.ZodLiteral<"website">>;
|
|
881
884
|
mode: z.ZodEnum<{
|
|
@@ -905,6 +908,7 @@ export declare const manifestV2Schema: z.ZodObject<{
|
|
|
905
908
|
windowSeconds: z.ZodOptional<z.ZodNumber>;
|
|
906
909
|
maxRestores: z.ZodOptional<z.ZodNumber>;
|
|
907
910
|
}, z.core.$strict>>;
|
|
911
|
+
history: z.ZodOptional<z.ZodLiteral<false>>;
|
|
908
912
|
}, z.core.$strict>]>>>;
|
|
909
913
|
sessionClaims: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
910
914
|
exposeToModel: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -1674,6 +1678,7 @@ export declare const manifestSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
1674
1678
|
name: z.ZodString;
|
|
1675
1679
|
}, z.core.$strict>>;
|
|
1676
1680
|
instructions: z.ZodOptional<z.ZodString>;
|
|
1681
|
+
history: z.ZodOptional<z.ZodLiteral<false>>;
|
|
1677
1682
|
}, z.core.$strict>, z.ZodObject<{
|
|
1678
1683
|
kind: z.ZodOptional<z.ZodLiteral<"website">>;
|
|
1679
1684
|
mode: z.ZodEnum<{
|
|
@@ -1703,6 +1708,7 @@ export declare const manifestSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
1703
1708
|
windowSeconds: z.ZodOptional<z.ZodNumber>;
|
|
1704
1709
|
maxRestores: z.ZodOptional<z.ZodNumber>;
|
|
1705
1710
|
}, z.core.$strict>>;
|
|
1711
|
+
history: z.ZodOptional<z.ZodLiteral<false>>;
|
|
1706
1712
|
}, z.core.$strict>]>>>;
|
|
1707
1713
|
sessionClaims: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
1708
1714
|
exposeToModel: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -2177,6 +2183,7 @@ export declare const manifestSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
2177
2183
|
name: z.ZodString;
|
|
2178
2184
|
}, z.core.$strict>>;
|
|
2179
2185
|
instructions: z.ZodOptional<z.ZodString>;
|
|
2186
|
+
history: z.ZodOptional<z.ZodLiteral<false>>;
|
|
2180
2187
|
}, z.core.$strict>, z.ZodObject<{
|
|
2181
2188
|
kind: z.ZodOptional<z.ZodLiteral<"website">>;
|
|
2182
2189
|
mode: z.ZodEnum<{
|
|
@@ -2206,6 +2213,7 @@ export declare const manifestSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
2206
2213
|
windowSeconds: z.ZodOptional<z.ZodNumber>;
|
|
2207
2214
|
maxRestores: z.ZodOptional<z.ZodNumber>;
|
|
2208
2215
|
}, z.core.$strict>>;
|
|
2216
|
+
history: z.ZodOptional<z.ZodLiteral<false>>;
|
|
2209
2217
|
}, z.core.$strict>]>>>;
|
|
2210
2218
|
sessionClaims: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
2211
2219
|
exposeToModel: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -269,6 +269,11 @@ const assistantCapabilitySchema = z
|
|
|
269
269
|
const MAX_SERVER_INSTRUCTIONS = 4000;
|
|
270
270
|
/** Verified session claims an embedding backend may pass at exchange; undeclared keys are dropped. */
|
|
271
271
|
const sessionClaimsSchema = z.record(z.string().regex(/^[A-Za-z][A-Za-z0-9_]{0,63}$/, 'claim keys are identifier-style'), z.object({ exposeToModel: z.boolean().optional() }).strict());
|
|
272
|
+
/**
|
|
273
|
+
* `false` means this surface's chats are never kept as history (ADR 0241 decision 11). Only the literal
|
|
274
|
+
* is accepted: a number of days is operator state (ADR 0212), so a count is refused, never read.
|
|
275
|
+
*/
|
|
276
|
+
const surfaceHistorySchema = z.literal(false).optional();
|
|
272
277
|
const embeddedAssistantSchema = z
|
|
273
278
|
.object({
|
|
274
279
|
model: z.union([
|
|
@@ -297,6 +302,7 @@ const embeddedAssistantSchema = z
|
|
|
297
302
|
mode: z.literal('public'),
|
|
298
303
|
capabilities: z.array(assistantCapabilitySchema),
|
|
299
304
|
instructions: z.string().trim().min(1).max(MAX_SERVER_INSTRUCTIONS).optional(),
|
|
305
|
+
history: surfaceHistorySchema,
|
|
300
306
|
})
|
|
301
307
|
.strict(),
|
|
302
308
|
z
|
|
@@ -324,6 +330,7 @@ const embeddedAssistantSchema = z
|
|
|
324
330
|
windowSeconds: z.number().int().min(0).max(600).optional(),
|
|
325
331
|
maxRestores: z.number().int().min(0).max(10).optional(),
|
|
326
332
|
}),
|
|
333
|
+
history: surfaceHistorySchema,
|
|
327
334
|
})
|
|
328
335
|
.strict(),
|
|
329
336
|
]))
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createHmac } from 'node:crypto';
|
|
2
2
|
import { canonicalJson, sha256Canonical } from '@noodle-borg/compiler';
|
|
3
|
-
import { ApplicationActivityListResponseSchema, ApplicationActivityPreviewResponseSchema,
|
|
3
|
+
import { ApplicationActivityListResponseSchema, ApplicationActivityPreviewResponseSchema, ApplicationHistorySettingsResponseSchema, ApplicationHistorySettingsSaveRequestSchema, ApplicationHistorySettingsSaveResponseSchema, OperationCoordinationListRequestSchema, OperationCoordinationListResponseSchema, OperationCoordinationResolveRequestSchema, OperationCoordinationResolveResponseSchema, } from '@noodle-borg/wire-contracts';
|
|
4
|
+
import { conversationPolicyFromSetting, historySettingsProjection, lazyHistorySetting, newInstallationHistorySetting, planHistorySettingsChange, } from './application-history-settings.js';
|
|
4
5
|
import { resolveApplicationRuntimeTarget } from './application-runtime-target.js';
|
|
5
6
|
import { createOperationCoordinationPort, } from './operation-coordination.js';
|
|
6
7
|
import { createOperationEvidencePort, } from './operation-evidence.js';
|
|
@@ -111,11 +112,13 @@ export class ApplicationActivity {
|
|
|
111
112
|
});
|
|
112
113
|
}
|
|
113
114
|
if (action === 'save-settings') {
|
|
114
|
-
const parsed =
|
|
115
|
+
const parsed = ApplicationHistorySettingsSaveRequestSchema.safeParse(input.body);
|
|
115
116
|
if (!parsed.success)
|
|
116
117
|
throw invalid();
|
|
117
|
-
const
|
|
118
|
-
|
|
118
|
+
const { audit, ...saved } = await this.save(scope, parsed.data, local);
|
|
119
|
+
const data = { ...saved, settings: await this.withApplicationView(scope, saved.settings) };
|
|
120
|
+
const response = ApplicationHistorySettingsSaveResponseSchema.parse({ ok: true, data });
|
|
121
|
+
return audit ? mutation(response, 'config.history.settings_changed', audit) : { response };
|
|
119
122
|
}
|
|
120
123
|
if (action === 'preview')
|
|
121
124
|
return {
|
|
@@ -126,9 +129,9 @@ export class ApplicationActivity {
|
|
|
126
129
|
};
|
|
127
130
|
if (action === 'settings')
|
|
128
131
|
return {
|
|
129
|
-
response:
|
|
132
|
+
response: ApplicationHistorySettingsResponseSchema.parse({
|
|
130
133
|
ok: true,
|
|
131
|
-
data: (await this.settings(scope, input.canEdit, local)).projection,
|
|
134
|
+
data: await this.withApplicationView(scope, (await this.settings(scope, input.canEdit, local)).projection),
|
|
132
135
|
}),
|
|
133
136
|
};
|
|
134
137
|
const limit = parameters.has('limit') ? Number(parameters.get('limit')) : 50;
|
|
@@ -149,7 +152,18 @@ export class ApplicationActivity {
|
|
|
149
152
|
}),
|
|
150
153
|
};
|
|
151
154
|
}
|
|
152
|
-
|
|
155
|
+
/** Adds what the application itself declared; the stored setting and its revision are unchanged. */
|
|
156
|
+
async withApplicationView(scope, projection) {
|
|
157
|
+
const disabled = (await this.options.historyDisabledSurfaces?.(scope)) ?? [];
|
|
158
|
+
if (disabled.length === 0)
|
|
159
|
+
return projection;
|
|
160
|
+
return {
|
|
161
|
+
...projection,
|
|
162
|
+
conversations: { ...projection.conversations, disabledByApplication: [...disabled] },
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
/** The verified plan allowance, or unavailable; never a default invented locally. */
|
|
166
|
+
async allowance(scope) {
|
|
153
167
|
const allowance = await this.options.allowance(scope.org);
|
|
154
168
|
if (!allowance ||
|
|
155
169
|
!validDays(allowance.maximumDays) ||
|
|
@@ -157,36 +171,77 @@ export class ApplicationActivity {
|
|
|
157
171
|
allowance.defaultDays > allowance.maximumDays ||
|
|
158
172
|
!allowance.revision)
|
|
159
173
|
throw new ActivityPolicyError('activity_unavailable');
|
|
174
|
+
return allowance;
|
|
175
|
+
}
|
|
176
|
+
async settings(scope, canEdit, local = runLocal) {
|
|
177
|
+
const allowance = await this.allowance(scope);
|
|
160
178
|
const setting = await local(async () => {
|
|
161
179
|
let value = await this.options.store.readRetention(scope);
|
|
162
180
|
if (!value) {
|
|
163
|
-
await this.options.store.setRetention(scope, allowance
|
|
181
|
+
await this.options.store.setRetention(scope, lazyHistorySetting(allowance), undefined);
|
|
164
182
|
value = await this.options.store.readRetention(scope);
|
|
165
183
|
}
|
|
166
184
|
return value;
|
|
167
185
|
});
|
|
168
|
-
if (!setting ||
|
|
186
|
+
if (!setting ||
|
|
187
|
+
!validDays(setting.days) ||
|
|
188
|
+
(setting.conversationDays !== null &&
|
|
189
|
+
setting.conversationDays !== 0 &&
|
|
190
|
+
!validDays(setting.conversationDays)))
|
|
169
191
|
throw new ActivityPolicyError('activity_unavailable');
|
|
170
192
|
return {
|
|
171
193
|
setting,
|
|
172
194
|
allowance,
|
|
173
|
-
projection:
|
|
174
|
-
revision: this.revision(scope, setting, allowance),
|
|
175
|
-
retentionDays: Math.min(setting.days, allowance.maximumDays),
|
|
176
|
-
maximumDays: allowance.maximumDays,
|
|
177
|
-
canEdit,
|
|
178
|
-
},
|
|
195
|
+
projection: historySettingsProjection(setting, allowance, this.revision(scope, setting, allowance), canEdit),
|
|
179
196
|
};
|
|
180
197
|
}
|
|
198
|
+
/**
|
|
199
|
+
* One change under one revision. Shortening or turning off conversations caps stored expiry right
|
|
200
|
+
* after the setting is written, so no longer transcript survives behind the shorter window; a dry
|
|
201
|
+
* run counts exactly what that cap would hide, saves nothing and is not audited.
|
|
202
|
+
*/
|
|
181
203
|
async save(scope, input, local = runLocal) {
|
|
182
204
|
const current = await this.settings(scope, true, local);
|
|
183
205
|
if (current.projection.revision !== input.expectedRevision)
|
|
184
206
|
throw new ActivityPolicyError('activity_conflict');
|
|
185
|
-
|
|
207
|
+
const plan = planHistorySettingsChange(current.setting, input, current.allowance.maximumDays);
|
|
208
|
+
if (!plan)
|
|
186
209
|
throw new ActivityPolicyError('activity_invalid');
|
|
187
|
-
|
|
210
|
+
const store = this.options.conversationHistory;
|
|
211
|
+
const window = (method) => plan.shortened === undefined || !store
|
|
212
|
+
? { conversations: 0, items: 0 }
|
|
213
|
+
: local(() => store[method]({ org: scope.org, app: scope.app, env: scope.env }, { now: this.options.now?.() ?? Date.now(), days: plan.shortened ?? 0 }));
|
|
214
|
+
const shortensConversations = plan.shortened !== undefined;
|
|
215
|
+
if (input.dryRun)
|
|
216
|
+
return {
|
|
217
|
+
shortensConversations,
|
|
218
|
+
impact: await window('countOutsideWindow'),
|
|
219
|
+
dryRun: true,
|
|
220
|
+
settings: current.projection,
|
|
221
|
+
audit: undefined,
|
|
222
|
+
};
|
|
223
|
+
if (!(await local(() => this.options.store.setRetention(scope, plan.next, current.setting.revision))))
|
|
188
224
|
throw new ActivityPolicyError('activity_conflict');
|
|
189
|
-
|
|
225
|
+
const impact = await window('capExpiry');
|
|
226
|
+
return {
|
|
227
|
+
shortensConversations,
|
|
228
|
+
impact,
|
|
229
|
+
dryRun: false,
|
|
230
|
+
settings: (await this.settings(scope, true, local)).projection,
|
|
231
|
+
audit: plan.audit,
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Writes a new installation's first setting so it records conversations from day one. Runs only for a
|
|
236
|
+
* created installation, before anything reads (and lazily creates) a not-enabled setting.
|
|
237
|
+
*/
|
|
238
|
+
async initializeHistory(scope) {
|
|
239
|
+
return this.options.store.setRetention(scope, newInstallationHistorySetting(await this.allowance(scope)), undefined);
|
|
240
|
+
}
|
|
241
|
+
/** The capture policy: side-effect free, so the capture path never creates a setting. */
|
|
242
|
+
async conversationPolicy(scope) {
|
|
243
|
+
const allowance = await this.allowance(scope);
|
|
244
|
+
return conversationPolicyFromSetting(await this.options.store.readRetention(scope), allowance);
|
|
190
245
|
}
|
|
191
246
|
async page(scope, input, local = runLocal) {
|
|
192
247
|
if (!Number.isInteger(input.limit) || input.limit < 1 || input.limit > 100)
|
|
@@ -198,7 +253,7 @@ export class ApplicationActivity {
|
|
|
198
253
|
revision: policy.revision,
|
|
199
254
|
limit: input.limit,
|
|
200
255
|
});
|
|
201
|
-
const records = await local(() => this.options.store.list(scope, this.options.now?.() ?? Date.now(), policy.retentionDays, input.limit + 1, decodeActivityCursor(input.cursor, binding)));
|
|
256
|
+
const records = await local(() => this.options.store.list(scope, this.options.now?.() ?? Date.now(), policy.activity.retentionDays, input.limit + 1, decodeActivityCursor(input.cursor, binding)));
|
|
202
257
|
if ((await this.settings(scope, false, local)).projection.revision !== policy.revision)
|
|
203
258
|
throw new ActivityPolicyError('activity_conflict');
|
|
204
259
|
const page = records.slice(0, input.limit);
|
|
@@ -217,7 +272,7 @@ export class ApplicationActivity {
|
|
|
217
272
|
: { completedAt: new Date(completedAt).toISOString() }),
|
|
218
273
|
...(reference === undefined ? {} : { reference }),
|
|
219
274
|
})),
|
|
220
|
-
historyDays: policy.retentionDays,
|
|
275
|
+
historyDays: policy.activity.retentionDays,
|
|
221
276
|
...(records.length > page.length && last
|
|
222
277
|
? {
|
|
223
278
|
nextCursor: Buffer.from(JSON.stringify({ binding, startedAt: last.startedAt, id: last.id })).toString('base64url'),
|
|
@@ -337,7 +392,7 @@ export class ApplicationActivity {
|
|
|
337
392
|
...this.options,
|
|
338
393
|
scope,
|
|
339
394
|
deploymentId: target.deploymentId,
|
|
340
|
-
historyDays: async () => (await this.settings(scope, false)).projection.retentionDays,
|
|
395
|
+
historyDays: async () => (await this.settings(scope, false)).projection.activity.retentionDays,
|
|
341
396
|
executionBoundMs: (intent) => intent.executionBoundMs,
|
|
342
397
|
connectionGeneration: (id) => target.served.deps.executionBinding?.connections[id] ?? revision,
|
|
343
398
|
authorize,
|