dsh-openwrite 0.2.19 → 0.2.21
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/README.md +23 -6
- package/docs/INSTALL.md +6 -0
- package/docs/ISSUE_76_REPAIRS.md +61 -0
- package/docs/RELEASE_ACCEPTANCE.md +14 -1
- package/package.json +1 -1
- package/packages/openwrite-bridge/lib/client.d.ts +1 -0
- package/packages/openwrite-bridge/lib/client.js +26 -4
- package/packages/openwrite-bridge/lib/contracts-generated.d.ts +1 -1
- package/packages/openwrite-bridge/lib/contracts-generated.js +1 -2
- package/packages/openwrite-bridge/lib/domain.js +2 -0
- package/packages/openwrite-bridge/lib/index.js +6 -6
- package/packages/openwrite-bridge/lib/managed-runtime.js +37 -37
- package/packages/openwrite-bridge/lib/tools.js +67 -31
- package/packages/studio-panel/lib/client.js +493 -484
- package/packages/studio-panel/lib/client.js.map +1 -1
- package/presets/openwrite/agent.cordis.yml +10 -2
- package/presets/openwrite/skills/canon/SKILL.md +2 -0
- package/presets/openwrite/skills/dialoguequality/SKILL.md +2 -0
- package/presets/openwrite/skills/dog-delivery-query/SKILL.md +2 -0
- package/presets/openwrite/skills/dog-import-query/SKILL.md +2 -0
- package/presets/openwrite/skills/dog-review-query/SKILL.md +2 -0
- package/presets/openwrite/skills/export-book/SKILL.md +2 -0
- package/presets/openwrite/skills/foreshadow/SKILL.md +2 -0
- package/presets/openwrite/skills/foreshadowing-system/SKILL.md +2 -0
- package/presets/openwrite/skills/learn/SKILL.md +2 -0
- package/presets/openwrite/skills/novel-creator/SKILL.md +2 -0
- package/presets/openwrite/skills/novel-reviewer/SKILL.md +2 -0
- package/presets/openwrite/skills/oh-story-deslop/SKILL.md +2 -0
- package/presets/openwrite/skills/oh-story-long-analyze/SKILL.md +2 -0
- package/presets/openwrite/skills/oh-story-long-scan/SKILL.md +2 -0
- package/presets/openwrite/skills/oh-story-long-write/SKILL.md +2 -0
- package/presets/openwrite/skills/oh-story-review/SKILL.md +2 -0
- package/presets/openwrite/skills/progress/SKILL.md +2 -0
- package/presets/openwrite/skills/review-chapter/SKILL.md +2 -0
- package/presets/openwrite/skills/revise-span/SKILL.md +2 -0
- package/presets/openwrite/skills/style-system/SKILL.md +2 -0
- package/presets/openwrite/skills/truth-validation/SKILL.md +2 -0
- package/presets/openwrite/skills/workflow-manager/SKILL.md +2 -0
- package/presets/openwrite/skills/world-query/SKILL.md +2 -0
- package/presets/openwrite/skills/write-next/SKILL.md +2 -0
- package/release/{openwrite-5.8.9-py3-none-any.whl → openwrite-5.8.10-py3-none-any.whl} +0 -0
- package/release/runtime-manifest.json +5 -5
- package/release/source.json +3 -3
|
@@ -124,7 +124,18 @@ function defineTool(options) {
|
|
|
124
124
|
const violations = validate(normalized);
|
|
125
125
|
if (violations.length > 0)
|
|
126
126
|
throw new ToolArgsError(violations);
|
|
127
|
-
|
|
127
|
+
try {
|
|
128
|
+
return await userExecute(normalized, exec);
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
if (!(error instanceof StudioError) || error.code.startsWith('WORKSPACE_'))
|
|
132
|
+
throw error;
|
|
133
|
+
error.message = JSON.stringify({ code: error.code, message: error.message,
|
|
134
|
+
status: error.status, details: error.details, request_id: error.requestId,
|
|
135
|
+
recovery: error.code === 'NETWORK_ERROR' ? 'List recent tasks and chapter runs before resubmitting; the server may have accepted the request.' : null,
|
|
136
|
+
});
|
|
137
|
+
throw error;
|
|
138
|
+
}
|
|
128
139
|
},
|
|
129
140
|
};
|
|
130
141
|
if (userFinalizeContent)
|
|
@@ -518,9 +529,14 @@ export function registerNovelTools(ctx, clientFactory, options) {
|
|
|
518
529
|
'rejects the edit with a conflict if the outline changed in between. ' +
|
|
519
530
|
'Operations: "rename" (node_id + title), "update_summary" (node_id + summary), ' +
|
|
520
531
|
'"add_child" (node_id = parent, + title/summary/kind), "add_after" (node_id = sibling, + title/summary/kind), ' +
|
|
521
|
-
'"delete" (node_id)
|
|
522
|
-
|
|
523
|
-
|
|
532
|
+
'"delete" (node_id, cascade=false keeps children), "promote" (remove a container), and ' +
|
|
533
|
+
'"get_meta"/"set_meta" (chapter metadata, optionally updates[] for a batch).',
|
|
534
|
+
parameters: {
|
|
535
|
+
operation: { type: 'string', required: true, enum: ['rename', 'update_summary', 'add_child', 'add_after', 'delete', 'promote', 'get_meta', 'set_meta'], description: 'The structural operation to apply.' },
|
|
536
|
+
cascade: { type: 'boolean', description: 'For delete, false removes only the container and keeps children and chapter IDs.' },
|
|
537
|
+
metadata: { type: 'json', description: 'Field object for set_meta; use target_words, beats, hooks or characters.' },
|
|
538
|
+
chapter_id: { type: 'string', description: 'Chapter ID alias for node_id in get_meta/set_meta.' },
|
|
539
|
+
updates: { type: 'json', description: 'Batch of 1-50 {chapter_id, metadata} objects for set_meta, or {chapter_id} for get_meta; one revision guards the whole batch.' },
|
|
524
540
|
revision: { type: 'string', required: true, description: 'The outline revision fingerprint from a fresh novel_outline_read call.' },
|
|
525
541
|
node_id: { type: 'string', description: 'Target node id (parent for add_child, sibling for add_after); not needed for add_child at root.' },
|
|
526
542
|
title: { type: 'string', description: 'Node title (rename / add_child / add_after).' },
|
|
@@ -538,6 +554,14 @@ export function registerNovelTools(ctx, clientFactory, options) {
|
|
|
538
554
|
body['title'] = args.title;
|
|
539
555
|
if (args.summary !== undefined)
|
|
540
556
|
body['summary'] = args.summary;
|
|
557
|
+
if (args.cascade !== undefined)
|
|
558
|
+
body['cascade'] = args.cascade;
|
|
559
|
+
if (args.metadata !== undefined)
|
|
560
|
+
body['metadata'] = args.metadata;
|
|
561
|
+
if (args.chapter_id !== undefined)
|
|
562
|
+
body['chapter_id'] = args.chapter_id;
|
|
563
|
+
if (args.updates !== undefined)
|
|
564
|
+
body['updates'] = args.updates;
|
|
541
565
|
if (args.kind !== undefined)
|
|
542
566
|
body['kind'] = args.kind;
|
|
543
567
|
return await client.postJson('/api/outline/edit', body, exec.signal);
|
|
@@ -671,11 +695,13 @@ export function registerNovelTools(ctx, clientFactory, options) {
|
|
|
671
695
|
}));
|
|
672
696
|
ctx.tools.register(defineTool({
|
|
673
697
|
name: 'novel_write_chapter',
|
|
674
|
-
description: '
|
|
698
|
+
description: 'Queue a chapter write and immediately return a durable task_id. Poll novel_task_get for progress/result. Omit chapter_id to write ' +
|
|
675
699
|
'the outline-recommended next chapter; a given chapter_id must match the current recommendation. ' +
|
|
676
700
|
'Preview context first with novel_context_preview and consult novel_outline_read for the recommendation.',
|
|
677
701
|
parameters: {
|
|
678
|
-
|
|
702
|
+
run_id_v2: { type: 'string', description: 'Resume this completed draft without generating it again.' },
|
|
703
|
+
resume_from_draft: { type: 'boolean', description: 'Resume the latest uncommitted draft for this chapter.' },
|
|
704
|
+
target_words: { type: 'integer', description: 'Target word count, 200-50000. When omitted use the outline target, then default 3000.' },
|
|
679
705
|
guidance: { type: 'string', description: 'Writing guidance for this chapter (plot beats, tone, POV notes).' },
|
|
680
706
|
chapter_id: { type: 'string', description: 'Chapter id to write; must equal the current outline recommendation. Omit for the next chapter.' },
|
|
681
707
|
temperature: { type: 'number', description: 'Sampling temperature (default 0.7).' },
|
|
@@ -685,11 +711,17 @@ export function registerNovelTools(ctx, clientFactory, options) {
|
|
|
685
711
|
timeoutMs,
|
|
686
712
|
async execute(args, exec) {
|
|
687
713
|
const client = clientFor(exec);
|
|
688
|
-
const targetWords = args.target_words
|
|
689
|
-
if (!Number.isInteger(targetWords) || targetWords < 200 || targetWords >
|
|
690
|
-
throw new Error(`target_words must be an integer between 200 and
|
|
714
|
+
const targetWords = args.target_words;
|
|
715
|
+
if (targetWords !== undefined && (!Number.isInteger(targetWords) || targetWords < 200 || targetWords > 50000)) {
|
|
716
|
+
throw new Error(`target_words must be an integer between 200 and 50000, got ${String(args.target_words)}`);
|
|
691
717
|
}
|
|
692
|
-
const body = {
|
|
718
|
+
const body = { guidance: args.guidance ?? '' };
|
|
719
|
+
if (targetWords !== undefined)
|
|
720
|
+
body['target_words'] = targetWords;
|
|
721
|
+
if (args.run_id_v2 !== undefined)
|
|
722
|
+
body['run_id_v2'] = args.run_id_v2;
|
|
723
|
+
if (args.resume_from_draft !== undefined)
|
|
724
|
+
body['resume_from_draft'] = args.resume_from_draft;
|
|
693
725
|
const chapterId = args.chapter_id?.trim();
|
|
694
726
|
if (chapterId)
|
|
695
727
|
body['chapter_id'] = chapterId;
|
|
@@ -697,17 +729,7 @@ export function registerNovelTools(ctx, clientFactory, options) {
|
|
|
697
729
|
body['temperature'] = args.temperature;
|
|
698
730
|
if (args.outline_revision !== undefined)
|
|
699
731
|
body['outline_revision'] = args.outline_revision;
|
|
700
|
-
|
|
701
|
-
const writeValue = objectValue(response);
|
|
702
|
-
const writeResult = objectValue(writeValue['result'] ?? response);
|
|
703
|
-
const writtenChapterId = chapterId || String(writeResult['chapter_id'] ?? '');
|
|
704
|
-
try {
|
|
705
|
-
const dogDelivery = await deliveryForResponse(client, response, writtenChapterId, exec.signal);
|
|
706
|
-
return { ...objectValue(response), dog_delivery: dogDelivery };
|
|
707
|
-
}
|
|
708
|
-
catch (error) {
|
|
709
|
-
return { ...objectValue(response), dog_delivery: { status: 'unavailable', error: error instanceof Error ? error.message : String(error) } };
|
|
710
|
-
}
|
|
732
|
+
return await client.postJson('/api/tasks', { type: 'chapter_write', input: body }, exec.signal);
|
|
711
733
|
},
|
|
712
734
|
}));
|
|
713
735
|
ctx.tools.register(defineTool({
|
|
@@ -715,6 +737,8 @@ export function registerNovelTools(ctx, clientFactory, options) {
|
|
|
715
737
|
description: 'Run OpenWrite\'s multi-dimensional review on a drafted manuscript chapter (LONG-RUNNING). ' +
|
|
716
738
|
'Pass either the manuscript path (e.g. "data/manuscript/ch_0007.md") or a chapter_id like "ch_0007".',
|
|
717
739
|
parameters: {
|
|
740
|
+
action: { type: 'string', enum: ['review', 'versions', 'compare'], description: 'Default review; versions lists saved reviews, compare compares a historical revision with the current review.' },
|
|
741
|
+
revision: { type: 'string', description: 'Historical review revision for compare.' },
|
|
718
742
|
path: { type: 'string', description: 'Manuscript path relative to the novel data root. Takes precedence over chapter_id.' },
|
|
719
743
|
chapter_id: { type: 'string', description: 'Chapter id (ch_<digits>), resolved client-side to data/manuscript/<id>.md.' },
|
|
720
744
|
strict: { type: 'boolean', description: 'Enable strict review mode (default false).' },
|
|
@@ -725,6 +749,8 @@ export function registerNovelTools(ctx, clientFactory, options) {
|
|
|
725
749
|
timeoutMs,
|
|
726
750
|
async execute(args, exec) {
|
|
727
751
|
const client = clientFor(exec);
|
|
752
|
+
if (args.action === 'versions' || args.action === 'compare')
|
|
753
|
+
return await client.postJson('/api/review/history', { action: args.action, chapter_id: reviewChapterId(args), revision: args.revision ?? '' }, exec.signal);
|
|
728
754
|
if (args.dimensions !== undefined && args.dimensions.some(value => !Number.isInteger(value) || value < 1 || value > 37)) {
|
|
729
755
|
throw new Error('dimensions must contain only integers from 1 to 37');
|
|
730
756
|
}
|
|
@@ -850,11 +876,12 @@ export function registerNovelTools(ctx, clientFactory, options) {
|
|
|
850
876
|
name: 'novel_document_change_plan',
|
|
851
877
|
description: 'Stage and review revision-gated changes to an existing novel document. Use action "preview" with path and edits ' +
|
|
852
878
|
'to receive an immutable preview_token and real before/after summary. Use "apply" only after author acceptance, ' +
|
|
853
|
-
'passing
|
|
879
|
+
'passing that token and confirm: true; "reject" discards it, "retry" rebuilds it against the current source, and "undo" safely ' +
|
|
854
880
|
'applies an undo_preview_token returned by a prior apply. Each edit uses old_text/new_text or unique start_text/' +
|
|
855
881
|
'end_text/new_text anchors. The server rechecks the source revision and writes exactly the stored preview.',
|
|
856
882
|
parameters: {
|
|
857
883
|
action: { type: 'string', required: true, enum: ['preview', 'apply', 'reject', 'retry', 'undo'], description: 'Plan lifecycle action.' },
|
|
884
|
+
confirm: { type: 'boolean', description: 'Required true for apply and undo after author acceptance.' },
|
|
858
885
|
path: { type: 'string', description: 'Existing document path for preview, relative to the novel root.' },
|
|
859
886
|
edits: { type: 'json', description: 'Preview edits: [{old_text,new_text}] or [{start_text,end_text,new_text}].' },
|
|
860
887
|
preview_token: { type: 'string', description: 'Immutable preview or undo token for apply/reject/retry/undo.' },
|
|
@@ -862,15 +889,19 @@ export function registerNovelTools(ctx, clientFactory, options) {
|
|
|
862
889
|
output: { schema: JSON_OUTPUT_SCHEMA, render: (_args, value) => renderJson(value) },
|
|
863
890
|
timeoutMs,
|
|
864
891
|
async execute(args, exec) {
|
|
865
|
-
if (args.action === 'preview' && (!Array.isArray(args.edits) || args.edits.length
|
|
866
|
-
throw new Error('preview requires
|
|
892
|
+
if (args.action === 'preview' && (!Array.isArray(args.edits) || (args.edits.length < 1 || args.edits.length > 50))) {
|
|
893
|
+
throw new Error('preview requires between 1 and 50 edits ' +
|
|
867
894
|
`(received ${describeValue(args.edits)})`);
|
|
868
895
|
}
|
|
869
896
|
if (args.action !== 'preview' && !args.preview_token?.trim()) {
|
|
870
897
|
throw new Error(`${args.action} requires preview_token`);
|
|
871
898
|
}
|
|
899
|
+
if (['apply', 'undo'].includes(args.action) && args.confirm !== true)
|
|
900
|
+
throw new Error('apply/undo requires confirm: true');
|
|
872
901
|
const client = clientFor(exec);
|
|
873
902
|
const body = { action: args.action };
|
|
903
|
+
if (args.confirm !== undefined)
|
|
904
|
+
body['confirm'] = args.confirm;
|
|
874
905
|
if (args.path !== undefined)
|
|
875
906
|
body['path'] = args.path;
|
|
876
907
|
if (args.edits !== undefined)
|
|
@@ -1319,6 +1350,7 @@ export function registerNovelTools(ctx, clientFactory, options) {
|
|
|
1319
1350
|
name: 'novel_tasks_list',
|
|
1320
1351
|
description: 'List background tasks (queued/running/finished AI jobs) with per-status counts.',
|
|
1321
1352
|
parameters: {
|
|
1353
|
+
since: { type: 'string', description: 'ISO timestamp filter for requests whose response was lost.' },
|
|
1322
1354
|
limit: { type: 'integer', description: 'Maximum number of tasks (server default 100).' },
|
|
1323
1355
|
},
|
|
1324
1356
|
output: { schema: JSON_OUTPUT_SCHEMA, render: (_args, value) => renderJson(value) },
|
|
@@ -1327,6 +1359,8 @@ export function registerNovelTools(ctx, clientFactory, options) {
|
|
|
1327
1359
|
async execute(args, exec) {
|
|
1328
1360
|
const client = clientFor(exec);
|
|
1329
1361
|
const params = {};
|
|
1362
|
+
if (args.since !== undefined)
|
|
1363
|
+
params['since'] = args.since;
|
|
1330
1364
|
if (args.limit !== undefined)
|
|
1331
1365
|
params['limit'] = String(args.limit);
|
|
1332
1366
|
return await client.getJson('/api/tasks', params, exec.signal);
|
|
@@ -1430,8 +1464,9 @@ export function registerNovelTools(ctx, clientFactory, options) {
|
|
|
1430
1464
|
}));
|
|
1431
1465
|
ctx.tools.register(defineTool({
|
|
1432
1466
|
name: 'novel_task_cancel',
|
|
1433
|
-
description: '
|
|
1467
|
+
description: 'Request cancellation of a queued or running task. Check cancelled and cancellation_status for the actual outcome; poll novel_task_get while cancellation is requested.',
|
|
1434
1468
|
parameters: {
|
|
1469
|
+
wait_seconds: { type: 'number', description: 'Wait up to 30 seconds for actual cancellation; running model calls may finish later.' },
|
|
1435
1470
|
task_id: { type: 'string', required: true, description: 'Task id (tsk_*).' },
|
|
1436
1471
|
},
|
|
1437
1472
|
output: { schema: JSON_OUTPUT_SCHEMA, render: (_args, value) => renderJson(value) },
|
|
@@ -1440,7 +1475,7 @@ export function registerNovelTools(ctx, clientFactory, options) {
|
|
|
1440
1475
|
const client = clientFor(exec);
|
|
1441
1476
|
if (!/^tsk_[A-Za-z0-9_-]+$/.test(args.task_id))
|
|
1442
1477
|
throw new Error(`invalid task_id "${args.task_id}"`);
|
|
1443
|
-
return await client.postJson(`/api/tasks/${encodeURIComponent(args.task_id)}/cancel`, {}, exec.signal);
|
|
1478
|
+
return await client.postJson(`/api/tasks/${encodeURIComponent(args.task_id)}/cancel`, { wait_seconds: args.wait_seconds ?? 0 }, exec.signal);
|
|
1444
1479
|
},
|
|
1445
1480
|
}));
|
|
1446
1481
|
ctx.tools.register(defineTool({
|
|
@@ -1910,7 +1945,7 @@ export function registerNovelTools(ctx, clientFactory, options) {
|
|
|
1910
1945
|
ctx.tools.register(defineTool({
|
|
1911
1946
|
name: 'novel_writing_targets',
|
|
1912
1947
|
description: 'Immediate compatibility write for targets the author has already authorized exactly; otherwise use ' +
|
|
1913
|
-
'novel_structured_change_plan. Update the project writing targets. Ranges: book_words 10000-5000000, chapter_words 200-
|
|
1948
|
+
'novel_structured_change_plan. Update the project writing targets. Ranges: book_words 10000-5000000, chapter_words 200-50000, ' +
|
|
1914
1949
|
'outline_volume_words 100-5000, outline_act_words 80-3000, outline_section_words 50-2000, ' +
|
|
1915
1950
|
'outline_chapter_words 30-1000. Omitted fields keep their current values.',
|
|
1916
1951
|
parameters: {
|
|
@@ -1983,11 +2018,12 @@ export function registerNovelTools(ctx, clientFactory, options) {
|
|
|
1983
2018
|
'confirm: true), "cancel" (run_id + revision, optional reason). All mutating actions are revision-gated: ' +
|
|
1984
2019
|
'pass the run\'s current revision from a list/get call.',
|
|
1985
2020
|
parameters: {
|
|
1986
|
-
action: { type: 'string', required: true, enum: ['list', 'get', 'record_intervention', 'update_intervention', 'cancel'], description: 'The chapter-run operation.' },
|
|
2021
|
+
action: { type: 'string', required: true, enum: ['list', 'get', 'record_intervention', 'update_intervention', 'cancel', 'archive'], description: 'The chapter-run operation.' },
|
|
1987
2022
|
run_id: { type: 'string', description: 'Run id (required for get/record_intervention/update_intervention/cancel).' },
|
|
1988
2023
|
revision: { type: 'string', description: 'Current run revision (required for mutating actions).' },
|
|
1989
2024
|
chapter_id: { type: 'string', description: 'Chapter filter for list.' },
|
|
1990
2025
|
statuses: { type: 'array', items: { type: 'string' }, description: 'Status filter for list.' },
|
|
2026
|
+
since: { type: 'string', description: 'ISO timestamp filter for recent runs.' },
|
|
1991
2027
|
limit: { type: 'integer', description: 'List limit (default 20).' },
|
|
1992
2028
|
scope: { type: 'string', description: 'Intervention scope (default "chapter").' },
|
|
1993
2029
|
risk: { type: 'string', description: 'Intervention risk level (default "medium").' },
|
|
@@ -2008,7 +2044,7 @@ export function registerNovelTools(ctx, clientFactory, options) {
|
|
|
2008
2044
|
async execute(args, exec) {
|
|
2009
2045
|
const client = clientFor(exec);
|
|
2010
2046
|
const body = { action: args.action };
|
|
2011
|
-
for (const key of ['run_id', 'revision', 'chapter_id', 'statuses', 'limit', 'scope', 'risk', 'request', 'affected_items', 'rewrite_required', 'intervention_id', 'state', 'facts_revision', 'impact', 'proposal', 'confirm', 'reason']) {
|
|
2047
|
+
for (const key of ['run_id', 'revision', 'chapter_id', 'statuses', 'since', 'limit', 'scope', 'risk', 'request', 'affected_items', 'rewrite_required', 'intervention_id', 'state', 'facts_revision', 'impact', 'proposal', 'confirm', 'reason']) {
|
|
2012
2048
|
const value = args[key];
|
|
2013
2049
|
if (value !== undefined)
|
|
2014
2050
|
body[key] = value;
|
|
@@ -2122,11 +2158,11 @@ export function registerNovelTools(ctx, clientFactory, options) {
|
|
|
2122
2158
|
name: 'novel_manuscript_acceptance_reconcile',
|
|
2123
2159
|
description: 'Reconcile the canonical manuscript acceptance ledger. Actions: "baseline" (accept current text as the ' +
|
|
2124
2160
|
'initial baseline), "external" (accept a detected external edit), "resume" (continue a durable incomplete ' +
|
|
2125
|
-
'operation), and "acknowledge" (record that the author reviewed the completed acceptance and impacts). ' +
|
|
2161
|
+
'operation), "abandon" (release a stale operation without deleting prose), and "acknowledge" (record that the author reviewed the completed acceptance and impacts). ' +
|
|
2126
2162
|
'Each action is mapped to the corresponding OpenWrite acceptance endpoint. Baseline, external, and ' +
|
|
2127
2163
|
'acknowledge are confirmation-gated by the server.',
|
|
2128
2164
|
parameters: {
|
|
2129
|
-
action: { type: 'string', required: true, enum: ['baseline', 'external', 'resume', 'acknowledge'], description: 'The reconciliation action.' },
|
|
2165
|
+
action: { type: 'string', required: true, enum: ['baseline', 'external', 'resume', 'acknowledge', 'abandon'], description: 'The reconciliation action.' },
|
|
2130
2166
|
chapter_id: { type: 'string', description: 'Optional chapter id (ch_<digits>); omit for the project-level operation.' },
|
|
2131
2167
|
operation_id: { type: 'string', description: 'Durable operation id to resume or acknowledge.' },
|
|
2132
2168
|
domains: { type: 'array', items: { type: 'string' }, description: 'Affected domains to acknowledge or retry; acknowledge defaults to outline and foreshadowing.' },
|