total-recall-brain 3.43.2 → 3.44.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/bin/total-recall-server-release.mjs +4 -0
- package/docs/reference/cli-reference.md +14 -0
- package/package.json +5 -4
- package/scaffold/.agent/skills/plugin/SKILL.md +7 -2
- package/scaffold/.agent/skills/plugin/evals/README.md +5 -0
- package/scaffold/.agent/skills/plugin/evals/cases/01-fabricated-seed.mjs +1 -0
- package/scaffold/.agent/skills/plugin/evals/cases/02-reassuring-default.mjs +1 -0
- package/scaffold/.agent/skills/plugin/evals/cases/03-declared-template.mjs +1 -0
- package/scaffold/.agent/skills/plugin/evals/cases/04-card-collects-credential.mjs +1 -0
- package/scaffold/.agent/skills/plugin/evals/cases/05-host-leak-in-scaffold.mjs +1 -0
- package/scaffold/.agent/skills/plugin/evals/cases/06-correct-plugin.mjs +1 -0
- package/scaffold/.agent/skills/plugin/evals/cases/07-missing-reuse.mjs +11 -0
- package/scaffold/.agent/skills/plugin/evals/cases/08-unresolved-owner-command.mjs +12 -0
- package/scaffold/.agent/skills/plugin/evals/cases/09-missing-brick.mjs +12 -0
- package/scaffold/.agent/skills/plugin/prompts/01-function.md +4 -3
- package/scaffold/.agent/skills/plugin/prompts/08-verify.md +1 -0
- package/scaffold/.agent/skills/plugin/references/surfaces.md +12 -2
- package/scaffold/.agent/skills/plugin/scripts/record.mjs +59 -2
- package/scaffold/.agent/skills/total-recall/plugins/auth/README.md +8 -0
- package/scaffold/.agent/skills/total-recall/plugins/auth/package-lock.json +14 -13
- package/scaffold/.agent/skills/total-recall/plugins/auth/package.json +3 -3
- package/scaffold/.agent/skills/total-recall/plugins/auth/plugin.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/cli-gh-login.mjs +102 -0
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/cli-secret.mjs +18 -1
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/login-projections.mjs +157 -13
- package/scaffold/.agent/skills/total-recall/plugins/auth/src/secrets-sync.mjs +161 -17
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/README.md +2 -2
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/core/check.mjs +39 -6
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/core/config.schema.json +2 -1
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/package-lock.json +14 -13
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/package.json +3 -3
- package/scaffold/.agent/skills/total-recall/plugins/code-quality/plugin.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/package-lock.json +14 -13
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/package.json +3 -3
- package/scaffold/.agent/skills/total-recall/plugins/composable-cli/plugin.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/package-lock.json +14 -13
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/package.json +3 -3
- package/scaffold/.agent/skills/total-recall/plugins/embeddings/plugin.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/package-lock.json +14 -13
- package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/package.json +3 -3
- package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/plugin.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/mesh/package-lock.json +14 -13
- package/scaffold/.agent/skills/total-recall/plugins/mesh/package.json +3 -3
- package/scaffold/.agent/skills/total-recall/plugins/mesh/plugin.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/README.md +38 -5
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/cli.mjs +38 -7
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/package-lock.json +14 -13
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/package.json +3 -3
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/plugin.json +85 -3
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/skills/meta-harness/SKILL.md +2 -1
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/adoption.mjs +104 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/cascade.mjs +12 -1
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/coordinator.mjs +15 -3
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/dispatch-request.mjs +38 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/dispatch.mjs +42 -11
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/executor.mjs +40 -13
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/included-usage.mjs +249 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/learner.mjs +3 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/pool.mjs +16 -8
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/process-manager.mjs +1 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/reasoning.mjs +5 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/remote-operation.mjs +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/router.mjs +27 -11
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/runtime.mjs +28 -6
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/usage-queue.mjs +110 -0
- package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/worker.mjs +322 -40
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/package-lock.json +14 -13
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/package.json +3 -3
- package/scaffold/.agent/skills/total-recall/plugins/openwiki/plugin.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/security/package-lock.json +14 -13
- package/scaffold/.agent/skills/total-recall/plugins/security/package.json +3 -3
- package/scaffold/.agent/skills/total-recall/plugins/security/plugin.json +1 -1
- package/scaffold/.agent/skills/total-recall/plugins/ssss/assets/scaffold.json +5 -5
- package/scaffold/.agent/skills/total-recall/plugins/ssss/package-lock.json +6 -6
- package/scaffold/.agent/skills/total-recall/plugins/ssss/package.json +2 -2
- package/scaffold/.agent/skills/total-recall/plugins/ssss/plugin.json +1 -1
- package/src/cli/connect.mjs +2 -29
- package/src/cli/context.mjs +1 -0
- package/src/cli/ingest-okf.mjs +4 -4
- package/src/cli/release.mjs +27 -2
- package/src/cli/startup.mjs +1 -0
- package/src/cli/update.mjs +14 -3
- package/src/core/automatic-brain-updates.mjs +13 -3
- package/src/core/autopull-install.mjs +14 -0
- package/src/core/brain-updates.mjs +12 -2
- package/src/core/context-compactor.mjs +7 -2
- package/src/core/context-compiler.mjs +6 -1
- package/src/core/context-policy.mjs +7 -3
- package/src/core/daemon-control.mjs +22 -0
- package/src/core/global-brain-updates.mjs +3 -3
- package/src/core/mesh-brain-updates.mjs +20 -5
- package/src/core/running-server.mjs +76 -0
- package/src/core/server-release.mjs +487 -0
- package/src/core/server-restart.mjs +128 -7
- package/src/core/source-brain-updates.mjs +35 -6
- package/src/core/ssss-kernel-bridge.mjs +1 -2
- package/src/core/startup-health.mjs +13 -2
- package/src/core/surface.mjs +3 -3
- package/src/core/task-context.mjs +9 -8
- package/src/core/validated-write.mjs +4 -0
- package/src/core/vault-files.mjs +8 -2
- package/src/core/vault.mjs +1 -1
- package/src/server/index.mjs +17 -1
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tr-plugin-meta-harness",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.9",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "tr-plugin-meta-harness",
|
|
9
|
-
"version": "0.3.
|
|
9
|
+
"version": "0.3.9",
|
|
10
10
|
"dependencies": {
|
|
11
|
-
"@gregiteen/ssss-cli": "0.14.
|
|
11
|
+
"@gregiteen/ssss-cli": "0.14.5"
|
|
12
12
|
},
|
|
13
13
|
"devDependencies": {
|
|
14
|
-
"total-recall-brain": "3.
|
|
14
|
+
"total-recall-brain": "3.43.3"
|
|
15
15
|
}
|
|
16
16
|
},
|
|
17
17
|
"node_modules/@alcalzone/ansi-tokenize": {
|
|
@@ -714,9 +714,9 @@
|
|
|
714
714
|
"license": "MIT"
|
|
715
715
|
},
|
|
716
716
|
"node_modules/@gregiteen/ssss-cli": {
|
|
717
|
-
"version": "0.14.
|
|
718
|
-
"resolved": "https://registry.npmjs.org/@gregiteen/ssss-cli/-/ssss-cli-0.14.
|
|
719
|
-
"integrity": "sha512-
|
|
717
|
+
"version": "0.14.5",
|
|
718
|
+
"resolved": "https://registry.npmjs.org/@gregiteen/ssss-cli/-/ssss-cli-0.14.5.tgz",
|
|
719
|
+
"integrity": "sha512-qVEYOocnwn97uJ0eDzmuwN06CYsYWr2t3+iKylpisBHX4wlLWk7P8EmFFdBlx7NyKVIZDDeYT164Gd7/T8UGgw==",
|
|
720
720
|
"license": "ISC",
|
|
721
721
|
"bin": {
|
|
722
722
|
"ssss": "scripts/ssss.mjs"
|
|
@@ -1589,9 +1589,9 @@
|
|
|
1589
1589
|
},
|
|
1590
1590
|
"node_modules/@ssss/cli": {
|
|
1591
1591
|
"name": "@gregiteen/ssss-cli",
|
|
1592
|
-
"version": "0.14.
|
|
1593
|
-
"resolved": "https://registry.npmjs.org/@gregiteen/ssss-cli/-/ssss-cli-0.14.
|
|
1594
|
-
"integrity": "sha512-
|
|
1592
|
+
"version": "0.14.5",
|
|
1593
|
+
"resolved": "https://registry.npmjs.org/@gregiteen/ssss-cli/-/ssss-cli-0.14.5.tgz",
|
|
1594
|
+
"integrity": "sha512-qVEYOocnwn97uJ0eDzmuwN06CYsYWr2t3+iKylpisBHX4wlLWk7P8EmFFdBlx7NyKVIZDDeYT164Gd7/T8UGgw==",
|
|
1595
1595
|
"dev": true,
|
|
1596
1596
|
"license": "ISC",
|
|
1597
1597
|
"bin": {
|
|
@@ -5546,9 +5546,9 @@
|
|
|
5546
5546
|
}
|
|
5547
5547
|
},
|
|
5548
5548
|
"node_modules/total-recall-brain": {
|
|
5549
|
-
"version": "3.
|
|
5550
|
-
"resolved": "https://registry.npmjs.org/total-recall-brain/-/total-recall-brain-3.
|
|
5551
|
-
"integrity": "sha512-
|
|
5549
|
+
"version": "3.43.3",
|
|
5550
|
+
"resolved": "https://registry.npmjs.org/total-recall-brain/-/total-recall-brain-3.43.3.tgz",
|
|
5551
|
+
"integrity": "sha512-yjfPN/BQxEEa+iodyFXQov/6fRt1pZg/7RcmgsbpALwBt6Ki3JJjT3tzArBC7NxHQmXgGYyKnCYId6xe/W2rPg==",
|
|
5552
5552
|
"dev": true,
|
|
5553
5553
|
"license": "MIT",
|
|
5554
5554
|
"workspaces": [
|
|
@@ -5579,6 +5579,7 @@
|
|
|
5579
5579
|
"antigravity": "bin/antigravity.mjs",
|
|
5580
5580
|
"total-recall": "bin/total-recall.mjs",
|
|
5581
5581
|
"total-recall-memory": "bin/total-recall-memory.mjs",
|
|
5582
|
+
"total-recall-server-release": "bin/total-recall-server-release.mjs",
|
|
5582
5583
|
"vibebrain": "bin/total-recall.mjs"
|
|
5583
5584
|
},
|
|
5584
5585
|
"optionalDependencies": {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tr-plugin-meta-harness",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.9",
|
|
4
4
|
"description": "Total Recall Meta Harness plugin",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -22,9 +22,9 @@
|
|
|
22
22
|
"access": "public"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@gregiteen/ssss-cli": "0.14.
|
|
25
|
+
"@gregiteen/ssss-cli": "0.14.5"
|
|
26
26
|
},
|
|
27
27
|
"overrides": {
|
|
28
|
-
"@ssss/cli": "npm:@gregiteen/ssss-cli@0.14.
|
|
28
|
+
"@ssss/cli": "npm:@gregiteen/ssss-cli@0.14.5"
|
|
29
29
|
}
|
|
30
30
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "meta-harness",
|
|
3
3
|
"name": "Meta Harness",
|
|
4
|
-
"version": "0.3.
|
|
4
|
+
"version": "0.3.9",
|
|
5
5
|
"description": "Discover, configure, document, track, and run coding agent harnesses through Total Recall across mesh nodes.",
|
|
6
6
|
"gate": {
|
|
7
7
|
"tasks": ["Run work in another agent harness", "Choose which harness takes a task", "Check harness versions, sign-in and quota", "Read a harness's official documentation"],
|
|
@@ -67,6 +67,27 @@
|
|
|
67
67
|
],
|
|
68
68
|
"timeout_ms": 120000
|
|
69
69
|
},
|
|
70
|
+
{
|
|
71
|
+
"name": "adoption",
|
|
72
|
+
"description": "Check installed agents, native gate activity and managed run receipts on this node or the mesh",
|
|
73
|
+
"effect": "read",
|
|
74
|
+
"output": "json",
|
|
75
|
+
"args": [
|
|
76
|
+
{
|
|
77
|
+
"name": "node",
|
|
78
|
+
"flag": "--node",
|
|
79
|
+
"type": "string",
|
|
80
|
+
"label": "Mesh node"
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
"name": "all",
|
|
84
|
+
"flag": "--all",
|
|
85
|
+
"type": "boolean",
|
|
86
|
+
"label": "Check the recorded fleet"
|
|
87
|
+
}
|
|
88
|
+
],
|
|
89
|
+
"timeout_ms": 120000
|
|
90
|
+
},
|
|
70
91
|
{
|
|
71
92
|
"name": "run-command",
|
|
72
93
|
"description": "Run an explicit plugin command through the selected agent and node using a JSON request on stdin",
|
|
@@ -77,7 +98,7 @@
|
|
|
77
98
|
},
|
|
78
99
|
{
|
|
79
100
|
"name": "runtime",
|
|
80
|
-
"description": "
|
|
101
|
+
"description": "Inspect a runtime or run supported maintenance; agent tasks use dispatch",
|
|
81
102
|
"effect": "write",
|
|
82
103
|
"output": "json",
|
|
83
104
|
"args": [
|
|
@@ -91,7 +112,7 @@
|
|
|
91
112
|
{
|
|
92
113
|
"name": "operation",
|
|
93
114
|
"type": "string",
|
|
94
|
-
"label": "
|
|
115
|
+
"label": "Maintenance operation",
|
|
95
116
|
"required": true,
|
|
96
117
|
"positional": true
|
|
97
118
|
}
|
|
@@ -106,6 +127,67 @@
|
|
|
106
127
|
"args": [],
|
|
107
128
|
"timeout_ms": 600000
|
|
108
129
|
},
|
|
130
|
+
{
|
|
131
|
+
"name": "included-status",
|
|
132
|
+
"description": "Read included usage policy and optionally check Codex allowance without a turn",
|
|
133
|
+
"effect": "read",
|
|
134
|
+
"output": "json",
|
|
135
|
+
"args": [{ "name": "probe", "flag": "--probe", "type": "boolean", "label": "Read live Codex allowance" }]
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
"name": "included-enforce",
|
|
139
|
+
"description": "Require included usage for managed agent dispatch on this node",
|
|
140
|
+
"effect": "write",
|
|
141
|
+
"output": "json",
|
|
142
|
+
"args": []
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
"name": "included-bind-claude",
|
|
146
|
+
"description": "Bind verified Max credit evidence on stdin to an existing Auth key",
|
|
147
|
+
"effect": "write",
|
|
148
|
+
"output": "json",
|
|
149
|
+
"args": []
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
"name": "included-bind-claude-plan",
|
|
153
|
+
"description": "Bind the signed-in Claude Max account after verifying Usage credits are off",
|
|
154
|
+
"effect": "write",
|
|
155
|
+
"output": "json",
|
|
156
|
+
"args": []
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
"name": "openrouter-bind",
|
|
160
|
+
"description": "Authorize an explicit OpenRouter harness with a verified Total Recall Auth key and catalog model",
|
|
161
|
+
"effect": "write",
|
|
162
|
+
"output": "json",
|
|
163
|
+
"args": [
|
|
164
|
+
{ "name": "agent", "type": "string", "label": "Harness", "required": true, "positional": true },
|
|
165
|
+
{ "name": "key", "type": "string", "label": "Auth key name", "required": true, "positional": true },
|
|
166
|
+
{ "name": "model", "flag": "--model", "type": "string", "label": "OpenRouter model ID", "required": true }
|
|
167
|
+
]
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
"name": "usage-queue",
|
|
171
|
+
"description": "List jobs waiting for included usage and their reset evidence",
|
|
172
|
+
"effect": "read",
|
|
173
|
+
"output": "json",
|
|
174
|
+
"args": []
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
"name": "usage-retry",
|
|
178
|
+
"description": "Retry a waiting job after checking included usage",
|
|
179
|
+
"effect": "write",
|
|
180
|
+
"output": "json",
|
|
181
|
+
"args": [{ "name": "job", "type": "string", "label": "Queued job ID", "required": true, "positional": true }],
|
|
182
|
+
"timeout_ms": 600000
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
"name": "usage-cancel",
|
|
186
|
+
"description": "Cancel a waiting included usage job",
|
|
187
|
+
"effect": "write",
|
|
188
|
+
"output": "json",
|
|
189
|
+
"args": [{ "name": "job", "type": "string", "label": "Queued job ID", "required": true, "positional": true }]
|
|
190
|
+
},
|
|
109
191
|
{
|
|
110
192
|
"name": "docs-status",
|
|
111
193
|
"description": "Verify cached official documentation coverage freshness and hashes",
|
package/scaffold/.agent/skills/total-recall/plugins/meta-harness/skills/meta-harness/SKILL.md
CHANGED
|
@@ -25,7 +25,8 @@ Coordinates coding agent harnesses across mesh nodes. `cli-agents` is an alias o
|
|
|
25
25
|
- `review [<git-range>] [--staged] [--author <id>] [--reviewer <id>] [--thread <id>]`: review by another model family. `NO_DIFF` means there was nothing to review.
|
|
26
26
|
- `dispatch --stdin` with JSON `{agent, cwd, prompt, permissions, node, allowUnknownAuth}`: one bootstrapped run with a receipt.
|
|
27
27
|
- `threads list|show|create|close|delete`, `learn [--emit]`, `alerts check|test [--dry-run]`, `adapter list|add|remove`.
|
|
28
|
-
- `
|
|
28
|
+
- `adoption [--node <id> | --all]`: inspect native gate activity and real managed run receipts separately from installation and login.
|
|
29
|
+
- `docs-status`, `docs-refresh`, `docs-search --query <q>`, `docs-read`, `maintenance`, `setup`, `runtime <agent> <maintenance-command>`. Use dispatch for agent tasks and the service owner for gateway changes.
|
|
29
30
|
|
|
30
31
|
## When something fails
|
|
31
32
|
Read `failureKind` on the result. `quota` carries `resetsAt`, and routing avoids that harness until then. `auth` and `not-ready` mean the operator must sign in or install something. These do not count against a harness's pass rate; `agent` and `gate` failures do.
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import { executeOnNode, listTargets } from './executor.mjs';
|
|
4
|
+
import { readReceipts } from './learner.mjs';
|
|
5
|
+
import { agentEnvironment, findBinary, inspectAgents, runProcess } from './worker.mjs';
|
|
6
|
+
|
|
7
|
+
const { version } = JSON.parse(fs.readFileSync(new URL('../plugin.json', import.meta.url), 'utf8'));
|
|
8
|
+
|
|
9
|
+
async function readPreflight(args) {
|
|
10
|
+
const binary = findBinary('total-recall');
|
|
11
|
+
if (!binary) throw new Error('Total Recall CLI is unavailable');
|
|
12
|
+
const result = await runProcess(binary, ['preflight', ...args, '--json'], {
|
|
13
|
+
cwd: os.homedir(), env: agentEnvironment(), timeoutMs: 25000,
|
|
14
|
+
});
|
|
15
|
+
if (result.reason || result.exitCode !== 0) throw new Error(`Preflight query failed: ${result.reason || `exit ${result.exitCode}`}`);
|
|
16
|
+
return JSON.parse(result.stdout);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const describeRun = run => run ? {
|
|
20
|
+
id: run.id, at: run.timestamp, success: run.success === true, exitCode: run.exitCode ?? null,
|
|
21
|
+
failureKind: run.failureKind ?? null,
|
|
22
|
+
...(run.billingMode ? { billingMode: run.billingMode } : {}),
|
|
23
|
+
...(run.tools?.length ? { tools: run.tools } : {}),
|
|
24
|
+
} : null;
|
|
25
|
+
|
|
26
|
+
function latestRuns(receipts) {
|
|
27
|
+
const runs = new Map();
|
|
28
|
+
for (const row of receipts) {
|
|
29
|
+
const time = Date.parse(row.timestamp);
|
|
30
|
+
if (row.node !== 'local' || !Number.isFinite(time)) continue;
|
|
31
|
+
const state = runs.get(row.harness) || {};
|
|
32
|
+
if (!state.attempt || time >= Date.parse(state.attempt.timestamp)) state.attempt = row;
|
|
33
|
+
if (row.success === true && row.exitCode === 0 && (!state.completed || time >= Date.parse(state.completed.timestamp))) state.completed = row;
|
|
34
|
+
runs.set(row.harness, state);
|
|
35
|
+
}
|
|
36
|
+
return runs;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** This query never launches an agent or treats a synthetic hook test as live activity. */
|
|
40
|
+
export async function inspectLocalAdoption({ inspect = inspectAgents, receipts = readReceipts, preflight = readPreflight } = {}) {
|
|
41
|
+
const [inventory, history, gates, mode] = await Promise.allSettled([
|
|
42
|
+
inspect(), receipts(), preflight(['report']), preflight(['mode', '--router']),
|
|
43
|
+
]);
|
|
44
|
+
const issues = [];
|
|
45
|
+
const inventoryRows = Array.isArray(inventory.value?.agents) ? inventory.value.agents : null;
|
|
46
|
+
const receiptRows = Array.isArray(history.value) ? history.value : null;
|
|
47
|
+
const adapterRows = Array.isArray(gates.value?.adapters) ? gates.value.adapters : null;
|
|
48
|
+
const decisions = gates.value?.compliance?.byHarness;
|
|
49
|
+
const routerMode = mode.value?.router?.mode;
|
|
50
|
+
if (!inventoryRows) issues.push('Agent inventory unavailable');
|
|
51
|
+
if (!receiptRows) issues.push('Managed run receipts unavailable');
|
|
52
|
+
if (!adapterRows || !decisions) issues.push('Native preflight evidence unavailable');
|
|
53
|
+
if (!['off', 'warn', 'enforce'].includes(routerMode)) issues.push('Native router policy unavailable');
|
|
54
|
+
const runs = latestRuns(receiptRows || []);
|
|
55
|
+
const adapters = new Map((adapterRows || []).map(row => [row.id, row.verification]));
|
|
56
|
+
const agents = (inventoryRows || []).map(({ id, name, installed, authenticated, version, blockedBy }) => {
|
|
57
|
+
const gate = adapters.get(id);
|
|
58
|
+
const decisionCount = decisions ? (Object.hasOwn(decisions, id) ? decisions[id]?.total ?? 0 : 0) : null;
|
|
59
|
+
const hookStatus = !adapterRows ? 'unknown' : !gate || gate.level === 'not wired' ? 'unsupported' : gate.installed ? 'configured' : 'missing';
|
|
60
|
+
const { attempt, completed } = runs.get(id) || {};
|
|
61
|
+
return {
|
|
62
|
+
id, name, installed, authenticated, version: version ?? null, ...(blockedBy ? { blockedBy } : {}),
|
|
63
|
+
nativeGate: { status: hookStatus, recentDecisions: decisionCount },
|
|
64
|
+
managedRun: { lastAttempt: describeRun(attempt), lastCompleted: describeRun(completed) },
|
|
65
|
+
};
|
|
66
|
+
});
|
|
67
|
+
const installed = agents.filter(agent => agent.installed);
|
|
68
|
+
const managedCompletions = installed.filter(agent => agent.managedRun.lastCompleted).length;
|
|
69
|
+
return {
|
|
70
|
+
ok: issues.length === 0, pluginVersion: version, routerMode: routerMode ?? null,
|
|
71
|
+
issues, agents,
|
|
72
|
+
summary: { installed: installed.length, managedCompletions, liveHookActivity: installed.filter(agent => agent.nativeGate.recentDecisions > 0).length },
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export async function inspectFleetAdoption({ node, inventory = listTargets, readLocal = inspectLocalAdoption, execute = executeOnNode } = {}) {
|
|
77
|
+
const targets = await inventory();
|
|
78
|
+
const nodes = node ? targets.nodes.filter(row => row.id === node || row.name === node) : targets.nodes;
|
|
79
|
+
if (!nodes.length) throw new Error('Select a recorded node from meta-harness nodes');
|
|
80
|
+
const results = await Promise.all(nodes.map(async target => {
|
|
81
|
+
const row = { node: target.id, name: target.name };
|
|
82
|
+
if (!target.self && target.online === false) return { ...row, status: 'offline' };
|
|
83
|
+
if (!target.self && !target.accessConfigured) return { ...row, status: 'no-cli-access' };
|
|
84
|
+
try {
|
|
85
|
+
let report;
|
|
86
|
+
if (target.self) report = await readLocal();
|
|
87
|
+
else {
|
|
88
|
+
const result = await execute(target.id, {
|
|
89
|
+
operation: 'direct', useHome: true, timeoutMs: 40000,
|
|
90
|
+
argv: ['total-recall', 'meta-harness', 'adoption', '--json'],
|
|
91
|
+
});
|
|
92
|
+
try { report = JSON.parse(result.output); } catch { /* The installed CLI did not supply an adoption report. */ }
|
|
93
|
+
if (report?.error === 'Unknown operation: adoption') return { ...row, status: 'update-required', error: 'The installed Meta Harness does not support adoption reports.' };
|
|
94
|
+
if (!report || !Array.isArray(report.agents) || typeof report.pluginVersion !== 'string' || typeof report.ok !== 'boolean' || (report.ok && !result.ok)) {
|
|
95
|
+
return { ...row, status: 'adoption-unavailable', error: 'The installed Meta Harness did not return an adoption report. Verify its version and CLI availability.' };
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return { ...row, status: report.ok ? 'inspected' : 'partial', report };
|
|
99
|
+
} catch (error) {
|
|
100
|
+
return { ...row, status: 'probe-failed', error: String(error?.message || error).slice(0, 400) };
|
|
101
|
+
}
|
|
102
|
+
}));
|
|
103
|
+
return { ok: !targets.meshError && results.every(row => row.status === 'inspected'), ...(targets.meshError ? { meshError: targets.meshError } : {}), nodes: results };
|
|
104
|
+
}
|
|
@@ -11,6 +11,7 @@ import { routeTask } from './router.mjs';
|
|
|
11
11
|
import { runQualityGate } from './gate.mjs';
|
|
12
12
|
import { isGitRepo, createWorktree, removeWorktree, commitAll, linkDependencies, deleteBranch } from './worktree.mjs';
|
|
13
13
|
import { forwardRemoteOperation } from './remote-operation.mjs';
|
|
14
|
+
import { queueIncludedJob } from './usage-queue.mjs';
|
|
14
15
|
|
|
15
16
|
export async function executeRoutedTask(task, options = {}) {
|
|
16
17
|
const remote = await forwardRemoteOperation('route', task, { ...options, execute: true });
|
|
@@ -19,6 +20,13 @@ export async function executeRoutedTask(task, options = {}) {
|
|
|
19
20
|
const cwd = options.cwd || process.cwd();
|
|
20
21
|
if (!isGitRepo(cwd)) throw new Error('Routed execution writes on its own branch, so it must run inside a git repository');
|
|
21
22
|
const route = options.route || await routeTask(task, options);
|
|
23
|
+
const replay = { kind: 'route', gate: options.gate === true, gateOptions: options.gateOptions };
|
|
24
|
+
if (!route.selected && route.deferred) {
|
|
25
|
+
const blocked = { ok: false, failureKind: 'quota', error: 'The included usage allowance is exhausted.', resetsAt: route.deferred.resetsAt };
|
|
26
|
+
const job = await queueIncludedJob({ agent: route.deferred.harness, cwd, prompt: task }, blocked,
|
|
27
|
+
{ role: 'Routed', replay, launchPrevented: true });
|
|
28
|
+
return { ...blocked, route, attempts: [], queued: true, queueId: job.id };
|
|
29
|
+
}
|
|
22
30
|
if (!route.selected) return { ok: false, route, attempts: [], error: 'No harness can take this task right now.' };
|
|
23
31
|
const node = localExecutionNode(options, route.placement);
|
|
24
32
|
const pool = options.pool || (await resolvePool(options)).ready.map(h => h.id);
|
|
@@ -35,9 +43,10 @@ export async function executeRoutedTask(task, options = {}) {
|
|
|
35
43
|
const directory = createWorktree(cwd, { branch });
|
|
36
44
|
const attempt = { harness, branch: null, commit: null, gate: null, escalatedFrom: previous };
|
|
37
45
|
attempts.push(attempt);
|
|
46
|
+
let stopped;
|
|
38
47
|
try {
|
|
39
48
|
const prompt = `Work only in this checkout. Do not commit; the harness commits your changes.\nTask:\n${task}${diagnostic ? `\n\nA previous attempt by ${previous} failed. Diagnostic:\n${diagnostic}` : ''}`;
|
|
40
|
-
const result = await dispatchWithReceipt({ agent: harness, node, cwd: directory, contextCwd: cwd, contextTask: task, prompt, permissions: { read: true, write: true }, allowUnknownAuth: options.allowUnknownAuth === true }, { dispatchFn: options.dispatchFn, role: 'Routed', complexityScore: route.complexity?.score, escalatedFrom: previous });
|
|
49
|
+
const result = await dispatchWithReceipt({ agent: harness, node, cwd: directory, contextCwd: cwd, contextTask: task, prompt, permissions: { read: true, write: true }, allowUnknownAuth: options.allowUnknownAuth === true }, { dispatchFn: options.dispatchFn, role: 'Routed', complexityScore: route.complexity?.score, escalatedFrom: previous, replay, retryId: options.retryId });
|
|
41
50
|
attempt.ok = result.ok === true;
|
|
42
51
|
attempt.durationMs = result.durationMs;
|
|
43
52
|
attempt.output = result.response || null;
|
|
@@ -51,6 +60,7 @@ export async function executeRoutedTask(task, options = {}) {
|
|
|
51
60
|
} else {
|
|
52
61
|
attempt.error = result.error;
|
|
53
62
|
attempt.failureKind = result.failureKind;
|
|
63
|
+
if (result.queued || result.queueError || (result.failureKind === 'quota' && options.retryId) || result.failureKind === 'billing-policy') stopped = result;
|
|
54
64
|
}
|
|
55
65
|
if (attempt.ok && options.gate) {
|
|
56
66
|
linkDependencies(cwd, directory);
|
|
@@ -74,6 +84,7 @@ export async function executeRoutedTask(task, options = {}) {
|
|
|
74
84
|
}
|
|
75
85
|
|
|
76
86
|
if (attempt.ok) return { ok: true, route, gateApplied: Boolean(options.gate), branch: attempt.branch, commit: attempt.commit, harness, attempts, alerts };
|
|
87
|
+
if (stopped) return { ...stopped, route, attempts, alerts };
|
|
77
88
|
|
|
78
89
|
const remaining = pool.filter(item => !tried.has(item));
|
|
79
90
|
const next = remaining.length ? evaluateGateCascade(harness, { testsPassed: false }, remaining) : null;
|
|
@@ -8,6 +8,7 @@ import { recordRunReceipt } from './learner.mjs';
|
|
|
8
8
|
import { classifyTaskComplexity, synthesizeOneWayValve } from '../capability/index.mjs';
|
|
9
9
|
import { notifyAlert } from './alerts.mjs';
|
|
10
10
|
import { dispatchWithReceipt } from './dispatch.mjs';
|
|
11
|
+
import { queueIncludedJob } from './usage-queue.mjs';
|
|
11
12
|
import { resolvePool, localExecutionNode } from './pool.mjs';
|
|
12
13
|
import { createThread, appendThreadTurn, updateThread } from './threads.mjs';
|
|
13
14
|
import { executeCodeReview } from './review.mjs';
|
|
@@ -101,9 +102,10 @@ async function runPipeline(task, poolInfo, options) {
|
|
|
101
102
|
const run = async (stage, harness, prompt, { workdir = cwd, permissions, role = stage.role } = {}) => {
|
|
102
103
|
stage.status = 'running';
|
|
103
104
|
const result = await dispatchWithReceipt({ agent: harness.id, node: 'local', cwd: workdir, contextCwd: cwd, contextTask: task, prompt, permissions: permissions || stage.permissions, allowUnknownAuth: options.allowUnknownAuth === true }, { dispatchFn: options.dispatchFn, role, complexityScore: complexity.score });
|
|
104
|
-
stage.status = result.ok ? 'completed' : 'failed';
|
|
105
|
+
stage.status = result.ok ? 'completed' : result.queued ? 'queued' : 'failed';
|
|
105
106
|
stage.output = result.response || result.error || null;
|
|
106
107
|
stage.failureKind = result.ok ? undefined : result.failureKind;
|
|
108
|
+
if (result.queueId) stage.queueId = result.queueId;
|
|
107
109
|
appendThreadTurn(thread.id, { role: role.toLowerCase(), agent: harness.id, content: stage.output || '' });
|
|
108
110
|
return result;
|
|
109
111
|
};
|
|
@@ -186,7 +188,8 @@ async function runCouncil(task, poolInfo, options) {
|
|
|
186
188
|
const cwd = options.cwd || process.cwd();
|
|
187
189
|
const votes = await Promise.all(seats.map(async harness => {
|
|
188
190
|
const result = await dispatchWithReceipt({ agent: harness, node: 'local', cwd, contextTask: task, prompt: `[ROLE: COUNCIL MEMBER]\nDo not change any file. Assess this proposal, with its trade-offs and risks:\n${task}\n\nEnd your reply with exactly one JSON object: {"vote":"APPROVE"|"REJECT","reason":"..."}`, permissions: READ, allowUnknownAuth: options.allowUnknownAuth === true }, { dispatchFn: options.dispatchFn, role: 'Council' });
|
|
189
|
-
return { harness, ok: result.ok === true, ...(result.ok ? parseVote(result.response) : { vote: 'ABSTAIN', reason: null, error: result.error }),
|
|
191
|
+
return { harness, ok: result.ok === true, ...(result.ok ? parseVote(result.response) : { vote: 'ABSTAIN', reason: null, error: result.error }),
|
|
192
|
+
...(result.queueId ? { queueId: result.queueId } : {}), feedback: result.response || null };
|
|
190
193
|
}));
|
|
191
194
|
const count = vote => votes.filter(item => item.vote === vote).length;
|
|
192
195
|
const majority = votes.length / 2;
|
|
@@ -214,7 +217,8 @@ async function runRace(task, poolInfo, options) {
|
|
|
214
217
|
if (commit && options.gate) linkDependencies(cwd, directory);
|
|
215
218
|
const gate = commit ? await (options.gateFn || runQualityGate)(directory, options.gateOptions) : null;
|
|
216
219
|
keep = Boolean(commit) && gate?.passed === true;
|
|
217
|
-
return { harness, branch: keep ? branch : null, commit: keep ? commit : null, ok: keep, gate, completedAt: Date.now(), durationMs: Date.now() - started, error: result.ok ? (commit ? (keep ? undefined : 'The quality gate failed') : 'No files changed') : result.error
|
|
220
|
+
return { harness, branch: keep ? branch : null, commit: keep ? commit : null, ok: keep, gate, completedAt: Date.now(), durationMs: Date.now() - started, error: result.ok ? (commit ? (keep ? undefined : 'The quality gate failed') : 'No files changed') : result.error,
|
|
221
|
+
...(result.queueId ? { queueId: result.queueId } : {}) };
|
|
218
222
|
} catch (error) {
|
|
219
223
|
return { harness, branch: null, commit: null, ok: false, gate: null, durationMs: null, error: String(error.message) };
|
|
220
224
|
} finally {
|
|
@@ -235,6 +239,14 @@ export async function coordinateTask(taskDescription, options = {}) {
|
|
|
235
239
|
if (!['pipeline', 'orchestrate', 'council', 'race'].includes(mode)) throw new Error(`Unsupported coordination mode: ${mode}`);
|
|
236
240
|
localExecutionNode(options);
|
|
237
241
|
const poolInfo = await resolvePool(options);
|
|
242
|
+
const wall = poolInfo.excluded.find(item => item.failureKind === 'quota');
|
|
243
|
+
const minimum = mode === 'race' || ((mode === 'pipeline' || mode === 'orchestrate') && options.allowSameFamily !== true) ? 2 : 1;
|
|
244
|
+
if (options.execute === true && wall && poolInfo.ready.length < minimum) {
|
|
245
|
+
const blocked = { ok: false, failureKind: 'quota', error: 'Coordination is waiting for included usage.', resetsAt: wall.resetsAt };
|
|
246
|
+
const job = await queueIncludedJob({ agent: wall.harness, cwd: options.cwd || process.cwd(), prompt: task }, blocked,
|
|
247
|
+
{ role: 'Coordinate', launchPrevented: true });
|
|
248
|
+
return { ...blocked, mode, executedLive: false, queued: true, queueId: job.id, needsReview: job.needsReview };
|
|
249
|
+
}
|
|
238
250
|
if (mode === 'council') return runCouncil(task, poolInfo, options);
|
|
239
251
|
if (mode === 'race') return runRace(task, poolInfo, options);
|
|
240
252
|
return runPipeline(task, poolInfo, options);
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
|
|
3
|
+
const DEFAULT_PERMISSIONS = Object.freeze({ read: true, write: false, send: false, destructive: false });
|
|
4
|
+
export { DEFAULT_PERMISSIONS };
|
|
5
|
+
|
|
6
|
+
export function commandPermissions(value = DEFAULT_PERMISSIONS) {
|
|
7
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error('Command permissions must be boolean values');
|
|
8
|
+
for (const key of Object.keys(value)) {
|
|
9
|
+
if (!Object.hasOwn(DEFAULT_PERMISSIONS, key) || typeof value[key] !== 'boolean') throw new Error(`Invalid permission ${key}`);
|
|
10
|
+
}
|
|
11
|
+
return { ...DEFAULT_PERMISSIONS, ...value };
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const PUBLIC_FIELDS = [
|
|
15
|
+
'agent', 'cwd', 'prompt', 'permissions', 'node', 'allowUnknownAuth', 'timeoutMs',
|
|
16
|
+
'model', 'tools', 'settingSources', 'allowedTools',
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
export function validateDispatchRequest(request) {
|
|
20
|
+
if (!request || typeof request !== 'object' || Array.isArray(request)) throw new Error('Dispatch requires a JSON object');
|
|
21
|
+
if (Object.keys(request).some(key => !PUBLIC_FIELDS.includes(key))) throw new Error('Unsupported dispatch field; workspace context and containment are managed by Meta Harness');
|
|
22
|
+
if (typeof request.agent !== 'string' || !/^[a-z][a-z0-9-]{0,63}$/.test(request.agent) ||
|
|
23
|
+
typeof request.cwd !== 'string' || !path.isAbsolute(request.cwd) || request.cwd.includes('\0') ||
|
|
24
|
+
typeof request.prompt !== 'string' || !request.prompt.trim() || Buffer.byteLength(request.prompt) > 65536) {
|
|
25
|
+
throw new Error('Dispatch requires an explicit agent, absolute workspace, and bounded nonempty prompt');
|
|
26
|
+
}
|
|
27
|
+
if (request.node !== undefined && (typeof request.node !== 'string' || !request.node.trim() || /[\0\r\n]/.test(request.node))) throw new Error('Invalid dispatch node');
|
|
28
|
+
if (request.allowUnknownAuth !== undefined && typeof request.allowUnknownAuth !== 'boolean') throw new Error('allowUnknownAuth must be a boolean');
|
|
29
|
+
const timeoutMs = request.timeoutMs === undefined ? 300000 : request.timeoutMs;
|
|
30
|
+
if (!Number.isInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 540000) throw new Error('Foreground dispatch timeout must be 1..540000 ms');
|
|
31
|
+
return { ...request, permissions: commandPermissions(request.permissions), timeoutMs };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** The installed destination starts from its own project context and decides containment itself. */
|
|
35
|
+
export function destinationDispatchRequest(request) {
|
|
36
|
+
const fields = PUBLIC_FIELDS.filter(key => key !== 'node' && request[key] !== undefined);
|
|
37
|
+
return validateDispatchRequest(Object.fromEntries(fields.map(key => [key, request[key]])));
|
|
38
|
+
}
|
|
@@ -1,22 +1,37 @@
|
|
|
1
1
|
/** One path for every agent run: contain it if needed, dispatch it, classify the outcome, write its receipt. */
|
|
2
2
|
import { executeOnNode } from './executor.mjs';
|
|
3
|
-
import { classifyFailure, READ_ONLY_ADAPTERS } from './worker.mjs';
|
|
3
|
+
import { classifyFailure, INCLUDED_BILLING, READ_ONLY_ADAPTERS } from './worker.mjs';
|
|
4
4
|
import { recordRunReceipt } from './learner.mjs';
|
|
5
5
|
import { isGitRepo, createWorktree, removeWorktree } from './worktree.mjs';
|
|
6
|
+
import { includedAdmission, openRouterAdmission, readIncludedPolicy } from './included-usage.mjs';
|
|
7
|
+
import { queueIncludedJob } from './usage-queue.mjs';
|
|
6
8
|
|
|
7
|
-
export async function dispatchWithReceipt(request, { dispatchFn, node = request.node || 'local', role, complexityScore, escalatedFrom,
|
|
9
|
+
export async function dispatchWithReceipt(request, { dispatchFn, node = request.node || 'local', role, complexityScore, escalatedFrom,
|
|
10
|
+
record = recordRunReceipt, replay, retryId, usagePolicy = readIncludedPolicy, admit = includedAdmission, admitOpenRouter = openRouterAdmission } = {}) {
|
|
8
11
|
const started = Date.now();
|
|
9
12
|
// A harness that cannot be told not to write does read-only work in a checkout that is thrown away.
|
|
10
13
|
const contain = node === 'local' && request.permissions?.write !== true && !READ_ONLY_ADAPTERS.includes(request.agent) && request.isolated !== true && isGitRepo(request.cwd);
|
|
11
|
-
let scratch = null, result;
|
|
14
|
+
let scratch = null, result, billingMode = null, launchPrevented = false;
|
|
12
15
|
try {
|
|
13
16
|
let effective = request;
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
+
const policy = await usagePolicy();
|
|
18
|
+
if (policy.enabled && node === 'local') {
|
|
19
|
+
const openRouter = Object.hasOwn(policy.openRouter ?? {}, request.agent);
|
|
20
|
+
billingMode = openRouter ? 'openrouter' : 'included';
|
|
21
|
+
const admission = openRouter ? await admitOpenRouter(request.agent, policy) : await admit(request.agent, request.cwd, policy);
|
|
22
|
+
if (!admission.ok) {
|
|
23
|
+
result = { ...admission, agent: request.agent, exitCode: null };
|
|
24
|
+
launchPrevented = true;
|
|
25
|
+
} else effective = { ...request, [INCLUDED_BILLING]: { key: admission.key, claudePlan: admission.claudePlan, openRouter: admission.openRouter } };
|
|
26
|
+
}
|
|
27
|
+
if (!result) {
|
|
28
|
+
if (contain) {
|
|
29
|
+
scratch = createWorktree(request.cwd, { prefix: 'meta-harness-readonly-' });
|
|
30
|
+
effective = { ...effective, cwd: scratch, contextCwd: request.contextCwd || request.cwd, isolated: true };
|
|
31
|
+
}
|
|
32
|
+
result = dispatchFn ? await dispatchFn(effective) : await executeOnNode(node, { ...effective, operation: 'dispatch' });
|
|
33
|
+
if (contain) result = { ...result, readOnly: 'disposable-checkout' };
|
|
17
34
|
}
|
|
18
|
-
result = dispatchFn ? await dispatchFn(effective) : await executeOnNode(node, { ...effective, operation: 'dispatch' });
|
|
19
|
-
if (contain) result = { ...result, readOnly: 'disposable-checkout' };
|
|
20
35
|
} catch (error) {
|
|
21
36
|
const message = String(error?.message || error);
|
|
22
37
|
result = { ok: false, error: message, agent: request.agent, ...(error?.code === 'NOT_READY' ? { failureKind: 'not-ready' } : classifyFailure(message)) };
|
|
@@ -24,15 +39,31 @@ export async function dispatchWithReceipt(request, { dispatchFn, node = request.
|
|
|
24
39
|
if (scratch) removeWorktree(request.cwd, scratch);
|
|
25
40
|
}
|
|
26
41
|
const durationMs = Date.now() - started;
|
|
42
|
+
if (result.execution?.receiptOwner === 'destination' && result.receiptId) return { ...result, durationMs };
|
|
43
|
+
if (result.execution?.remote && (result.queued || result.launchPrevented)) return { ...result, durationMs };
|
|
44
|
+
if (billingMode === 'included' && result.failureKind === 'quota') {
|
|
45
|
+
if (retryId) result = { ...result, needsReview: !launchPrevented && request.permissions?.write === true };
|
|
46
|
+
else {
|
|
47
|
+
try {
|
|
48
|
+
const job = await queueIncludedJob(request, result, { role, replay, launchPrevented });
|
|
49
|
+
result = { ...result, queued: true, queueId: job.id, needsReview: job.needsReview };
|
|
50
|
+
} catch (error) {
|
|
51
|
+
result = { ...result, queueError: error.message };
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (launchPrevented) return { ...result, launchPrevented, durationMs };
|
|
27
56
|
// A finished run keeps its result: the worktree and any cost are real even when bookkeeping fails.
|
|
28
|
-
let receiptError;
|
|
57
|
+
let receipt, receiptError;
|
|
29
58
|
try {
|
|
30
|
-
await record({
|
|
59
|
+
receipt = await record({
|
|
31
60
|
harness: request.agent, role, node, task: request.contextTask || request.prompt, complexityScore,
|
|
61
|
+
billingMode,
|
|
32
62
|
model: result.model ?? null, exitCode: result.exitCode ?? null, finishedReason: result.finishedReason ?? null,
|
|
63
|
+
tools: result.toolResults,
|
|
33
64
|
success: result.ok === true, durationMs, failureKind: result.ok ? null : result.failureKind || 'agent',
|
|
34
65
|
error: result.ok ? null : result.error, resetsAt: result.resetsAt, escalatedFrom,
|
|
35
66
|
});
|
|
36
67
|
} catch (error) { receiptError = String(error?.message || error); }
|
|
37
|
-
return { ...result, durationMs, ...(receiptError ? { receiptError } : {}) };
|
|
68
|
+
return { ...result, durationMs, ...(billingMode ? { billingMode } : {}), ...(receipt?.id ? { receiptId: receipt.id } : {}), ...(receiptError ? { receiptError } : {}) };
|
|
38
69
|
}
|