carthagent 1.1.0-beta.2 → 1.1.0-beta.3
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/docs/beta2-published-acceptance.md +79 -0
- package/docs/cloud-onboarding-acceptance.md +6 -3
- package/docs/release-notes-beta.3.md +47 -0
- package/lib/opencode/cli-runtime.mjs +22 -1
- package/lib/opencode/cli.mjs +6 -3
- package/lib/opencode/runtime-release.json +11 -11
- package/package.json +1 -1
- package/tests/cli.test.mjs +2 -2
- package/tests/opencode-cli.test.mjs +36 -2
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# Beta.2 release acceptance
|
|
2
|
+
|
|
3
|
+
November 2, 2026. Immutable tag `v1.1.0-beta.2` points to
|
|
4
|
+
`e14aeb17c213a927a27e9522da65db5928219647`.
|
|
5
|
+
|
|
6
|
+
## Completed
|
|
7
|
+
|
|
8
|
+
- Native onboarding PR #238 and first-task guidance PR #239 merged. Release PR
|
|
9
|
+
#240 merged with tag ancestry preserved at `fc5382364f41b1563ed137ed0db0c5711e041a83`.
|
|
10
|
+
- Five-platform prebuild [41505735348](https://github.com/B-icy/Carthagent/actions/runs/41505735348)
|
|
11
|
+
passed. Local manifest regeneration verified all archive/executable hashes and
|
|
12
|
+
embedded provenance against the pinned runtime recipe.
|
|
13
|
+
- Release CI [41509102387](https://github.com/B-icy/Carthagent/actions/runs/41509102387)
|
|
14
|
+
passed across Linux, macOS and Windows, including installed-package acceptance.
|
|
15
|
+
- Final draft acceptance [41511681097](https://github.com/B-icy/Carthagent/actions/runs/41511681097)
|
|
16
|
+
passed against the exact tag, assets and main ancestry.
|
|
17
|
+
- GitHub prerelease is public with five runtime archives, the manifest and
|
|
18
|
+
combined checksums. Independent anonymous verification of all five public
|
|
19
|
+
archives passed.
|
|
20
|
+
- Release workflow [41512736590](https://github.com/B-icy/Carthagent/actions/runs/41512736590)
|
|
21
|
+
passed anonymous installed-package acceptance and reports successful npm
|
|
22
|
+
publication with signed provenance (transparency log index 3648230788).
|
|
23
|
+
- Backend onboarding #178 and retention reporting #179 are deployed. Live
|
|
24
|
+
authenticated account identity and read-only plan terms were verified earlier.
|
|
25
|
+
|
|
26
|
+
## npm publication verified
|
|
27
|
+
|
|
28
|
+
After the workflow reported `+ carthagent@1.1.0-beta.2`, public registry requests
|
|
29
|
+
from the acceptance environment still returned 404 for that exact version.
|
|
30
|
+
Both npm CLI and cache-bypassed package metadata initially reported
|
|
31
|
+
`next=1.1.0-beta.1`, `latest=1.0.7`. Registry visibility subsequently converged;
|
|
32
|
+
no repeat publication was attempted.
|
|
33
|
+
|
|
34
|
+
The public beta.2 tarball's SHA-512 matches registry integrity and the provenance
|
|
35
|
+
subject digest. Sigstore verification passed for the GitHub Actions issuer and
|
|
36
|
+
the repository's `release.yml@refs/heads/main` identity. The statement identifies
|
|
37
|
+
release workflow 41512736590, and package `gitHead` matches the immutable tag.
|
|
38
|
+
Verified tags: `next=1.1.0-beta.2`, `latest=1.0.7`.
|
|
39
|
+
Evidence: `/tmp/opencode/beta2-npm-acceptance.json`.
|
|
40
|
+
|
|
41
|
+
## Installed update verified
|
|
42
|
+
|
|
43
|
+
The website command `npm install -g carthagent@next` installed beta.2 into an
|
|
44
|
+
isolated Linux x64 home/prefix. Default runtime acquisition resolved the pinned
|
|
45
|
+
CTG-owned executable with SHA-256
|
|
46
|
+
`8a34735274c0b62049f6ae882dc955016fde54458efc18822fbcedb2e8eddeda`.
|
|
47
|
+
The beta update check reported the installed version as current.
|
|
48
|
+
Evidence: `/tmp/opencode/beta2-clean-install-acceptance.json`.
|
|
49
|
+
|
|
50
|
+
An isolated npm installation of beta.1 detected beta.2 with
|
|
51
|
+
`ctg update --channel beta --check`. Running `ctg update --channel beta` upgraded
|
|
52
|
+
it to beta.2; a profile sentinel remained byte-for-byte intact. This used a
|
|
53
|
+
disposable home and global npm prefix, not the user's normal installation.
|
|
54
|
+
Evidence: `/tmp/opencode/beta2-update-acceptance.json`.
|
|
55
|
+
|
|
56
|
+
## Remaining acceptance
|
|
57
|
+
|
|
58
|
+
Stripe sandbox access subsequently became available. Real purchases, upgrades,
|
|
59
|
+
SCA, renewal, failed renewal and cancellation were exercised separately against
|
|
60
|
+
the isolated backend (see backend `docs/stripe-sandbox-acceptance.md`).
|
|
61
|
+
|
|
62
|
+
An isolated published beta.2 login connected the authorized existing Cloud account.
|
|
63
|
+
A real range-fix task passed four independent tests. Continuing that same session
|
|
64
|
+
hit its persistent elapsed-time budget; beta.2 showed only an unexplained interrupt.
|
|
65
|
+
The follow-up source fix adds actionable budget diagnostics and explicit
|
|
66
|
+
`--reset-budget` support. With that source launcher and the same installed runtime,
|
|
67
|
+
profile and session, continuation added an ascending-across-zero regression and
|
|
68
|
+
passed all five independent tests. This launcher fix is not part of immutable beta.2.
|
|
69
|
+
Execution budgets are not billing credit and never reset on payment or ordinary
|
|
70
|
+
continuation. Evidence: `/tmp/opencode/stripe-acceptance/live-continuation-fixed.log`.
|
|
71
|
+
|
|
72
|
+
Full task continuation across a genuine payment boundary remains incomplete:
|
|
73
|
+
the real model task used existing Cloud credit and sandbox payments used a separate
|
|
74
|
+
backend without a model upstream. Screenshot approval is not claimed. Local terminal timing failures
|
|
75
|
+
were followed by passing full local, CI and draft acceptance; this does not prove
|
|
76
|
+
that every terminal timing issue is resolved.
|
|
77
|
+
|
|
78
|
+
The normal installed profile and Invest-With-Friends files were not changed by
|
|
79
|
+
these release checks. Stable npm `latest` remains 1.0.7.
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
# Cloud onboarding —
|
|
1
|
+
# Cloud onboarding — beta.2 implementation
|
|
2
2
|
|
|
3
|
-
November 2, 2026.
|
|
4
|
-
1.
|
|
3
|
+
November 2, 2026. Beta.2 is published on GitHub and npm `next`; `latest` remains
|
|
4
|
+
1.0.7. See [published acceptance](beta2-published-acceptance.md) for final CI,
|
|
5
|
+
draft/public runtime gates, signed npm provenance, Linux clean installation and
|
|
6
|
+
isolated beta.1 → beta.2 update evidence. Historical preparation notes below do
|
|
7
|
+
not supersede that release record.
|
|
5
8
|
|
|
6
9
|
Native PR #238 merged at `a5ad2164d056fee7f7183579ce6cc97305bae53b` after CI
|
|
7
10
|
41504674623 passed. First-task guidance is tracked in PR #239. Beta.2 runtime
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Carthagent 1.1.0-beta.3 — release candidate
|
|
2
|
+
|
|
3
|
+
This candidate makes continuation failures actionable. Publication is pending;
|
|
4
|
+
these notes do not establish npm availability.
|
|
5
|
+
|
|
6
|
+
## Continue the existing task
|
|
7
|
+
|
|
8
|
+
- An exhausted persistent execution budget now reports the reason and recovery
|
|
9
|
+
command instead of an unexplained interrupt.
|
|
10
|
+
- `--reset-budget` explicitly authorizes a fresh budget for a resumed session:
|
|
11
|
+
|
|
12
|
+
```sh
|
|
13
|
+
ctg --print --continue --reset-budget "Continue the task"
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
- Ordinary continuation and payment preserve the execution budget, including
|
|
17
|
+
elapsed idle time. Billing credit is separate. Interactive users can run
|
|
18
|
+
`/delivery-budget-reset` before continuing the existing task.
|
|
19
|
+
- CLI help consistently names Carthagent Cloud and documents `billing refresh`.
|
|
20
|
+
|
|
21
|
+
## Acceptance evidence
|
|
22
|
+
|
|
23
|
+
The source fix passed 1,132 tests with five skips, builds and lint. PR #242 CI
|
|
24
|
+
passed Linux, macOS and Windows, browser and installed-package gates. One macOS
|
|
25
|
+
22 service-stop identity timeout passed on retry; the separate push run passed.
|
|
26
|
+
|
|
27
|
+
The authorized existing Cloud account completed a real range-fix task. The same
|
|
28
|
+
session, profile and installed beta.2 runtime then continued with the source
|
|
29
|
+
launcher and explicit reset, adding a regression and passing all five project
|
|
30
|
+
tests independently. Final beta.3 installed acceptance remains required.
|
|
31
|
+
|
|
32
|
+
Backend PRs #180–181 are deployed: automatic payment-status checks, clearer
|
|
33
|
+
sign-in and refund guidance, direct Builder review, modern Stripe compatibility
|
|
34
|
+
and accurate scheduled-cancellation dates while retaining paid access.
|
|
35
|
+
Sandbox purchases, prorated upgrades, initial and upgrade SCA, declined-upgrade
|
|
36
|
+
recovery, renewal, failed renewal, automatic refunds and disputes were exercised
|
|
37
|
+
separately. These do not establish a single
|
|
38
|
+
useful-task → payment boundary → same-task continuation journey: the model task
|
|
39
|
+
used existing production credit and payments used an isolated sandbox backend.
|
|
40
|
+
|
|
41
|
+
## Release channel
|
|
42
|
+
|
|
43
|
+
Targets npm `next`; stable `latest` remains 1.0.7. Runtime prebuild
|
|
44
|
+
[41537249365](https://github.com/B-icy/Carthagent/actions/runs/41537249365) uses
|
|
45
|
+
reviewed source `c11103f8b57c6b3254473a78d535a404bea9a84a` and the unchanged
|
|
46
|
+
combined-v5 runtime recipe. Final assets and manifest must pass immutable draft
|
|
47
|
+
acceptance before publication.
|
|
@@ -100,6 +100,7 @@ function errorText(error) {
|
|
|
100
100
|
function headlessFailureMessage(latest, headlessError) {
|
|
101
101
|
const raw = errorText(latest?.error ?? latest?.session?.error ?? lastAssistantError(latest?.messages) ?? headlessError);
|
|
102
102
|
const outcome = latest?.outcome ?? latest?.session?.outcome;
|
|
103
|
+
if (['elapsed-time', 'tool-calls', 'repair-rounds'].includes(latest?.budgetReason)) return `Session execution budget exhausted: ${latest.budgetReason}. Billing refresh does not reset execution limits. To authorize a fresh budget, resume this session with --reset-budget (for example: ctg --print --continue --reset-budget "Continue the task"), or run /delivery-budget-reset in the existing terminal session before continuing.`;
|
|
103
104
|
if (/ModelNotSelectedError|No model is available|no provider|provider.*not (connected|selected|configured)|not authenticated/i.test(raw ?? '')) {
|
|
104
105
|
return 'No AI provider connected. Run `ctg login` to connect one (Carthagent Cloud is recommended), or set an API key env var (e.g. ANTHROPIC_API_KEY), then retry. Run `ctg native --list-models` to see available models.';
|
|
105
106
|
}
|
|
@@ -559,6 +560,9 @@ export async function createOpenCodeCliRuntime({ cwd = process.cwd(), io = conso
|
|
|
559
560
|
options.session = await latestSessionForWorkspace(client, cwd);
|
|
560
561
|
if (!options.session) throw new Error('No native Carthagent session exists for this workspace to continue.');
|
|
561
562
|
}
|
|
563
|
+
if (options.resetBudget && !options.session && !options.continue && !options.resume) {
|
|
564
|
+
throw new Error('--reset-budget requires --session, --continue or --resume.');
|
|
565
|
+
}
|
|
562
566
|
eventController = new AbortController();
|
|
563
567
|
const nativeEvents = [];
|
|
564
568
|
let eventFailure;
|
|
@@ -600,6 +604,9 @@ export async function createOpenCodeCliRuntime({ cwd = process.cwd(), io = conso
|
|
|
600
604
|
const definitions = native.rpcDefinitions ?? await nativeRpcDefinitions();
|
|
601
605
|
await client.rpc(definitions.SessionControls).framing({ sessionID, enabled: options.autoFraming }, { location: { directory: cwd }, signal: signal.controller.signal });
|
|
602
606
|
}
|
|
607
|
+
if (options.resetBudget && sessionID) {
|
|
608
|
+
await client.session.command({ sessionID, name: 'delivery-budget-reset', text: '' }, { location: { directory: cwd }, signal: signal.controller.signal });
|
|
609
|
+
}
|
|
603
610
|
if (framedPrompt) {
|
|
604
611
|
if (!sessionID) {
|
|
605
612
|
const created = await headlessApi.createSession(service, cwd, { body: {
|
|
@@ -644,6 +651,11 @@ export async function createOpenCodeCliRuntime({ cwd = process.cwd(), io = conso
|
|
|
644
651
|
const definitions = native.rpcDefinitions ?? await nativeRpcDefinitions();
|
|
645
652
|
await client.rpc(definitions.SessionControls).framing({ sessionID: requestedSession, enabled: options.autoFraming }, { location: { directory: cwd }, signal: signal.controller.signal });
|
|
646
653
|
}
|
|
654
|
+
if (requestedSession && options.resetBudget && !signal.controller.signal.aborted) {
|
|
655
|
+
const resumed = await client.session.get({ sessionID: requestedSession }, { signal: signal.controller.signal });
|
|
656
|
+
assertSessionWorkspace(resumed, cwd);
|
|
657
|
+
await client.session.command({ sessionID: requestedSession, name: 'delivery-budget-reset', text: '' }, { location: { directory: cwd }, signal: signal.controller.signal });
|
|
658
|
+
}
|
|
647
659
|
try {
|
|
648
660
|
result = await headlessApi.runHeadless(service, cwd, framedPrompt || prompt || '', {
|
|
649
661
|
sessionID: options.session,
|
|
@@ -658,7 +670,16 @@ export async function createOpenCodeCliRuntime({ cwd = process.cwd(), io = conso
|
|
|
658
670
|
headlessError = error;
|
|
659
671
|
result = { session: { id: options.session }, messages: [], outcome: signal.controller.signal.aborted ? 'interrupted' : 'failed', terminal: true, error: error.message };
|
|
660
672
|
}
|
|
661
|
-
|
|
673
|
+
let latest = headlessError ? result : await reconcileHeadlessResult(result, { client, signal: signal.controller.signal });
|
|
674
|
+
if (latest.outcome === 'interrupted' && !latest.error && !latest.session?.error && !lastAssistantError(latest.messages) && latest.session?.id && typeof client.rpc === 'function' && !signal.controller.signal.aborted) {
|
|
675
|
+
// An interrupt may carry no error in the host projection. Read the
|
|
676
|
+
// authoritative persistent budget without restarting the task.
|
|
677
|
+
try {
|
|
678
|
+
const definitions = native.rpcDefinitions ?? await nativeRpcDefinitions();
|
|
679
|
+
const budget = await client.rpc(definitions.Carthagent).budgetStatus({ sessionID: latest.session.id }, { location: { directory: cwd }, signal: AbortSignal.any([signal.controller.signal, AbortSignal.timeout(2000)]) });
|
|
680
|
+
if (budget?.stopped) latest = { ...latest, budgetReason: budget.stopped, error: latest.error ?? headlessFailureMessage({ budgetReason: budget.stopped }) };
|
|
681
|
+
} catch { /* Keep the original failure when budget status is unavailable. */ }
|
|
682
|
+
}
|
|
662
683
|
eventController.abort();
|
|
663
684
|
await eventReader;
|
|
664
685
|
if (eventFailure) throw new Error(`Native Carthagent event stream failed: ${eventFailure.message}`, { cause: eventFailure });
|
package/lib/opencode/cli.mjs
CHANGED
|
@@ -26,6 +26,7 @@ const BOOL_FLAGS = new Map([
|
|
|
26
26
|
['--protect-existing', ['protectExisting', true]], ['--jev', ['jev', true]], ['--no-jev', ['jev', false]],
|
|
27
27
|
['--print', ['print', true]], ['--demo', ['demo', true]], ['-c', ['continue', true]],
|
|
28
28
|
['--continue', ['continue', true]], ['-r', ['resume', true]], ['--resume', ['resume', true]],
|
|
29
|
+
['--reset-budget', ['resetBudget', true]],
|
|
29
30
|
]);
|
|
30
31
|
|
|
31
32
|
const aliases = new Map([
|
|
@@ -84,9 +85,9 @@ export function cliHelp(version = '') {
|
|
|
84
85
|
\x1b[32mhash\x1b[0m, \x1b[32mfingerprint\x1b[0m Calculate workspace SHA-256 fingerprint
|
|
85
86
|
\x1b[32mvalidate\x1b[0m <file.json> Validate acceptance contract
|
|
86
87
|
\x1b[32mlogin\x1b[0m, \x1b[32mauth\x1b[0m Connect an AI provider
|
|
87
|
-
\x1b[32maccount\x1b[0m Show
|
|
88
|
-
\x1b[32mbilling\x1b[0m [checkout|portal] Open
|
|
89
|
-
\x1b[32mlogout-cloud\x1b[0m [session] Revoke the current or named
|
|
88
|
+
\x1b[32maccount\x1b[0m Show Cloud credit, plan, grants and CLI sessions
|
|
89
|
+
\x1b[32mbilling\x1b[0m [checkout|portal|refresh] Open or refresh Cloud billing
|
|
90
|
+
\x1b[32mlogout-cloud\x1b[0m [session] Revoke the current or named Cloud session
|
|
90
91
|
\x1b[32marchive\x1b[0m [list|search|read|export] Read-only legacy session archive
|
|
91
92
|
\x1b[32mreview\x1b[0m Configure self-review or review a PR
|
|
92
93
|
\x1b[32mserve\x1b[0m, \x1b[32mweb\x1b[0m, \x1b[32mstart\x1b[0m Start the web dashboard
|
|
@@ -110,6 +111,8 @@ export function cliHelp(version = '') {
|
|
|
110
111
|
--max-tools <n> Session tool-call budget (0 disables)
|
|
111
112
|
--max-seconds <n> Session elapsed-time budget (0 disables)
|
|
112
113
|
--max-repairs <n> Automatic-repair budget (0 disables)
|
|
114
|
+
--reset-budget Explicitly reset a resumed session's execution budget
|
|
115
|
+
Budgets persist across continuation, including idle time.
|
|
113
116
|
--protect-existing Preserve files present at session start
|
|
114
117
|
--rewrite-cap <n> Whole-file writes per path (0 disables)
|
|
115
118
|
--tool-output-cap <n> Bound text output per tool result
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 1,
|
|
3
|
-
"releaseVersion": "1.1.0-beta.
|
|
3
|
+
"releaseVersion": "1.1.0-beta.3",
|
|
4
4
|
"compatibilityVersion": "2.0.32",
|
|
5
5
|
"runtimeVersion": "0.0.0-ctg.2.0.32",
|
|
6
6
|
"sourceRevision": "843a6c514e606b436a7fe713422ace0a93ad3d61",
|
|
@@ -9,28 +9,28 @@
|
|
|
9
9
|
"assets": {
|
|
10
10
|
"linux-x64": {
|
|
11
11
|
"name": "opencode-linux-x64.tar.gz",
|
|
12
|
-
"archiveSha256": "
|
|
13
|
-
"executableSha256": "
|
|
12
|
+
"archiveSha256": "ab95bd99fc2bf52f867927a817dde1d3882122b80f85ef85cf416733afa62483",
|
|
13
|
+
"executableSha256": "f9ad13d8aad76bf1d16334712f0060b886757f5cdabddacce42f9805d4482c1c"
|
|
14
14
|
},
|
|
15
15
|
"linux-arm64": {
|
|
16
16
|
"name": "opencode-linux-arm64.tar.gz",
|
|
17
|
-
"archiveSha256": "
|
|
18
|
-
"executableSha256": "
|
|
17
|
+
"archiveSha256": "3869dd9398693afffe9f438192fbb12061b66a211993018379fb6870d2012bd2",
|
|
18
|
+
"executableSha256": "0c302d55bba608d83c42f54b769ec3994421d3dcb16ea5d2a9455dcd91f884a1"
|
|
19
19
|
},
|
|
20
20
|
"darwin-x64": {
|
|
21
21
|
"name": "opencode-darwin-x64.tar.gz",
|
|
22
|
-
"archiveSha256": "
|
|
23
|
-
"executableSha256": "
|
|
22
|
+
"archiveSha256": "b20111d8fd2368ac9bde7f90b12835a465b8c92da3db1f3a35671860a325eb27",
|
|
23
|
+
"executableSha256": "23f7a845e75592efa11dff407e147287584eae7f63fca1a9c4ccbe9558656bc3"
|
|
24
24
|
},
|
|
25
25
|
"darwin-arm64": {
|
|
26
26
|
"name": "opencode-darwin-arm64.tar.gz",
|
|
27
|
-
"archiveSha256": "
|
|
28
|
-
"executableSha256": "
|
|
27
|
+
"archiveSha256": "eb65c635c8cf19f0830b56b2d8087387148edaf44cf821c881b72388c9c185fd",
|
|
28
|
+
"executableSha256": "d10a7b549748a131c578ed8cd041aeb652f1ae9c79c5f3912a731f2a69218736"
|
|
29
29
|
},
|
|
30
30
|
"windows-x64": {
|
|
31
31
|
"name": "opencode-windows-x64.tar.gz",
|
|
32
|
-
"archiveSha256": "
|
|
33
|
-
"executableSha256": "
|
|
32
|
+
"archiveSha256": "a0309761781f57b5ea9f329d41f2e977391949addc9baaca6b85025780249d3c",
|
|
33
|
+
"executableSha256": "91eac290633313c7fce4a426ef265f193e995b285746df6a2a6010f4727fe813"
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
}
|
package/package.json
CHANGED
package/tests/cli.test.mjs
CHANGED
|
@@ -102,11 +102,11 @@ test('check fails clearly when no delivery report exists', t => {
|
|
|
102
102
|
assert.match(result.stderr, /No delivery report/);
|
|
103
103
|
});
|
|
104
104
|
|
|
105
|
-
test('guidance routing is not a user-facing command and
|
|
105
|
+
test('guidance routing is not a user-facing command and Cloud account commands are documented', t => {
|
|
106
106
|
const result = run(fixture(t), '--help');
|
|
107
107
|
assert.equal(result.status, 0, result.stderr);
|
|
108
108
|
assert.doesNotMatch(result.stdout, /\broute\b|\bprofiles\b/);
|
|
109
|
-
assert.match(result.stdout, /account.*
|
|
109
|
+
assert.match(result.stdout, /account.*Cloud credit/);
|
|
110
110
|
assert.match(result.stdout, /billing.*checkout\|portal/);
|
|
111
111
|
assert.match(result.stdout, /logout-cloud/);
|
|
112
112
|
});
|
|
@@ -42,6 +42,40 @@ test('native CLI selects headless execution and preserves output-mode options',
|
|
|
42
42
|
assert.equal(invocation.prompt, 'Do work');
|
|
43
43
|
});
|
|
44
44
|
|
|
45
|
+
test('headless continuation explains persistent budget exhaustion and resets only on explicit request', async t => {
|
|
46
|
+
const { mkdtempSync, rmSync } = await import('node:fs');
|
|
47
|
+
const { tmpdir } = await import('node:os');
|
|
48
|
+
const cwd = mkdtempSync(join(tmpdir(), 'ctg-budget-continuation-'));
|
|
49
|
+
t.after(() => rmSync(cwd, { recursive: true, force: true }));
|
|
50
|
+
const { io, output } = ioCapture();
|
|
51
|
+
const calls = [];
|
|
52
|
+
let stopped = 'elapsed-time';
|
|
53
|
+
const runtime = await createOpenCodeCliRuntime({ cwd, io, stdout: { write() {} }, native: {
|
|
54
|
+
runtime: {}, service: { ensureService: async () => ({}) }, rpcDefinitions: { Carthagent: {} },
|
|
55
|
+
clientFactory: async () => ({
|
|
56
|
+
rpc: () => ({ budgetStatus: async () => ({ stopped }) }),
|
|
57
|
+
session: {
|
|
58
|
+
list: async () => ({ data: [{ id: 'same-session', location: { directory: cwd } }] }),
|
|
59
|
+
get: async () => ({ id: 'same-session', location: { directory: cwd } }),
|
|
60
|
+
command: async input => { calls.push(input); stopped = null; },
|
|
61
|
+
},
|
|
62
|
+
}),
|
|
63
|
+
headless: { runHeadless: async (_service, _cwd, _prompt, options) => {
|
|
64
|
+
assert.equal(options.sessionID, 'same-session');
|
|
65
|
+
calls.push('prompt');
|
|
66
|
+
return { session: { id: 'same-session' }, outcome: stopped ? 'interrupted' : 'succeeded', terminal: true,
|
|
67
|
+
messages: stopped ? [] : [{ type: 'assistant', finish: 'stop', content: [{ type: 'text', text: 'continued' }] }] };
|
|
68
|
+
} },
|
|
69
|
+
} });
|
|
70
|
+
assert.equal(parseOpenCodeArgs(['--continue', '--reset-budget', 'Continue']).resetBudget, true);
|
|
71
|
+
assert.equal(await runtime.run({ mode: 'headless', options: { continue: true }, prompt: 'Continue' }), 1);
|
|
72
|
+
assert.match(output.stderr.at(-1), /budget exhausted: elapsed-time.*--reset-budget/);
|
|
73
|
+
assert.deepEqual(calls, ['prompt'], 'no implicit budget reset or prompt retry');
|
|
74
|
+
assert.equal(await runtime.run({ mode: 'headless', options: { continue: true, resetBudget: true }, prompt: 'Continue' }), 0);
|
|
75
|
+
assert.deepEqual(calls.slice(1), [{ sessionID: 'same-session', name: 'delivery-budget-reset', text: '' }, 'prompt']);
|
|
76
|
+
await assert.rejects(runtime.run({ mode: 'headless', options: { resetBudget: true }, prompt: 'New task' }), /requires --session/);
|
|
77
|
+
});
|
|
78
|
+
|
|
45
79
|
test('headless CLI validates prompts and maps cancellation to conventional exit code', async () => {
|
|
46
80
|
const { io, output } = ioCapture();
|
|
47
81
|
assert.equal(await runOpenCodeCli({ argv: [], stdoutIsTTY: false, io, runtime: {} }), 1);
|
|
@@ -77,10 +111,10 @@ test('model, auth, cloud and domain entrypoints dispatch to injected native capa
|
|
|
77
111
|
]);
|
|
78
112
|
});
|
|
79
113
|
|
|
80
|
-
test('help preserves command aliases,
|
|
114
|
+
test('help preserves command aliases, Cloud operations and isolation semantics', async () => {
|
|
81
115
|
const { output, io } = ioCapture();
|
|
82
116
|
assert.equal(await runOpenCodeCli({ argv: ['--help'], io, version: '1.0.3' }), 0);
|
|
83
|
-
assert.match(output.stdout[0], /account.*
|
|
117
|
+
assert.match(output.stdout[0], /account.*Cloud credit/);
|
|
84
118
|
assert.match(output.stdout[0], /billing.*checkout\|portal/);
|
|
85
119
|
assert.match(output.stdout[0], /--isolate\s+Isolate extension\/skill discovery \(not a worktree\)/);
|
|
86
120
|
assert.equal(cliHelp('x').includes('--engine-cli, --agent-cli'), true);
|