dsh-openwrite 0.2.27 → 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.
- package/README.md +8 -0
- package/docs/CLOSED_ISSUE_FOLLOWUPS.md +5 -3
- package/docs/CONTEXT_SLOTS.md +60 -0
- package/docs/EDITOR_SAFETY_REPAIRS.md +17 -0
- package/docs/ISSUES_121_123.md +97 -0
- package/docs/ISSUES_98_100.md +2 -0
- package/docs/ISSUE_123_FOLLOWUP.md +54 -0
- package/docs/RELEASE_ACCEPTANCE.md +73 -3
- package/docs/SELECTION_UPGRADE_REPAIRS.md +16 -0
- package/docs/TOOLS.md +1356 -0
- package/dog-tools.mjs +5 -3
- package/package.json +5 -4
- package/packages/openwrite-bridge/lib/dog-delivery.js +7 -3
- package/packages/openwrite-bridge/lib/managed-runtime.js +3 -1
- package/packages/openwrite-bridge/lib/tool-policy.js +2 -0
- package/packages/openwrite-bridge/lib/tools.js +28 -5
- package/packages/studio-panel/lib/client.js +760 -539
- package/packages/studio-panel/lib/client.js.map +1 -1
- package/packages/studio-panel/lib/types/client/VditorBody.d.ts +7 -1
- package/packages/studio-panel/lib/types/client/locales.d.ts +1 -1
- package/packages/studio-panel/lib/types/client/manuscript-annotations.d.ts +2 -1
- package/packages/studio-panel/lib/types/client/manuscript-overlay.d.ts +2 -1
- package/packages/studio-panel/lib/types/client/manuscript-selection.d.ts +18 -5
- package/packages/studio-panel/lib/types/client/selection-coordinates.d.ts +15 -0
- package/preset-definition.mjs +1 -1
- package/release/{openwrite-5.8.16-py3-none-any.whl → openwrite-5.8.20-py3-none-any.whl} +0 -0
- package/release/runtime-manifest.json +6 -5
- package/release/source.json +3 -3
- package/scripts/safe-upgrade.mjs +104 -9
- package/vendor/dsh-dog/lib/{core-BAlgZpLq.js → core--byUPJL8.js} +129 -12
- package/vendor/dsh-dog/lib/core.js +1 -1
- package/vendor/dsh-dog/lib/index.js +54 -103
package/dog-tools.mjs
CHANGED
|
@@ -4,8 +4,10 @@ export function apply(ctx) {
|
|
|
4
4
|
for (const tool of ctx.openwriteDog.tools) ctx.effect(() => ctx.tools.register(tool))
|
|
5
5
|
ctx.effect(() => ctx.tools.guard(execution => {
|
|
6
6
|
if (!execution.name.startsWith('dog_') || ['dog_status', 'dog_wait', 'dog_validate'].includes(execution.name)) return
|
|
7
|
-
if (!execution.agent) return 'DoG requires an Agent session'
|
|
8
|
-
if (execution.agent.ctx.get('planMode')?.get(execution.agent)
|
|
9
|
-
|
|
7
|
+
if (!execution.agent) return 'DOG_AGENT_SESSION_REQUIRED: DoG execution requires an Agent session; run this tool inside a writing Agent session.'
|
|
8
|
+
if (execution.agent.ctx.get('planMode')?.get(execution.agent)?.active) return 'DOG_PLAN_MODE: DoG execution is disabled in plan mode; leave plan mode before running it.'
|
|
9
|
+
const policy = ctx.get('sandboxPolicy')
|
|
10
|
+
if (!policy) return 'DOG_SANDBOX_POLICY_UNAVAILABLE: The host sandbox policy service is unavailable; check the host configuration.'
|
|
11
|
+
if (policy.resolve({ session: execution.agent.session }).mode === 'read-only') return 'DOG_READ_ONLY: DoG execution requires a writable session; the current sandbox is read-only.'
|
|
10
12
|
}))
|
|
11
13
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-openwrite",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.28",
|
|
4
4
|
"description": "OpenWrite 长篇小说领域后端接入 DeepSeek Harness:统一创作 Agent 预设 + 原生创作工作台",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -14,14 +14,15 @@
|
|
|
14
14
|
"install-dsh": "bash scripts/install.sh",
|
|
15
15
|
"dev": "bash scripts/dev.sh",
|
|
16
16
|
"doctor": "node scripts/plugin-doctor.mjs",
|
|
17
|
-
"test:maintenance": "node --test scripts/plugin-doctor.test.mjs scripts/lifecycle.test.mjs packages/openwrite-bridge/scripts/runtime-lifecycle.test.mjs scripts/maintenance.test.mjs scripts/preset-lifecycle.test.mjs scripts/safe-upgrade.test.mjs",
|
|
17
|
+
"test:maintenance": "node --test scripts/plugin-doctor.test.mjs scripts/lifecycle.test.mjs packages/openwrite-bridge/scripts/runtime-lifecycle.test.mjs scripts/maintenance.test.mjs scripts/preset-lifecycle.test.mjs scripts/safe-upgrade.test.mjs scripts/dog-tools.test.mjs",
|
|
18
18
|
"check": "npm run check:plugin && npm run test:contracts",
|
|
19
19
|
"test:lifecycle": "node --test scripts/lifecycle.test.mjs",
|
|
20
20
|
"test:runtime": "node --test packages/openwrite-bridge/scripts/runtime-lifecycle.test.mjs",
|
|
21
|
-
"check:plugin": "npm run build && npm run doctor && npm run test:maintenance && npm run test:profiles && npm run test:preset && npm --prefix packages/openwrite-bridge run smoke && npm --prefix packages/studio-panel run smoke && npm run test:epochs && npm run test:components",
|
|
21
|
+
"check:plugin": "npm run build && npm run docs:check && npm run doctor && npm run test:maintenance && npm run test:profiles && npm run test:preset && npm --prefix packages/openwrite-bridge run smoke && npm --prefix packages/studio-panel run smoke && npm run test:epochs && npm run test:components",
|
|
22
|
+
"docs:check": "node scripts/gen-tools-md.mjs --check && node scripts/check-editor-coordinates.mjs",
|
|
22
23
|
"test:profiles": "node scripts/profile-smoke.mjs",
|
|
23
24
|
"prepare": "node scripts/prepare.mjs",
|
|
24
|
-
"test:managed": "node --test packages/openwrite-bridge/scripts/managed-runtime.test.mjs packages/openwrite-bridge/scripts/tool-policy.test.mjs",
|
|
25
|
+
"test:managed": "node --test packages/openwrite-bridge/scripts/managed-runtime.test.mjs packages/openwrite-bridge/scripts/tool-policy.test.mjs packages/openwrite-bridge/scripts/followup-tools.test.mjs",
|
|
25
26
|
"test:release": "node scripts/release-smoke.mjs",
|
|
26
27
|
"test:client-boot": "node scripts/client-boot-smoke.mjs"
|
|
27
28
|
},
|
|
@@ -216,7 +216,7 @@ export async function materializeChapterDelivery(workspaceValue, chapterId, thre
|
|
|
216
216
|
});
|
|
217
217
|
let reviewStage;
|
|
218
218
|
if (Object.keys(review).length === 0) {
|
|
219
|
-
reviewStage = stage(chapterId, 'review', 'inconclusive', '
|
|
219
|
+
reviewStage = stage(chapterId, 'review', 'inconclusive', 'not_reviewed', {});
|
|
220
220
|
}
|
|
221
221
|
else if (!reviewCurrent) {
|
|
222
222
|
reviewStage = stage(chapterId, 'review', 'inconclusive', reviewStale ? 'stale' : 'unverifiable', {
|
|
@@ -225,9 +225,13 @@ export async function materializeChapterDelivery(workspaceValue, chapterId, thre
|
|
|
225
225
|
}
|
|
226
226
|
else {
|
|
227
227
|
const v2 = record(review['review_v2']);
|
|
228
|
-
|
|
228
|
+
const diagnostics = record(v2['delivery_diagnostics']);
|
|
229
|
+
const reviewState = !reviewPassed && currentDeliveryStatus === 'inconclusive' ? String(diagnostics['code'] ?? v2['delivery_reason'] ?? 'review_inconclusive') : 'current';
|
|
230
|
+
reviewStage = stage(chapterId, 'review', reviewPassed ? 'pass' : ['inconclusive', 'stale'].includes(currentDeliveryStatus) ? 'inconclusive' : 'fail', reviewState, {
|
|
229
231
|
score: review['score'] ?? null, threshold: effectiveThreshold, passedGate: reviewPassed,
|
|
230
|
-
qualityScore: v2['quality_score'] ?? review['score'] ?? null, coverage: v2['coverage'] ?? 1,
|
|
232
|
+
qualityScore: hasV2 ? v2['quality_score'] ?? null : review['score'] ?? null, coverage: v2['coverage'] ?? 1,
|
|
233
|
+
scoreValid: v2['score_valid'] ?? (hasV2 ? v2['quality_score'] != null : review['score'] != null),
|
|
234
|
+
scopeStatus: v2['scope_status'] ?? 'full', deliveryDiagnostics: diagnostics,
|
|
231
235
|
gateStatus: v2['gate_status'] ?? (hardIssueIds.length > 0 ? 'blocked' : 'pass'),
|
|
232
236
|
deliveryStatus: currentDeliveryStatus,
|
|
233
237
|
issueIds, hardIssueIds, sourceRevision: reviewSourceRevision,
|
|
@@ -191,7 +191,9 @@ export class ManagedRuntime {
|
|
|
191
191
|
}
|
|
192
192
|
status() { return { ...this.state }; }
|
|
193
193
|
update(phase, message, extra = {}) {
|
|
194
|
-
this.
|
|
194
|
+
const recoveryError = this.recovered && ['recovering', 'waiting', 'downloading', 'installing'].includes(phase)
|
|
195
|
+
? this.state.error : undefined;
|
|
196
|
+
this.state = { phase, message, ...(recoveryError ? { error: recoveryError } : {}), ...extra };
|
|
195
197
|
}
|
|
196
198
|
ensure() {
|
|
197
199
|
if (this.closed)
|
|
@@ -31,6 +31,8 @@ export function novelEffect(name, args) {
|
|
|
31
31
|
if (reads.has(name))
|
|
32
32
|
return 'read';
|
|
33
33
|
const values = args && typeof args === 'object' ? args : {};
|
|
34
|
+
if (name === 'novel_write_chapter' && values.dry_run === true)
|
|
35
|
+
return 'read';
|
|
34
36
|
if (readActions[name]?.includes(String(values.action)))
|
|
35
37
|
return 'read';
|
|
36
38
|
if (name === 'novel_model_benchmark' && values.action === 'run')
|
|
@@ -703,8 +703,10 @@ export function registerNovelTools(ctx, clientFactory, options) {
|
|
|
703
703
|
name: 'novel_write_chapter',
|
|
704
704
|
description: 'Queue a chapter write and immediately return a durable task_id. Poll novel_task_get for progress/result. Omit chapter_id to write ' +
|
|
705
705
|
'the outline-recommended next chapter; a given chapter_id must match the current recommendation. ' +
|
|
706
|
-
'Preview context first with novel_context_preview and consult novel_outline_read for the recommendation.'
|
|
706
|
+
'Preview context first with novel_context_preview and consult novel_outline_read for the recommendation. ' +
|
|
707
|
+
'Cancel with novel_task_cancel. Generated partial prose is retained; resume_from_draft continues it in a new run when the source/context revisions still match.',
|
|
707
708
|
parameters: {
|
|
709
|
+
dry_run: { type: 'boolean', description: 'Only assemble the current writer input and per-document sizes; no model call and no writing task. Guidance and target_words are included in this preview.' },
|
|
708
710
|
run_id_v2: { type: 'string', description: 'Resume this completed draft without generating it again.' },
|
|
709
711
|
resume_from_draft: { type: 'boolean', description: 'Resume the latest uncommitted draft for this chapter.' },
|
|
710
712
|
target_words: { type: 'integer', description: 'Target word count, 200-50000. When omitted use the outline target, then default 3000.' },
|
|
@@ -735,6 +737,8 @@ export function registerNovelTools(ctx, clientFactory, options) {
|
|
|
735
737
|
body['temperature'] = args.temperature;
|
|
736
738
|
if (args.outline_revision !== undefined)
|
|
737
739
|
body['outline_revision'] = args.outline_revision;
|
|
740
|
+
if (args.dry_run === true)
|
|
741
|
+
return await client.postJson('/api/write', { ...body, dry_run: true }, exec.signal);
|
|
738
742
|
return await client.postJson('/api/tasks', { type: 'chapter_write', input: body }, exec.signal);
|
|
739
743
|
},
|
|
740
744
|
}));
|
|
@@ -749,6 +753,8 @@ export function registerNovelTools(ctx, clientFactory, options) {
|
|
|
749
753
|
chapter_id: { type: 'string', description: 'Chapter id (ch_<digits>), resolved client-side to data/manuscript/<id>.md.' },
|
|
750
754
|
strict: { type: 'boolean', description: 'Enable strict review mode (default false).' },
|
|
751
755
|
dimensions: { type: 'array', items: { type: 'integer' }, description: 'Optional list of review dimension numbers (1-37) to restrict to.' },
|
|
756
|
+
depth: { type: 'string', enum: ['quick', 'full'], description: 'Default full. Quick reviews coherence/canon/prose and safety only; scope_status=partial cannot approve full-chapter delivery. Do not combine quick with dimensions.' },
|
|
757
|
+
domains: { type: 'array', items: { type: 'string' }, description: 'Review domain ids: coherence, character, plot, pacing, prose, canon. Use this or dimensions. Omitted domains remain explicitly unreviewed.' },
|
|
752
758
|
dog_threshold: { type: 'integer', description: 'DoG aggregate score threshold (default 70); this does not change OpenWrite review.' },
|
|
753
759
|
},
|
|
754
760
|
output: { schema: JSON_OUTPUT_SCHEMA, render: (_args, value) => renderJson(value) },
|
|
@@ -769,6 +775,10 @@ export function registerNovelTools(ctx, clientFactory, options) {
|
|
|
769
775
|
body['strict'] = args.strict;
|
|
770
776
|
if (args.dimensions !== undefined)
|
|
771
777
|
body['dimensions'] = args.dimensions;
|
|
778
|
+
if (args.depth !== undefined)
|
|
779
|
+
body['depth'] = args.depth;
|
|
780
|
+
if (args.domains !== undefined)
|
|
781
|
+
body['domains'] = args.domains;
|
|
772
782
|
const response = await client.postJson('/api/review', body, exec.signal);
|
|
773
783
|
const value = response !== null && typeof response === 'object' && !Array.isArray(response)
|
|
774
784
|
? response
|
|
@@ -887,7 +897,9 @@ export function registerNovelTools(ctx, clientFactory, options) {
|
|
|
887
897
|
'to receive an immutable preview_token and real before/after summary. Use "apply" only after author acceptance, ' +
|
|
888
898
|
'passing that token and confirm: true; "reject" discards it, "retry" rebuilds it against the current source, and "undo" safely ' +
|
|
889
899
|
'applies an undo_preview_token returned by a prior apply. Each edit uses old_text/new_text or unique start_text/' +
|
|
890
|
-
'end_text/new_text anchors.
|
|
900
|
+
'end_text/new_text anchors. Preview returns applied:false; changed/would_change describe only the proposed difference. ' +
|
|
901
|
+
'OLD_TEXT_NOT_FOUND is an anchor mismatch (400), while revision conflicts are 409; use the exact suggested bytes and reread the source before retrying. ' +
|
|
902
|
+
'The server rechecks the source revision and writes exactly the stored preview.',
|
|
891
903
|
parameters: {
|
|
892
904
|
action: { type: 'string', required: true, enum: ['preview', 'apply', 'reject', 'retry', 'undo'], description: 'Plan lifecycle action.' },
|
|
893
905
|
confirm: { type: 'boolean', description: 'Required true for apply and undo after author acceptance.' },
|
|
@@ -1207,13 +1219,14 @@ export function registerNovelTools(ctx, clientFactory, options) {
|
|
|
1207
1219
|
ctx.tools.register(defineTool({
|
|
1208
1220
|
name: 'novel_revision_create_selection',
|
|
1209
1221
|
description: 'Create a revision proposal for a text selection in a chapter (LONG-RUNNING, model-generated). ' +
|
|
1210
|
-
'start/end are
|
|
1222
|
+
'start/end are Unicode code-point offsets in the exact saved text, including original CR/LF characters; pass the selected text as original_text and the current expected_document_revision. The proposal is only staged — ' +
|
|
1211
1223
|
'apply it with novel_revision_apply.',
|
|
1212
1224
|
parameters: {
|
|
1213
1225
|
chapter_id: { type: 'string', required: true, description: 'Chapter id (ch_<digits>).' },
|
|
1214
1226
|
start: { type: 'integer', required: true, description: 'Selection start offset.' },
|
|
1215
1227
|
end: { type: 'integer', required: true, description: 'Selection end offset.' },
|
|
1216
1228
|
original_text: { type: 'string', description: 'The selected original text.' },
|
|
1229
|
+
expected_document_revision: { type: 'string', description: 'Current exact manuscript revision; reject before generation if the source changed.' },
|
|
1217
1230
|
instruction: { type: 'string', description: 'Revision instruction (max 4000 characters).' },
|
|
1218
1231
|
action: { type: 'string', enum: ['rewrite', 'expand', 'compress', 'pace', 'dialogue', 'naturalize', 'custom'], description: 'Revision mode (default "rewrite").' },
|
|
1219
1232
|
target_units: { type: 'integer', description: 'Target length in writing units (0 = keep similar).' },
|
|
@@ -1228,6 +1241,8 @@ export function registerNovelTools(ctx, clientFactory, options) {
|
|
|
1228
1241
|
const body = { chapter_id: args.chapter_id, start: args.start, end: args.end };
|
|
1229
1242
|
if (args.original_text !== undefined)
|
|
1230
1243
|
body['original_text'] = args.original_text;
|
|
1244
|
+
if (args.expected_document_revision !== undefined)
|
|
1245
|
+
body['expected_document_revision'] = args.expected_document_revision;
|
|
1231
1246
|
if (args.instruction !== undefined)
|
|
1232
1247
|
body['instruction'] = args.instruction;
|
|
1233
1248
|
if (args.action !== undefined)
|
|
@@ -1242,10 +1257,15 @@ export function registerNovelTools(ctx, clientFactory, options) {
|
|
|
1242
1257
|
ctx.tools.register(defineTool({
|
|
1243
1258
|
name: 'novel_revision_create_from_review',
|
|
1244
1259
|
description: 'Create a revision proposal that addresses selected issues from a chapter review (LONG-RUNNING, model-generated). ' +
|
|
1245
|
-
'Issue ids come from novel_review_chapter\'s issue_details. The proposal is only staged — apply it with novel_revision_apply.',
|
|
1260
|
+
'Issue ids come from novel_review_chapter\'s issue_details. Pass selection to constrain rewriting to an exact manuscript range; overlapping issue evidence never expands that range. The proposal is only staged — apply it with novel_revision_apply.',
|
|
1246
1261
|
parameters: {
|
|
1247
1262
|
chapter_id: { type: 'string', required: true, description: 'Chapter id (ch_<digits>).' },
|
|
1248
1263
|
issue_ids: { type: 'array', required: true, items: { type: 'string' }, description: 'Review issue ids to fix.' },
|
|
1264
|
+
selection: { type: 'object', additionalProperties: false, description: 'Optional exact saved-text range in Unicode code points; preserve raw CR/LF characters.', properties: {
|
|
1265
|
+
start: { type: 'integer', required: true, description: 'Inclusive source code-point offset.' },
|
|
1266
|
+
end: { type: 'integer', required: true, description: 'Exclusive source code-point offset.' },
|
|
1267
|
+
original_text: { type: 'string', required: true, description: 'Exact saved text inside this range.' },
|
|
1268
|
+
} },
|
|
1249
1269
|
instruction: { type: 'string', description: 'Additional revision instruction.' },
|
|
1250
1270
|
target_units: { type: 'integer', description: 'Target length in writing units (0 = keep similar).' },
|
|
1251
1271
|
expected_review_revision: { type: 'string', description: 'Exact review file revision from the chapter work brief.' },
|
|
@@ -1260,6 +1280,8 @@ export function registerNovelTools(ctx, clientFactory, options) {
|
|
|
1260
1280
|
if (args.issue_ids.length === 0)
|
|
1261
1281
|
throw new Error('issue_ids must be a non-empty array');
|
|
1262
1282
|
const body = { chapter_id: args.chapter_id, issue_ids: args.issue_ids };
|
|
1283
|
+
if (args.selection !== undefined)
|
|
1284
|
+
body['selection'] = args.selection;
|
|
1263
1285
|
if (args.instruction !== undefined)
|
|
1264
1286
|
body['instruction'] = args.instruction;
|
|
1265
1287
|
if (args.target_units !== undefined)
|
|
@@ -2027,6 +2049,7 @@ export function registerNovelTools(ctx, clientFactory, options) {
|
|
|
2027
2049
|
'confirm: true), "cancel" (run_id + revision, optional reason). All mutating actions are revision-gated: ' +
|
|
2028
2050
|
'pass the run\'s current revision from a list/get call.',
|
|
2029
2051
|
parameters: {
|
|
2052
|
+
include_archived: { type: 'boolean', description: 'For list: include historical runs moved to chapter_deletions when a chapter was deleted. Archived runs are read-only and include archive_directory.' },
|
|
2030
2053
|
action: { type: 'string', required: true, enum: ['list', 'get', 'record_intervention', 'update_intervention', 'cancel', 'archive'], description: 'The chapter-run operation.' },
|
|
2031
2054
|
run_id: { type: 'string', description: 'Run id (required for get/record_intervention/update_intervention/cancel).' },
|
|
2032
2055
|
revision: { type: 'string', description: 'Current run revision (required for mutating actions).' },
|
|
@@ -2053,7 +2076,7 @@ export function registerNovelTools(ctx, clientFactory, options) {
|
|
|
2053
2076
|
async execute(args, exec) {
|
|
2054
2077
|
const client = clientFor(exec);
|
|
2055
2078
|
const body = { action: args.action };
|
|
2056
|
-
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']) {
|
|
2079
|
+
for (const key of ['run_id', 'revision', 'chapter_id', 'statuses', 'since', 'limit', 'include_archived', 'scope', 'risk', 'request', 'affected_items', 'rewrite_required', 'intervention_id', 'state', 'facts_revision', 'impact', 'proposal', 'confirm', 'reason']) {
|
|
2057
2080
|
const value = args[key];
|
|
2058
2081
|
if (value !== undefined)
|
|
2059
2082
|
body[key] = value;
|