agentxchain 0.8.7 → 2.1.1

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 (94) hide show
  1. package/README.md +123 -154
  2. package/bin/agentxchain.js +240 -8
  3. package/dashboard/app.js +305 -0
  4. package/dashboard/components/blocked.js +145 -0
  5. package/dashboard/components/cross-repo.js +126 -0
  6. package/dashboard/components/gate.js +311 -0
  7. package/dashboard/components/hooks.js +177 -0
  8. package/dashboard/components/initiative.js +147 -0
  9. package/dashboard/components/ledger.js +165 -0
  10. package/dashboard/components/timeline.js +222 -0
  11. package/dashboard/index.html +352 -0
  12. package/package.json +16 -7
  13. package/scripts/agentxchain-autonudge.applescript +32 -5
  14. package/scripts/live-api-proxy-preflight-smoke.sh +531 -0
  15. package/scripts/publish-from-tag.sh +88 -0
  16. package/scripts/release-postflight.sh +231 -0
  17. package/scripts/release-preflight.sh +167 -0
  18. package/scripts/run-autonudge.sh +1 -1
  19. package/src/adapters/claude-code.js +7 -14
  20. package/src/adapters/cursor-local.js +17 -16
  21. package/src/commands/accept-turn.js +160 -0
  22. package/src/commands/approve-completion.js +80 -0
  23. package/src/commands/approve-transition.js +85 -0
  24. package/src/commands/branch.js +2 -2
  25. package/src/commands/claim.js +84 -9
  26. package/src/commands/config.js +16 -0
  27. package/src/commands/dashboard.js +70 -0
  28. package/src/commands/doctor.js +9 -1
  29. package/src/commands/init.js +540 -5
  30. package/src/commands/migrate.js +348 -0
  31. package/src/commands/multi.js +549 -0
  32. package/src/commands/plugin.js +157 -0
  33. package/src/commands/reject-turn.js +204 -0
  34. package/src/commands/resume.js +389 -0
  35. package/src/commands/status.js +196 -3
  36. package/src/commands/step.js +947 -0
  37. package/src/commands/stop.js +65 -33
  38. package/src/commands/template-list.js +33 -0
  39. package/src/commands/template-set.js +279 -0
  40. package/src/commands/update.js +24 -3
  41. package/src/commands/validate.js +20 -11
  42. package/src/commands/verify.js +71 -0
  43. package/src/commands/watch.js +112 -25
  44. package/src/lib/adapters/api-proxy-adapter.js +1076 -0
  45. package/src/lib/adapters/local-cli-adapter.js +337 -0
  46. package/src/lib/adapters/manual-adapter.js +169 -0
  47. package/src/lib/blocked-state.js +94 -0
  48. package/src/lib/config.js +143 -12
  49. package/src/lib/context-compressor.js +121 -0
  50. package/src/lib/context-section-parser.js +220 -0
  51. package/src/lib/coordinator-acceptance.js +428 -0
  52. package/src/lib/coordinator-config.js +461 -0
  53. package/src/lib/coordinator-dispatch.js +276 -0
  54. package/src/lib/coordinator-gates.js +487 -0
  55. package/src/lib/coordinator-hooks.js +239 -0
  56. package/src/lib/coordinator-recovery.js +523 -0
  57. package/src/lib/coordinator-state.js +365 -0
  58. package/src/lib/cross-repo-context.js +247 -0
  59. package/src/lib/dashboard/bridge-server.js +284 -0
  60. package/src/lib/dashboard/file-watcher.js +93 -0
  61. package/src/lib/dashboard/state-reader.js +96 -0
  62. package/src/lib/dispatch-bundle.js +568 -0
  63. package/src/lib/dispatch-manifest.js +252 -0
  64. package/src/lib/filter-agents.js +12 -0
  65. package/src/lib/gate-evaluator.js +285 -0
  66. package/src/lib/generate-vscode.js +158 -68
  67. package/src/lib/governed-state.js +2139 -0
  68. package/src/lib/governed-templates.js +145 -0
  69. package/src/lib/hook-runner.js +788 -0
  70. package/src/lib/next-owner.js +61 -6
  71. package/src/lib/normalized-config.js +539 -0
  72. package/src/lib/notify.js +14 -12
  73. package/src/lib/plugin-config-schema.js +192 -0
  74. package/src/lib/plugins.js +692 -0
  75. package/src/lib/prompt-core.js +108 -0
  76. package/src/lib/protocol-conformance.js +291 -0
  77. package/src/lib/reference-conformance-adapter.js +717 -0
  78. package/src/lib/repo-observer.js +597 -0
  79. package/src/lib/repo.js +0 -31
  80. package/src/lib/safe-write.js +44 -0
  81. package/src/lib/schema.js +189 -0
  82. package/src/lib/schemas/turn-result.schema.json +205 -0
  83. package/src/lib/seed-prompt-polling.js +15 -73
  84. package/src/lib/seed-prompt.js +17 -63
  85. package/src/lib/token-budget.js +206 -0
  86. package/src/lib/token-counter.js +27 -0
  87. package/src/lib/turn-paths.js +67 -0
  88. package/src/lib/turn-result-validator.js +496 -0
  89. package/src/lib/validation.js +167 -19
  90. package/src/lib/verify-command.js +72 -0
  91. package/src/templates/governed/api-service.json +31 -0
  92. package/src/templates/governed/cli-tool.json +30 -0
  93. package/src/templates/governed/generic.json +10 -0
  94. package/src/templates/governed/web-app.json +30 -0
@@ -0,0 +1,206 @@
1
+ /**
2
+ * Token budget evaluator for preflight tokenization.
3
+ *
4
+ * Combines the context section parser, context compressor, and token counter
5
+ * to produce a TokenBudgetReport and effective context for api_proxy dispatch.
6
+ *
7
+ * This is a pure computation helper — it does not perform network dispatch,
8
+ * write audit artifacts, or mutate state.
9
+ */
10
+
11
+ import { countTokens } from './token-counter.js';
12
+ import { parseContextSections, renderContextSections } from './context-section-parser.js';
13
+ import { compressContextSections } from './context-compressor.js';
14
+ import { getDispatchEffectiveContextPath } from './turn-paths.js';
15
+
16
+ const SEPARATOR = '\n\n---\n\n';
17
+
18
+ const SYSTEM_PROMPT = [
19
+ 'You are acting as a governed agent in an AgentXchain protocol run.',
20
+ 'Your task and rules are described in the user message.',
21
+ 'You MUST respond with a valid JSON object matching the turn result schema provided in the prompt.',
22
+ 'Do NOT wrap the JSON in markdown code fences. Respond with raw JSON only.',
23
+ ].join('\n');
24
+
25
+ /**
26
+ * Evaluate the token budget for a preflight tokenization check.
27
+ *
28
+ * @param {object} params
29
+ * @param {string} params.promptMd - Full PROMPT.md text
30
+ * @param {string} params.contextMd - Full CONTEXT.md text (may be empty)
31
+ * @param {string} params.provider - Provider name (e.g. "anthropic")
32
+ * @param {string} params.model - Model identifier
33
+ * @param {string} params.runtimeId - Runtime identifier for the report
34
+ * @param {string} params.runId - Run identifier for the report
35
+ * @param {string} params.turnId - Turn identifier for the report
36
+ * @param {number} params.contextWindowTokens - Total context window size
37
+ * @param {number} params.maxOutputTokens - Reserved output tokens
38
+ * @param {number} params.safetyMarginTokens - Safety margin tokens
39
+ * @returns {TokenBudgetResult}
40
+ */
41
+ export function evaluateTokenBudget({
42
+ promptMd,
43
+ contextMd,
44
+ provider,
45
+ model,
46
+ runtimeId,
47
+ runId,
48
+ turnId,
49
+ contextWindowTokens,
50
+ maxOutputTokens,
51
+ safetyMarginTokens,
52
+ }) {
53
+ const reservedOutputTokens = maxOutputTokens || 4096;
54
+ const safetyMargin = safetyMarginTokens ?? 2048;
55
+ const availableInputTokens = contextWindowTokens - reservedOutputTokens - safetyMargin;
56
+
57
+ // Count immutable parts
58
+ const systemPromptTokens = countTokens(SYSTEM_PROMPT, provider);
59
+ const promptTokens = countTokens(promptMd || '', provider);
60
+ const hasSeparator = !!(contextMd && contextMd.trim());
61
+ const separatorTokens = hasSeparator ? countTokens(SEPARATOR, provider) : 0;
62
+ const immutableTokens = systemPromptTokens + promptTokens + separatorTokens;
63
+
64
+ // Build base report fields
65
+ const baseReport = {
66
+ provider,
67
+ model,
68
+ runtime_id: runtimeId,
69
+ run_id: runId,
70
+ turn_id: turnId,
71
+ tokenizer: 'provider_local',
72
+ context_window_tokens: contextWindowTokens,
73
+ reserved_output_tokens: reservedOutputTokens,
74
+ safety_margin_tokens: safetyMargin,
75
+ available_input_tokens: availableInputTokens,
76
+ system_prompt_tokens: systemPromptTokens,
77
+ prompt_tokens: promptTokens,
78
+ separator_tokens: separatorTokens,
79
+ };
80
+
81
+ // Prompt-only overflow: if immutable parts alone exceed budget, fail immediately
82
+ if (immutableTokens > availableInputTokens) {
83
+ const originalContextTokens = hasSeparator
84
+ ? countTokens(contextMd, provider)
85
+ : 0;
86
+
87
+ return {
88
+ sent_to_provider: false,
89
+ effective_context: hasSeparator ? contextMd : '',
90
+ report: {
91
+ ...baseReport,
92
+ original_context_tokens: originalContextTokens,
93
+ final_context_tokens: originalContextTokens,
94
+ estimated_input_tokens: immutableTokens + originalContextTokens,
95
+ truncated: false,
96
+ sent_to_provider: false,
97
+ effective_context_path: getDispatchEffectiveContextPath(turnId),
98
+ sections: [],
99
+ },
100
+ };
101
+ }
102
+
103
+ // No context case
104
+ if (!hasSeparator) {
105
+ return {
106
+ sent_to_provider: true,
107
+ effective_context: '',
108
+ report: {
109
+ ...baseReport,
110
+ original_context_tokens: 0,
111
+ final_context_tokens: 0,
112
+ estimated_input_tokens: immutableTokens,
113
+ truncated: false,
114
+ sent_to_provider: true,
115
+ effective_context_path: getDispatchEffectiveContextPath(turnId),
116
+ sections: [],
117
+ },
118
+ };
119
+ }
120
+
121
+ // Parse context into sections and count original tokens per section
122
+ const sections = parseContextSections(contextMd);
123
+ const originalTokensById = new Map();
124
+ for (const section of sections) {
125
+ originalTokensById.set(section.id, countTokens(section.content, provider));
126
+ }
127
+
128
+ const originalContextTokens = countTokens(contextMd, provider);
129
+
130
+ // Budget callback for the compressor: check if the full outbound request fits
131
+ function fitsInBudget(effectiveContext) {
132
+ const contextTokens = countTokens(effectiveContext, provider);
133
+ return immutableTokens + contextTokens <= availableInputTokens;
134
+ }
135
+
136
+ // Check if it fits without compression
137
+ if (fitsInBudget(contextMd)) {
138
+ const sectionActions = sections.map((s) => ({
139
+ id: s.id,
140
+ required: s.required,
141
+ original_tokens: originalTokensById.get(s.id),
142
+ final_tokens: originalTokensById.get(s.id),
143
+ action: 'kept',
144
+ }));
145
+
146
+ return {
147
+ sent_to_provider: true,
148
+ effective_context: contextMd,
149
+ report: {
150
+ ...baseReport,
151
+ original_context_tokens: originalContextTokens,
152
+ final_context_tokens: originalContextTokens,
153
+ estimated_input_tokens: immutableTokens + originalContextTokens,
154
+ truncated: false,
155
+ sent_to_provider: true,
156
+ effective_context_path: getDispatchEffectiveContextPath(turnId),
157
+ sections: sectionActions,
158
+ },
159
+ };
160
+ }
161
+
162
+ // Run compression
163
+ const compressionResult = compressContextSections(sections, fitsInBudget);
164
+ const effectiveContext = compressionResult.effective_context;
165
+ const finalContextTokens = countTokens(effectiveContext, provider);
166
+ const estimatedInputTokens = immutableTokens + finalContextTokens;
167
+ const sentToProvider = !compressionResult.exhausted;
168
+
169
+ // Build per-section actions with token counts
170
+ const sectionActions = compressionResult.actions.map((a) => {
171
+ let finalTokens;
172
+ if (a.action === 'dropped') {
173
+ finalTokens = 0;
174
+ } else if (a.action === 'truncated') {
175
+ const remaining = compressionResult.sections.find((s) => s.id === a.id);
176
+ finalTokens = remaining ? countTokens(remaining.content, provider) : 0;
177
+ } else {
178
+ finalTokens = originalTokensById.get(a.id) ?? 0;
179
+ }
180
+
181
+ return {
182
+ id: a.id,
183
+ required: a.required,
184
+ original_tokens: originalTokensById.get(a.id) ?? 0,
185
+ final_tokens: finalTokens,
186
+ action: a.action,
187
+ };
188
+ });
189
+
190
+ return {
191
+ sent_to_provider: sentToProvider,
192
+ effective_context: effectiveContext,
193
+ report: {
194
+ ...baseReport,
195
+ original_context_tokens: originalContextTokens,
196
+ final_context_tokens: finalContextTokens,
197
+ estimated_input_tokens: estimatedInputTokens,
198
+ truncated: compressionResult.steps_applied > 0,
199
+ sent_to_provider: sentToProvider,
200
+ effective_context_path: getDispatchEffectiveContextPath(turnId),
201
+ sections: sectionActions,
202
+ },
203
+ };
204
+ }
205
+
206
+ export { SYSTEM_PROMPT, SEPARATOR };
@@ -0,0 +1,27 @@
1
+ import { countTokens as countAnthropicTokens } from '@anthropic-ai/tokenizer';
2
+
3
+ const SUPPORTED_TOKEN_COUNTER_PROVIDERS = ['anthropic'];
4
+
5
+ export { SUPPORTED_TOKEN_COUNTER_PROVIDERS };
6
+
7
+ export function countTokens(text, provider = 'anthropic') {
8
+ const normalizedProvider = String(provider || '').trim().toLowerCase();
9
+
10
+ if (!SUPPORTED_TOKEN_COUNTER_PROVIDERS.includes(normalizedProvider)) {
11
+ throw new Error(
12
+ `Unsupported token counter provider "${provider}". Supported: ${SUPPORTED_TOKEN_COUNTER_PROVIDERS.join(', ')}`
13
+ );
14
+ }
15
+
16
+ const normalizedText = String(text ?? '');
17
+ if (!normalizedText) {
18
+ return 0;
19
+ }
20
+
21
+ const tokens = countAnthropicTokens(normalizedText);
22
+ if (!Number.isInteger(tokens) || tokens < 0) {
23
+ throw new Error(`Anthropic tokenizer returned invalid token count: ${tokens}`);
24
+ }
25
+
26
+ return tokens;
27
+ }
@@ -0,0 +1,67 @@
1
+ const DISPATCH_ROOT = '.agentxchain/dispatch';
2
+ const DISPATCH_INDEX_PATH = `${DISPATCH_ROOT}/index.json`;
3
+ const DISPATCH_TURNS_DIR = `${DISPATCH_ROOT}/turns`;
4
+ const STAGING_ROOT = '.agentxchain/staging';
5
+
6
+ export function getDispatchTurnDir(turnId) {
7
+ return `${DISPATCH_TURNS_DIR}/${turnId}`;
8
+ }
9
+
10
+ export function getDispatchPromptPath(turnId) {
11
+ return `${getDispatchTurnDir(turnId)}/PROMPT.md`;
12
+ }
13
+
14
+ export function getDispatchContextPath(turnId) {
15
+ return `${getDispatchTurnDir(turnId)}/CONTEXT.md`;
16
+ }
17
+
18
+ export function getDispatchAssignmentPath(turnId) {
19
+ return `${getDispatchTurnDir(turnId)}/ASSIGNMENT.json`;
20
+ }
21
+
22
+ export function getDispatchApiRequestPath(turnId) {
23
+ return `${getDispatchTurnDir(turnId)}/API_REQUEST.json`;
24
+ }
25
+
26
+ export function getDispatchTokenBudgetPath(turnId) {
27
+ return `${getDispatchTurnDir(turnId)}/TOKEN_BUDGET.json`;
28
+ }
29
+
30
+ export function getDispatchEffectiveContextPath(turnId) {
31
+ return `${getDispatchTurnDir(turnId)}/CONTEXT.effective.md`;
32
+ }
33
+
34
+ export function getDispatchManifestPath(turnId) {
35
+ return `${getDispatchTurnDir(turnId)}/MANIFEST.json`;
36
+ }
37
+
38
+ export function getDispatchLogPath(turnId) {
39
+ return `${getDispatchTurnDir(turnId)}/stdout.log`;
40
+ }
41
+
42
+ export function getTurnStagingDir(turnId) {
43
+ return `${STAGING_ROOT}/${turnId}`;
44
+ }
45
+
46
+ export function getTurnStagingResultPath(turnId) {
47
+ return `${getTurnStagingDir(turnId)}/turn-result.json`;
48
+ }
49
+
50
+ export function getTurnProviderResponsePath(turnId) {
51
+ return `${getTurnStagingDir(turnId)}/provider-response.json`;
52
+ }
53
+
54
+ export function getTurnApiErrorPath(turnId) {
55
+ return `${getTurnStagingDir(turnId)}/api-error.json`;
56
+ }
57
+
58
+ export function getTurnRetryTracePath(turnId) {
59
+ return `${getTurnStagingDir(turnId)}/retry-trace.json`;
60
+ }
61
+
62
+ export {
63
+ DISPATCH_ROOT,
64
+ DISPATCH_INDEX_PATH,
65
+ DISPATCH_TURNS_DIR,
66
+ STAGING_ROOT,
67
+ };