@brainervirus/workit-codex 2.2.0 → 2.2.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/.codex-plugin/plugin.json +1 -1
- package/dist/launch-mcp.js +88 -7
- package/dist/workit-hook.js +92 -9
- package/package.json +3 -3
package/dist/launch-mcp.js
CHANGED
|
@@ -16683,7 +16683,7 @@ class TaskStore {
|
|
|
16683
16683
|
if (!current.data)
|
|
16684
16684
|
return failure2("not_found", "workspace not found");
|
|
16685
16685
|
if (current.data.revision !== expected)
|
|
16686
|
-
return this.
|
|
16686
|
+
return this.workspaceConflict(expected, current.data.revision);
|
|
16687
16687
|
const previousBytes = this.snapshotBytes(this.workspacePath);
|
|
16688
16688
|
if (!previousBytes)
|
|
16689
16689
|
return failure2("storage_error", "workspace snapshot disappeared during mutation");
|
|
@@ -16726,7 +16726,7 @@ class TaskStore {
|
|
|
16726
16726
|
if (task.data.revision !== expected)
|
|
16727
16727
|
return this.conflict(expected, task.data.revision);
|
|
16728
16728
|
if (workspace.data.revision !== input.expectedWorkspaceRevision)
|
|
16729
|
-
return this.
|
|
16729
|
+
return this.workspaceConflict(input.expectedWorkspaceRevision, workspace.data.revision);
|
|
16730
16730
|
const previousWorkspaceBytes = this.snapshotBytes(this.workspacePath);
|
|
16731
16731
|
const previousTaskBytes = this.snapshotBytes(this.taskPath(input.taskId));
|
|
16732
16732
|
if (!previousWorkspaceBytes || !previousTaskBytes)
|
|
@@ -17536,6 +17536,9 @@ class TaskStore {
|
|
|
17536
17536
|
actualRevision: actual
|
|
17537
17537
|
});
|
|
17538
17538
|
}
|
|
17539
|
+
workspaceConflict(expected, actual) {
|
|
17540
|
+
return failure2("revision_conflict", "workspace revision does not match; omit expectedWorkspaceRevision to use the current record", { expectedWorkspaceRevision: expected, actualWorkspaceRevision: actual });
|
|
17541
|
+
}
|
|
17539
17542
|
taskPath(taskId) {
|
|
17540
17543
|
return path4.join(this.tasksDir, `${taskId}.json`);
|
|
17541
17544
|
}
|
|
@@ -20588,6 +20591,29 @@ var applyWorkerLifecycle = (input) => {
|
|
|
20588
20591
|
return updated ? success(changed.data.task.revision, changed.data.workspace.revision, updated) : failure2("recovery_required", "worker disappeared during lifecycle observation");
|
|
20589
20592
|
};
|
|
20590
20593
|
var refsWithinScope = (refs, scope) => refs.every((ref) => ref.kind !== "file" || bindingCovers(scope, { description: "", paths: [ref.path], exclusions: [] }));
|
|
20594
|
+
var REVISION_RETRY_ATTEMPTS = 8;
|
|
20595
|
+
var retryPause = new Int32Array(new SharedArrayBuffer(4));
|
|
20596
|
+
var filledRevisions = (request) => {
|
|
20597
|
+
const value = typeof request === "object" && request !== null ? request : {};
|
|
20598
|
+
return {
|
|
20599
|
+
task: value.expectedRevision === undefined,
|
|
20600
|
+
workspace: value.expectedWorkspaceRevision === undefined
|
|
20601
|
+
};
|
|
20602
|
+
};
|
|
20603
|
+
var filledConflict = (result, filled) => !result.ok && result.code === "revision_conflict" && (("actualRevision" in result.details) && filled.task || ("actualWorkspaceRevision" in result.details) && filled.workspace);
|
|
20604
|
+
var retryFilledRevisions = (filled, run) => {
|
|
20605
|
+
if (!filled.task && !filled.workspace)
|
|
20606
|
+
return run();
|
|
20607
|
+
let result = run();
|
|
20608
|
+
const deadline = Date.now() + defaultLockTimeout();
|
|
20609
|
+
for (let attempt = 1;filledConflict(result, filled); attempt += 1) {
|
|
20610
|
+
if (attempt >= REVISION_RETRY_ATTEMPTS || Date.now() >= deadline)
|
|
20611
|
+
return failure2("busy", "records kept changing under concurrent writers; retry the call");
|
|
20612
|
+
Atomics.wait(retryPause, 0, 0, Math.floor(Math.random() * 4 * attempt) + 1);
|
|
20613
|
+
result = run();
|
|
20614
|
+
}
|
|
20615
|
+
return result;
|
|
20616
|
+
};
|
|
20591
20617
|
var trustedNow = (context) => typeof context.now === "function" ? context.now() : context.now;
|
|
20592
20618
|
var mapRef = (ref, ids) => {
|
|
20593
20619
|
if (ref.kind !== "record")
|
|
@@ -20835,6 +20861,9 @@ class WorkitCore {
|
|
|
20835
20861
|
if (input.expectedWorkspaceRevision === undefined)
|
|
20836
20862
|
input.expectedWorkspaceRevision = workspace ? workspace.revision : null;
|
|
20837
20863
|
}
|
|
20864
|
+
retryOmittedRevisions(request, run) {
|
|
20865
|
+
return retryFilledRevisions(filledRevisions(request), run);
|
|
20866
|
+
}
|
|
20838
20867
|
helperEntry(task, requireSession = false, requireActive = false) {
|
|
20839
20868
|
const workerId = this.context.workerId ?? null;
|
|
20840
20869
|
if (workerId === null)
|
|
@@ -20866,6 +20895,9 @@ class WorkitCore {
|
|
|
20866
20895
|
return null;
|
|
20867
20896
|
}
|
|
20868
20897
|
state(request) {
|
|
20898
|
+
return request?.action === "import" ? this.retryOmittedRevisions(request, () => this.stateOnce(request)) : this.stateOnce(request);
|
|
20899
|
+
}
|
|
20900
|
+
stateOnce(request) {
|
|
20869
20901
|
const root = this.contextRootError();
|
|
20870
20902
|
if (!root.ok)
|
|
20871
20903
|
return root;
|
|
@@ -20967,6 +20999,9 @@ class WorkitCore {
|
|
|
20967
20999
|
});
|
|
20968
21000
|
}
|
|
20969
21001
|
task(request) {
|
|
21002
|
+
return this.retryOmittedRevisions(request, () => this.taskOnce(request));
|
|
21003
|
+
}
|
|
21004
|
+
taskOnce(request) {
|
|
20970
21005
|
const root = this.contextRootError();
|
|
20971
21006
|
if (!root.ok)
|
|
20972
21007
|
return root;
|
|
@@ -21066,6 +21101,9 @@ class WorkitCore {
|
|
|
21066
21101
|
}
|
|
21067
21102
|
}
|
|
21068
21103
|
policy(request) {
|
|
21104
|
+
return this.retryOmittedRevisions(request, () => this.policyOnce(request));
|
|
21105
|
+
}
|
|
21106
|
+
policyOnce(request) {
|
|
21069
21107
|
const root = this.contextRootError();
|
|
21070
21108
|
if (!root.ok)
|
|
21071
21109
|
return root;
|
|
@@ -21112,6 +21150,9 @@ class WorkitCore {
|
|
|
21112
21150
|
return success(changed.data.revision, null, changed.data.policy);
|
|
21113
21151
|
}
|
|
21114
21152
|
evidence(request) {
|
|
21153
|
+
return this.retryOmittedRevisions(request, () => this.evidenceOnce(request));
|
|
21154
|
+
}
|
|
21155
|
+
evidenceOnce(request) {
|
|
21115
21156
|
const root = this.contextRootError();
|
|
21116
21157
|
if (!root.ok)
|
|
21117
21158
|
return root;
|
|
@@ -21196,6 +21237,23 @@ class WorkitCore {
|
|
|
21196
21237
|
observeStandingDecision(request) {
|
|
21197
21238
|
return this.recordDecision(request, undefined, true, true);
|
|
21198
21239
|
}
|
|
21240
|
+
commitTask(taskId, expected, filled, recheck, update) {
|
|
21241
|
+
let revision = expected;
|
|
21242
|
+
let first = true;
|
|
21243
|
+
return retryFilledRevisions({ task: filled, workspace: false }, () => {
|
|
21244
|
+
if (!first) {
|
|
21245
|
+
const fresh = this.store.readTask(taskId);
|
|
21246
|
+
if (!fresh.ok)
|
|
21247
|
+
return fresh;
|
|
21248
|
+
const checked = recheck(fresh.data);
|
|
21249
|
+
if (!checked.ok)
|
|
21250
|
+
return checked;
|
|
21251
|
+
revision = fresh.data.revision;
|
|
21252
|
+
}
|
|
21253
|
+
first = false;
|
|
21254
|
+
return this.store.mutateTask(taskId, revision, update, trustedNow(this.context));
|
|
21255
|
+
});
|
|
21256
|
+
}
|
|
21199
21257
|
recordDecision(request, nativeObservation, nativeRequired = false, standing = false) {
|
|
21200
21258
|
const root = this.contextRootError();
|
|
21201
21259
|
if (!root.ok)
|
|
@@ -21220,6 +21278,7 @@ class WorkitCore {
|
|
|
21220
21278
|
return task;
|
|
21221
21279
|
if ((this.context.workerId ?? null) !== null)
|
|
21222
21280
|
return failure2("permission_denied", "helpers cannot record or revoke decisions");
|
|
21281
|
+
const filled = input.expectedRevision === undefined;
|
|
21223
21282
|
this.fillRevisions(input, task.data);
|
|
21224
21283
|
if (input.action === "record") {
|
|
21225
21284
|
if (task.data.status === "closed")
|
|
@@ -21268,7 +21327,20 @@ class WorkitCore {
|
|
|
21268
21327
|
const nativeProvenance = native?.ok ? retireNativeAuthority(native.data) : null;
|
|
21269
21328
|
if (native?.ok && !nativeProvenance)
|
|
21270
21329
|
return failure2("permission_denied", "native decision authority was retired");
|
|
21271
|
-
|
|
21330
|
+
let standingProvenance = standingApproval?.ok === true ? standingApproval.data : null;
|
|
21331
|
+
const recheck = (fresh) => {
|
|
21332
|
+
if (fresh.status === "closed")
|
|
21333
|
+
return failure2("invalid_transition", "closed task cannot record a decision");
|
|
21334
|
+
if (!standingProvenance)
|
|
21335
|
+
return success(null, null, null);
|
|
21336
|
+
const again = verifyStandingApproval(this.store.root, fresh, this.context.caller, input.binding);
|
|
21337
|
+
if (again.ok)
|
|
21338
|
+
standingProvenance = again.data;
|
|
21339
|
+
return again;
|
|
21340
|
+
};
|
|
21341
|
+
const changed = this.commitTask(task.data.id, input.expectedRevision, filled, recheck, (current, mutation) => {
|
|
21342
|
+
if (current.status === "closed")
|
|
21343
|
+
return failure2("invalid_transition", "closed task cannot record a decision");
|
|
21272
21344
|
const latestContent = verifyDecisionContent(this.store, input.binding);
|
|
21273
21345
|
if (!latestContent.ok)
|
|
21274
21346
|
return latestContent;
|
|
@@ -21280,14 +21352,14 @@ class WorkitCore {
|
|
|
21280
21352
|
const entry = {
|
|
21281
21353
|
id: newId(),
|
|
21282
21354
|
recordedAt: mutation.now,
|
|
21283
|
-
provenance:
|
|
21355
|
+
provenance: standingProvenance ?? nativeProvenance ?? provenance(this.context, "agent_reported"),
|
|
21284
21356
|
data
|
|
21285
21357
|
};
|
|
21286
21358
|
return success(mutation.revision, null, {
|
|
21287
21359
|
...current,
|
|
21288
21360
|
decisions: [...current.decisions, entry]
|
|
21289
21361
|
});
|
|
21290
|
-
}
|
|
21362
|
+
});
|
|
21291
21363
|
if (!changed.ok)
|
|
21292
21364
|
return changed;
|
|
21293
21365
|
return success(changed.data.revision, null, changed.data.decisions.at(-1));
|
|
@@ -21301,7 +21373,7 @@ class WorkitCore {
|
|
|
21301
21373
|
return failure2("invalid_transition", "decision is already revoked");
|
|
21302
21374
|
if (decision.data.consumption)
|
|
21303
21375
|
return failure2("permission_denied", "consumed decision cannot be revoked");
|
|
21304
|
-
const changed = this.
|
|
21376
|
+
const changed = this.commitTask(task.data.id, input.expectedRevision, filled, () => success(null, null, null), (current, mutation) => {
|
|
21305
21377
|
const entry = current.decisions.find((candidate) => candidate.id === input.decisionId);
|
|
21306
21378
|
if (!entry)
|
|
21307
21379
|
return failure2("not_found", "decision not found");
|
|
@@ -21318,13 +21390,16 @@ class WorkitCore {
|
|
|
21318
21390
|
...current,
|
|
21319
21391
|
decisions: current.decisions.map((candidate) => candidate.id === input.decisionId ? updated : candidate)
|
|
21320
21392
|
});
|
|
21321
|
-
}
|
|
21393
|
+
});
|
|
21322
21394
|
if (!changed.ok)
|
|
21323
21395
|
return changed;
|
|
21324
21396
|
const entry = changed.data.decisions.find((candidate) => candidate.id === input.decisionId);
|
|
21325
21397
|
return entry ? success(changed.data.revision, null, entry) : failure2("recovery_required", "decision disappeared");
|
|
21326
21398
|
}
|
|
21327
21399
|
finding(request) {
|
|
21400
|
+
return this.retryOmittedRevisions(request, () => this.findingOnce(request));
|
|
21401
|
+
}
|
|
21402
|
+
findingOnce(request) {
|
|
21328
21403
|
const root = this.contextRootError();
|
|
21329
21404
|
if (!root.ok)
|
|
21330
21405
|
return root;
|
|
@@ -21448,6 +21523,9 @@ class WorkitCore {
|
|
|
21448
21523
|
return entry ? success(changed.data.revision, null, entry) : failure2("recovery_required", "finding disappeared");
|
|
21449
21524
|
}
|
|
21450
21525
|
worker(request) {
|
|
21526
|
+
return this.retryOmittedRevisions(request, () => this.workerOnce(request));
|
|
21527
|
+
}
|
|
21528
|
+
workerOnce(request) {
|
|
21451
21529
|
const root = this.contextRootError();
|
|
21452
21530
|
if (!root.ok)
|
|
21453
21531
|
return root;
|
|
@@ -21795,6 +21873,9 @@ class WorkitCore {
|
|
|
21795
21873
|
return applied;
|
|
21796
21874
|
}
|
|
21797
21875
|
writer(request) {
|
|
21876
|
+
return this.retryOmittedRevisions(request, () => this.writerOnce(request));
|
|
21877
|
+
}
|
|
21878
|
+
writerOnce(request) {
|
|
21798
21879
|
const root = this.contextRootError();
|
|
21799
21880
|
if (!root.ok)
|
|
21800
21881
|
return root;
|
package/dist/workit-hook.js
CHANGED
|
@@ -100,8 +100,10 @@ values are still concurrency-checked, so never copy revisions between calls.
|
|
|
100
100
|
A busy result means another live Workit call holds the checkout lock: retry the
|
|
101
101
|
same call; it is not a recovery condition. A lock left by a dead process is
|
|
102
102
|
reclaimed on the next write, and \`workit doctor --fix-lock\` clears it on demand.
|
|
103
|
-
|
|
104
|
-
|
|
103
|
+
An omitted revision absorbs a concurrent write: Workit re-reads, re-checks
|
|
104
|
+
policy, and reapplies the call, returning busy under persistent contention. A
|
|
105
|
+
revision_conflict means a revision you passed is stale: re-read the record
|
|
106
|
+
before deciding whether to retry.
|
|
105
107
|
A solo edit does not need writer acquisition; use it when concurrent checkout
|
|
106
108
|
writers need coordination. Record only observed facts and checks. Evidence can
|
|
107
109
|
become stale when its bound candidate changes; reconcile findings against the
|
|
@@ -10658,7 +10660,7 @@ class TaskStore {
|
|
|
10658
10660
|
if (!current.data)
|
|
10659
10661
|
return failure2("not_found", "workspace not found");
|
|
10660
10662
|
if (current.data.revision !== expected)
|
|
10661
|
-
return this.
|
|
10663
|
+
return this.workspaceConflict(expected, current.data.revision);
|
|
10662
10664
|
const previousBytes = this.snapshotBytes(this.workspacePath);
|
|
10663
10665
|
if (!previousBytes)
|
|
10664
10666
|
return failure2("storage_error", "workspace snapshot disappeared during mutation");
|
|
@@ -10701,7 +10703,7 @@ class TaskStore {
|
|
|
10701
10703
|
if (task.data.revision !== expected)
|
|
10702
10704
|
return this.conflict(expected, task.data.revision);
|
|
10703
10705
|
if (workspace.data.revision !== input.expectedWorkspaceRevision)
|
|
10704
|
-
return this.
|
|
10706
|
+
return this.workspaceConflict(input.expectedWorkspaceRevision, workspace.data.revision);
|
|
10705
10707
|
const previousWorkspaceBytes = this.snapshotBytes(this.workspacePath);
|
|
10706
10708
|
const previousTaskBytes = this.snapshotBytes(this.taskPath(input.taskId));
|
|
10707
10709
|
if (!previousWorkspaceBytes || !previousTaskBytes)
|
|
@@ -11511,6 +11513,9 @@ class TaskStore {
|
|
|
11511
11513
|
actualRevision: actual
|
|
11512
11514
|
});
|
|
11513
11515
|
}
|
|
11516
|
+
workspaceConflict(expected, actual) {
|
|
11517
|
+
return failure2("revision_conflict", "workspace revision does not match; omit expectedWorkspaceRevision to use the current record", { expectedWorkspaceRevision: expected, actualWorkspaceRevision: actual });
|
|
11518
|
+
}
|
|
11514
11519
|
taskPath(taskId) {
|
|
11515
11520
|
return path7.join(this.tasksDir, `${taskId}.json`);
|
|
11516
11521
|
}
|
|
@@ -13398,6 +13403,29 @@ var applyWorkerLifecycle = (input) => {
|
|
|
13398
13403
|
return updated ? success(changed.data.task.revision, changed.data.workspace.revision, updated) : failure2("recovery_required", "worker disappeared during lifecycle observation");
|
|
13399
13404
|
};
|
|
13400
13405
|
var refsWithinScope = (refs, scope) => refs.every((ref) => ref.kind !== "file" || bindingCovers(scope, { description: "", paths: [ref.path], exclusions: [] }));
|
|
13406
|
+
var REVISION_RETRY_ATTEMPTS = 8;
|
|
13407
|
+
var retryPause = new Int32Array(new SharedArrayBuffer(4));
|
|
13408
|
+
var filledRevisions = (request) => {
|
|
13409
|
+
const value = typeof request === "object" && request !== null ? request : {};
|
|
13410
|
+
return {
|
|
13411
|
+
task: value.expectedRevision === undefined,
|
|
13412
|
+
workspace: value.expectedWorkspaceRevision === undefined
|
|
13413
|
+
};
|
|
13414
|
+
};
|
|
13415
|
+
var filledConflict = (result, filled) => !result.ok && result.code === "revision_conflict" && (("actualRevision" in result.details) && filled.task || ("actualWorkspaceRevision" in result.details) && filled.workspace);
|
|
13416
|
+
var retryFilledRevisions = (filled, run) => {
|
|
13417
|
+
if (!filled.task && !filled.workspace)
|
|
13418
|
+
return run();
|
|
13419
|
+
let result = run();
|
|
13420
|
+
const deadline = Date.now() + defaultLockTimeout();
|
|
13421
|
+
for (let attempt = 1;filledConflict(result, filled); attempt += 1) {
|
|
13422
|
+
if (attempt >= REVISION_RETRY_ATTEMPTS || Date.now() >= deadline)
|
|
13423
|
+
return failure2("busy", "records kept changing under concurrent writers; retry the call");
|
|
13424
|
+
Atomics.wait(retryPause, 0, 0, Math.floor(Math.random() * 4 * attempt) + 1);
|
|
13425
|
+
result = run();
|
|
13426
|
+
}
|
|
13427
|
+
return result;
|
|
13428
|
+
};
|
|
13401
13429
|
var trustedNow = (context) => typeof context.now === "function" ? context.now() : context.now;
|
|
13402
13430
|
var mapRef = (ref, ids) => {
|
|
13403
13431
|
if (ref.kind !== "record")
|
|
@@ -13645,6 +13673,9 @@ class WorkitCore {
|
|
|
13645
13673
|
if (input.expectedWorkspaceRevision === undefined)
|
|
13646
13674
|
input.expectedWorkspaceRevision = workspace ? workspace.revision : null;
|
|
13647
13675
|
}
|
|
13676
|
+
retryOmittedRevisions(request, run) {
|
|
13677
|
+
return retryFilledRevisions(filledRevisions(request), run);
|
|
13678
|
+
}
|
|
13648
13679
|
helperEntry(task, requireSession = false, requireActive = false) {
|
|
13649
13680
|
const workerId = this.context.workerId ?? null;
|
|
13650
13681
|
if (workerId === null)
|
|
@@ -13676,6 +13707,9 @@ class WorkitCore {
|
|
|
13676
13707
|
return null;
|
|
13677
13708
|
}
|
|
13678
13709
|
state(request) {
|
|
13710
|
+
return request?.action === "import" ? this.retryOmittedRevisions(request, () => this.stateOnce(request)) : this.stateOnce(request);
|
|
13711
|
+
}
|
|
13712
|
+
stateOnce(request) {
|
|
13679
13713
|
const root = this.contextRootError();
|
|
13680
13714
|
if (!root.ok)
|
|
13681
13715
|
return root;
|
|
@@ -13777,6 +13811,9 @@ class WorkitCore {
|
|
|
13777
13811
|
});
|
|
13778
13812
|
}
|
|
13779
13813
|
task(request) {
|
|
13814
|
+
return this.retryOmittedRevisions(request, () => this.taskOnce(request));
|
|
13815
|
+
}
|
|
13816
|
+
taskOnce(request) {
|
|
13780
13817
|
const root = this.contextRootError();
|
|
13781
13818
|
if (!root.ok)
|
|
13782
13819
|
return root;
|
|
@@ -13876,6 +13913,9 @@ class WorkitCore {
|
|
|
13876
13913
|
}
|
|
13877
13914
|
}
|
|
13878
13915
|
policy(request) {
|
|
13916
|
+
return this.retryOmittedRevisions(request, () => this.policyOnce(request));
|
|
13917
|
+
}
|
|
13918
|
+
policyOnce(request) {
|
|
13879
13919
|
const root = this.contextRootError();
|
|
13880
13920
|
if (!root.ok)
|
|
13881
13921
|
return root;
|
|
@@ -13922,6 +13962,9 @@ class WorkitCore {
|
|
|
13922
13962
|
return success(changed.data.revision, null, changed.data.policy);
|
|
13923
13963
|
}
|
|
13924
13964
|
evidence(request) {
|
|
13965
|
+
return this.retryOmittedRevisions(request, () => this.evidenceOnce(request));
|
|
13966
|
+
}
|
|
13967
|
+
evidenceOnce(request) {
|
|
13925
13968
|
const root = this.contextRootError();
|
|
13926
13969
|
if (!root.ok)
|
|
13927
13970
|
return root;
|
|
@@ -14006,6 +14049,23 @@ class WorkitCore {
|
|
|
14006
14049
|
observeStandingDecision(request) {
|
|
14007
14050
|
return this.recordDecision(request, undefined, true, true);
|
|
14008
14051
|
}
|
|
14052
|
+
commitTask(taskId, expected, filled, recheck, update) {
|
|
14053
|
+
let revision = expected;
|
|
14054
|
+
let first = true;
|
|
14055
|
+
return retryFilledRevisions({ task: filled, workspace: false }, () => {
|
|
14056
|
+
if (!first) {
|
|
14057
|
+
const fresh = this.store.readTask(taskId);
|
|
14058
|
+
if (!fresh.ok)
|
|
14059
|
+
return fresh;
|
|
14060
|
+
const checked = recheck(fresh.data);
|
|
14061
|
+
if (!checked.ok)
|
|
14062
|
+
return checked;
|
|
14063
|
+
revision = fresh.data.revision;
|
|
14064
|
+
}
|
|
14065
|
+
first = false;
|
|
14066
|
+
return this.store.mutateTask(taskId, revision, update, trustedNow(this.context));
|
|
14067
|
+
});
|
|
14068
|
+
}
|
|
14009
14069
|
recordDecision(request, nativeObservation, nativeRequired = false, standing = false) {
|
|
14010
14070
|
const root = this.contextRootError();
|
|
14011
14071
|
if (!root.ok)
|
|
@@ -14030,6 +14090,7 @@ class WorkitCore {
|
|
|
14030
14090
|
return task;
|
|
14031
14091
|
if ((this.context.workerId ?? null) !== null)
|
|
14032
14092
|
return failure2("permission_denied", "helpers cannot record or revoke decisions");
|
|
14093
|
+
const filled = input.expectedRevision === undefined;
|
|
14033
14094
|
this.fillRevisions(input, task.data);
|
|
14034
14095
|
if (input.action === "record") {
|
|
14035
14096
|
if (task.data.status === "closed")
|
|
@@ -14078,7 +14139,20 @@ class WorkitCore {
|
|
|
14078
14139
|
const nativeProvenance = native?.ok ? retireNativeAuthority(native.data) : null;
|
|
14079
14140
|
if (native?.ok && !nativeProvenance)
|
|
14080
14141
|
return failure2("permission_denied", "native decision authority was retired");
|
|
14081
|
-
|
|
14142
|
+
let standingProvenance = standingApproval?.ok === true ? standingApproval.data : null;
|
|
14143
|
+
const recheck = (fresh) => {
|
|
14144
|
+
if (fresh.status === "closed")
|
|
14145
|
+
return failure2("invalid_transition", "closed task cannot record a decision");
|
|
14146
|
+
if (!standingProvenance)
|
|
14147
|
+
return success(null, null, null);
|
|
14148
|
+
const again = verifyStandingApproval(this.store.root, fresh, this.context.caller, input.binding);
|
|
14149
|
+
if (again.ok)
|
|
14150
|
+
standingProvenance = again.data;
|
|
14151
|
+
return again;
|
|
14152
|
+
};
|
|
14153
|
+
const changed = this.commitTask(task.data.id, input.expectedRevision, filled, recheck, (current, mutation) => {
|
|
14154
|
+
if (current.status === "closed")
|
|
14155
|
+
return failure2("invalid_transition", "closed task cannot record a decision");
|
|
14082
14156
|
const latestContent = verifyDecisionContent(this.store, input.binding);
|
|
14083
14157
|
if (!latestContent.ok)
|
|
14084
14158
|
return latestContent;
|
|
@@ -14090,14 +14164,14 @@ class WorkitCore {
|
|
|
14090
14164
|
const entry = {
|
|
14091
14165
|
id: newId(),
|
|
14092
14166
|
recordedAt: mutation.now,
|
|
14093
|
-
provenance:
|
|
14167
|
+
provenance: standingProvenance ?? nativeProvenance ?? provenance(this.context, "agent_reported"),
|
|
14094
14168
|
data
|
|
14095
14169
|
};
|
|
14096
14170
|
return success(mutation.revision, null, {
|
|
14097
14171
|
...current,
|
|
14098
14172
|
decisions: [...current.decisions, entry]
|
|
14099
14173
|
});
|
|
14100
|
-
}
|
|
14174
|
+
});
|
|
14101
14175
|
if (!changed.ok)
|
|
14102
14176
|
return changed;
|
|
14103
14177
|
return success(changed.data.revision, null, changed.data.decisions.at(-1));
|
|
@@ -14111,7 +14185,7 @@ class WorkitCore {
|
|
|
14111
14185
|
return failure2("invalid_transition", "decision is already revoked");
|
|
14112
14186
|
if (decision.data.consumption)
|
|
14113
14187
|
return failure2("permission_denied", "consumed decision cannot be revoked");
|
|
14114
|
-
const changed = this.
|
|
14188
|
+
const changed = this.commitTask(task.data.id, input.expectedRevision, filled, () => success(null, null, null), (current, mutation) => {
|
|
14115
14189
|
const entry = current.decisions.find((candidate) => candidate.id === input.decisionId);
|
|
14116
14190
|
if (!entry)
|
|
14117
14191
|
return failure2("not_found", "decision not found");
|
|
@@ -14128,13 +14202,16 @@ class WorkitCore {
|
|
|
14128
14202
|
...current,
|
|
14129
14203
|
decisions: current.decisions.map((candidate) => candidate.id === input.decisionId ? updated : candidate)
|
|
14130
14204
|
});
|
|
14131
|
-
}
|
|
14205
|
+
});
|
|
14132
14206
|
if (!changed.ok)
|
|
14133
14207
|
return changed;
|
|
14134
14208
|
const entry = changed.data.decisions.find((candidate) => candidate.id === input.decisionId);
|
|
14135
14209
|
return entry ? success(changed.data.revision, null, entry) : failure2("recovery_required", "decision disappeared");
|
|
14136
14210
|
}
|
|
14137
14211
|
finding(request) {
|
|
14212
|
+
return this.retryOmittedRevisions(request, () => this.findingOnce(request));
|
|
14213
|
+
}
|
|
14214
|
+
findingOnce(request) {
|
|
14138
14215
|
const root = this.contextRootError();
|
|
14139
14216
|
if (!root.ok)
|
|
14140
14217
|
return root;
|
|
@@ -14258,6 +14335,9 @@ class WorkitCore {
|
|
|
14258
14335
|
return entry ? success(changed.data.revision, null, entry) : failure2("recovery_required", "finding disappeared");
|
|
14259
14336
|
}
|
|
14260
14337
|
worker(request) {
|
|
14338
|
+
return this.retryOmittedRevisions(request, () => this.workerOnce(request));
|
|
14339
|
+
}
|
|
14340
|
+
workerOnce(request) {
|
|
14261
14341
|
const root = this.contextRootError();
|
|
14262
14342
|
if (!root.ok)
|
|
14263
14343
|
return root;
|
|
@@ -14605,6 +14685,9 @@ class WorkitCore {
|
|
|
14605
14685
|
return applied;
|
|
14606
14686
|
}
|
|
14607
14687
|
writer(request) {
|
|
14688
|
+
return this.retryOmittedRevisions(request, () => this.writerOnce(request));
|
|
14689
|
+
}
|
|
14690
|
+
writerOnce(request) {
|
|
14608
14691
|
const root = this.contextRootError();
|
|
14609
14692
|
if (!root.ok)
|
|
14610
14693
|
return root;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brainervirus/workit-codex",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Workit Codex CLI/desktop plugin — shared MCP transport, hooks, and fourteen method skills",
|
|
6
6
|
"keywords": [
|
|
@@ -38,8 +38,8 @@
|
|
|
38
38
|
"build": "bun scripts/build.ts"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@brainervirus/workit-core": "^2.2.
|
|
42
|
-
"@brainervirus/workit-mcp": "^2.2.
|
|
41
|
+
"@brainervirus/workit-core": "^2.2.1",
|
|
42
|
+
"@brainervirus/workit-mcp": "^2.2.1"
|
|
43
43
|
},
|
|
44
44
|
"engines": {
|
|
45
45
|
"node": ">=24"
|