viber-channel 0.8.1 → 0.8.2
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/lib/control_stream.ts +14 -1
- package/package.json +46 -46
- package/viber-codex-bridge.ts +33 -10
package/lib/control_stream.ts
CHANGED
|
@@ -297,6 +297,12 @@ export function runPersistentControlStream(
|
|
|
297
297
|
};
|
|
298
298
|
|
|
299
299
|
const done = (async () => {
|
|
300
|
+
// #389 step-02: announce ONCE that the instance control stream actually
|
|
301
|
+
// connected server-side (the SSE `connected` open-marker arrived) — proof
|
|
302
|
+
// the agent is ONLINE/reachable, not merely registered. vibe-master's spawn
|
|
303
|
+
// readiness gate polls the bridge log for this line, so a launch that
|
|
304
|
+
// registers then fails to connect (CF/401/network) is NOT reported as ready.
|
|
305
|
+
let announcedOnline = false;
|
|
300
306
|
try {
|
|
301
307
|
while (true) {
|
|
302
308
|
if (signal?.aborted) {
|
|
@@ -305,6 +311,13 @@ export function runPersistentControlStream(
|
|
|
305
311
|
}
|
|
306
312
|
try {
|
|
307
313
|
for await (const ev of subscribeControlStream(opts)) {
|
|
314
|
+
if (ev.event === "connected") {
|
|
315
|
+
if (!announcedOnline) {
|
|
316
|
+
announcedOnline = true;
|
|
317
|
+
log("[control-stream] online — instance control stream connected\n");
|
|
318
|
+
}
|
|
319
|
+
continue;
|
|
320
|
+
}
|
|
308
321
|
if (ev.event === "stop") {
|
|
309
322
|
settleFirstReject(new ControlStreamStopped());
|
|
310
323
|
handlers.onStop?.("stopped");
|
|
@@ -314,7 +327,7 @@ export function runPersistentControlStream(
|
|
|
314
327
|
handlers.onBeatNow?.();
|
|
315
328
|
continue;
|
|
316
329
|
}
|
|
317
|
-
if (ev.event !== "join") continue; //
|
|
330
|
+
if (ev.event !== "join") continue; // ping / unknown (connected handled above)
|
|
318
331
|
const minted = parseJoinPayload(ev.data, log);
|
|
319
332
|
if (!minted) continue;
|
|
320
333
|
handlers.onJoin?.(minted);
|
package/package.json
CHANGED
|
@@ -1,46 +1,46 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "viber-channel",
|
|
3
|
-
"version": "0.8.
|
|
4
|
-
"description": "Voice + text MCP channel between a Claude Code session and the Viber UI (https://viber.dgypx.dev). Push transcripts to Claude; send_message tool delivers text back to the UI.",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"bin": {
|
|
7
|
-
"viber-channel": "./viber-channel.ts",
|
|
8
|
-
"viber-codex-bridge": "./viber-codex-bridge.ts",
|
|
9
|
-
"viber-gemma-bridge": "./viber-gemma-bridge.ts",
|
|
10
|
-
"viber-codex-supervisor": "./viber-codex-supervisor.ts"
|
|
11
|
-
},
|
|
12
|
-
"files": [
|
|
13
|
-
"viber-channel.ts",
|
|
14
|
-
"viber-codex-bridge.ts",
|
|
15
|
-
"viber-gemma-bridge.ts",
|
|
16
|
-
"viber-codex-supervisor.ts",
|
|
17
|
-
"lib/",
|
|
18
|
-
"README.md"
|
|
19
|
-
],
|
|
20
|
-
"repository": {
|
|
21
|
-
"type": "git",
|
|
22
|
-
"url": "git+https://github.com/dgx80/viber.git",
|
|
23
|
-
"directory": "viber-channel"
|
|
24
|
-
},
|
|
25
|
-
"homepage": "https://viber.dgypx.dev",
|
|
26
|
-
"bugs": {
|
|
27
|
-
"url": "https://github.com/dgx80/viber/issues"
|
|
28
|
-
},
|
|
29
|
-
"keywords": [
|
|
30
|
-
"viber",
|
|
31
|
-
"claude-code",
|
|
32
|
-
"mcp",
|
|
33
|
-
"channel",
|
|
34
|
-
"voice",
|
|
35
|
-
"transcription"
|
|
36
|
-
],
|
|
37
|
-
"license": "MIT",
|
|
38
|
-
"scripts": {
|
|
39
|
-
"start": "bun run viber-channel.ts",
|
|
40
|
-
"start:codex-bridge": "bun run viber-codex-bridge.ts",
|
|
41
|
-
"test": "bun test"
|
|
42
|
-
},
|
|
43
|
-
"dependencies": {
|
|
44
|
-
"@modelcontextprotocol/sdk": "^1.0.0"
|
|
45
|
-
}
|
|
46
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "viber-channel",
|
|
3
|
+
"version": "0.8.2",
|
|
4
|
+
"description": "Voice + text MCP channel between a Claude Code session and the Viber UI (https://viber.dgypx.dev). Push transcripts to Claude; send_message tool delivers text back to the UI.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"viber-channel": "./viber-channel.ts",
|
|
8
|
+
"viber-codex-bridge": "./viber-codex-bridge.ts",
|
|
9
|
+
"viber-gemma-bridge": "./viber-gemma-bridge.ts",
|
|
10
|
+
"viber-codex-supervisor": "./viber-codex-supervisor.ts"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"viber-channel.ts",
|
|
14
|
+
"viber-codex-bridge.ts",
|
|
15
|
+
"viber-gemma-bridge.ts",
|
|
16
|
+
"viber-codex-supervisor.ts",
|
|
17
|
+
"lib/",
|
|
18
|
+
"README.md"
|
|
19
|
+
],
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "git+https://github.com/dgx80/viber.git",
|
|
23
|
+
"directory": "viber-channel"
|
|
24
|
+
},
|
|
25
|
+
"homepage": "https://viber.dgypx.dev",
|
|
26
|
+
"bugs": {
|
|
27
|
+
"url": "https://github.com/dgx80/viber/issues"
|
|
28
|
+
},
|
|
29
|
+
"keywords": [
|
|
30
|
+
"viber",
|
|
31
|
+
"claude-code",
|
|
32
|
+
"mcp",
|
|
33
|
+
"channel",
|
|
34
|
+
"voice",
|
|
35
|
+
"transcription"
|
|
36
|
+
],
|
|
37
|
+
"license": "MIT",
|
|
38
|
+
"scripts": {
|
|
39
|
+
"start": "bun run viber-channel.ts",
|
|
40
|
+
"start:codex-bridge": "bun run viber-codex-bridge.ts",
|
|
41
|
+
"test": "bun test"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"@modelcontextprotocol/sdk": "^1.0.0"
|
|
45
|
+
}
|
|
46
|
+
}
|
package/viber-codex-bridge.ts
CHANGED
|
@@ -356,7 +356,7 @@ function instructionsForTier(tier: AgentTier): string {
|
|
|
356
356
|
return [
|
|
357
357
|
"You are a bridge-owned Codex agent connected to Viber, in READ-WRITE mode.",
|
|
358
358
|
"You MAY read files, create/modify files within the workspace, and run commands (including state-changing ones) to carry out the user's requests.",
|
|
359
|
-
"The sandbox is workspace-write: writes are confined to the workspace and network access is
|
|
359
|
+
"The sandbox is workspace-write: writes are confined to the workspace, and network access is ENABLED — so you can run git (including `git push`) and open pull requests (e.g. with `gh`).",
|
|
360
360
|
"There is NO human approval step, so be deliberate: make only the changes the user asked for, and avoid destructive commands unless explicitly requested.",
|
|
361
361
|
VOICE_CONCISE_LINE,
|
|
362
362
|
CHANNEL_TOOLS_LINE,
|
|
@@ -385,17 +385,43 @@ function instructionsForTier(tier: AgentTier): string {
|
|
|
385
385
|
// Approval policy per tier. `read`/`write` use "never" so Codex runs its commands
|
|
386
386
|
// WITHOUT a human approver (there is none on the bridge) — the SANDBOX is the real
|
|
387
387
|
// guardrail (read-only blocks writes/network; workspace-write confines writes to
|
|
388
|
-
// the workspace
|
|
389
|
-
function approvalForTier(tier: AgentTier): "never" | "on-request" {
|
|
388
|
+
// the workspace). `chat` keeps "on-request" (no exec anyway).
|
|
389
|
+
export function approvalForTier(tier: AgentTier): "never" | "on-request" {
|
|
390
390
|
return tier === "chat" ? "on-request" : "never";
|
|
391
391
|
}
|
|
392
392
|
|
|
393
393
|
// Codex OS sandbox per tier. `write` widens to workspace-write (edits confined to
|
|
394
|
-
// the workspace
|
|
395
|
-
function sandboxForTier(tier: AgentTier): "read-only" | "workspace-write" {
|
|
394
|
+
// the workspace); chat/read stay read-only.
|
|
395
|
+
export function sandboxForTier(tier: AgentTier): "read-only" | "workspace-write" {
|
|
396
396
|
return tier === "write" ? "workspace-write" : "read-only";
|
|
397
397
|
}
|
|
398
398
|
|
|
399
|
+
// Network access per tier (#389 step-03). ONLY the trusted `write` tier gets
|
|
400
|
+
// network — so a codex coder can `git push` and open its PR (git-over-https +
|
|
401
|
+
// `gh`). read/chat stay offline. SECURITY: codex's workspace-write sandbox opens
|
|
402
|
+
// ALL network, not git-only (the app-server sandbox has no per-domain allowlist),
|
|
403
|
+
// so this is a TRUST decision scoped to the write tier, which is already
|
|
404
|
+
// operator-authorized-at-launch for unattended command execution. If codex ever
|
|
405
|
+
// exposes an outbound-domain allowlist, tighten here (follow-up issue).
|
|
406
|
+
export function networkForTier(tier: AgentTier): boolean {
|
|
407
|
+
return tier === "write";
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// The EXACT `sandboxPolicy` object sent on every `turn/start` (#389 step-03).
|
|
411
|
+
// Per the codex app-server protocol (generate-ts), `networkAccess` lives ONLY on
|
|
412
|
+
// this per-turn policy object (workspaceWrite/readOnly variants) — NOT on
|
|
413
|
+
// thread/start|resume, which take a bare `sandbox` MODE string. So the turn-level
|
|
414
|
+
// policy is the single lever for network. Exported to unit-test the payload/tier.
|
|
415
|
+
export function buildTurnSandboxPolicy(
|
|
416
|
+
tier: AgentTier,
|
|
417
|
+
):
|
|
418
|
+
| { type: "workspaceWrite"; networkAccess: boolean }
|
|
419
|
+
| { type: "readOnly"; networkAccess: boolean } {
|
|
420
|
+
return sandboxForTier(tier) === "workspace-write"
|
|
421
|
+
? { type: "workspaceWrite", networkAccess: networkForTier(tier) }
|
|
422
|
+
: { type: "readOnly", networkAccess: false };
|
|
423
|
+
}
|
|
424
|
+
|
|
399
425
|
const AGENT_TIER: AgentTier = resolveAgentTier();
|
|
400
426
|
// Identity line (#309 step-17) is appended AFTER the tier instructions so the
|
|
401
427
|
// permission rules stay first + strongest; the role is framed as descriptive and
|
|
@@ -635,10 +661,7 @@ class CodexAppServer {
|
|
|
635
661
|
cwd: process.cwd(),
|
|
636
662
|
runtimeWorkspaceRoots: [process.cwd()],
|
|
637
663
|
approvalPolicy: AGENT_APPROVAL_POLICY,
|
|
638
|
-
sandboxPolicy:
|
|
639
|
-
AGENT_SANDBOX === "workspace-write"
|
|
640
|
-
? { type: "workspaceWrite", networkAccess: false }
|
|
641
|
-
: { type: "readOnly", networkAccess: false },
|
|
664
|
+
sandboxPolicy: buildTurnSandboxPolicy(AGENT_TIER),
|
|
642
665
|
developerInstructions: AGENT_INSTRUCTIONS,
|
|
643
666
|
responsesapiClientMetadata: { source: "viber-codex-bridge" },
|
|
644
667
|
});
|
|
@@ -1115,7 +1138,7 @@ function setupCodexRuntime(deps: {
|
|
|
1115
1138
|
|
|
1116
1139
|
function logCodexSafety(): void {
|
|
1117
1140
|
process.stderr.write(
|
|
1118
|
-
`${LOG_PREFIX} codex safety: tier=${AGENT_TIER}, sandbox=${AGENT_SANDBOX}, approvalPolicy=${AGENT_APPROVAL_POLICY}, network
|
|
1141
|
+
`${LOG_PREFIX} codex safety: tier=${AGENT_TIER}, sandbox=${AGENT_SANDBOX}, approvalPolicy=${AGENT_APPROVAL_POLICY}, network=${networkForTier(AGENT_TIER)}, ` +
|
|
1119
1142
|
`instructions=${
|
|
1120
1143
|
AGENT_TIER === "write"
|
|
1121
1144
|
? "read+write/run commands (workspace)"
|