import assert from 'node:assert/strict' import fs from 'node:fs' import os from 'node:os' import path from 'node:path' const data = fs.mkdtempSync(path.join(os.tmpdir(), 'cowrite-default-agent-')) process.env.DATA_DIR = data const { defaultAgentHandle, ensureDefaultAgent, execFileWithClosedStdin, openCodeArgs, openCodeEnv, openCodeProcessSummary, parseAgentAnswer, prepareOpenCode, safeError } = await import('../server/default-agent.js') const agents = { 'alice-cowrite': { owner: 'someone-else' }, } const handle = defaultAgentHandle('Alice', agents) assert.match(handle, /^[a-z0-9][a-z0-9_.-]{1,38}$/) assert.notEqual(handle, 'alice-cowrite') assert.equal(defaultAgentHandle('Bob', {}), 'bob-cowrite') const managed = ensureDefaultAgent('Alice') const stored = JSON.parse(fs.readFileSync(path.join(data, 'agents.json'), 'utf8'))[managed] assert.equal(stored.owner, 'Alice') assert.equal(stored.managed, 'cowrite') assert.equal(stored.shared, false) assert.equal(stored.keyHash, undefined) const { pollMentions, snapshotMentions } = await import('../server/collab.js') assert.match(snapshotMentions('Alice').error || '', /no agent handles/, 'external owner-token polls cannot claim built-in work') assert.deepEqual(await pollMentions('Alice', 0, managed, { includeManaged: true }), { mentions: [] }) const prepared = prepareOpenCode('Alice') const { configPath } = prepared const config = JSON.parse(fs.readFileSync(configPath, 'utf8')) assert.equal(config.autoupdate, false) assert.equal(config.permission['*'], 'deny') assert.equal(config.permission.websearch, 'allow') assert.equal(config.permission.webfetch, 'deny') assert.equal(config.provider['cowrite-hf'].options.apiKey, '{env:HF_OAUTH_TOKEN}', 'config references the in-memory token by environment name') const isolatedEnv = openCodeEnv(prepared, 'test-oauth-token') assert.equal(isolatedEnv.OPENCODE_DISABLE_MODELS_FETCH, 'true') assert.equal(isolatedEnv.OPENCODE_FAST_BOOT, 'true') assert.equal(isolatedEnv.OPENCODE_EXPERIMENTAL_DISABLE_FILEWATCHER, 'true') assert.equal(isolatedEnv.OPENCODE_DISABLE_DEFAULT_PLUGINS, 'true') assert.equal(isolatedEnv.OPENCODE_DISABLE_PROJECT_CONFIG, 'true') assert.equal(isolatedEnv.HF_OAUTH_TOKEN, 'test-oauth-token') const commandArgs = openCodeArgs('/isolated/workspace', 'private prompt') assert.deepEqual(commandArgs.slice(0, 4), ['run', '--pure', '--auto', '--format']) assert.equal(commandArgs[commandArgs.indexOf('--title') + 1], 'Cowrite task') assert.equal(commandArgs.at(-1), 'private prompt') const stdinProbe = await execFileWithClosedStdin(process.execPath, [ '-e', 'process.stdin.resume(); process.stdin.on("end", () => process.stdout.write("closed"))', ], { timeout: 2_000 }) assert.equal(stdinProbe.stdout, 'closed', 'managed subprocess receives EOF after the command-line prompt') const secret = 'hf_oauth_secret-value_with-more' assert.equal(safeError(new Error(`provider rejected ${secret}`), [secret]).includes('secret-value'), false) assert.equal(safeError(new Error('provider rejected hf_oauth_tail-with_more')).includes('tail'), false) const processSummary = openCodeProcessSummary({ killed: true, signal: 'SIGTERM', stdout: [ JSON.stringify({ type: 'step_start' }), JSON.stringify({ type: 'text', part: { text: 'private document output' } }), JSON.stringify({ type: 'error', error: { data: { statusCode: 429, message: `retry ${secret}` } } }), ].join('\n'), stderr: 'message="creating instance"\nmessage="llm runtime selected" private stderr', }, [secret]) assert.match(processSummary, /timeout_or_kill/) assert.match(processSummary, /events=step_start:1,text:1,error:1/) assert.match(processSummary, /provider=429 retry \[redacted\]/) assert.match(processSummary, /stages=instance,llm_selected/) assert.doesNotMatch(processSummary, /private document output|private stderr|secret-value/) assert.deepEqual(parseAgentAnswer('```json\n{"reply":"Done","suggestions":[],"dismiss":false}\n```'), { reply: 'Done', suggestions: [], dismiss: false, }) assert.throws(() => parseAgentAnswer('not json'), /JSON object/) fs.rmSync(data, { recursive: true, force: true }) console.log('✓ default agent handle + structured answer parsing')