@brainervirus/workit-cursor 1.0.11 → 1.1.0
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 +732 -244
- package/dist/mcp-server.js +4130 -3842
- package/dist/workit-hook.js +729 -243
- package/package.json +3 -3
|
@@ -7492,7 +7492,87 @@ var SCHEMA_VERSION = 1, POLICY_VERSION = "1.0.0", OPERATION_FAMILIES, uuidPatter
|
|
|
7492
7492
|
const covers = (path) => normOuter.paths.some((base) => base === "." || path === base || path.startsWith(`${base}/`)) && !excluded(path, normOuter);
|
|
7493
7493
|
const innerExcludes = (path) => innerExclusions.some((excludedPath) => excludedPath === path || path.startsWith(`${excludedPath}/`));
|
|
7494
7494
|
return innerPaths.every(covers) && !normOuter.exclusions.some((excludedPath) => innerPaths.some((base) => (base === "." || excludedPath === base || excludedPath.startsWith(`${base}/`)) && !innerExcludes(excludedPath)));
|
|
7495
|
-
}, refSchema,
|
|
7495
|
+
}, refSchema, rewriteUnknown = (value, map) => {
|
|
7496
|
+
if (Array.isArray(value)) {
|
|
7497
|
+
const out = [];
|
|
7498
|
+
for (const item of value) {
|
|
7499
|
+
const rewritten = rewriteUnknown(item, map);
|
|
7500
|
+
if (!rewritten.drop)
|
|
7501
|
+
out.push(rewritten.value);
|
|
7502
|
+
}
|
|
7503
|
+
return { drop: false, value: out };
|
|
7504
|
+
}
|
|
7505
|
+
if (typeof value === "object" && value !== null) {
|
|
7506
|
+
if (refSchema.safeParse(value).success) {
|
|
7507
|
+
const mapped = map(value);
|
|
7508
|
+
return mapped === null ? { drop: true } : { drop: false, value: mapped };
|
|
7509
|
+
}
|
|
7510
|
+
const out = {};
|
|
7511
|
+
for (const [key, item] of Object.entries(value)) {
|
|
7512
|
+
const rewritten = rewriteUnknown(item, map);
|
|
7513
|
+
out[key] = rewritten.drop ? null : rewritten.value;
|
|
7514
|
+
}
|
|
7515
|
+
return { drop: false, value: out };
|
|
7516
|
+
}
|
|
7517
|
+
return { drop: false, value };
|
|
7518
|
+
}, rewriteNode = (value, schema, map) => {
|
|
7519
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value) && refSchema.safeParse(value).success) {
|
|
7520
|
+
const mapped = map(value);
|
|
7521
|
+
return mapped === null ? { drop: true } : { drop: false, value: mapped };
|
|
7522
|
+
}
|
|
7523
|
+
let node = schema;
|
|
7524
|
+
while (node instanceof ZodNullable || node instanceof ZodOptional || node instanceof ZodDefault) {
|
|
7525
|
+
node = node.unwrap();
|
|
7526
|
+
}
|
|
7527
|
+
if (node === refSchema) {
|
|
7528
|
+
if (!refSchema.safeParse(value).success)
|
|
7529
|
+
return { drop: false, value };
|
|
7530
|
+
const mapped = map(value);
|
|
7531
|
+
return mapped === null ? { drop: true } : { drop: false, value: mapped };
|
|
7532
|
+
}
|
|
7533
|
+
if (node instanceof ZodArray) {
|
|
7534
|
+
if (!Array.isArray(value))
|
|
7535
|
+
return { drop: false, value };
|
|
7536
|
+
const out = [];
|
|
7537
|
+
for (const item of value) {
|
|
7538
|
+
const rewritten = rewriteNode(item, node.element, map);
|
|
7539
|
+
if (!rewritten.drop)
|
|
7540
|
+
out.push(rewritten.value);
|
|
7541
|
+
}
|
|
7542
|
+
return { drop: false, value: out };
|
|
7543
|
+
}
|
|
7544
|
+
if (node instanceof ZodObject) {
|
|
7545
|
+
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
7546
|
+
return { drop: false, value };
|
|
7547
|
+
const shape = node.shape;
|
|
7548
|
+
const record = value;
|
|
7549
|
+
const out = {};
|
|
7550
|
+
for (const [key, item] of Object.entries(record)) {
|
|
7551
|
+
if (!(key in shape)) {
|
|
7552
|
+
const scrubbed = rewriteUnknown(item, map);
|
|
7553
|
+
out[key] = scrubbed.drop ? null : scrubbed.value;
|
|
7554
|
+
continue;
|
|
7555
|
+
}
|
|
7556
|
+
const child = shape[key];
|
|
7557
|
+
const rewritten = rewriteNode(item, child, map);
|
|
7558
|
+
if (rewritten.drop) {
|
|
7559
|
+
if (child instanceof ZodOptional || child instanceof ZodDefault)
|
|
7560
|
+
continue;
|
|
7561
|
+
if (child instanceof ZodNullable) {
|
|
7562
|
+
out[key] = null;
|
|
7563
|
+
continue;
|
|
7564
|
+
}
|
|
7565
|
+
return { drop: true };
|
|
7566
|
+
}
|
|
7567
|
+
out[key] = rewritten.value;
|
|
7568
|
+
}
|
|
7569
|
+
return { drop: false, value: out };
|
|
7570
|
+
}
|
|
7571
|
+
return { drop: false, value };
|
|
7572
|
+
}, rewriteRecordRefs = (value, schema, map) => {
|
|
7573
|
+
const rewritten = rewriteNode(value, schema, map);
|
|
7574
|
+
return rewritten.drop ? null : rewritten.value;
|
|
7575
|
+
}, provenanceSchema, entrySchema = (data) => object({ id, recordedAt: utc, provenance: provenanceSchema, data }).strict(), factSchema, signalSchema, signalSet, consequenceSchema, verificationSchema, assessmentSchema, constraintSchema, intentSchema, progressSchema, dimensionSchema, policySchema, requirementSchema, policyChangeSchema, candidateSchema, evidenceSchema, evidenceEvaluationSchema, decisionSchema, findingSchema, assignmentSchema, workerReportSchema, workerSchema, ownerSchema, closureSchema, actionProgressSchema, actionProgressListSchema, runtimeSchema, taskRecordSchema, workspaceRecordSchema, capabilitySchema, requirementEvaluationSchema, taskViewSchema, taskSummarySchema, exportBundleSchema, callerSchema, base, revisions, taskId, operation = (shape) => object({ ...base, ...shape }).strict(), taskOperations, policyOperations, evidenceOperations, findingOperations, decisionOperations, workerOperations, writerOperations, stateOperations, operationSchemas, compiledOperationSchemas, success = (revisionValue, workspaceRevisionValue, data) => ({
|
|
7496
7576
|
ok: true,
|
|
7497
7577
|
schemaVersion: 1,
|
|
7498
7578
|
revision: revisionValue,
|
|
@@ -7575,7 +7655,13 @@ var init_task_contract = __esm(() => {
|
|
|
7575
7655
|
id
|
|
7576
7656
|
}).strict(),
|
|
7577
7657
|
object({ kind: literal("host"), host: hostSchema, handle: nonEmpty }).strict(),
|
|
7578
|
-
object({ kind: literal("external"), url: text.url() }).strict()
|
|
7658
|
+
object({ kind: literal("external"), url: text.url() }).strict(),
|
|
7659
|
+
object({
|
|
7660
|
+
kind: literal("standing"),
|
|
7661
|
+
workspace: nonEmpty,
|
|
7662
|
+
class: nonEmpty,
|
|
7663
|
+
configDigest: nullableDigest
|
|
7664
|
+
}).strict()
|
|
7579
7665
|
]);
|
|
7580
7666
|
provenanceSchema = object({
|
|
7581
7667
|
kind: _enum(["host_observed", "agent_reported", "imported"]),
|
|
@@ -7796,7 +7882,8 @@ var init_task_contract = __esm(() => {
|
|
|
7796
7882
|
approvedContent: text,
|
|
7797
7883
|
displayed: text.optional(),
|
|
7798
7884
|
contentRefs: array(refSchema),
|
|
7799
|
-
statedChoice: object({ ref: text, text }).strict().optional()
|
|
7885
|
+
statedChoice: object({ ref: text, text }).strict().optional(),
|
|
7886
|
+
standing: object({ workspace: nonEmpty, class: nonEmpty }).strict().optional()
|
|
7800
7887
|
}).strict(),
|
|
7801
7888
|
digest,
|
|
7802
7889
|
response: _enum(["approved", "rejected", "stated"]),
|
|
@@ -9075,11 +9162,11 @@ class TaskStore {
|
|
|
9075
9162
|
return workspace;
|
|
9076
9163
|
if (!workspace.data)
|
|
9077
9164
|
return failure("not_found", "workspace not found");
|
|
9078
|
-
const
|
|
9079
|
-
if (!
|
|
9165
|
+
const expected = input.expectedRevision;
|
|
9166
|
+
if (!expected)
|
|
9080
9167
|
return failure("invalid_input", "task revision is required");
|
|
9081
|
-
if (task.data.revision !==
|
|
9082
|
-
return this.conflict(
|
|
9168
|
+
if (task.data.revision !== expected)
|
|
9169
|
+
return this.conflict(expected, task.data.revision);
|
|
9083
9170
|
if (workspace.data.revision !== input.expectedWorkspaceRevision)
|
|
9084
9171
|
return this.conflict(input.expectedWorkspaceRevision, workspace.data.revision);
|
|
9085
9172
|
const previousWorkspaceBytes = this.snapshotBytes(this.workspacePath);
|
|
@@ -9705,7 +9792,7 @@ var same = (left, right) => {
|
|
|
9705
9792
|
} catch {
|
|
9706
9793
|
return false;
|
|
9707
9794
|
}
|
|
9708
|
-
}, UNCERTAIN_WORKER_STATES, isUncertainWorker = (state) => UNCERTAIN_WORKER_STATES.includes(state), callerSession = (caller) => caller.session ?? { kind: "host", host: caller.host, handle: caller.actor }, validPath = (value) => typeof value === "string" && value.length > 0 && ![...value].some((char) => {
|
|
9795
|
+
}, UNCERTAIN_WORKER_STATES, isUncertainWorker = (state) => UNCERTAIN_WORKER_STATES.includes(state), workerBlocksTransition = (transition, state) => transition === "pause" ? false : isUncertainWorker(state), callerSession = (caller) => caller.session ?? { kind: "host", host: caller.host, handle: caller.actor }, validPath = (value) => typeof value === "string" && value.length > 0 && ![...value].some((char) => {
|
|
9709
9796
|
const code = char.charCodeAt(0);
|
|
9710
9797
|
return code < 32 || code === 127;
|
|
9711
9798
|
}) && !value.split(/[\\/]/).includes(".."), callerValue = (value) => ({ host: value.host, actor: value.actor }), workerIdOf = (value) => value.workerId ?? null;
|
|
@@ -9714,10 +9801,360 @@ var init_workers = __esm(() => {
|
|
|
9714
9801
|
UNCERTAIN_WORKER_STATES = ["dispatching", "running", "cancelling", "unknown"];
|
|
9715
9802
|
});
|
|
9716
9803
|
|
|
9717
|
-
// packages/workit-core/src/core/
|
|
9804
|
+
// packages/workit-core/src/core/boundary.ts
|
|
9805
|
+
var EVENT, errorDetail = (err) => {
|
|
9806
|
+
if (err instanceof Error) {
|
|
9807
|
+
return { error_name: err.name, error: err.message };
|
|
9808
|
+
}
|
|
9809
|
+
return { error_name: "unknown", error: String(err) };
|
|
9810
|
+
};
|
|
9811
|
+
var init_boundary = __esm(() => {
|
|
9812
|
+
EVENT = {
|
|
9813
|
+
initialization: "initialization",
|
|
9814
|
+
provenance: "provenance",
|
|
9815
|
+
assets: "assets",
|
|
9816
|
+
configurationSource: "configuration_source",
|
|
9817
|
+
hooks: "hooks",
|
|
9818
|
+
mcpConnection: "mcp_connection",
|
|
9819
|
+
migration: "migration",
|
|
9820
|
+
installSteps: "install_steps",
|
|
9821
|
+
uncaughtFailure: "uncaught_failure",
|
|
9822
|
+
toolsFailed: "tools_failed",
|
|
9823
|
+
doctor: "doctor"
|
|
9824
|
+
};
|
|
9825
|
+
});
|
|
9826
|
+
|
|
9827
|
+
// packages/workit-core/src/core/config.ts
|
|
9828
|
+
import {
|
|
9829
|
+
copyFileSync,
|
|
9830
|
+
cpSync,
|
|
9831
|
+
existsSync as existsSync3,
|
|
9832
|
+
mkdirSync as mkdirSync2,
|
|
9833
|
+
readFileSync as readFileSync2,
|
|
9834
|
+
readdirSync as readdirSync2,
|
|
9835
|
+
writeFileSync as writeFileSync2
|
|
9836
|
+
} from "node:fs";
|
|
9837
|
+
import os from "node:os";
|
|
9838
|
+
import path5 from "node:path";
|
|
9839
|
+
var diagnosticLogger, PRESETS, resolveConfigDir = () => process.env.WORKFLOW_TOOLKIT_CONFIG ?? process.env.WORKFLOW_TOOLKIT_CONFIG_DIR ?? path5.join(process.env.XDG_CONFIG_HOME || path5.join(os.homedir(), ".config"), "workit"), migratedDir = null, migrationFailed = false, ensureConfigDir = (dir = resolveConfigDir()) => {
|
|
9840
|
+
if (migratedDir === dir)
|
|
9841
|
+
return dir;
|
|
9842
|
+
if (process.env.WORKFLOW_TOOLKIT_CONFIG || process.env.WORKFLOW_TOOLKIT_CONFIG_DIR) {
|
|
9843
|
+
migratedDir = dir;
|
|
9844
|
+
return dir;
|
|
9845
|
+
}
|
|
9846
|
+
const legacy = path5.join(process.env.XDG_CONFIG_HOME || path5.join(os.homedir(), ".config"), "workflow-toolkit");
|
|
9847
|
+
if (!existsSync3(legacy)) {
|
|
9848
|
+
migratedDir = dir;
|
|
9849
|
+
return dir;
|
|
9850
|
+
}
|
|
9851
|
+
if (!migrationFailed && existsSync3(dir)) {
|
|
9852
|
+
migratedDir = dir;
|
|
9853
|
+
return dir;
|
|
9854
|
+
}
|
|
9855
|
+
migrationFailed = false;
|
|
9856
|
+
mkdirSync2(dir, { recursive: true });
|
|
9857
|
+
diagnosticLogger?.info(EVENT.migration, { from: legacy, to: dir });
|
|
9858
|
+
for (const entry of readdirSync2(legacy, { withFileTypes: true })) {
|
|
9859
|
+
const src = path5.join(legacy, entry.name);
|
|
9860
|
+
const dest = path5.join(dir, entry.name);
|
|
9861
|
+
if (existsSync3(dest))
|
|
9862
|
+
continue;
|
|
9863
|
+
try {
|
|
9864
|
+
if (entry.isDirectory())
|
|
9865
|
+
cpSync(src, dest, { recursive: true });
|
|
9866
|
+
else if (entry.isFile())
|
|
9867
|
+
copyFileSync(src, dest);
|
|
9868
|
+
} catch (err) {
|
|
9869
|
+
migrationFailed = true;
|
|
9870
|
+
diagnosticLogger?.warn(EVENT.migration, { from: src, ok: false, ...errorDetail(err) });
|
|
9871
|
+
console.warn(`[workit] config migration: a file could not be copied to ${dir}; it will be retried on the next run`);
|
|
9872
|
+
}
|
|
9873
|
+
}
|
|
9874
|
+
if (!migrationFailed)
|
|
9875
|
+
migratedDir = dir;
|
|
9876
|
+
return dir;
|
|
9877
|
+
}, configDir = () => ensureConfigDir(), DEFAULTS, isConfigObject = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
9878
|
+
var init_config = __esm(() => {
|
|
9879
|
+
init_boundary();
|
|
9880
|
+
PRESETS = {
|
|
9881
|
+
gitflow: {
|
|
9882
|
+
allowed: ["feature/*", "bugfix/*", "hotfix/*", "release/*"],
|
|
9883
|
+
protected: ["main", "develop", "master", "prod", "production"]
|
|
9884
|
+
},
|
|
9885
|
+
"github-flow": { allowed: ["*"], protected: ["main"] },
|
|
9886
|
+
"trunk-based": { allowed: ["*"], protected: ["main"] },
|
|
9887
|
+
custom: { allowed: [], protected: [] }
|
|
9888
|
+
};
|
|
9889
|
+
DEFAULTS = {
|
|
9890
|
+
locale: "en",
|
|
9891
|
+
localeOptions: ["en", "es-CL", "es-MX", "es-AR", "pt-BR"],
|
|
9892
|
+
timezone: "America/Santiago",
|
|
9893
|
+
branchPolicy: {
|
|
9894
|
+
preset: "gitflow",
|
|
9895
|
+
allowed: [...PRESETS.gitflow.allowed],
|
|
9896
|
+
protected: [...PRESETS.gitflow.protected]
|
|
9897
|
+
},
|
|
9898
|
+
commitPolicy: { preset: "conventional" }
|
|
9899
|
+
};
|
|
9900
|
+
});
|
|
9901
|
+
|
|
9902
|
+
// packages/workit-core/src/core/workspaces.ts
|
|
9903
|
+
import { readFileSync as readFileSync3, realpathSync as realpathSync2 } from "node:fs";
|
|
9904
|
+
import path6 from "node:path";
|
|
9905
|
+
var readWorkspacesResult = (dir = configDir()) => {
|
|
9906
|
+
const file = path6.join(dir, "workspaces.json");
|
|
9907
|
+
let raw;
|
|
9908
|
+
try {
|
|
9909
|
+
raw = readFileSync3(file, "utf8");
|
|
9910
|
+
} catch {
|
|
9911
|
+
return { status: "missing", path: file, entries: [] };
|
|
9912
|
+
}
|
|
9913
|
+
let parsed;
|
|
9914
|
+
try {
|
|
9915
|
+
parsed = JSON.parse(raw);
|
|
9916
|
+
} catch {
|
|
9917
|
+
return { status: "malformed", path: file, entries: [], error: `${file} is not valid JSON` };
|
|
9918
|
+
}
|
|
9919
|
+
if (!isConfigObject(parsed)) {
|
|
9920
|
+
return { status: "malformed", path: file, entries: [], error: `${file} is not a JSON object` };
|
|
9921
|
+
}
|
|
9922
|
+
const list = parsed.workspaces;
|
|
9923
|
+
return {
|
|
9924
|
+
status: "valid",
|
|
9925
|
+
path: file,
|
|
9926
|
+
entries: Array.isArray(list) ? list : []
|
|
9927
|
+
};
|
|
9928
|
+
}, loadWorkspacesFrom = (dir) => readWorkspacesResult(dir).entries, globToRegExp = (glob) => {
|
|
9929
|
+
let out = "";
|
|
9930
|
+
for (let i = 0;i < glob.length; i++) {
|
|
9931
|
+
const c = glob[i];
|
|
9932
|
+
if (c === "*") {
|
|
9933
|
+
if (glob[i + 1] === "*") {
|
|
9934
|
+
if (glob[i + 2] === "/") {
|
|
9935
|
+
if (out === "")
|
|
9936
|
+
out += "/?";
|
|
9937
|
+
out += "(?:[^/]+/)*";
|
|
9938
|
+
i += 2;
|
|
9939
|
+
} else {
|
|
9940
|
+
if (i + 2 >= glob.length) {
|
|
9941
|
+
if (out.endsWith("/")) {
|
|
9942
|
+
out = out.slice(0, -1);
|
|
9943
|
+
out += "(?:/.*)?";
|
|
9944
|
+
} else {
|
|
9945
|
+
out += ".*";
|
|
9946
|
+
}
|
|
9947
|
+
} else {
|
|
9948
|
+
out += ".*";
|
|
9949
|
+
}
|
|
9950
|
+
i++;
|
|
9951
|
+
}
|
|
9952
|
+
} else {
|
|
9953
|
+
out += "[^/]*";
|
|
9954
|
+
}
|
|
9955
|
+
} else {
|
|
9956
|
+
out += c.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
|
|
9957
|
+
}
|
|
9958
|
+
}
|
|
9959
|
+
return new RegExp(`^${out}$`);
|
|
9960
|
+
}, matchWorkspace = (glob, target) => globToRegExp(glob.replaceAll("\\", "/")).test(target.replaceAll("\\", "/")), realpathOf = (p) => {
|
|
9961
|
+
const real = realpathSync2.native ?? realpathSync2;
|
|
9962
|
+
try {
|
|
9963
|
+
return real(p);
|
|
9964
|
+
} catch {
|
|
9965
|
+
return p;
|
|
9966
|
+
}
|
|
9967
|
+
}, canonicalGlob = (glob) => {
|
|
9968
|
+
const m = /[*?[\]{]/.exec(glob);
|
|
9969
|
+
const prefix = m ? glob.slice(0, m.index) : glob;
|
|
9970
|
+
const rest = m ? glob.slice(m.index) : "";
|
|
9971
|
+
if (!prefix)
|
|
9972
|
+
return glob;
|
|
9973
|
+
const real = realpathOf(prefix.replace(/\/+$/, "") || "/");
|
|
9974
|
+
return real + (prefix.endsWith("/") ? "/" : "") + rest;
|
|
9975
|
+
}, resolveWorkspaceFrom = (cwd, dir) => {
|
|
9976
|
+
const targets = [cwd, realpathOf(cwd)].map((p) => p.replaceAll("\\", "/"));
|
|
9977
|
+
for (const entry of loadWorkspacesFrom(dir)) {
|
|
9978
|
+
if (!entry || typeof entry !== "object")
|
|
9979
|
+
continue;
|
|
9980
|
+
const ws = entry;
|
|
9981
|
+
if (typeof ws.glob !== "string" || !ws.glob)
|
|
9982
|
+
continue;
|
|
9983
|
+
const glob = ws.glob.replaceAll("\\", "/");
|
|
9984
|
+
const canonical = canonicalGlob(glob);
|
|
9985
|
+
for (const target of targets) {
|
|
9986
|
+
if (matchWorkspace(glob, target))
|
|
9987
|
+
return ws;
|
|
9988
|
+
if (canonical !== glob && matchWorkspace(canonical, target))
|
|
9989
|
+
return ws;
|
|
9990
|
+
}
|
|
9991
|
+
}
|
|
9992
|
+
return null;
|
|
9993
|
+
};
|
|
9994
|
+
var init_workspaces = __esm(() => {
|
|
9995
|
+
init_config();
|
|
9996
|
+
});
|
|
9997
|
+
|
|
9998
|
+
// packages/workit-core/src/core/git.ts
|
|
9999
|
+
var init_git = () => {};
|
|
10000
|
+
|
|
10001
|
+
// packages/workit-core/src/core/vcs-config.ts
|
|
10002
|
+
var init_vcs_config = __esm(() => {
|
|
10003
|
+
init_config();
|
|
10004
|
+
init_workspaces();
|
|
10005
|
+
init_branch();
|
|
10006
|
+
});
|
|
10007
|
+
|
|
10008
|
+
// packages/workit-core/src/core/branch.ts
|
|
10009
|
+
import { execFileSync } from "node:child_process";
|
|
10010
|
+
var verifyPushIdentity = (root, provider, account) => {
|
|
10011
|
+
if (!account || typeof account !== "string")
|
|
10012
|
+
return { ok: false, error: "push identity requires a configured area account" };
|
|
10013
|
+
const probe = provider === "gitlab" ? ["api", "user", "--jq", ".username"] : ["api", "user", "--jq", ".login"];
|
|
10014
|
+
const bin = provider === "gitlab" ? "glab" : "gh";
|
|
10015
|
+
let login;
|
|
10016
|
+
try {
|
|
10017
|
+
login = execFileSync(bin, [...probe], {
|
|
10018
|
+
cwd: root,
|
|
10019
|
+
encoding: "utf8",
|
|
10020
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
10021
|
+
}).trim();
|
|
10022
|
+
} catch {
|
|
10023
|
+
return { ok: false, error: `${bin} identity could not be resolved` };
|
|
10024
|
+
}
|
|
10025
|
+
if (!login || login.toLowerCase() !== account.toLowerCase())
|
|
10026
|
+
return {
|
|
10027
|
+
ok: false,
|
|
10028
|
+
error: `push identity ${login || "(unknown)"} does not match area account ${account}`
|
|
10029
|
+
};
|
|
10030
|
+
return { ok: true };
|
|
10031
|
+
};
|
|
10032
|
+
var init_branch = __esm(() => {
|
|
10033
|
+
init_git();
|
|
10034
|
+
init_config();
|
|
10035
|
+
init_workspaces();
|
|
10036
|
+
init_vcs_config();
|
|
10037
|
+
});
|
|
10038
|
+
|
|
10039
|
+
// packages/workit-core/src/core/auto-approval.ts
|
|
9718
10040
|
import { createHash as createHash3 } from "node:crypto";
|
|
10041
|
+
import { readFileSync as readFileSync4 } from "node:fs";
|
|
10042
|
+
var AUTO_CLASSES, OPERATION_CLASS, operationAutoClass = (operation) => typeof operation === "string" ? OPERATION_CLASS[operation] ?? null : null, digestOf = (configDirPath) => {
|
|
10043
|
+
try {
|
|
10044
|
+
const raw = readFileSync4(`${configDirPath}/workspaces.json`, "utf8");
|
|
10045
|
+
return createHash3("sha256").update(raw).digest("hex");
|
|
10046
|
+
} catch {
|
|
10047
|
+
return null;
|
|
10048
|
+
}
|
|
10049
|
+
}, normalizeClasses = (value) => {
|
|
10050
|
+
if (value === true)
|
|
10051
|
+
return [...AUTO_CLASSES];
|
|
10052
|
+
if (!Array.isArray(value))
|
|
10053
|
+
return [];
|
|
10054
|
+
const seen = new Set;
|
|
10055
|
+
for (const item of value) {
|
|
10056
|
+
if (typeof item === "string" && AUTO_CLASSES.includes(item))
|
|
10057
|
+
seen.add(item);
|
|
10058
|
+
}
|
|
10059
|
+
return [...seen];
|
|
10060
|
+
}, resolveAutoApproval = (root, dir = configDir()) => {
|
|
10061
|
+
let match;
|
|
10062
|
+
try {
|
|
10063
|
+
match = resolveWorkspaceFrom(root, dir);
|
|
10064
|
+
} catch {
|
|
10065
|
+
return { status: "off" };
|
|
10066
|
+
}
|
|
10067
|
+
if (!match)
|
|
10068
|
+
return { status: "off" };
|
|
10069
|
+
const classes = normalizeClasses(match.autoApprove);
|
|
10070
|
+
if (classes.length === 0)
|
|
10071
|
+
return { status: "off" };
|
|
10072
|
+
return {
|
|
10073
|
+
status: "on",
|
|
10074
|
+
workspace: match.name,
|
|
10075
|
+
classes,
|
|
10076
|
+
account: typeof match.vcs?.account === "string" && match.vcs.account ? match.vcs.account : null,
|
|
10077
|
+
configDigest: digestOf(dir)
|
|
10078
|
+
};
|
|
10079
|
+
}, isStandingReceipt = (value) => typeof value === "object" && value !== null && value.kind === "standing", standingApprovalLive = (root, receipt, cls, dir) => {
|
|
10080
|
+
if (!isStandingReceipt(receipt))
|
|
10081
|
+
return false;
|
|
10082
|
+
if (typeof cls !== "string" || !AUTO_CLASSES.includes(cls))
|
|
10083
|
+
return false;
|
|
10084
|
+
if (receipt.class !== cls)
|
|
10085
|
+
return false;
|
|
10086
|
+
const resolved = resolveAutoApproval(root, dir);
|
|
10087
|
+
if (resolved.status !== "on")
|
|
10088
|
+
return false;
|
|
10089
|
+
if (resolved.workspace !== receipt.workspace)
|
|
10090
|
+
return false;
|
|
10091
|
+
return resolved.classes.includes(cls);
|
|
10092
|
+
}, standingReceiptFor = (root, workspace, cls, dir) => {
|
|
10093
|
+
const resolved = resolveAutoApproval(root, dir);
|
|
10094
|
+
return {
|
|
10095
|
+
kind: "standing",
|
|
10096
|
+
workspace,
|
|
10097
|
+
class: cls,
|
|
10098
|
+
configDigest: resolved.status === "on" && resolved.workspace === workspace ? resolved.configDigest : null
|
|
10099
|
+
};
|
|
10100
|
+
}, verifyStandingApproval = (root, task, caller, binding) => {
|
|
10101
|
+
if (task.status !== "active")
|
|
10102
|
+
return failure("invalid_transition", "only active tasks can authorize actions");
|
|
10103
|
+
const session = task.intent?.provenance?.session;
|
|
10104
|
+
if (!session || session.kind !== "host" || session.host !== caller.host || session.handle !== caller.actor)
|
|
10105
|
+
return failure("permission_denied", "standing approval requires the lead session");
|
|
10106
|
+
const standing = binding.standing;
|
|
10107
|
+
if (!standing || typeof standing.workspace !== "string" || typeof standing.class !== "string")
|
|
10108
|
+
return failure("invalid_input", "standing approval needs a workspace and class");
|
|
10109
|
+
let operation;
|
|
10110
|
+
try {
|
|
10111
|
+
operation = typeof binding.approvedContent === "string" ? JSON.parse(binding.approvedContent).operation : null;
|
|
10112
|
+
} catch {
|
|
10113
|
+
operation = null;
|
|
10114
|
+
}
|
|
10115
|
+
const cls = operationAutoClass(operation);
|
|
10116
|
+
if (!cls || cls !== standing.class)
|
|
10117
|
+
return failure("permission_denied", "standing approval does not cover this operation");
|
|
10118
|
+
const resolved = resolveAutoApproval(root);
|
|
10119
|
+
if (resolved.status !== "on" || resolved.workspace !== standing.workspace || !resolved.classes.includes(cls))
|
|
10120
|
+
return failure("permission_denied", "standing auto-approval is not live for this operation");
|
|
10121
|
+
if (cls === "push") {
|
|
10122
|
+
const workspace = resolveWorkspaceFrom(root, configDir());
|
|
10123
|
+
const provider = workspace?.vcs?.provider;
|
|
10124
|
+
const account = resolved.account;
|
|
10125
|
+
if (!provider || !account)
|
|
10126
|
+
return failure("permission_denied", "auto-push requires a workspace provider and area account");
|
|
10127
|
+
const identity = verifyPushIdentity(root, provider, account);
|
|
10128
|
+
if (!identity.ok)
|
|
10129
|
+
return failure("permission_denied", identity.error);
|
|
10130
|
+
}
|
|
10131
|
+
return success(null, null, {
|
|
10132
|
+
kind: "host_observed",
|
|
10133
|
+
host: caller.host,
|
|
10134
|
+
session: { kind: "host", host: caller.host, handle: caller.actor },
|
|
10135
|
+
workerId: null,
|
|
10136
|
+
receipts: [standingReceiptFor(root, standing.workspace, cls)]
|
|
10137
|
+
});
|
|
10138
|
+
};
|
|
10139
|
+
var init_auto_approval = __esm(() => {
|
|
10140
|
+
init_workspaces();
|
|
10141
|
+
init_config();
|
|
10142
|
+
init_branch();
|
|
10143
|
+
init_task_contract();
|
|
10144
|
+
AUTO_CLASSES = ["branch", "commit", "push", "pr", "merge"];
|
|
10145
|
+
OPERATION_CLASS = {
|
|
10146
|
+
"git.branch_setup": "branch",
|
|
10147
|
+
"git.commit": "commit",
|
|
10148
|
+
"git.push": "push",
|
|
10149
|
+
"hosting.pull_request": "pr",
|
|
10150
|
+
"hosting.merge": "merge"
|
|
10151
|
+
};
|
|
10152
|
+
});
|
|
10153
|
+
|
|
10154
|
+
// packages/workit-core/src/core/authority.ts
|
|
10155
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
9719
10156
|
import * as fs5 from "node:fs";
|
|
9720
|
-
import
|
|
10157
|
+
import path7 from "node:path";
|
|
9721
10158
|
function applicableDecision(task, purpose, binding, checkoutRoot) {
|
|
9722
10159
|
if (binding.taskId !== task.id || binding.workspaceId !== task.workspaceId)
|
|
9723
10160
|
return [];
|
|
@@ -10000,6 +10437,33 @@ var verifiedAuthorities, trustedAuthority = (kind, provenance, expected, binding
|
|
|
10000
10437
|
const token = {};
|
|
10001
10438
|
verifiedAuthorities.set(token, { kind, provenance, ...expected, ...binding, caller });
|
|
10002
10439
|
return token;
|
|
10440
|
+
}, chainStepKey = (step) => step.kind === "commit" ? step.message : step.kind === "branch" ? `branch:${step.target}` : "pr", isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value), normalizeChainSteps = (steps) => {
|
|
10441
|
+
if (!Array.isArray(steps) || steps.length === 0 || steps.length > 32)
|
|
10442
|
+
return null;
|
|
10443
|
+
const normalized = [];
|
|
10444
|
+
for (const step of steps) {
|
|
10445
|
+
if (typeof step === "string") {
|
|
10446
|
+
if (!step.trim())
|
|
10447
|
+
return null;
|
|
10448
|
+
normalized.push({ kind: "commit", message: step });
|
|
10449
|
+
continue;
|
|
10450
|
+
}
|
|
10451
|
+
if (!isRecord(step))
|
|
10452
|
+
return null;
|
|
10453
|
+
if (typeof step.branch === "string" && step.branch.trim() && Object.keys(step).length === 1) {
|
|
10454
|
+
normalized.push({ kind: "branch", target: step.branch });
|
|
10455
|
+
continue;
|
|
10456
|
+
}
|
|
10457
|
+
if (step.pr === true && Object.keys(step).length === 1) {
|
|
10458
|
+
normalized.push({ kind: "pr" });
|
|
10459
|
+
continue;
|
|
10460
|
+
}
|
|
10461
|
+
return null;
|
|
10462
|
+
}
|
|
10463
|
+
const keys = normalized.map(chainStepKey);
|
|
10464
|
+
if (new Set(keys).size !== keys.length)
|
|
10465
|
+
return null;
|
|
10466
|
+
return normalized;
|
|
10003
10467
|
}, scopeEqual = (left, right) => canonicalJson(left) === canonicalJson(right), parseSteps = (content) => {
|
|
10004
10468
|
let value;
|
|
10005
10469
|
try {
|
|
@@ -10010,7 +10474,10 @@ var verifiedAuthorities, trustedAuthority = (kind, provenance, expected, binding
|
|
|
10010
10474
|
if (typeof value !== "object" || value === null || !("steps" in value))
|
|
10011
10475
|
return [];
|
|
10012
10476
|
const steps = value.steps;
|
|
10013
|
-
|
|
10477
|
+
if (!Array.isArray(steps))
|
|
10478
|
+
return null;
|
|
10479
|
+
const normalized = normalizeChainSteps(steps);
|
|
10480
|
+
return normalized;
|
|
10014
10481
|
}, decisionMatches = (entry, purpose, binding) => {
|
|
10015
10482
|
const decision = entry.data;
|
|
10016
10483
|
return binding.taskId === decision.binding.taskId && binding.workspaceId === decision.binding.workspaceId && decision.purpose === purpose && decision.response === "approved" && decision.revoked === null && (purpose !== "action" || decision.consumption === null) && decision.digest === decisionDigest(decision) && scopeEqual(decision.binding.scope, binding.scope) && canonicalJson(decision.binding) === canonicalJson(binding);
|
|
@@ -10089,7 +10556,7 @@ var verifiedAuthorities, trustedAuthority = (kind, provenance, expected, binding
|
|
|
10089
10556
|
const record = verifiedAuthorities.get(authority);
|
|
10090
10557
|
verifiedAuthorities.delete(authority);
|
|
10091
10558
|
return record?.provenance ?? null;
|
|
10092
|
-
}, authorityMatches = (authority, expected) => authority.kind === "action" && authority.taskId === expected.taskId && authority.workspaceId === expected.workspaceId && authority.decisionId === expected.decisionId && sameRef(authority.actionRef, expected.actionRef) && authority.taskRevision === expected.taskRevision && authority.workspaceRevision === expected.workspaceRevision && authority.outcome === expected.outcome, authorityDecisionMatches = (authority, decision) => authority.purpose === decision.purpose && authority.response === decision.response && authority.digest === decision.digest && canonicalJson(authority.binding) === canonicalJson(decision.binding) && canonicalJson(authority.requirementIds) === canonicalJson(decision.requirementIds), provenanceMatches = (left, right) => left.kind === "host_observed" && right.kind === "host_observed" && left.host === right.host && canonicalJson(left.session) === canonicalJson(right.session), digestBytes2 = (bytes) =>
|
|
10559
|
+
}, authorityMatches = (authority, expected) => authority.kind === "action" && authority.taskId === expected.taskId && authority.workspaceId === expected.workspaceId && authority.decisionId === expected.decisionId && sameRef(authority.actionRef, expected.actionRef) && authority.taskRevision === expected.taskRevision && authority.workspaceRevision === expected.workspaceRevision && authority.outcome === expected.outcome, authorityDecisionMatches = (authority, decision) => authority.purpose === decision.purpose && authority.response === decision.response && authority.digest === decision.digest && canonicalJson(authority.binding) === canonicalJson(decision.binding) && canonicalJson(authority.requirementIds) === canonicalJson(decision.requirementIds), provenanceMatches = (left, right) => left.kind === "host_observed" && right.kind === "host_observed" && left.host === right.host && canonicalJson(left.session) === canonicalJson(right.session), digestBytes2 = (bytes) => createHash4("sha256").update(bytes).digest("hex"), verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
10093
10560
|
let root;
|
|
10094
10561
|
try {
|
|
10095
10562
|
root = fs5.realpathSync(checkoutRoot);
|
|
@@ -10101,23 +10568,23 @@ var verifiedAuthorities, trustedAuthority = (kind, provenance, expected, binding
|
|
|
10101
10568
|
continue;
|
|
10102
10569
|
if (!reference.digest)
|
|
10103
10570
|
return failure("invalid_input", "document references require a byte digest");
|
|
10104
|
-
const target =
|
|
10105
|
-
if (target !== root && !target.startsWith(`${root}${
|
|
10571
|
+
const target = path7.resolve(root, reference.path);
|
|
10572
|
+
if (target !== root && !target.startsWith(`${root}${path7.sep}`))
|
|
10106
10573
|
return failure("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
10107
10574
|
fields: [
|
|
10108
10575
|
{ path: "contentRefs.path", reason: `${reference.path} is outside the checkout` }
|
|
10109
10576
|
]
|
|
10110
10577
|
});
|
|
10111
10578
|
try {
|
|
10112
|
-
const relative =
|
|
10579
|
+
const relative = path7.relative(root, target);
|
|
10113
10580
|
let current = root;
|
|
10114
|
-
for (const segment of relative.split(
|
|
10581
|
+
for (const segment of relative.split(path7.sep)) {
|
|
10115
10582
|
if (!segment)
|
|
10116
10583
|
continue;
|
|
10117
|
-
current =
|
|
10584
|
+
current = path7.join(current, segment);
|
|
10118
10585
|
const stat = fs5.lstatSync(current);
|
|
10119
10586
|
const real = fs5.realpathSync(current);
|
|
10120
|
-
if (real !== root && !real.startsWith(`${root}${
|
|
10587
|
+
if (real !== root && !real.startsWith(`${root}${path7.sep}`))
|
|
10121
10588
|
return failure("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
10122
10589
|
fields: [
|
|
10123
10590
|
{
|
|
@@ -10167,6 +10634,20 @@ var verifiedAuthorities, trustedAuthority = (kind, provenance, expected, binding
|
|
|
10167
10634
|
return failure("permission_denied", "decision was rejected");
|
|
10168
10635
|
if (entry.provenance.kind !== "host_observed" || entry.provenance.receipts.length === 0 || !provenanceMatches(entry.provenance, authority.provenance))
|
|
10169
10636
|
return failure("permission_denied", "action approval lacks native receipt assurance");
|
|
10637
|
+
const receipts = entry.provenance.receipts;
|
|
10638
|
+
const standing = receipts.filter((receipt) => typeof receipt === "object" && receipt !== null && receipt.kind === "standing");
|
|
10639
|
+
const questionBacked = receipts.some((receipt) => typeof receipt === "object" && receipt !== null && receipt.kind === "host");
|
|
10640
|
+
if (!questionBacked && standing.length > 0) {
|
|
10641
|
+
let operation;
|
|
10642
|
+
try {
|
|
10643
|
+
operation = JSON.parse(decision.binding.approvedContent).operation;
|
|
10644
|
+
} catch {
|
|
10645
|
+
operation = null;
|
|
10646
|
+
}
|
|
10647
|
+
const cls = operationAutoClass(operation);
|
|
10648
|
+
if (!cls || !standing.some((receipt) => standingApprovalLive(store.root, receipt, cls)))
|
|
10649
|
+
return failure("permission_denied", "standing auto-approval is not live for this operation");
|
|
10650
|
+
}
|
|
10170
10651
|
if (decision.revoked)
|
|
10171
10652
|
return failure("permission_denied", "decision is revoked");
|
|
10172
10653
|
if (decision.digest !== decisionDigest(decision))
|
|
@@ -10180,18 +10661,17 @@ var verifiedAuthorities, trustedAuthority = (kind, provenance, expected, binding
|
|
|
10180
10661
|
return content;
|
|
10181
10662
|
const requestedSteps = parseSteps(decision.binding.approvedContent);
|
|
10182
10663
|
if (requestedSteps === null)
|
|
10183
|
-
return failure("invalid_input", "bounded action steps must be a
|
|
10184
|
-
if (input.steps && canonicalJson(input.steps) !== canonicalJson(requestedSteps))
|
|
10664
|
+
return failure("invalid_input", "bounded action steps must be a unique non-empty array");
|
|
10665
|
+
if (input.steps && canonicalJson(input.steps) !== canonicalJson(requestedSteps.map(chainStepKey)))
|
|
10185
10666
|
return failure("permission_denied", "bounded action steps do not match approved content");
|
|
10186
|
-
if (requestedSteps.some((step) => !step || typeof step !== "string") || new Set(requestedSteps).size !== requestedSteps.length)
|
|
10187
|
-
return failure("invalid_input", "bounded action steps must be unique and non-empty");
|
|
10188
10667
|
const stored = task.actionProgress?.find((item) => item.decisionId === input.decisionId);
|
|
10189
|
-
const workflow = stored ? { steps: stored.steps, completed: stored.completedSteps } : { steps: requestedSteps, completed: [] };
|
|
10190
|
-
if (workflow.steps.length && requestedSteps.length && canonicalJson(workflow.steps) !== canonicalJson(requestedSteps))
|
|
10668
|
+
const workflow = stored ? { steps: stored.steps, completed: stored.completedSteps } : { steps: requestedSteps.map(chainStepKey), completed: [] };
|
|
10669
|
+
if (workflow.steps.length && requestedSteps.length && canonicalJson(workflow.steps) !== canonicalJson(requestedSteps.map(chainStepKey)))
|
|
10191
10670
|
return failure("permission_denied", "bounded action scope changed");
|
|
10192
10671
|
return success(null, null, { entry, workflow });
|
|
10193
10672
|
}, bindingCovers, verifyDecisionContent;
|
|
10194
10673
|
var init_authority = __esm(() => {
|
|
10674
|
+
init_auto_approval();
|
|
10195
10675
|
init_task_contract();
|
|
10196
10676
|
verifiedAuthorities = new WeakMap;
|
|
10197
10677
|
bindingCovers = scopeCovers;
|
|
@@ -10199,10 +10679,10 @@ var init_authority = __esm(() => {
|
|
|
10199
10679
|
});
|
|
10200
10680
|
|
|
10201
10681
|
// packages/workit-core/src/core/task-evaluation.ts
|
|
10202
|
-
import { createHash as
|
|
10682
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
10203
10683
|
import * as fs6 from "node:fs";
|
|
10204
10684
|
import { spawnSync } from "node:child_process";
|
|
10205
|
-
import
|
|
10685
|
+
import path8 from "node:path";
|
|
10206
10686
|
function captureCandidate(root, scope, environment = []) {
|
|
10207
10687
|
let checkout;
|
|
10208
10688
|
try {
|
|
@@ -10244,7 +10724,7 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
10244
10724
|
for (const item of [...names].sort()) {
|
|
10245
10725
|
if (!scopeMatches(item, normalizedScope))
|
|
10246
10726
|
continue;
|
|
10247
|
-
const target =
|
|
10727
|
+
const target = path8.join(checkout, item);
|
|
10248
10728
|
try {
|
|
10249
10729
|
const stat = fs6.lstatSync(target);
|
|
10250
10730
|
if (stat.isSymbolicLink()) {
|
|
@@ -10355,7 +10835,7 @@ function evaluateRequirements(task, workspace, capabilities, candidate, checkout
|
|
|
10355
10835
|
return (task.policy?.requirements ?? []).map((requirement) => {
|
|
10356
10836
|
const related = task.evidence.map((entry, index) => ({ entry, evaluation: evidence[index] })).filter(({ entry }) => entry.data.requirementIds.includes(requirement.id));
|
|
10357
10837
|
const passed = related.filter(({ entry, evaluation }) => {
|
|
10358
|
-
if (!evidenceMatchesRequirement(evaluation.status, entry.data.kind, requirement.dimension))
|
|
10838
|
+
if (!evidenceMatchesRequirement(evaluation.status, entry.data.kind, requirement.dimension, requirement.ruleId))
|
|
10359
10839
|
return false;
|
|
10360
10840
|
if ((entry.data.kind === "check" || entry.data.kind === "review") && (!entry.data.beforeCandidateId || !entry.data.candidateId || entry.data.beforeCandidateId !== entry.data.candidateId))
|
|
10361
10841
|
return false;
|
|
@@ -10363,11 +10843,12 @@ function evaluateRequirements(task, workspace, capabilities, candidate, checkout
|
|
|
10363
10843
|
return true;
|
|
10364
10844
|
const reviewSession = sessionFromRef(entry.data.reviewContext);
|
|
10365
10845
|
const sameImplementation = sameSession(reviewSession, task.intent.provenance.session);
|
|
10366
|
-
const sameEvidenceSession =
|
|
10846
|
+
const sameEvidenceSession = related.some(({ entry: other }) => other.id !== entry.id && other.data.kind === "review" && other.data.candidateId !== null && other.data.candidateId === entry.data.candidateId && sameSession(reviewSession, other.provenance.session));
|
|
10367
10847
|
return entry.data.kind === "review" && reviewSession !== null && sameSession(reviewSession, entry.provenance.session) && (requirement.ruleId === "self-review" || !sameImplementation && !sameEvidenceSession);
|
|
10368
10848
|
});
|
|
10369
10849
|
if (passed.length) {
|
|
10370
|
-
const
|
|
10850
|
+
const currentId = candidate?.id ?? null;
|
|
10851
|
+
const withRed = requirement.dimension === "testing" ? passed.filter(({ entry }) => related.some((other) => other.entry.data.kind === "check" && other.evaluation.status === "failed" && other.entry.data.requirementIds.includes(requirement.id) && other.entry.recordedAt <= entry.recordedAt) || related.some((baseline) => baseline.entry.id !== entry.id && baseline.entry.data.kind === "check" && baseline.entry.data.result === "passed" && baseline.entry.data.candidateId !== null && currentId !== null && baseline.entry.data.candidateId !== currentId)) : passed;
|
|
10371
10852
|
if (withRed.length)
|
|
10372
10853
|
return {
|
|
10373
10854
|
requirementId: requirement.id,
|
|
@@ -10416,6 +10897,14 @@ function evaluateRequirements(task, workspace, capabilities, candidate, checkout
|
|
|
10416
10897
|
reason: "an applicable approved limitation permits the missing evidence"
|
|
10417
10898
|
};
|
|
10418
10899
|
const unavailable = capabilities.some((capability) => capability.assurance === "unavailable" && (capability.name === requirement.dimension || capability.surface === requirement.dimension));
|
|
10900
|
+
if (!unavailable && related.length > 0 && !passed.length)
|
|
10901
|
+
return {
|
|
10902
|
+
requirementId: requirement.id,
|
|
10903
|
+
status: "unsatisfied",
|
|
10904
|
+
evidenceIds: related.map(({ entry }) => entry.id),
|
|
10905
|
+
decisionIds: [],
|
|
10906
|
+
reason: `no passing evidence (needs kind:${expectedKinds(requirement.dimension, requirement.ruleId)})`
|
|
10907
|
+
};
|
|
10419
10908
|
return {
|
|
10420
10909
|
requirementId: requirement.id,
|
|
10421
10910
|
status: unavailable ? "unavailable" : "unsatisfied",
|
|
@@ -10440,7 +10929,13 @@ function evaluateClosure(requestedOutcome, view) {
|
|
|
10440
10929
|
if (entry.data.disposition === "fixed") {
|
|
10441
10930
|
const verified = entry.data.resolution?.evidenceIds.some((id) => {
|
|
10442
10931
|
const evidence = view.task.evidence.find((item) => item.id === id);
|
|
10443
|
-
|
|
10932
|
+
if (!evidence)
|
|
10933
|
+
return false;
|
|
10934
|
+
return findingVerificationPasses(entry.data.candidateId, {
|
|
10935
|
+
kind: evidence.data.kind,
|
|
10936
|
+
candidateId: evidence.data.candidateId,
|
|
10937
|
+
status: evidenceById.get(id) ?? "missing"
|
|
10938
|
+
});
|
|
10444
10939
|
});
|
|
10445
10940
|
if (!verified)
|
|
10446
10941
|
return failure("requirements_unsatisfied", "fixed findings require current verification");
|
|
@@ -10456,7 +10951,12 @@ function evaluateClosure(requestedOutcome, view) {
|
|
|
10456
10951
|
}
|
|
10457
10952
|
}
|
|
10458
10953
|
}
|
|
10459
|
-
const blocking = evaluations.filter((item) =>
|
|
10954
|
+
const blocking = evaluations.filter((item) => {
|
|
10955
|
+
if (item.status !== "unsatisfied" && item.status !== "unavailable")
|
|
10956
|
+
return false;
|
|
10957
|
+
const requirement = view.task.policy?.requirements.find((candidate) => candidate.id === item.requirementId);
|
|
10958
|
+
return !requirement || requirement.before === "close";
|
|
10959
|
+
});
|
|
10460
10960
|
if (requestedOutcome !== "stopped" && blocking.length)
|
|
10461
10961
|
return failure("requirements_unsatisfied", "applicable requirements are unsatisfied", {
|
|
10462
10962
|
requirementIds: blocking.map((item) => item.requirementId),
|
|
@@ -10495,12 +10995,12 @@ function evaluateClosure(requestedOutcome, view) {
|
|
|
10495
10995
|
requirementIds: evaluations.map((item) => item.requirementId)
|
|
10496
10996
|
});
|
|
10497
10997
|
}
|
|
10498
|
-
var digestBytes3 = (value) =>
|
|
10998
|
+
var digestBytes3 = (value) => createHash5("sha256").update(value).digest("hex"), inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path8.sep}`), relative = (root, candidate) => path8.relative(root, candidate).split(path8.sep).join("/") || ".", canonicalPath = (value) => {
|
|
10499
10999
|
if (typeof value !== "string" || !value)
|
|
10500
11000
|
return null;
|
|
10501
11001
|
if (value.includes("\\") || value.split("/").includes(".."))
|
|
10502
11002
|
return null;
|
|
10503
|
-
const normalized =
|
|
11003
|
+
const normalized = path8.posix.normalize(value);
|
|
10504
11004
|
if (normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/"))
|
|
10505
11005
|
return null;
|
|
10506
11006
|
return normalized === "." ? "." : normalized.replace(/\/$/, "");
|
|
@@ -10542,14 +11042,14 @@ var digestBytes3 = (value) => createHash4("sha256").update(value).digest("hex"),
|
|
|
10542
11042
|
git: /not a git repository/i.test(stderr) ? false : true
|
|
10543
11043
|
};
|
|
10544
11044
|
}
|
|
10545
|
-
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(
|
|
11045
|
+
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path8.sep).join("/")) : [];
|
|
10546
11046
|
const stagedDeleted = spawnSync("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
|
|
10547
11047
|
if (stagedDeleted.status !== 0) {
|
|
10548
11048
|
const stderr = stagedDeleted.stderr?.toString("utf8") ?? "";
|
|
10549
11049
|
if (!/not a git repository/i.test(stderr))
|
|
10550
11050
|
return { paths, uncertain: true, git: true };
|
|
10551
11051
|
} else if (stagedDeleted.stdout) {
|
|
10552
|
-
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(
|
|
11052
|
+
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path8.sep).join("/")));
|
|
10553
11053
|
}
|
|
10554
11054
|
return { paths: [...new Set(paths)], uncertain: false, git: true };
|
|
10555
11055
|
}, walk = (root, directory, output) => {
|
|
@@ -10563,7 +11063,7 @@ var digestBytes3 = (value) => createHash4("sha256").update(value).digest("hex"),
|
|
|
10563
11063
|
for (const name of names) {
|
|
10564
11064
|
if (name === ".git" || name === ".workit")
|
|
10565
11065
|
continue;
|
|
10566
|
-
const target =
|
|
11066
|
+
const target = path8.join(directory, name);
|
|
10567
11067
|
let stat;
|
|
10568
11068
|
try {
|
|
10569
11069
|
stat = fs6.lstatSync(target);
|
|
@@ -10580,12 +11080,12 @@ var digestBytes3 = (value) => createHash4("sha256").update(value).digest("hex"),
|
|
|
10580
11080
|
}, scopeRoots = (root, scope) => {
|
|
10581
11081
|
const roots = [];
|
|
10582
11082
|
for (const item of scope.paths.length ? scope.paths : ["."]) {
|
|
10583
|
-
const target =
|
|
11083
|
+
const target = path8.resolve(root, item);
|
|
10584
11084
|
if (!inside(root, target))
|
|
10585
11085
|
return failure("invalid_input", `candidate scope escapes checkout: ${item} — keep one task per repository and coordinate a separate linked task in the other checkout`, { fields: [{ path: "scope.paths", reason: `${item} is outside the checkout` }] });
|
|
10586
11086
|
let ancestor = target;
|
|
10587
11087
|
while (!fs6.existsSync(ancestor) && ancestor !== root)
|
|
10588
|
-
ancestor =
|
|
11088
|
+
ancestor = path8.dirname(ancestor);
|
|
10589
11089
|
try {
|
|
10590
11090
|
if (!inside(root, fs6.realpathSync(ancestor)))
|
|
10591
11091
|
return failure("invalid_input", `candidate scope escapes checkout through a symlink: ${item} — keep one task per repository and coordinate a separate linked task in the other checkout`, { fields: [{ path: "scope.paths", reason: `${item} resolves outside the checkout` }] });
|
|
@@ -10612,11 +11112,23 @@ var digestBytes3 = (value) => createHash4("sha256").update(value).digest("hex"),
|
|
|
10612
11112
|
}, sameSession = (left, right) => {
|
|
10613
11113
|
const other = sessionFromRef(right);
|
|
10614
11114
|
return left !== null && other !== null && left.host === other.host && left.handle === other.handle;
|
|
10615
|
-
}, evidenceScopes = (task, ids) => (task.policy?.requirements ?? []).filter((requirement) => ids.includes(requirement.id)).map((requirement) => requirement.scope), scopeCovers2,
|
|
11115
|
+
}, evidenceScopes = (task, ids) => (task.policy?.requirements ?? []).filter((requirement) => ids.includes(requirement.id)).map((requirement) => requirement.scope), scopeCovers2, checkPin = (assignment, beforeCandidateId, candidateId) => {
|
|
11116
|
+
const pin = assignment.candidateId ?? null;
|
|
11117
|
+
if (pin === null)
|
|
11118
|
+
return true;
|
|
11119
|
+
return (beforeCandidateId ?? null) === pin || (candidateId ?? null) === pin;
|
|
11120
|
+
}, resolveBinding = (evidence, pin, currentCandidateId) => {
|
|
11121
|
+
const bound = evidence.kind === "check" || evidence.kind === "review";
|
|
11122
|
+
const fallback = bound ? pin ?? currentCandidateId : null;
|
|
11123
|
+
return {
|
|
11124
|
+
beforeCandidateId: evidence.beforeCandidateId ?? fallback,
|
|
11125
|
+
candidateId: evidence.candidateId ?? fallback
|
|
11126
|
+
};
|
|
11127
|
+
}, findingVerificationPasses = (findingCandidateId, evidence) => evidence.status === "passed" && (evidence.kind === "check" || evidence.kind === "review") && ((findingCandidateId ?? null) === null || evidence.candidateId === findingCandidateId), 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"))), 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 })), evidenceMatchesRequirement = (kind, evidenceKind, dimension, ruleId) => {
|
|
10616
11128
|
if (kind !== "passed")
|
|
10617
11129
|
return false;
|
|
10618
11130
|
if (dimension === "testing" || dimension === "verification")
|
|
10619
|
-
return evidenceKind === "check";
|
|
11131
|
+
return evidenceKind === "check" || ruleId === "pre-pr-cleanup" && evidenceKind === "artifact";
|
|
10620
11132
|
if (dimension === "review")
|
|
10621
11133
|
return evidenceKind === "review";
|
|
10622
11134
|
if (dimension === "investigation" || dimension === "challenge")
|
|
@@ -10624,6 +11136,20 @@ var digestBytes3 = (value) => createHash4("sha256").update(value).digest("hex"),
|
|
|
10624
11136
|
if (dimension === "artifacts" || dimension === "continuity")
|
|
10625
11137
|
return evidenceKind === "artifact";
|
|
10626
11138
|
return false;
|
|
11139
|
+
}, expectedKinds = (dimension, ruleId) => {
|
|
11140
|
+
if (dimension === "testing")
|
|
11141
|
+
return "check";
|
|
11142
|
+
if (dimension === "verification")
|
|
11143
|
+
return ruleId === "pre-pr-cleanup" ? "check or artifact" : "check";
|
|
11144
|
+
if (dimension === "review")
|
|
11145
|
+
return "review";
|
|
11146
|
+
if (dimension === "investigation" || dimension === "challenge")
|
|
11147
|
+
return "investigation";
|
|
11148
|
+
if (dimension === "artifacts" || dimension === "continuity")
|
|
11149
|
+
return "artifact";
|
|
11150
|
+
if (dimension === "decisions")
|
|
11151
|
+
return "decision";
|
|
11152
|
+
return "worker report";
|
|
10627
11153
|
};
|
|
10628
11154
|
var init_task_evaluation = __esm(() => {
|
|
10629
11155
|
init_task_contract();
|
|
@@ -10634,7 +11160,7 @@ var init_task_evaluation = __esm(() => {
|
|
|
10634
11160
|
// packages/workit-core/src/core/methods.ts
|
|
10635
11161
|
function selectMethods(policy, capabilities) {
|
|
10636
11162
|
return Object.entries(METHODS).flatMap(([id, definition]) => {
|
|
10637
|
-
const requirements = policy.requirements.filter((requirement) => methodMatches(
|
|
11163
|
+
const requirements = policy.requirements.filter((requirement) => methodMatches(definition, requirement));
|
|
10638
11164
|
if (requirements.length === 0)
|
|
10639
11165
|
return [];
|
|
10640
11166
|
const capability = assuranceFor(id, definition, capabilities);
|
|
@@ -10647,7 +11173,7 @@ function selectMethods(policy, capabilities) {
|
|
|
10647
11173
|
return [{ id, reason: reasons.join("; "), assurance: capability.assurance }];
|
|
10648
11174
|
});
|
|
10649
11175
|
}
|
|
10650
|
-
var METHODS, methodMatches = (
|
|
11176
|
+
var METHODS, methodMatches = (definition, requirement) => definition.dimensions?.includes(requirement.dimension) === true || definition.ruleIds?.includes(requirement.ruleId) === true, assuranceFor = (id, definition, capabilities) => {
|
|
10651
11177
|
const keys = new Set([
|
|
10652
11178
|
id,
|
|
10653
11179
|
...definition.dimensions ?? [],
|
|
@@ -10721,12 +11247,12 @@ act from memory of it.
|
|
|
10721
11247
|
var init_methods = __esm(() => {
|
|
10722
11248
|
METHODS = {
|
|
10723
11249
|
"workit-challenge": { dimensions: ["challenge", "decisions"] },
|
|
10724
|
-
"workit-behavioral-tdd": { dimensions: ["testing"] },
|
|
10725
|
-
"workit-review": { dimensions: ["review"], ruleIds: ["fresh-context-review"] },
|
|
11250
|
+
"workit-behavioral-tdd": { dimensions: ["testing"], ruleIds: ["mechanical-existing-checks"] },
|
|
11251
|
+
"workit-review": { dimensions: ["review"], ruleIds: ["fresh-context-review", "self-review"] },
|
|
10726
11252
|
"workit-plan": { dimensions: ["artifacts", "continuity"] },
|
|
10727
11253
|
"workit-implement": { dimensions: ["delegation"] },
|
|
10728
|
-
"workit-debug": {
|
|
10729
|
-
"workit-handoff": {
|
|
11254
|
+
"workit-debug": {},
|
|
11255
|
+
"workit-handoff": {},
|
|
10730
11256
|
"workit-deslop": { ruleIds: ["pre-pr-cleanup"] }
|
|
10731
11257
|
};
|
|
10732
11258
|
});
|
|
@@ -10837,10 +11363,14 @@ var init_route_intent = __esm(() => {
|
|
|
10837
11363
|
init_task_store();
|
|
10838
11364
|
});
|
|
10839
11365
|
|
|
11366
|
+
// packages/workit-core/src/core/runtime-identity.ts
|
|
11367
|
+
var init_runtime_identity = () => {};
|
|
11368
|
+
|
|
10840
11369
|
// packages/workit-core/src/core/external-action.ts
|
|
10841
11370
|
var externalActionSchema;
|
|
10842
11371
|
var init_external_action = __esm(() => {
|
|
10843
11372
|
init_task_contract();
|
|
11373
|
+
init_authority();
|
|
10844
11374
|
init_zod();
|
|
10845
11375
|
externalActionSchema = discriminatedUnion("operation", [
|
|
10846
11376
|
object({
|
|
@@ -10874,6 +11404,13 @@ var init_external_action = __esm(() => {
|
|
|
10874
11404
|
babysit: boolean2().optional()
|
|
10875
11405
|
}).strict()
|
|
10876
11406
|
}).strict(),
|
|
11407
|
+
object({
|
|
11408
|
+
operation: literal("hosting.merge"),
|
|
11409
|
+
payload: object({
|
|
11410
|
+
target_branch: string2().min(1).optional(),
|
|
11411
|
+
source_branch: string2().min(1).optional()
|
|
11412
|
+
}).strict()
|
|
11413
|
+
}).strict(),
|
|
10877
11414
|
object({
|
|
10878
11415
|
operation: literal("youtrack.update"),
|
|
10879
11416
|
payload: object({
|
|
@@ -11254,7 +11791,7 @@ var init_policy_resolver = __esm(() => {
|
|
|
11254
11791
|
});
|
|
11255
11792
|
|
|
11256
11793
|
// packages/workit-core/src/core/task-engine.ts
|
|
11257
|
-
import { realpathSync as
|
|
11794
|
+
import { realpathSync as realpathSync5 } from "node:fs";
|
|
11258
11795
|
|
|
11259
11796
|
class WorkitCore {
|
|
11260
11797
|
store;
|
|
@@ -11269,7 +11806,7 @@ class WorkitCore {
|
|
|
11269
11806
|
}
|
|
11270
11807
|
contextRootError() {
|
|
11271
11808
|
try {
|
|
11272
|
-
if (
|
|
11809
|
+
if (realpathSync5(this.context.root) !== this.store.root)
|
|
11273
11810
|
return failure("invalid_input", "operation context root does not match the task store root");
|
|
11274
11811
|
} catch {
|
|
11275
11812
|
return failure("invalid_input", "operation context root cannot be resolved");
|
|
@@ -11314,7 +11851,7 @@ class WorkitCore {
|
|
|
11314
11851
|
activeWorkerBlocker(task, workspace) {
|
|
11315
11852
|
if (workspace.writer)
|
|
11316
11853
|
return failure("recovery_required", "writer ownership must be released first");
|
|
11317
|
-
if (task.workers.some((entry) =>
|
|
11854
|
+
if (task.workers.some((entry) => workerBlocksTransition("close", entry.data.state)))
|
|
11318
11855
|
return failure("recovery_required", "worker state requires reconciliation");
|
|
11319
11856
|
return null;
|
|
11320
11857
|
}
|
|
@@ -11560,37 +12097,33 @@ class WorkitCore {
|
|
|
11560
12097
|
const currentCandidate = captureCandidate(this.store.root, task.data.intent.data.scope, environment());
|
|
11561
12098
|
if (!currentCandidate.ok)
|
|
11562
12099
|
return currentCandidate;
|
|
11563
|
-
|
|
11564
|
-
|
|
11565
|
-
evidence.candidateId ??= currentCandidate.data.id;
|
|
11566
|
-
} else {
|
|
11567
|
-
evidence.beforeCandidateId ??= null;
|
|
11568
|
-
evidence.candidateId ??= null;
|
|
11569
|
-
}
|
|
12100
|
+
const pin = helper && helper.ok ? helper.data.data.assignment.candidateId : null;
|
|
12101
|
+
const bound = resolveBinding(evidence, pin, currentCandidate.data.id);
|
|
11570
12102
|
if (helper?.ok) {
|
|
11571
12103
|
const assignment = helper.data.data.assignment;
|
|
11572
|
-
if (evidence.requirementIds.some((id) => !assignment.requirementIds.includes(id)) || assignment
|
|
12104
|
+
if (evidence.requirementIds.some((id) => !assignment.requirementIds.includes(id)) || !checkPin(assignment, bound.beforeCandidateId, bound.candidateId) || !refsWithinScope(evidence.refs, assignment.scope) || !refsWithinScope(evidence.refs, task.data.intent.data.scope))
|
|
11573
12105
|
return failure("permission_denied", "evidence is outside the worker assignment");
|
|
11574
12106
|
}
|
|
11575
12107
|
const knownCandidates = new Set(task.data.candidates.map((candidate) => candidate.id));
|
|
11576
|
-
for (const candidateId of [
|
|
12108
|
+
for (const candidateId of [bound.beforeCandidateId, bound.candidateId]) {
|
|
11577
12109
|
if (candidateId && candidateId !== currentCandidate.data.id && !knownCandidates.has(candidateId))
|
|
11578
12110
|
return failure("invalid_input", "evidence candidate is not a captured candidate");
|
|
11579
12111
|
}
|
|
12112
|
+
const boundEvidence = { ...evidence, ...bound };
|
|
11580
12113
|
const changed = this.store.mutateTask(task.data.id, input.expectedRevision, (current, mutation) => {
|
|
11581
12114
|
const candidates = current.candidates.some((item) => item.id === currentCandidate.data.id) ? current.candidates : [...current.candidates, currentCandidate.data];
|
|
11582
12115
|
const entry = {
|
|
11583
12116
|
id: newId(),
|
|
11584
12117
|
recordedAt: mutation.now,
|
|
11585
12118
|
provenance: provenance(this.context, "agent_reported"),
|
|
11586
|
-
data:
|
|
12119
|
+
data: boundEvidence
|
|
11587
12120
|
};
|
|
11588
12121
|
return success(mutation.revision, null, {
|
|
11589
12122
|
...current,
|
|
11590
12123
|
candidates,
|
|
11591
12124
|
evidence: [...current.evidence, entry],
|
|
11592
12125
|
findings: current.findings.map((finding) => {
|
|
11593
|
-
if (finding.data.disposition === "open" ||
|
|
12126
|
+
if (finding.data.disposition === "open" || boundEvidence.result === "skipped")
|
|
11594
12127
|
return finding;
|
|
11595
12128
|
if (finding.data.disposition !== "fixed")
|
|
11596
12129
|
return finding;
|
|
@@ -11599,8 +12132,12 @@ class WorkitCore {
|
|
|
11599
12132
|
item.evidenceId,
|
|
11600
12133
|
item.status
|
|
11601
12134
|
]));
|
|
11602
|
-
const verified = withEntry.evidence.some((item) =>
|
|
11603
|
-
|
|
12135
|
+
const verified = withEntry.evidence.some((item) => findingVerificationPasses(finding.data.candidateId, {
|
|
12136
|
+
kind: item.data.kind,
|
|
12137
|
+
candidateId: item.data.candidateId,
|
|
12138
|
+
status: statuses.get(item.id) ?? "missing"
|
|
12139
|
+
}));
|
|
12140
|
+
const contradicted = boundEvidence.result === "failed" && finding.data.refs.some((left) => boundEvidence.refs.some((right) => sameValue(left, right)));
|
|
11604
12141
|
return verified && !contradicted ? finding : { ...finding, data: { ...finding.data, disposition: "open", resolution: null } };
|
|
11605
12142
|
})
|
|
11606
12143
|
});
|
|
@@ -11615,7 +12152,10 @@ class WorkitCore {
|
|
|
11615
12152
|
observeDecision(request, observation) {
|
|
11616
12153
|
return this.recordDecision(request, observation, true);
|
|
11617
12154
|
}
|
|
11618
|
-
|
|
12155
|
+
observeStandingDecision(request) {
|
|
12156
|
+
return this.recordDecision(request, undefined, true, true);
|
|
12157
|
+
}
|
|
12158
|
+
recordDecision(request, nativeObservation, nativeRequired = false, standing = false) {
|
|
11619
12159
|
const root = this.contextRootError();
|
|
11620
12160
|
if (!root.ok)
|
|
11621
12161
|
return root;
|
|
@@ -11630,8 +12170,10 @@ class WorkitCore {
|
|
|
11630
12170
|
return failure("permission_denied", "stated choices cannot authorize mutating actions");
|
|
11631
12171
|
if (stated && !input.binding?.statedChoice)
|
|
11632
12172
|
return failure("invalid_input", "stated choices require binding.statedChoice");
|
|
11633
|
-
if (nativeRequired && nativeObservation === undefined && !stated)
|
|
12173
|
+
if (nativeRequired && nativeObservation === undefined && !stated && !standing)
|
|
11634
12174
|
return failure("permission_denied", "native decision observation is required");
|
|
12175
|
+
if (standing && (!input.binding?.standing || input.purpose !== "action" || input.response !== "approved"))
|
|
12176
|
+
return failure("invalid_input", "standing approvals need an approved action binding with a standing rule");
|
|
11635
12177
|
const task = this.store.readTask(input.taskId);
|
|
11636
12178
|
if (!task.ok)
|
|
11637
12179
|
return task;
|
|
@@ -11663,7 +12205,10 @@ class WorkitCore {
|
|
|
11663
12205
|
consumption: null
|
|
11664
12206
|
};
|
|
11665
12207
|
const data = { ...base, digest: decisionDigest(base) };
|
|
11666
|
-
const
|
|
12208
|
+
const standingApproval = standing && !stated ? verifyStandingApproval(this.store.root, task.data, this.context.caller, input.binding) : null;
|
|
12209
|
+
if (standingApproval && !standingApproval.ok)
|
|
12210
|
+
return standingApproval;
|
|
12211
|
+
const native = nativeRequired && !stated && !standingApproval ? verifyNativeDecision(this.context.nativeAuthority, {
|
|
11667
12212
|
observation: nativeObservation,
|
|
11668
12213
|
expected: {
|
|
11669
12214
|
taskId: task.data.id,
|
|
@@ -11694,7 +12239,7 @@ class WorkitCore {
|
|
|
11694
12239
|
const entry = {
|
|
11695
12240
|
id: newId(),
|
|
11696
12241
|
recordedAt: mutation.now,
|
|
11697
|
-
provenance: nativeProvenance ?? provenance(this.context, "agent_reported"),
|
|
12242
|
+
provenance: standingApproval?.ok === true ? standingApproval.data : nativeProvenance ?? provenance(this.context, "agent_reported"),
|
|
11698
12243
|
data
|
|
11699
12244
|
};
|
|
11700
12245
|
return success(mutation.revision, null, {
|
|
@@ -11758,7 +12303,7 @@ class WorkitCore {
|
|
|
11758
12303
|
if (input.action === "record") {
|
|
11759
12304
|
if (helper?.ok) {
|
|
11760
12305
|
const assignment = helper.data.data.assignment;
|
|
11761
|
-
if (!bindingCovers(assignment.scope, input.scope) || !bindingCovers(task.data.intent.data.scope, input.scope) || assignment
|
|
12306
|
+
if (!bindingCovers(assignment.scope, input.scope) || !bindingCovers(task.data.intent.data.scope, input.scope) || !checkPin(assignment, null, input.candidateId ?? null) || !refsWithinScope(input.refs, assignment.scope) || !refsWithinScope(input.refs, task.data.intent.data.scope))
|
|
11762
12307
|
return failure("permission_denied", "finding is outside the worker assignment");
|
|
11763
12308
|
}
|
|
11764
12309
|
if (input.candidateId && !task.data.candidates.some((candidate) => candidate.id === input.candidateId))
|
|
@@ -11805,7 +12350,11 @@ class WorkitCore {
|
|
|
11805
12350
|
const evaluations = evaluateEvidence(task.data, current.data);
|
|
11806
12351
|
const verified = evidence.some((entry) => {
|
|
11807
12352
|
const evaluation = evaluations.find((item) => item.evidenceId === entry.id);
|
|
11808
|
-
return
|
|
12353
|
+
return findingVerificationPasses(finding.data.candidateId, {
|
|
12354
|
+
kind: entry.data.kind,
|
|
12355
|
+
candidateId: entry.data.candidateId,
|
|
12356
|
+
status: evaluation?.status ?? "missing"
|
|
12357
|
+
});
|
|
11809
12358
|
});
|
|
11810
12359
|
if (!verified)
|
|
11811
12360
|
return failure("permission_denied", "fixed findings require passing verification evidence");
|
|
@@ -11817,7 +12366,7 @@ class WorkitCore {
|
|
|
11817
12366
|
if (entry.data.result === "missing" || entry.data.result === "skipped")
|
|
11818
12367
|
return false;
|
|
11819
12368
|
const candidateMatches = finding.data.candidateId === null ? entry.data.candidateId === null : entry.data.candidateId === finding.data.candidateId || entry.data.beforeCandidateId === finding.data.candidateId;
|
|
11820
|
-
const referenceMatches = finding.data.refs.some((left) => entry.data.refs.some((right) =>
|
|
12369
|
+
const referenceMatches = finding.data.refs.some((left) => entry.data.refs.some((right) => sameValue(left, right)));
|
|
11821
12370
|
return candidateMatches && (referenceMatches || entry.data.claim === finding.data.claim);
|
|
11822
12371
|
});
|
|
11823
12372
|
if (!relevant)
|
|
@@ -11895,7 +12444,7 @@ class WorkitCore {
|
|
|
11895
12444
|
const workerId = newId();
|
|
11896
12445
|
const changed = this.store.mutateTaskAndWorkspace({
|
|
11897
12446
|
taskId: task.data.id,
|
|
11898
|
-
|
|
12447
|
+
expectedRevision: input.expectedRevision,
|
|
11899
12448
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
11900
12449
|
now: trustedNow(this.context),
|
|
11901
12450
|
workspace: (current, mutation) => success(mutation.revision, mutation.revision, current),
|
|
@@ -11933,7 +12482,7 @@ class WorkitCore {
|
|
|
11933
12482
|
return failure("permission_denied", "worker report is outside the worker assignment");
|
|
11934
12483
|
const changed = this.store.mutateTaskAndWorkspace({
|
|
11935
12484
|
taskId: task.data.id,
|
|
11936
|
-
|
|
12485
|
+
expectedRevision: input.expectedRevision,
|
|
11937
12486
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
11938
12487
|
now: trustedNow(this.context),
|
|
11939
12488
|
workspace: (current, mutation) => success(mutation.revision, mutation.revision, current),
|
|
@@ -11957,24 +12506,25 @@ class WorkitCore {
|
|
|
11957
12506
|
return failure("invalid_transition", "closed task cannot cancel workers");
|
|
11958
12507
|
if (entry.data.state === "unknown")
|
|
11959
12508
|
return failure("recovery_required", "worker state requires recovery");
|
|
11960
|
-
if (entry.data.state === "stopped")
|
|
11961
|
-
|
|
11962
|
-
|
|
12509
|
+
if (entry.data.state === "stopped") {
|
|
12510
|
+
const stopped = task.data.workers.find((candidate) => candidate.id === input.workerId);
|
|
12511
|
+
return success(task.data.revision, workspace.data.revision, stopped);
|
|
12512
|
+
}
|
|
11963
12513
|
const changed = this.store.mutateTaskAndWorkspace({
|
|
11964
12514
|
taskId: task.data.id,
|
|
11965
|
-
|
|
12515
|
+
expectedRevision: input.expectedRevision,
|
|
11966
12516
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
11967
12517
|
now: trustedNow(this.context),
|
|
11968
|
-
workspace: (current, mutation) => success(mutation.revision, mutation.revision,
|
|
12518
|
+
workspace: (current, mutation) => success(mutation.revision, mutation.revision, {
|
|
12519
|
+
...current,
|
|
12520
|
+
writer: current.writer?.owner.workerId === input.workerId ? null : current.writer
|
|
12521
|
+
}),
|
|
11969
12522
|
task: (current, mutation) => success(mutation.revision, null, {
|
|
11970
12523
|
...current,
|
|
11971
12524
|
workers: current.workers.map((candidate) => candidate.id === input.workerId ? {
|
|
11972
12525
|
...candidate,
|
|
11973
12526
|
recordedAt: mutation.now,
|
|
11974
|
-
data: {
|
|
11975
|
-
...candidate.data,
|
|
11976
|
-
state: settles ? "stopped" : "cancelling"
|
|
11977
|
-
}
|
|
12527
|
+
data: { ...candidate.data, state: "stopped" }
|
|
11978
12528
|
} : candidate)
|
|
11979
12529
|
})
|
|
11980
12530
|
});
|
|
@@ -12071,7 +12621,7 @@ class WorkitCore {
|
|
|
12071
12621
|
return attested;
|
|
12072
12622
|
const changed = this.store.mutateTaskAndWorkspace({
|
|
12073
12623
|
taskId: task.data.id,
|
|
12074
|
-
|
|
12624
|
+
expectedRevision: input.expectedRevision,
|
|
12075
12625
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
12076
12626
|
now: trustedNow(this.context),
|
|
12077
12627
|
workspace: (current, mutation) => success(mutation.revision, mutation.revision, current),
|
|
@@ -12237,6 +12787,12 @@ class WorkitCore {
|
|
|
12237
12787
|
if (!sameSession2(helper.data.data.session, this.context))
|
|
12238
12788
|
return failure("permission_denied", "worker session does not match the caller");
|
|
12239
12789
|
}
|
|
12790
|
+
const gate = this.view(task.data);
|
|
12791
|
+
if (gate.ok) {
|
|
12792
|
+
const writeGates = (task.data.policy?.requirements ?? []).filter((requirement) => requirement.before === "write" && gate.data.requirements.some((evaluation) => evaluation.requirementId === requirement.id && (evaluation.status === "unsatisfied" || evaluation.status === "unavailable")));
|
|
12793
|
+
if (writeGates.length)
|
|
12794
|
+
return failure("requirements_unsatisfied", "writer ownership is gated by unsatisfied requirements; record their evidence or an approved limitation waiver first", { requirementIds: writeGates.map((requirement) => requirement.id) });
|
|
12795
|
+
}
|
|
12240
12796
|
const owner = {
|
|
12241
12797
|
taskId: task.data.id,
|
|
12242
12798
|
workerId: helperId,
|
|
@@ -12248,7 +12804,7 @@ class WorkitCore {
|
|
|
12248
12804
|
};
|
|
12249
12805
|
const changed = this.store.mutateTaskAndWorkspace({
|
|
12250
12806
|
taskId: task.data.id,
|
|
12251
|
-
|
|
12807
|
+
expectedRevision: input.expectedRevision,
|
|
12252
12808
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
12253
12809
|
now: trustedNow(this.context),
|
|
12254
12810
|
workspace: (current, mutation) => success(mutation.revision, mutation.revision, {
|
|
@@ -12279,7 +12835,7 @@ class WorkitCore {
|
|
|
12279
12835
|
}
|
|
12280
12836
|
const changed = this.store.mutateTaskAndWorkspace({
|
|
12281
12837
|
taskId: task.data.id,
|
|
12282
|
-
|
|
12838
|
+
expectedRevision: input.expectedRevision,
|
|
12283
12839
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
12284
12840
|
now: trustedNow(this.context),
|
|
12285
12841
|
workspace: (currentWorkspace, mutation) => success(mutation.revision, mutation.revision, { ...currentWorkspace, writer: null }),
|
|
@@ -12511,7 +13067,7 @@ class WorkitCore {
|
|
|
12511
13067
|
if (view.task.workspaceId !== view.workspace.id)
|
|
12512
13068
|
return failure("invalid_input", "task and workspace bindings are invalid");
|
|
12513
13069
|
try {
|
|
12514
|
-
if (
|
|
13070
|
+
if (realpathSync5(view.workspace.root) !== this.store.root)
|
|
12515
13071
|
return failure("invalid_input", "task view root does not match the task store");
|
|
12516
13072
|
} catch {
|
|
12517
13073
|
return failure("invalid_input", "task view root cannot be resolved");
|
|
@@ -12528,7 +13084,15 @@ class WorkitCore {
|
|
|
12528
13084
|
if (seen.has(observation.workerId))
|
|
12529
13085
|
return failure("invalid_input", "worker observations must be unique");
|
|
12530
13086
|
seen.add(observation.workerId);
|
|
12531
|
-
|
|
13087
|
+
const freshTask = this.store.readTask(view.task.id);
|
|
13088
|
+
const freshWorkspace = this.store.readWorkspace();
|
|
13089
|
+
if (!freshTask.ok)
|
|
13090
|
+
return freshTask;
|
|
13091
|
+
if (!freshWorkspace.ok)
|
|
13092
|
+
return freshWorkspace;
|
|
13093
|
+
if (!freshWorkspace.data)
|
|
13094
|
+
return failure("not_found", "workspace not found");
|
|
13095
|
+
if (observation.taskId !== view.task.id || observation.expectedRevision !== freshTask.data.revision || observation.expectedWorkspaceRevision !== freshWorkspace.data.revision)
|
|
12532
13096
|
return failure("revision_conflict", "worker observation is stale");
|
|
12533
13097
|
if (!view.task.workers.some((entry) => entry.id === observation.workerId))
|
|
12534
13098
|
return failure("permission_denied", "worker observation is not assigned");
|
|
@@ -12614,19 +13178,26 @@ class WorkitCore {
|
|
|
12614
13178
|
return failure("permission_denied", "imported resume requires native destination approval");
|
|
12615
13179
|
resumeCandidate = current.data.candidate;
|
|
12616
13180
|
}
|
|
12617
|
-
const blocker = this.activeWorkerBlocker(task.data, workspace.data);
|
|
13181
|
+
const blocker = status === "paused" ? workspace.data.writer ? failure("recovery_required", "writer ownership must be released first") : null : this.activeWorkerBlocker(task.data, workspace.data);
|
|
12618
13182
|
if (blocker)
|
|
12619
13183
|
return blocker;
|
|
13184
|
+
let pauseCandidate = null;
|
|
13185
|
+
if (status === "paused") {
|
|
13186
|
+
const captured = captureCandidate(this.store.root, task.data.intent.data.scope, environment());
|
|
13187
|
+
if (!captured.ok)
|
|
13188
|
+
return captured;
|
|
13189
|
+
pauseCandidate = captured.data;
|
|
13190
|
+
}
|
|
12620
13191
|
const changed = this.store.mutateTaskAndWorkspace({
|
|
12621
13192
|
taskId: task.data.id,
|
|
12622
|
-
|
|
13193
|
+
expectedRevision: input.expectedRevision,
|
|
12623
13194
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
12624
13195
|
now: trustedNow(this.context),
|
|
12625
13196
|
workspace: (workspace, context) => success(context.revision, context.revision, workspace),
|
|
12626
13197
|
task: (current, context) => success(context.revision, null, {
|
|
12627
13198
|
...current,
|
|
12628
13199
|
status,
|
|
12629
|
-
candidates: resumeCandidate && !current.candidates.some((candidate) => candidate.id === resumeCandidate.id) ? [...current.candidates, resumeCandidate] : current.candidates,
|
|
13200
|
+
candidates: (resumeCandidate ?? pauseCandidate) && !current.candidates.some((candidate) => candidate.id === (resumeCandidate ?? pauseCandidate).id) ? [...current.candidates, resumeCandidate ?? pauseCandidate] : current.candidates,
|
|
12630
13201
|
progress: current.progress,
|
|
12631
13202
|
pauseReason: status === "paused" ? input.reason ?? null : null
|
|
12632
13203
|
})
|
|
@@ -12664,12 +13235,13 @@ class WorkitCore {
|
|
|
12664
13235
|
return workspace;
|
|
12665
13236
|
if (!workspace.data)
|
|
12666
13237
|
return failure("not_found", "workspace not found");
|
|
12667
|
-
|
|
12668
|
-
|
|
13238
|
+
const revisionBlocker = this.activeWorkerBlocker(task.data, workspace.data);
|
|
13239
|
+
if (revisionBlocker)
|
|
13240
|
+
return revisionBlocker;
|
|
12669
13241
|
this.fillRevisions(input, task.data, workspace.data);
|
|
12670
13242
|
const changed = this.store.mutateTaskAndWorkspace({
|
|
12671
13243
|
taskId: task.data.id,
|
|
12672
|
-
|
|
13244
|
+
expectedRevision: input.expectedRevision,
|
|
12673
13245
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
12674
13246
|
now: trustedNow(this.context),
|
|
12675
13247
|
workspace: (workspace, context) => success(context.revision, context.revision, workspace),
|
|
@@ -12725,7 +13297,7 @@ class WorkitCore {
|
|
|
12725
13297
|
return closure;
|
|
12726
13298
|
const changed = this.store.mutateTaskAndWorkspace({
|
|
12727
13299
|
taskId: task.data.id,
|
|
12728
|
-
|
|
13300
|
+
expectedRevision: input.expectedRevision,
|
|
12729
13301
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
12730
13302
|
now: trustedNow(this.context),
|
|
12731
13303
|
workspace: (value, context) => success(context.revision, context.revision, value),
|
|
@@ -12850,6 +13422,8 @@ var provenance = (context, kind = context.provenanceKind ?? "host_observed") =>
|
|
|
12850
13422
|
if (task.data.status === "paused" && (input.state === "running" || input.state === "cancelling"))
|
|
12851
13423
|
return failure("invalid_transition", "paused task cannot run a worker");
|
|
12852
13424
|
const notStarted = input.dispatch === true;
|
|
13425
|
+
if (notStarted && entry.data.state === "stopped" && input.session === null)
|
|
13426
|
+
return success(task.data.revision, workspace.data.revision, entry);
|
|
12853
13427
|
if (notStarted && (input.state !== "stopped" || input.session !== null))
|
|
12854
13428
|
return failure("invalid_input", "a never-dispatched worker stops with no session");
|
|
12855
13429
|
if (notStarted && entry.data.session !== null)
|
|
@@ -12881,7 +13455,7 @@ var provenance = (context, kind = context.provenanceKind ?? "host_observed") =>
|
|
|
12881
13455
|
return success(task.data.revision, workspace.data.revision, entry);
|
|
12882
13456
|
const changed = input.store.mutateTaskAndWorkspace({
|
|
12883
13457
|
taskId: task.data.id,
|
|
12884
|
-
|
|
13458
|
+
expectedRevision: input.expectedRevision,
|
|
12885
13459
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
12886
13460
|
now: input.now,
|
|
12887
13461
|
workspace: (current, mutation) => success(mutation.revision, mutation.revision, {
|
|
@@ -12920,90 +13494,22 @@ var provenance = (context, kind = context.provenanceKind ?? "host_observed") =>
|
|
|
12920
13494
|
workerId: null,
|
|
12921
13495
|
receipts: []
|
|
12922
13496
|
}), portableTask = (task) => {
|
|
12923
|
-
const
|
|
12924
|
-
|
|
12925
|
-
|
|
12926
|
-
|
|
12927
|
-
|
|
12928
|
-
|
|
12929
|
-
|
|
12930
|
-
|
|
12931
|
-
|
|
12932
|
-
|
|
12933
|
-
|
|
12934
|
-
|
|
12935
|
-
|
|
12936
|
-
];
|
|
12937
|
-
for (const entry of entries)
|
|
12938
|
-
entry.provenance = portableProvenance(entry.provenance);
|
|
12939
|
-
for (const worker of clone.workers)
|
|
12940
|
-
worker.data.session = null;
|
|
12941
|
-
clone.intent = {
|
|
12942
|
-
...clone.intent,
|
|
12943
|
-
data: { ...clone.intent.data, authorityRefs: portableRefs(clone.intent.data.authorityRefs) }
|
|
13497
|
+
const stripped = rewriteRecordRefs(task, taskRecordSchema, portableRef);
|
|
13498
|
+
if (typeof stripped !== "object" || stripped === null || Array.isArray(stripped))
|
|
13499
|
+
throw new TypeError("portable task rewrite produced a non-record");
|
|
13500
|
+
const clone = structuredClone(stripped);
|
|
13501
|
+
const demote = (facts, signals, consequences) => {
|
|
13502
|
+
for (const fact of facts)
|
|
13503
|
+
Object.assign(fact, portableFact(fact));
|
|
13504
|
+
for (const name of Object.keys(signals)) {
|
|
13505
|
+
const signal = signals[name];
|
|
13506
|
+
Object.assign(signal, portableSignal(signal));
|
|
13507
|
+
}
|
|
13508
|
+
for (const consequence of consequences)
|
|
13509
|
+
Object.assign(consequence.fact, portableFact(consequence.fact));
|
|
12944
13510
|
};
|
|
12945
|
-
|
|
12946
|
-
|
|
12947
|
-
return source ? [
|
|
12948
|
-
{
|
|
12949
|
-
...constraint,
|
|
12950
|
-
source,
|
|
12951
|
-
requires: constraint.requires.map((requirement) => ({
|
|
12952
|
-
...requirement,
|
|
12953
|
-
refs: portableRefs(requirement.refs)
|
|
12954
|
-
}))
|
|
12955
|
-
}
|
|
12956
|
-
] : [];
|
|
12957
|
-
});
|
|
12958
|
-
clone.assessments = clone.assessments.map((entry) => ({
|
|
12959
|
-
...entry,
|
|
12960
|
-
data: {
|
|
12961
|
-
...entry.data,
|
|
12962
|
-
facts: entry.data.facts.map(portableFact),
|
|
12963
|
-
signals: Object.fromEntries(Object.entries(entry.data.signals).map(([name, signal]) => [name, portableSignal(signal)])),
|
|
12964
|
-
consequences: entry.data.consequences.map((consequence) => ({
|
|
12965
|
-
...consequence,
|
|
12966
|
-
fact: portableFact(consequence.fact)
|
|
12967
|
-
})),
|
|
12968
|
-
verification: entry.data.verification.map((verification) => ({
|
|
12969
|
-
...verification,
|
|
12970
|
-
availableChecks: portableRefs(verification.availableChecks)
|
|
12971
|
-
}))
|
|
12972
|
-
}
|
|
12973
|
-
}));
|
|
12974
|
-
clone.progress = {
|
|
12975
|
-
...clone.progress,
|
|
12976
|
-
blockers: clone.progress.blockers.map((blocker) => ({
|
|
12977
|
-
...blocker,
|
|
12978
|
-
refs: portableRefs(blocker.refs)
|
|
12979
|
-
}))
|
|
12980
|
-
};
|
|
12981
|
-
clone.evidence = clone.evidence.map((entry) => ({
|
|
12982
|
-
...entry,
|
|
12983
|
-
data: {
|
|
12984
|
-
...entry.data,
|
|
12985
|
-
refs: portableRefs(entry.data.refs),
|
|
12986
|
-
reviewContext: portableRef(entry.data.reviewContext)
|
|
12987
|
-
}
|
|
12988
|
-
}));
|
|
12989
|
-
clone.decisions = clone.decisions.map((entry) => ({
|
|
12990
|
-
...entry,
|
|
12991
|
-
data: {
|
|
12992
|
-
...entry.data,
|
|
12993
|
-
consumption: entry.data.consumption ? portableRef(entry.data.consumption.actionRef) ? {
|
|
12994
|
-
...entry.data.consumption,
|
|
12995
|
-
actionRef: portableRef(entry.data.consumption.actionRef)
|
|
12996
|
-
} : null : null,
|
|
12997
|
-
binding: {
|
|
12998
|
-
...entry.data.binding,
|
|
12999
|
-
contentRefs: portableRefs(entry.data.binding.contentRefs)
|
|
13000
|
-
}
|
|
13001
|
-
}
|
|
13002
|
-
}));
|
|
13003
|
-
clone.findings = clone.findings.map((entry) => ({
|
|
13004
|
-
...entry,
|
|
13005
|
-
data: { ...entry.data, refs: portableRefs(entry.data.refs) }
|
|
13006
|
-
}));
|
|
13511
|
+
for (const entry of clone.assessments)
|
|
13512
|
+
demote(entry.data.facts, entry.data.signals, entry.data.consequences);
|
|
13007
13513
|
clone.candidates = [];
|
|
13008
13514
|
return clone;
|
|
13009
13515
|
}, importedTask = (source, bundle, destinationWorkspaceId, context, timestamp) => {
|
|
@@ -13027,15 +13533,13 @@ var provenance = (context, kind = context.provenanceKind ?? "host_observed") =>
|
|
|
13027
13533
|
allocate(entry.id);
|
|
13028
13534
|
const fresh = (id) => allocate(id);
|
|
13029
13535
|
const provenance = importedProvenance(context);
|
|
13536
|
+
const remap = (ref) => mapRef(ref, ids);
|
|
13030
13537
|
const intent = {
|
|
13031
13538
|
...source.intent,
|
|
13032
13539
|
id: fresh(source.intent.id),
|
|
13033
13540
|
recordedAt: timestamp,
|
|
13034
13541
|
provenance,
|
|
13035
|
-
data:
|
|
13036
|
-
...source.intent.data,
|
|
13037
|
-
authorityRefs: source.intent.data.authorityRefs.map((ref) => mapRef(ref, ids))
|
|
13038
|
-
}
|
|
13542
|
+
data: rewriteRecordRefs(source.intent.data, intentSchema, remap)
|
|
13039
13543
|
};
|
|
13040
13544
|
const assessments = source.assessments.map((entry) => {
|
|
13041
13545
|
const data = entry.data;
|
|
@@ -13044,28 +13548,7 @@ var provenance = (context, kind = context.provenanceKind ?? "host_observed") =>
|
|
|
13044
13548
|
id: fresh(entry.id),
|
|
13045
13549
|
recordedAt: timestamp,
|
|
13046
13550
|
provenance,
|
|
13047
|
-
data:
|
|
13048
|
-
...data,
|
|
13049
|
-
facts: data.facts.map((fact) => ({
|
|
13050
|
-
...fact,
|
|
13051
|
-
refs: fact.refs.map((ref) => mapRef(ref, ids))
|
|
13052
|
-
})),
|
|
13053
|
-
signals: Object.fromEntries(Object.entries(data.signals).map(([name, signal]) => [
|
|
13054
|
-
name,
|
|
13055
|
-
{ ...signal, refs: signal.refs.map((ref) => mapRef(ref, ids)) }
|
|
13056
|
-
])),
|
|
13057
|
-
consequences: data.consequences.map((consequence) => ({
|
|
13058
|
-
...consequence,
|
|
13059
|
-
fact: {
|
|
13060
|
-
...consequence.fact,
|
|
13061
|
-
refs: consequence.fact.refs.map((ref) => mapRef(ref, ids))
|
|
13062
|
-
}
|
|
13063
|
-
})),
|
|
13064
|
-
verification: data.verification.map((verification) => ({
|
|
13065
|
-
...verification,
|
|
13066
|
-
availableChecks: verification.availableChecks.map((ref) => mapRef(ref, ids))
|
|
13067
|
-
}))
|
|
13068
|
-
}
|
|
13551
|
+
data: rewriteRecordRefs(data, assessmentSchema, remap)
|
|
13069
13552
|
};
|
|
13070
13553
|
});
|
|
13071
13554
|
const evidence = source.evidence.map((entry) => ({
|
|
@@ -13073,11 +13556,7 @@ var provenance = (context, kind = context.provenanceKind ?? "host_observed") =>
|
|
|
13073
13556
|
id: fresh(entry.id),
|
|
13074
13557
|
recordedAt: timestamp,
|
|
13075
13558
|
provenance,
|
|
13076
|
-
data:
|
|
13077
|
-
...entry.data,
|
|
13078
|
-
refs: entry.data.refs.map((ref) => mapRef(ref, ids)),
|
|
13079
|
-
reviewContext: entry.data.reviewContext ? mapRef(entry.data.reviewContext, ids) : null
|
|
13080
|
-
}
|
|
13559
|
+
data: rewriteRecordRefs(entry.data, evidenceSchema, remap)
|
|
13081
13560
|
}));
|
|
13082
13561
|
const decisions = source.decisions.map((entry) => ({
|
|
13083
13562
|
...entry,
|
|
@@ -13085,21 +13564,23 @@ var provenance = (context, kind = context.provenanceKind ?? "host_observed") =>
|
|
|
13085
13564
|
recordedAt: timestamp,
|
|
13086
13565
|
provenance
|
|
13087
13566
|
}));
|
|
13088
|
-
const findings = source.findings.map((entry) =>
|
|
13089
|
-
|
|
13090
|
-
|
|
13091
|
-
|
|
13092
|
-
|
|
13093
|
-
|
|
13094
|
-
|
|
13095
|
-
|
|
13096
|
-
|
|
13097
|
-
|
|
13098
|
-
|
|
13099
|
-
|
|
13100
|
-
|
|
13101
|
-
|
|
13102
|
-
|
|
13567
|
+
const findings = source.findings.map((entry) => {
|
|
13568
|
+
const data = rewriteRecordRefs(entry.data, findingSchema, remap);
|
|
13569
|
+
return {
|
|
13570
|
+
...entry,
|
|
13571
|
+
id: fresh(entry.id),
|
|
13572
|
+
recordedAt: timestamp,
|
|
13573
|
+
provenance,
|
|
13574
|
+
data: {
|
|
13575
|
+
...data,
|
|
13576
|
+
resolution: data.resolution ? {
|
|
13577
|
+
...data.resolution,
|
|
13578
|
+
evidenceIds: data.resolution.evidenceIds.map((id) => ids.get(id) ?? id),
|
|
13579
|
+
decisionIds: data.resolution.decisionIds.map((id) => ids.get(id) ?? id)
|
|
13580
|
+
} : null
|
|
13581
|
+
}
|
|
13582
|
+
};
|
|
13583
|
+
});
|
|
13103
13584
|
const workers = source.workers.map((entry) => ({
|
|
13104
13585
|
...entry,
|
|
13105
13586
|
id: fresh(entry.id),
|
|
@@ -13142,19 +13623,21 @@ var provenance = (context, kind = context.provenanceKind ?? "host_observed") =>
|
|
|
13142
13623
|
closure: null,
|
|
13143
13624
|
assessments,
|
|
13144
13625
|
evidence,
|
|
13145
|
-
decisions: decisions.map((entry) =>
|
|
13146
|
-
|
|
13147
|
-
|
|
13148
|
-
...entry
|
|
13149
|
-
|
|
13150
|
-
...
|
|
13151
|
-
|
|
13152
|
-
|
|
13153
|
-
|
|
13154
|
-
|
|
13155
|
-
|
|
13156
|
-
|
|
13157
|
-
|
|
13626
|
+
decisions: decisions.map((entry) => {
|
|
13627
|
+
const data = rewriteRecordRefs(entry.data, decisionSchema, remap);
|
|
13628
|
+
return {
|
|
13629
|
+
...entry,
|
|
13630
|
+
data: {
|
|
13631
|
+
...data,
|
|
13632
|
+
binding: {
|
|
13633
|
+
...data.binding,
|
|
13634
|
+
taskId: "",
|
|
13635
|
+
workspaceId: destinationWorkspaceId
|
|
13636
|
+
},
|
|
13637
|
+
consumption: null
|
|
13638
|
+
}
|
|
13639
|
+
};
|
|
13640
|
+
}),
|
|
13158
13641
|
...actionProgress ? { actionProgress } : {},
|
|
13159
13642
|
findings,
|
|
13160
13643
|
workers,
|
|
@@ -13182,6 +13665,7 @@ var init_task_engine = __esm(() => {
|
|
|
13182
13665
|
init_task_contract();
|
|
13183
13666
|
init_authority();
|
|
13184
13667
|
init_policy_resolver();
|
|
13668
|
+
init_auto_approval();
|
|
13185
13669
|
init_task_context();
|
|
13186
13670
|
init_workers();
|
|
13187
13671
|
init_task_evaluation();
|
|
@@ -13196,6 +13680,10 @@ var init_core3 = __esm(() => {
|
|
|
13196
13680
|
init_task_context();
|
|
13197
13681
|
init_methods();
|
|
13198
13682
|
init_route_intent();
|
|
13683
|
+
init_branch();
|
|
13684
|
+
init_auto_approval();
|
|
13685
|
+
init_runtime_identity();
|
|
13686
|
+
init_authority();
|
|
13199
13687
|
init_authority();
|
|
13200
13688
|
init_external_action();
|
|
13201
13689
|
init_external_action();
|
|
@@ -13205,8 +13693,8 @@ var init_core3 = __esm(() => {
|
|
|
13205
13693
|
});
|
|
13206
13694
|
|
|
13207
13695
|
// packages/workit-cursor/hooks/workit-hook.ts
|
|
13208
|
-
import { existsSync as
|
|
13209
|
-
import
|
|
13696
|
+
import { existsSync as existsSync5, realpathSync as realpathSync6 } from "node:fs";
|
|
13697
|
+
import path9 from "node:path";
|
|
13210
13698
|
var hostRef = (handle) => ({ kind: "host", host: "cursor", handle }), cursorCapabilities = (availability = {}) => {
|
|
13211
13699
|
const has = (name) => availability[name] === true;
|
|
13212
13700
|
return [
|
|
@@ -13260,14 +13748,14 @@ var hostRef = (handle) => ({ kind: "host", host: "cursor", handle }), cursorCapa
|
|
|
13260
13748
|
refs: [hostRef("sessionStart"), hostRef("preCompact")]
|
|
13261
13749
|
}
|
|
13262
13750
|
];
|
|
13263
|
-
},
|
|
13264
|
-
if (!
|
|
13751
|
+
}, isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), eventNames, nonEmpty2 = (value) => typeof value === "string" && value.trim() !== "", parseCursorHookInput = (value) => {
|
|
13752
|
+
if (!isRecord2(value) || !eventNames.has(value.hook_event_name))
|
|
13265
13753
|
return { ok: false, error: "hook_event_name is required" };
|
|
13266
13754
|
const roots = value.workspace_roots;
|
|
13267
13755
|
if (!Array.isArray(roots) || roots.length !== 1 || !roots.every(nonEmpty2))
|
|
13268
13756
|
return { ok: false, error: "exactly one workspace root is required" };
|
|
13269
13757
|
const root = roots[0];
|
|
13270
|
-
if (!
|
|
13758
|
+
if (!path9.isAbsolute(root) || !existsSync5(root))
|
|
13271
13759
|
return { ok: false, error: "workspace root must be an existing absolute path" };
|
|
13272
13760
|
const event = value.hook_event_name;
|
|
13273
13761
|
const conversationId = nonEmpty2(value.conversation_id) ? value.conversation_id : undefined;
|
|
@@ -13399,10 +13887,10 @@ var hostRef = (handle) => ({ kind: "host", host: "cursor", handle }), cursorCapa
|
|
|
13399
13887
|
}, handleSubagentStop = () => ({}), handleCursorHook = (raw) => {
|
|
13400
13888
|
const parsed = parseCursorHookInput(raw);
|
|
13401
13889
|
if (!parsed.ok) {
|
|
13402
|
-
return
|
|
13890
|
+
return isRecord2(raw) && raw.hook_event_name === "sessionStart" ? {} : deny(parsed.error);
|
|
13403
13891
|
}
|
|
13404
13892
|
const input = parsed.data;
|
|
13405
|
-
const root =
|
|
13893
|
+
const root = realpathSync6(input.workspace_roots[0]);
|
|
13406
13894
|
if (input.hook_event_name === "sessionStart") {
|
|
13407
13895
|
const actor = input.session_id ?? input.conversation_id;
|
|
13408
13896
|
let compact = "";
|
|
@@ -13443,7 +13931,7 @@ ${invariantBootstrap()}${compact}
|
|
|
13443
13931
|
const output = handleCursorHook(input);
|
|
13444
13932
|
process.stdout.write(`${JSON.stringify(output)}
|
|
13445
13933
|
`);
|
|
13446
|
-
const event =
|
|
13934
|
+
const event = isRecord2(input) ? input.hook_event_name : undefined;
|
|
13447
13935
|
if (output.permission === "deny" && ["preToolUse", "beforeShellExecution", "subagentStart"].includes(String(event)))
|
|
13448
13936
|
process.exitCode = 2;
|
|
13449
13937
|
} catch {
|