agent-relay-orchestrator 0.27.2 → 0.29.0
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/package.json +2 -2
- package/src/control.ts +1 -1
- package/src/relay.ts +1 -1
- package/src/self-supervision.ts +1 -1
- package/src/self-upgrade.ts +2 -2
- package/src/spawn.ts +1 -1
- package/src/terminal-stream.ts +16 -3
- package/src/workspace-probe.ts +4 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-relay-orchestrator",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.29.0",
|
|
4
4
|
"description": "Agent Relay orchestrator — manages agent lifecycle across hosts",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"test": "bun test"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"agent-relay-sdk": "0.2.
|
|
19
|
+
"agent-relay-sdk": "0.2.18"
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"@types/bun": "latest",
|
package/src/control.ts
CHANGED
|
@@ -192,7 +192,7 @@ function shutdownTimeoutMs(ctrl: Record<string, any>): number | undefined {
|
|
|
192
192
|
// input and stays defensive against junk).
|
|
193
193
|
// - cwd: `source.cwd || baseDir` — an empty-string cwd now falls back to
|
|
194
194
|
// baseDir on the restart path too (was a latent bug; empty cwd is invalid).
|
|
195
|
-
|
|
195
|
+
function spawnOptionsFromRecord(source: Record<string, any>, config: OrchestratorConfig): SpawnOptions {
|
|
196
196
|
return {
|
|
197
197
|
provider: source.provider === "codex" ? "codex" : "claude",
|
|
198
198
|
cwd: source.cwd || config.baseDir,
|
package/src/relay.ts
CHANGED
package/src/self-supervision.ts
CHANGED
|
@@ -32,7 +32,7 @@ export function detectSelfSupervision(moduleUrl: string = import.meta.url): Self
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
/** Reset the cache. Test-only. */
|
|
35
|
-
|
|
35
|
+
function resetSelfSupervisionCache(): void {
|
|
36
36
|
cached = undefined;
|
|
37
37
|
}
|
|
38
38
|
|
package/src/self-upgrade.ts
CHANGED
|
@@ -16,7 +16,7 @@ const CACHE_RACE_RE = /ETARGET|No matching version found|notarget/i;
|
|
|
16
16
|
const DEFAULT_INSTALL_RETRIES = 4;
|
|
17
17
|
const DEFAULT_INSTALL_RETRY_BASE_MS = 2000;
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
interface SelfUpgradeOptions {
|
|
20
20
|
/** Sleep between install retries (injectable for tests). */
|
|
21
21
|
sleep?: (ms: number) => Promise<void>;
|
|
22
22
|
/** Max extra install attempts after the first on a cache-race error. */
|
|
@@ -55,7 +55,7 @@ const defaultRunner: SelfUpgradeRunner = {
|
|
|
55
55
|
},
|
|
56
56
|
};
|
|
57
57
|
|
|
58
|
-
|
|
58
|
+
interface SelfUpgradePlan {
|
|
59
59
|
targetVersion: string;
|
|
60
60
|
providers: string[];
|
|
61
61
|
unit: string;
|
package/src/spawn.ts
CHANGED
package/src/terminal-stream.ts
CHANGED
|
@@ -144,7 +144,20 @@ export function decodeControlOutput(data: string): Uint8Array {
|
|
|
144
144
|
return Uint8Array.from(out);
|
|
145
145
|
}
|
|
146
146
|
|
|
147
|
-
|
|
147
|
+
// The control stream is read as latin1 so the `%output` octal path above sees faithful
|
|
148
|
+
// bytes. But command-reply blocks (capture-pane grid rows) arrive as raw UTF-8, so their
|
|
149
|
+
// lines reach us as the byte-faithful latin1 representation. Re-decode them to real UTF-8
|
|
150
|
+
// here, or multi-byte glyphs (box-drawing, powerline) double-encode on the next repaint
|
|
151
|
+
// and render as mojibake (#270). Safe to do per line: no UTF-8 continuation byte is
|
|
152
|
+
// 0x0A/0x0D, so the newline split never lands mid-sequence.
|
|
153
|
+
const REPLY_UTF8_DECODER = new TextDecoder("utf-8");
|
|
154
|
+
export function latin1LineToUtf8(line: string): string {
|
|
155
|
+
const bytes = new Uint8Array(line.length);
|
|
156
|
+
for (let i = 0; i < line.length; i++) bytes[i] = line.charCodeAt(i) & 0xff;
|
|
157
|
+
return REPLY_UTF8_DECODER.decode(bytes);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
type ControlLine =
|
|
148
161
|
| { type: "output"; pane: string; bytes: Uint8Array }
|
|
149
162
|
| { type: "exit"; reason?: string }
|
|
150
163
|
// Command-reply block framing. Every command written to the control client's stdin is
|
|
@@ -191,7 +204,7 @@ export function parseControlLine(line: string): ControlLine {
|
|
|
191
204
|
// scroll-region TUIs like Claude Code emit constantly). So we track whether the outbound
|
|
192
205
|
// stream is at a sequence boundary (ground) and only inject there. This is a deliberately
|
|
193
206
|
// minimal VT state machine: enough to know "are we mid-sequence?", not a full parser.
|
|
194
|
-
|
|
207
|
+
type AnsiState = "ground" | "esc" | "esc-charset" | "csi" | "string" | "string-esc";
|
|
195
208
|
|
|
196
209
|
export function advanceAnsiState(state: AnsiState, byte: number): AnsiState {
|
|
197
210
|
// CAN (0x18) / SUB (0x1a) abort any in-progress sequence from any state → ground.
|
|
@@ -428,7 +441,7 @@ class SessionStream {
|
|
|
428
441
|
if (!entry) return; // unexpected extra block — drop it rather than mis-correlate
|
|
429
442
|
if (entry.timer) clearTimeout(entry.timer);
|
|
430
443
|
if (isError) entry.reject(new Error(block.lines.join(" ").trim() || "tmux command error"));
|
|
431
|
-
else entry.resolve(block.lines);
|
|
444
|
+
else entry.resolve(block.lines.map(latin1LineToUtf8));
|
|
432
445
|
}
|
|
433
446
|
|
|
434
447
|
private enqueue(bytes: Uint8Array): void {
|
package/src/workspace-probe.ts
CHANGED
|
@@ -441,7 +441,7 @@ export function refreshWorkspaceDeps(repoRoot: string, worktreePath: string, opt
|
|
|
441
441
|
* install (full isolation, no shared cache), or =none to skip entirely.
|
|
442
442
|
* Never throws — provisioning failure must not block the spawn.
|
|
443
443
|
*/
|
|
444
|
-
|
|
444
|
+
function provisionWorkspaceDeps(repoRoot: string, worktreePath: string): WorkspaceDepsProvision {
|
|
445
445
|
const requested = (process.env.AGENT_RELAY_WORKSPACE_DEPS || "symlink").toLowerCase();
|
|
446
446
|
if (requested === "none") return { mode: "none" };
|
|
447
447
|
|
|
@@ -1086,7 +1086,9 @@ function workspaceId(input: WorkspaceResolutionInput): string {
|
|
|
1086
1086
|
|
|
1087
1087
|
function branchName(input: WorkspaceResolutionInput, id: string): string {
|
|
1088
1088
|
const owner = input.policyName || input.label || input.automationId || "manual";
|
|
1089
|
-
|
|
1089
|
+
// 40 fits a full UUID (36) plus the `sp_`-stripped slack. A tighter cap (was 24)
|
|
1090
|
+
// sliced the session UUID mid-string, leaving an unaddressable, dangling ref (#282).
|
|
1091
|
+
return `agent/${safeSegment(owner, 48)}/${safeSegment(id.replace(/^sp[_-]?/, ""), 40)}`;
|
|
1090
1092
|
}
|
|
1091
1093
|
|
|
1092
1094
|
/** Next free `<branch>-N` cycle name for a recycled worktree (#206). Strips any
|