@brainervirus/workit-cursor 2.2.0 → 2.2.2
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/.cursor-plugin/plugin.json +1 -1
- package/dist/cursor-session-start.js +90 -10
- package/dist/mcp-server.js +88 -7
- package/dist/workit-hook.js +92 -9
- package/package.json +3 -3
|
@@ -10442,7 +10442,7 @@ class TaskStore {
|
|
|
10442
10442
|
if (!current.data)
|
|
10443
10443
|
return failure2("not_found", "workspace not found");
|
|
10444
10444
|
if (current.data.revision !== expected)
|
|
10445
|
-
return this.
|
|
10445
|
+
return this.workspaceConflict(expected, current.data.revision);
|
|
10446
10446
|
const previousBytes = this.snapshotBytes(this.workspacePath);
|
|
10447
10447
|
if (!previousBytes)
|
|
10448
10448
|
return failure2("storage_error", "workspace snapshot disappeared during mutation");
|
|
@@ -10485,7 +10485,7 @@ class TaskStore {
|
|
|
10485
10485
|
if (task.data.revision !== expected)
|
|
10486
10486
|
return this.conflict(expected, task.data.revision);
|
|
10487
10487
|
if (workspace.data.revision !== input.expectedWorkspaceRevision)
|
|
10488
|
-
return this.
|
|
10488
|
+
return this.workspaceConflict(input.expectedWorkspaceRevision, workspace.data.revision);
|
|
10489
10489
|
const previousWorkspaceBytes = this.snapshotBytes(this.workspacePath);
|
|
10490
10490
|
const previousTaskBytes = this.snapshotBytes(this.taskPath(input.taskId));
|
|
10491
10491
|
if (!previousWorkspaceBytes || !previousTaskBytes)
|
|
@@ -11295,6 +11295,9 @@ class TaskStore {
|
|
|
11295
11295
|
actualRevision: actual
|
|
11296
11296
|
});
|
|
11297
11297
|
}
|
|
11298
|
+
workspaceConflict(expected, actual) {
|
|
11299
|
+
return failure2("revision_conflict", "workspace revision does not match; omit expectedWorkspaceRevision to use the current record", { expectedWorkspaceRevision: expected, actualWorkspaceRevision: actual });
|
|
11300
|
+
}
|
|
11298
11301
|
taskPath(taskId) {
|
|
11299
11302
|
return path7.join(this.tasksDir, `${taskId}.json`);
|
|
11300
11303
|
}
|
|
@@ -13036,8 +13039,10 @@ values are still concurrency-checked, so never copy revisions between calls.
|
|
|
13036
13039
|
A busy result means another live Workit call holds the checkout lock: retry the
|
|
13037
13040
|
same call; it is not a recovery condition. A lock left by a dead process is
|
|
13038
13041
|
reclaimed on the next write, and \`workit doctor --fix-lock\` clears it on demand.
|
|
13039
|
-
|
|
13040
|
-
|
|
13042
|
+
An omitted revision absorbs a concurrent write: Workit re-reads, re-checks
|
|
13043
|
+
policy, and reapplies the call, returning busy under persistent contention. A
|
|
13044
|
+
revision_conflict means a revision you passed is stale: re-read the record
|
|
13045
|
+
before deciding whether to retry.
|
|
13041
13046
|
A solo edit does not need writer acquisition; use it when concurrent checkout
|
|
13042
13047
|
writers need coordination. Record only observed facts and checks. Evidence can
|
|
13043
13048
|
become stale when its bound candidate changes; reconcile findings against the
|
|
@@ -13254,6 +13259,9 @@ class WorkitCore {
|
|
|
13254
13259
|
if (input.expectedWorkspaceRevision === undefined)
|
|
13255
13260
|
input.expectedWorkspaceRevision = workspace ? workspace.revision : null;
|
|
13256
13261
|
}
|
|
13262
|
+
retryOmittedRevisions(request, run) {
|
|
13263
|
+
return retryFilledRevisions(filledRevisions(request), run);
|
|
13264
|
+
}
|
|
13257
13265
|
helperEntry(task, requireSession = false, requireActive = false) {
|
|
13258
13266
|
const workerId = this.context.workerId ?? null;
|
|
13259
13267
|
if (workerId === null)
|
|
@@ -13285,6 +13293,9 @@ class WorkitCore {
|
|
|
13285
13293
|
return null;
|
|
13286
13294
|
}
|
|
13287
13295
|
state(request) {
|
|
13296
|
+
return request?.action === "import" ? this.retryOmittedRevisions(request, () => this.stateOnce(request)) : this.stateOnce(request);
|
|
13297
|
+
}
|
|
13298
|
+
stateOnce(request) {
|
|
13288
13299
|
const root = this.contextRootError();
|
|
13289
13300
|
if (!root.ok)
|
|
13290
13301
|
return root;
|
|
@@ -13386,6 +13397,9 @@ class WorkitCore {
|
|
|
13386
13397
|
});
|
|
13387
13398
|
}
|
|
13388
13399
|
task(request) {
|
|
13400
|
+
return this.retryOmittedRevisions(request, () => this.taskOnce(request));
|
|
13401
|
+
}
|
|
13402
|
+
taskOnce(request) {
|
|
13389
13403
|
const root = this.contextRootError();
|
|
13390
13404
|
if (!root.ok)
|
|
13391
13405
|
return root;
|
|
@@ -13485,6 +13499,9 @@ class WorkitCore {
|
|
|
13485
13499
|
}
|
|
13486
13500
|
}
|
|
13487
13501
|
policy(request) {
|
|
13502
|
+
return this.retryOmittedRevisions(request, () => this.policyOnce(request));
|
|
13503
|
+
}
|
|
13504
|
+
policyOnce(request) {
|
|
13488
13505
|
const root = this.contextRootError();
|
|
13489
13506
|
if (!root.ok)
|
|
13490
13507
|
return root;
|
|
@@ -13531,6 +13548,9 @@ class WorkitCore {
|
|
|
13531
13548
|
return success(changed.data.revision, null, changed.data.policy);
|
|
13532
13549
|
}
|
|
13533
13550
|
evidence(request) {
|
|
13551
|
+
return this.retryOmittedRevisions(request, () => this.evidenceOnce(request));
|
|
13552
|
+
}
|
|
13553
|
+
evidenceOnce(request) {
|
|
13534
13554
|
const root = this.contextRootError();
|
|
13535
13555
|
if (!root.ok)
|
|
13536
13556
|
return root;
|
|
@@ -13615,6 +13635,23 @@ class WorkitCore {
|
|
|
13615
13635
|
observeStandingDecision(request) {
|
|
13616
13636
|
return this.recordDecision(request, undefined, true, true);
|
|
13617
13637
|
}
|
|
13638
|
+
commitTask(taskId, expected, filled, recheck, update) {
|
|
13639
|
+
let revision = expected;
|
|
13640
|
+
let first = true;
|
|
13641
|
+
return retryFilledRevisions({ task: filled, workspace: false }, () => {
|
|
13642
|
+
if (!first) {
|
|
13643
|
+
const fresh = this.store.readTask(taskId);
|
|
13644
|
+
if (!fresh.ok)
|
|
13645
|
+
return fresh;
|
|
13646
|
+
const checked = recheck(fresh.data);
|
|
13647
|
+
if (!checked.ok)
|
|
13648
|
+
return checked;
|
|
13649
|
+
revision = fresh.data.revision;
|
|
13650
|
+
}
|
|
13651
|
+
first = false;
|
|
13652
|
+
return this.store.mutateTask(taskId, revision, update, trustedNow(this.context));
|
|
13653
|
+
});
|
|
13654
|
+
}
|
|
13618
13655
|
recordDecision(request, nativeObservation, nativeRequired = false, standing = false) {
|
|
13619
13656
|
const root = this.contextRootError();
|
|
13620
13657
|
if (!root.ok)
|
|
@@ -13639,6 +13676,7 @@ class WorkitCore {
|
|
|
13639
13676
|
return task;
|
|
13640
13677
|
if ((this.context.workerId ?? null) !== null)
|
|
13641
13678
|
return failure2("permission_denied", "helpers cannot record or revoke decisions");
|
|
13679
|
+
const filled = input.expectedRevision === undefined;
|
|
13642
13680
|
this.fillRevisions(input, task.data);
|
|
13643
13681
|
if (input.action === "record") {
|
|
13644
13682
|
if (task.data.status === "closed")
|
|
@@ -13687,7 +13725,20 @@ class WorkitCore {
|
|
|
13687
13725
|
const nativeProvenance = native?.ok ? retireNativeAuthority(native.data) : null;
|
|
13688
13726
|
if (native?.ok && !nativeProvenance)
|
|
13689
13727
|
return failure2("permission_denied", "native decision authority was retired");
|
|
13690
|
-
|
|
13728
|
+
let standingProvenance = standingApproval?.ok === true ? standingApproval.data : null;
|
|
13729
|
+
const recheck = (fresh) => {
|
|
13730
|
+
if (fresh.status === "closed")
|
|
13731
|
+
return failure2("invalid_transition", "closed task cannot record a decision");
|
|
13732
|
+
if (!standingProvenance)
|
|
13733
|
+
return success(null, null, null);
|
|
13734
|
+
const again = verifyStandingApproval(this.store.root, fresh, this.context.caller, input.binding);
|
|
13735
|
+
if (again.ok)
|
|
13736
|
+
standingProvenance = again.data;
|
|
13737
|
+
return again;
|
|
13738
|
+
};
|
|
13739
|
+
const changed = this.commitTask(task.data.id, input.expectedRevision, filled, recheck, (current, mutation) => {
|
|
13740
|
+
if (current.status === "closed")
|
|
13741
|
+
return failure2("invalid_transition", "closed task cannot record a decision");
|
|
13691
13742
|
const latestContent = verifyDecisionContent(this.store, input.binding);
|
|
13692
13743
|
if (!latestContent.ok)
|
|
13693
13744
|
return latestContent;
|
|
@@ -13699,14 +13750,14 @@ class WorkitCore {
|
|
|
13699
13750
|
const entry = {
|
|
13700
13751
|
id: newId(),
|
|
13701
13752
|
recordedAt: mutation.now,
|
|
13702
|
-
provenance:
|
|
13753
|
+
provenance: standingProvenance ?? nativeProvenance ?? provenance(this.context, "agent_reported"),
|
|
13703
13754
|
data
|
|
13704
13755
|
};
|
|
13705
13756
|
return success(mutation.revision, null, {
|
|
13706
13757
|
...current,
|
|
13707
13758
|
decisions: [...current.decisions, entry]
|
|
13708
13759
|
});
|
|
13709
|
-
}
|
|
13760
|
+
});
|
|
13710
13761
|
if (!changed.ok)
|
|
13711
13762
|
return changed;
|
|
13712
13763
|
return success(changed.data.revision, null, changed.data.decisions.at(-1));
|
|
@@ -13720,7 +13771,7 @@ class WorkitCore {
|
|
|
13720
13771
|
return failure2("invalid_transition", "decision is already revoked");
|
|
13721
13772
|
if (decision.data.consumption)
|
|
13722
13773
|
return failure2("permission_denied", "consumed decision cannot be revoked");
|
|
13723
|
-
const changed = this.
|
|
13774
|
+
const changed = this.commitTask(task.data.id, input.expectedRevision, filled, () => success(null, null, null), (current, mutation) => {
|
|
13724
13775
|
const entry = current.decisions.find((candidate) => candidate.id === input.decisionId);
|
|
13725
13776
|
if (!entry)
|
|
13726
13777
|
return failure2("not_found", "decision not found");
|
|
@@ -13737,13 +13788,16 @@ class WorkitCore {
|
|
|
13737
13788
|
...current,
|
|
13738
13789
|
decisions: current.decisions.map((candidate) => candidate.id === input.decisionId ? updated : candidate)
|
|
13739
13790
|
});
|
|
13740
|
-
}
|
|
13791
|
+
});
|
|
13741
13792
|
if (!changed.ok)
|
|
13742
13793
|
return changed;
|
|
13743
13794
|
const entry = changed.data.decisions.find((candidate) => candidate.id === input.decisionId);
|
|
13744
13795
|
return entry ? success(changed.data.revision, null, entry) : failure2("recovery_required", "decision disappeared");
|
|
13745
13796
|
}
|
|
13746
13797
|
finding(request) {
|
|
13798
|
+
return this.retryOmittedRevisions(request, () => this.findingOnce(request));
|
|
13799
|
+
}
|
|
13800
|
+
findingOnce(request) {
|
|
13747
13801
|
const root = this.contextRootError();
|
|
13748
13802
|
if (!root.ok)
|
|
13749
13803
|
return root;
|
|
@@ -13867,6 +13921,9 @@ class WorkitCore {
|
|
|
13867
13921
|
return entry ? success(changed.data.revision, null, entry) : failure2("recovery_required", "finding disappeared");
|
|
13868
13922
|
}
|
|
13869
13923
|
worker(request) {
|
|
13924
|
+
return this.retryOmittedRevisions(request, () => this.workerOnce(request));
|
|
13925
|
+
}
|
|
13926
|
+
workerOnce(request) {
|
|
13870
13927
|
const root = this.contextRootError();
|
|
13871
13928
|
if (!root.ok)
|
|
13872
13929
|
return root;
|
|
@@ -14214,6 +14271,9 @@ class WorkitCore {
|
|
|
14214
14271
|
return applied;
|
|
14215
14272
|
}
|
|
14216
14273
|
writer(request) {
|
|
14274
|
+
return this.retryOmittedRevisions(request, () => this.writerOnce(request));
|
|
14275
|
+
}
|
|
14276
|
+
writerOnce(request) {
|
|
14217
14277
|
const root = this.contextRootError();
|
|
14218
14278
|
if (!root.ok)
|
|
14219
14279
|
return root;
|
|
@@ -14972,7 +15032,25 @@ var provenance = (context, kind = context.provenanceKind ?? "host_observed") =>
|
|
|
14972
15032
|
return changed;
|
|
14973
15033
|
const updated = changed.data.task.workers.find((candidate) => candidate.id === input.workerId);
|
|
14974
15034
|
return updated ? success(changed.data.task.revision, changed.data.workspace.revision, updated) : failure2("recovery_required", "worker disappeared during lifecycle observation");
|
|
14975
|
-
}, refsWithinScope = (refs, scope) => refs.every((ref) => ref.kind !== "file" || bindingCovers(scope, { description: "", paths: [ref.path], exclusions: [] })),
|
|
15035
|
+
}, refsWithinScope = (refs, scope) => refs.every((ref) => ref.kind !== "file" || bindingCovers(scope, { description: "", paths: [ref.path], exclusions: [] })), REVISION_RETRY_ATTEMPTS = 8, retryPause, filledRevisions = (request) => {
|
|
15036
|
+
const value = typeof request === "object" && request !== null ? request : {};
|
|
15037
|
+
return {
|
|
15038
|
+
task: value.expectedRevision === undefined,
|
|
15039
|
+
workspace: value.expectedWorkspaceRevision === undefined
|
|
15040
|
+
};
|
|
15041
|
+
}, filledConflict = (result, filled) => !result.ok && result.code === "revision_conflict" && (("actualRevision" in result.details) && filled.task || ("actualWorkspaceRevision" in result.details) && filled.workspace), retryFilledRevisions = (filled, run) => {
|
|
15042
|
+
if (!filled.task && !filled.workspace)
|
|
15043
|
+
return run();
|
|
15044
|
+
let result = run();
|
|
15045
|
+
const deadline = Date.now() + defaultLockTimeout();
|
|
15046
|
+
for (let attempt = 1;filledConflict(result, filled); attempt += 1) {
|
|
15047
|
+
if (attempt >= REVISION_RETRY_ATTEMPTS || Date.now() >= deadline)
|
|
15048
|
+
return failure2("busy", "records kept changing under concurrent writers; retry the call");
|
|
15049
|
+
Atomics.wait(retryPause, 0, 0, Math.floor(Math.random() * 4 * attempt) + 1);
|
|
15050
|
+
result = run();
|
|
15051
|
+
}
|
|
15052
|
+
return result;
|
|
15053
|
+
}, trustedNow = (context) => typeof context.now === "function" ? context.now() : context.now, mapRef = (ref, ids) => {
|
|
14976
15054
|
if (ref.kind !== "record")
|
|
14977
15055
|
return ref;
|
|
14978
15056
|
return { ...ref, id: ids.get(ref.id) ?? ref.id };
|
|
@@ -15183,11 +15261,13 @@ var init_task_engine = __esm(() => {
|
|
|
15183
15261
|
init_authority();
|
|
15184
15262
|
init_policy_resolver();
|
|
15185
15263
|
init_auto_approval();
|
|
15264
|
+
init_store_lock();
|
|
15186
15265
|
init_task_context();
|
|
15187
15266
|
init_workers();
|
|
15188
15267
|
init_task_evaluation();
|
|
15189
15268
|
verifiedWorkers = new WeakMap;
|
|
15190
15269
|
dispatchReservations = new WeakMap;
|
|
15270
|
+
retryPause = new Int32Array(new SharedArrayBuffer(4));
|
|
15191
15271
|
});
|
|
15192
15272
|
|
|
15193
15273
|
// packages/workit-core/src/hooks/protocol.ts
|
package/dist/mcp-server.js
CHANGED
|
@@ -19061,7 +19061,7 @@ class TaskStore {
|
|
|
19061
19061
|
if (!current.data)
|
|
19062
19062
|
return failure2("not_found", "workspace not found");
|
|
19063
19063
|
if (current.data.revision !== expected)
|
|
19064
|
-
return this.
|
|
19064
|
+
return this.workspaceConflict(expected, current.data.revision);
|
|
19065
19065
|
const previousBytes = this.snapshotBytes(this.workspacePath);
|
|
19066
19066
|
if (!previousBytes)
|
|
19067
19067
|
return failure2("storage_error", "workspace snapshot disappeared during mutation");
|
|
@@ -19104,7 +19104,7 @@ class TaskStore {
|
|
|
19104
19104
|
if (task.data.revision !== expected)
|
|
19105
19105
|
return this.conflict(expected, task.data.revision);
|
|
19106
19106
|
if (workspace.data.revision !== input.expectedWorkspaceRevision)
|
|
19107
|
-
return this.
|
|
19107
|
+
return this.workspaceConflict(input.expectedWorkspaceRevision, workspace.data.revision);
|
|
19108
19108
|
const previousWorkspaceBytes = this.snapshotBytes(this.workspacePath);
|
|
19109
19109
|
const previousTaskBytes = this.snapshotBytes(this.taskPath(input.taskId));
|
|
19110
19110
|
if (!previousWorkspaceBytes || !previousTaskBytes)
|
|
@@ -19914,6 +19914,9 @@ class TaskStore {
|
|
|
19914
19914
|
actualRevision: actual
|
|
19915
19915
|
});
|
|
19916
19916
|
}
|
|
19917
|
+
workspaceConflict(expected, actual) {
|
|
19918
|
+
return failure2("revision_conflict", "workspace revision does not match; omit expectedWorkspaceRevision to use the current record", { expectedWorkspaceRevision: expected, actualWorkspaceRevision: actual });
|
|
19919
|
+
}
|
|
19917
19920
|
taskPath(taskId) {
|
|
19918
19921
|
return path4.join(this.tasksDir, `${taskId}.json`);
|
|
19919
19922
|
}
|
|
@@ -22966,6 +22969,29 @@ var applyWorkerLifecycle = (input) => {
|
|
|
22966
22969
|
return updated ? success(changed.data.task.revision, changed.data.workspace.revision, updated) : failure2("recovery_required", "worker disappeared during lifecycle observation");
|
|
22967
22970
|
};
|
|
22968
22971
|
var refsWithinScope = (refs, scope) => refs.every((ref) => ref.kind !== "file" || bindingCovers(scope, { description: "", paths: [ref.path], exclusions: [] }));
|
|
22972
|
+
var REVISION_RETRY_ATTEMPTS = 8;
|
|
22973
|
+
var retryPause = new Int32Array(new SharedArrayBuffer(4));
|
|
22974
|
+
var filledRevisions = (request) => {
|
|
22975
|
+
const value = typeof request === "object" && request !== null ? request : {};
|
|
22976
|
+
return {
|
|
22977
|
+
task: value.expectedRevision === undefined,
|
|
22978
|
+
workspace: value.expectedWorkspaceRevision === undefined
|
|
22979
|
+
};
|
|
22980
|
+
};
|
|
22981
|
+
var filledConflict = (result, filled) => !result.ok && result.code === "revision_conflict" && (("actualRevision" in result.details) && filled.task || ("actualWorkspaceRevision" in result.details) && filled.workspace);
|
|
22982
|
+
var retryFilledRevisions = (filled, run) => {
|
|
22983
|
+
if (!filled.task && !filled.workspace)
|
|
22984
|
+
return run();
|
|
22985
|
+
let result = run();
|
|
22986
|
+
const deadline = Date.now() + defaultLockTimeout();
|
|
22987
|
+
for (let attempt = 1;filledConflict(result, filled); attempt += 1) {
|
|
22988
|
+
if (attempt >= REVISION_RETRY_ATTEMPTS || Date.now() >= deadline)
|
|
22989
|
+
return failure2("busy", "records kept changing under concurrent writers; retry the call");
|
|
22990
|
+
Atomics.wait(retryPause, 0, 0, Math.floor(Math.random() * 4 * attempt) + 1);
|
|
22991
|
+
result = run();
|
|
22992
|
+
}
|
|
22993
|
+
return result;
|
|
22994
|
+
};
|
|
22969
22995
|
var trustedNow = (context) => typeof context.now === "function" ? context.now() : context.now;
|
|
22970
22996
|
var mapRef = (ref, ids) => {
|
|
22971
22997
|
if (ref.kind !== "record")
|
|
@@ -23213,6 +23239,9 @@ class WorkitCore {
|
|
|
23213
23239
|
if (input.expectedWorkspaceRevision === undefined)
|
|
23214
23240
|
input.expectedWorkspaceRevision = workspace ? workspace.revision : null;
|
|
23215
23241
|
}
|
|
23242
|
+
retryOmittedRevisions(request, run) {
|
|
23243
|
+
return retryFilledRevisions(filledRevisions(request), run);
|
|
23244
|
+
}
|
|
23216
23245
|
helperEntry(task, requireSession = false, requireActive = false) {
|
|
23217
23246
|
const workerId = this.context.workerId ?? null;
|
|
23218
23247
|
if (workerId === null)
|
|
@@ -23244,6 +23273,9 @@ class WorkitCore {
|
|
|
23244
23273
|
return null;
|
|
23245
23274
|
}
|
|
23246
23275
|
state(request) {
|
|
23276
|
+
return request?.action === "import" ? this.retryOmittedRevisions(request, () => this.stateOnce(request)) : this.stateOnce(request);
|
|
23277
|
+
}
|
|
23278
|
+
stateOnce(request) {
|
|
23247
23279
|
const root = this.contextRootError();
|
|
23248
23280
|
if (!root.ok)
|
|
23249
23281
|
return root;
|
|
@@ -23345,6 +23377,9 @@ class WorkitCore {
|
|
|
23345
23377
|
});
|
|
23346
23378
|
}
|
|
23347
23379
|
task(request) {
|
|
23380
|
+
return this.retryOmittedRevisions(request, () => this.taskOnce(request));
|
|
23381
|
+
}
|
|
23382
|
+
taskOnce(request) {
|
|
23348
23383
|
const root = this.contextRootError();
|
|
23349
23384
|
if (!root.ok)
|
|
23350
23385
|
return root;
|
|
@@ -23444,6 +23479,9 @@ class WorkitCore {
|
|
|
23444
23479
|
}
|
|
23445
23480
|
}
|
|
23446
23481
|
policy(request) {
|
|
23482
|
+
return this.retryOmittedRevisions(request, () => this.policyOnce(request));
|
|
23483
|
+
}
|
|
23484
|
+
policyOnce(request) {
|
|
23447
23485
|
const root = this.contextRootError();
|
|
23448
23486
|
if (!root.ok)
|
|
23449
23487
|
return root;
|
|
@@ -23490,6 +23528,9 @@ class WorkitCore {
|
|
|
23490
23528
|
return success(changed.data.revision, null, changed.data.policy);
|
|
23491
23529
|
}
|
|
23492
23530
|
evidence(request) {
|
|
23531
|
+
return this.retryOmittedRevisions(request, () => this.evidenceOnce(request));
|
|
23532
|
+
}
|
|
23533
|
+
evidenceOnce(request) {
|
|
23493
23534
|
const root = this.contextRootError();
|
|
23494
23535
|
if (!root.ok)
|
|
23495
23536
|
return root;
|
|
@@ -23574,6 +23615,23 @@ class WorkitCore {
|
|
|
23574
23615
|
observeStandingDecision(request) {
|
|
23575
23616
|
return this.recordDecision(request, undefined, true, true);
|
|
23576
23617
|
}
|
|
23618
|
+
commitTask(taskId, expected, filled, recheck, update) {
|
|
23619
|
+
let revision = expected;
|
|
23620
|
+
let first = true;
|
|
23621
|
+
return retryFilledRevisions({ task: filled, workspace: false }, () => {
|
|
23622
|
+
if (!first) {
|
|
23623
|
+
const fresh = this.store.readTask(taskId);
|
|
23624
|
+
if (!fresh.ok)
|
|
23625
|
+
return fresh;
|
|
23626
|
+
const checked = recheck(fresh.data);
|
|
23627
|
+
if (!checked.ok)
|
|
23628
|
+
return checked;
|
|
23629
|
+
revision = fresh.data.revision;
|
|
23630
|
+
}
|
|
23631
|
+
first = false;
|
|
23632
|
+
return this.store.mutateTask(taskId, revision, update, trustedNow(this.context));
|
|
23633
|
+
});
|
|
23634
|
+
}
|
|
23577
23635
|
recordDecision(request, nativeObservation, nativeRequired = false, standing = false) {
|
|
23578
23636
|
const root = this.contextRootError();
|
|
23579
23637
|
if (!root.ok)
|
|
@@ -23598,6 +23656,7 @@ class WorkitCore {
|
|
|
23598
23656
|
return task;
|
|
23599
23657
|
if ((this.context.workerId ?? null) !== null)
|
|
23600
23658
|
return failure2("permission_denied", "helpers cannot record or revoke decisions");
|
|
23659
|
+
const filled = input.expectedRevision === undefined;
|
|
23601
23660
|
this.fillRevisions(input, task.data);
|
|
23602
23661
|
if (input.action === "record") {
|
|
23603
23662
|
if (task.data.status === "closed")
|
|
@@ -23646,7 +23705,20 @@ class WorkitCore {
|
|
|
23646
23705
|
const nativeProvenance = native?.ok ? retireNativeAuthority(native.data) : null;
|
|
23647
23706
|
if (native?.ok && !nativeProvenance)
|
|
23648
23707
|
return failure2("permission_denied", "native decision authority was retired");
|
|
23649
|
-
|
|
23708
|
+
let standingProvenance = standingApproval?.ok === true ? standingApproval.data : null;
|
|
23709
|
+
const recheck = (fresh) => {
|
|
23710
|
+
if (fresh.status === "closed")
|
|
23711
|
+
return failure2("invalid_transition", "closed task cannot record a decision");
|
|
23712
|
+
if (!standingProvenance)
|
|
23713
|
+
return success(null, null, null);
|
|
23714
|
+
const again = verifyStandingApproval(this.store.root, fresh, this.context.caller, input.binding);
|
|
23715
|
+
if (again.ok)
|
|
23716
|
+
standingProvenance = again.data;
|
|
23717
|
+
return again;
|
|
23718
|
+
};
|
|
23719
|
+
const changed = this.commitTask(task.data.id, input.expectedRevision, filled, recheck, (current, mutation) => {
|
|
23720
|
+
if (current.status === "closed")
|
|
23721
|
+
return failure2("invalid_transition", "closed task cannot record a decision");
|
|
23650
23722
|
const latestContent = verifyDecisionContent(this.store, input.binding);
|
|
23651
23723
|
if (!latestContent.ok)
|
|
23652
23724
|
return latestContent;
|
|
@@ -23658,14 +23730,14 @@ class WorkitCore {
|
|
|
23658
23730
|
const entry = {
|
|
23659
23731
|
id: newId(),
|
|
23660
23732
|
recordedAt: mutation.now,
|
|
23661
|
-
provenance:
|
|
23733
|
+
provenance: standingProvenance ?? nativeProvenance ?? provenance(this.context, "agent_reported"),
|
|
23662
23734
|
data
|
|
23663
23735
|
};
|
|
23664
23736
|
return success(mutation.revision, null, {
|
|
23665
23737
|
...current,
|
|
23666
23738
|
decisions: [...current.decisions, entry]
|
|
23667
23739
|
});
|
|
23668
|
-
}
|
|
23740
|
+
});
|
|
23669
23741
|
if (!changed.ok)
|
|
23670
23742
|
return changed;
|
|
23671
23743
|
return success(changed.data.revision, null, changed.data.decisions.at(-1));
|
|
@@ -23679,7 +23751,7 @@ class WorkitCore {
|
|
|
23679
23751
|
return failure2("invalid_transition", "decision is already revoked");
|
|
23680
23752
|
if (decision.data.consumption)
|
|
23681
23753
|
return failure2("permission_denied", "consumed decision cannot be revoked");
|
|
23682
|
-
const changed = this.
|
|
23754
|
+
const changed = this.commitTask(task.data.id, input.expectedRevision, filled, () => success(null, null, null), (current, mutation) => {
|
|
23683
23755
|
const entry = current.decisions.find((candidate) => candidate.id === input.decisionId);
|
|
23684
23756
|
if (!entry)
|
|
23685
23757
|
return failure2("not_found", "decision not found");
|
|
@@ -23696,13 +23768,16 @@ class WorkitCore {
|
|
|
23696
23768
|
...current,
|
|
23697
23769
|
decisions: current.decisions.map((candidate) => candidate.id === input.decisionId ? updated : candidate)
|
|
23698
23770
|
});
|
|
23699
|
-
}
|
|
23771
|
+
});
|
|
23700
23772
|
if (!changed.ok)
|
|
23701
23773
|
return changed;
|
|
23702
23774
|
const entry = changed.data.decisions.find((candidate) => candidate.id === input.decisionId);
|
|
23703
23775
|
return entry ? success(changed.data.revision, null, entry) : failure2("recovery_required", "decision disappeared");
|
|
23704
23776
|
}
|
|
23705
23777
|
finding(request) {
|
|
23778
|
+
return this.retryOmittedRevisions(request, () => this.findingOnce(request));
|
|
23779
|
+
}
|
|
23780
|
+
findingOnce(request) {
|
|
23706
23781
|
const root = this.contextRootError();
|
|
23707
23782
|
if (!root.ok)
|
|
23708
23783
|
return root;
|
|
@@ -23826,6 +23901,9 @@ class WorkitCore {
|
|
|
23826
23901
|
return entry ? success(changed.data.revision, null, entry) : failure2("recovery_required", "finding disappeared");
|
|
23827
23902
|
}
|
|
23828
23903
|
worker(request) {
|
|
23904
|
+
return this.retryOmittedRevisions(request, () => this.workerOnce(request));
|
|
23905
|
+
}
|
|
23906
|
+
workerOnce(request) {
|
|
23829
23907
|
const root = this.contextRootError();
|
|
23830
23908
|
if (!root.ok)
|
|
23831
23909
|
return root;
|
|
@@ -24173,6 +24251,9 @@ class WorkitCore {
|
|
|
24173
24251
|
return applied;
|
|
24174
24252
|
}
|
|
24175
24253
|
writer(request) {
|
|
24254
|
+
return this.retryOmittedRevisions(request, () => this.writerOnce(request));
|
|
24255
|
+
}
|
|
24256
|
+
writerOnce(request) {
|
|
24176
24257
|
const root = this.contextRootError();
|
|
24177
24258
|
if (!root.ok)
|
|
24178
24259
|
return root;
|
package/dist/workit-hook.js
CHANGED
|
@@ -10502,7 +10502,7 @@ class TaskStore {
|
|
|
10502
10502
|
if (!current.data)
|
|
10503
10503
|
return failure2("not_found", "workspace not found");
|
|
10504
10504
|
if (current.data.revision !== expected)
|
|
10505
|
-
return this.
|
|
10505
|
+
return this.workspaceConflict(expected, current.data.revision);
|
|
10506
10506
|
const previousBytes = this.snapshotBytes(this.workspacePath);
|
|
10507
10507
|
if (!previousBytes)
|
|
10508
10508
|
return failure2("storage_error", "workspace snapshot disappeared during mutation");
|
|
@@ -10545,7 +10545,7 @@ class TaskStore {
|
|
|
10545
10545
|
if (task.data.revision !== expected)
|
|
10546
10546
|
return this.conflict(expected, task.data.revision);
|
|
10547
10547
|
if (workspace.data.revision !== input.expectedWorkspaceRevision)
|
|
10548
|
-
return this.
|
|
10548
|
+
return this.workspaceConflict(input.expectedWorkspaceRevision, workspace.data.revision);
|
|
10549
10549
|
const previousWorkspaceBytes = this.snapshotBytes(this.workspacePath);
|
|
10550
10550
|
const previousTaskBytes = this.snapshotBytes(this.taskPath(input.taskId));
|
|
10551
10551
|
if (!previousWorkspaceBytes || !previousTaskBytes)
|
|
@@ -11355,6 +11355,9 @@ class TaskStore {
|
|
|
11355
11355
|
actualRevision: actual
|
|
11356
11356
|
});
|
|
11357
11357
|
}
|
|
11358
|
+
workspaceConflict(expected, actual) {
|
|
11359
|
+
return failure2("revision_conflict", "workspace revision does not match; omit expectedWorkspaceRevision to use the current record", { expectedWorkspaceRevision: expected, actualWorkspaceRevision: actual });
|
|
11360
|
+
}
|
|
11358
11361
|
taskPath(taskId) {
|
|
11359
11362
|
return path7.join(this.tasksDir, `${taskId}.json`);
|
|
11360
11363
|
}
|
|
@@ -13028,8 +13031,10 @@ values are still concurrency-checked, so never copy revisions between calls.
|
|
|
13028
13031
|
A busy result means another live Workit call holds the checkout lock: retry the
|
|
13029
13032
|
same call; it is not a recovery condition. A lock left by a dead process is
|
|
13030
13033
|
reclaimed on the next write, and \`workit doctor --fix-lock\` clears it on demand.
|
|
13031
|
-
|
|
13032
|
-
|
|
13034
|
+
An omitted revision absorbs a concurrent write: Workit re-reads, re-checks
|
|
13035
|
+
policy, and reapplies the call, returning busy under persistent contention. A
|
|
13036
|
+
revision_conflict means a revision you passed is stale: re-read the record
|
|
13037
|
+
before deciding whether to retry.
|
|
13033
13038
|
A solo edit does not need writer acquisition; use it when concurrent checkout
|
|
13034
13039
|
writers need coordination. Record only observed facts and checks. Evidence can
|
|
13035
13040
|
become stale when its bound candidate changes; reconcile findings against the
|
|
@@ -13372,6 +13377,29 @@ var applyWorkerLifecycle = (input) => {
|
|
|
13372
13377
|
return updated ? success(changed.data.task.revision, changed.data.workspace.revision, updated) : failure2("recovery_required", "worker disappeared during lifecycle observation");
|
|
13373
13378
|
};
|
|
13374
13379
|
var refsWithinScope = (refs, scope) => refs.every((ref) => ref.kind !== "file" || bindingCovers(scope, { description: "", paths: [ref.path], exclusions: [] }));
|
|
13380
|
+
var REVISION_RETRY_ATTEMPTS = 8;
|
|
13381
|
+
var retryPause = new Int32Array(new SharedArrayBuffer(4));
|
|
13382
|
+
var filledRevisions = (request) => {
|
|
13383
|
+
const value = typeof request === "object" && request !== null ? request : {};
|
|
13384
|
+
return {
|
|
13385
|
+
task: value.expectedRevision === undefined,
|
|
13386
|
+
workspace: value.expectedWorkspaceRevision === undefined
|
|
13387
|
+
};
|
|
13388
|
+
};
|
|
13389
|
+
var filledConflict = (result, filled) => !result.ok && result.code === "revision_conflict" && (("actualRevision" in result.details) && filled.task || ("actualWorkspaceRevision" in result.details) && filled.workspace);
|
|
13390
|
+
var retryFilledRevisions = (filled, run) => {
|
|
13391
|
+
if (!filled.task && !filled.workspace)
|
|
13392
|
+
return run();
|
|
13393
|
+
let result = run();
|
|
13394
|
+
const deadline = Date.now() + defaultLockTimeout();
|
|
13395
|
+
for (let attempt = 1;filledConflict(result, filled); attempt += 1) {
|
|
13396
|
+
if (attempt >= REVISION_RETRY_ATTEMPTS || Date.now() >= deadline)
|
|
13397
|
+
return failure2("busy", "records kept changing under concurrent writers; retry the call");
|
|
13398
|
+
Atomics.wait(retryPause, 0, 0, Math.floor(Math.random() * 4 * attempt) + 1);
|
|
13399
|
+
result = run();
|
|
13400
|
+
}
|
|
13401
|
+
return result;
|
|
13402
|
+
};
|
|
13375
13403
|
var trustedNow = (context) => typeof context.now === "function" ? context.now() : context.now;
|
|
13376
13404
|
var mapRef = (ref, ids) => {
|
|
13377
13405
|
if (ref.kind !== "record")
|
|
@@ -13619,6 +13647,9 @@ class WorkitCore {
|
|
|
13619
13647
|
if (input.expectedWorkspaceRevision === undefined)
|
|
13620
13648
|
input.expectedWorkspaceRevision = workspace ? workspace.revision : null;
|
|
13621
13649
|
}
|
|
13650
|
+
retryOmittedRevisions(request, run) {
|
|
13651
|
+
return retryFilledRevisions(filledRevisions(request), run);
|
|
13652
|
+
}
|
|
13622
13653
|
helperEntry(task, requireSession = false, requireActive = false) {
|
|
13623
13654
|
const workerId = this.context.workerId ?? null;
|
|
13624
13655
|
if (workerId === null)
|
|
@@ -13650,6 +13681,9 @@ class WorkitCore {
|
|
|
13650
13681
|
return null;
|
|
13651
13682
|
}
|
|
13652
13683
|
state(request) {
|
|
13684
|
+
return request?.action === "import" ? this.retryOmittedRevisions(request, () => this.stateOnce(request)) : this.stateOnce(request);
|
|
13685
|
+
}
|
|
13686
|
+
stateOnce(request) {
|
|
13653
13687
|
const root = this.contextRootError();
|
|
13654
13688
|
if (!root.ok)
|
|
13655
13689
|
return root;
|
|
@@ -13751,6 +13785,9 @@ class WorkitCore {
|
|
|
13751
13785
|
});
|
|
13752
13786
|
}
|
|
13753
13787
|
task(request) {
|
|
13788
|
+
return this.retryOmittedRevisions(request, () => this.taskOnce(request));
|
|
13789
|
+
}
|
|
13790
|
+
taskOnce(request) {
|
|
13754
13791
|
const root = this.contextRootError();
|
|
13755
13792
|
if (!root.ok)
|
|
13756
13793
|
return root;
|
|
@@ -13850,6 +13887,9 @@ class WorkitCore {
|
|
|
13850
13887
|
}
|
|
13851
13888
|
}
|
|
13852
13889
|
policy(request) {
|
|
13890
|
+
return this.retryOmittedRevisions(request, () => this.policyOnce(request));
|
|
13891
|
+
}
|
|
13892
|
+
policyOnce(request) {
|
|
13853
13893
|
const root = this.contextRootError();
|
|
13854
13894
|
if (!root.ok)
|
|
13855
13895
|
return root;
|
|
@@ -13896,6 +13936,9 @@ class WorkitCore {
|
|
|
13896
13936
|
return success(changed.data.revision, null, changed.data.policy);
|
|
13897
13937
|
}
|
|
13898
13938
|
evidence(request) {
|
|
13939
|
+
return this.retryOmittedRevisions(request, () => this.evidenceOnce(request));
|
|
13940
|
+
}
|
|
13941
|
+
evidenceOnce(request) {
|
|
13899
13942
|
const root = this.contextRootError();
|
|
13900
13943
|
if (!root.ok)
|
|
13901
13944
|
return root;
|
|
@@ -13980,6 +14023,23 @@ class WorkitCore {
|
|
|
13980
14023
|
observeStandingDecision(request) {
|
|
13981
14024
|
return this.recordDecision(request, undefined, true, true);
|
|
13982
14025
|
}
|
|
14026
|
+
commitTask(taskId, expected, filled, recheck, update) {
|
|
14027
|
+
let revision = expected;
|
|
14028
|
+
let first = true;
|
|
14029
|
+
return retryFilledRevisions({ task: filled, workspace: false }, () => {
|
|
14030
|
+
if (!first) {
|
|
14031
|
+
const fresh = this.store.readTask(taskId);
|
|
14032
|
+
if (!fresh.ok)
|
|
14033
|
+
return fresh;
|
|
14034
|
+
const checked = recheck(fresh.data);
|
|
14035
|
+
if (!checked.ok)
|
|
14036
|
+
return checked;
|
|
14037
|
+
revision = fresh.data.revision;
|
|
14038
|
+
}
|
|
14039
|
+
first = false;
|
|
14040
|
+
return this.store.mutateTask(taskId, revision, update, trustedNow(this.context));
|
|
14041
|
+
});
|
|
14042
|
+
}
|
|
13983
14043
|
recordDecision(request, nativeObservation, nativeRequired = false, standing = false) {
|
|
13984
14044
|
const root = this.contextRootError();
|
|
13985
14045
|
if (!root.ok)
|
|
@@ -14004,6 +14064,7 @@ class WorkitCore {
|
|
|
14004
14064
|
return task;
|
|
14005
14065
|
if ((this.context.workerId ?? null) !== null)
|
|
14006
14066
|
return failure2("permission_denied", "helpers cannot record or revoke decisions");
|
|
14067
|
+
const filled = input.expectedRevision === undefined;
|
|
14007
14068
|
this.fillRevisions(input, task.data);
|
|
14008
14069
|
if (input.action === "record") {
|
|
14009
14070
|
if (task.data.status === "closed")
|
|
@@ -14052,7 +14113,20 @@ class WorkitCore {
|
|
|
14052
14113
|
const nativeProvenance = native?.ok ? retireNativeAuthority(native.data) : null;
|
|
14053
14114
|
if (native?.ok && !nativeProvenance)
|
|
14054
14115
|
return failure2("permission_denied", "native decision authority was retired");
|
|
14055
|
-
|
|
14116
|
+
let standingProvenance = standingApproval?.ok === true ? standingApproval.data : null;
|
|
14117
|
+
const recheck = (fresh) => {
|
|
14118
|
+
if (fresh.status === "closed")
|
|
14119
|
+
return failure2("invalid_transition", "closed task cannot record a decision");
|
|
14120
|
+
if (!standingProvenance)
|
|
14121
|
+
return success(null, null, null);
|
|
14122
|
+
const again = verifyStandingApproval(this.store.root, fresh, this.context.caller, input.binding);
|
|
14123
|
+
if (again.ok)
|
|
14124
|
+
standingProvenance = again.data;
|
|
14125
|
+
return again;
|
|
14126
|
+
};
|
|
14127
|
+
const changed = this.commitTask(task.data.id, input.expectedRevision, filled, recheck, (current, mutation) => {
|
|
14128
|
+
if (current.status === "closed")
|
|
14129
|
+
return failure2("invalid_transition", "closed task cannot record a decision");
|
|
14056
14130
|
const latestContent = verifyDecisionContent(this.store, input.binding);
|
|
14057
14131
|
if (!latestContent.ok)
|
|
14058
14132
|
return latestContent;
|
|
@@ -14064,14 +14138,14 @@ class WorkitCore {
|
|
|
14064
14138
|
const entry = {
|
|
14065
14139
|
id: newId(),
|
|
14066
14140
|
recordedAt: mutation.now,
|
|
14067
|
-
provenance:
|
|
14141
|
+
provenance: standingProvenance ?? nativeProvenance ?? provenance(this.context, "agent_reported"),
|
|
14068
14142
|
data
|
|
14069
14143
|
};
|
|
14070
14144
|
return success(mutation.revision, null, {
|
|
14071
14145
|
...current,
|
|
14072
14146
|
decisions: [...current.decisions, entry]
|
|
14073
14147
|
});
|
|
14074
|
-
}
|
|
14148
|
+
});
|
|
14075
14149
|
if (!changed.ok)
|
|
14076
14150
|
return changed;
|
|
14077
14151
|
return success(changed.data.revision, null, changed.data.decisions.at(-1));
|
|
@@ -14085,7 +14159,7 @@ class WorkitCore {
|
|
|
14085
14159
|
return failure2("invalid_transition", "decision is already revoked");
|
|
14086
14160
|
if (decision.data.consumption)
|
|
14087
14161
|
return failure2("permission_denied", "consumed decision cannot be revoked");
|
|
14088
|
-
const changed = this.
|
|
14162
|
+
const changed = this.commitTask(task.data.id, input.expectedRevision, filled, () => success(null, null, null), (current, mutation) => {
|
|
14089
14163
|
const entry = current.decisions.find((candidate) => candidate.id === input.decisionId);
|
|
14090
14164
|
if (!entry)
|
|
14091
14165
|
return failure2("not_found", "decision not found");
|
|
@@ -14102,13 +14176,16 @@ class WorkitCore {
|
|
|
14102
14176
|
...current,
|
|
14103
14177
|
decisions: current.decisions.map((candidate) => candidate.id === input.decisionId ? updated : candidate)
|
|
14104
14178
|
});
|
|
14105
|
-
}
|
|
14179
|
+
});
|
|
14106
14180
|
if (!changed.ok)
|
|
14107
14181
|
return changed;
|
|
14108
14182
|
const entry = changed.data.decisions.find((candidate) => candidate.id === input.decisionId);
|
|
14109
14183
|
return entry ? success(changed.data.revision, null, entry) : failure2("recovery_required", "decision disappeared");
|
|
14110
14184
|
}
|
|
14111
14185
|
finding(request) {
|
|
14186
|
+
return this.retryOmittedRevisions(request, () => this.findingOnce(request));
|
|
14187
|
+
}
|
|
14188
|
+
findingOnce(request) {
|
|
14112
14189
|
const root = this.contextRootError();
|
|
14113
14190
|
if (!root.ok)
|
|
14114
14191
|
return root;
|
|
@@ -14232,6 +14309,9 @@ class WorkitCore {
|
|
|
14232
14309
|
return entry ? success(changed.data.revision, null, entry) : failure2("recovery_required", "finding disappeared");
|
|
14233
14310
|
}
|
|
14234
14311
|
worker(request) {
|
|
14312
|
+
return this.retryOmittedRevisions(request, () => this.workerOnce(request));
|
|
14313
|
+
}
|
|
14314
|
+
workerOnce(request) {
|
|
14235
14315
|
const root = this.contextRootError();
|
|
14236
14316
|
if (!root.ok)
|
|
14237
14317
|
return root;
|
|
@@ -14579,6 +14659,9 @@ class WorkitCore {
|
|
|
14579
14659
|
return applied;
|
|
14580
14660
|
}
|
|
14581
14661
|
writer(request) {
|
|
14662
|
+
return this.retryOmittedRevisions(request, () => this.writerOnce(request));
|
|
14663
|
+
}
|
|
14664
|
+
writerOnce(request) {
|
|
14582
14665
|
const root = this.contextRootError();
|
|
14583
14666
|
if (!root.ok)
|
|
14584
14667
|
return root;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brainervirus/workit-cursor",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.2",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Workit Cursor plugin — shared MCP transport, native hooks, and fourteen method skills",
|
|
6
6
|
"keywords": [
|
|
@@ -44,8 +44,8 @@
|
|
|
44
44
|
"build": "bun scripts/build.ts"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@brainervirus/workit-core": "^2.2.
|
|
48
|
-
"@brainervirus/workit-mcp": "^2.2.
|
|
47
|
+
"@brainervirus/workit-core": "^2.2.2",
|
|
48
|
+
"@brainervirus/workit-mcp": "^2.2.2"
|
|
49
49
|
},
|
|
50
50
|
"engines": {
|
|
51
51
|
"node": ">=24"
|