@hasna/knowledge 0.2.26 → 0.2.28

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.
Files changed (61) hide show
  1. package/README.md +61 -0
  2. package/bin/open-knowledge-mcp.js +85 -9
  3. package/bin/open-knowledge.js +86 -86
  4. package/dist/agent.d.ts +35 -0
  5. package/dist/artifact-store.d.ts +63 -0
  6. package/dist/auth.d.ts +35 -0
  7. package/dist/embeddings.d.ts +77 -0
  8. package/dist/index.d.ts +20 -0
  9. package/dist/index.js +5709 -0
  10. package/dist/knowledge-db.d.ts +27 -0
  11. package/dist/manifest-ingest.d.ts +35 -0
  12. package/dist/outbox-consume.d.ts +25 -0
  13. package/dist/provenance.d.ts +50 -0
  14. package/dist/providers.d.ts +89 -0
  15. package/dist/reindex.d.ts +37 -0
  16. package/dist/remote-client.d.ts +108 -0
  17. package/dist/retrieval.d.ts +71 -0
  18. package/dist/safety.d.ts +70 -0
  19. package/dist/sdk.d.ts +72 -0
  20. package/dist/search.d.ts +65 -0
  21. package/dist/service.d.ts +117 -0
  22. package/dist/source-ingest.d.ts +18 -0
  23. package/dist/source-ref.d.ts +30 -0
  24. package/dist/source-resolver.d.ts +92 -0
  25. package/dist/storage-contract.d.ts +106 -0
  26. package/dist/web-search.d.ts +40 -0
  27. package/dist/wiki-compiler.d.ts +67 -0
  28. package/dist/wiki-layout.d.ts +23 -0
  29. package/dist/workspace.d.ts +111 -0
  30. package/docs/architecture/ai-native-knowledge-base.md +24 -0
  31. package/docs/architecture/hosted-wrapper-responsibilities.md +8 -0
  32. package/docs/canonical-secrets-bootstrap-2026-06-08.md +127 -0
  33. package/package.json +15 -7
  34. package/src/agent.ts +0 -367
  35. package/src/artifact-store.ts +0 -184
  36. package/src/auth.ts +0 -123
  37. package/src/cli.ts +0 -1181
  38. package/src/embeddings.ts +0 -516
  39. package/src/knowledge-db.ts +0 -354
  40. package/src/manifest-ingest.ts +0 -515
  41. package/src/mcp-http.js +0 -110
  42. package/src/mcp.js +0 -1503
  43. package/src/outbox-consume.ts +0 -463
  44. package/src/provenance.ts +0 -93
  45. package/src/providers.ts +0 -308
  46. package/src/reindex.ts +0 -260
  47. package/src/remote-client.ts +0 -268
  48. package/src/retrieval.ts +0 -326
  49. package/src/safety.ts +0 -265
  50. package/src/schema.js +0 -25
  51. package/src/search.ts +0 -510
  52. package/src/service.ts +0 -432
  53. package/src/source-ingest.ts +0 -268
  54. package/src/source-ref.ts +0 -104
  55. package/src/source-resolver.ts +0 -436
  56. package/src/storage-contract.ts +0 -293
  57. package/src/store.ts +0 -113
  58. package/src/web-search.ts +0 -330
  59. package/src/wiki-compiler.ts +0 -711
  60. package/src/wiki-layout.ts +0 -251
  61. package/src/workspace.ts +0 -213
@@ -0,0 +1,127 @@
1
+ # Canonical Secrets Bootstrap Evidence: 2026-06-08
2
+
3
+ Scope: canonical Hasna XYZ app secret paths in account `hasna-xyz-infra`
4
+ (`789877399345`), region `us-east-1`, mirrored into the local `spark02`
5
+ `secrets` vault.
6
+
7
+ This note records names and migration intent only. It intentionally does not
8
+ include secret payloads, passwords, API keys, connection strings, or `.env`
9
+ contents.
10
+
11
+ ## Ownership Rule
12
+
13
+ App-owned runtime/config secrets use:
14
+
15
+ ```txt
16
+ hasna/xyz/{app_type}/{app}/prod/{component}
17
+ ```
18
+
19
+ Shared infra/admin pointers use an infra-owned path:
20
+
21
+ ```txt
22
+ hasna/xyz/infra/{resource_group}/prod/{component}/{role}
23
+ ```
24
+
25
+ Legacy master credentials copied for migration are suffixed with
26
+ `legacy-master`. They are deprecation inputs for migration jobs, not the clean
27
+ runtime secret names new app code should read.
28
+
29
+ ## Open Files
30
+
31
+ Created or verified in AWS Secrets Manager and the local vault:
32
+
33
+ ```txt
34
+ hasna/xyz/opensource/files/prod/env
35
+ hasna/xyz/opensource/files/prod/aws
36
+ hasna/xyz/opensource/files/prod/s3
37
+ hasna/xyz/opensource/files/prod/rds
38
+ ```
39
+
40
+ Meaning:
41
+
42
+ - `env`: app environment configuration.
43
+ - `aws`: AWS account/profile/region and related app config metadata.
44
+ - `s3`: canonical app storage bucket metadata for
45
+ `hasna-xyz-opensource-files-prod`.
46
+ - `rds`: app runtime database connection fields for database `files` and role
47
+ `files_app`.
48
+
49
+ The `aws` and `s3` entries are metadata-only. They do not contain access keys or
50
+ tokens.
51
+
52
+ ## Open Knowledge
53
+
54
+ Created or verified in AWS Secrets Manager and the local vault:
55
+
56
+ ```txt
57
+ hasna/xyz/opensource/knowledge/prod/env
58
+ hasna/xyz/opensource/knowledge/prod/aws
59
+ hasna/xyz/opensource/knowledge/prod/s3
60
+ ```
61
+
62
+ Meaning:
63
+
64
+ - `env`: open-knowledge production app config metadata.
65
+ - `aws`: AWS account/profile/region and related app config metadata.
66
+ - `s3`: canonical app storage bucket metadata for
67
+ `hasna-xyz-opensource-knowledge-prod`.
68
+
69
+ No `hasna/xyz/opensource/knowledge/prod/rds` secret was created in this pass.
70
+ The OSS package is local-first and currently uses SQLite for local knowledge
71
+ state. If a hosted wrapper later provisions an app database, the app-owned
72
+ runtime secret should use:
73
+
74
+ ```txt
75
+ hasna/xyz/opensource/knowledge/prod/rds
76
+ ```
77
+
78
+ ## Legacy Secret Mapping
79
+
80
+ Mapped legacy AWS Secrets Manager names to canonical ownership paths:
81
+
82
+ | Legacy name | Canonical path | Use |
83
+ | --- | --- | --- |
84
+ | `prod/microservice/rds/master` | `hasna/xyz/opensource/microservices/prod/rds/legacy-master` | Migration-only legacy master alias. |
85
+ | `prod/connect/rds/master` | `hasna/xyz/opensource/connectors/prod/rds/legacy-master` | Migration-only legacy master alias. |
86
+ | `internalapps/prod/rds/master` | `hasna/xyz/infra/apps/prod/postgres/legacy-internalapps-master` | Migration-only legacy shared/admin alias. |
87
+ | `internalapps/prod/iapp-news/env` | `hasna/xyz/internalapp/news/prod/env` | Canonical app env path for internalapp `news`. |
88
+
89
+ The three RDS aliases preserve old master credential payloads under explicit
90
+ legacy names so migration jobs can read them without relying on noncanonical
91
+ paths. They should not be used as the final runtime credentials for new app
92
+ code. Clean app runtime database credentials should be provisioned under
93
+ app-owned paths such as:
94
+
95
+ ```txt
96
+ hasna/xyz/opensource/files/prod/rds
97
+ hasna/xyz/internalapp/news/prod/rds
98
+ ```
99
+
100
+ The shared canonical Postgres admin pointer remains:
101
+
102
+ ```txt
103
+ hasna/xyz/infra/apps/prod/postgres/master
104
+ ```
105
+
106
+ ## Verification
107
+
108
+ AWS Secrets Manager name-only verification returned these canonical entries:
109
+
110
+ ```txt
111
+ hasna/xyz/infra/apps/prod/postgres/legacy-internalapps-master
112
+ hasna/xyz/infra/apps/prod/postgres/master
113
+ hasna/xyz/internalapp/news/prod/env
114
+ hasna/xyz/opensource/connectors/prod/rds/legacy-master
115
+ hasna/xyz/opensource/files/prod/aws
116
+ hasna/xyz/opensource/files/prod/env
117
+ hasna/xyz/opensource/files/prod/rds
118
+ hasna/xyz/opensource/files/prod/s3
119
+ hasna/xyz/opensource/knowledge/prod/aws
120
+ hasna/xyz/opensource/knowledge/prod/env
121
+ hasna/xyz/opensource/knowledge/prod/s3
122
+ hasna/xyz/opensource/microservices/prod/rds/legacy-master
123
+ ```
124
+
125
+ Local `secrets list` verification returned the same names with redacted values.
126
+
127
+ No secret values were printed during creation or verification.
package/package.json CHANGED
@@ -1,8 +1,16 @@
1
1
  {
2
2
  "name": "@hasna/knowledge",
3
- "version": "0.2.26",
3
+ "version": "0.2.28",
4
4
  "description": "Agent-friendly local knowledge CLI with JSON output, pagination, and safe destructive actions",
5
5
  "type": "module",
6
+ "exports": {
7
+ ".": {
8
+ "import": "./dist/index.js",
9
+ "types": "./dist/index.d.ts"
10
+ }
11
+ },
12
+ "main": "./dist/index.js",
13
+ "types": "./dist/index.d.ts",
6
14
  "bin": {
7
15
  "knowledge": "bin/open-knowledge.js",
8
16
  "open-knowledge": "bin/open-knowledge.js",
@@ -10,7 +18,7 @@
10
18
  },
11
19
  "files": [
12
20
  "bin",
13
- "src",
21
+ "dist",
14
22
  "docs",
15
23
  "LICENSE",
16
24
  "README.md"
@@ -18,9 +26,8 @@
18
26
  "scripts": {
19
27
  "test": "bun test",
20
28
  "test:cli": "bun test tests/cli.test.ts",
21
- "build": "bun build --target=bun --outfile=bin/open-knowledge.js --minify --external @aws-sdk/client-s3 --external @aws-sdk/credential-providers --external ai --external @ai-sdk/openai --external @ai-sdk/anthropic --external @ai-sdk/deepseek src/cli.ts && bun build --target=bun --outfile=bin/open-knowledge-mcp.js --external @modelcontextprotocol/sdk --external @aws-sdk/client-s3 --external @aws-sdk/credential-providers --external ai --external @ai-sdk/openai --external @ai-sdk/anthropic --external @ai-sdk/deepseek src/mcp.js",
22
- "prepublishOnly": "bun run build",
23
- "postinstall": "bun run build"
29
+ "build": "rm -rf dist && bun build --target=bun --outfile=bin/open-knowledge.js --minify --external @aws-sdk/client-s3 --external @aws-sdk/credential-providers --external ai --external @ai-sdk/openai --external @ai-sdk/anthropic --external @ai-sdk/deepseek src/cli.ts && bun build --target=bun --outfile=bin/open-knowledge-mcp.js --external @modelcontextprotocol/sdk --external @aws-sdk/client-s3 --external @aws-sdk/credential-providers --external ai --external @ai-sdk/openai --external @ai-sdk/anthropic --external @ai-sdk/deepseek src/mcp.js && bun build ./src/index.ts --outdir ./dist --target bun --external @aws-sdk/client-s3 --external @aws-sdk/credential-providers --external ai --external @ai-sdk/openai --external @ai-sdk/anthropic --external @ai-sdk/deepseek && bunx tsc -p tsconfig.build.json",
30
+ "prepublishOnly": "bun run build"
24
31
  },
25
32
  "keywords": [
26
33
  "knowledge",
@@ -49,12 +56,13 @@
49
56
  "node": ">=18"
50
57
  },
51
58
  "dependencies": {
52
- "@aws-sdk/client-s3": "^3.1063.0",
53
- "@aws-sdk/credential-providers": "^3.1063.0",
54
59
  "@ai-sdk/anthropic": "^3.0.81",
55
60
  "@ai-sdk/deepseek": "^2.0.35",
56
61
  "@ai-sdk/openai": "^3.0.68",
62
+ "@aws-sdk/client-s3": "^3.1063.0",
63
+ "@aws-sdk/credential-providers": "^3.1063.0",
57
64
  "@modelcontextprotocol/sdk": "^1.29.0",
65
+ "@types/json-schema": "^7.0.15",
58
66
  "ai": "^6.0.197",
59
67
  "zod": "^4.3.6"
60
68
  },
package/src/agent.ts DELETED
@@ -1,367 +0,0 @@
1
- import { randomUUID } from 'node:crypto';
2
- import { migrateKnowledgeDb, openKnowledgeDb } from './knowledge-db';
3
- import { languageModelFor, normalizeAiSdkUsage, parseModelRef, recordProviderUsage, resolveModelRef } from './providers';
4
- import { retrieveKnowledgeContext, type KnowledgeContextPack, type RetrievalOptions } from './retrieval';
5
- import type { KnowledgeConfig } from './workspace';
6
-
7
- export interface KnowledgePromptOptions extends Omit<RetrievalOptions, 'query'> {
8
- prompt: string;
9
- generate?: boolean;
10
- approveWrite?: boolean;
11
- now?: Date;
12
- }
13
-
14
- export interface KnowledgePromptResult {
15
- run_id: string;
16
- prompt: string;
17
- generated: boolean;
18
- provider: string;
19
- model: string;
20
- answer: string;
21
- context: KnowledgeContextPack;
22
- citations: KnowledgeContextPack['citations'];
23
- proposed_wiki_updates: Array<{
24
- kind: 'answer_note';
25
- title: string;
26
- citations: string[];
27
- requires_approval: boolean;
28
- }>;
29
- write_policy: {
30
- approved: boolean;
31
- durable_writes_performed: false;
32
- reason: string;
33
- };
34
- usage: {
35
- input_tokens: number;
36
- output_tokens: number;
37
- cost_usd: number;
38
- };
39
- warnings: string[];
40
- }
41
-
42
- function estimateTokens(text: string): number {
43
- const words = text.trim().split(/\s+/).filter(Boolean).length;
44
- return Math.max(1, Math.ceil(words * 1.25));
45
- }
46
-
47
- function citationLabel(index: number): string {
48
- return `C${index + 1}`;
49
- }
50
-
51
- function localAnswer(prompt: string, context: KnowledgeContextPack): string {
52
- if (context.excerpts.length === 0) {
53
- return `No indexed knowledge matched the prompt: ${prompt}`;
54
- }
55
- const lines = [
56
- `Found ${context.excerpts.length} relevant knowledge excerpt(s) for: ${prompt}`,
57
- '',
58
- ...context.excerpts.slice(0, 5).map((excerpt, index) => {
59
- const citation = context.citations.find((entry) => entry.id === excerpt.citation_id);
60
- const ref = citation?.source_ref ?? citation?.source_uri ?? citation?.artifact_path ?? citation?.artifact_uri ?? 'unknown source';
61
- return `[${citationLabel(index)}] ${excerpt.text} (${ref})`;
62
- }),
63
- ];
64
- return lines.join('\n');
65
- }
66
-
67
- function promptForModel(prompt: string, context: KnowledgeContextPack): string {
68
- const citations = context.citations.map((citation, index) => ({
69
- id: citationLabel(index),
70
- source_ref: citation.source_ref,
71
- source_uri: citation.source_uri,
72
- artifact_path: citation.artifact_path,
73
- revision: citation.revision,
74
- hash: citation.hash,
75
- quote: citation.quote,
76
- }));
77
- const excerpts = context.excerpts.map((excerpt, index) => ({
78
- id: citationLabel(index),
79
- kind: excerpt.kind,
80
- text: excerpt.text,
81
- score: excerpt.score,
82
- }));
83
- return [
84
- `Prompt: ${prompt}`,
85
- '',
86
- 'Use only the provided context. Cite claims with citation ids like [C1]. If context is insufficient, say what is missing.',
87
- '',
88
- `Context excerpts:\n${JSON.stringify(excerpts, null, 2)}`,
89
- '',
90
- `Citations:\n${JSON.stringify(citations, null, 2)}`,
91
- ].join('\n');
92
- }
93
-
94
- function proposedUpdates(prompt: string, context: KnowledgeContextPack): KnowledgePromptResult['proposed_wiki_updates'] {
95
- if (context.citations.length === 0) return [];
96
- return [{
97
- kind: 'answer_note',
98
- title: prompt.length > 80 ? `${prompt.slice(0, 77)}...` : prompt,
99
- citations: context.citations.map((citation) => citation.id),
100
- requires_approval: true,
101
- }];
102
- }
103
-
104
- function insertRun(dbPath: string, input: {
105
- runId: string;
106
- prompt: string;
107
- status: string;
108
- provider: string;
109
- model: string;
110
- metadata: Record<string, unknown>;
111
- now: string;
112
- }): void {
113
- const db = openKnowledgeDb(dbPath);
114
- try {
115
- db.run(
116
- `INSERT INTO runs (id, type, prompt, status, provider, model, metadata_json, created_at, updated_at)
117
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
118
- [
119
- input.runId,
120
- 'knowledge-prompt',
121
- input.prompt,
122
- input.status,
123
- input.provider,
124
- input.model,
125
- JSON.stringify(input.metadata),
126
- input.now,
127
- input.now,
128
- ],
129
- );
130
- } finally {
131
- db.close();
132
- }
133
- }
134
-
135
- function addRunEvent(dbPath: string, input: {
136
- runId: string;
137
- level: 'info' | 'warn' | 'error';
138
- event: string;
139
- metadata: Record<string, unknown>;
140
- now: string;
141
- }): void {
142
- const db = openKnowledgeDb(dbPath);
143
- try {
144
- db.run(
145
- `INSERT INTO run_events (id, run_id, level, event, metadata_json, created_at)
146
- VALUES (?, ?, ?, ?, ?, ?)`,
147
- [
148
- `evt_${randomUUID()}`,
149
- input.runId,
150
- input.level,
151
- input.event,
152
- JSON.stringify(input.metadata),
153
- input.now,
154
- ],
155
- );
156
- } finally {
157
- db.close();
158
- }
159
- }
160
-
161
- function updateRun(dbPath: string, input: {
162
- runId: string;
163
- status: string;
164
- provider: string;
165
- model: string;
166
- metadata: Record<string, unknown>;
167
- now: string;
168
- }): void {
169
- const db = openKnowledgeDb(dbPath);
170
- try {
171
- db.run(
172
- `UPDATE runs
173
- SET status = ?, provider = ?, model = ?, metadata_json = ?, updated_at = ?
174
- WHERE id = ?`,
175
- [
176
- input.status,
177
- input.provider,
178
- input.model,
179
- JSON.stringify(input.metadata),
180
- input.now,
181
- input.runId,
182
- ],
183
- );
184
- } finally {
185
- db.close();
186
- }
187
- }
188
-
189
- function recordUsage(dbPath: string, runId: string, usage: KnowledgePromptResult['usage'], provider: string, model: string, now: string, metadata: Record<string, unknown> = {}): void {
190
- const db = openKnowledgeDb(dbPath);
191
- try {
192
- recordProviderUsage(db, {
193
- run_id: runId,
194
- provider,
195
- model,
196
- input_tokens: usage.input_tokens,
197
- output_tokens: usage.output_tokens,
198
- cost_usd: usage.cost_usd,
199
- metadata,
200
- created_at: now,
201
- });
202
- } finally {
203
- db.close();
204
- }
205
- }
206
-
207
- export async function runKnowledgePrompt(options: KnowledgePromptOptions): Promise<KnowledgePromptResult> {
208
- const prompt = options.prompt.trim();
209
- if (!prompt) throw new Error('Knowledge prompt is required.');
210
- const now = (options.now ?? new Date()).toISOString();
211
- const runId = `run_${randomUUID()}`;
212
- const modelRef = resolveModelRef(options.modelRef ?? 'default', options.config);
213
- const parsed = parseModelRef(modelRef);
214
-
215
- migrateKnowledgeDb(options.dbPath);
216
- insertRun(options.dbPath, {
217
- runId,
218
- prompt,
219
- status: options.generate ? 'running' : 'dry_run',
220
- provider: options.generate ? parsed.provider : 'local',
221
- model: options.generate ? parsed.model : 'context-draft',
222
- metadata: {
223
- semantic: options.semantic === true || options.fake === true || Boolean(options.modelRef),
224
- approve_write: options.approveWrite === true,
225
- generated: options.generate === true,
226
- },
227
- now,
228
- });
229
-
230
- const { prompt: _prompt, generate: _generate, approveWrite: _approveWrite, now: _now, ...retrievalOptions } = options;
231
- const context = await retrieveKnowledgeContext({
232
- ...retrievalOptions,
233
- query: prompt,
234
- });
235
- addRunEvent(options.dbPath, {
236
- runId,
237
- level: 'info',
238
- event: 'context_retrieved',
239
- metadata: {
240
- results: context.results.length,
241
- citations: context.citations.length,
242
- warnings: context.warnings,
243
- },
244
- now,
245
- });
246
-
247
- let answer = localAnswer(prompt, context);
248
- let generated = false;
249
- let provider = 'local';
250
- let model = 'context-draft';
251
- let usage = {
252
- input_tokens: estimateTokens(prompt) + context.excerpts.reduce((sum, excerpt) => sum + estimateTokens(excerpt.text), 0),
253
- output_tokens: estimateTokens(answer),
254
- cost_usd: 0,
255
- };
256
- const warnings = [...context.warnings];
257
-
258
- if (options.generate) {
259
- try {
260
- if (options.fake) {
261
- generated = true;
262
- provider = parsed.provider;
263
- model = parsed.model;
264
- answer = `Fake generated answer for: ${prompt}\n\n${answer}`;
265
- } else {
266
- const { generateText } = await import('ai');
267
- const languageModel = await languageModelFor(modelRef, {
268
- config: options.config,
269
- env: options.env,
270
- });
271
- const result = await generateText({
272
- model: languageModel as never,
273
- system: 'You answer company knowledge-base prompts using only provided context and citation ids.',
274
- prompt: promptForModel(prompt, context),
275
- });
276
- generated = true;
277
- provider = parsed.provider;
278
- model = parsed.model;
279
- answer = result.text;
280
- const normalized = normalizeAiSdkUsage({
281
- provider,
282
- model,
283
- usage: result.usage as Record<string, unknown> | undefined,
284
- providerMetadata: result.providerMetadata as Record<string, unknown> | undefined,
285
- });
286
- usage = {
287
- input_tokens: normalized.input_tokens,
288
- output_tokens: normalized.output_tokens,
289
- cost_usd: normalized.cost_usd,
290
- };
291
- }
292
- } catch (error) {
293
- addRunEvent(options.dbPath, {
294
- runId,
295
- level: 'error',
296
- event: 'answer_generation_failed',
297
- metadata: { message: error instanceof Error ? error.message : String(error) },
298
- now,
299
- });
300
- updateRun(options.dbPath, {
301
- runId,
302
- status: 'failed',
303
- provider: parsed.provider,
304
- model: parsed.model,
305
- metadata: {
306
- generated: false,
307
- error: error instanceof Error ? error.message : String(error),
308
- },
309
- now,
310
- });
311
- throw error;
312
- }
313
- }
314
-
315
- const updates = proposedUpdates(prompt, context);
316
- const writePolicy = {
317
- approved: options.approveWrite === true,
318
- durable_writes_performed: false as const,
319
- reason: options.approveWrite
320
- ? 'Approval flag recorded; durable wiki writing is deferred to the wiki compile task.'
321
- : 'Dry-run mode: proposed wiki updates require approval before durable writes.',
322
- };
323
- addRunEvent(options.dbPath, {
324
- runId,
325
- level: 'info',
326
- event: generated ? 'answer_generated' : 'answer_drafted',
327
- metadata: {
328
- provider,
329
- model,
330
- proposed_updates: updates.length,
331
- durable_writes_performed: false,
332
- },
333
- now,
334
- });
335
- recordUsage(options.dbPath, runId, usage, provider, model, now, {
336
- generated,
337
- citations: context.citations.length,
338
- });
339
- updateRun(options.dbPath, {
340
- runId,
341
- status: generated ? 'completed' : 'dry_run',
342
- provider,
343
- model,
344
- metadata: {
345
- generated,
346
- citations: context.citations.length,
347
- proposed_updates: updates.length,
348
- approve_write: options.approveWrite === true,
349
- },
350
- now,
351
- });
352
-
353
- return {
354
- run_id: runId,
355
- prompt,
356
- generated,
357
- provider,
358
- model,
359
- answer,
360
- context,
361
- citations: context.citations,
362
- proposed_wiki_updates: updates,
363
- write_policy: writePolicy,
364
- usage,
365
- warnings,
366
- };
367
- }