@brainervirus/workit-opencode 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/plugin.js +393 -187
- package/package.json +2 -2
package/dist/plugin.js
CHANGED
|
@@ -7965,6 +7965,7 @@ var workerSchema = object({
|
|
|
7965
7965
|
assignment: assignmentSchema,
|
|
7966
7966
|
state: _enum(["assigned", "dispatching", "running", "cancelling", "stopped", "unknown"]),
|
|
7967
7967
|
session: refSchema.nullable(),
|
|
7968
|
+
coordinator: refSchema.optional(),
|
|
7968
7969
|
report: workerReportSchema.nullable()
|
|
7969
7970
|
}).strict();
|
|
7970
7971
|
var ownerSchema = object({
|
|
@@ -8076,6 +8077,7 @@ var taskSummarySchema = object({
|
|
|
8076
8077
|
requirements: array(requirementEvaluationSchema),
|
|
8077
8078
|
writer: workspaceRecordSchema.shape.writer
|
|
8078
8079
|
}).strict();
|
|
8080
|
+
var taskListItemSchema = taskSummarySchema.omit({ policy: true, requirements: true });
|
|
8079
8081
|
var exportBundleSchema = object({
|
|
8080
8082
|
schemaVersion: literal(1),
|
|
8081
8083
|
exportedAt: utc,
|
|
@@ -8097,11 +8099,15 @@ var taskOperations = {
|
|
|
8097
8099
|
expectedWorkspaceRevision: revision.nullable().optional(),
|
|
8098
8100
|
intent: intentSchema
|
|
8099
8101
|
}),
|
|
8100
|
-
list: operation({
|
|
8102
|
+
list: operation({
|
|
8103
|
+
action: literal("list"),
|
|
8104
|
+
status: _enum(["open", "closed", "all"]).optional(),
|
|
8105
|
+
limit: number2().int().min(1).max(50).optional()
|
|
8106
|
+
}),
|
|
8101
8107
|
inspect: operation({
|
|
8102
8108
|
action: literal("inspect"),
|
|
8103
8109
|
...taskId,
|
|
8104
|
-
view: _enum(["summary", "full"])
|
|
8110
|
+
view: _enum(["summary", "full"]).optional()
|
|
8105
8111
|
}),
|
|
8106
8112
|
revise: operation({
|
|
8107
8113
|
action: literal("revise"),
|
|
@@ -8129,7 +8135,7 @@ var taskOperations = {
|
|
|
8129
8135
|
...taskId,
|
|
8130
8136
|
...revisions,
|
|
8131
8137
|
expectedWorkspaceRevision: revision.optional(),
|
|
8132
|
-
authorityRefs: array(refSchema)
|
|
8138
|
+
authorityRefs: array(refSchema).optional()
|
|
8133
8139
|
}),
|
|
8134
8140
|
close: operation({
|
|
8135
8141
|
action: literal("close"),
|
|
@@ -9962,6 +9968,10 @@ var validPath = (value) => typeof value === "string" && value.length > 0 && ![..
|
|
|
9962
9968
|
}) && !value.split(/[\\/]/).includes("..");
|
|
9963
9969
|
var callerValue = (value) => ({ host: value.host, actor: value.actor });
|
|
9964
9970
|
var workerIdOf = (value) => value.workerId ?? null;
|
|
9971
|
+
var currentWriterOwnsTask = (task, workspace, caller) => {
|
|
9972
|
+
const owner = workspace.writer?.owner;
|
|
9973
|
+
return task.status === "active" && task.workspaceId === workspace.id && owner?.taskId === task.id && owner.session.host === caller.host && owner.session.handle === caller.actor;
|
|
9974
|
+
};
|
|
9965
9975
|
function assertProductWriteAllowed(input) {
|
|
9966
9976
|
if (!Array.isArray(input.paths) || input.paths.some((path) => !validPath(path)))
|
|
9967
9977
|
return failure("invalid_input", "invalid product write path");
|
|
@@ -11144,9 +11154,11 @@ var standingReceiptFor = (root, workspace, cls, dir) => {
|
|
|
11144
11154
|
var verifyStandingApproval = (root, task, caller, binding) => {
|
|
11145
11155
|
if (task.status !== "active")
|
|
11146
11156
|
return failure("invalid_transition", "only active tasks can authorize actions");
|
|
11147
|
-
const
|
|
11148
|
-
if (!
|
|
11149
|
-
return
|
|
11157
|
+
const workspace = new TaskStore(root).readWorkspace();
|
|
11158
|
+
if (!workspace.ok)
|
|
11159
|
+
return workspace;
|
|
11160
|
+
if (!workspace.data || !currentWriterOwnsTask(task, workspace.data, caller))
|
|
11161
|
+
return failure("permission_denied", "standing approval requires the current writer session");
|
|
11150
11162
|
const standing = binding.standing;
|
|
11151
11163
|
if (!standing || typeof standing.workspace !== "string" || typeof standing.class !== "string")
|
|
11152
11164
|
return failure("invalid_input", "standing approval needs a workspace and class");
|
|
@@ -11194,14 +11206,14 @@ var standingAutoApplies = (store, host, actor, operation) => {
|
|
|
11194
11206
|
const workspace = store.readWorkspace();
|
|
11195
11207
|
if (!listed.ok || !workspace.ok || !workspace.data)
|
|
11196
11208
|
return null;
|
|
11197
|
-
const
|
|
11198
|
-
if (
|
|
11209
|
+
const task = listed.data.find((task) => currentWriterOwnsTask(task, workspace.data, { host, actor }));
|
|
11210
|
+
if (!task)
|
|
11199
11211
|
return null;
|
|
11200
11212
|
const resolved = resolveAutoApproval(store.root);
|
|
11201
11213
|
if (resolved.status !== "on")
|
|
11202
11214
|
return null;
|
|
11203
11215
|
return {
|
|
11204
|
-
task
|
|
11216
|
+
task,
|
|
11205
11217
|
workspaceId: workspace.data.id,
|
|
11206
11218
|
workspaceRevision: workspace.data.revision,
|
|
11207
11219
|
cls,
|
|
@@ -11460,6 +11472,20 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
|
11460
11472
|
};
|
|
11461
11473
|
var verifyContentRefs = (store, binding) => verifyDecisionContentAtRoot(store.root, binding);
|
|
11462
11474
|
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);
|
|
11475
|
+
var approvalAuthorityMatches = (store, task, approval, current) => {
|
|
11476
|
+
if (approval.kind !== "host_observed" || approval.receipts.length === 0)
|
|
11477
|
+
return false;
|
|
11478
|
+
if (provenanceMatches(approval, current))
|
|
11479
|
+
return true;
|
|
11480
|
+
const session = current.kind === "host_observed" ? current.session : null;
|
|
11481
|
+
if (!session || session.kind !== "host")
|
|
11482
|
+
return false;
|
|
11483
|
+
const workspace = store.readWorkspace();
|
|
11484
|
+
return workspace.ok && workspace.data !== null && currentWriterOwnsTask(task, workspace.data, {
|
|
11485
|
+
host: session.host,
|
|
11486
|
+
actor: session.handle
|
|
11487
|
+
});
|
|
11488
|
+
};
|
|
11463
11489
|
var validateAction = (store, task, workspaceId, input, authority) => {
|
|
11464
11490
|
if (task.status !== "active")
|
|
11465
11491
|
return failure("invalid_transition", "only active tasks can authorize actions");
|
|
@@ -11485,7 +11511,7 @@ var validateAction = (store, task, workspaceId, input, authority) => {
|
|
|
11485
11511
|
return failure("permission_denied", "native reservation authority does not match decision");
|
|
11486
11512
|
if (decision.response !== "approved")
|
|
11487
11513
|
return failure("permission_denied", "decision was rejected");
|
|
11488
|
-
if (
|
|
11514
|
+
if (!approvalAuthorityMatches(store, task, entry.provenance, authority.provenance))
|
|
11489
11515
|
return failure("permission_denied", "action approval lacks native receipt assurance");
|
|
11490
11516
|
const receipts = entry.provenance.receipts;
|
|
11491
11517
|
const standing = receipts.filter((receipt) => typeof receipt === "object" && receipt !== null && receipt.kind === "standing");
|
|
@@ -11652,7 +11678,7 @@ function settleAction(input) {
|
|
|
11652
11678
|
return failure("not_found", "decision not found");
|
|
11653
11679
|
if (!authorityDecisionMatches(authority, entry.data))
|
|
11654
11680
|
return failure("permission_denied", "native settlement authority does not match decision");
|
|
11655
|
-
if (!
|
|
11681
|
+
if (!approvalAuthorityMatches(input.store, current, entry.provenance, authority.provenance))
|
|
11656
11682
|
return failure("permission_denied", "native settlement caller does not match approval");
|
|
11657
11683
|
if (entry.data.purpose !== "action" || entry.data.digest !== decisionDigest(entry.data))
|
|
11658
11684
|
return failure("permission_denied", "decision binding is invalid");
|
|
@@ -11751,7 +11777,7 @@ function reconcileAction(input) {
|
|
|
11751
11777
|
const entry = current.decisions.find((candidate) => candidate.id === input.decisionId);
|
|
11752
11778
|
if (!entry || entry.data.purpose !== "action")
|
|
11753
11779
|
return failure("not_found", "decision not found");
|
|
11754
|
-
if (!authorityDecisionMatches(authority, entry.data) || !
|
|
11780
|
+
if (!authorityDecisionMatches(authority, entry.data) || !approvalAuthorityMatches(input.store, current, entry.provenance, authority.provenance))
|
|
11755
11781
|
return failure("permission_denied", "native reconciliation caller does not match approval");
|
|
11756
11782
|
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)
|
|
11757
11783
|
return failure("permission_denied", "reconciliation decision binding is invalid");
|
|
@@ -12088,7 +12114,7 @@ var resolveBinding = (evidence, pin, currentCandidateId) => {
|
|
|
12088
12114
|
candidateId: evidence.candidateId ?? fallback
|
|
12089
12115
|
};
|
|
12090
12116
|
};
|
|
12091
|
-
var findingVerificationPasses = (findingCandidateId, evidence) => evidence.status === "passed" && (evidence.kind === "check" || evidence.kind === "review") && ((findingCandidateId ?? null) === null || evidence.candidateId
|
|
12117
|
+
var findingVerificationPasses = (findingCandidateId, evidence) => evidence.status === "passed" && (evidence.kind === "check" || evidence.kind === "review") && ((findingCandidateId ?? null) === null || evidence.candidateId != null);
|
|
12092
12118
|
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")));
|
|
12093
12119
|
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 }));
|
|
12094
12120
|
var evidenceMatchesRequirement = (kind, evidenceKind, dimension, ruleId) => {
|
|
@@ -12558,6 +12584,11 @@ function shouldDenyShellRoute(dir, command) {
|
|
|
12558
12584
|
}
|
|
12559
12585
|
// packages/workit-core/src/core/external-action.ts
|
|
12560
12586
|
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
12587
|
+
var chainStepSchema = union([
|
|
12588
|
+
string2().min(1),
|
|
12589
|
+
object({ branch: string2().min(1) }).strict(),
|
|
12590
|
+
object({ pr: literal(true) }).strict()
|
|
12591
|
+
]);
|
|
12561
12592
|
var externalActionSchema = discriminatedUnion("operation", [
|
|
12562
12593
|
object({
|
|
12563
12594
|
operation: literal("git.branch_setup"),
|
|
@@ -12572,7 +12603,7 @@ var externalActionSchema = discriminatedUnion("operation", [
|
|
|
12572
12603
|
operation: literal("git.commit"),
|
|
12573
12604
|
payload: object({
|
|
12574
12605
|
message: string2().min(1).optional(),
|
|
12575
|
-
plan_steps: array(
|
|
12606
|
+
plan_steps: array(chainStepSchema).min(1).max(32).optional(),
|
|
12576
12607
|
plan_branch: string2().min(1).optional()
|
|
12577
12608
|
}).strict()
|
|
12578
12609
|
}).strict(),
|
|
@@ -12676,7 +12707,7 @@ var planReservationLength = (operation, descriptorPayload) => {
|
|
|
12676
12707
|
const payload = descriptorPayload ?? {};
|
|
12677
12708
|
return Array.isArray(payload.plan_steps) ? payload.plan_steps.length : null;
|
|
12678
12709
|
};
|
|
12679
|
-
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).";
|
|
12710
|
+
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).";
|
|
12680
12711
|
var externalActionRef = (host, actor, descriptor) => ({
|
|
12681
12712
|
kind: "host",
|
|
12682
12713
|
host,
|
|
@@ -12690,12 +12721,12 @@ var externalActionState = (store, host, actor, operation) => {
|
|
|
12690
12721
|
if (!workspace.data)
|
|
12691
12722
|
return failure("not_found", "workspace not found");
|
|
12692
12723
|
const candidates = listed.data.flatMap((task) => {
|
|
12693
|
-
if (task.status !== "active" || task.workspaceId !== workspace.data.id
|
|
12724
|
+
if (task.status !== "active" || task.workspaceId !== workspace.data.id)
|
|
12694
12725
|
return [];
|
|
12695
12726
|
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 }));
|
|
12696
12727
|
});
|
|
12697
12728
|
if (candidates.length !== 1)
|
|
12698
|
-
return failure("permission_denied", candidates.length === 0 ? "no approved action is bound to
|
|
12729
|
+
return failure("permission_denied", candidates.length === 0 ? "no approved action is bound to the active task" : "multiple approved actions match the active task");
|
|
12699
12730
|
return success(workspace.data.revision, null, { ...candidates[0], workspace: workspace.data });
|
|
12700
12731
|
};
|
|
12701
12732
|
var priorExternalAction = (store, host, actor, operation, payload) => {
|
|
@@ -12775,8 +12806,6 @@ var approvedPlanCommit = (store, host, actor, message) => {
|
|
|
12775
12806
|
const matches = listed.data.flatMap((task) => {
|
|
12776
12807
|
if (task.status !== "active" || task.workspaceId !== workspace.data.id)
|
|
12777
12808
|
return [];
|
|
12778
|
-
if (task.intent.provenance.session?.kind !== "host" || task.intent.provenance.session.host !== host || task.intent.provenance.session.handle !== actor)
|
|
12779
|
-
return [];
|
|
12780
12809
|
return task.decisions.flatMap((entry) => {
|
|
12781
12810
|
const decision = entry.data;
|
|
12782
12811
|
if (decision.purpose !== "action" || decision.response !== "approved" || decision.revoked !== null)
|
|
@@ -12818,8 +12847,6 @@ var approvedChainStep = (store, host, actor, query) => {
|
|
|
12818
12847
|
const matches = listed.data.flatMap((task) => {
|
|
12819
12848
|
if (task.status !== "active" || task.workspaceId !== workspace.data.id)
|
|
12820
12849
|
return [];
|
|
12821
|
-
if (task.intent.provenance.session?.kind !== "host" || task.intent.provenance.session.host !== host || task.intent.provenance.session.handle !== actor)
|
|
12822
|
-
return [];
|
|
12823
12850
|
return task.decisions.flatMap((entry) => {
|
|
12824
12851
|
const decision = entry.data;
|
|
12825
12852
|
if (decision.purpose !== "action" || decision.response !== "approved" || decision.revoked !== null)
|
|
@@ -12976,7 +13003,7 @@ var branchSetupIntent = (descriptor) => {
|
|
|
12976
13003
|
var approvedBranchSetupIntent = (store, host, actor, operation) => {
|
|
12977
13004
|
const current = branchSetupIntent(operation);
|
|
12978
13005
|
if (!current)
|
|
12979
|
-
return failure("permission_denied", "no approved action is bound to
|
|
13006
|
+
return failure("permission_denied", "no approved action is bound to the active task");
|
|
12980
13007
|
const listed = store.listTasks();
|
|
12981
13008
|
const workspace = store.readWorkspace();
|
|
12982
13009
|
if (!listed.ok || !workspace.ok)
|
|
@@ -12984,7 +13011,7 @@ var approvedBranchSetupIntent = (store, host, actor, operation) => {
|
|
|
12984
13011
|
if (!workspace.data)
|
|
12985
13012
|
return failure("not_found", "workspace not found");
|
|
12986
13013
|
const candidates = listed.data.flatMap((task) => {
|
|
12987
|
-
if (task.status !== "active" || task.workspaceId !== workspace.data.id
|
|
13014
|
+
if (task.status !== "active" || task.workspaceId !== workspace.data.id)
|
|
12988
13015
|
return [];
|
|
12989
13016
|
return task.decisions.filter((entry) => entry.data.purpose === "action" && entry.data.response === "approved" && entry.data.revoked === null).map((entry) => ({ task, entry }));
|
|
12990
13017
|
});
|
|
@@ -12993,7 +13020,7 @@ var approvedBranchSetupIntent = (store, host, actor, operation) => {
|
|
|
12993
13020
|
return intent !== null && intent.target === current.target && intent.base === current.base && intent.remoteBase === current.remoteBase && intent.targetExists === current.targetExists;
|
|
12994
13021
|
});
|
|
12995
13022
|
if (matches.length !== 1)
|
|
12996
|
-
return failure("permission_denied", matches.length === 0 ? "no approved action is bound to
|
|
13023
|
+
return failure("permission_denied", matches.length === 0 ? "no approved action is bound to the active task" : "multiple approved actions match the active task");
|
|
12997
13024
|
return success(workspace.data.revision, null, { ...matches[0], workspace: workspace.data });
|
|
12998
13025
|
};
|
|
12999
13026
|
var priorResolvedDrift = (store, host, actor, operation, payload, currentResolved) => {
|
|
@@ -13550,6 +13577,7 @@ var verifyNativeWorker = (verifier, input, binding) => {
|
|
|
13550
13577
|
expectedWorkspaceRevision: expected.expectedWorkspaceRevision,
|
|
13551
13578
|
state: expected.state,
|
|
13552
13579
|
session: expected.session,
|
|
13580
|
+
report: expected.report ?? null,
|
|
13553
13581
|
provenance: result.data,
|
|
13554
13582
|
...binding
|
|
13555
13583
|
});
|
|
@@ -13560,7 +13588,7 @@ var takeWorker = (token, binding, expected) => {
|
|
|
13560
13588
|
verifiedWorkers.delete(token);
|
|
13561
13589
|
if (!value)
|
|
13562
13590
|
return null;
|
|
13563
|
-
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))
|
|
13591
|
+
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))
|
|
13564
13592
|
return null;
|
|
13565
13593
|
return value;
|
|
13566
13594
|
};
|
|
@@ -13601,6 +13629,8 @@ var applyWorkerLifecycle = (input) => {
|
|
|
13601
13629
|
return failure("invalid_transition", "worker already has an observed session");
|
|
13602
13630
|
if (notStarted && !["dispatching", "cancelling"].includes(entry.data.state))
|
|
13603
13631
|
return failure("invalid_transition", "worker launch was not claimed");
|
|
13632
|
+
if (input.report && input.state !== "stopped")
|
|
13633
|
+
return failure("invalid_input", "worker reports require a stopped worker");
|
|
13604
13634
|
if ((input.state === "running" || input.state === "cancelling" || input.state === "stopped" && !notStarted) && !input.session)
|
|
13605
13635
|
return failure("invalid_input", "running worker observations require a session");
|
|
13606
13636
|
if (entry.data.session && !sameValue(entry.data.session, input.session))
|
|
@@ -13617,12 +13647,13 @@ var applyWorkerLifecycle = (input) => {
|
|
|
13617
13647
|
data: {
|
|
13618
13648
|
...entry.data,
|
|
13619
13649
|
state: terminalCancel ? "cancelling" : input.state,
|
|
13620
|
-
session: input.session
|
|
13650
|
+
session: input.session,
|
|
13651
|
+
report: entry.data.report ?? input.report ?? null
|
|
13621
13652
|
}
|
|
13622
13653
|
};
|
|
13623
13654
|
const shouldClear = input.state === "stopped" && workspace.data.writer !== null && workspace.data.writer.owner.taskId === task.data.id && workspace.data.writer.owner.workerId === input.workerId;
|
|
13624
13655
|
const shouldUncertain = input.state === "unknown" && workspace.data.writer !== null && workspace.data.writer.owner.taskId === task.data.id && workspace.data.writer.owner.workerId === input.workerId;
|
|
13625
|
-
if (entry.data.state === nextEntry.data.state && sameValue(entry.data.session, input.session) && !shouldClear && !shouldUncertain)
|
|
13656
|
+
if (entry.data.state === nextEntry.data.state && sameValue(entry.data.session, input.session) && sameValue(entry.data.report, nextEntry.data.report) && !shouldClear && !shouldUncertain)
|
|
13626
13657
|
return success(task.data.revision, workspace.data.revision, entry);
|
|
13627
13658
|
const changed = input.store.mutateTaskAndWorkspace({
|
|
13628
13659
|
taskId: task.data.id,
|
|
@@ -14040,14 +14071,30 @@ class WorkitCore {
|
|
|
14040
14071
|
const listed = this.store.listTasks();
|
|
14041
14072
|
if (!listed.ok)
|
|
14042
14073
|
return listed;
|
|
14043
|
-
const
|
|
14044
|
-
|
|
14045
|
-
|
|
14046
|
-
|
|
14047
|
-
|
|
14048
|
-
|
|
14049
|
-
|
|
14050
|
-
|
|
14074
|
+
const status = input.status ?? "open";
|
|
14075
|
+
const limit = input.limit ?? 20;
|
|
14076
|
+
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);
|
|
14077
|
+
if (selected.length === 0)
|
|
14078
|
+
return success(null, null, []);
|
|
14079
|
+
const workspace = this.store.readWorkspace();
|
|
14080
|
+
if (!workspace.ok)
|
|
14081
|
+
return workspace;
|
|
14082
|
+
if (!workspace.data)
|
|
14083
|
+
return failure("not_found", "workspace not found");
|
|
14084
|
+
const tasks = selected.map((task) => ({
|
|
14085
|
+
id: task.id,
|
|
14086
|
+
revision: task.revision,
|
|
14087
|
+
workspaceRevision: workspace.data.revision,
|
|
14088
|
+
createdAt: task.createdAt,
|
|
14089
|
+
updatedAt: task.updatedAt,
|
|
14090
|
+
runtime: task.runtime ?? null,
|
|
14091
|
+
objective: task.intent.data.objective,
|
|
14092
|
+
status: task.status,
|
|
14093
|
+
closure: task.closure,
|
|
14094
|
+
progress: task.progress,
|
|
14095
|
+
writer: task.status === "closed" ? null : workspace.data.writer
|
|
14096
|
+
}));
|
|
14097
|
+
return success(null, null, tasks);
|
|
14051
14098
|
}
|
|
14052
14099
|
case "inspect": {
|
|
14053
14100
|
const task = this.store.readTask(input.taskId);
|
|
@@ -14056,7 +14103,7 @@ class WorkitCore {
|
|
|
14056
14103
|
const helper = this.helperTaskGuard(task.data, true);
|
|
14057
14104
|
if (!helper.ok)
|
|
14058
14105
|
return helper;
|
|
14059
|
-
if (input.view === "summary")
|
|
14106
|
+
if ((input.view ?? "summary") === "summary")
|
|
14060
14107
|
return this.summary(task.data);
|
|
14061
14108
|
return this.view(task.data);
|
|
14062
14109
|
}
|
|
@@ -14400,7 +14447,7 @@ class WorkitCore {
|
|
|
14400
14447
|
const evaluations = evaluateEvidence(task.data, current.data);
|
|
14401
14448
|
const verified = evidence.some((entry) => {
|
|
14402
14449
|
const evaluation = evaluations.find((item) => item.evidenceId === entry.id);
|
|
14403
|
-
return findingVerificationPasses(finding.data.candidateId, {
|
|
14450
|
+
return entry.recordedAt >= finding.recordedAt && findingVerificationPasses(finding.data.candidateId, {
|
|
14404
14451
|
kind: entry.data.kind,
|
|
14405
14452
|
candidateId: entry.data.candidateId,
|
|
14406
14453
|
status: evaluation?.status ?? "missing"
|
|
@@ -14480,7 +14527,18 @@ class WorkitCore {
|
|
|
14480
14527
|
if (task.data.status !== "active")
|
|
14481
14528
|
return failure("invalid_transition", "paused or closed tasks cannot assign workers");
|
|
14482
14529
|
if (!bindingCovers(task.data.intent.data.scope, input.assignment.scope))
|
|
14483
|
-
return failure("permission_denied", "worker assignment is outside task scope"
|
|
14530
|
+
return failure("permission_denied", "worker assignment is outside task scope", {
|
|
14531
|
+
fields: [
|
|
14532
|
+
{
|
|
14533
|
+
path: "assignment.scope",
|
|
14534
|
+
reason: `paths ${JSON.stringify(input.assignment.scope.paths)} must be contained by lead paths ${JSON.stringify(task.data.intent.data.scope.paths)}`
|
|
14535
|
+
},
|
|
14536
|
+
{
|
|
14537
|
+
path: "task.intent.scope",
|
|
14538
|
+
reason: `lead paths are ${JSON.stringify(task.data.intent.data.scope.paths)}`
|
|
14539
|
+
}
|
|
14540
|
+
]
|
|
14541
|
+
});
|
|
14484
14542
|
if (input.assignment.decisionIds.some((id) => !task.data.decisions.some((entry) => entry.id === id)))
|
|
14485
14543
|
return failure("invalid_input", "worker assignment references an unknown decision");
|
|
14486
14544
|
if (input.assignment.requirementIds.some((id) => !task.data.policy?.requirements.some((requirement) => requirement.id === id)))
|
|
@@ -14687,7 +14745,15 @@ class WorkitCore {
|
|
|
14687
14745
|
...item,
|
|
14688
14746
|
recordedAt: mutation.now,
|
|
14689
14747
|
provenance: attested.data,
|
|
14690
|
-
data: {
|
|
14748
|
+
data: {
|
|
14749
|
+
...item.data,
|
|
14750
|
+
state: "dispatching",
|
|
14751
|
+
coordinator: {
|
|
14752
|
+
kind: "host",
|
|
14753
|
+
host: this.context.caller.host,
|
|
14754
|
+
handle: this.context.caller.actor
|
|
14755
|
+
}
|
|
14756
|
+
}
|
|
14691
14757
|
} : item)
|
|
14692
14758
|
});
|
|
14693
14759
|
}
|
|
@@ -14763,6 +14829,7 @@ class WorkitCore {
|
|
|
14763
14829
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
14764
14830
|
state: "stopped",
|
|
14765
14831
|
session: null,
|
|
14832
|
+
report: null,
|
|
14766
14833
|
provenance: attested.data,
|
|
14767
14834
|
owner: this.authorityOwner,
|
|
14768
14835
|
store: this.store,
|
|
@@ -15070,10 +15137,12 @@ class WorkitCore {
|
|
|
15070
15137
|
return workspace;
|
|
15071
15138
|
if (!workspace.data)
|
|
15072
15139
|
return failure("not_found", "workspace not found");
|
|
15073
|
-
const
|
|
15140
|
+
const historical = task.status === "closed" ? task.candidates.at(-1) : undefined;
|
|
15141
|
+
const current = historical ? success(null, null, historical) : captureCandidate(this.store.root, task.intent.data.scope, environment());
|
|
15074
15142
|
if (!current.ok)
|
|
15075
15143
|
return current;
|
|
15076
|
-
const
|
|
15144
|
+
const evaluationWorkspace = task.status === "closed" ? { ...workspace.data, writer: null } : workspace.data;
|
|
15145
|
+
const requirements = evaluateRequirements(task, evaluationWorkspace, this.context.capabilities, current.data, this.store.root, this.context.caller);
|
|
15077
15146
|
return success(task.revision, workspace.data.revision, {
|
|
15078
15147
|
id: task.id,
|
|
15079
15148
|
revision: task.revision,
|
|
@@ -15087,7 +15156,7 @@ class WorkitCore {
|
|
|
15087
15156
|
progress: task.progress,
|
|
15088
15157
|
policy: task.policy,
|
|
15089
15158
|
requirements,
|
|
15090
|
-
writer:
|
|
15159
|
+
writer: evaluationWorkspace.writer
|
|
15091
15160
|
});
|
|
15092
15161
|
}
|
|
15093
15162
|
view(task) {
|
|
@@ -15096,13 +15165,15 @@ class WorkitCore {
|
|
|
15096
15165
|
return workspace;
|
|
15097
15166
|
if (!workspace.data)
|
|
15098
15167
|
return failure("not_found", "workspace not found");
|
|
15099
|
-
const
|
|
15168
|
+
const historical = task.status === "closed" ? task.candidates.at(-1) : undefined;
|
|
15169
|
+
const current = historical ? success(null, null, historical) : captureCandidate(this.store.root, task.intent.data.scope, environment());
|
|
15100
15170
|
if (!current.ok)
|
|
15101
15171
|
return current;
|
|
15102
|
-
const
|
|
15172
|
+
const evaluationWorkspace = task.status === "closed" ? { ...workspace.data, writer: null } : workspace.data;
|
|
15173
|
+
const requirements = evaluateRequirements(task, evaluationWorkspace, this.context.capabilities, current.data, this.store.root, this.context.caller);
|
|
15103
15174
|
return success(task.revision, workspace.data.revision, {
|
|
15104
15175
|
task,
|
|
15105
|
-
workspace:
|
|
15176
|
+
workspace: evaluationWorkspace,
|
|
15106
15177
|
capabilities: this.context.capabilities,
|
|
15107
15178
|
requirements,
|
|
15108
15179
|
evidence: evaluateEvidence(task, current.data)
|
|
@@ -15193,8 +15264,6 @@ class WorkitCore {
|
|
|
15193
15264
|
return helper;
|
|
15194
15265
|
if (status === "paused" && task.data.status !== "active" || status === "active" && task.data.status !== "paused")
|
|
15195
15266
|
return failure("invalid_transition", `cannot transition ${task.data.status} to ${status}`);
|
|
15196
|
-
if (status === "active" && input.authorityRefs.length === 0)
|
|
15197
|
-
return failure("permission_denied", "resuming a task requires authority references");
|
|
15198
15267
|
const workspace = this.store.readWorkspace();
|
|
15199
15268
|
if (!workspace.ok)
|
|
15200
15269
|
return workspace;
|
|
@@ -15224,7 +15293,7 @@ class WorkitCore {
|
|
|
15224
15293
|
return failure("needs_input", "imported task requires fresh destination evidence");
|
|
15225
15294
|
if (current.data.blockers.some((blocker) => blocker.dependentAction === "resume" || blocker.reason === "worker state requires reconciliation"))
|
|
15226
15295
|
return failure("recovery_required", "imported task requires resume reconciliation");
|
|
15227
|
-
if (!validResumeApproval(task.data, workspace.data, input.authorityRefs, this.context))
|
|
15296
|
+
if (!validResumeApproval(task.data, workspace.data, input.authorityRefs ?? [], this.context))
|
|
15228
15297
|
return failure("permission_denied", "imported resume requires native destination approval");
|
|
15229
15298
|
resumeCandidate = current.data.candidate;
|
|
15230
15299
|
}
|
|
@@ -15354,7 +15423,7 @@ class WorkitCore {
|
|
|
15354
15423
|
task: (value, context) => success(context.revision, null, {
|
|
15355
15424
|
...value,
|
|
15356
15425
|
status: "closed",
|
|
15357
|
-
candidates:
|
|
15426
|
+
candidates: taskForView.candidates,
|
|
15358
15427
|
closure: {
|
|
15359
15428
|
outcome: closure.data.outcome,
|
|
15360
15429
|
at: context.now,
|
|
@@ -15731,31 +15800,25 @@ var packageRoot2 = () => {
|
|
|
15731
15800
|
var assetsRoot = () => path13.join(packageRoot2(), "assets");
|
|
15732
15801
|
|
|
15733
15802
|
// packages/workit-opencode/src/stale-sources.ts
|
|
15734
|
-
import { existsSync as existsSync8 } from "node:fs";
|
|
15803
|
+
import { existsSync as existsSync8, readdirSync as readdirSync5 } from "node:fs";
|
|
15735
15804
|
import path14 from "node:path";
|
|
15736
15805
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
15737
|
-
var
|
|
15738
|
-
|
|
15739
|
-
|
|
15740
|
-
|
|
15741
|
-
|
|
15742
|
-
|
|
15743
|
-
|
|
15744
|
-
|
|
15745
|
-
var coreDir = path14.join(packageRoot2(), "..", "workit-core", "src", "core");
|
|
15806
|
+
var typescriptFiles = (directory) => {
|
|
15807
|
+
if (!existsSync8(directory))
|
|
15808
|
+
return [];
|
|
15809
|
+
return readdirSync5(directory, { withFileTypes: true }).flatMap((entry) => {
|
|
15810
|
+
const file = path14.join(directory, entry.name);
|
|
15811
|
+
return entry.isDirectory() ? typescriptFiles(file) : entry.isFile() && file.endsWith(".ts") ? [file] : [];
|
|
15812
|
+
});
|
|
15813
|
+
};
|
|
15746
15814
|
var pluginSourceFiles = [
|
|
15747
|
-
|
|
15748
|
-
|
|
15749
|
-
|
|
15750
|
-
"
|
|
15751
|
-
"
|
|
15752
|
-
|
|
15753
|
-
|
|
15754
|
-
"workers.ts",
|
|
15755
|
-
"authority.ts",
|
|
15756
|
-
"methods.ts"
|
|
15757
|
-
].map((file) => path14.join(coreDir, file))
|
|
15758
|
-
].filter((file) => existsSync8(file));
|
|
15815
|
+
...new Set([
|
|
15816
|
+
fileURLToPath3(import.meta.url),
|
|
15817
|
+
...typescriptFiles(path14.join(packageRoot2(), "src")),
|
|
15818
|
+
path14.join(packageRoot2(), "..", "workit-core", "src", "core.ts"),
|
|
15819
|
+
...typescriptFiles(path14.join(packageRoot2(), "..", "workit-core", "src", "core"))
|
|
15820
|
+
])
|
|
15821
|
+
].filter((file) => existsSync8(file)).sort();
|
|
15759
15822
|
|
|
15760
15823
|
// packages/workit-opencode/src/tools/repo.ts
|
|
15761
15824
|
import { realpathSync as realpathSync9 } from "node:fs";
|
|
@@ -28471,7 +28534,7 @@ function parseVerifyOutput(stdout) {
|
|
|
28471
28534
|
|
|
28472
28535
|
// packages/workit-core/src/core/repo-context.ts
|
|
28473
28536
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
28474
|
-
import { existsSync as existsSync9, readFileSync as readFileSync8, readdirSync as
|
|
28537
|
+
import { existsSync as existsSync9, readFileSync as readFileSync8, readdirSync as readdirSync6 } from "node:fs";
|
|
28475
28538
|
import path16 from "node:path";
|
|
28476
28539
|
var isSafeContextRange = (value) => value.length <= 4096 && (() => {
|
|
28477
28540
|
try {
|
|
@@ -28586,7 +28649,7 @@ var printSection = (title) => `
|
|
|
28586
28649
|
`;
|
|
28587
28650
|
var probeCaseInsensitive = (dir, wanted) => {
|
|
28588
28651
|
try {
|
|
28589
|
-
for (const entry of
|
|
28652
|
+
for (const entry of readdirSync6(dir)) {
|
|
28590
28653
|
if (entry.toLowerCase() === wanted.toLowerCase())
|
|
28591
28654
|
return entry;
|
|
28592
28655
|
}
|
|
@@ -28655,7 +28718,7 @@ var documentationFiles = (cwd) => {
|
|
|
28655
28718
|
return;
|
|
28656
28719
|
let entries;
|
|
28657
28720
|
try {
|
|
28658
|
-
entries =
|
|
28721
|
+
entries = readdirSync6(dir, { withFileTypes: true });
|
|
28659
28722
|
} catch {
|
|
28660
28723
|
return;
|
|
28661
28724
|
}
|
|
@@ -29488,7 +29551,7 @@ import {
|
|
|
29488
29551
|
mkdirSync as mkdirSync5,
|
|
29489
29552
|
mkdtempSync,
|
|
29490
29553
|
readFileSync as readFileSync12,
|
|
29491
|
-
readdirSync as
|
|
29554
|
+
readdirSync as readdirSync7,
|
|
29492
29555
|
realpathSync as realpathSync8,
|
|
29493
29556
|
renameSync as renameSync2,
|
|
29494
29557
|
rmSync,
|
|
@@ -31754,7 +31817,7 @@ var readExternalContext = async (root, payload) => {
|
|
|
31754
31817
|
}
|
|
31755
31818
|
}
|
|
31756
31819
|
};
|
|
31757
|
-
var localAction = (operation) => operation === "git.branch_setup" || operation === "git.commit" || operation === "git.push" || operation === "hosting.pull_request" || operation === "changelog.apply";
|
|
31820
|
+
var localAction = (operation) => operation === "git.branch_setup" || operation === "git.commit" || operation === "git.push" || operation === "hosting.pull_request" || operation === "hosting.merge" || operation === "changelog.apply";
|
|
31758
31821
|
var assertLocalExternalActionWriter = (root, caller, paths = ["."]) => {
|
|
31759
31822
|
const store = new TaskStore(root);
|
|
31760
31823
|
const listed = store.listTasks();
|
|
@@ -31765,7 +31828,7 @@ var assertLocalExternalActionWriter = (root, caller, paths = ["."]) => {
|
|
|
31765
31828
|
return workspace;
|
|
31766
31829
|
if (!workspace.data)
|
|
31767
31830
|
return failure("not_found", "workspace not found");
|
|
31768
|
-
const candidates = listed.data.filter((task) => task
|
|
31831
|
+
const candidates = listed.data.filter((task) => currentWriterOwnsTask(task, workspace.data, caller));
|
|
31769
31832
|
if (candidates.length !== 1)
|
|
31770
31833
|
return failure("permission_denied", "local external action requires exactly one active task");
|
|
31771
31834
|
const allowed = assertProductWriteAllowed({
|
|
@@ -31775,7 +31838,7 @@ var assertLocalExternalActionWriter = (root, caller, paths = ["."]) => {
|
|
|
31775
31838
|
paths,
|
|
31776
31839
|
store
|
|
31777
31840
|
});
|
|
31778
|
-
return allowed
|
|
31841
|
+
return allowed;
|
|
31779
31842
|
};
|
|
31780
31843
|
var safePath = (root, value) => {
|
|
31781
31844
|
try {
|
|
@@ -31787,13 +31850,20 @@ var safePath = (root, value) => {
|
|
|
31787
31850
|
var branchCreationBaseline = (root, target, base) => {
|
|
31788
31851
|
const exists = gitValue(root, ["rev-parse", "--verify", "--quiet", `refs/heads/${target}`]) !== null;
|
|
31789
31852
|
if (exists)
|
|
31790
|
-
return {
|
|
31853
|
+
return {
|
|
31854
|
+
base_branch: base,
|
|
31855
|
+
target_exists: true,
|
|
31856
|
+
remote_base: null,
|
|
31857
|
+
target_head: gitValue(root, ["rev-parse", `refs/heads/${target}`])
|
|
31858
|
+
};
|
|
31791
31859
|
const remote = run4(root, ["ls-remote", "origin", `refs/heads/${base}`]);
|
|
31792
31860
|
const sha = remote.exitCode === 0 ? remote.stdout.trim().split(/\s+/)[0] : "";
|
|
31861
|
+
const remoteBase = /^[0-9a-f]{40}$/.test(sha ?? "") ? sha : null;
|
|
31793
31862
|
return {
|
|
31794
31863
|
base_branch: base,
|
|
31795
31864
|
target_exists: false,
|
|
31796
|
-
remote_base:
|
|
31865
|
+
remote_base: remoteBase,
|
|
31866
|
+
target_head: remoteBase
|
|
31797
31867
|
};
|
|
31798
31868
|
};
|
|
31799
31869
|
var shortSha = (value) => typeof value === "string" && value ? value.slice(0, 8) : "unknown";
|
|
@@ -31904,7 +31974,7 @@ var actionProposalQuestion = (request, descriptorPayload) => {
|
|
|
31904
31974
|
var approvedResolvedExternalAction = (content) => {
|
|
31905
31975
|
try {
|
|
31906
31976
|
const value = JSON.parse(content);
|
|
31907
|
-
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")
|
|
31977
|
+
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")
|
|
31908
31978
|
return failure("invalid_input", "approved action is not a reconciliable external action");
|
|
31909
31979
|
if (!value.payload || typeof value.payload !== "object")
|
|
31910
31980
|
return failure("invalid_input", "approved action payload is incomplete");
|
|
@@ -31915,18 +31985,24 @@ var approvedResolvedExternalAction = (content) => {
|
|
|
31915
31985
|
if (!request.ok || !resolved || typeof resolved !== "object")
|
|
31916
31986
|
return failure("invalid_input", "approved hosting target is incomplete");
|
|
31917
31987
|
const target = resolved;
|
|
31988
|
+
if (value.operation === "git.commit" && (typeof target.head !== "string" || typeof target.staged !== "string" || typeof payload.message !== "string"))
|
|
31989
|
+
return failure("invalid_input", "approved Git commit target is incomplete");
|
|
31990
|
+
if (value.operation === "git.push" && (typeof target.branch !== "string" || typeof target.commit !== "string" || typeof target.remote !== "string"))
|
|
31991
|
+
return failure("invalid_input", "approved Git push target is incomplete");
|
|
31992
|
+
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")))
|
|
31993
|
+
return failure("invalid_input", "approved Git branch target is incomplete");
|
|
31918
31994
|
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"))
|
|
31919
31995
|
return failure("invalid_input", "approved hosting target is incomplete");
|
|
31920
31996
|
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"))
|
|
31921
31997
|
return failure("invalid_input", "approved hosting target is incomplete");
|
|
31922
31998
|
if (value.operation === "changelog.apply" && (typeof target.target !== "string" || typeof target.beforeDigest !== "string" || typeof target.beforeExists !== "boolean" || typeof target.afterDigest !== "string"))
|
|
31923
31999
|
return failure("invalid_input", "approved changelog target is incomplete");
|
|
31924
|
-
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)))
|
|
32000
|
+
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)))
|
|
31925
32001
|
return failure("invalid_input", "approved YouTrack target is incomplete");
|
|
31926
32002
|
return success(null, null, {
|
|
31927
32003
|
request: request.data,
|
|
31928
32004
|
descriptorPayload: value.payload,
|
|
31929
|
-
marker:
|
|
32005
|
+
...typeof target.marker === "string" ? { marker: target.marker } : {}
|
|
31930
32006
|
});
|
|
31931
32007
|
} catch {
|
|
31932
32008
|
return failure("invalid_input", "approved action descriptor is invalid");
|
|
@@ -31952,12 +32028,13 @@ var unknownHostingEvidence = (operation) => failure("external_outcome_unknown",
|
|
|
31952
32028
|
outcome: "unknown"
|
|
31953
32029
|
});
|
|
31954
32030
|
var readHostingAction = async (root, resolved, actionRef) => {
|
|
31955
|
-
|
|
31956
|
-
|
|
32031
|
+
const operation = resolved.request.operation;
|
|
32032
|
+
if (operation !== "hosting.pull_request" && operation !== "hosting.merge" || !resolved.marker)
|
|
32033
|
+
return unknownHostingEvidence(operation);
|
|
31957
32034
|
try {
|
|
31958
32035
|
const cfg = vcsConfig("load", root);
|
|
31959
32036
|
if (!cfg.ok || !cfg.tokenPath || !cfg.provider || cfg.provider !== "github" && cfg.provider !== "gitlab")
|
|
31960
|
-
return unknownHostingEvidence(
|
|
32037
|
+
return unknownHostingEvidence(operation);
|
|
31961
32038
|
const remote = pushRemote(root);
|
|
31962
32039
|
const repo = remote ? remoteRepo(remote) : null;
|
|
31963
32040
|
const approved = resolved.descriptorPayload;
|
|
@@ -31976,7 +32053,7 @@ var readHostingAction = async (root, resolved, actionRef) => {
|
|
|
31976
32053
|
}
|
|
31977
32054
|
})();
|
|
31978
32055
|
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)
|
|
31979
|
-
return unknownHostingEvidence(
|
|
32056
|
+
return unknownHostingEvidence(operation);
|
|
31980
32057
|
const token = fs13.readFileSync(cfg.tokenPath, "utf8").trim();
|
|
31981
32058
|
const headers = { Authorization: `Bearer ${token}`, Accept: "application/vnd.github+json" };
|
|
31982
32059
|
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`;
|
|
@@ -31984,14 +32061,14 @@ var readHostingAction = async (root, resolved, actionRef) => {
|
|
|
31984
32061
|
headers: cfg.provider === "gitlab" ? { "PRIVATE-TOKEN": token } : headers
|
|
31985
32062
|
});
|
|
31986
32063
|
if (!response.ok)
|
|
31987
|
-
return unknownHostingEvidence(
|
|
32064
|
+
return unknownHostingEvidence(operation);
|
|
31988
32065
|
const values = await response.json();
|
|
31989
32066
|
if (!Array.isArray(values) || values.length >= 100)
|
|
31990
|
-
return unknownHostingEvidence(
|
|
32067
|
+
return unknownHostingEvidence(operation);
|
|
31991
32068
|
const records = [];
|
|
31992
32069
|
for (const item of values) {
|
|
31993
32070
|
if (!item || typeof item !== "object" || Array.isArray(item))
|
|
31994
|
-
return unknownHostingEvidence(
|
|
32071
|
+
return unknownHostingEvidence(operation);
|
|
31995
32072
|
const record = item;
|
|
31996
32073
|
const body = record.body ?? record.description;
|
|
31997
32074
|
const base = record.base;
|
|
@@ -32003,7 +32080,7 @@ var readHostingAction = async (root, resolved, actionRef) => {
|
|
|
32003
32080
|
const shaValue = cfg.provider === "github" && githubShape ? head.sha : record.sha;
|
|
32004
32081
|
const idValue = record.number ?? record.iid ?? record.id;
|
|
32005
32082
|
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")
|
|
32006
|
-
return unknownHostingEvidence(
|
|
32083
|
+
return unknownHostingEvidence(operation);
|
|
32007
32084
|
records.push(record);
|
|
32008
32085
|
}
|
|
32009
32086
|
const matches = records.filter((item) => {
|
|
@@ -32013,15 +32090,23 @@ var readHostingAction = async (root, resolved, actionRef) => {
|
|
|
32013
32090
|
const itemTarget = cfg.provider === "github" ? base?.ref : item.target_branch;
|
|
32014
32091
|
const itemSource = cfg.provider === "github" ? head?.ref : item.source_branch;
|
|
32015
32092
|
const itemSha = cfg.provider === "github" ? head?.sha : item.sha;
|
|
32016
|
-
|
|
32093
|
+
const merged = cfg.provider === "github" ? typeof item.merged_at === "string" : item.state === "merged" || typeof item.merged_at === "string";
|
|
32094
|
+
return (operation === "hosting.merge" ? merged : body.includes(resolved.marker)) && itemTarget === target && itemSource === branch && itemSha === sourceCommit;
|
|
32017
32095
|
});
|
|
32096
|
+
if (operation === "hosting.merge" && records.some((item) => {
|
|
32097
|
+
const base = item.base;
|
|
32098
|
+
const head = item.head;
|
|
32099
|
+
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");
|
|
32100
|
+
}))
|
|
32101
|
+
return unknownHostingEvidence(operation);
|
|
32018
32102
|
if (matches.length !== 1)
|
|
32019
|
-
return unknownHostingEvidence(
|
|
32103
|
+
return unknownHostingEvidence(operation);
|
|
32020
32104
|
const item = matches[0];
|
|
32021
32105
|
const id = item.number ?? item.iid ?? item.id;
|
|
32022
32106
|
if (typeof id !== "string" && typeof id !== "number")
|
|
32023
|
-
return unknownHostingEvidence(
|
|
32107
|
+
return unknownHostingEvidence(operation);
|
|
32024
32108
|
const evidenceDigest = sha256({
|
|
32109
|
+
operation,
|
|
32025
32110
|
provider: cfg.provider,
|
|
32026
32111
|
id,
|
|
32027
32112
|
marker: resolved.marker,
|
|
@@ -32042,7 +32127,7 @@ var readHostingAction = async (root, resolved, actionRef) => {
|
|
|
32042
32127
|
data: { provider: cfg.provider, id }
|
|
32043
32128
|
});
|
|
32044
32129
|
} catch {
|
|
32045
|
-
return unknownHostingEvidence(
|
|
32130
|
+
return unknownHostingEvidence(operation);
|
|
32046
32131
|
}
|
|
32047
32132
|
};
|
|
32048
32133
|
var appendMarker = (text, marker) => `${text}${text ? `
|
|
@@ -32132,7 +32217,70 @@ var readYouTrackAction = async (root, resolved, actionRef) => {
|
|
|
32132
32217
|
return unknownHostingEvidence(operation);
|
|
32133
32218
|
}
|
|
32134
32219
|
};
|
|
32135
|
-
var
|
|
32220
|
+
var readLocalAction = async (root, resolved, actionRef) => {
|
|
32221
|
+
const operation = resolved.request.operation;
|
|
32222
|
+
const target = resolved.descriptorPayload.resolved;
|
|
32223
|
+
if (!target)
|
|
32224
|
+
return unknownHostingEvidence(operation);
|
|
32225
|
+
let observed = null;
|
|
32226
|
+
if (operation === "git.commit") {
|
|
32227
|
+
const commit = gitValue(root, ["rev-parse", "HEAD"]);
|
|
32228
|
+
const parent = commit ? gitValue(root, ["rev-parse", `${commit}^`]) : null;
|
|
32229
|
+
const message = commit ? gitValue(root, ["show", "-s", "--format=%B", commit]) : null;
|
|
32230
|
+
const diff = commit && parent ? gitRaw(root, ["diff", "--raw", "-z", "--no-ext-diff", "--no-textconv", parent, commit]) : null;
|
|
32231
|
+
if (commit && parent === target.head && message === resolved.request.payload.message && diff !== null && sha256(diff) === target.staged)
|
|
32232
|
+
observed = { commit, parent, message, staged: target.staged };
|
|
32233
|
+
} else if (operation === "git.push") {
|
|
32234
|
+
const branch = target.branch;
|
|
32235
|
+
const commit = target.commit;
|
|
32236
|
+
const approvedRemote = target.remote;
|
|
32237
|
+
const currentRemote = pushRemote(root);
|
|
32238
|
+
const currentIdentity = currentRemote ? credentialFreeRemote(currentRemote) : null;
|
|
32239
|
+
if (typeof branch === "string" && typeof commit === "string" && typeof approvedRemote === "string" && currentIdentity === approvedRemote) {
|
|
32240
|
+
const remote = run4(root, ["ls-remote", currentRemote, `refs/heads/${branch}`]);
|
|
32241
|
+
const remoteCommit = remote.exitCode === 0 ? remote.stdout.trim().split(/\s+/)[0] : null;
|
|
32242
|
+
if (remoteCommit === commit)
|
|
32243
|
+
observed = { branch, commit, remote: approvedRemote };
|
|
32244
|
+
}
|
|
32245
|
+
} else if (operation === "git.branch_setup") {
|
|
32246
|
+
const targetBranch = resolved.request.payload.target_branch;
|
|
32247
|
+
const sdd = resolved.request.payload.sdd_dir;
|
|
32248
|
+
if (resolved.request.payload.action === "reapply_stash" && typeof sdd === "string") {
|
|
32249
|
+
try {
|
|
32250
|
+
const manifest = JSON.parse(fs13.readFileSync(path25.join(sdd, "manifest.json"), "utf8"));
|
|
32251
|
+
const stashes = gitRaw(root, ["stash", "list", "--format=%H"]);
|
|
32252
|
+
if (typeof target.stash_commit === "string" && manifest.stash_ref === undefined && stashes !== null && !stashes.split(`
|
|
32253
|
+
`).includes(target.stash_commit))
|
|
32254
|
+
observed = { stash_commit: target.stash_commit };
|
|
32255
|
+
} catch {}
|
|
32256
|
+
} 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) {
|
|
32257
|
+
try {
|
|
32258
|
+
const manifest = JSON.parse(fs13.readFileSync(path25.join(sdd, "manifest.json"), "utf8"));
|
|
32259
|
+
if (manifest.branch === targetBranch)
|
|
32260
|
+
observed = { branch: targetBranch };
|
|
32261
|
+
} catch {}
|
|
32262
|
+
}
|
|
32263
|
+
} else if (operation === "changelog.apply") {
|
|
32264
|
+
const afterDigest = target.afterDigest;
|
|
32265
|
+
const preview = changelogApplyPreview({ ...resolved.request.payload, workspace_root: root });
|
|
32266
|
+
if (!preview.error && typeof afterDigest === "string" && preview.beforeDigest === afterDigest)
|
|
32267
|
+
observed = { target: target.target, afterDigest };
|
|
32268
|
+
}
|
|
32269
|
+
if (!observed)
|
|
32270
|
+
return unknownHostingEvidence(operation);
|
|
32271
|
+
const evidenceDigest = sha256({ operation, observed });
|
|
32272
|
+
return success(null, null, {
|
|
32273
|
+
outcome: "succeeded",
|
|
32274
|
+
evidenceDigest,
|
|
32275
|
+
observation: {
|
|
32276
|
+
kind: "provider_read",
|
|
32277
|
+
actionRef,
|
|
32278
|
+
outcome: "succeeded",
|
|
32279
|
+
evidenceDigest
|
|
32280
|
+
}
|
|
32281
|
+
});
|
|
32282
|
+
};
|
|
32283
|
+
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);
|
|
32136
32284
|
var youTrackBase = () => {
|
|
32137
32285
|
try {
|
|
32138
32286
|
const config = JSON.parse(fs13.readFileSync(youTrackConfigPath(), "utf8"));
|
|
@@ -32190,6 +32338,16 @@ var resolveExternalActionRequest = (root, request, preservedDateMs) => {
|
|
|
32190
32338
|
return failure("storage_error", "current Git commit could not be resolved");
|
|
32191
32339
|
const dirty = Boolean((gitRaw(root, ["status", "--short"]) ?? "").trim());
|
|
32192
32340
|
const creation = action === "reapply_stash" || !target_branch ? { base_branch: policy.defaultTargetBranch } : branchCreationBaseline(root, target_branch, policy.defaultTargetBranch);
|
|
32341
|
+
let stashTarget;
|
|
32342
|
+
if (action === "reapply_stash") {
|
|
32343
|
+
try {
|
|
32344
|
+
const manifest = JSON.parse(fs13.readFileSync(path25.join(sdd_dir, "manifest.json"), "utf8"));
|
|
32345
|
+
const stash_ref = typeof manifest.stash_ref === "string" ? manifest.stash_ref : "";
|
|
32346
|
+
const stash_commit = stash_ref ? gitValue(root, ["rev-parse", stash_ref]) : null;
|
|
32347
|
+
if (stash_ref && stash_commit)
|
|
32348
|
+
stashTarget = { stash_ref, stash_commit };
|
|
32349
|
+
} catch {}
|
|
32350
|
+
}
|
|
32193
32351
|
const normalized = {
|
|
32194
32352
|
operation: request.operation,
|
|
32195
32353
|
payload: {
|
|
@@ -32204,7 +32362,7 @@ var resolveExternalActionRequest = (root, request, preservedDateMs) => {
|
|
|
32204
32362
|
request: normalized,
|
|
32205
32363
|
descriptorPayload: {
|
|
32206
32364
|
...normalized.payload,
|
|
32207
|
-
resolved: { head, dirty, ...creation }
|
|
32365
|
+
resolved: { head, dirty, ...creation, ...stashTarget }
|
|
32208
32366
|
}
|
|
32209
32367
|
});
|
|
32210
32368
|
}
|
|
@@ -32356,7 +32514,7 @@ var resolveExternalActionRequest = (root, request, preservedDateMs) => {
|
|
|
32356
32514
|
outcome: "not_started",
|
|
32357
32515
|
fields: [{ path: "source_branch", reason: "protected branch" }]
|
|
32358
32516
|
});
|
|
32359
|
-
const source_commit = gitValue(root, ["rev-parse", "
|
|
32517
|
+
const source_commit = source_branch ? gitValue(root, ["rev-parse", "--verify", `refs/heads/${source_branch}`]) : null;
|
|
32360
32518
|
const remote = pushRemote(root);
|
|
32361
32519
|
const identity = remote ? credentialFreeRemote(remote) : null;
|
|
32362
32520
|
if (!source_branch || !source_commit || !identity || remoteProvider2(identity) !== null && remoteProvider2(identity) !== String(vcsConfig("load", root).provider).toLowerCase())
|
|
@@ -32644,7 +32802,17 @@ var executeResolvedExternalAction = async (resolved, root, step, caller) => {
|
|
|
32644
32802
|
const workText = fresh.data.descriptorPayload.resolved?.workText;
|
|
32645
32803
|
const beforeDigest = typeof resolvedDetails.resolved?.beforeDigest === "string" ? resolvedDetails.resolved.beforeDigest : undefined;
|
|
32646
32804
|
const beforeExists = typeof resolvedDetails.resolved?.beforeExists === "boolean" ? resolvedDetails.resolved.beforeExists : undefined;
|
|
32647
|
-
|
|
32805
|
+
const result = await executeConcreteExternalAction(fresh.data.request, root, fresh.data.marker, resolvedDate, step, typeof workText === "string" ? workText : undefined, caller, beforeDigest, beforeExists);
|
|
32806
|
+
if (!result.ok && result.code === "external_outcome_unknown" && caller) {
|
|
32807
|
+
const observed = await readLocalAction(root, fresh.data, {
|
|
32808
|
+
kind: "host",
|
|
32809
|
+
host: caller.host,
|
|
32810
|
+
handle: `local-recovery:${fresh.data.request.operation}`
|
|
32811
|
+
});
|
|
32812
|
+
if (observed.ok)
|
|
32813
|
+
return success(null, null, { recovered: true });
|
|
32814
|
+
}
|
|
32815
|
+
return result;
|
|
32648
32816
|
};
|
|
32649
32817
|
|
|
32650
32818
|
// packages/workit-opencode/src/shared/session.ts
|
|
@@ -32702,6 +32870,7 @@ var purposeForQuestion = (question) => {
|
|
|
32702
32870
|
class NativeReceiptStore {
|
|
32703
32871
|
#receipts = new Map;
|
|
32704
32872
|
#observations = new WeakSet;
|
|
32873
|
+
#reservations = new WeakMap;
|
|
32705
32874
|
#pending = new Map;
|
|
32706
32875
|
#now;
|
|
32707
32876
|
constructor(options = {}) {
|
|
@@ -32774,7 +32943,7 @@ class NativeReceiptStore {
|
|
|
32774
32943
|
const args = input.args;
|
|
32775
32944
|
this.mint(input.sessionID, input.callID, args?.questions, answers);
|
|
32776
32945
|
}
|
|
32777
|
-
|
|
32946
|
+
reserve(sessionID, purpose, expected = {}) {
|
|
32778
32947
|
const queue = this.#receipts.get(sessionID) ?? [];
|
|
32779
32948
|
for (let i = queue.length - 1;i >= 0; i -= 1) {
|
|
32780
32949
|
const receipt = queue[i];
|
|
@@ -32782,11 +32951,9 @@ class NativeReceiptStore {
|
|
|
32782
32951
|
continue;
|
|
32783
32952
|
if (expected.callID !== undefined && receipt.callID !== expected.callID || expected.selectedLabel !== undefined && normalizeReceiptLabel(receipt.selectedLabel) !== normalizeReceiptLabel(expected.selectedLabel) || expected.selectedDescription !== undefined && receipt.selectedDescription !== expected.selectedDescription || expected.decisionPurpose !== undefined && receipt.decisionPurpose !== expected.decisionPurpose || expected.contentDigest !== undefined && receipt.contentDigest !== expected.contentDigest || expected.question !== undefined && receipt.question !== expected.question)
|
|
32784
32953
|
continue;
|
|
32785
|
-
queue.splice(i, 1);
|
|
32786
|
-
if (!queue.length)
|
|
32787
|
-
this.#receipts.delete(sessionID);
|
|
32788
32954
|
const observation = { receipt };
|
|
32789
32955
|
this.#observations.add(observation);
|
|
32956
|
+
this.#reservations.set(observation, receipt);
|
|
32790
32957
|
return { ok: true, receipt, observation };
|
|
32791
32958
|
}
|
|
32792
32959
|
return {
|
|
@@ -32812,6 +32979,26 @@ class NativeReceiptStore {
|
|
|
32812
32979
|
})()
|
|
32813
32980
|
};
|
|
32814
32981
|
}
|
|
32982
|
+
commit(observation) {
|
|
32983
|
+
const receipt = this.#reservations.get(observation);
|
|
32984
|
+
this.#reservations.delete(observation);
|
|
32985
|
+
if (!receipt)
|
|
32986
|
+
return false;
|
|
32987
|
+
const queue = this.#receipts.get(receipt.sessionID) ?? [];
|
|
32988
|
+
const index = queue.indexOf(receipt);
|
|
32989
|
+
if (index < 0)
|
|
32990
|
+
return false;
|
|
32991
|
+
queue.splice(index, 1);
|
|
32992
|
+
if (!queue.length)
|
|
32993
|
+
this.#receipts.delete(receipt.sessionID);
|
|
32994
|
+
return true;
|
|
32995
|
+
}
|
|
32996
|
+
consume(sessionID, purpose, expected = {}) {
|
|
32997
|
+
const reserved = this.reserve(sessionID, purpose, expected);
|
|
32998
|
+
if (reserved.ok)
|
|
32999
|
+
this.commit(reserved.observation);
|
|
33000
|
+
return reserved;
|
|
33001
|
+
}
|
|
32815
33002
|
verify(observation, sessionID, purpose) {
|
|
32816
33003
|
if (typeof observation !== "object" || observation === null || !this.#observations.has(observation))
|
|
32817
33004
|
return null;
|
|
@@ -33054,6 +33241,10 @@ var nativeExternalActionRunner = (root, actor, core, step) => createAuthorizedEx
|
|
|
33054
33241
|
settleObservation: (outcome, revisions) => nativeExternalActionObservation(actor, actionRef, outcome, actionRef.kind === "host" ? actionRef.handle : "external-action", revisions)
|
|
33055
33242
|
};
|
|
33056
33243
|
});
|
|
33244
|
+
var workerCoordinatorFor = (task, worker) => {
|
|
33245
|
+
const coordinator = worker.data.coordinator ?? task.intent.provenance.session;
|
|
33246
|
+
return coordinator?.kind === "host" && coordinator.host === "opencode" ? coordinator.handle : null;
|
|
33247
|
+
};
|
|
33057
33248
|
var workerIdFor = (store, actor, current, directChildren) => {
|
|
33058
33249
|
const parentID = sessionParent(current);
|
|
33059
33250
|
if (!parentID)
|
|
@@ -33064,10 +33255,9 @@ var workerIdFor = (store, actor, current, directChildren) => {
|
|
|
33064
33255
|
const matches = tasks.data.flatMap((task) => {
|
|
33065
33256
|
if (task.status !== "active")
|
|
33066
33257
|
return [];
|
|
33067
|
-
|
|
33068
|
-
if (coordinator?.kind !== "host" || coordinator.host !== "opencode" || coordinator.handle !== parentID || directChildren.get(actor) !== parentID)
|
|
33258
|
+
if (directChildren.get(actor) !== parentID)
|
|
33069
33259
|
return [];
|
|
33070
|
-
return task.workers.filter((worker) => worker.data.state === "running" && worker.data.session?.kind === "host" && worker.data.session.host === "opencode" && worker.data.session.handle === actor);
|
|
33260
|
+
return task.workers.filter((worker) => worker.data.state === "running" && worker.data.session?.kind === "host" && worker.data.session.host === "opencode" && worker.data.session.handle === actor && workerCoordinatorFor(task, worker) === parentID);
|
|
33071
33261
|
});
|
|
33072
33262
|
return matches.length === 1 ? matches[0].id : null;
|
|
33073
33263
|
};
|
|
@@ -33127,10 +33317,11 @@ var createWorkitTools = ({
|
|
|
33127
33317
|
};
|
|
33128
33318
|
if (decision.response === "approved")
|
|
33129
33319
|
expectation.selectedDescription = decision.binding.approvedContent;
|
|
33130
|
-
const observed = receipts.
|
|
33320
|
+
const observed = receipts.reserve(context.sessionID, "decision", expectation);
|
|
33131
33321
|
if (!observed.ok)
|
|
33132
33322
|
return output2(failure("permission_denied", observed.error));
|
|
33133
33323
|
let recordInput = parsed.data;
|
|
33324
|
+
let proposalToCommit;
|
|
33134
33325
|
if (decision.purpose === "action" && decision.response === "approved") {
|
|
33135
33326
|
const queue = actionProposals.get(context.sessionID) ?? [];
|
|
33136
33327
|
const textMatches = queue.filter((pending) => pending.presented === decision.binding.presented && pending.approvedText === decision.binding.approvedContent);
|
|
@@ -33142,7 +33333,7 @@ var createWorkitTools = ({
|
|
|
33142
33333
|
const fresh = resolveExternalActionRequest(context.directory, pending.request);
|
|
33143
33334
|
const freshDescriptor = fresh.ok ? externalActionDescriptor(pending.request.operation, fresh.data.descriptorPayload) : null;
|
|
33144
33335
|
if (freshDescriptor === pending.descriptor) {
|
|
33145
|
-
|
|
33336
|
+
proposalToCommit = pending;
|
|
33146
33337
|
recordInput = {
|
|
33147
33338
|
...recordInput,
|
|
33148
33339
|
binding: {
|
|
@@ -33161,6 +33352,13 @@ var createWorkitTools = ({
|
|
|
33161
33352
|
}
|
|
33162
33353
|
}
|
|
33163
33354
|
result = core.observeDecision(recordInput, observed.observation);
|
|
33355
|
+
if (result.ok) {
|
|
33356
|
+
receipts.commit(observed.observation);
|
|
33357
|
+
if (proposalToCommit) {
|
|
33358
|
+
const queue = actionProposals.get(context.sessionID) ?? [];
|
|
33359
|
+
actionProposals.set(context.sessionID, queue.filter((candidate) => candidate !== proposalToCommit));
|
|
33360
|
+
}
|
|
33361
|
+
}
|
|
33164
33362
|
} else {
|
|
33165
33363
|
const run = core[family];
|
|
33166
33364
|
result = run.call(core, parsed.data);
|
|
@@ -33216,7 +33414,7 @@ var createWorkitTools = ({
|
|
|
33216
33414
|
return output2(failure("permission_denied", "child sessions cannot run external actions"));
|
|
33217
33415
|
const store = new TaskStore(context.directory);
|
|
33218
33416
|
const prior = priorExternalAction(store, "opencode", context.sessionID, resolved.data.request.operation, resolved.data.request.payload);
|
|
33219
|
-
if (prior.ok && prior.data.entry.data.consumption !== null && prior.data.entry.data.consumption.state !== "uncertain")
|
|
33417
|
+
if (prior.ok && prior.data.entry.data.consumption !== null && prior.data.entry.data.consumption.state !== "uncertain" && resolved.data.request.operation !== "git.commit" && resolved.data.request.operation !== "git.push")
|
|
33220
33418
|
return output2(failure("permission_denied", "external action was already settled"));
|
|
33221
33419
|
const reconciliationTokens = new WeakSet;
|
|
33222
33420
|
const core = new WorkitCore(store, {
|
|
@@ -33519,10 +33717,10 @@ var plugin = async ({ client, directory }) => {
|
|
|
33519
33717
|
const listed = store.listTasks();
|
|
33520
33718
|
if (!workspace.ok || !workspace.data || !listed.ok)
|
|
33521
33719
|
return "unmanaged";
|
|
33522
|
-
const
|
|
33523
|
-
if (
|
|
33720
|
+
const managed = listed.data.filter((task) => task.status === "active" && task.workspaceId === workspace.data?.id && (task.intent.provenance.session?.kind === "host" && task.intent.provenance.session.host === "opencode" && task.intent.provenance.session.handle === coordinator || task.workers.some((entry) => entry.provenance.session?.kind === "host" && entry.provenance.session.host === "opencode" && entry.provenance.session.handle === coordinator) || workspace.data.writer?.owner.taskId === task.id && workspace.data.writer.owner.session.host === "opencode" && workspace.data.writer.owner.session.handle === coordinator));
|
|
33721
|
+
if (managed.length === 0)
|
|
33524
33722
|
return "unmanaged";
|
|
33525
|
-
const eligible =
|
|
33723
|
+
const eligible = managed.flatMap((task) => task.workers.filter((entry) => entry.data.state === "assigned" && entry.data.session === null && entry.provenance.session?.kind === "host" && entry.provenance.session.host === "opencode" && entry.provenance.session.handle === coordinator).map((entry) => ({ task, entry })));
|
|
33526
33724
|
const next = oldestOfSingleTask(eligible);
|
|
33527
33725
|
if (!next)
|
|
33528
33726
|
return "unbound";
|
|
@@ -33606,7 +33804,7 @@ var plugin = async ({ client, directory }) => {
|
|
|
33606
33804
|
dispatches.delete(coordinator);
|
|
33607
33805
|
return committed.ok;
|
|
33608
33806
|
};
|
|
33609
|
-
const observeLifecycle = async (sessionID, parentID, state, binding,
|
|
33807
|
+
const observeLifecycle = async (sessionID, parentID, state, binding, eventInfo, report) => {
|
|
33610
33808
|
const store = new TaskStore(directory);
|
|
33611
33809
|
const listed = store.listTasks();
|
|
33612
33810
|
const workspace = store.readWorkspace();
|
|
@@ -33616,7 +33814,7 @@ var plugin = async ({ client, directory }) => {
|
|
|
33616
33814
|
}
|
|
33617
33815
|
const persisted = listed.data.flatMap((task) => task.status === "active" ? task.workers.filter((entry) => entry.id === binding?.workerId || entry.data.session?.kind === "host" && entry.data.session.handle === sessionID).map((entry) => ({ task, entry })) : []);
|
|
33618
33816
|
const matches = persisted.filter(({ entry }) => entry.data.session?.kind === "host");
|
|
33619
|
-
if (binding &&
|
|
33817
|
+
if (binding && matches.length !== 1) {
|
|
33620
33818
|
droppedLifecycle(sessionID, "bound child worker handle is ambiguous");
|
|
33621
33819
|
return;
|
|
33622
33820
|
}
|
|
@@ -33625,7 +33823,7 @@ var plugin = async ({ client, directory }) => {
|
|
|
33625
33823
|
droppedLifecycle(sessionID, binding ? "bound worker is missing from persisted state" : "worker session is ambiguous");
|
|
33626
33824
|
return;
|
|
33627
33825
|
}
|
|
33628
|
-
if (
|
|
33826
|
+
if (selected.entry.data.session?.kind !== "host") {
|
|
33629
33827
|
droppedLifecycle(sessionID, "selected worker has no bound host session");
|
|
33630
33828
|
return;
|
|
33631
33829
|
}
|
|
@@ -33633,25 +33831,26 @@ var plugin = async ({ client, directory }) => {
|
|
|
33633
33831
|
droppedLifecycle(sessionID, "selected worker provenance is not host-observed");
|
|
33634
33832
|
return;
|
|
33635
33833
|
}
|
|
33636
|
-
|
|
33637
|
-
|
|
33638
|
-
if (
|
|
33639
|
-
droppedLifecycle(sessionID, "
|
|
33834
|
+
const observed = eventInfo ?? await sessionData2(client, sessionID);
|
|
33835
|
+
if (binding && eventInfo) {
|
|
33836
|
+
if (!deletedSessionEnd(directory, sessionID, parentID, observed)) {
|
|
33837
|
+
droppedLifecycle(sessionID, "deleted session payload contradicts the binding");
|
|
33640
33838
|
return;
|
|
33641
33839
|
}
|
|
33642
|
-
|
|
33840
|
+
} else if (!trustedSession(directory, sessionID, observed)) {
|
|
33841
|
+
droppedLifecycle(sessionID, "live session observation is not trusted");
|
|
33842
|
+
return;
|
|
33643
33843
|
}
|
|
33644
|
-
if (!
|
|
33645
|
-
const
|
|
33646
|
-
if (
|
|
33647
|
-
|
|
33648
|
-
droppedLifecycle(sessionID, "deleted session payload contradicts the binding");
|
|
33649
|
-
return;
|
|
33650
|
-
}
|
|
33651
|
-
} else if (!trustedSession(directory, sessionID, observed) || observed.parentID !== parentID) {
|
|
33652
|
-
droppedLifecycle(sessionID, "live session observation is not trusted");
|
|
33844
|
+
if (!binding) {
|
|
33845
|
+
const coordinator = workerCoordinatorFor(selected.task, selected.entry);
|
|
33846
|
+
if (!coordinator || observed.parentID !== coordinator) {
|
|
33847
|
+
droppedLifecycle(sessionID, "live session parent contradicts persisted coordinator");
|
|
33653
33848
|
return;
|
|
33654
33849
|
}
|
|
33850
|
+
parentID = coordinator;
|
|
33851
|
+
} else if (!eventInfo && observed.parentID !== parentID) {
|
|
33852
|
+
droppedLifecycle(sessionID, "live session parent contradicts the binding");
|
|
33853
|
+
return;
|
|
33655
33854
|
}
|
|
33656
33855
|
directChildren.set(sessionID, parentID);
|
|
33657
33856
|
const core = new WorkitCore(store, {
|
|
@@ -33669,6 +33868,7 @@ var plugin = async ({ client, directory }) => {
|
|
|
33669
33868
|
expectedWorkspaceRevision: workspace.data.revision,
|
|
33670
33869
|
state,
|
|
33671
33870
|
session: { kind: "host", host: "opencode", handle: sessionID },
|
|
33871
|
+
...report ? { report } : {},
|
|
33672
33872
|
observation: { event: state, sessionID }
|
|
33673
33873
|
});
|
|
33674
33874
|
if (!result.ok) {
|
|
@@ -33684,25 +33884,9 @@ var plugin = async ({ client, directory }) => {
|
|
|
33684
33884
|
const bindCreatedSession = async (info) => {
|
|
33685
33885
|
if (typeof info.id !== "string" || typeof info.parentID !== "string" || !sameWorkspace(directory, info.directory))
|
|
33686
33886
|
return;
|
|
33687
|
-
const live = dispatches.get(info.parentID);
|
|
33688
|
-
if (live)
|
|
33689
|
-
live.generation.childCreated = true;
|
|
33690
|
-
const store = new TaskStore(directory);
|
|
33691
|
-
const workspace = store.readWorkspace();
|
|
33692
|
-
const listed = store.listTasks();
|
|
33693
|
-
if (!workspace.ok || !workspace.data || !listed.ok)
|
|
33694
|
-
return;
|
|
33695
|
-
const candidates = listed.data.flatMap((task) => task.status === "active" && task.workspaceId === workspace.data?.id ? task.workers.filter((entry) => (entry.data.state === "assigned" || entry.data.state === "dispatching") && entry.data.session === null && entry.provenance.session?.kind === "host" && entry.provenance.session.host === "opencode" && entry.provenance.session.handle === info.parentID).map((entry) => ({ task, entry })) : []);
|
|
33696
|
-
const next = oldestOfSingleTask(candidates);
|
|
33697
|
-
if (!next)
|
|
33698
|
-
return;
|
|
33699
33887
|
directChildren.set(info.id, info.parentID);
|
|
33700
|
-
if (
|
|
33701
|
-
|
|
33702
|
-
await observeLifecycle(info.id, info.parentID, "running", {
|
|
33703
|
-
taskId: next.task.id,
|
|
33704
|
-
workerId: next.entry.id
|
|
33705
|
-
}, true);
|
|
33888
|
+
if (dispatches.has(info.parentID))
|
|
33889
|
+
commitDispatchStart(info.parentID, info.id);
|
|
33706
33890
|
};
|
|
33707
33891
|
return {
|
|
33708
33892
|
tool: tools,
|
|
@@ -33724,7 +33908,7 @@ var plugin = async ({ client, directory }) => {
|
|
|
33724
33908
|
return;
|
|
33725
33909
|
const binding = lifecycleBindings.get(sessionID);
|
|
33726
33910
|
const state = event.type === "session.status" ? event.properties.status.type === "busy" ? "running" : event.properties.status.type === "idle" ? "stopped" : "unknown" : event.type === "session.idle" ? "stopped" : event.type === "session.deleted" ? "stopped" : "unknown";
|
|
33727
|
-
await observeLifecycle(sessionID, binding?.parentID ?? "", state, binding,
|
|
33911
|
+
await observeLifecycle(sessionID, binding?.parentID ?? "", state, binding, event.type === "session.deleted" ? properties.info : undefined);
|
|
33728
33912
|
},
|
|
33729
33913
|
"tool.execute.after": async (input, output) => {
|
|
33730
33914
|
observeQuestion(receipts, input, output);
|
|
@@ -33750,7 +33934,12 @@ var plugin = async ({ client, directory }) => {
|
|
|
33750
33934
|
if (completedChild) {
|
|
33751
33935
|
const completionBinding = lifecycleBindings.get(completedChild);
|
|
33752
33936
|
if (completionBinding)
|
|
33753
|
-
await observeLifecycle(completedChild, input.sessionID, "stopped", completionBinding,
|
|
33937
|
+
await observeLifecycle(completedChild, input.sessionID, "stopped", completionBinding, undefined, {
|
|
33938
|
+
outcome: "completed",
|
|
33939
|
+
summary: outputText.match(/<task_result>([\s\S]*?)<\/task_result>/)?.[1]?.trim() || outputText.trim() || "Native worker completed.",
|
|
33940
|
+
evidenceIds: [],
|
|
33941
|
+
findingIds: []
|
|
33942
|
+
});
|
|
33754
33943
|
} else if (!resolved && /(?:cancel|interrupt|unknown|uncertain)/.test(state))
|
|
33755
33944
|
unresolvedTaskLaunches.add(input.sessionID);
|
|
33756
33945
|
}
|
|
@@ -33992,6 +34181,7 @@ var createV2Lifecycle = (deps) => {
|
|
|
33992
34181
|
return null;
|
|
33993
34182
|
return session;
|
|
33994
34183
|
};
|
|
34184
|
+
const managedByCoordinator = (task, workspace, coordinator) => task.status === "active" && task.workspaceId === workspace.id && (task.intent.provenance.session?.kind === "host" && task.intent.provenance.session.host === "opencode" && task.intent.provenance.session.handle === coordinator || task.workers.some((entry) => entry.provenance.session?.kind === "host" && entry.provenance.session.host === "opencode" && entry.provenance.session.handle === coordinator) || workspace.writer?.owner.taskId === task.id && workspace.writer.owner.session.host === "opencode" && workspace.writer.owner.session.handle === coordinator);
|
|
33995
34185
|
const ownsActiveTask = (coordinator) => {
|
|
33996
34186
|
try {
|
|
33997
34187
|
const store = new TaskStore(root);
|
|
@@ -33999,7 +34189,7 @@ var createV2Lifecycle = (deps) => {
|
|
|
33999
34189
|
const listed = store.listTasks();
|
|
34000
34190
|
if (!workspace.ok || !workspace.data || !listed.ok)
|
|
34001
34191
|
return false;
|
|
34002
|
-
return listed.data.some((task) => task
|
|
34192
|
+
return listed.data.some((task) => managedByCoordinator(task, workspace.data, coordinator));
|
|
34003
34193
|
} catch {
|
|
34004
34194
|
return false;
|
|
34005
34195
|
}
|
|
@@ -34027,10 +34217,10 @@ var createV2Lifecycle = (deps) => {
|
|
|
34027
34217
|
const listed = store.listTasks();
|
|
34028
34218
|
if (!workspace.ok || !workspace.data || !listed.ok)
|
|
34029
34219
|
return "unmanaged";
|
|
34030
|
-
const
|
|
34031
|
-
if (
|
|
34220
|
+
const managed = listed.data.filter((task) => managedByCoordinator(task, workspace.data, coordinator));
|
|
34221
|
+
if (managed.length === 0)
|
|
34032
34222
|
return "unmanaged";
|
|
34033
|
-
const eligible =
|
|
34223
|
+
const eligible = managed.flatMap((task) => task.workers.filter((entry) => entry.data.state === "assigned" && entry.data.session === null && entry.provenance.session?.kind === "host" && entry.provenance.session.host === "opencode" && entry.provenance.session.handle === coordinator).map((entry) => ({ task, entry })));
|
|
34034
34224
|
const next = oldestOfSingleTask(eligible);
|
|
34035
34225
|
if (!next)
|
|
34036
34226
|
return "unbound";
|
|
@@ -34098,7 +34288,7 @@ var createV2Lifecycle = (deps) => {
|
|
|
34098
34288
|
});
|
|
34099
34289
|
return true;
|
|
34100
34290
|
};
|
|
34101
|
-
const observeLifecycle = async (sessionID, parentID, state, binding,
|
|
34291
|
+
const observeLifecycle = async (sessionID, parentID, state, binding, deleted = false, report) => {
|
|
34102
34292
|
const store = new TaskStore(root);
|
|
34103
34293
|
const listed = store.listTasks();
|
|
34104
34294
|
const workspace = store.readWorkspace();
|
|
@@ -34106,24 +34296,28 @@ var createV2Lifecycle = (deps) => {
|
|
|
34106
34296
|
return;
|
|
34107
34297
|
const persisted = listed.data.flatMap((task) => task.status === "active" ? task.workers.filter((entry) => entry.id === binding?.workerId || entry.data.session?.kind === "host" && entry.data.session.handle === sessionID).map((entry) => ({ task, entry })) : []);
|
|
34108
34298
|
const matches = persisted.filter(({ entry }) => entry.data.session?.kind === "host");
|
|
34109
|
-
if (binding &&
|
|
34299
|
+
if (binding && matches.length !== 1)
|
|
34110
34300
|
return;
|
|
34111
34301
|
const selected = binding ? persisted.find(({ task, entry }) => task.id === binding.taskId && entry.id === binding.workerId) : matches.length === 1 ? matches[0] : undefined;
|
|
34112
34302
|
if (!selected)
|
|
34113
34303
|
return;
|
|
34114
|
-
if (
|
|
34304
|
+
if (selected.entry.data.session?.kind !== "host")
|
|
34115
34305
|
return;
|
|
34116
34306
|
if (selected.entry.provenance.session?.kind !== "host")
|
|
34117
34307
|
return;
|
|
34118
|
-
if (!binding)
|
|
34119
|
-
|
|
34120
|
-
|
|
34121
|
-
return;
|
|
34122
|
-
parentID = coordinator.handle;
|
|
34123
|
-
}
|
|
34124
|
-
if (!initial && !deleted) {
|
|
34308
|
+
if (!binding && deleted)
|
|
34309
|
+
return;
|
|
34310
|
+
if (!deleted) {
|
|
34125
34311
|
const observed = await readSession(sessionID);
|
|
34126
|
-
|
|
34312
|
+
const observedParent = parentOf(observed);
|
|
34313
|
+
if (!observed || !sameWorkspace(root, observed.directory) || !observedParent)
|
|
34314
|
+
return;
|
|
34315
|
+
if (!binding) {
|
|
34316
|
+
const coordinator = workerCoordinatorFor(selected.task, selected.entry);
|
|
34317
|
+
if (!coordinator || observedParent !== coordinator)
|
|
34318
|
+
return;
|
|
34319
|
+
parentID = coordinator;
|
|
34320
|
+
} else if (observedParent !== parentID)
|
|
34127
34321
|
return;
|
|
34128
34322
|
}
|
|
34129
34323
|
directChildren.set(sessionID, parentID);
|
|
@@ -34142,6 +34336,7 @@ var createV2Lifecycle = (deps) => {
|
|
|
34142
34336
|
expectedWorkspaceRevision: workspace.data.revision,
|
|
34143
34337
|
state,
|
|
34144
34338
|
session: { kind: "host", host: "opencode", handle: sessionID },
|
|
34339
|
+
...report ? { report } : {},
|
|
34145
34340
|
observation: { event: state, sessionID }
|
|
34146
34341
|
});
|
|
34147
34342
|
if (!result.ok)
|
|
@@ -34188,6 +34383,10 @@ var createV2Lifecycle = (deps) => {
|
|
|
34188
34383
|
const child = childID ? await readSession(childID) : null;
|
|
34189
34384
|
if (!child || parentOf(child) !== coordinator)
|
|
34190
34385
|
throw new Error("delegation_lineage_denied: a continuation sessionID must name an existing direct child of the coordinator");
|
|
34386
|
+
const listed = new TaskStore(root).listTasks();
|
|
34387
|
+
const bound = listed.ok ? listed.data.filter((task) => task.workers.some((worker) => worker.data.state === "running" && worker.data.session?.kind === "host" && worker.data.session.handle === childID && workerCoordinatorFor(task, worker) === coordinator)).length : 0;
|
|
34388
|
+
if (bound !== 1 && ownsActiveTask(coordinator))
|
|
34389
|
+
throw new Error("delegation_lineage_denied: a continuation sessionID must name a running Workit worker");
|
|
34191
34390
|
return;
|
|
34192
34391
|
}
|
|
34193
34392
|
if (!ownsActiveTask(coordinator)) {
|
|
@@ -34248,7 +34447,12 @@ var createV2Lifecycle = (deps) => {
|
|
|
34248
34447
|
if (completedChild) {
|
|
34249
34448
|
const binding = lifecycleBindings.get(completedChild);
|
|
34250
34449
|
if (binding)
|
|
34251
|
-
await observeLifecycle(completedChild, coordinator, "stopped", binding
|
|
34450
|
+
await observeLifecycle(completedChild, coordinator, "stopped", binding, false, {
|
|
34451
|
+
outcome: "completed",
|
|
34452
|
+
summary: output.trim() || "Native worker completed.",
|
|
34453
|
+
evidenceIds: [],
|
|
34454
|
+
findingIds: []
|
|
34455
|
+
});
|
|
34252
34456
|
}
|
|
34253
34457
|
} else {
|
|
34254
34458
|
const message = String(event.error?.message ?? event.error ?? "").toLowerCase();
|
|
@@ -34266,24 +34470,12 @@ var createV2Lifecycle = (deps) => {
|
|
|
34266
34470
|
const bindCreatedSession = async (data) => {
|
|
34267
34471
|
if (!sameWorkspace(root, data.directory))
|
|
34268
34472
|
return;
|
|
34473
|
+
directChildren.set(data.sessionID, data.parentID);
|
|
34269
34474
|
const live = dispatches.get(data.parentID);
|
|
34270
34475
|
if (live) {
|
|
34271
|
-
live.generation.childCreated = true;
|
|
34272
34476
|
live.childID = data.sessionID;
|
|
34477
|
+
commitDispatchStart(data.parentID, data.sessionID);
|
|
34273
34478
|
}
|
|
34274
|
-
const store = new TaskStore(root);
|
|
34275
|
-
const workspace = store.readWorkspace();
|
|
34276
|
-
const listed = store.listTasks();
|
|
34277
|
-
if (!workspace.ok || !workspace.data || !listed.ok)
|
|
34278
|
-
return;
|
|
34279
|
-
const candidates = listed.data.flatMap((task) => task.status === "active" && task.workspaceId === workspace.data?.id ? task.workers.filter((entry) => (entry.data.state === "assigned" || entry.data.state === "dispatching") && entry.data.session === null && entry.provenance.session?.kind === "host" && entry.provenance.session.host === "opencode" && entry.provenance.session.handle === data.parentID).map((entry) => ({ task, entry })) : []);
|
|
34280
|
-
const next = oldestOfSingleTask(candidates);
|
|
34281
|
-
if (!next)
|
|
34282
|
-
return;
|
|
34283
|
-
directChildren.set(data.sessionID, data.parentID);
|
|
34284
|
-
if (live && live.taskId === next.task.id && live.workerId === next.entry.id && commitDispatchStart(data.parentID, data.sessionID))
|
|
34285
|
-
return;
|
|
34286
|
-
await observeLifecycle(data.sessionID, data.parentID, "running", { taskId: next.task.id, workerId: next.entry.id }, true);
|
|
34287
34479
|
};
|
|
34288
34480
|
const handleEvent = async (event) => {
|
|
34289
34481
|
const type = event.type;
|
|
@@ -34310,7 +34502,7 @@ var createV2Lifecycle = (deps) => {
|
|
|
34310
34502
|
const directory = event.location?.directory;
|
|
34311
34503
|
if (typeof directory === "string" && !sameWorkspace(root, directory))
|
|
34312
34504
|
return;
|
|
34313
|
-
await observeLifecycle(sessionID, binding?.parentID ?? "", "stopped", binding,
|
|
34505
|
+
await observeLifecycle(sessionID, binding?.parentID ?? "", "stopped", binding, true);
|
|
34314
34506
|
return;
|
|
34315
34507
|
}
|
|
34316
34508
|
const state = type === "session.execution.started" ? "running" : "stopped";
|
|
@@ -34391,13 +34583,13 @@ var normalizeQuestionAnswers = (answers) => {
|
|
|
34391
34583
|
}
|
|
34392
34584
|
if (first && typeof first === "object") {
|
|
34393
34585
|
const values = Object.values(first);
|
|
34394
|
-
if (values.length
|
|
34586
|
+
if (values.length === 1 && singleValue(values[0]) !== null)
|
|
34395
34587
|
return [[singleValue(values[0])]];
|
|
34396
34588
|
}
|
|
34397
34589
|
return answers;
|
|
34398
34590
|
}
|
|
34399
34591
|
const values = Object.values(answers);
|
|
34400
|
-
if (values.length
|
|
34592
|
+
if (values.length === 1 && singleValue(values[0]) !== null)
|
|
34401
34593
|
return [[singleValue(values[0])]];
|
|
34402
34594
|
return answers;
|
|
34403
34595
|
};
|
|
@@ -34528,10 +34720,9 @@ var workerIdFor2 = (store, actor, parentID, directChildren) => {
|
|
|
34528
34720
|
const matches = tasks.data.flatMap((task) => {
|
|
34529
34721
|
if (task.status !== "active")
|
|
34530
34722
|
return [];
|
|
34531
|
-
|
|
34532
|
-
if (coordinator?.kind !== "host" || coordinator.host !== "opencode" || coordinator.handle !== parentID || directChildren.get(actor) !== parentID)
|
|
34723
|
+
if (directChildren.get(actor) !== parentID)
|
|
34533
34724
|
return [];
|
|
34534
|
-
return task.workers.filter((worker) => worker.data.state === "running" && worker.data.session?.kind === "host" && worker.data.session.host === "opencode" && worker.data.session.handle === actor);
|
|
34725
|
+
return task.workers.filter((worker) => worker.data.state === "running" && worker.data.session?.kind === "host" && worker.data.session.host === "opencode" && worker.data.session.handle === actor && workerCoordinatorFor(task, worker) === parentID);
|
|
34535
34726
|
});
|
|
34536
34727
|
return matches.length === 1 ? matches[0].id : null;
|
|
34537
34728
|
};
|
|
@@ -34543,7 +34734,7 @@ var recordDecision = (core, receipts, request, sessionID) => {
|
|
|
34543
34734
|
const budgetIssue = workitBindingQuestionIssue([content]);
|
|
34544
34735
|
if (budgetIssue)
|
|
34545
34736
|
return failure("invalid_input", budgetIssue);
|
|
34546
|
-
const observed = receipts.
|
|
34737
|
+
const observed = receipts.reserve(sessionID, "decision", {
|
|
34547
34738
|
selectedLabel: decision.response,
|
|
34548
34739
|
decisionPurpose: decision.purpose,
|
|
34549
34740
|
contentDigest: sha256(canonicalJson(content)),
|
|
@@ -34554,10 +34745,15 @@ var recordDecision = (core, receipts, request, sessionID) => {
|
|
|
34554
34745
|
return failure("permission_denied", observed.error);
|
|
34555
34746
|
if (decision.purpose === "action" && decision.response === "approved" && !isSelfAuthorizingActionContent(decision.binding.approvedContent))
|
|
34556
34747
|
return failure("invalid_input", "no matching action proposal; resolve the action through the action tool first");
|
|
34557
|
-
|
|
34748
|
+
const result = core.observeDecision(request, observed.observation);
|
|
34749
|
+
if (result.ok)
|
|
34750
|
+
receipts.commit(observed.observation);
|
|
34751
|
+
return result;
|
|
34558
34752
|
};
|
|
34559
34753
|
var setup = async (ctx) => {
|
|
34560
34754
|
const root = ctx.location.directory;
|
|
34755
|
+
const sourceMarker = markSourcesLoaded(pluginSourceFiles);
|
|
34756
|
+
let staleSourcesWarned = false;
|
|
34561
34757
|
const receipts = new NativeReceiptStore;
|
|
34562
34758
|
const lifecycle = createV2Lifecycle({
|
|
34563
34759
|
root,
|
|
@@ -34664,6 +34860,16 @@ var setup = async (ctx) => {
|
|
|
34664
34860
|
await ctx.session.hook("context", async (event) => {
|
|
34665
34861
|
const session = await sessionFacts(ctx, String(event.sessionID));
|
|
34666
34862
|
injectAgentContext(root, session, lifecycle.directChildren, event.system);
|
|
34863
|
+
if (!staleSourcesWarned) {
|
|
34864
|
+
const changed = changedSourcesSinceLoad(sourceMarker);
|
|
34865
|
+
if (changed.length > 0) {
|
|
34866
|
+
staleSourcesWarned = true;
|
|
34867
|
+
event.system.push({
|
|
34868
|
+
type: "text",
|
|
34869
|
+
text: `<workit-warning>Workit sources changed after plugin load (${changed.map((file) => file.split(/[\\/]/).at(-1)).join(", ")}); restart the session for latest behavior.</workit-warning>`
|
|
34870
|
+
});
|
|
34871
|
+
}
|
|
34872
|
+
}
|
|
34667
34873
|
});
|
|
34668
34874
|
await ctx.session.hook("compaction", (event) => {
|
|
34669
34875
|
injectCompactionContext(root, String(event.sessionID), event.system);
|