hoi4-agent-tools 0.1.7 → 0.2.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/CHANGELOG.md +24 -0
- package/README.md +31 -25
- package/SECURITY.md +4 -2
- package/dist/bin/setup.js +11 -4
- package/dist/bin/setup.js.map +1 -1
- package/dist/hoi4_agent_tools/core/configuration.d.ts +1 -0
- package/dist/hoi4_agent_tools/core/configuration.d.ts.map +1 -1
- package/dist/hoi4_agent_tools/core/configuration.js +3 -3
- package/dist/hoi4_agent_tools/core/configuration.js.map +1 -1
- package/dist/hoi4_agent_tools/core/engine.d.ts +1 -0
- package/dist/hoi4_agent_tools/core/engine.d.ts.map +1 -1
- package/dist/hoi4_agent_tools/core/engine.js +1 -0
- package/dist/hoi4_agent_tools/core/engine.js.map +1 -1
- package/dist/hoi4_agent_tools/core/transactions.d.ts +2 -0
- package/dist/hoi4_agent_tools/core/transactions.d.ts.map +1 -1
- package/dist/hoi4_agent_tools/core/transactions.js +49 -6
- package/dist/hoi4_agent_tools/core/transactions.js.map +1 -1
- package/dist/hoi4_agent_tools/core/workspace.js +1 -1
- package/dist/hoi4_agent_tools/core/workspace.js.map +1 -1
- package/dist/hoi4_agent_tools/focus/service.d.ts.map +1 -1
- package/dist/hoi4_agent_tools/focus/service.js +4 -18
- package/dist/hoi4_agent_tools/focus/service.js.map +1 -1
- package/dist/hoi4_agent_tools/gui/studio.d.ts.map +1 -1
- package/dist/hoi4_agent_tools/gui/studio.js +3 -2
- package/dist/hoi4_agent_tools/gui/studio.js.map +1 -1
- package/dist/hoi4_agent_tools/map/service.d.ts.map +1 -1
- package/dist/hoi4_agent_tools/map/service.js +11 -5
- package/dist/hoi4_agent_tools/map/service.js.map +1 -1
- package/dist/hoi4_agent_tools/mcp/prompts/register.d.ts +2 -1
- package/dist/hoi4_agent_tools/mcp/prompts/register.d.ts.map +1 -1
- package/dist/hoi4_agent_tools/mcp/prompts/register.js +35 -13
- package/dist/hoi4_agent_tools/mcp/prompts/register.js.map +1 -1
- package/dist/hoi4_agent_tools/mcp/registry/metadata.d.ts +1 -1
- package/dist/hoi4_agent_tools/mcp/security/auth.d.ts.map +1 -1
- package/dist/hoi4_agent_tools/mcp/security/auth.js +1 -4
- package/dist/hoi4_agent_tools/mcp/security/auth.js.map +1 -1
- package/dist/hoi4_agent_tools/mcp/server/base-tools.d.ts +12 -0
- package/dist/hoi4_agent_tools/mcp/server/base-tools.d.ts.map +1 -1
- package/dist/hoi4_agent_tools/mcp/server/base-tools.js +239 -220
- package/dist/hoi4_agent_tools/mcp/server/base-tools.js.map +1 -1
- package/dist/hoi4_agent_tools/mcp/server/create.d.ts.map +1 -1
- package/dist/hoi4_agent_tools/mcp/server/create.js +8 -2
- package/dist/hoi4_agent_tools/mcp/server/create.js.map +1 -1
- package/dist/hoi4_agent_tools/mcp/server/output-schemas.d.ts +5 -0
- package/dist/hoi4_agent_tools/mcp/server/output-schemas.d.ts.map +1 -1
- package/dist/hoi4_agent_tools/mcp/server/output-schemas.js +1 -0
- package/dist/hoi4_agent_tools/mcp/server/output-schemas.js.map +1 -1
- package/dist/hoi4_agent_tools/mcp/server/result.js +1 -1
- package/dist/hoi4_agent_tools/mcp/server/result.js.map +1 -1
- package/dist/hoi4_agent_tools/mcp/server/transaction-execution.d.ts +31 -0
- package/dist/hoi4_agent_tools/mcp/server/transaction-execution.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/mcp/server/transaction-execution.js +127 -0
- package/dist/hoi4_agent_tools/mcp/server/transaction-execution.js.map +1 -0
- package/dist/hoi4_agent_tools/mcp/tools/focus.d.ts.map +1 -1
- package/dist/hoi4_agent_tools/mcp/tools/focus.js +69 -23
- package/dist/hoi4_agent_tools/mcp/tools/focus.js.map +1 -1
- package/dist/hoi4_agent_tools/mcp/tools/gui.d.ts +6 -0
- package/dist/hoi4_agent_tools/mcp/tools/gui.d.ts.map +1 -1
- package/dist/hoi4_agent_tools/mcp/tools/gui.js +40 -12
- package/dist/hoi4_agent_tools/mcp/tools/gui.js.map +1 -1
- package/dist/hoi4_agent_tools/mcp/tools/map.d.ts.map +1 -1
- package/dist/hoi4_agent_tools/mcp/tools/map.js +39 -12
- package/dist/hoi4_agent_tools/mcp/tools/map.js.map +1 -1
- package/dist/hoi4_agent_tools/mcp/transports/http.d.ts.map +1 -1
- package/dist/hoi4_agent_tools/mcp/transports/http.js +3 -0
- package/dist/hoi4_agent_tools/mcp/transports/http.js.map +1 -1
- package/dist/hoi4_agent_tools/version.d.ts +1 -1
- package/dist/hoi4_agent_tools/version.js +1 -1
- package/docs/README.md +2 -2
- package/docs/adr/0012-autonomous-rewrites.md +55 -0
- package/docs/adr/README.md +1 -0
- package/docs/agent-integration.md +33 -23
- package/docs/architecture.md +20 -4
- package/docs/artifacts.md +8 -5
- package/docs/completion-report.md +85 -125
- package/docs/configuration.md +24 -8
- package/docs/focus-workflow.md +16 -14
- package/docs/gui-workflow.md +7 -3
- package/docs/limitations.md +3 -2
- package/docs/map-workflow.md +15 -9
- package/docs/performance.md +1 -1
- package/docs/release.md +11 -9
- package/docs/research/mcp-2026-07-10.md +9 -3
- package/docs/security.md +12 -6
- package/docs/self-hosting.md +8 -6
- package/docs/transactions.md +45 -33
- package/docs/troubleshooting.md +12 -4
- package/package.json +2 -2
- package/schemas/configuration.schema.json +3 -2
- package/schemas/continuous-focus-palette.schema.json +1 -1
- package/schemas/focus-plan.schema.json +1 -1
- package/schemas/focus-planning-sidecar.schema.json +1 -1
- package/schemas/gui-animation-source.schema.json +1 -1
- package/schemas/gui-helper.schema.json +1 -1
- package/schemas/gui-scenario.schema.json +1 -1
- package/schemas/map-operation.schema.json +1 -1
- package/schemas/operation-result.schema.json +1 -1
- package/schemas/transaction-manifest.schema.json +1 -1
- package/server.json +3 -3
|
@@ -261,7 +261,7 @@ function focusReferences(engineSnapshot) {
|
|
|
261
261
|
helper: active('scripted_effect'),
|
|
262
262
|
};
|
|
263
263
|
}
|
|
264
|
-
async function postValidateTransaction(engine, transaction, principal, signal) {
|
|
264
|
+
export async function postValidateTransaction(engine, transaction, principal, signal, autonomous = false) {
|
|
265
265
|
engine.invalidate(transaction.workspaceId);
|
|
266
266
|
const snapshot = await engine.scan(transaction.workspaceId, {}, principal, signal);
|
|
267
267
|
const changed = new Set(transaction.files.map(({ relativePath }) => sourcePathComparisonKey(relativePath)));
|
|
@@ -362,18 +362,26 @@ async function postValidateTransaction(engine, transaction, principal, signal) {
|
|
|
362
362
|
if (diagnostics.length > availableDiagnostics) {
|
|
363
363
|
const workspace = engine.resolver.get(transaction.workspaceId, principal);
|
|
364
364
|
const sourceEvidence = boundedSourceHashEvidence(Object.fromEntries(transaction.files.flatMap(({ relativePath, afterSha256 }) => afterSha256 === null ? [] : [[relativePath, afterSha256]])));
|
|
365
|
-
const artifact = await engine.artifacts.putChunked(workspace,
|
|
365
|
+
const artifact = await engine.artifacts.putChunked(workspace, autonomous
|
|
366
|
+
? `${transaction.operationKind}.post-validation.json`
|
|
367
|
+
: `${transaction.transactionId}.post-validation.json`, 'application/json', `${canonicalJson({
|
|
366
368
|
schemaVersion: 1,
|
|
367
|
-
|
|
368
|
-
|
|
369
|
+
...(autonomous
|
|
370
|
+
? { execution: 'autonomous-rewrite' }
|
|
371
|
+
: {
|
|
372
|
+
transactionId: transaction.transactionId,
|
|
373
|
+
planHash: transaction.planHash,
|
|
374
|
+
}),
|
|
369
375
|
operationKind: transaction.operationKind,
|
|
370
376
|
diagnosticCount: diagnostics.length,
|
|
371
377
|
checks,
|
|
372
378
|
diagnostics,
|
|
373
379
|
})}\n`, {
|
|
374
|
-
kind: 'transaction-post-validation',
|
|
380
|
+
kind: autonomous ? 'autonomous-rewrite-post-validation' : 'transaction-post-validation',
|
|
375
381
|
toolVersion: PACKAGE_VERSION,
|
|
376
|
-
schemaVersion:
|
|
382
|
+
schemaVersion: autonomous
|
|
383
|
+
? 'autonomous-rewrite-post-validation.v1'
|
|
384
|
+
: 'transaction-post-validation.v1',
|
|
377
385
|
sourceHashes: sourceEvidence.sourceHashes,
|
|
378
386
|
metadata: { sourceHashInventory: sourceEvidence.inventory },
|
|
379
387
|
}, 'Complete source-linked diagnostics from post-write transaction validation', signal);
|
|
@@ -411,9 +419,10 @@ async function postValidateTransaction(engine, transaction, principal, signal) {
|
|
|
411
419
|
},
|
|
412
420
|
],
|
|
413
421
|
checks,
|
|
422
|
+
artifacts: [artifactLink],
|
|
414
423
|
};
|
|
415
424
|
}
|
|
416
|
-
return { diagnostics, checks };
|
|
425
|
+
return { diagnostics, checks, artifacts: [] };
|
|
417
426
|
}
|
|
418
427
|
export function registerBaseTools(server, engine, context) {
|
|
419
428
|
server.registerTool('hoi4.project_register', {
|
|
@@ -570,229 +579,239 @@ export function registerBaseTools(server, engine, context) {
|
|
|
570
579
|
return errorResult(error, workspaceId);
|
|
571
580
|
}
|
|
572
581
|
});
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
582
|
+
if (engine.resolver.config().writePolicy !== 'autonomous') {
|
|
583
|
+
server.registerTool('hoi4.transaction_status', {
|
|
584
|
+
title: 'Inspect transaction',
|
|
585
|
+
description: 'Return compact dry-run/apply/rollback counts and current state with a complete manifest resource.',
|
|
586
|
+
inputSchema: workspaceInput.extend({ transactionId: transactionIdSchema }).strict(),
|
|
587
|
+
outputSchema: transactionStatusOutputSchema,
|
|
588
|
+
annotations: readOnly,
|
|
589
|
+
}, async ({ workspaceId, transactionId }, extra) => {
|
|
590
|
+
try {
|
|
591
|
+
const progress = progressReporter(extra);
|
|
592
|
+
progress.signal.throwIfAborted();
|
|
593
|
+
const manifest = await engine.transactions.status(workspaceId, transactionId, context.principal, progress.signal);
|
|
594
|
+
progress.signal.throwIfAborted();
|
|
595
|
+
const result = emptyServiceResult(workspaceId, {
|
|
596
|
+
manifest: transactionSummary(manifest),
|
|
597
|
+
});
|
|
598
|
+
result.code = 'TRANSACTION_STATUS';
|
|
599
|
+
result.transactionId = manifest.transactionId;
|
|
600
|
+
result.planHash = manifest.planHash;
|
|
601
|
+
result.proposedFiles = manifest.files
|
|
602
|
+
.slice(0, 100)
|
|
603
|
+
.map(({ relativePath }) => relativePath);
|
|
604
|
+
result.changedFiles = manifest.appliedFiles.slice(0, 100);
|
|
605
|
+
result.artifacts = [transactionResourceLink(manifest)];
|
|
606
|
+
result.validation = manifest.validation;
|
|
607
|
+
result.rollbackStatus = manifest.rollbackStatus;
|
|
608
|
+
return toolResult(result);
|
|
609
|
+
}
|
|
610
|
+
catch (error) {
|
|
611
|
+
return errorResult(error, workspaceId);
|
|
612
|
+
}
|
|
613
|
+
});
|
|
614
|
+
server.registerTool('hoi4.transaction_diff', {
|
|
615
|
+
title: 'Review transaction diff',
|
|
616
|
+
description: 'Page through the complete affected-file set, operation summaries, and source/binary artifact links before apply.',
|
|
617
|
+
inputSchema: workspaceInput
|
|
618
|
+
.extend({
|
|
619
|
+
transactionId: transactionIdSchema,
|
|
620
|
+
limit: z.number().int().min(1).max(20).default(20),
|
|
621
|
+
cursor: z.string().min(1).max(4096).optional(),
|
|
622
|
+
})
|
|
623
|
+
.strict(),
|
|
624
|
+
outputSchema: transactionDiffOutputSchema,
|
|
625
|
+
annotations: readOnly,
|
|
626
|
+
}, async ({ workspaceId, transactionId, limit, cursor }, extra) => {
|
|
627
|
+
try {
|
|
628
|
+
const progress = progressReporter(extra);
|
|
629
|
+
progress.signal.throwIfAborted();
|
|
630
|
+
const manifest = await engine.transactions.status(workspaceId, transactionId, context.principal, progress.signal);
|
|
631
|
+
progress.signal.throwIfAborted();
|
|
632
|
+
let fileOffset = 0;
|
|
633
|
+
let operationOffset = 0;
|
|
634
|
+
let artifactOffset = 0;
|
|
635
|
+
if (cursor !== undefined) {
|
|
636
|
+
const decoded = decodeTransactionDiffCursor(cursor);
|
|
637
|
+
if (decoded.planHash !== manifest.planHash ||
|
|
638
|
+
decoded.fileOffset > manifest.files.length ||
|
|
639
|
+
decoded.operationOffset > manifest.operations.length ||
|
|
640
|
+
decoded.artifactOffset > manifest.artifacts.length) {
|
|
641
|
+
throw new ServiceError('TRANSACTION_CURSOR_STALE', 'Transaction plan changed or the diff cursor is stale');
|
|
642
|
+
}
|
|
643
|
+
fileOffset = decoded.fileOffset;
|
|
644
|
+
operationOffset = decoded.operationOffset;
|
|
645
|
+
artifactOffset = decoded.artifactOffset;
|
|
628
646
|
}
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
647
|
+
const manifestFiles = manifest.files.slice(fileOffset, fileOffset + limit);
|
|
648
|
+
const files = manifestFiles.map(({ relativePath, operationIds, beforeSha256, afterSha256, beforeSize, afterSize, mediaType, diffArtifact, }) => ({
|
|
649
|
+
relativePath,
|
|
650
|
+
operationIdCount: operationIds.length,
|
|
651
|
+
operationIds: operationIds.slice(0, 20),
|
|
652
|
+
beforeSha256,
|
|
653
|
+
afterSha256,
|
|
654
|
+
beforeSize,
|
|
655
|
+
afterSize,
|
|
656
|
+
mediaType,
|
|
657
|
+
...(diffArtifact === undefined ? {} : { diffArtifact }),
|
|
658
|
+
}));
|
|
659
|
+
const operations = manifest.operations.slice(operationOffset, operationOffset + limit);
|
|
660
|
+
const artifacts = manifest.artifacts.slice(artifactOffset, artifactOffset + limit);
|
|
661
|
+
const nextFileOffset = fileOffset + manifestFiles.length;
|
|
662
|
+
const nextOperationOffset = operationOffset + operations.length;
|
|
663
|
+
const nextArtifactOffset = artifactOffset + artifacts.length;
|
|
664
|
+
const nextCursor = nextFileOffset < manifest.files.length ||
|
|
665
|
+
nextOperationOffset < manifest.operations.length ||
|
|
666
|
+
nextArtifactOffset < manifest.artifacts.length
|
|
667
|
+
? encodeTransactionDiffCursor(manifest.planHash, nextFileOffset, nextOperationOffset, nextArtifactOffset)
|
|
668
|
+
: undefined;
|
|
669
|
+
const result = emptyServiceResult(workspaceId, {
|
|
670
|
+
files,
|
|
671
|
+
operations: operations.map(({ id, kind, summary }) => ({ id, kind, summary })),
|
|
672
|
+
fileCount: manifest.files.length,
|
|
673
|
+
operationCount: manifest.operations.length,
|
|
674
|
+
artifactCount: manifest.artifacts.length,
|
|
675
|
+
returnedFiles: files.length,
|
|
676
|
+
returnedOperations: operations.length,
|
|
677
|
+
returnedArtifacts: artifacts.length,
|
|
678
|
+
...(nextCursor === undefined ? {} : { nextCursor }),
|
|
679
|
+
expiresAt: manifest.expiresAt,
|
|
680
|
+
});
|
|
681
|
+
result.code = 'TRANSACTION_DIFF';
|
|
682
|
+
result.transactionId = manifest.transactionId;
|
|
683
|
+
result.planHash = manifest.planHash;
|
|
684
|
+
result.proposedFiles = files.map(({ relativePath }) => relativePath);
|
|
685
|
+
result.artifacts = [transactionResourceLink(manifest), ...artifacts];
|
|
686
|
+
result.validation = manifest.validation;
|
|
687
|
+
result.rollbackStatus = manifest.rollbackStatus;
|
|
688
|
+
return toolResult(result);
|
|
632
689
|
}
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
const nextArtifactOffset = artifactOffset + artifacts.length;
|
|
650
|
-
const nextCursor = nextFileOffset < manifest.files.length ||
|
|
651
|
-
nextOperationOffset < manifest.operations.length ||
|
|
652
|
-
nextArtifactOffset < manifest.artifacts.length
|
|
653
|
-
? encodeTransactionDiffCursor(manifest.planHash, nextFileOffset, nextOperationOffset, nextArtifactOffset)
|
|
654
|
-
: undefined;
|
|
655
|
-
const result = emptyServiceResult(workspaceId, {
|
|
656
|
-
files,
|
|
657
|
-
operations: operations.map(({ id, kind, summary }) => ({ id, kind, summary })),
|
|
658
|
-
fileCount: manifest.files.length,
|
|
659
|
-
operationCount: manifest.operations.length,
|
|
660
|
-
artifactCount: manifest.artifacts.length,
|
|
661
|
-
returnedFiles: files.length,
|
|
662
|
-
returnedOperations: operations.length,
|
|
663
|
-
returnedArtifacts: artifacts.length,
|
|
664
|
-
...(nextCursor === undefined ? {} : { nextCursor }),
|
|
665
|
-
expiresAt: manifest.expiresAt,
|
|
666
|
-
});
|
|
667
|
-
result.code = 'TRANSACTION_DIFF';
|
|
668
|
-
result.transactionId = manifest.transactionId;
|
|
669
|
-
result.planHash = manifest.planHash;
|
|
670
|
-
result.proposedFiles = files.map(({ relativePath }) => relativePath);
|
|
671
|
-
result.artifacts = [transactionResourceLink(manifest), ...artifacts];
|
|
672
|
-
result.validation = manifest.validation;
|
|
673
|
-
result.rollbackStatus = manifest.rollbackStatus;
|
|
674
|
-
return toolResult(result);
|
|
675
|
-
}
|
|
676
|
-
catch (error) {
|
|
677
|
-
return errorResult(error, workspaceId);
|
|
678
|
-
}
|
|
679
|
-
});
|
|
680
|
-
server.registerTool('hoi4.transaction_apply', {
|
|
681
|
-
title: 'Apply reviewed transaction',
|
|
682
|
-
description: 'Apply a completed dry run only when transaction ID, workspace, principal, source hashes, and expected plan hash still match.',
|
|
683
|
-
inputSchema: transactionInput,
|
|
684
|
-
outputSchema: transactionMutationOutputSchema,
|
|
685
|
-
annotations: {
|
|
686
|
-
readOnlyHint: false,
|
|
687
|
-
destructiveHint: true,
|
|
688
|
-
idempotentHint: false,
|
|
689
|
-
openWorldHint: false,
|
|
690
|
-
},
|
|
691
|
-
}, async ({ workspaceId, transactionId, expectedPlanHash }, extra) => {
|
|
692
|
-
try {
|
|
693
|
-
requireServerScope(context, 'hoi4:write');
|
|
694
|
-
const progress = progressReporter(extra);
|
|
695
|
-
await progress.report(0, 3, 'Checking transaction preconditions');
|
|
696
|
-
const manifest = await engine.transactions.apply(workspaceId, transactionId, expectedPlanHash, {
|
|
697
|
-
...(context.principal === undefined ? {} : { principal: context.principal }),
|
|
698
|
-
signal: progress.signal,
|
|
699
|
-
postValidate: (transaction, signal) => postValidateTransaction(engine, transaction, context.principal, signal),
|
|
700
|
-
});
|
|
701
|
-
engine.invalidate(workspaceId);
|
|
702
|
-
await engine.scan(workspaceId, {}, context.principal, progress.signal);
|
|
703
|
-
await progress.report(3, 3, 'Apply and index rebuild complete');
|
|
704
|
-
const result = emptyServiceResult(workspaceId, {
|
|
705
|
-
state: manifest.state,
|
|
706
|
-
fileCount: manifest.files.length,
|
|
707
|
-
appliedFileCount: manifest.appliedFiles.length,
|
|
708
|
-
artifactCount: manifest.artifacts.length,
|
|
709
|
-
});
|
|
710
|
-
result.code = 'TRANSACTION_APPLIED';
|
|
711
|
-
result.transactionId = manifest.transactionId;
|
|
712
|
-
result.planHash = manifest.planHash;
|
|
713
|
-
result.proposedFiles = manifest.files.slice(0, 100).map(({ relativePath }) => relativePath);
|
|
714
|
-
result.changedFiles = manifest.appliedFiles.slice(0, 100);
|
|
715
|
-
result.artifacts = [transactionResourceLink(manifest)];
|
|
716
|
-
result.validation = manifest.validation;
|
|
717
|
-
result.rollbackStatus = manifest.rollbackStatus;
|
|
718
|
-
return toolResult(result);
|
|
719
|
-
}
|
|
720
|
-
catch (error) {
|
|
721
|
-
engine.invalidate(workspaceId);
|
|
690
|
+
catch (error) {
|
|
691
|
+
return errorResult(error, workspaceId);
|
|
692
|
+
}
|
|
693
|
+
});
|
|
694
|
+
server.registerTool('hoi4.transaction_apply', {
|
|
695
|
+
title: 'Apply reviewed transaction',
|
|
696
|
+
description: 'Apply a completed dry run only when transaction ID, workspace, principal, source hashes, and expected plan hash still match.',
|
|
697
|
+
inputSchema: transactionInput,
|
|
698
|
+
outputSchema: transactionMutationOutputSchema,
|
|
699
|
+
annotations: {
|
|
700
|
+
readOnlyHint: false,
|
|
701
|
+
destructiveHint: true,
|
|
702
|
+
idempotentHint: false,
|
|
703
|
+
openWorldHint: false,
|
|
704
|
+
},
|
|
705
|
+
}, async ({ workspaceId, transactionId, expectedPlanHash }, extra) => {
|
|
722
706
|
try {
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
validation: failed.validation,
|
|
731
|
-
rollbackStatus: failed.rollbackStatus,
|
|
732
|
-
data: { state: failed.state, failure: failed.failure ?? null },
|
|
707
|
+
requireServerScope(context, 'hoi4:write');
|
|
708
|
+
const progress = progressReporter(extra);
|
|
709
|
+
await progress.report(0, 3, 'Checking transaction preconditions');
|
|
710
|
+
const manifest = await engine.transactions.apply(workspaceId, transactionId, expectedPlanHash, {
|
|
711
|
+
...(context.principal === undefined ? {} : { principal: context.principal }),
|
|
712
|
+
signal: progress.signal,
|
|
713
|
+
postValidate: (transaction, signal) => postValidateTransaction(engine, transaction, context.principal, signal),
|
|
733
714
|
});
|
|
715
|
+
engine.invalidate(workspaceId);
|
|
716
|
+
await engine.scan(workspaceId, {}, context.principal, progress.signal);
|
|
717
|
+
await progress.report(3, 3, 'Apply and index rebuild complete');
|
|
718
|
+
const result = emptyServiceResult(workspaceId, {
|
|
719
|
+
state: manifest.state,
|
|
720
|
+
fileCount: manifest.files.length,
|
|
721
|
+
appliedFileCount: manifest.appliedFiles.length,
|
|
722
|
+
artifactCount: manifest.artifacts.length,
|
|
723
|
+
});
|
|
724
|
+
result.code = 'TRANSACTION_APPLIED';
|
|
725
|
+
result.transactionId = manifest.transactionId;
|
|
726
|
+
result.planHash = manifest.planHash;
|
|
727
|
+
result.proposedFiles = manifest.files
|
|
728
|
+
.slice(0, 100)
|
|
729
|
+
.map(({ relativePath }) => relativePath);
|
|
730
|
+
result.changedFiles = manifest.appliedFiles.slice(0, 100);
|
|
731
|
+
result.artifacts = [transactionResourceLink(manifest)];
|
|
732
|
+
result.validation = manifest.validation;
|
|
733
|
+
result.rollbackStatus = manifest.rollbackStatus;
|
|
734
|
+
return toolResult(result);
|
|
734
735
|
}
|
|
735
|
-
catch {
|
|
736
|
-
|
|
736
|
+
catch (error) {
|
|
737
|
+
engine.invalidate(workspaceId);
|
|
738
|
+
try {
|
|
739
|
+
const failed = await engine.transactions.status(workspaceId, transactionId, context.principal);
|
|
740
|
+
return errorResult(error, workspaceId, {
|
|
741
|
+
transactionId: failed.transactionId,
|
|
742
|
+
planHash: failed.planHash,
|
|
743
|
+
proposedFiles: failed.files.slice(0, 100).map(({ relativePath }) => relativePath),
|
|
744
|
+
changedFiles: failed.state === 'applied' ? failed.appliedFiles.slice(0, 100) : [],
|
|
745
|
+
artifacts: [transactionResourceLink(failed)],
|
|
746
|
+
validation: failed.validation,
|
|
747
|
+
rollbackStatus: failed.rollbackStatus,
|
|
748
|
+
data: { state: failed.state, failure: failed.failure ?? null },
|
|
749
|
+
});
|
|
750
|
+
}
|
|
751
|
+
catch {
|
|
752
|
+
// A forged or cross-workspace transaction ID intentionally reveals no manifest details.
|
|
753
|
+
}
|
|
754
|
+
return errorResult(error, workspaceId);
|
|
737
755
|
}
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
},
|
|
752
|
-
}, async ({ workspaceId, transactionId, expectedPlanHash }, extra) => {
|
|
753
|
-
try {
|
|
754
|
-
requireServerScope(context, 'hoi4:write');
|
|
755
|
-
const progress = progressReporter(extra);
|
|
756
|
-
await progress.report(0, 2, 'Checking rollback preconditions');
|
|
757
|
-
progress.signal.throwIfAborted();
|
|
758
|
-
const manifest = await engine.transactions.rollback(workspaceId, transactionId, expectedPlanHash, context.principal, progress.signal);
|
|
759
|
-
engine.invalidate(workspaceId);
|
|
760
|
-
const result = emptyServiceResult(workspaceId, {
|
|
761
|
-
state: manifest.state,
|
|
762
|
-
fileCount: manifest.files.length,
|
|
763
|
-
appliedFileCount: manifest.appliedFiles.length,
|
|
764
|
-
artifactCount: manifest.artifacts.length,
|
|
765
|
-
});
|
|
766
|
-
result.code = 'TRANSACTION_ROLLED_BACK';
|
|
767
|
-
result.transactionId = manifest.transactionId;
|
|
768
|
-
result.planHash = manifest.planHash;
|
|
769
|
-
result.changedFiles = manifest.files.slice(0, 100).map(({ relativePath }) => relativePath);
|
|
770
|
-
result.artifacts = [transactionResourceLink(manifest)];
|
|
771
|
-
result.validation = manifest.validation;
|
|
772
|
-
result.rollbackStatus = manifest.rollbackStatus;
|
|
773
|
-
await progress.report(2, 2, 'Atomic rollback complete');
|
|
774
|
-
return toolResult(result);
|
|
775
|
-
}
|
|
776
|
-
catch (error) {
|
|
756
|
+
});
|
|
757
|
+
server.registerTool('hoi4.transaction_rollback', {
|
|
758
|
+
title: 'Roll back applied transaction',
|
|
759
|
+
description: 'Restore exact original bytes when the applied result still matches rollback preconditions.',
|
|
760
|
+
inputSchema: transactionInput,
|
|
761
|
+
outputSchema: transactionMutationOutputSchema,
|
|
762
|
+
annotations: {
|
|
763
|
+
readOnlyHint: false,
|
|
764
|
+
destructiveHint: true,
|
|
765
|
+
idempotentHint: false,
|
|
766
|
+
openWorldHint: false,
|
|
767
|
+
},
|
|
768
|
+
}, async ({ workspaceId, transactionId, expectedPlanHash }, extra) => {
|
|
777
769
|
try {
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
770
|
+
requireServerScope(context, 'hoi4:write');
|
|
771
|
+
const progress = progressReporter(extra);
|
|
772
|
+
await progress.report(0, 2, 'Checking rollback preconditions');
|
|
773
|
+
progress.signal.throwIfAborted();
|
|
774
|
+
const manifest = await engine.transactions.rollback(workspaceId, transactionId, expectedPlanHash, context.principal, progress.signal);
|
|
775
|
+
engine.invalidate(workspaceId);
|
|
776
|
+
const result = emptyServiceResult(workspaceId, {
|
|
777
|
+
state: manifest.state,
|
|
778
|
+
fileCount: manifest.files.length,
|
|
779
|
+
appliedFileCount: manifest.appliedFiles.length,
|
|
780
|
+
artifactCount: manifest.artifacts.length,
|
|
788
781
|
});
|
|
782
|
+
result.code = 'TRANSACTION_ROLLED_BACK';
|
|
783
|
+
result.transactionId = manifest.transactionId;
|
|
784
|
+
result.planHash = manifest.planHash;
|
|
785
|
+
result.changedFiles = manifest.files
|
|
786
|
+
.slice(0, 100)
|
|
787
|
+
.map(({ relativePath }) => relativePath);
|
|
788
|
+
result.artifacts = [transactionResourceLink(manifest)];
|
|
789
|
+
result.validation = manifest.validation;
|
|
790
|
+
result.rollbackStatus = manifest.rollbackStatus;
|
|
791
|
+
await progress.report(2, 2, 'Atomic rollback complete');
|
|
792
|
+
return toolResult(result);
|
|
789
793
|
}
|
|
790
|
-
catch {
|
|
791
|
-
|
|
794
|
+
catch (error) {
|
|
795
|
+
try {
|
|
796
|
+
const failed = await engine.transactions.status(workspaceId, transactionId, context.principal);
|
|
797
|
+
return errorResult(error, workspaceId, {
|
|
798
|
+
transactionId: failed.transactionId,
|
|
799
|
+
planHash: failed.planHash,
|
|
800
|
+
proposedFiles: failed.files.slice(0, 100).map(({ relativePath }) => relativePath),
|
|
801
|
+
changedFiles: failed.state === 'applied' ? failed.appliedFiles.slice(0, 100) : [],
|
|
802
|
+
artifacts: [transactionResourceLink(failed)],
|
|
803
|
+
validation: failed.validation,
|
|
804
|
+
rollbackStatus: failed.rollbackStatus,
|
|
805
|
+
data: { state: failed.state, failure: failed.failure ?? null },
|
|
806
|
+
});
|
|
807
|
+
}
|
|
808
|
+
catch {
|
|
809
|
+
// Invalid or inaccessible transaction IDs intentionally reveal no manifest details.
|
|
810
|
+
}
|
|
811
|
+
return errorResult(error, workspaceId);
|
|
792
812
|
}
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
});
|
|
813
|
+
});
|
|
814
|
+
}
|
|
796
815
|
server.registerTool('hoi4.artifact_list', {
|
|
797
816
|
title: 'List workspace artifacts',
|
|
798
817
|
description: 'List content-addressed artifacts without exposing filesystem paths.',
|