@brainervirus/workit-pi 1.2.0 → 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 +251 -75
- 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(),
|
|
@@ -12482,7 +12513,7 @@ var planReservationLength = (operation, descriptorPayload) => {
|
|
|
12482
12513
|
const payload = descriptorPayload ?? {};
|
|
12483
12514
|
return Array.isArray(payload.plan_steps) ? payload.plan_steps.length : null;
|
|
12484
12515
|
};
|
|
12485
|
-
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}; 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,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).";
|
|
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).";
|
|
12486
12517
|
var externalActionRef = (host, actor, descriptor) => ({
|
|
12487
12518
|
kind: "host",
|
|
12488
12519
|
host,
|
|
@@ -12496,12 +12527,12 @@ var externalActionState = (store, host, actor, operation) => {
|
|
|
12496
12527
|
if (!workspace.data)
|
|
12497
12528
|
return failure("not_found", "workspace not found");
|
|
12498
12529
|
const candidates = listed.data.flatMap((task) => {
|
|
12499
|
-
if (task.status !== "active" || task.workspaceId !== workspace.data.id
|
|
12530
|
+
if (task.status !== "active" || task.workspaceId !== workspace.data.id)
|
|
12500
12531
|
return [];
|
|
12501
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 }));
|
|
12502
12533
|
});
|
|
12503
12534
|
if (candidates.length !== 1)
|
|
12504
|
-
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");
|
|
12505
12536
|
return success(workspace.data.revision, null, { ...candidates[0], workspace: workspace.data });
|
|
12506
12537
|
};
|
|
12507
12538
|
var priorExternalAction = (store, host, actor, operation, payload) => {
|
|
@@ -12581,8 +12612,6 @@ var approvedPlanCommit = (store, host, actor, message) => {
|
|
|
12581
12612
|
const matches = listed.data.flatMap((task) => {
|
|
12582
12613
|
if (task.status !== "active" || task.workspaceId !== workspace.data.id)
|
|
12583
12614
|
return [];
|
|
12584
|
-
if (task.intent.provenance.session?.kind !== "host" || task.intent.provenance.session.host !== host || task.intent.provenance.session.handle !== actor)
|
|
12585
|
-
return [];
|
|
12586
12615
|
return task.decisions.flatMap((entry) => {
|
|
12587
12616
|
const decision = entry.data;
|
|
12588
12617
|
if (decision.purpose !== "action" || decision.response !== "approved" || decision.revoked !== null)
|
|
@@ -12624,8 +12653,6 @@ var approvedChainStep = (store, host, actor, query) => {
|
|
|
12624
12653
|
const matches = listed.data.flatMap((task) => {
|
|
12625
12654
|
if (task.status !== "active" || task.workspaceId !== workspace.data.id)
|
|
12626
12655
|
return [];
|
|
12627
|
-
if (task.intent.provenance.session?.kind !== "host" || task.intent.provenance.session.host !== host || task.intent.provenance.session.handle !== actor)
|
|
12628
|
-
return [];
|
|
12629
12656
|
return task.decisions.flatMap((entry) => {
|
|
12630
12657
|
const decision = entry.data;
|
|
12631
12658
|
if (decision.purpose !== "action" || decision.response !== "approved" || decision.revoked !== null)
|
|
@@ -12782,7 +12809,7 @@ var branchSetupIntent = (descriptor) => {
|
|
|
12782
12809
|
var approvedBranchSetupIntent = (store, host, actor, operation) => {
|
|
12783
12810
|
const current = branchSetupIntent(operation);
|
|
12784
12811
|
if (!current)
|
|
12785
|
-
return failure("permission_denied", "no approved action is bound to
|
|
12812
|
+
return failure("permission_denied", "no approved action is bound to the active task");
|
|
12786
12813
|
const listed = store.listTasks();
|
|
12787
12814
|
const workspace = store.readWorkspace();
|
|
12788
12815
|
if (!listed.ok || !workspace.ok)
|
|
@@ -12790,7 +12817,7 @@ var approvedBranchSetupIntent = (store, host, actor, operation) => {
|
|
|
12790
12817
|
if (!workspace.data)
|
|
12791
12818
|
return failure("not_found", "workspace not found");
|
|
12792
12819
|
const candidates = listed.data.flatMap((task) => {
|
|
12793
|
-
if (task.status !== "active" || task.workspaceId !== workspace.data.id
|
|
12820
|
+
if (task.status !== "active" || task.workspaceId !== workspace.data.id)
|
|
12794
12821
|
return [];
|
|
12795
12822
|
return task.decisions.filter((entry) => entry.data.purpose === "action" && entry.data.response === "approved" && entry.data.revoked === null).map((entry) => ({ task, entry }));
|
|
12796
12823
|
});
|
|
@@ -12799,7 +12826,7 @@ var approvedBranchSetupIntent = (store, host, actor, operation) => {
|
|
|
12799
12826
|
return intent !== null && intent.target === current.target && intent.base === current.base && intent.remoteBase === current.remoteBase && intent.targetExists === current.targetExists;
|
|
12800
12827
|
});
|
|
12801
12828
|
if (matches.length !== 1)
|
|
12802
|
-
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");
|
|
12803
12830
|
return success(workspace.data.revision, null, { ...matches[0], workspace: workspace.data });
|
|
12804
12831
|
};
|
|
12805
12832
|
var priorResolvedDrift = (store, host, actor, operation, payload, currentResolved) => {
|
|
@@ -13356,6 +13383,7 @@ var verifyNativeWorker = (verifier, input, binding) => {
|
|
|
13356
13383
|
expectedWorkspaceRevision: expected.expectedWorkspaceRevision,
|
|
13357
13384
|
state: expected.state,
|
|
13358
13385
|
session: expected.session,
|
|
13386
|
+
report: expected.report ?? null,
|
|
13359
13387
|
provenance: result.data,
|
|
13360
13388
|
...binding
|
|
13361
13389
|
});
|
|
@@ -13366,7 +13394,7 @@ var takeWorker = (token, binding, expected) => {
|
|
|
13366
13394
|
verifiedWorkers.delete(token);
|
|
13367
13395
|
if (!value)
|
|
13368
13396
|
return null;
|
|
13369
|
-
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))
|
|
13370
13398
|
return null;
|
|
13371
13399
|
return value;
|
|
13372
13400
|
};
|
|
@@ -13407,6 +13435,8 @@ var applyWorkerLifecycle = (input) => {
|
|
|
13407
13435
|
return failure("invalid_transition", "worker already has an observed session");
|
|
13408
13436
|
if (notStarted && !["dispatching", "cancelling"].includes(entry.data.state))
|
|
13409
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");
|
|
13410
13440
|
if ((input.state === "running" || input.state === "cancelling" || input.state === "stopped" && !notStarted) && !input.session)
|
|
13411
13441
|
return failure("invalid_input", "running worker observations require a session");
|
|
13412
13442
|
if (entry.data.session && !sameValue(entry.data.session, input.session))
|
|
@@ -13423,12 +13453,13 @@ var applyWorkerLifecycle = (input) => {
|
|
|
13423
13453
|
data: {
|
|
13424
13454
|
...entry.data,
|
|
13425
13455
|
state: terminalCancel ? "cancelling" : input.state,
|
|
13426
|
-
session: input.session
|
|
13456
|
+
session: input.session,
|
|
13457
|
+
report: entry.data.report ?? input.report ?? null
|
|
13427
13458
|
}
|
|
13428
13459
|
};
|
|
13429
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;
|
|
13430
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;
|
|
13431
|
-
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)
|
|
13432
13463
|
return success(task.data.revision, workspace.data.revision, entry);
|
|
13433
13464
|
const changed = input.store.mutateTaskAndWorkspace({
|
|
13434
13465
|
taskId: task.data.id,
|
|
@@ -13846,14 +13877,30 @@ class WorkitCore {
|
|
|
13846
13877
|
const listed = this.store.listTasks();
|
|
13847
13878
|
if (!listed.ok)
|
|
13848
13879
|
return listed;
|
|
13849
|
-
const
|
|
13850
|
-
|
|
13851
|
-
|
|
13852
|
-
|
|
13853
|
-
|
|
13854
|
-
|
|
13855
|
-
|
|
13856
|
-
|
|
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);
|
|
13857
13904
|
}
|
|
13858
13905
|
case "inspect": {
|
|
13859
13906
|
const task = this.store.readTask(input.taskId);
|
|
@@ -13862,7 +13909,7 @@ class WorkitCore {
|
|
|
13862
13909
|
const helper = this.helperTaskGuard(task.data, true);
|
|
13863
13910
|
if (!helper.ok)
|
|
13864
13911
|
return helper;
|
|
13865
|
-
if (input.view === "summary")
|
|
13912
|
+
if ((input.view ?? "summary") === "summary")
|
|
13866
13913
|
return this.summary(task.data);
|
|
13867
13914
|
return this.view(task.data);
|
|
13868
13915
|
}
|
|
@@ -14206,7 +14253,7 @@ class WorkitCore {
|
|
|
14206
14253
|
const evaluations = evaluateEvidence(task.data, current.data);
|
|
14207
14254
|
const verified = evidence.some((entry) => {
|
|
14208
14255
|
const evaluation = evaluations.find((item) => item.evidenceId === entry.id);
|
|
14209
|
-
return findingVerificationPasses(finding.data.candidateId, {
|
|
14256
|
+
return entry.recordedAt >= finding.recordedAt && findingVerificationPasses(finding.data.candidateId, {
|
|
14210
14257
|
kind: entry.data.kind,
|
|
14211
14258
|
candidateId: entry.data.candidateId,
|
|
14212
14259
|
status: evaluation?.status ?? "missing"
|
|
@@ -14286,7 +14333,18 @@ class WorkitCore {
|
|
|
14286
14333
|
if (task.data.status !== "active")
|
|
14287
14334
|
return failure("invalid_transition", "paused or closed tasks cannot assign workers");
|
|
14288
14335
|
if (!bindingCovers(task.data.intent.data.scope, input.assignment.scope))
|
|
14289
|
-
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
|
+
});
|
|
14290
14348
|
if (input.assignment.decisionIds.some((id) => !task.data.decisions.some((entry) => entry.id === id)))
|
|
14291
14349
|
return failure("invalid_input", "worker assignment references an unknown decision");
|
|
14292
14350
|
if (input.assignment.requirementIds.some((id) => !task.data.policy?.requirements.some((requirement) => requirement.id === id)))
|
|
@@ -14493,7 +14551,15 @@ class WorkitCore {
|
|
|
14493
14551
|
...item,
|
|
14494
14552
|
recordedAt: mutation.now,
|
|
14495
14553
|
provenance: attested.data,
|
|
14496
|
-
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
|
+
}
|
|
14497
14563
|
} : item)
|
|
14498
14564
|
});
|
|
14499
14565
|
}
|
|
@@ -14569,6 +14635,7 @@ class WorkitCore {
|
|
|
14569
14635
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
14570
14636
|
state: "stopped",
|
|
14571
14637
|
session: null,
|
|
14638
|
+
report: null,
|
|
14572
14639
|
provenance: attested.data,
|
|
14573
14640
|
owner: this.authorityOwner,
|
|
14574
14641
|
store: this.store,
|
|
@@ -14876,10 +14943,12 @@ class WorkitCore {
|
|
|
14876
14943
|
return workspace;
|
|
14877
14944
|
if (!workspace.data)
|
|
14878
14945
|
return failure("not_found", "workspace not found");
|
|
14879
|
-
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());
|
|
14880
14948
|
if (!current.ok)
|
|
14881
14949
|
return current;
|
|
14882
|
-
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);
|
|
14883
14952
|
return success(task.revision, workspace.data.revision, {
|
|
14884
14953
|
id: task.id,
|
|
14885
14954
|
revision: task.revision,
|
|
@@ -14893,7 +14962,7 @@ class WorkitCore {
|
|
|
14893
14962
|
progress: task.progress,
|
|
14894
14963
|
policy: task.policy,
|
|
14895
14964
|
requirements,
|
|
14896
|
-
writer:
|
|
14965
|
+
writer: evaluationWorkspace.writer
|
|
14897
14966
|
});
|
|
14898
14967
|
}
|
|
14899
14968
|
view(task) {
|
|
@@ -14902,13 +14971,15 @@ class WorkitCore {
|
|
|
14902
14971
|
return workspace;
|
|
14903
14972
|
if (!workspace.data)
|
|
14904
14973
|
return failure("not_found", "workspace not found");
|
|
14905
|
-
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());
|
|
14906
14976
|
if (!current.ok)
|
|
14907
14977
|
return current;
|
|
14908
|
-
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);
|
|
14909
14980
|
return success(task.revision, workspace.data.revision, {
|
|
14910
14981
|
task,
|
|
14911
|
-
workspace:
|
|
14982
|
+
workspace: evaluationWorkspace,
|
|
14912
14983
|
capabilities: this.context.capabilities,
|
|
14913
14984
|
requirements,
|
|
14914
14985
|
evidence: evaluateEvidence(task, current.data)
|
|
@@ -14999,8 +15070,6 @@ class WorkitCore {
|
|
|
14999
15070
|
return helper;
|
|
15000
15071
|
if (status === "paused" && task.data.status !== "active" || status === "active" && task.data.status !== "paused")
|
|
15001
15072
|
return failure("invalid_transition", `cannot transition ${task.data.status} to ${status}`);
|
|
15002
|
-
if (status === "active" && input.authorityRefs.length === 0)
|
|
15003
|
-
return failure("permission_denied", "resuming a task requires authority references");
|
|
15004
15073
|
const workspace = this.store.readWorkspace();
|
|
15005
15074
|
if (!workspace.ok)
|
|
15006
15075
|
return workspace;
|
|
@@ -15030,7 +15099,7 @@ class WorkitCore {
|
|
|
15030
15099
|
return failure("needs_input", "imported task requires fresh destination evidence");
|
|
15031
15100
|
if (current.data.blockers.some((blocker) => blocker.dependentAction === "resume" || blocker.reason === "worker state requires reconciliation"))
|
|
15032
15101
|
return failure("recovery_required", "imported task requires resume reconciliation");
|
|
15033
|
-
if (!validResumeApproval(task.data, workspace.data, input.authorityRefs, this.context))
|
|
15102
|
+
if (!validResumeApproval(task.data, workspace.data, input.authorityRefs ?? [], this.context))
|
|
15034
15103
|
return failure("permission_denied", "imported resume requires native destination approval");
|
|
15035
15104
|
resumeCandidate = current.data.candidate;
|
|
15036
15105
|
}
|
|
@@ -15160,7 +15229,7 @@ class WorkitCore {
|
|
|
15160
15229
|
task: (value, context) => success(context.revision, null, {
|
|
15161
15230
|
...value,
|
|
15162
15231
|
status: "closed",
|
|
15163
|
-
candidates:
|
|
15232
|
+
candidates: taskForView.candidates,
|
|
15164
15233
|
closure: {
|
|
15165
15234
|
outcome: closure.data.outcome,
|
|
15166
15235
|
at: context.now,
|
|
@@ -17469,7 +17538,7 @@ var readExternalContext = async (root, payload) => {
|
|
|
17469
17538
|
}
|
|
17470
17539
|
}
|
|
17471
17540
|
};
|
|
17472
|
-
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";
|
|
17473
17542
|
var assertLocalExternalActionWriter = (root, caller, paths = ["."]) => {
|
|
17474
17543
|
const store = new TaskStore(root);
|
|
17475
17544
|
const listed = store.listTasks();
|
|
@@ -17480,7 +17549,7 @@ var assertLocalExternalActionWriter = (root, caller, paths = ["."]) => {
|
|
|
17480
17549
|
return workspace;
|
|
17481
17550
|
if (!workspace.data)
|
|
17482
17551
|
return failure("not_found", "workspace not found");
|
|
17483
|
-
const candidates = listed.data.filter((task) => task
|
|
17552
|
+
const candidates = listed.data.filter((task) => currentWriterOwnsTask(task, workspace.data, caller));
|
|
17484
17553
|
if (candidates.length !== 1)
|
|
17485
17554
|
return failure("permission_denied", "local external action requires exactly one active task");
|
|
17486
17555
|
const allowed = assertProductWriteAllowed({
|
|
@@ -17490,7 +17559,7 @@ var assertLocalExternalActionWriter = (root, caller, paths = ["."]) => {
|
|
|
17490
17559
|
paths,
|
|
17491
17560
|
store
|
|
17492
17561
|
});
|
|
17493
|
-
return allowed
|
|
17562
|
+
return allowed;
|
|
17494
17563
|
};
|
|
17495
17564
|
var safePath = (root, value) => {
|
|
17496
17565
|
try {
|
|
@@ -17502,13 +17571,20 @@ var safePath = (root, value) => {
|
|
|
17502
17571
|
var branchCreationBaseline = (root, target, base) => {
|
|
17503
17572
|
const exists = gitValue(root, ["rev-parse", "--verify", "--quiet", `refs/heads/${target}`]) !== null;
|
|
17504
17573
|
if (exists)
|
|
17505
|
-
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
|
+
};
|
|
17506
17580
|
const remote = run3(root, ["ls-remote", "origin", `refs/heads/${base}`]);
|
|
17507
17581
|
const sha = remote.exitCode === 0 ? remote.stdout.trim().split(/\s+/)[0] : "";
|
|
17582
|
+
const remoteBase = /^[0-9a-f]{40}$/.test(sha ?? "") ? sha : null;
|
|
17508
17583
|
return {
|
|
17509
17584
|
base_branch: base,
|
|
17510
17585
|
target_exists: false,
|
|
17511
|
-
remote_base:
|
|
17586
|
+
remote_base: remoteBase,
|
|
17587
|
+
target_head: remoteBase
|
|
17512
17588
|
};
|
|
17513
17589
|
};
|
|
17514
17590
|
var shortSha = (value) => typeof value === "string" && value ? value.slice(0, 8) : "unknown";
|
|
@@ -17619,7 +17695,7 @@ var actionProposalQuestion = (request, descriptorPayload) => {
|
|
|
17619
17695
|
var approvedResolvedExternalAction = (content) => {
|
|
17620
17696
|
try {
|
|
17621
17697
|
const value = JSON.parse(content);
|
|
17622
|
-
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")
|
|
17623
17699
|
return failure("invalid_input", "approved action is not a reconciliable external action");
|
|
17624
17700
|
if (!value.payload || typeof value.payload !== "object")
|
|
17625
17701
|
return failure("invalid_input", "approved action payload is incomplete");
|
|
@@ -17630,18 +17706,24 @@ var approvedResolvedExternalAction = (content) => {
|
|
|
17630
17706
|
if (!request.ok || !resolved || typeof resolved !== "object")
|
|
17631
17707
|
return failure("invalid_input", "approved hosting target is incomplete");
|
|
17632
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");
|
|
17633
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"))
|
|
17634
17716
|
return failure("invalid_input", "approved hosting target is incomplete");
|
|
17635
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"))
|
|
17636
17718
|
return failure("invalid_input", "approved hosting target is incomplete");
|
|
17637
17719
|
if (value.operation === "changelog.apply" && (typeof target.target !== "string" || typeof target.beforeDigest !== "string" || typeof target.beforeExists !== "boolean" || typeof target.afterDigest !== "string"))
|
|
17638
17720
|
return failure("invalid_input", "approved changelog target is incomplete");
|
|
17639
|
-
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)))
|
|
17640
17722
|
return failure("invalid_input", "approved YouTrack target is incomplete");
|
|
17641
17723
|
return success(null, null, {
|
|
17642
17724
|
request: request.data,
|
|
17643
17725
|
descriptorPayload: value.payload,
|
|
17644
|
-
marker:
|
|
17726
|
+
...typeof target.marker === "string" ? { marker: target.marker } : {}
|
|
17645
17727
|
});
|
|
17646
17728
|
} catch {
|
|
17647
17729
|
return failure("invalid_input", "approved action descriptor is invalid");
|
|
@@ -17667,12 +17749,13 @@ var unknownHostingEvidence = (operation) => failure("external_outcome_unknown",
|
|
|
17667
17749
|
outcome: "unknown"
|
|
17668
17750
|
});
|
|
17669
17751
|
var readHostingAction = async (root, resolved, actionRef) => {
|
|
17670
|
-
|
|
17671
|
-
|
|
17752
|
+
const operation = resolved.request.operation;
|
|
17753
|
+
if (operation !== "hosting.pull_request" && operation !== "hosting.merge" || !resolved.marker)
|
|
17754
|
+
return unknownHostingEvidence(operation);
|
|
17672
17755
|
try {
|
|
17673
17756
|
const cfg = vcsConfig("load", root);
|
|
17674
17757
|
if (!cfg.ok || !cfg.tokenPath || !cfg.provider || cfg.provider !== "github" && cfg.provider !== "gitlab")
|
|
17675
|
-
return unknownHostingEvidence(
|
|
17758
|
+
return unknownHostingEvidence(operation);
|
|
17676
17759
|
const remote = pushRemote(root);
|
|
17677
17760
|
const repo = remote ? remoteRepo(remote) : null;
|
|
17678
17761
|
const approved = resolved.descriptorPayload;
|
|
@@ -17691,7 +17774,7 @@ var readHostingAction = async (root, resolved, actionRef) => {
|
|
|
17691
17774
|
}
|
|
17692
17775
|
})();
|
|
17693
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)
|
|
17694
|
-
return unknownHostingEvidence(
|
|
17777
|
+
return unknownHostingEvidence(operation);
|
|
17695
17778
|
const token = fs12.readFileSync(cfg.tokenPath, "utf8").trim();
|
|
17696
17779
|
const headers = { Authorization: `Bearer ${token}`, Accept: "application/vnd.github+json" };
|
|
17697
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`;
|
|
@@ -17699,14 +17782,14 @@ var readHostingAction = async (root, resolved, actionRef) => {
|
|
|
17699
17782
|
headers: cfg.provider === "gitlab" ? { "PRIVATE-TOKEN": token } : headers
|
|
17700
17783
|
});
|
|
17701
17784
|
if (!response.ok)
|
|
17702
|
-
return unknownHostingEvidence(
|
|
17785
|
+
return unknownHostingEvidence(operation);
|
|
17703
17786
|
const values = await response.json();
|
|
17704
17787
|
if (!Array.isArray(values) || values.length >= 100)
|
|
17705
|
-
return unknownHostingEvidence(
|
|
17788
|
+
return unknownHostingEvidence(operation);
|
|
17706
17789
|
const records = [];
|
|
17707
17790
|
for (const item of values) {
|
|
17708
17791
|
if (!item || typeof item !== "object" || Array.isArray(item))
|
|
17709
|
-
return unknownHostingEvidence(
|
|
17792
|
+
return unknownHostingEvidence(operation);
|
|
17710
17793
|
const record = item;
|
|
17711
17794
|
const body = record.body ?? record.description;
|
|
17712
17795
|
const base = record.base;
|
|
@@ -17718,7 +17801,7 @@ var readHostingAction = async (root, resolved, actionRef) => {
|
|
|
17718
17801
|
const shaValue = cfg.provider === "github" && githubShape ? head.sha : record.sha;
|
|
17719
17802
|
const idValue = record.number ?? record.iid ?? record.id;
|
|
17720
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")
|
|
17721
|
-
return unknownHostingEvidence(
|
|
17804
|
+
return unknownHostingEvidence(operation);
|
|
17722
17805
|
records.push(record);
|
|
17723
17806
|
}
|
|
17724
17807
|
const matches = records.filter((item) => {
|
|
@@ -17728,15 +17811,23 @@ var readHostingAction = async (root, resolved, actionRef) => {
|
|
|
17728
17811
|
const itemTarget = cfg.provider === "github" ? base?.ref : item.target_branch;
|
|
17729
17812
|
const itemSource = cfg.provider === "github" ? head?.ref : item.source_branch;
|
|
17730
17813
|
const itemSha = cfg.provider === "github" ? head?.sha : item.sha;
|
|
17731
|
-
|
|
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;
|
|
17732
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);
|
|
17733
17823
|
if (matches.length !== 1)
|
|
17734
|
-
return unknownHostingEvidence(
|
|
17824
|
+
return unknownHostingEvidence(operation);
|
|
17735
17825
|
const item = matches[0];
|
|
17736
17826
|
const id = item.number ?? item.iid ?? item.id;
|
|
17737
17827
|
if (typeof id !== "string" && typeof id !== "number")
|
|
17738
|
-
return unknownHostingEvidence(
|
|
17828
|
+
return unknownHostingEvidence(operation);
|
|
17739
17829
|
const evidenceDigest = sha256({
|
|
17830
|
+
operation,
|
|
17740
17831
|
provider: cfg.provider,
|
|
17741
17832
|
id,
|
|
17742
17833
|
marker: resolved.marker,
|
|
@@ -17757,7 +17848,7 @@ var readHostingAction = async (root, resolved, actionRef) => {
|
|
|
17757
17848
|
data: { provider: cfg.provider, id }
|
|
17758
17849
|
});
|
|
17759
17850
|
} catch {
|
|
17760
|
-
return unknownHostingEvidence(
|
|
17851
|
+
return unknownHostingEvidence(operation);
|
|
17761
17852
|
}
|
|
17762
17853
|
};
|
|
17763
17854
|
var appendMarker = (text, marker) => `${text}${text ? `
|
|
@@ -17847,7 +17938,70 @@ var readYouTrackAction = async (root, resolved, actionRef) => {
|
|
|
17847
17938
|
return unknownHostingEvidence(operation);
|
|
17848
17939
|
}
|
|
17849
17940
|
};
|
|
17850
|
-
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);
|
|
17851
18005
|
var youTrackBase = () => {
|
|
17852
18006
|
try {
|
|
17853
18007
|
const config = JSON.parse(fs12.readFileSync(youTrackConfigPath(), "utf8"));
|
|
@@ -17905,6 +18059,16 @@ var resolveExternalActionRequest = (root, request, preservedDateMs) => {
|
|
|
17905
18059
|
return failure("storage_error", "current Git commit could not be resolved");
|
|
17906
18060
|
const dirty = Boolean((gitRaw(root, ["status", "--short"]) ?? "").trim());
|
|
17907
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
|
+
}
|
|
17908
18072
|
const normalized = {
|
|
17909
18073
|
operation: request.operation,
|
|
17910
18074
|
payload: {
|
|
@@ -17919,7 +18083,7 @@ var resolveExternalActionRequest = (root, request, preservedDateMs) => {
|
|
|
17919
18083
|
request: normalized,
|
|
17920
18084
|
descriptorPayload: {
|
|
17921
18085
|
...normalized.payload,
|
|
17922
|
-
resolved: { head, dirty, ...creation }
|
|
18086
|
+
resolved: { head, dirty, ...creation, ...stashTarget }
|
|
17923
18087
|
}
|
|
17924
18088
|
});
|
|
17925
18089
|
}
|
|
@@ -18071,7 +18235,7 @@ var resolveExternalActionRequest = (root, request, preservedDateMs) => {
|
|
|
18071
18235
|
outcome: "not_started",
|
|
18072
18236
|
fields: [{ path: "source_branch", reason: "protected branch" }]
|
|
18073
18237
|
});
|
|
18074
|
-
const source_commit = gitValue(root, ["rev-parse", "
|
|
18238
|
+
const source_commit = source_branch ? gitValue(root, ["rev-parse", "--verify", `refs/heads/${source_branch}`]) : null;
|
|
18075
18239
|
const remote = pushRemote(root);
|
|
18076
18240
|
const identity = remote ? credentialFreeRemote(remote) : null;
|
|
18077
18241
|
if (!source_branch || !source_commit || !identity || remoteProvider2(identity) !== null && remoteProvider2(identity) !== String(vcsConfig("load", root).provider).toLowerCase())
|
|
@@ -18359,7 +18523,17 @@ var executeResolvedExternalAction = async (resolved, root, step, caller) => {
|
|
|
18359
18523
|
const workText = fresh.data.descriptorPayload.resolved?.workText;
|
|
18360
18524
|
const beforeDigest = typeof resolvedDetails.resolved?.beforeDigest === "string" ? resolvedDetails.resolved.beforeDigest : undefined;
|
|
18361
18525
|
const beforeExists = typeof resolvedDetails.resolved?.beforeExists === "boolean" ? resolvedDetails.resolved.beforeExists : undefined;
|
|
18362
|
-
|
|
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;
|
|
18363
18537
|
};
|
|
18364
18538
|
|
|
18365
18539
|
// packages/workit-pi/src/tools.ts
|
|
@@ -18626,6 +18800,7 @@ var registerWorkitTools = (pi, options = {}) => {
|
|
|
18626
18800
|
"hosting.merge",
|
|
18627
18801
|
"changelog.apply"
|
|
18628
18802
|
].includes(resolved.data.request.operation);
|
|
18803
|
+
let writerTaskId = null;
|
|
18629
18804
|
if (localOperation) {
|
|
18630
18805
|
const writer = assertLocalExternalActionWriter(ctx.cwd, { host: "pi", actor });
|
|
18631
18806
|
if (!writer.ok)
|
|
@@ -18634,6 +18809,7 @@ var registerWorkitTools = (pi, options = {}) => {
|
|
|
18634
18809
|
operation: resolved.data.request.operation,
|
|
18635
18810
|
guidance: "Acquire checkout writer ownership first (writer.acquire) and retry the action."
|
|
18636
18811
|
}));
|
|
18812
|
+
writerTaskId = writer.data.taskId;
|
|
18637
18813
|
}
|
|
18638
18814
|
const descriptor = externalActionDescriptor(resolved.data.request.operation, resolved.data.descriptorPayload);
|
|
18639
18815
|
const priorRequest = priorExternalAction(store, "pi", actor, resolved.data.request.operation, resolved.data.request.payload);
|
|
@@ -18671,7 +18847,7 @@ var registerWorkitTools = (pi, options = {}) => {
|
|
|
18671
18847
|
}
|
|
18672
18848
|
return output(failure("external_outcome_unknown", "previous external action outcome is unknown"));
|
|
18673
18849
|
}
|
|
18674
|
-
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")
|
|
18675
18851
|
return output(failure("permission_denied", "external action was already settled"));
|
|
18676
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);
|
|
18677
18853
|
if (!drift.ok)
|
|
@@ -18702,7 +18878,7 @@ var registerWorkitTools = (pi, options = {}) => {
|
|
|
18702
18878
|
const workspace = store.readWorkspace();
|
|
18703
18879
|
if (!listed.ok || !workspace.ok || !workspace.data)
|
|
18704
18880
|
return output(failure("storage_error", "external action state is unavailable"));
|
|
18705
|
-
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));
|
|
18706
18882
|
if (candidates.length !== 1)
|
|
18707
18883
|
return output(failure("permission_denied", "external action requires exactly one active Pi task"));
|
|
18708
18884
|
const task = candidates[0];
|