carthagent 1.0.7 → 1.1.0-preview.13
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/README.md +140 -83
- package/bin/ctg.mjs +14 -732
- package/docs/glyph-review/host-chrome-glyph-compatibility-v4.md +83 -0
- package/docs/glyph-review/test-isolation-v1.md +26 -0
- package/docs/glyph-review/typecheck-base.log +1 -0
- package/docs/glyph-review/typecheck-v4.log +1 -0
- package/docs/glyph-review/unicode-inventory-v4.csv +402 -0
- package/docs/help-dispatch-review.md +69 -0
- package/docs/legacy-cli.md +150 -0
- package/docs/live-theme-plugin-api.md +18 -0
- package/docs/native-cli.md +102 -0
- package/docs/native-compatibility.md +16 -0
- package/docs/native-daily-quests.md +132 -0
- package/docs/native-glyph-pty.md +59 -0
- package/docs/native-manual-testing.md +336 -0
- package/docs/native-slash-acceptance-handoff.md +49 -0
- package/docs/native-terminal-paired-failure-2026-10-17.md +40 -0
- package/docs/native-terminal-performance.md +310 -0
- package/docs/native-terminal-semantics-evidence/README.md +25 -0
- package/docs/native-terminal-semantics-evidence/controlled-host-model-selftest.json +16 -0
- package/docs/native-terminal-semantics-evidence/current-acceptance-status.json +59 -0
- package/docs/native-terminal-semantics-evidence/final-binary-focused-85bb44a.json +154 -0
- package/docs/native-terminal-semantics-evidence/final-binary-full-incomplete-85bb44a.json +160 -0
- package/docs/native-terminal-semantics-evidence/final-binary-layout-85bb44a.json +149 -0
- package/docs/native-terminal-semantics-evidence/final-binary-layout-interactive-85bb44a.json +159 -0
- package/docs/native-terminal-semantics-evidence/final-binary-model-picker-escape-unchanged-85bb44a.json +44 -0
- package/docs/native-terminal-semantics-evidence/final-binary-model-picker-exact-identity-85bb44a.json +47 -0
- package/docs/native-terminal-semantics-evidence/final-binary-model-picker-unchanged-model-incomplete-85bb44a.json +58 -0
- package/docs/native-terminal-semantics-evidence/final-binary-model-selftest.json +16 -0
- package/docs/native-terminal-semantics-evidence/final-binary-picker-cancel-incomplete-85bb44a.json +62 -0
- package/docs/native-terminal-semantics-evidence/focused-clipboard-title-stale-binary.json +161 -0
- package/docs/native-terminal-semantics-evidence/layout-stale-binary-observation-not-accepted.json +153 -0
- package/docs/native-terminal-semantics-evidence/picker-latest-incomplete-attempt.json +62 -0
- package/docs/native-terminal-semantics-evidence/picker-previous-probe-pass.json +61 -0
- package/docs/native-terminal-semantics.md +89 -0
- package/docs/native-upgrade.md +177 -0
- package/docs/opencode-composed-source-review.md +89 -0
- package/docs/opencode-migration.md +225 -9
- package/docs/opencode-owned-runtime.md +241 -0
- package/docs/opencode-runtime.md +12 -0
- package/docs/opencode-theme-provider-proof/README.md +55 -0
- package/docs/opencode-theme-provider-proof/live-theme-api-base.patch +122 -0
- package/docs/opencode-theme-provider-proof/plugin-theme-provider.patch +107 -0
- package/docs/opencode-theme-provider-proof/session-ui-base.patch +1217 -0
- package/docs/owned-host-controls-proof.md +39 -0
- package/docs/owned-runtime-release-manifest.md +42 -0
- package/docs/owned-visual-clear-proof.md +40 -0
- package/docs/patches/opencode-v2.0.32-glyph-spinner-test-isolation-v1.patch +50 -0
- package/docs/patches/opencode-v2.0.32-glyphs.patch +3670 -0
- package/docs/patches/opencode-v2.0.32-help-dispatch.patch +156 -0
- package/docs/patches/opencode-v2.0.32-live-theme-api.patch +122 -0
- package/docs/patches/opencode-v2.0.32-session-ui-combined-v1.patch +5272 -0
- package/docs/patches/opencode-v2.0.32-session-ui-combined-v2.patch +6052 -0
- package/docs/patches/opencode-v2.0.32-session-ui-combined-v3.patch +8657 -0
- package/docs/patches/opencode-v2.0.32-session-ui-combined-v4.patch +9928 -0
- package/docs/patches/opencode-v2.0.32-session-ui-combined-v5.patch +13965 -0
- package/docs/patches/opencode-v2.0.32-session-ui.patch +5137 -0
- package/docs/release-notes-native-draft.md +73 -0
- package/docs/release-notes-preview.10.md +121 -0
- package/docs/release-notes-preview.11.md +76 -0
- package/docs/release-notes-preview.12.md +55 -0
- package/docs/release-notes-preview.13.md +68 -0
- package/docs/release-notes-preview.9.md +130 -0
- package/docs/source-preview.md +120 -0
- package/evaluate.mjs +28 -1
- package/lib/budget.mjs +1 -1
- package/lib/codegraph.mjs +1 -1
- package/lib/delivery-review.mjs +11 -1
- package/lib/delivery.mjs +3 -2
- package/lib/environment.mjs +8 -0
- package/lib/guidance.mjs +1 -1
- package/lib/opencode/cli-runtime.mjs +1171 -0
- package/lib/opencode/cli.mjs +252 -0
- package/lib/opencode/credentials.mjs +63 -8
- package/lib/opencode/demo.mjs +53 -0
- package/lib/opencode/evaluation-budget-plugin/index.ts +3 -0
- package/lib/opencode/evaluation.mjs +32 -12
- package/lib/opencode/headless.mjs +8 -4
- package/lib/opencode/provider-policy.mjs +23 -0
- package/lib/opencode/review.mjs +37 -16
- package/lib/opencode/runtime-artifact.mjs +335 -0
- package/lib/opencode/runtime-github-transport.mjs +121 -0
- package/lib/opencode/runtime-release.json +36 -0
- package/lib/opencode/runtime.mjs +299 -20
- package/lib/opencode/service.mjs +77 -35
- package/lib/opencode/session-options.mjs +6 -2
- package/lib/opencode/update.mjs +222 -0
- package/lib/opencode/workspace.mjs +1 -1
- package/lib/planning-advice.mjs +2 -2
- package/lib/providers/experiential.mjs +2 -2
- package/lib/tui/framing.mjs +1 -80
- package/lib/update.mjs +10 -1
- package/package.json +33 -7
- package/plugins/carthagent/README.md +23 -0
- package/plugins/carthagent/src/bootstrap.ts +1 -1
- package/plugins/carthagent/src/daily-evidence.mjs +57 -0
- package/plugins/carthagent/src/daily-quests-rpc.ts +50 -0
- package/plugins/carthagent/src/daily-source-rpc.ts +52 -0
- package/plugins/carthagent/src/index.ts +71 -24
- package/plugins/carthagent/src/rpc.ts +51 -6
- package/plugins/carthagent/src/server/codegraph.ts +12 -5
- package/plugins/carthagent/src/server/daily-quests-rpc.ts +277 -0
- package/plugins/carthagent/src/server/daily-source-rpc.ts +211 -0
- package/plugins/carthagent/src/server/delivery.ts +227 -112
- package/plugins/carthagent/src/server/framing.mjs +80 -0
- package/plugins/carthagent/src/server/policy.ts +1 -1
- package/plugins/carthagent/src/server/providers.ts +59 -31
- package/plugins/carthagent/src/server/session-controls.ts +3 -1
- package/plugins/carthagent/src/server/ship-agents.ts +77 -0
- package/plugins/carthagent/src/server/ship-context.mjs +60 -0
- package/plugins/carthagent/src/server/ship-guidance.ts +41 -0
- package/plugins/carthagent/src/server/ship-rpc.ts +69 -0
- package/plugins/carthagent/src/server/voyages-rpc.ts +223 -0
- package/plugins/carthagent/src/testing.ts +5 -0
- package/plugins/carthagent/src/tui.tsx +496 -0
- package/plugins/carthagent/src/ui/client.ts +125 -0
- package/plugins/carthagent/src/ui/commands.ts +70 -0
- package/plugins/carthagent/src/ui/daily-editor-focus.mjs +52 -0
- package/plugins/carthagent/src/ui/daily-editor.mjs +4 -0
- package/plugins/carthagent/src/ui/daily-quests-events.mjs +18 -0
- package/plugins/carthagent/src/ui/daily-quests-state.mjs +218 -0
- package/plugins/carthagent/src/ui/daily-quests.tsx +235 -0
- package/plugins/carthagent/src/ui/host-state.ts +42 -0
- package/plugins/carthagent/src/ui/model-command.ts +32 -0
- package/plugins/carthagent/src/ui/native-navigation.ts +23 -0
- package/plugins/carthagent/src/ui/native-review.d.mts +33 -0
- package/plugins/carthagent/src/ui/native-review.mjs +159 -0
- package/plugins/carthagent/src/ui/resume-session.mjs +34 -0
- package/plugins/carthagent/src/ui/select-mouse.ts +17 -0
- package/plugins/carthagent/src/ui/session-preferences.d.mts +7 -0
- package/plugins/carthagent/src/ui/session-preferences.mjs +14 -0
- package/plugins/carthagent/src/ui/theme-provision.d.mts +3 -0
- package/plugins/carthagent/src/ui/theme-provision.mjs +134 -0
- package/plugins/carthagent/src/ui/voyages-navigation-state.mjs +56 -0
- package/plugins/carthagent/src/ui/voyages-navigation.tsx +131 -0
- package/plugins/carthagent/src/ui/voyages-state.mjs +142 -0
- package/plugins/carthagent/src/ui/voyages.tsx +129 -0
- package/plugins/carthagent/src/ui/workspace-live.ts +61 -0
- package/plugins/carthagent/src/ui/workspace.tsx +379 -0
- package/plugins/carthagent/src/voyages-rpc.ts +60 -0
- package/plugins/carthagent/src/workspace-rpc.ts +36 -0
- package/plugins/carthagent/tests/commands.test.ts +107 -0
- package/plugins/carthagent/tests/session-preferences.test.mjs +41 -0
- package/plugins/carthagent/tests/voyages-navigation.test.tsx +269 -0
- package/plugins/carthagent/themes/carthage.json +67 -0
- package/plugins/carthagent/tui.ts +26 -1
- package/script/ctg/build-owned-runtime.sh +166 -0
- package/tests/budget-engine.test.mjs +4 -3
- package/tests/cli.test.mjs +52 -96
- package/tests/daily-evidence.test.mjs +62 -0
- package/tests/delivery-review.test.mjs +11 -0
- package/tests/delivery.test.mjs +22 -1
- package/tests/fixtures/native-discovery-plugin/server.ts +15 -0
- package/tests/generate-owned-runtime-release.test.mjs +354 -0
- package/tests/installed-runtime-contract.test.mjs +43 -0
- package/tests/native-cli-host-smoke.mjs +457 -0
- package/tests/native-daily-editor-focus.test.mjs +213 -0
- package/tests/native-daily-editor.test.mjs +43 -0
- package/tests/native-daily-quests-layout.test.mjs +53 -0
- package/tests/native-daily-quests-rpc.test.mjs +456 -0
- package/tests/native-daily-quests-ui.test.mjs +778 -0
- package/tests/native-daily-source.test.mjs +451 -0
- package/tests/native-demo-ui.test.mjs +48 -0
- package/tests/native-evaluation-admission.test.mjs +24 -0
- package/tests/native-fixture-browser-cleanup.test.mjs +65 -0
- package/tests/native-fixture-runtime.test.mjs +111 -0
- package/tests/native-fixture-stop-diagnostics.test.mjs +67 -0
- package/tests/native-glyph-pty.test.mjs +107 -0
- package/tests/native-manual-projects.test.mjs +170 -0
- package/tests/native-model-command.test.mjs +67 -0
- package/tests/native-owned-runtime-env.test.mjs +140 -0
- package/tests/native-paired-guards.test.mjs +464 -0
- package/tests/native-provider-policy.test.mjs +31 -0
- package/tests/native-pty-invocation.test.mjs +114 -0
- package/tests/native-pty-runtime.test.mjs +45 -0
- package/tests/native-resume-session.test.mjs +46 -0
- package/tests/native-review-consumer.test.mjs +286 -0
- package/tests/native-runtime-identity.test.mjs +214 -0
- package/tests/native-select-mouse.test.mjs +23 -0
- package/tests/native-ship-parity.test.mjs +526 -0
- package/tests/native-ship-plan.test.mjs +85 -0
- package/tests/native-ship-rpc.test.mjs +71 -0
- package/tests/native-ui-commands.test.mjs +6 -0
- package/tests/native-voyages-layout.test.mjs +28 -0
- package/tests/native-voyages-navigation.test.mjs +169 -0
- package/tests/native-voyages-rpc.test.mjs +395 -0
- package/tests/native-voyages-ui.test.mjs +277 -0
- package/tests/opencode-atomic-write.test.mjs +55 -0
- package/tests/opencode-cli.test.mjs +1312 -0
- package/tests/opencode-codegraph-plugin.test.mjs +47 -0
- package/tests/opencode-delivery.test.mjs +16 -0
- package/tests/opencode-providers.test.mjs +245 -18
- package/tests/opencode-runtime.test.mjs +546 -8
- package/tests/opencode-session-controls.test.mjs +15 -0
- package/tests/opencode-ship-rpc.test.mjs +71 -0
- package/tests/opencode-update.test.mjs +475 -0
- package/tests/opencode-workspace.test.mjs +51 -2
- package/tests/owned-runtime-publish-workflow.test.mjs +477 -0
- package/tests/release-workflow.test.mjs +46 -0
- package/tests/runtime-artifact.test.mjs +269 -0
- package/tests/runtime-development-trust.test.mjs +230 -0
- package/tests/runtime-github-transport.test.mjs +132 -0
- package/tests/runtime-token-env.test.mjs +23 -0
- package/tests/server.test.mjs +3 -1
- package/tests/ship-telemetry.test.mjs +1 -1
- package/tests/source-preview.test.mjs +253 -0
- package/tests/test_native_terminal_probe.py +76 -0
- package/tests/test_native_terminal_semantics.py +232 -0
- package/tests/update.test.mjs +42 -5
- package/tests/verify-draft-runtime-gate.test.mjs +280 -0
- package/tests/verify-owned-runtime-release.test.mjs +371 -0
- package/tests/workspace-live.test.mjs +127 -0
- package/tools/benchmark/diagnose-request.mjs +59 -36
- package/tools/benchmark/eval-common.mjs +3 -3
- package/tools/benchmark/evaluators.test.mjs +4 -4
- package/tools/benchmark/guard.test.mjs +35 -2
- package/tools/benchmark/guard.ts +20 -7
- package/tools/benchmark/planning-startup.test.mjs +30 -34
- package/tools/benchmark/runner.mjs +12 -7
- package/tools/benchmark/runner.test.mjs +23 -7
- package/tools/benchmark/upstream-error.test.mjs +16 -2
- package/tools/generate-owned-runtime-release.mjs +217 -0
- package/tools/installed-runtime-contract.mjs +16 -0
- package/tools/manual-runtime-manifest.mjs +39 -0
- package/tools/native-fixture-browser-cleanup.mjs +59 -0
- package/tools/native-fixture-stop-diagnostics.mjs +43 -0
- package/tools/native-fixture.mjs +199 -40
- package/tools/native-glyph-helpers.py +127 -0
- package/tools/native-glyph-pty.py +144 -0
- package/tools/native-manual-projects.mjs +259 -0
- package/tools/native-manual.mjs +338 -0
- package/tools/native-model-picker-acceptance.py +306 -0
- package/tools/native-owned-runtime-env.sh +47 -0
- package/tools/native-paired-guards.mjs +282 -0
- package/tools/native-paired-manifest.mjs +72 -0
- package/tools/native-paired-orchestration.sh +51 -0
- package/tools/native-plugin-readiness.mjs +33 -0
- package/tools/native-plugin-readiness.test.mjs +53 -0
- package/tools/native-pty-invocation.mjs +26 -0
- package/tools/native-pty-runtime.mjs +83 -0
- package/tools/native-pty-smoke.py +147 -24
- package/tools/native-public-repo-shell-result.mjs +13 -0
- package/tools/native-public-repo-shell-result.test.mjs +28 -0
- package/tools/native-runtime-identity.mjs +201 -0
- package/tools/native-slash-acceptance-env.py +38 -0
- package/tools/native-terminal-semantics.py +876 -0
- package/tools/owned-shell-observer/index.ts +16 -0
- package/tools/owned-shell-observer/package.json +1 -0
- package/tools/owned-visual-clear-env.py +48 -0
- package/tools/owned-visual-clear-model.py +21 -0
- package/tools/pinned-owned-runtime-inputs.mjs +46 -0
- package/tools/prepare-native-manual.mjs +122 -0
- package/tools/run-native-terminal-paired.sh +403 -0
- package/tools/runtime-token-env.mjs +8 -0
- package/tools/seed-native-switch-fixture.mjs +79 -0
- package/tools/test-native-controls.mjs +3 -1
- package/tools/test-native-daily-quests.mjs +65 -0
- package/tools/test-native-default-cli.mjs +106 -0
- package/tools/test-native-delivery-host.mjs +102 -20
- package/tools/test-native-evaluation.mjs +7 -3
- package/tools/test-native-glyph-pty.mjs +94 -0
- package/tools/test-native-host.mjs +87 -63
- package/tools/test-native-outcomes.mjs +3 -1
- package/tools/test-native-package.mjs +206 -38
- package/tools/test-native-performance.mjs +26 -0
- package/tools/test-native-product.mjs +90 -0
- package/tools/test-native-pty.mjs +34 -9
- package/tools/test-native-public-repo.mjs +224 -0
- package/tools/test-native-refresh-reentry.mjs +106 -0
- package/tools/test-native-report.mjs +27 -20
- package/tools/test-native-review.mjs +75 -0
- package/tools/test-native-runtime.mjs +120 -0
- package/tools/test-native-ship-models.mjs +176 -0
- package/tools/test-native-ship-walkthrough.mjs +96 -0
- package/tools/test-native-slash-commands.mjs +125 -0
- package/tools/test-native-slash-commands.py +242 -0
- package/tools/test-native-swe-host.mjs +224 -55
- package/tools/test-native-tasks.mjs +77 -0
- package/tools/test-native-terminal-performance.py +887 -0
- package/tools/test-native-terminal-readiness.sh +85 -0
- package/tools/test-native-terminal-semantics-installed.mjs +38 -0
- package/tools/test-native-visual-clear-installed.mjs +48 -0
- package/tools/test-owned-host-controls.mjs +238 -0
- package/tools/test-owned-opencode-runtime.mjs +35 -0
- package/tools/test-owned-visual-clear-env.py +120 -0
- package/tools/test-owned-visual-clear.py +373 -0
- package/tools/verify-draft-runtime-gate.mjs +138 -0
- package/tools/verify-owned-runtime-release.mjs +114 -0
- package/tools/verify-release-version.mjs +21 -1
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { appendFileSync } from 'node:fs';
|
|
2
|
+
import { Plugin } from '@opencode/plugin';
|
|
3
|
+
|
|
4
|
+
export default Plugin.define({
|
|
5
|
+
id: 'ctg.owned-shell-observer',
|
|
6
|
+
async setup(ctx) {
|
|
7
|
+
const file = process.env.CTG_OWNED_SHELL_CAPTURE;
|
|
8
|
+
if (!file) throw new Error('CTG_OWNED_SHELL_CAPTURE is required by the offline shell provenance probe');
|
|
9
|
+
const registration = await ctx.shell.hook('create.before', event => {
|
|
10
|
+
if (event.command.includes('owned-identical-shell') || event.command.includes('process.stdout.write')) {
|
|
11
|
+
appendFileSync(file, `${JSON.stringify({ command: event.command, sessionID: event.sessionID ?? null, origin: event.origin ?? null, callID: event.callID ?? null })}\n`);
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
return () => registration.dispose();
|
|
15
|
+
},
|
|
16
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"module","main":"./index.ts","exports":"./index.ts"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""Platform-neutral child-environment allowlist for the owned visual-clear probe."""
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
SAFE_PARENT_ENV = ('PATH', 'LANG', 'LC_ALL')
|
|
5
|
+
SAFE_WINDOWS_ENV = ('SystemRoot', 'WINDIR', 'COMSPEC', 'PATHEXT')
|
|
6
|
+
TUI_ENV_KEYS = frozenset((*SAFE_PARENT_ENV, *SAFE_WINDOWS_ENV, 'HOME', 'USERPROFILE',
|
|
7
|
+
'APPDATA', 'LOCALAPPDATA', 'XDG_CONFIG_HOME', 'XDG_DATA_HOME', 'XDG_STATE_HOME',
|
|
8
|
+
'XDG_CACHE_HOME', 'OPENCODE_TEST_HOME', 'OPENCODE_DB', 'OPENCODE_CONFIG',
|
|
9
|
+
'OPENCODE_CONFIG_DIR', 'OPENCODE_CONFIG_PROJECT_DISABLE',
|
|
10
|
+
'OPENCODE_DISABLE_MODELS_FETCH', 'OPENCODE_DISABLE_CLAUDE_CODE',
|
|
11
|
+
'OPENCODE_DISABLE_CLAUDE_CODE_SKILLS', 'OPENCODE_DISABLE_AUTOUPDATE',
|
|
12
|
+
'CTG_OPENCODE_HOME', 'TERM', 'COLORTERM', 'OPENCODE_PASSWORD',
|
|
13
|
+
'OPENCODE_SERVER_PASSWORD', 'OPENCODE_SERVER_USERNAME'))
|
|
14
|
+
FIXTURE_ENV_KEYS = TUI_ENV_KEYS | frozenset((
|
|
15
|
+
'CTG_OWNED_OPENCODE_RUNTIME', 'CTG_TEST_RUNTIME_RELEASE_MANIFEST'))
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def filtered_parent_environment(source, home):
|
|
19
|
+
"""Build a child baseline from a short allowlist, never ambient credentials."""
|
|
20
|
+
result = {key: source[key] for key in (*SAFE_PARENT_ENV, *SAFE_WINDOWS_ENV)
|
|
21
|
+
if key in source}
|
|
22
|
+
result['PATH'] = result.get('PATH', '/usr/bin:/bin')
|
|
23
|
+
result['LANG'] = result.get('LANG', 'C.UTF-8')
|
|
24
|
+
result['HOME'] = str(home)
|
|
25
|
+
result['USERPROFILE'] = str(home)
|
|
26
|
+
return result
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def fixture_environment(source, home, owned_root, release_manifest):
|
|
30
|
+
"""Environment for fixture/preflight Node children; selectors are paths, not secrets."""
|
|
31
|
+
result = filtered_parent_environment(source, home)
|
|
32
|
+
result.update({
|
|
33
|
+
'CTG_OWNED_OPENCODE_RUNTIME': str(owned_root),
|
|
34
|
+
'CTG_TEST_RUNTIME_RELEASE_MANIFEST': str(release_manifest),
|
|
35
|
+
'XDG_CONFIG_HOME': str(Path(home) / 'config'),
|
|
36
|
+
'XDG_DATA_HOME': str(Path(home) / 'data'),
|
|
37
|
+
'XDG_STATE_HOME': str(Path(home) / 'state'),
|
|
38
|
+
'XDG_CACHE_HOME': str(Path(home) / 'cache'),
|
|
39
|
+
})
|
|
40
|
+
return result
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def assert_clean_child_environment(env, allowed=TUI_ENV_KEYS):
|
|
44
|
+
if any(key.casefold() == 'ctg_runtime_github_token' for key in env):
|
|
45
|
+
raise AssertionError('child environment contains reserved runtime download credential')
|
|
46
|
+
unexpected = sorted(set(env) - allowed)
|
|
47
|
+
if unexpected:
|
|
48
|
+
raise AssertionError(f'child environment contains non-allowlisted names: {unexpected}')
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""Pure canonical projections used by the owned visual-clear proof."""
|
|
2
|
+
import json
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def canonical_conversation(messages):
|
|
6
|
+
"""Normalize API message order without dropping IDs, content, or metadata."""
|
|
7
|
+
return sorted(messages, key=lambda message: json.dumps(
|
|
8
|
+
message, sort_keys=True, separators=(',', ':'), ensure_ascii=False))
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def latest_message(messages):
|
|
12
|
+
"""Return the latest message from the native message.list() API response.
|
|
13
|
+
|
|
14
|
+
The API returns the list newest-first (including non-conversational events
|
|
15
|
+
such as the trailing idle message). Keep cutoff selection separate from
|
|
16
|
+
canonical conversation ordering, which intentionally sorts for equality.
|
|
17
|
+
"""
|
|
18
|
+
data = messages.get('data', [])
|
|
19
|
+
if not data:
|
|
20
|
+
raise ValueError('message.list() returned no messages')
|
|
21
|
+
return data[0]
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { readFile, realpath } from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
|
|
5
|
+
/** Read the pinned recipe constants and the exact patch selected by that recipe. */
|
|
6
|
+
export async function readPinnedOwnedRuntimeInputs(repoRoot) {
|
|
7
|
+
const canonicalRoot = await realpath(path.resolve(repoRoot));
|
|
8
|
+
const recipe = await readFile(path.join(canonicalRoot, 'script/ctg/build-owned-runtime.sh'), 'utf8');
|
|
9
|
+
const constant = (name) => {
|
|
10
|
+
const matches = [...recipe.matchAll(new RegExp(`^${name}="([^"\\r\\n]+)"$`, 'gm'))];
|
|
11
|
+
if (matches.length !== 1) throw new Error(`Build recipe must contain exactly one literal ${name}`);
|
|
12
|
+
return matches[0][1];
|
|
13
|
+
};
|
|
14
|
+
const patchAssignments = [...recipe.matchAll(/^\s*patch_file\s*=.*$/gm)];
|
|
15
|
+
if (patchAssignments.length !== 1) throw new Error('Build recipe must contain exactly one literal ROOT-relative patch_file');
|
|
16
|
+
const patchFileMatch = patchAssignments[0][0].match(/^\s*patch_file="\$ROOT\/([^"\r\n]+)"\s*$/);
|
|
17
|
+
if (!patchFileMatch) throw new Error('Build recipe patch_file must be a literal ROOT-relative path');
|
|
18
|
+
const patchFile = patchFileMatch[1];
|
|
19
|
+
if (!patchFile.startsWith('docs/patches/')
|
|
20
|
+
|| patchFile.includes('\\')
|
|
21
|
+
|| path.posix.isAbsolute(patchFile)
|
|
22
|
+
|| path.posix.normalize(patchFile) !== patchFile
|
|
23
|
+
|| patchFile.split('/').some((part) => part === '.' || part === '..' || part === '')) {
|
|
24
|
+
throw new Error('Build recipe patch_file must be a canonical docs/patches-relative path');
|
|
25
|
+
}
|
|
26
|
+
const patchPath = path.resolve(canonicalRoot, ...patchFile.split('/'));
|
|
27
|
+
const relativePatchPath = path.relative(canonicalRoot, patchPath);
|
|
28
|
+
if (relativePatchPath.startsWith(`..${path.sep}`) || relativePatchPath === '..' || path.isAbsolute(relativePatchPath)) {
|
|
29
|
+
throw new Error('Build recipe patch_file escapes the repository root');
|
|
30
|
+
}
|
|
31
|
+
const canonicalPatchPath = await realpath(patchPath);
|
|
32
|
+
const actualRelativePatchPath = path.relative(canonicalRoot, canonicalPatchPath);
|
|
33
|
+
if (actualRelativePatchPath.startsWith(`..${path.sep}`) || actualRelativePatchPath === '..' || path.isAbsolute(actualRelativePatchPath)) {
|
|
34
|
+
throw new Error('Build recipe patch_file resolves outside the repository root');
|
|
35
|
+
}
|
|
36
|
+
const patch = await readFile(canonicalPatchPath);
|
|
37
|
+
const expectedPatchSha256 = constant('EXPECTED_PATCH').toLowerCase();
|
|
38
|
+
if (!/^[a-f0-9]{64}$/.test(expectedPatchSha256)) throw new Error('Build recipe EXPECTED_PATCH is not a SHA-256 digest');
|
|
39
|
+
const patchSha256 = createHash('sha256').update(patch).digest('hex');
|
|
40
|
+
if (patchSha256 !== expectedPatchSha256) throw new Error('Pinned patch bytes do not match the build recipe');
|
|
41
|
+
return {
|
|
42
|
+
sourceRevision: constant('EXPECTED_SOURCE'),
|
|
43
|
+
patchSha256,
|
|
44
|
+
lockSha256: constant('EXPECTED_LOCK').toLowerCase(),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { mkdir, writeFile, rm } from 'node:fs/promises';
|
|
3
|
+
import { createReadStream } from 'node:fs';
|
|
4
|
+
import { Readable } from 'node:stream';
|
|
5
|
+
import { spawnSync } from 'node:child_process';
|
|
6
|
+
import { resolve, join } from 'node:path';
|
|
7
|
+
import { pathToFileURL } from 'node:url';
|
|
8
|
+
import { acquireOwnedRuntime } from '../lib/opencode/runtime-artifact.mjs';
|
|
9
|
+
import { readManualRuntimeManifest } from './manual-runtime-manifest.mjs';
|
|
10
|
+
import { preflightNativeTestRuntimeArtifact } from './native-runtime-identity.mjs';
|
|
11
|
+
import { createManualProjects } from './native-manual-projects.mjs';
|
|
12
|
+
|
|
13
|
+
const root = resolve(import.meta.dirname, '..');
|
|
14
|
+
|
|
15
|
+
export async function prepareManualKit(
|
|
16
|
+
directory,
|
|
17
|
+
{ assetsDirectory, packages = 128, runtimeManifest, manifestSha256: expectedManifestSha256 } = {},
|
|
18
|
+
) {
|
|
19
|
+
if (!Number.isInteger(packages) || packages < 2 || packages > 1024)
|
|
20
|
+
throw Error('packages must be an integer from 2 to 1024');
|
|
21
|
+
const kit = resolve(directory);
|
|
22
|
+
await mkdir(kit, { mode: 0o700 });
|
|
23
|
+
try {
|
|
24
|
+
const { manifest, manifestPath, manifestSha256 } = await readManualRuntimeManifest({ runtimeManifest, manifestSha256: expectedManifestSha256 });
|
|
25
|
+
const platform = process.platform === 'win32' ? 'windows' : process.platform;
|
|
26
|
+
const target = `${platform}-${process.arch}`;
|
|
27
|
+
const asset = manifest.assets[target];
|
|
28
|
+
if (!asset) throw Error(`The candidate does not support ${target}`);
|
|
29
|
+
const assets = assetsDirectory && resolve(assetsDirectory);
|
|
30
|
+
const ownedRoot = await acquireOwnedRuntime({
|
|
31
|
+
releaseVersion: manifest.releaseVersion,
|
|
32
|
+
platform,
|
|
33
|
+
arch: process.arch,
|
|
34
|
+
expectedArchiveSha256: asset.archiveSha256,
|
|
35
|
+
expectedExecutableSha256: asset.executableSha256,
|
|
36
|
+
expectedSourceRevision: manifest.sourceRevision,
|
|
37
|
+
expectedPatchSha256: manifest.patchSha256,
|
|
38
|
+
expectedLockSha256: manifest.lockSha256,
|
|
39
|
+
expectedCompatibilityVersion: manifest.compatibilityVersion,
|
|
40
|
+
expectedRuntimeVersion: manifest.runtimeVersion,
|
|
41
|
+
expectedChannel: `ctg.${manifest.compatibilityVersion}`,
|
|
42
|
+
baseUrl: `https://github.com/B-icy/Carthagent/releases/download/v${manifest.releaseVersion}`,
|
|
43
|
+
cacheRoot: join(kit, 'runtimes'),
|
|
44
|
+
...(assets ? { fetchImpl: async (url) => {
|
|
45
|
+
if (
|
|
46
|
+
decodeURIComponent(new URL(url).pathname.split('/').at(-1)) !==
|
|
47
|
+
asset.name
|
|
48
|
+
)
|
|
49
|
+
throw Error('Unexpected runtime asset requested');
|
|
50
|
+
return new Response(
|
|
51
|
+
Readable.toWeb(createReadStream(join(assets, asset.name))),
|
|
52
|
+
);
|
|
53
|
+
} } : {}),
|
|
54
|
+
});
|
|
55
|
+
const { identity } = await preflightNativeTestRuntimeArtifact({
|
|
56
|
+
ownedRoot,
|
|
57
|
+
expectedManifestPath: manifestPath,
|
|
58
|
+
env: {},
|
|
59
|
+
});
|
|
60
|
+
const projects = await createManualProjects(join(kit, 'projects'), {
|
|
61
|
+
packages,
|
|
62
|
+
});
|
|
63
|
+
const source = spawnSync('git', ['rev-parse', 'HEAD'], {
|
|
64
|
+
cwd: root,
|
|
65
|
+
encoding: 'utf8',
|
|
66
|
+
});
|
|
67
|
+
const status = spawnSync('git', ['status', '--porcelain'], {
|
|
68
|
+
cwd: root,
|
|
69
|
+
encoding: 'utf8',
|
|
70
|
+
});
|
|
71
|
+
const record = {
|
|
72
|
+
version: 1,
|
|
73
|
+
createdAt: new Date().toISOString(),
|
|
74
|
+
releaseVersion: manifest.releaseVersion,
|
|
75
|
+
manifestSource: runtimeManifest ? 'explicit-pinned-candidate' : 'packaged-release',
|
|
76
|
+
sourceCommit:
|
|
77
|
+
source.status === 0 ? source.stdout.trim() : 'source-tarball',
|
|
78
|
+
sourceDirty: status.status === 0 ? Boolean(status.stdout.trim()) : null,
|
|
79
|
+
target,
|
|
80
|
+
runtime: `runtimes/${manifest.releaseVersion}/${target}`,
|
|
81
|
+
binarySha256: identity.binarySha256,
|
|
82
|
+
manifestSha256,
|
|
83
|
+
projects: projects.projects,
|
|
84
|
+
packages,
|
|
85
|
+
};
|
|
86
|
+
await writeFile(
|
|
87
|
+
join(kit, 'manual-kit.json'),
|
|
88
|
+
JSON.stringify(record, null, 2) + '\n',
|
|
89
|
+
);
|
|
90
|
+
await writeFile(join(kit, '.gitignore'), '*\n');
|
|
91
|
+
const manifestArgs = runtimeManifest ? ` --manifest ${JSON.stringify(manifestPath)} --manifest-sha256 ${manifestSha256}` : '';
|
|
92
|
+
console.log(
|
|
93
|
+
`Manual kit ready: ${kit}\nVerified ${target}: ${identity.binarySha256}\nOffline UI: node tools/native-manual.mjs ${JSON.stringify(kit)} tiny${manifestArgs}\nRead docs/native-manual-testing.md before --live. This is not a released/default install.`,
|
|
94
|
+
);
|
|
95
|
+
return record;
|
|
96
|
+
} catch (error) {
|
|
97
|
+
await rm(kit, { recursive: true, force: true });
|
|
98
|
+
throw error;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (
|
|
103
|
+
process.argv[1] &&
|
|
104
|
+
import.meta.url === pathToFileURL(resolve(process.argv[1])).href
|
|
105
|
+
) {
|
|
106
|
+
const [directory, ...args] = process.argv.slice(2);
|
|
107
|
+
if (!directory || args.includes('--help'))
|
|
108
|
+
throw Error(
|
|
109
|
+
'Usage: node tools/prepare-native-manual.mjs <new-directory> [--assets <directory>] [--packages 128] [--manifest <file> --manifest-sha256 <digest>]',
|
|
110
|
+
);
|
|
111
|
+
const options = {};
|
|
112
|
+
for (let i = 0; i < args.length; i += 2) {
|
|
113
|
+
const value = args[i + 1];
|
|
114
|
+
if (!value || !['--assets', '--packages', '--manifest', '--manifest-sha256'].includes(args[i]))
|
|
115
|
+
throw Error(`Invalid option ${args[i]}`);
|
|
116
|
+
if (args[i] === '--assets') options.assetsDirectory = value;
|
|
117
|
+
else if (args[i] === '--packages') options.packages = Number(value);
|
|
118
|
+
else if (args[i] === '--manifest') options.runtimeManifest = value;
|
|
119
|
+
else options.manifestSha256 = value;
|
|
120
|
+
}
|
|
121
|
+
await prepareManualKit(directory, options);
|
|
122
|
+
}
|