hoi4-agent-tools 3.0.9 → 3.1.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.
Files changed (124) hide show
  1. package/CHANGELOG.md +23 -1
  2. package/README.md +4 -0
  3. package/dist/bin/stdio.js +23 -12
  4. package/dist/hoi4_agent_tools/core/analysis-checkpoints.js +82 -0
  5. package/dist/hoi4_agent_tools/core/artifact-resource.js +173 -0
  6. package/dist/hoi4_agent_tools/core/artifacts.js +84 -2
  7. package/dist/hoi4_agent_tools/core/dependency-pages.js +216 -0
  8. package/dist/hoi4_agent_tools/core/dependency-walk.js +266 -0
  9. package/dist/hoi4_agent_tools/core/domain-validation.js +188 -0
  10. package/dist/hoi4_agent_tools/core/engine.js +48 -5
  11. package/dist/hoi4_agent_tools/core/helper-expansion.js +186 -0
  12. package/dist/hoi4_agent_tools/core/index-segments.js +137 -0
  13. package/dist/hoi4_agent_tools/core/index.js +47 -4
  14. package/dist/hoi4_agent_tools/core/job-controls.js +109 -0
  15. package/dist/hoi4_agent_tools/core/job-executor.js +190 -0
  16. package/dist/hoi4_agent_tools/core/job-service.js +112 -0
  17. package/dist/hoi4_agent_tools/core/job-store.js +641 -0
  18. package/dist/hoi4_agent_tools/core/job-transactions.js +126 -0
  19. package/dist/hoi4_agent_tools/core/job-worker-host.js +200 -0
  20. package/dist/hoi4_agent_tools/core/job-worker.js +62 -0
  21. package/dist/hoi4_agent_tools/core/operation-context.js +31 -0
  22. package/dist/hoi4_agent_tools/core/operation-result.js +412 -0
  23. package/dist/hoi4_agent_tools/core/operation-tasks.js +382 -0
  24. package/dist/hoi4_agent_tools/core/persistent-analysis-cache.js +383 -0
  25. package/dist/hoi4_agent_tools/core/result.js +1 -0
  26. package/dist/hoi4_agent_tools/core/scanner.js +15 -75
  27. package/dist/hoi4_agent_tools/core/semantic-dependencies.js +233 -0
  28. package/dist/hoi4_agent_tools/core/shared-request-capacity.js +43 -11
  29. package/dist/hoi4_agent_tools/core/source/cache.js +107 -0
  30. package/dist/hoi4_agent_tools/core/source/parser.js +11 -1
  31. package/dist/hoi4_agent_tools/core/transaction-execution.js +134 -0
  32. package/dist/hoi4_agent_tools/core/transactions.js +80 -27
  33. package/dist/hoi4_agent_tools/event/graph.js +214 -290
  34. package/dist/hoi4_agent_tools/event/helper-expansion.js +33 -0
  35. package/dist/hoi4_agent_tools/event/job-operations.js +51 -0
  36. package/dist/hoi4_agent_tools/event/operations.js +179 -0
  37. package/dist/hoi4_agent_tools/event/service.js +67 -15
  38. package/dist/hoi4_agent_tools/event/source-analysis.js +5 -4
  39. package/dist/hoi4_agent_tools/focus/job-operations.js +77 -0
  40. package/dist/hoi4_agent_tools/focus/tool-operations.js +669 -0
  41. package/dist/hoi4_agent_tools/focus/tool-support.js +123 -0
  42. package/dist/hoi4_agent_tools/gui/job-operations.js +81 -0
  43. package/dist/hoi4_agent_tools/gui/tool-operations.js +270 -0
  44. package/dist/hoi4_agent_tools/map/job-operations.js +76 -0
  45. package/dist/hoi4_agent_tools/map/tool-operations.js +493 -0
  46. package/dist/hoi4_agent_tools/mcp/prompts/probability.js +25 -0
  47. package/dist/hoi4_agent_tools/mcp/prompts/register-modern.js +29 -0
  48. package/dist/hoi4_agent_tools/mcp/resources/register-modern.js +34 -0
  49. package/dist/hoi4_agent_tools/mcp/resources/register.js +4 -150
  50. package/dist/hoi4_agent_tools/mcp/server/base-tools.js +3 -219
  51. package/dist/hoi4_agent_tools/mcp/server/create-modern-operations.js +188 -0
  52. package/dist/hoi4_agent_tools/mcp/server/create.js +16 -6
  53. package/dist/hoi4_agent_tools/mcp/server/instructions.js +2 -0
  54. package/dist/hoi4_agent_tools/mcp/server/job-task-store.js +67 -0
  55. package/dist/hoi4_agent_tools/mcp/server/legacy-task-tools.js +20 -0
  56. package/dist/hoi4_agent_tools/mcp/server/modern-tasks.js +168 -0
  57. package/dist/hoi4_agent_tools/mcp/server/progress.js +17 -15
  58. package/dist/hoi4_agent_tools/mcp/server/request-lifecycle.js +43 -0
  59. package/dist/hoi4_agent_tools/mcp/server/result.js +2 -412
  60. package/dist/hoi4_agent_tools/mcp/server/task-request-context.js +9 -0
  61. package/dist/hoi4_agent_tools/mcp/server/task-tool-catalog.js +14 -0
  62. package/dist/hoi4_agent_tools/mcp/server/task-tool-definition.js +1 -0
  63. package/dist/hoi4_agent_tools/mcp/server/transaction-execution.js +1 -123
  64. package/dist/hoi4_agent_tools/mcp/tools/chaosx.js +38 -21
  65. package/dist/hoi4_agent_tools/mcp/tools/event.js +24 -315
  66. package/dist/hoi4_agent_tools/mcp/tools/focus.js +34 -1039
  67. package/dist/hoi4_agent_tools/mcp/tools/gui.js +19 -504
  68. package/dist/hoi4_agent_tools/mcp/tools/job.js +46 -0
  69. package/dist/hoi4_agent_tools/mcp/tools/map.js +21 -600
  70. package/dist/hoi4_agent_tools/mcp/tools/probability.js +39 -463
  71. package/dist/hoi4_agent_tools/mcp/tools/technology.js +40 -304
  72. package/dist/hoi4_agent_tools/mcp/transports/http.js +58 -1
  73. package/dist/hoi4_agent_tools/mcp/transports/modern-task-routing.js +109 -0
  74. package/dist/hoi4_agent_tools/mcp/transports/negotiated-stdio.js +121 -0
  75. package/dist/hoi4_agent_tools/probability/job-operations.js +58 -0
  76. package/dist/hoi4_agent_tools/probability/operations.js +183 -0
  77. package/dist/hoi4_agent_tools/probability/service.js +24 -3
  78. package/dist/hoi4_agent_tools/schemas/common.js +2 -0
  79. package/dist/hoi4_agent_tools/schemas/event.js +72 -1
  80. package/dist/hoi4_agent_tools/schemas/focus-requests.js +189 -0
  81. package/dist/hoi4_agent_tools/schemas/gui-requests.js +193 -0
  82. package/dist/hoi4_agent_tools/schemas/helper-expansion.js +58 -0
  83. package/dist/hoi4_agent_tools/schemas/map-requests.js +85 -0
  84. package/dist/hoi4_agent_tools/schemas/probability-requests.js +168 -0
  85. package/dist/hoi4_agent_tools/schemas/technology.js +90 -1
  86. package/dist/hoi4_agent_tools/schemas/transaction.js +1 -0
  87. package/dist/hoi4_agent_tools/technology/graph.js +83 -78
  88. package/dist/hoi4_agent_tools/technology/helper-expansion.js +33 -0
  89. package/dist/hoi4_agent_tools/technology/job-operations.js +60 -0
  90. package/dist/hoi4_agent_tools/technology/operations.js +125 -0
  91. package/dist/hoi4_agent_tools/technology/service.js +89 -15
  92. package/dist/hoi4_agent_tools/technology/source-analysis.js +2 -1
  93. package/dist/hoi4_agent_tools/version.js +1 -1
  94. package/docs/README.md +2 -0
  95. package/docs/development.md +2 -0
  96. package/docs/events.md +7 -2
  97. package/docs/helper-expansion.md +78 -0
  98. package/docs/http.md +2 -0
  99. package/docs/jobs.md +85 -0
  100. package/docs/technology.md +6 -2
  101. package/package.json +11 -5
  102. package/schemas/configuration.schema.json +1 -1
  103. package/schemas/continuous-focus-palette.schema.json +1 -1
  104. package/schemas/custom-weighted-pool.schema.json +1 -1
  105. package/schemas/event-feature-manifest.schema.json +1 -1
  106. package/schemas/focus-plan.schema.json +1 -1
  107. package/schemas/focus-planning-sidecar.schema.json +1 -1
  108. package/schemas/gui-animation-source.schema.json +1 -1
  109. package/schemas/gui-helper.schema.json +1 -1
  110. package/schemas/gui-scenario.schema.json +1 -1
  111. package/schemas/helper-expansion-request.schema.json +44 -0
  112. package/schemas/helper-expansion-summary.schema.json +70 -0
  113. package/schemas/map-operation.schema.json +1 -1
  114. package/schemas/operation-result.schema.json +1 -1
  115. package/schemas/probability-analysis-result.schema.json +1 -1
  116. package/schemas/probability-compare-input.schema.json +1 -1
  117. package/schemas/probability-evaluate-input.schema.json +1 -1
  118. package/schemas/probability-inspect-input.schema.json +1 -1
  119. package/schemas/probability-render-input.schema.json +1 -1
  120. package/schemas/probability-scenario-set.schema.json +1 -1
  121. package/schemas/probability-sequence-input.schema.json +1 -1
  122. package/schemas/probability-simulate-input.schema.json +1 -1
  123. package/schemas/probability-sweep-input.schema.json +1 -1
  124. package/server.json +2 -2
@@ -1,412 +1,2 @@
1
- import { z } from 'zod/v4';
2
- import { canonicalJson } from '../../core/canonical.js';
3
- import { ServiceError } from '../../core/result.js';
4
- import { diagnosticSchema } from '../../schemas/common.js';
5
- import { artifactLinkSchema, validationSummarySchema } from '../../schemas/transaction.js';
6
- export const MAX_INLINE_FILES_SCANNED = 64;
7
- export const MAX_INLINE_PROPOSED_FILES = 64;
8
- export const MAX_INLINE_CHANGED_FILES = 64;
9
- export const MAX_INLINE_DIAGNOSTICS = 20;
10
- export const MAX_INLINE_ARTIFACT_LINKS = 32;
11
- export const MAX_INLINE_BLOCKERS = 20;
12
- export const MAX_INLINE_VALIDATION_CHECKS = 5;
13
- // Leave headroom for the enclosing JSON-RPC response and transport metadata.
14
- export const MAX_TOOL_RESULT_BYTES = 32_768;
15
- const deferredFilesDiagnostic = Symbol('deferredFilesDiagnostic');
16
- const operationResultBaseSchema = z
17
- .object({
18
- status: z.enum(['ok', 'blocked', 'error']),
19
- code: z.string().max(256),
20
- workspaceId: z.string().max(64),
21
- filesScanned: z.array(z.string().max(4096)).max(MAX_INLINE_FILES_SCANNED),
22
- proposedFiles: z.array(z.string().max(1024)).max(MAX_INLINE_PROPOSED_FILES),
23
- changedFiles: z.array(z.string().max(1024)).max(MAX_INLINE_CHANGED_FILES),
24
- diagnostics: z.array(diagnosticSchema).max(MAX_INLINE_DIAGNOSTICS),
25
- artifacts: z.array(artifactLinkSchema).max(MAX_INLINE_ARTIFACT_LINKS),
26
- validation: validationSummarySchema,
27
- blockers: z
28
- .array(z
29
- .object({
30
- code: z.string().max(256),
31
- message: z.string().max(4096),
32
- details: z.record(z.string().max(256), z.unknown()).optional(),
33
- })
34
- .strict())
35
- .max(MAX_INLINE_BLOCKERS),
36
- })
37
- .strict();
38
- export const operationResultSchema = operationResultBaseSchema
39
- .extend({ data: z.record(z.string().max(256), z.unknown()) })
40
- .strict();
41
- const emptyToolDataSchema = z.object({}).strict();
42
- const compactPublicOperationResultSchema = z
43
- .object({
44
- status: z.enum(['ok', 'blocked', 'error']),
45
- code: z.string(),
46
- workspaceId: z.string(),
47
- filesScanned: z.array(z.unknown()),
48
- proposedFiles: z.array(z.unknown()),
49
- changedFiles: z.array(z.unknown()),
50
- diagnostics: z.array(z.unknown()).max(MAX_INLINE_DIAGNOSTICS),
51
- artifacts: z.array(z.unknown()).max(MAX_INLINE_ARTIFACT_LINKS),
52
- validation: z.unknown(),
53
- blockers: z.array(z.unknown()),
54
- data: z.object({}).catchall(z.unknown()),
55
- })
56
- .strict();
57
- /**
58
- * Advertise one compact operation envelope while retaining exact per-tool runtime validation.
59
- * The generic operation-result schema remains available for stored envelopes.
60
- */
61
- export function strictOperationResultSchema(successDataSchema) {
62
- const exactSchema = operationResultBaseSchema
63
- .extend({ data: z.union([emptyToolDataSchema, successDataSchema]) })
64
- .strict();
65
- return compactPublicOperationResultSchema.superRefine((value, context) => {
66
- const parsed = exactSchema.safeParse(value);
67
- if (parsed.success)
68
- return;
69
- for (const issue of parsed.error.issues) {
70
- context.addIssue({ code: 'custom', path: issue.path, message: issue.message });
71
- }
72
- });
73
- }
74
- /** Keep source inventories compact; complete inventories belong in linked resources. */
75
- export function setInlineFilesScanned(result, files) {
76
- result.filesScanned = files.slice(0, MAX_INLINE_FILES_SCANNED);
77
- if (files.length <= MAX_INLINE_FILES_SCANNED)
78
- return;
79
- const truncation = {
80
- code: 'MCP_INLINE_FILES_TRUNCATED',
81
- severity: 'info',
82
- category: 'configuration',
83
- message: `Inline source inventory is limited to ${MAX_INLINE_FILES_SCANNED} paths; use the linked resource for the complete inventory`,
84
- details: {
85
- total: files.length,
86
- returned: MAX_INLINE_FILES_SCANNED,
87
- },
88
- };
89
- Object.defineProperty(result, deferredFilesDiagnostic, {
90
- value: truncation,
91
- enumerable: false,
92
- configurable: true,
93
- });
94
- result.diagnostics = [truncation, ...result.diagnostics].slice(0, MAX_INLINE_DIAGNOSTICS);
95
- }
96
- function preserveDeferredFilesDiagnostic(result) {
97
- const deferred = result[deferredFilesDiagnostic];
98
- if (deferred === undefined || result.diagnostics.some(({ code }) => code === deferred.code)) {
99
- return result;
100
- }
101
- return {
102
- ...result,
103
- diagnostics: [deferred, ...result.diagnostics].slice(0, MAX_INLINE_DIAGNOSTICS),
104
- };
105
- }
106
- function boundedResult(result, actualBytes) {
107
- const hasLinkedEvidence = result.artifacts.length > 0;
108
- const truncationDiagnostic = {
109
- code: 'MCP_RESPONSE_TRUNCATED',
110
- severity: 'warning',
111
- category: 'configuration',
112
- message: hasLinkedEvidence
113
- ? 'Tool output exceeded the wire budget; inspect the linked evidence for complete details'
114
- : 'Tool output exceeded the wire budget; nonessential inline details were omitted',
115
- details: { actualBytes, maxBytes: MAX_TOOL_RESULT_BYTES },
116
- };
117
- return {
118
- status: result.status,
119
- code: result.code.slice(0, 256),
120
- workspaceId: result.workspaceId.slice(0, 64),
121
- filesScanned: [],
122
- proposedFiles: [],
123
- changedFiles: [],
124
- diagnostics: [truncationDiagnostic],
125
- artifacts: result.artifacts
126
- .slice(0, 1)
127
- .map(({ description: _description, ...artifact }) => artifact),
128
- validation: { passed: result.validation.passed, checks: [] },
129
- blockers: result.status === 'ok'
130
- ? []
131
- : [
132
- {
133
- code: 'MCP_RESPONSE_TRUNCATED',
134
- message: hasLinkedEvidence
135
- ? 'Tool output exceeded the wire budget; inspect the linked evidence for complete details'
136
- : 'Tool output exceeded the wire budget; nonessential inline details were omitted',
137
- details: {
138
- actualBytes,
139
- maxBytes: MAX_TOOL_RESULT_BYTES,
140
- artifactLinksTotal: result.artifacts.length,
141
- },
142
- },
143
- ],
144
- data: {},
145
- };
146
- }
147
- function jsonStringBytes(value) {
148
- let bytes = 2 + Buffer.byteLength(value, 'utf8');
149
- for (let index = 0; index < value.length; index += 1) {
150
- const code = value.charCodeAt(index);
151
- if (code === 0x22 || code === 0x5c)
152
- bytes += 1;
153
- else if (code <= 0x1f)
154
- bytes += [0x08, 0x09, 0x0a, 0x0c, 0x0d].includes(code) ? 1 : 5;
155
- else if (code >= 0xd800 && code <= 0xdbff) {
156
- const next = value.charCodeAt(index + 1);
157
- if (next >= 0xdc00 && next <= 0xdfff)
158
- index += 1;
159
- else
160
- bytes += 3;
161
- }
162
- else if (code >= 0xdc00 && code <= 0xdfff)
163
- bytes += 3;
164
- }
165
- return bytes;
166
- }
167
- function jsonBytesWithin(value, budget) {
168
- let bytes = 0;
169
- const seen = new WeakSet();
170
- const maximumDepth = 128;
171
- const add = (count) => {
172
- bytes += count;
173
- return bytes <= budget;
174
- };
175
- const visit = (candidate, depth = 0) => {
176
- if (depth > maximumDepth)
177
- return false;
178
- if (candidate === null)
179
- return add(4);
180
- if (typeof candidate === 'string')
181
- return add(jsonStringBytes(candidate));
182
- if (typeof candidate === 'number') {
183
- if (!Number.isFinite(candidate))
184
- return false;
185
- return add(String(Object.is(candidate, -0) ? 0 : candidate).length);
186
- }
187
- if (typeof candidate === 'boolean')
188
- return add(candidate ? 4 : 5);
189
- if (Array.isArray(candidate)) {
190
- if (seen.has(candidate))
191
- return false;
192
- seen.add(candidate);
193
- try {
194
- if (!add(2 + Math.max(0, candidate.length - 1)))
195
- return false;
196
- return candidate.every((entry) => visit(entry, depth + 1));
197
- }
198
- finally {
199
- seen.delete(candidate);
200
- }
201
- }
202
- if (typeof candidate === 'object') {
203
- const object = candidate;
204
- if (seen.has(object))
205
- return false;
206
- seen.add(object);
207
- try {
208
- const entries = Object.entries(object).filter(([, entry]) => entry !== undefined);
209
- if (!add(2 + Math.max(0, entries.length - 1)))
210
- return false;
211
- for (const [key, entry] of entries) {
212
- if (!add(jsonStringBytes(key) + 1) || !visit(entry, depth + 1))
213
- return false;
214
- }
215
- return true;
216
- }
217
- finally {
218
- seen.delete(object);
219
- }
220
- }
221
- return false;
222
- };
223
- return visit(value);
224
- }
225
- function compactDetails(details) {
226
- return jsonBytesWithin(details, 2_048) ? details : { truncated: true };
227
- }
228
- function compactDescriptions(result) {
229
- const compactLocation = (location) => location === undefined
230
- ? undefined
231
- : {
232
- ...location,
233
- path: location.path.slice(0, 1_024),
234
- ...(location.symbol === undefined ? {} : { symbol: location.symbol.slice(0, 256) }),
235
- };
236
- const collectionTotals = {
237
- filesScanned: result.filesScanned.length,
238
- proposedFiles: result.proposedFiles.length,
239
- changedFiles: result.changedFiles.length,
240
- diagnostics: result.diagnostics.length,
241
- artifacts: result.artifacts.length,
242
- validationChecks: result.validation.checks.length,
243
- blockers: result.blockers.length,
244
- };
245
- const collectionLimits = {
246
- filesScanned: MAX_INLINE_FILES_SCANNED,
247
- proposedFiles: MAX_INLINE_PROPOSED_FILES,
248
- changedFiles: MAX_INLINE_CHANGED_FILES,
249
- diagnostics: MAX_INLINE_DIAGNOSTICS,
250
- artifacts: MAX_INLINE_ARTIFACT_LINKS,
251
- validationChecks: MAX_INLINE_VALIDATION_CHECKS,
252
- blockers: MAX_INLINE_BLOCKERS,
253
- };
254
- const truncatedCollections = Object.fromEntries(Object.entries(collectionTotals).flatMap(([name, total]) => {
255
- const limit = collectionLimits[name];
256
- return total > limit ? [[name, { total, limit }]] : [];
257
- }));
258
- if (Object.keys(truncatedCollections).length > 0 &&
259
- result.diagnostics.length > MAX_INLINE_DIAGNOSTICS - 1) {
260
- truncatedCollections.diagnostics = {
261
- total: result.diagnostics.length,
262
- limit: MAX_INLINE_DIAGNOSTICS - 1,
263
- };
264
- }
265
- const needsTruncationDiagnostic = Object.keys(truncatedCollections).length > 0;
266
- const diagnosticSlots = Math.max(0, MAX_INLINE_DIAGNOSTICS - (needsTruncationDiagnostic ? 1 : 0));
267
- const hardDiagnostics = result.diagnostics.filter(({ severity }) => severity === 'error' || severity === 'blocker');
268
- const priorityDiagnostics = result.diagnostics.filter(({ code, severity }) => severity === 'error' || severity === 'blocker' || code === 'MCP_INLINE_FILES_TRUNCATED');
269
- const otherDiagnostics = result.diagnostics.filter((diagnostic) => !priorityDiagnostics.includes(diagnostic));
270
- const retainedPriorityDiagnostics = priorityDiagnostics.slice(0, diagnosticSlots);
271
- const retainedDiagnostics = [
272
- ...retainedPriorityDiagnostics,
273
- ...otherDiagnostics.slice(0, diagnosticSlots - retainedPriorityDiagnostics.length),
274
- ];
275
- const omittedHardDiagnostics = hardDiagnostics.filter((diagnostic) => !retainedDiagnostics.includes(diagnostic)).length;
276
- const compactDiagnostics = retainedDiagnostics.map((diagnostic) => ({
277
- ...diagnostic,
278
- code: diagnostic.code.slice(0, 256),
279
- message: diagnostic.message.slice(0, 1_024),
280
- ...(diagnostic.operationId === undefined
281
- ? {}
282
- : { operationId: diagnostic.operationId.slice(0, 256) }),
283
- ...(diagnostic.location === undefined
284
- ? {}
285
- : { location: compactLocation(diagnostic.location) }),
286
- ...(diagnostic.related === undefined
287
- ? {}
288
- : {
289
- related: diagnostic.related.slice(0, 3).map((location) => compactLocation(location)),
290
- }),
291
- ...(diagnostic.details === undefined ? {} : { details: compactDetails(diagnostic.details) }),
292
- }));
293
- const truncationDiagnostic = needsTruncationDiagnostic
294
- ? {
295
- code: 'MCP_INLINE_COLLECTIONS_TRUNCATED',
296
- severity: omittedHardDiagnostics > 0 ? 'blocker' : 'info',
297
- category: 'configuration',
298
- message: result.artifacts.length > 0
299
- ? 'Large collections were bounded inline; linked resources retain bulk evidence'
300
- : 'Large collections were bounded inline',
301
- details: {
302
- collections: truncatedCollections,
303
- diagnosticsReturned: retainedDiagnostics.length,
304
- omittedHardDiagnostics,
305
- },
306
- }
307
- : undefined;
308
- return {
309
- ...result,
310
- filesScanned: result.filesScanned.slice(0, MAX_INLINE_FILES_SCANNED),
311
- proposedFiles: result.proposedFiles.slice(0, MAX_INLINE_PROPOSED_FILES),
312
- changedFiles: result.changedFiles.slice(0, MAX_INLINE_CHANGED_FILES),
313
- diagnostics: truncationDiagnostic === undefined
314
- ? compactDiagnostics
315
- : [truncationDiagnostic, ...compactDiagnostics],
316
- artifacts: result.artifacts.slice(0, MAX_INLINE_ARTIFACT_LINKS).map((artifact) => ({
317
- ...artifact,
318
- ...(artifact.description === undefined
319
- ? {}
320
- : { description: artifact.description.slice(0, 256) }),
321
- })),
322
- validation: {
323
- ...result.validation,
324
- checks: result.validation.checks.slice(0, MAX_INLINE_VALIDATION_CHECKS).map((check) => ({
325
- ...check,
326
- id: check.id.slice(0, 256),
327
- message: check.message.slice(0, 1_024),
328
- })),
329
- },
330
- blockers: result.blockers.slice(0, MAX_INLINE_BLOCKERS).map((blocker) => ({
331
- ...blocker,
332
- code: blocker.code.slice(0, 256),
333
- message: blocker.message.slice(0, 1_024),
334
- ...(blocker.details === undefined ? {} : { details: compactDetails(blocker.details) }),
335
- })),
336
- };
337
- }
338
- function buildToolResult(selected) {
339
- const structuredContent = operationResultSchema.parse(selected);
340
- const links = selected.artifacts.map((artifact) => ({
341
- type: 'resource_link',
342
- uri: artifact.uri,
343
- name: artifact.name,
344
- mimeType: artifact.mimeType,
345
- ...(artifact.description === undefined ? {} : { description: artifact.description }),
346
- ...(artifact.size === undefined ? {} : { size: artifact.size }),
347
- }));
348
- const summary = canonicalJson({
349
- status: selected.status,
350
- code: selected.code,
351
- workspaceId: selected.workspaceId,
352
- artifactCount: selected.artifacts.length,
353
- });
354
- return {
355
- content: [{ type: 'text', text: summary }, ...links],
356
- structuredContent,
357
- ...(selected.status === 'error' ? { isError: true } : {}),
358
- };
359
- }
360
- export function toolResult(result) {
361
- const completeResult = preserveDeferredFilesDiagnostic(result);
362
- const compact = compactDescriptions(completeResult);
363
- const estimatedBytes = jsonBytesWithin(compact, MAX_TOOL_RESULT_BYTES)
364
- ? Buffer.byteLength(canonicalJson(compact), 'utf8')
365
- : MAX_TOOL_RESULT_BYTES + 1;
366
- const selected = estimatedBytes > MAX_TOOL_RESULT_BYTES ? boundedResult(compact, estimatedBytes) : compact;
367
- let output = buildToolResult(selected);
368
- const wireBytes = Buffer.byteLength(canonicalJson(output), 'utf8');
369
- if (wireBytes > MAX_TOOL_RESULT_BYTES) {
370
- output = buildToolResult(boundedResult(compact, wireBytes));
371
- }
372
- const finalBytes = Buffer.byteLength(canonicalJson(output), 'utf8');
373
- if (finalBytes > MAX_TOOL_RESULT_BYTES) {
374
- throw new Error('Constant MCP response-limit result exceeded its wire budget');
375
- }
376
- return output;
377
- }
378
- export function errorResult(error, workspaceId = '', context = {}) {
379
- const serviceError = error instanceof ServiceError
380
- ? error
381
- : new ServiceError('INTERNAL_ERROR', 'Unexpected internal error');
382
- const internalWriteError = serviceError.code.startsWith('TRANSACTION_');
383
- const publicCode = internalWriteError
384
- ? `REWRITE_${serviceError.code.slice('TRANSACTION_'.length)}`
385
- : serviceError.code;
386
- const publicMessage = internalWriteError
387
- ? 'Rewrite could not be completed'
388
- : serviceError.message;
389
- const publicDetails = internalWriteError
390
- ? {}
391
- : Object.fromEntries(Object.entries(serviceError.details).filter(([key]) => ![
392
- 'cause',
393
- 'stack',
394
- 'absolutePath',
395
- 'transactionId',
396
- 'planHash',
397
- 'rollbackStatus',
398
- ].includes(key)));
399
- return toolResult({
400
- status: publicCode.includes('BLOCKED') ? 'blocked' : 'error',
401
- code: publicCode,
402
- workspaceId,
403
- filesScanned: [],
404
- proposedFiles: context.proposedFiles ?? [],
405
- changedFiles: context.changedFiles ?? [],
406
- diagnostics: [],
407
- artifacts: context.artifacts ?? [],
408
- validation: context.validation ?? { passed: false, checks: [] },
409
- blockers: [{ code: publicCode, message: publicMessage, details: publicDetails }],
410
- data: context.data ?? {},
411
- });
412
- }
1
+ /** Compatibility exports; foreground and background operations share the core result contract. */
2
+ export * from '../../core/operation-result.js';
@@ -0,0 +1,9 @@
1
+ import { AsyncLocalStorage } from 'node:async_hooks';
2
+ const taskRequestContext = new AsyncLocalStorage();
3
+ /** Bind transport cancellation to foreground-compatible task creation only. */
4
+ export function withTaskRequestSignal(signal, action) {
5
+ return taskRequestContext.run({ signal }, action);
6
+ }
7
+ export function currentTaskRequestSignal() {
8
+ return taskRequestContext.getStore()?.signal;
9
+ }
@@ -0,0 +1,14 @@
1
+ import { eventTaskTools } from '../tools/event.js';
2
+ import { focusTaskTools } from '../tools/focus.js';
3
+ import { guiTaskTools } from '../tools/gui.js';
4
+ import { mapTaskTools } from '../tools/map.js';
5
+ import { probabilityTaskTools } from '../tools/probability.js';
6
+ import { technologyTaskTools } from '../tools/technology.js';
7
+ export const taskToolCatalog = [
8
+ ...focusTaskTools,
9
+ ...guiTaskTools,
10
+ ...mapTaskTools,
11
+ ...eventTaskTools,
12
+ ...technologyTaskTools,
13
+ ...probabilityTaskTools,
14
+ ];
@@ -1,123 +1 @@
1
- import { canonicalJson } from '../../core/canonical.js';
2
- import { boundedSourceHashEvidence, publicArtifactLink } from '../../core/artifacts.js';
3
- import { ServiceError } from '../../core/result.js';
4
- import { PACKAGE_VERSION } from '../../version.js';
5
- import { postValidateTransaction } from './base-tools.js';
6
- import { MAX_INLINE_ARTIFACT_LINKS } from './result.js';
7
- export class AutonomousRewriteError extends ServiceError {
8
- manifest;
9
- artifacts;
10
- constructor(cause, manifest, artifacts = []) {
11
- const restored = manifest.state === 'rolled_back' && manifest.rollbackStatus === 'applied';
12
- const code = cause.code.startsWith('TRANSACTION_')
13
- ? `REWRITE_${cause.code.slice('TRANSACTION_'.length)}`
14
- : cause.code;
15
- super(code, restored
16
- ? 'Rewrite verification failed and the original files were restored'
17
- : 'Rewrite failed and automatic recovery could not be completed', {
18
- execution: 'failed',
19
- automaticRecovery: restored ? 'restored' : 'incomplete',
20
- });
21
- this.manifest = manifest;
22
- this.artifacts = artifacts;
23
- this.name = 'AutonomousRewriteError';
24
- }
25
- }
26
- async function storeAutonomousValidationEvidence(engine, manifest, postValidation, principal, signal) {
27
- const diagnostics = [...manifest.diagnostics, ...postValidation.diagnostics];
28
- const checks = [...manifest.validation.checks, ...postValidation.checks];
29
- const passed = manifest.validation.passed &&
30
- checks.every((check) => check.passed) &&
31
- !diagnostics.some(({ severity }) => severity === 'error' || severity === 'blocker');
32
- const sourceEvidence = boundedSourceHashEvidence(Object.fromEntries(manifest.files.flatMap(({ relativePath, afterSha256 }) => afterSha256 === null ? [] : [[relativePath, afterSha256]])));
33
- const artifact = await engine.artifacts.putChunked(engine.resolver.get(manifest.workspaceId, principal), `${manifest.operationKind}.execution-validation.json`, 'application/json', `${canonicalJson({
34
- schemaVersion: 1,
35
- execution: passed ? 'post-validation-passed' : 'post-validation-failed',
36
- operationKind: manifest.operationKind,
37
- files: manifest.files.map(({ relativePath, afterSha256, afterSize, mediaType }) => ({
38
- relativePath,
39
- afterSha256,
40
- afterSize,
41
- mediaType,
42
- })),
43
- validation: { passed, checks },
44
- diagnostics,
45
- resources: postValidation.artifacts,
46
- })}\n`, {
47
- kind: 'autonomous-rewrite-execution-validation',
48
- toolVersion: PACKAGE_VERSION,
49
- schemaVersion: 'autonomous-rewrite-execution-validation.v1',
50
- sourceHashes: sourceEvidence.sourceHashes,
51
- metadata: {
52
- operationKind: manifest.operationKind,
53
- fileCount: manifest.files.length,
54
- checkCount: checks.length,
55
- diagnosticCount: diagnostics.length,
56
- sourceHashInventory: sourceEvidence.inventory,
57
- },
58
- }, 'Complete rewrite and post-write validation evidence', signal);
59
- return publicArtifactLink(artifact);
60
- }
61
- function uniqueArtifacts(artifacts) {
62
- const unique = new Map();
63
- for (const artifact of artifacts)
64
- unique.set(artifact.uri, artifact);
65
- return [...unique.values()].slice(0, MAX_INLINE_ARTIFACT_LINKS);
66
- }
67
- export function autonomousResultArtifacts(execution) {
68
- return uniqueArtifacts([...execution.artifacts, ...execution.transaction.artifacts]);
69
- }
70
- /**
71
- * Complete a validated domain rewrite inside one MCP tool call. Hash checks, durable recovery,
72
- * post-write validation, and automatic failure recovery remain internal guarantees.
73
- */
74
- export async function executePlannedTransaction(engine, transaction, principal, signal) {
75
- if (!transaction.validation.passed) {
76
- return { transaction, outcome: 'blocked', artifacts: [] };
77
- }
78
- if (transaction.files.length === 0)
79
- return { transaction, outcome: 'unchanged', artifacts: [] };
80
- let executionArtifacts = [];
81
- try {
82
- const applied = await engine.transactions.apply(transaction.workspaceId, transaction.transactionId, transaction.planHash, {
83
- ...(principal === undefined ? {} : { principal }),
84
- ...(signal === undefined ? {} : { signal }),
85
- postValidate: async (manifest, validationSignal) => {
86
- const validation = await postValidateTransaction(engine, manifest, principal, validationSignal);
87
- executionArtifacts = [...validation.artifacts];
88
- executionArtifacts.unshift(await storeAutonomousValidationEvidence(engine, manifest, validation, principal, validationSignal));
89
- return validation;
90
- },
91
- });
92
- // Post-validation already invalidates and rebuilds the shared index before apply can succeed.
93
- return { transaction: applied, outcome: 'applied', artifacts: executionArtifacts };
94
- }
95
- catch (error) {
96
- engine.invalidate(transaction.workspaceId);
97
- try {
98
- const manifest = await engine.transactions.status(transaction.workspaceId, transaction.transactionId, principal);
99
- const cause = error instanceof ServiceError
100
- ? error
101
- : new ServiceError('AUTONOMOUS_REWRITE_FAILED', 'Autonomous rewrite failed');
102
- throw new AutonomousRewriteError(cause, manifest, executionArtifacts);
103
- }
104
- catch (statusError) {
105
- if (statusError instanceof AutonomousRewriteError)
106
- throw statusError;
107
- throw error;
108
- }
109
- }
110
- }
111
- export function autonomousFailureContext(error) {
112
- if (!(error instanceof AutonomousRewriteError))
113
- return undefined;
114
- const manifest = error.manifest;
115
- return {
116
- proposedFiles: manifest.files.slice(0, 100).map(({ relativePath }) => relativePath),
117
- changedFiles: manifest.rollbackStatus === 'failed'
118
- ? manifest.files.slice(0, 100).map(({ relativePath }) => relativePath)
119
- : [],
120
- artifacts: uniqueArtifacts([...error.artifacts, ...manifest.artifacts]),
121
- validation: manifest.validation,
122
- };
123
- }
1
+ export * from '../../core/transaction-execution.js';
@@ -333,24 +333,17 @@ async function resolveGuiRevision(engine, context, studio, cache, workspaceId, s
333
333
  filesScanned: Object.keys(sourceHashes).sort(compareCodeUnits),
334
334
  };
335
335
  }
336
- export function registerChaosxTools(server, engine, context) {
336
+ export function createChaosxToolOperations(engine, context) {
337
337
  const guiStudio = new ScriptedGuiStudio(engine);
338
338
  const guiRevisionCache = new Map();
339
339
  const guiCacheLifetime = new IdleCacheLifetime(() => {
340
340
  guiStudio.clearCaches();
341
341
  guiRevisionCache.clear();
342
342
  });
343
- server.registerTool('chaosx.visual_revision', {
344
- title: 'Check ChaosX visual revisions',
345
- description: 'Private ChaosX cache-coherency endpoint. Computes exact scripted-GUI source revisions without rendering PNG artifacts.',
346
- inputSchema: chaosxVisualRevisionInput,
347
- outputSchema: chaosxVisualRevisionOutput,
348
- annotations: readOnly,
349
- }, async (input, extra) => {
350
- const workspaceId = await resolveServerWorkspaceId(engine, context, input.workspaceId, extra.signal);
343
+ const visualCall = async (input, progress) => {
344
+ const workspaceId = await resolveServerWorkspaceId(engine, context, input.workspaceId, progress.signal);
351
345
  const releaseCaches = guiCacheLifetime.begin();
352
346
  try {
353
- const progress = progressReporter(extra);
354
347
  await progress.report(0, 2, 'Checking ChaosX scripted-GUI sources');
355
348
  const resolvedGuiRevisions = [];
356
349
  for (const selector of input.guiWindows)
@@ -381,17 +374,10 @@ export function registerChaosxTools(server, engine, context) {
381
374
  finally {
382
375
  releaseCaches();
383
376
  }
384
- });
385
- server.registerTool('chaosx.focus_country_assets', {
386
- title: 'Render ChaosX focus country assets',
387
- description: 'Private ChaosX Discord integration endpoint. Returns country flags and leader portraits to accompany a focus-tree raster; it is not intended for coding-agent workflows.',
388
- inputSchema: chaosxCountryAssetsInput,
389
- outputSchema: chaosxCountryAssetsOutput,
390
- annotations: artifactProducing,
391
- }, async (input, extra) => {
392
- const workspaceId = await resolveServerWorkspaceId(engine, context, input.workspaceId, extra.signal);
377
+ };
378
+ const countryCall = async (input, progress) => {
379
+ const workspaceId = await resolveServerWorkspaceId(engine, context, input.workspaceId, progress.signal);
393
380
  try {
394
- const progress = progressReporter(extra);
395
381
  await progress.report(0, 3, 'Resolving ChaosX country assets');
396
382
  const discoveryWorkspace = engine.resolver.get(workspaceId, context.principal);
397
383
  const snapshot = await engine.scan(workspaceId, { patterns: countryAssetDiscoveryPatterns(discoveryWorkspace) }, context.principal, progress.signal);
@@ -488,5 +474,36 @@ export function registerChaosxTools(server, engine, context) {
488
474
  catch (error) {
489
475
  return errorResult(error, workspaceId);
490
476
  }
491
- });
477
+ };
478
+ return {
479
+ visual: {
480
+ definition: {
481
+ name: 'chaosx.visual_revision',
482
+ title: 'Check ChaosX visual revisions',
483
+ description: 'Private ChaosX cache-coherency endpoint. Computes exact scripted-GUI source revisions without rendering PNG artifacts.',
484
+ inputSchema: chaosxVisualRevisionInput,
485
+ outputSchema: chaosxVisualRevisionOutput,
486
+ annotations: readOnly,
487
+ },
488
+ call: visualCall,
489
+ },
490
+ country: {
491
+ definition: {
492
+ name: 'chaosx.focus_country_assets',
493
+ title: 'Render ChaosX focus country assets',
494
+ description: 'Private ChaosX Discord integration endpoint. Returns country flags and leader portraits to accompany a focus-tree raster; it is not intended for coding-agent workflows.',
495
+ inputSchema: chaosxCountryAssetsInput,
496
+ outputSchema: chaosxCountryAssetsOutput,
497
+ annotations: artifactProducing,
498
+ },
499
+ call: countryCall,
500
+ },
501
+ };
502
+ }
503
+ export function registerChaosxTools(server, engine, context) {
504
+ const operations = createChaosxToolOperations(engine, context);
505
+ const { name: visualName, ...visualDefinition } = operations.visual.definition;
506
+ const { name: countryName, ...countryDefinition } = operations.country.definition;
507
+ server.registerTool(visualName, visualDefinition, (input, extra) => operations.visual.call(input, progressReporter(extra)));
508
+ server.registerTool(countryName, countryDefinition, (input, extra) => operations.country.call(input, progressReporter(extra)));
492
509
  }