hoi4-agent-tools 0.1.6 → 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 +46 -0
- package/README.md +44 -27
- package/SECURITY.md +4 -2
- package/dist/bin/setup.js +43 -6
- 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/index.d.ts.map +1 -1
- package/dist/hoi4_agent_tools/core/index.js +10 -2
- package/dist/hoi4_agent_tools/core/index.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/compiler.d.ts +2 -1
- package/dist/hoi4_agent_tools/focus/compiler.d.ts.map +1 -1
- package/dist/hoi4_agent_tools/focus/compiler.js +12 -2
- package/dist/hoi4_agent_tools/focus/compiler.js.map +1 -1
- package/dist/hoi4_agent_tools/focus/importer.d.ts.map +1 -1
- package/dist/hoi4_agent_tools/focus/importer.js +13 -2
- package/dist/hoi4_agent_tools/focus/importer.js.map +1 -1
- package/dist/hoi4_agent_tools/focus/layout.d.ts.map +1 -1
- package/dist/hoi4_agent_tools/focus/layout.js +165 -42
- package/dist/hoi4_agent_tools/focus/layout.js.map +1 -1
- package/dist/hoi4_agent_tools/focus/lint.d.ts.map +1 -1
- package/dist/hoi4_agent_tools/focus/lint.js +37 -8
- package/dist/hoi4_agent_tools/focus/lint.js.map +1 -1
- package/dist/hoi4_agent_tools/focus/model.d.ts +5 -1
- package/dist/hoi4_agent_tools/focus/model.d.ts.map +1 -1
- package/dist/hoi4_agent_tools/focus/model.js +2 -0
- package/dist/hoi4_agent_tools/focus/model.js.map +1 -1
- package/dist/hoi4_agent_tools/focus/presentation.d.ts.map +1 -1
- package/dist/hoi4_agent_tools/focus/presentation.js +13 -5
- package/dist/hoi4_agent_tools/focus/presentation.js.map +1 -1
- package/dist/hoi4_agent_tools/focus/render.d.ts +4 -0
- package/dist/hoi4_agent_tools/focus/render.d.ts.map +1 -1
- package/dist/hoi4_agent_tools/focus/render.js +17 -3
- package/dist/hoi4_agent_tools/focus/render.js.map +1 -1
- package/dist/hoi4_agent_tools/focus/service.d.ts.map +1 -1
- package/dist/hoi4_agent_tools/focus/service.js +142 -97
- package/dist/hoi4_agent_tools/focus/service.js.map +1 -1
- package/dist/hoi4_agent_tools/focus/source-update.d.ts.map +1 -1
- package/dist/hoi4_agent_tools/focus/source-update.js +9 -4
- package/dist/hoi4_agent_tools/focus/source-update.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/resources/register.d.ts.map +1 -1
- package/dist/hoi4_agent_tools/mcp/resources/register.js +1 -0
- package/dist/hoi4_agent_tools/mcp/resources/register.js.map +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 +297 -218
- 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 +131 -30
- 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/schemas/focus.d.ts +1 -1
- package/dist/hoi4_agent_tools/schemas/focus.d.ts.map +1 -1
- package/dist/hoi4_agent_tools/schemas/focus.js +16 -2
- package/dist/hoi4_agent_tools/schemas/focus.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 +3 -2
- package/docs/adr/0012-autonomous-rewrites.md +55 -0
- package/docs/adr/README.md +1 -0
- package/docs/agent-integration.md +146 -0
- package/docs/architecture.md +20 -4
- package/docs/artifacts.md +8 -5
- package/docs/completion-report.md +109 -79
- package/docs/configuration.md +30 -10
- package/docs/focus-workflow.md +134 -5
- package/docs/gui-workflow.md +7 -3
- package/docs/limitations.md +4 -2
- package/docs/map-workflow.md +15 -9
- package/docs/performance.md +1 -1
- package/docs/release.md +12 -7
- 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 -27
- package/docs/troubleshooting.md +12 -4
- package/package.json +7 -3
- package/schemas/configuration.schema.json +3 -2
- package/schemas/continuous-focus-palette.schema.json +1 -1
- package/schemas/focus-plan.schema.json +74 -49
- package/schemas/focus-planning-sidecar.schema.json +33 -18
- 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 +4 -4
|
@@ -4,6 +4,7 @@ import { boundedSourceHashEvidence, publicArtifactLink } from '../../core/artifa
|
|
|
4
4
|
import { workspaceRegistrationSchema } from '../../core/configuration.js';
|
|
5
5
|
import { emptyServiceResult, ServiceError } from '../../core/result.js';
|
|
6
6
|
import { parseClausewitz } from '../../core/source/index.js';
|
|
7
|
+
import { TRANSACTION_MAX_DIAGNOSTICS } from '../../core/transaction-limits.js';
|
|
7
8
|
import { importContinuousFocusPalettes, importFocusTrees, layoutFocusTreeAsync, lintContinuousFocusPalette, lintFocusTree, } from '../../focus/index.js';
|
|
8
9
|
import { ScriptedGuiStudio } from '../../gui/index.js';
|
|
9
10
|
import { AgentNudger, attributeMapValidationDiagnostics } from '../../map/index.js';
|
|
@@ -260,7 +261,7 @@ function focusReferences(engineSnapshot) {
|
|
|
260
261
|
helper: active('scripted_effect'),
|
|
261
262
|
};
|
|
262
263
|
}
|
|
263
|
-
async function postValidateTransaction(engine, transaction, principal, signal) {
|
|
264
|
+
export async function postValidateTransaction(engine, transaction, principal, signal, autonomous = false) {
|
|
264
265
|
engine.invalidate(transaction.workspaceId);
|
|
265
266
|
const snapshot = await engine.scan(transaction.workspaceId, {}, principal, signal);
|
|
266
267
|
const changed = new Set(transaction.files.map(({ relativePath }) => sourcePathComparisonKey(relativePath)));
|
|
@@ -273,7 +274,9 @@ async function postValidateTransaction(engine, transaction, principal, signal) {
|
|
|
273
274
|
const checks = [];
|
|
274
275
|
if (transaction.operationKind === 'focus-plan-changes') {
|
|
275
276
|
const catalog = focusReferences(snapshot);
|
|
276
|
-
for (const file of snapshot.files.filter(({ shadowedBy, relativePath }) => shadowedBy === undefined &&
|
|
277
|
+
for (const file of snapshot.files.filter(({ shadowedBy, relativePath }) => shadowedBy === undefined &&
|
|
278
|
+
relativePath.toLowerCase().endsWith('.txt') &&
|
|
279
|
+
changed.has(sourcePathComparisonKey(relativePath)))) {
|
|
277
280
|
const document = parseClausewitz(file.bytes, file.displayPath);
|
|
278
281
|
const imported = importFocusTrees(document, { references: catalog });
|
|
279
282
|
diagnostics.push(...imported.diagnostics);
|
|
@@ -296,7 +299,9 @@ async function postValidateTransaction(engine, transaction, principal, signal) {
|
|
|
296
299
|
});
|
|
297
300
|
}
|
|
298
301
|
else if (transaction.operationKind === 'continuous-focus-plan-changes') {
|
|
299
|
-
for (const file of snapshot.files.filter(({ shadowedBy, relativePath }) => shadowedBy === undefined &&
|
|
302
|
+
for (const file of snapshot.files.filter(({ shadowedBy, relativePath }) => shadowedBy === undefined &&
|
|
303
|
+
relativePath.toLowerCase().endsWith('.txt') &&
|
|
304
|
+
changed.has(sourcePathComparisonKey(relativePath)))) {
|
|
300
305
|
const document = parseClausewitz(file.bytes, file.displayPath);
|
|
301
306
|
const imported = importContinuousFocusPalettes(document);
|
|
302
307
|
diagnostics.push(...imported.diagnostics);
|
|
@@ -353,7 +358,71 @@ async function postValidateTransaction(engine, transaction, principal, signal) {
|
|
|
353
358
|
? 'Changed source reparsed and reindexed successfully'
|
|
354
359
|
: 'Changed source has blocking post-write diagnostics',
|
|
355
360
|
});
|
|
356
|
-
|
|
361
|
+
const availableDiagnostics = Math.max(0, TRANSACTION_MAX_DIAGNOSTICS - transaction.diagnostics.length);
|
|
362
|
+
if (diagnostics.length > availableDiagnostics) {
|
|
363
|
+
const workspace = engine.resolver.get(transaction.workspaceId, principal);
|
|
364
|
+
const sourceEvidence = boundedSourceHashEvidence(Object.fromEntries(transaction.files.flatMap(({ relativePath, afterSha256 }) => afterSha256 === null ? [] : [[relativePath, afterSha256]])));
|
|
365
|
+
const artifact = await engine.artifacts.putChunked(workspace, autonomous
|
|
366
|
+
? `${transaction.operationKind}.post-validation.json`
|
|
367
|
+
: `${transaction.transactionId}.post-validation.json`, 'application/json', `${canonicalJson({
|
|
368
|
+
schemaVersion: 1,
|
|
369
|
+
...(autonomous
|
|
370
|
+
? { execution: 'autonomous-rewrite' }
|
|
371
|
+
: {
|
|
372
|
+
transactionId: transaction.transactionId,
|
|
373
|
+
planHash: transaction.planHash,
|
|
374
|
+
}),
|
|
375
|
+
operationKind: transaction.operationKind,
|
|
376
|
+
diagnosticCount: diagnostics.length,
|
|
377
|
+
checks,
|
|
378
|
+
diagnostics,
|
|
379
|
+
})}\n`, {
|
|
380
|
+
kind: autonomous ? 'autonomous-rewrite-post-validation' : 'transaction-post-validation',
|
|
381
|
+
toolVersion: PACKAGE_VERSION,
|
|
382
|
+
schemaVersion: autonomous
|
|
383
|
+
? 'autonomous-rewrite-post-validation.v1'
|
|
384
|
+
: 'transaction-post-validation.v1',
|
|
385
|
+
sourceHashes: sourceEvidence.sourceHashes,
|
|
386
|
+
metadata: { sourceHashInventory: sourceEvidence.inventory },
|
|
387
|
+
}, 'Complete source-linked diagnostics from post-write transaction validation', signal);
|
|
388
|
+
const retainedLimit = Math.max(0, availableDiagnostics - 1);
|
|
389
|
+
const hard = diagnostics.filter(({ severity }) => severity === 'error' || severity === 'blocker');
|
|
390
|
+
const retainedHard = hard.slice(0, retainedLimit);
|
|
391
|
+
const retainedSoft = diagnostics
|
|
392
|
+
.filter(({ severity }) => severity !== 'error' && severity !== 'blocker')
|
|
393
|
+
.slice(0, retainedLimit - retainedHard.length);
|
|
394
|
+
const omittedHard = hard.length - retainedHard.length;
|
|
395
|
+
const artifactLink = publicArtifactLink(artifact);
|
|
396
|
+
checks.push({
|
|
397
|
+
id: 'post-write-diagnostics-resource',
|
|
398
|
+
passed: omittedHard === 0,
|
|
399
|
+
message: `Complete post-write diagnostics are stored at ${artifactLink.uri}`,
|
|
400
|
+
});
|
|
401
|
+
return {
|
|
402
|
+
diagnostics: availableDiagnostics === 0
|
|
403
|
+
? []
|
|
404
|
+
: [
|
|
405
|
+
...retainedHard,
|
|
406
|
+
...retainedSoft,
|
|
407
|
+
{
|
|
408
|
+
code: 'POST_VALIDATION_DIAGNOSTICS_IN_RESOURCE',
|
|
409
|
+
severity: omittedHard > 0 ? 'blocker' : 'warning',
|
|
410
|
+
category: 'transaction',
|
|
411
|
+
message: `Complete post-write validation contains ${diagnostics.length} diagnostics and is stored as an MCP resource`,
|
|
412
|
+
details: {
|
|
413
|
+
total: diagnostics.length,
|
|
414
|
+
returned: availableDiagnostics,
|
|
415
|
+
omitted: diagnostics.length - retainedLimit,
|
|
416
|
+
omittedHard,
|
|
417
|
+
artifact: artifactLink,
|
|
418
|
+
},
|
|
419
|
+
},
|
|
420
|
+
],
|
|
421
|
+
checks,
|
|
422
|
+
artifacts: [artifactLink],
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
return { diagnostics, checks, artifacts: [] };
|
|
357
426
|
}
|
|
358
427
|
export function registerBaseTools(server, engine, context) {
|
|
359
428
|
server.registerTool('hoi4.project_register', {
|
|
@@ -499,7 +568,7 @@ export function registerBaseTools(server, engine, context) {
|
|
|
499
568
|
passed: true,
|
|
500
569
|
message: snapshot.complete
|
|
501
570
|
? `${snapshot.index.symbols.length} symbols indexed from a complete inventory`
|
|
502
|
-
: `${snapshot.index.symbols.length} symbols indexed; ${snapshot.skippedSourceCount} over-limit source(s) were
|
|
571
|
+
: `${snapshot.index.symbols.length} symbols indexed from an incomplete inventory; ${snapshot.skippedSourceCount} over-limit source(s) were recorded and aggregate index ceilings may also apply`,
|
|
503
572
|
},
|
|
504
573
|
],
|
|
505
574
|
};
|
|
@@ -510,229 +579,239 @@ export function registerBaseTools(server, engine, context) {
|
|
|
510
579
|
return errorResult(error, workspaceId);
|
|
511
580
|
}
|
|
512
581
|
});
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
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;
|
|
568
646
|
}
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
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);
|
|
572
689
|
}
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
const nextArtifactOffset = artifactOffset + artifacts.length;
|
|
590
|
-
const nextCursor = nextFileOffset < manifest.files.length ||
|
|
591
|
-
nextOperationOffset < manifest.operations.length ||
|
|
592
|
-
nextArtifactOffset < manifest.artifacts.length
|
|
593
|
-
? encodeTransactionDiffCursor(manifest.planHash, nextFileOffset, nextOperationOffset, nextArtifactOffset)
|
|
594
|
-
: undefined;
|
|
595
|
-
const result = emptyServiceResult(workspaceId, {
|
|
596
|
-
files,
|
|
597
|
-
operations: operations.map(({ id, kind, summary }) => ({ id, kind, summary })),
|
|
598
|
-
fileCount: manifest.files.length,
|
|
599
|
-
operationCount: manifest.operations.length,
|
|
600
|
-
artifactCount: manifest.artifacts.length,
|
|
601
|
-
returnedFiles: files.length,
|
|
602
|
-
returnedOperations: operations.length,
|
|
603
|
-
returnedArtifacts: artifacts.length,
|
|
604
|
-
...(nextCursor === undefined ? {} : { nextCursor }),
|
|
605
|
-
expiresAt: manifest.expiresAt,
|
|
606
|
-
});
|
|
607
|
-
result.code = 'TRANSACTION_DIFF';
|
|
608
|
-
result.transactionId = manifest.transactionId;
|
|
609
|
-
result.planHash = manifest.planHash;
|
|
610
|
-
result.proposedFiles = files.map(({ relativePath }) => relativePath);
|
|
611
|
-
result.artifacts = [transactionResourceLink(manifest), ...artifacts];
|
|
612
|
-
result.validation = manifest.validation;
|
|
613
|
-
result.rollbackStatus = manifest.rollbackStatus;
|
|
614
|
-
return toolResult(result);
|
|
615
|
-
}
|
|
616
|
-
catch (error) {
|
|
617
|
-
return errorResult(error, workspaceId);
|
|
618
|
-
}
|
|
619
|
-
});
|
|
620
|
-
server.registerTool('hoi4.transaction_apply', {
|
|
621
|
-
title: 'Apply reviewed transaction',
|
|
622
|
-
description: 'Apply a completed dry run only when transaction ID, workspace, principal, source hashes, and expected plan hash still match.',
|
|
623
|
-
inputSchema: transactionInput,
|
|
624
|
-
outputSchema: transactionMutationOutputSchema,
|
|
625
|
-
annotations: {
|
|
626
|
-
readOnlyHint: false,
|
|
627
|
-
destructiveHint: true,
|
|
628
|
-
idempotentHint: false,
|
|
629
|
-
openWorldHint: false,
|
|
630
|
-
},
|
|
631
|
-
}, async ({ workspaceId, transactionId, expectedPlanHash }, extra) => {
|
|
632
|
-
try {
|
|
633
|
-
requireServerScope(context, 'hoi4:write');
|
|
634
|
-
const progress = progressReporter(extra);
|
|
635
|
-
await progress.report(0, 3, 'Checking transaction preconditions');
|
|
636
|
-
const manifest = await engine.transactions.apply(workspaceId, transactionId, expectedPlanHash, {
|
|
637
|
-
...(context.principal === undefined ? {} : { principal: context.principal }),
|
|
638
|
-
signal: progress.signal,
|
|
639
|
-
postValidate: (transaction, signal) => postValidateTransaction(engine, transaction, context.principal, signal),
|
|
640
|
-
});
|
|
641
|
-
engine.invalidate(workspaceId);
|
|
642
|
-
await engine.scan(workspaceId, {}, context.principal, progress.signal);
|
|
643
|
-
await progress.report(3, 3, 'Apply and index rebuild complete');
|
|
644
|
-
const result = emptyServiceResult(workspaceId, {
|
|
645
|
-
state: manifest.state,
|
|
646
|
-
fileCount: manifest.files.length,
|
|
647
|
-
appliedFileCount: manifest.appliedFiles.length,
|
|
648
|
-
artifactCount: manifest.artifacts.length,
|
|
649
|
-
});
|
|
650
|
-
result.code = 'TRANSACTION_APPLIED';
|
|
651
|
-
result.transactionId = manifest.transactionId;
|
|
652
|
-
result.planHash = manifest.planHash;
|
|
653
|
-
result.proposedFiles = manifest.files.slice(0, 100).map(({ relativePath }) => relativePath);
|
|
654
|
-
result.changedFiles = manifest.appliedFiles.slice(0, 100);
|
|
655
|
-
result.artifacts = [transactionResourceLink(manifest)];
|
|
656
|
-
result.validation = manifest.validation;
|
|
657
|
-
result.rollbackStatus = manifest.rollbackStatus;
|
|
658
|
-
return toolResult(result);
|
|
659
|
-
}
|
|
660
|
-
catch (error) {
|
|
661
|
-
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) => {
|
|
662
706
|
try {
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
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),
|
|
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,
|
|
673
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);
|
|
674
735
|
}
|
|
675
|
-
catch {
|
|
676
|
-
|
|
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);
|
|
677
755
|
}
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
},
|
|
692
|
-
}, async ({ workspaceId, transactionId, expectedPlanHash }, extra) => {
|
|
693
|
-
try {
|
|
694
|
-
requireServerScope(context, 'hoi4:write');
|
|
695
|
-
const progress = progressReporter(extra);
|
|
696
|
-
await progress.report(0, 2, 'Checking rollback preconditions');
|
|
697
|
-
progress.signal.throwIfAborted();
|
|
698
|
-
const manifest = await engine.transactions.rollback(workspaceId, transactionId, expectedPlanHash, context.principal, progress.signal);
|
|
699
|
-
engine.invalidate(workspaceId);
|
|
700
|
-
const result = emptyServiceResult(workspaceId, {
|
|
701
|
-
state: manifest.state,
|
|
702
|
-
fileCount: manifest.files.length,
|
|
703
|
-
appliedFileCount: manifest.appliedFiles.length,
|
|
704
|
-
artifactCount: manifest.artifacts.length,
|
|
705
|
-
});
|
|
706
|
-
result.code = 'TRANSACTION_ROLLED_BACK';
|
|
707
|
-
result.transactionId = manifest.transactionId;
|
|
708
|
-
result.planHash = manifest.planHash;
|
|
709
|
-
result.changedFiles = manifest.files.slice(0, 100).map(({ relativePath }) => relativePath);
|
|
710
|
-
result.artifacts = [transactionResourceLink(manifest)];
|
|
711
|
-
result.validation = manifest.validation;
|
|
712
|
-
result.rollbackStatus = manifest.rollbackStatus;
|
|
713
|
-
await progress.report(2, 2, 'Atomic rollback complete');
|
|
714
|
-
return toolResult(result);
|
|
715
|
-
}
|
|
716
|
-
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) => {
|
|
717
769
|
try {
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
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,
|
|
728
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);
|
|
729
793
|
}
|
|
730
|
-
catch {
|
|
731
|
-
|
|
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);
|
|
732
812
|
}
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
});
|
|
813
|
+
});
|
|
814
|
+
}
|
|
736
815
|
server.registerTool('hoi4.artifact_list', {
|
|
737
816
|
title: 'List workspace artifacts',
|
|
738
817
|
description: 'List content-addressed artifacts without exposing filesystem paths.',
|