@brainervirus/workit-pi 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/dist/worker.js +15 -4
- package/dist/workit.js +266 -78
- package/package.json +1 -1
package/dist/worker.js
CHANGED
|
@@ -7602,6 +7602,7 @@ var workerSchema = object({
|
|
|
7602
7602
|
assignment: assignmentSchema,
|
|
7603
7603
|
state: _enum(["assigned", "dispatching", "running", "cancelling", "stopped", "unknown"]),
|
|
7604
7604
|
session: refSchema.nullable(),
|
|
7605
|
+
coordinator: refSchema.optional(),
|
|
7605
7606
|
report: workerReportSchema.nullable()
|
|
7606
7607
|
}).strict();
|
|
7607
7608
|
var ownerSchema = object({
|
|
@@ -7713,6 +7714,7 @@ var taskSummarySchema = object({
|
|
|
7713
7714
|
requirements: array(requirementEvaluationSchema),
|
|
7714
7715
|
writer: workspaceRecordSchema.shape.writer
|
|
7715
7716
|
}).strict();
|
|
7717
|
+
var taskListItemSchema = taskSummarySchema.omit({ policy: true, requirements: true });
|
|
7716
7718
|
var exportBundleSchema = object({
|
|
7717
7719
|
schemaVersion: literal(1),
|
|
7718
7720
|
exportedAt: utc,
|
|
@@ -7734,11 +7736,15 @@ var taskOperations = {
|
|
|
7734
7736
|
expectedWorkspaceRevision: revision.nullable().optional(),
|
|
7735
7737
|
intent: intentSchema
|
|
7736
7738
|
}),
|
|
7737
|
-
list: operation({
|
|
7739
|
+
list: operation({
|
|
7740
|
+
action: literal("list"),
|
|
7741
|
+
status: _enum(["open", "closed", "all"]).optional(),
|
|
7742
|
+
limit: number2().int().min(1).max(50).optional()
|
|
7743
|
+
}),
|
|
7738
7744
|
inspect: operation({
|
|
7739
7745
|
action: literal("inspect"),
|
|
7740
7746
|
...taskId,
|
|
7741
|
-
view: _enum(["summary", "full"])
|
|
7747
|
+
view: _enum(["summary", "full"]).optional()
|
|
7742
7748
|
}),
|
|
7743
7749
|
revise: operation({
|
|
7744
7750
|
action: literal("revise"),
|
|
@@ -7766,7 +7772,7 @@ var taskOperations = {
|
|
|
7766
7772
|
...taskId,
|
|
7767
7773
|
...revisions,
|
|
7768
7774
|
expectedWorkspaceRevision: revision.optional(),
|
|
7769
|
-
authorityRefs: array(refSchema)
|
|
7775
|
+
authorityRefs: array(refSchema).optional()
|
|
7770
7776
|
}),
|
|
7771
7777
|
close: operation({
|
|
7772
7778
|
action: literal("close"),
|
|
@@ -8022,6 +8028,11 @@ var DEFAULTS = {
|
|
|
8022
8028
|
// packages/workit-core/src/core/authority.ts
|
|
8023
8029
|
var verifiedAuthorities = new WeakMap;
|
|
8024
8030
|
// packages/workit-core/src/core/external-action.ts
|
|
8031
|
+
var chainStepSchema = union([
|
|
8032
|
+
string2().min(1),
|
|
8033
|
+
object({ branch: string2().min(1) }).strict(),
|
|
8034
|
+
object({ pr: literal(true) }).strict()
|
|
8035
|
+
]);
|
|
8025
8036
|
var externalActionSchema = discriminatedUnion("operation", [
|
|
8026
8037
|
object({
|
|
8027
8038
|
operation: literal("git.branch_setup"),
|
|
@@ -8036,7 +8047,7 @@ var externalActionSchema = discriminatedUnion("operation", [
|
|
|
8036
8047
|
operation: literal("git.commit"),
|
|
8037
8048
|
payload: object({
|
|
8038
8049
|
message: string2().min(1).optional(),
|
|
8039
|
-
plan_steps: array(
|
|
8050
|
+
plan_steps: array(chainStepSchema).min(1).max(32).optional(),
|
|
8040
8051
|
plan_branch: string2().min(1).optional()
|
|
8041
8052
|
}).strict()
|
|
8042
8053
|
}).strict(),
|
package/dist/workit.js
CHANGED
|
@@ -7949,6 +7949,7 @@ var workerSchema = object({
|
|
|
7949
7949
|
assignment: assignmentSchema,
|
|
7950
7950
|
state: _enum(["assigned", "dispatching", "running", "cancelling", "stopped", "unknown"]),
|
|
7951
7951
|
session: refSchema.nullable(),
|
|
7952
|
+
coordinator: refSchema.optional(),
|
|
7952
7953
|
report: workerReportSchema.nullable()
|
|
7953
7954
|
}).strict();
|
|
7954
7955
|
var ownerSchema = object({
|
|
@@ -8060,6 +8061,7 @@ var taskSummarySchema = object({
|
|
|
8060
8061
|
requirements: array(requirementEvaluationSchema),
|
|
8061
8062
|
writer: workspaceRecordSchema.shape.writer
|
|
8062
8063
|
}).strict();
|
|
8064
|
+
var taskListItemSchema = taskSummarySchema.omit({ policy: true, requirements: true });
|
|
8063
8065
|
var exportBundleSchema = object({
|
|
8064
8066
|
schemaVersion: literal(1),
|
|
8065
8067
|
exportedAt: utc,
|
|
@@ -8081,11 +8083,15 @@ var taskOperations = {
|
|
|
8081
8083
|
expectedWorkspaceRevision: revision.nullable().optional(),
|
|
8082
8084
|
intent: intentSchema
|
|
8083
8085
|
}),
|
|
8084
|
-
list: operation({
|
|
8086
|
+
list: operation({
|
|
8087
|
+
action: literal("list"),
|
|
8088
|
+
status: _enum(["open", "closed", "all"]).optional(),
|
|
8089
|
+
limit: number2().int().min(1).max(50).optional()
|
|
8090
|
+
}),
|
|
8085
8091
|
inspect: operation({
|
|
8086
8092
|
action: literal("inspect"),
|
|
8087
8093
|
...taskId,
|
|
8088
|
-
view: _enum(["summary", "full"])
|
|
8094
|
+
view: _enum(["summary", "full"]).optional()
|
|
8089
8095
|
}),
|
|
8090
8096
|
revise: operation({
|
|
8091
8097
|
action: literal("revise"),
|
|
@@ -8113,7 +8119,7 @@ var taskOperations = {
|
|
|
8113
8119
|
...taskId,
|
|
8114
8120
|
...revisions,
|
|
8115
8121
|
expectedWorkspaceRevision: revision.optional(),
|
|
8116
|
-
authorityRefs: array(refSchema)
|
|
8122
|
+
authorityRefs: array(refSchema).optional()
|
|
8117
8123
|
}),
|
|
8118
8124
|
close: operation({
|
|
8119
8125
|
action: literal("close"),
|
|
@@ -9946,6 +9952,10 @@ var validPath = (value) => typeof value === "string" && value.length > 0 && ![..
|
|
|
9946
9952
|
}) && !value.split(/[\\/]/).includes("..");
|
|
9947
9953
|
var callerValue = (value) => ({ host: value.host, actor: value.actor });
|
|
9948
9954
|
var workerIdOf = (value) => value.workerId ?? null;
|
|
9955
|
+
var currentWriterOwnsTask = (task, workspace, caller) => {
|
|
9956
|
+
const owner = workspace.writer?.owner;
|
|
9957
|
+
return task.status === "active" && task.workspaceId === workspace.id && owner?.taskId === task.id && owner.session.host === caller.host && owner.session.handle === caller.actor;
|
|
9958
|
+
};
|
|
9949
9959
|
function assertProductWriteAllowed(input) {
|
|
9950
9960
|
if (!Array.isArray(input.paths) || input.paths.some((path) => !validPath(path)))
|
|
9951
9961
|
return failure("invalid_input", "invalid product write path");
|
|
@@ -10950,9 +10960,11 @@ var standingReceiptFor = (root, workspace, cls, dir) => {
|
|
|
10950
10960
|
var verifyStandingApproval = (root, task, caller, binding) => {
|
|
10951
10961
|
if (task.status !== "active")
|
|
10952
10962
|
return failure("invalid_transition", "only active tasks can authorize actions");
|
|
10953
|
-
const
|
|
10954
|
-
if (!
|
|
10955
|
-
return
|
|
10963
|
+
const workspace = new TaskStore(root).readWorkspace();
|
|
10964
|
+
if (!workspace.ok)
|
|
10965
|
+
return workspace;
|
|
10966
|
+
if (!workspace.data || !currentWriterOwnsTask(task, workspace.data, caller))
|
|
10967
|
+
return failure("permission_denied", "standing approval requires the current writer session");
|
|
10956
10968
|
const standing = binding.standing;
|
|
10957
10969
|
if (!standing || typeof standing.workspace !== "string" || typeof standing.class !== "string")
|
|
10958
10970
|
return failure("invalid_input", "standing approval needs a workspace and class");
|
|
@@ -11000,14 +11012,14 @@ var standingAutoApplies = (store, host, actor, operation) => {
|
|
|
11000
11012
|
const workspace = store.readWorkspace();
|
|
11001
11013
|
if (!listed.ok || !workspace.ok || !workspace.data)
|
|
11002
11014
|
return null;
|
|
11003
|
-
const
|
|
11004
|
-
if (
|
|
11015
|
+
const task = listed.data.find((task) => currentWriterOwnsTask(task, workspace.data, { host, actor }));
|
|
11016
|
+
if (!task)
|
|
11005
11017
|
return null;
|
|
11006
11018
|
const resolved = resolveAutoApproval(store.root);
|
|
11007
11019
|
if (resolved.status !== "on")
|
|
11008
11020
|
return null;
|
|
11009
11021
|
return {
|
|
11010
|
-
task
|
|
11022
|
+
task,
|
|
11011
11023
|
workspaceId: workspace.data.id,
|
|
11012
11024
|
workspaceRevision: workspace.data.revision,
|
|
11013
11025
|
cls,
|
|
@@ -11266,6 +11278,20 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
|
11266
11278
|
};
|
|
11267
11279
|
var verifyContentRefs = (store, binding) => verifyDecisionContentAtRoot(store.root, binding);
|
|
11268
11280
|
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);
|
|
11281
|
+
var approvalAuthorityMatches = (store, task, approval, current) => {
|
|
11282
|
+
if (approval.kind !== "host_observed" || approval.receipts.length === 0)
|
|
11283
|
+
return false;
|
|
11284
|
+
if (provenanceMatches(approval, current))
|
|
11285
|
+
return true;
|
|
11286
|
+
const session = current.kind === "host_observed" ? current.session : null;
|
|
11287
|
+
if (!session || session.kind !== "host")
|
|
11288
|
+
return false;
|
|
11289
|
+
const workspace = store.readWorkspace();
|
|
11290
|
+
return workspace.ok && workspace.data !== null && currentWriterOwnsTask(task, workspace.data, {
|
|
11291
|
+
host: session.host,
|
|
11292
|
+
actor: session.handle
|
|
11293
|
+
});
|
|
11294
|
+
};
|
|
11269
11295
|
var validateAction = (store, task, workspaceId, input, authority) => {
|
|
11270
11296
|
if (task.status !== "active")
|
|
11271
11297
|
return failure("invalid_transition", "only active tasks can authorize actions");
|
|
@@ -11291,7 +11317,7 @@ var validateAction = (store, task, workspaceId, input, authority) => {
|
|
|
11291
11317
|
return failure("permission_denied", "native reservation authority does not match decision");
|
|
11292
11318
|
if (decision.response !== "approved")
|
|
11293
11319
|
return failure("permission_denied", "decision was rejected");
|
|
11294
|
-
if (
|
|
11320
|
+
if (!approvalAuthorityMatches(store, task, entry.provenance, authority.provenance))
|
|
11295
11321
|
return failure("permission_denied", "action approval lacks native receipt assurance");
|
|
11296
11322
|
const receipts = entry.provenance.receipts;
|
|
11297
11323
|
const standing = receipts.filter((receipt) => typeof receipt === "object" && receipt !== null && receipt.kind === "standing");
|
|
@@ -11458,7 +11484,7 @@ function settleAction(input) {
|
|
|
11458
11484
|
return failure("not_found", "decision not found");
|
|
11459
11485
|
if (!authorityDecisionMatches(authority, entry.data))
|
|
11460
11486
|
return failure("permission_denied", "native settlement authority does not match decision");
|
|
11461
|
-
if (!
|
|
11487
|
+
if (!approvalAuthorityMatches(input.store, current, entry.provenance, authority.provenance))
|
|
11462
11488
|
return failure("permission_denied", "native settlement caller does not match approval");
|
|
11463
11489
|
if (entry.data.purpose !== "action" || entry.data.digest !== decisionDigest(entry.data))
|
|
11464
11490
|
return failure("permission_denied", "decision binding is invalid");
|
|
@@ -11557,7 +11583,7 @@ function reconcileAction(input) {
|
|
|
11557
11583
|
const entry = current.decisions.find((candidate) => candidate.id === input.decisionId);
|
|
11558
11584
|
if (!entry || entry.data.purpose !== "action")
|
|
11559
11585
|
return failure("not_found", "decision not found");
|
|
11560
|
-
if (!authorityDecisionMatches(authority, entry.data) || !
|
|
11586
|
+
if (!authorityDecisionMatches(authority, entry.data) || !approvalAuthorityMatches(input.store, current, entry.provenance, authority.provenance))
|
|
11561
11587
|
return failure("permission_denied", "native reconciliation caller does not match approval");
|
|
11562
11588
|
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)
|
|
11563
11589
|
return failure("permission_denied", "reconciliation decision binding is invalid");
|
|
@@ -11894,7 +11920,7 @@ var resolveBinding = (evidence, pin, currentCandidateId) => {
|
|
|
11894
11920
|
candidateId: evidence.candidateId ?? fallback
|
|
11895
11921
|
};
|
|
11896
11922
|
};
|
|
11897
|
-
var findingVerificationPasses = (findingCandidateId, evidence) => evidence.status === "passed" && (evidence.kind === "check" || evidence.kind === "review") && ((findingCandidateId ?? null) === null || evidence.candidateId
|
|
11923
|
+
var findingVerificationPasses = (findingCandidateId, evidence) => evidence.status === "passed" && (evidence.kind === "check" || evidence.kind === "review") && ((findingCandidateId ?? null) === null || evidence.candidateId != null);
|
|
11898
11924
|
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")));
|
|
11899
11925
|
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 }));
|
|
11900
11926
|
var evidenceMatchesRequirement = (kind, evidenceKind, dimension, ruleId) => {
|
|
@@ -12364,6 +12390,11 @@ function shouldDenyShellRoute(dir, command) {
|
|
|
12364
12390
|
}
|
|
12365
12391
|
// packages/workit-core/src/core/external-action.ts
|
|
12366
12392
|
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
12393
|
+
var chainStepSchema = union([
|
|
12394
|
+
string2().min(1),
|
|
12395
|
+
object({ branch: string2().min(1) }).strict(),
|
|
12396
|
+
object({ pr: literal(true) }).strict()
|
|
12397
|
+
]);
|
|
12367
12398
|
var externalActionSchema = discriminatedUnion("operation", [
|
|
12368
12399
|
object({
|
|
12369
12400
|
operation: literal("git.branch_setup"),
|
|
@@ -12378,7 +12409,7 @@ var externalActionSchema = discriminatedUnion("operation", [
|
|
|
12378
12409
|
operation: literal("git.commit"),
|
|
12379
12410
|
payload: object({
|
|
12380
12411
|
message: string2().min(1).optional(),
|
|
12381
|
-
plan_steps: array(
|
|
12412
|
+
plan_steps: array(chainStepSchema).min(1).max(32).optional(),
|
|
12382
12413
|
plan_branch: string2().min(1).optional()
|
|
12383
12414
|
}).strict()
|
|
12384
12415
|
}).strict(),
|
|
@@ -12476,7 +12507,13 @@ var externalActionDescriptor = (operation, payload) => canonicalJson({
|
|
|
12476
12507
|
payload,
|
|
12477
12508
|
...payload && typeof payload === "object" && !Array.isArray(payload) && "resolved" in payload && payload.resolved && typeof payload.resolved === "object" && Array.isArray(payload.resolved.steps) ? { steps: payload.resolved.steps } : {}
|
|
12478
12509
|
});
|
|
12479
|
-
var
|
|
12510
|
+
var planReservationLength = (operation, descriptorPayload) => {
|
|
12511
|
+
if (operation !== "git.commit")
|
|
12512
|
+
return null;
|
|
12513
|
+
const payload = descriptorPayload ?? {};
|
|
12514
|
+
return Array.isArray(payload.plan_steps) ? payload.plan_steps.length : null;
|
|
12515
|
+
};
|
|
12516
|
+
var externalActionHelp = "Fixed actions: git.branch_setup {action?,sdd_dir?,target_branch(required unless reapply_stash; the working branch to create or switch to),stash?}; git.commit {message?; plan_steps?: (string | {branch:string} | {pr:true})[]; plan_branch?}; git.push {branch?}; hosting.pull_request {title,body?,draft?,target_branch?,babysit?}; hosting.merge {target_branch?,source_branch?}; youtrack.update {issueId,markdown,minutes?}; youtrack.time {issueId,minutes,text?,dateMs?}; youtrack.meeting {issueId,minutes,text}; changelog.apply {entries?,path?,normalize_only?}; context.read {kind: git|pr|youtrack|github_issue|gitlab_issue|changelog|release|affected,range?,issueId?,issueUrl?,issueRef?,mode?,specPath?,planPath?}. context.read is read-only and needs no approval; all other operations require native approval (CLI uses a TTY; caller-unattested MCP cannot mutate).";
|
|
12480
12517
|
var externalActionRef = (host, actor, descriptor) => ({
|
|
12481
12518
|
kind: "host",
|
|
12482
12519
|
host,
|
|
@@ -12490,12 +12527,12 @@ var externalActionState = (store, host, actor, operation) => {
|
|
|
12490
12527
|
if (!workspace.data)
|
|
12491
12528
|
return failure("not_found", "workspace not found");
|
|
12492
12529
|
const candidates = listed.data.flatMap((task) => {
|
|
12493
|
-
if (task.status !== "active" || task.workspaceId !== workspace.data.id
|
|
12530
|
+
if (task.status !== "active" || task.workspaceId !== workspace.data.id)
|
|
12494
12531
|
return [];
|
|
12495
12532
|
return task.decisions.filter((entry) => entry.data.purpose === "action" && entry.data.response === "approved" && entry.data.revoked === null && entry.data.binding.approvedContent === operation).map((entry) => ({ task, entry }));
|
|
12496
12533
|
});
|
|
12497
12534
|
if (candidates.length !== 1)
|
|
12498
|
-
return failure("permission_denied", candidates.length === 0 ? "no approved action is bound to
|
|
12535
|
+
return failure("permission_denied", candidates.length === 0 ? "no approved action is bound to the active task" : "multiple approved actions match the active task");
|
|
12499
12536
|
return success(workspace.data.revision, null, { ...candidates[0], workspace: workspace.data });
|
|
12500
12537
|
};
|
|
12501
12538
|
var priorExternalAction = (store, host, actor, operation, payload) => {
|
|
@@ -12575,8 +12612,6 @@ var approvedPlanCommit = (store, host, actor, message) => {
|
|
|
12575
12612
|
const matches = listed.data.flatMap((task) => {
|
|
12576
12613
|
if (task.status !== "active" || task.workspaceId !== workspace.data.id)
|
|
12577
12614
|
return [];
|
|
12578
|
-
if (task.intent.provenance.session?.kind !== "host" || task.intent.provenance.session.host !== host || task.intent.provenance.session.handle !== actor)
|
|
12579
|
-
return [];
|
|
12580
12615
|
return task.decisions.flatMap((entry) => {
|
|
12581
12616
|
const decision = entry.data;
|
|
12582
12617
|
if (decision.purpose !== "action" || decision.response !== "approved" || decision.revoked !== null)
|
|
@@ -12618,8 +12653,6 @@ var approvedChainStep = (store, host, actor, query) => {
|
|
|
12618
12653
|
const matches = listed.data.flatMap((task) => {
|
|
12619
12654
|
if (task.status !== "active" || task.workspaceId !== workspace.data.id)
|
|
12620
12655
|
return [];
|
|
12621
|
-
if (task.intent.provenance.session?.kind !== "host" || task.intent.provenance.session.host !== host || task.intent.provenance.session.handle !== actor)
|
|
12622
|
-
return [];
|
|
12623
12656
|
return task.decisions.flatMap((entry) => {
|
|
12624
12657
|
const decision = entry.data;
|
|
12625
12658
|
if (decision.purpose !== "action" || decision.response !== "approved" || decision.revoked !== null)
|
|
@@ -12776,7 +12809,7 @@ var branchSetupIntent = (descriptor) => {
|
|
|
12776
12809
|
var approvedBranchSetupIntent = (store, host, actor, operation) => {
|
|
12777
12810
|
const current = branchSetupIntent(operation);
|
|
12778
12811
|
if (!current)
|
|
12779
|
-
return failure("permission_denied", "no approved action is bound to
|
|
12812
|
+
return failure("permission_denied", "no approved action is bound to the active task");
|
|
12780
12813
|
const listed = store.listTasks();
|
|
12781
12814
|
const workspace = store.readWorkspace();
|
|
12782
12815
|
if (!listed.ok || !workspace.ok)
|
|
@@ -12784,7 +12817,7 @@ var approvedBranchSetupIntent = (store, host, actor, operation) => {
|
|
|
12784
12817
|
if (!workspace.data)
|
|
12785
12818
|
return failure("not_found", "workspace not found");
|
|
12786
12819
|
const candidates = listed.data.flatMap((task) => {
|
|
12787
|
-
if (task.status !== "active" || task.workspaceId !== workspace.data.id
|
|
12820
|
+
if (task.status !== "active" || task.workspaceId !== workspace.data.id)
|
|
12788
12821
|
return [];
|
|
12789
12822
|
return task.decisions.filter((entry) => entry.data.purpose === "action" && entry.data.response === "approved" && entry.data.revoked === null).map((entry) => ({ task, entry }));
|
|
12790
12823
|
});
|
|
@@ -12793,7 +12826,7 @@ var approvedBranchSetupIntent = (store, host, actor, operation) => {
|
|
|
12793
12826
|
return intent !== null && intent.target === current.target && intent.base === current.base && intent.remoteBase === current.remoteBase && intent.targetExists === current.targetExists;
|
|
12794
12827
|
});
|
|
12795
12828
|
if (matches.length !== 1)
|
|
12796
|
-
return failure("permission_denied", matches.length === 0 ? "no approved action is bound to
|
|
12829
|
+
return failure("permission_denied", matches.length === 0 ? "no approved action is bound to the active task" : "multiple approved actions match the active task");
|
|
12797
12830
|
return success(workspace.data.revision, null, { ...matches[0], workspace: workspace.data });
|
|
12798
12831
|
};
|
|
12799
12832
|
var priorResolvedDrift = (store, host, actor, operation, payload, currentResolved) => {
|
|
@@ -13350,6 +13383,7 @@ var verifyNativeWorker = (verifier, input, binding) => {
|
|
|
13350
13383
|
expectedWorkspaceRevision: expected.expectedWorkspaceRevision,
|
|
13351
13384
|
state: expected.state,
|
|
13352
13385
|
session: expected.session,
|
|
13386
|
+
report: expected.report ?? null,
|
|
13353
13387
|
provenance: result.data,
|
|
13354
13388
|
...binding
|
|
13355
13389
|
});
|
|
@@ -13360,7 +13394,7 @@ var takeWorker = (token, binding, expected) => {
|
|
|
13360
13394
|
verifiedWorkers.delete(token);
|
|
13361
13395
|
if (!value)
|
|
13362
13396
|
return null;
|
|
13363
|
-
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))
|
|
13397
|
+
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))
|
|
13364
13398
|
return null;
|
|
13365
13399
|
return value;
|
|
13366
13400
|
};
|
|
@@ -13401,6 +13435,8 @@ var applyWorkerLifecycle = (input) => {
|
|
|
13401
13435
|
return failure("invalid_transition", "worker already has an observed session");
|
|
13402
13436
|
if (notStarted && !["dispatching", "cancelling"].includes(entry.data.state))
|
|
13403
13437
|
return failure("invalid_transition", "worker launch was not claimed");
|
|
13438
|
+
if (input.report && input.state !== "stopped")
|
|
13439
|
+
return failure("invalid_input", "worker reports require a stopped worker");
|
|
13404
13440
|
if ((input.state === "running" || input.state === "cancelling" || input.state === "stopped" && !notStarted) && !input.session)
|
|
13405
13441
|
return failure("invalid_input", "running worker observations require a session");
|
|
13406
13442
|
if (entry.data.session && !sameValue(entry.data.session, input.session))
|
|
@@ -13417,12 +13453,13 @@ var applyWorkerLifecycle = (input) => {
|
|
|
13417
13453
|
data: {
|
|
13418
13454
|
...entry.data,
|
|
13419
13455
|
state: terminalCancel ? "cancelling" : input.state,
|
|
13420
|
-
session: input.session
|
|
13456
|
+
session: input.session,
|
|
13457
|
+
report: entry.data.report ?? input.report ?? null
|
|
13421
13458
|
}
|
|
13422
13459
|
};
|
|
13423
13460
|
const shouldClear = input.state === "stopped" && workspace.data.writer !== null && workspace.data.writer.owner.taskId === task.data.id && workspace.data.writer.owner.workerId === input.workerId;
|
|
13424
13461
|
const shouldUncertain = input.state === "unknown" && workspace.data.writer !== null && workspace.data.writer.owner.taskId === task.data.id && workspace.data.writer.owner.workerId === input.workerId;
|
|
13425
|
-
if (entry.data.state === nextEntry.data.state && sameValue(entry.data.session, input.session) && !shouldClear && !shouldUncertain)
|
|
13462
|
+
if (entry.data.state === nextEntry.data.state && sameValue(entry.data.session, input.session) && sameValue(entry.data.report, nextEntry.data.report) && !shouldClear && !shouldUncertain)
|
|
13426
13463
|
return success(task.data.revision, workspace.data.revision, entry);
|
|
13427
13464
|
const changed = input.store.mutateTaskAndWorkspace({
|
|
13428
13465
|
taskId: task.data.id,
|
|
@@ -13840,14 +13877,30 @@ class WorkitCore {
|
|
|
13840
13877
|
const listed = this.store.listTasks();
|
|
13841
13878
|
if (!listed.ok)
|
|
13842
13879
|
return listed;
|
|
13843
|
-
const
|
|
13844
|
-
|
|
13845
|
-
|
|
13846
|
-
|
|
13847
|
-
|
|
13848
|
-
|
|
13849
|
-
|
|
13850
|
-
|
|
13880
|
+
const status = input.status ?? "open";
|
|
13881
|
+
const limit = input.limit ?? 20;
|
|
13882
|
+
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);
|
|
13883
|
+
if (selected.length === 0)
|
|
13884
|
+
return success(null, null, []);
|
|
13885
|
+
const workspace = this.store.readWorkspace();
|
|
13886
|
+
if (!workspace.ok)
|
|
13887
|
+
return workspace;
|
|
13888
|
+
if (!workspace.data)
|
|
13889
|
+
return failure("not_found", "workspace not found");
|
|
13890
|
+
const tasks = selected.map((task) => ({
|
|
13891
|
+
id: task.id,
|
|
13892
|
+
revision: task.revision,
|
|
13893
|
+
workspaceRevision: workspace.data.revision,
|
|
13894
|
+
createdAt: task.createdAt,
|
|
13895
|
+
updatedAt: task.updatedAt,
|
|
13896
|
+
runtime: task.runtime ?? null,
|
|
13897
|
+
objective: task.intent.data.objective,
|
|
13898
|
+
status: task.status,
|
|
13899
|
+
closure: task.closure,
|
|
13900
|
+
progress: task.progress,
|
|
13901
|
+
writer: task.status === "closed" ? null : workspace.data.writer
|
|
13902
|
+
}));
|
|
13903
|
+
return success(null, null, tasks);
|
|
13851
13904
|
}
|
|
13852
13905
|
case "inspect": {
|
|
13853
13906
|
const task = this.store.readTask(input.taskId);
|
|
@@ -13856,7 +13909,7 @@ class WorkitCore {
|
|
|
13856
13909
|
const helper = this.helperTaskGuard(task.data, true);
|
|
13857
13910
|
if (!helper.ok)
|
|
13858
13911
|
return helper;
|
|
13859
|
-
if (input.view === "summary")
|
|
13912
|
+
if ((input.view ?? "summary") === "summary")
|
|
13860
13913
|
return this.summary(task.data);
|
|
13861
13914
|
return this.view(task.data);
|
|
13862
13915
|
}
|
|
@@ -14200,7 +14253,7 @@ class WorkitCore {
|
|
|
14200
14253
|
const evaluations = evaluateEvidence(task.data, current.data);
|
|
14201
14254
|
const verified = evidence.some((entry) => {
|
|
14202
14255
|
const evaluation = evaluations.find((item) => item.evidenceId === entry.id);
|
|
14203
|
-
return findingVerificationPasses(finding.data.candidateId, {
|
|
14256
|
+
return entry.recordedAt >= finding.recordedAt && findingVerificationPasses(finding.data.candidateId, {
|
|
14204
14257
|
kind: entry.data.kind,
|
|
14205
14258
|
candidateId: entry.data.candidateId,
|
|
14206
14259
|
status: evaluation?.status ?? "missing"
|
|
@@ -14280,7 +14333,18 @@ class WorkitCore {
|
|
|
14280
14333
|
if (task.data.status !== "active")
|
|
14281
14334
|
return failure("invalid_transition", "paused or closed tasks cannot assign workers");
|
|
14282
14335
|
if (!bindingCovers(task.data.intent.data.scope, input.assignment.scope))
|
|
14283
|
-
return failure("permission_denied", "worker assignment is outside task scope"
|
|
14336
|
+
return failure("permission_denied", "worker assignment is outside task scope", {
|
|
14337
|
+
fields: [
|
|
14338
|
+
{
|
|
14339
|
+
path: "assignment.scope",
|
|
14340
|
+
reason: `paths ${JSON.stringify(input.assignment.scope.paths)} must be contained by lead paths ${JSON.stringify(task.data.intent.data.scope.paths)}`
|
|
14341
|
+
},
|
|
14342
|
+
{
|
|
14343
|
+
path: "task.intent.scope",
|
|
14344
|
+
reason: `lead paths are ${JSON.stringify(task.data.intent.data.scope.paths)}`
|
|
14345
|
+
}
|
|
14346
|
+
]
|
|
14347
|
+
});
|
|
14284
14348
|
if (input.assignment.decisionIds.some((id) => !task.data.decisions.some((entry) => entry.id === id)))
|
|
14285
14349
|
return failure("invalid_input", "worker assignment references an unknown decision");
|
|
14286
14350
|
if (input.assignment.requirementIds.some((id) => !task.data.policy?.requirements.some((requirement) => requirement.id === id)))
|
|
@@ -14487,7 +14551,15 @@ class WorkitCore {
|
|
|
14487
14551
|
...item,
|
|
14488
14552
|
recordedAt: mutation.now,
|
|
14489
14553
|
provenance: attested.data,
|
|
14490
|
-
data: {
|
|
14554
|
+
data: {
|
|
14555
|
+
...item.data,
|
|
14556
|
+
state: "dispatching",
|
|
14557
|
+
coordinator: {
|
|
14558
|
+
kind: "host",
|
|
14559
|
+
host: this.context.caller.host,
|
|
14560
|
+
handle: this.context.caller.actor
|
|
14561
|
+
}
|
|
14562
|
+
}
|
|
14491
14563
|
} : item)
|
|
14492
14564
|
});
|
|
14493
14565
|
}
|
|
@@ -14563,6 +14635,7 @@ class WorkitCore {
|
|
|
14563
14635
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
14564
14636
|
state: "stopped",
|
|
14565
14637
|
session: null,
|
|
14638
|
+
report: null,
|
|
14566
14639
|
provenance: attested.data,
|
|
14567
14640
|
owner: this.authorityOwner,
|
|
14568
14641
|
store: this.store,
|
|
@@ -14870,10 +14943,12 @@ class WorkitCore {
|
|
|
14870
14943
|
return workspace;
|
|
14871
14944
|
if (!workspace.data)
|
|
14872
14945
|
return failure("not_found", "workspace not found");
|
|
14873
|
-
const
|
|
14946
|
+
const historical = task.status === "closed" ? task.candidates.at(-1) : undefined;
|
|
14947
|
+
const current = historical ? success(null, null, historical) : captureCandidate(this.store.root, task.intent.data.scope, environment());
|
|
14874
14948
|
if (!current.ok)
|
|
14875
14949
|
return current;
|
|
14876
|
-
const
|
|
14950
|
+
const evaluationWorkspace = task.status === "closed" ? { ...workspace.data, writer: null } : workspace.data;
|
|
14951
|
+
const requirements = evaluateRequirements(task, evaluationWorkspace, this.context.capabilities, current.data, this.store.root, this.context.caller);
|
|
14877
14952
|
return success(task.revision, workspace.data.revision, {
|
|
14878
14953
|
id: task.id,
|
|
14879
14954
|
revision: task.revision,
|
|
@@ -14887,7 +14962,7 @@ class WorkitCore {
|
|
|
14887
14962
|
progress: task.progress,
|
|
14888
14963
|
policy: task.policy,
|
|
14889
14964
|
requirements,
|
|
14890
|
-
writer:
|
|
14965
|
+
writer: evaluationWorkspace.writer
|
|
14891
14966
|
});
|
|
14892
14967
|
}
|
|
14893
14968
|
view(task) {
|
|
@@ -14896,13 +14971,15 @@ class WorkitCore {
|
|
|
14896
14971
|
return workspace;
|
|
14897
14972
|
if (!workspace.data)
|
|
14898
14973
|
return failure("not_found", "workspace not found");
|
|
14899
|
-
const
|
|
14974
|
+
const historical = task.status === "closed" ? task.candidates.at(-1) : undefined;
|
|
14975
|
+
const current = historical ? success(null, null, historical) : captureCandidate(this.store.root, task.intent.data.scope, environment());
|
|
14900
14976
|
if (!current.ok)
|
|
14901
14977
|
return current;
|
|
14902
|
-
const
|
|
14978
|
+
const evaluationWorkspace = task.status === "closed" ? { ...workspace.data, writer: null } : workspace.data;
|
|
14979
|
+
const requirements = evaluateRequirements(task, evaluationWorkspace, this.context.capabilities, current.data, this.store.root, this.context.caller);
|
|
14903
14980
|
return success(task.revision, workspace.data.revision, {
|
|
14904
14981
|
task,
|
|
14905
|
-
workspace:
|
|
14982
|
+
workspace: evaluationWorkspace,
|
|
14906
14983
|
capabilities: this.context.capabilities,
|
|
14907
14984
|
requirements,
|
|
14908
14985
|
evidence: evaluateEvidence(task, current.data)
|
|
@@ -14993,8 +15070,6 @@ class WorkitCore {
|
|
|
14993
15070
|
return helper;
|
|
14994
15071
|
if (status === "paused" && task.data.status !== "active" || status === "active" && task.data.status !== "paused")
|
|
14995
15072
|
return failure("invalid_transition", `cannot transition ${task.data.status} to ${status}`);
|
|
14996
|
-
if (status === "active" && input.authorityRefs.length === 0)
|
|
14997
|
-
return failure("permission_denied", "resuming a task requires authority references");
|
|
14998
15073
|
const workspace = this.store.readWorkspace();
|
|
14999
15074
|
if (!workspace.ok)
|
|
15000
15075
|
return workspace;
|
|
@@ -15024,7 +15099,7 @@ class WorkitCore {
|
|
|
15024
15099
|
return failure("needs_input", "imported task requires fresh destination evidence");
|
|
15025
15100
|
if (current.data.blockers.some((blocker) => blocker.dependentAction === "resume" || blocker.reason === "worker state requires reconciliation"))
|
|
15026
15101
|
return failure("recovery_required", "imported task requires resume reconciliation");
|
|
15027
|
-
if (!validResumeApproval(task.data, workspace.data, input.authorityRefs, this.context))
|
|
15102
|
+
if (!validResumeApproval(task.data, workspace.data, input.authorityRefs ?? [], this.context))
|
|
15028
15103
|
return failure("permission_denied", "imported resume requires native destination approval");
|
|
15029
15104
|
resumeCandidate = current.data.candidate;
|
|
15030
15105
|
}
|
|
@@ -15154,7 +15229,7 @@ class WorkitCore {
|
|
|
15154
15229
|
task: (value, context) => success(context.revision, null, {
|
|
15155
15230
|
...value,
|
|
15156
15231
|
status: "closed",
|
|
15157
|
-
candidates:
|
|
15232
|
+
candidates: taskForView.candidates,
|
|
15158
15233
|
closure: {
|
|
15159
15234
|
outcome: closure.data.outcome,
|
|
15160
15235
|
at: context.now,
|
|
@@ -17463,7 +17538,7 @@ var readExternalContext = async (root, payload) => {
|
|
|
17463
17538
|
}
|
|
17464
17539
|
}
|
|
17465
17540
|
};
|
|
17466
|
-
var localAction = (operation) => operation === "git.branch_setup" || operation === "git.commit" || operation === "git.push" || operation === "hosting.pull_request" || operation === "changelog.apply";
|
|
17541
|
+
var localAction = (operation) => operation === "git.branch_setup" || operation === "git.commit" || operation === "git.push" || operation === "hosting.pull_request" || operation === "hosting.merge" || operation === "changelog.apply";
|
|
17467
17542
|
var assertLocalExternalActionWriter = (root, caller, paths = ["."]) => {
|
|
17468
17543
|
const store = new TaskStore(root);
|
|
17469
17544
|
const listed = store.listTasks();
|
|
@@ -17474,7 +17549,7 @@ var assertLocalExternalActionWriter = (root, caller, paths = ["."]) => {
|
|
|
17474
17549
|
return workspace;
|
|
17475
17550
|
if (!workspace.data)
|
|
17476
17551
|
return failure("not_found", "workspace not found");
|
|
17477
|
-
const candidates = listed.data.filter((task) => task
|
|
17552
|
+
const candidates = listed.data.filter((task) => currentWriterOwnsTask(task, workspace.data, caller));
|
|
17478
17553
|
if (candidates.length !== 1)
|
|
17479
17554
|
return failure("permission_denied", "local external action requires exactly one active task");
|
|
17480
17555
|
const allowed = assertProductWriteAllowed({
|
|
@@ -17484,7 +17559,7 @@ var assertLocalExternalActionWriter = (root, caller, paths = ["."]) => {
|
|
|
17484
17559
|
paths,
|
|
17485
17560
|
store
|
|
17486
17561
|
});
|
|
17487
|
-
return allowed
|
|
17562
|
+
return allowed;
|
|
17488
17563
|
};
|
|
17489
17564
|
var safePath = (root, value) => {
|
|
17490
17565
|
try {
|
|
@@ -17496,13 +17571,20 @@ var safePath = (root, value) => {
|
|
|
17496
17571
|
var branchCreationBaseline = (root, target, base) => {
|
|
17497
17572
|
const exists = gitValue(root, ["rev-parse", "--verify", "--quiet", `refs/heads/${target}`]) !== null;
|
|
17498
17573
|
if (exists)
|
|
17499
|
-
return {
|
|
17574
|
+
return {
|
|
17575
|
+
base_branch: base,
|
|
17576
|
+
target_exists: true,
|
|
17577
|
+
remote_base: null,
|
|
17578
|
+
target_head: gitValue(root, ["rev-parse", `refs/heads/${target}`])
|
|
17579
|
+
};
|
|
17500
17580
|
const remote = run3(root, ["ls-remote", "origin", `refs/heads/${base}`]);
|
|
17501
17581
|
const sha = remote.exitCode === 0 ? remote.stdout.trim().split(/\s+/)[0] : "";
|
|
17582
|
+
const remoteBase = /^[0-9a-f]{40}$/.test(sha ?? "") ? sha : null;
|
|
17502
17583
|
return {
|
|
17503
17584
|
base_branch: base,
|
|
17504
17585
|
target_exists: false,
|
|
17505
|
-
remote_base:
|
|
17586
|
+
remote_base: remoteBase,
|
|
17587
|
+
target_head: remoteBase
|
|
17506
17588
|
};
|
|
17507
17589
|
};
|
|
17508
17590
|
var shortSha = (value) => typeof value === "string" && value ? value.slice(0, 8) : "unknown";
|
|
@@ -17613,7 +17695,7 @@ var actionProposalQuestion = (request, descriptorPayload) => {
|
|
|
17613
17695
|
var approvedResolvedExternalAction = (content) => {
|
|
17614
17696
|
try {
|
|
17615
17697
|
const value = JSON.parse(content);
|
|
17616
|
-
if (value.operation !== "hosting.pull_request" && value.operation !== "hosting.merge" && value.operation !== "changelog.apply" && value.operation !== "youtrack.update" && value.operation !== "youtrack.time" && value.operation !== "youtrack.meeting")
|
|
17698
|
+
if (value.operation !== "git.branch_setup" && value.operation !== "git.commit" && value.operation !== "git.push" && value.operation !== "hosting.pull_request" && value.operation !== "hosting.merge" && value.operation !== "changelog.apply" && value.operation !== "youtrack.update" && value.operation !== "youtrack.time" && value.operation !== "youtrack.meeting")
|
|
17617
17699
|
return failure("invalid_input", "approved action is not a reconciliable external action");
|
|
17618
17700
|
if (!value.payload || typeof value.payload !== "object")
|
|
17619
17701
|
return failure("invalid_input", "approved action payload is incomplete");
|
|
@@ -17624,18 +17706,24 @@ var approvedResolvedExternalAction = (content) => {
|
|
|
17624
17706
|
if (!request.ok || !resolved || typeof resolved !== "object")
|
|
17625
17707
|
return failure("invalid_input", "approved hosting target is incomplete");
|
|
17626
17708
|
const target = resolved;
|
|
17709
|
+
if (value.operation === "git.commit" && (typeof target.head !== "string" || typeof target.staged !== "string" || typeof payload.message !== "string"))
|
|
17710
|
+
return failure("invalid_input", "approved Git commit target is incomplete");
|
|
17711
|
+
if (value.operation === "git.push" && (typeof target.branch !== "string" || typeof target.commit !== "string" || typeof target.remote !== "string"))
|
|
17712
|
+
return failure("invalid_input", "approved Git push target is incomplete");
|
|
17713
|
+
if (value.operation === "git.branch_setup" && (typeof target.head !== "string" || (payload.action === "reapply_stash" ? typeof target.stash_commit !== "string" : typeof payload.target_branch !== "string" || typeof target.target_head !== "string")))
|
|
17714
|
+
return failure("invalid_input", "approved Git branch target is incomplete");
|
|
17627
17715
|
if (value.operation === "hosting.pull_request" && (typeof target.marker !== "string" || typeof target.source_branch !== "string" || typeof target.source_commit !== "string" || typeof target.remote !== "string" || typeof payload.target_branch !== "string"))
|
|
17628
17716
|
return failure("invalid_input", "approved hosting target is incomplete");
|
|
17629
17717
|
if (value.operation === "hosting.merge" && (typeof target.marker !== "string" || typeof target.source_branch !== "string" || typeof target.source_commit !== "string" || typeof target.remote !== "string" || typeof payload.target_branch !== "string"))
|
|
17630
17718
|
return failure("invalid_input", "approved hosting target is incomplete");
|
|
17631
17719
|
if (value.operation === "changelog.apply" && (typeof target.target !== "string" || typeof target.beforeDigest !== "string" || typeof target.beforeExists !== "boolean" || typeof target.afterDigest !== "string"))
|
|
17632
17720
|
return failure("invalid_input", "approved changelog target is incomplete");
|
|
17633
|
-
if (value.operation !== "hosting.pull_request" && value.operation !== "changelog.apply" && (typeof target.marker !== "string" || typeof target.baseUrl !== "string" || typeof target.issueId !== "string" || !Array.isArray(target.steps)))
|
|
17721
|
+
if (value.operation !== "git.branch_setup" && value.operation !== "git.commit" && value.operation !== "git.push" && value.operation !== "hosting.pull_request" && value.operation !== "hosting.merge" && value.operation !== "changelog.apply" && (typeof target.marker !== "string" || typeof target.baseUrl !== "string" || typeof target.issueId !== "string" || !Array.isArray(target.steps)))
|
|
17634
17722
|
return failure("invalid_input", "approved YouTrack target is incomplete");
|
|
17635
17723
|
return success(null, null, {
|
|
17636
17724
|
request: request.data,
|
|
17637
17725
|
descriptorPayload: value.payload,
|
|
17638
|
-
marker:
|
|
17726
|
+
...typeof target.marker === "string" ? { marker: target.marker } : {}
|
|
17639
17727
|
});
|
|
17640
17728
|
} catch {
|
|
17641
17729
|
return failure("invalid_input", "approved action descriptor is invalid");
|
|
@@ -17661,12 +17749,13 @@ var unknownHostingEvidence = (operation) => failure("external_outcome_unknown",
|
|
|
17661
17749
|
outcome: "unknown"
|
|
17662
17750
|
});
|
|
17663
17751
|
var readHostingAction = async (root, resolved, actionRef) => {
|
|
17664
|
-
|
|
17665
|
-
|
|
17752
|
+
const operation = resolved.request.operation;
|
|
17753
|
+
if (operation !== "hosting.pull_request" && operation !== "hosting.merge" || !resolved.marker)
|
|
17754
|
+
return unknownHostingEvidence(operation);
|
|
17666
17755
|
try {
|
|
17667
17756
|
const cfg = vcsConfig("load", root);
|
|
17668
17757
|
if (!cfg.ok || !cfg.tokenPath || !cfg.provider || cfg.provider !== "github" && cfg.provider !== "gitlab")
|
|
17669
|
-
return unknownHostingEvidence(
|
|
17758
|
+
return unknownHostingEvidence(operation);
|
|
17670
17759
|
const remote = pushRemote(root);
|
|
17671
17760
|
const repo = remote ? remoteRepo(remote) : null;
|
|
17672
17761
|
const approved = resolved.descriptorPayload;
|
|
@@ -17685,7 +17774,7 @@ var readHostingAction = async (root, resolved, actionRef) => {
|
|
|
17685
17774
|
}
|
|
17686
17775
|
})();
|
|
17687
17776
|
if (!repo || typeof branch !== "string" || typeof target !== "string" || typeof sourceCommit !== "string" || typeof approvedRemote !== "string" || !currentIdentity || currentIdentity !== approvedIdentity || repo !== remoteRepo(approvedRemote) || remoteProvider2(approvedRemote) !== null && remoteProvider2(approvedRemote) !== cfg.provider || remoteParts(approvedRemote)?.host !== apiHost)
|
|
17688
|
-
return unknownHostingEvidence(
|
|
17777
|
+
return unknownHostingEvidence(operation);
|
|
17689
17778
|
const token = fs12.readFileSync(cfg.tokenPath, "utf8").trim();
|
|
17690
17779
|
const headers = { Authorization: `Bearer ${token}`, Accept: "application/vnd.github+json" };
|
|
17691
17780
|
const url = cfg.provider === "github" ? `https://api.github.com/repos/${repo}/pulls?state=all&head=${encodeURIComponent(`${repo.split("/")[0]}:${branch}`)}&base=${encodeURIComponent(target)}&per_page=100&page=1` : `${cfg.gitlab?.apiUrl ?? "https://gitlab.com/api/v4"}/projects/${encodeURIComponent(repo)}/merge_requests?state=all&source_branch=${encodeURIComponent(branch)}&target_branch=${encodeURIComponent(target)}&per_page=100&page=1`;
|
|
@@ -17693,14 +17782,14 @@ var readHostingAction = async (root, resolved, actionRef) => {
|
|
|
17693
17782
|
headers: cfg.provider === "gitlab" ? { "PRIVATE-TOKEN": token } : headers
|
|
17694
17783
|
});
|
|
17695
17784
|
if (!response.ok)
|
|
17696
|
-
return unknownHostingEvidence(
|
|
17785
|
+
return unknownHostingEvidence(operation);
|
|
17697
17786
|
const values = await response.json();
|
|
17698
17787
|
if (!Array.isArray(values) || values.length >= 100)
|
|
17699
|
-
return unknownHostingEvidence(
|
|
17788
|
+
return unknownHostingEvidence(operation);
|
|
17700
17789
|
const records = [];
|
|
17701
17790
|
for (const item of values) {
|
|
17702
17791
|
if (!item || typeof item !== "object" || Array.isArray(item))
|
|
17703
|
-
return unknownHostingEvidence(
|
|
17792
|
+
return unknownHostingEvidence(operation);
|
|
17704
17793
|
const record = item;
|
|
17705
17794
|
const body = record.body ?? record.description;
|
|
17706
17795
|
const base = record.base;
|
|
@@ -17712,7 +17801,7 @@ var readHostingAction = async (root, resolved, actionRef) => {
|
|
|
17712
17801
|
const shaValue = cfg.provider === "github" && githubShape ? head.sha : record.sha;
|
|
17713
17802
|
const idValue = record.number ?? record.iid ?? record.id;
|
|
17714
17803
|
if (body !== undefined && body !== null && typeof body !== "string" || (cfg.provider === "github" ? !githubShape : !gitlabShape) || typeof targetValue !== "string" || typeof sourceValue !== "string" || typeof shaValue !== "string" || typeof idValue !== "string" && typeof idValue !== "number")
|
|
17715
|
-
return unknownHostingEvidence(
|
|
17804
|
+
return unknownHostingEvidence(operation);
|
|
17716
17805
|
records.push(record);
|
|
17717
17806
|
}
|
|
17718
17807
|
const matches = records.filter((item) => {
|
|
@@ -17722,15 +17811,23 @@ var readHostingAction = async (root, resolved, actionRef) => {
|
|
|
17722
17811
|
const itemTarget = cfg.provider === "github" ? base?.ref : item.target_branch;
|
|
17723
17812
|
const itemSource = cfg.provider === "github" ? head?.ref : item.source_branch;
|
|
17724
17813
|
const itemSha = cfg.provider === "github" ? head?.sha : item.sha;
|
|
17725
|
-
|
|
17814
|
+
const merged = cfg.provider === "github" ? typeof item.merged_at === "string" : item.state === "merged" || typeof item.merged_at === "string";
|
|
17815
|
+
return (operation === "hosting.merge" ? merged : body.includes(resolved.marker)) && itemTarget === target && itemSource === branch && itemSha === sourceCommit;
|
|
17726
17816
|
});
|
|
17817
|
+
if (operation === "hosting.merge" && records.some((item) => {
|
|
17818
|
+
const base = item.base;
|
|
17819
|
+
const head = item.head;
|
|
17820
|
+
return (cfg.provider === "github" ? base?.ref : item.target_branch) === target && (cfg.provider === "github" ? head?.ref : item.source_branch) === branch && (cfg.provider === "github" ? typeof item.merged_at !== "string" : item.state !== "merged" && typeof item.merged_at !== "string");
|
|
17821
|
+
}))
|
|
17822
|
+
return unknownHostingEvidence(operation);
|
|
17727
17823
|
if (matches.length !== 1)
|
|
17728
|
-
return unknownHostingEvidence(
|
|
17824
|
+
return unknownHostingEvidence(operation);
|
|
17729
17825
|
const item = matches[0];
|
|
17730
17826
|
const id = item.number ?? item.iid ?? item.id;
|
|
17731
17827
|
if (typeof id !== "string" && typeof id !== "number")
|
|
17732
|
-
return unknownHostingEvidence(
|
|
17828
|
+
return unknownHostingEvidence(operation);
|
|
17733
17829
|
const evidenceDigest = sha256({
|
|
17830
|
+
operation,
|
|
17734
17831
|
provider: cfg.provider,
|
|
17735
17832
|
id,
|
|
17736
17833
|
marker: resolved.marker,
|
|
@@ -17751,7 +17848,7 @@ var readHostingAction = async (root, resolved, actionRef) => {
|
|
|
17751
17848
|
data: { provider: cfg.provider, id }
|
|
17752
17849
|
});
|
|
17753
17850
|
} catch {
|
|
17754
|
-
return unknownHostingEvidence(
|
|
17851
|
+
return unknownHostingEvidence(operation);
|
|
17755
17852
|
}
|
|
17756
17853
|
};
|
|
17757
17854
|
var appendMarker = (text, marker) => `${text}${text ? `
|
|
@@ -17841,7 +17938,70 @@ var readYouTrackAction = async (root, resolved, actionRef) => {
|
|
|
17841
17938
|
return unknownHostingEvidence(operation);
|
|
17842
17939
|
}
|
|
17843
17940
|
};
|
|
17844
|
-
var
|
|
17941
|
+
var readLocalAction = async (root, resolved, actionRef) => {
|
|
17942
|
+
const operation = resolved.request.operation;
|
|
17943
|
+
const target = resolved.descriptorPayload.resolved;
|
|
17944
|
+
if (!target)
|
|
17945
|
+
return unknownHostingEvidence(operation);
|
|
17946
|
+
let observed = null;
|
|
17947
|
+
if (operation === "git.commit") {
|
|
17948
|
+
const commit = gitValue(root, ["rev-parse", "HEAD"]);
|
|
17949
|
+
const parent = commit ? gitValue(root, ["rev-parse", `${commit}^`]) : null;
|
|
17950
|
+
const message = commit ? gitValue(root, ["show", "-s", "--format=%B", commit]) : null;
|
|
17951
|
+
const diff = commit && parent ? gitRaw(root, ["diff", "--raw", "-z", "--no-ext-diff", "--no-textconv", parent, commit]) : null;
|
|
17952
|
+
if (commit && parent === target.head && message === resolved.request.payload.message && diff !== null && sha256(diff) === target.staged)
|
|
17953
|
+
observed = { commit, parent, message, staged: target.staged };
|
|
17954
|
+
} else if (operation === "git.push") {
|
|
17955
|
+
const branch = target.branch;
|
|
17956
|
+
const commit = target.commit;
|
|
17957
|
+
const approvedRemote = target.remote;
|
|
17958
|
+
const currentRemote = pushRemote(root);
|
|
17959
|
+
const currentIdentity = currentRemote ? credentialFreeRemote(currentRemote) : null;
|
|
17960
|
+
if (typeof branch === "string" && typeof commit === "string" && typeof approvedRemote === "string" && currentIdentity === approvedRemote) {
|
|
17961
|
+
const remote = run3(root, ["ls-remote", currentRemote, `refs/heads/${branch}`]);
|
|
17962
|
+
const remoteCommit = remote.exitCode === 0 ? remote.stdout.trim().split(/\s+/)[0] : null;
|
|
17963
|
+
if (remoteCommit === commit)
|
|
17964
|
+
observed = { branch, commit, remote: approvedRemote };
|
|
17965
|
+
}
|
|
17966
|
+
} else if (operation === "git.branch_setup") {
|
|
17967
|
+
const targetBranch = resolved.request.payload.target_branch;
|
|
17968
|
+
const sdd = resolved.request.payload.sdd_dir;
|
|
17969
|
+
if (resolved.request.payload.action === "reapply_stash" && typeof sdd === "string") {
|
|
17970
|
+
try {
|
|
17971
|
+
const manifest = JSON.parse(fs12.readFileSync(path16.join(sdd, "manifest.json"), "utf8"));
|
|
17972
|
+
const stashes = gitRaw(root, ["stash", "list", "--format=%H"]);
|
|
17973
|
+
if (typeof target.stash_commit === "string" && manifest.stash_ref === undefined && stashes !== null && !stashes.split(`
|
|
17974
|
+
`).includes(target.stash_commit))
|
|
17975
|
+
observed = { stash_commit: target.stash_commit };
|
|
17976
|
+
} catch {}
|
|
17977
|
+
} else if (typeof targetBranch === "string" && typeof sdd === "string" && gitValue(root, ["branch", "--show-current"]) === targetBranch && typeof target.target_head === "string" && gitValue(root, ["rev-parse", "HEAD"]) === target.target_head) {
|
|
17978
|
+
try {
|
|
17979
|
+
const manifest = JSON.parse(fs12.readFileSync(path16.join(sdd, "manifest.json"), "utf8"));
|
|
17980
|
+
if (manifest.branch === targetBranch)
|
|
17981
|
+
observed = { branch: targetBranch };
|
|
17982
|
+
} catch {}
|
|
17983
|
+
}
|
|
17984
|
+
} else if (operation === "changelog.apply") {
|
|
17985
|
+
const afterDigest = target.afterDigest;
|
|
17986
|
+
const preview = changelogApplyPreview({ ...resolved.request.payload, workspace_root: root });
|
|
17987
|
+
if (!preview.error && typeof afterDigest === "string" && preview.beforeDigest === afterDigest)
|
|
17988
|
+
observed = { target: target.target, afterDigest };
|
|
17989
|
+
}
|
|
17990
|
+
if (!observed)
|
|
17991
|
+
return unknownHostingEvidence(operation);
|
|
17992
|
+
const evidenceDigest = sha256({ operation, observed });
|
|
17993
|
+
return success(null, null, {
|
|
17994
|
+
outcome: "succeeded",
|
|
17995
|
+
evidenceDigest,
|
|
17996
|
+
observation: {
|
|
17997
|
+
kind: "provider_read",
|
|
17998
|
+
actionRef,
|
|
17999
|
+
outcome: "succeeded",
|
|
18000
|
+
evidenceDigest
|
|
18001
|
+
}
|
|
18002
|
+
});
|
|
18003
|
+
};
|
|
18004
|
+
var readExternalAction = async (root, resolved, actionRef) => resolved.request.operation === "git.branch_setup" || resolved.request.operation === "git.commit" || resolved.request.operation === "git.push" || resolved.request.operation === "changelog.apply" ? readLocalAction(root, resolved, actionRef) : resolved.request.operation === "hosting.pull_request" || resolved.request.operation === "hosting.merge" ? readHostingAction(root, resolved, actionRef) : readYouTrackAction(root, resolved, actionRef);
|
|
17845
18005
|
var youTrackBase = () => {
|
|
17846
18006
|
try {
|
|
17847
18007
|
const config = JSON.parse(fs12.readFileSync(youTrackConfigPath(), "utf8"));
|
|
@@ -17899,6 +18059,16 @@ var resolveExternalActionRequest = (root, request, preservedDateMs) => {
|
|
|
17899
18059
|
return failure("storage_error", "current Git commit could not be resolved");
|
|
17900
18060
|
const dirty = Boolean((gitRaw(root, ["status", "--short"]) ?? "").trim());
|
|
17901
18061
|
const creation = action === "reapply_stash" || !target_branch ? { base_branch: policy.defaultTargetBranch } : branchCreationBaseline(root, target_branch, policy.defaultTargetBranch);
|
|
18062
|
+
let stashTarget;
|
|
18063
|
+
if (action === "reapply_stash") {
|
|
18064
|
+
try {
|
|
18065
|
+
const manifest = JSON.parse(fs12.readFileSync(path16.join(sdd_dir, "manifest.json"), "utf8"));
|
|
18066
|
+
const stash_ref = typeof manifest.stash_ref === "string" ? manifest.stash_ref : "";
|
|
18067
|
+
const stash_commit = stash_ref ? gitValue(root, ["rev-parse", stash_ref]) : null;
|
|
18068
|
+
if (stash_ref && stash_commit)
|
|
18069
|
+
stashTarget = { stash_ref, stash_commit };
|
|
18070
|
+
} catch {}
|
|
18071
|
+
}
|
|
17902
18072
|
const normalized = {
|
|
17903
18073
|
operation: request.operation,
|
|
17904
18074
|
payload: {
|
|
@@ -17913,7 +18083,7 @@ var resolveExternalActionRequest = (root, request, preservedDateMs) => {
|
|
|
17913
18083
|
request: normalized,
|
|
17914
18084
|
descriptorPayload: {
|
|
17915
18085
|
...normalized.payload,
|
|
17916
|
-
resolved: { head, dirty, ...creation }
|
|
18086
|
+
resolved: { head, dirty, ...creation, ...stashTarget }
|
|
17917
18087
|
}
|
|
17918
18088
|
});
|
|
17919
18089
|
}
|
|
@@ -18065,7 +18235,7 @@ var resolveExternalActionRequest = (root, request, preservedDateMs) => {
|
|
|
18065
18235
|
outcome: "not_started",
|
|
18066
18236
|
fields: [{ path: "source_branch", reason: "protected branch" }]
|
|
18067
18237
|
});
|
|
18068
|
-
const source_commit = gitValue(root, ["rev-parse", "
|
|
18238
|
+
const source_commit = source_branch ? gitValue(root, ["rev-parse", "--verify", `refs/heads/${source_branch}`]) : null;
|
|
18069
18239
|
const remote = pushRemote(root);
|
|
18070
18240
|
const identity = remote ? credentialFreeRemote(remote) : null;
|
|
18071
18241
|
if (!source_branch || !source_commit || !identity || remoteProvider2(identity) !== null && remoteProvider2(identity) !== String(vcsConfig("load", root).provider).toLowerCase())
|
|
@@ -18353,7 +18523,17 @@ var executeResolvedExternalAction = async (resolved, root, step, caller) => {
|
|
|
18353
18523
|
const workText = fresh.data.descriptorPayload.resolved?.workText;
|
|
18354
18524
|
const beforeDigest = typeof resolvedDetails.resolved?.beforeDigest === "string" ? resolvedDetails.resolved.beforeDigest : undefined;
|
|
18355
18525
|
const beforeExists = typeof resolvedDetails.resolved?.beforeExists === "boolean" ? resolvedDetails.resolved.beforeExists : undefined;
|
|
18356
|
-
|
|
18526
|
+
const result = await executeConcreteExternalAction(fresh.data.request, root, fresh.data.marker, resolvedDate, step, typeof workText === "string" ? workText : undefined, caller, beforeDigest, beforeExists);
|
|
18527
|
+
if (!result.ok && result.code === "external_outcome_unknown" && caller) {
|
|
18528
|
+
const observed = await readLocalAction(root, fresh.data, {
|
|
18529
|
+
kind: "host",
|
|
18530
|
+
host: caller.host,
|
|
18531
|
+
handle: `local-recovery:${fresh.data.request.operation}`
|
|
18532
|
+
});
|
|
18533
|
+
if (observed.ok)
|
|
18534
|
+
return success(null, null, { recovered: true });
|
|
18535
|
+
}
|
|
18536
|
+
return result;
|
|
18357
18537
|
};
|
|
18358
18538
|
|
|
18359
18539
|
// packages/workit-pi/src/tools.ts
|
|
@@ -18620,6 +18800,7 @@ var registerWorkitTools = (pi, options = {}) => {
|
|
|
18620
18800
|
"hosting.merge",
|
|
18621
18801
|
"changelog.apply"
|
|
18622
18802
|
].includes(resolved.data.request.operation);
|
|
18803
|
+
let writerTaskId = null;
|
|
18623
18804
|
if (localOperation) {
|
|
18624
18805
|
const writer = assertLocalExternalActionWriter(ctx.cwd, { host: "pi", actor });
|
|
18625
18806
|
if (!writer.ok)
|
|
@@ -18628,6 +18809,7 @@ var registerWorkitTools = (pi, options = {}) => {
|
|
|
18628
18809
|
operation: resolved.data.request.operation,
|
|
18629
18810
|
guidance: "Acquire checkout writer ownership first (writer.acquire) and retry the action."
|
|
18630
18811
|
}));
|
|
18812
|
+
writerTaskId = writer.data.taskId;
|
|
18631
18813
|
}
|
|
18632
18814
|
const descriptor = externalActionDescriptor(resolved.data.request.operation, resolved.data.descriptorPayload);
|
|
18633
18815
|
const priorRequest = priorExternalAction(store, "pi", actor, resolved.data.request.operation, resolved.data.request.payload);
|
|
@@ -18665,7 +18847,7 @@ var registerWorkitTools = (pi, options = {}) => {
|
|
|
18665
18847
|
}
|
|
18666
18848
|
return output(failure("external_outcome_unknown", "previous external action outcome is unknown"));
|
|
18667
18849
|
}
|
|
18668
|
-
if (priorRequest.ok && priorRequest.data.entry.data.consumption !== null)
|
|
18850
|
+
if (priorRequest.ok && priorRequest.data.entry.data.consumption !== null && resolved.data.request.operation !== "git.commit" && resolved.data.request.operation !== "git.push")
|
|
18669
18851
|
return output(failure("permission_denied", "external action was already settled"));
|
|
18670
18852
|
const drift = resolved.data.request.operation === "git.branch_setup" ? priorResolvedDrift(store, "pi", actor, resolved.data.request.operation, resolved.data.request.payload, resolved.data.descriptorPayload.resolved) : success(null, null, null);
|
|
18671
18853
|
if (!drift.ok)
|
|
@@ -18680,6 +18862,12 @@ var registerWorkitTools = (pi, options = {}) => {
|
|
|
18680
18862
|
return output(result);
|
|
18681
18863
|
}
|
|
18682
18864
|
if (standingAutoApplies(store, "pi", actor, descriptor)) {
|
|
18865
|
+
const planCount = planReservationLength(resolved.data.request.operation, resolved.data.descriptorPayload);
|
|
18866
|
+
if (planCount !== null) {
|
|
18867
|
+
const bound = standingAutoBinding(core, store, "pi", actor, descriptor);
|
|
18868
|
+
if (bound)
|
|
18869
|
+
return output(success(null, null, { plan_commits: planCount }));
|
|
18870
|
+
}
|
|
18683
18871
|
const result = await nativeExternalActionRunner(ctx.cwd, actor, core)(descriptor, (step) => executeResolvedExternalAction(resolved.data, ctx.cwd, step, { host: "pi", actor }));
|
|
18684
18872
|
return output(result);
|
|
18685
18873
|
}
|
|
@@ -18690,7 +18878,7 @@ var registerWorkitTools = (pi, options = {}) => {
|
|
|
18690
18878
|
const workspace = store.readWorkspace();
|
|
18691
18879
|
if (!listed.ok || !workspace.ok || !workspace.data)
|
|
18692
18880
|
return output(failure("storage_error", "external action state is unavailable"));
|
|
18693
|
-
const candidates = listed.data.filter((task) => task.status === "active" && task.workspaceId === workspace.data.id &&
|
|
18881
|
+
const candidates = listed.data.filter((task) => task.status === "active" && task.workspaceId === workspace.data.id && (writerTaskId === null || task.id === writerTaskId));
|
|
18694
18882
|
if (candidates.length !== 1)
|
|
18695
18883
|
return output(failure("permission_denied", "external action requires exactly one active Pi task"));
|
|
18696
18884
|
const task = candidates[0];
|
|
@@ -18722,9 +18910,9 @@ var registerWorkitTools = (pi, options = {}) => {
|
|
|
18722
18910
|
if (!decision.ok)
|
|
18723
18911
|
return output(decision);
|
|
18724
18912
|
}
|
|
18725
|
-
const
|
|
18726
|
-
if (
|
|
18727
|
-
return output(success(null, null, { plan_commits:
|
|
18913
|
+
const planCount = planReservationLength(resolved.data.request.operation, resolved.data.descriptorPayload);
|
|
18914
|
+
if (planCount !== null)
|
|
18915
|
+
return output(success(null, null, { plan_commits: planCount }));
|
|
18728
18916
|
const result = await nativeExternalActionRunner(ctx.cwd, actor, core)(descriptor, (step) => executeResolvedExternalAction(resolved.data, ctx.cwd, step, { host: "pi", actor }));
|
|
18729
18917
|
return output(result);
|
|
18730
18918
|
}
|