Skip to content
Merged
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
174 changes: 87 additions & 87 deletions .beads/issues.jsonl

Large diffs are not rendered by default.

65 changes: 65 additions & 0 deletions .trajectories/completed/2026-01/traj_xy9vifpqet80.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
{
"id": "traj_xy9vifpqet80",
"version": 1,
"task": {
"title": "Extract BaseWrapper from PtyWrapper and TmuxWrapper",
"source": {
"system": "plain",
"id": "agent-relay-wrap1"
}
},
"status": "completed",
"startedAt": "2026-01-06T12:27:13.004Z",
"agents": [
{
"name": "khaliqgant",
"role": "lead",
"joinedAt": "2026-01-06T12:27:13.007Z"
}
],
"chapters": [
{
"id": "chap_8t7vbaqwiz3f",
"title": "Work",
"agentName": "default",
"startedAt": "2026-01-06T12:27:45.698Z",
"events": [
{
"ts": 1767702465699,
"type": "decision",
"content": "Using abstract class with protected methods for shared implementation: Using abstract class with protected methods for shared implementation",
"raw": {
"question": "Using abstract class with protected methods for shared implementation",
"chosen": "Using abstract class with protected methods for shared implementation",
"alternatives": [],
"reasoning": "Allows subclasses to override while providing default behavior"
},
"significance": "high"
},
{
"ts": 1767703025592,
"type": "decision",
"content": "Created BaseWrapper abstract class with comprehensive tests: Created BaseWrapper abstract class with comprehensive tests",
"raw": {
"question": "Created BaseWrapper abstract class with comprehensive tests",
"chosen": "Created BaseWrapper abstract class with comprehensive tests",
"alternatives": [],
"reasoning": "Extracted shared functionality from PtyWrapper/TmuxWrapper into base class. Tests cover message queue, spawn/release, continuity, and relay command handling."
},
"significance": "high"
}
],
"endedAt": "2026-01-06T12:58:24.003Z"
}
],
"commits": [],
"filesChanged": [],
"projectId": "/Users/khaliqgant/Projects/agent-workforce/relay",
"tags": [],
"completedAt": "2026-01-06T12:58:24.003Z",
"retrospective": {
"summary": "Extracted BaseWrapper abstract class from PtyWrapper and TmuxWrapper using TDD approach. Created 29 new tests, removed ~900 lines of duplicate code, both wrappers now inherit shared functionality.",
"approach": "Standard approach",
"confidence": 0.9
}
}
37 changes: 37 additions & 0 deletions .trajectories/completed/2026-01/traj_xy9vifpqet80.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Trajectory: Extract BaseWrapper from PtyWrapper and TmuxWrapper

> **Status:** ✅ Completed
> **Task:** agent-relay-wrap1
> **Confidence:** 90%
> **Started:** January 6, 2026 at 01:27 PM
> **Completed:** January 6, 2026 at 01:58 PM

---

## Summary

Extracted BaseWrapper abstract class from PtyWrapper and TmuxWrapper using TDD approach. Created 29 new tests, removed ~900 lines of duplicate code, both wrappers now inherit shared functionality.

**Approach:** Standard approach

---

## Key Decisions

### Using abstract class with protected methods for shared implementation
- **Chose:** Using abstract class with protected methods for shared implementation
- **Reasoning:** Allows subclasses to override while providing default behavior

### Created BaseWrapper abstract class with comprehensive tests
- **Chose:** Created BaseWrapper abstract class with comprehensive tests
- **Reasoning:** Extracted shared functionality from PtyWrapper/TmuxWrapper into base class. Tests cover message queue, spawn/release, continuity, and relay command handling.

---

## Chapters

### 1. Work
*Agent: default*

- Using abstract class with protected methods for shared implementation: Using abstract class with protected methods for shared implementation
- Created BaseWrapper abstract class with comprehensive tests: Created BaseWrapper abstract class with comprehensive tests
9 changes: 8 additions & 1 deletion .trajectories/index.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"version": 1,
"lastUpdated": "2026-01-06T08:25:20.797Z",
"lastUpdated": "2026-01-06T12:58:24.029Z",
"trajectories": {
"traj_ozd98si6a7ns": {
"title": "Fix thinking indicator showing on all messages",
Expand Down Expand Up @@ -344,6 +344,13 @@
"startedAt": "2026-01-06T08:24:36.222Z",
"completedAt": "2026-01-06T08:25:20.777Z",
"path": "/home/user/relay/.trajectories/completed/2026-01/traj_ui9b4tqxoa7j.json"
},
"traj_xy9vifpqet80": {
"title": "Extract BaseWrapper from PtyWrapper and TmuxWrapper",
"status": "completed",
"startedAt": "2026-01-06T12:27:13.004Z",
"completedAt": "2026-01-06T12:58:24.003Z",
"path": "/Users/khaliqgant/Projects/agent-workforce/relay/.trajectories/completed/2026-01/traj_xy9vifpqet80.json"
}
}
}
28 changes: 28 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -525,3 +525,31 @@ The dashboard automatically tracks:

Agents can view their status at the dashboard URL provided at startup.
<!-- prpm:snippet:end @agent-relay/[email protected] -->

# Git Workflow Rules

## NEVER Push Directly to Main

**CRITICAL: Agents must NEVER push directly to the main branch.**

- Always work on a feature branch
- Commit and push to the feature branch only
- Let the user decide when to merge to main
- Do not merge to main without explicit user approval

```bash
# CORRECT workflow
git checkout -b feature/my-feature
# ... do work ...
git add .
git commit -m "My changes"
git push origin feature/my-feature
# STOP HERE - let user merge

# WRONG - never do this
git checkout main
git merge feature/my-feature
git push origin main # NO!
```

This ensures the user maintains control over what goes into the main branch.
77 changes: 57 additions & 20 deletions src/cloud/api/onboarding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -343,30 +343,67 @@ onboardingRouter.post('/cli/:provider/complete/:sessionId', async (req: Request,
session.status = 'success';
}

// Fetch credentials from workspace
// Fetch credentials from workspace with retry
// Credentials may not be immediately available after OAuth completes
if (!accessToken) {
try {
const credsResponse = await fetch(
`${session.workspaceUrl}/auth/cli/${provider}/creds/${session.workspaceSessionId}`
);
if (credsResponse.ok) {
const creds = await credsResponse.json() as {
token?: string;
refreshToken?: string;
expiresAt?: string;
const MAX_CREDS_RETRIES = 5;
const CREDS_RETRY_DELAY = 1000; // 1 second between retries

for (let attempt = 1; attempt <= MAX_CREDS_RETRIES; attempt++) {
try {
console.log(`[onboarding] Fetching credentials from workspace (attempt ${attempt}/${MAX_CREDS_RETRIES})`);
const credsResponse = await fetch(
`${session.workspaceUrl}/auth/cli/${provider}/creds/${session.workspaceSessionId}`
);

if (credsResponse.ok) {
const creds = await credsResponse.json() as {
token?: string;
refreshToken?: string;
tokenExpiresAt?: string;
};
accessToken = creds.token;
refreshToken = creds.refreshToken;
if (creds.tokenExpiresAt) {
tokenExpiresAt = new Date(creds.tokenExpiresAt);
}
console.log('[onboarding] Fetched credentials from workspace:', {
hasToken: !!accessToken,
hasRefreshToken: !!refreshToken,
attempt,
});
break; // Success, exit retry loop
}

// Check if it's an error state (not just "not ready yet")
const errorBody = await credsResponse.json().catch(() => ({})) as {
status?: string;
error?: string;
errorHint?: string;
recoverable?: boolean;
};
accessToken = creds.token;
refreshToken = creds.refreshToken;
if (creds.expiresAt) {
tokenExpiresAt = new Date(creds.expiresAt);

if (errorBody.status === 'error') {
// Auth failed, don't retry
console.error('[onboarding] Auth failed in workspace:', errorBody);
return res.status(400).json({
error: errorBody.error || 'Authentication failed',
errorHint: errorBody.errorHint,
recoverable: errorBody.recoverable,
});
}

// If not ready yet and we have more retries, wait and try again
if (attempt < MAX_CREDS_RETRIES) {
console.log(`[onboarding] Credentials not ready yet, retrying in ${CREDS_RETRY_DELAY}ms...`);
await new Promise(resolve => setTimeout(resolve, CREDS_RETRY_DELAY));
}
} catch (err) {
console.error(`[onboarding] Failed to get credentials from workspace (attempt ${attempt}):`, err);
if (attempt < MAX_CREDS_RETRIES) {
await new Promise(resolve => setTimeout(resolve, CREDS_RETRY_DELAY));
}
console.log('[onboarding] Fetched credentials from workspace:', {
hasToken: !!accessToken,
hasRefreshToken: !!refreshToken,
});
}
} catch (err) {
console.error('[onboarding] Failed to get credentials from workspace:', err);
}
}
}
Expand Down
Loading