Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions packages/core/src/__tests__/channel-messenger.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import { describe, expect, it, vi } from 'vitest';
import {
ChannelMessenger,
formatError,
formatObserverGuidance,
formatStepOutput,
scrubSecrets,
sendToChannel,
truncateMessage,
} from '../channel-messenger.js';
Expand Down Expand Up @@ -42,6 +44,42 @@ describe('channel messenger helpers', () => {
expect(formatted).not.toContain('ghp_abcdefghijklmnopqrstuvwxyzABCDEFGHIJ');
});

it.each([
'rk_live_0123456789abcdef',
'at_live_0123456789abcdef',
'nt_live_0123456789abcdef',
'ot_live_0123456789abcdef',
'cld_at_0123456789abcdef',
'rth_at_0123456789abcdef',
'ocl_node_enr_0123456789abcdef',
'br_0123456789abcdef',
])('scrubSecrets redacts Relay credential value %s', (credential) => {
const scrubbed = scrubSecrets(`request denied for ${credential}`);
expect(scrubbed).toBe('request denied for [REDACTED]');
expect(scrubbed).not.toContain(credential);
});

it.each([
'broker started on port 3888',
'the library is at ./br',
'abbreviation',
'number_of_brokers=4',
'https://agentrelay.com/observer',
])('scrubSecrets preserves non-secret output %s', (text) => {
expect(scrubSecrets(text)).toBe(text);
});

it('omits credential-bearing observer links from auto-created workspace guidance', () => {
const guidance = formatObserverGuidance('workflow-room');

expect(guidance).toEqual([
'Workspace created for this workflow.',
' Observation: requires a separately provisioned, read-only observer token',
' Channel: workflow-room',
]);
expect(guidance.join('\n')).not.toMatch(/observer\?key=|\[REDACTED\]/);
});

it('formatError normalizes unknown errors', () => {
expect(formatError('build', new Error('Boom'))).toBe('**[build]** Failed: Boom');
expect(formatError('build', 'bad input')).toBe('**[build]** Failed: bad input');
Expand Down
9 changes: 9 additions & 0 deletions packages/core/src/channel-messenger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export function formatError(stepName: string, error: unknown): string {
// Common secret patterns to redact from channel output.
const SECRET_PATTERNS = [
/(?:api[_-]?key|apikey|secret[_-]?key|access[_-]?token|auth[_-]?token|bearer)\s*[:=]\s*\S+/gi,
/(?:rk_live_|at_live_|nt_live_|ot_live_|cld_at_|rth_at_|ocl_node_enr_|br_)[a-zA-Z0-9_%-]+(?:\.[a-zA-Z0-9_%-]+)*/g,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Align the br pattern and regression test with the required live prefix.

The br_ alternative matches br_live_... only by prefix overlap. It also redacts unrelated br_... values. The test does not verify the required br_live_... format.

  • packages/core/src/channel-messenger.ts#L50-L50: use br_live_; retain br_ only if it is a documented legacy format.
  • packages/core/src/__tests__/channel-messenger.test.ts#L47-L60: add br_live_0123456789abcdef as an exact regression case.
📍 Affects 2 files
  • packages/core/src/channel-messenger.ts#L50-L50 (this comment)
  • packages/core/src/__tests__/channel-messenger.test.ts#L47-L60
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/core/src/channel-messenger.ts` at line 50, The secret-redaction
regex must require the documented br_live_ prefix and include an exact
regression case. In packages/core/src/channel-messenger.ts lines 50-50, replace
the br_ alternative with br_live_ unless br_ is a documented legacy format; in
packages/core/src/__tests__/channel-messenger.test.ts lines 47-60, add
br_live_0123456789abcdef as an exact test case.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Constrain matching of the short br_ prefix

When step or forwarded channel output contains an ordinary identifier such as br_feature, this alternative treats it as a credential because br_ is followed by only an unconstrained +, and scrubForChannel applies the pattern to all posted output. This silently changes legitimate workflow results to [REDACTED]; require the actual credential length or stronger token boundaries for this especially generic prefix.

Useful? React with 👍 / 👎.

/(?:sk|pk|rk|ak)[-_][a-zA-Z0-9]{20,}/g,
/ghp_[a-zA-Z0-9]{36,}/g,
/gho_[a-zA-Z0-9]{36,}/g,
Expand Down Expand Up @@ -97,6 +98,14 @@ export function scrubSecrets(text: string): string {
return result;
}

export function formatObserverGuidance(channel: string): string[] {
return [
'Workspace created for this workflow.',
' Observation: requires a separately provisioned, read-only observer token',
` Channel: ${channel}`,
];
}

function stripMalformedPtyFrameGarbage(line: string): string {
const strippedRuns = line.replace(MALFORMED_PTY_FRAME_RUN_RE, ' ');
const compact = strippedRuns.replace(SPINNER_RE, '').replace(/\s+/g, '');
Expand Down
14 changes: 9 additions & 5 deletions packages/core/src/runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,11 @@ import { ensureRelayfileMount, type MountHandle } from '@relayfile/sdk/workspace
import { collectCliSession, type CliSessionReport } from './cli-session-collector.js';
import { executeApiStep } from './api-executor.js';
import { BudgetExceededError, BudgetTracker } from './budget-tracker.js';
import { ChannelMessenger, scrubForChannel as scrubWorkflowOutputForChannel } from './channel-messenger.js';
import {
ChannelMessenger,
formatObserverGuidance,
scrubForChannel as scrubWorkflowOutputForChannel,
} from './channel-messenger.js';
import { InMemoryWorkflowDb } from './memory-db.js';
import { buildCommand as buildProcessCommand, spawnProcess } from './process-spawner.js';
import { createProcessBackendExecutor } from './process-backend-executor.js';
Expand Down Expand Up @@ -3780,10 +3784,10 @@ export class WorkflowRunner {
this.log('Resolving Relaycast API key...');
await this.ensureRelaycastApiKey(channel);
this.log('API key resolved');
if (this.relayApiKeyAutoCreated && this.relayApiKey) {
this.log(`Workspace created — follow this run in Relaycast:`);
this.log(` Observer: https://agentrelay.com/observer?key=${this.relayApiKey}`);
this.log(` Channel: ${channel}`);
if (this.relayApiKeyAutoCreated) {
for (const line of formatObserverGuidance(channel)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Auto-created workspace guidance disappears from CLI/Listr output when the workflow uses a configured channel such as my-channel, so users receive neither the observer instructions nor the channel. The output filters should allow the new guidance markers (or all Channel: lines) in addition to the legacy observer URL markers.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/core/src/runner.ts, line 3788:

<comment>Auto-created workspace guidance disappears from CLI/Listr output when the workflow uses a configured channel such as `my-channel`, so users receive neither the observer instructions nor the channel. The output filters should allow the new guidance markers (or all `Channel:` lines) in addition to the legacy observer URL markers.</comment>

<file context>
@@ -3784,10 +3784,10 @@ export class WorkflowRunner {
-            this.log(`  Observer: ${formatObserverUrl(this.relayApiKey)}`);
-            this.log(`  Channel: ${channel}`);
+          if (this.relayApiKeyAutoCreated) {
+            for (const line of formatObserverGuidance(channel)) {
+              this.log(line);
+            }
</file context>

this.log(line);
}
}
}

Expand Down