session-collab-mcp 2.3.0 → 2.3.1
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 +25 -9
- package/dist/{chunk-TFGXE3EI.js → chunk-UDJMG3TR.js} +447 -61
- package/dist/chunk-UDJMG3TR.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/http/cli.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-TFGXE3EI.js.map +0 -1
|
@@ -188,12 +188,20 @@ Coordinate sessions and persist context across conversations.
|
|
|
188
188
|
|
|
189
189
|
1. **On start**: \`collab_session_start\` with project_root
|
|
190
190
|
2. **Before editing**: \`collab_claim\` action="check"
|
|
191
|
-
3. **For changes**: \`collab_claim\` action="create" (
|
|
191
|
+
3. **For changes**: \`collab_claim\` action="create" (smart mode claims safe files and queues blocked files)
|
|
192
192
|
4. **Save context**: \`collab_memory_save\` for important findings
|
|
193
193
|
5. **While working**: \`collab_session_update\` with current_task/todos
|
|
194
194
|
6. **When done**: \`collab_claim\` action="release"
|
|
195
195
|
7. **On end**: \`collab_session_end\`
|
|
196
196
|
|
|
197
|
+
## Conflict Handling
|
|
198
|
+
|
|
199
|
+
- \`strict\`: conflicting claims are blocked; coordinate before editing.
|
|
200
|
+
- \`smart\` (default): same-file work can proceed when symbol claims do not overlap; mixed requests claim safe files and create coordination requests for blocked files.
|
|
201
|
+
- \`bypass\`: overlapping claims require explicit \`allow_conflicts=true\` and return a warning.
|
|
202
|
+
- If a file is blocked, narrow the claim to specific symbols before retrying. Do not overwrite, revert, or delete another active session's work.
|
|
203
|
+
- Check \`collab_status\` or \`collab_session_list\` for pending coordination requests.
|
|
204
|
+
|
|
197
205
|
## Memory Categories
|
|
198
206
|
|
|
199
207
|
- **finding**: Discovered facts, root causes
|
|
@@ -4286,6 +4294,29 @@ function createToolResult(text, isError = false) {
|
|
|
4286
4294
|
};
|
|
4287
4295
|
}
|
|
4288
4296
|
|
|
4297
|
+
// src/db/types.ts
|
|
4298
|
+
function getPriorityLevel(priority) {
|
|
4299
|
+
if (priority >= 90) return { value: priority, level: "critical", label: "Critical (90-100)" };
|
|
4300
|
+
if (priority >= 70) return { value: priority, level: "high", label: "High (70-89)" };
|
|
4301
|
+
if (priority >= 40) return { value: priority, level: "normal", label: "Normal (40-69)" };
|
|
4302
|
+
return { value: priority, level: "low", label: "Low (0-39)" };
|
|
4303
|
+
}
|
|
4304
|
+
var DEFAULT_SESSION_CONFIG = {
|
|
4305
|
+
mode: "smart",
|
|
4306
|
+
allow_release_others: false,
|
|
4307
|
+
auto_release_stale: false,
|
|
4308
|
+
stale_threshold_hours: 2,
|
|
4309
|
+
auto_release_immediate: false,
|
|
4310
|
+
auto_release_delay_minutes: 5
|
|
4311
|
+
};
|
|
4312
|
+
var SCOPE_WAIT_MINUTES = {
|
|
4313
|
+
small: 30,
|
|
4314
|
+
medium: 120,
|
|
4315
|
+
// 2 hours
|
|
4316
|
+
large: 480
|
|
4317
|
+
// 8 hours
|
|
4318
|
+
};
|
|
4319
|
+
|
|
4289
4320
|
// src/utils/crypto.ts
|
|
4290
4321
|
function generateId() {
|
|
4291
4322
|
return crypto.randomUUID();
|
|
@@ -4681,6 +4712,144 @@ async function removeSessionFromAllQueues(db, sessionId) {
|
|
|
4681
4712
|
const result = await db.prepare("DELETE FROM claim_queue WHERE session_id = ?").bind(sessionId).run();
|
|
4682
4713
|
return result.meta.changes;
|
|
4683
4714
|
}
|
|
4715
|
+
async function getNextQueuePosition(db, claimId) {
|
|
4716
|
+
const result = await db.prepare("SELECT MAX(position) as max_pos FROM claim_queue WHERE claim_id = ?").bind(claimId).first();
|
|
4717
|
+
return (result?.max_pos ?? 0) + 1;
|
|
4718
|
+
}
|
|
4719
|
+
async function calculateEstimatedWait(db, claimId, position) {
|
|
4720
|
+
const result = await db.prepare(
|
|
4721
|
+
`SELECT scope FROM claim_queue
|
|
4722
|
+
WHERE claim_id = ? AND position < ?
|
|
4723
|
+
ORDER BY priority DESC, position ASC`
|
|
4724
|
+
).bind(claimId, position).all();
|
|
4725
|
+
let totalMinutes = 0;
|
|
4726
|
+
for (const entry of result.results) {
|
|
4727
|
+
totalMinutes += SCOPE_WAIT_MINUTES[entry.scope] ?? SCOPE_WAIT_MINUTES.medium;
|
|
4728
|
+
}
|
|
4729
|
+
const claim = await getClaim(db, claimId);
|
|
4730
|
+
if (claim) {
|
|
4731
|
+
totalMinutes += Math.round(SCOPE_WAIT_MINUTES[claim.scope] / 2);
|
|
4732
|
+
}
|
|
4733
|
+
return totalMinutes;
|
|
4734
|
+
}
|
|
4735
|
+
async function joinQueue(db, params) {
|
|
4736
|
+
const existing = await db.prepare("SELECT * FROM claim_queue WHERE claim_id = ? AND session_id = ?").bind(params.claim_id, params.session_id).first();
|
|
4737
|
+
if (existing) {
|
|
4738
|
+
return existing;
|
|
4739
|
+
}
|
|
4740
|
+
const id = generateId();
|
|
4741
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4742
|
+
const priority = params.priority ?? 50;
|
|
4743
|
+
const scope = params.scope ?? "medium";
|
|
4744
|
+
const position = await getNextQueuePosition(db, params.claim_id);
|
|
4745
|
+
const estimatedWait = await calculateEstimatedWait(db, params.claim_id, position);
|
|
4746
|
+
await db.prepare(
|
|
4747
|
+
`INSERT INTO claim_queue (id, claim_id, session_id, intent, position, priority, scope, estimated_wait_minutes, created_at)
|
|
4748
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
4749
|
+
).bind(id, params.claim_id, params.session_id, params.intent, position, priority, scope, estimatedWait, now).run();
|
|
4750
|
+
return {
|
|
4751
|
+
id,
|
|
4752
|
+
claim_id: params.claim_id,
|
|
4753
|
+
session_id: params.session_id,
|
|
4754
|
+
intent: params.intent,
|
|
4755
|
+
position,
|
|
4756
|
+
priority,
|
|
4757
|
+
scope,
|
|
4758
|
+
estimated_wait_minutes: estimatedWait,
|
|
4759
|
+
created_at: now
|
|
4760
|
+
};
|
|
4761
|
+
}
|
|
4762
|
+
async function listQueue(db, params = {}) {
|
|
4763
|
+
let query = `
|
|
4764
|
+
SELECT
|
|
4765
|
+
q.*,
|
|
4766
|
+
s.name as session_name,
|
|
4767
|
+
c.session_id as owner_session_id,
|
|
4768
|
+
c.intent as claim_intent,
|
|
4769
|
+
cs.name as claim_session_name,
|
|
4770
|
+
GROUP_CONCAT(cf.file_path, '|||') as claim_files_concat
|
|
4771
|
+
FROM claim_queue q
|
|
4772
|
+
JOIN sessions s ON q.session_id = s.id
|
|
4773
|
+
JOIN claims c ON q.claim_id = c.id
|
|
4774
|
+
JOIN sessions cs ON c.session_id = cs.id
|
|
4775
|
+
LEFT JOIN claim_files cf ON c.id = cf.claim_id
|
|
4776
|
+
WHERE c.status = 'active'
|
|
4777
|
+
AND s.status = 'active'
|
|
4778
|
+
AND cs.status = 'active'
|
|
4779
|
+
`;
|
|
4780
|
+
const bindings = [];
|
|
4781
|
+
if (params.claim_id) {
|
|
4782
|
+
query += " AND q.claim_id = ?";
|
|
4783
|
+
bindings.push(params.claim_id);
|
|
4784
|
+
}
|
|
4785
|
+
if (params.session_id) {
|
|
4786
|
+
query += " AND q.session_id = ?";
|
|
4787
|
+
bindings.push(params.session_id);
|
|
4788
|
+
}
|
|
4789
|
+
if (params.owner_session_id) {
|
|
4790
|
+
query += " AND c.session_id = ?";
|
|
4791
|
+
bindings.push(params.owner_session_id);
|
|
4792
|
+
}
|
|
4793
|
+
query += " GROUP BY q.id ORDER BY q.priority DESC, q.position ASC";
|
|
4794
|
+
const result = await db.prepare(query).bind(...bindings).all();
|
|
4795
|
+
return result.results.map((entry) => ({
|
|
4796
|
+
...entry,
|
|
4797
|
+
claim_files: entry.claim_files_concat ? entry.claim_files_concat.split("|||") : []
|
|
4798
|
+
}));
|
|
4799
|
+
}
|
|
4800
|
+
async function createNotification(db, params) {
|
|
4801
|
+
const id = generateId();
|
|
4802
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4803
|
+
const metadataJson = params.metadata ? JSON.stringify(params.metadata) : null;
|
|
4804
|
+
await db.prepare(
|
|
4805
|
+
`INSERT INTO notifications (id, session_id, type, title, message, reference_type, reference_id, metadata, created_at)
|
|
4806
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
4807
|
+
).bind(
|
|
4808
|
+
id,
|
|
4809
|
+
params.session_id,
|
|
4810
|
+
params.type,
|
|
4811
|
+
params.title,
|
|
4812
|
+
params.message,
|
|
4813
|
+
params.reference_type ?? null,
|
|
4814
|
+
params.reference_id ?? null,
|
|
4815
|
+
metadataJson,
|
|
4816
|
+
now
|
|
4817
|
+
).run();
|
|
4818
|
+
return {
|
|
4819
|
+
id,
|
|
4820
|
+
session_id: params.session_id,
|
|
4821
|
+
type: params.type,
|
|
4822
|
+
title: params.title,
|
|
4823
|
+
message: params.message,
|
|
4824
|
+
reference_type: params.reference_type ?? null,
|
|
4825
|
+
reference_id: params.reference_id ?? null,
|
|
4826
|
+
metadata: metadataJson,
|
|
4827
|
+
read_at: null,
|
|
4828
|
+
created_at: now
|
|
4829
|
+
};
|
|
4830
|
+
}
|
|
4831
|
+
async function notifyQueueOnClaimRelease(db, claimId, releasedBy, files) {
|
|
4832
|
+
const queuedSessions = await listQueue(db, { claim_id: claimId });
|
|
4833
|
+
let notified = 0;
|
|
4834
|
+
for (const entry of queuedSessions) {
|
|
4835
|
+
await createNotification(db, {
|
|
4836
|
+
session_id: entry.session_id,
|
|
4837
|
+
type: "queue_ready",
|
|
4838
|
+
title: "Claim released",
|
|
4839
|
+
message: `The claim for ${files.join(", ")} has been released. You can claim these files now.`,
|
|
4840
|
+
reference_type: "claim",
|
|
4841
|
+
reference_id: claimId,
|
|
4842
|
+
metadata: {
|
|
4843
|
+
claim_id: claimId,
|
|
4844
|
+
files,
|
|
4845
|
+
released_by: releasedBy,
|
|
4846
|
+
queue_position: entry.position
|
|
4847
|
+
}
|
|
4848
|
+
});
|
|
4849
|
+
notified++;
|
|
4850
|
+
}
|
|
4851
|
+
return notified;
|
|
4852
|
+
}
|
|
4684
4853
|
async function logAuditEvent(db, params) {
|
|
4685
4854
|
const id = generateId();
|
|
4686
4855
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -4953,22 +5122,6 @@ async function getProtectedFiles(db, sessionId) {
|
|
|
4953
5122
|
});
|
|
4954
5123
|
}
|
|
4955
5124
|
|
|
4956
|
-
// src/db/types.ts
|
|
4957
|
-
function getPriorityLevel(priority) {
|
|
4958
|
-
if (priority >= 90) return { value: priority, level: "critical", label: "Critical (90-100)" };
|
|
4959
|
-
if (priority >= 70) return { value: priority, level: "high", label: "High (70-89)" };
|
|
4960
|
-
if (priority >= 40) return { value: priority, level: "normal", label: "Normal (40-69)" };
|
|
4961
|
-
return { value: priority, level: "low", label: "Low (0-39)" };
|
|
4962
|
-
}
|
|
4963
|
-
var DEFAULT_SESSION_CONFIG = {
|
|
4964
|
-
mode: "smart",
|
|
4965
|
-
allow_release_others: false,
|
|
4966
|
-
auto_release_stale: false,
|
|
4967
|
-
stale_threshold_hours: 2,
|
|
4968
|
-
auto_release_immediate: false,
|
|
4969
|
-
auto_release_delay_minutes: 5
|
|
4970
|
-
};
|
|
4971
|
-
|
|
4972
5125
|
// src/mcp/schemas.ts
|
|
4973
5126
|
var sessionIdSchema = external_exports.string().min(1, "session_id is required");
|
|
4974
5127
|
var claimIdSchema = external_exports.string().min(1, "claim_id is required");
|
|
@@ -5148,6 +5301,35 @@ function parseJsonField(value) {
|
|
|
5148
5301
|
return null;
|
|
5149
5302
|
}
|
|
5150
5303
|
}
|
|
5304
|
+
function formatIncomingCoordination(entry) {
|
|
5305
|
+
return {
|
|
5306
|
+
queue_id: entry.id,
|
|
5307
|
+
owner_claim_id: entry.claim_id,
|
|
5308
|
+
requested_by_session_id: entry.session_id,
|
|
5309
|
+
requested_by_session_name: entry.session_name,
|
|
5310
|
+
intent: entry.intent,
|
|
5311
|
+
files: entry.claim_files,
|
|
5312
|
+
priority: getPriorityLevel(entry.priority),
|
|
5313
|
+
position: entry.position,
|
|
5314
|
+
estimated_wait_minutes: entry.estimated_wait_minutes,
|
|
5315
|
+
created_at: entry.created_at
|
|
5316
|
+
};
|
|
5317
|
+
}
|
|
5318
|
+
function formatOutgoingCoordination(entry) {
|
|
5319
|
+
return {
|
|
5320
|
+
queue_id: entry.id,
|
|
5321
|
+
owner_claim_id: entry.claim_id,
|
|
5322
|
+
owner_session_id: entry.owner_session_id,
|
|
5323
|
+
owner_session_name: entry.claim_session_name,
|
|
5324
|
+
owner_intent: entry.claim_intent,
|
|
5325
|
+
intent: entry.intent,
|
|
5326
|
+
files: entry.claim_files,
|
|
5327
|
+
priority: getPriorityLevel(entry.priority),
|
|
5328
|
+
position: entry.position,
|
|
5329
|
+
estimated_wait_minutes: entry.estimated_wait_minutes,
|
|
5330
|
+
created_at: entry.created_at
|
|
5331
|
+
};
|
|
5332
|
+
}
|
|
5151
5333
|
var sessionTools = [
|
|
5152
5334
|
{
|
|
5153
5335
|
name: "collab_session_start",
|
|
@@ -5381,6 +5563,8 @@ Decisions:
|
|
|
5381
5563
|
const sessionsWithClaims = await Promise.all(
|
|
5382
5564
|
sessions.map(async (session) => {
|
|
5383
5565
|
const claims = await listClaims(db, { session_id: session.id, status: "active" });
|
|
5566
|
+
const incomingCoordination = await listQueue(db, { owner_session_id: session.id });
|
|
5567
|
+
const outgoingCoordination = await listQueue(db, { session_id: session.id });
|
|
5384
5568
|
return {
|
|
5385
5569
|
id: session.id,
|
|
5386
5570
|
name: session.name,
|
|
@@ -5396,6 +5580,11 @@ Decisions:
|
|
|
5396
5580
|
priority: getPriorityLevel(c.priority),
|
|
5397
5581
|
created_at: c.created_at
|
|
5398
5582
|
})),
|
|
5583
|
+
pending_coordination: {
|
|
5584
|
+
incoming: incomingCoordination.length,
|
|
5585
|
+
outgoing: outgoingCoordination.length
|
|
5586
|
+
},
|
|
5587
|
+
coordination_requests: incomingCoordination.map(formatIncomingCoordination),
|
|
5399
5588
|
last_heartbeat: session.last_heartbeat
|
|
5400
5589
|
};
|
|
5401
5590
|
})
|
|
@@ -5479,6 +5668,8 @@ Decisions:
|
|
|
5479
5668
|
const claims = await listClaims(db, { session_id: sessionId, status: "active" });
|
|
5480
5669
|
const memories = await getActiveMemories(db, sessionId, { priority_threshold: 70, max_items: 10 });
|
|
5481
5670
|
const allSessions = await listSessions(db, { project_root: session.project_root });
|
|
5671
|
+
const incomingCoordination = await listQueue(db, { owner_session_id: sessionId });
|
|
5672
|
+
const outgoingCoordination = await listQueue(db, { session_id: sessionId });
|
|
5482
5673
|
return successResponse({
|
|
5483
5674
|
session: {
|
|
5484
5675
|
id: session.id,
|
|
@@ -5492,6 +5683,14 @@ Decisions:
|
|
|
5492
5683
|
})),
|
|
5493
5684
|
active_memories: memories.length,
|
|
5494
5685
|
other_sessions: allSessions.filter((s) => s.id !== sessionId).length,
|
|
5686
|
+
pending_coordination: {
|
|
5687
|
+
incoming: incomingCoordination.length,
|
|
5688
|
+
outgoing: outgoingCoordination.length
|
|
5689
|
+
},
|
|
5690
|
+
coordination_requests: {
|
|
5691
|
+
incoming: incomingCoordination.map(formatIncomingCoordination),
|
|
5692
|
+
outgoing: outgoingCoordination.map(formatOutgoingCoordination)
|
|
5693
|
+
},
|
|
5495
5694
|
message: `Session active. ${claims.length} claim(s), ${memories.length} memories.`
|
|
5496
5695
|
});
|
|
5497
5696
|
}
|
|
@@ -5501,11 +5700,155 @@ Decisions:
|
|
|
5501
5700
|
}
|
|
5502
5701
|
|
|
5503
5702
|
// src/mcp/tools/claim.ts
|
|
5703
|
+
function parseSessionConfig(session) {
|
|
5704
|
+
if (!session.config) {
|
|
5705
|
+
return DEFAULT_SESSION_CONFIG;
|
|
5706
|
+
}
|
|
5707
|
+
try {
|
|
5708
|
+
return { ...DEFAULT_SESSION_CONFIG, ...JSON.parse(session.config) };
|
|
5709
|
+
} catch {
|
|
5710
|
+
return DEFAULT_SESSION_CONFIG;
|
|
5711
|
+
}
|
|
5712
|
+
}
|
|
5713
|
+
function mapConflict(conflict, ownerSession) {
|
|
5714
|
+
return {
|
|
5715
|
+
claim_id: conflict.claim_id,
|
|
5716
|
+
session_id: conflict.session_id,
|
|
5717
|
+
session_name: conflict.session_name,
|
|
5718
|
+
file: conflict.file_path,
|
|
5719
|
+
intent: conflict.intent,
|
|
5720
|
+
scope: conflict.scope,
|
|
5721
|
+
created_at: conflict.created_at,
|
|
5722
|
+
conflict_level: conflict.conflict_level,
|
|
5723
|
+
symbol_name: conflict.symbol_name ?? null,
|
|
5724
|
+
symbol_type: conflict.symbol_type ?? null,
|
|
5725
|
+
current_task: ownerSession?.current_task ?? null,
|
|
5726
|
+
last_heartbeat: ownerSession?.last_heartbeat ?? null
|
|
5727
|
+
};
|
|
5728
|
+
}
|
|
5729
|
+
async function formatConflicts(db, conflicts) {
|
|
5730
|
+
const sessions = /* @__PURE__ */ new Map();
|
|
5731
|
+
for (const conflict of conflicts) {
|
|
5732
|
+
if (!sessions.has(conflict.session_id)) {
|
|
5733
|
+
sessions.set(conflict.session_id, await getSession(db, conflict.session_id));
|
|
5734
|
+
}
|
|
5735
|
+
}
|
|
5736
|
+
return conflicts.map((conflict) => mapConflict(conflict, sessions.get(conflict.session_id) ?? null));
|
|
5737
|
+
}
|
|
5738
|
+
function uniqueBlockedFiles(conflicts) {
|
|
5739
|
+
return Array.from(new Set(conflicts.map((conflict) => conflict.file_path)));
|
|
5740
|
+
}
|
|
5741
|
+
function filterSafeSymbols(symbols, safeFiles) {
|
|
5742
|
+
if (!symbols || symbols.length === 0) {
|
|
5743
|
+
return void 0;
|
|
5744
|
+
}
|
|
5745
|
+
const safeFileSet = new Set(safeFiles);
|
|
5746
|
+
const safeSymbols = symbols.filter((symbol) => safeFileSet.has(symbol.file));
|
|
5747
|
+
return safeSymbols.length > 0 ? safeSymbols : void 0;
|
|
5748
|
+
}
|
|
5749
|
+
function getCoordinationRecommendation(symbols, conflicts) {
|
|
5750
|
+
const hasFileLevelConflict = conflicts.some((conflict) => conflict.conflict_level === "file");
|
|
5751
|
+
if (symbols && symbols.length > 0 && hasFileLevelConflict) {
|
|
5752
|
+
return "provide_symbols_or_wait";
|
|
5753
|
+
}
|
|
5754
|
+
return "wait_for_release_or_coordinate";
|
|
5755
|
+
}
|
|
5756
|
+
async function createTrackedClaim(db, input) {
|
|
5757
|
+
const { claim } = await createClaim(db, {
|
|
5758
|
+
session_id: input.session_id,
|
|
5759
|
+
files: input.files,
|
|
5760
|
+
symbols: input.symbols,
|
|
5761
|
+
intent: input.intent,
|
|
5762
|
+
scope: input.scope,
|
|
5763
|
+
priority: input.priority
|
|
5764
|
+
});
|
|
5765
|
+
await logAuditEvent(db, {
|
|
5766
|
+
session_id: input.session_id,
|
|
5767
|
+
action: "claim_created",
|
|
5768
|
+
entity_type: "claim",
|
|
5769
|
+
entity_id: claim.id,
|
|
5770
|
+
metadata: { files: input.files, intent: input.intent }
|
|
5771
|
+
});
|
|
5772
|
+
await saveMemory(db, input.session_id, {
|
|
5773
|
+
category: "state",
|
|
5774
|
+
key: `claim_${claim.id}`,
|
|
5775
|
+
content: `Working on: ${input.intent}
|
|
5776
|
+
Files: ${input.files.join(", ")}`,
|
|
5777
|
+
priority: 60,
|
|
5778
|
+
related_claim_id: claim.id,
|
|
5779
|
+
metadata: { claim_id: claim.id, files: input.files }
|
|
5780
|
+
});
|
|
5781
|
+
return claim.id;
|
|
5782
|
+
}
|
|
5783
|
+
async function createCoordinationRequests(db, input, conflicts) {
|
|
5784
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
5785
|
+
for (const conflict of conflicts) {
|
|
5786
|
+
const existing = grouped.get(conflict.claim_id);
|
|
5787
|
+
if (existing) {
|
|
5788
|
+
existing.files.add(conflict.file_path);
|
|
5789
|
+
} else {
|
|
5790
|
+
grouped.set(conflict.claim_id, {
|
|
5791
|
+
conflict,
|
|
5792
|
+
files: /* @__PURE__ */ new Set([conflict.file_path])
|
|
5793
|
+
});
|
|
5794
|
+
}
|
|
5795
|
+
}
|
|
5796
|
+
const requests = [];
|
|
5797
|
+
for (const { conflict, files } of grouped.values()) {
|
|
5798
|
+
const fileList = Array.from(files);
|
|
5799
|
+
const queueEntry = await joinQueue(db, {
|
|
5800
|
+
claim_id: conflict.claim_id,
|
|
5801
|
+
session_id: input.session_id,
|
|
5802
|
+
intent: input.intent,
|
|
5803
|
+
priority: input.priority,
|
|
5804
|
+
scope: input.scope
|
|
5805
|
+
});
|
|
5806
|
+
await logAuditEvent(db, {
|
|
5807
|
+
session_id: input.session_id,
|
|
5808
|
+
action: "queue_joined",
|
|
5809
|
+
entity_type: "queue",
|
|
5810
|
+
entity_id: queueEntry.id,
|
|
5811
|
+
metadata: {
|
|
5812
|
+
claim_id: conflict.claim_id,
|
|
5813
|
+
files: fileList,
|
|
5814
|
+
conflicting_session_id: conflict.session_id,
|
|
5815
|
+
conflicting_session_name: conflict.session_name ?? void 0,
|
|
5816
|
+
priority: queueEntry.priority,
|
|
5817
|
+
scope: queueEntry.scope
|
|
5818
|
+
}
|
|
5819
|
+
});
|
|
5820
|
+
await createNotification(db, {
|
|
5821
|
+
session_id: conflict.session_id,
|
|
5822
|
+
type: "conflict_detected",
|
|
5823
|
+
title: "Coordination requested",
|
|
5824
|
+
message: `Another session is waiting to coordinate work on ${fileList.join(", ")}.`,
|
|
5825
|
+
reference_type: "claim",
|
|
5826
|
+
reference_id: conflict.claim_id,
|
|
5827
|
+
metadata: {
|
|
5828
|
+
claim_id: conflict.claim_id,
|
|
5829
|
+
files: fileList,
|
|
5830
|
+
conflicting_session_id: input.session_id
|
|
5831
|
+
}
|
|
5832
|
+
});
|
|
5833
|
+
requests.push({
|
|
5834
|
+
queue_id: queueEntry.id,
|
|
5835
|
+
owner_claim_id: conflict.claim_id,
|
|
5836
|
+
owner_session_id: conflict.session_id,
|
|
5837
|
+
owner_session_name: conflict.session_name,
|
|
5838
|
+
requested_by_session_id: input.session_id,
|
|
5839
|
+
requested_intent: input.intent,
|
|
5840
|
+
files: fileList,
|
|
5841
|
+
position: queueEntry.position,
|
|
5842
|
+
estimated_wait_minutes: queueEntry.estimated_wait_minutes
|
|
5843
|
+
});
|
|
5844
|
+
}
|
|
5845
|
+
return requests;
|
|
5846
|
+
}
|
|
5504
5847
|
var claimTools = [
|
|
5505
5848
|
{
|
|
5506
5849
|
name: "collab_claim",
|
|
5507
5850
|
description: `Unified tool for file/symbol claims. Use action parameter to:
|
|
5508
|
-
- "create": Declare files you're about to modify
|
|
5851
|
+
- "create": Declare files you're about to modify; smart mode queues blocked files for coordination
|
|
5509
5852
|
- "check": Check if files are being worked on (ALWAYS call before editing)
|
|
5510
5853
|
- "release": Release a claim when done
|
|
5511
5854
|
- "list": List all active claims`,
|
|
@@ -5609,67 +5952,111 @@ async function handleClaimTool(db, name, args) {
|
|
|
5609
5952
|
if (!sessionResult.valid) {
|
|
5610
5953
|
return sessionResult.error;
|
|
5611
5954
|
}
|
|
5955
|
+
const config = parseSessionConfig(sessionResult.session);
|
|
5612
5956
|
const symbolFiles = (input.symbols ?? []).map((symbol) => symbol.file);
|
|
5613
5957
|
const files = Array.from(/* @__PURE__ */ new Set([...input.files ?? [], ...symbolFiles]));
|
|
5614
5958
|
const conflicts = await checkConflicts(db, files, input.session_id, input.symbols);
|
|
5615
|
-
|
|
5959
|
+
const formattedConflicts = await formatConflicts(db, conflicts);
|
|
5960
|
+
const blockedFiles = uniqueBlockedFiles(conflicts);
|
|
5961
|
+
const safeFiles = files.filter((file) => !blockedFiles.includes(file));
|
|
5962
|
+
const safeSymbols = filterSafeSymbols(input.symbols, safeFiles);
|
|
5963
|
+
if (conflicts.length > 0 && config.mode === "strict") {
|
|
5616
5964
|
return successResponse({
|
|
5617
5965
|
success: false,
|
|
5618
5966
|
status: "blocked_by_conflicts",
|
|
5619
5967
|
files,
|
|
5620
5968
|
symbols: input.symbols ?? [],
|
|
5621
|
-
|
|
5622
|
-
|
|
5623
|
-
|
|
5624
|
-
|
|
5625
|
-
|
|
5969
|
+
claimed_files: [],
|
|
5970
|
+
safe_files: safeFiles,
|
|
5971
|
+
blocked_files: blockedFiles,
|
|
5972
|
+
conflicts: formattedConflicts,
|
|
5973
|
+
recommendation: "coordinate_before_editing",
|
|
5626
5974
|
message: `Claim not created. ${conflicts.length} conflict(s) detected. Coordinate before proceeding.`
|
|
5627
5975
|
});
|
|
5628
5976
|
}
|
|
5629
|
-
const { claim } = await createClaim(db, {
|
|
5630
|
-
session_id: input.session_id,
|
|
5631
|
-
files,
|
|
5632
|
-
symbols: input.symbols,
|
|
5633
|
-
intent: input.intent,
|
|
5634
|
-
scope: input.scope,
|
|
5635
|
-
priority: input.priority
|
|
5636
|
-
});
|
|
5637
|
-
await logAuditEvent(db, {
|
|
5638
|
-
session_id: input.session_id,
|
|
5639
|
-
action: "claim_created",
|
|
5640
|
-
entity_type: "claim",
|
|
5641
|
-
entity_id: claim.id,
|
|
5642
|
-
metadata: { files, intent: input.intent }
|
|
5643
|
-
});
|
|
5644
|
-
await saveMemory(db, input.session_id, {
|
|
5645
|
-
category: "state",
|
|
5646
|
-
key: `claim_${claim.id}`,
|
|
5647
|
-
content: `Working on: ${input.intent}
|
|
5648
|
-
Files: ${files.join(", ")}`,
|
|
5649
|
-
priority: 60,
|
|
5650
|
-
related_claim_id: claim.id,
|
|
5651
|
-
metadata: { claim_id: claim.id, files }
|
|
5652
|
-
});
|
|
5653
5977
|
if (conflicts.length > 0) {
|
|
5978
|
+
if (!input.allow_conflicts) {
|
|
5979
|
+
const coordinationRequests = await createCoordinationRequests(db, {
|
|
5980
|
+
session_id: input.session_id,
|
|
5981
|
+
intent: input.intent,
|
|
5982
|
+
scope: input.scope,
|
|
5983
|
+
priority: input.priority
|
|
5984
|
+
}, conflicts);
|
|
5985
|
+
if (config.mode === "smart" && safeFiles.length > 0) {
|
|
5986
|
+
const claimId3 = await createTrackedClaim(db, {
|
|
5987
|
+
session_id: input.session_id,
|
|
5988
|
+
files: safeFiles,
|
|
5989
|
+
symbols: safeSymbols,
|
|
5990
|
+
intent: input.intent,
|
|
5991
|
+
scope: input.scope,
|
|
5992
|
+
priority: input.priority
|
|
5993
|
+
});
|
|
5994
|
+
return successResponse({
|
|
5995
|
+
success: true,
|
|
5996
|
+
claim_id: claimId3,
|
|
5997
|
+
status: "partial_claim_created",
|
|
5998
|
+
files,
|
|
5999
|
+
claimed_files: safeFiles,
|
|
6000
|
+
safe_files: safeFiles,
|
|
6001
|
+
blocked_files: blockedFiles,
|
|
6002
|
+
symbols: safeSymbols ?? [],
|
|
6003
|
+
conflicts: formattedConflicts,
|
|
6004
|
+
coordination_requests: coordinationRequests,
|
|
6005
|
+
recommendation: getCoordinationRecommendation(input.symbols, conflicts),
|
|
6006
|
+
message: `Claim created for safe files only. Coordinate before editing blocked files: [${blockedFiles.join(", ")}].`
|
|
6007
|
+
});
|
|
6008
|
+
}
|
|
6009
|
+
return successResponse({
|
|
6010
|
+
success: false,
|
|
6011
|
+
status: "waiting_for_coordination",
|
|
6012
|
+
files,
|
|
6013
|
+
claimed_files: [],
|
|
6014
|
+
safe_files: [],
|
|
6015
|
+
blocked_files: blockedFiles,
|
|
6016
|
+
symbols: input.symbols ?? [],
|
|
6017
|
+
conflicts: formattedConflicts,
|
|
6018
|
+
coordination_requests: coordinationRequests,
|
|
6019
|
+
recommendation: getCoordinationRecommendation(input.symbols, conflicts),
|
|
6020
|
+
message: `Claim not created. Waiting for coordination on blocked files: [${blockedFiles.join(", ")}].`
|
|
6021
|
+
});
|
|
6022
|
+
}
|
|
6023
|
+
const claimId2 = await createTrackedClaim(db, {
|
|
6024
|
+
session_id: input.session_id,
|
|
6025
|
+
files,
|
|
6026
|
+
symbols: input.symbols,
|
|
6027
|
+
intent: input.intent,
|
|
6028
|
+
scope: input.scope,
|
|
6029
|
+
priority: input.priority
|
|
6030
|
+
});
|
|
5654
6031
|
return successResponse({
|
|
5655
6032
|
success: true,
|
|
5656
|
-
claim_id:
|
|
6033
|
+
claim_id: claimId2,
|
|
5657
6034
|
status: "created_with_conflicts",
|
|
5658
6035
|
files,
|
|
6036
|
+
claimed_files: files,
|
|
6037
|
+
safe_files: safeFiles,
|
|
6038
|
+
blocked_files: blockedFiles,
|
|
5659
6039
|
symbols: input.symbols ?? [],
|
|
5660
|
-
conflicts:
|
|
5661
|
-
session_name: c.session_name,
|
|
5662
|
-
file: c.file_path,
|
|
5663
|
-
intent: c.intent
|
|
5664
|
-
})),
|
|
6040
|
+
conflicts: formattedConflicts,
|
|
5665
6041
|
warning: `\u26A0\uFE0F ${conflicts.length} conflict(s) detected. Coordinate before proceeding.`
|
|
5666
6042
|
});
|
|
5667
6043
|
}
|
|
6044
|
+
const claimId = await createTrackedClaim(db, {
|
|
6045
|
+
session_id: input.session_id,
|
|
6046
|
+
files,
|
|
6047
|
+
symbols: input.symbols,
|
|
6048
|
+
intent: input.intent,
|
|
6049
|
+
scope: input.scope,
|
|
6050
|
+
priority: input.priority
|
|
6051
|
+
});
|
|
5668
6052
|
return successResponse({
|
|
5669
6053
|
success: true,
|
|
5670
|
-
claim_id:
|
|
6054
|
+
claim_id: claimId,
|
|
5671
6055
|
status: "created",
|
|
5672
6056
|
files,
|
|
6057
|
+
claimed_files: files,
|
|
6058
|
+
safe_files: files,
|
|
6059
|
+
blocked_files: [],
|
|
5673
6060
|
symbols: input.symbols ?? [],
|
|
5674
6061
|
intent: input.intent,
|
|
5675
6062
|
message: "Claim created successfully."
|
|
@@ -5687,6 +6074,7 @@ Files: ${files.join(", ")}`,
|
|
|
5687
6074
|
const excludeSelf = input.exclude_self ?? true;
|
|
5688
6075
|
const excludeSessionId = excludeSelf ? input.session_id : void 0;
|
|
5689
6076
|
const conflicts = await checkConflicts(db, input.files, excludeSessionId, input.symbols);
|
|
6077
|
+
const formattedConflicts = await formatConflicts(db, conflicts);
|
|
5690
6078
|
if (conflicts.length === 0) {
|
|
5691
6079
|
return successResponse({
|
|
5692
6080
|
safe: true,
|
|
@@ -5707,11 +6095,7 @@ Files: ${files.join(", ")}`,
|
|
|
5707
6095
|
recommendation: safeFiles.length > 0 ? "proceed_safe_only" : "abort",
|
|
5708
6096
|
safe_files: safeFiles,
|
|
5709
6097
|
blocked_files: Array.from(blockedFiles),
|
|
5710
|
-
conflicts:
|
|
5711
|
-
session_name: c.session_name,
|
|
5712
|
-
file: c.file_path,
|
|
5713
|
-
intent: c.intent
|
|
5714
|
-
})),
|
|
6098
|
+
conflicts: formattedConflicts,
|
|
5715
6099
|
message: safeFiles.length > 0 ? `Edit ONLY safe files: [${safeFiles.join(", ")}]. Skip blocked files.` : "All files blocked. Coordinate with other session(s)."
|
|
5716
6100
|
});
|
|
5717
6101
|
}
|
|
@@ -5746,6 +6130,7 @@ Files: ${files.join(", ")}`,
|
|
|
5746
6130
|
if (claim.status !== "active") {
|
|
5747
6131
|
return errorResponse(ERROR_CODES.CLAIM_ALREADY_RELEASED, `Claim already ${claim.status}`);
|
|
5748
6132
|
}
|
|
6133
|
+
const notifications_sent = await notifyQueueOnClaimRelease(db, input.claim_id, input.session_id, claim.files);
|
|
5749
6134
|
await releaseClaim(db, input.claim_id, {
|
|
5750
6135
|
status,
|
|
5751
6136
|
summary: input.summary
|
|
@@ -5762,6 +6147,7 @@ Files: ${files.join(", ")}`,
|
|
|
5762
6147
|
success: true,
|
|
5763
6148
|
claim_id: input.claim_id,
|
|
5764
6149
|
files: claim.files,
|
|
6150
|
+
notifications_sent,
|
|
5765
6151
|
message: `Claim ${status} released. Files now available.`
|
|
5766
6152
|
});
|
|
5767
6153
|
}
|
|
@@ -6234,4 +6620,4 @@ export {
|
|
|
6234
6620
|
getMcpTools,
|
|
6235
6621
|
handleMcpRequest
|
|
6236
6622
|
};
|
|
6237
|
-
//# sourceMappingURL=chunk-
|
|
6623
|
+
//# sourceMappingURL=chunk-UDJMG3TR.js.map
|