@noodleseed/one 0.175.1 → 0.177.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/knowledge.d.ts.map +1 -1
- package/dist/commands/knowledge.js +4 -7
- package/dist/commands/knowledge.js.map +1 -1
- package/dist/commands/solutions-conversations.d.ts +6 -0
- package/dist/commands/solutions-conversations.d.ts.map +1 -0
- package/dist/commands/solutions-conversations.js +137 -0
- package/dist/commands/solutions-conversations.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 +12 -2
- package/dist/commands/whatsapp-ops.js.map +1 -1
- package/dist/commands/whatsapp-output.d.ts.map +1 -1
- package/dist/commands/whatsapp-output.js +1 -0
- package/dist/commands/whatsapp-output.js.map +1 -1
- package/node_modules/@noodle-borg/assistant-gateway/dist/channel-coordinator.d.ts +14 -8
- package/node_modules/@noodle-borg/assistant-gateway/dist/channel-coordinator.js +11 -2
- package/node_modules/@noodle-borg/assistant-gateway/dist/channel-types.d.ts +14 -5
- package/node_modules/@noodle-borg/cli-catalog/dist/catalog-data-channels.js +5 -3
- package/node_modules/@noodle-borg/cli-catalog/dist/catalog-data-solution-conversations.js +97 -0
- package/node_modules/@noodle-borg/cli-catalog/dist/catalog-data-solutions.js +2 -0
- package/node_modules/@noodle-borg/knowledge-operations/dist/executor.d.ts +3 -5
- package/node_modules/@noodle-borg/knowledge-operations/dist/executor.js +2 -7
- package/node_modules/@noodle-borg/knowledge-operations/dist/portable.d.ts +1 -1
- package/node_modules/@noodle-borg/knowledge-operations/dist/portable.js +1 -1
- package/node_modules/@noodle-borg/knowledge-operations/dist/publication.d.ts +0 -1
- package/node_modules/@noodle-borg/knowledge-operations/dist/publication.js +1 -10
- package/node_modules/@noodle-borg/knowledge-operations/dist/routes.d.ts +0 -4
- package/node_modules/@noodle-borg/knowledge-operations/dist/routes.js +1 -17
- package/node_modules/@noodle-borg/knowledge-operations/dist/search-port.d.ts +1 -2
- package/node_modules/@noodle-borg/knowledge-operations/dist/search-port.js +1 -3
- package/node_modules/@noodle-borg/knowledge-operations/dist/service-wiring.d.ts +4 -4
- package/node_modules/@noodle-borg/knowledge-operations/dist/service-wiring.js +3 -8
- package/node_modules/@noodle-borg/knowledge-operations/dist/status-routes.d.ts +1 -2
- package/node_modules/@noodle-borg/knowledge-operations/dist/status-routes.js +0 -14
- package/node_modules/@noodle-borg/protocol/dist/knowledge-tool.js +3 -5
- package/node_modules/@noodle-borg/runtime/dist/execute.d.ts +2 -3
- package/node_modules/@noodle-borg/service/dist/assistant-composition.js +9 -0
- package/node_modules/@noodle-borg/service/dist/business-information/validation.js +1 -22
- package/node_modules/@noodle-borg/service/dist/channels/360dialog.js +41 -145
- package/node_modules/@noodle-borg/service/dist/channels/composition.js +1 -0
- package/node_modules/@noodle-borg/service/dist/channels/meta-cloud.js +102 -0
- package/node_modules/@noodle-borg/service/dist/channels/provider.js +187 -0
- package/node_modules/@noodle-borg/service/dist/channels/runtime.js +49 -21
- package/node_modules/@noodle-borg/service/dist/conversation-history/capture.js +99 -0
- package/node_modules/@noodle-borg/service/dist/conversation-history/contracts.js +17 -0
- package/node_modules/@noodle-borg/service/dist/conversation-history/operator.js +184 -0
- package/node_modules/@noodle-borg/service/dist/payment-card.js +34 -0
- package/node_modules/@noodle-borg/service/dist/routes/assistant-elevation.js +2 -0
- package/node_modules/@noodle-borg/service/dist/routes/assistant-interaction-stream.js +4 -2
- package/node_modules/@noodle-borg/service/dist/routes/assistant-interactions.js +15 -0
- package/node_modules/@noodle-borg/service/dist/routes/assistant-knowledge.js +1 -3
- package/node_modules/@noodle-borg/service/dist/routes/assistant.js +8 -6
- package/node_modules/@noodle-borg/service/dist/routes/business-information-conversations.js +65 -0
- package/node_modules/@noodle-borg/service/dist/routes/business-information-dispatch.js +3 -0
- package/node_modules/@noodle-borg/service/dist/routes/business-information-paths.js +16 -0
- package/node_modules/@noodle-borg/service/dist/routes/whatsapp-meta.js +130 -0
- package/node_modules/@noodle-borg/service/dist/routes/whatsapp.js +18 -9
- package/node_modules/@noodle-borg/service/dist/service-history-projections.js +24 -0
- package/node_modules/@noodle-borg/service/dist/service.js +3 -7
- package/node_modules/@noodle-borg/transport-http/dist/index.d.ts +1 -1
- package/node_modules/@noodle-borg/transport-http/dist/index.js +1 -1
- package/node_modules/@noodle-borg/transport-http/dist/request-body.d.ts +9 -0
- package/node_modules/@noodle-borg/transport-http/dist/request-body.js +2 -1
- package/node_modules/@noodle-borg/wire-contracts/dist/conversation-history.d.ts +347 -0
- package/node_modules/@noodle-borg/wire-contracts/dist/conversation-history.js +120 -0
- 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/@noodle-borg/wire-contracts/dist/whatsapp.d.ts +31 -9
- package/node_modules/@noodle-borg/wire-contracts/dist/whatsapp.js +27 -4
- package/package.json +1 -1
|
@@ -65,7 +65,15 @@ export class ChannelCoordinator {
|
|
|
65
65
|
};
|
|
66
66
|
});
|
|
67
67
|
}
|
|
68
|
+
/** The binding that owns a provider phone-number asset, whatever its provider or state. */
|
|
69
|
+
async bindingForAsset(phoneNumberId) {
|
|
70
|
+
return this.store.transaction([CHANNEL_REGISTRY], (tx) => channelValue(tx, CHANNEL_REGISTRY, `asset:${channelDigest(phoneNumberId)}`));
|
|
71
|
+
}
|
|
68
72
|
async configure(input, actor, key, revision) {
|
|
73
|
+
const meta = input.provider === 'meta';
|
|
74
|
+
// 360dialog authenticates callbacks per binding; Meta signs them with the app secret per WABA.
|
|
75
|
+
if (meta ? !input.wabaId || input.webhookSecret : !input.webhookSecret || input.wabaId)
|
|
76
|
+
throw new ChannelError('provider_configuration_invalid');
|
|
69
77
|
const tenantKey = channelTenantKey(input.tenant);
|
|
70
78
|
const priorId = await this.store.transaction([CHANNEL_REGISTRY], (tx) => channelValue(tx, CHANNEL_REGISTRY, tenantKey));
|
|
71
79
|
const prior = priorId ? await this.internal(priorId) : undefined;
|
|
@@ -86,10 +94,11 @@ export class ChannelCoordinator {
|
|
|
86
94
|
if (owner && owner !== id)
|
|
87
95
|
throw new ChannelError('asset_in_use');
|
|
88
96
|
const now = this.now();
|
|
97
|
+
const { provider: _provider, ...configured } = input;
|
|
89
98
|
const binding = {
|
|
90
|
-
...
|
|
99
|
+
...configured,
|
|
91
100
|
id,
|
|
92
|
-
provider: '360dialog',
|
|
101
|
+
provider: meta ? 'meta' : '360dialog',
|
|
93
102
|
revision: revision + 1,
|
|
94
103
|
generation: (existing?.generation ?? 0) + 1,
|
|
95
104
|
state: 'paused',
|
|
@@ -21,6 +21,8 @@ export type ChannelLimits = {
|
|
|
21
21
|
export type ChannelLimitsInput = {
|
|
22
22
|
[K in keyof ChannelLimits]?: number | undefined;
|
|
23
23
|
};
|
|
24
|
+
/** `360dialog` relays the Cloud API with a per-binding callback secret; `meta` is the Cloud API itself. */
|
|
25
|
+
export type ChannelProvider = '360dialog' | 'meta';
|
|
24
26
|
export type ChannelCapability = {
|
|
25
27
|
readonly kind: 'tool' | 'knowledge';
|
|
26
28
|
readonly name: string;
|
|
@@ -28,10 +30,14 @@ export type ChannelCapability = {
|
|
|
28
30
|
export interface ChannelBinding {
|
|
29
31
|
readonly id: string;
|
|
30
32
|
readonly tenant: TenantRef;
|
|
31
|
-
readonly provider:
|
|
33
|
+
readonly provider: ChannelProvider;
|
|
32
34
|
readonly phoneNumberId: string;
|
|
35
|
+
/** The WhatsApp Business Account that owns the number; present only for `meta`. */
|
|
36
|
+
readonly wabaId?: string | undefined;
|
|
37
|
+
/** Tenant secret name of the provider credential: 360dialog key or Meta business token. */
|
|
33
38
|
readonly apiKeySecret: string;
|
|
34
|
-
|
|
39
|
+
/** Tenant secret name of the per-binding callback secret; present only for `360dialog`. */
|
|
40
|
+
readonly webhookSecret?: string | undefined;
|
|
35
41
|
readonly deploymentId: string;
|
|
36
42
|
readonly capabilities: readonly ChannelCapability[];
|
|
37
43
|
readonly supportEmail: string;
|
|
@@ -48,7 +54,9 @@ export interface ChannelBinding {
|
|
|
48
54
|
readonly readyRevision?: number | undefined;
|
|
49
55
|
readonly readyAt?: number | undefined;
|
|
50
56
|
}
|
|
51
|
-
export type ChannelConfigure = Pick<ChannelBinding, 'tenant' | 'phoneNumberId' | 'apiKeySecret' | 'webhookSecret' | 'deploymentId' | 'capabilities' | 'supportEmail'> & {
|
|
57
|
+
export type ChannelConfigure = Pick<ChannelBinding, 'tenant' | 'phoneNumberId' | 'wabaId' | 'apiKeySecret' | 'webhookSecret' | 'deploymentId' | 'capabilities' | 'supportEmail'> & {
|
|
58
|
+
/** Defaults to `360dialog`. */
|
|
59
|
+
readonly provider?: ChannelProvider | undefined;
|
|
52
60
|
readonly limits?: ChannelLimitsInput | undefined;
|
|
53
61
|
};
|
|
54
62
|
export interface ChannelAddress {
|
|
@@ -126,10 +134,11 @@ export declare function writeChannelEvent(tx: ChannelTransaction, scope: string,
|
|
|
126
134
|
export declare function publicChannelBinding(binding: ChannelBinding): {
|
|
127
135
|
id: string;
|
|
128
136
|
tenant: TenantRef;
|
|
129
|
-
provider:
|
|
137
|
+
provider: ChannelProvider;
|
|
130
138
|
phoneNumberId: string;
|
|
139
|
+
wabaId?: string | undefined;
|
|
131
140
|
apiKeySecret: string;
|
|
132
|
-
webhookSecret
|
|
141
|
+
webhookSecret?: string | undefined;
|
|
133
142
|
deploymentId: string;
|
|
134
143
|
capabilities: readonly ChannelCapability[];
|
|
135
144
|
supportEmail: string;
|
|
@@ -48,14 +48,16 @@ export const CHANNELS_COMMAND = {
|
|
|
48
48
|
arguments: [],
|
|
49
49
|
flags: [],
|
|
50
50
|
subcommands: [
|
|
51
|
-
group('whatsapp', 'Configure
|
|
51
|
+
group('whatsapp', 'Configure a 360dialog or Meta Cloud API number, test readiness, and control public messaging.', [
|
|
52
52
|
leaf('status', 'Show configuration and current revision.'),
|
|
53
53
|
leaf('configure', 'Save a paused binding using managed secret references.', [
|
|
54
54
|
revision,
|
|
55
55
|
...mutation,
|
|
56
|
+
flag('provider', '360dialog (default) or meta for the Meta Cloud API.'),
|
|
56
57
|
flag('phone-number-id', '360dialog/Meta phone-number asset ID.'),
|
|
57
|
-
flag('
|
|
58
|
-
flag('
|
|
58
|
+
flag('waba-id', 'WhatsApp Business Account ID; required with --provider meta.'),
|
|
59
|
+
flag('api-key-secret', 'Managed secret name for the 360dialog key or the Meta business token.'),
|
|
60
|
+
flag('webhook-secret', 'Independent managed callback secret name; 360dialog only.'),
|
|
59
61
|
flag('capabilities', 'Comma-separated knowledge:name or tool:name selections.'),
|
|
60
62
|
flag('support-email', 'Contact shown when an answer cannot be completed.'),
|
|
61
63
|
...limits,
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/** Staff conversation history operations (ADR 0241); imported by the solutions family. Data only. */
|
|
2
|
+
import { INSTALLATION_ARGUMENT, OPTIONAL_FLAG, SOLUTION_COMMON_FLAGS, } from './catalog-data-solution-flags.js';
|
|
3
|
+
const paging = (maximum) => [
|
|
4
|
+
{
|
|
5
|
+
...OPTIONAL_FLAG,
|
|
6
|
+
name: 'limit',
|
|
7
|
+
type: 'integer',
|
|
8
|
+
value: '<count>',
|
|
9
|
+
summary: 'Maximum conversations in this page.',
|
|
10
|
+
constraints: { minimum: 1, maximum },
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
...OPTIONAL_FLAG,
|
|
14
|
+
name: 'cursor',
|
|
15
|
+
type: 'string',
|
|
16
|
+
value: '<cursor>',
|
|
17
|
+
summary: 'Opaque continuation from the prior page.',
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
...OPTIONAL_FLAG,
|
|
21
|
+
name: 'channel',
|
|
22
|
+
type: 'string',
|
|
23
|
+
value: '<channel>',
|
|
24
|
+
summary: 'Only conversations from this channel.',
|
|
25
|
+
constraints: { choices: ['website', 'whatsapp'] },
|
|
26
|
+
},
|
|
27
|
+
];
|
|
28
|
+
const SELECTORS = ['conversation', 'customer', 'participant'];
|
|
29
|
+
const SELECTOR_SUMMARIES = {
|
|
30
|
+
conversation: 'Conversation id to erase.',
|
|
31
|
+
customer: 'Verified customer reference whose conversations are erased.',
|
|
32
|
+
participant: 'WhatsApp participant reference whose conversations are erased.',
|
|
33
|
+
};
|
|
34
|
+
export const SOLUTION_CONVERSATIONS = {
|
|
35
|
+
name: 'conversations',
|
|
36
|
+
summary: 'Read, export and forget customer conversation history.',
|
|
37
|
+
arguments: [],
|
|
38
|
+
flags: [],
|
|
39
|
+
subcommands: [
|
|
40
|
+
{
|
|
41
|
+
name: 'list',
|
|
42
|
+
summary: 'List conversations with unexpired messages, newest first.',
|
|
43
|
+
arguments: [INSTALLATION_ARGUMENT],
|
|
44
|
+
flags: [...SOLUTION_COMMON_FLAGS, ...paging(100)],
|
|
45
|
+
jsonOutput: { mode: 'single' },
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
name: 'show',
|
|
49
|
+
summary: 'Show one conversation with its messages; the read is audited.',
|
|
50
|
+
arguments: [INSTALLATION_ARGUMENT],
|
|
51
|
+
flags: [
|
|
52
|
+
...SOLUTION_COMMON_FLAGS,
|
|
53
|
+
{
|
|
54
|
+
...OPTIONAL_FLAG,
|
|
55
|
+
required: true,
|
|
56
|
+
name: 'conversation',
|
|
57
|
+
type: 'string',
|
|
58
|
+
value: '<id>',
|
|
59
|
+
summary: 'Conversation id from conversations list.',
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
jsonOutput: { mode: 'single' },
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: 'export',
|
|
66
|
+
summary: 'Export one page of conversations with messages (Owner or Administrator).',
|
|
67
|
+
arguments: [INSTALLATION_ARGUMENT],
|
|
68
|
+
flags: [...SOLUTION_COMMON_FLAGS, ...paging(25)],
|
|
69
|
+
jsonOutput: { mode: 'single' },
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
name: 'forget',
|
|
73
|
+
summary: 'Erase one conversation, or every conversation of a customer or participant.',
|
|
74
|
+
arguments: [INSTALLATION_ARGUMENT],
|
|
75
|
+
flags: [
|
|
76
|
+
...SOLUTION_COMMON_FLAGS,
|
|
77
|
+
...SELECTORS.map((name) => ({
|
|
78
|
+
...OPTIONAL_FLAG,
|
|
79
|
+
name,
|
|
80
|
+
type: 'string',
|
|
81
|
+
value: name === 'conversation' ? '<id>' : '<ref>',
|
|
82
|
+
summary: SELECTOR_SUMMARIES[name],
|
|
83
|
+
conflictsWith: SELECTORS.filter((other) => other !== name),
|
|
84
|
+
})),
|
|
85
|
+
{
|
|
86
|
+
...OPTIONAL_FLAG,
|
|
87
|
+
name: 'confirm',
|
|
88
|
+
type: 'boolean',
|
|
89
|
+
required: true,
|
|
90
|
+
summary: 'Confirm permanently erasing the selected conversation history.',
|
|
91
|
+
},
|
|
92
|
+
],
|
|
93
|
+
jsonOutput: { mode: 'single' },
|
|
94
|
+
},
|
|
95
|
+
],
|
|
96
|
+
};
|
|
97
|
+
//# sourceMappingURL=catalog-data-solution-conversations.js.map
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/** Solution installation/operator catalog. Imports only dependency-free sibling data. */
|
|
2
|
+
import { SOLUTION_CONVERSATIONS } from './catalog-data-solution-conversations.js';
|
|
2
3
|
import { SOLUTION_DRAFTS } from './catalog-data-solution-drafts.js';
|
|
3
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';
|
|
4
5
|
import { SOLUTION_PAGE } from './catalog-data-solution-page.js';
|
|
@@ -171,6 +172,7 @@ export const CATALOG_SOLUTIONS = {
|
|
|
171
172
|
},
|
|
172
173
|
],
|
|
173
174
|
},
|
|
175
|
+
SOLUTION_CONVERSATIONS,
|
|
174
176
|
{
|
|
175
177
|
name: 'activity',
|
|
176
178
|
summary: 'Inspect payload-free operation evidence and its retention policy.',
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Runtime `search_<name>` execution (ADR 0202 amendment 2026-08-18):
|
|
3
|
-
*
|
|
2
|
+
* Runtime `search_<name>` execution (ADR 0202 amendment 2026-08-18): bundled BM25 for documents
|
|
3
|
+
* → bundled BM25 over the crawled site corpus → deterministic RRF fusion with
|
|
4
4
|
* the exact origin/path policy postfilter. Every query is in-process and free; the crawl-page
|
|
5
5
|
* budget is enforced at crawl time (`crawl-lifecycle.ts`), not here. Before a component's first
|
|
6
6
|
* completed crawl the site corpus is honestly empty, with the crawl state as the operator
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { type SearchHit } from '@noodle-borg/knowledge/portable';
|
|
10
10
|
import type { KnowledgeDeployHooks } from './publication.js';
|
|
11
|
-
import {
|
|
11
|
+
import type { KnowledgeTenantRef } from './routes.js';
|
|
12
12
|
/** The component facts the executor needs; a structural subset of the compiled component. */
|
|
13
13
|
export interface KnowledgeSearchComponent {
|
|
14
14
|
readonly name: string;
|
|
@@ -22,13 +22,11 @@ export interface KnowledgeSearchRequest {
|
|
|
22
22
|
readonly limit?: number;
|
|
23
23
|
}
|
|
24
24
|
export interface KnowledgeSearchExecutor {
|
|
25
|
-
enabled(tenant: KnowledgeTenantRef): Promise<boolean>;
|
|
26
25
|
/** Throws `KnowledgeError` or `SearchBudgetExhaustedError`; never returns partial truth. */
|
|
27
26
|
search(tenant: KnowledgeTenantRef, component: KnowledgeSearchComponent, request: KnowledgeSearchRequest): Promise<readonly SearchHit[]>;
|
|
28
27
|
}
|
|
29
28
|
export interface KnowledgeSearchExecutorDeps {
|
|
30
29
|
readonly hooks: KnowledgeDeployHooks;
|
|
31
|
-
readonly knowledgeEnabled: (tenant: KnowledgeTenantRef) => Promise<boolean>;
|
|
32
30
|
}
|
|
33
31
|
export declare function createKnowledgeSearchExecutor(deps: KnowledgeSearchExecutorDeps): KnowledgeSearchExecutor;
|
|
34
32
|
//# sourceMappingURL=executor.d.ts.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Runtime `search_<name>` execution (ADR 0202 amendment 2026-08-18):
|
|
3
|
-
*
|
|
2
|
+
* Runtime `search_<name>` execution (ADR 0202 amendment 2026-08-18): bundled BM25 for documents
|
|
3
|
+
* → bundled BM25 over the crawled site corpus → deterministic RRF fusion with
|
|
4
4
|
* the exact origin/path policy postfilter. Every query is in-process and free; the crawl-page
|
|
5
5
|
* budget is enforced at crawl time (`crawl-lifecycle.ts`), not here. Before a component's first
|
|
6
6
|
* completed crawl the site corpus is honestly empty, with the crawl state as the operator
|
|
@@ -8,14 +8,9 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { DEFAULT_RESULT_LIMIT, fuseHits, KnowledgeError, MAX_QUERY_CHARS, MAX_RESULT_LIMIT, MIN_QUERY_CHARS, MIN_RESULT_LIMIT, } from '@noodle-borg/knowledge/portable';
|
|
10
10
|
import { searchSiteCorpus } from './crawl-lifecycle.js';
|
|
11
|
-
import { knowledgeEnableCommand } from './routes.js';
|
|
12
11
|
export function createKnowledgeSearchExecutor(deps) {
|
|
13
12
|
return {
|
|
14
|
-
enabled: (tenant) => deps.knowledgeEnabled(tenant),
|
|
15
13
|
async search(tenant, component, request) {
|
|
16
|
-
if (!(await deps.knowledgeEnabled(tenant))) {
|
|
17
|
-
throw new KnowledgeError('request', `knowledge is not enabled for this org/app/env; run: ${knowledgeEnableCommand(tenant)}`);
|
|
18
|
-
}
|
|
19
14
|
if (request.query.length < MIN_QUERY_CHARS || request.query.length > MAX_QUERY_CHARS) {
|
|
20
15
|
throw new KnowledgeError('request', `query must be between ${MIN_QUERY_CHARS} and ${MAX_QUERY_CHARS} characters`);
|
|
21
16
|
}
|
|
@@ -5,7 +5,7 @@ export { createKnowledgeSearchExecutor, type KnowledgeSearchComponent, type Know
|
|
|
5
5
|
export { readBody, sendJson } from './http.js';
|
|
6
6
|
export { type KnowledgeRouteRef, parseKnowledgePath } from './paths.js';
|
|
7
7
|
export { createKnowledgeDeployHooks, type KnowledgeDeployFailure, type KnowledgeDeployHooks, KnowledgePublicationError, type KnowledgePublicationPlan, withKnowledgePublication, } from './publication.js';
|
|
8
|
-
export { handleKnowledgeDocumentUpload, handleKnowledgePreflight, type KnowledgeRouteDeps, type KnowledgeTenantRef,
|
|
8
|
+
export { handleKnowledgeDocumentUpload, handleKnowledgePreflight, type KnowledgeRouteDeps, type KnowledgeTenantRef, knowledgeTenantKey, } from './routes.js';
|
|
9
9
|
export { type BoundKnowledgeSearchPort, bindKnowledgeSearchPort, type KnowledgeSearchPortFactory, knowledgeSearchPortFactory, } from './search-port.js';
|
|
10
10
|
export { budgetCeilingsResolver, buildKnowledgeRouteDeps, defaultKnowledgeStores, type KnowledgeServiceStores, wireKnowledge, } from './service-wiring.js';
|
|
11
11
|
export { InMemoryKnowledgeStagingStore, type KnowledgeStagingStore, STAGING_TTL_MS, } from './staging-store.js';
|
|
@@ -5,7 +5,7 @@ export { createKnowledgeSearchExecutor, } from './executor.js';
|
|
|
5
5
|
export { readBody, sendJson } from './http.js';
|
|
6
6
|
export { parseKnowledgePath } from './paths.js';
|
|
7
7
|
export { createKnowledgeDeployHooks, KnowledgePublicationError, withKnowledgePublication, } from './publication.js';
|
|
8
|
-
export { handleKnowledgeDocumentUpload, handleKnowledgePreflight,
|
|
8
|
+
export { handleKnowledgeDocumentUpload, handleKnowledgePreflight, knowledgeTenantKey, } from './routes.js';
|
|
9
9
|
export { bindKnowledgeSearchPort, knowledgeSearchPortFactory, } from './search-port.js';
|
|
10
10
|
export { budgetCeilingsResolver, buildKnowledgeRouteDeps, defaultKnowledgeStores, wireKnowledge, } from './service-wiring.js';
|
|
11
11
|
export { InMemoryKnowledgeStagingStore, STAGING_TTL_MS, } from './staging-store.js';
|
|
@@ -51,7 +51,6 @@ export interface KnowledgeDeployHooks {
|
|
|
51
51
|
sweep(): Promise<void>;
|
|
52
52
|
}
|
|
53
53
|
export declare function createKnowledgeDeployHooks(stores: KnowledgeServiceStores, options: {
|
|
54
|
-
readonly knowledgeEnabled: (tenant: KnowledgeTenantRef) => Promise<boolean>;
|
|
55
54
|
/** Managed site datastore existence; when bound, a site() deploy fails before activation. */
|
|
56
55
|
readonly siteProvisioned?: (tenant: KnowledgeTenantRef) => Promise<boolean>;
|
|
57
56
|
/**
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* the previous artifact + revision pair; retry is idempotent by content hash.
|
|
6
6
|
*/
|
|
7
7
|
import { Bm25KnowledgeIndex, identityDocumentTextCodec, KnowledgeError, revisionContentHash, } from '@noodle-borg/knowledge/portable';
|
|
8
|
-
import {
|
|
8
|
+
import { knowledgeTenantKey } from './routes.js';
|
|
9
9
|
/** Structured publication failure the registry maps onto its deploy error shape. */
|
|
10
10
|
export class KnowledgePublicationError extends Error {
|
|
11
11
|
errors;
|
|
@@ -84,15 +84,6 @@ export function createKnowledgeDeployHooks(stores, options) {
|
|
|
84
84
|
const hooks = {
|
|
85
85
|
async publish(tenant, deploymentId, components) {
|
|
86
86
|
const withDocuments = components.filter((component) => component.documents.length > 0);
|
|
87
|
-
if (components.length > 0 && !(await options.knowledgeEnabled(tenant))) {
|
|
88
|
-
throw new KnowledgePublicationError([
|
|
89
|
-
{
|
|
90
|
-
code: 'knowledge_not_enabled',
|
|
91
|
-
path: 'knowledge',
|
|
92
|
-
message: `knowledge is not enabled for this org/app/env; run: ${knowledgeEnableCommand(tenant)}`,
|
|
93
|
-
},
|
|
94
|
-
]);
|
|
95
|
-
}
|
|
96
87
|
const withSites = components.filter((component) => component.sites.length > 0);
|
|
97
88
|
if (withSites.length > 0 && options.siteProvisioned !== undefined) {
|
|
98
89
|
if (!(await options.siteProvisioned(tenant))) {
|
|
@@ -8,8 +8,6 @@ export interface KnowledgeTenantRef {
|
|
|
8
8
|
}
|
|
9
9
|
export interface KnowledgeRouteDeps {
|
|
10
10
|
readonly staging: KnowledgeStagingStore;
|
|
11
|
-
/** Managed-config feature gate (`NOODLE_KNOWLEDGE_ENABLED`), resolved org → app → env. */
|
|
12
|
-
readonly knowledgeEnabled: (tenant: KnowledgeTenantRef) => Promise<boolean>;
|
|
13
11
|
readonly maxBodyBytes: number;
|
|
14
12
|
/** Seals staged bytes at rest; the service injects its secret-box codec. */
|
|
15
13
|
readonly codec?: DocumentTextCodec;
|
|
@@ -17,8 +15,6 @@ export interface KnowledgeRouteDeps {
|
|
|
17
15
|
readonly status?: import('./status-routes.js').KnowledgeStatusDeps;
|
|
18
16
|
}
|
|
19
17
|
export declare function knowledgeTenantKey(tenant: KnowledgeTenantRef): string;
|
|
20
|
-
/** The one command that turns the feature on for a tenant; every fail-closed error names it. */
|
|
21
|
-
export declare function knowledgeEnableCommand(tenant: KnowledgeTenantRef): string;
|
|
22
18
|
/**
|
|
23
19
|
* `POST /v1/orgs/{o}/apps/{a}/envs/{e}/knowledge/preflight` — diff declared document hashes
|
|
24
20
|
* against transient staging and published revisions; respond with exactly the hashes that
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Knowledge control-plane route handlers (ADR 0202 as amended). The service dispatch
|
|
3
3
|
* authorizes the request and parses the tenant path, then delegates here; these handlers own
|
|
4
|
-
* validation,
|
|
4
|
+
* validation, staging, and wire shapes — never auth.
|
|
5
5
|
*/
|
|
6
6
|
import { createHash } from 'node:crypto';
|
|
7
7
|
import { MAX_DOCUMENT_BYTES } from '@noodle-borg/knowledge/limits';
|
|
@@ -11,18 +11,6 @@ import { readBody, sendJson } from './http.js';
|
|
|
11
11
|
export function knowledgeTenantKey(tenant) {
|
|
12
12
|
return `${tenant.org}/${tenant.app}/${tenant.env}`;
|
|
13
13
|
}
|
|
14
|
-
/** The one command that turns the feature on for a tenant; every fail-closed error names it. */
|
|
15
|
-
export function knowledgeEnableCommand(tenant) {
|
|
16
|
-
return (`noodle variables set NOODLE_KNOWLEDGE_ENABLED --value true --runtime cloud ` +
|
|
17
|
-
`--scope env --org ${tenant.org} --app ${tenant.app} --env ${tenant.env}`);
|
|
18
|
-
}
|
|
19
|
-
function sendGateClosed(res, tenant) {
|
|
20
|
-
sendJson(res, 403, {
|
|
21
|
-
code: 'knowledge_not_enabled',
|
|
22
|
-
error: 'knowledge is not enabled for this org/app/env',
|
|
23
|
-
fix: knowledgeEnableCommand(tenant),
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
14
|
const SHA256_HEX = /^[0-9a-f]{64}$/;
|
|
27
15
|
/**
|
|
28
16
|
* `POST /v1/orgs/{o}/apps/{a}/envs/{e}/knowledge/preflight` — diff declared document hashes
|
|
@@ -30,8 +18,6 @@ const SHA256_HEX = /^[0-9a-f]{64}$/;
|
|
|
30
18
|
* still need bytes. Descriptors only; no contents cross this route.
|
|
31
19
|
*/
|
|
32
20
|
export async function handleKnowledgePreflight(req, res, tenant, deps) {
|
|
33
|
-
if (!(await deps.knowledgeEnabled(tenant)))
|
|
34
|
-
return sendGateClosed(res, tenant);
|
|
35
21
|
const body = await readBody(req, deps.maxBodyBytes);
|
|
36
22
|
if (!body.ok)
|
|
37
23
|
return sendJson(res, body.status, { error: body.error });
|
|
@@ -74,8 +60,6 @@ export async function handleKnowledgePreflight(req, res, tenant, deps) {
|
|
|
74
60
|
* the service verifies declared length and hash before publication).
|
|
75
61
|
*/
|
|
76
62
|
export async function handleKnowledgeDocumentUpload(req, res, tenant, sha256, deps) {
|
|
77
|
-
if (!(await deps.knowledgeEnabled(tenant)))
|
|
78
|
-
return sendGateClosed(res, tenant);
|
|
79
63
|
if (!SHA256_HEX.test(sha256)) {
|
|
80
64
|
return sendJson(res, 400, {
|
|
81
65
|
code: 'invalid_knowledge_request',
|
|
@@ -8,7 +8,6 @@ import { type SearchHit } from '@noodle-borg/knowledge/portable';
|
|
|
8
8
|
import type { KnowledgeSearchComponent, KnowledgeSearchExecutor } from './executor.js';
|
|
9
9
|
import type { KnowledgeTenantRef } from './routes.js';
|
|
10
10
|
export interface BoundKnowledgeSearchPort {
|
|
11
|
-
enabled(): Promise<boolean>;
|
|
12
11
|
search(componentName: string, request: {
|
|
13
12
|
readonly query: string;
|
|
14
13
|
readonly limit?: number;
|
|
@@ -17,7 +16,7 @@ export interface BoundKnowledgeSearchPort {
|
|
|
17
16
|
readonly hits: readonly SearchHit[];
|
|
18
17
|
} | {
|
|
19
18
|
readonly ok: false;
|
|
20
|
-
readonly reason: 'budget_exhausted' | '
|
|
19
|
+
readonly reason: 'budget_exhausted' | 'provider_error';
|
|
21
20
|
readonly message: string;
|
|
22
21
|
}>;
|
|
23
22
|
}
|
|
@@ -10,7 +10,6 @@ export function knowledgeSearchPortFactory(executor) {
|
|
|
10
10
|
}
|
|
11
11
|
export function bindKnowledgeSearchPort(executor, tenant, components) {
|
|
12
12
|
return {
|
|
13
|
-
enabled: () => executor.enabled(tenant),
|
|
14
13
|
async search(componentName, request) {
|
|
15
14
|
const component = components.find((candidate) => candidate.name === componentName);
|
|
16
15
|
if (component === undefined) {
|
|
@@ -25,8 +24,7 @@ export function bindKnowledgeSearchPort(executor, tenant, components) {
|
|
|
25
24
|
return { ok: false, reason: 'budget_exhausted', message: error.message };
|
|
26
25
|
}
|
|
27
26
|
if (error instanceof KnowledgeError) {
|
|
28
|
-
|
|
29
|
-
return { ok: false, reason, message: error.message };
|
|
27
|
+
return { ok: false, reason: 'provider_error', message: error.message };
|
|
30
28
|
}
|
|
31
29
|
return { ok: false, reason: 'provider_error', message: 'knowledge search failed' };
|
|
32
30
|
}
|
|
@@ -20,8 +20,8 @@ export interface KnowledgeServiceStores {
|
|
|
20
20
|
readonly crawlState?: CrawlStateStore;
|
|
21
21
|
/**
|
|
22
22
|
* Composition seams for tests and BYO providers: fetcher selection defaults to the managed
|
|
23
|
-
* first-party crawler; the
|
|
24
|
-
*
|
|
23
|
+
* first-party crawler; the managed crawler needs no provisioning and BYO configuration checks
|
|
24
|
+
* bind here.
|
|
25
25
|
*/
|
|
26
26
|
readonly fetcherFor?: (tenant: KnowledgeTenantRef, component: CrawlableComponent) => SiteFetcher;
|
|
27
27
|
/** BYO index selection override; defaults to declaration-driven provider adapters. */
|
|
@@ -32,8 +32,8 @@ export interface KnowledgeServiceStores {
|
|
|
32
32
|
export declare function defaultKnowledgeStores(): KnowledgeServiceStores;
|
|
33
33
|
/** Budget ceilings from managed config with the ratified v0 defaults (2000 org / 1000 app). */
|
|
34
34
|
export declare function budgetCeilingsResolver(resolveVariables: (tenant: KnowledgeTenantRef) => Promise<Record<string, string>>): (tenant: KnowledgeTenantRef) => Promise<SearchBudgetCeilings>;
|
|
35
|
-
/** Assemble
|
|
36
|
-
export declare function buildKnowledgeRouteDeps(stores: KnowledgeServiceStores,
|
|
35
|
+
/** Assemble the always-available knowledge route dependencies. */
|
|
36
|
+
export declare function buildKnowledgeRouteDeps(stores: KnowledgeServiceStores, maxBodyBytes: number): KnowledgeRouteDeps;
|
|
37
37
|
/** Map a compiled index declaration plus resolved config values onto its provider adapter. */
|
|
38
38
|
export declare function knowledgeIndexFromDeclaration(declaration: NonNullable<CompiledKnowledgeComponent['index']>, config: Readonly<Record<string, string>>, scope: KnowledgeScope): KnowledgeIndex;
|
|
39
39
|
/**
|
|
@@ -32,11 +32,10 @@ export function budgetCeilingsResolver(resolveVariables) {
|
|
|
32
32
|
};
|
|
33
33
|
};
|
|
34
34
|
}
|
|
35
|
-
/** Assemble
|
|
36
|
-
export function buildKnowledgeRouteDeps(stores,
|
|
35
|
+
/** Assemble the always-available knowledge route dependencies. */
|
|
36
|
+
export function buildKnowledgeRouteDeps(stores, maxBodyBytes) {
|
|
37
37
|
return {
|
|
38
38
|
staging: stores.staging,
|
|
39
|
-
knowledgeEnabled: async (tenant) => (await resolveVariables(tenant)).NOODLE_KNOWLEDGE_ENABLED === 'true',
|
|
40
39
|
maxBodyBytes,
|
|
41
40
|
...(stores.codec === undefined ? {} : { codec: stores.codec }),
|
|
42
41
|
};
|
|
@@ -61,7 +60,7 @@ export function knowledgeIndexFromDeclaration(declaration, config, scope) {
|
|
|
61
60
|
*/
|
|
62
61
|
export function wireKnowledge(registry, stores, resolveVariables, maxBodyBytes, resolveSecrets = async () => ({})) {
|
|
63
62
|
const resolved = stores ?? defaultKnowledgeStores();
|
|
64
|
-
const deps = buildKnowledgeRouteDeps(resolved,
|
|
63
|
+
const deps = buildKnowledgeRouteDeps(resolved, maxBodyBytes);
|
|
65
64
|
const ceilings = budgetCeilingsResolver(resolveVariables);
|
|
66
65
|
// The managed first-party crawler needs no provisioning, so the deploy gate defaults open;
|
|
67
66
|
// BYO provider configuration checks replace this seam when a component declares a provider.
|
|
@@ -117,7 +116,6 @@ export function wireKnowledge(registry, stores, resolveVariables, maxBodyBytes,
|
|
|
117
116
|
return knowledgeIndexFromDeclaration(resolvedDeclaration, config, scope);
|
|
118
117
|
};
|
|
119
118
|
const hooks = createKnowledgeDeployHooks(resolved, {
|
|
120
|
-
knowledgeEnabled: deps.knowledgeEnabled,
|
|
121
119
|
siteProvisioned,
|
|
122
120
|
indexFor: resolved.indexFor ?? defaultIndexFor,
|
|
123
121
|
});
|
|
@@ -146,12 +144,10 @@ export function wireKnowledge(registry, stores, resolveVariables, maxBodyBytes,
|
|
|
146
144
|
};
|
|
147
145
|
const bareExecutor = createKnowledgeSearchExecutor({
|
|
148
146
|
hooks,
|
|
149
|
-
knowledgeEnabled: deps.knowledgeEnabled,
|
|
150
147
|
});
|
|
151
148
|
// Refresh-on-traffic: serving a sites component also checks whether its refresh is due and
|
|
152
149
|
// kicks the crawl in the background — the visitor's answer never waits on it.
|
|
153
150
|
const executor = {
|
|
154
|
-
enabled: bareExecutor.enabled,
|
|
155
151
|
search: async (tenant, component, request) => {
|
|
156
152
|
const hits = await bareExecutor.search(tenant, component, request);
|
|
157
153
|
if (component.sites.length > 0) {
|
|
@@ -182,7 +178,6 @@ export function wireKnowledge(registry, stores, resolveVariables, maxBodyBytes,
|
|
|
182
178
|
registry.setKnowledgeDeployHooks(crawlingHooks, knowledgeSearchPortFactory(executor));
|
|
183
179
|
const status = {
|
|
184
180
|
revisionStore: resolved.revisionStore,
|
|
185
|
-
knowledgeEnabled: deps.knowledgeEnabled,
|
|
186
181
|
crawlState: (tenant, componentName) => crawlState.get(tenant, componentName),
|
|
187
182
|
refresh: (tenant, component) => runComponentCrawl(crawlDeps, tenant, component),
|
|
188
183
|
// Mirrors the fail-closed deploy gate above: without the managed project every site() is
|
|
@@ -7,10 +7,9 @@
|
|
|
7
7
|
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
8
8
|
import type { CompiledKnowledgeComponent, KnowledgeRevisionStore } from '@noodle-borg/knowledge/portable';
|
|
9
9
|
import type { KnowledgeBudgetState, KnowledgeCrawlState } from '@noodle-borg/wire-contracts';
|
|
10
|
-
import {
|
|
10
|
+
import type { KnowledgeTenantRef } from './routes.js';
|
|
11
11
|
export interface KnowledgeStatusDeps {
|
|
12
12
|
readonly revisionStore: KnowledgeRevisionStore;
|
|
13
|
-
readonly knowledgeEnabled: (tenant: KnowledgeTenantRef) => Promise<boolean>;
|
|
14
13
|
/** The active deployment's compiled knowledge components, or undefined when none is active. */
|
|
15
14
|
readonly activeKnowledge: (tenant: KnowledgeTenantRef) => Promise<{
|
|
16
15
|
readonly components: readonly CompiledKnowledgeComponent[];
|
|
@@ -1,12 +1,4 @@
|
|
|
1
1
|
import { sendJson } from './http.js';
|
|
2
|
-
import { knowledgeEnableCommand } from './routes.js';
|
|
3
|
-
function gateClosed(res, tenant) {
|
|
4
|
-
sendJson(res, 403, {
|
|
5
|
-
code: 'knowledge_not_enabled',
|
|
6
|
-
error: 'knowledge is not enabled for this org/app/env',
|
|
7
|
-
fix: knowledgeEnableCommand(tenant),
|
|
8
|
-
});
|
|
9
|
-
}
|
|
10
2
|
async function componentSummary(tenant, component, deploymentId, revisionStore) {
|
|
11
3
|
const scope = { org: tenant.org, app: tenant.app, env: tenant.env };
|
|
12
4
|
const active = component.documents.length > 0 ? await revisionStore.active(scope, component.name) : undefined;
|
|
@@ -22,8 +14,6 @@ async function componentSummary(tenant, component, deploymentId, revisionStore)
|
|
|
22
14
|
}
|
|
23
15
|
/** `GET /v1/orgs/{o}/apps/{a}/envs/{e}/knowledge` */
|
|
24
16
|
export async function handleKnowledgeList(_req, res, tenant, deps) {
|
|
25
|
-
if (!(await deps.knowledgeEnabled(tenant)))
|
|
26
|
-
return gateClosed(res, tenant);
|
|
27
17
|
const active = await deps.activeKnowledge(tenant);
|
|
28
18
|
const components = [];
|
|
29
19
|
for (const component of active?.components ?? []) {
|
|
@@ -38,8 +28,6 @@ export async function handleKnowledgeList(_req, res, tenant, deps) {
|
|
|
38
28
|
}
|
|
39
29
|
/** `GET /v1/orgs/{o}/apps/{a}/envs/{e}/knowledge/{name}/status` */
|
|
40
30
|
export async function handleKnowledgeStatus(_req, res, tenant, componentName, deps) {
|
|
41
|
-
if (!(await deps.knowledgeEnabled(tenant)))
|
|
42
|
-
return gateClosed(res, tenant);
|
|
43
31
|
const active = await deps.activeKnowledge(tenant);
|
|
44
32
|
const component = active?.components.find((candidate) => candidate.name === componentName);
|
|
45
33
|
if (active === undefined || component === undefined) {
|
|
@@ -70,8 +58,6 @@ export async function handleKnowledgeStatus(_req, res, tenant, componentName, de
|
|
|
70
58
|
* The outcome (including failures) is the returned crawl state, never a thrown error.
|
|
71
59
|
*/
|
|
72
60
|
export async function handleKnowledgeRefresh(_req, res, tenant, componentName, deps) {
|
|
73
|
-
if (!(await deps.knowledgeEnabled(tenant)))
|
|
74
|
-
return gateClosed(res, tenant);
|
|
75
61
|
const active = await deps.activeKnowledge(tenant);
|
|
76
62
|
const component = active?.components.find((candidate) => candidate.name === componentName);
|
|
77
63
|
if (component === undefined || deps.refresh === undefined) {
|
|
@@ -2,20 +2,18 @@ import { coerceToolArguments } from './input-validation.js';
|
|
|
2
2
|
export function findKnowledgeComponent(artifact, toolName) {
|
|
3
3
|
return artifact.server.knowledge?.find((component) => component.generatedTool.name === toolName);
|
|
4
4
|
}
|
|
5
|
-
/** Listing predicate: the artifact declares knowledge
|
|
5
|
+
/** Listing predicate: the artifact declares knowledge and the deployment has a search port. */
|
|
6
6
|
export async function knowledgeToolsEnabled(artifact, deps) {
|
|
7
7
|
if ((artifact.server.knowledge?.length ?? 0) === 0)
|
|
8
8
|
return false;
|
|
9
|
-
|
|
10
|
-
return false;
|
|
11
|
-
return deps.knowledgeSearch.enabled();
|
|
9
|
+
return deps.knowledgeSearch !== undefined;
|
|
12
10
|
}
|
|
13
11
|
export async function runKnowledgeSearchTool(component, deps, rawArguments) {
|
|
14
12
|
const coerced = coerceToolArguments(component.generatedTool.inputSchema, rawArguments);
|
|
15
13
|
if (coerced.issues.length > 0)
|
|
16
14
|
return { kind: 'invalid', issues: coerced.issues };
|
|
17
15
|
if (deps.knowledgeSearch === undefined) {
|
|
18
|
-
return { kind: 'error', reason: '
|
|
16
|
+
return { kind: 'error', reason: 'provider_error', message: 'knowledge search is unavailable' };
|
|
19
17
|
}
|
|
20
18
|
const request = coerced.value;
|
|
21
19
|
const outcome = await deps.knowledgeSearch.search(component.name, request);
|
|
@@ -61,10 +61,9 @@ export interface KnowledgeSearchHit {
|
|
|
61
61
|
}
|
|
62
62
|
/**
|
|
63
63
|
* Deployment-bound knowledge retrieval port (structural; the service adapts its executor).
|
|
64
|
-
*
|
|
64
|
+
* Presence of the port makes declared knowledge tools available.
|
|
65
65
|
*/
|
|
66
66
|
export interface KnowledgeSearchPort {
|
|
67
|
-
enabled(): Promise<boolean>;
|
|
68
67
|
search(componentName: string, request: {
|
|
69
68
|
readonly query: string;
|
|
70
69
|
readonly limit?: number | undefined;
|
|
@@ -73,7 +72,7 @@ export interface KnowledgeSearchPort {
|
|
|
73
72
|
readonly hits: readonly KnowledgeSearchHit[];
|
|
74
73
|
} | {
|
|
75
74
|
readonly ok: false;
|
|
76
|
-
readonly reason: 'budget_exhausted' | '
|
|
75
|
+
readonly reason: 'budget_exhausted' | 'provider_error';
|
|
77
76
|
readonly message: string;
|
|
78
77
|
}>;
|
|
79
78
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ConversationCapture } from './conversation-history/capture.js';
|
|
1
2
|
/** Browser and messaging resolve model, execution and tenant policy from one composition. */
|
|
2
3
|
export function assistantRouteDependencies(options, core) {
|
|
3
4
|
return {
|
|
@@ -13,6 +14,14 @@ export function assistantRouteDependencies(options, core) {
|
|
|
13
14
|
: {}),
|
|
14
15
|
...(options.captureRequestEvent ? { captureRequestEvent: options.captureRequestEvent } : {}),
|
|
15
16
|
...(options.clock ? { clock: options.clock } : {}),
|
|
17
|
+
...(options.conversationHistory
|
|
18
|
+
? {
|
|
19
|
+
conversations: new ConversationCapture(options.conversationHistory.store, options.conversationHistory.policy, {
|
|
20
|
+
now: () => (options.clock?.() ?? new Date()).getTime(),
|
|
21
|
+
...(core.logger ? { logger: core.logger } : {}),
|
|
22
|
+
}),
|
|
23
|
+
}
|
|
24
|
+
: {}),
|
|
16
25
|
};
|
|
17
26
|
}
|
|
18
27
|
//# sourceMappingURL=assistant-composition.js.map
|