@brainervirus/workit-codex 1.1.1 → 1.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 +110 -37
- package/dist/workit-hook.js +109 -36
- package/package.json +3 -3
package/dist/launch-mcp.js
CHANGED
|
@@ -14551,6 +14551,7 @@ var workerSchema = object({
|
|
|
14551
14551
|
assignment: assignmentSchema,
|
|
14552
14552
|
state: _enum(["assigned", "dispatching", "running", "cancelling", "stopped", "unknown"]),
|
|
14553
14553
|
session: refSchema.nullable(),
|
|
14554
|
+
coordinator: refSchema.optional(),
|
|
14554
14555
|
report: workerReportSchema.nullable()
|
|
14555
14556
|
}).strict();
|
|
14556
14557
|
var ownerSchema = object({
|
|
@@ -14662,6 +14663,7 @@ var taskSummarySchema = object({
|
|
|
14662
14663
|
requirements: array(requirementEvaluationSchema),
|
|
14663
14664
|
writer: workspaceRecordSchema.shape.writer
|
|
14664
14665
|
}).strict();
|
|
14666
|
+
var taskListItemSchema = taskSummarySchema.omit({ policy: true, requirements: true });
|
|
14665
14667
|
var exportBundleSchema = object({
|
|
14666
14668
|
schemaVersion: literal(1),
|
|
14667
14669
|
exportedAt: utc,
|
|
@@ -14683,11 +14685,15 @@ var taskOperations = {
|
|
|
14683
14685
|
expectedWorkspaceRevision: revision.nullable().optional(),
|
|
14684
14686
|
intent: intentSchema
|
|
14685
14687
|
}),
|
|
14686
|
-
list: operation({
|
|
14688
|
+
list: operation({
|
|
14689
|
+
action: literal("list"),
|
|
14690
|
+
status: _enum(["open", "closed", "all"]).optional(),
|
|
14691
|
+
limit: number2().int().min(1).max(50).optional()
|
|
14692
|
+
}),
|
|
14687
14693
|
inspect: operation({
|
|
14688
14694
|
action: literal("inspect"),
|
|
14689
14695
|
...taskId,
|
|
14690
|
-
view: _enum(["summary", "full"])
|
|
14696
|
+
view: _enum(["summary", "full"]).optional()
|
|
14691
14697
|
}),
|
|
14692
14698
|
revise: operation({
|
|
14693
14699
|
action: literal("revise"),
|
|
@@ -14715,7 +14721,7 @@ var taskOperations = {
|
|
|
14715
14721
|
...taskId,
|
|
14716
14722
|
...revisions,
|
|
14717
14723
|
expectedWorkspaceRevision: revision.optional(),
|
|
14718
|
-
authorityRefs: array(refSchema)
|
|
14724
|
+
authorityRefs: array(refSchema).optional()
|
|
14719
14725
|
}),
|
|
14720
14726
|
close: operation({
|
|
14721
14727
|
action: literal("close"),
|
|
@@ -16548,6 +16554,10 @@ var validPath = (value) => typeof value === "string" && value.length > 0 && ![..
|
|
|
16548
16554
|
}) && !value.split(/[\\/]/).includes("..");
|
|
16549
16555
|
var callerValue = (value) => ({ host: value.host, actor: value.actor });
|
|
16550
16556
|
var workerIdOf = (value) => value.workerId ?? null;
|
|
16557
|
+
var currentWriterOwnsTask = (task, workspace, caller) => {
|
|
16558
|
+
const owner = workspace.writer?.owner;
|
|
16559
|
+
return task.status === "active" && task.workspaceId === workspace.id && owner?.taskId === task.id && owner.session.host === caller.host && owner.session.handle === caller.actor;
|
|
16560
|
+
};
|
|
16551
16561
|
function assertProductWriteAllowed(input) {
|
|
16552
16562
|
if (!Array.isArray(input.paths) || input.paths.some((path) => !validPath(path)))
|
|
16553
16563
|
return failure("invalid_input", "invalid product write path");
|
|
@@ -17264,9 +17274,11 @@ var standingReceiptFor = (root, workspace, cls, dir) => {
|
|
|
17264
17274
|
var verifyStandingApproval = (root, task, caller, binding) => {
|
|
17265
17275
|
if (task.status !== "active")
|
|
17266
17276
|
return failure("invalid_transition", "only active tasks can authorize actions");
|
|
17267
|
-
const
|
|
17268
|
-
if (!
|
|
17269
|
-
return
|
|
17277
|
+
const workspace = new TaskStore(root).readWorkspace();
|
|
17278
|
+
if (!workspace.ok)
|
|
17279
|
+
return workspace;
|
|
17280
|
+
if (!workspace.data || !currentWriterOwnsTask(task, workspace.data, caller))
|
|
17281
|
+
return failure("permission_denied", "standing approval requires the current writer session");
|
|
17270
17282
|
const standing = binding.standing;
|
|
17271
17283
|
if (!standing || typeof standing.workspace !== "string" || typeof standing.class !== "string")
|
|
17272
17284
|
return failure("invalid_input", "standing approval needs a workspace and class");
|
|
@@ -17503,6 +17515,20 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
|
17503
17515
|
};
|
|
17504
17516
|
var verifyContentRefs = (store, binding) => verifyDecisionContentAtRoot(store.root, binding);
|
|
17505
17517
|
var storedDecisionApplicable = (store, task, purpose, binding) => task.decisions.filter((entry) => entry.provenance.kind !== "imported" && decisionMatches(entry, purpose, binding) && verifyDecisionContentAtRoot(store.root, entry.data.binding).ok);
|
|
17518
|
+
var approvalAuthorityMatches = (store, task, approval, current) => {
|
|
17519
|
+
if (approval.kind !== "host_observed" || approval.receipts.length === 0)
|
|
17520
|
+
return false;
|
|
17521
|
+
if (provenanceMatches(approval, current))
|
|
17522
|
+
return true;
|
|
17523
|
+
const session = current.kind === "host_observed" ? current.session : null;
|
|
17524
|
+
if (!session || session.kind !== "host")
|
|
17525
|
+
return false;
|
|
17526
|
+
const workspace = store.readWorkspace();
|
|
17527
|
+
return workspace.ok && workspace.data !== null && currentWriterOwnsTask(task, workspace.data, {
|
|
17528
|
+
host: session.host,
|
|
17529
|
+
actor: session.handle
|
|
17530
|
+
});
|
|
17531
|
+
};
|
|
17506
17532
|
var validateAction = (store, task, workspaceId, input, authority) => {
|
|
17507
17533
|
if (task.status !== "active")
|
|
17508
17534
|
return failure("invalid_transition", "only active tasks can authorize actions");
|
|
@@ -17528,7 +17554,7 @@ var validateAction = (store, task, workspaceId, input, authority) => {
|
|
|
17528
17554
|
return failure("permission_denied", "native reservation authority does not match decision");
|
|
17529
17555
|
if (decision.response !== "approved")
|
|
17530
17556
|
return failure("permission_denied", "decision was rejected");
|
|
17531
|
-
if (
|
|
17557
|
+
if (!approvalAuthorityMatches(store, task, entry.provenance, authority.provenance))
|
|
17532
17558
|
return failure("permission_denied", "action approval lacks native receipt assurance");
|
|
17533
17559
|
const receipts = entry.provenance.receipts;
|
|
17534
17560
|
const standing = receipts.filter((receipt) => typeof receipt === "object" && receipt !== null && receipt.kind === "standing");
|
|
@@ -17695,7 +17721,7 @@ function settleAction(input) {
|
|
|
17695
17721
|
return failure("not_found", "decision not found");
|
|
17696
17722
|
if (!authorityDecisionMatches(authority, entry.data))
|
|
17697
17723
|
return failure("permission_denied", "native settlement authority does not match decision");
|
|
17698
|
-
if (!
|
|
17724
|
+
if (!approvalAuthorityMatches(input.store, current, entry.provenance, authority.provenance))
|
|
17699
17725
|
return failure("permission_denied", "native settlement caller does not match approval");
|
|
17700
17726
|
if (entry.data.purpose !== "action" || entry.data.digest !== decisionDigest(entry.data))
|
|
17701
17727
|
return failure("permission_denied", "decision binding is invalid");
|
|
@@ -17794,7 +17820,7 @@ function reconcileAction(input) {
|
|
|
17794
17820
|
const entry = current.decisions.find((candidate) => candidate.id === input.decisionId);
|
|
17795
17821
|
if (!entry || entry.data.purpose !== "action")
|
|
17796
17822
|
return failure("not_found", "decision not found");
|
|
17797
|
-
if (!authorityDecisionMatches(authority, entry.data) || !
|
|
17823
|
+
if (!authorityDecisionMatches(authority, entry.data) || !approvalAuthorityMatches(input.store, current, entry.provenance, authority.provenance))
|
|
17798
17824
|
return failure("permission_denied", "native reconciliation caller does not match approval");
|
|
17799
17825
|
if (entry.data.response !== "approved" || entry.data.revoked !== null || entry.data.digest !== decisionDigest(entry.data) || entry.data.binding.taskId !== current.id || entry.data.binding.workspaceId !== current.workspaceId || entry.data.binding.workspaceId !== workspace.data.id)
|
|
17800
17826
|
return failure("permission_denied", "reconciliation decision binding is invalid");
|
|
@@ -18131,7 +18157,7 @@ var resolveBinding = (evidence, pin, currentCandidateId) => {
|
|
|
18131
18157
|
candidateId: evidence.candidateId ?? fallback
|
|
18132
18158
|
};
|
|
18133
18159
|
};
|
|
18134
|
-
var findingVerificationPasses = (findingCandidateId, evidence) => evidence.status === "passed" && (evidence.kind === "check" || evidence.kind === "review") && ((findingCandidateId ?? null) === null || evidence.candidateId
|
|
18160
|
+
var findingVerificationPasses = (findingCandidateId, evidence) => evidence.status === "passed" && (evidence.kind === "check" || evidence.kind === "review") && ((findingCandidateId ?? null) === null || evidence.candidateId != null);
|
|
18135
18161
|
var applicableDecision2 = (task, workspace, requirement, checkoutRoot) => task.decisions.filter((entry) => entry.provenance.kind !== "imported").map((entry) => entry.data).filter((decision) => decision.purpose === "limitation" && decision.response === "approved" && decision.revoked === null && decision.binding.taskId === task.id && decision.binding.workspaceId === workspace.id && decision.requirementIds.includes(requirement.id) && decision.binding.scope && scopeCovers2(decision.binding.scope, requirement.scope) && decision.digest === decisionDigest(decision) && (checkoutRoot ? verifyDecisionContentAtRoot(checkoutRoot, decision.binding).ok : decision.binding.contentRefs.every((reference) => reference.kind !== "file")));
|
|
18136
18162
|
var applicableRequirementDecision = (task, workspace, requirement, checkoutRoot) => task.decisions.filter(({ data, provenance }) => provenance.kind !== "imported" && data.purpose !== "limitation" && (data.response === "approved" || data.response === "stated") && data.revoked === null && data.binding.taskId === task.id && data.binding.workspaceId === workspace.id && data.requirementIds.includes(requirement.id) && scopeCovers2(data.binding.scope, requirement.scope) && data.digest === decisionDigest(data) && (checkoutRoot ? verifyDecisionContentAtRoot(checkoutRoot, data.binding).ok : data.binding.contentRefs.every((reference) => reference.kind !== "file"))).map(({ id }) => ({ id }));
|
|
18137
18163
|
var evidenceMatchesRequirement = (kind, evidenceKind, dimension, ruleId) => {
|
|
@@ -18366,6 +18392,11 @@ var exportDigest = (bundle) => sha256({
|
|
|
18366
18392
|
task: bundle.task
|
|
18367
18393
|
});
|
|
18368
18394
|
// packages/workit-core/src/core/external-action.ts
|
|
18395
|
+
var chainStepSchema = union([
|
|
18396
|
+
string2().min(1),
|
|
18397
|
+
object({ branch: string2().min(1) }).strict(),
|
|
18398
|
+
object({ pr: literal(true) }).strict()
|
|
18399
|
+
]);
|
|
18369
18400
|
var externalActionSchema = discriminatedUnion("operation", [
|
|
18370
18401
|
object({
|
|
18371
18402
|
operation: literal("git.branch_setup"),
|
|
@@ -18380,7 +18411,7 @@ var externalActionSchema = discriminatedUnion("operation", [
|
|
|
18380
18411
|
operation: literal("git.commit"),
|
|
18381
18412
|
payload: object({
|
|
18382
18413
|
message: string2().min(1).optional(),
|
|
18383
|
-
plan_steps: array(
|
|
18414
|
+
plan_steps: array(chainStepSchema).min(1).max(32).optional(),
|
|
18384
18415
|
plan_branch: string2().min(1).optional()
|
|
18385
18416
|
}).strict()
|
|
18386
18417
|
}).strict(),
|
|
@@ -18866,6 +18897,7 @@ var verifyNativeWorker = (verifier, input, binding) => {
|
|
|
18866
18897
|
expectedWorkspaceRevision: expected.expectedWorkspaceRevision,
|
|
18867
18898
|
state: expected.state,
|
|
18868
18899
|
session: expected.session,
|
|
18900
|
+
report: expected.report ?? null,
|
|
18869
18901
|
provenance: result.data,
|
|
18870
18902
|
...binding
|
|
18871
18903
|
});
|
|
@@ -18876,7 +18908,7 @@ var takeWorker = (token, binding, expected) => {
|
|
|
18876
18908
|
verifiedWorkers.delete(token);
|
|
18877
18909
|
if (!value)
|
|
18878
18910
|
return null;
|
|
18879
|
-
if (value.owner !== binding.owner || value.store !== binding.store || value.root !== binding.root || value.root !== value.store.root || !sameValue(value.caller, binding.caller) || value.taskId !== expected.taskId || value.workerId !== expected.workerId || value.expectedRevision !== expected.expectedRevision || value.expectedWorkspaceRevision !== expected.expectedWorkspaceRevision || value.state !== expected.state || !sameValue(value.session, expected.session))
|
|
18911
|
+
if (value.owner !== binding.owner || value.store !== binding.store || value.root !== binding.root || value.root !== value.store.root || !sameValue(value.caller, binding.caller) || value.taskId !== expected.taskId || value.workerId !== expected.workerId || value.expectedRevision !== expected.expectedRevision || value.expectedWorkspaceRevision !== expected.expectedWorkspaceRevision || value.state !== expected.state || !sameValue(value.session, expected.session) || !sameValue(value.report, expected.report ?? null))
|
|
18880
18912
|
return null;
|
|
18881
18913
|
return value;
|
|
18882
18914
|
};
|
|
@@ -18917,6 +18949,8 @@ var applyWorkerLifecycle = (input) => {
|
|
|
18917
18949
|
return failure("invalid_transition", "worker already has an observed session");
|
|
18918
18950
|
if (notStarted && !["dispatching", "cancelling"].includes(entry.data.state))
|
|
18919
18951
|
return failure("invalid_transition", "worker launch was not claimed");
|
|
18952
|
+
if (input.report && input.state !== "stopped")
|
|
18953
|
+
return failure("invalid_input", "worker reports require a stopped worker");
|
|
18920
18954
|
if ((input.state === "running" || input.state === "cancelling" || input.state === "stopped" && !notStarted) && !input.session)
|
|
18921
18955
|
return failure("invalid_input", "running worker observations require a session");
|
|
18922
18956
|
if (entry.data.session && !sameValue(entry.data.session, input.session))
|
|
@@ -18933,12 +18967,13 @@ var applyWorkerLifecycle = (input) => {
|
|
|
18933
18967
|
data: {
|
|
18934
18968
|
...entry.data,
|
|
18935
18969
|
state: terminalCancel ? "cancelling" : input.state,
|
|
18936
|
-
session: input.session
|
|
18970
|
+
session: input.session,
|
|
18971
|
+
report: entry.data.report ?? input.report ?? null
|
|
18937
18972
|
}
|
|
18938
18973
|
};
|
|
18939
18974
|
const shouldClear = input.state === "stopped" && workspace.data.writer !== null && workspace.data.writer.owner.taskId === task.data.id && workspace.data.writer.owner.workerId === input.workerId;
|
|
18940
18975
|
const shouldUncertain = input.state === "unknown" && workspace.data.writer !== null && workspace.data.writer.owner.taskId === task.data.id && workspace.data.writer.owner.workerId === input.workerId;
|
|
18941
|
-
if (entry.data.state === nextEntry.data.state && sameValue(entry.data.session, input.session) && !shouldClear && !shouldUncertain)
|
|
18976
|
+
if (entry.data.state === nextEntry.data.state && sameValue(entry.data.session, input.session) && sameValue(entry.data.report, nextEntry.data.report) && !shouldClear && !shouldUncertain)
|
|
18942
18977
|
return success(task.data.revision, workspace.data.revision, entry);
|
|
18943
18978
|
const changed = input.store.mutateTaskAndWorkspace({
|
|
18944
18979
|
taskId: task.data.id,
|
|
@@ -19356,14 +19391,30 @@ class WorkitCore {
|
|
|
19356
19391
|
const listed = this.store.listTasks();
|
|
19357
19392
|
if (!listed.ok)
|
|
19358
19393
|
return listed;
|
|
19359
|
-
const
|
|
19360
|
-
|
|
19361
|
-
|
|
19362
|
-
|
|
19363
|
-
|
|
19364
|
-
|
|
19365
|
-
|
|
19366
|
-
|
|
19394
|
+
const status = input.status ?? "open";
|
|
19395
|
+
const limit = input.limit ?? 20;
|
|
19396
|
+
const selected = listed.data.filter((task) => status === "all" ? true : status === "closed" ? task.status === "closed" : task.status !== "closed").sort((left, right) => right.updatedAt.localeCompare(left.updatedAt)).slice(0, limit);
|
|
19397
|
+
if (selected.length === 0)
|
|
19398
|
+
return success(null, null, []);
|
|
19399
|
+
const workspace = this.store.readWorkspace();
|
|
19400
|
+
if (!workspace.ok)
|
|
19401
|
+
return workspace;
|
|
19402
|
+
if (!workspace.data)
|
|
19403
|
+
return failure("not_found", "workspace not found");
|
|
19404
|
+
const tasks = selected.map((task) => ({
|
|
19405
|
+
id: task.id,
|
|
19406
|
+
revision: task.revision,
|
|
19407
|
+
workspaceRevision: workspace.data.revision,
|
|
19408
|
+
createdAt: task.createdAt,
|
|
19409
|
+
updatedAt: task.updatedAt,
|
|
19410
|
+
runtime: task.runtime ?? null,
|
|
19411
|
+
objective: task.intent.data.objective,
|
|
19412
|
+
status: task.status,
|
|
19413
|
+
closure: task.closure,
|
|
19414
|
+
progress: task.progress,
|
|
19415
|
+
writer: task.status === "closed" ? null : workspace.data.writer
|
|
19416
|
+
}));
|
|
19417
|
+
return success(null, null, tasks);
|
|
19367
19418
|
}
|
|
19368
19419
|
case "inspect": {
|
|
19369
19420
|
const task = this.store.readTask(input.taskId);
|
|
@@ -19372,7 +19423,7 @@ class WorkitCore {
|
|
|
19372
19423
|
const helper = this.helperTaskGuard(task.data, true);
|
|
19373
19424
|
if (!helper.ok)
|
|
19374
19425
|
return helper;
|
|
19375
|
-
if (input.view === "summary")
|
|
19426
|
+
if ((input.view ?? "summary") === "summary")
|
|
19376
19427
|
return this.summary(task.data);
|
|
19377
19428
|
return this.view(task.data);
|
|
19378
19429
|
}
|
|
@@ -19716,7 +19767,7 @@ class WorkitCore {
|
|
|
19716
19767
|
const evaluations = evaluateEvidence(task.data, current.data);
|
|
19717
19768
|
const verified = evidence.some((entry) => {
|
|
19718
19769
|
const evaluation = evaluations.find((item) => item.evidenceId === entry.id);
|
|
19719
|
-
return findingVerificationPasses(finding.data.candidateId, {
|
|
19770
|
+
return entry.recordedAt >= finding.recordedAt && findingVerificationPasses(finding.data.candidateId, {
|
|
19720
19771
|
kind: entry.data.kind,
|
|
19721
19772
|
candidateId: entry.data.candidateId,
|
|
19722
19773
|
status: evaluation?.status ?? "missing"
|
|
@@ -19796,7 +19847,18 @@ class WorkitCore {
|
|
|
19796
19847
|
if (task.data.status !== "active")
|
|
19797
19848
|
return failure("invalid_transition", "paused or closed tasks cannot assign workers");
|
|
19798
19849
|
if (!bindingCovers(task.data.intent.data.scope, input.assignment.scope))
|
|
19799
|
-
return failure("permission_denied", "worker assignment is outside task scope"
|
|
19850
|
+
return failure("permission_denied", "worker assignment is outside task scope", {
|
|
19851
|
+
fields: [
|
|
19852
|
+
{
|
|
19853
|
+
path: "assignment.scope",
|
|
19854
|
+
reason: `paths ${JSON.stringify(input.assignment.scope.paths)} must be contained by lead paths ${JSON.stringify(task.data.intent.data.scope.paths)}`
|
|
19855
|
+
},
|
|
19856
|
+
{
|
|
19857
|
+
path: "task.intent.scope",
|
|
19858
|
+
reason: `lead paths are ${JSON.stringify(task.data.intent.data.scope.paths)}`
|
|
19859
|
+
}
|
|
19860
|
+
]
|
|
19861
|
+
});
|
|
19800
19862
|
if (input.assignment.decisionIds.some((id) => !task.data.decisions.some((entry) => entry.id === id)))
|
|
19801
19863
|
return failure("invalid_input", "worker assignment references an unknown decision");
|
|
19802
19864
|
if (input.assignment.requirementIds.some((id) => !task.data.policy?.requirements.some((requirement) => requirement.id === id)))
|
|
@@ -20003,7 +20065,15 @@ class WorkitCore {
|
|
|
20003
20065
|
...item,
|
|
20004
20066
|
recordedAt: mutation.now,
|
|
20005
20067
|
provenance: attested.data,
|
|
20006
|
-
data: {
|
|
20068
|
+
data: {
|
|
20069
|
+
...item.data,
|
|
20070
|
+
state: "dispatching",
|
|
20071
|
+
coordinator: {
|
|
20072
|
+
kind: "host",
|
|
20073
|
+
host: this.context.caller.host,
|
|
20074
|
+
handle: this.context.caller.actor
|
|
20075
|
+
}
|
|
20076
|
+
}
|
|
20007
20077
|
} : item)
|
|
20008
20078
|
});
|
|
20009
20079
|
}
|
|
@@ -20079,6 +20149,7 @@ class WorkitCore {
|
|
|
20079
20149
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
20080
20150
|
state: "stopped",
|
|
20081
20151
|
session: null,
|
|
20152
|
+
report: null,
|
|
20082
20153
|
provenance: attested.data,
|
|
20083
20154
|
owner: this.authorityOwner,
|
|
20084
20155
|
store: this.store,
|
|
@@ -20386,10 +20457,12 @@ class WorkitCore {
|
|
|
20386
20457
|
return workspace;
|
|
20387
20458
|
if (!workspace.data)
|
|
20388
20459
|
return failure("not_found", "workspace not found");
|
|
20389
|
-
const
|
|
20460
|
+
const historical = task.status === "closed" ? task.candidates.at(-1) : undefined;
|
|
20461
|
+
const current = historical ? success(null, null, historical) : captureCandidate(this.store.root, task.intent.data.scope, environment());
|
|
20390
20462
|
if (!current.ok)
|
|
20391
20463
|
return current;
|
|
20392
|
-
const
|
|
20464
|
+
const evaluationWorkspace = task.status === "closed" ? { ...workspace.data, writer: null } : workspace.data;
|
|
20465
|
+
const requirements = evaluateRequirements(task, evaluationWorkspace, this.context.capabilities, current.data, this.store.root, this.context.caller);
|
|
20393
20466
|
return success(task.revision, workspace.data.revision, {
|
|
20394
20467
|
id: task.id,
|
|
20395
20468
|
revision: task.revision,
|
|
@@ -20403,7 +20476,7 @@ class WorkitCore {
|
|
|
20403
20476
|
progress: task.progress,
|
|
20404
20477
|
policy: task.policy,
|
|
20405
20478
|
requirements,
|
|
20406
|
-
writer:
|
|
20479
|
+
writer: evaluationWorkspace.writer
|
|
20407
20480
|
});
|
|
20408
20481
|
}
|
|
20409
20482
|
view(task) {
|
|
@@ -20412,13 +20485,15 @@ class WorkitCore {
|
|
|
20412
20485
|
return workspace;
|
|
20413
20486
|
if (!workspace.data)
|
|
20414
20487
|
return failure("not_found", "workspace not found");
|
|
20415
|
-
const
|
|
20488
|
+
const historical = task.status === "closed" ? task.candidates.at(-1) : undefined;
|
|
20489
|
+
const current = historical ? success(null, null, historical) : captureCandidate(this.store.root, task.intent.data.scope, environment());
|
|
20416
20490
|
if (!current.ok)
|
|
20417
20491
|
return current;
|
|
20418
|
-
const
|
|
20492
|
+
const evaluationWorkspace = task.status === "closed" ? { ...workspace.data, writer: null } : workspace.data;
|
|
20493
|
+
const requirements = evaluateRequirements(task, evaluationWorkspace, this.context.capabilities, current.data, this.store.root, this.context.caller);
|
|
20419
20494
|
return success(task.revision, workspace.data.revision, {
|
|
20420
20495
|
task,
|
|
20421
|
-
workspace:
|
|
20496
|
+
workspace: evaluationWorkspace,
|
|
20422
20497
|
capabilities: this.context.capabilities,
|
|
20423
20498
|
requirements,
|
|
20424
20499
|
evidence: evaluateEvidence(task, current.data)
|
|
@@ -20509,8 +20584,6 @@ class WorkitCore {
|
|
|
20509
20584
|
return helper;
|
|
20510
20585
|
if (status === "paused" && task.data.status !== "active" || status === "active" && task.data.status !== "paused")
|
|
20511
20586
|
return failure("invalid_transition", `cannot transition ${task.data.status} to ${status}`);
|
|
20512
|
-
if (status === "active" && input.authorityRefs.length === 0)
|
|
20513
|
-
return failure("permission_denied", "resuming a task requires authority references");
|
|
20514
20587
|
const workspace = this.store.readWorkspace();
|
|
20515
20588
|
if (!workspace.ok)
|
|
20516
20589
|
return workspace;
|
|
@@ -20540,7 +20613,7 @@ class WorkitCore {
|
|
|
20540
20613
|
return failure("needs_input", "imported task requires fresh destination evidence");
|
|
20541
20614
|
if (current.data.blockers.some((blocker) => blocker.dependentAction === "resume" || blocker.reason === "worker state requires reconciliation"))
|
|
20542
20615
|
return failure("recovery_required", "imported task requires resume reconciliation");
|
|
20543
|
-
if (!validResumeApproval(task.data, workspace.data, input.authorityRefs, this.context))
|
|
20616
|
+
if (!validResumeApproval(task.data, workspace.data, input.authorityRefs ?? [], this.context))
|
|
20544
20617
|
return failure("permission_denied", "imported resume requires native destination approval");
|
|
20545
20618
|
resumeCandidate = current.data.candidate;
|
|
20546
20619
|
}
|
|
@@ -20670,7 +20743,7 @@ class WorkitCore {
|
|
|
20670
20743
|
task: (value, context) => success(context.revision, null, {
|
|
20671
20744
|
...value,
|
|
20672
20745
|
status: "closed",
|
|
20673
|
-
candidates:
|
|
20746
|
+
candidates: taskForView.candidates,
|
|
20674
20747
|
closure: {
|
|
20675
20748
|
outcome: closure.data.outcome,
|
|
20676
20749
|
at: context.now,
|
|
@@ -20745,7 +20818,7 @@ function run2(root, executable, args, env = {}) {
|
|
|
20745
20818
|
var SUPPORT_MATRIX = {
|
|
20746
20819
|
bun: "1.4.1",
|
|
20747
20820
|
node: { minimum: "24", current: "24.20.0" },
|
|
20748
|
-
opencode: { minimum: "1.
|
|
20821
|
+
opencode: { minimum: "1.18.30", current: "1.18.30" },
|
|
20749
20822
|
codex: { cli: "0.153.4", desktopPackage: "26.901.20858", bundledCodexCli: "0.153.0-alpha.5" },
|
|
20750
20823
|
os: ["ubuntu-latest", "macos-latest", "windows-latest"]
|
|
20751
20824
|
};
|
package/dist/workit-hook.js
CHANGED
|
@@ -7705,6 +7705,7 @@ var workerSchema = object({
|
|
|
7705
7705
|
assignment: assignmentSchema,
|
|
7706
7706
|
state: _enum(["assigned", "dispatching", "running", "cancelling", "stopped", "unknown"]),
|
|
7707
7707
|
session: refSchema.nullable(),
|
|
7708
|
+
coordinator: refSchema.optional(),
|
|
7708
7709
|
report: workerReportSchema.nullable()
|
|
7709
7710
|
}).strict();
|
|
7710
7711
|
var ownerSchema = object({
|
|
@@ -7816,6 +7817,7 @@ var taskSummarySchema = object({
|
|
|
7816
7817
|
requirements: array(requirementEvaluationSchema),
|
|
7817
7818
|
writer: workspaceRecordSchema.shape.writer
|
|
7818
7819
|
}).strict();
|
|
7820
|
+
var taskListItemSchema = taskSummarySchema.omit({ policy: true, requirements: true });
|
|
7819
7821
|
var exportBundleSchema = object({
|
|
7820
7822
|
schemaVersion: literal(1),
|
|
7821
7823
|
exportedAt: utc,
|
|
@@ -7837,11 +7839,15 @@ var taskOperations = {
|
|
|
7837
7839
|
expectedWorkspaceRevision: revision.nullable().optional(),
|
|
7838
7840
|
intent: intentSchema
|
|
7839
7841
|
}),
|
|
7840
|
-
list: operation({
|
|
7842
|
+
list: operation({
|
|
7843
|
+
action: literal("list"),
|
|
7844
|
+
status: _enum(["open", "closed", "all"]).optional(),
|
|
7845
|
+
limit: number2().int().min(1).max(50).optional()
|
|
7846
|
+
}),
|
|
7841
7847
|
inspect: operation({
|
|
7842
7848
|
action: literal("inspect"),
|
|
7843
7849
|
...taskId,
|
|
7844
|
-
view: _enum(["summary", "full"])
|
|
7850
|
+
view: _enum(["summary", "full"]).optional()
|
|
7845
7851
|
}),
|
|
7846
7852
|
revise: operation({
|
|
7847
7853
|
action: literal("revise"),
|
|
@@ -7869,7 +7875,7 @@ var taskOperations = {
|
|
|
7869
7875
|
...taskId,
|
|
7870
7876
|
...revisions,
|
|
7871
7877
|
expectedWorkspaceRevision: revision.optional(),
|
|
7872
|
-
authorityRefs: array(refSchema)
|
|
7878
|
+
authorityRefs: array(refSchema).optional()
|
|
7873
7879
|
}),
|
|
7874
7880
|
close: operation({
|
|
7875
7881
|
action: literal("close"),
|
|
@@ -9641,6 +9647,10 @@ var validPath = (value) => typeof value === "string" && value.length > 0 && ![..
|
|
|
9641
9647
|
}) && !value.split(/[\\/]/).includes("..");
|
|
9642
9648
|
var callerValue = (value) => ({ host: value.host, actor: value.actor });
|
|
9643
9649
|
var workerIdOf = (value) => value.workerId ?? null;
|
|
9650
|
+
var currentWriterOwnsTask = (task, workspace, caller) => {
|
|
9651
|
+
const owner = workspace.writer?.owner;
|
|
9652
|
+
return task.status === "active" && task.workspaceId === workspace.id && owner?.taskId === task.id && owner.session.host === caller.host && owner.session.handle === caller.actor;
|
|
9653
|
+
};
|
|
9644
9654
|
function assertProductWriteAllowed(input) {
|
|
9645
9655
|
if (!Array.isArray(input.paths) || input.paths.some((path) => !validPath(path)))
|
|
9646
9656
|
return failure("invalid_input", "invalid product write path");
|
|
@@ -9998,9 +10008,11 @@ var standingReceiptFor = (root, workspace, cls, dir) => {
|
|
|
9998
10008
|
var verifyStandingApproval = (root, task, caller, binding) => {
|
|
9999
10009
|
if (task.status !== "active")
|
|
10000
10010
|
return failure("invalid_transition", "only active tasks can authorize actions");
|
|
10001
|
-
const
|
|
10002
|
-
if (!
|
|
10003
|
-
return
|
|
10011
|
+
const workspace = new TaskStore(root).readWorkspace();
|
|
10012
|
+
if (!workspace.ok)
|
|
10013
|
+
return workspace;
|
|
10014
|
+
if (!workspace.data || !currentWriterOwnsTask(task, workspace.data, caller))
|
|
10015
|
+
return failure("permission_denied", "standing approval requires the current writer session");
|
|
10004
10016
|
const standing = binding.standing;
|
|
10005
10017
|
if (!standing || typeof standing.workspace !== "string" || typeof standing.class !== "string")
|
|
10006
10018
|
return failure("invalid_input", "standing approval needs a workspace and class");
|
|
@@ -10237,6 +10249,20 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
|
10237
10249
|
};
|
|
10238
10250
|
var verifyContentRefs = (store, binding) => verifyDecisionContentAtRoot(store.root, binding);
|
|
10239
10251
|
var storedDecisionApplicable = (store, task, purpose, binding) => task.decisions.filter((entry) => entry.provenance.kind !== "imported" && decisionMatches(entry, purpose, binding) && verifyDecisionContentAtRoot(store.root, entry.data.binding).ok);
|
|
10252
|
+
var approvalAuthorityMatches = (store, task, approval, current) => {
|
|
10253
|
+
if (approval.kind !== "host_observed" || approval.receipts.length === 0)
|
|
10254
|
+
return false;
|
|
10255
|
+
if (provenanceMatches(approval, current))
|
|
10256
|
+
return true;
|
|
10257
|
+
const session = current.kind === "host_observed" ? current.session : null;
|
|
10258
|
+
if (!session || session.kind !== "host")
|
|
10259
|
+
return false;
|
|
10260
|
+
const workspace = store.readWorkspace();
|
|
10261
|
+
return workspace.ok && workspace.data !== null && currentWriterOwnsTask(task, workspace.data, {
|
|
10262
|
+
host: session.host,
|
|
10263
|
+
actor: session.handle
|
|
10264
|
+
});
|
|
10265
|
+
};
|
|
10240
10266
|
var validateAction = (store, task, workspaceId, input, authority) => {
|
|
10241
10267
|
if (task.status !== "active")
|
|
10242
10268
|
return failure("invalid_transition", "only active tasks can authorize actions");
|
|
@@ -10262,7 +10288,7 @@ var validateAction = (store, task, workspaceId, input, authority) => {
|
|
|
10262
10288
|
return failure("permission_denied", "native reservation authority does not match decision");
|
|
10263
10289
|
if (decision.response !== "approved")
|
|
10264
10290
|
return failure("permission_denied", "decision was rejected");
|
|
10265
|
-
if (
|
|
10291
|
+
if (!approvalAuthorityMatches(store, task, entry.provenance, authority.provenance))
|
|
10266
10292
|
return failure("permission_denied", "action approval lacks native receipt assurance");
|
|
10267
10293
|
const receipts = entry.provenance.receipts;
|
|
10268
10294
|
const standing = receipts.filter((receipt) => typeof receipt === "object" && receipt !== null && receipt.kind === "standing");
|
|
@@ -10429,7 +10455,7 @@ function settleAction(input) {
|
|
|
10429
10455
|
return failure("not_found", "decision not found");
|
|
10430
10456
|
if (!authorityDecisionMatches(authority, entry.data))
|
|
10431
10457
|
return failure("permission_denied", "native settlement authority does not match decision");
|
|
10432
|
-
if (!
|
|
10458
|
+
if (!approvalAuthorityMatches(input.store, current, entry.provenance, authority.provenance))
|
|
10433
10459
|
return failure("permission_denied", "native settlement caller does not match approval");
|
|
10434
10460
|
if (entry.data.purpose !== "action" || entry.data.digest !== decisionDigest(entry.data))
|
|
10435
10461
|
return failure("permission_denied", "decision binding is invalid");
|
|
@@ -10528,7 +10554,7 @@ function reconcileAction(input) {
|
|
|
10528
10554
|
const entry = current.decisions.find((candidate) => candidate.id === input.decisionId);
|
|
10529
10555
|
if (!entry || entry.data.purpose !== "action")
|
|
10530
10556
|
return failure("not_found", "decision not found");
|
|
10531
|
-
if (!authorityDecisionMatches(authority, entry.data) || !
|
|
10557
|
+
if (!authorityDecisionMatches(authority, entry.data) || !approvalAuthorityMatches(input.store, current, entry.provenance, authority.provenance))
|
|
10532
10558
|
return failure("permission_denied", "native reconciliation caller does not match approval");
|
|
10533
10559
|
if (entry.data.response !== "approved" || entry.data.revoked !== null || entry.data.digest !== decisionDigest(entry.data) || entry.data.binding.taskId !== current.id || entry.data.binding.workspaceId !== current.workspaceId || entry.data.binding.workspaceId !== workspace.data.id)
|
|
10534
10560
|
return failure("permission_denied", "reconciliation decision binding is invalid");
|
|
@@ -10865,7 +10891,7 @@ var resolveBinding = (evidence, pin, currentCandidateId) => {
|
|
|
10865
10891
|
candidateId: evidence.candidateId ?? fallback
|
|
10866
10892
|
};
|
|
10867
10893
|
};
|
|
10868
|
-
var findingVerificationPasses = (findingCandidateId, evidence) => evidence.status === "passed" && (evidence.kind === "check" || evidence.kind === "review") && ((findingCandidateId ?? null) === null || evidence.candidateId
|
|
10894
|
+
var findingVerificationPasses = (findingCandidateId, evidence) => evidence.status === "passed" && (evidence.kind === "check" || evidence.kind === "review") && ((findingCandidateId ?? null) === null || evidence.candidateId != null);
|
|
10869
10895
|
var applicableDecision2 = (task, workspace, requirement, checkoutRoot) => task.decisions.filter((entry) => entry.provenance.kind !== "imported").map((entry) => entry.data).filter((decision) => decision.purpose === "limitation" && decision.response === "approved" && decision.revoked === null && decision.binding.taskId === task.id && decision.binding.workspaceId === workspace.id && decision.requirementIds.includes(requirement.id) && decision.binding.scope && scopeCovers2(decision.binding.scope, requirement.scope) && decision.digest === decisionDigest(decision) && (checkoutRoot ? verifyDecisionContentAtRoot(checkoutRoot, decision.binding).ok : decision.binding.contentRefs.every((reference) => reference.kind !== "file")));
|
|
10870
10896
|
var applicableRequirementDecision = (task, workspace, requirement, checkoutRoot) => task.decisions.filter(({ data, provenance }) => provenance.kind !== "imported" && data.purpose !== "limitation" && (data.response === "approved" || data.response === "stated") && data.revoked === null && data.binding.taskId === task.id && data.binding.workspaceId === workspace.id && data.requirementIds.includes(requirement.id) && scopeCovers2(data.binding.scope, requirement.scope) && data.digest === decisionDigest(data) && (checkoutRoot ? verifyDecisionContentAtRoot(checkoutRoot, data.binding).ok : data.binding.contentRefs.every((reference) => reference.kind !== "file"))).map(({ id }) => ({ id }));
|
|
10871
10897
|
var evidenceMatchesRequirement = (kind, evidenceKind, dimension, ruleId) => {
|
|
@@ -11334,6 +11360,11 @@ function shouldDenyShellRoute(dir, command) {
|
|
|
11334
11360
|
return shellRouteIntent(command);
|
|
11335
11361
|
}
|
|
11336
11362
|
// packages/workit-core/src/core/external-action.ts
|
|
11363
|
+
var chainStepSchema = union([
|
|
11364
|
+
string2().min(1),
|
|
11365
|
+
object({ branch: string2().min(1) }).strict(),
|
|
11366
|
+
object({ pr: literal(true) }).strict()
|
|
11367
|
+
]);
|
|
11337
11368
|
var externalActionSchema = discriminatedUnion("operation", [
|
|
11338
11369
|
object({
|
|
11339
11370
|
operation: literal("git.branch_setup"),
|
|
@@ -11348,7 +11379,7 @@ var externalActionSchema = discriminatedUnion("operation", [
|
|
|
11348
11379
|
operation: literal("git.commit"),
|
|
11349
11380
|
payload: object({
|
|
11350
11381
|
message: string2().min(1).optional(),
|
|
11351
|
-
plan_steps: array(
|
|
11382
|
+
plan_steps: array(chainStepSchema).min(1).max(32).optional(),
|
|
11352
11383
|
plan_branch: string2().min(1).optional()
|
|
11353
11384
|
}).strict()
|
|
11354
11385
|
}).strict(),
|
|
@@ -11834,6 +11865,7 @@ var verifyNativeWorker = (verifier, input, binding) => {
|
|
|
11834
11865
|
expectedWorkspaceRevision: expected.expectedWorkspaceRevision,
|
|
11835
11866
|
state: expected.state,
|
|
11836
11867
|
session: expected.session,
|
|
11868
|
+
report: expected.report ?? null,
|
|
11837
11869
|
provenance: result.data,
|
|
11838
11870
|
...binding
|
|
11839
11871
|
});
|
|
@@ -11844,7 +11876,7 @@ var takeWorker = (token, binding, expected) => {
|
|
|
11844
11876
|
verifiedWorkers.delete(token);
|
|
11845
11877
|
if (!value)
|
|
11846
11878
|
return null;
|
|
11847
|
-
if (value.owner !== binding.owner || value.store !== binding.store || value.root !== binding.root || value.root !== value.store.root || !sameValue(value.caller, binding.caller) || value.taskId !== expected.taskId || value.workerId !== expected.workerId || value.expectedRevision !== expected.expectedRevision || value.expectedWorkspaceRevision !== expected.expectedWorkspaceRevision || value.state !== expected.state || !sameValue(value.session, expected.session))
|
|
11879
|
+
if (value.owner !== binding.owner || value.store !== binding.store || value.root !== binding.root || value.root !== value.store.root || !sameValue(value.caller, binding.caller) || value.taskId !== expected.taskId || value.workerId !== expected.workerId || value.expectedRevision !== expected.expectedRevision || value.expectedWorkspaceRevision !== expected.expectedWorkspaceRevision || value.state !== expected.state || !sameValue(value.session, expected.session) || !sameValue(value.report, expected.report ?? null))
|
|
11848
11880
|
return null;
|
|
11849
11881
|
return value;
|
|
11850
11882
|
};
|
|
@@ -11885,6 +11917,8 @@ var applyWorkerLifecycle = (input) => {
|
|
|
11885
11917
|
return failure("invalid_transition", "worker already has an observed session");
|
|
11886
11918
|
if (notStarted && !["dispatching", "cancelling"].includes(entry.data.state))
|
|
11887
11919
|
return failure("invalid_transition", "worker launch was not claimed");
|
|
11920
|
+
if (input.report && input.state !== "stopped")
|
|
11921
|
+
return failure("invalid_input", "worker reports require a stopped worker");
|
|
11888
11922
|
if ((input.state === "running" || input.state === "cancelling" || input.state === "stopped" && !notStarted) && !input.session)
|
|
11889
11923
|
return failure("invalid_input", "running worker observations require a session");
|
|
11890
11924
|
if (entry.data.session && !sameValue(entry.data.session, input.session))
|
|
@@ -11901,12 +11935,13 @@ var applyWorkerLifecycle = (input) => {
|
|
|
11901
11935
|
data: {
|
|
11902
11936
|
...entry.data,
|
|
11903
11937
|
state: terminalCancel ? "cancelling" : input.state,
|
|
11904
|
-
session: input.session
|
|
11938
|
+
session: input.session,
|
|
11939
|
+
report: entry.data.report ?? input.report ?? null
|
|
11905
11940
|
}
|
|
11906
11941
|
};
|
|
11907
11942
|
const shouldClear = input.state === "stopped" && workspace.data.writer !== null && workspace.data.writer.owner.taskId === task.data.id && workspace.data.writer.owner.workerId === input.workerId;
|
|
11908
11943
|
const shouldUncertain = input.state === "unknown" && workspace.data.writer !== null && workspace.data.writer.owner.taskId === task.data.id && workspace.data.writer.owner.workerId === input.workerId;
|
|
11909
|
-
if (entry.data.state === nextEntry.data.state && sameValue(entry.data.session, input.session) && !shouldClear && !shouldUncertain)
|
|
11944
|
+
if (entry.data.state === nextEntry.data.state && sameValue(entry.data.session, input.session) && sameValue(entry.data.report, nextEntry.data.report) && !shouldClear && !shouldUncertain)
|
|
11910
11945
|
return success(task.data.revision, workspace.data.revision, entry);
|
|
11911
11946
|
const changed = input.store.mutateTaskAndWorkspace({
|
|
11912
11947
|
taskId: task.data.id,
|
|
@@ -12324,14 +12359,30 @@ class WorkitCore {
|
|
|
12324
12359
|
const listed = this.store.listTasks();
|
|
12325
12360
|
if (!listed.ok)
|
|
12326
12361
|
return listed;
|
|
12327
|
-
const
|
|
12328
|
-
|
|
12329
|
-
|
|
12330
|
-
|
|
12331
|
-
|
|
12332
|
-
|
|
12333
|
-
|
|
12334
|
-
|
|
12362
|
+
const status = input.status ?? "open";
|
|
12363
|
+
const limit = input.limit ?? 20;
|
|
12364
|
+
const selected = listed.data.filter((task) => status === "all" ? true : status === "closed" ? task.status === "closed" : task.status !== "closed").sort((left, right) => right.updatedAt.localeCompare(left.updatedAt)).slice(0, limit);
|
|
12365
|
+
if (selected.length === 0)
|
|
12366
|
+
return success(null, null, []);
|
|
12367
|
+
const workspace = this.store.readWorkspace();
|
|
12368
|
+
if (!workspace.ok)
|
|
12369
|
+
return workspace;
|
|
12370
|
+
if (!workspace.data)
|
|
12371
|
+
return failure("not_found", "workspace not found");
|
|
12372
|
+
const tasks = selected.map((task) => ({
|
|
12373
|
+
id: task.id,
|
|
12374
|
+
revision: task.revision,
|
|
12375
|
+
workspaceRevision: workspace.data.revision,
|
|
12376
|
+
createdAt: task.createdAt,
|
|
12377
|
+
updatedAt: task.updatedAt,
|
|
12378
|
+
runtime: task.runtime ?? null,
|
|
12379
|
+
objective: task.intent.data.objective,
|
|
12380
|
+
status: task.status,
|
|
12381
|
+
closure: task.closure,
|
|
12382
|
+
progress: task.progress,
|
|
12383
|
+
writer: task.status === "closed" ? null : workspace.data.writer
|
|
12384
|
+
}));
|
|
12385
|
+
return success(null, null, tasks);
|
|
12335
12386
|
}
|
|
12336
12387
|
case "inspect": {
|
|
12337
12388
|
const task = this.store.readTask(input.taskId);
|
|
@@ -12340,7 +12391,7 @@ class WorkitCore {
|
|
|
12340
12391
|
const helper = this.helperTaskGuard(task.data, true);
|
|
12341
12392
|
if (!helper.ok)
|
|
12342
12393
|
return helper;
|
|
12343
|
-
if (input.view === "summary")
|
|
12394
|
+
if ((input.view ?? "summary") === "summary")
|
|
12344
12395
|
return this.summary(task.data);
|
|
12345
12396
|
return this.view(task.data);
|
|
12346
12397
|
}
|
|
@@ -12684,7 +12735,7 @@ class WorkitCore {
|
|
|
12684
12735
|
const evaluations = evaluateEvidence(task.data, current.data);
|
|
12685
12736
|
const verified = evidence.some((entry) => {
|
|
12686
12737
|
const evaluation = evaluations.find((item) => item.evidenceId === entry.id);
|
|
12687
|
-
return findingVerificationPasses(finding.data.candidateId, {
|
|
12738
|
+
return entry.recordedAt >= finding.recordedAt && findingVerificationPasses(finding.data.candidateId, {
|
|
12688
12739
|
kind: entry.data.kind,
|
|
12689
12740
|
candidateId: entry.data.candidateId,
|
|
12690
12741
|
status: evaluation?.status ?? "missing"
|
|
@@ -12764,7 +12815,18 @@ class WorkitCore {
|
|
|
12764
12815
|
if (task.data.status !== "active")
|
|
12765
12816
|
return failure("invalid_transition", "paused or closed tasks cannot assign workers");
|
|
12766
12817
|
if (!bindingCovers(task.data.intent.data.scope, input.assignment.scope))
|
|
12767
|
-
return failure("permission_denied", "worker assignment is outside task scope"
|
|
12818
|
+
return failure("permission_denied", "worker assignment is outside task scope", {
|
|
12819
|
+
fields: [
|
|
12820
|
+
{
|
|
12821
|
+
path: "assignment.scope",
|
|
12822
|
+
reason: `paths ${JSON.stringify(input.assignment.scope.paths)} must be contained by lead paths ${JSON.stringify(task.data.intent.data.scope.paths)}`
|
|
12823
|
+
},
|
|
12824
|
+
{
|
|
12825
|
+
path: "task.intent.scope",
|
|
12826
|
+
reason: `lead paths are ${JSON.stringify(task.data.intent.data.scope.paths)}`
|
|
12827
|
+
}
|
|
12828
|
+
]
|
|
12829
|
+
});
|
|
12768
12830
|
if (input.assignment.decisionIds.some((id) => !task.data.decisions.some((entry) => entry.id === id)))
|
|
12769
12831
|
return failure("invalid_input", "worker assignment references an unknown decision");
|
|
12770
12832
|
if (input.assignment.requirementIds.some((id) => !task.data.policy?.requirements.some((requirement) => requirement.id === id)))
|
|
@@ -12971,7 +13033,15 @@ class WorkitCore {
|
|
|
12971
13033
|
...item,
|
|
12972
13034
|
recordedAt: mutation.now,
|
|
12973
13035
|
provenance: attested.data,
|
|
12974
|
-
data: {
|
|
13036
|
+
data: {
|
|
13037
|
+
...item.data,
|
|
13038
|
+
state: "dispatching",
|
|
13039
|
+
coordinator: {
|
|
13040
|
+
kind: "host",
|
|
13041
|
+
host: this.context.caller.host,
|
|
13042
|
+
handle: this.context.caller.actor
|
|
13043
|
+
}
|
|
13044
|
+
}
|
|
12975
13045
|
} : item)
|
|
12976
13046
|
});
|
|
12977
13047
|
}
|
|
@@ -13047,6 +13117,7 @@ class WorkitCore {
|
|
|
13047
13117
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
13048
13118
|
state: "stopped",
|
|
13049
13119
|
session: null,
|
|
13120
|
+
report: null,
|
|
13050
13121
|
provenance: attested.data,
|
|
13051
13122
|
owner: this.authorityOwner,
|
|
13052
13123
|
store: this.store,
|
|
@@ -13354,10 +13425,12 @@ class WorkitCore {
|
|
|
13354
13425
|
return workspace;
|
|
13355
13426
|
if (!workspace.data)
|
|
13356
13427
|
return failure("not_found", "workspace not found");
|
|
13357
|
-
const
|
|
13428
|
+
const historical = task.status === "closed" ? task.candidates.at(-1) : undefined;
|
|
13429
|
+
const current = historical ? success(null, null, historical) : captureCandidate(this.store.root, task.intent.data.scope, environment());
|
|
13358
13430
|
if (!current.ok)
|
|
13359
13431
|
return current;
|
|
13360
|
-
const
|
|
13432
|
+
const evaluationWorkspace = task.status === "closed" ? { ...workspace.data, writer: null } : workspace.data;
|
|
13433
|
+
const requirements = evaluateRequirements(task, evaluationWorkspace, this.context.capabilities, current.data, this.store.root, this.context.caller);
|
|
13361
13434
|
return success(task.revision, workspace.data.revision, {
|
|
13362
13435
|
id: task.id,
|
|
13363
13436
|
revision: task.revision,
|
|
@@ -13371,7 +13444,7 @@ class WorkitCore {
|
|
|
13371
13444
|
progress: task.progress,
|
|
13372
13445
|
policy: task.policy,
|
|
13373
13446
|
requirements,
|
|
13374
|
-
writer:
|
|
13447
|
+
writer: evaluationWorkspace.writer
|
|
13375
13448
|
});
|
|
13376
13449
|
}
|
|
13377
13450
|
view(task) {
|
|
@@ -13380,13 +13453,15 @@ class WorkitCore {
|
|
|
13380
13453
|
return workspace;
|
|
13381
13454
|
if (!workspace.data)
|
|
13382
13455
|
return failure("not_found", "workspace not found");
|
|
13383
|
-
const
|
|
13456
|
+
const historical = task.status === "closed" ? task.candidates.at(-1) : undefined;
|
|
13457
|
+
const current = historical ? success(null, null, historical) : captureCandidate(this.store.root, task.intent.data.scope, environment());
|
|
13384
13458
|
if (!current.ok)
|
|
13385
13459
|
return current;
|
|
13386
|
-
const
|
|
13460
|
+
const evaluationWorkspace = task.status === "closed" ? { ...workspace.data, writer: null } : workspace.data;
|
|
13461
|
+
const requirements = evaluateRequirements(task, evaluationWorkspace, this.context.capabilities, current.data, this.store.root, this.context.caller);
|
|
13387
13462
|
return success(task.revision, workspace.data.revision, {
|
|
13388
13463
|
task,
|
|
13389
|
-
workspace:
|
|
13464
|
+
workspace: evaluationWorkspace,
|
|
13390
13465
|
capabilities: this.context.capabilities,
|
|
13391
13466
|
requirements,
|
|
13392
13467
|
evidence: evaluateEvidence(task, current.data)
|
|
@@ -13477,8 +13552,6 @@ class WorkitCore {
|
|
|
13477
13552
|
return helper;
|
|
13478
13553
|
if (status === "paused" && task.data.status !== "active" || status === "active" && task.data.status !== "paused")
|
|
13479
13554
|
return failure("invalid_transition", `cannot transition ${task.data.status} to ${status}`);
|
|
13480
|
-
if (status === "active" && input.authorityRefs.length === 0)
|
|
13481
|
-
return failure("permission_denied", "resuming a task requires authority references");
|
|
13482
13555
|
const workspace = this.store.readWorkspace();
|
|
13483
13556
|
if (!workspace.ok)
|
|
13484
13557
|
return workspace;
|
|
@@ -13508,7 +13581,7 @@ class WorkitCore {
|
|
|
13508
13581
|
return failure("needs_input", "imported task requires fresh destination evidence");
|
|
13509
13582
|
if (current.data.blockers.some((blocker) => blocker.dependentAction === "resume" || blocker.reason === "worker state requires reconciliation"))
|
|
13510
13583
|
return failure("recovery_required", "imported task requires resume reconciliation");
|
|
13511
|
-
if (!validResumeApproval(task.data, workspace.data, input.authorityRefs, this.context))
|
|
13584
|
+
if (!validResumeApproval(task.data, workspace.data, input.authorityRefs ?? [], this.context))
|
|
13512
13585
|
return failure("permission_denied", "imported resume requires native destination approval");
|
|
13513
13586
|
resumeCandidate = current.data.candidate;
|
|
13514
13587
|
}
|
|
@@ -13638,7 +13711,7 @@ class WorkitCore {
|
|
|
13638
13711
|
task: (value, context) => success(context.revision, null, {
|
|
13639
13712
|
...value,
|
|
13640
13713
|
status: "closed",
|
|
13641
|
-
candidates:
|
|
13714
|
+
candidates: taskForView.candidates,
|
|
13642
13715
|
closure: {
|
|
13643
13716
|
outcome: closure.data.outcome,
|
|
13644
13717
|
at: context.now,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brainervirus/workit-codex",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.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": "^1.
|
|
42
|
-
"@brainervirus/workit-mcp": "^1.
|
|
41
|
+
"@brainervirus/workit-core": "^1.2.1",
|
|
42
|
+
"@brainervirus/workit-mcp": "^1.2.1"
|
|
43
43
|
},
|
|
44
44
|
"engines": {
|
|
45
45
|
"node": ">=24"
|