agent-relay 1.3.1 → 1.3.3
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/.trajectories/active/traj_3yx9dy148mge.json +42 -0
- package/.trajectories/completed/2026-01/traj_1g7yx6qtg4ai.json +49 -0
- package/.trajectories/completed/2026-01/traj_1g7yx6qtg4ai.md +31 -0
- package/.trajectories/completed/2026-01/traj_4qwd4zmhfwp4.json +49 -0
- package/.trajectories/completed/2026-01/traj_4qwd4zmhfwp4.md +31 -0
- package/.trajectories/completed/2026-01/traj_6unwwmgyj5sq.json +109 -0
- package/.trajectories/completed/2026-01/traj_a0tqx8biw9c4.json +49 -0
- package/.trajectories/completed/2026-01/traj_a0tqx8biw9c4.md +31 -0
- package/.trajectories/completed/2026-01/traj_ax8uungxz2qh.json +66 -0
- package/.trajectories/completed/2026-01/traj_ax8uungxz2qh.md +36 -0
- package/.trajectories/completed/2026-01/traj_c9izbh2snpzf.json +49 -0
- package/.trajectories/completed/2026-01/traj_c9izbh2snpzf.md +31 -0
- package/.trajectories/completed/2026-01/traj_cpn70dw066nt.json +65 -0
- package/.trajectories/completed/2026-01/traj_cpn70dw066nt.md +37 -0
- package/.trajectories/completed/2026-01/traj_erglv2f8t9eh.json +36 -0
- package/.trajectories/completed/2026-01/traj_erglv2f8t9eh.md +21 -0
- package/.trajectories/completed/2026-01/traj_he75f24d1xfm.json +101 -0
- package/.trajectories/completed/2026-01/traj_he75f24d1xfm.md +52 -0
- package/.trajectories/completed/2026-01/traj_lgtodco7dp1n.json +61 -0
- package/.trajectories/completed/2026-01/traj_lgtodco7dp1n.md +36 -0
- package/.trajectories/completed/2026-01/traj_oszg9flv74pk.json +73 -0
- package/.trajectories/completed/2026-01/traj_oszg9flv74pk.md +41 -0
- package/.trajectories/completed/2026-01/traj_pulomd3y8cvj.json +77 -0
- package/.trajectories/completed/2026-01/traj_pulomd3y8cvj.md +42 -0
- package/.trajectories/completed/2026-01/traj_rsavt0jipi3c.json +109 -0
- package/.trajectories/completed/2026-01/traj_rsavt0jipi3c.md +56 -0
- package/.trajectories/completed/2026-01/traj_x721m1j9rzup.json +113 -0
- package/.trajectories/completed/2026-01/traj_x721m1j9rzup.md +57 -0
- package/.trajectories/completed/2026-01/traj_xjqvmep5ed3h.json +61 -0
- package/.trajectories/completed/2026-01/traj_xjqvmep5ed3h.md +36 -0
- package/.trajectories/completed/2026-01/traj_y7n6hfbf7dmg.json +49 -0
- package/.trajectories/completed/2026-01/traj_y7n6hfbf7dmg.md +31 -0
- package/.trajectories/completed/2026-01/traj_yvfkwnkdiso2.json +49 -0
- package/.trajectories/completed/2026-01/traj_yvfkwnkdiso2.md +31 -0
- package/.trajectories/index.json +140 -1
- package/README.md +23 -9
- package/TRAIL_GIT_AUTH_FIX.md +113 -0
- package/deploy/workspace/codex.config.toml +1 -1
- package/deploy/workspace/entrypoint.sh +20 -79
- package/deploy/workspace/gh-relay +156 -0
- package/deploy/workspace/git-credential-relay +5 -1
- package/dist/bridge/multi-project-client.js +13 -10
- package/dist/bridge/spawner.d.ts +2 -0
- package/dist/bridge/spawner.js +58 -76
- package/dist/bridge/types.d.ts +2 -0
- package/dist/cli/index.d.ts +8 -6
- package/dist/cli/index.js +297 -30
- package/dist/cloud/api/admin.js +16 -3
- package/dist/cloud/api/codex-auth-helper.js +28 -8
- package/dist/cloud/api/consensus.d.ts +13 -0
- package/dist/cloud/api/consensus.js +259 -0
- package/dist/cloud/api/daemons.js +205 -1
- package/dist/cloud/api/git.js +37 -7
- package/dist/cloud/api/onboarding.js +4 -1
- package/dist/cloud/api/provider-env.d.ts +5 -0
- package/dist/cloud/api/provider-env.js +27 -0
- package/dist/cloud/api/providers.js +2 -0
- package/dist/cloud/api/test-helpers.js +130 -0
- package/dist/cloud/api/workspaces.js +38 -3
- package/dist/cloud/db/bulk-ingest.d.ts +88 -0
- package/dist/cloud/db/bulk-ingest.js +268 -0
- package/dist/cloud/db/drizzle.d.ts +33 -0
- package/dist/cloud/db/drizzle.js +174 -2
- package/dist/cloud/db/index.d.ts +24 -5
- package/dist/cloud/db/index.js +19 -4
- package/dist/cloud/db/schema.d.ts +397 -3
- package/dist/cloud/db/schema.js +75 -1
- package/dist/cloud/provisioner/index.d.ts +8 -0
- package/dist/cloud/provisioner/index.js +256 -50
- package/dist/cloud/server.js +47 -3
- package/dist/cloud/services/index.d.ts +1 -0
- package/dist/cloud/services/index.js +2 -0
- package/dist/cloud/services/nango.d.ts +3 -4
- package/dist/cloud/services/nango.js +11 -33
- package/dist/cloud/services/workspace-keepalive.d.ts +76 -0
- package/dist/cloud/services/workspace-keepalive.js +234 -0
- package/dist/config/relay-config.d.ts +23 -0
- package/dist/config/relay-config.js +23 -0
- package/dist/daemon/agent-manager.d.ts +20 -1
- package/dist/daemon/agent-manager.js +51 -0
- package/dist/daemon/agent-registry.js +4 -4
- package/dist/daemon/agent-signing.d.ts +158 -0
- package/dist/daemon/agent-signing.js +523 -0
- package/dist/daemon/api.js +18 -1
- package/dist/daemon/cli-auth.d.ts +4 -1
- package/dist/daemon/cli-auth.js +55 -11
- package/dist/daemon/cloud-sync.d.ts +47 -1
- package/dist/daemon/cloud-sync.js +152 -3
- package/dist/daemon/connection.d.ts +28 -0
- package/dist/daemon/connection.js +113 -22
- package/dist/daemon/consensus-integration.d.ts +167 -0
- package/dist/daemon/consensus-integration.js +371 -0
- package/dist/daemon/consensus.d.ts +271 -0
- package/dist/daemon/consensus.js +632 -0
- package/dist/daemon/delivery-tracker.d.ts +34 -0
- package/dist/daemon/delivery-tracker.js +104 -0
- package/dist/daemon/enhanced-features.d.ts +118 -0
- package/dist/daemon/enhanced-features.js +178 -0
- package/dist/daemon/index.d.ts +4 -0
- package/dist/daemon/index.js +5 -0
- package/dist/daemon/rate-limiter.d.ts +68 -0
- package/dist/daemon/rate-limiter.js +130 -0
- package/dist/daemon/router.d.ts +18 -11
- package/dist/daemon/router.js +57 -113
- package/dist/daemon/server.d.ts +13 -1
- package/dist/daemon/server.js +71 -9
- package/dist/daemon/sync-queue.d.ts +116 -0
- package/dist/daemon/sync-queue.js +361 -0
- package/dist/dashboard/out/404.html +1 -1
- package/dist/dashboard/out/_next/static/chunks/116-de2a4ac06e5000dc.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/847-f1f467060f32afff.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/919-87d604a5d76c1fbd.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/app/app/{page-c617745b81344f4f.js → page-7f64824ae7d06707.js} +1 -1
- package/dist/dashboard/out/_next/static/chunks/app/cloud/link/page-3f559d393902aad2.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/app/login/page-16d1715ddaa874ee.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/app/{page-dc786c183425c2ac.js → page-814efc4d77b4191d.js} +1 -1
- package/dist/dashboard/out/_next/static/chunks/{main-2ee6beb2ae96d210.js → main-5a40a5ae29646e1b.js} +1 -1
- package/dist/dashboard/out/_next/static/css/44d2b52637b511bc.css +1 -0
- package/dist/dashboard/out/app/onboarding.html +1 -1
- package/dist/dashboard/out/app/onboarding.txt +1 -1
- package/dist/dashboard/out/app.html +1 -1
- package/dist/dashboard/out/app.txt +2 -2
- package/dist/dashboard/out/cloud/link.html +1 -0
- package/dist/dashboard/out/cloud/link.txt +7 -0
- package/dist/dashboard/out/connect-repos.html +1 -1
- package/dist/dashboard/out/connect-repos.txt +1 -1
- package/dist/dashboard/out/history.html +1 -1
- package/dist/dashboard/out/history.txt +2 -2
- package/dist/dashboard/out/index.html +1 -1
- package/dist/dashboard/out/index.txt +2 -2
- package/dist/dashboard/out/login.html +2 -3
- package/dist/dashboard/out/login.txt +2 -2
- package/dist/dashboard/out/metrics.html +1 -1
- package/dist/dashboard/out/metrics.txt +2 -2
- package/dist/dashboard/out/pricing.html +2 -2
- package/dist/dashboard/out/pricing.txt +1 -1
- package/dist/dashboard/out/providers/setup/claude.html +1 -1
- package/dist/dashboard/out/providers/setup/claude.txt +1 -1
- package/dist/dashboard/out/providers/setup/codex.html +1 -1
- package/dist/dashboard/out/providers/setup/codex.txt +1 -1
- package/dist/dashboard/out/providers.html +1 -1
- package/dist/dashboard/out/providers.txt +1 -1
- package/dist/dashboard/out/signup.html +2 -2
- package/dist/dashboard/out/signup.txt +1 -1
- package/dist/dashboard-server/server.js +244 -28
- package/dist/health-worker-manager.d.ts +62 -0
- package/dist/health-worker-manager.js +144 -0
- package/dist/health-worker.d.ts +9 -0
- package/dist/health-worker.js +79 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +5 -1
- package/dist/memory/context-compaction.d.ts +156 -0
- package/dist/memory/context-compaction.js +453 -0
- package/dist/memory/index.d.ts +1 -0
- package/dist/memory/index.js +1 -0
- package/dist/protocol/channels.js +4 -4
- package/dist/protocol/framing.d.ts +72 -10
- package/dist/protocol/framing.js +194 -25
- package/dist/storage/adapter.d.ts +8 -1
- package/dist/storage/adapter.js +11 -0
- package/dist/storage/batched-sqlite-adapter.d.ts +71 -0
- package/dist/storage/batched-sqlite-adapter.js +183 -0
- package/dist/storage/dead-letter-queue.d.ts +196 -0
- package/dist/storage/dead-letter-queue.js +427 -0
- package/dist/storage/dlq-adapter.d.ts +195 -0
- package/dist/storage/dlq-adapter.js +664 -0
- package/dist/trajectory/config.d.ts +32 -14
- package/dist/trajectory/config.js +38 -16
- package/dist/trajectory/integration.js +217 -64
- package/dist/utils/git-remote.d.ts +47 -0
- package/dist/utils/git-remote.js +125 -0
- package/dist/utils/id-generator.d.ts +35 -0
- package/dist/utils/id-generator.js +60 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/dist/utils/precompiled-patterns.d.ts +110 -0
- package/dist/utils/precompiled-patterns.js +322 -0
- package/dist/wrapper/auth-detection.js +1 -1
- package/dist/wrapper/base-wrapper.d.ts +40 -0
- package/dist/wrapper/base-wrapper.js +60 -6
- package/dist/wrapper/client.d.ts +14 -4
- package/dist/wrapper/client.js +89 -31
- package/dist/wrapper/idle-detector.d.ts +102 -0
- package/dist/wrapper/idle-detector.js +279 -0
- package/dist/wrapper/parser.d.ts +4 -0
- package/dist/wrapper/parser.js +19 -1
- package/dist/wrapper/pty-wrapper.d.ts +14 -2
- package/dist/wrapper/pty-wrapper.js +132 -32
- package/dist/wrapper/shared.d.ts +1 -1
- package/dist/wrapper/shared.js +1 -1
- package/dist/wrapper/tmux-wrapper.d.ts +20 -2
- package/dist/wrapper/tmux-wrapper.js +163 -40
- package/package.json +3 -1
- package/scripts/run-migrations.js +43 -0
- package/scripts/verify-schema.js +134 -0
- package/tests/benchmarks/protocol.bench.ts +310 -0
- package/dist/dashboard/out/_next/static/chunks/116-2502180def231162.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/899-fc02ed79e3de4302.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/login/page-c22d080201cbd9fb.js +0 -1
- package/dist/dashboard/out/_next/static/css/48a8fbe3e659080e.css +0 -1
- /package/dist/dashboard/out/_next/static/{sDcbGRTYLcpPvyTs_rsNb → R-uQOUcOLINtsp6ACeZa9}/_buildManifest.js +0 -0
- /package/dist/dashboard/out/_next/static/{sDcbGRTYLcpPvyTs_rsNb → R-uQOUcOLINtsp6ACeZa9}/_ssgManifest.js +0 -0
|
@@ -1,21 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Trajectory Configuration
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* in the user's home directory instead of the repo.
|
|
4
|
+
* Manages centralized relay configuration for trajectory storage settings.
|
|
5
|
+
* Config is stored in a central location (not per-repo) and applies to all projects.
|
|
7
6
|
*
|
|
8
|
-
*
|
|
9
|
-
* 1.
|
|
10
|
-
* -
|
|
11
|
-
* -
|
|
7
|
+
* ARCHITECTURE:
|
|
8
|
+
* 1. Config location: Centralized, not repo-specific
|
|
9
|
+
* - Primary: AGENT_RELAY_CONFIG_DIR/relay.json (if env var set)
|
|
10
|
+
* - Default: ~/.config/agent-relay/relay.json
|
|
11
|
+
* - Reasoning: Single config applies to all projects; survives repo deletion
|
|
12
12
|
*
|
|
13
|
-
* 2.
|
|
14
|
-
* -
|
|
15
|
-
* -
|
|
13
|
+
* 2. Default behavior: trajectories are OPT-OUT (stored in user home)
|
|
14
|
+
* - Location: ~/.config/agent-relay/trajectories/<project-hash>/
|
|
15
|
+
* - Users can opt-in via central config: storeInRepo: true
|
|
16
16
|
*
|
|
17
|
-
* 3.
|
|
18
|
-
* -
|
|
17
|
+
* 3. Opt-in storage: .trajectories/ directory in repo root
|
|
18
|
+
* - Applied when: User sets storeInRepo: true in central relay.json
|
|
19
|
+
* - Users should add .trajectories/ to .gitignore if private
|
|
20
|
+
*
|
|
21
|
+
* 4. Initialization: When users initialize relay in a repo, prompt to opt-in
|
|
22
|
+
* - Creates entry in ~/.config/agent-relay/relay.json
|
|
23
|
+
* - Does NOT create repo-specific config
|
|
19
24
|
*/
|
|
20
25
|
/**
|
|
21
26
|
* Relay config structure
|
|
@@ -26,16 +31,29 @@ export interface RelayConfig {
|
|
|
26
31
|
/**
|
|
27
32
|
* Whether to store trajectories in the repo (.trajectories/)
|
|
28
33
|
* Default: false (stored in ~/.config/agent-relay/trajectories/)
|
|
34
|
+
*
|
|
35
|
+
* NOTE: When true, this project will track trajectories in git.
|
|
36
|
+
* Users should add .trajectories/ to .gitignore if they prefer
|
|
37
|
+
* not to track trajectories for privacy reasons.
|
|
29
38
|
*/
|
|
30
39
|
storeInRepo?: boolean;
|
|
31
40
|
};
|
|
32
41
|
}
|
|
33
42
|
/**
|
|
34
43
|
* Get the relay config file path
|
|
44
|
+
*
|
|
45
|
+
* Returns the central relay configuration file, not repo-specific.
|
|
46
|
+
* This config is shared across all projects and stored in:
|
|
47
|
+
* - AGENT_RELAY_CONFIG_DIR/relay.json (if AGENT_RELAY_CONFIG_DIR is set)
|
|
48
|
+
* - ~/.config/agent-relay/relay.json (default)
|
|
49
|
+
*
|
|
50
|
+
* NOTE: projectRoot parameter is kept for backwards compatibility but ignored.
|
|
51
|
+
* Config is always loaded from the central location.
|
|
35
52
|
*/
|
|
36
|
-
export declare function getRelayConfigPath(
|
|
53
|
+
export declare function getRelayConfigPath(_projectRoot?: string): string;
|
|
37
54
|
/**
|
|
38
|
-
* Read the relay config from the
|
|
55
|
+
* Read the relay config from the central location
|
|
56
|
+
* Config is shared across all projects
|
|
39
57
|
*/
|
|
40
58
|
export declare function readRelayConfig(projectRoot?: string): RelayConfig;
|
|
41
59
|
/**
|
|
@@ -1,40 +1,62 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Trajectory Configuration
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* in the user's home directory instead of the repo.
|
|
4
|
+
* Manages centralized relay configuration for trajectory storage settings.
|
|
5
|
+
* Config is stored in a central location (not per-repo) and applies to all projects.
|
|
7
6
|
*
|
|
8
|
-
*
|
|
9
|
-
* 1.
|
|
10
|
-
* -
|
|
11
|
-
* -
|
|
7
|
+
* ARCHITECTURE:
|
|
8
|
+
* 1. Config location: Centralized, not repo-specific
|
|
9
|
+
* - Primary: AGENT_RELAY_CONFIG_DIR/relay.json (if env var set)
|
|
10
|
+
* - Default: ~/.config/agent-relay/relay.json
|
|
11
|
+
* - Reasoning: Single config applies to all projects; survives repo deletion
|
|
12
12
|
*
|
|
13
|
-
* 2.
|
|
14
|
-
* -
|
|
15
|
-
* -
|
|
13
|
+
* 2. Default behavior: trajectories are OPT-OUT (stored in user home)
|
|
14
|
+
* - Location: ~/.config/agent-relay/trajectories/<project-hash>/
|
|
15
|
+
* - Users can opt-in via central config: storeInRepo: true
|
|
16
16
|
*
|
|
17
|
-
* 3.
|
|
18
|
-
* -
|
|
17
|
+
* 3. Opt-in storage: .trajectories/ directory in repo root
|
|
18
|
+
* - Applied when: User sets storeInRepo: true in central relay.json
|
|
19
|
+
* - Users should add .trajectories/ to .gitignore if private
|
|
20
|
+
*
|
|
21
|
+
* 4. Initialization: When users initialize relay in a repo, prompt to opt-in
|
|
22
|
+
* - Creates entry in ~/.config/agent-relay/relay.json
|
|
23
|
+
* - Does NOT create repo-specific config
|
|
19
24
|
*/
|
|
20
25
|
import { existsSync, readFileSync, mkdirSync, statSync } from 'node:fs';
|
|
21
26
|
import { join } from 'node:path';
|
|
22
27
|
import { homedir } from 'node:os';
|
|
23
28
|
import { createHash } from 'node:crypto';
|
|
24
29
|
import { getProjectPaths } from '../utils/project-namespace.js';
|
|
30
|
+
/**
|
|
31
|
+
* Get the central agent-relay config directory
|
|
32
|
+
* Uses AGENT_RELAY_CONFIG_DIR environment variable if set,
|
|
33
|
+
* otherwise defaults to ~/.config/agent-relay
|
|
34
|
+
*/
|
|
35
|
+
function getAgentRelayConfigDir() {
|
|
36
|
+
return process.env.AGENT_RELAY_CONFIG_DIR ??
|
|
37
|
+
join(homedir(), '.config', 'agent-relay');
|
|
38
|
+
}
|
|
25
39
|
/**
|
|
26
40
|
* Cache for config to avoid repeated file reads
|
|
27
41
|
*/
|
|
28
42
|
let configCache = null;
|
|
29
43
|
/**
|
|
30
44
|
* Get the relay config file path
|
|
45
|
+
*
|
|
46
|
+
* Returns the central relay configuration file, not repo-specific.
|
|
47
|
+
* This config is shared across all projects and stored in:
|
|
48
|
+
* - AGENT_RELAY_CONFIG_DIR/relay.json (if AGENT_RELAY_CONFIG_DIR is set)
|
|
49
|
+
* - ~/.config/agent-relay/relay.json (default)
|
|
50
|
+
*
|
|
51
|
+
* NOTE: projectRoot parameter is kept for backwards compatibility but ignored.
|
|
52
|
+
* Config is always loaded from the central location.
|
|
31
53
|
*/
|
|
32
|
-
export function getRelayConfigPath(
|
|
33
|
-
|
|
34
|
-
return join(root, '.relay', 'config.json');
|
|
54
|
+
export function getRelayConfigPath(_projectRoot) {
|
|
55
|
+
return join(getAgentRelayConfigDir(), 'relay.json');
|
|
35
56
|
}
|
|
36
57
|
/**
|
|
37
|
-
* Read the relay config from the
|
|
58
|
+
* Read the relay config from the central location
|
|
59
|
+
* Config is shared across all projects
|
|
38
60
|
*/
|
|
39
61
|
export function readRelayConfig(projectRoot) {
|
|
40
62
|
const configPath = getRelayConfigPath(projectRoot);
|
|
@@ -14,13 +14,40 @@
|
|
|
14
14
|
* - Provides hooks for key agent lifecycle events
|
|
15
15
|
*/
|
|
16
16
|
import { spawn, execSync } from 'node:child_process';
|
|
17
|
-
import { readFileSync, existsSync } from 'node:fs';
|
|
17
|
+
import { readFileSync, existsSync, readdirSync } from 'node:fs';
|
|
18
18
|
import { join } from 'node:path';
|
|
19
19
|
import { getProjectPaths } from '../utils/project-namespace.js';
|
|
20
20
|
import { getPrimaryTrajectoriesDir, getAllTrajectoriesDirs, getTrajectoryEnvVars, } from './config.js';
|
|
21
21
|
/**
|
|
22
22
|
* Read a single trajectory index file from a directory
|
|
23
23
|
*/
|
|
24
|
+
function resolveIndexEntryPath(trajectoriesDir, entryPath) {
|
|
25
|
+
if (!entryPath) {
|
|
26
|
+
return entryPath;
|
|
27
|
+
}
|
|
28
|
+
if (existsSync(entryPath)) {
|
|
29
|
+
return entryPath;
|
|
30
|
+
}
|
|
31
|
+
const normalized = entryPath.replace(/\\/g, '/');
|
|
32
|
+
const marker = '/.trajectories/';
|
|
33
|
+
const markerIndex = normalized.indexOf(marker);
|
|
34
|
+
if (markerIndex >= 0) {
|
|
35
|
+
const relative = normalized.slice(markerIndex + marker.length);
|
|
36
|
+
const candidate = join(trajectoriesDir, relative);
|
|
37
|
+
if (existsSync(candidate)) {
|
|
38
|
+
return candidate;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
const subdirMatch = normalized.match(/\/(active|completed)\/.+$/);
|
|
42
|
+
if (subdirMatch) {
|
|
43
|
+
const relative = subdirMatch[0].slice(1);
|
|
44
|
+
const candidate = join(trajectoriesDir, relative);
|
|
45
|
+
if (existsSync(candidate)) {
|
|
46
|
+
return candidate;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return entryPath;
|
|
50
|
+
}
|
|
24
51
|
function readSingleTrajectoryIndex(trajectoriesDir) {
|
|
25
52
|
try {
|
|
26
53
|
const indexPath = join(trajectoriesDir, 'index.json');
|
|
@@ -28,7 +55,15 @@ function readSingleTrajectoryIndex(trajectoriesDir) {
|
|
|
28
55
|
return null;
|
|
29
56
|
}
|
|
30
57
|
const content = readFileSync(indexPath, 'utf-8');
|
|
31
|
-
|
|
58
|
+
const index = JSON.parse(content);
|
|
59
|
+
const trajectories = index.trajectories ?? {};
|
|
60
|
+
for (const [id, entry] of Object.entries(trajectories)) {
|
|
61
|
+
trajectories[id] = {
|
|
62
|
+
...entry,
|
|
63
|
+
path: resolveIndexEntryPath(trajectoriesDir, entry.path),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
return { ...index, trajectories };
|
|
32
67
|
}
|
|
33
68
|
catch {
|
|
34
69
|
return null;
|
|
@@ -91,6 +126,39 @@ function readTrajectoryFile(trajectoryPath) {
|
|
|
91
126
|
return null;
|
|
92
127
|
}
|
|
93
128
|
}
|
|
129
|
+
function getCurrentPhaseFromTrajectory(trajectory) {
|
|
130
|
+
if (!trajectory?.chapters?.length) {
|
|
131
|
+
return undefined;
|
|
132
|
+
}
|
|
133
|
+
const lastChapter = trajectory.chapters[trajectory.chapters.length - 1];
|
|
134
|
+
for (const event of [...(lastChapter.events || [])].reverse()) {
|
|
135
|
+
if (event.type === 'phase_transition' || event.type === 'phase') {
|
|
136
|
+
const phaseMatch = event.content?.match(/phase[:\s]+(\w+)/i);
|
|
137
|
+
if (phaseMatch) {
|
|
138
|
+
return phaseMatch[1].toLowerCase();
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return undefined;
|
|
143
|
+
}
|
|
144
|
+
function listActiveTrajectoryFiles(trajectoriesDir) {
|
|
145
|
+
const activeDir = join(trajectoriesDir, 'active');
|
|
146
|
+
if (!existsSync(activeDir)) {
|
|
147
|
+
return [];
|
|
148
|
+
}
|
|
149
|
+
return readdirSync(activeDir, { withFileTypes: true })
|
|
150
|
+
.filter(entry => entry.isFile() && entry.name.endsWith('.json'))
|
|
151
|
+
.map(entry => join(activeDir, entry.name));
|
|
152
|
+
}
|
|
153
|
+
function findActiveTrajectoryPathById(trajectoryDirs, trajectoryId) {
|
|
154
|
+
for (const dir of trajectoryDirs) {
|
|
155
|
+
const candidate = join(dir, 'active', `${trajectoryId}.json`);
|
|
156
|
+
if (existsSync(candidate)) {
|
|
157
|
+
return candidate;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
94
162
|
/**
|
|
95
163
|
* Run a trail CLI command
|
|
96
164
|
* Uses config-based environment to control trajectory storage location
|
|
@@ -163,33 +231,37 @@ export async function startTrajectory(options) {
|
|
|
163
231
|
*/
|
|
164
232
|
export async function getTrajectoryStatus() {
|
|
165
233
|
const index = readTrajectoryIndex();
|
|
166
|
-
|
|
234
|
+
const trajectoryDirs = getAllTrajectoriesDirs();
|
|
235
|
+
if (!index && trajectoryDirs.length === 0) {
|
|
167
236
|
return { active: false };
|
|
168
237
|
}
|
|
169
238
|
// Find an active trajectory
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
239
|
+
if (index) {
|
|
240
|
+
for (const [id, entry] of Object.entries(index.trajectories)) {
|
|
241
|
+
if (entry.status === 'active') {
|
|
242
|
+
// Read the full trajectory file to get phase info
|
|
243
|
+
const trajectory = readTrajectoryFile(entry.path);
|
|
244
|
+
const currentPhase = getCurrentPhaseFromTrajectory(trajectory);
|
|
245
|
+
return {
|
|
246
|
+
active: true,
|
|
247
|
+
trajectoryId: id,
|
|
248
|
+
phase: currentPhase,
|
|
249
|
+
task: entry.title,
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
for (const dir of trajectoryDirs) {
|
|
255
|
+
for (const activePath of listActiveTrajectoryFiles(dir)) {
|
|
256
|
+
const trajectory = readTrajectoryFile(activePath);
|
|
257
|
+
if (!trajectory) {
|
|
258
|
+
continue;
|
|
187
259
|
}
|
|
188
260
|
return {
|
|
189
261
|
active: true,
|
|
190
|
-
trajectoryId: id,
|
|
191
|
-
phase:
|
|
192
|
-
task:
|
|
262
|
+
trajectoryId: trajectory.id,
|
|
263
|
+
phase: getCurrentPhaseFromTrajectory(trajectory),
|
|
264
|
+
task: trajectory.task?.title,
|
|
193
265
|
};
|
|
194
266
|
}
|
|
195
267
|
}
|
|
@@ -284,51 +356,98 @@ export async function abandonTrajectory(reason) {
|
|
|
284
356
|
* Reads directly from filesystem instead of using CLI
|
|
285
357
|
*/
|
|
286
358
|
export async function listTrajectorySteps(trajectoryId) {
|
|
359
|
+
const trajectoryDirs = getAllTrajectoriesDirs();
|
|
287
360
|
const index = readTrajectoryIndex();
|
|
288
|
-
if (!index) {
|
|
361
|
+
if (!index && trajectoryDirs.length === 0) {
|
|
289
362
|
return { success: true, steps: [] };
|
|
290
363
|
}
|
|
291
|
-
//
|
|
292
|
-
|
|
364
|
+
// Collect all trajectory paths to load
|
|
365
|
+
const trajectoryPaths = new Set();
|
|
293
366
|
if (trajectoryId) {
|
|
294
|
-
// Use specified trajectory
|
|
295
|
-
|
|
367
|
+
// Use specified trajectory - try multiple lookup strategies
|
|
368
|
+
let foundPath = null;
|
|
369
|
+
// Strategy 1: Look up by index key
|
|
370
|
+
const entry = index?.trajectories[trajectoryId];
|
|
296
371
|
if (entry) {
|
|
297
|
-
|
|
372
|
+
foundPath = entry.path;
|
|
373
|
+
console.log('[trajectory] Found by index key:', trajectoryId, '->', foundPath);
|
|
374
|
+
}
|
|
375
|
+
// Strategy 2: If not found by key, search index entries by file's internal ID
|
|
376
|
+
// This handles cases where the index key differs from the file's internal ID
|
|
377
|
+
if (!foundPath && index) {
|
|
378
|
+
for (const [_key, indexEntry] of Object.entries(index.trajectories)) {
|
|
379
|
+
const trajectory = readTrajectoryFile(indexEntry.path);
|
|
380
|
+
if (trajectory && trajectory.id === trajectoryId) {
|
|
381
|
+
foundPath = indexEntry.path;
|
|
382
|
+
console.log('[trajectory] Found by file ID scan:', trajectoryId, '->', foundPath);
|
|
383
|
+
break;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
// Strategy 3: Fall back to active directory filename match
|
|
388
|
+
if (!foundPath) {
|
|
389
|
+
foundPath = findActiveTrajectoryPathById(trajectoryDirs, trajectoryId);
|
|
390
|
+
if (foundPath) {
|
|
391
|
+
console.log('[trajectory] Found by active dir fallback:', trajectoryId, '->', foundPath);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
if (foundPath) {
|
|
395
|
+
trajectoryPaths.add(foundPath);
|
|
396
|
+
}
|
|
397
|
+
else {
|
|
398
|
+
console.log('[trajectory] Not found:', trajectoryId);
|
|
298
399
|
}
|
|
299
400
|
}
|
|
300
401
|
else {
|
|
301
|
-
//
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
402
|
+
// Collect ALL active trajectories (not just the first one)
|
|
403
|
+
if (index) {
|
|
404
|
+
for (const [_id, entry] of Object.entries(index.trajectories)) {
|
|
405
|
+
if (entry.status === 'active') {
|
|
406
|
+
trajectoryPaths.add(entry.path);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
for (const dir of trajectoryDirs) {
|
|
411
|
+
for (const activePath of listActiveTrajectoryFiles(dir)) {
|
|
412
|
+
trajectoryPaths.add(activePath);
|
|
306
413
|
}
|
|
307
414
|
}
|
|
308
415
|
}
|
|
309
|
-
if (
|
|
310
|
-
return { success: true, steps: [] };
|
|
311
|
-
}
|
|
312
|
-
const trajectory = readTrajectoryFile(trajectoryPath);
|
|
313
|
-
if (!trajectory) {
|
|
416
|
+
if (trajectoryPaths.size === 0) {
|
|
314
417
|
return { success: true, steps: [] };
|
|
315
418
|
}
|
|
316
|
-
//
|
|
419
|
+
// Load all trajectories and merge their steps
|
|
317
420
|
const steps = [];
|
|
318
421
|
let stepIndex = 0;
|
|
319
|
-
for (const
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
422
|
+
for (const trajectoryPath of trajectoryPaths) {
|
|
423
|
+
const trajectory = readTrajectoryFile(trajectoryPath);
|
|
424
|
+
if (!trajectory) {
|
|
425
|
+
continue;
|
|
426
|
+
}
|
|
427
|
+
// Extract events from all chapters
|
|
428
|
+
// Include trajectory ID in step IDs to ensure uniqueness across trajectories
|
|
429
|
+
// This prevents React key collisions when switching between trajectories
|
|
430
|
+
const trajId = trajectory.id || 'unknown';
|
|
431
|
+
for (const chapter of trajectory.chapters || []) {
|
|
432
|
+
for (const event of chapter.events || []) {
|
|
433
|
+
steps.push({
|
|
434
|
+
id: `${trajId}-step-${stepIndex++}`,
|
|
435
|
+
timestamp: event.ts || Date.now(),
|
|
436
|
+
type: mapEventType(event.type),
|
|
437
|
+
title: event.content?.slice(0, 50) || event.type || 'Event',
|
|
438
|
+
description: event.content,
|
|
439
|
+
metadata: event.raw,
|
|
440
|
+
status: mapEventStatus(trajectory.status),
|
|
441
|
+
});
|
|
442
|
+
}
|
|
330
443
|
}
|
|
331
444
|
}
|
|
445
|
+
// Sort steps by timestamp to maintain chronological order across all trajectories
|
|
446
|
+
steps.sort((a, b) => {
|
|
447
|
+
const timeA = typeof a.timestamp === 'number' ? a.timestamp : new Date(a.timestamp).getTime();
|
|
448
|
+
const timeB = typeof b.timestamp === 'number' ? b.timestamp : new Date(b.timestamp).getTime();
|
|
449
|
+
return timeA - timeB;
|
|
450
|
+
});
|
|
332
451
|
return { success: true, steps };
|
|
333
452
|
}
|
|
334
453
|
/**
|
|
@@ -336,22 +455,33 @@ export async function listTrajectorySteps(trajectoryId) {
|
|
|
336
455
|
* Reads directly from filesystem
|
|
337
456
|
*/
|
|
338
457
|
export async function getTrajectoryHistory() {
|
|
458
|
+
const trajectoryDirs = getAllTrajectoriesDirs();
|
|
339
459
|
const index = readTrajectoryIndex();
|
|
340
|
-
if (!index) {
|
|
460
|
+
if (!index && trajectoryDirs.length === 0) {
|
|
341
461
|
return { success: true, trajectories: [] };
|
|
342
462
|
}
|
|
343
463
|
const trajectories = [];
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
464
|
+
const seenIds = new Set();
|
|
465
|
+
if (index) {
|
|
466
|
+
for (const [indexKey, entry] of Object.entries(index.trajectories)) {
|
|
467
|
+
// Read the trajectory file to get the actual internal ID
|
|
468
|
+
// This ensures consistency between history list IDs and file content
|
|
469
|
+
const trajectory = entry.path ? readTrajectoryFile(entry.path) : null;
|
|
470
|
+
// Use the file's internal ID if available, otherwise fall back to index key
|
|
471
|
+
// This fixes the mismatch where index key differs from file's internal ID
|
|
472
|
+
const actualId = trajectory?.id || indexKey;
|
|
473
|
+
// Skip if we've already seen this ID (from a previous directory's index)
|
|
474
|
+
if (seenIds.has(actualId)) {
|
|
475
|
+
continue;
|
|
476
|
+
}
|
|
477
|
+
const historyEntry = {
|
|
478
|
+
id: actualId,
|
|
479
|
+
title: trajectory?.task?.title || entry.title,
|
|
480
|
+
status: entry.status,
|
|
481
|
+
startedAt: trajectory?.startedAt || entry.startedAt,
|
|
482
|
+
completedAt: trajectory?.completedAt || entry.completedAt,
|
|
483
|
+
};
|
|
484
|
+
// Add additional details from trajectory file
|
|
355
485
|
if (trajectory) {
|
|
356
486
|
historyEntry.agents = trajectory.agents?.map(a => a.name);
|
|
357
487
|
if (trajectory.retrospective) {
|
|
@@ -359,8 +489,31 @@ export async function getTrajectoryHistory() {
|
|
|
359
489
|
historyEntry.confidence = trajectory.retrospective.confidence;
|
|
360
490
|
}
|
|
361
491
|
}
|
|
492
|
+
trajectories.push(historyEntry);
|
|
493
|
+
seenIds.add(actualId);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
for (const dir of trajectoryDirs) {
|
|
497
|
+
for (const activePath of listActiveTrajectoryFiles(dir)) {
|
|
498
|
+
const trajectory = readTrajectoryFile(activePath);
|
|
499
|
+
if (!trajectory || seenIds.has(trajectory.id)) {
|
|
500
|
+
continue;
|
|
501
|
+
}
|
|
502
|
+
const historyEntry = {
|
|
503
|
+
id: trajectory.id,
|
|
504
|
+
title: trajectory.task?.title || 'Untitled trajectory',
|
|
505
|
+
status: trajectory.status ?? 'active',
|
|
506
|
+
startedAt: trajectory.startedAt,
|
|
507
|
+
completedAt: trajectory.completedAt,
|
|
508
|
+
agents: trajectory.agents?.map(a => a.name),
|
|
509
|
+
};
|
|
510
|
+
if (trajectory.retrospective) {
|
|
511
|
+
historyEntry.summary = trajectory.retrospective.summary;
|
|
512
|
+
historyEntry.confidence = trajectory.retrospective.confidence;
|
|
513
|
+
}
|
|
514
|
+
trajectories.push(historyEntry);
|
|
515
|
+
seenIds.add(trajectory.id);
|
|
362
516
|
}
|
|
363
|
-
trajectories.push(historyEntry);
|
|
364
517
|
}
|
|
365
518
|
// Sort by startedAt descending (most recent first)
|
|
366
519
|
trajectories.sort((a, b) => new Date(b.startedAt).getTime() - new Date(a.startedAt).getTime());
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Git Remote Detection Utility
|
|
3
|
+
*
|
|
4
|
+
* Detects the git remote URL from a working directory and parses it
|
|
5
|
+
* to extract the repository full name (owner/repo).
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Parse a git remote URL to extract owner/repo format.
|
|
9
|
+
*
|
|
10
|
+
* Supports:
|
|
11
|
+
* - git@github.com:owner/repo.git
|
|
12
|
+
* - https://github.com/owner/repo.git
|
|
13
|
+
* - https://github.com/owner/repo
|
|
14
|
+
* - git://github.com/owner/repo.git
|
|
15
|
+
*/
|
|
16
|
+
export declare function parseGitRemoteUrl(url: string): string | null;
|
|
17
|
+
/**
|
|
18
|
+
* Get the git remote URL from a directory.
|
|
19
|
+
*
|
|
20
|
+
* @param workingDirectory The directory to check for git remote
|
|
21
|
+
* @param remoteName The remote name to use (default: 'origin')
|
|
22
|
+
* @returns The remote URL or null if not found
|
|
23
|
+
*/
|
|
24
|
+
export declare function getGitRemoteUrl(workingDirectory: string, remoteName?: string): string | null;
|
|
25
|
+
/**
|
|
26
|
+
* Get the repository full name (owner/repo) from a working directory.
|
|
27
|
+
*
|
|
28
|
+
* @param workingDirectory The directory to check
|
|
29
|
+
* @returns The repo full name (e.g., "AgentWorkforce/relay") or null
|
|
30
|
+
*/
|
|
31
|
+
export declare function getRepoFullName(workingDirectory: string): string | null;
|
|
32
|
+
/**
|
|
33
|
+
* Find the git root directory from a given path.
|
|
34
|
+
* Walks up the directory tree looking for .git folder.
|
|
35
|
+
*
|
|
36
|
+
* @param startPath The path to start searching from
|
|
37
|
+
* @returns The git root directory or null if not in a git repo
|
|
38
|
+
*/
|
|
39
|
+
export declare function findGitRoot(startPath: string): string | null;
|
|
40
|
+
/**
|
|
41
|
+
* Get repository full name, walking up to find git root if needed.
|
|
42
|
+
*
|
|
43
|
+
* @param workingDirectory The directory to start from
|
|
44
|
+
* @returns The repo full name or null
|
|
45
|
+
*/
|
|
46
|
+
export declare function getRepoFullNameFromPath(workingDirectory: string): string | null;
|
|
47
|
+
//# sourceMappingURL=git-remote.d.ts.map
|