collab-runtime 0.4.2__tar.gz → 0.5.1__tar.gz
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.
- {collab_runtime-0.4.2/collab_runtime.egg-info → collab_runtime-0.5.1}/PKG-INFO +18 -1
- {collab_runtime-0.4.2 → collab_runtime-0.5.1}/README.md +34 -12
- {collab_runtime-0.4.2 → collab_runtime-0.5.1}/collab/agent_identity.py +52 -5
- {collab_runtime-0.4.2 → collab_runtime-0.5.1}/collab/dashboard/index.html +138 -11
- {collab_runtime-0.4.2 → collab_runtime-0.5.1}/collab/dashboard_server.py +2 -0
- collab_runtime-0.5.1/collab/githooks.py +318 -0
- collab_runtime-0.5.1/collab/hook_templates/commit-msg +21 -0
- collab_runtime-0.5.1/collab/hook_templates/post-commit +30 -0
- collab_runtime-0.5.1/collab/hook_templates/pre-commit +75 -0
- collab_runtime-0.5.1/collab/hook_templates/pre-push +58 -0
- {collab_runtime-0.4.2 → collab_runtime-0.5.1}/collab/live_locks_watcher.py +116 -2
- {collab_runtime-0.4.2 → collab_runtime-0.5.1}/collab/lock_client.py +69 -7
- {collab_runtime-0.4.2 → collab_runtime-0.5.1}/collab/main.py +97 -2
- {collab_runtime-0.4.2 → collab_runtime-0.5.1/collab_runtime.egg-info}/PKG-INFO +18 -1
- {collab_runtime-0.4.2 → collab_runtime-0.5.1}/collab_runtime.egg-info/SOURCES.txt +5 -0
- {collab_runtime-0.4.2 → collab_runtime-0.5.1}/docs/pypi/README.md +17 -0
- {collab_runtime-0.4.2 → collab_runtime-0.5.1}/pyproject.toml +4 -2
- {collab_runtime-0.4.2 → collab_runtime-0.5.1}/LICENSE +0 -0
- {collab_runtime-0.4.2 → collab_runtime-0.5.1}/collab/__init__.py +0 -0
- {collab_runtime-0.4.2 → collab_runtime-0.5.1}/collab/__main__.py +0 -0
- {collab_runtime-0.4.2 → collab_runtime-0.5.1}/collab/dashboard/dashboard-format.js +0 -0
- {collab_runtime-0.4.2 → collab_runtime-0.5.1}/collab/errors.py +0 -0
- {collab_runtime-0.4.2 → collab_runtime-0.5.1}/collab/logging_config.py +0 -0
- {collab_runtime-0.4.2 → collab_runtime-0.5.1}/collab/platform_probe.py +0 -0
- {collab_runtime-0.4.2 → collab_runtime-0.5.1}/collab/safe_subprocess.py +0 -0
- {collab_runtime-0.4.2 → collab_runtime-0.5.1}/collab/subprocess_bridge.py +0 -0
- {collab_runtime-0.4.2 → collab_runtime-0.5.1}/collab_runtime.egg-info/dependency_links.txt +0 -0
- {collab_runtime-0.4.2 → collab_runtime-0.5.1}/collab_runtime.egg-info/entry_points.txt +0 -0
- {collab_runtime-0.4.2 → collab_runtime-0.5.1}/collab_runtime.egg-info/requires.txt +0 -0
- {collab_runtime-0.4.2 → collab_runtime-0.5.1}/collab_runtime.egg-info/top_level.txt +0 -0
- {collab_runtime-0.4.2 → collab_runtime-0.5.1}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: collab-runtime
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.1
|
|
4
4
|
Summary: Collaborative file locking runtime
|
|
5
5
|
Author-email: KirilMT <kiril.mt95@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -130,6 +130,19 @@ collab active
|
|
|
130
130
|
|
|
131
131
|
If connected, this lists all currently active locks (empty on a fresh setup).
|
|
132
132
|
|
|
133
|
+
### 4 — Install Git Hooks (optional)
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
collab init-hooks
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
This installs `pre-commit`, `post-commit`, `pre-push`, and `commit-msg` hooks into the current
|
|
140
|
+
repository. The hooks acquire locks for staged files, block commits that conflict with another
|
|
141
|
+
developer's lock, and release locks after a successful push. They resolve the project `.venv` first,
|
|
142
|
+
so **commits from VS Code / Cursor Source Control behave the same as a venv-activated terminal**.
|
|
143
|
+
|
|
144
|
+
Existing non-collab hooks are preserved; pass `--force` to overwrite them.
|
|
145
|
+
|
|
133
146
|
---
|
|
134
147
|
|
|
135
148
|
## CLI Reference
|
|
@@ -162,6 +175,10 @@ collab force-release-all
|
|
|
162
175
|
collab acquire-batch path/to/a.py path/to/b.py --reason "Refactoring"
|
|
163
176
|
collab release-batch path/to/a.py path/to/b.py
|
|
164
177
|
|
|
178
|
+
# Install git hooks into the current repo (offline)
|
|
179
|
+
collab init-hooks
|
|
180
|
+
collab init-hooks --force
|
|
181
|
+
|
|
165
182
|
# Reconcile local and remote lock state
|
|
166
183
|
collab reconcile
|
|
167
184
|
|
|
@@ -74,15 +74,18 @@ The setup script automatically:
|
|
|
74
74
|
|
|
75
75
|
After setup, verify your `.env` at the project root has these values:
|
|
76
76
|
|
|
77
|
-
| Variable | Description
|
|
78
|
-
| --------------------------- |
|
|
79
|
-
| `SUPABASE_URL` | Your Supabase project URL (from Project Settings → API)
|
|
80
|
-
| `SUPABASE_ANON_KEY` | Anonymous/public key (from Project Settings → API)
|
|
81
|
-
| `SUPABASE_SERVICE_ROLE_KEY` | Service role key (**required** for dashboard force-release)
|
|
82
|
-
| `LOCK_STRICT` | If `1`, git hooks block on lock errors. Default `0` (warn only)
|
|
83
|
-
| `COLLAB_AGENT_ID` | Optional stable id for an AI agent session (multi-agent locking)
|
|
84
|
-
| `COLLAB_AGENT_LABEL` | Optional
|
|
85
|
-
| `
|
|
77
|
+
| Variable | Description |
|
|
78
|
+
| --------------------------- | ------------------------------------------------------------------- |
|
|
79
|
+
| `SUPABASE_URL` | Your Supabase project URL (from Project Settings → API) |
|
|
80
|
+
| `SUPABASE_ANON_KEY` | Anonymous/public key (from Project Settings → API) |
|
|
81
|
+
| `SUPABASE_SERVICE_ROLE_KEY` | Service role key (**required** for dashboard force-release) |
|
|
82
|
+
| `LOCK_STRICT` | If `1`, git hooks block on lock errors. Default `0` (warn only) |
|
|
83
|
+
| `COLLAB_AGENT_ID` | Optional stable id for an AI agent session (multi-agent locking) |
|
|
84
|
+
| `COLLAB_AGENT_LABEL` | Optional task label shown on the dashboard (e.g. `refactor-auth`) |
|
|
85
|
+
| `COLLAB_AGENT_KIND` | Optional AI runtime for the dashboard icon (auto-detected) |
|
|
86
|
+
| `COLLAB_AGENT_MODE` | Set to `1` to auto-generate/persist an agent id when unset |
|
|
87
|
+
| `COLLAB_AGENT_HOOKS` | Set to `1` to enable the IDE edit hook that auto-claims agent edits |
|
|
88
|
+
| `COLLAB_WATCHER_AGENT_ID` | Opt in to a dedicated agent watcher (default: watcher = human) |
|
|
86
89
|
|
|
87
90
|
> **Important:** `SUPABASE_SERVICE_ROLE_KEY` is needed for the dashboard's Force Release button. Without it, only your own locks can be released.
|
|
88
91
|
|
|
@@ -102,9 +105,28 @@ set COLLAB_AGENT_ID=agent-fix-tests
|
|
|
102
105
|
collab acquire src/auth.py # conflict — locked by agent-refactor-auth
|
|
103
106
|
```
|
|
104
107
|
|
|
105
|
-
For existing Supabase projects, re-run
|
|
106
|
-
`agent_label` columns
|
|
107
|
-
already include them).
|
|
108
|
+
For existing Supabase projects, re-run [supabase/schema.sql](supabase/schema.sql) to add the
|
|
109
|
+
`agent_id` / `agent_label` / `origin` / `agent_kind` columns and the updated `acquire_lock` function
|
|
110
|
+
(the script is idempotent; fresh installs already include them).
|
|
111
|
+
|
|
112
|
+
#### Strict user-vs-agent attribution
|
|
113
|
+
|
|
114
|
+
The dashboard distinguishes **human** edits from **AI agent** edits and shows _what the agent is
|
|
115
|
+
working on_ — not a cryptic id. Attribution is decided by an explicit signal:
|
|
116
|
+
|
|
117
|
+
- The background watcher locks bulk git changes as the **human** (`User` chip), even inside an AI
|
|
118
|
+
IDE. So normal work is never mislabelled as an agent.
|
|
119
|
+
- An AI agent claims the files it edits, producing an **"AI Agent"** badge (runtime icon + task).
|
|
120
|
+
Make this automatic by wiring your IDE's edit hook to `collab claim` — see
|
|
121
|
+
[scripts/agent-hooks/](scripts/agent-hooks/README.md). It is **runtime-agnostic** (Cursor, Claude
|
|
122
|
+
Code, Copilot, Gemini, ...). Enable with `COLLAB_AGENT_HOOKS=1` and optionally
|
|
123
|
+
`COLLAB_AGENT_LABEL="<task>"`.
|
|
124
|
+
|
|
125
|
+
Agents can also claim explicitly:
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
collab claim src/auth.py --label "refactor-auth" --reason "Refactor auth"
|
|
129
|
+
```
|
|
108
130
|
|
|
109
131
|
### 4. Verify Setup
|
|
110
132
|
|
|
@@ -58,13 +58,19 @@ def detect_agent_runtime_label() -> Optional[str]:
|
|
|
58
58
|
|
|
59
59
|
|
|
60
60
|
def is_agent_mode_requested() -> bool:
|
|
61
|
-
"""Return True when agent identity should be active for this process.
|
|
61
|
+
"""Return True when agent identity should be active for this process.
|
|
62
|
+
|
|
63
|
+
STRICT ATTRIBUTION: the mere *presence* of an AI runtime (e.g. a process
|
|
64
|
+
spawned from a Cursor/Claude terminal that exports ``CURSOR_TRACE_ID``) does
|
|
65
|
+
NOT by itself attribute locks to an agent. Doing so caused every background
|
|
66
|
+
auto-lock to be mislabelled as the runtime. Agent attribution now requires an
|
|
67
|
+
*explicit* signal: ``COLLAB_AGENT_ID`` or ``COLLAB_AGENT_MODE``. The detected
|
|
68
|
+
runtime is still used for friendly display only (see :func:`resolve_agent_kind`).
|
|
69
|
+
"""
|
|
62
70
|
if _read_clean_env("COLLAB_AGENT_ID"):
|
|
63
71
|
return True
|
|
64
72
|
if _is_truthy_env("COLLAB_AGENT_MODE"):
|
|
65
73
|
return True
|
|
66
|
-
if detect_agent_runtime_label():
|
|
67
|
-
return True
|
|
68
74
|
return False
|
|
69
75
|
|
|
70
76
|
|
|
@@ -152,12 +158,17 @@ def resolve_agent_label(
|
|
|
152
158
|
explicit_label: Optional[str] = None,
|
|
153
159
|
runtime_label: Optional[str] = None,
|
|
154
160
|
) -> Optional[str]:
|
|
155
|
-
"""Resolve
|
|
161
|
+
"""Resolve the human-readable *task* label (the "why / what for").
|
|
162
|
+
|
|
163
|
+
This intentionally does NOT fall back to the runtime name (e.g. ``cursor``): the
|
|
164
|
+
label describes the task an agent is working on (``fix-ci-dashboard``), while the
|
|
165
|
+
runtime family is tracked separately as ``agent_kind`` for display. When no task
|
|
166
|
+
label is supplied the dashboard shows a generic "AI Agent".
|
|
167
|
+
"""
|
|
156
168
|
for candidate in (
|
|
157
169
|
explicit_label,
|
|
158
170
|
_read_clean_env("COLLAB_AGENT_LABEL"),
|
|
159
171
|
runtime_label,
|
|
160
|
-
detect_agent_runtime_label(),
|
|
161
172
|
):
|
|
162
173
|
if candidate:
|
|
163
174
|
val = candidate.strip()
|
|
@@ -166,6 +177,40 @@ def resolve_agent_label(
|
|
|
166
177
|
return None
|
|
167
178
|
|
|
168
179
|
|
|
180
|
+
def resolve_agent_kind(
|
|
181
|
+
*,
|
|
182
|
+
explicit_kind: Optional[str] = None,
|
|
183
|
+
agent_id: Optional[str] = None,
|
|
184
|
+
) -> Optional[str]:
|
|
185
|
+
"""Resolve the AI runtime family for friendly display (icon/name).
|
|
186
|
+
|
|
187
|
+
Precedence: explicit value → ``COLLAB_AGENT_KIND`` → detected runtime marker.
|
|
188
|
+
When an agent identity exists but the runtime is unknown, falls back to the
|
|
189
|
+
generic ``"other"`` so the dashboard can still render an AI badge. Returns
|
|
190
|
+
``None`` for human (no agent) locks.
|
|
191
|
+
"""
|
|
192
|
+
for candidate in (
|
|
193
|
+
explicit_kind,
|
|
194
|
+
_read_clean_env("COLLAB_AGENT_KIND"),
|
|
195
|
+
detect_agent_runtime_label(),
|
|
196
|
+
):
|
|
197
|
+
if candidate:
|
|
198
|
+
val = candidate.strip().lower()
|
|
199
|
+
if val:
|
|
200
|
+
return val[:64]
|
|
201
|
+
if agent_id:
|
|
202
|
+
return "other"
|
|
203
|
+
return None
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def resolve_origin(agent_id: Optional[str]) -> str:
|
|
207
|
+
"""Return the authoritative attribution origin for a lock.
|
|
208
|
+
|
|
209
|
+
``'agent'`` when a unique agent identity is present, otherwise ``'human'``.
|
|
210
|
+
"""
|
|
211
|
+
return "agent" if agent_id else "human"
|
|
212
|
+
|
|
213
|
+
|
|
169
214
|
def agent_ids_match(
|
|
170
215
|
lock_agent_id: Optional[str],
|
|
171
216
|
client_agent_id: Optional[str],
|
|
@@ -282,6 +327,7 @@ def identity_summary(
|
|
|
282
327
|
developer_id: str,
|
|
283
328
|
agent_id: Optional[str],
|
|
284
329
|
agent_label: Optional[str],
|
|
330
|
+
agent_kind: Optional[str] = None,
|
|
285
331
|
) -> dict[str, Optional[str]]:
|
|
286
332
|
"""Return a dict suitable for ``collab whoami`` JSON output."""
|
|
287
333
|
mode = "agent" if agent_id else "human"
|
|
@@ -289,5 +335,6 @@ def identity_summary(
|
|
|
289
335
|
"developer_id": developer_id,
|
|
290
336
|
"agent_id": agent_id,
|
|
291
337
|
"agent_label": agent_label,
|
|
338
|
+
"agent_kind": agent_kind,
|
|
292
339
|
"mode": mode,
|
|
293
340
|
}
|
|
@@ -324,6 +324,55 @@
|
|
|
324
324
|
color: #4338ca;
|
|
325
325
|
}
|
|
326
326
|
|
|
327
|
+
/* Human vs AI actor attribution badges */
|
|
328
|
+
.actor-chip {
|
|
329
|
+
display: inline-flex;
|
|
330
|
+
align-items: center;
|
|
331
|
+
gap: 0.3rem;
|
|
332
|
+
margin-left: 0.4rem;
|
|
333
|
+
padding: 0.12rem 0.5rem;
|
|
334
|
+
border-radius: 999px;
|
|
335
|
+
font-size: 0.72rem;
|
|
336
|
+
font-weight: 600;
|
|
337
|
+
white-space: nowrap;
|
|
338
|
+
vertical-align: middle;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
.user-chip {
|
|
342
|
+
background: #f1f5f9;
|
|
343
|
+
color: #475569;
|
|
344
|
+
border: 1px solid var(--border-color);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
.agent-badge {
|
|
348
|
+
background: linear-gradient(135deg, #ede9fe 0%, #e0e7ff 100%);
|
|
349
|
+
color: #5b21b6;
|
|
350
|
+
border: 1px solid #ddd6fe;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
.agent-badge-owner {
|
|
354
|
+
background: linear-gradient(135deg, #ddd6fe 0%, #c7d2fe 100%);
|
|
355
|
+
color: #4c1d95;
|
|
356
|
+
border-color: #c4b5fd;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
.agent-badge .agent-badge-kind {
|
|
360
|
+
font-weight: 700;
|
|
361
|
+
text-transform: capitalize;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
.agent-badge .agent-badge-task {
|
|
365
|
+
font-weight: 500;
|
|
366
|
+
opacity: 0.85;
|
|
367
|
+
max-width: 16rem;
|
|
368
|
+
overflow: hidden;
|
|
369
|
+
text-overflow: ellipsis;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
.agent-badge .agent-badge-sep {
|
|
373
|
+
opacity: 0.5;
|
|
374
|
+
}
|
|
375
|
+
|
|
327
376
|
code {
|
|
328
377
|
color: #1d4ed8;
|
|
329
378
|
background: #f8fafc;
|
|
@@ -759,20 +808,98 @@ SUPABASE_ANON_KEY=your_anon_key</pre>
|
|
|
759
808
|
return !!(SUPABASE_USER && lock.developer_id === SUPABASE_USER);
|
|
760
809
|
}
|
|
761
810
|
|
|
811
|
+
function escapeHtml(value) {
|
|
812
|
+
return String(value == null ? "" : value)
|
|
813
|
+
.replace(/&/g, "&")
|
|
814
|
+
.replace(/</g , "<") .replace( />/g, ">")
|
|
815
|
+
.replace(/"/g, """)
|
|
816
|
+
.replace(/'/g, "'");
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
function isAgentLock(lock) {
|
|
820
|
+
if (lock.origin === "agent") {
|
|
821
|
+
return true;
|
|
822
|
+
}
|
|
823
|
+
// Backward compatibility for rows created before `origin` existed.
|
|
824
|
+
return !!lock.agent_id && lock.origin !== "human";
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
function agentKindIcon(kind) {
|
|
828
|
+
switch (String(kind || "").toLowerCase()) {
|
|
829
|
+
case "cursor":
|
|
830
|
+
return '<i class="fas fa-i-cursor"></i>';
|
|
831
|
+
case "copilot":
|
|
832
|
+
return '<i class="fab fa-github"></i>';
|
|
833
|
+
case "claude-code":
|
|
834
|
+
case "claude":
|
|
835
|
+
case "composer":
|
|
836
|
+
case "other":
|
|
837
|
+
default:
|
|
838
|
+
return '<i class="fas fa-robot"></i>';
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
function prettifyKind(kind) {
|
|
843
|
+
const k = String(kind || "").trim();
|
|
844
|
+
if (!k || k === "other") {
|
|
845
|
+
return "";
|
|
846
|
+
}
|
|
847
|
+
return k
|
|
848
|
+
.split(/[-_]/)
|
|
849
|
+
.map((p) => (p ? p.charAt(0).toUpperCase() + p.slice(1) : p))
|
|
850
|
+
.join(" ");
|
|
851
|
+
}
|
|
852
|
+
|
|
762
853
|
function formatDeveloperCell(lock) {
|
|
763
854
|
const dev = lock.developer_id || "?";
|
|
764
|
-
const
|
|
765
|
-
const chip = agentText
|
|
766
|
-
? '<span class="agent-chip' + (lockOwnedByMe(lock) ? " agent-chip-owner" : "") + '">' +
|
|
767
|
-
agentText +
|
|
768
|
-
"</span>"
|
|
769
|
-
: '<span class="agent-chip">user</span>';
|
|
770
|
-
return (
|
|
855
|
+
const devTag =
|
|
771
856
|
'<span class="dev-tag ' + (lockOwnedByMe(lock) ? "dev-tag-owner" : "") + '">' +
|
|
772
|
-
dev +
|
|
773
|
-
"</span>"
|
|
774
|
-
|
|
775
|
-
)
|
|
857
|
+
escapeHtml(dev) +
|
|
858
|
+
"</span>";
|
|
859
|
+
|
|
860
|
+
if (!isAgentLock(lock)) {
|
|
861
|
+
const userChip =
|
|
862
|
+
'<span class="actor-chip user-chip" title="Edited by a human developer">' +
|
|
863
|
+
'<i class="fas fa-user"></i>User</span>';
|
|
864
|
+
return devTag + userChip;
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
// AI agent lock: show clearly that it was an AI agent and what for.
|
|
868
|
+
// The raw agent_id is intentionally never displayed in the cell — it
|
|
869
|
+
// appears only in the hover tooltip for debugging.
|
|
870
|
+
const kind = lock.agent_kind || "";
|
|
871
|
+
const kindName = prettifyKind(kind);
|
|
872
|
+
const task = String(lock.agent_label || "").trim();
|
|
873
|
+
|
|
874
|
+
const tip = ["Edited by an AI agent"];
|
|
875
|
+
if (kindName) tip.push("Runtime: " + kindName);
|
|
876
|
+
if (task) tip.push("Task: " + task);
|
|
877
|
+
if (lock.agent_id) tip.push("Agent id: " + lock.agent_id);
|
|
878
|
+
if (lock.reason) tip.push("Reason: " + lock.reason);
|
|
879
|
+
|
|
880
|
+
let inner =
|
|
881
|
+
agentKindIcon(kind) + '<span class="agent-badge-text">AI Agent</span>';
|
|
882
|
+
if (kindName) {
|
|
883
|
+
inner +=
|
|
884
|
+
'<span class="agent-badge-sep">·</span>' +
|
|
885
|
+
'<span class="agent-badge-kind">' +
|
|
886
|
+
escapeHtml(kindName) +
|
|
887
|
+
"</span>";
|
|
888
|
+
}
|
|
889
|
+
if (task) {
|
|
890
|
+
inner +=
|
|
891
|
+
'<span class="agent-badge-sep">·</span>' +
|
|
892
|
+
'<span class="agent-badge-task">' +
|
|
893
|
+
escapeHtml(task) +
|
|
894
|
+
"</span>";
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
const badge =
|
|
898
|
+
'<span class="actor-chip agent-badge' + (lockOwnedByMe(lock) ? " agent-badge-owner" : "") + '" title="' + escapeHtml(tip.join("\n")) + '">' +
|
|
899
|
+
inner +
|
|
900
|
+
"</span>";
|
|
901
|
+
|
|
902
|
+
return devTag + badge;
|
|
776
903
|
}
|
|
777
904
|
|
|
778
905
|
function updateTimestamp() {
|
|
@@ -161,6 +161,7 @@ def load_runtime_supabase_config(project_root: str) -> dict[str, Any]:
|
|
|
161
161
|
state_dir = state_override or os.path.join(project_root, ".collab")
|
|
162
162
|
agent_id = agent_identity.resolve_agent_id(state_dir)
|
|
163
163
|
agent_label = agent_identity.resolve_agent_label()
|
|
164
|
+
agent_kind = agent_identity.resolve_agent_kind(agent_id=agent_id)
|
|
164
165
|
project_name = resolve_project_display_name(project_root, file_vals)
|
|
165
166
|
return {
|
|
166
167
|
"url": url,
|
|
@@ -169,6 +170,7 @@ def load_runtime_supabase_config(project_root: str) -> dict[str, Any]:
|
|
|
169
170
|
"user": user,
|
|
170
171
|
"agentId": agent_id,
|
|
171
172
|
"agentLabel": agent_label,
|
|
173
|
+
"agentKind": agent_kind,
|
|
172
174
|
"projectName": project_name,
|
|
173
175
|
}
|
|
174
176
|
|