quorumkit-engine 3.0.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/RELEASING.md +151 -0
- package/SECURITY.md +82 -0
- package/action.yml +25 -0
- package/dist/212.index.js +171 -0
- package/dist/212.index.js.map +1 -0
- package/dist/563.index.js +100 -0
- package/dist/563.index.js.map +1 -0
- package/dist/992.index.js +211 -0
- package/dist/992.index.js.map +1 -0
- package/dist/agent-identities.schema.json +33 -0
- package/dist/apm-msg.schema.json +62 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/licenses.txt +1 -0
- package/dist/package.json +3 -0
- package/dist/pipeline.schema.json +108 -0
- package/dist/runtimes.schema.json +43 -0
- package/dist/sourcemap-register.cjs +1 -0
- package/package.json +21 -0
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
export const id = 992;
|
|
2
|
+
export const ids = [992,563];
|
|
3
|
+
export const modules = {
|
|
4
|
+
|
|
5
|
+
/***/ 3563:
|
|
6
|
+
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
|
|
7
|
+
|
|
8
|
+
__webpack_require__.r(__webpack_exports__);
|
|
9
|
+
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
10
|
+
/* harmony export */ isRetryable: () => (/* binding */ isRetryable),
|
|
11
|
+
/* harmony export */ withRetry: () => (/* binding */ withRetry)
|
|
12
|
+
/* harmony export */ });
|
|
13
|
+
/**
|
|
14
|
+
* runtimes/_retry.js
|
|
15
|
+
* Bounded exponential-backoff retry helper shared by all runtime adapters
|
|
16
|
+
* (ADR-007 §8, FR-030).
|
|
17
|
+
*
|
|
18
|
+
* Policy (defaults): 3 attempts, base 2 s, jitter ±25 %, max wait 30 s total.
|
|
19
|
+
* Retries are triggered by network errors, HTTP 5xx, and HTTP 429 (rate-limit).
|
|
20
|
+
* Non-retryable errors are rethrown immediately.
|
|
21
|
+
*
|
|
22
|
+
* Returns the function's resolved value plus the number of retries used,
|
|
23
|
+
* so the caller can record `runtime_retries` in the audit comment without
|
|
24
|
+
* counting the invocation as a separate loop-budget iteration.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
const DEFAULT_OPTS = Object.freeze({
|
|
28
|
+
maxAttempts: 3,
|
|
29
|
+
baseMs: 2_000,
|
|
30
|
+
maxTotalMs: 30_000,
|
|
31
|
+
jitter: 0.25,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Determine whether an error is retryable.
|
|
36
|
+
* @param {any} err
|
|
37
|
+
* @returns {boolean}
|
|
38
|
+
*/
|
|
39
|
+
function isRetryable(err) {
|
|
40
|
+
if (!err) return false;
|
|
41
|
+
// Octokit / fetch error styles
|
|
42
|
+
if (err.status === 429) return true;
|
|
43
|
+
if (typeof err.status === 'number' && err.status >= 500 && err.status < 600) return true;
|
|
44
|
+
// Node fetch network errors
|
|
45
|
+
const code = err.code ?? err.cause?.code;
|
|
46
|
+
if (code && /^(ECONNRESET|ETIMEDOUT|ENETUNREACH|EAI_AGAIN|UND_ERR_SOCKET)$/.test(code)) return true;
|
|
47
|
+
if (typeof err.message === 'string' && /timeout|network/i.test(err.message)) return true;
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Run `fn` with bounded exponential backoff.
|
|
53
|
+
*
|
|
54
|
+
* @template T
|
|
55
|
+
* @param {() => Promise<T>} fn
|
|
56
|
+
* @param {object} [options]
|
|
57
|
+
* @param {object} [options.clock] - injectable clock { now(): number, sleep(ms): Promise<void> }
|
|
58
|
+
* @returns {Promise<{ value: T, retries: number }>}
|
|
59
|
+
*/
|
|
60
|
+
async function withRetry(fn, options = {}) {
|
|
61
|
+
const opts = { ...DEFAULT_OPTS, ...options };
|
|
62
|
+
const clock = options.clock ?? defaultClock;
|
|
63
|
+
|
|
64
|
+
const start = clock.now();
|
|
65
|
+
let attempt = 0;
|
|
66
|
+
let lastError;
|
|
67
|
+
|
|
68
|
+
while (attempt < opts.maxAttempts) {
|
|
69
|
+
try {
|
|
70
|
+
const value = await fn();
|
|
71
|
+
return { value, retries: attempt };
|
|
72
|
+
} catch (err) {
|
|
73
|
+
lastError = err;
|
|
74
|
+
attempt += 1;
|
|
75
|
+
if (!isRetryable(err) || attempt >= opts.maxAttempts) break;
|
|
76
|
+
|
|
77
|
+
const elapsed = clock.now() - start;
|
|
78
|
+
if (elapsed >= opts.maxTotalMs) break;
|
|
79
|
+
|
|
80
|
+
const exp = Math.pow(2, attempt - 1) * opts.baseMs;
|
|
81
|
+
const jitter = exp * opts.jitter * (Math.random() * 2 - 1);
|
|
82
|
+
const wait = Math.max(0, Math.min(exp + jitter, opts.maxTotalMs - elapsed));
|
|
83
|
+
await clock.sleep(wait);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
throw lastError;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const defaultClock = {
|
|
91
|
+
now: () => Date.now(),
|
|
92
|
+
sleep: ms => new Promise(r => setTimeout(r, ms)),
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
/***/ }),
|
|
97
|
+
|
|
98
|
+
/***/ 1992:
|
|
99
|
+
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
|
|
100
|
+
|
|
101
|
+
__webpack_require__.r(__webpack_exports__);
|
|
102
|
+
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
103
|
+
/* harmony export */ KIND: () => (/* binding */ KIND),
|
|
104
|
+
/* harmony export */ invoke: () => (/* binding */ invoke),
|
|
105
|
+
/* harmony export */ requiredPermissions: () => (/* binding */ requiredPermissions)
|
|
106
|
+
/* harmony export */ });
|
|
107
|
+
/* harmony import */ var _retry_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(3563);
|
|
108
|
+
/**
|
|
109
|
+
* runtimes/copilot.js
|
|
110
|
+
* Adapter for the Copilot (GitHub Models) runtime kind (ADR-005, ADR-003).
|
|
111
|
+
*
|
|
112
|
+
* Dispatches the agent's Copilot workflow file (`copilot-agent-<slug>.yml`)
|
|
113
|
+
* via repository_dispatch / workflow_dispatch through the supplied client.
|
|
114
|
+
* Wraps the call in the shared bounded-backoff retry helper (ADR-007 §8).
|
|
115
|
+
*
|
|
116
|
+
* The adapter is purely a dispatch layer — the LLM call itself happens
|
|
117
|
+
* inside the dispatched GHA workflow, where the GITHUB_TOKEN is already
|
|
118
|
+
* scoped via the workflow file. No secret value ever crosses this module.
|
|
119
|
+
*
|
|
120
|
+
* Exports:
|
|
121
|
+
* requiredPermissions — GH Actions token scopes consumed by this kind
|
|
122
|
+
* invoke(context) — dispatch entry point
|
|
123
|
+
*/
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
const KIND = 'copilot';
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Permissions consumed by the dispatched copilot workflow. The orchestrator
|
|
131
|
+
* unions these with the scopes of every other adapter actually used at run
|
|
132
|
+
* time (ADR-007 §7) when composing dispatched workflow `permissions:` blocks.
|
|
133
|
+
*/
|
|
134
|
+
const requiredPermissions = Object.freeze({
|
|
135
|
+
contents: 'read',
|
|
136
|
+
issues: 'write',
|
|
137
|
+
'pull-requests': 'write',
|
|
138
|
+
models: 'read', // GitHub Models inference
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Resolve the credential for a runtime entry. Returns the env var value or
|
|
143
|
+
* throws { code: 'runtime-credential-missing', credential_ref } if absent.
|
|
144
|
+
*
|
|
145
|
+
* NEVER returns the value to a caller that logs/comments — only the dispatch
|
|
146
|
+
* call uses it, and only the *name* (credential_ref) is recorded in audits.
|
|
147
|
+
*/
|
|
148
|
+
function resolveCredential(runtime, env = process.env) {
|
|
149
|
+
const ref = runtime.credential_ref;
|
|
150
|
+
if (!ref) {
|
|
151
|
+
const e = new Error('runtime-credential-missing');
|
|
152
|
+
e.code = 'runtime-credential-missing';
|
|
153
|
+
e.credential_ref = '(none declared)';
|
|
154
|
+
throw e;
|
|
155
|
+
}
|
|
156
|
+
const value = env[ref];
|
|
157
|
+
if (!value) {
|
|
158
|
+
const e = new Error('runtime-credential-missing');
|
|
159
|
+
e.code = 'runtime-credential-missing';
|
|
160
|
+
e.credential_ref = ref;
|
|
161
|
+
throw e;
|
|
162
|
+
}
|
|
163
|
+
return value;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Invoke the runtime for one step.
|
|
168
|
+
*
|
|
169
|
+
* @param {object} context
|
|
170
|
+
* @param {object} context.client - GitHub client with triggerWorkflow()
|
|
171
|
+
* @param {string} context.owner
|
|
172
|
+
* @param {string} context.repo
|
|
173
|
+
* @param {string} context.agent - agent slug (e.g. "qa-agent")
|
|
174
|
+
* @param {string} context.ref - git ref to dispatch on
|
|
175
|
+
* @param {number} context.issueNumber
|
|
176
|
+
* @param {string} context.runId
|
|
177
|
+
* @param {string} context.step
|
|
178
|
+
* @param {number} context.iteration
|
|
179
|
+
* @param {object} context.runtime - resolved runtime entry
|
|
180
|
+
* @param {string} context.runtimeName
|
|
181
|
+
* @param {object} [context.env] - injectable env (for tests)
|
|
182
|
+
* @returns {Promise<{ dispatched: true, retries: number, workflow: string }>}
|
|
183
|
+
*/
|
|
184
|
+
async function invoke(context) {
|
|
185
|
+
const env = context.env ?? process.env;
|
|
186
|
+
// Trigger credential check (throws on absence) but never expose value:
|
|
187
|
+
resolveCredential(context.runtime, env);
|
|
188
|
+
|
|
189
|
+
const workflow = `copilot-agent-${context.agent}.yml`;
|
|
190
|
+
const dispatchRef = context.ref ?? 'main';
|
|
191
|
+
const inputs = {
|
|
192
|
+
issue_number: String(context.issueNumber),
|
|
193
|
+
run_id: context.runId ?? '',
|
|
194
|
+
step: context.step ?? '',
|
|
195
|
+
iteration: String(context.iteration ?? 1),
|
|
196
|
+
runtime: context.runtimeName ?? '',
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
const { retries } = await (0,_retry_js__WEBPACK_IMPORTED_MODULE_0__.withRetry)(
|
|
200
|
+
() => context.client.triggerWorkflow(context.owner, context.repo, workflow, dispatchRef, inputs),
|
|
201
|
+
{ clock: context.clock }
|
|
202
|
+
);
|
|
203
|
+
return { dispatched: true, retries, workflow };
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
/***/ })
|
|
208
|
+
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
//# sourceMappingURL=992.index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"992.index.js","mappings":";;;;;;;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;AChFA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","sources":[".././orchestrator/runtimes/_retry.js",".././orchestrator/runtimes/copilot.js"],"sourcesContent":["/**\n * runtimes/_retry.js\n * Bounded exponential-backoff retry helper shared by all runtime adapters\n * (ADR-007 §8, FR-030).\n *\n * Policy (defaults): 3 attempts, base 2 s, jitter ±25 %, max wait 30 s total.\n * Retries are triggered by network errors, HTTP 5xx, and HTTP 429 (rate-limit).\n * Non-retryable errors are rethrown immediately.\n *\n * Returns the function's resolved value plus the number of retries used,\n * so the caller can record `runtime_retries` in the audit comment without\n * counting the invocation as a separate loop-budget iteration.\n */\n\nconst DEFAULT_OPTS = Object.freeze({\n maxAttempts: 3,\n baseMs: 2_000,\n maxTotalMs: 30_000,\n jitter: 0.25,\n});\n\n/**\n * Determine whether an error is retryable.\n * @param {any} err\n * @returns {boolean}\n */\nexport function isRetryable(err) {\n if (!err) return false;\n // Octokit / fetch error styles\n if (err.status === 429) return true;\n if (typeof err.status === 'number' && err.status >= 500 && err.status < 600) return true;\n // Node fetch network errors\n const code = err.code ?? err.cause?.code;\n if (code && /^(ECONNRESET|ETIMEDOUT|ENETUNREACH|EAI_AGAIN|UND_ERR_SOCKET)$/.test(code)) return true;\n if (typeof err.message === 'string' && /timeout|network/i.test(err.message)) return true;\n return false;\n}\n\n/**\n * Run `fn` with bounded exponential backoff.\n *\n * @template T\n * @param {() => Promise<T>} fn\n * @param {object} [options]\n * @param {object} [options.clock] - injectable clock { now(): number, sleep(ms): Promise<void> }\n * @returns {Promise<{ value: T, retries: number }>}\n */\nexport async function withRetry(fn, options = {}) {\n const opts = { ...DEFAULT_OPTS, ...options };\n const clock = options.clock ?? defaultClock;\n\n const start = clock.now();\n let attempt = 0;\n let lastError;\n\n while (attempt < opts.maxAttempts) {\n try {\n const value = await fn();\n return { value, retries: attempt };\n } catch (err) {\n lastError = err;\n attempt += 1;\n if (!isRetryable(err) || attempt >= opts.maxAttempts) break;\n\n const elapsed = clock.now() - start;\n if (elapsed >= opts.maxTotalMs) break;\n\n const exp = Math.pow(2, attempt - 1) * opts.baseMs;\n const jitter = exp * opts.jitter * (Math.random() * 2 - 1);\n const wait = Math.max(0, Math.min(exp + jitter, opts.maxTotalMs - elapsed));\n await clock.sleep(wait);\n }\n }\n\n throw lastError;\n}\n\nconst defaultClock = {\n now: () => Date.now(),\n sleep: ms => new Promise(r => setTimeout(r, ms)),\n};\n","/**\n * runtimes/copilot.js\n * Adapter for the Copilot (GitHub Models) runtime kind (ADR-005, ADR-003).\n *\n * Dispatches the agent's Copilot workflow file (`copilot-agent-<slug>.yml`)\n * via repository_dispatch / workflow_dispatch through the supplied client.\n * Wraps the call in the shared bounded-backoff retry helper (ADR-007 §8).\n *\n * The adapter is purely a dispatch layer — the LLM call itself happens\n * inside the dispatched GHA workflow, where the GITHUB_TOKEN is already\n * scoped via the workflow file. No secret value ever crosses this module.\n *\n * Exports:\n * requiredPermissions — GH Actions token scopes consumed by this kind\n * invoke(context) — dispatch entry point\n */\n\nimport { withRetry } from './_retry.js';\n\nexport const KIND = 'copilot';\n\n/**\n * Permissions consumed by the dispatched copilot workflow. The orchestrator\n * unions these with the scopes of every other adapter actually used at run\n * time (ADR-007 §7) when composing dispatched workflow `permissions:` blocks.\n */\nexport const requiredPermissions = Object.freeze({\n contents: 'read',\n issues: 'write',\n 'pull-requests': 'write',\n models: 'read', // GitHub Models inference\n});\n\n/**\n * Resolve the credential for a runtime entry. Returns the env var value or\n * throws { code: 'runtime-credential-missing', credential_ref } if absent.\n *\n * NEVER returns the value to a caller that logs/comments — only the dispatch\n * call uses it, and only the *name* (credential_ref) is recorded in audits.\n */\nfunction resolveCredential(runtime, env = process.env) {\n const ref = runtime.credential_ref;\n if (!ref) {\n const e = new Error('runtime-credential-missing');\n e.code = 'runtime-credential-missing';\n e.credential_ref = '(none declared)';\n throw e;\n }\n const value = env[ref];\n if (!value) {\n const e = new Error('runtime-credential-missing');\n e.code = 'runtime-credential-missing';\n e.credential_ref = ref;\n throw e;\n }\n return value;\n}\n\n/**\n * Invoke the runtime for one step.\n *\n * @param {object} context\n * @param {object} context.client - GitHub client with triggerWorkflow()\n * @param {string} context.owner\n * @param {string} context.repo\n * @param {string} context.agent - agent slug (e.g. \"qa-agent\")\n * @param {string} context.ref - git ref to dispatch on\n * @param {number} context.issueNumber\n * @param {string} context.runId\n * @param {string} context.step\n * @param {number} context.iteration\n * @param {object} context.runtime - resolved runtime entry\n * @param {string} context.runtimeName\n * @param {object} [context.env] - injectable env (for tests)\n * @returns {Promise<{ dispatched: true, retries: number, workflow: string }>}\n */\nexport async function invoke(context) {\n const env = context.env ?? process.env;\n // Trigger credential check (throws on absence) but never expose value:\n resolveCredential(context.runtime, env);\n\n const workflow = `copilot-agent-${context.agent}.yml`;\n const dispatchRef = context.ref ?? 'main';\n const inputs = {\n issue_number: String(context.issueNumber),\n run_id: context.runId ?? '',\n step: context.step ?? '',\n iteration: String(context.iteration ?? 1),\n runtime: context.runtimeName ?? '',\n };\n\n const { retries } = await withRetry(\n () => context.client.triggerWorkflow(context.owner, context.repo, workflow, dispatchRef, inputs),\n { clock: context.clock }\n );\n return { dispatched: true, retries, workflow };\n}\n"],"names":[],"sourceRoot":""}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://apm.dev/schemas/agent-identities.schema.json",
|
|
4
|
+
"title": "QuorumKit Agent Identity Registry",
|
|
5
|
+
"description": "Maps GitHub user/app identities to agent slugs (FR-013).",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["identities"],
|
|
8
|
+
"additionalProperties": false,
|
|
9
|
+
"properties": {
|
|
10
|
+
"identities": {
|
|
11
|
+
"type": "array",
|
|
12
|
+
"minItems": 1,
|
|
13
|
+
"items": {
|
|
14
|
+
"type": "object",
|
|
15
|
+
"required": ["agent", "logins"],
|
|
16
|
+
"additionalProperties": false,
|
|
17
|
+
"properties": {
|
|
18
|
+
"agent": {
|
|
19
|
+
"type": "string",
|
|
20
|
+
"minLength": 1,
|
|
21
|
+
"description": "Agent slug (e.g. 'qa-agent')."
|
|
22
|
+
},
|
|
23
|
+
"logins": {
|
|
24
|
+
"type": "array",
|
|
25
|
+
"minItems": 1,
|
|
26
|
+
"items": { "type": "string", "minLength": 1 },
|
|
27
|
+
"description": "GitHub logins or app installation slugs that map to this agent."
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://apm.dev/schemas/apm-msg.schema.json",
|
|
4
|
+
"title": "QuorumKit Agent Message (apm-msg)",
|
|
5
|
+
"description": "Single machine-readable message emitted by an agent at the end of a step (FR-011, FR-012).",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["version", "runId", "step", "agent", "iteration", "outcome", "summary"],
|
|
8
|
+
"additionalProperties": false,
|
|
9
|
+
"properties": {
|
|
10
|
+
"version": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"enum": ["2"],
|
|
13
|
+
"description": "apm-msg schema version. v2 is the only supported value."
|
|
14
|
+
},
|
|
15
|
+
"runId": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
"minLength": 1,
|
|
18
|
+
"description": "Pipeline run UUID; must equal the active run."
|
|
19
|
+
},
|
|
20
|
+
"step": {
|
|
21
|
+
"type": "string",
|
|
22
|
+
"minLength": 1,
|
|
23
|
+
"description": "Step name as declared in the pipeline."
|
|
24
|
+
},
|
|
25
|
+
"agent": {
|
|
26
|
+
"type": "string",
|
|
27
|
+
"minLength": 1,
|
|
28
|
+
"description": "Agent slug (e.g. 'qa-agent', 'dev-agent') matching the agent identity registry."
|
|
29
|
+
},
|
|
30
|
+
"iteration": {
|
|
31
|
+
"type": "integer",
|
|
32
|
+
"minimum": 1,
|
|
33
|
+
"description": "1-based iteration counter for the step on the current edge."
|
|
34
|
+
},
|
|
35
|
+
"outcome": {
|
|
36
|
+
"type": "string",
|
|
37
|
+
"enum": [
|
|
38
|
+
"success",
|
|
39
|
+
"fail",
|
|
40
|
+
"blocker",
|
|
41
|
+
"spec_gap",
|
|
42
|
+
"timeout",
|
|
43
|
+
"needs-human",
|
|
44
|
+
"runtime-error",
|
|
45
|
+
"protocol-violation",
|
|
46
|
+
"orchestrator-failure"
|
|
47
|
+
],
|
|
48
|
+
"description": "Declared outcome enum. Source of truth: docs/AGENT_PROTOCOL.md."
|
|
49
|
+
},
|
|
50
|
+
"summary": {
|
|
51
|
+
"type": "string",
|
|
52
|
+
"minLength": 1,
|
|
53
|
+
"maxLength": 280,
|
|
54
|
+
"description": "Single-line human summary (max 280 chars)."
|
|
55
|
+
},
|
|
56
|
+
"payload": {
|
|
57
|
+
"type": "object",
|
|
58
|
+
"description": "Optional per-outcome payload. Schema is per-outcome; see regulation document.",
|
|
59
|
+
"additionalProperties": true
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|