@imdeadpool/guardex 7.0.23 → 7.0.25
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/README.md +30 -3
- package/package.json +1 -1
- package/src/cli/args.js +9 -0
- package/src/cli/main.js +7 -2
- package/src/context.js +22 -0
- package/src/doctor/index.js +158 -5
- package/src/finish/index.js +1 -0
- package/src/report/session-severity.js +77 -8
- package/templates/AGENTS.multiagent-safety.md +3 -0
- package/templates/codex/skills/guardex-merge-skills-to-dev/SKILL.md +3 -2
- package/templates/scripts/agent-branch-finish.sh +155 -5
- package/templates/scripts/agent-branch-start.sh +35 -0
- package/templates/scripts/agent-worktree-prune.sh +22 -1
- package/templates/vscode/guardex-active-agents/README.md +6 -3
- package/templates/vscode/guardex-active-agents/extension.js +1706 -247
- package/templates/vscode/guardex-active-agents/fileicons/gitguardex-fileicons.json +54 -0
- package/templates/vscode/guardex-active-agents/fileicons/icons/agent.svg +4 -0
- package/templates/vscode/guardex-active-agents/fileicons/icons/branch.svg +4 -0
- package/templates/vscode/guardex-active-agents/fileicons/icons/config.svg +4 -0
- package/templates/vscode/guardex-active-agents/fileicons/icons/hook.svg +3 -0
- package/templates/vscode/guardex-active-agents/fileicons/icons/openspec.svg +5 -0
- package/templates/vscode/guardex-active-agents/fileicons/icons/plan.svg +4 -0
- package/templates/vscode/guardex-active-agents/fileicons/icons/spec.svg +4 -0
- package/templates/vscode/guardex-active-agents/media/active-agents-hivemind.svg +14 -0
- package/templates/vscode/guardex-active-agents/package.json +39 -11
- package/templates/vscode/guardex-active-agents/session-schema.js +226 -8
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"iconDefinitions": {
|
|
3
|
+
"_gitguardex_agent": {
|
|
4
|
+
"iconPath": "./icons/agent.svg"
|
|
5
|
+
},
|
|
6
|
+
"_gitguardex_branch": {
|
|
7
|
+
"iconPath": "./icons/branch.svg"
|
|
8
|
+
},
|
|
9
|
+
"_gitguardex_config": {
|
|
10
|
+
"iconPath": "./icons/config.svg"
|
|
11
|
+
},
|
|
12
|
+
"_gitguardex_hook": {
|
|
13
|
+
"iconPath": "./icons/hook.svg"
|
|
14
|
+
},
|
|
15
|
+
"_gitguardex_openspec": {
|
|
16
|
+
"iconPath": "./icons/openspec.svg"
|
|
17
|
+
},
|
|
18
|
+
"_gitguardex_plan": {
|
|
19
|
+
"iconPath": "./icons/plan.svg"
|
|
20
|
+
},
|
|
21
|
+
"_gitguardex_spec": {
|
|
22
|
+
"iconPath": "./icons/spec.svg"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"folderNames": {
|
|
26
|
+
".agents": "_gitguardex_agent",
|
|
27
|
+
".githooks": "_gitguardex_hook",
|
|
28
|
+
".omc": "_gitguardex_agent",
|
|
29
|
+
".omx": "_gitguardex_agent",
|
|
30
|
+
"agent-worktrees": "_gitguardex_branch",
|
|
31
|
+
"changes": "_gitguardex_openspec",
|
|
32
|
+
"plan": "_gitguardex_plan",
|
|
33
|
+
"rules": "_gitguardex_spec",
|
|
34
|
+
"specs": "_gitguardex_spec"
|
|
35
|
+
},
|
|
36
|
+
"fileNames": {
|
|
37
|
+
".openspec.yaml": "_gitguardex_config",
|
|
38
|
+
"AGENT.lock": "_gitguardex_agent",
|
|
39
|
+
"AGENTS.md": "_gitguardex_agent",
|
|
40
|
+
"CLAUDE.md": "_gitguardex_agent",
|
|
41
|
+
"config.yaml": "_gitguardex_config",
|
|
42
|
+
"context-docs-cue.md": "_gitguardex_spec",
|
|
43
|
+
"post-checkout": "_gitguardex_hook",
|
|
44
|
+
"pre-commit": "_gitguardex_hook",
|
|
45
|
+
"pre-push": "_gitguardex_hook",
|
|
46
|
+
"proposal.md": "_gitguardex_openspec",
|
|
47
|
+
"spec.md": "_gitguardex_spec",
|
|
48
|
+
"tasks.md": "_gitguardex_plan",
|
|
49
|
+
"plan.md": "_gitguardex_plan"
|
|
50
|
+
},
|
|
51
|
+
"fileExtensions": {
|
|
52
|
+
"openspec.yaml": "_gitguardex_config"
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
|
2
|
+
<path fill="#89d185" d="M4 3.5a2 2 0 1 1 2.5 1.94v3.12A4 4 0 0 0 10 4.6V3h1.5v1.6a5.5 5.5 0 0 1-5 5.47v.49A2 2 0 1 1 4 12.5a2 2 0 0 1 1-1.73V5.23a2 2 0 0 1-1-1.73Zm2-.5a.5.5 0 1 0 0 1 .5.5 0 0 0 0-1Zm0 9a.5.5 0 1 0 0 1 .5.5 0 0 0 0-1Z"/>
|
|
3
|
+
<path fill="#4ec9b0" d="M10.5 1.5H14V5h-3.5V1.5Z"/>
|
|
4
|
+
</svg>
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
|
2
|
+
<path fill="#c586c0" d="M7.2 1h1.6l.4 1.7a5.4 5.4 0 0 1 1.2.5l1.5-.9 1.1 1.1-.9 1.5c.2.4.4.8.5 1.2l1.7.4v1.6l-1.7.4c-.1.4-.3.8-.5 1.2l.9 1.5-1.1 1.1-1.5-.9c-.4.2-.8.4-1.2.5L8.8 15H7.2l-.4-1.7a5.4 5.4 0 0 1-1.2-.5l-1.5.9L3 12.6l.9-1.5a5.4 5.4 0 0 1-.5-1.2l-1.7-.4V7.9l1.7-.4c.1-.4.3-.8.5-1.2L3 4.8l1.1-1.1 1.5.9c.4-.2.8-.4 1.2-.5L7.2 1Z"/>
|
|
3
|
+
<circle cx="8" cy="8" r="2.2" fill="#1e1e1e"/>
|
|
4
|
+
</svg>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path
|
|
3
|
+
fill="currentColor"
|
|
4
|
+
fill-rule="evenodd"
|
|
5
|
+
clip-rule="evenodd"
|
|
6
|
+
d="M12 4.25C9.96 4.25 8.31 5.9 8.31 7.94C8.31 9.32 9.08 10.53 10.22 11.16V12.55C8.15 13.19 6.69 15.12 6.69 17.29V18.31C6.69 19.02 7.27 19.6 7.98 19.6H9.06V20.46C9.06 20.89 9.41 21.25 9.85 21.25H14.15C14.59 21.25 14.94 20.89 14.94 20.46V19.6H16.02C16.73 19.6 17.31 19.02 17.31 18.31V17.29C17.31 15.12 15.85 13.19 13.78 12.55V11.16C14.92 10.53 15.69 9.32 15.69 7.94C15.69 5.9 14.04 4.25 12 4.25Z"
|
|
7
|
+
/>
|
|
8
|
+
<path
|
|
9
|
+
d="M9.88 19.6H14.12"
|
|
10
|
+
stroke="currentColor"
|
|
11
|
+
stroke-width="1.4"
|
|
12
|
+
stroke-linecap="round"
|
|
13
|
+
/>
|
|
14
|
+
</svg>
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gitguardex-active-agents",
|
|
3
3
|
"displayName": "GitGuardex Active Agents",
|
|
4
|
-
"description": "Shows live Guardex sandbox sessions and repo changes
|
|
4
|
+
"description": "Shows live Guardex sandbox sessions and repo changes in a dedicated VS Code Active Agents sidebar.",
|
|
5
5
|
"publisher": "recodeee",
|
|
6
|
-
"version": "0.0.
|
|
6
|
+
"version": "0.0.17",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"icon": "icon.png",
|
|
9
9
|
"engines": {
|
|
@@ -31,6 +31,11 @@
|
|
|
31
31
|
"command": "gitguardex.activeAgents.refresh",
|
|
32
32
|
"title": "Refresh Active Agents"
|
|
33
33
|
},
|
|
34
|
+
{
|
|
35
|
+
"command": "gitguardex.activeAgents.restart",
|
|
36
|
+
"title": "Restart Active Agents",
|
|
37
|
+
"icon": "$(debug-restart)"
|
|
38
|
+
},
|
|
34
39
|
{
|
|
35
40
|
"command": "gitguardex.activeAgents.commitSelectedSession",
|
|
36
41
|
"title": "Commit Selected Session",
|
|
@@ -61,16 +66,27 @@
|
|
|
61
66
|
"icon": "$(debug-stop)"
|
|
62
67
|
},
|
|
63
68
|
{
|
|
64
|
-
"command": "gitguardex.activeAgents.
|
|
65
|
-
"title": "
|
|
66
|
-
"icon": "$(
|
|
69
|
+
"command": "gitguardex.activeAgents.showSessionTerminal",
|
|
70
|
+
"title": "Show Terminal",
|
|
71
|
+
"icon": "$(terminal)"
|
|
67
72
|
}
|
|
68
73
|
],
|
|
74
|
+
"viewsContainers": {
|
|
75
|
+
"activitybar": [
|
|
76
|
+
{
|
|
77
|
+
"id": "gitguardex.activeAgentsContainer",
|
|
78
|
+
"title": "Active Agents",
|
|
79
|
+
"icon": "media/active-agents-hivemind.svg"
|
|
80
|
+
}
|
|
81
|
+
]
|
|
82
|
+
},
|
|
69
83
|
"views": {
|
|
70
|
-
"
|
|
84
|
+
"gitguardex.activeAgentsContainer": [
|
|
71
85
|
{
|
|
72
86
|
"id": "gitguardex.activeAgents",
|
|
73
87
|
"name": "Active Agents",
|
|
88
|
+
"contextualTitle": "Active Agents",
|
|
89
|
+
"icon": "media/active-agents-hivemind.svg",
|
|
74
90
|
"visibility": "visible"
|
|
75
91
|
}
|
|
76
92
|
]
|
|
@@ -78,7 +94,7 @@
|
|
|
78
94
|
"viewsWelcome": [
|
|
79
95
|
{
|
|
80
96
|
"view": "gitguardex.activeAgents",
|
|
81
|
-
"contents": "No live Guardex agents are visible in this workspace yet.\n\nThis
|
|
97
|
+
"contents": "No live Guardex agents are visible in this workspace yet.\n\nThis sidebar tracks Guardex session files and managed worktree telemetry without taking over Source Control.\n\n[Start agent](command:gitguardex.activeAgents.startAgent)\n[Open guide](https://github.com/recodeee/gitguardex/blob/main/vscode/guardex-active-agents/README.md#quick-start)\n[Refresh](command:gitguardex.activeAgents.refresh)"
|
|
82
98
|
}
|
|
83
99
|
],
|
|
84
100
|
"menus": {
|
|
@@ -88,12 +104,24 @@
|
|
|
88
104
|
"when": "view == gitguardex.activeAgents && guardex.hasAgents",
|
|
89
105
|
"group": "navigation@1"
|
|
90
106
|
},
|
|
107
|
+
{
|
|
108
|
+
"command": "gitguardex.activeAgents.restart",
|
|
109
|
+
"when": "view == gitguardex.activeAgents",
|
|
110
|
+
"group": "navigation@8"
|
|
111
|
+
},
|
|
91
112
|
{
|
|
92
113
|
"command": "gitguardex.activeAgents.refresh",
|
|
93
114
|
"when": "view == gitguardex.activeAgents",
|
|
94
115
|
"group": "navigation@9"
|
|
95
116
|
}
|
|
96
117
|
],
|
|
118
|
+
"extension/context": [
|
|
119
|
+
{
|
|
120
|
+
"command": "gitguardex.activeAgents.restart",
|
|
121
|
+
"when": "extension == recodeee.gitguardex-active-agents && extensionStatus == installed",
|
|
122
|
+
"group": "2_configure@2"
|
|
123
|
+
}
|
|
124
|
+
],
|
|
97
125
|
"view/item/context": [
|
|
98
126
|
{
|
|
99
127
|
"command": "gitguardex.activeAgents.openWorktree",
|
|
@@ -106,22 +134,22 @@
|
|
|
106
134
|
"group": "inline"
|
|
107
135
|
},
|
|
108
136
|
{
|
|
109
|
-
"command": "gitguardex.activeAgents.
|
|
137
|
+
"command": "gitguardex.activeAgents.showSessionTerminal",
|
|
110
138
|
"when": "view == gitguardex.activeAgents && viewItem == gitguardex.session",
|
|
111
139
|
"group": "inline"
|
|
112
140
|
},
|
|
113
141
|
{
|
|
114
|
-
"command": "gitguardex.activeAgents.
|
|
142
|
+
"command": "gitguardex.activeAgents.finishSession",
|
|
115
143
|
"when": "view == gitguardex.activeAgents && viewItem == gitguardex.session",
|
|
116
144
|
"group": "inline"
|
|
117
145
|
},
|
|
118
146
|
{
|
|
119
|
-
"command": "gitguardex.activeAgents.
|
|
147
|
+
"command": "gitguardex.activeAgents.syncSession",
|
|
120
148
|
"when": "view == gitguardex.activeAgents && viewItem == gitguardex.session",
|
|
121
149
|
"group": "inline"
|
|
122
150
|
},
|
|
123
151
|
{
|
|
124
|
-
"command": "gitguardex.activeAgents.
|
|
152
|
+
"command": "gitguardex.activeAgents.stopSession",
|
|
125
153
|
"when": "view == gitguardex.activeAgents && viewItem == gitguardex.session",
|
|
126
154
|
"group": "inline"
|
|
127
155
|
}
|
|
@@ -76,6 +76,46 @@ function toPositiveInteger(value) {
|
|
|
76
76
|
return Number.isInteger(normalized) && normalized > 0 ? normalized : null;
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
+
function toBoundedInteger(value, min, max) {
|
|
80
|
+
const normalized = Number.parseInt(String(value ?? ''), 10);
|
|
81
|
+
if (!Number.isInteger(normalized) || normalized < min || normalized > max) {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
return normalized;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function normalizeStringList(values) {
|
|
88
|
+
if (!Array.isArray(values)) {
|
|
89
|
+
return [];
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return values
|
|
93
|
+
.map((value) => toNonEmptyString(value))
|
|
94
|
+
.filter(Boolean);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function normalizeSessionHealthPayload(input) {
|
|
98
|
+
if (!input || typeof input !== 'object' || Array.isArray(input)) {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const rawScores = input.scores && typeof input.scores === 'object' && !Array.isArray(input.scores)
|
|
103
|
+
? input.scores
|
|
104
|
+
: null;
|
|
105
|
+
const score = toBoundedInteger(input.score ?? input.total ?? rawScores?.total, 0, 100);
|
|
106
|
+
if (score === null) {
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return {
|
|
111
|
+
score,
|
|
112
|
+
label: toNonEmptyString(input.label),
|
|
113
|
+
primaryDriver: toNonEmptyString(input.primaryDriver),
|
|
114
|
+
secondaries: normalizeStringList(input.secondaries),
|
|
115
|
+
outputLine: toNonEmptyString(input.outputLine),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
79
119
|
function normalizeTaskMode(value) {
|
|
80
120
|
const normalized = toNonEmptyString(value).toLowerCase();
|
|
81
121
|
return normalized === 'caveman' || normalized === 'omx' ? normalized : '';
|
|
@@ -126,6 +166,17 @@ function normalizeRelativePath(value) {
|
|
|
126
166
|
return toNonEmptyString(value).replace(/\\/g, '/').replace(/^\.\//, '');
|
|
127
167
|
}
|
|
128
168
|
|
|
169
|
+
function normalizeProjectPath(value) {
|
|
170
|
+
const normalized = toNonEmptyString(value);
|
|
171
|
+
if (!normalized) {
|
|
172
|
+
return '';
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return path.isAbsolute(normalized)
|
|
176
|
+
? path.resolve(normalized)
|
|
177
|
+
: normalizeRelativePath(normalized);
|
|
178
|
+
}
|
|
179
|
+
|
|
129
180
|
function readJsonFile(filePath) {
|
|
130
181
|
try {
|
|
131
182
|
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
@@ -742,8 +793,12 @@ function buildSessionRecord(input) {
|
|
|
742
793
|
repoRoot,
|
|
743
794
|
branch,
|
|
744
795
|
taskName: toNonEmptyString(input.taskName, 'task'),
|
|
745
|
-
latestTaskPreview:
|
|
796
|
+
latestTaskPreview: toNonEmptyString(input.latestTaskPreview),
|
|
746
797
|
agentName: toNonEmptyString(input.agentName, 'agent'),
|
|
798
|
+
projectName: toNonEmptyString(input.projectName),
|
|
799
|
+
projectPath: normalizeProjectPath(input.projectPath),
|
|
800
|
+
snapshotName: toNonEmptyString(input.snapshotName),
|
|
801
|
+
snapshotEmail: toNonEmptyString(input.snapshotEmail || input.email),
|
|
747
802
|
worktreePath,
|
|
748
803
|
pid,
|
|
749
804
|
cliName: toNonEmptyString(input.cliName, 'codex'),
|
|
@@ -753,6 +808,7 @@ function buildSessionRecord(input) {
|
|
|
753
808
|
startedAt: startedAt.toISOString(),
|
|
754
809
|
lastHeartbeatAt: lastHeartbeatAt.toISOString(),
|
|
755
810
|
state: normalizeAdvisoryState(input.state),
|
|
811
|
+
sessionHealth: normalizeSessionHealthPayload(input.sessionHealth || input.sessionSeverity),
|
|
756
812
|
};
|
|
757
813
|
}
|
|
758
814
|
|
|
@@ -792,8 +848,12 @@ function normalizeSessionRecord(input, options = {}) {
|
|
|
792
848
|
repoRoot: path.resolve(repoRoot),
|
|
793
849
|
branch,
|
|
794
850
|
taskName: toNonEmptyString(input.taskName, 'task'),
|
|
795
|
-
latestTaskPreview:
|
|
851
|
+
latestTaskPreview: toNonEmptyString(input.latestTaskPreview),
|
|
796
852
|
agentName: toNonEmptyString(input.agentName, 'agent'),
|
|
853
|
+
projectName: toNonEmptyString(input.projectName),
|
|
854
|
+
projectPath: normalizeProjectPath(input.projectPath),
|
|
855
|
+
snapshotName: toNonEmptyString(input.snapshotName),
|
|
856
|
+
snapshotEmail: toNonEmptyString(input.snapshotEmail || input.email),
|
|
797
857
|
worktreePath: path.resolve(worktreePath),
|
|
798
858
|
pid,
|
|
799
859
|
cliName: toNonEmptyString(input.cliName, 'codex'),
|
|
@@ -813,6 +873,7 @@ function normalizeSessionRecord(input, options = {}) {
|
|
|
813
873
|
lockSnapshotCount: 0,
|
|
814
874
|
lockSessionCount: 0,
|
|
815
875
|
collaboration: false,
|
|
876
|
+
sessionHealth: normalizeSessionHealthPayload(input.sessionHealth || input.sessionSeverity),
|
|
816
877
|
};
|
|
817
878
|
}
|
|
818
879
|
|
|
@@ -867,6 +928,23 @@ function deriveAgentNameFromBranch(branch) {
|
|
|
867
928
|
return 'agent';
|
|
868
929
|
}
|
|
869
930
|
|
|
931
|
+
function isManagedAgentBranch(branch) {
|
|
932
|
+
return toNonEmptyString(branch).startsWith('agent/');
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
function deriveManagedWorktreeStartedAt(worktreePath, now = Date.now()) {
|
|
936
|
+
try {
|
|
937
|
+
const stats = fs.statSync(worktreePath);
|
|
938
|
+
if (Number.isFinite(stats.mtimeMs)) {
|
|
939
|
+
return new Date(stats.mtimeMs).toISOString();
|
|
940
|
+
}
|
|
941
|
+
} catch (_error) {
|
|
942
|
+
// Directory mtime is best-effort context only; fall back to current scan time.
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
return new Date(now).toISOString();
|
|
946
|
+
}
|
|
947
|
+
|
|
870
948
|
function flattenTelemetrySnapshotSessions(lockPayload) {
|
|
871
949
|
const flattened = [];
|
|
872
950
|
const snapshots = Array.isArray(lockPayload?.snapshots) ? lockPayload.snapshots : [];
|
|
@@ -880,6 +958,9 @@ function flattenTelemetrySnapshotSessions(lockPayload) {
|
|
|
880
958
|
projectPath: toNonEmptyString(session?.projectPath),
|
|
881
959
|
snapshotName: toNonEmptyString(snapshot?.snapshotName),
|
|
882
960
|
email: toNonEmptyString(snapshot?.email),
|
|
961
|
+
sessionHealth: normalizeSessionHealthPayload(
|
|
962
|
+
session?.sessionHealth || session?.sessionSeverity || snapshot?.sessionHealth || snapshot?.sessionSeverity,
|
|
963
|
+
),
|
|
883
964
|
});
|
|
884
965
|
}
|
|
885
966
|
}
|
|
@@ -898,7 +979,19 @@ function sortSessionsByTimestamp(sessions) {
|
|
|
898
979
|
}
|
|
899
980
|
|
|
900
981
|
function deriveLockTaskAnchor(entries, fallbackTaskName, fallbackTimestamp) {
|
|
901
|
-
const sortedEntries =
|
|
982
|
+
const sortedEntries = sortTelemetryEntriesForAnchor(entries);
|
|
983
|
+
|
|
984
|
+
const latestEntry = sortedEntries[0] || null;
|
|
985
|
+
return {
|
|
986
|
+
taskName: latestEntry?.taskPreview || fallbackTaskName || 'task',
|
|
987
|
+
latestTaskPreview: latestEntry?.taskPreview || '',
|
|
988
|
+
timestamp: latestEntry?.taskUpdatedAt || fallbackTimestamp || '',
|
|
989
|
+
sessionHealth: latestEntry?.sessionHealth || null,
|
|
990
|
+
};
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
function sortTelemetryEntriesForAnchor(entries) {
|
|
994
|
+
return [...entries].sort((left, right) => {
|
|
902
995
|
const timeDelta = Date.parse(right.taskUpdatedAt || '') - Date.parse(left.taskUpdatedAt || '');
|
|
903
996
|
if (timeDelta !== 0) {
|
|
904
997
|
return timeDelta;
|
|
@@ -908,12 +1001,23 @@ function deriveLockTaskAnchor(entries, fallbackTaskName, fallbackTimestamp) {
|
|
|
908
1001
|
}
|
|
909
1002
|
return (right.projectPath || '').localeCompare(left.projectPath || '');
|
|
910
1003
|
});
|
|
1004
|
+
}
|
|
911
1005
|
|
|
912
|
-
|
|
1006
|
+
function deriveLockSnapshotIdentity(entries) {
|
|
1007
|
+
const latestEntry = sortTelemetryEntriesForAnchor(entries)
|
|
1008
|
+
.find((entry) => entry?.snapshotName || entry?.email) || null;
|
|
913
1009
|
return {
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
1010
|
+
snapshotName: toNonEmptyString(latestEntry?.snapshotName),
|
|
1011
|
+
snapshotEmail: toNonEmptyString(latestEntry?.email),
|
|
1012
|
+
};
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
function deriveLockProjectMetadata(entries) {
|
|
1016
|
+
const latestEntry = sortTelemetryEntriesForAnchor(entries)
|
|
1017
|
+
.find((entry) => entry?.projectPath || entry?.projectName) || null;
|
|
1018
|
+
return {
|
|
1019
|
+
projectName: toNonEmptyString(latestEntry?.projectName),
|
|
1020
|
+
projectPath: normalizeProjectPath(latestEntry?.projectPath),
|
|
917
1021
|
};
|
|
918
1022
|
}
|
|
919
1023
|
|
|
@@ -927,6 +1031,8 @@ function buildWorktreeLockSession(repoRoot, worktreePath, lockPayload, options =
|
|
|
927
1031
|
: `agent/telemetry/${path.basename(worktreePath)}`;
|
|
928
1032
|
const label = deriveSessionLabel(effectiveBranch, worktreePath);
|
|
929
1033
|
const taskAnchor = deriveLockTaskAnchor(telemetryEntries, label, telemetryUpdatedAt);
|
|
1034
|
+
const snapshotIdentity = deriveLockSnapshotIdentity(telemetryEntries);
|
|
1035
|
+
const projectMetadata = deriveLockProjectMetadata(telemetryEntries);
|
|
930
1036
|
const startedAt = taskAnchor.timestamp || telemetryUpdatedAt || new Date(now).toISOString();
|
|
931
1037
|
|
|
932
1038
|
const session = {
|
|
@@ -936,6 +1042,10 @@ function buildWorktreeLockSession(repoRoot, worktreePath, lockPayload, options =
|
|
|
936
1042
|
taskName: taskAnchor.taskName,
|
|
937
1043
|
latestTaskPreview: taskAnchor.latestTaskPreview,
|
|
938
1044
|
agentName: deriveAgentNameFromBranch(effectiveBranch),
|
|
1045
|
+
projectName: projectMetadata.projectName,
|
|
1046
|
+
projectPath: projectMetadata.projectPath,
|
|
1047
|
+
snapshotName: snapshotIdentity.snapshotName,
|
|
1048
|
+
snapshotEmail: snapshotIdentity.snapshotEmail,
|
|
939
1049
|
worktreePath: path.resolve(worktreePath),
|
|
940
1050
|
pid: null,
|
|
941
1051
|
cliName: 'codex',
|
|
@@ -955,6 +1065,56 @@ function buildWorktreeLockSession(repoRoot, worktreePath, lockPayload, options =
|
|
|
955
1065
|
lockSnapshotCount: toPositiveInteger(lockPayload?.snapshotCount) || 0,
|
|
956
1066
|
lockSessionCount: toPositiveInteger(lockPayload?.sessionCount) || telemetryEntries.length,
|
|
957
1067
|
collaboration: Boolean(lockPayload?.collaboration),
|
|
1068
|
+
sessionHealth: taskAnchor.sessionHealth || normalizeSessionHealthPayload(
|
|
1069
|
+
lockPayload?.sessionHealth || lockPayload?.sessionSeverity,
|
|
1070
|
+
),
|
|
1071
|
+
};
|
|
1072
|
+
|
|
1073
|
+
session.elapsedLabel = formatElapsedFrom(session.startedAt, now);
|
|
1074
|
+
Object.assign(session, deriveSessionActivity(session, { now }));
|
|
1075
|
+
return session;
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
function buildManagedWorktreeSession(repoRoot, worktreePath, options = {}) {
|
|
1079
|
+
const now = options.now || Date.now();
|
|
1080
|
+
const branch = readWorktreeBranch(worktreePath);
|
|
1081
|
+
if (!branch || branch === 'HEAD' || !isManagedAgentBranch(branch)) {
|
|
1082
|
+
return null;
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
const label = deriveSessionLabel(branch, worktreePath);
|
|
1086
|
+
const startedAt = deriveManagedWorktreeStartedAt(worktreePath, now);
|
|
1087
|
+
const session = {
|
|
1088
|
+
schemaVersion: SESSION_SCHEMA_VERSION,
|
|
1089
|
+
repoRoot: path.resolve(repoRoot),
|
|
1090
|
+
branch,
|
|
1091
|
+
taskName: label,
|
|
1092
|
+
latestTaskPreview: '',
|
|
1093
|
+
agentName: deriveAgentNameFromBranch(branch),
|
|
1094
|
+
projectName: '',
|
|
1095
|
+
projectPath: '',
|
|
1096
|
+
snapshotName: '',
|
|
1097
|
+
snapshotEmail: '',
|
|
1098
|
+
worktreePath: path.resolve(worktreePath),
|
|
1099
|
+
pid: null,
|
|
1100
|
+
cliName: 'gx',
|
|
1101
|
+
taskMode: '',
|
|
1102
|
+
openspecTier: '',
|
|
1103
|
+
taskRoutingReason: '',
|
|
1104
|
+
startedAt,
|
|
1105
|
+
lastHeartbeatAt: '',
|
|
1106
|
+
state: '',
|
|
1107
|
+
filePath: path.join(worktreePath, '.git'),
|
|
1108
|
+
label,
|
|
1109
|
+
changedPaths: [],
|
|
1110
|
+
worktreeChangedPaths: [],
|
|
1111
|
+
sourceKind: 'managed-worktree',
|
|
1112
|
+
telemetryUpdatedAt: '',
|
|
1113
|
+
telemetrySource: 'managed-worktree',
|
|
1114
|
+
lockSnapshotCount: 0,
|
|
1115
|
+
lockSessionCount: 0,
|
|
1116
|
+
collaboration: false,
|
|
1117
|
+
sessionHealth: null,
|
|
958
1118
|
};
|
|
959
1119
|
|
|
960
1120
|
session.elapsedLabel = formatElapsedFrom(session.startedAt, now);
|
|
@@ -1004,6 +1164,48 @@ function readWorktreeLockSessions(repoRoot, options = {}) {
|
|
|
1004
1164
|
return sortSessionsByTimestamp(sessions);
|
|
1005
1165
|
}
|
|
1006
1166
|
|
|
1167
|
+
function readManagedWorktreeSessions(repoRoot, options = {}) {
|
|
1168
|
+
const lockSessions = readWorktreeLockSessions(repoRoot, options);
|
|
1169
|
+
const lockSessionsByWorktree = new Map(
|
|
1170
|
+
lockSessions.map((session) => [path.resolve(session.worktreePath), session]),
|
|
1171
|
+
);
|
|
1172
|
+
const sessions = [];
|
|
1173
|
+
|
|
1174
|
+
for (const managedRoot of resolveManagedWorktreeRoots(repoRoot)) {
|
|
1175
|
+
if (!fs.existsSync(managedRoot)) {
|
|
1176
|
+
continue;
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
let entries;
|
|
1180
|
+
try {
|
|
1181
|
+
entries = fs.readdirSync(managedRoot, { withFileTypes: true });
|
|
1182
|
+
} catch (_error) {
|
|
1183
|
+
continue;
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
for (const entry of entries) {
|
|
1187
|
+
if (!entry.isDirectory()) {
|
|
1188
|
+
continue;
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
const worktreePath = path.join(managedRoot, entry.name);
|
|
1192
|
+
const worktreeKey = path.resolve(worktreePath);
|
|
1193
|
+
const lockSession = lockSessionsByWorktree.get(worktreeKey);
|
|
1194
|
+
if (lockSession) {
|
|
1195
|
+
sessions.push(lockSession);
|
|
1196
|
+
continue;
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
const managedSession = buildManagedWorktreeSession(repoRoot, worktreePath, options);
|
|
1200
|
+
if (managedSession) {
|
|
1201
|
+
sessions.push(managedSession);
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
return sortSessionsByTimestamp(sessions);
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1007
1209
|
function mergeSessionSources(primarySessions, lockSessions) {
|
|
1008
1210
|
const lockSessionsByWorktree = new Map(
|
|
1009
1211
|
lockSessions.map((session) => [path.resolve(session.worktreePath), session]),
|
|
@@ -1019,6 +1221,21 @@ function mergeSessionSources(primarySessions, lockSessions) {
|
|
|
1019
1221
|
}
|
|
1020
1222
|
if (lockSession) {
|
|
1021
1223
|
consumedLockWorktrees.add(worktreeKey);
|
|
1224
|
+
merged.push({
|
|
1225
|
+
...session,
|
|
1226
|
+
latestTaskPreview: session.latestTaskPreview || lockSession.latestTaskPreview,
|
|
1227
|
+
projectName: session.projectName || lockSession.projectName,
|
|
1228
|
+
projectPath: session.projectPath || lockSession.projectPath,
|
|
1229
|
+
snapshotName: session.snapshotName || lockSession.snapshotName,
|
|
1230
|
+
snapshotEmail: session.snapshotEmail || lockSession.snapshotEmail,
|
|
1231
|
+
telemetryUpdatedAt: session.telemetryUpdatedAt || lockSession.telemetryUpdatedAt,
|
|
1232
|
+
telemetrySource: session.telemetrySource || lockSession.telemetrySource,
|
|
1233
|
+
lockSnapshotCount: session.lockSnapshotCount || lockSession.lockSnapshotCount,
|
|
1234
|
+
lockSessionCount: session.lockSessionCount || lockSession.lockSessionCount,
|
|
1235
|
+
collaboration: session.collaboration || lockSession.collaboration,
|
|
1236
|
+
sessionHealth: session.sessionHealth || lockSession.sessionHealth,
|
|
1237
|
+
});
|
|
1238
|
+
continue;
|
|
1022
1239
|
}
|
|
1023
1240
|
merged.push(session);
|
|
1024
1241
|
}
|
|
@@ -1061,7 +1278,7 @@ function readActiveSessions(repoRoot, options = {}) {
|
|
|
1061
1278
|
|
|
1062
1279
|
return mergeSessionSources(
|
|
1063
1280
|
sortSessionsByTimestamp(sessionFileSessions),
|
|
1064
|
-
|
|
1281
|
+
readManagedWorktreeSessions(repoRoot, { now }),
|
|
1065
1282
|
);
|
|
1066
1283
|
}
|
|
1067
1284
|
|
|
@@ -1096,6 +1313,7 @@ module.exports = {
|
|
|
1096
1313
|
parseRepoChangeLine,
|
|
1097
1314
|
previewChangedPaths,
|
|
1098
1315
|
readActiveSessions,
|
|
1316
|
+
readManagedWorktreeSessions,
|
|
1099
1317
|
readWorktreeLockSessions,
|
|
1100
1318
|
readRepoChanges,
|
|
1101
1319
|
deriveRepoChangeStatus,
|