subconscious-cli 5.0.3 → 6.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -5
- package/bin/agents.js +211 -71
- package/bin/auth.js +80 -32
- package/bin/branding.js +3 -1
- package/bin/cli.js +28 -20
- package/bin/clipboard-image.js +22 -5
- package/bin/colors.js +1 -1
- package/bin/feedback.js +57 -18
- package/bin/model-capabilities.js +4 -1
- package/bin/models.js +22 -6
- package/bin/native/subc-tui-darwin-amd64 +0 -0
- package/bin/native/subc-tui-darwin-arm64 +0 -0
- package/bin/native/subc-tui-linux-amd64 +0 -0
- package/bin/native/subc-tui-linux-arm64 +0 -0
- package/bin/native/subc-tui-windows-amd64.exe +0 -0
- package/bin/native/subc-tui-windows-arm64.exe +0 -0
- package/bin/opencode-provider.js +27 -5
- package/bin/profiles.js +126 -44
- package/bin/registry.generated.json +1 -1
- package/bin/runbook/README.md +1 -1
- package/bin/runbook/opencode/opencode-plugin.d.ts +9 -0
- package/bin/runbook/opencode/subconscious-compaction.ts +26 -26
- package/bin/runbook/pi/subconscious-compaction.ts +69 -49
- package/bin/sessions.js +126 -41
- package/bin/tui.js +105 -34
- package/bin/update-check.js +49 -20
- package/bin/upgrade.js +49 -18
- package/bin/usage.js +26 -14
- package/bin/windows/agents.js +135 -32
- package/bin/windows/claude.js +43 -14
- package/bin/windows/common.js +56 -19
- package/bin/windows/install.js +120 -32
- package/bin/windows/launch.js +211 -49
- package/bin/windows/pi-compaction.ts +68 -13
- package/bin/windows/process.js +198 -56
- package/bin/windows/setup.js +231 -56
- package/package.json +21 -6
package/bin/windows/install.js
CHANGED
|
@@ -1,26 +1,38 @@
|
|
|
1
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
1
3
|
import fs from 'node:fs/promises';
|
|
2
4
|
import os from 'node:os';
|
|
3
5
|
import path from 'node:path';
|
|
4
|
-
import { createHash, randomUUID } from 'node:crypto';
|
|
5
|
-
import { readFileSync } from 'node:fs';
|
|
6
6
|
import { powershellCommand, runWindows } from './process.js';
|
|
7
7
|
|
|
8
|
-
const registry = JSON.parse(
|
|
8
|
+
const registry = JSON.parse(
|
|
9
|
+
readFileSync(new URL('../registry.generated.json', import.meta.url), 'utf8'),
|
|
10
|
+
);
|
|
9
11
|
const repository = 'subconscious-systems/subconscious-code';
|
|
10
12
|
|
|
11
13
|
function npmInstall(command) {
|
|
12
|
-
const match = /^npm (?:i|install) -g ([@a-zA-Z0-9/._-]+)$/.exec(
|
|
14
|
+
const match = /^npm (?:i|install) -g ([@a-zA-Z0-9/._-]+)$/.exec(
|
|
15
|
+
command || '',
|
|
16
|
+
);
|
|
13
17
|
return match ? { command: 'npm', args: ['install', '-g', match[1]] } : null;
|
|
14
18
|
}
|
|
15
19
|
|
|
16
20
|
export function windowsInstallSpec(id, env = process.env) {
|
|
17
|
-
const install = registry.agents.find(agent => agent.id === id)?.install;
|
|
21
|
+
const install = registry.agents.find((agent) => agent.id === id)?.install;
|
|
18
22
|
const command = typeof install === 'object' ? install.win32 : undefined;
|
|
19
|
-
if (id === 'subconscious-code')
|
|
23
|
+
if (id === 'subconscious-code')
|
|
24
|
+
return { nativeRelease: true, display: 'subc marathon install' };
|
|
20
25
|
if (!command) return null; // Never fall back to a Linux installer.
|
|
21
26
|
if (id === 'claude-code') {
|
|
22
|
-
const primary = powershellCommand(
|
|
23
|
-
|
|
27
|
+
const primary = powershellCommand(
|
|
28
|
+
"$ErrorActionPreference = 'Stop'; Invoke-RestMethod 'https://claude.ai/install.ps1' | Invoke-Expression; if (-not $?) { exit 1 }",
|
|
29
|
+
env,
|
|
30
|
+
);
|
|
31
|
+
return {
|
|
32
|
+
...primary,
|
|
33
|
+
display: command,
|
|
34
|
+
fallback: npmInstall(install.fallback),
|
|
35
|
+
};
|
|
24
36
|
}
|
|
25
37
|
const spec = npmInstall(command);
|
|
26
38
|
if (!spec) throw new Error(`No safe Windows installer is defined for ${id}`);
|
|
@@ -28,38 +40,97 @@ export function windowsInstallSpec(id, env = process.env) {
|
|
|
28
40
|
}
|
|
29
41
|
|
|
30
42
|
export function windowsReleaseAsset(release, arch = process.arch) {
|
|
31
|
-
const target = {
|
|
32
|
-
|
|
43
|
+
const target = {
|
|
44
|
+
x64: 'x86_64-pc-windows-msvc',
|
|
45
|
+
arm64: 'aarch64-pc-windows-msvc',
|
|
46
|
+
}[arch];
|
|
47
|
+
if (!target)
|
|
48
|
+
throw new Error(
|
|
49
|
+
`Subconscious Code does not support Windows architecture ${arch}`,
|
|
50
|
+
);
|
|
33
51
|
// Pinned older releases are installed under the new executable name too.
|
|
34
52
|
// Never resolve or overwrite Windows' own sc.exe.
|
|
35
|
-
const names = [
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
53
|
+
const names = [
|
|
54
|
+
`marathon-${target}.zip`,
|
|
55
|
+
`marathon-${target}.exe`,
|
|
56
|
+
`sc-${target}.zip`,
|
|
57
|
+
`sc-${target}.exe`,
|
|
58
|
+
];
|
|
59
|
+
const asset = names
|
|
60
|
+
.map((name) => release.assets?.find((asset) => asset.name === name))
|
|
61
|
+
.find(Boolean);
|
|
62
|
+
if (!asset)
|
|
63
|
+
throw new Error(
|
|
64
|
+
`Marathon ${release.tag_name || 'latest'} has no published native Windows ${arch} binary for subc marathon install.`,
|
|
65
|
+
);
|
|
66
|
+
const checksum = release.assets.find(
|
|
67
|
+
(candidate) => candidate.name === `${asset.name}.sha256`,
|
|
68
|
+
);
|
|
69
|
+
if (!checksum)
|
|
70
|
+
throw new Error(
|
|
71
|
+
`The Windows release is missing ${asset.name}.sha256; refusing an unverified installation.`,
|
|
72
|
+
);
|
|
40
73
|
return { asset, checksum };
|
|
41
74
|
}
|
|
42
75
|
|
|
43
|
-
export async function installWindowsSC(
|
|
76
|
+
export async function installWindowsSC(
|
|
77
|
+
env,
|
|
78
|
+
{
|
|
79
|
+
fetchImpl = fetch,
|
|
80
|
+
run = runWindows,
|
|
81
|
+
home = os.homedir(),
|
|
82
|
+
arch = process.arch,
|
|
83
|
+
log = console.log,
|
|
84
|
+
} = {},
|
|
85
|
+
) {
|
|
44
86
|
const version = env.SC_CODE_VERSION?.replace(/^v/, '');
|
|
45
|
-
const endpoint = version
|
|
46
|
-
|
|
47
|
-
|
|
87
|
+
const endpoint = version
|
|
88
|
+
? `tags/${encodeURIComponent(`v${version}`)}`
|
|
89
|
+
: 'latest';
|
|
90
|
+
const response = await fetchImpl(
|
|
91
|
+
`https://api.github.com/repos/${repository}/releases/${endpoint}`,
|
|
92
|
+
{
|
|
93
|
+
headers: { Accept: 'application/vnd.github+json' },
|
|
94
|
+
signal: AbortSignal.timeout(15000),
|
|
95
|
+
},
|
|
96
|
+
);
|
|
97
|
+
if (!response.ok)
|
|
98
|
+
throw new Error(
|
|
99
|
+
`Could not find a Subconscious Code release (HTTP ${response.status}).`,
|
|
100
|
+
);
|
|
48
101
|
const release = await response.json();
|
|
49
102
|
const { asset, checksum } = windowsReleaseAsset(release, arch);
|
|
50
|
-
const download = async asset => {
|
|
103
|
+
const download = async (asset) => {
|
|
51
104
|
const url = new URL(asset.browser_download_url);
|
|
52
|
-
if (
|
|
53
|
-
|
|
54
|
-
|
|
105
|
+
if (
|
|
106
|
+
url.origin !== 'https://github.com' ||
|
|
107
|
+
!url.pathname.startsWith(`/${repository}/releases/download/`)
|
|
108
|
+
)
|
|
109
|
+
throw new Error('Unexpected release download URL');
|
|
110
|
+
const response = await fetchImpl(url.href, {
|
|
111
|
+
signal: AbortSignal.timeout(120000),
|
|
112
|
+
});
|
|
113
|
+
if (!response.ok)
|
|
114
|
+
throw new Error(
|
|
115
|
+
`Download failed (HTTP ${response.status}): ${asset.name}`,
|
|
116
|
+
);
|
|
55
117
|
return Buffer.from(await response.arrayBuffer());
|
|
56
118
|
};
|
|
57
119
|
log(`Downloading Marathon ${release.tag_name} for Windows ${arch}...`);
|
|
58
|
-
const [binary, checksumText] = await Promise.all([
|
|
120
|
+
const [binary, checksumText] = await Promise.all([
|
|
121
|
+
download(asset),
|
|
122
|
+
download(checksum),
|
|
123
|
+
]);
|
|
59
124
|
const expected = checksumText.toString('utf8').trim().split(/\s+/)[0];
|
|
60
|
-
if (
|
|
125
|
+
if (
|
|
126
|
+
!/^[a-f0-9]{64}$/i.test(expected) ||
|
|
127
|
+
createHash('sha256').update(binary).digest('hex') !== expected.toLowerCase()
|
|
128
|
+
)
|
|
129
|
+
throw new Error('Subconscious Code checksum verification failed');
|
|
61
130
|
const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'subc-sc-install-'));
|
|
62
|
-
const installDir = path.resolve(
|
|
131
|
+
const installDir = path.resolve(
|
|
132
|
+
env.SC_INSTALL_DIR || path.join(home, '.local', 'bin'),
|
|
133
|
+
);
|
|
63
134
|
const destination = path.join(installDir, 'marathon.exe');
|
|
64
135
|
const staging = path.join(installDir, `marathon-${randomUUID()}.tmp`);
|
|
65
136
|
try {
|
|
@@ -68,11 +139,24 @@ export async function installWindowsSC(env, { fetchImpl = fetch, run = runWindow
|
|
|
68
139
|
const archive = path.join(dir, 'release.zip');
|
|
69
140
|
await fs.writeFile(archive, binary, { flag: 'wx' });
|
|
70
141
|
// Extract only the exact root executable. No archive-controlled paths.
|
|
71
|
-
const executable = asset.name.startsWith('marathon-')
|
|
72
|
-
|
|
73
|
-
|
|
142
|
+
const executable = asset.name.startsWith('marathon-')
|
|
143
|
+
? 'marathon.exe'
|
|
144
|
+
: 'sc.exe';
|
|
145
|
+
const spec = powershellCommand(
|
|
146
|
+
"$ErrorActionPreference = 'Stop'; Add-Type -AssemblyName System.IO.Compression.FileSystem; $z = [IO.Compression.ZipFile]::OpenRead($env:SUBC_ARCHIVE); try { $e = $z.GetEntry($env:SUBC_EXECUTABLE); if ($null -eq $e) { throw 'Archive has no expected root executable' }; [IO.Compression.ZipFileExtensions]::ExtractToFile($e, $env:SUBC_EXTRACTED, $false) } finally { $z.Dispose() }",
|
|
147
|
+
{
|
|
148
|
+
...env,
|
|
149
|
+
SUBC_ARCHIVE: archive,
|
|
150
|
+
SUBC_EXTRACTED: extracted,
|
|
151
|
+
SUBC_EXECUTABLE: executable,
|
|
152
|
+
},
|
|
153
|
+
);
|
|
154
|
+
if (await run(spec.command, spec.args, { env: spec.env }))
|
|
155
|
+
throw new Error('Could not extract the Windows release');
|
|
74
156
|
} else await fs.writeFile(extracted, binary, { flag: 'wx' });
|
|
75
|
-
const magic = (await fs.readFile(extracted))
|
|
157
|
+
const magic = (await fs.readFile(extracted))
|
|
158
|
+
.subarray(0, 2)
|
|
159
|
+
.toString('ascii');
|
|
76
160
|
if (magic !== 'MZ') throw new Error('Release is not a Windows executable');
|
|
77
161
|
await fs.mkdir(installDir, { recursive: true });
|
|
78
162
|
await fs.copyFile(extracted, staging);
|
|
@@ -90,8 +174,12 @@ export async function installWindowsAgent(id, env, run = runWindows) {
|
|
|
90
174
|
if (!spec) throw new Error('Install this agent separately, then rerun subc.');
|
|
91
175
|
if (spec.nativeRelease) return installWindowsSC(env, { run });
|
|
92
176
|
let code;
|
|
93
|
-
try {
|
|
94
|
-
|
|
177
|
+
try {
|
|
178
|
+
code = await run(spec.command, spec.args, { env: spec.env || env });
|
|
179
|
+
} catch (error) {
|
|
180
|
+
if (!spec.fallback) throw error;
|
|
181
|
+
code = 1;
|
|
182
|
+
}
|
|
95
183
|
if (code && spec.fallback) {
|
|
96
184
|
console.error('Native installer failed; trying the npm package.');
|
|
97
185
|
code = await run(spec.fallback.command, spec.fallback.args, { env });
|
package/bin/windows/launch.js
CHANGED
|
@@ -2,10 +2,18 @@
|
|
|
2
2
|
import fs from 'node:fs/promises';
|
|
3
3
|
import os from 'node:os';
|
|
4
4
|
import path from 'node:path';
|
|
5
|
-
import { claudeNativeLaunch } from './claude.js';
|
|
6
|
-
import { buildOpenCodeConfig } from '../opencode-provider.js';
|
|
7
5
|
import { modelSupportsVision } from '../model-capabilities.js';
|
|
8
|
-
import {
|
|
6
|
+
import { buildOpenCodeConfig } from '../opencode-provider.js';
|
|
7
|
+
import { claudeNativeLaunch } from './claude.js';
|
|
8
|
+
import {
|
|
9
|
+
defaults,
|
|
10
|
+
modelIds,
|
|
11
|
+
origin,
|
|
12
|
+
parseOptions,
|
|
13
|
+
positiveInteger,
|
|
14
|
+
value,
|
|
15
|
+
writeJson,
|
|
16
|
+
} from './common.js';
|
|
9
17
|
|
|
10
18
|
function connection(env, keyName) {
|
|
11
19
|
return {
|
|
@@ -15,82 +23,236 @@ function connection(env, keyName) {
|
|
|
15
23
|
};
|
|
16
24
|
}
|
|
17
25
|
|
|
18
|
-
export async function windowsLaunch(
|
|
19
|
-
|
|
20
|
-
|
|
26
|
+
export async function windowsLaunch(
|
|
27
|
+
id,
|
|
28
|
+
argv,
|
|
29
|
+
env,
|
|
30
|
+
{ tempRoot = os.tmpdir() } = {},
|
|
31
|
+
) {
|
|
32
|
+
if (id === 'claude-code')
|
|
33
|
+
return { command: 'claude', ...claudeNativeLaunch(argv, env) };
|
|
34
|
+
const keyName = {
|
|
35
|
+
codex: 'CODEX_API_KEY',
|
|
36
|
+
opencode: 'OPENCODE_API_KEY',
|
|
37
|
+
pi: 'PI_API_KEY',
|
|
38
|
+
'subconscious-code': 'SC_API_KEY',
|
|
39
|
+
'deepseek-harness': 'DEEPSEEK_HARNESS_API_KEY',
|
|
40
|
+
}[id];
|
|
21
41
|
const { model, base, key } = connection(env, keyName);
|
|
22
|
-
const childEnv = {
|
|
42
|
+
const childEnv = {
|
|
43
|
+
...env,
|
|
44
|
+
SUBCONSCIOUS_API_KEY: key,
|
|
45
|
+
SUBCONSCIOUS_GATEWAY_URL: base,
|
|
46
|
+
};
|
|
23
47
|
if (id === 'subconscious-code') {
|
|
24
|
-
return {
|
|
48
|
+
return {
|
|
49
|
+
command: 'marathon',
|
|
50
|
+
args: argv,
|
|
51
|
+
env: {
|
|
52
|
+
...childEnv,
|
|
53
|
+
SC_API_KEY: key,
|
|
54
|
+
SC_BASE_URL: `${base}/v1`,
|
|
55
|
+
SC_DLR_URL: base,
|
|
56
|
+
SC_DLR_ENABLED: 'true',
|
|
57
|
+
SC_MODEL: model,
|
|
58
|
+
},
|
|
59
|
+
};
|
|
25
60
|
}
|
|
26
|
-
if (id === 'pi')
|
|
61
|
+
if (id === 'pi')
|
|
62
|
+
return {
|
|
63
|
+
command: 'pi',
|
|
64
|
+
args: ['--provider', 'subconscious', '--model', model, ...argv],
|
|
65
|
+
env: childEnv,
|
|
66
|
+
};
|
|
27
67
|
if (id === 'opencode') {
|
|
28
|
-
const context = positiveInteger(
|
|
29
|
-
|
|
68
|
+
const context = positiveInteger(
|
|
69
|
+
value(env, 'OPENCODE_CONTEXT_LIMIT', '5000000'),
|
|
70
|
+
'OPENCODE_CONTEXT_LIMIT',
|
|
71
|
+
);
|
|
72
|
+
const output = positiveInteger(
|
|
73
|
+
value(env, 'OPENCODE_OUTPUT_LIMIT', '65536'),
|
|
74
|
+
'OPENCODE_OUTPUT_LIMIT',
|
|
75
|
+
);
|
|
30
76
|
childEnv.OPENCODE_CONFIG_CONTENT = JSON.stringify(
|
|
31
|
-
buildOpenCodeConfig({
|
|
77
|
+
buildOpenCodeConfig({
|
|
78
|
+
baseUrl: base,
|
|
79
|
+
model,
|
|
80
|
+
modelIds: modelIds(env),
|
|
81
|
+
context,
|
|
82
|
+
output,
|
|
83
|
+
}),
|
|
32
84
|
);
|
|
33
|
-
return {
|
|
85
|
+
return {
|
|
86
|
+
command: 'opencode',
|
|
87
|
+
args: parseOptions(argv, {}).rest,
|
|
88
|
+
env: childEnv,
|
|
89
|
+
};
|
|
34
90
|
}
|
|
35
91
|
if (id === 'codex') {
|
|
36
|
-
const { options: opts, rest } = parseOptions(argv, {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
92
|
+
const { options: opts, rest } = parseOptions(argv, {
|
|
93
|
+
'--context-window': 1,
|
|
94
|
+
'--max-context-window': 1,
|
|
95
|
+
'--auto-compact-token-limit': 1,
|
|
96
|
+
'--reasoning-effort': 1,
|
|
97
|
+
'--subagents': true,
|
|
98
|
+
'--external-tools': true,
|
|
99
|
+
});
|
|
100
|
+
const context = positiveInteger(
|
|
101
|
+
opts['--context-window'] || value(env, 'CODEX_CONTEXT_WINDOW', '5000000'),
|
|
102
|
+
'context-window',
|
|
103
|
+
);
|
|
104
|
+
const maxContext = positiveInteger(
|
|
105
|
+
opts['--max-context-window'] ||
|
|
106
|
+
value(env, 'CODEX_MAX_CONTEXT_WINDOW', String(context)),
|
|
107
|
+
'max-context-window',
|
|
108
|
+
);
|
|
109
|
+
const compact = positiveInteger(
|
|
110
|
+
opts['--auto-compact-token-limit'] ||
|
|
111
|
+
value(env, 'CODEX_AUTO_COMPACT_TOKEN_LIMIT', '4500000'),
|
|
112
|
+
'auto-compact-token-limit',
|
|
113
|
+
);
|
|
114
|
+
const reasoning =
|
|
115
|
+
opts['--reasoning-effort'] || value(env, 'CODEX_REASONING_EFFORT', 'max');
|
|
116
|
+
if (!['none', 'low', 'medium', 'high', 'max'].includes(reasoning))
|
|
117
|
+
throw new Error(
|
|
118
|
+
'reasoning-effort must be none, low, medium, high, or max',
|
|
119
|
+
);
|
|
120
|
+
const catalog = {
|
|
121
|
+
models: modelIds(env).map((id) => ({
|
|
122
|
+
slug: id,
|
|
123
|
+
display_name: id,
|
|
124
|
+
description: `Subconscious model ${id}`,
|
|
125
|
+
context_window: context,
|
|
126
|
+
max_context_window: maxContext,
|
|
127
|
+
auto_compact_token_limit: compact,
|
|
128
|
+
effective_context_window_percent: 95,
|
|
129
|
+
supported_reasoning_levels: [],
|
|
130
|
+
shell_type: 'shell_command',
|
|
131
|
+
visibility: 'list',
|
|
132
|
+
supported_in_api: true,
|
|
133
|
+
priority: 0,
|
|
134
|
+
service_tiers: [
|
|
135
|
+
{
|
|
136
|
+
id: 'priority',
|
|
137
|
+
name: 'Priority',
|
|
138
|
+
description:
|
|
139
|
+
'Route requests through the configured priority service tier',
|
|
140
|
+
},
|
|
141
|
+
],
|
|
142
|
+
availability_nux: null,
|
|
143
|
+
upgrade: null,
|
|
144
|
+
base_instructions: 'You are Codex, a coding agent.',
|
|
145
|
+
supports_reasoning_summaries: false,
|
|
146
|
+
support_verbosity: false,
|
|
147
|
+
default_verbosity: null,
|
|
148
|
+
apply_patch_tool_type: 'freeform',
|
|
149
|
+
truncation_policy: { mode: 'tokens', limit: 10000 },
|
|
150
|
+
supports_parallel_tool_calls: true,
|
|
151
|
+
experimental_supported_tools: [],
|
|
152
|
+
...(modelSupportsVision(id)
|
|
153
|
+
? { input_modalities: ['text', 'image'] }
|
|
154
|
+
: {}),
|
|
155
|
+
})),
|
|
156
|
+
};
|
|
54
157
|
// Validate all flags before creating a temporary directory.
|
|
55
|
-
const threads = positiveInteger(
|
|
158
|
+
const threads = positiveInteger(
|
|
159
|
+
value(env, 'MAX_CONCURRENT_SUBAGENTS', '4'),
|
|
160
|
+
'MAX_CONCURRENT_SUBAGENTS',
|
|
161
|
+
);
|
|
56
162
|
const dir = await fs.mkdtemp(path.join(tempRoot, 'subc-codex-'));
|
|
57
163
|
const cleanup = () => fs.rm(dir, { recursive: true, force: true });
|
|
58
164
|
try {
|
|
59
165
|
const file = path.join(dir, 'models.json');
|
|
60
166
|
await writeJson(file, catalog);
|
|
61
167
|
const config = {
|
|
62
|
-
model,
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
168
|
+
model,
|
|
169
|
+
model_provider: 'subconscious',
|
|
170
|
+
model_catalog_json: file,
|
|
171
|
+
model_reasoning_effort: reasoning,
|
|
172
|
+
web_search: 'disabled',
|
|
173
|
+
'model_providers.subconscious.name': 'Subconscious',
|
|
174
|
+
'model_providers.subconscious.base_url': `${base}/v1`,
|
|
175
|
+
'model_providers.subconscious.wire_api': 'responses',
|
|
176
|
+
'model_providers.subconscious.env_key': 'SUBCONSCIOUS_API_KEY',
|
|
177
|
+
'model_providers.subconscious.stream_idle_timeout_ms': 300000,
|
|
66
178
|
};
|
|
67
|
-
if (
|
|
68
|
-
|
|
179
|
+
if (
|
|
180
|
+
!opts['--external-tools'] &&
|
|
181
|
+
value(env, 'CODEX_EXTERNAL_TOOLS', 'false') !== 'true'
|
|
182
|
+
) {
|
|
183
|
+
Object.assign(config, {
|
|
184
|
+
'features.apps': false,
|
|
185
|
+
'features.plugins': false,
|
|
186
|
+
'apps._default.enabled': false,
|
|
187
|
+
});
|
|
69
188
|
}
|
|
70
|
-
if (opts['--subagents'])
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
189
|
+
if (opts['--subagents'])
|
|
190
|
+
Object.assign(config, {
|
|
191
|
+
'features.multi_agent': true,
|
|
192
|
+
'agents.max_threads': threads,
|
|
193
|
+
'agents.max_depth': 1,
|
|
194
|
+
'agents.interrupt_message': true,
|
|
195
|
+
});
|
|
196
|
+
const args = Object.entries(config).flatMap(([k, v]) => [
|
|
197
|
+
'-c',
|
|
198
|
+
`${k}=${JSON.stringify(v)}`,
|
|
199
|
+
]);
|
|
200
|
+
return {
|
|
201
|
+
command: opts['--subagents'] ? 'npx' : 'codex',
|
|
202
|
+
args: [
|
|
203
|
+
...(opts['--subagents'] ? ['-y', '@openai/codex@0.132.0'] : []),
|
|
204
|
+
...args,
|
|
205
|
+
...rest,
|
|
206
|
+
],
|
|
207
|
+
env: childEnv,
|
|
208
|
+
cleanup,
|
|
209
|
+
};
|
|
210
|
+
} catch (error) {
|
|
211
|
+
await cleanup();
|
|
212
|
+
throw error;
|
|
213
|
+
}
|
|
74
214
|
}
|
|
75
215
|
if (id === 'deepseek-harness') {
|
|
76
|
-
const context = positiveInteger(
|
|
77
|
-
|
|
216
|
+
const context = positiveInteger(
|
|
217
|
+
value(env, 'DEEPSEEK_HARNESS_CONTEXT_WINDOW', '5000000'),
|
|
218
|
+
'DEEPSEEK_HARNESS_CONTEXT_WINDOW',
|
|
219
|
+
);
|
|
220
|
+
const maxTokens = positiveInteger(
|
|
221
|
+
value(env, 'DEEPSEEK_HARNESS_MAX_TOKENS', '65536'),
|
|
222
|
+
'DEEPSEEK_HARNESS_MAX_TOKENS',
|
|
223
|
+
);
|
|
78
224
|
const models = modelIds(env);
|
|
79
225
|
const dir = await fs.mkdtemp(path.join(tempRoot, 'subc-dsh-'));
|
|
80
226
|
const cleanup = () => fs.rm(dir, { recursive: true, force: true });
|
|
81
227
|
try {
|
|
82
228
|
const file = path.join(dir, 'subconscious.cordis.yml');
|
|
83
|
-
const text = `- id: llm-pi-ai\n config:\n providers:\n subconscious:\n apiKeyEnv: SUBCONSCIOUS_API_KEY\n displayName: Subconscious Gateway\n api: openai-completions\n baseURL: !!js process.env.SUBCONSCIOUS_DSH_BASE_URL\n headers:\n x-subconscious-client: deepseek-harness\n compat:\n supportsDeveloperRole: false\n maxTokensField: max_tokens\n defaultContextWindow: ${context}\n defaultMaxTokens: ${maxTokens}\n models:\n${models.map(id => ` - id: '${id}'\n name: '${id}'\n${modelSupportsVision(id) ? ' input: [text, image]\n' : ''} contextWindow: ${context}\n maxTokens: ${maxTokens}`).join('\n')}\n- id: agent-default-model\n config:\n provider: subconscious\n model: '${model}'\n`;
|
|
229
|
+
const text = `- id: llm-pi-ai\n config:\n providers:\n subconscious:\n apiKeyEnv: SUBCONSCIOUS_API_KEY\n displayName: Subconscious Gateway\n api: openai-completions\n baseURL: !!js process.env.SUBCONSCIOUS_DSH_BASE_URL\n headers:\n x-subconscious-client: deepseek-harness\n compat:\n supportsDeveloperRole: false\n maxTokensField: max_tokens\n defaultContextWindow: ${context}\n defaultMaxTokens: ${maxTokens}\n models:\n${models.map((id) => ` - id: '${id}'\n name: '${id}'\n${modelSupportsVision(id) ? ' input: [text, image]\n' : ''} contextWindow: ${context}\n maxTokens: ${maxTokens}`).join('\n')}\n- id: agent-default-model\n config:\n provider: subconscious\n model: '${model}'\n`;
|
|
84
230
|
await fs.writeFile(file, text, { mode: 0o600, flag: 'wx' });
|
|
85
231
|
const mode = ['web', 'headless'].includes(argv[0]) ? argv[0] : 'web';
|
|
86
232
|
const rest = ['web', 'headless'].includes(argv[0]) ? argv.slice(1) : argv;
|
|
87
|
-
return {
|
|
88
|
-
|
|
233
|
+
return {
|
|
234
|
+
command: 'dsh',
|
|
235
|
+
args: [
|
|
236
|
+
...(mode === 'web' ? ['web'] : ['--profile', 'headless']),
|
|
237
|
+
'--patch',
|
|
238
|
+
file,
|
|
239
|
+
...rest,
|
|
240
|
+
],
|
|
241
|
+
env: { ...childEnv, SUBCONSCIOUS_DSH_BASE_URL: `${base}/v1` },
|
|
242
|
+
cleanup,
|
|
243
|
+
};
|
|
244
|
+
} catch (error) {
|
|
245
|
+
await cleanup();
|
|
246
|
+
throw error;
|
|
247
|
+
}
|
|
89
248
|
}
|
|
90
249
|
throw new Error(`No native Windows launcher is available for ${id}`);
|
|
91
250
|
}
|
|
92
251
|
|
|
93
252
|
export async function executeWindowsLaunch(spec, run) {
|
|
94
|
-
try {
|
|
95
|
-
|
|
253
|
+
try {
|
|
254
|
+
return await run(spec.command, spec.args, { env: spec.env });
|
|
255
|
+
} finally {
|
|
256
|
+
await spec.cleanup?.();
|
|
257
|
+
}
|
|
96
258
|
}
|
|
@@ -1,29 +1,84 @@
|
|
|
1
1
|
// Windows-only Pi extension. Loaded config is relative to this installed file.
|
|
2
|
-
|
|
2
|
+
|
|
3
3
|
import { randomUUID } from 'node:crypto';
|
|
4
|
-
import {
|
|
4
|
+
import { readFileSync } from 'node:fs';
|
|
5
5
|
import { dirname, join } from 'node:path';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
|
|
8
|
+
type PiCompactEvent = {
|
|
9
|
+
reason?: unknown;
|
|
10
|
+
preparation?: { tokensBefore?: unknown };
|
|
11
|
+
fromExtension?: unknown;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
type PiContext = {
|
|
15
|
+
sessionManager?: { getSessionId?: () => string | undefined };
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
type WindowsPiConfig = {
|
|
19
|
+
gatewayUrl?: string;
|
|
20
|
+
apiKey?: string;
|
|
21
|
+
};
|
|
6
22
|
|
|
7
|
-
export default function (pi: {
|
|
8
|
-
|
|
9
|
-
|
|
23
|
+
export default function (pi: {
|
|
24
|
+
on: (
|
|
25
|
+
event: string,
|
|
26
|
+
handler: (event: PiCompactEvent, ctx: PiContext) => Promise<void>,
|
|
27
|
+
) => void;
|
|
28
|
+
}) {
|
|
29
|
+
for (const [eventName, phase] of [
|
|
30
|
+
['session_before_compact', 'start'],
|
|
31
|
+
['session_compact', 'end'],
|
|
32
|
+
] as const) {
|
|
33
|
+
pi.on(eventName, async (event, ctx) => {
|
|
10
34
|
const session = ctx?.sessionManager?.getSessionId?.();
|
|
11
35
|
if (!session) return;
|
|
12
36
|
const controller = new AbortController();
|
|
13
37
|
const timer = setTimeout(() => controller.abort(), 1500);
|
|
14
38
|
try {
|
|
15
|
-
const config = JSON.parse(
|
|
39
|
+
const config = JSON.parse(
|
|
40
|
+
readFileSync(
|
|
41
|
+
join(
|
|
42
|
+
dirname(fileURLToPath(import.meta.url)),
|
|
43
|
+
'..',
|
|
44
|
+
'subconscious-windows.json',
|
|
45
|
+
),
|
|
46
|
+
'utf8',
|
|
47
|
+
),
|
|
48
|
+
) as WindowsPiConfig;
|
|
16
49
|
const url = process.env.SUBCONSCIOUS_GATEWAY_URL || config.gatewayUrl;
|
|
17
50
|
const key = process.env.SUBCONSCIOUS_API_KEY || config.apiKey;
|
|
18
51
|
if (!url || !key) return;
|
|
19
|
-
const response = await fetch(
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
52
|
+
const response = await fetch(
|
|
53
|
+
`${url.replace(/\/+$/, '')}/v1/agent-hooks`,
|
|
54
|
+
{
|
|
55
|
+
method: 'POST',
|
|
56
|
+
headers: {
|
|
57
|
+
'content-type': 'application/json',
|
|
58
|
+
authorization: `Bearer ${key}`,
|
|
59
|
+
'x-subconscious-client': 'pi',
|
|
60
|
+
},
|
|
61
|
+
body: JSON.stringify({
|
|
62
|
+
event: 'conversation_compaction',
|
|
63
|
+
conversation_id: session,
|
|
64
|
+
phase,
|
|
65
|
+
hook_event_name: eventName,
|
|
66
|
+
dedupe_key: `${session}:${phase}:${randomUUID()}`,
|
|
67
|
+
metadata: {
|
|
68
|
+
reason: event?.reason,
|
|
69
|
+
tokens_before: event?.preparation?.tokensBefore,
|
|
70
|
+
from_extension: event?.fromExtension,
|
|
71
|
+
},
|
|
72
|
+
}),
|
|
73
|
+
signal: controller.signal,
|
|
74
|
+
},
|
|
75
|
+
);
|
|
24
76
|
await response.body?.cancel();
|
|
25
|
-
} catch {
|
|
26
|
-
|
|
77
|
+
} catch {
|
|
78
|
+
/* observational: never cancel or replace compaction */
|
|
79
|
+
} finally {
|
|
80
|
+
clearTimeout(timer);
|
|
81
|
+
}
|
|
27
82
|
});
|
|
28
83
|
}
|
|
29
84
|
}
|