Attempts to normalized Agent output when using OpenSSH Server
This commit is contained in:
@@ -275,6 +275,11 @@ function createSshChildCtx(ctx, stream) {
|
||||
return Object.assign({}, ctx, {
|
||||
console: { log, info: log, warn: err, error: err, debug: log },
|
||||
writeScreen: (s) => w(String(s)),
|
||||
/**
|
||||
* Raw stream writes from utilities (e.g. `/bin/agent` SSE chunks) must newline-normalize like
|
||||
* `bareOpensshWritePtyConsoleLine` so streaming text renders over SSH PTYs.
|
||||
*/
|
||||
bareOsPtyStdoutCrlf: true,
|
||||
/** `/bin/edit`, baretop, etc. use replStdin/replStdout — not Pear CLI. */
|
||||
replStdin: stream,
|
||||
replStdout: stream,
|
||||
|
||||
@@ -125,6 +125,10 @@ export interface BareOsKernelContext {
|
||||
* Utilities may emit **one record per line** instead of multi-column text (e.g. **`ls`**).
|
||||
*/
|
||||
bareOsStdoutCaptured?: boolean
|
||||
/**
|
||||
* When true (SSH PTY child contexts), utilities that raw-write **`replStdout`** should emit CRLF newlines like **`bareOpensshWritePtyConsoleLine`** so streaming output (e.g. **`agent`**) displays line-by-line.
|
||||
*/
|
||||
bareOsPtyStdoutCrlf?: boolean
|
||||
/** Optional raw output hook for NUL/binary (e.g. **`printenv -0`**, **`find -print0`**) when **`process.stdout.write`** is unavailable. */
|
||||
bareOsBinWrite?(chunk: Uint8Array | string): void
|
||||
bareOsIpc?: BareOsIpc
|
||||
|
||||
@@ -25,19 +25,36 @@ function bareAgentErr(ctx, s) {
|
||||
}
|
||||
|
||||
/**
|
||||
* SSH PTYs expect CRLF for line breaks (same as `bare-openssh-pty-console.js` / `man` via console).
|
||||
* @param {Record<string, unknown> | undefined} ctx
|
||||
* @param {string} s
|
||||
*/
|
||||
function bareAgentNormalizeStreamNewlines(ctx, s) {
|
||||
if (ctx && ctx.bareOsPtyStdoutCrlf)
|
||||
return String(s).replace(/\r?\n/g, '\r\n')
|
||||
return String(s)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Record<string, unknown> | undefined} ctx
|
||||
* @param {import('stream').Writable | undefined} out
|
||||
* @param {string} s
|
||||
*/
|
||||
function bareAgentWriteOut(out, s) {
|
||||
function bareAgentWriteOut(ctx, out, s) {
|
||||
const text = bareAgentNormalizeStreamNewlines(ctx, s)
|
||||
if (out && typeof out.write === 'function') {
|
||||
try {
|
||||
out.write(s)
|
||||
out.write(text)
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
process.stdout.write(s)
|
||||
if (typeof bareOsEmitRaw === 'function') {
|
||||
bareOsEmitRaw(ctx, text)
|
||||
} else if (typeof process !== 'undefined' && process.stdout?.write) {
|
||||
process.stdout.write(text)
|
||||
}
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
@@ -125,7 +142,7 @@ async function bareAgentPromptSetupLine(ctx, prompt) {
|
||||
if (!stdin || typeof stdin.on !== 'function') {
|
||||
throw new Error('setup: stdin stream unavailable')
|
||||
}
|
||||
bareAgentWriteOut(stdout, '\x1b[?25h\x1b[0m' + prompt)
|
||||
bareAgentWriteOut(ctx, stdout, '\x1b[?25h\x1b[0m' + prompt)
|
||||
return bareAgentReadStreamLineOnce(stdin)
|
||||
}
|
||||
|
||||
@@ -263,6 +280,7 @@ async function bareAgentInteractiveSetup(ctx, argv0, paths, config, opts) {
|
||||
}
|
||||
const stdout = ctx.replStdout || ctx.stdout
|
||||
bareAgentWriteOut(
|
||||
ctx,
|
||||
stdout,
|
||||
'\n=== ' +
|
||||
argv0 +
|
||||
@@ -540,6 +558,7 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
|
||||
const fn = () => {
|
||||
masterAbort.abort()
|
||||
bareAgentWriteOut(
|
||||
ctx,
|
||||
stdout,
|
||||
bareEditSgr('dim', useColor) + '^C' + EDIT_ANSI_RESET + '\n'
|
||||
)
|
||||
@@ -609,7 +628,7 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
|
||||
if (e.type === 'delta_content') {
|
||||
const chunk = typeof e.content === 'string' ? e.content : ''
|
||||
assistantContent += chunk
|
||||
bareAgentWriteOut(stdout, chunk)
|
||||
bareAgentWriteOut(ctx, stdout, chunk)
|
||||
} else if (e.type === 'delta_tool_calls') {
|
||||
const arr = e.tool_calls
|
||||
if (Array.isArray(arr)) {
|
||||
@@ -650,6 +669,7 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
|
||||
const pt = u.prompt_tokens
|
||||
const ct = u.completion_tokens
|
||||
bareAgentWriteOut(
|
||||
ctx,
|
||||
stdout,
|
||||
'\n' +
|
||||
bareEditSgr('dim', useColor) +
|
||||
@@ -664,7 +684,7 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
|
||||
|
||||
if (!hasTools) {
|
||||
await bareAgentSaveHistory(ctx, paths.history, messages)
|
||||
bareAgentWriteOut(stdout, '\n')
|
||||
bareAgentWriteOut(ctx, stdout, '\n')
|
||||
break
|
||||
}
|
||||
|
||||
@@ -685,6 +705,7 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
|
||||
const name = fn?.name || ''
|
||||
const argsStr = fn?.arguments || '{}'
|
||||
bareAgentWriteOut(
|
||||
ctx,
|
||||
stdout,
|
||||
'\n' +
|
||||
bareEditSgr('keyword', useColor) +
|
||||
@@ -695,7 +716,7 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
|
||||
)
|
||||
|
||||
const spinner = bareEditSgr('dim', useColor) + '… running ' + name + EDIT_ANSI_RESET
|
||||
bareAgentWriteOut(stdout, spinner + '\r')
|
||||
bareAgentWriteOut(ctx, stdout, spinner + '\r')
|
||||
|
||||
const resultStr = await bareAgentDispatchTool({
|
||||
ctx,
|
||||
@@ -710,7 +731,7 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
|
||||
onTaskComplete
|
||||
})
|
||||
|
||||
bareAgentWriteOut(stdout, '\x1b[K')
|
||||
bareAgentWriteOut(ctx, stdout, '\x1b[K')
|
||||
|
||||
messages.push({
|
||||
role: 'tool',
|
||||
@@ -724,6 +745,7 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
|
||||
await bareAgentSaveHistory(ctx, paths.history, messages)
|
||||
if (completed) {
|
||||
bareAgentWriteOut(
|
||||
ctx,
|
||||
stdout,
|
||||
bareEditSgr('dim', useColor) +
|
||||
'\nDone: ' +
|
||||
|
||||
@@ -4225,19 +4225,36 @@ function bareAgentErr(ctx, s) {
|
||||
}
|
||||
|
||||
/**
|
||||
* SSH PTYs expect CRLF for line breaks (same as `bare-openssh-pty-console.js` / `man` via console).
|
||||
* @param {Record<string, unknown> | undefined} ctx
|
||||
* @param {string} s
|
||||
*/
|
||||
function bareAgentNormalizeStreamNewlines(ctx, s) {
|
||||
if (ctx && ctx.bareOsPtyStdoutCrlf)
|
||||
return String(s).replace(/\r?\n/g, '\r\n')
|
||||
return String(s)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Record<string, unknown> | undefined} ctx
|
||||
* @param {import('stream').Writable | undefined} out
|
||||
* @param {string} s
|
||||
*/
|
||||
function bareAgentWriteOut(out, s) {
|
||||
function bareAgentWriteOut(ctx, out, s) {
|
||||
const text = bareAgentNormalizeStreamNewlines(ctx, s)
|
||||
if (out && typeof out.write === 'function') {
|
||||
try {
|
||||
out.write(s)
|
||||
out.write(text)
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
process.stdout.write(s)
|
||||
if (typeof bareOsEmitRaw === 'function') {
|
||||
bareOsEmitRaw(ctx, text)
|
||||
} else if (typeof process !== 'undefined' && process.stdout?.write) {
|
||||
process.stdout.write(text)
|
||||
}
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
@@ -4325,7 +4342,7 @@ async function bareAgentPromptSetupLine(ctx, prompt) {
|
||||
if (!stdin || typeof stdin.on !== 'function') {
|
||||
throw new Error('setup: stdin stream unavailable')
|
||||
}
|
||||
bareAgentWriteOut(stdout, '\x1b[?25h\x1b[0m' + prompt)
|
||||
bareAgentWriteOut(ctx, stdout, '\x1b[?25h\x1b[0m' + prompt)
|
||||
return bareAgentReadStreamLineOnce(stdin)
|
||||
}
|
||||
|
||||
@@ -4463,6 +4480,7 @@ async function bareAgentInteractiveSetup(ctx, argv0, paths, config, opts) {
|
||||
}
|
||||
const stdout = ctx.replStdout || ctx.stdout
|
||||
bareAgentWriteOut(
|
||||
ctx,
|
||||
stdout,
|
||||
'\n=== ' +
|
||||
argv0 +
|
||||
@@ -4740,6 +4758,7 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
|
||||
const fn = () => {
|
||||
masterAbort.abort()
|
||||
bareAgentWriteOut(
|
||||
ctx,
|
||||
stdout,
|
||||
bareEditSgr('dim', useColor) + '^C' + EDIT_ANSI_RESET + '\n'
|
||||
)
|
||||
@@ -4809,7 +4828,7 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
|
||||
if (e.type === 'delta_content') {
|
||||
const chunk = typeof e.content === 'string' ? e.content : ''
|
||||
assistantContent += chunk
|
||||
bareAgentWriteOut(stdout, chunk)
|
||||
bareAgentWriteOut(ctx, stdout, chunk)
|
||||
} else if (e.type === 'delta_tool_calls') {
|
||||
const arr = e.tool_calls
|
||||
if (Array.isArray(arr)) {
|
||||
@@ -4850,6 +4869,7 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
|
||||
const pt = u.prompt_tokens
|
||||
const ct = u.completion_tokens
|
||||
bareAgentWriteOut(
|
||||
ctx,
|
||||
stdout,
|
||||
'\n' +
|
||||
bareEditSgr('dim', useColor) +
|
||||
@@ -4864,7 +4884,7 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
|
||||
|
||||
if (!hasTools) {
|
||||
await bareAgentSaveHistory(ctx, paths.history, messages)
|
||||
bareAgentWriteOut(stdout, '\n')
|
||||
bareAgentWriteOut(ctx, stdout, '\n')
|
||||
break
|
||||
}
|
||||
|
||||
@@ -4885,6 +4905,7 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
|
||||
const name = fn?.name || ''
|
||||
const argsStr = fn?.arguments || '{}'
|
||||
bareAgentWriteOut(
|
||||
ctx,
|
||||
stdout,
|
||||
'\n' +
|
||||
bareEditSgr('keyword', useColor) +
|
||||
@@ -4895,7 +4916,7 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
|
||||
)
|
||||
|
||||
const spinner = bareEditSgr('dim', useColor) + '… running ' + name + EDIT_ANSI_RESET
|
||||
bareAgentWriteOut(stdout, spinner + '\r')
|
||||
bareAgentWriteOut(ctx, stdout, spinner + '\r')
|
||||
|
||||
const resultStr = await bareAgentDispatchTool({
|
||||
ctx,
|
||||
@@ -4910,7 +4931,7 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
|
||||
onTaskComplete
|
||||
})
|
||||
|
||||
bareAgentWriteOut(stdout, '\x1b[K')
|
||||
bareAgentWriteOut(ctx, stdout, '\x1b[K')
|
||||
|
||||
messages.push({
|
||||
role: 'tool',
|
||||
@@ -4924,6 +4945,7 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
|
||||
await bareAgentSaveHistory(ctx, paths.history, messages)
|
||||
if (completed) {
|
||||
bareAgentWriteOut(
|
||||
ctx,
|
||||
stdout,
|
||||
bareEditSgr('dim', useColor) +
|
||||
'\nDone: ' +
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"schema": 2,
|
||||
"profileId": "bare-os-posix-like",
|
||||
"generatedAt": "2026-04-22T07:30:25.994Z",
|
||||
"generatedAt": "2026-04-22T21:10:53.539Z",
|
||||
"note": "Sparse POSIX Issue 7 coverage hints for /bin utilities. Omitted command names are not yet profiled here.",
|
||||
"commandIndex": [
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"schema": 1,
|
||||
"atMs": 1776843025994,
|
||||
"atMs": 1776892253538,
|
||||
"commands": [
|
||||
"agent",
|
||||
"arch",
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user